Repository: carbon-language/carbon-lang Branch: trunk Commit: 8e5b358ec27a Files: 5011 Total size: 29.3 MB Directory structure: gitextract_q3vemk7y/ ├── .agents/ │ └── skills/ │ ├── code_style/ │ │ └── SKILL.md │ ├── summarize_testdata_changes/ │ │ ├── SKILL.md │ │ └── scripts/ │ │ └── parse_diff.py │ ├── tool_usage/ │ │ └── SKILL.md │ └── toolchain_development/ │ └── SKILL.md ├── .bazelignore ├── .bazelrc ├── .bazelversion ├── .clang-format ├── .clang-tidy ├── .clangd ├── .codespell_ignore ├── .gdbinit ├── .gitattributes ├── .github/ │ ├── ISSUE_TEMPLATE/ │ │ ├── 01_toolchain_bug.yml │ │ ├── 02_documentation_bug.yml │ │ ├── 03_leads_question.yml │ │ └── config.yml │ ├── actions/ │ │ ├── build-setup-common/ │ │ │ └── action.yml │ │ ├── build-setup-macos/ │ │ │ └── action.yml │ │ ├── build-setup-ubuntu/ │ │ │ └── action.yml │ │ └── test-setup/ │ │ └── action.yml │ ├── pull_request_template.md │ ├── release.yaml │ └── workflows/ │ ├── README.md │ ├── auto_label_prs.yaml │ ├── clangd_tidy.yaml │ ├── discord_wiki.yaml │ ├── gh_pages_ci.yaml │ ├── gh_pages_deploy.yaml │ ├── nightly_release.yaml │ ├── pre_commit.yaml │ ├── pre_commit_suggestions.yaml │ ├── proposal_labeled.yaml │ ├── proposal_ready.yaml │ ├── sync_repos.yaml │ ├── tests.yaml │ └── triage_inactive.yaml ├── .gitignore ├── .lldbinit ├── .pre-commit-config.yaml ├── .prettierrc.yaml ├── .python-version ├── .vscode/ │ ├── extensions.json │ ├── gdb_launch.json │ ├── lldb_launch.json │ └── tasks.json ├── BUILD ├── CODEOWNERS ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── GEMINI.md ├── LICENSE ├── MODULE.bazel ├── README.md ├── SECURITY.md ├── bazel/ │ ├── carbon_rules/ │ │ ├── BUILD │ │ └── defs.bzl │ ├── cc_rules/ │ │ ├── BUILD │ │ └── defs.bzl │ ├── cc_toolchains/ │ │ ├── BUILD │ │ ├── cc_toolchain_actions.bzl │ │ ├── cc_toolchain_base_features.bzl │ │ ├── cc_toolchain_carbon_project_features.bzl │ │ ├── cc_toolchain_config_features.bzl │ │ ├── cc_toolchain_cpp_features.bzl │ │ ├── cc_toolchain_debugging.bzl │ │ ├── cc_toolchain_features.bzl │ │ ├── cc_toolchain_linking.bzl │ │ ├── cc_toolchain_modules.bzl │ │ ├── cc_toolchain_optimization.bzl │ │ ├── cc_toolchain_sanitizer_features.bzl │ │ ├── cc_toolchain_tools.bzl │ │ ├── clang_cc_toolchain_config.bzl │ │ ├── clang_configuration.bzl │ │ ├── clang_detected_variables.tpl.bzl │ │ ├── clang_toolchain.BUILD │ │ └── defs.bzl │ ├── check_deps/ │ │ ├── BUILD │ │ ├── check_non_test_cc_deps.py │ │ └── update_roots.py │ ├── llvm_project/ │ │ ├── 0001_Patch_for_mallinfo2_when_using_Bazel_build_system.patch │ │ ├── 0002_Added_Bazel_build_for_compiler_rt_fuzzer.patch │ │ ├── 0004_Introduce_basic_sources_exporting_for_libunwind.patch │ │ ├── 0005_Introduce_basic_sources_exporting_for_libcxx_and_libcxxabi.patch │ │ ├── 0009_Introduce_starlark_exporting_compiler-rt_build_information.patch │ │ ├── BUILD │ │ └── llvm_project.bzl │ ├── malloc/ │ │ └── BUILD │ ├── manifest/ │ │ ├── BUILD │ │ └── defs.bzl │ └── version/ │ ├── BUILD │ ├── compute_version.bzl │ ├── gen_tmpl.py │ └── rules.bzl ├── common/ │ ├── BUILD │ ├── all_llvm_targets.cpp │ ├── array_stack.h │ ├── array_stack_test.cpp │ ├── bazel_working_dir.h │ ├── build_data.cpp │ ├── build_data.h │ ├── build_data_linkstamp.cpp │ ├── build_data_linkstamp.h │ ├── build_data_test.cpp │ ├── check.h │ ├── check_internal.cpp │ ├── check_internal.h │ ├── check_test.cpp │ ├── command_line.cpp │ ├── command_line.h │ ├── command_line_test.cpp │ ├── concepts.h │ ├── emplace_by_calling.h │ ├── emplace_by_calling_test.cpp │ ├── enum_base.h │ ├── enum_base_test.cpp │ ├── enum_base_test.def │ ├── enum_mask_base.h │ ├── enum_mask_base_test.cpp │ ├── error.h │ ├── error_test.cpp │ ├── error_test_helpers.h │ ├── exe_path.cpp │ ├── exe_path.h │ ├── exe_path_test.cpp │ ├── filesystem.cpp │ ├── filesystem.h │ ├── filesystem_benchmark.cpp │ ├── filesystem_test.cpp │ ├── find.h │ ├── find_test.cpp │ ├── growing_range.h │ ├── growing_range_test.cpp │ ├── hashing.cpp │ ├── hashing.h │ ├── hashing_benchmark.cpp │ ├── hashing_test.cpp │ ├── hashtable_key_context.h │ ├── hashtable_key_context_test.cpp │ ├── init_llvm.cpp │ ├── init_llvm.h │ ├── latch.cpp │ ├── latch.h │ ├── latch_test.cpp │ ├── map.h │ ├── map_benchmark.cpp │ ├── map_test.cpp │ ├── move_only.h │ ├── ostream.h │ ├── pretty_stack_trace_function.h │ ├── raw_hashtable.cpp │ ├── raw_hashtable.h │ ├── raw_hashtable_benchmark_helpers.cpp │ ├── raw_hashtable_benchmark_helpers.h │ ├── raw_hashtable_metadata_group.cpp │ ├── raw_hashtable_metadata_group.h │ ├── raw_hashtable_metadata_group_benchmark.cpp │ ├── raw_hashtable_test_helpers.h │ ├── raw_string_ostream.h │ ├── raw_string_ostream_test.cpp │ ├── set.h │ ├── set_benchmark.cpp │ ├── set_test.cpp │ ├── string_helpers.cpp │ ├── string_helpers.h │ ├── string_helpers_test.cpp │ ├── struct_reflection.h │ ├── struct_reflection_test.cpp │ ├── template_string.h │ ├── template_string_test.cpp │ ├── type_enum.h │ ├── version.h │ ├── version.tmpl.cpp │ ├── version_stamp.tmpl.cpp │ ├── vlog.h │ └── vlog_test.cpp ├── core/ │ ├── BUILD │ ├── io.carbon │ ├── prelude/ │ │ ├── copy.carbon │ │ ├── default.carbon │ │ ├── destroy.carbon │ │ ├── iterate.carbon │ │ ├── operators/ │ │ │ ├── arithmetic.carbon │ │ │ ├── as.carbon │ │ │ ├── bitwise.carbon │ │ │ ├── comparison.carbon │ │ │ ├── deref.carbon │ │ │ └── index.carbon │ │ ├── operators.carbon │ │ ├── types/ │ │ │ ├── bool.carbon │ │ │ ├── char.carbon │ │ │ ├── cpp/ │ │ │ │ ├── int.carbon │ │ │ │ ├── nullptr.carbon │ │ │ │ └── void.carbon │ │ │ ├── float.carbon │ │ │ ├── float_literal.carbon │ │ │ ├── form.carbon │ │ │ ├── int.carbon │ │ │ ├── int_literal.carbon │ │ │ ├── maybe_unformed.carbon │ │ │ ├── optional.carbon │ │ │ ├── string.carbon │ │ │ └── uint.carbon │ │ └── types.carbon │ ├── prelude.carbon │ └── range.carbon ├── docs/ │ ├── README.md │ ├── design/ │ │ ├── README.md │ │ ├── aliases.md │ │ ├── assignment.md │ │ ├── blocks_and_statements.md │ │ ├── classes.md │ │ ├── code_and_name_organization/ │ │ │ ├── README.md │ │ │ └── source_files.md │ │ ├── control_flow/ │ │ │ ├── README.md │ │ │ ├── conditionals.md │ │ │ ├── loops.md │ │ │ └── return.md │ │ ├── declaring_entities.md │ │ ├── expressions/ │ │ │ ├── README.md │ │ │ ├── arithmetic.md │ │ │ ├── as_expressions.md │ │ │ ├── bitwise.md │ │ │ ├── comparison_operators.md │ │ │ ├── if.md │ │ │ ├── implicit_conversions.md │ │ │ ├── indexing.md │ │ │ ├── literals.md │ │ │ ├── logical_operators.md │ │ │ ├── member_access.md │ │ │ ├── pointer_operators.md │ │ │ └── type_operators.md │ │ ├── functions.md │ │ ├── generics/ │ │ │ ├── README.md │ │ │ ├── appendix-coherence.md │ │ │ ├── appendix-rewrite-constraints.md │ │ │ ├── appendix-witness.md │ │ │ ├── details.md │ │ │ ├── goals.md │ │ │ ├── overview.md │ │ │ └── terminology.md │ │ ├── interoperability/ │ │ │ ├── README.md │ │ │ └── philosophy_and_goals.md │ │ ├── lambdas.md │ │ ├── lexical_conventions/ │ │ │ ├── README.md │ │ │ ├── comments.md │ │ │ ├── numeric_literals.md │ │ │ ├── string_literals.md │ │ │ ├── symbolic_tokens.md │ │ │ ├── whitespace.md │ │ │ └── words.md │ │ ├── metaprogramming.md │ │ ├── name_lookup.md │ │ ├── naming_conventions.md │ │ ├── pattern_matching.md │ │ ├── safety/ │ │ │ ├── README.md │ │ │ └── terminology.md │ │ ├── sum_types.md │ │ ├── templates.md │ │ ├── tuples.md │ │ ├── type_inference.md │ │ ├── values.md │ │ └── variadics.md │ ├── guides/ │ │ ├── README.md │ │ └── glossary.md │ ├── images/ │ │ └── snippets.md │ ├── project/ │ │ ├── README.md │ │ ├── code_review.md │ │ ├── commit_access.md │ │ ├── contribution_tools.md │ │ ├── cpp_style_guide.md │ │ ├── design_style_guide.md │ │ ├── difficulties_improving_cpp.md │ │ ├── evolution.md │ │ ├── faq.md │ │ ├── goals.md │ │ ├── groups.md │ │ ├── milestones.md │ │ ├── moderators.md │ │ ├── principles/ │ │ │ ├── README.md │ │ │ ├── error_handling.md │ │ │ ├── information_accumulation.md │ │ │ ├── library_apis_only.md │ │ │ ├── low_context_sensitivity.md │ │ │ ├── namespace_cleanliness.md │ │ │ ├── one_way.md │ │ │ ├── progressive_disclosure.md │ │ │ ├── safety_strategy.md │ │ │ ├── static_open_extension.md │ │ │ └── success_criteria.md │ │ ├── pull_request_workflow.md │ │ ├── roadmap.md │ │ ├── roadmap_process.md │ │ ├── teams/ │ │ │ └── conduct_team.md │ │ ├── transparency_reports.md │ │ └── versioning.md │ └── spec/ │ ├── README.md │ ├── lang/ │ │ ├── README.md │ │ ├── execution.md │ │ ├── lex.md │ │ ├── libs.md │ │ ├── names.md │ │ ├── parsing.md │ │ └── semantics.md │ └── lib/ │ └── README.md ├── examples/ │ ├── BUILD │ ├── advent2024/ │ │ ├── BUILD │ │ ├── README.md │ │ ├── day10_common.carbon │ │ ├── day10_part1.carbon │ │ ├── day10_part2.carbon │ │ ├── day11_common.carbon │ │ ├── day11_part1.carbon │ │ ├── day11_part2.carbon │ │ ├── day12_common.carbon │ │ ├── day12_part1.carbon │ │ ├── day12_part2.carbon │ │ ├── day13_common.carbon │ │ ├── day13_part1.carbon │ │ ├── day13_part2.carbon │ │ ├── day14_common.carbon │ │ ├── day14_part1.carbon │ │ ├── day14_part2.carbon │ │ ├── day15_common.carbon │ │ ├── day15_part1.carbon │ │ ├── day1_common.carbon │ │ ├── day1_part1.carbon │ │ ├── day1_part2.carbon │ │ ├── day2_common.carbon │ │ ├── day2_part1.carbon │ │ ├── day2_part2.carbon │ │ ├── day3_common.carbon │ │ ├── day3_part1.carbon │ │ ├── day3_part2.carbon │ │ ├── day4_common.carbon │ │ ├── day4_part1.carbon │ │ ├── day4_part2.carbon │ │ ├── day5_common.carbon │ │ ├── day5_part1.carbon │ │ ├── day5_part2.carbon │ │ ├── day6_common.carbon │ │ ├── day6_part1.carbon │ │ ├── day6_part2.carbon │ │ ├── day7_common.carbon │ │ ├── day7_part1.carbon │ │ ├── day7_part2.carbon │ │ ├── day8_common.carbon │ │ ├── day8_part1.carbon │ │ ├── day8_part2.carbon │ │ ├── day9_common.carbon │ │ ├── day9_part1.carbon │ │ ├── day9_part2.carbon │ │ ├── io_utils.carbon │ │ └── sort.carbon │ ├── bazel/ │ │ ├── BUILD │ │ ├── MODULE.bazel │ │ ├── example.cpp │ │ ├── example_lib.cpp │ │ ├── example_lib.h │ │ └── update_module_to_nightly.py │ ├── bazel_test_runner.py │ ├── hello_world.carbon │ ├── interop/ │ │ └── cpp/ │ │ ├── BUILD │ │ ├── hello_world.carbon │ │ └── socket.carbon │ ├── re2_playground/ │ │ ├── BUILD │ │ └── re2_playground.carbon │ └── sieve.carbon ├── github_tools/ │ ├── BUILD │ ├── MODULE.bazel │ ├── README.md │ ├── WORKSPACE │ ├── __init__.py │ ├── github_helpers.py │ ├── github_helpers_test.py │ ├── pr_comments.py │ ├── pr_comments_test.py │ ├── requirements.in │ └── requirements.txt ├── proposals/ │ ├── README.md │ ├── __init__.py │ ├── p0024.md │ ├── p0029.md │ ├── p0042.md │ ├── p0044.md │ ├── p0051.md │ ├── p0063.md │ ├── p0074.md │ ├── p0083.md │ ├── p0107.md │ ├── p0113.md │ ├── p0120.md │ ├── p0140.md │ ├── p0142.md │ ├── p0143.md │ ├── p0144.md │ ├── p0149.md │ ├── p0157.md │ ├── p0162.md │ ├── p0175.md │ ├── p0179.md │ ├── p0196.md │ ├── p0198.md │ ├── p0199.md │ ├── p0253.md │ ├── p0257.md │ ├── p0285.md │ ├── p0301.md │ ├── p0339.md │ ├── p0340.md │ ├── p0353.md │ ├── p0415.md │ ├── p0426.md │ ├── p0438.md │ ├── p0444.md │ ├── p0447.md │ ├── p0524.md │ ├── p0538.md │ ├── p0540.md │ ├── p0553.md │ ├── p0555/ │ │ ├── figures.py │ │ └── yacc-parser/ │ │ ├── Makefile │ │ ├── example.l │ │ └── example.y │ ├── p0555.md │ ├── p0561.md │ ├── p0601.md │ ├── p0618.md │ ├── p0623.md │ ├── p0646.md │ ├── p0676.md │ ├── p0680.md │ ├── p0702.md │ ├── p0720.md │ ├── p0722.md │ ├── p0731.md │ ├── p0752.md │ ├── p0777.md │ ├── p0818/ │ │ └── regular_equivalence_classes.md │ ├── p0818.md │ ├── p0820.md │ ├── p0826.md │ ├── p0829.md │ ├── p0845.md │ ├── p0851.md │ ├── p0861.md │ ├── p0866.md │ ├── p0875.md │ ├── p0911.md │ ├── p0920.md │ ├── p0931.md │ ├── p0950.md │ ├── p0981.md │ ├── p0983.md │ ├── p0989.md │ ├── p0990.md │ ├── p0998.md │ ├── p1013.md │ ├── p1025.md │ ├── p1083.md │ ├── p1084.md │ ├── p1088.md │ ├── p1144.md │ ├── p1146.md │ ├── p1154.md │ ├── p1178.md │ ├── p1190.md │ ├── p1191.md │ ├── p1270.md │ ├── p1280.md │ ├── p1327.md │ ├── p1344.md │ ├── p1360.md │ ├── p1363.md │ ├── p1367.md │ ├── p1382.md │ ├── p1885.md │ ├── p1891.md │ ├── p1964.md │ ├── p1983.md │ ├── p2006.md │ ├── p2015.md │ ├── p2022.md │ ├── p2040.md │ ├── p2107.md │ ├── p2138.md │ ├── p2173.md │ ├── p2187.md │ ├── p2188.md │ ├── p2200.md │ ├── p2240.md │ ├── p2274.md │ ├── p2287.md │ ├── p2295.md │ ├── p2347.md │ ├── p2360.md │ ├── p2365.md │ ├── p2376.md │ ├── p2483.md │ ├── p2511.md │ ├── p2550.md │ ├── p2551.md │ ├── p2665.md │ ├── p2687.md │ ├── p2759.md │ ├── p2760.md │ ├── p2868.md │ ├── p2875.md │ ├── p2922.md │ ├── p2964.md │ ├── p3162.md │ ├── p3403.md │ ├── p3407.md │ ├── p3532.md │ ├── p3564.md │ ├── p3646.md │ ├── p3720.md │ ├── p3762.md │ ├── p3763.md │ ├── p3797.md │ ├── p3833.md │ ├── p3848.md │ ├── p3927.md │ ├── p3938.md │ ├── p3980.md │ ├── p4075.md │ ├── p4105.md │ ├── p4246.md │ ├── p4682.md │ ├── p4864.md │ ├── p4880.md │ ├── p5017.md │ ├── p5087.md │ ├── p5164.md │ ├── p5168.md │ ├── p5233.md │ ├── p5270.md │ ├── p5337.md │ ├── p5366.md │ ├── p5434.md │ ├── p5448.md │ ├── p5545.md │ ├── p5606.md │ ├── p5661.md │ ├── p5670.md │ ├── p5689.md │ ├── p5914.md │ ├── p6008.md │ ├── p6177.md │ ├── p6231.md │ ├── p6254.md │ ├── p6333.md │ ├── p6357.md │ ├── p6358.md │ ├── p6395.md │ ├── p6641.md │ ├── p6668.md │ ├── p6676.md │ ├── p6699.md │ ├── p6710.md │ ├── p6716.md │ ├── p6910.md │ └── scripts/ │ ├── BUILD │ ├── README.md │ ├── __init__.py │ ├── new_proposal.py │ ├── new_proposal_test.py │ └── template.md ├── pyproject.toml ├── scripts/ │ ├── BUILD │ ├── bazel_mod_deps.py │ ├── bench_runner.py │ ├── calculate_release_shas.py │ ├── check_build_graph.py │ ├── check_header_guards.py │ ├── check_sha_filenames.py │ ├── create_compdb.py │ ├── deps_for_clangd_tidy.cpp │ ├── fix_cc_deps.py │ ├── forbid_llvm_googletest.py │ ├── lldbinit.py │ ├── no_op_test.py │ ├── query_module_versions.py │ ├── run_bazel.py │ ├── run_bazelisk.py │ ├── run_buildifier.py │ ├── run_buildozer.py │ ├── scripts_utils.py │ ├── source_stats.py │ ├── sync_repos.sh │ ├── target_determinator.py │ └── workspace_status.py ├── setup.cfg ├── testing/ │ ├── README.md │ ├── base/ │ │ ├── BUILD │ │ ├── benchmark_main.cpp │ │ ├── capture_std_streams.cpp │ │ ├── capture_std_streams.h │ │ ├── file_helpers.cpp │ │ ├── file_helpers.h │ │ ├── global_exe_path.cpp │ │ ├── global_exe_path.h │ │ ├── global_exe_path_test.cpp │ │ ├── gtest_main.cpp │ │ ├── source_gen.cpp │ │ ├── source_gen.h │ │ ├── source_gen_main.cpp │ │ ├── source_gen_test.cpp │ │ ├── unified_diff_matcher.h │ │ └── unified_diff_matcher_test.cpp │ ├── file_test/ │ │ ├── BUILD │ │ ├── README.md │ │ ├── autoupdate.cpp │ │ ├── autoupdate.h │ │ ├── autoupdate_testdata.sh │ │ ├── file_test_base.cpp │ │ ├── file_test_base.h │ │ ├── file_test_base_test.cpp │ │ ├── line.h │ │ ├── manifest.cpp │ │ ├── manifest.h │ │ ├── rules.bzl │ │ ├── run_test.cpp │ │ ├── run_test.h │ │ ├── test_file.cpp │ │ ├── test_file.h │ │ ├── test_file_test.cpp │ │ └── testdata/ │ │ ├── alternating_files.carbon │ │ ├── args.carbon │ │ ├── autoupdate_split.carbon │ │ ├── autoupdate_split_standalone.carbon │ │ ├── capture_console_output.carbon │ │ ├── escaping.carbon │ │ ├── example.carbon │ │ ├── fail_example.carbon │ │ ├── fail_multi_success_overall_fail.carbon │ │ ├── file_only_re_multi_file.carbon │ │ ├── file_only_re_one_file.carbon │ │ ├── include_args.carbon │ │ ├── include_args_and_extra_args.carbon │ │ ├── include_empty.carbon │ │ ├── include_extra_args.carbon │ │ ├── include_files/ │ │ │ ├── args.carbon │ │ │ ├── empty.carbon │ │ │ ├── extra_args.carbon │ │ │ ├── no_split.carbon │ │ │ ├── recursive.carbon │ │ │ └── split.carbon │ │ ├── include_no_split.carbon │ │ ├── include_recursive.carbon │ │ ├── include_repeated.carbon │ │ ├── include_split.carbon │ │ ├── lsp_autofill.carbon │ │ ├── lsp_keywords.carbon │ │ ├── multi_success.carbon │ │ ├── multi_success_and_fail.carbon │ │ ├── multiple_file_refs_in_line.carbon │ │ ├── no_line_number.carbon │ │ ├── not_split.carbon │ │ ├── replace_content.carbon │ │ ├── replace_split_content.carbon │ │ ├── stdin.carbon │ │ ├── two_files.carbon │ │ └── unattached_multi_file.carbon │ └── fuzzing/ │ ├── BUILD │ ├── libfuzzer.h │ └── rules.bzl ├── third_party/ │ ├── README.md │ ├── examples/ │ │ ├── README.md │ │ └── re2/ │ │ ├── LICENSE │ │ └── re2.carbon │ └── llvm/ │ ├── BUILD │ ├── README.md │ ├── clang_cc1.cpp │ └── clang_cc1.h ├── toolchain/ │ ├── BUILD │ ├── README.md │ ├── autoupdate_testdata.py │ ├── base/ │ │ ├── BUILD │ │ ├── block_value_store.h │ │ ├── canonical_value_store.h │ │ ├── canonical_value_store_test.cpp │ │ ├── clang_invocation.cpp │ │ ├── clang_invocation.h │ │ ├── fixed_size_value_store.h │ │ ├── for_each_macro.h │ │ ├── id_tag.h │ │ ├── index_base.h │ │ ├── install_paths.cpp │ │ ├── install_paths.h │ │ ├── install_paths_test.cpp │ │ ├── install_paths_test_helpers.cpp │ │ ├── install_paths_test_helpers.h │ │ ├── int.cpp │ │ ├── int.h │ │ ├── int_test.cpp │ │ ├── kind_switch.h │ │ ├── kind_switch_test.cpp │ │ ├── llvm_tools.bzl │ │ ├── llvm_tools.cpp │ │ ├── llvm_tools.h │ │ ├── mem_usage.h │ │ ├── relational_value_store.h │ │ ├── runtimes_build_info.bzl │ │ ├── runtimes_build_info.tpl.h │ │ ├── runtimes_build_vars.tpl.bzl │ │ ├── shared_value_stores.h │ │ ├── shared_value_stores_test.cpp │ │ ├── test_binary.cpp │ │ ├── timings.h │ │ ├── value_ids.h │ │ ├── value_store.h │ │ ├── value_store_test.cpp │ │ ├── value_store_types.h │ │ └── yaml.h │ ├── check/ │ │ ├── BUILD │ │ ├── action.cpp │ │ ├── action.h │ │ ├── call.cpp │ │ ├── call.h │ │ ├── check.cpp │ │ ├── check.h │ │ ├── check_fuzzer.cpp │ │ ├── check_unit.cpp │ │ ├── check_unit.h │ │ ├── class.cpp │ │ ├── class.h │ │ ├── context.cpp │ │ ├── context.h │ │ ├── control_flow.cpp │ │ ├── control_flow.h │ │ ├── convert.cpp │ │ ├── convert.h │ │ ├── core_identifier.cpp │ │ ├── core_identifier.def │ │ ├── core_identifier.h │ │ ├── cpp/ │ │ │ ├── access.cpp │ │ │ ├── access.h │ │ │ ├── call.cpp │ │ │ ├── call.h │ │ │ ├── constant.cpp │ │ │ ├── constant.h │ │ │ ├── context.cpp │ │ │ ├── context.h │ │ │ ├── custom_type_mapping.cpp │ │ │ ├── custom_type_mapping.h │ │ │ ├── generate_ast.cpp │ │ │ ├── generate_ast.h │ │ │ ├── impl_lookup.cpp │ │ │ ├── impl_lookup.h │ │ │ ├── import.cpp │ │ │ ├── import.h │ │ │ ├── location.cpp │ │ │ ├── location.h │ │ │ ├── macros.cpp │ │ │ ├── macros.h │ │ │ ├── operators.cpp │ │ │ ├── operators.h │ │ │ ├── overload_resolution.cpp │ │ │ ├── overload_resolution.h │ │ │ ├── thunk.cpp │ │ │ ├── thunk.h │ │ │ ├── type_mapping.cpp │ │ │ └── type_mapping.h │ │ ├── custom_witness.cpp │ │ ├── custom_witness.h │ │ ├── decl_introducer_state.h │ │ ├── decl_name_stack.cpp │ │ ├── decl_name_stack.h │ │ ├── deduce.cpp │ │ ├── deduce.h │ │ ├── deferred_definition_worklist.cpp │ │ ├── deferred_definition_worklist.h │ │ ├── diagnostic_emitter.cpp │ │ ├── diagnostic_emitter.h │ │ ├── diagnostic_helpers.h │ │ ├── dump.cpp │ │ ├── eval.cpp │ │ ├── eval.h │ │ ├── eval_inst.cpp │ │ ├── eval_inst.h │ │ ├── facet_type.cpp │ │ ├── facet_type.h │ │ ├── full_pattern_stack.h │ │ ├── function.cpp │ │ ├── function.h │ │ ├── fuzzer_corpus/ │ │ │ ├── 008cb7b685ac13c051abc8e778bb56c6be8c920d │ │ │ ├── 016a8df42375f098eaf2bda8fa7ed1cdd322e51d │ │ │ ├── 01b6283c2544cf135b8c219478e5b0f8da2325ed │ │ │ ├── 02163cf92aea19e124c4a5838dae8f0ed340fbfa │ │ │ ├── 0234ac9a0b945c8a32fc87ee33f9a6d8f9933019 │ │ │ ├── 023877f085e3eedd9a0f13951f04859de07944e9 │ │ │ ├── 0272d8c7b9a7495efe6a19e9e814ecbe55823213 │ │ │ ├── 03a61af13df6ab601a52a1784995ad7b84d04555 │ │ │ ├── 03f031da904f8bb48412e82acaa77957fde2445b │ │ │ ├── 0444b366b646a800d860d362876dec36afb41e42 │ │ │ ├── 048e2bfcc725778f118b704acf2679b712bc6ded │ │ │ ├── 068ac29021d30e21dc976177ae56f68096da6740 │ │ │ ├── 06c71952ad52d77790a1a5eb631eaa0759800fec │ │ │ ├── 078ec0d032fa51fabffef18fc82712e9536b013e │ │ │ ├── 07b0e6716d0adf617012146642d9cba39b9b006b │ │ │ ├── 081d7c4effd1e9cc6129090e3c831e1af2633e62 │ │ │ ├── 0831e49d5a00cd7ae6512b7cd8f782c1ed305fa5 │ │ │ ├── 083cd63392060ddfbc062ce0ba14a7ac89441a1f │ │ │ ├── 0900a9afaef22b798df75b576e8096343a98903a │ │ │ ├── 09762ec95a5e7e5a232b735f95e808da510678fd │ │ │ ├── 0c1db2e8ec3df9d6adf52c88ba67333e2059da97 │ │ │ ├── 0ce1b0382ddf1eaf0a21e39a1932936edd0c7b63 │ │ │ ├── 0d824e8e58e5a28b74bfde959ca83df292a33702 │ │ │ ├── 0d9b1f8863ca4b08680e246aeb58b12737afc902 │ │ │ ├── 0dc65251b81300d504287a2f7be97054bd22d386 │ │ │ ├── 0fc717e2905c26bce32a4591acdd0f46859765a2 │ │ │ ├── 10204d82b32ad9dc256b595048de9efabd93067f │ │ │ ├── 11253cbc5df4b6b891cc68ac943599555d3e30dc │ │ │ ├── 12b56f338f82115bc9733846f50bc5863ca3fd9c │ │ │ ├── 12c9b3f16c56bbb271111f4bdc8a2401dab16154 │ │ │ ├── 138a0e4547e111ad5cc545e9b19a1e430348636b │ │ │ ├── 13f9d954261112e0d9131eb97327515395550d00 │ │ │ ├── 1551a409e456b8102c5b44313303aad008b2a06d │ │ │ ├── 155ec88a933f5a3ae411ee810eb89f5631bff2d5 │ │ │ ├── 15a7fdcf1eda6e722c03d2cccbc93ddf75681128 │ │ │ ├── 162348262c5e2797cf53a49adec2c1df056268d2 │ │ │ ├── 16fb4fcee5f2fbd4b5b4e65358ab00e800f67db9 │ │ │ ├── 17261c3182bd1de4382f0d3fef3917a949c36e32 │ │ │ ├── 17dc6eb1bbb8fd9671f46aa63924cf1a76837a2c │ │ │ ├── 184c236354c35072383945d9e6bdf0c005b5d20e │ │ │ ├── 18909d083c5867fc03a952211bd61ca688bb908e │ │ │ ├── 1986b4fb3bc5c60ad5f4faa1b8003beb70a7ece8 │ │ │ ├── 1a5c8022aeb946ee3551e569dad4ed36cdbcd44f │ │ │ ├── 1afe54e48b1e5bcdd7c8949513e0b2a3fbde1a65 │ │ │ ├── 1b85cf86024d9fcfe011f6998861b289aac97e60 │ │ │ ├── 1baac08b0dfe6bf0b1157757b14ad4a389ba4693 │ │ │ ├── 1be679f324846aca4379ea4939981410425b287f │ │ │ ├── 1c56a65ba702fd27f5a72caf9c5e9170312db350 │ │ │ ├── 1c7eed351b92a87b8fd0c60d58e1916f3ae242d6 │ │ │ ├── 1d35c2bfb3606a1875a7d9218edc05b242cc75aa │ │ │ ├── 1e1c015742738ddadeecb98baeb2988e9debfb6f │ │ │ ├── 1e55c67e9a55008a70df08daf7964b04102f5dd7 │ │ │ ├── 1f6409865ff8ddad8bc5771a00d12932a67fb7dd │ │ │ ├── 20e03ea11ed959b8d8c2815133dbf0b31e2164c2 │ │ │ ├── 2106211513dcb519a34bb65957c8fd0c85f75b16 │ │ │ ├── 219513dc72d769293ba33b019ebd2282b7833404 │ │ │ ├── 2268ee6ebd54f257f7fe853a15c6ef7785108cea │ │ │ ├── 228ce64e3803e722ecf32dc43b499d6726756daf │ │ │ ├── 2335a5e08cccb3045cf4b8bc8e68feb442d2324f │ │ │ ├── 236e8e31b5787b58b23bb57e00b101f89e8535b0 │ │ │ ├── 2379517a82b1d7db9c20bb4922230d42bbffa68a │ │ │ ├── 2394164e5f5c1d546a3da770f5b6e05994f3a30f │ │ │ ├── 241b6bbd6226547b2428d4a5cb71fed63597659d │ │ │ ├── 24a8948753be6ae079a443e207af49e88785fc27 │ │ │ ├── 24f96b44faf9d816c26ec36acd1f5c4562d0f1e9 │ │ │ ├── 2689f156236c25d70a265b1261f8bea8cfef527f │ │ │ ├── 283a045fd6c041aa47df273da411d601e7dada48 │ │ │ ├── 291711a108e2ed04586d978f1ac4a730277ffbb8 │ │ │ ├── 2a9df05598f94d7fefa11dd1451237e8c7b99f60 │ │ │ ├── 2b504c78bc414daf63ba4618ebbd36bd92004d6e │ │ │ ├── 2c05dbcf73a57af47793888b03384cdd43d5c594 │ │ │ ├── 2d316de15bbe6edbbbeb3bc65e9350a7b53e0abe │ │ │ ├── 2df8a8f5865c0e813ed26a874c2ea0daf7421086 │ │ │ ├── 2e02f9befd32eac2983de14b6ac8cf6cef960b4c │ │ │ ├── 2e05abea6fd46fc2b5315dca60d3b56e5b407fd4 │ │ │ ├── 2e1aea3071937c4307dd48ebb2b1df3bc9e8716f │ │ │ ├── 300e45006b32eec72e90d33d3cd6685680448957 │ │ │ ├── 301dc6263f2a34b4630aa88e940f42acf020f160 │ │ │ ├── 3037fd1afeda607e889fe3e73fb1088d13c9f8bf │ │ │ ├── 306f80d197d3d627368e752f1ad6aa42d2e89aa6 │ │ │ ├── 3082cf2a6045d00b6e81d3ae14a3a3d56314b988 │ │ │ ├── 310b6d61f64901b21169432ef6bbc13a2b602376 │ │ │ ├── 3116bbbb3bae537a09e62f5c8331a431d6c56b1e │ │ │ ├── 3119cacb5f47007660225c4a63a934eff83091a2 │ │ │ ├── 317bdb0ba661c19d4be7dc1a9e0eb74529c80bba │ │ │ ├── 31e5d9b3712744f0659429e330ba44958eadce7c │ │ │ ├── 3292cacd15031b51dcfdd4d623e0ef30c66eb86b │ │ │ ├── 32ffc355b444c61ccd2d80ca466aa47b4f5b4d4b │ │ │ ├── 33358007d39274e780a256e497695c3fe46f1029 │ │ │ ├── 3476b8646b5b6b64fba9ab4b662850eff9312d41 │ │ │ ├── 358f451f412939d0e0863704128cbc4756839499 │ │ │ ├── 37934fb6e5e6d857d3227ea76872ce6a3bc4d630 │ │ │ ├── 37b6c00cc00d3ea562a9a67d365b1617be17cda4 │ │ │ ├── 396c9121839a9d9866282f987fb283fa86556474 │ │ │ ├── 3a414cdb79537ba6113eebe683a258098caebe9c │ │ │ ├── 3abd6729fc8141ee2cf270432da6cfe0bc85c3b6 │ │ │ ├── 3ade1694dc4a9b14c34f03ad62b68872d22dee82 │ │ │ ├── 3c57cd00b20c65032e36beeae83dbc34da90d96a │ │ │ ├── 3cf84352b479800d6fc3cf96f8493945c2211b0f │ │ │ ├── 3d3543e880e8d01a5f3de62c8e5137fb6cddf741 │ │ │ ├── 3e2b7a2777680a17f8ee4fdc72e6e2eeccb06bbe │ │ │ ├── 41de2a58a6891d68e779f2017ef063decc946cd4 │ │ │ ├── 423c611625411a3e7e1a514f78b3f56d4482852f │ │ │ ├── 4250d5daad936e112c495fba82c027d68b64d5af │ │ │ ├── 43403b1e7abd01b7c671e6defefd7531f5c6702e │ │ │ ├── 4340b66cecbff7df2c6932cb03440a29e55bdfd3 │ │ │ ├── 4455db071205f7d5bdb9fc2fae623c64067b42d7 │ │ │ ├── 47afc3df9dd4fcdebc2c8a1ef7b0dc87435dc016 │ │ │ ├── 483ceff680765b8a2c582e18111fe71a754e1844 │ │ │ ├── 48b15c4b985c9b46b8e95e3c822ad1f65108ae39 │ │ │ ├── 48f161ebf1c515fbc92fc959814c40d6394b5360 │ │ │ ├── 498eecf3c6bcc6332b955ae5c2260a5451e73add │ │ │ ├── 4a035921222f3bc29547b41d7ba8b6da0d6d1918 │ │ │ ├── 4a2b48d7a92dd98fe609be64224be82772f55d0b │ │ │ ├── 4a53102a9f8003845552337ac998169e11d09d50 │ │ │ ├── 4ab72b4e37f3135ed58cbad67238c92072799757 │ │ │ ├── 4be506d3d29dbde616deda5b966e460b702d8078 │ │ │ ├── 4c74c032df7c917af8e4bc833b777bb2bec80211 │ │ │ ├── 4ce8273ccba8f2b4cf0d8b721bf35b100e19e0b2 │ │ │ ├── 4d2449569a2536b234828155b7449ee755d60a1c │ │ │ ├── 4dd5200e6960e3538c180263762afa41dafd9e85 │ │ │ ├── 4f3bfc326a24c79bc61dd3f657879de3c4c92393 │ │ │ ├── 50fa09b0616f12e3550c8d8db69371cc2909a299 │ │ │ ├── 51be544495cddd6b63fa15569d1f9c15da88d404 │ │ │ ├── 52aef5e56788c511815ca05612341c56cf96bc7c │ │ │ ├── 547ffea2b538ce83c02ff5e112290b3c72f944dc │ │ │ ├── 54887e4d15dfaada3929936e0a970b82c1dfb62e │ │ │ ├── 54dab9151ed3293f3aec67e58aa20e26aa632a98 │ │ │ ├── 54e697b7fd97a4ca280483aac15d69a1fe995994 │ │ │ ├── 55bafb0b85e56d740867aa2eced4270cee6022ec │ │ │ ├── 55cffdb200c153f12a0b584d8f24754b6a5cdbca │ │ │ ├── 56aa3535abaf9c5df81b685d1eb0a71f5d80f43c │ │ │ ├── 5730670e62da8cb89476867288e21c654dcde6e5 │ │ │ ├── 5aa8d527e2f0f75c4013ddde704d0f7f6afa4ad5 │ │ │ ├── 5b0309128112c0a5ac3a2ffcd03a66d062224d48 │ │ │ ├── 5b19e864b0b73fde9f4ec0903239424fb6c036ca │ │ │ ├── 5b3a56d8084b2c87582aa9cb7f0ff56cfde0f0b5 │ │ │ ├── 5bbdca0e72629685bfc1132d00e114960b2fcb82 │ │ │ ├── 5bd0c3c2901ec5b0052a7a9a76eb84cb611d1c42 │ │ │ ├── 5d0c180874af1a76aaa137f32b3eecedb408ceef │ │ │ ├── 5d8390990020b1a44590ce1b13c484d1075dc50f │ │ │ ├── 5da086526436d984a0e8b01ca482b178852084e5 │ │ │ ├── 5dce231f6950396fe0965788b6d8f24cf033b4b4 │ │ │ ├── 5e45e4e1b479e4388e38277605992ce44cea1e57 │ │ │ ├── 5e968b8fb1b7fa51cea34638d04c2b0c75b3c92f │ │ │ ├── 5f89b222e699d47c8a41bb74abebc81f76578cf0 │ │ │ ├── 5f954e8ee1ace4e6a57d4defefc7d87c30deadbe │ │ │ ├── 6003b3dcd82396edba816e537507a435839fe1f8 │ │ │ ├── 616631daa1b2f3fb44b606c331569f490f341778 │ │ │ ├── 631701015ee748a5c3d22fe34412f7b00c32a4af │ │ │ ├── 6485f068351f2f9eb9124bc4032d5fdad43fc611 │ │ │ ├── 64cabff382c905a7598bdb67fdc450e7d675a22e │ │ │ ├── 64d95aad6787b2388d89783f9f19de16ad3bf546 │ │ │ ├── 66400bd8514c00f6cf25eff62d2e48445f57182c │ │ │ ├── 6771c2bd2677e585863903ea55026479ccd17870 │ │ │ ├── 67aaf0d5127285b5bc7f230c34abeb2f046cc18c │ │ │ ├── 68adfc912ab9c22031735ea064b9abfb759e860b │ │ │ ├── 68aebf293bd4ae573c514613325fcf490c8d98c4 │ │ │ ├── 69ae9f5b1bed179e5cccdfb4f7351a3fa7825d46 │ │ │ ├── 69bab187ee3bed045d1af0a1325d5daabbfebcb7 │ │ │ ├── 6b59783e9131166ae1396039351b8920c1532eda │ │ │ ├── 6bcc2a0c19cfffb661acb141bc57e351b31fe545 │ │ │ ├── 6c0ade4e7511d55f3594435cbab260c61c6eb433 │ │ │ ├── 6c4ef5a1d954eca2c4076a9ab38c85ed546d7d78 │ │ │ ├── 6c7a321e86789ab26b7bdb3e5709364a0d4cd686 │ │ │ ├── 6d8712954da7301cf9eba612efb43217ff2c9c9d │ │ │ ├── 6d9f91f6f40fb8ddf061ae76c5f2beaefe6c8bee │ │ │ ├── 6e7b40cf5df2608cac7f3a034875623185812bdc │ │ │ ├── 6ee7d990d6d84b254f8ff655a2a9061ff87df262 │ │ │ ├── 6f39466681bb52b19ab384450e0bbb67598fd2ce │ │ │ ├── 6f97d913a4f39bd5e8501dec2cc7bcf87e23e9d0 │ │ │ ├── 70f5b6fb23691d532301ffb58ab24649f6454d8c │ │ │ ├── 710cfbe95b3fc5a9bb53fce89ef1a0abe2e09e85 │ │ │ ├── 71a53f6b5ecb6b4368eee4224d614609f4ff4d56 │ │ │ ├── 723771f1826c803bbd50afe6e703268a89d6edab │ │ │ ├── 72a4e959879e3eaffd79dc30af15cb2ada073648 │ │ │ ├── 73235c4067e2da4fc1dbfe403e2354fd0598b639 │ │ │ ├── 7405fcca02b54d584c14af5493adc45467b2fdd9 │ │ │ ├── 7471dc3d8630cf15ed61e384f95aeb80117b3e69 │ │ │ ├── 760ef78e2194d159e3160c5f1ed9c2d14461da19 │ │ │ ├── 78280fde00fc74d5293e1df394c1cf6012277f2c │ │ │ ├── 79189aed1ece423113c07026aa1d7b2c9f79050c │ │ │ ├── 79df4004383e0a52581c0b97ce9cf662e0032101 │ │ │ ├── 7a700c8e06316b9d48f34dcbdd6c79c9ca739570 │ │ │ ├── 7b1de3f5a9e1d40dbd7c4b342ec1da84cceb016f │ │ │ ├── 7c27cb6aeaf045bb89f59207280218ae5712357d │ │ │ ├── 7c829002e321ac8fbc1b7bcc46f58f3032646bfb │ │ │ ├── 7ca9fe97a14160b8620cee481aba4f12ef5e239f │ │ │ ├── 7cc77a9919de591289f6f01b7f3df2d2daa58297 │ │ │ ├── 7f5cf073e6e1bfe73c3256257aa83cffc3456ce5 │ │ │ ├── 80e834464c299e6fa3fed5551d31712d4ae0adc2 │ │ │ ├── 8232c8a2839a7671df6947987108ae25e6027767 │ │ │ ├── 82431aad891633c384f8e3dc8af548420cfc207a │ │ │ ├── 827994a1d2fbe88e785e28ac7fe3dc2d8043e8ad │ │ │ ├── 8308e1750041583282a6c913cc4e2c4fb9f48353 │ │ │ ├── 83941bf67b0cd2e4d3f7502b2bb40a83889bf78b │ │ │ ├── 83e710c959e54010bceb9b0aad34ded746b6eb2a │ │ │ ├── 842268df9223d721e951b5865fb9d9c684129679 │ │ │ ├── 85017c3fdc47972d87932e4089f349f6c324ece9 │ │ │ ├── 85cde7cf9e3e8e282a265f653b60cb0ef60b8b48 │ │ │ ├── 85f4667d9dee7230ec98fd7a1b1c7455a6a4ab5d │ │ │ ├── 874eddf3ad6e08849a2214088ee525df154883c6 │ │ │ ├── 877257bc5b2b43345bd445518317b709596eac7c │ │ │ ├── 88031d95585417463e2939d40ff53cc9c8a12221 │ │ │ ├── 89a6c64c03c3683482810e5362c2417be45ed07c │ │ │ ├── 89af2ff0da3a9c42a04a7b2b3bbc05755060273d │ │ │ ├── 89fab5bae29d8a674f37cce24cbedeff198cfb5d │ │ │ ├── 8aed12f4aa27c3ac5d1b3703d202428777fe983b │ │ │ ├── 8cbe098387a007f60eb4209259915d6d85532fa5 │ │ │ ├── 8e0b56d89ffc8caa0feaf38b36642aea4c1be601 │ │ │ ├── 8f27934a9f00dec469d67e7336b7d8f52ec4abd6 │ │ │ ├── 8fab0dc50d28280cdeab5def1ae75d70f860a1a7 │ │ │ ├── 90ce2617c868946d7fa04649195ab1ab5fff4b20 │ │ │ ├── 92904c2b09ee456d0d1a2cf3d69dfd26fd190641 │ │ │ ├── 92b290bf1d1d95e5a7e7813af2199781f92b350e │ │ │ ├── 92b34177d4245d4172436708ba988ac171eb9eb2 │ │ │ ├── 932e6052bd882919514042cc953293cd63659b5e │ │ │ ├── 94a3ce6126281beaf24a4595ac2f27a2c6e04b2d │ │ │ ├── 964b65adb9c33d86539ec025f5cf745e1c4e61e1 │ │ │ ├── 96656d19f3e5b82f968f3b4282ba524a814595b3 │ │ │ ├── 968568fe7d320af2e8e6adfc30e3f81dd2f8818e │ │ │ ├── 9706cab087de61b00158e0ac6d755f1352589bdd │ │ │ ├── 9745b84a597bf13955fb7d208e94f2795aeb7813 │ │ │ ├── 9787ad2c566323a856a6225b91dbbfbf8e8d3ee8 │ │ │ ├── 979f63c217bbde19dd385c49d577dc3090d54317 │ │ │ ├── 98b45893e4f4c1b0a893d2562df5e8559555f223 │ │ │ ├── 9909b29e2f33447fa30b3b261cc7c672740e1236 │ │ │ ├── 9a3f71755e720ffdafd5d7112321c8796d46fc62 │ │ │ ├── 9af28460c915ac87c79b91ff233959dc031cf3cc │ │ │ ├── 9bfc6a4a473aa74403e38910c8843a87cbee0dd9 │ │ │ ├── 9cadde2867b9fe7867d7d217139982632f640cc0 │ │ │ ├── 9f27297beea402d07125cf1bb0c7081ceb401bb5 │ │ │ ├── a01f53c98465a54636523de8f520a4a20bdd1403 │ │ │ ├── a091cfa270edff9d7192eeb8f594ea77b09bba3d │ │ │ ├── a0c798f9f52432ff1f72f8aa1ba9f7830a105f09 │ │ │ ├── a13c6779bdbc457a609f9667267beed711896f6f │ │ │ ├── a1786976927e1d3184a978427bc5bb1cfa7aefa4 │ │ │ ├── a1bba96a1c0e17301f64b08c70a3df48170bd730 │ │ │ ├── a1f193dcf4589332a9130b6551c1afa7fe79656a │ │ │ ├── a2cfb003a0e59b4e421af3880c551363b8ff78f0 │ │ │ ├── a2de12dcf9c2df7a3ac23b82f802b3d076e6ad86 │ │ │ ├── a3cfd48c1f4a33aad9d1552ddbe46d44a2436384 │ │ │ ├── a6bf2f116b1e98f7546e9bce3b9d2946d8074aa2 │ │ │ ├── a780174f6f7b59148dbb023be4e3659a92541020 │ │ │ ├── a8a90bb993589759a112cf567143c47be5a89ac4 │ │ │ ├── a8a999063a872e7de8a55734e0f4a198efeeaafb │ │ │ ├── a93b46ffd68cc67330744e0ed1099a14bdb56b47 │ │ │ ├── a9c4f7786b6b8ba23fa102e8ba2296752120ae84 │ │ │ ├── ab1d10a814f9068e09642a678671d76511342dd4 │ │ │ ├── ab97f0b738451b9e490d7cadaacc8081ae676240 │ │ │ ├── ac4a09688df096b7967d347059fe514b011a0c5e │ │ │ ├── ad153a95127f656da230d82cdba152a50f871556 │ │ │ ├── aedf34fefa255ebc431491de0b68156a724221ae │ │ │ ├── afd20d50d83e001bbd7f6db02ca1e2e0a6d1f309 │ │ │ ├── b08132223d41827e2b8cd8f849ffb7ac4af4e993 │ │ │ ├── b0a0c7b28dfac0b65bba3cf74ebd1100adf7c0b7 │ │ │ ├── b0e276497e7036c6b3c90deeff71a055256ed5e3 │ │ │ ├── b1f68671cb99a64e7c35d8453fd1f13759823e49 │ │ │ ├── b3738109fea51776656084b4904eb6161501f39e │ │ │ ├── b39d93d15f5f9693f621391de119e616687fbfa3 │ │ │ ├── b3e6b065765987b63bb992656e202cd2753d579d │ │ │ ├── b46338342e410d950a63cae90807b22ad2674468 │ │ │ ├── b4a3cec4308215fd5e3c772c7f3ce019c9c5b026 │ │ │ ├── b4e1923164eed688e64d926007d332b4d9a7bafb │ │ │ ├── b5025c7140bdccbefb9823d5906815f7aea5f555 │ │ │ ├── b585c712c89adab1146766f73010d48b0a9d9de5 │ │ │ ├── b6b53ceb779903ab3b483786d48390423bbbe797 │ │ │ ├── b8695514d6502d5b1b9e09cc61711898dbe1e486 │ │ │ ├── b9d290495c4dce17305b5d9d29e2ae54c6242b54 │ │ │ ├── ba4fa6565defd5cc4b8921ed0d478be330350658 │ │ │ ├── ba5b59911968d39433b9003d02e3ff4fa7b4b177 │ │ │ ├── bab137edbdcbcf1220dc018b55d49066378523e9 │ │ │ ├── bb0ee9a7c7f604c41cfef744a974c6603df4b0db │ │ │ ├── bc8de3dd156c3193c1a5209c7023499abe9bbdde │ │ │ ├── bd4d126e58ef4f15642eeba1d41a830e217a4ad1 │ │ │ ├── bd5f439085bdf3ab0cbe796b3637cf9139fb1914 │ │ │ ├── bee65bde4d7da5558b60a5dacb8a5826486b1df2 │ │ │ ├── bff4337d7117fd0883b58ac02469d740648a8072 │ │ │ ├── c07f23f6980fe961e62413e101aa21f1696e3e41 │ │ │ ├── c294ac41a73adf8be9ed88cbc20458d7fad20f68 │ │ │ ├── c2efeeb24454e0da713af7391d28f6b1f1820d6c │ │ │ ├── c3c888581796e961aed506d812407f76c99c1659 │ │ │ ├── c42ace6ddd8e547dced2e0ed23181b826788913d │ │ │ ├── c4d643c7e8081d02c8467600200be7be29d58d8d │ │ │ ├── c5cd5cb2197a889e003767ff5c0de2040094ac96 │ │ │ ├── c5ec8aaab0717d6b901c8bc42757700a342aaf32 │ │ │ ├── c6812fb07715a0206afeac4ac2275784a158cf63 │ │ │ ├── c855a3994277ba5408dbbadaa49d92b52f2467d2 │ │ │ ├── c86a4df5e4046b6cffb57a8563514e8ea459147a │ │ │ ├── c8c23b0c0d7655f81ac96fdb089bcb7a82c25950 │ │ │ ├── ca5382377576c400f270660fcce6472e8664deb9 │ │ │ ├── ca69be54b8273f334ab8c7db0ad454c5ff0291c9 │ │ │ ├── cb2c2e620fb2192d723bcecff7a8cadd41775362 │ │ │ ├── cbd23efc1218cbf81ebdcb503cfeaaf6925c29d2 │ │ │ ├── cbe88d560d36df81f2d7ae7910a44e6926d46f7e │ │ │ ├── cc055d77975b79cf9281d71e00182aaeef7ccf1b │ │ │ ├── cc2d5a48c731ae4058d884411e7235b2ae4bf829 │ │ │ ├── cc774069d5739d66235983360e14f8740de6d975 │ │ │ ├── ccaf2f7ac19101566bddd7e0b4bcbb5434a38bf4 │ │ │ ├── cd940109a8870955e7eb8e03004304e9dc7fc808 │ │ │ ├── cded9327e7355942a46c8e25c8328275dbef50bf │ │ │ ├── ce2080f786cedebec8a0147b450b3a9a85365258 │ │ │ ├── cff3eed86965820544d6454ed5d9cf0735a7bf48 │ │ │ ├── d0d95941336a7a4ac3202f363708dbc685900f64 │ │ │ ├── d1ec799a11b9190d5a39b84e45558cba93c31fdd │ │ │ ├── d2a4af5c22209dd1503f38d0a36d75e9b21a8bd4 │ │ │ ├── d3fc2d9df6573461a1ea20c7997f27dfd9f1ee8f │ │ │ ├── d533854f44af409027dfc8c1c288f90ebf111192 │ │ │ ├── d5a91fb15ac01afd500538b5679647289146045a │ │ │ ├── d5af10d2b47f3859bcc15da85fa3d077b942f43a │ │ │ ├── d5f20d0057317362bc92258e8c06f6b03c2926a4 │ │ │ ├── d60be9abac4bd2cb92b3432b7c10771def0c009e │ │ │ ├── d698884570f5265a3cd5ab0b822ed28fba467d13 │ │ │ ├── d77e0fdacf1d3d9c711e24c8b088e2da26d98746 │ │ │ ├── d7de67b2375a361719d942f4cf49ea0a9cdf2c45 │ │ │ ├── d81e51e207bd2a4077a15890f337487a93060f7b │ │ │ ├── d87e64cb8726f55d05773858cbe6979e550c0f5c │ │ │ ├── d967a050c50d0b75e8f64611560b0f221bd9525f │ │ │ ├── da795d40354dd38e3d00b82c7790eb4d6c8ec2b5 │ │ │ ├── db5937dfd7d8e43ea817fbcac0b83ad38ccae116 │ │ │ ├── dbd0b78b1bfd4878676ac3d882aee2b797d7af15 │ │ │ ├── dccc5c3818f328ee1fd500f899c37ac0af7b2182 │ │ │ ├── dcde55e2a4e60d6bf3dd574d15a425e30279d53c │ │ │ ├── dd662568f4fe92a74f53b026f11638725f966e0b │ │ │ ├── de46bc1199653f93b7db2c1c39e7ca61dfa6b810 │ │ │ ├── e028180f3ba177567c45f5149e96c0313d1b175f │ │ │ ├── e03df3e0be45ac48d5ad2d59b67b677678a76de6 │ │ │ ├── e0afc6bd74d5718e01a5888df16600e8a4645f63 │ │ │ ├── e2c590fff5cd302d986f3fed578e45fa09836052 │ │ │ ├── e3895b1888cb48ad3ab9c9cd3b78debedb0ad5e9 │ │ │ ├── e45bda3cd3778f2372ce18cc0d9ff7bbf1f50ba5 │ │ │ ├── e48c19846a9d9a8418fbf4e363eb69d720fdab9d │ │ │ ├── e4b64d16a5d43dc7e5bee135714611d2929364a6 │ │ │ ├── e657c790f4c10f0678e90dc52facef51920a94d3 │ │ │ ├── e673a37cb310aaa88cbc216044663eb2c49ae76f │ │ │ ├── e6da0876675b17c319ea55bf19ecbc12b779d3df │ │ │ ├── e77f7b118df2a85fa55e8798a40f343b59ceab88 │ │ │ ├── e78aa6328c0b365b87a34a2dd1cb8e5270e04a0b │ │ │ ├── e9dff14915dda34282efb04147877d0fc83ba2a0 │ │ │ ├── ea454bd2c8e7219c267ca0b01e27c649999c1451 │ │ │ ├── ea82b6f22005103932231c0641c74b125fcc7fb0 │ │ │ ├── eaa7bb9e4dbf3576f9587cd88b29976ca2024fe6 │ │ │ ├── eaca8f02a5ca41d60a68be9186e5f79538ae2719 │ │ │ ├── ebb86c45027660ce57bbadede73ab1a5dbc43e35 │ │ │ ├── ecd12a6a72a7c5cdf93dd9bb7637cb35f65c1d00 │ │ │ ├── ecdb111d6c89a22189b961cb47ab1e73df8ed430 │ │ │ ├── ed0769767eea7a7e0ee58d52278417c698605719 │ │ │ ├── ed28ae6948896b9be69c556380b22f8c6fdee8d1 │ │ │ ├── ed306e9bbd8d275d000beb5cf551284765c97c23 │ │ │ ├── ee45e8d94544f8f79e7b6330365a9426715f47f9 │ │ │ ├── ee8db01890a0f9c520e30f1d75c44efabbbbdb01 │ │ │ ├── eed2aa2d6ca72b1972eaf2bb206a0591a2ce9a95 │ │ │ ├── eeea07311e1c93999ce139f71cc5e6be83ae938d │ │ │ ├── ef87d1f2a1afcc05941ade6a5bd57c9fdea83b34 │ │ │ ├── f0122e82b0498ec11a6a9d68e6de8440d891b272 │ │ │ ├── f0457f1e27c67e8425559e6b753cca540cf69f65 │ │ │ ├── f0ddb293345e5013757ebfacd7769ef92acd41d4 │ │ │ ├── f1986481baa957b8dd977f8ca3e8c4107d492799 │ │ │ ├── f19c5bb5147295d791fb5d6899d93ed7a23c072f │ │ │ ├── f1b24ed86c606cde53c498fc6411053d0ae22ced │ │ │ ├── f1d88daac0be722a2a2e6bedd09b39e240043653 │ │ │ ├── f2b43d44db5dec0cca4bc6fc63f487fabae1b941 │ │ │ ├── f39ac0d9de634c2a723f879231e77ef600e036d9 │ │ │ ├── f4f4353df90e753e832eb974c782e27dbe6753d4 │ │ │ ├── f669b57c4be29746f06e2ad46b428769a791dd07 │ │ │ ├── f71cbd0475ddb41b78a0d439aa78ae1e2eec0df5 │ │ │ ├── f72b3c6b4ade452568f6834dd6e5896d1772ff2f │ │ │ ├── f72fd742e291347d71ef000e002dc8e8baa13830 │ │ │ ├── f7b8bc9f4b18f013697267cc9c966ae65f4cd7e3 │ │ │ ├── f7c747dc5107c73b9c4bfe6f24267f04ba16d129 │ │ │ ├── f91e230199efd6367f8622f5a6eaa54d1d4d8e90 │ │ │ ├── fa056963c20a8a8dc279cb2e2a9b769338cc92da │ │ │ ├── fa11a132da6f10be8e8aa10cbd25ea5d8107a614 │ │ │ ├── fb4504a84cc35d086689b9f93eb9f365331f4750 │ │ │ ├── fbd855380068533d0148c6011a302bf07cae0612 │ │ │ ├── fc2e6307261474cbadb9046919ff50710904d4d5 │ │ │ ├── fc927b8e82af4dcdaa2495e28ff7269c43456ff2 │ │ │ └── fdc040e7e474c3ec5da7044a80e2df6bfcb80917 │ │ ├── generic.cpp │ │ ├── generic.h │ │ ├── generic_region_stack.cpp │ │ ├── generic_region_stack.h │ │ ├── global_init.cpp │ │ ├── global_init.h │ │ ├── handle.h │ │ ├── handle_alias.cpp │ │ ├── handle_array.cpp │ │ ├── handle_binding_pattern.cpp │ │ ├── handle_call_expr.cpp │ │ ├── handle_choice.cpp │ │ ├── handle_class.cpp │ │ ├── handle_codeblock.cpp │ │ ├── handle_export.cpp │ │ ├── handle_expr_statement.cpp │ │ ├── handle_file.cpp │ │ ├── handle_form_literal.cpp │ │ ├── handle_function.cpp │ │ ├── handle_if_expr.cpp │ │ ├── handle_if_statement.cpp │ │ ├── handle_impl.cpp │ │ ├── handle_import_and_package.cpp │ │ ├── handle_index.cpp │ │ ├── handle_interface.cpp │ │ ├── handle_lambda.cpp │ │ ├── handle_let_and_var.cpp │ │ ├── handle_literal.cpp │ │ ├── handle_loop_statement.cpp │ │ ├── handle_match.cpp │ │ ├── handle_modifier.cpp │ │ ├── handle_name.cpp │ │ ├── handle_named_constraint.cpp │ │ ├── handle_namespace.cpp │ │ ├── handle_noop.cpp │ │ ├── handle_observe.cpp │ │ ├── handle_operator.cpp │ │ ├── handle_paren_expr.cpp │ │ ├── handle_pattern_list.cpp │ │ ├── handle_require.cpp │ │ ├── handle_return_statement.cpp │ │ ├── handle_struct.cpp │ │ ├── handle_tuple_literal.cpp │ │ ├── handle_where.cpp │ │ ├── impl.cpp │ │ ├── impl.h │ │ ├── impl_lookup.cpp │ │ ├── impl_lookup.h │ │ ├── impl_validation.cpp │ │ ├── impl_validation.h │ │ ├── import.cpp │ │ ├── import.h │ │ ├── import_ref.cpp │ │ ├── import_ref.h │ │ ├── inst.cpp │ │ ├── inst.h │ │ ├── inst_block_stack.cpp │ │ ├── inst_block_stack.h │ │ ├── interface.cpp │ │ ├── interface.h │ │ ├── keyword_modifier_set.cpp │ │ ├── keyword_modifier_set.h │ │ ├── lexical_lookup.h │ │ ├── literal.cpp │ │ ├── literal.h │ │ ├── member_access.cpp │ │ ├── member_access.h │ │ ├── merge.cpp │ │ ├── merge.h │ │ ├── modifiers.cpp │ │ ├── modifiers.h │ │ ├── name_component.cpp │ │ ├── name_component.h │ │ ├── name_lookup.cpp │ │ ├── name_lookup.h │ │ ├── name_ref.cpp │ │ ├── name_ref.h │ │ ├── name_scope.cpp │ │ ├── name_scope.h │ │ ├── node_id_traversal.cpp │ │ ├── node_id_traversal.h │ │ ├── node_stack.cpp │ │ ├── node_stack.h │ │ ├── operator.cpp │ │ ├── operator.h │ │ ├── param_and_arg_refs_stack.h │ │ ├── pattern.cpp │ │ ├── pattern.h │ │ ├── pattern_match.cpp │ │ ├── pattern_match.h │ │ ├── pending_block.h │ │ ├── pointer_dereference.cpp │ │ ├── pointer_dereference.h │ │ ├── region_stack.h │ │ ├── return.cpp │ │ ├── return.h │ │ ├── scope_index.h │ │ ├── scope_stack.cpp │ │ ├── scope_stack.h │ │ ├── subst.cpp │ │ ├── subst.h │ │ ├── testdata/ │ │ │ ├── alias/ │ │ │ │ ├── basics.carbon │ │ │ │ ├── builtins.carbon │ │ │ │ ├── export_name.carbon │ │ │ │ ├── import.carbon │ │ │ │ ├── import_access.carbon │ │ │ │ ├── import_order.carbon │ │ │ │ ├── in_namespace.carbon │ │ │ │ ├── local.carbon │ │ │ │ └── preserve_in_type_printing.carbon │ │ │ ├── array/ │ │ │ │ ├── basics.carbon │ │ │ │ ├── bound_values.carbon │ │ │ │ ├── element_mismatches.carbon │ │ │ │ ├── import.carbon │ │ │ │ ├── index_not_literal.carbon │ │ │ │ └── init_dependent_bound.carbon │ │ │ ├── as/ │ │ │ │ ├── adapter_conversion.carbon │ │ │ │ ├── basics.carbon │ │ │ │ ├── const.carbon │ │ │ │ ├── maybe_unformed.carbon │ │ │ │ ├── partial.carbon │ │ │ │ ├── unsafe_as.carbon │ │ │ │ └── var_init.carbon │ │ │ ├── basics/ │ │ │ │ ├── dump_prelude.carbon │ │ │ │ ├── dump_sem_ir_ranges.carbon │ │ │ │ ├── dump_sem_ir_ranges_ignore.carbon │ │ │ │ ├── dump_sem_ir_ranges_only.carbon │ │ │ │ ├── duplicate_name_same_line.carbon │ │ │ │ ├── empty.carbon │ │ │ │ ├── include_in_dumps.carbon │ │ │ │ ├── multi_error.carbon │ │ │ │ ├── name_lookup.carbon │ │ │ │ ├── parens.carbon │ │ │ │ ├── raw_identifier.carbon │ │ │ │ ├── raw_sem_ir/ │ │ │ │ │ ├── builtins.carbon │ │ │ │ │ ├── cpp_interop.carbon │ │ │ │ │ ├── multifile.carbon │ │ │ │ │ ├── multifile_with_textual_ir.carbon │ │ │ │ │ ├── one_file.carbon │ │ │ │ │ └── one_file_with_textual_ir.carbon │ │ │ │ └── verbose.carbon │ │ │ ├── builtins/ │ │ │ │ ├── bool/ │ │ │ │ │ ├── eq.carbon │ │ │ │ │ ├── make_type.carbon │ │ │ │ │ └── neq.carbon │ │ │ │ ├── char/ │ │ │ │ │ └── convert_checked.carbon │ │ │ │ ├── char_literal/ │ │ │ │ │ └── make_type.carbon │ │ │ │ ├── cpp/ │ │ │ │ │ └── std/ │ │ │ │ │ └── initializer_list/ │ │ │ │ │ └── make.carbon │ │ │ │ ├── float/ │ │ │ │ │ ├── add.carbon │ │ │ │ │ ├── add_assign.carbon │ │ │ │ │ ├── convert_checked.carbon │ │ │ │ │ ├── div.carbon │ │ │ │ │ ├── div_assign.carbon │ │ │ │ │ ├── eq.carbon │ │ │ │ │ ├── greater.carbon │ │ │ │ │ ├── greater_eq.carbon │ │ │ │ │ ├── less.carbon │ │ │ │ │ ├── less_eq.carbon │ │ │ │ │ ├── make_type.carbon │ │ │ │ │ ├── mul.carbon │ │ │ │ │ ├── mul_assign.carbon │ │ │ │ │ ├── negate.carbon │ │ │ │ │ ├── neq.carbon │ │ │ │ │ ├── sub.carbon │ │ │ │ │ └── sub_assign.carbon │ │ │ │ ├── float_literal/ │ │ │ │ │ └── make_type.carbon │ │ │ │ ├── form/ │ │ │ │ │ └── make_type.carbon │ │ │ │ ├── int/ │ │ │ │ │ ├── and.carbon │ │ │ │ │ ├── and_assign.carbon │ │ │ │ │ ├── complement.carbon │ │ │ │ │ ├── convert.carbon │ │ │ │ │ ├── convert_checked.carbon │ │ │ │ │ ├── eq.carbon │ │ │ │ │ ├── greater.carbon │ │ │ │ │ ├── greater_eq.carbon │ │ │ │ │ ├── left_shift.carbon │ │ │ │ │ ├── left_shift_assign.carbon │ │ │ │ │ ├── less.carbon │ │ │ │ │ ├── less_eq.carbon │ │ │ │ │ ├── make_type_signed.carbon │ │ │ │ │ ├── make_type_unsigned.carbon │ │ │ │ │ ├── neq.carbon │ │ │ │ │ ├── or.carbon │ │ │ │ │ ├── or_assign.carbon │ │ │ │ │ ├── right_shift.carbon │ │ │ │ │ ├── right_shift_assign.carbon │ │ │ │ │ ├── sadd.carbon │ │ │ │ │ ├── sadd_assign.carbon │ │ │ │ │ ├── sdiv.carbon │ │ │ │ │ ├── sdiv_assign.carbon │ │ │ │ │ ├── smod.carbon │ │ │ │ │ ├── smod_assign.carbon │ │ │ │ │ ├── smul.carbon │ │ │ │ │ ├── smul_assign.carbon │ │ │ │ │ ├── snegate.carbon │ │ │ │ │ ├── ssub.carbon │ │ │ │ │ ├── ssub_assign.carbon │ │ │ │ │ ├── uadd.carbon │ │ │ │ │ ├── uadd_assign.carbon │ │ │ │ │ ├── udiv.carbon │ │ │ │ │ ├── udiv_assign.carbon │ │ │ │ │ ├── umod.carbon │ │ │ │ │ ├── umod_assign.carbon │ │ │ │ │ ├── umul.carbon │ │ │ │ │ ├── umul_assign.carbon │ │ │ │ │ ├── unegate.carbon │ │ │ │ │ ├── usub.carbon │ │ │ │ │ ├── usub_assign.carbon │ │ │ │ │ ├── xor.carbon │ │ │ │ │ └── xor_assign.carbon │ │ │ │ ├── int_literal/ │ │ │ │ │ └── make_type.carbon │ │ │ │ ├── maybe_unformed/ │ │ │ │ │ └── make_type.carbon │ │ │ │ ├── no_op.carbon │ │ │ │ ├── pointer/ │ │ │ │ │ ├── is_null.carbon │ │ │ │ │ └── make_null.carbon │ │ │ │ ├── print/ │ │ │ │ │ ├── char.carbon │ │ │ │ │ └── int.carbon │ │ │ │ ├── read/ │ │ │ │ │ └── char.carbon │ │ │ │ └── type/ │ │ │ │ └── and.carbon │ │ │ ├── choice/ │ │ │ │ ├── basic.carbon │ │ │ │ ├── generic.carbon │ │ │ │ └── params.carbon │ │ │ ├── class/ │ │ │ │ ├── abstract/ │ │ │ │ │ ├── abstract.carbon │ │ │ │ │ ├── fail_abstract_in_struct.carbon │ │ │ │ │ └── fail_abstract_in_tuple.carbon │ │ │ │ ├── access/ │ │ │ │ │ ├── access_modifers.carbon │ │ │ │ │ ├── import_access.carbon │ │ │ │ │ ├── inheritance_access.carbon │ │ │ │ │ ├── method_access.carbon │ │ │ │ │ └── todo_access_modifiers.carbon │ │ │ │ ├── adapter/ │ │ │ │ │ ├── adapt.carbon │ │ │ │ │ ├── adapt_copy.carbon │ │ │ │ │ ├── convert_incomplete.carbon │ │ │ │ │ ├── extend_adapt.carbon │ │ │ │ │ ├── fail_adapt_bad_decl.carbon │ │ │ │ │ ├── fail_adapt_bad_type.carbon │ │ │ │ │ ├── fail_adapt_modifiers.carbon │ │ │ │ │ ├── fail_adapt_with_base.carbon │ │ │ │ │ ├── fail_adapt_with_subobjects.carbon │ │ │ │ │ └── init_adapt.carbon │ │ │ │ ├── basic.carbon │ │ │ │ ├── complete_in_member_fn.carbon │ │ │ │ ├── cross_package_import.carbon │ │ │ │ ├── destroy_calls.carbon │ │ │ │ ├── export_name.carbon │ │ │ │ ├── extern.carbon │ │ │ │ ├── extern_library.carbon │ │ │ │ ├── fail_compound_type_mismatch.carbon │ │ │ │ ├── fail_convert_to_invalid.carbon │ │ │ │ ├── fail_error_recovery.carbon │ │ │ │ ├── fail_import_misuses.carbon │ │ │ │ ├── fail_incomplete.carbon │ │ │ │ ├── fail_init.carbon │ │ │ │ ├── fail_memaccess_category.carbon │ │ │ │ ├── fail_member_of_let.carbon │ │ │ │ ├── fail_modifiers.carbon │ │ │ │ ├── fail_redeclaration_scope.carbon │ │ │ │ ├── fail_redefinition.carbon │ │ │ │ ├── fail_scope.carbon │ │ │ │ ├── fail_unknown_member.carbon │ │ │ │ ├── field/ │ │ │ │ │ ├── comp_time_field.carbon │ │ │ │ │ ├── compound_field.carbon │ │ │ │ │ ├── fail_field_modifiers.carbon │ │ │ │ │ ├── fail_todo_field_initializer.carbon │ │ │ │ │ ├── fail_unbound_field.carbon │ │ │ │ │ ├── field_access.carbon │ │ │ │ │ └── field_access_in_value.carbon │ │ │ │ ├── forward_declared.carbon │ │ │ │ ├── generic/ │ │ │ │ │ ├── adapt.carbon │ │ │ │ │ ├── base_is_generic.carbon │ │ │ │ │ ├── basic.carbon │ │ │ │ │ ├── call.carbon │ │ │ │ │ ├── complete_in_conversion.carbon │ │ │ │ │ ├── empty_params.carbon │ │ │ │ │ ├── field.carbon │ │ │ │ │ ├── generic_vs_params.carbon │ │ │ │ │ ├── import.carbon │ │ │ │ │ ├── init.carbon │ │ │ │ │ ├── member_access.carbon │ │ │ │ │ ├── member_inline.carbon │ │ │ │ │ ├── member_lookup.carbon │ │ │ │ │ ├── member_out_of_line.carbon │ │ │ │ │ ├── member_type.carbon │ │ │ │ │ ├── method_deduce.carbon │ │ │ │ │ ├── redeclare.carbon │ │ │ │ │ ├── self.carbon │ │ │ │ │ └── stringify.carbon │ │ │ │ ├── implicit_import.carbon │ │ │ │ ├── import.carbon │ │ │ │ ├── import_forward_decl.carbon │ │ │ │ ├── import_indirect.carbon │ │ │ │ ├── import_member_cycle.carbon │ │ │ │ ├── import_struct_cycle.carbon │ │ │ │ ├── incomplete_ref.carbon │ │ │ │ ├── indirect_import_member.carbon │ │ │ │ ├── inheritance/ │ │ │ │ │ ├── base.carbon │ │ │ │ │ ├── base_field.carbon │ │ │ │ │ ├── base_function_unqualified.carbon │ │ │ │ │ ├── base_method.carbon │ │ │ │ │ ├── base_method_qualified.carbon │ │ │ │ │ ├── base_method_shadow.carbon │ │ │ │ │ ├── derived_to_base.carbon │ │ │ │ │ ├── fail_base_as_declared_name.carbon │ │ │ │ │ ├── fail_base_bad_type.carbon │ │ │ │ │ ├── fail_base_method_define.carbon │ │ │ │ │ ├── fail_base_misplaced.carbon │ │ │ │ │ ├── fail_base_modifiers.carbon │ │ │ │ │ ├── fail_base_no_extend.carbon │ │ │ │ │ ├── fail_base_repeated.carbon │ │ │ │ │ ├── fail_base_unbound.carbon │ │ │ │ │ ├── fail_derived_to_base.carbon │ │ │ │ │ ├── fail_extend_cycle.carbon │ │ │ │ │ ├── import_base.carbon │ │ │ │ │ └── self_conversion.carbon │ │ │ │ ├── init.carbon │ │ │ │ ├── init_as.carbon │ │ │ │ ├── init_nested.carbon │ │ │ │ ├── local.carbon │ │ │ │ ├── method/ │ │ │ │ │ ├── fail_generic_method.carbon │ │ │ │ │ ├── fail_method.carbon │ │ │ │ │ ├── fail_method_modifiers.carbon │ │ │ │ │ ├── fail_method_redefinition.carbon │ │ │ │ │ ├── fail_out_of_line_decl.carbon │ │ │ │ │ ├── generic_method.carbon │ │ │ │ │ ├── method.carbon │ │ │ │ │ └── static_method.carbon │ │ │ │ ├── name_poisoning.carbon │ │ │ │ ├── nested.carbon │ │ │ │ ├── nested_name.carbon │ │ │ │ ├── no_definition_in_impl_file.carbon │ │ │ │ ├── partial/ │ │ │ │ │ ├── init.carbon │ │ │ │ │ └── qualifier.carbon │ │ │ │ ├── redeclaration.carbon │ │ │ │ ├── redeclaration_introducer.carbon │ │ │ │ ├── reenter_scope.carbon │ │ │ │ ├── reorder.carbon │ │ │ │ ├── reorder_qualified.carbon │ │ │ │ ├── scope.carbon │ │ │ │ ├── self/ │ │ │ │ │ ├── fail_ref_self.carbon │ │ │ │ │ ├── fail_self.carbon │ │ │ │ │ ├── fail_self_param.carbon │ │ │ │ │ ├── fail_self_type_member.carbon │ │ │ │ │ ├── raw_self.carbon │ │ │ │ │ ├── raw_self_type.carbon │ │ │ │ │ ├── self.carbon │ │ │ │ │ └── self_type.carbon │ │ │ │ ├── syntactic_merge.carbon │ │ │ │ ├── syntactic_merge_literal.carbon │ │ │ │ └── virtual_modifiers.carbon │ │ │ ├── const/ │ │ │ │ ├── basics.carbon │ │ │ │ └── import.carbon │ │ │ ├── deduce/ │ │ │ │ ├── array.carbon │ │ │ │ ├── binding_pattern.carbon │ │ │ │ ├── generic_type.carbon │ │ │ │ ├── int_float.carbon │ │ │ │ ├── symbolic_facets.carbon │ │ │ │ ├── tuple.carbon │ │ │ │ ├── type_operator.carbon │ │ │ │ ├── value_with_type_through_access.carbon │ │ │ │ └── where.carbon │ │ │ ├── eval/ │ │ │ │ ├── aggregates.carbon │ │ │ │ ├── binding.carbon │ │ │ │ ├── branch.carbon │ │ │ │ ├── call.carbon │ │ │ │ ├── recursion.carbon │ │ │ │ ├── symbolic.carbon │ │ │ │ └── unexpected_runtime.carbon │ │ │ ├── facet/ │ │ │ │ ├── access.carbon │ │ │ │ ├── aggregate_through_access.carbon │ │ │ │ ├── call_combined_impl_witness.carbon │ │ │ │ ├── combine.carbon │ │ │ │ ├── convert_class_type_to_facet_type.carbon │ │ │ │ ├── convert_class_type_to_generic_facet_value.carbon │ │ │ │ ├── convert_class_value_to_facet_value_value.carbon │ │ │ │ ├── convert_class_value_to_generic_facet_value_value.carbon │ │ │ │ ├── convert_facet_type_to_facet_value.carbon │ │ │ │ ├── convert_facet_value_as_type_knows_original_type.carbon │ │ │ │ ├── convert_facet_value_to_facet_value.carbon │ │ │ │ ├── convert_facet_value_to_itself.carbon │ │ │ │ ├── convert_facet_value_to_narrowed_facet_type.carbon │ │ │ │ ├── convert_facet_value_value_to_blanket_impl.carbon │ │ │ │ ├── convert_facet_value_value_to_generic_facet_value_value.carbon │ │ │ │ ├── convert_facet_value_value_to_itself.carbon │ │ │ │ ├── convert_interface.carbon │ │ │ │ ├── early_rewrites.carbon │ │ │ │ ├── facet_assoc_const.carbon │ │ │ │ ├── fail_convert_class_type_to_generic_facet_value.carbon │ │ │ │ ├── fail_convert_facet_value_to_missing_impl.carbon │ │ │ │ ├── fail_convert_type_erased_type_to_facet.carbon │ │ │ │ ├── fail_deduction_uses_runtime_type_conversion.carbon │ │ │ │ ├── fail_incomplete.carbon │ │ │ │ ├── fail_namespace_type.carbon │ │ │ │ ├── identify_self_canonicalized.carbon │ │ │ │ ├── named_constant.carbon │ │ │ │ ├── nested_facet_types.carbon │ │ │ │ ├── period_self.carbon │ │ │ │ ├── require_constrains_self.carbon │ │ │ │ ├── require_import.carbon │ │ │ │ ├── require_invalid.carbon │ │ │ │ ├── require_satisified.carbon │ │ │ │ ├── runtime_value.carbon │ │ │ │ ├── self_in_interface_param.carbon │ │ │ │ ├── tuple_and_struct_type_literal.carbon │ │ │ │ ├── tuple_and_struct_type_value.carbon │ │ │ │ ├── validate_impl_constraints.carbon │ │ │ │ └── validate_rewrite_constraints.carbon │ │ │ ├── for/ │ │ │ │ ├── actual.carbon │ │ │ │ ├── basic.carbon │ │ │ │ └── pattern.carbon │ │ │ ├── function/ │ │ │ │ ├── builtin/ │ │ │ │ │ ├── adapted_type.carbon │ │ │ │ │ ├── call.carbon │ │ │ │ │ ├── call_from_operator.carbon │ │ │ │ │ ├── definition.carbon │ │ │ │ │ ├── fail_redefined.carbon │ │ │ │ │ ├── fail_unknown.carbon │ │ │ │ │ ├── import.carbon │ │ │ │ │ ├── method.carbon │ │ │ │ │ └── positional.carbon │ │ │ │ ├── call/ │ │ │ │ │ ├── alias.carbon │ │ │ │ │ ├── empty_struct.carbon │ │ │ │ │ ├── empty_tuple.carbon │ │ │ │ │ ├── eval_musteval.carbon │ │ │ │ │ ├── fail_explicit_self_param.carbon │ │ │ │ │ ├── fail_not_callable.carbon │ │ │ │ │ ├── fail_param_count.carbon │ │ │ │ │ ├── fail_param_type.carbon │ │ │ │ │ ├── fail_return_type_mismatch.carbon │ │ │ │ │ ├── fail_runtime_implicit_param.carbon │ │ │ │ │ ├── form.carbon │ │ │ │ │ ├── i32.carbon │ │ │ │ │ ├── more_param_ir.carbon │ │ │ │ │ ├── params_one.carbon │ │ │ │ │ ├── params_one_comma.carbon │ │ │ │ │ ├── params_two.carbon │ │ │ │ │ ├── params_two_comma.carbon │ │ │ │ │ ├── params_zero.carbon │ │ │ │ │ ├── prefer_unqualified_lookup.carbon │ │ │ │ │ ├── ref.carbon │ │ │ │ │ └── return_implicit.carbon │ │ │ │ ├── declaration/ │ │ │ │ │ ├── eval_musteval.carbon │ │ │ │ │ ├── export_name.carbon │ │ │ │ │ ├── extern.carbon │ │ │ │ │ ├── extern_library.carbon │ │ │ │ │ ├── extern_library_for_default.carbon │ │ │ │ │ ├── extern_library_from_default.carbon │ │ │ │ │ ├── fail_import_incomplete_return.carbon │ │ │ │ │ ├── fail_modifiers.carbon │ │ │ │ │ ├── fail_param_in_type.carbon │ │ │ │ │ ├── fail_param_redecl.carbon │ │ │ │ │ ├── fail_redecl.carbon │ │ │ │ │ ├── fail_todo_no_params.carbon │ │ │ │ │ ├── implicit_import.carbon │ │ │ │ │ ├── import.carbon │ │ │ │ │ ├── name_poisoning.carbon │ │ │ │ │ ├── no_definition_in_impl_file.carbon │ │ │ │ │ ├── param_same_name.carbon │ │ │ │ │ ├── pattern_in_signature.carbon │ │ │ │ │ ├── ref.carbon │ │ │ │ │ └── simple.carbon │ │ │ │ ├── definition/ │ │ │ │ │ ├── basics.carbon │ │ │ │ │ ├── extern.carbon │ │ │ │ │ ├── extern_library.carbon │ │ │ │ │ ├── fail_decl_param_mismatch.carbon │ │ │ │ │ ├── fail_local_decl.carbon │ │ │ │ │ ├── fail_redef.carbon │ │ │ │ │ ├── forward_decl.carbon │ │ │ │ │ ├── implicit_import.carbon │ │ │ │ │ ├── import.carbon │ │ │ │ │ ├── import_access.carbon │ │ │ │ │ ├── order.carbon │ │ │ │ │ ├── params_one.carbon │ │ │ │ │ ├── params_one_comma.carbon │ │ │ │ │ ├── params_two.carbon │ │ │ │ │ ├── params_two_comma.carbon │ │ │ │ │ ├── params_zero.carbon │ │ │ │ │ └── syntactic_merge.carbon │ │ │ │ └── generic/ │ │ │ │ ├── call.carbon │ │ │ │ ├── call_method_on_generic_facet.carbon │ │ │ │ ├── deduce.carbon │ │ │ │ ├── deduce_nested_facet_value.carbon │ │ │ │ ├── fail_deduce_imported_function.carbon │ │ │ │ ├── fail_type_param_mismatch.carbon │ │ │ │ ├── forward_decl.carbon │ │ │ │ ├── import_specific.carbon │ │ │ │ ├── indirect_generic_type.carbon │ │ │ │ ├── param_in_type.carbon │ │ │ │ ├── redeclare.carbon │ │ │ │ ├── resolve_used.carbon │ │ │ │ ├── return_slot.carbon │ │ │ │ ├── template_param.carbon │ │ │ │ ├── type_param.carbon │ │ │ │ ├── type_param_scope.carbon │ │ │ │ └── undefined.carbon │ │ │ ├── generic/ │ │ │ │ ├── call_basic_depth.carbon │ │ │ │ ├── complete_type.carbon │ │ │ │ ├── dependent_param.carbon │ │ │ │ ├── dot_self_symbolic_type.carbon │ │ │ │ ├── extend_type_completion.carbon │ │ │ │ ├── fail_generic_copy.carbon │ │ │ │ ├── forward_decl.carbon │ │ │ │ ├── identify_specific_facet_type.carbon │ │ │ │ ├── local.carbon │ │ │ │ ├── template/ │ │ │ │ │ ├── convert.carbon │ │ │ │ │ ├── fail_todo_template_access_assoc_const.carbon │ │ │ │ │ ├── member_access.carbon │ │ │ │ │ └── unimplemented.carbon │ │ │ │ └── template_dependence.carbon │ │ │ ├── global/ │ │ │ │ └── basics.carbon │ │ │ ├── if/ │ │ │ │ └── basics.carbon │ │ │ ├── if_expr/ │ │ │ │ ├── basic.carbon │ │ │ │ ├── constant_condition.carbon │ │ │ │ ├── control_flow.carbon │ │ │ │ ├── fail_not_in_function.carbon │ │ │ │ ├── fail_partial_constant.carbon │ │ │ │ ├── nested.carbon │ │ │ │ └── struct.carbon │ │ │ ├── impl/ │ │ │ │ ├── assoc_const_self.carbon │ │ │ │ ├── basic.carbon │ │ │ │ ├── compound.carbon │ │ │ │ ├── custom_witness/ │ │ │ │ │ └── int_fits_in.carbon │ │ │ │ ├── declaration.carbon │ │ │ │ ├── empty.carbon │ │ │ │ ├── error_recovery.carbon │ │ │ │ ├── eval_musteval.carbon │ │ │ │ ├── extend_final.carbon │ │ │ │ ├── extend_impl.carbon │ │ │ │ ├── extend_impl_generic.carbon │ │ │ │ ├── fail_alias.carbon │ │ │ │ ├── fail_call_invalid.carbon │ │ │ │ ├── fail_extend_impl_forall.carbon │ │ │ │ ├── fail_extend_impl_scope.carbon │ │ │ │ ├── fail_extend_impl_type_as.carbon │ │ │ │ ├── fail_extend_non_interface.carbon │ │ │ │ ├── fail_extend_partially_defined_interface.carbon │ │ │ │ ├── fail_extend_undefined_interface.carbon │ │ │ │ ├── fail_impl_as_scope.carbon │ │ │ │ ├── fail_impl_bad_assoc_const.carbon │ │ │ │ ├── fail_impl_bad_assoc_fn.carbon │ │ │ │ ├── fail_impl_bad_interface.carbon │ │ │ │ ├── fail_impl_bad_type.carbon │ │ │ │ ├── fail_redefinition.carbon │ │ │ │ ├── fail_self_type_mismatch.carbon │ │ │ │ ├── fail_todo_form_thunk.carbon │ │ │ │ ├── forward_decls.carbon │ │ │ │ ├── generic_redeclaration.carbon │ │ │ │ ├── impl_as.carbon │ │ │ │ ├── impl_as_named_constraint.carbon │ │ │ │ ├── impl_assoc_const.carbon │ │ │ │ ├── impl_assoc_const_with_prelude.carbon │ │ │ │ ├── impl_forall.carbon │ │ │ │ ├── impl_self_as.carbon │ │ │ │ ├── impl_thunk.carbon │ │ │ │ ├── impl_thunk_min_prelude.carbon │ │ │ │ ├── impl_where_redecl.carbon │ │ │ │ ├── import_builtin_call.carbon │ │ │ │ ├── import_canonical_witnesses.carbon │ │ │ │ ├── import_compound.carbon │ │ │ │ ├── import_extend_impl.carbon │ │ │ │ ├── import_generic.carbon │ │ │ │ ├── import_impl_with_no_interface.carbon │ │ │ │ ├── import_interface_assoc_const.carbon │ │ │ │ ├── import_self.carbon │ │ │ │ ├── import_self_specific.carbon │ │ │ │ ├── import_thunk.carbon │ │ │ │ ├── import_use_generic.carbon │ │ │ │ ├── incomplete.carbon │ │ │ │ ├── interface_args.carbon │ │ │ │ ├── lookup/ │ │ │ │ │ ├── alias.carbon │ │ │ │ │ ├── canonical_query_self.carbon │ │ │ │ │ ├── fail_alias_impl_not_found.carbon │ │ │ │ │ ├── fail_function_types.carbon │ │ │ │ │ ├── fail_todo_undefined_impl.carbon │ │ │ │ │ ├── find_in_final.carbon │ │ │ │ │ ├── generic.carbon │ │ │ │ │ ├── impl_cycle.carbon │ │ │ │ │ ├── impl_forall.carbon │ │ │ │ │ ├── impl_overlap.carbon │ │ │ │ │ ├── impl_overlap_wrapped_types.carbon │ │ │ │ │ ├── import.carbon │ │ │ │ │ ├── import_error.carbon │ │ │ │ │ ├── import_final.carbon │ │ │ │ │ ├── instance_method.carbon │ │ │ │ │ ├── lookup_interface_with_enclosing_generic_inside_rewrite_constraint.carbon │ │ │ │ │ ├── overlap_with_non_types.carbon │ │ │ │ │ ├── specialization.carbon │ │ │ │ │ ├── specialization_poison.carbon │ │ │ │ │ ├── specialization_with_symbolic_rewrite.carbon │ │ │ │ │ ├── specific_args.carbon │ │ │ │ │ ├── struct.carbon │ │ │ │ │ ├── symbolic_lookup.carbon │ │ │ │ │ ├── transitive.carbon │ │ │ │ │ └── unused_generic_binding.carbon │ │ │ │ ├── multiple_extend.carbon │ │ │ │ ├── name_poisoning.carbon │ │ │ │ ├── no_definition_in_impl_file.carbon │ │ │ │ ├── orphan.carbon │ │ │ │ ├── redeclaration.carbon │ │ │ │ ├── self_in_class.carbon │ │ │ │ ├── self_in_signature.carbon │ │ │ │ ├── todo_impl_with_unrelated_fn.carbon │ │ │ │ ├── use_assoc_entity.carbon │ │ │ │ └── using_invalid_interface.carbon │ │ │ ├── index/ │ │ │ │ ├── array_element_access.carbon │ │ │ │ ├── expr_category.carbon │ │ │ │ ├── fail_array_large_index.carbon │ │ │ │ ├── fail_array_non_int_indexing.carbon │ │ │ │ ├── fail_array_out_of_bound_access.carbon │ │ │ │ ├── fail_expr_category.carbon │ │ │ │ ├── fail_invalid_base.carbon │ │ │ │ ├── fail_name_not_found.carbon │ │ │ │ ├── fail_negative_indexing.carbon │ │ │ │ └── fail_non_tuple_access.carbon │ │ │ ├── interface/ │ │ │ │ ├── as_type.carbon │ │ │ │ ├── as_type_of_type.carbon │ │ │ │ ├── assoc_const.carbon │ │ │ │ ├── assoc_const_in_generic.carbon │ │ │ │ ├── basic.carbon │ │ │ │ ├── compound_member_access.carbon │ │ │ │ ├── default_fn.carbon │ │ │ │ ├── export_name.carbon │ │ │ │ ├── fail_add_member_outside_definition.carbon │ │ │ │ ├── fail_assoc_const_alias.carbon │ │ │ │ ├── fail_assoc_const_bad_default.carbon │ │ │ │ ├── fail_assoc_fn_invalid_use.carbon │ │ │ │ ├── fail_definition_imported.carbon │ │ │ │ ├── fail_duplicate.carbon │ │ │ │ ├── fail_generic_redeclaration.carbon │ │ │ │ ├── fail_lookup_in_type_type.carbon │ │ │ │ ├── fail_lookup_undefined.carbon │ │ │ │ ├── fail_member_lookup.carbon │ │ │ │ ├── fail_modifiers.carbon │ │ │ │ ├── fail_redeclare_member.carbon │ │ │ │ ├── fail_todo_assoc_const_default.carbon │ │ │ │ ├── fail_todo_define_default_fn_inline.carbon │ │ │ │ ├── fail_todo_define_default_fn_out_of_line.carbon │ │ │ │ ├── fail_todo_modifiers.carbon │ │ │ │ ├── fail_type_as_facet.carbon │ │ │ │ ├── final.carbon │ │ │ │ ├── generic.carbon │ │ │ │ ├── generic_binding_after_assoc_const.carbon │ │ │ │ ├── generic_default_fn.carbon │ │ │ │ ├── generic_import.carbon │ │ │ │ ├── generic_method.carbon │ │ │ │ ├── generic_vs_params.carbon │ │ │ │ ├── import.carbon │ │ │ │ ├── import_access.carbon │ │ │ │ ├── import_interface_decl.carbon │ │ │ │ ├── incomplete.carbon │ │ │ │ ├── local.carbon │ │ │ │ ├── member_lookup.carbon │ │ │ │ ├── name_poisoning.carbon │ │ │ │ ├── require.carbon │ │ │ │ ├── require_invalid_modifiers.carbon │ │ │ │ ├── self.carbon │ │ │ │ ├── syntactic_merge.carbon │ │ │ │ └── todo_define_not_default.carbon │ │ │ ├── interop/ │ │ │ │ └── cpp/ │ │ │ │ ├── bad_flags.carbon │ │ │ │ ├── bad_import.carbon │ │ │ │ ├── builtins.carbon │ │ │ │ ├── builtins.llp64.carbon │ │ │ │ ├── builtins.lp64.carbon │ │ │ │ ├── class/ │ │ │ │ │ ├── abstract.carbon │ │ │ │ │ ├── access.carbon │ │ │ │ │ ├── base.carbon │ │ │ │ │ ├── class.carbon │ │ │ │ │ ├── constructor.carbon │ │ │ │ │ ├── field.carbon │ │ │ │ │ ├── invalid.carbon │ │ │ │ │ ├── method.carbon │ │ │ │ │ ├── nested.carbon │ │ │ │ │ ├── non_aggregate_init.carbon │ │ │ │ │ ├── struct.carbon │ │ │ │ │ ├── template.carbon │ │ │ │ │ └── union.carbon │ │ │ │ ├── constexpr.carbon │ │ │ │ ├── cpp_diagnostics.carbon │ │ │ │ ├── cpp_namespace.carbon │ │ │ │ ├── deref.carbon │ │ │ │ ├── enum/ │ │ │ │ │ ├── anonymous.carbon │ │ │ │ │ ├── convert.carbon │ │ │ │ │ ├── copy.carbon │ │ │ │ │ ├── fixed.carbon │ │ │ │ │ ├── incomplete.carbon │ │ │ │ │ ├── invalid.carbon │ │ │ │ │ ├── member.carbon │ │ │ │ │ ├── scoped.carbon │ │ │ │ │ ├── unscoped.carbon │ │ │ │ │ └── using.carbon │ │ │ │ ├── fail_fuzzing.carbon │ │ │ │ ├── fail_todo_arithmetic_types_unmapped.carbon │ │ │ │ ├── file_not_found.carbon │ │ │ │ ├── function/ │ │ │ │ │ ├── arithmetic_types_bridged.carbon │ │ │ │ │ ├── arithmetic_types_direct.carbon │ │ │ │ │ ├── class.carbon │ │ │ │ │ ├── decayed_param.carbon │ │ │ │ │ ├── default_arg.carbon │ │ │ │ │ ├── extern_c.carbon │ │ │ │ │ ├── full_semir.carbon │ │ │ │ │ ├── function.carbon │ │ │ │ │ ├── in_template.carbon │ │ │ │ │ ├── inline.carbon │ │ │ │ │ ├── multiple_too_few_args_calls.carbon │ │ │ │ │ ├── operators.carbon │ │ │ │ │ ├── overloads.carbon │ │ │ │ │ ├── param_unsupported.carbon │ │ │ │ │ ├── pointer.carbon │ │ │ │ │ ├── qualified_param.carbon │ │ │ │ │ ├── reference.carbon │ │ │ │ │ ├── return.carbon │ │ │ │ │ ├── struct.carbon │ │ │ │ │ ├── template.carbon │ │ │ │ │ ├── thunk_ast.carbon │ │ │ │ │ ├── union.carbon │ │ │ │ │ ├── variadic.carbon │ │ │ │ │ └── void_pointer.carbon │ │ │ │ ├── impls/ │ │ │ │ │ ├── as.carbon │ │ │ │ │ ├── copy.carbon │ │ │ │ │ ├── destroy.carbon │ │ │ │ │ └── implicit_as.carbon │ │ │ │ ├── import.carbon │ │ │ │ ├── include.carbon │ │ │ │ ├── include_paths.carbon │ │ │ │ ├── inline.carbon │ │ │ │ ├── macros.carbon │ │ │ │ ├── modules.carbon │ │ │ │ ├── multiple_imports.carbon │ │ │ │ ├── namespace.carbon │ │ │ │ ├── reverse/ │ │ │ │ │ └── simple.carbon │ │ │ │ ├── stdlib/ │ │ │ │ │ ├── initializer_list.carbon │ │ │ │ │ └── string_view.carbon │ │ │ │ ├── template/ │ │ │ │ │ ├── alias_template.carbon │ │ │ │ │ ├── argument_count.carbon │ │ │ │ │ ├── class_template.carbon │ │ │ │ │ ├── concept.carbon │ │ │ │ │ ├── generic_call.carbon │ │ │ │ │ ├── non_type_param.carbon │ │ │ │ │ ├── template_template_param.carbon │ │ │ │ │ ├── type_param.carbon │ │ │ │ │ └── var_template.carbon │ │ │ │ ├── typedef.carbon │ │ │ │ ├── unsupported_decl_type.carbon │ │ │ │ ├── unused_internal.carbon │ │ │ │ ├── var/ │ │ │ │ │ ├── global.carbon │ │ │ │ │ └── namespace.carbon │ │ │ │ ├── void.carbon │ │ │ │ └── void_pointer.carbon │ │ │ ├── let/ │ │ │ │ ├── compile_time_bindings.carbon │ │ │ │ ├── convert.carbon │ │ │ │ ├── fail_duplicate_decl.carbon │ │ │ │ ├── fail_generic.carbon │ │ │ │ ├── fail_generic_import.carbon │ │ │ │ ├── fail_missing_value.carbon │ │ │ │ ├── fail_modifiers.carbon │ │ │ │ ├── fail_use_in_init.carbon │ │ │ │ ├── generic.carbon │ │ │ │ ├── generic_import.carbon │ │ │ │ ├── global.carbon │ │ │ │ ├── import.carbon │ │ │ │ ├── import_access.carbon │ │ │ │ ├── local.carbon │ │ │ │ ├── ref.carbon │ │ │ │ └── shadowed_decl.carbon │ │ │ ├── main_run/ │ │ │ │ ├── README.md │ │ │ │ ├── fail_mismatch_params.carbon │ │ │ │ ├── fail_mismatch_return.carbon │ │ │ │ ├── no_return.carbon │ │ │ │ └── return_i32.carbon │ │ │ ├── named_constraint/ │ │ │ │ ├── basic.carbon │ │ │ │ ├── convert.carbon │ │ │ │ ├── empty.carbon │ │ │ │ ├── empty_generic.carbon │ │ │ │ ├── generic.carbon │ │ │ │ ├── import_constraint_decl.carbon │ │ │ │ ├── incomplete.carbon │ │ │ │ ├── invalid_members.carbon │ │ │ │ ├── require.carbon │ │ │ │ └── require_invalid_modifiers.carbon │ │ │ ├── namespace/ │ │ │ │ ├── add_to_import.carbon │ │ │ │ ├── alias.carbon │ │ │ │ ├── fail_conflict_after_merge.carbon │ │ │ │ ├── fail_conflict_imported_namespace_first.carbon │ │ │ │ ├── fail_conflict_imported_namespace_nested.carbon │ │ │ │ ├── fail_conflict_imported_namespace_second.carbon │ │ │ │ ├── fail_conflict_in_imports_namespace_first.carbon │ │ │ │ ├── fail_conflict_in_imports_namespace_second.carbon │ │ │ │ ├── fail_decl_in_alias.carbon │ │ │ │ ├── fail_duplicate.carbon │ │ │ │ ├── fail_modifiers.carbon │ │ │ │ ├── fail_not_top_level.carbon │ │ │ │ ├── fail_params.carbon │ │ │ │ ├── fail_unresolved_scope.carbon │ │ │ │ ├── function.carbon │ │ │ │ ├── imported.carbon │ │ │ │ ├── imported_indirect.carbon │ │ │ │ ├── merging.carbon │ │ │ │ ├── merging_with_indirections.carbon │ │ │ │ ├── name_poisoning.carbon │ │ │ │ ├── nested.carbon │ │ │ │ ├── shadow.carbon │ │ │ │ └── unqualified_lookup.carbon │ │ │ ├── operators/ │ │ │ │ ├── builtin/ │ │ │ │ │ ├── and.carbon │ │ │ │ │ ├── assignment.carbon │ │ │ │ │ ├── bit_and.carbon │ │ │ │ │ ├── fail_and_or_not_in_function.carbon │ │ │ │ │ ├── fail_and_or_partial_constant.carbon │ │ │ │ │ ├── fail_assignment_to_error.carbon │ │ │ │ │ ├── fail_assignment_to_non_assignable.carbon │ │ │ │ │ ├── fail_redundant_compound_access.carbon │ │ │ │ │ ├── fail_type_mismatch.carbon │ │ │ │ │ ├── fail_type_mismatch_assignment.carbon │ │ │ │ │ ├── fail_type_mismatch_once.carbon │ │ │ │ │ ├── fail_unimplemented_op.carbon │ │ │ │ │ ├── or.carbon │ │ │ │ │ ├── ref.carbon │ │ │ │ │ └── unary_op.carbon │ │ │ │ └── overloaded/ │ │ │ │ ├── add.carbon │ │ │ │ ├── binary_op.carbon.tmpl │ │ │ │ ├── bit_and.carbon │ │ │ │ ├── bit_complement.carbon │ │ │ │ ├── bit_or.carbon │ │ │ │ ├── bit_xor.carbon │ │ │ │ ├── dec.carbon │ │ │ │ ├── div.carbon │ │ │ │ ├── eq.carbon │ │ │ │ ├── fail_assign_non_ref.carbon │ │ │ │ ├── fail_error_recovery.carbon │ │ │ │ ├── fail_no_impl.carbon │ │ │ │ ├── fail_no_impl_for_arg.carbon │ │ │ │ ├── implicit_as.carbon │ │ │ │ ├── inc.carbon │ │ │ │ ├── index.carbon │ │ │ │ ├── index_with_prelude.carbon │ │ │ │ ├── left_shift.carbon │ │ │ │ ├── make_tests.sh │ │ │ │ ├── mod.carbon │ │ │ │ ├── mul.carbon │ │ │ │ ├── negate.carbon │ │ │ │ ├── ordered.carbon │ │ │ │ ├── right_shift.carbon │ │ │ │ ├── string_indexing.carbon │ │ │ │ ├── sub.carbon │ │ │ │ ├── unary_op.carbon.tmpl │ │ │ │ └── unary_stmt.carbon.tmpl │ │ │ ├── package_expr/ │ │ │ │ ├── fail_not_found.carbon │ │ │ │ └── syntax.carbon │ │ │ ├── packages/ │ │ │ │ ├── core_name_poisoning.carbon │ │ │ │ ├── cross_package_export.carbon │ │ │ │ ├── cross_package_import.carbon │ │ │ │ ├── explicit_imports.carbon │ │ │ │ ├── export_import.carbon │ │ │ │ ├── export_mixed.carbon │ │ │ │ ├── export_name.carbon │ │ │ │ ├── fail_api_not_found.carbon │ │ │ │ ├── fail_conflict_no_namespaces.carbon │ │ │ │ ├── fail_cycle.carbon │ │ │ │ ├── fail_duplicate_api.carbon │ │ │ │ ├── fail_export_name_member.carbon │ │ │ │ ├── fail_export_name_params.carbon │ │ │ │ ├── fail_extension.carbon │ │ │ │ ├── fail_import_default.carbon │ │ │ │ ├── fail_import_inline_not_cpp.carbon │ │ │ │ ├── fail_import_invalid.carbon │ │ │ │ ├── fail_import_repeat.carbon │ │ │ │ ├── fail_import_type_error.carbon │ │ │ │ ├── fail_modifiers.carbon │ │ │ │ ├── fail_name_with_import_failure.carbon │ │ │ │ ├── implicit_imports_empty.carbon │ │ │ │ ├── implicit_imports_entities.carbon │ │ │ │ ├── implicit_imports_prelude.carbon │ │ │ │ ├── loaded_global.carbon │ │ │ │ ├── missing_prelude.carbon │ │ │ │ ├── raw_core.carbon │ │ │ │ ├── restricted_package_names.carbon │ │ │ │ └── unused_lazy_import.carbon │ │ │ ├── patterns/ │ │ │ │ ├── tuple.carbon │ │ │ │ ├── underscore.carbon │ │ │ │ ├── unused.carbon │ │ │ │ └── unused_underscore.carbon │ │ │ ├── pointer/ │ │ │ │ ├── address_of_deref.carbon │ │ │ │ ├── address_of_lvalue.carbon │ │ │ │ ├── arrow.carbon │ │ │ │ ├── basic.carbon │ │ │ │ ├── convert_qualifiers.carbon │ │ │ │ ├── fail_address_of_error.carbon │ │ │ │ ├── fail_address_of_value.carbon │ │ │ │ ├── fail_deref_error.carbon │ │ │ │ ├── fail_deref_function.carbon │ │ │ │ ├── fail_deref_namespace.carbon │ │ │ │ ├── fail_deref_not_pointer.carbon │ │ │ │ ├── fail_deref_type.carbon │ │ │ │ ├── fail_type_mismatch.carbon │ │ │ │ ├── import.carbon │ │ │ │ ├── nested_const.carbon │ │ │ │ └── types.carbon │ │ │ ├── primitives/ │ │ │ │ ├── import_symbolic.carbon │ │ │ │ ├── int_conversions.carbon │ │ │ │ ├── numeric_literals.carbon │ │ │ │ ├── string_literals.carbon │ │ │ │ └── type_literals.carbon │ │ │ ├── return/ │ │ │ │ ├── code_after_return.carbon │ │ │ │ ├── code_after_return_value.carbon │ │ │ │ ├── fail_call_in_type.carbon │ │ │ │ ├── fail_error_in_type.carbon │ │ │ │ ├── fail_let_in_type.carbon │ │ │ │ ├── fail_missing_return.carbon │ │ │ │ ├── fail_missing_return_empty_tuple.carbon │ │ │ │ ├── fail_return_var_no_returned_var.carbon │ │ │ │ ├── fail_return_with_returned_var.carbon │ │ │ │ ├── fail_returned_var_form.carbon │ │ │ │ ├── fail_returned_var_no_return_type.carbon │ │ │ │ ├── fail_returned_var_shadow.carbon │ │ │ │ ├── fail_returned_var_type.carbon │ │ │ │ ├── fail_type_mismatch.carbon │ │ │ │ ├── fail_value_disallowed.carbon │ │ │ │ ├── fail_value_missing.carbon │ │ │ │ ├── fail_var_in_type.carbon │ │ │ │ ├── import_convert_function.carbon │ │ │ │ ├── missing_return_no_return_type.carbon │ │ │ │ ├── no_value.carbon │ │ │ │ ├── returned_var.carbon │ │ │ │ ├── returned_var_scope.carbon │ │ │ │ ├── struct.carbon │ │ │ │ ├── tuple.carbon │ │ │ │ └── value.carbon │ │ │ ├── struct/ │ │ │ │ ├── empty.carbon │ │ │ │ ├── fail_access_into_invalid.carbon │ │ │ │ ├── fail_assign_empty.carbon │ │ │ │ ├── fail_assign_nested.carbon │ │ │ │ ├── fail_assign_to_empty.carbon │ │ │ │ ├── fail_duplicate_name.carbon │ │ │ │ ├── fail_field_name_mismatch.carbon │ │ │ │ ├── fail_field_type_mismatch.carbon │ │ │ │ ├── fail_keyword_name.carbon │ │ │ │ ├── fail_member_access_type.carbon │ │ │ │ ├── fail_member_of_function.carbon │ │ │ │ ├── fail_nested_incomplete.carbon │ │ │ │ ├── fail_non_member_access.carbon │ │ │ │ ├── fail_too_few_values.carbon │ │ │ │ ├── fail_type_assign.carbon │ │ │ │ ├── fail_value_as_type.carbon │ │ │ │ ├── import.carbon │ │ │ │ ├── literal_member_access.carbon │ │ │ │ ├── member_access.carbon │ │ │ │ ├── nested_struct_in_place.carbon │ │ │ │ ├── one_entry.carbon │ │ │ │ ├── partially_const.carbon │ │ │ │ ├── reorder_fields.carbon │ │ │ │ ├── tuple_as_element.carbon │ │ │ │ └── two_entries.carbon │ │ │ ├── tuple/ │ │ │ │ ├── basics.carbon │ │ │ │ ├── class_tuples.carbon │ │ │ │ ├── element_access.carbon │ │ │ │ ├── import.carbon │ │ │ │ ├── in_place_tuple_init.carbon │ │ │ │ ├── size_mismatch.carbon │ │ │ │ └── tuple_pattern.carbon │ │ │ ├── var/ │ │ │ │ ├── export_name.carbon │ │ │ │ ├── fail_duplicate_decl.carbon │ │ │ │ ├── fail_generic.carbon │ │ │ │ ├── fail_init_type_mismatch.carbon │ │ │ │ ├── fail_init_with_self.carbon │ │ │ │ ├── fail_lookup_outside_scope.carbon │ │ │ │ ├── fail_modifiers.carbon │ │ │ │ ├── fail_namespace_conflict.carbon │ │ │ │ ├── fail_not_copyable.carbon │ │ │ │ ├── fail_storage_is_literal.carbon │ │ │ │ ├── fail_todo_control_flow_init.carbon │ │ │ │ ├── global_decl.carbon │ │ │ │ ├── global_decl_import.carbon │ │ │ │ ├── global_decl_with_init.carbon │ │ │ │ ├── global_lookup.carbon │ │ │ │ ├── global_lookup_in_scope.carbon │ │ │ │ ├── import.carbon │ │ │ │ ├── import_access.carbon │ │ │ │ ├── initialization.carbon │ │ │ │ ├── lookup.carbon │ │ │ │ ├── shadowing.carbon │ │ │ │ └── var_pattern.carbon │ │ │ ├── where_expr/ │ │ │ │ ├── constraints.carbon │ │ │ │ ├── designator.carbon │ │ │ │ ├── dot_self_impls.carbon │ │ │ │ ├── dot_self_index.carbon │ │ │ │ ├── equal_rewrite.carbon │ │ │ │ ├── fail_not_facet.carbon │ │ │ │ └── non_generic.carbon │ │ │ └── while/ │ │ │ └── while.carbon │ │ ├── thunk.cpp │ │ ├── thunk.h │ │ ├── type.cpp │ │ ├── type.h │ │ ├── type_completion.cpp │ │ ├── type_completion.h │ │ ├── type_structure.cpp │ │ ├── type_structure.h │ │ ├── unused.cpp │ │ └── unused.h │ ├── codegen/ │ │ ├── BUILD │ │ ├── codegen.cpp │ │ ├── codegen.h │ │ └── testdata/ │ │ ├── assembly/ │ │ │ └── basic.carbon │ │ ├── fail_target_triple.carbon │ │ └── objcode/ │ │ └── basic.carbon │ ├── diagnostics/ │ │ ├── BUILD │ │ ├── check_diagnostics.py │ │ ├── consumer.cpp │ │ ├── consumer.h │ │ ├── coverage_test.cpp │ │ ├── diagnostic.cpp │ │ ├── diagnostic.h │ │ ├── emitter.h │ │ ├── emitter_test.cpp │ │ ├── file_diagnostics.h │ │ ├── format_providers.cpp │ │ ├── format_providers.h │ │ ├── format_providers_test.cpp │ │ ├── kind.cpp │ │ ├── kind.def │ │ ├── kind.h │ │ ├── mocks.cpp │ │ ├── mocks.h │ │ ├── null_diagnostics.h │ │ ├── sorting_consumer.h │ │ └── sorting_consumer_test.cpp │ ├── docs/ │ │ ├── README.md │ │ ├── adding_features.md │ │ ├── check/ │ │ │ ├── README.md │ │ │ ├── associated_constant.md │ │ │ └── pattern_matching.md │ │ ├── coalesce_generic_lowering.md │ │ ├── debugging.md │ │ ├── design/ │ │ │ ├── README.md │ │ │ └── clang_api.md │ │ ├── diagnostics.md │ │ ├── driver.md │ │ ├── idioms.md │ │ ├── lex.md │ │ ├── lower.md │ │ └── parse.md │ ├── driver/ │ │ ├── BUILD │ │ ├── bazel_build_clang_runtimes.cpp │ │ ├── build_runtimes_subcommand.cpp │ │ ├── build_runtimes_subcommand.h │ │ ├── clang_runner.cpp │ │ ├── clang_runner.h │ │ ├── clang_runner_test.cpp │ │ ├── clang_runtimes.cpp │ │ ├── clang_runtimes.h │ │ ├── clang_runtimes_test.cpp │ │ ├── clang_subcommand.cpp │ │ ├── clang_subcommand.h │ │ ├── codegen_options.cpp │ │ ├── codegen_options.h │ │ ├── compile_benchmark.cpp │ │ ├── compile_subcommand.cpp │ │ ├── compile_subcommand.h │ │ ├── config_subcommand.cpp │ │ ├── config_subcommand.h │ │ ├── driver.cpp │ │ ├── driver.h │ │ ├── driver_env.h │ │ ├── driver_fuzzer.cpp │ │ ├── driver_subcommand.cpp │ │ ├── driver_subcommand.h │ │ ├── driver_test.cpp │ │ ├── flags.def │ │ ├── format_subcommand.cpp │ │ ├── format_subcommand.h │ │ ├── fuzzer_corpus/ │ │ │ ├── 019dd4030b151d2c67da557bf3d56d96dc7839c1 │ │ │ ├── 02c83534aab9233198863881aa7d82dde0a5b980 │ │ │ ├── 059a104f98f5658171c48a4d6b0d39036f953264 │ │ │ ├── 0ab2969c765b7dfd5a132f93c87d60f700089d26 │ │ │ ├── 11780e1967cb34483305ee0ce43df22b498db8de │ │ │ ├── 140803cdfca738e7d23521f13ecf6d87d0bd8980 │ │ │ ├── 1b65c7f425af22130c7c3aa117961873a194a8ef │ │ │ ├── 2251142ebdcd99890424c932de502d094925ad98 │ │ │ ├── 256dbb574b157de6b00c720c6e123de5d3129e56 │ │ │ ├── 26305a3c41da25872752f99d86ba0cff5c96c15a │ │ │ ├── 285ad940d4ba382b1ca2698edae0f48a132d3f4c │ │ │ ├── 2d8c6aebe55a5df4c61804509d3ff1c14978c578 │ │ │ ├── 2f99d3b7e96de13b21d1b33aa77d5d48fc6e6f88 │ │ │ ├── 2fd2a7b121a56fdc1e623de01dc1678414adc5dc │ │ │ ├── 300ba8e152fe095fcd1f88d18bc64d5b100059e6 │ │ │ ├── 34c0284640cf11c78b5802ecaf21802ebc83df04 │ │ │ ├── 3519a9e1095165b91c5d4ba9824d32227da30bd6 │ │ │ ├── 39294db001ba643d5353c8f418f754c6352c8485 │ │ │ ├── 3c4c33f72a9ff968e60adf7a2db5206d6375b7c5 │ │ │ ├── 3c585604e87f855973731fea83e21fab9392d2fc │ │ │ ├── 3da89ee273be13437e7ecf760f3fbd4dc0e8d1fe │ │ │ ├── 3dbc1b67cab9a71bac41d7ee17fa31a8b32a9904 │ │ │ ├── 4148ee13ed2c74d0f5ce52c46ebd0983c26beb64 │ │ │ ├── 45f063671e63a4a29f2b05bdd3b6731568119e38 │ │ │ ├── 4c6f81cf8a9546cd87d1fce006bbe64abd47697b │ │ │ ├── 4e0f684219dcb4518aaf4166bd2b51c957ef36de │ │ │ ├── 508abb20ddc995fe9e72b69216fd46c24c966358 │ │ │ ├── 58607edfe3d6ae988025bc9062e244d4c47f54dc │ │ │ ├── 58aaf472c4ed651739b9b6debf73437e4eaafb0e │ │ │ ├── 596cf9d9a653583c4753d4f51b95e679b63f7c71 │ │ │ ├── 5dab5e799f3875176eeaa35987db34271c1067a0 │ │ │ ├── 64e1cd6e55c120c9f2fb329773f345ee6406b74a │ │ │ ├── 67cba3df1acabd24c9b66e3c02b819f25f3fcf57 │ │ │ ├── 6854bbf33c0126b61050dd5fc0ef0126a9701ff0 │ │ │ ├── 6b03ca7a6835a17b1077f1b98f43c63efd3cac13 │ │ │ ├── 6ba597db92a7d33adb74499c15e5034ee57cd41b │ │ │ ├── 6cd45830db903347e26c3e114ba494052a4a42bd │ │ │ ├── 6f3f7dcc0a69d64ed2d99bcee0c0fbb81f970fc6 │ │ │ ├── 74a21fca37339bc7223b8963473dff771ae964f5 │ │ │ ├── 78d5bd054d5f82b116ac00b834314d4e24f6c21b │ │ │ ├── 79119613c827b0d5bea100613dcd8aa8765c31f6 │ │ │ ├── 798bd7bd8e4d8d1835ffe6f02fbeb6319f4f106e │ │ │ ├── 7bc4f9278deaa7046a5f512c89eac67ebf3ba0d3 │ │ │ ├── 86179d08482c27dab51fe8a8ea5d8324ef2461e9 │ │ │ ├── 8b0653417087d3d7fd049166a72cd7bcbc5f6dd1 │ │ │ ├── 8cabec887fe80b7d9f8b28e989db65a6d72dd22b │ │ │ ├── 8eda19d8bfbc4dbd6070bf626cd2f33160ff88a0 │ │ │ ├── 9069ca78e7450a285173431b3e52c5c25299e473 │ │ │ ├── 922b5a97efaa1a3ecba58234ad8d01f41d36d313 │ │ │ ├── 9d47be29ac84966acf09290f8a7df5ba0ac4ea4e │ │ │ ├── a2ae2a7db83b33dc95396607258f553114c9183c │ │ │ ├── a454ca483b4a66b83826d061be2859dd79ff0d6c │ │ │ ├── a5bbebbdf11c537a22a3a9dab4b83498ceb441ca │ │ │ ├── a69f09257d9cd8f5edd9a87b728ab3d75d4352c4 │ │ │ ├── a8502f9cfc7efd4ddb3cb771ae00364f60bc279f │ │ │ ├── adc83b19e793491b1c6ea0fd8b46cd9f32e592fc │ │ │ ├── aecfd3909eadc5707dd46249f6239cad4a2d8618 │ │ │ ├── af0fb9fda0e123b97ba7be099545a02b8c21a058 │ │ │ ├── af1ec8ae5cda07dc979bd68e45d67ffe0d7ffed6 │ │ │ ├── b36251f56081e4735b65b1c9d3d765f25ce67a66 │ │ │ ├── b62c8e9630f2bb2106ce5717ecf7f10c1b89b958 │ │ │ ├── b74fd0148ec8333ad7ce831dd7caa6bc4eff73a9 │ │ │ ├── b82f012f0ede708c493207a1eeee577e3c0f884c │ │ │ ├── bb709bdd1d83c039d5c2f02313009030a4f73e0f │ │ │ ├── bc75eaba7c2f73ce991969295463f6af70bfbf1a │ │ │ ├── be0ef7b40ed46b91af0482ce0eb7ad38042b5659 │ │ │ ├── beacd06c224890fcf6d9b09b771e1248e241ab4b │ │ │ ├── c349a8a58eb9c57bf1c97e5af483db5144482772 │ │ │ ├── c673fa568bbd98569d347b44306c46b50cc2e53f │ │ │ ├── c949526372921c8c5a75ed9e1b198f44fb7a96a1 │ │ │ ├── cf294d20a4419644a7716bc1e1783a770c5e2e33 │ │ │ ├── cfa74701753e8c5eda1d7364b63b26b667c71bb2 │ │ │ ├── d25a8e16325922f85262deec08e77c04acf69f8e │ │ │ ├── d5ebc4110136110c161d48f2d8e53cd1b4edfc0d │ │ │ ├── d6e1cfb07890f99e8e5142c9868cf0a7d07b97ab │ │ │ ├── d97e2e42e26d865d34f68bc92cf8bfe6a40f2eb4 │ │ │ ├── e3d8d13265fdbea01751381344b1a7466fb0cdfb │ │ │ ├── e4cd6da031338746e34eb86e8a931d3b589e3d50 │ │ │ ├── e8e9adab43ade785323869812e73201de7abf7ad │ │ │ ├── ea21645db01d3810168f14c8dfa601d54d7a52f5 │ │ │ ├── ed301991137b18831e72c6a6045fac64e5c010c9 │ │ │ ├── efa312d99ad0a086577dfb243164f522aa921b86 │ │ │ ├── f488a972b82d572833fccb4600a74d8d4a33ff84 │ │ │ ├── f4d6728f4d7cb31739d3ed633a564e4f0015d7cf │ │ │ ├── fb80d614f998904a2fcee5f4ac25ce3e71787364 │ │ │ ├── fbd4c32be3552f308d7b77155fe35dab861cf45c │ │ │ ├── fd354923ab6a356b717147ef68e51581e81ec8c8 │ │ │ └── fe0ff78dd1420db832b271cc868019e9945f5f79 │ │ ├── language_server_subcommand.cpp │ │ ├── language_server_subcommand.h │ │ ├── link_subcommand.cpp │ │ ├── link_subcommand.h │ │ ├── lld_runner.cpp │ │ ├── lld_runner.h │ │ ├── lld_runner_test.cpp │ │ ├── lld_subcommand.cpp │ │ ├── lld_subcommand.h │ │ ├── llvm_runner.cpp │ │ ├── llvm_runner.h │ │ ├── llvm_runner_test.cpp │ │ ├── llvm_subcommand.cpp │ │ ├── llvm_subcommand.h │ │ ├── prebuilt_runtimes.bzl │ │ ├── runtimes_cache.cpp │ │ ├── runtimes_cache.h │ │ ├── runtimes_cache_test.cpp │ │ ├── testdata/ │ │ │ ├── compile/ │ │ │ │ ├── clang_args_warning.carbon │ │ │ │ ├── fail_clang_arg_extra_input.carbon │ │ │ │ ├── fail_clang_args.carbon │ │ │ │ ├── fail_clang_args_error.carbon │ │ │ │ ├── init_array.carbon │ │ │ │ ├── no_init_array.carbon │ │ │ │ └── optimize/ │ │ │ │ ├── clang_no_optimize_twice.carbon │ │ │ │ ├── fail_clang_forward_optimize_size.carbon │ │ │ │ ├── fail_clang_forward_optimize_speed.carbon │ │ │ │ ├── fail_clang_no_optimize_arg.carbon │ │ │ │ ├── fail_clang_override_optimize_arg.carbon │ │ │ │ ├── optimize_debug.carbon │ │ │ │ ├── optimize_default.carbon │ │ │ │ ├── optimize_none.carbon │ │ │ │ ├── optimize_size.carbon │ │ │ │ └── optimize_speed.carbon │ │ │ ├── dump_mem_usage.carbon │ │ │ ├── dump_shared_values.carbon │ │ │ ├── dump_timings.carbon │ │ │ ├── fail_bad_prebuilt_runtimes.carbon │ │ │ ├── fail_bad_runtimes_cache.carbon │ │ │ ├── fail_clang_fuzzing.cpp │ │ │ ├── fail_clang_no_args.cpp │ │ │ ├── fail_config.carbon │ │ │ ├── fail_dump_phase_conflict.carbon │ │ │ ├── fail_errors_in_two_files.carbon │ │ │ ├── fail_errors_sorted.carbon │ │ │ ├── fail_errors_streamed.carbon │ │ │ ├── fail_flag.carbon │ │ │ ├── fail_flush_errors.carbon │ │ │ ├── fail_fuzzing_invalid_clang_arg.carbon │ │ │ ├── fail_fuzzing_invalid_target.carbon │ │ │ ├── fail_input_is_directory.carbon │ │ │ ├── fail_lld_fuzzing.carbon │ │ │ ├── fail_llvm_fuzzing.carbon │ │ │ ├── fail_missing_file.carbon │ │ │ ├── fail_missing_stdin_output.carbon │ │ │ ├── fail_output_is_directory.carbon │ │ │ ├── link/ │ │ │ │ ├── fail_object_files_missing.carbon │ │ │ │ └── fail_output_missing.carbon │ │ │ ├── multi_input_with_output.carbon │ │ │ ├── stdin.carbon │ │ │ └── verbose.carbon │ │ ├── tool_runner_base.cpp │ │ └── tool_runner_base.h │ ├── format/ │ │ ├── BUILD │ │ ├── format.cpp │ │ ├── format.h │ │ ├── formatter.cpp │ │ ├── formatter.h │ │ └── testdata/ │ │ └── basics/ │ │ ├── braces.carbon │ │ ├── comments.carbon │ │ ├── empty.carbon │ │ ├── fail_invalid_comment.carbon │ │ ├── fail_multi_file_one_output.carbon │ │ ├── fail_nonexistent.carbon │ │ └── simple.carbon │ ├── install/ │ │ ├── BUILD │ │ ├── bazel/ │ │ │ ├── carbon_cc_toolchain_config.bzl │ │ │ ├── carbon_detected_variables.tpl.bzl │ │ │ ├── carbon_runtimes.bzl │ │ │ ├── carbon_toolchain.bzl │ │ │ ├── clang_resource_dir.BUILD │ │ │ ├── empty.BUILD │ │ │ ├── install.BUILD │ │ │ ├── install.MODULE.bazel │ │ │ ├── make_include_copts.bzl │ │ │ └── runtimes.BUILD │ │ ├── busybox_info.cpp │ │ ├── busybox_info.h │ │ ├── busybox_info_test.cpp │ │ ├── busybox_main.cpp │ │ ├── carbon_install.txt │ │ ├── configure_cmake_file.bzl │ │ ├── configure_cmake_file_impl.py │ │ ├── install_filegroups.bzl │ │ ├── llvm_symlinks_test.py │ │ ├── make_installation_digest.cpp │ │ ├── pkg_helpers.bzl │ │ ├── symlink_helpers.bzl │ │ └── toolchain_tar_test.py │ ├── language_server/ │ │ ├── BUILD │ │ ├── context.cpp │ │ ├── context.h │ │ ├── handle.h │ │ ├── handle_document_symbol.cpp │ │ ├── handle_initialize.cpp │ │ ├── handle_shutdown.cpp │ │ ├── handle_text_document.cpp │ │ ├── incoming_messages.cpp │ │ ├── incoming_messages.h │ │ ├── language_server.cpp │ │ ├── language_server.h │ │ ├── outgoing_messages.h │ │ └── testdata/ │ │ ├── basics/ │ │ │ ├── exit.carbon │ │ │ ├── fail_empty_stdin.carbon │ │ │ ├── fail_no_stdin.carbon │ │ │ ├── fail_shutdown_without_exit.carbon │ │ │ ├── initialize.carbon │ │ │ ├── notify_parse_error.carbon │ │ │ ├── unexpected_reply.carbon │ │ │ ├── unsupported_call.carbon │ │ │ ├── unsupported_notification.carbon │ │ │ └── verbose.carbon │ │ ├── document_symbol/ │ │ │ ├── basics.carbon │ │ │ ├── language.carbon │ │ │ ├── nested.carbon │ │ │ └── unknown.carbon │ │ └── text_document/ │ │ ├── change_unknown.carbon │ │ ├── close_unknown.carbon │ │ ├── diagnostics.carbon │ │ ├── incremental_sync.carbon │ │ ├── incremental_sync_multiline.carbon │ │ ├── open_change_close.carbon │ │ ├── open_duplicate.carbon │ │ └── open_with_cpp_nonexistent.carbon │ ├── lex/ │ │ ├── BUILD │ │ ├── character_set.h │ │ ├── dump.cpp │ │ ├── dump.h │ │ ├── fuzzer_corpus/ │ │ │ ├── numeric_literal/ │ │ │ │ ├── 00b8cd836c457801a27431b2fae14dc889e3e92a │ │ │ │ ├── 020bf8374f78631e3832c436818bc4a077464c9f │ │ │ │ ├── 046ea6e92945727581cc0d3550754bf545fbcce3 │ │ │ │ ├── 048ca7783b27c2f9ffa11a6e415df2821de767e3 │ │ │ │ ├── 04cc6041841507c0e7513b38e31ca3f7e63851d7 │ │ │ │ ├── 0523a3cf13b055ebe6d16f0edfe7cd1c54802372 │ │ │ │ ├── 0699989c219e1d7b336851c646e88a651859d081 │ │ │ │ ├── 06be63f8a856b3c2ff1e4f6456ae2a598dcdfe59 │ │ │ │ ├── 0733e1eab32796fc80c49e6a1b83b767d9728825 │ │ │ │ ├── 082fb8b49e412ef6a8c8946b149c9b19cd6e2837 │ │ │ │ ├── 0842360c9209267aefbc4bcafc6a14821257f942 │ │ │ │ ├── 08b69222e0a6ac6dd67d1c91719d3f89149bbf3f │ │ │ │ ├── 0b7093469bb6c349a4a730b484bc99f8755a3cb5 │ │ │ │ ├── 0bbd8b6c814360f7eb032d8e921fb916f3af643a │ │ │ │ ├── 0c2730c2230eb2beaf907d1feb4b754a1d9fa322 │ │ │ │ ├── 111f083d72dc69da5c2df23c8142b96fde044fc3 │ │ │ │ ├── 1213ea480fd8224227d3016271c7dd8c96d839cc │ │ │ │ ├── 1245401b55dbe86b0881a84e17b7ad5424557a6c │ │ │ │ ├── 13c1daecff92125665a018520dbd23f495f7d21b │ │ │ │ ├── 14892954f44819d5064f3b2efeef3d59877956b1 │ │ │ │ ├── 1496a76414a61fd6d77fb52acf040b2176890a9c │ │ │ │ ├── 14a9830e5443add35bee80d7b59e56ed2ed58231 │ │ │ │ ├── 156aee9179b8f39e73332a9b59336a97d1526c97 │ │ │ │ ├── 164585b5e3297b62f49428fc82661b3d1a6bd524 │ │ │ │ ├── 17675e27a67f6ed960be339c6784fa902ffb568c │ │ │ │ ├── 181bd138b714c47c705d218938274a6ff2c0315a │ │ │ │ ├── 1a349dcc540a3978584510d982075f838b17cd6d │ │ │ │ ├── 1d5872c3a5cc3ac317e3611c7167eaa142f2d63c │ │ │ │ ├── 1d6b5255a8715ae63cd18e2b238f889f5a6a3dab │ │ │ │ ├── 1ddd636055b4b62ed55bd9c5406b98508df65094 │ │ │ │ ├── 1e9be73c8fc8acf59e2fd87e8a659b77fb8e1420 │ │ │ │ ├── 2047371f2b0343b6e64f40fb260c09b2037847e2 │ │ │ │ ├── 20602f5b4440f20ea21ced88bddcf8ba073ddac3 │ │ │ │ ├── 22cf82b68b95049bffb91128349ccc312a460b10 │ │ │ │ ├── 2368a873bce5809ef902426b152742363bc21800 │ │ │ │ ├── 259b09861744d6d77d8d2d4c25d43cafd4d9a003 │ │ │ │ ├── 25cc5d59a72a1c274cf218d947dafcde63c6c7b6 │ │ │ │ ├── 25f06d4d4f5e0a73147b3b758bf85754b40bd6f8 │ │ │ │ ├── 2b991489843e1d4b3960a9c905cc5e63dcb98768 │ │ │ │ ├── 2c4224570fe8a21dec1bafe6520a7ec5619b7511 │ │ │ │ ├── 2d1e7879ec3e332fe432986620d4f4648378473d │ │ │ │ ├── 2d7bd1ddf796ff4bbb5efed75ad66e55489780fd │ │ │ │ ├── 2fa6402156da7aed557f89e02fb9b8bd0a8bfb6a │ │ │ │ ├── 2fcefe6a06ecee3472758a9f259b7456da40cc8d │ │ │ │ ├── 2fd76b1e86feae72db5813fa27e0760a607a13f2 │ │ │ │ ├── 320cc080c41a4f5846cd740e82f007645010bfa1 │ │ │ │ ├── 3286cf588786f87fef481c7ee465dc60eee4391e │ │ │ │ ├── 32bb601f3f3aa9a3ac30ee4ad2138c5143251714 │ │ │ │ ├── 334200ab9eb04bd01cb44a21e16306f3e8de12be │ │ │ │ ├── 356a192b7913b04c54574d18c28d46e6395428ab │ │ │ │ ├── 36076c28fff33bea1867304432420ed685d27b91 │ │ │ │ ├── 362091df7a6a4b38be6f958d21f4cadc6fea5e8a │ │ │ │ ├── 37251be083d7f99af9e1c7a4f11936f2935f0b17 │ │ │ │ ├── 38d829e423d480cd24a1b83513ea6d9424627212 │ │ │ │ ├── 38f6d7875e3195bdaee448d2cb6917f3ae4994af │ │ │ │ ├── 392d029d257986310089d500b1d4521818f45cd5 │ │ │ │ ├── 3939dac890fba2eb21814e82eb539d6f7de9da07 │ │ │ │ ├── 3bfd922772e61d1c51ad7a3c0d6db7e686b70859 │ │ │ │ ├── 3c79ec3deaaa87395a6955a8f39be784643d52ea │ │ │ │ ├── 3cf5e1f649a73e69b4ee05c62da8e0d5252823df │ │ │ │ ├── 3e140e6d8d719ce82a88adbcbf3668e890b8d025 │ │ │ │ ├── 3e754b50d7ed4402123e6ee1351344d67b834918 │ │ │ │ ├── 3e9be1b258c67169e262e993f89656cb27c3da9c │ │ │ │ ├── 3ed54accbc161980c788a8951426f1d5931db238 │ │ │ │ ├── 3f675324a715fc02cf00afb8c485eadfba257aa3 │ │ │ │ ├── 3f9f95592f43e9c035a6a53f0c32026eed0d299d │ │ │ │ ├── 3ff2c6b37ae1c0fcc3cdd04a35f9d8e532fa7d20 │ │ │ │ ├── 41901ac30f9e7e68c32ba753436e9c5bc668d3c5 │ │ │ │ ├── 41add85724d3b9bff3d877a949acb2fc93fd76fc │ │ │ │ ├── 41ff53703fa8231deadc00322805ee78f793b5da │ │ │ │ ├── 449812e74454e863ea558e7ab9e17cbe64572e97 │ │ │ │ ├── 45c1e6525e6d1b2496ac19b3064245b6473a49fc │ │ │ │ ├── 46448068fa6276bb5f7c4f56066fb34ece186c66 │ │ │ │ ├── 489a73b15ac1c4dffab102dfe6c109792a7e1dfa │ │ │ │ ├── 49237f66b40eaad2c23d8be346fead4c2a004636 │ │ │ │ ├── 4a095aef5824cd0a13c8706ff870a6ef2f3dd399 │ │ │ │ ├── 4e52ef8facabaeead3d4e12b91b0f4a4ec102732 │ │ │ │ ├── 4ff447b8ef42ca51fa6fb287bed8d40f49be58f1 │ │ │ │ ├── 51711e08538ddc6d50e7c3978cb2a00f50a5a305 │ │ │ │ ├── 52cfe1fb5fda21b7f088d637a41e5de1592fb2e0 │ │ │ │ ├── 550312ca5140e036ac9d728db3d44df0991a4d88 │ │ │ │ ├── 55cab295e6fb717b173702eed50ffbead9de2957 │ │ │ │ ├── 5667a766c3d9fdf13333fd6c5b6fdafd3cebb442 │ │ │ │ ├── 57c1c5becc3cf566581f06378dfe1987172dad7d │ │ │ │ ├── 58409e6b84395163cdbf63ba09ad2231527adedb │ │ │ │ ├── 58c748af73c955937a1dc9e25733cb8b0f9eae3e │ │ │ │ ├── 5941347f0af754e5f70069077c4e87509cefde58 │ │ │ │ ├── 5cc4fc2171135fefc805291872a391ee7e35c015 │ │ │ │ ├── 5d870fa1cb981790514473e2a688fc61f0150307 │ │ │ │ ├── 5dfa4702490bf06ce54b742ebab131c459e21e8f │ │ │ │ ├── 5e0218964ab8a1d04b56e9b4e798729ce5499909 │ │ │ │ ├── 5e4d4905d69735de1b7fb0101dde0f8e4d17d536 │ │ │ │ ├── 5eb55dea99ee665801b7e3725a784a11ed846372 │ │ │ │ ├── 5efd0f0af5b1f1e870f78a2192c5f03bdca52d25 │ │ │ │ ├── 5f773135f0a7c8209d25b3297730293d3e238753 │ │ │ │ ├── 5fc5e3ad16ffb81cd87a08e3ab245390e6eb94e2 │ │ │ │ ├── 613cf0e439d1968b62526744b596b6cd832e3443 │ │ │ │ ├── 61b9f39ee191e759ef0ad6fd735396bfddb2cbf2 │ │ │ │ ├── 6240d35f66f3994f2007b36a162283da33bdf0b4 │ │ │ │ ├── 62db736f788068332552999e2042e8df794b798d │ │ │ │ ├── 6330f177b9d4ffc57bacb9009083de56d3a9fdf1 │ │ │ │ ├── 637a6f32cb071a302042abe613d71f135baf0c5e │ │ │ │ ├── 6417ac8b13eaa831f05ca81c8b289a4627d0635f │ │ │ │ ├── 6422d887d7ff4e44e59a0b28c92a65130b9c2d12 │ │ │ │ ├── 6477186c0f869c7a5b1c91e08010767ecf61c68e │ │ │ │ ├── 666d79489b330d37cd5360ce87f3378a19c368d4 │ │ │ │ ├── 66c382031cc45a841fc9284e1c29b46ef4b7d8e6 │ │ │ │ ├── 66cf30ceb3ebffae51ec04fe54d14f0e1da764d0 │ │ │ │ ├── 684a3fef4c5504bd7c8ee2bb7d417e93d2ad80d8 │ │ │ │ ├── 6afec1f1f70abcf1cbae35977b37c2bc048b4f17 │ │ │ │ ├── 6b453b65ac16f51d4166e1bc52c24281a945385e │ │ │ │ ├── 6bec19b44bcdac7bca8bc9b10925d2cdc754175b │ │ │ │ ├── 6d45ff4bfda8922cdbd9117551d7479ef3bd9adb │ │ │ │ ├── 6e1eec8ed109fbdf7c12458769ea0c002cdeac56 │ │ │ │ ├── 6ef7576d39b7bc259e00aafe441718f698e3cd87 │ │ │ │ ├── 70384f05c971ac3ff0f4ebd9fa2d2f3d82b4993d │ │ │ │ ├── 718fce6b8f3937863dd947bc9c188136d05d7130 │ │ │ │ ├── 7343e86dc760c4b6e623d1646c6e82b378ba24b0 │ │ │ │ ├── 739d7debab4eb975e3b5aa91ed2b965948caf457 │ │ │ │ ├── 7540797d21454f5609272245c05b3981be33b7aa │ │ │ │ ├── 759ce3343927be93883bb26ea5540ea21e5aa386 │ │ │ │ ├── 75e22f6c35544f9a4760dd136ee93d7c5f2518d4 │ │ │ │ ├── 76a6acc2af2d920a632b7addec28fbb42d6227f8 │ │ │ │ ├── 7715d946c6d57bc306d04052c49369f0afb2ac32 │ │ │ │ ├── 7744609c9961fca0f8d55e9c7616509e9e3df808 │ │ │ │ ├── 77a99713d7aa3100c9c0680c9cbeba67d4f30ce9 │ │ │ │ ├── 77c782bdb9521dbe6eb7682493fcde89ce762f44 │ │ │ │ ├── 7ae756e9681dcdb739fb1ae0e30a3bf8043fe60c │ │ │ │ ├── 7b41852284e3035d7a807d1769524a4514295373 │ │ │ │ ├── 7bdb26daf76ed012b61a65e0b6d521f0b34e55f6 │ │ │ │ ├── 7dcc9c53752ca6a800dc7dda472a0bd2ed09e7c4 │ │ │ │ ├── 7ef0a8d8149342fa93469f228609bc52508e4396 │ │ │ │ ├── 8021f51a3efe8bc03538901a5d07a2f20a39b701 │ │ │ │ ├── 81304e0dcfb8fb2fd1ba2309d416ffe1f38f2a3d │ │ │ │ ├── 8149371e9bcb20ef99f926fe20c176cafa676285 │ │ │ │ ├── 814a3876b8285a2bcbecd99a201cf1af49e6b11a │ │ │ │ ├── 832dcee787c4ff526198f707d0d8e16b129767c2 │ │ │ │ ├── 83f542ffb794bd569945866aaf8e37228669a701 │ │ │ │ ├── 849af41229745fd6e9840d69bf6edd7079a311fd │ │ │ │ ├── 84b1ef034e51a5082e8218e85975c1bf4337bc59 │ │ │ │ ├── 8500486975fcce132a3ba3e1a20f8de7b29508a7 │ │ │ │ ├── 85eef37d8dc68b042afa0b50e172897464034a74 │ │ │ │ ├── 85f3830484d7409a99017e899e2399f7985af94e │ │ │ │ ├── 87a2b80f9272583517c0207af176fc40ea55022c │ │ │ │ ├── 89608cabe045206c25384b6785aa92daf7c171bb │ │ │ │ ├── 8c954006e2cb2f5ffbc20dfbcbb7fe404be4ba5c │ │ │ │ ├── 8d5c212a40a328da7565d7bbd5fbe7517538ee3d │ │ │ │ ├── 8dceb2a8bb9a98ee0fcfe9a89d3a3ff3fdce414a │ │ │ │ ├── 8de1f2c13c48532f772061efda9edb38fe26c0a3 │ │ │ │ ├── 93221a7273be83763f9dd883946978eb0935e762 │ │ │ │ ├── 93b713025575996366c633ef8cea8fbdc737d8d8 │ │ │ │ ├── 950b8c9dd09799288af9d11ff958ed2e63d72005 │ │ │ │ ├── 95d9c8b6e3bf3de68dd56743596c43f819e818de │ │ │ │ ├── 99ce00d43f9fc7818a4f3423b7b8289d0bb87234 │ │ │ │ ├── 99df4151dcd9f163434a0b284c10936af0f4e5fb │ │ │ │ ├── 9ac784b1c686635d12495b0ed4a25029f0baaa9a │ │ │ │ ├── 9ae0a713e4c39c47492f77de7102534a7b96aab6 │ │ │ │ ├── 9b4e655a5683ce75fba463fa880b257be3ffa8f8 │ │ │ │ ├── 9bcb018977812159c15a834f373ac135b782a10a │ │ │ │ ├── 9d27e500ee25dbdecb9fc4680812eb96e48f1d2d │ │ │ │ ├── 9d41a9818e211ee98ac596495c1124f6696d09fc │ │ │ │ ├── 9d51f1423722c5e8aa4d107865e10d293a1fa140 │ │ │ │ ├── 9df9f3ac537c579ad5b0aceef46a2dcce31037de │ │ │ │ ├── 9ffdd9130c8e506c54bb4631080871fc1c7ba88f │ │ │ │ ├── a1422e6a168630cdd214ac5e31ca01ae1bee8d92 │ │ │ │ ├── a2eea62736951c8d78626f741bfbfc51bc6c9bd2 │ │ │ │ ├── a41c607d984aec3d796569da2b55dda8f0048b0c │ │ │ │ ├── a574380880b8038ee74cdb44cb7aa7f2b6617186 │ │ │ │ ├── a5c4ec5775088e87a334b664e6e8e86dd7e10130 │ │ │ │ ├── a6c9027065d9ae3dc8f1b1b880d5f1775240ca0c │ │ │ │ ├── aa8250319828b4163695c726ea6e69cf4c140e30 │ │ │ │ ├── aae7c0fb777f220875f1fe54665d0fda9b03890e │ │ │ │ ├── aaf78b268bc0226a03e73e2bf184a61734c8aa0b │ │ │ │ ├── ab3baefd06064b7a6137cb2893456835abcaa118 │ │ │ │ ├── ae367cc8cabac896debece6700e842aa85c40b78 │ │ │ │ ├── ae80bd232c1ebf8afacc37405c2a4505f475e2aa │ │ │ │ ├── aeced42b612c49741b262e34706c1de01eaf97e4 │ │ │ │ ├── af31568f6b0df0b3338588f29c0eb4460a99b69c │ │ │ │ ├── af8720cb64bd570dc7e6d9c68481efe81aed22e0 │ │ │ │ ├── b108a36270fd0835a7be248d5fd4a85a5250a7dd │ │ │ │ ├── b13b57aa1c98d3a129e0dd718a604dac3c65770a │ │ │ │ ├── b419ff42ac4a0f2b5d3bc8979d5aba98d79798e8 │ │ │ │ ├── b49182a7be9de06418c6af2c3f09a79e4ec4197a │ │ │ │ ├── b61f36e968b85c99a3dfd19b9932595e782b8913 │ │ │ │ ├── b6589fc6ab0dc82cf12099d1c2d40ab994e8410c │ │ │ │ ├── b70cfeaa4a7ef5329e8ee37c5015ca3b648c450c │ │ │ │ ├── b86fb9c87e493ea5af6004126150feb9bdb825c9 │ │ │ │ ├── b8983fc9befbd305e19d529db1c66ad18320ac69 │ │ │ │ ├── b8d80dd9af3eb7eb136f1d14a07b1c528f13e911 │ │ │ │ ├── bab0d1528dc42ded1936a436f20afba10014f891 │ │ │ │ ├── bc1ac6a8f86333e9fd372331e6d3f444be735402 │ │ │ │ ├── bc977149aaf6eb3a51c1769e8cb19901b1829e92 │ │ │ │ ├── bceea7f1cd74bebe2ce08e43dac9d261dbf18f91 │ │ │ │ ├── bdb5dc3199e5448ada3fbdfe4c8af7943cbd235a │ │ │ │ ├── bebc59f212a559756b4bbc3faba4ae10d315d497 │ │ │ │ ├── c23f7da7070511444ebc75875fd9d202b5dd13cf │ │ │ │ ├── c2e6e1aee1616fa868685dfabbcf5291438f9747 │ │ │ │ ├── c2e71fd65bf2dd7fdfc5c0690c27b7fcd7ad1194 │ │ │ │ ├── c2fb7620971c2d676b0aa62c0e1bbecd80b2d9be │ │ │ │ ├── c368c154d2fbc8d41e38012e9a506bdc6cb36b3c │ │ │ │ ├── c3d43e895d5403308b1f5d20ac68089f278f58bb │ │ │ │ ├── c4d9aa3c1b056db569ee53f2a8576eee1648a803 │ │ │ │ ├── c596925beec028955b43a195888e284ac19f62a1 │ │ │ │ ├── c98cd4991b593a1979150edef362454ba06bc5a5 │ │ │ │ ├── c9d3ec0e53562b12d9b1e081ed9511ff14dc7925 │ │ │ │ ├── cb7064c4c88c44784df84f6babb078c0f8804741 │ │ │ │ ├── cd949405982c4dc60814478e4aa7d4a3f0428479 │ │ │ │ ├── ce201031c9ef369ba2bfe7d52b0fcc1dca0b13b2 │ │ │ │ ├── d0d3d231e7108e8fa61e4e591fa559ecf2792a94 │ │ │ │ ├── d1188e7f3438ba7c3ff7191398ecc2efa9470957 │ │ │ │ ├── d1c2104e3492dc81240c303b52b64fdb0aa8f827 │ │ │ │ ├── d208b7c3d0653df162f4ccbbadb15ec7dae74ab8 │ │ │ │ ├── d48c09534c35bf5ce0cc6c64a6031723d0906bdd │ │ │ │ ├── d48edc137f3088ed35681ccab30161061b3feb90 │ │ │ │ ├── d5b7f37a21c4af8b3c102f969881e4ee39cc6a8f │ │ │ │ ├── d625fc319a744dd916251e56a472cfef5a98f0b2 │ │ │ │ ├── d650151dbb711eab8630ceaa36a4f8b1a9e39df0 │ │ │ │ ├── d69e58e80c7e82f3fdb59db0191087c54f79089f │ │ │ │ ├── d7f82bce8bd35cf64667f447a2138afdb496da3b │ │ │ │ ├── d9ac7b8dc973d67c518cf931bdf42ad08cf561a4 │ │ │ │ ├── da3293ce5d82067c6f11257981823849169f3403 │ │ │ │ ├── dab0d12291e4cbd77a94f279ba1cbb1672d55c1c │ │ │ │ ├── daf7d7d2c624233ed78b89b6b6684f74dd1c0c04 │ │ │ │ ├── dbb93daa94c24b650e09cc72a7e56097e22fff84 │ │ │ │ ├── dbd01297d254f7fa305219e2aa1a369ea7a22527 │ │ │ │ ├── dc400bc8f31a8bb2593d4c28112e785e29d0f38f │ │ │ │ ├── df984f06ec102745c15edefd29f33eda2a1c85a0 │ │ │ │ ├── e38b56ceb12230a32054d611e0d1f7c056eedf6b │ │ │ │ ├── e526dcac9c6b2eab2ef2ca472a99963347e2f87e │ │ │ │ ├── e57269fa24e5a40f2292b37089d4366cec35c35d │ │ │ │ ├── e57c8da00d97212eea169f7dadd281722334acd0 │ │ │ │ ├── e6e85f599f4562e2d1397d023628b65965fa14d7 │ │ │ │ ├── e79e527bbcef0c4e5e267131063d6866232cec3a │ │ │ │ ├── e860ae2a7d700d40bd3bdeb7caca2b446ee824ef │ │ │ │ ├── e889b88d53a1a45d01232047189825a7747e8c45 │ │ │ │ ├── e8a863ef5127ed01bbba73449a6c9de20125812e │ │ │ │ ├── e8b113846dd4cbe61e80e5f750280a16dd37c863 │ │ │ │ ├── e922ba00132e742d435c6caafed22be03024e3dc │ │ │ │ ├── e9a20a4af61c91b0232679bc583058b278fb2926 │ │ │ │ ├── eb111d98d9cc8e8f4589864319f6c67c0da320d1 │ │ │ │ ├── eb393579a5c6d84365c6c3e95d2de9ed10054d31 │ │ │ │ ├── eb8edaf4c4c13ac51d85c39ff93366be8e4be7cb │ │ │ │ ├── ec7f371c0fb5c2faaaaa23dc6fcdc99283489ba7 │ │ │ │ ├── ee5f2d15733b40d12b0774af406c78a45ebe6148 │ │ │ │ ├── eee6b2d3ea106e614f0491d195c8c8c192c85bf8 │ │ │ │ ├── efba95a85c4192602fa2c3659143c92b571a5c94 │ │ │ │ ├── eff4911030d04500307ffe2e0fded422fe1ae75b │ │ │ │ ├── f08ed337a7f1ab963f1209b7848d84221b563f08 │ │ │ │ ├── f0983d0151f1e21eef6430bbc8547242a477416e │ │ │ │ ├── f197ac3297a72d1413edee2426e7f38917b45423 │ │ │ │ ├── f1abd670358e036c31296e66b3b66c382ac00812 │ │ │ │ ├── f1e14bac6731ce7bd468043a41efdf7b7254305e │ │ │ │ ├── f25de8b0eaf377a3d96af8469ecd618aedd0cf9e │ │ │ │ ├── f53f19616fe3d835283bfd6d318c604991b66c26 │ │ │ │ ├── f57c45a511ae9289677ef448b07003946db78212 │ │ │ │ ├── f5a1859ae6b51feea5a485d0d8143439f1a9991e │ │ │ │ ├── f5df3991f088f7a6710535dcae3250052b786763 │ │ │ │ ├── f5e4d64b7ba71c108572d4f0785e91bbe427512e │ │ │ │ ├── f633db7e95d71992c0d246a910b6ef0c4f848dc0 │ │ │ │ ├── f7185e0cf7f84c726e60c907e24f28954b68b8b3 │ │ │ │ ├── f7486ae138c2b85cb854545476d92e7a0373ef83 │ │ │ │ ├── f79cc4c8d9f8f447188eb98a03b6941c4c84947d │ │ │ │ ├── f7a2155a1adc314c9df71252e9b5f6a6f750ecd6 │ │ │ │ ├── f8de2e14b709554c407425de229e30296a529b80 │ │ │ │ ├── f9610fa79c299e70839877aee63f39e96972a16d │ │ │ │ ├── fa1d66dd261334900916180b83c948551f52275e │ │ │ │ ├── fae00ad0f90febb0e1f8862e3b72edcf30be61b8 │ │ │ │ ├── fb96549631c835eb239cd614cc6b5cb7d295121a │ │ │ │ ├── fbd4a4ffc26cdf139663aed3057eedf51ce790f6 │ │ │ │ ├── fcfe7aa00d7b88df2566fb89ac0d93e5b95f1251 │ │ │ │ ├── fd08016e47acfabcb0032079b3243a3ba8aeec41 │ │ │ │ └── fe34eec4627bd91b7c585b6f4be9561fafd9b3f3 │ │ │ ├── string_literal/ │ │ │ │ ├── 0000c663aecfb5630f9cdaade3002e875ff652d8 │ │ │ │ ├── 019d3a61f8243c016c02a5608b6976d3c070a792 │ │ │ │ ├── 03116a305890a94abc5e1ddc1f414f05a894a8c6 │ │ │ │ ├── 0376b433f55d2717b9d41f2b8138c50c46b2aae8 │ │ │ │ ├── 03b96cd9dc4f7f012bb55d2028e23522b3182cb0 │ │ │ │ ├── 04c810f17ef88440680ff21b94aa2f8700f3bf1a │ │ │ │ ├── 04f36296f1af72207b79d1a9bccad3c0cc12495e │ │ │ │ ├── 06b234fddbe40ffc81affde9f21a7e5bb85958dd │ │ │ │ ├── 06fc68871d6c253e93ed33c7490417567a5397cc │ │ │ │ ├── 07b3fc6b794ea7593e8107ad9885666eb3d8a2aa │ │ │ │ ├── 0891ef166a76ed8fae0d965e822ffbd16c80cbde │ │ │ │ ├── 0915fb5ee1694089eeb6e50b8df17675e53c818a │ │ │ │ ├── 096d488c0e9abe2cd0e12bd2c39fb44301c8be8e │ │ │ │ ├── 099f3667fe4a3a217aa1dd17de0925690fb78bec │ │ │ │ ├── 0a98d217558ac2d3a8092b2aa0f5b95bbdea0c07 │ │ │ │ ├── 0aa91ba10de9207476869788f14113120bbcd217 │ │ │ │ ├── 0ab6c929e37c33fb225d50e69e02f3f8505aabf6 │ │ │ │ ├── 0ad3a8f69b5a9026b01b7fac7ee57b79eb75e09e │ │ │ │ ├── 0b262e8f731e0757184f3d79722b87a0698c41a0 │ │ │ │ ├── 0b8ef897460cd34ddb05e34db985ce877f3d644a │ │ │ │ ├── 0b9d95b97b5cb13a1ab508375616d2c7ca98642a │ │ │ │ ├── 0cac1ad1af736bc8b5ce0aa69bf70286102930a1 │ │ │ │ ├── 0ce49d624305931085b2fe24c6504245611b17b5 │ │ │ │ ├── 0d285e68641514a5238342fd7474c04d70e9abc7 │ │ │ │ ├── 0d48eef2e43ee941fd2a921b1ffc2c9a960a03b1 │ │ │ │ ├── 0d8a52a207c1d24dada33edbf6c4c002a3bb5162 │ │ │ │ ├── 0e0f56266d53af7193855295e4474252bddd42b2 │ │ │ │ ├── 0eb2f4cd2ffb648daab824ed5b30e23c6b114ca8 │ │ │ │ ├── 0eca3f968964de26bb7fdbc86b620e467510cb3e │ │ │ │ ├── 1072f52aed5634ffc5461ccfde141e612e1c8101 │ │ │ │ ├── 110be2627bce9c69711bb92db6e1b4f3563472d0 │ │ │ │ ├── 1144a2cfdfd3882ae4a12453ae570ed5ba9d68bc │ │ │ │ ├── 11823356e0d93c50ab5165897d16322b08e41ecb │ │ │ │ ├── 118bca83c4556dba2b8aa7eb11775454d99c7f52 │ │ │ │ ├── 1202a87fd052064983e01e897b34808f3ff6e7cd │ │ │ │ ├── 1207d0aceee2266d0619757d2c21bcde11b3ed61 │ │ │ │ ├── 121e0dd50daa2712ac60c7a7ad75a3a96c8058c2 │ │ │ │ ├── 128d834f107c1a29ac1bb616a3c9420c461eb4ed │ │ │ │ ├── 12c6e768a24fae7af97517b5b7b6ad45451e8afd │ │ │ │ ├── 14321e2ca435f16798a6c7b0ada316d4c298328d │ │ │ │ ├── 156f8635b11c9313551c85d49e008478e42dcce1 │ │ │ │ ├── 1580d4c19a6fbcdaf475fff8a4a5b7b007f1dd45 │ │ │ │ ├── 168b94a27056b46bb49207944a088ea147b01875 │ │ │ │ ├── 18020898aa2f970510835e93a646055faf3151f0 │ │ │ │ ├── 19ed681682ad5f130c19b58afcf93cd51ba6681b │ │ │ │ ├── 1aa5479826030cdf5e87225bf7abe744ded8aa7f │ │ │ │ ├── 1baba5d163df4121850aa75bfb65b9eec1e0339a │ │ │ │ ├── 1d693296a54fa1e51183aa60a86fa56dd1c5abe3 │ │ │ │ ├── 1dec96b1cc5b85730ef22ef6746bfccf6a285e54 │ │ │ │ ├── 1e7add2f35b6f74817a36dffade4d52d0a783e92 │ │ │ │ ├── 1eafed7ea98652650f95f073f49d6664ebdcb3a1 │ │ │ │ ├── 1f012f5338605e81d19806071950625522855c1d │ │ │ │ ├── 1f26d0df21008fefad883dbd3fdb7c32a68f4b3e │ │ │ │ ├── 1f59243b72386dc9ab6601e63f15e540acef9f39 │ │ │ │ ├── 202d546ab2df1c12c5e463cfd5d52357d27ee56f │ │ │ │ ├── 20b40ff2624a56b681d03737e2cfea7ed545726b │ │ │ │ ├── 20f5eb26a89cd1c5d84e325d17e8200f9692ef74 │ │ │ │ ├── 21b5c5652f169812f0294cc35933b1852d3b0b2b │ │ │ │ ├── 2266623c81f7b52bc5f3310e2258de6260d5bd25 │ │ │ │ ├── 23b95eb9f83d222e121afeaba7eee05e33ea8128 │ │ │ │ ├── 23e20b367426915a863b50cd639e2d6ed6581a9f │ │ │ │ ├── 25b94b337518f8634eb71e7bbb2997de9d1cdb94 │ │ │ │ ├── 2660cec736ab868f3af22f5a38d83b46f4ec9a2f │ │ │ │ ├── 26b41bb63bac10df5e71a0ff1dcafd60e3ddd470 │ │ │ │ ├── 2744b033daab57eab64e4a13c08b42508bfe7f61 │ │ │ │ ├── 2795191870e070dfac97a76ab2225078d9d39e4a │ │ │ │ ├── 2956bece562048144fe63b675cd74d8dc16749b6 │ │ │ │ ├── 2a6db340a61cb534f3db621b68674bd28deb7bac │ │ │ │ ├── 2ace62c1befa19e3ea37dd52be9f6d508c5163e6 │ │ │ │ ├── 2c13849cdce15524155c078531f0596e28942f91 │ │ │ │ ├── 2d75f23163d702e30d7f75642e72e5ebe35f66dd │ │ │ │ ├── 2dad69592eaf5c29fb6f946ea30345b94d4f0f46 │ │ │ │ ├── 2dc0d48e579bdf90ab944f87745559511e8330dd │ │ │ │ ├── 2de4b5c5bd2776262e8c1ebf3995baad9eb144f9 │ │ │ │ ├── 2e70dec62a5c168328c578d4825633017c792c27 │ │ │ │ ├── 2f381733d9cddec878047164d26e926e22a9da9a │ │ │ │ ├── 2fa1f8eb4a7ad1ef0de818edf2ee3a36a5c8a6e2 │ │ │ │ ├── 2fa32e589ca12d6de0a0fd22934ecc1f365804bc │ │ │ │ ├── 2fc29f8b41ce47eff1e4b93ffd68b69ced392d1c │ │ │ │ ├── 30f77ae1d04529b675c4f3e93f859315b60631c0 │ │ │ │ ├── 30fd329fecda454f9dd37560dec0debd9aa29c6f │ │ │ │ ├── 316e54e58b49f9caddf88ad506e9123828c5c974 │ │ │ │ ├── 3206a6a100fd709e5bfd6938b604f539089a6947 │ │ │ │ ├── 322ebc5473609e5e6f494d567bc026ff72348a1f │ │ │ │ ├── 32b92c4a39dd4919ea65e6c8f6e797a38d6d2261 │ │ │ │ ├── 33b21ecc4cf191463b613684e4c3aa5f94f421b4 │ │ │ │ ├── 3417353f392588851b3b4a5556ff33630fa90cf7 │ │ │ │ ├── 36436a22ce2c601dc2c13366fe59735c7ac36dba │ │ │ │ ├── 365a8cbdb459515eb8e623755fd17521aaa52990 │ │ │ │ ├── 36973b3723dd44ff93e859fa66f7d91b80890fae │ │ │ │ ├── 37684258bd1cf52151a72e51593b4377bfb5a41d │ │ │ │ ├── 37a69b77459452d038efc3817a2fbcdb60367f82 │ │ │ │ ├── 37b42d00eca8685ea439d865c02cb6d5f6d11924 │ │ │ │ ├── 37dcc3879b12d50e7a7b33f0fb825662a291ccc4 │ │ │ │ ├── 38eee8ead800a4f44d308c1f38d3505e2b7243c1 │ │ │ │ ├── 394dca45032ba109ccb1eb22c0d75dca6433a1c3 │ │ │ │ ├── 39a5f7060eab5629e4dd20e76658480b860ce284 │ │ │ │ ├── 39e3c2ba0c4f5d65edc00e30f3ab308456e9fb6b │ │ │ │ ├── 3af7aaa16a9328b8d00a75d2dd515fa7ac444502 │ │ │ │ ├── 3b32af2a2a317a9b204764bf221cc83b05665e7f │ │ │ │ ├── 3b7a06bb1102f7d788e3167ff05a9b20da93212e │ │ │ │ ├── 3b9535ea11e068bf92fc4bfc7ff57c47b815b9c0 │ │ │ │ ├── 3bca58822768446e54453af8ec624c4b48dc5095 │ │ │ │ ├── 3c05ef50a60513cedd1842a06c01bcfdc0323b1a │ │ │ │ ├── 3c92cdfb0db748189f34876fb32b66aa1d584a51 │ │ │ │ ├── 3da0f12b5b913d2a8ac137cc917f0e73b9f59976 │ │ │ │ ├── 3e6fff494bd0b1b0e2a7cdc5dc6fa8cc7cbaed60 │ │ │ │ ├── 3eee55022df10643b2e79632fb724ffa891e3d91 │ │ │ │ ├── 4087566889f6007e2e8770c94e1ebff6ad32bf5f │ │ │ │ ├── 413300f2a1d4692c7fab0112511a2411f465bc4e │ │ │ │ ├── 414120154dcdcacd4a92863fbcfb1e3e3991a406 │ │ │ │ ├── 4150c8dc06ecb3f620577622b439d38425d51c78 │ │ │ │ ├── 44cd53e8e58df6df2fbc9a46f4a3c13ff8f09200 │ │ │ │ ├── 45369e2121a4e20b3b57f28fe9a4319d7b16af54 │ │ │ │ ├── 4561b622b4f9a8bc970a677ca191c16c19737ec2 │ │ │ │ ├── 456ed7542e7eec5ebba7517e1b435578c32e2aff │ │ │ │ ├── 484640d261434a02a5091ebe283a9aaa2bc6bb95 │ │ │ │ ├── 499108a16874f242f9341089cad86162b8c03072 │ │ │ │ ├── 4b12ed448db88ad4c3d2f0d0737cb665b5b824b7 │ │ │ │ ├── 4b8430148ace414db66ff719d791a53aaf5462ac │ │ │ │ ├── 4bb117d9155a3957bab90af9e84a0d5847ff04c1 │ │ │ │ ├── 4c132146c393971d805e60f366c366fa9da523ee │ │ │ │ ├── 4d9f2c473f4d4a9a081a7b44c18b970c50bc32fc │ │ │ │ ├── 4dab43cb12e13a7224adf4dba343bdbab887f74b │ │ │ │ ├── 4fc5bf5ca2b3a5a9e5ec6bbd53f7a99e941129d5 │ │ │ │ ├── 4fdff4cb139acc2c25fd2dfdd0dc19f5afbe42ab │ │ │ │ ├── 5056c7a8bcedf92a1b27c07c813dc282bca1d06c │ │ │ │ ├── 506825d4376c705a90d5811c7cac100db436efd8 │ │ │ │ ├── 510fb3639791c534198374f7423bfa9791774d28 │ │ │ │ ├── 5132c55dc1641aaab606231181d9896bf0154f7f │ │ │ │ ├── 51866c8db22d6668abb60fed4bfcb21052f0d00a │ │ │ │ ├── 52973603cf5c835ea4da789097d252eb42305468 │ │ │ │ ├── 531edc565c3e984aa9320a413dc471e68aebfee8 │ │ │ │ ├── 541f135097e6a2669e43af326e53d70c327ec629 │ │ │ │ ├── 55e65784b6e7a55c2a8a46bb7e06e2bda659e629 │ │ │ │ ├── 560927416d9f52ac4c2cd4704c79f3595eb75dba │ │ │ │ ├── 56e3911b87b48bd7c3c6598b97272bcdfbee399c │ │ │ │ ├── 574bc1c47024caad6e4bb4bee2df4a0a0a83ceba │ │ │ │ ├── 57d56fa9e0d9b6a145cbf08d92d1ec905357682f │ │ │ │ ├── 589c697128b915888509ea62fc5947c6b02d24f9 │ │ │ │ ├── 589f003fe9c3b5cbb6abc3e262b1ebcd43f2c7eb │ │ │ │ ├── 58e1b119f4bb9801fc6e5e89460030def82601dd │ │ │ │ ├── 595bd3da9cb8c029f3a7848eef7a2b8164f3cf3b │ │ │ │ ├── 59d6888e8e389be5174eb7269b457824da4521ef │ │ │ │ ├── 5b6672c68afdb254668f6e9b859e7ddba5471288 │ │ │ │ ├── 5bb17a6271cb4025a28572954d30145f4521ba82 │ │ │ │ ├── 5bbf3bfcd7c31e9285dd2324fac77e0d7322e452 │ │ │ │ ├── 5cf45e86559b80c5d4e049bd737613e477654ec3 │ │ │ │ ├── 5e7a76d4a38b352010c4a8c8ad4aa461f6218793 │ │ │ │ ├── 5eb47faba454a8187890675ee243eeeba073fb0e │ │ │ │ ├── 5f28eb629ea31547978309b712407cf5f6538095 │ │ │ │ ├── 5f422d74bc3008c571d0abe58eeaf7ba3feda93b │ │ │ │ ├── 60b668789254f2427fd51d1bc30da5b31416ff59 │ │ │ │ ├── 60bb7d18180cfa0c90783c4340344ea193045a27 │ │ │ │ ├── 615220b2a3800a887f14f42f5f642f1e524ea7b5 │ │ │ │ ├── 64ef2895f91f5aab5b6c228534e7cdc94a57c731 │ │ │ │ ├── 65a052f77e6f5dc2ad248320f241e7932b18f4aa │ │ │ │ ├── 65d1bf6f2e113d4cd74bd512898dc0be3d850598 │ │ │ │ ├── 670d6b2808d9ae0e710bf7426069d9ec5573fdd3 │ │ │ │ ├── 6750e65dfe53985ea620b25b74690e9adb912580 │ │ │ │ ├── 677ce415ea13b040def8c70a4b224920d4b7558c │ │ │ │ ├── 678b1652c59a1becf5eb8660b992e5ef3504f3ef │ │ │ │ ├── 6974aa9946f3f4710d3c8269af99f159f3d33b0e │ │ │ │ ├── 6a3f865363698dc8eb11d530c234e830a30af5a0 │ │ │ │ ├── 6c706d640239c2175c875a26a9c1baf1634ce26b │ │ │ │ ├── 6cf13d3995b0b218a5c0c0fdce5a0cec9089f850 │ │ │ │ ├── 6d19d3b77b3be81f82feeb998ffd7725fd701fd0 │ │ │ │ ├── 6db98d2f4080d7279fe56f669bf11fdefa97a50c │ │ │ │ ├── 6def6e6cff73ce5d27104d5891dce3090e14ecf3 │ │ │ │ ├── 6e195d1aae39e27a34f7bc7a325066b675b5678b │ │ │ │ ├── 6e1f5e5722e7877bb4c3054c7f6be4a3a2c36d8c │ │ │ │ ├── 7124d84b33d82906d70c6bef3fdc65994b5291b8 │ │ │ │ ├── 71d1a859ee6be79eab5856d3215a820c35e0c52b │ │ │ │ ├── 71f5d8f973ad94d40e91577cb62fc838dd61700e │ │ │ │ ├── 73008346249a9885bcf8bd6e6824d77267ab4639 │ │ │ │ ├── 731222f561fe7b81db8f55d086f5befef2160961 │ │ │ │ ├── 73688477f15db57f6169386d10b85e552f86d561 │ │ │ │ ├── 753889c6d4cd0e89717dccc0ff50ebd30415ac22 │ │ │ │ ├── 75b706660b62c7aaa9c11552320fe581f39c6820 │ │ │ │ ├── 764062a8bb71380cbb1ce81d8c7d331f738b804a │ │ │ │ ├── 77aa6fc67ffbab1ee2961d33c0ef55dc29f0f766 │ │ │ │ ├── 781ac37c9f8222af3404840604c0d4559d4089c6 │ │ │ │ ├── 78a7fb5dbfef4da9d0da838dbf594a3ad91cea5b │ │ │ │ ├── 78df962da98901882f2530945e54bb62c7ec22b9 │ │ │ │ ├── 79099ff6620cb6d92fd51f7f46fa274b58dd3173 │ │ │ │ ├── 794622981056b9f89a08e8ab5e1a00c38b1fa562 │ │ │ │ ├── 7950d568f4d548cc3304e877969a55a0b997bcf9 │ │ │ │ ├── 799e94b786fa81e26772bc5db0a71e94c5020da6 │ │ │ │ ├── 79d23363b6d0dea7ca3e03350d21ca3d24bede9b │ │ │ │ ├── 7ac82c42433bc4ca35742ecb8083deddd965efcb │ │ │ │ ├── 7b22b1953b1864ce50a5adfe6019716b19371f72 │ │ │ │ ├── 7bf6d56d723dec165ca1da8b089bb4403b992eb7 │ │ │ │ ├── 7c135e14fa77b5eb9b9e70eac7b9b629be1aeb80 │ │ │ │ ├── 7cbf6e6a1b2a43d310d2c69d0a772b34e794bd72 │ │ │ │ ├── 7cd55ada85ac33da68ea075c7b86cc45402dbf4c │ │ │ │ ├── 7d1c19a209ccb08910c4c60bf0fb6e6b38f48bec │ │ │ │ ├── 7e3aa07547c90a0d597c824ba84ae097ac479e72 │ │ │ │ ├── 7f4fbaa57b3d81a406ad55f452e61d3192b0a91c │ │ │ │ ├── 8087a1e31c52972200c4a536a5192cbcda9fc2a0 │ │ │ │ ├── 80cb08da094850c934d89e75b20fd2ad366247ef │ │ │ │ ├── 80cdc41e66d2a7f513c53257610737234662cce5 │ │ │ │ ├── 831af40692156be859277cb8332cf5b3b575f4e0 │ │ │ │ ├── 8365cd0e25f4ee55757dead2fc50e5b89317896a │ │ │ │ ├── 83bfe4a8ee18369a2ce07741955a0aa80df20c4a │ │ │ │ ├── 84b2ec838d54669b11af47a63909e0511d59d80b │ │ │ │ ├── 857ffafcb227e709435d591d083a07cd61d9f9db │ │ │ │ ├── 85e4ed0582d0e1814bc2a4c28be4f51c677ea645 │ │ │ │ ├── 85ec3d48177403439495300df0b885709a3a01df │ │ │ │ ├── 86b84eeb82fa5888a890da0a35ff8c75d8d6eea6 │ │ │ │ ├── 874000f9bb3f8caca4e38317ef3bf70dccd1fc45 │ │ │ │ ├── 8839992e511880e6b62ce435a2baadaf0754adfc │ │ │ │ ├── 89a9d6fbce967e64793c29f69bdec1dac15d4cb1 │ │ │ │ ├── 89c672b6b84f976b8ecd05d7221e535d9a2a3b0a │ │ │ │ ├── 8a57af39652be7bebe49059056452c57ebf29fad │ │ │ │ ├── 8a5ba0066b9a54f67372a861e0d181ac159d4c78 │ │ │ │ ├── 8ac23ef53588d00ec6eb2b23568399cb6b921897 │ │ │ │ ├── 8c3ed6d5c089b598cee0d1433d8b528a33b2e58a │ │ │ │ ├── 8ddcc5903761d3bb3d938bbfe494c3205e6b9879 │ │ │ │ ├── 8fc905873a9165d927a1d8ba4805042155137d0c │ │ │ │ ├── 900ed842e35028aec93f6bca38c7505b839b22a6 │ │ │ │ ├── 905564fb4e2123756c27b597a449b9463ebfc0a8 │ │ │ │ ├── 91ab872f72d2bbc7185649dc9b7289813b16234c │ │ │ │ ├── 9204651ab3fa893d6ab8783902e989df92d17fda │ │ │ │ ├── 924856b767fd44a9660764543d0a40361a96a30b │ │ │ │ ├── 926d91f7ec3005dda6c8dddeeb050dc3f160869d │ │ │ │ ├── 92b065254b2251dcd339dc32ac90deae8b474716 │ │ │ │ ├── 934738f8a075948429a076e2bc2957011a6589d1 │ │ │ │ ├── 93ac8946882128457cd9e283b30ca851945e6690 │ │ │ │ ├── 940e6cc96bf5fa825ee5d68a092a2d9fbf9a29a9 │ │ │ │ ├── 955b9053d403db2ccec289c7fe7bc3f852acd9e1 │ │ │ │ ├── 9575dfa681de3bab7d00776eb882133deaef3929 │ │ │ │ ├── 95ce5b9a9edd09853e12884f214bebafd40fa46a │ │ │ │ ├── 96a149083217f06ec7b7805e2767d0f62ef90480 │ │ │ │ ├── 96b76b8f2d822cdb61467273c8a3c212f51a47b5 │ │ │ │ ├── 970edf01dbe20c4c0356cf71a62559b7144baa27 │ │ │ │ ├── 972ae0f520c71fbde536db4f1c9e7d8d3062eb71 │ │ │ │ ├── 9730916030311413bb1d2a4e9d93223612d49e94 │ │ │ │ ├── 98356a8902ab5ea28b79329449aa5c10ec4f2cf3 │ │ │ │ ├── 986cc7b9818415e1fb08575a978aac57299ac5fb │ │ │ │ ├── 992e61e336c4635bc1830a40c86846b8feeb0ab9 │ │ │ │ ├── 9a135d30087fdbb82c95afc4f4aba5d12cf2d410 │ │ │ │ ├── 9b1cc3376be668975eb18581a771e3d3b4634040 │ │ │ │ ├── 9b6f95fa0f731632af30d58a9538dbe7e27e103d │ │ │ │ ├── 9bba8c359dfe58cbc7043fa9a375d1d08ca3c62d │ │ │ │ ├── 9bccc112d628b0dc50a865fe841a5aa99ee37a51 │ │ │ │ ├── 9be0df2f364c213dafee965ae895ac70b54067f8 │ │ │ │ ├── 9c0dc6d4503a8f66596713a60ff5d101cce59430 │ │ │ │ ├── 9db8975828684dde7a36aee4ba72610c36f0083b │ │ │ │ ├── 9f0309cd3690c0a3c1f4b27f46a6621ad7444280 │ │ │ │ ├── 9f4a4b6693ec585370525949dec5cd76552c7462 │ │ │ │ ├── 9f4d2eaf7bc680cf3c03404884d8c7e083388f5c │ │ │ │ ├── a08513fd362c0631d1c2831ddf398c89f5a1b271 │ │ │ │ ├── a0852c2b59e4ca5bfaa98c528aa74e850c2bd6d4 │ │ │ │ ├── a12745c8f9d50c3c8c2e66930fb658125da3b93c │ │ │ │ ├── a1bb38f60c9abc335ffbc40b5aa2f47428671698 │ │ │ │ ├── a2029c2c7b5657f63e650eee9e893a4487edb348 │ │ │ │ ├── a27a8c08834306d201a0cddb84bf9a2ee9878d08 │ │ │ │ ├── a35cbfbcea5f87ac7fd604e9b7a67780a12a7cc0 │ │ │ │ ├── a41e604d8008993286621596c98fc7eec69d994f │ │ │ │ ├── a448aecc0b639fab71f42982e20a00102756cdb9 │ │ │ │ ├── a4e438338444c3fd3acf87b2988acb575095ea96 │ │ │ │ ├── a510865a683d3e320591d15c54520918498dcb76 │ │ │ │ ├── a5360c092ad19b8e542361a7f89e3b370dd83634 │ │ │ │ ├── a5a195b3c9fcbc991bd83f5cbe4fb029bd63ea56 │ │ │ │ ├── a5cfcffcce577a5f7ba7ac535474ecc3c07c49ff │ │ │ │ ├── a5ec120dfe381e7d433153a2cbf634e5d527bcf7 │ │ │ │ ├── a65d6b36db17585ada3f0852359fbadeb37378c3 │ │ │ │ ├── a70e328a8f8b1e4309e944b5f6d79e9e31909484 │ │ │ │ ├── a7a348b219303e69cee1d69c72434615c7ef181b │ │ │ │ ├── a7a4917ae72b218ed27ae83f1e0c49b479e7f72f │ │ │ │ ├── a7b765b566d7b56672dc8faa7338248730f2132d │ │ │ │ ├── a80b93977a5192c9b2455afd07361f19a2fd3492 │ │ │ │ ├── a86beff470353262447e79cae5d041e1e87eade0 │ │ │ │ ├── a8cd5e96f3aad050f75998476ff342cff0eca57d │ │ │ │ ├── a8e45c402e1556bf48369f1218e80e243d590f2b │ │ │ │ ├── aa2fd1f81adc4acc33409235ccae56393db1aced │ │ │ │ ├── abd02fcd74bd62e86f8b6580181df41017c8a72d │ │ │ │ ├── ac0c97b629fc32f60d6dcc5751cb95bda0602940 │ │ │ │ ├── ad29df63a3d387eaba14735348e2a5150e00b925 │ │ │ │ ├── ad53af03a37abd62993924b889738c01efcf6541 │ │ │ │ ├── b055ba7c474ec23a2831f583338145959041d016 │ │ │ │ ├── b08faa33bffa2203ba1a124367e2618949408850 │ │ │ │ ├── b0d1d2c07a3538c670883fcc3380af3232794071 │ │ │ │ ├── b179c14764f907b6b0459172bcca0bc917c0aa65 │ │ │ │ ├── b1b69bcbd372f00aaa8225547a1502327a0da00e │ │ │ │ ├── b204ac127143968fdb1858d15009767c604afdf6 │ │ │ │ ├── b301bb54860d6352587dc4e824a6a22a1de86ad6 │ │ │ │ ├── b38c1b6d767d0cf2023fbfb6b2e8d816eb65e6c6 │ │ │ │ ├── b442231af8d19c70c66bd2254b2a3d12e9f809ee │ │ │ │ ├── b55d87d33a3bd7df1ad15276f9acc87d88cbfec4 │ │ │ │ ├── b62877512feaca935f7e50bdf5e233f5ab465b59 │ │ │ │ ├── b75101049a878bc14368997b4828521e2fe112e3 │ │ │ │ ├── b75b1ed7c86e1a2cb36df7553056fd0d51a9327e │ │ │ │ ├── b7ada01ce7839e0a5bfe6b3a093b28dd23c902a5 │ │ │ │ ├── b818b239ef45f737cfd97008f3990ea01817451e │ │ │ │ ├── b9ab2b22f14b92e094ffdc16d9873d8d800469cc │ │ │ │ ├── ba009751c34ea9bcc3d4c62cfa2cb56ed9931e14 │ │ │ │ ├── ba0bc0a9b4cb282d386e50fecffc6afe47db39c1 │ │ │ │ ├── bb0cbbb21041bff11c4ed846705dcbbff52512ce │ │ │ │ ├── bb98284f0e271c119cc2bb1663aec03d70f7ab56 │ │ │ │ ├── bb9cb3d055953180b2c1a49b082a57014b63d46f │ │ │ │ ├── be9a63dad81f730cc6d159e0f872bff27621a343 │ │ │ │ ├── bf3797cd98ce8ccdb14feed4bb34f1a927bdeee2 │ │ │ │ ├── c0159cb389c964f20c91b1e38177cbb0ad23848f │ │ │ │ ├── c04130b0a29dc211df8af640a902cb56b3505ba3 │ │ │ │ ├── c0d83e56b82082e617d31cd7f25064ce8a9fec1f │ │ │ │ ├── c0eeec57dfa448064cc21a9648188b50baf012e9 │ │ │ │ ├── c2547ecf3ca4d877bc6f0b80172beda4f04853b0 │ │ │ │ ├── c3949f3b731c8df590e4488d7a5f1a285be50d18 │ │ │ │ ├── c54c99aae6a6ca7191b0d42e59fade69ad2c27f7 │ │ │ │ ├── c634dd4cae2349adb2706b90b4506a804bc09ede │ │ │ │ ├── c672382d2badada3d2a86ac6dd9ffcd9bd3617d8 │ │ │ │ ├── c7126eb2a28a325ddf89f4ebb30ecf59908eed73 │ │ │ │ ├── c773a17398b3951946a39579573e37acdfb5040f │ │ │ │ ├── c958f0fbe4055c05d206111bd85dd9ecd7197f8d │ │ │ │ ├── c9842a53949e66f67ad11c22e005635ccefa5f36 │ │ │ │ ├── ca31449ca638806490a2e083b763b28df489dd71 │ │ │ │ ├── cc365a16b17bed8b3cd9ad02e26b8acf0a568e49 │ │ │ │ ├── cd0046da5f05badbdc471fc58bc5291b44f34a55 │ │ │ │ ├── cda85ebcba806561741c36fc7de6d06b5e66e819 │ │ │ │ ├── cef5be5ba6726a3e91a233982201223c63efcae0 │ │ │ │ ├── cf173e484a8e02c5caad461d592e84ab2d5ac5ef │ │ │ │ ├── cf1adcfef9425c515d37f5a0f1f1832939f85d4b │ │ │ │ ├── cf9e22eb2c602b04741195274add0e478a1056dc │ │ │ │ ├── cfaf498d71ae8a79ba6fc76b1d87fee1731fb350 │ │ │ │ ├── d00959fdc962b5a78aa29b9c46824c9c45119fd8 │ │ │ │ ├── d07f2c22aadf537098267e5a43d83de86ada11be │ │ │ │ ├── d08f88df745fa7950b104e4a707a31cfce7b5841 │ │ │ │ ├── d1dc5ad2e9555716fbe9f8e64294ff5d46741b6a │ │ │ │ ├── d227b7881c8e348194c02e6cb878ada56e9d23b7 │ │ │ │ ├── d441e69fb96bc945e42bbdfe9dd1aabe68df0996 │ │ │ │ ├── d57440c5b07c1dd1d0372d532e2cfcc30ef6821c │ │ │ │ ├── d58cff7c05b067edbb74b784e468527ebebb6d08 │ │ │ │ ├── d5d5a633db9e2a5c3926ce3698e36042955e20b3 │ │ │ │ ├── d690f51b5f6d3cbb1121c1c5b0a2d8035e7f9656 │ │ │ │ ├── d696853b0a7dd15ba25337fb4bfbac3568fa55e7 │ │ │ │ ├── d6d070ba545de29d35a476af9d5b09bd612cda5b │ │ │ │ ├── d7bb954c2b4013a367a287096dddd8cb9ea3e948 │ │ │ │ ├── d878897ed681351eb52f76b458616e19e5481e0e │ │ │ │ ├── d879caca8bf6c854b3fccec2f25daea6a448b73c │ │ │ │ ├── d8d9c2938593eef2a82b28ccd953d15fc0663799 │ │ │ │ ├── d94cd0abecdad5f3a080bf6d192c26f84607177e │ │ │ │ ├── da53fcc5738a1305cb30ea27fbd2782df8d3e045 │ │ │ │ ├── da60ae57e2b87ce1988e49c9af5bfcc92e7067b4 │ │ │ │ ├── daca17e1af88467ac5c66749941b5dc016c60941 │ │ │ │ ├── dad8fef81affc1d18b9a0bd6a01e574300c87e2d │ │ │ │ ├── db207d11d0f18aca083f2dea4a124ee740bbc7a9 │ │ │ │ ├── db5efb7768292614e872abd9c7d8f4ef1d5e0b4e │ │ │ │ ├── dc64cc322fd899dc5ef9ab53d05ed1d13ba673ce │ │ │ │ ├── dc6aa8f6168e5275f05edb96035e3c6c68f3e4a2 │ │ │ │ ├── dd79c8cfb8beeacd0460429944b4ecbe95a31561 │ │ │ │ ├── ddb4452ebd9245936764cd90aecd16a46a8ac6cd │ │ │ │ ├── df3098b65fcd256ccd6668326a75ffaa80041365 │ │ │ │ ├── e185a2958e6b101f75b2aeb47d7451ea9003cba4 │ │ │ │ ├── e3667ad3bfbf4b30367bcbe31c58e55aae0d62e9 │ │ │ │ ├── e36f0fd33d8f4f1469a9a28ff9c4932f3bcbc05d │ │ │ │ ├── e3f7be8f63c58239a2dedaf66661259615cb472d │ │ │ │ ├── e42598c27eeebdbb66e08f13863d1dba279f2b2f │ │ │ │ ├── e4dd444751a857cd6b8fe0ed1dc42e75f2461e0d │ │ │ │ ├── e5df18ef3c2495af7234c67fe82a211e10395b5b │ │ │ │ ├── e6faee615498be2fa19d84dcd18f49c83650c4a0 │ │ │ │ ├── e6fed90453a5dfe8ea1fb4088ef46b54cf7d7b77 │ │ │ │ ├── e710f92e9fe937088ca313f6313ae910581d0d3f │ │ │ │ ├── e7d9a36b62671ea6854f896b1afb922d85312fbb │ │ │ │ ├── e985bfabaa93f41797aed1abf9bca44e329115e7 │ │ │ │ ├── e992a0e2045ae3f5a0f6d0c21581f8c2f2a3f2b9 │ │ │ │ ├── e9d9ad12fa588bcadc74e461a1fba393bdc3776d │ │ │ │ ├── eb43923537146440042f13e5fd99f2aff7aaf999 │ │ │ │ ├── ebcaff5d54d618e7ad6ecc88666b419226f9c348 │ │ │ │ ├── ebd5655c445e1f38830b665aaa1ffdb5f5a47ac6 │ │ │ │ ├── ebdf72b482bfc5f88cdca5e3dc545d0b3a2f8b5f │ │ │ │ ├── ed33909c916d6c0e9768d4979c22216654e82a6a │ │ │ │ ├── ed93811452ee834cd32299a271da63e9b4590cb0 │ │ │ │ ├── ee093801766e997f07bfb180f35ef3699b0fb80f │ │ │ │ ├── eefa09859330cf646185816670083875bbf4ae7a │ │ │ │ ├── ef50648b50f757b9ec4961debacd4241f41bef46 │ │ │ │ ├── efe384f984e9f8ecd515dd742dd21e6cc1f05e41 │ │ │ │ ├── f08af995f85e8ce36b328093c935e88c120ddb1d │ │ │ │ ├── f1bd9dc7634fdae830ee5354d92d177ef78ac0b7 │ │ │ │ ├── f1cc201552471c06746ac9e9750dfea3f9d96e4c │ │ │ │ ├── f220d53bd7bd65026c20c376c81b447b65df63da │ │ │ │ ├── f28e5554399d43c668850c65c82a303c86f31e3b │ │ │ │ ├── f34ee07d8776fcd466cb565a724c361bcef329dd │ │ │ │ ├── f36d731e7bbe857f35061f2532fd988054388f7d │ │ │ │ ├── f3707064c68f565ec82ccc011de1692708610447 │ │ │ │ ├── f372e11022b04fb607f4065d55532c34a91770be │ │ │ │ ├── f389edf5218f5aacbdc12d538ec1d71b1023ddf9 │ │ │ │ ├── f3c35ce004d52c58bcab57d684ae96b6b21781a0 │ │ │ │ ├── f48036032784e45d48a1a8bc44a77c15f8eb022b │ │ │ │ ├── f49b5e134ef410edf1a82d50b8932e797beb2419 │ │ │ │ ├── f56c4bc9276d9f47b3c20524825ab5ea3dd2284a │ │ │ │ ├── f58c53a691f1a57bc71af24e3584a4bcee1ce69f │ │ │ │ ├── f6141693bfc695906fe8271fce1b8b68fe599713 │ │ │ │ ├── f6cb218e3fc60c5b47dddf25c790f1f334a06c45 │ │ │ │ ├── f7012a12dc520aa6d962300120f793feec6b2ea0 │ │ │ │ ├── f7ed109685abbf92bcb50a847c4d3861a246b743 │ │ │ │ ├── f86e634d1f56afeeb500bdfcb3aec9d2a0b65588 │ │ │ │ ├── f8b0241f1563b284d95042e1aa3041dfc0b4cb8e │ │ │ │ ├── f8b9b2f8b56139461b8e522bf482532fccc112d4 │ │ │ │ ├── f8f498840b2a48396467a5b6f87bfb1cb74a39ca │ │ │ │ ├── f92e3d66c814deb02ccd8fa344c51f515b692c8a │ │ │ │ ├── f9702b2cf1a92261780a39aececfc85c22232caa │ │ │ │ ├── fb9ea91a7cc23f9f5a28417ad63822670a8fbb5c │ │ │ │ ├── ff007be7d293751393b31ac05181ce4f9d01171f │ │ │ │ └── ff546a9b073eefef4541c06b70c57ce5a7517aaf │ │ │ └── tokenized_buffer/ │ │ │ ├── 00b9598434d1cb0aca2446e5d8059f955898661b │ │ │ ├── 01096186d4e1347ff16a01491d6ef629f5f5fd42 │ │ │ ├── 011f63c979b2de6c44a0f974437ff176fb309cf7 │ │ │ ├── 013a89a4caf353b43c203a2cbd270857c46863c8 │ │ │ ├── 02ddec8b4855127d5616e521bc826659cb04a796 │ │ │ ├── 0327df1d12e0abf8a355b7ed09e0228dc396d787 │ │ │ ├── 037dc35353d675c9f4d2f03c9db8186422050b2d │ │ │ ├── 04030ac2f55537459994c38435a6b7d00ae264bf │ │ │ ├── 0543ca422553bae7a2e8b46a81c8fee7eb12c9a7 │ │ │ ├── 0555830a8ac12955924bf235d97792583fb8d6da │ │ │ ├── 05666d20f461b2d4a93030744abd619ce0f25fe7 │ │ │ ├── 058baf2eca3ac04d3382c94dc213af644b547796 │ │ │ ├── 05910a7c99851cbb657cfbf8850e1e67e98408b1 │ │ │ ├── 062fe100147aaaf3a5f3f0a67c64991480597cfc │ │ │ ├── 0685140d284f7c98815eb844fe6445ac2e4d25da │ │ │ ├── 06b6c1a09f2ec62f93b7a3f1b03a2188700c8117 │ │ │ ├── 075e66efafa8882c10f46de45be654ad1f930c1f │ │ │ ├── 07f95640a7ba78bebf24bea549bd129d81d03f14 │ │ │ ├── 085e5e94c0193bc313159a1aac208bc7232fa24b │ │ │ ├── 08988f30662dbfc7a95bee73cef111dafbcd64af │ │ │ ├── 08a189c1daec7835d24c69454657ea460add9e57 │ │ │ ├── 0928b559c298c803b98e59564c8b6b5443ec1697 │ │ │ ├── 09de090fce19575b878349e5b08e44774cf901b3 │ │ │ ├── 0a44775851faa48a0293c03c5823526a05432a41 │ │ │ ├── 0a73a50a6c6470643bc6ac4731df70e168a1d72a │ │ │ ├── 0b02094a3c9673c0ca1a0f0ebb8a2a1cd334bd5d │ │ │ ├── 0b50b447ee13e7ea31515670dee9cfe0159182a3 │ │ │ ├── 0b656451855e16143574319c2c02c7278916e53d │ │ │ ├── 0b7b607b163793a78dd80383f3dfc1283ae77822 │ │ │ ├── 0b832ffe11d2337ca3efd79aa32140076d90dc67 │ │ │ ├── 0b9b08eca9652ba3a3e389f051e0d19e5e46fd3c │ │ │ ├── 0beed1e9ae76d37f24c30c22c0b254f8efb30744 │ │ │ ├── 0c38e5795a262ad27f8130c77a4bc34ffe7eaa82 │ │ │ ├── 0c5a294e646b32fc10624947911d167a7b550cf4 │ │ │ ├── 0ccd028acb41f2d1902f9ce943084cf855977282 │ │ │ ├── 0ce0d735a0d8e0c637030b8bed3bbcc3a3864866 │ │ │ ├── 0d16aa665e382f2118a4788ac09401f0de985a00 │ │ │ ├── 0d968cc12e55af8d4306e667c5c817b16b8e1b79 │ │ │ ├── 0dfaf14bb1c6bbaf15c4840df4bbcba70727fb2f │ │ │ ├── 0e3efc3ff89fc9956def38fa002510e8f408e4e4 │ │ │ ├── 0ea6b54fd89072271de9d6db7291db91e412ba0c │ │ │ ├── 0eb01abf75b501eab2e9f8cf7c0a46d150e8035e │ │ │ ├── 0ec0f5fdb11fb62a0552c7e13a49c164b757b049 │ │ │ ├── 0f1ba78b09883fc942f3108c0d0238e5bbba7261 │ │ │ ├── 0f88a6fcd8459f99ca2009cc17572e4ee6c57949 │ │ │ ├── 0fa6870ba61bffbcf4331293a0c36abf2a53745d │ │ │ ├── 0fb249d887ffa905f90fe43c97cb0dea4277f682 │ │ │ ├── 0ff985bb50cd92e0904ae0c3bf562a665a9761c8 │ │ │ ├── 107c59baea92d5aa0b7daf7acc49d20b6a25d180 │ │ │ ├── 108fb5fc2033bcad402775791f7d3a83a434d402 │ │ │ ├── 10d4b9dc18446170ba10afb28ca65744f9010ed1 │ │ │ ├── 11c0e4ea13d10a785b7298b416b5d1ec70cb9097 │ │ │ ├── 11d6f314e0ee9b6b319c3e4961ad4b2c041e01f1 │ │ │ ├── 12033d109a3ab4a4068fb4f5283aa8ef2b4b2939 │ │ │ ├── 1388ff1c64cf1523471677300bcc78f2cb057178 │ │ │ ├── 139cf5f51919c2f88794b3d6afecd894d3795bc4 │ │ │ ├── 13a567de304d3293826a5915b124c898d905c533 │ │ │ ├── 13a7d12471b67d795eeb620fd9bcd23e611716b4 │ │ │ ├── 143df8b19029156bef78ed64f99f6a29847fd707 │ │ │ ├── 1489f923c4dca729178b3e3233458550d8dddf29 │ │ │ ├── 14b4cd2980fd137c24ea87f505d3ee4cadfde097 │ │ │ ├── 14c55494f5bfead510993f7aa6b19f2a99d78e64 │ │ │ ├── 15d136091465e9311ea3fff6a81f3a16cd472b9a │ │ │ ├── 15f060997fb0309d15af20cf592235d47e6653cc │ │ │ ├── 167feb1fda57dac588688aa0d0b7a50683620144 │ │ │ ├── 1941ef6ffb0e99428fec8f63ce04c9eac4953bf4 │ │ │ ├── 19c2134264582d9b56d13f26eb96e92d374574c6 │ │ │ ├── 1a51e55c866f939f2d132aef8ff5e600242ec29a │ │ │ ├── 1a7d939aee936be5d25799c3a57f8e8f3c851c89 │ │ │ ├── 1a9160bf021e36d9c2bf8f783e5b24211dbc3a28 │ │ │ ├── 1abb3bd834e95e4044a7eeca6e54a49d3c6ef802 │ │ │ ├── 1af2d87f0c834bdfe46af5d5f18f014c3294c466 │ │ │ ├── 1afe41bb4bfe01753e61e7aac1449e6fa2e96fe6 │ │ │ ├── 1bb9f4d20b726e743eb6af284a699ce4afa36583 │ │ │ ├── 1c62dba4b7e1567683b1824278e7686e26d52834 │ │ │ ├── 1da4e90919ecbf001b876a4972845144cf042f3a │ │ │ ├── 1ec7532018f463cad2ba3d6928fb3c6b46df398a │ │ │ ├── 1f1e84248afa238a7417b29be35fee5af6ba57cc │ │ │ ├── 1ff7f0b20cb7d2be88fe82f1779f71a9b860b7b8 │ │ │ ├── 20e233dd43706cd6dff2c7b774cdc47ead78de71 │ │ │ ├── 213764f0401b8e8a9040c21bc153edff9286d315 │ │ │ ├── 213be3df2b6b577edcbb6f913298974fbb8d7324 │ │ │ ├── 21ae4af22c4c258550b92d3cea9ce479a88c12a2 │ │ │ ├── 21c85dd996a874a885825773fe3ae6f66272ae77 │ │ │ ├── 21d6931263d831c0aed540fd1bab0b60799af038 │ │ │ ├── 21dc828f45d060ee0455004297b50d2b64d17e02 │ │ │ ├── 221c22da6c2c0fd71d5ebc21876fd57cb77b1b00 │ │ │ ├── 22812a981f0458f49a0cae159de66a24631464a3 │ │ │ ├── 22854bee28c089600335e2c16d6c6adad03fba54 │ │ │ ├── 234563c77ded0c75e0583be09a1f4a8a016b8024 │ │ │ ├── 236e3801dd0cf62a835705da142af1587e498516 │ │ │ ├── 24426f52ba7ad6d178e9e20cb28cf9182d51871f │ │ │ ├── 24586a887ef47d547a4bea0a89972fcf3c19c454 │ │ │ ├── 2600c18db42bde973e72b07f0dadf5d32fbfc48a │ │ │ ├── 26514fd3aa2e7d09114d7b5c971e9cbc96e49aba │ │ │ ├── 26ffdd8987ca20c37b2e8b30866e534342d1c175 │ │ │ ├── 274399a85ea96a507d87d6199c58ea701113ec73 │ │ │ ├── 28135abd5f66044cb0fd6f82a3b0dd81b2059593 │ │ │ ├── 281bc1b2b4a20d112c7ea17951397b5a789b5ee8 │ │ │ ├── 28434409ed42ff7b5f6cf4c0731765f70aae12ba │ │ │ ├── 29b8dee814dce4d94d388d035c10d4f6149b934b │ │ │ ├── 29c07937042029d42ded435734bb17a9da1a13ef │ │ │ ├── 29dd4eab6410e5cad789f8ad21b564f5ff580b62 │ │ │ ├── 2a6775c7917e01ee00754918264977fdd60f15ea │ │ │ ├── 2ad3087a982396bd302d9d445d6c5423c248d3c2 │ │ │ ├── 2b58f8d18c8ddfb98618711a7da2afd0efc9bcf8 │ │ │ ├── 2bea1061181244323cdf95ad6a489b72ee38d225 │ │ │ ├── 2cce2108f1d888fdecf0c406fd5fb2ab0448d88d │ │ │ ├── 2d1f6ee26e62965c25a2540c47cda839e86cbc9d │ │ │ ├── 2e047eb1a04b386c558490f7634175199fb86d2b │ │ │ ├── 2eca067d0407b2ce79b7fe3aa0abf38b48178e16 │ │ │ ├── 2eca430ce72ba8cfe14a36dae22c3af594455d78 │ │ │ ├── 300268d606563a63b1fa078deedf83afcaaae4f0 │ │ │ ├── 30122a51022d504d0bd883ec9083db0ebc122f3a │ │ │ ├── 3055e6fc37e2846c56a0a7ef9c910807325c2df1 │ │ │ ├── 30a79f74923a4a0450c38b8d78c32dd0ec49fb1e │ │ │ ├── 30d2affb78dce801693964e7aa7a776e46764142 │ │ │ ├── 33970181a654f47a1b7350a040f66460573c2b37 │ │ │ ├── 33fbd678677fdcc9ff26ce50d77282550ed7b152 │ │ │ ├── 3410eb3f248569d94b015591f12cf76034f8c64d │ │ │ ├── 34c37b9bcc5ca133876f590f4225bd361357eb13 │ │ │ ├── 36388f570f44f86bc535dc8e510721d1c28a0637 │ │ │ ├── 36444b4480602bbc82c421d3a5dca926ee56f17d │ │ │ ├── 378f7e36a287439cdbcb171241c946981517e6b7 │ │ │ ├── 37d5919815fa0c3579c90f5882bc78d2175f2658 │ │ │ ├── 37d7800daba869cfb014eb10e5f46a67fbbbf14d │ │ │ ├── 38197ad1d6330f1836fecfc754c19dcfe8d8da6d │ │ │ ├── 383ecf2dd4fee1f6fad7a2fc6a1014ea612cc705 │ │ │ ├── 38aae1be66ab0f6d35121b4372baa54f3a734cd1 │ │ │ ├── 38d2de4fb4421617c287855ede415e253edf27fc │ │ │ ├── 39cbaf1cff432636f99ae2086461c48f081f3608 │ │ │ ├── 39fbf7272789a966efbe93fc511b64c764d89513 │ │ │ ├── 3a33064962ff92045a47b9409449cf1552d685d7 │ │ │ ├── 3a3fc43f4c64d0305c0d697a33acb209e48971af │ │ │ ├── 3a85b6e409d6180559bbaf0282fe374734ac8ab7 │ │ │ ├── 3aef9b8111cb335c85a241d9f59929e11707730f │ │ │ ├── 3d6d742aaaea3abffa078fd4c03af3cd03c5bb94 │ │ │ ├── 3e21eed3aab591d3aab15d975b2660648ce7bc9e │ │ │ ├── 3e23582c9fe275a17897308f4529ce965ef26639 │ │ │ ├── 3f0c682d7231780f496758f26a19dea90eb0a63f │ │ │ ├── 3f128a449c26d99e65f137b08835072d0f4fa93a │ │ │ ├── 3f5305709a3b6e105cf4b8c1368a8d5c0540a2da │ │ │ ├── 3ffa1177da8e5aa9fe3d071384f8958da89d40a6 │ │ │ ├── 401b5ffa27fd9bcc9c4c99acc3294b51b7ab5435 │ │ │ ├── 403d7b69e46cde2a0c35e051dc0343b283943a6a │ │ │ ├── 419ccf5d9e7c1a163b62230196e1154953a91f9f │ │ │ ├── 42880c44925ec18e3a69b2274b05e0aeff25e454 │ │ │ ├── 434337b14da4a5fb3cdc96cdf47f17517583815c │ │ │ ├── 4351e9a0bb6b53c76164f05125a112cb283cfd81 │ │ │ ├── 4480089669b1f8da9065f5eb9491687d9e31e9da │ │ │ ├── 4487fe428cf14de470fa39e874acece46fcfcbe8 │ │ │ ├── 44df8bdb51a76972b66deb94dfdf5783de99a64e │ │ │ ├── 467d884be458d07e7fd5bdda5fff78c9133a1212 │ │ │ ├── 4852364d47db4ef47740f9184a39e0631aa69cd0 │ │ │ ├── 487b9de35feaf3e349a411438344e46c870120d5 │ │ │ ├── 48ba5eedfbf3c64e54d39fb78dcef8081d4cafc2 │ │ │ ├── 49af95277336d8c0ed0d7a7522e73f8c87d89020 │ │ │ ├── 49d400fc7de4b5392935638c26ce2e62778d416e │ │ │ ├── 4a0034191d5d0e629583d8b06b6a4a81ce024bee │ │ │ ├── 4b32fa9e7beb0500c4f76cdf27a4d1be933ddc68 │ │ │ ├── 4b53f47724ca22ac2b26e7b2191779c9fe74fc4c │ │ │ ├── 4c5e150ebdd825a7ad25ac960eed57c54838c614 │ │ │ ├── 4cb14c262491d3c0e6d47cef077c5ff05353e43d │ │ │ ├── 4d042b03473298b5ef025639d90062272204d6b9 │ │ │ ├── 4dd2cd4a75976db57d91729517748dc8f1e9eb39 │ │ │ ├── 4df44f90bc0f13fb557f109e46647b4d91a5014e │ │ │ ├── 4e5560d451aa505e253511657c115baf69d8e6f8 │ │ │ ├── 4e6876ea3989c69057b0a81a029f83e5246f70dc │ │ │ ├── 4fbc30b7354b6bd455608e7fb5bb7f6feb37941e │ │ │ ├── 532fd1978e5444b41c97d1639fe7b8efc8da14e9 │ │ │ ├── 5378a1d0f6db372c2e26926f3ab692aaff281d5d │ │ │ ├── 53f0d72af41e99e951722231524ec437759422f2 │ │ │ ├── 5458cc3caca58cf412198f6d5085f370b7d1cb97 │ │ │ ├── 5577956e2ca1e38f1ece81a6c2190490c3273f20 │ │ │ ├── 568e139f25140400eefdf6d4e2a38bc21e61d4c0 │ │ │ ├── 56bc35408f7d618b0b2ad0cc28738fa9d38a0bd2 │ │ │ ├── 57e42e4edb7e862c38e24209fea0d89c06034e4d │ │ │ ├── 59837c18261ef4bf33c51437a98d25cebf414840 │ │ │ ├── 598b22649052ab5cf9c8f2d9e43c97c0ca1ecb6f │ │ │ ├── 59f33d4f075c2cf6448f94f623fac85834bdb2cf │ │ │ ├── 59fe75149f202f3e7920a1fd87959628855929a8 │ │ │ ├── 5a02c95e0a7d591f275f196eb5274f1b2209e244 │ │ │ ├── 5aa449aa73c5c05cf57a957914e47a4d2adbd4c9 │ │ │ ├── 5adf5dd22c409ad57c9f6b29eeb92aaafdf5372c │ │ │ ├── 5b6e66c2980a4e9eae54c9996bd413b58e564150 │ │ │ ├── 5bfebbdbbfb4f41a5d5d388504bd062d7a99c3e0 │ │ │ ├── 5c3a3ba5513cfcdc49f76b2009d806141588f85f │ │ │ ├── 5d31e2a814a923c713cd3533f1cf397c902d304e │ │ │ ├── 5d6c6fa498ecdef22875bdb7b8d212f146cc387b │ │ │ ├── 5e05747916bcae6d1d14627260fa82de4caea0d7 │ │ │ ├── 5e399a625d3bb3c6447dafe42e4553fc16950f6e │ │ │ ├── 5eb107e4dc5d78891aac3f0c1e83883b7a663fdf │ │ │ ├── 6015eb2c2f9da9ac11698c60c65c0bb094a66657 │ │ │ ├── 603588985367299d4b4e56c81186715fc8ab5298 │ │ │ ├── 60603d960234eaa4cf2748d2f8b43e1303171ec7 │ │ │ ├── 6235a1b078cf86f65be51d3e4d952688c0cf4027 │ │ │ ├── 62618378c0f30271418e65d9642b681cf5445c1e │ │ │ ├── 62d8ba9122d4a19be633439c04f949e0683f417d │ │ │ ├── 63fbf21dc690999b09177bf4ac4216676ed26b6e │ │ │ ├── 64ea37e683f8f7ad3eb0a9c4ad19d82cfbdc9774 │ │ │ ├── 658886802343686540cb41c5499a46f1705b9d5c │ │ │ ├── 6614bca5723ef48592ebb2efee5bc73e17760aee │ │ │ ├── 664bd1be9730de5ce106f7b2d87774e6549c6ada │ │ │ ├── 66a6d4bff6fa7a62ec7a16d9bf5adf706efea00f │ │ │ ├── 66b622ca15899fc16af4040369d852faf77fd604 │ │ │ ├── 66cee929f075d88bfa46887e565b5d3d148f9154 │ │ │ ├── 6a8a9d4ec0485d6a585db4b56ecfc8779e7888fd │ │ │ ├── 6af377722221a297079f87c07a93c2189ac24fb1 │ │ │ ├── 6b557d729ebff7f4da9b746da200980115618627 │ │ │ ├── 6c4fb24e430367960280c066b72fb107b1c424ab │ │ │ ├── 6c6e255ee96eef814112752fe189fae0dfe78743 │ │ │ ├── 6d44e5997f87b024c53eab22f5912e1988cb66f3 │ │ │ ├── 6d512f956c75ca835d38cbcb6e26e1a2473abd08 │ │ │ ├── 6d72ae632ce79e9c197f5ca799f667d61d2a3df8 │ │ │ ├── 6e62c2d775c1c825125cab26e5a4b004a06a4ba7 │ │ │ ├── 6fcdcf3781990fdceb9b08ba304283f1fd8f9b4e │ │ │ ├── 7016224c7c5f50c8e8aeb6e43851f5e958aa36da │ │ │ ├── 709dcef4bfe4c6162c6a983a3d12eb20ac5cbed7 │ │ │ ├── 70aed57684596e5887c493a5a79dc1d6bcb5eb43 │ │ │ ├── 716d1769833e864f111085008d8322d53a4daf30 │ │ │ ├── 7194db810c5947f300d6035d2a4a743090210e21 │ │ │ ├── 71a5590952fd84047c6d6e9be45bf1f045355625 │ │ │ ├── 73a854c8805585f984236fa44232cb965b9083ae │ │ │ ├── 74bd0a894550c238cee609bb561591118137bc37 │ │ │ ├── 75977e3e56167889e781b65443e91eb49902cfbf │ │ │ ├── 767654d5cbd98934fc45a7ba38f606c4efe7e359 │ │ │ ├── 7737f7fabd55d3fff123dbc65fee826311c2580d │ │ │ ├── 77453d8dc39b44fdf3c80a0ca80c7ead9149de84 │ │ │ ├── 788e547a1eb3cab3960e638885457a0fe112904c │ │ │ ├── 7897d38321a88305b14831654cdb32ba40665cfd │ │ │ ├── 799ee6b016fe616eec6ee67f1977744aee58aee0 │ │ │ ├── 7a0cf48bba4dafb8f490124a89977a93c73988b4 │ │ │ ├── 7a26e6bcc857b6e9b797b3c918f2bcb1a569ddde │ │ │ ├── 7a34b6c09a1ec38856874d086cf899709fa20650 │ │ │ ├── 7ae9e827c496473c836ad4de36f8b029cbb005c8 │ │ │ ├── 7bc478ddfb9efcd7b4650c00e27da372d2cbee86 │ │ │ ├── 7c9291251d2b1628c0bf6b31e2074445408df3e9 │ │ │ ├── 7d7bcfb836b397d537c2b3649c86e9a9c48303e0 │ │ │ ├── 7dcb764405ae26b71cb127b30336b45fd802640a │ │ │ ├── 7dde4f40a01dfa3b917b86b8bd035929132f32db │ │ │ ├── 7e01c8cdc6b1904415e575240588c4759047692d │ │ │ ├── 7e10b31e7041e9432b39bb1ae327355d96dfd62c │ │ │ ├── 7e479935227c90be28bb8a6261fe2141a12821e5 │ │ │ ├── 7e6775e595a452e61552a68facb7aa884fc579e5 │ │ │ ├── 7fbd42707b95e15ea0584236b080b01a86041f0a │ │ │ ├── 80347f920096695efda11b33e2c2d06024c0c82a │ │ │ ├── 808863bfb504bd1a7ba89b4f8eb8c118a9125798 │ │ │ ├── 80a0247ebe3fd2aceff058d956c1c921ed7681a4 │ │ │ ├── 80d94c313466452d7b50fd144838a527809f2c7f │ │ │ ├── 80f348579a9a803a72d4a4cccd54824488b55aac │ │ │ ├── 8211bb03478a5a26c1fb40236780cbee8da45a06 │ │ │ ├── 833ca5a7d4cdf2c1867491c242ece799a01d77a9 │ │ │ ├── 83a58c921e3f326f742c80f8b885c6c6dcc68803 │ │ │ ├── 83a9bf595bc7dea203b912007f70743b88f7af95 │ │ │ ├── 83cd63c7765c3f1d6f7f4f1f9b032f8efd46b3ef │ │ │ ├── 8476e6af7e75aa144bad68b33b499cdac5da2bf4 │ │ │ ├── 8477039b36c6e7e796ce4036aa78d32e00ce0103 │ │ │ ├── 85232cbda473f24e2195b75d84e387f25956a358 │ │ │ ├── 860f19159238baa82dd6680e5f00cd818d869fca │ │ │ ├── 86c5f1d7668cd16af68c6901ef37d10a929b937e │ │ │ ├── 875d260c6259b312bf7adc25aa6d37de04cacef9 │ │ │ ├── 87870c833802ae370b72ded06d6b5466283e5189 │ │ │ ├── 882f2aaac218ec0d84736ac2c55910a4594a1eae │ │ │ ├── 8840b9e11ea5d3aea427c815ebfe1896dc3b765e │ │ │ ├── 8873098a4246f81ee6e5576070d0a7244b3ac9f3 │ │ │ ├── 8904890ebc3c9798fabda724359e14379698ebd4 │ │ │ ├── 891f275cd08dde2776d7140e46cb047faf9f0f8d │ │ │ ├── 89371ce91fe1a6d01ba115e5bf54bf577340b8eb │ │ │ ├── 895e8fd679feb6a39068aba3d31fb206089a064b │ │ │ ├── 8a00c440fa5a60b20d2c8085a38baeaaf3c5670c │ │ │ ├── 8a88097f625c33cc9cbe29343feb52ab248b5c1b │ │ │ ├── 8b0a819cd3fc4d68cf3e0e0855f7ebcd89a20317 │ │ │ ├── 8b98620ebbf7bab27f5ca3fc0ec663cc58c76db9 │ │ │ ├── 8c623105d5f0f8ac252343c858286d65b992687c │ │ │ ├── 8c74dc0dc68a7e61878314355623c887ace5eefe │ │ │ ├── 8d19aefc358ee1284e3a594474f26015586dd9e9 │ │ │ ├── 8dc12bb4d6f47bdce0349d159084f7a90ab432a4 │ │ │ ├── 8df1c55d2c899a91d4de757ad55abeaee00718e5 │ │ │ ├── 8ea0ce1a9c4c228095a13e76e7c43657f8bd6e23 │ │ │ ├── 8eeef9f346f4ffdc8e3e6456301cf775d164c42d │ │ │ ├── 8f1f1bdd61e03a0432e1425982bc95d4eed1dc8f │ │ │ ├── 8fc80309676fc4e9b457cba583e09623e3a50e8b │ │ │ ├── 9032685281013329ab00123038a24e88ee87e99f │ │ │ ├── 903b7c38e142f05daeefa445f48a7da5630e1efe │ │ │ ├── 90fb17406d6d1e431223bda418dc24452cd47cbd │ │ │ ├── 910a4cd86e2bdc41e8e4ae4e06e918ec0bc91b61 │ │ │ ├── 92180b5585dde95b54512c0aa418844642cfbf1c │ │ │ ├── 92a9994a35a5751f87ec0261a50c46f6cac276c1 │ │ │ ├── 932d2c4206b09575f98aba174b4ae7eeecf9dd87 │ │ │ ├── 936b204683d9aace88edec6916348263f8845bd2 │ │ │ ├── 94992041a722e9461cb4da61dc3ca6b1c807fc52 │ │ │ ├── 94f232a0e1b64975bc82dc84a484907795dd3c9a │ │ │ ├── 94f5a62a7150c24701f9b0ccc4f003afaf33b04b │ │ │ ├── 951cfc17803941388dcb27de4933bcf3cfa4e308 │ │ │ ├── 96080c9605fcf493be8852845bc14430217f7f10 │ │ │ ├── 9623152b8ee14d7d2be5fd3ca2eef2085125a009 │ │ │ ├── 965d67a89f658ebc162f41d13880ceb53878398a │ │ │ ├── 969a5335de7a36dab4b48601f83e8289d1e24e14 │ │ │ ├── 971e03f3b57ac29e3328d70fb6a901e50cc1e3c3 │ │ │ ├── 976127759e62f70d02f40d83a2ed5b7951571ceb │ │ │ ├── 97846c394ced714617a96b8b35b582e232a9e7b3 │ │ │ ├── 9888bb78613415dc88bd114be3f2633e747ae22f │ │ │ ├── 991945983caa4499432036384514d356d791b3be │ │ │ ├── 99a14a1d0ef9a631b975d4e3d898f1fc28634572 │ │ │ ├── 99c0f2d3879cc32cf8f28dbe298b8f51780b0691 │ │ │ ├── 99c58901dd5b578c26d9f06a680cd1586d884d0b │ │ │ ├── 99d75bc6a864abd29d7531565e692e87cd78ea7f │ │ │ ├── 9af095b9240a4b086859755798d38cd676803dd2 │ │ │ ├── 9bef829363a2ea30f044c9521dc187bac9e518e8 │ │ │ ├── 9c098fec348d1fb7ebd8f71dc3253896efec0698 │ │ │ ├── 9c5122ab103de543630bc727b76bd7b21839c073 │ │ │ ├── 9d89da8664157ea1b5faa5bde8caf9f31d85b4a4 │ │ │ ├── 9da97ed219829e31b2b2a551b3ba371c7d7ab7f7 │ │ │ ├── 9e1bf8c243c01b2c171f28550c25b974eb3fc910 │ │ │ ├── 9f915817eadb6a53e1ba53785c9e39d3addfb7a7 │ │ │ ├── a019adfa6af044c1f3102fd4eb35f3883eed7767 │ │ │ ├── a03d6067a14043eca56172c64d9d734bbc6d3453 │ │ │ ├── a0c3025c93be787b318b0a0915efce0568f29fb4 │ │ │ ├── a0e3646d2ccccb4a16225b1c4995d6053b4bdd4e │ │ │ ├── a0edc24b3d17348f5cb80d2b6b002ee58db6b035 │ │ │ ├── a101443f19af96e9408021094d007f3da407fb7f │ │ │ ├── a116527e7791e8cfe660a8c53382415e74b1634e │ │ │ ├── a1ca34f30d867511c5071bd62a0ec9532226a626 │ │ │ ├── a204e6fc1dd252eb72786637ece539fc8ccb08be │ │ │ ├── a208cbe77067c1b6f065b09fc05a11f79843e4cf │ │ │ ├── a24bab38ef7386f98fc7f93be8d07c3f6ceb3ece │ │ │ ├── a24c8bbc68f5bbc75f9e4125919196e52f3b3419 │ │ │ ├── a259e1a662bcc421d6834afeab3a47dc344eedba │ │ │ ├── a2967df1f22ff6eb94dc4837d98507c543aae3cb │ │ │ ├── a29d6d6337656a0bd738e477cdd90231050f37cd │ │ │ ├── a2c6ea965e4b89873c59972ad24de3521f5ec565 │ │ │ ├── a3be9919f350dff90746d5bbd335e9c13e075c6b │ │ │ ├── a45e530738453fbacd12389fc0b7c6bb0b5908b5 │ │ │ ├── a4fdd8e51e18577132e9b1d5d9e8504e4d3c96dc │ │ │ ├── a5091dc3125fa52413368b176e86a49738c917a3 │ │ │ ├── a5bd5b33d21e9a221872d7ea360f18433d3f3be1 │ │ │ ├── a6a2deed0d75339e09924b0f19655f6656a15f4f │ │ │ ├── a81a394ce3ae2d25c0ee201c6578069a1b174e55 │ │ │ ├── a9bcae8634ddb628111c2eedd0fcb768f85ff9f5 │ │ │ ├── aa19f2bf66f2268aca0d9eca37dbc685625342a8 │ │ │ ├── aa597b4fbdaf9c8d1c9a17426569c76134ff0fb9 │ │ │ ├── ab437c655c7ad4f2d048b93a9bb3306d0d560e0f │ │ │ ├── ab7ec188984da88b4ab1fb8082a95165d2341dd1 │ │ │ ├── ac0b4253921b9410c5dc7fb082cedf8836ccbcf6 │ │ │ ├── ac15ecf3a7ca5b032a266d1e9e008ee0aa60df50 │ │ │ ├── ac20951ae95689b3ead13a8095570c9a2ed30e5e │ │ │ ├── ac7e926bb0bd586f189a08bd9887dd903b7ca7ab │ │ │ ├── aca10067e6738a677ea8f18c501d1078bc89586b │ │ │ ├── acd71bf955344d94564db8a7d3ab6ed13f5f30e5 │ │ │ ├── ad121b61847802cec3b334da80f661e215dd16d3 │ │ │ ├── adc83b19e793491b1c6ea0fd8b46cd9f32e592fc │ │ │ ├── adcb06d68c467bc49a484a781f3645f2e0fce25d │ │ │ ├── ae4efce72909d7c1fc095fa47f3cf98f4a7ffb34 │ │ │ ├── ae9c533353214d987cef18fd97d4fbaa24c39c09 │ │ │ ├── afeb1f561aa265b0793c0cf08ffbe986ba95cccd │ │ │ ├── b02b1b59c1aaf66b44b15dfc215b921a276f23e9 │ │ │ ├── b071ec550ba1741734797a724d10985838abe3f0 │ │ │ ├── b1256a6b51fe7e4af001152847a41e23822312fd │ │ │ ├── b1898734ba5a8617c5535e2ec02feb33764c5da7 │ │ │ ├── b1ac04528499a30c79bb5a6a887c7f093ffd3d7b │ │ │ ├── b27345719ecf784b471e96fdc2407baaac45ecd0 │ │ │ ├── b378c4f7a72199ec1da13bd6756d4487ac1a9fa3 │ │ │ ├── b399c00c30a1356525d5d39e7cfeb99b2f878bee │ │ │ ├── b41ca4c43c7c6700e337d07a188f3f1a8c13fe46 │ │ │ ├── b41da924198c3920fdaa0b0ff572615dae93f7dd │ │ │ ├── b4899afd21a1c4c6eb7459bf91ed43689919798e │ │ │ ├── b49b6c4ec35af87447943af9acb1a14a637ba4ae │ │ │ ├── b49dddf68a09edcb754e5d95cadc35e2683df728 │ │ │ ├── b4c66e7c06855671e747f3100c9d5072b79cf83a │ │ │ ├── b52f85fe855438edbb3177ba64ea88040c5068c3 │ │ │ ├── b5945b57aad75381e3a3545542330957d8af699b │ │ │ ├── b5eefc1ac36df7e12eaf561ea2198bd05650348f │ │ │ ├── b68b13e7dbf523d3850bfb4ee1879ee267ce1309 │ │ │ ├── b6f15ce61e23d12e16ac972ece7ecb71a1f4c87a │ │ │ ├── b7294356ab92ecb2a8f35f0b61cb2f3fc68af2f7 │ │ │ ├── b7719b430c03af92aef14110ee2fbf3abba0de5d │ │ │ ├── b7cd4bb8ad5ec4e73e07a3cda397faa3f19e4286 │ │ │ ├── b8e9125a0ae565f71d1d93249e347d56068d42bf │ │ │ ├── b9a93efe3d76aecec38a121521ae64810a0769c2 │ │ │ ├── bb1e5e53f6a1253fe1e896b50e9edc0095e2932b │ │ │ ├── bd7c4c25399bf8f278eb615f58b75b8f72349bf6 │ │ │ ├── be7f31b717f86fda96b3148fb706908303e17f8f │ │ │ ├── bee6d10492fd61954b05c5108f958c5406b30fc9 │ │ │ ├── befde2b724d2370f61d7dc2da95d1be21a9c862b │ │ │ ├── c02bf5d08db57f3aae88172e17cad08ebd076c22 │ │ │ ├── c18fefd5d4d05d1ee9f162801aa6e17f428ba8da │ │ │ ├── c1b2bcb63da8041ce87b328132217ba70d920fca │ │ │ ├── c1df12a64dd8abefb0e6590527cb10ed692cf324 │ │ │ ├── c21a4d67d547c0845676886c6892c8c6827d75e2 │ │ │ ├── c2bec5c726a06b456cdd88f28a32f48831044a11 │ │ │ ├── c32505224c93f972f500f44afb188d3889dfa159 │ │ │ ├── c3670cccd1b3582fb1ec1b742f6e2898cfe26555 │ │ │ ├── c3ed53cf882409c54cc6c988aae34deacecf057a │ │ │ ├── c63072e6cf44fde5c8ec3e7ea1a5328497ea5479 │ │ │ ├── c6435d401d81e17e25e273719e4f1fa7fe1b53b7 │ │ │ ├── c70643d26ec7090724a54c6e87ea68c89d4c4e91 │ │ │ ├── c7fb3f5be75a6dacfc3e28abbf73fe5e58495d30 │ │ │ ├── ca576072e488152f50e6f1158a1396f73ddd2c07 │ │ │ ├── cb2edb9e0c977a705a6f29fae0934079a1b60c92 │ │ │ ├── cb864d68d0b90ecc000956495f457f2da8cc0181 │ │ │ ├── cbe9914a17fe70a1883c2dab7491fd0211a5ee38 │ │ │ ├── cc34b4cfee9dc462dcc95d7560a21cbf4adcf7b0 │ │ │ ├── cc652bd0ebdb4b1726182372be30c691099f7e8c │ │ │ ├── ccaac59aa2a93544b76f073311b350dcf65fbf5e │ │ │ ├── ccc73c223aabaa38c58624ea80eb778c645346f2 │ │ │ ├── cd04f9a383899d9640ff1e9707e47e77afdd9562 │ │ │ ├── cd2ef6d312ee80a3d4c377361fddbb793889f4c8 │ │ │ ├── ce010e3e4729a79b9793c64a4532b53c54c9430c │ │ │ ├── ce3e985cb8f77fcd48e3ea06f922260a2dd36fda │ │ │ ├── cf7e04eb0562f842999ecc3f4ec6877fa4f1e342 │ │ │ ├── d1020364827748c3a6e1bf09c6e75a5edf44079e │ │ │ ├── d19cd0207ab449752da6b55a82975be7f056a70b │ │ │ ├── d1ee881e19534be9fc12fd862bb3f2269a09bdd7 │ │ │ ├── d26ba8c01b80dbb3ea1a50d429b832a33b6af614 │ │ │ ├── d2b69ab4a6972e182cdef5c319be940062388300 │ │ │ ├── d38a79abc527773b20840f0ac21b618049746c32 │ │ │ ├── d3ae4114281302b6810c02cee903e2f05aea79ce │ │ │ ├── d3c908f06f91576b96d60c7b165811e69993ae78 │ │ │ ├── d42d277470474b4dbf7a5181f298b299d3028a1a │ │ │ ├── d488eca7d059c07b0f9e8ad7d7206ace887cda85 │ │ │ ├── d51ab1559f11d168cd4cdf123602113f60732e79 │ │ │ ├── d8130b7018da880f1090b166df7bb13810f101bd │ │ │ ├── d8e267ac5306c3dae043f15df37dae5b76a44a52 │ │ │ ├── d9437de0ae53e80a335a15a00ec9e622f2fb89d3 │ │ │ ├── da5dc3160083ac54ec11a162d23293ffef7029c8 │ │ │ ├── daa6495ffb6c5cbf5cd3dbf3b7b8bfc42803d1d2 │ │ │ ├── daed34b7f0f3a2f9b0716b14514473d6efa38c3f │ │ │ ├── dba6c808eafe79949543a1f50ee39f82de48ccee │ │ │ ├── dbb41d9522e68d0faf7f5a03f41aed64e7b9d7ee │ │ │ ├── dc119760a36e1c532dc48cf83cb4ff18cbef6c15 │ │ │ ├── dcc6432dae6ed005aaa9f060c4909bb89c1d98d8 │ │ │ ├── dcf81fe92865368756d9729c63739c37b38f037b │ │ │ ├── dd0da9f51f2783d95680664efb3f5a322ee885b5 │ │ │ ├── dd3f58c65c86aea5827d99b4fe213b183fc79107 │ │ │ ├── de676ef0bc27e3411664f351ea13ee0f2901a8f1 │ │ │ ├── df9bc143232105f71dbc4cd6f29f1faf40adc348 │ │ │ ├── e01aeb76d2ded444ad0373a68f2c784db09ca0bd │ │ │ ├── e0938f8484d9bc11fc2e6c6a95b95bcda639c25a │ │ │ ├── e0db3d59e7b298f022cace1fd52d3875e5fc9694 │ │ │ ├── e255f1112e76bf142d77ee3c42cda90853fefeaf │ │ │ ├── e26caaba5987ef0e4c8e8960a65d163eb739b2a2 │ │ │ ├── e27d3a4fd0455fcd2766c39bf0c71431549a65cb │ │ │ ├── e2ca6c74ac95f6b766e427545f297fa1df3e7ae8 │ │ │ ├── e358ad9b805d394bdc48b7258e0c3b8becb2513b │ │ │ ├── e36e20af158c5a94704e98e0ee695eca5c85d557 │ │ │ ├── e4faefa4502eaf582fb184a2300122f40de097af │ │ │ ├── e5f2b058f54b45dc07f3b9d6e4637e8b7af2c5a7 │ │ │ ├── e72cc6d41d020b5c579ab185b34447ec9520f86c │ │ │ ├── e82759d6621dc86de4cf5033868a3673e74053dd │ │ │ ├── e8546e69f53ac69e138b8a739d321f629be57164 │ │ │ ├── e8d8ab82dea469e3e3c7a536df5a77e3ad899129 │ │ │ ├── e8e793189ab4cce6a062fa09c0740693b6ce1e19 │ │ │ ├── e8ec7cc1ca98820ae6e98c1354ec2d6bece46ef2 │ │ │ ├── e91c47ea25efbbfb60ba17d786caa9659bbab6d2 │ │ │ ├── ea17e12d2e985a026980a016b72acacb30244a76 │ │ │ ├── ea69aad1620b5025cc85f415080cd799b66ebc68 │ │ │ ├── ea6da9d77a360c4826d1a29c2bf40e9209bc1c0b │ │ │ ├── eb2f5a2dd2fee2ef20a54406427e10043b345485 │ │ │ ├── eb6be60b4aa6b17f16953f6095b6ea7efa92f6d4 │ │ │ ├── ec34b45a65e9bad07d332490983b305e6f1d358b │ │ │ ├── ec6905048c1682c644f01e41531ab3d87e0a2247 │ │ │ ├── ed2df06c65bbfdf156d4afc88bdc66d9d3290613 │ │ │ ├── ed93b3caa649c0dc8d2e7bc2fa885ecd7ad0ba36 │ │ │ ├── edd491a75e971973bd4cd9dc84a0023cebd62f92 │ │ │ ├── edf2f1a4740661b72760a302a261518fa842bde0 │ │ │ ├── eec540334e2254864e033353ec23daa556fadcd0 │ │ │ ├── eee58a61a1959843405fb465464ec698f6181a6f │ │ │ ├── ef6721b59164581f5525e2e2f16bbdb626de6ba7 │ │ │ ├── f05b2bd236751d7863172a380f5ab4315344968e │ │ │ ├── f0c16a24aba99a02eb9924dd4a4725ab8e92ffad │ │ │ ├── f10576d1e2fb48314b8dfbfbeb5d708defe6097b │ │ │ ├── f17479189c9591f3c73906ed84b33b87ac6e8e1f │ │ │ ├── f1778e9e69871a38d2ce4d8b7c4a3e0b35ece46a │ │ │ ├── f2e3dc7f73104b527baf917360356e7994267cdd │ │ │ ├── f3b6007923c6e42984caf9c5fb239cf38815e841 │ │ │ ├── f3da8335de7de651461ad28357d506e940d18bdf │ │ │ ├── f40831624c06f31927bd2af861887ad578246010 │ │ │ ├── f4ddda0ae8524f4f632d5d03291b59acc1952a74 │ │ │ ├── f53e9d74f34d349f856d8aba3f40712bcfe8d916 │ │ │ ├── f5581adcf52c7acfd5fa26cbb317231aff1d9ed7 │ │ │ ├── f57bb62ad6d29b8f4d1fb3baa4474f3af4e0cbab │ │ │ ├── f6117176339050e85b72129ff118f2713b118931 │ │ │ ├── f66e211bfde320bf50efe03fde1cbbbf5df440c6 │ │ │ ├── f6a16230ec874c52b6c1cd73ccc0b9dee2dd5574 │ │ │ ├── f7014162c0e7c8229146243ca875cec5757f6b39 │ │ │ ├── f7e31e99e09ddb661dc4a5f639f48baa48e233fb │ │ │ ├── f9373536de828c70789357df4be4069df64d5dc2 │ │ │ ├── f9a0b5a03a08526b1622f57bf3cefae54eb49abd │ │ │ ├── f9a6502095aa770e7213f96511a3d63f33757674 │ │ │ ├── f9b7f4ad29450f6bdf1baf2663c7b86a7ec39cfc │ │ │ ├── faa689e2f7b3c3877c1581ae943cffa5c037ffbf │ │ │ ├── facca3ae362b2fab961eb9e724b3790b1977d0a8 │ │ │ ├── fbe65ad556a0f0fe9b8215385e70da5555a3fba1 │ │ │ ├── fc880c8c473a9d0b14bb5827260c1170e8ca9535 │ │ │ ├── fc96ab8937b4d16598d2831e4d1f89df4b720d72 │ │ │ ├── fcc91d6415ebf086ccc8b84ef729d5099cc19a84 │ │ │ ├── fd958cb8a73d5d70d0be71a387c7973c3b1df2e9 │ │ │ ├── fdce145b982b47c9598cfe55883672e86727d5b8 │ │ │ ├── fe9a584f5c7c5d9eb5e4b15f85960ca7f1079a2e │ │ │ ├── fecd4b2d80be6971216cd554f44793b3b8adaba6 │ │ │ ├── fef279c42c269b2a38ccfbf781f73897284ec2b2 │ │ │ ├── ff5f77350f4850c12dc9576ea268de27bb55ade1 │ │ │ └── ffcfd70e30e22415a17a3152c0ba1cc0f9f756a3 │ │ ├── helpers.cpp │ │ ├── helpers.h │ │ ├── lex.cpp │ │ ├── lex.h │ │ ├── numeric_literal.cpp │ │ ├── numeric_literal.h │ │ ├── numeric_literal_benchmark.cpp │ │ ├── numeric_literal_fuzzer.cpp │ │ ├── numeric_literal_test.cpp │ │ ├── string_literal.cpp │ │ ├── string_literal.h │ │ ├── string_literal_benchmark.cpp │ │ ├── string_literal_fuzzer.cpp │ │ ├── string_literal_test.cpp │ │ ├── test_helpers.h │ │ ├── testdata/ │ │ │ ├── basic_syntax.carbon │ │ │ ├── char_literals.carbon │ │ │ ├── dump_sem_ir_range.carbon │ │ │ ├── fail_bad_comment_introducers.carbon │ │ │ ├── fail_bad_comment_introducers_mid_block_indent_change.carbon │ │ │ ├── fail_bad_raw_identifier.carbon │ │ │ ├── fail_block_string_second_line.carbon │ │ │ ├── fail_char_literals_bad_encoding.carbon │ │ │ ├── fail_mismatched_brackets.carbon │ │ │ ├── fail_mismatched_brackets_2.carbon │ │ │ ├── fail_multifile.carbon │ │ │ ├── fail_trailing_comments.carbon │ │ │ ├── include_in_dumps.carbon │ │ │ ├── keywords.carbon │ │ │ ├── multifile.carbon │ │ │ ├── multiline_string_literals.carbon │ │ │ ├── numeric_literals.carbon │ │ │ ├── printing_digit_padding.carbon │ │ │ ├── printing_integer_literal.carbon │ │ │ ├── printing_real_literal.carbon │ │ │ ├── printing_token.carbon │ │ │ ├── raw_identifier.carbon │ │ │ ├── repeated_tuple_index.carbon │ │ │ └── string_literals.carbon │ │ ├── token_index.h │ │ ├── token_info.h │ │ ├── token_kind.cpp │ │ ├── token_kind.def │ │ ├── token_kind.h │ │ ├── token_kind_test.cpp │ │ ├── tokenized_buffer.cpp │ │ ├── tokenized_buffer.h │ │ ├── tokenized_buffer_benchmark.cpp │ │ ├── tokenized_buffer_fuzzer.cpp │ │ ├── tokenized_buffer_test.cpp │ │ └── tokenized_buffer_test_helpers.h │ ├── lower/ │ │ ├── BUILD │ │ ├── aggregate.cpp │ │ ├── aggregate.h │ │ ├── clang_global_decl.cpp │ │ ├── clang_global_decl.h │ │ ├── constant.cpp │ │ ├── constant.h │ │ ├── context.cpp │ │ ├── context.h │ │ ├── file_context.cpp │ │ ├── file_context.h │ │ ├── function_context.cpp │ │ ├── function_context.h │ │ ├── handle.cpp │ │ ├── handle_aggregates.cpp │ │ ├── handle_call.cpp │ │ ├── handle_expr_category.cpp │ │ ├── lower.cpp │ │ ├── lower.h │ │ ├── mangler.cpp │ │ ├── mangler.h │ │ ├── options.h │ │ ├── specific_coalescer.cpp │ │ ├── specific_coalescer.h │ │ └── testdata/ │ │ ├── alias/ │ │ │ └── local.carbon │ │ ├── array/ │ │ │ ├── array_in_place.carbon │ │ │ ├── assign_return_value.carbon │ │ │ ├── base.carbon │ │ │ ├── field.carbon │ │ │ ├── function_param.carbon │ │ │ └── iterate.carbon │ │ ├── basics/ │ │ │ ├── empty.carbon │ │ │ └── fail_before_lowering.carbon │ │ ├── builtins/ │ │ │ ├── bool.carbon │ │ │ ├── char.carbon │ │ │ ├── cpp.carbon │ │ │ ├── float.carbon │ │ │ ├── int.carbon │ │ │ ├── int_literal.carbon │ │ │ ├── maybe_unformed.carbon │ │ │ ├── method_vs_nonmethod.carbon │ │ │ ├── no_op.carbon │ │ │ ├── overloaded_operator.carbon │ │ │ ├── pointer.carbon │ │ │ ├── print_read.carbon │ │ │ ├── types.carbon │ │ │ └── uint.carbon │ │ ├── class/ │ │ │ ├── adapt.carbon │ │ │ ├── base.carbon │ │ │ ├── basic.carbon │ │ │ ├── convert.carbon │ │ │ ├── field.carbon │ │ │ ├── generic.carbon │ │ │ ├── import.carbon │ │ │ ├── method.carbon │ │ │ ├── self.carbon │ │ │ ├── value_access.carbon │ │ │ └── virtual.carbon │ │ ├── debug/ │ │ │ └── nodebug.carbon │ │ ├── for/ │ │ │ ├── bindings.carbon │ │ │ ├── break_continue.carbon │ │ │ └── for.carbon │ │ ├── function/ │ │ │ ├── call/ │ │ │ │ ├── empty_struct.carbon │ │ │ │ ├── empty_tuple.carbon │ │ │ │ ├── form.carbon │ │ │ │ ├── i32.carbon │ │ │ │ ├── implicit_empty_tuple_as_arg.carbon │ │ │ │ ├── params_one.carbon │ │ │ │ ├── params_one_comma.carbon │ │ │ │ ├── params_two.carbon │ │ │ │ ├── params_two_comma.carbon │ │ │ │ ├── params_zero.carbon │ │ │ │ ├── ref_param.carbon │ │ │ │ ├── ref_return.carbon │ │ │ │ ├── return_implicit.carbon │ │ │ │ ├── struct_param.carbon │ │ │ │ ├── tuple_param.carbon │ │ │ │ ├── tuple_param_with_return_slot.carbon │ │ │ │ └── var_param.carbon │ │ │ ├── declaration/ │ │ │ │ └── simple.carbon │ │ │ ├── definition/ │ │ │ │ ├── destroy.carbon │ │ │ │ ├── empty_struct.carbon │ │ │ │ ├── eval_musteval.carbon │ │ │ │ ├── params_one.carbon │ │ │ │ ├── params_two.carbon │ │ │ │ ├── params_zero.carbon │ │ │ │ ├── raw_name.carbon │ │ │ │ └── var_param.carbon │ │ │ └── generic/ │ │ │ ├── call.carbon │ │ │ ├── call_basic.carbon │ │ │ ├── call_basic_depth.carbon │ │ │ ├── call_dedup_ptr.carbon │ │ │ ├── call_deref_ptr.carbon │ │ │ ├── call_different_associated_const.carbon │ │ │ ├── call_different_impls.carbon │ │ │ ├── call_different_impls_with_const.carbon │ │ │ ├── call_different_specific.carbon │ │ │ ├── call_impl_function.carbon │ │ │ ├── call_method.carbon │ │ │ ├── call_recursive_basic.carbon │ │ │ ├── call_recursive_diamond.carbon │ │ │ ├── call_recursive_impl.carbon │ │ │ ├── call_recursive_mutual.carbon │ │ │ ├── call_recursive_reorder.carbon │ │ │ ├── call_recursive_reorder_more.carbon │ │ │ ├── call_recursive_sccs_deep.carbon │ │ │ ├── call_specific_in_class.carbon │ │ │ ├── cross_library_name_collision_private.carbon │ │ │ ├── import.carbon │ │ │ ├── local_function.carbon │ │ │ ├── reverse_canonical.carbon │ │ │ ├── self_canonical.carbon │ │ │ ├── type_param.carbon │ │ │ └── type_representation.carbon │ │ ├── global/ │ │ │ ├── class_obj.carbon │ │ │ ├── class_with_fun.carbon │ │ │ ├── decl.carbon │ │ │ ├── simple_init.carbon │ │ │ ├── simple_with_fun.carbon │ │ │ └── use.carbon │ │ ├── if/ │ │ │ ├── else.carbon │ │ │ └── no_else.carbon │ │ ├── if_expr/ │ │ │ ├── basic.carbon │ │ │ └── empty_block.carbon │ │ ├── impl/ │ │ │ ├── assoc_fn_alias.carbon │ │ │ ├── extend_impl.carbon │ │ │ ├── impl.carbon │ │ │ ├── import.carbon │ │ │ ├── import_facet.carbon │ │ │ ├── import_thunk.carbon │ │ │ ├── instance_method.carbon │ │ │ └── thunk.carbon │ │ ├── index/ │ │ │ └── array_element_access.carbon │ │ ├── interface/ │ │ │ ├── assoc.carbon │ │ │ ├── basic.carbon │ │ │ ├── mangle_declared_class.carbon │ │ │ └── where.carbon │ │ ├── interop/ │ │ │ └── cpp/ │ │ │ ├── base.carbon │ │ │ ├── clang_code_generator_callbacks.carbon │ │ │ ├── constructor.carbon │ │ │ ├── cpp_compat_int.carbon │ │ │ ├── cpp_run.carbon │ │ │ ├── enum.carbon │ │ │ ├── extern_c.carbon │ │ │ ├── field.carbon │ │ │ ├── function_decl.carbon │ │ │ ├── function_in_template.carbon │ │ │ ├── globals.carbon │ │ │ ├── import_inline.carbon │ │ │ ├── method.carbon │ │ │ ├── nullptr.carbon │ │ │ ├── parameters.carbon │ │ │ ├── pointer.carbon │ │ │ ├── reference.carbon │ │ │ ├── return.carbon │ │ │ ├── std_initializer_list.carbon │ │ │ ├── template.carbon │ │ │ ├── virtual_base.carbon │ │ │ └── void.carbon │ │ ├── let/ │ │ │ ├── copy_value_rep.carbon │ │ │ ├── local.carbon │ │ │ └── tuple.carbon │ │ ├── namespace/ │ │ │ ├── function.carbon │ │ │ ├── namespace_run.carbon │ │ │ └── nested.carbon │ │ ├── operators/ │ │ │ ├── and.carbon │ │ │ ├── and_empty_block.carbon │ │ │ ├── arithmetic.carbon │ │ │ ├── assignment.carbon │ │ │ ├── increment.carbon │ │ │ ├── not.carbon │ │ │ ├── or.carbon │ │ │ ├── or_empty_block.carbon │ │ │ ├── overloaded.carbon │ │ │ └── string_indexing.carbon │ │ ├── packages/ │ │ │ ├── cross_package_call.carbon │ │ │ └── imported_package_mangle.carbon │ │ ├── pointer/ │ │ │ ├── address_of_field.carbon │ │ │ ├── address_of_unused.carbon │ │ │ ├── basic.carbon │ │ │ ├── convert.carbon │ │ │ └── pointer_to_pointer.carbon │ │ ├── primitives/ │ │ │ ├── bool.carbon │ │ │ ├── int_types.carbon │ │ │ ├── numeric_literals.carbon │ │ │ ├── optional.carbon │ │ │ ├── string.carbon │ │ │ ├── type_values.carbon │ │ │ └── zero.carbon │ │ ├── return/ │ │ │ ├── code_after_return.carbon │ │ │ ├── no_value.carbon │ │ │ ├── return_var.carbon │ │ │ ├── return_var_byval.carbon │ │ │ ├── value.carbon │ │ │ └── var.carbon │ │ ├── struct/ │ │ │ ├── empty.carbon │ │ │ ├── member_access.carbon │ │ │ ├── nested_struct.carbon │ │ │ ├── nested_struct_in_place.carbon │ │ │ ├── one_entry.carbon │ │ │ ├── partially_const.carbon │ │ │ └── two_entries.carbon │ │ ├── tuple/ │ │ │ ├── access/ │ │ │ │ ├── element_access.carbon │ │ │ │ └── return_value_access.carbon │ │ │ ├── empty.carbon │ │ │ ├── nested_tuple.carbon │ │ │ ├── nested_tuple_in_place.carbon │ │ │ ├── one_entry.carbon │ │ │ ├── two_entries.carbon │ │ │ ├── value_formation.carbon │ │ │ └── value_forwarding.carbon │ │ ├── var/ │ │ │ ├── global.carbon │ │ │ ├── import.carbon │ │ │ ├── local.carbon │ │ │ ├── nested.carbon │ │ │ └── uninitialized.carbon │ │ └── while/ │ │ ├── break_continue.carbon │ │ ├── preheader.carbon │ │ ├── unreachable_end.carbon │ │ └── while.carbon │ ├── parse/ │ │ ├── BUILD │ │ ├── context.cpp │ │ ├── context.h │ │ ├── coverage_test.cpp │ │ ├── dump.cpp │ │ ├── dump.h │ │ ├── extract.cpp │ │ ├── fuzzer_corpus/ │ │ │ ├── 0070c4b257a5d62735b72ecbd27831689e0f741a │ │ │ ├── 00983b0026fb2dae8de02df8c2bf4d25ad9d2957 │ │ │ ├── 00f54312e38b5706db8c824c9c52c495d2283a82 │ │ │ ├── 01ecec351990aceb79096662578dbcebef530266 │ │ │ ├── 02081880688a5ba27e5382e4989d62f71bffe7d2 │ │ │ ├── 02927414c09a1e0ebc8728d6e2e3507d297720e0 │ │ │ ├── 02ddec8b4855127d5616e521bc826659cb04a796 │ │ │ ├── 0388b2df0537639ff948074ce9f67c8b5ec81724 │ │ │ ├── 039a35c0b5788e58d490e6170c5e2a3ca93126b7 │ │ │ ├── 04001c5feb6d32510ea2dac4a7e3b2782178d233 │ │ │ ├── 043f9a99b06b50b4f72d7d64785c9f6aed8955be │ │ │ ├── 044b47fa071d1a9a649f6929df2a86a602316739 │ │ │ ├── 049cb040706926a43a72ba2eed2293fde16ee4f7 │ │ │ ├── 04e69659919613110074c6b55a2535005628624b │ │ │ ├── 062300fe49127d478e279f2c8c6624cf921e761b │ │ │ ├── 073fa4c84b38942fe8294893b1d8ee760c824f37 │ │ │ ├── 076affaa4c46bf4b9b399969094dc72a9df8d775 │ │ │ ├── 07872cd439777dea0917e53c4070c4e2f5499c34 │ │ │ ├── 0847ff144a8a5fd6df2efc24062333302de31d8a │ │ │ ├── 08de8afe0f4b1a7482f82b4483ed8e67ff9d3950 │ │ │ ├── 09010764cf42320a71f4ef9f9f4b07aa9ff3fb28 │ │ │ ├── 0927368a50716a0cf040472d656fadaaf03355de │ │ │ ├── 09943ce3320190181bc98edd7a8f25b342a4717a │ │ │ ├── 09f51bfa00ad03af42fbf220cdfe078cb86eba82 │ │ │ ├── 09f88ffa07c965dec16c66e720ca192c8c069375 │ │ │ ├── 0a7ef04e3f3e44d0ecc02728a5b2e7766b961983 │ │ │ ├── 0aa3cf02285b3dc71f066761840882453a7b2206 │ │ │ ├── 0ad8d7ffe749474f00b83fdc0ab63fd0efe4be17 │ │ │ ├── 0b656451855e16143574319c2c02c7278916e53d │ │ │ ├── 0ba5482c447a7032f934f443de5577fe83169fcd │ │ │ ├── 0d75c206d438f55ffb3055aafa6ece4dc0b1d8c2 │ │ │ ├── 0d968cc12e55af8d4306e667c5c817b16b8e1b79 │ │ │ ├── 0db8178cf99d00340ad773ad8cbd9559037707f3 │ │ │ ├── 0e39781318c2c288f8b50d2599e5db584ca6403c │ │ │ ├── 0e7ed085031d14c75de885a43f8055de2f3771c5 │ │ │ ├── 0ee1b55e445f4a407f08dbc3bbf8c19b11d1d56c │ │ │ ├── 0f3abe6df8abad7e60c636a1b55987e10596a9a8 │ │ │ ├── 0fcf0bd2e6e6735804be2e231844f4b0035c6658 │ │ │ ├── 0fe5a6a76aaaf81ee9456d3c7316edec7e8d0767 │ │ │ ├── 10de165e3456bde804899c08a8b74b6a490f1eba │ │ │ ├── 114f91b835c2b13afc7c92dc7c98eabbc7c4c3a5 │ │ │ ├── 12033d109a3ab4a4068fb4f5283aa8ef2b4b2939 │ │ │ ├── 1208be067b482d13dffa6e84244371f7266c8160 │ │ │ ├── 128fa7deaa928f1d747d86062e5d3d213c25c726 │ │ │ ├── 12b4b334134a4d960f633e6c4099056d6254cf51 │ │ │ ├── 12ec48cdf6b94105e7ade03f1c2963d13ac0d77c │ │ │ ├── 139cf5f51919c2f88794b3d6afecd894d3795bc4 │ │ │ ├── 13da308a93caa154fd268cb66ff9350d5e001308 │ │ │ ├── 143e2326892e723c0b68722b1e4b58a44e61e839 │ │ │ ├── 1489f923c4dca729178b3e3233458550d8dddf29 │ │ │ ├── 150b1dea9b203b242440628e55aa9962d328e970 │ │ │ ├── 1518eddffd2ea99c2db347e4b07bbb9c2e148616 │ │ │ ├── 15457429ccbc4ced908ec8d06f103c2c98bbbdb3 │ │ │ ├── 155662963cb5f19922dab58838168228b1e014c3 │ │ │ ├── 1571c4355b51fd1956088954d879c57afc5163ff │ │ │ ├── 168dc21309d61fe1e063df68c2a5f149311cfcdc │ │ │ ├── 16b64cf94678e34113f9455ed7800c90a0027dec │ │ │ ├── 176afca999aa82b2b71ea72c3240ba7776079f15 │ │ │ ├── 17bbee402bd8cdd6e386a039cb0023ceee7c21b0 │ │ │ ├── 18be96b3ef011defa68c04113354445d6677fadf │ │ │ ├── 191074e33f978a2a8d63c9e1e34bb17077a9a588 │ │ │ ├── 19693216f273f9aff7108346e4bcd2f8c966f4ae │ │ │ ├── 1a1c812723cfc99c0d1950586bf64a8a0bd8dd21 │ │ │ ├── 1b39bdf9057eb34a7ce083c976d91beaff9243a2 │ │ │ ├── 1c049c5e2792a5a37dd9b381673d185ab4f0f9ee │ │ │ ├── 1cf7234e1ba7123b380fc9bdb1be2e3f01dac04d │ │ │ ├── 1e6d3c3b55ccb0d68d53c73a555b846e6cf8186c │ │ │ ├── 1fa756fd20f8cdf9686442489978b5c949f03c94 │ │ │ ├── 2075398620e9ece84ba45696102d607461d3013e │ │ │ ├── 207a33e16f5a0d38333804850baa752141048077 │ │ │ ├── 2185caa3a086573629b49f7030abfbc5cb164a05 │ │ │ ├── 21ab82bcdb73cef5c88faccf2930439a80f6aa2a │ │ │ ├── 2380c194a3cf4ebd90289510bd36d3237c4eff90 │ │ │ ├── 2405a901ebd782565915e40a9c7175af077da320 │ │ │ ├── 2431f06ad2f7e5a00c3edcf50bd8df35ad2d213a │ │ │ ├── 248e15cac0d0566b32ee1de84f1a97258c808048 │ │ │ ├── 2494a52ac3fd2d281018a9de8d8b99a96d23b755 │ │ │ ├── 24f09b8f67911a60fb76b0787a3aa5c51b9e4aa7 │ │ │ ├── 259f67ed1c6eddaf7ff1b7a57354e8e05b1764b7 │ │ │ ├── 25eb49052f569f2b6224ec69827bb0559c6266ce │ │ │ ├── 25f17147065a7604fe02ff52c22d77d55f46aec0 │ │ │ ├── 26375a214ff72dcec3d99a0e8c3e12f48c174f91 │ │ │ ├── 26ffdd8987ca20c37b2e8b30866e534342d1c175 │ │ │ ├── 27b24837f9fb8b231882aba80241a51a732d7080 │ │ │ ├── 285fc11d7f2b301050dad1055ae07ae8ea434fa7 │ │ │ ├── 29189967d00b3cbd0bec6d3ba7a0ba53593d68b0 │ │ │ ├── 298d9fd041f4045e8a6f51395cb9b1f12f84740c │ │ │ ├── 29c07937042029d42ded435734bb17a9da1a13ef │ │ │ ├── 2a02e597d1a05281f3438aa56e604af5ea226391 │ │ │ ├── 2a632a8af327465dfa149e5e36f761e0c3ed9cbb │ │ │ ├── 2adee695b16f21579babe698c46496bc7888d515 │ │ │ ├── 2b54b86a7a03996193e2a16e2dedc7bc3c4fdcf8 │ │ │ ├── 2b5d3b133ba8094e7e96c18bbb5750320af115b7 │ │ │ ├── 2bb15c06e73edf03fe6da1d344054e7629da174e │ │ │ ├── 2bd5448f636ce587c431dcafc751d97bc6ad184e │ │ │ ├── 2c5052fa044a9adda5adc2c63bb504f021b94ce0 │ │ │ ├── 2cb15df85cd18a7c7a5c0a6144c52898f33b85b1 │ │ │ ├── 2d43a65482c23c269139ef944a7bc83c7cef365d │ │ │ ├── 2e14812407ae3bbcc0897edec4dd60a23cc24362 │ │ │ ├── 2f02a7cf2f97d748af0c7a5ea9b08709497f5a46 │ │ │ ├── 2f5199b8dbb288d7772573dfd072a01ab6c5ee0d │ │ │ ├── 31285759f36b7a75479ca9b1df982ce5f97548a1 │ │ │ ├── 31d33fa3b66c317fea1bf723e0f18e17778b8189 │ │ │ ├── 320a9de88d971d897339ca8a03fa0f6249115c85 │ │ │ ├── 32726187689ba9e51e68aeba228d3675fce9b0ce │ │ │ ├── 339850af6f14c8ce8d036c13c2a0e3e5425bce6a │ │ │ ├── 341f7c736b9635585cc9c3eba7a778c2c0b630d4 │ │ │ ├── 34792f34f593fa3f456d026fa9ffe1503cac1b77 │ │ │ ├── 34ad684f808d905e7cb4f1206fb0f2379276a591 │ │ │ ├── 35756112846aec20c0ea410e4e176bdc7aa6d088 │ │ │ ├── 36444b4480602bbc82c421d3a5dca926ee56f17d │ │ │ ├── 3715d976d21ae2a2c15495c4a763394e990b90a8 │ │ │ ├── 386740becc4c02670666928a4699c330b2fe1152 │ │ │ ├── 394792793e4f314d0886cbfb89fb8049792482a9 │ │ │ ├── 39c9ae18a99bb14ac71c8f5113b02ab12ff236ac │ │ │ ├── 3a4eed561f8fc8abaf6593a20b4edac297cc5e15 │ │ │ ├── 3aa943b6c35628b187f138aba548df6f3be5da12 │ │ │ ├── 3ac768f61dc4ed8422f9e2711bd6a3aadeb38445 │ │ │ ├── 3aef2c0da68c5632f15f479387b81e934198703f │ │ │ ├── 3b1085b86f86d2d88196b9d5262217e756bf728b │ │ │ ├── 3b71bf232208afeffcf15d4ff2cc8730896ea865 │ │ │ ├── 3be5b44a1721c738786cf72588b08d2b9e390075 │ │ │ ├── 3c0a2660b7288f8308256c7e8546818303b71997 │ │ │ ├── 3d5fdc6616ba35966fbcf6838eb05f6de07aade1 │ │ │ ├── 3d8dccaeb75743323a9a529f87a66d0c484c5c2b │ │ │ ├── 3ed68a2ece43b2bd7718aefc0a4fd7db5c5de056 │ │ │ ├── 3f128a449c26d99e65f137b08835072d0f4fa93a │ │ │ ├── 3f879a2ddc7c450bebbbf1d8e18b3d13be9e97fe │ │ │ ├── 3fbd2427a77b6f19ffc4221553f72ed0b9e853ed │ │ │ ├── 4030af13b66be0e6cc87eac9cf6cc0230124b7e6 │ │ │ ├── 403b12429ad7509a098202162534c621ea84e22d │ │ │ ├── 40aeb8233ec6ec0956a073258b493a6f3f109ad0 │ │ │ ├── 412005dc4d8c236fc60dc39355e9e671dbe09d8e │ │ │ ├── 41483173f64fb2b10b0fc410ec367e48af3258e6 │ │ │ ├── 418dccb65acf0f27d9ad4f66844bfb900fd341e5 │ │ │ ├── 41a18a6da2101d4fc073f881774d365cac9df69f │ │ │ ├── 41ff90328e5650588c247848d651f0390237b795 │ │ │ ├── 422a2a4de398a58124633a31c4e52902ce85444a │ │ │ ├── 42c8f8111af667130b9d5672d007bdc125827020 │ │ │ ├── 42e59128df90545517099afd0b604dfa56bc05b5 │ │ │ ├── 43142a391a23602f37adb8e58795e413b2f930f4 │ │ │ ├── 43620674069e40000726f5201bb470a56686c3b2 │ │ │ ├── 4384398a7160f0d94ddf9800823d26654b8ba494 │ │ │ ├── 43e8770e73be69be07a92b1158a81872d064983c │ │ │ ├── 458805c494b025b1429890e1b1f4d21e20c9f24f │ │ │ ├── 46e2ab94f4eda80788a6b78e7d918b91cb0cc5ec │ │ │ ├── 46f15ec0a8f3b58021a71695b9d74bb7320eab7a │ │ │ ├── 47bff9ffccc62e338cd602ebc8a6949328c714c8 │ │ │ ├── 47ed2ecb2adafaaf3e20f6c8244d2e2ea58cf53d │ │ │ ├── 47feacb6748ff05002f098417c384c819a65b97e │ │ │ ├── 482704ffe5691bcddb334b81517b3a05f87fbe5a │ │ │ ├── 484a39ded2bc25ee5377a01c8cda97ddeafc87ef │ │ │ ├── 48526c21334aa86c0df6320d510a1fed8dc09b35 │ │ │ ├── 486a8f55e4b44564c3223c1b38ed06971606e9a3 │ │ │ ├── 49f6c92b4735986f2daa0863462d6add05422e37 │ │ │ ├── 4b72a9874e77ce171cf3f60eff7beede65c5563a │ │ │ ├── 4b7c0cbad40165a88b1da6e4f393cb83be84ae57 │ │ │ ├── 4ba74383259bd616e28171d92c9435f3f09ea019 │ │ │ ├── 4ba75e317ec2959faa02620eb79057f59bfe53f0 │ │ │ ├── 4bacddbb420583304d54e49585c719868272e744 │ │ │ ├── 4c5a8c6a90fce1e1b56ee77115a5259354f7df5c │ │ │ ├── 4c6b39b374d9de57f377c0a756df6cf2a1674773 │ │ │ ├── 4c7d9b1e8380cbc69d7f0bbe49d50c5c669a48b5 │ │ │ ├── 4c883bcf149e3ab24ed690c491d8c5f4760101c2 │ │ │ ├── 4cecf81e69c4eb64195f388d82fbbcc0b9f713f8 │ │ │ ├── 4d32ab98d9d1b052654c6c2b105449dfd058f91c │ │ │ ├── 4dc7cba8ab34945620d27a03d92294d46a68036e │ │ │ ├── 4e324a84b2aefab1344cbb5f18b0a4a8d0d03019 │ │ │ ├── 4e6377c22a1909fc40f3ea947039cec500b3296d │ │ │ ├── 4ea6b7ccec37ce161208bc3c99206d9fd1060daf │ │ │ ├── 4f3097e7856d45dfc5eeb4c0c9477bdb04579a40 │ │ │ ├── 4f68aae1dca08eb2eb96530414b3078ec89483f2 │ │ │ ├── 4fbec82e43616db68d8a12887e4c3db1f6aa9c02 │ │ │ ├── 501f1b1a10740d5e2dcfc32e17409afea7962f78 │ │ │ ├── 5059e627bb1e0fb3e5ddb2d5ca6a596a99f0fc2c │ │ │ ├── 507d4024b8b9a86c644ce870f701410d19023113 │ │ │ ├── 5092f70c135b0380dbe0b55e18c8fce52626a3f7 │ │ │ ├── 51f640c8579d64801a7f7452d90b656abc2859e2 │ │ │ ├── 52d8d1ca1212f03ee060addf4380c352030b2782 │ │ │ ├── 533d06fe6d276f71a662e6ba523af66ec36ab06d │ │ │ ├── 53523b19ecd547e517a668769fa18afcd4405bfd │ │ │ ├── 53a3208136364aa265d253bf1b3e3d0579a6eecd │ │ │ ├── 53ba1ae3c5eee31d03ebd31270c7a01ed598443d │ │ │ ├── 5442a6bf03fea8e6b9618456cd6efedf30020e10 │ │ │ ├── 54540d6a17ccd28d75f912f4ad8ffb37c0eb1680 │ │ │ ├── 561347acc32cf000a7e4f51d531ed8637c913758 │ │ │ ├── 561c786c543a6fa8a1a7fdc928a90ee41b70ca1f │ │ │ ├── 56bd15b8f00812bb08d4c603f503ba7c1b042736 │ │ │ ├── 5709a1d9daa62e4493a7f2cb2bae56cd0ea6d20d │ │ │ ├── 5868c42e7020defa91726147111426f28c4ad601 │ │ │ ├── 595383d60c2dccbb80138f6e74cbc21123513166 │ │ │ ├── 59ba33d9c4f408b15f5d76c77d0e52fde0c8aff4 │ │ │ ├── 59bd17e3d305e245d49cf2b99d571a581e2a9579 │ │ │ ├── 5a19572e3bddde76bf59bac3c0fdbd03f32dc5c1 │ │ │ ├── 5a73876feaa1e31e90034e567d1b4dc0960ba846 │ │ │ ├── 5c24e633494a599e4aa0289ec722ad493de7eb13 │ │ │ ├── 5c352187269ff84470f2849818ec955a3f59910f │ │ │ ├── 5ca6f35474513dd58a090036631eb25b957418c1 │ │ │ ├── 5ce0499d80c7189d349f18e7765892cc2dcf8ace │ │ │ ├── 5d0bde7baad969eafc39495e67f39a8fb3e6dd8b │ │ │ ├── 5d3c510266c1c9f2ecb98a00acd9e7217b9d93fa │ │ │ ├── 5d813d4a0ae116b287ef263033018205895f24c5 │ │ │ ├── 5de18561c4791d508bbf0b45805a55ca79fbeb7b │ │ │ ├── 607e885f7fa26b683aa0344525798e08b15aa872 │ │ │ ├── 61414938295dca0deb7506125c48a85604f107c7 │ │ │ ├── 614ee8b49211c0b8a3c88990c543f091f8fb408c │ │ │ ├── 61661c063e9ac7c1dcd449d0e0d724bf170d4fdb │ │ │ ├── 616b3ef4f8bb602faf5121b9b7e422244ac7f6e9 │ │ │ ├── 61fde34cab597f13e4cf1482625f83a4234720a2 │ │ │ ├── 626ceef156893d7b4bc6fffd1a6934d9668e7f3d │ │ │ ├── 629c0641de83ea88b3d5485b3080ad2e0c9b7cb4 │ │ │ ├── 62a2efa3f7538a5f612c13067692e87b0c6f6bc0 │ │ │ ├── 63424cf928c03d1635a685f728a7223705ac063c │ │ │ ├── 641a81137df780f10842b14c6b8ad0213ca173b6 │ │ │ ├── 6485c5ea97a83c96121a5f34199c551ecb7cccdd │ │ │ ├── 6546f482ba1e853e5fbd2314d3d49e6b49ee9d9b │ │ │ ├── 65aa83012a63f2d24042b716ab92a782a6db002c │ │ │ ├── 66a9200e2a8cba2bba66cd672f2fd63bf5bbaac5 │ │ │ ├── 671f30b13fd9d75b380b1e9dd3a442853fa25747 │ │ │ ├── 675bdd56c7ed46abb4a9bb858ba9a9bf4ec5605d │ │ │ ├── 67c49783d3f8db96c294736133a27a9fa237274e │ │ │ ├── 688dd153a4d138d983be5cc34ba6652ef8541fb7 │ │ │ ├── 690bf49502dbf01976bc66ca2c3ac06c97642db2 │ │ │ ├── 692bb335794be9a7b844feb443757fbb179d85b0 │ │ │ ├── 6956d350ad4779b9744928e979b9b87ac7b2d799 │ │ │ ├── 69760227f602e7a7523a5e0297bd205fa37e9e02 │ │ │ ├── 6a00fb86f8727e0a4708359954fa811737e83d8f │ │ │ ├── 6b4768a78f5353cb4dd4d30aee22449460892e5b │ │ │ ├── 6d08d22c417beb30e21ce3e0f1e1ed97cc130330 │ │ │ ├── 6dfbc2d1a3ac19c3ae9e77bc510d0f639b974f10 │ │ │ ├── 70ecda93edeab49006cc4f58324a8384c2009a19 │ │ │ ├── 7200f4fc3115ac14a4ba74243e8b8d72bdb14104 │ │ │ ├── 72449ed710356adfffc12ec768423cb0784a9b2b │ │ │ ├── 72de9fee019f22dc1492a2c8b83ad3879a74a388 │ │ │ ├── 72eb7a6416e719d3ec3d9ffade194370c8f2802e │ │ │ ├── 7312fa4feada84b033ea67cd4371956466e6aeb0 │ │ │ ├── 732d0c57994b9ce22f94966ebab4a5e40c4bec2a │ │ │ ├── 7368f496a342780f4bd2d5492aeff9bd64ac7da4 │ │ │ ├── 74b426917d84eac125ffc0699c11ade39fe28b01 │ │ │ ├── 74ed87a7698e8be34163a772b40065f65eeb66b3 │ │ │ ├── 750964224488b2703992ceb45ce5eac74a904c98 │ │ │ ├── 7526dbb8bd908dbb53d6caf0f1dc652c0c5d62f8 │ │ │ ├── 758cfe7990237eeaaf3c0cdeca65a888b98db0e8 │ │ │ ├── 76fd1253a98007fce566d49869a659409f895b1a │ │ │ ├── 779dd6f1a4b6c6bb052d3b2b8e4e061d56c7b493 │ │ │ ├── 77f353c7e4d8862e77f0d385751c4953bb850562 │ │ │ ├── 77f6b1e3b42ca7acbb891938ec281d9f5bf18ac5 │ │ │ ├── 7941c33ccfdd7084a6ec19a807302319ee6249c9 │ │ │ ├── 79ec607348296778e4ccf0890e8d5704e6226776 │ │ │ ├── 7a26e6bcc857b6e9b797b3c918f2bcb1a569ddde │ │ │ ├── 7ad4070a7a504f1b7045720b5c9333b20be5937d │ │ │ ├── 7b0758cf04d7fcbc1a504005eeb64921b132e5b4 │ │ │ ├── 7bbe3489b3fff6f2455d83beaefdd468b2bec93f │ │ │ ├── 7bc1889b0d60824bf64af54a98efd9b4a331332a │ │ │ ├── 7d51b95219a1ff6a9f6d15fbcfff36da556c4f84 │ │ │ ├── 7d59f86c9b72019b592378b0eb7c5f482333854d │ │ │ ├── 7d9a836850cfaf6af1ae3b19b996f6735e5ccb4d │ │ │ ├── 7e0f5d7b3d4448157b7df1c2aea9bea6462e498e │ │ │ ├── 7e48a254a435955e508905e9c94311f17b0ec2ad │ │ │ ├── 7e82c0cc2c6fe005354e6ae51ff7825c430fbdad │ │ │ ├── 7ed77cc3b9b1e08614f0d5dfe79433605267052d │ │ │ ├── 7f684c29d88eeed2fb0adea80948906e32e04e73 │ │ │ ├── 7fb2ef54332fca52e531ccaf4d3d4a8a9d664634 │ │ │ ├── 80a561bd5e7f1526af6b533a9818c2161be7889d │ │ │ ├── 811dedfa7c1108081bd7d76e57f2b15c1967985a │ │ │ ├── 815c5b9d6c2fe40cbff3496cb635466cd630ee1e │ │ │ ├── 81e8bbb34be8af488894aeb191b073796b896ad8 │ │ │ ├── 8211bb03478a5a26c1fb40236780cbee8da45a06 │ │ │ ├── 82aab7e70b7f6f2789562063cfefcbb63ff11aad │ │ │ ├── 82bf34d3e6847764b3bc8f5795cb103e9e7af45e │ │ │ ├── 833ca5a7d4cdf2c1867491c242ece799a01d77a9 │ │ │ ├── 836c3f22ebc9a6328b9fae025ae061bd8eadff0e │ │ │ ├── 83f64b56aa2296a799cb9b3f636140964678ac5c │ │ │ ├── 8419f207db4c3ee47461b9507daf459861ab2448 │ │ │ ├── 84b9cd50438d17589ace47d7ba49e02cbddb7203 │ │ │ ├── 84c385be14635966668563f2668ed58f3d01b47f │ │ │ ├── 84d7204e8e1e9b2488340c4eb864221bae1abe19 │ │ │ ├── 856d69f865f0b629c075e885c3b70e0607821869 │ │ │ ├── 85de2258f4b1a73711730059071456ee0a908091 │ │ │ ├── 85f376b8fe702b88f8bc1e0bff8c5bd63b72dff2 │ │ │ ├── 876db58923ca6d09c577a5b32a7aae1a0bcac5ce │ │ │ ├── 8854d8b576cfad21365ff8c2c6ea4eaec7477575 │ │ │ ├── 88c2f1d247ab356950a61dfe0c6d3e60a8e82dd7 │ │ │ ├── 88edce9dc25c3c95e6c5639ef10ca1f596da97cd │ │ │ ├── 88f1bc245ed0b7e0b3350ce90f217c14be56f592 │ │ │ ├── 89ba32692ebbe196d87d1243c7c65d16c7ccd8a4 │ │ │ ├── 89efdee452a9967c32b1f1a66c2dc50932822ee1 │ │ │ ├── 8b1c937b1bfc8252815ca8dd71de1c9797a7782b │ │ │ ├── 8b23c7151401e60f284b2e7f30d98f7241dac2b5 │ │ │ ├── 8b582296512c64807da8d482aaea76ce3e95c094 │ │ │ ├── 8b5e592b25775d0891d38b3d1f68667e50909aaf │ │ │ ├── 8c566842c7374f57eb06cc8bb579d6ec03c4c9de │ │ │ ├── 8d13dc4609982f698eb89d42b3f117531131c87c │ │ │ ├── 8e5ec5bcd9657607aa4f9977e1ffeb4a16169058 │ │ │ ├── 8e673f602ba7adb87f0cf80de0277ac80a418fcd │ │ │ ├── 8e831833b922e03cdb368467aa0da57e57ba983e │ │ │ ├── 8e996d6c817a50ee87159a6bbc50e8ef9dbee1ed │ │ │ ├── 8eacd9b35e1dd172432234a2f6cfc100eb92e401 │ │ │ ├── 8ede9b1aca577a3eb712af7831ba58585a3743cf │ │ │ ├── 8feb9bdb14ada4c050ed698502b7773c55c43233 │ │ │ ├── 9009956bde55dd4f411159b2b564e581bdbc8fba │ │ │ ├── 9032dc936e913c82c1457aa0a65dd8249dccf8d2 │ │ │ ├── 907961daa7ea39e027c800fc79de28c9ed1972bb │ │ │ ├── 907e4d130751ca218e030a85587842d435464109 │ │ │ ├── 90acc0983b16b3d3e989febb6aedbf84becc88e1 │ │ │ ├── 91780e7449df96b0a8c1e841bffd8ad68200093f │ │ │ ├── 91eade2ab334d2ff2feaeee72cb91a3ea3e61150 │ │ │ ├── 92d0a38c7f27e5024f69b9cdabea3a82665f2c75 │ │ │ ├── 94f5a62a7150c24701f9b0ccc4f003afaf33b04b │ │ │ ├── 9527d603aaf7a1bc7d30c583e644364d5ecce4c0 │ │ │ ├── 959e112a9a2a7dd9600c675f243c1d9961bd8bd2 │ │ │ ├── 96721dcb6003dd52b839a1b990fc64f3dedc0aa3 │ │ │ ├── 967f817cc4c091a594876af709be397c0c67b61f │ │ │ ├── 97582ce806d7eff69bb2aaf452ac03611982309d │ │ │ ├── 9782633b6092fef93b5b0438e850ef55117ada41 │ │ │ ├── 985e39e88fcc468ef153db4bf1cfe430ed2fe3c8 │ │ │ ├── 998fb2fcd1b17d5a1fde317f544cca2c1175e7e8 │ │ │ ├── 9a373d015af44d3727dadea7dc66cffe20013ab4 │ │ │ ├── 9ae8f230b3161d008f96b69e521b766c5f31a8aa │ │ │ ├── 9b32ea34edd357a945115746124fd370ac2a7719 │ │ │ ├── 9b4258f12405878745b251d9cb8a72954222b4c6 │ │ │ ├── 9b44e1c23dd9ff05b2a13a5d979f8141b3f46ecc │ │ │ ├── 9b5d8ca6dd784aaeebb34bbca352245c2b691f29 │ │ │ ├── 9baff1a33904e8dd23ead518097a82095c76ff1f │ │ │ ├── 9c10bd9eb26f5e7910ad3ce57c346d5003d1ab00 │ │ │ ├── 9c681f537d7d7f8d8ee53ae89389082b9090d151 │ │ │ ├── 9c8a0b97d4733a8d2301d696f65096645c346823 │ │ │ ├── 9ca499e0eb839fb1724aae11fbf040292ec79847 │ │ │ ├── 9ce531462dff4cb213913d30663a7e5126fa2a7a │ │ │ ├── 9d3d7288dbee735f16fe063ab8a351f727b8b0f5 │ │ │ ├── 9d6b3607c50b95f8b00645b444721235fe1bdc8c │ │ │ ├── 9d972c9189601f118e9a74faaf4bfa6cc831283d │ │ │ ├── 9ddf6a1f23a4f818bcdca5ee8733b7e46a591230 │ │ │ ├── 9e27ad6246c93b1eca00eae989d196db40f81838 │ │ │ ├── 9fe18f241aa92c422b8414c18cb789d5239cea80 │ │ │ ├── a0221ddba1edef902d01f310da19a9bde15268b0 │ │ │ ├── a0526b418179cfe6bf5a67cc3cca2ea452f2295c │ │ │ ├── a101443f19af96e9408021094d007f3da407fb7f │ │ │ ├── a170a142e71402f9c4159b716bfee0a947f92647 │ │ │ ├── a185d78519f7b345e20c17162a0e533fbccc744b │ │ │ ├── a21b00a226ce8a5def77824f2b176d23209d92a1 │ │ │ ├── a26f2182b059c6783f8825b7804ef8bee75f7795 │ │ │ ├── a2f6690f1c5c1a6dd4a1642083360037f3a31b6d │ │ │ ├── a41b2a6cc5e3e2ec3d1d3e7b95956288eca0093a │ │ │ ├── a4cc508b8a7196999829076fc5b5f0b516581b30 │ │ │ ├── a55127b6c6399c91a0086a0b6c95d9cca1cdc547 │ │ │ ├── a65eafc04f13942ed24a4a4db5f8b159f8d561d0 │ │ │ ├── a6832e8acfd7eb836f7baee4c72f0b68200d1c89 │ │ │ ├── a7fdfaf6e55703564ec08e608e97d6fd71cad605 │ │ │ ├── a877d5fdd6b598d7f288ffdb2b837a873a2734fc │ │ │ ├── a96f49db5ca643f12b1477d37b914085fdd1d49e │ │ │ ├── aa0e9c9f08fee4ac97e31916ba820538a423180c │ │ │ ├── aa37a4c4e0ed4ce24d2729a3c4c007785a750e37 │ │ │ ├── aa9bf79728ec5ddac4176b64435d4899744c59cf │ │ │ ├── ac7b7ef4c81569632e8c7dd0629520007a6df557 │ │ │ ├── aca10067e6738a677ea8f18c501d1078bc89586b │ │ │ ├── accd89deb594675ae017c252a9c768cc6466c2a0 │ │ │ ├── ad1a7e9e9d45443409b8086443efcfcfce9e8db8 │ │ │ ├── add95dbca92f87b7e8c6185078d0176d0ea9cfef │ │ │ ├── add95f1abc0265c4f0f7a90f65cd54fa20d6cc75 │ │ │ ├── adfcb40fe2cb146d47939aa206189dd67449f25a │ │ │ ├── ae97befcdda5c7e3ec2549b2d763cdc9ca29f99b │ │ │ ├── ae9a8e1a810fb4cb143714cfc4b2e0087606e4a5 │ │ │ ├── aeb0ec605d994579b0690f58991d9cae133683c5 │ │ │ ├── af0829e7998f9921e13f3a8bb6c082b86761a5ec │ │ │ ├── af595b538f4790dfd61af9ca81ae78d4f23810d3 │ │ │ ├── afab519f1288df4fbc4bc2e290dced8c068c18b7 │ │ │ ├── afddf87d6a80ddb08380a6444f8e1238d30795bf │ │ │ ├── b0302b5a33196e1b0243c262dae25d479268bcad │ │ │ ├── b03c6997dbec0d439daee909ac46e0fce254c0d3 │ │ │ ├── b066275060e00b49f6fae6c4f16dc2b97922cced │ │ │ ├── b0b3a91ca74b193a625677e3e9f2a77e6a38ff1a │ │ │ ├── b0cc77ab0be01ab8db765ab96f57ad636c3ca811 │ │ │ ├── b1131ea73d0d3fa31367834fe1f6abfa1fc3b8b7 │ │ │ ├── b1957a8f39f79bd85a1f49e12240c051af821bd1 │ │ │ ├── b27345719ecf784b471e96fdc2407baaac45ecd0 │ │ │ ├── b2ebf519d5c9625461cd1116d458d4ece646b167 │ │ │ ├── b36916b71545637ee40369cd28ab5508a55e7900 │ │ │ ├── b5058de7178ce0a5fdf7199d821b9c2153385a83 │ │ │ ├── b51d6507a607c776f2525a83ce9fc16a4e91ad64 │ │ │ ├── b5f2d0d0aa509acc428a6110c83216d30efd59f7 │ │ │ ├── b621159ac023b743a58712fef32d6a6a63b0ff44 │ │ │ ├── b6ffbad17fd1e1693df744ed310863c644926b1b │ │ │ ├── b78c1aca8f8ba9bd6c6d5f2aad243acf382ea3fb │ │ │ ├── b84f0f25b9e9d7da2165cd73bccce8094e48de68 │ │ │ ├── b8b4bbd09bd1f7f435c3678c727dc785f76e597d │ │ │ ├── b8e569612b2eda760c95d81965d2b609be3b11d2 │ │ │ ├── b9b2b0532a7b0c3bbc74ce8ef5e3f33e3dc232b0 │ │ │ ├── b9fa30f8a2df145bc02e6045f39ad0bca8c5514f │ │ │ ├── ba501c2a86e38e6962abeed9f2f8d7d983dcc0d1 │ │ │ ├── ba58b8743d635ffe42efe8d626e7cad42acd6fbf │ │ │ ├── ba5a33db44c01ee63a272aec46411aa72c700d1a │ │ │ ├── bb1e5e53f6a1253fe1e896b50e9edc0095e2932b │ │ │ ├── bb6d44f31206299e9926504ac121f705cd5a16f1 │ │ │ ├── bb90e14d44d5c579915d23bb2c26017c3407642f │ │ │ ├── bbbc47339528b5e201a78d8e6db4826b28b4a157 │ │ │ ├── bcaf3f2740e6453d17271574c692896ede4a2198 │ │ │ ├── bef45de8f4511cad456c7ee0b22feeca70ada282 │ │ │ ├── bf11a1e48b92f1c2ebeb3d5c81929dbefa5172b0 │ │ │ ├── bf31348d41bb4ea93fb6fe1ff50258347e197b90 │ │ │ ├── bf52817452d24e8767610ea3a1ba253c67bab007 │ │ │ ├── c025ccecaf1873a6ebf81258e2375ba1d3498aed │ │ │ ├── c09cea010672cd2dfba9d2da4c518b3588e702c7 │ │ │ ├── c0a7e62cd3b3a25048ecee5ae6175b42a128cf3e │ │ │ ├── c0c075e14b3d17bad67086c710a31b93e35e4655 │ │ │ ├── c0e773cc49a2c9436794db319e74958c9080bbc7 │ │ │ ├── c16cfc2a45f2e4ddd9be1f3c6025c8df747b5133 │ │ │ ├── c200c38be10536f8732a24d2323eb18ea3ee7a90 │ │ │ ├── c24e7aa1f8f36256c213ae5da9bf51f99e805c4f │ │ │ ├── c25a18dcfe09361f8a829aaa7155d0174dd78d0d │ │ │ ├── c3450080dbe8542a29419c1f67055f4dfc6a600d │ │ │ ├── c384e65002f5917964f75a5f913d6064cdbef7f1 │ │ │ ├── c420dd8ea1e78ce9e8cb09b86c9283a6db29afcb │ │ │ ├── c48ae389c3c820042fa705393cb4baacca494758 │ │ │ ├── c4acddb4fdfa24517561ac3aabf1aab680028855 │ │ │ ├── c4c7a64439fedafdc8708a63e0c06250c02d520c │ │ │ ├── c4df7e6878cf8aba570efc0507641e53d06c7db9 │ │ │ ├── c4f18a576d5f668451405294b4aaa1273425edf3 │ │ │ ├── c58c3872778d1a08a51e05d1fe6ed676221fee94 │ │ │ ├── c641388c0a72cf47ac0aea29cddb87107e63a45c │ │ │ ├── c7595352bd33a8183fa094e449ee60a0b78fad92 │ │ │ ├── c7960b8d2b271bb156f71f5e572c2ad7aa828b77 │ │ │ ├── c7fb3f5be75a6dacfc3e28abbf73fe5e58495d30 │ │ │ ├── c7fca825358e94bf235f99e313f676eedc5aa242 │ │ │ ├── c82df0f0cf67c0a5b79af00766a4493f412810c3 │ │ │ ├── c9f60043e6511fd9b7e45a2e3fd6576a05abdc8b │ │ │ ├── ca891cf7d3741e9e30407033e82eb6b9cc9b93e5 │ │ │ ├── cbf2f00a2a700797b4ec107a68f51087a0ad94cc │ │ │ ├── cc45dd95f397e105b19acd88fa3b294c17e571f3 │ │ │ ├── cc6cdda12c94aa36000e47fe0231caa68107e876 │ │ │ ├── ccc7a43c1113fe7bc104d75d0a94eca87869b49b │ │ │ ├── cda176363b77958bda3f43de9cd5c247436c58d4 │ │ │ ├── ce1762b8776a54bb8fc8590be155ea3a5dba7038 │ │ │ ├── ce8feeabf17f91818086e472c931a0176d6fadb1 │ │ │ ├── cfa4e12263404b188a58a4ca10b57e1ec5b3f7f4 │ │ │ ├── cffd91a961e766a4303b0e11457acf5610b6dd64 │ │ │ ├── d0b1ab263d05c2e139c0b5f40ee12e77f28c372c │ │ │ ├── d0f4f84cc6ec3fb4fe8e1cd6cdbdf54cc862c3e8 │ │ │ ├── d2c4ebf61640f5733dd008d588ed94d6718f7614 │ │ │ ├── d2f36719ac08f67431ffcad1598700107017478a │ │ │ ├── d403016349c3b426965f8a8e647b4a2f068a29a6 │ │ │ ├── d488eca7d059c07b0f9e8ad7d7206ace887cda85 │ │ │ ├── d4c8e6060d19582883599d5f796dd1ac445b3109 │ │ │ ├── d550d5d74863eabcf0ecebf42a962dcfd376a73f │ │ │ ├── d632e7bdcf37908f2e88a8aaba516b55d592ed5d │ │ │ ├── d65c098bcb3f8a4fe446654bb3a937f0cf26c427 │ │ │ ├── d6993e611b08fb0ba0144fd4c5016926da42dcd3 │ │ │ ├── d6c0781dc6507fef639443e9caa84b7dce3d38cd │ │ │ ├── d6c7b8e5a1a856bd79e3f5915647ce9b18f587f8 │ │ │ ├── d71d06eb3831fc90baf63e860f4d9683552c73c3 │ │ │ ├── d7527ee455860fe79eb17904d9ee4eb35e6222ac │ │ │ ├── d785fa12d464d26d3653d2bcd14c1bf4a508747d │ │ │ ├── d833b2fb6fed227da6867135f639cce035e530af │ │ │ ├── d8faba61555afbe6b773515e3862230526f142a8 │ │ │ ├── d99b219117c5166c5f3e573c8a4809be7bd61182 │ │ │ ├── d9b1db92a1ff835eb7c0dfb5ad9188709cb820bb │ │ │ ├── da2abecc3157fd111e136d55ba8a4d62d3fc7dae │ │ │ ├── da32bcd3ec4fe8e13172bd1ede208231ff7bd134 │ │ │ ├── da8e60a5bc2ab260d0c5aa7c9e1d25ae40901e61 │ │ │ ├── db65b0be46a701febee8d76667b40b2fcb952d24 │ │ │ ├── dca333d112ca96e7dadd659dfad7af72ee5a1abf │ │ │ ├── dcb19d594db74197bd716af457fed3b890a035f4 │ │ │ ├── dcb58eff559d95540887ed0bdbef74bf9dd26c2a │ │ │ ├── de3506c7a9b702d6be207504d6be31f61fd5eb10 │ │ │ ├── ded9c9ecae35be064e52e1b96c9af61af2b9619b │ │ │ ├── e05d2a4fb180767b93540103879b4c946753e471 │ │ │ ├── e1c7586556577c4fb5f90a194de15d756c17953b │ │ │ ├── e2d0040a836f7e414ef68a89d7f186f65ad5f4f2 │ │ │ ├── e34ec5637d29ed545137f9d737699ca99fe1ae86 │ │ │ ├── e4162076f43c93505114215c41728ad0acb650b6 │ │ │ ├── e42913c21e960d31fc3a47f82db34c1b58414777 │ │ │ ├── e43d140b963f53cc754dcfe916937e99006b8dfe │ │ │ ├── e44fc9d05badf1e2be145604fb80a7fb60c1793c │ │ │ ├── e4d3d4abd13fac7cae3a475d77bd6bb0728058e7 │ │ │ ├── e61f840bde7e9408b02934332a0d3d5bcd5da413 │ │ │ ├── e7509dc7b9438e814836cbf4c8ebdd39f9c9f0a0 │ │ │ ├── e7ba552c9d44a14b681c264909a520b07f3348f2 │ │ │ ├── e8e8e3e63bf2ce3b11ac9dd7db088c29b4006a4d │ │ │ ├── ea58540da7e98da87b5cb9daa84163649793cac7 │ │ │ ├── eade348fbd2b42ef28a63152216754bece3a59ef │ │ │ ├── ebc30b6a41e3bda49d3d7e5393ac71e56470970e │ │ │ ├── ecf1ddd3f6904b170509c9455048ec24e921d8c5 │ │ │ ├── ed4b989c9cec98d08f0760d75620e6b5023cc514 │ │ │ ├── ed93b3caa649c0dc8d2e7bc2fa885ecd7ad0ba36 │ │ │ ├── ee0687f930c6047ab6607baa2430126d1e3a50a1 │ │ │ ├── ee42d3e6ce9f812b9290ec7392da2e7b56121651 │ │ │ ├── ee7fb9a74cb9e6d6e0fe019c232a4368810a6c72 │ │ │ ├── ef1895642eede0d6a1f8085c4b5a76aafc01b94b │ │ │ ├── ef5efa80b2aa8d65ec2e932f5f02e34d52af9761 │ │ │ ├── ef6c9ecc69f26172f0c996a73de38e32a1364bf6 │ │ │ ├── ef6d2f5ba7147b5e244c00cbeff1ac97c5833032 │ │ │ ├── efb89d9efbf0dfe207c9378a816246007a4216da │ │ │ ├── f00869232e4933bb896be82663d156f1b06ec21a │ │ │ ├── f07b6399b6bb76d4146fa21779444401ae1e2531 │ │ │ ├── f1691601665c586b5927ae2f699c7269b05cb409 │ │ │ ├── f18fbc6522dd0309a38a25e17932cf8fbb8fccf0 │ │ │ ├── f1cfbb50a822b085c5e35f30dfb20df9938272c5 │ │ │ ├── f21398f94f3153f5f7adc5ef8423708a4397b255 │ │ │ ├── f246c9dce3d13873c1a28ba9a67dfd3713f16271 │ │ │ ├── f2a7b10c4fe12c03a372e79b1a714b50ba9799f0 │ │ │ ├── f2b750a07d12bbc16741d1bf67a80096c3ef62e9 │ │ │ ├── f2c3126fa98e73186491bb8a4f7e6cee7a9ba22b │ │ │ ├── f2d7ad91a0029a5742205340845a66ae2ce59c7e │ │ │ ├── f35d603a125528b2137d2db6c2bbcc202b9677ee │ │ │ ├── f3c3904ba6d6a4151bb140d2a9080bb6a56176c0 │ │ │ ├── f471856fca60088e401c0c52a81e4557a2218ae3 │ │ │ ├── f47738f0f89b10fa5eaf79af2974a39c8a539de1 │ │ │ ├── f53e9d74f34d349f856d8aba3f40712bcfe8d916 │ │ │ ├── f62ccac5e6a5c2ba2c57433167d181a75c415b22 │ │ │ ├── f639ad42343ae1c1c8b7eb4bbc20eb5c06eb267b │ │ │ ├── f6dcc0c83b9a564cf616334fe654a54e8ce44da5 │ │ │ ├── f70e9a5aff0fb315b0ecccb7e7aaf5b4c1ac255c │ │ │ ├── f7737a2ecd68d0a240674c170aa7e0a7543769c1 │ │ │ ├── f8407e180bd92589b728af21c5626c18770cf26b │ │ │ ├── f8d3a7cd8b910fbcf0faeaa731caf4de7f77570c │ │ │ ├── f93636cd851146007f9ec6390b7d6bbf5b20074d │ │ │ ├── fa2fb749076643c7f52d82caa20f50474604d97b │ │ │ ├── fbf2f40e82503a202d4a684c1c12f5ec8d3acec3 │ │ │ ├── fc3a8cca646b7a6063440ad78fc250d753f6d161 │ │ │ ├── fc5feac0c3827bbbfb189f8f9df289c6fe2edb24 │ │ │ ├── fcb356405f3007a27c718552ec3906edf04b29d7 │ │ │ ├── fce1511b680fcca4dcb2e4f4144b7deef23dd2ef │ │ │ ├── fd5aca5170e759a76da21b51a2b9b2af1a3003f3 │ │ │ ├── fd6bce94e488102a4091e6508ec9fa84da4502f7 │ │ │ ├── fe4e01160902eae24481da9bc592d26f2f853f7d │ │ │ ├── fe98b143c6cda5231f43560850af104e987d3091 │ │ │ ├── fe9a584f5c7c5d9eb5e4b15f85960ca7f1079a2e │ │ │ ├── fea1c884158ee61dd23849f8840219b81f22b754 │ │ │ ├── feb335288d8bd4156d6b6d5dfc578a4a0810d288 │ │ │ ├── feda8e894af74bbb7c2ac8b3101b1448e715a3a1 │ │ │ └── ff794d3672b7ed32edf8e6a556e2451ca5b3ca7b │ │ ├── handle.h │ │ ├── handle_adapt.cpp │ │ ├── handle_alias.cpp │ │ ├── handle_array_expr.cpp │ │ ├── handle_base.cpp │ │ ├── handle_binding_pattern.cpp │ │ ├── handle_brace_expr.cpp │ │ ├── handle_choice.cpp │ │ ├── handle_code_block.cpp │ │ ├── handle_decl_definition.cpp │ │ ├── handle_decl_name_and_params.cpp │ │ ├── handle_decl_scope_loop.cpp │ │ ├── handle_expr.cpp │ │ ├── handle_form_literal.cpp │ │ ├── handle_function.cpp │ │ ├── handle_if_expr.cpp │ │ ├── handle_impl.cpp │ │ ├── handle_import_and_package.cpp │ │ ├── handle_index_expr.cpp │ │ ├── handle_lambda.cpp │ │ ├── handle_let.cpp │ │ ├── handle_match.cpp │ │ ├── handle_namespace.cpp │ │ ├── handle_observe.cpp │ │ ├── handle_paren_condition.cpp │ │ ├── handle_paren_expr.cpp │ │ ├── handle_pattern.cpp │ │ ├── handle_pattern_list.cpp │ │ ├── handle_period.cpp │ │ ├── handle_require.cpp │ │ ├── handle_requirement.cpp │ │ ├── handle_statement.cpp │ │ ├── handle_type.cpp │ │ ├── handle_unused.cpp │ │ ├── handle_var.cpp │ │ ├── node_category.cpp │ │ ├── node_category.h │ │ ├── node_ids.h │ │ ├── node_kind.cpp │ │ ├── node_kind.def │ │ ├── node_kind.h │ │ ├── parse.cpp │ │ ├── parse.h │ │ ├── parse_fuzzer.cpp │ │ ├── precedence.cpp │ │ ├── precedence.h │ │ ├── precedence_test.cpp │ │ ├── state.cpp │ │ ├── state.def │ │ ├── state.h │ │ ├── testdata/ │ │ │ ├── alias/ │ │ │ │ ├── basic.carbon │ │ │ │ └── fail_syntax.carbon │ │ │ ├── array/ │ │ │ │ ├── basic.carbon │ │ │ │ └── fail_syntax.carbon │ │ │ ├── auto/ │ │ │ │ ├── let.carbon │ │ │ │ ├── match_case.carbon │ │ │ │ ├── return.carbon │ │ │ │ └── var.carbon │ │ │ ├── basics/ │ │ │ │ ├── empty.carbon │ │ │ │ ├── empty_decl.carbon │ │ │ │ ├── fail_bracket_recovery.carbon │ │ │ │ ├── fail_invalid_designators.carbon │ │ │ │ ├── fail_modifiers_before_semi.carbon │ │ │ │ ├── fail_no_intro_with_semi.carbon │ │ │ │ ├── fail_no_intro_without_semi.carbon │ │ │ │ ├── fail_paren_match_regression.carbon │ │ │ │ ├── form_literals.carbon │ │ │ │ ├── function_call.carbon │ │ │ │ ├── multifile.carbon │ │ │ │ ├── multiline_token.carbon │ │ │ │ ├── parens.carbon │ │ │ │ ├── type_literals.carbon │ │ │ │ └── value_literals.carbon │ │ │ ├── choice/ │ │ │ │ ├── basic.carbon │ │ │ │ ├── fail_alternative_with_implicit_params.carbon │ │ │ │ ├── fail_invalid_alternative_identifier.carbon │ │ │ │ ├── fail_invalid_braces.carbon │ │ │ │ ├── fail_missing_definition.carbon │ │ │ │ ├── fail_missing_definition_parameterized.carbon │ │ │ │ ├── fail_qualified_alternative_name.carbon │ │ │ │ ├── local.carbon │ │ │ │ ├── modifiers.carbon │ │ │ │ └── parameterized.carbon │ │ │ ├── class/ │ │ │ │ ├── adapt.carbon │ │ │ │ ├── base.carbon │ │ │ │ ├── base_misplaced.carbon │ │ │ │ ├── basic.carbon │ │ │ │ ├── fail_base.carbon │ │ │ │ ├── fail_modifiers.carbon │ │ │ │ ├── fail_var_name.carbon │ │ │ │ ├── fn_definitions.carbon │ │ │ │ ├── introducer.carbon │ │ │ │ ├── local.carbon │ │ │ │ ├── mismatched_introducer.carbon │ │ │ │ └── var.carbon │ │ │ ├── for/ │ │ │ │ ├── fail_colon_instead_of_in.carbon │ │ │ │ ├── fail_missing_cond.carbon │ │ │ │ ├── fail_missing_in.carbon │ │ │ │ ├── fail_returned_var.carbon │ │ │ │ ├── fail_square_brackets.carbon │ │ │ │ ├── nested.carbon │ │ │ │ └── pattern.carbon │ │ │ ├── function/ │ │ │ │ ├── call.carbon │ │ │ │ ├── decl_statement.carbon │ │ │ │ ├── declaration.carbon │ │ │ │ ├── definition.carbon │ │ │ │ ├── eval_musteval.carbon │ │ │ │ ├── extern.carbon │ │ │ │ └── terse.carbon │ │ │ ├── generics/ │ │ │ │ ├── deduced_params/ │ │ │ │ │ ├── empty.carbon │ │ │ │ │ ├── no_parens.carbon │ │ │ │ │ ├── one.carbon │ │ │ │ │ ├── one_suffix_comma.carbon │ │ │ │ │ ├── six.carbon │ │ │ │ │ ├── two.carbon │ │ │ │ │ └── two_suffix_comma.carbon │ │ │ │ ├── generic_params/ │ │ │ │ │ ├── basic.carbon │ │ │ │ │ ├── fail_template_no_param.carbon │ │ │ │ │ └── template.carbon │ │ │ │ ├── impl/ │ │ │ │ │ ├── basic.carbon │ │ │ │ │ ├── class.carbon │ │ │ │ │ ├── declaration.carbon │ │ │ │ │ ├── empty_body.carbon │ │ │ │ │ ├── fail_impl.carbon │ │ │ │ │ ├── fail_out_of_line_member.carbon │ │ │ │ │ └── forall.carbon │ │ │ │ ├── interface/ │ │ │ │ │ ├── associated_constants.carbon │ │ │ │ │ ├── basic.carbon │ │ │ │ │ ├── declaration.carbon │ │ │ │ │ ├── default_fn.carbon │ │ │ │ │ ├── empty_body.carbon │ │ │ │ │ ├── fail_associated_constants.carbon │ │ │ │ │ ├── fail_missing_name.carbon │ │ │ │ │ ├── fail_missing_open_curly.carbon │ │ │ │ │ ├── fail_self_param_syntax.carbon │ │ │ │ │ ├── final_fn.carbon │ │ │ │ │ ├── final_member_definition.carbon │ │ │ │ │ ├── non_instance_fn.carbon │ │ │ │ │ ├── require.carbon │ │ │ │ │ └── self_ref.carbon │ │ │ │ ├── named_constraint/ │ │ │ │ │ ├── basic.carbon │ │ │ │ │ ├── fail_incomplete.carbon │ │ │ │ │ ├── invalid_method.carbon │ │ │ │ │ └── template_constraint.carbon │ │ │ │ └── params/ │ │ │ │ ├── empty.carbon │ │ │ │ ├── name_qualifier.carbon │ │ │ │ ├── one.carbon │ │ │ │ ├── one_suffix_comma.carbon │ │ │ │ ├── six.carbon │ │ │ │ ├── two.carbon │ │ │ │ └── two_suffix_comma.carbon │ │ │ ├── if/ │ │ │ │ ├── basic.carbon │ │ │ │ ├── else.carbon │ │ │ │ ├── fail_else_unbraced.carbon │ │ │ │ ├── fail_errors.carbon │ │ │ │ ├── fail_missing_cond.carbon │ │ │ │ ├── fail_square_brackets.carbon │ │ │ │ └── fail_unbraced.carbon │ │ │ ├── if_expr/ │ │ │ │ ├── basic.carbon │ │ │ │ ├── fail_condition_missing.carbon │ │ │ │ ├── fail_else_expr_missing.carbon │ │ │ │ ├── fail_else_missing.carbon │ │ │ │ ├── fail_then_expr_missing.carbon │ │ │ │ ├── fail_then_missing.carbon │ │ │ │ ├── fail_top_level_if.carbon │ │ │ │ ├── in_type.carbon │ │ │ │ └── precedence.carbon │ │ │ ├── index/ │ │ │ │ ├── assign_to_var.carbon │ │ │ │ ├── fail_empty_expr.carbon │ │ │ │ └── fail_malformed_expr.carbon │ │ │ ├── lambda/ │ │ │ │ └── lambda.carbon │ │ │ ├── let/ │ │ │ │ ├── fail_bad_name.carbon │ │ │ │ ├── fail_empty.carbon │ │ │ │ ├── fail_generic_ref.carbon │ │ │ │ ├── fail_missing_name.carbon │ │ │ │ ├── fail_missing_type.carbon │ │ │ │ ├── fail_no_semi.carbon │ │ │ │ ├── let.carbon │ │ │ │ ├── let_ref.carbon │ │ │ │ ├── let_tuple.carbon │ │ │ │ └── missing_value.carbon │ │ │ ├── match/ │ │ │ │ ├── fail_cases_after_default.carbon │ │ │ │ ├── fail_missing_case_arrow.carbon │ │ │ │ ├── fail_missing_case_pattern.carbon │ │ │ │ ├── fail_missing_case_statements_block.carbon │ │ │ │ ├── fail_missing_cases.carbon │ │ │ │ ├── fail_missing_cases_block.carbon │ │ │ │ ├── fail_missing_default_arrow.carbon │ │ │ │ ├── fail_missing_default_statements_block.carbon │ │ │ │ ├── fail_missing_guard_close_paren.carbon │ │ │ │ ├── fail_missing_guard_open_paren.carbon │ │ │ │ ├── fail_missing_guard_parens_only_parse_errors.carbon │ │ │ │ ├── fail_missing_matched_expr.carbon │ │ │ │ ├── fail_unexpected_tokens_in_cases_block.carbon │ │ │ │ └── match.carbon │ │ │ ├── member_access/ │ │ │ │ ├── compound.carbon │ │ │ │ ├── fail_keyword.carbon │ │ │ │ ├── keyword.carbon │ │ │ │ └── simple.carbon │ │ │ ├── namespace/ │ │ │ │ ├── args.carbon │ │ │ │ ├── basic.carbon │ │ │ │ ├── fail_arrow.carbon │ │ │ │ ├── fail_incomplete.carbon │ │ │ │ ├── fail_incomplete_name.carbon │ │ │ │ ├── fail_modifiers.carbon │ │ │ │ ├── fail_no_name.carbon │ │ │ │ ├── local.carbon │ │ │ │ ├── modifiers.carbon │ │ │ │ └── nested.carbon │ │ │ ├── observe/ │ │ │ │ ├── fail_invalid_operator.carbon │ │ │ │ ├── fail_missing_expr.carbon │ │ │ │ └── observe.carbon │ │ │ ├── operators/ │ │ │ │ ├── assign.carbon │ │ │ │ ├── associative.carbon │ │ │ │ ├── fail_chained_assign.carbon │ │ │ │ ├── fail_infix_uneven_space_after.carbon │ │ │ │ ├── fail_infix_uneven_space_before.carbon │ │ │ │ ├── fail_invalid_infix.carbon │ │ │ │ ├── fail_postfix_space.carbon │ │ │ │ ├── fail_postfix_space_before_comma.carbon │ │ │ │ ├── fail_postfix_space_in_call.carbon │ │ │ │ ├── fail_postfix_space_surrounding.carbon │ │ │ │ ├── fail_postincrement.carbon │ │ │ │ ├── fail_precedence_and_or.carbon │ │ │ │ ├── fail_precedence_as.carbon │ │ │ │ ├── fail_precedence_assign.carbon │ │ │ │ ├── fail_precedence_or_and.carbon │ │ │ │ ├── fail_precedence_where.carbon │ │ │ │ ├── fail_prefix_repeat.carbon │ │ │ │ ├── fail_prefix_space.carbon │ │ │ │ ├── fail_prefix_uneven_space_with_assign.carbon │ │ │ │ ├── fail_star_minus.carbon │ │ │ │ ├── fail_star_star.carbon │ │ │ │ ├── fail_star_star_no_space.carbon │ │ │ │ ├── fail_variety.carbon │ │ │ │ ├── fixity_in_call.carbon │ │ │ │ ├── fixity_in_params.carbon │ │ │ │ ├── fixity_in_var.carbon │ │ │ │ ├── fixity_with_assign.carbon │ │ │ │ ├── infix.carbon │ │ │ │ ├── infix_no_space.carbon │ │ │ │ ├── infix_with_paren_after.carbon │ │ │ │ ├── infix_with_paren_before.carbon │ │ │ │ ├── modifier.carbon │ │ │ │ ├── postfix.carbon │ │ │ │ ├── postfix_repeat.carbon │ │ │ │ ├── postfix_space_after_op.carbon │ │ │ │ ├── precedence_as.carbon │ │ │ │ ├── precedence_assign.carbon │ │ │ │ ├── precedence_not.carbon │ │ │ │ ├── precedence_unary.carbon │ │ │ │ ├── precedence_where.carbon │ │ │ │ ├── prefix.carbon │ │ │ │ ├── prefix_no_space.carbon │ │ │ │ ├── prefix_repeat.carbon │ │ │ │ ├── ref.carbon │ │ │ │ ├── spaceship.carbon │ │ │ │ ├── three_stars.carbon │ │ │ │ └── unary.carbon │ │ │ ├── package_expr/ │ │ │ │ ├── basic.carbon │ │ │ │ ├── core.carbon │ │ │ │ ├── fail_in_name.carbon │ │ │ │ └── fail_standalone.carbon │ │ │ ├── packages/ │ │ │ │ ├── export.carbon │ │ │ │ ├── import/ │ │ │ │ │ ├── after_import.carbon │ │ │ │ │ ├── after_package.carbon │ │ │ │ │ ├── basic.carbon │ │ │ │ │ ├── core.carbon │ │ │ │ │ ├── cpp_inline.carbon │ │ │ │ │ ├── current_package_library.carbon │ │ │ │ │ ├── export.carbon │ │ │ │ │ ├── fail_after_decl.carbon │ │ │ │ │ ├── fail_after_decl_repeated.carbon │ │ │ │ │ ├── fail_extra_string.carbon │ │ │ │ │ ├── fail_in_nested_scope.carbon │ │ │ │ │ ├── fail_library_is_identifier.carbon │ │ │ │ │ ├── fail_name_is_keyword.carbon │ │ │ │ │ ├── fail_no_name.carbon │ │ │ │ │ ├── fail_no_semi.carbon │ │ │ │ │ ├── fail_omit_library_keyword.carbon │ │ │ │ │ ├── fail_type.carbon │ │ │ │ │ ├── library.carbon │ │ │ │ │ ├── ordering.carbon │ │ │ │ │ └── semi_before.carbon │ │ │ │ ├── keyword_names.carbon │ │ │ │ ├── library/ │ │ │ │ │ ├── basic.carbon │ │ │ │ │ ├── fail_in_nested_scope.carbon │ │ │ │ │ ├── fail_invalid_name.carbon │ │ │ │ │ ├── fail_semi_before.carbon │ │ │ │ │ └── fail_too_late.carbon │ │ │ │ └── package/ │ │ │ │ ├── api.carbon │ │ │ │ ├── api_library.carbon │ │ │ │ ├── core.carbon │ │ │ │ ├── fail_after_decl.carbon │ │ │ │ ├── fail_after_import.carbon │ │ │ │ ├── fail_after_package.carbon │ │ │ │ ├── fail_extra_string.carbon │ │ │ │ ├── fail_in_nested_scope.carbon │ │ │ │ ├── fail_library_is_identifier.carbon │ │ │ │ ├── fail_library_skips_name.carbon │ │ │ │ ├── fail_name_is_keyword.carbon │ │ │ │ ├── fail_no_name.carbon │ │ │ │ ├── fail_no_semi.carbon │ │ │ │ ├── fail_omit_library_keyword.carbon │ │ │ │ ├── fail_semi_before.carbon │ │ │ │ ├── fail_trailing_impl.carbon │ │ │ │ ├── impl.carbon │ │ │ │ ├── impl_library.carbon │ │ │ │ └── modifiers.carbon │ │ │ ├── pointer/ │ │ │ │ ├── const_pointer.carbon │ │ │ │ ├── fail_pointer_type_in_expr.carbon │ │ │ │ ├── fail_tuple_instead_of_compound_member_access.carbon │ │ │ │ ├── pointer_type.carbon │ │ │ │ └── pointer_value.carbon │ │ │ ├── return/ │ │ │ │ ├── basic.carbon │ │ │ │ ├── expr.carbon │ │ │ │ ├── fail_expr_no_semi.carbon │ │ │ │ ├── fail_no_semi.carbon │ │ │ │ ├── fail_returned_no_var.carbon │ │ │ │ ├── fail_var_no_semi.carbon │ │ │ │ └── returned_var.carbon │ │ │ ├── struct/ │ │ │ │ ├── fail_comma_only.carbon │ │ │ │ ├── fail_comma_repeat_in_type.carbon │ │ │ │ ├── fail_comma_repeat_in_value.carbon │ │ │ │ ├── fail_extra_token_in_type.carbon │ │ │ │ ├── fail_extra_token_in_value.carbon │ │ │ │ ├── fail_identifier_colon.carbon │ │ │ │ ├── fail_identifier_equals.carbon │ │ │ │ ├── fail_identifier_only.carbon │ │ │ │ ├── fail_invalid_struct_designator.carbon │ │ │ │ ├── fail_missing_type.carbon │ │ │ │ ├── fail_missing_value.carbon │ │ │ │ ├── fail_mix_type_and_value.carbon │ │ │ │ ├── fail_mix_value_and_type.carbon │ │ │ │ ├── fail_mix_with_unknown.carbon │ │ │ │ ├── fail_no_colon_or_equals.carbon │ │ │ │ ├── fail_period_only.carbon │ │ │ │ ├── fail_period_paren.carbon │ │ │ │ ├── fail_period_string_colon.carbon │ │ │ │ ├── fail_period_string_equals.carbon │ │ │ │ ├── fail_type_no_designator.carbon │ │ │ │ ├── no_entries.carbon │ │ │ │ ├── one_entry_no_comma.carbon │ │ │ │ ├── one_entry_with_comma.carbon │ │ │ │ └── two_entries.carbon │ │ │ ├── tuple/ │ │ │ │ ├── access/ │ │ │ │ │ ├── repeated.carbon │ │ │ │ │ └── value_access.carbon │ │ │ │ ├── nested.carbon │ │ │ │ └── two_entries.carbon │ │ │ ├── var/ │ │ │ │ ├── fail_bad_name.carbon │ │ │ │ ├── fail_empty.carbon │ │ │ │ ├── fail_in_interface.carbon │ │ │ │ ├── fail_no_semi.carbon │ │ │ │ ├── fail_ref.carbon │ │ │ │ ├── unused.carbon │ │ │ │ ├── var.carbon │ │ │ │ ├── var_pattern.carbon │ │ │ │ └── var_tuple.carbon │ │ │ ├── where_expr/ │ │ │ │ ├── basic.carbon │ │ │ │ ├── designators.carbon │ │ │ │ ├── fail_rewrite.carbon │ │ │ │ ├── impl_where.carbon │ │ │ │ └── where_and.carbon │ │ │ └── while/ │ │ │ ├── basic.carbon │ │ │ ├── fail_missing_cond.carbon │ │ │ ├── fail_no_semi.carbon │ │ │ └── fail_unbraced.carbon │ │ ├── tree.cpp │ │ ├── tree.h │ │ ├── tree_and_subtrees.cpp │ │ ├── tree_and_subtrees.h │ │ ├── tree_test.cpp │ │ ├── typed_nodes.h │ │ └── typed_nodes_test.cpp │ ├── run_tool.bzl │ ├── sem_ir/ │ │ ├── BUILD │ │ ├── absolute_node_id.cpp │ │ ├── absolute_node_id.h │ │ ├── associated_constant.h │ │ ├── builtin_function_kind.cpp │ │ ├── builtin_function_kind.def │ │ ├── builtin_function_kind.h │ │ ├── clang_decl.cpp │ │ ├── clang_decl.h │ │ ├── class.cpp │ │ ├── class.h │ │ ├── constant.cpp │ │ ├── constant.h │ │ ├── copy_on_write_block.h │ │ ├── cpp_file.h │ │ ├── cpp_global_var.h │ │ ├── cpp_initializer_list.cpp │ │ ├── cpp_initializer_list.h │ │ ├── cpp_overload_set.h │ │ ├── diagnostic_loc_converter.cpp │ │ ├── diagnostic_loc_converter.h │ │ ├── dump.cpp │ │ ├── dump.h │ │ ├── entity_name.h │ │ ├── entity_with_params_base.h │ │ ├── entry_point.cpp │ │ ├── entry_point.h │ │ ├── expr_info.cpp │ │ ├── expr_info.h │ │ ├── facet_type_info.cpp │ │ ├── facet_type_info.h │ │ ├── file.cpp │ │ ├── file.h │ │ ├── formatter.cpp │ │ ├── formatter.h │ │ ├── formatter_chunks.cpp │ │ ├── formatter_chunks.h │ │ ├── function.cpp │ │ ├── function.h │ │ ├── generic.cpp │ │ ├── generic.h │ │ ├── id_kind.h │ │ ├── ids.cpp │ │ ├── ids.h │ │ ├── ids_test.cpp │ │ ├── impl.cpp │ │ ├── impl.h │ │ ├── import_cpp.h │ │ ├── import_ir.cpp │ │ ├── import_ir.h │ │ ├── inst.cpp │ │ ├── inst.h │ │ ├── inst_categories.h │ │ ├── inst_fingerprinter.cpp │ │ ├── inst_fingerprinter.h │ │ ├── inst_kind.cpp │ │ ├── inst_kind.def │ │ ├── inst_kind.h │ │ ├── inst_namer.cpp │ │ ├── inst_namer.h │ │ ├── interface.h │ │ ├── name.cpp │ │ ├── name.h │ │ ├── name_scope.cpp │ │ ├── name_scope.h │ │ ├── name_scope_test.cpp │ │ ├── named_constraint.h │ │ ├── pattern.cpp │ │ ├── pattern.h │ │ ├── require_impls.h │ │ ├── singleton_insts.h │ │ ├── specific_interface.h │ │ ├── specific_named_constraint.h │ │ ├── stringify.cpp │ │ ├── stringify.h │ │ ├── struct_type_field.h │ │ ├── type.cpp │ │ ├── type.h │ │ ├── type_info.cpp │ │ ├── type_info.h │ │ ├── type_iterator.cpp │ │ ├── type_iterator.h │ │ ├── typed_insts.h │ │ ├── typed_insts_test.cpp │ │ ├── vtable.h │ │ └── yaml_test.cpp │ ├── source/ │ │ ├── BUILD │ │ ├── source_buffer.cpp │ │ ├── source_buffer.h │ │ └── source_buffer_test.cpp │ └── testing/ │ ├── BUILD │ ├── compile_helper.cpp │ ├── compile_helper.h │ ├── coverage_helper.h │ ├── file_test.cpp │ ├── testdata/ │ │ └── min_prelude/ │ │ ├── bool.carbon │ │ ├── convert.carbon │ │ ├── destroy.carbon │ │ ├── for.carbon │ │ ├── full.carbon │ │ ├── int.carbon │ │ ├── none.carbon │ │ ├── parts/ │ │ │ ├── as.carbon │ │ │ ├── bool.carbon │ │ │ ├── char.carbon │ │ │ ├── copy.carbon │ │ │ ├── default.carbon │ │ │ ├── destroy.carbon │ │ │ ├── float.carbon │ │ │ ├── float_literal.carbon │ │ │ ├── form.carbon │ │ │ ├── int.carbon │ │ │ ├── int_literal.carbon │ │ │ ├── iterate.carbon │ │ │ ├── maybe_unformed.carbon │ │ │ ├── optional.carbon │ │ │ ├── string.carbon │ │ │ └── uint.carbon │ │ ├── primitives.carbon │ │ ├── uint.carbon │ │ └── unformed.carbon │ ├── yaml_test_helpers.cpp │ ├── yaml_test_helpers.h │ └── yaml_test_helpers_test.cpp ├── utils/ │ ├── README.md │ ├── highlightjs/ │ │ ├── README.md │ │ ├── highlightjs_carbon_lang.js │ │ └── highlightjs_example.html │ ├── nvim/ │ │ ├── README.md │ │ ├── carbon.lua │ │ └── setup.sh │ ├── textmate/ │ │ ├── README.md │ │ ├── Samples/ │ │ │ ├── choices.carbon │ │ │ ├── comments.carbon │ │ │ ├── customs.carbon │ │ │ ├── functions_variables.carbon │ │ │ ├── keywords.carbon │ │ │ ├── literals.carbon │ │ │ ├── main.carbon │ │ │ └── types.carbon │ │ ├── Syntaxes/ │ │ │ └── carbon.tmLanguage.json │ │ └── info.plist │ ├── tree_sitter/ │ │ ├── .gitignore │ │ ├── BUILD │ │ ├── README.md │ │ ├── grammar.js │ │ ├── helix.sh │ │ ├── queries/ │ │ │ └── highlights.scm │ │ ├── src/ │ │ │ └── scanner.c │ │ ├── test_runner.cpp │ │ └── testdata/ │ │ └── string/ │ │ ├── block_inside_quotes.carbon │ │ ├── fail_block_not_enough_quotes.carbon │ │ ├── fail_block_single_quote.carbon │ │ ├── fail_block_unclosed_string.carbon │ │ ├── fail_newline.carbon │ │ ├── fail_raw_block_more_hash_tags_on_left.carbon │ │ ├── fail_raw_block_more_hash_tags_on_right.carbon │ │ ├── fail_raw_block_quotes_not_on_own_line.carbon │ │ ├── fail_raw_more_hash_tags_on_left.carbon │ │ ├── fail_raw_more_hash_tags_on_right.carbon │ │ ├── fail_simple_escaped_newline.carbon │ │ └── fail_unclosed_string.carbon │ ├── vim/ │ │ ├── README.md │ │ ├── ftdetect/ │ │ │ └── carbon.vim │ │ ├── ftplugin/ │ │ │ └── carbon.vim │ │ └── syntax/ │ │ └── carbon.vim │ └── vscode/ │ ├── .gitignore │ ├── .vscode/ │ │ ├── launch.json │ │ └── tasks.json │ ├── .vscodeignore │ ├── README.md │ ├── development.md │ ├── esbuild.js │ ├── eslint.config.mjs │ ├── language-configuration.json │ ├── package.json │ ├── src/ │ │ └── extension.ts │ └── tsconfig.json ├── version_base.bzl └── website/ ├── .ruby-version ├── 404.md ├── Gemfile ├── README.md ├── _config.yml ├── _includes/ │ ├── footer_custom.html │ └── nav_footer_custom.html ├── _sass/ │ └── custom/ │ ├── custom.scss │ └── setup.scss ├── implementation.md └── prebuild.py ================================================ FILE CONTENTS ================================================ ================================================ FILE: .agents/skills/code_style/SKILL.md ================================================ --- name: Code style description: Instructions for code formatting and style guidelines in the Carbon toolchain. --- # Code style ## License - **Licenses**: All Carbon files outside of `third_party/` should have a license following [CONTRIBUTING license instructions](/CONTRIBUTING.md#license). ## Formatting - **Bazel**: Use `pre-commit run buildifier --files ` to format Bazel files. - **C++**: Use `pre-commit run clang-format --files ` to format C++ files. - **Carbon**: The toolchain's `format` command doesn't work well right now. Instead, try to format Carbon code based on other Carbon files and the C++ style. - **Markdown**: Use `pre-commit run prettier --files ` to format markdown files. - **Python**: Use `pre-commit run black --files ` to format Python files. ## Style Guides - **C++ style**: Follow the [Carbon C++ Project Style Guide](/docs/project/cpp_style_guide.md). - **Markdown style**: Follow the [Google developer documentation style guide](https://developers.google.com/style). - **Python style**: Follow the [PEP 8](https://peps.python.org/pep-0008/) style guide. - Wrap code and comments to 80 columns. - Run `pre-commit run flake8 --files ` to check Python style. ================================================ FILE: .agents/skills/summarize_testdata_changes/SKILL.md ================================================ --- name: Summarize testdata changes description: Instructions for summarizing changes to Carbon testdata files (`toolchain/*/testdata`). --- # Summarize testdata changes This skill provides instructions for creating a comprehensive report summarizing changes to Carbon testdata files (`toolchain/*/testdata`) and associating them with related code changes. ## Goals Produce a report that: 1. Summarizes code changes outside of testdata. 2. Groups similar testdata changes together, listing all affected files for each group. **Every change to testdata must be represented by at least one group. This includes changes to CHECK lines.** 3. Provides detailed breakdowns of test input changes and diagnostic output changes in the corresponding group. **Every single change to inputs or to STDERR checks must be explicitly mentioned in the group, with either an inline diff or a link to the file.** ## Process ### 1. Identify Changes Use your VCS (Git or Jujutsu) or query Github to identify changes. For large changes, it is recommended to use the included helper script to extract test input changes. #### For Git Users: - **Summarize code changes**: `git diff --stat -- ':!toolchain/*/testdata'` - To see content of non-testdata changes: `git diff -- ':!toolchain/*/testdata'` - **Identify testdata changes**: `git diff --name-only 'toolchain/*/testdata'` #### For Jujutsu (jj) Users: - **Summarize code changes**: `jj --no-pager diff --stat '~toolchain/*/testdata'` - Note: Quoting the fileset `'~toolchain/*/testdata'` is critical if it contains wildcards. - To see content of non-testdata changes, use `--git` to get standard unified diff format: `jj --no-pager diff --git '~toolchain/*/testdata'` - **Identify testdata changes**: `jj --no-pager diff --name-only 'toolchain/*/testdata'` #### For Github Pull Requests: - **Summarize code changes**: `gh pr diff` - **Identify testdata changes**: `gh pr diff --name-only | grep '^toolchain/.*/testdata'` #### Handling Specific Revisions: If you are summarizing changes in a specific revision (for example, `@-`) or pull request (for example, #1234), add `-r ` or `` to the commands: - `git diff ^ ...` (or use `git show `) - `jj --no-pager diff -r ...` - `gh pr diff ` ### 2. Extract Test Input Changes (Recommended) To easily identify changes, use the included Python helper script to extract all text additions and removals from the diff, categorized by Input, STDERR, and STDOUT changes. This script reads a unified diff from stdin. ```bash # For Git: git diff -- 'toolchain/*/testdata' | python3 .agents/skills/summarize_testdata_changes/scripts/parse_diff.py # For Jujutsu (jj): jj diff --git 'toolchain/*/testdata' | python3 .agents/skills/summarize_testdata_changes/scripts/parse_diff.py # For a specific revision with jj: jj diff -r @- --git 'toolchain/*/testdata' | python3 .agents/skills/summarize_testdata_changes/scripts/parse_diff.py # For a specific PR with Github: gh pr diff 1234 | python3 .agents/skills/summarize_testdata_changes/scripts/parse_diff.py ``` ### 3. Identify Patterns and Produce a List of Groups - Read the diff and produce a list of groups of changes that share a common theme or cause (for example, "Updated expected output for integer literals", "Added tests for new keyword"). - **CRITICAL**: _Every single change_ in the testdata diff must be represented by at least one group. Do not ignore changes to `CHECK` lines. - If it's not clear what group a change belongs to, create a new group for it. - For each group: - Provide a brief description of the group. - (Optional) Briefly note if the group appears to be an intended or unintended consequence of the code changes. - Divide the groups into sections: - Test Changes: Changes to test inputs (lines not prefixed with `// CHECK`), along with diagnostic output changes where relevant - Diagnostic Changes: Changes to diagnostic output (lines prefixed with `// CHECK:STDERR`) with no corresponding changes to test inputs - [Output Type] Changes: Changes to STDOUT (lines prefixed with `// CHECK:STDOUT`) - Create one section for each relevant kind of test. For example, parser tests should typically be in a "Parse Tree Changes" section, check tests should typically be in a "SemIR Changes" section, and lower tests should typically be in an "LLVM IR Changes" section. ### 4. Improve Grouping - Read the list of groups and check to see if any of them should be combined or split apart. If needed, do so. ### 5. Assign Changes to Groups - Read the diff again, and then for _each_ change in the diff: - Add the change to the appropriate group (or, rarely, groups). - **CRITICAL**: _Every single change_ in the testdata diff must be represented by at least one group. Do not ignore changes to `CHECK` lines. - If the change affects _test inputs_ (lines not prefixed with `// CHECK`) or _diagnostic output_ (lines prefixed with `// CHECK:STDERR`): - List the file within the group. Don't just give one or a few examples. Include every file. - Provide an inline diff if the change is small. - Provide a link to the file if the change is large. - Otherwise, if the change only affects _STDOUT_ (lines prefixed with `// CHECK:STDOUT`): - Ensure the group contains a representative example that matches the current change. - The representative example should be an inline diff of the change. - **CRITICAL**: _Every single change_ to test inputs and diagnostic outputs in the files being summarized must be explicitly listed in at least one group. Do not skip changes, even if they are similar to changes you've already seen, and do not just give examples. ### 6. Validation As a final validation step: - Read through the testdata diff again. - Ensure that every change in the diff is reflected by at least one group in the report. ## Report Template Use the following template for the generated report: ```markdown # `testdata` Change Summary ## Code Changes [One paragraph summarizing changes outside of testdata.] ## Test Changes ### [Group Name] [Description of the group.] [Change 1: diff context OR link] [Change 2: diff context OR link] ... ## Diagnostic Changes ### [Group Name] [Description of the group.] [Change 1: diff context OR link] [Change 2: diff context OR link] ... ## [Output Type] Changes ### [File Path] [Description of the group.] [Example diff context] Changes of this kind were found in [Number] files. Examples: [List of files] ... ``` Skip sections that would be empty. ================================================ FILE: .agents/skills/summarize_testdata_changes/scripts/parse_diff.py ================================================ __copyright__ = """ Part of the Carbon Language project, under the Apache License v2.0 with LLVM Exceptions. See /LICENSE for license information. SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception """ import sys from collections import defaultdict from typing import TextIO, Dict, List def parse_diff(stream: TextIO) -> None: current_file: str = "" file_changes: Dict[str, Dict[str, List[str]]] = defaultdict( lambda: {"input": [], "stderr": [], "stdout": []} ) for line in stream: if line.startswith("diff --git"): parts = line.split() if len(parts) >= 4: current_file = ( parts[3][2:] if parts[3].startswith("b/") else parts[3] ) elif line.startswith("+") or line.startswith("-"): if not line.startswith("+++") and not line.startswith("---"): stripped = line[1:].strip() if stripped.startswith("// CHECK:STDERR"): file_changes[current_file]["stderr"].append( line.rstrip("\n") ) elif stripped.startswith("// CHECK:STDOUT"): file_changes[current_file]["stdout"].append( line.rstrip("\n") ) elif stripped.startswith("// CHECK"): file_changes[current_file]["stdout"].append( line.rstrip("\n") ) else: file_changes[current_file]["input"].append( line.rstrip("\n") ) for f, c in file_changes.items(): if not c["input"] and not c["stderr"] and not c["stdout"]: continue print(f"File: {f}") if c["input"]: print(" --- Input Changes ---") for change in c["input"]: print(f" {change}") if c["stderr"]: print(" --- STDERR Changes ---") for change in c["stderr"]: print(f" {change}") if c["stdout"]: print(" --- STDOUT Changes ---") for change in c["stdout"]: print(f" {change}") print("-" * 40) if __name__ == "__main__": parse_diff(sys.stdin) ================================================ FILE: .agents/skills/tool_usage/SKILL.md ================================================ --- name: Tool usage description: Instructions for AI assistants on what tools to use in the carbon-lang project. --- # Tool usage ## Bazelisk and Bazel We use `bazelisk` for build and test. **IMPORTANT**: AI assistants use `bazelisk` instead of `bazel`. ## Pre-commit Running `pre-commit` is mandatory. To run it on all files: ```bash pre-commit run -a ``` To validate a specific list of files: ```bash pre-commit run --files ``` ================================================ FILE: .agents/skills/toolchain_development/SKILL.md ================================================ --- name: Toolchain development description: Instructions for checking, building, debugging, and understanding the Carbon toolchain. --- # Toolchain development ## Toolchain structure - Under [`toolchain/`](/toolchain/): - [`base/`](/toolchain/base/): Base infrastructure and common utilities. - [`check/`](/toolchain/check/): Semantic analysis (SemIR generation). - [`lex/`](/toolchain/lex/): Lexing (Source -> Tokens). - [`lower/`](/toolchain/lower/): Lowering to LLVM IR. - [`parse/`](/toolchain/parse/): Parsing (Token -> Parse Tree). - [`sem_ir/`](/toolchain/sem_ir/): Semantic Intermediate Representation (SemIR) definitions. ## Toolchain architecture - **Documentation**: Refer to [`toolchain/docs`](/toolchain/docs) for detailed architecture design and patterns. - Refer to [Toolchain Idioms](/toolchain/docs/idioms.md) for a comprehensive list of patterns (for example, `ValueStore`, formatting `.def` files, struct reflection) used throughout the implementation. - **Phases**: Lex -> Parse -> Check -> Lower. - **Definitions**: Many kinds (tokens, parse nodes, SemIR instructions) are defined in `.def` files and expanded by way of macros. - **Handlers**: - Parser: `Handle` in `parse/handle_*.cpp`. - Checker: `HandleParseNode` in `check/handle_*.cpp`. - Lowering: `HandleInst` in `lower/handle_*.cpp`. - **Iteration**: Prefer iterative algorithms over recursive ones to prevent stack exhaustion on complex codebases. ### Essential commands - **Test everything**: `bazelisk test //...` - **Test specific target**: `bazelisk test //toolchain/testing:file_test` - **Test specific file**: `bazelisk test //toolchain/testing:file_test --test_arg=--file_tests=` - **Build toolchain**: `bazelisk build //toolchain/...` ### Updating test data Carbon tests often use `file_test` (for example, `//toolchain/testing/file_test`). If you change compiler behavior, you likely need to update expected test outputs. **Do not manually edit thousands of lines of expected output.** Use the script: ```bash ./toolchain/autoupdate_testdata.py # Or for a specific file: ./toolchain/autoupdate_testdata.py toolchain/check/testdata/my_test.carbon ``` ## Debugging and diagnostics - **Printing to stderr**: Use `llvm::errs() << "debug info\n";`. - Avoid `std::cout` (it may interfere with tool output). - **SemIR Stringification**: - SemIR objects often have a `Print` method or `operator<<`. - `inst.Print(llvm::errs())` - **Debugging Crashes**: - Bazel sandboxing can hide artifacts. Use `--sandbox_debug` if needed, but often running the binary directly from `bazel-bin/` is easier for debugging. ## Error handling - **No exceptions**: Do not use C++ exceptions. - **`ErrorOr`**: Return `ErrorOr` for fallible operations. - Check with `if (auto result = Function(); result) { Use(*result); }` - **`llvm::Expected`**: Similar to `ErrorOr`, used when interfacing with LLVM. ### Casting (LLVM style) - Use `llvm::cast(obj)` (checked, asserts on failure). - Use `llvm::dyn_cast(obj)` (returns null on failure). - Use `llvm::isa(obj)` (boolean check). - **Avoid** `dynamic_cast` and standard RTTI. ### Data structures - Prefer APIs in `common/` and `toolchain/base/` over LLVM ADTs. For example, use `Map` instead of `llvm::DenseMap`. - If no Carbon API exists, prefer LLVM ADTs over standard library ones (for example `llvm::SmallVector`, `llvm::StringRef`). - `StringRef` is a view; be careful with lifetimes. ## Common pitfalls 1. **Legacy `explorer` references**: The `explorer` prototype has been moved. Ignore references to it in proposals or old docs; focus on `toolchain`. 2. **Manually updating test files**: Always check if `autoupdate_testdata.py` can do it for you. 3. **Using `std::string` unnecessarily**: Prefer `llvm::StringRef` for arguments. 4. **Header includes**: Use specific include orders (often enforced by `clang-format`). 5. **Parse node order**: Semantics processes parse nodes in post-order; ensure your parser transitions support this. ================================================ FILE: .bazelignore ================================================ # Part of the Carbon Language project, under the Apache License v2.0 with LLVM # Exceptions. See /LICENSE for license information. # SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception # Carbon creates some non-standard bazel directories, so ignore them. bazel-carbon-lang # See github_tools/MODULE.bazel. github_tools # Example Bazel project. examples/bazel ================================================ FILE: .bazelrc ================================================ # Part of the Carbon Language project, under the Apache License v2.0 with LLVM # Exceptions. See /LICENSE for license information. # SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception # Setup stamping with Carbon's workspace status attached but disable it by # default. # # Note that while we have minimized the impact of stamping on build caching, it # still has a meaningful impact, especially during development. So we disable # stamping by default and builds that need to include the workspace status # should explicitly enable it with `--stamp`. common --workspace_status_command=./scripts/workspace_status.py common --nostamp # Provide aliases for configuring the release and pre-release version being # built. For documentation of these flags, see //bazel/version/BUILD. common --flag_alias=release=//bazel/version:release common --flag_alias=pre_release=//bazel/version:pre_release common --flag_alias=rc_number=//bazel/version:rc_number common --flag_alias=nightly_date=//bazel/version:nightly_date # Support running clang-tidy with: # bazel build --config=clang-tidy -k //... # See: https://github.com/erenon/bazel_clang_tidy common:clang-tidy --aspects @bazel_clang_tidy//clang_tidy:clang_tidy.bzl%clang_tidy_aspect common:clang-tidy --output_groups=report common:clang-tidy --@bazel_clang_tidy//:clang_tidy_config=//:clang_tidy_config common:clang-tidy --action_env=PATH --host_action_env=PATH # This warning seems to incorrectly fire in this build configuration, despite # not firing in our normal builds. common:clang-tidy --copt=-Wno-unknown-pragmas # --config=non-fatal-checks makes CHECK failures not terminate compilation. common:non-fatal-checks --per_file_copt=common/check_internal.cpp@-DCARBON_NON_FATAL_CHECKS # Provide an alias for controlling the `carbon_*` Bazel rules' configuration. We # enable use of the target config here to make our build and tests more # efficient, see the documentation in //bazel/carbon_rules/BUILD for details. common --flag_alias=use_target_config_carbon_rules=//bazel/carbon_rules:use_target_config_carbon_rules common --flag_alias=use_target_config_runtimes_builder=//toolchain/driver:use_target_config_runtimes_builder # Bazel doesn't track what commands the flag_alias is valid for, so we can't use # common here. build --use_target_config_carbon_rules build --use_target_config_runtimes_builder # Default to using a disk cache to minimize re-building LLVM and Clang which we # try to avoid updating too frequently to minimize rebuild cost. The location # here can be overridden in the user configuration where needed. common --disk_cache=~/.cache/carbon-lang-build-cache # If you'd like a different disk cache size, override it by copying this # line to `user.bazelrc` in the repository root and modify the number there. common --experimental_disk_cache_gc_max_size=100G # Enable some safety when using the build cache. Defaults to `lite`. common --guard_against_concurrent_changes=full # Used by clang_configuration.bzl. common --action_env=CC --host_action_env=CC common --action_env=CMAKE_SYSROOT --host_action_env=CMAKE_SYSROOT # Disable warnings for all external compilations. These involve code that isn't # developed as part of Carbon and may be difficult or impossible to patch, so # warnings aren't likely to be actionable. common --per_file_copt=external/.*\.(c|cc|cpp|cxx)$@-w common --host_per_file_copt=external/.*\.(c|cc|cpp|cxx)$@-w # Default dynamic linking to off. While this can help build performance in some # edge cases with very large linked executables and a slow linker, between using # fast linkers on all platforms (LLD and the Apple linker), as well as having # relatively few such executables, shared objects simply waste too much space in # our builds. common --dynamic_mode=off # Always compile PIC code. There are few if any disadvantages on the platforms # and architectures we care about and it avoids the need to compile files twice. common --force_pic # Completely disable Bazel's automatic stripping of debug information. Removing # that information causes unhelpful backtraces from unittest failures and other # crashes. Optimized builds already avoid using debug information by default. common --strip=never # Enable Abseil for GoogleTest. common --define=absl=1 # Enable TCMalloc on Linux in optimized builds. common --custom_malloc=//bazel/malloc:tcmalloc_if_linux_opt # Configuration for enabling Address Sanitizer. Note that this is enabled by # default for fastbuild. The config is provided to enable ASan even in # optimized or other build configurations. Note that ASan and TCMalloc are # incompatible so this explicitly forces the system malloc. common:asan --features=asan common:asan --custom_malloc=@bazel_tools//tools/cpp:malloc # Configuration for enabling LibFuzzer (along with ASan). common:fuzzer --features=fuzzer # Force actions to have a UTF-8 language encoding. # TODO: Need to investigate what this should be on Windows, but at least for # Linux and macOS this seems strictly better than the Bazel default of just # `en_US`. common --action_env=LANG=en_US.UTF-8 # Allow per-platform configuration. common --enable_platform_specific_config # Enable libpfm for google_benchmark on Linux only. common:linux --define=pfm=1 # Enable split debug info on Linux, which is significantly more space efficient # and should work well with modern debuggers. Note that this is Linux specific # as macOS has its own approach that is always partially but not completely # split. # # Note: if using GDB, see documentation to get that working: # https://docs.carbon-lang.dev/docs/project/contribution_tools.html#debugging-with-gdb-instead-of-lldb # # TODO: Bazel has a bug where it doesn't manage dwo files in the cache correctly. # common:linux --fission=yes # Disables `actions.declare_symlink`. Done for cross-environment support. common --allow_unresolved_symlinks=false # Allow users to override any of the flags desired by importing a user-specific # RC file here if present. try-import %workspace%/user.bazelrc # Query error in `@bazel_tools`. This reproduces with # `bazel query 'deps(//...)'`. # TODO: Enable the flag once compatibility issues are fixed. # common --incompatible_disable_non_executable_java_binary # Incompatible with the clang-tidy build mode. # TODO: Enable the flag once compatibility issues are fixed. # common --incompatible_auto_exec_groups # Incompatible with `rules_cc`. # TODO: Enable the flag once compatibility issues are fixed. # common --incompatible_no_rule_outputs_param # common --incompatible_stop_exporting_language_modules # Incompatible with `rules_pkg`. # TODO: Enable the flag once compatibility issues are fixed. # common --incompatible_disable_target_default_provider_fields # Incompatible with `rules_shell`. # TODO: Enable the flag once compatibility issues are fixed. # common --incompatible_check_visibility_for_toolchains # Enable as many incompatible flags as we can, per # https://bazel.build/release/backward-compatibility. To get the latest list, # using `bazelisk --migrate build //...` will help. common --incompatible_allow_tags_propagation common --incompatible_always_check_depset_elements common --incompatible_always_include_files_in_data common --incompatible_bazel_test_exec_run_under common --incompatible_check_sharding_support common --incompatible_check_testonly_for_output_files common --incompatible_config_setting_private_default_visibility common --incompatible_default_to_explicit_init_py common --incompatible_depset_for_java_output_source_jars common --incompatible_depset_for_libraries_to_link_getter common --incompatible_disable_autoloads_in_main_repo common --incompatible_disable_native_android_rules common --incompatible_disable_native_repo_rules common --incompatible_disable_objc_library_transition common --incompatible_disable_starlark_host_transitions common --incompatible_disable_target_provider_fields common --incompatible_disallow_ctx_resolve_tools common --incompatible_disallow_empty_glob common --incompatible_disallow_legacy_py_provider common --incompatible_disallow_sdk_frameworks_attributes common --incompatible_disallow_struct_provider_syntax common --incompatible_do_not_split_linking_cmdline common --incompatible_dont_enable_host_nonhost_crosstool_features common --incompatible_dont_use_javasourceinfoprovider common --incompatible_enable_apple_toolchain_resolution common --incompatible_enable_deprecated_label_apis common --incompatible_enable_proto_toolchain_resolution common --incompatible_enforce_config_setting_visibility common --incompatible_enforce_starlark_utf8 common --incompatible_exclusive_test_sandboxed common --incompatible_fail_on_unknown_attributes common --incompatible_fix_package_group_reporoot_syntax common --incompatible_java_common_parameters common --incompatible_legacy_local_fallback common --incompatible_locations_prefers_executable common --incompatible_make_thinlto_command_lines_standalone common --incompatible_merge_fixed_and_default_shell_env common --incompatible_merge_genfiles_directory common --incompatible_modify_execution_info_additive common --incompatible_new_actions_api common --incompatible_no_attr_license common --incompatible_no_implicit_file_export common --incompatible_no_implicit_watch_label common --incompatible_objc_alwayslink_by_default common --incompatible_package_group_has_public_syntax common --incompatible_py2_outputs_are_suffixed common --incompatible_py3_is_default common --incompatible_python_disable_py2 common --incompatible_python_disallow_native_rules common --incompatible_remote_use_new_exit_code_for_lost_inputs common --incompatible_remove_legacy_whole_archive common --incompatible_require_ctx_in_configure_features common --incompatible_require_linker_input_cc_api common --incompatible_run_shell_command_string common --incompatible_sandbox_hermetic_tmp common --incompatible_simplify_unconditional_selects_in_rule_attrs common --incompatible_stop_exporting_build_file_path common --incompatible_strict_action_env common --incompatible_strip_executable_safely common --incompatible_top_level_aspects_require_providers common --incompatible_unambiguous_label_stringification common --incompatible_use_cc_configure_from_rules_cc common --incompatible_use_new_cgroup_implementation common --incompatible_use_plus_in_repo_names common --incompatible_use_python_toolchains common --incompatible_validate_top_level_header_inclusions common --incompatible_visibility_private_attributes_at_definition ================================================ FILE: .bazelversion ================================================ 8.5.1 ================================================ FILE: .clang-format ================================================ # Part of the Carbon Language project, under the Apache License v2.0 with LLVM # Exceptions. See /LICENSE for license information. # SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception --- BasedOnStyle: Google AllowShortBlocksOnASingleLine: 'false' AllowShortIfStatementsOnASingleLine: Never AllowShortLoopsOnASingleLine: 'false' DerivePointerAlignment: 'false' ExperimentalAutoDetectBinPacking: 'false' FixNamespaceComments: 'true' InsertBraces: 'true' PointerAlignment: Left # We abuse control macros for formatting other kinds of macros. SpaceBeforeParens: ControlStatementsExceptControlMacros IfMacros: [ 'CARBON_DEFINE_RAW_ENUM_CLASS', 'CARBON_DEFINE_ENUM_CLASS_NAMES', 'CARBON_DEFINE_RAW_ENUM_MASK', 'CARBON_DEFINE_ENUM_MASK_NAMES', 'CARBON_KIND_SWITCH', ] StatementMacros: ['ABSTRACT'] QualifierAlignment: Custom QualifierOrder: [inline, static, friend, constexpr, const, volatile, restrict, type] ================================================ FILE: .clang-tidy ================================================ # Part of the Carbon Language project, under the Apache License v2.0 with LLVM # Exceptions. See /LICENSE for license information. # SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception --- # Get colors when outputting through `bazel build --config=clang-tidy`. UseColor: true # This is necessary for `--config=clang-tidy` to catch errors. WarningsAsErrors: '*' Checks: # We turn on all of a few categories by default. - '-*' - 'bugprone-*' - 'google-*' - 'misc-*' - 'modernize-*' - 'performance-*' - 'readability-*' # Disabled due to the implied style choices. - '-misc-const-correctness' - '-misc-include-cleaner' - '-misc-use-anonymous-namespace' - '-modernize-deprecated-headers' - '-modernize-return-braced-init-list' - '-modernize-use-default-member-init' - '-modernize-use-integer-sign-comparison' - '-modernize-use-emplace' - '-readability-avoid-nested-conditional-operator' - '-readability-convert-member-functions-to-static' - '-readability-else-after-return' - '-readability-identifier-length' - '-readability-implicit-bool-conversion' - '-readability-make-member-function-const' - '-readability-math-missing-parentheses' - '-readability-static-definition-in-anonymous-namespace' - '-readability-use-anyofallof' # Warns when we have multiple empty cases in switches, which we do for comment # reasons. - '-bugprone-branch-clone' # Frequently warns on multiple parameters of the same type. - '-bugprone-easily-swappable-parameters' # Finds issues like out-of-memory in main(). We don't use exceptions, so it's # unlikely to find real issues. - '-bugprone-exception-escape' # Has false positives in places such as using an argument to declare a name, # which cannot have parentheses. For our limited use of macros, this is a # common conflict. - '-bugprone-macro-parentheses' # Conflicts with integer type C++ style. - '-bugprone-narrowing-conversions' # Has false positives for `enum_base.h`. Clang's built-in switch warnings # cover most of our risk of bugs here. - '-bugprone-switch-missing-default-case' # In clang-tidy 16, has false positives on code like: # while (auto name_ref = insts().Get(inst_id).TryAs()) { # inst_id = name_ref->value_id; # ^ unchecked access to optional value # } - '-bugprone-unchecked-optional-access' # Overlaps with `readability-function-size`. - '-google-readability-function-size' # Suggests usernames on TODOs, which we don't want. - '-google-readability-todo' # Extremely slow. TODO: Re-enable once # https://github.com/llvm/llvm-project/issues/128797 is fixed. - '-misc-confusable-identifiers' # Overlaps with `-Wno-missing-prototypes`. - '-misc-use-internal-linkage' # Suggests `std::array`, which we could migrate to, but conflicts with the # status quo. - '-modernize-avoid-c-arrays' # Warns on creation of SemIR typed insts, for which we do not currently want # to use designated initialization. - '-modernize-use-designated-initializers' # Only fixes const methods, not non-const, which yields distracting results on # accessors. - '-modernize-use-nodiscard' # We aren't using the ranges library due to performance concerns. - '-modernize-use-ranges' # Low value compared to the engineering cost. - '-performance-enum-size' # Duplicates `modernize-pass-by-value`. - '-performance-unnecessary-value-param' # Warns on enums which use the `LastValue = Value` pattern if all the other # discriminants aren't given an explicit value. - '-readability-enum-initial-value' # Warns too frequently. - '-readability-function-cognitive-complexity' # Warns in reasonably documented situations. - '-readability-magic-numbers' # Warns on `= {}` which is also used to indicate which fields do not need to # be explicitly initialized in aggregate initialization. - '-readability-redundant-member-init' # Warns when callers use similar names as different parameters. - '-readability-suspicious-call-argument' CheckOptions: # Don't warn on structs; done by ignoring when there are only public members. - key: misc-non-private-member-variables-in-classes.IgnoreClassesWithAllMemberVariablesBeingPublic value: true # CamelCase names. - key: readability-identifier-naming.ClassCase value: CamelCase - key: readability-identifier-naming.ClassConstantCase value: CamelCase - key: readability-identifier-naming.ConstexprVariableCase value: CamelCase - key: readability-identifier-naming.NamespaceCase value: CamelCase - key: readability-identifier-naming.StructCase value: CamelCase - key: readability-identifier-naming.TemplateParameterCase value: CamelCase - key: readability-identifier-naming.TypeAliasCase value: CamelCase - key: readability-identifier-naming.TypedefCase value: CamelCase - key: readability-identifier-naming.UnionCase value: CamelCase # lower_case names. - key: readability-identifier-naming.ClassMemberCase value: lower_case - key: readability-identifier-naming.ParameterCase value: lower_case - key: readability-identifier-naming.VariableCase value: lower_case # TODO: This is for explorer's use of LLVM casting support, so we should be # able to remove it once explorer is deleted. - key: readability-identifier-naming.MethodIgnoredRegexp value: '^classof$' # This erroneously fires in C++20 mode with LLVM 16 clang-tidy, due to: # https://github.com/llvm/llvm-project/issues/46097 - key: readability-identifier-naming.TemplateParameterIgnoredRegexp value: '^expr-type$' # Don't require writing a return type on lambdas. - key: modernize-use-trailing-return-type.TransformLambdas value: none # Use lines rather than statements to measure function size, because # for readability purposes we care about the code as written, before # preprocessing. - key: readability-function-size.StatementThreshold value: none - key: readability-function-size.LineThreshold # Chose 800 to match the default for StatementThreshold. value: 800 ================================================ FILE: .clangd ================================================ # Part of the Carbon Language project, under the Apache License v2.0 with LLVM # Exceptions. See /LICENSE for license information. # SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception CompileFlags: # Workaround for https://github.com/clangd/clangd/issues/1582 Remove: [-march=*] Diagnostics: # `unused-includes`: has false positives, reporting includes unused when # they are used. Suppress: [unused-includes] --- # Suppress common diagnostics for x-macro files. If: PathMatch: .*\.def Diagnostics: Suppress: # The `#error` requiring a macro definition. - pp_hash_error --- # Suppress diagnostics for template source files. If: PathMatch: .*\.tpl\.h Diagnostics: Suppress: - undeclared_var_use ================================================ FILE: .codespell_ignore ================================================ # Part of the Carbon Language project, under the Apache License v2.0 with LLVM # Exceptions. See /LICENSE for license information. # SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception AggregateT ArchType atleast circularly compiletime copyable crate createor crossreference falsy forin groupt indext inout isELF parameteras pullrequest rightt rouge statics switcht ================================================ FILE: .gdbinit ================================================ # Part of the Carbon Language project, under the Apache License v2.0 with LLVM # Exceptions. See /LICENSE for license information. # SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception source external/+llvm_project+llvm-project/llvm/utils/gdb-scripts/prettyprinters.py source external/+llvm_project+llvm-project/libcxx/utils/gdb/libcxx/printers.py python register_libcxx_printer_loader() ================================================ FILE: .gitattributes ================================================ # Part of the Carbon Language project, under the Apache License v2.0 with LLVM # Exceptions. See /LICENSE for license information. # SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception # This tells Github to detect files having the extension `.def` as `C++` files, which # ensures that these files get syntax highlighted properly. *.def linguist-language=C++ ================================================ FILE: .github/ISSUE_TEMPLATE/01_toolchain_bug.yml ================================================ # Part of the Carbon Language project, under the Apache License v2.0 with LLVM # Exceptions. See /LICENSE for license information. # SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception name: Toolchain bug description: > Report a bug with the toolchain. labels: [toolchain] body: - type: markdown attributes: value: > **Attention:** If this is a _question_, please use either [GitHub Discussions](https://github.com/carbon-language/carbon-lang/discussions) or [Discord](https://discord.gg/ZjVdShJDAs). - type: textarea id: desc attributes: label: > Description of the bug: - type: textarea id: repro attributes: label: > What did you do, or what's a simple way to reproduce the bug? description: > Please provide example code and errors; a shortlink to the execution on carbon.compiler-explorer.com can also help. - type: textarea id: expected attributes: label: > What did you expect to happen? - type: textarea id: actual attributes: label: > What actually happened? - type: textarea id: extras attributes: label: > Any other information, logs, or outputs that you want to share? ================================================ FILE: .github/ISSUE_TEMPLATE/02_documentation_bug.yml ================================================ # Part of the Carbon Language project, under the Apache License v2.0 with LLVM # Exceptions. See /LICENSE for license information. # SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception name: Documentation bug description: > Report a bug with the Carbon Language project documentation. body: - type: markdown attributes: value: > **Attention:** If this is a _question_, please use either [GitHub Discussions](https://github.com/carbon-language/carbon-lang/discussions) or [Discord](https://discord.gg/ZjVdShJDAs). - type: textarea id: desc attributes: label: > Description of the bug: - type: textarea id: input attributes: label: > Link to documentation: - type: textarea id: actual attributes: label: > What should it say instead? - type: textarea id: extras attributes: label: > Any other information, logs, or outputs that you want to share? ================================================ FILE: .github/ISSUE_TEMPLATE/03_leads_question.yml ================================================ # Part of the Carbon Language project, under the Apache License v2.0 with LLVM # Exceptions. See /LICENSE for license information. # SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception name: Leads question description: > Ask the leads for feedback on an important issue, such as a language design choice. labels: [leads question] body: - type: markdown attributes: value: > **Attention:** If this is a _question_, please use either [GitHub Discussions](https://github.com/carbon-language/carbon-lang/discussions) or [Discord](https://discord.gg/ZjVdShJDAs). - type: textarea id: summary attributes: label: > Summary of issue: description: > Provide a short, one-paragraph summary of the issue. - type: textarea id: detail attributes: label: > Details: description: > Provide detailed information about the issue. For example, on a language design choice, it can help to provide various options with a summary of trade-offs. - type: textarea id: extras attributes: label: > Any other information that you want to share? ================================================ FILE: .github/ISSUE_TEMPLATE/config.yml ================================================ # Part of the Carbon Language project, under the Apache License v2.0 with LLVM # Exceptions. See /LICENSE for license information. # SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception blank_issues_enabled: true contact_links: - name: I have a question about Carbon url: https://github.com/carbon-language/carbon-lang/discussions/categories/getting-started about: Ask questions about Carbon in GitHub Discussions. - name: I want to join the Discord url: https://discord.gg/ZjVdShJDAs about: Discord is where a lot of design discussion occurs. ================================================ FILE: .github/actions/build-setup-common/action.yml ================================================ # Part of the Carbon Language project, under the Apache License v2.0 with LLVM # Exceptions. See /LICENSE for license information. # SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception name: Setup build environment (macOS) inputs: matrix_runner: required: true remote_cache_upload: required: true runs: using: composite steps: # Setup Python and related tools. - uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 with: # Match the min version listed in docs/project/contribution_tools.md # or the oldest version available on the OS. python-version: ${{ inputs.matrix_runner == 'macos-14' && '3.11' || '3.10' }} - uses: ./.github/actions/build-setup-macos if: startsWith(inputs.matrix_runner, 'macos') with: matrix_runner: ${{ inputs.matrix_runner }} - uses: ./.github/actions/build-setup-ubuntu if: startsWith(inputs.matrix_runner, 'ubuntu') # Print the various tool paths and versions to help in debugging. - name: Print tool debugging info shell: bash run: | echo '*** PATH' echo $PATH echo '*** bazelisk' which bazelisk bazelisk --version echo '*** run_bazel.py' ./scripts/run_bazel.py --version echo '*** python' which python python --version echo '*** clang' which clang clang --version echo '*** clang++' which clang++ clang++ --version echo '*** clang-tidy' which clang-tidy clang-tidy --version # Add our bazel configuration and print basic info to ease debugging. - name: Configure Bazel and print info env: # Add a cache version for changes that bazel won't otherwise detect, # like llvm version changes. CACHE_VERSION: 1 shell: bash run: | cat >user.bazelrc <" echo '*** Delete iOS simulators' xcrun simctl delete all || \ xcrun simctl delete all || \ xcrun simctl delete all sudo rm -rf ~/Library/Developer/CoreSimulator/Caches/* # Install and cache LLVM 19 from Homebrew. Some runners may have LLVM 19, # but this is reliable (including with libc++), and gives us testing at the # minimum supported LLVM version. - name: Cache Homebrew id: cache-homebrew-macos uses: actions/cache@cdf6c1fa76f9f475f3d7449005a359c84ca0f306 # v5.0.3 with: # Cover all the critical parts of Homebrew here. Homebrew on Arm macOS # uses its own prefix making this easy to cover, but we need a few # different paths for Intel. path: | ${{ runner.arch == 'ARM64' && '/opt/homebrew' || ' /usr/local/Homebrew /usr/local/Cellar /usr/local/Frameworks /usr/local/bin /usr/local/opt ' }} # Note the key needs to include all the packages we're adding. key: Homebrew-Cache-${{ inputs.matrix_runner }}-${{ runner.arch }}-llvm@19 - name: Install LLVM and Clang with Homebrew if: steps.cache-homebrew-macos.outputs.cache-hit != 'true' shell: bash run: | echo '*** Prune brew leaves' # We prune all the leaf packages to have a minimal environment. This # both minimizes the install space and avoids accidental dependencies # on installed packages. brew leaves LEAVES=$(brew leaves | egrep -v '^(bazelisk|gh|git|git-lfs|gnu-tar|go@.*|jq|pipx|node@.*|openssl@.*|wget|yq|zlib)$') brew uninstall -f --ignore-dependencies $LEAVES echo '*** Installing LLVM deps' brew install --force-bottle --only-dependencies llvm@19 echo '*** Installing LLVM itself' brew install --force-bottle --force --verbose llvm@19 echo '*** brew info llvm@19' brew info llvm@19 echo '*** brew autoremove' brew autoremove echo '*** brew info' brew info echo '*** brew leaves' brew leaves echo '*** brew config' brew config - name: Setup LLVM and Clang shell: bash run: | LLVM_PATH="$(brew --prefix llvm@19)" echo "Using ${LLVM_PATH}" echo "${LLVM_PATH}/bin" >> $GITHUB_PATH echo '*** ls "${LLVM_PATH}"' ls "${LLVM_PATH}" echo '*** ls "${LLVM_PATH}/bin"' ls "${LLVM_PATH}/bin" ================================================ FILE: .github/actions/build-setup-ubuntu/action.yml ================================================ # Part of the Carbon Language project, under the Apache License v2.0 with LLVM # Exceptions. See /LICENSE for license information. # SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception name: Setup build environment (Ubuntu) runs: using: composite steps: # Ubuntu images start with ~23GB available; this takes a few seconds to add # ~22GB more. # # Although we could delete more, if we run into a limit, not deleting # everything provides a little flexibility to get space while trying # to shrink the build. - name: Free up disk space uses: jlumbroso/free-disk-space@54081f138730dfa15788a46383842cd2f914a1be # v1.3.1 with: android: true dotnet: true haskell: true # Enabling large-packages adds ~3 minutes to save ~4GB, so turn it off # to save time. large-packages: false # Cache and install a recent version of LLVM. This uses the GitHub action # cache to avoid directly downloading on each iteration and improve # reliability. - name: Cache LLVM and Clang installation id: cache-llvm-ubuntu uses: actions/cache@cdf6c1fa76f9f475f3d7449005a359c84ca0f306 # v5.0.3 with: path: ~/llvm key: LLVM-19.1.7-Cache-ubuntu-${{ runner.arch }} - name: Download LLVM and Clang installation if: steps.cache-llvm-ubuntu.outputs.cache-hit != 'true' shell: bash run: | cd ~ LLVM_RELEASE=19.1.7 LLVM_TARBALL_NAME=LLVM-$LLVM_RELEASE-Linux-X64 LLVM_PATH=~/llvm echo "*** Downloading $LLVM_RELEASE" wget --show-progress=off "https://github.com/llvm/llvm-project/releases/download/llvmorg-$LLVM_RELEASE/$LLVM_TARBALL_NAME.tar.xz" echo "*** Extracting $LLVM_TARBALL_NAME.tar.xz" mkdir $LLVM_PATH tar -xJf $LLVM_TARBALL_NAME.tar.xz --strip-components=1 -C $LLVM_PATH echo "*** Deleting $LLVM_TARBALL_NAME.tar.xz" rm $LLVM_TARBALL_NAME.tar.xz echo "*** Testing `clang++ --version`" $LLVM_PATH/bin/clang++ --version # The installation contains *huge* parts of LLVM we don't need for the # toolchain. Prune them here to keep our cache small. echo "*** Cleaning the 'llvm' directory" rm $LLVM_PATH/lib/{*.a,*.so,*.so.*} rm $LLVM_PATH/bin/{flang-*,mlir-*,clang-{scan-deps,check,repl},*-test,llvm-{lto*,reduce,bolt*,exegesis,jitlink},bugpoint,opt,llc} echo "*** Size of the 'llvm' directory" du -hs $LLVM_PATH - name: Setup LLVM and Clang paths shell: bash run: | LLVM_PATH=~/llvm echo "Using ${LLVM_PATH}" echo "${LLVM_PATH}/bin" >> $GITHUB_PATH echo '*** ls "${LLVM_PATH}"' ls "${LLVM_PATH}" echo '*** ls "${LLVM_PATH}/bin"' ls "${LLVM_PATH}/bin" ================================================ FILE: .github/actions/test-setup/action.yml ================================================ # Part of the Carbon Language project, under the Apache License v2.0 with LLVM # Exceptions. See /LICENSE for license information. # SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception name: Test setup inputs: matrix_runner: required: true base_sha: required: true remote_cache_key: required: true targets_file: required: true use_direct_targets: default: 'false' outputs: has_code: value: ${{ steps.filter.outputs.has_code}} has_cpp_files: value: ${{ steps.filter.outputs.has_cpp_files}} runs: using: composite steps: # Tests should only run on applicable paths, but we still need to have an # action run for the merge queue. We filter steps based on the paths here, # and condition steps on the output. - id: filter uses: dorny/paths-filter@de90cc6fb38fc0963ad72b210f1f284cd68cea36 # v3.0.2 with: filters: | has_code: - '!{**/*.md,LICENSE,CODEOWNERS,.git*}' has_cpp_files: - '{**/*.cpp,**/*.h}' # Disable uploads when the remote cache is read-only. - name: Set up remote cache access (read-only) if: steps.filter.outputs.has_code == 'true' && github.event_name == 'pull_request' shell: bash run: | echo "remote_cache_upload=--remote_upload_local_results=false" \ >> $GITHUB_ENV # Provide a cache key when the remote cache is read-write. - name: Set up remote cache access (read-write) if: steps.filter.outputs.has_code == 'true' && github.event_name != 'pull_request' shell: bash env: REMOTE_CACHE_KEY: ${{ inputs.remote_cache_key }} run: | echo "$REMOTE_CACHE_KEY" | base64 -d > $HOME/remote_cache_key.json echo "remote_cache_upload=--google_credentials=$HOME/remote_cache_key.json" \ >> $GITHUB_ENV - uses: ./.github/actions/build-setup-common if: steps.filter.outputs.has_code == 'true' with: matrix_runner: ${{ inputs.matrix_runner }} remote_cache_upload: ${{ env.remote_cache_upload }} # Just for visibility, print space before and after the build. - name: Disk space before build if: steps.filter.outputs.has_code == 'true' shell: bash run: df -h - name: Verify MODULE.bazel.lock if: steps.filter.outputs.has_code == 'true' shell: bash run: | exit_code=0 ./scripts/run_bazel.py \ --attempts=5 \ mod deps --lockfile_mode=error || exit_code=$? if (( $exit_code != 0 )); then ./scripts/run_bazel.py \ --attempts=5 \ mod deps --lockfile_mode=update echo "MODULE.bazel.lock is out of date! Use below file for update." echo "Platforms may require merging output, for example by applying" echo "an update, re-running triggers, and applying the next update." echo "============================================================" cat MODULE.bazel.lock echo "============================================================" exit 1 fi # Build and run all targets on branch pushes to ensure we always have a # clean tree. We don't expect this to be an interactive path and so don't # optimize the latency of this step. - name: Using all targets for push if: steps.filter.outputs.has_code == 'true' && github.event_name == 'push' shell: bash env: TARGETS_FILE: ${{ inputs.targets_file }} run: | echo "//..." >$TARGETS_FILE # Compute the set of possible rules impacted by this change using # Bazel-based diffing. This lets PRs and the merge queue have a much more # efficient test CI action by avoiding even enumerating (and downloading) # all of the unaffected Bazel targets. - name: Compute indirect pull request targets if: steps.filter.outputs.has_code == 'true' && github.event_name != 'push' && inputs.use_direct_targets != 'true' shell: bash env: # Compute the base SHA from the different event structures. GIT_BASE_SHA: ${{ inputs.base_sha }} TARGETS_FILE: ${{ inputs.targets_file }} run: | # First fetch the relevant base into the git repository. git fetch --depth=1 origin $GIT_BASE_SHA # Do a retried query to try to download things for target-determinator. ./scripts/run_bazel.py --attempts=5 cquery //... > /dev/null # Then use `target-determinator` as wrapped by our script. ./scripts/target_determinator.py $GIT_BASE_SHA >$TARGETS_FILE # Bazel requires a test target to run the test command. There may be # no targets or there may only be non-test targets that we want to # build, so simply inject an explicit no-op test target. echo "//scripts:no_op_test" >> $TARGETS_FILE # Run the query to generate the targets file. - name: Compute direct pull request targets if: steps.filter.outputs.has_code == 'true' && github.event_name != 'push' && inputs.use_direct_targets == 'true' shell: bash env: GIT_BASE_SHA: ${{ inputs.base_sha }} QUERY_FILE: ${{ inputs.targets_file }}.query TARGETS_FILE: ${{ inputs.targets_file }} run: | # First fetch the relevant base into the git repository. git fetch --depth=1 origin $GIT_BASE_SHA # Generate the query file. `same_pkg_direct_rdeps` is used to try to # only get targets that contain modified files as srcs or hdrs (or # similar artifacts). echo 'same_pkg_direct_rdeps(' > $QUERY_FILE # Start with an uninteresting file so that we can `union` below. echo ' scripts/no_op_test.py' >> $QUERY_FILE # Use `union` to join the list of files. Add quotes to defend against # spaces. Note we can filter to the intersection of Carbon extensions # and the supported list at: # https://github.com/erenon/bazel_clang_tidy/blob/master/clang_tidy/clang_tidy.bzl#L65 for f in $(git diff --name-only --diff-filter=d \ $GIT_BASE_SHA -- '**/*.h' '**/*.cpp'); do echo " union '$f'" >> $QUERY_FILE done echo ')' >> $QUERY_FILE # Use query because cquery doesn't support `same_pkg_direct_rdeps`. ./scripts/run_bazel.py \ --attempts=5 \ query --query_file=$QUERY_FILE \ > $TARGETS_FILE ================================================ FILE: .github/pull_request_template.md ================================================ **Replace this paragraph with a description of what this PR is changing or adding, and why.** Closes #ISSUE ================================================ FILE: .github/release.yaml ================================================ # Part of the Carbon Language project, under the Apache License v2.0 with LLVM # Exceptions. See /LICENSE for license information. # SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception # # Provide the configuration and categorization for generated release notes. changelog: exclude: labels: - ignore-for-release categories: - title: 'Proposals accepted and merged :scroll:' labels: - proposal exclude.labels: - bot - title: 'Toolchain and implementation changes :hammer_and_wrench:' labels: - toolchain exclude.labels: - bot - title: 'Documentation changes :memo:' labels: - documentation exclude.labels: - bot - title: 'Utilities :triangular_ruler:' labels: - utilities exclude.labels: - bot - title: 'Infrastructure changes :building_construction:' labels: - infrastructure exclude.labels: - bot - title: 'Automated robot PRs :robot:' labels: - bot - title: 'Other changes' labels: - '*' ================================================ FILE: .github/workflows/README.md ================================================ # Workflows ## Hardening Workflows are hardened using [Step Security tool](https://app.stepsecurity.io/secureworkflow). Findings for the "Harden Runner" steps are [available online](https://app.stepsecurity.io/github/carbon-language/carbon-lang/actions/runs). ### Allowed endpoints Most jobs only have a few endpoints, but due to tools which do downloads, a few have significantly more. These are: - clangd_tidy.yaml (Bazel) - pre_commit.yaml (Bazel, pre-commit) - nightly_release.yaml (Bazel) - tests.yaml (Bazel) When updating one of these, consider updating all of them. We try to keep `allowed-endpoints` with one per line. Prettier wants to wrap them, which we fix this with `prettier-ignore`. ## Testing We keep around an `action-test` branch in carbon-lang, which can be used to test triggers with `push:` configurations. For example: ``` on: push: branches: [action-test] ``` ================================================ FILE: .github/workflows/auto_label_prs.yaml ================================================ # Part of the Carbon Language project, under the Apache License v2.0 with LLVM # Exceptions. See /LICENSE for license information. # SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception name: 'Auto label PRs' on: pull_request_target: types: [opened, ready_for_review] permissions: pull-requests: write # For gh to edit labels. # TODO: `--repo carbon-language/carbon-lang` is a temporary workaround for: # https://github.com/cli/cli/issues/11055 # Once a later version is released on runners, maybe August 2025, we should be # able to remove the extra flag. jobs: set_labels: runs-on: ubuntu-latest steps: - name: Harden Runner uses: step-security/harden-runner@58077d3c7e43986b6b15fba718e8ea69e387dfcc # v2.15.1 with: disable-sudo: true egress-policy: block # prettier-ignore allowed-endpoints: > api.github.com:443 - id: filter uses: dorny/paths-filter@de90cc6fb38fc0963ad72b210f1f284cd68cea36 # v3.0.2 with: filters: | documentation: - '*.md' - 'docs/**' - 'examples/**' - 'third_party/examples/**' infrastructure: - '*.bzl' - '*.cfg' - '*.toml' - '.*' - '.*/**' - 'BUILD' - 'MODULE.*' - 'WORKSPACE' - 'bazel/**' - 'github_tools/**' - 'proposal/scripts/**' - 'scripts/**' # Here we only want the `proposal` label when a *new* file is added # directly in this directory. We use `added` and a single level glob # to achieve that. proposal: - added: 'proposals/*' # We include common, shared code into the toolchain label for # convenience. Essentially, this is everything we intend to ship as # part of the reference implementation of the language. toolchain: - 'common/**' - 'core/**' - 'testing/**' - 'toolchain/**' utilities: - 'utils/**' - id: documentation if: steps.filter.outputs.documentation == 'true' run: | gh pr edit "${PR}" --add-label "documentation" --repo carbon-language/carbon-lang env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} PR: ${{ github.event.pull_request.html_url }} - id: infrastructure if: steps.filter.outputs.infrastructure == 'true' run: | gh pr edit "${PR}" --add-label "infrastructure" --repo carbon-language/carbon-lang env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} PR: ${{ github.event.pull_request.html_url }} - id: proposal if: steps.filter.outputs.proposal == 'true' run: | gh pr edit "${PR}" --add-label "proposal" --repo carbon-language/carbon-lang env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} PR: ${{ github.event.pull_request.html_url }} - id: toolchain if: steps.filter.outputs.toolchain == 'true' run: | gh pr edit "${PR}" --add-label "toolchain" --repo carbon-language/carbon-lang env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} PR: ${{ github.event.pull_request.html_url }} - id: utilities if: steps.filter.outputs.utilities == 'true' run: | gh pr edit "${PR}" --add-label "utilities" --repo carbon-language/carbon-lang env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} PR: ${{ github.event.pull_request.html_url }} # Note that this is not a path-based label, but an *author* based label, # and it applies orthogonally to the others. - id: automated if: contains(fromJSON('["CarbonInfraBot", "dependabot"]'), github.event.pull_request.user.login) run: | gh pr edit "${PR}" --add-label "automated" --repo carbon-language/carbon-lang env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} PR: ${{ github.event.pull_request.html_url }} ================================================ FILE: .github/workflows/clangd_tidy.yaml ================================================ # Part of the Carbon Language project, under the Apache License v2.0 with LLVM # Exceptions. See /LICENSE for license information. # SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception name: 'Clang Tidy (clangd)' on: push: branches: [trunk, action-test] pull_request: merge_group: permissions: contents: read # For actions/checkout. pull-requests: read # For dorny/paths-filter to read pull requests. # Cancel previous workflows on the PR when there are multiple fast commits. # https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#concurrency concurrency: group: ${{ github.workflow }}-${{ github.head_ref || github.ref }} cancel-in-progress: true jobs: clangd-tidy: runs-on: ubuntu-22.04 steps: - name: Harden Runner uses: step-security/harden-runner@58077d3c7e43986b6b15fba718e8ea69e387dfcc # v2.15.1 with: egress-policy: block # When adding endpoints, see README.md. # prettier-ignore allowed-endpoints: > *.blob.storage.azure.net:443 *.githubapp.com:443 *.sourceforge.net:443 api.github.com:443 api.ipify.org:443 bcr.bazel.build:443 downloads.sourceforge.net:443 files.pythonhosted.org:443 github.com:443 go.dev:443 mirror.bazel.build:443 mirrors.kernel.org:443 nodejs.org:443 oauth2.googleapis.com:443 objects.githubusercontent.com:443 pypi.org:443 registry.npmjs.org:443 release-assets.githubusercontent.com:443 releases.bazel.build:443 storage.googleapis.com:443 uploads.github.com:443 www.googleapis.com:443 - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - id: filter uses: dorny/paths-filter@de90cc6fb38fc0963ad72b210f1f284cd68cea36 # v3.0.2 with: filters: | has_cpp: - added|modified: '{**/*.cpp,**/*.h}' list-files: 'shell' - uses: ./.github/actions/build-setup-common if: steps.filter.outputs.has_cpp == 'true' with: matrix_runner: 'ubuntu-22.04' remote_cache_upload: '--remote_upload_local_results=false' - name: Create compile commands if: steps.filter.outputs.has_cpp == 'true' run: ./scripts/create_compdb.py - name: Build deps for clangd-tidy if: steps.filter.outputs.has_cpp == 'true' run: ./scripts/run_bazel.py build //scripts:deps_for_clangd_tidy - name: Install clangd-tidy if: steps.filter.outputs.has_cpp == 'true' run: pip install clangd-tidy==1.1.0.post2 - name: Run clangd-tidy if: steps.filter.outputs.has_cpp == 'true' env: FILTER_FILES: ${{ steps.filter.outputs.has_cpp_files }} run: | clangd-tidy -p . -j 10 $FILTER_FILES ================================================ FILE: .github/workflows/discord_wiki.yaml ================================================ # Part of the Carbon Language project, under the Apache License v2.0 with LLVM # Exceptions. See /LICENSE for license information. # SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception name: Discord Wiki Change Notifications on: gollum # Minimum permissions. permissions: contents: read jobs: notify: runs-on: ubuntu-latest steps: - name: Harden Runner uses: step-security/harden-runner@58077d3c7e43986b6b15fba718e8ea69e387dfcc # v2.15.1 with: egress-policy: audit - uses: oznu/gh-wiki-edit-discord-notification@1f5b688c27310fba606368b20469c81f5ffd9a2f # v1.0.0 with: discord-webhook-url: ${{ secrets.DISCORD_WEBHOOK_WIKI_EDIT }} ================================================ FILE: .github/workflows/gh_pages_ci.yaml ================================================ # Part of the Carbon Language project, under the Apache License v2.0 with LLVM # Exceptions. See /LICENSE for license information. # SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception name: GitHub Pages CI on: pull_request: # Cancel previous workflows on the PR when there are multiple fast commits. # https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#concurrency concurrency: group: ${{ github.workflow }}-${{ github.head_ref || github.ref }} cancel-in-progress: true permissions: contents: read jobs: # Build job build: runs-on: ubuntu-latest steps: - name: Harden Runner uses: step-security/harden-runner@58077d3c7e43986b6b15fba718e8ea69e387dfcc # v2.15.1 with: egress-policy: audit - name: Checkout uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - name: Prebuild actions run: ./website/prebuild.py - name: Setup Ruby uses: ruby/setup-ruby@6ca151fd1bfcfd6fe0c4eb6837eb0584d0134a0c # v1.290.0 with: # Runs 'bundle install' and caches installed gems automatically. bundler-cache: true # Increment this number if you need to re-download cached gems. cache-version: 0 - name: Build with Jekyll run: bundle exec jekyll build ================================================ FILE: .github/workflows/gh_pages_deploy.yaml ================================================ # Part of the Carbon Language project, under the Apache License v2.0 with LLVM # Exceptions. See /LICENSE for license information. # SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception name: GitHub Pages deploy on: # Runs on pushes targeting the default branch. push: branches: ['trunk'] # Allows you to run this workflow manually from the Actions tab. workflow_dispatch: # Cancel previous workflows on the PR when there are multiple fast commits. # https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#concurrency concurrency: group: ${{ github.workflow }}-${{ github.head_ref || github.ref }} cancel-in-progress: true # Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages. permissions: contents: read pages: write id-token: write jobs: build: runs-on: ubuntu-latest steps: - name: Harden Runner uses: step-security/harden-runner@58077d3c7e43986b6b15fba718e8ea69e387dfcc # v2.15.1 with: egress-policy: audit - name: Checkout uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - name: Prebuild actions run: ./website/prebuild.py - name: Setup Pages uses: actions/configure-pages@983d7736d9b0ae728b81ab479565c72886d7745b # v5.0.0 - name: Setup Ruby uses: ruby/setup-ruby@6ca151fd1bfcfd6fe0c4eb6837eb0584d0134a0c # v1.290.0 with: # Runs 'bundle install' and caches installed gems automatically. bundler-cache: true # Increment this number if you need to re-download cached gems. cache-version: 0 - name: Build with Jekyll env: JEKYLL_ENV: production run: | bundle exec jekyll build --verbose \ --source ./ \ --destination ./_site \ --baseurl "${{ steps.pages.outputs.base_path }}" - name: Upload artifact # Automatically uploads an artifact from the './_site' directory by # default. uses: actions/upload-pages-artifact@7b1f4a764d45c48632c6b24a0339c27f5614fb0b # v4.0.0 deploy: environment: name: github-pages url: ${{ steps.deployment.outputs.page_url }} runs-on: ubuntu-latest needs: build steps: - name: Harden Runner uses: step-security/harden-runner@58077d3c7e43986b6b15fba718e8ea69e387dfcc # v2.15.1 with: egress-policy: audit - name: Deploy to GitHub Pages id: deployment uses: actions/deploy-pages@d6db90164ac5ed86f2b6aed7e0febac5b3c0c03e # v4.0.5 ================================================ FILE: .github/workflows/nightly_release.yaml ================================================ # Part of the Carbon Language project, under the Apache License v2.0 with LLVM # Exceptions. See /LICENSE for license information. # SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception # # This workflow creates a GitHub "release" of a nightly build of the project. # # Note: This is just an initial rough attempt, there is a lot of future work # needed here. A brief summary of TODOs: # # - Configure a nice release notes template and switch to generating the title # and notes instead of hard coding them. # # - Do some amount of testing prior to building and uploading the release. # - Tempting to try to examine existing testing workflow, but maybe better to # allow reusing any complex parts and do our own testing. That would, for # example, allow us to narrow or expand the set of tests uses for # pre-release testing to potentially be different from continuous testing. # - Some questions around what to do in the event of a failure... error? Where # does the error go? Create a draft, unpublished release instead? # # - Build artifacts for all the different OSes we have GitHub runners for rather # than just x86 Linux. name: Nightly Release on: schedule: - cron: '0 2 * * *' # Enable manual runs for testing or manually (re-)creating a nightly release. workflow_dispatch: permissions: contents: write # For creating and uploading to releases. jobs: release: runs-on: ubuntu-22.04 steps: - name: Harden Runner uses: step-security/harden-runner@58077d3c7e43986b6b15fba718e8ea69e387dfcc # v2.15.1 with: egress-policy: block # When adding endpoints, see README.md. # prettier-ignore allowed-endpoints: > *.blob.storage.azure.net:443 *.githubapp.com:443 *.sourceforge.net:443 api.github.com:443 api.ipify.org:443 bcr.bazel.build:443 downloads.sourceforge.net:443 files.pythonhosted.org:443 github.com:443 go.dev:443 mirror.bazel.build:443 mirrors.kernel.org:443 nodejs.org:443 oauth2.googleapis.com:443 objects.githubusercontent.com:443 pypi.org:443 registry.npmjs.org:443 release-assets.githubusercontent.com:443 releases.bazel.build:443 storage.googleapis.com:443 uploads.github.com:443 www.googleapis.com:443 - name: Checkout branch uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - name: Set up remote cache access env: REMOTE_CACHE_KEY: ${{ secrets.CARBON_BUILDS_GITHUB }} run: | echo "$REMOTE_CACHE_KEY" | base64 -d > $HOME/remote_cache_key.json echo "remote_cache_upload=--google_credentials=$HOME/remote_cache_key.json" \ >> $GITHUB_ENV - uses: ./.github/actions/build-setup-common with: matrix_runner: ubuntu-22.04 remote_cache_upload: ${{ env.remote_cache_upload }} - name: Get nightly date run: | echo "nightly_date=$(date '+%Y.%m.%d')" >> $GITHUB_ENV - name: Build release run: | ./scripts/run_bazel.py \ --attempts=5 --jobs-on-last-attempt=4 \ test -c opt --stamp --remote_download_toplevel \ --pre_release=nightly --nightly_date=${{ env.nightly_date }} \ //toolchain \ //toolchain/install:carbon_toolchain_tar_gz_rule \ //toolchain/install:carbon_toolchain_tar_gz_test - name: Extract the release version run: | # Make sure we can run the toolchain to get the version. ./bazel-bin/toolchain/carbon version # Now stash it in a variable and export it. VERSION=$( \ ./bazel-bin/toolchain/carbon version \ | cut -d' ' -f5 | cut -d'+' -f1) echo "release_version=$VERSION" >> $GITHUB_ENV - name: Create the release env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: | gh release create \ --title "Nightly build ${{ env.nightly_date }}" \ --generate-notes \ --prerelease \ v${{ env.release_version }} \ "bazel-bin/toolchain/install/carbon_toolchain-${{ env.release_version }}.tar.gz" ================================================ FILE: .github/workflows/pre_commit.yaml ================================================ # Part of the Carbon Language project, under the Apache License v2.0 with LLVM # Exceptions. See /LICENSE for license information. # SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception name: pre-commit on: pull_request: merge_group: push: branches: [trunk] permissions: contents: read # For actions/checkout. jobs: pre-commit: runs-on: ubuntu-22.04 steps: - name: Harden Runner uses: step-security/harden-runner@58077d3c7e43986b6b15fba718e8ea69e387dfcc # v2.15.1 with: egress-policy: block # When adding endpoints, see README.md. # prettier-ignore allowed-endpoints: > *.blob.storage.azure.net:443 *.githubapp.com:443 *.sourceforge.net:443 api.github.com:443 api.ipify.org:443 bcr.bazel.build:443 downloads.sourceforge.net:443 files.pythonhosted.org:443 github.com:443 go.dev:443 mirror.bazel.build:443 mirrors.kernel.org:443 nodejs.org:443 oauth2.googleapis.com:443 objects.githubusercontent.com:443 pypi.org:443 registry.npmjs.org:443 release-assets.githubusercontent.com:443 releases.bazel.build:443 storage.googleapis.com:443 uploads.github.com:443 www.googleapis.com:443 - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 # Ensure LLVM is set up consistently. - uses: ./.github/actions/build-setup-common with: matrix_runner: ubuntu-22.04 remote_cache_upload: '--remote_upload_local_results=false' - uses: pre-commit/action@2c7b3805fd2a0fd8c1884dcaebf91fc102a13ecd # v3.0.1 # We want to automatically create github suggestions for pre-commit file # changes for a pull request. But `pull_request` actions never have write # permissions to the repository, so we create the suggestions in a separate # privileged `workflow_run` action in pre_commit_suggestions.yaml. Here, # we upload the diffs and event configuration to an artifact for use by # that action. - name: Collect pre-commit output if: failure() run: | mkdir -p pre-commit-output git diff > pre-commit-output/diff cp $GITHUB_EVENT_PATH pre-commit-output/event - uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0 if: failure() with: name: pre-commit output path: pre-commit-output/* ================================================ FILE: .github/workflows/pre_commit_suggestions.yaml ================================================ # Part of the Carbon Language project, under the Apache License v2.0 with LLVM # Exceptions. See /LICENSE for license information. # SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception # Create PR suggestions based on problems found by pre-commit action. name: 'Add pre-commit suggestions' # This action is run whenever the `pre-commit` action finishes. Because the # `pre-commit` action is an unprivileged action running on (for example) the # `pull_request` event, it's run without write permissions to the repository, so # we use a separate privileged `workflow_run` action here to pick up its results # and convert them into suggestion comments. # # This action is only run from the workflow file on the trunk branch. Changes to # this file will not take effect until they are merged to trunk. on: workflow_run: workflows: [pre-commit] types: - completed # Note reviewdog/reviewdog has its own token. permissions: contents: read # For actions/checkout. jobs: pull-request-suggestions: # Only generate suggestions if pre-commit for a PR failed. if: | github.event.workflow_run.conclusion == 'failure' && github.event.workflow_run.event == 'pull_request' && github.actor != 'jonmeow' runs-on: ubuntu-latest steps: - name: Harden Runner uses: step-security/harden-runner@58077d3c7e43986b6b15fba718e8ea69e387dfcc # v2.15.1 with: disable-sudo: true egress-policy: block # prettier-ignore allowed-endpoints: > api.github.com:443 github.com:443 objects.githubusercontent.com:443 raw.githubusercontent.com:443 - uses: reviewdog/action-setup@3f401fe1d58fe77e10d665ab713057375e39b887 # v1.3.0 with: reviewdog_version: latest - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - name: Download pre-commit output uses: actions/download-artifact@70fc10c6e5e1ce46ad2ea6f2b72d43f7d47b13c3 # v8.0.0 with: name: pre-commit output github-token: ${{ secrets.GITHUB_TOKEN }} run-id: ${{ github.event.workflow_run.id }} # Use https://github.com/reviewdog/reviewdog to create PR suggestions # matching the diff that pre-commit created. - name: Create suggestions env: REVIEWDOG_GITHUB_API_TOKEN: ${{ secrets.CARBON_INFRA_BOT_FOR_REVIEWDOG }} run: | cat ./diff | \ GITHUB_EVENT_PATH=./event \ reviewdog -f=diff -f.diff.strip=1 -reporter=github-pr-review ================================================ FILE: .github/workflows/proposal_labeled.yaml ================================================ # Part of the Carbon Language project, under the Apache License v2.0 with LLVM # Exceptions. See /LICENSE for license information. # SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception # Applies mutual exclusivity between states labels: # - proposal draft # - proposal rfc # - proposal accepted # - proposal declined # - proposal deferred # # The "proposal" label is always applied in order to make searching for all # proposals (regardless of state) easy, although it will typically already be # present. name: Proposal labeled on: pull_request_target: types: - labeled permissions: pull-requests: write # For gh to edit labels. # TODO: `--repo carbon-language/carbon-lang` is a temporary workaround for: # https://github.com/cli/cli/issues/11055 # Once a later version is released on runners, maybe August 2025, we should be # able to remove the extra flag. jobs: proposal_labeled: runs-on: ubuntu-latest steps: - name: Harden Runner uses: step-security/harden-runner@58077d3c7e43986b6b15fba718e8ea69e387dfcc # v2.15.1 with: disable-sudo: true egress-policy: block # prettier-ignore allowed-endpoints: > api.github.com:443 - name: draft if: | github.event.label.name == 'proposal draft' run: | gh pr edit "${PR}" \ --remove-label "proposal rfc" \ --remove-label "proposal accepted" \ --remove-label "proposal declined" \ --remove-label "proposal deferred" \ --add-label "proposal" \ --repo carbon-language/carbon-lang env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} PR: ${{ github.event.pull_request.html_url }} - name: rfc if: | github.event.label.name == 'proposal rfc' run: | gh pr edit "${PR}" \ --remove-label "proposal draft" \ --remove-label "proposal accepted" \ --remove-label "proposal declined" \ --remove-label "proposal deferred" \ --add-label "proposal" \ --repo carbon-language/carbon-lang env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} PR: ${{ github.event.pull_request.html_url }} - name: accepted if: | github.event.label.name == 'proposal accepted' run: | gh pr edit "${PR}" \ --remove-label "proposal draft" \ --remove-label "proposal rfc" \ --remove-label "proposal declined" \ --remove-label "proposal deferred" \ --add-label "proposal" \ --repo carbon-language/carbon-lang env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} PR: ${{ github.event.pull_request.html_url }} - name: declined if: | github.event.label.name == 'proposal declined' run: | gh pr edit "${PR}" \ --remove-label "proposal draft" \ --remove-label "proposal rfc" \ --remove-label "proposal accepted" \ --remove-label "proposal deferred" \ --add-label "proposal" \ --repo carbon-language/carbon-lang env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} PR: ${{ github.event.pull_request.html_url }} - name: deferred if: | github.event.label.name == 'proposal deferred' run: | gh pr edit "${PR}" \ --remove-label "proposal draft" \ --remove-label "proposal rfc" \ --remove-label "proposal accepted" \ --remove-label "proposal declined" \ --add-label "proposal" \ --repo carbon-language/carbon-lang env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} PR: ${{ github.event.pull_request.html_url }} ================================================ FILE: .github/workflows/proposal_ready.yaml ================================================ # Part of the Carbon Language project, under the Apache License v2.0 with LLVM # Exceptions. See /LICENSE for license information. # SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception # Applies the "proposal rfc" label when a proposal is marked ready for review. name: Proposal ready for review on: pull_request_target: types: - ready_for_review permissions: pull-requests: write # For gh to edit labels. # TODO: `--repo carbon-language/carbon-lang` is a temporary workaround for: # https://github.com/cli/cli/issues/11055 # Once a later version is released on runners, maybe August 2025, we should be # able to remove the extra flag. jobs: proposal_ready: if: contains(github.event.pull_request.labels.*.name, 'proposal') runs-on: ubuntu-latest steps: - name: Harden Runner uses: step-security/harden-runner@58077d3c7e43986b6b15fba718e8ea69e387dfcc # v2.15.1 with: disable-sudo: true egress-policy: block # prettier-ignore allowed-endpoints: > api.github.com:443 - name: rfc run: | gh pr edit "${PR}" \ --remove-label "proposal draft" \ --remove-label "proposal accepted" \ --remove-label "proposal declined" \ --remove-label "proposal deferred" \ --add-label "proposal rfc" \ --repo carbon-language/carbon-lang env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} PR: ${{ github.event.pull_request.html_url }} ================================================ FILE: .github/workflows/sync_repos.yaml ================================================ # Part of the Carbon Language project, under the Apache License v2.0 with LLVM # Exceptions. See /LICENSE for license information. # SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception name: Sync repos on: push: branches: [trunk] paths: # Minimize where we run this to changes to the top-level files and # specific trees that we sync to other repositories. - '*' - 'utils/**' # Also run if the action itself is updated. - '.github/workflows/sync_repos.yaml' - 'scripts/sync_repos.sh' # Note the sync script has its own token. permissions: contents: read # For actions/checkout. jobs: sync-repos: runs-on: ubuntu-latest steps: - name: Harden Runner uses: step-security/harden-runner@58077d3c7e43986b6b15fba718e8ea69e387dfcc # v2.15.1 with: egress-policy: audit # Checkout our main repository. - name: Checkout the main repository uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 # Run the sync script. - name: Sync to other repositories env: API_TOKEN_GITHUB: ${{ secrets.SYNC_REPOS_API_TOKEN_GITHUB }} run: ./scripts/sync_repos.sh ================================================ FILE: .github/workflows/tests.yaml ================================================ # Part of the Carbon Language project, under the Apache License v2.0 with LLVM # Exceptions. See /LICENSE for license information. # SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception name: Tests on: push: branches: [trunk, action-test] pull_request: merge_group: permissions: contents: read # For actions/checkout. pull-requests: read # For dorny/paths-filter to read pull requests. # Cancel previous workflows on the PR when there are multiple fast commits. # https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#concurrency concurrency: group: ${{ github.workflow }}-${{ github.head_ref || github.ref }} cancel-in-progress: true jobs: test: strategy: matrix: # Test a recent version of each supported OS. runner: ['ubuntu-22.04', 'macos-14'] build_mode: [fastbuild, opt] runs-on: ${{ matrix.runner }} steps: - name: Harden Runner uses: step-security/harden-runner@58077d3c7e43986b6b15fba718e8ea69e387dfcc # v2.15.1 with: egress-policy: block # When adding endpoints, see README.md. # prettier-ignore allowed-endpoints: > *.blob.storage.azure.net:443 *.githubapp.com:443 *.sourceforge.net:443 api.github.com:443 api.ipify.org:443 bcr.bazel.build:443 downloads.sourceforge.net:443 files.pythonhosted.org:443 github.com:443 go.dev:443 mirror.bazel.build:443 mirrors.kernel.org:443 nodejs.org:443 oauth2.googleapis.com:443 objects.githubusercontent.com:443 pypi.org:443 registry.npmjs.org:443 release-assets.githubusercontent.com:443 releases.bazel.build:443 storage.googleapis.com:443 uploads.github.com:443 www.googleapis.com:443 - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - id: test-setup uses: ./.github/actions/test-setup with: matrix_runner: ${{ matrix.runner }} base_sha: ${{ github.event_name == 'pull_request' && github.event.pull_request.base.sha || github.event.merge_group.base_sha }} remote_cache_key: ${{ secrets.CARBON_BUILDS_GITHUB }} targets_file: ${{ runner.temp }}/targets # Build and run just the tests impacted by the PR or merge group. - name: Test (${{ matrix.build_mode }}) if: steps.test-setup.outputs.has_code == 'true' shell: bash env: # 'libtool_check_unique failed to generate' workaround. # https://github.com/bazelbuild/bazel/issues/14113#issuecomment-999794586 BAZEL_USE_CPP_ONLY_TOOLCHAIN: 1 TARGETS_FILE: ${{ runner.temp }}/targets run: | # Decrease the jobs sharply if we see repeated failures to try to # work around transient network errors even if it makes things # slower. ./scripts/run_bazel.py \ --attempts=5 --jobs-on-last-attempt=4 \ test -c ${{ matrix.build_mode }} \ --target_pattern_file=$TARGETS_FILE # See "Disk space before build" in `test-setup`. - name: Disk space after build if: steps.test-setup.outputs.has_code == 'true' run: df -h ================================================ FILE: .github/workflows/triage_inactive.yaml ================================================ # Part of the Carbon Language project, under the Apache License v2.0 with LLVM # Exceptions. See /LICENSE for license information. # SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception name: 'Triage inactive issues and PRs' on: schedule: - cron: '30 1 * * *' permissions: issues: write # For actions/stale to close stale issues. pull-requests: write # For actions/stale to close stale PRs. jobs: stale: runs-on: ubuntu-latest steps: - name: Harden Runner uses: step-security/harden-runner@58077d3c7e43986b6b15fba718e8ea69e387dfcc # v2.15.1 with: disable-sudo: true egress-policy: block # prettier-ignore allowed-endpoints: > api.github.com:443 - uses: actions/stale@28ca1036281a5e5922ead5184a1bbf96e5fc984e # v9.0.0 with: stale-issue-message: > We triage inactive PRs and issues in order to make it easier to find active work. If this issue should remain active or becomes active again, please comment or remove the `inactive` label. The `long term issue` label can also be added for issues which are expected to take time. This issue is labeled `inactive` because the last activity was over 90 days ago. stale-pr-message: > We triage inactive PRs and issues in order to make it easier to find active work. If this PR should remain active, please comment or remove the `inactive` label. This PR is labeled `inactive` because the last activity was over 90 days ago. This PR will be closed and archived after 14 additional days without activity. close-pr-message: > We triage inactive PRs and issues in order to make it easier to find active work. If this PR should remain active or becomes active again, please reopen it. This PR was closed and archived because there has been no new activity in the 14 days since the `inactive` label was added. stale-issue-label: 'inactive' stale-pr-label: 'inactive' exempt-issue-labels: 'long term issue,design idea,design update,good first issue,leads question' days-before-stale: 90 days-before-close: 14 days-before-issue-close: -1 operations-per-run: 100 ================================================ FILE: .gitignore ================================================ # Part of the Carbon Language project, under the Apache License v2.0 with LLVM # Exceptions. See /LICENSE for license information. # SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception # Directories and symlinks generated by bazel. /bazel-* # See github_tools/MODULE.bazel. /github_tools/bazel-* /github_tools/MODULE.bazel.lock # We also have example Bazel projects that shouldn't have their implementation # details committed. /examples/**/bazel-* /examples/**/MODULE.bazel.lock # Directories created by python. **/__pycache__/ # Ignore the user's VSCode settings and debug setup. /.vscode/settings.json /.vscode/launch.json # Ignore the user's Idea settings. /.idea/ # Directories created by clangd /.cache/clangd # User-specific Bazel configuration. /user.bazelrc # Compilation database used by clangd /compile_commands.json # Emacs temporary files *~ \#*\# # vim temporary files .*.sw[a-p] .swp # generated by utils/tree_sitter/helix.sh /.helix/ # Ignore .DS_Store files .DS_Store # Ignore the .gdb_history that's created next to the project-specific .gdbinit .gdb_history ================================================ FILE: .lldbinit ================================================ # Part of the Carbon Language project, under the Apache License v2.0 with LLVM # Exceptions. See /LICENSE for license information. # SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception command script import external/+llvm_project+llvm-project/llvm/utils/lldbDataFormatters.py command script import scripts/lldbinit.py settings set escape-non-printables false settings set target.max-string-summary-length 10000 ================================================ FILE: .pre-commit-config.yaml ================================================ # Part of the Carbon Language project, under the Apache License v2.0 with LLVM # Exceptions. See /LICENSE for license information. # SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception # Update versions with: # pre-commit autoupdate --freeze && pre-commit run -a # See https://pre-commit.com for more information # See https://pre-commit.com/hooks.html for more hooks default_language_version: python: python3 # Defaults to python2, so override it. repos: - repo: https://github.com/pre-commit/pre-commit-hooks rev: 3e8a8703264a2f4a69428a0aa4dcb512790b2c8c # frozen: v6.0.0 hooks: - id: check-added-large-files - id: check-case-conflict - id: check-executables-have-shebangs - id: check-merge-conflict - id: check-symlinks exclude: ^bazel-execroot$ - id: check-yaml - id: detect-private-key - id: end-of-file-fixer exclude: '^(.*/fuzzer_corpus/.*|.*\.svg)$' - id: mixed-line-ending args: ['--fix=lf'] exclude: '^(.*/fuzzer_corpus/.*|.*\.svg)$' - id: trailing-whitespace exclude: '^(.*/fuzzer_corpus/.*|.*/testdata/.*\.golden|.*\.svg)$' - repo: https://github.com/google/pre-commit-tool-hooks rev: efaea7c61c774c0b1a9805fd999e754a2d19dbd1 # frozen: v1.2.5 hooks: - id: check-google-doc-style - id: markdown-toc - repo: local hooks: - id: fix-cc-deps name: Fix missing C++ deps entry: scripts/fix_cc_deps.py language: python files: ^.*/(BUILD|[^/]+\.(h|cpp))$ pass_filenames: false # Formatters should be run late so that they can re-format any prior changes. - repo: https://github.com/psf/black rev: 35ea67920b7f6ac8e09be1c47278752b1e827f76 # frozen: 26.3.0 hooks: - id: black - repo: local hooks: - id: prettier name: prettier language: node # TODO: Not upgrading to/past 3.4.0 due to list indent changes that may # get fixed. See: https://github.com/prettier/prettier/issues/16929 additional_dependencies: ['prettier@3.3.3'] types_or: [html, javascript, json, markdown, yaml] entry: npx prettier@3.3.3 --write --log-level=warn - repo: local hooks: - id: buildifier name: Bazel buildifier entry: scripts/run_buildifier.py # Beyond just formatting, explicitly fix lint warnings. args: ['--lint=fix', '--warnings=all', '-r', '.'] language: python files: | (?x)^( .*BUILD.*| .*MODULE.bazel.*| .*WORKSPACE.*| .*\.bzl )$ - id: check-bazel-mod-deps # Check this after buildifier because buildifier may modify inputs, and # MODULE.bazel.lock includes line/column details. name: Check bazel mod deps entry: scripts/bazel_mod_deps.py language: python files: | (?x)^( .*MODULE.bazel.*| .*WORKSPACE.* )$ - id: clang-format name: clang-format entry: clang-format types_or: [c++, def] language: python args: ['-i'] additional_dependencies: ['clang-format==21.1.8'] - repo: local hooks: - id: check-header-guards # This should run after clang-format, which may reformat a guard. name: Check header guards entry: scripts/check_header_guards.py language: python files: ^.*\.h$ - id: check-sha-filenames # This may rename files, so it's deliberately between formatters and # linters. name: Check fuzzer SHA filenames entry: scripts/check_sha_filenames.py language: python files: ^.*/fuzzer_corpus/.*$ - id: check-toolchain-diagnostics name: Check toolchain diagnostics entry: toolchain/diagnostics/check_diagnostics.py language: python files: | (?x)^( toolchain/.*\.cpp| toolchain/.*\.h| toolchain/diagnostics/check_diagnostics\.py| toolchain/diagnostics/diagnostic_kind\.def )$ pass_filenames: false # Run linters last, as formatters and other checks may fix issues. - repo: local hooks: - id: forbid-llvm-googletest name: Checks for deps on LLVM's version of GoogleTest entry: scripts/forbid_llvm_googletest.py language: python files: ^.*/BUILD$ pass_filenames: false - repo: https://github.com/PyCQA/flake8 rev: d93590f5be797aabb60e3b09f2f52dddb02f349f # frozen: 7.3.0 hooks: - id: flake8 - repo: https://github.com/pre-commit/mirrors-mypy rev: 'a66e98df7b4aeeb3724184b332785976d062b92e' # frozen: v1.19.1 hooks: - id: mypy # Use setup.cfg to match the command line. args: - --config-file=setup.cfg # This should match the requirements added in the WORKSPACE pip_install. additional_dependencies: - gql >= 2.0.0, < 3.0.0 - PyGitHub - rich # Exclusions are: # - p#### scripts because they're not tested or maintained. # - lit.cfg.py because it has multiple copies, breaking mypy. # - `bazel_test_runner.py` which depends on Bazel-specific imports. # - Unit tests because they sometimes violate typing, such as by # assigning a mock to a function. exclude: | (?x)^( proposals/(?!scripts/).*| .*/lit\.cfg\.py| examples/bazel_test_runner\.py| .*_test\.py )$ - repo: https://github.com/codespell-project/codespell rev: 2ccb47ff45ad361a21071a7eedda4c37e6ae8c5a # frozen: v2.4.2 hooks: - id: codespell args: ['-I', '.codespell_ignore', '--uri-ignore-words-list', '*'] # Test data may contain intentional misspellings, as well as short, # meaningless identifiers that codespell incorrectly identifies as # typos but that we would want to detect in other contexts. exclude: | (?x)^( .*/testdata/.*| .*/fuzzer_corpus/.* )$ - repo: https://github.com/google/pre-commit-tool-hooks rev: efaea7c61c774c0b1a9805fd999e754a2d19dbd1 # frozen: v1.2.5 hooks: - id: check-copyright args: - --copyright - |+ Part of the Carbon Language project, under the Apache License v2.0 with LLVM Exceptions. See /LICENSE for license information. SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - --custom_format - '\.(carbon|c|json|scss|ypp)(\.tmpl)?$' - '' - '// ' - '' - --custom_format - '\.(js|ts|mjs)$' - '/*' - ' * ' - ' */' - --custom_format - '\.(l|lpp|y)$' - '/*' - '' - '*/' - --custom_format - '\.(plist)$' - '' - --custom_format - '\.vim$' - '' - '" ' - '' - --custom_format - '\.scm$' - '' - '; ' - '' - --custom_format - '\.lua$' - '' - '-- ' - '' exclude: | (?x)^( .bazelversion| .github/pull_request_template.md| .python-version| compile_flags.txt| github_tools/requirements.txt| third_party/.*| utils/vscode/esbuild.js| website/.ruby-version| website/Gemfile.lock| .*\.def| .*\.png| .*\.svg| .*/fuzzer_corpus/.*| .*/testdata/.*\.golden )$ - id: check-links - repo: local hooks: - id: check-build-graph name: Check build graph entry: scripts/check_build_graph.py language: python files: | (?x)^( .*BUILD.*| .*MODULE.bazel.*| .*WORKSPACE.*| .*\.bzl )$ # This excludes third-party code, and patches to third-party code. exclude: | (?x)^( MODULE.bazel.lock| bazel/bazel_clang_tidy/.*\.patch| bazel/google_benchmark/.*\.patch| bazel/libpfm/.*\.patch| bazel/llvm_project/.*\.patch| third_party/examples/.*/carbon/.*| )$ ================================================ FILE: .prettierrc.yaml ================================================ # Part of the Carbon Language project, under the Apache License v2.0 with LLVM # Exceptions. See /LICENSE for license information. # SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception printWidth: 80 proseWrap: 'always' singleQuote: true tabWidth: 2 trailingComma: 'es5' useTabs: false overrides: - files: '**/*.md' options: tabWidth: 4 ================================================ FILE: .python-version ================================================ 3.10 ================================================ FILE: .vscode/extensions.json ================================================ { "recommendations": [ "bazelbuild.vscode-bazel", "bierner.github-markdown-preview", "carbon-lang.carbon-vscode", "esbenp.prettier-vscode", "llvm-vs-code-extensions.vscode-clangd", "ms-python.black-formatter", "ms-python.python" ] } ================================================ FILE: .vscode/gdb_launch.json ================================================ { "version": "0.2.0", "configurations": [ { "type": "by-gdb", "request": "launch", "name": "file_test (gdb)", "program": "bazel-bin/toolchain/testing/file_test", "programArgs": "--file_tests=${relativeFile}", "cwd": "${workspaceFolder}", "env": { "TEST_TARGET": "//toolchain/testing:file_test", "TEST_TMPDIR": "/tmp" } }, { "type": "by-gdb", "request": "launch", "name": "carbon compile (gdb)", "program": "bazel-bin/toolchain/carbon", "programArgs": "compile --phase=lower --dump-sem-ir --stream-errors ${relativeFile}", "cwd": "${workspaceFolder}" } ] } ================================================ FILE: .vscode/lldb_launch.json ================================================ { "version": "0.2.0", "configurations": [ { "type": "lldb-dap", "request": "launch", "name": "file_test (lldb)", "program": "bazel-bin/toolchain/testing/file_test", "args": ["--file_tests=${relativeFile}"], "debuggerRoot": "${workspaceFolder}", "initCommands": [ "command script import external/+llvm_project+llvm-project/llvm/utils/lldbDataFormatters.py", "command script import scripts/lldbinit.py", "settings append target.source-map \".\" \"${workspaceFolder}\"", "settings append target.source-map \"/proc/self/cwd\" \"${workspaceFolder}\"", "settings set escape-non-printables false", "settings set target.max-string-summary-length 10000", "env TEST_TARGET=//toolchain/testing:file_test", "env TEST_TMPDIR=/tmp" ] }, { "type": "lldb-dap", "request": "launch", "name": "carbon compile (lldb)", "program": "bazel-bin/toolchain/carbon", "args": [ "compile", "--phase=lower", "--dump-sem-ir", "--stream-errors", "${relativeFile}" ], "debuggerRoot": "${workspaceFolder}", "initCommands": [ "command script import external/+llvm_project+llvm-project/llvm/utils/lldbDataFormatters.py", "command script import scripts/lldbinit.py", "settings append target.source-map \".\" \"${workspaceFolder}\"", "settings append target.source-map \"/proc/self/cwd\" \"${workspaceFolder}\"", "settings set escape-non-printables false", "settings set target.max-string-summary-length 10000" ] } ] } ================================================ FILE: .vscode/tasks.json ================================================ { // See https://go.microsoft.com/fwlink/?LinkId=733558 // for the documentation about the tasks.json format "version": "2.0.0", "tasks": [ { "label": "saveAll", "command": "${command:workbench.action.files.saveAll}" }, { "label": "autoupdate: toolchain tests", "type": "process", "command": "toolchain/autoupdate_testdata.py", "group": "build", "dependsOn": ["saveAll"], "dependsOrder": "sequence", "presentation": { "echo": false, "panel": "dedicated", "showReuseMessage": false, "clear": true }, "problemMatcher": { "owner": "cpp", "fileLocation": ["relative", "${workspaceFolder}"], "source": "autoupdate", "pattern": { "regexp": "^(.*):(\\d+):(\\d+):\\s+(warning|error):\\s+(.*)$", "file": 1, "line": 2, "column": 3, "severity": 4, "message": 5 } } } ] } ================================================ FILE: BUILD ================================================ # Part of the Carbon Language project, under the Apache License v2.0 with LLVM # Exceptions. See /LICENSE for license information. # SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception filegroup( name = "clang_tidy_config", srcs = [".clang-tidy"], visibility = ["//visibility:public"], ) # `bazel run //:generate_compile_commands` to produce `compile_commands.json`. alias( name = "generate_compile_commands", actual = "@wolfd_bazel_compile_commands//:generate_compile_commands", ) ================================================ FILE: CODEOWNERS ================================================ # Part of the Carbon Language project, under the Apache License v2.0 with LLVM # Exceptions. See /LICENSE for license information. # SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception # This file is only used for PR autoassignment. Branch protections don't enforce # it. # # Syntax: # https://docs.github.com/en/repositories/managing-your-repositorys-settings-and-features/customizing-your-repository/about-code-owners#codeowners-syntax # Toolchain reviewers are used as a fallback. * @carbon-language/toolchain-reviewers # Key project documents should be reviewed by leads. /*.md @carbon-language/leads /LICENSE @carbon-language/leads /docs/project/evolution.md @carbon-language/leads /docs/project/goals.md @carbon-language/leads /docs/project/principles/* @carbon-language/leads /docs/project/roadmap.md @carbon-language/leads /proposals/*.md @carbon-language/leads # Toolchain code. /toolchain @carbon-language/toolchain-reviewers ================================================ FILE: CODE_OF_CONDUCT.md ================================================ # Code of conduct ## Table of contents - [Guidelines](#guidelines) - [Conduct team](#conduct-team) - [Reporting conduct](#reporting-conduct) - [Filing a report](#filing-a-report) - [What happens after contacting the conduct team?](#what-happens-after-contacting-the-conduct-team) - [Appealing](#appealing) - [Special cases](#special-cases) - [Enforcement action guidelines](#enforcement-action-guidelines) - [Acknowledgements](#acknowledgements) ## Guidelines The Carbon community works to be welcoming and kind among itself and to others, with a deep commitment to psychological safety, and we want to ensure that doesn’t change as we grow and evolve. To that end, we have a few ground rules that we ask all community members to adhere to: - be welcoming, - be friendly and patient, - be considerate, - be kind, - be careful in the words that we choose, - when we disagree, try to understand why, and - recognize when progress has stopped, and take a step back. This list isn't exhaustive. Rather, take it in the spirit in which it’s intended -- a guide to make it easier to communicate and participate in the community. This code of conduct applies to all spaces managed by the Carbon project. This includes chat systems, forums, emails (on lists or between members), issue trackers, events, and any other spaces that the community uses for communication. It applies to all of your communication and conduct in these spaces, including emails, chats, things you say, slides, videos, posters, signs, or even t-shirts you display in these spaces. All community members should help support our standards of acceptable behavior. Everyone is encouraged to speak up in response to any behavior that they deem inappropriate, threatening, offensive, or harmful. If you believe someone is violating the code of conduct, please report it to the [conduct team](#conduct-team). More detailed guidance on how to participate effectively in our community spaces: - **Be welcoming.** We strive to be a community that welcomes and supports people of all backgrounds and identities. This includes, but is not limited to, members of any race, ethnicity, culture, national origin, color, immigration status, social and economic class, educational level, sex, sexual orientation, gender identity and expression, physical appearance, age, size, family status, relationship status, political belief, religion or lack thereof, and mental and physical ability. - **Be friendly and patient.** We want to encourage people to participate in our community in a constructive manner, so we can keep a friendly atmosphere. This is especially important because many of our communication tools on the Internet are low-fidelity and make it difficult to understand each other. Be patient, acknowledge that we are all on a learning journey, and stay supportive so that we can learn how to collaborate effectively as a group. - **Be considerate.** Your work will be used by other people, and you in turn will depend on the work of others. Any decision you make will affect users and colleagues, and you should take those consequences into account. Remember that we’re a world-wide community, so you might not be communicating in someone else’s primary language. - **Be kind.** Not all of us will agree all the time, but disagreement is no excuse for poor behavior and hurtful words. We might all experience some frustration now and then, but we cannot allow that frustration to turn into a personal attack. It’s important to remember that a community where people feel threatened is not a productive one. Members of our community should be kind when dealing with other members as well as with people outside the Carbon community. - **Be careful in the words that we choose and be kind to others.** Do not use insults or put downs. Harassment and other exclusionary behaviors aren’t acceptable. This includes, but is not limited to: - Violent threats or language directed against another person. - Discriminatory jokes and language. - Posting sexually explicit or violent material. - Posting, or threatening to post, other people’s personally identifying information without their explicit permission ("doxing"). - Personal insults, especially those using racist or sexist terms. - Unwelcome sexual attention. - Advocating for, or encouraging, any of the above behavior. - In general, if someone asks you to stop, then stop. Persisting after being asked to stop is considered harassment. - **When we disagree, we try to understand why.** Disagreements, both social and technical, happen all the time, and Carbon is no exception. It is important that we resolve disagreements and differing views constructively. Remember that we’re different. The strength of the project comes from its varied community: people from a wide range of backgrounds. Different people have different perspectives on issues. Being unable to understand why someone holds a viewpoint doesn’t mean that they’re wrong. Don’t forget that it is human to err and blaming each other doesn’t get us anywhere. Instead, focus on helping to resolve issues and learning from mistakes. - **Recognize when progress has stopped, and take a step back.** Regardless of whether you're trying to resolve a disagreement or anything else, think about whether you're making progress. Sometimes messaging doesn't give time to think about a situation fully, and repeating positions can make people defensive. Step back for a few minutes or hours to think through the issue before responding again. Consider pulling in another community member to give a fresh perspective. Maybe meet over VC instead. Switching approaches can help resume progress. No weapons are allowed at Carbon events. Weapons include, but are not limited to, explosives (including fireworks), guns, and large knives such as those used for hunting or display, as well as any other item used for the purpose of causing injury or harm to others. In rare cases, violations of this code _outside_ of these spaces may affect, and be detrimental to, a person’s ability to participate _within_ these spaces. Important examples include, but are not limited to, [sexual and gender-based violence and/or harassment](https://hr.un.org/sites/hr.un.org/files/SEA%20Glossary%20%20%5BSecond%20Edition%20-%202017%5D%20-%20English_0.pdf), [hate crimes](https://hatecrime.osce.org/), and [hate speech](https://www.un.org/en/genocideprevention/documents/UN%20Strategy%20and%20Plan%20of%20Action%20on%20Hate%20Speech%2018%20June%20SYNOPSIS.pdf). Although we do not conduct proactive research, we have an obligation to respond to reported and, to the extent possible, corroborated concerns. Our motivations are not rooted in responding punitively, or holding people accountable. Instead, our response will be focused on how the continued participation of the person at issue could impact the community's safety, well-being, and inclusivity. It is our priority to remain a welcoming community to victims as well as groups subjected to systemic marginalization or underrepresentation. If you have questions, please feel free to ask on Discord, [GitHub](https://github.com/carbon-language/carbon-lang/discussions), or contact any member of the conduct team directly. ## Conduct team The conduct team can be emailed at conduct@carbon-lang.dev. More details about the team, its current members and its management are on the [conduct team page](/docs/project/teams/conduct_team.md). ### Reporting conduct If you believe someone is violating the code of conduct, you can report it several ways, including: - On [Discord](https://discord.gg/ZjVdShJDAs), DM ModMail. It may require confirmation that you want help on the Carbon Language server. An available moderator will respond. - Look for ModMail in the user list under "Bots", and click the name to send a direct message. - Moderators can also be found and messaged similarly, but ModMail is preferred. - Emailing the [conduct team](#conduct-team) directly at conduct@carbon-lang.dev. Bear in mind that [responses may take time](#what-happens-after-contacting-the-conduct-team). **All reports will be kept confidential**, and are only used by the conduct team to address conduct issues and keep the Carbon community safe and inclusive. Please send reports concerning a member of the conduct team directly to other members of the conduct team. This allows discussion of the complaint to more easily exclude the concerned member as a [special case](#special-cases). If you believe anyone is in **physical danger**, please notify appropriate law enforcement first. If you are unsure what law enforcement agency is appropriate, please include this in your report and we will attempt to notify them. If the violation occurs at an event and requires immediate attention, you can also reach out to any of the event organizers or staff. Event organizers and staff will be prepared to handle the incident and be able to help. If you cannot find one of the organizers, the venue staff can locate one for you. Specific event information will include detailed contact information for that event. In person reports will still be kept confidential exactly as above, but also feel free to email the conduct team, anonymously if needed. ### Filing a report Reports can be as formal or informal as needed for the situation at hand. If possible, please include as much information as you can. If you feel comfortable, please consider including: - Your contact info, so we can get in touch with you if we need to follow up. - Names -- real, nicknames, or pseudonyms -- of any individuals involved. If there were other witnesses besides you, please try to include them as well. - When and where the incident occurred. Please be as specific as possible. - Your account of what occurred, including any private chat logs or email. - Links for any public records, including community discussions. - Any extra context for the incident. - Whether you believe this incident is ongoing. - Any other information you believe we should have. ### What happens after contacting the conduct team? You will receive a reply from the conduct team acknowledging receipt within 1 business day, and we will aim to respond much quicker than that. The conduct team will review the incident as soon as possible and try to determine: - What happened and who was involved. - Whether this event constitutes a code of conduct violation. - Whether this is an ongoing situation, or if there is a threat to anyone’s physical safety. If this is determined to be an ongoing incident or a threat to physical safety, the conduct team's immediate priority will be to protect everyone involved. This means we may delay an "official" response until we believe that the situation has ended and that everyone is physically safe. The conduct team will try to contact other parties involved or witnessing the event to gain clarity on what happened and understand any different perspectives. Once the conduct team has a complete account of the events they will make a decision as to how to respond. Responses may include: - Nothing, if no violation occurred or it has already been appropriately resolved. - One or more [enforcement actions](#enforcement-action-guidelines). - Involvement of relevant law enforcement if appropriate. If the situation is not resolved within one week, we’ll respond to the original reporter with an update and explanation. Once we’ve determined our response, we will separately contact the original reporter and other individuals to let them know what actions, if any, we’ll be taking. We will take into account feedback from the individuals involved on the appropriateness of our response, but we don’t guarantee we’ll act on it. After any incident, the conduct team will make a report on the situation to the Carbon leads. The Carbon leads may choose to make a public statement about the incident. If that’s the case, the identities of anyone involved will remain confidential unless instructed otherwise by those individuals. ### Appealing Only permanent resolutions, such as bans, or requests for public actions may be appealed. To appeal a decision of the conduct team, contact the [Carbon leads](docs/project/evolution.md#carbon-leads-1) with your appeal and they will review the case. In general, it is **not** appropriate to appeal a particular decision in public areas of GitHub or Discord. Doing so would involve disclosure of information which should remain confidential. Disclosing this kind of information publicly may be considered a separate and, potentially, more serious violation of the Code of Conduct. This is not meant to limit discussion of the Code of Conduct, the conduct team itself, or the appropriateness of responses in general, but **please** refrain from mentioning specific facts about cases without the explicit permission of all parties involved. ### Special cases If a complaint is raised against a member of the conduct team or Carbon leads, they will be excluded from conduct discussion and decisions, including appeals. They will only be included as needed for an involved party, such as to get their perspective or to notify them of decisions. Such complaints may lead to a member's responsibilities being revoked. ## Enforcement action guidelines The conduct team, moderators, and event organizers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct. They will communicate reasons for moderation decisions when appropriate. The conduct team may take additional action they deem appropriate for any violation of this Code of Conduct using these guidelines based on the behavior involved: 1. **Correction** - **Behavior:** Use of inappropriate language or other minor violations of the code of conduct. - **Action:** A private, written message providing clarity around the nature of the violation and an explanation of why the behavior was inappropriate. A public apology may be requested. 1. **Warning** - **Behavior:** A code of conduct violation through a single moderate incident, or a series of minor violations. - **Action:** In addition to the correction action, a temporary restriction forbidding interaction with the people involved for a specified period of time, including unsolicited interaction with the conduct team. Violating these terms may lead to a ban. 1. **Temporary ban** - **Behavior:** A serious violation of the code of conduct, including sustained inappropriate behavior. - **Action:** In addition to the warning action, a temporary ban from use of Carbon's community spaces for a specified period of time. External channels, such as social media, should not be used to bypass these restrictions during the temporary ban. Violating these terms may lead to a permanent ban. 1. **Permanent ban** - **Behavior:** Demonstrating a pattern of violation of the code of conduct. - **Action:** A permanent ban from use of Carbon's community spaces. ## Acknowledgements This code is based on the [LLVM Code of Conduct](https://llvm.org/docs/CodeOfConduct.html), the [Django Project Code of Conduct](https://github.com/django/code-of-conduct), the [Speak Up! project](http://web.archive.org/web/20141109123859/http://speakup.io/coc.html), and the [Contributor Covenant version 2.0](https://www.contributor-covenant.org/version/2/0/code_of_conduct/). Many thanks to all of these projects for their work helping build effective tools for open source communities. ================================================ FILE: CONTRIBUTING.md ================================================ # Contributing ## Table of contents - [Overview](#overview) - [Ways to contribute](#ways-to-contribute) - [Contributing to the language design](#contributing-to-the-language-design) - [Comment on proposals](#comment-on-proposals) - [Contribute design ideas to Carbon](#contribute-design-ideas-to-carbon) - [Contributing to the language implementation](#contributing-to-the-language-implementation) - [Review and comment on Pull Requests (no code)](#review-and-comment-on-pull-requests-no-code) - [Implement Carbon's design](#implement-carbons-design) - [Triage, analyze or address bugs](#triage-analyze-or-address-bugs) - [How to become a contributor to Carbon](#how-to-become-a-contributor-to-carbon) - [Contributor License Agreements (CLAs)](#contributor-license-agreements-clas) - [Future CLA plans](#future-cla-plans) - [Collaboration systems](#collaboration-systems) - [Getting access](#getting-access) - [Contribution tools](#contribution-tools) - [Using AI-based contribution tools](#using-ai-based-contribution-tools) - [Contribution guidelines and standards](#contribution-guidelines-and-standards) - [Guidelines and philosophy for contributions](#guidelines-and-philosophy-for-contributions) - [How to say things](#how-to-say-things) - [Make your point concisely](#make-your-point-concisely) - [Style](#style) - [Google Docs and Markdown](#google-docs-and-markdown) - [Other files](#other-files) - [License](#license) - [Google Docs](#google-docs) - [Markdown](#markdown) - [Other files](#other-files-1) - [Workflow](#workflow) - [Acknowledgements](#acknowledgements) ## Overview Thank you for your interest in contributing to Carbon! There are many ways to contribute, and we appreciate all of them. If you have questions, please feel free to ask on [Discord](https://discord.gg/ZjVdShJDAs) `#contributing-help` channel or [GitHub](https://github.com/carbon-language/carbon-lang/discussions). Everyone who contributes to Carbon is expected to: - Read and follow the [Code of Conduct](CODE_OF_CONDUCT.md). We expect everyone in our community to be welcoming, helpful, and respectful. - Ensure you have signed the [Contributor License Agreement (CLA)](https://cla.developers.google.com/). We need this to cover some legal bases. We also encourage anyone interested in contributing to check out all the information here in our contributing guide, especially the [guidelines and philosophy for contributions](#guidelines-and-philosophy-for-contributions) ## Ways to contribute ### Contributing to the language design #### Comment on proposals If you're looking for a quick way to contribute, commenting on proposals is a way to provide proposal authors with a breadth of feedback. The ["leads questions" label](https://github.com/carbon-language/carbon-lang/issues?q=is%3Aissue+is%3Aopen+label%3A%22leads+question%22) has questions the community is looking for a decision on. The [list of open proposals](https://github.com/carbon-language/carbon-lang/pulls?q=is%3Apr+is%3Aopen+label%3A%22proposal+rfc%22) will have more mature proposals that are nearing a decision. For more about the difference, see the [evolution process](docs/project/evolution.md). When giving feedback, please keep comments positive, constructive, and [concise](#make-your-point-concisely). Our goal is to use community discussion to improve proposals and assist authors. #### Contribute design ideas to Carbon If you have ideas for Carbon, we encourage you to discuss it with the community, and potentially prepare a proposal for it. Ultimately, any changes or improvements to Carbon will need to turn into a proposal and go through our [evolution process](docs/project/evolution.md). If you do start working on a proposal, keep in mind that this requires a time investment to discuss the idea with the community, get it reviewed, and eventually implemented. A good starting point is to read through the [evolution process](docs/project/evolution.md). We encourage discussing the idea early, before even writing a proposal, and the process explains how to do that. ### Contributing to the language implementation #### Review and comment on Pull Requests (no code) Helping with [pull requests](https://github.com/carbon-language/carbon-lang/pulls) review is a good way to provide feedback, while getting acquainted with the code base. #### Implement Carbon's design The implementation of the Carbon language design is currently focused on the [Carbon toolchain](/toolchain/) (see Carbon [toolchain issues](https://github.com/carbon-language/carbon-lang/issues?q=is%3Aissue+is%3Aopen+label%3Atoolchain)) **Some issues are also marked as ["good first issues"](https://github.com/carbon-language/carbon-lang/labels/good%20first%20issue)**. These are intended to be a good place to start contributing. To pick up a "good first issue", check to make sure there's no in-flight pull request on the issue, and then start working on it. We don't assign issues to new contributors because some people have different time constraints, and we want new contributors to feel welcome to pick the issue up when it may not be making progress. Even if someone else merges a fix before you, these issues should be a quick and helpful way to start learning how Carbon is built, building towards larger contributions. #### Triage, analyze or address bugs As Carbon's design and implementation take shape, we'll inevitably encounter plenty of bugs. Helping us triage, analyze, and address them is always a great way to get involved. See [open issues on GitHub](https://github.com/carbon-language/carbon-lang/issues). When triaging issues, we typically won't assign issues because we want to be confident that contributors who have an issue assigned to them are planning for the amount of time it will take, which requires familiarity. Contributors with write access are expected to have that familiarity and may assign issues to themselves. ## How to become a contributor to Carbon ### Contributor License Agreements (CLAs) We'd love to accept your documentation, pull requests, and comments! Before we can accept them, we need you to cover some legal bases. Please fill out either the individual or corporate CLA. - If you are an individual contributing to spec discussions or writing original source code and you're sure you own the intellectual property, then you'll need to sign an [individual CLA](https://code.google.com/legal/individual-cla-v1.0.html). - If you work for a company that wants to allow you to contribute your work, then you'll need to sign a [corporate CLA](https://code.google.com/legal/corporate-cla-v1.0.html). Follow either of the two links above to access the appropriate CLA and instructions for how to sign and return it. Once we receive it, we'll be able to accept your documents, comments and pull requests. **_NOTE_**: Only original content from you and other people who have signed the CLA can be accepted as Carbon contributions: this covers GitHub (including both code and discussion), Google Docs, and Discord. #### Future CLA plans Initially, Carbon is bootstrapping using Google's CLA. We are planning to create an open source foundation and transfer all Carbon-related rights to it; our goal is for the foundation setup to be similar to other open source projects, such as LLVM or Kubernetes. ### Collaboration systems We use a few systems for collaboration which contributors should be aware of. Before using these systems, everyone must sign the CLA. They are all governed by the Code of Conduct. - [The GitHub carbon-language organization](https://github.com/carbon-language) is used for our repositories. - [Discord](https://discord.gg/ZjVdShJDAs) is used for chat. - [A shared Google Drive](https://drive.google.com/drive/folders/1aC5JJ5EcI8B7cgVDrLvO7WNw97F0LpS2) is used for all of our Google Docs, particularly proposal drafts. - [Google Calendar](https://calendar.google.com/calendar/embed?src=c_07td7k4qjq0ssb4gdl6bmbnkik%40group.calendar.google.com) is used for meeting invites and project reminders. Contributors may add calendar entries for meetings added to discuss details. Standard entries have [minutes](https://drive.google.com/drive/folders/1VssO6kn9-HeKfzPDqBDR0Sy5CUE0OnSs) and include: - The weekly sync, where contributors are welcome. - Open discussions, which are unstructured meeting slots used for discussing proposals, tooling, and other Carbon topics based on who attends. #### Getting access Our collaboration systems are all viewable publicly, and most can be joined without particular requests. However, some require extra permissions, such as editing Google Docs, joining meetings, or some details of the proposal process. When requesting any of the following access, please provide a reason for the access. All requests require a [signed CLA](#contributor-license-agreements-clas). - Google Docs/Calendar access groups: - **Commenter** access: [join group](https://groups.google.com/a/carbon-lang.dev/g/commenters/about) - [Google Docs](https://drive.google.com/drive/folders/1aC5JJ5EcI8B7cgVDrLvO7WNw97F0LpS2): Comment on files. - [Google Calendar](https://calendar.google.com/calendar/embed?src=c_07td7k4qjq0ssb4gdl6bmbnkik%40group.calendar.google.com): View event details. - **Contributor** access: [join group](https://groups.google.com/a/carbon-lang.dev/g/contributors/about) - [Google Docs](https://drive.google.com/drive/folders/1aC5JJ5EcI8B7cgVDrLvO7WNw97F0LpS2): Add, edit, and comment on files. - [Google Calendar](https://calendar.google.com/calendar/embed?src=c_07td7k4qjq0ssb4gdl6bmbnkik%40group.calendar.google.com): View and edit event details. - After you apply to join, please let us know on [#access-requests](https://discord.com/channels/655572317891461132/1006221387574292540); we don't get notifications otherwise. - GitHub Label/project contributor access: [ask on #access-requests](https://discord.com/channels/655572317891461132/1006221387574292540) - Don't forget to mention your GitHub username. - Used by the proposal process. If you simply want to chime in on GitHub or Discord, none of this is needed. If you're interested in joining meetings, ask for commenter access. If you're trying to write proposals, both types of contributor access will help. ### Contribution tools Please see our [contribution tool](/docs/project/contribution_tools.md) documentation for information on setting up a git client for Carbon development, as well as helpful tooling that will ease the contribution process. For example, [pre-commit](https://pre-commit.com) is used to simplify [code review](/docs/project/code_review.md). #### Using AI-based contribution tools If you are using an AI assistant to help you contribute, or if you are an AI assistant yourself, please consult [GEMINI.md](/GEMINI.md) for high-density technical context and tips. All submissions to Carbon need to follow our [Contributor License Agreement (CLA)](#contributor-license-agreements-clas), which covers any original work of authorship included in the submission. This doesn't prohibit the use of coding assistance tools, including tool-, AI-, or machine-generated code, as long as these submissions abide by the CLA's requirements. All contributions, regardless of what tools are used, are also still the responsibility of the operator of these tools and subject to normal code review and our [guidelines and standards](#contribution-guidelines-and-standards) below. We also emphasize two additional requirements for contributors operating or using AI-based tools: 1. **Contributions should not become extractive of the project and community**: the value added should outweigh the overhead of landing the contribution. The overhead of landing contributions ranges from code review, to discussions, distractions from the current project priorities, or growing maintenance burden without growing maintainers. 2. **Each PR should be transparent about the tooling used** in proportion to how much of the PR was produced by the tool and whether the tool is a standard one for the project. For example, formatting with the standard tools is reasonable to assume without further comment. But if a PR is largely derived from running a specific Python script, regular expression, or AI-based tool over the codebase, we ask that its commit message is transparent about this and include a description of how the tool was used to formulate the change. For PRs largely derived from AI-based tooling, we suggest following the pattern established by the Fedora Project to mark commits with `Assisted-by: ...`. Our policies and practices here are inspired by and aim to be roughly compatible with several other open source projects: - [Fedora Project's policy](https://docs.fedoraproject.org/en-US/council/policy/ai-contribution-policy/) - [LLVM Developer Policy around AI generated code](https://llvm.org/docs/DeveloperPolicy.html#ai-generated-contributions) As the open source community evolves and learns how best to integrate these tools into project and development workflows, we expect to reflect that with updates and improvements here. ### Contribution guidelines and standards All documents and pull requests must be consistent with the guidelines and follow the Carbon documentation and coding styles. #### Guidelines and philosophy for contributions - For **both** documentation and code: - When the Carbon team accepts new documentation or features, to Carbon, by default they take on the maintenance burden. This means they'll weigh the benefit of each contribution against the cost of maintaining it. - The appropriate [style](#style) is applied. - The [license](#license) is present in all contributions. - [Code review](/docs/project/code_review.md) is used to improve the correctness, clarity, and consistency of all contributions. - Please avoid rebasing PRs after receiving comments; it can break viewing of the comments in files. - For documentation: - All documentation is written for clarity and readability. Beyond fixing spelling and grammar, this also means content is worded to be accessible to a broad audience. - Substantive changes to Carbon follow the [evolution process](docs/project/evolution.md). Pull requests are only sent after the documentation changes have been accepted by the reviewing team. - Typos or other minor fixes that don't change the meaning of a document do not need formal review, and are often handled directly as a pull request. - For code: - New features should have a documented design that has been approved through the [evolution process](docs/project/evolution.md). This includes modifications to preexisting designs. - Bug fixes and mechanical improvements don't need this. - All new features include unit tests, as they help to (a) document and validate concrete usage of the feature and its edge cases, and (b) guard against future breaking changes to lower the maintenance cost. - Bug fixes also generally include unit tests, because the presence of bugs usually indicates insufficient test coverage. - Unit tests must pass with the changes. - If some tests fail for unrelated reasons, we wait until they're fixed. It helps to contribute a fix! - Code changes should be made with API compatibility and evolvability in mind. - Keep in mind that code contribution guidelines are incomplete while we start work on Carbon, and may change later. #### How to say things - Treat others with respect. - Recognize that other points of view are valid, and most decisions are about choosing the best set of trade-offs or closest alignment with Carbon's goals since there usually isn't a single best answer. It is important to frame feedback and discussion about someone else's proposal under the assumption that they too have deep experience in the area but may have come to a different conclusion. - Be clear when something is an opinion by using "I" or "me": - Not as helpful: "`foo` is objectively better word to use" - More helpful: "I find `bar` confusing since it has this alternate meaning, I think `foo` is clearer." - It can be helpful to define your terms. When trying to make a point, please employ these strategies to make your argument _effective_ and _helpful_: - Focus should be on explaining the basis by which others can come to a conclusion. Generally this means connecting a potential solution to the use cases it helps with. - Be specific and concrete, using examples to demonstrate the benefits and disadvantages of the different options - Minimally helpful: "I like <X>", "Carbon should have feature <Y>" - More helpful: "I think Carbon should have feature <Z>, it would mean <this example> would be written <like this> instead of <like that>." (Assuming the reader will think the example is representative of an important class of use cases.) - Very helpful: "If we go with approach <X>, it helps with problem <P> in <this example> of use case <A>. It doesn't help with use case <B>, but that can be better solved by <Y>, as can be seen in <other example>." - Very helpful: "Yes <X> gives a nice answer in that case, but it has a problem / I don't see how it applies in <this other situation>." - Explain the reasoning by which you can come to your conclusion - Avoid [fallacies](https://en.wikipedia.org/wiki/List_of_fallacies) like [arguing from authority](https://en.wikipedia.org/wiki/Argument_from_authority) - Don't expect people to read long works to understand your point If someone questions or argues with your point, try to directly address the points being made. Try not to step backwards or switch to a more general or more meta level as that can seem like you're evading the question. ##### Make your point concisely - **Asking questions is OK**: Asking questions about a particular discussion is almost always OK. If you're worried the questions might be more about _background_ and might be long enough to get distracting, you can always ask them in some of our dedicated spaces like `#language-questions` on Discord. - **Favor new and relevant information**: When sharing ideas and opinions in community discussions, it is important to do so in a way that both _adds new information_ in some way/shape/form, makes sure that information is _relevant_ to the discussion, and avoids _repetition_. This means reviewing what you write before posting a larger response, and editing it down to just the points you want to make that have not already been made, and any new arguments supporting those points. - **Be inclusive by staying concise**: We need to be mindful that writing many paragraphs of extra text is going to exclude people. Some people can be excluded from consuming the conversation, either because a wall of text is too intimidating to read, or they don't have the time or bandwidth to wade through the extra text to find the new information being conveyed. It can also drown out other contributors - **Prefer upvote to repetition**: One person saying "I don't like this feature" is useful, and that same message with 100 upvotes is extremely useful, but 100 people writing separate messages saying "I don't like this feature" is not. Emoji reactions are available in both Discord and GitHub and should be used for this purpose in both. It is also okay to do things like ask a question to get clarification about what someone has said or to solicit opinions about various options. Just be respectful, and don't drown out other discussion. ## Style ### Google Docs and Markdown Changes to Carbon documentation follow the [Google developer documentation style guide](https://developers.google.com/style). Markdown files should additionally use [Prettier](https://prettier.io) for formatting, which we automate with [pre-commit](/docs/project/contribution_tools.md#main-tools). Other style points to be aware of are: - Whereas the Google developer documentation style guide [says to use an em dash](https://developers.google.com/style/dashes) (`text—text`), we are using a double-hyphen with surrounding spaces (`text -- text`). We are doing this because we frequently read Markdown with fixed-width fonts where em dashes are not clearly visible. - Prefer the term "developers" when talking about people who would write Carbon code. We expect the Carbon's community to include people who think of themselves using many titles, including software developers, software engineers, systems engineers, reliability engineers, data scientists, computer scientists, programmers, and coders. We're using "developers" to succinctly cover the variety of titles. ### Other files If you're not sure what style to use, please ask on Discord or GitHub. ## License A license is required at the top of all documents and files. ### Google Docs Google Docs all use [this template](https://docs.google.com/document/d/1tAwE0230PDxweVruHUVY6DSfSnrJF2LnLOWXQYqNJuI/template/preview?usp=sharing&resourcekey=0-zsrwCWP7ictbxhCuePk-fw). It puts the license at the top of every page if printed. ### Markdown Markdown files always have at the top: ``` # DOC TITLE ``` For example, see the top of [CONTRIBUTING.md](https://github.com/carbon-language/carbon-lang/raw/trunk/CONTRIBUTING.md)'s raw content. ### Other files Every file type uses a variation on the same license text ("Apache-2.0 WITH LLVM-exception") with similar formatting. If you're not sure what text to use, please ask on Discord or GitHub. ## Workflow Carbon repositories all follow a common [pull-request workflow](docs/project/pull_request_workflow.md) for landing changes. It is a trunk-based development model that emphasizes small, incremental changes and preserves a simple linear history. ## Acknowledgements Carbon's Contributing guidelines are based on [TensorFlow](https://github.com/tensorflow/tensorflow/blob/master/CONTRIBUTING.md) and [Flutter](https://github.com/flutter/flutter/blob/master/CONTRIBUTING.md) guidelines. Many thanks to these communities for their help in providing a basis. ================================================ FILE: GEMINI.md ================================================ # Gemini & AI assistant guide for Carbon This document provides high-density technical context for AI assistants (and humans!) contributing to the Carbon Language project. If you are an AI assistant, **read this first** to avoid common pitfalls. ## Table of contents - [General instructions](#general-instructions) - [Project structure](#project-structure) - [Toolchain development](#toolchain-development) ## General instructions - **Communication**: Be concise, professional, and technical. Use GitHub-style markdown. - **Verification**: Always run relevant tests. - **Tool usage**: Use web search for any research outside the immediate codebase or KIs. ## Project structure - **[`common/`](common/)**: Common C++ utilities used across the project. - **[`core/`](core/)**: The Carbon standard library (Core). - **[`docs/`](docs/)**: Project documentation, design, and style guides. - **[`examples/`](examples/)**: Example Carbon programs and code snippets. - **[`proposals/`](proposals/)**: Evolution proposals. - **[`testing/`](testing/)**: Testing utilities and infrastructure. - **[`toolchain/`](toolchain/)**: The C++ implementation of the compiler (Toolchain). ## Tool usage See the "Tool usage" skill for instructions on what tools to use in the carbon-lang project. ## Code style See the "Code style" skill for instructions on formatting, style guides, and code conventions to follow. ## Toolchain development See the "Toolchain Development" skill for instructions on architecture, building, testing, debugging, C++ patterns, and common pitfalls. ================================================ FILE: LICENSE ================================================ ============================================================================== The Carbon language is under the Apache License v2.0 with LLVM Exceptions: ============================================================================== Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright [yyyy] [name of copyright owner] Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. ---- LLVM Exceptions to the Apache 2.0 License ---- As an exception, if, as a result of your compiling your source code, portions of this Software are embedded into an Object form of such source code, you may redistribute such embedded portions in such Object form without complying with the conditions of Sections 4(a), 4(b) and 4(d) of the License. In addition, if you combine or link compiled forms of this Software with software that is licensed under the GPLv2 ("Combined Software") and if a court of competent jurisdiction determines that the patent provision (Section 3), the indemnity provision (Section 9) or other Section of the License conflicts with the conditions of the GPLv2, you may retroactively and prospectively choose to deem waived or otherwise exclude such Section(s) of the License, but only in their entirety and only with respect to the Combined Software. ============================================================================== Software from third parties included in the Carbon language: ============================================================================== The Carbon language contains third party software which is under different license terms. All such code will be identified clearly using at least one of two mechanisms: 1) It will be in a separate directory tree with its own `LICENSE.txt` or `LICENSE` file at the top containing the specific license and restrictions which apply to that software, or 2) It will contain specific license and restriction terms at the top of every file. ================================================ FILE: MODULE.bazel ================================================ # Part of the Carbon Language project, under the Apache License v2.0 with LLVM # Exceptions. See /LICENSE for license information. # SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception """Bazel modules. `MODULE.bazel.lock` may change locally when `bazel` is executed. This means one of: 1. An input is changing, typically `MODULE.bazel` or `.bazelversion`. - Running `bazel mod deps` provides a canonical update to `MODULE.bazel.lock`; include the changes. - GitHub test actions may also identify platform-specific lockfile updates. 2. The host platform hasn't yet been added to the lock file. - Platforms tested with GitHub actions are kept up-to-date. Other platforms may fall out of sync due to `bazel` or dependency changes, and should be updated with a PR the same way a platform is added. - Running `bazel mod deps` provides a canonical update to `MODULE.bazel.lock`; create a PR with those changes in order to include the host platform. For updates, run `scripts/query_module_versions.py` to list the latest package versions. """ module(name = "carbon") bazel_dep(name = "abseil-cpp", version = "20260107.1") bazel_dep(name = "bazel_skylib", version = "1.9.0") bazel_dep(name = "boost.unordered", version = "1.90.0.bcr.1") bazel_dep(name = "google_benchmark", version = "1.9.5") bazel_dep(name = "googletest", version = "1.17.0.bcr.2") bazel_dep(name = "libpfm", version = "4.13.0") bazel_dep(name = "re2", version = "2025-11-05.bcr.1") bazel_dep(name = "rules_cc", version = "0.2.17") bazel_dep(name = "rules_pkg", version = "1.2.0") bazel_dep(name = "rules_shell", version = "0.6.1") bazel_dep(name = "tcmalloc", version = "0.0.0-20250927-12f2552") bazel_dep(name = "tree-sitter-bazel", version = "0.26.5") bazel_dep(name = "wolfd_bazel_compile_commands", version = "0.5.2", dev_dependency = True) bazel_dep(name = "bazel_clang_tidy", dev_dependency = True) git_override( module_name = "bazel_clang_tidy", # HEAD as of 2026-01-28. commit = "c4d35e0d0b838309358e57a2efed831780f85cd0", remote = "https://github.com/erenon/bazel_clang_tidy.git", ) bazel_cc_toolchain = use_extension( "//bazel/cc_toolchains:clang_configuration.bzl", "clang_toolchain_extension", ) use_repo(bazel_cc_toolchain, "bazel_cc_toolchain") register_toolchains("@bazel_cc_toolchain//:all") # TODO: Trying out `wolfd_bazel_compile_commands`, figure out if there are # issues. Once people have had a chance to try and check for problems, remove # one or the other. bazel_dep(name = "hedron_compile_commands", dev_dependency = True) git_override( module_name = "hedron_compile_commands", # HEAD as of 2026-01-28. commit = "abb61a688167623088f8768cc9264798df6a9d10", remote = "https://github.com/hedronvision/bazel-compile-commands-extractor.git", ) # Required for llvm-project. bazel_dep(name = "platforms", version = "1.0.0") bazel_dep(name = "protobuf", version = "34.0.bcr.1", repo_name = "com_google_protobuf") bazel_dep(name = "zlib-ng", version = "2.0.7", repo_name = "llvm_zlib") bazel_dep(name = "zstd", version = "1.5.7.bcr.1", repo_name = "llvm_zstd") ############################################################################### # llvm-project ############################################################################### # Load a repository for the raw llvm-project, pre-overlay. bazel_dep(name = "llvm-raw") git_override( module_name = "llvm-raw", build_file_content = "# empty", # We pin to specific upstream commits and try to track top-of-tree # reasonably closely rather than pinning to a specific release. # HEAD as of 2026-03-09. commit = "d8474abfc1e7c3856b85b088f1133ae2e90da3d0", patch_cmds = ["echo \"module(name='llvm-raw')\" > MODULE.bazel"], patch_strip = 1, patches = [ "//bazel/llvm_project:0001_Patch_for_mallinfo2_when_using_Bazel_build_system.patch", "//bazel/llvm_project:0002_Added_Bazel_build_for_compiler_rt_fuzzer.patch", "//bazel/llvm_project:0004_Introduce_basic_sources_exporting_for_libunwind.patch", "//bazel/llvm_project:0005_Introduce_basic_sources_exporting_for_libcxx_and_libcxxabi.patch", "//bazel/llvm_project:0009_Introduce_starlark_exporting_compiler-rt_build_information.patch", ], remote = "https://github.com/llvm/llvm-project.git", ) # Apply the overlay to produce llvm-project. llvm_project = use_extension( "//bazel/llvm_project:llvm_project.bzl", "llvm_project", ) use_repo(llvm_project, "llvm-project") ############################################################################### # Python ############################################################################### bazel_dep(name = "rules_python", version = "1.9.0") python = use_extension("@rules_python//python/extensions:python.bzl", "python") python.toolchain( python_version = "3.11", ) use_repo(python, "python_versions") ############################################################################### # Bazel integration testing ############################################################################### bazel_dep( name = "rules_bazel_integration_test", version = "0.37.1", dev_dependency = True, ) # We test against our current Bazel version, the latest release, and the latest # release candidate. bazel_binaries = use_extension( "@rules_bazel_integration_test//:extensions.bzl", "bazel_binaries", dev_dependency = True, ) bazel_binaries.download(version_file = "//:.bazelversion") bazel_binaries.download(version = "latest") bazel_binaries.download(version = "last_rc") use_repo( bazel_binaries, "bazel_binaries", "bazel_binaries_bazelisk", "build_bazel_bazel_.bazelversion", "build_bazel_bazel_last_rc", "build_bazel_bazel_latest", ) ================================================ FILE: README.md ================================================ # Carbon Language:
An experimental successor to C++

Why? | Goals | Status | Getting started | Join us

**See our [announcement video](https://youtu.be/omrY53kbVoA) from [CppNorth](https://cppnorth.ca/).** Note that Carbon is [not ready for use](#project-status). Quicksort code in Carbon. Follow the link to read more.
**Fast and works with C++** - Performance matching C++ using LLVM, with low-level access to bits and addresses - Interoperate with your existing C++ code, from inheritance to templates - Fast and scalable builds that work with your existing C++ build systems **Modern and evolving** - Solid language foundations that are easy to learn, especially if you have used C++ - Easy, tool-based upgrades between Carbon versions - Safer fundamentals, and an incremental path towards a memory-safe subset **Welcoming open-source community** - Clear goals and priorities with robust governance - Community that works to be welcoming, inclusive, and friendly - Batteries-included approach: compiler, libraries, docs, tools, package manager, and more ## Why build Carbon? C++ remains the dominant programming language for performance-critical software, with massive and growing codebases and investments. However, it is struggling to improve and meet developers' needs, as outlined above, in no small part due to accumulating decades of technical debt. Incrementally improving C++ is [extremely difficult](/docs/project/difficulties_improving_cpp.md), both due to the technical debt itself and challenges with its evolution process. The best way to address these problems is to avoid inheriting the legacy of C or C++ directly, and instead start with solid language foundations like [modern generics system](#generics), modular code organization, and consistent, simple syntax. Existing modern languages already provide an excellent developer experience: Go, Swift, Kotlin, Rust, and many more. **Developers that _can_ use one of these existing languages _should_.** Unfortunately, the designs of these languages present significant barriers to adoption and migration from C++. These barriers range from changes in the idiomatic design of software to performance overhead. Carbon is fundamentally **a successor language approach**, rather than an attempt to incrementally evolve C++. It is designed around interoperability with C++ as well as large-scale adoption and migration for existing C++ codebases and developers. A successor language for C++ requires: - **Performance matching C++**, an essential property for our developers. - **Seamless, bidirectional interoperability with C++**, such that a library anywhere in an existing C++ stack can adopt Carbon without porting the rest. - **A gentle learning curve** with reasonable familiarity for C++ developers. - **Comparable expressivity** and support for existing software's design and architecture. - **Scalable migration**, with some level of source-to-source translation for idiomatic C++ code. With this approach, we can build on top of C++'s existing ecosystem, and bring along existing investments, codebases, and developer populations. There are a few languages that have followed this model for other ecosystems, and Carbon aims to fill an analogous role for C++: - JavaScript → TypeScript - Java → Kotlin - C++ → **_Carbon_** ## Language Goals We are designing Carbon to support: - Performance-critical software - Software and language evolution - Code that is easy to read, understand, and write - Practical safety and testing mechanisms - Fast and scalable development - Modern OS platforms, hardware architectures, and environments - Interoperability with and migration from existing C++ code While many languages share subsets of these goals, what distinguishes Carbon is their combination. We also have explicit _non-goals_ for Carbon, notably including: - A stable [application binary interface](https://en.wikipedia.org/wiki/Application_binary_interface) (ABI) for the entire language and library - Perfect backwards or forwards compatibility Our detailed [goals](/docs/project/goals.md) document fleshes out these ideas and provides a deeper view into our goals for the Carbon project and language. ## Project status Carbon Language is currently an experimental project. We are hard at work on a toolchain implementation with compiler and linker. You can try out the current state at [compiler-explorer.com](http://carbon.compiler-explorer.com/). We want to better understand whether we can build a language that meets our successor language criteria, and whether the resulting language can gather a critical mass of interest within the larger C++ industry and community. Currently, we have fleshed out several core aspects of both Carbon the project and the language: - The strategy of the Carbon Language and project. - An open-source project structure, governance model, and evolution process. - Critical and foundational aspects of the language design informed by our experience with C++ and the most difficult challenges we anticipate. This includes designs for: - Generics - Class types - Inheritance - Operator overloading - Lexical and syntactic structure - Code organization and modular structure - An under-development [compiler and toolchain](/toolchain/) that will compile Carbon (and eventually C++ code as well) into standard executable code. This is where most of our current implementation efforts are directed. - Historically, there was also a prototype [explorer](https://github.com/carbon-language/explorer) interpreter that implemented an older version of the Carbon language design, but is no longer under development and has been archived. If you're interested in contributing, we're currently focused on developing the Carbon toolchain until it can [support Carbon ↔ C++ interop](/docs/project/roadmap.md#access-most-non-template-c-apis-in-carbon). Beyond that, we plan to continue developing the design and toolchain until we can ship the [0.1 language](/docs/project/milestones.md#milestone-01-a-minimum-viable-product-mvp-for-evaluation) and support evaluating Carbon in more detail. You can see our [full roadmap](/docs/project/roadmap.md) for more details. ## Carbon and C++ If you're already a C++ developer, Carbon should have a gentle learning curve. It is built out of a consistent set of language constructs that should feel familiar and be easy to read and understand. The Carbon code here is hypothetical and meant to show the look and feel of the language. C++ code like this: A snippet of C++ code. Follow the link to read it. corresponds to this Carbon code: A snippet of converted Carbon code. Follow the link to read it. You can call Carbon from C++ without overhead and the other way around. This means you migrate a single C++ library to Carbon within an application, or write new Carbon on top of your existing C++ investment. For example: A snippet of mixed Carbon and C++ code. Follow the link to read it. Read more about [C++ interop in Carbon](/docs/design/interoperability/philosophy_and_goals.md). Beyond interoperability between Carbon and C++, we're also planning to support migration tools that will mechanically translate idiomatic C++ code into Carbon code to help you switch an existing C++ codebase to Carbon. ## Generics Carbon provides a **[modern generics system](/docs/design/generics/overview.md#what-are-generics)** with checked definitions, while still **supporting opt-in [templates](/docs/design/templates.md) for seamless C++ interop**. Checked generics provide several advantages compared to C++ templates: - **Generic definitions are fully type-checked**, removing the need to instantiate to check for errors and giving greater confidence in code. - Avoids the compile-time cost of re-checking the definition for every instantiation. - When using a definition-checked generic, usage error messages are clearer, directly showing which requirements are not met. - **Enables automatic, opt-in type erasure and dynamic dispatch** without a separate implementation. This can reduce the binary size and enables constructs like heterogeneous containers. - **Strong, checked interfaces** mean fewer accidental dependencies on implementation details and a clearer contract for consumers. Without sacrificing these advantages, **Carbon generics support specialization**, ensuring it can fully address performance-critical use cases of C++ templates. For more details about Carbon's generics, see their [design](/docs/design/generics). In addition to easy and powerful interop with C++, Carbon templates can be constrained and incrementally migrated to checked generics at a fine granularity and with a smooth evolutionary path. ## Memory safety Safety, and especially [memory safety](https://en.wikipedia.org/wiki/Memory_safety), remains a key challenge for C++ and something a successor language needs to address. We plan to support a two step migration process: 1. Highly automated, minimal supervision migration from C++ to a dialect of Carbon designed for C++ interop and migration. 2. Incremental refactoring of the Carbon code to adopt memory-safe designs, patterns, and APIs. We also want to address important, low-hanging fruit in the safety space immediately when migrating into Carbon: - Tracking uninitialized states better, increased enforcement of initialization, and hardening against initialization bugs when needed. - Designing fundamental APIs and idioms to support dynamic bounds checking. - Switching from undefined behavior to erroneous behavior wherever possible, and marking the remaining undefined behavior with visible `unsafe` syntax. - Having a default debug build mode that has less runtime overhead while being more comprehensive than existing C++ debug build modes combined with [Address Sanitizer](https://github.com/google/sanitizers/wiki/AddressSanitizer). For more details, see our [safety design](/docs/design/safety). ## Getting started To try out Carbon immediately in your browser, you can use the toolchain at: [carbon.compiler-explorer.com](http://carbon.compiler-explorer.com/). We are developing a traditional toolchain for Carbon that can compile and link programs. However, Carbon is still an early, experimental project, and so we only have very experimental nightly releases of the Carbon toolchain available to download, and only on limited platforms. If you are using a recent Ubuntu Linux or similar (Debian, WSL, etc.), you can try these out by going to our [releases](https://github.com/carbon-language/carbon-lang/releases) page and download the latest nightly toolchain tar file: `carbon_toolchain-0.0.0-0.nightly.YYYY.MM.DD.tar.gz`. Then you can try it out: ```shell # A variable with the nightly version from yesterday: VERSION="$(date -d yesterday +0.0.0-0.nightly.%Y.%m.%d)" # Get the release wget https://github.com/carbon-language/carbon-lang/releases/download/v${VERSION}/carbon_toolchain-${VERSION}.tar.gz # Unpack the toolchain: tar -xvf carbon_toolchain-${VERSION}.tar.gz # Create a simple Carbon source file: echo "import Core library \"io\"; fn Run() { Core.Print(42); }" > forty_two.carbon # Compile to an object file: ./carbon_toolchain-${VERSION}/bin/carbon compile \ --output=forty_two.o forty_two.carbon # Install minimal system libraries used for linking. Note that installing `gcc` # or `g++` for compiling C/C++ code with GCC will also be sufficient, these are # just the specific system libraries Carbon linking still uses. sudo apt install libgcc-11-dev # Link to an executable: ./carbon_toolchain-${VERSION}/bin/carbon link \ --output=forty_two forty_two.o # Run it: ./forty_two ``` As a reminder, the toolchain is still very early and many things don't yet work. Please hold off on filing lots of bugs: we know many parts of this don't work yet or may not work on all systems. We expect to have releases that are much more robust and reliable that you can try out when we reach our [0.1 milestone](/docs/project/milestones.md#milestone-01-a-minimum-viable-product-mvp-for-evaluation). If you want to build Carbon's toolchain yourself or are thinking about contributing fixes or improvements to Carbon, you'll need to install our [build dependencies](/docs/project/contribution_tools.md#setup-commands) (Clang, LLD, libc++) and check out the Carbon repository. For example, on Debian or Ubuntu: ```shell # Update apt. sudo apt update # Install tools. sudo apt install \ clang \ libc++-dev \ libc++abi-dev \ lld # Download Carbon's code. $ git clone https://github.com/carbon-language/carbon-lang $ cd carbon-lang ``` Then you can try out our toolchain which has a very early-stage compiler for Carbon: ```shell # Build and run the toolchain's help to get documentation on the command line. $ ./scripts/run_bazelisk.py run //toolchain -- help ``` For complete instructions, including installing dependencies on various different platforms, see our [contribution tools documentation](/docs/project/contribution_tools.md). Learn more about the Carbon project: - [Project goals](/docs/project/goals.md) - [Language design overview](/docs/design) - [Carbon Toolchain](/toolchain) - [FAQ](/docs/project/faq.md) ## Conference talks Carbon focused talks from the community: ### 2026 - Benchmarking and optimizing the Carbon compiler, NDC {Toronto} (May 5-8) - Carbon: graduating from the experiment, NDC {Toronto} (May 5-8) ### 2025 - Carbon: from C++ to Memory Safety, REBASE - ICFP/SPLASH ([slides](https://chandlerc.blog/slides/2025-rebase-carbon)) - Memory safety everywhere with both Carbon and Rust, RustConf ([video](https://youtu.be/FYLuom6gg_s), [slides](https://chandlerc.blog/slides/2025-rustconf-memory-safety-everywhere)) ### 2024 - Generic implementation strategies in Carbon and Clang, LLVM Developers' Meeting ([video](https://youtu.be/j0BL52NdjAU), [slides](https://chandlerc.blog/slides/2024-llvm-generic-implementation/#/)) - The Carbon Language: Road to 0.1, NDC {TechTown} ([video](https://youtu.be/bBvLmDJrzvI), [slides](https://chandlerc.blog/slides/2024-ndc-techtown-carbon-road-to-0-dot-1)) - How designing Carbon with C++ interop taught me about C++ variadics and overloads, CppNorth ([video](https://youtu.be/8SGMy9ENGz8), [slides](https://chandlerc.blog/slides/2024-cppnorth-design-stories)) - Generic Arity: Definition-Checked Variadics in Carbon, C++Now ([video](https://youtu.be/Y_px536l_80), [slides](https://docs.google.com/presentation/d/10aM1mFMN6Cd5ZkE4OfeiZtSnkVNbo33N-V0et21umww/edit)) - Carbon: An experiment in different tradeoffs, panel session, EuroLLVM ([video](https://youtu.be/Za_KWj5RMR8), [slides](https://llvm.org/devmtg/2024-04/slides/LightningTalks/Smith-Carbons-high-level-semanticIR.pdf)) - [Alex Bradbury's notes](https://muxup.com/2024q2/notes-from-the-carbon-panel-session-at-eurollvm) - Carbon's high-level semantic IR lightning talk, EuroLLVM ([video](https://youtu.be/vIWT4RhUcyw)) ### 2023 - Carbon’s Successor Strategy: From C++ interop to memory safety, C++Now ([video](https://youtu.be/1ZTJ9omXOQ0), [slides](https://chandlerc.blog/slides/2023-cppnow-carbon-strategy/index.html#/)) - Definition-Checked Generics, C++Now - Part 1 ([video](https://youtu.be/FKC8WACSMP0), [slides](https://chandlerc.blog/slides/2023-cppnow-generics-1/#/)) - Part 2 ([video](https://youtu.be/VxQ3PwxiSzk), [slides](https://chandlerc.blog/slides/2023-cppnow-generics-2/#/)) - Modernizing Compiler Design for Carbon’s Toolchain, C++Now ([video](https://youtu.be/ZI198eFghJk), [slides](https://chandlerc.blog/slides/2023-cppnow-compiler/index.html#/)) ### 2022 - Carbon Language: Syntax and trade-offs, Core C++ ([video](https://youtu.be/9Y2ivB8VaIs), [slides](https://docs.google.com/presentation/d/1znvL12xCuEfcsP6tpPdrQPnh-UoPFOLnC_RVXZteYaM/edit)) - Carbon Language: An experimental successor to C++, CppNorth ([video](https://youtu.be/omrY53kbVoA), [slides](https://chandlerc.blog/slides/2022-07-19-cppnorth-keynote/#/)) ### Other videos We additionally have [toolchain videos](/toolchain/docs/README.md#videos). ## Join us We'd love to have folks join us and contribute to the project. Carbon is committed to a welcoming and inclusive environment where everyone can contribute. - Most of Carbon's design discussions occur on [Discord](https://discord.gg/ZjVdShJDAs). - To watch for major release announcements, subscribe to our [Carbon release post on GitHub](https://github.com/carbon-language/carbon-lang/discussions/1020) and [star carbon-lang](https://github.com/carbon-language/carbon-lang). - See our [code of conduct](CODE_OF_CONDUCT.md) and [contributing guidelines](CONTRIBUTING.md) for information about the Carbon development community. ### Contributing You can also directly: - [Contribute to the language design](CONTRIBUTING.md#contributing-to-the-language-design): feedback on design, new design proposal - [Contribute to the language implementation](CONTRIBUTING.md#contributing-to-the-language-implementation) - [Carbon Toolchain](/toolchain/), and project infrastructure You can **check out some ["good first issues"](https://github.com/carbon-language/carbon-lang/labels/good%20first%20issue)**, or join the `#contributing-help` channel on [Discord](https://discord.gg/ZjVdShJDAs). See our full [`CONTRIBUTING`](CONTRIBUTING.md) documentation for more details. ================================================ FILE: SECURITY.md ================================================ # Security policy It's important to us that the Carbon Language provides a secure implementation. Thank you for taking the time to report vulnerabilities. The Carbon Language is still an [experimental project](/README.md#project-status), so please be careful if using it in security-sensitive environments. ## Reporting a vulnerability Please use to report security vulnerabilities. We use GitHub's vulnerability reporting for intake. We will respond to reports within two weeks. For valid issues we will coordinate and disclose on GitHub. If you haven't received a response, a couple steps to take are (in order): 1. Contact individuals directly: - [Chandler Carruth](mailto:chandlerc@gmail.com) - [Richard Smith](mailto:richard@metafoo.co.uk) - [Jon Ross-Perkins](mailto:jperkins@google.com) 2. Reach out on [#infra](https://discord.com/channels/655572317891461132/707150492370862090) on Discord ([invite](https://discord.gg/ZjVdShJDAs)) - This is a public forum, so say you're asking for a security contact rather than talking about the security issue directly. ================================================ FILE: bazel/carbon_rules/BUILD ================================================ # Part of the Carbon Language project, under the Apache License v2.0 with LLVM # Exceptions. See /LICENSE for license information. # SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception load("@bazel_skylib//rules:common_settings.bzl", "bool_flag") package(default_visibility = ["//visibility:public"]) # Flag controlling whether the target config is used for the `carbon_*` Bazel # rules. The default is to use the exec config as that is more correct in cases # where the target config is not compatible with the exec (cross compiling), and # for library users of Carbon likely the most efficient as it will provide an # optimized toolchain. # # However, for building the Carbon project itself, this will roughly double the # build cost by forcing a build in both target and exec config. As a consequence # we disable the flag in the `.bazelrc` of the project for its builds. bool_flag( name = "use_target_config_carbon_rules", build_setting_default = False, ) config_setting( name = "use_target_config_carbon_rules_config", flag_values = {":use_target_config_carbon_rules": "True"}, ) ================================================ FILE: bazel/carbon_rules/defs.bzl ================================================ # Part of the Carbon Language project, under the Apache License v2.0 with LLVM # Exceptions. See /LICENSE for license information. # SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception """Provides rules for building Carbon files using the toolchain.""" load("@rules_cc//cc/common:cc_info.bzl", "CcInfo") def _runtimes_path(runtimes_target): path = None for f in runtimes_target: if f.short_path.endswith("clang_resource_dir/lib"): path = f.path break if not path: fail("Could not find the `clang_resource_dir` in target {}".format(runtimes_target.label)) return path[:-len("/clang_resource_dir/lib")] def _carbon_binary_impl(ctx): toolchain_driver = ctx.executable.internal_exec_toolchain_driver toolchain_data = ctx.files.internal_exec_toolchain_data prebuilt_runtimes = ctx.files.internal_exec_prebuilt_runtimes # If the exec driver isn't provided, that means we're trying to use a target # config toolchain, likely to avoid build overhead of two configs. if toolchain_driver == None: toolchain_driver = ctx.executable.internal_target_toolchain_driver toolchain_data = ctx.files.internal_target_toolchain_data prebuilt_runtimes = ctx.files.internal_target_prebuilt_runtimes # Pass any C++ flags from our dependencies onto Carbon. dep_flags = [] dep_hdrs = [] dep_link_flags = [] dep_link_inputs = [] for dep in ctx.attr.deps: if CcInfo in dep: cc_info = dep[CcInfo] # TODO: We should reuse the feature-based flag generation in # bazel/cc_toolchains here. dep_flags += ["--clang-arg=-D{0}".format(define) for define in cc_info.compilation_context.defines.to_list()] dep_flags += ["--clang-arg=-I{0}".format(path) for path in cc_info.compilation_context.includes.to_list()] dep_flags += ["--clang-arg=-iquote{0}".format(path) for path in cc_info.compilation_context.quote_includes.to_list()] dep_flags += ["--clang-arg=-isystem{0}".format(path) for path in cc_info.compilation_context.system_includes.to_list()] dep_hdrs.append(cc_info.compilation_context.headers) for link_input in cc_info.linking_context.linker_inputs.to_list(): # TODO: `carbon link` doesn't support linker flags yet. # dep_link_flags += link_input.user_link_flags dep_link_inputs += link_input.additional_inputs for lib in link_input.libraries: dep_link_inputs += [dep for dep in [lib.dynamic_library, lib.static_library] if dep] dep_link_inputs += lib.objects if DefaultInfo in dep: dep_link_inputs += dep[DefaultInfo].files.to_list() dep_link_flags += [dep.path for dep in dep_link_inputs] # Build object files for the prelude and for the binary itself. # TODO: Eventually the prelude should be build as a separate `carbon_library`. srcs_and_flags = [ (ctx.files.prelude_srcs, ["--no-prelude-import"]), (ctx.files.srcs, dep_flags), ] objs = [] for (srcs, extra_flags) in srcs_and_flags: for src in srcs: # Build each source file. For now, we pass all sources to each compile # because we don't have visibility into dependencies and have no way to # specify multiple output files. Object code for each input is written # into the output file in turn, so the final carbon source file # specified ends up determining the contents of the object file. # # TODO: This is a hack; replace with something better once the toolchain # supports doing so. # # TODO: Switch to the `prefix` based rule similar to linking when # the prelude moves there. out = ctx.actions.declare_file("_objs/{0}/{1}o".format( ctx.label.name, src.short_path.removeprefix(ctx.label.package).removesuffix(src.extension), )) objs.append(out) srcs_reordered = [s for s in srcs if s != src] + [src] ctx.actions.run( outputs = [out], inputs = depset(direct = srcs_reordered, transitive = dep_hdrs), executable = toolchain_driver, tools = depset(toolchain_data), arguments = ["compile", "--output=" + out.path, "--output-last-input-only"] + [s.path for s in srcs_reordered] + extra_flags + ctx.attr.flags, mnemonic = "CarbonCompile", progress_message = "Compiling " + src.short_path, ) bin = ctx.actions.declare_file(ctx.label.name) ctx.actions.run( outputs = [bin], inputs = objs + dep_link_inputs, executable = toolchain_driver, tools = depset(toolchain_data + prebuilt_runtimes), arguments = ["--prebuilt-runtimes=" + _runtimes_path(prebuilt_runtimes), "link", "--output=" + bin.path] + ["--"] + dep_link_flags + [o.path for o in objs], mnemonic = "CarbonLink", progress_message = "Linking " + bin.short_path, ) return [DefaultInfo(files = depset([bin]), executable = bin)] _carbon_binary_internal = rule( implementation = _carbon_binary_impl, attrs = { "deps": attr.label_list(allow_files = True, providers = [[CcInfo]]), "flags": attr.string_list(), # The exec config toolchain attributes. These will be `None` when using # the target config and populated when using the exec config. We have to # use duplicate attributes here and below to have different `cfg` # settings, as that isn't `select`-able, and we'll use `select`s when # populating these. "internal_exec_prebuilt_runtimes": attr.label( cfg = "exec", ), "internal_exec_toolchain_data": attr.label( cfg = "exec", ), "internal_exec_toolchain_driver": attr.label( allow_single_file = True, executable = True, cfg = "exec", ), # The target config toolchain attributes. These will be 'None' when # using the exec config and populated when using the target config. We # have to use duplicate attributes here and below to have different # `cfg` settings, as that isn't `select`-able, and we'll use `select`s # when populating these. "internal_target_prebuilt_runtimes": attr.label( cfg = "target", ), "internal_target_toolchain_data": attr.label( cfg = "target", ), "internal_target_toolchain_driver": attr.label( allow_single_file = True, executable = True, cfg = "target", ), "prelude_srcs": attr.label_list(allow_files = [".carbon"]), "srcs": attr.label_list(allow_files = [".carbon"]), "_cc_toolchain": attr.label(default = "@bazel_tools//tools/cpp:current_cc_toolchain"), }, executable = True, ) def carbon_binary(name, srcs, deps = [], flags = [], tags = []): """Compiles a Carbon binary. Args: name: The name of the build target. srcs: List of Carbon source files to compile. deps: List of dependencies. flags: Extra flags to pass to the Carbon compile command. tags: Tags to apply to the rule. """ _carbon_binary_internal( name = name, srcs = srcs, prelude_srcs = ["//core:prelude_files"], deps = deps, flags = flags, tags = tags, # We synthesize two sets of attributes from mirrored `select`s here # because we want to select on an internal property of these attributes # but that isn't `select`-able. Instead, we have both attributes and # `select` which one we use. internal_exec_toolchain_driver = select({ "//bazel/carbon_rules:use_target_config_carbon_rules_config": None, "//conditions:default": "//toolchain/install:prefix/bin/carbon", }), internal_exec_toolchain_data = select({ "//bazel/carbon_rules:use_target_config_carbon_rules_config": None, "//conditions:default": "//toolchain/install:install_data", }), internal_exec_prebuilt_runtimes = select({ "//bazel/carbon_rules:use_target_config_carbon_rules_config": None, "//conditions:default": "//toolchain/driver:prebuilt_runtimes", }), internal_target_toolchain_driver = select({ "//bazel/carbon_rules:use_target_config_carbon_rules_config": "//toolchain/install:prefix/bin/carbon", "//conditions:default": None, }), internal_target_toolchain_data = select({ "//bazel/carbon_rules:use_target_config_carbon_rules_config": "//toolchain/install:install_data", "//conditions:default": None, }), internal_target_prebuilt_runtimes = select({ "//bazel/carbon_rules:use_target_config_carbon_rules_config": "//toolchain/driver:prebuilt_runtimes", "//conditions:default": None, }), ) ================================================ FILE: bazel/cc_rules/BUILD ================================================ # Part of the Carbon Language project, under the Apache License v2.0 with LLVM # Exceptions. See /LICENSE for license information. # SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception # Empty; only defs.bzl is needed. ================================================ FILE: bazel/cc_rules/defs.bzl ================================================ # Part of the Carbon Language project, under the Apache License v2.0 with LLVM # Exceptions. See /LICENSE for license information. # SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception """Wraps standard cc rules with the `cc_env` addition. These should generally be used in place of `@rules_cc`. """ load( "@rules_cc//cc:defs.bzl", actual_cc_binary = "cc_binary", actual_cc_library = "cc_library", actual_cc_test = "cc_test", ) load("//bazel/cc_toolchains:defs.bzl", "cc_env") # Expose cc_library directly, for consistency. cc_library = actual_cc_library def cc_binary(env = {}, **kwargs): """Wraps `cc_binary`, adding `cc_env`.""" actual_cc_binary( env = cc_env() | env, **kwargs ) def cc_test(env = {}, **kwargs): """Wraps `cc_binary`, adding `cc_env`.""" actual_cc_test( env = cc_env() | env, **kwargs ) ================================================ FILE: bazel/cc_toolchains/BUILD ================================================ # Part of the Carbon Language project, under the Apache License v2.0 with LLVM # Exceptions. See /LICENSE for license information. # SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception load("@bazel_skylib//lib:selects.bzl", "selects") package(default_visibility = ["//visibility:public"]) # For use by defs.bzl. # Matches when asan is enabled on a macOS platform. selects.config_setting_group( name = "macos_asan", match_all = [ ":is_macos", ":macos_asan_build_modes", ], ) # For use by defs.bzl. # Matches macOS platforms. config_setting( name = "is_macos", constraint_values = ["@platforms//os:macos"], ) # For use by defs.bzl. # Matches build modes where asan is enabled. selects.config_setting_group( name = "macos_asan_build_modes", match_any = [ ":dbg", ":fastbuild", ], ) # For use by defs.bzl. # Matches dbg. config_setting( name = "dbg", values = {"compilation_mode": "dbg"}, ) # For use by defs.bzl. # Matches fastbuild. config_setting( name = "fastbuild", values = {"compilation_mode": "fastbuild"}, ) filegroup( name = "installed_cc_toolchain_starlark", srcs = [ "cc_toolchain_actions.bzl", "cc_toolchain_base_features.bzl", "cc_toolchain_config_features.bzl", "cc_toolchain_cpp_features.bzl", "cc_toolchain_debugging.bzl", "cc_toolchain_features.bzl", "cc_toolchain_linking.bzl", "cc_toolchain_modules.bzl", "cc_toolchain_optimization.bzl", "cc_toolchain_sanitizer_features.bzl", "cc_toolchain_tools.bzl", ], ) ================================================ FILE: bazel/cc_toolchains/cc_toolchain_actions.bzl ================================================ # Part of the Carbon Language project, under the Apache License v2.0 with LLVM # Exceptions. See /LICENSE for license information. # SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception """Useful sets of actions for defining `cc_toolchain_config` features.""" load("@rules_cc//cc:action_names.bzl", "ACTION_NAMES") all_c_compile_actions = [ ACTION_NAMES.c_compile, ACTION_NAMES.assemble, ACTION_NAMES.preprocess_assemble, ] all_cpp_compile_actions = [ ACTION_NAMES.cpp_compile, ACTION_NAMES.linkstamp_compile, ACTION_NAMES.cpp_header_parsing, ACTION_NAMES.cpp_module_compile, ACTION_NAMES.cpp_module_codegen, ] all_compile_actions = all_c_compile_actions + all_cpp_compile_actions preprocessor_compile_actions = [ ACTION_NAMES.c_compile, ACTION_NAMES.cpp_compile, ACTION_NAMES.linkstamp_compile, ACTION_NAMES.preprocess_assemble, ACTION_NAMES.cpp_header_parsing, ACTION_NAMES.cpp_module_compile, ] codegen_compile_actions = [ ACTION_NAMES.c_compile, ACTION_NAMES.cpp_compile, ACTION_NAMES.linkstamp_compile, ACTION_NAMES.assemble, ACTION_NAMES.preprocess_assemble, ACTION_NAMES.cpp_module_codegen, ] all_link_actions = [ ACTION_NAMES.cpp_link_executable, ACTION_NAMES.cpp_link_dynamic_library, ACTION_NAMES.cpp_link_nodeps_dynamic_library, ] ================================================ FILE: bazel/cc_toolchains/cc_toolchain_base_features.bzl ================================================ # Part of the Carbon Language project, under the Apache License v2.0 with LLVM # Exceptions. See /LICENSE for license information. # SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception """Definitions used for the base features of a `cc_toolchain_config`.""" load( "@rules_cc//cc:cc_toolchain_config_lib.bzl", "feature", "feature_set", "flag_group", "flag_set", ) load( ":cc_toolchain_actions.bzl", "all_compile_actions", "all_link_actions", ) # Declare features that are used by Bazel to model specific build modes. dbg_feature = feature(name = "dbg") fastbuild_feature = feature(name = "fastbuild") host_feature = feature(name = "host") opt_feature = feature(name = "opt") # Declare features that control enabling and disabling Bazel logic. no_legacy_features_feature = feature(name = "no_legacy_features") supports_pic_feature = feature(name = "supports_pic", enabled = True) supports_dynamic_linker_feature = feature( name = "supports_dynamic_linker", enabled = True, requires = [feature_set(["linux_target"])], ) supports_start_end_lib_feature = feature( name = "supports_start_end_lib", enabled = True, requires = [feature_set(["linux_target"])], ) user_flags_feature = feature( name = "user_flags", enabled = True, flag_sets = [ flag_set( actions = all_compile_actions, flag_groups = [flag_group( expand_if_available = "user_compile_flags", flags = ["%{user_compile_flags}"], iterate_over = "user_compile_flags", )], ), flag_set( actions = all_link_actions, flag_groups = [flag_group( expand_if_available = "user_link_flags", flags = ["%{user_link_flags}"], iterate_over = "user_link_flags", )], ), ], ) # TODO: It's not clear this is the right location for these flags, and it is a # little awkward. output_flags_feature = feature( name = "output_flags", enabled = True, flag_sets = [ flag_set( actions = all_compile_actions, flag_groups = [ # For compile actions we have a single source and so put it at # the end next to the output. flag_group( expand_if_available = "source_file", flags = ["%{source_file}"], ), flag_group( expand_if_available = "output_file", flags = ["-o", "%{output_file}"], ), ], ), flag_set( actions = all_link_actions, flag_groups = [flag_group( expand_if_available = "output_execpath", flags = ["-o", "%{output_execpath}"], )], ), ], ) base_features = [ dbg_feature, fastbuild_feature, host_feature, no_legacy_features_feature, opt_feature, supports_pic_feature, supports_dynamic_linker_feature, supports_start_end_lib_feature, ] ================================================ FILE: bazel/cc_toolchains/cc_toolchain_carbon_project_features.bzl ================================================ # Part of the Carbon Language project, under the Apache License v2.0 with LLVM # Exceptions. See /LICENSE for license information. # SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception """Defines `cc_toolchain_config` features specific to the Carbon project.""" load("@rules_cc//cc:action_names.bzl", "ACTION_NAMES") load( "@rules_cc//cc:cc_toolchain_config_lib.bzl", "feature", "feature_set", "flag_group", "flag_set", "with_feature_set", ) load( ":cc_toolchain_actions.bzl", "all_compile_actions", "preprocessor_compile_actions", ) # An enabled feature that requires the `fastbuild` compilation. This is used # to toggle general features on by default, while allowing them to be # directly enabled and disabled more generally as desired. carbon_project_fastbuild_feature = feature( name = "enable_in_fastbuild", enabled = True, requires = [feature_set(["fastbuild"])], implies = [ "asan", "asan_min_size", "minimal_optimization_flags", "minimal_debug_info_flags", ], ) def carbon_project_features(cache_key): return [carbon_project_fastbuild_feature, feature( name = "project_flags", enabled = True, flag_sets = [ flag_set( actions = all_compile_actions, flag_groups = [flag_group(flags = [ # Don't warn on external code as we can't # necessarily patch it easily. Note that these have # to be initial directories in the `#include` line. "--system-header-prefix=absl/", "--system-header-prefix=benchmark/", "--system-header-prefix=boost/", "--system-header-prefix=clang-tools-extra/", "--system-header-prefix=clang/", "--system-header-prefix=gmock/", "--system-header-prefix=gtest/", "--system-header-prefix=libfuzzer/", "--system-header-prefix=llvm/", "--system-header-prefix=re2/", "--system-header-prefix=tools/cpp/", "--system-header-prefix=tree_sitter/", ])], ), flag_set( actions = preprocessor_compile_actions, flag_groups = [flag_group(flags = [ # Pass a cache key as a `-D` flag to avoid unintended Bazel # cache hits when the underlying toolchain changes. # TODO: We should consider replacing this by causing changes # to the installed toolchain to more reliably end up as part # of the action digest. "-DBAZEL_COMPILE_CACHE_KEY=\"%s\"" % cache_key, ])], ), flag_set( actions = [ ACTION_NAMES.c_compile, ACTION_NAMES.cpp_compile, ACTION_NAMES.cpp_header_parsing, ACTION_NAMES.cpp_module_compile, ], flag_groups = [flag_group(flags = ["-DHAVE_MALLCTL"])], with_features = [with_feature_set(["freebsd_target"])], ), ], )] ================================================ FILE: bazel/cc_toolchains/cc_toolchain_config_features.bzl ================================================ # Part of the Carbon Language project, under the Apache License v2.0 with LLVM # Exceptions. See /LICENSE for license information. # SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception """Configuration features for other features in a `cc_toolchain_config`. These features are designed to be used by other features in a `cc_toolchain_config` that need to configure their behavior in some way. This can be configuration based on either the target or host of the build, and along multiple dimensions of each. """ load( "@rules_cc//cc:cc_toolchain_config_lib.bzl", "feature", ) os_names = [ "freebsd", "linux", "macos", "windows", ] def target_os_features(target_os): if target_os not in os_names: fail("Unsupported target OS: %s" % target_os) return [ feature(name = os_name + "_target", enabled = os_name == target_os) for os_name in os_names ] cpu_names = [ "aarch64", "x86_64", ] # Also support canonicalizing different spellings of CPUs to one of the above # names. cpu_canonical_name_map = { "aarch64": "aarch64", "arm64": "aarch64", "x86_64": "x86_64", } def target_cpu_features(target_cpu): if target_cpu not in cpu_canonical_name_map: fail("Unsupported target CPU: %s" % target_cpu) target_cpu = cpu_canonical_name_map[target_cpu] return [ feature(name = cpu_name + "_target", enabled = cpu_name == target_cpu) for cpu_name in cpu_names ] ================================================ FILE: bazel/cc_toolchains/cc_toolchain_cpp_features.bzl ================================================ # Part of the Carbon Language project, under the Apache License v2.0 with LLVM # Exceptions. See /LICENSE for license information. # SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception """Definitions of general C++ `cc_toolchain_config` features.""" load("@rules_cc//cc:action_names.bzl", "ACTION_NAMES") load( "@rules_cc//cc:cc_toolchain_config_lib.bzl", "feature", "flag_group", "flag_set", "with_feature_set", ) load( ":cc_toolchain_actions.bzl", "all_compile_actions", "all_cpp_compile_actions", "all_link_actions", "codegen_compile_actions", "preprocessor_compile_actions", ) clang_feature = feature( name = "clang", enabled = True, flag_sets = [ flag_set( actions = all_compile_actions + all_link_actions, flag_groups = [ flag_group(flags = [ "-no-canonical-prefixes", "-fcolor-diagnostics", ]), flag_group( expand_if_available = "sysroot", flags = ["--sysroot=%{sysroot}"], ), ], ), flag_set( actions = all_compile_actions, flag_groups = [ flag_group(flags = [ # Compile actions shouldn't link anything. "-c", ]), # Flags controlling the production of specific outputs from # compile actions. flag_group( expand_if_available = "output_assembly_file", flags = ["-S"], ), flag_group( expand_if_available = "output_preprocess_file", flags = ["-E"], ), flag_group( expand_if_available = "dependency_file", flags = ["-MD", "-MF", "%{dependency_file}"], ), flag_group( expand_if_available = "output_file", flags = ["-frandom-seed=%{output_file}"], ), ], ), flag_set( # Flags specific to compiling C++ sources. actions = all_cpp_compile_actions, flag_groups = [flag_group(flags = [ "-std=c++20", ])], ), flag_set( actions = codegen_compile_actions, flag_groups = [flag_group(flags = [ "-ffunction-sections", "-fdata-sections", ])], ), flag_set( actions = codegen_compile_actions, flag_groups = [flag_group( expand_if_available = "pic", flags = ["-fPIC"], )], ), flag_set( actions = preprocessor_compile_actions, flag_groups = [ flag_group(flags = [ # Disable a warning and override builtin macros to # ensure a hermetic build. "-Wno-builtin-macro-redefined", "-D__DATE__=\"redacted\"", "-D__TIMESTAMP__=\"redacted\"", "-D__TIME__=\"redacted\"", ]), flag_group( flags = ["-D%{preprocessor_defines}"], iterate_over = "preprocessor_defines", ), flag_group( expand_if_available = "includes", flags = ["-include", "%{includes}"], iterate_over = "includes", ), flag_group( flags = ["-iquote", "%{quote_include_paths}"], iterate_over = "quote_include_paths", ), flag_group( flags = ["-I%{include_paths}"], iterate_over = "include_paths", ), flag_group( flags = ["-isystem", "%{system_include_paths}"], iterate_over = "system_include_paths", ), ], ), flag_set( actions = [ ACTION_NAMES.cpp_link_dynamic_library, ACTION_NAMES.cpp_link_nodeps_dynamic_library, ], flag_groups = [flag_group(flags = ["-shared"])], ), flag_set( actions = all_link_actions, flag_groups = [ flag_group( expand_if_available = "strip_debug_symbols", flags = ["-Wl,-S"], ), flag_group( expand_if_available = "library_search_directories", flags = ["-L%{library_search_directories}"], iterate_over = "library_search_directories", ), flag_group( expand_if_available = "runtime_library_search_directories", iterate_over = "runtime_library_search_directories", flags = [ "-Wl,-rpath,$ORIGIN/%{runtime_library_search_directories}", ], ), ], ), flag_set( actions = all_link_actions, flag_groups = [ flag_group( flags = [ "-fuse-ld=lld", # Force the C++ standard library and runtime libraries # to be statically linked. This works even with libc++ # and libunwind despite the names, provided libc++ is # built with the CMake option: # - `-DCMAKE_POSITION_INDEPENDENT_CODE=ON` "-static-libstdc++", "-static-libgcc", # Link with Clang's runtime library. This is always # linked statically. "-rtlib=compiler-rt", # Link with pthread. "-lpthread", ], ), ], with_features = [with_feature_set(["linux_target"])], ), flag_set( actions = [ACTION_NAMES.cpp_link_executable], flag_groups = [flag_group( expand_if_available = "force_pic", flags = ["-pie"], )], with_features = [with_feature_set([ "linux_target", "freebsd_target", ])], ), flag_set( actions = [ACTION_NAMES.cpp_link_executable], flag_groups = [flag_group( expand_if_available = "force_pic", flags = ["-fpie"], )], with_features = [with_feature_set(["macos_target"])], ), ], ) clang_warnings_feature = feature( name = "clang_warnings", enabled = True, flag_sets = [flag_set( actions = all_compile_actions, flag_groups = [flag_group(flags = [ "-Werror", "-Wall", "-Wextra", "-Wthread-safety", "-Wself-assign", "-Wimplicit-fallthrough", "-Wctad-maybe-unsupported", "-Wextra-semi", "-Wmissing-prototypes", "-Wzero-as-null-pointer-constant", "-Wdelete-non-virtual-dtor", # TODO: Regression that warns on anonymous unions; remove depending # on fix. "-Wno-missing-designated-field-initializers", ])], )], ) # Libc++ HARDENING_MODE has 4 possible values: # https://libcxx.llvm.org/Hardening.html#notes-for-users # # Do not enable DEBUG hardening mode, even for -c dbg, because its performance # impact on llvm-symbolizer is too severe -- this flag results in symbolization # becoming quadratic in the number of debug symbols, in practice meaning it # never completes. _libcpp_debug_flags = [ "-D_LIBCPP_HARDENING_MODE=_LIBCPP_HARDENING_MODE_EXTENSIVE", ] _libcpp_release_flags = [ "-D_LIBCPP_HARDENING_MODE=_LIBCPP_HARDENING_MODE_FAST", ] def libcxx_feature(llvm_bindir = None, clang_bindir = None): """Builds a libc++ feature. Returns: The feature for use with `cc_toolchain_config`. Args: llvm_bindir: Optional LLVM installation `bin` directory, causes the feature to look for adjacent installed libraries. clang_bindir: Optional Clang installation `bin` directory, causes the feature to look for adjacent installed libraries if different from `llvm_bindir`. """ # Explicitly add LLVM libs to the search path to preempt the # detected GCC installation's library paths. Those might have a # system installed libc++ and we want to find the one next to # our Clang. extra_link_flags = [] if llvm_bindir: extra_link_flags.append("-L" + llvm_bindir + "/../lib") if clang_bindir and clang_bindir != llvm_bindir: extra_link_flags.append("-L" + clang_bindir + "/../lib") return feature( name = "libcxx", enabled = True, flag_sets = [ flag_set( actions = all_cpp_compile_actions + all_link_actions, flag_groups = [flag_group(flags = [ "-stdlib=libc++", ])], with_features = [ # libc++ is only used on non-Windows platforms. with_feature_set(not_features = ["windows_target"]), ], ), flag_set( actions = all_cpp_compile_actions, flag_groups = [flag_group(flags = _libcpp_debug_flags)], with_features = [with_feature_set(not_features = ["opt"])], ), flag_set( actions = all_cpp_compile_actions, flag_groups = [flag_group(flags = _libcpp_release_flags)], with_features = [with_feature_set(features = ["opt"])], ), flag_set( actions = all_link_actions, flag_groups = [flag_group(flags = [ "-unwindlib=libunwind", ])], with_features = [ # libc++ is only used on non-Windows platforms. with_feature_set(not_features = ["windows_target"]), ], ), flag_set( actions = all_link_actions, flag_groups = [flag_group(flags = extra_link_flags + [ # Force linking the static libc++abi archive here. This # *should* be linked automatically, but not every release of # LLVM correctly sets the CMake flags to do so. "-l:libc++abi.a", ])], with_features = [with_feature_set(["linux_target"])], ), ], ) ================================================ FILE: bazel/cc_toolchains/cc_toolchain_debugging.bzl ================================================ # Part of the Carbon Language project, under the Apache License v2.0 with LLVM # Exceptions. See /LICENSE for license information. # SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception """Definitions of debugging related features used in a `cc_toolchain_config`.""" load( "@rules_cc//cc:cc_toolchain_config_lib.bzl", "feature", "feature_set", "flag_group", "flag_set", ) load( ":cc_toolchain_actions.bzl", "all_link_actions", "codegen_compile_actions", ) # Handle different levels and forms of debug info emission with individual # features so that they can be ordered and the defaults can override the # minimal settings if both are enabled. minimal_debug_info_flags = feature( name = "minimal_debug_info_flags", implies = ["debug_info_compression_flags"], flag_sets = [flag_set( actions = codegen_compile_actions, flag_groups = [flag_group(flags = ["-gmlt"])], )], ) debug_info_flags = feature( name = "debug_info_flags", implies = ["debug_info_compression_flags"], flag_sets = [flag_set( actions = codegen_compile_actions, flag_groups = [ flag_group(flags = ["-g"]), flag_group( expand_if_available = "per_object_debug_info_file", flags = ["-gsplit-dwarf"], ), ], )], ) debug_info_compression_flags = feature( name = "debug_info_compression_flags", flag_sets = [flag_set( actions = codegen_compile_actions + all_link_actions, flag_groups = [flag_group(flags = ["-gz"])], )], ) # Define a set of mutually exclusive debugger flags. debugger_flags = feature(name = "debugger_flags") lldb_flags = feature( # Use a convenient name for users to select if needed. name = "lldb_flags", # Default enable LLDB-optimized flags whenever debugging. enabled = True, requires = [feature_set(features = ["debug_info_flags"])], provides = ["debugger_flags"], flag_sets = [flag_set( actions = codegen_compile_actions, flag_groups = [flag_group(flags = [ "-glldb", "-gpubnames", "-gsimple-template-names", ])], )], ) gdb_flags = feature( # Use a convenient name for users to select if needed. name = "gdb_flags", requires = [feature_set(features = ["debug_info_flags"])], provides = ["debugger_flags"], flag_sets = [ flag_set( actions = codegen_compile_actions, flag_groups = [flag_group(flags = [ "-ggdb", "-ggnu-pubnames", ])], ), flag_set( actions = all_link_actions, flag_groups = [flag_group(flags = ["-Wl,--gdb-index"])], ), ], ) # This feature can be enabled in conjunction with any optimizations to # ensure accurate call stacks and backtraces for profilers or errors. preserve_call_stacks = feature( name = "preserve_call_stacks", flag_sets = [flag_set( actions = codegen_compile_actions, flag_groups = [flag_group(flags = [ # Ensure good backtraces by preserving frame pointers and # disabling tail call elimination. "-fno-omit-frame-pointer", "-mno-omit-leaf-frame-pointer", "-fno-optimize-sibling-calls", ])], )], ) # Enable split debug info whenever debug info is requested. enable_split_debug_info = feature( name = "per_object_debug_info", enabled = True, # This has to be directly conditioned on requesting debug info at # all, otherwise Bazel will look for an extra output file and not # find one. requires = [feature_set(features = ["debug_info_flags"])], ) # Enable debug info whenever in the `dbg` build mode. We do this separately from # the `debug_info_flags` feature itself as other things may want to enable that # feature as well. enable_debug_info_in_dbg = feature( name = "enable_debug_info_in_dbg", enabled = True, requires = [feature_set(["dbg"])], implies = ["debug_info_flags"], ) # Note that the order of features is significant in this list and determines the # relative order of flags from the features listed. debugging_features = [ minimal_debug_info_flags, debug_info_flags, debug_info_compression_flags, debugger_flags, lldb_flags, gdb_flags, preserve_call_stacks, enable_split_debug_info, enable_debug_info_in_dbg, ] ================================================ FILE: bazel/cc_toolchains/cc_toolchain_features.bzl ================================================ # Part of the Carbon Language project, under the Apache License v2.0 with LLVM # Exceptions. See /LICENSE for license information. # SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception """Helpers to construct ordered sequences of `cc_toolchain` features.""" load( ":cc_toolchain_base_features.bzl", "base_features", "output_flags_feature", "user_flags_feature", ) load( ":cc_toolchain_config_features.bzl", "target_cpu_features", "target_os_features", ) load( ":cc_toolchain_cpp_features.bzl", "clang_feature", "clang_warnings_feature", ) load(":cc_toolchain_debugging.bzl", "debugging_features") load(":cc_toolchain_linking.bzl", "linking_features") load(":cc_toolchain_modules.bzl", "modules_features") load(":cc_toolchain_optimization.bzl", "optimization_features") load(":cc_toolchain_sanitizer_features.bzl", "sanitizer_features") def clang_cc_toolchain_features( target_os, target_cpu, project_features = [], extra_cpp_features = []): """Builds a sequence of Clang-oriented `cc_toolchain_config` features. Returns: The list of features for calling `create_cc_toolchain_config_info`. Args: target_os: Used to select OS-specific features to include. target_cpu: Used to select CPU-specific features to include. project_features: Optional list of project-specific features to include. extra_cpp_features: Optional list of extra C++ features to include, for example `libcxx_feature` can be passed here to enable using libc++. """ # The order of the features determines the relative order of flags used. features = [] features += target_os_features(target_os) features += target_cpu_features(target_cpu) features += base_features features += [ # We always use Clang in the toolchain and enable all of its warnings. clang_feature, clang_warnings_feature, ] # Enable any extra baseline C++ features here where others can override # their flags if needed. features += extra_cpp_features features += sanitizer_features features += optimization_features features += modules_features features += debugging_features features += linking_features # Lastly, we add project features and the user flags so they can override # anything above, and the output flags last of all for ease of debugging. features += project_features features += [ user_flags_feature, output_flags_feature, ] return features ================================================ FILE: bazel/cc_toolchains/cc_toolchain_linking.bzl ================================================ # Part of the Carbon Language project, under the Apache License v2.0 with LLVM # Exceptions. See /LICENSE for license information. # SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception """Definitions of linking related features used in a `cc_toolchain_config`.""" load("@rules_cc//cc:action_names.bzl", "ACTION_NAMES") load( "@rules_cc//cc:cc_toolchain_config_lib.bzl", "feature", "flag_group", "flag_set", "variable_with_value", "with_feature_set", ) load( ":cc_toolchain_actions.bzl", "all_link_actions", ) link_libraries_feature = feature( name = "link_libraries", enabled = True, flag_sets = [ flag_set( actions = all_link_actions, flag_groups = [ flag_group( expand_if_available = "linkstamp_paths", flags = ["%{linkstamp_paths}"], iterate_over = "linkstamp_paths", ), flag_group( expand_if_available = "libraries_to_link", flag_groups = [ flag_group( expand_if_equal = variable_with_value( name = "libraries_to_link.type", value = "object_file_group", ), flags = ["-Wl,--start-lib"], ), flag_group( expand_if_true = "libraries_to_link.is_whole_archive", flags = ["-Wl,-whole-archive"], ), flag_group( expand_if_equal = variable_with_value( name = "libraries_to_link.type", value = "object_file_group", ), flags = ["%{libraries_to_link.object_files}"], iterate_over = "libraries_to_link.object_files", ), flag_group( expand_if_equal = variable_with_value( name = "libraries_to_link.type", value = "object_file", ), flags = ["%{libraries_to_link.name}"], ), flag_group( expand_if_equal = variable_with_value( name = "libraries_to_link.type", value = "interface_library", ), flags = ["%{libraries_to_link.name}"], ), flag_group( expand_if_equal = variable_with_value( name = "libraries_to_link.type", value = "static_library", ), flags = ["%{libraries_to_link.name}"], ), flag_group( expand_if_equal = variable_with_value( name = "libraries_to_link.type", value = "dynamic_library", ), flags = ["-l%{libraries_to_link.name}"], ), flag_group( expand_if_equal = variable_with_value( name = "libraries_to_link.type", value = "versioned_dynamic_library", ), flags = ["-l:%{libraries_to_link.name}"], ), flag_group( expand_if_true = "libraries_to_link.is_whole_archive", flags = ["-Wl,-no-whole-archive"], ), flag_group( expand_if_equal = variable_with_value( name = "libraries_to_link.type", value = "object_file_group", ), flags = ["-Wl,--end-lib"], ), ], iterate_over = "libraries_to_link", ), # Note that the params file comes at the end, after the # libraries to link above. flag_group( expand_if_available = "linker_param_file", flags = ["@%{linker_param_file}"], ), ], with_features = [with_feature_set(not_features = ["macos_target"])], ), flag_set( actions = all_link_actions, flag_groups = [ flag_group( expand_if_available = "linkstamp_paths", flags = ["%{linkstamp_paths}"], iterate_over = "linkstamp_paths", ), flag_group( expand_if_available = "libraries_to_link", flag_groups = [ flag_group( expand_if_equal = variable_with_value( name = "libraries_to_link.type", value = "object_file_group", ), flags = ["-Wl,--start-lib"], ), flag_group( expand_if_equal = variable_with_value( name = "libraries_to_link.type", value = "object_file_group", ), flag_groups = [ flag_group( expand_if_false = "libraries_to_link.is_whole_archive", flags = ["%{libraries_to_link.object_files}"], ), flag_group( expand_if_true = "libraries_to_link.is_whole_archive", flags = ["-Wl,-force_load,%{libraries_to_link.object_files}"], ), ], iterate_over = "libraries_to_link.object_files", ), flag_group( expand_if_equal = variable_with_value( name = "libraries_to_link.type", value = "object_file", ), flag_groups = [ flag_group( expand_if_false = "libraries_to_link.is_whole_archive", flags = ["%{libraries_to_link.name}"], ), flag_group( expand_if_true = "libraries_to_link.is_whole_archive", flags = ["-Wl,-force_load,%{libraries_to_link.name}"], ), ], ), flag_group( expand_if_equal = variable_with_value( name = "libraries_to_link.type", value = "interface_library", ), flag_groups = [ flag_group( expand_if_false = "libraries_to_link.is_whole_archive", flags = ["%{libraries_to_link.name}"], ), flag_group( expand_if_true = "libraries_to_link.is_whole_archive", flags = ["-Wl,-force_load,%{libraries_to_link.name}"], ), ], ), flag_group( expand_if_equal = variable_with_value( name = "libraries_to_link.type", value = "static_library", ), flag_groups = [ flag_group( expand_if_false = "libraries_to_link.is_whole_archive", flags = ["%{libraries_to_link.name}"], ), flag_group( expand_if_true = "libraries_to_link.is_whole_archive", flags = ["-Wl,-force_load,%{libraries_to_link.name}"], ), ], ), flag_group( expand_if_equal = variable_with_value( name = "libraries_to_link.type", value = "dynamic_library", ), flags = ["-l%{libraries_to_link.name}"], ), flag_group( expand_if_equal = variable_with_value( name = "libraries_to_link.type", value = "versioned_dynamic_library", ), flags = ["-l:%{libraries_to_link.name}"], ), flag_group( expand_if_true = "libraries_to_link.is_whole_archive", flag_groups = [ flag_group( expand_if_false = "macos_flags", flags = ["-Wl,-no-whole-archive"], ), ], ), flag_group( expand_if_equal = variable_with_value( name = "libraries_to_link.type", value = "object_file_group", ), flags = ["-Wl,--end-lib"], ), ], iterate_over = "libraries_to_link", ), # Note that the params file comes at the end, after the # libraries to link above. flag_group( expand_if_available = "linker_param_file", flags = ["@%{linker_param_file}"], ), ], with_features = [with_feature_set(["macos_target"])], ), ], ) # Archive actions have an entirely independent set of flags and don't # interact with either compiler or link actions. archiving_feature = feature( name = "archiving", enabled = True, flag_sets = [flag_set( actions = [ACTION_NAMES.cpp_link_static_library], flag_groups = [ flag_group(flags = ["rcsD"]), flag_group( expand_if_available = "output_execpath", flags = ["%{output_execpath}"], ), flag_group( expand_if_available = "libraries_to_link", flag_groups = [ flag_group( expand_if_equal = variable_with_value( name = "libraries_to_link.type", value = "object_file", ), flags = ["%{libraries_to_link.name}"], ), flag_group( expand_if_equal = variable_with_value( name = "libraries_to_link.type", value = "object_file_group", ), flags = ["%{libraries_to_link.object_files}"], iterate_over = "libraries_to_link.object_files", ), ], iterate_over = "libraries_to_link", ), flag_group( expand_if_available = "linker_param_file", flags = ["@%{linker_param_file}"], ), ], )], ) # Note that the order of features is significant in this list and determines the # relative order of flags from the features listed. linking_features = [ link_libraries_feature, archiving_feature, ] ================================================ FILE: bazel/cc_toolchains/cc_toolchain_modules.bzl ================================================ # Part of the Carbon Language project, under the Apache License v2.0 with LLVM # Exceptions. See /LICENSE for license information. # SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception """Definitions of C++ and Clang header modules toolchain features.""" load("@rules_cc//cc:action_names.bzl", "ACTION_NAMES") load( "@rules_cc//cc:cc_toolchain_config_lib.bzl", "feature", "feature_set", "flag_group", "flag_set", ) use_module_maps = feature( name = "use_module_maps", requires = [feature_set(features = ["module_maps"])], flag_sets = [ flag_set( actions = [ ACTION_NAMES.c_compile, ACTION_NAMES.cpp_compile, ACTION_NAMES.cpp_header_parsing, ACTION_NAMES.cpp_module_compile, ], flag_groups = [ # These flag groups are separate so they do not expand to # the cross product of the variables. flag_group(flags = ["-fmodule-name=%{module_name}"]), flag_group( flags = ["-fmodule-map-file=%{module_map_file}"], ), ], ), ], ) # Tell bazel we support module maps in general, so they will be generated # for all c/c++ rules. # Note: not all C++ rules support module maps; thus, do not imply this # feature from other features - instead, require it. module_maps = feature( name = "module_maps", enabled = True, implies = [ # "module_map_home_cwd", # "module_map_without_extern_module", # "generate_submodules", ], ) layering_check = feature( name = "layering_check", implies = ["use_module_maps"], flag_sets = [flag_set( actions = [ ACTION_NAMES.c_compile, ACTION_NAMES.cpp_compile, ACTION_NAMES.cpp_header_parsing, ACTION_NAMES.cpp_module_compile, ], flag_groups = [ flag_group(flags = [ "-fmodules-strict-decluse", "-Wprivate-header", ]), flag_group( iterate_over = "dependent_module_map_files", flags = ["-fmodule-map-file=%{dependent_module_map_files}"], ), ], )], ) # Note that the order of features is significant in this list and determines the # relative order of flags from the features listed. modules_features = [ layering_check, module_maps, use_module_maps, ] ================================================ FILE: bazel/cc_toolchains/cc_toolchain_optimization.bzl ================================================ # Part of the Carbon Language project, under the Apache License v2.0 with LLVM # Exceptions. See /LICENSE for license information. # SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception """Definitions of optimization `cc_toolchain_config` features.""" load( "@rules_cc//cc:cc_toolchain_config_lib.bzl", "feature", "feature_set", "flag_group", "flag_set", "with_feature_set", ) load( ":cc_toolchain_actions.bzl", "all_compile_actions", "all_link_actions", "codegen_compile_actions", ) # Handle different levels of optimization with individual features so that # they can be ordered and the defaults can override the minimal settings if # both are enabled. minimal_optimization_flags = feature( name = "minimal_optimization_flags", flag_sets = [flag_set( actions = codegen_compile_actions, flag_groups = [flag_group(flags = ["-O1"])], )], ) default_optimization_flags = feature( name = "default_optimization_flags", enabled = True, requires = [feature_set(["opt"])], flag_sets = [ flag_set( actions = all_compile_actions, flag_groups = [flag_group(flags = ["-DNDEBUG"])], ), flag_set( actions = codegen_compile_actions, flag_groups = [flag_group(flags = ["-O3"])], ), ], ) cpu_flags = feature( name = "aarch64_cpu_flags", enabled = True, flag_sets = [ flag_set( actions = all_compile_actions + all_link_actions, flag_groups = [flag_group(flags = ["-march=armv8.2-a"])], with_features = [with_feature_set(["aarch64_target"])], ), flag_set( actions = all_compile_actions + all_link_actions, flag_groups = [flag_group(flags = ["-march=x86-64-v2"])], with_features = [with_feature_set(["x86_64_target"])], ), ], ) # Note that the order of features is significant in this list and determines the # relative order of flags from the features listed. optimization_features = [ minimal_optimization_flags, default_optimization_flags, cpu_flags, ] ================================================ FILE: bazel/cc_toolchains/cc_toolchain_sanitizer_features.bzl ================================================ # Part of the Carbon Language project, under the Apache License v2.0 with LLVM # Exceptions. See /LICENSE for license information. # SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception """Definitions of sanitizer-related `cc_toolchain_config` features.""" load( "@rules_cc//cc:cc_toolchain_config_lib.bzl", "feature", "feature_set", "flag_group", "flag_set", "with_feature_set", ) load( ":cc_toolchain_actions.bzl", "all_compile_actions", "all_link_actions", ) sanitizer_common_flags = feature( name = "sanitizer_common_flags", implies = ["minimal_debug_info_flags", "preserve_call_stacks"], flag_sets = [flag_set( actions = all_link_actions, flag_groups = [flag_group(flags = ["-static-libsan"])], with_features = [ with_feature_set(["linux_target"]), with_feature_set(["freebsd_target"]), ], )], ) asan = feature( name = "asan", implies = ["sanitizer_common_flags"], flag_sets = [flag_set( actions = all_compile_actions + all_link_actions, flag_groups = [flag_group(flags = [ "-fsanitize=address,undefined,nullability", "-fsanitize-address-use-after-scope", # Outlining is almost always the right tradeoff for our # sanitizer usage where we're more pressured on generated code # size than runtime performance. "-fsanitize-address-outline-instrumentation", # We don't need the recovery behavior of UBSan as we expect # builds to be clean. Not recovering is a bit cheaper. "-fno-sanitize-recover=undefined,nullability", # Don't embed the full path name for files. This limits the size # and combined with line numbers is unlikely to result in many # ambiguities. "-fsanitize-undefined-strip-path-components=-1", # Needed due to clang AST issues, such as in # clang/AST/Redeclarable.h line 199. "-fno-sanitize=vptr", ])], )], ) # A feature that further reduces the generated code size of our the ASan # feature, but at the cost of lower quality diagnostics. This is enabled # along with ASan in our fastbuild configuration, but can be disabled # explicitly to get better error messages. asan_min_size = feature( name = "asan_min_size", requires = [feature_set(["asan"])], flag_sets = [flag_set( actions = all_compile_actions + all_link_actions, flag_groups = [flag_group(flags = [ # Force two UBSan checks that have especially large code size # cost to use the minimal branch to a trapping instruction model # instead of the full diagnostic. "-fsanitize-trap=alignment,null", ])], )], ) fuzzer = feature( name = "fuzzer", flag_sets = [flag_set( actions = all_compile_actions + all_link_actions, flag_groups = [flag_group(flags = [ "-fsanitize=fuzzer-no-link", ])], )], ) sanitizer_workarounds = feature( name = "sanitizer_workarounds", enabled = True, requires = [feature_set(["asan"])], flag_sets = [flag_set( actions = all_compile_actions + all_link_actions, flag_groups = [flag_group(flags = [ # Likely due to being unable to use the static-linked and up-to-date # sanitizer runtimes, we have to disable this sanitizer on macOS. "-fno-sanitize=function", ])], with_features = [with_feature_set(["macos_target"])], )], ) # Note that the order of features is significant in this list and determines the # relative order of flags from the features listed. sanitizer_features = [ sanitizer_common_flags, asan, asan_min_size, fuzzer, # Note that the workarounds must come last here to override earlier flags. sanitizer_workarounds, ] ================================================ FILE: bazel/cc_toolchains/cc_toolchain_tools.bzl ================================================ # Part of the Carbon Language project, under the Apache License v2.0 with LLVM # Exceptions. See /LICENSE for license information. # SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception """Macros to produce tool-related parts of a `cc_toolchain_config`. These macros cover both the `actions_config` array and the `tool_paths` array. They presume an LLVM and Clang toolchain's tools, but support both a single installation and installations that split the LLVM tools and Clang tools apart. """ load("@rules_cc//cc:action_names.bzl", "ACTION_NAMES") load( "@rules_cc//cc:cc_toolchain_config_lib.bzl", "action_config", "tool", "tool_path", ) load( ":cc_toolchain_actions.bzl", "all_c_compile_actions", "all_cpp_compile_actions", "all_link_actions", ) def llvm_tool_paths(llvm_bindir, clang_bindir = None): if not clang_bindir: clang_bindir = llvm_bindir return [ tool_path(name = "ar", path = llvm_bindir + "/llvm-ar"), tool_path(name = "ld", path = clang_bindir + "/ld.lld"), tool_path(name = "cpp", path = clang_bindir + "/clang-cpp"), tool_path(name = "gcc", path = clang_bindir + "/clang++"), tool_path(name = "dwp", path = llvm_bindir + "/llvm-dwp"), tool_path(name = "gcov", path = llvm_bindir + "/llvm-cov"), tool_path(name = "nm", path = llvm_bindir + "/llvm-nm"), tool_path(name = "objcopy", path = llvm_bindir + "/llvm-objcopy"), tool_path(name = "objdump", path = llvm_bindir + "/llvm-objdump"), tool_path(name = "strip", path = llvm_bindir + "/llvm-strip"), ] def llvm_action_configs(llvm_bindir, clang_bindir = None): if not clang_bindir: clang_bindir = llvm_bindir return [ action_config( action_name = name, enabled = True, tools = [tool(path = clang_bindir + "/clang")], ) for name in all_c_compile_actions ] + [ action_config( action_name = name, enabled = True, tools = [tool(path = clang_bindir + "/clang++")], ) for name in all_cpp_compile_actions ] + [ action_config( action_name = name, enabled = True, tools = [tool(path = clang_bindir + "/clang++")], ) for name in all_link_actions ] + [ action_config( action_name = name, enabled = True, tools = [tool(path = llvm_bindir + "/llvm-ar")], ) for name in [ACTION_NAMES.cpp_link_static_library] ] + [ action_config( action_name = name, enabled = True, tools = [tool(path = llvm_bindir + "/llvm-strip")], ) for name in [ACTION_NAMES.strip] ] ================================================ FILE: bazel/cc_toolchains/clang_cc_toolchain_config.bzl ================================================ # Part of the Carbon Language project, under the Apache License v2.0 with LLVM # Exceptions. See /LICENSE for license information. # SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception """A Starlark cc_toolchain configuration rule""" load("@rules_cc//cc:defs.bzl", "cc_toolchain") load("@rules_cc//cc/common:cc_common.bzl", "cc_common") load(":cc_toolchain_carbon_project_features.bzl", "carbon_project_features") load(":cc_toolchain_cpp_features.bzl", "libcxx_feature") load(":cc_toolchain_features.bzl", "clang_cc_toolchain_features") load( ":cc_toolchain_tools.bzl", "llvm_action_configs", "llvm_tool_paths", ) load( ":clang_detected_variables.bzl", "clang_bindir", "clang_include_dirs_list", "clang_resource_dir", "clang_version_for_cache", "llvm_bindir", "sysroot_dir", ) def _impl(ctx): # Only use a sysroot if one was found when detecting Clang. sysroot = None if sysroot_dir != "None": sysroot = sysroot_dir identifier = "local-{0}-{1}".format(ctx.attr.target_cpu, ctx.attr.target_os) return cc_common.create_cc_toolchain_config_info( ctx = ctx, features = clang_cc_toolchain_features( target_os = ctx.attr.target_os, target_cpu = ctx.attr.target_cpu, project_features = carbon_project_features(clang_version_for_cache), extra_cpp_features = [libcxx_feature(llvm_bindir, clang_bindir)], ), action_configs = llvm_action_configs(llvm_bindir, clang_bindir), cxx_builtin_include_directories = clang_include_dirs_list + [ # Add Clang's resource directory to the end of the builtin include # directories to cover the use of sanitizer resource files by the # driver. clang_resource_dir + "/share", ], builtin_sysroot = sysroot, # This configuration only supports local non-cross builds so derive # everything from the target CPU selected. toolchain_identifier = identifier, # This is used to expose a "flag" that `config_setting` rules can use to # determine if the compiler is Clang. compiler = "clang", # We do have to pass in our tool paths. tool_paths = llvm_tool_paths(llvm_bindir, clang_bindir), ) cc_toolchain_config = rule( implementation = _impl, attrs = { "target_cpu": attr.string(mandatory = True), "target_os": attr.string(mandatory = True), }, provides = [CcToolchainConfigInfo], ) def cc_local_toolchain_suite(name, configs): """Create a toolchain suite that uses the local Clang/LLVM install. Args: name: The name of the toolchain suite to produce. configs: An array of (os, cpu) pairs to support in the toolchain. """ # An empty filegroup to use when stubbing out the toolchains. native.filegroup( name = name + "_empty", srcs = [], ) # Create the individual local toolchains for each CPU. for (os, cpu) in configs: config_name = "{0}_{1}_{2}".format(name, os, cpu) cc_toolchain_config( name = config_name + "_config", target_os = os, target_cpu = cpu, ) cc_toolchain( name = config_name + "_tools", all_files = ":" + name + "_empty", ar_files = ":" + name + "_empty", as_files = ":" + name + "_empty", compiler_files = ":" + name + "_empty", dwp_files = ":" + name + "_empty", linker_files = ":" + name + "_empty", objcopy_files = ":" + name + "_empty", strip_files = ":" + name + "_empty", supports_param_files = 1, toolchain_config = ":" + config_name + "_config", toolchain_identifier = config_name, ) compatible_with = ["@platforms//cpu:" + cpu, "@platforms//os:" + os] native.toolchain( name = config_name, exec_compatible_with = compatible_with, target_compatible_with = compatible_with, toolchain = config_name + "_tools", toolchain_type = "@bazel_tools//tools/cpp:toolchain_type", ) ================================================ FILE: bazel/cc_toolchains/clang_configuration.bzl ================================================ # Part of the Carbon Language project, under the Apache License v2.0 with LLVM # Exceptions. See /LICENSE for license information. # SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception """Starlark repository rules to configure Clang (and LLVM) toolchain for Bazel. These rules should be run from the `WORKSPACE` file to substitute appropriate configured values into a `clang_detected_variables.bzl` file that can be used by the actual toolchain configuration. """ def _run(repository_ctx, cmd): """Runs the provided `cmd`, checks for failure, and returns the result.""" exec_result = repository_ctx.execute(cmd) if exec_result.return_code != 0: fail("Command failed with return code {0}: {1}\n{2}".format( exec_result.return_code, str(cmd), exec_result.stderr, )) return exec_result def _clang_version(version_output): """Returns version information, or a (None, "unknown") tuple if not found. Returns both the major version number (16) and the full version number for caching. """ clang_version = None clang_version_for_cache = "unknown" version_prefix = "clang version " version_start = version_output.find(version_prefix) if version_start == -1: # No version return (clang_version, clang_version_for_cache) version_start += len(version_prefix) # Find the newline. version_newline = version_output.find("\n", version_start) if version_newline == -1: return (clang_version, clang_version_for_cache) clang_version_for_cache = version_output[version_start:version_newline] # Find a dot to indicate something like 'clang version 16.0.1', and grab the # major version. version_dot = version_output.find(".", version_start) if version_dot != -1 and version_dot < version_newline: clang_version = int(version_output[version_start:version_dot]) return (clang_version, clang_version_for_cache) def _detect_system_clang(repository_ctx): """Detects whether the system-provided clang can be used. Returns a tuple of (is_clang, environment). """ # If the user provides an explicit `CC` environment variable, use that as # the compiler. This should point at the `clang` executable to use. cc = repository_ctx.os.environ.get("CC") cc_path = None if cc: cc_path = repository_ctx.path(cc) if not cc_path.exists: cc_path = repository_ctx.which(cc) if not cc_path: cc_path = repository_ctx.which("clang") if not cc_path: fail("Cannot find clang or CC (%s); either correct your path or set the CC environment variable" % cc) version_output = _run(repository_ctx, [cc_path, "--version"]).stdout if "clang" not in version_output: fail("Searching for clang or CC (%s), and found (%s), which is not a Clang compiler" % (cc, cc_path)) clang_version, clang_version_for_cache = _clang_version(version_output) return (cc_path.realpath, clang_version, clang_version_for_cache) def _compute_clang_resource_dir(repository_ctx, clang): """Runs the `clang` binary to get its resource dir.""" output = _run( repository_ctx, [clang, "-no-canonical-prefixes", "--print-resource-dir"], ).stdout # The only line printed is this path. return output.splitlines()[0] def _compute_mac_os_sysroot(repository_ctx): """Runs `xcrun` to extract the correct sysroot.""" xcrun = repository_ctx.which("xcrun") if not xcrun: fail("`xcrun` not found: is Xcode installed?") output = _run(repository_ctx, [xcrun, "--show-sdk-path"]).stdout return output.splitlines()[0] def _compute_bsd_sysroot(repository_ctx): """Look around for sysroot. Return root (/) if nothing found.""" # Try it-just-works for CMake users. default = "/" sysroot = repository_ctx.os.environ.get("CMAKE_SYSROOT", default) sysroot_path = repository_ctx.path(sysroot) if sysroot_path.exists: return sysroot_path.realpath return default # File content used when computing search paths. This additionally verifies that # libc++ is installed. _CLANG_INCLUDE_FILE_CONTENT = """ #if __has_include() #include #endif #ifndef _LIBCPP_STD_VER #error "No libc++ install found!" #endif """ def _compute_clang_cpp_include_search_paths(repository_ctx, clang, sysroot): """Runs the `clang` binary and extracts the include search paths. Returns the resulting paths as a list of strings. """ # Create a file for Clang to use as input. repository_ctx.file("_temp", _CLANG_INCLUDE_FILE_CONTENT) input_file = repository_ctx.path("_temp") # The only way to get this out of Clang currently is to parse the verbose # output of the compiler when it is compiling C++ code. cmd = [ clang, # Avoid canonicalizing away symlinks. "-no-canonical-prefixes", # Extract verbose output. "-v", # Just parse the input, don't generate outputs. "-fsyntax-only", # Force the language to be C++. "-x", "c++", # Use the input file. input_file, # Always use libc++. "-stdlib=libc++", ] # We need to use a sysroot to correctly represent a run on macOS. if repository_ctx.os.name.lower().startswith("mac os"): if not sysroot: fail("Must provide a sysroot on macOS!") cmd.append("--sysroot=" + sysroot) # Note that verbose output is on stderr, not stdout! output = _run(repository_ctx, cmd).stderr.splitlines() # Return the list of directories printed for system headers. These are the # only ones that Bazel needs us to manually provide. We find these by # searching for a begin and end marker. We also have to strip off a leading # space from each path. include_begin = output.index("#include <...> search starts here:") + 1 include_end = output.index("End of search list.", include_begin) # Suffix present on framework paths. framework_suffix = " (framework directory)" return [ repository_ctx.path(s.lstrip(" ").removesuffix(framework_suffix)) for s in output[include_begin:include_end] ] def _configure_clang_toolchain_impl(repository_ctx): # First just symlink in the untemplated parts of the toolchain repo. repository_ctx.symlink(repository_ctx.attr._clang_toolchain_build, "BUILD") repository_ctx.symlink( repository_ctx.attr._clang_cc_toolchain_config, "cc_toolchain_config.bzl", ) for file_label in repository_ctx.attr._clang_toolchain_files: repository_ctx.symlink(file_label, file_label.name) # Find a Clang C++ compiler, and where it lives. We need to walk symlinks # here as the other LLVM tools may not be symlinked into the PATH even if # `clang` is. We also insist on finding the basename of `clang++` as that is # important for C vs. C++ compiles. (clang, clang_version, clang_version_for_cache) = _detect_system_clang( repository_ctx, ) if clang_version and clang_version < 19: fail("Found clang {0}. ".format(clang_version) + "Carbon requires clang >=19. See " + "https://github.com/carbon-language/carbon-lang/blob/trunk/docs/project/contribution_tools.md#old-llvm-versions") clang_cpp = clang.dirname.get_child("clang++") # Compute the various directories used by Clang. resource_dir = _compute_clang_resource_dir(repository_ctx, clang_cpp) sysroot_dir = None if repository_ctx.os.name.lower().startswith("mac os"): sysroot_dir = _compute_mac_os_sysroot(repository_ctx) if repository_ctx.os.name == "freebsd": sysroot_dir = _compute_bsd_sysroot(repository_ctx) include_dirs = _compute_clang_cpp_include_search_paths( repository_ctx, clang_cpp, sysroot_dir, ) # We expect that the LLVM binutils live adjacent to llvm-ar. # First look for llvm-ar adjacent to clang, so that if found, # it is most likely to match the same version as clang. # Otherwise, try PATH. ar_path = clang.dirname.get_child("llvm-ar") if not ar_path.exists: ar_path = repository_ctx.which("llvm-ar") if not ar_path: fail("`llvm-ar` not found in PATH or adjacent to clang") # By default Windows uses '\' in its paths. These will be # interpreted as escape characters and fail the build, thus # we must manually replace the backslashes with '/' if repository_ctx.os.name.lower().startswith("windows"): resource_dir = resource_dir.replace("\\", "/") include_dirs = [str(s).replace("\\", "/") for s in include_dirs] repository_ctx.template( "clang_detected_variables.bzl", repository_ctx.attr._clang_detected_variables_template, substitutions = { "{CLANG_BINDIR}": str(clang.dirname), "{CLANG_INCLUDE_DIRS_LIST}": str( [str(path) for path in include_dirs], ), "{CLANG_RESOURCE_DIR}": resource_dir, "{CLANG_VERSION_FOR_CACHE}": clang_version_for_cache.replace('"', "_").replace("\\", "_"), "{CLANG_VERSION}": str(clang_version), "{LLVM_BINDIR}": str(ar_path.dirname), "{LLVM_SYMBOLIZER}": str(ar_path.dirname.get_child("llvm-symbolizer")), "{SYSROOT}": str(sysroot_dir), }, executable = False, ) configure_clang_toolchain = repository_rule( implementation = _configure_clang_toolchain_impl, configure = True, local = True, attrs = { "_clang_cc_toolchain_config": attr.label( default = Label( "//bazel/cc_toolchains:clang_cc_toolchain_config.bzl", ), allow_single_file = True, ), "_clang_detected_variables_template": attr.label( default = Label( "//bazel/cc_toolchains:clang_detected_variables.tpl.bzl", ), allow_single_file = True, ), "_clang_toolchain_build": attr.label( default = Label("//bazel/cc_toolchains:clang_toolchain.BUILD"), allow_single_file = True, ), "_clang_toolchain_files": attr.label_list( default = [ Label("//bazel/cc_toolchains:cc_toolchain_actions.bzl"), Label("//bazel/cc_toolchains:cc_toolchain_base_features.bzl"), Label("//bazel/cc_toolchains:cc_toolchain_carbon_project_features.bzl"), Label("//bazel/cc_toolchains:cc_toolchain_config_features.bzl"), Label("//bazel/cc_toolchains:cc_toolchain_cpp_features.bzl"), Label("//bazel/cc_toolchains:cc_toolchain_debugging.bzl"), Label("//bazel/cc_toolchains:cc_toolchain_features.bzl"), Label("//bazel/cc_toolchains:cc_toolchain_linking.bzl"), Label("//bazel/cc_toolchains:cc_toolchain_modules.bzl"), Label("//bazel/cc_toolchains:cc_toolchain_optimization.bzl"), Label("//bazel/cc_toolchains:cc_toolchain_sanitizer_features.bzl"), Label("//bazel/cc_toolchains:cc_toolchain_tools.bzl"), ], ), }, environ = ["CC"], ) clang_toolchain_extension = module_extension( implementation = lambda ctx: configure_clang_toolchain(name = "bazel_cc_toolchain"), ) ================================================ FILE: bazel/cc_toolchains/clang_detected_variables.tpl.bzl ================================================ # Part of the Carbon Language project, under the Apache License v2.0 with LLVM # Exceptions. See /LICENSE for license information. # SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception """A Starlark file exporting detected Clang configuration variables. This file gets processed by a repository rule, substituting the `{VARIABLE}`s with values detected by invoking Clang. """ llvm_bindir = "{LLVM_BINDIR}" llvm_symbolizer = "{LLVM_SYMBOLIZER}" clang_bindir = "{CLANG_BINDIR}" clang_version = {CLANG_VERSION} clang_version_for_cache = "{CLANG_VERSION_FOR_CACHE}" clang_resource_dir = "{CLANG_RESOURCE_DIR}" clang_include_dirs_list = {CLANG_INCLUDE_DIRS_LIST} sysroot_dir = "{SYSROOT}" ================================================ FILE: bazel/cc_toolchains/clang_toolchain.BUILD ================================================ # Part of the Carbon Language project, under the Apache License v2.0 with LLVM # Exceptions. See /LICENSE for license information. # SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception # This file is symlinked into a configured Clang toolchain repository as the # root `BUILD` file for that repository. load(":cc_toolchain_config.bzl", "cc_local_toolchain_suite") package(default_visibility = ["//visibility:public"]) cc_local_toolchain_suite( name = "bazel_cc_toolchain", configs = [ ("linux", "aarch64"), ("linux", "x86_64"), ("freebsd", "x86_64"), ("macos", "arm64"), ("macos", "x86_64"), ("windows", "x86_64"), ], ) ================================================ FILE: bazel/cc_toolchains/defs.bzl ================================================ # Part of the Carbon Language project, under the Apache License v2.0 with LLVM # Exceptions. See /LICENSE for license information. # SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception """Provides helpers for cc rules. Intended for general consumption.""" load("@bazel_cc_toolchain//:clang_detected_variables.bzl", "llvm_symbolizer") def cc_env(): """Returns standard environment settings for a cc_binary. In use, this looks like: ``` load("//bazel/cc_toolchains:defs.bzl", "cc_env") cc_binary( ... env = cc_env(), ) ``` We're currently setting this on a target-by-target basis, mainly because it's difficult to modify default behaviors. """ # Settings which apply cross-platform. # buildifier: disable=unsorted-dict-items common_env = { "LLVM_SYMBOLIZER_PATH": llvm_symbolizer, # Sanitizers don't use LLVM as fallback, but sometimes ASAN may be used # for UBSAN errors; we still set UBSAN in case it's directly used. "ASAN_SYMBOLIZER_PATH": llvm_symbolizer, "UBSAN_SYMBOLIZER_PATH": llvm_symbolizer, # Default to printing traces for UBSAN. "UBSAN_OPTIONS": "print_stacktrace=1", } # On macOS, there's a nano zone allocation warning when asan is enabled. # This suppresses the warning in `bazel run`. macos_env = {"MallocNanoZone": "0"} return common_env | select({ "//bazel/cc_toolchains:macos_asan": macos_env, "//conditions:default": {}, }) ================================================ FILE: bazel/check_deps/BUILD ================================================ # Part of the Carbon Language project, under the Apache License v2.0 with LLVM # Exceptions. See /LICENSE for license information. # SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception load("@rules_python//python:defs.bzl", "py_test") # The filegroups establishing root rules for `genquery` invocations can be # updated by running: # # ``` # ./bazel/check_deps/update_roots.py # ``` filegroup( name = "non_test_cc_rules", data = [ "//toolchain/install:carbon_toolchain_tar_gz_rule", "//toolchain/install:carbon_toolchain_tar_rule", ], tags = ["manual"], ) genquery( name = "non_test_cc_deps.txt", expression = "kind('cc.* rule', deps(//bazel/check_deps:non_test_cc_rules))", opts = [ "--notool_deps", "--noimplicit_deps", ], scope = [":non_test_cc_rules"], ) py_test( name = "check_non_test_cc_deps", size = "small", srcs = ["check_non_test_cc_deps.py"], data = [":non_test_cc_deps.txt"], main = "check_non_test_cc_deps.py", ) ================================================ FILE: bazel/check_deps/check_non_test_cc_deps.py ================================================ #!/usr/bin/env python3 """Check that non-test C++ rules only depend on Carbon and LLVM. Carbon works to ensure its user-visible libraries and binaries only depend on their code and LLVM. Among other benefits, this provides a single, simple license used for the whole project. However, we frequently use third-party projects and libraries where useful in our test code. Here, we verify that the dependencies of non-test C++ rules only include Carbon and LLVM code. """ __copyright__ = """ Part of the Carbon Language project, under the Apache License v2.0 with LLVM Exceptions. See /LICENSE for license information. SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception """ import os import sys from pathlib import Path runfiles = Path(os.environ["TEST_SRCDIR"]) deps_path = runfiles / "_main" / "bazel" / "check_deps" / "non_test_cc_deps.txt" try: with deps_path.open() as deps_file: deps = deps_file.read().splitlines() except FileNotFoundError: sys.exit("ERROR: unable to find deps file: %s" % deps_path) # This errors out on dependencies that aren't recognized, and continues on # allowed dependencies. for dep in deps: print("Checking dependency: " + dep) repo, _, rule = dep.partition("//") if repo == "@@+llvm_project+llvm-project": package, _, rule = rule.partition(":") # Other packages in the LLVM project shouldn't be accidentally used # in Carbon. We can expand the above list if use cases emerge. if package not in ( "clang", "clang-tools-extra/clangd", "libc", "libcxx", "libcxxabi", "libunwind", "lld", "llvm", # While this is in a `third_party` directory, its code is documented # as part of LLVM and for use in compiler-rt. "third-party/siphash", ) and ( package == "third-party" and rule not in ( # LLVM wrappers for zlib-ng and zstd, which are fine as linked. "zlib", "zstd", ) ): sys.exit( "ERROR: unexpected dependency into the LLVM project: %s" % dep ) # Check for accidentally using the copy of GoogleTest in LLVM. if rule in ("gmock", "gtest", "gtest_main"): sys.exit( "ERROR: dependency on LLVM's GoogleTest from non-test code: %s" % dep ) # The rest of LLVM, LLD, and Clang themselves are safe to depend on. continue # Carbon code is always allowed. if repo == "" and not rule.startswith("third_party"): continue # LLVM code managed in the Carbon repository is still LLVM code and OK. if repo == "" and rule.startswith("third_party/llvm:"): continue # Utility libraries provided by Bazel that are under a compatible license. if repo in ("@@rules_cc+", "@@bazel_tools"): continue # These libraries have compatible licenses and are linked in without copying # source, so fine for our binaries. if repo in ( "@@zlib-ng+", "@@zstd+", ): continue # This should never be reached from non-test code, but these targets do # exist. Specially diagnose them to try to provide a more helpful # message. if repo in ( "@google_benchmark", "@abseil-cpp", "@googletest", ): sys.exit("ERROR: dependency only allowed in test code: %s" % dep) # Conservatively fail if a dependency isn't explicitly allowed above. sys.exit(f"ERROR: unknown dependency on {repo}: {dep}") ================================================ FILE: bazel/check_deps/update_roots.py ================================================ #!/usr/bin/env python3 """Update the roots of the Carbon build used for dependency checking. The dependency checking cannot use wildcard queries, so we use them here and then create lists of relevant roots in the build file. """ __copyright__ = """ Part of the Carbon Language project, under the Apache License v2.0 with LLVM Exceptions. See /LICENSE for license information. SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception """ import os import subprocess from pathlib import Path # Change the working directory to the repository root so that the remaining # operations reliably operate relative to that root. os.chdir(Path(__file__).parents[2]) print("Compute non-test C++ root targets...") query_arg = ( "let non_tests = attr(" " testonly, 0, //..." # Exclude tree_sitter because its @platforms dependency errors in # query. Note if it ends up in releases, we might want to do more, # but that should also be caught by check_deps.py. " except //utils/tree_sitter/..." # Exclude tcmalloc as an optional external library. " except //bazel/malloc:tcmalloc_if_linux_opt" ") in kind('(cc|pkg)_.* rule', deps($non_tests))" ) non_test_cc_roots_query = subprocess.check_output( [ "./scripts/run_bazel.py", "query", "--noshow_progress", "--noimplicit_deps", "--notool_deps", "--output=minrank", query_arg, ], universal_newlines=True, ) ranked_targets = [line.split() for line in non_test_cc_roots_query.splitlines()] roots = [target for rank, target in ranked_targets if int(rank) == 0] print("Found roots:\n%s" % "\n".join(roots)) print("Replace non-test C++ roots in the BUILD file...") buildozer_run = subprocess.run( [ "./scripts/run_buildozer.py", "remove data", ] + ["add data '%s'" % root for root in roots] + ["//bazel/check_deps:non_test_cc_rules"], ) if buildozer_run.returncode == 3: print("No changes needed!") else: buildozer_run.check_returncode() print("Successfully updated roots in the BUILD file!") ================================================ FILE: bazel/llvm_project/0001_Patch_for_mallinfo2_when_using_Bazel_build_system.patch ================================================ From 80f5475adfe179739a45d42850f8c06a630bc3a0 Mon Sep 17 00:00:00 2001 From: Chandler Carruth Date: Fri, 17 Jun 2022 09:10:41 +0000 Subject: [PATCH] Patch for mallinfo2 when using Bazel build system. This detects and defines the `HAVE_MALLINFO2` macro based on the glibc version to allow easy use of the Bazel build on systems with modern glibc installs. --- llvm/lib/Support/Unix/Process.inc | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/llvm/lib/Support/Unix/Process.inc b/llvm/lib/Support/Unix/Process.inc index 550b0de2e045..6d449489c450 100644 --- a/llvm/lib/Support/Unix/Process.inc +++ b/llvm/lib/Support/Unix/Process.inc @@ -22,6 +22,13 @@ #include #include #include +// When glibc is in use, detect mallinfo2 to address mallinfo deprecation +// warnings. +#if !defined(HAVE_MALLINFO2) && defined(__GLIBC_PREREQ) +#if __GLIBC_PREREQ(2, 33) +#define HAVE_MALLINFO2 +#endif // __GLIBC_PREREQ(2, 33) +#endif // !defined(HAVE_MALLINFO2) && defined(__GLIBC_PREREQ) #if defined(HAVE_MALLINFO) || defined(HAVE_MALLINFO2) #include #endif -- 2.48.1 ================================================ FILE: bazel/llvm_project/0002_Added_Bazel_build_for_compiler_rt_fuzzer.patch ================================================ From 20d0f1fb854d9b7ce135d08575112fc58c4c7ce7 Mon Sep 17 00:00:00 2001 From: jonmeow Date: Thu, 14 Sep 2023 20:31:11 +0000 Subject: [PATCH] Add libfuzzer target to compiler-rt. --- .../compiler-rt/BUILD.bazel | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/utils/bazel/llvm-project-overlay/compiler-rt/BUILD.bazel b/utils/bazel/llvm-project-overlay/compiler-rt/BUILD.bazel index 90264449de76..115da4cb77f6 100644 --- a/utils/bazel/llvm-project-overlay/compiler-rt/BUILD.bazel +++ b/utils/bazel/llvm-project-overlay/compiler-rt/BUILD.bazel @@ -57,6 +57,23 @@ cc_library( ], ) +cc_library( + name = "FuzzerMain", + srcs = glob( + ["lib/fuzzer/Fuzzer*.cpp"], + ), + hdrs = glob([ + "lib/fuzzer/Fuzzer*.h", + "lib/fuzzer/Fuzzer*.def", + ]), + copts = [ + # Not using no-sanitize=address per https://github.com/google/sanitizers/wiki/AddressSanitizerContainerOverflow#false-positives + "-fno-sanitize=memory,thread,undefined", + "-fsanitize-coverage=0", + ], + includes = ["lib/fuzzer"], +) + cc_library( name = "orc_rt_common_headers", hdrs = [ -- 2.42.0 ================================================ FILE: bazel/llvm_project/0004_Introduce_basic_sources_exporting_for_libunwind.patch ================================================ Commit ID: 354e38c89f28e2cc284e655a9cde707f457dc02c Change ID: sxspxmonsuvqzuvxvrvorlumwpwromsv Author : Chandler Carruth (2025-09-25 22:55:26) Committer: Chandler Carruth (2026-02-14 03:46:06) Introduce basic sources exporting for libunwind This exports the source files directly so that they can be used to build this runtime library on demand. diff --git a/utils/bazel/llvm-project-overlay/libunwind/BUILD.bazel b/utils/bazel/llvm-project-overlay/libunwind/BUILD.bazel index c9fdc819c0..7d734c5a06 100644 --- a/utils/bazel/llvm-project-overlay/libunwind/BUILD.bazel +++ b/utils/bazel/llvm-project-overlay/libunwind/BUILD.bazel @@ -21,3 +21,19 @@ ], strip_include_prefix = "include", ) + +filegroup( + name = "libunwind_hdrs", + srcs = glob(["include/**/*.h"]), +) + +filegroup( + name = "libunwind_srcs", + srcs = glob([ + "src/*.cpp", + "src/*.hpp", + "src/*.c", + "src/*.h", + "src/*.S", + ]), +) diff --git a/utils/bazel/llvm-project-overlay/libunwind/libunwind_library.bzl b/utils/bazel/llvm-project-overlay/libunwind/libunwind_library.bzl new file mode 100644 index 0000000000..25675d3070 --- /dev/null +++ b/utils/bazel/llvm-project-overlay/libunwind/libunwind_library.bzl @@ -0,0 +1,24 @@ +# This file is licensed under the Apache License v2.0 with LLVM Exceptions. +# See https://llvm.org/LICENSE.txt for license information. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +"""Starlark variables and macros for building libunwind. + +Variables provide base line information for how to build libunwind source files. +These can be used to generate non-Bazel builds of the library. + +Macros provide a convenient way to construct Bazel `cc_library` rules for +libunwind. +""" + +# TODO: Should libunwind use `-fvisibility-inlines-hidden` and +# `-fvisibility=hidden`, similar to libc++? +libunwind_copts = [ + "-D_LIBUNWIND_IS_NATIVE_ONLY", + "-O3", + "-fPIC", + "-fno-exceptions", + "-fno-rtti", + "-funwind-tables", + "-nostdinc++", +] ================================================ FILE: bazel/llvm_project/0005_Introduce_basic_sources_exporting_for_libcxx_and_libcxxabi.patch ================================================ Commit ID: 1fd710ed69a0f47f454c386d39302ddb756a88b3 Change ID: mstnwoqruyypnoouksnyqssllrsozpos Bookmarks: bz-libcxx* bz-libcxx@git Author : Chandler Carruth (2025-09-25 22:55:26) Committer: Chandler Carruth (2026-03-08 07:41:54) Introduce basic sources exporting for libcxx and libcxxabi This exports the source files directly so that they can be used to build a libcxx runtime library on demand. It also differentiates between normal sources and textual sources. diff --git a/utils/bazel/llvm-project-overlay/libcxx/BUILD.bazel b/utils/bazel/llvm-project-overlay/libcxx/BUILD.bazel new file mode 100644 index 0000000000..c8b517ab56 --- /dev/null +++ b/utils/bazel/llvm-project-overlay/libcxx/BUILD.bazel @@ -0,0 +1,139 @@ +# This file is licensed under the Apache License v2.0 with LLVM Exceptions. +# See https://llvm.org/LICENSE.txt for license information. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +licenses(["notice"]) + +package( + default_visibility = ["//visibility:public"], +) + +exports_files([ + "include/__config_site.in", + "include/module.modulemap.in", + "vendor/llvm/default_assertion_handler.in", +]) + +filegroup( + name = "libcxx_hdrs", + srcs = glob( + [ + # Top level includes and those in `experimental` and `ext` sometimes + # have no extension. + "include/*", + "include/experimental/*", + "include/ext/*", + + # Implementation detail headers all use `.h` extensions + "include/**/*.h", + ], + exclude = [ + # Omit CMake and CMake-configured files that get caught by the + # extension-less patterns. + "**/*.in", + "**/CMakeLists.txt", + + # Omit C++03 compatibility headers as current users don't need them. + "include/__cxx03/**", + ], + ), +) + +LIBCXX_SRCS_PSTL_LIBDISPATCH = [ + "src/pstl/libdispatch.cpp", +] + +filegroup( + name = "libcxx_srcs_pstl_libdispatch", + srcs = LIBCXX_SRCS_PSTL_LIBDISPATCH, +) + +LIBCXX_SRCS_SUPPORT_IBM_PATTERNS = [ + "src/support/ibm/**/*.cpp", +] + +filegroup( + name = "libcxx_srcs_support_ibm", + srcs = glob(LIBCXX_SRCS_SUPPORT_IBM_PATTERNS), +) + +LIBCXX_SRCS_SUPPORT_WIN32_PATTERNS = [ + "src/support/win32/**/*.cpp", +] + +filegroup( + name = "libcxx_srcs_support_win32", + srcs = glob(LIBCXX_SRCS_SUPPORT_WIN32_PATTERNS), +) + +LIBCXX_SRCS_TZDB = [ + "src/experimental/chrono_exception.cpp", + "src/experimental/time_zone.cpp", + "src/experimental/tzdb.cpp", + "src/experimental/tzdb_list.cpp", +] + +filegroup( + name = "libcxx_srcs_tzdb", + srcs = LIBCXX_SRCS_TZDB, +) + +# Exclude platform-dependent patterns that are provided by per-target filegroups +# above. +LIBCXX_SRCS_TARGET_EXCLUDES = ( + LIBCXX_SRCS_PSTL_LIBDISPATCH + + LIBCXX_SRCS_SUPPORT_IBM_PATTERNS + + LIBCXX_SRCS_SUPPORT_WIN32_PATTERNS + + LIBCXX_SRCS_TZDB +) + +filegroup( + name = "libcxx_srcs_generic", + srcs = glob( + [ + "src/**/*.cpp", + "src/**/*.h", + "src/**/*.ipp", + ], + exclude = [ + # Build is for use with libc++abi and so don't need 'new.cpp'. + "src/new.cpp", + + # Build is for compiler-rt platforms so we have its int128 support. + "src/filesystem/int128_builtins.cpp", + ] + LIBCXX_SRCS_TARGET_EXCLUDES, + ), +) + +filegroup( + name = "libcxx_linux_srcs", + srcs = [ + ":libcxx_srcs_generic", + ":libcxx_srcs_tzdb", + ], +) + +filegroup( + name = "libcxx_macos_srcs", + srcs = [ + ":libcxx_srcs_generic", + # TODO: Include libdispatch sources here to enable that pstl backend. + ], +) + +filegroup( + name = "libcxx_win32_srcs", + srcs = [ + ":libcxx_srcs_generic", + ":libcxx_srcs_support_win32", + ], +) + +filegroup( + name = "libcxx_all_srcs", + srcs = [ + ":libcxx_linux_srcs", + ":libcxx_macos_srcs", + ":libcxx_win32_srcs", + ], +) diff --git a/utils/bazel/llvm-project-overlay/libcxx/libcxx_library.bzl b/utils/bazel/llvm-project-overlay/libcxx/libcxx_library.bzl new file mode 100644 index 0000000000..66f64f3610 --- /dev/null +++ b/utils/bazel/llvm-project-overlay/libcxx/libcxx_library.bzl @@ -0,0 +1,37 @@ +# This file is licensed under the Apache License v2.0 with LLVM Exceptions. +# See https://llvm.org/LICENSE.txt for license information. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +"""Starlark variables and macros for building libc++ and libc++abi. + +Variables provide base line information for how to build libc++ and libc++abi +source files. These can be used to generate non-Bazel builds of the library. + +TODO: Add macros that provide a convenient way to construct Bazel `cc_library` +rules for libc++ and libc++abi. + +TODO: Add either sufficient usage in the macros, or add a how-to example here in +the documentation so the use of these variables is more clear. +""" + +_libcxx_base_copts = [ + "-std=c++26", + "-O3", + "-fPIC", + "-fvisibility-inlines-hidden", + "-fvisibility=hidden", + "-nostdinc++", +] + +_libcxx_defines = [ + "-D_LIBCPP_BUILDING_LIBRARY", + "-D_LIBCPP_REMOVE_TRANSITIVE_INCLUDES", +] + +_libcxxabi_defines = [ + "-DLIBCXX_BUILDING_LIBCXXABI", +] + +libcxx_copts = _libcxx_base_copts + _libcxx_defines +libcxxabi_copts = _libcxx_base_copts + _libcxxabi_defines +libcxx_and_abi_copts = _libcxx_base_copts + _libcxx_defines + _libcxxabi_defines diff --git a/utils/bazel/llvm-project-overlay/libcxxabi/BUILD.bazel b/utils/bazel/llvm-project-overlay/libcxxabi/BUILD.bazel new file mode 100644 index 0000000000..2db70b54e2 --- /dev/null +++ b/utils/bazel/llvm-project-overlay/libcxxabi/BUILD.bazel @@ -0,0 +1,30 @@ +# This file is licensed under the Apache License v2.0 with LLVM Exceptions. +# See https://llvm.org/LICENSE.txt for license information. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +licenses(["notice"]) + +package( + default_visibility = ["//visibility:public"], +) + +filegroup( + name = "libcxxabi_hdrs", + srcs = glob(["include/*.h"]), +) + +filegroup( + name = "libcxxabi_srcs", + srcs = glob([ + "src/**/*.cpp", + "src/**/*.h", + ]), +) + +filegroup( + name = "libcxxabi_textual_srcs", + srcs = glob([ + "src/**/*.def", + "src/**/*.inc", + ]), +) ================================================ FILE: bazel/llvm_project/0009_Introduce_starlark_exporting_compiler-rt_build_information.patch ================================================ Commit ID: d3b82534c2546a892a27856672ed95a7db97dba3 Change ID: zyxuvzwmzsnorloyuupuurxkppkoplnw Author : Chandler Carruth (2026-02-16 23:17:06) Committer: Chandler Carruth (2026-03-11 07:54:02) Improve compiler-rt build structure and export compilation info This first improves the structure of the compiler-rt BUILD.bazel, fixing bugs and exposing more carefully arranged source files. It also exposes compilation info for builtins and CRT files for use in compiling these source files. diff --git a/utils/bazel/llvm-project-overlay/compiler-rt/BUILD.bazel b/utils/bazel/llvm-project-overlay/compiler-rt/BUILD.bazel index 4ded226174..3b5b8fc787 100644 --- a/utils/bazel/llvm-project-overlay/compiler-rt/BUILD.bazel +++ b/utils/bazel/llvm-project-overlay/compiler-rt/BUILD.bazel @@ -3,6 +3,7 @@ # SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception load("@rules_cc//cc:defs.bzl", "cc_library") +load("compiler-rt.bzl", "make_filtered_builtins_srcs_groups") package( default_visibility = ["//visibility:public"], @@ -160,9 +161,15 @@ srcs = BUILTINS_CRTEND_SRCS, ) +BUILTINS_EMUTLS_SRCS = ["lib/builtins/emutls.c"] + +filegroup( + name = "builtins_emutls_srcs", + srcs = BUILTINS_EMUTLS_SRCS, +) + BUILTINS_HOSTED_SRCS = [ "lib/builtins/clear_cache.c", - "lib/builtins/emutls.c", "lib/builtins/enable_execute_stack.c", "lib/builtins/eprintf.c", ] @@ -224,11 +231,11 @@ ), ) -BUILTNS_ATOMICS_SRCS = ["lib/builtins/atomic.c"] +BUILTINS_ATOMICS_SRCS = ["lib/builtins/atomic.c"] filegroup( name = "builtins_atomics_srcs", - srcs = BUILTNS_ATOMICS_SRCS + ["lib/builtins/assembly.h"], + srcs = BUILTINS_ATOMICS_SRCS + ["lib/builtins/assembly.h"], ) BUILTINS_MACOS_ATOMIC_SRCS_PATTERNS = [ @@ -241,6 +248,28 @@ srcs = glob(BUILTINS_MACOS_ATOMIC_SRCS_PATTERNS), ) +# Source files for portable components of the compiler builtins library. +filegroup( + name = "builtins_generic_srcs", + srcs = ["lib/builtins/cpu_model/cpu_model.h"] + glob( + [ + "lib/builtins/*.c", + "lib/builtins/*.cpp", + "lib/builtins/*.h", + "lib/builtins/*.inc", + ], + allow_empty = True, + exclude = ( + BUILTINS_CRTBEGIN_SRCS + + BUILTINS_CRTEND_SRCS + + BUILTINS_TF_EXCLUDES + + BUILTINS_TF_SRCS_PATTERNS + + BUILTINS_ATOMICS_SRCS + + BUILTINS_MACOS_ATOMIC_SRCS_PATTERNS + ), + ), +) + # Apple-platform specific SME source file. filegroup( name = "builtins_aarch64_apple_sme_srcs", @@ -305,10 +334,13 @@ # Source files for the AArch64 architecture-specific builtins. filegroup( - name = "builtins_aarch64_srcs", + name = "builtins_unfiltered_aarch64_srcs", srcs = [ "lib/builtins/cpu_model/aarch64.c", "lib/builtins/cpu_model/aarch64.h", + ":builtins_bf16_srcs", + ":builtins_generic_srcs", + ":builtins_tf_srcs", ] + [ AARCH64_OUTLINE_ATOMICS_FMT.format(pat, size, model) for (pat, size, model) in AARCH64_OUTLINE_ATOMICS @@ -328,10 +360,20 @@ "lib/builtins/aarch64/lse.S", # These files are provided by SME-specific file groups above. "lib/builtins/aarch64/*sme*", + # This is only used with MinGW. + "lib/builtins/aarch64/chkstk.S", + # TODO: Remove this once we have a way of accessing `SipHash.h`. + "lib/builtins/aarch64/emupac.cpp", ], ), ) +make_filtered_builtins_srcs_groups( + name = "builtins_aarch64_srcs", + srcs = [":builtins_unfiltered_aarch64_srcs"], + textual_name = "builtins_aarch64_textual_srcs", +) + BUILTINS_ARM_VFP_SRCS_PATTERNS = [ "lib/builtins/arm/*vfp*.S", "lib/builtins/arm/*vfp*.c", @@ -348,9 +390,19 @@ ), ) +BUILTINS_ARM_IMPLICIT_IT_SRCS = [ + "lib/builtins/arm/mulsf3.S", + "lib/builtins/arm/divsf3.S", +] + +filegroup( + name = "builtins_arm_implicit_it_srcs", + srcs = BUILTINS_ARM_IMPLICIT_IT_SRCS, +) + # Source files for the ARM architecture-specific builtins. filegroup( - name = "builtins_arm_srcs", + name = "builtins_arm_arch_srcs", srcs = glob( [ "lib/builtins/arm/*.S", @@ -359,14 +411,52 @@ "lib/builtins/arm/*.h", ], allow_empty = True, - exclude = BUILTINS_ARM_VFP_SRCS_PATTERNS, + exclude = (BUILTINS_ARM_VFP_SRCS_PATTERNS + + BUILTINS_ARM_IMPLICIT_IT_SRCS) + [ + # This is only used with MinGW. + "lib/builtins/arm/chkstk.S", + ], ), ) -# Source files for the PPC architecture-specific builtins. -filegroup( - name = "builtins_ppc_srcs", - srcs = glob( +filegroup( + name = "builtins_unfiltered_armv7_srcs", + srcs = [ + ":builtins_arm_arch_srcs", + ":builtins_arm_vfp_srcs", + ":builtins_bf16_srcs", + ":builtins_generic_srcs", + ], +) + +make_filtered_builtins_srcs_groups( + name = "builtins_armv7_srcs", + srcs = [":builtins_unfiltered_armv7_srcs"], + textual_name = "builtins_armv7_textual_srcs", +) + +filegroup( + name = "builtins_unfiltered_aarch32_srcs", + srcs = [ + ":builtins_arm_arch_srcs", + ":builtins_arm_vfp_srcs", + ":builtins_bf16_srcs", + ":builtins_generic_srcs", + ], +) + +make_filtered_builtins_srcs_groups( + name = "builtins_aarch32_srcs", + srcs = [":builtins_unfiltered_aarch32_srcs"], + textual_name = "builtins_aarch32_textual_srcs", +) + +filegroup( + name = "builtins_unfiltered_ppc64_srcs", + srcs = [ + ":builtins_generic_srcs", + ":builtins_tf_srcs", + ] + glob( [ "lib/builtins/ppc/*.S", "lib/builtins/ppc/*.c", @@ -377,17 +467,64 @@ ), ) -# Source files for the RISC-V architecture-specific builtins. -filegroup( - name = "builtins_riscv_srcs", - srcs = glob( - [ - "lib/builtins/riscv/*.S", - "lib/builtins/riscv/*.c", - "lib/builtins/riscv/*.cpp", - ], - allow_empty = True, - ), +make_filtered_builtins_srcs_groups( + name = "builtins_ppc64_srcs", + srcs = [":builtins_unfiltered_ppc64_srcs"], + textual_name = "builtins_ppc64_textual_srcs", +) + +filegroup( + name = "builtins_unfiltered_ppc32_srcs", + srcs = [":builtins_generic_srcs"], +) + +make_filtered_builtins_srcs_groups( + name = "builtins_ppc32_srcs", + srcs = [":builtins_unfiltered_ppc32_srcs"], + textual_name = "builtins_ppc32_textual_srcs", +) + +filegroup( + name = "builtins_unfiltered_riscv64_srcs", + srcs = [ + ":builtins_generic_srcs", + ":builtins_tf_srcs", + ] + glob( + [ + "lib/builtins/riscv/*.S", + "lib/builtins/riscv/*.c", + "lib/builtins/riscv/*.cpp", + "lib/builtins/riscv/*.h", + ], + allow_empty = True, + ), +) + +make_filtered_builtins_srcs_groups( + name = "builtins_riscv64_srcs", + srcs = [":builtins_unfiltered_riscv64_srcs"], + textual_name = "builtins_riscv64_textual_srcs", +) + +filegroup( + name = "builtins_unfiltered_riscv32_srcs", + srcs = [ + ":builtins_generic_srcs", + ] + glob( + [ + "lib/builtins/riscv/*.S", + "lib/builtins/riscv/*.c", + "lib/builtins/riscv/*.cpp", + "lib/builtins/riscv/*.h", + ], + allow_empty = True, + ), +) + +make_filtered_builtins_srcs_groups( + name = "builtins_riscv32_srcs", + srcs = [":builtins_unfiltered_riscv32_srcs"], + textual_name = "builtins_riscv32_textual_srcs", ) # Source files for the x86 architecture specific builtins (both 32-bit and @@ -402,8 +539,14 @@ # Source files for the x86-64 architecture specific builtins. filegroup( - name = "builtins_x86_64_srcs", - srcs = glob( + name = "builtins_unfiltered_x86_64_srcs", + srcs = [ + ":builtins_bf16_srcs", + ":builtins_generic_srcs", + ":builtins_tf_srcs", + ":builtins_x86_arch_srcs", + ":builtins_x86_fp80_srcs", + ] + glob( [ "lib/builtins/x86_64/*.S", "lib/builtins/x86_64/*.c", @@ -411,13 +554,29 @@ "lib/builtins/x86_64/*.h", ], allow_empty = True, + exclude = [ + # This is a Windows-specific routine. + # TODO: We should expose this as a Windows source at some point. + "lib/builtins/x86_64/chkstk.S", + ], ), ) +make_filtered_builtins_srcs_groups( + name = "builtins_x86_64_srcs", + srcs = [":builtins_unfiltered_x86_64_srcs"], + textual_name = "builtins_x86_64_textual_srcs", +) + # Source files for the 32-bit-specific x86 architecture specific builtins. filegroup( - name = "builtins_i386_srcs", - srcs = glob( + name = "builtins_unfiltered_i386_srcs", + srcs = [ + ":builtins_bf16_srcs", + ":builtins_generic_srcs", + ":builtins_x86_arch_srcs", + ":builtins_x86_fp80_srcs", + ] + glob( [ "lib/builtins/i386/*.S", "lib/builtins/i386/*.c", @@ -429,28 +588,16 @@ # This file is used for both i386 and x86_64 and so included in the # broader x86 sources. "lib/builtins/i386/fp_mode.c", + # These are Windows-specific routines. + # TODO: We should expose these as Windows source at some point. + "lib/builtins/i386/chkstk.S", + "lib/builtins/i386/chkstk2.S", ], ), ) -# Source files for portable components of the compiler builtins library. -filegroup( - name = "builtins_generic_srcs", - srcs = ["lib/builtins/cpu_model/cpu_model.h"] + glob( - [ - "lib/builtins/*.c", - "lib/builtins/*.cpp", - "lib/builtins/*.h", - "lib/builtins/*.inc", - ], - allow_empty = True, - exclude = ( - BUILTINS_CRTBEGIN_SRCS + - BUILTINS_CRTEND_SRCS + - BUILTINS_TF_EXCLUDES + - BUILTINS_TF_SRCS_PATTERNS + - BUILTNS_ATOMICS_SRCS + - BUILTINS_MACOS_ATOMIC_SRCS_PATTERNS - ), - ), +make_filtered_builtins_srcs_groups( + name = "builtins_i386_srcs", + srcs = [":builtins_unfiltered_i386_srcs"], + textual_name = "builtins_i386_textual_srcs", ) diff --git a/utils/bazel/llvm-project-overlay/compiler-rt/compiler-rt.bzl b/utils/bazel/llvm-project-overlay/compiler-rt/compiler-rt.bzl new file mode 100644 index 0000000000..e33ceb6a89 --- /dev/null +++ b/utils/bazel/llvm-project-overlay/compiler-rt/compiler-rt.bzl @@ -0,0 +1,153 @@ +# This file is licensed under the Apache License v2.0 with LLVM Exceptions. +# See https://llvm.org/LICENSE.txt for license information. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +"""Starlark for building parts of compiler-rt. + +Variables provide baseline information for how to build various parts of +compiler-rt. These can be used to generate non-Bazel builds of the library. + +Rules and macros support building the relevant filegroups of source files. + +TODO: Add macros that provide a convenient way to construct a Bazel target for +the Clang resource directory with builtins and crt files. +""" + +_common_copts = [ + "-O3", + "-fPIC", + "-ffreestanding", + "-std=c11", +] + +crt_copts = _common_copts + [ + "-DCRT_HAS_INITFINI_ARRAY", + "-DEH_USE_FRAME_REGISTRY", + "-fno-lto", +] + +builtins_copts = _common_copts + [ + "-fno-builtin", + "-fomit-frame-pointer", + "-fvisibility=hidden", + "-Wno-missing-prototypes", + "-Wno-unused-parameter", +] + +def _get_rel_path(path_str): + rel_path = path_str.rpartition("/lib/builtins/")[2] + if rel_path == path_str: + fail("Expected '/lib/builtins/' in path " + path_str) + return rel_path + +def _filtered_builtins_srcs_impl(ctx): + """Implementation of filter_builtins_srcs rule.""" + + # Build a map from generic file basename to list of overriding files. + overrides = {} + for f in ctx.files.srcs: + rel_path = _get_rel_path(f.short_path) + if "/" in rel_path: + base_file = rel_path.rpartition("/")[2] + if base_file.endswith(".S"): + base_file = base_file.removesuffix(".S") + ".c" + overrides[base_file] = True + + filtered_files = [] + for f in ctx.files.srcs: + rel_path = _get_rel_path(f.short_path) + if "/" not in rel_path: + # This is a generic file. Check if it's overridden. + if rel_path not in overrides: + filtered_files.append(f) + else: + # This is an arch-specific file, include it. + filtered_files.append(f) + + # Remove any textual sources from this list. + filtered_files = [ + f + for f in filtered_files + if f.extension not in ["inc", "def"] + ] + + return [DefaultInfo(files = depset(filtered_files))] + +filtered_builtins_srcs = rule( + implementation = _filtered_builtins_srcs_impl, + attrs = { + "srcs": attr.label_list( + mandatory = True, + allow_files = True, + doc = "Input files.", + ), + }, + doc = """Build a filtered filegroup of non-textual srcs for builtins. + + Accepts a filegroup whose files are in lib/builtins/, and produces a target + behaving like a filegroup containing filtered files. + + This removes any textual source files (`.inc` or `.def`) from the input. + + It also replaces generic srcs that are overridden by architecture-specific + sources. For example, given a list of sources from filegroup of the form: + + - `.../lib/builtins/file_0.c` + - `.../lib/builtins/file_1.c` + - `.../lib/builtins/file_2.c` + - `.../lib/builtins/arch/file_0.c` + - `.../lib/builtins/arch/file_1.S` + + It removes any source-file at the top level of lib/builtins/ (e.g. + lib/builtins/file_0.c) that has a corresponding source-file in an arch + directory (e.g. lib/builtins/arch/file_0.c or lib/builtins/arch/file_1.S), + producing a list like: + + - `.../lib/builtins/file_2.c` + - `.../lib/builtins/arch/file_0.c` + - `.../lib/builtins/arch/file_1.S` + + This allows a target architecture to simply add a specialized file to the + list of sources with the architecture prefix and have the specialized + version override the generic version. + """, +) + +def _filtered_builtins_textual_srcs_impl(ctx): + """Implementation of filter_builtins_textual_srcs rule.""" + + filtered_files = [ + f + for f in ctx.files.srcs + if f.extension in ["inc", "def"] + ] + + return [DefaultInfo(files = depset(filtered_files))] + +filtered_builtins_textual_srcs = rule( + implementation = _filtered_builtins_textual_srcs_impl, + attrs = { + "srcs": attr.label_list( + mandatory = True, + allow_files = True, + doc = "Input files.", + ), + }, + doc = """Build a filegroup of the textual srcs for builtins. + + Textual sources are those that can't be compiled directly and aren't + recognized as header files by Bazel. The extensions recognized here are + `.inc` and `.def`. + """, +) + +def make_filtered_builtins_srcs_groups(name, textual_name, srcs): + """Macro to expand both the non-textual and textual filtered srcs groups.""" + filtered_builtins_srcs( + name = name, + srcs = srcs, + ) + filtered_builtins_textual_srcs( + name = textual_name, + srcs = srcs, + ) ================================================ FILE: bazel/llvm_project/BUILD ================================================ # Part of the Carbon Language project, under the Apache License v2.0 with LLVM # Exceptions. See /LICENSE for license information. # SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception package(default_visibility = ["//visibility:public"]) exports_files(glob([ "*.patch", ])) ================================================ FILE: bazel/llvm_project/llvm_project.bzl ================================================ # Part of the Carbon Language project, under the Apache License v2.0 with LLVM # Exceptions. See /LICENSE for license information. # SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception """Apply llvm_configure to produce a llvm-project repo.""" load("@llvm-raw//utils/bazel:configure.bzl", "llvm_configure") llvm_project = module_extension( implementation = lambda ctx: llvm_configure( name = "llvm-project", targets = [ "AArch64", "X86", ], ), ) ================================================ FILE: bazel/malloc/BUILD ================================================ # Part of the Carbon Language project, under the Apache License v2.0 with LLVM # Exceptions. See /LICENSE for license information. # SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception load("@bazel_skylib//lib:selects.bzl", "selects") load("//bazel/cc_rules:defs.bzl", "cc_library") config_setting( name = "is_linux", constraint_values = ["@platforms//os:linux"], ) config_setting( name = "opt", values = {"compilation_mode": "opt"}, ) selects.config_setting_group( name = "linux_opt", match_all = [ ":is_linux", ":opt", ], ) # A library that enables TCMalloc for optimized builds on Linux. On other # platforms and configurations, this falls back on the system malloc. cc_library( name = "tcmalloc_if_linux_opt", deps = select({ ":linux_opt": ["@tcmalloc//tcmalloc"], "//conditions:default": ["@bazel_tools//tools/cpp:malloc"], }), ) ================================================ FILE: bazel/manifest/BUILD ================================================ # Part of the Carbon Language project, under the Apache License v2.0 with LLVM # Exceptions. See /LICENSE for license information. # SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception # Empty; only defs.bzl is needed. ================================================ FILE: bazel/manifest/defs.bzl ================================================ # Part of the Carbon Language project, under the Apache License v2.0 with LLVM # Exceptions. See /LICENSE for license information. # SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception """Rule for producing a manifest for a filegroup.""" def _get_files(ctx): files = [] for src in ctx.attr.srcs: files.extend([f.path for f in src[DefaultInfo].files.to_list()]) files.extend([ f.path for f in src[DefaultInfo].default_runfiles.files.to_list() ]) if ctx.attr.strip_package_dir: # Files may or may not be prefixed with the bin directory, and then # may or may not be prefixed with the package directory. Strip both. bin_dir = ctx.bin_dir.path + "/" package_dir = ctx.label.package + "/" files_stripped = [f.removeprefix(bin_dir).removeprefix(package_dir) for f in files] else: files_stripped = files return files_stripped def _manifest(ctx): out = ctx.actions.declare_file(ctx.label.name) files = _get_files(ctx) ctx.actions.write(out, "\n".join(files) + "\n") return [ DefaultInfo( files = depset(direct = [out]), runfiles = ctx.runfiles(files = [out]), ), ] # Produces the manifest as a series of lines. manifest = rule( implementation = _manifest, attrs = { "srcs": attr.label_list(allow_files = True, mandatory = True), "strip_package_dir": attr.bool(default = False), }, ) def _manifest_as_cpp(ctx): out = ctx.actions.declare_file(ctx.label.name) files = _get_files(ctx) lines = [ "// Auto-generated by manifest_as_cpp.", "const char* {0}[] = {{".format(ctx.attr.var_name), ] lines += [ " \"{0}\",".format(file) for file in files ] lines += [ " nullptr,", "};", ] ctx.actions.write(out, "\n".join(lines) + "\n") return [ DefaultInfo( files = depset(direct = [out]), runfiles = ctx.runfiles(files = [out]), ), ] # Produces the manifest as a nullptr-terminated `const char* var_name[]`. # Use with `extern const char* var_name[];`. manifest_as_cpp = rule( implementation = _manifest_as_cpp, attrs = { "srcs": attr.label_list(allow_files = True, mandatory = True), "strip_package_dir": attr.bool(default = False), "var_name": attr.string(mandatory = True), }, ) ================================================ FILE: bazel/version/BUILD ================================================ # Part of the Carbon Language project, under the Apache License v2.0 with LLVM # Exceptions. See /LICENSE for license information. # SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception load("@bazel_skylib//rules:common_settings.bzl", "bool_flag", "int_flag", "string_flag") load("@rules_python//python:defs.bzl", "py_binary") package(default_visibility = ["//toolchain/install:__pkg__"]) py_binary( name = "gen_tmpl", srcs = ["gen_tmpl.py"], ) # Several flags are provided for customizing the exact version used for the # build of Carbon. Each of these is documented here, but rather than using the # label-based names in Bazel invocations (`bazel build --//bazel/version:flag`) # we suggest using the flag aliases provided in the project's `.bazelrc` and we # document the flags using those aliases. The aliases match the local flag names # here. # # For more details on the versioning scheme used by Carbon, see: # - https://docs.google.com/document/d/11S5VAPe5Pm_BZPlajWrqDDVr9qc7-7tS2VshqO0wWkk/edit?resourcekey=0-2YFC9Uvl4puuDnWlr2MmYw # TODO: Replace with path to the markdown once this lands. # # First, we provide a flag to enable a release version: `--release`. It is # disabled by default, and if enabled it must be the only version flag used. bool_flag( name = "release", build_setting_default = False, ) # A `--pre_release=KIND` flag where `KIND` must be one of: # - `rc` -- a release candidate version. # Example: `--pre_release=rc --rc_number=2` # - `nightly -- a nightly version. # Example: `--pre_release=nightly --nightly_date=2024.06.17` # - `dev` -- the default, a development build. # Example: `--pre_release=dev` # # This flag cannot be used along with `--release`, and for all but the `dev` # kind must be combined with one of the below flags to specify further details # of the version. string_flag( name = "pre_release", build_setting_default = "dev", values = [ "rc", "nightly", "dev", ], ) # `--rc_number=N` sets the release candidate number to `N`. Requires # `--pre_release=rc`. int_flag( name = "rc_number", build_setting_default = -1, ) # `--nigthly_date=YYYY.MM.DD` sets the nightly pre-release date. Requires # `--pre_release=nightly`. The value for this flag must be a string with the # exact format of `YYYY.MM.DD`. string_flag( name = "nightly_date", build_setting_default = "", ) # A config setting to observe the value of the `--stamp` command line flag # within starlark with a macro and `select`. This is a workaround suggested for # a Bazel issue: https://github.com/bazelbuild/bazel/issues/11164 config_setting( name = "internal_stamp_flag_detect", values = {"stamp": "1"}, visibility = ["//visibility:public"], ) ================================================ FILE: bazel/version/compute_version.bzl ================================================ # Part of the Carbon Language project, under the Apache License v2.0 with LLVM # Exceptions. See /LICENSE for license information. # SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception """Compute the version string.""" load("@bazel_skylib//rules:common_settings.bzl", "BuildSettingInfo") load("//:version_base.bzl", "version_base") def _validate_nightly_date(date): date_components = date.split(".", 2) if len(date_components) != 3: fail("Must provide a nightly date in 'YYYY.MM.DD' format, found '{}'.".format(date)) year = date_components[0] if len(year) != 4 or not year.isdigit(): fail("The nightly date year was not a sequence of four digits.") month = date_components[1] if len(month) != 2 or not month.isdigit(): fail("The nightly date month was not a sequence of two digits.") day = date_components[2] if len(day) != 2 or not day.isdigit(): fail("The nightly date day was not a sequence of two digits.") def compute_version(ctx): """Compute the version string. Args: ctx: The context for a rule computing the version. Returns: The version string. """ version = version_base # See if we need to append a pre-release suffix to the version. # # TODO: We should more fully check for erroneous combinations of flags here # to help ensure users don't get surprising results. if not ctx.attr._release_flag[BuildSettingInfo].value: pre_release = ctx.attr._pre_release_flag[BuildSettingInfo].value pre_release_numbers = { "rc": ctx.attr._rc_number_flag[BuildSettingInfo].value, } if pre_release in pre_release_numbers: number = pre_release_numbers[pre_release] if number < 0: fail("Must provide a non-negative {} number when building that pre-release.".format(pre_release)) version += "-{0}.{1}".format(pre_release, number) elif pre_release == "nightly": date = ctx.attr._nightly_date_flag[BuildSettingInfo].value _validate_nightly_date(date) version += "-0.nightly.{}".format(date) elif pre_release == "dev": version += "-0.dev" else: fail("Invalid pre-release flag: " + pre_release) return version VERSION_ATTRS = { "_nightly_date_flag": attr.label(default = ":nightly_date"), "_pre_release_flag": attr.label(default = ":pre_release"), "_rc_number_flag": attr.label(default = ":rc_number"), "_release_flag": attr.label(default = ":release"), } ================================================ FILE: bazel/version/gen_tmpl.py ================================================ #!/usr/bin/env python3 """Generate a file from a template, substituting the provided key/value pairs. The file format should match Python's `string.Template` substitution rules: - `$$` for a literal `$` - `$identifier` for some key `identifier` to be substituted - `${identifier}` when adjacent text would be interpreted as part of the identifier. The keys must be strings that are valid identifiers: `[_A-Za-z][_A-Za-z0-9]*` The values may not contain newlines or any vertical whitespace. The initial key/value pairs are read from the command line using repeated `--substitute=KEY=DEFAULT-VALUE` flags. Updated values for those keys will be read from any files provided to the `--status-file` flag. This flag can be given multiple times and the values will be read and updated from the files in order, meaning the last file's value will win. New keys are never read from these files. The file format parsed is Bazel's [status file format](https://bazel.build/docs/user-manual#workspace-status): each line is a single entry starting with a key using only characters `[_A-Z]`, one space character, and the rest of the line is the value. To assist with using Bazel status files, if the key parsed from the file begins with `STABLE_`, that prefix is removed. Any keys which are present in the substitutions provided on the command line will have their value updated with the string read from the file. """ __copyright__ = """ Part of the Carbon Language project, under the Apache License v2.0 with LLVM Exceptions. See /LICENSE for license information. SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception """ import argparse import sys from pathlib import Path from string import Template def main() -> None: parser = argparse.ArgumentParser(__doc__) parser.add_argument( "--template", metavar="FILE", type=Path, required=True, help="The template source file to use.", ) parser.add_argument( "--output", metavar="FILE", type=Path, required=True, help="The output source file to produce.", ) parser.add_argument( "--substitution", metavar="KEY=DEFAULT-VALUE", action="append", help="A substitution that should be supported and its default value.", ) parser.add_argument( "--status-file", metavar="FILE", type=Path, action="append", default=[], help="A file of key/value updates in Bazel's status file format.", ) parser.add_argument("-v", "--verbose", action="store_true") args = parser.parse_args() # Collect the supported substitutions from the command line. substitutions = {} for substitution_arg in args.substitution: key, value = substitution_arg.split("=", 1) substitutions.update({key: value}) # Read either of the two status files provided to build up substitutions, # with the stable file last so its values override any duplicates. for status_file in args.status_file: if args.verbose: print(f"Reading status file: {status_file}", file=sys.stderr) for line in status_file.open(): # Remove line endings. line = line.rstrip("\r\n") # Exactly matches our pattern key, value = line.split(" ", 1) key = key.removeprefix("STABLE_") if key in substitutions: if args.verbose: print(f"Parsed: '{key}': '{value}'", file=sys.stderr) substitutions.update({key: value}) if args.verbose: print(f"Reading template file: {args.template}", file=sys.stderr) with open(args.template) as template_file: template = template_file.read() result = Template(template).substitute(substitutions) if args.verbose: print(f"Writing output file: {args.output}", file=sys.stderr) with open(args.output, mode="w") as output_file: output_file.write(result) if __name__ == "__main__": main() ================================================ FILE: bazel/version/rules.bzl ================================================ # Part of the Carbon Language project, under the Apache License v2.0 with LLVM # Exceptions. See /LICENSE for license information. # SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception """Rule to expand Bazel templates with version and build information. This rule takes a source code template and turns that into a specific source code output, substituting version information and build information from Bazel's `stable-status.txt` and `volatile-status.txt` produced by the `workpsace_status_command` during the build. When stamping is disabled, the build information is replaced with constant values to provide better caching. The template files should use Python's "template strings" syntax[1]. These rules provide a fixed set of keys whose values will be substituted, and those keys will always be substituted with something. They will have the value in the stable status file if present, otherwise the value in the volatile status file if present, otherwise the value "unknown". When reading keys from the status files, a prefix of `STABLE_` will be removed from the key if present. [1]: https://docs.python.org/3/library/string.html#template-strings The substituted keys, and any guidance on values: - `VERSION` (the version string for Carbon) - `BUILD_EMBED_LABEL` (value of --embed_label) - `BUILD_HOST` (the name of the host machine running the build) - `BUILD_USER` (the name of the user running the build) - `GIT_COMMIT_SHA` (output of `git parse-rev --short HEAD` or `unknown`) - `GIT_DIRTY_SUFFIX` (`.dirty` if dirty client state or `` if unknown) - `BUILD_TIMESTAMP` (the time of the build in seconds since the Unix Epoch) - `ATTRIBUTE` (an optional attribute to apply to definitions, defaults to empty) """ load(":compute_version.bzl", "compute_version") _STAMP_DOC = """ Follows behavior of the common 'stamp' attributes on rules. Set to 1 or 0 to force stamping with actual build info on or off respectively, and to -1 to follow the value of the command line flag `--stamp`. """ def _is_exec_config(ctx): """Detect if this is the exec configuration, previously known as "host". Sadly, there is not yet a supported way to detect this so replicate the hacks others currently use. Bazel issue: https://github.com/bazelbuild/bazel/issues/14444 """ return "-exec" in ctx.bin_dir.path or "/host/" in ctx.bin_dir.path def _expand_version_build_info_impl(ctx): """Generates a file from a template, substituting version and build info.""" inputs = [ctx.file.template] # The substitutions provided and their default values. substitutions = { "BUILD_EMBED_LABEL": "unknown", "BUILD_HOST": "unknown", "BUILD_TIMESTAMP": "unknown", "BUILD_USER": "unknown", "GIT_COMMIT_SHA": "unknown", "GIT_DIRTY_SUFFIX": "", "MAKE_WEAK": "0", "VERSION": compute_version(ctx), } substitutions.update(ctx.attr.substitutions) arguments = [ "--template=" + ctx.file.template.path, "--output=" + ctx.outputs.out.path, ] + [ "--substitution=" + key + "=" + value for key, value in substitutions.items() ] # We only want to allow stamping outside of the exec configuration. if not _is_exec_config(ctx): # Look at the attribute. stamp = ctx.attr.stamp # If requested, use the command line flag to select. if stamp == -1: # Set the default from `--stamp` / `--nostamp` command line flag, # which we detect through a macro and `config_setting`, and pipe # through an attribute. stamp = 1 if ctx.attr.internal_stamp_flag_detect else 0 # Add the status files if stamping. if stamp == 1: inputs += [ ctx.info_file, ctx.version_file, ] arguments += [ "--status-file=" + ctx.info_file.path, "--status-file=" + ctx.version_file.path, ] ctx.actions.run( inputs = inputs, outputs = [ctx.outputs.out], executable = ctx.executable._gen_tmpl_tool, arguments = arguments, progress_message = "Generating templated source file: " + ctx.outputs.out.short_path, ) expand_version_build_info_internal = rule( implementation = _expand_version_build_info_impl, attrs = { "internal_stamp_flag_detect": attr.bool(default = False), "out": attr.output(mandatory = True), "stamp": attr.int(values = [-1, 0, 1], default = -1, doc = _STAMP_DOC), "substitutions": attr.string_dict( doc = "Extra substitutions, potentially overriding defaults.", ), "template": attr.label( allow_single_file = True, ), "_gen_tmpl_tool": attr.label( default = Label("//bazel/version:gen_tmpl"), allow_files = True, executable = True, cfg = "exec", ), "_nightly_date_flag": attr.label(default = ":nightly_date"), "_pre_release_flag": attr.label(default = ":pre_release"), "_rc_number_flag": attr.label(default = ":rc_number"), "_release_flag": attr.label(default = ":release"), }, ) # We need a macro wrapping the rule so that we can inject a select that observes # the `--stamp` command-line value and use that when needed. def expand_version_build_info(name, **kwargs): expand_version_build_info_internal( name = name, internal_stamp_flag_detect = False if kwargs.get("stamp") == 0 else select({ "//bazel/version:internal_stamp_flag_detect": True, "//conditions:default": False, }), **kwargs ) ================================================ FILE: common/BUILD ================================================ # Part of the Carbon Language project, under the Apache License v2.0 with LLVM # Exceptions. See /LICENSE for license information. # SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception load("@rules_shell//shell:sh_test.bzl", "sh_test") load("//bazel/cc_rules:defs.bzl", "cc_binary", "cc_library", "cc_test") load("//bazel/version:rules.bzl", "expand_version_build_info") package(default_visibility = ["//visibility:public"]) cc_library( name = "array_stack", hdrs = ["array_stack.h"], deps = [ ":check", "@llvm-project//llvm:Support", ], ) cc_test( name = "array_stack_test", size = "small", srcs = ["array_stack_test.cpp"], deps = [ ":array_stack", "//testing/base:gtest_main", "@googletest//:gtest", ], ) cc_library( name = "bazel_working_dir", hdrs = ["bazel_working_dir.h"], deps = [ ":check", ":filesystem", "@llvm-project//llvm:Support", ], ) cc_library( name = "build_data", srcs = [ "build_data.cpp", "build_data_linkstamp.h", ], hdrs = ["build_data.h"], linkstamp = "build_data_linkstamp.cpp", deps = ["@llvm-project//llvm:Support"], ) cc_test( name = "build_data_test", size = "small", srcs = ["build_data_test.cpp"], deps = [ ":build_data", ":ostream", "//testing/base:gtest_main", "@googletest//:gtest", ], ) cc_library( name = "command_line", srcs = ["command_line.cpp"], hdrs = ["command_line.h"], deps = [ ":check", ":error", ":ostream", ":raw_string_ostream", "@llvm-project//llvm:Support", ], ) cc_test( name = "command_line_test", size = "small", srcs = ["command_line_test.cpp"], deps = [ ":command_line", ":error_test_helpers", ":raw_string_ostream", "//testing/base:gtest_main", "@googletest//:gtest", "@llvm-project//llvm:Support", ], ) cc_library( name = "check", srcs = [ "check_internal.cpp", "check_internal.h", ], hdrs = ["check.h"], deps = [ ":ostream", ":template_string", "@llvm-project//llvm:Support", ], ) cc_test( name = "check_test", size = "small", srcs = ["check_test.cpp"], deps = [ ":check", "//testing/base:gtest_main", "@googletest//:gtest", ], ) cc_library( name = "concepts", hdrs = ["concepts.h"], ) cc_library( name = "emplace_by_calling", hdrs = ["emplace_by_calling.h"], ) cc_test( name = "emplace_by_calling_test", srcs = ["emplace_by_calling_test.cpp"], deps = [ ":emplace_by_calling", "//testing/base:gtest_main", "@googletest//:gtest", ], ) cc_library( name = "enum_base", hdrs = ["enum_base.h"], deps = [ "//common:ostream", "@llvm-project//llvm:Support", ], ) cc_library( name = "enum_base_test_def", testonly = 1, textual_hdrs = ["enum_base_test.def"], ) cc_test( name = "enum_base_test", size = "small", srcs = ["enum_base_test.cpp"], deps = [ ":enum_base", ":enum_base_test_def", ":raw_string_ostream", "//testing/base:gtest_main", "@googletest//:gtest", ], ) cc_library( name = "enum_mask_base", hdrs = ["enum_mask_base.h"], deps = [ ":enum_base", "@llvm-project//llvm:Support", ], ) cc_test( name = "enum_mask_base_test", size = "small", srcs = ["enum_mask_base_test.cpp"], deps = [ ":enum_mask_base", ":raw_string_ostream", "//testing/base:gtest_main", "@googletest//:gtest", ], ) cc_library( name = "error", hdrs = ["error.h"], deps = [ ":check", ":ostream", ":raw_string_ostream", "@llvm-project//llvm:Support", ], ) cc_library( name = "error_test_helpers", testonly = 1, hdrs = ["error_test_helpers.h"], deps = [ ":error", ":ostream", "@googletest//:gtest", ], ) cc_test( name = "error_test", size = "small", srcs = ["error_test.cpp"], deps = [ ":error", ":error_test_helpers", ":raw_string_ostream", "//testing/base:gtest_main", "@googletest//:gtest", ], ) cc_library( name = "exe_path", srcs = ["exe_path.cpp"], hdrs = ["exe_path.h"], deps = [ "@llvm-project//llvm:Support", ], ) cc_test( name = "exe_path_test", size = "small", srcs = ["exe_path_test.cpp"], deps = [ ":exe_path", "//testing/base:gtest_main", "@googletest//:gtest", "@llvm-project//llvm:Support", ], ) cc_library( name = "filesystem", srcs = ["filesystem.cpp"], hdrs = ["filesystem.h"], deps = [ ":build_data", ":check", ":error", ":ostream", ":raw_string_ostream", ":template_string", "@llvm-project//llvm:Support", ], ) cc_test( name = "filesystem_test", size = "small", srcs = ["filesystem_test.cpp"], deps = [ ":error_test_helpers", ":filesystem", "//testing/base:gtest_main", "@googletest//:gtest", "@llvm-project//llvm:Support", ], ) cc_binary( name = "filesystem_benchmark", testonly = 1, srcs = ["filesystem_benchmark.cpp"], deps = [ ":check", ":filesystem", "//testing/base:benchmark_main", "@abseil-cpp//absl/hash", "@abseil-cpp//absl/random", "@google_benchmark//:benchmark", "@llvm-project//llvm:Support", ], ) sh_test( name = "filesystem_benchmark_test", size = "small", srcs = [":filesystem_benchmark"], args = [ "--benchmark_dry_run", # Restrict the sizes to 4-digit ones or smaller to keep test times low. # The `$$` is repeated for Bazel escaping of `$`. "--benchmark_filter=^[^/]+(/[0-9]{1,4}(/[0-9]+)?)?/real_time$$", ], ) cc_library( name = "find", hdrs = ["find.h"], deps = [ ":check", ":ostream", ":raw_string_ostream", "@llvm-project//llvm:Support", ], ) cc_test( name = "find_test", size = "small", srcs = ["find_test.cpp"], deps = [ ":find", "//testing/base:gtest_main", "@googletest//:gtest", ], ) cc_library( name = "growing_range", hdrs = ["growing_range.h"], ) cc_test( name = "growing_range_test", size = "small", srcs = ["growing_range_test.cpp"], deps = [ ":growing_range", "//testing/base:gtest_main", "@googletest//:gtest", ], ) cc_library( name = "hashing", srcs = ["hashing.cpp"], hdrs = ["hashing.h"], deps = [ ":check", ":ostream", "@llvm-project//llvm:Support", ], ) cc_test( name = "hashing_test", size = "small", srcs = ["hashing_test.cpp"], deps = [ ":hashing", ":raw_string_ostream", "//testing/base:gtest_main", "@googletest//:gtest", "@llvm-project//llvm:Support", ], ) cc_binary( name = "hashing_benchmark", testonly = 1, srcs = ["hashing_benchmark.cpp"], deps = [ ":check", ":hashing", "//testing/base:benchmark_main", "@abseil-cpp//absl/hash", "@abseil-cpp//absl/random", "@google_benchmark//:benchmark", "@llvm-project//llvm:Support", ], ) cc_library( name = "hashtable_key_context", hdrs = ["hashtable_key_context.h"], deps = [ ":hashing", "@llvm-project//llvm:Support", ], ) cc_test( name = "hashtable_key_context_test", size = "small", srcs = ["hashtable_key_context_test.cpp"], deps = [ ":hashtable_key_context", "//testing/base:gtest_main", "@googletest//:gtest", "@llvm-project//llvm:Support", ], ) cc_library( name = "init_llvm", srcs = ["init_llvm.cpp"], hdrs = ["init_llvm.h"], deps = [ "@llvm-project//llvm:Support", ], ) # Link against this to cause `:init_llvm` to pull in all LLVM targets. # # Be careful when depending on this: it pulls in several hundred megabytes of # LLVM binary size in -c fastbuild. This should only be depended on by a # `cc_binary` or `cc_test` target, never a `cc_library`. cc_library( name = "all_llvm_targets", srcs = ["all_llvm_targets.cpp"], deps = [ ":init_llvm", "@llvm-project//llvm:AllTargetsAsmParsers", "@llvm-project//llvm:AllTargetsCodeGens", "@llvm-project//llvm:Support", ], alwayslink = 1, ) cc_library( name = "latch", srcs = ["latch.cpp"], hdrs = ["latch.h"], deps = [ ":check", "@llvm-project//llvm:Support", ], ) cc_test( name = "latch_test", size = "small", srcs = ["latch_test.cpp"], deps = [ ":latch", "//testing/base:gtest_main", "@googletest//:gtest", "@llvm-project//llvm:Support", ], ) cc_library( name = "map", hdrs = ["map.h"], deps = [ ":check", ":concepts", ":hashtable_key_context", ":raw_hashtable", "@llvm-project//llvm:Support", ], ) cc_test( name = "map_test", size = "small", srcs = ["map_test.cpp"], deps = [ ":map", ":raw_hashtable_test_helpers", "//testing/base:gtest_main", "@googletest//:gtest", ], ) cc_binary( name = "map_benchmark", testonly = 1, srcs = ["map_benchmark.cpp"], deps = [ ":map", ":raw_hashtable_benchmark_helpers", "//testing/base:benchmark_main", "@abseil-cpp//absl/container:flat_hash_map", "@abseil-cpp//absl/random", "@boost.unordered", "@google_benchmark//:benchmark", "@llvm-project//llvm:Support", ], ) sh_test( name = "map_benchmark_test", # The benchmark allocates a large amount of memory. size = "enormous", # We configure the test to run somewhat quickly. timeout = "moderate", srcs = [":map_benchmark"], args = [ "--benchmark_dry_run", # The `$$` is repeated for Bazel escaping of `$`. "--benchmark_filter=^[^/]*/[1-9][0-9]{0,3}(/[0-9]+)?$$", ], ) cc_library( name = "move_only", hdrs = ["move_only.h"], ) cc_library( name = "ostream", hdrs = ["ostream.h"], deps = [ "@llvm-project//llvm:Support", ], ) cc_library( name = "pretty_stack_trace_function", hdrs = ["pretty_stack_trace_function.h"], deps = [ "@llvm-project//llvm:Support", ], ) cc_library( name = "raw_hashtable", srcs = ["raw_hashtable.cpp"], hdrs = ["raw_hashtable.h"], deps = [ ":check", ":concepts", ":hashing", ":hashtable_key_context", ":raw_hashtable_metadata_group", "@llvm-project//llvm:Support", ], ) cc_library( name = "raw_hashtable_metadata_group", srcs = ["raw_hashtable_metadata_group.cpp"], hdrs = ["raw_hashtable_metadata_group.h"], deps = [ ":check", ":ostream", "@llvm-project//llvm:Support", ], ) cc_binary( name = "raw_hashtable_metadata_group_benchmark", testonly = 1, srcs = ["raw_hashtable_metadata_group_benchmark.cpp"], deps = [ ":raw_hashtable_metadata_group", "//testing/base:benchmark_main", "@abseil-cpp//absl/random", "@google_benchmark//:benchmark", "@llvm-project//llvm:Support", ], ) sh_test( name = "raw_hashtable_metadata_group_benchmark_test", size = "small", srcs = ["raw_hashtable_metadata_group_benchmark"], args = [ "--benchmark_dry_run", ], ) cc_library( name = "raw_hashtable_benchmark_helpers", testonly = 1, srcs = ["raw_hashtable_benchmark_helpers.cpp"], hdrs = ["raw_hashtable_benchmark_helpers.h"], copts = [ "-O2", # Always optimize to make testing benchmarks faster. ], deps = [ ":check", ":hashing", ":raw_hashtable", ":set", "@abseil-cpp//absl/base:no_destructor", "@abseil-cpp//absl/hash", "@abseil-cpp//absl/random", "@boost.unordered", "@google_benchmark//:benchmark", "@llvm-project//llvm:Support", ], ) cc_library( name = "raw_hashtable_test_helpers", testonly = 1, hdrs = ["raw_hashtable_test_helpers.h"], deps = [ ":check", ":hashing", ":hashtable_key_context", ":ostream", ], ) cc_library( name = "raw_string_ostream", hdrs = ["raw_string_ostream.h"], deps = [ ":check", ":ostream", ], ) cc_test( name = "raw_string_ostream_test", size = "small", srcs = ["raw_string_ostream_test.cpp"], deps = [ ":raw_string_ostream", "//testing/base:gtest_main", "@googletest//:gtest", ], ) cc_library( name = "set", hdrs = ["set.h"], deps = [ ":check", ":hashtable_key_context", ":raw_hashtable", "@llvm-project//llvm:Support", ], ) cc_test( name = "set_test", size = "small", srcs = ["set_test.cpp"], deps = [ ":raw_hashtable_test_helpers", ":set", "//testing/base:gtest_main", "@googletest//:gtest", ], ) cc_binary( name = "set_benchmark", testonly = 1, srcs = ["set_benchmark.cpp"], deps = [ ":raw_hashtable_benchmark_helpers", ":set", "//testing/base:benchmark_main", "@abseil-cpp//absl/container:flat_hash_set", "@google_benchmark//:benchmark", "@llvm-project//llvm:Support", ], ) sh_test( name = "set_benchmark_test", # The benchmark allocates a large amount of memory. size = "enormous", # We configure the test to run somewhat quickly. timeout = "moderate", srcs = [":set_benchmark"], args = [ "--benchmark_dry_run", # The `$$` is repeated for Bazel escaping of `$`. "--benchmark_filter=^[^/]*/[1-9][0-9]{0,3}(/[0-9]+)?$$", ], ) cc_library( name = "string_helpers", srcs = ["string_helpers.cpp"], hdrs = ["string_helpers.h"], deps = [ ":check", ":error", "@llvm-project//llvm:Support", ], ) cc_test( name = "string_helpers_test", size = "small", srcs = ["string_helpers_test.cpp"], deps = [ ":string_helpers", "//testing/base:gtest_main", "@googletest//:gtest", "@llvm-project//llvm:Support", ], ) cc_library( name = "struct_reflection", hdrs = ["struct_reflection.h"], ) cc_test( name = "struct_reflection_test", size = "small", srcs = ["struct_reflection_test.cpp"], deps = [ ":struct_reflection", "//testing/base:gtest_main", "@googletest//:gtest", ], ) cc_library( name = "template_string", hdrs = ["template_string.h"], deps = [ "@llvm-project//llvm:Support", ], ) cc_test( name = "template_string_test", size = "small", srcs = ["template_string_test.cpp"], deps = [ ":template_string", "//testing/base:gtest_main", "@googletest//:gtest", ], ) cc_library( name = "type_enum", hdrs = ["type_enum.h"], deps = [":ostream"], ) # The base version source file only uses non-stamped parts of the version # information so we expand it once here without any stamping. expand_version_build_info( name = "version_cpp_gen", out = "version.cpp", stamp = 0, template = "version.tmpl.cpp", ) # Build a nostamp version of the stamp source, but mark its definitions as weak. # We'll include this in the library to satisfy definitions of library and test # users, but still allow binaries that want full build stamping to depend on the # stamp library below to override with strong, stamped definitions. expand_version_build_info( name = "version_nostamp_cpp_gen", out = "version_nostamp.cpp", stamp = 0, substitutions = {"MAKE_WEAK": "1"}, template = "version_stamp.tmpl.cpp", ) # Provides APIs for accessing Carbon version information. # # These provide full access to the major, minor, and patch version. It also # provides an API for querying version strings that may contain detailed build # information such as the commit SHA. # # By default, this provides the API and an *unstamped* implementations of # version strings. As a consequence, depending on this library doesn't introduce # any dependency on the commit SHA or loss of build caching. # # Targets that want full build info stamping in the data produced by these APIs # should additionally depend on `:version_stamp` below -- the data these APIs # return will be overridden in any binaries depending on that rule with the # fully stamped details. cc_library( name = "version", srcs = [ "version.cpp", "version_nostamp.cpp", ], hdrs = ["version.h"], deps = [ "@llvm-project//llvm:Support", ], ) # Generate the fully stamped sourcefile if stamping is enabled in the build. expand_version_build_info( name = "version_stamp_cpp_gen", out = "version_stamp.cpp", template = "version_stamp.tmpl.cpp", ) # Depend on this library to enable fully-stamped build information in the # version API provided by `:version`. This doesn't provide the API, it injects # an override of stamped versions of the data. # # Note that depending on this will significantly reduce build caching with # `--stamp` builds. It should be used sparingly, typically in user-facing # binaries or systems that need to render a maximally detailed version string # with build information stamped into it. cc_library( name = "version_stamp", srcs = ["version_stamp.cpp"], deps = [ ":version", "@llvm-project//llvm:Support", ], ) cc_library( name = "vlog", hdrs = ["vlog.h"], deps = [ ":ostream", ":template_string", "@llvm-project//llvm:Support", ], ) cc_test( name = "vlog_test", size = "small", srcs = ["vlog_test.cpp"], deps = [ ":raw_string_ostream", ":vlog", "//testing/base:gtest_main", "@googletest//:gtest", ], ) ================================================ FILE: common/all_llvm_targets.cpp ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #include "common/init_llvm.h" #include "llvm/Support/TargetSelect.h" namespace Carbon { static auto InitLLVMTargets() -> void { llvm::InitializeAllTargetInfos(); llvm::InitializeAllTargets(); llvm::InitializeAllTargetMCs(); llvm::InitializeAllAsmParsers(); llvm::InitializeAllAsmPrinters(); } // On program startup, set `InitLLVM::InitializeTargets` to be our // initialization function so that `InitLLVM` can call it at the right moment. const char InitLLVM::RegisterTargets = (InitializeTargets = &InitLLVMTargets, 0); } // namespace Carbon ================================================ FILE: common/array_stack.h ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #ifndef CARBON_COMMON_ARRAY_STACK_H_ #define CARBON_COMMON_ARRAY_STACK_H_ #include "common/check.h" #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/SmallVector.h" namespace Carbon { // Provides a stack of arrays. Only the array at the top of the stack can have // elements added. // // Example usage: // // Push to start. // PushArray(); // // Add values. // AppendToTop(3); // // Look at values. // PeekArray(); // // Pop when done. // PopArray(); // // By using a single vector for elements, the intent is that as arrays are // pushed and popped, the same storage will be reused. This should yield // efficiencies for heap allocations. For example, in the toolchain we // frequently have an array per scope, and only add to the current scope's // array; this allows better reuse when entering and leaving scopes. template class ArrayStack { public: // Pushes a new array onto the stack. auto PushArray() -> void { array_offsets_.push_back(values_.size()); } // Pops the top array from the stack. auto PopArray() -> void { auto region = array_offsets_.pop_back_val(); values_.truncate(region); } // Returns the top array from the stack. auto PeekArray() const -> llvm::ArrayRef { CARBON_CHECK(!array_offsets_.empty()); return llvm::ArrayRef(values_).slice(array_offsets_.back()); } auto PeekArray() -> llvm::MutableArrayRef { CARBON_CHECK(!array_offsets_.empty()); return llvm::MutableArrayRef(values_).slice(array_offsets_.back()); } // Returns the array at a specific index. auto PeekArrayAt(int index) const -> llvm::ArrayRef { auto ref = llvm::ArrayRef(values_).slice(array_offsets_[index]); if (index + 1 < static_cast(array_offsets_.size())) { ref = ref.take_front(array_offsets_[index + 1] - array_offsets_[index]); } return ref; } // Returns the full set of values on the stack, regardless of whether any // arrays are pushed. auto PeekAllValues() const -> llvm::ArrayRef { return values_; } // Appends a value to the top array on the stack. auto AppendToTop(const ValueT& value) -> void { CARBON_CHECK(!array_offsets_.empty(), "Must call PushArray before AppendToTop."); values_.push_back(value); } // Appends a value to the top array on the stack. auto AppendToTop(ValueT&& value) -> void { CARBON_CHECK(!array_offsets_.empty(), "Must call PushArray before AppendToTop."); values_.push_back(std::move(value)); } // Adds multiple values to the top array on the stack. auto AppendToTop(llvm::ArrayRef values) -> void { CARBON_CHECK(!array_offsets_.empty(), "Must call PushArray before AppendToTop."); llvm::append_range(values_, values); } // Returns the current number of values in all arrays. auto all_values_size() const -> size_t { return values_.size(); } // Returns true if the stack has no arrays pushed. auto empty() const -> bool { return array_offsets_.empty(); } private: // For each pushed array, the start index in `values_`. llvm::SmallVector array_offsets_; // The full set of elements in all arrays. llvm::SmallVector values_; }; } // namespace Carbon #endif // CARBON_COMMON_ARRAY_STACK_H_ ================================================ FILE: common/array_stack_test.cpp ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #include "common/array_stack.h" #include #include namespace Carbon::Testing { namespace { using ::testing::ElementsAre; using ::testing::IsEmpty; TEST(ArrayStack, Basics) { ArrayStack stack; // PeekAllValues is valid when there are no arrays. EXPECT_THAT(stack.PeekAllValues(), IsEmpty()); // An array starts empty. stack.PushArray(); EXPECT_THAT(stack.PeekArray(), IsEmpty()); EXPECT_THAT(stack.PeekAllValues(), IsEmpty()); // Pushing a couple values works. stack.AppendToTop(1); stack.AppendToTop(2); EXPECT_THAT(stack.PeekArray(), ElementsAre(1, 2)); EXPECT_THAT(stack.PeekAllValues(), ElementsAre(1, 2)); // Pushing a new array starts empty, old values are still there. stack.PushArray(); EXPECT_THAT(stack.PeekArray(), IsEmpty()); EXPECT_THAT(stack.PeekAllValues(), ElementsAre(1, 2)); // The added value goes to the 2nd array. stack.AppendToTop(3); EXPECT_THAT(stack.PeekArray(), ElementsAre(3)); EXPECT_THAT(stack.PeekAllValues(), ElementsAre(1, 2, 3)); // Popping goes back to the 1st array. stack.PopArray(); EXPECT_THAT(stack.PeekArray(), ElementsAre(1, 2)); EXPECT_THAT(stack.PeekAllValues(), ElementsAre(1, 2)); // Push a couple arrays, then a value on the now-3rd array. stack.PushArray(); stack.PushArray(); stack.AppendToTop(4); EXPECT_THAT(stack.PeekArray(), ElementsAre(4)); EXPECT_THAT(stack.PeekAllValues(), ElementsAre(1, 2, 4)); // Popping the 3rd array goes to the 2nd array, which is empty. stack.PopArray(); EXPECT_THAT(stack.PeekArray(), IsEmpty()); EXPECT_THAT(stack.PeekAllValues(), ElementsAre(1, 2)); // Again back to the 1st array. stack.PopArray(); EXPECT_THAT(stack.PeekArray(), ElementsAre(1, 2)); EXPECT_THAT(stack.PeekAllValues(), ElementsAre(1, 2)); // Go down to no arrays. stack.PopArray(); EXPECT_THAT(stack.PeekAllValues(), IsEmpty()); // Add a new 1st array. stack.PushArray(); stack.AppendToTop(5); EXPECT_THAT(stack.PeekArray(), ElementsAre(5)); EXPECT_THAT(stack.PeekAllValues(), ElementsAre(5)); } TEST(ArrayStack, AppendArray) { ArrayStack stack; stack.PushArray(); stack.AppendToTop(llvm::ArrayRef()); EXPECT_THAT(stack.PeekArray(), IsEmpty()); stack.AppendToTop({1, 2}); EXPECT_THAT(stack.PeekArray(), ElementsAre(1, 2)); } TEST(ArrayStack, PeekArrayAt) { ArrayStack stack; // Verify behavior with a single array. stack.PushArray(); stack.AppendToTop(1); stack.AppendToTop(2); EXPECT_THAT(stack.PeekArrayAt(0), ElementsAre(1, 2)); // Verify behavior with a couple more arrays. stack.PushArray(); stack.PushArray(); stack.AppendToTop(3); EXPECT_THAT(stack.PeekArrayAt(0), ElementsAre(1, 2)); EXPECT_THAT(stack.PeekArrayAt(1), IsEmpty()); EXPECT_THAT(stack.PeekArrayAt(2), ElementsAre(3)); } } // namespace } // namespace Carbon::Testing ================================================ FILE: common/bazel_working_dir.h ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #ifndef CARBON_COMMON_BAZEL_WORKING_DIR_H_ #define CARBON_COMMON_BAZEL_WORKING_DIR_H_ #include #include #include #include "common/check.h" #include "common/filesystem.h" namespace Carbon { // Change working directory to behave as if it is where `bazel run` was invoked. // // Accepts an optional `exe_path` argument that will be adjusted to continue to // be valid after this adjustment. // // There is no reasonable recovery we can do if either we can't make the path // absolute or we can't change directory. As a consequence, this aborts if // either of those fail rather than propagating any error. inline auto SetWorkingDirForBazelRun(std::filesystem::path exe_path = {}) -> std::filesystem::path { char* build_working_dir = getenv("BUILD_WORKING_DIRECTORY"); if (build_working_dir == nullptr) { return exe_path; } // Adjust `exe_path` before changing directory. if (!exe_path.empty()) { std::error_code err; exe_path = std::filesystem::absolute(exe_path, err); CARBON_CHECK(!err, "Unable to make an absolute path for `{0}`: {1}", exe_path, err.message()); } auto chdir_result = Filesystem::Cwd().Chdir(build_working_dir); CARBON_CHECK(chdir_result.ok(), "Unable to change working directory to `{0}`: {1}", build_working_dir, chdir_result.error()); return exe_path; } } // namespace Carbon #endif // CARBON_COMMON_BAZEL_WORKING_DIR_H_ ================================================ FILE: common/build_data.cpp ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #include "common/build_data.h" namespace Carbon::BuildData { const llvm::StringRef Platform = Internal::platform; const bool BuildCoverageEnabled = Internal::build_coverage_enabled; const llvm::StringRef TargetName = Internal::target_name; const llvm::StringRef BuildTarget = Internal::build_target; } // namespace Carbon::BuildData ================================================ FILE: common/build_data.h ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #ifndef CARBON_COMMON_BUILD_DATA_H_ #define CARBON_COMMON_BUILD_DATA_H_ #include "common/build_data_linkstamp.h" #include "llvm/ADT/StringRef.h" namespace Carbon::BuildData { // Build information for a binary, from bazel. Stamped values come from: // https://github.com/bazelbuild/bazel/blob/master/src/main/java/com/google/devtools/build/lib/rules/cpp/CppLinkstampCompileHelper.java // NOLINTBEGIN(readability-identifier-naming): We want to use constant-style // names for the public variables, but cannot use constexpr. // The platform, per https://bazel.build/extending/platforms. extern const llvm::StringRef Platform; // Whether coverage is enabled. extern const bool BuildCoverageEnabled; // The binary target, such as `//common:build_data_test`. extern const llvm::StringRef TargetName; // The path to the build target, such as // `bazel-out/k8-fastbuild/bin/common/build_data_test`. extern const llvm::StringRef BuildTarget; // NOLINTEND(readability-identifier-naming) } // namespace Carbon::BuildData #endif // CARBON_COMMON_BUILD_DATA_H_ ================================================ FILE: common/build_data_linkstamp.cpp ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #include "common/build_data_linkstamp.h" namespace Carbon::BuildData::Internal { const char platform[] = GPLATFORM; const bool build_coverage_enabled = BUILD_COVERAGE_ENABLED; const char target_name[] = G3_TARGET_NAME; const char build_target[] = G3_BUILD_TARGET; } // namespace Carbon::BuildData::Internal ================================================ FILE: common/build_data_linkstamp.h ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #ifndef CARBON_COMMON_BUILD_DATA_LINKSTAMP_H_ #define CARBON_COMMON_BUILD_DATA_LINKSTAMP_H_ namespace Carbon::BuildData::Internal { // See build_data.h; the list of names here should match. // // Bazel will build dependencies on the `:build_data` library (which exposes // `build_data_linkstamp.h`) throughout the build process, but // `build_data_linkstamp.cpp` is compiled and linked per-binary -- essentially a // separate library. In essence, `build_data_linkstamp.h` is exposing values // that are assigned later (this has consequences like preventing `constexpr` // use). // // Also, when build_data_linkstamp.cpp is compiled, this doesn't receive deps, // so we can't use things like `llvm::StringRef` here. It should ideally be // purely hermetic -- not even using STL for `string_view`. As a result, we use // `build_data.h` as an intermediary to do a `StringRef` wrap. extern const char platform[]; extern const bool build_coverage_enabled; extern const char target_name[]; extern const char build_target[]; } // namespace Carbon::BuildData::Internal #endif // CARBON_COMMON_BUILD_DATA_LINKSTAMP_H_ ================================================ FILE: common/build_data_test.cpp ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #include "common/build_data.h" #include #include "common/ostream.h" namespace Carbon { namespace { using ::testing::EndsWith; using ::testing::HasSubstr; TEST(BuildDataTest, Values) { EXPECT_FALSE(BuildData::Platform.empty()); // Can't really test BuildCoverageEnabled. // This doesn't require `//`, for a bit of incremental robustness. EXPECT_THAT(BuildData::TargetName, EndsWith("/common:build_data_test")); // This doesn't examine the path, for platform robustness (e.g. `.exe`). EXPECT_THAT(BuildData::BuildTarget, HasSubstr("build_data_test")); } } // namespace } // namespace Carbon ================================================ FILE: common/check.h ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #ifndef CARBON_COMMON_CHECK_H_ #define CARBON_COMMON_CHECK_H_ #include "common/check_internal.h" namespace Carbon { // Checks the given condition, and if it's false, prints a stack, streams the // error message, then exits. This should be used for unexpected errors, such as // a bug in the application. // // For example: // CARBON_CHECK(is_valid, "Data is not valid!"); // // The condition must be parenthesized if it contains top-level commas, for // example in a template argument list: // CARBON_CHECK((inst.IsOneOf()), // "Unexpected inst {0}", inst); #define CARBON_CHECK(condition, ...) \ CARBON_INTERNAL_CHECK_CONDITION(condition) \ ? (void)0 : CARBON_INTERNAL_CHECK(condition __VA_OPT__(, ) __VA_ARGS__) // DCHECK calls CHECK in debug mode, and does nothing otherwise. #ifndef NDEBUG #define CARBON_DCHECK(condition, ...) \ CARBON_CHECK(condition __VA_OPT__(, ) __VA_ARGS__) #else // When in a debug build we want to preserve as much as we can of how the // parameters are used, other than making them be trivially in dead code and // eliminated by the optimizer. As a consequence we preserve the condition but // prefix it with a short-circuit operator, and we still emit the (dead) call to // the check implementation. But we use a special implementation that reduces // the compile time cost. #define CARBON_DCHECK(condition, ...) \ (true || CARBON_INTERNAL_CHECK_CONDITION(condition)) \ ? (void)0 \ : CARBON_INTERNAL_DEAD_DCHECK(condition __VA_OPT__(, ) __VA_ARGS__) #endif // This is similar to CHECK, but is unconditional. Writing // `CARBON_FATAL("message")` is clearer than `CARBON_CHECK(false, "message") // because it avoids confusion about control flow. // // For example: // CARBON_FATAL("Unreachable!"); #define CARBON_FATAL(...) CARBON_INTERNAL_FATAL(__VA_ARGS__) } // namespace Carbon #endif // CARBON_COMMON_CHECK_H_ ================================================ FILE: common/check_internal.cpp ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #include "common/check_internal.h" #include #include #include "common/ostream.h" #include "llvm/Support/Signals.h" namespace Carbon::Internal { auto CheckFailImpl(const char* kind, const char* file, int line, const char* condition_str, llvm::StringRef extra_message) -> void { // Render the final check string here. std::string message = llvm::formatv( "{0} failure at {1}:{2}{3}{4}{5}{6}\n", kind, file, line, llvm::StringRef(condition_str).empty() ? "" : ": ", condition_str, extra_message.empty() ? "" : ": ", extra_message); // This macro is defined by `--config=non-fatal-checks`. #ifdef CARBON_NON_FATAL_CHECKS #ifdef NDEBUG #error "--config=non-fatal-checks is incompatible with -c opt" #endif // TODO: It'd be nice to print the LLVM PrettyStackTrace, but LLVM doesn't // expose functionality to do so. llvm::sys::PrintStackTrace(llvm::errs()); llvm::errs() << message; #else // Register another signal handler to print the message. This is because we // want it at the bottom of output, after LLVM's builtin stack output, rather // than the top. llvm::sys::AddSignalHandler( [](void* str) { llvm::errs() << reinterpret_cast(str); }, const_cast(message.c_str())); // It's useful to exit the program with `std::abort()` for integration with // debuggers and other tools. We also assume LLVM's exit handling is // installed, which will stack trace on `std::abort()`. std::abort(); #endif } } // namespace Carbon::Internal ================================================ FILE: common/check_internal.h ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #ifndef CARBON_COMMON_CHECK_INTERNAL_H_ #define CARBON_COMMON_CHECK_INTERNAL_H_ #include "common/template_string.h" #include "llvm/Support/FormatVariadic.h" namespace Carbon::Internal { // Evaluates a condition in a CHECK. This diagnoses if the condition evaluates // to the constant `true` or `false`. [[clang::always_inline]] constexpr bool // Trailing GNU function attributes are incompatible with trailing return types. // Filed as https://github.com/llvm/llvm-project/issues/118697 // NOLINTNEXTLINE(modernize-use-trailing-return-type) CheckCondition(bool condition) __attribute__((diagnose_if(condition, "CHECK condition is always true; replace with " "static_assert if this is intended", "error"))) __attribute__((diagnose_if(!condition, "CHECK condition is always false; replace with " "CARBON_FATAL if this is intended", "error"))) { return condition; } // Implements the check failure message printing. // // This is out-of-line and will arrange to stop the program, print any debugging // information and this string. In `!NDEBUG` mode (`dbg` and `fastbuild`), check // failures can be made non-fatal by a build flag, so this is not `[[noreturn]]` // in that case. // // This API uses `const char*` C string arguments rather than `llvm::StringRef` // because we know that these are available as C strings and passing them that // way lets the code size of calling it be smaller: it only needs to materialize // a single pointer argument for each. The runtime cost of re-computing the size // should be minimal. The extra message however might not be compile-time // guaranteed to be a C string so we use a normal `StringRef` there. #ifdef NDEBUG [[noreturn]] #endif auto CheckFailImpl(const char* kind, const char* file, int line, const char* condition_str, llvm::StringRef extra_message) -> void; // Allow converting format values; the default behaviour is to just pass them // through. template auto ConvertFormatValue(T&& t) -> T&& { return std::forward(t); } // Convert enums to larger integers so that byte-sized enums are not confused // with being chars and printed as invalid (or nul-terminating) characters. // Scoped enums are explicitly converted to integers so they can be printed // without the user writing a cast. template requires(std::is_enum_v>) auto ConvertFormatValue(T&& t) -> auto { if constexpr (std::is_signed_v< std::underlying_type_t>>) { return static_cast(t); } else { return static_cast(t); } } // Prints a check failure, including rendering any user-provided message using // a format string. // // Most of the parameters are passed as compile-time template strings to avoid // runtime cost of parameter setup in optimized builds. Each of these are passed // along to the underlying implementation to include in the final printed // message. // // Any user-provided format string and values are directly passed to // `llvm::formatv` which handles all of the formatting of output. template #ifdef NDEBUG [[noreturn]] #endif [[gnu::cold, clang::noinline]] auto CheckFail(Ts&&... values) -> void { if constexpr (llvm::StringRef(FormatStr).empty()) { // Skip the format string rendering if empty. Note that we don't skip it // even if there are no values as we want to have consistent handling of // `{}`s in the format string. This case is about when there is no message // at all, just the condition. CheckFailImpl(Kind.c_str(), File.c_str(), Line, ConditionStr.c_str(), ""); } else { CheckFailImpl(Kind.c_str(), File.c_str(), Line, ConditionStr.c_str(), llvm::formatv(FormatStr.c_str(), ConvertFormatValue(std::forward(values))...) .str()); } } } // namespace Carbon::Internal // Evaluates the condition of a CHECK as a boolean value. // // This performs a contextual conversion to bool, diagnoses if the condition is // always true or always false, and returns its value. #define CARBON_INTERNAL_CHECK_CONDITION(cond) \ (Carbon::Internal::CheckCondition(true && (cond))) // Implements check messages without any formatted values. // // Passes each of the provided components of the message to the template // parameters of the check failure printing function above, including an empty // string for the format string. Because there are multiple template arguments, // the entire call is wrapped in parentheses. #define CARBON_INTERNAL_CHECK_IMPL(kind, file, line, condition_str) \ (Carbon::Internal::CheckFail()) // Implements check messages with a format string and potentially formatted // values. // // Each of the main components is passed as a template arguments, and then any // formatted values are passed as arguments. Because there are multiple template // arguments, the entire call is wrapped in parentheses. #define CARBON_INTERNAL_CHECK_IMPL_FORMAT(kind, file, line, condition_str, \ format_str, ...) \ (Carbon::Internal::CheckFail( \ __VA_ARGS__)) // Implements the failure of a check. // // Collects all the metadata about the failure to be printed, such as source // location and stringified condition, and passes those, any format string and // formatted arguments to the correct implementation macro above. #define CARBON_INTERNAL_CHECK(condition, ...) \ CARBON_INTERNAL_CHECK_IMPL##__VA_OPT__(_FORMAT)( \ "CHECK", __FILE__, __LINE__, #condition __VA_OPT__(, ) __VA_ARGS__) // Implements the fatal macro. // // Similar to the check failure macro, but tags the message as a fatal one and // leaves the stringified condition empty. #define CARBON_INTERNAL_FATAL(...) \ (CARBON_INTERNAL_CHECK_IMPL##__VA_OPT__(_FORMAT)( \ "FATAL", __FILE__, __LINE__, "" __VA_OPT__(, ) __VA_ARGS__), \ CARBON_INTERNAL_FATAL_NORETURN_SUFFIX()) #ifdef NDEBUG // For `DCHECK` in optimized builds we have a dead check that we want to // potentially "use" arguments, but otherwise have the minimal overhead. We // avoid forming interesting format strings here so that we don't have to // repeatedly instantiate the `Check` function above. This format string would // be an error if actually used. #define CARBON_INTERNAL_DEAD_DCHECK(condition, ...) \ CARBON_INTERNAL_DEAD_DCHECK_IMPL##__VA_OPT__(_FORMAT)(__VA_ARGS__) #define CARBON_INTERNAL_DEAD_DCHECK_IMPL() \ Carbon::Internal::CheckFail<"", "", 0, "", "">() #define CARBON_INTERNAL_DEAD_DCHECK_IMPL_FORMAT(format_str, ...) \ Carbon::Internal::CheckFail<"", "", 0, "", "">(__VA_ARGS__) // The CheckFail function itself is noreturn in NDEBUG. #define CARBON_INTERNAL_FATAL_NORETURN_SUFFIX() void() #else #define CARBON_INTERNAL_FATAL_NORETURN_SUFFIX() std::abort() #endif #endif // CARBON_COMMON_CHECK_INTERNAL_H_ ================================================ FILE: common/check_test.cpp ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #include "common/check.h" #include #include namespace Carbon { namespace { // Non-constexpr functions that always return true and false, to bypass constant // condition checking. auto AlwaysTrue() -> bool { return true; } auto AlwaysFalse() -> bool { return false; } TEST(CheckTest, CheckTrue) { CARBON_CHECK(AlwaysTrue()); } TEST(CheckTest, CheckFalse) { ASSERT_DEATH( { CARBON_CHECK(AlwaysFalse()); }, R"( CHECK failure at common/check_test.cpp:\d+: AlwaysFalse\(\) )"); } TEST(CheckTest, CheckFalseHasStackDump) { ASSERT_DEATH({ CARBON_CHECK(AlwaysFalse()); }, "\nStack dump:\n"); } TEST(CheckTest, CheckTrueCallbackNotUsed) { bool called = false; auto callback = [&]() { called = true; return "called"; }; CARBON_CHECK(AlwaysTrue(), "{0}", callback()); EXPECT_FALSE(called); } TEST(CheckTest, CheckFalseMessage) { ASSERT_DEATH( { CARBON_CHECK(AlwaysFalse(), "msg"); }, R"( CHECK failure at common/check_test.cpp:.+: AlwaysFalse\(\): msg )"); } TEST(CheckTest, CheckFalseFormattedMessage) { const char msg[] = "msg"; std::string str = "str"; int i = 1; ASSERT_DEATH( { CARBON_CHECK(AlwaysFalse(), "{0} {1} {2} {3}", msg, str, i, 0); }, R"( CHECK failure at common/check_test.cpp:.+: AlwaysFalse\(\): msg str 1 0 )"); } TEST(CheckTest, CheckOutputForms) { const char msg[] = "msg"; std::string str = "str"; int i = 1; CARBON_CHECK(AlwaysTrue(), "{0} {1} {2} {3}", msg, str, i, 0); } TEST(CheckTest, Fatal) { ASSERT_DEATH( { CARBON_FATAL("msg"); }, "\nFATAL failure at common/check_test.cpp:.+: msg\n"); } TEST(CheckTest, FatalHasStackDump) { ASSERT_DEATH({ CARBON_FATAL("msg"); }, "\nStack dump:\n"); } auto FatalNoReturnRequired() -> int { CARBON_FATAL("msg"); } TEST(ErrorTest, FatalNoReturnRequired) { ASSERT_DEATH( { FatalNoReturnRequired(); }, "\nFATAL failure at common/check_test.cpp:.+: msg\n"); } // Detects whether `CARBON_CHECK(F())` compiles. template concept CheckCompilesWithCondition = requires { CARBON_CHECK(F()); }; TEST(CheckTest, CheckConstantCondition) { EXPECT_TRUE(CheckCompilesWithCondition<[] { return AlwaysTrue(); }>); EXPECT_TRUE(CheckCompilesWithCondition<[] { return AlwaysFalse(); }>); EXPECT_FALSE(CheckCompilesWithCondition<[] { return true; }>); EXPECT_FALSE(CheckCompilesWithCondition<[] { return false; }>); } } // namespace } // namespace Carbon ================================================ FILE: common/command_line.cpp ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #include "common/command_line.h" #include #include #include #include #include #include "common/raw_string_ostream.h" #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/PointerIntPair.h" #include "llvm/Support/FormatVariadic.h" // Recursion is used for subcommands. This should be okay since recursion is // limited by command line architecture. // NOLINTBEGIN(misc-no-recursion) namespace Carbon::CommandLine { auto operator<<(llvm::raw_ostream& output, ParseResult result) -> llvm::raw_ostream& { switch (result) { case ParseResult::MetaSuccess: return output << "MetaSuccess"; case ParseResult::Success: return output << "Success"; } CARBON_FATAL("Corrupt parse result!"); } auto operator<<(llvm::raw_ostream& output, ArgKind kind) -> llvm::raw_ostream& { switch (kind) { case ArgKind::Flag: return output << "Boolean"; case ArgKind::Integer: return output << "Integer"; case ArgKind::String: return output << "String"; case ArgKind::OneOf: return output << "OneOf"; case ArgKind::MetaActionOnly: return output << "MetaActionOnly"; case ArgKind::Invalid: return output << "Invalid"; } CARBON_FATAL("Corrupt argument kind!"); } auto operator<<(llvm::raw_ostream& output, CommandKind kind) -> llvm::raw_ostream& { switch (kind) { case CommandKind::Invalid: return output << "Invalid"; case CommandKind::RequiresSubcommand: return output << "RequiresSubcommand"; case CommandKind::Action: return output << "Action"; case CommandKind::MetaAction: return output << "MetaAction"; } CARBON_FATAL("Corrupt command kind!"); } template static auto PrintListOfAlternatives(llvm::raw_ostream& output, llvm::ArrayRef alternatives, ToPrintable to_printable) -> void { for (const auto& alternative : alternatives.drop_back()) { output << "`" << to_printable(alternative) << (alternatives.size() > 2 ? "`, " : "` "); } if (alternatives.size() > 1) { output << "or "; } output << "`" << to_printable(alternatives.back()) << "`"; } Arg::Arg(ArgInfo info) : info(info) {} Arg::~Arg() { switch (kind) { case Kind::Flag: case Kind::Integer: case Kind::String: case Kind::MetaActionOnly: case Kind::Invalid: // Nothing to do! break; case Kind::OneOf: value_strings.~decltype(value_strings)(); value_action.~ValueActionT(); if (has_default) { default_action.~DefaultActionT(); } break; } } Command::Command(CommandInfo info, Command* parent) : info(info), parent(parent) {} class MetaPrinter { public: // `out` must not be null. explicit MetaPrinter(llvm::raw_ostream* out) : out_(out) {} // Registers this meta printer with a command through the provided builder. // // This adds meta subcommands or options to print both help and version // information for the command. auto RegisterWithCommand(const Command& command, CommandBuilder& builder) -> void; auto PrintHelp(const Command& command) const -> void; auto PrintHelpForSubcommandName(const Command& command, llvm::StringRef subcommand_name) const -> void; auto PrintVersion(const Command& command) const -> void; auto PrintSubcommands(const Command& command) const -> void; private: // The indent is calibrated to allow a short and long option after a two // character indent on the prior line to be visually recognized as separate // from the hanging indent. // // Visual guide: | -x, --extract // | Hanging indented. static constexpr llvm::StringRef BlockIndent = " "; // Width limit for parent command options in usage rendering. static constexpr int MaxParentOptionUsageWidth = 8; // Width limit for the leaf command options in usage rendering. static constexpr int MaxLeafOptionUsageWidth = 16; static constexpr CommandInfo HelpCommandInfo = { .name = "help", .help = R"""( Prints help information for the command, including a description, command line usage, and details of each subcommand and option that can be provided. )""", .help_short = R"""( Prints help information. )""", }; static constexpr ArgInfo HelpArgInfo = { .name = "help", .value_name = "(full|short)", .help = R"""( Prints help information for the command, including a description, command line usage, and details of each option that can be provided. )""", .help_short = HelpCommandInfo.help_short, }; // Provide a customized description for help on a subcommand to avoid // confusion with the top-level help. static constexpr CommandInfo SubHelpCommandInfo = { .name = "help", .help = R"""( Prints help information for the subcommand, including a description, command line usage, and details of each further subcommand and option that can be provided. )""", .help_short = R"""( Prints subcommand help information. )""", }; static constexpr ArgInfo SubHelpArgInfo = { .name = "help", .value_name = "(full|short)", .help = R"""( Prints help information for the subcommand, including a description, command line usage, and details of each option that can be provided. )""", .help_short = SubHelpCommandInfo.help_short, }; static constexpr ArgInfo HelpSubcommandArgInfo = { .name = "subcommand", .help = R"""( Which subcommand to print help information for. )""", }; static constexpr CommandInfo VersionCommandInfo = { .name = "version", .help = R"""( Prints the version of this command. )""", }; static constexpr ArgInfo VersionArgInfo = { .name = "version", .help = VersionCommandInfo.help, }; // A general helper for rendering a text block. auto PrintTextBlock(llvm::StringRef indent, llvm::StringRef text) const -> void; // Helpers for version and build information printing. auto PrintRawVersion(const Command& command, llvm::StringRef indent) const -> void; auto PrintRawBuildInfo(const Command& command, llvm::StringRef indent) const -> void; // Helpers for printing components of help and usage output for arguments, // including options and positional arguments. auto PrintArgValueUsage(const Arg& arg) const -> void; auto PrintOptionUsage(const Arg& option) const -> void; auto PrintOptionShortName(const Arg& arg) const -> void; auto PrintArgShortValues(const Arg& arg) const -> void; auto PrintArgLongValues(const Arg& arg, llvm::StringRef indent) const -> void; auto PrintArgHelp(const Arg& arg, llvm::StringRef indent) const -> void; // Helpers for printing command usage summaries. auto PrintRawUsageCommandAndOptions( const Command& command, int max_option_width = MaxLeafOptionUsageWidth) const -> void; auto PrintRawUsage(const Command& command, llvm::StringRef indent) const -> void; auto PrintUsage(const Command& command) const -> void; // Helpers to print various sections of `PrintHelp` that only occur within // that output. auto PrintHelpSubcommands(const Command& command) const -> void; auto PrintHelpPositionalArgs(const Command& command) const -> void; auto PrintHelpOptions(const Command& command) const -> void; llvm::raw_ostream* out_; // A flag that may be configured during command line parsing to select between // long and short form help output. bool short_help_ = false; // The requested subcommand to print help information for. llvm::StringRef help_subcommand_; }; auto MetaPrinter::RegisterWithCommand(const Command& command, CommandBuilder& builder) -> void { bool is_subcommand = command.parent; bool has_subcommands = !command.subcommands.empty(); // If this command has subcommands, we prefer that model for access meta // actions, but still silently support using the flags. But we never want to // *add* subcommands if they aren't already being used. if (has_subcommands) { builder.AddSubcommand( is_subcommand ? SubHelpCommandInfo : HelpCommandInfo, [&](CommandBuilder& sub_b) { sub_b.AddStringPositionalArg(HelpSubcommandArgInfo, [&](auto& arg_b) { arg_b.Set(&help_subcommand_); }); sub_b.Meta([this, &command]() { if (help_subcommand_.empty()) { PrintHelp(command); } else { PrintHelpForSubcommandName(command, help_subcommand_); } }); }); // Only add version printing support if there is a version string // configured for this command. if (!command.info.version.empty()) { builder.AddSubcommand(VersionCommandInfo, [&](CommandBuilder& sub_b) { sub_b.Meta([this, &command]() { PrintVersion(command); }); }); } } builder.AddOneOfOption( is_subcommand ? SubHelpArgInfo : HelpArgInfo, [&](auto& arg_b) { arg_b.HelpHidden(has_subcommands); arg_b.SetOneOf( { arg_b.OneOfValue("full", false).Default(true), arg_b.OneOfValue("short", true), }, &short_help_); arg_b.MetaAction([this, &command]() { PrintHelp(command); }); }); // Only add version printing support if there is a version string configured // for this command. if (!command.info.version.empty()) { builder.AddMetaActionOption(VersionArgInfo, [&](auto& arg_b) { arg_b.HelpHidden(has_subcommands); arg_b.MetaAction([this, &command]() { PrintVersion(command); }); }); } } auto MetaPrinter::PrintHelp(const Command& command) const -> void { // TODO: begin using the short setting to customize the output. (void)short_help_; const CommandInfo& info = command.info; if (!info.version.empty()) { // We use the version string as a header for the command help when present. PrintRawVersion(command, /*indent=*/""); *out_ << "\n"; } if (!command.info.help.empty()) { PrintTextBlock("", info.help); *out_ << "\n"; } if (!info.build_info.empty()) { *out_ << "Build info:\n"; PrintRawBuildInfo(command, /*indent=*/" "); *out_ << "\n"; } PrintUsage(command); PrintHelpSubcommands(command); PrintHelpPositionalArgs(command); PrintHelpOptions(command); if (!info.help_epilogue.empty()) { *out_ << "\n"; PrintTextBlock("", info.help_epilogue); } // End with a blank line for the long help to make it easier to separate from // anything that follows in the shell. *out_ << "\n"; } auto MetaPrinter::PrintHelpForSubcommandName( const Command& command, llvm::StringRef subcommand_name) const -> void { for (const auto& subcommand : command.subcommands) { if (subcommand->info.name == subcommand_name) { PrintHelp(*subcommand); return; } } // TODO: This should really be connected up so that parsing can return an // Error instead of ParseResult::MetaSuccess in this case. *out_ << "error: could not find a subcommand named '" << subcommand_name << "'\n"; } auto MetaPrinter::PrintVersion(const Command& command) const -> void { CARBON_CHECK( !command.info.version.empty(), "Printing should not be enabled without a version string configured."); PrintRawVersion(command, /*indent=*/""); if (!command.info.build_info.empty()) { *out_ << "\n"; // If there is build info to print, we also render that without any indent. PrintRawBuildInfo(command, /*indent=*/""); } } auto MetaPrinter::PrintSubcommands(const Command& command) const -> void { PrintListOfAlternatives>( *out_, command.subcommands, [](const std::unique_ptr& subcommand) { return subcommand->info.name; }); } auto MetaPrinter::PrintRawVersion(const Command& command, llvm::StringRef indent) const -> void { // Newlines are trimmed from the version string an a closing newline added but // no other formatting is performed. *out_ << indent << command.info.version.trim('\n') << "\n"; } auto MetaPrinter::PrintRawBuildInfo(const Command& command, llvm::StringRef indent) const -> void { // Print the build info line-by-line without any wrapping in case it // contains line-oriented formatted text, but drop leading and trailing blank // lines. llvm::SmallVector lines; command.info.build_info.trim('\n').split(lines, "\n"); for (auto line : lines) { *out_ << indent << line << "\n"; } } auto MetaPrinter::PrintTextBlock(llvm::StringRef indent, llvm::StringRef text) const -> void { // Strip leading and trailing newlines to make it easy to use multiline raw // string literals that will naturally have those. text = text.trim('\n'); // For empty text, print nothing at all. The caller formatting will work to // handle this gracefully. if (text.empty()) { return; } // Remove line breaks from the text that would typically be removed when // rendering it as Markdown. The goal is to preserve: // // - Blank lines as paragraph separators. // - Line breaks after list items or other structural components in Markdown. // - Fenced regions exactly as they appear. // // And within paragraphs (including those nested in lists), reflow the // paragraph intelligently to the column width. There are TODOs below about // both lists and reflowing. llvm::SmallVector input_lines; text.split(input_lines, "\n"); for (int i = 0, size = input_lines.size(); i < size;) { if (input_lines[i].empty()) { // Blank lines are preserved. *out_ << "\n"; ++i; continue; } if (input_lines[i].starts_with("```")) { // Fenced regions are preserved verbatim. llvm::StringRef fence = input_lines[i].slice(0, input_lines[i].find_first_not_of("`")); do { *out_ << indent << input_lines[i] << "\n"; ++i; } while (i < size && !input_lines[i].starts_with(fence)); if (i >= size) { // Don't error on malformed text blocks, just print what we've got. break; } // Including the close of the fence. *out_ << indent << input_lines[i] << "\n"; ++i; continue; } if (input_lines[i].starts_with(" ")) { // Indented code blocks ar preserved verbatim, but we don't support tabs // in the indent for simplicity. do { *out_ << indent << input_lines[i] << "\n"; ++i; } while (i < size && input_lines[i].starts_with(" ")); continue; } // TODO: Detect other Markdown structures, especially lists and tables. // Otherwise, collect all of the lines until the end or the next blank line // as a block of text. // // TODO: This is where we should re-flow. llvm::StringRef space = indent; do { *out_ << space << input_lines[i].trim(); space = " "; ++i; } while (i < size && !input_lines[i].empty()); *out_ << "\n"; } } auto MetaPrinter::PrintArgValueUsage(const Arg& arg) const -> void { if (!arg.info.value_name.empty()) { *out_ << arg.info.value_name; return; } if (arg.kind == Arg::Kind::OneOf) { *out_ << "("; llvm::ListSeparator sep("|"); for (llvm::StringRef value_string : arg.value_strings) { *out_ << sep << value_string; } *out_ << ")"; return; } *out_ << "..."; } auto MetaPrinter::PrintOptionUsage(const Arg& option) const -> void { if (option.kind == Arg::Kind::Flag) { *out_ << "--" << (option.default_flag ? "no-" : "") << option.info.name; return; } *out_ << "--" << option.info.name; if (option.kind != Arg::Kind::MetaActionOnly) { *out_ << (option.has_default ? "[" : "") << "="; PrintArgValueUsage(option); if (option.has_default) { *out_ << "]"; } } } auto MetaPrinter::PrintOptionShortName(const Arg& arg) const -> void { CARBON_CHECK(!arg.info.short_name.empty(), "No short name to use."); *out_ << "-" << arg.info.short_name; } auto MetaPrinter::PrintArgShortValues(const Arg& arg) const -> void { CARBON_CHECK( arg.kind == Arg::Kind::OneOf, "Only one-of arguments have interesting value snippets to print."); llvm::ListSeparator sep; for (llvm::StringRef value_string : arg.value_strings) { *out_ << sep << value_string; } } auto MetaPrinter::PrintArgLongValues(const Arg& arg, llvm::StringRef indent) const -> void { *out_ << indent << "Possible values:\n"; // TODO: It would be good to add help text for each value and then print it // here. for (auto [i, value_string] : llvm::enumerate(arg.value_strings)) { *out_ << indent << "- " << value_string; if (arg.has_default && static_cast(i) == arg.default_value_index) { *out_ << " (default)"; } *out_ << "\n"; } } auto MetaPrinter::PrintArgHelp(const Arg& arg, llvm::StringRef indent) const -> void { // Print out the main help text. PrintTextBlock(indent, arg.info.help); // Then print out any help based on the values. switch (arg.kind) { case Arg::Kind::Integer: if (arg.has_default) { *out_ << "\n"; *out_ << indent << "Default value: " << arg.default_integer << "\n"; } break; case Arg::Kind::String: if (arg.has_default) { *out_ << "\n"; *out_ << indent << "Default value: " << arg.default_string << "\n"; } break; case Arg::Kind::OneOf: *out_ << "\n"; PrintArgLongValues(arg, indent); break; case Arg::Kind::Flag: case Arg::Kind::MetaActionOnly: // No value help. break; case Arg::Kind::Invalid: CARBON_FATAL("Argument configured without any action or kind!"); } } auto MetaPrinter::PrintRawUsageCommandAndOptions(const Command& command, int max_option_width) const -> void { // Recursively print parent usage first with a compressed width. if (command.parent) { PrintRawUsageCommandAndOptions(*command.parent, MaxParentOptionUsageWidth); *out_ << " "; } *out_ << command.info.name; // Buffer the options rendering so we can limit its length. RawStringOstream buffer_out; MetaPrinter buffer_printer(&buffer_out); bool have_short_flags = false; for (const auto& arg : command.options) { if (static_cast(buffer_out.size()) > max_option_width) { break; } // We can summarize positive boolean flags with a short name using a // sequence of short names in a single rendered argument. if (arg->kind == Arg::Kind::Flag && !arg->default_flag && !arg->info.short_name.empty()) { if (!have_short_flags) { have_short_flags = true; buffer_out << "-"; } buffer_out << arg->info.short_name; } } llvm::StringRef space = have_short_flags ? " " : ""; for (const auto& option : command.options) { if (static_cast(buffer_out.size()) > max_option_width) { break; } if (option->is_help_hidden || option->meta_action) { // Skip hidden and options with meta actions attached. continue; } if (option->kind == Arg::Kind::Flag && !option->default_flag && !option->info.short_name.empty()) { // Handled with short names above. continue; } buffer_out << space; buffer_printer.PrintOptionUsage(*option); space = " "; } if (!buffer_out.empty()) { if (static_cast(buffer_out.size()) <= max_option_width) { *out_ << " [" << buffer_out.TakeStr() << "]"; } else { buffer_out.clear(); *out_ << " [OPTIONS]"; } } } auto MetaPrinter::PrintRawUsage(const Command& command, llvm::StringRef indent) const -> void { if (!command.info.usage.empty()) { PrintTextBlock(indent, command.info.usage); return; } if (command.kind != Command::Kind::RequiresSubcommand) { // We're a valid leaf command, so synthesize a full usage line. *out_ << indent; PrintRawUsageCommandAndOptions(command); if (!command.positional_args.empty()) { bool open_optional = false; for (auto [i, arg] : llvm::enumerate(command.positional_args)) { *out_ << " "; if (i != 0 && command.positional_args[i - 1]->is_append) { *out_ << "-- "; } if (!arg->is_required && !open_optional) { *out_ << "["; open_optional = true; } *out_ << "<" << arg->info.name << ">"; if (arg->is_append) { *out_ << "..."; } } if (open_optional) { *out_ << "]"; } } *out_ << "\n"; } // If we have subcommands, also recurse into them so each one can print their // usage lines. for (const auto& subcommand : command.subcommands) { if (subcommand->is_help_hidden || subcommand->kind == Command::Kind::MetaAction) { continue; } PrintRawUsage(*subcommand, indent); } } auto MetaPrinter::PrintUsage(const Command& command) const -> void { if (!command.parent) { *out_ << "Usage:\n"; } else { *out_ << "Subcommand `" << command.info.name << "` usage:\n"; } PrintRawUsage(command, " "); } auto MetaPrinter::PrintHelpSubcommands(const Command& command) const -> void { bool first_subcommand = true; for (const auto& subcommand : command.subcommands) { if (subcommand->is_help_hidden) { continue; } if (first_subcommand) { first_subcommand = false; if (!command.parent) { *out_ << "\nSubcommands:"; } else { *out_ << "\nSubcommand `" << command.info.name << "` subcommands:"; } } *out_ << "\n"; *out_ << " " << subcommand->info.name << "\n"; PrintTextBlock(BlockIndent, subcommand->info.help); } } auto MetaPrinter::PrintHelpPositionalArgs(const Command& command) const -> void { bool first_positional_arg = true; for (const auto& positional_arg : command.positional_args) { if (positional_arg->is_help_hidden) { continue; } if (first_positional_arg) { first_positional_arg = false; if (!command.parent) { *out_ << "\nPositional arguments:"; } else { *out_ << "\nSubcommand `" << command.info.name << "` positional arguments:"; } } *out_ << "\n"; *out_ << " " << positional_arg->info.name << "\n"; PrintArgHelp(*positional_arg, BlockIndent); } } auto MetaPrinter::PrintHelpOptions(const Command& command) const -> void { bool first_option = true; for (const auto& option : command.options) { if (option->is_help_hidden) { continue; } if (first_option) { first_option = false; if (!command.parent && command.subcommands.empty()) { // Only one command level. *out_ << "\nOptions:"; } else if (!command.parent) { *out_ << "\nCommand options:"; } else { *out_ << "\nSubcommand `" << command.info.name << "` options:"; } } *out_ << "\n"; *out_ << " "; if (!option->info.short_name.empty()) { PrintOptionShortName(*option); *out_ << ", "; } else { *out_ << " "; } PrintOptionUsage(*option); *out_ << "\n"; PrintArgHelp(*option, BlockIndent); } } class Parser { public: // `out` must not be null. explicit Parser(llvm::raw_ostream* out, CommandInfo command_info, llvm::function_refvoid> build); auto Parse(llvm::ArrayRef unparsed_args) -> ErrorOr; private: friend CommandBuilder; // For the option and subcommand maps, we use somewhat large small size // buffers (16) as there is no real size pressure on these and its nice to // avoid heap allocation in the small cases. using OptionMapT = llvm::SmallDenseMap, 16>; using SubcommandMapT = llvm::SmallDenseMap; // This table is sized to be 128 so that it can hold ASCII characters. We // don't need any more than this and using a direct table indexed by the // character's numeric value makes for a convenient map. using ShortOptionTableT = std::array; auto PopulateMaps(const Command& command) -> void; auto SetOptionDefault(const Arg& option) -> void; auto ParseNegatedFlag(const Arg& flag, std::optional value) -> ErrorOr; auto ParseFlag(const Arg& flag, std::optional value) -> ErrorOr; auto ParseIntegerArgValue(const Arg& arg, llvm::StringRef value) -> ErrorOr; auto ParseStringArgValue(const Arg& arg, llvm::StringRef value) -> ErrorOr; auto ParseOneOfArgValue(const Arg& arg, llvm::StringRef value) -> ErrorOr; auto ParseArg(const Arg& arg, bool short_spelling, std::optional value, bool negated_name = false) -> ErrorOr; auto SplitValue(llvm::StringRef& unparsed_arg) -> std::optional; auto ParseLongOption(llvm::StringRef unparsed_arg) -> ErrorOr; auto ParseShortOptionSeq(llvm::StringRef unparsed_arg) -> ErrorOr; auto FinalizeParsedOptions() -> ErrorOr; auto ParsePositionalArg(llvm::StringRef unparsed_arg) -> ErrorOr; auto ParseSubcommand(llvm::StringRef unparsed_arg) -> ErrorOr; auto ParsePositionalSuffix(llvm::ArrayRef unparsed_args) -> ErrorOr; auto FinalizeParse() -> ErrorOr; // When building a command, it registers arguments and potentially subcommands // that are meta actions to print things to standard out, so we build a meta // printer for that here. MetaPrinter meta_printer_; Command root_command_; const Command* command_; OptionMapT option_map_; ShortOptionTableT short_option_table_; SubcommandMapT subcommand_map_; int positional_arg_index_ = 0; bool appending_to_positional_arg_ = false; ActionT arg_meta_action_; }; auto Parser::PopulateMaps(const Command& command) -> void { option_map_.clear(); for (const auto& option : command.options) { option_map_.insert({option->info.name, {option.get(), false}}); } short_option_table_.fill(nullptr); for (auto& map_entry : option_map_) { const Arg* option = map_entry.second.getPointer(); if (option->info.short_name.empty()) { continue; } CARBON_CHECK(option->info.short_name.size() == 1, "Short option names must have exactly one character."); unsigned char short_char = option->info.short_name[0]; CARBON_CHECK(short_char < short_option_table_.size(), "Short option name outside of the expected range."); short_option_table_[short_char] = &map_entry.second; } subcommand_map_.clear(); for (const auto& subcommand : command.subcommands) { subcommand_map_.insert({subcommand->info.name, subcommand.get()}); } } auto Parser::SetOptionDefault(const Arg& option) -> void { CARBON_CHECK(option.has_default, "No default value available!"); switch (option.kind) { case Arg::Kind::Flag: *option.flag_storage = option.default_flag; break; case Arg::Kind::Integer: *option.integer_storage = option.default_integer; break; case Arg::Kind::String: *option.string_storage = option.default_string; break; case Arg::Kind::OneOf: option.default_action(option); break; case Arg::Kind::MetaActionOnly: CARBON_FATAL("Can't set a default value for a meta action!"); case Arg::Kind::Invalid: CARBON_FATAL("Option configured without any action or kind!"); } } auto Parser::ParseNegatedFlag(const Arg& flag, std::optional value) -> ErrorOr { if (flag.kind != Arg::Kind::Flag) { return Error( "cannot use a negated flag name by prefixing it with `no-` when it " "isn't a boolean flag argument"); } if (value) { return Error( "cannot specify a value when using a flag name prefixed with `no-` -- " "that prefix implies a value of `false`"); } *flag.flag_storage = false; return Success(); } auto Parser::ParseFlag(const Arg& flag, std::optional value) -> ErrorOr { CARBON_CHECK(flag.kind == Arg::Kind::Flag, "Incorrect kind: {0}", flag.kind); if (!value || *value == "true") { *flag.flag_storage = true; } else if (*value == "false") { *flag.flag_storage = false; } else { return Error(llvm::formatv( "invalid value specified for the boolean flag `--{0}`: {1}", flag.info.name, *value)); } return Success(); } auto Parser::ParseIntegerArgValue(const Arg& arg, llvm::StringRef value) -> ErrorOr { CARBON_CHECK(arg.kind == Arg::Kind::Integer, "Incorrect kind: {0}", arg.kind); int integer_value; // Note that this method returns *true* on error! if (value.getAsInteger(/*Radix=*/0, integer_value)) { return Error(llvm::formatv( "cannot parse value for option `--{0}` as an integer: {1}", arg.info.name, value)); } if (!arg.is_append) { *arg.integer_storage = integer_value; } else { arg.integer_sequence->push_back(integer_value); } return Success(); } auto Parser::ParseStringArgValue(const Arg& arg, llvm::StringRef value) -> ErrorOr { CARBON_CHECK(arg.kind == Arg::Kind::String, "Incorrect kind: {0}", arg.kind); if (!arg.is_append) { *arg.string_storage = value; } else { arg.string_sequence->push_back(value); } return Success(); } auto Parser::ParseOneOfArgValue(const Arg& arg, llvm::StringRef value) -> ErrorOr { CARBON_CHECK(arg.kind == Arg::Kind::OneOf, "Incorrect kind: {0}", arg.kind); if (!arg.value_action(arg, value)) { RawStringOstream error; error << "option `--" << arg.info.name << "="; llvm::printEscapedString(value, error); error << "` has an invalid value `"; llvm::printEscapedString(value, error); error << "`; valid values are: "; PrintListOfAlternatives( error, arg.value_strings, [](llvm::StringRef x) { return x; }); return Error(error.TakeStr()); } return Success(); } auto Parser::ParseArg(const Arg& arg, bool short_spelling, std::optional value, bool negated_name) -> ErrorOr { // If this argument has a meta action, replace the current meta action with // it. if (arg.meta_action) { arg_meta_action_ = arg.meta_action; } // Boolean flags have special parsing logic. if (negated_name) { return ParseNegatedFlag(arg, value); } if (arg.kind == Arg::Kind::Flag) { return ParseFlag(arg, value); } std::string name; if (short_spelling) { name = llvm::formatv("`-{0}` (short for `--{1}`)", arg.info.short_name, arg.info.name); } else { name = llvm::formatv("`--{0}`", arg.info.name); } if (!value) { // We can't have a positional argument without a value, so we know this is // an option and handle it as such. if (arg.kind == Arg::Kind::MetaActionOnly) { // Nothing further to do here, this is only a meta-action. return Success(); } if (!arg.has_default) { return Error(llvm::formatv( "option {0} requires a value to be provided and none was", name)); } SetOptionDefault(arg); return Success(); } // There is a value to parse as part of the argument. switch (arg.kind) { case Arg::Kind::Integer: return ParseIntegerArgValue(arg, *value); case Arg::Kind::String: return ParseStringArgValue(arg, *value); case Arg::Kind::OneOf: return ParseOneOfArgValue(arg, *value); case Arg::Kind::MetaActionOnly: // TODO: Improve message. return Error(llvm::formatv( "option {0} cannot be used with a value, and '{1}' was provided", name, value)); case Arg::Kind::Flag: case Arg::Kind::Invalid: CARBON_FATAL("Invalid kind!"); } } auto Parser::SplitValue(llvm::StringRef& unparsed_arg) -> std::optional { // Split out a value if present. std::optional value; auto index = unparsed_arg.find('='); if (index != llvm::StringRef::npos) { value = unparsed_arg.substr(index + 1); unparsed_arg = unparsed_arg.substr(0, index); } return value; } auto Parser::ParseLongOption(llvm::StringRef unparsed_arg) -> ErrorOr { CARBON_CHECK(unparsed_arg.starts_with("--") && unparsed_arg.size() > 2, "Must only be called on a potential long option."); // Walk past the double dash. unparsed_arg = unparsed_arg.drop_front(2); bool negated_name = unparsed_arg.consume_front("no-"); std::optional value = SplitValue(unparsed_arg); auto option_it = option_map_.find(unparsed_arg); if (option_it == option_map_.end()) { // TODO: Improve error. return Error(llvm::formatv("unknown option `--{0}{1}`", negated_name ? "no-" : "", unparsed_arg)); } // Mark this option as parsed. option_it->second.setInt(true); // Parse this specific option and any value. const Arg& option = *option_it->second.getPointer(); return ParseArg(option, /*short_spelling=*/false, value, negated_name); } auto Parser::ParseShortOptionSeq(llvm::StringRef unparsed_arg) -> ErrorOr { CARBON_CHECK(unparsed_arg.starts_with("-") && unparsed_arg.size() > 1, "Must only be called on a potential short option sequence."); unparsed_arg = unparsed_arg.drop_front(); std::optional value = SplitValue(unparsed_arg); if (value && unparsed_arg.size() != 1) { return Error(llvm::formatv( "cannot provide a value to the group of multiple short options " "`-{0}=...`; values must be provided to a single option, using " "either the short or long spelling", unparsed_arg)); } for (unsigned char c : unparsed_arg) { auto* arg_entry = (c < short_option_table_.size()) ? short_option_table_[c] : nullptr; if (!arg_entry) { return Error( llvm::formatv("unknown short option `-{0}`", static_cast(c))); } // Mark this argument as parsed. arg_entry->setInt(true); // Parse the argument, including the value if this is the last. const Arg& arg = *arg_entry->getPointer(); CARBON_RETURN_IF_ERROR(ParseArg(arg, /*short_spelling=*/true, value)); } return Success(); } auto Parser::FinalizeParsedOptions() -> ErrorOr { llvm::SmallVector missing_options; for (const auto& option_entry : option_map_) { const Arg* option = option_entry.second.getPointer(); if (!option_entry.second.getInt()) { // If the argument has a default value and isn't a meta-action, we need to // act on that when it isn't passed. if (option->has_default && !option->meta_action) { SetOptionDefault(*option); } // Remember any missing required arguments, we'll diagnose those. if (option->is_required) { missing_options.push_back(option); } } } if (missing_options.empty()) { return Success(); } // Sort the missing arguments by name to provide a stable and deterministic // error message. We know there can't be duplicate names because these came // from a may keyed on the name, so this provides a total ordering. llvm::sort(missing_options, [](const Arg* lhs, const Arg* rhs) { return lhs->info.name < rhs->info.name; }); RawStringOstream error; error << "required options not provided: "; llvm::ListSeparator sep; for (const Arg* option : missing_options) { error << sep << "--" << option->info.name; } return Error(error.TakeStr()); } auto Parser::ParsePositionalArg(llvm::StringRef unparsed_arg) -> ErrorOr { if (static_cast(positional_arg_index_) >= command_->positional_args.size()) { return Error(llvm::formatv( "completed parsing all {0} configured positional arguments, and found " "an additional positional argument: `{1}`", command_->positional_args.size(), unparsed_arg)); } const Arg& arg = *command_->positional_args[positional_arg_index_]; // Mark that we'll keep appending here until a `--` marker. When already // appending this is redundant but harmless. appending_to_positional_arg_ = arg.is_append; if (!appending_to_positional_arg_) { // If we're not continuing to append to a current positional arg, // increment the positional arg index to find the next argument we // should use here. ++positional_arg_index_; } return ParseArg(arg, /*short_spelling=*/false, unparsed_arg); } auto Parser::ParseSubcommand(llvm::StringRef unparsed_arg) -> ErrorOr { auto subcommand_it = subcommand_map_.find(unparsed_arg); if (subcommand_it == subcommand_map_.end()) { RawStringOstream error; error << "invalid subcommand `" << unparsed_arg << "`; available subcommands: "; MetaPrinter(&error).PrintSubcommands(*command_); return Error(error.TakeStr()); } // Before we recurse into the subcommand, verify that all the required // arguments for this command were in fact parsed. CARBON_RETURN_IF_ERROR(FinalizeParsedOptions()); // Recurse into the subcommand, tracking the active command. command_ = subcommand_it->second; PopulateMaps(*command_); return Success(); } auto Parser::FinalizeParse() -> ErrorOr { // If an argument action is provided, we run that and consider the parse // meta-successful rather than verifying required arguments were provided and // the (sub)command action. if (arg_meta_action_) { arg_meta_action_(); return ParseResult::MetaSuccess; } // Verify we're not missing any arguments. CARBON_RETURN_IF_ERROR(FinalizeParsedOptions()); // If we were appending to a positional argument, mark that as complete. llvm::ArrayRef positional_args = command_->positional_args; if (appending_to_positional_arg_) { CARBON_CHECK( static_cast(positional_arg_index_) < positional_args.size(), "Appending to a positional argument with an invalid index: {0}", positional_arg_index_); ++positional_arg_index_; } // See if any positional args are required and unparsed. auto unparsed_positional_args = positional_args.slice(positional_arg_index_); if (!unparsed_positional_args.empty()) { // There are un-parsed positional arguments, make sure they aren't required. const Arg& missing_arg = *unparsed_positional_args.front(); if (missing_arg.is_required) { return Error( llvm::formatv("not all required positional arguments were provided; " "first missing and required positional argument: `{0}`", missing_arg.info.name)); } for (const auto& arg_ptr : unparsed_positional_args) { CARBON_CHECK( !arg_ptr->is_required, "Cannot have required positional parameters after an optional one."); } } switch (command_->kind) { case Command::Kind::Invalid: CARBON_FATAL("Should never have a parser with an invalid command!"); case Command::Kind::RequiresSubcommand: { RawStringOstream error; error << "no subcommand specified; available subcommands: "; MetaPrinter(&error).PrintSubcommands(*command_); return Error(error.TakeStr()); } case Command::Kind::Action: // All arguments have been successfully parsed, run any action for the // most specific selected command. Only the leaf command's action is run. command_->action(); return ParseResult::Success; case Command::Kind::MetaAction: command_->action(); return ParseResult::MetaSuccess; } } auto Parser::ParsePositionalSuffix( llvm::ArrayRef unparsed_args) -> ErrorOr { CARBON_CHECK( !command_->positional_args.empty(), "Cannot do positional suffix parsing without positional arguments!"); CARBON_CHECK( !unparsed_args.empty() && unparsed_args.front() == "--", "Must be called with a suffix of arguments starting with a `--` that " "switches to positional suffix parsing."); // Once we're in the positional suffix, we can track empty positional // arguments. bool empty_positional = false; while (!unparsed_args.empty()) { llvm::StringRef unparsed_arg = unparsed_args.consume_front(); if (unparsed_arg != "--") { CARBON_RETURN_IF_ERROR(ParsePositionalArg(unparsed_arg)); empty_positional = false; continue; } if (appending_to_positional_arg_ || empty_positional) { ++positional_arg_index_; if (static_cast(positional_arg_index_) >= command_->positional_args.size()) { return Error( llvm::formatv("completed parsing all {0} configured positional " "arguments, but found a subsequent `--` and have no " "further positional arguments to parse beyond it", command_->positional_args.size())); } } appending_to_positional_arg_ = false; empty_positional = true; } return Success(); } Parser::Parser(llvm::raw_ostream* out, CommandInfo command_info, llvm::function_refvoid> build) : meta_printer_(out), root_command_(command_info) { // Run the command building lambda on a builder for the root command. CommandBuilder builder(&root_command_, &meta_printer_); build(builder); builder.Finalize(); command_ = &root_command_; } auto Parser::Parse(llvm::ArrayRef unparsed_args) -> ErrorOr { PopulateMaps(*command_); while (!unparsed_args.empty()) { // Peak at the front for an exact `--` argument that switches to a // positional suffix parsing without dropping this argument. if (unparsed_args.front() == "--") { if (command_->positional_args.empty()) { return Error( "cannot meaningfully end option and subcommand arguments with a " "`--` argument when there are no positional arguments to parse"); } if (static_cast(positional_arg_index_) >= command_->positional_args.size()) { return Error( "switched to purely positional arguments with a `--` argument " "despite already having parsed all positional arguments for this " "command"); } CARBON_RETURN_IF_ERROR(ParsePositionalSuffix(unparsed_args)); // No more unparsed arguments to handle. break; } // Now that we're not switching parse modes, drop the current unparsed // argument and parse it. llvm::StringRef unparsed_arg = unparsed_args.consume_front(); if (unparsed_arg.starts_with("--")) { // Note that the exact argument "--" has been handled above already. CARBON_RETURN_IF_ERROR(ParseLongOption(unparsed_arg)); continue; } if (unparsed_arg.starts_with("-") && unparsed_arg.size() > 1) { CARBON_RETURN_IF_ERROR(ParseShortOptionSeq(unparsed_arg)); continue; } CARBON_CHECK( command_->positional_args.empty() || command_->subcommands.empty(), "Cannot have both positional arguments and subcommands!"); if (command_->positional_args.empty() && command_->subcommands.empty()) { return Error(llvm::formatv( "found unexpected positional argument or subcommand: `{0}`", unparsed_arg)); } if (!command_->positional_args.empty()) { CARBON_RETURN_IF_ERROR(ParsePositionalArg(unparsed_arg)); continue; } CARBON_RETURN_IF_ERROR(ParseSubcommand(unparsed_arg)); } return FinalizeParse(); } auto ArgBuilder::Required(bool is_required) -> void { arg_->is_required = is_required; } auto ArgBuilder::HelpHidden(bool is_help_hidden) -> void { arg_->is_help_hidden = is_help_hidden; } ArgBuilder::ArgBuilder(Arg* arg) : arg_(arg) {} auto FlagBuilder::Default(bool flag_value) -> void { arg()->has_default = true; arg()->default_flag = flag_value; } auto FlagBuilder::Set(bool* flag) -> void { arg()->flag_storage = flag; } auto IntegerArgBuilder::Default(int integer_value) -> void { arg()->has_default = true; arg()->default_integer = integer_value; } auto IntegerArgBuilder::Set(int* integer) -> void { arg()->is_append = false; arg()->integer_storage = integer; } auto IntegerArgBuilder::Append(llvm::SmallVectorImpl* sequence) -> void { arg()->is_append = true; arg()->integer_sequence = sequence; } auto StringArgBuilder::Default(llvm::StringRef string_value) -> void { arg()->has_default = true; arg()->default_string = string_value; } auto StringArgBuilder::Set(llvm::StringRef* string) -> void { arg()->is_append = false; arg()->string_storage = string; } auto StringArgBuilder::Append(llvm::SmallVectorImpl* sequence) -> void { arg()->is_append = true; arg()->string_sequence = sequence; } static auto IsValidName(llvm::StringRef name) -> bool { if (name.size() <= 1) { return false; } if (!llvm::isAlnum(name.front())) { return false; } if (!llvm::isAlnum(name.back())) { return false; } for (char c : name.drop_front().drop_back()) { if (c != '-' && c != '_' && !llvm::isAlnum(c)) { return false; } } // We disallow names starting with "no-" as we will parse those for boolean // flags. return !name.starts_with("no-"); } auto CommandBuilder::AddFlag(const ArgInfo& info, llvm::function_refvoid> build) -> void { FlagBuilder builder(AddArgImpl(info, Arg::Kind::Flag)); // All boolean flags have an implicit default of `false`, although it can be // overridden in the build callback. builder.Default(false); build(builder); } auto CommandBuilder::AddIntegerOption( const ArgInfo& info, llvm::function_refvoid> build) -> void { IntegerArgBuilder builder(AddArgImpl(info, Arg::Kind::Integer)); build(builder); } auto CommandBuilder::AddStringOption( const ArgInfo& info, llvm::function_refvoid> build) -> void { StringArgBuilder builder(AddArgImpl(info, Arg::Kind::String)); build(builder); } auto CommandBuilder::AddOneOfOption( const ArgInfo& info, llvm::function_refvoid> build) -> void { OneOfArgBuilder builder(AddArgImpl(info, Arg::Kind::OneOf)); build(builder); } auto CommandBuilder::AddMetaActionOption( const ArgInfo& info, llvm::function_refvoid> build) -> void { ArgBuilder builder(AddArgImpl(info, Arg::Kind::MetaActionOnly)); build(builder); } auto CommandBuilder::AddIntegerPositionalArg( const ArgInfo& info, llvm::function_refvoid> build) -> void { AddPositionalArgImpl(info, Arg::Kind::Integer, [build](Arg& arg) { IntegerArgBuilder builder(&arg); build(builder); }); } auto CommandBuilder::AddStringPositionalArg( const ArgInfo& info, llvm::function_refvoid> build) -> void { AddPositionalArgImpl(info, Arg::Kind::String, [build](Arg& arg) { StringArgBuilder builder(&arg); build(builder); }); } auto CommandBuilder::AddOneOfPositionalArg( const ArgInfo& info, llvm::function_refvoid> build) -> void { AddPositionalArgImpl(info, Arg::Kind::OneOf, [build](Arg& arg) { OneOfArgBuilder builder(&arg); build(builder); }); } auto CommandBuilder::AddSubcommand( const CommandInfo& info, llvm::function_refvoid> build) -> void { CARBON_CHECK(IsValidName(info.name), "Invalid subcommand name: {0}", info.name); CARBON_CHECK(subcommand_names_.insert(info.name).second, "Added a duplicate subcommand: {0}", info.name); CARBON_CHECK( command_->positional_args.empty(), "Cannot add subcommands to a command with a positional argument."); command_->subcommands.emplace_back(new Command(info, command_)); CommandBuilder builder(command_->subcommands.back().get(), meta_printer_); build(builder); builder.Finalize(); } auto CommandBuilder::HelpHidden(bool is_help_hidden) -> void { command_->is_help_hidden = is_help_hidden; } auto CommandBuilder::RequiresSubcommand() -> void { CARBON_CHECK(!command_->subcommands.empty(), "Cannot require subcommands unless there are subcommands."); CARBON_CHECK(command_->positional_args.empty(), "Cannot require subcommands and have a positional argument."); CARBON_CHECK(command_->kind == Kind::Invalid, "Already established the kind of this command as: {0}", command_->kind); command_->kind = Kind::RequiresSubcommand; } auto CommandBuilder::Do(ActionT action) -> void { CARBON_CHECK(command_->kind == Kind::Invalid, "Already established the kind of this command as: {0}", command_->kind); command_->kind = Kind::Action; command_->action = std::move(action); } auto CommandBuilder::Meta(ActionT action) -> void { CARBON_CHECK(command_->kind == Kind::Invalid, "Already established the kind of this command as: {0}", command_->kind); command_->kind = Kind::MetaAction; command_->action = std::move(action); } CommandBuilder::CommandBuilder(Command* command, MetaPrinter* meta_printer) : command_(command), meta_printer_(meta_printer) {} auto CommandBuilder::AddArgImpl(const ArgInfo& info, Arg::Kind kind) -> Arg* { CARBON_CHECK(IsValidName(info.name), "Invalid argument name: {0}", info.name); CARBON_CHECK(arg_names_.insert(info.name).second, "Added a duplicate argument name: {0}", info.name); command_->options.emplace_back(new Arg(info)); Arg* arg = command_->options.back().get(); arg->kind = kind; return arg; } auto CommandBuilder::AddPositionalArgImpl( const ArgInfo& info, Arg::Kind kind, llvm::function_refvoid> build) -> void { CARBON_CHECK(IsValidName(info.name), "Invalid argument name: {0}", info.name); CARBON_CHECK( command_->subcommands.empty(), "Cannot add a positional argument to a command with subcommands."); command_->positional_args.emplace_back(new Arg(info)); Arg& arg = *command_->positional_args.back(); arg.kind = kind; build(arg); CARBON_CHECK(!arg.is_help_hidden, "Cannot have a help-hidden positional argument."); if (arg.is_required && command_->positional_args.size() > 1) { CARBON_CHECK((*std::prev(command_->positional_args.end(), 2))->is_required, "A required positional argument cannot be added after an " "optional one."); } } auto CommandBuilder::Finalize() -> void { meta_printer_->RegisterWithCommand(*command_, *this); } auto Parse(llvm::ArrayRef unparsed_args, llvm::raw_ostream& out, CommandInfo command_info, llvm::function_refvoid> build) -> ErrorOr { // Build a parser, which includes building the command description provided by // the user. Parser parser(&out, command_info, build); // Now parse the arguments provided using that parser. return parser.Parse(unparsed_args); } } // namespace Carbon::CommandLine // NOLINTEND(misc-no-recursion) ================================================ FILE: common/command_line.h ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #ifndef CARBON_COMMON_COMMAND_LINE_H_ #define CARBON_COMMON_COMMAND_LINE_H_ #include #include #include "common/check.h" #include "common/error.h" #include "common/ostream.h" #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/DenseSet.h" #include "llvm/ADT/Sequence.h" #include "llvm/ADT/SmallVector.h" #include "llvm/ADT/StringExtras.h" #include "llvm/ADT/StringRef.h" // # Command-line argument parsing library. // // This is a collection of tools to describe both simple and reasonably complex // command line interfaces, and parse arguments based on those descriptions. It // optimizes for command line tools that will parse arguments exactly once per // execution, and specifically tools that make heavy use of subcommand-style // command line interfaces. // // ## Terminology used by this library // // _Argument_ or _arg_: One of the parsed components of the command line. // // _Option_: A _named_ argument starting with `--` to configure some aspect of // the tool. These often have both long spellings that start with `--` and // short, single character spellings that start with `-` and can be bundled with // other single character options. // // _Flag_: A boolean or binary option. These can only be enabled or disabled. // // _Positional argument_: An argument that is not named but is identified based // on the order in which it is encountered in the command line, with options // removed. Only a leaf command can contain positional arguments. // // _Value_: The value parameter provided to an argument. For options, this is // provided after an `=` and the option name. For positional arguments, the // value is the only thing provided. The string of the value is parsed according // to the kind of argument with fairly simple rules. See the argument builders // for more details. // // _Command_: The container of options, subcommands, and positional arguments to // parse and an action to take when successful. // // _Leaf command_: A command that doesn't contain subcommands. This is the only // kind of command that can contain positional arguments. // // _Subcommand_: A command nested within another command and identified by a // specific name that ends the parsing of arguments based on the parent and // switches to parse based on the specific subcommand's options and positional // arguments. A command with subcommands cannot parse positional arguments. // // _Action_: An open-ended callback, typically reflecting a specific subcommand // being parsed. Can either directly perform the operation or simply mark which // operation was selected. // // _Meta action_: An action fully handled by argument parsing such as displaying // help, version information, or completion. // // Example command to illustrate the different components: // // git --no-pager clone --shared --filter=blob:none my-repo my-directory // // This command breaks down as: // - `git`: The top-level command. // - `--no-pager`: A negated flag on the top-level command (`git`). // - `clone`: A subcommand. // - `--shared`: A positive flag for the subcommand (`clone`). // - `--filter=blob:none`: An option named `filter` with a value `blob:none`. // - `my-repo`: The first positional argument to the subcommand. // - `my-directory`: the second positional argument to the subcommand. // // **Note:** while the example uses a `git` command to be relatively familiar // and well documented, this library does not support the same flag syntaxes as // `git` or use anything similar for its subcommand dispatch. This example is // just to help clarify the terminology used, and carefully chosen to only use a // syntax that overlaps with this library's parsed syntax. // // ## Options and flags // // The option syntax and behavior supported by this library is designed to be // strict and relatively simple while still supporting a wide range of expected // use cases: // // - All options must have a unique long name that is accessed with a `--` // prefix. The name must consist of characters in the set [-a-zA-Z0-9], and it // must not start with a `-` or `no-`. // // - Values are always attached using an `=` after the name. Only a few simple // value formats are supported: // - Arbitrary strings // - Integers as parsed by `llvm::StringRef` and whose value fits in an // `int`. // - One of a fixed set of strings // // - Options may be parsed multiple times, and the behavior can be configured: // - Each time, they can set a new value, overwriting any previous. // - They can append the value to a container. // - TODO: They can increment a count. // // - Options may have a default value that will be synthesized even if they do // not occur in the parsed command line. // // - Flags (boolean options) have some special rules. // - They may be spelled normally, and default to setting that flag to `true`. // - They may also accept a value of either exactly `true` or `false` and that // is the value. // - They may be spelled with a `no-` prefix, such as `--no-verbose`, and that // is exactly equivalent to `--verbose=false`. // - For flags with a default `true` value, they are rendered in help using // the `no-` prefix. // // - Options may additionally have a short name of a single character [a-zA-Z]. // - There is no distinction between the behavior of long and short names. // - The short name can only specify the positive or `true` value for flags. // There is no negative form of short names. // - Short names are parsed after a single `-`, such as `-v`. // - Any number of short names for boolean flags or options with default // values may be grouped after `-`, such as `-xyz`: this is three options, // `x`, `y`, and `z`. // - Short options may include a value after an `=`, but not when grouped with // other short options. // // - Options are parsed from any argument until either a subcommand switches to // that subcommand's options or a `--` argument ends all option parsing to // allow arguments that would be ambiguous with the option syntax. // // ## Subcommands // // Subcommands can be nested to any depth, and each subcommand gets its own // option space. // // ## Positional arguments // // Leaf commands (and subcommands) can accept positional arguments. These work // similar to options but consist *only* of the value. They have names, but the // name is only used in documentation and error messages. Except for the special // case of the exact argument `-`, positional argument values cannot start with // a `-` character until after option parsing is ended with a `--` argument. // // Singular positional arguments store the single value in that position. // Appending positional arguments append all of the values in sequence. // // Multiple positional arguments to a single command are parsed in sequence. For // appending positional arguments, the sequence of values appended is ended with // a `--` argument. For example, a command that takes two sequences of // positional arguments might look like: // // my-diff-tool a.txt b.txt c.txt -- new-a.txt new-b.txt new-c.txt // // The `--` used in this way *both* ends option parsing and the positional // argument sequence. Note that if there are no positional arguments prior to // the first `--`, then it will just end option parsing. To pass an empty // sequence of positional arguments two `--` arguments would be required. Once // option parsing is ended, even a single positional argument can be skipped // using a `--` argument without a positional argument. // // ## Help text blocks and rendering // // At many points in this library, a block of text is specified as a string. // These should be written using multi-line raw string literals that start on // their own line such as: // // ```cpp // ... // .help = R"""( // Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod // tempor incididunt ut labore et dolore magna aliqua. // )""", // ``` // // The code using these blocks will remove leading and trailing newlines. TODO: // It should also reflow all of the text in a way that tries to be roughly // similar to how Markdown would be reflowed when rendered: // // - Blank lines will be preserved. // - (TODO) A best effort to detect lists and other common Markdown constructs // and preserve the newlines between those. // - (TODO) Fenced regions will be preserved exactly. // // The remaining blocks will have all newlines removed when there is no column // width information on the output stream, or will be re-flowed to the output // stream's column width when available. // // ## The `Args` class // // The `Args` class is not a meaningful type, it serves two purposes: to give a // structured name prefix to the inner types, and to allow marking many // components `private` and using `friend` to grant access to implementation // details. // // ## Roadmap / TODOs / planned future work // // - Detect column width when the stream is a terminal and re-flow text. // - Implement short help display mode (and enable that by default). // - Add help text to one-of values and render it when present. // - Add formatting when the stream supports it (colors, bold, etc). // - Improve error messages when parsing fails. // - Reliably display the most appropriate usage string. // - Suggest typo corrections? // - Print snippet or otherwise render the failure better? // - Add support for responding to shell autocomplete queries. // - Finish adding support for setting and printing version information. // - Add short option counting support (`-vvv` -> `--verbose=3`). // namespace Carbon::CommandLine { // Forward declare some implementation detail classes and classes that are // friended. struct Arg; struct Command; class MetaPrinter; class Parser; class CommandBuilder; // The result of parsing arguments. enum class ParseResult : int8_t { // Signifies that program arguments were successfully parsed and can be // used. Success, // Signifies a successful meta operation such as displaying help text // was performed. No parsed arguments are available, and the side-effects // have been directly provided via the streams provided to the parser. MetaSuccess, }; auto operator<<(llvm::raw_ostream& output, ParseResult result) -> llvm::raw_ostream&; // Actions are stored in data structures so we use an owning closure to model // them. using ActionT = std::functionvoid>; // The core argument info used to render help and other descriptive // information. This is used for both options and positional arguments. // Commands use an extended version below. struct ArgInfo { // The name of the argument. For options, this is the long name parsed after // `--`. Option names must also be unique. Conventionally, these are spelled // in lower case with a single dash separating words as that tends to be // especially easy to type and reasonably easy to read. // // For positional arguments this is only used for help text, and is often // spelled with `ALL-CAPS` to make it clear that it is a placeholder name // and does not appear in the actual command line. llvm::StringRef name; // Optional short name for options. This must consist of a single character // in the set [a-zA-Z]. llvm::StringRef short_name = ""; // For options, it is sometimes useful to render a distinct value // placeholder name to help clarify what the value should contain. These are // often in `ALL-CAPS` to make it clear they are placeholders. For example, // an `output` option might set this to `FILE` so it renders as // `--output=FILE`. llvm::StringRef value_name = ""; // A long-form help string that will be rendered for this argument in // command help strings. It supports multiple lines and is rendered as a // text block described in the `Args` top-level comment. llvm::StringRef help = ""; // A short help string for the argument. This should be as short as // possible, and always fit easily on a single line. Ideally, it can fit in // a narrow column of a single line and is in the range of 40 columns wide. // // If not provided, the first paragraph (up to a blank line) from `help` // will be used. llvm::StringRef help_short = ""; }; // The kinds of arguments that can be parsed. enum class ArgKind : int8_t { Invalid, Flag, Integer, String, OneOf, MetaActionOnly, }; auto operator<<(llvm::raw_ostream& output, ArgKind kind) -> llvm::raw_ostream&; // A builder used to configure an argument for parsing. class ArgBuilder { public: // When marked as required, if an argument is not provided explicitly in the // command line the parse will produce an error. auto Required(bool is_required) -> void; // An argument can be hidden from the help output. auto HelpHidden(bool is_help_hidden) -> void; // Sets a meta-action to run when this argument is parsed. This is used to // set up arguments like `--help` or `--version` that can be entirely // handled during parsing and rather than produce parsed information about // the command line, override that for some custom behavior. template auto MetaAction(T action) -> void; protected: friend class CommandBuilder; // `arg` must not be null. explicit ArgBuilder(Arg* arg); auto arg() -> Arg* { return arg_; } private: Arg* arg_; }; // Customized argument builder when the value is a boolean flag. class FlagBuilder : public ArgBuilder { public: // Flags can be defaulted to true. However, flags always have *some* // default, this merely customizes which value is default. If uncustomized, // the default of a flag is false. auto Default(bool flag_value) -> void; // Configures the argument to store a parsed value in the provided storage. // // This must be called on the builder. auto Set(bool* flag) -> void; private: using ArgBuilder::ArgBuilder; }; // Customized argument builder when the value is an integer. class IntegerArgBuilder : public ArgBuilder { public: // Sets a default for the argument with the provided value. There is no // default for integer arguments unless this is called. // // For arguments that are `Set` below, this value will be stored even if the // argument is never explicitly provided. For arguments that are `Append`ed // below, this value will be used whenever the argument occurs without an // explicit value, but unless the argument is parsed nothing will be // appended. auto Default(int integer_value) -> void; // Configures the argument to store a parsed value in the provided storage. // Each time the argument is parsed, it will write a new value to this // storage. // // Exactly one of this method or `Append` below must be configured for the // argument. auto Set(int* integer) -> void; // Configures the argument to append a parsed value to the provided // container. Each time the argument is parsed, a new value will be // appended. // // Exactly one of this method or `Set` above must be configured for the // argument. auto Append(llvm::SmallVectorImpl* sequence) -> void; private: using ArgBuilder::ArgBuilder; }; // Customized argument builder when the value is a string. class StringArgBuilder : public ArgBuilder { public: // Sets a default for the argument with the provided value. There is no // default for string arguments unless this is called. // // For arguments that are `Set` below, this value will be stored even if the // argument is never explicitly provided. For arguments that are `Append`ed // below, this value will be used whenever the argument occurs without an // explicit value, but unless the argument is parsed nothing will be // appended. auto Default(llvm::StringRef string_value) -> void; // Configures the argument to store a parsed value in the provided storage. // Each time the argument is parsed, it will write a new value to this // storage. // // Exactly one of this method or `Append` below must be configured for the // argument. auto Set(llvm::StringRef* string) -> void; // Configures the argument to append a parsed value to the provided // container. Each time the argument is parsed, a new value will be // appended. // // Exactly one of this method or `Set` above must be configured for the // argument. auto Append(llvm::SmallVectorImpl* sequence) -> void; private: using ArgBuilder::ArgBuilder; }; // Customized argument builder when the value is required to be one of a fixed // set of possible strings, and each one maps to a specific value of some // type, often an enumerator. class OneOfArgBuilder : public ArgBuilder { public: // A tiny helper / builder type for describing one of the possible values // that a particular argument accepts. // // Beyond carrying the string, type, and value for this candidate, it also // allows marking the candidate as the default. template class OneOfValueT { public: // Configure whether a candidate is the default. If not called, it is not // the default. This can only be used when setting, not when appending. auto Default(bool is_default) && -> OneOfValueT; private: friend OneOfArgBuilder; explicit OneOfValueT(llvm::StringRef str, T value); llvm::StringRef str; T value; bool is_default = false; }; // A helper function to create an object that models one of the candidate // values for this argument. It takes the string used to select this value // on the command line, deduces the type of the value, and accepts the value // itself that should be used when this candidate is parsed. template static auto OneOfValue(llvm::StringRef string, T value) -> OneOfValueT; // Configures the argument to store a parsed value in the provided storage. // Each time the argument is parsed, it will write a new value to this // storage. // // Exactly one of this method or `AppendOneOf` below must be configured for // the argument. // // For one-of arguments, this method also provides an array of possible // values that may be used: // // ```cpp // arg_b.SetOneOf( // { // arg_b.OneOfValue("x", 1), // arg_b.OneOfValue("y", 2), // arg_b.OneOfValue("z", 3).Default(true), // }, // &value); // ``` // // The only value strings that will be parsed are those described in the // array, and the value stored in the storage for a particular parsed value // string is the one associated with it. There must be a single homogeneous // type for the all of the candidate values and that type must be storable // into the result storage pointee type. At most one of the array can also // be marked as a default value, which will make specifying a value // optional, and also will cause that value to be stored into the result // even if the argument is not parsed explicitly. template auto SetOneOf(const OneOfValueT (&values)[N], T* result) -> void; // Configures the argument to append a parsed value to the provided // container. Each time the argument is parsed, a new value will be // appended. // // Exactly one of this method or `SetOneOf` above must be configured for the // argument. // // Similar to `SetOneOf`, this must also describe the candidate value // strings that can be parsed and the consequent values that are used for // those strings: // // ```cpp // arg_b.AppendOneOf( // { // arg_b.OneOfValue("x", 1), // arg_b.OneOfValue("y", 2), // arg_b.OneOfValue("z", 3), // }, // &values); // ``` // // Instead of storing, these values are appended. // // However, appending one-of arguments cannot use a default. The values must // always be explicitly parsed. template auto AppendOneOf(const OneOfValueT (&values)[N], llvm::SmallVectorImpl* sequence) -> void; private: using ArgBuilder::ArgBuilder; template auto OneOfImpl(const OneOfValueT (&input_values)[N], MatchT match, std::index_sequence /*indices*/) -> void; }; // The extended info for a command, including for a subcommand. // // This provides the primary descriptive information for commands used by help // and other diagnostic messages. For subcommands, it also provides the name // used to access the subcommand. struct CommandInfo { // The name of the command. For subcommands, this is also the argument // spelling that accesses this subcommand. It must consist of characters in // the set [-a-zA-Z0-9], and must not start with a `-`. llvm::StringRef name; // An optional version string that will be rendered as part of version meta // action by the library. While this library does not impose a machine // parsable structure, users will expect this to be extractable and parsable // in practice. // // Subcommands with an empty version will inherit the first non-empty parent // version. // // Whether a (possibly inherited) version string is non-empty determines // whether this library provides the version-printing meta actions via a // `--version` flag or, if there are subcommands, a `version` subcommand. llvm::StringRef version = ""; // Optional build information to include when printing the version. llvm::StringRef build_info = ""; // An optional long-form help string for the command. // // When accessing a command's dedicated help output, this will form the main // prose output at the beginning of the help message. When listing // subcommands, the subcommand's `help` string will be used to describe it // in the list. // // The help meta actions are available regardless of whether this is // provided in order to mechanically describe other aspects of the command. // // This field supports multiple lines and uses the text block handling // described in the top-level `Args` comment. llvm::StringRef help = ""; // An optional short help string for the command. // // This should be very short, at most one line and ideally fitting within a // narrow column of a line. If left blank, the first paragraph of text (up // to the first blank line) in the `help` string will be used. llvm::StringRef help_short = ""; // An optional custom block of text to describe the usage of the command. // // The usage should just be a series of lines with possible invocations of // the command summarized as briefly as possible. Ideally, each variation on // a single line. The goal is to show the *structure* of different command // invocations, not to be comprehensive. // // When omitted, the library will generate these based on the parsing logic. // The generated usage is expected to be suitable for the vast majority of // users, but can be customized in cases where necessary. llvm::StringRef usage = ""; // An optional epilogue multi-line block of text appended to the help display // for this command. It is only used for this command's dedicated help, but // can contain extra, custom guidance that is especially useful to have at // the very end of the output. llvm::StringRef help_epilogue = ""; }; // Commands are classified based on the action they result in when run. // // Commands that require a subcommand have no action and are just a means to // reach the subcommand actions. // // Commands with _meta_ actions are also a separate kind from those with // normal actions. enum class CommandKind : int8_t { Invalid, RequiresSubcommand, Action, MetaAction, }; auto operator<<(llvm::raw_ostream& output, CommandKind kind) -> llvm::raw_ostream&; // Builder used to configure a command to parse. // // An instance of this is used to configure the top-level command, and a fresh // instance is provided for configuring each subcommand as well. class CommandBuilder { public: using Kind = CommandKind; auto AddFlag(const ArgInfo& info, llvm::function_refvoid> build) -> void; auto AddIntegerOption( const ArgInfo& info, llvm::function_refvoid> build) -> void; auto AddStringOption(const ArgInfo& info, llvm::function_refvoid> build) -> void; auto AddOneOfOption(const ArgInfo& info, llvm::function_refvoid> build) -> void; auto AddMetaActionOption(const ArgInfo& info, llvm::function_refvoid> build) -> void; auto AddIntegerPositionalArg( const ArgInfo& info, llvm::function_refvoid> build) -> void; auto AddStringPositionalArg( const ArgInfo& info, llvm::function_refvoid> build) -> void; auto AddOneOfPositionalArg( const ArgInfo& info, llvm::function_refvoid> build) -> void; auto AddSubcommand(const CommandInfo& info, llvm::function_refvoid> build) -> void; // Subcommands can be hidden from the help listing of their parents with // this setting. Hiding a subcommand doesn't disable its own help, it just // removes it from the listing. auto HelpHidden(bool is_help_hidden) -> void; // Exactly one of these three should be called to select and configure the // kind of the built command. auto RequiresSubcommand() -> void; auto Do(ActionT action) -> void; auto Meta(ActionT meta_action) -> void; private: friend Parser; // `command` and `meta_printer` must not be null. explicit CommandBuilder(Command* command, MetaPrinter* meta_printer); auto AddArgImpl(const ArgInfo& info, ArgKind kind) -> Arg*; auto AddPositionalArgImpl(const ArgInfo& info, ArgKind kind, llvm::function_refvoid> build) -> void; auto Finalize() -> void; Command* command_; MetaPrinter* meta_printer_; llvm::SmallDenseSet arg_names_; llvm::SmallDenseSet subcommand_names_; }; // Builds a description of a command and then parses the provided arguments // for that command. // // This is the main entry point to both build up the description of the // command whose arguments are being parsed and to do the parsing. Everything // is done in a single invocation as the common case is to build a command // description, parse the arguments once, and then run with that // configuration. // // The `out` stream is treated like `stdout` would be for a Unix-style command // line tool, and `errors` like `stderr`: any errors or diagnostic information // are printed to `errors`, but meta-actions like printing a command's help go // to `out`. auto Parse(llvm::ArrayRef unparsed_args, llvm::raw_ostream& out, CommandInfo command_info, llvm::function_refvoid> build) -> ErrorOr; // Implementation details only below. // The internal representation of a parsable argument description. struct Arg { using Kind = ArgKind; using ValueActionT = std::functionbool>; using DefaultActionT = std::functionvoid>; explicit Arg(ArgInfo info); ~Arg(); ArgInfo info; Kind kind = Kind::Invalid; bool has_default = false; bool is_required = false; bool is_append = false; bool is_help_hidden = false; // Meta action storage, only populated if this argument causes a meta action. ActionT meta_action; // Storage options depending on the kind. union { // Singular argument storage pointers. bool* flag_storage; int* integer_storage; llvm::StringRef* string_storage; // Appending argument storage pointers. llvm::SmallVectorImpl* integer_sequence; llvm::SmallVectorImpl* string_sequence; // One-of information. struct { llvm::SmallVector value_strings; ValueActionT value_action; }; }; // Default values depending on the kind. union { bool default_flag; int default_integer; llvm::StringRef default_string; struct { DefaultActionT default_action; int default_value_index; }; }; }; // The internal representation of a parsable command description, including its // options, positional arguments, and subcommands. struct Command { using Kind = CommandBuilder::Kind; explicit Command(CommandInfo info, Command* parent = nullptr); CommandInfo info; Command* parent; ActionT action; Kind kind = Kind::Invalid; bool is_help_hidden = false; llvm::SmallVector> options; llvm::SmallVector> positional_args; llvm::SmallVector> subcommands; }; template auto ArgBuilder::MetaAction(T action) -> void { CARBON_CHECK(!arg_->meta_action, "Cannot set a meta action twice!"); arg_->meta_action = std::move(action); } template auto OneOfArgBuilder::OneOfValueT::Default( bool is_default) && -> OneOfValueT { OneOfValueT result = std::move(*this); result.is_default = is_default; return result; } template OneOfArgBuilder::OneOfValueT::OneOfValueT(llvm::StringRef str, T value) : str(str), value(std::move(value)) {} template auto OneOfArgBuilder::OneOfValue(llvm::StringRef str, T value) -> OneOfValueT { return OneOfValueT(str, value); } template auto OneOfArgBuilder::SetOneOf(const OneOfValueT (&values)[N], T* result) -> void { static_assert(N > 0, "Must include at least one value."); arg()->is_append = false; OneOfImpl( values, [result](T value) { *result = value; }, std::make_index_sequence{}); } template auto OneOfArgBuilder::AppendOneOf(const OneOfValueT (&values)[N], llvm::SmallVectorImpl* sequence) -> void { static_assert(N > 0, "Must include at least one value."); arg()->is_append = true; OneOfImpl( values, [sequence](T value) { sequence->push_back(value); }, std::make_index_sequence{}); } // An implementation tool for the one-of value candidate handling. Delegating to // this allows us to deduce a pack of indices from the array of candidates, and // then use that variadic pack to operate on the array in the variadic space. // This includes packaging the components up separately into our storage // representation, as well as processing the array to find and register any // default. // // The representation is especially tricky because we want all of the actual // values and even the *type* of values to be erased. We do that by building // lambdas that do the type-aware operations and storing those into type-erased // function objects. template auto OneOfArgBuilder::OneOfImpl(const OneOfValueT (&input_values)[N], MatchT match, std::index_sequence /*indices*/) -> void { std::array values = {input_values[Indices].value...}; // Directly copy the value strings into a vector. new (&arg()->value_strings) llvm::SmallVector( std::initializer_list{input_values[Indices].str...}); // And build a type-erased action that maps a specific value string to a value // by index. new (&arg()->value_action) Arg::ValueActionT( [values, match](const Arg& arg, llvm::StringRef value_string) -> bool { for (auto [value, arg_value_string] : llvm::zip_equal(values, arg.value_strings)) { if (value_string == arg_value_string) { match(value); return true; } } return false; }); // Fold over all the input values to see if there is a default. if ((input_values[Indices].is_default || ...)) { CARBON_CHECK(!arg()->is_append, "Can't append default."); CARBON_CHECK((input_values[Indices].is_default + ... + 0) == 1, "Cannot default more than one value."); arg()->has_default = true; // First build a lambda that configures the default using an index. We'll // call this below, this lambda isn't the one that is stored. auto set_default = [&](size_t index, const auto& default_value) { // Now that we have the desired default index, build a lambda and store it // as the default action. This lambda is stored and so it captures the // necessary information explicitly and by value. new (&arg()->default_action) Arg::DefaultActionT([value = default_value.value, match](const Arg& /*arg*/) { match(value); }); // Also store the index itself for use when printing help. arg()->default_value_index = index; }; // Now we fold across the inputs and in the one case that is the default, we // call the lambda. This is just a somewhat awkward way to write a loop with // a condition in it over a pack. ((input_values[Indices].is_default ? set_default(Indices, input_values[Indices]) : static_cast(0)), ...); } } } // namespace Carbon::CommandLine #endif // CARBON_COMMON_COMMAND_LINE_H_ ================================================ FILE: common/command_line_test.cpp ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #include "common/command_line.h" #include #include #include #include "common/error_test_helpers.h" #include "common/raw_string_ostream.h" #include "llvm/Support/FormatVariadic.h" namespace Carbon::CommandLine { namespace { using ::Carbon::Testing::IsError; using ::Carbon::Testing::IsSuccess; using ::testing::ElementsAre; using ::testing::Eq; using ::testing::StrEq; constexpr CommandInfo TestCommandInfo = { .name = "test", .help = "A test command.", .help_epilogue = "TODO", }; enum class TestEnum : int8_t { Val1, Val2, }; enum class TestSubcommand : int8_t { Sub1, Sub2, }; TEST(ArgParserTest, BasicCommand) { bool flag = false; int integer_option = -1; llvm::StringRef string_option = ""; auto parse = [&](llvm::ArrayRef args) { return Parse(args, llvm::errs(), TestCommandInfo, [&](auto& b) { b.AddFlag({.name = "flag"}, [&](auto& arg_b) { arg_b.Set(&flag); }); b.AddIntegerOption({.name = "option1"}, [&](auto& arg_b) { arg_b.Set(&integer_option); }); b.AddStringOption({.name = "option2"}, [&](auto& arg_b) { arg_b.Set(&string_option); }); b.Do([] {}); }); }; EXPECT_THAT(parse({"--flag", "--option2=value", "--option1=42"}), IsSuccess(Eq(ParseResult::Success))); EXPECT_TRUE(flag); EXPECT_THAT(integer_option, Eq(42)); EXPECT_THAT(string_option, StrEq("value")); } TEST(ArgParserTest, BooleanFlags) { bool flag = false; auto parse = [&](llvm::ArrayRef args, llvm::raw_ostream& s) { return Parse(args, s, TestCommandInfo, [&](auto& b) { b.AddFlag({.name = "flag"}, [&](auto& arg_b) { arg_b.Set(&flag); }); b.Do([] {}); }); }; EXPECT_THAT(parse({"--no-flag"}, llvm::errs()), IsSuccess(Eq(ParseResult::Success))); EXPECT_FALSE(flag); EXPECT_THAT(parse({"--flag", "--no-flag"}, llvm::errs()), IsSuccess(Eq(ParseResult::Success))); EXPECT_FALSE(flag); EXPECT_THAT(parse({"--no-flag", "--flag"}, llvm::errs()), IsSuccess(Eq(ParseResult::Success))); EXPECT_TRUE(flag); EXPECT_THAT(parse({"--no-flag", "--flag", "--flag=false"}, llvm::errs()), IsSuccess(Eq(ParseResult::Success))); EXPECT_FALSE(flag); EXPECT_THAT(parse({"--no-flag", "--flag=true"}, llvm::errs()), IsSuccess(Eq(ParseResult::Success))); EXPECT_TRUE(flag); EXPECT_THAT(parse({"--no-flag", "--flag=true", "--no-flag"}, llvm::errs()), IsSuccess(Eq(ParseResult::Success))); EXPECT_FALSE(flag); RawStringOstream os; EXPECT_THAT( parse({"--no-flag=true"}, os), IsError(StrEq("cannot specify a value when using a flag name prefixed " "with `no-` -- that prefix implies a value of `false`"))); EXPECT_THAT( parse({"--no-flag=false"}, os), IsError(StrEq("cannot specify a value when using a flag name prefixed " "with `no-` -- that prefix implies a value of `false`"))); } TEST(ArgParserTest, ArgDefaults) { bool flag = false; int integer_option = -1; llvm::StringRef string_option = ""; auto parse = [&](llvm::ArrayRef args, llvm::raw_ostream& s) { return Parse(args, s, TestCommandInfo, [&](auto& b) { b.AddFlag({.name = "flag"}, [&](auto& arg_b) { arg_b.Default(true); arg_b.Set(&flag); }); b.AddIntegerOption({.name = "option1"}, [&](auto& arg_b) { arg_b.Default(7); arg_b.Set(&integer_option); }); b.AddStringOption({.name = "option2"}, [&](auto& arg_b) { arg_b.Default("default"); arg_b.Set(&string_option); }); b.Do([] {}); }); }; EXPECT_THAT(parse({}, llvm::errs()), IsSuccess(Eq(ParseResult::Success))); EXPECT_TRUE(flag); EXPECT_THAT(integer_option, Eq(7)); EXPECT_THAT(string_option, StrEq("default")); EXPECT_THAT(parse({"--option1", "--option2"}, llvm::errs()), IsSuccess(Eq(ParseResult::Success))); EXPECT_TRUE(flag); EXPECT_THAT(integer_option, Eq(7)); EXPECT_THAT(string_option, StrEq("default")); EXPECT_THAT(parse({"--option1=42", "--option2=explicit"}, llvm::errs()), IsSuccess(Eq(ParseResult::Success))); EXPECT_TRUE(flag); EXPECT_THAT(integer_option, Eq(42)); EXPECT_THAT(string_option, StrEq("explicit")); EXPECT_THAT( parse({"--option1=42", "--option2=explicit", "--option1", "--option2"}, llvm::errs()), IsSuccess(Eq(ParseResult::Success))); EXPECT_TRUE(flag); EXPECT_THAT(integer_option, Eq(7)); EXPECT_THAT(string_option, StrEq("default")); } TEST(ArgParserTest, ShortArgs) { bool flag = false; bool example = false; int integer_option = -1; auto parse = [&](llvm::ArrayRef args, llvm::raw_ostream& s) { return Parse(args, s, TestCommandInfo, [&](auto& b) { b.AddFlag({.name = "flag", .short_name = "f"}, [&](auto& arg_b) { arg_b.Set(&flag); }); b.AddFlag({.name = "example", .short_name = "x"}, [&](auto& arg_b) { arg_b.Set(&example); }); b.AddIntegerOption({.name = "option1", .short_name = "o"}, [&](auto& arg_b) { arg_b.Default(123); arg_b.Set(&integer_option); }); b.AddIntegerOption({.name = "option2", .short_name = "z"}, [&](auto& arg_b) { arg_b.Set(&integer_option); }); b.Do([] {}); }); }; EXPECT_THAT(parse({"-f", "-o=42", "-x"}, llvm::errs()), IsSuccess(Eq(ParseResult::Success))); EXPECT_TRUE(flag); EXPECT_TRUE(example); EXPECT_THAT(integer_option, Eq(42)); flag = false; example = false; EXPECT_THAT(parse({"--option1=13", "-xfo"}, llvm::errs()), IsSuccess(Eq(ParseResult::Success))); EXPECT_TRUE(flag); EXPECT_TRUE(example); EXPECT_THAT(integer_option, Eq(123)); RawStringOstream os; EXPECT_THAT(parse({"-v"}, os), IsError(StrEq("unknown short option `-v`"))); EXPECT_THAT(parse({"-xvx"}, os), IsError(StrEq("unknown short option `-v`"))); EXPECT_THAT( parse({"-xzf"}, os), IsError(StrEq("option `-z` (short for `--option2`) requires a value to " "be provided and none was"))); EXPECT_THAT( parse({"--option2"}, os), IsError(StrEq( "option `--option2` requires a value to be provided and none was"))); EXPECT_THAT( parse({"-xz=123"}, os), IsError(StrEq("cannot provide a value to the group of multiple short " "options `-xz=...`; values must be provided to a single " "option, using either the short or long spelling"))); } TEST(ArgParserTest, PositionalArgs) { bool flag = false; llvm::StringRef string_option = ""; llvm::StringRef source_string; llvm::StringRef dest_string; auto parse = [&](llvm::ArrayRef args, llvm::raw_ostream& s) { return Parse(args, s, TestCommandInfo, [&](auto& b) { b.AddFlag({.name = "flag"}, [&](auto& arg_b) { arg_b.Set(&flag); }); b.AddStringOption({.name = "option"}, [&](auto& arg_b) { arg_b.Set(&string_option); }); b.AddStringPositionalArg({.name = "source"}, [&](auto& arg_b) { arg_b.Set(&source_string); arg_b.Required(true); }); b.AddStringPositionalArg({.name = "dest"}, [&](auto& arg_b) { arg_b.Set(&dest_string); arg_b.Required(true); }); b.Do([] {}); }); }; RawStringOstream os; EXPECT_THAT(parse({"--flag", "--option=x"}, os), IsError(StrEq( "not all required positional arguments were provided; first " "missing and required positional argument: `source`"))); EXPECT_THAT(parse({"src", "--flag", "--option=value", "dst"}, llvm::errs()), IsSuccess(Eq(ParseResult::Success))); EXPECT_TRUE(flag); EXPECT_THAT(string_option, StrEq("value")); EXPECT_THAT(source_string, StrEq("src")); EXPECT_THAT(dest_string, StrEq("dst")); EXPECT_THAT(parse({"src2", "--", "dst2"}, llvm::errs()), IsSuccess(Eq(ParseResult::Success))); EXPECT_THAT(source_string, StrEq("src2")); EXPECT_THAT(dest_string, StrEq("dst2")); EXPECT_THAT(parse({"-", "--", "-"}, llvm::errs()), IsSuccess(Eq(ParseResult::Success))); EXPECT_THAT(source_string, StrEq("-")); EXPECT_THAT(dest_string, StrEq("-")); } TEST(ArgParserTest, PositionalAppendArgs) { bool flag = false; llvm::StringRef string_option = ""; llvm::SmallVector source_strings; llvm::SmallVector dest_strings; auto parse = [&](llvm::ArrayRef args, llvm::raw_ostream& s) { return Parse(args, s, TestCommandInfo, [&](auto& b) { b.AddFlag({.name = "flag"}, [&](auto& arg_b) { arg_b.Set(&flag); }); b.AddStringOption({.name = "option"}, [&](auto& arg_b) { arg_b.Set(&string_option); }); b.AddStringPositionalArg({.name = "sources"}, [&](auto& arg_b) { arg_b.Append(&source_strings); arg_b.Required(true); }); b.AddStringPositionalArg({.name = "dest"}, [&](auto& arg_b) { arg_b.Append(&dest_strings); arg_b.Required(true); }); b.Do([] {}); }); }; RawStringOstream os; EXPECT_THAT(parse({"--flag", "--option=x"}, os), IsError(StrEq( "not all required positional arguments were provided; first " "missing and required positional argument: `sources`"))); EXPECT_THAT( parse({"src1", "--flag", "src2", "--option=value", "--", "--dst--"}, llvm::errs()), IsSuccess(Eq(ParseResult::Success))); EXPECT_TRUE(flag); EXPECT_THAT(string_option, StrEq("value")); EXPECT_THAT(source_strings, ElementsAre(StrEq("src1"), StrEq("src2"))); EXPECT_THAT(dest_strings, ElementsAre(StrEq("--dst--"))); source_strings.clear(); dest_strings.clear(); EXPECT_THAT( parse({"--", "--src1--", "--src2--", "--", "dst1", "dst2"}, llvm::errs()), IsSuccess(Eq(ParseResult::Success))); EXPECT_THAT(source_strings, ElementsAre(StrEq("--src1--"), StrEq("--src2--"))); EXPECT_THAT(dest_strings, ElementsAre(StrEq("dst1"), StrEq("dst2"))); source_strings.clear(); dest_strings.clear(); EXPECT_THAT(parse({"--", "--", "dst1", "dst2"}, llvm::errs()), IsSuccess(Eq(ParseResult::Success))); EXPECT_THAT(source_strings, ElementsAre()); EXPECT_THAT(dest_strings, ElementsAre(StrEq("dst1"), StrEq("dst2"))); } TEST(ArgParserTest, BasicSubcommands) { bool flag = false; llvm::StringRef option1 = ""; llvm::StringRef option2 = ""; TestSubcommand subcommand; bool subsub_command = false; auto parse = [&](llvm::ArrayRef args, llvm::raw_ostream& s) { return Parse(args, s, TestCommandInfo, [&](auto& b) { b.AddSubcommand({.name = "sub1"}, [&](auto& sub_b) { sub_b.AddFlag({.name = "flag"}, [&](auto& arg_b) { arg_b.Set(&flag); }); sub_b.AddStringOption({.name = "option"}, [&](auto& arg_b) { arg_b.Set(&option1); }); sub_b.Do([&] { subcommand = TestSubcommand::Sub1; }); }); b.AddSubcommand({.name = "sub2"}, [&](auto& sub_b) { sub_b.AddStringOption({.name = "option"}, [&](auto& arg_b) { arg_b.Set(&option1); }); sub_b.AddSubcommand({.name = "subsub"}, [&](auto& subsub_b) { subsub_b.AddStringOption({.name = "option"}, [&](auto& arg_b) { arg_b.Set(&option2); }); subsub_b.Do([&] { subsub_command = true; }); }); sub_b.Do([&] { subcommand = TestSubcommand::Sub2; }); }); b.RequiresSubcommand(); }); }; RawStringOstream os; EXPECT_THAT(parse({}, os), IsError(StrEq("no subcommand specified; available subcommands: " "`sub1`, `sub2`, or `help`"))); EXPECT_THAT(parse({"--flag"}, os), IsError(StrEq("unknown option `--flag`"))); EXPECT_THAT(parse({"sub3"}, os), IsError(StrEq("invalid subcommand `sub3`; available subcommands: " "`sub1`, `sub2`, or `help`"))); EXPECT_THAT(parse({"--flag", "sub1"}, os), IsError(StrEq("unknown option `--flag`"))); EXPECT_THAT(parse({"sub1", "--flag"}, llvm::errs()), IsSuccess(Eq(ParseResult::Success))); EXPECT_THAT(subcommand, Eq(TestSubcommand::Sub1)); EXPECT_TRUE(flag); EXPECT_THAT(parse({"sub2", "--option=value"}, llvm::errs()), IsSuccess(Eq(ParseResult::Success))); EXPECT_THAT(subcommand, Eq(TestSubcommand::Sub2)); EXPECT_THAT(option1, StrEq("value")); EXPECT_THAT(parse({"sub2", "--option=abc", "subsub42", "--option=xyz"}, os), IsError(StrEq("invalid subcommand `subsub42`; available " "subcommands: `subsub` or `help`"))); EXPECT_THAT( parse({"sub2", "--option=abc", "subsub", "--option=xyz"}, llvm::errs()), IsSuccess(Eq(ParseResult::Success))); EXPECT_TRUE(subsub_command); EXPECT_THAT(option1, StrEq("abc")); EXPECT_THAT(option2, StrEq("xyz")); } TEST(ArgParserTest, Appending) { llvm::SmallVector integers; llvm::SmallVector strings; auto parse = [&](llvm::ArrayRef args, llvm::raw_ostream& s) { return Parse(args, s, TestCommandInfo, [&](auto& b) { b.AddIntegerOption({.name = "int"}, [&](auto& arg_b) { arg_b.Append(&integers); }); b.AddStringOption({.name = "str"}, [&](auto& arg_b) { arg_b.Append(&strings); }); b.Do([] {}); }); }; EXPECT_THAT(parse({}, llvm::errs()), IsSuccess(Eq(ParseResult::Success))); EXPECT_THAT(integers, ElementsAre()); EXPECT_THAT(strings, ElementsAre()); EXPECT_THAT(parse({"--int=1", "--int=2", "--int=3"}, llvm::errs()), IsSuccess(Eq(ParseResult::Success))); EXPECT_THAT(integers, ElementsAre(Eq(1), Eq(2), Eq(3))); EXPECT_THAT(strings, ElementsAre()); EXPECT_THAT(parse({"--str=a", "--str=b", "--str=c"}, llvm::errs()), IsSuccess(Eq(ParseResult::Success))); EXPECT_THAT(integers, ElementsAre(Eq(1), Eq(2), Eq(3))); EXPECT_THAT(strings, ElementsAre(StrEq("a"), StrEq("b"), StrEq("c"))); EXPECT_THAT(parse({"--str=d", "--int=4", "--str=e"}, llvm::errs()), IsSuccess(Eq(ParseResult::Success))); EXPECT_THAT(integers, ElementsAre(Eq(1), Eq(2), Eq(3), Eq(4))); EXPECT_THAT(strings, ElementsAre(StrEq("a"), StrEq("b"), StrEq("c"), StrEq("d"), StrEq("e"))); } TEST(ArgParserTest, PositionalAppending) { llvm::SmallVector option_strings; llvm::SmallVector strings1; llvm::SmallVector strings2; llvm::SmallVector strings3; auto parse = [&](llvm::ArrayRef args, llvm::raw_ostream& s) { return Parse(args, s, TestCommandInfo, [&](auto& b) { b.AddStringOption({.name = "opt"}, [&](auto& arg_b) { arg_b.Append(&option_strings); }); b.AddStringPositionalArg({.name = "s1"}, [&](auto& arg_b) { arg_b.Append(&strings1); }); b.AddStringPositionalArg({.name = "s2"}, [&](auto& arg_b) { arg_b.Append(&strings2); }); b.AddStringPositionalArg({.name = "s3"}, [&](auto& arg_b) { arg_b.Append(&strings3); }); b.Do([] {}); }); }; EXPECT_THAT(parse({"a", "--opt=x", "b", "--opt=y", "--", "c", "--opt=z", "d", "--", "e", "f"}, llvm::errs()), IsSuccess(Eq(ParseResult::Success))); EXPECT_THAT(option_strings, ElementsAre(StrEq("x"), StrEq("y"))); EXPECT_THAT(strings1, ElementsAre(StrEq("a"), StrEq("b"))); EXPECT_THAT(strings2, ElementsAre(StrEq("c"), StrEq("--opt=z"), StrEq("d"))); EXPECT_THAT(strings3, ElementsAre(StrEq("e"), StrEq("f"))); } static auto ParseOneOfOption( llvm::ArrayRef args, llvm::raw_ostream& s, llvm::function_refvoid> build) -> ErrorOr { return Parse(args, s, TestCommandInfo, [&](auto& b) { b.AddOneOfOption({.name = "option"}, build); b.Do([] {}); }); } TEST(ArgParserTest, OneOfOption) { int value = 0; auto parse = [&](llvm::ArrayRef args, llvm::raw_ostream& s) { return ParseOneOfOption(args, s, [&](auto& arg_b) { arg_b.SetOneOf( { arg_b.OneOfValue("x", 1), arg_b.OneOfValue("y", 2), arg_b.OneOfValue("z", 3), }, &value); }); }; EXPECT_THAT(parse({"--option=x"}, llvm::errs()), IsSuccess(Eq(ParseResult::Success))); EXPECT_THAT(value, Eq(1)); EXPECT_THAT(parse({"--option=y"}, llvm::errs()), IsSuccess(Eq(ParseResult::Success))); EXPECT_THAT(value, Eq(2)); EXPECT_THAT(parse({"--option=z"}, llvm::errs()), IsSuccess(Eq(ParseResult::Success))); EXPECT_THAT(value, Eq(3)); RawStringOstream os; EXPECT_THAT( parse({"--option"}, os), IsError(StrEq( "option `--option` requires a value to be provided and none was"))); constexpr const char* ErrorStr = "option `--option={0}` has an invalid value `{0}`; valid values are: " "`x`, `y`, or `z`"; EXPECT_THAT(parse({"--option=a"}, os), IsError(StrEq(llvm::formatv(ErrorStr, "a")))); EXPECT_THAT(parse({"--option="}, os), IsError(StrEq(llvm::formatv(ErrorStr, "")))); EXPECT_THAT(parse({"--option=xx"}, os), IsError(StrEq(llvm::formatv(ErrorStr, "xx")))); EXPECT_THAT(parse({"--option=\xFF"}, os), IsError(StrEq(llvm::formatv(ErrorStr, "\\FF")))); EXPECT_THAT(parse({llvm::StringRef("--option=\0", 10)}, os), IsError(StrEq(llvm::formatv(ErrorStr, "\\00")))); } TEST(ArgParserTest, OneOfOptionWithTwoOptions) { int value = 0; RawStringOstream os; EXPECT_THAT(ParseOneOfOption({"--option=z"}, os, [&](auto& arg_b) { arg_b.SetOneOf( { arg_b.OneOfValue("x", 1), arg_b.OneOfValue("y", 2), }, &value); }), IsError(StrEq("option `--option=z` has an invalid value `z`; " "valid values are: `x` or `y`"))); } TEST(ArgParserTest, OneOfOptionWithOneOption) { int value = 0; RawStringOstream os; EXPECT_THAT(ParseOneOfOption({"--option=z"}, os, [&](auto& arg_b) { arg_b.SetOneOf( { arg_b.OneOfValue("x", 1), }, &value); }), IsError(StrEq("option `--option=z` has an invalid value `z`; " "valid values are: `x`"))); } TEST(ArgParserTest, OneOfOptionAppending) { llvm::SmallVector values; auto parse = [&](llvm::ArrayRef args, llvm::raw_ostream& s) { return ParseOneOfOption(args, s, [&](auto& arg_b) { arg_b.AppendOneOf( { arg_b.OneOfValue("x", 1), arg_b.OneOfValue("y", 2), arg_b.OneOfValue("z", 3), }, &values); }); }; EXPECT_THAT(parse({}, llvm::errs()), IsSuccess(Eq(ParseResult::Success))); EXPECT_THAT(values, ElementsAre()); EXPECT_THAT(parse({"--option=x", "--option=y", "--option=z"}, llvm::errs()), IsSuccess(Eq(ParseResult::Success))); EXPECT_THAT(values, ElementsAre(Eq(1), Eq(2), Eq(3))); EXPECT_THAT(parse({"--option=y", "--option=y", "--option=x"}, llvm::errs()), IsSuccess(Eq(ParseResult::Success))); EXPECT_THAT(values, ElementsAre(Eq(1), Eq(2), Eq(3), Eq(2), Eq(2), Eq(1))); } TEST(ArgParserTest, RequiredArgs) { int integer_option; llvm::StringRef string_option; TestSubcommand subcommand; int integer_sub_option; llvm::StringRef string_sub_option; TestEnum enum_sub_option; auto parse = [&](llvm::ArrayRef args, llvm::raw_ostream& s) { return Parse(args, s, TestCommandInfo, [&](auto& b) { b.AddIntegerOption({.name = "opt1"}, [&](auto& arg_b) { arg_b.Required(true); arg_b.Set(&integer_option); }); b.AddStringOption({.name = "opt2"}, [&](auto& arg_b) { arg_b.Required(true); arg_b.Set(&string_option); }); b.AddSubcommand({.name = "sub1"}, [&](auto& sub_b) { sub_b.AddIntegerOption({.name = "sub-opt1"}, [&](auto& arg_b) { arg_b.Required(true); arg_b.Set(&integer_sub_option); }); sub_b.AddStringOption({.name = "sub-opt2"}, [&](auto& arg_b) { arg_b.Required(true); arg_b.Set(&string_sub_option); }); sub_b.Do([&] { subcommand = TestSubcommand::Sub1; }); }); b.AddSubcommand({.name = "sub2"}, [&](auto& sub_b) { sub_b.AddIntegerOption({.name = "sub-opt1"}, [&](auto& arg_b) { arg_b.Required(true); arg_b.Set(&integer_sub_option); }); sub_b.AddStringOption({.name = "sub-opt2"}, [&](auto& arg_b) { arg_b.Required(true); arg_b.Set(&string_sub_option); }); sub_b.AddOneOfOption({.name = "sub-opt3"}, [&](auto& arg_b) { arg_b.Required(true); arg_b.SetOneOf( { arg_b.OneOfValue("a", TestEnum::Val1), arg_b.OneOfValue("b", TestEnum::Val2), }, &enum_sub_option); }); sub_b.Do([&] { subcommand = TestSubcommand::Sub2; }); }); b.Do([] {}); }); }; RawStringOstream os; EXPECT_THAT(parse({}, os), IsError(StrEq("required options not provided: --opt1, --opt2"))); EXPECT_THAT(parse({"--opt2=xyz"}, os), IsError(StrEq("required options not provided: --opt1"))); EXPECT_THAT(parse({"--opt2=xyz", "--opt1=42"}, llvm::errs()), IsSuccess(Eq(ParseResult::Success))); EXPECT_THAT(integer_option, Eq(42)); EXPECT_THAT(string_option, StrEq("xyz")); EXPECT_THAT(parse({"--opt2=xyz", "--opt1=42", "sub2"}, os), IsError(StrEq("required options not provided: --sub-opt1, " "--sub-opt2, --sub-opt3"))); EXPECT_THAT( parse({"--opt2=xyz", "--opt1=42", "sub2", "--sub-opt3=b"}, os), IsError(StrEq("required options not provided: --sub-opt1, --sub-opt2"))); EXPECT_THAT(parse({"--opt2=xyz", "--opt1=42", "sub2", "--sub-opt3=b", "--sub-opt1=13"}, os), IsError(StrEq("required options not provided: --sub-opt2"))); EXPECT_THAT(parse({"--opt2=xyz", "--opt1=42", "sub2", "--sub-opt3=b", "--sub-opt1=13", "--sub-opt2=abc"}, llvm::errs()), IsSuccess(Eq(ParseResult::Success))); EXPECT_THAT(integer_option, Eq(42)); EXPECT_THAT(string_option, StrEq("xyz")); EXPECT_THAT(subcommand, Eq(TestSubcommand::Sub2)); EXPECT_THAT(integer_sub_option, Eq(13)); EXPECT_THAT(string_sub_option, StrEq("abc")); EXPECT_THAT(enum_sub_option, Eq(TestEnum::Val2)); } TEST(ArgParserTest, HelpAndVersion) { bool flag = false; int storage = -1; llvm::StringRef string = ""; auto parse = [&](llvm::ArrayRef args, llvm::raw_ostream& s) { return Parse( // Force line break. args, s, { .name = "test", .version = "Test Command -- version 1.2.3", .build_info = R"""( Build timestamp: )""" __TIMESTAMP__ R"""( Build config: test-config-info )""", .help = R"""( A test command. Lots more information about the test command. )""", .help_epilogue = R"""( Closing remarks. )""", }, [&](auto& b) { b.AddFlag( { .name = "flag", .short_name = "f", .help = R"""( A boolean flag. )""", }, [&](auto& arg_b) { arg_b.Set(&flag); }); b.AddFlag( { .name = "hidden_flag", .help = R"""( A *hidden* boolean flag. )""", }, [&](auto& arg_b) { arg_b.HelpHidden(true); arg_b.Set(&flag); }); b.AddSubcommand( { .name = "edit", .help = R"""( Edit the widget. This will take the provided widgets and edit them. )""", .help_epilogue = R"""( That's all. )""", }, [&](auto& sub_b) { sub_b.AddIntegerOption( { .name = "option", .short_name = "o", .help = R"""( An integer option. )""", }, [&](auto& arg_b) { arg_b.Set(&storage); }); sub_b.Do([] {}); }); b.AddSubcommand( { .name = "run", .help = R"""( Run wombats across the screen. This will cause several wombats to run across your screen. )""", .help_epilogue = R"""( Or it won't, who knows. )""", }, [&](auto& sub_b) { sub_b.AddStringOption( { .name = "option", .short_name = "o", .help = R"""( A string option. )""", }, [&](auto& arg_b) { arg_b.Set(&string); }); sub_b.AddOneOfOption( { .name = "one-of-option", .help = R"""( A one-of option. )""", }, [&](auto& arg_b) { arg_b.SetOneOf( { arg_b.OneOfValue("x", 1), arg_b.OneOfValue("y", 2), arg_b.OneOfValue("z", 3), }, &storage); }); sub_b.Do([] {}); }); b.AddSubcommand( { .name = "hidden", .help = R"""( A hidden subcommand. )""", }, [&](auto& sub_b) { sub_b.HelpHidden(true); sub_b.Do([] {}); }); b.RequiresSubcommand(); }); }; RawStringOstream os; EXPECT_THAT(parse({"--flag", "--help"}, os), IsSuccess(Eq(ParseResult::MetaSuccess))); std::string help_flag_output = os.TakeStr(); EXPECT_THAT(help_flag_output, StrEq(llvm::StringRef(R"""( Test Command -- version 1.2.3 A test command. Lots more information about the test command. Build info: Build timestamp: )""" __TIMESTAMP__ R"""( Build config: test-config-info Usage: test [-f] edit [--option=...] test [-f] run [OPTIONS] Subcommands: edit Edit the widget. This will take the provided widgets and edit them. run Run wombats across the screen. This will cause several wombats to run across your screen. help Prints help information for the command, including a description, command line usage, and details of each subcommand and option that can be provided. version Prints the version of this command. Command options: -f, --flag A boolean flag. Closing remarks. )""") .ltrim('\n'))); EXPECT_THAT(parse({"help"}, os), IsSuccess(Eq(ParseResult::MetaSuccess))); EXPECT_THAT(os.TakeStr(), StrEq(help_flag_output)); EXPECT_THAT(parse({"--version"}, os), IsSuccess(Eq(ParseResult::MetaSuccess))); std::string version_flag_output = os.TakeStr(); EXPECT_THAT(version_flag_output, StrEq(llvm::StringRef(R"""( Test Command -- version 1.2.3 Build timestamp: )""" __TIMESTAMP__ R"""( Build config: test-config-info )""") .ltrim('\n'))); EXPECT_THAT(parse({"version"}, os), IsSuccess(Eq(ParseResult::MetaSuccess))); EXPECT_THAT(os.TakeStr(), StrEq(version_flag_output)); EXPECT_THAT(parse({"--flag", "edit", "--option=42", "--help"}, os), IsSuccess(Eq(ParseResult::MetaSuccess))); std::string edit_help_output = os.TakeStr(); EXPECT_THAT(edit_help_output, StrEq(llvm::StringRef(R"""( Edit the widget. This will take the provided widgets and edit them. Subcommand `edit` usage: test [-f] edit [--option=...] Subcommand `edit` options: -o, --option=... An integer option. --help[=(full|short)] Prints help information for the subcommand, including a description, command line usage, and details of each option that can be provided. Possible values: - full (default) - short That's all. )""") .ltrim('\n'))); EXPECT_THAT(parse({"help", "edit"}, os), IsSuccess(Eq(ParseResult::MetaSuccess))); EXPECT_THAT(os.TakeStr(), StrEq(edit_help_output)); EXPECT_THAT(parse({"--flag", "run", "--option=abc", "--help"}, os), IsSuccess(Eq(ParseResult::MetaSuccess))); std::string run_help_output = os.TakeStr(); EXPECT_THAT(run_help_output, StrEq(llvm::StringRef(R"""( Run wombats across the screen. This will cause several wombats to run across your screen. Subcommand `run` usage: test [-f] run [OPTIONS] Subcommand `run` options: -o, --option=... A string option. --one-of-option=(x|y|z) A one-of option. Possible values: - x - y - z --help[=(full|short)] Prints help information for the subcommand, including a description, command line usage, and details of each option that can be provided. Possible values: - full (default) - short Or it won't, who knows. )""") .ltrim('\n'))); EXPECT_THAT(parse({"help", "run"}, os), IsSuccess(Eq(ParseResult::MetaSuccess))); EXPECT_THAT(os.TakeStr(), StrEq(run_help_output)); } TEST(ArgParserTest, HelpMarkdownLike) { bool flag = false; auto parse = [&](llvm::ArrayRef args, llvm::raw_ostream& s) { return Parse( // Force line break. args, s, {.name = "test"}, [&](auto& b) { b.AddFlag( { .name = "flag", .help = R"""( A boolean flag. Preformatted code.... ........ But here lines are collapsed. ``` x y z ``` )""", }, [&](auto& arg_b) { arg_b.Set(&flag); }); b.Do([] {}); }); }; RawStringOstream os; EXPECT_THAT(parse({"--help"}, os), IsSuccess(Eq(ParseResult::MetaSuccess))); EXPECT_THAT(os.TakeStr(), StrEq(llvm::StringRef(R"""( Usage: test [--flag] Options: --flag A boolean flag. Preformatted code.... ........ But here lines are collapsed. ``` x y z ``` --help[=(full|short)] Prints help information for the command, including a description, command line usage, and details of each option that can be provided. Possible values: - full (default) - short )""") .ltrim('\n'))); } } // namespace } // namespace Carbon::CommandLine ================================================ FILE: common/concepts.h ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #ifndef CARBON_COMMON_CONCEPTS_H_ #define CARBON_COMMON_CONCEPTS_H_ #include namespace Carbon { // True if `T` is the same as one of `OtherT`. template concept SameAsOneOf = (std::same_as || ...); } // namespace Carbon #endif // CARBON_COMMON_CONCEPTS_H_ ================================================ FILE: common/emplace_by_calling.h ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #ifndef CARBON_COMMON_EMPLACE_BY_CALLING_H_ #define CARBON_COMMON_EMPLACE_BY_CALLING_H_ #include #include namespace Carbon { // A utility to use when calling an `emplace` function to emplace the result of // a function call. Expected usage is: // // my_widget_vec.emplace_back(EmplaceByCalling([&] { // return ConstructAWidget(...); // })); // // In this example, the result of `ConstructAWidget` will be constructed // directly into the new element of `my_widget_vec`, without performing a copy // or move. // // Note that the type of the argument to `emplace_back` is an `EmplaceByCalling` // instance, not the type `DestT` stored in the container. When the `DestT` // instance is eventually initialized directly from the `EmplaceByCalling`, a // conversion function on `EmplaceByCalling` is used that converts to the type // `DestT` being emplaced. This `DestT` initialization does not call an // additional `DestT` copy or move constructor to initialize the result, and // instead initializes it in-place in the container's storage, per the C++17 // guaranteed copy elision rules. Similarly, within the conversion function, the // result is initialized directly by calling `make_fn`, again relying on // guaranteed copy elision. // // Because the make function is called from the conversion function, // `EmplaceByCalling` should only be used in contexts where it will be used to // initialize a `DestT` object exactly once. This is generally true of `emplace` // functions. Also, because the `make_fn` callback will be called after the // container has made space for the new element, it should not inspect or modify // the container that is being emplaced into. template class EmplaceByCalling { public: explicit(false) EmplaceByCalling(MakeFnT make_fn) : make_fn_(std::move(make_fn)) {} // Convert to the exact return type of the make function, by calling the make // function to construct the result. No implicit conversions are permitted // here, as that would mean we are not constructing the result in place. template requires std::same_as> // NOLINTNEXTLINE(google-explicit-constructor) explicit(false) operator DestT() && { return std::move(make_fn_)(); } private: MakeFnT make_fn_; }; template EmplaceByCalling(MakeFnT) -> EmplaceByCalling; } // namespace Carbon #endif // CARBON_COMMON_EMPLACE_BY_CALLING_H_ ================================================ FILE: common/emplace_by_calling_test.cpp ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #include "common/emplace_by_calling.h" #include #include namespace Carbon { namespace { struct NoncopyableType { NoncopyableType() = default; NoncopyableType(const NoncopyableType&) = delete; auto operator=(const NoncopyableType&) -> NoncopyableType& = delete; }; auto Make() -> NoncopyableType { return NoncopyableType(); } TEST(EmplaceByCalling, Noncopyable) { std::list list; // This should compile. list.emplace_back(EmplaceByCalling(Make)); } TEST(EmplaceByCalling, NoncopyableInAggregate) { struct Aggregate { int a, b, c; NoncopyableType noncopyable; }; std::list list; // This should compile. list.emplace_back(EmplaceByCalling( [] { return Aggregate{.a = 1, .b = 2, .c = 3, .noncopyable = Make()}; })); } class CopyCounter { public: explicit CopyCounter(int* counter) : counter_(counter) {} CopyCounter(const CopyCounter& other) : counter_(other.counter_) { ++*counter_; } private: int* counter_; }; TEST(EmplaceByCalling, NoCopies) { std::vector vec; vec.reserve(10); int copies = 0; for (int i = 0; i != 10; ++i) { vec.emplace_back(EmplaceByCalling([&] { return CopyCounter(&copies); })); } EXPECT_EQ(0, copies); } } // namespace } // namespace Carbon ================================================ FILE: common/enum_base.h ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #ifndef CARBON_COMMON_ENUM_BASE_H_ #define CARBON_COMMON_ENUM_BASE_H_ #include #include #include "common/ostream.h" #include "llvm/ADT/StringRef.h" namespace Carbon::Internal { // CRTP-style base class used to define the common pattern of Carbon enum-like // classes. The result is a class with named constants similar to enumerators, // but that are normal classes, can contain other methods, and support a `name` // method and printing the enums. These even work in switch statements and // support `case MyEnum::Name:`. // // It is specifically designed to compose with X-MACRO style `.def` files that // stamp out all the enumerators. // // Users must be in the `Carbon` namespace and should look like the following. // // In `my_kind.h`: // ``` // CARBON_DEFINE_RAW_ENUM_CLASS(MyKind, uint8_t) { // #define CARBON_MY_KIND(Name) CARBON_RAW_ENUM_ENUMERATOR(Name) // #include ".../my_kind.def" // }; // // class MyKind : public CARBON_ENUM_BASE(MyKind) { // public: // #define CARBON_MY_KIND(Name) CARBON_ENUM_CONSTANT_DECL(Name) // #include ".../my_kind.def" // // // OPTIONAL: To support converting to and from the underlying type of // // the enumerator, add these lines: // using EnumBase::AsInt; // using EnumBase::FromInt; // // // OPTIONAL: To expose the ability to create an instance from the raw // // enumerator (for unusual use cases), add this: // using EnumBase::Make; // // // Plus, anything else you wish to include. // }; // // #define CARBON_MY_KIND(Name) CARBON_ENUM_CONSTANT_DEFINITION(MyKind, Name) // #include ".../my_kind.def" // ``` // // In `my_kind.cpp`: // ``` // CARBON_DEFINE_ENUM_CLASS_NAMES(MyKind) { // #define CARBON_MY_KIND(Name) CARBON_ENUM_CLASS_NAME_STRING(Name) // #include ".../my_kind.def" // }; // ``` // // The result of the above: // - An enum class (`RawEnumType`) defined in an `Internal` namespace with one // enumerator per call to CARBON_MY_KIND(Name) in `.../my_kind.def`, with name // `Name`. This won't generally be used directly, but may be needed for niche // use cases such as a template argument. // - A type `MyKind` that extends `Carbon::Internal::EnumBase`. // - `MyKind` includes all the public members of `EnumBase`, like `name` and // `Print`. For example, you might call `name()` to construct an error // message: // ``` // auto ErrorMessage(MyKind k) -> std::string { // return k.name() + " not found"; // } // ``` // - `MyKind` includes all protected members of `EnumBase`, like `AsInt`, // `FromInt`. They will be part of the public API of `EnumBase` if they // were included in a `using` declaration. // - `MyKind` includes a member `static const MyKind Name;` per call to // `CARBON_MY_KIND(Name)` in `.../my_kind.def`. It will have the // corresponding value from `RawEnumType`. This is the primary way to create // an instance of `MyKind`. For example, it might be used like: // ``` // ErrorMessage(MyKind::Name1); // ``` // - `MyKind` includes an implicit conversion to the `RawEnumType`, returning // the value of a private field in `EnumBase`. This is used when writing a // `switch` statement, as in this example: // ``` // auto MyFunction(MyKind k) -> void { // // Implicitly converts `k` and every `case` expression to // // `RawEnumType`: // switch (k) { // case MyKind::Name1: // // ... // break; // // case MyKind::Name2: // // ... // break; // // // No `default` case needed if the above cases are exhaustive. // // Prefer no `default` case when possible, to get an error if // // a case is skipped. // } // } // ``` // template class EnumBase : public Printable { public: // An alias for the raw enum type. This is an implementation detail and // should rarely be used directly, only when an actual enum type is needed. using RawEnumType = EnumT; using EnumType = DerivedT; using UnderlyingType = std::underlying_type_t; // Enable conversion to the raw enum type, including in a `constexpr` context, // to enable comparisons and usage in `switch` and `case`. The enum type // remains an implementation detail and nothing else should be using this // function. // // NOLINTNEXTLINE(google-explicit-constructor) explicit(false) constexpr operator RawEnumType() const { return value_; } // Conversion to bool is deleted to prevent direct use in an `if` condition // instead of comparing with another value. explicit operator bool() const = delete; // Returns the name of this value. auto name() const -> llvm::StringRef { return Names[AsInt()]; } // Prints this value using its name. auto Print(llvm::raw_ostream& out) const -> void { out << name(); } // Don't support comparison of enums by default. friend auto operator<(DerivedT lhs, DerivedT rhs) -> bool = delete; friend auto operator<=(DerivedT lhs, DerivedT rhs) -> bool = delete; friend auto operator>(DerivedT lhs, DerivedT rhs) -> bool = delete; friend auto operator>=(DerivedT lhs, DerivedT rhs) -> bool = delete; friend auto operator<=>(DerivedT lhs, DerivedT rhs) -> std::partial_ordering = delete; protected: // The default constructor is explicitly defaulted (and constexpr) as a // protected constructor to allow derived classes to be constructed but not // the base itself. This should only be used in the `Make` function below. constexpr EnumBase() = default; // Create an instance from the raw enumerator. Mainly used internally, but may // be exposed for unusual use cases. static constexpr auto Make(RawEnumType value) -> EnumType { EnumType result; result.value_ = value; return result; } // Convert to the underlying integer type. Derived types can choose to expose // this as part of their API. constexpr auto AsInt() const -> UnderlyingType { return static_cast(value_); } // Convert from the underlying integer type. Derived types can choose to // expose this as part of their API. static constexpr auto FromInt(UnderlyingType value) -> EnumType { return Make(static_cast(value)); } private: template friend class EnumMaskBase; RawEnumType value_; }; } // namespace Carbon::Internal // Use this before defining a class that derives from `EnumBase` to begin the // definition of the raw `enum class`. It should be followed by the body of that // raw enum class. #define CARBON_DEFINE_RAW_ENUM_CLASS(EnumClassName, UnderlyingType) \ namespace Internal { \ struct EnumClassName##Data { \ static const llvm::StringLiteral Names[]; \ enum class RawEnum : UnderlyingType; \ }; \ } \ enum class Internal::EnumClassName##Data::RawEnum : UnderlyingType // In the `CARBON_DEFINE_RAW_ENUM_CLASS` block, use this to generate each // enumerator. #define CARBON_RAW_ENUM_ENUMERATOR(Name) Name, // Use this to compute the `Internal::EnumBase` specialization for a Carbon enum // class. It both computes the name of the raw enum and ensures all the // namespaces are correct. #define CARBON_ENUM_BASE(EnumClassName) \ ::Carbon::Internal::EnumBase // Use this within the Carbon enum class body to generate named constant // declarations for each value. #define CARBON_ENUM_CONSTANT_DECL(Name) static const EnumType Name; // Use this immediately after the Carbon enum class body to define each named // constant. #define CARBON_ENUM_CONSTANT_DEFINITION(EnumClassName, Name) \ inline constexpr EnumClassName EnumClassName::Name = \ EnumClassName::Make(RawEnumType::Name); // Use this in the `.cpp` file for an enum class to start the definition of the // constant names array for each enumerator. It is followed by the desired // constant initializer. // // `clang-format` has a bug with spacing around `->` returns in macros. See // https://bugs.llvm.org/show_bug.cgi?id=48320 for details. #define CARBON_DEFINE_ENUM_CLASS_NAMES(EnumClassName) \ constexpr llvm::StringLiteral Internal::EnumClassName##Data::Names[] = // Use this within the names array initializer to generate a string for each // name. #define CARBON_ENUM_CLASS_NAME_STRING(Name) #Name, #endif // CARBON_COMMON_ENUM_BASE_H_ ================================================ FILE: common/enum_base_test.cpp ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #include "common/enum_base.h" #include #include "common/raw_string_ostream.h" namespace Carbon { namespace { // These are directly in the Carbon namespace because the defines require it. CARBON_DEFINE_RAW_ENUM_CLASS(TestKind, uint8_t) { #define CARBON_ENUM_BASE_TEST_KIND(Name) CARBON_RAW_ENUM_ENUMERATOR(Name) #include "common/enum_base_test.def" }; class TestKind : public CARBON_ENUM_BASE(TestKind) { public: #define CARBON_ENUM_BASE_TEST_KIND(Name) CARBON_ENUM_CONSTANT_DECL(Name) #include "common/enum_base_test.def" using EnumBase::AsInt; using EnumBase::FromInt; }; #define CARBON_ENUM_BASE_TEST_KIND(Name) \ CARBON_ENUM_CONSTANT_DEFINITION(TestKind, Name) #include "common/enum_base_test.def" CARBON_DEFINE_ENUM_CLASS_NAMES(TestKind) { #define CARBON_ENUM_BASE_TEST_KIND(Name) CARBON_ENUM_CLASS_NAME_STRING(Name) #include "common/enum_base_test.def" }; static_assert(sizeof(TestKind) == sizeof(uint8_t), "Class size doesn't match enum size!"); TEST(EnumBaseTest, NamesAndConstants) { EXPECT_EQ("Beep", TestKind::Beep.name()); EXPECT_EQ("Boop", TestKind::Boop.name()); EXPECT_EQ("Burr", TestKind::Burr.name()); } TEST(EnumBaseTest, Printing) { RawStringOstream stream; TestKind kind = TestKind::Beep; stream << kind << " " << TestKind::Beep; kind = TestKind::Boop; stream << " " << kind; // Check the streamed results and also make sure we can stream into GoogleTest // assertions. EXPECT_EQ("Beep Beep Boop", stream.TakeStr()) << "Final kind: " << kind; } TEST(EnumBaseTest, Switch) { TestKind kind = TestKind::Boop; switch (kind) { case TestKind::Beep: { FAIL() << "Beep case selected!"; break; } case TestKind::Boop: { EXPECT_EQ("Boop", kind.name()); break; } case TestKind::Burr: { FAIL() << "Burr case selected!"; break; } } } TEST(EnumBaseTest, Comparison) { TestKind kind = TestKind::Beep; // Make sure all the different comparisons work, and also work with // GoogleTest expectations. EXPECT_EQ(TestKind::Beep, kind); EXPECT_NE(TestKind::Boop, kind); // These should also all be constexpr. constexpr TestKind Kind2 = TestKind::Beep; static_assert(Kind2 == TestKind::Beep); static_assert(Kind2 != TestKind::Boop); } TEST(EnumBaseTest, IntConversion) { EXPECT_EQ(0, TestKind::Beep.AsInt()); EXPECT_EQ(1, TestKind::Boop.AsInt()); EXPECT_EQ(2, TestKind::Burr.AsInt()); EXPECT_EQ(TestKind::Beep, TestKind::FromInt(0)); EXPECT_EQ(TestKind::Boop, TestKind::FromInt(1)); EXPECT_EQ(TestKind::Burr, TestKind::FromInt(2)); } } // namespace } // namespace Carbon ================================================ FILE: common/enum_base_test.def ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // Note that this is an X-macro header. It does not use `#include` guards, and // instead is designed to be `#include`ed after the x-macro is defined in order // for its inclusion to expand to the desired output. #ifndef CARBON_ENUM_BASE_TEST_KIND #error "Must define the x-macro to use this file." #define CARBON_ENUM_BASE_TEST_KIND(Name) #endif CARBON_ENUM_BASE_TEST_KIND(Beep) CARBON_ENUM_BASE_TEST_KIND(Boop) CARBON_ENUM_BASE_TEST_KIND(Burr) #undef CARBON_ENUM_BASE_TEST_KIND ================================================ FILE: common/enum_mask_base.h ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #ifndef CARBON_COMMON_ENUM_MASK_BASE_H_ #define CARBON_COMMON_ENUM_MASK_BASE_H_ #include #include "common/enum_base.h" #include "llvm/ADT/StringExtras.h" namespace Carbon::Internal { // CRTP-style base class similar to `EnumBase`, but supporting mask enums. // Enumerator values are consecutive bit shifts (1 << 0, 1 << 1, 1 << 2, 1 << 3, // ...). // // Users must be in the `Carbon` namespace and should look like the following. // // In `my_kind.h`: // ``` // #define CARBON_MY_KIND(X) \ // X(Enumerator1) \ // X(Enumerator2) \ // X(Enumerator3) \ // ... // // CARBON_DEFINE_RAW_ENUM_MASK(MyKind, uint32_t) { // CARBON_MY_KIND(CARBON_RAW_ENUM_MASK_ENUMERATOR) // }; // // class MyKind : public CARBON_ENUM_MASK_BASE(MyKind) { // public: // CARBON_MY_KIND(CARBON_ENUM_MASK_CONSTANT_DECL) // // // Plus, anything else you wish to include. // }; // // #define CARBON_MY_KIND_WITH_TYPE(X) \ // CARBON_ENUM_MASK_CONSTANT_DEFINITION(MyKind, X) // CARBON_MY_KIND(CARBON_MY_KIND_WITH_TYPE) // #undef CARBON_MY_KIND_WITH_TYPE // ``` // // In `my_kind.cpp`: // ``` // CARBON_DEFINE_ENUM_MASK_NAMES(MyKind) { // CARBON_MY_KIND(CARBON_ENUM_MASK_NAME_STRING) // }; // ``` template class EnumMaskBase : public EnumBase { public: // Provide a standard `None`. // // This uses a `&` to trigger slightly different instantiation behaviors in // Clang. For context on why this is needed, see http://wg21.link/CWG2800. // NOLINTNEXTLINE(readability-identifier-naming) static const DerivedT& None; // Returns true if there's a non-empty set intersection. constexpr auto HasAnyOf(DerivedT other) const -> bool { return !(*this & other).empty(); } // Adds entries to the mask. auto Add(DerivedT other) -> void { *this = *this | other; } // Removes entries from the mask. auto Remove(DerivedT other) -> void { *this = *this & ~other; } constexpr auto empty() const -> bool { return this->AsInt() == 0; } constexpr auto operator|(DerivedT other) const -> DerivedT { return DerivedT::FromInt(this->AsInt() | other.AsInt()); } constexpr auto operator&(DerivedT other) const -> DerivedT { return DerivedT::FromInt(this->AsInt() & other.AsInt()); } constexpr auto operator~() const -> DerivedT { return DerivedT::FromInt(~this->AsInt()); } // Use `Print` for mask entries. This hides `EnumBase::name`; it's not // compatible with `EnumMaskBase`. auto name() const -> llvm::StringRef = delete; // Prints this value as a `|`-separated list of mask entries, or `None`. // // This shadows EnumBase::Print. auto Print(llvm::raw_ostream& out) const -> void { int value = this->AsInt(); if (value == 0) { out << "None"; return; } llvm::ListSeparator sep("|"); for (int bit = 0; value != 0; value >>= 1, ++bit) { if (value & 1) { out << sep << Names[bit]; } } } }; template constexpr const DerivedT& EnumMaskBase::None = DerivedT::FromInt(0); } // namespace Carbon::Internal // Use this before defining a class that derives from `EnumMaskBase` to begin // the definition of the raw `enum class`. It should be followed by the body of // that raw enum class. #define CARBON_DEFINE_RAW_ENUM_MASK(EnumMaskName, UnderlyingType) \ namespace Internal { \ struct EnumMaskName##Data { \ static const llvm::StringLiteral Names[]; \ /* For bit shifts, track the initial counter value. This will increment on \ * each enum entry. */ \ static constexpr uint64_t BitShiftCounter = __COUNTER__ + 1; \ enum class RawEnum : UnderlyingType; \ }; \ } \ enum class Internal::EnumMaskName##Data::RawEnum : UnderlyingType // In the `CARBON_DEFINE_RAW_ENUM_MASK` block, use this to generate each // enumerator. #define CARBON_RAW_ENUM_MASK_ENUMERATOR(Name) \ Name = 1 << (__COUNTER__ - BitShiftCounter), // Use this to compute the `Internal::EnumMaskBase` specialization for a Carbon // enum mask. It both computes the name of the raw enum and ensures all the // namespaces are correct. #define CARBON_ENUM_MASK_BASE(EnumMaskName) \ ::Carbon::Internal::EnumMaskBase // Constants and names are declared equivalently as to `EnumBase`. #define CARBON_ENUM_MASK_CONSTANT_DECL(Name) CARBON_ENUM_CONSTANT_DECL(Name) #define CARBON_ENUM_MASK_CONSTANT_DEFINITION(EnumMaskName, Name) \ CARBON_ENUM_CONSTANT_DEFINITION(EnumMaskName, Name) #define CARBON_DEFINE_ENUM_MASK_NAMES(EnumMaskName) \ CARBON_DEFINE_ENUM_CLASS_NAMES(EnumMaskName) #define CARBON_ENUM_MASK_NAME_STRING(Name) CARBON_ENUM_CLASS_NAME_STRING(Name) #endif // CARBON_COMMON_ENUM_MASK_BASE_H_ ================================================ FILE: common/enum_mask_base_test.cpp ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #include "common/enum_mask_base.h" #include #include "common/raw_string_ostream.h" namespace Carbon { namespace { #define CARBON_TEST_KIND(X) \ X(Beep) \ X(Boop) \ X(Burr) CARBON_DEFINE_RAW_ENUM_MASK(TestKind, uint8_t) { CARBON_TEST_KIND(CARBON_RAW_ENUM_MASK_ENUMERATOR) }; class TestKind : public CARBON_ENUM_MASK_BASE(TestKind) { public: CARBON_TEST_KIND(CARBON_ENUM_MASK_CONSTANT_DECL) using EnumMaskBase::AsInt; using EnumMaskBase::FromInt; }; #define CARBON_TEST_KIND_WITH_TYPE(X) \ CARBON_ENUM_MASK_CONSTANT_DEFINITION(TestKind, X) CARBON_TEST_KIND(CARBON_TEST_KIND_WITH_TYPE) #undef CARBON_TEST_KIND_WITH_TYPE CARBON_DEFINE_ENUM_MASK_NAMES(TestKind) { CARBON_TEST_KIND(CARBON_ENUM_MASK_NAME_STRING) }; static_assert(sizeof(TestKind) == sizeof(uint8_t), "Class size doesn't match enum size!"); TEST(EnumMaskBaseTest, Printing) { RawStringOstream stream; TestKind kind = TestKind::Beep; stream << kind; EXPECT_EQ("Beep", stream.TakeStr()); kind = TestKind::Boop; stream << kind; EXPECT_EQ("Boop", stream.TakeStr()); stream << TestKind::Beep; EXPECT_EQ("Beep", stream.TakeStr()); stream << (TestKind::Beep | TestKind::Burr); EXPECT_EQ("Beep|Burr", stream.TakeStr()); } // This just ensures it compiles, it's not validating what's printed. TEST(EnumMaskBaseTest, PrintToGoogletest) { EXPECT_TRUE(true) << TestKind::Beep; } TEST(EnumMaskBaseTest, Switch) { TestKind kind = TestKind::Boop; switch (kind) { case TestKind::Beep: { FAIL() << "Beep case selected!"; break; } case TestKind::Boop: { EXPECT_EQ(kind, TestKind::Boop); break; } case TestKind::Burr: { FAIL() << "Burr case selected!"; break; } } } TEST(EnumMaskBaseTest, Equality) { TestKind kind = TestKind::Beep; // Make sure all the different comparisons work, and also work with // GoogleTest expectations. EXPECT_EQ(TestKind::Beep, kind); EXPECT_NE(TestKind::Boop, kind); // These should also all be constexpr. constexpr TestKind Kind2 = TestKind::Beep; static_assert(Kind2 == TestKind::Beep); static_assert(Kind2 != TestKind::Boop); } TEST(EnumMaskBaseTest, AddRemove) { TestKind kind = TestKind::Beep; EXPECT_EQ(kind, TestKind::Beep); kind.Add(TestKind::Beep); EXPECT_EQ(kind, TestKind::Beep); kind.Add(TestKind::Burr); EXPECT_EQ(kind, TestKind::Beep | TestKind::Burr); kind.Remove(TestKind::Beep); EXPECT_EQ(kind, TestKind::Burr); kind.Remove(TestKind::Beep); EXPECT_EQ(kind, TestKind::Burr); kind.Remove(TestKind::Burr); EXPECT_EQ(kind, TestKind::None); } TEST(EnumMaskBaseTest, HasAnyOf) { static_assert(TestKind::Beep.HasAnyOf(TestKind::Beep)); static_assert(TestKind::Beep.HasAnyOf(TestKind::Beep | TestKind::Burr)); static_assert(!TestKind::Beep.HasAnyOf(TestKind::Burr)); } TEST(EnumMaskBaseTest, MaskOperations) { TestKind kind = TestKind::Beep | (TestKind::Burr & (TestKind::Burr | TestKind::Beep)); EXPECT_EQ(kind, TestKind::Beep | TestKind::Burr); // These should also all be constexpr. static_assert((TestKind::Beep & TestKind::Burr) == TestKind::None); static_assert((TestKind::Beep | TestKind::Burr) != TestKind::None); static_assert(TestKind::Beep == ~~TestKind::Beep); } TEST(EnumMaskBaseTest, IntConversion) { EXPECT_EQ(1, TestKind::Beep.AsInt()); EXPECT_EQ(2, TestKind::Boop.AsInt()); EXPECT_EQ(4, TestKind::Burr.AsInt()); EXPECT_EQ(TestKind::Beep, TestKind::FromInt(1)); EXPECT_EQ(TestKind::Boop, TestKind::FromInt(2)); EXPECT_EQ(TestKind::Burr, TestKind::FromInt(4)); } } // namespace } // namespace Carbon ================================================ FILE: common/error.h ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #ifndef CARBON_COMMON_ERROR_H_ #define CARBON_COMMON_ERROR_H_ #include #include #include #include #include #include #include "common/check.h" #include "common/ostream.h" #include "common/raw_string_ostream.h" #include "llvm/ADT/Twine.h" namespace Carbon { // Success values should be represented as the presence of a value in ErrorOr, // using `ErrorOr` and `return Success();` if no value needs to be // returned. struct Success : public Printable { auto Print(llvm::raw_ostream& out) const -> void { out << "Success"; } }; // Tracks an error message. // // This is nodiscard to enforce error handling prior to destruction. class [[nodiscard]] Error : public Printable { public: // Represents an error state. explicit Error(llvm::Twine message) : message_(message.str()) { CARBON_CHECK(!message_.empty(), "Errors must have a message."); } // Move-only. Error(Error&& other) noexcept = default; auto operator=(Error&& other) noexcept -> Error& = default; // Prints the error string. auto Print(llvm::raw_ostream& out) const -> void { out << message(); } // Returns the error message. auto message() const -> const std::string& { return message_; } private: // The error message. std::string message_; }; // A common base class that custom error types should derive from. // // This combines the ability to be printed with the ability to convert the error // to a string and in turn to a non-customized `Error` type by rendering into a // string. // // The goal is that custom error types can be used for errors that are common // and/or would have cost to fully render the error message to a string. A // custom type can then be used to allow custom, light-weight handling of errors // when appropriate. But to avoid these custom types being excessively viral, we // ensure they can be converted to normal `Error` types when needed by rendering // fully to a string. template class [[nodiscard]] ErrorBase : public Printable { public: ErrorBase(const ErrorBase&) = delete; auto operator=(const ErrorBase&) -> ErrorBase& = delete; auto ToString() const -> std::string { RawStringOstream os; static_cast(this)->Print(os); return os.TakeStr(); } auto ToError() const -> Error { return Error(this->ToString()); } protected: ErrorBase() = default; ErrorBase(ErrorBase&&) noexcept = default; auto operator=(ErrorBase&&) noexcept -> ErrorBase& = default; }; // Holds a value of type `T`, or an `ErrorT` type explaining why the value is // unavailable. // // The `ErrorT` type defaults to `Error` but can be customized where desired // with a type that derives from `ErrorBase` above. See the documentation for // `ErrorBase` to understand the expected contract of custom error types. // // This is nodiscard to enforce error handling prior to destruction. template class [[nodiscard]] ErrorOr { public: using ValueT = std::remove_reference_t; // Check that the custom error type is structured the way we expect. These // need to be `static_assert`s to enable forward declared error types to be // used with `ErrorOr` in function signatures. static_assert(!std::is_reference_v); static_assert(std::same_as || std::derived_from>); // Constructs with an error; the error must not be Error::Success(). // Implicit for easy construction on returns. explicit(false) ErrorOr(ErrorT err) : val_(std::move(err)) {} // Constructs from a custom error type derived from `ErrorBase` into an // `ErrorOr` for `Error` to facilitate returning errors transparently. template requires(std::same_as && std::derived_from>) // Implicit for easy construction on returns. explicit(false) ErrorOr(OtherErrorT other_err) : val_(other_err.ToError()) {} // Constructs with any convertible error type, necessary for return statements // that are already converting to the `ErrorOr` wrapper. // // This supports *explicitly* conversions, not just implicit, which is // important to make common patterns of returning and adjusting the error // type without each error type conversion needing to be implicit. template requires(std::constructible_from && std::derived_from>) // Implicit for easy construction on returns. explicit(false) ErrorOr(OtherErrorT other_err) : val_(std::in_place_type, std::move(other_err)) {} // Constructs with a reference. // Implicit for easy construction on returns. explicit(false) ErrorOr(T ref) requires std::is_reference_v : val_(std::ref(ref)) {} // Constructs with a value. // Implicit for easy construction on returns. explicit(false) ErrorOr(T val) requires(!std::is_reference_v) : val_(std::move(val)) {} // Returns true for success. auto ok() const -> bool { return std::holds_alternative(val_); } // Returns the contained error. // REQUIRES: `ok()` is false. auto error() const& -> const ErrorT& { CARBON_CHECK(!ok()); return std::get(val_); } auto error() && -> ErrorT { CARBON_CHECK(!ok()); return std::get(std::move(val_)); } // Checks that `ok()` is true. // REQUIRES: `ok()` is true. auto Check() const -> void { CARBON_CHECK(ok(), "{0}", error()); } // Returns the contained value. // REQUIRES: `ok()` is true. [[nodiscard]] auto operator*() & -> ValueT& { Check(); return std::get(val_); } // Returns the contained value. // REQUIRES: `ok()` is true. [[nodiscard]] auto operator*() const& -> const ValueT& { Check(); return std::get(val_); } // Returns the contained value. // REQUIRES: `ok()` is true. [[nodiscard]] auto operator*() && -> ValueT&& { Check(); return std::get(std::move(val_)); } // Returns the contained value. // REQUIRES: `ok()` is true. auto operator->() -> ValueT* { return &**this; } // Returns the contained value. // REQUIRES: `ok()` is true. auto operator->() const -> const ValueT* { return &**this; } private: using StoredT = std::conditional_t, std::reference_wrapper, T>; // Either an error message or a value. std::variant val_; }; // A helper class for accumulating error message and converting to // `Error` and `ErrorOr`. class ErrorBuilder { public: explicit ErrorBuilder() : out_(std::make_unique()) {} ErrorBuilder(ErrorBuilder&&) = default; auto operator=(ErrorBuilder&&) -> ErrorBuilder& = default; // Accumulates string message to a temporary `ErrorBuilder`. After streaming, // the builder must be converted to an `Error` or `ErrorOr`. template auto operator<<(T&& message) && -> ErrorBuilder&& { *out_ << message; return std::move(*this); } // Accumulates string message for an lvalue error builder. template auto operator<<(T&& message) & -> ErrorBuilder& { *out_ << message; return *this; } // NOLINTNEXTLINE(google-explicit-constructor): Implicit cast for returns. explicit(false) operator Error() { return Error(out_->TakeStr()); } template // NOLINTNEXTLINE(google-explicit-constructor): Implicit cast for returns. explicit(false) operator ErrorOr() { return Error(out_->TakeStr()); } private: std::unique_ptr out_; }; } // namespace Carbon // Macro hackery to get a unique variable name. #define CARBON_MAKE_UNIQUE_NAME_IMPL(a, b, c) a##b##c #define CARBON_MAKE_UNIQUE_NAME(a, b, c) CARBON_MAKE_UNIQUE_NAME_IMPL(a, b, c) // Macro to prevent a top-level comma from being interpreted as a macro // argument separator. #define CARBON_PROTECT_COMMAS(...) __VA_ARGS__ #define CARBON_RETURN_IF_ERROR_IMPL(unique_name, expr) \ if (auto unique_name = (expr); !(unique_name).ok()) { \ return std::move(unique_name).error(); \ } #define CARBON_RETURN_IF_ERROR(expr) \ CARBON_RETURN_IF_ERROR_IMPL( \ CARBON_MAKE_UNIQUE_NAME(_llvm_error_line, __LINE__, __COUNTER__), \ CARBON_PROTECT_COMMAS(expr)) #define CARBON_ASSIGN_OR_RETURN_IMPL(unique_name, var, expr) \ auto unique_name = (expr); \ if (!(unique_name).ok()) { \ return std::move(unique_name).error(); \ } \ var = std::move(*(unique_name)); #define CARBON_ASSIGN_OR_RETURN(var, expr) \ CARBON_ASSIGN_OR_RETURN_IMPL( \ CARBON_MAKE_UNIQUE_NAME(_llvm_expected_line, __LINE__, __COUNTER__), \ CARBON_PROTECT_COMMAS(var), CARBON_PROTECT_COMMAS(expr)) #endif // CARBON_COMMON_ERROR_H_ ================================================ FILE: common/error_test.cpp ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #include "common/error.h" #include #include #include #include "common/error_test_helpers.h" #include "common/raw_string_ostream.h" namespace Carbon { namespace { using ::Carbon::Testing::IsError; using ::Carbon::Testing::IsSuccess; using ::testing::_; using ::testing::Eq; using ::testing::VariantWith; TEST(ErrorTest, Error) { Error err("test"); EXPECT_EQ(err.message(), "test"); } TEST(ErrorTest, ErrorEmptyString) { ASSERT_DEATH({ Error err(""); }, "CHECK failure at"); } auto IndirectError() -> Error { return Error("test"); } TEST(ErrorTest, IndirectError) { EXPECT_EQ(IndirectError().message(), "test"); } TEST(ErrorTest, ErrorBuilderOperatorImplicitCast) { ErrorOr result = ErrorBuilder() << "msg"; EXPECT_THAT(result, IsError("msg")); } // Make sure a custom error type can be forward declared and used with `ErrorOr` // until the `ErrorOr` is required to be complete itself. class CustomError; auto TestFunction() -> ErrorOr; class CustomError : public ErrorBase { public: auto Print(llvm::raw_ostream& os) const -> void { os << "Custom test error!"; } }; auto TestFunction() -> ErrorOr { return CustomError(); } TEST(ErrorTest, UseErrorOrWithCustomError) { // Uses `TestFunction` to ensure it compiles correctly with forward // declarations above. EXPECT_THAT(TestFunction(), IsError("Custom test error!")); } template class ErrorOrTest : public ::testing::Test { public: auto ErrorStr() -> std::string { if constexpr (std::same_as) { return "test error"; } else if constexpr (std::same_as) { return CustomError().ToString(); } else { static_assert(false, "Unsupported custom error type!"); } } auto MakeError() -> ErrorT { if constexpr (std::same_as) { return Error("test error"); } else if constexpr (std::same_as) { return CustomError(); } else { static_assert(false, "Unsupported custom error type!"); } } }; using ErrorOrTestParams = ::testing::Types; TYPED_TEST_SUITE(ErrorOrTest, ErrorOrTestParams); TYPED_TEST(ErrorOrTest, ErrorOr) { using TestErrorOr = ErrorOr; TestErrorOr err(this->MakeError()); EXPECT_THAT(err, IsError(this->ErrorStr())); } TYPED_TEST(ErrorOrTest, ErrorOrValue) { using TestErrorOr = ErrorOr; EXPECT_TRUE(TestErrorOr(0).ok()); } template auto IndirectErrorOrTest(Fixture& fixture) -> ErrorOr { return fixture.MakeError(); } TYPED_TEST(ErrorOrTest, IndirectErrorOr) { EXPECT_FALSE(IndirectErrorOrTest(*this).ok()); } struct Val { int val; }; TYPED_TEST(ErrorOrTest, ErrorOrArrowOp) { using TestErrorOr = ErrorOr; TestErrorOr err({1}); EXPECT_EQ(err->val, 1); } TYPED_TEST(ErrorOrTest, ErrorOrReference) { using TestErrorOr = ErrorOr; Val val = {1}; TestErrorOr maybe_val(val); EXPECT_EQ(maybe_val->val, 1); } template auto IndirectErrorOrSuccessTest() -> ErrorOr { return Success(); } TYPED_TEST(ErrorOrTest, IndirectErrorOrSuccess) { EXPECT_TRUE(IndirectErrorOrSuccessTest().ok()); } TYPED_TEST(ErrorOrTest, MoveValue) { using TestErrorOr = ErrorOr, TypeParam>; auto make_value = []() -> TestErrorOr { return std::make_unique(42); }; std::unique_ptr p = *make_value(); EXPECT_THAT(*p, Eq(42)); auto result = make_value(); std::unique_ptr p2 = *std::move(result); EXPECT_THAT(*p2, Eq(42)); } TYPED_TEST(ErrorOrTest, UnprintableValue) { struct X { int i; }; using TestErrorOr = ErrorOr; TestErrorOr value(X{.i = 42}); EXPECT_THAT(value, IsSuccess(_)); TestErrorOr error = this->MakeError(); EXPECT_THAT(error, IsError(this->ErrorStr())); } // Note that this is more of a test of `IsSuccess` than `ErrorOr` itself. TYPED_TEST(ErrorOrTest, NestedMatching) { using TestErrorOr = ErrorOr, TypeParam>; TestErrorOr i(42); EXPECT_THAT(i, IsSuccess(VariantWith(Eq(42)))); TestErrorOr f(0.42F); EXPECT_THAT(f, IsSuccess(VariantWith(Eq(0.42F)))); } TYPED_TEST(ErrorOrTest, ReturnIfErrorNoError) { using TestErrorOr = ErrorOr; auto result = []() -> TestErrorOr { CARBON_RETURN_IF_ERROR(TestErrorOr(Success())); CARBON_RETURN_IF_ERROR(TestErrorOr(Success())); return Success(); }(); EXPECT_TRUE(result.ok()); } TYPED_TEST(ErrorOrTest, ReturnIfErrorHasError) { using TestErrorOr = ErrorOr; auto result = [this]() -> TestErrorOr { CARBON_RETURN_IF_ERROR(TestErrorOr(Success())); CARBON_RETURN_IF_ERROR(TestErrorOr(this->MakeError())); return Success(); }(); EXPECT_THAT(result, IsError(this->ErrorStr())); } TYPED_TEST(ErrorOrTest, AssignOrReturnNoError) { using TestErrorOr = ErrorOr; auto result = []() -> TestErrorOr { CARBON_ASSIGN_OR_RETURN(int a, TestErrorOr(1)); CARBON_ASSIGN_OR_RETURN(const int b, TestErrorOr(2)); int c = 0; CARBON_ASSIGN_OR_RETURN(c, TestErrorOr(3)); return a + b + c; }(); EXPECT_THAT(result, IsSuccess(Eq(6))); } TYPED_TEST(ErrorOrTest, AssignOrReturnHasDirectError) { using TestErrorOr = ErrorOr; auto result = [this]() -> TestErrorOr { CARBON_RETURN_IF_ERROR(TestErrorOr(this->MakeError())); return 0; }(); EXPECT_THAT(result, IsError(this->ErrorStr())); } TYPED_TEST(ErrorOrTest, AssignOrReturnHasErrorInExpected) { using TestErrorOr = ErrorOr; auto result = [this]() -> TestErrorOr { CARBON_ASSIGN_OR_RETURN(int a, TestErrorOr(this->MakeError())); return a; }(); EXPECT_THAT(result, IsError(this->ErrorStr())); } class AnotherCustomError : public ErrorBase { public: auto Print(llvm::raw_ostream& os) const -> void { os << "Another custom test error!"; } explicit operator CustomError() { return CustomError(); } }; TYPED_TEST(ErrorOrTest, AssignOrReturnNoErrorAcrossErrorTypes) { using TestErrorOr = ErrorOr; auto result = []() -> ErrorOr { CARBON_ASSIGN_OR_RETURN(int a, TestErrorOr(1)); CARBON_ASSIGN_OR_RETURN(const int b, []() -> TestErrorOr { CARBON_ASSIGN_OR_RETURN(int inner, (ErrorOr(2))); return inner; }()); int c = 0; CARBON_ASSIGN_OR_RETURN(c, TestErrorOr(3)); return a + b + c; }(); EXPECT_THAT(result, IsSuccess(Eq(6))); } TYPED_TEST(ErrorOrTest, AssignOrReturnErrorAcrossErrorTypes) { using TestErrorOr = ErrorOr; auto result = []() -> ErrorOr { CARBON_ASSIGN_OR_RETURN(int a, TestErrorOr(1)); CARBON_ASSIGN_OR_RETURN(const int b, []() -> TestErrorOr { CARBON_ASSIGN_OR_RETURN( int inner, (ErrorOr(AnotherCustomError()))); return inner; }()); int c = 0; CARBON_ASSIGN_OR_RETURN(c, TestErrorOr(3)); return a + b + c; }(); // When directly using the `Error` type, the explicit custom type above has // its message preserved. When testing against `CustomError`, that one // overrides the message. if constexpr (std::same_as) { EXPECT_THAT(result, IsError("Another custom test error!")); } else { EXPECT_THAT(result, IsError("Custom test error!")); } } } // namespace } // namespace Carbon ================================================ FILE: common/error_test_helpers.h ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #ifndef CARBON_COMMON_ERROR_TEST_HELPERS_H_ #define CARBON_COMMON_ERROR_TEST_HELPERS_H_ #include #include #include "common/error.h" #include "common/ostream.h" namespace Carbon::Testing { // Matches the message for an error state of `ErrorOr`. For example: // EXPECT_THAT(my_result, IsError(StrEq("error message"))); class IsError { public: // NOLINTNEXTLINE(readability-identifier-naming) using is_gtest_matcher = void; explicit IsError(::testing::Matcher matcher) : matcher_(std::move(matcher)) {} template auto MatchAndExplain(const ErrorOr& result, ::testing::MatchResultListener* listener) const -> bool { if (result.ok()) { *listener << "is a success"; return false; } else { RawStringOstream os; os << result.error(); return matcher_.MatchAndExplain(os.TakeStr(), listener); } } auto DescribeTo(std::ostream* os) const -> void { *os << "is an error and matches "; matcher_.DescribeTo(os); } auto DescribeNegationTo(std::ostream* os) const -> void { *os << "is a success or does not match "; matcher_.DescribeTo(os); } private: ::testing::Matcher matcher_; }; // Implementation of a success matcher for a specific `T` and `ErrorT` in an // `ErrorOr`. Supports a nested matcher for the `T` value. template class IsSuccessMatcherImpl : public ::testing::MatcherInterface&> { public: explicit IsSuccessMatcherImpl(const ::testing::Matcher& matcher) : matcher_(matcher) {} auto MatchAndExplain(const ErrorOr& result, ::testing::MatchResultListener* listener) const -> bool override { if (result.ok()) { return matcher_.MatchAndExplain(*result, listener); } else { *listener << "is an error with `" << result.error() << "`"; return false; } } auto DescribeTo(std::ostream* os) const -> void override { *os << "is a success and matches "; matcher_.DescribeTo(os); } auto DescribeNegationTo(std::ostream* os) const -> void override { *os << "is an error or does not match "; matcher_.DescribeNegationTo(os); } private: ::testing::Matcher matcher_; }; // Polymorphic match implementation for GoogleTest. // // To support matching arbitrary types that `InnerMatcher` can also match, this // itself must match arbitrary types. This is accomplished by not being a // matcher itself, but by being convertible into matchers for any particular // `ErrorOr`. template class IsSuccessMatcher { public: explicit IsSuccessMatcher(InnerMatcher matcher) : matcher_(std::move(matcher)) {} template explicit(false) // NOLINTNEXTLINE(google-explicit-constructor): Required for matcher APIs. operator ::testing::Matcher&>() const { return ::testing::Matcher&>( new IsSuccessMatcherImpl( ::testing::SafeMatcherCast(matcher_))); } private: InnerMatcher matcher_; }; // Returns a matcher the value for a non-error state of `ErrorOr`. // // For example: // EXPECT_THAT(my_result, IsSuccess(Eq(3))); template auto IsSuccess(InnerMatcher matcher) -> IsSuccessMatcher { return IsSuccessMatcher(matcher); } } // namespace Carbon::Testing namespace Carbon { // Supports printing `ErrorOr` to `std::ostream` in tests. template requires(std::same_as || std::derived_from>) auto operator<<(std::ostream& out, const ErrorOr& error_or) -> std::ostream& { if (error_or.ok()) { // Try and print the value, but only if we can find a viable `<<` overload // for the value type. This should ensure that the `formatv` below can // compile cleanly, and avoid erroring when using matchers on `ErrorOr` with // unprintable value types. if constexpr (requires(const T& value) { out << value; }) { out << llvm::formatv("ErrorOr{{.value = `{0}`}}", *error_or); } else { out << "ErrorOr{{.value = ``}}"; } } else { out << llvm::formatv("ErrorOr{{.error = \"{0}\"}}", error_or.error()); } return out; } } // namespace Carbon #endif // CARBON_COMMON_ERROR_TEST_HELPERS_H_ ================================================ FILE: common/exe_path.cpp ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #include "common/exe_path.h" #include #include #include #include "llvm/ADT/StringRef.h" #include "llvm/Support/FileSystem.h" #include "llvm/Support/Program.h" namespace Carbon { // Returns true if a found path resolves to the actual executable path. static auto RealPathMatches(const char* found_path, llvm::StringRef exe_path) -> bool { char* buffer = realpath(found_path, nullptr); if (!buffer) { return false; } bool matches = exe_path == buffer; free(buffer); return matches; } auto FindExecutablePath(const char* argv0) -> std::string { static int static_for_main_addr; // Note this returns the canonical path, dropping symlink information that we // might want. As a consequence, we use it as a last resort. However, it's // also helpful to use to ensure we found the correct tool through other // means. std::string exe_path = llvm::sys::fs::getMainExecutable(argv0, &static_for_main_addr); llvm::StringRef argv0_ref = argv0; // If `argv[0]` is path-like and points at the executable, use the form in // `argv[0]`. if (argv0_ref.contains('/') && RealPathMatches(argv0, exe_path)) { return argv0_ref.str(); } // If we can find `argv[0]` in `$PATH`, use the form from that. // // For example, `llvm-symbolizer` is subprocessed with `argv[0]` that uses // this path. If `LLVM_SYMBOLIZER_PATH` is set to Carbon, but // `llvm-symbolizer` in `$PATH` is a different binary, that can lead to // problems -- which is why we verify the match. if (llvm::ErrorOr path = llvm::sys::findProgramByName(argv0_ref); path && RealPathMatches(path->c_str(), exe_path)) { return std::move(*path); } // As a fallback, use exe_path. return exe_path; } } // namespace Carbon ================================================ FILE: common/exe_path.h ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #ifndef CARBON_COMMON_EXE_PATH_H_ #define CARBON_COMMON_EXE_PATH_H_ #include "llvm/ADT/StringRef.h" namespace Carbon { // Computes the executable path for the given `argv[0]` value from `main`. // `argv0` is required to be null-terminated. // // A simplistic approach -- if the provided string isn't already a valid path, // we look it up in the PATH environment variable. Doesn't resolve any symlinks. // If it doesn't find a value based on `argv[0]`, returns the main executable // path. auto FindExecutablePath(const char* argv0) -> std::string; } // namespace Carbon #endif // CARBON_COMMON_EXE_PATH_H_ ================================================ FILE: common/exe_path_test.cpp ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #include "common/exe_path.h" #include #include #include #include "llvm/ADT/SmallString.h" #include "llvm/Support/FileSystem.h" #include "llvm/Support/Path.h" namespace Carbon { namespace { TEST(ExePath, FailureFallback) { static int static_for_main_addr; std::string running_binary = llvm::sys::fs::getMainExecutable("exe_path_test", &static_for_main_addr); llvm::SmallString<128> path = llvm::StringRef(getenv("TEST_TMPDIR")); llvm::sys::path::append(path, "non_existant_binary"); std::string exe_path = FindExecutablePath(path.c_str()); EXPECT_EQ(running_binary, exe_path); } TEST(ExePath, Symlink) { static int static_for_main_addr; std::string running_binary = llvm::sys::fs::getMainExecutable("exe_path_test", &static_for_main_addr); llvm::SmallString<128> path = llvm::StringRef(getenv("TEST_TMPDIR")); llvm::sys::path::append(path, "test_binary"); std::error_code ec; std::filesystem::create_symlink(running_binary, path.c_str(), ec); ASSERT_TRUE(!ec) << "Error code: " << ec; std::string exe_path = FindExecutablePath(path.c_str()); EXPECT_EQ(path, exe_path); } TEST(ExePath, PathLookup) { // TODO: This is not likely to work well on Windows (outside of WSL). But some // of that may be hidden by Bazel's test environment. Regardless, we should // revisit this when we have good coverage of Windows build with something // appropriate for that platform. std::string exe_path = FindExecutablePath("bash"); EXPECT_NE(exe_path, "bash"); EXPECT_TRUE(llvm::sys::fs::exists(exe_path)); EXPECT_TRUE(llvm::sys::fs::can_execute(exe_path)); } } // namespace } // namespace Carbon ================================================ FILE: common/filesystem.cpp ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #include "common/filesystem.h" #include #include #include #include #include "common/build_data.h" #include "llvm/Support/MathExtras.h" namespace Carbon::Filesystem { // Render an error number from `errno` to the provided stream using the richest // rendering available on the platform. static auto PrintErrorNumber(llvm::raw_ostream& out, int errnum) -> void { #if defined(_GNU_SOURCE) && \ (__GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 32)) // For sufficiently recent glibc versions, use GNU-specific routines to // compute the error name and description. llvm::StringRef name = strerrordesc_np(errnum); llvm::StringRef desc = strerrorname_np(errnum); out << llvm::formatv("{0}: {1}", name, desc); #elif defined(__APPLE__) || defined(_GNU_SOURCE) || defined(_POSIX_SOURCE) // Broadly portable fallback for Unix-like systems. char buffer[4096]; #ifdef _GNU_SOURCE const char* str = strerror_r(errnum, buffer, sizeof(buffer)); // The GNU version doesn't report a meta-error. int meta_error = 0; #else int meta_error = strerror_r(errnum, buffer, sizeof(buffer)); const char* str = buffer; #endif if (meta_error == 0) { out << llvm::formatv("errno {0}: {1}", errnum, llvm::StringRef(str)); } else { out << llvm::formatv( "error number {0}; encountered meta-error number {1} while rendering " "an error message", errnum, meta_error); } #else #error TODO: Implement this for other platforms. #endif } auto FdError::Print(llvm::raw_ostream& out) const -> void { // The `format_` member is a `StringLiteral` that is null terminated, so // `.data()` is safe here. // NOLINTNEXTLINE(bugprone-suspicious-stringview-data-usage) out << llvm::formatv(format_.data(), fd_) << " failed: "; PrintErrorNumber(out, unix_errnum()); } auto PathError::Print(llvm::raw_ostream& out) const -> void { // The `format_` member is a `StringLiteral` that is null terminated, so // `.data()` is safe here. // NOLINTNEXTLINE(bugprone-suspicious-stringview-data-usage) out << llvm::formatv(format_.data(), path_, dir_fd_ == AT_FDCWD ? std::string("AT_FDCWD") : std::to_string(dir_fd_)) << " failed: "; PrintErrorNumber(out, unix_errnum()); } auto Internal::FileRefBase::ReadFileToString() -> ErrorOr { std::string result; // Read a buffer at a time until we reach the end. We use the pipe buffer // length as our max buffer size as it is likely to be small but reasonable // for the OS, and in the case of pipes the same chunking in which the data // will arrive. // // TODO: Replace this with a smaller buffer and using `resize_and_overwrite` // to read into the string in-place for larger strings. Unclear if that will // be any faster, but it will be much more friendly to callers with // constrained stack sizes and use less memory overall. std::byte buffer[PIPE_BUF]; CARBON_RETURN_IF_ERROR(SeekFromBeginning(0)); for (;;) { auto read_result = ReadToBuffer(buffer); if (!read_result.ok()) { return std::move(read_result).error(); } if (read_result->empty()) { // EOF break; } result.append(reinterpret_cast(read_result->data()), read_result->size()); } return result; } auto Internal::FileRefBase::WriteFileFromString(llvm::StringRef str) -> ErrorOr { CARBON_RETURN_IF_ERROR(SeekFromBeginning(0)); auto bytes = llvm::ArrayRef( reinterpret_cast(str.data()), str.size()); while (!bytes.empty()) { auto write_result = WriteFromBuffer(bytes); if (!write_result.ok()) { return std::move(write_result).error(); } bytes = *write_result; } CARBON_RETURN_IF_ERROR(Truncate(str.size())); return Success(); } // A macOS specific sleep routine that builds on more standard utilities. This // is technically a portable implementation so we always compile it but only use // it on macOS where the more efficient direct use of `clock_nanosleep` isn't // available. [[maybe_unused]] static auto SleepMacos(Duration sleep) -> void { TimePoint stop = Clock::now() + sleep; timespec sleep_ts = Internal::DurationToTimespec(sleep); for (;;) { timespec rem_sleep_ts = {}; int result = nanosleep(&sleep_ts, &rem_sleep_ts); if (result == 0) { return; } // Continue sleeping if we get interrupted by a resumable signal. For // everything else report it. if (errno != EINTR) { int errnum = errno; RawStringOstream error_os; PrintErrorNumber(error_os, errnum); CARBON_FATAL("Unexpected error while sleeping: {0}", error_os.TakeStr()); } // Update to the remaining sleep time for the next attempt at sleeping. sleep_ts = rem_sleep_ts; // Also check if the clock has passed our stop time as a fallback to avoid // too much clock skew. if (Clock::now() > stop) { return; } } } static auto Sleep(Duration sleep) -> void { // For every platform but macOS we can sleep directly on an absolute time. #if __APPLE__ // On Apple platforms, dispatch to a specialized routine. SleepMacos(sleep); #else // We use `clock_gettime` instead of the filesystem `Clock` or some other // `std::chrono` clock because we want to use the exact same clock that we'll // use for sleeping below, and we'll need the time in a `timespec` for that // call anyways. We do use a monotonic clock to try and avoid sleeps being // interrupted by clock changes. timespec ts = {}; int result = clock_gettime(CLOCK_MONOTONIC, &ts); CARBON_CHECK(result == 0, "Error getting the time: {0}", strerror(errno)); // Now convert the timespec to a duration that we can safely do arithmetic on. // Since the sleep interval is in nanoseconds it is tempting to directly do // arithmetic here, but this has a subtle pitfall near the boundary between // the nanosecond component and the second component. // // Note that our `Duration` uses `__int128` to avoid worrying about running // out of precision to represent the final deadline. Duration stop_time = std::chrono::seconds(ts.tv_sec); stop_time += std::chrono::nanoseconds(ts.tv_nsec); stop_time += sleep; // Now convert back to timespec. ts = Internal::DurationToTimespec(stop_time); do { result = clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME, &ts, nullptr); // Continue sleeping if we get interrupted by a resumable signal. Because // we're using a monotonic clock and an absolute deadline time we will // eventually progress past that deadline. } while (result != 0 && (errno == EINTR)); if (result != 0) { int errnum = errno; RawStringOstream error_os; PrintErrorNumber(error_os, errnum); CARBON_FATAL("Unexpected error while sleeping: {0}", error_os.TakeStr()); } #endif } auto Internal::FileRefBase::TryLock(FileLock::Kind kind, Duration deadline, Duration poll_interval) -> ErrorOr { CARBON_CHECK(poll_interval <= deadline); if (deadline != Duration(0) && poll_interval == Duration(0)) { // If the caller didn't provide a poll interval but did provide a deadline, // pick a poll interval to roughly be 1/1000th of the deadline but at least // 1 microsecond. We don't support polling faster than 1 microsecond given // how expensive file locking is. poll_interval = std::max(Duration(std::chrono::microseconds(1)), deadline / 1000); } if (deadline != Duration(0)) { CARBON_CHECK( deadline >= std::chrono::microseconds(10), "A deadline for a file lock shorter than 10 microseconds is not " "supported, callers can implement their own polling logic."); CARBON_CHECK(poll_interval >= std::chrono::microseconds(1), "Polling for a file lock faster than every microsecond is not " "supported, callers can implement their own polling logic."); } auto stop = Clock::now() + deadline; for (;;) { int result = flock(fd_, static_cast(kind) | LOCK_NB); if (result == 0) { return FileLock(fd_); } // Return an error if this is something other than blocking for the lock to // be available, or we didn't get a deadline for continuing to try and // acquire the lock, or we've reached our deadline. if (errno != EWOULDBLOCK || deadline == Duration(0) || Clock::now() >= stop) { return FdError(errno, "File::TryLock on '{0}'", fd_); } // The caller requested attempting to wait up to a deadline to acquire the // lock with a specific poll interval. Try to sleep for that poll interval // before trying the lock again. Sleep(poll_interval); } } auto DirRef::AppendEntriesIf( llvm::SmallVectorImpl& entries, llvm::function_refbool> predicate) -> ErrorOr { CARBON_ASSIGN_OR_RETURN(Reader reader, Read()); for (const Entry& entry : reader) { llvm::StringRef name = entry.name(); if (name == "." || name == "..") { continue; } if (predicate && !predicate(name)) { continue; } entries.push_back(name.str()); } return Success(); } auto DirRef::AppendEntriesIf( llvm::SmallVectorImpl& dir_entries, llvm::SmallVectorImpl& non_dir_entries, llvm::function_refbool> predicate) -> ErrorOr { CARBON_ASSIGN_OR_RETURN(Reader reader, Read()); for (const Entry& entry : reader) { llvm::StringRef name = entry.name(); if (name == "." || name == "..") { continue; } if (predicate && !predicate(name)) { continue; } std::filesystem::path name_path = name.str(); if (entry.is_known_dir()) { dir_entries.push_back(std::move(name_path)); continue; } if (!entry.is_unknown_type()) { non_dir_entries.push_back(std::move(name_path)); continue; } auto stat_result = Lstat(name_path); if (!stat_result.ok()) { return FdError(stat_result.error().unix_errnum(), "Dir::AppendEntriesIf on '{0}' failed while stat-ing " "entries to determine which are directories", dfd_); } if (stat_result->is_dir()) { dir_entries.push_back(std::move(name_path)); } else { non_dir_entries.push_back(std::move(name_path)); } } return Success(); } auto DirRef::OpenDir(const std::filesystem::path& path, CreationOptions creation_options, ModeType creation_mode, OpenFlags open_flags) -> ErrorOr { // If we potentially need to create a directory, we have to do that // separately as no systems support `O_CREAT | O_DIRECTORY`, even though // that would be (much) nicer. if (creation_options == CreateNew) { // If we are required to be the one that created the directory, disable // following the last symlink when we open that directory. The last symlink // is the only one that matters for security here because it is only valid // to create the last component. It is that directory component that we want // to ensure has not been replaced with a symlink by an adversarial // concurrent process. open_flags |= OpenFlags::NoFollow; } if (creation_options != OpenExisting) { CARBON_CHECK(creation_options != CreateAlways, "Invalid `creation_options` value of `CreateAlways`: there is " "no support for truncating directories, and so they cannot be " "created in an analogous way to files if they already exist."); if (mkdirat(dfd_, path.c_str(), creation_mode) != 0) { // Unless the error is just that the path already exists, and that is // allowed for the requested creation flags, report any error here as part // of opening just like we would if the error originated from `openat` // with `O_CREAT`. if (creation_options == CreateNew || errno != EEXIST) { return PathError(errno, "Calling `mkdirat` on '{0}' relative to '{1}' during " "DirRef::OpenDir", path, dfd_); } } } // Open this path as a directory. Note that this has to succeed, and when we // created the directory we require the last component to not be a symlink in // case it was _replaced_ with a symlink while running. int result_fd = openat(dfd_, path.c_str(), static_cast(open_flags) | O_DIRECTORY); if (result_fd == -1) { // No need for `EINTR` handling here as if this is a FIFO it would be an // error with `O_DIRECTORY`. return PathError( errno, "Calling `openat` on '{0}' relative to '{1}' during DirRef::OpenDir", path, dfd_); } Dir result(result_fd); // If we were required to create the directory, we also need to verify that // the opened file descriptor continues to have the same permissions and the // correct owner as we couldn't do the creation atomically with the open. This // defends against an adversarial removal of the created directory and // creation of a new directory with the same name but either with wider // permissions such as all-write, or with a different owner. // // We don't defend against replacement with a directory of the same name, same // permissions, same owner, but different group. There is no good way to do // this defense given the complexity of group assignment, and there appears to // be no need. Achieving such a replacement without superuser power would // require a parent directory with `setgid` bit, and a group that gives the // attacker access -- but such a parent directory would make *any* creation // vulnerable without any need for a replacement, so we can't defend against // that here. The caller has ample tools to defend against this including // taking care with the parent directory and restricting the group permission // bits which we *do* verify. if (creation_options == CreateNew) { auto stat_result = result.Stat(); if (!stat_result.ok()) { // Manually propagate this error so we can attach it back to the opened // path and relative directory. return PathError(stat_result.error().unix_errnum(), "DirRef::Stat after opening '{0}' relative to '{1}'", path, dfd_); } // Check that the owning UID is the current effective UID. if (stat_result->unix_uid() != geteuid()) { // Model this as `EPERM`, which is a bit awkward, but should be fine. return PathError(EPERM, "Unexpected UID change after creating '{0}' relative to " "'{1}' during DirRef::OpenDir", path, dfd_); } // Check that the permissions are a subset of the requested ones. They may // have been masked down by `umask`, but if there are *new* permissions, // that would be a security issue. if ((stat_result->permissions() & creation_mode) != stat_result->permissions()) { // Model this with `EPERM` and a custom message. return PathError(EPERM, "Unexpected permissions after creating '{0}' relative " "to '{1}' during DirRef::OpenDir", path, dfd_); } } return result; } auto DirRef::ReadFileToString(const std::filesystem::path& path) -> ErrorOr { CARBON_ASSIGN_OR_RETURN(ReadFile f, OpenReadOnly(path)); auto result = f.ReadFileToString(); if (result.ok()) { return *std::move(result); } return PathError(result.error().unix_errnum(), "Dir::ReadFileToString on '{0}' relative to '{1}'", path, dfd_); } auto DirRef::WriteFileFromString(const std::filesystem::path& path, llvm::StringRef content, CreationOptions creation_options) -> ErrorOr { CARBON_ASSIGN_OR_RETURN(WriteFile f, OpenWriteOnly(path, creation_options)); auto write_result = f.WriteFileFromString(content); // Immediately close the file as even if there was a write error we don't want // to leave the file open. auto close_result = std::move(f).Close(); // Now report the first error encountered or return success. if (!write_result.ok()) { return PathError( write_result.error().unix_errnum(), "Write error in Dir::WriteFileFromString on '{0}' relative to '{1}'", path, dfd_); } if (!close_result.ok()) { return PathError( close_result.error().unix_errnum(), "Close error in Dir::WriteFileFromString on '{0}' relative to '{1}'", path, dfd_); } return Success(); } auto DirRef::CreateDirectories(const std::filesystem::path& path, ModeType creation_mode) -> ErrorOr { // Avoid having to handle an empty path by immediately rejecting it as // invalid. if (path.empty()) { return PathError(EINVAL, "DirRef::CreateDirectories on '{0}' relative to '{1}'", path, dfd_); } // Try directly opening the directory and use that if successful. This is an // important hot path case of users essentially doing an "open-always" form of // creating multiple steps of directories. auto open_result = OpenDir(path, OpenExisting); if (open_result.ok()) { return std::move(*open_result); } else if (!open_result.error().no_entity()) { return std::move(open_result).error(); } // Walk from the full path towards this directory (or the root) to find the // first existing directory. This is faster than walking down as no file // descriptors have to be allocated for any intervening directories, etc. We // keep the path components that are missing as we pop them off for easy // traversal back down. std::optional work_dir; // Paths typically consist of relatively few components // and so we can use a bit of stack and avoid allocating and moving the paths // in common cases. We use `8` as an arbitrary but likely good for all of the // hottest cases. llvm::SmallVector missing_components; missing_components.push_back(path.filename()); for (std::filesystem::path parent_path = path.parent_path(); !parent_path.empty(); parent_path = parent_path.parent_path()) { auto open_result = OpenDir(parent_path, OpenExisting); if (open_result.ok()) { work_dir = std::move(*open_result); break; } missing_components.push_back(parent_path.filename()); } CARBON_CHECK(!missing_components.empty()); // If we haven't yet opened an intermediate directory, start by creating one // relative to this directory. We can't do this as part of the loop below as // `this` and the newly opened directory have different types. if (!work_dir) { std::filesystem::path component = missing_components.pop_back_val(); CARBON_ASSIGN_OR_RETURN( Dir component_dir, OpenDir(component, CreationOptions::OpenAlways, creation_mode)); // Move this component into our temporary directory slot. work_dir = std::move(component_dir); } // Now walk through the remaining components opening and creating each // relative to the previous. while (!missing_components.empty()) { std::filesystem::path component = missing_components.pop_back_val(); CARBON_ASSIGN_OR_RETURN( Dir component_dir, work_dir->OpenDir(component, CreationOptions::OpenAlways, creation_mode)); // Close the current temporary directory and move the new component // directory object into its place. work_dir = std::move(component_dir); } CARBON_CHECK(work_dir, "Should always have created at least one directory for a " "non-empty path!"); return std::move(work_dir).value(); } auto DirRef::Rmtree(const std::filesystem::path& path) -> ErrorOr { struct DirAndIterator { DirRef::Reader dir; ssize_t dir_entry_start; }; llvm::SmallVector dir_stack; llvm::SmallVector dir_entries; llvm::SmallVector unknown_entries; dir_entries.push_back(path); for (;;) { // When we bottom out, we're removing the initial tree path and doing so // relative to `this` directory. DirRef current = dir_stack.empty() ? *this : dir_stack.back().dir; ssize_t dir_entry_start = dir_stack.empty() ? 0 : dir_stack.back().dir_entry_start; // If we've finished all the child directories of the current entry in the // stack, pop it off and continue. if (dir_entry_start == static_cast(dir_entries.size())) { dir_stack.pop_back(); continue; } CARBON_CHECK(dir_entry_start < static_cast(dir_entries.size())); // Take the last entry under the current directory and try removing it. const std::filesystem::path& entry_path = dir_entries.back(); auto rmdir_result = current.Rmdir(entry_path); if (rmdir_result.ok() || rmdir_result.error().no_entity()) { // Removed here or elsewhere already, so pop the entry. dir_entries.pop_back(); if (dir_entries.empty()) { // The last entry is the input path with an empty stack, so we've // finished at this point. CARBON_CHECK(dir_stack.empty()); return Success(); } continue; } // If we get any error other than not-empty, just return that. if (!rmdir_result.error().not_empty()) { return std::move(rmdir_result).error(); } // Recurse into the subdirectory since it isn't empty, opening it, getting a // reader, and pushing it onto our stack. CARBON_ASSIGN_OR_RETURN(Dir subdir, current.OpenDir(entry_path)); auto read_result = std::move(subdir).TakeAndRead(); if (!read_result.ok()) { return PathError( read_result.error().unix_errnum(), "Dir::Read on '{0}' relative to '{1}' during RmdirRecursively", entry_path, current.dfd_); } dir_stack.push_back( {*std::move(read_result), static_cast(dir_entries.size())}); // Now read the directory entries. It would be nice to be able to directly // remove the files and empty directories as we find them when reading, and // the POSIX spec appears to require that to work, but testing shows at // least some Linux environments don't work reliably in this case and will // fail to visit some entries entirely. As a consequence, we walk the entire // directory and collect the entries into data structures before beginning // to remove them. DirRef::Reader& subdir_reader = dir_stack.back().dir; for (const auto& entry : subdir_reader) { llvm::StringRef name = entry.name(); if (name == "." || name == "..") { continue; } if (entry.is_known_dir()) { dir_entries.push_back(name.str()); } else { // We end up here for entries known to be regular files, other kinds of // non-directory entries, or when the entry kind isn't known. // // Unless we *know* the entry is a directory, we put it into the unknown // entries. For these, we unlink them first in case they are // non-directory entries and use the failure of that to move any // directories that end up here to the directory entries list. unknown_entries.push_back(name.str()); } } // We can immediately try to unlink all the unknown entries, which will // include any regular files, and use an error on directories that were // unknown above to switch them to the `dir_entries` list. while (!unknown_entries.empty()) { std::filesystem::path name = unknown_entries.pop_back_val(); auto unlink_result = subdir_reader.Unlink(name); if (unlink_result.ok() || unlink_result.error().no_entity()) { continue; } else if (!unlink_result.error().is_dir()) { return std::move(unlink_result).error(); } dir_entries.push_back(std::move(name)); } // We'll handle the directory entries we've queued here in the next // iteration, removing them or recursing as needed. } } auto DirRef::ReadlinkSlow(const std::filesystem::path& path) -> ErrorOr { constexpr ssize_t MinBufferSize = #ifdef PATH_MAX PATH_MAX #else 1024 #endif ; // Read directly into a string to avoid allocating two large buffers. std::string large_buffer; // Stat the symlink to get an initial guess at the size. CARBON_ASSIGN_OR_RETURN(FileStatus status, Lstat(path)); // We try to use the size from the `lstat` unless it is empty, in which case // we try to use our minimum buffer size which is `PATH_MAX` or a constant // value. We have a fallback to dynamically discover an adequate buffer size // below that will handle any inaccuracy. ssize_t buffer_size = status.size(); if (buffer_size == 0) { buffer_size = MinBufferSize; } large_buffer.resize(status.size()); ssize_t result = readlinkat(dfd_, path.c_str(), large_buffer.data(), large_buffer.size()); if (result == -1) { return PathError(errno, "Readlink on '{0}' relative to '{1}'", path, dfd_); } // Now the really bad fallback case: if there are racing writes to the // symlink, the guessed size may not have been large enough. As a last-ditch // effort, begin doubling (from the next power of two >= our min buffer size) // the length until it fits. We cap this at 10 MiB to prevent egregious file // system contents (or some bug somewhere) from exhausting memory. constexpr ssize_t MaxBufferSize = 10 << 20; while (result == static_cast(large_buffer.size())) { int64_t next_buffer_size = std::max( MinBufferSize, llvm::NextPowerOf2(large_buffer.size())); if (next_buffer_size > MaxBufferSize) { return PathError(ENOMEM, "Readlink on '{0}' relative to '{1}'", path, dfd_); } large_buffer.resize(next_buffer_size); result = readlinkat(dfd_, path.c_str(), large_buffer.data(), large_buffer.size()); if (result == -1) { return PathError(errno, "Readlink on '{0}' relative to '{1}'", path, dfd_); } } // Fix-up the size of the string and return it. large_buffer.resize(result); return large_buffer; } auto MakeTmpDir() -> ErrorOr { std::filesystem::path tmpdir_path = "/tmp"; // We use both `TEST_TMPDIR` and `TMPDIR`. The `TEST_TMPDIR` is set by Bazel // and preferred to keep tests using the expected output tree rather than // the system temporary directory. for (const char* tmpdir_env_name : {"TEST_TMPDIR", "TMPDIR"}) { const char* tmpdir_env_cstr = getenv(tmpdir_env_name); if (tmpdir_env_cstr == nullptr) { continue; } std::filesystem::path tmpdir_env = tmpdir_env_cstr; tmpdir_path = std::move(tmpdir_env); break; } std::filesystem::path target = BuildData::BuildTarget.str(); tmpdir_path /= target.filename(); return MakeTmpDirWithPrefix(std::move(tmpdir_path)); } auto MakeTmpDirWithPrefix(std::filesystem::path prefix) -> ErrorOr { std::filesystem::path tmpdir_path = std::move(prefix); tmpdir_path += ".XXXXXX"; std::string tmpdir_path_buffer = tmpdir_path.native(); char* result = mkdtemp(tmpdir_path_buffer.data()); if (result == nullptr) { RawStringOstream os; os << llvm::formatv("Calling mkdtemp on '{0}' failed: ", tmpdir_path.native()); PrintErrorNumber(os, errno); return Error(os.TakeStr()); } CARBON_CHECK(result == tmpdir_path_buffer.data(), "`mkdtemp` used a modified path"); tmpdir_path = std::move(tmpdir_path_buffer); // Because `mkdtemp` doesn't return an open directory atomically, open the // created directory and perform safety checks similar to `OpenDir` when // creating a new directory. CARBON_ASSIGN_OR_RETURN( Dir tmp, Cwd().OpenDir(tmpdir_path, OpenExisting, /*creation_mode=*/0, OpenFlags::NoFollow)); // Make sure we try to remove the directory from here on out. RemovingDir result_dir(std::move(tmp), tmpdir_path); // It's a bit awkward to report `fstat` errors as `Error`s, but we // don't have much choice. The stat failing here would be very weird. CARBON_ASSIGN_OR_RETURN(FileStatus stat, result_dir.Stat()); // The permissions must be exactly 0700 for a temporary directory, and the UID // should be ours. if (stat.permissions() != 0700 && stat.unix_uid() != geteuid()) { return Error( llvm::formatv("Found incorrect permissions or UID on tmpdir '{0}'", tmpdir_path.native()) .str()); } return result_dir; } } // namespace Carbon::Filesystem ================================================ FILE: common/filesystem.h ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #ifndef CARBON_COMMON_FILESYSTEM_H_ #define CARBON_COMMON_FILESYSTEM_H_ #include #include #include #include #include #include #include #include #include #include #include #include "common/check.h" #include "common/error.h" #include "common/ostream.h" #include "common/raw_string_ostream.h" #include "common/template_string.h" #include "llvm/ADT/ScopeExit.h" #include "llvm/ADT/StringRef.h" #include "llvm/Support/FormatVariadic.h" // Provides a filesystem library for use in the Carbon project. // // This library provides an API designed to support modern Unix / Linux / POSIX // style filesystem operations, often called "Unix-like"[1] here, efficiently // and securely, while also carefully staying to a set of abstractions and // operations that can be reasonably implemented even on Windows platforms. // // TODO: Currently, there is not a Windows implementation, but this is actively // desired when we have testing infrastructure in place for Windows development. // Lacking that testing infrastructure and a full Windows port, the operations // here are manually compared with LLVM's filesystem library to ensure a // reasonable Windows implementation is possible. // // The library uses C++'s `std::filesystem::path` as its abstraction for // paths. This library provides two core APIs: open directories and files. // // Open directories provide relative- and absolute-path based operations to open // other directories or files. This allows secure creation of directories even // in the face of adversarial operations for example in a shared `/tmp` // directory. There is a `constexpr` current working directory available as // `Cwd()` that models normal filesystem operations with paths. // // Open files provide read, write, and other operations on the file. There are // separate types for read-only, write-only, and read-write files to model the // different APIs available. // // The APIs for both directories and files are primarily on `*Ref` types that // model a non-owning reference to the directory or file. These types are the // preferred types to use on an API boundary. Owning versions are provided that // ensure the file or directory is closed on destruction. Files support explicit // closing in order to observe any close-specific errors. // // Where APIs require flag parameters of some form, this library provides // enumerations that model those flags. The enumeration values are in turn // chosen to simplify passing these to specific native APIs. This means the // enumeration *values* should not be expected to be portable across platforms. // Customizing the values is part of the larger TODO to port the implementation // to Windows. // // [1]: Note that we refer to platforms as "Unix-like" rather than POSIX as we // want to group together all the OSes where the Unix-derived APIs are the // primary and expected way to interact with the filesystem, regardless of // whether a POSIX conforming API happens to exist. For example, both macOS // and WSL (Windows Subsystem for Linux) _are_ Unix-like as those are the // primary APIs used to access files in those environments. But Windows // itself _isn't_ Unix-like, even considering things like the defunct NT // POSIX subsystem or modern WSL, as those aren't the primary filesystem // APIs for the (non-WSL) Windows platform. This also matches the rough OS // classification used in LLVM. namespace Carbon::Filesystem { // The different creation options available when opening a file or directory. // // Because these are by far the most common parameters and they have unambiguous // names, the enumerators are also available directly within the namespace. enum class CreationOptions { // Requires an existing file or directory. OpenExisting = 0, // Opens an existing file or directory, and create one otherwise. OpenAlways = O_CREAT, // Opens and truncates an existing file or creates a new file. Provides // consistent behavior of an empty file regardless of the starting state. This // cannot be used for directories as they cannot be truncated on open. This is // essentially a short-cut for using `OpenAlways` and passing the // `OpenFlags::Truncate` below. CreateAlways = O_CREAT | O_TRUNC, // Requires no existing file or directory and will error if one is found. Only // succeeds when it creates a new file or directory. CreateNew = O_CREAT | O_EXCL, }; using enum CreationOptions; // General flags to control the behavior of opening files that aren't covered by // other more specific flags. // // These can be combined using the `|` operator where the semantics are // compatible, although not all are. enum class OpenFlags : int { None = 0, // Open the file for appending rather than with the position at the start. // // An error to combine with `Truncate` or to use with `CreateAlways`. Append = O_APPEND, // Open the file and truncate its contents to be empty. Truncate = O_TRUNC, // Don't follow a symlink in the final path component being opened. NoFollow = O_NOFOLLOW, }; inline auto operator|(OpenFlags lhs, OpenFlags rhs) -> OpenFlags { return static_cast(static_cast(lhs) | static_cast(rhs)); } inline auto operator|=(OpenFlags& lhs, OpenFlags rhs) -> OpenFlags& { lhs = lhs | rhs; return lhs; } // Flags controlling which permissions should be checked in an `Access` call. // // These permissions can also be combined with the `|` operator, so // `AccessCheckFlags::Read | AccessCheckFlags::Write` checks for both read and // write access. enum class AccessCheckFlags : int { Exists = F_OK, Read = R_OK, Write = W_OK, Execute = X_OK, }; inline auto operator|(AccessCheckFlags lhs, AccessCheckFlags rhs) -> AccessCheckFlags { return static_cast(static_cast(lhs) | static_cast(rhs)); } inline auto operator|=(AccessCheckFlags& lhs, AccessCheckFlags rhs) -> AccessCheckFlags& { lhs = lhs | rhs; return lhs; } // The underlying integer type that should be used to model the mode of a file. // // The mode is used in this API to represent both the permission bit mask and // special properties of a file. For example, on Unix-like systems, it combines // permissions with set-user-ID, set-group-ID, and sticky bits. // // The permission bits in the mode are represented using the Unix-style bit // pattern that facilitates octal modeling: // - Owner bit mask: 0700 // - Group bit mask: 0070 // - All bit mask: 0007 // // For each, read is an octal value of `1`, write `2`, and execute `4`. // // Windows gracefully degrades to the effective permissions modeled using // these values. using ModeType = mode_t; // Enumeration of the different file types recognized. // // In addition to the specific type values being arranged for ease of use with // the POSIX APIs, the underlying type of the enum is arranged to use the common // mode type. enum class FileType : ModeType { // Portable file types that need to be supported across platform // implementations. Directory = S_IFDIR, RegularFile = S_IFREG, SymbolicLink = S_IFLNK, // Non-portable Unix-like platform specific types. UnixFifo = S_IFIFO, UnixCharDevice = S_IFCHR, UnixBlockDevice = S_IFBLK, UnixSocket = S_IFSOCK, // Mask for the Unix-like types to allow easy extraction. UnixMask = S_IFMT, }; using Clock = std::chrono::file_clock; using Duration = std::chrono::duration<__int128, std::nano>; using TimePoint = std::chrono::time_point; // Enumerates the different open access modes available. // // These are largely used to parameterize types in order to constrain which API // subset is available, and rarely needed directly. enum class OpenAccess { ReadOnly = O_RDONLY, WriteOnly = O_WRONLY, ReadWrite = O_RDWR, }; // Forward declarations of various types that appear in APIs. class DirRef; class Dir; class RemovingDir; template class FileRef; template class File; class FdError; class PathError; namespace Internal { class FileRefBase; } // namespace Internal // Returns a constant `Dir` object that models the open current working // directory. // // Whatever the working directory of the process is will be used as the base for // any relative path operations on this object. For example, on Unix-like // systems, `Cwd().Stat("some/path")` is equivalent to `stat("some/path")`. consteval auto Cwd() -> Dir; // Creates a temporary directory and returns a removing directory handle to it. // // Each directory created will be unique and newly created by the call. It is // the caller's responsibility to clean up this directory. auto MakeTmpDir() -> ErrorOr; // Creates a temporary directory and returns a removing directory handle to it. // // The path of the temporary directory will use the provided prefix, and will // not add any additional directory separators. Every component but the last in // the prefix path must exist and be a directory, the last directory must be // writable. auto MakeTmpDirWithPrefix(std::filesystem::path prefix) -> ErrorOr; // Class modeling a file (or directory) status information structure. // // This provides a largely-portable model that callers can use, as well as a few // APIs to access non-portable implementation details when necessary. class FileStatus { public: // The size of the file in bytes. auto size() const -> int64_t { return stat_buf_.st_size; } auto type() const -> FileType { return static_cast(stat_buf_.st_mode & static_cast(FileType::UnixMask)); } // Convenience predicates to test for specific values of `type()`. auto is_dir() const -> bool { return type() == FileType::Directory; } auto is_file() const -> bool { return type() == FileType::RegularFile; } auto is_symlink() const -> bool { return type() == FileType::SymbolicLink; } // The read, write, and execute permissions for user, group, and others. See // the `ModeType` documentation for how to interpret the result. auto permissions() const -> ModeType { return stat_buf_.st_mode & 0777; } auto mtime() const -> TimePoint { // Linux, FreeBSD, and OpenBSD all use `st_mtim`, but macOS uses a different // spelling. #if __APPLE__ timespec ts = stat_buf_.st_mtimespec; #else timespec ts = stat_buf_.st_mtim; #endif TimePoint t(std::chrono::seconds(ts.tv_sec)); return t + std::chrono::nanoseconds(ts.tv_nsec); } // Non-portable APIs only available on Unix-like systems. See the // documentation of the Unix `stat` structure fields they expose for their // meaning. auto unix_inode() const -> uint64_t { return stat_buf_.st_ino; } auto unix_uid() const -> uid_t { return stat_buf_.st_uid; } private: friend DirRef; friend Internal::FileRefBase; FileStatus() = default; struct stat stat_buf_ = {}; }; // Models a held lock on a file or directory. // // Can model either a shared or exclusive lock with respect to the file, but the // held lock is unique. // // Must be released prior to the underlying file or directory being closed as it // contains a non-owning reference to that underlying file. class FileLock { public: enum class Kind { Shared = LOCK_SH, Exclusive = LOCK_EX, }; using enum Kind; FileLock() = default; FileLock(FileLock&& arg) noexcept : fd_(std::exchange(arg.fd_, -1)) {} auto operator=(FileLock&& arg) noexcept -> FileLock& { Destroy(); fd_ = std::exchange(arg.fd_, -1); return *this; } ~FileLock() { Destroy(); } // Returns true if the lock is currently held. auto is_locked() const -> bool { return fd_ != -1; } private: friend Internal::FileRefBase; explicit FileLock(int fd) : fd_(fd) {} auto Destroy() -> void; int fd_ = -1; }; // The base class defining the core `File` API. // // While not used directly, this is the base class used to implement all of the // main `File` types: `ReadFileRef`, `WriteFileRef`, and `ReadWriteFileRef`. // // Objects using this type have access to an open file handle to a specific file // and expose operations on that open file. These operations may fail directly // with their `ErrorOr` return, but some errors may be deferred until the // underlying owning file is closed. // // The type provides reference semantics to the underlying file, but is // rebindable, movable, and copyable unlike a C++ language reference. class Internal::FileRefBase { public: // This object can be default constructed, but will hold an invalid file // handle in that case. This is to support rebinding operations. FileRefBase() = default; // Returns true if this refers to a valid open file, and false otherwise. auto is_valid() const -> bool { return fd_ != -1; } // Reads the file status. // // Analogous to the Unix-like `fstat` call. auto Stat() -> ErrorOr; // Updates the access and modification times for the open file. // // If no explicit `time_point` is provided, sets both times to the current // time. If an explicit `time_point` is provided, the times are updated to // that time. auto UpdateTimes(std::optional time_point = std::nullopt) -> ErrorOr; // Methods to seek the current file position, with various semantics for the // offset. auto Seek(int64_t delta) -> ErrorOr; auto SeekFromBeginning(int64_t delta_from_beginning) -> ErrorOr; auto SeekFromEnd(int64_t delta_from_end) -> ErrorOr; // Truncates or extends the size of the file to the provided size. // // If the new size is smaller, all bytes beyond this size will be unavailable. // If the new size is larger, the file will be zero-filled to the new size. // The position of reads and writes does not change. auto Truncate(int64_t new_size) -> ErrorOr; // Reads as much data as is available and fits into the provided buffer. // // On success, this returns a new slice from the start to the end of the // successfully read bytes. These will always be located in the passed-in // buffer, but not all of the buffer may be filled. A partial read does not // mean that the end of the file has been reached. // // When a successful read with an *empty* slice is returned, that represents // reaching EOF on the underlying file successfully and there is no more data // to read. // // This method retries `EINTR` on Unix-like systems and returns // other errors to the caller. auto ReadToBuffer(llvm::MutableArrayRef buffer) -> ErrorOr, FdError>; // Writes as much data as possible from the provided buffer. // // On success, this returns a new slice of the *unwritten* bytes still present // in the buffer. An empty return represents a successful write of all bytes // in the buffer. A non-empty return does not represent an error or the // inability to finish writing. // // This method retries `EINTR` on Unix-like systems and returns // other errors to the caller. auto WriteFromBuffer(llvm::ArrayRef buffer) -> ErrorOr, FdError>; // Returns an LLVM `raw_fd_ostream` that writes to this file. // // Note that this doesn't expose any write errors here, those will surface // through the `raw_fd_ostream` API. The stream will also not close the file // which remains owned by the owning `File` object. auto WriteStream() -> llvm::raw_fd_ostream; // Reads the file from its start until EOF into the returned string. // // This method will retry any recoverable errors and work to completely read // the file contents from its beginning up to first encountering EOF. This // will seek the file to ensure it starts at the beginning regardless of // previous read or write operations. // // Any non-recoverable errors are returned to the caller. auto ReadFileToString() -> ErrorOr; // Writes the provided string to the file from the start and truncating to the // written size. // // This method will retry any recoverable errors and work to completely write // the provided content into the file from its beginning, and truncate the // file's size to the provided string size. Essentially, this function // replaces the file contents with the string's contents. // // Any non-recoverable errors are returned to the caller. auto WriteFileFromString(llvm::StringRef str) -> ErrorOr; // Attempt to acquire an advisory shared lock on this directory. // // This is always done as a non-blocking operation, as blocking on advisory // locks without a deadline can easily result in build systems essentially // "fork-bombing" a machine. However, a `deadline` duration can be provided // and this routine will block and attempt to acquire the requested lock for a // bounded amount of time approximately based on that duration. Further, a // `poll_interval` can be provided to control how quickly the lock will be // polled during that duration. There is no scaling of the poll intervals at // this layer, if a back-off heuristic is desired the caller should manage the // polling themselves. The goal is to allow simple cases of spurious failures // to be easily handled without unbounded blocking calls. Typically, callers // should use a duration that is a reasonable upper bound on the latency to // begin the locked operation and a poll interval that is a reasonably low // median latency to begin the operation as 1-2 polls is expected to be // common. These should not be set anywhere near the cost of acquiring a file // lock, and in general file locks should only be used for expensive // operations where it is worth significant delays to avoid duplicate work. // // If the lock cannot be acquired, the most recent lock-attempt error is // returned. auto TryLock(FileLock::Kind kind, Duration deadline = {}, Duration poll_interval = {}) -> ErrorOr; protected: explicit FileRefBase(int fd) : fd_(fd) {} // Note: this should only be used or made part of the public API by subclasses // that provide *ownership* of the open file. It is implemented here to // provide a single, non-templated implementation. auto Close() && -> ErrorOr; // Factored out code to destroy an open read-only file. This calls `Close` // above but ignores any errors as there is no risk of data loss for a // read-only file. // // Note: this is a private API that should not be made public, and should only // be used by the implementation of subclass destructors. It should also only // be called for subclasses with *ownership* of the file reference, and is // provided here as a single non-template implementation. auto ReadOnlyDestroy() -> void; // Factored out code to destroy an open writable file. This _requires_ the // file to have already been closed with an explicit `Close` call, where it // can report any errors. Without that, destroying a writable file can easily // result in unnoticed data loss. // // Note: this is a private API that should not be made public, and should only // be used by the implementation of subclass destructors. It should also only // be called for subclasses with *ownership* of the file reference, and is // provided here as a single non-template implementation. auto WriteableDestroy() -> void; // State representing a potentially open file. // // On POSIX systems, this will be a file descriptor. For moved-from and // default-constructed file objects this may be an invalid negative value to // signal that state. // // TODO: This should be customized on non-POSIX systems. // // This member is made protected rather than private as the derived classes // need direct access to it in several contexts. // NOLINTNEXTLINE(misc-non-private-member-variables-in-classes) int fd_ = -1; }; // A non-owning reference to an open file. // // Instances model a reference to an open file. Generally, rather than using a // `WriteFile&`, code should use a `WriteFileRef`. // // A specific instance provides the subset of the file API suitable for its // access based on its template parameter: read, write, or both. // // The API for file references is factored into a base class // `Internal::FileRefBase` to avoid duplication for each access instantiation. // Only the methods that are constrained by access are defined here, and they // are defined as wrappers around methods in the base where the documentation // and implementation live. template class FileRef : public Internal::FileRefBase { public: static constexpr bool Readable = A == OpenAccess::ReadOnly || A == OpenAccess::ReadWrite; static constexpr bool Writeable = A == OpenAccess::WriteOnly || A == OpenAccess::ReadWrite; // This object can be default constructed, but will hold an invalid file // handle in that case. This is to support rebinding operations. FileRef() = default; // Read and Write methods that delegate to the `FileRefBase` implementations, // but require the relevant access. See the methods on `FileRefBase` for full // documentation. auto Truncate(int64_t new_size) -> ErrorOr requires Writeable; auto ReadToBuffer(llvm::MutableArrayRef buffer) -> ErrorOr, FdError> requires Readable; auto WriteFromBuffer(llvm::ArrayRef buffer) -> ErrorOr, FdError> requires Writeable; auto WriteStream() -> llvm::raw_fd_ostream requires Writeable; auto ReadFileToString() -> ErrorOr requires Readable; auto WriteFileFromString(llvm::StringRef str) -> ErrorOr requires Writeable; protected: friend File; friend DirRef; // Other constructors from the base are also available, but remain protected. using FileRefBase::FileRefBase; }; // Convenience type defs for the three access combinations. using ReadFileRef = FileRef; using WriteFileRef = FileRef; using ReadWriteFileRef = FileRef; // An owning handle to an open file. // // This extends the `FileRef` API to provide ownership of the file handle. Most // of the API is defined by `FileRef`. // // The file will be closed when the object is destroyed, and must close without // errors. If there is a chance of errors on close, and that is often where // errors are reported, code must use the `Close` API to directly handle them or // it must be correct to check-fail on them. // // This type allows intentional "slicing" to the `FileRef` base class as that is // a correct and safe conversion to pass a non-owning reference to a file to // another function, much like binding a reference to an owning type is // implicit. template class File : public FileRef { public: static constexpr bool Readable = A == OpenAccess::ReadOnly || A == OpenAccess::ReadWrite; static constexpr bool Writeable = A == OpenAccess::WriteOnly || A == OpenAccess::ReadWrite; // Default constructs an invalid file. // // This can be destroyed or assigned safely, but no other operations are // correct. File() = default; // File objects are move-only as they model ownership. File(File&& arg) noexcept : FileRef(std::exchange(arg.fd_, -1)) {} auto operator=(File&& arg) noexcept -> File& { Destroy(); this->fd_ = std::exchange(arg.fd_, -1); return *this; } File(const File&) = delete; auto operator=(const File&) -> File& = delete; ~File() { Destroy(); } // Closes the open file and leaves the file in a moved-from state. // // The signature is `auto Close() && -> ErrorOr`. // // This type provides ownership of the file, so expose the `Close` method to // allow checked destruction and release of the file resources. // // If any errors are encountered during closing, returns them. Note that the // file should still be considered closed, and the object is moved-from even // if errors occur. using Internal::FileRefBase::Close; private: friend DirRef; // Destroy the file. // // This dispatches to non-template code in `FileRefBase` based on whether the // file is writable or readonly. The core logic is in the non-template // methods. auto Destroy() -> void; explicit File(int fd) : FileRef(fd) {} }; // Convenience type defs for the three access combinations. using ReadFile = File; using WriteFile = File; using ReadWriteFile = File; // A non-owning reference to an open directory. // // This is the main API for accessing and opening files and other directories. // Conceptually, every open file or directory is relative to some other // directory. The symbolic current working directory object is available via the // `Cwd()` function. When on a Unix-like platform, this is intended to provide // the semantics of `openat` and related functions, including the ability to // write secure filesystem operations in the face of adversarial parallel // filesystem operations. // // Relative path parameters are always relative to this directory. Absolute path // parameters are also allowed and are treated as absolute paths. This parallels // the behavior of `/` for path concatenation where an absolute path ignores all // preceding components. // // Errors for directory operations retain the path parameter used in order to // print helpful detail when unhandled, but otherwise work to be lazy and // lightweight to support low-overhead expected error patterns. // // The names are designed to mirror the underlying Unix-like APIs that implement // them, with extensions to add clarity. However, the set of operations is // expected to be reasonable to implement on Windows with reasonable fidelity. class DirRef { public: class Entry; class Iterator; class Reader; constexpr DirRef() = default; // Returns true if this refers to a valid open directory, and false otherwise. auto is_valid() const -> bool { return dfd_ != -1; } // Begin reading the entries in a directory. // // This returns a `Reader` object that can be iterated to walk over all the // entries in this directory. Note that the returned `Reader` owns a newly // allocated handle to this directory, and provides the full `DirRef` API. If // it isn't necessary to keep both open, the `Dir` class offers a // move-qualified method `TakeAndRead` that optimizes this case. // // Note that it is unspecified whether added and removed files during the // lifetime of the reader will be included when iterating, but otherwise // concurrent mutations are well defined. auto Read() -> ErrorOr; // Reads all of the entries in this directory into a vector. // // A helper function wrapping `Read` and walking the resulting reader's // entries to produce a container. // // For details on errors, see the documentation of `Read` and `Reader`. auto ReadEntries() -> ErrorOr, FdError>; // Reads the directory and appends entries to a container if they pass a // predicate. The predicate can be null, which is treated as if it always // returns true. // // For details on errors, see the documentation of `Read` and `Reader`. auto AppendEntriesIf( llvm::SmallVectorImpl& entries, llvm::function_refbool> predicate = {}) -> ErrorOr; // Reads the directory and appends entries to one of two containers if they // pass a predicate. The predicate can be null, which is treated as if it // always returns true. // // Which container an entry is appended to depends on its kind -- directories // go to the first and non-directories go to the second. This turns out to be // a very common split with subdirectories often needing separate handling // from other entries. // // For details on errors, see the documentation of `Read` and `Reader`. This // may also `Stat` entries if necessary to determine whether they are // directories. auto AppendEntriesIf( llvm::SmallVectorImpl& dir_entries, llvm::SmallVectorImpl& non_dir_entries, llvm::function_refbool> predicate = {}) -> ErrorOr; // Checks that the provided path can be accessed. auto Access(const std::filesystem::path& path, AccessCheckFlags check = AccessCheckFlags::Exists) -> ErrorOr; // Reads the `FileStatus` for the open directory. auto Stat() -> ErrorOr; // Reads the `FileStatus` for the provided path (without opening it). // // Like the `stat` system call on Unix-like platforms, this will follow any // symlinks and provide the status of the underlying file or directory. auto Stat(const std::filesystem::path& path) -> ErrorOr; // Reads the `FileStatus` for the provided path (without opening it). // // Like the `lstat` system call on Unix-like platforms, this will *not* follow // symlinks, and instead will return the status of the symlink itself. auto Lstat(const std::filesystem::path& path) -> ErrorOr; // Updates the access and modification times for the provided path. // // If no explicit `time_point` is provided, sets both times to the current // time. If an explicit `time_point` is provided, the times are updated to // that time. auto UpdateTimes(const std::filesystem::path& path, std::optional time_point = std::nullopt) -> ErrorOr; // Reads the target string of the symlink at the provided path. // // This does not follow the symlink, and does not require the symlink target // to be valid or exist. It merely reads the textual string. // // Returns an error if called with a path that is not a symlink. auto Readlink(const std::filesystem::path& path) -> ErrorOr; // Opens the provided path as a read-only file. // // The interaction with an existing file is governed by `creation_options` and // defaults to error unless opening an existing file. When creating a file, // only the leaf component in the provided path can be created with this call. // // If creating a file, the file is created with `creation_mode` which defaults // to a restrictive `0600`. The creation permission bits are also completely // independent of the access provided via the opened file. For example, // creating with write permissions doesn't impact whether write access is // available via the returned file. And creating _without_ write permission // bits is compatible with opening the file for writing. // // Additional flags can be provided to `flags` to control other aspects of // behavior on open. // // This is an error if the path exists and is a directory. If the path is a // symlink, it will follow the symlink. auto OpenReadOnly(const std::filesystem::path& path, CreationOptions creation_options = OpenExisting, ModeType creation_mode = 0600, OpenFlags flags = OpenFlags::None) -> ErrorOr; // Opens the provided path as a write-only file. Otherwise, behaves as // `OpenReadOnly`. auto OpenWriteOnly(const std::filesystem::path& path, CreationOptions creation_options = OpenExisting, ModeType creation_mode = 0600, OpenFlags flags = OpenFlags::None) -> ErrorOr; // Opens the provided path as a read-and-write file. Otherwise, behaves as // `OpenReadOnly`. auto OpenReadWrite(const std::filesystem::path& path, CreationOptions creation_options = OpenExisting, ModeType creation_mode = 0600, OpenFlags flags = OpenFlags::None) -> ErrorOr; // Opens the provided path as a directory. // // Similar to `OpenReadOnly` and other file opening APIs, accepts // `creation_options` to control the interaction with any existing directory. // However, `CreateAlways` is not implementable for directories and an error // if passed. The default permissions in the `creation_mode` are `0700` which // is more suitable for directories. There are no extra flags that can be // passed. // // As with other open routines, when creating a directory, only the leaf // component can be created by the call to this routine. // // When creating a directory with `CreateNew`, this routine works to be safe // even in the presence of adversarial, concurrent operations that attempt to // replace the created directory with one that is controlled by the adversary. // // Specifically, for `CreateNew` we ensure that the last component is a // created directory in its parent, and cannot be replaced by a symlink into // an attacker-controlled directory. We further ensure it cannot have been // replaced by a directory with a different owner or with wider permissions // than the created directory. // // However, no validation is done on any prefix path components leading to the // leaf component created. When securely creating directories, the initial // creation should have a single component from an opened existing parent // directory. Also, no validation of the owning _group_ is performed. When // securely creating a directory, the caller should either ensure the parent // directory does not have a malicious setgid bit set, or restrict the // created mode to not give group access, or both. In general, the lack of // control over the owning group motivates our choice to make the default mode // permissions restrictive and not include any group access. // // To securely achieve a result similar to `OpenAlways` instead of // `CreateNew`, callers can directly `CreateNew` and handle failures with an // explicit `OpenExisting` that also blocks following symlinks with // `OpenFlags::NoFollow` and performs any needed validation. auto OpenDir(const std::filesystem::path& path, CreationOptions creation_options = OpenExisting, ModeType creation_mode = 0700, OpenFlags flags = OpenFlags::None) -> ErrorOr; // Reads the file at the provided path to a string. // // This is a convenience wrapper for opening the path, reading the returned // file to a string, and closing it. Errors from any step are returned. auto ReadFileToString(const std::filesystem::path& path) -> ErrorOr; // Writes the provided `content` to the provided path. // // This is a convenience wrapper for opening the path, creating it according // to `creation_options` as necessary, writing `content` to it, and closing // it. Errors from any step are returned. auto WriteFileFromString(const std::filesystem::path& path, llvm::StringRef content, CreationOptions creation_options = CreateAlways) -> ErrorOr; // Renames an entry from one directory to another directory, replacing any // existing entry with the target path. // // Note that this is *not* a general purpose move! It must be possible for // this operation to be performed as a metadata-only change, and so without // moving any actual data. This means it will not work across devices, mounts, // or filesystems. However, these restrictions make this an *atomic* rename. // // The most common usage is to rename an entry within a single directory, by // passing `*this` as `target_dir`. auto Rename(const std::filesystem::path& path, DirRef target_dir, const std::filesystem::path& target_path) -> ErrorOr; // Changes the current working directory to this directory. auto Chdir() -> ErrorOr; // Changes the current working directory to the provided path. // // An error if the provided path is not a directory. Does not open the // provided path as a directory, but it will be available as the current // working directory via `Cwd()`. auto Chdir(const std::filesystem::path& path) -> ErrorOr; // Creates a symlink at the provided path with the contents of `target`. // // Note that the target of a symlink is an arbitrary string and there is no // error checking on whether it exists or is sensible. Also, the target string // set will be up to the first null byte in `target`, regardless of its // `size`. This will not overwrite an existing symlink at the provided path. // // Also note that the written symlink will be the null-terminated string // `target.c_str()`, ignoring everything past any embedded null bytes. auto Symlink(const std::filesystem::path& path, const std::string& target) -> ErrorOr; // Creates the directories in the provided path, using the permissions in // `creation_mode`. // // This will create any missing directory components in `path`. Relative paths // will be created relative to this directory, and without re-resolving its // path. The leaf created directory is opened and returned. // // The implementation allows for concurrent creation of the same directory (or // a prefix) without error or corruption and optimizes for performance of // creating the requested path. As a consequence, this creation is _unsafe_ in // the face of adversarial concurrent manipulation of components of the path. // If you need to create directories securely, first create an initial // directory securely using `OpenDir` and `CreateNew` with restricted // permissions that preclude any adversarial behavior, then use this API to // create tree components within that root. auto CreateDirectories(const std::filesystem::path& path, ModeType creation_mode = 0700) -> ErrorOr; // Unlink the last component of the path, removing that name from its parent // directory. // // If this was the last link to the underlying file its contents will be // removed when the last open file handle to it is closed. // // The path must not be a directory. If the path is a symbolic link, the link // will be removed, not the target. Models the behavior of `unlinkat(2)` on // Unix-like platforms. auto Unlink(const std::filesystem::path& path) -> ErrorOr; // Remove the directory entry of the last component of the path. // // The path must be a directory, and that directory must be empty. Models // `rmdirat(2)` on Unix-like platforms. auto Rmdir(const std::filesystem::path& path) -> ErrorOr; // Remove the directory tree identified by the last component of the path. // // The provided path must name a directory. This removes all files and // subdirectories contained within that named directory and then removes the // directory itself once empty. auto Rmtree(const std::filesystem::path& path) -> ErrorOr; protected: constexpr explicit DirRef(int dfd) : dfd_(dfd) {} // Slow-path fallback when unable to read the symlink target into a small // stack buffer. auto ReadlinkSlow(const std::filesystem::path& path) -> ErrorOr; // Generic implementation of the various `Open*` variants using the // `OpenAccess` enumerator. template auto OpenImpl(const std::filesystem::path& path, CreationOptions creation_options, ModeType creation_mode, OpenFlags flags) -> ErrorOr, PathError>; // State representing an open directory. // // On POSIX systems, this will be a file descriptor. For moved-from and // default-constructed file objects this may be an invalid negative value to // signal that state. // // TODO: This should be customized on non-POSIX systems. // // The directory's file descriptor is part of the protected API. // NOLINTNEXTLINE(misc-non-private-member-variables-in-classes): int dfd_ = -1; }; // An owning handle to an open directory. // // This extends the `DirRef` API to provide ownership of the directory. Most of // the API is defined by `DirRef`. It additionally provides optimized move-based // variations on those APIs where relevant. // // The directory will be closed when the object is destroyed. Closing an open // directory isn't an interesting error reporting path and so no direct close // API is provided. // // This type allows intentional "slicing" to the `DirRef` base class as that is // a correct and safe conversion to pass a non-owning reference to a directory // to another function, much like binding a reference to an owning type is // implicit. class Dir : public DirRef { public: Dir() = default; // Dir objects are move-only as they model ownership. Dir(Dir&& arg) noexcept : DirRef(std::exchange(arg.dfd_, -1)) {} auto operator=(Dir&& arg) noexcept -> Dir& { Destroy(); dfd_ = std::exchange(arg.dfd_, -1); return *this; } Dir(const Dir&) = delete; auto operator=(const Dir&) -> Dir& = delete; constexpr ~Dir(); // An optimized way to read the entries in a directory when moving from an // owning `Dir` object. // // This avoids creating a duplicate file handle for the returned `Reader`. // That `Reader` also supports the full `DirRef` API and so can often be used // without retaining the original `Dir`. // // For more details about reading, see the documentation on `DirRef::Read`. auto TakeAndRead() && -> ErrorOr; // Also include `DirRef`'s read API. using DirRef::Read; private: friend consteval auto Cwd() -> Dir; friend DirRef; friend RemovingDir; explicit constexpr Dir(int dfd) : DirRef(dfd) {} // Prevent implicit creation of a `Dir` object from a `RemovingDir` which will // end up as a subclass below and represent harmful implicit slicing. Instead, // require friendship and an explicit construction on an _intended_ release of // the removing semantics. explicit Dir(RemovingDir&& arg) noexcept; constexpr auto Destroy() -> void; }; // An owning handle to an open directory and its absolute path that will be // removed recursively when destroyed. // // This can be used to ensure removal of a directory, and also exposes the // absolute path of the directory. // // As removal may encounter errors, unless the desired behavior is a // check-failure, users should explicitly move and call `Remove` at the end of // lifetime and handle any resultant errors. class RemovingDir : public Dir { public: // Takes ownership of the open directory `d` and wraps it in a `RemovingDir` // that will remove it on destruction using `path`. Note that for a relative // `path`, the removal will re-resolve this relative to the current working // directory on removal. // // Note that there is no way for the implementation to validate what directory // `path` refers to, that is the responsibility of the caller. explicit RemovingDir(Dir d, std::filesystem::path path) : Dir(std::move(d)), path_(std::move(path)) {} RemovingDir() = default; RemovingDir(RemovingDir&& arg) = default; auto operator=(RemovingDir&& rhs) -> RemovingDir& = default; ~RemovingDir(); auto path() const [[clang::lifetimebound]] -> const std::filesystem::path& { return path_; } // Releases the directory from being removed and returns just the underlying // owning handle. auto Release() && -> Dir { return std::move(*this); } // Removes the directory immediately and surfaces any errors encountered. auto Remove() && -> ErrorOr; private: friend Dir; std::filesystem::path path_; }; // A named entry in a directory. // // This provides access to the scanned data when reading the entries of the // directory. It can only be produced by iterating over a `DirRef::Reader`. class DirRef::Entry { public: // The name of the entry. // // This is exposed as a null-terminated C-string as that is the most common // representation. auto name() const -> const char* { return dent_->d_name; } // Test if the entry has an unknown type. In this case, all other type // predicates will return false and the caller will have to directly `Lstat()` // the entry to determine its type. auto is_unknown_type() const -> bool { return dent_->d_type == DT_UNKNOWN; } // Predicates to test for known entry types. // // Note that we don't provide an enumerator here as we don't have any reliable // way to predict the set of possible values or narrow to that set. Different // platforms and even different versions of the same header may change the set // of types surfaced here. auto is_known_dir() const -> bool { return dent_->d_type == DT_DIR; } auto is_known_regular_file() const -> bool { return dent_->d_type == DT_REG; } auto is_known_symlink() const -> bool { return dent_->d_type == DT_LNK; } private: friend Dir::Reader; friend Dir::Iterator; Entry() = default; explicit Entry(dirent* dent) : dent_(dent) {} dirent* dent_ = nullptr; }; // An iterator into a `DirRef::Reader`, used for walking the entries in a // directory. // // Most of the work of iterating a directory is done when constructing the // `Reader`, when constructing the beginning iterator, or when incrementing the // iterator. class DirRef::Iterator : public llvm::iterator_facade_base { public: // Default construct a general end iterator. Iterator() = default; auto operator==(const Iterator& rhs) const -> bool { CARBON_DCHECK(dirp_ == nullptr || rhs.dirp_ == nullptr || dirp_ == rhs.dirp_); return entry_.dent_ == rhs.entry_.dent_; } auto operator*() const [[clang::lifetimebound]] -> const Entry& { return entry_; } auto operator++() -> Iterator&; private: friend Dir::Reader; // Construct a begin iterator for a specific directory stream. explicit Iterator(DIR* dirp) : dirp_(dirp) { // Increment immediately to populate the initial entry. ++*this; } DIR* dirp_ = nullptr; Entry entry_; }; // A reader for a directory. // // This class owns a handle to a directory that is set up for reading the // entries within the directory. Because it owns a handle to the directory, it // also implements the full `DirRef` API for convenience. // // Beyond the `DirRef` API, this object can be iterated as a range to visit all // the entries in the directory. // // Note that it is unspecified whether entries added or removed prior to being // visited while iterating. Iterating also cannot be re-started once begun -- // this models an input iterable range, not even a forward iterable range. // // This type allows intentional "slicing" to the `DirRef` base class as that is // a correct and safe conversion to pass a non-owning reference to a directory // to another function, much like binding a reference to an owning type is // implicit. class DirRef::Reader : public DirRef { public: Reader() = default; Reader(Reader&& arg) noexcept // The directory file descriptor isn't owning, but clear it for clarity. : DirRef(std::exchange(arg.dfd_, -1)), dirp_(std::exchange(arg.dirp_, nullptr)) {} Reader(const Reader&) = delete; auto operator=(Reader&& arg) noexcept -> Reader& { Destroy(); // The directory file descriptor isn't owning, but clear it for clarity. dfd_ = std::exchange(arg.dfd_, -1); dirp_ = std::exchange(arg.dirp_, nullptr); return *this; } ~Reader() { Destroy(); } // Compute the begin and end iterators for reading the entries of the // directory. auto begin() -> Iterator; auto end() -> Iterator; private: friend DirRef; friend Dir; explicit Reader(DIR* dirp) : DirRef(dirfd(dirp)), dirp_(dirp) {} auto Destroy() -> void; DIR* dirp_ = nullptr; }; namespace Internal { // Base class for `errno` errors. // // This is where we extract common APIs and logic for querying the specific // `errno`-based error. template class ErrnoErrorBase : public ErrorBase { public: // Accessors to test for specific kinds of errors that are portably available. auto already_exists() const -> bool { return errnum_ == EEXIST; } auto is_dir() const -> bool { return errnum_ == EISDIR; } auto no_entity() const -> bool { return errnum_ == ENOENT; } auto not_dir() const -> bool { return errnum_ == ENOTDIR; } auto access_denied() const -> bool { return errnum_ == EACCES; } auto would_block() const -> bool { return errnum_ == EWOULDBLOCK; } // Specific to `Rmdir` and `Rename` operations that remove a directory name, // two different error values can be used. auto not_empty() const -> bool { return errnum_ == ENOTEMPTY || errnum_ == EEXIST; } // Accessor for the `errno` based error number. This is not a portable API, // code using it will need to be ported to use a different API on Windows. // TODO: Add a Windows-specific API for its low-level error information. auto unix_errnum() const -> int { return errnum_; } protected: // NOLINTNEXTLINE(bugprone-crtp-constructor-accessibility): explicit ErrnoErrorBase(int errnum) : errnum_(errnum) {} private: int errnum_; }; } // namespace Internal // Error from a file-descriptor operation. // // This is the implementation of the file-descriptor-based error type. When // operations on a file descriptor fail, they use this object to convey the // error plus the descriptor in question. // // Specific context on the exact point or nature of the operation that failed // can be included in the custom format string. The format string should include // a placeholder for the file descriptor to be substituted into. The format // string should describe the _operation_ that failed, once rendered it will // have `failed: ` and a description of the `errno`-indicated failure appended. // // For example: // // `FdError(EPERM, "Read of file '{0}'", 42)` // // Will be rendered similarly to: // // "Read of file '42' failed: EPERM: ..." class FdError : public Internal::ErrnoErrorBase { public: FdError(FdError&&) noexcept = default; auto operator=(FdError&&) noexcept -> FdError& = default; // Prints this error to the provided string. // // Works to render the `errno` in a friendly way and includes the file // descriptor for context. auto Print(llvm::raw_ostream& out) const -> void; private: friend FileLock; friend Internal::FileRefBase; friend ReadFile; friend WriteFile; friend ReadWriteFile; friend DirRef; friend Dir; explicit FdError(int errnum, llvm::StringLiteral format, int fd) : ErrnoErrorBase(errnum), fd_(fd), format_(format) {} int fd_; llvm::StringLiteral format_; }; // Error from a path-based operation. // // This is the implementation of the path-based error type. When operations on a // path fail, they use this object to convey the error plus both the path and // relevant directory FD leading to the failure. // // Specific context on the exact point or nature of the operation that failed // can be included in the custom format string. The format string should include // placeholders for the path and the directory file descriptor to be substituted // into. The format string should describe the _operation_ that failed, once // rendered it will have `failed: ` and a description of the `errno`-indicated // failure appended. // // For example: // // `PathError(EPERM, "Open of '{0}' relative to '{1}'", "filename", 42)` // // Will be rendered similarly to: // // "Open of 'filename' relative to '42' failed: EPERM: ..." class PathError : public Internal::ErrnoErrorBase { public: PathError(PathError&&) noexcept = default; auto operator=(PathError&&) noexcept -> PathError& = default; // Prints this error to the provided string. // // Works to render the `errno` in a friendly way and includes the path and // directory file descriptor for context. auto Print(llvm::raw_ostream& out) const -> void; private: friend DirRef; friend Dir; explicit PathError(int errnum, llvm::StringLiteral format, std::filesystem::path path, int dir_fd) : ErrnoErrorBase(errnum), dir_fd_(dir_fd), path_(std::move(path)), format_(format) {} int dir_fd_; std::filesystem::path path_; llvm::StringLiteral format_; }; // Implementation details only below. namespace Internal { inline auto DurationToTimespec(Duration d) -> timespec { timespec ts = {}; ts.tv_sec = std::chrono::duration_cast(d).count(); d -= std::chrono::seconds(ts.tv_sec); ts.tv_nsec = std::chrono::duration_cast(d).count(); return ts; } } // namespace Internal consteval auto Cwd() -> Dir { return Dir(AT_FDCWD); } inline auto FileLock::Destroy() -> void { if (fd_ == -1) { // Nothing to unlock. return; } // We always try to unlock in a non-blocking way as there should never be a // reason to block here. int result = flock(fd_, LOCK_UN | LOCK_NB); // The only realistic error is `EBADF` that would represent a programming // error the type system should prevent. We conservatively check-fail if an // error occurs here. CARBON_CHECK(result == 0, "{0}", FdError(errno, "Unexpected error while _unlocking_ '{0}'", fd_)); } inline auto Internal::FileRefBase::Stat() -> ErrorOr { FileStatus status; if (fstat(fd_, &status.stat_buf_) == 0) { return status; } return FdError(errno, "File::Stat on '{0}'", fd_); } inline auto Internal::FileRefBase::UpdateTimes( std::optional time_point) -> ErrorOr { if (!time_point) { if (futimens(fd_, nullptr) == -1) { return FdError(errno, "File::UpdateTimes to now on '{0}'", fd_); } return Success(); } timespec times[2]; times[0] = Internal::DurationToTimespec(time_point->time_since_epoch()); times[1] = times[0]; if (futimens(fd_, times) == -1) { return FdError(errno, "File::UpdateTimes to a specific time on '{0}'", fd_); } return Success(); } inline auto Internal::FileRefBase::Seek(int64_t delta) -> ErrorOr { int64_t byte_offset = lseek(fd_, delta, SEEK_CUR); if (byte_offset == -1) { return FdError(errno, "File::Seek on '{0}'", fd_); } return byte_offset; } inline auto Internal::FileRefBase::SeekFromBeginning( int64_t delta_from_beginning) -> ErrorOr { int64_t byte_offset = lseek(fd_, delta_from_beginning, SEEK_SET); if (byte_offset == -1) { return FdError(errno, "File::SeekTo on '{0}'", fd_); } return byte_offset; } inline auto Internal::FileRefBase::SeekFromEnd(int64_t delta_from_end) -> ErrorOr { int64_t byte_offset = lseek(fd_, delta_from_end, SEEK_END); if (byte_offset == -1) { return FdError(errno, "File::SeekFromEnd on '{0}'", fd_); } return byte_offset; } inline auto Internal::FileRefBase::Truncate(int64_t new_size) -> ErrorOr { int64_t result = ftruncate(fd_, new_size); if (result == -1) { return FdError(errno, "File::Truncate on '{0}'", fd_); } return Success(); } inline auto Internal::FileRefBase::ReadToBuffer( llvm::MutableArrayRef buffer) -> ErrorOr, FdError> { for (;;) { ssize_t read_bytes = read(fd_, buffer.data(), buffer.size()); if (read_bytes == -1) { if (errno == EINTR) { continue; } return FdError(errno, "File::Read on '{0}'", fd_); } return buffer.slice(0, read_bytes); } } inline auto Internal::FileRefBase::WriteFromBuffer( llvm::ArrayRef buffer) -> ErrorOr, FdError> { for (;;) { ssize_t written_bytes = write(fd_, buffer.data(), buffer.size()); if (written_bytes == -1) { if (errno == EINTR) { continue; } return FdError(errno, "File::Write on '{0}'", fd_); } return buffer.drop_front(written_bytes); } } inline auto Internal::FileRefBase::WriteStream() -> llvm::raw_fd_ostream { return llvm::raw_fd_ostream(fd_, /*shouldClose=*/false); } inline auto Internal::FileRefBase::Close() && -> ErrorOr { // Put the file in a moved-from state immediately as it is invalid to // retry closing or use the file in any way even if the close fails. int fd = std::exchange(fd_, -1); int result = close(fd); if (result == 0) { return Success(); } return FdError(errno, "File::Close on '{0}'", fd); } inline auto Internal::FileRefBase::ReadOnlyDestroy() -> void { if (fd_ >= 0) { auto result = std::move(*this).Close(); // Intentionally drop errors, as there is no interesting error here. There // is no risk of data loss, and the least bad thing we can do is to just // leak the file descriptor. static_cast(result); } } inline auto Internal::FileRefBase::WriteableDestroy() -> void { CARBON_CHECK( fd_ == -1, "Cannot destroy an open writable file, they _must_ be destroyed by " "calling `Close` and handling any errors to avoid data loss."); } template auto FileRef::Truncate(int64_t new_size) -> ErrorOr requires Writeable { return FileRefBase::Truncate(new_size); } template auto FileRef::ReadToBuffer(llvm::MutableArrayRef buffer) -> ErrorOr, FdError> requires Readable { return FileRefBase::ReadToBuffer(buffer); } template auto FileRef::ReadFileToString() -> ErrorOr requires Readable { return FileRefBase::ReadFileToString(); } template auto FileRef::WriteFromBuffer(llvm::ArrayRef buffer) -> ErrorOr, FdError> requires Writeable { return FileRefBase::WriteFromBuffer(buffer); } template auto FileRef::WriteStream() -> llvm::raw_fd_ostream requires Writeable { return FileRefBase::WriteStream(); } template auto FileRef::WriteFileFromString(llvm::StringRef str) -> ErrorOr requires Writeable { return FileRefBase::WriteFileFromString(str); } template auto File::Destroy() -> void { if constexpr (Writeable) { this->WriteableDestroy(); } else { this->ReadOnlyDestroy(); } } inline auto DirRef::Read() -> ErrorOr { int dup_dfd = dup(dfd_); if (dup_dfd == -1) { // There are very few plausible errors here, but we can return one so it // doesn't hurt to do so. While `EINTR` and `EBUSY` are mentioned in some // documentation, there is no indication that for just `dup` it is useful to // loop and retry. return FdError(errno, "Dir::Read on '{0}'", dfd_); } return Dir(dup_dfd).TakeAndRead(); } inline auto DirRef::ReadEntries() -> ErrorOr, FdError> { llvm::SmallVector entries; CARBON_RETURN_IF_ERROR(AppendEntriesIf(entries)); return entries; } inline auto DirRef::Access(const std::filesystem::path& path, AccessCheckFlags check) -> ErrorOr { if (faccessat(dfd_, path.c_str(), static_cast(check), /*flags=*/0) == 0) { return true; } return PathError(errno, "Dir::Access on '{0}' relative to '{1}'", path, dfd_); } inline auto DirRef::Stat() -> ErrorOr { FileStatus status; if (fstat(dfd_, &status.stat_buf_) == 0) { return status; } return FdError(errno, "Dir::Stat on '{0}': ", dfd_); } inline auto DirRef::Stat(const std::filesystem::path& path) -> ErrorOr { FileStatus status; if (fstatat(dfd_, path.c_str(), &status.stat_buf_, /*flags=*/0) == 0) { return status; } return PathError(errno, "Dir::Stat on '{0}' relative to '{1}'", path, dfd_); } inline auto DirRef::Lstat(const std::filesystem::path& path) -> ErrorOr { FileStatus status; if (fstatat(dfd_, path.c_str(), &status.stat_buf_, /*flags=*/AT_SYMLINK_NOFOLLOW) == 0) { return status; } return PathError(errno, "Dir::Lstat on '{0}' relative to '{1}'", path, dfd_); } inline auto DirRef::UpdateTimes(const std::filesystem::path& path, std::optional time_point) -> ErrorOr { if (!time_point) { if (utimensat(dfd_, path.c_str(), nullptr, /*flags*/ 0) == -1) { return PathError(errno, "Dir::UpdateTimes to now on '{0}' relative to '{1}'", path, dfd_); } return Success(); } timespec times[2]; times[0] = Internal::DurationToTimespec(time_point->time_since_epoch()); times[1] = times[0]; if (utimensat(dfd_, path.c_str(), times, /*flags*/ 0) == -1) { return PathError( errno, "Dir::UpdateTimes to a specific time on '{0}' relative to '{1}'", path, dfd_); } return Success(); } inline auto DirRef::Readlink(const std::filesystem::path& path) -> ErrorOr { // On the fast path, we read into a small stack buffer and get the whole // contents. constexpr ssize_t BufferSize = 256; char buffer[BufferSize]; ssize_t read_bytes = readlinkat(dfd_, path.c_str(), buffer, BufferSize); if (read_bytes == -1) { return PathError(errno, "Dir::Readlink on '{0}' relative to '{1}'", path, dfd_); } if (read_bytes < BufferSize) { // We got the whole contents in one shot, return it. return std::string(buffer, read_bytes); } // Otherwise, fallback to an out-of-line function to handle the slow path. return ReadlinkSlow(path); } inline auto DirRef::OpenReadOnly(const std::filesystem::path& path, CreationOptions creation_options, ModeType creation_mode, OpenFlags flags) -> ErrorOr { return OpenImpl(path, creation_options, creation_mode, flags); } inline auto DirRef::OpenWriteOnly(const std::filesystem::path& path, CreationOptions creation_options, ModeType creation_mode, OpenFlags flags) -> ErrorOr { return OpenImpl(path, creation_options, creation_mode, flags); } inline auto DirRef::OpenReadWrite(const std::filesystem::path& path, CreationOptions creation_options, ModeType creation_mode, OpenFlags flags) -> ErrorOr { return OpenImpl(path, creation_options, creation_mode, flags); } inline auto DirRef::Rename(const std::filesystem::path& path, DirRef target_dir, const std::filesystem::path& target_path) -> ErrorOr { if (renameat(dfd_, path.c_str(), target_dir.dfd_, target_path.c_str()) == -1) { return PathError(errno, "Dir::Rename on '{0}' relative to '{1}'", path, dfd_); } return Success(); } inline auto DirRef::Chdir() -> ErrorOr { if (fchdir(dfd_) == -1) { return FdError(errno, "Dir::Chdir on '{0}'", dfd_); } return Success(); } inline auto DirRef::Chdir(const std::filesystem::path& path) -> ErrorOr { if (path.is_absolute()) { if (chdir(path.c_str()) == -1) { return PathError(errno, "Dir::Chdir on '{0}' relative to '{1}'", path, dfd_); } return Success(); } CARBON_ASSIGN_OR_RETURN(Dir d, OpenDir(path)); auto result = d.Chdir(); if (result.ok()) { return Success(); } return PathError(result.error().unix_errnum(), "Dir::Chdir on '{0}' relative to '{1}'", path, dfd_); } inline auto DirRef::Symlink(const std::filesystem::path& path, const std::string& target) -> ErrorOr { if (symlinkat(target.c_str(), dfd_, path.c_str()) == -1) { return PathError(errno, "Dir::Symlink on '{0}' relative to '{1}'", path, dfd_); } return Success(); } inline auto DirRef::Unlink(const std::filesystem::path& path) -> ErrorOr { if (unlinkat(dfd_, path.c_str(), /*flags=*/0) == -1) { return PathError(errno, "Dir::Unlink on '{0}' relative to '{1}'", path, dfd_); } return Success(); } inline auto DirRef::Rmdir(const std::filesystem::path& path) -> ErrorOr { if (unlinkat(dfd_, path.c_str(), AT_REMOVEDIR) == -1) { return PathError(errno, "Dir::Rmdir on '{0}' relative to '{1}'", path, dfd_); } return Success(); } template inline auto DirRef::OpenImpl(const std::filesystem::path& path, CreationOptions creation_options, ModeType creation_mode, OpenFlags flags) -> ErrorOr, PathError> { for (;;) { int fd = openat(dfd_, path.c_str(), static_cast(A) | static_cast(creation_options) | static_cast(flags), creation_mode); if (fd == -1) { // May need to retry on `EINTR` when opening FIFOs on Linux. if (errno == EINTR) { continue; } return PathError(errno, "Dir::Open on '{0}' relative to '{1}'", path, dfd_); } return File(fd); } } constexpr Dir::~Dir() { Destroy(); } inline auto Dir::TakeAndRead() && -> ErrorOr { // Transition our file descriptor into a directory stream, clearing it in the // process. int dfd = std::exchange(dfd_, -1); DIR* dirp = fdopendir(dfd); if (dirp == nullptr) { return FdError(errno, "Dir::Read on '{0}'", dfd); } return Dir::Reader(dirp); } inline Dir::Dir(RemovingDir&& arg) noexcept : Dir(static_cast(arg)) { arg.path_.clear(); } constexpr auto Dir::Destroy() -> void { if (dfd_ != -1 && dfd_ != AT_FDCWD) { auto result = close(dfd_); // Closing a directory shouldn't produce errors, directly check fail on any. // // This is a very different case from `close` on a file producing an error. // We don't actually write through the directory file descriptor, and for // most platforms `closedir` (the closest thing in documentation and // exclusively about directories), only provides a very few possible errors // here: // // EBADF: This should be precluded by the types here, and so we consider // it a programming error. // // EINTR: Technically, a system could fail here. We have good evidence // that systems we practically support don't as there also is nothing // useful to *do* in the face of this: retrying on almost all systems // is not allowed as the file descriptor is immediately released. And // here, there is no potentially dropped data to report. // // If we ever discover a platform that fails here, we should adjust this // code to not fail in the face of that, likely by dropping the error. If we // end up supporting a platform that actually requires well-specified // retries, this code should handle that. Until then, we require these to // succeed so we will learn about any issues during porting to new // platforms. CARBON_CHECK(result == 0, "{0}", FdError(errno, "Dir::Destroy on '{0}'", dfd_)); } dfd_ = -1; } inline RemovingDir::~RemovingDir() { if (dfd_ != -1) { auto result = std::move(*this).Remove(); CARBON_CHECK(result.ok(), "{0}", result.error()); } } inline auto RemovingDir::Remove() && -> ErrorOr { CARBON_CHECK(dfd_ != -1, "Unexpected explicit remove on a `RemovingDir` with no owned " "directory!"); // Close the directory base object prior to removing it. static_cast(*this) = Dir(); return Cwd().Rmtree(path_); } inline auto Dir::Iterator::operator++() -> Iterator& { CARBON_CHECK(dirp_, "Cannot increment an end-iterator"); errno = 0; entry_.dent_ = readdir(dirp_); // There are no documented errors beyond an erroneous `dirp_` which would be // a programming error and not due to any recoverable failure of the // filesystem. CARBON_CHECK(entry_.dent_ != nullptr || errno == 0, "Using a directory iterator with a non-directory, errno '{0}'", errno); if (entry_.dent_ == nullptr) { // Clear the directory pointer to ease debugging increments past the end. dirp_ = nullptr; } return *this; } inline auto Dir::Reader::begin() -> Iterator { // Reset the position of the directory stream to get the actual beginning. rewinddir(dirp_); return Iterator(dirp_); } inline auto Dir::Reader::end() -> Iterator { return Iterator(); } inline auto Dir::Reader::Destroy() -> void { if (dirp_) { int result = closedir(dirp_); // Closing a directory shouldn't produce interesting errors, so check fail // on them directly. // // See the detailed comment on `Dir::Destroy` for more context on closing of // directories, why we check-fail, and what we should do if we discover // platforms where an error needs to be handled here. CARBON_CHECK(result == 0, "{0}", FdError(errno, "Dir::Reader::Destroy on '{0}'", dfd_)); dirp_ = nullptr; dfd_ = -1; } } } // namespace Carbon::Filesystem #endif // CARBON_COMMON_FILESYSTEM_H_ ================================================ FILE: common/filesystem_benchmark.cpp ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #include #include #include #include "absl/random/random.h" #include "common/filesystem.h" #include "llvm/ADT/Sequence.h" #include "llvm/ADT/StringExtras.h" namespace Carbon::Filesystem { namespace { // Alternative implementation strategies to allow comparing performance. // // WHen implementing benchmarks below, we try to make them templates on this // enum and then switch in the body between different implementations. This // allows us to share the framework of each benchmark but select different // implementations for different instantiations. The different instantiations // get these enumerators in their names in the output, so we keep them short. enum BenchmarkComparables { Carbon, Std, }; // Filler text. constexpr llvm::StringLiteral Text = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod " "tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim " "veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea " "commodo consequat. Duis aute irure dolor in reprehenderit in voluptate " "velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint " "occaecat cupidatat non proident, sunt in culpa qui officia deserunt " "mollit anim id est laborum."; // Gets the filler text repeated up to a specific length. static auto GetText(int length) -> std::string { std::string content; content.reserve(length); while (static_cast(content.size()) < length) { content += Text.substr(0, length - content.size()); } CARBON_CHECK(static_cast(content.size()) == length); return content; } // We build a collection of file paths to use across different benchmarks in // batches to avoid looking at the same file over and over again. We can even // shuffle the file orders to further avoid hiding performance cost. If there // are specific cases where we want to measure the cached / predicted speed, we // can write those benchmarks against a specific file, but most often we instead // look at the worst case scenario for wall-clock time and use cycle counters // and instruction counters to measure aspects of the best case. The exact // number here was chosen arbitrarily to not make running benchmarks excessively // slow due to the large batches. constexpr int NumFiles = 64; // A common set of context used in benchmarks below. A separate context object // works better than the benchmark fixture support in practice. // // This is a struct as there are no invariants or contracts enforced. This is // just a container of commonly useful data and commonly useful helper // functions. struct BenchContext { RemovingDir tmpdir; absl::BitGen rng; std::array file_paths; std::array missing_paths; BenchContext() : tmpdir(*MakeTmpDir()) { for (int i : llvm::seq(NumFiles)) { file_paths[i] = llvm::formatv("file_{0}", i).str(); auto result = tmpdir.WriteFileFromString(file_paths[i], Text); CARBON_CHECK(result.ok(), "{0}", result.error()); missing_paths[i] = llvm::formatv("missing_{0}", i).str(); } ShuffleFilePaths(); ShuffleMissingPaths(); } auto ShuffleFilePaths() -> void { std::shuffle(file_paths.begin(), file_paths.end(), rng); } auto ShuffleMissingPaths() -> void { std::shuffle(missing_paths.begin(), missing_paths.end(), rng); } // Create a tree of files and directories starting from a `base` new directory // in our tmp directory, and containing `entries` total entries with // `entries_per_dir` in each directory. These will be a mixture of further // subdirectories and files. auto CreateTree(std::filesystem::path base, int entries, int entries_per_dir) -> void { CARBON_CHECK(entries >= 1); CARBON_CHECK(entries_per_dir >= 1); int num_subdirs = std::max(entries_per_dir / 2, 1); struct DirStackEntry { Dir dir; int num_entries; int subdir_count; }; llvm::SmallVector dir_stack; auto d = tmpdir.OpenDir(base, CreationOptions::CreateNew); CARBON_CHECK(d.ok(), "{0}", d.error()); dir_stack.push_back({std::move(*d), entries, 0}); while (!dir_stack.empty()) { auto& [dir, num_entries, subdir_count] = dir_stack.back(); // We want `num_entries` transitively in this directory, and // `entries_per_dir` directly. Spread the remaining entries across // `num_subdirs`. int entries_per_subdir = ((num_entries - entries_per_dir) / num_subdirs); CARBON_CHECK(entries_per_subdir < num_entries); // While we'll still put entries in a subdirectory, and we still need more // subdirectories in this directory, create another subdirectory, push it // on the stack, and recurse to it by continuing. if (entries_per_subdir >= entries_per_dir && subdir_count < num_subdirs) { auto name = llvm::formatv("dir_{0}", subdir_count).str(); auto subdir = dir.OpenDir(name, CreationOptions::CreateNew); CARBON_CHECK(subdir.ok(), "{0}", subdir.error()); ++subdir_count; // Note we have to continue after `push_back` as this will invalidate // the current references. dir_stack.push_back({std::move(*subdir), entries_per_subdir, 0}); continue; } // Otherwise, we're finished with subdirectories and just need to create // direct files. int num_files = entries_per_dir - subdir_count; CARBON_CHECK(num_files >= 0); for (int i = 0; i < num_files; ++i) { auto name = llvm::formatv("file_{0}", i).str(); auto f = dir.OpenWriteOnly(name, CreationOptions::CreateNew); CARBON_CHECK(f.ok(), "{0}", f.error()); auto close_result = std::move(*f).Close(); CARBON_CHECK(close_result.ok(), "{0}", close_result.error()); } dir_stack.pop_back(); } } }; template auto BM_Access(benchmark::State& state) -> void { BenchContext context; while (state.KeepRunningBatch(NumFiles)) { for (int i : llvm::seq(NumFiles)) { if constexpr (Comp == Carbon) { auto result = context.tmpdir.Access(context.file_paths[i]); CARBON_CHECK(result.ok(), "{0}", result.error()); } else if constexpr (Comp == Std) { std::error_code ec; bool exists = std::filesystem::exists( context.tmpdir.path() / context.file_paths[i], ec); CARBON_CHECK(!ec, "{0}", ec.message()); CARBON_CHECK(exists); } else { static_assert(false, "Invalid benchmark comparable"); } } } } BENCHMARK(BM_Access)->UseRealTime(); BENCHMARK(BM_Access)->UseRealTime(); template auto BM_AccessMissing(benchmark::State& state) -> void { BenchContext context; while (state.KeepRunningBatch(NumFiles)) { for (int i : llvm::seq(NumFiles)) { if constexpr (Comp == Carbon) { auto result = context.tmpdir.Access(context.missing_paths[i]); CARBON_CHECK(result.error().no_entity()); } else if constexpr (Comp == Std) { std::error_code ec; auto exists = std::filesystem::exists( context.tmpdir.path() / context.missing_paths[i], ec); CARBON_CHECK(!ec, "{0}", ec.message()); CARBON_CHECK(!exists); } else { static_assert(false, "Invalid benchmark comparable"); } } } } BENCHMARK(BM_AccessMissing)->UseRealTime(); BENCHMARK(BM_AccessMissing)->UseRealTime(); template auto BM_Stat(benchmark::State& state) -> void { BenchContext context; while (state.KeepRunningBatch(NumFiles)) { for (int i : llvm::seq(NumFiles)) { if constexpr (Comp == Carbon) { auto status = context.tmpdir.Stat(context.file_paths[i]); CARBON_CHECK(status.ok(), "{0}", status.error()); benchmark::DoNotOptimize(status->permissions()); } else if constexpr (Comp == Std) { std::error_code ec; auto status = std::filesystem::status( context.tmpdir.path() / context.file_paths[i], ec); CARBON_CHECK(!ec, "{0}", ec.message()); benchmark::DoNotOptimize(status.permissions()); } else { static_assert(false, "Invalid benchmark comparable"); } } } } BENCHMARK(BM_Stat)->UseRealTime(); BENCHMARK(BM_Stat)->UseRealTime(); template auto BM_StatMissing(benchmark::State& state) -> void { BenchContext context; while (state.KeepRunningBatch(NumFiles)) { for (int i : llvm::seq(NumFiles)) { if constexpr (Comp == Carbon) { auto status = context.tmpdir.Stat(context.missing_paths[i]); CARBON_CHECK(status.error().no_entity()); } else if constexpr (Comp == Std) { std::error_code ec; auto status = std::filesystem::status( context.tmpdir.path() / context.missing_paths[i], ec); CARBON_CHECK(ec.value() == ENOENT, "{0}", ec.message()); } else { static_assert(false, "Invalid benchmark comparable"); } } } } BENCHMARK(BM_StatMissing)->UseRealTime(); BENCHMARK(BM_StatMissing)->UseRealTime(); template auto BM_OpenMissing(benchmark::State& state) -> void { BenchContext context; while (state.KeepRunningBatch(NumFiles)) { for (int i : llvm::seq(NumFiles)) { if constexpr (Comp == Carbon) { auto f = context.tmpdir.OpenReadOnly(context.missing_paths[i]); CARBON_CHECK(f.error().no_entity()); } else if constexpr (Comp == Std) { std::ifstream f(context.tmpdir.path() / context.missing_paths[i]); CARBON_CHECK(!f.is_open()); } else { static_assert(false, "Invalid benchmark comparable"); } } } } BENCHMARK(BM_OpenMissing)->UseRealTime(); BENCHMARK(BM_OpenMissing)->UseRealTime(); template auto BM_OpenClose(benchmark::State& state) -> void { BenchContext context; while (state.KeepRunningBatch(NumFiles)) { for (int i : llvm::seq(NumFiles)) { if constexpr (Comp == Carbon) { auto f = context.tmpdir.OpenReadOnly(context.file_paths[i]); CARBON_CHECK(f.ok(), "{0}", f.error()); auto close_result = std::move(*f).Close(); CARBON_CHECK(close_result.ok(), "{0}", close_result.error()); } else if constexpr (Comp == Std) { std::ifstream f(context.tmpdir.path() / context.file_paths[i]); CARBON_CHECK(f.is_open()); } else { static_assert(false, "Invalid benchmark comparable"); } } } } BENCHMARK(BM_OpenClose)->UseRealTime(); BENCHMARK(BM_OpenClose)->UseRealTime(); template auto BM_CreateRemove(benchmark::State& state) -> void { BenchContext context; while (state.KeepRunningBatch(NumFiles)) { for (int i : llvm::seq(NumFiles)) { if constexpr (Comp == Carbon) { // Create the file by opening it. auto f = context.tmpdir.OpenWriteOnly(context.missing_paths[i], CreationOptions::CreateNew); CARBON_CHECK(f.ok(), "{0}", f.error()); // Close it right away. auto close_result = std::move(*f).Close(); CARBON_CHECK(close_result.ok(), "{0}", close_result.error()); // Remove it. auto remove_result = context.tmpdir.Unlink(context.missing_paths[i]); CARBON_CHECK(remove_result.ok(), "{0}", remove_result.error()); } else if constexpr (Comp == Std) { auto path = context.tmpdir.path() / context.missing_paths[i]; // Create the file by opening it. std::ofstream f(path); CARBON_CHECK(f.is_open()); // Close it right away. f.close(); // Remove it. std::error_code ec; std::filesystem::remove(path, ec); CARBON_CHECK(!ec, "{0}", ec.message()); } else { static_assert(false, "Invalid benchmark comparable"); } } } } BENCHMARK(BM_CreateRemove)->UseRealTime(); BENCHMARK(BM_CreateRemove)->UseRealTime(); template auto BM_Read(benchmark::State& state) -> void { BenchContext context; int length = state.range(0); std::string content = GetText(length); for (int i : llvm::seq(NumFiles)) { auto result = context.tmpdir.WriteFileFromString(context.file_paths[i], content); CARBON_CHECK(result.ok(), "{0}", result.error()); } while (state.KeepRunningBatch(NumFiles)) { // Re-shuffle the order of the files for each batch to avoid exact cache // hits. state.PauseTiming(); context.ShuffleFilePaths(); state.ResumeTiming(); for (int i : llvm::seq(NumFiles)) { if constexpr (Comp == Carbon) { auto read_result = context.tmpdir.ReadFileToString(context.file_paths[i]); CARBON_CHECK(read_result.ok(), "{0}", read_result.error()); benchmark::DoNotOptimize(*read_result); } else if constexpr (Comp == Std) { std::ifstream f(context.tmpdir.path() / context.file_paths[i], std::ios::binary); CARBON_CHECK(f.is_open()); // This may be a somewhat surprising implementation, but benchmarking // against several other ways of reading the file with `std::ifstream` // all have the same or worse performance. std::string read_content((std::istreambuf_iterator(f)), (std::istreambuf_iterator())); benchmark::DoNotOptimize(read_content); } else { static_assert(false, "Invalid benchmark comparable"); } } } } BENCHMARK(BM_Read)->Range(4, 1024LL * 1024)->UseRealTime(); BENCHMARK(BM_Read)->Range(4, 1024LL * 1024)->UseRealTime(); template auto BM_Write(benchmark::State& state) -> void { BenchContext context; int length = state.range(0); std::string content = GetText(length); while (state.KeepRunningBatch(NumFiles)) { // Re-shuffle the order of the files for each batch to avoid exact cache // hits. state.PauseTiming(); context.ShuffleFilePaths(); state.ResumeTiming(); for (int i : llvm::seq(NumFiles)) { if constexpr (Comp == Carbon) { auto write_result = context.tmpdir.WriteFileFromString(context.file_paths[i], content); CARBON_CHECK(write_result.ok(), "{0}", write_result.error()); } else if constexpr (Comp == Std) { std::ofstream f(context.tmpdir.path() / context.file_paths[i], std::ios::binary | std::ios::trunc); CARBON_CHECK(f.is_open()); f.write(content.data(), content.length()); } else { static_assert(false, "Invalid benchmark comparable"); } } } } BENCHMARK(BM_Write)->Range(4, 1024LL * 1024)->UseRealTime(); BENCHMARK(BM_Write)->Range(4, 1024LL * 1024)->UseRealTime(); template auto BM_Rmtree(benchmark::State& state) -> void { BenchContext context; int entries = state.range(0); int depth = state.range(1); // Configure our batch size based on the number of entries. Creating large // numbers of entries in the filesystem can cause problems, and is also very // slow. We don't need that much accuracy once the trees get large. int batch_size = entries <= 1024 ? 10 : entries <= (32 * 1024) ? 5 : 1; while (state.KeepRunningBatch(batch_size)) { state.PauseTiming(); for (int i : llvm::seq(batch_size)) { context.CreateTree(llvm::formatv("tree_{0}", i).str(), entries, depth); } state.ResumeTiming(); for (int i : llvm::seq(batch_size)) { std::string tree = llvm::formatv("tree_{0}", i).str(); if constexpr (Comp == Carbon) { auto rmdir_result = context.tmpdir.Rmtree(tree); CARBON_CHECK(rmdir_result.ok(), "{0}", rmdir_result.error()); } else if constexpr (Comp == Std) { std::error_code ec; std::filesystem::remove_all(context.tmpdir.path() / tree, ec); CARBON_CHECK(!ec, "{0}", ec.message()); } else { static_assert(false, "Invalid benchmark comparable"); } } } } BENCHMARK(BM_Rmtree) ->Ranges({{1, 256}, {1, 32}}) ->Ranges({{2 * 1024, 256 * 1024}, {512, 1024}}) ->Unit(benchmark::kMicrosecond) ->UseRealTime(); BENCHMARK(BM_Rmtree) ->Ranges({{1, 256}, {1, 32}}) ->Ranges({{2 * 1024, 256 * 1024}, {512, 1024}}) ->Unit(benchmark::kMicrosecond) ->UseRealTime(); template auto BM_CreateDirectories(benchmark::State& state) -> void { BenchContext context; int depth = state.range(0); int existing_depth = state.range(1); CARBON_CHECK(existing_depth <= depth); CARBON_CHECK(depth > 0); // Use a batch size of 10 to get avoid completely swamping the measurements // with overhead from creating existing directories and cleaning up. constexpr int BatchSize = 10; // Pre-build both the paths and the existing paths. Note that we use // relatively short paths here, which if anything makes the benefits of the // Carbon library smaller. llvm::SmallVector paths; llvm::SmallVector existing_paths; for (int i : llvm::seq(BatchSize)) { RawStringOstream path; llvm::ListSeparator sep("/"); for (int j = 0; j < existing_depth; ++j) { path << sep << "exists_" << (j == 0 ? i : j); } existing_paths.push_back(path.TakeStr()); path << existing_paths.back(); for (int k = existing_depth; k < depth; ++k) { path << sep << "dir_" << (k == 0 ? i : k); } paths.push_back(path.TakeStr()); } while (state.KeepRunningBatch(BatchSize)) { state.PauseTiming(); for (int i : llvm::seq(BatchSize)) { if (existing_depth > 0) { auto result = context.tmpdir.CreateDirectories(existing_paths[i]); CARBON_CHECK(result.ok(), "{0}", result.error()); } } state.ResumeTiming(); for (int i : llvm::seq(BatchSize)) { if constexpr (Comp == Carbon) { auto result = context.tmpdir.CreateDirectories(paths[i]); CARBON_CHECK(result.ok(), "Failed to create '{0}': {1}", paths[i], result.error()); // Create a file in the provided directory. This adds some baseline // overhead but matches the realistic use case and ensures that there // isn't some laziness that makes just creating a directory have an // unusually low cost. auto f = result->OpenWriteOnly("test", CreationOptions::CreateNew); CARBON_CHECK(f.ok(), "{0}", f.error()); auto close_result = std::move(*f).Close(); CARBON_CHECK(close_result.ok(), "{0}", close_result.error()); } else if constexpr (Comp == Std) { std::filesystem::path path = context.tmpdir.path() / paths[i]; std::error_code ec; std::filesystem::create_directories(path, ec); CARBON_CHECK(!ec, "{0}", ec.message()); // Create a file in the directory, similar to above. This has a (much) // bigger effect though because the C++ APIs don't open the created // directory, and so the creation cost of it can very much be hidden // from the benchmark if we don't use it. This also lets us see the // benefit of not needing to re-walk the path to create the file. std::ofstream f(path / "test"); CARBON_CHECK(f.is_open()); f.close(); } else { static_assert(false, "Invalid benchmark comparable"); } } state.PauseTiming(); for (int i : llvm::seq(BatchSize)) { auto result = context.tmpdir.Rmtree( llvm::formatv("{0}_{1}", existing_depth > 0 ? "exists" : "dir", i) .str()); CARBON_CHECK(result.ok(), "{0}", result.error()); } state.ResumeTiming(); } } static auto CreateDirectoriesBenchArgs(benchmark::Benchmark* b) { // The first argument is the depth of directory to create. We mostly care // about reasonably small depths here. It must be >= 1 for there to be // something to benchmark. The second number is the depth of pre-existing // directories which can vary from 0 to equal to the depth to benchmark the // case of no new directory being needed. for (int i = 1; i <= 8; i *= 2) { b->Args({i, 0}); for (int j = 1; j <= i; j *= 2) { b->Args({i, j}); } } } BENCHMARK(BM_CreateDirectories) ->Apply(CreateDirectoriesBenchArgs) ->UseRealTime(); BENCHMARK(BM_CreateDirectories) ->Apply(CreateDirectoriesBenchArgs) ->UseRealTime(); } // namespace } // namespace Carbon::Filesystem ================================================ FILE: common/filesystem_test.cpp ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #include "common/filesystem.h" #include #include #include #include #include #include #include "common/error_test_helpers.h" namespace Carbon::Filesystem { namespace { using ::testing::_; using ::testing::Eq; using ::testing::HasSubstr; using Testing::IsError; using Testing::IsSuccess; using ::testing::UnorderedElementsAre; class FilesystemTest : public ::testing::Test { public: explicit FilesystemTest() { auto result = MakeTmpDir(); CARBON_CHECK(result.ok(), "{0}", result.error()); dir_ = std::move(*result); } ~FilesystemTest() override { auto result = std::move(dir_).Remove(); CARBON_CHECK(result.ok(), "{0}", result.error()); } auto path() const -> const std::filesystem::path& { return dir_.path(); } // The test's temp directory, deleted on destruction. RemovingDir dir_; }; TEST_F(FilesystemTest, CreateOpenCloseAndUnlink) { auto unlink_result = dir_.Unlink("test"); ASSERT_FALSE(unlink_result.ok()); EXPECT_TRUE(unlink_result.error().no_entity()); #if defined(_GNU_SOURCE) && \ (__GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 32)) EXPECT_THAT(unlink_result, IsError(HasSubstr("ENOENT"))); #endif EXPECT_THAT(unlink_result, IsError(HasSubstr("No such file"))); auto f = dir_.OpenWriteOnly("test", CreationOptions::CreateNew); ASSERT_THAT(f, IsSuccess(_)); auto result = (*std::move(f)).Close(); EXPECT_THAT(result, IsSuccess(_)); f = dir_.OpenWriteOnly("test", CreationOptions::CreateNew); ASSERT_FALSE(f.ok()); EXPECT_TRUE(f.error().already_exists()); #if defined(_GNU_SOURCE) && \ (__GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 32)) EXPECT_THAT(f, IsError(HasSubstr("EEXIST"))); #endif EXPECT_THAT(f, IsError(HasSubstr("File exists"))); f = dir_.OpenWriteOnly("test"); ASSERT_THAT(f, IsSuccess(_)); result = std::move(*f).Close(); EXPECT_THAT(result, IsSuccess(_)); f = dir_.OpenWriteOnly("test"); ASSERT_THAT(f, IsSuccess(_)); result = std::move(*f).Close(); EXPECT_THAT(result, IsSuccess(_)); unlink_result = dir_.Unlink("test"); EXPECT_THAT(unlink_result, IsSuccess(_)); f = dir_.OpenWriteOnly("test"); EXPECT_FALSE(f.ok()); EXPECT_TRUE(f.error().no_entity()); #if defined(_GNU_SOURCE) && \ (__GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 32)) EXPECT_THAT(f, IsError(HasSubstr("ENOENT"))); #endif EXPECT_THAT(f, IsError(HasSubstr("No such file"))); f = dir_.OpenWriteOnly("test", CreationOptions::OpenAlways); ASSERT_THAT(f, IsSuccess(_)); result = std::move(*f).Close(); EXPECT_THAT(result, IsSuccess(_)); unlink_result = dir_.Unlink("test"); EXPECT_THAT(unlink_result, IsSuccess(_)); } TEST_F(FilesystemTest, BasicWriteAndRead) { std::string content_str = "0123456789"; { auto f = dir_.OpenWriteOnly("test", CreationOptions::CreateNew); ASSERT_THAT(f, IsSuccess(_)); auto write_result = f->WriteFileFromString(content_str); EXPECT_THAT(write_result, IsSuccess(_)); (*std::move(f)).Close().Check(); } { auto f = dir_.OpenReadOnly("test"); ASSERT_THAT(f, IsSuccess(_)); auto read_result = f->ReadFileToString(); EXPECT_THAT(read_result, IsSuccess(Eq(content_str))); } auto unlink_result = dir_.Unlink("test"); EXPECT_THAT(unlink_result, IsSuccess(_)); } TEST_F(FilesystemTest, SeekReadAndWrite) { std::string content_str = "0123456789"; // First write some initial content. { auto f = dir_.OpenWriteOnly("test", CreationOptions::CreateNew); ASSERT_THAT(f, IsSuccess(_)); auto write_result = f->WriteFileFromString(content_str); EXPECT_THAT(write_result, IsSuccess(_)); (*std::move(f)).Close().Check(); } // Now seek and read. { auto f = dir_.OpenReadOnly("test"); ASSERT_THAT(f, IsSuccess(_)); auto seek_result = f->Seek(3); ASSERT_THAT(seek_result, IsSuccess(Eq(3))); std::array buffer; auto read_result = f->ReadToBuffer(buffer); ASSERT_THAT(read_result, IsSuccess(_)); EXPECT_THAT(std::string(reinterpret_cast(read_result->data()), read_result->size()), Eq(content_str.substr(3, read_result->size()))); // Now test that we can seek back to the beginning and read the full file. auto read_file_result = f->ReadFileToString(); EXPECT_THAT(read_file_result, IsSuccess(Eq(content_str))); } // Now a mixture of reads, writes, an seeking. { auto f = dir_.OpenReadWrite("test"); ASSERT_THAT(f, IsSuccess(_)); auto seek_result = f->SeekFromEnd(-6); ASSERT_THAT(seek_result, IsSuccess(Eq(content_str.size() - 6))); std::string new_content_str = "abcdefg"; llvm::ArrayRef new_content_bytes( reinterpret_cast(new_content_str.data()), new_content_str.size()); for (auto write_bytes = new_content_bytes.slice(0, 4); !write_bytes.empty();) { auto write_result = f->WriteFromBuffer(write_bytes); ASSERT_THAT(write_result, IsSuccess(_)); write_bytes = *write_result; } std::array buffer; auto read_result = f->ReadToBuffer(buffer); ASSERT_THAT(read_result, IsSuccess(_)); EXPECT_THAT(std::string(reinterpret_cast(read_result->data()), read_result->size()), Eq(content_str.substr(8, read_result->size()))); EXPECT_THAT(*f->ReadFileToString(), "0123abcd89"); // Now write the entire file, also changing its size, after a fresh seek. seek_result = f->Seek(-6); ASSERT_THAT(seek_result, IsSuccess(Eq(content_str.size() - 6))); auto write_file_result = f->WriteFileFromString(new_content_str); EXPECT_THAT(write_file_result, IsSuccess(_)); EXPECT_THAT(*f->ReadFileToString(), "abcdefg"); (*std::move(f)).Close().Check(); } auto unlink_result = dir_.Unlink("test"); EXPECT_THAT(unlink_result, IsSuccess(_)); } TEST_F(FilesystemTest, CreateAndRemoveDirecotries) { auto d1 = Cwd().CreateDirectories(path() / "a" / "b" / "c" / "test1"); ASSERT_THAT(d1, IsSuccess(_)); auto d2 = Cwd().CreateDirectories(path() / "a" / "b" / "c" / "test2"); ASSERT_THAT(d2, IsSuccess(_)); auto d3 = Cwd().CreateDirectories(path() / "a" / "b" / "c" / "test3"); ASSERT_THAT(d3, IsSuccess(_)); // Get a directory object to use, this shouldn't cover much new. auto d4 = Cwd().CreateDirectories(path()); EXPECT_THAT(d4, IsSuccess(_)); // Single, present, relative component. auto d5 = d4->CreateDirectories("a"); EXPECT_THAT(d5, IsSuccess(_)); // Multiple, present, but relative components. auto d6 = d5->CreateDirectories(std::filesystem::path("b") / "c"); EXPECT_THAT(d6, IsSuccess(_)); // Single new component. auto d7 = d6->CreateDirectories("test4"); ASSERT_THAT(d7, IsSuccess(_)); // Two new relative components. auto d8 = d6->CreateDirectories(std::filesystem::path("test5") / "d"); EXPECT_THAT(d8, IsSuccess(_)); // Mixed relative components. auto d9 = d5->CreateDirectories(std::filesystem::path("b") / "test6"); EXPECT_THAT(d9, IsSuccess(_)); { auto f1 = d1->OpenWriteOnly("file1", CreateNew); ASSERT_THAT(f1, IsSuccess(_)); auto f2 = d2->OpenWriteOnly("file2", CreateNew); ASSERT_THAT(f2, IsSuccess(_)); auto f3 = d3->OpenWriteOnly("file3", CreateNew); ASSERT_THAT(f3, IsSuccess(_)); auto f4 = d7->OpenWriteOnly("file4", CreateNew); ASSERT_THAT(f4, IsSuccess(_)); (*std::move(f1)).Close().Check(); (*std::move(f2)).Close().Check(); (*std::move(f3)).Close().Check(); (*std::move(f4)).Close().Check(); } auto rm_result = Cwd().Rmtree(path() / "a"); ASSERT_THAT(rm_result, IsSuccess(_)); } TEST_F(FilesystemTest, StatAndAccess) { auto access_result = dir_.Access("test"); ASSERT_FALSE(access_result.ok()); EXPECT_TRUE(access_result.error().no_entity()); // Make sure the flags and bit-or-ing them works in the boring case. access_result = dir_.Access("test", AccessCheckFlags::Read | AccessCheckFlags::Write | AccessCheckFlags::Execute); ASSERT_FALSE(access_result.ok()); EXPECT_TRUE(access_result.error().no_entity()); auto stat_result = dir_.Stat("test"); ASSERT_FALSE(access_result.ok()); EXPECT_TRUE(access_result.error().no_entity()); // Create a file for testing, using very unusual and minimal permissions to // help us test. Hopefully this isn't modified on the usual `umask` tests run // under. std::string content_str = "0123456789"; ModeType permissions = 0450; auto f = dir_.OpenWriteOnly("test", CreationOptions::CreateNew, permissions); ASSERT_THAT(f, IsSuccess(_)); auto write_result = f->WriteFileFromString(content_str); EXPECT_THAT(write_result, IsSuccess(_)); access_result = dir_.Access("test"); EXPECT_THAT(access_result, IsSuccess(_)); access_result = dir_.Access("test", AccessCheckFlags::Read); EXPECT_THAT(access_result, IsSuccess(_)); // Neither write nor execute permission should be present though. access_result = dir_.Access("test", AccessCheckFlags::Write); ASSERT_FALSE(access_result.ok()); EXPECT_TRUE(access_result.error().access_denied()); access_result = dir_.Access("test", AccessCheckFlags::Read | AccessCheckFlags::Write | AccessCheckFlags::Execute); ASSERT_FALSE(access_result.ok()); EXPECT_TRUE(access_result.error().access_denied()); stat_result = dir_.Stat("test"); ASSERT_THAT(stat_result, IsSuccess(_)); EXPECT_TRUE(stat_result->is_file()); EXPECT_FALSE(stat_result->is_dir()); EXPECT_FALSE(stat_result->is_symlink()); EXPECT_THAT(stat_result->size(), Eq(content_str.size())); EXPECT_THAT(stat_result->permissions(), Eq(permissions)); // Directory instead of file. access_result = dir_.Access(".", AccessCheckFlags::Read | AccessCheckFlags::Write | AccessCheckFlags::Execute); EXPECT_THAT(access_result, IsSuccess(_)); stat_result = dir_.Stat("."); ASSERT_THAT(stat_result, IsSuccess(_)); EXPECT_FALSE(stat_result->is_file()); EXPECT_TRUE(stat_result->is_dir()); EXPECT_FALSE(stat_result->is_symlink()); // Can remove file but still stat through the file. auto unlink_result = dir_.Unlink("test"); ASSERT_THAT(unlink_result, IsSuccess(_)); auto file_stat_result = f->Stat(); ASSERT_THAT(file_stat_result, IsSuccess(_)); EXPECT_TRUE(file_stat_result->is_file()); EXPECT_FALSE(file_stat_result->is_dir()); EXPECT_FALSE(file_stat_result->is_symlink()); EXPECT_THAT(file_stat_result->size(), Eq(content_str.size())); EXPECT_THAT(file_stat_result->permissions(), Eq(permissions)); (*std::move(f)).Close().Check(); } TEST_F(FilesystemTest, Symlinks) { auto readlink_result = dir_.Readlink("test"); ASSERT_FALSE(readlink_result.ok()); EXPECT_TRUE(readlink_result.error().no_entity()); auto lstat_result = dir_.Lstat("test"); ASSERT_FALSE(lstat_result.ok()); EXPECT_TRUE(lstat_result.error().no_entity()); auto symlink_result = dir_.Symlink("test", "abc"); EXPECT_THAT(symlink_result, IsSuccess(_)); readlink_result = dir_.Readlink("test"); EXPECT_THAT(readlink_result, IsSuccess(Eq("abc"))); symlink_result = dir_.Symlink("test", "def"); ASSERT_FALSE(symlink_result.ok()); EXPECT_TRUE(symlink_result.error().already_exists()); lstat_result = dir_.Lstat("test"); ASSERT_THAT(lstat_result, IsSuccess(_)); EXPECT_FALSE(lstat_result->is_file()); EXPECT_FALSE(lstat_result->is_dir()); EXPECT_TRUE(lstat_result->is_symlink()); EXPECT_THAT(lstat_result->size(), Eq(strlen("abc"))); auto unlink_result = dir_.Unlink("test"); EXPECT_THAT(unlink_result, IsSuccess(_)); readlink_result = dir_.Readlink("test"); ASSERT_FALSE(readlink_result.ok()); EXPECT_TRUE(readlink_result.error().no_entity()); // Try a symlink with null bytes for fun. This demonstrates that the symlink // syscall only uses the leading C-string. symlink_result = dir_.Symlink("test", std::string("a\0b\0c", 5)); EXPECT_THAT(symlink_result, IsSuccess(_)); readlink_result = dir_.Readlink("test"); EXPECT_THAT(readlink_result, IsSuccess(Eq("a"))); } TEST_F(FilesystemTest, Chdir) { auto current_result = Cwd().OpenDir("."); ASSERT_THAT(current_result, IsSuccess(_)); auto symlink_result = dir_.Symlink("test", "abc"); EXPECT_THAT(symlink_result, IsSuccess(_)); auto chdir_result = dir_.Chdir(); EXPECT_THAT(chdir_result, IsSuccess(_)); auto readlink_result = Cwd().Readlink("test"); EXPECT_THAT(readlink_result, IsSuccess(Eq("abc"))); auto chdir_path_result = dir_.Chdir("missing"); ASSERT_FALSE(chdir_path_result.ok()); EXPECT_TRUE(chdir_path_result.error().no_entity()); // Dangling symlink. chdir_path_result = dir_.Chdir("test"); ASSERT_FALSE(chdir_path_result.ok()); EXPECT_TRUE(chdir_path_result.error().no_entity()); // Create a regular file and try to chdir to that. auto f = dir_.OpenWriteOnly("test2", CreationOptions::CreateNew); ASSERT_THAT(f, IsSuccess(_)); auto write_result = f->WriteFileFromString("test2"); EXPECT_THAT(write_result, IsSuccess(_)); chdir_path_result = dir_.Chdir("test2"); ASSERT_FALSE(chdir_path_result.ok()); EXPECT_TRUE(chdir_path_result.error().not_dir()); auto d2_result = Cwd().OpenDir("test_d2", CreationOptions::CreateNew); ASSERT_THAT(d2_result, IsSuccess(_)); symlink_result = d2_result->Symlink("test2", "def"); EXPECT_THAT(symlink_result, IsSuccess(_)); chdir_path_result = dir_.Chdir("test_d2"); ASSERT_THAT(chdir_path_result, IsSuccess(_)); readlink_result = Cwd().Readlink("test2"); EXPECT_THAT(readlink_result, IsSuccess(Eq("def"))); readlink_result = Cwd().Readlink("../test"); EXPECT_THAT(readlink_result, IsSuccess(Eq("abc"))); chdir_result = current_result->Chdir(); ASSERT_THAT(chdir_result, IsSuccess(_)); readlink_result = Cwd().Readlink("test"); ASSERT_FALSE(readlink_result.ok()); EXPECT_TRUE(readlink_result.error().no_entity()); (*std::move(f)).Close().Check(); } TEST_F(FilesystemTest, WriteStream) { std::string content_str = "0123456789"; auto write = dir_.OpenWriteOnly("test", CreationOptions::CreateNew); ASSERT_THAT(write, IsSuccess(_)); { llvm::raw_fd_ostream os = write->WriteStream(); os << content_str; EXPECT_FALSE(os.has_error()) << os.error(); } (*std::move(write)).Close().Check(); EXPECT_THAT(dir_.ReadFileToString("test"), IsSuccess(Eq(content_str))); } TEST_F(FilesystemTest, Rename) { // Rename a file within a directory. ASSERT_THAT(dir_.WriteFileFromString("file1", "content1"), IsSuccess(_)); EXPECT_THAT(dir_.Rename("file1", dir_, "file2"), IsSuccess(_)); EXPECT_THAT(dir_.ReadFileToString("file2"), IsSuccess(Eq("content1"))); auto read_missing = dir_.ReadFileToString("file1"); EXPECT_FALSE(read_missing.ok()); EXPECT_TRUE(read_missing.error().no_entity()); // Rename a file between two directories. auto d1 = *dir_.CreateDirectories("subdir1"); EXPECT_THAT(dir_.Rename("file2", d1, "file1"), IsSuccess(_)); EXPECT_THAT(d1.ReadFileToString("file1"), IsSuccess(Eq("content1"))); auto d2 = *dir_.CreateDirectories("subdir2"); EXPECT_THAT(d1.Rename("file1", d2, "file1"), IsSuccess(_)); EXPECT_THAT(d2.ReadFileToString("file1"), IsSuccess(Eq("content1"))); // Close the first directory. d1 = Filesystem::Dir(); EXPECT_THAT(dir_.Rmdir("subdir1"), IsSuccess(_)) << "Directory should have bene empty!"; // Rename directories. ASSERT_THAT(dir_.ReadFileToString(std::filesystem::path("subdir2") / "file1"), IsSuccess(Eq("content1"))); EXPECT_THAT(dir_.Rename("subdir2", dir_, "subdir1"), IsSuccess(_)); EXPECT_THAT(dir_.ReadFileToString(std::filesystem::path("subdir1") / "file1"), IsSuccess(Eq("content1"))); // The open directory `d2` should survive the rename and point at the same // directory. EXPECT_THAT(d2.ReadFileToString("file1"), IsSuccess(Eq("content1"))); EXPECT_THAT(d2.WriteFileFromString("file2", "content2"), IsSuccess(_)); EXPECT_THAT(dir_.ReadFileToString(std::filesystem::path("subdir1") / "file2"), IsSuccess(Eq("content2"))); // Rename over an existing file. EXPECT_THAT(d2.Rename("file2", d2, "file1"), IsSuccess(_)); EXPECT_THAT(d2.ReadFileToString("file1"), IsSuccess(Eq("content2"))); // Test error calls as well. auto result = dir_.Rename("missing1", dir_, "missing2"); EXPECT_TRUE(result.error().no_entity()) << result.error(); result = d2.Rename("file1", dir_, std::filesystem::path("missing_subdir") / "file2"); EXPECT_TRUE(result.error().no_entity()) << result.error(); // Note that `d2` was renamed `subdir1` above, which is why this creates // infinite subdirectories. result = dir_.Rename("subdir1", d2, "infinite_subdirs"); EXPECT_THAT(result.error().unix_errnum(), EINVAL) << result.error(); } TEST_F(FilesystemTest, TryLock) { auto file = dir_.OpenReadWrite("test_file", CreateNew); ASSERT_THAT(file, IsSuccess(_)); // Acquire an exclusive lock. auto lock = file->TryLock(FileLock::Exclusive); ASSERT_THAT(lock, IsSuccess(_)); EXPECT_TRUE(lock->is_locked()); // Try to acquire a second lock from a different file object. auto file2 = dir_.OpenReadOnly("test_file"); ASSERT_THAT(file2, IsSuccess(_)); auto lock2 = file2->TryLock(FileLock::Exclusive); ASSERT_THAT(lock2, IsError(_)); EXPECT_TRUE(lock2.error().would_block()); // A shared lock should also fail. auto lock3 = file2->TryLock(FileLock::Shared); ASSERT_THAT(lock3, IsError(_)); EXPECT_TRUE(lock3.error().would_block()); // Release the first lock. *lock = {}; EXPECT_FALSE(lock->is_locked()); // Now we can acquire an exclusive lock. lock2 = file2->TryLock(FileLock::Exclusive); ASSERT_THAT(lock2, IsSuccess(_)); EXPECT_TRUE(lock2->is_locked()); *lock2 = {}; // Test shared locks. auto shared_lock1 = file->TryLock(FileLock::Shared); ASSERT_THAT(shared_lock1, IsSuccess(_)); EXPECT_TRUE(shared_lock1->is_locked()); auto shared_lock2 = file2->TryLock(FileLock::Shared); ASSERT_THAT(shared_lock2, IsSuccess(_)); EXPECT_TRUE(shared_lock2->is_locked()); // An exclusive lock should fail. auto file3 = dir_.OpenReadOnly("test_file"); ASSERT_THAT(file3, IsSuccess(_)); auto exclusive_lock = file3->TryLock(FileLock::Exclusive); ASSERT_THAT(exclusive_lock, IsError(_)); EXPECT_TRUE(exclusive_lock.error().would_block()); // Release locks and close files. *shared_lock1 = {}; *shared_lock2 = {}; ASSERT_THAT((*std::move(file)).Close(), IsSuccess(_)); ASSERT_THAT((*std::move(file2)).Close(), IsSuccess(_)); ASSERT_THAT((*std::move(file3)).Close(), IsSuccess(_)); } TEST_F(FilesystemTest, ReadAndAppendEntries) { // Test with an empty directory. { auto entries = dir_.ReadEntries(); ASSERT_THAT(entries, IsSuccess(_)); EXPECT_TRUE(entries->empty()); } { llvm::SmallVector entries; EXPECT_THAT(dir_.AppendEntriesIf(entries), IsSuccess(_)); EXPECT_TRUE(entries.empty()); } // Create some files and directories. ASSERT_THAT(dir_.WriteFileFromString("file1", ""), IsSuccess(_)); ASSERT_THAT(dir_.WriteFileFromString("file2", ""), IsSuccess(_)); ASSERT_THAT(dir_.WriteFileFromString(".hidden", ""), IsSuccess(_)); ASSERT_THAT(dir_.CreateDirectories("subdir1"), IsSuccess(_)); ASSERT_THAT(dir_.CreateDirectories("subdir2"), IsSuccess(_)); // Test ReadEntries. { auto entries = dir_.ReadEntries(); ASSERT_THAT(entries, IsSuccess(_)); EXPECT_THAT(*entries, UnorderedElementsAre(".hidden", "file1", "file2", "subdir1", "subdir2")); } // Test AppendEntriesIf with no predicate. { llvm::SmallVector entries; EXPECT_THAT(dir_.AppendEntriesIf(entries), IsSuccess(_)); EXPECT_THAT(entries, UnorderedElementsAre(".hidden", "file1", "file2", "subdir1", "subdir2")); } // Test AppendEntriesIf with a predicate. { llvm::SmallVector entries; auto result = dir_.AppendEntriesIf( entries, [](llvm::StringRef name) { return name.starts_with("file"); }); EXPECT_THAT(result, IsSuccess(_)); EXPECT_THAT(entries, UnorderedElementsAre("file1", "file2")); } // Test AppendEntriesIf with directory splitting and a predicate. { llvm::SmallVector dir_entries; llvm::SmallVector non_dir_entries; auto result = dir_.AppendEntriesIf( dir_entries, non_dir_entries, [](llvm::StringRef name) { return !name.starts_with("."); }); EXPECT_THAT(result, IsSuccess(_)); EXPECT_THAT(dir_entries, UnorderedElementsAre("subdir1", "subdir2")); EXPECT_THAT(non_dir_entries, UnorderedElementsAre("file1", "file2")); } } TEST_F(FilesystemTest, MtimeAndUpdateTimes) { // Test UpdateTimes on a path that doesn't exist. auto update_missing = dir_.UpdateTimes("test_file"); ASSERT_THAT(update_missing, IsError(_)); EXPECT_TRUE(update_missing.error().no_entity()); // Create a file and get its initial modification time. ASSERT_THAT(dir_.WriteFileFromString("test_file", "content"), IsSuccess(_)); auto stat = dir_.Stat("test_file"); ASSERT_THAT(stat, IsSuccess(_)); auto time1 = stat->mtime(); // Repeated stats have stable time. stat = dir_.Stat("test_file"); ASSERT_THAT(stat, IsSuccess(_)); EXPECT_THAT(stat->mtime(), Eq(time1)); // Update the timestamp to a specific time in the past. auto past_time = time1 - std::chrono::seconds(120); ASSERT_THAT(dir_.UpdateTimes("test_file", past_time), IsSuccess(_)); stat = dir_.Stat("test_file"); ASSERT_THAT(stat, IsSuccess(_)); EXPECT_THAT(stat->mtime(), Eq(past_time)); // Now test updating times on an open file. Should still be at `past_time`. auto file = *dir_.OpenReadWrite("test_file"); auto file_stat = file.Stat(); ASSERT_THAT(file_stat, IsSuccess(_)); EXPECT_THAT(file_stat->mtime(), Eq(past_time)); // Update the times through the file and verify those updates arrived. ASSERT_THAT(file.UpdateTimes(time1), IsSuccess(_)); file_stat = file.Stat(); ASSERT_THAT(file_stat, IsSuccess(_)); EXPECT_THAT(file_stat->mtime(), Eq(time1)); ASSERT_THAT(std::move(file).Close(), IsSuccess(_)); } } // namespace } // namespace Carbon::Filesystem ================================================ FILE: common/find.h ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #ifndef CARBON_COMMON_FIND_H_ #define CARBON_COMMON_FIND_H_ #include #include #include "llvm/ADT/STLExtras.h" namespace Carbon { namespace Internal { template using RangePointerType = typename std::iterator_traits()))>::pointer; template using RangeValueType = typename std::iterator_traits()))>::value_type; template concept IsValidFindPredicate = requires(const RangeValueType& elem, Pred pred) { { pred(elem) } -> std::convertible_to; }; template concept IsComparable = requires(const A& a, const B& b) { { a == b } -> std::convertible_to; }; template concept RangeValueHasNoneType = requires { { RangeValueType::None } -> std::convertible_to>; }; } // namespace Internal // Finds a value in the given `range` by testing the `predicate`. Returns a // pointer to the value from the range on success, and nullptr if nothing is // found. // // This is similar to `std::find_if()` but returns a pointer to the value // instead of an iterator that must be tested against `end()`. template requires Internal::IsValidFindPredicate constexpr auto FindIfOrNull(Range&& range, Pred predicate) -> Internal::RangePointerType { auto it = llvm::find_if(range, predicate); if (it != range.end()) { return std::addressof(*it); } else { return nullptr; } } // Finds a value in the given `range` by testing the `predicate` and returns a // copy of it. If no match is found, returns `T::None` where the input range is // over values of type `T`. template requires Internal::IsValidFindPredicate && Internal::RangeValueHasNoneType && std::copy_constructible> constexpr auto FindIfOrNone(Range&& range, Pred predicate) -> Internal::RangeValueType { auto it = llvm::find_if(range, predicate); if (it != range.end()) { return *it; } else { return Internal::RangeValueType::None; } } // Finds a value in the given `range` by comparing to `query`. Returns a // pointer to the value from the range on success, and nullptr if nothing is // found. // // This is similar to `std::find_if()` but returns a pointer to the value // instead of an iterator that must be tested against `end()`. template > requires Internal::IsComparable> constexpr auto Contains(Range&& range, const Query& query) -> bool { return llvm::find(range, query) != range.end(); } } // namespace Carbon #endif // CARBON_COMMON_FIND_H_ ================================================ FILE: common/find_test.cpp ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #include "common/find.h" #include #include namespace Carbon { namespace { struct NoneType { static const NoneType None; int i; friend auto operator==(NoneType, NoneType) -> bool = default; }; const NoneType NoneType::None = {.i = -1}; TEST(FindTest, ReturnType) { const std::vector c; std::vector m; auto pred = [](int) { return true; }; static_assert(std::same_as); static_assert(std::same_as); } TEST(FindTest, FindIfOrNull) { auto make_pred = [](int query) { return [=](int elem) { return query == elem; }; }; std::vector empty; EXPECT_EQ(FindIfOrNull(empty, make_pred(0)), nullptr); std::vector range = {1, 2}; EXPECT_EQ(FindIfOrNull(range, make_pred(0)), nullptr); // NOLINTNEXTLINE(readability-container-data-pointer) EXPECT_EQ(FindIfOrNull(range, make_pred(1)), &range[0]); EXPECT_EQ(FindIfOrNull(range, make_pred(2)), &range[1]); EXPECT_EQ(FindIfOrNull(range, make_pred(3)), nullptr); } TEST(FindTest, FindIfOrNone) { auto make_pred = [](NoneType query) { return [=](NoneType elem) { return query == elem; }; }; std::vector empty; EXPECT_EQ(FindIfOrNone(empty, make_pred(NoneType{0})).i, -1); std::vector range = {NoneType{1}, NoneType{2}}; EXPECT_EQ(FindIfOrNone(range, make_pred(NoneType{0})).i, -1); EXPECT_EQ(FindIfOrNone(range, make_pred(NoneType{1})).i, 1); EXPECT_EQ(FindIfOrNone(range, make_pred(NoneType{2})).i, 2); EXPECT_EQ(FindIfOrNone(range, make_pred(NoneType{3})).i, -1); } TEST(FindTest, Contains) { std::vector empty; EXPECT_EQ(Contains(empty, 0), false); std::vector range = {1, 2}; EXPECT_EQ(Contains(range, 0), false); EXPECT_EQ(Contains(range, 1), true); EXPECT_EQ(Contains(range, 2), true); EXPECT_EQ(Contains(range, 3), false); } } // namespace } // namespace Carbon ================================================ FILE: common/growing_range.h ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #ifndef CARBON_COMMON_GROWING_RANGE_H_ #define CARBON_COMMON_GROWING_RANGE_H_ #include namespace Carbon { // A range adaptor for a random-access container such as `std::vector` or // `llvm::SmallVector` that might have elements appended during the iteration. // This adaptor avoids invalidation issues by tracking an index instead of an // iterator, and by returning by value from `operator*` instead of by reference. // // This class is intended only for use as the range in a range-based for loop, // and as such does not provide a complete range or iterator interface. Instead, // it provides only the interface required by the range-based for loop. template requires std::ranges::sized_range && std::ranges::random_access_range class GrowingRange { public: // An end sentinel for the range. class End {}; // An iterator into a potentially-growing range. Tracks the container and the // current index, and indexes the container on each dereference. class Iterator { public: // Dereferences the iterator. These intentionally don't return by reference, // to avoid handing out a reference that would be invalidated when the // container grows during the traversal. auto operator*() -> auto { return (*container_)[index_]; } auto operator*() const -> auto { return (*container_)[index_]; } friend auto operator!=(Iterator it, End /*end*/) -> bool { return it.index_ != it.container_->size(); } auto operator++() -> void { ++index_; } private: friend class GrowingRange; explicit Iterator(const ContainerT* container) : container_(container), index_(0) {} const ContainerT* container_; size_t index_; }; explicit GrowingRange(const ContainerT& container) : container_(&container) {} auto begin() const -> Iterator { return Iterator(container_); } auto end() const -> End { return {}; } private: const ContainerT* container_; }; template GrowingRange(const ContainerT&) -> GrowingRange; } // namespace Carbon #endif // CARBON_COMMON_GROWING_RANGE_H_ ================================================ FILE: common/growing_range_test.cpp ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #include "common/growing_range.h" #include #include namespace Carbon { namespace { TEST(GrowingRangeTest, TestUnchanged) { std::vector v = {1, 2, 3, 4, 5}; int k = 0; for (int n : GrowingRange(v)) { EXPECT_EQ(n, ++k); } } TEST(GrowingRangeTest, TestGrowWithRealloc) { std::vector expected = {3, 2, 2, 2, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0}; std::vector v; v.reserve(1); v.push_back(3); EXPECT_LT(v.capacity(), expected.size()); int i = 0; for (int n : GrowingRange(v)) { // Append n copies of n - 1. v.insert(v.end(), n, n - 1); EXPECT_EQ(n, expected[i++]); } } TEST(GrowingRangeTest, TestNoReference) { std::vector v; // Use `decltype(auto)` to capture the type of the element including whether // it's a reference. for (decltype(auto) elem : GrowingRange(v)) { // The type of `elem` should be `int`, not `int&`. static_assert(std::same_as); } } } // namespace } // namespace Carbon ================================================ FILE: common/hashing.cpp ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #include "common/hashing.h" #include namespace Carbon { auto Hasher::HashSizedBytesLarge(llvm::ArrayRef bytes) -> void { const std::byte* data_ptr = bytes.data(); const ssize_t size = bytes.size(); CARBON_DCHECK(size > 32); // If we have 64 bytes or more, we're going to handle two 32-byte chunks at a // time using a simplified version of the main algorithm. This is based // heavily on the 64-byte and larger processing approach used by Abseil. The // goal is to mix the input data using as few multiplies (or other operations) // as we can and with as much [ILP][1] as we can. The ILP comes largely from // creating parallel structures to the operations. // // [1]: https://en.wikipedia.org/wiki/Instruction-level_parallelism auto mix32 = [](const std::byte* data_ptr, uint64_t buffer, uint64_t random0, uint64_t random1) { uint64_t a = Read8(data_ptr); uint64_t b = Read8(data_ptr + 8); uint64_t c = Read8(data_ptr + 16); uint64_t d = Read8(data_ptr + 24); uint64_t m0 = Mix(a ^ random0, b ^ buffer); uint64_t m1 = Mix(c ^ random1, d ^ buffer); return (m0 ^ m1); }; // Prefetch the first bytes into cache. __builtin_prefetch(data_ptr, 0 /* read */, 0 /* discard after next use */); uint64_t buffer0 = buffer ^ StaticRandomData[0]; uint64_t buffer1 = buffer ^ StaticRandomData[2]; const std::byte* tail_32b_ptr = data_ptr + (size - 32); const std::byte* tail_16b_ptr = data_ptr + (size - 16); const std::byte* end_ptr = data_ptr + (size - 64); while (data_ptr < end_ptr) { // Prefetch the next 64-bytes while we process the current 64-bytes. __builtin_prefetch(data_ptr + 64, 0 /* read */, 0 /* discard after next use */); buffer0 = mix32(data_ptr, buffer0, StaticRandomData[4], StaticRandomData[5]); buffer1 = mix32(data_ptr + 32, buffer1, StaticRandomData[6], StaticRandomData[7]); data_ptr += 64; } // If we haven't reached our 32-byte tail pointer, consume another 32-bytes // directly. if (data_ptr < tail_32b_ptr) { buffer0 = mix32(data_ptr, buffer0, StaticRandomData[4], StaticRandomData[5]); data_ptr += 32; } if (data_ptr < tail_16b_ptr) { // We have more than 16-bytes in the tail so use a full 32-byte mix from the // 32-byte tail pointer. buffer1 = mix32(tail_32b_ptr, buffer1, StaticRandomData[6], StaticRandomData[7]); } else { // 16-bytes or less in the tail, do something more minimal instead of a full // 32-byte mix. As this only involves a single multiply, we don't decompose // further even when the tail is (much) shorter. buffer1 = Mix(Read8(tail_16b_ptr) ^ StaticRandomData[6], Read8(tail_16b_ptr + 8) ^ buffer1); } buffer = buffer0 ^ buffer1; HashDense(size); } } // namespace Carbon ================================================ FILE: common/hashing.h ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #ifndef CARBON_COMMON_HASHING_H_ #define CARBON_COMMON_HASHING_H_ #include #include #include #include #include #include "common/check.h" #include "common/ostream.h" #include "llvm/ADT/APFloat.h" #include "llvm/ADT/APInt.h" #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/SmallVector.h" #include "llvm/ADT/StringRef.h" #include "llvm/Support/FormatVariadic.h" #ifdef __ARM_ACLE #include #endif namespace Carbon { // A 64-bit hash code produced by `Carbon::HashValue`. // // This provides methods for extracting high-quality bits from the hash code // quickly. // // This class can also be a hashing input when recursively hashing more complex // data structures. class HashCode : public Printable { public: HashCode() = default; constexpr explicit HashCode(uint64_t value) : value_(value) {} friend constexpr auto operator==(HashCode lhs, HashCode rhs) -> bool { return lhs.value_ == rhs.value_; } friend constexpr auto operator!=(HashCode lhs, HashCode rhs) -> bool { return lhs.value_ != rhs.value_; } // Extracts an index from the hash code as a `ssize_t`. This index covers the // full range of that type, and may even be negative. Typical usage will // involve masking this down to some positive range using a bitand with a mask // computed from a power-of-two size. This routine doesn't do any masking to // ensure a positive index to avoid redundant computations with the typical // user of the index. constexpr auto ExtractIndex() -> ssize_t; // Extracts an index and a fixed `N`-bit tag from the hash code. // // This extracts these values from the position of the hash code which // maximizes the entropy in the tag and the low bits of the index, as typical // indices will be further masked down to fall in a smaller range. // // `N` must be in the range [1, 32]. The returned index will be in the range // [0, 2**(64-N)). template constexpr auto ExtractIndexAndTag() -> std::pair; // Extract the full 64-bit hash code as an integer. // // The methods above should be preferred rather than directly manipulating // this integer. This is provided primarily to enable Merkle-tree hashing or // other recursive hashing where that is needed or more efficient. explicit operator uint64_t() const { return value_; } auto Print(llvm::raw_ostream& out) const -> void { out << llvm::formatv("{0:x16}", value_); } private: uint64_t value_ = 0; }; // Computes a hash code for the provided value, incorporating the provided seed. // // The seed doesn't need to be of any particular high quality, but a zero seed // has bad effects in several places. Prefer the unseeded routine rather than // providing a zero here. // // This **not** a cryptographically secure or stable hash -- it is only designed // for use with in-memory hash table style data structures. Being fast and // effective for that use case is the guiding principle of its design. // // There is no guarantee that the values produced are stable from execution to // execution. For speed and quality reasons, the implementation does not // introduce any variance to defend against accidental dependencies. As a // consequence, it is strongly encouraged to use a seed that varies from // execution to execution to avoid depending on specific values produced. // // The algorithm used is most heavily based on [Abseil's hashing algorithm][1], // with some additional ideas and inspiration from the fallback hashing // algorithm in [Rust's AHash][2] and the [FxHash][3] function. However, there // are also *significant* changes introduced here. // // [1]: https://github.com/abseil/abseil-cpp/tree/master/absl/hash/internal // [2]: https://github.com/tkaitchuck/aHash/wiki/AHash-fallback-algorithm // [3]: https://docs.rs/fxhash/latest/fxhash/ // // This hash algorithm does *not* defend against hash flooding. While it can be // viewed as "keyed" on the seed, it is expected to be possible to craft inputs // for some data types that cancel out the seed used and manufacture endlessly // colliding sets of keys. In general, this function works to be *fast* for hash // tables. If you need to defend against hash flooding, either directly use a // data structure with strong worst-case guarantees, or a hash table which // detects catastrophic collisions and falls back to such a data structure. // // This hash function is heavily optimized for *latency* over *quality*. Modern // hash tables designs can efficiently handle reasonable collision rates, // including using extra bits from the hash to avoid all efficiency coming from // the same low bits. Because of this, low-latency is significantly more // important for performance than high-quality, and this is heavily leveraged. // The result is that the hash codes produced *do* have significant avalanche // problems for small keys. The upside is that the latency for hashing integers, // pointers, and small byte strings (up to 32-bytes) is exceptionally low, and // essentially a small constant time instruction sequence. // // No exotic instruction set extensions are required, and the state used is // small. It does rely on being able to get the low- and high-64-bit results of // a 64-bit multiply efficiently. // // The function supports many typical data types such as primitives, string-ish // views, and types composing primitives transparently like pairs, tuples, and // array-ish views. It is also extensible to support user-defined types. // // The builtin support for string-like types include: // - `std::string_view` // - `std::string` // - `llvm::StringRef` // - `llvm::SmallString` // // This function supports heterogeneous lookup between all of the string-like // types. It also supports heterogeneous lookup between pointer types regardless // of pointee type and `nullptr`. // // However, these are the only heterogeneous lookup support including for the // builtin in, standard, and LLVM types. Notably, each different size and // signedness integer type may hash differently for efficiency reasons. Hash // tables should pick a single integer type in which to manage keys and do // lookups. // // To add support for your type, you need to implement a customization point -- // a free function that can be found by ADL for your type -- called // `CarbonHashValue` with the following signature: // // ```cpp // auto CarbonHashValue(const YourType& value, uint64_t seed) -> HashCode; // ``` // // The extension point needs to ensure that values that compare equal (including // any comparisons with different types that might be used with a hash table of // `YourType` keys) produce the same `HashCode` values. // // `HashCode` values should typically be produced using the `Hasher` helper type // below. See its documentation for more details about implementing these // customization points and how best to incorporate the value's state into a // `HashCode`. // // For two input values that are almost but not quite equal, the extension // point should maximize the probability of each bit of their resulting // `HashCode`s differing. More formally, `HashCode`s should exhibit an // [avalanche effect][4]. However, while this is desirable, it should be // **secondary** to low latency. The intended use case of these functions is not // cryptography but in-memory hashtables where the latency and overhead of // computing the `HashCode` is *significantly* more important than achieving a // particularly high quality. The goal is to have "just enough" avalanche // effect, but there is not a fixed criteria for how much is enough. That should // be determined through practical experimentation with a hashtable and // distribution of keys. // // [4]: https://en.wikipedia.org/wiki/Avalanche_effect template inline auto HashValue(const T& value, uint64_t seed) -> HashCode; // The same as the seeded version of `HashValue` but without callers needing to // provide a seed. // // Generally prefer the seeded version, but this is available if there is no // reasonable seed. In particular, this will behave better than using a seed of // `0`. One important use case is for recursive hashing of sub-objects where // appropriate or needed. template inline auto HashValue(const T& value) -> HashCode; // Object and APIs that eventually produce a hash code. // // This type is primarily used by types to implement a customization point // `CarbonHashValue` that will in turn be used by the `HashValue` function. See // the `HashValue` function for details of that extension point. // // The methods on this type can be used to incorporate data from your // user-defined type into its internal state which can be converted to a // `HashCode` at any time. These methods will only produce the same `HashCode` // if they are called in the exact same order with the same arguments -- there // are no guaranteed equivalences between calling different methods. // // Example usage: // ```cpp // auto CarbonHashValue(const MyType& value, uint64_t seed) -> HashCode { // Hasher hasher(seed); // hasher.HashTwo(value.x, value.y); // return static_cast(hasher); // } // ``` // // This type's API also reflects the reality that high-performance hash tables // are used with keys that are generally small and cheap to hash. // // To ensure this type's code is optimized effectively, it should typically be // used as a local variable and not passed across function boundaries // unnecessarily. // // The type also provides a number of static helper functions and static data // members that may be used by authors of `CarbonHashValue` implementations to // efficiently compute the inputs to the core `Hasher` methods, or even to // manually do some amounts of hashing in performance-tuned ways outside of the // methods provided. class Hasher { public: Hasher() = default; explicit Hasher(uint64_t seed) : buffer(seed) {} Hasher(Hasher&& arg) = default; Hasher(const Hasher& arg) = delete; auto operator=(Hasher&& rhs) -> Hasher& = default; // Extracts the current state as a `HashCode` for use. explicit operator HashCode() const { return HashCode(buffer); } // Incorporates a variable number of objects into the `hasher`s state. // // The `values` here can be anything hashable, and this routine handles // recursively hashing a single value as appropriate and then in turn // incorporating that. However, it is optimized for relatively small numbers // of values and/or small elements. A large tree structure will be better // handled by a dedicated Merkle-tree decomposition rather than the ad-hoc one // provided here. This routine's decomposition is mostly useful for combining // N small bits of data with one recursively hashed entity. // // There is no guaranteed correspondence between the behavior of a single call // with multiple parameters and multiple calls. template auto Hash(const Ts&... values) -> void; // Incorporates an array of objects into the hasher's state. // // Similar to the variadic `Hash`, this will handle recursively hashing if // necessary, but is optimized to avoid it when possible and is especially // efficient when hashing a raw array of bytes. // // Note that this is especially inefficient when it must recursively hash for // long arrays -- that pattern should be avoided if possible. It is usually // more effective to optimize that pattern at a higher level with a dedicated // hashing implementation. template auto HashArray(llvm::ArrayRef values) -> void; // Incorporates an object into the hasher's state by hashing its object // representation. Requires `value`'s type to have a unique object // representation. This is primarily useful for builtin and primitive types. // // This can be directly used for simple users combining some aggregation of // objects. However, when possible, prefer the variadic version below for // aggregating several primitive types into a hash. template requires std::has_unique_object_representations_v auto HashRaw(const T& value) -> void; // Simpler and more primitive functions to incorporate state represented in // `uint64_t` values into the hasher's state. // // These may be slightly less efficient than the `Hash` method above for a // typical application code `uint64_t`, but are designed to work well even // when relevant data has been packed into the `uint64_t` parameters densely. auto HashDense(uint64_t data) -> void; auto HashDense(uint64_t data0, uint64_t data1) -> void; // A heavily optimized routine for incorporating a dynamically sized sequence // of bytes into the hasher's state. // // This routine has carefully structured inline code paths for short byte // sequences and a reasonably high bandwidth code path for longer sequences. // The size of the byte sequence is always incorporated into the hasher's // state along with the contents. auto HashSizedBytes(llvm::ArrayRef bytes) -> void; // Incorporate a dynamically sized sequence of bytes represented as an array // of objects into the hasher's state. template requires std::has_unique_object_representations_v auto HashSizedBytes(llvm::ArrayRef data) -> void { HashSizedBytes(llvm::ArrayRef( reinterpret_cast(data.data()), data.size() * sizeof(T))); } // An out-of-line, throughput-optimized routine for incorporating a // dynamically sized sequence when the sequence size is guaranteed to be >32. // The size is always incorporated into the state. auto HashSizedBytesLarge(llvm::ArrayRef bytes) -> void; // Utility functions to read data of various sizes efficiently into a // 64-bit value. These pointers need-not be aligned, and can alias other // objects. The representation of the read data in the `uint64_t` returned is // not stable or guaranteed. static auto Read1(const std::byte* data) -> uint64_t; static auto Read2(const std::byte* data) -> uint64_t; static auto Read4(const std::byte* data) -> uint64_t; static auto Read8(const std::byte* data) -> uint64_t; // Similar to the `ReadN` functions, but supports reading a range of different // bytes provided by the size *without branching on the size*. The lack of // branches is often key, and the code in these routines works to be efficient // in extracting a *dynamic* size of bytes into the returned `uint64_t`. There // may be overlap between different routines, because these routines are based // on different implementation techniques that do have some overlap in the // range of sizes they can support. Which routine is the most efficient for a // size in the overlap isn't trivial, and so these primitives are provided // as-is and should be selected based on the localized generated code and // benchmarked performance. static auto Read1To3(const std::byte* data, ssize_t size) -> uint64_t; static auto Read4To8(const std::byte* data, ssize_t size) -> uint64_t; static auto Read8To16(const std::byte* data, ssize_t size) -> std::pair; // Reads the underlying object representation of a type into a 64-bit integer // efficiently. Only supports types with unique object representation and at // most 8-bytes large. This is typically used to read primitive types. template requires std::has_unique_object_representations_v && (sizeof(T) <= 8) static auto ReadSmall(const T& value) -> uint64_t; // The core of the hash algorithm is this mix function. The specific // operations are not guaranteed to be stable but are described here for // hashing authors to understand what to expect. // // Currently, this uses the same "mix" operation as in Abseil, AHash, and // several other hashing algorithms. It takes two 64-bit integers, and // multiplies them, capturing both the high 64-bit result and the low 64-bit // result, and then XOR-ing those two halves together. // // A consequence of this operation is that a zero on either side will fail to // incorporate any bits from the other side. Often, this is an acceptable rate // of collision in practice. But it is worth being aware of and working to // avoid common paths encountering this. For example, naively used this might // cause different length all-zero byte strings to hash the same, essentially // losing the length in the composition of the hash for a likely important // case of byte sequence. // // Another consequence of the particular implementation is that it is useful // to have a reasonable distribution of bits throughout both sides of the // multiplication. However, it is not *necessary* as we do capture the // complete 128-bit result. Where reasonable, the caller should XOR random // data into operands before calling `Mix` to try and increase the // distribution of bits feeding the multiply. static auto Mix(uint64_t lhs, uint64_t rhs) -> uint64_t; // An alternative to `Mix` that is significantly weaker but also lower // latency. It should not be used when the input `uint64_t` is densely packed // with data, but is a good option for hashing a single integer or pointer // where the full 64-bits are sparsely populated and especially the high bits // are often invariant between interestingly different values. // // This uses just the low 64-bit result of a multiply. It ensures the operand // is good at diffusing bits, but inherently the high bits of the input will // be (significantly) less often represented in the output. It also does some // reversal to ensure the *low* bits of the result are the most useful ones. static auto WeakMix(uint64_t value) -> uint64_t; // We have a 64-byte random data pool designed to fit on a single cache line. // This routine allows sampling it at byte indices, which allows getting 64 - // 8 different random 64-bit results. The offset must be in the range [0, 56). static auto SampleRandomData(ssize_t offset) -> uint64_t { CARBON_DCHECK(offset + sizeof(uint64_t) < sizeof(StaticRandomData)); uint64_t data; memcpy(&data, reinterpret_cast(&StaticRandomData) + offset, sizeof(data)); return data; } // As above, but for small offsets, we can use aligned loads, which are // faster. The offset must be in the range [0, 8). static auto SampleAlignedRandomData(ssize_t offset) -> uint64_t { CARBON_DCHECK(static_cast(offset) < sizeof(StaticRandomData) / sizeof(uint64_t)); return StaticRandomData[offset]; } // Random data taken from the hexadecimal digits of Pi's fractional component, // written in lexical order for convenience of reading. The resulting // byte-stream will be different due to little-endian integers. These can be // used directly for convenience rather than calling `SampleRandomData`, but // be aware that this is the underlying pool. The goal is to reuse the same // single cache-line of constant data. // // The initializers here can be generated with the following shell script, // which will generate 8 64-bit values and one more digit. The `bc` command's // decimal based scaling means that without getting at least some extra hex // digits rendered there will be rounding that we don't want so the script // below goes on to produce one more hex digit ensuring the 8 initializers // aren't rounded in any way. Using a higher scale won't cause the 8 // initializers here to change further. // // ```sh // echo 'obase=16; scale=155; 4*a(1)' | env BC_LINE_LENGTH=500 bc -l \ // | cut -c 3- | tr '[:upper:]' '[:lower:]' \ // | sed -e "s/.\{4\}/&'/g" \ // | sed -e "s/\(.\{4\}'.\{4\}'.\{4\}'.\{4\}\)'/0x\1,\n/g" // ``` alignas(64) static constexpr std::array StaticRandomData = { 0x243f'6a88'85a3'08d3, 0x1319'8a2e'0370'7344, 0xa409'3822'299f'31d0, 0x082e'fa98'ec4e'6c89, 0x4528'21e6'38d0'1377, 0xbe54'66cf'34e9'0c6c, 0xc0ac'29b7'c97c'50dd, 0x3f84'd5b5'b547'0917, }; // We need a multiplicative hashing constant for both 64-bit multiplicative // hashing fast paths and some other 128-bit folded multiplies. We use an // empirically better constant compared to Knuth's, Rust's FxHash, and others // we've tried. It was found by a search of uniformly distributed odd numbers // and examining them for desirable properties when used as a multiplicative // hash, however our search seems largely to have been lucky rather than // having a highly effective set of criteria. We evaluated this constant by // integrating this hash function with a hashtable and looking at the // collision rates of several different but very fundamental patterns of keys: // integers counting from 0, pointers allocated on the heap, and strings with // character and size distributions matching C-style ASCII identifiers. // Different constants found with this search worked better or less well, but // fairly consistently across the different types of keys. At the end, far and // away the best behaved constant we found was one of the first ones in the // search and is what we use here. // // For reference, some other constants include one derived by diving 2^64 by // Phi: 0x9e37'79b9'7f4a'7c15U -- see these sites for details: // https://probablydance.com/2018/06/16/fibonacci-hashing-the-optimization-that-the-world-forgot-or-a-better-alternative-to-integer-modulo/ // https://book.huihoo.com/data-structures-and-algorithms-with-object-oriented-design-patterns-in-c++/html/page214.html // // Another very good constant derived by minimizing repeating bit patterns is // 0xdcb2'2ca6'8cb1'34edU and its bit-reversed form. However, this constant // has observed frequent issues at roughly 4k pointer keys, connected to a // common hashtable seed also being a pointer. These issues appear to occur // both more often and have a larger impact relative to the number of keys // than the rare cases where some combinations of pointer seeds and pointer // keys create minor quality issues with the constant we use. static constexpr uint64_t MulConstant = 0x79d5'f9e0'de1e'8cf5U; private: uint64_t buffer; }; // A dedicated namespace for `CarbonHashValue` overloads that are not found by // ADL with their associated types. For example, primitive type overloads or // overloads for types in LLVM's libraries. // // Note that these are internal implementation details and **not** part of the // public API. They should not be used directly by client code. namespace InternalHashDispatch { template inline auto CarbonHashValue(llvm::ArrayRef values, uint64_t seed) -> HashCode { Hasher hasher(seed); hasher.HashArray(values); return static_cast(hasher); } inline auto CarbonHashValue(llvm::ArrayRef bytes, uint64_t seed) -> HashCode { Hasher hasher(seed); hasher.HashSizedBytes(bytes); return static_cast(hasher); } // Hashing implementation for `llvm::StringRef`. We forward all the other // string-like types that support heterogeneous lookup to this one. inline auto CarbonHashValue(llvm::StringRef value, uint64_t seed) -> HashCode { return CarbonHashValue( llvm::ArrayRef(reinterpret_cast(value.data()), value.size()), seed); } inline auto CarbonHashValue(std::string_view value, uint64_t seed) -> HashCode { return CarbonHashValue(llvm::StringRef(value.data(), value.size()), seed); } inline auto CarbonHashValue(const std::string& value, uint64_t seed) -> HashCode { return CarbonHashValue(llvm::StringRef(value.data(), value.size()), seed); } template inline auto CarbonHashValue(const llvm::SmallString& value, uint64_t seed) -> HashCode { return CarbonHashValue(llvm::StringRef(value.data(), value.size()), seed); } // Support types that are array-like by building an `llvm::ArrayRef` out of // them. We can't do this by accepting any type convertible to an `ArrayRef` // because that type supports building a synthetic array out of any single // element. template inline auto CarbonHashValue(const std::vector& arg, uint64_t seed) -> HashCode { return CarbonHashValue(llvm::ArrayRef(arg), seed); } template inline auto CarbonHashValue(const llvm::SmallVectorImpl& arg, uint64_t seed) -> HashCode { return CarbonHashValue(llvm::ArrayRef(arg), seed); } template inline auto CarbonHashValue(const std::array& arg, uint64_t seed) -> HashCode { return CarbonHashValue(llvm::ArrayRef(arg), seed); } template inline auto CarbonHashValue(const T (&arg)[N], uint64_t seed) -> HashCode { return CarbonHashValue(llvm::ArrayRef(arg), seed); } inline auto CarbonHashValue(llvm::APInt value, uint64_t seed) -> HashCode { Hasher hasher(seed); if (LLVM_LIKELY(value.isSingleWord())) { hasher.Hash(value.getBitWidth(), value.getZExtValue()); } else { hasher.HashRaw(value.getBitWidth()); hasher.HashSizedBytes( llvm::ArrayRef(value.getRawData(), value.getNumWords())); } return static_cast(hasher); } inline auto CarbonHashValue(llvm::APFloat value, uint64_t seed) -> HashCode { Hasher hasher(seed); // Hashing floating point numbers is complex and depends on the specific // internal semantics of `APFloat`, so delegate to the LLVM hashing framework // here. We re-hash the result to mix in our seed. All of this is a bit // inefficient, and we can revisit this to provide a dedicated implementation // if it becomes a bottleneck. using llvm::hash_value; hasher.HashRaw(hash_value(value)); return static_cast(hasher); } template inline auto CarbonHashValue(const std::tuple& value, uint64_t seed) -> HashCode { Hasher hasher(seed); std::apply([&](const auto&... args) { hasher.Hash(args...); }, value); return static_cast(hasher); } template inline auto CarbonHashValue(const std::pair& value, uint64_t seed) -> HashCode { Hasher hasher(seed); hasher.Hash(value.first, value.second); return static_cast(hasher); } // Implementation detail predicate to detect if there is a `CarbonHashValue` // overload available for a particular type, either in this namespace or found // via ADL. Note that this should not be moved above any overloads. template concept HasCarbonHashValue = requires(const T& value, uint64_t seed) { { CarbonHashValue(value, seed) } -> std::same_as; }; // C++ guarantees this is true for the unsigned variants, but we require it for // signed variants and pointers. static_assert(std::has_unique_object_representations_v); static_assert(std::has_unique_object_representations_v); static_assert(std::has_unique_object_representations_v); static_assert(std::has_unique_object_representations_v); static_assert(std::has_unique_object_representations_v); // Overloaded function to provide mappings or conversions required to types that // should be hashed as plain data but where can't directly examine the storage. // // For example, C++ uses `std::nullptr_t` but unfortunately doesn't make it have // a unique object representation. To address that, we need a function that // converts `nullptr` back into a `void*` that will have a unique object // representation. And this needs to be done by-value as we need to build a // temporary object to return, which requires a separate overload rather than // just using a type function that could be used in parallel in the predicate // below. Instead, we build the predicate independently of the mapping overload, // but together they should produce the correct result. template inline auto MapToRawDataType(const T& value) -> const T& { // This overload should never be selected for `std::nullptr_t`, so // static_assert to get some better compiler error messages. static_assert(!std::same_as); // NOLINTNEXTLINE(bugprone-return-const-ref-from-parameter) return value; } inline auto MapToRawDataType(std::nullptr_t /*value*/) -> const void* { return nullptr; } // Implementation detail predicate to detect if we can hash as a raw data type. // When used, it should be combined with our mapping function `MapToRawDataType` // to handle any necessary edge cases that don't directly work. template concept CanHashAsRawDataType = std::same_as || std::has_unique_object_representations_v; // Implementation of the unqualified dispatch to any provided `CarbonHashValue` // overloads, either here, or via ADL. Note that similar to // `HasCarbonHashValue`, this must not be moved above any of those overloads. template inline auto DispatchImpl(const T& value, uint64_t seed) -> HashCode { // If we have an explicit overload for `CarbonHashValue`, call it. This may be // provided above or via ADL, and is preferred as it represents an explicit // request for how the type is hashed. if constexpr (HasCarbonHashValue) { return CarbonHashValue(value, seed); } else if constexpr (CanHashAsRawDataType) { // There was no explicit overload to call, but the type allows us to hash it // as raw data, do so. Hasher hasher(seed); hasher.HashRaw(MapToRawDataType(value)); return static_cast(hasher); } else { // We can only synthesize hashing for types that are hashable as raw data. // This type isn't so fail a static assert due to the lack of an overload. // We use the concept here to try and get the best diagnostics we can about // candidates. static_assert(HasCarbonHashValue, "Attempted to hash a type which does not have a " "`CarbonHashValue` overload."); } } } // namespace InternalHashDispatch template inline auto HashValue(const T& value, uint64_t seed) -> HashCode { return InternalHashDispatch::DispatchImpl(value, seed); } template inline auto HashValue(const T& value) -> HashCode { // When a seed isn't provided, use the last 64-bit chunk of random data. Other // chunks (especially the first) are more often XOR-ed with the seed and risk // cancelling each other out and feeding a zero to a `Mix` call in a way that // sharply increasing collisions. return HashValue(value, Hasher::StaticRandomData[7]); } constexpr auto HashCode::ExtractIndex() -> ssize_t { return value_; } template constexpr auto HashCode::ExtractIndexAndTag() -> std::pair { static_assert(N >= 1); static_assert(N < 32); return {static_cast(value_ >> N), static_cast(value_ & ((1U << N) - 1))}; } // Building with `-DCARBON_MCA_MARKERS` will enable `llvm-mca` annotations in // the source code. These can interfere with optimization, but allows analyzing // the generated `.s` file with the `llvm-mca` tool. Documentation for these // markers is here: // https://llvm.org/docs/CommandGuide/llvm-mca.html#using-markers-to-analyze-specific-code-blocks #if CARBON_MCA_MARKERS #define CARBON_MCA_BEGIN(NAME) \ __asm volatile("# LLVM-MCA-BEGIN " NAME "" ::: "memory"); #define CARBON_MCA_END(NAME) \ __asm volatile("# LLVM-MCA-END " NAME "" ::: "memory"); #else #define CARBON_MCA_BEGIN(NAME) #define CARBON_MCA_END(NAME) #endif inline auto Hasher::Read1(const std::byte* data) -> uint64_t { uint8_t result; std::memcpy(&result, data, sizeof(result)); return result; } inline auto Hasher::Read2(const std::byte* data) -> uint64_t { uint16_t result; std::memcpy(&result, data, sizeof(result)); return result; } inline auto Hasher::Read4(const std::byte* data) -> uint64_t { uint32_t result; std::memcpy(&result, data, sizeof(result)); return result; } inline auto Hasher::Read8(const std::byte* data) -> uint64_t { uint64_t result; std::memcpy(&result, data, sizeof(result)); return result; } inline auto Hasher::Read1To3(const std::byte* data, ssize_t size) -> uint64_t { // Use carefully crafted indexing to avoid branches on the exact size while // reading. uint64_t byte0 = static_cast(data[0]); uint64_t byte1 = static_cast(data[size - 1]); uint64_t byte2 = static_cast(data[size >> 1]); return (byte0 << 8) | (byte1 << 16) | byte2; } inline auto Hasher::Read4To8(const std::byte* data, ssize_t size) -> uint64_t { uint32_t low; std::memcpy(&low, data, sizeof(low)); uint32_t high; std::memcpy(&high, data + size - sizeof(high), sizeof(high)); return (static_cast(low) << 32) | high; } inline auto Hasher::Read8To16(const std::byte* data, ssize_t size) -> std::pair { uint64_t low; std::memcpy(&low, data, sizeof(low)); uint64_t high; std::memcpy(&high, data + size - sizeof(high), sizeof(high)); return {low, high}; } inline auto Hasher::Mix(uint64_t lhs, uint64_t rhs) -> uint64_t { // Use the C23 extended integer support that Clang provides as a general // language extension. using U128 = unsigned _BitInt(128); U128 result = static_cast(lhs) * static_cast(rhs); return static_cast(result) ^ static_cast(result >> 64); } inline auto Hasher::WeakMix(uint64_t value) -> uint64_t { value *= MulConstant; #ifdef __ARM_ACLE // Arm has a fast bit-reversal that gives us the optimal distribution. value = __rbitll(value); #else // Otherwise, assume an optimized BSWAP such as x86's. That's close enough. value = __builtin_bswap64(value); #endif return value; } inline auto Hasher::HashDense(uint64_t data) -> void { // When hashing exactly one 64-bit entity use the Phi-derived constant as this // is just multiplicative hashing. The initial buffer is mixed on input to // pipeline with materializing the constant. buffer = Mix(data ^ buffer, MulConstant); } inline auto Hasher::HashDense(uint64_t data0, uint64_t data1) -> void { // When hashing two chunks of data at the same time, we XOR it with random // data to avoid common inputs from having especially bad multiplicative // effects. We also XOR in the starting buffer as seed or to chain. Note that // we don't use *consecutive* random data 64-bit values to avoid a common // compiler "optimization" of loading both 64-bit chunks into a 128-bit vector // and doing the XOR in the vector unit. The latency of extracting the data // afterward eclipses any benefit. Callers will routinely have two consecutive // data values here, but using non-consecutive keys avoids any vectorization // being tempting. // // XOR-ing both the incoming state and a random word over the second data is // done to pipeline with materializing the constants and is observed to have // better performance than XOR-ing after the mix. // // This roughly matches the mix pattern used in the larger mixing routines // from Abseil, which is a more minimal form than used in other algorithms // such as AHash and seems adequate for latency-optimized use cases. buffer = Mix(data0 ^ StaticRandomData[1], data1 ^ StaticRandomData[3] ^ buffer); } template requires std::has_unique_object_representations_v && (sizeof(T) <= 8) inline auto Hasher::ReadSmall(const T& value) -> uint64_t { const auto* storage = reinterpret_cast(&value); if constexpr (sizeof(T) == 1) { return Read1(storage); } else if constexpr (sizeof(T) == 2) { return Read2(storage); } else if constexpr (sizeof(T) == 3) { return Read2(storage) | (Read1(&storage[2]) << 16); } else if constexpr (sizeof(T) == 4) { return Read4(storage); } else if constexpr (sizeof(T) == 5) { return Read4(storage) | (Read1(&storage[4]) << 32); } else if constexpr (sizeof(T) == 6 || sizeof(T) == 7) { // Use overlapping 4-byte reads for 6 and 7 bytes. return Read4(storage) | (Read4(&storage[sizeof(T) - 4]) << 32); } else if constexpr (sizeof(T) == 8) { return Read8(storage); } else { static_assert(sizeof(T) <= 8); } } template inline auto Hasher::Hash(const Ts&... values) -> void { if constexpr (sizeof...(Ts) == 0) { buffer ^= StaticRandomData[0]; return; } using InternalHashDispatch::CanHashAsRawDataType; using InternalHashDispatch::HasCarbonHashValue; using InternalHashDispatch::MapToRawDataType; // Special-case a single element tuple that we will hash as raw data. if constexpr (sizeof...(Ts) == 1 && (... && (!HasCarbonHashValue && CanHashAsRawDataType))) { HashRaw(MapToRawDataType(values)...); return; } // Map each value into a uint64_t, either by hashing it using any custom hash // function required, reading its data into a 64-bit value, or if large // hashing it as raw data and using that hash code as the 64-bit data. This // mirrors the logic in `InternalHashDispatch::DispatchImpl`, but minimizes // early hashing of anything small we can just read as data. While this may be // a little bit wasteful in some cases, collapsing down to a flat array of // 64-bit integers is more efficient to hash. auto map_value = [](const T& value) -> uint64_t { if constexpr (HasCarbonHashValue) { // Use the top-level `HashValue` to re-dispatch to the custom // implementation with a fixed seed. return static_cast(HashValue(value)); } else if constexpr (CanHashAsRawDataType) { auto raw_value = MapToRawDataType(value); if constexpr (sizeof(raw_value) <= 8) { return ReadSmall(raw_value); } else { // Use the top-level `HashValue` to pick up a good fixed seed and hash // this large object as raw data. return static_cast(HashValue(raw_value)); } } else { // We can only synthesize hashing for types that are hashable as raw data. // This type isn't so fail a static assert due to the lack of an overload. // We use the concept here to try and get the best diagnostics we can // about candidates. static_assert(HasCarbonHashValue, "Attempted to hash a type which does not have a " "`CarbonHashValue` overload."); } }; const uint64_t data[] = {map_value(values)...}; if constexpr (sizeof...(Ts) == 2) { HashDense(data[0], data[1]); return; } HashRaw(data); } template inline auto Hasher::HashArray(llvm::ArrayRef values) -> void { using InternalHashDispatch::CanHashAsRawDataType; using InternalHashDispatch::HasCarbonHashValue; // This logic similarly mirrors `InternalHashDispatch::DispatchImpl`, but is // specialized here to allow us to efficiently process the array when it // *doesn't* require recursive hashing. if constexpr (HasCarbonHashValue) { // Use a trivial loop to give consistent behavior for arrays requiring // recursive hashing. This isn't terribly efficient, but if clients care // they should specialize the entire hashing operation. For simple, tiny // cases, this avoids an awkward functionality cliff. for (const T& value : values) { HashDense(static_cast(HashValue(value))); } HashRaw(values.size()); } else if constexpr (std::has_unique_object_representations_v) { // This code is a narrow special case for `CanHashAsRawDataType` that we can // further hash the underlying storage directly. We check that it is a // subset. static_assert(CanHashAsRawDataType); HashSizedBytes(values); } else { // We can only synthesize hashing for types that are hashable as raw data. // This type isn't so fail a static assert due to the lack of an overload. // We use the concept here to try and get the best diagnostics we can // about candidates. static_assert(HasCarbonHashValue, "Attempted to hash a type which does not have a " "`CarbonHashValue` overload."); } } template requires std::has_unique_object_representations_v inline auto Hasher::HashRaw(const T& value) -> void { if constexpr (sizeof(T) <= 8) { // For types size 8-bytes and smaller directly being hashed (as opposed to // 8-bytes potentially bit-packed with data), we rarely expect the incoming // data to fully and densely populate all 8 bytes. For these cases we have a // `WeakMix` routine that is lower latency but lower quality. CARBON_MCA_BEGIN("fixed-8b"); buffer = WeakMix(buffer ^ ReadSmall(value)); CARBON_MCA_END("fixed-8b"); return; } const auto* data_ptr = reinterpret_cast(&value); if constexpr (8 < sizeof(T) && sizeof(T) <= 16) { CARBON_MCA_BEGIN("fixed-16b"); auto values = Read8To16(data_ptr, sizeof(T)); HashDense(values.first, values.second); CARBON_MCA_END("fixed-16b"); return; } if constexpr (16 < sizeof(T) && sizeof(T) <= 32) { CARBON_MCA_BEGIN("fixed-32b"); // Essentially the same technique used for dynamically sized byte sequences // of this size, but we start with a fixed XOR of random data. buffer ^= StaticRandomData[0]; uint64_t m0 = Mix(Read8(data_ptr) ^ StaticRandomData[1], Read8(data_ptr + 8) ^ buffer); const std::byte* tail_16b_ptr = data_ptr + (sizeof(T) - 16); uint64_t m1 = Mix(Read8(tail_16b_ptr) ^ StaticRandomData[3], Read8(tail_16b_ptr + 8) ^ buffer); buffer = m0 ^ m1; CARBON_MCA_END("fixed-32b"); return; } // Hashing the size isn't relevant here, but is harmless, so fall back to a // common code path. HashSizedBytesLarge(llvm::ArrayRef(data_ptr, sizeof(T))); } inline auto Hasher::HashSizedBytes(llvm::ArrayRef bytes) -> void { const std::byte* data_ptr = bytes.data(); const ssize_t size = bytes.size(); // First handle short sequences under 8 bytes. We distribute the branches a // bit for short strings. if (size <= 8) { if (size >= 4) { CARBON_MCA_BEGIN("dynamic-8b"); uint64_t data = Read4To8(data_ptr, size); // We optimize for latency on short strings by hashing both the data and // size in a single multiply here, using the small nature of size to // sample a specific sequence of bytes with well distributed bits into one // side of the multiply. This results in a *statistically* weak hash // function, but one with very low latency. // // Note that we don't drop to the `WeakMix` routine here because we want // to use sampled random data to encode the size, which may not be as // effective without the full 128-bit folded result. buffer = Mix(data ^ buffer, SampleAlignedRandomData(size - 1)); CARBON_MCA_END("dynamic-8b"); return; } // When we only have 0-3 bytes of string, we can avoid the cost of `Mix`. // Instead, for empty strings we can just XOR some of our data against the // existing buffer. For 1-3 byte lengths we do 3 one-byte reads adjusted to // always read in-bounds without branching. Then we OR the size into the 4th // byte and use `WeakMix`. CARBON_MCA_BEGIN("dynamic-4b"); if (size == 0) { buffer ^= StaticRandomData[0]; } else { uint64_t data = Read1To3(data_ptr, size) | size << 24; buffer = WeakMix(data); } CARBON_MCA_END("dynamic-4b"); return; } if (size <= 16) { CARBON_MCA_BEGIN("dynamic-16b"); // Similar to the above, we optimize primarily for latency here and spread // the incoming data across both ends of the multiply. Note that this does // have a drawback -- any time one half of the mix function becomes zero it // will fail to incorporate any bits from the other half. However, there is // exactly 1 in 2^64 values for each side that achieve this, and only when // the size is exactly 16 -- for smaller sizes there is an overlapping byte // that makes this impossible unless the seed is *also* incredibly unlucky. // // Because this hash function makes no attempt to defend against hash // flooding, we accept this risk in order to keep the latency low. If this // becomes a non-flooding problem, we can restrict the size to <16 and send // the 16-byte case down the next tier of cost. uint64_t size_hash = SampleRandomData(size); auto data = Read8To16(data_ptr, size); buffer = Mix(data.first ^ size_hash, data.second ^ buffer); CARBON_MCA_END("dynamic-16b"); return; } if (size <= 32) { CARBON_MCA_BEGIN("dynamic-32b"); // Do two mixes of overlapping 16-byte ranges in parallel to minimize // latency. We also incorporate the size by sampling random data into the // seed before both. buffer ^= SampleRandomData(size); uint64_t m0 = Mix(Read8(data_ptr) ^ StaticRandomData[1], Read8(data_ptr + 8) ^ buffer); const std::byte* tail_16b_ptr = data_ptr + (size - 16); uint64_t m1 = Mix(Read8(tail_16b_ptr) ^ StaticRandomData[3], Read8(tail_16b_ptr + 8) ^ buffer); // Just an XOR mix at the end is quite weak here, but we prefer that for // latency over a more robust approach. Doing another mix with the size (the // way longer string hashing does) increases the latency on x86-64 // significantly (approx. 20%). buffer = m0 ^ m1; CARBON_MCA_END("dynamic-32b"); return; } HashSizedBytesLarge(bytes); } } // namespace Carbon #endif // CARBON_COMMON_HASHING_H_ ================================================ FILE: common/hashing_benchmark.cpp ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #include #include #include #include #include #include "absl/hash/hash.h" #include "absl/random/random.h" #include "common/hashing.h" #include "llvm/ADT/Hashing.h" namespace Carbon { namespace { // We want the benchmark working set to fit in the L1 cache where possible so // that the benchmark focuses on the CPU-execution costs and not memory latency. // For most CPUs we're going to care about, 16k will fit easily, and 32k will // probably fit. But we also need to include sizes for string benchmarks. This // targets 8k of entropy with each object up to 8k of size for a total of 16k. constexpr int EntropySize = 8 << 10; constexpr int EntropyObjSize = 8 << 10; // An array of random entropy with `EntropySize` bytes plus 8k. The goal is that // clients can read `EntropySize` objects of up to 8k size out of this pool by // starting at different byte offsets. static const llvm::ArrayRef entropy_bytes = []() -> llvm::ArrayRef { static llvm::SmallVector bytes; // Pad out the entropy for up to 1kb objects. bytes.resize(EntropySize + EntropyObjSize); absl::BitGen gen; for (std::byte& b : bytes) { b = static_cast(absl::Uniform(gen)); } return bytes; }(); // Based on 16k of entropy above and an L1 cache size often up to 32k, keep each // array of sizes small at 8k or 1k 8-byte sizes. constexpr int NumSizes = 1 << 10; // Selects an array of `NumSizes` sizes, witch each one in the range [0, // MaxSize). The sizes will be in a random order, but the sum of sizes will // always be the same. template static const std::array rand_sizes = []() { std::array sizes; // Build an array with a deterministic set of sizes in the // range [0, MaxSize), using the golden ratio to select well distributed // points in that range. See https://www.youtube.com/watch?v=lOIP_Z_-0Hs for // an example of why this is an effective strategy for selecting sizes in the // range. static_assert(NumSizes > 128); constexpr size_t Scale = std::max(1, MaxSize / std::numbers::phi); for (auto [i, size] : llvm::enumerate(sizes)) { size = (i * Scale) % MaxSize; } // Shuffle the sizes randomly so that there isn't any pattern of sizes // encountered and we get relatively realistic branch prediction behavior // when branching on the size. We use this approach rather than random // sizes to ensure we always have the same total size of data processed. std::shuffle(sizes.begin(), sizes.end(), absl::BitGen()); return sizes; }(); // A small helper class to synthesize random values out of our entropy pool. // This is done in a way that depends on an arbitrary input (`x`) to allow us to // create a benchmark that measures a *dependent* chain of hashes of these // values. // // `T` needs to be default constructable and reasonable to synthesize an // instance by copying random bytes into its underlying storage. // // This helper class also accumulates the number of bytes of data generated in // order to let us compute throughput measurements as well as latency // measurements. // // This helper class has the same API as the `RandStrings` helpers below so that // they can all be used as type parameters to a common benchmark routine below. template struct RandValues { size_t bytes = 0; // Get a random value. We don't need to iterate through sizes so `i` is // ignored, but we use `x` to select our entropy ensuring a dependency on `x` // for the benchmark. auto Get(ssize_t /*i*/, uint64_t x) -> T { static_assert(sizeof(T) <= EntropyObjSize); bytes += sizeof(T); T result; // Clang Tidy complains about this `memcpy` despite this being the canonical // formulation. Removing the type `T` would also remove warnings for getting // the size incorrect. // NOLINTNEXTLINE(bugprone-multi-level-implicit-pointer-conversion) memcpy(&result, &entropy_bytes[x % EntropySize], sizeof(T)); return result; } }; // A specialization to help with building pairs of values. template struct RandValues> { size_t bytes = 0; auto Get(ssize_t /*i*/, uint64_t x) -> std::pair { static_assert(sizeof(std::pair) <= EntropyObjSize); bytes += sizeof(std::pair); T result0; U result1; // Clang Tidy complains about this `memcpy` despite this being the canonical // formulation. Removing the type `T` would also remove warnings for getting // the size incorrect. // NOLINTNEXTLINE(bugprone-multi-level-implicit-pointer-conversion) memcpy(&result0, &entropy_bytes[x % EntropySize], sizeof(T)); // NOLINTNEXTLINE(bugprone-multi-level-implicit-pointer-conversion) memcpy(&result1, &entropy_bytes[x % EntropySize] + sizeof(T), sizeof(U)); return {result0, result1}; } }; // A helper class similar to `RandValues`, but for building strings rather than // values. The string content is pulled from the entropy pool. The size can be // random from [0, MaxSize], or it can be fixed at `MaxSize`. But the `MaxSize` // cannot be larger than a single byte sequence pulled from the entropy pool // (`EntropyObjSize`). template struct RandStrings { size_t bytes = 0; // Get a random string. If the sizes are random, we use `i` to select each // size and require it to be in the range [0, NumSizes). Otherwise `i` is // ignored. We always use `x` to select the entropy and establish a dependency // on the input. auto Get(ssize_t i, uint64_t x) -> llvm::StringRef { static_assert(MaxSize <= EntropyObjSize); size_t s = MaxSize; if constexpr (RandSize) { // When using random sizes, we leverage `i` which is guaranteed to range // from [0, NumSizes). s = rand_sizes[i]; } else { // Prevent `s` from being constant folded when we directly use `MaxSize`. benchmark::DoNotOptimize(s); } bytes += s; return llvm::StringRef( reinterpret_cast(&entropy_bytes[x % EntropySize]), s); } }; struct HashBenchBase { uint64_t seed; HashBenchBase() { // The real-world use case we care about is in a hash table where we'll mix // in some seed state, likely some ASLR address. To simulate this for // benchmarking, compute a seed from the address of a stack local variable. volatile char key; key = 42; // Rinse this through a volatile variable as well so returning it isn't // flagged. The whole point is to escape the address of something on the // stack. volatile auto key_addr = reinterpret_cast(&key); seed = key_addr; } }; struct CarbonHashBench : HashBenchBase { template auto operator()(const T& value) -> uint64_t { return static_cast(HashValue(value, seed)); } }; struct AbseilHashBench : HashBenchBase { template auto operator()(const T& value) -> uint64_t { // Manually seed this with an after-the-fact XOR as there isn't a seeded // version. This matches what Abseil's hash tables do as well. return absl::HashOf(value) ^ seed; } }; struct LLVMHashBench : HashBenchBase { template auto operator()(const T& value) -> uint64_t { // Manually seed this with an after-the-fact XOR as there isn't a seeded // version. return llvm::hash_value(value) ^ seed; } }; template auto BM_LatencyHash(benchmark::State& state) -> void { uint64_t x = 13; Values v; Hasher h; // We run the benchmark in `NumSizes` batches so that when needed we always // process each of the sizes and we don't randomly end up with a skewed set of // sizes. while (state.KeepRunningBatch(NumSizes)) { for (ssize_t i = 0; i < NumSizes; ++i) { benchmark::DoNotOptimize(x = h(v.Get(i, x))); } } state.SetBytesProcessed(v.bytes); } // Latency benchmarks are grouped by the three different hash functions to // facilitate comparing their performance for a given value type or string size // bucket. #define LATENCY_VALUE_BENCHMARKS(...) \ BENCHMARK(BM_LatencyHash, CarbonHashBench>); \ BENCHMARK(BM_LatencyHash, AbseilHashBench>); \ BENCHMARK(BM_LatencyHash, LLVMHashBench>) LATENCY_VALUE_BENCHMARKS(uint8_t); LATENCY_VALUE_BENCHMARKS(uint16_t); LATENCY_VALUE_BENCHMARKS(std::pair); LATENCY_VALUE_BENCHMARKS(uint32_t); LATENCY_VALUE_BENCHMARKS(std::pair); LATENCY_VALUE_BENCHMARKS(uint64_t); LATENCY_VALUE_BENCHMARKS(int*); LATENCY_VALUE_BENCHMARKS(std::pair); LATENCY_VALUE_BENCHMARKS(std::pair); LATENCY_VALUE_BENCHMARKS(std::pair); LATENCY_VALUE_BENCHMARKS(std::pair); LATENCY_VALUE_BENCHMARKS(std::pair); LATENCY_VALUE_BENCHMARKS(__uint128_t); LATENCY_VALUE_BENCHMARKS(std::pair); LATENCY_VALUE_BENCHMARKS(std::pair); LATENCY_VALUE_BENCHMARKS(std::pair); LATENCY_VALUE_BENCHMARKS(std::pair); #define LATENCY_STRING_BENCHMARKS(MaxSize) \ BENCHMARK(BM_LatencyHash, \ CarbonHashBench>); \ BENCHMARK(BM_LatencyHash, \ AbseilHashBench>); \ BENCHMARK( \ BM_LatencyHash, LLVMHashBench>) LATENCY_STRING_BENCHMARKS(/*MaxSize=*/4); LATENCY_STRING_BENCHMARKS(/*MaxSize=*/8); LATENCY_STRING_BENCHMARKS(/*MaxSize=*/16); LATENCY_STRING_BENCHMARKS(/*MaxSize=*/32); LATENCY_STRING_BENCHMARKS(/*MaxSize=*/64); LATENCY_STRING_BENCHMARKS(/*MaxSize=*/256); LATENCY_STRING_BENCHMARKS(/*MaxSize=*/512); LATENCY_STRING_BENCHMARKS(/*MaxSize=*/1024); LATENCY_STRING_BENCHMARKS(/*MaxSize=*/2048); LATENCY_STRING_BENCHMARKS(/*MaxSize=*/4096); LATENCY_STRING_BENCHMARKS(/*MaxSize=*/8192); // We also want to check for size-specific cliffs, particularly in small sizes // and sizes around implementation inflection points such as powers of two and // half-way points between powers of two. Because these benchmarks are looking // for size-related cliffs, all the runs for particular hash function are kept // together. // // Note: because these use a fixed size, their specific timing isn't terribly // informative. The branch predictor behavior on a modern CPU will be // significantly different in this benchmarks from any other and may distort all // manner of the timings. The results should really only be compared between // sizes for cliffs, and not directly compared with other numbers. #define LATENCY_STRING_SIZE_BENCHMARKS(Hash) \ BENCHMARK(BM_LatencyHash, Hash>); \ BENCHMARK(BM_LatencyHash, Hash>); \ BENCHMARK(BM_LatencyHash, Hash>); \ BENCHMARK(BM_LatencyHash, Hash>); \ BENCHMARK(BM_LatencyHash, Hash>); \ BENCHMARK(BM_LatencyHash, Hash>); \ BENCHMARK(BM_LatencyHash, Hash>); \ BENCHMARK(BM_LatencyHash, Hash>); \ BENCHMARK(BM_LatencyHash, Hash>); \ BENCHMARK(BM_LatencyHash, Hash>); \ BENCHMARK(BM_LatencyHash, Hash>); \ BENCHMARK(BM_LatencyHash, Hash>); \ BENCHMARK(BM_LatencyHash, Hash>); \ BENCHMARK(BM_LatencyHash, Hash>); \ BENCHMARK(BM_LatencyHash, Hash>); \ BENCHMARK(BM_LatencyHash, Hash>); \ BENCHMARK(BM_LatencyHash, Hash>); \ BENCHMARK(BM_LatencyHash, Hash>); \ BENCHMARK(BM_LatencyHash, Hash>); \ BENCHMARK(BM_LatencyHash, Hash>); \ BENCHMARK(BM_LatencyHash, Hash>); \ BENCHMARK(BM_LatencyHash, Hash>); \ BENCHMARK(BM_LatencyHash, Hash>); \ BENCHMARK(BM_LatencyHash, Hash>); \ BENCHMARK(BM_LatencyHash, Hash>); \ BENCHMARK(BM_LatencyHash, Hash>); \ BENCHMARK(BM_LatencyHash, Hash>); \ BENCHMARK(BM_LatencyHash, Hash>); \ BENCHMARK(BM_LatencyHash, Hash>); \ BENCHMARK(BM_LatencyHash, Hash>); \ BENCHMARK(BM_LatencyHash, Hash>) // Because these just look for size-related cliffs in performance, we only do a // minimal number of benchmarks. There are a lot of sizes so this avoids wasted // time in benchmark runs and there isn't much value from greater comparative // coverage here. LATENCY_STRING_SIZE_BENCHMARKS(CarbonHashBench); LATENCY_STRING_SIZE_BENCHMARKS(AbseilHashBench); } // namespace } // namespace Carbon ================================================ FILE: common/hashing_test.cpp ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #include "common/hashing.h" #include #include #include #include #include #include #include #include "common/raw_string_ostream.h" #include "llvm/ADT/Sequence.h" #include "llvm/ADT/StringExtras.h" #include "llvm/Support/FormatVariadic.h" #include "llvm/Support/TypeName.h" namespace Carbon { namespace { using ::testing::Eq; using ::testing::Le; using ::testing::Ne; TEST(HashingTest, HashCodeApi) { // Manually compute a few hash codes where we can exercise the underlying API. HashCode empty = HashValue(""); HashCode a = HashValue("a"); HashCode b = HashValue("b"); ASSERT_THAT(HashValue(""), Eq(empty)); ASSERT_THAT(HashValue("a"), Eq(a)); ASSERT_THAT(HashValue("b"), Eq(b)); ASSERT_THAT(empty, Ne(a)); ASSERT_THAT(empty, Ne(b)); ASSERT_THAT(a, Ne(b)); // Exercise the methods in basic ways across a few sizes. This doesn't check // much beyond stability across re-computed values, crashing, or hitting UB. EXPECT_THAT(HashValue("a").ExtractIndex(), Eq(a.ExtractIndex())); EXPECT_THAT(a.ExtractIndex(), Ne(b.ExtractIndex())); EXPECT_THAT(a.ExtractIndex(), Ne(empty.ExtractIndex())); // The tag shouldn't have bits set outside the range requested. EXPECT_THAT(HashValue("a").ExtractIndexAndTag<1>().second & ~0b1, Eq(0)); EXPECT_THAT(HashValue("a").ExtractIndexAndTag<2>().second & ~0b11, Eq(0)); EXPECT_THAT(HashValue("a").ExtractIndexAndTag<3>().second & ~0b111, Eq(0)); EXPECT_THAT(HashValue("a").ExtractIndexAndTag<4>().second & ~0b1111, Eq(0)); // Note that the index produced with a tag may be different from the index // alone! EXPECT_THAT(HashValue("a").ExtractIndexAndTag<2>(), Eq(a.ExtractIndexAndTag<2>())); EXPECT_THAT(HashValue("a").ExtractIndexAndTag<16>(), Eq(a.ExtractIndexAndTag<16>())); EXPECT_THAT(HashValue("a").ExtractIndexAndTag<7>(), Eq(a.ExtractIndexAndTag<7>())); const auto [a_index, a_tag] = a.ExtractIndexAndTag<4>(); const auto [b_index, b_tag] = b.ExtractIndexAndTag<4>(); EXPECT_THAT(a_index, Ne(b_index)); EXPECT_THAT(a_tag, Ne(b_tag)); } TEST(HashingTest, Integers) { for (int64_t i : {0, 1, 2, 3, 42, -1, -2, -3, -13}) { SCOPED_TRACE(llvm::formatv("Hashing: {0}", i).str()); auto test_int_hash = [](auto i) { using T = decltype(i); SCOPED_TRACE( llvm::formatv("Hashing type: {0}", llvm::getTypeName()).str()); HashCode hash = HashValue(i); // Hashes should be stable within the execution. EXPECT_THAT(HashValue(i), Eq(hash)); // Zero should match, and other integers shouldn't collide trivially. HashCode hash_zero = HashValue(static_cast(0)); if (i == 0) { EXPECT_THAT(hash, Eq(hash_zero)); } else { EXPECT_THAT(hash, Ne(hash_zero)); } }; test_int_hash(static_cast(i)); test_int_hash(static_cast(i)); test_int_hash(static_cast(i)); test_int_hash(static_cast(i)); test_int_hash(static_cast(i)); test_int_hash(static_cast(i)); // `i` is already an int64_t variable. test_int_hash(i); test_int_hash(static_cast(i)); } } TEST(HashingTest, BasicSeeding) { auto unseeded_hash = HashValue(42); EXPECT_THAT(unseeded_hash, Ne(HashValue(42, 1))); EXPECT_THAT(unseeded_hash, Ne(HashValue(42, 2))); EXPECT_THAT(unseeded_hash, Ne(HashValue(42, 3))); EXPECT_THAT(unseeded_hash, Ne(HashValue(42, static_cast(unseeded_hash)))); } TEST(HashingTest, Pointers) { int object1 = 42; std::string object2 = "Hello World! This is a long-ish string so it ends up on the heap!"; HashCode hash_null = HashValue(nullptr); // Hashes should be stable. EXPECT_THAT(HashValue(nullptr), Eq(hash_null)); // Hash other kinds of pointers without trivial collisions. HashCode hash1 = HashValue(&object1); HashCode hash2 = HashValue(&object2); HashCode hash3 = HashValue(object2.data()); EXPECT_THAT(hash1, Ne(hash_null)); EXPECT_THAT(hash2, Ne(hash_null)); EXPECT_THAT(hash3, Ne(hash_null)); EXPECT_THAT(hash1, Ne(hash2)); EXPECT_THAT(hash1, Ne(hash3)); EXPECT_THAT(hash2, Ne(hash3)); // Hash values reflect the address and not the type. EXPECT_THAT(HashValue(static_cast(nullptr)), Eq(hash_null)); EXPECT_THAT(HashValue(static_cast(nullptr)), Eq(hash_null)); EXPECT_THAT(HashValue(static_cast(nullptr)), Eq(hash_null)); EXPECT_THAT(HashValue(reinterpret_cast(&object1)), Eq(hash1)); EXPECT_THAT(HashValue(reinterpret_cast(&object2)), Eq(hash2)); EXPECT_THAT(HashValue(reinterpret_cast(object2.data())), Eq(hash3)); } TEST(HashingTest, PairsAndTuples) { // Note that we can't compare hash codes across arity, or in general, compare // hash codes for different types as the type isn't part of the hash. These // hashes are targeted at use in hash tables which pick a single type that's // the basis of any comparison. HashCode hash_00 = HashValue(std::pair(0, 0)); HashCode hash_01 = HashValue(std::pair(0, 1)); HashCode hash_10 = HashValue(std::pair(1, 0)); HashCode hash_11 = HashValue(std::pair(1, 1)); EXPECT_THAT(hash_00, Ne(hash_01)); EXPECT_THAT(hash_00, Ne(hash_10)); EXPECT_THAT(hash_00, Ne(hash_11)); EXPECT_THAT(hash_01, Ne(hash_10)); EXPECT_THAT(hash_01, Ne(hash_11)); EXPECT_THAT(hash_10, Ne(hash_11)); HashCode hash_000 = HashValue(std::tuple(0, 0, 0)); HashCode hash_001 = HashValue(std::tuple(0, 0, 1)); HashCode hash_010 = HashValue(std::tuple(0, 1, 0)); HashCode hash_011 = HashValue(std::tuple(0, 1, 1)); HashCode hash_100 = HashValue(std::tuple(1, 0, 0)); HashCode hash_101 = HashValue(std::tuple(1, 0, 1)); HashCode hash_110 = HashValue(std::tuple(1, 1, 0)); HashCode hash_111 = HashValue(std::tuple(1, 1, 1)); EXPECT_THAT(hash_000, Ne(hash_001)); EXPECT_THAT(hash_000, Ne(hash_010)); EXPECT_THAT(hash_000, Ne(hash_011)); EXPECT_THAT(hash_000, Ne(hash_100)); EXPECT_THAT(hash_000, Ne(hash_101)); EXPECT_THAT(hash_000, Ne(hash_110)); EXPECT_THAT(hash_000, Ne(hash_111)); EXPECT_THAT(hash_001, Ne(hash_010)); EXPECT_THAT(hash_001, Ne(hash_011)); EXPECT_THAT(hash_001, Ne(hash_100)); EXPECT_THAT(hash_001, Ne(hash_101)); EXPECT_THAT(hash_001, Ne(hash_110)); EXPECT_THAT(hash_001, Ne(hash_111)); EXPECT_THAT(hash_010, Ne(hash_011)); EXPECT_THAT(hash_010, Ne(hash_100)); EXPECT_THAT(hash_010, Ne(hash_101)); EXPECT_THAT(hash_010, Ne(hash_110)); EXPECT_THAT(hash_010, Ne(hash_111)); EXPECT_THAT(hash_011, Ne(hash_100)); EXPECT_THAT(hash_011, Ne(hash_101)); EXPECT_THAT(hash_011, Ne(hash_110)); EXPECT_THAT(hash_011, Ne(hash_111)); EXPECT_THAT(hash_100, Ne(hash_101)); EXPECT_THAT(hash_100, Ne(hash_110)); EXPECT_THAT(hash_100, Ne(hash_111)); EXPECT_THAT(hash_101, Ne(hash_110)); EXPECT_THAT(hash_101, Ne(hash_111)); EXPECT_THAT(hash_110, Ne(hash_111)); // Hashing a 2-tuple and a pair should produce identical results, so pairs // are compatible with code using things like variadic tuple construction. EXPECT_THAT(HashValue(std::tuple(0, 0)), Eq(hash_00)); EXPECT_THAT(HashValue(std::tuple(0, 1)), Eq(hash_01)); EXPECT_THAT(HashValue(std::tuple(1, 0)), Eq(hash_10)); EXPECT_THAT(HashValue(std::tuple(1, 1)), Eq(hash_11)); // Integers in tuples should also work. for (int i : {0, 1, 2, 3, 42, -1, -2, -3, -13}) { SCOPED_TRACE(llvm::formatv("Hashing: ({0}, {0}, {0})", i).str()); auto test_int_tuple_hash = [](auto i) { using T = decltype(i); SCOPED_TRACE( llvm::formatv("Hashing integer type: {0}", llvm::getTypeName()) .str()); std::tuple v = {i, i, i}; HashCode hash = HashValue(v); // Hashes should be stable within the execution. EXPECT_THAT(HashValue(v), Eq(hash)); // Zero should match, and other integers shouldn't collide trivially. T zero = 0; std::tuple zero_tuple = {zero, zero, zero}; HashCode hash_zero = HashValue(zero_tuple); if (i == 0) { EXPECT_THAT(hash, Eq(hash_zero)); } else { EXPECT_THAT(hash, Ne(hash_zero)); } }; test_int_tuple_hash(i); test_int_tuple_hash(static_cast(i)); test_int_tuple_hash(static_cast(i)); test_int_tuple_hash(static_cast(i)); test_int_tuple_hash(static_cast(i)); test_int_tuple_hash(static_cast(i)); test_int_tuple_hash(static_cast(i)); test_int_tuple_hash(static_cast(i)); test_int_tuple_hash(static_cast(i)); // Heterogeneous integer types should also work, but we only support // comparing against hashes of tuples with the exact same type. using T1 = std::tuple; using T2 = std::tuple; if (i == 0) { EXPECT_THAT(HashValue(T1{i, i, i}), Eq(HashValue(T1{0, 0, 0}))); EXPECT_THAT(HashValue(T2{i, i, i}), Eq(HashValue(T2{0, 0, 0}))); } else { EXPECT_THAT(HashValue(T1{i, i, i}), Ne(HashValue(T1{0, 0, 0}))); EXPECT_THAT(HashValue(T2{i, i, i}), Ne(HashValue(T2{0, 0, 0}))); } } // Hash values of pointers in pairs and tuples reflect the address and not the // type. Pairs and 2-tuples give the same hash values. HashCode hash_2null = HashValue(std::pair(nullptr, nullptr)); EXPECT_THAT(HashValue(std::tuple(static_cast(nullptr), static_cast(nullptr))), Eq(hash_2null)); // Hash other kinds of pointers without trivial collisions. int object1 = 42; std::string object2 = "Hello world!"; HashCode hash_3ptr = HashValue(std::tuple(&object1, &object2, object2.data())); EXPECT_THAT(hash_3ptr, Ne(HashValue(std::tuple(nullptr, nullptr, nullptr)))); // Hash values reflect the address and not the type. EXPECT_THAT( HashValue(std::tuple(reinterpret_cast(&object1), reinterpret_cast(&object2), reinterpret_cast(object2.data()))), Eq(hash_3ptr)); } TEST(HashingTest, BasicStrings) { llvm::SmallVector> hashes; for (int size : {0, 1, 2, 4, 16, 64, 256, 1024}) { std::string s(size, 'a'); hashes.push_back({s, HashValue(s)}); } for (const auto& [s1, hash1] : hashes) { EXPECT_THAT(HashValue(s1), Eq(hash1)); // Also check that we get the same hashes even when using string-wrapping // types. EXPECT_THAT(HashValue(std::string_view(s1)), Eq(hash1)); EXPECT_THAT(HashValue(llvm::StringRef(s1)), Eq(hash1)); // And some basic tests that simple things don't collide. for (const auto& [s2, hash2] : hashes) { if (s1 != s2) { EXPECT_THAT(hash1, Ne(hash2)) << "Matching hashes for '" << s1 << "' and '" << s2 << "'"; } } } } TEST(HashingTest, ArrayLike) { int c_array[] = {1, 2, 3, 4}; EXPECT_THAT(HashValue(c_array), Eq(HashValue(c_array))); EXPECT_THAT(HashValue(std::array{1, 2, 3, 4}), Eq(HashValue(c_array))); EXPECT_THAT(HashValue(std::vector{1, 2, 3, 4}), Eq(HashValue(c_array))); EXPECT_THAT(HashValue(llvm::SmallVector{1, 2, 3, 4}), Eq(HashValue(c_array))); } TEST(HashingTest, HashAPInt) { // The bit width should be hashed as well as the value. llvm::APInt one_64(/*numBits=*/64, /*val=*/1); llvm::APInt two_64(/*numBits=*/64, /*val=*/2); llvm::APInt one_128(/*numBits=*/128, /*val=*/1); llvm::APInt two_128(/*numBits=*/128, /*val=*/2); std::array array = {one_64, two_64, one_128, two_128}; for (int i : llvm::seq(array.size())) { EXPECT_THAT(HashValue(array[i]), Eq(HashValue(array[i]))); for (int j : llvm::seq(i + 1, array.size())) { EXPECT_THAT(HashValue(array[i]), Ne(HashValue(array[j]))) << "Hashing #" << i << " and #" << j; } } } TEST(HashingTest, HashAPFloat) { // Hashtable equality for `APFloat` uses a bitwise comparison. This // differentiates between various things that would otherwise not make sense: // - Different floating point semantics // - `-0.0` and `0.0` // // It also allows NaNs to be compared meaningfully. llvm::APFloat zero_float = llvm::APFloat::getZero(llvm::APFloat::IEEEsingle()); llvm::APFloat neg_zero_float = llvm::APFloat::getZero(llvm::APFloat::IEEEsingle(), /*Negative=*/true); llvm::APFloat zero_double = llvm::APFloat::getZero(llvm::APFloat::IEEEdouble()); llvm::APFloat zero_bfloat = llvm::APFloat::getZero(llvm::APFloat::BFloat()); llvm::APFloat one_float = llvm::APFloat::getOne(llvm::APFloat::IEEEsingle()); llvm::APFloat inf_float = llvm::APFloat::getInf(llvm::APFloat::IEEEsingle()); llvm::APFloat nan_0_float = llvm::APFloat::getNaN( llvm::APFloat::IEEEsingle(), /*Negative=*/false, /*payload=*/0); llvm::APFloat nan_42_float = llvm::APFloat::getNaN( llvm::APFloat::IEEEsingle(), /*Negative=*/false, /*payload=*/42); std::array array = {zero_float, neg_zero_float, zero_double, zero_bfloat, one_float, inf_float, nan_42_float}; for (int i : llvm::seq(array.size())) { EXPECT_THAT(HashValue(array[i]), Eq(HashValue(array[i]))); for (int j : llvm::seq(i + 1, array.size())) { EXPECT_THAT(HashValue(array[i]), Ne(HashValue(array[j]))) << "Hashing #" << i << " and #" << j; } } // Note that currently we use LLVM's hashing of `APFloat` which does *not* // hash the payload of NaNs. EXPECT_THAT(HashValue(nan_0_float), Eq(HashValue(nan_42_float))); } // A type that has hashing customization. However, it also works to be small and // appear to have a unique object representation. This helps ensure that when a // user provides custom hashing it is reliably used. struct HashableType { int8_t x; int8_t y; int16_t ignored = 0; // Provide the hashing but try to craft a relatively low-ranking overload to // help ensure that the hashing framework doesn't accidentally override this. template requires(std::same_as) friend auto CarbonHashValue(const T& value, uint64_t seed) -> HashCode { Hasher hasher(seed); hasher.Hash(value.x, value.y); return static_cast(hasher); } }; static_assert(std::has_unique_object_representations_v); TEST(HashingTest, CustomType) { HashableType a = {.x = 1, .y = 2}; HashableType b = {.x = 3, .y = 4}; EXPECT_THAT(HashValue(a), Eq(HashValue(a))); EXPECT_THAT(HashValue(a), Ne(HashValue(b))); // Differences in an ignored field have no impact. HashableType c = {.x = 3, .y = 4, .ignored = 42}; EXPECT_THAT(HashValue(c), Eq(HashValue(b))); } TEST(HashingTest, ArrayRecursion) { // Make sure we correctly recurse when hashing an array and don't try to use // the object representation. llvm::APInt one_64(/*numBits=*/64, /*val=*/1); llvm::APInt two_64(/*numBits=*/64, /*val=*/2); llvm::APInt one_128(/*numBits=*/128, /*val=*/1); llvm::APInt two_128(/*numBits=*/128, /*val=*/2); std::array apint_array = {one_64, two_64, one_128, two_128}; EXPECT_THAT(HashValue(apint_array), Eq(HashValue(std::array{one_64, two_64, one_128, two_128}))); EXPECT_THAT(HashValue(apint_array), Ne(HashValue(std::array{one_64, two_64, two_128, one_128}))); EXPECT_THAT(HashValue(apint_array), Ne(HashValue(std::array{one_64, two_64, one_64, two_128}))); EXPECT_THAT(HashValue(apint_array), Ne(HashValue(std::array{one_64, two_128, one_128, two_128}))); EXPECT_THAT(HashValue(apint_array), Ne(HashValue(std::array{one_64, two_64, one_128}))); EXPECT_THAT( HashValue(apint_array), Ne(HashValue(std::array{one_64, two_64, one_128, two_128, two_128}))); // Also test for a custom type that still *looks* like plain data. HashableType a = {.x = 1, .y = 2}; HashableType b = {.x = 3, .y = 4}; HashableType c = {.x = 3, .y = 4, .ignored = 42}; std::array custom_array = {a, b, c, a}; EXPECT_THAT(HashValue(custom_array), Eq(HashValue(std::array{a, b, c, a}))); EXPECT_THAT(HashValue(custom_array), Eq(HashValue(std::array{a, b, b, a}))); EXPECT_THAT(HashValue(custom_array), Ne(HashValue(std::array{a, b, c, b}))); EXPECT_THAT(HashValue(custom_array), Ne(HashValue(std::array{a, b, a, c}))); EXPECT_THAT(HashValue(custom_array), Ne(HashValue(std::array{a, b, c}))); EXPECT_THAT(HashValue(custom_array), Ne(HashValue(std::array{a, b, c, a, a}))); } TEST(HashingTest, TupleRecursion) { // Make sure we can hash pairs and tuples which require us to recurse for each // element rather than treating the whole object as raw storage. // We can use APInt values to help test this. llvm::APInt one_64(/*numBits=*/64, /*val=*/1); llvm::APInt two_64(/*numBits=*/64, /*val=*/2); llvm::APInt one_128(/*numBits=*/128, /*val=*/1); llvm::APInt two_128(/*numBits=*/128, /*val=*/2); EXPECT_THAT(HashValue(std::pair{one_64, one_128}), Eq(HashValue(std::pair{one_64, one_128}))); EXPECT_THAT(HashValue(std::pair{one_64, one_128}), Ne(HashValue(std::pair{one_64, two_64}))); EXPECT_THAT(HashValue(std::pair{one_64, one_128}), Ne(HashValue(std::pair{one_64, one_64}))); EXPECT_THAT(HashValue(std::pair{one_64, one_128}), Ne(HashValue(std::pair{one_128, one_64}))); EXPECT_THAT(HashValue(std::tuple{one_64, one_128, two_64}), Eq(HashValue(std::tuple{one_64, one_128, two_64}))); EXPECT_THAT(HashValue(std::tuple{one_64, one_128, two_64}), Ne(HashValue(std::tuple{one_64, two_64, two_64}))); EXPECT_THAT(HashValue(std::tuple{one_64, one_128, two_64}), Ne(HashValue(std::tuple{one_64, one_64, two_64}))); EXPECT_THAT(HashValue(std::tuple{one_64, one_128, two_64}), Ne(HashValue(std::tuple{one_64, two_64, one_128}))); EXPECT_THAT(HashValue(std::tuple{one_64, one_128, two_64}), Ne(HashValue(std::tuple{one_64, one_128}))); // Also test for a custom type that still *looks* like plain data. HashableType a = {.x = 1, .y = 2}; HashableType b = {.x = 3, .y = 4}; HashableType c = {.x = 3, .y = 4, .ignored = 42}; EXPECT_THAT(HashValue(std::pair{a, b}), Eq(HashValue(std::pair{a, b}))); EXPECT_THAT(HashValue(std::pair{a, b}), Ne(HashValue(std::pair{a, a}))); EXPECT_THAT(HashValue(std::pair{a, b}), Ne(HashValue(std::pair{b, a}))); EXPECT_THAT(HashValue(std::pair{a, b}), Eq(HashValue(std::pair{a, c}))); EXPECT_THAT(HashValue(std::tuple{a, b, a}), Eq(HashValue(std::tuple{a, b, a}))); EXPECT_THAT(HashValue(std::tuple{a, b, a}), Ne(HashValue(std::tuple{a, b, b}))); EXPECT_THAT(HashValue(std::tuple{a, b, a}), Ne(HashValue(std::tuple{a, a, a}))); EXPECT_THAT(HashValue(std::tuple{a, b, a}), Eq(HashValue(std::tuple{a, c, a}))); } // The only significantly bad seed is zero, so pick a non-zero seed with a tiny // amount of entropy to make sure that none of the testing relies on the entropy // from this. constexpr uint64_t TestSeed = 42ULL * 1024; auto ToHexBytes(llvm::StringRef s) -> std::string { RawStringOstream rendered; rendered << "{"; llvm::ListSeparator sep(", "); for (const char c : s) { rendered << sep << llvm::formatv("{0:x2}", static_cast(c)); } rendered << "}"; return rendered.TakeStr(); } template struct HashedValue { HashCode hash; T v; }; using HashedString = HashedValue; template auto PrintFullWidthHex(llvm::raw_ostream& os, T value) { static_assert(sizeof(T) == 1 || sizeof(T) == 2 || sizeof(T) == 4 || sizeof(T) == 8); // Given the nature of a format string and the good formatting, a nested // conditional seems like the most readable structure. // NOLINTBEGIN(readability-avoid-nested-conditional-operator) os << llvm::formatv(sizeof(T) == 1 ? "{0:x2}" : sizeof(T) == 2 ? "{0:x4}" : sizeof(T) == 4 ? "{0:x8}" : "{0:x16}", static_cast(value)); // NOLINTEND(readability-avoid-nested-conditional-operator) } template requires std::integral auto operator<<(llvm::raw_ostream& os, HashedValue hv) -> llvm::raw_ostream& { os << "hash " << hv.hash << " for value "; PrintFullWidthHex(os, hv.v); return os; } template requires std::integral && std::integral auto operator<<(llvm::raw_ostream& os, HashedValue> hv) -> llvm::raw_ostream& { os << "hash " << hv.hash << " for pair of "; PrintFullWidthHex(os, hv.v.first); os << " and "; PrintFullWidthHex(os, hv.v.second); return os; } struct Collisions { int total; int median; int max; }; // Analyzes a list of hashed values to find all of the hash codes which collide // within a specific bit-range. // // With `BitBegin=0` and `BitEnd=64`, this is equivalent to finding full // collisions. But when the begin and end of the bit range are narrower than the // 64-bits of the hash code, it allows this function to analyze a specific // window of bits within the 64-bit hash code to understand how many collisions // emerge purely within that bit range. // // With narrow ranges (we often look at the first N and last N bits for small // N), collisions are common and so this function summarizes this with the total // number of collisions and the median number of collisions for an input value. template auto FindBitRangeCollisions(llvm::ArrayRef> hashes) -> Collisions { static_assert(BitBegin < BitEnd); constexpr int BitCount = BitEnd - BitBegin; static_assert(BitCount <= 32); constexpr int BitShift = BitBegin; constexpr uint64_t BitMask = ((1ULL << BitCount) - 1) << BitShift; // We collect counts of collisions in a vector. Initially, we just have a zero // and all inputs map to that collision count. As we discover collisions, // we'll create a dedicated counter for it and count how many inputs collide. llvm::SmallVector collision_counts; collision_counts.push_back(0); // The "map" for collision counts. Each input hashed value has a corresponding // index stored here. That index is the index of the collision count in the // container above. We resize this to fill it with zeros to start as the zero // index above has a collision count of zero. // // The result of this is that the number of collisions for `hashes[i]` is // `collision_counts[collision_map[i]]`. llvm::SmallVector collision_map; collision_map.resize(hashes.size()); // First, we extract the bit subsequence we want to examine from each hash and // store it with an index back into the hashed values (or the collision map). // // The result is that, `bits_and_indices[i].bits` has the hash bits of // interest from `hashes[bits_and_indices[i].index]`. // // And because `collision_map` above uses the same indices as `hashes`, // `collision_counts[collision_map[bits_and_indices[i].index]]` is the number // of collisions for `bits_and_indices[i].bits`. struct BitSequenceAndHashIndex { // The bit subsequence of a hash input, adjusted into the low bits. uint32_t bits; // The index of the hash input corresponding to this bit sequence. int index; }; llvm::SmallVector bits_and_indices; bits_and_indices.reserve(hashes.size()); for (const auto& [hash, v] : hashes) { CARBON_DCHECK(v == hashes[bits_and_indices.size()].v); auto hash_bits = (static_cast(hash) & BitMask) >> BitShift; bits_and_indices.push_back( {.bits = static_cast(hash_bits), .index = static_cast(bits_and_indices.size())}); } // Now we sort by the extracted bit sequence so we can efficiently scan for // colliding bit patterns. llvm::sort(bits_and_indices, [](const auto& lhs, const auto& rhs) { return lhs.bits < rhs.bits; }); // Scan the sorted bit sequences we've extracted looking for collisions. We // count the total collisions, but we also track the number of individual // inputs that collide with each specific bit pattern. uint32_t prev_hash_bits = bits_and_indices[0].bits; int prev_index = bits_and_indices[0].index; bool in_collision = false; int total = 0; for (const auto& [hash_bits, hash_index] : llvm::ArrayRef(bits_and_indices).slice(1)) { // Check if we've found a new hash (and thus a new value), reset everything. CARBON_CHECK(hashes[prev_index].v != hashes[hash_index].v); if (hash_bits != prev_hash_bits) { CARBON_CHECK(hashes[prev_index].hash != hashes[hash_index].hash); prev_hash_bits = hash_bits; prev_index = hash_index; in_collision = false; continue; } // Otherwise, we have a colliding bit sequence. ++total; // If we've already created a collision count to track this, just increment // it and map this hash to it. if (in_collision) { ++collision_counts.back(); collision_map[hash_index] = collision_counts.size() - 1; continue; } // If this is a new collision, create a dedicated count to track it and // begin counting. in_collision = true; collision_map[prev_index] = collision_counts.size(); collision_map[hash_index] = collision_counts.size(); collision_counts.push_back(1); } // Sort by collision count for each hash. llvm::sort(bits_and_indices, [&](const auto& lhs, const auto& rhs) { return collision_counts[collision_map[lhs.index]] < collision_counts[collision_map[rhs.index]]; }); // And compute the median and max. int median = collision_counts [collision_map[bits_and_indices[bits_and_indices.size() / 2].index]]; int max = *llvm::max_element(collision_counts); CARBON_CHECK(max == collision_counts[collision_map[bits_and_indices.back().index]]); return {.total = total, .median = median, .max = max}; } auto CheckNoDuplicateValues(llvm::ArrayRef hashes) -> void { for (int i = 0, size = hashes.size(); i < size - 1; ++i) { const auto& [_, value] = hashes[i]; CARBON_CHECK(value != hashes[i + 1].v, "Duplicate value: {0}", value); } } template auto AllByteStringsHashedAndSorted() { static_assert(N < 5, "Can only generate all 4-byte strings or shorter."); llvm::SmallVector hashes; int64_t count = 1LL << (N * 8); for (int64_t i : llvm::seq(count)) { uint8_t bytes[N]; for (int j : llvm::seq(N)) { bytes[j] = (static_cast(i) >> (8 * j)) & 0xff; } std::string s(std::begin(bytes), std::end(bytes)); hashes.push_back({HashValue(s, TestSeed), s}); } llvm::sort(hashes, [](const HashedString& lhs, const HashedString& rhs) { return static_cast(lhs.hash) < static_cast(rhs.hash); }); CheckNoDuplicateValues(hashes); return hashes; } auto ExpectNoHashCollisions(llvm::ArrayRef hashes) -> void { HashCode prev_hash = hashes[0].hash; llvm::StringRef prev_s = hashes[0].v; for (const auto& [hash, s] : hashes.slice(1)) { if (hash != prev_hash) { prev_hash = hash; prev_s = s; continue; } FAIL() << "Colliding hash '" << hash << "' of strings " << ToHexBytes(prev_s) << " and " << ToHexBytes(s); } } TEST(HashingTest, Collisions1ByteSized) { auto hashes_storage = AllByteStringsHashedAndSorted<1>(); llvm::ArrayRef hashes = hashes_storage; ExpectNoHashCollisions(hashes); auto low_32bit_collisions = FindBitRangeCollisions<0, 32>(hashes); EXPECT_THAT(low_32bit_collisions.total, Eq(0)); auto high_32bit_collisions = FindBitRangeCollisions<32, 64>(hashes); EXPECT_THAT(high_32bit_collisions.total, Eq(0)); // We expect collisions when only looking at 7-bits of the hash. However, // modern hash table designs need to use either the low or high 7 bits as tags // for faster searching. So we add some direct testing that the median and max // collisions for any given key stay within bounds. We express the bounds in // terms of the minimum expected "perfect" rate of collisions if uniformly // distributed. int min_7bit_collisions = llvm::NextPowerOf2(hashes.size() - 1) / (1 << 7); auto low_7bit_collisions = FindBitRangeCollisions<0, 7>(hashes); EXPECT_THAT(low_7bit_collisions.median, Le(8 * min_7bit_collisions)); EXPECT_THAT(low_7bit_collisions.max, Le(8 * min_7bit_collisions)); auto high_7bit_collisions = FindBitRangeCollisions<64 - 7, 64>(hashes); EXPECT_THAT(high_7bit_collisions.median, Le(2 * min_7bit_collisions)); EXPECT_THAT(high_7bit_collisions.max, Le(4 * min_7bit_collisions)); } TEST(HashingTest, Collisions2ByteSized) { auto hashes_storage = AllByteStringsHashedAndSorted<2>(); llvm::ArrayRef hashes = hashes_storage; ExpectNoHashCollisions(hashes); auto low_32bit_collisions = FindBitRangeCollisions<0, 32>(hashes); EXPECT_THAT(low_32bit_collisions.total, Eq(0)); auto high_32bit_collisions = FindBitRangeCollisions<32, 64>(hashes); EXPECT_THAT(high_32bit_collisions.total, Eq(0)); // Similar to 1-byte keys, we do expect a certain rate of collisions here but // bound the median and max. int min_7bit_collisions = llvm::NextPowerOf2(hashes.size() - 1) / (1 << 7); auto low_7bit_collisions = FindBitRangeCollisions<0, 7>(hashes); EXPECT_THAT(low_7bit_collisions.median, Le(2 * min_7bit_collisions)); EXPECT_THAT(low_7bit_collisions.max, Le(2 * min_7bit_collisions)); auto high_7bit_collisions = FindBitRangeCollisions<64 - 7, 64>(hashes); EXPECT_THAT(high_7bit_collisions.median, Le(2 * min_7bit_collisions)); EXPECT_THAT(high_7bit_collisions.max, Le(2 * min_7bit_collisions)); } // Generate and hash all strings of of [BeginByteCount, EndByteCount) bytes, // with [BeginSetBitCount, EndSetBitCount) contiguous bits at each possible bit // offset set to one and all other bits set to zero. template struct SparseHashTestParamRanges { static_assert(BeginByteCount >= 0); static_assert(BeginByteCount < EndByteCount); static_assert(BeginSetBitCount >= 0); static_assert(BeginSetBitCount < EndSetBitCount); // Note that we intentionally allow the end-set-bit-count to result in more // set bits than are available -- we truncate the number of set bits to fit // within the byte string. static_assert(BeginSetBitCount <= BeginByteCount * 8); struct ByteCount { static constexpr int Begin = BeginByteCount; static constexpr int End = EndByteCount; }; struct SetBitCount { static constexpr int Begin = BeginSetBitCount; static constexpr int End = EndSetBitCount; }; }; template struct SparseHashTest : ::testing::Test { using ByteCount = typename ParamRanges::ByteCount; using SetBitCount = typename ParamRanges::SetBitCount; static auto GetHashedByteStrings() { llvm::SmallVector hashes; for (int byte_count : llvm::seq_inclusive(ByteCount::Begin, ByteCount::End)) { int bits = byte_count * 8; for (int set_bit_count : llvm::seq_inclusive( SetBitCount::Begin, std::min(bits, SetBitCount::End))) { if (set_bit_count == 0) { std::string s(byte_count, '\0'); hashes.push_back({HashValue(s, TestSeed), std::move(s)}); continue; } for (int begin_set_bit : llvm::seq_inclusive(0, bits - set_bit_count)) { std::string s(byte_count, '\0'); int begin_set_bit_byte_index = begin_set_bit / 8; int begin_set_bit_bit_index = begin_set_bit % 8; int end_set_bit_byte_index = (begin_set_bit + set_bit_count) / 8; int end_set_bit_bit_index = (begin_set_bit + set_bit_count) % 8; // We build a begin byte and end byte. We set the begin byte, set // subsequent bytes up to *and including* the end byte to all ones, // and then mask the end byte. For multi-byte runs, the mask just sets // the end byte and for single-byte runs the mask computes the // intersecting bits. // // Consider a 4-set-bit count, starting at bit 2. The begin bit index // is 2, and the end bit index is 6. // // Begin byte: 0b11111111 -(shl 2)-----> 0b11111100 // End byte: 0b11111111 -(shr (8-6))-> 0b00111111 // Masked byte: 0b00111100 // // Or a 10-set-bit-count starting at bit 2. The begin bit index is 2, // the end byte index is (12 / 8) or 1, and the end bit index is (12 % // 8) or 4. // // Begin byte: 0b11111111 -(shl 2)-----> 0b11111100 -> 6 bits // End byte: 0b11111111 -(shr (8-4))-> 0b00001111 -> 4 bits // 10 total bits // uint8_t begin_set_bit_byte = 0xFFU << begin_set_bit_bit_index; uint8_t end_set_bit_byte = 0xFFU >> (8 - end_set_bit_bit_index); bool has_end_byte_bits = end_set_bit_byte != 0; s[begin_set_bit_byte_index] = begin_set_bit_byte; for (int i : llvm::seq(begin_set_bit_byte_index + 1, end_set_bit_byte_index + has_end_byte_bits)) { s[i] = '\xFF'; } // If there are no bits set in the end byte, it may be past-the-end // and we can't even mask a zero byte safely. if (has_end_byte_bits) { s[end_set_bit_byte_index] &= end_set_bit_byte; } hashes.push_back({HashValue(s, TestSeed), std::move(s)}); } } } llvm::sort(hashes, [](const HashedString& lhs, const HashedString& rhs) { return static_cast(lhs.hash) < static_cast(rhs.hash); }); CheckNoDuplicateValues(hashes); return hashes; } }; using SparseHashTestParams = ::testing::Types< SparseHashTestParamRanges, SparseHashTestParamRanges, SparseHashTestParamRanges>; TYPED_TEST_SUITE(SparseHashTest, SparseHashTestParams); TYPED_TEST(SparseHashTest, Collisions) { auto hashes_storage = this->GetHashedByteStrings(); llvm::ArrayRef hashes = hashes_storage; ExpectNoHashCollisions(hashes); int min_7bit_collisions = llvm::NextPowerOf2(hashes.size() - 1) / (1 << 7); auto low_7bit_collisions = FindBitRangeCollisions<0, 7>(hashes); EXPECT_THAT(low_7bit_collisions.median, Le(2 * min_7bit_collisions)); EXPECT_THAT(low_7bit_collisions.max, Le(2 * min_7bit_collisions)); auto high_7bit_collisions = FindBitRangeCollisions<64 - 7, 64>(hashes); EXPECT_THAT(high_7bit_collisions.median, Le(2 * min_7bit_collisions)); EXPECT_THAT(high_7bit_collisions.max, Le(2 * min_7bit_collisions)); } } // namespace } // namespace Carbon ================================================ FILE: common/hashtable_key_context.h ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #ifndef CARBON_COMMON_HASHTABLE_KEY_CONTEXT_H_ #define CARBON_COMMON_HASHTABLE_KEY_CONTEXT_H_ #include #include "common/hashing.h" #include "llvm/ADT/APFloat.h" #include "llvm/ADT/APInt.h" namespace Carbon { // The equality comparison used by the the hashtable key contexts in this file, // and suitable for using in other hashtable key contexts. // // This provides a hashtable-specific extension point to implement equality // comparison within a hashtable key context. By default, it will use // `operator==` on the LHS and RHS operands. However, types can provide a // dedicated customization point by implementing a free function that can be // found by ADL for your type called `CarbonHashtableEq` with the following // signature: // // ```cpp // auto CarbonHashtableEq(const YourType& lhs, const YourType& rhs) -> bool; // ``` // // Any such overload will be able to override the default we provide for types // that can compare with `==`. // // This library also provides any customization points for LLVM or standard // library types either lacking `operator==` or where that operator is not // suitable for hashtables. For example, `llvm::APInt` and `llvm::APFloat` have // custom equality comparisons provided through this extension point. template auto HashtableEq(const LeftT& lhs, const RightT& rhs) -> bool; // Customizable context for keys in hashtables. // // This type or customizations matching its API are used with the data // structures in `map.h` and `set.h`. By providing a custom version of the // `KeyContext` type parameter to those data structures, users can provide // either stateless or stateful customization of the two core hashtable key // operations: hashing and comparison. // // The default for hashing uses Carbon's `hashing.h`. Customizations must still // return a `HashCode` as defined there, and it needs to have the same core // properties of hashes produced by the `hashing.h` infrastructure. // // The default for comparison is `operator==`. The `KeyEq` method is always // called with a key *stored in the hashtable* as the second or "Rhs" parameter. // This is to allow simplifying the set of overloads needed for heterogeneous // contexts: only the first, LHS, parameter needs to support different lookup // key types. // // Custom KeyContext types should have the the same API as the default type. // They can choose to use templates to support heterogeneous key types or not as // appropriate. The default context can also be used as a base class with only // one or the other APIs customized. // // An important consideration is how the key context is constructed. When the // key context can be default constructed, hashtable APIs trafficking in keys // will have overloads that provide a default constructed key context. When the // context is *not* default constructible, every API that accepts a key will // also require a context argument to be called, and that argument will be used // throughout that operation. The intent is to allow callers to provide stateful // contexts to each API where it would be needed, while managing that state // outside the hashtable. Often the needed state is trivially part of the // caller's existing state and needn't be stored separately. // // Example for a stateful, customized key context for interned strings: // ```cpp // class InternedStringIndexKeyContext { // public: // InternedStringIndexKeyContext( // llvm::ArrayRef interned_strings) // : interned_strings_(interned_strings) {} // // auto HashKey(llvm::StringRef s, uint64_t seed) const -> HashCode { // return HashValue(s); // } // auto HashKey(int index_key, uint64_t seed) const -> HashCode { // return HashKey(interned_strings_[index_key]); // } // // auto KeyEq(llvm::StringRef lhs, int rhs_index) const -> bool { // return lhs == interned_strings_[rhs_index]; // } // auto KeyEq(int lhs_index, int rhs_index) const -> bool { // return KeyEq(interned_strings_[lhs_index], rhs_index); // } // // private: // llvm::ArrayRef interned_strings_; // }; // ``` struct DefaultKeyContext { template auto HashKey(const AnyKeyT& key, uint64_t seed) const -> HashCode; template auto KeyEq(const AnyKeyT& lhs_key, const TableKeyT& rhs_key) const -> bool; }; // A CRTP mixin for a custom key context type that first translates keys to a // different type, possibly using some state. // // Derived types should publicly inherit from this mixin and define overloads of // the `TranslateKey` method as indicated below in its comment. template class TranslatingKeyContext { public: // Derived types should provide one or more overloads that hide this function // and perform translation for the key types which need it. // // For any key type, the context will check if there exists a callable // `TranslateKey` function on the derived type. If so, that function will be // called and the result used for hashing or comparison. If not, the key will // be used directly. The derived type doesn't need to and shouldn't provide a // default no-op overload. Instead, for any types that need no translation, it // should ensure no overload is viable. // // Note that this function should be *hidden* by the derived overloads. It is // provided here to help detect typos or misspellings or cases where no // overload is provided at all. template auto TranslateKey(const TranslateKeyT& /*key*/) const -> int { // A static_assert that will fail on any actual instantiation (it can't be // instantiated with a void type). We have to make this dependent as // Clang-16 will fail to compile even when the definition is never // instantiated otherwise. static_assert( std::same_as, "No `TranslateKey` overload was provided by the derived type!"); } template auto HashKey(const AnyKeyT& key, uint64_t seed) const -> HashCode; template auto KeyEq(const AnyKeyT& lhs_key, const TableKeyT& rhs_key) const -> bool; }; //////////////////////////////////////////////////////////////////////////////// // // Only implementation details below this point. // //////////////////////////////////////////////////////////////////////////////// namespace InternalHashtableEqDispatch { inline auto CarbonHashtableEq(const llvm::APInt& lhs, const llvm::APInt& rhs) -> bool { return lhs.getBitWidth() == rhs.getBitWidth() && lhs == rhs; } inline auto CarbonHashtableEq(const llvm::APFloat& lhs, const llvm::APFloat& rhs) -> bool { return lhs.bitwiseIsEqual(rhs); } template inline auto CarbonHashtableEq(const LeftT& lhs, const RightT& rhs) -> bool requires(requires { { lhs == rhs } -> std::convertible_to; }) { return lhs == rhs; } template inline auto DispatchImpl(const LeftT& lhs, const RightT& rhs) -> bool { // This unqualified call will find both the overloads in our internal // namespace above and ADL-found functions within an associated namespace for // either `LeftT` or `RightT`. return CarbonHashtableEq(lhs, rhs); } } // namespace InternalHashtableEqDispatch template inline auto HashtableEq(const LeftT& lhs, const RightT& rhs) -> bool { return InternalHashtableEqDispatch::DispatchImpl(lhs, rhs); } template auto DefaultKeyContext::HashKey(const AnyKeyT& key, uint64_t seed) const -> HashCode { return HashValue(key, seed); } template auto DefaultKeyContext::KeyEq(const AnyKeyT& lhs_key, const TableKeyT& rhs_key) const -> bool { return HashtableEq(lhs_key, rhs_key); } template template auto TranslatingKeyContext::HashKey(const AnyKeyT& key, uint64_t seed) const -> HashCode { const DerivedT& self = *static_cast(this); if constexpr (requires { self.TranslateKey(key); }) { return HashValue(self.TranslateKey(key), seed); } else { return HashValue(key, seed); } } template template auto TranslatingKeyContext::KeyEq(const AnyKeyT& lhs_key, const TableKeyT& rhs_key) const -> bool { const DerivedT& self = *static_cast(this); // Because we don't want to make no-op calls and potentially struggle with // temporary lifetimes at runtime we have to fully expand the 4 states. constexpr bool TranslateLhs = requires { self.TranslateKey(lhs_key); }; constexpr bool TranslateRhs = requires { self.TranslateKey(rhs_key); }; if constexpr (TranslateLhs && TranslateRhs) { return HashtableEq(self.TranslateKey(lhs_key), self.TranslateKey(rhs_key)); } else if constexpr (TranslateLhs) { return HashtableEq(self.TranslateKey(lhs_key), rhs_key); } else if constexpr (TranslateRhs) { return HashtableEq(lhs_key, self.TranslateKey(rhs_key)); } else { return HashtableEq(lhs_key, rhs_key); } } } // namespace Carbon #endif // CARBON_COMMON_HASHTABLE_KEY_CONTEXT_H_ ================================================ FILE: common/hashtable_key_context_test.cpp ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #include "common/hashtable_key_context.h" #include #include namespace Carbon { namespace { using ::testing::Eq; using ::testing::Ne; struct DefaultEq { int x, y; friend auto operator==(const DefaultEq& lhs, const DefaultEq& rhs) -> bool = default; }; struct CustomEq { int x, y; friend auto operator==(const CustomEq& lhs, const CustomEq& rhs) -> bool { return lhs.x == rhs.x && lhs.y == rhs.y; } }; struct CustomExtEq { int x, y; friend auto CarbonHashtableEq(const CustomExtEq& lhs, const CustomExtEq& rhs) -> bool { return lhs.x == rhs.x && lhs.y == rhs.y; } }; TEST(HashtableKeyContextTest, HashtableEq) { EXPECT_TRUE(HashtableEq(0, 0)); EXPECT_FALSE(HashtableEq(1, 0)); EXPECT_FALSE(HashtableEq(0, 1)); EXPECT_FALSE(HashtableEq(1234, 5678)); EXPECT_TRUE(HashtableEq(5678, 5678)); EXPECT_TRUE( HashtableEq(DefaultEq{.x = 0, .y = 0}, DefaultEq{.x = 0, .y = 0})); EXPECT_FALSE( HashtableEq(DefaultEq{.x = 1, .y = 2}, DefaultEq{.x = 3, .y = 4})); EXPECT_TRUE(HashtableEq(CustomEq{.x = 0, .y = 0}, CustomEq{.x = 0, .y = 0})); EXPECT_FALSE(HashtableEq(CustomEq{.x = 1, .y = 2}, CustomEq{.x = 3, .y = 4})); EXPECT_TRUE( HashtableEq(CustomExtEq{.x = 0, .y = 0}, CustomExtEq{.x = 0, .y = 0})); EXPECT_FALSE( HashtableEq(CustomExtEq{.x = 1, .y = 2}, CustomExtEq{.x = 3, .y = 4})); } TEST(HashtableKeyContextTest, HashtableEqAPInt) { // Hashtable equality doesn't assert on mismatched bit width, it includes the // bit width in the comparison. llvm::APInt one_64(/*numBits=*/64, /*val=*/1); llvm::APInt two_64(/*numBits=*/64, /*val=*/2); llvm::APInt one_128(/*numBits=*/128, /*val=*/1); llvm::APInt two_128(/*numBits=*/128, /*val=*/2); EXPECT_TRUE(HashtableEq(one_64, one_64)); EXPECT_FALSE(HashtableEq(one_64, one_128)); EXPECT_TRUE(HashtableEq(two_128, two_128)); EXPECT_FALSE(HashtableEq(two_64, two_128)); EXPECT_FALSE(HashtableEq(one_64, two_64)); EXPECT_FALSE(HashtableEq(one_64, two_128)); EXPECT_FALSE(HashtableEq(one_128, two_128)); EXPECT_FALSE(HashtableEq(one_128, two_64)); } TEST(HashtableKeyContextTest, HashtableEqAPFloat) { // Hashtable equality for `APFloat` uses a bitwise comparison. This // differentiates between various things that would otherwise not make sense: // - Different floating point semantics // - `-0.0` and `0.0` // // It also allows NaNs to be compared meaningfully. llvm::APFloat zero_float = llvm::APFloat::getZero(llvm::APFloat::IEEEsingle()); llvm::APFloat neg_zero_float = llvm::APFloat::getZero(llvm::APFloat::IEEEsingle(), /*Negative=*/true); llvm::APFloat zero_double = llvm::APFloat::getZero(llvm::APFloat::IEEEdouble()); llvm::APFloat zero_bfloat = llvm::APFloat::getZero(llvm::APFloat::BFloat()); llvm::APFloat one_float = llvm::APFloat::getOne(llvm::APFloat::IEEEsingle()); llvm::APFloat inf_float = llvm::APFloat::getInf(llvm::APFloat::IEEEsingle()); llvm::APFloat nan_0_float = llvm::APFloat::getNaN( llvm::APFloat::IEEEsingle(), /*Negative=*/false, /*payload=*/0); llvm::APFloat nan_42_float = llvm::APFloat::getNaN( llvm::APFloat::IEEEsingle(), /*Negative=*/false, /*payload=*/42); // Boring cases. EXPECT_TRUE(HashtableEq(zero_float, zero_float)); EXPECT_FALSE(HashtableEq(zero_float, one_float)); EXPECT_TRUE(HashtableEq(inf_float, inf_float)); EXPECT_FALSE(HashtableEq(inf_float, one_float)); // Confirm a case where we expect `==` to work but produce a different result. ASSERT_TRUE(zero_float == neg_zero_float); EXPECT_FALSE(HashtableEq(zero_float, neg_zero_float)); // Now work through less reasonable things outside of a hashtable such as // mixing semantics and NaNs. EXPECT_FALSE(HashtableEq(zero_float, zero_double)); EXPECT_FALSE(HashtableEq(zero_float, zero_bfloat)); EXPECT_FALSE(HashtableEq(zero_float, nan_0_float)); EXPECT_FALSE(HashtableEq(zero_float, nan_42_float)); EXPECT_FALSE(HashtableEq(nan_0_float, nan_42_float)); } struct CustomHash { int x; friend auto CarbonHashValue(const CustomHash& value, uint64_t seed) -> HashCode { return HashValue(value.x + 42, seed); } }; TEST(HashtableKeyContextTest, DefaultKeyContext) { // Make sure the default context dispatches appropriately, including for // interesting types. We don't cover all the cases here and use the direct // tests of `HashtableEq` for that. DefaultKeyContext context; EXPECT_FALSE(context.KeyEq(1234, 5678)); EXPECT_TRUE(context.KeyEq(5678, 5678)); EXPECT_TRUE(context.KeyEq(DefaultEq{0, 0}, DefaultEq{0, 0})); EXPECT_FALSE(context.KeyEq(DefaultEq{1, 2}, DefaultEq{3, 4})); EXPECT_TRUE(context.KeyEq(CustomEq{0, 0}, CustomEq{0, 0})); EXPECT_FALSE(context.KeyEq(CustomEq{1, 2}, CustomEq{3, 4})); EXPECT_TRUE(context.KeyEq(CustomExtEq{0, 0}, CustomExtEq{0, 0})); EXPECT_FALSE(context.KeyEq(CustomExtEq{1, 2}, CustomExtEq{3, 4})); llvm::APInt one_64(/*numBits=*/64, /*val=*/1); llvm::APInt one_128(/*numBits=*/128, /*val=*/1); EXPECT_TRUE(HashtableEq(one_64, one_64)); EXPECT_FALSE(HashtableEq(one_64, one_128)); llvm::APFloat zero_float = llvm::APFloat::getZero(llvm::APFloat::IEEEsingle()); llvm::APFloat neg_zero_float = llvm::APFloat::getZero(llvm::APFloat::IEEEsingle(), /*Negative=*/true); EXPECT_TRUE(HashtableEq(zero_float, zero_float)); EXPECT_FALSE(HashtableEq(zero_float, neg_zero_float)); // Also check hash dispatching. uint64_t seed = 1234; EXPECT_THAT(context.HashKey(42, seed), Eq(HashValue(42, seed))); EXPECT_THAT(context.HashKey(CustomHash{.x = 1234}, seed), Eq(HashValue(CustomHash{.x = 1234}, seed))); EXPECT_THAT(context.HashKey(one_64, seed), Eq(HashValue(one_64, seed))); EXPECT_THAT(context.HashKey(one_128, seed), Eq(HashValue(one_128, seed))); EXPECT_THAT(context.HashKey(one_64, seed), Ne(context.HashKey(one_128, seed))); EXPECT_THAT(context.HashKey(zero_float, seed), Eq(HashValue(zero_float, seed))); EXPECT_THAT(context.HashKey(neg_zero_float, seed), Eq(HashValue(neg_zero_float, seed))); EXPECT_THAT(context.HashKey(zero_float, seed), Ne(context.HashKey(neg_zero_float, seed))); } struct TestTranslatingKeyContext : TranslatingKeyContext { auto TranslateKey(int index) const -> const llvm::APInt& { return array[index]; } llvm::ArrayRef array; }; TEST(HashtableKeyContextTest, TranslatingKeyContext) { llvm::APInt one_64(/*numBits=*/64, /*val=*/1); llvm::APInt two_64(/*numBits=*/64, /*val=*/2); llvm::APInt one_128(/*numBits=*/128, /*val=*/1); llvm::APInt two_128(/*numBits=*/128, /*val=*/2); // An array of values, including some duplicates. llvm::SmallVector values = {one_64, two_64, one_128, two_128, one_64, one_64}; TestTranslatingKeyContext context = {.array = values}; uint64_t seed = 1234; EXPECT_THAT(context.HashKey(0, seed), Eq(HashValue(one_64, seed))); EXPECT_THAT(context.HashKey(1, seed), Eq(HashValue(two_64, seed))); EXPECT_THAT(context.HashKey(2, seed), Eq(HashValue(one_128, seed))); EXPECT_THAT(context.HashKey(3, seed), Eq(HashValue(two_128, seed))); EXPECT_THAT(context.HashKey(4, seed), Eq(HashValue(one_64, seed))); EXPECT_THAT(context.HashKey(5, seed), Eq(HashValue(one_64, seed))); EXPECT_TRUE(context.KeyEq(one_64, 0)); EXPECT_TRUE(context.KeyEq(one_64, 4)); EXPECT_TRUE(context.KeyEq(one_64, 5)); EXPECT_TRUE(context.KeyEq(0, one_64)); EXPECT_TRUE(context.KeyEq(0, 0)); EXPECT_TRUE(context.KeyEq(0, 4)); EXPECT_TRUE(context.KeyEq(4, 5)); EXPECT_FALSE(context.KeyEq(one_64, 1)); EXPECT_FALSE(context.KeyEq(one_64, 2)); EXPECT_FALSE(context.KeyEq(one_64, 3)); EXPECT_FALSE(context.KeyEq(1, one_64)); EXPECT_FALSE(context.KeyEq(2, one_64)); EXPECT_FALSE(context.KeyEq(3, one_64)); EXPECT_FALSE(context.KeyEq(0, 1)); EXPECT_FALSE(context.KeyEq(0, 2)); EXPECT_FALSE(context.KeyEq(4, 3)); } } // namespace } // namespace Carbon ================================================ FILE: common/init_llvm.cpp ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #include "common/init_llvm.h" #include "llvm/Support/TargetSelect.h" #include "llvm/Support/raw_ostream.h" namespace Carbon { InitLLVM::InitLLVM(int& argc, char**& argv) : init_llvm_(argc, argv), // LLVM assumes that argc and argv won't change, and registers them with // an `llvm::PrettyStackTraceProgram` that will crash if an argv element // gets nulled out, which for example `testing::InitGoogleTest` does. So // make a copy of the argv that LLVM produces in order to support // mutation. args_(argv, argv + argc) { // `argv[argc]` is expected to be a null pointer (may reallocate `args_`). args_.push_back(nullptr); // Return our mutable copy of argv for the program to use. argc = args_.size() - 1; argv = args_.data(); llvm::setBugReportMsg( "Please report issues to " "https://github.com/carbon-language/carbon-lang/issues and include the " "crash backtrace.\n"); // Initialize LLVM targets if //common:all_llvm_targets was linked in. if (InitializeTargets) { InitializeTargets(); } // Printing to stderr should flush stdout. This is most noticeable when stderr // is piped to stdout. llvm::errs().tie(&llvm::outs()); } InitLLVM::InitializeTargetsFn* InitLLVM::InitializeTargets = nullptr; } // namespace Carbon ================================================ FILE: common/init_llvm.h ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #ifndef CARBON_COMMON_INIT_LLVM_H_ #define CARBON_COMMON_INIT_LLVM_H_ #include "llvm/ADT/SmallVector.h" #include "llvm/Support/InitLLVM.h" namespace Carbon { // A RAII class to handle initializing LLVM and shutting it down. An instance of // this class should be created in the `main` function of each Carbon binary // that interacts with LLVM, before `argc` and `argv` are first inspected. class InitLLVM { public: // Initializes LLVM for use by a Carbon binary. On Windows, `argc` and `argv` // are updated to refer to properly-encoded UTF-8 versions of the command line // arguments. explicit InitLLVM(int& argc, char**& argv); // Shuts down LLVM. ~InitLLVM() = default; private: using InitializeTargetsFn = auto() -> void; llvm::InitLLVM init_llvm_; llvm::SmallVector args_; // A pointer to the LLVM target initialization function, if :all_llvm_targets // is linked in. Otherwise nullptr. // NOLINTNEXTLINE(readability-identifier-naming): Constant after static init. static InitializeTargetsFn* InitializeTargets; // The initializer of this static data member populates `InitializeTargets`. // Defined only if :all_llvm_targets is linked in. This is a member so that // it has access to `InitializeTargets`. static const char RegisterTargets; }; } // namespace Carbon #endif // CARBON_COMMON_INIT_LLVM_H_ ================================================ FILE: common/latch.cpp ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #include "common/latch.h" #include "common/check.h" namespace Carbon { auto Latch::Inc() -> void { // The increment must be _atomic_ but is _relaxed_. // // Increments and decrements can happen concurrently on separate threads, so // we need to prevent tearing and for there to be a total ordering of stores // to this atomic. // // However we provide no _synchronization_ of the increment with any other // operations. Instead, the caller must provide some extrinsic happens-before // between its call to `Inc` and its later call to `Dec`. When that call to // `Dec` synchronizes-with another call to `Dec`, all relaxed stores are // covered by the resulting inter-thread happens-before relationship. count_.fetch_add(1, std::memory_order_relaxed); } auto Latch::Dec() -> bool { // The decrement is both an _acquire_ and _release_ operation. // // All threads which decrement to a non-zero value need to synchronize-with // the thread which decrements to a zero value. This means the decrements to // non-zero values need to have _release_ semantics that are _acquired_ by the // decrement to zero. Since there is a single decrement operation, it must be // both _acquire_ and _release_. // // Note that this technically provides a stronger guarantee than the contract // of `Dec` requires -- *all* decrements synchronize with all decrements whose // value they observe, we only need that to be true of the decrement arriving // at zero. This could in theory be modeled by conditional fences, but those // have their own problems and we don't need to model the more precise // semantics for efficiency. auto previous = count_.fetch_sub(1, std::memory_order_acq_rel); CARBON_CHECK(previous > 0); if (previous == 1) { // Ensure that our closure is fully destroyed here, releasing any // resources, locks, or other synchronization primitives. auto on_zero = std::exchange(on_zero_, [] {}); std::move(on_zero)(); return true; } return false; } auto Latch::Init(llvm::unique_functionvoid> on_zero) -> Handle { CARBON_CHECK(count_ == 0); on_zero_ = std::move(on_zero); return Handle(this); } } // namespace Carbon ================================================ FILE: common/latch.h ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #ifndef CARBON_COMMON_LATCH_H_ #define CARBON_COMMON_LATCH_H_ #include #include "llvm/ADT/FunctionExtras.h" namespace Carbon { // A synchronization primitive similar to `std::latch` to coordinate starting // some action once all of a set of other actions complete. // // Users initialize the latch (with `Init`), and receive a handle RAII object. // This handle can be copied, and the latch is satisfied when the last copy of // the handle returned by `Init` is destroyed. // // The latch synchronizes between every destruction of a handle and the // destruction of the last handle, allowing code that runs after the latch is // satisfied to access everything written by any thread that destroyed a handle. // For more details of the synchronization mechanics, see the comments on `Inc` // and `Dec` that implement this logic. // // This type also supports holding a closure to run when satisfied to simplify // patterns where that body of code is easier to express at the start of work // being synchronized instead of as each work item completes. // // The initialization API is separate from the constructor both for convenience // and to enable it to provide the initial handle. This makes it easy to build // constructively correct code where each work unit holds a handle until // finished, including the initializer of the latch, often using by-value // captures in a lambda that does the work. class Latch { public: class Handle; Latch() = default; Latch(const Latch&) = delete; Latch(Latch&&) = delete; // Initialize a latch and get the initial handle to it. // // When the last copy of the returned handle is destroyed, the latch will be // satisfied. // // A closure may be provided which will be called when that last handle is // destroyed. Note that the closure will run on whatever thread executes the // last handle destruction. Typically, the closure here should _schedule_ the // next step of work on some thread pool rather than performing it directly. // // Once this method is called, it cannot be called again until all handles are // destroyed and the latch is satisfied. It can then be called again to get a // fresh handle (and provide a new closure if desired). auto Init(llvm::unique_functionvoid> on_zero = [] {}) -> Handle; private: // Increments the latch's counter. // // This is thread-safe, and may be called concurrently on multiple threads, // and may be called concurrently with `Dec`. However, the caller _must_ call // `Inc` and then `Dec`, and provide some happens-before relationship between // the `Inc` and `Dec`. Typically, this is done with either same-thread // happens-before, or because some other synchronization event such as // starting a thread or popping a task from a thread pool provides the // inter-thread happens-before relationship. auto Inc() -> void; // Decrements the latch's counter, and returns true when it reaches zero. // // This is thread-safe, and may be called concurrently with other calls to // `Dec` or `Inc`. // // It also ensures that all threads which call `Dec` and receive `false` // synchronize-with the thread that calls `Dec` and receives `true`. As a // consequence everything that happens-before the call to `Dec` has an // inter-thread happens-before for any code when `Dec` returns `true`. // // Note that there is no guarantee of inter-thread happens-before to // operations after a `Dec` call that returns `false`. auto Dec() -> bool; std::atomic count_; llvm::unique_functionvoid> on_zero_; }; // A copyable RAII handle around a `Latch`. // // When the last copy of a handle returned by `Latch::Init` is destroyed, the // latch is considered satisfied. Copying is supported by incrementing the // count of the latch. That increment can always be performed because it starts // from a live handle and so the count cannot have reached zero. // // For more details, see the `Latch` class. class Latch::Handle { public: Handle(const Handle& arg) : latch_(arg.latch_) { if (latch_) { arg.latch_->Inc(); } } Handle(Handle&& arg) noexcept : latch_(std::exchange(arg.latch_, nullptr)) {} ~Handle() { if (latch_) { latch_->Dec(); } } // Drops a handle explicitly, rather than waiting for it to fall out of scope. // // This also allows observing whether the underlying latch is satisfied. // Calls to this function synchronize with all other drops or destructions of // latch handles when it returns true, and only the last will return true. auto Drop() && -> bool { bool last = latch_->Dec(); latch_ = nullptr; return last; } private: friend Latch; // Private constructor used by `Latch::Init` to create the initial handle for // a latch. explicit Handle(Latch* latch) : latch_(latch) { latch_->Inc(); } Latch* latch_ = nullptr; }; } // namespace Carbon #endif // CARBON_COMMON_LATCH_H_ ================================================ FILE: common/latch_test.cpp ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #include "common/latch.h" #include #include #include #include namespace Carbon { namespace { // Basic test for the Latch. TEST(LatchTest, Basic) { Latch latch; Latch::Handle handle = latch.Init(); // Dropping a copy doesn't satisfy the latch. Latch::Handle handle_copy = handle; EXPECT_FALSE(std::move(handle_copy).Drop()); // Can create more copies even after we start dropping. Latch::Handle handle_copy2 = handle; EXPECT_FALSE(std::move(handle_copy2).Drop()); // Now drop the last handle. EXPECT_TRUE(std::move(handle).Drop()); } // Tests that the on-zero callback is called. TEST(LatchTest, OnZeroCallback) { Latch latch; bool called = false; Latch::Handle handle = latch.Init([&] { called = true; }); Latch::Handle handle2 = handle; Latch::Handle handle3 = handle; EXPECT_FALSE(called); EXPECT_FALSE(std::move(handle).Drop()); EXPECT_FALSE(called); EXPECT_FALSE(std::move(handle2).Drop()); EXPECT_FALSE(called); EXPECT_TRUE(std::move(handle3).Drop()); EXPECT_TRUE(called); } // Tests moving a handle. TEST(LatchTest, MoveHandle) { Latch latch; bool called = false; Latch::Handle handle = latch.Init([&] { called = true; }); Latch::Handle handle2 = std::move(handle); // Check that dropping the new handle satisfies the latch. EXPECT_FALSE(called); EXPECT_TRUE(std::move(handle2).Drop()); EXPECT_TRUE(called); } // Test that creating and destroying a handle without dropping works. TEST(LatchTest, Destructor) { Latch latch; bool called = false; Latch::Handle handle = latch.Init([&] { called = true; }); { // NOLINTNEXTLINE(performance-unnecessary-copy-initialization) Latch::Handle handle2 = handle; EXPECT_FALSE(called); } EXPECT_FALSE(called); EXPECT_TRUE(std::move(handle).Drop()); EXPECT_TRUE(called); } // Tests calling `Init` more than once. TEST(LatchTest, Reuse) { Latch latch; bool called = false; Latch::Handle handle = latch.Init([&] { called = true; }); Latch::Handle handle2 = handle; EXPECT_FALSE(called); EXPECT_FALSE(std::move(handle).Drop()); EXPECT_FALSE(called); EXPECT_TRUE(std::move(handle2).Drop()); EXPECT_TRUE(called); // Now initialize the latch again with a new closure. bool called2 = false; Latch::Handle handle3 = latch.Init([&] { called2 = true; }); Latch::Handle handle4 = handle3; EXPECT_FALSE(called2); EXPECT_FALSE(std::move(handle3).Drop()); EXPECT_FALSE(called2); EXPECT_TRUE(std::move(handle4).Drop()); EXPECT_TRUE(called2); } // Tests the latch with multiple threads. TEST(LatchTest, MultiThreaded) { Latch latch; std::atomic counter = 0; bool called = false; constexpr int NumThreads = 5; // The `on_zero` callback will be executed by the last thread to drop its // handle. auto handle = latch.Init([&] { // Check that all threads have done their work. EXPECT_EQ(counter.load(), NumThreads); called = true; }); std::vector threads; threads.reserve(NumThreads); for (int i = 0; i < NumThreads; ++i) { threads.emplace_back([&, handle_copy = handle] { // Each thread has its own copy of the handle. // Simulate some work. std::this_thread::sleep_for(std::chrono::milliseconds(10)); counter++; // The handle is dropped when the thread exits. }); } // Drop the main thread's handle. std::move(handle).Drop(); for (auto& thread : threads) { thread.join(); } EXPECT_TRUE(called); } } // namespace } // namespace Carbon ================================================ FILE: common/map.h ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #ifndef CARBON_COMMON_MAP_H_ #define CARBON_COMMON_MAP_H_ #include #include #include #include "common/check.h" #include "common/concepts.h" #include "common/hashtable_key_context.h" #include "common/raw_hashtable.h" #include "llvm/Support/Compiler.h" namespace Carbon { // Forward declarations to resolve cyclic references. template class MapView; template class MapBase; template class Map; // A read-only view type for a map from key to value. // // This view is a cheap-to-copy type that should be passed by value, but // provides view or read-only reference semantics to the underlying map data // structure. // // This should always be preferred to a `const`-ref parameter for the `MapBase` // or `Map` type as it provides more flexibility and a cleaner API. By default a // `MapView` provides no more immutability than a `const Map`: elements can't be // added or removed, but both keys and values can be mutated, and the user is // responsible for avoiding mutations that affect the key's hash value or // equality comparison. However, the key and value types can be // `const`-qualified to prevent those mutations. For example, a `MapView` // can be converted to `MapView` to prevent mutating the keys. As // with any other view type, `const` on the `MapView` itself is "shallow": it // prevents rebinding the `MapView` to a different underlying map, but doesn't // affect mutability of the underlying map. // // A specific `KeyContextT` type can optionally be provided to configure how // keys will be hashed and compared. The default is `DefaultKeyContext` which is // stateless and will hash using `Carbon::HashValue` and compare using // `operator==`. Every method accepting a lookup key or operating on the keys in // the table will also accept an instance of this type. For stateless context // types, including the default, an instance will be default constructed if not // provided to these methods. However, stateful contexts should be constructed // and passed in explicitly. The context type should be small and reasonable to // pass by value, often a wrapper or pointer to the relevant context needed for // hashing and comparing keys. For more details about the key context, see // `hashtable_key_context.h`. template class MapView : RawHashtable::ViewImpl { using ImplT = RawHashtable::ViewImpl; using EntryT = typename ImplT::EntryT; public: using KeyT = typename ImplT::KeyT; using ValueT = typename ImplT::ValueT; using KeyContextT = typename ImplT::KeyContextT; using MetricsT = typename ImplT::MetricsT; // This type represents the result of lookup operations. It encodes whether // the lookup was a success as well as accessors for the key and value. class LookupKVResult { public: LookupKVResult() = default; explicit LookupKVResult(EntryT* entry) : entry_(entry) {} explicit operator bool() const { return entry_ != nullptr; } auto key() const -> KeyT& { return entry_->key(); } auto value() const -> ValueT& { return entry_->value(); } private: EntryT* entry_ = nullptr; }; // Enable implicit conversions that add `const`-ness to either key or value // type. This is always safe to do with a view. We use a template to avoid // needing all 3 versions. template explicit(false) MapView(MapView other_view) requires(SameAsOneOf && SameAsOneOf) : ImplT(other_view) {} // Tests whether a key is present in the map. template auto Contains(LookupKeyT lookup_key, KeyContextT key_context = KeyContextT()) const -> bool; // Lookup a key in the map. template auto Lookup(LookupKeyT lookup_key, KeyContextT key_context = KeyContextT()) const -> LookupKVResult; // Lookup a key in the map and try to return a pointer to its value. Returns // null on a missing key. template auto operator[](LookupKeyT lookup_key) const -> ValueT* requires(std::default_initializable); // Run the provided callback for every key and value in the map. template auto ForEach(CallbackT callback) -> void requires(std::invocable); // This routine is relatively inefficient and only intended for use in // benchmarking or logging of performance anomalies. The specific metrics // returned have no specific guarantees beyond being informative in // benchmarks. auto ComputeMetrics(KeyContextT key_context = KeyContextT()) -> MetricsT { return ImplT::ComputeMetricsImpl(key_context); } private: template friend class Map; friend class MapBase; friend class MapView; friend class MapView; friend class MapView; MapView() = default; explicit(false) MapView(ImplT base) : ImplT(base) {} MapView(ssize_t size, RawHashtable::Storage* storage) : ImplT(size, storage) {} }; // A base class for a `Map` type that remains mutable while type-erasing the // `SmallSize` (SSO) template parameter. // // Note that `MapBase` has "shallow" const semantics: a `const MapBase&` // can't be used to mutate the map data structure itself (e.g. by changing the // number of elements), but it can be used to mutate the keys and values it // contains. The user is responsible for avoiding mutations that would change // the hash value or equality of a key. A `MapView` with const-qualified key and // value types can be used to provide read-only access to the elements of a // `MapBase`. // // A pointer or reference to this type is the preferred way to pass a mutable // handle to a `Map` type across API boundaries as it avoids encoding specific // SSO sizing information while providing a near-complete mutable API. template class MapBase : protected RawHashtable::BaseImpl { protected: using ImplT = RawHashtable::BaseImpl; using EntryT = typename ImplT::EntryT; public: using KeyT = typename ImplT::KeyT; using ValueT = typename ImplT::ValueT; using KeyContextT = typename ImplT::KeyContextT; using ViewT = MapView; using LookupKVResult = typename ViewT::LookupKVResult; using MetricsT = typename ImplT::MetricsT; // The result type for insertion operations both indicates whether an insert // was needed (as opposed to finding an existing element), and provides access // to the element's key and value. class InsertKVResult { public: InsertKVResult() = default; explicit InsertKVResult(bool inserted, EntryT& entry) : entry_(&entry), inserted_(inserted) {} auto is_inserted() const -> bool { return inserted_; } auto key() const -> KeyT& { return entry_->key(); } auto value() const -> ValueT& { return entry_->value(); } private: EntryT* entry_; bool inserted_; }; // Implicitly convertible to the relevant view type. // // NOLINTNEXTLINE(google-explicit-constructor): Designed to implicitly decay. explicit(false) operator ViewT() const { return this->view_impl(); } // We can't chain the above conversion with the conversions on `ViewT` to add // const, so explicitly support adding const to produce a view here. template // NOLINTNEXTLINE(google-explicit-constructor) explicit(false) operator MapView() const requires(SameAsOneOf && SameAsOneOf) { return ViewT(*this); } // Convenience forwarder to the view type. template auto Contains(LookupKeyT lookup_key, KeyContextT key_context = KeyContextT()) const -> bool { return ViewT(*this).Contains(lookup_key, key_context); } // Convenience forwarder to the view type. template auto Lookup(LookupKeyT lookup_key, KeyContextT key_context = KeyContextT()) const -> LookupKVResult { return ViewT(*this).Lookup(lookup_key, key_context); } // Convenience forwarder to the view type. template auto operator[](LookupKeyT lookup_key) const -> ValueT* requires(std::default_initializable) { return ViewT(*this)[lookup_key]; } // Convenience forwarder to the view type. template auto ForEach(CallbackT callback) const -> void requires(std::invocable) { return ViewT(*this).ForEach(callback); } // Convenience forwarder to the view type. auto ComputeMetrics(KeyContextT key_context = KeyContextT()) const -> MetricsT { return ViewT(*this).ComputeMetrics(key_context); } // Insert a key and value into the map. If the key is already present, the new // value is discarded and the existing value preserved. template auto Insert(LookupKeyT lookup_key, ValueT new_v, KeyContextT key_context = KeyContextT()) -> InsertKVResult; // Insert a key into the map and call the provided callback if necessary to // produce a new value when no existing value is found. // // Example: `m.Insert(key, [] { return default_value; });` // // TODO: The `;` formatting below appears to be bugs in clang-format with // concepts that should be filed upstream. template auto Insert(LookupKeyT lookup_key, ValueCallbackT value_cb, KeyContextT key_context = KeyContextT()) -> InsertKVResult requires( !std::same_as && std::convertible_to()()), ValueT>) ; // Lookup a key in the map and if missing insert it and call the provided // callback to in-place construct both the key and value. The lookup key is // passed through to the callback so it needn't be captured and can be kept in // a register argument throughout. // // Example: // ```cpp // m.Insert("widget", [](MyStringViewType lookup_key, void* key_storage, // void* value_storage) { // new (key_storage) MyStringType(lookup_key); // new (value_storage) MyValueType(....); // }); // ``` template auto Insert(LookupKeyT lookup_key, InsertCallbackT insert_cb, KeyContextT key_context = KeyContextT()) -> InsertKVResult requires(!std::same_as && std::invocable); // Replace a key's value in a map if already present or insert it if not // already present. The new value is always used. template auto Update(LookupKeyT lookup_key, ValueT new_v, KeyContextT key_context = KeyContextT()) -> InsertKVResult; // Lookup or insert a key into the map, and set it's value to the result of // the `value_cb` callback. The callback is always run and its result is // always used, whether the key was already in the map or not. Any existing // value is replaced with the result. // // Example: `m.Update(key, [] { return new_value; });` template auto Update(LookupKeyT lookup_key, ValueCallbackT value_cb, KeyContextT key_context = KeyContextT()) -> InsertKVResult requires( !std::same_as && std::convertible_to()()), ValueT>) ; // Lookup or insert a key into the map. If not already present and the key is // inserted, the `insert_cb` is used to construct the new key and value in // place. When inserting, the lookup key is passed through to the callback so // it needn't be captured and can be kept in a register argument throughout. // If the key was already present, the `update_cb` is called to update the // existing key and value as desired. // // Example of counting occurrences: // ```cpp // m.Update(item, /*insert_cb=*/[](MyStringViewType lookup_key, // void* key_storage, void* value_storage) { // new (key_storage) MyItem(lookup_key); // new (value_storage) Count(1); // }, // /*update_cb=*/[](MyItem& /*key*/, Count& count) { // ++count; // }); // ``` template auto Update(LookupKeyT lookup_key, InsertCallbackT insert_cb, UpdateCallbackT update_cb, KeyContextT key_context = KeyContextT()) -> InsertKVResult requires(!std::same_as && std::invocable && std::invocable); // Grow the map to a specific allocation size. // // This will grow the map's hashtable if necessary for it to have an // allocation size of `target_alloc_size` which must be a power of two. Note // that this will not allow that many keys to be inserted, but a smaller // number based on the maximum load factor. If a specific number of insertions // need to be achieved without triggering growth, use the `GrowForInsertCount` // method. auto GrowToAllocSize(ssize_t target_alloc_size, KeyContextT key_context = KeyContextT()) -> void; // Grow the map sufficiently to allow inserting the specified number of keys. auto GrowForInsertCount(ssize_t count, KeyContextT key_context = KeyContextT()) -> void; // Erase a key from the map. template auto Erase(LookupKeyT lookup_key, KeyContextT key_context = KeyContextT()) -> bool; // Clear all key/value pairs from the map but leave the underlying hashtable // allocated and in place. auto Clear() -> void; protected: using ImplT::ImplT; }; // A data structure mapping from key to value. // // This map also supports small size optimization (or "SSO"). The provided // `SmallSize` type parameter indicates the size of an embedded buffer for // storing maps small enough to fit. The default is zero, which always allocates // a heap buffer on construction. When non-zero, must be a multiple of the // `MaxGroupSize` which is currently 16. The library will check that the size is // valid and provide an error at compile time if not. We don't automatically // select the next multiple or otherwise fit the size to the constraints to make // it clear in the code how much memory is used by the SSO buffer. // // This data structure optimizes heavily for small key types that are cheap to // move and even copy. Using types with large keys or expensive to copy keys may // create surprising performance bottlenecks. A `std::string` key should be fine // with generally small strings, but if some or many strings are large heap // allocations the performance of hashtable routines may be unacceptably bad and // another data structure or key design is likely preferable. // // Note that like `MapBase`, `Map` has "shallow" const semantics. Note also that // this type should typically not appear on API boundaries; either `MapBase` or // `MapView` should be used instead. template class Map : public RawHashtable::TableImpl< MapBase, SmallSize> { using BaseT = MapBase; using ImplT = RawHashtable::TableImpl; public: using KeyT = typename BaseT::KeyT; using ValueT = typename BaseT::ValueT; Map() = default; Map(const Map& arg) = default; Map(Map&& arg) noexcept = default; auto operator=(const Map& arg) -> Map& = default; auto operator=(Map&& arg) noexcept -> Map& = default; // Reset the entire state of the hashtable to as it was when constructed, // throwing away any intervening allocations. auto Reset() -> void; }; template template auto MapView::Contains( LookupKeyT lookup_key, KeyContextT key_context) const -> bool { return this->LookupEntry(lookup_key, key_context) != nullptr; } template template auto MapView::Lookup( LookupKeyT lookup_key, KeyContextT key_context) const -> LookupKVResult { return LookupKVResult(this->LookupEntry(lookup_key, key_context)); } template template auto MapView::operator[]( LookupKeyT lookup_key) const -> ValueT* requires(std::default_initializable) { auto result = Lookup(lookup_key, KeyContextT()); return result ? &result.value() : nullptr; } template template auto MapView::ForEach( CallbackT callback) -> void requires(std::invocable) { this->ForEachEntry( [callback](EntryT& entry) { callback(entry.key(), entry.value()); }, [](auto...) {}); } template template [[clang::always_inline]] auto MapBase::Insert( LookupKeyT lookup_key, ValueT new_v, KeyContextT key_context) -> InsertKVResult { return Insert( lookup_key, [&new_v](LookupKeyT lookup_key, void* key_storage, void* value_storage) { new (key_storage) KeyT(lookup_key); new (value_storage) ValueT(std::move(new_v)); }, key_context); } template template [[clang::always_inline]] auto MapBase::Insert( LookupKeyT lookup_key, ValueCallbackT value_cb, KeyContextT key_context) -> InsertKVResult requires( !std::same_as && std::convertible_to()()), ValueT>) { return Insert( lookup_key, [&value_cb](LookupKeyT lookup_key, void* key_storage, void* value_storage) { new (key_storage) KeyT(lookup_key); new (value_storage) ValueT(value_cb()); }, key_context); } template template [[clang::always_inline]] auto MapBase::Insert( LookupKeyT lookup_key, InsertCallbackT insert_cb, KeyContextT key_context) -> InsertKVResult requires(!std::same_as && std::invocable) { auto [entry, inserted] = this->InsertImpl(lookup_key, key_context); CARBON_DCHECK(entry, "Should always result in a valid index."); if (LLVM_LIKELY(!inserted)) { return InsertKVResult(false, *entry); } insert_cb(lookup_key, static_cast(&entry->key_storage), static_cast(&entry->value_storage)); return InsertKVResult(true, *entry); } template template [[clang::always_inline]] auto MapBase::Update( LookupKeyT lookup_key, ValueT new_v, KeyContextT key_context) -> InsertKVResult { return Update( lookup_key, [&new_v](LookupKeyT lookup_key, void* key_storage, void* value_storage) { new (key_storage) KeyT(lookup_key); new (value_storage) ValueT(std::move(new_v)); }, [&new_v](KeyT& /*key*/, ValueT& value) { value.~ValueT(); new (&value) ValueT(std::move(new_v)); }, key_context); } template template [[clang::always_inline]] auto MapBase::Update( LookupKeyT lookup_key, ValueCallbackT value_cb, KeyContextT key_context) -> InsertKVResult requires( !std::same_as && std::convertible_to()()), ValueT>) { return Update( lookup_key, [&value_cb](LookupKeyT lookup_key, void* key_storage, void* value_storage) { new (key_storage) KeyT(lookup_key); new (value_storage) ValueT(value_cb()); }, [&value_cb](KeyT& /*key*/, ValueT& value) { value.~ValueT(); new (&value) ValueT(value_cb()); }, key_context); } template template [[clang::always_inline]] auto MapBase::Update( LookupKeyT lookup_key, InsertCallbackT insert_cb, UpdateCallbackT update_cb, KeyContextT key_context) -> InsertKVResult requires(!std::same_as && std::invocable && std::invocable) { auto [entry, inserted] = this->InsertImpl(lookup_key, key_context); CARBON_DCHECK(entry, "Should always result in a valid index."); if (LLVM_LIKELY(!inserted)) { update_cb(entry->key(), entry->value()); return InsertKVResult(false, *entry); } insert_cb(lookup_key, static_cast(&entry->key_storage), static_cast(&entry->value_storage)); return InsertKVResult(true, *entry); } template auto MapBase::GrowToAllocSize( ssize_t target_alloc_size, KeyContextT key_context) -> void { this->GrowToAllocSizeImpl(target_alloc_size, key_context); } template auto MapBase::GrowForInsertCount( ssize_t count, KeyContextT key_context) -> void { this->GrowForInsertCountImpl(count, key_context); } template template auto MapBase::Erase( LookupKeyT lookup_key, KeyContextT key_context) -> bool { return this->EraseImpl(lookup_key, key_context); } template auto MapBase::Clear() -> void { this->ClearImpl(); } template auto Map::Reset() -> void { this->ResetImpl(); } } // namespace Carbon #endif // CARBON_COMMON_MAP_H_ ================================================ FILE: common/map_benchmark.cpp ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #include #include #include #include "absl/container/flat_hash_map.h" #include "common/map.h" #include "common/raw_hashtable_benchmark_helpers.h" #include "llvm/ADT/DenseMap.h" namespace Carbon { namespace { using RawHashtable::CarbonHashDI; using RawHashtable::GetKeysAndHitKeys; using RawHashtable::GetKeysAndMissKeys; using RawHashtable::HitArgs; using RawHashtable::LowZeroBitInt; using RawHashtable::ReportTableMetrics; using RawHashtable::SizeArgs; using RawHashtable::ValueToBool; // Helpers to synthesize some value of one of the three types we use as value // types. template auto MakeValue() -> T { if constexpr (std::is_same_v) { return "abc"; } else if constexpr (std::is_pointer_v) { static std::remove_pointer_t x; return &x; } else { return 42; } } template auto MakeValue2() -> T { if constexpr (std::is_same_v) { return "qux"; } else if constexpr (std::is_pointer_v) { static std::remove_pointer_t y; return &y; } else { return 7; } } template struct IsCarbonMapImpl : std::false_type {}; template struct IsCarbonMapImpl> : std::true_type {}; template static constexpr bool IsCarbonMap = IsCarbonMapImpl::value; // A wrapper around various map types that we specialize to implement a common // API used in the benchmarks for various different map data structures that // support different APIs. The primary template assumes a roughly // `std::unordered_map` API design, and types with a different API design are // supported through specializations. template struct MapWrapperImpl { using MapT = InMapT; using KeyT = typename MapT::key_type; using ValueT = typename MapT::mapped_type; MapT m; auto BenchContains(KeyT k) -> bool { return m.find(k) != m.end(); } auto BenchLookup(KeyT k) -> bool { auto it = m.find(k); if (it == m.end()) { return false; } return ValueToBool(it->second); } auto BenchInsert(KeyT k, ValueT v) -> bool { auto result = m.insert({k, v}); return result.second; } auto BenchUpdate(KeyT k, ValueT v) -> bool { auto result = m.insert({k, v}); result.first->second = v; return result.second; } auto BenchErase(KeyT k) -> bool { return m.erase(k) != 0; } }; // Explicit (partial) specialization for the Carbon map type that uses its // different API design. template struct MapWrapperImpl> { using MapT = Map; using KeyT = KT; using ValueT = VT; MapT m; auto BenchContains(KeyT k) -> bool { return m.Contains(k); } auto BenchLookup(KeyT k) -> bool { auto result = m.Lookup(k); if (!result) { return false; } return ValueToBool(result.value()); } auto BenchInsert(KeyT k, ValueT v) -> bool { auto result = m.Insert(k, v); return result.is_inserted(); } auto BenchUpdate(KeyT k, ValueT v) -> bool { auto result = m.Update(k, v); return result.is_inserted(); } auto BenchErase(KeyT k) -> bool { return m.Erase(k); } }; // Provide a way to override the Carbon Map specific benchmark runs with another // hashtable implementation. When building, you can use one of these enum names // in a macro define such as `-DCARBON_MAP_BENCH_OVERRIDE=Name` in order to // trigger a specific override for the `Map` type benchmarks. This is used to // get before/after runs that compare the performance of Carbon's Map versus // other implementations. enum class MapOverride : uint8_t { None, Abseil, Boost, LLVM, LLVMAndCarbonHash, }; #ifndef CARBON_MAP_BENCH_OVERRIDE #define CARBON_MAP_BENCH_OVERRIDE None #endif template struct MapWrapperOverride : MapWrapperImpl {}; template struct MapWrapperOverride, MapOverride::Abseil> : MapWrapperImpl> {}; template struct MapWrapperOverride, MapOverride::Boost> : MapWrapperImpl> {}; template struct MapWrapperOverride, MapOverride::LLVM> : MapWrapperImpl> {}; template struct MapWrapperOverride, MapOverride::LLVMAndCarbonHash> : MapWrapperImpl>> {}; template using MapWrapper = MapWrapperOverride; template auto ReportMetrics(const MapWrapper& m_wrapper, benchmark::State& state) -> void { // Report some extra statistics about the Carbon type. if constexpr (IsCarbonMap>) { ReportTableMetrics(m_wrapper.m, state); } } // NOLINTBEGIN(bugprone-macro-parentheses): Parentheses are incorrect here. #define MAP_BENCHMARK_ONE_OP_SIZE(NAME, APPLY, KT, VT) \ BENCHMARK(NAME>)->Apply(APPLY); \ BENCHMARK(NAME>)->Apply(APPLY); \ BENCHMARK(NAME>)->Apply(APPLY); \ BENCHMARK(NAME>)->Apply(APPLY); \ BENCHMARK(NAME>>)->Apply(APPLY) // NOLINTEND(bugprone-macro-parentheses) #define MAP_BENCHMARK_ONE_OP(NAME, APPLY) \ MAP_BENCHMARK_ONE_OP_SIZE(NAME, APPLY, int, int); \ MAP_BENCHMARK_ONE_OP_SIZE(NAME, APPLY, int*, int*); \ MAP_BENCHMARK_ONE_OP_SIZE(NAME, APPLY, int, llvm::StringRef); \ MAP_BENCHMARK_ONE_OP_SIZE(NAME, APPLY, llvm::StringRef, int) // Benchmark the minimal latency of checking if a key is contained within a map, // when it *is* definitely in that map. Because this is only really measuring // the *minimal* latency, it is more similar to a throughput benchmark. // // While this is structured to observe the latency of testing for presence of a // key, it is important to understand the reality of what this measures. Because // the boolean result testing for whether a key is in a map is fundamentally // provided not by accessing some data, but by branching on data to a control // flow path which sets the boolean to `true` or `false`, the result can be // speculatively provided based on predicting the conditional branch without // waiting for the results of the comparison to become available. And because // this is a small operation and we arrange for all the candidate keys to be // present, that branch *should* be predicted extremely well. The result is that // this measures the un-speculated latency of testing for presence which should // be small or zero. Which is why this is ultimately more similar to a // throughput benchmark. // // Because of these measurement oddities, the specific measurements here may not // be very interesting for predicting real-world performance in any way, but // they are useful for comparing how 'cheap' the operation is across changes to // the data structure or between similar data structures with similar // properties. template static void BM_MapContainsHit(benchmark::State& state) { using MapWrapperT = MapWrapper; using KT = typename MapWrapperT::KeyT; using VT = typename MapWrapperT::ValueT; MapWrapperT m; auto [keys, lookup_keys] = GetKeysAndHitKeys(state.range(0), state.range(1)); for (auto k : keys) { m.BenchInsert(k, MakeValue()); } ssize_t lookup_keys_size = lookup_keys.size(); while (state.KeepRunningBatch(lookup_keys_size)) { for (ssize_t i = 0; i < lookup_keys_size;) { // We block optimizing `i` as that has proven both more effective at // blocking the loop from being optimized away and avoiding disruption of // the generated code that we're benchmarking. benchmark::DoNotOptimize(i); bool result = m.BenchContains(lookup_keys[i]); CARBON_DCHECK(result); // We use the lookup success to step through keys, establishing a // dependency between each lookup. This doesn't fully allow us to measure // latency rather than throughput, as noted above. i += static_cast(result); } } ReportMetrics(m, state); } MAP_BENCHMARK_ONE_OP(BM_MapContainsHit, HitArgs); // Similar to `BM_MapContainsHit`, while this is structured as a latency // benchmark, the critical path is expected to be well predicted and so it // should turn into something closer to a throughput benchmark. template static void BM_MapContainsMiss(benchmark::State& state) { using MapWrapperT = MapWrapper; using KT = typename MapWrapperT::KeyT; using VT = typename MapWrapperT::ValueT; MapWrapperT m; auto [keys, lookup_keys] = GetKeysAndMissKeys(state.range(0)); for (auto k : keys) { m.BenchInsert(k, MakeValue()); } ssize_t lookup_keys_size = lookup_keys.size(); while (state.KeepRunningBatch(lookup_keys_size)) { for (ssize_t i = 0; i < lookup_keys_size;) { benchmark::DoNotOptimize(i); bool result = m.BenchContains(lookup_keys[i]); CARBON_DCHECK(!result); i += static_cast(!result); } } ReportMetrics(m, state); } MAP_BENCHMARK_ONE_OP(BM_MapContainsMiss, SizeArgs); // This is a genuine latency benchmark. We lookup a key in the hashtable and use // the value associated with that key in the critical path of loading the next // iteration's key. We still ensure the keys are always present, and so we // generally expect the data structure branches to be well predicted. But we // vary the keys aggressively to avoid any prediction artifacts from repeatedly // examining the same key. // // This latency can be very helpful for understanding a range of data structure // behaviors: // - Many users of hashtables are directly dependent on the latency of this // operation, and this micro-benchmark will reflect the expected latency for // them. // - Showing how latency varies across different sizes of table and different // fractions of the table being accessed (and thus needing space in the // cache). // // However, it remains an ultimately synthetic and unrepresentative benchmark. // It should primarily be used to understand the relative cost of these // operations between versions of the data structure or between related data // structures. // // We vary both the number of entries in the table and the number of distinct // keys used when doing lookups. As the table becomes large, the latter dictates // the fraction of the table that will be accessed and thus the working set size // of the benchmark. Querying the same small number of keys in even a large // table doesn't actually encounter any cache pressure, so only a few of these // benchmarks will show any effects of the caching subsystem. template static void BM_MapLookupHit(benchmark::State& state) { using MapWrapperT = MapWrapper; using KT = typename MapWrapperT::KeyT; using VT = typename MapWrapperT::ValueT; MapWrapperT m; auto [keys, lookup_keys] = GetKeysAndHitKeys(state.range(0), state.range(1)); for (auto k : keys) { m.BenchInsert(k, MakeValue()); } ssize_t lookup_keys_size = lookup_keys.size(); while (state.KeepRunningBatch(lookup_keys_size)) { for (ssize_t i = 0; i < lookup_keys_size;) { benchmark::DoNotOptimize(i); bool result = m.BenchLookup(lookup_keys[i]); CARBON_DCHECK(result); i += static_cast(result); } } ReportMetrics(m, state); } MAP_BENCHMARK_ONE_OP(BM_MapLookupHit, HitArgs); // We also do some minimal benchmarking with integers that have a // large number of low zero bits shifted into them. These present particular // challenges to the hashing strategy Carbon's hash tables use and so they help // form stress tests and benchmark to make sure the hash function quality // remains reasonable even under adverse conditions. We can't go past a certain // limit here without our hash tables becoming impossibly slow due to complete // collapse of the hash functions -- if we ever need to hash integers with more // than 32 low zero bits, we'll ask that code to use a custom hash algorithm. // // We don't benchmark these everywhere as they only provide marginal information // beyond the core types, and checking just this operation covers that // sufficiently. MAP_BENCHMARK_ONE_OP_SIZE(BM_MapLookupHit, HitArgs, LowZeroBitInt<12>, int); MAP_BENCHMARK_ONE_OP_SIZE(BM_MapLookupHit, HitArgs, LowZeroBitInt<24>, int); MAP_BENCHMARK_ONE_OP_SIZE(BM_MapLookupHit, HitArgs, LowZeroBitInt<32>, int); // This is an update throughput benchmark in practice. While whether the key was // a hit is kept in the critical path, we only use keys that are hits and so // expect that to be fully predicted and speculated. // // However, we expect this fairly closely matches how user code interacts with // an update-style API. It will have some conditional testing (even if just an // assert) on whether the key was a hit and otherwise continue executing. As a // consequence the actual update is expected to not be in a meaningful critical // path. // // This still provides a basic way to measure the cost of this operation, // especially when comparing between implementations or across different hash // tables. template static void BM_MapUpdateHit(benchmark::State& state) { using MapWrapperT = MapWrapper; using KT = typename MapWrapperT::KeyT; using VT = typename MapWrapperT::ValueT; MapWrapperT m; auto [keys, lookup_keys] = GetKeysAndHitKeys(state.range(0), state.range(1)); for (auto k : keys) { m.BenchInsert(k, MakeValue()); } ssize_t lookup_keys_size = lookup_keys.size(); while (state.KeepRunningBatch(lookup_keys_size)) { for (ssize_t i = 0; i < lookup_keys_size; ++i) { benchmark::DoNotOptimize(i); bool inserted = m.BenchUpdate(lookup_keys[i], MakeValue2()); CARBON_DCHECK(!inserted); } } ReportMetrics(m, state); } MAP_BENCHMARK_ONE_OP(BM_MapUpdateHit, HitArgs); // First erase and then insert the key. The code path will always be the same // here and so we expect this to largely be a throughput benchmark because of // branch prediction and speculative execution. // // We don't expect erase followed by insertion to be a common user code // sequence, but we don't have a good way of benchmarking either erase or insert // in isolation -- each would change the size of the table and thus the next // iteration's benchmark. And if we try to correct the table size outside of the // timed region, we end up trying to exclude too fine grained of a region from // timers to get good measurement data. // // Our solution is to benchmark both erase and insertion back to back. We can // then get a good profile of the code sequence of each, and at least measure // the sum cost of these reliably. Careful profiling can help attribute that // cost between erase and insert in order to understand which of the two // operations is contributing most to any performance artifacts observed. template static void BM_MapEraseUpdateHit(benchmark::State& state) { using MapWrapperT = MapWrapper; using KT = typename MapWrapperT::KeyT; using VT = typename MapWrapperT::ValueT; MapWrapperT m; auto [keys, lookup_keys] = GetKeysAndHitKeys(state.range(0), state.range(1)); for (auto k : keys) { m.BenchInsert(k, MakeValue()); } ssize_t lookup_keys_size = lookup_keys.size(); while (state.KeepRunningBatch(lookup_keys_size)) { for (ssize_t i = 0; i < lookup_keys_size; ++i) { benchmark::DoNotOptimize(i); m.BenchErase(lookup_keys[i]); benchmark::ClobberMemory(); bool inserted = m.BenchUpdate(lookup_keys[i], MakeValue2()); CARBON_DCHECK(inserted); } } } MAP_BENCHMARK_ONE_OP(BM_MapEraseUpdateHit, HitArgs); // NOLINTBEGIN(bugprone-macro-parentheses): Parentheses are incorrect here. #define MAP_BENCHMARK_OP_SEQ_SIZE(NAME, KT, VT) \ BENCHMARK(NAME>)->Apply(SizeArgs); \ BENCHMARK(NAME>)->Apply(SizeArgs); \ BENCHMARK(NAME>) \ ->Apply(SizeArgs); \ BENCHMARK(NAME>)->Apply(APPLY); \ BENCHMARK(NAME>>)->Apply(SizeArgs) // NOLINTEND(bugprone-macro-parentheses) #define MAP_BENCHMARK_OP_SEQ(NAME) \ MAP_BENCHMARK_OP_SEQ_SIZE(NAME, int, int); \ MAP_BENCHMARK_OP_SEQ_SIZE(NAME, int*, int*); \ MAP_BENCHMARK_OP_SEQ_SIZE(NAME, int, llvm::StringRef); \ MAP_BENCHMARK_OP_SEQ_SIZE(NAME, llvm::StringRef, int) // This is an interesting, somewhat specialized benchmark that measures the cost // of inserting a sequence of key/value pairs into a table with no collisions up // to some size and then inserting a colliding key and throwing away the table. // // This can give an idea of the cost of building up a map of a particular size, // but without actually using it. Or of algorithms like cycle-detection which // for some reason need an associative container. // // It also covers both the insert-into-an-empty-slot code path that isn't // covered elsewhere, and the code path for growing a table to a larger size. // // Because this benchmark operates on whole maps, we also compute the number of // probed keys for Carbon's set as that is both a general reflection of the // efficacy of the underlying hash function, and a direct factor that drives the // cost of these operations. template static void BM_MapInsertSeq(benchmark::State& state) { using MapWrapperT = MapWrapper; using KT = typename MapWrapperT::KeyT; using VT = typename MapWrapperT::ValueT; constexpr ssize_t LookupKeysSize = 1 << 8; auto [keys, lookup_keys] = GetKeysAndHitKeys(state.range(0), LookupKeysSize); // Note that we don't force batches that use all the lookup keys because // there's no difference in cache usage by covering all the different lookup // keys. ssize_t i = 0; for (auto _ : state) { benchmark::DoNotOptimize(i); MapWrapperT m; for (auto k : keys) { bool inserted = m.BenchInsert(k, MakeValue()); CARBON_DCHECK(inserted, "Must be a successful insert!"); } // Now insert a final random repeated key. bool inserted = m.BenchInsert(lookup_keys[i], MakeValue2()); CARBON_DCHECK(!inserted, "Must already be in the map!"); // Rotate through the shuffled keys. i = (i + static_cast(!inserted)) & (LookupKeysSize - 1); } // It can be easier in some cases to think of this as a key-throughput rate of // insertion rather than the latency of inserting N keys, so construct the // rate counter as well. state.counters["KeyRate"] = benchmark::Counter( keys.size(), benchmark::Counter::kIsIterationInvariantRate); // Report some extra statistics about the Carbon type. if constexpr (IsCarbonMap) { // Re-build a map outside of the timing loop to look at the statistics // rather than the timing. MapWrapperT m; for (auto k : keys) { bool inserted = m.BenchInsert(k, MakeValue()); CARBON_DCHECK(inserted, "Must be a successful insert!"); } ReportMetrics(m, state); // Uncomment this call to print out statistics about the index-collisions // among these keys for debugging: // // RawHashtable::DumpHashStatistics(keys); } } MAP_BENCHMARK_ONE_OP(BM_MapInsertSeq, SizeArgs); } // namespace } // namespace Carbon ================================================ FILE: common/map_test.cpp ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #include "common/map.h" #include #include #include #include #include #include #include "common/raw_hashtable_test_helpers.h" // Workaround for std::pair comparison deficiency in libc++ 16. #if defined(_LIBCPP_VERSION) && _LIBCPP_VERSION < 170000 namespace std { template requires(convertible_to && convertible_to) inline auto operator==( pair, std::reference_wrapper> lhs, pair rhs) -> bool { return lhs.first == static_cast(rhs.first) && lhs.second == static_cast(rhs.second); } } // namespace std #endif namespace Carbon::Testing { namespace { using RawHashtable::FixedHashKeyContext; using RawHashtable::IndexKeyContext; using RawHashtable::MoveOnlyTestData; using RawHashtable::TestData; using RawHashtable::TestKeyContext; using ::testing::Pair; using ::testing::UnorderedElementsAreArray; template auto ExpectMapElementsAre(MapT&& m, MatcherRangeT element_matchers) -> void { // Now collect the elements into a container. using KeyT = typename std::remove_reference::type::KeyT; using ValueT = typename std::remove_reference::type::ValueT; std::vector< std::pair, std::reference_wrapper>> map_entries; m.ForEach([&map_entries](KeyT& k, ValueT& v) { map_entries.push_back({std::ref(k), std::ref(v)}); }); // Use the GoogleMock unordered container matcher to validate and show errors // on wrong elements. EXPECT_THAT(map_entries, UnorderedElementsAreArray(element_matchers)); } // Allow directly using an initializer list. template auto ExpectMapElementsAre(MapT&& m, std::initializer_list element_matchers) -> void { std::vector element_matchers_storage = element_matchers; ExpectMapElementsAre(m, element_matchers_storage); } template auto MakeKeyValues(ValueCB value_cb, RangeT&& range, RangeTs&&... ranges) -> auto { using KeyT = typename RangeT::value_type; using ValueT = decltype(value_cb(std::declval())); std::vector> elements; auto add_range = [&](RangeT&& r) { for (const auto&& e : r) { elements.push_back({e, value_cb(e)}); } }; add_range(std::forward(range)); (add_range(std::forward(ranges)), ...); return elements; } template class MapTest : public ::testing::Test {}; template class MoveOnlyMapTest : public ::testing::Test {}; using Types = ::testing::Types< Map, Map, Map, Map, Map, Map, Map, Map, Map, Map>; TYPED_TEST_SUITE(MapTest, Types); using MoveOnlyTypes = ::testing::Types< Map, Map, Map, Map, Map>; TYPED_TEST_SUITE(MoveOnlyMapTest, MoveOnlyTypes); TYPED_TEST(MapTest, Basic) { TypeParam m; EXPECT_FALSE(m.Contains(42)); EXPECT_EQ(nullptr, m[42]); EXPECT_TRUE(m.Insert(1, 100).is_inserted()); ASSERT_TRUE(m.Contains(1)); auto result = m.Lookup(1); EXPECT_TRUE(result); EXPECT_EQ(1, result.key()); EXPECT_EQ(100, result.value()); EXPECT_EQ(100, *m[1]); // Reinsertion doesn't change the value. auto i_result = m.Insert(1, 101); EXPECT_FALSE(i_result.is_inserted()); EXPECT_EQ(100, i_result.value()); EXPECT_EQ(100, *m[1]); // Update does change the value. i_result = m.Update(1, 101); EXPECT_FALSE(i_result.is_inserted()); EXPECT_EQ(101, i_result.value()); EXPECT_EQ(101, *m[1]); // Verify all the elements. ExpectMapElementsAre(m, {Pair(1, 101)}); // Fill up a bunch to ensure we trigger growth a few times. for (int i : llvm::seq(2, 512)) { SCOPED_TRACE(llvm::formatv("Key: {0}", i).str()); EXPECT_TRUE(m.Insert(i, i * 100).is_inserted()); } ExpectMapElementsAre( m, MakeKeyValues([](int k) { return k * 100 + static_cast(k == 1); }, llvm::seq(1, 512))); for (int i : llvm::seq(1, 512)) { SCOPED_TRACE(llvm::formatv("Key: {0}", i).str()); EXPECT_FALSE(m.Insert(i, i * 100 + 1).is_inserted()); EXPECT_EQ(i * 100 + static_cast(i == 1), *m[i]); EXPECT_FALSE(m.Update(i, i * 100 + 1).is_inserted()); EXPECT_EQ(i * 100 + 1, *m[i]); } EXPECT_FALSE(m.Contains(513)); // Verify all the elements. ExpectMapElementsAre( m, MakeKeyValues([](int k) { return k * 100 + 1; }, llvm::seq(1, 512))); } TYPED_TEST(MapTest, FactoryApi) { TypeParam m; EXPECT_TRUE(m.Insert(1, [] { return 100; }).is_inserted()); ASSERT_TRUE(m.Contains(1)); EXPECT_EQ(100, *m[1]); // Reinsertion doesn't invoke the callback. EXPECT_FALSE(m.Insert(1, []() -> int { llvm_unreachable("Should never be called!"); }).is_inserted()); // Update does invoke the callback. auto i_result = m.Update(1, [] { return 101; }); EXPECT_FALSE(i_result.is_inserted()); EXPECT_EQ(101, i_result.value()); EXPECT_EQ(101, *m[1]); } TYPED_TEST(MapTest, Copy) { using MapT = TypeParam; MapT m; // Make sure we exceed the small size for some of the map types, but not all // of them, so we cover all the combinations of copying between small and // large. for (int i : llvm::seq(1, 24)) { SCOPED_TRACE(llvm::formatv("Key: {0}", i).str()); ASSERT_TRUE(m.Insert(i, i * 100).is_inserted()); } MapT other_m1 = m; ExpectMapElementsAre( other_m1, MakeKeyValues([](int k) { return k * 100; }, llvm::seq(1, 24))); // Add some more elements to the original. for (int i : llvm::seq(24, 32)) { SCOPED_TRACE(llvm::formatv("Key: {0}", i).str()); ASSERT_TRUE(m.Insert(i, i * 100).is_inserted()); } // The first copy doesn't change. ExpectMapElementsAre( other_m1, MakeKeyValues([](int k) { return k * 100; }, llvm::seq(1, 24))); // A new copy does. MapT other_m2 = m; ExpectMapElementsAre( other_m2, MakeKeyValues([](int k) { return k * 100; }, llvm::seq(1, 32))); // Copy-assign updates. other_m1 = m; ExpectMapElementsAre( other_m1, MakeKeyValues([](int k) { return k * 100; }, llvm::seq(1, 32))); // Self-assign is a no-op. other_m1 = const_cast(other_m1); ExpectMapElementsAre( other_m1, MakeKeyValues([](int k) { return k * 100; }, llvm::seq(1, 32))); // But mutating original still doesn't change copies. for (int i : llvm::seq(32, 48)) { SCOPED_TRACE(llvm::formatv("Key: {0}", i).str()); ASSERT_TRUE(m.Insert(i, i * 100).is_inserted()); } ExpectMapElementsAre( other_m1, MakeKeyValues([](int k) { return k * 100; }, llvm::seq(1, 32))); ExpectMapElementsAre( other_m2, MakeKeyValues([](int k) { return k * 100; }, llvm::seq(1, 32))); } TYPED_TEST(MapTest, Move) { using MapT = TypeParam; MapT m; // Make sure we exceed the small size for some of the map types, but not all // of them, so we cover all the combinations of moving between small and // large. for (int i : llvm::seq(1, 24)) { SCOPED_TRACE(llvm::formatv("Key: {0}", i).str()); EXPECT_TRUE(m.Insert(i, i * 100).is_inserted()); } MapT other_m1 = std::move(m); ExpectMapElementsAre( other_m1, MakeKeyValues([](int k) { return k * 100; }, llvm::seq(1, 24))); // Add some more elements. for (int i : llvm::seq(24, 32)) { SCOPED_TRACE(llvm::formatv("Key: {0}", i).str()); ASSERT_TRUE(other_m1.Insert(i, i * 100).is_inserted()); } ExpectMapElementsAre( other_m1, MakeKeyValues([](int k) { return k * 100; }, llvm::seq(1, 32))); // Move back over a moved-from. m = std::move(other_m1); ExpectMapElementsAre( m, MakeKeyValues([](int k) { return k * 100; }, llvm::seq(1, 32))); // Copy over moved-from state also works. other_m1 = m; ExpectMapElementsAre( other_m1, MakeKeyValues([](int k) { return k * 100; }, llvm::seq(1, 32))); // Now add still more elements. for (int i : llvm::seq(32, 48)) { SCOPED_TRACE(llvm::formatv("Key: {0}", i).str()); ASSERT_TRUE(other_m1.Insert(i, i * 100).is_inserted()); } ExpectMapElementsAre( other_m1, MakeKeyValues([](int k) { return k * 100; }, llvm::seq(1, 48))); // And move-assign over the copy looks like the moved-from table not the copy. other_m1 = std::move(m); ExpectMapElementsAre( other_m1, MakeKeyValues([](int k) { return k * 100; }, llvm::seq(1, 32))); // Self-swap (which does a self-move) works and is a no-op. std::swap(other_m1, other_m1); ExpectMapElementsAre( other_m1, MakeKeyValues([](int k) { return k * 100; }, llvm::seq(1, 32))); // Test copying of a moved-from table over a valid table and // self-move-assign. The former is required to be valid, and the latter is // in at least the case of self-move-assign-when-moved-from, but the result // can be in any state so just do them and ensure we don't crash. MapT other_m2 = other_m1; // NOLINTNEXTLINE(bugprone-use-after-move): Testing required use-after-move. other_m2 = m; other_m1 = std::move(other_m1); m = std::move(m); } TYPED_TEST(MoveOnlyMapTest, MoveOnlyTypes) { using MapT = TypeParam; static_assert(!std::is_copy_assignable_v); static_assert(!std::is_copy_constructible_v); static_assert(std::is_move_assignable_v); static_assert(std::is_move_constructible_v); auto make_map = [] { MapT m; // Make sure we exceed the small size for some of the map types, but not all // of them, so we cover all the combinations of moving between small and // large. for (int i : llvm::seq(1, 24)) { SCOPED_TRACE(llvm::formatv("Key: {0}", i).str()); EXPECT_TRUE(m.Insert(i, i * 100).is_inserted()); } return m; }; MapT m = make_map(); MapT other_m1 = std::move(m); ExpectMapElementsAre( other_m1, MakeKeyValues([](int k) { return k * 100; }, llvm::seq(1, 24))); // Add some more elements. for (int i : llvm::seq(24, 32)) { SCOPED_TRACE(llvm::formatv("Key: {0}", i).str()); ASSERT_TRUE(other_m1.Insert(i, i * 100).is_inserted()); } ExpectMapElementsAre( other_m1, MakeKeyValues([](int k) { return k * 100; }, llvm::seq(1, 32))); // Move back over a moved-from. m = std::move(other_m1); ExpectMapElementsAre( m, MakeKeyValues([](int k) { return k * 100; }, llvm::seq(1, 32))); // Now add still more elements, crossing the small size limit for all tested // map types. for (int i : llvm::seq(32, 72)) { SCOPED_TRACE(llvm::formatv("Key: {0}", i).str()); ASSERT_TRUE(m.Insert(i, i * 100).is_inserted()); } ExpectMapElementsAre( m, MakeKeyValues([](int k) { return k * 100; }, llvm::seq(1, 72))); // Assignment replaces the contents. m = make_map(); ExpectMapElementsAre( m, MakeKeyValues([](int k) { return k * 100; }, llvm::seq(1, 24))); // Self-swap (which does a self-move) works and is a no-op. std::swap(m, m); ExpectMapElementsAre( m, MakeKeyValues([](int k) { return k * 100; }, llvm::seq(1, 24))); } TYPED_TEST(MapTest, Conversions) { using MapT = TypeParam; using KeyT = MapT::KeyT; using ValueT = MapT::ValueT; using KeyContextT = MapT::KeyContextT; MapT m; ASSERT_TRUE(m.Insert(1, 101).is_inserted()); ASSERT_TRUE(m.Insert(2, 102).is_inserted()); ASSERT_TRUE(m.Insert(3, 103).is_inserted()); ASSERT_TRUE(m.Insert(4, 104).is_inserted()); MapView mv = m; MapView cmv = m; MapView cmv2 = m; MapView cmv3 = m; EXPECT_TRUE(mv.Contains(1)); EXPECT_EQ(101, *mv[1]); EXPECT_TRUE(cmv.Contains(2)); EXPECT_EQ(102, *cmv[2]); EXPECT_TRUE(cmv2.Contains(3)); EXPECT_EQ(103, *cmv2[3]); EXPECT_TRUE(cmv3.Contains(4)); EXPECT_EQ(104, *cmv3[4]); } TYPED_TEST(MapTest, GrowToAllocSize) { using MapT = TypeParam; MapT m; // Grow when empty. May be a no-op for some small sizes. m.GrowToAllocSize(32); // Add some elements that will need to be propagated through subsequent // growths. Also delete some. ssize_t storage_bytes = m.ComputeMetrics().storage_bytes; for (int i : llvm::seq(1, 24)) { SCOPED_TRACE(llvm::formatv("Key: {0}", i).str()); ASSERT_TRUE(m.Insert(i, i * 100).is_inserted()); } for (int i : llvm::seq(1, 8)) { SCOPED_TRACE(llvm::formatv("Key: {0}", i).str()); ASSERT_TRUE(m.Erase(i)); } // No further growth triggered. EXPECT_EQ(storage_bytes, m.ComputeMetrics().storage_bytes); // No-op. m.GrowToAllocSize(16); ExpectMapElementsAre( m, MakeKeyValues([](int k) { return k * 100; }, llvm::seq(8, 24))); // No further growth triggered. EXPECT_EQ(storage_bytes, m.ComputeMetrics().storage_bytes); // Get a few doubling based growths, and at least one beyond the largest small // size. m.GrowToAllocSize(64); ExpectMapElementsAre( m, MakeKeyValues([](int k) { return k * 100; }, llvm::seq(8, 24))); m.GrowToAllocSize(128); ExpectMapElementsAre( m, MakeKeyValues([](int k) { return k * 100; }, llvm::seq(8, 24))); // Update the storage bytes after growth. EXPECT_LT(storage_bytes, m.ComputeMetrics().storage_bytes); storage_bytes = m.ComputeMetrics().storage_bytes; // Add some more, but not enough to trigger further growth, and then grow by // several more multiples of two to test handling large growth. for (int i : llvm::seq(24, 48)) { SCOPED_TRACE(llvm::formatv("Key: {0}", i).str()); ASSERT_TRUE(m.Insert(i, i * 100).is_inserted()); } for (int i : llvm::seq(8, 16)) { SCOPED_TRACE(llvm::formatv("Key: {0}", i).str()); ASSERT_TRUE(m.Erase(i)); } // No growth from insertions. EXPECT_EQ(storage_bytes, m.ComputeMetrics().storage_bytes); m.GrowToAllocSize(1024); ExpectMapElementsAre( m, MakeKeyValues([](int k) { return k * 100; }, llvm::seq(16, 48))); // Storage should have grown. EXPECT_LT(storage_bytes, m.ComputeMetrics().storage_bytes); } TYPED_TEST(MapTest, GrowForInsert) { using MapT = TypeParam; MapT m; m.GrowForInsertCount(42); ssize_t storage_bytes = m.ComputeMetrics().storage_bytes; for (int i : llvm::seq(1, 42)) { SCOPED_TRACE(llvm::formatv("Key: {0}", i).str()); ASSERT_TRUE(m.Insert(i, i * 100).is_inserted()); } ExpectMapElementsAre( m, MakeKeyValues([](int k) { return k * 100; }, llvm::seq(1, 42))); EXPECT_EQ(storage_bytes, m.ComputeMetrics().storage_bytes); // Erase many elements and grow again for another insert. for (int i : llvm::seq(1, 32)) { SCOPED_TRACE(llvm::formatv("Key: {0}", i).str()); ASSERT_TRUE(m.Erase(i)); } m.GrowForInsertCount(42); storage_bytes = m.ComputeMetrics().storage_bytes; for (int i : llvm::seq(42, 84)) { SCOPED_TRACE(llvm::formatv("Key: {0}", i).str()); ASSERT_TRUE(m.Insert(i, i * 100).is_inserted()); } ExpectMapElementsAre( m, MakeKeyValues([](int k) { return k * 100; }, llvm::seq(32, 84))); EXPECT_EQ(storage_bytes, m.ComputeMetrics().storage_bytes); // Erase all the elements, then grow for a much larger insertion and insert // again. for (int i : llvm::seq(32, 84)) { SCOPED_TRACE(llvm::formatv("Key: {0}", i).str()); ASSERT_TRUE(m.Erase(i)); } m.GrowForInsertCount(321); storage_bytes = m.ComputeMetrics().storage_bytes; for (int i : llvm::seq(128, 321 + 128)) { SCOPED_TRACE(llvm::formatv("Key: {0}", i).str()); ASSERT_TRUE(m.Insert(i, i * 100).is_inserted()); } ExpectMapElementsAre(m, MakeKeyValues([](int k) { return k * 100; }, llvm::seq(128, 321 + 128))); EXPECT_EQ(storage_bytes, m.ComputeMetrics().storage_bytes); } // This test is largely exercising the underlying `RawHashtable` implementation // with complex growth, erasure, and re-growth. TYPED_TEST(MapTest, ComplexOpSequence) { // Use a small size as well to cover more growth scenarios. TypeParam m; EXPECT_FALSE(m.Contains(42)); EXPECT_EQ(nullptr, m[42]); EXPECT_TRUE(m.Insert(1, 100).is_inserted()); ASSERT_TRUE(m.Contains(1)); auto result = m.Lookup(1); EXPECT_TRUE(result); EXPECT_EQ(1, result.key()); EXPECT_EQ(100, result.value()); EXPECT_EQ(100, *m[1]); // Reinsertion doesn't change the value. auto i_result = m.Insert(1, 101); EXPECT_FALSE(i_result.is_inserted()); EXPECT_EQ(100, i_result.value()); EXPECT_EQ(100, *m[1]); // Update does change the value. i_result = m.Update(1, 101); EXPECT_FALSE(i_result.is_inserted()); EXPECT_EQ(101, i_result.value()); EXPECT_EQ(101, *m[1]); // Verify all the elements. ExpectMapElementsAre(m, {Pair(1, 101)}); // Fill up the small buffer but don't overflow it. for (int i : llvm::seq(2, 5)) { SCOPED_TRACE(llvm::formatv("Key: {0}", i).str()); EXPECT_TRUE(m.Insert(i, i * 100).is_inserted()); } for (int i : llvm::seq(1, 5)) { SCOPED_TRACE(llvm::formatv("Key: {0}", i).str()); ASSERT_TRUE(m.Contains(i)); EXPECT_EQ(i * 100 + static_cast(i == 1), *m[i]); EXPECT_FALSE(m.Insert(i, i * 100 + 1).is_inserted()); EXPECT_EQ(i * 100 + static_cast(i == 1), *m[i]); EXPECT_FALSE(m.Update(i, i * 100 + 1).is_inserted()); EXPECT_EQ(i * 100 + 1, *m[i]); } EXPECT_FALSE(m.Contains(5)); // Verify all the elements. ExpectMapElementsAre( m, {Pair(1, 101), Pair(2, 201), Pair(3, 301), Pair(4, 401)}); // Erase some entries from the small buffer. EXPECT_FALSE(m.Erase(42)); EXPECT_TRUE(m.Erase(2)); EXPECT_EQ(101, *m[1]); EXPECT_EQ(nullptr, m[2]); EXPECT_EQ(301, *m[3]); EXPECT_EQ(401, *m[4]); EXPECT_TRUE(m.Erase(1)); EXPECT_EQ(nullptr, m[1]); EXPECT_EQ(nullptr, m[2]); EXPECT_EQ(301, *m[3]); EXPECT_EQ(401, *m[4]); EXPECT_TRUE(m.Erase(4)); EXPECT_EQ(nullptr, m[1]); EXPECT_EQ(nullptr, m[2]); EXPECT_EQ(301, *m[3]); EXPECT_EQ(nullptr, m[4]); // Fill them back in, but with a different order and going back to the // original value. EXPECT_TRUE(m.Insert(1, 100).is_inserted()); EXPECT_TRUE(m.Insert(2, 200).is_inserted()); EXPECT_TRUE(m.Insert(4, 400).is_inserted()); EXPECT_EQ(100, *m[1]); EXPECT_EQ(200, *m[2]); EXPECT_EQ(301, *m[3]); EXPECT_EQ(400, *m[4]); // Then update their values to match. EXPECT_FALSE(m.Update(1, 101).is_inserted()); EXPECT_FALSE(m.Update(2, 201).is_inserted()); EXPECT_FALSE(m.Update(4, 401).is_inserted()); // Now fill up the first metadata group. for (int i : llvm::seq(5, 14)) { SCOPED_TRACE(llvm::formatv("Key: {0}", i).str()); EXPECT_TRUE(m.Insert(i, i * 100).is_inserted()); } for (int i : llvm::seq(1, 14)) { SCOPED_TRACE(llvm::formatv("Key: {0}", i).str()); ASSERT_TRUE(m.Contains(i)); EXPECT_EQ(i * 100 + static_cast(i < 5), *m[i]); EXPECT_FALSE(m.Insert(i, i * 100 + 2).is_inserted()); EXPECT_EQ(i * 100 + static_cast(i < 5), *m[i]); EXPECT_FALSE(m.Update(i, i * 100 + 2).is_inserted()); EXPECT_EQ(i * 100 + 2, *m[i]); } EXPECT_FALSE(m.Contains(42)); // Verify all the elements by walking the entire map. ExpectMapElementsAre( m, {Pair(1, 102), Pair(2, 202), Pair(3, 302), Pair(4, 402), Pair(5, 502), Pair(6, 602), Pair(7, 702), Pair(8, 802), Pair(9, 902), Pair(10, 1002), Pair(11, 1102), Pair(12, 1202), Pair(13, 1302)}); // Now fill up several more groups. for (int i : llvm::seq(14, 100)) { SCOPED_TRACE(llvm::formatv("Key: {0}", i).str()); EXPECT_TRUE(m.Insert(i, i * 100).is_inserted()); } for (int i : llvm::seq(1, 100)) { SCOPED_TRACE(llvm::formatv("Key: {0}", i).str()); ASSERT_TRUE(m.Contains(i)); EXPECT_EQ(i * 100 + 2 * static_cast(i < 14), *m[i]); EXPECT_FALSE(m.Insert(i, i * 100 + 1).is_inserted()); EXPECT_EQ(i * 100 + 2 * static_cast(i < 14), *m[i]); EXPECT_FALSE(m.Update(i, i * 100 + 3).is_inserted()); EXPECT_EQ(i * 100 + 3, *m[i]); } EXPECT_FALSE(m.Contains(420)); // Check walking the entire container. ExpectMapElementsAre( m, MakeKeyValues([](int k) { return k * 100 + 3; }, llvm::seq(1, 100))); // Clear back to empty. m.Clear(); EXPECT_FALSE(m.Contains(42)); EXPECT_EQ(nullptr, m[42]); // Refill but with both overlapping and different values. for (int i : llvm::seq(50, 150)) { EXPECT_TRUE(m.Insert(i, i * 100).is_inserted()); } for (int i : llvm::seq(50, 150)) { SCOPED_TRACE(llvm::formatv("Key: {0}", i).str()); ASSERT_TRUE(m.Contains(i)); EXPECT_EQ(i * 100, *m[i]); EXPECT_FALSE(m.Insert(i, i * 100 + 1).is_inserted()); EXPECT_EQ(i * 100, *m[i]); EXPECT_FALSE(m.Update(i, i * 100 + 1).is_inserted()); EXPECT_EQ(i * 100 + 1, *m[i]); } EXPECT_FALSE(m.Contains(42)); EXPECT_FALSE(m.Contains(420)); ExpectMapElementsAre( m, MakeKeyValues([](int k) { return k * 100 + 1; }, llvm::seq(50, 150))); EXPECT_FALSE(m.Erase(42)); EXPECT_TRUE(m.Contains(73)); EXPECT_TRUE(m.Erase(73)); EXPECT_FALSE(m.Contains(73)); for (int i : llvm::seq(102, 136)) { SCOPED_TRACE(llvm::formatv("Key: {0}", i).str()); EXPECT_TRUE(m.Contains(i)); EXPECT_TRUE(m.Erase(i)); EXPECT_FALSE(m.Contains(i)); } for (int i : llvm::seq(50, 150)) { SCOPED_TRACE(llvm::formatv("Key: {0}", i).str()); if (i == 73 || (i >= 102 && i < 136)) { continue; } ASSERT_TRUE(m.Contains(i)); EXPECT_EQ(i * 100 + 1, *m[i]); EXPECT_FALSE(m.Insert(i, i * 100 + 2).is_inserted()); EXPECT_EQ(i * 100 + 1, *m[i]); EXPECT_FALSE(m.Update(i, i * 100 + 2).is_inserted()); EXPECT_EQ(i * 100 + 2, *m[i]); } EXPECT_TRUE(m.Insert(73, 73 * 100 + 3).is_inserted()); EXPECT_EQ(73 * 100 + 3, *m[73]); ExpectMapElementsAre( m, MakeKeyValues([](int k) { return k * 100 + 2 + (k == 73); }, llvm::seq(50, 102), llvm::seq(136, 150))); // Reset back to empty and small. m.Reset(); EXPECT_FALSE(m.Contains(42)); EXPECT_EQ(nullptr, m[42]); // Refill but with both overlapping and different values, now triggering // growth too. Also, use update instead of insert. for (int i : llvm::seq(75, 175)) { SCOPED_TRACE(llvm::formatv("Key: {0}", i).str()); EXPECT_TRUE(m.Update(i, i * 100).is_inserted()); } for (int i : llvm::seq(75, 175)) { SCOPED_TRACE(llvm::formatv("Key: {0}", i).str()); ASSERT_TRUE(m.Contains(i)); EXPECT_EQ(i * 100, *m[i]); EXPECT_FALSE(m.Insert(i, i * 100 + 1).is_inserted()); EXPECT_EQ(i * 100, *m[i]); EXPECT_FALSE(m.Update(i, i * 100 + 1).is_inserted()); EXPECT_EQ(i * 100 + 1, *m[i]); } EXPECT_FALSE(m.Contains(42)); EXPECT_FALSE(m.Contains(420)); ExpectMapElementsAre( m, MakeKeyValues([](int k) { return k * 100 + 1; }, llvm::seq(75, 175))); EXPECT_FALSE(m.Erase(42)); EXPECT_TRUE(m.Contains(93)); EXPECT_TRUE(m.Erase(93)); EXPECT_FALSE(m.Contains(93)); for (int i : llvm::seq(102, 136)) { SCOPED_TRACE(llvm::formatv("Key: {0}", i).str()); EXPECT_TRUE(m.Contains(i)); EXPECT_TRUE(m.Erase(i)); EXPECT_FALSE(m.Contains(i)); } for (int i : llvm::seq(75, 175)) { SCOPED_TRACE(llvm::formatv("Key: {0}", i).str()); if (i == 93 || (i >= 102 && i < 136)) { continue; } ASSERT_TRUE(m.Contains(i)); EXPECT_EQ(i * 100 + 1, *m[i]); EXPECT_FALSE(m.Insert(i, i * 100 + 2).is_inserted()); EXPECT_EQ(i * 100 + 1, *m[i]); EXPECT_FALSE(m.Update(i, i * 100 + 2).is_inserted()); EXPECT_EQ(i * 100 + 2, *m[i]); } EXPECT_TRUE(m.Insert(93, 93 * 100 + 3).is_inserted()); EXPECT_EQ(93 * 100 + 3, *m[93]); ExpectMapElementsAre( m, MakeKeyValues([](int k) { return k * 100 + 2 + (k == 93); }, llvm::seq(75, 102), llvm::seq(136, 175))); } template class MapCollisionTest : public ::testing::Test {}; using CollisionTypes = ::testing::Types< Map>, Map>, Map>, Map(0)>>>; TYPED_TEST_SUITE(MapCollisionTest, CollisionTypes); TYPED_TEST(MapCollisionTest, Basic) { TypeParam m; // Fill the map through a couple of growth steps, verifying at each step. Note // that because this is a collision test, we synthesize actively harmful // hashes in terms of collisions and so this test is essentially quadratic. We // need to keep it relatively small. for (int i : llvm::seq(1, 256)) { SCOPED_TRACE(llvm::formatv("Key: {0}", i).str()); EXPECT_TRUE(m.Insert(i, i * 100).is_inserted()); } ExpectMapElementsAre( m, MakeKeyValues([](int k) { return k * 100; }, llvm::seq(1, 256))); // Erase and re-fill from the back. for (int i : llvm::seq(192, 256)) { SCOPED_TRACE(llvm::formatv("Key: {0}", i).str()); EXPECT_TRUE(m.Erase(i)); } ExpectMapElementsAre( m, MakeKeyValues([](int k) { return k * 100; }, llvm::seq(1, 192))); for (int i : llvm::seq(192, 256)) { SCOPED_TRACE(llvm::formatv("Key: {0}", i).str()); EXPECT_TRUE(m.Insert(i, i * 100 + 1).is_inserted()); } ExpectMapElementsAre(m, MakeKeyValues([](int k) { return k * 100 + (k >= 192); }, llvm::seq(1, 256))); // Erase and re-fill from the front. for (int i : llvm::seq(1, 64)) { SCOPED_TRACE(llvm::formatv("Key: {0}", i).str()); EXPECT_TRUE(m.Erase(i)); } ExpectMapElementsAre(m, MakeKeyValues([](int k) { return k * 100 + (k >= 192); }, llvm::seq(64, 256))); for (int i : llvm::seq(1, 64)) { SCOPED_TRACE(llvm::formatv("Key: {0}", i).str()); EXPECT_TRUE(m.Insert(i, i * 100 + 1).is_inserted()); } ExpectMapElementsAre( m, MakeKeyValues([](int k) { return k * 100 + (k < 64) + (k >= 192); }, llvm::seq(1, 256))); // Erase and re-fill from the middle. for (int i : llvm::seq(64, 192)) { SCOPED_TRACE(llvm::formatv("Key: {0}", i).str()); EXPECT_TRUE(m.Erase(i)); } ExpectMapElementsAre(m, MakeKeyValues([](int k) { return k * 100 + 1; }, llvm::seq(1, 64), llvm::seq(192, 256))); for (int i : llvm::seq(64, 192)) { SCOPED_TRACE(llvm::formatv("Key: {0}", i).str()); EXPECT_TRUE(m.Insert(i, i * 100 + 1).is_inserted()); } ExpectMapElementsAre( m, MakeKeyValues([](int k) { return k * 100 + 1; }, llvm::seq(1, 256))); // Erase and re-fill from both the back and front. for (auto s : {llvm::seq(192, 256), llvm::seq(1, 64)}) { for (int i : s) { SCOPED_TRACE(llvm::formatv("Key: {0}", i).str()); EXPECT_TRUE(m.Erase(i)); } } ExpectMapElementsAre( m, MakeKeyValues([](int k) { return k * 100 + 1; }, llvm::seq(64, 192))); for (auto s : {llvm::seq(192, 256), llvm::seq(1, 64)}) { for (int i : s) { SCOPED_TRACE(llvm::formatv("Key: {0}", i).str()); EXPECT_TRUE(m.Insert(i, i * 100 + 2).is_inserted()); } } ExpectMapElementsAre( m, MakeKeyValues([](int k) { return k * 100 + 1 + (k < 64) + (k >= 192); }, llvm::seq(1, 256))); // And update the middle elements in place. for (int i : llvm::seq(64, 192)) { SCOPED_TRACE(llvm::formatv("Key: {0}", i).str()); EXPECT_FALSE(m.Update(i, i * 100 + 2).is_inserted()); } ExpectMapElementsAre( m, MakeKeyValues([](int k) { return k * 100 + 2; }, llvm::seq(1, 256))); } TEST(MapContextTest, Basic) { llvm::SmallVector keys; for (int i : llvm::seq(0, 513)) { keys.push_back(i * 100000); } IndexKeyContext key_context(keys); Map> m; EXPECT_FALSE(m.Contains(42, key_context)); EXPECT_TRUE(m.Insert(1, 100, key_context).is_inserted()); ASSERT_TRUE(m.Contains(1, key_context)); auto result = m.Lookup(TestData(100000), key_context); EXPECT_TRUE(result); EXPECT_EQ(1, result.key()); EXPECT_EQ(100, result.value()); // Reinsertion doesn't change the value. Also, double check a temporary // context. auto i_result = m.Insert(1, 101, IndexKeyContext(keys)); EXPECT_FALSE(i_result.is_inserted()); EXPECT_EQ(100, i_result.value()); // Update does change the value. i_result = m.Update(1, 101, key_context); EXPECT_FALSE(i_result.is_inserted()); EXPECT_EQ(101, i_result.value()); // Verify all the elements. ExpectMapElementsAre(m, {Pair(1, 101)}); // Fill up a bunch to ensure we trigger growth a few times. for (int i : llvm::seq(2, 512)) { SCOPED_TRACE(llvm::formatv("Key: {0}", i).str()); EXPECT_TRUE(m.Insert(i, i * 100, key_context).is_inserted()); } // Check all the elements, including using the context. for (int j : llvm::seq(1, 512)) { SCOPED_TRACE(llvm::formatv("Assert key: {0}", j).str()); ASSERT_EQ(j * 100 + static_cast(j == 1), m.Lookup(j, key_context).value()); ASSERT_EQ(j * 100 + static_cast(j == 1), m.Lookup(TestData(j * 100000), key_context).value()); } for (int i : llvm::seq(1, 512)) { SCOPED_TRACE(llvm::formatv("Key: {0}", i).str()); EXPECT_FALSE(m.Insert(i, i * 100 + 1, key_context).is_inserted()); EXPECT_EQ(i * 100 + static_cast(i == 1), m.Lookup(i, key_context).value()); EXPECT_FALSE(m.Update(i, i * 100 + 1, key_context).is_inserted()); EXPECT_EQ(i * 100 + 1, m.Lookup(i, key_context).value()); } EXPECT_FALSE(m.Contains(0, key_context)); EXPECT_FALSE(m.Contains(512, key_context)); // Verify all the elements. ExpectMapElementsAre( m, MakeKeyValues([](int k) { return k * 100 + 1; }, llvm::seq(1, 512))); } } // namespace } // namespace Carbon::Testing ================================================ FILE: common/move_only.h ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #ifndef CARBON_COMMON_MOVE_ONLY_H_ #define CARBON_COMMON_MOVE_ONLY_H_ namespace Carbon { // A base class that indicates a type is move-only. Typically this can be // achieved by declaring the move constructor and move assignment yourself; this // type should be used only when doing that is not feasible, such as when // aggregate initialization is still desired. // // This class uses CRTP to ensure that each MoveOnly base class has a different // type. This is important to avoid the compiler adding extra padding to derived // classes to give multiple MoveOnly subobjects of the same type different // addresses. template struct MoveOnly { MoveOnly() = default; MoveOnly(MoveOnly&&) noexcept = default; auto operator=(MoveOnly&&) noexcept -> MoveOnly& = default; }; } // namespace Carbon #endif // CARBON_COMMON_MOVE_ONLY_H_ ================================================ FILE: common/ostream.h ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #ifndef CARBON_COMMON_OSTREAM_H_ #define CARBON_COMMON_OSTREAM_H_ // Libraries should include this header instead of raw_ostream. #include #include #include #include "llvm/Support/Compiler.h" #include "llvm/Support/raw_os_ostream.h" #include "llvm/Support/raw_ostream.h" // IWYU pragma: export namespace Carbon { // CRTP base class for printable types. Children (DerivedT) must implement: // - auto Print(llvm::raw_ostream& out) const -> void template // NOLINTNEXTLINE(bugprone-crtp-constructor-accessibility) class Printable { // Provides simple printing for debuggers. LLVM_DUMP_METHOD auto Dump() const -> std::string { std::string buffer; llvm::raw_string_ostream stream(buffer); static_cast(this)->Print(stream); return buffer; } // Supports printing to llvm::raw_ostream. friend auto operator<<(llvm::raw_ostream& out, const DerivedT& obj) -> llvm::raw_ostream& { obj.Print(out); return out; } // Supports printing to std::ostream. friend auto operator<<(std::ostream& out, const DerivedT& obj) -> std::ostream& { llvm::raw_os_ostream raw_os(out); obj.Print(raw_os); return out; } // Allows GoogleTest and GoogleMock to print pointers by dereferencing them. // This is important to allow automatic printing of arguments of mocked // APIs. friend auto PrintTo(DerivedT* p, std::ostream* out) -> void { *out << static_cast(p); // Also print the object if non-null. if (p) { *out << " pointing to " << *p; } } }; // Helper class for printing strings with escapes. // // For example: // stream << FormatEscaped(str); // Is equivalent to: // stream.write_escaped(str); class FormatEscaped : public Printable { public: explicit FormatEscaped(llvm::StringRef str, bool use_hex_escapes = false) : str_(str), use_hex_escapes_(use_hex_escapes) {} auto Print(llvm::raw_ostream& out) const -> void { out.write_escaped(str_, use_hex_escapes_); } private: llvm::StringRef str_; bool use_hex_escapes_; }; // Returns the result of printing the value. template requires std::derived_from> inline auto PrintToString(const T& val) -> std::string { std::string str; llvm::raw_string_ostream stream(str); stream << val; return str; } } // namespace Carbon namespace llvm { // Injects an `operator<<` overload into the `llvm` namespace which detects LLVM // types with `raw_ostream` overloads and uses that to map to a `std::ostream` // overload. This allows LLVM types to be printed to `std::ostream` via their // `raw_ostream` operator overloads, which is needed both for logging and // testing. // // To make this overload be unusually low priority, it is designed to take even // the `std::ostream` parameter as a template, and SFINAE disable itself unless // that template parameter is derived from `std::ostream`. This ensures that an // *explicit* operator will be preferred when provided. Some LLVM types may have // this, and so we want to prioritize accordingly. // // It would be slightly cleaner for LLVM itself to provide this overload in // `raw_os_ostream.h` so that we wouldn't need to inject into LLVM's namespace, // but supporting `std::ostream` isn't a priority for LLVM so we handle it // locally instead. template requires std::derived_from, std::ostream> && (!std::same_as, raw_ostream>) && requires(raw_ostream& os, const ClassT& value) { os << value; } auto operator<<(StreamT& standard_out, const ClassT& value) -> StreamT& { raw_os_ostream(standard_out) << value; return standard_out; } } // namespace llvm #endif // CARBON_COMMON_OSTREAM_H_ ================================================ FILE: common/pretty_stack_trace_function.h ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #ifndef CARBON_COMMON_PRETTY_STACK_TRACE_FUNCTION_H_ #define CARBON_COMMON_PRETTY_STACK_TRACE_FUNCTION_H_ #include #include "llvm/Support/PrettyStackTrace.h" namespace Carbon { // Calls `fn` as part of LLVM's pretty stack trace support. Implementations // should typically have a terminating `\n`. class PrettyStackTraceFunction : public llvm::PrettyStackTraceEntry { public: explicit PrettyStackTraceFunction( std::functionvoid> fn) : fn_(std::move(fn)) {} ~PrettyStackTraceFunction() override = default; auto print(llvm::raw_ostream& output) const -> void override { fn_(output); } private: const std::functionvoid> fn_; }; } // namespace Carbon #endif // CARBON_COMMON_PRETTY_STACK_TRACE_FUNCTION_H_ ================================================ FILE: common/raw_hashtable.cpp ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #include "common/raw_hashtable.h" #include namespace Carbon::RawHashtable { volatile std::byte global_addr_seed{1}; } // namespace Carbon::RawHashtable ================================================ FILE: common/raw_hashtable.h ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #ifndef CARBON_COMMON_RAW_HASHTABLE_H_ #define CARBON_COMMON_RAW_HASHTABLE_H_ #include #include #include #include #include #include #include #include #include "common/check.h" #include "common/concepts.h" #include "common/hashing.h" #include "common/raw_hashtable_metadata_group.h" #include "llvm/Support/Compiler.h" #include "llvm/Support/MathExtras.h" // A namespace collecting a set of low-level utilities for building hashtable // data structures. These should only be used as implementation details of // higher-level data-structure APIs. // // The utilities here use the `hashtable_key_context.h` provided `KeyContext` to // support the necessary hashtable operations on keys: hashing and comparison. // This also serves as the customization point for hashtables built on this // infrastructure for those operations. See that header file for details. // // These utilities support hashtables following a *specific* API design pattern, // and using Small-Size Optimization, or "SSO", when desired. We expect there to // be three layers to any hashtable design: // // - A *view* type: a read-only view of the hashtable contents. This type should // be a value type and is expected to be passed by-value in APIs. However, it // will have `const`-reference semantics, much like a `std::string_view`. Note // that the *entries* will continue to be mutable, it is only the *table* that // is read-only. // // - A *base* type: a base class type of the actual hashtable, which allows // almost all mutable operations but erases any specific SSO buffer size. // Because this is a base of the actual hash table, it is designed to be // passed as a non-`const` reference or pointer. // // - A *table* type: the actual hashtable which derives from the base type and // adds any desired SSO storage buffer. Beyond the physical storage, it also // allows resetting the table to its initial state & allocated size, as well // as copying and moving the table. // // For complete examples of the API design, see `set.h` for a hashtable-based // set data structure, and `map.h` for a hashtable-based map data structure. // // The hashtable design implemented here has several key invariants and design // elements that are essential to all three of the types above and the // functionality they provide. // // - The underlying hashtable uses [open addressing], a power-of-two table size, // and quadratic probing rather than closed addressing and chaining. // // [open addressing]: https://en.wikipedia.org/wiki/Open_addressing // // - Each _slot_ in the table corresponds to a key, a value, and one byte of // metadata. Each _entry_ is a key and value. The key and value for an entry // are stored together. // // - The allocated storage is organized into an array of metadata bytes followed // by an array of entry storage. // // - The metadata byte corresponding to each entry marks that entry is either // empty, deleted, or present. When present, a 7-bit tag is also stored using // another 7 bits from the hash of the entry key. // // - The storage for an entry is an internal type that should not be exposed to // users, and instead only the underlying keys and values. // // - The hash addressing and probing occurs over *groups* of slots rather than // individual entries. When inserting a new entry, it can be added to the // group it hashes to as long it is not full, and can even replace a slot with // a tombstone indicating a previously deleted entry. Only when the group is // full will it look at the next group in the probe sequence. As a result, // there may be entries in a group where a different group is the start of // that entry's probe sequence. Also, when performing a lookup, every group in // the probe sequence must be inspected for the lookup key until it is found // or the group has an empty slot. // // - Groups are scanned rapidly using the one-byte metadata for each entry in // the group and CPU instructions that allow comparing all of the metadata for // a group in parallel. For more details on the metadata group encoding and // scanning, see `raw_hashtable_metadata_group.h`. // // - `GroupSize` is a platform-specific relatively small power of two that fits // in some hardware register. However, `MaxGroupSize` is provided as a // portable max that is also a power of two. The table storage, whether // provided by an SSO buffer or allocated, is required to be a multiple of // `MaxGroupSize` to keep the requirement portable but sufficient for all // platforms. // // - There is *always* an allocated table of some multiple of `MaxGroupSize`. // This allows accesses to be branchless. When heap allocated, we pro-actively // allocate at least a minimum heap size table. When there is a small-size // optimization (SSO) buffer, that provides the initial allocation. // // - The table performs a minimal amount of bookkeeping that limits the APIs it // can support: // - `alloc_size` is the size of the table *allocated* (not *used*), and is // always a power of 2 at least as big as `MinAllocatedSize`. // - `storage` is a pointer to the storage for the `alloc_size` slots of the // table, and never null. // - `small_alloc_size` is the maximum `alloc_size` where the table is stored // in the object itself instead of separately on the heap. In this case, // `storage` points to `small_storage_`. // - `growth_budget` is the number of entries that may be added before the // table allocation is doubled. It is always // `GrowthThresholdForAllocSize(alloc_size)` minus the number of // non-empty (filled or deleted) slots. If it ever falls to 0, the table // is grown to keep it greater than 0. // There is also the "moved-from" state where the table may only be // reinitialized or destroyed where the `alloc_size` is 0 and `storage` is // null. Since it doesn't track the exact number of filled entries in a table, // it doesn't support a container-style `size` API. // // - There is no direct iterator support because of the complexity of embedding // the group-based metadata scanning into an iterator model. Instead, there is // just a for-each method that is passed a lambda to observe all entries. The // order of this observation is also not guaranteed. namespace Carbon::RawHashtable { // Which prefetch strategies to enable can be controlled via macros to enable // doing experiments. // // Currently, benchmarking on both modern AMD and ARM CPUs seems to indicate // that the entry group prefetching is more beneficial than metadata, but that // benefit is degraded when enabling them both. This determined our current // default of no metadata prefetch but enabled entry group prefetch. // // Override these by defining them as part of the build explicitly to either `0` // or `1`. If left undefined, the defaults will be supplied. #ifndef CARBON_ENABLE_PREFETCH_METADATA #define CARBON_ENABLE_PREFETCH_METADATA 0 #endif #ifndef CARBON_ENABLE_PREFETCH_ENTRY_GROUP #define CARBON_ENABLE_PREFETCH_ENTRY_GROUP 1 #endif // If allocating storage, allocate a minimum of one cacheline of group metadata // or a minimum of one group, whichever is larger. inline constexpr ssize_t MinAllocatedSize = std::max(64, MaxGroupSize); // An entry in the hashtable storage of a `KeyT` and `ValueT` object. // // Allows manual construction, destruction, and access to these values so we can // create arrays af the entries prior to populating them with actual keys and // values. template struct StorageEntry { static constexpr bool IsTriviallyDestructible = std::is_trivially_destructible_v && std::is_trivially_destructible_v; static constexpr bool IsTriviallyRelocatable = IsTriviallyDestructible && std::is_trivially_move_constructible_v && std::is_trivially_move_constructible_v; static constexpr bool IsCopyable = IsTriviallyRelocatable || (std::is_copy_constructible_v && std::is_copy_constructible_v); auto key() const -> const KeyT& { // Ensure we don't need more alignment than available. Inside a method body // to apply to the complete type. static_assert( alignof(StorageEntry) <= MinAllocatedSize, "The minimum allocated size turns into the alignment of our array of " "storage entries as they follow the metadata byte array."); return *std::launder(reinterpret_cast(&key_storage)); } auto key() -> KeyT& { return const_cast(const_cast(this)->key()); } auto value() const -> const ValueT& { return *std::launder(reinterpret_cast(&value_storage)); } auto value() -> ValueT& { return const_cast(const_cast(this)->value()); } // We handle destruction and move manually as we only want to expose distinct // `KeyT` and `ValueT` subobjects to user code that may need to do in-place // construction. As a consequence, this struct only provides the storage and // we have to manually manage the construction, move, and destruction of the // objects. auto Destroy() -> void { static_assert(!IsTriviallyDestructible, "Should never instantiate when trivial!"); key().~KeyT(); value().~ValueT(); } auto CopyFrom(const StorageEntry& entry) -> void { if constexpr (IsTriviallyRelocatable) { memcpy(this, &entry, sizeof(StorageEntry)); } else { new (&key_storage) KeyT(entry.key()); new (&value_storage) ValueT(entry.value()); } } // Move from an expiring entry and destroy that entry's key and value. // Optimizes to directly use `memcpy` when correct. auto MoveFrom(StorageEntry&& entry) -> void { if constexpr (IsTriviallyRelocatable) { memcpy(this, &entry, sizeof(StorageEntry)); } else { new (&key_storage) KeyT(std::move(entry.key())); entry.key().~KeyT(); new (&value_storage) ValueT(std::move(entry.value())); entry.value().~ValueT(); } } alignas(KeyT) std::byte key_storage[sizeof(KeyT)]; alignas(ValueT) std::byte value_storage[sizeof(ValueT)]; }; // A specialization of the storage entry for sets without a distinct value type. // Somewhat duplicative with the key-value version, but C++ specialization makes // doing better difficult. template struct StorageEntry { static constexpr bool IsTriviallyDestructible = std::is_trivially_destructible_v; static constexpr bool IsTriviallyRelocatable = IsTriviallyDestructible && std::is_trivially_move_constructible_v; static constexpr bool IsCopyable = IsTriviallyRelocatable || std::is_copy_constructible_v; auto key() const -> const KeyT& { // Ensure we don't need more alignment than available. static_assert( alignof(StorageEntry) <= MinAllocatedSize, "The minimum allocated size turns into the alignment of our array of " "storage entries as they follow the metadata byte array."); return *std::launder(reinterpret_cast(&key_storage)); } auto key() -> KeyT& { return const_cast(const_cast(this)->key()); } auto Destroy() -> void { static_assert(!IsTriviallyDestructible, "Should never instantiate when trivial!"); key().~KeyT(); } auto CopyFrom(const StorageEntry& entry) -> void requires(IsCopyable) { if constexpr (IsTriviallyRelocatable) { memcpy(this, &entry, sizeof(StorageEntry)); } else { new (&key_storage) KeyT(entry.key()); } } auto MoveFrom(StorageEntry&& entry) -> void { if constexpr (IsTriviallyRelocatable) { memcpy(this, &entry, sizeof(StorageEntry)); } else { new (&key_storage) KeyT(std::move(entry.key())); entry.key().~KeyT(); } } alignas(KeyT) std::byte key_storage[sizeof(KeyT)]; }; struct Metrics { // How many keys are present in the table. ssize_t key_count = 0; // How many slots of the table are reserved due to deleted markers required to // preserve probe sequences. ssize_t deleted_count = 0; // How many bytes of allocated storage are used by the table. Note, does not // include the table object or any small-size buffer. ssize_t storage_bytes = 0; // How many keys have required probing beyond the initial group. These are the // keys with a probe distance > 0. ssize_t probed_key_count = 0; // The probe distance averaged over every key. If every key is in its initial // group, this will be zero as no keys will have a larger probe distance. In // general, we want this to be as close to zero as possible. double probe_avg_distance = 0.0; // The maximum probe distance found for a single key in the table. ssize_t probe_max_distance = 0; // The average number of probing comparisons required to locate a specific key // in the table. This is how many comparisons are required *before* the key is // located, or the *failed* comparisons. We always have to do one successful // comparison at the end. This successful comparison isn't counted because // that focuses this metric on the overhead the table is introducing, and // keeps a "perfect" table with an average of `0.0` here similar to the // perfect average of `0.0` average probe distance. double probe_avg_compares = 0.0; // The maximum number of probing comparisons required to locate a specific // key in the table. ssize_t probe_max_compares = 0; }; // A placeholder empty type used to model pointers to the allocated buffer of // storage. // // The allocated storage doesn't have a meaningful static layout -- it consists // of an array of metadata groups followed by an array of storage entries. // However, we want to be able to mark pointers to this and so use pointers to // this placeholder type as that signifier. // // This is a complete, empty type so that it can be used as a base class of a // specific concrete storage type for compile-time sized storage. struct Storage {}; // Forward declaration to support friending, see the definition below. template class BaseImpl; // Implementation helper for defining a read-only view type for a hashtable. // // A specific user-facing hashtable view type should derive privately from this // type, and forward the implementation of its interface to functions in this // type. // // The methods available to user-facing hashtable types are `protected`, and // where they are expected to directly map to a public API, named with an // `Impl`. The suffix naming ensures types don't `using` in these low-level APIs // but declare their own and implement them by forwarding to these APIs. We // don't want users to have to read these implementation details to understand // their container's API, so none of these methods should be `using`-ed into the // user facing types. // // Some of the types are just convenience aliases and aren't important to // surface as part of the user-facing type API for readers and so those are // reasonable to add via a `using`. // // Some methods are used by other parts of the raw hashtable implementation. // Those are kept `private` and where necessary the other components of the raw // hashtable implementation are friended to give access to them. template class ViewImpl { protected: using KeyT = InputKeyT; using ValueT = InputValueT; using KeyContextT = InputKeyContextT; using EntryT = StorageEntry; using MetricsT = Metrics; friend class BaseImpl; template friend class TableImpl; // Make more-`const` types friends to enable conversions that add `const`. friend class ViewImpl; friend class ViewImpl; friend class ViewImpl; ViewImpl() = default; // Support adding `const` to either key or value type of some other view. template explicit(false) ViewImpl(ViewImpl other_view) requires(SameAsOneOf && SameAsOneOf) : alloc_size_(other_view.alloc_size_), storage_(other_view.storage_) {} // Looks up an entry in the hashtable and returns its address or null if not // present. template auto LookupEntry(LookupKeyT lookup_key, KeyContextT key_context) const -> EntryT*; // Calls `entry_callback` for each entry in the hashtable. All the entries // within a specific group are visited first, and then `group_callback` is // called on the group itself. The `group_callback` is typically only used by // the internals of the hashtable. template auto ForEachEntry(EntryCallbackT entry_callback, GroupCallbackT group_callback) const -> void; // Returns a collection of informative metrics on the the current state of the // table, useful for performance analysis. These include relatively slow to // compute metrics requiring deep inspection of the table's state. auto ComputeMetricsImpl(KeyContextT key_context) const -> MetricsT; private: ViewImpl(ssize_t alloc_size, Storage* storage) : alloc_size_(alloc_size), storage_(storage) {} // Computes the offset from the metadata array to the entries array for a // given size. This is trivial, but we use this routine to enforce invariants // on the sizes. static constexpr auto EntriesOffset(ssize_t alloc_size) -> ssize_t { CARBON_DCHECK(llvm::isPowerOf2_64(alloc_size), "Size must be a power of two for a hashed buffer!"); // The size is always a power of two. We prevent any too-small sizes so it // being a power of two provides the needed alignment. As a result, the // offset is exactly the size. We validate this here to catch alignment bugs // early. CARBON_DCHECK(static_cast(alloc_size) == llvm::alignTo(alloc_size)); return alloc_size; } // Compute the allocated table's byte size. static constexpr auto AllocByteSize(ssize_t alloc_size) -> ssize_t { return EntriesOffset(alloc_size) + sizeof(EntryT) * alloc_size; } auto metadata() const -> uint8_t* { return reinterpret_cast(storage_); } auto entries() const -> EntryT* { return reinterpret_cast(reinterpret_cast(storage_) + EntriesOffset(alloc_size_)); } // Prefetch the metadata prior to probing. This is to overlap any of the // memory access latency we can with the hashing of a key or other // latency-bound operation prior to probing. auto PrefetchMetadata() const -> void { if constexpr (CARBON_ENABLE_PREFETCH_METADATA) { // Prefetch with a "low" temporal locality as we're primarily expecting a // brief use of the metadata and then to return to application code. __builtin_prefetch(metadata(), /*read*/ 0, /*low-locality*/ 1); } } // Prefetch an entry. This prefetches for read as it is primarily expected to // be used in the probing path, and writing afterwards isn't especially slowed // down. We don't want to synthesize writes unless we *know* we're going to // write. static auto PrefetchEntryGroup(const EntryT* entry_group) -> void { if constexpr (CARBON_ENABLE_PREFETCH_ENTRY_GROUP) { // Prefetch with a "low" temporal locality as we're primarily expecting a // brief use of the entries and then to return to application code. __builtin_prefetch(entry_group, /*read*/ 0, /*low-locality*/ 1); } } ssize_t alloc_size_; Storage* storage_; }; // Implementation helper for defining a read-write base type for a hashtable // that type-erases any SSO buffer. // // A specific user-facing hashtable base type should derive using *`protected`* // inheritance from this type, and forward the implementation of its interface // to functions in this type. // // Other than the use of `protected` inheritance, the patterns for this type, // and how to build user-facing hashtable base types from it, mirror those of // `ViewImpl`. See its documentation for more details. template class BaseImpl { protected: using KeyT = InputKeyT; using ValueT = InputValueT; using KeyContextT = InputKeyContextT; using ViewImplT = ViewImpl; using EntryT = typename ViewImplT::EntryT; using MetricsT = typename ViewImplT::MetricsT; BaseImpl(int small_alloc_size, Storage* small_storage) : small_alloc_size_(small_alloc_size) { CARBON_CHECK(small_alloc_size >= 0); Construct(small_storage); } // Only used for copying and moving, and leaves storage uninitialized. BaseImpl(ssize_t alloc_size, int growth_budget, int small_alloc_size) : view_impl_(alloc_size, nullptr), growth_budget_(growth_budget), small_alloc_size_(small_alloc_size) {} // Destruction must be handled by the table where it can destroy entries in // any small buffer, so make the base destructor protected but defaulted here. ~BaseImpl() = default; // NOLINTNEXTLINE(google-explicit-constructor): Designed to implicitly decay. explicit(false) operator ViewImplT() const { return view_impl(); } auto view_impl() const -> ViewImplT { return view_impl_; } // Looks up the provided key in the hashtable. If found, returns a pointer to // that entry and `false`. // // If not found, will locate an empty entry for inserting into, set the // metadata for that entry, and return a pointer to the entry and `true`. When // necessary, this will grow the hashtable to cause there to be sufficient // empty entries. template auto InsertImpl(LookupKeyT lookup_key, KeyContextT key_context) -> std::pair; // Grow the table to specific allocation size. // // This will grow the the table if necessary for it to have an allocation size // of `target_alloc_size` which must be a power of two. Note that this will // not allow that many keys to be inserted into the hashtable, but a smaller // number based on the load factor. If a specific number of insertions need to // be achieved without triggering growth, use the `GrowForInsertCountImpl` // method. auto GrowToAllocSizeImpl(ssize_t target_alloc_size, KeyContextT key_context) -> void; // Grow the table to allow inserting the specified number of keys. auto GrowForInsertCountImpl(ssize_t count, KeyContextT key_context) -> void; // Looks up the entry in the hashtable, and if found destroys the entry and // returns `true`. If not found, returns `false`. // // Does not release any memory, just leaves a tombstone behind so this entry // cannot be found and the slot can in theory be reused. template auto EraseImpl(LookupKeyT lookup_key, KeyContextT key_context) -> bool; // Erases all entries in the hashtable but leaves the allocated storage. auto ClearImpl() -> void; private: template friend class TableImpl; static constexpr ssize_t Alignment = std::max( alignof(MetadataGroup), alignof(StorageEntry)); // Implementation of inline small storage for the provided key type, value // type, and small size. Specialized for a zero small size to be an empty // struct. template struct SmallStorage : Storage { alignas(Alignment) uint8_t metadata[SmallSize]; mutable StorageEntry entries[SmallSize]; }; // Specialized storage with no inline buffer to avoid any extra alignment. template <> struct SmallStorage<0> {}; static auto Allocate(ssize_t alloc_size) -> Storage*; static auto Deallocate(Storage* storage, ssize_t alloc_size) -> void; auto growth_budget() const -> ssize_t { return growth_budget_; } auto alloc_size() const -> ssize_t { return view_impl_.alloc_size_; } auto alloc_size() -> ssize_t& { return view_impl_.alloc_size_; } auto storage() const -> Storage* { return view_impl_.storage_; } auto storage() -> Storage*& { return view_impl_.storage_; } auto metadata() const -> uint8_t* { return view_impl_.metadata(); } auto entries() const -> EntryT* { return view_impl_.entries(); } auto small_alloc_size() const -> ssize_t { return static_cast(small_alloc_size_); } auto is_small() const -> bool { CARBON_DCHECK(alloc_size() >= small_alloc_size()); return alloc_size() == small_alloc_size(); } // Wrapper to call `ViewImplT::PrefetchStorage`, see that method for details. auto PrefetchStorage() const -> void { view_impl_.PrefetchMetadata(); } auto Construct(Storage* small_storage) -> void; auto Destroy() -> void; auto CopySlotsFrom(const BaseImpl& arg) -> void requires(EntryT::IsCopyable); auto MoveFrom(BaseImpl&& arg, Storage* small_storage) -> void; auto InsertIntoEmpty(HashCode hash) -> EntryT*; static auto ComputeNextAllocSize(ssize_t old_alloc_size) -> ssize_t; static auto GrowthThresholdForAllocSize(ssize_t alloc_size) -> ssize_t; auto GrowToNextAllocSize(KeyContextT key_context) -> void; auto GrowAndInsert(HashCode hash, KeyContextT key_context) -> EntryT*; ViewImplT view_impl_; int growth_budget_; int small_alloc_size_; }; // Implementation helper for defining a hashtable type with an SSO buffer. // // A specific user-facing hashtable should derive privately from this // type, and forward the implementation of its interface to functions in this // type. It should provide the corresponding user-facing hashtable base type as // the `InputBaseT` type parameter (rather than a key/value pair), and this type // will in turn derive from that provided base type. This allows derived-to-base // conversion from the user-facing hashtable type to the user-facing hashtable // base type. And it does so keeping the inheritance linear. The resulting // linear inheritance hierarchy for a `Map` type will look like: // // Map // ↓ // TableImpl> // ↓ // MapBase // ↓ // BaseImpl // // Other than this inheritance technique, the patterns for this type, and how to // build user-facing hashtable types from it, mirror those of `ViewImpl`. See // its documentation for more details. template class TableImpl : public InputBaseT { protected: using BaseT = InputBaseT; TableImpl() : BaseT(SmallSize, small_storage()) {} TableImpl(const TableImpl& arg) requires(BaseT::EntryT::IsCopyable); TableImpl(TableImpl&& arg) noexcept; auto operator=(const TableImpl& arg) -> TableImpl& requires(BaseT::EntryT::IsCopyable); auto operator=(TableImpl&& arg) noexcept -> TableImpl&; ~TableImpl(); // Resets the hashtable to its initial state, clearing all entries and // releasing all memory. If the hashtable had an SSO buffer, that is restored // as the storage. Otherwise, a minimum sized table storage is allocated. auto ResetImpl() -> void; private: using KeyT = BaseT::KeyT; using ValueT = BaseT::ValueT; using EntryT = BaseT::EntryT; using SmallStorage = BaseT::template SmallStorage; auto small_storage() const -> Storage*; auto SetUpStorage() -> void; [[no_unique_address]] mutable SmallStorage small_storage_; }; //////////////////////////////////////////////////////////////////////////////// // // Only implementation details below this point. // //////////////////////////////////////////////////////////////////////////////// // Computes a seed that provides a small amount of entropy from ASLR where // available with minimal cost. The priority is speed, and this computes the // entropy in a way that doesn't require loading from memory, merely accessing // entropy already available without accessing memory. inline auto ComputeSeed() -> uint64_t { // A global variable whose address is used as a seed. This allows ASLR to // introduce some variation in hashtable ordering when enabled via the code // model for globals. extern volatile std::byte global_addr_seed; return reinterpret_cast(&global_addr_seed); } inline auto ComputeProbeMaskFromSize(ssize_t size) -> size_t { CARBON_DCHECK(llvm::isPowerOf2_64(size), "Size must be a power of two for a hashed buffer!"); // Since `size` is a power of two, we can make sure the probes are less // than `size` by making the mask `size - 1`. We also mask off the low // bits so the probes are a multiple of the size of the groups of entries. return (size - 1) & ~GroupMask; } // This class handles building a sequence of probe indices from a given // starting point, including both the quadratic growth and masking the index // to stay within the bucket array size. The starting point doesn't need to be // clamped to the size ahead of time (or even be positive), we will do it // internally. // // For reference on quadratic probing: // https://en.wikipedia.org/wiki/Quadratic_probing // // We compute the quadratic probe index incrementally, but we can also compute // it mathematically and will check that the incremental result matches our // mathematical expectation. We use the quadratic probing formula of: // // p(start, step) = (start + (step + step^2) / 2) (mod size / GroupSize) // // However, we compute it incrementally and scale all the variables by the group // size so it can be used as an index without an additional multiplication. class ProbeSequence { public: ProbeSequence(ssize_t start, ssize_t size) { mask_ = ComputeProbeMaskFromSize(size); p_ = start & mask_; #ifndef NDEBUG start_ = start & mask_; size_ = size; #endif } auto Next() -> void { step_ += GroupSize; p_ = (p_ + step_) & mask_; #ifndef NDEBUG // Verify against the quadratic formula we expect to be following by scaling // everything down by `GroupSize`. CARBON_DCHECK( (p_ / GroupSize) == ((start_ / GroupSize + (step_ / GroupSize + (step_ / GroupSize) * (step_ / GroupSize)) / 2) % (size_ / GroupSize)), "Index in probe sequence does not match the expected formula."); CARBON_DCHECK(step_ < size_, "We necessarily visit all groups, so we can't have more " "probe steps than groups."); #endif } auto index() const -> ssize_t { return p_; } private: ssize_t step_ = 0; size_t mask_; ssize_t p_; #ifndef NDEBUG ssize_t start_; ssize_t size_; #endif }; // TODO: Evaluate keeping this outlined to see if macro benchmarks observe the // same perf hit as micro benchmarks. template template auto ViewImpl::LookupEntry( LookupKeyT lookup_key, KeyContextT key_context) const -> EntryT* { PrefetchMetadata(); ssize_t local_size = alloc_size_; CARBON_DCHECK(local_size > 0); uint8_t* local_metadata = metadata(); HashCode hash = key_context.HashKey(lookup_key, ComputeSeed()); auto [hash_index, tag] = hash.ExtractIndexAndTag<7>(); EntryT* local_entries = entries(); // Walk through groups of entries using a quadratic probe starting from // `hash_index`. ProbeSequence s(hash_index, local_size); do { ssize_t group_index = s.index(); // Load the group's metadata and prefetch the entries for this group. The // prefetch here helps hide key access latency while we're matching the // metadata. MetadataGroup g = MetadataGroup::Load(local_metadata, group_index); EntryT* group_entries = &local_entries[group_index]; PrefetchEntryGroup(group_entries); // For each group, match the tag against the metadata to extract the // potentially matching entries within the group. auto metadata_matched_range = g.Match(tag); if (LLVM_LIKELY(metadata_matched_range)) { // If any entries in this group potentially match based on their metadata, // walk each candidate and compare its key to see if we have definitively // found a match. auto byte_it = metadata_matched_range.begin(); auto byte_end = metadata_matched_range.end(); do { EntryT* entry = byte_it.index_ptr(group_entries); if (LLVM_LIKELY(key_context.KeyEq(lookup_key, entry->key()))) { __builtin_assume(entry != nullptr); return entry; } ++byte_it; } while (LLVM_UNLIKELY(byte_it != byte_end)); } // We failed to find a matching entry in this bucket, so check if there are // empty slots as that indicates we're done probing -- no later probed index // could have a match. auto empty_byte_matched_range = g.MatchEmpty(); if (LLVM_LIKELY(empty_byte_matched_range)) { return nullptr; } s.Next(); // We use a weird construct of an "unlikely" condition of `true`. The goal // is to get the compiler to not prioritize the back edge of the loop for // code layout, and in at least some tests this seems to be an effective // construct for achieving this. } while (LLVM_UNLIKELY(true)); } // Note that we force inlining here because we expect to be called with lambdas // that will in turn be inlined to form the loop body. We don't want function // boundaries within the loop for performance, and recognizing the degree of // simplification from inlining these callbacks may be difficult to // automatically recognize. template template [[clang::always_inline]] auto ViewImpl::ForEachEntry( EntryCallbackT entry_callback, GroupCallbackT group_callback) const -> void { uint8_t* local_metadata = metadata(); EntryT* local_entries = entries(); ssize_t local_size = alloc_size_; for (ssize_t group_index = 0; group_index < local_size; group_index += GroupSize) { auto g = MetadataGroup::Load(local_metadata, group_index); auto present_matched_range = g.MatchPresent(); if (!present_matched_range) { continue; } for (ssize_t byte_index : present_matched_range) { entry_callback(local_entries[group_index + byte_index]); } group_callback(&local_metadata[group_index]); } } template auto ViewImpl::ComputeMetricsImpl( KeyContextT key_context) const -> Metrics { uint8_t* local_metadata = metadata(); EntryT* local_entries = entries(); ssize_t local_size = alloc_size_; Metrics metrics; // Compute the ones we can directly. metrics.deleted_count = llvm::count( llvm::ArrayRef(local_metadata, local_size), MetadataGroup::Deleted); metrics.storage_bytes = AllocByteSize(local_size); // We want to process present slots specially to collect metrics on their // probing behavior. for (ssize_t group_index = 0; group_index < local_size; group_index += GroupSize) { auto g = MetadataGroup::Load(local_metadata, group_index); auto present_matched_range = g.MatchPresent(); for (ssize_t byte_index : present_matched_range) { ++metrics.key_count; ssize_t index = group_index + byte_index; HashCode hash = key_context.HashKey(local_entries[index].key(), ComputeSeed()); auto [hash_index, tag] = hash.ExtractIndexAndTag<7>(); ProbeSequence s(hash_index, local_size); metrics.probed_key_count += static_cast(s.index() != group_index); // For each probed key, go through the probe sequence to find both the // probe distance and how many comparisons are required. ssize_t distance = 0; ssize_t compares = 0; for (; s.index() != group_index; s.Next()) { auto probe_g = MetadataGroup::Load(local_metadata, s.index()); auto probe_matched_range = probe_g.Match(tag); compares += std::distance(probe_matched_range.begin(), probe_matched_range.end()); distance += 1; } auto probe_g = MetadataGroup::Load(local_metadata, s.index()); auto probe_matched_range = probe_g.Match(tag); CARBON_CHECK(!probe_matched_range.empty()); for (ssize_t match_index : probe_matched_range) { if (match_index >= byte_index) { // Note we only count the compares that will *fail* as part of // probing. The last successful compare isn't interesting, it is // always needed. break; } compares += 1; } metrics.probe_avg_distance += distance; metrics.probe_max_distance = std::max(metrics.probe_max_distance, distance); metrics.probe_avg_compares += compares; metrics.probe_max_compares = std::max(metrics.probe_max_compares, compares); } } if (metrics.key_count > 0) { metrics.probe_avg_compares /= metrics.key_count; metrics.probe_avg_distance /= metrics.key_count; } return metrics; } // TODO: Evaluate whether it is worth forcing this out-of-line given the // reasonable ABI boundary it forms and large volume of code necessary to // implement it. template template auto BaseImpl::InsertImpl( LookupKeyT lookup_key, KeyContextT key_context) -> std::pair { CARBON_DCHECK(alloc_size() > 0); PrefetchStorage(); uint8_t* local_metadata = metadata(); HashCode hash = key_context.HashKey(lookup_key, ComputeSeed()); auto [hash_index, tag] = hash.ExtractIndexAndTag<7>(); // We re-purpose the empty control byte to signal no insert is needed to the // caller. This is guaranteed to not be a control byte we're inserting. // constexpr uint8_t NoInsertNeeded = Group::Empty; ssize_t group_with_deleted_index; MetadataGroup::MatchIndex deleted_match = {}; EntryT* local_entries = entries(); auto return_insert_at_index = [&](ssize_t index) -> std::pair { // We'll need to insert at this index so set the control group byte to the // proper value. local_metadata[index] = tag | MetadataGroup::PresentMask; return {&local_entries[index], true}; }; for (ProbeSequence s(hash_index, alloc_size());; s.Next()) { ssize_t group_index = s.index(); // Load the group's metadata and prefetch the entries for this group. The // prefetch here helps hide key access latency while we're matching the // metadata. auto g = MetadataGroup::Load(local_metadata, group_index); EntryT* group_entries = &local_entries[group_index]; ViewImplT::PrefetchEntryGroup(group_entries); auto control_byte_matched_range = g.Match(tag); if (control_byte_matched_range) { auto byte_it = control_byte_matched_range.begin(); auto byte_end = control_byte_matched_range.end(); do { EntryT* entry = byte_it.index_ptr(group_entries); if (LLVM_LIKELY(key_context.KeyEq(lookup_key, entry->key()))) { return {entry, false}; } ++byte_it; } while (LLVM_UNLIKELY(byte_it != byte_end)); } // Track the first group with a deleted entry that we could insert over. if (!deleted_match) { deleted_match = g.MatchDeleted(); group_with_deleted_index = group_index; } // We failed to find a matching entry in this bucket, so check if there are // no empty slots. In that case, we'll continue probing. auto empty_match = g.MatchEmpty(); if (!empty_match) { continue; } // Ok, we've finished probing without finding anything and need to insert // instead. // If we found a deleted slot, we don't need the probe sequence to insert // so just bail. We want to ensure building up a table is fast so we // de-prioritize this a bit. In practice this doesn't have too much of an // effect. if (LLVM_UNLIKELY(deleted_match)) { return return_insert_at_index(group_with_deleted_index + deleted_match.index()); } // We're going to need to grow by inserting into an empty slot. Check that // we have the budget for that before we compute the exact index of the // empty slot. Without the growth budget we'll have to completely rehash and // so we can just bail here. if (LLVM_UNLIKELY(growth_budget_ == 0)) { return {GrowAndInsert(hash, key_context), true}; } --growth_budget_; CARBON_DCHECK(growth_budget() >= 0, "Growth budget shouldn't have gone negative!"); return return_insert_at_index(group_index + empty_match.index()); } CARBON_FATAL( "We should never finish probing without finding the entry or an empty " "slot."); } template [[clang::noinline]] auto BaseImpl::GrowToAllocSizeImpl( ssize_t target_alloc_size, KeyContextT key_context) -> void { CARBON_CHECK(llvm::isPowerOf2_64(target_alloc_size)); if (target_alloc_size <= alloc_size()) { return; } // If this is the next alloc size, we can used our optimized growth strategy. if (target_alloc_size == ComputeNextAllocSize(alloc_size())) { GrowToNextAllocSize(key_context); return; } // Create locals for the old state of the table. ssize_t old_size = alloc_size(); CARBON_DCHECK(old_size > 0); bool old_small = is_small(); Storage* old_storage = storage(); uint8_t* old_metadata = metadata(); EntryT* old_entries = entries(); // Configure for the new size and allocate the new storage. alloc_size() = target_alloc_size; storage() = Allocate(target_alloc_size); std::memset(metadata(), 0, target_alloc_size); growth_budget_ = GrowthThresholdForAllocSize(target_alloc_size); // Just re-insert all the entries. As we're more than doubling the table size, // we don't bother with fancy optimizations here. Even using `memcpy` for the // entries seems unlikely to be a significant win given how sparse the // insertions will end up being. ssize_t count = 0; for (ssize_t group_index = 0; group_index < old_size; group_index += GroupSize) { auto g = MetadataGroup::Load(old_metadata, group_index); auto present_matched_range = g.MatchPresent(); for (ssize_t byte_index : present_matched_range) { ++count; ssize_t index = group_index + byte_index; HashCode hash = key_context.HashKey(old_entries[index].key(), ComputeSeed()); EntryT* new_entry = InsertIntoEmpty(hash); new_entry->MoveFrom(std::move(old_entries[index])); } } growth_budget_ -= count; if (!old_small) { // Old isn't a small buffer, so we need to deallocate it. Deallocate(old_storage, old_size); } } template auto BaseImpl::GrowForInsertCountImpl( ssize_t count, KeyContextT key_context) -> void { if (count < growth_budget_) { // Already space for the needed growth. return; } // Currently, we don't account for any tombstones marking deleted elements, // and just conservatively ensure the growth will create adequate growth // budget for insertions. We could make this more precise by instead walking // the table and only counting present slots, as once we grow we'll be able to // reclaim all of the deleted slots. But this adds complexity and it isn't // clear this is necessary so we do the simpler conservative thing. ssize_t used_budget = GrowthThresholdForAllocSize(alloc_size()) - growth_budget_; ssize_t budget_needed = used_budget + count; ssize_t space_needed = budget_needed + (budget_needed / 7); ssize_t target_alloc_size = llvm::NextPowerOf2(space_needed); CARBON_CHECK(GrowthThresholdForAllocSize(target_alloc_size) > (budget_needed)); GrowToAllocSizeImpl(target_alloc_size, key_context); } template template auto BaseImpl::EraseImpl( LookupKeyT lookup_key, KeyContextT key_context) -> bool { EntryT* entry = view_impl_.LookupEntry(lookup_key, key_context); if (!entry) { return false; } // If there are empty slots in this group then nothing will probe past this // group looking for an entry so we can simply set this slot to empty as // well. However, if every slot in this group is full, it might be part of // a long probe chain that we can't disrupt. In that case we mark the slot's // metadata as deleted to keep probes continuing past it. // // If we mark the slot as empty, we'll also need to increase the growth // budget. uint8_t* local_metadata = metadata(); EntryT* local_entries = entries(); ssize_t index = entry - local_entries; ssize_t group_index = index & ~GroupMask; auto g = MetadataGroup::Load(local_metadata, group_index); auto empty_matched_range = g.MatchEmpty(); if (empty_matched_range) { local_metadata[index] = MetadataGroup::Empty; ++growth_budget_; } else { local_metadata[index] = MetadataGroup::Deleted; } if constexpr (!EntryT::IsTriviallyDestructible) { entry->Destroy(); } return true; } template auto BaseImpl::ClearImpl() -> void { view_impl_.ForEachEntry( [](EntryT& entry) { if constexpr (!EntryT::IsTriviallyDestructible) { entry.Destroy(); } }, [](uint8_t* metadata_group) { // Clear the group. std::memset(metadata_group, 0, GroupSize); }); growth_budget_ = GrowthThresholdForAllocSize(alloc_size()); } // Allocates the appropriate memory layout for a table of the given // `alloc_size`, with space both for the metadata array and entries. // // The returned pointer *must* be deallocated by calling the below `Deallocate` // function with the same `alloc_size` as used here. template auto BaseImpl::Allocate( ssize_t alloc_size) -> Storage* { return reinterpret_cast(__builtin_operator_new( ViewImplT::AllocByteSize(alloc_size), static_cast(Alignment), std::nothrow_t())); } // Deallocates a table's storage that was allocated with the `Allocate` // function. template auto BaseImpl::Deallocate( Storage* storage, ssize_t alloc_size) -> void { ssize_t allocated_size = ViewImplT::AllocByteSize(alloc_size); // We don't need the size, but make sure it always compiles. static_cast(allocated_size); __builtin_operator_delete(storage, #if __cpp_sized_deallocation allocated_size, #endif static_cast(Alignment)); } // Construct a table using the provided small storage if `small_alloc_size_` is // non-zero. If `small_alloc_size_` is zero, then `small_storage` won't be used // and can be null. Regardless, after this the storage pointer is non-null and // the size is non-zero so that we can directly begin inserting or querying the // table. template auto BaseImpl::Construct( Storage* small_storage) -> void { if (small_alloc_size_ > 0) { alloc_size() = small_alloc_size_; storage() = small_storage; } else { // Directly allocate the initial buffer so that the hashtable is never in // an empty state. alloc_size() = MinAllocatedSize; storage() = Allocate(MinAllocatedSize); } std::memset(metadata(), 0, alloc_size()); growth_budget_ = GrowthThresholdForAllocSize(alloc_size()); } // Destroy the current table, releasing any memory used. template auto BaseImpl::Destroy() -> void { // Check for a moved-from state and don't do anything. Only a moved-from table // has a zero size. if (alloc_size() == 0) { return; } // Destroy all the entries. if constexpr (!EntryT::IsTriviallyDestructible) { view_impl_.ForEachEntry([](EntryT& entry) { entry.Destroy(); }, [](auto...) {}); } // If small, nothing to deallocate. if (is_small()) { return; } // Just deallocate the storage without updating anything when destroying the // object. Deallocate(storage(), alloc_size()); } // Copy all of the slots over from another table that is exactly the same // allocation size. // // This requires the current table to already have storage allocated and set up // but not initialized (or already cleared). It directly overwrites the storage // allocation of the table to match the incoming argument. // // Despite being used in construction, this shouldn't be called for a moved-from // `arg` -- in practice it is better for callers to handle this when setting up // storage. template auto BaseImpl::CopySlotsFrom( const BaseImpl& arg) -> void requires(EntryT::IsCopyable) { CARBON_DCHECK(alloc_size() == arg.alloc_size()); ssize_t local_size = alloc_size(); // Preserve which slot every entry is in, including tombstones in the // metadata, in order to copy into the new table's storage without rehashing // all of the keys. This is especially important as we don't have an easy way // to access the key context needed for rehashing here. uint8_t* local_metadata = metadata(); EntryT* local_entries = entries(); const uint8_t* local_arg_metadata = arg.metadata(); const EntryT* local_arg_entries = arg.entries(); memcpy(local_metadata, local_arg_metadata, local_size); for (ssize_t group_index = 0; group_index < local_size; group_index += GroupSize) { auto g = MetadataGroup::Load(local_arg_metadata, group_index); for (ssize_t byte_index : g.MatchPresent()) { local_entries[group_index + byte_index].CopyFrom( local_arg_entries[group_index + byte_index]); } } } // Move from another table to this one. // // Note that the `small_storage` is *this* table's small storage pointer, // provided from the `TableImpl` to this `BaseImpl` method as an argument. // // Requires the table to have size and growth already set up but otherwise the // the table has not yet been initialized. Notably, storage should either not // yet be constructed or already destroyed. It both sets up the storage and // handles any moving slots needed. // // Note that because this is used in construction it needs to handle a // moved-from `arg`. template auto BaseImpl::MoveFrom( BaseImpl&& arg, Storage* small_storage) -> void { ssize_t local_size = alloc_size(); CARBON_DCHECK(local_size == arg.alloc_size()); // If `arg` is moved-from, skip the rest as the local size is all we need. if (local_size == 0) { return; } if (arg.is_small()) { CARBON_DCHECK(local_size == small_alloc_size_); this->storage() = small_storage; // For small tables, we have to move the entries as we can't move the tables // themselves. We do this preserving their slots and even tombstones to // avoid rehashing. uint8_t* local_metadata = this->metadata(); EntryT* local_entries = this->entries(); uint8_t* local_arg_metadata = arg.metadata(); EntryT* local_arg_entries = arg.entries(); memcpy(local_metadata, local_arg_metadata, local_size); if (EntryT::IsTriviallyRelocatable) { memcpy(local_entries, local_arg_entries, local_size * sizeof(EntryT)); } else { for (ssize_t group_index = 0; group_index < local_size; group_index += GroupSize) { auto g = MetadataGroup::Load(local_arg_metadata, group_index); for (ssize_t byte_index : g.MatchPresent()) { local_entries[group_index + byte_index].MoveFrom( std::move(local_arg_entries[group_index + byte_index])); } } } } else { // Just point to the allocated storage. storage() = arg.storage(); } // Finally, put the incoming table into a moved-from state. arg.alloc_size() = 0; // Replace the pointer with null to ease debugging. arg.storage() = nullptr; } // Optimized routine to insert a key into a table when that key *definitely* // isn't present in the table and the table *definitely* has a viable empty slot // (and growth space) to insert into before any deleted slots. When both of // these are true, typically just after growth, we can dramatically simplify the // insert position search. template auto BaseImpl::InsertIntoEmpty( HashCode hash) -> EntryT* { auto [hash_index, tag] = hash.ExtractIndexAndTag<7>(); uint8_t* local_metadata = metadata(); EntryT* local_entries = entries(); for (ProbeSequence s(hash_index, alloc_size());; s.Next()) { ssize_t group_index = s.index(); auto g = MetadataGroup::Load(local_metadata, group_index); if (auto empty_match = g.MatchEmpty()) { ssize_t index = group_index + empty_match.index(); local_metadata[index] = tag | MetadataGroup::PresentMask; return &local_entries[index]; } // Otherwise we continue probing. } } // Apply our doubling growth strategy and (re-)check invariants around table // size. template auto BaseImpl::ComputeNextAllocSize( ssize_t old_alloc_size) -> ssize_t { CARBON_DCHECK(llvm::isPowerOf2_64(old_alloc_size), "Expected a power of two!"); ssize_t new_alloc_size; bool overflow = __builtin_mul_overflow(old_alloc_size, 2, &new_alloc_size); CARBON_CHECK(!overflow, "Computing the new size overflowed `ssize_t`!"); return new_alloc_size; } // Compute the growth threshold for a given size. template auto BaseImpl::GrowthThresholdForAllocSize(ssize_t alloc_size) -> ssize_t { // We use a 7/8ths load factor to trigger growth. return alloc_size - alloc_size / 8; } // Optimized routine for growing to the next alloc size. // // A particularly common and important-to-optimize path is growing to the next // alloc size, which will always be a doubling of the allocated size. This // allows an important optimization -- we're adding exactly one more high bit to // the hash-computed index for each entry. This in turn means we can classify // every entry in the table into three cases: // // 1) The new high bit is zero, the entry is at the same index in the new // table as the old. // // 2) The new high bit is one, the entry is at the old index plus the old // size. // // 3) The entry's current index doesn't match the initial hash index because // it required some amount of probing to find an empty slot. // // The design of the hash table tries to minimize how many entries fall into // case (3), so we expect the vast majority of entries to be in (1) or (2). This // lets us model growth notionally as copying the hashtable twice into the lower // and higher halves of the new allocation, clearing out the now-empty slots // (from both deleted entries and entries in the other half of the table after // growth), and inserting any probed elements. That model in turn is much more // efficient than re-inserting all of the elements as it avoids the unnecessary // parts of insertion and avoids interleaving random accesses for the probed // elements. But most importantly, for trivially relocatable types it allows us // to use `memcpy` rather than moving the elements individually. template auto BaseImpl::GrowToNextAllocSize( KeyContextT key_context) -> void { // We collect the probed elements in a small vector for re-insertion. It is // tempting to reuse the already allocated storage, but doing so appears to // be a (very slight) performance regression. These are relatively rare and // storing them into the existing storage creates stores to the same regions // of memory we're reading. Moreover, it requires moving both the key and the // value twice, and doing the `memcpy` widening for relocatable types before // the group walk rather than after the group walk. In practice, between the // statistical rareness and using a large small size buffer here on the stack, // we can handle this most efficiently with temporary, additional storage. llvm::SmallVector, 128> probed_indices; // Create locals for the old state of the table. ssize_t old_size = alloc_size(); CARBON_DCHECK(old_size > 0); bool old_small = is_small(); Storage* old_storage = storage(); uint8_t* old_metadata = metadata(); EntryT* old_entries = entries(); #ifndef NDEBUG // Count how many of the old table slots will end up being empty after we grow // the table. This is both the currently empty slots, but also the deleted // slots because we clear them to empty and re-insert everything that had any // probing. ssize_t debug_empty_count = llvm::count(llvm::ArrayRef(old_metadata, old_size), MetadataGroup::Empty); ssize_t debug_deleted_count = llvm::count( llvm::ArrayRef(old_metadata, old_size), MetadataGroup::Deleted); CARBON_DCHECK( debug_empty_count >= (old_size - GrowthThresholdForAllocSize(old_size)), "debug_empty_count: {0}, debug_deleted_count: {1}, size: {2}", debug_empty_count, debug_deleted_count, old_size); #endif // Configure for the new size and allocate the new storage. ssize_t new_size = ComputeNextAllocSize(old_size); alloc_size() = new_size; storage() = Allocate(new_size); growth_budget_ = GrowthThresholdForAllocSize(new_size); // Now extract the new components of the table. uint8_t* new_metadata = metadata(); EntryT* new_entries = entries(); // Walk the metadata groups, clearing deleted to empty, duplicating the // metadata for the low and high halves, and updating it based on where each // entry will go in the new table. The updated metadata group is written to // the new table, and for non-trivially relocatable entry types, the entry is // also moved to its new location. ssize_t count = 0; for (ssize_t group_index = 0; group_index < old_size; group_index += GroupSize) { auto low_g = MetadataGroup::Load(old_metadata, group_index); // Make sure to match present elements first to enable pipelining with // clearing. auto present_matched_range = low_g.MatchPresent(); low_g.ClearDeleted(); MetadataGroup high_g; if constexpr (MetadataGroup::FastByteClear) { // When we have a fast byte clear, we can update the metadata for the // growth in-register and store at the end. high_g = low_g; } else { // If we don't have a fast byte clear, we can store the metadata group // eagerly here and overwrite bytes with a byte store below instead of // clearing the byte in-register. low_g.Store(new_metadata, group_index); low_g.Store(new_metadata, group_index | old_size); } for (ssize_t byte_index : present_matched_range) { ++count; ssize_t old_index = group_index + byte_index; if constexpr (!MetadataGroup::FastByteClear) { CARBON_DCHECK(new_metadata[old_index] == old_metadata[old_index]); CARBON_DCHECK(new_metadata[old_index | old_size] == old_metadata[old_index]); } HashCode hash = key_context.HashKey(old_entries[old_index].key(), ComputeSeed()); ssize_t old_hash_index = hash.ExtractIndexAndTag<7>().first & ComputeProbeMaskFromSize(old_size); if (LLVM_UNLIKELY(old_hash_index != group_index)) { probed_indices.push_back({old_index, hash}); if constexpr (MetadataGroup::FastByteClear) { low_g.ClearByte(byte_index); high_g.ClearByte(byte_index); } else { new_metadata[old_index] = MetadataGroup::Empty; new_metadata[old_index | old_size] = MetadataGroup::Empty; } continue; } ssize_t new_index = hash.ExtractIndexAndTag<7>().first & ComputeProbeMaskFromSize(new_size); CARBON_DCHECK(new_index == old_hash_index || new_index == (old_hash_index | old_size)); // Toggle the newly added bit of the index to get to the other possible // target index. if constexpr (MetadataGroup::FastByteClear) { (new_index == old_hash_index ? high_g : low_g).ClearByte(byte_index); new_index += byte_index; } else { new_index += byte_index; new_metadata[new_index ^ old_size] = MetadataGroup::Empty; } // If we need to explicitly move (and destroy) the key or value, do so // here where we already know its target. if constexpr (!EntryT::IsTriviallyRelocatable) { new_entries[new_index].MoveFrom(std::move(old_entries[old_index])); } } if constexpr (MetadataGroup::FastByteClear) { low_g.Store(new_metadata, group_index); high_g.Store(new_metadata, (group_index | old_size)); } } CARBON_DCHECK((count - static_cast(probed_indices.size())) == (new_size - llvm::count(llvm::ArrayRef(new_metadata, new_size), MetadataGroup::Empty))); #ifndef NDEBUG CARBON_DCHECK((debug_empty_count + debug_deleted_count) == (old_size - count)); CARBON_DCHECK(llvm::count(llvm::ArrayRef(new_metadata, new_size), MetadataGroup::Empty) == debug_empty_count + debug_deleted_count + static_cast(probed_indices.size()) + old_size); #endif // If the keys or values are trivially relocatable, we do a bulk memcpy of // them into place. This will copy them into both possible locations, which is // fine. One will be empty and clobbered if reused or ignored. The other will // be the one used. This might seem like it needs it to be valid for us to // create two copies, but it doesn't. This produces the exact same storage as // copying the storage into the wrong location first, and then again into the // correct location. Only one is live and only one is destroyed. if constexpr (EntryT::IsTriviallyRelocatable) { memcpy(new_entries, old_entries, old_size * sizeof(EntryT)); memcpy(new_entries + old_size, old_entries, old_size * sizeof(EntryT)); } // We then need to do a normal insertion for anything that was probed before // growth, but we know we'll find an empty slot, so leverage that. for (auto [old_index, hash] : probed_indices) { EntryT* new_entry = InsertIntoEmpty(hash); new_entry->MoveFrom(std::move(old_entries[old_index])); } CARBON_DCHECK(count == (new_size - llvm::count(llvm::ArrayRef(new_metadata, new_size), MetadataGroup::Empty))); growth_budget_ -= count; CARBON_DCHECK(growth_budget_ == (GrowthThresholdForAllocSize(new_size) - (new_size - llvm::count(llvm::ArrayRef(new_metadata, new_size), MetadataGroup::Empty)))); CARBON_DCHECK(growth_budget_ > 0 && "Must still have a growth budget after rehash!"); if (!old_small) { // Old isn't a small buffer, so we need to deallocate it. Deallocate(old_storage, old_size); } } // Grow the hashtable to create space and then insert into it. Returns the // selected insertion entry. Never returns null. In addition to growing and // selecting the insertion entry, this routine updates the metadata array so // that this function can be directly called and the result returned from // `InsertImpl`. template [[clang::noinline]] auto BaseImpl::GrowAndInsert( HashCode hash, KeyContextT key_context) -> EntryT* { GrowToNextAllocSize(key_context); // And insert the lookup_key into an index in the newly grown map and return // that index for use. --growth_budget_; return InsertIntoEmpty(hash); } template TableImpl::TableImpl(const TableImpl& arg) requires(BaseT::EntryT::IsCopyable) : BaseT(arg.alloc_size(), arg.growth_budget_, SmallSize) { // Check for completely broken objects. These invariants should be true even // in a moved-from state. CARBON_DCHECK(arg.alloc_size() == 0 || !arg.is_small() || arg.alloc_size() == SmallSize); CARBON_DCHECK(arg.small_alloc_size_ == SmallSize); CARBON_DCHECK(this->small_alloc_size_ == SmallSize); if (this->alloc_size() != 0) { SetUpStorage(); this->CopySlotsFrom(arg); } } template auto TableImpl::operator=(const TableImpl& arg) -> TableImpl& requires(BaseT::EntryT::IsCopyable) { // Check for completely broken objects. These invariants should be true even // in a moved-from state. CARBON_DCHECK(arg.alloc_size() == 0 || !arg.is_small() || arg.alloc_size() == SmallSize); CARBON_DCHECK(arg.small_alloc_size_ == SmallSize); CARBON_DCHECK(this->small_alloc_size_ == SmallSize); // We have to end up with an allocation size exactly equivalent to the // incoming argument to avoid re-hashing every entry in the table, which isn't // possible without key context. if (arg.alloc_size() == this->alloc_size()) { // No effective way for self-assignment to fall out of an efficient // implementation so detect and bypass here. Similarly, if both are in a // moved-from state, there is nothing to do. if (&arg == this || this->alloc_size() == 0) { return *this; } CARBON_DCHECK(arg.storage() != this->storage()); if constexpr (!EntryT::IsTriviallyDestructible) { this->view_impl_.ForEachEntry([](EntryT& entry) { entry.Destroy(); }, [](auto...) {}); } } else { // The sizes don't match so destroy everything and re-setup the table // storage. this->Destroy(); this->alloc_size() = arg.alloc_size(); // If `arg` is moved-from, we've clear out our elements and put ourselves // into a moved-from state. We're done. if (this->alloc_size() == 0) { return *this; } SetUpStorage(); } this->growth_budget_ = arg.growth_budget_; this->CopySlotsFrom(arg); return *this; } // Puts the incoming table into a moved-from state that can be destroyed or // re-initialized but must not be used otherwise. template TableImpl::TableImpl(TableImpl&& arg) noexcept : BaseT(arg.alloc_size(), arg.growth_budget_, SmallSize) { // Check for completely broken objects. These invariants should be true even // in a moved-from state. CARBON_DCHECK(arg.alloc_size() == 0 || !arg.is_small() || arg.alloc_size() == SmallSize); CARBON_DCHECK(arg.small_alloc_size_ == SmallSize); CARBON_DCHECK(this->small_alloc_size_ == SmallSize); this->MoveFrom(std::move(arg), small_storage()); } template auto TableImpl::operator=(TableImpl&& arg) noexcept -> TableImpl& { // Check for completely broken objects. These invariants should be true even // in a moved-from state. CARBON_DCHECK(arg.alloc_size() == 0 || !arg.is_small() || arg.alloc_size() == SmallSize); CARBON_DCHECK(arg.small_alloc_size_ == SmallSize); CARBON_DCHECK(this->small_alloc_size_ == SmallSize); // Destroy and deallocate our table. this->Destroy(); // Defend against self-move by zeroing the size here before we start moving // out of `arg`. this->alloc_size() = 0; // Setup to match argument and then finish the move. this->alloc_size() = arg.alloc_size(); this->growth_budget_ = arg.growth_budget_; this->MoveFrom(std::move(arg), small_storage()); return *this; } template TableImpl::~TableImpl() { this->Destroy(); } // Reset a table to its original state, including releasing any allocated // memory. template auto TableImpl::ResetImpl() -> void { this->Destroy(); // Re-initialize the whole thing. CARBON_DCHECK(this->small_alloc_size() == SmallSize); this->Construct(small_storage()); } template auto TableImpl::small_storage() const -> Storage* { if constexpr (SmallSize > 0) { // Do a bunch of validation of the small size to establish our invariants // when we know we have a non-zero small size. static_assert(llvm::isPowerOf2_64(SmallSize), "SmallSize must be a power of two for a hashed buffer!"); static_assert( SmallSize >= MaxGroupSize, "We require all small sizes to multiples of the largest group " "size supported to ensure it can be used portably. "); static_assert( (SmallSize % MaxGroupSize) == 0, "Small size must be a multiple of the max group size supported " "so that we can allocate a whole number of groups."); // Implied by the max asserts above. static_assert(SmallSize >= GroupSize); static_assert((SmallSize % GroupSize) == 0); static_assert(SmallSize >= alignof(StorageEntry), "Requested a small size that would require padding between " "metadata bytes and correctly aligned key and value types. " "Either a larger small size or a zero small size and heap " "allocation are required for this key and value type."); static_assert(offsetof(SmallStorage, entries) == SmallSize, "Offset to entries in small size storage doesn't match " "computed offset!"); return &small_storage_; } else { static_assert( sizeof(TableImpl) == sizeof(BaseT), "Empty small storage caused a size difference and wasted space!"); return nullptr; } } // Helper to set up the storage of a table when a specific size has already been // set up. If possible, uses any small storage, otherwise allocates. template auto TableImpl::SetUpStorage() -> void { CARBON_DCHECK(this->small_alloc_size() == SmallSize); ssize_t local_size = this->alloc_size(); CARBON_DCHECK(local_size != 0); if (local_size == SmallSize) { this->storage() = small_storage(); } else { this->storage() = BaseT::Allocate(local_size); } } } // namespace Carbon::RawHashtable #endif // CARBON_COMMON_RAW_HASHTABLE_H_ ================================================ FILE: common/raw_hashtable_benchmark_helpers.cpp ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #include "common/raw_hashtable_benchmark_helpers.h" #include #include #include #include #include namespace Carbon::RawHashtable { // A local shuffle implementation built on Abseil to improve performance in // debug builds. template static auto Shuffle(llvm::MutableArrayRef data, absl::BitGen& gen) { for (ssize_t i : llvm::seq(0, data.size() - 1)) { ssize_t j = absl::Uniform(gen, 0, data.size() - i); if (j != 0) { std::swap(data[i], data[i + j]); } } } constexpr ssize_t NumChars = 64; static_assert(llvm::isPowerOf2_64(NumChars)); // For benchmarking, we use short strings in a fixed distribution with common // characters. Real-world strings aren't uniform across ASCII or Unicode, etc. // And for *micro*-benchmarking we want to focus on the map overhead with short, // fast keys. static auto MakeChars() -> llvm::SmallVector { llvm::SmallVector characters; characters.reserve(NumChars); // Start with `-` and `_`, and then add `a` - `z`, `A` - `Z`, and `0` - `9`. characters.push_back('-'); characters.push_back('_'); llvm::append_range(characters, llvm::seq_inclusive('a', 'z')); llvm::append_range(characters, llvm::seq_inclusive('A', 'Z')); llvm::append_range(characters, llvm::seq_inclusive('0', '9')); CARBON_CHECK(characters.size() == NumChars, "Expected exactly {0} characters, got {1} instead!", NumChars, characters.size()); return characters; } constexpr ssize_t NumFourCharStrs = NumChars * NumChars * NumChars * NumChars; static_assert(llvm::isPowerOf2_64(NumFourCharStrs)); // Compute every 4-character string in a shuffled array. This is a little memory // intense -- 64 MiB -- but ends up being much cheaper by letting us reliably // select a unique 4-character sequence to avoid collisions. static auto MakeFourCharStrs(llvm::ArrayRef characters, absl::BitGen& gen) -> llvm::SmallVector> { constexpr ssize_t NumCharsMask = NumChars - 1; constexpr ssize_t NumCharsShift = llvm::ConstantLog2(); llvm::SmallVector> four_char_strs( llvm::map_range(llvm::seq(NumFourCharStrs), [&](auto i) { std::array str; str[0] = characters[i & NumCharsMask]; i >>= NumCharsShift; str[1] = characters[i & NumCharsMask]; i >>= NumCharsShift; str[2] = characters[i & NumCharsMask]; i >>= NumCharsShift; CARBON_CHECK((i & ~NumCharsMask) == 0); str[3] = characters[i]; return str; })); Shuffle>(four_char_strs, gen); return four_char_strs; } constexpr ssize_t NumRandomChars = static_cast(64) * 1024; // Create a pool of random characters to sample from rather than computing this // for every string which is very slow in debug builds. We also pad this pool // with the max length so we can pull the full length from the end to simplify // the logic when wrapping around the pool. static auto MakeRandomChars(llvm::ArrayRef characters, int max_length, absl::BitGen& gen) -> llvm::SmallVector { return llvm::SmallVector( llvm::map_range(llvm::seq(NumRandomChars + max_length), [&](auto /*i*/) { return characters[absl::Uniform(gen, 0, NumChars)]; })); } // Make a small vector of pointers into a single allocation of raw strings. The // allocated memory is expected to leak and must be transitively referenced by a // global. Each string has `length` size (which must be >= 4), and there are // `key_count` keys in the result. Each key is filled from the `random_chars` // until the last 4 characters. The last four characters of each string will be // taken sequentially from `four_char_strs` from some random start position to // ensure no duplicate keys are produced. static auto MakeRawStrKeys(ssize_t length, ssize_t key_count, llvm::ArrayRef> four_char_strs, llvm::ArrayRef random_chars, absl::BitGen& gen) -> llvm::SmallVector { llvm::SmallVector raw_keys; CARBON_CHECK(length >= 4); ssize_t prefix_length = length - 4; // Select a random start for indexing our four character strings. ssize_t four_char_index = absl::Uniform(gen, 0, NumFourCharStrs); // Select a random start for the prefix random characters. ssize_t random_chars_index = absl::Uniform(gen, 0, NumRandomChars); // Do a single memory allocation for all the keys of this length to // avoid an excessive number of small and fragmented allocations. This // memory is intentionally leaked as the keys are global and will // themselves will point into it. char* key_text = new char[key_count * length]; // Reserve all the key space since we know how many we'll need. raw_keys.reserve(key_count); for ([[gnu::unused]] ssize_t i : llvm::seq(0, key_count)) { memcpy(key_text, random_chars.data() + random_chars_index, prefix_length); random_chars_index += prefix_length; random_chars_index &= NumRandomChars - 1; // Set the last four characters with this entry in the shuffled // sequence. memcpy(key_text + prefix_length, four_char_strs[four_char_index].data(), 4); // Step through the shuffled sequence. We start at a random position, // so we need to wrap around the end. ++four_char_index; four_char_index &= NumFourCharStrs - 1; // And finally save the start pointer as one of our raw keys. raw_keys.push_back(key_text); key_text += length; } return raw_keys; } // Build up a large collection of random and unique string keys. This is // actually a relatively expensive operation due to needing to build all the // random string text. As a consequence, the initializer of this global is // somewhat performance tuned to ensure benchmarks don't take an excessive // amount of time to run or use an excessive amount of memory. static absl::NoDestructor> raw_str_keys{[] { llvm::SmallVector keys(MaxNumKeys); absl::BitGen gen; std::array length_buckets = { 4, 4, 4, 4, 5, 5, 5, 5, 7, 7, 10, 10, 15, 25, 40, 80, }; static_assert((MaxNumKeys % length_buckets.size()) == 0); CARBON_CHECK(llvm::is_sorted(length_buckets)); // For each distinct length bucket, we build a vector of raw keys. std::forward_list> raw_keys_storage; // And a parallel array to the length buckets with the raw keys of that // length. std::array*, length_buckets.size()> raw_keys_buckets; llvm::SmallVector characters = MakeChars(); llvm::SmallVector> four_char_strs = MakeFourCharStrs(characters, gen); llvm::SmallVector random_chars = MakeRandomChars(characters, /*max_length=*/length_buckets.back(), gen); ssize_t prev_length = -1; for (auto [length_index, length] : llvm::enumerate(length_buckets)) { // We can detect repetitions in length as they are sorted. if (length == prev_length) { raw_keys_buckets[length_index] = raw_keys_buckets[length_index - 1]; continue; } prev_length = length; // We want to compute all the keys of this length that we'll need. ssize_t key_count = (MaxNumKeys / length_buckets.size()) * llvm::count(length_buckets, length); raw_keys_buckets[length_index] = &raw_keys_storage.emplace_front( MakeRawStrKeys(length, key_count, four_char_strs, random_chars, gen)); } // Now build the actual key array from our intermediate storage by // round-robin extracting from the length buckets. for (auto [index, key] : llvm::enumerate(keys)) { ssize_t bucket = index % length_buckets.size(); ssize_t length = length_buckets[bucket]; // We pop a raw key from the list of them associated with this bucket. const char* raw_key = raw_keys_buckets[bucket]->pop_back_val(); // And build our key from that. key = llvm::StringRef(raw_key, length); } // Check that in fact we popped every raw key into our main keys. for (const auto& raw_keys : raw_keys_storage) { CARBON_CHECK(raw_keys.empty()); } return keys; }()}; static absl::NoDestructor> raw_ptr_keys(llvm::map_range( llvm::seq(MaxNumKeys), [](int index) { return new int{index}; })); static absl::NoDestructor> raw_int_keys(llvm::map_range( llvm::seq(MaxNumKeys), [](int index) { return index + 1; })); template static absl::NoDestructor>> raw_low_zero_bit_int_keys( llvm::map_range(llvm::seq(MaxNumKeys), [](int index) { return LowZeroBitInt(index + 1); })); namespace { // Allow generically dispatching over the specific key types we build and // support. template auto GetRawKeys() -> llvm::ArrayRef { if constexpr (std::is_same_v) { return *raw_str_keys; } else if constexpr (std::is_pointer_v) { return *raw_ptr_keys; } else if constexpr (std::is_same_v>) { return *raw_low_zero_bit_int_keys<12>; } else if constexpr (std::is_same_v>) { return *raw_low_zero_bit_int_keys<24>; } else if constexpr (std::is_same_v>) { return *raw_low_zero_bit_int_keys<32>; } else { return *raw_int_keys; } } template static absl::NoDestructor< std::map, llvm::SmallVector>> lookup_keys_storage; // Given a particular table keys size and lookup keys size, provide an array ref // to a shuffled set of lookup keys. // // Because different table sizes pull from different sub-ranges of our raw keys, // we need to compute a distinct set of random keys in the table to use for // lookups depending on the table size. And we also want to have an even // distribution of key *sizes* throughout the lookup keys, and so we can't // compute a single lookup keys array of the maximum size. Instead we need to // compute a distinct special set of lookup keys for each pair of table and // lookup size, and then shuffle that specific set into a random sequence that // is returned. This function memoizes this sequence for each pair of sizes. template auto GetShuffledLookupKeys(ssize_t table_keys_size, ssize_t lookup_keys_size) -> llvm::ArrayRef { // The raw keys aren't shuffled and round-robin through the sizes. We want to // keep the total size of lookup keys used exactly the same across runs. So // for a given size we always take the leading sequence from the raw keys for // that size, duplicating as needed to get the desired lookup sequence size, // and then shuffle the keys in that sequence to end up with a random sequence // of keys. We store each of these shuffled sequences in a map to avoid // repeatedly computing them. llvm::SmallVector& lookup_keys = (*lookup_keys_storage)[{table_keys_size, lookup_keys_size}]; if (lookup_keys.empty()) { auto raw_keys = GetRawKeys(); llvm::append_range( lookup_keys, llvm::map_range(llvm::seq(lookup_keys_size), [&](int index) { return raw_keys[index % table_keys_size]; })); absl::BitGen gen; Shuffle(lookup_keys, gen); } CARBON_CHECK(static_cast(lookup_keys.size()) == lookup_keys_size); return lookup_keys; } } // namespace template auto GetKeysAndMissKeys(ssize_t table_keys_size) -> std::pair, llvm::ArrayRef> { CARBON_CHECK(table_keys_size <= MaxNumKeys); // The raw keys aren't shuffled and round-robin through the sizes. Take the // tail of this sequence and shuffle it to form a random set of miss keys with // a consistent total size. static absl::NoDestructor> miss_keys{[] { llvm::SmallVector keys(GetRawKeys().take_back(NumOtherKeys)); CARBON_CHECK(keys.size() == NumOtherKeys); absl::BitGen gen; Shuffle(keys, gen); return keys; }()}; return {GetRawKeys().slice(0, table_keys_size), *miss_keys}; } template auto GetKeysAndMissKeys(ssize_t size) -> std::pair, llvm::ArrayRef>; template auto GetKeysAndMissKeys(ssize_t size) -> std::pair, llvm::ArrayRef>; template auto GetKeysAndMissKeys(ssize_t size) -> std::pair, llvm::ArrayRef>; template auto GetKeysAndMissKeys>(ssize_t size) -> std::pair>, llvm::ArrayRef>>; template auto GetKeysAndMissKeys>(ssize_t size) -> std::pair>, llvm::ArrayRef>>; template auto GetKeysAndMissKeys>(ssize_t size) -> std::pair>, llvm::ArrayRef>>; template auto GetKeysAndHitKeys(ssize_t table_keys_size, ssize_t lookup_keys_size) -> std::pair, llvm::ArrayRef> { CARBON_CHECK(table_keys_size <= MaxNumKeys); CARBON_CHECK(lookup_keys_size <= MaxNumKeys); return {GetRawKeys().slice(0, table_keys_size), GetShuffledLookupKeys(table_keys_size, lookup_keys_size)}; } template auto GetKeysAndHitKeys(ssize_t size, ssize_t lookup_keys_size) -> std::pair, llvm::ArrayRef>; template auto GetKeysAndHitKeys(ssize_t size, ssize_t lookup_keys_size) -> std::pair, llvm::ArrayRef>; template auto GetKeysAndHitKeys(ssize_t size, ssize_t lookup_keys_size) -> std::pair, llvm::ArrayRef>; template auto GetKeysAndHitKeys>(ssize_t size, ssize_t lookup_keys_size) -> std::pair>, llvm::ArrayRef>>; template auto GetKeysAndHitKeys>(ssize_t size, ssize_t lookup_keys_size) -> std::pair>, llvm::ArrayRef>>; template auto GetKeysAndHitKeys>(ssize_t size, ssize_t lookup_keys_size) -> std::pair>, llvm::ArrayRef>>; template auto DumpHashStatistics(llvm::ArrayRef keys) -> void { if (keys.size() < GroupSize) { return; } // The hash table load factor is 7/8ths, so we want to add 1/7th of our // current size, subtract one, and pick the next power of two to get the power // of two where 7/8ths is greater than or equal to the incoming key size. ssize_t expected_size = llvm::NextPowerOf2(keys.size() + (keys.size() / 7) - 1); constexpr int GroupShift = llvm::ConstantLog2(); size_t mask = ComputeProbeMaskFromSize(expected_size); uint64_t salt = ComputeSeed(); auto get_hash_index = [mask, salt](auto x) -> ssize_t { auto [hash_index, _] = HashValue(x, salt).template ExtractIndexAndTag<7>(); return (hash_index & mask) >> GroupShift; }; std::vector> grouped_key_indices(expected_size >> GroupShift); for (auto [i, k] : llvm::enumerate(keys)) { ssize_t hash_index = get_hash_index(k); CARBON_CHECK(hash_index < (expected_size >> GroupShift), "{0}", hash_index); grouped_key_indices[hash_index].push_back(i); } ssize_t max_group_index = llvm::max_element(grouped_key_indices, [](const auto& lhs, const auto& rhs) { return lhs.size() < rhs.size(); }) - grouped_key_indices.begin(); // If the max number of collisions on the index is less than or equal to the // group size, there shouldn't be any necessary probing (outside of deletion) // and so this isn't interesting, skip printing. if (grouped_key_indices[max_group_index].size() <= GroupSize) { return; } llvm::errs() << "keys: " << keys.size() << " groups: " << grouped_key_indices.size() << "\n" << "max group index: " << llvm::formatv("{0x8}", max_group_index) << " collisions: " << grouped_key_indices[max_group_index].size() << "\n"; for (auto i : llvm::ArrayRef(grouped_key_indices[max_group_index]) .take_front(2 * GroupSize)) { auto k = keys[i]; auto hash = static_cast(HashValue(k, salt)); llvm::errs() << " key: " << k << " salt: " << llvm::formatv("{0:x16}", salt) << " hash: " << llvm::formatv("{0:x16}", hash) << "\n"; } } template auto DumpHashStatistics(llvm::ArrayRef keys) -> void; template auto DumpHashStatistics(llvm::ArrayRef> keys) -> void; template auto DumpHashStatistics(llvm::ArrayRef> keys) -> void; template auto DumpHashStatistics(llvm::ArrayRef> keys) -> void; template auto DumpHashStatistics(llvm::ArrayRef keys) -> void; template auto DumpHashStatistics(llvm::ArrayRef keys) -> void; } // namespace Carbon::RawHashtable ================================================ FILE: common/raw_hashtable_benchmark_helpers.h ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #ifndef CARBON_COMMON_RAW_HASHTABLE_BENCHMARK_HELPERS_H_ #define CARBON_COMMON_RAW_HASHTABLE_BENCHMARK_HELPERS_H_ #include #include #include #include #include #include #include #include "absl/base/no_destructor.h" #include "absl/random/random.h" #include "common/check.h" #include "common/hashing.h" #include "common/raw_hashtable.h" #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/Sequence.h" #include "llvm/ADT/SmallVector.h" #include "llvm/ADT/StringRef.h" namespace Carbon::RawHashtable { // We want to support benchmarking with 16M keys plus up to 256 "other" keys // (for misses). The large number of keys helps check for performance hiccups // with especially large tables and when missing all levels of cache. inline constexpr ssize_t NumOtherKeys = 1 << 8; inline constexpr ssize_t MaxNumKeys = (1 << 24) + NumOtherKeys; // Get an array of main keys with the given `size`, which must be less than // 2^24. Also get a miss keys array of `NumOtherKeys` which has no collisions // with the main keys. // // For a given size, this will return the same arrays. This uses unsynchronized // global state, and so is thread hostile and must not be called before main. template auto GetKeysAndMissKeys(ssize_t table_keys_size) -> std::pair, llvm::ArrayRef>; // Get an array of main keys with the given `size`, which must be less than // 2^24. Also get a hit keys array of `lookup_keys_size` all of which will occur // in the may keys array. If the lookup size is larger than the main size, the // lookup sequence will contain duplicates. // // For a given size, this will return the same arrays. This uses unsynchronized // global state, and so is thread hostile and must not be called before main. template auto GetKeysAndHitKeys(ssize_t table_keys_size, ssize_t lookup_keys_size) -> std::pair, llvm::ArrayRef>; // Dump statistics about hashing the given keys. template auto DumpHashStatistics(llvm::ArrayRef keys) -> void; // A type that works like an `int` but shifting in the specified number of low // zero bits. This is only intended for testing hash tables with especially // difficult to hash integer values, it isn't meant to be used otherwise. template struct LowZeroBitInt { int64_t shifted_value = 0; explicit constexpr LowZeroBitInt() = default; explicit constexpr LowZeroBitInt(int64_t value) : shifted_value(value << LowZeroBits) {} friend auto operator<<(llvm::raw_ostream& out, const LowZeroBitInt& value) -> llvm::raw_ostream& { return out << value.shifted_value; } constexpr auto operator==(const LowZeroBitInt& rhs) const -> bool = default; constexpr auto operator<=>(const LowZeroBitInt& rhs) const -> std::strong_ordering = default; friend auto CarbonHashValue(const LowZeroBitInt& value, uint64_t seed) -> HashCode { return HashValue(value.shifted_value, seed); } template friend auto AbslHashValue(H h, const LowZeroBitInt& value) -> H { return H::combine(std::move(h), value.shifted_value); } friend auto hash_value(const LowZeroBitInt& value) -> size_t { boost::hash hasher; return hasher(value.shifted_value); } }; // Convert values used in hashtable benchmarking to a bool. This is used to form // dependencies between values stored in the hashtable between benchmark // iterations. template auto ValueToBool(T value) -> bool { if constexpr (std::is_same_v) { return value.size() > 0; } else if constexpr (std::is_pointer_v) { return value != nullptr; } else { // We want our keys to include `0` for integers, so use the largest value. return value != std::numeric_limits::max(); } } inline auto SizeArgs(benchmark::Benchmark* b) -> void { // Benchmarks for "miss" operations only have one parameter -- the size of the // table. These benchmarks use a fixed `NumOtherKeys` set of extra keys for // each miss operation. b->DenseRange(1, 4, 1); b->Arg(8); b->Arg(16); b->Arg(32); // For sizes >= 64 we first use the power of two which will have a low load // factor, and then target exactly at our max load factor. auto large_sizes = {64, 1 << 8, 1 << 12, 1 << 16, 1 << 20, 1 << 24}; for (auto s : large_sizes) { b->Arg(s); } for (auto s : large_sizes) { b->Arg(s - (s / 8)); } } inline auto HitArgs(benchmark::Benchmark* b) -> void { // There are two parameters for benchmarks of "hit" operations. The first is // the size of the hashtable itself. The second is the size of a buffer of // random keys actually in the hashtable to use for the operations. // // For small sizes, we use a fixed `NumOtherKeys` lookup key count. This is // enough to avoid patterns of queries training the branch predictor just from // the keys themselves, while small enough to avoid significant L1 cache // pressure. b->ArgsProduct({benchmark::CreateDenseRange(1, 4, 1), {NumOtherKeys}}); b->Args({8, NumOtherKeys}); b->Args({16, NumOtherKeys}); b->Args({32, NumOtherKeys}); // For sizes >= 64 we first use the power of two which will have a low load // factor, and then target exactly at our max load factor. Start the sizes // list off with the powers of two, and the append a version of each power of // two adjusted down to the load factor. We'll then build the benchmarks from // these below. std::vector large_sizes = {64, 1 << 8, 1 << 12, 1 << 16, 1 << 20, 1 << 24}; for (auto i : llvm::seq(0, large_sizes.size())) { ssize_t s = large_sizes[i]; large_sizes.push_back(s - (s / 8)); } for (auto s : large_sizes) { b->Args({s, NumOtherKeys}); // Once the sizes are more than 4x the `NumOtherKeys` minimum lookup buffer // size, also include 25% and 50% lookup buffer sizes which will // increasingly exhaust the ability to keep matching entries in the cache. if (s >= NumOtherKeys) { b->Args({s, s / 4}); b->Args({s, s / 2}); } } } // Provide some Dense{Map,Set}Info viable implementations for the key types // using Carbon's hashing framework. These let us benchmark the data structure // alone rather than the combination of data structure and hashing routine. // // We only provide these for benchmarking -- they are *not* necessarily suitable // for broader use. The Carbon hashing infrastructure has only been evaluated in // the context of its specific hashtable design. template struct CarbonHashDI; template <> struct CarbonHashDI { static auto getEmptyKey() -> int { return -1; } static auto getTombstoneKey() -> int { return -2; } static auto getHashValue(const int val) -> unsigned { return static_cast(HashValue(val)); } static auto isEqual(const int lhs, const int rhs) -> bool { return lhs == rhs; } }; template struct CarbonHashDI> { using IntT = LowZeroBitInt; static auto getEmptyKey() -> IntT { return IntT(-1); } static auto getTombstoneKey() -> IntT { return IntT(-2); } static auto getHashValue(const IntT val) -> unsigned { return static_cast(HashValue(val)); } static auto isEqual(const IntT lhs, const IntT rhs) -> bool { return lhs == rhs; } }; template struct CarbonHashDI { static constexpr uintptr_t Log2MaxAlign = 12; static auto getEmptyKey() -> T* { auto val = static_cast(-1); val <<= Log2MaxAlign; // NOLINTNEXTLINE(performance-no-int-to-ptr): This is required by the API. return reinterpret_cast(val); } static auto getTombstoneKey() -> T* { auto val = static_cast(-2); val <<= Log2MaxAlign; // NOLINTNEXTLINE(performance-no-int-to-ptr): This is required by the API. return reinterpret_cast(val); } static auto getHashValue(const T* ptr_val) -> unsigned { return static_cast(HashValue(ptr_val)); } static auto isEqual(const T* lhs, const T* rhs) -> bool { return lhs == rhs; } }; template <> struct CarbonHashDI { static auto getEmptyKey() -> llvm::StringRef { return llvm::StringRef( // NOLINTNEXTLINE(performance-no-int-to-ptr): Required by the API. reinterpret_cast(~static_cast(0)), 0); } static auto getTombstoneKey() -> llvm::StringRef { return llvm::StringRef( // NOLINTNEXTLINE(performance-no-int-to-ptr): Required by the API. reinterpret_cast(~static_cast(1)), 0); } static auto getHashValue(llvm::StringRef val) -> unsigned { return static_cast(HashValue(val)); } static auto isEqual(llvm::StringRef lhs, llvm::StringRef rhs) -> bool { if (rhs.data() == getEmptyKey().data()) { return lhs.data() == getEmptyKey().data(); } if (rhs.data() == getTombstoneKey().data()) { return lhs.data() == getTombstoneKey().data(); } return lhs == rhs; } }; template auto ReportTableMetrics(const TableT& table, benchmark::State& state) -> void { // While this count is "iteration invariant" (it should be exactly the same // for every iteration as the set of keys is the same), we don't use that // because it will scale this by the number of iterations. We want to // display the metrics for this benchmark *parameter*, not what resulted // from the number of iterations. That means we use the normal counter API // without flags. auto metrics = table.ComputeMetrics(); state.counters["P-compares"] = metrics.probe_avg_compares; state.counters["P-distance"] = metrics.probe_avg_distance; state.counters["P-fraction"] = static_cast(metrics.probed_key_count) / metrics.key_count; state.counters["Pmax-distance"] = metrics.probe_max_distance; state.counters["Pmax-compares"] = metrics.probe_max_compares; state.counters["Probed"] = metrics.probed_key_count; state.counters["Storage"] = metrics.storage_bytes; // Also compute how 'efficient' the storage is, 1.0 being zero bytes outside // of key and value. ssize_t element_size; if constexpr (requires { TableT::ValueT; }) { element_size = sizeof(typename TableT::KeyT) + sizeof(typename TableT::ValueT); } else { element_size = sizeof(typename TableT::KeyT); } state.counters["Storage eff"] = static_cast(metrics.key_count * element_size) / metrics.storage_bytes; } } // namespace Carbon::RawHashtable namespace llvm { // Enable LLVM to hash our special stress testing integer type. template struct DenseMapInfo> { using IntT = Carbon::RawHashtable::LowZeroBitInt; static auto getEmptyKey() -> IntT { return IntT(-1); } static auto getTombstoneKey() -> IntT { return IntT(-2); } static auto getHashValue(const IntT val) -> unsigned { return DenseMapInfo::getHashValue(val.shifted_value); } static auto isEqual(const IntT lhs, const IntT rhs) -> bool { return lhs == rhs; } }; } // namespace llvm #endif // CARBON_COMMON_RAW_HASHTABLE_BENCHMARK_HELPERS_H_ ================================================ FILE: common/raw_hashtable_metadata_group.cpp ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #include "common/raw_hashtable_metadata_group.h" #include "llvm/ADT/StringExtras.h" namespace Carbon::RawHashtable { auto MetadataGroup::Print(llvm::raw_ostream& out) const -> void { out << "["; llvm::ListSeparator sep; for (uint8_t byte : metadata_bytes) { out << sep << llvm::formatv("{0:x2}", byte); } out << "]"; } } // namespace Carbon::RawHashtable ================================================ FILE: common/raw_hashtable_metadata_group.h ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #ifndef CARBON_COMMON_RAW_HASHTABLE_METADATA_GROUP_H_ #define CARBON_COMMON_RAW_HASHTABLE_METADATA_GROUP_H_ #include #include #include #include #include "common/check.h" #include "common/ostream.h" #include "llvm/ADT/Sequence.h" #include "llvm/ADT/bit.h" #include "llvm/Support/FormatVariadic.h" #include "llvm/Support/MathExtras.h" // Detect whether we can use SIMD accelerated implementations of the control // groups, and include the relevant platform specific APIs for the SIMD // implementations. // // Reference documentation for the SIMD APIs used here: // - https://arm-software.github.io/acle/neon_intrinsics/advsimd.html // - https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html #if defined(__SSSE3__) #include #define CARBON_X86_SIMD_SUPPORT 1 #elif defined(__ARM_NEON) #include #define CARBON_NEON_SIMD_SUPPORT 1 #endif // This namespace collects low-level utilities for implementing hashtable // data structures. This file only provides one of them: // // - Primitives to manage "groups" of hashtable entries that have densely packed // control bytes we can scan rapidly as a group, often using SIMD facilities // to process the entire group at once. namespace Carbon::RawHashtable { // We define a constant max group size. The particular group size used in // practice may vary, but we want to have some upper bound used to ensure // memory allocation is done consistently across different architectures. inline constexpr ssize_t MaxGroupSize = 16; // This takes a collection of bits representing the results of looking for a // particular tag in this metadata group and determines the first position with // a match. The position is represented by either the least significant set bit // or the least significant non-zero byte, depending on `ByteEncoding`. When // represented with a non-zero byte, that byte must have at least its most // significant bit set, but may have other bits set to any value. Bits more // significant than the match may have any value provided there is at least one // match. Zero matches must be represented by a zero input. // // Some bits of the underlying value may be known-zero, which can optimize // various operations. These can be represented as a `ZeroMask`. template class BitIndex : public Printable> { public: using BitsT = BitsInputT; static constexpr bool ByteEncoding = ByteEncodingInput; BitIndex() = default; explicit BitIndex(BitsT bits) : bits_(bits) {} friend auto operator==(BitIndex lhs, BitIndex rhs) -> bool { if (lhs.empty() || rhs.empty()) { return lhs.empty() == rhs.empty(); } // For non-empty bit indices, compare the indices directly to ignore other // (extraneous) parts of the incoming bits. return lhs.index() == rhs.index(); } auto Print(llvm::raw_ostream& out) const -> void { out << llvm::formatv("{0:x}", bits_); } explicit operator bool() const { return !empty(); } // Returns true when there are no matches for the tag. auto empty() const -> bool { CARBON_DCHECK((bits_ & ZeroMask) == 0, "Unexpected non-zero bits!"); __builtin_assume((bits_ & ZeroMask) == 0); return bits_ == 0; } // Returns the index of the first matched tag. auto index() -> ssize_t { CARBON_DCHECK(bits_ != 0, "Cannot get an index from zero bits!"); __builtin_assume(bits_ != 0); ssize_t index = unscaled_index(); if constexpr (ByteEncoding) { // Shift to scale out of the byte encoding. index >>= ByteEncodingShift; } return index; } // Optimized tool to index a pointer `p` by `index()`. template auto index_ptr(T* pointer) -> T* { CARBON_DCHECK(bits_ != 0, "Cannot get an index from zero bits!"); __builtin_assume(bits_ != 0); if constexpr (!ByteEncoding) { return &pointer[unscaled_index()]; } ssize_t index = unscaled_index(); // Scale the index as we counted zero *bits* and not zero *bytes*. // However, we can fold that scale with the size of `T` when it is a power // of two or divisible by 8. CARBON_DCHECK( (index & ((static_cast(1) << ByteEncodingShift) - 1)) == 0); if constexpr (sizeof(T) % 8 == 0) { constexpr size_t FoldedScale = sizeof(T) / 8; index *= FoldedScale; return reinterpret_cast( &reinterpret_cast(pointer)[index]); } else if constexpr (llvm::isPowerOf2_64(sizeof(T))) { constexpr size_t ScaleShift = llvm::ConstantLog2(); static_assert(ScaleShift <= ByteEncodingShift, "Scaling by >=8 should be handled above!"); constexpr size_t FoldedShift = ByteEncodingShift - ScaleShift; index >>= FoldedShift; return reinterpret_cast( &reinterpret_cast(pointer)[index]); } // Nothing we can fold here. return &pointer[index >> ByteEncodingShift]; } private: // When using a byte encoding, we'll need to shift any index by this amount. static constexpr size_t ByteEncodingShift = 3; auto unscaled_index() -> ssize_t { if constexpr (!ByteEncoding) { // Note the cast to `size_t` to force zero extending the result. return static_cast(llvm::countr_zero(bits_)); } else { // The index is encoded in the high bit of each byte. We compute the index // by counting the number of low zero bytes there are before the first // byte with its high bit set. Rather that shifting the high bit to be the // low bit and counting the trailing (least significant) zero bits // directly, we instead byte-reverse the bits and count the *leading* // (most significant) zero bits. While this may be a wash on CPUs with // direct support for counting the trailing zero bits, AArch64 only // supports counting the leading zero bits and requires a bit-reverse to // count the trailing zero bits. Doing the byte-reverse approach // essentially combines moving the high bit into the low bit and the // reverse necessary for counting the zero bits. While this only removes // one instruction, it is an instruction in the critical path of the // hottest part of table lookup, and that critical path dependency height // is few enough instructions that removing even one significantly impacts // latency. // // We also cast to `size_t` to clearly zero-extend the result. return static_cast(llvm::countl_zero(llvm::byteswap(bits_))); } } BitsT bits_ = 0; }; // This is like `BitIndex`, but allows iterating through all of the matches. // // A key requirement for efficient iteration is that all of the matches are // represented with a single bit and there are no other bits set. For example, // with byte-encoded bit indices, exactly the high bit and no other bit of each // matching byte must be set. This is a stricter constraint than what `BitIndex` // alone would impose on any one of the matches. template class BitIndexRange : public Printable> { public: using BitsT = BitIndexT::BitsT; static_assert(BitIndexT::ByteEncoding || ByteEncodingMask == 0, "Non-byte encoding must not have a byte encoding mask."); class Iterator : public llvm::iterator_facade_base { public: Iterator() = default; explicit Iterator(BitsT bits) : bits_(bits) {} friend auto operator==(const Iterator& lhs, const Iterator& rhs) -> bool { return lhs.bits_ == rhs.bits_; } auto operator*() -> ssize_t& { CARBON_DCHECK(bits_ != 0, "Cannot get an index from zero bits!"); __builtin_assume(bits_ != 0); index_ = BitIndexT(bits_).index(); // Note that we store the index in a member so we can return a reference // to it here as required to be a forward iterator. return index_; } template auto index_ptr(T* pointer) -> T* { return BitIndexT(bits_).index_ptr(pointer); } auto operator++() -> Iterator& { CARBON_DCHECK(bits_ != 0, "Must not increment past the end!"); __builtin_assume(bits_ != 0); if constexpr (ByteEncodingMask != 0) { // Apply an increment mask to the bits first. This is used with the byte // encoding when the mask isn't needed until we begin incrementing. bits_ &= ByteEncodingMask; } // Clears the least significant set bit, effectively stepping to the next // match. bits_ &= (bits_ - 1); return *this; } private: ssize_t index_; BitsT bits_ = 0; }; BitIndexRange() = default; explicit BitIndexRange(BitsT bits) : bits_(bits) {} explicit operator bool() const { return !empty(); } auto empty() const -> bool { return BitIndexT(bits_).empty(); } auto begin() const -> Iterator { return Iterator(bits_); } auto end() const -> Iterator { return Iterator(); } friend auto operator==(BitIndexRange lhs, BitIndexRange rhs) -> bool { if constexpr (ByteEncodingMask == 0) { // If there is no encoding mask, we can just compare the bits directly. return lhs.bits_ == rhs.bits_; } else { // Otherwise, compare the initial bit indices and the masked bits. return BitIndexT(lhs.bits_) == BitIndexT(rhs.bits_) && (lhs.bits_ & ByteEncodingMask) == (rhs.bits_ & ByteEncodingMask); } } // Define heterogeneous equality between a masked (the current type) and // unmasked range. Requires a non-zero mask to avoid a redundant definition // with the homogeneous equality. friend auto operator==(BitIndexRange lhs, BitIndexRange rhs) -> bool requires(ByteEncodingMask != 0) { // For mixed masked / unmasked comparison, we make sure the initial indices // are the same and that the masked side (LHS) is the same after masking as // the unmasked side (RHS). return BitIndexT(lhs.bits_) == BitIndexT(rhs.bits_) && (lhs.bits_ & ByteEncodingMask) == rhs.bits_; } auto Print(llvm::raw_ostream& out) const -> void { out << llvm::formatv("{0:x}", bits_); } explicit operator BitsT() const { return bits_; } explicit operator BitIndexT() const { return BitIndexT(bits_); } private: template friend class BitIndexRange; BitsT bits_ = 0; }; // A group of metadata bytes that can be manipulated together. // // The metadata bytes used Carbon's hashtable implementation are designed to // support being manipulating as groups, either using architecture specific SIMD // code sequences or using portable SIMD-in-an-integer-register code sequences. // These operations are unusually performance sensitive and in sometimes // surprising ways. The implementations here are crafted specifically to // optimize the particular usages in Carbon's hashtable and should not be // expected to be reusable in any other context. // // Throughout the functions operating on this type we try to use patterns with a // fallback portable implementation which can be directly used in the absence of // a SIMD implementation, but is also used (with the same code) to check that // any SIMD implementation produces the same result as the portable one. These // patterns help minimize un-compiled or un-tested paths through either portable // or SIMD code, regardless of which path is actually *used* on a particular // platform. To illustrate a common version of this pattern, we might have code // like: // // ```cpp // auto MetadataGroup::Operation(...) -> ... { // ... portable_result; // ... simd_result; // if constexpr (!UseSimd || DebugSimd) { // portable_result = PortableOperation(...); // } // if (UseSimd || DebugSimd) { // simd_result = SimdOperation(...) // CARBON_DCHECK(result == portable_result, "{0}", ...); // } // return UseSimd ? simd_result : portable_result; // } // ``` class MetadataGroup : public Printable { public: static constexpr ssize_t Size = #if CARBON_X86_SIMD_SUPPORT 16; #else 8; #endif static_assert(Size >= 8); static_assert(Size % 8 == 0); static_assert(Size <= MaxGroupSize); static_assert(MaxGroupSize % Size == 0); static_assert(llvm::isPowerOf2_64(Size), "The group size must be a constant power of two so dividing by " "it is a simple shift."); static constexpr ssize_t Mask = Size - 1; // Each control byte can have special values. All special values have the // most significant bit cleared to distinguish them from the seven hash bits // stored when the control byte represents a full bucket. // // Otherwise, their values are chose primarily to provide efficient SIMD // implementations of the common operations on an entire control group. static constexpr uint8_t Empty = 0; static constexpr uint8_t Deleted = 1; static constexpr uint8_t PresentMask = 0b1000'0000; // Whether to use a SIMD implementation. Even when we *support* a SIMD // implementation, we do not always have to use it in the event that it is // less efficient than the portable version. static constexpr bool UseSimd = #if CARBON_X86_SIMD_SUPPORT true; #else false; #endif // Some architectures make it much more efficient to build the match indices // in a byte-encoded form rather than a bit-encoded form. This encoding // changes verification and other aspects of our algorithms. static constexpr bool ByteEncoding = #if CARBON_X86_SIMD_SUPPORT false; #else true; #endif static_assert(!ByteEncoding || Size == 8, "We can only support byte encoding with a group size of 8."); // We need to indicate to users of the metadata group when they can hold a // group value in a "register" (local variable) across clearing of individual // bytes in the group efficiently. If the entire group can fit in an integer // register, this works well and clients of the group should work to use the // already-loaded value when clearing bytes. But when we have a larger group // size, clearing the byte will typically require storing a byte to memory and // re-loading the group. The usage patterns that need to clear bytes can in // those cases avoid clearing a loaded group, and clear the byte directly in // the larger metadata array. static constexpr bool FastByteClear = Size == 8; // Most and least significant bits set. static constexpr uint64_t Msbs = 0x8080'8080'8080'8080ULL; static constexpr uint64_t Lsbs = 0x0101'0101'0101'0101ULL; using MatchIndex = BitIndex, ByteEncoding, /*ZeroMask=*/ByteEncoding ? 0 : (~0U << Size)>; // Only one kind of portable matched range is needed. using PortableMatchRange = BitIndexRange; // We use specialized match range types for SIMD implementations to allow // deferring the masking operation where useful. When that optimization // doesn't apply, these will be the same type. using SimdMatchRange = BitIndexRange; using SimdMatchPresentRange = BitIndexRange; // The public API range types can be either the portable or SIMD variations, // selected here. using MatchRange = std::conditional_t; using MatchPresentRange = std::conditional_t; union { uint8_t metadata_bytes[Size]; uint64_t metadata_ints[Size / 8]; #if CARBON_NEON_SIMD_SUPPORT uint8x8_t metadata_vec = {}; static_assert(sizeof(metadata_vec) == Size); #elif CARBON_X86_SIMD_SUPPORT __m128i metadata_vec = {}; static_assert(sizeof(metadata_vec) == Size); #endif }; auto Print(llvm::raw_ostream& out) const -> void; friend auto operator==(MetadataGroup lhs, MetadataGroup rhs) -> bool { return CompareEqual(lhs, rhs); } // The main API for this class. This API will switch between a portable and // SIMD implementation based on what is most efficient, but in debug builds // will cross check that the implementations do not diverge. // Load and return a group of metadata bytes out of the main metadata array at // a particular `index`. The index must be a multiple of `GroupSize`. This // will arrange for the load to place the group into the correct structure for // efficient register-based processing. static auto Load(const uint8_t* metadata, ssize_t index) -> MetadataGroup; // Store this metadata group into the main metadata array at the provided // `index`. The index must be a multiple of `GroupSize`. auto Store(uint8_t* metadata, ssize_t index) const -> void; // Clear a byte of this group's metadata at the provided `byte_index` to the // empty value. // // Note that this must only be called when `FastByteClear` is true -- in all // other cases users of this class should arrange to clear individual bytes in // the underlying array rather than using the group API. This is checked by a // static_assert, and the function is templated so that it is not instantiated // in the cases where it would not be valid. template auto ClearByte(ssize_t byte_index) -> void; // Clear all of this group's metadata bytes that indicate a deleted slot to // the empty value. auto ClearDeleted() -> void; // Find all of the bytes of metadata in this group that are present and whose // low 7 bits match the provided `tag`. The `tag` byte must have a clear high // bit, only 7 bits of tag are used. Note that this means the provided tag is // *not* the actual present metadata byte -- this function is responsible for // mapping the tag into that form as it can do so more efficiently in some // cases. A range over all of the byte indices which matched is returned. auto Match(uint8_t tag) const -> MatchRange; // Find all of the present bytes of metadata in this group. A range over all // of the byte indices which are present is returned. auto MatchPresent() const -> MatchPresentRange; // Find the first byte of the metadata group that is empty and return that // index. There is no order or position required for which of the bytes of // metadata is considered "first", any model will do that makes it efficient // to produce the matching index. Must return an empty match index if no bytes // match the empty metadata. auto MatchEmpty() const -> MatchIndex; // Find the first byte of the metadata group that is deleted and return that // index. There is no order or position required for which of the bytes of // metadata is considered "first", any model will do that makes it efficient // to produce the matching index. Must return an empty match index if no bytes // match the deleted metadata. auto MatchDeleted() const -> MatchIndex; private: // Two classes only defined in the benchmark code are allowed to directly call // the portable and SIMD implementations for benchmarking purposes. friend class BenchmarkPortableMetadataGroup; friend class BenchmarkSimdMetadataGroup; // All SIMD variants that we have an implementation for should be enabled for // debugging. This lets us maintain a SIMD implementation even if it is not // used due to performance reasons, and easily re-enable it if the performance // changes. static constexpr bool DebugSimd = #if !defined(NDEBUG) && (CARBON_NEON_SIMD_SUPPORT || CARBON_X86_SIMD_SUPPORT) true; #else false; #endif using MatchBitsT = MatchIndex::BitsT; // A helper function to allow deducing the return type from the selected arm // of a `constexpr` ternary. template static auto ConstexprTernary(LeftT lhs, RightT rhs) { if constexpr (Condition) { return lhs; } else { return rhs; } } static auto CompareEqual(MetadataGroup lhs, MetadataGroup rhs) -> bool; // Functions for validating the returned matches agree with what is predicted // by the `byte_match` function. These either `CHECK`-fail or return true. To // pass validation, the `*_bits` argument must have `0x80` for those bytes // where `byte_match` returns true, and `0` for the rest. // `VerifyIndexBits` is for functions that return `MatchIndex`, as they only // promise to return accurate information up to the first match. auto VerifyIndexBits( MatchBitsT index_bits, llvm::function_refbool> byte_match) const -> bool; // `VerifyPortableRangeBits` is for functions that return `MatchRange`, and so // it validates all the bytes of `range_bits`. auto VerifyPortableRangeBits( MatchBitsT range_bits, llvm::function_refbool> byte_match) const -> bool; // Portable implementations of each operation. These are used on platforms // without SIMD support or where the portable implementation is faster than // SIMD. They are heavily optimized even though they are not SIMD because we // expect there to be platforms where the portable implementation can // outperform SIMD. Their behavior and semantics exactly match the // documentation for the un-prefixed functions. // // In debug builds, these also directly verify their results to help establish // baseline functionality. static auto PortableLoad(const uint8_t* metadata, ssize_t index) -> MetadataGroup; auto PortableStore(uint8_t* metadata, ssize_t index) const -> void; auto PortableClearDeleted() -> void; auto PortableMatch(uint8_t tag) const -> PortableMatchRange; auto PortableMatchPresent() const -> PortableMatchRange; auto PortableMatchEmpty() const -> MatchIndex; auto PortableMatchDeleted() const -> MatchIndex; static auto PortableCompareEqual(MetadataGroup lhs, MetadataGroup rhs) -> bool; // SIMD implementations of each operation. We minimize platform-specific APIs // to reduce the scope of errors that can only be discovered building on one // platform, so the bodies of these contain the platform specific code. Their // behavior and semantics exactly match the documentation for the un-prefixed // functions. // // These routines don't directly verify their results as we can build simpler // debug checks by comparing them against the verified portable results. static auto SimdLoad(const uint8_t* metadata, ssize_t index) -> MetadataGroup; auto SimdStore(uint8_t* metadata, ssize_t index) const -> void; auto SimdClearDeleted() -> void; auto SimdMatch(uint8_t tag) const -> SimdMatchRange; auto SimdMatchPresent() const -> SimdMatchPresentRange; auto SimdMatchEmpty() const -> MatchIndex; auto SimdMatchDeleted() const -> MatchIndex; static auto SimdCompareEqual(MetadataGroup lhs, MetadataGroup rhs) -> bool; #if CARBON_X86_SIMD_SUPPORT // A common routine for x86 SIMD matching that can be used for matching // present, empty, and deleted bytes with equal efficiency. auto X86SimdMatch(uint8_t match_byte) const -> SimdMatchRange; #endif }; // Promote the size and mask to top-level constants as we'll need to operate on // the grouped structure outside of the metadata bytes. inline constexpr ssize_t GroupSize = MetadataGroup::Size; inline constexpr ssize_t GroupMask = MetadataGroup::Mask; inline auto MetadataGroup::Load(const uint8_t* metadata, ssize_t index) -> MetadataGroup { MetadataGroup portable_g; if constexpr (!UseSimd || DebugSimd) { portable_g = PortableLoad(metadata, index); if constexpr (!UseSimd) { return portable_g; } } MetadataGroup g = SimdLoad(metadata, index); CARBON_DCHECK(g == portable_g); return g; } inline auto MetadataGroup::Store(uint8_t* metadata, ssize_t index) const -> void { if constexpr (!UseSimd) { std::memcpy(metadata + index, &metadata_bytes, Size); } else { SimdStore(metadata, index); } CARBON_DCHECK(0 == std::memcmp(metadata + index, &metadata_bytes, Size)); } template inline auto MetadataGroup::ClearByte(ssize_t byte_index) -> void { static_assert(!IsCalled || FastByteClear, "Only use byte clearing when fast!"); static_assert(!IsCalled || Size == 8, "The clear implementation assumes an 8-byte group."); metadata_ints[0] &= ~(static_cast(0xff) << (byte_index * 8)); } inline auto MetadataGroup::ClearDeleted() -> void { MetadataGroup portable_g = *this; MetadataGroup simd_g = *this; if constexpr (!UseSimd || DebugSimd) { portable_g.PortableClearDeleted(); } if constexpr (UseSimd || DebugSimd) { simd_g.SimdClearDeleted(); CARBON_DCHECK( simd_g == portable_g, "SIMD cleared group '{0}' doesn't match portable cleared group '{1}'", simd_g, portable_g); } *this = UseSimd ? simd_g : portable_g; } inline auto MetadataGroup::Match(uint8_t tag) const -> MatchRange { // The caller should provide us with the present byte hash, and not set any // present bit tag on it so that this layer can manage tagging the high bit of // a present byte. CARBON_DCHECK((tag & PresentMask) == 0, "{0:x}", tag); PortableMatchRange portable_result; SimdMatchRange simd_result; if constexpr (!UseSimd || DebugSimd) { portable_result = PortableMatch(tag); } if constexpr (UseSimd || DebugSimd) { simd_result = SimdMatch(tag); CARBON_DCHECK(simd_result == portable_result, "SIMD result '{0}' doesn't match portable result '{1}'", simd_result, portable_result); } // Return whichever result we're using. return ConstexprTernary(simd_result, portable_result); } inline auto MetadataGroup::MatchPresent() const -> MatchPresentRange { PortableMatchRange portable_result; SimdMatchPresentRange simd_result; if constexpr (!UseSimd || DebugSimd) { portable_result = PortableMatchPresent(); } if constexpr (UseSimd || DebugSimd) { simd_result = SimdMatchPresent(); CARBON_DCHECK(simd_result == portable_result, "SIMD result '{0}' doesn't match portable result '{1}'", simd_result, portable_result); } // Return whichever result we're using. return ConstexprTernary(simd_result, portable_result); } inline auto MetadataGroup::MatchEmpty() const -> MatchIndex { MatchIndex portable_result; MatchIndex simd_result; if constexpr (!UseSimd || DebugSimd) { portable_result = PortableMatchEmpty(); } if constexpr (UseSimd || DebugSimd) { simd_result = SimdMatchEmpty(); CARBON_DCHECK(simd_result == portable_result, "SIMD result '{0}' doesn't match portable result '{1}'", simd_result, portable_result); } return UseSimd ? simd_result : portable_result; } inline auto MetadataGroup::MatchDeleted() const -> MatchIndex { MatchIndex portable_result; MatchIndex simd_result; if constexpr (!UseSimd || DebugSimd) { portable_result = PortableMatchDeleted(); } if constexpr (UseSimd || DebugSimd) { simd_result = SimdMatchDeleted(); CARBON_DCHECK(simd_result == portable_result, "SIMD result '{0}' doesn't match portable result '{1}'", simd_result, portable_result); } return UseSimd ? simd_result : portable_result; } inline auto MetadataGroup::CompareEqual(MetadataGroup lhs, MetadataGroup rhs) -> bool { bool portable_result; bool simd_result; if constexpr (!UseSimd || DebugSimd) { portable_result = PortableCompareEqual(lhs, rhs); } if constexpr (UseSimd || DebugSimd) { simd_result = SimdCompareEqual(lhs, rhs); CARBON_DCHECK(simd_result == portable_result); } return UseSimd ? simd_result : portable_result; } inline auto MetadataGroup::VerifyIndexBits( MatchBitsT index_bits, llvm::function_refbool> byte_match) const -> bool { for (ssize_t byte_index : llvm::seq(0, Size)) { if constexpr (!ByteEncoding) { if (byte_match(metadata_bytes[byte_index])) { CARBON_CHECK(((index_bits >> byte_index) & 1) == 1, "Bit not set at matching byte index: {0}", byte_index); // Only the first match is needed, so stop scanning once found. break; } CARBON_CHECK(((index_bits >> byte_index) & 1) == 0, "Bit set at non-matching byte index: {0}", byte_index); } else { // `index_bits` is byte-encoded rather than bit encoded, so extract a // byte. uint8_t index_byte = (index_bits >> (byte_index * 8)) & 0xFF; if (byte_match(metadata_bytes[byte_index])) { CARBON_CHECK( (index_byte & 0x80) == 0x80, "Should have the high bit set for a matching byte, found: {0:x}", index_byte); // Only the first match is needed so stop scanning once found. break; } CARBON_CHECK( index_byte == 0, "Should have no bits set for an unmatched byte, found: {0:x}", index_byte); } } return true; } inline auto MetadataGroup::VerifyPortableRangeBits( MatchBitsT range_bits, llvm::function_refbool> byte_match) const -> bool { for (ssize_t byte_index : llvm::seq(0, Size)) { if constexpr (!ByteEncoding) { if (byte_match(metadata_bytes[byte_index])) { CARBON_CHECK(((range_bits >> byte_index) & 1) == 1, "Bit not set at matching byte index: {0}", byte_index); } else { CARBON_CHECK(((range_bits >> byte_index) & 1) == 0, "Bit set at non-matching byte index: {0}", byte_index); } } else { // `range_bits` is byte-encoded rather than bit encoded, so extract a // byte. uint8_t range_byte = (range_bits >> (byte_index * 8)) & 0xFF; if (byte_match(metadata_bytes[byte_index])) { CARBON_CHECK(range_byte == 0x80, "Should just have the high bit set for a matching byte, " "found: {0:x}", range_byte); } else { CARBON_CHECK( range_byte == 0, "Should have no bits set for an unmatched byte, found: {0:x}", range_byte); } } } return true; } inline auto MetadataGroup::PortableLoad(const uint8_t* metadata, ssize_t index) -> MetadataGroup { MetadataGroup g; static_assert(sizeof(g) == Size); std::memcpy(&g.metadata_bytes, metadata + index, Size); return g; } inline auto MetadataGroup::PortableStore(uint8_t* metadata, ssize_t index) const -> void { std::memcpy(metadata + index, &metadata_bytes, Size); } inline auto MetadataGroup::PortableClearDeleted() -> void { for (uint64_t& metadata_int : metadata_ints) { // Deleted bytes have only the least significant bits set, so to clear them // we only need to clear the least significant bit. And empty bytes already // have a clear least significant bit, so the only least significant bits we // need to preserve are those of present bytes. The most significant bit of // every present byte is set, so we take the most significant bit of each // byte, shift it into the least significant bit position, and bit-or it // with the compliment of `Lsbs`. This will have ones for every bit but the // least significant bits, and ones for the least significant bits of every // present byte. metadata_int &= (~Lsbs | metadata_int >> 7); } } inline auto MetadataGroup::PortableMatch(uint8_t tag) const -> MatchRange { // The caller should provide us with the present byte hash, and not set any // present bit tag on it so that this layer can manage tagging the high bit of // a present byte. CARBON_DCHECK((tag & PresentMask) == 0, "{0:x}", tag); // Use a simple fallback approach for sizes beyond 8. // TODO: Instead of a simple fallback, we should generalize the below // algorithm for sizes above 8, even if to just exercise the same code on // more platforms. if constexpr (Size > 8) { static_assert(Size <= 32, "Sizes larger than 32 not yet supported!"); uint32_t match_bits = 0; uint32_t bit = 1; uint8_t present_byte = tag | PresentMask; for (ssize_t i : llvm::seq(0, Size)) { if (metadata_bytes[i] == present_byte) { match_bits |= bit; } bit <<= 1; } return MatchRange(match_bits); } // This algorithm only works for matching *present* bytes. We leverage the // set high bit in the present case as part of the algorithm. The whole // algorithm has a critical path height of 4 operations, and does 6 // operations total on AArch64. The operation dependency graph is: // // group | Msbs Lsbs * match_byte + Msbs // \ / // match_bits ^ broadcast // | // group & Msbs Msbs - match_bits // \ / // group_Msbs & match_bits // // This diagram and the operation count are specific to AArch64 where we have // a fused *integer* multiply-add operation. // // While it is superficially similar to the "find zero bytes in a word" bit // math trick, it is different because this is designed to have no false // positives and perfectly produce 0x80 for matching bytes and 0x00 for // non-matching bytes. This is do-able because we constrain to only handle // present matches which only require testing 7 bits and have a particular // layout. // Set the high bit of every byte to `1`. Any matching byte is a present byte // and so always has this bit set as well, which means the xor below, in // addition to zeroing the low 7 bits of any byte that matches the tag, also // clears the high bit of every byte. uint64_t match_bits = metadata_ints[0] | Msbs; // Broadcast the match byte to all bytes, and mask in the present bits in the // Msbs of each byte. We structure this as a multiply and an add because we // know that the add cannot carry, and this way it can be lowered using // combined multiply-add instructions if available. uint64_t broadcast = Lsbs * tag + Msbs; CARBON_DCHECK(broadcast == (Lsbs * tag | Msbs), "Unexpected carry from addition!"); // Xor the broadcast byte pattern. This makes bytes with matches become 0, and // clears the high-bits of non-matches. Note that if we are looking for a tag // with the same value as `Empty` or `Deleted`, those bytes will be zero as // well. match_bits = match_bits ^ broadcast; // Subtract each byte of `match_bits` from `0x80` bytes. After this, the high // bit will be set only for those bytes that were zero. match_bits = Msbs - match_bits; // Zero everything but the high bits, and also zero the high bits of any bytes // for "not present" slots in the original group. This avoids false positives // for `Empty` and `Deleted` bytes in the metadata. match_bits &= (metadata_ints[0] & Msbs); // At this point, `match_bits` has the high bit set for bytes where the // original group byte equals `tag` plus the high bit. CARBON_DCHECK(VerifyPortableRangeBits( match_bits, [&](uint8_t byte) { return byte == (tag | PresentMask); })); return MatchRange(match_bits); } inline auto MetadataGroup::PortableMatchPresent() const -> MatchRange { // Use a simple fallback approach for sizes beyond 8. // TODO: Instead of a simple fallback, we should generalize the below // algorithm for sizes above 8, even if to just exercise the same code on // more platforms. if constexpr (Size > 8) { static_assert(Size <= 32, "Sizes larger than 32 not yet supported!"); uint32_t match_bits = 0; uint32_t bit = 1; for (ssize_t i : llvm::seq(0, Size)) { if (metadata_bytes[i] & PresentMask) { match_bits |= bit; } bit <<= 1; } return MatchRange(match_bits); } // Want to keep the high bit of each byte, which indicates whether that byte // represents a present slot. uint64_t match_bits = metadata_ints[0] & Msbs; CARBON_DCHECK(VerifyPortableRangeBits( match_bits, [&](uint8_t byte) { return (byte & PresentMask) != 0; })); return MatchRange(match_bits); } inline auto MetadataGroup::PortableMatchEmpty() const -> MatchIndex { // Use a simple fallback approach for sizes beyond 8. // TODO: Instead of a simple fallback, we should generalize the below // algorithm for sizes above 8, even if to just exercise the same code on // more platforms. if constexpr (Size > 8) { static_assert(Size <= 32, "Sizes larger than 32 not yet supported!"); uint32_t bit = 1; for (ssize_t i : llvm::seq(0, Size)) { if (metadata_bytes[i] == Empty) { return MatchIndex(bit); } bit <<= 1; } return MatchIndex(0); } // This sets the high bit of every byte in `match_bits` unless the // corresponding metadata byte is 0. We take advantage of the fact that // the metadata bytes in are non-zero only if they are either: // - present: in which case the high bit of the byte will already be set; or // - deleted: in which case the byte will be 1, and shifting it left by 7 will // cause the high bit to be set. uint64_t match_bits = metadata_ints[0] | (metadata_ints[0] << 7); // This inverts the high bits of the bytes, and clears the remaining bits. match_bits = ~match_bits & Msbs; // The high bits of the bytes of `match_bits` are set if the corresponding // metadata byte is `Empty`. CARBON_DCHECK( VerifyIndexBits(match_bits, [](uint8_t byte) { return byte == Empty; })); return MatchIndex(match_bits); } inline auto MetadataGroup::PortableMatchDeleted() const -> MatchIndex { // Use a simple fallback approach for sizes beyond 8. // TODO: Instead of a simple fallback, we should generalize the below // algorithm for sizes above 8, even if to just exercise the same code on // more platforms. if constexpr (Size > 8) { static_assert(Size <= 32, "Sizes larger than 32 not yet supported!"); uint32_t bit = 1; for (ssize_t i : llvm::seq(0, Size)) { if (metadata_bytes[i] == Deleted) { return MatchIndex(bit); } bit <<= 1; } return MatchIndex(0); } // This sets the high bit of every byte in `match_bits` unless the // corresponding metadata byte is 1. We take advantage of the fact that the // metadata bytes are not 1 only if they are either: // - present: in which case the high bit of the byte will already be set; or // - empty: in which case the byte will be 0, and in that case inverting and // shifting left by 7 will have the high bit set. uint64_t match_bits = metadata_ints[0] | (~metadata_ints[0] << 7); // This inverts the high bits of the bytes, and clears the remaining bits. match_bits = ~match_bits & Msbs; // The high bits of the bytes of `match_bits` are set if the corresponding // metadata byte is `Deleted`. CARBON_DCHECK(VerifyIndexBits(match_bits, [](uint8_t byte) { return byte == Deleted; })); return MatchIndex(match_bits); } inline auto MetadataGroup::PortableCompareEqual(MetadataGroup lhs, MetadataGroup rhs) -> bool { return llvm::equal(lhs.metadata_bytes, rhs.metadata_bytes); } inline auto MetadataGroup::SimdLoad(const uint8_t* metadata, ssize_t index) -> MetadataGroup { MetadataGroup g; #if CARBON_NEON_SIMD_SUPPORT g.metadata_vec = vld1_u8(metadata + index); #elif CARBON_X86_SIMD_SUPPORT g.metadata_vec = _mm_load_si128(reinterpret_cast(metadata + index)); #else static_assert(!UseSimd, "Unimplemented SIMD operation"); static_cast(metadata); static_cast(index); #endif return g; } // NOLINTNEXTLINE(readability-non-const-parameter): Mutation is in #if. inline auto MetadataGroup::SimdStore(uint8_t* metadata, ssize_t index) const -> void { #if CARBON_NEON_SIMD_SUPPORT vst1_u8(metadata + index, metadata_vec); #elif CARBON_X86_SIMD_SUPPORT _mm_store_si128(reinterpret_cast<__m128i*>(metadata + index), metadata_vec); #else static_assert(!UseSimd, "Unimplemented SIMD operation"); static_cast(metadata); static_cast(index); #endif } inline auto MetadataGroup::SimdClearDeleted() -> void { #if CARBON_NEON_SIMD_SUPPORT // There is no good Neon operation to implement this, so do it using integer // code. This is reasonably fast, but unfortunate because it forces the group // out of a SIMD register and into a general purpose register, which can have // high latency. metadata_ints[0] &= (~Lsbs | metadata_ints[0] >> 7); #elif CARBON_X86_SIMD_SUPPORT // For each byte, use `metadata_vec` if the byte's high bit is set (indicating // it is present), otherwise (it is empty or deleted) replace it with zero // (representing empty). metadata_vec = _mm_blendv_epi8(_mm_setzero_si128(), metadata_vec, metadata_vec); #else static_assert(!UseSimd && !DebugSimd, "Unimplemented SIMD operation"); #endif } inline auto MetadataGroup::SimdMatch(uint8_t tag) const -> SimdMatchRange { SimdMatchRange result; #if CARBON_NEON_SIMD_SUPPORT // Broadcast byte we want to match to every byte in the vector. auto match_byte_vec = vdup_n_u8(tag | PresentMask); // Result bytes have all bits set for the bytes that match, so we have to // clear everything but Msbs next. auto match_byte_cmp_vec = vceq_u8(metadata_vec, match_byte_vec); uint64_t match_bits = vreinterpret_u64_u8(match_byte_cmp_vec)[0]; // Note that the range will lazily mask to the Msbs as part of incrementing. result = SimdMatchRange(match_bits); #elif CARBON_X86_SIMD_SUPPORT result = X86SimdMatch(tag | PresentMask); #else static_assert(!UseSimd && !DebugSimd, "Unimplemented SIMD operation"); static_cast(tag); #endif return result; } inline auto MetadataGroup::SimdMatchPresent() const -> SimdMatchPresentRange { SimdMatchPresentRange result; #if CARBON_NEON_SIMD_SUPPORT // Just extract the metadata directly. uint64_t match_bits = vreinterpret_u64_u8(metadata_vec)[0]; // Even though the Neon SIMD range will do its own masking, we have to mask // here so that `empty` is correct. result = SimdMatchPresentRange(match_bits & Msbs); #elif CARBON_X86_SIMD_SUPPORT // We arranged the byte vector so that present bytes have the high bit set, // which this instruction extracts. result = SimdMatchPresentRange(_mm_movemask_epi8(metadata_vec)); #else static_assert(!UseSimd && !DebugSimd, "Unimplemented SIMD operation"); #endif return result; } inline auto MetadataGroup::SimdMatchEmpty() const -> MatchIndex { MatchIndex result; #if CARBON_NEON_SIMD_SUPPORT // Compare all bytes with zero, as that is the empty byte value. Result will // have all bits set for any input zero byte, so we zero all but the high bits // below. auto cmp_vec = vceqz_u8(metadata_vec); uint64_t metadata_bits = vreinterpret_u64_u8(cmp_vec)[0]; // The matched range is likely to be tested for zero by the caller, and that // test can often be folded into masking the bits with `Msbs` when we do that // mask in the scalar domain rather than the SIMD domain. So we do the mask // here rather than above prior to extracting the match bits. result = MatchIndex(metadata_bits & Msbs); #elif CARBON_X86_SIMD_SUPPORT // Even though we only need the first match rather than all matches, we don't // have a more efficient way to compute this on x86 and so we reuse the // general match infrastructure that computes all matches in a bit-encoding. // We then convert it into a `MatchIndex` that just finds the first one. result = static_cast(X86SimdMatch(Empty)); #else static_assert(!UseSimd && !DebugSimd, "Unimplemented SIMD operation"); #endif return result; } inline auto MetadataGroup::SimdMatchDeleted() const -> MatchIndex { MatchIndex result; #if CARBON_NEON_SIMD_SUPPORT // Broadcast the `Deleted` byte across the vector and compare the bytes of // that with the metadata vector. The result will have all bits set for any // input zero byte, so we zero all but the high bits below. auto cmp_vec = vceq_u8(metadata_vec, vdup_n_u8(Deleted)); uint64_t match_bits = vreinterpret_u64_u8(cmp_vec)[0]; // The matched range is likely to be tested for zero by the caller, and that // test can often be folded into masking the bits with `Msbs` when we do that // mask in the scalar domain rather than the SIMD domain. So we do the mask // here rather than above prior to extracting the match bits. result = MatchIndex(match_bits & Msbs); #elif CARBON_X86_SIMD_SUPPORT // Even though we only need the first match rather than all matches, we don't // have a more efficient way to compute this on x86 and so we reuse the // general match infrastructure that computes all matches in a bit-encoding. // We then convert it into a `MatchIndex` that just finds the first one. result = static_cast(X86SimdMatch(Deleted)); #else static_assert(!UseSimd && !DebugSimd, "Unimplemented SIMD operation"); #endif return result; } inline auto MetadataGroup::SimdCompareEqual(MetadataGroup lhs, MetadataGroup rhs) -> bool { #if CARBON_NEON_SIMD_SUPPORT return vreinterpret_u64_u8(vceq_u8(lhs.metadata_vec, rhs.metadata_vec))[0] == static_cast(-1LL); #elif CARBON_X86_SIMD_SUPPORT // Different x86 SIMD extensions provide different comparison functionality // available. #if __SSE4_2__ // With SSE 4.2, we can directly test and branch in the SIMD domain on whether // the two metadata vectors are equal. return _mm_testc_si128(_mm_cmpeq_epi8(lhs.metadata_vec, rhs.metadata_vec), _mm_set1_epi8(0xff)) == 1; #else // With older versions of SSE we have to extract the result of the comparison, // much like we do when matching. That will have the usual bitmask // representing equal bytes, and test for that exact bitmask in scalar code. return _mm_movemask_epi8(_mm_cmpeq_epi8(lhs.metadata_vec, rhs.metadata_vec)) == 0x0000'ffffU; #endif #else static_assert(!UseSimd && !DebugSimd, "Unimplemented SIMD operation"); static_cast(lhs); static_cast(rhs); return false; #endif } #if CARBON_X86_SIMD_SUPPORT inline auto MetadataGroup::X86SimdMatch(uint8_t match_byte) const -> MatchRange { // Broadcast the byte we're matching against to all bytes in a vector, and // compare those bytes with the metadata vector bytes. auto match_byte_vec = _mm_set1_epi8(match_byte); auto match_byte_cmp_vec = _mm_cmpeq_epi8(metadata_vec, match_byte_vec); // Extract the result of each byte-wise comparison into the low bits of an // integer. uint32_t match_bits = _mm_movemask_epi8(match_byte_cmp_vec); return MatchRange(match_bits); } #endif } // namespace Carbon::RawHashtable #endif // CARBON_COMMON_RAW_HASHTABLE_METADATA_GROUP_H_ ================================================ FILE: common/raw_hashtable_metadata_group_benchmark.cpp ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #include #include #include #include #include "absl/random/random.h" #include "common/raw_hashtable_metadata_group.h" namespace Carbon::RawHashtable { // If we have any SIMD support, create dedicated benchmark utilities for the // portable and SIMD implementation so we can directly benchmark both. #if CARBON_NEON_SIMD_SUPPORT || CARBON_X86_SIMD_SUPPORT // Override the core API with explicit use of the portable API. class BenchmarkPortableMetadataGroup : public MetadataGroup { public: explicit BenchmarkPortableMetadataGroup(MetadataGroup g) : MetadataGroup(g) {} static auto Load(uint8_t* metadata, ssize_t index) -> BenchmarkPortableMetadataGroup { return BenchmarkPortableMetadataGroup(PortableLoad(metadata, index)); } auto Store(uint8_t* metadata, ssize_t index) const -> void { PortableStore(metadata, index); } auto ClearDeleted() -> void { PortableClearDeleted(); } auto Match(uint8_t present_byte) const -> PortableMatchRange { return PortableMatch(present_byte); } auto MatchPresent() const -> PortableMatchRange { return PortableMatchPresent(); } auto MatchEmpty() const -> MatchIndex { return PortableMatchEmpty(); } auto MatchDeleted() const -> MatchIndex { return PortableMatchDeleted(); } }; // Override the core API with explicit use of the SIMD API. class BenchmarkSimdMetadataGroup : public MetadataGroup { public: explicit BenchmarkSimdMetadataGroup(MetadataGroup g) : MetadataGroup(g) {} static auto Load(uint8_t* metadata, ssize_t index) -> BenchmarkSimdMetadataGroup { return BenchmarkSimdMetadataGroup(SimdLoad(metadata, index)); } auto Store(uint8_t* metadata, ssize_t index) const -> void { SimdStore(metadata, index); } auto ClearDeleted() -> void { SimdClearDeleted(); } auto Match(uint8_t present_byte) const -> SimdMatchRange { return SimdMatch(present_byte); } auto MatchPresent() const -> SimdMatchPresentRange { return SimdMatchPresent(); } auto MatchEmpty() const -> MatchIndex { return SimdMatchEmpty(); } auto MatchDeleted() const -> MatchIndex { return SimdMatchDeleted(); } }; #endif namespace { // The number of metadata groups we use when benchmarking a particular scenario // of matching within a group. constexpr ssize_t BenchSize = 256; #if CARBON_NEON_SIMD_SUPPORT || CARBON_X86_SIMD_SUPPORT using PortableGroup = BenchmarkPortableMetadataGroup; using SimdGroup = BenchmarkSimdMetadataGroup; #endif struct BenchMetadata { // The metadata for benchmarking, arranged in `BenchSize` groups, each one // `GroupSize` in length. As a consequence, the size of this array will always // be `BenchSize * GroupSize`. llvm::MutableArrayRef metadata; // For benchmarking random matches in the metadata, each byte here is the tag // that should be matched against the corresponding group of the metadata. // Because this array parallels the *groups* of the metadata array, its size // will be `BenchSize`. For other kinds, this is empty. llvm::ArrayRef bytes; }; enum class BenchKind : uint8_t { Random, Empty, Deleted, }; // This routine should only be called once per `BenchKind` as the initializer of // a global variable below. It returns an `ArrayRef` pointing into // function-local static storage that provides our benchmark metadata. // // The returned array will have exactly `GroupSize` elements, each of // `BenchMetadata`. For the `BenchMetadata` at index `i`, there will be `i+1` // matches of that kind within each group of the metadata. This lets us // benchmark each of the possible match-counts for a group. template static auto BuildBenchMetadata() -> llvm::ArrayRef { // We build `GroupSize` elements of `BenchMetadata` below, and so we need // `GroupSize` copies of each of these arrays to serve as inputs to it. // // The first storage is of `BenchSize` groups of metadata. static uint8_t metadata_storage[GroupSize][BenchSize * GroupSize]; // When `Kind` is `Random`, each group above will have a *different* byte that // matches in that group. This array stores those bytes for the benchmark to // match against the group. static uint8_t bytes_storage[GroupSize][BenchSize]; // The backing storage for the returned `ArrayRef`. static BenchMetadata bm_storage[GroupSize]; absl::BitGen gen; for (auto [bm_index, bm] : llvm::enumerate(bm_storage)) { int match_count = bm_index + 1; for (ssize_t g_index : llvm::seq(0, BenchSize)) { // Start by filling the group with random bytes. llvm::MutableArrayRef group_bytes( &metadata_storage[bm_index][g_index * GroupSize], GroupSize); for (uint8_t& b : group_bytes) { b = absl::Uniform(gen) | MetadataGroup::PresentMask; } // Now we need up to `match_count` random indices into the group where // we'll put a matching byte. std::array group_indices; std::iota(group_indices.begin(), group_indices.end(), 0); std::shuffle(group_indices.begin(), group_indices.end(), gen); // Now cause the first match index to have the desired value. ssize_t match_index = *group_indices.begin(); uint8_t& match_b = group_bytes[match_index]; switch (Kind) { case BenchKind::Random: { // Already a random value, but we need to ensure it isn't one that // repeats elsewhere in the group. while (llvm::count(group_bytes, match_b) > 1) { match_b = absl::Uniform(gen) | MetadataGroup::PresentMask; } // Store this as the byte to search for in this group, but without the // present bit to simulate where we start when using a 7-bit tag // from a hash. bytes_storage[bm_index][g_index] = match_b & ~MetadataGroup::PresentMask; break; } case BenchKind::Empty: { match_b = MetadataGroup::Empty; break; } case BenchKind::Deleted: { match_b = MetadataGroup::Deleted; break; } } // Replicate the match byte in each of the other matching indices. for (ssize_t m_index : llvm::ArrayRef(group_indices) .drop_front() .take_front(match_count - 1)) { group_bytes[m_index] = match_b; } } // Now that the storage is set up, record these in our struct. bm.metadata = metadata_storage[bm_index]; if constexpr (Kind == BenchKind::Random) { bm.bytes = bytes_storage[bm_index]; } } return bm_storage; } template // NOLINTNEXTLINE(google-readability-casting): False positive clang-tidy bug. const auto bench_metadata = BuildBenchMetadata(); // Benchmark that simulates the dynamic execution pattern when we match exactly // one entry in the group, typically then using the index of the matching byte // to index into an element of a group of entries. But notably, the *first* // match is sufficient, and we never have to find the *next* match within the // group. template static void BM_LoadMatch(benchmark::State& s) { // NOLINTNEXTLINE(google-readability-casting): Same as on `bench_metadata`. BenchMetadata bm = bench_metadata[0]; // We want to make the index used by the next iteration of the benchmark have // a data dependency on the result of matching. A match produces an index into // the group of metadata. To consume this match in a way that is // representative of how it will be used in a hashtable (indexing into an // array of entries), while establishing that dependence, we keep a // group-sized array of the value `1` in memory that we can index into to // increment to the next step of the loop. We do have to hide the contents of // the loop from the optimizer by clobbering the memory. ssize_t all_ones[GroupSize]; for (ssize_t& n : all_ones) { n = 1; } benchmark::ClobberMemory(); // We don't want the optimizer to peel iterations off of this loop, so hide // the starting index. ssize_t i = 0; benchmark::DoNotOptimize(i); // This loop looks *really* attractive to unroll to the compiler. However, // that can easily overlap some of the memory operations and generally makes // it harder to analyze the exact operation sequence we care about. #pragma clang loop unroll(disable) for (auto _ : s) { auto g = GroupT::Load(bm.metadata.data(), i * GroupSize); typename GroupT::MatchIndex matches; if constexpr (Kind == BenchKind::Empty) { matches = g.MatchEmpty(); } else if constexpr (Kind == BenchKind::Deleted) { matches = g.MatchDeleted(); } else { static_assert(Kind == BenchKind::Random); matches = static_cast(g.Match(bm.bytes[i])); } // Despite not being a DCHECK, this is fine for benchmarking. In an actual // hashtable, we expect to have a test for empty of the match prior to using // it to index an array, and that test is expected to be strongly predicted. // That exactly matches how the `CARBON_CHECK` macro works, and so this // serves as both a good correctness test and replication of hashtable usage // of a match. CARBON_CHECK(matches); // Now do the data-dependent increment by indexing our "all ones" array. The // index into `all_ones` is analogous to the index into a group of hashtable // entries. i = (i + all_ones[matches.index()]) & (BenchSize - 1); } } BENCHMARK(BM_LoadMatch); BENCHMARK(BM_LoadMatch); BENCHMARK(BM_LoadMatch); #if CARBON_NEON_SIMD_SUPPORT || CARBON_X86_SIMD_SUPPORT BENCHMARK(BM_LoadMatch); BENCHMARK(BM_LoadMatch); BENCHMARK(BM_LoadMatch); BENCHMARK(BM_LoadMatch); BENCHMARK(BM_LoadMatch); BENCHMARK(BM_LoadMatch); #endif // Benchmark that measures the speed of a match that is only found after at // least one miss. Because the first match doesn't work, this covers // incrementing to the next match, with a number of increments taken from the // `Step` template parameter. template static void BM_LoadMatchMissSteps(benchmark::State& s) { static_assert(Steps > 0); static_assert(Steps <= GroupSize); // We pick the benchmark metadata at index `Steps - 1`, which will have // `Steps` matches within each group. BenchMetadata bm = bench_metadata[Steps - 1]; // We want to make the index used by the next iteration of the benchmark have // a data dependency on the result of matching. A match produces an index into // the group of metadata. To consume this match in a way that is // representative of how it will be used in a hashtable (indexing into an // array of entries), while establishing that dependence, we keep a // group-sized array of the value `1` in memory that we can index into to // increment to the next step of the loop. We do have to hide the contents of // the loop from the optimizer by clobbering the memory. ssize_t all_ones[GroupSize]; for (ssize_t& n : all_ones) { n = 1; } benchmark::ClobberMemory(); // We don't want the optimizer to peel iterations off of this loop, so hide // the starting index. ssize_t i = 0; benchmark::DoNotOptimize(i); // This loop looks *really* attractive to unroll to the compiler. However, // that can easily overlap some of the memory operations and generally makes // it harder to analyze the exact operation sequence we care about. #pragma clang loop unroll(disable) for (auto _ : s) { auto g = MetadataGroup::Load(bm.metadata.data(), i * GroupSize); auto matched_range = g.Match(bm.bytes[i]); // We don't use a `CARBON_CHECK` here as the loop below will test the range // to see if the loop should be skipped, replicating the test that we also // expect in hashtable usage. // We want to simulate the code sequence a hashtable would produce when // matching indices are "misses" in the hashtable, but only the aspects of // those that reflect on the specific *match* implementation's generated // code and performance. For each index in the match, we locate it in the // `matched_range`, extract it as an index, and use that to index a // group-sized array. We read memory from that array to increment `indices`, // establishing data dependencies on each match index. This loop will run // exactly `Steps` times. ssize_t indices = 0; for (ssize_t index : matched_range) { indices += all_ones[index]; } // We want to propagate the data dependencies accumulated into `indices` // into the next value of `i`, and we know exactly how many increments were // done in the loop, so subtract that constant and add one to arrive back at // an increment of 1. i = (i + (indices - Steps + 1)) & (BenchSize - 1); } } BENCHMARK(BM_LoadMatchMissSteps); BENCHMARK(BM_LoadMatchMissSteps); BENCHMARK(BM_LoadMatchMissSteps); BENCHMARK(BM_LoadMatchMissSteps); #if CARBON_USE_X86_SIMD_CONTROL_GROUP BENCHMARK(BM_LoadMatchMissSteps); BENCHMARK(BM_LoadMatchMissSteps); #endif } // namespace } // namespace Carbon::RawHashtable ================================================ FILE: common/raw_hashtable_test_helpers.h ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #ifndef CARBON_COMMON_RAW_HASHTABLE_TEST_HELPERS_H_ #define CARBON_COMMON_RAW_HASHTABLE_TEST_HELPERS_H_ #include #include "common/check.h" #include "common/hashing.h" #include "common/hashtable_key_context.h" #include "common/ostream.h" namespace Carbon::RawHashtable { // Non-trivial type for testing. struct TestData : Printable { int value; // NOLINTNEXTLINE: google-explicit-constructor TestData(int v) : value(v) { CARBON_CHECK(value >= 0); } ~TestData() { CARBON_CHECK(value >= 0); value = -1; } TestData(const TestData& other) : TestData(other.value) {} TestData(TestData&& other) noexcept : TestData(other.value) { other.value = 0; } auto Print(llvm::raw_ostream& out) const -> void { out << value; } friend auto operator==(TestData lhs, TestData rhs) -> bool { return lhs.value == rhs.value; } friend auto operator<=>(TestData lhs, TestData rhs) -> std::strong_ordering { return lhs.value <=> rhs.value; } friend auto CarbonHashValue(TestData data, uint64_t seed) -> HashCode { return Carbon::HashValue(data.value, seed); } }; static_assert(std::is_copy_constructible_v); // Non-trivial type for testing. struct MoveOnlyTestData : Printable { int value; // NOLINTNEXTLINE: google-explicit-constructor MoveOnlyTestData(int v) : value(v) { CARBON_CHECK(value >= 0); } ~MoveOnlyTestData() { CARBON_CHECK(value >= 0); value = -1; } MoveOnlyTestData(MoveOnlyTestData&& other) noexcept : MoveOnlyTestData(other.value) { other.value = 0; } auto operator=(MoveOnlyTestData&& other) noexcept -> MoveOnlyTestData& { value = other.value; other.value = 0; return *this; } auto Print(llvm::raw_ostream& out) const -> void { out << value; } friend auto operator==(const MoveOnlyTestData& lhs, const MoveOnlyTestData& rhs) -> bool { return lhs.value == rhs.value; } friend auto operator<=>(const MoveOnlyTestData& lhs, const MoveOnlyTestData& rhs) -> std::strong_ordering { return lhs.value <=> rhs.value; } friend auto CarbonHashValue(const MoveOnlyTestData& data, uint64_t seed) -> HashCode { return Carbon::HashValue(data.value, seed); } }; static_assert(!std::is_copy_constructible_v); static_assert(std::is_move_constructible_v); // Test stateless key context that produces different hashes from normal. // Changing the hash values should result in test failures if the context ever // fails to be used. struct TestKeyContext : DefaultKeyContext { template auto HashKey(const KeyT& key, uint64_t seed) const -> HashCode { Hasher hash(seed); // Inject some other data to the hash. hash.HashRaw(42); hash.HashRaw(HashValue(key)); return static_cast(hash); } }; // Hostile fixed hashing key context used for stress testing. Allows control // over which parts of the hash will be forced to collide, and the values they // are coerced to. Note that this relies on implementation details and internals // of `HashCode`. template struct FixedHashKeyContext : DefaultKeyContext { template auto HashKey(const KeyT& key, uint64_t seed) const -> HashCode { HashCode original_hash = HashValue(key, seed); auto raw_hash = static_cast(original_hash); constexpr uint64_t TagMask = (1U << TagBits) - 1; if (FixIndexBits) { raw_hash &= TagMask; raw_hash |= FixedVal << TagBits; CARBON_DCHECK(HashCode(raw_hash).ExtractIndexAndTag().first == (FixedVal & (~static_cast(0) >> TagBits))); } if (FixTagBits) { raw_hash &= ~TagMask; raw_hash |= FixedVal & TagMask; CARBON_DCHECK(HashCode(raw_hash).ExtractIndexAndTag().second == (FixedVal & TagMask)); } return HashCode(raw_hash); } }; template class IndexKeyContext : public TranslatingKeyContext> { using Base = TranslatingKeyContext; public: explicit IndexKeyContext(llvm::ArrayRef array) : array_(array) {} auto TranslateKey(ssize_t index) const -> const T& { return array_[index]; } // Override the CRTP approach when we have two indices as we can optimize that // approach. using Base::KeyEq; auto KeyEq(ssize_t lhs_index, ssize_t rhs_index) const -> bool { // No need to compare the elements, if the indices are equal, the values // must be. return lhs_index == rhs_index; } private: llvm::ArrayRef array_; }; } // namespace Carbon::RawHashtable #endif // CARBON_COMMON_RAW_HASHTABLE_TEST_HELPERS_H_ ================================================ FILE: common/raw_string_ostream.h ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #ifndef CARBON_COMMON_RAW_STRING_OSTREAM_H_ #define CARBON_COMMON_RAW_STRING_OSTREAM_H_ #include "common/check.h" #include "common/ostream.h" namespace Carbon { // Implements streaming output with an underlying string. The string must always // be taken prior to destruction. // // Versus `llvm::raw_string_ostream`: // - Owns the underlying string. // - Supports `pwrite` for compatibility with more stream uses in tests. class RawStringOstream : public llvm::raw_pwrite_stream { public: explicit RawStringOstream() : llvm::raw_pwrite_stream(/*Unbuffered=*/true) {} ~RawStringOstream() override { CARBON_CHECK(str_.empty(), "Expected to be emptied by TakeStr, have: {0}", str_); } // Returns the streamed contents, clearing the stream back to empty. auto TakeStr() -> std::string { std::string result = std::move(str_); clear(); return result; } // Clears the buffer, which can be helpful when destructing without // necessarily calling `TakeStr()`. auto clear() -> void { str_.clear(); } auto empty() -> bool { return str_.empty(); } auto size() -> size_t { return str_.size(); } private: auto current_pos() const -> uint64_t override { return str_.size(); } auto pwrite_impl(const char* ptr, size_t size, uint64_t offset) -> void override { str_.replace(offset, size, ptr, size); } auto write_impl(const char* ptr, size_t size) -> void override { str_.append(ptr, size); } auto reserveExtraSpace(uint64_t extra_size) -> void override { str_.reserve(str_.size() + extra_size); } // The actual buffer. std::string str_; }; } // namespace Carbon #endif // CARBON_COMMON_RAW_STRING_OSTREAM_H_ ================================================ FILE: common/raw_string_ostream_test.cpp ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #include "common/raw_string_ostream.h" #include #include namespace Carbon::Testing { namespace { using ::testing::HasSubstr; using ::testing::StrEq; TEST(RawStringOstreamTest, Basics) { RawStringOstream os; os << "test 1"; EXPECT_THAT(os.TakeStr(), StrEq("test 1")); os << "test" << " " << "2"; EXPECT_THAT(os.TakeStr(), StrEq("test 2")); os << "test"; os << " "; os << "3"; EXPECT_THAT(os.TakeStr(), StrEq("test 3")); } TEST(RawStringOstreamTest, MultipleStreams) { RawStringOstream os1; RawStringOstream os2; os1 << "test "; os2 << "test stream 2"; os1 << "stream 1"; EXPECT_THAT(os1.TakeStr(), StrEq("test stream 1")); EXPECT_THAT(os2.TakeStr(), StrEq("test stream 2")); } TEST(RawStringOstreamTest, MultipleLines) { RawStringOstream os; os << "test line 1\n"; os << "test line 2\n"; os << "test line 3\n"; EXPECT_THAT(os.TakeStr(), StrEq("test line 1\ntest line 2\ntest line 3\n")); } TEST(RawStringOstreamTest, Substring) { RawStringOstream os; os << "test line 1\n"; os << "test line 2\n"; os << "test line 3\n"; EXPECT_THAT(os.TakeStr(), HasSubstr("test line 2")); } TEST(RawStringOstreamTest, Pwrite) { RawStringOstream os; os << "test line 1\n"; os.pwrite("splat", 5, 1); os << "test line 2\n"; EXPECT_THAT(os.TakeStr(), HasSubstr("tsplatine 1\ntest line 2\n")); } } // namespace } // namespace Carbon::Testing ================================================ FILE: common/set.h ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #ifndef CARBON_COMMON_SET_H_ #define CARBON_COMMON_SET_H_ #include #include #include "common/check.h" #include "common/hashtable_key_context.h" #include "common/raw_hashtable.h" #include "llvm/Support/Compiler.h" namespace Carbon { // Forward declarations to resolve cyclic references. template class SetView; template class SetBase; template class Set; // A read-only view type for a set of keys. // // This view is a cheap-to-copy type that should be passed by value, but // provides view or read-only reference semantics to the underlying set data // structure. // // This should always be preferred to a `const`-ref parameter for the `SetBase` // or `Set` type as it provides more flexibility and a cleaner API. By default // a `SetView` provides no more immutability than a `const Set`: elements // can't be added or removed, but they can be mutated, and the user is // responsible for avoiding mutations that affect the hash value or equality // comparison. However, a `SetView` can be converted to a `SetView`, // which prevents mutating the elements. As with any other view type, `const` // on the `SetView` itself is "shallow": it prevents rebinding the `SetView` to // a different underlying set, but doesn't affect mutability of the underlying // set. // // A specific `KeyContextT` type can optionally be provided to configure how // keys will be hashed and compared. The default is `DefaultKeyContext` which is // stateless and will hash using `Carbon::HashValue` and compare using // `operator==`. Every method accepting a lookup key or operating on the keys in // the table will also accept an instance of this type. For stateless context // types, including the default, an instance will be default constructed if not // provided to these methods. However, stateful contexts should be constructed // and passed in explicitly. The context type should be small and reasonable to // pass by value, often a wrapper or pointer to the relevant context needed for // hashing and comparing keys. For more details about the key context, see // `hashtable_key_context.h`. template class SetView : RawHashtable::ViewImpl { using ImplT = RawHashtable::ViewImpl; public: using KeyT = typename ImplT::KeyT; using KeyContextT = typename ImplT::KeyContextT; using MetricsT = typename ImplT::MetricsT; // This type represents the result of lookup operations. It encodes whether // the lookup was a success as well as accessors for the key. class LookupResult { public: LookupResult() = default; explicit LookupResult(KeyT& key) : key_(&key) {} explicit operator bool() const { return key_ != nullptr; } auto key() const -> KeyT& { return *key_; } private: KeyT* key_ = nullptr; }; // Enable implicit conversions that add `const`-ness to the key type. explicit(false) SetView(const SetView, KeyContextT>& other_view) requires(!std::same_as>) : ImplT(other_view) {} // Tests whether a key is present in the set. template auto Contains(LookupKeyT lookup_key, KeyContextT key_context = KeyContextT()) const -> bool; // Lookup a key in the set. template auto Lookup(LookupKeyT lookup_key, KeyContextT key_context = KeyContextT()) const -> LookupResult; // Run the provided callback for every key in the set. template auto ForEach(CallbackT callback) const -> void requires(std::invocable); // This routine is relatively inefficient and only intended for use in // benchmarking or logging of performance anomalies. The specific metrics // returned have no specific guarantees beyond being informative in // benchmarks. auto ComputeMetrics(KeyContextT key_context = KeyContextT()) -> MetricsT { return ImplT::ComputeMetricsImpl(key_context); } private: template friend class Set; friend class SetBase; friend class SetView; using EntryT = typename ImplT::EntryT; SetView() = default; explicit(false) SetView(ImplT base) : ImplT(base) {} SetView(ssize_t size, RawHashtable::Storage* storage) : ImplT(size, storage) {} }; // A base class for a `Set` type that remains mutable while type-erasing the // `SmallSize` (SSO) template parameter. // // Note that `SetBase` has "shallow" const semantics: a `const SetBase&` // can't be used to mutate the set data structure itself (e.g. by changing // the number of elements), but it can be used to mutate the `T` elements it // contains. The user is responsible for avoiding mutations that would change // the hash value or equality of an element. A `SetView` can be used // to provide read-only access to the elements of a `SetBase`. // // A pointer or reference to this type is the preferred way to pass a mutable // handle to a `Set` type across API boundaries as it avoids encoding specific // SSO sizing information while providing a near-complete mutable API. template class SetBase : protected RawHashtable::BaseImpl { protected: using ImplT = RawHashtable::BaseImpl; public: using KeyT = typename ImplT::KeyT; using KeyContextT = typename ImplT::KeyContextT; using ViewT = SetView; using LookupResult = typename ViewT::LookupResult; using MetricsT = typename ImplT::MetricsT; // The result type for insertion operations both indicates whether an insert // was needed (as opposed to the key already being in the set), and provides // access to the key. class InsertResult { public: InsertResult() = default; explicit InsertResult(bool inserted, KeyT& key) : key_(&key), inserted_(inserted) {} auto is_inserted() const -> bool { return inserted_; } auto key() const -> KeyT& { return *key_; } private: KeyT* key_; bool inserted_; }; // Implicitly convertible to the relevant view type. // // NOLINTNEXTLINE(google-explicit-constructor): Designed to implicitly decay. explicit(false) operator ViewT() const { return this->view_impl(); } // We can't chain the above conversion with the conversions on `ViewT` to add // const, so explicitly support adding const to produce a view here. // // NOLINTNEXTLINE(google-explicit-constructor): Designed to implicitly decay. explicit(false) operator SetView() const { return ViewT(*this); } // Convenience forwarder to the view type. template auto Contains(LookupKeyT lookup_key, KeyContextT key_context = KeyContextT()) const -> bool { return ViewT(*this).Contains(lookup_key, key_context); } // Convenience forwarder to the view type. template auto Lookup(LookupKeyT lookup_key, KeyContextT key_context = KeyContextT()) const -> LookupResult { return ViewT(*this).Lookup(lookup_key, key_context); } // Convenience forwarder to the view type. template auto ForEach(CallbackT callback) const -> void requires(std::invocable) { return ViewT(*this).ForEach(callback); } // Convenience forwarder to the view type. auto ComputeMetrics(KeyContextT key_context = KeyContextT()) const -> MetricsT { return ViewT(*this).ComputeMetrics(key_context); } // Insert a key into the set. If the key is already present, no insertion is // performed and that present key is available in the result. Otherwise a new // key is inserted and constructed from the argument and available in the // result. template auto Insert(LookupKeyT lookup_key, KeyContextT key_context = KeyContextT()) -> InsertResult; // Insert a key into the map and call the provided callback if necessary to // produce a new key when no existing value is found. // // Example: `m.Insert(key_equivalent, [] { return real_key; });` // // The point of this function is when the lookup key is _different_from the // stored key. However, we don't restrict it in case that blocks generic // usage. template auto Insert(LookupKeyT lookup_key, KeyCallbackT key_cb, KeyContextT key_context = KeyContextT()) -> InsertResult requires( !std::same_as && std::convertible_to()()), KeyT>); // Insert a key into the set and call the provided callback to allow in-place // construction of the key if not already present. The lookup key is passed // through to the callback so it needn't be captured and can be kept in a // register argument throughout. // // Example: // ```cpp // m.Insert("widget", [](MyStringViewType lookup_key, void* key_storage) { // new (key_storage) MyStringType(lookup_key); // }); // ``` template auto Insert(LookupKeyT lookup_key, InsertCallbackT insert_cb, KeyContextT key_context = KeyContextT()) -> InsertResult requires std::invocable; // Grow the set to a specific allocation size. // // This will grow the set's hashtable if necessary for it to have an // allocation size of `target_alloc_size` which must be a power of two. Note // that this will not allow that many keys to be inserted, but a smaller // number based on the maximum load factor. If a specific number of insertions // need to be achieved without triggering growth, use the `GrowForInsertCount` // method. auto GrowToAllocSize(ssize_t target_alloc_size, KeyContextT key_context = KeyContextT()) -> void; // Grow the set sufficiently to allow inserting the specified number of keys. auto GrowForInsertCount(ssize_t count, KeyContextT key_context = KeyContextT()) -> void; // Erase a key from the set. template auto Erase(LookupKeyT lookup_key, KeyContextT key_context = KeyContextT()) -> bool; // Clear all key/value pairs from the set but leave the underlying hashtable // allocated and in place. auto Clear() -> void; protected: using ImplT::ImplT; }; // A data structure for a set of keys. // // This set supports small size optimization (or "SSO"). The provided // `SmallSize` type parameter indicates the size of an embedded buffer for // storing sets small enough to fit. The default is zero, which always allocates // a heap buffer on construction. When non-zero, must be a multiple of the // `MaxGroupSize` which is currently 16. The library will check that the size is // valid and provide an error at compile time if not. We don't automatically // select the next multiple or otherwise fit the size to the constraints to make // it clear in the code how much memory is used by the SSO buffer. // // This data structure optimizes heavily for small key types that are cheap to // move and even copy. Using types with large keys or expensive to copy keys may // create surprising performance bottlenecks. A `std::string` key should be fine // with generally small strings, but if some or many strings are large heap // allocations the performance of hashtable routines may be unacceptably bad and // another data structure or key design is likely preferable. // // Note that `Set`, like `SetBase`, has "shallow" const semantics. Note also // that this type should typically not appear on API boundaries; either // `SetBase` or `SetView` should be used instead. template class Set : public RawHashtable::TableImpl, SmallSize> { using BaseT = SetBase; using ImplT = RawHashtable::TableImpl; public: using KeyT = typename BaseT::KeyT; Set() = default; Set(const Set& arg) = default; Set(Set&& arg) noexcept = default; auto operator=(const Set& arg) -> Set& = default; auto operator=(Set&& arg) noexcept -> Set& = default; // Reset the entire state of the hashtable to as it was when constructed, // throwing away any intervening allocations. auto Reset() -> void; }; template template auto SetView::Contains( LookupKeyT lookup_key, KeyContextT key_context) const -> bool { return this->LookupEntry(lookup_key, key_context) != nullptr; } template template auto SetView::Lookup(LookupKeyT lookup_key, KeyContextT key_context) const -> LookupResult { EntryT* entry = this->LookupEntry(lookup_key, key_context); if (!entry) { return LookupResult(); } return LookupResult(entry->key()); } template template auto SetView::ForEach(CallbackT callback) const -> void requires(std::invocable) { this->ForEachEntry([callback](EntryT& entry) { callback(entry.key()); }, [](auto...) {}); } template template auto SetBase::Insert(LookupKeyT lookup_key, KeyContextT key_context) -> InsertResult { return Insert( lookup_key, [](LookupKeyT lookup_key, void* key_storage) { new (key_storage) KeyT(std::move(lookup_key)); }, key_context); } template template auto SetBase::Insert(LookupKeyT lookup_key, KeyCallbackT key_cb, KeyContextT key_context) -> InsertResult requires(!std::same_as && std::convertible_to()()), KeyT>) { return Insert( lookup_key, [&key_cb](LookupKeyT /*lookup_key*/, void* key_storage) { new (key_storage) KeyT(key_cb()); }, key_context); } template template auto SetBase::Insert(LookupKeyT lookup_key, InsertCallbackT insert_cb, KeyContextT key_context) -> InsertResult requires std::invocable { auto [entry, inserted] = this->InsertImpl(lookup_key, key_context); CARBON_DCHECK(entry, "Should always result in a valid index."); if (LLVM_LIKELY(!inserted)) { return InsertResult(false, entry->key()); } insert_cb(lookup_key, static_cast(&entry->key_storage)); return InsertResult(true, entry->key()); } template auto SetBase::GrowToAllocSize( ssize_t target_alloc_size, KeyContextT key_context) -> void { this->GrowToAllocSizeImpl(target_alloc_size, key_context); } template auto SetBase::GrowForInsertCount( ssize_t count, KeyContextT key_context) -> void { this->GrowForInsertCountImpl(count, key_context); } template template auto SetBase::Erase(LookupKeyT lookup_key, KeyContextT key_context) -> bool { return this->EraseImpl(lookup_key, key_context); } template auto SetBase::Clear() -> void { this->ClearImpl(); } template auto Set::Reset() -> void { this->ResetImpl(); } } // namespace Carbon #endif // CARBON_COMMON_SET_H_ ================================================ FILE: common/set_benchmark.cpp ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #include #include #include "absl/container/flat_hash_set.h" #include "common/raw_hashtable_benchmark_helpers.h" #include "common/set.h" #include "llvm/ADT/DenseSet.h" namespace Carbon { namespace { using RawHashtable::CarbonHashDI; using RawHashtable::GetKeysAndHitKeys; using RawHashtable::GetKeysAndMissKeys; using RawHashtable::HitArgs; using RawHashtable::ReportTableMetrics; using RawHashtable::SizeArgs; using RawHashtable::ValueToBool; template struct IsCarbonSetImpl : std::false_type {}; template struct IsCarbonSetImpl> : std::true_type {}; template static constexpr bool IsCarbonSet = IsCarbonSetImpl::value; // A wrapper around various set types that we specialize to implement a common // API used in the benchmarks for various different map data structures that // support different APIs. The primary template assumes a roughly // `std::unordered_set` API design, and types with a different API design are // supported through specializations. template struct SetWrapperImpl { using KeyT = typename SetT::key_type; SetT s; auto BenchContains(KeyT k) -> bool { return s.find(k) != s.end(); } auto BenchLookup(KeyT k) -> bool { auto it = s.find(k); if (it == s.end()) { return false; } // We expect keys to always convert to `true` so directly return that here. return ValueToBool(*it); } auto BenchInsert(KeyT k) -> bool { auto result = s.insert(k); return result.second; } auto BenchErase(KeyT k) -> bool { return s.erase(k) != 0; } }; // Explicit (partial) specialization for the Carbon map type that uses its // different API design. template struct SetWrapperImpl> { using SetT = Set; using KeyT = KT; SetT s; auto BenchContains(KeyT k) -> bool { return s.Contains(k); } auto BenchLookup(KeyT k) -> bool { auto result = s.Lookup(k); if (!result) { return false; } return ValueToBool(result.key()); } auto BenchInsert(KeyT k) -> bool { auto result = s.Insert(k); return result.is_inserted(); } auto BenchErase(KeyT k) -> bool { return s.Erase(k); } }; // Provide a way to override the Carbon Set specific benchmark runs with another // hashtable implementation. When building, you can use one of these enum names // in a macro define such as `-DCARBON_SET_BENCH_OVERRIDE=Name` in order to // trigger a specific override for the `Set` type benchmarks. This is used to // get before/after runs that compare the performance of Carbon's Set versus // other implementations. enum class SetOverride : uint8_t { Abseil, LLVM, LLVMAndCarbonHash, }; template struct SetWrapperOverride : SetWrapperImpl {}; template struct SetWrapperOverride, SetOverride::Abseil> : SetWrapperImpl> {}; template struct SetWrapperOverride, SetOverride::LLVM> : SetWrapperImpl> {}; template struct SetWrapperOverride, SetOverride::LLVMAndCarbonHash> : SetWrapperImpl>> {}; #ifndef CARBON_SET_BENCH_OVERRIDE template using SetWrapper = SetWrapperImpl; #else template using SetWrapper = SetWrapperOverride; #endif // NOLINTBEGIN(bugprone-macro-parentheses): Parentheses are incorrect here. #define MAP_BENCHMARK_ONE_OP_SIZE(NAME, APPLY, KT) \ BENCHMARK(NAME>)->Apply(APPLY); \ BENCHMARK(NAME>)->Apply(APPLY); \ BENCHMARK(NAME>)->Apply(APPLY); \ BENCHMARK(NAME>>)->Apply(APPLY) // NOLINTEND(bugprone-macro-parentheses) #define MAP_BENCHMARK_ONE_OP(NAME, APPLY) \ MAP_BENCHMARK_ONE_OP_SIZE(NAME, APPLY, int); \ MAP_BENCHMARK_ONE_OP_SIZE(NAME, APPLY, int*); \ MAP_BENCHMARK_ONE_OP_SIZE(NAME, APPLY, llvm::StringRef) // Benchmark the "latency" of testing for a key in a set. This always tests with // a key that is found. // // However, because the key is always found and because the test ultimately // involves conditional control flow that can be predicted, we expect modern // CPUs to perfectly predict the control flow here and turn the measurement from // one iteration to the next into a throughput measurement rather than a real // latency measurement. // // However, this does represent a particularly common way in which a set data // structure is accessed. The numbers should just be carefully interpreted in // the context of being more a reflection of reciprocal throughput than actual // latency. See the `Lookup` benchmarks for a genuine latency measure with its // own caveats. // // However, this does still show some interesting caching effects when querying // large fractions of large tables, and can give a sense of the inescapable // magnitude of these effects even when there is a great deal of prediction and // speculative execution to hide memory access latency. template static void BM_SetContainsHitPtr(benchmark::State& state) { using SetWrapperT = SetWrapper; using KT = typename SetWrapperT::KeyT; SetWrapperT s; auto [keys, lookup_keys] = GetKeysAndHitKeys(state.range(0), state.range(1)); for (auto k : keys) { s.BenchInsert(k); } ssize_t lookup_keys_size = lookup_keys.size(); while (state.KeepRunningBatch(lookup_keys_size)) { for (ssize_t i = 0; i < lookup_keys_size;) { // We block optimizing `i` as that has proven both more effective at // blocking the loop from being optimized away and avoiding disruption of // the generated code that we're benchmarking. benchmark::DoNotOptimize(i); bool result = s.BenchContains(lookup_keys[i]); CARBON_DCHECK(result); // We use the lookup success to step through keys, establishing a // dependency between each lookup. This doesn't fully allow us to measure // latency rather than throughput, as noted above. i += static_cast(result); } } } MAP_BENCHMARK_ONE_OP(BM_SetContainsHitPtr, HitArgs); // Benchmark the "latency" (but more likely the reciprocal throughput, see // comment above) of testing for a key in the set that is *not* present. template static void BM_SetContainsMissPtr(benchmark::State& state) { using SetWrapperT = SetWrapper; using KT = typename SetWrapperT::KeyT; SetWrapperT s; auto [keys, lookup_keys] = GetKeysAndMissKeys(state.range(0)); for (auto k : keys) { s.BenchInsert(k); } ssize_t lookup_keys_size = lookup_keys.size(); while (state.KeepRunningBatch(lookup_keys_size)) { for (ssize_t i = 0; i < lookup_keys_size;) { benchmark::DoNotOptimize(i); bool result = s.BenchContains(lookup_keys[i]); CARBON_DCHECK(!result); i += static_cast(!result); } } } MAP_BENCHMARK_ONE_OP(BM_SetContainsMissPtr, SizeArgs); // A somewhat contrived latency test for the lookup code path. // // While lookups into a set are often (but not always) simply used to influence // control flow, that style of access produces difficult to evaluate benchmark // results (see the comments on the `Contains` benchmarks above). // // So here we actually access the key in the set and convert that key's value to // a boolean on the critical path of each iteration. This lets us have a genuine // latency benchmark of looking up a key in the set, at the expense of being // somewhat contrived. That said, for usage where the key object is queried or // operated on in some way once looked up in the set, this will be fairly // representative of the latency cost from the data structure. template static void BM_SetLookupHitPtr(benchmark::State& state) { using SetWrapperT = SetWrapper; using KT = typename SetWrapperT::KeyT; SetWrapperT s; auto [keys, lookup_keys] = GetKeysAndHitKeys(state.range(0), state.range(1)); for (auto k : keys) { s.BenchInsert(k); } ssize_t lookup_keys_size = lookup_keys.size(); while (state.KeepRunningBatch(lookup_keys_size)) { for (ssize_t i = 0; i < lookup_keys_size;) { benchmark::DoNotOptimize(i); bool result = s.BenchLookup(lookup_keys[i]); CARBON_DCHECK(result); i += static_cast(result); } } } MAP_BENCHMARK_ONE_OP(BM_SetLookupHitPtr, HitArgs); // First erase and then insert the key. The code path will always be the same // here and so we expect this to largely be a throughput benchmark because of // branch prediction and speculative execution. // // We don't expect erase followed by insertion to be a common user code // sequence, but we don't have a good way of benchmarking either erase or insert // in isolation -- each would change the size of the table and thus the next // iteration's benchmark. And if we try to correct the table size outside of the // timed region, we end up trying to exclude too fine grained of a region from // timers to get good measurement data. // // Our solution is to benchmark both erase and insertion back to back. We can // then get a good profile of the code sequence of each, and at least measure // the sum cost of these reliably. Careful profiling can help attribute that // cost between erase and insert in order to understand which of the two // operations is contributing most to any performance artifacts observed. template static void BM_SetEraseInsertHitPtr(benchmark::State& state) { using SetWrapperT = SetWrapper; using KT = typename SetWrapperT::KeyT; SetWrapperT s; auto [keys, lookup_keys] = GetKeysAndHitKeys(state.range(0), state.range(1)); for (auto k : keys) { s.BenchInsert(k); } ssize_t lookup_keys_size = lookup_keys.size(); while (state.KeepRunningBatch(lookup_keys_size)) { for (ssize_t i = 0; i < lookup_keys_size;) { benchmark::DoNotOptimize(i); s.BenchErase(lookup_keys[i]); benchmark::ClobberMemory(); bool inserted = s.BenchInsert(lookup_keys[i]); CARBON_DCHECK(inserted); i += static_cast(inserted); } } } MAP_BENCHMARK_ONE_OP(BM_SetEraseInsertHitPtr, HitArgs); // NOLINTBEGIN(bugprone-macro-parentheses): Parentheses are incorrect here. #define MAP_BENCHMARK_OP_SEQ_SIZE(NAME, KT) \ BENCHMARK(NAME>)->Apply(SizeArgs); \ BENCHMARK(NAME>)->Apply(SizeArgs); \ BENCHMARK(NAME>)->Apply(SizeArgs); \ BENCHMARK(NAME>>)->Apply(SizeArgs) // NOLINTEND(bugprone-macro-parentheses) #define MAP_BENCHMARK_OP_SEQ(NAME) \ MAP_BENCHMARK_OP_SEQ_SIZE(NAME, int); \ MAP_BENCHMARK_OP_SEQ_SIZE(NAME, int*); \ MAP_BENCHMARK_OP_SEQ_SIZE(NAME, llvm::StringRef) // This is an interesting, somewhat specialized benchmark that measures the cost // of inserting a sequence of keys into a set up to some size and then inserting // a colliding key and throwing away the set. // // This is an especially important usage pattern for sets as a large number of // algorithms essentially look like this, such as collision detection, cycle // detection, de-duplication, etc. // // It also covers both the insert-into-an-empty-slot code path that isn't // covered elsewhere, and the code path for growing a table to a larger size. // // This is the second most important aspect of expected set usage after testing // for presence. It also nicely lends itself to a single benchmark that covers // the total cost of this usage pattern. // // Because this benchmark operates on whole sets, we also compute the number of // probed keys for Carbon's set as that is both a general reflection of the // efficacy of the underlying hash function, and a direct factor that drives the // cost of these operations. template static void BM_SetInsertSeq(benchmark::State& state) { using SetWrapperT = SetWrapper; using KT = typename SetWrapperT::KeyT; constexpr ssize_t LookupKeysSize = 1 << 8; auto [keys, lookup_keys] = GetKeysAndHitKeys(state.range(0), LookupKeysSize); // Now build a large shuffled set of keys (with duplicates) we'll use at the // end. ssize_t i = 0; for (auto _ : state) { benchmark::DoNotOptimize(i); SetWrapperT s; for (auto k : keys) { bool inserted = s.BenchInsert(k); CARBON_DCHECK(inserted, "Must be a successful insert!"); } // Now insert a final random repeated key. bool inserted = s.BenchInsert(lookup_keys[i]); CARBON_DCHECK(!inserted, "Must already be in the map!"); // Rotate through the shuffled keys. i = (i + static_cast(!inserted)) & (LookupKeysSize - 1); } // It can be easier in some cases to think of this as a key-throughput rate of // insertion rather than the latency of inserting N keys, so construct the // rate counter as well. state.counters["KeyRate"] = benchmark::Counter( keys.size(), benchmark::Counter::kIsIterationInvariantRate); // Report some extra statistics about the Carbon type. if constexpr (IsCarbonSet) { // Re-build a set outside of the timing loop to look at the statistics // rather than the timing. SetT s; for (auto k : keys) { bool inserted = s.Insert(k).is_inserted(); CARBON_DCHECK(inserted, "Must be a successful insert!"); } ReportTableMetrics(s, state); // Uncomment this call to print out statistics about the index-collisions // among these keys for debugging: // // RawHashtable::DumpHashStatistics(raw_keys); } } MAP_BENCHMARK_OP_SEQ(BM_SetInsertSeq); } // namespace } // namespace Carbon ================================================ FILE: common/set_test.cpp ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #include "common/set.h" #include #include #include #include #include #include "common/raw_hashtable_test_helpers.h" namespace Carbon { namespace { using RawHashtable::IndexKeyContext; using RawHashtable::MoveOnlyTestData; using RawHashtable::TestData; using ::testing::UnorderedElementsAreArray; template auto ExpectSetElementsAre(SetT&& s, MatcherRangeT element_matchers) -> void { // Collect the elements into a container. using KeyT = typename std::remove_reference::type::KeyT; std::vector> entries; s.ForEach([&entries](KeyT& k) { entries.push_back(std::ref(k)); }); // Use the GoogleMock unordered container matcher to validate and show errors // on wrong elements. EXPECT_THAT(entries, UnorderedElementsAreArray(element_matchers)); } // Allow directly using an initializer list. template auto ExpectSetElementsAre(SetT&& s, std::initializer_list element_matchers) -> void { std::vector element_matchers_storage = element_matchers; ExpectSetElementsAre(s, element_matchers_storage); } template auto MakeElements(RangeT&& range, RangeTs&&... ranges) { std::vector elements; auto add_range = [&elements](RangeT&& r) { for (const auto&& e : r) { elements.push_back(e); } }; add_range(std::forward(range)); (add_range(std::forward(ranges)), ...); return elements; } template class SetTest : public ::testing::Test {}; template class MoveOnlySetTest : public ::testing::Test {}; using Types = ::testing::Types, Set, Set, Set, Set>; TYPED_TEST_SUITE(SetTest, Types); using MoveOnlyTypes = ::testing::Types, Set, Set>; TYPED_TEST_SUITE(MoveOnlySetTest, MoveOnlyTypes); TYPED_TEST(SetTest, Basic) { using SetT = TypeParam; SetT s; EXPECT_FALSE(s.Contains(42)); EXPECT_TRUE(s.Insert(1).is_inserted()); EXPECT_TRUE(s.Contains(1)); auto result = s.Lookup(1); EXPECT_TRUE(result); EXPECT_EQ(1, result.key()); auto i_result = s.Insert(1); EXPECT_FALSE(i_result.is_inserted()); EXPECT_TRUE(s.Contains(1)); // Verify all the elements. ExpectSetElementsAre(s, {1}); // Fill up a bunch to ensure we trigger growth a few times. for (int i : llvm::seq(2, 512)) { SCOPED_TRACE(llvm::formatv("Key: {0}", i).str()); EXPECT_TRUE(s.Insert(i).is_inserted()); } for (int i : llvm::seq(1, 512)) { SCOPED_TRACE(llvm::formatv("Key: {0}", i).str()); EXPECT_TRUE(s.Contains(i)); EXPECT_FALSE(s.Insert(i).is_inserted()); } EXPECT_FALSE(s.Contains(513)); // Verify all the elements. ExpectSetElementsAre(s, MakeElements(llvm::seq(1, 512))); } TYPED_TEST(SetTest, FactoryApi) { using SetT = TypeParam; SetT s; EXPECT_TRUE(s.Insert(1, [](int k, void* key_storage) { return new (key_storage) int(k); }).is_inserted()); ASSERT_TRUE(s.Contains(1)); // Reinsertion doesn't invoke the callback. EXPECT_FALSE(s.Insert(1, [](int, void*) -> int* { llvm_unreachable("Should never be called!"); }).is_inserted()); } TYPED_TEST(SetTest, Copy) { using SetT = TypeParam; SetT s; // Make sure we exceed the small size for some of the set types, but not all // of them, so we cover all the combinations of copying between small and // large. for (int i : llvm::seq(1, 24)) { SCOPED_TRACE(llvm::formatv("Key: {0}", i).str()); ASSERT_TRUE(s.Insert(i).is_inserted()); } SetT other_s1 = s; ExpectSetElementsAre(other_s1, MakeElements(llvm::seq(1, 24))); // Add some more elements to the original. for (int i : llvm::seq(24, 32)) { SCOPED_TRACE(llvm::formatv("Key: {0}", i).str()); ASSERT_TRUE(s.Insert(i).is_inserted()); } // The first copy doesn't change. ExpectSetElementsAre(other_s1, MakeElements(llvm::seq(1, 24))); // A new copy does. SetT other_s2 = s; ExpectSetElementsAre(other_s2, MakeElements(llvm::seq(1, 32))); // Copy-assign updates. other_s1 = s; ExpectSetElementsAre(other_s1, MakeElements(llvm::seq(1, 32))); // Self-assign is a no-op. other_s1 = const_cast(other_s1); ExpectSetElementsAre(other_s1, MakeElements(llvm::seq(1, 32))); // But mutating original still doesn't change copies. for (int i : llvm::seq(32, 48)) { SCOPED_TRACE(llvm::formatv("Key: {0}", i).str()); ASSERT_TRUE(s.Insert(i).is_inserted()); } ExpectSetElementsAre(other_s1, MakeElements(llvm::seq(1, 32))); ExpectSetElementsAre(other_s2, MakeElements(llvm::seq(1, 32))); } TYPED_TEST(SetTest, Move) { using SetT = TypeParam; SetT s; // Make sure we exceed the small size for some of the set types, but not all // of them, so we cover all the combinations of copying between small and // large. for (int i : llvm::seq(1, 24)) { SCOPED_TRACE(llvm::formatv("Key: {0}", i).str()); EXPECT_TRUE(s.Insert(i).is_inserted()); } SetT other_s1 = std::move(s); ExpectSetElementsAre(other_s1, MakeElements(llvm::seq(1, 24))); // Add some more elements. for (int i : llvm::seq(24, 32)) { SCOPED_TRACE(llvm::formatv("Key: {0}", i).str()); ASSERT_TRUE(other_s1.Insert(i).is_inserted()); } ExpectSetElementsAre(other_s1, MakeElements(llvm::seq(1, 32))); // Move back over a moved-from. s = std::move(other_s1); ExpectSetElementsAre(s, MakeElements(llvm::seq(1, 32))); // Copy over moved-from state also works. other_s1 = s; ExpectSetElementsAre(other_s1, MakeElements(llvm::seq(1, 32))); // Now add still more elements. for (int i : llvm::seq(32, 48)) { SCOPED_TRACE(llvm::formatv("Key: {0}", i).str()); ASSERT_TRUE(other_s1.Insert(i).is_inserted()); } ExpectSetElementsAre(other_s1, MakeElements(llvm::seq(1, 48))); // Move-assign over the copy looks like the moved-from table not the copy. other_s1 = std::move(s); ExpectSetElementsAre(other_s1, MakeElements(llvm::seq(1, 32))); // Self-swap (which does a self-move) works and is a no-op. std::swap(other_s1, other_s1); ExpectSetElementsAre(other_s1, MakeElements(llvm::seq(1, 32))); // Test copying of a moved-from table over a valid table and // self-move-assign. The former is required to be valid, and the latter is // in at least the case of self-move-assign-when-moved-from, but the result // can be in any state so just do them and ensure we don't crash. SetT other_s2 = other_s1; // NOLINTNEXTLINE(bugprone-use-after-move): Testing required use-after-move. other_s2 = s; other_s1 = std::move(other_s1); s = std::move(s); } TYPED_TEST(MoveOnlySetTest, Move) { using SetT = TypeParam; static_assert(!std::is_copy_assignable_v); static_assert(!std::is_copy_constructible_v); static_assert(std::is_move_assignable_v); static_assert(std::is_move_constructible_v); auto make_set = [] { SetT s; // Make sure we exceed the small size for some of the set types, but not all // of them, so we cover all the combinations of copying between small and // large. for (int i : llvm::seq(1, 24)) { SCOPED_TRACE(llvm::formatv("Key: {0}", i).str()); EXPECT_TRUE(s.Insert(i).is_inserted()); } return s; }; SetT s = make_set(); SetT other_s1 = std::move(s); ExpectSetElementsAre(other_s1, MakeElements(llvm::seq(1, 24))); // Add some more elements. for (int i : llvm::seq(24, 32)) { SCOPED_TRACE(llvm::formatv("Key: {0}", i).str()); ASSERT_TRUE(other_s1.Insert(i).is_inserted()); } ExpectSetElementsAre(other_s1, MakeElements(llvm::seq(1, 32))); // Move back over a moved-from. s = std::move(other_s1); ExpectSetElementsAre(s, MakeElements(llvm::seq(1, 32))); // Now add still more elements, crossing the small size limit for all tested // map types. for (int i : llvm::seq(32, 72)) { SCOPED_TRACE(llvm::formatv("Key: {0}", i).str()); ASSERT_TRUE(s.Insert(i).is_inserted()); } ExpectSetElementsAre(s, MakeElements(llvm::seq(1, 72))); // Assignment replaces the contents. s = make_set(); ExpectSetElementsAre(s, MakeElements(llvm::seq(1, 24))); // Self-swap (which does a self-move) works and is a no-op. std::swap(s, s); ExpectSetElementsAre(s, MakeElements(llvm::seq(1, 24))); } TYPED_TEST(SetTest, Conversions) { using SetT = TypeParam; using KeyT = SetT::KeyT; SetT s; ASSERT_TRUE(s.Insert(1).is_inserted()); ASSERT_TRUE(s.Insert(2).is_inserted()); ASSERT_TRUE(s.Insert(3).is_inserted()); ASSERT_TRUE(s.Insert(4).is_inserted()); SetView sv = s; SetView csv = sv; SetView csv2 = s; EXPECT_TRUE(sv.Contains(1)); EXPECT_TRUE(csv.Contains(2)); EXPECT_TRUE(csv2.Contains(3)); } TYPED_TEST(SetTest, GrowToAllocSize) { using SetT = TypeParam; SetT s; // Grow when empty. May be a no-op for some small sizes. s.GrowToAllocSize(32); // Add some elements that will need to be propagated through subsequent // growths. Also delete some. ssize_t storage_bytes = s.ComputeMetrics().storage_bytes; for (int i : llvm::seq(1, 24)) { SCOPED_TRACE(llvm::formatv("Key: {0}", i).str()); ASSERT_TRUE(s.Insert(i).is_inserted()); } for (int i : llvm::seq(1, 8)) { SCOPED_TRACE(llvm::formatv("Key: {0}", i).str()); ASSERT_TRUE(s.Erase(i)); } // No further growth triggered. EXPECT_EQ(storage_bytes, s.ComputeMetrics().storage_bytes); // No-op. s.GrowToAllocSize(16); ExpectSetElementsAre(s, MakeElements(llvm::seq(8, 24))); // No further growth triggered. EXPECT_EQ(storage_bytes, s.ComputeMetrics().storage_bytes); // Get a few doubling based growths, and at least one beyond the largest small // size. s.GrowToAllocSize(64); ExpectSetElementsAre(s, MakeElements(llvm::seq(8, 24))); s.GrowToAllocSize(128); ExpectSetElementsAre(s, MakeElements(llvm::seq(8, 24))); s.GrowToAllocSize(256); ExpectSetElementsAre(s, MakeElements(llvm::seq(8, 24))); // Update the storage bytes after growth. EXPECT_LT(storage_bytes, s.ComputeMetrics().storage_bytes); storage_bytes = s.ComputeMetrics().storage_bytes; // Add some more, but not enough to trigger further growth, and then grow by // several more multiples of two to test handling large growth. for (int i : llvm::seq(24, 48)) { SCOPED_TRACE(llvm::formatv("Key: {0}", i).str()); ASSERT_TRUE(s.Insert(i).is_inserted()); } for (int i : llvm::seq(8, 16)) { SCOPED_TRACE(llvm::formatv("Key: {0}", i).str()); ASSERT_TRUE(s.Erase(i)); } // No growth from insertions. EXPECT_EQ(storage_bytes, s.ComputeMetrics().storage_bytes); s.GrowToAllocSize(1024); ExpectSetElementsAre(s, MakeElements(llvm::seq(16, 48))); // Storage should have grown. EXPECT_LT(storage_bytes, s.ComputeMetrics().storage_bytes); } TYPED_TEST(SetTest, GrowForInsert) { using SetT = TypeParam; SetT s; s.GrowForInsertCount(42); ssize_t storage_bytes = s.ComputeMetrics().storage_bytes; for (int i : llvm::seq(1, 42)) { SCOPED_TRACE(llvm::formatv("Key: {0}", i).str()); ASSERT_TRUE(s.Insert(i).is_inserted()); } ExpectSetElementsAre(s, MakeElements(llvm::seq(1, 42))); EXPECT_EQ(storage_bytes, s.ComputeMetrics().storage_bytes); // Erase many elements and grow again for another insert. for (int i : llvm::seq(1, 32)) { SCOPED_TRACE(llvm::formatv("Key: {0}", i).str()); ASSERT_TRUE(s.Erase(i)); } s.GrowForInsertCount(42); storage_bytes = s.ComputeMetrics().storage_bytes; for (int i : llvm::seq(42, 84)) { SCOPED_TRACE(llvm::formatv("Key: {0}", i).str()); ASSERT_TRUE(s.Insert(i).is_inserted()); } ExpectSetElementsAre(s, MakeElements(llvm::seq(32, 84))); EXPECT_EQ(storage_bytes, s.ComputeMetrics().storage_bytes); // Erase all the elements, then grow for a much larger insertion and insert // again. for (int i : llvm::seq(32, 84)) { SCOPED_TRACE(llvm::formatv("Key: {0}", i).str()); ASSERT_TRUE(s.Erase(i)); } s.GrowForInsertCount(321); storage_bytes = s.ComputeMetrics().storage_bytes; for (int i : llvm::seq(128, 321 + 128)) { SCOPED_TRACE(llvm::formatv("Key: {0}", i).str()); ASSERT_TRUE(s.Insert(i).is_inserted()); } ExpectSetElementsAre(s, MakeElements(llvm::seq(128, 321 + 128))); EXPECT_EQ(storage_bytes, s.ComputeMetrics().storage_bytes); } TEST(SetContextTest, Basic) { llvm::SmallVector keys; for (int i : llvm::seq(0, 513)) { keys.push_back(i * 100); } IndexKeyContext key_context(keys); Set> s; EXPECT_FALSE(s.Contains(42, key_context)); EXPECT_TRUE(s.Insert(1, key_context).is_inserted()); EXPECT_TRUE(s.Contains(1, key_context)); auto result = s.Lookup(TestData(100), key_context); EXPECT_TRUE(result); EXPECT_EQ(1, result.key()); auto i_result = s.Insert(1, IndexKeyContext(keys)); EXPECT_FALSE(i_result.is_inserted()); EXPECT_TRUE(s.Contains(1, key_context)); EXPECT_TRUE( s.Insert(TestData(200), [] { return 2; }, key_context).is_inserted()); EXPECT_TRUE(s.Contains(2, key_context)); EXPECT_TRUE(s.Contains(TestData(200), key_context)); // Verify all the elements. ExpectSetElementsAre(s, {1, 2}); // Fill up a bunch to ensure we trigger growth a few times. for (int i : llvm::seq(3, 512)) { SCOPED_TRACE(llvm::formatv("Key: {0}", i).str()); EXPECT_TRUE(s.Insert(i, key_context).is_inserted()); } for (int i : llvm::seq(1, 512)) { SCOPED_TRACE(llvm::formatv("Key: {0}", i).str()); EXPECT_TRUE(s.Contains(i, key_context)); EXPECT_FALSE(s.Insert(i, key_context).is_inserted()); } EXPECT_FALSE(s.Contains(0, key_context)); EXPECT_FALSE(s.Contains(512, key_context)); EXPECT_FALSE(s.Contains(TestData(0), key_context)); EXPECT_FALSE(s.Contains(TestData(51200), key_context)); // Verify all the elements. ExpectSetElementsAre(s, MakeElements(llvm::seq(1, 512))); } } // namespace } // namespace Carbon ================================================ FILE: common/string_helpers.cpp ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #include "common/string_helpers.h" #include #include #include #include #include #include #include "common/check.h" #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/SmallVector.h" #include "llvm/ADT/StringExtras.h" #include "llvm/ADT/StringRef.h" #include "llvm/Support/Allocator.h" #include "llvm/Support/ConvertUTF.h" namespace Carbon { static constexpr llvm::StringRef TripleQuotes = "'''"; static constexpr llvm::StringRef HorizontalWhitespaceChars = " \t"; // Carbon only takes uppercase hex input. static auto FromHex(char c) -> std::optional { if (c >= '0' && c <= '9') { return c - '0'; } if (c >= 'A' && c <= 'F') { return 10 + c - 'A'; } return std::nullopt; } auto UnescapeStringLiteral(llvm::StringRef source, const int hashtag_num, bool is_block_string) -> std::optional { std::string ret; ret.reserve(source.size()); std::string escape = "\\"; escape.resize(hashtag_num + 1, '#'); size_t i = 0; while (i < source.size()) { char c = source[i]; if (i + hashtag_num < source.size() && source.slice(i, i + hashtag_num + 1) == escape) { i += hashtag_num + 1; if (i == source.size()) { return std::nullopt; } switch (source[i]) { case 'n': ret.push_back('\n'); break; case 'r': ret.push_back('\r'); break; case 't': ret.push_back('\t'); break; case '0': if (i + 1 < source.size() && llvm::isDigit(source[i + 1])) { // \0[0-9] is reserved. return std::nullopt; } ret.push_back('\0'); break; case '"': ret.push_back('"'); break; case '\'': ret.push_back('\''); break; case '\\': ret.push_back('\\'); break; case 'x': { i += 2; if (i >= source.size()) { return std::nullopt; } std::optional c1 = FromHex(source[i - 1]); std::optional c2 = FromHex(source[i]); if (c1 == std::nullopt || c2 == std::nullopt) { return std::nullopt; } ret.push_back(16 * *c1 + *c2); break; } case 'u': { ++i; if (i >= source.size() || source[i] != '{') { return std::nullopt; } unsigned int unicode_int = 0; ++i; int original_i = i; while (i < source.size() && source[i] != '}') { std::optional hex_val = FromHex(source[i]); if (hex_val == std::nullopt) { return std::nullopt; } unicode_int = unicode_int << 4; unicode_int += hex_val.value(); ++i; if (i - original_i > 8) { return std::nullopt; } } if (i >= source.size()) { return std::nullopt; } if (i - original_i == 0) { return std::nullopt; } char utf8_buf[4]; char* utf8_end = &utf8_buf[0]; if (!llvm::ConvertCodePointToUTF8(unicode_int, utf8_end)) { return std::nullopt; } ret.append(utf8_buf, utf8_end - utf8_buf); break; } case '\n': if (!is_block_string) { return std::nullopt; } break; default: // Unsupported. return std::nullopt; } } else if (c == '\t') { // Disallow non-` ` horizontal whitespace: // https://github.com/carbon-language/carbon-lang/blob/trunk/docs/design/lexical_conventions/whitespace.md // TODO: This doesn't handle unicode whitespace. return std::nullopt; } else { ret.push_back(c); } ++i; } return ret; } auto ParseBlockStringLiteral(llvm::StringRef source, const int hashtag_num) -> ErrorOr { llvm::SmallVector lines; source.split(lines, '\n', /*MaxSplit=*/-1, /*KeepEmpty=*/true); if (lines.size() < 2) { return Error("Too few lines"); } llvm::StringRef first = lines[0]; if (!first.consume_front(TripleQuotes)) { return Error("Should start with triple quotes: " + first); } first = first.rtrim(HorizontalWhitespaceChars); // Remaining chars, if any, are a file type indicator. if (first.find_first_of("\"#") != llvm::StringRef::npos || first.find_first_of(HorizontalWhitespaceChars) != llvm::StringRef::npos) { return Error("Invalid characters in file type indicator: " + first); } llvm::StringRef last = lines[lines.size() - 1]; const size_t last_length = last.size(); last = last.ltrim(HorizontalWhitespaceChars); const size_t indent = last_length - last.size(); if (last != TripleQuotes) { return Error("Should end with triple quotes: " + last); } std::string parsed; for (size_t i = 1; i < lines.size() - 1; ++i) { llvm::StringRef line = lines[i]; const size_t first_non_ws = line.find_first_not_of(HorizontalWhitespaceChars); if (first_non_ws == llvm::StringRef::npos) { // Empty or whitespace-only line. line = ""; } else { if (first_non_ws < indent) { return Error("Wrong indent for line: " + line + ", expected " + llvm::Twine(indent)); } line = line.drop_front(indent).rtrim(HorizontalWhitespaceChars); } // Unescaping with \n appended to handle things like \\. llvm::SmallVector buffer; std::optional unescaped = UnescapeStringLiteral((line + "\n").toStringRef(buffer), hashtag_num, /*is_block_string=*/true); if (!unescaped.has_value()) { return Error("Invalid escaping in " + line); } // A \ string collapses into nothing. if (!unescaped->empty()) { parsed.append(*unescaped); } } return parsed; } auto StringRefContainsPointer(llvm::StringRef ref, const char* ptr) -> bool { auto le = std::less_equal<>(); return le(ref.begin(), ptr) && le(ptr, ref.end()); } auto BuildCStrArgs(llvm::StringRef tool_path, llvm::ArrayRef args, llvm::BumpPtrAllocator& alloc) -> llvm::SmallVector { return BuildCStrArgs(tool_path, /*prefix_args=*/{}, args, alloc); } auto BuildCStrArgs(llvm::StringRef tool_path, llvm::ArrayRef prefix_args, llvm::ArrayRef args, llvm::BumpPtrAllocator& alloc) -> llvm::SmallVector { ssize_t i = 0; auto make_cstr = [&](llvm::StringRef arg) { char* cstr = alloc.Allocate(arg.size() + 1); memcpy(cstr, arg.data(), arg.size()); cstr[arg.size()] = '\0'; i += arg.size() + 1; return cstr; }; llvm::SmallVector cstr_args; cstr_args.reserve(1 + prefix_args.size() + args.size()); cstr_args.push_back(make_cstr(tool_path)); for (const std::string& prefix_arg : prefix_args) { cstr_args.push_back(prefix_arg.c_str()); } for (llvm::StringRef arg : args) { cstr_args.push_back(make_cstr(arg)); } return cstr_args; } } // namespace Carbon ================================================ FILE: common/string_helpers.h ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #ifndef CARBON_COMMON_STRING_HELPERS_H_ #define CARBON_COMMON_STRING_HELPERS_H_ #include #include #include "common/error.h" #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/SmallVector.h" #include "llvm/ADT/StringRef.h" #include "llvm/Support/Allocator.h" namespace Carbon { // Note llvm StringExtras has significant functionality which is intended to be // complementary to this. // Unescapes Carbon escape sequences in the source string. Returns std::nullopt // on bad input. `is_block_string` enables escaping unique to block string // literals, such as \. auto UnescapeStringLiteral(llvm::StringRef source, int hashtag_num = 0, bool is_block_string = false) -> std::optional; // Parses a block string literal in `source`. auto ParseBlockStringLiteral(llvm::StringRef source, int hashtag_num = 0) -> ErrorOr; // Returns true if the pointer is in the string ref (including equality with // `ref.end()`). This should be used instead of `<=` comparisons for // correctness. auto StringRefContainsPointer(llvm::StringRef ref, const char* ptr) -> bool; // Converts `tool_path` and each of the `args` into C-strings and returns the // results. This is intended for use with APIs that expect `argv`-like command // line argument lists. // // Accepts a `cstr_arg_storage` that will provide the underlying storage for // the C-strings, and returns a small vector of the C-string pointers. The // returned small vector uses a large small size to allow most common command // lines to avoid extra allocations and growth passes. auto BuildCStrArgs(llvm::StringRef tool_path, llvm::ArrayRef args, llvm::BumpPtrAllocator& alloc) -> llvm::SmallVector; // An overload of `BuildCStrArgs` with the same core behavior as the above, but // with an extra series of `prefix_args` that are placed between the `tool_path` // and the `args` in the resulting list. // // Unlike the tool path and the main `args`, the `prefix_args` are accepted as // an array of `std::string`s and those string object's `c_str()` method is used // to get the underlying C-strings to include in the result. This is because // callers with prefix arguments regularly need to provide dedicated storage for // these arguments anyways and we can efficiently reuse that. In contrast, the // `args` are often pulled from an existing `llvm::StringRef` that may never // exist as a valid C-string and so we need to rebuild those using the storage. auto BuildCStrArgs(llvm::StringRef tool_path, llvm::ArrayRef prefix_args, llvm::ArrayRef args, llvm::BumpPtrAllocator& alloc) -> llvm::SmallVector; } // namespace Carbon #endif // CARBON_COMMON_STRING_HELPERS_H_ ================================================ FILE: common/string_helpers_test.cpp ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #include "common/string_helpers.h" #include #include #include #include #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/StringRef.h" #include "llvm/Support/Allocator.h" using ::testing::Eq; using ::testing::Optional; using ::testing::StrEq; namespace Carbon { namespace { TEST(UnescapeStringLiteral, Valid) { EXPECT_THAT(UnescapeStringLiteral("test"), Optional(Eq("test"))); EXPECT_THAT(UnescapeStringLiteral("okay whitespace"), Optional(Eq("okay whitespace"))); EXPECT_THAT(UnescapeStringLiteral("test\n"), Optional(Eq("test\n"))); EXPECT_THAT(UnescapeStringLiteral("test\\n"), Optional(Eq("test\n"))); EXPECT_THAT(UnescapeStringLiteral("abc\\ndef"), Optional(Eq("abc\ndef"))); EXPECT_THAT(UnescapeStringLiteral("test\\\\n"), Optional(Eq("test\\n"))); EXPECT_THAT(UnescapeStringLiteral("\\xAA"), Optional(Eq("\xAA"))); EXPECT_THAT(UnescapeStringLiteral("\\x12"), Optional(Eq("\x12"))); EXPECT_THAT(UnescapeStringLiteral("test", 1), Optional(Eq("test"))); EXPECT_THAT(UnescapeStringLiteral("test\\#n", 1), Optional(Eq("test\n"))); EXPECT_THAT(UnescapeStringLiteral( "r\\u{000000E9}al \\u{2764}\\u{FE0F}\\u{1F50A}!\\u{10FFFF}"), Optional(Eq("réal ❤️🔊!􏿿"))); } TEST(UnescapeStringLiteral, Invalid) { // Missing char after `\`. EXPECT_THAT(UnescapeStringLiteral("a\\"), Eq(std::nullopt)); // Not a supported escape. EXPECT_THAT(UnescapeStringLiteral("\\e"), Eq(std::nullopt)); // Needs 2 hex chars. EXPECT_THAT(UnescapeStringLiteral("\\x"), Eq(std::nullopt)); // Needs 2 hex chars. EXPECT_THAT(UnescapeStringLiteral("\\xA"), Eq(std::nullopt)); // Needs uppercase hex. EXPECT_THAT(UnescapeStringLiteral("\\xaa"), Eq(std::nullopt)); // Reserved. EXPECT_THAT(UnescapeStringLiteral("\\00"), Eq(std::nullopt)); EXPECT_THAT(UnescapeStringLiteral("\\#00", 1), Eq(std::nullopt)); } TEST(UnescapeStringLiteral, InvalidUnicodes) { // Various incomplete Unicode specifiers EXPECT_THAT(UnescapeStringLiteral("\\u"), Eq(std::nullopt)); EXPECT_THAT(UnescapeStringLiteral("\\u1"), Eq(std::nullopt)); EXPECT_THAT(UnescapeStringLiteral("\\uz"), Eq(std::nullopt)); EXPECT_THAT(UnescapeStringLiteral("\\u{"), Eq(std::nullopt)); EXPECT_THAT(UnescapeStringLiteral("\\u{z"), Eq(std::nullopt)); EXPECT_THAT(UnescapeStringLiteral("\\u{E9"), Eq(std::nullopt)); EXPECT_THAT(UnescapeStringLiteral("\\u{E9z"), Eq(std::nullopt)); EXPECT_THAT(UnescapeStringLiteral("\\u{}"), Eq(std::nullopt)); // invalid characters in unicode EXPECT_THAT(UnescapeStringLiteral("\\u{z}"), Eq(std::nullopt)); // lowercase hexadecimal EXPECT_THAT(UnescapeStringLiteral("\\u{e9}"), Eq(std::nullopt)); // Codepoint number too high EXPECT_THAT(UnescapeStringLiteral("\\u{110000}"), Eq(std::nullopt)); // codepoint more than 8 hex digits EXPECT_THAT(UnescapeStringLiteral("\\u{FF000000E9}"), Eq(std::nullopt)); } TEST(UnescapeStringLiteral, Nul) { std::optional str = UnescapeStringLiteral("a\\0b"); ASSERT_NE(str, std::nullopt); EXPECT_THAT(str->size(), Eq(3)); EXPECT_THAT(strlen(str->c_str()), Eq(1)); EXPECT_THAT((*str)[0], Eq('a')); EXPECT_THAT((*str)[1], Eq('\0')); EXPECT_THAT((*str)[2], Eq('b')); } TEST(ParseBlockStringLiteral, FailTooFewLines) { EXPECT_THAT(ParseBlockStringLiteral("").error().message(), Eq("Too few lines")); } TEST(ParseBlockStringLiteral, FailNoLeadingTripleQuotes) { EXPECT_THAT(ParseBlockStringLiteral("'a'\n").error().message(), Eq("Should start with triple quotes: 'a'")); } TEST(ParseBlockStringLiteral, FailInvalideFiletypeIndicator) { EXPECT_THAT(ParseBlockStringLiteral("'''carbon file\n").error().message(), Eq("Invalid characters in file type indicator: carbon file")); } TEST(ParseBlockStringLiteral, FailEndingTripleQuotes) { EXPECT_THAT(ParseBlockStringLiteral("'''\n").error().message(), Eq("Should end with triple quotes: ")); } TEST(ParseBlockStringLiteral, FailWrongIndent) { constexpr char Input[] = R"(''' A block string literal with wrong indent ''')"; EXPECT_THAT(ParseBlockStringLiteral(Input).error().message(), Eq("Wrong indent for line: with wrong indent, expected 5")); } TEST(ParseBlockStringLiteral, FailInvalidEscaping) { constexpr char Input[] = R"(''' \q ''')"; EXPECT_THAT(ParseBlockStringLiteral(Input).error().message(), Eq("Invalid escaping in \\q")); constexpr char InputRaw[] = R"(''' \#q ''')"; EXPECT_THAT(ParseBlockStringLiteral(InputRaw, 1).error().message(), Eq("Invalid escaping in \\#q")); } TEST(ParseBlockStringLiteral, OkEmptyString) { constexpr char Input[] = R"(''' ''')"; EXPECT_THAT(*ParseBlockStringLiteral(Input), Eq("")); } TEST(ParseBlockStringLiteral, OkOneLineString) { constexpr char Input[] = R"(''' A block string literal ''')"; constexpr char Expected[] = R"(A block string literal )"; EXPECT_THAT(*ParseBlockStringLiteral(Input), Eq(Expected)); } TEST(ParseBlockStringLiteral, OkTwoLineString) { constexpr char Input[] = R"(''' A block string literal with indent. ''')"; constexpr char Expected[] = R"(A block string literal with indent. )"; EXPECT_THAT(*ParseBlockStringLiteral(Input), Eq(Expected)); } TEST(ParseBlockStringLiteral, OkWithFileTypeIndicator) { constexpr char Input[] = R"('''carbon A block string literal with file type indicator. ''')"; constexpr char Expected[] = R"(A block string literal with file type indicator. )"; EXPECT_THAT(*ParseBlockStringLiteral(Input), Eq(Expected)); } TEST(ParseBlockStringLiteral, OkWhitespaceAfterOpeningQuotes) { constexpr char Input[] = R"(''' A block string literal ''')"; constexpr char Expected[] = R"(A block string literal )"; EXPECT_THAT(*ParseBlockStringLiteral(Input), Eq(Expected)); } TEST(ParseBlockStringLiteral, OkWithEmptyLines) { constexpr char Input[] = R"(''' A block string literal with empty lines. ''')"; constexpr char Expected[] = R"(A block string literal with empty lines. )"; EXPECT_THAT(*ParseBlockStringLiteral(Input), Eq(Expected)); } TEST(ParseBlockStringLiteral, OkWithSlashNewlineEscape) { constexpr char Input[] = R"(''' A block string literal\ ''')"; constexpr char Expected[] = "A block string literal"; EXPECT_THAT(*ParseBlockStringLiteral(Input), Eq(Expected)); } TEST(ParseBlockStringLiteral, OkWithDoubleSlashNewline) { constexpr char Input[] = R"(''' A block string literal\\ ''')"; constexpr char Expected[] = R"(A block string literal\ )"; EXPECT_THAT(*ParseBlockStringLiteral(Input), Eq(Expected)); } TEST(ParseBlockStringLiteral, OkWithTripleSlashNewline) { constexpr char Input[] = R"(''' A block string literal\\\ ''')"; constexpr char Expected[] = R"(A block string literal\)"; EXPECT_THAT(*ParseBlockStringLiteral(Input), Eq(Expected)); } TEST(ParseBlockStringLiteral, OkMultipleSlashes) { constexpr char Input[] = R"(''' A block string literal\ \ \ \ ''')"; constexpr char Expected[] = "A block string literal"; EXPECT_THAT(*ParseBlockStringLiteral(Input), Eq(Expected)); } TEST(BuildCStrArgs, NoArgs) { llvm::BumpPtrAllocator alloc; auto result = BuildCStrArgs("tool", {}, alloc); ASSERT_THAT(result.size(), Eq(1)); EXPECT_THAT(result[0], StrEq("tool")); } TEST(BuildCStrArgs, OneArg) { llvm::BumpPtrAllocator alloc; auto result = BuildCStrArgs("tool", {"arg1"}, alloc); ASSERT_THAT(result.size(), Eq(2)); EXPECT_THAT(result[0], StrEq("tool")); EXPECT_THAT(result[1], StrEq("arg1")); } TEST(BuildCStrArgs, MultipleArgs) { llvm::BumpPtrAllocator alloc; auto result = BuildCStrArgs("tool", {"arg1", "arg2"}, alloc); ASSERT_THAT(result.size(), Eq(3)); EXPECT_THAT(result[0], StrEq("tool")); EXPECT_THAT(result[1], StrEq("arg1")); EXPECT_THAT(result[2], StrEq("arg2")); } TEST(BuildCStrArgsWithPrefix, NoArgs) { llvm::BumpPtrAllocator alloc; auto result = BuildCStrArgs("tool", {}, {}, alloc); ASSERT_THAT(result.size(), Eq(1)); EXPECT_THAT(result[0], StrEq("tool")); } TEST(BuildCStrArgsWithPrefix, PrefixOnly) { llvm::BumpPtrAllocator alloc; std::string prefix_args[] = {"p_arg1", "p_arg2"}; auto result = BuildCStrArgs("tool", prefix_args, {}, alloc); ASSERT_THAT(result.size(), Eq(3)); EXPECT_THAT(result[0], StrEq("tool")); EXPECT_THAT(result[1], Eq(prefix_args[0].c_str())); EXPECT_THAT(result[2], Eq(prefix_args[1].c_str())); } TEST(BuildCStrArgsWithPrefix, ArgsOnly) { llvm::BumpPtrAllocator alloc; auto result = BuildCStrArgs("tool", {}, {"arg1", "arg2"}, alloc); ASSERT_THAT(result.size(), Eq(3)); EXPECT_THAT(result[0], StrEq("tool")); EXPECT_THAT(result[1], StrEq("arg1")); EXPECT_THAT(result[2], StrEq("arg2")); } TEST(BuildCStrArgsWithPrefix, BothPrefixAndArgs) { llvm::BumpPtrAllocator alloc; std::string prefix_args[] = {"p_arg1", "p_arg2"}; auto result = BuildCStrArgs("tool", prefix_args, {"arg1", "arg2"}, alloc); ASSERT_THAT(result.size(), Eq(5)); EXPECT_THAT(result[0], StrEq("tool")); EXPECT_THAT(result[1], Eq(prefix_args[0].c_str())); EXPECT_THAT(result[2], Eq(prefix_args[1].c_str())); EXPECT_THAT(result[3], StrEq("arg1")); EXPECT_THAT(result[4], StrEq("arg2")); } } // namespace } // namespace Carbon ================================================ FILE: common/struct_reflection.h ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #ifndef CARBON_COMMON_STRUCT_REFLECTION_H_ #define CARBON_COMMON_STRUCT_REFLECTION_H_ // Reflection support for simple struct types. // // Example usage: // // ``` // struct A { int x; std::string y; }; // // A a; // std::tuple t = StructReflection::AsTuple(a); // ``` // // Limitations: // // - Only simple aggregate structs are supported. Types with base classes, // non-public data members, constructors, or virtual functions are not // supported. // - Structs with more than 8 fields are not supported. This limit is easy to // increase if needed, but removing it entirely is hard. // - Structs containing a reference to the same type are not supported. #include #include namespace Carbon::StructReflection { namespace Internal { // A type that can be converted to any field type within type T. template struct AnyField { template // NOLINTNEXTLINE(google-explicit-constructor) explicit(false) operator FieldT&() const; template // NOLINTNEXTLINE(google-explicit-constructor) explicit(false) operator FieldT&&() const; // Don't allow conversion to T itself. This ensures we don't match against a // copy or move constructor. operator T&() const = delete; operator T&&() const = delete; }; // The detection mechanism below intentionally misses field initializers. #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wmissing-field-initializers" // Detector for whether we can list-initialize T from the given list of fields. template constexpr auto CanListInitialize(decltype(T{Fields()...})* /*unused*/) -> bool { return true; } template constexpr auto CanListInitialize(...) -> bool { return false; } #pragma clang diagnostic pop // Simple detector to find the number of data fields in a struct. This proceeds // in two passes: // // 1) Add AnyFields until we can initialize T from our list of initializers. // 2) Add more AnyFields until we can't initialize any more. template constexpr auto CountFields() -> int { if constexpr (CanListInitialize(nullptr)) { return CountFields>(); } else if constexpr (AnyWorkedSoFar) { constexpr int NumFields = sizeof...(Fields) - 1; static_assert(NumFields <= 8, "Unsupported: too many fields in struct"); return NumFields; } else if constexpr (sizeof...(Fields) > 32) { // If we go too far without finding a working initializer, something // probably went wrong with our calculation. Bail out before we recurse too // deeply. static_assert(sizeof...(Fields) <= 32, "Internal error, could not count fields in struct"); } else { return CountFields>(); } } // Utility to access fields by index. template struct FieldAccessor; template <> struct FieldAccessor<0> { template static auto Get(T& /*value*/) -> auto { return std::tuple<>(); } }; template <> struct FieldAccessor<1> { template static auto Get(T& value) -> auto { auto& [field0] = value; return std::tuple(field0); } }; template <> struct FieldAccessor<2> { template static auto Get(T& value) -> auto { auto& [field0, field1] = value; return std::tuple(field0, field1); } }; template <> struct FieldAccessor<3> { template static auto Get(T& value) -> auto { auto& [field0, field1, field2] = value; return std::tuple( field0, field1, field2); } }; template <> struct FieldAccessor<4> { template static auto Get(T& value) -> auto { auto& [field0, field1, field2, field3] = value; return std::tuple(field0, field1, field2, field3); } }; template <> struct FieldAccessor<5> { template static auto Get(T& value) -> auto { auto& [field0, field1, field2, field3, field4] = value; return std::tuple( field0, field1, field2, field3, field4); } }; template <> struct FieldAccessor<6> { template static auto Get(T& value) -> auto { auto& [field0, field1, field2, field3, field4, field5] = value; return std::tuple( field0, field1, field2, field3, field4, field5); } }; template <> struct FieldAccessor<7> { template static auto Get(T& value) -> auto { auto& [field0, field1, field2, field3, field4, field5, field6] = value; return std::tuple(field0, field1, field2, field3, field4, field5, field6); } }; template <> struct FieldAccessor<8> { template static auto Get(T& value) -> auto { auto& [field0, field1, field2, field3, field4, field5, field6, field7] = value; return std::tuple( field0, field1, field2, field3, field4, field5, field6, field7); } }; } // namespace Internal // Get the fields of the struct `T` as a tuple. template auto AsTuple(T value) -> auto { // We use aggregate initialization to detect the number of fields. static_assert(std::is_aggregate_v, "Only aggregates are supported"); return Internal::FieldAccessor()>::Get(value); } } // namespace Carbon::StructReflection #endif // CARBON_COMMON_STRUCT_REFLECTION_H_ ================================================ FILE: common/struct_reflection_test.cpp ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #include "common/struct_reflection.h" #include #include namespace Carbon::StructReflection { namespace { struct ZeroFields {}; struct OneField { int x; }; struct TwoFields { int x; int y; }; struct SixFields { int one; int two; int three; int four; int five; int six; }; struct ReferenceField { int& ref; }; struct NoDefaultConstructor { explicit NoDefaultConstructor(int n) : v(n) {} int v; }; struct OneFieldNoDefaultConstructor { NoDefaultConstructor x; }; struct TwoFieldsNoDefaultConstructor { NoDefaultConstructor x; NoDefaultConstructor y; }; TEST(StructReflectionTest, CanListInitialize) { { using Type = OneField; using Field = Internal::AnyField; static_assert(Internal::CanListInitialize(nullptr)); static_assert(Internal::CanListInitialize(nullptr)); static_assert(!Internal::CanListInitialize(0)); } { using Type = OneFieldNoDefaultConstructor; using Field = Internal::AnyField; static_assert(!Internal::CanListInitialize(0)); static_assert(Internal::CanListInitialize(nullptr)); static_assert(!Internal::CanListInitialize(0)); } } TEST(StructReflectionTest, CountFields) { static_assert(Internal::CountFields() == 0); static_assert(Internal::CountFields() == 1); static_assert(Internal::CountFields() == 2); static_assert(Internal::CountFields() == 6); static_assert(Internal::CountFields() == 1); static_assert(Internal::CountFields() == 1); } TEST(StructReflectionTest, EmptyStruct) { std::tuple<> fields = AsTuple(ZeroFields()); static_cast(fields); } TEST(StructReflectionTest, OneField) { std::tuple fields = AsTuple(OneField{.x = 1}); EXPECT_EQ(std::get<0>(fields), 1); } TEST(StructReflectionTest, TwoFields) { std::tuple fields = AsTuple(TwoFields{.x = 1, .y = 2}); EXPECT_EQ(std::get<0>(fields), 1); EXPECT_EQ(std::get<1>(fields), 2); } TEST(StructReflectionTest, SixFields) { std::tuple fields = AsTuple(SixFields{ .one = 1, .two = 2, .three = 3, .four = 4, .five = 5, .six = 6}); EXPECT_EQ(std::get<0>(fields), 1); EXPECT_EQ(std::get<1>(fields), 2); EXPECT_EQ(std::get<2>(fields), 3); EXPECT_EQ(std::get<3>(fields), 4); EXPECT_EQ(std::get<4>(fields), 5); EXPECT_EQ(std::get<5>(fields), 6); } TEST(StructReflectionTest, NoDefaultConstructor) { std::tuple fields = AsTuple(TwoFieldsNoDefaultConstructor{.x = NoDefaultConstructor(1), .y = NoDefaultConstructor(2)}); EXPECT_EQ(std::get<0>(fields).v, 1); EXPECT_EQ(std::get<1>(fields).v, 2); } TEST(StructReflectionTest, ReferenceField) { int n = 0; std::tuple fields = AsTuple(ReferenceField{.ref = n}); EXPECT_EQ(&std::get<0>(fields), &n); } } // namespace } // namespace Carbon::StructReflection ================================================ FILE: common/template_string.h ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #ifndef CARBON_COMMON_TEMPLATE_STRING_H_ #define CARBON_COMMON_TEMPLATE_STRING_H_ #include "llvm/ADT/StringRef.h" namespace Carbon { // Represents a compile-time string in a form suitable for use as a non-type // template argument. // // These arguments are required to be a "structural type", and so we copy the // string contents into a public array of `char`s. For details, see: // https://en.cppreference.com/w/cpp/language/template_parameters#Non-type_template_parameter // // Designed to support implicitly deduced construction from a string literal // template argument. This type will implicitly convert to an `llvm::StringRef` // for accessing the string contents, and also provides a dedicated `c_str()` // method to access the string as a C string. // // Example usage: // ```cpp // template auto F() -> void { // llvm::cout() << Str; // } // // auto Example() -> void { // F<"string contents here">(); // } // ``` template struct TemplateString { // Constructs the template string from a string literal. // // Intentionally allows implicit conversion from string literals for use as a // non-type template parameter. // // The closest we can get to explicitly accepting a string literal is to // accept an array of `const char`s, so we additionally use Clang's constexpr // `enable_if` attribute to require the array to be usable as a C string with // the expected length. This checks both for null-termination and no embedded // `0` bytes. explicit(false) constexpr TemplateString(const char (&str)[N + 1]) __attribute__(( enable_if(__builtin_strlen(str) == N, "character array is not null-terminated valid C string"))) { // Rely on Clang's constexpr `__builtin_memcpy` to minimize compile time // overhead copying the string contents around. __builtin_memcpy(storage_, str, N + 1); } // This type is designed to act as a `StringLiteral` implicitly while having // the storage necessary to be used as a template parameter. // // NOLINTNEXTLINE(google-explicit-constructor) explicit(false) constexpr operator llvm::StringLiteral() const { return llvm::StringLiteral::withInnerNUL(storage_); } // Accesses the string data directly as a compile-time C string. constexpr auto c_str() const -> const char* { return storage_; } // Note that this must be public for the type to be structural and available // as a template argument, but this is not part of the public API. char storage_[N + 1]; }; // Allow deducing `N` when implicitly constructing these so that we can directly // use a string literal in a template argument. The array needs an extra char // for the null terminator. template TemplateString(const char (&str)[M]) -> TemplateString; } // namespace Carbon #endif // CARBON_COMMON_TEMPLATE_STRING_H_ ================================================ FILE: common/template_string_test.cpp ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #include "common/template_string.h" #include #include #include namespace Carbon { namespace { using ::testing::StrEq; template constexpr auto TemplateAsStringRef() -> llvm::StringRef { return S; } template constexpr auto TemplateAsStringLiteral() -> llvm::StringLiteral { return S; } template constexpr auto TemplateAsCStr() -> const char* { return S.c_str(); } // An overload that will be active when it is passed a valid `TemplateString`. // Returns a true type to allow detection of a valid `TemplateString` argument. template constexpr auto IsValidTemplateString(int /*unused*/) -> std::true_type { return {}; } // A struct that can be used as a template parameter for any template argument. struct AnythingAsTemplateArg { // An implicit constructor that can accept any argument and discards it. template // NOLINTNEXTLINE(bugprone-forwarding-reference-overload) explicit(false) constexpr AnythingAsTemplateArg(T&& /*unused*/) {} }; // An overload that will be active for any template argument. Returns a false // type and is used to detect when a template argument cannot correctly match a // `TemplateString`. template constexpr auto IsValidTemplateString(...) -> std::false_type { return {}; } // Compile time tests with `static_assert` static_assert(TemplateAsStringRef<"test">().size() == 4, "Not usable in a `constexpr` context."); static_assert(TemplateAsStringLiteral<"test">().size() == 4, "Not usable in a `constexpr` context."); static_assert(__builtin_strlen(TemplateAsCStr<"test">()) == 4, "Not usable in a `constexpr` context."); // The string must not contain embedded nulls. static_assert(IsValidTemplateString<"test">(0)); static_assert(!IsValidTemplateString<"test\0test">(0)); // The string must be null-terminated. using FourChars = char[4]; static_assert(IsValidTemplateString(0)); static_assert(!IsValidTemplateString(0)); TEST(TemplateStringTest, Test) { EXPECT_THAT(TemplateAsStringRef<"test">(), StrEq("test")); EXPECT_THAT(TemplateAsStringLiteral<"test">(), StrEq("test")); EXPECT_THAT(TemplateAsCStr<"test">(), StrEq("test")); constexpr char GoodStr[5] = {'t', 'e', 's', 't', '\0'}; static_assert(IsValidTemplateString(0)); EXPECT_THAT(TemplateAsStringRef(), StrEq("test")); EXPECT_THAT(TemplateAsStringLiteral(), StrEq("test")); constexpr char BadStr[4] = {'t', 'e', 's', 't'}; static_assert(!IsValidTemplateString(0)); } } // namespace } // namespace Carbon ================================================ FILE: common/type_enum.h ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #ifndef CARBON_COMMON_TYPE_ENUM_H_ #define CARBON_COMMON_TYPE_ENUM_H_ #include #include "common/ostream.h" namespace Carbon { // An enum whose values are the specified types. template class TypeEnum : public Printable> { public: using TypeTuple = std::tuple; static constexpr size_t NumTypes = sizeof...(Types); static constexpr size_t NumValues = NumTypes + 2; static_assert(NumValues <= 256, "Too many types for raw enum."); // The underlying raw enumeration type. // // The enum_extensibility attribute indicates that this enum is intended to // take values that do not correspond to its declared enumerators. enum class [[clang::enum_extensibility(open)]] RawEnumType : uint8_t { // The first sizeof...(Types) values correspond to the types. // An explicitly invalid value. Invalid = NumTypes, // Indicates that no type should be used. // TODO: This doesn't really fit the model of this type, but it's convenient // for all of its users. None, }; // Accesses the type given an enum value. template requires(K != RawEnumType::Invalid) using TypeFor = __type_pack_element(K), Types...>; // Workaround for Clang bug https://github.com/llvm/llvm-project/issues/85461 template static constexpr auto FromRaw = TypeEnum(Value); // Names for the `Invalid` and `None` enumeration values. static constexpr const TypeEnum& Invalid = FromRaw; static constexpr const TypeEnum& None = FromRaw; // Accesses the enumeration value for the type `Type`. If `AllowInvalid` is // set, any unexpected type is mapped to `Invalid`, otherwise an invalid type // results in a compile error. // // The `Self` parameter is an implementation detail to allow `ForImpl` to be // defined after this template, and should not be specified. template static constexpr auto For = Self::template ForImpl(); // This bool indicates whether the specified type corresponds to a value in // this enum. template static constexpr bool Contains = For.is_valid(); // Explicitly convert from the raw enum type. explicit constexpr TypeEnum(RawEnumType value) : value_(value) {} // Implicitly convert to the raw enum type, for use in `switch`. // // NOLINTNEXTLINE(google-explicit-constructor) explicit(false) constexpr operator RawEnumType() const { return value_; } // Conversion to bool is deleted to prevent direct use in an `if` condition // instead of comparing with another value. explicit operator bool() const = delete; // Returns the raw enum value. constexpr auto ToRaw() const -> RawEnumType { return value_; } // Returns a value that can be used as an array index. Returned value will be // < NumValues. constexpr auto ToIndex() const -> size_t { return static_cast(value_); } // Returns whether this is a valid value, not `Invalid`. constexpr auto is_valid() const -> bool { return value_ != RawEnumType::Invalid; } auto Print(llvm::raw_ostream& out) const -> void { out << "TypeEnum("; if (value_ == RawEnumType::None) { out << "None"; } else { static constexpr std::array Names = { Types::Label..., }; out << Names[static_cast(value_)]; } out << ")"; } private: // Translates a type to its enum value, or `Invalid`. template static constexpr auto ForImpl() -> TypeEnum { // A bool for each type saying whether it matches. The result is the index // of the first `true` in this list. If none matches, then the result is the // length of the list, which is mapped to `Invalid`. constexpr bool TypeMatches[] = {std::same_as...}; constexpr int Index = std::find(TypeMatches, TypeMatches + NumTypes, true) - TypeMatches; static_assert(Index != NumTypes || AllowInvalid, "Unexpected type passed to TypeEnum::For<...>"); return TypeEnum(static_cast(Index)); } RawEnumType value_; }; } // namespace Carbon #endif // CARBON_COMMON_TYPE_ENUM_H_ ================================================ FILE: common/version.h ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #ifndef CARBON_COMMON_VERSION_H_ #define CARBON_COMMON_VERSION_H_ #include "llvm/ADT/StringRef.h" namespace Carbon { struct Version { static const int Major; static const int Minor; static const int Patch; static const llvm::StringLiteral String; // A dedicated version information string to use in the toolchain as its // command line rendered version. Composed centrally so it can be composed at // compile time with potentially build info stamped components. static const llvm::StringLiteral ToolchainInfo; }; } // namespace Carbon #endif // CARBON_COMMON_VERSION_H_ ================================================ FILE: common/version.tmpl.cpp ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #include "common/version.h" #include namespace Carbon { // A simplistic string-to-integer routine that is consteval for compile-time // extracting specific components of the version from the string form. We use // `std::string_view` for its broader `constexpr` API. static consteval auto ToInt(std::string_view str) -> int { int result = 0; while (true) { result += str.front() - '0'; str.remove_prefix(1); if (str.empty()) { break; } result *= 10; } return result; } static consteval auto MajorVersion(std::string_view str) -> int { return ToInt(str.substr(0, str.find('.'))); } static consteval auto MinorVersion(std::string_view str) -> int { str.remove_prefix(str.find('.') + 1); return ToInt(str.substr(0, str.find('.'))); } static consteval auto PatchVersion(std::string_view str) -> int { str.remove_prefix(str.find('.') + 1); str.remove_prefix(str.find('.') + 1); // Note that searching for `-` may find the end of the string if there is no // pre-release component, but that produces the correct result here. return ToInt(str.substr(0, str.find('-'))); } // The major, minor, and patch versions are always provided and stable. They // don't depend on build stamping or introduce caching issues. Provide normal // strong definitions. constexpr int Version::Major = MajorVersion("$VERSION"); constexpr int Version::Minor = MinorVersion("$VERSION"); constexpr int Version::Patch = PatchVersion("$VERSION"); } // namespace Carbon ================================================ FILE: common/version_stamp.tmpl.cpp ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #include "common/version.h" namespace Carbon { #pragma clang attribute push // If requested, make the contents of this file weak. #if $MAKE_WEAK #pragma clang attribute(__attribute__((weak)), \ apply_to = any(function, variable)) #endif constexpr llvm::StringLiteral Version::String = "$VERSION+$GIT_COMMIT_SHA$GIT_DIRTY_SUFFIX"; constexpr llvm::StringLiteral Version::ToolchainInfo = R"""( Carbon Language toolchain version: $VERSION+$GIT_COMMIT_SHA$GIT_DIRTY_SUFFIX )"""; #pragma clang attribute pop } // namespace Carbon ================================================ FILE: common/vlog.h ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #ifndef CARBON_COMMON_VLOG_H_ #define CARBON_COMMON_VLOG_H_ #include "common/ostream.h" #include "common/template_string.h" #include "llvm/Support/FormatVariadic.h" namespace Carbon::Internal { // Implements verbose logging. // // This is designed to minimize the overhead in callers by being a // forcibly-outlined routine that takes a minimal number of parameters. // // Internally uses `llvm::formatv` to render the format string with any value // arguments, and streams the result to the provided stream. template [[clang::noinline]] auto VLogImpl(llvm::raw_ostream* stream, Ts&&... values) -> void { *stream << llvm::formatv(FormatStr.c_str(), std::forward(values)...); } } // namespace Carbon::Internal // Logs when verbose logging is enabled. CARBON_VLOG_TO uses a provided stream; // CARBON_VLOG requires a member named `vlog_stream_`. // // For example: // CARBON_VLOG_TO(vlog_stream, "Verbose message: {0}", "extra information"); // CARBON_VLOG("Verbose message: {0}", "extra information"); // // The first argument must be a string literal format string valid for passing // to `llvm::formatv`. If it contains any substitutions, those should be passed // as subsequent arguments. // // Also supports a legacy syntax where no arguments are passed and the desired // logging is streamed into the call: // CARBON_VLOG() << "Legacy verbose message"; // // However, the streaming syntax has higher overhead and can inhibit inlining. // Code should prefer the format string form, and eventually when all code has // migrated the streaming interface will be removed. #define CARBON_VLOG_TO(Stream, FormatStr, ...) \ __builtin_expect(Stream == nullptr, true) \ ? (void)0 \ : Carbon::Internal::VLogImpl<"" FormatStr>(Stream __VA_OPT__(, ) \ __VA_ARGS__) #define CARBON_VLOG(FormatStr, ...) \ CARBON_VLOG_TO(vlog_stream_, FormatStr, __VA_ARGS__) #endif // CARBON_COMMON_VLOG_H_ ================================================ FILE: common/vlog_test.cpp ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #include "common/vlog.h" #include #include #include #include "common/raw_string_ostream.h" namespace Carbon::Testing { namespace { using ::testing::IsEmpty; using ::testing::StrEq; // Helper class with a vlog_stream_ member for CARBON_VLOG. class VLogger { public: explicit VLogger(bool enable) { if (enable) { vlog_stream_ = &buffer_; } } auto VLog() -> void { CARBON_VLOG("Test\n"); } auto VLogFormatArgs() -> void { CARBON_VLOG("Test {0} {1} {2}\n", 1, 2, 3); } auto TakeStr() -> std::string { return buffer_.TakeStr(); } private: RawStringOstream buffer_; llvm::raw_ostream* vlog_stream_ = nullptr; }; TEST(VLogTest, Enabled) { VLogger vlog(/*enable=*/true); vlog.VLog(); EXPECT_THAT(vlog.TakeStr(), StrEq("Test\n")); vlog.VLogFormatArgs(); EXPECT_THAT(vlog.TakeStr(), StrEq("Test 1 2 3\n")); } TEST(VLogTest, Disabled) { VLogger vlog(/*enable=*/false); vlog.VLog(); EXPECT_THAT(vlog.TakeStr(), IsEmpty()); } TEST(VLogTest, To) { RawStringOstream buffer; CARBON_VLOG_TO(&buffer, "Test"); EXPECT_THAT(buffer.TakeStr(), "Test"); } TEST(VLogTest, ToNull) { CARBON_VLOG_TO(nullptr, "Unused"); } } // namespace } // namespace Carbon::Testing ================================================ FILE: core/BUILD ================================================ # Part of the Carbon Language project, under the Apache License v2.0 with LLVM # Exceptions. See /LICENSE for license information. # SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception load("//bazel/manifest:defs.bzl", "manifest") # Raw prelude files. # TODO: This includes all of Core, not just the prelude. filegroup( name = "prelude_files", srcs = glob(["**/*.carbon"]), visibility = ["//visibility:public"], ) # A list of prelude inputs. # This is consumed by //toolchain/base:install_paths. manifest( name = "prelude_manifest.txt", srcs = [":prelude_files"], strip_package_dir = True, ) # All files for the toolchain install. filegroup( name = "prelude", srcs = [ ":prelude_files", ":prelude_manifest.txt", ], visibility = ["//visibility:public"], ) ================================================ FILE: core/io.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // TODO: This library is not part of the design. Either write a matching // proposal or remove this. package Core library "io"; import library "prelude"; // TODO: Support printing other types. // TODO: Consider rewriting using native support once library support exists. fn Print(x: i32) = "print.int"; fn PrintChar(x: char) -> i32 = "print.char"; fn PrintStr(msg: str) { let size: i64 = msg.Size() as i64; var i: i64 = 0; while (i < size) { PrintChar(msg[i]); ++i; } } // TODO: Return an `Optional(char)` instead of `i32`. fn ReadChar() -> i32 = "read.char"; // TODO: Change this to a global constant once they are fully supported. fn EOF() -> i32 { return -1; } ================================================ FILE: core/prelude/copy.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception package Core library "prelude/copy"; // Copying an object, which is a conversion from a value representation to an // initializing representation. interface Copy { fn Op[self: Self]() -> Self; } private fn Bool() -> type = "bool.make_type"; private fn CharLiteral() -> type = "char_literal.make_type"; private fn FloatLiteral() -> type = "float_literal.make_type"; private fn IntLiteral() -> type = "int_literal.make_type"; impl forall [T:! Copy] const T as Copy { fn Op[self: Self]() -> Self { return (self as T).(Copy.Op)() as const T; } } impl Bool() as Copy { fn Op[self: Self]() -> Self = "primitive_copy"; } impl CharLiteral() as Copy { fn Op[self: Self]() -> Self = "primitive_copy"; } impl FloatLiteral() as Copy { fn Op[self: Self]() -> Self = "primitive_copy"; } impl IntLiteral() as Copy { fn Op[self: Self]() -> Self = "primitive_copy"; } impl type as Copy { fn Op[self: Self]() -> Self = "primitive_copy"; } impl forall [T:! type] T* as Copy { fn Op[self: Self]() -> Self = "primitive_copy"; } // TODO: Implement tuple copy as a variadic generic impl. impl () as Copy { fn Op[self: Self]() -> Self = "no_op"; } impl forall [T:! Copy] (T,) as Copy { fn Op[self: Self]() -> Self { return (self.0.Op(),); } } impl forall [T:! Copy, U:! Copy] (T, U) as Copy { fn Op[self: Self]() -> Self { return (self.0.Op(), self.1.Op()); } } impl forall [T:! Copy, U:! Copy, V:! Copy] (T, U, V) as Copy { fn Op[self: Self]() -> Self { return (self.0.Op(), self.1.Op(), self.2.Op()); } } ================================================ FILE: core/prelude/default.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception package Core library "prelude/default"; import library "prelude/types/bool"; import library "prelude/types/int_literal"; // Provides the default value of an object. If implemented for a type `T`, this // is used to initialize declarations without an explicit initializer, such as // `var x: T;`, and leaves them in a fully-formed state. interface Default { fn Op() -> Self; } // Indicates that a type permits unformed initialization, which leaves the // object in a state where calling the destructor is valid but optional, and no // other operations on the object except for reinitialization are permitted. interface UnformedInit { // TODO: This should probably be: // let StructT:! type; // fn Op() -> StructT; // and should be able to initialize a subset of the fields. For now we always // leave the object uninitialized when it is in an unformed state. // See https://github.com/carbon-language/carbon-lang/pull/5913 } // Implementations for some builtin types. These need to be here to satisfy the // orphan rule because these builtin types have no associated library of their // own. impl bool as UnformedInit {} impl forall [T:! type] T* as UnformedInit {} impl forall [T:! UnformedInit, N:! IntLiteral()] array(T, N) as UnformedInit {} // TODO: Generalize these to apply to tuples and structs containing only // `UnformedInit` types. impl () as UnformedInit {} impl {} as UnformedInit {} // Provides a default, possibly unformed, value of an object. This should not be // implemented directly. Instead, implement `Default` to provide a fully-formed // state or (eventually) `UnformedInit` to provide an unformed state. interface DefaultOrUnformed { // TODO: This should return `MaybeUnformed(Self)` once that is supported. fn Op() -> Self; } final impl forall [T:! Default] T as DefaultOrUnformed { fn Op() -> Self { return T.Op(); } } impl forall [T:! UnformedInit] T as DefaultOrUnformed { fn Op() -> Self = "make_uninitialized"; } ================================================ FILE: core/prelude/destroy.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception package Core library "prelude/destroy"; // TODO: Add `Destructor`, as in: // interface Destructor { // private fn Op[ref self: Self](); // } // Destroys objects. This will invoke `Destructor` impls recursively on members; // it does not deallocate memory. interface Destroy { fn Op[ref self: Self](); } ================================================ FILE: core/prelude/iterate.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception package Core library "prelude/iterate"; export import library "prelude/copy"; export import library "prelude/destroy"; export import library "prelude/types"; export import library "prelude/operators"; interface Iterate { // TODO: Support iterating ranges of non-copyable values. let ElementType:! Copy & Destroy; let CursorType:! type; fn NewCursor[self: Self]() -> CursorType; fn Next[self: Self](cursor: CursorType*) -> Optional(ElementType); } impl forall [T:! Copy & Destroy, N:! IntLiteral()] array(T, N) as Iterate where .ElementType = T and .CursorType = i32 { fn NewCursor[unused self: Self]() -> i32 { return 0; } fn Next[self: Self](cursor: i32*) -> Optional(T) { if (*cursor < N) { ++*cursor; return Optional(T).Some(self[*cursor - 1]); } else { return Optional(T).None(); } } } ================================================ FILE: core/prelude/operators/arithmetic.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception package Core library "prelude/operators/arithmetic"; import library "prelude/types/int_literal"; // TODO: Per the design, the associated type `Result` in each of these // interfaces should have a default value of `Self`: // // default let Result:! type = Self; // TODO: Per the design, for each *With interface there should also be a // non-With named constraint, such as: // // constraint Add { // extend require impls AddWith(Self) where .Result = Self; // } // Addition: `a + b`. interface AddWith(Other:! type) { let Result:! type; fn Op[self: Self](other: Other) -> Result; } // Addition with assignment: `a += b`. interface AddAssignWith(Other:! type) { fn Op[ref self: Self](other: Other); } // Increment: `++a`. interface Inc { fn Op[ref self: Self](); } // Negation: `-a`. interface Negate { let Result:! type; fn Op[self: Self]() -> Result; } // Subtraction: `a - b`. interface SubWith(Other:! type) { let Result:! type; fn Op[self: Self](other: Other) -> Result; } // Subtraction with assignment: `a -= b`. interface SubAssignWith(Other:! type) { fn Op[ref self: Self](other: Other); } // Decrement: `--a`. interface Dec { fn Op[ref self: Self](); } // Multiplication: `a * b`. interface MulWith(Other:! type) { let Result:! type; fn Op[self: Self](other: Other) -> Result; } // Multiplication with assignment: `a *= b`. interface MulAssignWith(Other:! type) { fn Op[ref self: Self](other: Other); } // Division: `a / b`. interface DivWith(Other:! type) { let Result:! type; fn Op[self: Self](other: Other) -> Result; } // Division with assignment: `a /= b`. interface DivAssignWith(Other:! type) { fn Op[ref self: Self](other: Other); } // Modulo: `a % b`. interface ModWith(Other:! type) { let Result:! type; fn Op[self: Self](other: Other) -> Result; } // Modulo with assignment: `a %= b`. interface ModAssignWith(Other:! type) { fn Op[ref self: Self](other: Other); } // Operations for IntLiteral. These need to be here because IntLiteral has no // associated library of its own. impl IntLiteral() as AddWith(Self) where .Result = Self { fn Op[self: Self](other: Self) -> Self = "int.sadd"; } impl IntLiteral() as DivWith(Self) where .Result = Self { fn Op[self: Self](other: Self) -> Self = "int.sdiv"; } impl IntLiteral() as ModWith(Self) where .Result = Self { fn Op[self: Self](other: Self) -> Self = "int.smod"; } impl IntLiteral() as MulWith(Self) where .Result = Self { fn Op[self: Self](other: Self) -> Self = "int.smul"; } impl IntLiteral() as Negate where .Result = Self { fn Op[self: Self]() -> Self = "int.snegate"; } impl IntLiteral() as SubWith(Self) where .Result = Self { fn Op[self: Self](other: Self) -> Self = "int.ssub"; } ================================================ FILE: core/prelude/operators/as.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception package Core library "prelude/operators/as"; import library "prelude/copy"; interface UnsafeAs(Dest:! type) { fn Convert[self: Self]() -> Dest; } interface As(Dest:! type) { // TODO: extend UnsafeAs(Dest); fn Convert[self: Self]() -> Dest; } interface ImplicitAs(Dest:! type) { // TODO: extend As(Dest); fn Convert[self: Self]() -> Dest; } // Workaround: ImplicitAs extends As. impl forall [U:! type, T:! ImplicitAs(U)] T as As(U) { fn Convert[self: Self]() -> U { return self.Convert(); } } // Workaround: As extends UnsafeAs. impl forall [U:! type, T:! As(U)] T as UnsafeAs(U) { fn Convert[self: Self]() -> U { return self.Convert(); } } // Copyable types have an identity conversion that performs a copy. impl forall [T:! Copy] T as ImplicitAs(T) { fn Convert[self: Self]() -> Self { return self.(Copy.Op)(); } } // `const` can be added and removed when converting a value. impl forall [T:! type, U:! ImplicitAs(T)] U as ImplicitAs(const T) { fn Convert[self: U]() -> const T { return self.Convert(); } } impl forall [T:! type, U:! ImplicitAs(T)] const U as ImplicitAs(T) { fn Convert[self: const U]() -> T { return (self as U).Convert(); } } // Pointer types can be unsafely cast to other pointer types. // TODO: Should `unsafe as` be able to remove `const`? impl forall [T:! type, U:! type] T* as UnsafeAs(U*) { fn Convert[self: T*]() -> U* = "pointer.unsafe_convert"; } interface IntFitsIn(Dest:! type) {} ================================================ FILE: core/prelude/operators/bitwise.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception package Core library "prelude/operators/bitwise"; import library "prelude/types/int_literal"; // TODO: Per the design, the associated type `Result` in each of these // interfaces should have a default value of `Self`: // // default let Result:! type = Self; // TODO: Per the design, for each *With interface there should also be a // non-With named constraint, such as: // // constraint BitAnd { // extend require impls BitAndWith(Self) where .Result = Self; // } // Bit complement: `^a`. interface BitComplement { let Result:! type; fn Op[self: Self]() -> Result; } // Bitwise AND: `a & b`. interface BitAndWith(Other:! type) { let Result:! type; fn Op[self: Self](other: Other) -> Result; } // Bitwise AND with assignment: `a &= b`. interface BitAndAssignWith(Other:! type) { fn Op[ref self: Self](other: Other); } // Bitwise OR: `a | b`. interface BitOrWith(Other:! type) { let Result:! type; fn Op[self: Self](other: Other) -> Result; } // Bitwise OR with assignment: `a |= b`. interface BitOrAssignWith(Other:! type) { fn Op[ref self: Self](other: Other); } // Bitwise XOR: `a ^ b`. interface BitXorWith(Other:! type) { let Result:! type; fn Op[self: Self](other: Other) -> Result; } // Bitwise XOR with assignment: `a ^= b`. interface BitXorAssignWith(Other:! type) { fn Op[ref self: Self](other: Other); } // Left shift: `a << b`. interface LeftShiftWith(Other:! type) { let Result:! type; fn Op[self: Self](other: Other) -> Result; } // Left shift with assignment: `a <<= b`. interface LeftShiftAssignWith(Other:! type) { fn Op[ref self: Self](other: Other); } // Right shift: `a >> b`. interface RightShiftWith(Other:! type) { let Result:! type; fn Op[self: Self](other: Other) -> Result; } // Right shift with assignment: `a >>= b`. interface RightShiftAssignWith(Other:! type) { fn Op[ref self: Self](other: Other); } // Operations for IntLiteral. These need to be here because IntLiteral has no // associated library of its own. impl IntLiteral() as BitAndWith(Self) where .Result = Self { fn Op[self: Self](other: Self) -> Self = "int.and"; } impl IntLiteral() as BitComplement where .Result = Self { fn Op[self: Self]() -> Self = "int.complement"; } impl IntLiteral() as BitOrWith(Self) where .Result = Self { fn Op[self: Self](other: Self) -> Self = "int.or"; } impl IntLiteral() as BitXorWith(Self) where .Result = Self { fn Op[self: Self](other: Self) -> Self = "int.xor"; } impl IntLiteral() as LeftShiftWith(Self) where .Result = Self { fn Op[self: Self](other: Self) -> Self = "int.left_shift"; } impl IntLiteral() as RightShiftWith(Self) where .Result = Self { fn Op[self: Self](other: Self) -> Self = "int.right_shift"; } // Operations for `type`. These need to be here because `type` has no // associated library of its own. // Facet type combination. impl type as BitAndWith(Self) where .Result = Self { fn Op[self: Self](other: Self) -> Self = "type.and"; } ================================================ FILE: core/prelude/operators/comparison.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception package Core library "prelude/operators/comparison"; export import library "prelude/types/bool"; import library "prelude/types/int_literal"; // TODO: Per the design, for each *With interface there should also be a // non-With named constraint, such as: // // constraint Eq { // extend require impls EqWith(Self); // } // Equality comparison: `a == b` and `a != b`. interface EqWith(Other:! type) { fn Equal[self: Self](other: Other) -> bool; fn NotEqual[self: Self](other: Other) -> bool; } // Relational comparison: `a < b`, `a <= b`, `a > b`, `a >= b`. interface OrderedWith(Other:! type) { // TODO: fn Compare fn Less[self: Self](other: Other) -> bool; fn LessOrEquivalent[self: Self](other: Other) -> bool; fn Greater[self: Self](other: Other) -> bool; fn GreaterOrEquivalent[self: Self](other: Other) -> bool; } // Equality comparison for `bool`. // Note that this must be provided in this library as `bool` doesn't have any // associated libraries of its own. impl bool as EqWith(Self) { fn Equal[self: Self](other: Self) -> bool = "bool.eq"; fn NotEqual[self: Self](other: Self) -> bool = "bool.neq"; } // Operations for IntLiteral. These need to be here because IntLiteral has no // associated library of its own. impl IntLiteral() as EqWith(Self) { fn Equal[self: Self](other: Self) -> bool = "int.eq"; fn NotEqual[self: Self](other: Self) -> bool = "int.neq"; } impl IntLiteral() as OrderedWith(Self) { // TODO: fn Compare fn Less[self: Self](other: Self) -> bool = "int.less"; fn LessOrEquivalent[self: Self](other: Self) -> bool = "int.less_eq"; fn Greater[self: Self](other: Self) -> bool = "int.greater"; fn GreaterOrEquivalent[self: Self](other: Self) -> bool = "int.greater_eq"; } ================================================ FILE: core/prelude/operators/deref.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception package Core library "prelude/operators/deref"; // TODO: Align with https://docs.carbon-lang.dev/docs/design/values.html#dereferencing-customization. interface CppUnsafeDeref { let Result:! type; fn Op[self: Self]() -> ref Result; } ================================================ FILE: core/prelude/operators/index.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception package Core library "prelude/operators/index"; interface IndexWith(SubscriptType:! type) { let ElementType:! type; fn At[self: Self](subscript: SubscriptType) -> ElementType; } ================================================ FILE: core/prelude/operators.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception package Core library "prelude/operators"; export import library "prelude/operators/arithmetic"; export import library "prelude/operators/as"; export import library "prelude/operators/bitwise"; export import library "prelude/operators/comparison"; export import library "prelude/operators/index"; export import library "prelude/operators/deref"; ================================================ FILE: core/prelude/types/bool.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception package Core library "prelude/types/bool"; fn Bool() -> type = "bool.make_type"; ================================================ FILE: core/prelude/types/char.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception package Core library "prelude/types/char"; import library "prelude/copy"; import library "prelude/default"; import library "prelude/destroy"; import library "prelude/operators"; import library "prelude/types/uint"; import library "prelude/types/int"; import library "prelude/types/int_literal"; fn CharLiteral() -> type = "char_literal.make_type"; class Char { adapt u8; } impl Char as UnformedInit {} impl Char as Copy { fn Op[self: Self]() -> Self = "primitive_copy"; } impl CharLiteral() as ImplicitAs(Char) { fn Convert[self: Self]() -> Char = "char.convert_checked"; } // TODO: Remove these once ImplicitAs extends As. impl CharLiteral() as As(Char) { fn Convert[self: Self]() -> Char = "char.convert_checked"; } impl forall [From:! IntLiteral()] UInt(From) as As(Char) { fn Convert[self: Self]() -> Char = "int.convert_char"; } ================================================ FILE: core/prelude/types/cpp/int.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception package Core library "prelude/types/cpp/int"; import library "prelude/copy"; import library "prelude/default"; import library "prelude/operators"; import library "prelude/types/int"; import library "prelude/types/int_literal"; import library "prelude/types/uint"; namespace CppCompat; class CppCompat.Long32 { adapt i32; } class CppCompat.ULong32 { adapt u32; } class CppCompat.LongLong64 { adapt i64; } class CppCompat.ULongLong64 { adapt u64; } impl CppCompat.Long32 as UnformedInit {} impl CppCompat.ULong32 as UnformedInit {} impl CppCompat.LongLong64 as UnformedInit {} impl CppCompat.ULongLong64 as UnformedInit {} // Copy impl CppCompat.Long32 as Copy { fn Op[self: Self]() -> Self = "primitive_copy"; } impl CppCompat.ULong32 as Copy { fn Op[self: Self]() -> Self = "primitive_copy"; } impl CppCompat.LongLong64 as Copy { fn Op[self: Self]() -> Self = "primitive_copy"; } impl CppCompat.ULongLong64 as Copy { fn Op[self: Self]() -> Self = "primitive_copy"; } // Conversions. impl IntLiteral() as ImplicitAs(CppCompat.Long32) { fn Convert[self: Self]() -> CppCompat.Long32 = "int.convert_checked"; } // TODO: Remove this once `ImplicitAs` extends `As`. impl IntLiteral() as As(CppCompat.Long32) { fn Convert[self: Self]() -> CppCompat.Long32 = "int.convert_checked"; } final impl CppCompat.Long32 as ImplicitAs(IntLiteral()) { fn Convert[self: Self]() -> IntLiteral() = "int.convert_checked"; } // TODO: ImplicitAs from Int(N) to Long32 if N < 32. impl i32 as ImplicitAs(CppCompat.Long32) { fn Convert[self: Self]() -> CppCompat.Long32 = "int.convert"; } // TODO: Generalize ImplicitAs from Long32 to Int(N) for all N > 32. // N == 32 is explicitly skipped, as Long32 is considered to // be between i32 and i33 (details: // https://github.com/carbon-language/carbon-lang/issues/6275#issuecomment-3488010485). // This follows the rule that implicit conversions exist for all iN -> iM if // M > N. Otherwise, operations like i32 + Cpp.long result in i32, instead of // Cpp.long, which differs from the result for Cpp.long + i32 -> Cpp.long, as // the best match for the left operand is being selected. final impl CppCompat.Long32 as ImplicitAs(i64) { fn Convert[self: Self]() -> i64 = "int.convert"; } impl IntLiteral() as ImplicitAs(CppCompat.ULong32) { fn Convert[self: Self]() -> CppCompat.ULong32 = "int.convert_checked"; } // TODO: Remove this once `ImplicitAs` extends `As`. impl IntLiteral() as As(CppCompat.ULong32) { fn Convert[self: Self]() -> CppCompat.ULong32 = "int.convert_checked"; } final impl CppCompat.ULong32 as ImplicitAs(IntLiteral()) { fn Convert[self: Self]() -> IntLiteral() = "int.convert_checked"; } // TODO: ImplicitAs from UInt(N) to ULong32 if N < 32. impl u32 as ImplicitAs(CppCompat.ULong32) { fn Convert[self: Self]() -> CppCompat.ULong32 = "int.convert"; } // TODO: ImplicitAs from ULong32 to UInt(N) if N > 32. final impl CppCompat.ULong32 as ImplicitAs(u64) { fn Convert[self: Self]() -> u64 = "int.convert"; } impl IntLiteral() as ImplicitAs(CppCompat.LongLong64) { fn Convert[self: Self]() -> CppCompat.LongLong64 = "int.convert_checked"; } // TODO: Remove this once `ImplicitAs` extends `As`. impl IntLiteral() as As(CppCompat.LongLong64) { fn Convert[self: Self]() -> CppCompat.LongLong64 = "int.convert_checked"; } final impl CppCompat.LongLong64 as ImplicitAs(IntLiteral()) { fn Convert[self: Self]() -> IntLiteral() = "int.convert_checked"; } // TODO: ImplicitAs from Int(N) to LongLong64 if N < 64. impl i64 as ImplicitAs(CppCompat.LongLong64) { fn Convert[self: Self]() -> CppCompat.LongLong64 = "int.convert"; } // TODO: ImplicitAs from LongLong64 to Int(N) if N > 64. // N == 64 is explicitly skipped as LongLong64 is considered to // be between i64 and i65 (details: // https://github.com/carbon-language/carbon-lang/issues/6275#issuecomment-3488010485). final impl CppCompat.LongLong64 as ImplicitAs(i128) { fn Convert[self: Self]() -> i128 = "int.convert"; } impl IntLiteral() as ImplicitAs(CppCompat.ULongLong64) { fn Convert[self: Self]() -> CppCompat.ULongLong64 = "int.convert_checked"; } // TODO: Remove this once `ImplicitAs` extends `As`. impl IntLiteral() as As(CppCompat.ULongLong64) { fn Convert[self: Self]() -> CppCompat.ULongLong64 = "int.convert_checked"; } final impl CppCompat.ULongLong64 as ImplicitAs(IntLiteral()) { fn Convert[self: Self]() -> IntLiteral() = "int.convert_checked"; } // TODO: ImplicitAs from UInt(N) to ULongLong64 if N < 64. impl u64 as ImplicitAs(CppCompat.ULongLong64) { fn Convert[self: Self]() -> CppCompat.ULongLong64 = "int.convert"; } // TODO: ImplicitAs from ULongLong64 to UInt(N) if N > 64. final impl CppCompat.ULongLong64 as ImplicitAs(u128) { fn Convert[self: Self]() -> u128 = "int.convert"; } // TODO: As from Long32 to Int(N) if N < 32. // TODO: As from ULong32 to UInt(N) if N < 32. // TODO: As from LongLong64 to Int(N) if N < 64. // TODO: As from ULongLong64 to UInt(N) if N < 64. // TODO: As from Int(N) to Long32 if N > 32. // TODO: As from UInt(N) to ULong32 if N > 32. // TODO: As from Int(N) to LongLong64 if N > 64. // TODO: As from UInt(N) to ULongLong64 if N > 64. // Comparisons. // - Homogeneous. // - CppCompat.T vs CppCompat.T final impl CppCompat.Long32 as EqWith(CppCompat.Long32) { fn Equal[self: Self](other: Self) -> bool = "int.eq"; fn NotEqual[self: Self](other: Self) -> bool = "int.neq"; } final impl CppCompat.Long32 as OrderedWith(CppCompat.Long32) { fn Less[self: Self](other: Self) -> bool = "int.less"; fn LessOrEquivalent[self: Self](other: Self) -> bool = "int.less_eq"; fn Greater[self: Self](other: Self) -> bool = "int.greater"; fn GreaterOrEquivalent[self: Self](other: Self) -> bool = "int.greater_eq"; } final impl CppCompat.LongLong64 as EqWith(CppCompat.LongLong64) { fn Equal[self: Self](other: Self) -> bool = "int.eq"; fn NotEqual[self: Self](other: Self) -> bool = "int.neq"; } final impl CppCompat.LongLong64 as OrderedWith(CppCompat.LongLong64) { fn Less[self: Self](other: Self) -> bool = "int.less"; fn LessOrEquivalent[self: Self](other: Self) -> bool = "int.less_eq"; fn Greater[self: Self](other: Self) -> bool = "int.greater"; fn GreaterOrEquivalent[self: Self](other: Self) -> bool = "int.greater_eq"; } // - Heterogeneous. // - CppCompat.T on left-hand side. impl forall [T:! ImplicitAs(CppCompat.Long32)] CppCompat.Long32 as EqWith(T) { fn Equal[self: Self](other: Self) -> bool = "int.eq"; fn NotEqual[self: Self](other: Self) -> bool = "int.neq"; } impl forall [T:! ImplicitAs(CppCompat.Long32)] CppCompat.Long32 as OrderedWith(T) { fn Less[self: Self](other: Self) -> bool = "int.less"; fn LessOrEquivalent[self: Self](other: Self) -> bool = "int.less_eq"; fn Greater[self: Self](other: Self) -> bool = "int.greater"; fn GreaterOrEquivalent[self: Self](other: Self) -> bool = "int.greater_eq"; } impl forall [T:! ImplicitAs(CppCompat.LongLong64)] CppCompat.LongLong64 as EqWith(T) { fn Equal[self: Self](other: Self) -> bool = "int.eq"; fn NotEqual[self: Self](other: Self) -> bool = "int.neq"; } impl forall [T:! ImplicitAs(CppCompat.LongLong64)] CppCompat.LongLong64 as OrderedWith(T) { fn Less[self: Self](other: Self) -> bool = "int.less"; fn LessOrEquivalent[self: Self](other: Self) -> bool = "int.less_eq"; fn Greater[self: Self](other: Self) -> bool = "int.greater"; fn GreaterOrEquivalent[self: Self](other: Self) -> bool = "int.greater_eq"; } // - CppCompat.T on right-hand side. impl forall [T:! ImplicitAs(CppCompat.Long32)] T as EqWith(CppCompat.Long32) { fn Equal[self: CppCompat.Long32](other: CppCompat.Long32) -> bool = "int.eq"; fn NotEqual[self: CppCompat.Long32](other: CppCompat.Long32) -> bool = "int.neq"; } impl forall [T:! ImplicitAs(CppCompat.Long32)] T as OrderedWith(CppCompat.Long32) { fn Less[self: CppCompat.Long32](other: CppCompat.Long32) -> bool = "int.less"; fn LessOrEquivalent[self: CppCompat.Long32](other: CppCompat.Long32) -> bool = "int.less_eq"; fn Greater[self: CppCompat.Long32](other: CppCompat.Long32) -> bool = "int.greater"; fn GreaterOrEquivalent[self: CppCompat.Long32](other: CppCompat.Long32) -> bool = "int.greater_eq"; } impl forall [T:! ImplicitAs(CppCompat.LongLong64)] T as EqWith(CppCompat.LongLong64) { fn Equal[self: CppCompat.LongLong64]( other: CppCompat.LongLong64) -> bool = "int.eq"; fn NotEqual[self: CppCompat.LongLong64]( other: CppCompat.LongLong64) -> bool = "int.neq"; } impl forall [T:! ImplicitAs(CppCompat.LongLong64)] T as OrderedWith(CppCompat.LongLong64) { fn Less[self: CppCompat.LongLong64]( other: CppCompat.LongLong64) -> bool = "int.less"; fn LessOrEquivalent[self: CppCompat.LongLong64]( other: CppCompat.LongLong64) -> bool = "int.less_eq"; fn Greater[self: CppCompat.LongLong64]( other: CppCompat.LongLong64) -> bool = "int.greater"; fn GreaterOrEquivalent[self: CppCompat.LongLong64]( other: CppCompat.LongLong64) -> bool = "int.greater_eq"; } // TODO: Comparisons for ULong32, ULongLong64. // Arithmetic. // - Homogeneous. // - CppCompat.Long32 final impl CppCompat.Long32 as Negate where .Result = Self { fn Op[self: Self]() -> Self = "int.snegate"; } final impl CppCompat.Long32 as AddWith(Self) where .Result = Self { fn Op[self: Self](other: Self) -> Self = "int.sadd"; } final impl CppCompat.Long32 as SubWith(Self) where .Result = Self { fn Op[self: Self](other: Self) -> Self = "int.ssub"; } final impl CppCompat.Long32 as MulWith(Self) where .Result = Self { fn Op[self: Self](other: Self) -> Self = "int.smul"; } final impl CppCompat.Long32 as DivWith(Self) where .Result = Self { fn Op[self: Self](other: Self) -> Self = "int.sdiv"; } final impl CppCompat.Long32 as ModWith(Self) where .Result = Self { fn Op[self: Self](other: Self) -> Self = "int.smod"; } // - CppCompat.LongLong64 final impl CppCompat.LongLong64 as Negate where .Result = Self { fn Op[self: Self]() -> Self = "int.snegate"; } final impl CppCompat.LongLong64 as AddWith(Self) where .Result = Self { fn Op[self: Self](other: Self) -> Self = "int.sadd"; } final impl CppCompat.LongLong64 as SubWith(Self) where .Result = Self { fn Op[self: Self](other: Self) -> Self = "int.ssub"; } final impl CppCompat.LongLong64 as MulWith(Self) where .Result = Self { fn Op[self: Self](other: Self) -> Self = "int.smul"; } final impl CppCompat.LongLong64 as DivWith(Self) where .Result = Self { fn Op[self: Self](other: Self) -> Self = "int.sdiv"; } final impl CppCompat.LongLong64 as ModWith(Self) where .Result = Self { fn Op[self: Self](other: Self) -> Self = "int.smod"; } // - Heterogeneous. // - CppCompat.Long32 on left-hand side. impl forall [T:! ImplicitAs(CppCompat.Long32)] CppCompat.Long32 as AddWith(T) where .Result = CppCompat.Long32 { fn Op[self: CppCompat.Long32](other: CppCompat.Long32) -> CppCompat.Long32 = "int.sadd"; } impl forall [T:! ImplicitAs(CppCompat.Long32)] CppCompat.Long32 as SubWith(T) where .Result = CppCompat.Long32 { fn Op[self: CppCompat.Long32](other: CppCompat.Long32) -> CppCompat.Long32 = "int.ssub"; } impl forall [T:! ImplicitAs(CppCompat.Long32)] CppCompat.Long32 as MulWith(T) where .Result = CppCompat.Long32 { fn Op[self: CppCompat.Long32](other: CppCompat.Long32) -> CppCompat.Long32 = "int.smul"; } impl forall [T:! ImplicitAs(CppCompat.Long32)] CppCompat.Long32 as DivWith(T) where .Result = CppCompat.Long32 { fn Op[self: CppCompat.Long32](other: CppCompat.Long32) -> CppCompat.Long32 = "int.sdiv"; } impl forall [T:! ImplicitAs(CppCompat.Long32)] CppCompat.Long32 as ModWith(T) where .Result = CppCompat.Long32 { fn Op[self: CppCompat.Long32](other: CppCompat.Long32) -> CppCompat.Long32 = "int.smod"; } // - CppCompat.LongLong64 on left-hand side. impl forall [T:! ImplicitAs(CppCompat.LongLong64)] CppCompat.LongLong64 as AddWith(T) where .Result = CppCompat.LongLong64 { fn Op[self: CppCompat.LongLong64]( other: CppCompat.LongLong64) -> CppCompat.LongLong64 = "int.sadd"; } impl forall [T:! ImplicitAs(CppCompat.LongLong64)] CppCompat.LongLong64 as SubWith(T) where .Result = CppCompat.LongLong64 { fn Op[self: CppCompat.LongLong64]( other: CppCompat.LongLong64) -> CppCompat.LongLong64 = "int.ssub"; } impl forall [T:! ImplicitAs(CppCompat.LongLong64)] CppCompat.LongLong64 as MulWith(T) where .Result = CppCompat.LongLong64 { fn Op[self: CppCompat.LongLong64]( other: CppCompat.LongLong64) -> CppCompat.LongLong64 = "int.smul"; } impl forall [T:! ImplicitAs(CppCompat.LongLong64)] CppCompat.LongLong64 as DivWith(T) where .Result = CppCompat.LongLong64 { fn Op[self: CppCompat.LongLong64]( other: CppCompat.LongLong64) -> CppCompat.LongLong64 = "int.sdiv"; } impl forall [T:! ImplicitAs(CppCompat.LongLong64)] CppCompat.LongLong64 as ModWith(T) where .Result = CppCompat.LongLong64 { fn Op[self: CppCompat.LongLong64]( other: CppCompat.LongLong64) -> CppCompat.LongLong64 = "int.smod"; } // - CppCompat.Long32 on right-hand side. impl forall [T:! ImplicitAs(CppCompat.Long32)] T as AddWith(CppCompat.Long32) where .Result = CppCompat.Long32 { fn Op[self: CppCompat.Long32](other: CppCompat.Long32) -> CppCompat.Long32 = "int.sadd"; } impl forall [T:! ImplicitAs(CppCompat.Long32)] T as SubWith(CppCompat.Long32) where .Result = CppCompat.Long32 { fn Op[self: CppCompat.Long32](other: CppCompat.Long32) -> CppCompat.Long32 = "int.ssub"; } impl forall [T:! ImplicitAs(CppCompat.Long32)] T as MulWith(CppCompat.Long32) where .Result = CppCompat.Long32 { fn Op[self: CppCompat.Long32](other: CppCompat.Long32) -> CppCompat.Long32 = "int.smul"; } impl forall [T:! ImplicitAs(CppCompat.Long32)] T as DivWith(CppCompat.Long32) where .Result = CppCompat.Long32 { fn Op[self: CppCompat.Long32](other: CppCompat.Long32) -> CppCompat.Long32 = "int.sdiv"; } impl forall [T:! ImplicitAs(CppCompat.Long32)] T as ModWith(CppCompat.Long32) where .Result = CppCompat.Long32 { fn Op[self: CppCompat.Long32](other: CppCompat.Long32) -> CppCompat.Long32 = "int.smod"; } // - CppCompat.LongLong64 on right-hand side. impl forall [T:! ImplicitAs(CppCompat.LongLong64)] T as AddWith(CppCompat.LongLong64) where .Result = CppCompat.LongLong64 { fn Op[self: CppCompat.LongLong64]( other: CppCompat.LongLong64) -> CppCompat.LongLong64 = "int.sadd"; } impl forall [T:! ImplicitAs(CppCompat.LongLong64)] T as SubWith(CppCompat.LongLong64) where .Result = CppCompat.LongLong64 { fn Op[self: CppCompat.LongLong64]( other: CppCompat.LongLong64) -> CppCompat.LongLong64 = "int.ssub"; } impl forall [T:! ImplicitAs(CppCompat.LongLong64)] T as MulWith(CppCompat.LongLong64) where .Result = CppCompat.LongLong64 { fn Op[self: CppCompat.LongLong64]( other: CppCompat.LongLong64) -> CppCompat.LongLong64 = "int.smul"; } impl forall [T:! ImplicitAs(CppCompat.LongLong64)] T as DivWith(CppCompat.LongLong64) where .Result = CppCompat.LongLong64 { fn Op[self: CppCompat.LongLong64]( other: CppCompat.LongLong64) -> CppCompat.LongLong64 = "int.sdiv"; } impl forall [T:! ImplicitAs(CppCompat.LongLong64)] T as ModWith(CppCompat.LongLong64) where .Result = CppCompat.LongLong64 { fn Op[self: CppCompat.LongLong64]( other: CppCompat.LongLong64) -> CppCompat.LongLong64 = "int.smod"; } // TODO: ULong32, ULongLong64: arithmetic operators. // Bitwise operators. // - Homogeneous. // - CppCompat.Long32 final impl CppCompat.Long32 as BitComplement where .Result = Self { fn Op[self: Self]() -> Self = "int.complement"; } final impl CppCompat.Long32 as BitAndWith(Self) where .Result = Self { fn Op[self: Self](other: Self) -> Self = "int.and"; } final impl CppCompat.Long32 as BitOrWith(Self) where .Result = Self { fn Op[self: Self](other: Self) -> Self = "int.or"; } final impl CppCompat.Long32 as BitXorWith(Self) where .Result = Self { fn Op[self: Self](other: Self) -> Self = "int.xor"; } final impl CppCompat.Long32 as LeftShiftWith(Self) where .Result = Self { fn Op[self: Self](other: Self) -> Self = "int.left_shift"; } final impl CppCompat.Long32 as RightShiftWith(Self) where .Result = Self { fn Op[self: Self](other: Self) -> Self = "int.right_shift"; } // - CppCompat.LongLong64 final impl CppCompat.LongLong64 as BitComplement where .Result = Self { fn Op[self: Self]() -> Self = "int.complement"; } final impl CppCompat.LongLong64 as BitAndWith(Self) where .Result = Self { fn Op[self: Self](other: Self) -> Self = "int.and"; } final impl CppCompat.LongLong64 as BitOrWith(Self) where .Result = Self { fn Op[self: Self](other: Self) -> Self = "int.or"; } final impl CppCompat.LongLong64 as BitXorWith(Self) where .Result = Self { fn Op[self: Self](other: Self) -> Self = "int.xor"; } final impl CppCompat.LongLong64 as LeftShiftWith(Self) where .Result = Self { fn Op[self: Self](other: Self) -> Self = "int.left_shift"; } final impl CppCompat.LongLong64 as RightShiftWith(Self) where .Result = Self { fn Op[self: Self](other: Self) -> Self = "int.right_shift"; } // - Heterogeneous. // - CppCompat.Long32 on left-hand side. impl forall [T:! ImplicitAs(CppCompat.Long32)] CppCompat.Long32 as BitAndWith(T) where .Result = CppCompat.Long32 { fn Op[self: CppCompat.Long32]( other:CppCompat.Long32) -> CppCompat.Long32 = "int.and"; } impl forall [T:! ImplicitAs(CppCompat.Long32)] CppCompat.Long32 as BitOrWith(T) where .Result = CppCompat.Long32 { fn Op[self: CppCompat.Long32]( other: CppCompat.Long32) -> CppCompat.Long32 = "int.or"; } impl forall [T:! ImplicitAs(CppCompat.Long32)] CppCompat.Long32 as BitXorWith(T) where .Result = CppCompat.Long32 { fn Op[self: CppCompat.Long32]( other: CppCompat.Long32) -> CppCompat.Long32 = "int.xor"; } impl forall [T:! ImplicitAs(CppCompat.Long32)] CppCompat.Long32 as LeftShiftWith(T) where .Result = CppCompat.Long32 { fn Op[self: CppCompat.Long32]( other: CppCompat.Long32) -> CppCompat.Long32 = "int.left_shift"; } impl forall [T:! ImplicitAs(CppCompat.Long32)] CppCompat.Long32 as RightShiftWith(T) where .Result = CppCompat.Long32 { fn Op[self: CppCompat.Long32]( other: CppCompat.Long32) -> CppCompat.Long32 = "int.right_shift"; } // - CppCompat.LongLong64 on left-hand side. impl forall [T:! ImplicitAs(CppCompat.LongLong64)] CppCompat.LongLong64 as BitAndWith(T) where .Result = CppCompat.LongLong64 { fn Op[self: CppCompat.LongLong64]( other:CppCompat.LongLong64) -> CppCompat.LongLong64 = "int.and"; } impl forall [T:! ImplicitAs(CppCompat.LongLong64)] CppCompat.LongLong64 as BitOrWith(T) where .Result = CppCompat.LongLong64 { fn Op[self: CppCompat.LongLong64]( other: CppCompat.LongLong64) -> CppCompat.LongLong64 = "int.or"; } impl forall [T:! ImplicitAs(CppCompat.LongLong64)] CppCompat.LongLong64 as BitXorWith(T) where .Result = CppCompat.LongLong64 { fn Op[self: CppCompat.LongLong64]( other: CppCompat.LongLong64) -> CppCompat.LongLong64 = "int.xor"; } impl forall [T:! ImplicitAs(CppCompat.LongLong64)] CppCompat.LongLong64 as LeftShiftWith(T) where .Result = CppCompat.LongLong64 { fn Op[self: CppCompat.LongLong64]( other: CppCompat.LongLong64) -> CppCompat.LongLong64 = "int.left_shift"; } impl forall [T:! ImplicitAs(CppCompat.LongLong64)] CppCompat.LongLong64 as RightShiftWith(T) where .Result = CppCompat.LongLong64 { fn Op[self: CppCompat.LongLong64]( other: CppCompat.LongLong64) -> CppCompat.LongLong64 = "int.right_shift"; } // - CppCompat.Long32 on right-hand side. impl forall [T:! ImplicitAs(CppCompat.Long32)] T as BitAndWith(CppCompat.Long32) where .Result = CppCompat.Long32 { fn Op[self: CppCompat.Long32]( other: CppCompat.Long32) -> CppCompat.Long32 = "int.and"; } impl forall [T:! ImplicitAs(CppCompat.Long32)] T as BitOrWith(CppCompat.Long32) where .Result = CppCompat.Long32 { fn Op[self: CppCompat.Long32]( other: CppCompat.Long32) -> CppCompat.Long32 = "int.or"; } impl forall [T:! ImplicitAs(CppCompat.Long32)] T as BitXorWith(CppCompat.Long32) where .Result = CppCompat.Long32 { fn Op[self: CppCompat.Long32]( other: CppCompat.Long32) -> CppCompat.Long32 = "int.xor"; } impl forall [T:! ImplicitAs(CppCompat.Long32)] T as LeftShiftWith(CppCompat.Long32) where .Result = CppCompat.Long32 { fn Op[self: CppCompat.Long32]( other: CppCompat.Long32) -> CppCompat.Long32 = "int.left_shift"; } impl forall [T:! ImplicitAs(CppCompat.Long32)] T as RightShiftWith(CppCompat.Long32) where .Result = CppCompat.Long32 { fn Op[self: CppCompat.Long32]( other: CppCompat.Long32) -> CppCompat.Long32 = "int.right_shift"; } // - CppCompat.LongLong64 on right-hand side. impl forall [T:! ImplicitAs(CppCompat.LongLong64)] T as BitAndWith(CppCompat.LongLong64) where .Result = CppCompat.LongLong64 { fn Op[self: CppCompat.LongLong64]( other: CppCompat.LongLong64) -> CppCompat.LongLong64 = "int.and"; } impl forall [T:! ImplicitAs(CppCompat.LongLong64)] T as BitOrWith(CppCompat.LongLong64) where .Result = CppCompat.LongLong64 { fn Op[self: CppCompat.LongLong64]( other: CppCompat.LongLong64) -> CppCompat.LongLong64 = "int.or"; } impl forall [T:! ImplicitAs(CppCompat.LongLong64)] T as BitXorWith(CppCompat.LongLong64) where .Result = CppCompat.LongLong64 { fn Op[self: CppCompat.LongLong64]( other: CppCompat.LongLong64) -> CppCompat.LongLong64 = "int.xor"; } impl forall [T:! ImplicitAs(CppCompat.LongLong64)] T as LeftShiftWith(CppCompat.LongLong64) where .Result = CppCompat.LongLong64 { fn Op[self: CppCompat.LongLong64]( other: CppCompat.LongLong64) -> CppCompat.LongLong64 = "int.left_shift"; } impl forall [T:! ImplicitAs(CppCompat.LongLong64)] T as RightShiftWith(CppCompat.LongLong64) where .Result = CppCompat.LongLong64 { fn Op[self: CppCompat.LongLong64]( other: CppCompat.LongLong64) -> CppCompat.LongLong64 = "int.right_shift"; } // TODO: ULong32, ULongLong64: bitwise operators. // Compound assignments. // Compound arithmetic assignments. // - CppCompat.Long32 impl forall [U:! ImplicitAs(CppCompat.Long32)] CppCompat.Long32 as AddAssignWith(U) { fn Op[ref self: Self](other: Self) = "int.sadd_assign"; } impl forall [U:! ImplicitAs(CppCompat.Long32)] CppCompat.Long32 as SubAssignWith(U) { fn Op[ref self: Self](other: Self) = "int.ssub_assign"; } impl forall [U:! ImplicitAs(CppCompat.Long32)] CppCompat.Long32 as MulAssignWith(U) { fn Op[ref self: Self](other: Self) = "int.smul_assign"; } impl forall [U:! ImplicitAs(CppCompat.Long32)] CppCompat.Long32 as DivAssignWith(U) { fn Op[ref self: Self](other: Self) = "int.sdiv_assign"; } impl forall [U:! ImplicitAs(CppCompat.Long32)] CppCompat.Long32 as ModAssignWith(U) { fn Op[ref self: Self](other: Self) = "int.smod_assign"; } // - CppCompat.LongLong64 impl forall [U:! ImplicitAs(CppCompat.LongLong64)] CppCompat.LongLong64 as AddAssignWith(U) { fn Op[ref self: Self](other: Self) = "int.sadd_assign"; } impl forall [U:! ImplicitAs(CppCompat.LongLong64)] CppCompat.LongLong64 as SubAssignWith(U) { fn Op[ref self: Self](other: Self) = "int.ssub_assign"; } impl forall [U:! ImplicitAs(CppCompat.LongLong64)] CppCompat.LongLong64 as MulAssignWith(U) { fn Op[ref self: Self](other: Self) = "int.smul_assign"; } impl forall [U:! ImplicitAs(CppCompat.LongLong64)] CppCompat.LongLong64 as DivAssignWith(U) { fn Op[ref self: Self](other: Self) = "int.sdiv_assign"; } impl forall [U:! ImplicitAs(CppCompat.LongLong64)] CppCompat.LongLong64 as ModAssignWith(U) { fn Op[ref self: Self](other: Self) = "int.smod_assign"; } // Compound bitwise assignments. // - CppCompat.Long32 impl forall [U:! ImplicitAs(CppCompat.Long32)] CppCompat.Long32 as BitAndAssignWith(U) { fn Op[ref self: Self](other: Self) = "int.and_assign"; } impl forall [U:! ImplicitAs(CppCompat.Long32)] CppCompat.Long32 as BitOrAssignWith(U) { fn Op[ref self: Self](other: Self) = "int.or_assign"; } impl forall [U:! ImplicitAs(CppCompat.Long32)] CppCompat.Long32 as BitXorAssignWith(U) { fn Op[ref self: Self](other: Self) = "int.xor_assign"; } impl forall [U:! ImplicitAs(CppCompat.Long32)] CppCompat.Long32 as LeftShiftAssignWith(U) { fn Op[ref self: Self](other: Self) = "int.left_shift_assign"; } impl forall [U:! ImplicitAs(CppCompat.Long32)] CppCompat.Long32 as RightShiftAssignWith(U) { fn Op[ref self: Self](other: Self) = "int.right_shift_assign"; } // - CppCompat.LongLong64 impl forall [U:! ImplicitAs(CppCompat.LongLong64)] CppCompat.LongLong64 as BitAndAssignWith(U) { fn Op[ref self: Self](other: Self) = "int.and_assign"; } impl forall [U:! ImplicitAs(CppCompat.LongLong64)] CppCompat.LongLong64 as BitOrAssignWith(U) { fn Op[ref self: Self](other: Self) = "int.or_assign"; } impl forall [U:! ImplicitAs(CppCompat.LongLong64)] CppCompat.LongLong64 as BitXorAssignWith(U) { fn Op[ref self: Self](other: Self) = "int.xor_assign"; } impl forall [U:! ImplicitAs(CppCompat.LongLong64)] CppCompat.LongLong64 as LeftShiftAssignWith(U) { fn Op[ref self: Self](other: Self) = "int.left_shift_assign"; } impl forall [U:! ImplicitAs(CppCompat.LongLong64)] CppCompat.LongLong64 as RightShiftAssignWith(U) { fn Op[ref self: Self](other: Self) = "int.right_shift_assign"; } // TODO: ULong32, ULongLong64: compound assignments. // Increment and decrement. // - CppCompat.Long32 impl CppCompat.Long32 as Dec { fn Op[ref self: Self]() { self -= (1 as CppCompat.Long32); } } impl CppCompat.Long32 as Inc { fn Op[ref self: Self]() { self += (1 as CppCompat.Long32); } } // - CppCompat.LongLong64 impl CppCompat.LongLong64 as Dec { fn Op[ref self: Self]() { self -= (1 as CppCompat.LongLong64); } } impl CppCompat.LongLong64 as Inc { fn Op[ref self: Self]() { self += (1 as CppCompat.LongLong64); } } // TODO: Increment/decrement operations for ULong32, ULongLong64. ================================================ FILE: core/prelude/types/cpp/nullptr.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception package Core library "prelude/types/cpp/nullptr"; import library "prelude/copy"; import library "prelude/default"; import library "prelude/destroy"; import library "prelude/operators/as"; import library "prelude/types/cpp/void"; import library "prelude/types/maybe_unformed"; import library "prelude/types/optional"; namespace CppCompat; // C++ `std::nullptr_t` as a Carbon type. // // The C++ type `decltype(nullptr)`, also known by the library-provided alias // `std::nullptr_t`, is a fundamental type, not a class type, so it needs a // custom mapping as part of C++ interoperability. We map it to this class // type. After a suitable import, this class can be named as // `Cpp.std.nullptr_t`. This is also the type of the constant `Cpp.nullptr`. // // This type supports implicit conversion to any optional pointer type, and // produces the `None` value of that type. class CppCompat.NullptrT { // nullptr_t has the same size and alignment as a pointer, but the // corresponding pointer is always unformed. // TODO: Give this type a custom empty value representation. adapt MaybeUnformed(VoidBase*); // TODO: This should be just // fn Make() -> Self = "make_uninitialized"; // but we don't yet delay processing builtin function definitions until the // end of the enclosing class. fn Make() -> Self { fn MakeImpl() -> Self = "make_uninitialized"; return MakeImpl(); } impl as Copy { fn Op[unused self: Self]() -> Self { return Make(); } } // TODO: impl as EqWith(Self) impl forall [T:! type] as ImplicitAs(Optional(T*)) { fn Convert[unused self: Self]() -> Optional(T*) { return Optional(T*).None(); } } } impl CppCompat.NullptrT as UnformedInit {} ================================================ FILE: core/prelude/types/cpp/void.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception package Core library "prelude/types/cpp/void"; import library "prelude/operators/as"; namespace CppCompat; // C++ `void` as a Carbon type. // // This type represents a notional "base-most" object at the root of every // inheritance hierarchy. There are no objects of type `VoidType`, but any value // can be converted to a value of type `VoidType`. // // This type is used as the default mapping for C++ `void`, which is used in // most contexts in which `void` can appear: for example, as a pointee of a // pointer type, as the type of a typedef, or as a type template argument. // However, in a function signature, a `void` return type maps to `()` instead, // and a `(void)` parameter list maps to `()`. // // This type is also available via the alias `Cpp.void`. abstract class CppCompat.VoidBase { adapt (); } // TODO: Support a conversion from a value of any type to a value of type // `Cpp.void` once we allow defining a conversion to a value category rather // than to an initializing category. // TODO: Do not allow a pointer to `const T` to convert to `Cpp.void*`. // #6357 allows it, and we don't have a good way to express "non-const" as a // constraint on this impl, but this conversion is not const-correct. impl forall [T:! type] T* as ImplicitAs(CppCompat.VoidBase*) { fn Convert[self: T*]() -> CppCompat.VoidBase* = "pointer.unsafe_convert"; } impl forall [T:! type] T* as ImplicitAs(const CppCompat.VoidBase*) { fn Convert[self: T*]() -> CppCompat.VoidBase* = "pointer.unsafe_convert"; } ================================================ FILE: core/prelude/types/float.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception package Core library "prelude/types/float"; import library "prelude/copy"; import library "prelude/default"; import library "prelude/destroy"; import library "prelude/operators"; import library "prelude/types/float_literal"; import library "prelude/types/int_literal"; private fn MakeFloat(size: IntLiteral()) -> type = "float.make_type"; class Float(N:! IntLiteral()) { adapt MakeFloat(N); } impl forall [N:! IntLiteral()] Float(N) as UnformedInit {} // Copy. impl forall [N:! IntLiteral()] Float(N) as Copy { fn Op[self: Self]() -> Self = "primitive_copy"; } // Conversions. // TODO: Support mixed-size conversions. // TODO: Support int-to-float conversions. impl forall [N:! IntLiteral()] FloatLiteral() as ImplicitAs(Float(N)) { fn Convert[self: Self]() -> Float(N) = "float.convert_checked"; } // TODO: Remove this once ImplicitAs extends As. impl forall [N:! IntLiteral()] FloatLiteral() as As(Float(N)) { fn Convert[self: Self]() -> Float(N) = "float.convert_checked"; } // Comparisons. // TODO: Support mixed-type comparisons. final impl forall [N:! IntLiteral()] Float(N) as EqWith(Float(N)) { fn Equal[self: Self](other: Float(N)) -> bool = "float.eq"; fn NotEqual[self: Self](other: Float(N)) -> bool = "float.neq"; } final impl forall [N:! IntLiteral()] Float(N) as OrderedWith(Float(N)) { fn Less[self: Self](other: Float(N)) -> bool = "float.less"; fn LessOrEquivalent[self: Self](other: Float(N)) -> bool = "float.less_eq"; fn Greater[self: Self](other: Float(N)) -> bool = "float.greater"; fn GreaterOrEquivalent[self: Self](other: Float(N)) -> bool = "float.greater_eq"; } // Arithmetic. final impl forall [N:! IntLiteral()] Float(N) as AddWith(Self) where .Result = Self { fn Op[self: Self](other: Self) -> Self = "float.add"; } final impl forall [N:! IntLiteral()] Float(N) as DivWith(Self) where .Result = Self { fn Op[self: Self](other: Self) -> Self = "float.div"; } final impl forall [N:! IntLiteral()] Float(N) as MulWith(Self) where .Result = Self { fn Op[self: Self](other: Self) -> Self = "float.mul"; } final impl forall [N:! IntLiteral()] Float(N) as Negate where .Result = Self { fn Op[self: Self]() -> Self = "float.negate"; } final impl forall [N:! IntLiteral()] Float(N) as SubWith(Self) where .Result = Self { fn Op[self: Self](other: Self) -> Self = "float.sub"; } // Compound assignments. final impl forall [N:! IntLiteral()] Float(N) as AddAssignWith(Self) { fn Op[ref self: Self](other: Self) = "float.add_assign"; } final impl forall [N:! IntLiteral()] Float(N) as DivAssignWith(Self) { fn Op[ref self: Self](other: Self) = "float.div_assign"; } final impl forall [N:! IntLiteral()] Float(N) as MulAssignWith(Self) { fn Op[ref self: Self](other: Self) = "float.mul_assign"; } final impl forall [N:! IntLiteral()] Float(N) as SubAssignWith(Self) { fn Op[ref self: Self](other: Self) = "float.sub_assign"; } ================================================ FILE: core/prelude/types/float_literal.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception package Core library "prelude/types/float_literal"; fn FloatLiteral() -> type = "float_literal.make_type"; ================================================ FILE: core/prelude/types/form.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception package Core library "prelude/types/form"; // TODO: this should be a concrete constant, not a function. fn Form() -> type = "form.make_type"; ================================================ FILE: core/prelude/types/int.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception package Core library "prelude/types/int"; import library "prelude/copy"; import library "prelude/default"; import library "prelude/destroy"; import library "prelude/operators"; import library "prelude/types/int_literal"; private fn MakeInt(size: IntLiteral()) -> type = "int.make_type_signed"; class Int(N:! IntLiteral()) { adapt MakeInt(N); } impl forall [N:! IntLiteral()] Int(N) as UnformedInit {} // Copy. impl forall [N:! IntLiteral()] Int(N) as Copy { fn Op[self: Self]() -> Self = "primitive_copy"; } // Conversions. impl forall [To:! IntLiteral()] IntLiteral() as ImplicitAs(Int(To)) { fn Convert[self: Self]() -> Int(To) = "int.convert_checked"; } final impl forall [From:! IntLiteral()] Int(From) as ImplicitAs(IntLiteral()) { fn Convert[self: Self]() -> IntLiteral() = "int.convert_checked"; } // TODO: Remove these once ImplicitAs extends As. For now we need them because // the forwarding impl of As in terms of ImplicitAs is not usable at compile // time. impl forall [To:! IntLiteral()] IntLiteral() as As(Int(To)) { fn Convert[self: Self]() -> Int(To) = "int.convert_checked"; } final impl forall [From:! IntLiteral()] Int(From) as As(IntLiteral()) { fn Convert[self: Self]() -> IntLiteral() = "int.convert_checked"; } // Work around the inability to put a `where` clause on a generic parameter of // type `IntLiteral`. // TODO: Remove this once possible. private interface AnyInt { let Width:! IntLiteral(); fn AsInt[self: Self]() -> Int(Width); } impl forall [N:! IntLiteral()] Int(N) as AnyInt where .Width = N { fn AsInt[self: Self]() -> Self { return self; } } impl forall [To:! IntLiteral(), From:! AnyInt & IntFitsIn(Int(To))] From as ImplicitAs(Int(To)) { fn Convert[self: Self]() -> Int(To) { fn Impl(src: Int(From.Width)) -> Int(To) = "int.convert"; return Impl(self.AsInt()); } } final impl forall [From:! IntLiteral(), To:! IntLiteral()] Int(From) as As(Int(To)) { fn Convert[self: Self]() -> Int(To) = "int.convert"; } // Comparisons. // - Int(N) vs Int(M). final impl forall [N:! IntLiteral(), M:! IntLiteral()] Int(N) as EqWith(Int(M)) { fn Equal[self: Self](other: Int(M)) -> bool = "int.eq"; fn NotEqual[self: Self](other: Int(M)) -> bool = "int.neq"; } final impl forall [N:! IntLiteral(), M:! IntLiteral()] Int(N) as OrderedWith(Int(M)) { // TODO: fn Compare fn Less[self: Self](other: Int(M)) -> bool = "int.less"; fn LessOrEquivalent[self: Self](other: Int(M)) -> bool = "int.less_eq"; fn Greater[self: Self](other: Int(M)) -> bool = "int.greater"; fn GreaterOrEquivalent[self: Self](other: Int(M)) -> bool = "int.greater_eq"; } // - Int(N) on left-hand side. impl forall [N:! IntLiteral(), T:! ImplicitAs(Int(N))] Int(N) as EqWith(T) { fn Equal[self: Self](other: Self) -> bool = "int.eq"; fn NotEqual[self: Self](other: Self) -> bool = "int.neq"; } impl forall [N:! IntLiteral(), T:! ImplicitAs(Int(N))] Int(N) as OrderedWith(T) { // TODO: fn Compare fn Less[self: Self](other: Self) -> bool = "int.less"; fn LessOrEquivalent[self: Self](other: Self) -> bool = "int.less_eq"; fn Greater[self: Self](other: Self) -> bool = "int.greater"; fn GreaterOrEquivalent[self: Self](other: Self) -> bool = "int.greater_eq"; } // - Int(N) on right-hand side. impl forall [N:! IntLiteral(), T:! ImplicitAs(Int(N))] T as EqWith(Int(N)) { fn Equal[self: Int(N)](other: Int(N)) -> bool = "int.eq"; fn NotEqual[self: Int(N)](other: Int(N)) -> bool = "int.neq"; } impl forall [N:! IntLiteral(), T:! ImplicitAs(Int(N))] T as OrderedWith(Int(N)) { // TODO: fn Compare fn Less[self: Int(N)](other: Int(N)) -> bool = "int.less"; fn LessOrEquivalent[self: Int(N)](other: Int(N)) -> bool = "int.less_eq"; fn Greater[self: Int(N)](other: Int(N)) -> bool = "int.greater"; fn GreaterOrEquivalent[self: Int(N)](other: Int(N)) -> bool = "int.greater_eq"; } // Arithmetic. // - Homogeneous. final impl forall [N:! IntLiteral()] Int(N) as AddWith(Self) where .Result = Self { fn Op[self: Self](other: Self) -> Self = "int.sadd"; } final impl forall [N:! IntLiteral()] Int(N) as DivWith(Self) where .Result = Self { fn Op[self: Self](other: Self) -> Self = "int.sdiv"; } final impl forall [N:! IntLiteral()] Int(N) as ModWith(Self) where .Result = Self { fn Op[self: Self](other: Self) -> Self = "int.smod"; } final impl forall [N:! IntLiteral()] Int(N) as MulWith(Self) where .Result = Self { fn Op[self: Self](other: Self) -> Self = "int.smul"; } final impl forall [N:! IntLiteral()] Int(N) as Negate where .Result = Self { fn Op[self: Self]() -> Self = "int.snegate"; } final impl forall [N:! IntLiteral()] Int(N) as SubWith(Self) where .Result = Self { fn Op[self: Self](other: Self) -> Self = "int.ssub"; } // - Int(N) on left-hand side. impl forall [N:! IntLiteral(), U:! ImplicitAs(Int(N))] Int(N) as AddWith(U) where .Result = Int(N) { fn Op[self: Int(N)](other: Int(N)) -> Int(N) = "int.sadd"; } impl forall [N:! IntLiteral(), U:! ImplicitAs(Int(N))] Int(N) as DivWith(U) where .Result = Int(N) { fn Op[self: Int(N)](other: Int(N)) -> Int(N) = "int.sdiv"; } impl forall [N:! IntLiteral(), U:! ImplicitAs(Int(N))] Int(N) as ModWith(U) where .Result = Int(N) { fn Op[self: Int(N)](other: Int(N)) -> Int(N) = "int.smod"; } impl forall [N:! IntLiteral(), U:! ImplicitAs(Int(N))] Int(N) as MulWith(U) where .Result = Int(N) { fn Op[self: Int(N)](other: Int(N)) -> Int(N) = "int.smul"; } impl forall [N:! IntLiteral(), U:! ImplicitAs(Int(N))] Int(N) as SubWith(U) where .Result = Int(N) { fn Op[self: Int(N)](other: Int(N)) -> Int(N) = "int.ssub"; } // - Int(N) on right-hand side. impl forall [N:! IntLiteral(), T:! ImplicitAs(Int(N))] T as AddWith(Int(N)) where .Result = Int(N) { fn Op[self: Int(N)](other: Int(N)) -> Int(N) = "int.sadd"; } impl forall [N:! IntLiteral(), T:! ImplicitAs(Int(N))] T as DivWith(Int(N)) where .Result = Int(N) { fn Op[self: Int(N)](other: Int(N)) -> Int(N) = "int.sdiv"; } impl forall [N:! IntLiteral(), T:! ImplicitAs(Int(N))] T as ModWith(Int(N)) where .Result = Int(N) { fn Op[self: Int(N)](other: Int(N)) -> Int(N) = "int.smod"; } impl forall [N:! IntLiteral(), T:! ImplicitAs(Int(N))] T as MulWith(Int(N)) where .Result = Int(N) { fn Op[self: Int(N)](other: Int(N)) -> Int(N) = "int.smul"; } impl forall [N:! IntLiteral(), T:! ImplicitAs(Int(N))] T as SubWith(Int(N)) where .Result = Int(N) { fn Op[self: Int(N)](other: Int(N)) -> Int(N) = "int.ssub"; } // Bitwise operators. // - Homogeneous. final impl forall [N:! IntLiteral()] Int(N) as BitAndWith(Self) where .Result = Self { fn Op[self: Self](other: Self) -> Self = "int.and"; } final impl forall [N:! IntLiteral()] Int(N) as BitComplement where .Result = Self { fn Op[self: Self]() -> Self = "int.complement"; } final impl forall [N:! IntLiteral()] Int(N) as BitOrWith(Self) where .Result = Self { fn Op[self: Self](other: Self) -> Self = "int.or"; } final impl forall [N:! IntLiteral()] Int(N) as BitXorWith(Self) where .Result = Self { fn Op[self: Self](other: Self) -> Self = "int.xor"; } final impl forall [N:! IntLiteral()] Int(N) as LeftShiftWith(Self) where .Result = Self { fn Op[self: Self](other: Self) -> Self = "int.left_shift"; } final impl forall [N:! IntLiteral()] Int(N) as RightShiftWith(Self) where .Result = Self { fn Op[self: Self](other: Self) -> Self = "int.right_shift"; } // - Int(N) on left-hand side. impl forall [N:! IntLiteral(), U:! ImplicitAs(Int(N))] Int(N) as BitAndWith(U) where .Result = Int(N) { fn Op[self: Int(N)](other: Int(N)) -> Int(N) = "int.and"; } impl forall [N:! IntLiteral(), U:! ImplicitAs(Int(N))] Int(N) as BitOrWith(U) where .Result = Int(N) { fn Op[self: Int(N)](other: Int(N)) -> Int(N) = "int.or"; } impl forall [N:! IntLiteral(), U:! ImplicitAs(Int(N))] Int(N) as BitXorWith(U) where .Result = Int(N) { fn Op[self: Int(N)](other: Int(N)) -> Int(N) = "int.xor"; } impl forall [N:! IntLiteral(), U:! ImplicitAs(Int(N))] Int(N) as LeftShiftWith(U) where .Result = Int(N) { fn Op[self: Int(N)](other: Int(N)) -> Int(N) = "int.left_shift"; } impl forall [N:! IntLiteral(), U:! ImplicitAs(Int(N))] Int(N) as RightShiftWith(U) where .Result = Int(N) { fn Op[self: Int(N)](other: Int(N)) -> Int(N) = "int.right_shift"; } // - Int(N) on right-hand side. impl forall [N:! IntLiteral(), T:! ImplicitAs(Int(N))] T as BitAndWith(Int(N)) where .Result = Int(N) { fn Op[self: Int(N)](other: Int(N)) -> Int(N) = "int.and"; } impl forall [N:! IntLiteral(), T:! ImplicitAs(Int(N))] T as BitOrWith(Int(N)) where .Result = Int(N) { fn Op[self: Int(N)](other: Int(N)) -> Int(N) = "int.or"; } impl forall [N:! IntLiteral(), T:! ImplicitAs(Int(N))] T as BitXorWith(Int(N)) where .Result = Int(N) { fn Op[self: Int(N)](other: Int(N)) -> Int(N) = "int.xor"; } impl forall [N:! IntLiteral(), T:! ImplicitAs(Int(N))] T as LeftShiftWith(Int(N)) where .Result = Int(N) { fn Op[self: Int(N)](other: Int(N)) -> Int(N) = "int.left_shift"; } impl forall [N:! IntLiteral(), T:! ImplicitAs(Int(N))] T as RightShiftWith(Int(N)) where .Result = Int(N) { fn Op[self: Int(N)](other: Int(N)) -> Int(N) = "int.right_shift"; } // Compound assignments. impl forall [N:! IntLiteral(), U:! ImplicitAs(Int(N))] Int(N) as AddAssignWith(U) { fn Op[ref self: Self](other: Self) = "int.sadd_assign"; } impl forall [N:! IntLiteral(), U:! ImplicitAs(Int(N))] Int(N) as BitAndAssignWith(U) { fn Op[ref self: Self](other: Self) = "int.and_assign"; } impl forall [N:! IntLiteral(), U:! ImplicitAs(Int(N))] Int(N) as BitOrAssignWith(U) { fn Op[ref self: Self](other: Self) = "int.or_assign"; } impl forall [N:! IntLiteral(), U:! ImplicitAs(Int(N))] Int(N) as BitXorAssignWith(U) { fn Op[ref self: Self](other: Self) = "int.xor_assign"; } impl forall [N:! IntLiteral(), U:! ImplicitAs(Int(N))] Int(N) as DivAssignWith(U) { fn Op[ref self: Self](other: Self) = "int.sdiv_assign"; } impl forall [N:! IntLiteral(), U:! ImplicitAs(Int(N))] Int(N) as LeftShiftAssignWith(U) { fn Op[ref self: Self](other: Self) = "int.left_shift_assign"; } impl forall [N:! IntLiteral(), U:! ImplicitAs(Int(N))] Int(N) as ModAssignWith(U) { fn Op[ref self: Self](other: Self) = "int.smod_assign"; } impl forall [N:! IntLiteral(), U:! ImplicitAs(Int(N))] Int(N) as MulAssignWith(U) { fn Op[ref self: Self](other: Self) = "int.smul_assign"; } impl forall [N:! IntLiteral(), U:! ImplicitAs(Int(N))] Int(N) as RightShiftAssignWith(U) { fn Op[ref self: Self](other: Self) = "int.right_shift_assign"; } impl forall [N:! IntLiteral(), U:! ImplicitAs(Int(N))] Int(N) as SubAssignWith(U) { fn Op[ref self: Self](other: Self) = "int.ssub_assign"; } // Increment and decrement. // TODO: Add builtins for these to avoid emitting a call. impl forall [N:! IntLiteral()] Int(N) as Dec { fn Op[ref self: Self]() { // TODO: `self -= 1;` should work, but fails because the `impl IntLiteral // as ImplicitAs(Int(N))` is not final, which causes us to not attempt the // conversion from `1` to `Int(N)` until runtime, when we've lost the // constant value. fn AsInt(n: IntLiteral()) -> Int(N) = "int.convert_checked"; self -= AsInt(1); } } impl forall [N:! IntLiteral()] Int(N) as Inc { fn Op[ref self: Self]() { fn AsInt(n: IntLiteral()) -> Int(N) = "int.convert_checked"; self += AsInt(1); } } ================================================ FILE: core/prelude/types/int_literal.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception package Core library "prelude/types/int_literal"; fn IntLiteral() -> type = "int_literal.make_type"; ================================================ FILE: core/prelude/types/maybe_unformed.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception package Core library "prelude/types/maybe_unformed"; import library "prelude/default"; import library "prelude/destroy"; private fn MakeMaybeUnformed(t: type) -> type = "maybe_unformed.make_type"; // TODO: This should probably support non-destructible types. class MaybeUnformed(T:! Destroy) { adapt MakeMaybeUnformed(T); } impl forall [T:! Destroy] MaybeUnformed(T) as UnformedInit {} ================================================ FILE: core/prelude/types/optional.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception package Core library "prelude/types/optional"; import library "prelude/copy"; import library "prelude/default"; import library "prelude/destroy"; import library "prelude/operators/as"; import library "prelude/operators/bitwise"; import library "prelude/types/bool"; import library "prelude/types/maybe_unformed"; // TODO: Decide how to expose this in the public API. private interface OptionalStorage { let Type:! type; fn None() -> Type; fn Some[self: Self]() -> Type; fn Has(value: Type) -> bool; fn Get(value: Type) -> Self; } // TODO: We don't have an approved design for an `Optional` type yet, but it's // used by the design for `Iterate`. The API here is a placeholder. class Optional(T:! OptionalStorage) { fn None() -> Self { return T.None() as Self; } fn Some(value: T) -> Self { return value.Some() as Self; } fn HasValue[self: Self]() -> bool { return T.Has(self as T.Type); } fn Get[self: Self]() -> T { return T.Get(self as T.Type); } // TODO: Use `private adapt` or `unsafe adapt` once available. adapt T.Type; } impl forall [T:! OptionalStorage & UnformedInit] Optional(T) as UnformedInit {} // Support for converting a `T` to an `Optional(U)` if `T` converts to `U`. // Once we have match_first, this can be rewritten more simply as: // // match_first { // impl forall [T:! OptionalStorage] // T as ImplicitAs(Optional(T)) { // fn Convert[self: T]() -> Optional(T) { // return Optional(T).Some(self); // } // } // // impl forall [T:! Destroy & OptionalStorage, U:! ImplicitAs(T)] // U as ImplicitAs(Optional(T)) { // fn Convert[self: U]() -> Optional(T) { // return Optional(T).Some(self.Convert()); // } // } // } private interface OptionalAs(T:! OptionalStorage) { fn Convert[self: Self]() -> Optional(T); } final impl forall [T:! OptionalStorage] T as OptionalAs(T) { fn Convert[self: Self]() -> Optional(T) { return Optional(T).Some(self); } } impl forall [T:! Destroy & OptionalStorage, U:! ImplicitAs(T)] U as OptionalAs(T) { fn Convert[self: Self]() -> Optional(T) { return Optional(T).Some(self.Convert()); } } impl forall [T:! Destroy & OptionalStorage, U:! Destroy & OptionalStorage & ImplicitAs(T)] Optional(U) as OptionalAs(T) { fn Convert[self: Self]() -> Optional(T) { if (self.HasValue()) { return Optional(T).Some(self.Get().Convert()); } return Optional(T).None(); } } impl forall [T:! OptionalStorage, U:! OptionalAs(T)] U as ImplicitAs(Optional(T)) { fn Convert[self: Self]() -> Optional(T) { return self.Convert(); } } // By default, an `Optional(T)` is stored as a pair of a `bool` and a // `MaybeUnformed(T)`, with the `MaybeUnformed(T)` left uninitialized if the // `bool` is `false`. // // TODO: Revisit this once we have choice types implemented in the toolchain. private class DefaultOptionalStorage(T:! Copy & Destroy) { var value: MaybeUnformed(T); var has_value: bool; } private fn MakeUninitializedOptionalStorage(T:! Copy & Destroy) -> DefaultOptionalStorage(T) = "make_uninitialized"; impl forall [T:! Copy & Destroy] T as OptionalStorage where .Type = DefaultOptionalStorage(T) { fn None() -> DefaultOptionalStorage(T) { returned var me: DefaultOptionalStorage(T) = MakeUninitializedOptionalStorage(T); me.has_value = false; return var; } fn Some[self: Self]() -> DefaultOptionalStorage(T) { // TODO: This whole function should be just // return {.value = self, .has_value = true}; // but that requires that `T` implements `ImplicitAs(MaybeUnformed(T))`. returned var me: DefaultOptionalStorage(T) = MakeUninitializedOptionalStorage(T); me.value unsafe as T = self; me.has_value = true; return var; } fn Has(value: DefaultOptionalStorage(T)) -> bool { return value.has_value; } fn Get(value: DefaultOptionalStorage(T)) -> T { return value.value unsafe as T; } } private fn PointerIsNull[T:! type](value: MaybeUnformed(T*)) -> bool = "pointer.is_null"; private fn MakeUninitializedOptionalPointer(T:! type) -> MaybeUnformed(T*) = "make_uninitialized"; // For pointers, we use a null pointer value as the "None" value. This allows // `Optional(T*)` to be ABI-compatible with a C++ nullable pointer. final impl forall [T:! type] T* as OptionalStorage where .Type = MaybeUnformed(T*) { fn None() -> MaybeUnformed(T*) = "pointer.make_null"; fn Some[self: Self]() -> MaybeUnformed(T*) { returned var result: MaybeUnformed(T*) = MakeUninitializedOptionalPointer(T); result unsafe as T* = self; return var; } fn Has(value: MaybeUnformed(T*)) -> bool { return not PointerIsNull(value); } fn Get(value: MaybeUnformed(T*)) -> T* { return value unsafe as T*; } } ================================================ FILE: core/prelude/types/string.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception package Core library "prelude/types/string"; import library "prelude/copy"; import library "prelude/default"; import library "prelude/destroy"; import library "prelude/types/char"; import library "prelude/types/uint"; import library "prelude/operators/index"; import library "prelude/types/int"; import library "prelude/operators/as"; class String { fn Size[self: Self]() -> u64 { return self.size; } impl as Copy { fn Op[self: Self]() -> Self { return {.ptr = self.ptr, .size = self.size}; } } // TODO: This should be an array iterator. private var ptr: Char*; // TODO: This should be a word-sized integer. private var size: u64; } impl String as UnformedInit {} impl forall [T:! ImplicitAs(i64)] String as IndexWith(T) where .ElementType = Char { fn At[self: Self](subscript: T) -> Char = "string.at"; } ================================================ FILE: core/prelude/types/uint.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception package Core library "prelude/types/uint"; import library "prelude/copy"; import library "prelude/default"; import library "prelude/destroy"; import library "prelude/operators"; import library "prelude/types/int"; import library "prelude/types/int_literal"; private fn MakeUInt(size: IntLiteral()) -> type = "int.make_type_unsigned"; class UInt(N:! IntLiteral()) { adapt MakeUInt(N); } impl forall [N:! IntLiteral()] UInt(N) as UnformedInit {} // Copy. impl forall [N:! IntLiteral()] UInt(N) as Copy { fn Op[self: Self]() -> Self = "primitive_copy"; } // Conversions. impl forall [To:! IntLiteral()] IntLiteral() as ImplicitAs(UInt(To)) { fn Convert[self: Self]() -> UInt(To) = "int.convert_checked"; } final impl forall [From:! IntLiteral()] UInt(From) as ImplicitAs(IntLiteral()) { fn Convert[self: Self]() -> IntLiteral() = "int.convert_checked"; } // TODO: Remove these once ImplicitAs extends As. For now we need them because // the forwarding impl of As in terms of ImplicitAs is not usable at compile // time. impl forall [To:! IntLiteral()] IntLiteral() as As(UInt(To)) { fn Convert[self: Self]() -> UInt(To) = "int.convert_checked"; } final impl forall [From:! IntLiteral()] UInt(From) as As(IntLiteral()) { fn Convert[self: Self]() -> IntLiteral() = "int.convert_checked"; } // Work around the inability to put a `where` clause on a generic parameter of // type `IntLiteral`. // TODO: Remove this once possible. private interface AnyUInt { let Width:! IntLiteral(); fn AsUInt[self: Self]() -> UInt(Width); } impl forall [N:! IntLiteral()] UInt(N) as AnyUInt where .Width = N { fn AsUInt[self: Self]() -> Self { return self; } } // Work around the inability to constrain types other than `.Self` by reversing // the Self and argument type of `ImplicitAs`. // TODO: Remove this once possible. private interface FromUInt(From:! type) { fn Convert(from: From) -> Self; } impl forall [To:! IntLiteral(), From:! AnyUInt & IntFitsIn(UInt(To))] UInt(To) as FromUInt(From) { fn Convert(from: From) -> Self { fn Impl(src: UInt(From.Width)) -> Self = "int.convert"; return Impl(from.AsUInt()); } } impl forall [To:! IntLiteral(), From:! AnyUInt & IntFitsIn(Int(To))] Int(To) as FromUInt(From) { fn Convert(from: From) -> Self { fn Impl(src: UInt(From.Width)) -> Self = "int.convert"; return Impl(from.AsUInt()); } } impl forall [From:! IntLiteral(), To:! FromUInt(UInt(From))] UInt(From) as ImplicitAs(To) { fn Convert[self: Self]() -> To { return To.Convert(self); } } final impl forall [From:! IntLiteral(), To:! IntLiteral()] UInt(From) as As(UInt(To)) { fn Convert[self: Self]() -> UInt(To) = "int.convert"; } final impl forall [From:! IntLiteral(), To:! IntLiteral()] UInt(From) as As(Int(To)) { fn Convert[self: Self]() -> Int(To) = "int.convert"; } // Never implicit. impl forall [From:! IntLiteral(), To:! IntLiteral()] Int(From) as As(UInt(To)) { fn Convert[self: Self]() -> UInt(To) = "int.convert"; } // Comparisons. // - UInt(N) vs UInt(M). final impl forall [N:! IntLiteral(), M:! IntLiteral()] UInt(N) as EqWith(UInt(M)) { fn Equal[self: Self](other: UInt(M)) -> bool = "int.eq"; fn NotEqual[self: Self](other: UInt(M)) -> bool = "int.neq"; } final impl forall [N:! IntLiteral(), M:! IntLiteral()] UInt(N) as OrderedWith(UInt(M)) { // TODO: fn Compare fn Less[self: Self](other: UInt(M)) -> bool = "int.less"; fn LessOrEquivalent[self: Self](other: UInt(M)) -> bool = "int.less_eq"; fn Greater[self: Self](other: UInt(M)) -> bool = "int.greater"; fn GreaterOrEquivalent[self: Self](other: UInt(M)) -> bool = "int.greater_eq"; } // - UInt(N) vs Int(M). final impl forall [N:! IntLiteral(), M:! IntLiteral()] UInt(N) as EqWith(Int(M)) { fn Equal[self: Self](other: Int(M)) -> bool = "int.eq"; fn NotEqual[self: Self](other: Int(M)) -> bool = "int.neq"; } final impl forall [N:! IntLiteral(), M:! IntLiteral()] UInt(N) as OrderedWith(Int(M)) { // TODO: fn Compare fn Less[self: Self](other: Int(M)) -> bool = "int.less"; fn LessOrEquivalent[self: Self](other: Int(M)) -> bool = "int.less_eq"; fn Greater[self: Self](other: Int(M)) -> bool = "int.greater"; fn GreaterOrEquivalent[self: Self](other: Int(M)) -> bool = "int.greater_eq"; } // - Int(N) vs UInt(M). impl forall [N:! IntLiteral(), M:! IntLiteral()] Int(N) as EqWith(UInt(M)) { fn Equal[self: Self](other: UInt(M)) -> bool = "int.eq"; fn NotEqual[self: Self](other: UInt(M)) -> bool = "int.neq"; } impl forall [N:! IntLiteral(), M:! IntLiteral()] Int(N) as OrderedWith(UInt(M)) { // TODO: fn Compare fn Less[self: Self](other: UInt(M)) -> bool = "int.less"; fn LessOrEquivalent[self: Self](other: UInt(M)) -> bool = "int.less_eq"; fn Greater[self: Self](other: UInt(M)) -> bool = "int.greater"; fn GreaterOrEquivalent[self: Self](other: UInt(M)) -> bool = "int.greater_eq"; } // - UInt(N) on left-hand side. impl forall [N:! IntLiteral(), T:! ImplicitAs(UInt(N))] UInt(N) as EqWith(T) { fn Equal[self: Self](other: Self) -> bool = "int.eq"; fn NotEqual[self: Self](other: Self) -> bool = "int.neq"; } impl forall [N:! IntLiteral(), T:! ImplicitAs(UInt(N))] UInt(N) as OrderedWith(T) { // TODO: fn Compare fn Less[self: Self](other: Self) -> bool = "int.less"; fn LessOrEquivalent[self: Self](other: Self) -> bool = "int.less_eq"; fn Greater[self: Self](other: Self) -> bool = "int.greater"; fn GreaterOrEquivalent[self: Self](other: Self) -> bool = "int.greater_eq"; } // - UInt(N) on right-hand side. impl forall [N:! IntLiteral(), T:! ImplicitAs(UInt(N))] T as EqWith(UInt(N)) { fn Equal[self: UInt(N)](other: UInt(N)) -> bool = "int.eq"; fn NotEqual[self: UInt(N)](other: UInt(N)) -> bool = "int.neq"; } impl forall [N:! IntLiteral(), T:! ImplicitAs(UInt(N))] T as OrderedWith(UInt(N)) { // TODO: fn Compare fn Less[self: UInt(N)](other: UInt(N)) -> bool = "int.less"; fn LessOrEquivalent[self: UInt(N)](other: UInt(N)) -> bool = "int.less_eq"; fn Greater[self: UInt(N)](other: UInt(N)) -> bool = "int.greater"; fn GreaterOrEquivalent[self: UInt(N)](other: UInt(N)) -> bool = "int.greater_eq"; } // Arithmetic. // - Homogeneous. final impl forall [N:! IntLiteral()] UInt(N) as AddWith(Self) where .Result = Self { fn Op[self: Self](other: Self) -> Self = "int.uadd"; } final impl forall [N:! IntLiteral()] UInt(N) as DivWith(Self) where .Result = Self { fn Op[self: Self](other: Self) -> Self = "int.udiv"; } final impl forall [N:! IntLiteral()] UInt(N) as ModWith(Self) where .Result = Self { fn Op[self: Self](other: Self) -> Self = "int.umod"; } final impl forall [N:! IntLiteral()] UInt(N) as MulWith(Self) where .Result = Self { fn Op[self: Self](other: Self) -> Self = "int.umul"; } final impl forall [N:! IntLiteral()] UInt(N) as Negate where .Result = Self { fn Op[self: Self]() -> Self = "int.unegate"; } final impl forall [N:! IntLiteral()] UInt(N) as SubWith(Self) where .Result = Self { fn Op[self: Self](other: Self) -> Self = "int.usub"; } // - UInt(N) on left-hand side. impl forall [N:! IntLiteral(), U:! ImplicitAs(UInt(N))] UInt(N) as AddWith(U) where .Result = UInt(N) { fn Op[self: UInt(N)](other: UInt(N)) -> UInt(N) = "int.uadd"; } impl forall [N:! IntLiteral(), U:! ImplicitAs(UInt(N))] UInt(N) as DivWith(U) where .Result = UInt(N) { fn Op[self: UInt(N)](other: UInt(N)) -> UInt(N) = "int.udiv"; } impl forall [N:! IntLiteral(), U:! ImplicitAs(UInt(N))] UInt(N) as ModWith(U) where .Result = UInt(N) { fn Op[self: UInt(N)](other: UInt(N)) -> UInt(N) = "int.umod"; } impl forall [N:! IntLiteral(), U:! ImplicitAs(UInt(N))] UInt(N) as MulWith(U) where .Result = UInt(N) { fn Op[self: UInt(N)](other: UInt(N)) -> UInt(N) = "int.umul"; } impl forall [N:! IntLiteral(), U:! ImplicitAs(UInt(N))] UInt(N) as SubWith(U) where .Result = UInt(N) { fn Op[self: UInt(N)](other: UInt(N)) -> UInt(N) = "int.usub"; } // - UInt(N) on right-hand side. impl forall [N:! IntLiteral(), T:! ImplicitAs(UInt(N))] T as AddWith(UInt(N)) where .Result = UInt(N) { fn Op[self: UInt(N)](other: UInt(N)) -> UInt(N) = "int.uadd"; } impl forall [N:! IntLiteral(), T:! ImplicitAs(UInt(N))] T as DivWith(UInt(N)) where .Result = UInt(N) { fn Op[self: UInt(N)](other: UInt(N)) -> UInt(N) = "int.udiv"; } impl forall [N:! IntLiteral(), T:! ImplicitAs(UInt(N))] T as ModWith(UInt(N)) where .Result = UInt(N) { fn Op[self: UInt(N)](other: UInt(N)) -> UInt(N) = "int.umod"; } impl forall [N:! IntLiteral(), T:! ImplicitAs(UInt(N))] T as MulWith(UInt(N)) where .Result = UInt(N) { fn Op[self: UInt(N)](other: UInt(N)) -> UInt(N) = "int.umul"; } impl forall [N:! IntLiteral(), T:! ImplicitAs(UInt(N))] T as SubWith(UInt(N)) where .Result = UInt(N) { fn Op[self: UInt(N)](other: UInt(N)) -> UInt(N) = "int.usub"; } // Bitwise operators. // - Homogeneous. final impl forall [N:! IntLiteral()] UInt(N) as BitAndWith(Self) where .Result = Self { fn Op[self: Self](other: Self) -> Self = "int.and"; } final impl forall [N:! IntLiteral()] UInt(N) as BitComplement where .Result = Self { fn Op[self: Self]() -> Self = "int.complement"; } final impl forall [N:! IntLiteral()] UInt(N) as BitOrWith(Self) where .Result = Self { fn Op[self: Self](other: Self) -> Self = "int.or"; } final impl forall [N:! IntLiteral()] UInt(N) as BitXorWith(Self) where .Result = Self { fn Op[self: Self](other: Self) -> Self = "int.xor"; } final impl forall [N:! IntLiteral()] UInt(N) as LeftShiftWith(Self) where .Result = Self { fn Op[self: Self](other: Self) -> Self = "int.left_shift"; } final impl forall [N:! IntLiteral()] UInt(N) as RightShiftWith(Self) where .Result = Self { fn Op[self: Self](other: Self) -> Self = "int.right_shift"; } // - UInt(N) on left-hand side. impl forall [N:! IntLiteral(), U:! ImplicitAs(UInt(N))] UInt(N) as BitAndWith(U) where .Result = UInt(N) { fn Op[self: UInt(N)](other: UInt(N)) -> UInt(N) = "int.and"; } impl forall [N:! IntLiteral(), U:! ImplicitAs(UInt(N))] UInt(N) as BitOrWith(U) where .Result = UInt(N) { fn Op[self: UInt(N)](other: UInt(N)) -> UInt(N) = "int.or"; } impl forall [N:! IntLiteral(), U:! ImplicitAs(UInt(N))] UInt(N) as BitXorWith(U) where .Result = UInt(N) { fn Op[self: UInt(N)](other: UInt(N)) -> UInt(N) = "int.xor"; } impl forall [N:! IntLiteral(), U:! ImplicitAs(UInt(N))] UInt(N) as LeftShiftWith(U) where .Result = UInt(N) { fn Op[self: UInt(N)](other: UInt(N)) -> UInt(N) = "int.left_shift"; } impl forall [N:! IntLiteral(), U:! ImplicitAs(UInt(N))] UInt(N) as RightShiftWith(U) where .Result = UInt(N) { fn Op[self: UInt(N)](other: UInt(N)) -> UInt(N) = "int.right_shift"; } // - UInt(N) on right-hand side. impl forall [N:! IntLiteral(), T:! ImplicitAs(UInt(N))] T as BitAndWith(UInt(N)) where .Result = UInt(N) { fn Op[self: UInt(N)](other: UInt(N)) -> UInt(N) = "int.and"; } impl forall [N:! IntLiteral(), T:! ImplicitAs(UInt(N))] T as BitOrWith(UInt(N)) where .Result = UInt(N) { fn Op[self: UInt(N)](other: UInt(N)) -> UInt(N) = "int.or"; } impl forall [N:! IntLiteral(), T:! ImplicitAs(UInt(N))] T as BitXorWith(UInt(N)) where .Result = UInt(N) { fn Op[self: UInt(N)](other: UInt(N)) -> UInt(N) = "int.xor"; } impl forall [N:! IntLiteral(), T:! ImplicitAs(UInt(N))] T as LeftShiftWith(UInt(N)) where .Result = UInt(N) { fn Op[self: UInt(N)](other: UInt(N)) -> UInt(N) = "int.left_shift"; } impl forall [N:! IntLiteral(), T:! ImplicitAs(UInt(N))] T as RightShiftWith(UInt(N)) where .Result = UInt(N) { fn Op[self: UInt(N)](other: UInt(N)) -> UInt(N) = "int.right_shift"; } // Compound assignments. impl forall [N:! IntLiteral(), U:! ImplicitAs(UInt(N))] UInt(N) as AddAssignWith(U) { fn Op[ref self: Self](other: Self) = "int.uadd_assign"; } impl forall [N:! IntLiteral(), U:! ImplicitAs(UInt(N))] UInt(N) as BitAndAssignWith(U) { fn Op[ref self: Self](other: Self) = "int.and_assign"; } impl forall [N:! IntLiteral(), U:! ImplicitAs(UInt(N))] UInt(N) as BitOrAssignWith(U) { fn Op[ref self: Self](other: Self) = "int.or_assign"; } impl forall [N:! IntLiteral(), U:! ImplicitAs(UInt(N))] UInt(N) as BitXorAssignWith(U) { fn Op[ref self: Self](other: Self) = "int.xor_assign"; } impl forall [N:! IntLiteral(), U:! ImplicitAs(UInt(N))] UInt(N) as DivAssignWith(U) { fn Op[ref self: Self](other: Self) = "int.udiv_assign"; } impl forall [N:! IntLiteral(), U:! ImplicitAs(UInt(N))] UInt(N) as LeftShiftAssignWith(U) { fn Op[ref self: Self](other: Self) = "int.left_shift_assign"; } impl forall [N:! IntLiteral(), U:! ImplicitAs(UInt(N))] UInt(N) as ModAssignWith(U) { fn Op[ref self: Self](other: Self) = "int.umod_assign"; } impl forall [N:! IntLiteral(), U:! ImplicitAs(UInt(N))] UInt(N) as MulAssignWith(U) { fn Op[ref self: Self](other: Self) = "int.umul_assign"; } impl forall [N:! IntLiteral(), U:! ImplicitAs(UInt(N))] UInt(N) as RightShiftAssignWith(U) { fn Op[ref self: Self](other: Self) = "int.right_shift_assign"; } impl forall [N:! IntLiteral(), U:! ImplicitAs(UInt(N))] UInt(N) as SubAssignWith(U) { fn Op[ref self: Self](other: Self) = "int.usub_assign"; } // Increment and decrement. // TODO: Add builtins for these to avoid emitting a call. impl forall [N:! IntLiteral()] UInt(N) as Dec { fn Op[ref self: Self]() { // TODO: `self -= 1;` should work, but fails because the `impl IntLiteral // as ImplicitAs(Int(N))` is not final, which causes us to not attempt the // conversion from `1` to `Int(N)` until runtime, when we've lost the // constant value. fn AsUInt(n: IntLiteral()) -> UInt(N) = "int.convert_checked"; self -= AsUInt(1); } } impl forall [N:! IntLiteral()] UInt(N) as Inc { fn Op[ref self: Self]() { fn AsUInt(n: IntLiteral()) -> UInt(N) = "int.convert_checked"; self += AsUInt(1); } } ================================================ FILE: core/prelude/types.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception package Core library "prelude/types"; export import library "prelude/types/bool"; export import library "prelude/types/char"; export import library "prelude/types/cpp/int"; export import library "prelude/types/cpp/nullptr"; export import library "prelude/types/cpp/void"; export import library "prelude/types/float"; export import library "prelude/types/float_literal"; export import library "prelude/types/int"; export import library "prelude/types/int_literal"; export import library "prelude/types/maybe_unformed"; export import library "prelude/types/optional"; export import library "prelude/types/string"; export import library "prelude/types/uint"; ================================================ FILE: core/prelude.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE package Core library "prelude"; export import library "prelude/copy"; export import library "prelude/default"; export import library "prelude/destroy"; export import library "prelude/iterate"; export import library "prelude/operators"; export import library "prelude/types"; ================================================ FILE: core/range.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // TODO: This library is not part of the design. Either write a matching // proposal or remove this. package Core library "range"; import library "prelude/destroy"; import library "prelude/iterate"; import library "prelude/operators/arithmetic"; import library "prelude/operators/as"; import library "prelude/operators/comparison"; import library "prelude/types/int"; import library "prelude/types/int_literal"; import library "prelude/types/optional"; class IntRange(N:! IntLiteral()) { fn Make(start: Int(N), end: Int(N)) -> Self { return {.start = start, .end = end}; } impl as Iterate where .CursorType = Int(N) and .ElementType = Int(N) { fn NewCursor[self: Self]() -> Int(N) { return self.start; } fn Next[self: Self](cursor: Int(N)*) -> Optional(Int(N)) { var value: Int(N) = *cursor; if (value < self.end) { ++*cursor; return Optional(Int(N)).Some(value); } else { return Optional(Int(N)).None(); } } } private var start: Int(N); private var end: Int(N); } fn Range(end: i32) -> IntRange(32) { return IntRange(32).Make(0, end); } fn InclusiveRange(start: i32, end: i32) -> IntRange(32) { return IntRange(32).Make(start, end + 1); } ================================================ FILE: docs/README.md ================================================ # Docs This directory contains current, accepted documentation underpinning Carbon. These documents cover all aspects of Carbon ranging from the project down to detailed designs for specific language features. If you're trying to learn more about Carbon, we recommend starting at [`/README.md`](/README.md). ## Design Carbon language's design and rationale are documented in the [`design/` directory](design/README.md). This documentation is intended to support the following audiences: - People who wish to determine whether Carbon would be the right choice for a project compared to other existing languages. - People working on the evolution of the Carbon language who wish to understand the rationale and motivation for existing design decisions. - People working on a specification or implementation of the Carbon language who need a detailed understanding of the intended design. - People writing Carbon code who wish to understand why the language rules are the way they are. This is in contrast to [proposals](/proposals/README.md), which document the individual decisions that led to this design (along with other changes to the Carbon project), including the rationale and alternatives considered. ## Project The [`project/` directory](project/README.md) contains project-related documentation for Carbon, including: - [goals](project/goals.md), and the [principles](project/principles/README.md) and [roadmap](project/roadmap.md) derived from those goals, - how the project works, and - how to contribute. ## Guides The [`guides/` directory](guides/README.md) contains **to-be-written** end-user documentation for developers writing programs in Carbon. ## Spec The [`spec/` directory](spec/) contains the **to-be-written** formal specification of the Carbon language. This is for implementers of compilers or other tooling. This is intended to complement the [toolchain](/toolchain/). ================================================ FILE: docs/design/README.md ================================================ # Language design > **STATUS:** Up-to-date on 09-Aug-2022, including proposals up through > [#1382](https://github.com/carbon-language/carbon-lang/pull/1382). ## Table of contents - [Introduction](#introduction) - [This document is provisional](#this-document-is-provisional) - [Tour of the basics](#tour-of-the-basics) - [Code and comments](#code-and-comments) - [Build modes](#build-modes) - [Types are values](#types-are-values) - [Values usable as types](#values-usable-as-types) - [Primitive types](#primitive-types) - [`bool`](#bool) - [Integer types](#integer-types) - [Integer literals](#integer-literals) - [Floating-point types](#floating-point-types) - [Floating-point literals](#floating-point-literals) - [String types](#string-types) - [String literals](#string-literals) - [Values, objects, and expressions](#values-objects-and-expressions) - [Expression categories](#expression-categories) - [Expression phases](#expression-phases) - [Composite types](#composite-types) - [Tuples](#tuples) - [Struct types](#struct-types) - [Pointer types](#pointer-types) - [Arrays and slices](#arrays-and-slices) - [Expressions](#expressions) - [Declarations, Definitions, and Scopes](#declarations-definitions-and-scopes) - [Patterns](#patterns) - [Binding patterns](#binding-patterns) - [Destructuring patterns](#destructuring-patterns) - [Refutable patterns](#refutable-patterns) - [Name-binding declarations](#name-binding-declarations) - [Constant `let` declarations](#constant-let-declarations) - [Variable `var` declarations](#variable-var-declarations) - [`auto`](#auto) - [Global constants and variables](#global-constants-and-variables) - [Functions](#functions) - [Parameters](#parameters) - [`auto` return type](#auto-return-type) - [Blocks and statements](#blocks-and-statements) - [Control flow](#control-flow) - [`if` and `else`](#if-and-else) - [Loops](#loops) - [`while`](#while) - [`for`](#for) - [`break`](#break) - [`continue`](#continue) - [`return`](#return) - [`returned var`](#returned-var) - [`match`](#match) - [User-defined types](#user-defined-types) - [Classes](#classes) - [Assignment](#assignment) - [Class functions and factory functions](#class-functions-and-factory-functions) - [Methods](#methods) - [Inheritance](#inheritance) - [Access control](#access-control) - [Destructors](#destructors) - [`const`](#const) - [Unformed state](#unformed-state) - [Move](#move) - [Mixins](#mixins) - [Choice types](#choice-types) - [Names](#names) - [Files, libraries, packages](#files-libraries-packages) - [Package declaration](#package-declaration) - [Imports](#imports) - [Same-package imports](#same-package-imports) - [Cross-package imports](#cross-package-imports) - [Name visibility](#name-visibility) - [Package scope](#package-scope) - [Namespaces](#namespaces) - [Naming conventions](#naming-conventions) - [Aliases](#aliases) - [Name lookup](#name-lookup) - [Name lookup for common types](#name-lookup-for-common-types) - [Generics](#generics) - [Checked and template parameters](#checked-and-template-parameters) - [Interfaces and implementations](#interfaces-and-implementations) - [Combining constraints](#combining-constraints) - [Template name lookup](#template-name-lookup) - [Associated constants](#associated-constants) - [Generic entities](#generic-entities) - [Generic Classes](#generic-classes) - [Generic choice types](#generic-choice-types) - [Generic interfaces](#generic-interfaces) - [Generic implementations](#generic-implementations) - [Other features](#other-features) - [Generic type equality and `observe` declarations](#generic-type-equality-and-observe-declarations) - [Operator overloading](#operator-overloading) - [Common type](#common-type) - [Bidirectional interoperability with C and C++](#bidirectional-interoperability-with-c-and-c) - [Goals](#goals) - [Non-goals](#non-goals) - [Importing and `#include`](#importing-and-include) - [ABI and dynamic linking](#abi-and-dynamic-linking) - [Operator overloading](#operator-overloading-1) - [Templates](#templates) - [Standard types](#standard-types) - [Inheritance](#inheritance-1) - [Enums](#enums) - [Unfinished tales](#unfinished-tales) - [Safety](#safety) - [Lifetime and move semantics](#lifetime-and-move-semantics) - [Metaprogramming](#metaprogramming) - [Pattern matching as function overload resolution](#pattern-matching-as-function-overload-resolution) - [Error handling](#error-handling) - [Execution abstractions](#execution-abstractions) - [Abstract machine and execution model](#abstract-machine-and-execution-model) - [Lambdas](#lambdas) - [Co-routines](#co-routines) - [Concurrency](#concurrency) ## Introduction This documentation describes the design of the Carbon language, and the rationale for that design. This documentation is an overview of the Carbon project in its current state, written for the builders of Carbon and for those interested in learning more about Carbon. This document is _not_ a complete programming manual, and, nor does it provide detailed and comprehensive justification for design decisions. These descriptions are found in linked dedicated designs. ### This document is provisional This document includes much that is provisional or placeholder. This means that the syntax used, language rules, standard library, and other aspects of the design have things that have not been decided through the Carbon process. This preliminary material fills in gaps until aspects of the design can be filled in. Features that are provisional have been marked as such on a best-effort basis. ### Tour of the basics Here is a simple function showing some Carbon code: ```carbon import Core library "io"; import Math; // Returns the smallest factor of `n` > 1, and // whether `n` itself is prime. fn SmallestFactor(n: i32) -> (i32, bool) { let limit: i32 = Math.Sqrt(n) as i32; var i: i32 = 2; while (i <= limit) { let remainder: i32 = n % i; if (remainder == 0) { Core.Print("{0} is a factor of {1}", i, n); return (i, false); } if (i == 2) { i = 3; } else { // Skip even numbers once we get past `2`. i += 2; } } return (n, true); } ``` Carbon is a language that should feel familiar to C++ and C developers. This example has familiar constructs like [imports](#imports), [comments](#code-and-comments), [function definitions](#functions), [typed arguments](#binding-patterns), and [expressions](#expressions). [Statements](#blocks-and-statements) and [declarations](#declarations-definitions-and-scopes) are terminated with a `;` or something in curly braces `{`...`}`. A few other features that are unlike C or C++ may stand out. First, [declarations](#declarations-definitions-and-scopes) start with introducer keywords. `fn` introduces a function declaration, and `var` introduces a [variable declaration](#variable-var-declarations). The example starts with an [`import` declaration](#imports). Carbon imports are more like [C++ modules](https://en.cppreference.com/w/cpp/language/modules) than [textual inclusion during preprocessing using `#include`](https://en.cppreference.com/w/cpp/preprocessor/include). The `import` declaration imports a [library from a package](#files-libraries-packages). It must appear at the top of a Carbon source file, the first thing after the [optional `package` declaration](#package-declaration). Libraries can optionally be split into [API and implementation files](#files-libraries-packages), like C++'s header and source files but without requiring a source file in any cases. This declaration from the example: ```carbon import Math; ``` imports the default library from package `Math`. The names from this library are accessible as members of `Math`, like `Math.Sqrt`. The `Core.Print` function comes from the `Core` package's `io` library. Unlike C++, the namespaces of different packages are kept separate, so there are no name conflicts. Carbon [comments](#code-and-comments) must be on a line by themselves starting with `//`: ```carbon // Returns the smallest factor of `n` > 1, and // whether `n` itself is prime. ... // Skip even numbers once we get past `2`. ``` A [function definition](#functions) consists of: - the `fn` keyword introducer, - the function's name, - a parameter list in round parens `(`...`)`, - an optional `->` and return type, and - a body inside curly braces `{`...`}`. ```carbon fn SmallestFactor(n: i32) -> (i32, bool) { ... return (i, false); ... return (n, true); } ``` The body of the function is an ordered sequence of [statements](#blocks-and-statements) and [declarations](#declarations-definitions-and-scopes). Function execution ends when it reaches a `return` statement or the end of the function body. `return` statements can also specify an expression whose value is returned. Here `i32` refers to a signed [integer type](#integer-types), with 32 bits, and `bool` is the [boolean type](#bool). Carbon also has [floating-point types](#floating-point-types) like `f32` and `f64`, and [string types](#string-types). A [variable declaration](#variable-var-declarations) has three parts: - the `var` keyword introducer, - the name followed by a `:` and a type, declared the same way as a parameter in a function signature, and - an optional initializer. ```carbon var i: i32 = 2; ``` You can modify the value of a variable with an [assignment statement](assignment.md): ```carbon i = 3; ... ++i; ... i += 2; ``` [Constants are declared](#constant-let-declarations) with the `let` keyword introducer. The syntax parallels variable declarations except the initializer is required: ```carbon let limit: i32 = Math.Sqrt(n) as i32; ... let remainder: i32 = n % i; ``` The initializer `Math.Sqrt(n) as i32` is an [expression](#expressions). It first calls the `Math.Sqrt` function with `n` as the argument. Then, the `as` operator casts the floating-point return value to `i32`. Lossy conversions like that must be done explicitly. Other expressions include `n % i`, which applies the binary `%` modulo operator with `n` and `i` as arguments, and `remainder == 0`, which applies the `==` comparison operator producing a `bool` result. Expression return values are ignored when expressions are used as statements, as in this call to the `Core.Print` function: ```carbon Core.Print("{0} is a factor of {1}", i, n); ``` Function calls consist of the name of the function followed by the comma-separated argument list in round parentheses `(`...`)`. Control flow statements, including `if`, `while`, `for`, `break`, and `continue`, change the order that statements are executed, as they do in C++: ```carbon while (i <= limit) { ... if (remainder == 0) { ... } if (i == 2) { ... } else { ... } } ``` Every code block in curly braces `{`...`}` defines a scope. Names are visible from their declaration until the end of innermost scope containing it. So `remainder` in the example is visible until the curly brace `}` that closes the `while`. The example function uses a [_tuple_](#tuples), a [composite type](#composite-types), to return multiple values. Both tuple values and types are written using a comma-separated list inside parentheses. So `(i, false)` and `(n, true)` are tuple values, and `(i32, bool)` is their type. [Struct types](#struct-types) are similar, except their members are referenced by name instead of position. The example could be changed to use structs instead as follows: ```carbon // Return type of `{.factor: i32, .prime: bool}` is a struct // with an `i32` field named `.factor`, and a `bool` field // named `.prime`. fn SmallestFactor(n: i32) -> {.factor: i32, .prime: bool} { ... if (remainder == 0) { // Return a struct value. return {.factor = i, .prime = false}; } ... // Return a struct value. return {.factor = n, .prime = true}; } ``` ## Code and comments All source code is UTF-8 encoded text. Comments, identifiers, and strings are allowed to have non-ASCII characters. ```carbon var résultat: String = "Succès"; ``` Comments start with two slashes `//` and go to the end of the line. They are required to be the only non-whitespace on the line. ```carbon // Compute an approximation of π ``` > References: > > - [Source files](code_and_name_organization/source_files.md) > - [Lexical conventions](lexical_conventions) > - Proposal > [#142: Unicode source files](https://github.com/carbon-language/carbon-lang/pull/142) > - Proposal > [#198: Comments](https://github.com/carbon-language/carbon-lang/pull/198) ## Build modes The behavior of the Carbon compiler depends on the _build mode_: - In a _debug build_, the priority is diagnosing problems and fast build time. - In a _release build_, the priority is fastest execution time and lowest memory usage. > References: [Safety design](/docs/design/safety#build-modes) ## Types are values Expressions compute values in Carbon, and these values are always strongly typed much like in C++. However, an important difference from C++ is that types are themselves modeled as values; specifically, compile-time-constant values of type `type`. This has a number of consequences: - Names for types are in the same namespace shared with functions, variables, namespaces, and so on. - The grammar for writing a type is the [expression](#expressions) grammar, not a separate grammar for types. As a result, Carbon doesn't use angle brackets `<`...`>` in types, since `<` and `>` are used for comparison in expressions. - Function call syntax is used to specify parameters to a type, like `HashMap(String, i64)`. > References: > > - Proposal > [#2360: Types are values of type `type`](https://github.com/carbon-language/carbon-lang/pull/2360) ### Values usable as types A value used in a type position, like after a `:` in a variable declaration or the return type after a `->` in a function declaration, must: - be [a compile-time constant](#expression-phases), so the compiler can evaluate it at compile time, and - have a defined implicit conversion to type `type`. The actual type used is the result of the conversion to type `type`. Of course this includes values that already are of type `type`, but also allows some non-type values to be used in a type position. For example, the value `(bool, bool)` represents a [tuple](#tuples) of types, but is not itself a type, since it doesn't have type `type`. It does have a defined implicit conversion to type `type`, which results in the value `(bool, bool) as type`. This means `(bool, bool)` may be used in a type position. `(bool, bool) as type` is the type of the value `(true, false)` (among others), so this code is legal: ```carbon var b: (bool, bool) = (true, false);` ``` There is some need to be careful here, since the declaration makes it look like the type of `b` is `(bool, bool)`, when in fact it is `(bool, bool) as type`. `(bool, bool) as type` and `(bool, bool)` are different values since they have different types: the first has type `type`, and the second has type `(type, type) as type`. In addition to the types of [tuples](#tuples), this also comes up with [struct types](#struct-types) and [facets](generics/terminology.md#facet). ## Primitive types Primitive types fall into the following categories: - the boolean type `bool`, - signed and unsigned integer types, - IEEE-754 floating-point types, and - string types. These are made available through the [prelude](#name-lookup-for-common-types). ### `bool` The type `bool` is a boolean type with two possible values: `true` and `false`. The names `bool`, `true`, and `false` are keywords. [Comparison expressions](#expressions) produce `bool` values. The condition arguments in [control-flow statements](#control-flow), like [`if`](#if-and-else) and [`while`](#while), and [`if`-`then`-`else` conditional expressions](#expressions) take `bool` values. > References: > > - Question-for-leads issue > [#750: Naming conventions for Carbon-provided features](https://github.com/carbon-language/carbon-lang/issues/750) > - Proposal > [#861: Naming conventions](https://github.com/carbon-language/carbon-lang/pull/861) ### Integer types The signed-integer type with bit width `N` may be written `iN`, as long as `N` is a positive multiple of 8. For example, `i32` is a signed 32-bit integer. Signed-integer [overflow](expressions/arithmetic.md#overflow-and-other-error-conditions) is a programming error: - In a development build, overflow will be caught immediately when it happens at runtime. - In a performance build, the optimizer can assume that such conditions don't occur. As a consequence, if they do, the behavior of the program is not defined. - In a hardened build, overflow does not result in undefined behavior. Instead, either the program will be aborted, or the arithmetic will evaluate to a mathematically incorrect result, such as a two's complement result or zero. The unsigned-integer types may be written `uN`, with `N` a positive multiple of 8. Unsigned integer types wrap around on overflow; we strongly advise that they are not used except when those semantics are desired. These types are intended for bit manipulation or modular arithmetic as often found in [hashing](https://en.wikipedia.org/wiki/Hash_function), [cryptography](https://en.wikipedia.org/wiki/Cryptography), and [PRNG](https://en.wikipedia.org/wiki/Pseudorandom_number_generator) use cases. Values which can never be negative, like sizes, but for which wrapping does not make sense [should use signed integer types](/proposals/p1083.md#dont-let-unsigned-arithmetic-wrap). Identifiers of the form `iN` and `uN` are _type literals_, resulting in the corresponding type. Not all operations will be supported for all bit sizes. For example, division may be limited to integers of at most 128 bits due to LLVM limitations. > **Open question:** Bit-field ([1](https://en.wikipedia.org/wiki/Bit_field), > [2](https://en.cppreference.com/w/cpp/language/bit_field)) support will need > some way to talk about non-multiple-of-eight-bit integers, even though Carbon > will likely not support pointers to those types. > References: > > - [Numeric type literal expressions](expressions/literals.md#numeric-type-literals) > - Question-for-leads issue > [#543: pick names for fixed-size integer types](https://github.com/carbon-language/carbon-lang/issues/543) > - Question-for-leads issue > [#750: Naming conventions for Carbon-provided features](https://github.com/carbon-language/carbon-lang/issues/750) > - Proposal > [#861: Naming conventions](https://github.com/carbon-language/carbon-lang/pull/861) > - Proposal > [#1083: Arithmetic expressions](https://github.com/carbon-language/carbon-lang/pull/1083) > - Proposal > [#2015: Numeric type literal syntax](https://github.com/carbon-language/carbon-lang/pull/2015) #### Integer literals Integers may be written in decimal, hexadecimal, octal, or binary: - `12345` (decimal) - `0x1FE` (hexadecimal) - `0o755` (octal) - `0b1010` (binary) Underscores (`_`) may be used as digit separators. Numeric literals are case-sensitive: `0x`, `0o`, `0b` must be lowercase, whereas hexadecimal digits must be uppercase. Integer literals never contain a `.`. Unlike in C++, literals do not have a suffix to indicate their type. Instead, numeric literals have a type derived from their value, and can be [implicitly converted](expressions/implicit_conversions.md) to any type that can represent that value. > References: > > - [Integer literal syntax](lexical_conventions/numeric_literals.md#integer-literals) > - [Numeric literal expressions](expressions/literals.md#numeric-literals) > - Proposal > [#143: Numeric literals](https://github.com/carbon-language/carbon-lang/pull/143) > - Proposal > [#144: Numeric literal semantics](https://github.com/carbon-language/carbon-lang/pull/144) > - Proposal > [#820: Implicit conversions](https://github.com/carbon-language/carbon-lang/pull/820) > - Proposal > [#1983: Weaken digit separator placement rules](https://github.com/carbon-language/carbon-lang/pull/1983) ### Floating-point types Floating-point types in Carbon have IEEE-754 semantics, use the round-to-nearest rounding mode, and do not set any floating-point exception state. They are named using _type literals_, consisting of `f` and the number of bits, which must be a multiple of 8. These types will always be available: [`f16`](https://en.wikipedia.org/wiki/Half-precision_floating-point_format), [`f32`](https://en.wikipedia.org/wiki/Single-precision_floating-point_format), and [`f64`](https://en.wikipedia.org/wiki/Double-precision_floating-point_format). Other sizes may be available, depending on the platform, such as [`f80`](https://en.wikipedia.org/wiki/Extended_precision), [`f128`](https://en.wikipedia.org/wiki/Quadruple-precision_floating-point_format), or [`f256`](https://en.wikipedia.org/wiki/Octuple-precision_floating-point_format). Carbon also supports the [`BFloat16`](https://en.wikipedia.org/wiki/Bfloat16_floating-point_format) format, a 16-bit truncation of a "binary32" IEEE-754 format floating point number. > References: > > - [Numeric type literal expressions](expressions/literals.md#numeric-type-literals) > - Question-for-leads issue > [#543: pick names for fixed-size integer types](https://github.com/carbon-language/carbon-lang/issues/543) > - Question-for-leads issue > [#750: Naming conventions for Carbon-provided features](https://github.com/carbon-language/carbon-lang/issues/750) > - Proposal > [#861: Naming conventions](https://github.com/carbon-language/carbon-lang/pull/861) > - Proposal > [#1083: Arithmetic expressions](https://github.com/carbon-language/carbon-lang/pull/1083) > - Proposal > [#2015: Numeric type literal syntax](https://github.com/carbon-language/carbon-lang/pull/2015) #### Floating-point literals Floating-point types along with [user-defined types](#user-defined-types) may initialized from _real-number literals_. Decimal and hexadecimal real-number literals are supported: - `123.456` (digits on both sides of the `.`) - `123.456e789` (optional `+` or `-` after the `e`) - `0x1.Ap123` (optional `+` or `-` after the `p`) As with integer literals, underscores (`_`) may be used as digit separators. Real-number literals always have a period (`.`) and a digit on each side of the period. When a real-number literal is interpreted as a value of a floating-point type, its value is the representable real number closest to the value of the literal. In the case of a tie, the nearest value whose mantissa is even is selected. > References: > > - [Real-number literal syntax](lexical_conventions/numeric_literals.md#real-number-literals) > - [Numeric literal expressions](expressions/literals.md#numeric-literals) > - Proposal > [#143: Numeric literals](https://github.com/carbon-language/carbon-lang/pull/143) > - Proposal > [#144: Numeric literal semantics](https://github.com/carbon-language/carbon-lang/pull/144) > - Proposal > [#820: Implicit conversions](https://github.com/carbon-language/carbon-lang/pull/820) > - Proposal > [#866: Allow ties in floating literals](https://github.com/carbon-language/carbon-lang/pull/866) > - Proposal > [#1983: Weaken digit separator placement rules](https://github.com/carbon-language/carbon-lang/pull/1983) ### String types > **Note:** This is provisional, no design for string types has been through the > proposal process yet. There are two string types: - `String` - a byte sequence treated as containing UTF-8 encoded text. - `StringView` - a read-only reference to a byte sequence treated as containing UTF-8 encoded text. There is an [implicit conversion](expressions/implicit_conversions.md) from `String` to `StringView`. > References: > > - Question-for-leads issue > [#750: Naming conventions for Carbon-provided features](https://github.com/carbon-language/carbon-lang/issues/750) > - Proposal > [#820: Implicit conversions](https://github.com/carbon-language/carbon-lang/pull/820) > - Proposal > [#861: Naming conventions](https://github.com/carbon-language/carbon-lang/pull/861) #### String literals String literals may be written on a single line using a double quotation mark (`"`) at the beginning and end of the string, as in `"example"`. Multi-line string literals, called _block string literals_, begin and end with three single quotation marks (`'''`), and may have a file type indicator after the first `'''`. ```carbon // Block string literal: var block: String = ''' The winds grow high; so do your stomachs, lords. How irksome is this music to my heart! When such strings jar, what hope of harmony? I pray, my lords, let me compound this strife. -- History of Henry VI, Part II, Act II, Scene 1, W. Shakespeare '''; ``` The indentation of a block string literal's terminating line is removed from all preceding lines. Strings may contain [escape sequences](lexical_conventions/string_literals.md#escape-sequences) introduced with a backslash (`\`). [Raw string literals](lexical_conventions/string_literals.md#raw-string-literals) are available for representing strings with `\`s and `"`s. > References: > > - [String literals](lexical_conventions/string_literals.md) > - Proposal > [#199: String literals](https://github.com/carbon-language/carbon-lang/pull/199) ## Values, objects, and expressions Carbon has both abstract _values_ and concrete _objects_. Carbon _values_ are things like `42`, `true`, and `i32` (a type value). Carbon _objects_ have _storage_ where values can be read and written. Storage also allows taking the address of an object in memory in Carbon. > References: > > - [Values, variables, and pointers](values.md) > - Proposal > [#2006: Values, variables, pointers, and references](https://github.com/carbon-language/carbon-lang/pull/2006) ### Expression categories A Carbon expression produces a value, references an object, or initializes an object. Every expression has a [category](), similar to [C++](https://en.cppreference.com/w/cpp/language/value_category): - [_Value expressions_](values.md#value-expressions) produce abstract, read-only _values_ that cannot be modified or have their address taken. - [_Reference expressions_](values.md#reference-expressions) refer to _objects_ with _storage_ where a value may be read or written and the object's address can be taken. - [_Initializing expressions_](values.md#initializing-expressions) which require storage to be provided implicitly when evaluating the expression. The expression then initializes an object in that storage. These are used to model function returns, which can construct the returned value directly in the caller's storage. Expressions in one category can be converted to any other category when needed. The primitive conversion steps used are: - _Value acquisition_ converts a reference expression into a value expression. - _Direct initialization_ converts a value expression into an initializing expression. - _Copy initialization_ converts a reference expression into an initializing expression. - _Temporary materialization_ converts an initializing expression into a reference expression. > References: > > - [Expression categories](values.md#expression-categories) > - Proposal > [#2006: Values, variables, pointers, and references](https://github.com/carbon-language/carbon-lang/pull/2006) ### Expression phases Value expressions are further broken down into three _expression phases_: - A _template constant_ has a value known at compile time, and that value is available during type checking, for example to use as the size of an array. These include literals ([integer](#integer-literals), [floating-point](#floating-point-literals), [string](#string-literals)), concrete type values (like `f64` or `Optional(i32*)`), expressions in terms of constants, and values of [`template` parameters](#checked-and-template-parameters). - A _symbolic constant_ has a value that will be known at the code generation stage of compilation when [monomorphization](https://en.wikipedia.org/wiki/Monomorphization) happens, but is not known during type checking. This includes [checked-generic parameters](#checked-and-template-parameters), and type expressions with checked-generic arguments, like `Optional(T*)`. - A _runtime value_ has a dynamic value only known at runtime. Template constants and symbolic constants are collectively called _compile-time constants_ and correspond to declarations using `:!`. Carbon will automatically convert a template constant to a symbolic constant, or any value to a runtime value: ```mermaid graph TD; A(template constant)-->B(symbolic constant)-->C(runtime value); D(reference expression)-->C; ``` Template constants convert to symbolic constants and to runtime values. Symbolic constants will generally convert into runtime values if an operation that inspects the value is performed on them. Runtime values will convert into template or symbolic constants if constant evaluation of the runtime expression succeeds. > **Note:** Conversion of runtime values to other phases is provisional. > References: > > - Proposal > [#2200: Template generics](https://github.com/carbon-language/carbon-lang/pull/2200) > - Proposal > [#2964: Expression phase terminology](https://github.com/carbon-language/carbon-lang/pull/2964) > - Proposal > [#3162: Reduce ambiguity in terminology](https://github.com/carbon-language/carbon-lang/pull/3162) ## Composite types ### Tuples A tuple is a fixed-size collection of values that can have different types, where each value is identified by its position in the tuple. An example use of tuples is to return multiple values from a function: ```carbon fn DoubleBoth(x: i32, y: i32) -> (i32, i32) { return (2 * x, 2 * y); } ``` Breaking this example apart: - The return type is a tuple of two `i32` types. - The expression uses tuple syntax to build a tuple of two `i32` values. Both of these are expressions using the tuple syntax `(, )`. The only difference is the type of the tuple expression: one is a tuple of types, the other a tuple of values. In other words, a tuple type is a tuple _of_ types. The components of a tuple are accessed positionally, so element access uses subscript syntax, but the index must be a compile-time constant: ```carbon fn DoubleTuple(x: (i32, i32)) -> (i32, i32) { return (2 * x[0], 2 * x[1]); } ``` Tuple types are [structural](https://en.wikipedia.org/wiki/Structural_type_system). > **Note:** This is provisional, no design for tuples has been through the > proposal process yet. Many of these questions were discussed in dropped > proposal [#111](https://github.com/carbon-language/carbon-lang/pull/111). > References: [Tuples](tuples.md) ### Struct types Carbon also has [structural types](https://en.wikipedia.org/wiki/Structural_type_system) whose members are identified by name instead of position. These are called _structural data classes_, also known as a _struct types_ or _structs_. Both struct types and values are written inside curly braces (`{`...`}`). In both cases, they have a comma-separated list of members that start with a period (`.`) followed by the field name. - In a struct type, the field name is followed by a colon (`:`) and the type, as in: `{.name: String, .count: i32}`. - In a struct value, called a _structural data class literal_ or a _struct literal_, the field name is followed by an equal sign (`=`) and the value, as in `{.key = "Joe", .count = 3}`. > References: > > - [Struct types](classes.md#struct-types) > - Proposal > [#561: Basic classes: use cases, struct literals, struct types, and future work](https://github.com/carbon-language/carbon-lang/pull/561) > - Proposal > [#981: Implicit conversions for aggregates](https://github.com/carbon-language/carbon-lang/pull/981) > - Proposal > [#710: Default comparison for data classes](https://github.com/carbon-language/carbon-lang/issues/710) ### Pointer types The type of pointers-to-values-of-type-`T` is written `T*`. Carbon pointers do not support [pointer arithmetic](); the only pointer [operations](#expressions) are: - Dereference: given a pointer `p`, `*p` gives the value `p` points to as a [reference expression](#expression-categories). `p->m` is syntactic sugar for `(*p).m`. - Address-of: given a [reference expression](#expression-categories) `x`, `&x` returns a pointer to `x`. There are no [null pointers](https://en.wikipedia.org/wiki/Null_pointer) in Carbon. To represent a pointer that may not refer to a valid object, use the type `Optional(T*)`. **Future work:** Perhaps Carbon will have [stricter pointer provenance](https://www.ralfj.de/blog/2022/04/11/provenance-exposed.html) or restrictions on casts between pointers and integers. > References: > > - [Pointers](values.md#pointers) > - Proposal > [#2006: Values, variables, pointers, and references](https://github.com/carbon-language/carbon-lang/pull/2006) ### Arrays and slices > **TODO:** The provisional array syntax documented here has been superseded by > [p4682: The Core.Array type for direct-storage immutably-sized buffers](/proposals/p4682.md). The type of an array of holding 4 `i32` values is written `[i32; 4]`. There is an [implicit conversion](expressions/implicit_conversions.md) from tuples to arrays of the same length as long as every component of the tuple may be implicitly converted to the destination element type. In cases where the size of the array may be deduced, it may be omitted, as in: ```carbon var i: i32 = 1; // `[i32;]` equivalent to `[i32; 3]` here. var a: [i32;] = (i, i, i); ``` Elements of an array may be accessed using square brackets (`[`...`]`), as in `a[i]`: ```carbon a[i] = 2; Core.Print(a[0]); ``` > **TODO:** Slices > **Note:** This is provisional, no design for arrays has been through the > proposal process yet. ## Expressions Expressions describe some computed value. The simplest example would be a literal number like `42`: an expression that computes the integer value 42. Some common expressions in Carbon include: - Literals: - [boolean](#bool): `true`, `false` - [integer](#integer-literals): `42`, `-7` - [real-number](#floating-point-literals): `3.1419`, `6.022e+23` - [string](#string-literals): `"Hello World!"` - [tuple](#tuples): `(1, 2, 3)` - [struct](#struct-types): `{.word = "the", .count = 56}` - [Names](#names) and [member access](expressions/member_access.md) - [Operators](expressions#operators): - [Arithmetic](expressions/arithmetic.md): `-x`, `1 + 2`, `3 - 4`, `2 * 5`, `6 / 3`, `5 % 3` - [Bitwise](expressions/bitwise.md): `2 & 3`, `2 | 4`, `3 ^ 1`, `^7` - [Bit shift](expressions/bitwise.md): `1 << 3`, `8 >> 1` - [Comparison](expressions/comparison_operators.md): `2 == 2`, `3 != 4`, `5 < 6`, `7 > 6`, `8 <= 8`, `8 >= 8` - [Conversion](expressions/as_expressions.md): `2 as i32` - [Logical](expressions/logical_operators.md): `a and b`, `c or d`, `not e` - [Indexing](#arrays-and-slices): `a[3]` - [Function](#functions) call: `f(4)` - [Pointer](expressions/pointer_operators.md): `*p`, `p->m`, `&x` - [Move](#move): `~x` - [Conditionals](expressions/if.md): `if c then t else f` - Parentheses: `(7 + 8) * (3 - 1)` When an expression appears in a context in which an expression of a specific type is expected, [implicit conversions](expressions/implicit_conversions.md) are applied to convert the expression to the target type. > References: > > - [Expressions](expressions/) > - Proposal > [#162: Basic Syntax](https://github.com/carbon-language/carbon-lang/pull/162) > - Proposal > [#555: Operator precedence](https://github.com/carbon-language/carbon-lang/pull/555) > - Proposal > [#601: Operator tokens](https://github.com/carbon-language/carbon-lang/pull/601) > - Proposal > [#680: And, or, not](https://github.com/carbon-language/carbon-lang/pull/680) > - Proposal > [#702: Comparison operators](https://github.com/carbon-language/carbon-lang/pull/702) > - Proposal > [#845: as expressions](https://github.com/carbon-language/carbon-lang/pull/845) > - Proposal > [#911: Conditional expressions](https://github.com/carbon-language/carbon-lang/pull/911) > - Proposal > [#1083: Arithmetic expressions](https://github.com/carbon-language/carbon-lang/pull/1083) > - Proposal > [#2006: Values, variables, pointers, and references](https://github.com/carbon-language/carbon-lang/pull/2006) ## Declarations, Definitions, and Scopes _Declarations_ introduce a new [name](#names) and say what that name represents. For some kinds of entities, like [functions](#functions), there are two kinds of declarations: _forward declarations_ and _definitions_. For those entities, there should be exactly one definition for the name, and at most one additional forward declaration that introduces the name before it is defined, plus any number of declarations in a [`match_first` block](generics/details.md#prioritization-rule). Forward declarations can be used to separate interface from implementation, such as to declare a name in an [API file](#files-libraries-packages) that is defined in an [implementation file](#files-libraries-packages). Forward declarations also allow entities to be used before they are defined, such as to allow cyclic references. A name that has been declared but not defined is called _incomplete_, and in some cases there are limitations on what can be done with an incomplete name. Within a definition, the defined name is incomplete until the end of the definition is reached, but is complete in the bodies of member functions because they are [parsed as if they appeared after the definition](#class-functions-and-factory-functions). A name is valid until the end of the innermost enclosing [_scope_](). There are a few kinds of scopes: - the outermost scope, which includes the whole file, - scopes that are enclosed in curly braces (`{`...`}`), and - scopes that encompass a single declaration. For example, the names of the parameters of a [function](#functions) or [class](#classes) are valid until the end of the declaration. The name of the function or class itself is visible until the end of the enclosing scope. > References: > > - [Principle: Information accumulation](/docs/project/principles/information_accumulation.md) > - Proposal > [#875: Principle: information accumulation](https://github.com/carbon-language/carbon-lang/pull/875) > - Question-for-leads issue > [#472: Open question: Calling functions defined later in the same file](https://github.com/carbon-language/carbon-lang/issues/472) ## Patterns > **Note:** This is provisional, no design for patterns has been through the > proposal process yet. A _pattern_ says how to receive some data that is being matched against. There are two kinds of patterns: - _Refutable_ patterns can fail to match based on the runtime value being matched. - _Irrefutable_ patterns are guaranteed to match, so long as the code type-checks. In the [introduction](#tour-of-the-basics), [function parameters](#functions), [variable `var` declarations](#variable-var-declarations), and [constant `let` declarations](#constant-let-declarations) use a "name `:` type" construction. That construction is an example of an irrefutable pattern, and in fact any irrefutable pattern may be used in those positions. [`match` statements](#match) can include both refutable patterns and irrefutable patterns. > References: > > - [Pattern matching](pattern_matching.md) > - Proposal > [#162: Basic Syntax](https://github.com/carbon-language/carbon-lang/pull/162) > - Proposal > [#2188: Pattern matching syntax and semantics](https://github.com/carbon-language/carbon-lang/pull/2188) ### Binding patterns The most common irrefutable pattern is a _binding pattern_, consisting of a new name, a colon (`:`), and a type. It binds the matched value of that type to that name. It can only match values that may be [implicitly converted](expressions/implicit_conversions.md) to that type. A underscore (`_`) may be used instead of the name to match a value but without binding any name to it. Binding patterns default to _`let` bindings_. The `var` keyword is used to make it a _`var` binding_. - A `let` binding binds a name to a value, so the name can be used as a [value expression](#expression-categories). This means the value cannot be modified, and its address generally cannot be taken. - A `var` binding creates an object with dedicated storage, and so the name can be used as a [reference expression](#expression-categories) which can be modified and has a stable address. A `let`-binding may be [implemented](values.md#value-expressions) as an alias for the original value (like a [`const` reference in C++]()), or it may be copied from the original value (if it is copyable), or it may be moved from the original value (if it was a temporary). The Carbon implementation's choice among these options may be indirectly observable, for example through side effects of the destructor, copy, and move operations, but the program's correctness must not depend on which option the Carbon implementation chooses. A [compile-time binding](#checked-and-template-parameters) uses `:!` instead of a colon (`:`) and can only match [compile-time constants](#expression-phases), not run-time values. A `template` keyword before the binding selects a template binding instead of a symbolic binding. The keyword `auto` may be used in place of the type in a binding pattern, as long as the type can be deduced from the type of a value in the same declaration. > References: > > - Proposal > [#3162: Reduce ambiguity in terminology](https://github.com/carbon-language/carbon-lang/pull/3162) ### Destructuring patterns There are also irrefutable _destructuring patterns_, such as _tuple destructuring_. A tuple destructuring pattern looks like a tuple of patterns. It may only be used to match tuple values whose components match the component patterns of the tuple. An example use is: ```carbon // `Bar()` returns a tuple consisting of an // `i32` value and 2-tuple of `f32` values. fn Bar() -> (i32, (f32, f32)); fn Foo() -> i64 { // Pattern in `var` declaration: var (p: i64, _: auto) = Bar(); return p; } ``` The pattern used in the `var` declaration destructures the tuple value returned by `Bar()`. The first component pattern, `p: i64`, corresponds to the first component of the value returned by `Bar()`, which has type `i32`. This is allowed since there is an implicit conversion from `i32` to `i64`. The result of this conversion is assigned to the name `p`. The second component pattern, `_: auto`, matches the second component of the value returned by `Bar()`, which has type `(f32, f32)`. ### Refutable patterns Additional kinds of patterns are allowed in [`match` statements](#match), that may or may not match based on the runtime value of the `match` expression: - An _expression pattern_ is an expression, such as `42`, whose value must be equal to match. - A _choice pattern_ matches one case from a choice type, as described in [the choice types section](#choice-types). - A _dynamic cast pattern_ is tests the dynamic type, as described in [inheritance](#inheritance). See [`match`](#match) for examples of refutable patterns. > References: > > - [Pattern matching](pattern_matching.md) > - Question-for-leads issue > [#1283: how should pattern matching and implicit conversion interact?](https://github.com/carbon-language/carbon-lang/issues/1283) ## Name-binding declarations There are two kinds of name-binding declarations: - constant declarations, introduced with `let`, and - variable declarations, introduced with `var`. There are no forward declarations of these; all name-binding declarations are [definitions](#declarations-definitions-and-scopes). ### Constant `let` declarations A `let` declaration matches an [irrefutable pattern](#patterns) to a value. In this example, the name `x` is bound to the value `42` with type `i64`: ```carbon let x: i64 = 42; ``` Here `x: i64` is the pattern, which is followed by an equal sign (`=`) and the value to match, `42`. The names from [binding patterns](#binding-patterns) are introduced into the enclosing [scope](#declarations-definitions-and-scopes). > References: > > - [Binding patterns and local variables with `let` and `var`](values.md#binding-patterns-and-local-variables-with-let-and-var) > - Proposal > [#2006: Values, variables, pointers, and references](https://github.com/carbon-language/carbon-lang/pull/2006) ### Variable `var` declarations A `var` declaration is similar, except with `var` bindings, so `x` here is a [reference expression](#expression-categories) for an object with storage and an address, and so may be modified: ```carbon var x: i64 = 42; x = 7; ``` Variables with a type that has [an unformed state](#unformed-state) do not need to be initialized in the variable declaration, but do need to be assigned before they are used. > References: > > - [Binding patterns and local variables with `let` and `var`](values.md#binding-patterns-and-local-variables-with-let-and-var) > - Proposal > [#162: Basic Syntax](https://github.com/carbon-language/carbon-lang/pull/162) > - Proposal > [#257: Initialization of memory and variables](https://github.com/carbon-language/carbon-lang/pull/257) > - Proposal > [#339: Add `var [ = ];` syntax for variables](https://github.com/carbon-language/carbon-lang/pull/339) > - Proposal > [#618: var ordering](https://github.com/carbon-language/carbon-lang/pull/618) > - Proposal > [#2006: Values, variables, pointers, and references](https://github.com/carbon-language/carbon-lang/pull/2006) ### `auto` If `auto` is used as the type in a `var` or `let` declaration, the type is the static type of the initializer expression, which is required. ``` var x: i64 = 2; // The type of `y` is inferred to be `i64`. let y: auto = x + 3; // The type of `z` is inferred to be `bool`. var z: auto = (y > 1); ``` > References: > > - [Type inference](type_inference.md) > - Proposal > [#851: auto keyword for vars](https://github.com/carbon-language/carbon-lang/pull/851) ### Global constants and variables [Constant `let` declarations](#constant-let-declarations) may occur at a global scope as well as local and member scopes. However, there are currently no global variables. > **Note**: The semantics of global constant declarations and absence of global > variable declarations is currently provisional. > > We are exploring several different ideas for how to design less bug-prone > patterns to replace the important use cases programmers still have for global > variables. We may be unable to fully address them, at least for migrated code, > and be forced to add some limited form of global variables back. We may also > discover that their convenience outweighs any improvements afforded. ## Functions Functions are the core unit of behavior. For example, this is a [forward declaration](#declarations-definitions-and-scopes) of a function that adds two 64-bit integers: ```carbon fn Add(a: i64, b: i64) -> i64; ``` Breaking this apart: - `fn` is the keyword used to introduce a function. - Its name is `Add`. This is the name added to the enclosing [scope](#declarations-definitions-and-scopes). - The [parameter list](#parameters) in parentheses (`(`...`)`) is a comma-separated list of [irrefutable patterns](#patterns). - It returns an `i64` result. Functions that return nothing omit the `->` and return type. You would call this function like `Add(1, 2)`. A function definition is a function declaration that has a body [block](#blocks-and-statements) instead of a semicolon: ```carbon fn Add(a: i64, b: i64) -> i64 { return a + b; } ``` The names of the parameters are in scope until the end of the definition or declaration. The parameter names in a forward declaration may be omitted using `_`, but must match the definition if they are specified. > References: > > - [Functions](functions.md) > - Proposal > [#162: Basic Syntax](https://github.com/carbon-language/carbon-lang/pull/162) > - Proposal > [#438: Add statement syntax for function declarations](https://github.com/carbon-language/carbon-lang/pull/438) > - Question-for-leads issue > [#476: Optional argument names (unused arguments)](https://github.com/carbon-language/carbon-lang/issues/476) > - Question-for-leads issue > [#1132: How do we match forward declarations with their definitions?](https://github.com/carbon-language/carbon-lang/issues/1132) ### Parameters The bindings in the parameter list default to [`let` bindings](#binding-patterns), and so the parameter names are treated as [value expressions](#expression-categories). This is appropriate for input parameters. This binding will be implemented using a pointer, unless it is legal to copy and copying is cheaper. If the `var` keyword is added before the binding pattern, then the arguments will be copied (or moved from a temporary) to new storage, and so can be mutated in the function body. The copy ensures that any mutations will not be visible to the caller. Use a [pointer](#pointer-types) parameter type to represent an [input/output parameter](), allowing a function to modify a variable of the caller's. This makes the possibility of those modifications visible: by taking the address using `&` in the caller, and dereferencing using `*` in the callee. Outputs of a function should prefer to be returned. Multiple values may be returned using a [tuple](#tuples) or [struct](#struct-types) type. > References: > > - [Binding patterns and local variables with `let` and `var`](values.md#binding-patterns-and-local-variables-with-let-and-var) > - Proposal > [#2006: Values, variables, pointers, and references](https://github.com/carbon-language/carbon-lang/pull/2006) ### `auto` return type If `auto` is used in place of the return type, the return type of the function is inferred from the function body. It is set to [common type](#common-type) of the static type of arguments to the [`return` statements](#return) in the function. This is not allowed in a forward declaration. ``` // Return type is inferred to be `bool`, the type of `a > 0`. fn Positive(a: i64) -> auto { return a > 0; } ``` > References: > > - [Type inference](type_inference.md) > - [Function return clause](functions.md#return-clause) > - Proposal > [#826: Function return type inference](https://github.com/carbon-language/carbon-lang/pull/826) ### Blocks and statements A _block_ is a sequence of _statements_. A block defines a [scope](#declarations-definitions-and-scopes) and, like other scopes, is enclosed in curly braces (`{`...`}`). Each statement is terminated by a semicolon or block. [Expressions](#expressions), [assignments](assignment.md), [`var`](#variable-var-declarations) and [`let`](#constant-let-declarations) are valid statements. Statements within a block are normally executed in the order they appear in the source code, except when modified by control-flow statements. The body of a function is defined by a block, and some [control-flow statements](#control-flow) have their own blocks of code. These are nested within the enclosing scope. For example, here is a function definition with a block of statements defining the body of the function, and a nested block as part of a `while` statement: ```carbon fn Foo() { Bar(); while (Baz()) { Quux(); } } ``` > References: > > - [Blocks and statements](blocks_and_statements.md) > - Proposal > [#162: Basic Syntax](https://github.com/carbon-language/carbon-lang/pull/162) > - Proposal > [#2665: Semicolons terminate statements](https://github.com/carbon-language/carbon-lang/pull/2665) ### Control flow Blocks of statements are generally executed sequentially. Control-flow statements give additional control over the flow of execution and which statements are executed. Some control-flow statements include [blocks](#blocks-and-statements). Those blocks will always be within curly braces `{`...`}`. ```carbon // Curly braces { ... } are required. if (condition) { ExecutedWhenTrue(); } else { ExecutedWhenFalse(); } ``` This is unlike C++, which allows control-flow constructs to omit curly braces around a single statement. > References: > > - [Control flow](control_flow/README.md) > - Proposal > [#162: Basic Syntax](https://github.com/carbon-language/carbon-lang/pull/162) > - Proposal > [#623: Require braces](https://github.com/carbon-language/carbon-lang/pull/623) #### `if` and `else` `if` and `else` provide conditional execution of statements. An `if` statement consists of: - An `if` introducer followed by a condition in parentheses. If the condition evaluates to `true`, the block following the condition is executed, otherwise it is skipped. - This may be followed by zero or more `else if` clauses, whose conditions are evaluated if all prior conditions evaluate to `false`, with a block that is executed if that evaluation is to `true`. - A final optional `else` clause, with a block that is executed if all conditions evaluate to `false`. For example: ```carbon if (fruit.IsYellow()) { Core.Print("Banana!"); } else if (fruit.IsOrange()) { Core.Print("Orange!"); } else { Core.Print("Vegetable!"); } ``` This code will: - Print `Banana!` if `fruit.IsYellow()` is `true`. - Print `Orange!` if `fruit.IsYellow()` is `false` and `fruit.IsOrange()` is `true`. - Print `Vegetable!` if both of the above return `false`. > References: > > - [Control flow](control_flow/conditionals.md) > - Proposal > [#285: if/else](https://github.com/carbon-language/carbon-lang/pull/285) #### Loops > References: [Loops](control_flow/loops.md) ##### `while` `while` statements loop for as long as the passed expression returns `true`. For example, this prints `0`, `1`, `2`, then `Done!`: ```carbon var x: i32 = 0; while (x < 3) { Core.Print(x); ++x; } Core.Print("Done!"); ``` > References: > > - [`while` loops](control_flow/loops.md#while) > - Proposal > [#340: Add C++-like `while` loops](https://github.com/carbon-language/carbon-lang/pull/340) ##### `for` `for` statements support range-based looping, typically over containers. For example, this prints each `String` value in `names`: ```carbon for (var name: String in names) { Core.Print(name); } ``` > References: > > - [`for` loops](control_flow/loops.md#for) > - Proposal > [#353: Add C++-like `for` loops](https://github.com/carbon-language/carbon-lang/pull/353) > - Proposal > [#1885: ranged-based `for` for user-defined types](https://github.com/carbon-language/carbon-lang/pull/1885) ##### `break` The `break` statement immediately ends a `while` or `for` loop. Execution will continue starting from the end of the loop's scope. For example, this processes steps until a manual step is hit (if no manual step is hit, all steps are processed): ```carbon for (var step: Step in steps) { if (step.IsManual()) { Core.Print("Reached manual step!"); break; } step.Process(); } ``` > References: > > - [`break`](control_flow/loops.md#break) > - Proposal > [#340: Add C++-like `while` loops](https://github.com/carbon-language/carbon-lang/pull/340) > - Proposal > [#353: Add C++-like `for` loops](https://github.com/carbon-language/carbon-lang/pull/353) ##### `continue` The `continue` statement immediately goes to the next loop of a `while` or `for`. In a `while`, execution continues with the `while` expression. For example, this prints all non-empty lines of a file, using `continue` to skip empty lines: ```carbon var f: File = OpenFile(path); while (!f.EOF()) { var line: String = f.ReadLine(); if (line.IsEmpty()) { continue; } Core.Print(line); } ``` > References: > > - [`continue`](control_flow/loops.md#continue) > - Proposal > [#340: Add C++-like `while` loops](https://github.com/carbon-language/carbon-lang/pull/340) > - Proposal > [#353: Add C++-like `for` loops](https://github.com/carbon-language/carbon-lang/pull/353) #### `return` The `return` statement ends the flow of execution within a function, returning execution to the caller. ```carbon // Prints the integers 1 .. `n` and then // returns to the caller. fn PrintFirstN(n: i32) { var i: i32 = 0; while (true) { i += 1; if (i > n) { // None of the rest of the function is // executed after a `return`. return; } Core.Print(i); } } ``` If the function returns a value to the caller, that value is provided by an expression in the return statement. For example: ```carbon fn Sign(i: i32) -> i32 { if (i > 0) { return 1; } if (i < 0) { return -1; } return 0; } Assert(Sign(-3) == -1); ``` > References: > > - [`return`](control_flow/return.md) > - [`return` statements](functions.md#return-statements) > - Proposal > [#415: return](https://github.com/carbon-language/carbon-lang/pull/415) > - Proposal > [#538: return with no argument](https://github.com/carbon-language/carbon-lang/pull/538) ##### `returned var` To avoid a copy when returning a variable, add a `returned` prefix to the variable's declaration and use `return var` instead of returning an expression, as in: ```carbon fn MakeCircle(radius: i32) -> Circle { returned var c: Circle; c.radius = radius; // `return c` would be invalid because `returned` is in use. return var; } ``` This is instead of [the "named return value optimization" of C++](https://en.wikipedia.org/wiki/Copy_elision#Return_value_optimization). > References: > > - [`returned var`](control_flow/return.md#returned-var) > - Proposal > [#257: Initialization of memory and variables](https://github.com/carbon-language/carbon-lang/pull/257) #### `match` `match` is a control flow similar to `switch` of C and C++ and mirrors similar constructs in other languages, such as Swift. The `match` keyword is followed by an expression in parentheses, whose value is matched against the `case` declarations, each of which contains a [refutable pattern](#refutable-patterns), in order. The refutable pattern may optionally be followed by an `if` expression, which may use the names from bindings in the pattern. The code for the first matching `case` is executed. An optional `default` block may be placed after the `case` declarations, it will be executed if none of the `case` declarations match. An example `match` is: ```carbon fn Bar() -> (i32, (f32, f32)); fn Foo() -> f32 { match (Bar()) { case (42, (x: f32, y: f32)) => { return x - y; } case (p: i32, (x: f32, _: f32)) if (p < 13) => { return p * x; } case (p: i32, _: auto) if (p > 3) => { return p * Pi; } default => { return Pi; } } } ``` > References: > > - [Pattern matching](pattern_matching.md) > - Question-for-leads issue > [#1283: how should pattern matching and implicit conversion interact?](https://github.com/carbon-language/carbon-lang/issues/1283) > - Proposal > [#2188: Pattern matching syntax and semantics](https://github.com/carbon-language/carbon-lang/pull/2188) ## User-defined types ### Classes _Nominal classes_, or just [_classes_](), are a way for users to define their own [data structures](https://en.wikipedia.org/wiki/Data_structure) or [record types](). This is an example of a class [definition](#declarations-definitions-and-scopes): ```carbon class Widget { var x: i32; var y: i32; var payload: String; } ``` Breaking this apart: - This defines a class named `Widget`. `Widget` is the name added to the enclosing [scope](#declarations-definitions-and-scopes). - The name `Widget` is followed by curly braces (`{`...`}`) containing the class _body_, making this a [definition](#declarations-definitions-and-scopes). A [forward declaration](#declarations-definitions-and-scopes) would instead have a semicolon(`;`). - Those braces delimit the class' [scope](#declarations-definitions-and-scopes). - Fields, or [instances variables](https://en.wikipedia.org/wiki/Instance_variable), are defined using [`var` declarations](#variable-var-declarations). `Widget` has two `i32` fields (`x` and `y`), and one `String` field (`payload`). The order of the field declarations determines the fields' memory-layout order. Classes may have other kinds of members beyond fields declared in its scope: - [Class functions](#class-functions-and-factory-functions) - [Methods](#methods) - [`alias`](#aliases) - [`let`](#constant-let-declarations) to define class constants. **TODO:** Another syntax to define constants associated with the class like `class let` or `static let`? - `class`, to define a [_member class_ or _nested class_](https://en.wikipedia.org/wiki/Inner_class) Within the scope of a class, the unqualified name `Self` can be used to refer to the class itself. Members of a class are [accessed](expressions/member_access.md) using the dot (`.`) notation, so given an instance `dial` of type `Widget`, `dial.payload` refers to its `payload` field. Both [structural data classes](#struct-types) and nominal classes are considered _class types_, but they are commonly referred to as "structs" and "classes" respectively when that is not confusing. Like structs, classes refer to their members by name. Unlike structs, classes are [nominal types](https://en.wikipedia.org/wiki/Nominal_type_system#Nominal_typing). > References: > > - [Classes](classes.md#nominal-class-types) > - Proposal > [#722: Nominal classes and methods](https://github.com/carbon-language/carbon-lang/pull/722) > - Proposal > [#989: Member access expressions](https://github.com/carbon-language/carbon-lang/pull/989) #### Assignment There is an [implicit conversions](expressions/implicit_conversions.md) defined between a [struct literal](#struct-types) and a class type with the same fields, in any scope that has [access](#access-control) to all of the class' fields. This may be used to assign or initialize a variable with a class type, as in: ```carbon var sprocket: Widget = {.x = 3, .y = 4, .payload = "Sproing"}; sprocket = {.x = 2, .y = 1, .payload = "Bounce"}; ``` > References: > > - [Classes: Assignment](classes.md#assignment) > - Proposal > [#722: Nominal classes and methods](https://github.com/carbon-language/carbon-lang/pull/722) > - Proposal > [#981: Implicit conversions for aggregates](https://github.com/carbon-language/carbon-lang/pull/981) #### Class functions and factory functions Classes may also contain _class functions_. These are functions that are accessed as members of the type, like [static member functions in C++](), as opposed to [methods](#methods) that are members of instances. They are commonly used to define a function that creates instances. Carbon does not have separate [constructors]() like C++ does. ```carbon class Point { // Class function that instantiates `Point`. // `Self` in class scope means the class currently being defined. fn Origin() -> Self { return {.x = 0, .y = 0}; } var x: i32; var y: i32; } ``` Note that if the definition of a function is provided inside the class scope, the body is treated as if it was defined immediately after the outermost class definition. This means that members such as the fields will be considered declared even if their declarations are later in the source than the class function. The [`returned var` feature](#returned-var) can be used if the address of the instance being created is needed in a factory function, as in: ```carbon class Registered { fn Make() -> Self { returned var result: Self = {...}; StoreMyPointerSomewhere(&result); return var; } } ``` This approach can also be used for types that can't be copied or moved. > References: > > - [Classes: Construction](classes.md#construction) > - Proposal > [#722: Nominal classes and methods](https://github.com/carbon-language/carbon-lang/pull/722) #### Methods Class type definitions can include methods: ```carbon class Point { // Method defined inline fn Distance[self: Self](x2: i32, y2: i32) -> f32 { var dx: i32 = x2 - self.x; var dy: i32 = y2 - self.y; return Math.Sqrt(dx * dx + dy * dy); } // Mutating method declaration fn Offset[ref self: Self](dx: i32, dy: i32); var x: i32; var y: i32; } // Out-of-line definition of method declared inline fn Point.Offset[ref self: Self](dx: i32, dy: i32) { self.x += dx; self.y += dy; } var origin: Point = {.x = 0, .y = 0}; Assert(Math.Abs(origin.Distance(3, 4) - 5.0) < 0.001); origin.Offset(3, 4); Assert(origin.Distance(3, 4) == 0.0); ``` This defines a `Point` class type with two integer data members `x` and `y` and two methods `Distance` and `Offset`: - Methods are defined as class functions with a `self` parameter inside square brackets `[`...`]` before the regular explicit parameter list in parens `(`...`)`. - Methods are called using the member syntax, `origin.Distance(`...`)` and `origin.Offset(`...`)`. - `Distance` computes and returns the distance to another point, without modifying the `Point`. This is signified using `[self: Self]` in the method declaration. - `origin.Offset(`...`)` _does_ modify the value of `origin`. This is signified using `[ref self: Self]` in the method declaration. It may only be called on [reference expressions](#expression-categories). - Methods may be declared lexically inline like `Distance`, or lexically out of line like `Offset`. > References: > > - [Methods](classes.md#methods) > - Proposal > [#722: Nominal classes and methods](https://github.com/carbon-language/carbon-lang/pull/722) #### Inheritance The philosophy of inheritance support in Carbon is to focus on use cases where inheritance is a good match, and use other features for other cases. For example, [mixins](#mixins) for implementation reuse and [generics](#generics) for [separating interface from implementation](#interfaces-and-implementations). This allows Carbon to move away from [multiple inheritance](https://en.wikipedia.org/wiki/Multiple_inheritance), which doesn't have as efficient of an implementation strategy. Classes by default are [_final_](), which means they may not be extended. A class may be declared as allowing extension using either the `base class` or `abstract class` introducer instead of `class`. An `abstract class` is a base class that may not itself be instantiated. ```carbon base class MyBaseClass { ... } ``` Either kind of base class may be _extended_ to get a _derived class_. Derived classes are final unless they are themselves declared `base` or `abstract`. Classes may only extend a single class. Carbon only supports single inheritance, and will use mixins instead of multiple inheritance. ```carbon base class MiddleDerived { extend base: MyBaseClass; ... } class FinalDerived { extend base: MiddleDerived; ... } // ❌ Forbidden: class Illegal { extend base: FinalDerived; ... } // may not extend `FinalDerived` since not declared `base` or `abstract`. ``` A base class may define [virtual methods](https://en.wikipedia.org/wiki/Virtual_function). These are methods whose implementation may be overridden in a derived class. By default methods are _non-virtual_, the declaration of a virtual method must be prefixed by one of these three keywords: - A method marked `virtual` has a definition in this class but not in any base. - A method marked `abstract` does not have a definition in this class, but must have a definition in any non-`abstract` derived class. - A method marked `impl` has a definition in this class, overriding any definition in a base class. A pointer to a derived class may be cast to a pointer to one of its base classes. Calling a virtual method through a pointer to a base class will use the overriding definition provided in the derived class. Base classes with `virtual` methods may use [run-time type information](https://en.wikipedia.org/wiki/Run-time_type_information) in a match statement to dynamically test whether the dynamic type of a value is some derived class, as in: ```carbon var base_ptr: MyBaseType* = ...; match (base_ptr) { case dyn p: MiddleDerived* => { ... } } ``` For purposes of construction, a derived class acts like its first field is called `base` with the type of its immediate base class. ```carbon class MyDerivedType { extend base: MyBaseType; fn Make() -> MyDerivedType { return {.base = MyBaseType.Make(), .derived_field = 7}; } var derived_field: i32; } ``` Abstract classes can't be instantiated, so instead they should define class functions returning `partial Self`. Those functions should be marked [`protected`](#access-control) so they may only be used by derived classes. ```carbon abstract class AbstractClass { protected fn Make() -> partial Self { return {.field_1 = 3, .field_2 = 9}; } // ... var field_1: i32; var field_2: i32; } // ❌ Error: can't instantiate abstract class var abc: AbstractClass = ...; class DerivedFromAbstract { extend base: AbstractClass; fn Make() -> Self { // AbstractClass.Make() returns a // `partial AbstractClass` that can be used as // the `.base` member when constructing a value // of a derived class. return {.base = AbstractClass.Make(), .derived_field = 42 }; } var derived_field: i32; } ``` > References: > > - [Classes: Inheritance](classes.md#inheritance) > - Proposal > [#777: Inheritance](https://github.com/carbon-language/carbon-lang/pull/777) > - Proposal > [#820: Implicit conversions](https://github.com/carbon-language/carbon-lang/pull/820) #### Access control Class members are by default publicly accessible. The `private` keyword prefix can be added to the member's declaration to restrict it to members of the class or any friends. A `private virtual` or `private abstract` method may be implemented in derived classes, even though it may not be called. Friends may be declared using a `friend` declaration inside the class naming an existing function or type. Unlike C++, `friend` declarations may only refer to names resolvable by the compiler, and don't act like forward declarations. `protected` is like `private`, but also gives access to derived classes. > References: > > - [Access control for class members](classes.md#access-control) > - Question-for-leads issue > [#665: `private` vs `public` _syntax_ strategy, as well as other visibility tools like `external`/`api`/etc.](https://github.com/carbon-language/carbon-lang/issues/665) > - Proposal > [#777: Inheritance](https://github.com/carbon-language/carbon-lang/pull/777) > - Question-for-leads issue > [#971: Private interfaces in public API files](https://github.com/carbon-language/carbon-lang/issues/971) #### Destructors A destructor for a class is custom code executed when the lifetime of a value of that type ends. They are defined with `fn destroy` followed by either `[self: Self]` or `[ref self: Self]` (as is done with [methods](#methods)) and the block of code in the class definition, as in: ```carbon class MyClass { fn destroy[self: Self]() { ... } } ``` or: ```carbon class MyClass { // Can modify `self` in the body. fn destroy[ref self: Self]() { ... } } ``` The destructor for a class is run before the destructors of its data members. The data members are destroyed in reverse order of declaration. Derived classes are destroyed before their base classes. A destructor in an abstract or base class may be declared `virtual` like with [methods](#inheritance). Destructors in classes derived from one with a virtual destructor must be declared with the `impl` keyword prefix. It is illegal to delete an instance of a derived class through a pointer to a base class unless the base class is declared `virtual` or `impl`. To delete a pointer to a non-abstract base class when it is known not to point to a value with a derived type, use `UnsafeDelete`. > References: > > - [Classes: Destructors](classes.md#destructors) > - Proposal > [#1154: Destructors](https://github.com/carbon-language/carbon-lang/pull/1154) #### `const` For every type `MyClass`, there is the type `const MyClass` such that: - The data representation is the same, so a `MyClass*` value may be implicitly converted to a `(const MyClass)*`. - A `const MyClass` [reference expression](#expression-categories) may automatically convert to a `MyClass` value expression, the same way that a `MyClass` reference expression can. - If member `x` of `MyClass` has type `T`, then member `x` of `const MyClass` has type `const T`. - While all of the member names in `MyClass` are also member names in `const MyClass`, the effective API of a `const MyClass` reference expression is a subset of `MyClass`, because only `ref` methods accepting a `const Self` will be valid. Note that `const` binds more tightly than postfix-`*` for forming a pointer type, so `const MyClass*` is equal to `(const MyClass)*`. This example uses the definition of `Point` from the ["methods" section](#methods): ```carbon var origin: Point = {.x = 0, .y = 0}; // ✅ Allowed conversion from `Point*` to // `const Point*`: let p: const Point* = &origin; // ✅ Allowed conversion of `const Point` reference expression // to `Point` value expression. let five: f32 = p->Distance(3, 4); // ❌ Error: mutating method `Offset` excluded // from `const Point` API. p->Offset(3, 4); // ❌ Error: mutating method `AssignAdd.Op` // excluded from `const i32` API. p->x += 2; ``` > References: > > - [`const`-qualified types](values.md#const-qualified-types) > - Proposal > [#2006: Values, variables, pointers, and references](https://github.com/carbon-language/carbon-lang/pull/2006) #### Unformed state Types indicate that they support unformed states by [implementing a particular interface](#interfaces-and-implementations), otherwise variables of that type must be explicitly initialized when they are declared. An unformed state for an object is one that satisfies the following properties: - Assignment from a fully formed value is correct using the normal assignment implementation for the type. - Destruction must be correct using the type's normal destruction implementation. - Destruction must be optional. The behavior of the program must be equivalent whether the destructor is run or not for an unformed object, including not leaking resources. A type might have more than one in-memory representation for the unformed state, and those representations may be the same as valid fully formed values for that type. For example, all values are legal representations of the unformed state for any type with a trivial destructor like `i32`. Types may define additional initialization for the [hardened build mode](#build-modes). For example, this causes integers to be set to `0` when in unformed state in this mode. Any operation on an unformed object _other_ than destruction or assignment from a fully formed value is an error, even if its in-memory representation is that of a valid value for that type. > References: > > - Proposal > [#257: Initialization of memory and variables](https://github.com/carbon-language/carbon-lang/pull/257) #### Move Carbon will allow types to define if and how they are moved. This can happen when returning a value from a function or by using the _move operator_ `~x`. This leaves `x` in an [unformed state](#unformed-state) and returns its old value. > **Note:** This is provisional. The move operator was discussed but not > proposed in accepted proposal > [#257: Initialization of memory and variables](https://github.com/carbon-language/carbon-lang/pull/257). #### Mixins Mixins allow reuse with different trade-offs compared to [inheritance](#inheritance). Mixins focus on implementation reuse, such as might be done using [CRTP](https://en.wikipedia.org/wiki/Curiously_recurring_template_pattern) or [multiple inheritance](https://en.wikipedia.org/wiki/Multiple_inheritance) in C++. > **TODO:** The design for mixins is still under development. The details here > are provisional. The mixin use case was included in accepted proposal > [#561: Basic classes: use cases, struct literals, struct types, and future work](https://github.com/carbon-language/carbon-lang/pull/561). ### Choice types A _choice type_ is a [tagged union](https://en.wikipedia.org/wiki/Tagged_union), that can store different types of data in a storage space that can hold the largest. A choice type has a name, and a list of cases separated by commas (`,`). Each case has a name and an optional parameter list. ```carbon choice IntResult { Success(value: i32), Failure(error: String), Cancelled } ``` The value of a choice type is one of the cases, plus the values of the parameters to that case, if any. A value can be constructed by naming the case and providing values for the parameters, if any: ```carbon fn ParseAsInt(s: String) -> IntResult { var r: i32 = 0; for (c: i32 in s) { if (not IsDigit(c)) { // Equivalent to `IntResult.Failure(...)` return .Failure("Invalid character"); } // ... } return .Success(r); } ``` Choice type values may be consumed using a [`match` statement](#match): ```carbon match (ParseAsInt(s)) { case .Success(value: i32) => { return value; } case .Failure(error: String) => { Display(error); } case .Cancelled => { Terminate(); } } ``` They can also represent an [enumerated type](https://en.wikipedia.org/wiki/Enumerated_type), if no additional data is associated with the choices, as in: ```carbon choice LikeABoolean { False, True } ``` > References: > > - [Sum types](sum_types.md) > - Proposal > [#157: Design direction for sum types](https://github.com/carbon-language/carbon-lang/pull/157) > - Proposal > [#162: Basic Syntax](https://github.com/carbon-language/carbon-lang/pull/162) ## Names Names are introduced by [declarations](#declarations-definitions-and-scopes) and are valid until the end of the scope in which they appear. Code may not refer to names earlier in the source than they are declared. In executable scopes such as function bodies, names declared later are not found. In declarative scopes such as packages, classes, and interfaces, it is an error to refer to names declared later, except that inline class member function bodies are [parsed as if they appeared after the class](#class-functions-and-factory-functions). A name in Carbon is formed from a sequence of letters, numbers, and underscores, and starts with a letter. We intend to follow [Unicode's Annex 31](https://unicode.org/reports/tr31/) in selecting valid identifier characters, but a concrete set of valid characters has not been selected yet. > References: > > - [Lexical conventions](lexical_conventions) > - [Principle: Information accumulation](/docs/project/principles/information_accumulation.md) > - Proposal > [#142: Unicode source files](https://github.com/carbon-language/carbon-lang/pull/142) > - Question-for-leads issue > [#472: Open question: Calling functions defined later in the same file](https://github.com/carbon-language/carbon-lang/issues/472) > - Proposal > [#875: Principle: information accumulation](https://github.com/carbon-language/carbon-lang/pull/875) ### Files, libraries, packages - **Files** are grouped into libraries, which are in turn grouped into packages. There are two kinds of files: - API files describe the interface of a library. - Implementation files implement part of the interface of a library. - **Libraries** are the granularity of code reuse through imports. - **Packages** are the unit of distribution. Each library must have exactly one API file. This file includes declarations for all public names of the library. Definitions for those declarations must be in some file in the library, either the API file or an implementation file. Every package has its own namespace. This means libraries within a package need to coordinate to avoid name conflicts, but not across packages. > References: > > - [Code and name organization](code_and_name_organization) > - Proposal > [#107: Code and name organization](https://github.com/carbon-language/carbon-lang/pull/107) ### Package declaration Files start with an optional package declaration, consisting of: - optionally, the `impl` modifier keyword, indicating the file is an implementation file rather than an API file, - optionally, the `package` keyword followed by an identifier specifying the package name, - optionally, the `library` keyword followed by a string with the library name, and - a terminating semicolon (`;`). For example: ```carbon // Package name is `Geometry`. // Library name is "Shapes". // This file is an API file, not an implementation file. package Geometry library "Shapes"; ``` Parts of this declaration may be omitted: - If the package keyword is not specified, as in `library "Widgets";`, the file contributes to the `Main` package. No other package may import from the `Main` package, and it cannot be named explicitly. - If the library keyword is not specified, as in `package Geometry;`, this file contributes to the default library. - If both keywords are omitted, the package declaration must be omitted entirely. In this case, the file is the API file for the default library of the `Main` package, which cannot have any implementation files. This library is used to define the entry point for the program, and tests and smaller examples may choose to reside entirely within this library. No other library can import this library even from within the default package. If the default library of the `Main` package contains a function named `Run`, that function is the program entry point. Otherwise, the program's entry point may be defined in another language, such as by defining a C++ `main` function. > **Note:** Valid signatures for the entry point have not yet been decided. > References: > > - [Code and name organization](code_and_name_organization) > - Proposal > [#107: Code and name organization](https://github.com/carbon-language/carbon-lang/pull/107) > - Proposal > [#2550: Simplified package declaration for the main package](https://github.com/carbon-language/carbon-lang/pull/2550) ### Imports After the package declaration, files may include `import` declarations. The `import` keyword is followed by the package name, `library` followed by the library name, or both. If the library is omitted, the default library for that package is imported. All `import` declarations must appear before all other non-`package` declarations in the file. #### Same-package imports The package name must be omitted when importing a library from the current package. ```carbon // Import the "Vertex" library from the package containing this file. import library "Vertex"; ``` The `import library ...` syntax adds all the public top-level names within the given library to the top-level scope of the current file as [`private`](#name-visibility) names, and similarly for names in [namespaces](#namespaces). Every implementation file automatically imports the API file for its library. Attempting to perform an import of the current library is invalid. ``` impl package MyPackage library "Widgets"; // ❌ Error, this import is performed implicitly. import MyPackage library "Widgets"; ``` The default library for a package does not have a string name, and is instead named with the `default` keyword. ```carbon // Import the default library from the same package. import library default; ``` It is an error to use the `import library default;` syntax in the `Main` package. #### Cross-package imports When the package name is specified, the `import` declaration imports a library from another package. ```carbon impl package MyPackage; // Import the "Vector" library from the `LinearAlgebra` package. import LinearAlgebra library "Vector"; // Import the default library from the `ArbitraryPrecision` package. import ArbitraryPrecision; ``` The syntax `import PackageName ...` introduces the name `PackageName` as a [`private`](#name-visibility) name naming the given package. Importing additional libraries from that package makes additional members of `PackageName` visible. It is an error to specify the name of the current package. The package name must be omitted when [importing from the same package](#same-package-imports). It is an error to specify `library default` in a package-qualified import. Instead, omit the `library` portion of the declaration. It is an error to specify the package name `Main`. Libraries in the `Main` package can only be imported from within that package. > References: > > - [Code and name organization](code_and_name_organization) > - Proposal > [#107: Code and name organization](https://github.com/carbon-language/carbon-lang/pull/107) > - Proposal > [#2550: Simplified package declaration for the main package](https://github.com/carbon-language/carbon-lang/pull/2550) ### Name visibility The names visible from an imported library are determined by these rules: - Declarations in an API file are by default _public_, which means visible to any file that imports that library. This matches class members, which are also [default public](#access-control). - A `private` prefix on a declaration in an API file makes the name _library private_. This means the name is visible in the file and all implementation files for the same library. - The visibility of a name is determined by its first declaration, considering API files before implementation files. The `private` prefix is only allowed on the first declaration. - A name declared in an implementation file and not the corresponding API file is _file private_, meaning visible in just that file. Its first declaration must be marked with a `private` prefix. **TODO:** This needs to be finalized in a proposal to resolve inconsistency between [#665](https://github.com/carbon-language/carbon-lang/issues/665#issuecomment-914661914) and [#1136](https://github.com/carbon-language/carbon-lang/issues/1136). - Private names don't conflict with names outside the region they're private to: two different libraries can have different private names `foo` without conflict, but a private name conflicts with a public name in the same scope. At most one API file in a package transitively used in a program may declare a given name public. > References: > > - [Exporting entities from an API file](code_and_name_organization/README.md#exporting-entities-from-an-api-file) > - Question-for-leads issue > [#665: `private` vs `public` _syntax_ strategy, as well as other visibility tools like `external`/`api`/etc.](https://github.com/carbon-language/carbon-lang/issues/665) > - Proposal > [#752: api file default public](https://github.com/carbon-language/carbon-lang/pull/752) > - Proposal > [#931: Generic impls access (details 4)](https://github.com/carbon-language/carbon-lang/pull/931) > - Question-for-leads issue > [#1136: what is the top-level scope in a source file, and what names are found there?](https://github.com/carbon-language/carbon-lang/issues/1136) ### Package scope The top-level scope in a file is the scope of the package. This means: - Within this scope (and its sub-namespaces), all visible names from the same package appear. This includes names from the same file, names from the API file of a library when inside an implementation file, and names from imported libraries of the same package. - In scopes where package members might have a name conflict with something else, the syntax `package.Foo` can be used to name the `Foo` member of the current package. In this example, the names `F` and `P` are used in a scope where they could mean two different things, and [qualifications are needed to disambiguate](#name-lookup): ```carbon import P; fn F(); class C { fn F(); class P { fn H(); } fn G() { // ❌ Error: ambiguous whether `F` means // `package.F` or `package.C.F`. F(); // ✅ Allowed: fully qualified package.F(); package.C.F(); // ✅ Allowed: unambiguous C.F(); // ❌ Error: ambiguous whether `P` means // `package.P` or `package.C.P`. P.H(); // ✅ Allowed package.P.H(); package.C.P.H(); C.P.H(); } } ``` > References: > > - [Code and name organization](code_and_name_organization) > - Proposal > [#107: Code and name organization](https://github.com/carbon-language/carbon-lang/pull/107) > - Proposal > [#752: api file default public](https://github.com/carbon-language/carbon-lang/pull/752) > - Question-for-leads issue > [#1136: what is the top-level scope in a source file, and what names are found there?](https://github.com/carbon-language/carbon-lang/issues/1136) ### Namespaces A `namespace` declaration defines a name that may be used as a prefix of names declared afterward. When defining a member of a namespace, other members of that namespace are considered in scope and may be found by [name lookup](#name-lookup) without the namespace prefix. In this example, package `P` defines some of its members inside a namespace `N`: ```carbon package P; // Defines namespace `N` within the current package. namespace N; // Defines namespaces `M` and `M.L`. namespace M.L; fn F(); // ✅ Allowed: Declares function `G` in namespace `N`. private fn N.G(); // ❌ Error: `Bad` hasn't been declared. fn Bad.H(); fn J() { // ❌ Error: No `package.G` G(); } fn N.K() { // ✅ Allowed: Looks in both `package` and `package.N`. // Finds `package.F` and `package.N.G`. F(); G(); } // ✅ Allowed: Declares function `R` in namespace `M.L`. fn M.L.R(); // ✅ Allowed: Declares function `Q` in namespace `M`. fn M.Q(); ``` Another package importing `P` can refer to the public members of that namespace by prefixing with the package name `P` followed by the namespace: ```carbon import P; // ✅ Allowed: `F` is public member of `P`. P.F(); // ❌ Error: `N.G` is a private member of `P`. P.N.G(); // ✅ Allowed: `N.K` is public member of `P`. P.N.K(); // ✅ Allowed: `M.L.R` is public member of `P`. P.M.L.R(); // ✅ Allowed: `M.Q` is public member of `P`. P.M.Q(); ``` > References: > > - ["Namespaces" in "Code and name organization"](code_and_name_organization/README.md#namespaces) > - ["Package and namespace members" in "Qualified names and member access"](expressions/member_access.md#package-and-namespace-members) > - Proposal > [#107: Code and name organization](https://github.com/carbon-language/carbon-lang/pull/107) > - Proposal > [#989: Member access expressions](https://github.com/carbon-language/carbon-lang/pull/989) > - Question-for-leads issue > [#1136: what is the top-level scope in a source file, and what names are found there?](https://github.com/carbon-language/carbon-lang/issues/1136) ### Naming conventions Our naming conventions are: - For idiomatic Carbon code: - `UpperCamelCase` will be used when the named entity cannot have a dynamically varying value. For example, functions, namespaces, or compile-time constant values. Note that [`virtual` methods](#inheritance) are named the same way to be consistent with other functions and methods. - `lower_snake_case` will be used when the named entity's value won't be known until runtime, such as for variables. - For Carbon-provided features: - Keywords and type literals will use `lower_snake_case`. - Other code will use the conventions for idiomatic Carbon code. > References: > > - [Naming conventions](naming_conventions.md) > - Proposal > [#861: Naming conventions](https://github.com/carbon-language/carbon-lang/pull/861) ### Aliases `alias` declares a name as equivalent to another name, for example: ```carbon alias NewName = SomePackage.OldName; ``` Note that the right-hand side of the equal sign (`=`) is a name not a value, so `alias four = 4;` is not allowed. This allows `alias` to work with entities like namespaces, which aren't values in Carbon. This can be used during an incremental migration when changing a name. For example, `alias` would allow you to have two names for a data field in a class while clients were migrated between the old name and the new name. ```carbon class MyClass { var new_name: String; alias old_name = new_name; } var x: MyClass = {.new_name = "hello"}; Carbon.Assert(x.old_name == "hello"); ``` Another use is to include a name in a public API. For example, `alias` may be used to include a name from an interface implementation as a member of a class or [named constraint](generics/details.md#named-constraints), possibly renamed: ```carbon class ContactInfo { impl as Printable; impl as ToPrinterDevice; alias PrintToScreen = Printable.Print; alias PrintToPrinter = ToPrinterDevice.Print; ... } ``` > References: > > - [Aliases](aliases.md) > - ["Aliasing" in "Code and name organization"](code_and_name_organization/README.md#aliasing) > - [`alias` a name from an interface impl](generics/details.md#avoiding-name-collisions) > - [`alias` a name in a named constraint](generics/details.md#named-constraints) > - Proposal > [#107: Code and name organization](https://github.com/carbon-language/carbon-lang/pull/107) > - Proposal > [#553: Generics details part 1](https://github.com/carbon-language/carbon-lang/pull/553) > - Question-for-leads issue > [#749: Alias syntax](https://github.com/carbon-language/carbon-lang/issues/749) > - Proposal > [#989: Member access expressions](https://github.com/carbon-language/carbon-lang/pull/989) ### Name lookup The general principle of Carbon name lookup is that we look up names in all relevant scopes, and report an error if the name is found to refer to more than one different entity. So Carbon requires disambiguation by adding qualifiers instead of doing any [shadowing](https://en.wikipedia.org/wiki/Variable_shadowing) of names. [Member name lookup](expressions/member_access.md) follows a similar philosophy. For an example, see [the "package scope" section](#package-scope). Unqualified name lookup walks the semantically-enclosing scopes, not only the lexically-enclosing ones. So when a lookup is performed within `fn MyNamespace.MyClass.MyNestedClass.MyFunction()`, we will look in `MyNestedClass`, `MyClass`, `MyNamespace`, and the package scope, even when the lexically-enclosing scope is the package scope. This means that the definition of a method will look for names in the class' scope even if it is written lexically out of line: ``` class C { fn F(); fn G(); } fn C.G() { // ✅ Allowed: resolves to `package.C.F`. F(); } ``` Carbon also rejects cases that would be invalid if all declarations in the file, including ones appearing later, were visible everywhere, not only after their point of appearance: ```carbon class C { fn F(); fn G(); } fn C.G() { F(); } // Error: use of `F` in `C.G` would be ambiguous // if this declaration was earlier. fn F(); ``` > References: > > - [Name lookup](name_lookup.md) > - ["Qualified names and member access" section of "Expressions"](expressions/README.md#qualified-names-and-member-access) > - [Qualified names and member access](expressions/member_access.md) > - [Principle: Information accumulation](/docs/project/principles/information_accumulation.md) > - Proposal > [#875: Principle: information accumulation](https://github.com/carbon-language/carbon-lang/pull/875) > - Proposal > [#989: Member access expressions](https://github.com/carbon-language/carbon-lang/pull/989) > - Question-for-leads issue > [#1136: what is the top-level scope in a source file, and what names are found there?](https://github.com/carbon-language/carbon-lang/issues/1136) #### Name lookup for common types Common types that we expect to be used universally will be provided for every file are made available as if there was a special "prelude" package that was imported automatically into every API file. Dedicated type literal syntaxes like `i32` and `bool` refer to types defined within this package, based on the ["all APIs are library APIs" principle](/docs/project/principles/library_apis_only.md). > **TODO:** Prelude provisionally imports the `Carbon` package which includes > common facilities, like `Print` and the interfaces for operator overloading. > References: > > - [Name lookup](name_lookup.md) > - [Principle: All APIs are library APIs](/docs/project/principles/library_apis_only.md) > - Question-for-leads issue > [#750: Naming conventions for Carbon-provided features](https://github.com/carbon-language/carbon-lang/issues/750) > - Question-for-leads issue > [#1058: How should interfaces for core functionality be named?](https://github.com/carbon-language/carbon-lang/issues/1058) > - Proposal > [#1280: Principle: All APIs are library APIs](https://github.com/carbon-language/carbon-lang/pull/1280) ## Generics Generics allow Carbon constructs like [functions](#functions) and [classes](#classes) to be written with compile-time parameters to generalize across different values of those parameters. For example, this `Min` function has a type\* parameter `T` that can be any type that implements the `Ordered` interface. ```carbon fn Min[T:! Ordered](x: T, y: T) -> T { // Can compare `x` and `y` since they have // type `T` known to implement `Ordered`. return if x <= y then x else y; } var a: i32 = 1; var b: i32 = 2; // `T` is deduced to be `i32` Assert(Min(a, b) == 1); // `T` is deduced to be `String` Assert(Min("abc", "xyz") == "abc"); ``` Since the `T` parameter is in the deduced parameter list in square brackets (`[`...`]`) before the explicit parameter list in parentheses (`(`...`)`), the value of `T` is determined from the types of the explicit arguments instead of being passed as a separate explicit argument. (\*) Note: `T` here may be thought of as a type parameter, but its values are actually [facets](generics/terminology.md#facet), which are [values usable as types](#values-usable-as-types). The `T` in this example is not itself a type. > References: **TODO:** Revisit > > - [Generics: Overview](generics/overview.md) > - Proposal > [#524: Generics overview](https://github.com/carbon-language/carbon-lang/pull/524) > - Proposal > [#553: Generics details part 1](https://github.com/carbon-language/carbon-lang/pull/553) > - Proposal > [#950: Generic details 6: remove facets](https://github.com/carbon-language/carbon-lang/pull/950) > - Proposal > [#2360: Types are values of type `type`](https://github.com/carbon-language/carbon-lang/pull/2360) ### Checked and template parameters The `:!` marks it as a compile-time binding pattern, and so `T` is a compile-time parameter. Compile-time parameters may either be _checked_ or _template_, and default to checked. "Checked" here means that the body of `Min` is type checked when the function is defined, independent of the specific values `T` is instantiated with, and name lookup is delegated to the constraint on `T` (`Ordered` in this case). This type checking is equivalent to saying the function would pass type checking given any type `T` that implements the `Ordered` interface. Subsequent calls to `Min` only need to check that the deduced value of `T` implements `Ordered`. The parameter could alternatively be declared to be a _template_ generic parameter by prefixing with the `template` keyword, as in `template T:! type`. ```carbon fn Convert[template T:! type](source: T, template U:! type) -> U { var converted: U = source; return converted; } fn Foo(i: i32) -> f32 { // Instantiates with the `T` implicit argument set to `i32` and the `U` // explicit argument set to `f32`, then calls with the runtime value `i`. return Convert(i, f32); } ``` A template parameter can still use a constraint. The `Min` example could have been declared as: ```carbon fn TemplatedMin[template T:! Ordered](x: T, y: T) -> T { return if x <= y then x else y; } ``` Carbon templates follow the same fundamental paradigm as [C++ templates](): they are instantiated when called, resulting in late type checking, duck typing, and lazy binding. One difference from C++ templates, Carbon template instantiation is not controlled by the SFINAE rule of C++ ([1](https://en.wikipedia.org/wiki/Substitution_failure_is_not_an_error), [2](https://en.cppreference.com/w/cpp/language/sfinae)) but by explicit constraints declared in the function signature and evaluated at compile-time. > **TODO:** The design for template constraints is still under development. The [expression phase](#expression-phases) of a checked parameter is a symbolic constant whereas the expression phase of a template parameter is template constant. A binding pattern using `:!` is a _compile-time binding pattern_; more specifically a _template binding pattern_ if it uses `template`, and a _symbolic binding pattern_ if it does not. Although checked generics are generally preferred, templates enable translation of code between C++ and Carbon, and address some cases where the type checking rigor of checked generics is problematic. > References: > > - [Templates](templates.md) > - Proposal > [#553: Generics details part 1](https://github.com/carbon-language/carbon-lang/pull/553) > - Question-for-leads issue > [#949: Constrained template name lookup](https://github.com/carbon-language/carbon-lang/issues/949) > - Proposal > [#2138: Checked and template generic terminology](https://github.com/carbon-language/carbon-lang/pull/2138) > - Proposal > [#2200: Template generics](https://github.com/carbon-language/carbon-lang/pull/2200) ### Interfaces and implementations _Interfaces_ specify a set of requirements that a type might satisfy. Interfaces act both as constraints on types a caller might supply and capabilities that may be assumed of types that satisfy that constraint. ```carbon interface Printable { // Inside an interface definition `Self` means // "the type implementing this interface". fn Print[self: Self](); } ``` An interface is kind of [facet type](generics/terminology.md#facet-type), and the values of this type are [facets](generics/terminology.md#facet), which are [values usable as types](#values-usable-as-types). In addition to function requirements, interfaces can contain: - [requirements that other interfaces be implemented](generics/details.md#interface-requiring-other-interfaces) or [interfaces that this interface extends](generics/details.md#interface-extension) - [associated facets](generics/details.md#associated-facets) and other [associated constants](generics/details.md#associated-constants) - [interface defaults](generics/details.md#interface-defaults) - [`final` interface members](generics/details.md#final-members) Types only implement an interface if there is an explicit `impl` declaration that they do. Simply having a `Print` function with the right signature is not sufficient. ```carbon // Class `Text` does not implement the `Printable` interface. class Text { fn Print[self: Self](); } class Circle { var radius: f32; // This `impl` declaration establishes that `Circle` implements // `Printable`. impl as Printable { fn Print[self: Self]() { Core.Print("Circle with radius: {0}", self.radius); } } } ``` In this case, `Print` is not a direct member of `Circle`, but: - `Circle` may be passed to functions expecting a type that implements `Printable`. ```carbon fn GenericPrint[T:! Printable](x: T) { // Look up into `T` delegates to `Printable`, so this // finds `Printable.Print`: x.Print(); } ``` - The members of `Printable` such as `Print` may be called using compound member access syntax ([1](expressions/member_access.md), [2](generics/details.md#qualified-member-names-and-compound-member-access)) to qualify the name of the member, as in: ```carbon fn CirclePrint(c: Circle) { // Succeeds, even though `c.Print()` would not. c.(Printable.Print)(); } ``` To include the members of the interface as direct members of the type, use the [`extend`](generics/details.md#extend-impl) keyword, as in `extend impl as Printable`. This is only permitted on `impl` declarations in the body of a class definition. Without `extend`, implementations don't have to be in the same library as the type definition, subject to the [orphan rule](generics/details.md#orphan-rule) for [coherence](generics/terminology.md#coherence). Interfaces and implementations may be [forward declared](generics/details.md#forward-declarations-and-cyclic-references) by replacing the definition scope in curly braces (`{`...`}`) with a semicolon. > References: > > - [Generics: Interfaces](generics/details.md#interfaces) > - [Generics: Implementing interfaces](generics/details.md#implementing-interfaces) > - Proposal > [#553: Generics details part 1](https://github.com/carbon-language/carbon-lang/pull/553) > - Proposal > [#731: Generics details 2: adapters, associated types, parameterized interfaces](https://github.com/carbon-language/carbon-lang/pull/731) > - Proposal > [#624: Coherence: terminology, rationale, alternatives considered](https://github.com/carbon-language/carbon-lang/pull/624) > - Proposal > [#989: Member access expressions](https://github.com/carbon-language/carbon-lang/pull/989) > - Proposal > [#990: Generics details 8: interface default and final members](https://github.com/carbon-language/carbon-lang/pull/990) > - Proposal > [#1084: Generics details 9: forward declarations](https://github.com/carbon-language/carbon-lang/pull/1084) > - Question-for-leads issue > [#1132: How do we match forward declarations with their definitions?](https://github.com/carbon-language/carbon-lang/issues/1132) > - Proposal > [#2360: Types are values of type `type`](https://github.com/carbon-language/carbon-lang/pull/2360) > - Proposal > [#2760: Consistent `class` and `interface` syntax](https://github.com/carbon-language/carbon-lang/pull/2760) ### Combining constraints A function can require type arguments to implement multiple interfaces (or other facet types) by combining them using an ampersand (`&`): ```carbon fn PrintMin[T:! Ordered & Printable](x: T, y: T) { // Can compare since type `T` implements `Ordered`. if (x <= y) { // Can call `Print` since type `T` implements `Printable`. x.Print(); } else { y.Print(); } } ``` The body of the function may call functions that are in either interface, except for names that are members of both. In that case, use the compound member access syntax ([1](expressions/member_access.md), [2](generics/details.md#qualified-member-names-and-compound-member-access)) to qualify the name of the member, as in: ```carbon fn DrawTies[T:! Renderable & GameResult](x: T) { if (x.(GameResult.Draw)()) { x.(Renderable.Draw)(); } } ``` > References: > > - [Combining interfaces by anding facet types](generics/details.md#combining-interfaces-by-anding-facet-types) > - Question-for-leads issue > [#531: Combine interfaces with `+` or `&`](https://github.com/carbon-language/carbon-lang/issues/531) > - Proposal > [#553: Generics details part 1](https://github.com/carbon-language/carbon-lang/pull/553) ### Template name lookup Member lookup into a template parameter is done in the actual value provided by the caller, _in addition_ to any constraints. This means member name lookup and type checking for anything [dependent](generics/terminology.md#dependent-names) on the template parameter can't be completed until the template is instantiated with a specific concrete type. When the constraint is just `type`, this gives semantics similar to C++ templates. ```carbon class Game { fn Draw[self: Self]() -> bool; impl as Renderable { fn Draw[self: Self](); } } fn TemplateDraw[template T:! type](x: T) { // Calls `Game.Draw` when `T` is `Game`: x.Draw(); } fn ConstrainedTemplateDraw[template T:! Renderable](x: T) { // ❌ Error when `T` is `Game`: Finds both `T.Draw` and // `Renderable.Draw`, and they are different. x.Draw(); } fn CheckedGenericDraw[T:! Renderable](x: T) { // Always calls `Renderable.Draw`, even when `T` is `Game`: x.Draw(); } ``` This allows a safe transition from template to checked generics. Constraints can be added incrementally, with the compiler verifying that the semantics stay the same. If adding the constraint would change which function gets called, an error is triggered, as in `ConstrainedTemplateDraw` from the example. Once all constraints have been added, it is safe to remove the word `template` to switch to a checked parameter. > References: > > - Proposal > [#989: Member access expressions](https://github.com/carbon-language/carbon-lang/pull/989) > - Proposal > [#2200: Template generics](https://github.com/carbon-language/carbon-lang/pull/2200) ### Associated constants An associated constant is a member of an interface whose value is determined by the implementation of that interface for a specific type. These values are set to compile-time values in implementations, and so use the [`:!` compile-time binding pattern syntax](#checked-and-template-parameters) inside a [`let` declaration](#constant-let-declarations) without an initializer. This allows types in the signatures of functions in the interface to vary. For example, an interface describing a [stack]() might use an associated constant to represent the type of elements stored in the stack. ``` interface StackInterface { let ElementType:! Movable; fn Push[ref self: Self](value: ElementType); fn Pop[ref self: Self]() -> ElementType; fn IsEmpty[self: Self]() -> bool; } ``` Then different types implementing `StackInterface` can specify different values for the `ElementType` member of the interface using a `where` clause: ```carbon class IntStack { extend impl as StackInterface where .ElementType = i32 { fn Push[ref self: Self](value: i32); // ... } } class FruitStack { extend impl as StackInterface where .ElementType = Fruit { fn Push[ref self: Self](value: Fruit); // ... } } ``` > References: > > - [Generics: Associated constants](generics/details.md#associated-constants) > - Proposal > [#731: Generics details 2: adapters, associated types, parameterized interfaces](https://github.com/carbon-language/carbon-lang/pull/731) > - Proposal > [#1013: Generics: Set associated constants using `where` constraints](https://github.com/carbon-language/carbon-lang/pull/1013) ### Generic entities Many Carbon entities, not just functions, may be made generic by adding [checked or template parameters](#checked-and-template-parameters). #### Generic Classes Classes may be defined with an optional explicit parameter list. All parameters to a class must be compile-time, and so defined with `:!`, either with or without the `template` prefix. For example, to define a stack that can hold values of any type `T`: ```carbon class Stack(T:! type) { fn Push[ref self: Self](value: T); fn Pop[ref self: Self]() -> T; var storage: Array(T); } var int_stack: Stack(i32); ``` In this example: - `Stack` is a type parameterized by a type `T`. - `T` may be used within the definition of `Stack` anywhere a normal type would be used. - `Array(T)` instantiates generic type `Array` with its argument set to `T`. - `Stack(i32)` instantiates `Stack` with `T` set to `i32`. The values of type parameters are part of a type's value, and so may be deduced in a function call, as in this example: ```carbon fn PeekTopOfStack[T:! type](s: Stack(T)*) -> T { var top: T = s->Pop(); s->Push(top); return top; } // `int_stack` has type `Stack(i32)`, so `T` is deduced to be `i32`: PeekTopOfStack(&int_stack); ``` > References: > > - [Generic or parameterized types](generics/details.md#parameterized-types) > - Proposal > [#1146: Generic details 12: parameterized types](https://github.com/carbon-language/carbon-lang/pull/1146) #### Generic choice types [Choice types](#choice-types) may be parameterized similarly to classes: ```carbon choice Result(T:! type, Error:! type) { Success(value: T), Failure(error: Error) } ``` #### Generic interfaces Interfaces are always parameterized by a `Self` type, but in some cases they will have additional parameters. ```carbon interface AddWith(U:! type); ``` Interfaces without parameters may only be implemented once for a given type, but a type can have distinct implementations of `AddWith(i32)` and `AddWith(BigInt)`. Parameters to an interface _determine_ which implementation is selected for a type, in contrast to [associated constants](#associated-constants) which are _determined by_ the implementation of an interface for a type. > References: > > - [Generic or parameterized interfaces](generics/details.md#parameterized-interfaces) > - Proposal > [#731: Generics details 2: adapters, associated types, parameterized interfaces](https://github.com/carbon-language/carbon-lang/pull/731) #### Generic implementations An `impl` declaration may be parameterized by adding `forall [`_compile-time parameter list_`]` after the `impl` keyword introducer, as in: ```carbon impl forall [T:! Printable] Vector(T) as Printable; impl forall [Key:! Hashable, Value:! type] HashMap(Key, Value) as Has(Key); impl forall [T:! Ordered] T as PartiallyOrdered; impl forall [T:! ImplicitAs(i32)] BigInt as AddWith(T); impl forall [U:! type, T:! As(U)] Optional(T) as As(Optional(U)); ``` Generic implementations can create a situation where multiple `impl` definitions apply to a given type and interface query. The [specialization](generics/details.md#lookup-resolution-and-specialization) rules pick which definition is selected. These rules ensure: - Implementations have [coherence](generics/terminology.md#coherence), so the same implementation is always selected for a given query. - Libraries will work together as long as they pass their separate checks. - A generic function can assume that some impl will be successfully selected if it can see an impl that applies, even though another more-specific impl may be selected. Implementations may be marked [`final`](generics/details.md#final-impl-declarations) to indicate that they may not be specialized, subject to [some restrictions](generics/details.md#libraries-that-can-contain-a-final-impl). > References: > > - [Generic or parameterized impl declarationss](generics/details.md#parameterized-impl-declarations) > - Proposal > [#624: Coherence: terminology, rationale, alternatives considered](https://github.com/carbon-language/carbon-lang/pull/624) > - Proposal > [#920: Generic parameterized impls (details 5)](https://github.com/carbon-language/carbon-lang/pull/920) > - Proposal > [#983: Generics details 7: final impls](https://github.com/carbon-language/carbon-lang/pull/983) > - Question-for-leads issue > [1192: Parameterized impl syntax](https://github.com/carbon-language/carbon-lang/issues/1192) > - Proposal > [#1327: Generics: `impl forall`](https://github.com/carbon-language/carbon-lang/pull/1327) ### Other features Carbon generics have a number of other features, including: - [Named constraints](generics/details.md#named-constraints) may be used to disambiguate when combining two interfaces that have name conflicts. Named constraints define facet types, and may be implemented and otherwise used in place of an interface. - [Template constraints](generics/details.md#named-constraints) are a kind of named constraint that can contain structural requirements. For example, a template constraint could match any type that has a function with a specific name and signature without any explicit declaration that the type implements the constraint. Template constraints may only be used as requirements for template parameters. - An [adapter type](generics/details.md#adapting-types) is a type with the same data representation as an existing type, so you may cast between the two types, but can implement different interfaces or implement interfaces differently. - Additional requirements can be placed on the associated facets of an interface using [`where` constraints](generics/details.md#where-constraints). - [Implied constraints](generics/details.md#implied-constraints) allow some constraints to be deduced and omitted from a function signature. - _Planned_ [dynamic erased types](generics/details.md#runtime-type-fields) can hold any value with a type implementing an interface, and allow the functions in that interface to be called using [dynamic dispatch](https://en.wikipedia.org/wiki/Dynamic_dispatch), for some interfaces marked "`dyn`-safe". **Note:** Provisional. - _Planned_ [variadics](generics/details.md#variadic-arguments) supports variable-length parameter lists. **Note:** Provisional. > References: > > - [Generics details](generics/details.md) > - Proposal > [#553: Generics details part 1](https://github.com/carbon-language/carbon-lang/pull/553) > - Proposal > [#731: Generics details 2: adapters, associated types, parameterized interfaces](https://github.com/carbon-language/carbon-lang/pull/731) > - Proposal > [#818: Constraints for generics (generics details 3)](https://github.com/carbon-language/carbon-lang/pull/818) ### Generic type equality and `observe` declarations Determining whether two types must be equal in a checked-generic context is in general undecidable, as [has been shown in Swift](https://forums.swift.org/t/swift-type-checking-is-undecidable/39024). To make compilation fast, the Carbon compiler will limit its search to a depth of 1, only identifying types as equal if there is an explicit declaration that they are equal in the code, such as in a [`where` constraint](generics/details.md#where-constraints). There will be situations where two types must be equal as the result of combining these facts, but the compiler will return a type error since it did not realize they are equal due to the limit of the search. An [`observe`...`==` declaration](generics/details.md#observe-declarations) may be added to describe how two types are equal, allowing more code to pass type checking. An `observe` declaration showing types are equal can increase the set of interfaces the compiler knows that a type implements. It is also possible that knowing a type implements one interface implies that it implements another, from an [interface requirement](generics/details.md#interface-requiring-other-interfaces) or [generic implementation](#generic-implementations). An `observe`...`impls` declaration may be used to [observe that a type implements an interface](generics/details.md#observing-a-type-implements-an-interface). > References: > > - [Generics: `observe` declarations](generics/details.md#observe-declarations) > - [Generics: Observing a type implements an interface](generics/details.md#observing-a-type-implements-an-interface) > - Proposal > [#818: Constraints for generics (generics details 3)](https://github.com/carbon-language/carbon-lang/pull/818) > - Proposal > [#1088: Generic details 10: interface-implemented requirements](https://github.com/carbon-language/carbon-lang/pull/1088) ### Operator overloading Uses of an operator in an [expression](#expressions) is translated into a call to a method of an interface. For example, if `x` has type `T` and `y` has type `U`, then `x + y` is translated into a call to `x.(AddWith(U).Op)(y)`. So overloading of the `+` operator is accomplished by implementing interface `AddWith(U)` for type `T`. In order to support [implicit conversion](expressions/implicit_conversions.md) of the first operand to type `T` and the second argument to type `U`, add the `like` keyword to both types in the `impl` declaration, as in: ```carbon impl like T as AddWith(like U) where .Result = V { // `Self` is `T` here fn Op[self: Self](other: U) -> V { ... } } ``` When the operand types and result type are all the same, this is equivalent to implementing the `Add` interface: ```carbon impl T as Add { fn Op[self: Self](other: Self) -> Self { ... } } ``` The interfaces that correspond to each operator are given by: - [Arithmetic](expressions/arithmetic.md#extensibility): - `-x`: `Negate` - `x + y`: `Add` or `AddWith(U)` - `x - y`: `Sub` or `SubWith(U)` - `x * y`: `Mul` or `MulWith(U)` - `x / y`: `Div` or `DivWith(U)` - `x % y`: `Mod` or `ModWith(U)` - [Bitwise and shift operators](expressions/bitwise.md#extensibility): - `^x`: `BitComplement` - `x & y`: `BitAnd` or `BitAndWith(U)` - `x | y`: `BitOr` or `BitOrWith(U)` - `x ^ y`: `BitXor` or `BitXorWith(U)` - `x << y`: `LeftShift` or `LeftShiftWith(U)` - `x >> y`: `RightShift` or `RightShiftWith(U)` - Comparison: - `x == y`, `x != y` overloaded by implementing [`Eq` or `EqWith(U)`](expressions/comparison_operators.md#equality) - `x < y`, `x > y`, `x <= y`, `x >= y` overloaded by implementing [`Ordered` or `OrderedWith(U)`](expressions/comparison_operators.md#ordering) - Conversion: - `x as U` is rewritten to use the [`As(U)`](expressions/as_expressions.md#extensibility) interface - Implicit conversions use [`ImplicitAs(U)`](expressions/implicit_conversions.md#extensibility) - Indexing: - `x[y]` is rewritten to use the [`IndexWith` or `IndirectIndexWith`](expressions/indexing.md) interface. - **TODO:** Dereference: `*p` - **TODO:** [Move](#move): `~x` - **TODO:** Function call: `f(4)` The [logical operators can not be overloaded](expressions/logical_operators.md#overloading). Operators that result in [reference expressions](#expression-categories), such as dereferencing `*p` and indexing `a[3]`, have interfaces that return the address of the value. Carbon automatically dereferences the pointer to form the reference expression. Operators that can take multiple arguments, such as function calling operator `f(4)`, have a [variadic](generics/details.md#variadic-arguments) parameter list. **TODO: Variadics are still provisional.** Whether and how a value supports other operations, such as being copied, swapped, or set into an [unformed state](#unformed-state), is also determined by implementing corresponding interfaces for the value's type. > References: > > - [Operator overloading](generics/details.md#operator-overloading) > - Proposal > [#702: Comparison operators](https://github.com/carbon-language/carbon-lang/pull/702) > - Proposal > [#820: Implicit conversions](https://github.com/carbon-language/carbon-lang/pull/820) > - Proposal > [#845: as expressions](https://github.com/carbon-language/carbon-lang/pull/845) > - Question-for-leads issue > [#1058: How should interfaces for core functionality be named?](https://github.com/carbon-language/carbon-lang/issues/1058) > - Proposal > [#1083: Arithmetic expressions](https://github.com/carbon-language/carbon-lang/pull/1083) > - Proposal > [#1191: Bitwise operators](https://github.com/carbon-language/carbon-lang/pull/1191) > - Proposal > [#1178: Rework operator interfaces](https://github.com/carbon-language/carbon-lang/pull/1178) #### Common type There are some situations where the common type for two types is needed: - A [conditional expression like `if c then t else f`](expressions/if.md) returns a value with the common type of `t` and `f`. - If there are multiple parameters to a function with a type parameter, it will be set to the common type of the corresponding arguments, as in: ```carbon fn F[T:! type](x: T, y: T); // Calls `F` with `T` set to the // common type of `G()` and `H()`: F(G(), H()); ``` - The inferred return type of a function with [`auto` return type](#auto-return-type) is the common type of its `return` statements. The common type is specified by implementing the `CommonTypeWith` interface: ```carbon // Common type of `A` and `B` is `C`. impl A as CommonTypeWith(B) where .Result = C { } ``` The common type is required to be a type that both types have an [implicit conversion](expressions/implicit_conversions.md) to. > References: > > - [`if` expressions](expressions/if.md#finding-a-common-type) > - Proposal > [#911: Conditional expressions](https://github.com/carbon-language/carbon-lang/pull/911) > - Question-for-leads issue > [#1077: find a way to permit impls of CommonTypeWith where the LHS and RHS type overlap](https://github.com/carbon-language/carbon-lang/issues/1077) ## Bidirectional interoperability with C and C++ Interoperability, or _interop_, is the ability to call C and C++ code from Carbon code and the other way around. This ability achieves two goals: - Allows sharing a code and library ecosystem with C and C++. - Allows incremental migration to Carbon from C and C++. Carbon's approach to interop is most similar to [Java/Kotlin interop](interoperability/philosophy_and_goals.md#other-interoperability-layers), where the two languages are different, but share enough of runtime model that data from one side can be used from the other. For example, C++ and Carbon will use the same [memory model](https://en.cppreference.com/w/cpp/language/memory_model). The design for interoperability between Carbon and C++ hinges on: 1. The ability to interoperate with a wide variety of code, such as classes/structs and [templates](), not just free functions. 2. A willingness to expose the idioms of C++ into Carbon code, and the other way around, when necessary to maximize performance of the interoperability layer. 3. The use of wrappers and generic programming, including templates, to minimize or eliminate runtime overhead. This feature will have some restrictions; only a subset of Carbon APIs will be available to C++ and a subset of C++ APIs will be available to Carbon. - To achieve simplification in Carbon, its programming model will exclude some rarely used and complex features of C++. For example, there will be limitations on [multiple inheritance](https://en.wikipedia.org/wiki/Multiple_inheritance). - C or C++ features that compromise the performance of code that don't use that feature, like [RTTI](https://en.wikipedia.org/wiki/Run-time_type_information) and [exceptions](https://en.wikipedia.org/wiki/Exception_handling), are in particular subject to revision in Carbon. > References: > > - [Bidirectional interoperability with C and C++](interoperability/README.md) > - Proposal > [#175: C++ interoperability goals](https://github.com/carbon-language/carbon-lang/pull/175) ### Goals The [goals for interop](interoperability/philosophy_and_goals.md#goals) include: - [Support mixing Carbon and C++ toolchains](interoperability/philosophy_and_goals.md#support-mixing-carbon-and-c-toolchains) - [Compatibility with the C++ memory model](interoperability/philosophy_and_goals.md#compatibility-with-the-c-memory-model) - [Minimize bridge code](interoperability/philosophy_and_goals.md#minimize-bridge-code) - [Unsurprising mappings between C++ and Carbon types](interoperability/philosophy_and_goals.md#unsurprising-mappings-between-c-and-carbon-types) - [Allow C++ bridge code in Carbon files](interoperability/philosophy_and_goals.md#allow-c-bridge-code-in-carbon-files) - [Carbon inheritance from C++ types](interoperability/philosophy_and_goals.md#carbon-inheritance-from-c-types) - [Support use of advanced C++ features](interoperability/philosophy_and_goals.md#support-use-of-advanced-c-features) - [Support basic C interoperability](interoperability/philosophy_and_goals.md#support-basic-c-interoperability) > References: > > - [Interoperability: Goals](interoperability/philosophy_and_goals.md#goals) ### Non-goals The [non-goals for interop](interoperability/philosophy_and_goals.md#non-goals) include: - [Full parity between a Carbon-only toolchain and mixing C++/Carbon toolchains](interoperability/philosophy_and_goals.md#full-parity-between-a-carbon-only-toolchain-and-mixing-ccarbon-toolchains) - [Never require bridge code](interoperability/philosophy_and_goals.md#never-require-bridge-code) - [Convert all C++ types to Carbon types](interoperability/philosophy_and_goals.md#convert-all-c-types-to-carbon-types) - [Support for C++ exceptions without bridge code](interoperability/philosophy_and_goals.md#support-for-c-exceptions-without-bridge-code) - [Cross-language metaprogramming](interoperability/philosophy_and_goals.md#cross-language-metaprogramming) - [Offer equivalent support for languages other than C++](interoperability/philosophy_and_goals.md#offer-equivalent-support-for-languages-other-than-c) > References: > > - [Interoperability: Non-goals](interoperability/philosophy_and_goals.md#non-goals) ### Importing and `#include` > **Note:** This is provisional, no design for importing C++ has been through > the proposal process yet. A C++ library header file may be [imported](#imports) into Carbon using an `import` declaration of the special `Cpp` package. ```carbon // like `#include "circle.h"` in C++ import Cpp library "circle.h"; ``` This adds the names from `circle.h` into the `Cpp` namespace. If `circle.h` defines some names in a `namespace shapes { ... }` scope, those will be found in Carbon's `Cpp.shapes` namespace. In the other direction, Carbon packages can export a header file to be `#include`d from C++ files. ```c++ // like `import Geometry` in Carbon #include "geometry.carbon.h" ``` Generally Carbon entities will be usable from C++ and C++ entities will be usable from Carbon. This includes types, function, and constants. Some entities, such as Carbon interfaces, won't be able to be translated directly. C and C++ macros that are defining constants will be imported as constants. Otherwise, C and C++ macros will be unavailable in Carbon. C and C++ `typedef`s would be translated into type constants, as if declared using a [`let`](#constant-let-declarations). Carbon functions and types that satisfy some restrictions may be annotated as exported to C as well, like C++'s [`extern "C"`](https://en.wikipedia.org/wiki/Compatibility_of_C_and_C%2B%2B#Linking_C_and_C++_code) marker. ### ABI and dynamic linking > **Note:** This reflects goals and plans. No specific design for the > implementation has been through the proposal process yet. Carbon itself will not have a stable ABI for the language as a whole, and most language features will be designed around not having any ABI stability. Instead, we expect to add dedicated language features that are specifically designed to provide an ABI-stable boundary between two separate parts of a Carbon program. These ABI-resilient language features and API boundaries will be opt-in and explicit. They may also have functionality restrictions to make them easy to implement with strong ABI resilience. When interoperating with already compiled C++ object code or shared libraries, the C++ interop may be significantly less feature rich than otherwise. This is an open area for us to explore, but we expect to require re-compiling C++ code in order to get the full ergonomic and performance benefits when interoperating with Carbon. For example, recompilation lets us ensure Carbon and C++ can use the same representation for key vocabulary types. However, we expect to have full support for the C ABI when interoperating with already-compiled C object code or shared libraries. We expect Carbon's bridge code functionality to cover similar use cases as C++'s [`extern "C"`](https://en.wikipedia.org/wiki/Compatibility_of_C_and_C%2B%2B#Linking_C_and_C++_code) marker in order to provide full bi-directional support here. The functionality available across this interop boundary will of course be restricted to what is expressible in the C ABI, and types may need explicit markers to have guaranteed ABI compatibility. > References: > > - [Goals: Stable language and library ABI non-goal](https://github.com/carbon-language/carbon-lang/blob/trunk/docs/project/goals.md#stable-language-and-library-abi) > - [#175: C++ interoperability goals: Support mixing Carbon and C++ toolchains](/proposals/p0175.md#support-mixing-carbon-and-c-toolchains) ### Operator overloading > **Note:** This is provisional, no design for this has been through the > proposal process yet. [Operator overloading](#operator-overloading) is supported in Carbon, but is done by [implementing an interface](#interfaces-and-implementations) instead of defining a method or nonmember function as in C++. Carbon types implementing an operator overload using an interface should get the corresponding operator overload in C++. So implementing `ModWith(U)` in Carbon for a type effectively implements `operator%` in C++ for that type. This also works in the other direction, so C++ types implementing an operator overload are automatically considered to implement the corresponding Carbon interface. So implementing `operator%` in C++ for a type also implements interface `ModWith(U)` in Carbon. However, there may be edge cases around implicit conversions or overload selection that don't map completely into Carbon. In some cases, the operation might be written differently in the two languages. In those cases, they are matched according to which operation has the most similar semantics rather than using the same symbols. For example, the `^x` operation and `BitComplement` interface in Carbon corresponds to the `~x` operation and `operator~` function in C++. Similarly, the `ImplicitAs(U)` Carbon interface corresponds to implicit conversions in C++, which can be written in multiple different ways. Other [C++ customization points](http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/n4381.html) like `swap` will correspond to a Carbon interface, on a case-by-case basis. Some operators will only exist or be overridable in C++, such as logical operators or the comma operator. In the unlikely situation where those operators need to be overridden for a Carbon type, that can be done with a nonmember C++ function. Carbon interfaces with no C++ equivalent, such as [`CommonTypeWith(U)`](#common-type), may be implemented for C++ types out-of-line in Carbon code. To satisfy the [orphan rule](generics/details.md#orphan-rule), each C++ library will have a corresponding Carbon wrapper library that must be imported instead of the C++ library if the Carbon wrapper exists. **TODO:** Perhaps it will automatically be imported, so a wrapper may be added without requiring changes to importers? ### Templates Carbon supports both [checked and template generics](#checked-and-template-parameters). This provides a migration path for C++ template code: - C++ template -> Carbon template: This involves migrating the code from C++ to Carbon. If that migration is faithful, the change should be transparent to callers. - -> Carbon template with constraints: Constraints may be added one at a time. Adding a constraint never changes the meaning of the code as long as it continues to compile. Compile errors will point to types for which an implementation of missing interfaces is needed. A temporary template implementation of that interface can act as a bridge during the transition. - -> Carbon checked generic: Once all callers work after all constraints have been added, the template parameter may be switched to a checked generic. Carbon will also provide direct interop with C++ templates in many ways: - Ability to call C++ templates and use C++ templated types from Carbon. - Ability to instantiate a C++ template with a Carbon type. - Ability to instantiate a Carbon generic with a C++ type. We expect the best interop in these areas to be based on a Carbon-provided C++ toolchain. However, even when using Carbon's generated C++ headers for interop, we will include the ability where possible to use a Carbon generic from C++ as if it were a C++ template. > References: > > - Proposal > [#2200: Template generics](https://github.com/carbon-language/carbon-lang/pull/2200) ### Standard types > **Note:** This is provisional, no design for this has been through the > proposal process yet. The Carbon integer types, like `i32` and `u64`, are considered equal to the corresponding fixed-width integer types in C++, like `int32_t` and `uint64_t`, provided by `` or ``. The basic C and C++ integer types like `int`, `char`, and `unsigned long` are available in Carbon inside the `Cpp` namespace given an `import Cpp;` declaration, with names like `Cpp.int`, `Cpp.char`, and `Cpp.unsigned_long`. C++ types are considered different if C++ considers them different, so C++ overloads are resolved the same way. Carbon [conventions for implicit conversions between integer types](expressions/implicit_conversions.md#data-types) apply here, allowing them whenever the numerical value for all inputs may be preserved by the conversion. Other C and C++ types are equal to Carbon types as follows: | C or C++ | Carbon | | -------- | -------------- | | `bool` | `bool` | | `float` | `f32` | | `double` | `f64` | | `T*` | `Optional(T*)` | | `T[4]` | `[T; 4]` | Further, C++ reference types like `T&` will be translated to `T*` in Carbon, which is Carbon's non-null pointer type. Carbon will work to have idiomatic vocabulary _view_ types for common data structures, like `std::string_view` and `std::span`, map transparently between C++ and the Carbon equivalents. This will include data layout so that even pointers to these types translate seamlessly, contingent on a suitable C++ ABI for those types, potentially by re-compiling the C++ code with a customized ABI. We will also explore how to expand coverage to similar view types in other libraries. However, Carbon's containers will be distinct from the C++ standard library containers in order to maximize our ability to improve performance and leverage language features like checked generics in their design and implementation. Where possible, we will also try to provide implementations of Carbon's standard library container _interfaces_ for the relevant C++ container types so that they can be directly used with checked-generic Carbon code. This should allow checked-generic code in Carbon to work seamlessly with both Carbon and C++ containers without performance loss or constraining the Carbon container implementations. In the other direction, Carbon containers will satisfy C++ container requirements, so templated C++ code can operate directly on Carbon containers as well. ### Inheritance [Carbon has single inheritance](#inheritance) allowing C++ classes using inheritance to be migrated. The data representation will be consistent so that Carbon classes may inherit from C++ classes, and the other way around, even with virtual methods. C++ [multiple inheritance](https://en.wikipedia.org/wiki/Multiple_inheritance) and [CRTP](https://en.wikipedia.org/wiki/Curiously_recurring_template_pattern) will be migrated using a combination of Carbon features. Carbon mixins support implementation reuse and Carbon interfaces allow a type to implement multiple APIs. However, there may be limits on the degree of interop available with multiple inheritance across the C++ <-> Carbon boundaries. Carbon dyn-safe interfaces may be exported to C++ as an [abstract base class](). The reverse operation is also possible using a proxy object implementing a C++ abstract base class and holding a pointer to a type implementing the corresponding interface. > References: > > - Proposal > [#561: Basic classes: use cases, struct literals, struct types, and future work](https://github.com/carbon-language/carbon-lang/pull/561) > - Proposal > [#777: Inheritance](https://github.com/carbon-language/carbon-lang/pull/777) ### Enums > **TODO** ## Unfinished tales > **Note:** Everything in this section is provisional and forward looking. ### Safety Carbon's premise is that C++ users can't give up performance to get safety. Even if some isolated users can make that tradeoff, they share code with performance-sensitive users. Any path to safety must preserve performance of C++ today. This rules out garbage collection, and many other options. The only well understood mechanism of achieving safety without giving up performance is compile-time safety. The leading example of how to achieve this is Rust. The difference between Rust's approach and Carbon's is that Rust starts with safety and Carbons starts with migration. Rust supports interop with C, and there is ongoing work to improve the C++-interop story and develop migration tools. However, there is a large gap in programming models between the two languages, generally requiring a revision to the architecture. So, thus far the common pattern in the Rust community is to "rewrite it in Rust" ([1](https://deprogrammaticaipsum.com/the-great-rewriting-in-rust/), [2](https://web.archive.org/web/20230923033736/https://unhandledexpression.com/rust/2017/07/10/why-you-should-actually-rewrite-it-in-rust.html), [3](https://transitiontech.ca/random/RIIR)). Carbon's approach is to focus on migration from C++, including seamless interop, and then incrementally improve safety. The first impact on Carbon's design to support its safety strategy are the necessary building blocks for this level of compile-time safety. We look at existing languages like Rust and Swift to understand what fundamental capabilities they ended up needing. The two components that stand out are: - Expanded type system that includes more semantic information. - More pervasive use of type system abstractions (typically checked generics). For migrating C++ code, we also need the ability to add features and migrate code to use those new features incrementally and over time. This requires designing the language with evolution baked in on day one. This impacts a wide range of features: - At the lowest level, a simple and extensible syntax and grammar. - Tools and support for adding and removing APIs. - Scalable migration strategies, including tooling support. Rust shows the value of expanded semantic information in the type system such as precise lifetimes. This is hard to do in C++ since it has too many kinds of references and pointers, which increases the complexity in the type system multiplicatively. Carbon is attempting to compress C++'s type variations into just values and [pointers](#pointer-types). Rust also shows the value of functions parameterized by lifetimes. Since lifetimes are only used to establish safety properties of the code, there is no reason to pay the cost of monomorphization for those parameters. So we need a [checked-generics system](#generics) that can reason about code before it is instantiated, unlike C++ templates. In conclusion, there are two patterns in how Carbon diverges from C++: - Simplify and removing things to create space for new safety features. This trivially requires breaking backwards compatibility. - Re-engineer foundations to model and enforce safety. This is complex and difficult in C++ without first simplifying the language. This leads to Carbon's incremental path to safety: - Keep your performance, your existing codebase, and your developers. - Adopt Carbon through a scalable, tool-assisted migration from C++. - Address initial, easy safety improvements starting day one. - Shift the Carbon code onto an incremental path towards memory safety over the next decade. > References: [Safety design](/docs/design/safety) ### Lifetime and move semantics > **TODO:** ### Metaprogramming > **TODO:** References need to be evolved. Needs a detailed design and a high > level summary provided inline. Carbon provides metaprogramming facilities that look similar to regular Carbon code. These are structured, and do not offer arbitrary inclusion or preprocessing of source text such as C and C++ do. > References: [Metaprogramming](metaprogramming.md) ### Pattern matching as function overload resolution > **TODO:** References need to be evolved. Needs a detailed design and a high > level summary provided inline. > References: [Pattern matching](pattern_matching.md) ### Error handling For now, Carbon does not have language features dedicated to error handling, but we would consider adding some in the future. At this point, errors are represented using [choice types](#choice-types) like `Result` and `Optional`. This is similar to the story for Rust, which started using `Result`, then added [`?` operator](https://doc.rust-lang.org/reference/expressions/operator-expr.html#the-question-mark-operator) for convenience, and is now considering ([1](https://yaah.dev/try-blocks), [2](https://doc.rust-lang.org/beta/unstable-book/language-features/try-blocks.html)) adding more. ### Execution abstractions Carbon provides some higher-order abstractions of program execution, as well as the critical underpinnings of such abstractions. #### Abstract machine and execution model > **TODO:** #### Lambdas > **TODO:** References need to be evolved. Needs a detailed design and a high > level summary provided inline. > References: [Lambdas](lambdas.md), > [Proposal #3848: Lambdas](https://github.com/carbon-language/carbon-lang/pull/3848) #### Co-routines > **TODO:** #### Concurrency > **TODO:** ================================================ FILE: docs/design/aliases.md ================================================ # Aliases ## Table of contents - [TODO](#todo) - [Overview](#overview) - [Alternatives](#alternatives) ## TODO This is a skeletal design, added to support [the overview](README.md). It should not be treated as accepted by the core team; rather, it is a placeholder until we have more time to examine this detail. Please feel welcome to rewrite and update as appropriate. ## Overview Naming is one of the things that most often requires careful management over time -- things tend to get renamed and moved around. Carbon provides a fully general name aliasing facility to declare a new name as an alias for a value; everything is a value in Carbon. This is a fully general facility because everything is a value in Carbon, including types. For example: ``` alias MyInt = Int; ``` This creates an alias called `MyInt` for whatever `Int` resolves to. Code textually after this can refer to `MyInt`, and it will transparently refer to `Int`. ### Alternatives The syntax here is not at all in a good state yet. We've considered a few alternatives, but they all end up being confusing in some way. We need to figure out a good and clean syntax that can be used here. ================================================ FILE: docs/design/assignment.md ================================================ # Assignment ## Table of contents - [Overview](#overview) - [Syntax](#syntax) - [Simple assignment semantics](#simple-assignment-semantics) - [Compound assignment semantics](#compound-assignment-semantics) - [Built-in types](#built-in-types) - [Tuples, structs, choice types, and data classes](#tuples-structs-choice-types-and-data-classes) - [Extensibility](#extensibility) - [Simple assignment](#simple-assignment) - [Arithmetic](#arithmetic) - [Bitwise and bit-shift](#bitwise-and-bit-shift) - [Defaults](#defaults) - [Alternatives considered](#alternatives-considered) - [References](#references) ## Overview Values can be assigned to variables using the `=` operator: ``` var a: i32 = 5; a = 6; ``` For each binary [arithmetic](expressions/arithmetic.md) or [bitwise](expressions/bitwise.md) operator `$`, a corresponding compound assignment `$=` is provided that performs the operation in-place: ``` // Same as `a = a + 1;` a += 1; // Same as `a = a << 3;` a <<= 3; ``` In addition, increment and decrement operators are provided: ``` // Same as `a = a + 1;` ++a; // Same as `a = a - 1;` --a; ``` These simple assignment, compound assignment, increment, and decrement operators can only be used as complete statements, not as subexpressions of other operators, even when parenthesized: ``` var n: i32; // Error, assignment is not permitted as a subexpression. if (F() and (n = GetValue()) > 5) { } ``` User-defined types can define the meaning of these operations by [implementing an interface](#extensibility) provided as part of the Carbon standard library. ## Syntax The operands of these operators can be any [expression](expressions/README.md). However, the first operand must be modifiable because it is passed to a `[ref self: Self]` parameter, which disallows most expression forms other than: - The name of a `var` binding. - A dereference of a pointer. - Array indexing that produces a modifiable result. - Member access naming a field, where the object is one of these expressions. ## Simple assignment semantics A simple assignment statement is intended to exactly mirror the semantics of initialization. The following two code snippets should have the same meaning if they are both valid: ``` // Declare and initialize. var v: T = init; ``` ``` // Declare separately from initialization. // Requires that `T` has an unformed state. var v: T; v = init; ``` This equivalence is not enforced, but when an object is in an unformed state, running the assignment function is _optional_, just like running the destructor is. If the assignment function is not run, the object will be directly initialized from the right-hand side instead. The type is still required to implement `AssignWith` for the assignment to be valid. ``` class C { ... } fn F() -> C { returned var c: C = {...}; // `&c` here is `&x` for the first call to `F()`. // `&c` here can be `&y` for the second call to `F()`. return var; } fn G() { var x: C = F(); var y: C; y = F(); } ``` ## Compound assignment semantics The syntax `a $= b;` is intended to be syntactic sugar for `a = a $ b;`, except as follows: - A type might be able to provide a more efficient implementation for the compound assignment form than for the uncombined form. - A type might not be able to, or might not want to, provide the uncombined form at all, for example because creating a new instance requires additional resources that might not be available, such as a context object or an allocator. The syntactic sugar is implemented by a [default implementation](#defaults) of `$=` in terms of `$` and `=`. In contrast, `++a;` and `--a;` are not simply syntactic sugar for `a = a + 1;` and `a = a - 1;`. Instead, we interpret these operators as meaning "move to the next value" and "move to the previous value". These operations may be available and meaningful in cases where adding an integer is not a desirable operation, such as for an iterator into a linked list, and may not be available in cases where adding an integer is meaningful, such as for a type representing a rational number. ## Built-in types Integers and floating-point types, `bool`, and pointer types support simple assignment with `=`. The right-hand operand is implicitly converted to the type of the left-hand operand, and the converted value replaces the value of that operand. Compound assignment `$=` for integer and floating point types is [provided automatically](#defaults) for each supported operator `$`. For integer types, `++n;` and `--n;` behave the same as `n += 1;` and `n -= 1;` respectively. For floating-point types, these operators are not provided. ## Tuples, structs, choice types, and data classes _TODO_: Describe the rules for assignment in these cases. See leads issue [#686: Operation order in struct/class assignment/initialization](https://github.com/carbon-language/carbon-lang/issues/686) ## Extensibility Assignment operators can be provided for user-defined types by implementing the following families of interfaces. Implementations of these interfaces are provided for built-in types as necessary to give the semantics described above. ### Simple assignment ``` // Simple `=`. interface AssignWith(U:! type) { fn Op[ref self: Self](other: U); } constraint Assign { extend AssignWith(Self); } ``` Given `var x: T` and `y: U`: - The statement `x = y;` is rewritten to `x.(AssignWith(U).Op)(y);`. ### Arithmetic ``` // Compound `+=`. interface AddAssignWith(U:! type) { fn Op[ref self: Self](other: U); } constraint AddAssign { extend AddAssignWith(Self); } ``` ``` // Compound `-=`. interface SubAssignWith(U:! type) { fn Op[ref self: Self](other: U); } constraint SubAssign { extend SubAssignWith(Self); } ``` ``` // Compound `*=`. interface MulAssignWith(U:! type) { fn Op[ref self: Self](other: U); } constraint MulAssign { extend MulAssignWith(Self); } ``` ``` // Compound `/=`. interface DivAssignWith(U:! type) { fn Op[ref self: Self](other: U); } constraint DivAssign { extend DivAssignWith(Self); } ``` ``` // Compound `%=`. interface ModAssignWith(U:! type) { fn Op[ref self: Self](other: U); } constraint ModAssign { extend ModAssignWith(Self); } ``` ``` // Increment `++`. interface Inc { fn Op[ref self: Self](); } // Decrement `++`. interface Dec { fn Op[ref self: Self](); } ``` Given `var x: T` and `y: U`: - The statement `x += y;` is rewritten to `x.(AddAssignWith(U).Op)(y);`. - The statement `x -= y;` is rewritten to `x.(SubAssignWith(U).Op)(y);`. - The statement `x *= y;` is rewritten to `x.(MulAssignWith(U).Op)(y);`. - The statement `x /= y;` is rewritten to `x.(DivAssignWith(U).Op)(y);`. - The statement `x %= y;` is rewritten to `x.(ModAssignWith(U).Op)(y);`. - The statement `++x;` is rewritten to `x.(Inc.Op)();`. - The statement `--x;` is rewritten to `x.(Dec.Op)();`. ### Bitwise and bit-shift ``` // Compound `&=`. interface BitAndAssignWith(U:! type) { fn Op[ref self: Self](other: U); } constraint BitAndAssign { extend BitAndAssignWith(Self); } ``` ``` // Compound `|=`. interface BitOrAssignWith(U:! type) { fn Op[ref self: Self](other: U); } constraint BitOrAssign { extend BitOrAssignWith(Self); } ``` ``` // Compound `^=`. interface BitXorAssignWith(U:! type) { fn Op[ref self: Self](other: U); } constraint BitXorAssign { extend BitXorAssignWith(Self); } ``` ``` // Compound `<<=`. interface LeftShiftAssignWith(U:! type) { fn Op[ref self: Self](other: U); } constraint LeftShiftAssign { extend LeftShiftAssignWith(Self); } ``` ``` // Compound `>>=`. interface RightShiftAssignWith(U:! type) { fn Op[ref self: Self](other: U); } constraint RightShiftAssign { extend RightShiftAssignWith(Self); } ``` Given `var x: T` and `y: U`: - The statement `x &= y;` is rewritten to `x.(BitAndAssignWith(U).Op)(y);`. - The statement `x |= y;` is rewritten to `x.(BitOrAssignWith(U).Op)(y);`. - The statement `x ^= y;` is rewritten to `x.(BitXorAssignWith(U).Op)(y);`. - The statement `x <<= y;` is rewritten to `x.(LeftShiftAssignWith(U).Op)(y);`. - The statement `x >>= y;` is rewritten to `x.(RightShiftAssignWith(U).Op)(y)`;. Implementations of these interfaces are provided for built-in types as necessary to give the semantics described above. ### Defaults When a type provides both an assignment and a binary operator `$`, so that `a = a $ b;` is valid, Carbon provides a default `$=` implementation so that `a $= b;` is valid and has the same meaning as `a = a $ b;`. This defaulting is accomplished by a parameterized implementation of `OpAssignWith(U)` defined in terms of `AssignWith` and `OpWith`: ``` impl forall [U:! type, T:! OpWith(U) where .Self impls AssignWith(.Self.Result)] T as OpAssignWith(U) { fn Op[ref self: Self](other: U) { // Here, `$` is the operator described by `OpWith`. *self = *self $ other; } } ``` If a more efficient form of compound assignment is possible for a type, a more specific `impl` can be provided: ``` impl like MyString as AddWith(like MyString) { // Allocate new memory and perform addition. } impl MyString as AddAssignWith(like MyString) { // Reuse existing storage where possible. } ``` ## Alternatives considered - [Allow assignment as a subexpression](/proposals/p2511.md#allow-assignment-as-a-subexpression) - [Allow chained assignment](/proposals/p2511.md#allow-chained-assignment) - [Do not provide increment and decrement](/proposals/p2511.md#do-not-provide-increment-and-decrement) - [Treat increment as syntactic sugar for adding `1`](/proposals/p2511.md#treat-increment-as-syntactic-sugar-for-adding-1) - [Define `$` in terms of `$=`](/proposals/p2511.md#define--in-terms-of-) - [Do not allow overloading the behavior of `=`](/proposals/p2511.md#do-not-allow-overloading-the-behavior-of-) - [Treat the left hand side of `=` as a pattern](/proposals/p2511.md#treat-the-left-hand-side-of--as-a-pattern) - [Different names for interfaces](/proposals/p2511.md#different-names-for-interfaces) ## References - Leads issue [#451: Do we want variable-arity operators?](https://github.com/carbon-language/carbon-lang/issues/451) - Proposal [#257: Initialization of memory and variables](https://github.com/carbon-language/carbon-lang/pull/257) - Proposal [#1083: Arithmetic](https://github.com/carbon-language/carbon-lang/pull/1083) - Proposal [#1178: Rework operator interfaces](https://github.com/carbon-language/carbon-lang/pull/1178) - Proposal [#1191: Bitwise and shift operators](https://github.com/carbon-language/carbon-lang/pull/1191) - Proposal [#2511: Assignment statements](https://github.com/carbon-language/carbon-lang/pull/2511) ================================================ FILE: docs/design/blocks_and_statements.md ================================================ # Blocks and statements ## Table of contents - [TODO](#todo) - [Overview](#overview) ## TODO This is a skeletal design, added to support [the overview](README.md). It should not be treated as accepted by the core team; rather, it is a placeholder until we have more time to examine this detail. Please feel welcome to rewrite and update as appropriate. ## Overview The body or definition of a function is provided by a block of code containing statements, much like in C or C++. The body of a function is also a new, nested scope inside the function's scope (meaning that parameter names are available). Statements within a block are terminated by a semicolon. Each statement can, among other things, be an expression. Here is a trivial example of a function definition using a block of statements: ``` fn Foo() { Bar(); Baz(); } ``` Statements can also themselves be a block of statements, which provide scopes and nesting: ``` fn Foo() { Bar(); { Baz(); } } ``` ================================================ FILE: docs/design/classes.md ================================================ # Classes ## Table of contents - [Overview](#overview) - [Use cases](#use-cases) - [Data classes](#data-classes) - [Encapsulated types](#encapsulated-types) - [Without inheritance](#without-inheritance) - [With inheritance and subtyping](#with-inheritance-and-subtyping) - [Polymorphic types](#polymorphic-types) - [Interface as base class](#interface-as-base-class) - [Non-polymorphic inheritance](#non-polymorphic-inheritance) - [Interop with C++ multiple inheritance](#interop-with-c-multiple-inheritance) - [Mixins](#mixins) - [Background](#background) - [Members](#members) - [Data members have an order](#data-members-have-an-order) - [Struct types](#struct-types) - [Literals](#literals) - [Type expression](#type-expression) - [Assignment and initialization](#assignment-and-initialization) - [Operations performed field-wise](#operations-performed-field-wise) - [Nominal class types](#nominal-class-types) - [Fields](#fields) - [Forward declaration](#forward-declaration) - [`Self`](#self) - [Construction](#construction) - [Assignment](#assignment) - [Member functions](#member-functions) - [Class functions](#class-functions) - [Methods](#methods) - [Deferred member function definitions](#deferred-member-function-definitions) - [Name lookup in classes](#name-lookup-in-classes) - [Nominal data classes](#nominal-data-classes) - [Member type](#member-type) - [Let](#let) - [Alias](#alias) - [Inheritance](#inheritance) - [Virtual methods](#virtual-methods) - [Virtual modifier keywords](#virtual-modifier-keywords) - [Subtyping](#subtyping) - [`Self` refers to the current type](#self-refers-to-the-current-type) - [Constructors](#constructors) - [Partial class type](#partial-class-type) - [Usage](#usage) - [Assignment with inheritance](#assignment-with-inheritance) - [Destructors](#destructors) - [Access control](#access-control) - [Private access](#private-access) - [Protected access](#protected-access) - [Friends](#friends) - [Test friendship](#test-friendship) - [Access control for construction](#access-control-for-construction) - [Operator overloading](#operator-overloading) - [Future work](#future-work) - [Struct literal shortcut](#struct-literal-shortcut) - [Optional named parameters](#optional-named-parameters) - [Field defaults for struct types](#field-defaults-for-struct-types) - [Destructuring in pattern matching](#destructuring-in-pattern-matching) - [Discussion](#discussion) - [Inheritance](#inheritance-1) - [C++ abstract base classes interoperating with object-safe interfaces](#c-abstract-base-classes-interoperating-with-object-safe-interfaces) - [Overloaded methods](#overloaded-methods) - [Interop with C++ inheritance](#interop-with-c-inheritance) - [Virtual base classes](#virtual-base-classes) - [Mixins](#mixins-1) - [Memory layout](#memory-layout) - [No `static` variables](#no-static-variables) - [Computed properties](#computed-properties) - [Interfaces implemented for data classes](#interfaces-implemented-for-data-classes) - [Alternatives considered](#alternatives-considered) - [References](#references) ## Overview A Carbon _class_ is a user-defined [record type](). A class has members that are referenced by their names, in contrast to a [Carbon tuple](tuples.md) which defines a [product type](https://en.wikipedia.org/wiki/Product_type) whose members are referenced positionally. Classes are the primary mechanism for users to extend the Carbon type system and are deeply rooted in C++ and its history (C and Simula). We call them classes rather than other terms as that is both familiar to existing programmers and accurately captures their essence: they define the types of objects with (optional) support for methods, encapsulation, and so on. Carbon supports both named, or "nominal", and unnamed, anonymous, or "structural", class types. Nominal class types are all distinct, but structural types are equal if they have the same sequence of member types and names. Structural class literals may be used to initialize or assign values to nominal class variables. A class type defines the interpretation of the bytes of a value of that type, including the size, data members, and layout. It defines the operations that may be performed on those values, including what methods may be called. A class type may directly have constant members. The type itself is a compile-time immutable constant value. ## Use cases The use cases for classes include both cases motivated by C++ interop, and cases that we expect to be included in idiomatic Carbon-only code. **This design currently only attempts to address the "data classes" and "encapsulated types" use cases.** Addressing the "interface as base class", "interop with C++ multiple inheritance" and "mixin" use cases is future work. ### Data classes Data classes are types that consist of data fields that are publicly accessible and directly read and manipulated by client code. They have few if any methods, and generally are not involved in inheritance at all. Examples include: - a key and value pair returned from a `SortedMap` or `HashMap` - a 2D point that might be used in a rendering API Properties: - Operations like copy, move, destroy, unformed, and so on are defined field-wise. - Anonymous classes types and literals should match data class semantics. Expected in idiomatic Carbon-only code. **Background:** Kotlin has a dedicated concise syntax for defining [_data classes_](https://kotlinlang.org/docs/data-classes.html) that avoids boilerplate. Python has a [data class library](https://docs.python.org/3/library/dataclasses.html), proposed in [PEP 557](https://www.python.org/dev/peps/pep-0557/), that fills a similar role. ### Encapsulated types There are several categories of types that support [encapsulation](). This is done by making their data fields private so access and modification of values are all done through methods defined on the type. #### Without inheritance The common case for encapsulated types are those that do not participate in inheritance. These types neither support being inherited from (they are ["final"]()) nor do they extend other types. Examples of this use case include: - strings, containers, iterators - types with invariants such as `Date` - RAII types that are movable but not copyable like C++'s `std::unique_ptr` or a file handle - non-movable types like `Mutex` We expect two kinds of methods on these types: public methods defining the API for accessing and manipulating values of the type, and private helper methods used as an implementation detail of the public methods. These types are expected in idiomatic Carbon-only code. #### With inheritance and subtyping The [subtyping](https://en.wikipedia.org/wiki/Subtyping) you get with inheritance is that you may assign the address of an object of a derived type to a pointer to its base type. For this to work, the compiler needs implementation strategies that allow operations performed through the pointer to the base type work independent of which derived type it actually points to. These strategies include: - Arranging for the data layout of derived types to start with the data layout of the base type as a prefix. - Putting a pointer to a table of function pointers, a [_vtable_](https://en.wikipedia.org/wiki/Virtual_method_table), as the first data member of the object. This allows methods to be [_virtual_](https://en.wikipedia.org/wiki/Virtual_function) and have a derived-type-specific implementation, an _override_, that is used even when invoking the method on a pointer to a base type. - Non-virtual methods implemented on a base type should be applicable to all derived types. In general, derived types should not attempt to overload or override non-virtual names defined in the base type. Note that these subtyping implementation strategies generally rely on encapsulation, but encapsulation is not a strict requirement in all cases. This subtyping relationship also creates safety concerns, which Carbon should protect against. [Slicing problems](https://en.wikipedia.org/wiki/Object_slicing) can arise when the source or target of an assignment is a dereferenced pointer to the base type. It is also incorrect to delete an object with a non-virtual destructor through a pointer to a base type. ##### Polymorphic types Carbon will fully support single-inheritance type hierarchies with polymorphic types. Polymorphic types support [dynamic dispatch](https://en.wikipedia.org/wiki/Dynamic_dispatch) using a [vtable](https://en.wikipedia.org/wiki/Virtual_method_table), and data members, but only single inheritance. Individual methods opt in to using dynamic dispatch, so types will have a mix of ["virtual"](https://en.wikipedia.org/wiki/Virtual_function) and non-virtual methods. Polymorphic types support traditional [object-oriented single inheritance](), a mix of [subtyping](https://en.wikipedia.org/wiki/Subtyping) and [implementation and code reuse](). We exclude complex multiple inheritance schemes, virtual inheritance, and so on from this use case. This is to avoid the complexity and overhead they bring, particularly since the use of these features in C++ is generally discouraged. The rule is that every type has at most one base type with data members for subtyping purposes. Carbon will support additional base types as long as they [don't have data members](#interface-as-base-class) or [don't support subtyping](#mixins). **Background:** [The "Nothing is Something" talk by Sandi Metz](https://www.youtube.com/watch?v=OMPfEXIlTVE) and [the Composition Over Inheritance Principle](https://python-patterns.guide/gang-of-four/composition-over-inheritance/) describe design patterns to use instead of multiple inheritance to support types that vary over multiple axes. In rare cases where the complex multiple inheritance schemes of C++ are truly needed, they can be effectively approximated using a combination of these simpler building blocks. Polymorphic types support a number of different kinds of methods: - They will have virtual methods: - Polymorphic types will typically include virtual destructors. - The virtual methods types may have default implementations or be [_abstract_]() (or [_pure virtual_](https://en.wikipedia.org/wiki/Virtual_function#Abstract_classes_and_pure_virtual_functions)). In the latter case, they must be implemented in any derived class that can be instantiated. - Virtual methods may be [_protected_](https://en.wikipedia.org/wiki/Access_modifiers) or [_private_](https://stackoverflow.com/questions/2170688/private-virtual-method-in-c), intended to be called by methods in the base type but implemented in the descendant. - They may have non-virtual public or private helper methods, like [encapsulated types without inheritance](#without-inheritance). These avoid the overhead of a virtual function call, and can be written when the base class has sufficient data members. - They may have protected helper methods, typically non-virtual, provided by the base type to be called by the descendant. Note that there are two uses for protected methods: those implemented in the base and called in the descendant, and the other way around. ["The End Of Object Inheritance & The Beginning Of A New Modularity" talk by Augie Fackler and Nathaniel Manista](https://www.youtube.com/watch?v=3MNVP9-hglc) discusses design patterns that split up types to reduce the number of kinds of calls between base and derived types, and make sure calls only go in one direction. We expect polymorphic types in idiomatic Carbon-only code, at least for the medium term. Extending this design to support polymorphic types is future work. ###### Interface as base class We distinguish the specific case of polymorphic base classes that have no data members: - From an implementation perspective, the lack of data members removes most of the problems with supporting multiple inheritance. - They are about decoupling two pieces of code instead of collaborating. - As a use case, they are used primarily for subtyping and much less implementation reuse than other polymorphic types. - This case overlaps with the [interface](/docs/design/generics/terminology.md#interface) concept introduced for [Carbon generics](/docs/design/generics/overview.md). Removing support for data fields greatly simplifies supporting multiple inheritance. For example, it removes the need for a mechanism to figure out the offset of those data fields in the object. Similarly we don't need [C++'s virtual inheritance](https://en.wikipedia.org/wiki/Virtual_inheritance) to avoid duplicating those fields. Some complexities still remain, such as pointers changing values when casting to a secondary parent type, but these seem manageable given the benefits of supporting this useful case of multiple inheritance. While an interface base class is generally for providing an API that allows decoupling two pieces of code, a polymorphic type is a collaboration between a base and derived type to provide some functionality. This is a bit like the difference between a library and a framework, where you might use many of the former but only one of the latter. Interface base classes are primarily used for subtyping. The extent of implementation reuse is generally limited by the lack of data members, and the decoupling role they play is usually about defining an API as a set of public pure-virtual methods. Compared to other polymorphic types, they more rarely have methods with implementations (virtual or not), or have methods with restricted access. The main use case is when there is a method that is implemented in terms of pure-virtual methods. Those pure-virtual methods may be marked as protected to ensure they are only called through the non-abstract API, but can still be implemented in descendants. While it is typical for this case to be associated with single-level inheritance hierarchies, there are some cases where there is an interface at the root of a type hierarchy and polymorphic types as interior branches of the tree. The case of interfaces extending or requiring other interface would also be modeled by deeper inheritance hierarchies. An interface as base class needs to either have a virtual destructor or forbid deallocation. There is significant overlap between interface base classes and [Carbon interfaces](generics/overview.md#interfaces). Both represent APIs as a collection of method names and signatures to implement. The subset of interfaces that support dynamic dispatch are called _object-safe_, following [Rust](https://doc.rust-lang.org/reference/items/traits.html#object-safety): - They don't have a `Self` in the signature of a method in a contravariant position like a parameter. - They don't have free associated facets or other associated items used in a method signature. The restrictions on object-safe interfaces match the restrictions on base class methods. The main difference is the representation in memory. A type extending a base class with virtual methods includes a pointer to the table of methods in the object value itself, while a type implementing an interface would store the pointer alongside the pointer to the value in a `DynPtr(MyInterface)`. Of course, the interface option also allows the method table to be passed at compile time. **Note:** This presumes that we include some concept of `final` methods in interfaces to match non-virtual functions in base classes. We expect idiomatic Carbon-only code to generally use Carbon interfaces instead of interface base classes. We may still support interface base classes long term if we determine that the ability to put the pointer to the method implementations in the object value is important for users, particularly with a single parent as in the [polymorphic type case](#polymorphic-types). Extending this design to support interface base classes is future work. **Background:** [C++ abstract base classes](https://en.wikipedia.org/wiki/Abstract_type) that don't have data members and [Java interfaces]() model this case. ##### Non-polymorphic inheritance While it is not common, there are cases where C++ code uses inheritance without dynamic dispatch or a [vtable](https://en.wikipedia.org/wiki/Virtual_method_table). Instead, methods are never overridden, and derived types only add data and methods. There are some cases where this is done in C++ but would be done differently in Carbon: - For implementation reuse without subtyping, Carbon code should use mixins or composition. Carbon won't support private inheritance. - Carbon will allow data members to have size zero, so the [empty-base optimization](https://en.cppreference.com/w/cpp/language/ebo) is unnecessary. - For cases where the derived type does not add any data members, in Carbon you can potentially use adapter types instead of inheritance. However, there are still some cases where non-virtual inheritance makes sense. One is a parameterized type where a prefix of the data is the same independent of the parameter. An example of this is containers with a [small-buffer optimization](https://akrzemi1.wordpress.com/2014/04/14/common-optimizations/#sbo), as described in the talk [CppCon 2016: Chandler Carruth "High Performance Code 201: Hybrid Data Structures"](https://www.youtube.com/watch?v=vElZc6zSIXM). By moving the data and methods that don't depend on the buffer size to a base class, we reduce the instantiation overhead for monomorphization. The base type is also useful for reducing instantiation for consumers of the container, as long as they only need to access methods defined in the base. Another case for non-virtual inheritance is for different node types within a data structure that have some data members in common. This is done in LLVM's map, [red-black tree](https://github.com/llvm-mirror/libcxx/blob/master/include/__tree), and list data structure types. In a linked list, the base type might have the next and previous pointers, which is enough for a sentinel node, and there would also be a derived type with the actual data member. The base type can define operations like "splice" that only operate on the pointers not the data, and this is in fact enforced by the type system. Only the derived node type needs to be parameterized by the element type, saving on instantiation costs as before. Many of the concerns around non-polymorphic inheritance are the same as for the non-virtual methods of [polymorphic types](#polymorphic-types). Assignment and destruction are examples of operations that need particular care to be sure they are only done on values of the correct type, rather than through a subtyping relationship. This means having some extrinsic way of knowing when it is safe to downcast before performing one of those operations, or performing them on pointers that were never upcast to the base type. ##### Interop with C++ multiple inheritance While Carbon won't support all the C++ forms of multiple inheritance, Carbon code will still need to interoperate with C++ code that does. Of particular concern are the `std::iostream` family of types. Most uses of those types are the input and output variations or could be migrated to use those variations, not the harder bidirectional cases. Much of the complexity of this interoperation could be alleviated by adopting the restriction that Carbon code can't directly access the fields of a virtual base class. In the cases where such access is needed, the workaround is to access them through C++ functions. We do not expect idiomatic Carbon-only code to use multiple inheritance. Extending this design to support interoperating with C++ types using multiple inheritance is future work. ### Mixins A [mixin](https://en.wikipedia.org/wiki/Mixin) is a declaration of data, methods, and interface implementations that can be added to another type, called the "main type". The methods of a mixin may also use data, methods, and interface implementations provided by the main type. Mixins are designed around implementation reuse rather than subtyping, and so don't need to use a vtable. A mixin might be an implementation detail of a [data class](#data-classes), or [encapsulated type](#encapsulated-types). A mixin might partially implement an [interface as base class](#interface-as-base-class). **Examples:** [intrusive linked list](https://www.boost.org/doc/libs/1_63_0/doc/html/intrusive.html), intrusive reference count In both of these examples, the mixin needs the ability to convert between a pointer to the mixin's data (like a "next" pointer or reference count) and a pointer to the containing object with the main type. Mixins are expected in idiomatic Carbon-only code. Extending this design to support mixins is future work. **Background:** Mixins are typically implemented using the [curiously recurring template pattern](https://en.wikipedia.org/wiki/Curiously_recurring_template_pattern) in C++, but other languages support them directly. - In Dart, the mixin defines an interface that the destination type ends up implementing, which restores a form of subtyping. See [Dart: What are mixins?](https://medium.com/flutter-community/dart-what-are-mixins-3a72344011f3). - Swift is considering [a proposal to add mixin support](https://github.com/Anton3/swift-evolution/blob/mixins/proposals/NNNN-mixins.md). ## Background See how other languages tackle this problem: - [Swift](https://docs.swift.org/swift-book/LanguageGuide/ClassesAndStructures.html) - has two different concepts: classes support [inheritance](https://docs.swift.org/swift-book/LanguageGuide/Inheritance.html) and use [reference counting](https://docs.swift.org/swift-book/LanguageGuide/AutomaticReferenceCounting.html) while structs have value semantics - may have [constructor functions called "initializers"](https://docs.swift.org/swift-book/LanguageGuide/Initialization.html) and [destructors called "deinitializers"](https://docs.swift.org/swift-book/LanguageGuide/Deinitialization.html) - supports [properties](https://docs.swift.org/swift-book/LanguageGuide/Properties.html), including computed & lazy properties - methods are const by default [unless marked mutating](https://docs.swift.org/swift-book/LanguageGuide/Methods.html#ID239) - supports [extensions](https://docs.swift.org/swift-book/LanguageGuide/Extensions.html) - has per-field [access control](https://docs.swift.org/swift-book/LanguageGuide/AccessControl.html) - [Rust](https://doc.rust-lang.org/book/ch05-01-defining-structs.html) - has no support for inheritance - has no special constructor functions, instead has literal syntax - has some convenience syntax for common cases: [variable and field names matching](https://doc.rust-lang.org/book/ch05-01-defining-structs.html#using-the-field-init-shorthand-when-variables-and-fields-have-the-same-name), [updating a subset of fields](https://doc.rust-lang.org/book/ch05-01-defining-structs.html#creating-instances-from-other-instances-with-struct-update-syntax) - [can have unnamed fields](https://doc.rust-lang.org/book/ch05-01-defining-structs.html#using-tuple-structs-without-named-fields-to-create-different-types) - [supports structs with size 0](https://doc.rust-lang.org/book/ch05-01-defining-structs.html#unit-like-structs-without-any-fields) - [Zig](https://ziglang.org/documentation/0.6.0/#struct) - [explicitly mark structs as packed to manually control layout](https://ziglang.org/documentation/0.6.0/#packed-struct) - has a struct literal syntax, [including for anonymous structs](https://ziglang.org/documentation/0.6.0/#Anonymous-Struct-Literals) - no special constructor functions - supports fields with undefined values - supports structs with size 0 - supports generics by way of memoized compile time functions accepting and returning types - [supports default field values](https://ziglang.org/documentation/0.6.0/#toc-Default-Field-Values) - [has no properties or operator overloading -- Zig does not like hidden control flow](https://ziglang.org/#Small-simple-language) ## Members The members of a class are named, and are accessed with the `.` notation. For example: ``` var p: Point2D = ...; // Data member access p.x = 1; p.y = 2; // Method call Print(p.DistanceFromOrigin()); ``` [Tuples](tuples.md) are used for cases where accessing the members positionally is more appropriate. ### Data members have an order The data members of a class, or _fields_, have an order that matches the order they are declared in. This determines the order of those fields in memory, and the order that the fields are destroyed when a value goes out of scope or is deallocated. ## Struct types _Structural data classes_, or _struct types_, are convenient for defining [data classes](#data-classes) in an ad-hoc manner. They would commonly be used: - as the return type of a function that returns multiple values and wants those values to have names so a [tuple](tuples.md) is inappropriate - as an initializer for other `class` variables or values - as a type parameter to a container Note that struct types are examples of _data class types_ and are still classes. The ["nominal data classes" section](#nominal-data-classes) describes another way to define a data class type. Also note that there is no `struct` keyword, "struct" is just convenient shorthand terminology for a structural data class. ### Literals _Structural data class literals_, or _struct literals_, are written using this syntax: ``` var kvpair: auto = {.key = "the", .value = 27}; ``` This produces a struct value with two fields: - The first field is named "`key`" and has the value `"the"`. The type of the field is set to the type of the value, and so is `String`. - The second field is named "`value`" and has the value `27`. The type of the field is set to the type of the value, and so is `i32`. Note: A comma `,` may optionally be included after the last field: ``` var kvpair: auto = {.key = "the", .value = 27,}; ``` **Open question:** To keep the literal syntax from being ambiguous with compound statements, Carbon will adopt some combination of: - looking ahead after a `{` to see if it is followed by `.name`; - not allowing a struct literal at the beginning of a statement; - only allowing `{` to introduce a compound statement in contexts introduced by a keyword where they are required, like requiring `{ ... }` around the cases of an `if...else` statement. ### Type expression The type of `kvpair` in the last example would be represented by this expression: ``` {.key: String, .value: i32} ``` This syntax is intended to parallel the literal syntax, and so uses commas (`,`) to separate fields instead of a semicolon (`;`) terminator. This choice also reflects the expected use inline in function signature declarations. Struct types may only have data members, so the type declaration is just a list of field names and types. The result of a struct type expression is an immutable compile-time type value. Note: Like with struct literal expressions, a comma `,` may optionally be included after the last field: ``` {.key: String, .value: i32,} ``` Also note that `{}` represents both the empty struct literal and its type. ### Assignment and initialization When initializing or assigning a variable with a data class such as a struct type to a struct value on the right hand side, the order of the fields does not have to match, just the names. ``` var different_order: {.x: i32, .y: i32} = {.y = 2, .x = 3}; Assert(different_order.x == 3); Assert(different_order.y == 2); ``` Initialization and assignment occur field-by-field. The order of fields is determined by the source on the right side of the `=`, and individual operations are generally interleaved field-by-field. See [here](values.md#type-conversions) for details about the semantics, and [here](pattern_matching.md#evaluation-order) for details about the order of operations. > **Open question:** Do we need a way for a class to require the source order to > match? Should that be the default, with an opt out? > **Open question:** What operations and in what order happen for assignment? > > - Is assignment just destruction followed by initialization? Is that > destruction completed for the whole object before initializing, or is it > interleaved field-by-field? > - Perhaps some operations are _not_ ordered with respect to each other? ### Operations performed field-wise Generally speaking, the operations that are available on a data class value, such as a value with a struct type, are dependent on those operations being available for all the types of the fields. For example, two values of the same data class type may be compared for equality or inequality if equality is supported for every member of the type: ``` var p: auto = {.x = 2, .y = 3}; Assert(p == {.x = 2, .y = 3}); Assert(p != {.x = 2, .y = 4}); Assert({.x = 2, .y = 4} != {.x = 5, .y = 3}); ``` Equality and inequality comparisons are also allowed between different data class types when: - At least one is a struct type. - They have the same set of field names, though the order may be different. - Equality comparison is defined between the pairs of member types with the same field names. For example, since [comparison between `i32` and `u32` is defined](/proposals/p0702.md#built-in-comparisons-and-implicit-conversions), equality comparison between values of types `{.x: i32, .y: i32}` and `{.y: u32, .x: u32}` is as well. Equality and inequality comparisons compare fields using the field order of the left-hand operand and stop once the outcome of the comparison is determined. However, the comparison order and short-circuiting are generally expected to affect only the performance characteristics of the comparison and not its meaning. Ordering comparisons, such as `<` and `<=`, use the order of the fields to do a [lexicographical comparison](https://en.wikipedia.org/wiki/Lexicographic_order). The argument types must have a matching order of the field names. Otherwise, the restrictions on ordering comparisons between different data class types are analogous to equality comparisons: - At least one is a struct type. - Ordering comparison is defined between the pairs of member types with the same field names. Implicit conversion from a struct type to a data class type is allowed when the set of field names is the same and implicit conversion is defined between the pairs of member types with the same field names. So calling a function effectively performs an initialization of each of the function's parameters from the caller's arguments, and will be valid when those initializations are all valid. A data class has an unformed state if all its members do. Treatment of unformed state follows proposal [#257](https://github.com/carbon-language/carbon-lang/pull/257). Destruction is performed field-wise in reverse order. Extending user-defined operations on the fields to an operation on an entire data class is [future work](#interfaces-implemented-for-data-classes). **References:** The rules for assignment, comparison, and implicit conversion for argument passing were decided in [question-for-leads issue #710](https://github.com/carbon-language/carbon-lang/issues/710). ## Nominal class types The declarations for nominal class types will have: - an optional `abstract` or `base` prefix - `class` introducer - the name of the class - `{`, an open curly brace - a sequence of declarations - `}`, a close curly brace Declarations within a class should generally have the same syntax as declarations that occur in other contexts. For example, member functions are introduced with `fn`. ### Fields Fields of a nominal class type are declared with `var`: ``` class TextLabel { var x: i32; var y: i32; var text: String = "default"; } ``` Notice that this is subtly different from the meaning of `var` in other contexts: it declares an [instance variable](https://en.wikipedia.org/wiki/Instance_variable), not just a variable in the class's scope. > **Open question:** Is there a way to declare class variables (scoped to the > class, not an instance)? In a field declaration, an initializer (such as `= "default"` above) specifies the default value of the field, and will be ignored if another value is supplied for that field when constructing an instance of the class. Defaults must be constants whose value can be determined at compile time. The pattern in a field declaration must be a run-time binding pattern, so the full syntax is: _field-declaration_ ::= `var` _identifier_ `:` _expression_ [ `=` _expression_ ] `;` ### Forward declaration To support circular references between class types, we allow [forward declaration](https://en.wikipedia.org/wiki/Forward_declaration) of types. Forward declarations end with semicolon `;` after the name of the class, instead of the block of declarations in curly braces `{`...`}`. A type that is forward declared is considered incomplete until the end of a definition with the same name. ``` // Forward declaration of `GraphNode`. class GraphNode; class GraphEdge { var head: GraphNode*; var tail: GraphNode*; } class GraphNode { var edges: Vector(GraphEdge*); } // `GraphNode` is first complete here. ``` **Open question:** What is specifically allowed and forbidden with an incomplete type has not yet been decided. > **TODO:** Document that qualified names can be looked up in an incomplete > type, as adopted in > [p5087: Qualified lookup into types being defined](/proposals/p5087.md). ### `Self` A `class` definition may provisionally include references to its own name in limited ways. These limitations arise from the type not being complete until the end of its definition is reached. ``` class IntListNode { var data: i32; var next: IntListNode*; } ``` An equivalent definition of `IntListNode`, since the `Self` keyword is an alias for the current type, is: ``` class IntListNode { var data: i32; var next: Self*; } ``` `Self` refers to the innermost type declaration: ``` class IntList { class IntListNode { var data: i32; // `Self` is `IntListNode`, not `IntList`. var next: Self*; } var first: IntListNode*; } ``` ### Construction Any function with access to all the data fields of a class can construct one by converting a [struct value](#struct-types) to the class type: ``` var tl1: TextLabel = {.x = 1, .y = 2}; var tl2: auto = {.x = 1, .y = 2} as TextLabel; Assert(tl1.x == tl2.x); fn ReturnsATextLabel() -> TextLabel { return {.x = 1, .y = 2}; } var tl3: TextLabel = ReturnsATextLabel(); fn AcceptsATextLabel(tl: TextLabel) -> i32 { return tl.x + tl.y; } Assert(AcceptsATextLabel({.x = 2, .y = 4}) == 6); ``` Note that a nominal class, unlike a [struct type](#type-expression), can define default values for fields, and so may be initialized with a [struct value](#literals) that omits some or all of those fields. #### Assignment Assignment to a struct value is also allowed in a function with access to all the data fields of a class. Assignment always overwrites all of the field members. ``` var tl: TextLabel = {.x = 1, .y = 2}; Assert(tl.text == "default"); // ✅ Allowed: assigns all fields tl = {.x = 3, .y = 4, .text = "new"}; // ✅ Allowed: This statement is evaluated in two steps: // 1. {.x = 5, .y = 6} is converted into a new TextLabel value, // using default for field `text`. // 2. tl is assigned to a TextLabel, which has values for all // fields. tl = {.x = 5, .y = 6}; Assert(tl.text == "default"); ``` **Open question:** This behavior might be surprising because there is an ambiguity about whether to use the default value or the previous value for a field. We could require all fields to be specified when assigning, and only use field defaults when initializing a new value. ``` // ❌ Forbidden: should tl.text == "default" or "new"? tl = {.x = 5, .y = 6}; ``` ### Member functions Member functions can either be class functions or methods. Class functions are members of the type, while methods can only be called on instances. #### Class functions A class function is like a [C++ static member function](https://en.cppreference.com/w/cpp/language/static#Static_member_functions), and is declared like a function at file scope. The declaration can include a definition of the function body, or that definition can be provided out of line after the class definition is finished. A common use is for constructor functions. ``` class Point { fn Origin() -> Self { return {.x = 0, .y = 0}; } fn CreateCentered() -> Self; var x: i32; var y: i32; } fn Point.CreateCentered() -> Self { return {.x = ScreenWidth() / 2, .y = ScreenHeight() / 2}; } ``` Class functions are members of the type, and may be accessed as using dot `.` member access either the type or any instance. ``` var p1: Point = Point.Origin(); var p2: Point = p1.CreateCentered(); ``` #### Methods [Method]() declarations are distinguished from [class function](#class-functions) declarations by having a `self` parameter in square brackets `[`...`]` before the explicit parameter list in parens `(`...`)`. There is no implicit member access in methods, so inside the method body members are accessed through the `self` parameter. Methods may be written lexically inline or after the class declaration. ```carbon class Circle { fn Diameter[self: Self]() -> f32 { return self.radius * 2; } fn Expand[ref self: Self](distance: f32); var center: Point; var radius: f32; } fn Circle.Expand[ref self: Self](distance: f32) { self.radius += distance; } var c: Circle = {.center = Point.Origin(), .radius = 1.5 }; Assert(Math.Abs(c.Diameter() - 3.0) < 0.001); c.Expand(0.5); Assert(Math.Abs(c.Diameter() - 4.0) < 0.001); ``` - Methods are called using the dot `.` member syntax, `c.Diameter()` and `c.Expand(`...`)`. - `Diameter` computes and returns the diameter of the circle without modifying the `Circle` instance. This is signified using `[self: Self]` in the method declaration. - `c.Expand(`...`)` does modify the value of `c`. This is signified using `[ref self: Self]` in the method declaration. The pattern '`ref self:` _type_' means "the argument must be a [reference expression](/docs/design/values.md#reference-expressions), and must match the pattern '`self:` _type_'". If the method declaration also includes [deduced compile-time parameters](/docs/design/generics/overview.md#deduced-parameters), the `self` parameter must be in the same list in square brackets `[`...`]`. The `self` parameter may appear in any position in that list, as long as it appears after any names needed to describe its type. #### Deferred member function definitions When defining a member function lexically inline, the body is deferred and processed as if it appeared immediately after the end of the outermost enclosing class, like in C++. For example, given a class with inline function definitions: ```carbon class Point { fn Distance[self: Self]() -> f32 { return Math.Sqrt(self.x * self.x + self.y * self.y); } fn Make(x: f32, y: f32) -> Point { return {.x = x, .y = y}; } var x: f32; var y: f32; } ``` These are all parsed as if they were defined outside the class scope: ```carbon class Point { fn Distance[self: Self]() -> f32; fn Make(x: f32, y: f32) -> Point; var x: f32; var y: f32; } fn Point.Distance[self: Self]() -> f32 { return Math.Sqrt(self.x * self.x + self.y * self.y); } fn Point.Make(x: f32, y: f32) -> Point { return {.x = x, .y = y}; } ``` #### Name lookup in classes [Member access](expressions/member_access.md) is an expression; details are covered there. Because function definitions are [deferred](#deferred-member-function-definitions), name lookup in classes works the same regardless of whether a function is inline. The class body forms a scope for name lookup, and function definitions can perform unqualified name lookup within that scope. For example: ```carbon class Square { fn GetArea[self: Self]() -> f32 { // ✅ OK: performs name lookup on `self`. return self.size * self.size; // ❌ Error: finds `Square.size`, but an instance is required. return size * size; // ❌ Error: an instance is required. return Square.size * Square.size; // ✅ OK: performs instance binding with `self`. return self.(Square.size) * self.(Square.size); // ✅ OK: uses unqualified name lookup to find `Square.size`, then performs // instance binding with `self`. return self.(size) * self.(size); } fn GetDoubled[self: Self]() -> Square { // ✅ OK: performs name lookup on `Square` for `Create`. return Square.Make(self.size); // ✅ OK: performs unqualified name lookup within class scope for `Create`. return Make(self.size); // ✅ OK: performs name lookup on `self` for `Create`. return self.Make(self.size); } fn Make(size: f32) -> Square; var size: f32; } ``` The example's name lookups refer to `Create` and `size` which are defined after the example member access; this is valid because of [deferred member function definitions](#deferred-member-function-definitions). However, function signatures must still complete lookup without deferring. For example: ```carbon class List { // ❌ Error: `Iterator` has not yet been defined. fn Iterate() -> Iterator; class Iterator { ... } // ✅ OK: The definition of Iterator is now available. fn Iterate() -> Iterator; } ``` An out-of-line function definition's parameters, return type, and body are evaluated as if in-scope. For example: ```carbon // ✅ OK: The return type performs unqualified name lookup into `List` for // `Iterator`. fn List.Iterate() -> Iterator { ... } ``` ### Nominal data classes We will mark [data classes](#data-classes) with an `impl as Data {}` line. ``` class TextLabel { var x: i32; var y: i32; var text: String; // This line makes `TextLabel` a data class, which defines // a number of operations field-wise. impl as Data {} } ``` The fields of data classes must all be public. That line will add [field-wise implementations and operations of all interfaces that a struct with the same fields would get by default](#operations-performed-field-wise). The word `Data` here refers to an empty interface in the Carbon prologue. That interface would then be part of our [strategy for defining how other interfaces are implemented for data classes](#interfaces-implemented-for-data-classes). **References:** Rationale for this approach is given in proposal [#722](/proposals/p0722.md#nominal-data-class). ### Member type Additional types may be defined in the scope of a class definition. ``` class StringCounts { class Node { var key: String; var count: i32; } var counts: Vector(Node); } ``` The inner type is a member of the type, and is given the name `StringCounts.Node`. This case is called a _member class_ since the type is a class, but other kinds of type declarations, like choice types, are allowed. ### Let Other type constants can be defined using a `let` declaration: ``` class MyClass { let Pi:! f32 = 3.141592653589793; let IndexType:! type = i32; } ``` The `:!` indicates that this is defining a compile-time constant, and so does not affect the storage of instances of that class. ### Alias You may declare aliases of the names of class members. This is to allow them to be renamed in multiple steps or support alternate names. ``` class StringPair { var key: String; var value: String; alias first = key; alias second = value; } var sp1: StringPair = {.key = "K", .value = "1"}; var sp2: StringPair = {.first = "K", .second = "2"}; Assert(sp1.first == sp2.key); Assert(&sp1.first == &sp1.key); ``` **Future work:** This needs to be connected to the broader design of aliases, once that lands. ### Inheritance Carbon supports [inheritance]() using a [class hierarchy](), on an opt-in basis. Classes by default are [_final_](), which means they may not be extended. To declare a class as allowing extension, use either the `base class` or `abstract class` introducer: ``` base class MyBaseClass { ... } ``` A _base class_ may be _extended_ to get a _derived class_: ``` base class MiddleDerived { extend base: MyBaseClass; ... } class FinalDerived { extend base: MiddleDerived; ... } // ❌ Forbidden: class Illegal { extend base: FinalDerived; ... } // may not extend `FinalDerived` since not declared `base` or `abstract`. ``` An _[abstract class](https://en.wikipedia.org/wiki/Abstract_type)_ or _abstract base class_ is a base class that may not be instantiated. ``` abstract class MyAbstractClass { ... } // ❌ Forbidden: var a: MyAbstractClass = ...; ``` **Future work:** For now, the Carbon design only supports single inheritance. In the future, Carbon will support multiple inheritance with limitations on all base classes except the one listed first. **Terminology:** We say `MiddleDerived` and `FinalDerived` are _derived classes_, transitively extending or _derived from_ `MyBaseClass`. Similarly `FinalDerived` is derived from or extends `MiddleDerived`. `MiddleDerived` is `FinalDerived`'s _immediate base class_, and both `MiddleDerived` and `MyBaseClass` are base classes of `FinalDerived`. Base classes that are not abstract are called _extensible classes_. A derived class has all the members of the class it extends, including data members and methods, though it may not be able to access them if they were declared `private`. #### Virtual methods A base class may define [virtual methods](https://en.wikipedia.org/wiki/Virtual_function). These are methods whose implementation may be overridden in a derived class. Only methods defined in the scope of the class definition may be virtual, not any defined in [out-of-line interface `impl` declarations](/docs/design/generics/details.md#out-of-line-impl). Interface methods may be implemented using virtual methods when the [impl is inline](/docs/design/generics/details.md#inline-impl), and calls to those methods by way of the interface will do virtual dispatch just like a direct call to the method does. [Class functions](#class-functions) may not be declared virtual. Neither may functions with [compile-time parameters](/docs/design/generics/overview.md), whether those are `template` or checked, explicit or deduced. Compile-time parameters on the enclosing scope are allowed, though, so generic classes may have virtual methods. ##### Virtual modifier keywords A method is declared as virtual by using a _virtual modifier keyword_ in its declaration before `fn`. ``` base class MyBaseClass { virtual fn Overridable[self: Self]() -> i32 { return 7; } } ``` This matches C++, and makes it relatively easy for authors of derived classes to find the functions that can be overridden. If no keyword is specified, the default for methods is that they are _non-virtual_. This means: - they can't override methods in bases of this class; - they can't be overridden in derived classes; and - they have an implementation in the current class, and that implementation must work for all derived classes. There are three virtual modifier keywords: - `virtual` - This marks a method as not present in bases of this class and having an implementation in this class. That implementation may be overridden in derived classes. - `abstract` - This marks a method that must be overridden in a derived class since it has no implementation in this class. This is short for "abstract virtual" but is called ["pure virtual" in C++](https://en.wikipedia.org/wiki/Virtual_function#Abstract_classes_and_pure_virtual_functions). Only abstract classes may have unimplemented abstract methods. - `override` - This marks a method that overrides a method marked `virtual` or `abstract` in the base class with an implementation specific to -- and defined within -- this class. The method is still virtual and may be overridden again in subsequent derived classes if this is a base class. See [method overriding in Wikipedia](https://en.wikipedia.org/wiki/Method_overriding). Requiring a keyword when overriding allows the compiler to diagnose when the derived class accidentally uses the wrong signature or spelling and so doesn't match the base class. | Keyword on
method in `C` | Allowed in
`abstract class C` | Allowed in
`base class C` | Allowed in
final `class C` | in `B` where
`C` extends `B` | in `D` where
`D` extends `C` | | ----------------------------- | ---------------------------------- | ------------------------------ | ------------------------------- | ------------------------------------------------------------ | ------------------------------------------------------------------------------------ | | `virtual` | ✅ | ✅ | ❌ | _not present_ | `abstract`
`override`
_not mentioned_ | | `abstract` | ✅ | ❌ | ❌ | _not present_
`virtual`
`abstract`
`override` | `abstract`
`override`
_may not be
mentioned if
`D` is not final_ | | `override` | ✅ | ✅ | ✅ | `virtual`
`abstract`
`override` | `abstract`
`override` | Since validating a method with a virtual modifier keyword involves looking for methods with the same name in the base class, virtual methods must be declared after the `extend base` declaration when present in a class definition. This simplifies the compiler, and follows the [information accumulation principle](/docs/project/principles/information_accumulation.md). #### Subtyping A pointer to a base class, like `MyBaseClass*` is actually considered to be a pointer to that type or any derived class, like `MiddleDerived` or `FinalDerived`. This means that a `FinalDerived*` value may be implicitly cast to type `MiddleDerived*` or `MyBaseClass*`. This is accomplished by making the data layout of a type extending `MyBaseClass` have `MyBaseClass` as a prefix. In addition, the first class in the inheritance chain with a virtual method will include a virtual pointer, or _vptr_, pointing to a [virtual method table](https://en.wikipedia.org/wiki/Virtual_method_table), or _vtable_. Any calls to virtual methods will perform [dynamic dispatch](https://en.wikipedia.org/wiki/Dynamic_dispatch) by calling the method using the function pointer in the vtable, to get the overridden implementation from the most derived class that implements the method. This data layout is reflected in the order of declarations in a class definition. An `extend base` declaration, when present in a class definition, must appear before any other declarations adding data to the class instances, such as instance variables. Since a final class may not be extended, the compiler can bypass the vtable and use [static dispatch](https://en.wikipedia.org/wiki/Static_dispatch). In general, you can use a combination of an abstract base class and a final class instead of an extensible class if you need to distinguish between "exactly a type" and "possibly a subtype." ``` base class Extensible { ... } // Can be replaced by: abstract class ExtensibleBase { ... } class ExactlyExtensible { extend base: ExtensibleBase; ... } ``` #### `Self` refers to the current type Note that `Self` in a class definition means "the current type being defined" not "the type implementing this method." To implement a method in a derived class that uses `Self` in the declaration in the base class, only the type of `self` should change: ``` base class B1 { virtual fn F[self: Self](x: Self) -> Self; // Means exactly the same thing as: // virtual fn F[self: B1](x: B1) -> B1; } class D1 { extend base: B1; // ❌ Illegal: // override fn F[self: Self](x: Self) -> Self; // since that would mean the same thing as: // override fn F[self: Self](x: D1) -> D1; // and `D1` is a different type than `B1`. // ✅ Allowed: Parameter and return types // of `F` match declaration in `B1`. override fn F[self: Self](x: B1) -> B1; // Or: override fn F[self: D1](x: B1) -> B1; } ``` The exception is when there is a [subtyping relationship](#subtyping) such that it would be legal for a caller using the base classes signature to actually be calling the derived implementation, as in: ``` base class B2 { virtual fn Clone[self: Self]() -> Self*; // Means exactly the same thing as: // virtual fn Clone[self: B2]() -> B2*; } class D2 { extend base: B2; // ✅ Allowed override fn Clone[self: Self]() -> Self*; // Means the same thing as: // override fn Clone[self: D2]() -> D2*; // which is allowed since `D2*` is a // subtype of `B2*`. } ``` #### Constructors Like for classes without inheritance, constructors for a derived class are ordinary functions that return an instance of the derived class. Generally constructor functions should return the constructed value without copying, as in proposal [#257: Initialization of memory and variables](https://github.com/carbon-language/carbon-lang/pull/257). This means either [creating the object in the return statement itself](/proposals/p0257.md#function-returns-and-initialization), or in [a `returned var` declaration](/proposals/p0257.md#declared-returned-variable). As before, instances can be created by casting a struct value into the class type, this time with a `.base` member to initialize the members of the immediate base type. ``` class MyDerivedType { extend base: MyBaseType; fn Make() -> MyDerivedType { return {.base = MyBaseType.Make(), .derived_field = ...}; } } ``` There are two cases that aren't well supported with this pattern: - Users cannot create a value of an abstract class, which is necessary when it has private fields or otherwise requires initialization. - Users may want to reduce the chance of mistakes from calling a method on a partially constructed object. Of particular concern is calling a virtual method prior to forming the derived class and so it uses the base class implementation. While expected to be relatively rarely needed, we will address both of these concerns with a specialized type just used during construction of base classes, called the partial class type for the class. ##### Partial class type The partial class type for a base class type like `MyBaseType` is written `partial MyBaseType`. - Only methods that take the partial class type may be called on the partial class type, so methods have to opt in to being called on an object that isn't fully constructed. - No virtual methods may take the partial class type, so there is no way to transitively call a virtual method on an object that isn't fully constructed. - `partial MyBaseClass` and `MyBaseClass` have the same fields in the same order with the same data layout. The only difference is that `partial MyBaseClass` doesn't use (look into) its hidden vptr slot. To reliably catch any bugs where virtual function calls occur in this state, both fast and hardened release builds will initialize the hidden vptr slot to a null pointer. Debug builds will initialize it to an alternate vtable whose functions will abort the program with a clear diagnostic. - Since `partial MyBaseClass` has the same data layout but only uses a subset, there is a subtyping relationship between these types. A `MyBaseClass` value is a `partial MyBaseClass` value, but not the other way around. So you can cast `MyBaseClass*` to `partial MyBaseClass*`, but the other direction is not safe. - When `MyBaseClass` may be instantiated, there is a conversion from `partial MyBaseClass` to `MyBaseClass`. It changes the value by filling in the hidden vptr slot. If `MyBaseClass` is abstract, then attempting that conversion is an error. - `partial MyBaseClass` is considered final. This is despite the fact that from a data layout perspective, `partial MyDerivedClass` will have `partial MyBaseClass` as a prefix if `MyDerivedClass` extends `MyBaseClass`. The type `partial MyBaseClass` specifically means "exactly this and no more." This means we don't need to look at the hidden vptr slot, and we can instantiate it even if it doesn't have a virtual [destructor](#destructors). - The keyword `partial` is only valid for a `base` or `abstract` class. For final classes, there is no need for a second type. ##### Usage The general pattern is that base classes can define constructors returning the partial class type. ``` base class MyBaseClass { fn Make() -> partial Self { return {.base_field_1 = ..., .base_field_2 = ...}; } // ... } ``` Extensible classes can be instantiated even from a partial class type value: ``` var mbc: MyBaseClass = MyBaseClass.Make(); ``` The conversion from `partial MyBaseClass` to `MyBaseClass` only fills in the vptr value and can be done in place. After the conversion, all public methods may be called, including virtual methods. The partial class type is required for abstract classes, since otherwise they may not be instantiated. Constructor functions for abstract classes should be marked [protected](#protected-access) so they may only be accessed in derived classes. ``` abstract class MyAbstractClass { protected fn Make() -> partial Self { return {.base_field_1 = ..., .base_field_2 = ...}; } // ... } // ❌ Error: can't instantiate abstract class var abc: MyAbstractClass = ...; ``` If a base class wants to store a pointer to itself somewhere in the constructor function, there are two choices: - An extensible class could use the plain type instead of the partial class type. ``` base class MyBaseClass { fn Make() -> Self { returned var result: Self = {...}; StoreMyPointerSomewhere(&result); return var; } } ``` - The other choice is to explicitly cast the type of its address. This pointer should not be used to call any virtual method until the object is finished being constructed, since the vptr will be null. ``` abstract class MyAbstractClass { protected fn Make() -> partial Self { returned var result: partial Self = {...}; // Careful! Pointer to object that isn't fully constructed! StoreMyPointerSomewhere(&result as Self*); return var; } } ``` The constructor for a derived class may construct values from a partial class type of the class' immediate base type or the full type: ``` abstract class MyAbstractClass { protected fn Make() -> partial Self { ... } } // Base class returns a partial type base class Derived { extend base: MyAbstractClass; protected fn Make() -> partial Self { return {.base = MyAbstractClass.Make(), .derived_field = ...}; } ... } base class MyBaseClass { fn Make() -> Self { ... } } // Base class returns a full type base class ExtensibleDerived { extend base: MyBaseClass; fn Make() -> Self { return {.base = MyBaseClass.Make(), .derived_field = ...}; } ... } ``` And final classes will return a type that does not use the partial class type: ``` class FinalDerived { extend base: MiddleDerived; fn Make() -> Self { return {.base = MiddleDerived.Make(), .derived_field = ...}; } ... } ``` Observe that the vptr is only assigned twice in release builds if you use partial class types: - The first class value created, by the factory function creating the base of the class hierarchy, initialized the vptr field to nullptr. Every derived type transitively created from that value will leave it alone. - Only when the value has its most-derived class and is converted from the partial class type to its final type is the vptr field set to its final value. In the case that the base class can be instantiated, tooling could optionally recommend that functions returning `Self` that are used to initialize a derived class be changed to return `partial Self` instead. However, the consequences of returning `Self` instead of `partial Self` when the value will be used to initialize a derived class are fairly minor: - The vptr field will be assigned more than necessary. - The types won't protect against calling methods on a value while it is being constructed, much like the situation in C++ currently. #### Assignment with inheritance Since the assignment operator method should not be virtual, it is only safe to implement it for final types. However, following the [maxim that Carbon should "focus on encouraging appropriate usage of features rather than restricting misuse"](/docs/project/goals.md#code-that-is-easy-to-read-understand-and-write), we allow users to also implement assignment on extensible classes, even though it can lead to [slicing](https://en.wikipedia.org/wiki/Object_slicing). ### Destructors Every non-abstract type is _destructible_, meaning has a defined destructor method called when the lifetime of a value of that type ends, such as when a variable goes out of scope. The destructor for a class may be customized using the `destroy` method: ```carbon class MyClass { fn destroy[self: Self]() { ... } } ``` or: ```carbon class MyClass { // Can modify `self` in the body. fn destroy[ref self: Self]() { ... } } ``` If a class has no `destroy` method, it gets the default destructor, which is equivalent to `fn destroy[self: Self] { }`. The destructor for a class is run before the destructors of its data members. The data members are destroyed in reverse order of declaration. Derived classes are destroyed before their base classes, so the order of operations is: - derived class' destructor runs, - the data members of the derived class are destroyed, in reverse order of declaration, - the immediate base class' destructor runs, - the data members of the immediate base class are destroyed, in reverse order of declaration, - and so on. Destructors may be declared in class scope and then defined out-of-line: ```carbon class MyClass { fn destroy[ref self: Self](); } fn MyClass.destroy[ref self: Self]() { ... } ``` It is illegal to delete an instance of a derived class through a pointer to one of its base classes unless it has a [virtual destructor](https://en.wikipedia.org/wiki/Virtual_function#Virtual_destructors). An abstract or base class' destructor may be declared virtual using the `virtual` introducer, in which case any derived class destructor declaration must be `override`: ```carbon base class MyBaseClass { virtual fn destroy[ref self: Self]() { ... } } class MyDerivedClass { extend base: MyBaseClass; override fn destroy[ref self: Self]() { ... } } ``` The properties of a type, whether type is abstract, base, or final, and whether the destructor is virtual or non-virtual, determines which [facet types](/docs/design/generics/terminology.md#facet-type) it satisfies. - Non-abstract classes are `Concrete`. This means you can create local and member variables of this type. `Concrete` types have destructors that are called when the local variable goes out of scope or the containing object of the member variable is destroyed. - Final classes and classes with a virtual destructor are `Deletable`. These may be safely deleted through a pointer. - Classes that are `Concrete`, `Deletable`, or both are `Destructible`. These are types that may be deleted through a pointer, but it might not be safe. The concerning situation is when you have a pointer to a base class without a virtual destructor. It is unsafe to delete that pointer when it is actually pointing to a derived class. **Note:** The names `Deletable` and `Destructible` are [**placeholders**](/proposals/p1154.md#type-of-type-naming) since they do not conform to the decision on [question-for-leads issue #1058: "How should interfaces for core functionality be named?"](https://github.com/carbon-language/carbon-lang/issues/1058). | Class | Destructor | `Concrete` | `Deletable` | `Destructible` | | -------- | ----------- | ---------- | ----------- | -------------- | | abstract | non-virtual | no | no | no | | abstract | virtual | no | yes | yes | | base | non-virtual | yes | no | yes | | base | virtual | yes | yes | yes | | final | any | yes | yes | yes | The compiler automatically determines which of these [facet types](/docs/design/generics/terminology.md#facet-type) a given type satisfies. It is illegal to directly implement `Concrete`, `Deletable`, or `Destructible`. For more about these constraints, see ["destructor constraints" in the detailed generics design](/docs/design/generics/details.md#destructor-constraints). A pointer to `Deletable` types may be passed to the `Delete` method of the `Allocator` [interface](/docs/design/generics/terminology.md#interface). To deallocate a pointer to a base class without a virtual destructor, which may only be done when it is not actually pointing to a value with a derived type, call the `UnsafeDelete` method instead. Note that you may not call `UnsafeDelete` on abstract types without virtual destructors, it requires `Destructible`. ``` interface Allocator { // ... fn Delete[T:! Deletable, ref self: Self](p: T*); fn UnsafeDelete[T:! Destructible, ref self: Self](p: T*); } ``` To pass a pointer to a base class without a virtual destructor to a checked-generic function expecting a `Deletable` type, use the `UnsafeAllowDelete` [type adapter](/docs/design/generics/details.md#adapting-types). ``` class UnsafeAllowDelete(T:! Concrete) { extend adapt T; impl as Deletable {} } // Example usage: fn RequiresDeletable[T:! Deletable](p: T*); var x: MyExtensible; RequiresDeletable(&x as UnsafeAllowDelete(MyExtensible)*); ``` If a virtual method is transitively called from inside a destructor, the implementation from the current class is used, not any overrides from derived classes. It will abort the execution of the program if that method is abstract and not implemented in the current class. **Future work:** Allow or require destructors to be declared as taking `partial Self` in order to prove no use of virtual methods. Types satisfy the [`TrivialDestructor`](/docs/design/generics/details.md#destructor-constraints) facet type if: - the class declaration does not define a destructor or the class defines the destructor with an empty body `{ }`, - all data members implement `TrivialDestructor`, and - all base classes implement `TrivialDestructor`. For example, a [struct type](#struct-types) implements `TrivialDestructor` if all its members do. `TrivialDestructor` implies that their destructor does nothing, which may be used to generate optimized specializations. There is no provision for handling failure in a destructor. All operations that could potentially fail must be performed before the destructor is called. Unhandled failure during a destructor call will abort the program. **Future work:** Allow or require destructors to be declared as taking `[var self: Self]`. **Alternatives considered:** - [Types implement destructor interface](/proposals/p1154.md#types-implement-destructor-interface) - [Prevent virtual function calls in destructors](/proposals/p1154.md#prevent-virtual-function-calls-in-destructors) - [Allow functions to act as destructors](/proposals/p1154.md#allow-functions-to-act-as-destructors) - [Allow private destructors](/proposals/p1154.md#allow-private-destructors) - [Allow multiple conditional destructors](/proposals/p1154.md#allow-multiple-conditional-destructors) - [Don't distinguish safe and unsafe delete operations](/proposals/p1154.md#dont-distinguish-safe-and-unsafe-delete-operations) - [Don't allow unsafe delete](/proposals/p1154.md#dont-allow-unsafe-delete) - [Allow final destructors](/proposals/p1154.md#allow-final-destructors) ### Access control By default, all members of a class are fully publicly accessible. Access can be restricted by adding a keyword, called an [access modifier](https://en.wikipedia.org/wiki/Access_modifiers), prior to the declaration. Access modifiers are how Carbon supports [encapsulation](#encapsulated-types). The [access modifier](https://en.wikipedia.org/wiki/Access_modifiers) is written before any [virtual modifier keyword](#virtual-modifier-keywords). **Rationale:** Carbon makes members public by default for a few reasons: - The readability of public members is the most important, since we expect most readers to be concerned with the public API of a type. - The members that are most commonly private are the data fields, which have relatively less complicated definitions that suffer less from the extra annotation. Additionally, there is precedent for this approach in modern object-oriented languages such as [Kotlin](https://kotlinlang.org/docs/visibility-modifiers.html) and [Python](https://docs.python.org/3/tutorial/classes.html), both of which are well regarded for their usability. Keywords controlling visibility are attached to individual declarations instead of C++'s approach of labels controlling the visibility for all following declarations to [reduce context sensitivity](/docs/project/principles/low_context_sensitivity.md). This matches [Rust](https://doc.rust-lang.org/reference/visibility-and-privacy.html), [Swift](https://docs.swift.org/swift-book/LanguageGuide/AccessControl.html), [Java](http://rosettacode.org/wiki/Classes#Java), [C#](https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/classes-and-structs/access-modifiers), [Kotlin](https://kotlinlang.org/docs/visibility-modifiers.html#classes-and-interfaces), and [D](https://wiki.dlang.org/Access_specifiers_and_visibility). **References:** Proposal [#561: Basic classes](https://github.com/carbon-language/carbon-lang/pull/561) included the decision that [members default to publicly accessible](/proposals/p0561.md#access-control) originally asked in issue [#665](https://github.com/carbon-language/carbon-lang/issues/665). #### Private access As in C++, `private` means only accessible to members of the class and any [friends](#friends). ```carbon class Point { fn Distance[self: Self]() -> f32; // These are only accessible to members of `Point`. private var x: f32; private var y: f32; } ``` A `private virtual` or `private abstract` method may be implemented in derived classes, even though it may not be called. This allows derived classes to customize the behavior of a function called by a method of the base class, while still preventing the derived class from calling it. This matches the behavior of C++ and is more orthogonal. **Future work:** `private` will give the member internal linkage unless it needs to be external because it is used in an inline method or template. We may in the future [add a way to specify internal linkage explicitly](/proposals/p0722.md#specifying-linkage-as-part-of-the-access-modifier). **Open questions:** Using `private` to mean "restricted to this class" matches C++. Other languages support restricting to different scopes: - Swift supports "restrict to this module" and "restrict to this file". - Rust supports "restrict to this module and any children of this module", as well as "restrict to this crate", "restrict to parent module", and "restrict to a specific ancestor module". **Comparison to other languages:** C++, Rust, and Swift all make class members private by default. C++ offers the `struct` keyword that makes members public by default. #### Protected access Protected members may only be accessed by members of this class, members of derived classes, and any [friends](#friends). ``` base class MyBaseClass { protected fn HelperClassFunction(x: i32) -> i32; protected fn HelperMethod[self: Self](x: i32) -> i32; protected var data: i32; } class MyDerivedClass { extend base: MyBaseClass; fn UsesProtected[ref self: Self]() { // Can access protected members in derived class var x: i32 = HelperClassFunction(3); self.data = self.HelperMethod(x); } } ``` #### Friends Classes may have a _friend_ declaration: ``` class Buddy { ... } class Pal { private var x: i32; friend Buddy; } ``` This declares `Buddy` to be a friend of `Pal`, which means that `Buddy` can access all members of this class, even the ones that are declared `private` or `protected`. The `friend` keyword is followed by the name of an existing function, type, or parameterized family of types. Unlike C++, it won't act as a forward declaration of that name. The name must be resolvable by the compiler, and so may not be a member of a template. #### Test friendship **Future work:** There should be a convenient way of allowing tests in the same library as the class definition to access private members of the class. Ideally this could be done without changing the class definition itself, since it doesn't affect the class' public API. #### Access control for construction A function may construct a class, by casting a struct value to the class type, if it has access to (write) all of its fields. **Future work:** There should be a way to limit which code can construct a class even when it only has public fields. This will be resolved in question-for-leads issue [#803](https://github.com/carbon-language/carbon-lang/issues/803). ### Operator overloading Developers may define how standard Carbon operators, such as `+` and `/`, apply to custom types by implementing the [interface](generics/terminology.md#interface) that corresponds to that operator for the types of the operands. See the ["operator overloading" section](generics/details.md#operator-overloading) of the [generics design](generics/overview.md). The specific interface used for a given operator may be found in the [expressions design](/docs/design/expressions/README.md). ## Future work This includes features that need to be designed, questions to answer, and a description of the provisional syntax in use until these decisions have been made. ### Struct literal shortcut We could allow you to write `{x, y}` as a short hand for `{.x = x, .y = y}`. ### Optional named parameters Structs are being considered as a possible mechanism for implementing optional named parameters. We have three main candidate approaches: allowing struct types to have field defaults, having dedicated support for destructuring struct values in pattern contexts, or having a dedicated optional named parameter syntax. #### Field defaults for struct types If struct types could have field defaults, you could write a function declaration with all of the optional parameters in an option struct: ``` fn SortIntVector( v: Vector(i32)*, options: {.stable: bool = false, .descending: bool = false} = {}) { // Code using `options.stable` and `options.descending`. } // Uses defaults of `.stable` and `.descending` equal to `false`. SortIntVector(&v); SortIntVector(&v, {}); // Sets `.stable` option to `true`. SortIntVector(&v, {.stable = true}); // Sets `.descending` option to `true`. SortIntVector(&v, {.descending = true}); // Sets both `.stable` and `.descending` options to `true`. SortIntVector(&v, {.stable = true, .descending = true}); // Order can be different for arguments as well. SortIntVector(&v, {.descending = true, .stable = true}); ``` #### Destructuring in pattern matching We might instead support destructuring struct patterns with defaults: ``` fn SortIntVector( v: Vector(i32)*, {stable: bool = false, descending: bool = false}) { // Code using `stable` and `descending`. } ``` This would allow the same syntax at the call site, but avoids [some concerns with field defaults](https://github.com/carbon-language/carbon-lang/pull/561#discussion_r683856715) and allows some other use cases such as destructuring return values. #### Discussion We might support destructuring directly: ``` var {key: String, value: i32} = ReturnKeyValue(); ``` or by way of a mechanism that converts a struct into a tuple: ``` var (key: String, value: i32) = ReturnKeyValue().extract(.key, .value); // or maybe: var (key: String, value: i32) = ReturnKeyValue()[(.key, .value)]; ``` Similarly we might support optional named parameters directly instead of by way of struct types. Some discussion on this topic has occurred in: - [question-for-leads issue #505 on named parameters](https://github.com/carbon-language/carbon-lang/issues/505) - labeled params brainstorming docs [1](https://docs.google.com/document/d/1Ui2OEHLwa9LZ6ktc1joJqE7_N-ZHX2gBvBpaFw6DUy8/edit?usp=sharing&resourcekey=0-6bEnyc03QePVcttPRSFoew), [2](https://docs.google.com/document/d/1kK_tti4DwPqa3Oh5CgA5pWSx0g3bKlZG1yREMpq9uiU/edit?usp=sharing&resourcekey=0-oFV6tXtCVu1bcHz4oCMyMQ) - ["match" in syntax choices doc](https://docs.google.com/document/d/1EhZA3AlY9TaCMho9jz2ynFxK-6eS6BwMAkE5jNYQzEA/edit?usp=sharing&resourcekey=0-QXEoh-b4_sQG2u636gIa1A#heading=h.y566d16ivoy2) ### Inheritance #### C++ abstract base classes interoperating with object-safe interfaces We want four things so that Carbon's object-safe interfaces may interoperate with C++ abstract base classes without data members, matching the [interface as base class use case](#interface-as-base-class): - Ability to convert an object-safe interface (a facet type) into an C++-compatible base class (a base type), maybe using `AsBaseClass(MyInterface)`. - Ability to convert a C++ base class without data members (a base type) into an object-safe interface (a facet type), maybe using `AsInterface(MyIBC)`. - Ability to convert a (thin) pointer to an abstract base class to a `DynPtr` of the corresponding interface. - Ability to convert `DynPtr(MyInterface)` values to a proxy type that extends the corresponding base class `AsBaseType(MyInterface)`. Note that the proxy type extending `AsBaseType(MyInterface)` would be a different type than `DynPtr(MyInterface)` since the receiver input to the function members of the vtable for the former does not match those in the witness table for the latter. #### Overloaded methods We allow a derived class to define a [class function](#class-functions) with the same name as a class function in the base class. For example, we expect it to be pretty common to have a constructor function named `Create` at all levels of the type hierarchy. Beyond that, we may want some rules or restrictions about defining methods in a derived class with the same name as a base class method without overriding it. There are some opportunities to improve on and simplify the C++ story: - We don't want to silently hide methods in the base class because of a method with the same name in a derived class. There are uses for this in C++, but it also causes problems and without multiple inheritance there isn't the same need in Carbon. - Overload resolution should happen before virtual dispatch. - For evolution purposes, you should be able to add private members to a base class that have the same name as member of a derived class without affecting overload resolution on instances of the derived class, in functions that aren't friends of the base class. **References:** This was discussed in [the open discussion on 2021-07-12](https://docs.google.com/document/d/1TvHK6HWAcCtnseMpcNsLYrgdtNy9qNrv6EaY9n063ok/edit#heading=h.40jlsrcgp8mr). #### Interop with C++ inheritance This design directly supports Carbon classes inheriting from a single C++ class. ``` class CarbonClass { extend base: Cpp.CPlusPlusClass; fn Make() -> Self { return {.base = Cpp.CPlusPlusClass(...), .other_fields = ...}; } ... } ``` To allow C++ classes to extend Carbon classes, there needs to be some way for C++ constructors to initialize their base class: - There could be some way to export a Carbon class that identifies which factory functions may be used as constructors. - We could explicitly call the Carbon factory function, as in: ``` // `Base` is a Carbon class which gets converted to a // C++ class for interop purposes: class Base { public: virtual ~Base() {} static auto Make() -> Base; }; // In C++ class Derived : public Base { public: virtual ~Derived() override {} // This isn't currently a case where C++ guarantees no copy, // and so it currently still requires a notional copy and // there appear to be implementation challenges with // removing them. This may require an extension to make work // reliably without an extraneous copy of the base subobject. Derived() : Base(Base::Make()) {} }; ``` However, this doesn't work in the case where `Base` can't be instantiated, or `Base` does not have a copy constructor, even though it shouldn't be called due to RVO. ##### Virtual base classes TODO: Ask zygoloid to fill this in. Carbon won't support declaring virtual base classes, and the C++ interop use cases Carbon needs to support are limited. This will allow us to simplify the C++ interop by allowing Carbon to delegate initialization of virtual base classes to the C++ side. This requires that we enforce two rules: - No multiple inheritance of C++ classes with virtual bases - No C++ class extending a Carbon class that extends a C++ class with a virtual base ### Mixins We will need some way to declare mixins. This syntax will need a way to distinguish defining versus requiring member variables. Methods may additionally be given a default definition but may be overridden. Interface implementations may only be partially provided by a mixin. Mixin methods will need to be able to convert between pointers to the mixin type and the main type. Open questions include whether a mixin is its own type that is a member of the containing type, and whether mixins are templated on the containing type. Mixins also complicate how constructors work. ### Memory layout Carbon will need some way for users to specify the memory layout of class types beyond simple ordering of fields, such as controlling the packing and alignment for the whole type or individual members. We may allow members of a derived class like to put data members in the final padding of its base class prefix. Tail-padding reuse has both advantages and disadvantages, so we may have some way for a class to explicitly mark that its tail padding is available for use by a derived class, Advantages: - Tail-padding reuse is sometimes a nice layout optimization (eg, in Clang we save 8 bytes per `Expr` by reusing tail padding). - No class size regressions when migrating from C++. - Special case of reusing the tail padding of a class that is empty other than its tail padding is very important, to the extent that we will likely need to support either zero-sized types or tail-padding reuse in order to have acceptable class layouts. Disadvantages: - Cannot use `memcpy(p, q, sizeof(Base))` to copy around base class subobjects if the destination is an in-lifetime, because they might overlap other objects' representations. - Somewhat more complex model. - We need some mechanism for disabling tail-padding reuse in "standard layout" types. - We may also have to use narrowed loads for the last member of a base class to avoid accidentally creating a race condition. However, we can still use `memcpy` and `memset` to initialize a base class subobject, even if its tail padding might be reused, so long as we guarantee that no other object lives in the tail padding and is initialized before the base class. In C++, that happens only due to virtual base classes getting initialized early and laid out at the end of the object; if we disallow virtual base classes then we can guarantee that initialization order is address order, removing most of the downside of tail-padding reuse. ### No `static` variables At the moment, there is no proposal to support [`static` member variables](https://en.wikipedia.org/wiki/Class_variable#Static_member_variables_and_static_member_functions), in line with avoiding global variables more generally. Carbon may need some support in this area, though, for parity with and migration from C++. ### Computed properties Carbon might want to support members of a type that are accessed like a data member but return a computed value like a function. This has a number of implications: - It would be a way of publicly exposing data members for [encapsulated types](#encapsulated-types), allowing for rules that otherwise forbid mixing public and private data members. - It would provide a more graceful evolution path from a [data class](#data-classes) to an [encapsulated type](#encapsulated-types). - It would give an option to start with a [data class](#data-classes) instead of writing all the boilerplate to create an [encapsulated type](#encapsulated-types) preemptively to allow future evolution. - It would let you take a variable away and put a property in its place with no other code changes. The number one use for this is so you can put a breakpoint in the property code, then later go back to public variable once you understand who was misbehaving. - We should have some guidance for when to use a computed property instead of a function with no arguments. One possible criteria is when it is a pure function of the state of the object and executes in an amount of time similar to ordinary member access. However, there are likely to be differences between computed properties and other data members, such as the ability to take the address of them. We might want to support "read only" data members, that can be read through the public API but only modified with private access, for data members which may need to evolve into a computed property. There are also questions regarding how to support assigning or modifying computed properties, such as using `+=`. ### Interfaces implemented for data classes We should define a way for defining implementations of interfaces for struct types. To satisfy coherence, these implementations would have to be defined in the library with the interface definition. The syntax might look like: ``` interface ConstructWidgetFrom { fn Construct(Self) -> Widget; } impl {.kind: WidgetKind, .size: i32} as ConstructWidgetFrom { ... } ``` In addition, we should define a way for interfaces to define templated blanket implementations for [data classes](#data-classes) more generally. These implementations will typically subject to the criteria that all the data fields of the type must implement the interface. An example use case would be to say that a data class is serializable if all of its fields were. For this we will need a facet type for capturing that criteria, maybe something like `DataFieldsImplement(MyInterface)`. The templated implementation will need some way of iterating through the fields so it can perform operations fieldwise. This feature should also implement the interfaces for any tuples whose fields satisfy the criteria. It is an open question how to define implementations for binary operators. For example, if `i32` is comparable to `f64`, then `{.x = 3, .y = 2.72}` should be comparable to `{.x = 3.14, .y = 2}`. The trick is how to declare the criteria that "`T` is comparable to `U` if they have the same field names in the same order, and for every field `x`, the type of `T.x` implements `ComparableTo` for the type of `U.x`." ## Alternatives considered - [#257: Initialization of memory and variables](https://github.com/carbon-language/carbon-lang/pull/257) - [Require compile-time-proven initialization](/proposals/p0257.md#require-compile-time-proven-initialization) - [C and C++ uninitialized](/proposals/p0257.md#c-and-c-uninitialized) - [Allow passing unformed objects to parameters or returning them?](/proposals/p0257.md#allow-passing-unformed-objects-to-parameters-or-returning-them) - [Allow assigning an unformed object to another unformed object?](/proposals/p0257.md#allow-assigning-an-unformed-object-to-another-unformed-object) - [Fully destructive move (Rust)](/proposals/p0257.md#fully-destructive-move-rust) - [Completely non-destructive move (C++)](/proposals/p0257.md#completely-non-destructive-move-c) - [Named return variable in place of a return type](/proposals/p0257.md#named-return-variable-in-place-of-a-return-type) - [Allow unformed members](/proposals/p0257.md#allow-unformed-members) - [#561: Basic classes: use cases, struct literals, struct types, and future work](https://github.com/carbon-language/carbon-lang/pull/561) - [Early proposal #98](https://github.com/carbon-language/carbon-lang/pull/98) - [Interfaces implemented for anonymous data classes](/proposals/p0561.md#interfaces-implemented-for-anonymous-data-classes) - [Access control](/proposals/p0561.md#access-control) - [Introducer for structural data class types](/proposals/p0561.md#introducer-for-structural-data-class-types) - [Terminology](/proposals/p0561.md#terminology) - [#722: Nominal classes and methods](https://github.com/carbon-language/carbon-lang/pull/722) - [Method syntax](/proposals/p0722.md#method-syntax) - [Marking mutating methods at the call site](/proposals/p0722.md#marking-mutating-methods-at-the-call-site) - [Differences between functions and methods](/proposals/p0722.md#differences-between-functions-and-methods) - [Specifying linkage as part of the access modifier](/proposals/p0722.md#specifying-linkage-as-part-of-the-access-modifier) - [Nominal data class](/proposals/p0722.md#nominal-data-class) - [Let constants](/proposals/p0722.md#let-constants) - [#777: Inheritance](https://github.com/carbon-language/carbon-lang/pull/777) - [Classes are final by default](/proposals/p0777.md#classes-are-final-by-default) - [Allow keywords to be written when they would have no effect](/proposals/p0777.md#allow-keywords-to-be-written-when-they-would-have-no-effect) - [Different virtual override keywords](/proposals/p0777.md#different-virtual-override-keywords) - [Different virtual override keyword placement](/proposals/p0777.md#different-virtual-override-keyword-placement) - [Final methods](/proposals/p0777.md#final-methods) - [Constructors](/proposals/p0777.md#constructors) - [Implicit abstract classes](/proposals/p0777.md#implicit-abstract-classes) - [No extensible objects with non-virtual destructors](/proposals/p0777.md#no-extensible-objects-with-non-virtual-destructors) - [Separate "exact" and "or derived" variations on types](/proposals/p0777.md#separate-exact-and-or-derived-variations-on-types) - [Separate "exact" and "or derived" variations on pointers](/proposals/p0777.md#separate-exact-and-or-derived-variations-on-pointers) - [#875: Principle: Information accumulation](https://github.com/carbon-language/carbon-lang/pull/875) - Allow information to be used before it is provided [globally](/proposals/p0875.md#strict-global-consistency), [within a file](/proposals/p0875.md#context-sensitive-local-consistency), or [within a top-level declaration](/proposals/p0875.md#top-down-with-minimally-deferred-type-checking). - [Do not allow inline method bodies to use members before they are declared](/proposals/p0875.md#strict-top-down) - [Do not allow separate declaration and definition](/proposals/p0875.md#disallow-separate-declaration-and-definition) - [#981: Implicit conversions for aggregates](https://github.com/carbon-language/carbon-lang/pull/981) - [Field order is not significant](/proposals/p0981.md#field-order-is-not-significant) - [Different field orders are incompatible](/proposals/p0981.md#different-field-orders-are-incompatible) - [Explicit instead of implicit conversions](/proposals/p0981.md#explicit-instead-of-implicit-conversions) - [#1154: Destructors](https://github.com/carbon-language/carbon-lang/pull/1154) - [Types implement destructor interface](/proposals/p1154.md#types-implement-destructor-interface) - [Prevent virtual function calls in destructors](/proposals/p1154.md#prevent-virtual-function-calls-in-destructors) - [Allow functions to act as destructors](/proposals/p1154.md#allow-functions-to-act-as-destructors) - [Allow private destructors](/proposals/p1154.md#allow-private-destructors) - [Allow multiple conditional destructors](/proposals/p1154.md#allow-multiple-conditional-destructors) - [Facet type naming](/proposals/p1154.md#type-of-type-naming) - [Other approaches to extensible classes without vtables](/proposals/p1154.md#other-approaches-to-extensible-classes-without-vtables) - [#2107: Clarify rules around `Self` and `.Self`](https://github.com/carbon-language/carbon-lang/pull/2107) - [`Self` not a keyword](/proposals/p2107.md#self-not-a-keyword) - [Make `Self` a member of all types](/proposals/p2107.md#make-self-a-member-of-all-types) - [`where` operator could be associative](/proposals/p2107.md#where-operator-could-be-associative) - [#2287: Allow unqualified name lookup for class members](https://github.com/carbon-language/carbon-lang/pull/2287) - [No unqualified lookup when defining outside a scope](/proposals/p2287.md#no-unqualified-lookup-when-defining-outside-a-scope) - [#2760: Consistent `class` and `interface` syntax](https://github.com/carbon-language/carbon-lang/pull/2760) - [Use `extends` instead of `extend`](/proposals/p2760.md#use-extends-instead-of-extend) - [List base class in class declaration](/proposals/p2760.md#list-base-class-in-class-declaration) - [#5017: Destructor syntax](https://github.com/carbon-language/carbon-lang/pull/5017) - [Destructor syntax options](/proposals/p5017.md#destructor-syntax-options) - [Destructor name options](/proposals/p5017.md#destructor-name-options) - [#6008: Replace `impl fn` with `override fn`](https://github.com/carbon-language/carbon-lang/pull/6008) ## References - [#257: Initialization of memory and variables](https://github.com/carbon-language/carbon-lang/pull/257) - [#561: Basic classes: use cases, struct literals, struct types, and future work](https://github.com/carbon-language/carbon-lang/pull/561) - [#722: Nominal classes and methods](https://github.com/carbon-language/carbon-lang/pull/722) - [#777: Inheritance](https://github.com/carbon-language/carbon-lang/pull/777) - [#875: Principle: Information accumulation](https://github.com/carbon-language/carbon-lang/pull/875) - [#981: Implicit conversions for aggregates](https://github.com/carbon-language/carbon-lang/pull/981) - [#1154: Destructors](https://github.com/carbon-language/carbon-lang/pull/1154) - [#2107: Clarify rules around `Self` and `.Self`](https://github.com/carbon-language/carbon-lang/pull/2107) - [#2287: Allow unqualified name lookup for class members](https://github.com/carbon-language/carbon-lang/pull/2287) - [#2760: Consistent `class` and `interface` syntax](https://github.com/carbon-language/carbon-lang/pull/2760) - [#5017: Destructor syntax](https://github.com/carbon-language/carbon-lang/pull/5017) ================================================ FILE: docs/design/code_and_name_organization/README.md ================================================ # Code and name organization ## Table of contents - [Goals and philosophy](#goals-and-philosophy) - [Overview](#overview) - [Small programs](#small-programs) - [Packages](#packages) - [Sizing packages and libraries](#sizing-packages-and-libraries) - [Details](#details) - [Source file introduction](#source-file-introduction) - [Name paths](#name-paths) - [Packages](#packages-1) - [`package` directives](#package-directives) - [`library` directives](#library-directives) - [`Main//default`](#maindefault) - [Files and libraries](#files-and-libraries) - [Shorthand notation for libraries in packages](#shorthand-notation-for-libraries-in-packages) - [Package name conflicts](#package-name-conflicts) - [Libraries](#libraries) - [Exporting entities from an API file](#exporting-entities-from-an-api-file) - [Granularity of libraries](#granularity-of-libraries) - [Exporting namespaces](#exporting-namespaces) - [Imports](#imports) - [Imports from the current package](#imports-from-the-current-package) - [Exporting imported names](#exporting-imported-names) - [Namespaces](#namespaces) - [Redeclaring imported namespaces](#redeclaring-imported-namespaces) - [Declaring namespace members](#declaring-namespace-members) - [Aliasing](#aliasing) - [Caveats](#caveats) - [Package and library name conflicts](#package-and-library-name-conflicts) - [Potential refactorings](#potential-refactorings) - [Update imports](#update-imports) - [Between API and implementation files](#between-api-and-implementation-files) - [Other refactorings](#other-refactorings) - [Preference for few child namespaces](#preference-for-few-child-namespaces) - [Redundant markers](#redundant-markers) - [Open questions](#open-questions) - [Different file extensions](#different-file-extensions) - [Imports from other languages](#imports-from-other-languages) - [Imports from URLs](#imports-from-urls) - [Test file type](#test-file-type) - [Alternatives considered](#alternatives-considered) - [References](#references) ## Goals and philosophy Important Carbon goals for code and name organization are: - [Language tools and ecosystem](/docs/project/goals.md#language-tools-and-ecosystem) - Tooling support is important for Carbon, including the possibility of a package manager. - Developer tooling, including both IDEs and refactoring tools, are expected to exist and be well-supported. - [Software and language evolution](/docs/project/goals.md#software-and-language-evolution): - We should support libraries adding new structs, functions or other identifiers without those new identifiers being able to shadow or break existing users that already have identifiers with conflicting names. - We should make it easy to refactor code, including moving code between files. This includes refactoring both by humans and by developer tooling. - [Fast and scalable development](/docs/project/goals.md#fast-and-scalable-development): - It should be easy for developer tooling to parse code, without needing to parse imports for context. - Structure should be provided for large projects to opt into features which will help maintain scaling of their codebase, while not adding burdens to small projects that don't need it. ## Overview Carbon [source files](source_files.md) have a `.carbon` extension, such as `geometry.carbon`. These files are the basic unit of compilation. ### Small programs For programs that fit into a single source file, no syntax is required to introduce the file. A very simple Carbon program can consist of a single file containing only a `Run` function: ``` fn Run() -> i32 { return 6 * 9; } ``` However, as the program grows larger, it is desirable to split it across multiple source files. To support this, _libraries_[[define](/docs/guides/glossary.md#library)] can be written containing pieces of the program: ``` library "Colors"; choice Color { Red, Green, Blue } fn ColorName(c: Color) -> String; ``` ``` impl library "Colors"; fn ColorName(c: Color) -> String { match (c) { case .Red => { return "Red"; } case .Green => { return "Green"; } case .Blue => { return "Blue"; } } } ``` ``` // Make the "Colors" library visible here. import library "Colors"; fn Run() { Print(ColorName(Color.Red)); } ``` A library is the basic unit of _dependency_. Separating code into multiple libraries can speed up the overall build while also making it clear which code is being reused. A library has a single API file which defines its interface, plus zero or more implementation files that can provide any implementation details that were omitted from the API file. These files are distinguished by whether the `library` declaration starts with the `impl` modifier. By convention, implementation files also use a file extension of `.impl.carbon`. Separating a library into interface and implementation may help organize code as a library grows, or to let the build system distinguish between the dependencies of the API itself and its underlying implementation. Implementation files allow for code to be extracted out from the API file, while only being callable from other files within the library, including both API and implementation files. A source file that does not specify a library is implicitly within the default library. This is the library in which a Carbon `Run` function should reside. ### Packages A program usually doesn't consist of only a single collection of source files developed by a team of people working together. In order to make it easy for different components of a program to be independently authored, Carbon supports _packages_ of code. Each source file in a package begins with a declaration of which _package_[[define](/docs/guides/glossary.md#package)] it belongs in. The package is the unit of _distribution_. The package name is a single identifier, such as `Geometry`. An example API file in the `Geometry` package would start with: ``` package Geometry; ``` A tiny package may consist of a single library with a single API file. As with libraries, additional implementation files can be added to the package by using the `impl` keyword in the package declaration: ``` impl package Geometry; ``` However, as a package adds more files, it will probably want to separate out into multiple libraries. These work exactly like the libraries described above. In fact, if no package is specified for a source file, it is implicitly part of the `Main` package. For example, an API file adding the library `Shapes` to the `Geometry` package, or `Geometry//Shapes` in [shorthand](#shorthand-notation-for-libraries-in-packages), would start with: ``` package Geometry library "Shapes"; ``` This library can be imported within the same package by using: ``` import library "Shapes"; ``` This will result in the public names declared in the `"Shapes"` library becoming visible in the importer, for example `Circle` might refer to a public type `Circle` declared in library `"Shapes"`. From a different package, this library can be imported by using: ``` import Geometry library "Shapes"; ``` Unlike in a same-package import, this import only introduces the name `Geometry`, as a private name for the importer to use to refer to the `Geometry` package. Names declared within this package can be found as members of the name `Geometry`, for example `Geometry.Circle`. The `library` portion is optional in this syntax, and if omitted, the default (unnamed) library is imported. ``` // Imports the source file beginning `package Geometry;` import Geometry; ``` The default library of a named package can be imported within that same package by using: ``` import library default; ``` The `Main` package can only be imported from other parts of the `Main` package, never other packages. Importing `Main//default` is invalid, regardless of which package is used. As code becomes more complex, and users pull in more code, it may also be helpful to add _namespaces_[[define](/docs/guides/glossary.md#namespace)] to give related entities consistently structured names. A namespace affects the _name path_[[define](/docs/guides/glossary.md#name-path)] used when calling code. For example, with no namespace, if a `Geometry` package defines `Circle` then the name path will be `Geometry.Circle`. However, it can be named `Geometry.TwoDimensional.Circle` with a `namespace`; for example: ``` package Geometry library "Shapes"; namespace TwoDimensional; struct TwoDimensional.Circle { ... }; ``` This scaling of programs into packages, libraries, and namespaces is how Carbon supports both small and large codebases. ### Sizing packages and libraries A different way to think of the sizing of packages and libraries is: - A package is a GitHub repository. - Small and medium projects that fit in a single repository will typically have a single package. For example, a medium-sized project like [Abseil](https://github.com/abseil/abseil-cpp/tree/master/absl) could still use a single `Abseil` package. - Large projects will have multiple packages. For example, Mozilla may have multiple packages for Firefox and other efforts. - A library is a few files that provide an interface and implementation, and should remain small. - Small projects will have a single library when it's easy to maintain all code in a few files. - Medium and large projects will have multiple libraries. For example, [Boost Geometry's Distance](https://github.com/boostorg/geometry/blob/develop/include/boost/geometry/algorithms/detail/distance/interface.hpp) interface and implementation might be its own library within `Boost`, with dependencies on other libraries in `Boost` and potentially other packages from Boost. - Library names could be named after the feature, such as `library "Algorithms"`, or include part of the path to reduce the chance of name collisions, such as `library "Geometry/Algorithms"`. Packages may choose to expose libraries that expose unions of interfaces from other libraries within the package. However, doing so would also provide the transitive closure of build-time dependencies, and is likely to be discouraged in many cases. ## Details ### Source file introduction Every source file will consist of, in order: 1. Either a `package` directive, a `library` directive, or no introduction. 2. A section of zero or more `import` directives. 3. Source file body, with other code. Comments and blank lines may be intermingled with these sections. [Metaprogramming](/docs/design/metaprogramming.md) code may also be intermingled, so long as the outputted code is consistent with the enforced ordering. Other types of code must be in the source file body. ### Name paths [Name paths](#name-paths) are defined above as sequences of identifiers separated by dots. This syntax may be loosely expressed as a regular expression: ```regex IDENTIFIER(\.IDENTIFIER)* ``` Name conflicts are addressed by [name lookup](/docs/design/name_lookup.md). ### Packages #### `package` directives The `package` directive's syntax may be loosely expressed as a regular expression: ```regex (impl)? package IDENTIFIER (library STRING)?; ``` For example: ```carbon impl package Geometry library "Objects/FourSides"; ``` Breaking this apart: - The use of the `impl` keyword indicates this is an implementation files as described under [libraries](#libraries). If it were omitted, this would instead be an API file. - The identifier after the `package` keyword, `Geometry`, is the package name and will prefix both library and namespace paths. - The `package` keyword also declares a package entity matching the package name. A package entity is almost identical to a namespace entity, except with some package/import-specific handling. For example, if the file declares `namespace TwoDimensional;` and `struct TwoDimensional.Line`, the struct may be used from files in other packages that import the library as `Geometry.TwoDimensional.Line`, using the `Geometry` package entity created by the `package` keyword. - `Main` is invalid for use as the package name. `Main` libraries must be defined by either the [`library` directive](#library-directives) or the [`Main//default`](#maindefault) rule. - The string after the `library` keyword sets the name of the library within the package. In this example, the `Geometry//Objects/FourSides` library will be used. - If the `library` portion were omitted, the file would implicitly be part of the default library, which does not have a string name. #### `library` directives The syntax for `library` directives is the same, without the `package` portion: ```regex (impl)? library STRING; ``` For example: ```carbon impl library "PrimeGenerator"; ``` If the `library` directive is used, the file is implicitly part of the `Main` package, whose name cannot be written explicitly. #### `Main//default` If neither a `package` directive nor a `library` directive is provided, the file is an API file for `Main//default`. An `impl` cannot be provided for `Main//default`. #### Files and libraries Every file is in exactly one library, which is always part of a package. Because every file is within a package, and packages act as top-level namespaces, every entity in Carbon will be in a namespace, even if its namespace path consists of only the package name. There is no "global" namespace. - Every entity in a file will be defined within the namespace described in the `package` directive. - Entities within a file may be defined in [child namespaces](#namespaces). Files contributing to the `Geometry//Objects/FourSides` library must all start with [`impl`] `package` `Geometry` `library` `"Objects/FourSides"` `;`. #### Shorthand notation for libraries in packages Library names may also be referred to as `PACKAGE//LIBRARY` as shorthand in text. `PACKAGE//default` will refer to the name of the library used when no `library` argument is specified, although `PACKAGE` may also be used in situations where it is unambiguous that it still refers to the default library. The package name `Main` is always used implicitly and cannot appear as a package name within source code, but still appears in shorthand notation. For example, `Main//default` is the library that is expected to contain a Carbon `Run` function. It's recommended that libraries use a single `/` for separators where desired, in order to distinguish between the `//` of the package and `/` separating library segments. For example, `Geometry//Objects/FourSides` uses a single `/` to separate the `Object/FourSides` library name. #### Package name conflicts Because an import of a package declares a namespace entity with the same name, conflicts with the package name are possible. For example, this is a conflict for `DateTime`: ```carbon import DateTime; struct DateTime { ... } ``` Note that [imported name conflicts](#package-and-library-name-conflicts) are handled differently. ### Libraries Every Carbon library consists of one or more files. Each Carbon library has a primary file that defines its API, and may optionally contain additional files that are implementation. - An API file's `package` directive does not include the `impl` modifier. For example, `package Geometry library "Shapes";` - API filenames must have the `.carbon` extension. They must not have a `.impl.carbon` extension. - API file paths will correspond to the library name. - The precise form of this correspondence is undetermined, but should be expected to be similar to a "Math/Algebra" library being in a "Math/Algebra.carbon" file path. - The package will not be used when considering the file path. - An implementation file's `package` directive includes an `impl` modifier. For example, `impl package Geometry library "Shapes";`. - Implementation filenames must have the `.impl.carbon` extension. - Implementation file paths need not correspond to the library name. - Implementation files implicitly import the library's API. Implementation files cannot import each other. There is no facility for file or non-API imports. The difference between API and implementation will act as a form of access control. API files must compile independently of implementation, only importing from APIs from other libraries. API files are also visible to all files and libraries for import. Implementation files only see API files for import, not other implementation files. When any file imports a library's API, it should be expected that the transitive closure of imported files from the primary API file will be a compilation dependency. The size of that transitive closure affects compilation time, so libraries with complex implementations should endeavor to minimize their API imports. Libraries also serve as a critical unit of compilation. Dependencies between libraries must be clearly marked, and the resulting dependency graph will allow for separate compilation. #### Exporting entities from an API file Entities in the API file are part of the library's public API by default. They may be marked as `private` to indicate they should only be visible to other parts of the library. ```carbon package Geometry library "Shapes"; // Circle is part of the public API of the library, and will be available to // other libraries as Geometry.Circle. struct Circle { ... } // CircleHelper is private, and so will not be available to other libraries. private fn CircleHelper(circle: Circle) { ... } namespace Operations; // Operations.GetCircumference is part of the public API of the library, and // will be available to other libraries as Geometry.Operations.GetCircumference. fn Operations.GetCircumference(circle: Circle) { ... } ``` This means that an API file can contain all implementation code for a library. However, separate implementation files are still desirable for a few reasons: - It will be easier for readers to quickly scan an API-only file for API documentation. - Reducing the amount of code in an API file can speed up compilation, especially if fewer imports are needed. This can result in transitive compilation performance improvements for files using the library. - From a code maintenance perspective, having smaller files can make a library more maintainable. Entities in an implementation file should never have visibility keywords. If they are forward declared in the API file, they use the declaration's visibility; if they are only present in an implementation file, they are implicitly `private`. #### Granularity of libraries The compilation graph of Carbon will generally consist of API files depending on each other, and implementation files depending only on API files. Compiling a given file requires compiling the transitive closure of API files first. Parallelization of compilation is then limited by how large that transitive closure is, in terms of total volume of code rather than quantity. This also affects build cache invalidation. In order to maximize opportunities to improve compilation performance, we will encourage granular libraries. Conceptually, we want libraries to be very small, possibly containing only a single class. The choice of only allowing a single API file per library should help encourage developers to write small libraries. #### Exporting namespaces A namespace declared in an API file is only exported if it contains at least one `public` non-namespace name. For example, given this code: ```carbon package Checksums library "Sha"; namespace Sha256; namespace ImplementationDetails; private fn ImplementationDetails.ShaHelper(data: Bytes) -> Bytes; fn Sha256.HexDigest(data: Bytes) -> String { ... } ``` Calling code may look like: ```carbon package Caller; import Checksums library "Sha"; fn Process(data: Bytes) { ... var digest: String = Checksums.Sha256.HexDigest(data); ... } ``` In this example, the `Sha256` namespace is exported as part of the API implicitly, but the name `Checksums.ImplementationDetails` is not available in the caller. ### Imports `import` directives supports reusing code from other files and libraries. The `import` directive's syntax may be loosely expressed as a regular expression: ```regex import IDENTIFIER (library NAME_PATH)?; import Core (library NAME_PATH)?; import library NAME_PATH; import library default; ``` An import with a package name `IDENTIFIER` declares a package entity named after the imported package, and makes API entities from the imported library available through it. `Main` cannot be imported from other packages; in other words, only `import library NAME_PATH` syntax can be used to import from `Main`. Imports of `Main//default` are invalid. The keyword `Core` can be used as a package name in an import in order to import portions of the standard library that are not part of the prelude. The full name path is a concatenation of the names of the package entity, any namespace entities applied, and the final entity addressed. Child namespaces or entities may be [aliased](/docs/design/aliases.md) if desired. For example, given a library: ```carbon package Math; namespace Trigonometry; fn Trigonometry.Sin(...); ``` Calling code would import it and use it like: ```carbon package Geometry; import Math; fn DoSomething() { ... Math.Trigonometry.Sin(...); ... } ``` Repeat imports from the same package reuse the same package entity. For example, this produces only one `Math` package entity: ```carbon import Math; import Math library "Trigonometry"; ``` NOTE: A library must never import itself. Any implementation files in a library automatically import the API, so a self-import should never be required. #### Imports from the current package An import without a package name imports the public names from the given library of the same package. Entities defined in the API of the current library and in imported libraries in the current package may be used without mentioning the package prefix. However, symbols from other packages must be imported and accessed through the package namespace. For example: ```carbon package Geometry; // This is required even though it's still in the Geometry package. import library "Shapes"; // Circle is visible here. The name Geometry is not declared, so // Geometry.Circle is invalid. fn GetArea(c: Circle) { ... } ``` #### Exporting imported names The `export` keyword supports exporting names that come from imported libraries. This can be used to create an API file that provides contents from multiple other API files. `export` can be used either as a modifier to the `import` keyword to export the entire imported library, or in an `export ` declaration to export a specific entity. For example: ``` // Exports every name from the "Foo" library. export import library "Foo"; // Exports just the "Bar" entity, which must come from an import. export Bar; // Exports the "Wiz" entity in the "NS" namespace. export NS.Wiz; ``` When `export` is used as a modifier to `import`, it is still considered to be an `import` directive for the [source file introduction](#source-file-introduction). Namespaces cannot be exported using a `export ` declaration. It is possible a form of support will be added as [future work](/proposals/p3938.md#namespaces). Names in other packages also cannot be exported. This covers both possible syntaxes: `export import ` and `export .`. However, a name in another package can be aliased inside the current package, and that alias can be re-exported. ### Namespaces Namespaces offer named paths for entities. Namespaces must be declared at file scope, and may be nested. Multiple libraries may contribute to the same namespace. In practice, packages may have namespaces such as `Testing` containing entities that benefit from an isolated space but are present in many libraries. The `namespace` keyword's syntax may loosely be expressed as a regular expression: ```regex namespace NAME_PATH; ``` The `namespace` keyword declares a namespace entity. The namespace is applied to other entities by including it as a prefix when declaring a name. For example: ```carbon package Time; namespace Timezones.Internal; struct Timezones.Internal.RawData { ... } fn ParseData(data: Timezones.Internal.RawData); ``` A namespace declaration adds the first identifier in the name path as a name in the file's namespace. In the above example, after declaring `namespace Timezones.Internal;`, `Timezones` is available as an identifier and `Internal` is reached through `Timezones`. #### Redeclaring imported namespaces Namespaces may exist in imported package entities, in addition to being declared in the current file. However, even if the namespace already exists in an imported library from the current package, the namespace must still be declared locally in order to add symbols to it. For example, if the `Geometry//Shapes/ThreeSides` library provides the `Geometry.Shapes` namespace, this code is still valid: ```carbon package Geometry library "Shapes/FourSides"; import library "Shapes/ThreeSides"; // This does not conflict with the existence of `Geometry.Shapes` from // `Geometry//Shapes/ThreeSides`, even though the name path is identical. namespace Shapes; // This requires the above 'namespace Shapes' declaration. It cannot use // `Geometry.Shapes` from `Geometry//Shapes/ThreeSides`. struct Shapes.Square { ... }; ``` #### Declaring namespace members Namespace members may only be declared in the same name scope which was used to declare the namespace. For example: ```carbon namespace NS; // ✅ Allowed: declaration is in file scope, which also declared `NS`. class NS.ClassT { // ❌ Error: A class body has its own name scope. var NS.a: i32 = 0; } fn Function() { // ❌ Error: A function body has its own name scope. var NS.b: i32 = 1; } // ✅ Allowed: declaration is in file scope, which also declared `NS`. namespace NS.MemberNS; // ✅ Allowed: declaration is in file scope, which also declared `NS.MemberNS`. class NS.MemberNS.MemberClassT {} ``` When multiple names are declared by binding patterns in the same pattern, all names must be in the same namespace. Because namespace members can only be declared in the same scope as the namespace, a namespace-qualified pattern binding can only be used in the pattern of a `var` or `let` declaration. For example: ```carbon namespace NS; // ✅ Allowed: `a` and `b` use the default namespace. var (a: i32, b: i32) = (1, 2); // ✅ Allowed: `c` and `d` are in the same namespace. var (NS.c: i32, NS.d: i32) = (3, 4); // ❌ Error: `e` and `f` are not in the same namespace. var (e: i32, NS.f: i32) = (5, 6); ``` This restriction only applies when declaring names in binding patterns, not other name uses in patterns. #### Aliasing Carbon's [alias keyword](/docs/design/aliases.md) will support aliasing namespaces. For example, this would be valid code: ```carbon namespace Timezones.Internal; alias TI = Timezones.internal; struct TI.RawData { ... } fn ParseData(data: TI.RawData); ``` ## Caveats ### Package and library name conflicts Library name conflicts should be avoidable, because it's expected that a given package is maintained by a single organization. It's the responsibility of that organization to maintain unique library names within their package. A package name conflict occurs when two different packages use the same name, such as two packages named `Stats`. Versus libraries, package name conflicts are more likely because two organizations may independently choose identical names. We will encourage a unique package naming scheme, such as maintaining a name server for open source packages. Conflicts can also be addressed by renaming one of the packages, either at the source, or as a local modification. We do need to address the case of package names conflicting with other entity names. It's possible that a preexisting entity will conflict with a new import, and that renaming the entity is infeasible to rename due to existing callers. Alternately, the entity may be using an idiomatic name that it would contradict naming conventions to rename. In either case, this conflict may exist in a single file without otherwise affecting users of the API. This will be addressed by [name lookup](/docs/design/name_lookup.md). ### Potential refactorings These are potential refactorings that we consider important to make it easy to automate. #### Update imports Imports will frequently need to be updated as part of refactorings. When code is deleted, it should be possible to parse the remaining code, parse the imports, and determine which entities in imports are referred to. Unused imports can then be removed. When code is moved, it's similar to deletion in the originating file. For the destination file, the moved code should be parsed to determine which entities it referred to from the originating file's imports, and these will need to be included in the destination file: either reused if already present, or added. When new code is added, existing imports can be checked to see if they provide the symbol in question. There may also be heuristics which can be implemented to check build dependencies for where imports should be added from, such as a database of possible entities and their libraries. However, adding references may require manually adding imports. #### Between API and implementation files - Move the definition of an entity from an API file to an implementation file, while leaving a declaration behind. - This should be a local change that will not affect any calling code. - Inlining will be affected because the implementation won't be visible to callers. - [Update imports](#update-imports). - Split an API and implementation file. - This is a repeated operation of individual API moves, as noted above. - Move the definition of an entity from an implementation file to the API file. - This should be a local change that will not affect any calling code. - Inlining will be affected because the implementation becomes visible to callers. - [Update imports](#update-imports). - Combine an API and implementation file. - This is a repeated operation of individual API moves, as noted above. - Add the `private` modifier to a declaration. - Search for library-external callers, and fix them first. - Remove the `private` modifier from a declaration. - This should be a local change that will not affect any calling code. - Move a `private` declaration from the API file to an implementation file. - The declaration must be moved to the same file as the definition of the entity. - The declaration can only be used by the implementation file that now contains it. Search for other callers within the library, and fix them first. - [Update imports](#update-imports). - Move a `private` declaration from an implementation file to the API file. - This should be a local change that will not affect any calling code. - [Update imports](#update-imports). - Move a declaration and definition from one implementation file to another. - Search for any callers within the source implementation file, and either move them too, or fix them first. - [Update imports](#update-imports). #### Other refactorings - Rename a package. - The imports of all calling files must be updated accordingly. - All call sites must be changed, as the package name changes. - [Update imports](#update-imports). - Move a public declaration and definition between different packages. - The imports of all calling files must be updated accordingly. - All call sites must be changed, as the package name changes. - [Update imports](#update-imports). - Move a public declaration and definition between libraries in the same package. - The imports of all calling files must be updated accordingly. - As long as the namespaces remain the same, no call sites will need to be changed. - [Update imports](#update-imports). - Rename a library. - This is equivalent to a repeated operation of moving a public declaration and definition between libraries in the same package. - Move a declaration and definition from one namespace to another. - Ensure the new namespace is declared for the declaration and definition. - Update the namespace used by call sites. - The imports of all calling files may remain the same. - Rename a namespace. - This is equivalent to a repeated operation of moving a declaration and definition from one namespace to another. - Rename a file, or move a file between directories. - Build configuration will need to be updated. - This additionally requires the steps to rename a library, because library names must correspond to the renamed paths. ### Preference for few child namespaces We expect that most code should use a package and library, but avoid specifying namespaces beneath the package. The package name itself should typically be sufficient distinction for names. Child namespaces create longer names, which engineers will dislike typing. Based on experience, we expect to start seeing aliasing even at name lengths around six characters long. With longer names, we should expect more aliasing, which in turn will reduce code readability because more types will have local names. We believe it's feasible for even large projects to collapse namespaces down to a top level, avoiding internal tiers of namespaces. We understand that child namespaces are sometimes helpful, and will robustly support them for that. However, we will model code organization to encourage fewer namespaces. ### Redundant markers We use a few possibly redundant markers for packages and libraries: - The filename and the presence or absence of the `impl` keyword duplicate the API versus implementation choice. - The filename and the library name portion of the package declaration duplicate the name of the library. - The `import` keyword requires the full library. These choices are made to assist human readability and tooling: - Being explicit about imports creates the opportunity to generate build dependencies from files, rather than having them maintained separately. - Being explicit about API versus implementation in the filename makes it easier for both humans and tooling to determine what to expect, and makes it possible to check the type without reading file content. - Repeating the type and library name in the file content makes non-file-system-based builds possible. ## Open questions These open questions are expected to be revisited by future proposals. ### Different file extensions Currently, we're using `.carbon` and `.impl.carbon`. In the future, we may want to change the extension, particularly because Carbon may be renamed. There are several other possible extensions / commands that we've considered in coming to the current extension: - `.carbon`: This is an obvious and unsurprising choice, but also quite long for a file extension. - `.6c`: This sounds a little like 'sexy' when read aloud. - `.c6`: This seems a weird incorrect ordering of the atomic number and has a bad, if obscure, Internet slang association. - `.cb` or `.cbn`: These collide with several acronyms and may not be especially memorable as referring to Carbon. - `.crb`: This has a bad Internet slang association. ### Imports from other languages Currently, we do not support cross-language imports. In the future, we will likely want to support imports from other languages, particularly for C++ interoperability. To fit into the proposed `import` syntax, we are provisionally using a special `Cpp` package to import headers from C++ code, as in: ```carbon import Cpp library ""; import Cpp library "myproject/myclass.h"; fn MyCarbonCall(x: Cpp.std.map(Cpp.MyProject.MyClass)); ``` ### Imports from URLs Currently, we don't support any kind of package management with imports. In the future, we may want to support tagging imports with a URL that identifies the repository where that package can be found. This can be used to help drive package management tooling and to support providing a non-name identity for a package that is used to enable handling conflicted package names. Although we're not designing this right now, it could fit into the proposed syntax. For example: ```carbon import Carbon library "Utilities" url("https://github.com/carbon-language/carbon-libraries"); ``` ### Test file type Similar to API and implementation files, we may eventually want test-specific files. This should be part of a larger testing plan. ## Alternatives considered - Packages - [Name paths for package names](/proposals/p0107.md#name-paths-for-package-names) - [Referring to the package as `package`](/proposals/p0107.md#referring-to-the-package-as-package) - [Remove the `library` keyword from `package` and `import`](/proposals/p0107.md#remove-the-library-keyword-from-package-and-import) - [Rename package concept](/proposals/p0107.md#rename-package-concept) - [No association between the file system path and library/namespace](/proposals/p0107.md#no-association-between-the-file-system-path-and-librarynamespace) - [Require the use of the identifier `Main` in package declarations](/proposals/p2550.md#require-the-use-of-the-identifier-main-in-package-declarations) - [Permit the use of the identifier `Main` in package declarations](/proposals/p2550.md#permit-the-use-of-the-identifier-main-in-package-declarations) - [Make the main package be unnamed](/proposals/p2550.md#make-the-main-package-be-unnamed) - [Use a different name for the main package](/proposals/p2550.md#use-a-different-name-for-the-main-package) - [Use a different name for the entry point](/proposals/p2550.md#use-a-different-name-for-the-entry-point) - [Distinguish file scope from package scope](/proposals/p2550.md#distinguish-file-scope-from-package-scope) - [Default to an implementation file for `Main//default` instead of an API file](/proposals/p3403.md#default-to-maindefault-impl-instead-of-maindefault-api) - Libraries - [Allow exporting namespaces](/proposals/p0107.md#allow-exporting-namespaces) - [Allow importing implementation files from within the same library](/proposals/p0107.md#allow-importing-implementation-files-from-within-the-same-library) - [Alternative library separators and shorthand](/proposals/p0107.md#alternative-library-separators-and-shorthand) - [Single-word libraries](/proposals/p0107.md#single-word-libraries) - [Collapse API and implementation file concepts](/proposals/p0107.md#collapse-api-and-implementation-file-concepts) - [Automatically generating the API separation](/proposals/p0107.md#automatically-generating-the-api-separation) - [Collapse file and library concepts](/proposals/p0107.md#collapse-file-and-library-concepts) - [Collapse the library concept into packages](/proposals/p0107.md#collapse-the-library-concept-into-packages) - [Collapse the package concept into libraries](/proposals/p0107.md#collapse-the-package-concept-into-libraries) - [Default API to private](/proposals/p0752.md#default-api-to-private) - [Default `impl` to public](/proposals/p0752.md#default-impl-to-public) - [Different file type labels](/proposals/p0107.md#different-file-type-labels) - [Don't default to introducing an API file](/proposals/p3927.md#mandatory-api-or-impl-as-suffix) - [Function-like syntax](/proposals/p0107.md#function-like-syntax) - [Inlining from implementation files](/proposals/p0107.md#inlining-from-implementation-files) - [Library-private access controls](/proposals/p0107.md#library-private-access-controls) - [Make keywords either optional or required in separate definitions](/proposals/p0752.md#make-keywords-either-optional-or-required-in-separate-definitions) - [Managing API versus implementation in libraries](/proposals/p0107.md#managing-api-versus-implementation-in-libraries) - [Multiple API files](/proposals/p0107.md#multiple-api-files) - [Name paths as library names](/proposals/p0107.md#name-paths-as-library-names) - [Put the `impl` modifier at the end](/proposals/p3927.md#mandatory-api-or-impl-as-suffix) - [Put the `impl` modifier before `library`](/proposals/p3927.md#put-the-impl-modifier-before-library) - Imports - [Block imports](/proposals/p0107.md#block-imports) - [Block imports of libraries of a single package](/proposals/p0107.md#block-imports-of-libraries-of-a-single-package) - [Broader imports, either all names or arbitrary code](/proposals/p0107.md#broader-imports-either-all-names-or-arbitrary-code) - [Direct name imports](/proposals/p0107.md#direct-name-imports) - [Always include the package name in imports](/proposals/p2550.md#keep-the-package-name-in-imports) - Exports - [Other `export` syntax structures](/proposals/p3938.md#other-export-syntax-structures) - [Other `export name` placements](/proposals/p3938.md#other-export-name-placements) - [Re-exporting cross-package](/proposals/p3938.md#re-exporting-cross-package) - Namespaces - [File-level namespaces](/proposals/p0107.md#file-level-namespaces) - [Scoped namespaces](/proposals/p0107.md#scoped-namespaces) - [Allow prefixing a tuple binding pattern with a namespace](/proposals/p3407.md#allow-prefixing-a-tuple-binding-pattern-with-a-namespace) - [Allow binding patterns to declare names in multiple namespaces](/proposals/p3407.md#allow-binding-patterns-to-declare-names-in-multiple-namespaces) - [Allow declaring names in namespaces not owned by the current scope](/proposals/p3407.md#allow-declaring-names-in-namespaces-not-owned-by-the-current-scope) - [Allow declaring namespaces in scopes other than the file scope](/proposals/p3407.md#allow-declaring-namespaces-in-scopes-other-than-the-file-scope) ## References - Proposal [#107: Code and name organization](https://github.com/carbon-language/carbon-lang/pull/107) - Proposal [#2550: Simplified package declaration for the main package](https://github.com/carbon-language/carbon-lang/pull/2550) - Proposal [#3403: Change Main//default to an api file](https://github.com/carbon-language/carbon-lang/pull/3403) - Proposal [#3453: Clarify name bindings in namespaces.](https://github.com/carbon-language/carbon-lang/pull/3407) - Proposal [#3927: More consistent package syntax](https://github.com/carbon-language/carbon-lang/pull/3927) - Proposal [#3938: Exporting imported names](https://github.com/carbon-language/carbon-lang/pull/3938) ================================================ FILE: docs/design/code_and_name_organization/source_files.md ================================================ # Source files ## Table of contents - [Overview](#overview) - [Encoding](#encoding) - [Alternatives considered](#alternatives-considered) - [References](#references) ## Overview A Carbon _source file_ is a sequence of Unicode code points in Unicode Normalization Form C ("NFC"), and represents a portion of the complete text of a program. Program text can come from a variety of sources, such as an interactive programming environment (a so-called "Read-Evaluate-Print-Loop" or REPL), a database, a memory buffer of an IDE, or a command-line argument. The canonical representation for Carbon programs is in files stored as a sequence of bytes in a file system on disk. Such files have a `.carbon` extension. ## Encoding The on-disk representation of a Carbon source file is encoded in UTF-8. Such files may begin with an optional UTF-8 BOM, that is, the byte sequence EF16,BB16,BF16. This prefix, if present, is ignored. No Unicode normalization is performed when reading an on-disk representation of a Carbon source file, so the byte representation is required to be normalized in Normalization Form C. The Carbon source formatting tool will convert source files to NFC as necessary. ## Alternatives considered - [Character encoding](/proposals/p0142.md#character-encoding-1) - [Byte order marks](/proposals/p0142.md#byte-order-marks) - [Normalization forms](/proposals/p0142.md#normalization-forms) ## References - Proposal [#142: Unicode source files](https://github.com/carbon-language/carbon-lang/pull/142) - [Unicode](https://www.unicode.org/versions/latest/) is a universal character encoding, maintained by the [Unicode Consortium](https://home.unicode.org/basic-info/overview/). It is the canonical encoding used for textual information interchange across all modern technology. Carbon is based on Unicode 13.0, which is currently the latest version of the Unicode standard. Newer versions will be considered for adoption as they are released. - [Unicode Standard Annex #15: Unicode Normalization Forms](https://www.unicode.org/reports/tr15/tr15-50.html) - [Wikipedia Unicode equivalence page: Normal forms](https://en.wikipedia.org/wiki/Unicode_equivalence#Normal_forms) ================================================ FILE: docs/design/control_flow/README.md ================================================ # Control flow ## Overview Blocks of statements are generally executed linearly. However, statements are the primary place where this flow of execution can be controlled. Carbon's flow control statements are: - [`if` and `else`](conditionals.md) provides conditional execution of statements. - Loops: - [`while`](loops.md#while) executes the loop body for as long as the loop expression returns `True`. - [`for`](loops.md#for) iterates over an object, such as elements in an array. - [`break`](loops.md#break) exits loops. - [`continue`](loops.md#continue) goes to the next iteration of a loop. - [`return`](return.md) ends the flow of execution within a function, returning it to the caller. ================================================ FILE: docs/design/control_flow/conditionals.md ================================================ # Conditionals ## Table of contents - [Overview](#overview) - [Alternatives considered](#alternatives-considered) - [References](#references) ## Overview `if` and `else` provide conditional execution of statements. Syntax is: > `if (`_boolean expression_ `) {` _statements_ `}` > > [ `else if (` _boolean expression_ `) {` _statements_ `}` ] ... > > [ `else {` _statements_ `}` ] Only one group of statements will execute: - When the first `if`'s boolean expression evaluates to true, its associated statements will execute. - When earlier boolean expressions evaluate to false and an `else if`'s boolean expression evaluates to true, its associated statements will execute. - `... else if ...` is equivalent to `... else { if ... }`, but without visible nesting of braces. - When all boolean expressions evaluate to false, the `else`'s associated statements will execute. When a boolean expression evaluates to true, no later boolean expressions will evaluate. Note that `else if` may be repeated. For example: ```carbon if (fruit.IsYellow()) { Print("Banana!"); } else if (fruit.IsOrange()) { Print("Orange!"); } else if (fruit.IsGreen()) { Print("Apple!"); } else { Print("Vegetable!"); } fruit.Eat(); ``` This code will: - Evaluate `fruit.IsYellow()`: - When `True`, print `Banana!` and resume execution at `fruit.Eat()`. - When `False`, evaluate `fruit.IsOrange()`: - When `True`, print `Orange!` and resume execution at `fruit.Eat()`. - When `False`, evaluate `fruit.IsGreen()`: - When `True`, print `Apple!` and resume execution at `fruit.Eat()`. - When `False`, print `Vegetable!` and resume execution at `fruit.Eat()`. ## Alternatives considered - [Optional braces](/proposals/p0623.md#optional-braces) - [Optional parentheses](/proposals/p0623.md#optional-parentheses) - [`elif`](/proposals/p0623.md#elif) ## References - Proposal [#285: `if` and `else`](https://github.com/carbon-language/carbon-lang/pull/285) - Proposal [#623: Require braces](https://github.com/carbon-language/carbon-lang/pull/623) ================================================ FILE: docs/design/control_flow/loops.md ================================================ # Loops ## Table of contents - [Overview](#overview) - [Details](#details) - [`while`](#while) - [`for`](#for) - [`break`](#break) - [`continue`](#continue) - [Alternatives considered](#alternatives-considered) - [References](#references) ## Overview Carbon provides loops using the `while` and `for` statements. Within a loop, the `break` and `continue` statements can be used for flow control. ## Details ### `while` `while` statements loop for as long as the passed expression returns `True`. Syntax is: > `while (` _boolean expression_ `) {` _statements_ `}` For example, this prints `0`, `1`, `2`, then `Done!`: ```carbon var x: Int = 0; while (x < 3) { Print(x); ++x; } Print("Done!"); ``` ### `for` `for` statements support range-based looping, typically over containers. Syntax is: > `for (` _var declaration_ `in` _expression_ `) {` _statements_ `}` For example, this prints all names in `names`: ```carbon for (var name: String in names) { Print(name); } ``` `PrintNames()` prints each `String` in the `names` `List` in iteration order. TODO: Add semantics discussion from [#1885: ranged-based `for` for user-defined types](https://github.com/carbon-language/carbon-lang/pull/1885). ### `break` The `break` statement immediately ends a `while` or `for` loop. Execution will resume at the end of the loop's scope. Syntax is: > `break;` For example, this processes steps until a manual step is hit (if no manual step is hit, all steps are processed): ```carbon for (var step: Step in steps) { if (step.IsManual()) { Print("Reached manual step!"); break; } step.Process(); } ``` ### `continue` The `continue` statement immediately goes to the next loop of a `while` or `for`. In a `while`, execution continues with the `while` expression. Syntax is: > `continue;` For example, this prints all non-empty lines of a file, using `continue` to skip empty lines: ```carbon var f: File = OpenFile(path); while (!f.EOF()) { var line: String = f.ReadLine(); if (line.IsEmpty()) { continue; } Print(line); } ``` ## Alternatives considered - [Non-C++ syntax](/proposals/p0340.md#non-c-syntax) - [Initializing variables in the `while`](/proposals/p0340.md#initializing-variables-in-the-while) - `for`: - [Include semisemi `for` loops](/proposals/p0353.md#include-semisemi-for-loops) - [Multi-variable bindings](/proposals/p0353.md#multi-variable-bindings) - [`:` versus `in`](/proposals/p0618.md#-versus-in) - [Optional braces](/proposals/p0623.md#optional-braces) - [Optional parentheses](/proposals/p0623.md#optional-parentheses) ## References - Proposal [#340: `while`](https://github.com/carbon-language/carbon-lang/pull/340) - Proposal [#353: `for`](https://github.com/carbon-language/carbon-lang/pull/353) - Proposal [#618: `var` ordering](https://github.com/carbon-language/carbon-lang/pull/618) - Proposal [#623: Require braces](https://github.com/carbon-language/carbon-lang/pull/623) ================================================ FILE: docs/design/control_flow/return.md ================================================ # `return` ## Table of contents - [Overview](#overview) - [Returning empty tuples](#returning-empty-tuples) - [`returned var`](#returned-var) - [`return` and initialization](#return-and-initialization) - [Alternatives considered](#alternatives-considered) - [References](#references) ## Overview The `return` statement ends the flow of execution within a [function](../functions.md), returning execution to the caller. Its syntax is: > `return` _[ expression ]_ `;` If the function returns a value to the caller, that value is provided by an expression in the return statement. For example: ```carbon fn Sum(a: i32, b: i32) -> i32 { return a + b; } ``` When a return type is specified, a function must _always_ `return` before control flow can reach the end of the function body. In other words, `fn DoNothing() -> i32 {}` would be invalid because execution will reach the end of the function body without returning a value. ### Returning empty tuples Returning an empty tuple `()` is special, and similar to C++'s `void` returns. When a function has no specified return type, its return type is implicitly `()`. `return` must not have an expression argument in this case. It also has an implicit `return;` at the end of the function body. For example: ```carbon // No return type is specified, so this returns `()` implicitly. fn MaybeDraw(should_draw: bool) { if (!should_draw) { // No expression is passed to `return`. return; } ActuallyDraw(); // There is an implicit `return;` here. } ``` When `-> ()` is specified in the function signature, the return expression is required. Omitting `-> ()` is encouraged, but specifying it is supported for generalized code structures, including [templates](../templates.md). In order to be consistent with other explicitly specified return types, `return;` is invalid in this case. For example: ```carbon // `-> ()` defines an explicit return value. fn MaybeDraw(should_draw: bool) -> () { if (!should_draw) { // As a consequence, a return value must be passed. return (); } ActuallyDraw(); // The return value must again be explicit. return (); } ``` ### `returned var` [Local variables](../values.md#binding-patterns-and-local-variables-with-let-and-var) may be declared with a `returned` statement. Its syntax is: > `returned` _var statement_ When a variable is marked as `returned`, it must be the only `returned` value in-scope. If a `returned var` is returned, the specific syntax `return var;` must be used. Returning expressions is not allowed while a `returned var` is in scope. For example: ```carbon fn MakeCircle(radius: i32) -> Circle { returned var c: Circle; c.radius = radius; // `return c` would be invalid because `returned` is in use. return var; } ``` If control flow exits the scope of a `returned` variable in any way other than `return var;`, the `returned var`'s lifetime ends as normal. When this occurs, `return` may again be used with expressions. For example: ```carbon fn MakePointInArea(area: Area, preferred_x: i32, preferred_y: i32) -> Point { if (preferred_x >= 0 && preferred_y >= 0) { returned var p: Point = { .x = preferred_x, .y = preferred_y }; if (area.Contains(p)) { return var; } // p's lifetime ends here when `return var;` is not reached. } return area.RandomPoint(); } ``` ### `return` and initialization Consider the following common initialization code: ```carbon fn CreateMyObject() -> MyType { return ; } var x: MyType = CreateMyObject(); ``` The `` in the return statement of `CreateMyObject` initializes the variable `x` here. There is no copy or similar. It is equivalent to: ```carbon var x: MyType = ; ``` This applies recursively, similar to C++'s guaranteed copy elision. In the case where additional statements should be run between constructing the return value and returning, the use of `returned var` allows for improved efficiency because the `returned var` can directly use the address of `var` declared by the caller. For example, here the `returned var vector` in `CreateVector` uses the storage of `my_vector` for initialization, avoiding a copy: ```carbon fn CreateVector(x: i32, y: i32) -> Vector { returned var vector: Vector; vector.x = x; vector.y = y; return var; } var my_vector: Vector = CreateVector(1, 2); ``` As a consequence, `returned var` is encouraged because it makes it easier to avoid copies. > **TODO:** Have some discussion of RVO and NRVO as they are found in C++ here, > and the fact that Carbon provides the essential part of these as first-class > features and therefore they are never "optimizations" or done implicitly or > optionally. ## Alternatives considered - [Implicit or expression returns](/proposals/p0415.md#implicit-or-expression-returns) - [Named return variable in place of a return type](/proposals/p0257.md#named-return-variable-in-place-of-a-return-type) - [Retain the C++ rule](/proposals/p0538.md#retain-the-c-rule) - [Fully divorce functions and procedures](/proposals/p0538.md#fully-divorce-functions-and-procedures) ## References - Proposal [#257: Initialization of memory and variables](https://github.com/carbon-language/carbon-lang/pull/257) - Proposal [#415: Syntax: `return`](https://github.com/carbon-language/carbon-lang/pull/415) - Proposal [#538: `return` with no argument](https://github.com/carbon-language/carbon-lang/pull/538) ================================================ FILE: docs/design/declaring_entities.md ================================================ # Declaring entities ## Table of contents - [Overview](#overview) - [Matching redeclarations of an entity](#matching-redeclarations-of-an-entity) - [Details](#details) - [`extern` and `extern library`](#extern-and-extern-library) - [Valid scopes for `extern`](#valid-scopes-for-extern) - [Effect on indirect imports](#effect-on-indirect-imports) - [Alternatives considered](#alternatives-considered) - [References](#references) ## Overview Entities may have up to three declarations: - An optional, owning forward declaration. - For example, `class MyClass;`. - This must come before the definition. The API file is considered to be before the implementation file. - A required, owning definition. - For example, `class MyClass { ... }`. - The definition might be the _only_ declaration. - An optional, non-owning `extern library ""` declaration. - For example, `extern library "OtherLibrary" class MyClass;`. - It must be in a separate library from the definition. - The owning library's API file must import the `extern` declaration, and must also contain a declaration. - The owning library's declarations must have the `extern` modifier (without `library`). - For example, `extern class MyClass;`. For example, a library can have a forward declaration of an entity in the API file, and use the implementation file for the entity's definition. Putting the definition in an implementation file this way can reduce the dependencies for API file evaluation, improving compile time. This is commonly done with functions. For example: ``` library "MyLibrary"; fn DoSomething(); ``` ``` impl library "MyLibrary"; fn DoSomething() { ... } ``` ## Matching redeclarations of an entity In order to determine whether two redeclarations refer to the same entity, we apply the rules: - Two named declarations _declare the same entity_ if they have the same scope and the same name. This includes imported declarations. - When two named declarations declare the same entity, the second is said to be a _redeclaration_. - Two owned declarations _differ_ if they don't syntactically match. - Otherwise, if one is a non-owned `extern library` declaration, declarations differ if they don't match semantically. - The program is invalid if it contains two declarations of the same entity that differ. ```carbon class A { // This function will be redeclared in order to provide a definition. fn F(n: i32); } // ✅ Valid: The declaration matches syntactically. fn A.F(n: i32) {} // ❌ Invalid: The parameter name differs. fn A.F(m: i32) {} // ❌ Invalid: The parameter type differs syntactically. fn A.F(n: (i32)) {} ``` ### Details TODO: Figure out what details to pull from [#3762](https://github.com/carbon-language/carbon-lang/pull/3762) and [#3763](https://github.com/carbon-language/carbon-lang/pull/3763). ## `extern` and `extern library` There are two forms of the `extern` modifier: - On an owning declaration, `extern` limits access to the definition. - The entity must be directly imported in order to use of the definition. - An `extern library` declaration is optional. - On a non-owning declaration, `extern library` allows references to an entity without depending on the owning library. - The library name indicates where the entity is defined. - This can be used to improve build performance, such as by splitting out a declaration in order to reduce a library's dependencies. For example, a use of both might look like: ``` library "owner"; // This `import` is required due to the `extern library`, but we also make use // of `MyClassFactory` below. This is a circular use of `MyClass` that we // couldn't split between libraries without `extern`. import library "factory"; extern class MyClass { fn Make() -> MyClass* { return MyClassFactory(); } var val: i32 = 0; } ``` ``` library "factory"; // Declares `MyClass` so that `MyClassFactory` can return it. extern library "owner" class MyClass; fn MyClassFactory(val: i32) -> MyClass*; ``` ``` impl library "factory"; // Imports the definition of `MyClass`. import library "owner"; extern fn MyClassFactory(val: i32) -> MyClass* { var c: MyClass* = new MyClass(); c->val = val; return c; } ``` ### Valid scopes for `extern` The `extern` modifier is only valid on namespace-scoped entities, including in the file scope. In other words, `class C { extern fn F(); }` is invalid. ### Effect on indirect imports Indirect imports won't see the definition of an `extern` entity. We expect this to primarily affect return types of functions. If an incomplete type is encountered this way, it can be resolved by directly importing the definition. For example: ``` library "type"; // Because this is `extern`, the definition must be directly imported. extern class MyType { var x: i32 } ``` ``` library "make_type"; import library "type"; // Here we have a function which returns the type. fn MakeMyType() -> MyType*; ``` ``` library "invalid_use"; import library "make_type"; fn InvalidUse() -> i32 { // ❌ Invalid: `MyType` is incomplete because it's `extern` and not directly // imported. `x` cannot be accessed. return MakeMyType()->x; } ``` ``` library "valid_use"; import library "make_type"; // ✅ Valid: By directly importing the definition, we can now access `x`. import library "type"; fn ValidUse() -> i32 { return MakeMyType()->x; } ``` ## Alternatives considered - [Other modifier keyword merging approaches](/proposals/p3762.md#other-modifier-keyword-merging-approaches) - [No `extern` keyword](/proposals/p3762.md#no-extern-keyword) - [Looser restrictions on declarations](/proposals/p3762.md#looser-restrictions-on-declarations) - [`extern` naming](/proposals/p3762.md#extern-naming) - [Default `extern` to private](/proposals/p3762.md#default-extern-to-private) - [Opaque types](/proposals/p3762.md#opaque-types) - [Require a library provide its own `extern` declarations](/proposals/p3762.md#require-a-library-provide-its-own-extern-declarations) - [Allow cross-package `extern` declarations](/proposals/p3762.md#allow-cross-package-extern-declarations) - [Use a partially or fully semantic rule](/proposals/p3763.md#use-a-partially-or-fully-semantic-rule) - [Use package-wide name poisoning](/proposals/p3763.md#use-package-wide-name-poisoning) - [Allow shadowing in implementation file after use in API file](/proposals/p3763.md#allow-shadowing-in-implementation-file-after-use-in-api-file) - [Allow multiple non-owning declarations, remove the import requirement, or both](/proposals/p3980.md#allow-multiple-non-owning-declarations-remove-the-import-requirement-or-both) - [Total number of allowed declarations (owning and non-owning)](/proposals/p3980.md#total-number-of-allowed-declarations-owning-and-non-owning) - [Do not restrict the number of forward declarations](/proposals/p3980.md#do-not-restrict-the-number-of-forward-declarations) - [Allow up to two declarations total](/proposals/p3980.md#allow-up-to-two-declarations-total) - [Allow up to four declarations total](/proposals/p3980.md#allow-up-to-four-declarations-total) - [Don't require a modifier on the owning declarations](/proposals/p3980.md#dont-require-a-modifier-on-the-owning-declarations) - [Only require `extern` on the first owning declaration](/proposals/p3980.md#only-require-extern-on-the-first-owning-declaration) - [Separate require-direct-import from non-owning declarations](/proposals/p3980.md#separate-require-direct-import-from-non-owning-declarations) - [Other `extern` syntaxes](/proposals/p3980.md#other-extern-syntaxes) - [Have types with `extern` members re-export them](/proposals/p3980.md#have-types-with-extern-members-re-export-them) - [Require syntactic matching for `extern library` declarations](/proposals/p3980.md#require-syntactic-matching-for-extern-library-declarations) ## References - Proposal [#3762: Merging forward declarations](https://github.com/carbon-language/carbon-lang/pull/3762) - Proposal [#3763: Matching redeclarations](https://github.com/carbon-language/carbon-lang/pull/3763) - Proposal [#3980: Singular `extern` declarations](https://github.com/carbon-language/carbon-lang/pull/3980) ================================================ FILE: docs/design/expressions/README.md ================================================ # Expressions ## Table of contents - [Overview](#overview) - [Precedence](#precedence) - [Names](#names) - [Unqualified names](#unqualified-names) - [Qualified names and member access](#qualified-names-and-member-access) - [Operators](#operators) - [Suffix operators](#suffix-operators) - [Conversions and casts](#conversions-and-casts) - [`if` expressions](#if-expressions) - [Numeric type literal expressions](#numeric-type-literal-expressions) - [Alternatives considered](#alternatives-considered) - [References](#references) ## Overview Expressions are the portions of Carbon syntax that produce values. Because types in Carbon are values, this includes anywhere that a type is specified. ``` fn Foo(a: i32*) -> i32 { return *a; } ``` Here, the parameter type `i32*`, the return type `i32`, and the operand `*a` of the `return` statement are all expressions. ## Precedence Expressions are interpreted based on a partial [precedence ordering](https://en.wikipedia.org/wiki/Order_of_operations). Expression components which lack a relative ordering must be disambiguated by the developer, for example by adding parentheses; otherwise, the expression will be invalid due to ambiguity. Precedence orderings will only be added when it's reasonable to expect most developers to understand the precedence without parentheses. The precedence diagram is defined thusly: ```mermaid %%{init: {'themeVariables': {'fontFamily': 'monospace'}}}%% graph BT parens["(...)"] braces["{...}"] click braces "https://github.com/carbon-language/carbon-lang/blob/trunk/docs/design/classes.md#literals" unqualifiedName["x"] click unqualifiedName "https://github.com/carbon-language/carbon-lang/blob/trunk/docs/design/expressions/README.md#unqualified-names" top((" ")) suffixOps{"x.y x.(...) x->y x->(...) x(...) x[y]"} click suffixOps "https://github.com/carbon-language/carbon-lang/blob/trunk/docs/design/expressions/README.md#suffix-operators" qualifiedType["const T partial T"] click pointer-type "https://github.com/carbon-language/carbon-lang/blob/trunk/docs/design/expressions/type_operators.md" pointerType{"T*"} click pointer-type "https://github.com/carbon-language/carbon-lang/blob/trunk/docs/design/expressions/type_operators.md" pointer{"*x &x"} click pointer "https://github.com/carbon-language/carbon-lang/blob/trunk/docs/design/expressions/pointer.md" negation["-x"] click negation "https://github.com/carbon-language/carbon-lang/blob/trunk/docs/design/expressions/arithmetic.md" complement["^x"] click complement "https://github.com/carbon-language/carbon-lang/blob/trunk/docs/design/expressions/bitwise.md" incDec["++x; --x;"] click incDec "https://github.com/carbon-language/carbon-lang/blob/trunk/docs/design/assignment.md" unary((" ")) as["x as T"] click as "https://github.com/carbon-language/carbon-lang/blob/trunk/docs/design/expressions/implicit_conversions.md" multiplication>"x * y x / y"] click multiplication "https://github.com/carbon-language/carbon-lang/blob/trunk/docs/design/expressions/arithmetic.md" addition>"x + y x - y"] click addition "https://github.com/carbon-language/carbon-lang/blob/trunk/docs/design/expressions/arithmetic.md" modulo["x % y"] click modulo "https://github.com/carbon-language/carbon-lang/blob/trunk/docs/design/expressions/arithmetic.md" bitwise_and>"x & y"] bitwise_or>"x | y"] bitwise_xor>"x ^ y"] click bitwise_and "https://github.com/carbon-language/carbon-lang/blob/trunk/docs/design/expressions/bitwise.md" click bitwise_or "https://github.com/carbon-language/carbon-lang/blob/trunk/docs/design/expressions/bitwise.md" click bitwise_xor "https://github.com/carbon-language/carbon-lang/blob/trunk/docs/design/expressions/bitwise.md" shift["x << y x >> y"] click shift "https://github.com/carbon-language/carbon-lang/blob/trunk/docs/design/expressions/bitwise.md" binaryOps((" ")) where["T where R"] comparison["x == y x != y x < y x <= y x > y x >= y"] click comparison "https://github.com/carbon-language/carbon-lang/blob/trunk/docs/design/expressions/comparison_operators.md" not["not x"] click not "https://github.com/carbon-language/carbon-lang/blob/trunk/docs/design/expressions/logical_operators.md" logicalOperand((" ")) and>"x and y"] click and "https://github.com/carbon-language/carbon-lang/blob/trunk/docs/design/expressions/logical_operators.md" or>"x or y"] click or "https://github.com/carbon-language/carbon-lang/blob/trunk/docs/design/expressions/logical_operators.md" logicalExpression((" ")) ref["ref x"] if>"if x then y else z"] click if "https://github.com/carbon-language/carbon-lang/blob/trunk/docs/design/expressions/if.md" insideParens["(...)"] assignment["x = y; x $= y;"] click assignment "https://github.com/carbon-language/carbon-lang/blob/trunk/docs/design/assignment.md" expressionStatement["x;"] top --> parens & braces & unqualifiedName suffixOps --> top qualifiedType --> suffixOps pointerType --> qualifiedType pointer --> suffixOps negation & complement & incDec --> pointer unary --> pointerType & negation & complement %% Use a longer arrow here to put `not` next to other unary operators not ---> suffixOps %% `as` at the same level as `where` and comparisons as -----> unary multiplication & modulo & bitwise_and & bitwise_or & bitwise_xor & shift --> unary addition --> multiplication binaryOps --> addition & modulo & bitwise_and & bitwise_or & bitwise_xor & shift where --> binaryOps comparison --> binaryOps logicalOperand --> comparison & not %% This helps group `and` and `or` together classDef hidden display: none; HIDDEN:::hidden ~~~ logicalOperand and & or --> logicalOperand logicalExpression --> as & where & and & or ref & expressionStatement --> logicalExpression if ---> ref insideParens & assignment --> if ``` The diagram's attributes are: - Each non-empty node represents a precedence group. Empty circles are used to simplify the graph, and do not represent a precedence group. - When an expression is composed from different precedence groups, the interpretation is determined by the precedence edges: - A precedence edge A --> B means that A is lower precedence than B, so A can contain B without parentheses. For example, `or --> not` means that `not x or y` is treated as `(not x) or y`. - Precedence edges are transitive. For example, `or --> == --> as` means that `or` is lower precedence than `as`. - When a binary operator expression is composed from a single precedence group, the interpretation is determined by the [associativity](https://en.wikipedia.org/wiki/Operator_associativity) of the precedence group: ```mermaid graph TD non["Non-associative"] left>"Left associative"] ``` - For example, `+` and `-` are left-associative and in the same precedence group, so `a + b + c - d` is treated as `((a + b) + c) - d`. - Note that in Carbon, we currently only have left-associative operators. Unlike C++ and other languages, [assignment](/docs/design/assignment.md) isn't a right-associative operator, it uses its own statement. - When a unary operator expression is composed from a single precedence group, it can allow unparenthesized repetition or not: ```mermaid graph TD non["Non-repeating"] repeating{"Repeating"} ``` This is analogous to associativity for binary operators. ## Names ### Unqualified names An _unqualified name_ is a [word](../lexical_conventions/words.md) that is not a keyword and is not preceded by a period (`.`). **TODO:** Name lookup rules for unqualified names. ### Qualified names and member access A _qualified name_ is a word that appears immediately after a period or rightward arrow. Qualified names appear in the following contexts: - [Designators](/docs/design/classes.md#literals): `.` _word_ - [Simple member access expressions](member_access.md): _expression_ `.` _word_ - [Simple pointer member access expressions](member_access.md): _expression_ `->` _word_ ``` var x: auto = {.hello = 1, .world = 2}; ^^^^^ ^^^^^ qualified name ^^^^^^ ^^^^^^ designator x.hello = x.world; ^^^^^ ^^^^^ qualified name ^^^^^^^ ^^^^^^^ member access expression x.hello = (&x)->world; ^^^^^ qualified name ^^^^^^^^^^^ pointer member access expression ``` Qualified names refer to members of an entity determined by the context in which the expression appears. For a member access, the entity is named by the expression preceding the period. In a struct literal, the entity is the struct type. For example: ``` package Foo; namespace N; fn N.F() {} fn G() { // Same as `(Foo.N).F()`. // `Foo.N` names namespace `N` in package `Foo`. // `(Foo.N).F` names function `F` in namespace `N`. Foo.N.F(); } // `.n` refers to the member `n` of `{.n: i32}`. fn H(a: {.n: i32}) -> i32 { // `a.n` is resolved to the member `{.n: i32}.n`, // and names the corresponding subobject of `a`. return a.n; } fn J() { // `.n` refers to the member `n of `{.n: i32}`. H({.n = 5 as i32}); } ``` Member access expressions associate left-to-right. If the member name is more complex than a single _word_, a compound member access expression can be used, with parentheses around the member name: - _expression_ `.` `(` _expression_ `)` - _expression_ `->` `(` _expression_ `)` ``` interface I { fn F[self: Self](); } class X {} impl X as I { fn F[self: Self]() {} } // `x.I.F()` would mean `(x.I).F()`. fn Q(x: X) { x.(I.F)(); } ``` Either simple or compound member access can be part of a _pointer_ member access expression when an `->` is used instead of a `.`, where _expression_ `->` _..._ is syntactic sugar for `(` `*` _expression_ `)` `.` _..._. ## Operators Most expressions are modeled as operators: | Category | Operator | Syntax | Function | | ---------- | ----------------------------------- | --------- | --------------------------------------------------------------------- | | Call | `()` (unary) | `x(...)` | Function call: the value returned by calling the function `x`. | | Call | [`[]`](indexing.md) (unary) | `x[y]` | Subscripting or indexing: returns the element `y` of `x`. | | Pointer | [`*`](pointer_operators.md) (unary) | `*x` | Pointer dereference: the object pointed to by `x`. | | Pointer | [`&`](pointer_operators.md) (unary) | `&x` | Address-of: a pointer to the object `x`. | | Arithmetic | [`-`](arithmetic.md) (unary) | `-x` | The negation of `x`. | | Bitwise | [`^`](bitwise.md) (unary) | `^x` | The bitwise complement of `x`. | | Arithmetic | [`+`](arithmetic.md) | `x + y` | The sum of `x` and `y`. | | Arithmetic | [`-`](arithmetic.md) (binary) | `x - y` | The difference of `x` and `y`. | | Arithmetic | [`*`](arithmetic.md) | `x * y` | The product of `x` and `y`. | | Arithmetic | [`/`](arithmetic.md) | `x / y` | `x` divided by `y`, or the quotient thereof. | | Arithmetic | [`%`](arithmetic.md) | `x % y` | `x` modulo `y`. | | Bitwise | [`&`](bitwise.md) | `x & y` | The bitwise AND of `x` and `y`. | | Bitwise | [`\|`](bitwise.md) | `x \| y` | The bitwise OR of `x` and `y`. | | Bitwise | [`^`](bitwise.md) (binary) | `x ^ y` | The bitwise XOR of `x` and `y`. | | Bitwise | [`<<`](bitwise.md) | `x << y` | `x` bit-shifted left `y` places. | | Bitwise | [`>>`](bitwise.md) | `x >> y` | `x` bit-shifted right `y` places. | | Conversion | [`as`](as_expressions.md) | `x as T` | Converts the value `x` to the type `T`. | | Comparison | [`==`](comparison_operators.md) | `x == y` | Equality: `true` if `x` is equal to `y`. | | Comparison | [`!=`](comparison_operators.md) | `x != y` | Inequality: `true` if `x` is not equal to `y`. | | Comparison | [`<`](comparison_operators.md) | `x < y` | Less than: `true` if `x` is less than `y`. | | Comparison | [`<=`](comparison_operators.md) | `x <= y` | Less than or equal: `true` if `x` is less than or equal to `y`. | | Comparison | [`>`](comparison_operators.md) | `x > y` | Greater than: `true` if `x` is greater than to `y`. | | Comparison | [`>=`](comparison_operators.md) | `x >= y` | Greater than or equal: `true` if `x` is greater than or equal to `y`. | | Logical | [`and`](logical_operators.md) | `x and y` | A short-circuiting logical AND: `true` if both operands are `true`. | | Logical | [`or`](logical_operators.md) | `x or y` | A short-circuiting logical OR: `true` if either operand is `true`. | | Logical | [`not`](logical_operators.md) | `not x` | Logical NOT: `true` if the operand is `false`. | The binary arithmetic and bitwise operators also have [compound assignment](/docs/design/assignment.md) forms. These are statements rather than expressions, and do not produce a value. ## Suffix operators These operators act like unary postfix operators for purposes of precedence: - [Member access operators](member_access.md), like `x.y` and the dereferencing variant `x->y`, only have an expression on their left-hand side. The right-hand side is a name. - The [compound member access operators](member_access.md), `x.(...)` and `x->(...)`, have an expression as their second operand, but put that expression in parentheses and so it doesn't participate in the precedence considerations of its first operand. - The [indexing operator](indexing.md), `x[y]`, similarly puts its second operand in matching square brackets. - The call operator, `x(...)`, takes a comma-separated list of arguments, but again puts them in parentheses that clearly separate them for precedence purposes. ## Conversions and casts When an expression appears in a context in which an expression of a specific type is expected, [implicit conversions](implicit_conversions.md) are applied to convert the expression to the target type. Expressions can also be converted to a specific type using an [`as` expression](as_expressions.md). ``` fn Bar(n: i32); fn Baz(n: i64) { // OK, same as Bar(n as i32) Bar(n); } ``` ## `if` expressions An [`if` expression](if.md) chooses between two expressions. ``` fn Run(args: Span(StringView)) { var file: StringView = if args.size() > 1 then args[1] else "/dev/stdin"; } ``` `if` expressions are analogous to `?:` ternary expressions in C and C++. ## Numeric type literal expressions Carbon's syntax provides a simple way to represent different types of integers and floating-point numbers. Each type is identified with a keyword-like syntax, prefixed with either `i`, `u`, or `f` followed by a multiple of 8, representing the size in bits of the data type. These are referred to as [numeric type literals](literals.md#numeric-type-literals). ## Alternatives considered Other expression documents will list more alternatives; this lists alternatives not noted elsewhere. - [Total order](/proposals/p0555.md#total-order) - [Different precedence for different operands](/proposals/p0555.md#different-precedence-for-different-operands) - [Require less than a partial order](/proposals/p0555.md#require-less-than-a-partial-order) ## References Other expression documents will list more references; this lists references not noted elsewhere. - Proposal [#555: Operator precedence](https://github.com/carbon-language/carbon-lang/pull/555). ================================================ FILE: docs/design/expressions/arithmetic.md ================================================ # Arithmetic ## Table of contents - [Overview](#overview) - [Precedence and associativity](#precedence-and-associativity) - [Built-in types](#built-in-types) - [Integer types](#integer-types) - [Overflow and other error conditions](#overflow-and-other-error-conditions) - [Floating-point types](#floating-point-types) - [Strings](#strings) - [Extensibility](#extensibility) - [Alternatives considered](#alternatives-considered) - [References](#references) ## Overview Carbon provides a conventional set of arithmetic operators: ``` var a: i32 = 5; var b: i32 = 3; // -5 var negation: i32 = -a; // 8 var sum: i32 = a + b; // 2 var difference: i32 = a - b; // 15 var product: i32 = a * b; // 1 var quotient: i32 = a / b; // 2 var remainder: i32 = a % b; ``` These operators have predefined meanings for some of Carbon's [built-in types](#built-in-types). User-defined types can define the meaning of these operations by [implementing an interface](#extensibility) provided as part of the Carbon standard library. ## Precedence and associativity ```mermaid %%{init: {'themeVariables': {'fontFamily': 'monospace'}}}%% graph BT negation["-x"] multiplication>"x * y x / y"] addition>"x + y x - y"] modulo["x % y"] multiplication & modulo --> negation addition --> multiplication ``` [Instructions for reading this diagram.](README.md#precedence) Binary `+` and `-` can be freely mixed, and are left-associative. ``` // -2, same as `((1 - 2) + 3) - 4`. var n: i32 = 1 - 2 + 3 - 4; ``` Binary `*` and `/` can be freely mixed, and are left-associative. ``` // 0.375, same as `((1.0 / 2.0) * 3.0) / 4.0`. var m: f32 = 1.0 / 2.0 * 3.0 / 4.0; ``` Unary `-` has higher precedence than binary `*`, `/`, and `%`. Binary `*` and `/` have higher precedence than binary `+` and `-`. ``` // 5, same as `(-1) + ((-2) * (-3))`. var x: i32 = -1 + -2 * -3; // Error, parentheses required: no precedence order between `+` and `%`. var y: i32 = 2 + 3 % 5; ``` ## Built-in types For binary operators, if the operands have different built-in types, they are converted as follows: - If the types are `uN` and `uM`, or they are `iN` and `iM`, the operands are converted to the larger type. - If one type is `iN` and the other type is `uM`, and `M` < `N`, the `uM` operand is converted to `iN`. - If one type is `fN` and the other type is `iM` or `uM`, and there is an [implicit conversion](implicit_conversions.md#data-types) from the integer type to `fN`, then the integer operand is converted to `fN`. More broadly, if one operand is of built-in type and the other operand can be implicitly converted to that type, then it is, unless that behavior is [overridden](#extensibility). A built-in arithmetic operation is performed if, after the above conversion step, the operands have the same built-in type. The result type is that type. The result type is never wider than the operands, and the conversions applied to the operands are always lossless, so arithmetic between a wider unsigned integer type and a narrower signed integer is not defined. Although the conversions are always lossless, the arithmetic may still [overflow](#overflow-and-other-error-conditions). ### Integer types Signed and unsigned integer types support all the arithmetic operators. Signed integer arithmetic produces the usual mathematical result. Unsigned integer arithmetic in `uN` wraps around modulo 2`N`. Division truncates towards zero. The result of the `%` operator is defined by the equation `a % b == a - (a / b) * b`. #### Overflow and other error conditions Integer arithmetic is subject to two classes of problems for which an operation has no representable result: - Overflow, where the resulting value is too large to be represented in the type, or, for `%`, when the implied multiplication overflows. - Division by zero. Unsigned integer arithmetic cannot overflow, but division by zero can still occur. **Note:** All arithmetic operators can overflow for signed integer types. For example, given a value `v: iN` that is the least possible value for its type, `-v`, `v + v`, `v - 1`, `v * 2`, `v / -1`, and `v % -1` all result in overflow. Signed integer overflow and signed or unsigned integer division by zero are programming errors: - In a development build, they will be caught immediately when they happen at runtime. - In a performance build, the optimizer can assume that such conditions don't occur. As a consequence, if they do, the behavior of the program is not defined. - In a hardened build, overflow and division by zero do not result in undefined behavior. On overflow and division by zero, either the program will be aborted, or the arithmetic will evaluate to a mathematically incorrect result, such as a two's complement result or zero. The program might not in all cases be aborted immediately -- for example, multiple overflow checks might be combined into one -- but no control flow or memory access that depends on the value will be performed. **TODO:** Unify the description of these programming errors with those of bit-shift domain errors, document the behavior in a common place and link to it from here. **TODO:** In a hardened build, should we prefer to trap on overflow, give a two's complement result, or produce zero? Using zero may defeat some classes of exploit, but comes at a code size and performance cost. ### Floating-point types Floating-point types support all the arithmetic operators other than `%`. Floating-point types in Carbon have IEEE 754 semantics, use the round-to-nearest rounding mode, and do not set any floating-point exception state. Because floating-point arithmetic follows IEEE 754 rules: overflow results in ±∞, and division by zero results in either ±∞ or, for 0.0 / 0.0, a quiet NaN. ### Strings **TODO:** Decide whether strings are built-in types, and whether they support `+` for concatenation. See [#457](https://github.com/carbon-language/carbon-lang/issues/457). ## Extensibility Arithmetic operators can be provided for user-defined types by implementing the following family of interfaces: ``` // Unary `-`. interface Negate { default let Result:! type = Self; fn Op[self: Self]() -> Result; } ``` ``` // Binary `+`. interface AddWith(U:! type) { default let Result:! type = Self; fn Op[self: Self](other: U) -> Result; } constraint Add { extend AddWith(Self) where .Result = Self; } ``` ``` // Binary `-`. interface SubWith(U:! type) { default let Result:! type = Self; fn Op[self: Self](other: U) -> Result; } constraint Sub { extend SubWith(Self) where .Result = Self; } ``` ``` // Binary `*`. interface MulWith(U:! type) { default let Result:! type = Self; fn Op[self: Self](other: U) -> Result; } constraint Mul { extend MulWith(Self) where .Result = Self; } ``` ``` // Binary `/`. interface DivWith(U:! type) { default let Result:! type = Self; fn Op[self: Self](other: U) -> Result; } constraint Div { extend DivWith(Self) where .Result = Self; } ``` ``` // Binary `%`. interface ModWith(U:! type) { default let Result:! type = Self; fn Op[self: Self](other: U) -> Result; } constraint Mod { extend ModWith(Self) where .Result = Self; } ``` Given `x: T` and `y: U`: - The expression `-x` is rewritten to `x.(Negate.Op)()`. - The expression `x + y` is rewritten to `x.(AddWith(U).Op)(y)`. - The expression `x - y` is rewritten to `x.(SubWith(U).Op)(y)`. - The expression `x * y` is rewritten to `x.(MulWith(U).Op)(y)`. - The expression `x / y` is rewritten to `x.(DivWith(U).Op)(y)`. - The expression `x % y` is rewritten to `x.(ModWith(U).Op)(y)`. Implementations of these interfaces are provided for built-in types as necessary to give the semantics described above. ## Alternatives considered - [Use a sufficiently wide result type to avoid overflow](/proposals/p1083.md#use-a-sufficiently-wide-result-type-to-avoid-overflow) - [Guarantee that the program never proceeds with an incorrect value after overflow](/proposals/p1083.md#guarantee-that-the-program-never-proceeds-with-an-incorrect-value-after-overflow) - [Guarantee that all integer arithmetic is two's complement](/proposals/p1083.md#guarantee-that-all-integer-arithmetic-is-twos-complement) - [Treat overflow as an error but don't optimize on it](/proposals/p1083.md#treat-overflow-as-an-error-but-dont-optimize-on-it) - [Don't let `Unsigned` arithmetic wrap](/proposals/p1083.md#dont-let-unsigned-arithmetic-wrap) - [Provide separate wrapping types](/proposals/p1083.md#provide-separate-wrapping-types) - [Do not provide an ordering or division for `uN`](/proposals/p1083.md#do-not-provide-an-ordering-or-division-for-un) - [Give unary `-` lower precedence](/proposals/p1083.md#give-unary---lower-precedence) - [Include a unary plus operator](/proposals/p1083.md#include-a-unary-plus-operator) - [Floating-point modulo operator](/proposals/p1083.md#floating-point-modulo-operator) - [Provide different division operators](/proposals/p1083.md#provide-different-division-operators) - [Use different division and modulo semantics](/proposals/p1083.md#use-different-division-and-modulo-semantics) - [Use different precedence groups for division and multiplication](/proposals/p1083.md#use-different-precedence-groups-for-division-and-multiplication) - [Use the same precedence group for modulo and multiplication](/proposals/p1083.md#use-the-same-precedence-group-for-modulo-and-multiplication) - [Use a different spelling for modulo](/proposals/p1083.md#use-a-different-spelling-for-modulo) ## References - Proposal [#1083: Arithmetic](https://github.com/carbon-language/carbon-lang/pull/1083) - Proposal [#1178: Rework operator interfaces](https://github.com/carbon-language/carbon-lang/pull/1178) ================================================ FILE: docs/design/expressions/as_expressions.md ================================================ # `as` expressions ## Table of contents - [Overview](#overview) - [Precedence and associativity](#precedence-and-associativity) - [Built-in types](#built-in-types) - [Data types](#data-types) - [Compatible types](#compatible-types) - [Extensibility](#extensibility) - [Alternatives considered](#alternatives-considered) - [References](#references) ## Overview An expression of one type can be explicitly cast to another type by using an `as` expression: ``` var n: i32 = Get(); var f: f32 = n as f32; ``` An `as` expression can be used to perform any implicit conversion, either when the context does not imply a destination type or when it is valuable to a reader of the code to make the conversion explicit. In addition, `as` expressions can perform safe conversions that nonetheless should not be performed implicitly, such as lossy conversions or conversions that lose capabilities or change the way a type would be interpreted. As guidelines, an `as` conversion should be permitted when: - The conversion is _complete_: it produces a well-defined output value for each input value. - The conversion is _unsurprising_: the resulting value is the expected value in the destination type. For example: - A conversion from `fM` to `iN` is not complete, because it is not defined for input values that are out of the range of the destination type, such as infinities or, if `N` is too small, large finite values. - A conversion from `iM` to `iN`, where `N` < `M`, is either not complete or not unsurprising, because there is more than one possible expected behavior for an input value that is not within the destination type, and those behaviors are not substantially the same -- we could perform two's complement wrapping, saturate, or produce undefined behavior analogous to arithmetic overflow. - A conversion from `iM` to `fN` can be unsurprising, because even though there may be a choice of which way to round, the possible values are substantially the same. It is possible for user-defined types to [extend](#extensibility) the set of valid explicit casts that can be performed by `as`. Such extensions are expected to follow these guidelines. ## Precedence and associativity `as` expressions are non-associative. ``` var b: bool = true; // OK var n: i32 = (b as i1) as i32; var m: auto = b as (bool as Hashable); // Error, ambiguous var m: auto = b as T as U; ``` **Note:** `b as (bool as Hashable)` is valid but not useful, because [the second operand of `as` is implicitly converted to type `type`](#extensibility). This expression therefore has the same interpretation as `b as bool`. **TODO:** We should consider making `as` expressions left-associative now that facet types have been removed from the language. The `as` operator has lower precedence than operators that visually bind tightly: - prefix symbolic operators - dereference (`*a`) - negation (`-a`) - complement (`~a`) - postfix symbolic operators - pointer type formation (`T*`), - function call (`a(...)`), - array indexing (`a[...]`), and - member access (`a.m`). The `as` operator has higher precedence than assignment and comparison. It is unordered with respect to binary arithmetic, bitwise operators, and unary `not`. ``` // OK var x: i32* as Eq; // OK, `x as (U*)` not `(x as U)*`. var y: auto = x as U*; var a: i32; var b: i32; // OK, `(a as i64) < ((*x) as i64)`. if (a as i64 < *x as i64) {} // Ambiguous: `(a + b) as i64` or `a + (b as i64)`? var c: i32 = a + b as i64; // Ambiguous: `(a as i64) + b` or `a as (i64 + b)`? var d: i32 = a as i64 + b; // OK, `(-a) as f64`, not `-(a as f64)`. // Unfortunately, the former is undefined if `a` is `i32.MinValue()`; // the latter is not. var u: f64 = -a as f64; // OK, `i32 as (GetType())`, not `(i32 as GetType)()`. var e: i32 as GetType(); ``` ## Built-in types ### Data types In addition to the [implicit conversions](implicit_conversions.md#data-types), the following numeric conversions are supported by `as`: - `iN`, `uN`, or `fN` -> `fM`, for any `N` and `M`. Values that cannot be exactly represented are suitably rounded to one of the two nearest representable values. Very large finite values may be rounded to an infinity. NaN values are converted to NaN values. - `bool` -> `iN` or `uN`. `false` converts to `0` and `true` converts to `1` (or to `-1` for `i1`). Conversions from numeric types to `bool` are not supported with `as`; instead of using `as bool`, such conversions can be performed with `!= 0`. Lossy conversions between `iN` or `uN` and `iM` or `uM` are not supported with `as`, and similarly conversions from `fN` to `iM` are not supported. **Future work:** Add mechanisms to perform these conversions. ### Compatible types The following conversion is supported by `as`: - `T` -> `U` if `T` is [compatible](../generics/terminology.md#compatible-types) with `U`. **Future work:** We may need a mechanism to restrict which conversions between adapters are permitted and which code can perform them. Some of the conversions permitted by this rule may only be allowed in certain contexts. ## Extensibility Explicit casts can be defined for user-defined types such as [classes](../classes.md) by implementing the `As` interface: ``` interface As(Dest:! type) { fn Convert[self: Self]() -> Dest; } ``` The expression `x as U` is rewritten to `x.(As(U).Convert)()`. **Note:** This rewrite causes the expression `U` to be implicitly converted to type `type`. The program is invalid if this conversion is not possible. ## Alternatives considered - [Allow `as` to perform some unsafe conversions](/proposals/p0845.md#allow-as-to-perform-some-unsafe-conversions) - [Allow `as` to perform two's complement truncation](/proposals/p0845.md#allow-as-to-perform-twos-complement-truncation) - [`as` only performs implicit conversions](/proposals/p0845.md#as-only-performs-implicit-conversions) - [Integer to bool conversions](/proposals/p0845.md#integer-to-bool-conversions) - [Bool to integer conversions](/proposals/p0845.md#bool-to-integer-conversions) ## References - [Implicit conversions in C++](https://en.cppreference.com/w/cpp/language/implicit_conversion) - Proposal [#845: `as` expressions](https://github.com/carbon-language/carbon-lang/pull/845). ================================================ FILE: docs/design/expressions/bitwise.md ================================================ # Bitwise and shift operators ## Table of contents - [Overview](#overview) - [Precedence and associativity](#precedence-and-associativity) - [Integer types](#integer-types) - [Integer constants](#integer-constants) - [Extensibility](#extensibility) - [Alternatives considered](#alternatives-considered) - [References](#references) ## Overview Carbon provides a conventional set of operators for operating on bits: ``` var a: u8 = 5; var b: u8 = 3; var c: i8 = -5; // 250 var complement: u8 = ^a; // 1 var bitwise_and: u8 = a & b; // 7 var bitwise_or: u8 = a | b; // 6 var bitwise_xor: u8 = a ^ b; // 40 var left_shift: u8 = a << b; // 2 var logical_right_shift: u8 = a >> 1; // -3 var arithmetic_right_shift: i8 = c >> 1; ``` These operators have [predefined meanings](#integer-types) for Carbon's integer types. User-defined types can define the meaning of these operations by [implementing an interface](#extensibility) provided as part of the Carbon standard library. ## Precedence and associativity ```mermaid %%{init: {'themeVariables': {'fontFamily': 'monospace'}}}%% graph BT complement["^x"] bitwise_and>"x & y"] bitwise_or>"x | y"] bitwise_xor>"x ^ y"] shift["x << y x >> y"] bitwise_and & bitwise_or & bitwise_xor & shift --> complement ``` [Instructions for reading this diagram.](README.md#precedence) Parentheses are required when mixing different bitwise and bit-shift operators. Binary `&`, `|`, and `^` are left-associative. The bit-shift operators `<<` and `>>` are non-associative. ``` // ✅ Same as (1 | 2) | 4, evaluates to 7. var a: i32 = 1 | 2 | 4; // ❌ Error, parentheses are required to distinguish between // (3 | 5) & 6, which evaluates to 6, and // 3 | (5 & 6), which evaluates to 7. var b: i32 = 3 | 5 & 6; // ❌ Error, parentheses are required to distinguish between // (1 << 2) << 3, which evaluates to 4 << 3 == 32, and // 1 << (2 << 3), which evaluates to 1 << 16 == 65536. var c: i32 = 1 << 2 << 3; // ❌ Error, can't repeat the `^` operator. Use `^(^4)` or simply `4`. var d: i32 = ^^4; ``` ## Integer types Bitwise and bit-shift operators are supported for Carbon's built-in integer types, and, unless that behavior is [overridden](#extensibility), for types that can be implicitly converted to integer types, as follows: For binary bitwise operators, if one operand has an integer type and the other operand can be implicitly converted to that type, then it is. If both operands are of integer type, this results in the following conversions: - If the types are `uN` and `uM`, or they are `iN` and `iM`, the operands are converted to the larger type. - If one type is `iN` and the other type is `uM`, and `M` < `N`, the `uM` operand is converted to `iN`. A built-in binary bitwise `&`, `|`, or `^` operation is performed if, after the above conversion step, the operands have the same integer type. The result type is that type, and the result value is produced by applying the relevant operation -- AND, OR, or XOR -- to each pair of corresponding bits in the input, including the sign bit for a signed integer type. A built-in complement operation is performed if the operand can be implicitly converted to an integer type. The result type is that type, and the result value is produced by flipping all bits in the input, including the sign bit for a signed integer type. `^a` is equivalent to `a ^ x`, where `x` is the all-one-bits value of the same type as `a`. A built-in bit-shift operation is performed if both operands are, or can be implicitly converted to, integer types. The result type is the converted type of the first operand. The result value is produced by shifting the first operand left for `<<` or right for `>>` a number of positions equal to the second operand. Vacant positions are filled with `0` bits, except for a right shift where the first operand is of a signed type and has a negative value, in which case they are filled with `1` bits. For the purposes of bit-shifts, bits are ordered by significance, with the most significant bit being the leftmost bit and the least significant bit being the rightmost bit. As a consequence, in the absence of overflow a left shift is equivalent to a multiplication by a power of 2 and a right shift is equivalent to a division by a power of two, rounding downwards. The second operand of a bit-shift is required to be between zero (inclusive) and the bit-width of the first operand (exclusive); it is a programming error if the second operand is not within that range. - In a development build, they will be caught immediately when they happen at runtime. - In a performance build, the optimizer may assume that this programming error does not occur. - In a hardened build, the result will have well the defined behavior of either aborting the program or performing a shift of an unspecified number of bits, which if wider than the first operand will result in `0` or `-1`. In the case where the program is aborted, the program might not in all cases be aborted immediately -- for example, multiple checks might be combined into one -- but no control flow or memory access that depends on the value will be performed. **TODO:** Unify the description of these programming errors with those of arithmetic overflow, document the behavior in a common place and link to it from here. ## Integer constants These operations can also be applied to a pair of integer constants, or to an integer constant and a value of integer type, as follows: - If any binary bitwise or bit-shift operator is applied to two integer constants, or the unary `^` operator is applied to an integer constant, the result is an integer constant. Integer constants are treated as having infinitely many high-order bits, where all but finitely many of those bits are sign bits. For example, `-1` comprises infinitely many `1` bits. Note that there is no difference between an arithmetic and a logical right shift on an integer constant, because every bit always has a higher-order bit to shift from. - It is easy to produce extremely large numbers by left-shifting an integer constant. For example, the binary representation of `1 << (1 << 1000)` is thought to be substantially larger than the total entropy in the observable universe. In practice, Carbon implementations will set a much lower limit on the largest integer constant that they support. - If a binary bitwise `&`, `|`, or `^` operation is applied to an integer constant and a value of an integer type to which the constant can be implicitly converted, the operand that is an integer constant is implicitly converted to the integer type and the computation is performed as described [above](#integer-types). - If the second operand of a bit-shift operator is an integer constant and the first operand is not, and the second operand is between 0 (inclusive) and the bit-width of the first operand (exclusive), the integer constant is converted to an integer type that can hold its value and the computation is performed as described above. Other operations involving integer constants are invalid. For example, a bitwise `&` between a `u8` and an integer constant `500` is invalid because `500` doesn't fit into `u8`, and `1 << n` is invalid if `n` is an integer variable because we don't know what type to use to compute the result. Note that the unary `^` operator applied to a non-negative integer constant results in a negative integer constant, and the binary `^` operator gives a negative result if exactly one of the input operands was negative. For example, `^0 == -1` evaluates to `true`. ## Extensibility Bitwise and shift operators can be provided for user-defined types by implementing the following family of interfaces: ``` // Unary `^`. interface BitComplement { default let Result:! type = Self; fn Op[self: Self]() -> Result; } ``` ``` // Binary `&`. interface BitAndWith(U:! type) { default let Result:! type = Self; fn Op[self: Self](other: U) -> Result; } constraint BitAnd { extend BitAndWith(Self) where .Result = Self; } ``` ``` // Binary `|`. interface BitOrWith(U:! type) { default let Result:! type = Self; fn Op[self: Self](other: U) -> Result; } constraint BitOr { extend BitOrWith(Self) where .Result = Self; } ``` ``` // Binary `^`. interface BitXorWith(U:! type) { default let Result:! type = Self; fn Op[self: Self](other: U) -> Result; } constraint BitXor { extend BitXorWith(Self) where .Result = Self; } ``` ``` // Binary `<<`. interface LeftShiftWith(U:! type) { default let Result:! type = Self; fn Op[self: Self](other: U) -> Result; } constraint LeftShift { extend LeftShiftWith(Self) where .Result = Self; } ``` ``` // Binary `>>`. interface RightShiftWith(U:! type) { default let Result:! type = Self; fn Op[self: Self](other: U) -> Result; } constraint RightShift { extend RightShiftWith(Self) where .Result = Self; } ``` Given `x: T` and `y: U`: - The expression `^x` is rewritten to `x.(BitComplement.Op)()`. - The expression `x & y` is rewritten to `x.(BitAndWith(U).Op)(y)`. - The expression `x | y` is rewritten to `x.(BitOrWith(U).Op)(y)`. - The expression `x ^ y` is rewritten to `x.(BitXorWith(U).Op)(y)`. - The expression `x << y` is rewritten to `x.(LeftShiftWith(U).Op)(y)`. - The expression `x >> y` is rewritten to `x.(RightShiftWith(U).Op)(y)`. Implementations of these interfaces are provided for built-in types as necessary to give the semantics described above. ## Alternatives considered - [Use different symbols for bitwise operators](/proposals/p1191.md#use-different-symbols-for-bitwise-operators) - [Provide different operators for arithmetic and logical shifts](/proposals/p1191.md#provide-different-operators-for-arithmetic-and-logical-shifts) - [Provide rotate operators](/proposals/p1191.md#provide-rotate-operators) - [Guarantee the behavior of large shifts](/proposals/p1191.md#guarantee-behavior-of-large-shifts) - [Support shifting a constant by a variable](/proposals/p1191.md#support-shifting-a-constant-by-a-variable) ## References - Proposal [#1191: bitwise and shift operators](https://github.com/carbon-language/carbon-lang/pull/1191). ================================================ FILE: docs/design/expressions/comparison_operators.md ================================================ # Comparison operators ## Table of contents - [Overview](#overview) - [Details](#details) - [Precedence](#precedence) - [Associativity](#associativity) - [Built-in comparisons and implicit conversions](#built-in-comparisons-and-implicit-conversions) - [Consistency with implicit conversions](#consistency-with-implicit-conversions) - [Comparisons with constants](#comparisons-with-constants) - [Extensibility](#extensibility) - [Equality](#equality) - [Ordering](#ordering) - [Compatibility of equality and ordering](#compatibility-of-equality-and-ordering) - [Custom result types](#custom-result-types) - [Default implementations for basic types](#default-implementations-for-basic-types) - [Open questions](#open-questions) - [Alternatives considered](#alternatives-considered) - [References](#references) ## Overview Carbon provides equality and relational comparison operators, each with a standard mathematical meaning: | Category | Operator | Example | Mathematical meaning | Description | | ---------- | -------- | -------- | -------------------- | -------------------------- | | Equality | `==` | `x == y` | = | Equality or equal to | | Equality | `!=` | `x != y` | ≠ | Inequality or not equal to | | Relational | `<` | `x < y` | < | Less than | | Relational | `<=` | `x <= y` | ≤ | Less than or equal to | | Relational | `>` | `x > y` | > | Greater than | | Relational | `>=` | `x >= y` | ≥ | Greater than or equal to | Comparison operators all return a `bool`; they evaluate to `true` when the indicated comparison is true. All comparison operators are infix binary operators. These operators have predefined meanings for some of Carbon's [built-in types](#built-in-comparisons-and-implicit-conversions), as well as for simple ["data" types](#default-implementations-for-basic-types) like structs and tuples. User-defined types can define the meaning of these operations by [implementing an interface](#extensibility) provided as part of the Carbon standard library. ## Details ### Precedence The comparison operators are all at the same precedence level. This level is lower than operators used to compute (non-`bool`) values, higher than the [logical operators](logical_operators.md) `and` and `or`, and incomparable with the precedence of `not`. For example: ```carbon // ✅ Valid: precedence provides order of evaluation. if (n + m * 3 < n * n and 3 < m and m < 6) { ... } // The above is equivalent to: if (((n + (m * 3)) < (n * n)) and ((3 < m) and (m < 6))) { ... } // ❌ Invalid due to ambiguity: `(not a) == b` or `not (a == b)`? if (not a == b) { ... } // ❌ Invalid due to precedence: write `a == (not b)`. if (a == not b) { ... } // ❌ Invalid due to precedence: write `not (f < 5.0)`. if (not f < 5.0) { .... } ``` ### Associativity The comparison operators are non-associative. For example: ```carbon // ❌ Invalid: write `3 < m and m < 6`. if (3 < m < 6) { ... } // ❌ Invalid: write `a == b and b == c`. if (a == b == c) { ... } // ❌ Invalid: write `(m > 1) == (n > 1)`. if (m > 1 == n > 1) { ... } ``` ### Built-in comparisons and implicit conversions Built-in comparisons are permitted in three cases: 1. When both operands are of standard Carbon integer types (`Int(n)` or `Unsigned(n)`). 2. When both operands are of standard Carbon floating-point types (`Float(n)`). 3. When one operand is of floating-point type and the other is of integer type, if all values of the integer type can be exactly represented in the floating-point type. In each case, the result is the mathematically-correct answer. This applies even when comparing `Int(n)` with `Unsigned(m)`. For example: ```carbon // ✅ Valid: Fits case #1. The value of `compared` is `true` because `a` is less // than `b`, even though the result of a wrapping `i32` or `u32` comparison // would be `false`. fn Compare(a: i32, b: u32) -> bool { return a < b; } let compared: bool = Compare(-1, 4_000_000_000); // ❌ Invalid: Doesn't fit case #3 because `i64` values in general are not // exactly representable in the type `f32`. let float: f32 = 1.0e18; let integer: i64 = 1_000_000_000_000_000_000; let eq: bool = float == integer; ``` Comparisons involving integer and floating-point constants are not covered by these rules and are [discussed separately](#comparisons-with-constants). #### Consistency with implicit conversions We support the following [implicit conversions](implicit_conversions.md): - From `Int(n)` to `Int(m)` if `m > n`. - From `Unsigned(n)` to `Int(m)` or `Unsigned(m)` if `m > n`. - From `Float(n)` to `Float(m)` if `m > n`. - From `Int(n)` to `Float(m)` if `Float(m)` can represent all values of `Int(n)`. These rules can be summarized as: a type `T` can be converted to `U` if every value of type `T` is a value of type `U`. Implicit conversions are also supported from certain kinds of integer and floating-point constants to `Int(n)` and `Float(n)` types, if the constant can be represented in the type. All built-in comparisons can be viewed as performing implicit conversions on at most one of the operands in order to reach a suitable pair of identical or very similar types, and then performing a comparison on those types. The target types for these implicit conversions are, for each suitable value `n`: - `Int(n)` versus `Int(n)` - `Unsigned(n)` versus `Unsigned(n)` - `Int(n)` versus `Unsigned(n)` - `Unsigned(n)` versus `Int(n)` - `Float(n)` versus `Float(n)` There will in general be multiple combinations of implicit conversions that will lead to one of the above forms, but we will arrive at the same result regardless of which is selected, because all comparisons are mathematically correct and all implicit conversions are lossless. Implementations are expected to do whatever is most efficient: for example, for `u16 < i32` it is likely that the best choice would be to promote the `u16` to `i32`, not `u32`. Because we only ever convert at most one operand, we never use an intermediate type that is larger than both input types. For example, both `i32` and `f32` can be implicitly converted to `f64`, but we do not permit comparisons between `i32` and `f32` even though we could perform those comparisons in `f64`. If such comparisons were permitted, the results could be surprising: ```carbon // `i32` can exactly represent this value. var integer: i32 = 2_000_000_001; // This value is within the representable range for `f32`, but will be rounded // to 2_000_000_000.0 due to the limited precision of `f32`. var float: f32 = 2_000_000_001.0; // ❌ Invalid: `f32` cannot exactly represent all values of `i32`. if (integer == float) { ... } // ✅ Valid: An explicit cast to `f64` on either side makes the code valid, but // will compare unequal because `float` was rounded to 2_000_000_000.0 // but `integer` will convert to exactly 2_000_000_001.0. if (integer == float as f64) { ... } if (integer as f64 == float) { ... } ``` The two kinds of mixed-type comparison may be [less efficient](/proposals/p0702.md#performance) than the other kinds due to the slightly wider domain. Note that this approach diverges from C++, which would convert both operands to a common type first, sometimes performing a lossy conversion potentially giving an incorrect result, sometimes converting both operands, and sometimes using a wider type than either of the operand types. #### Comparisons with constants We permit the following comparisons involving constants: - A constant can be compared with a value of any type to which it can be implicitly converted. - Any two constants can be compared, even if there is no type that can represent both. As described in [implicit conversions](implicit_conversions.md#data-types), integer constants can be implicitly converted to any integer or floating-point type that can represent their value, and floating-point constants can be implicitly converted to any floating-point type that can represent their value. Note that this disallows comparisons between, for example, `i32` and an integer literal that cannot be represented in `i32`. Such comparisons would always be tautological. This decision should be revisited if it proves problematic in practice, for example in templated code where the literal is sometimes in range. ### Extensibility User-defined types can extend the behavior of the comparison operators by implementing interfaces. In this section, various properties are specified that such implementations "should" satisfy. These properties are not enforced in general, but the standard library might detect violations of some of them in some circumstances. These properties may be assumed by checked-generic code, resulting in unexpected behavior if they are violated. #### Equality Comparison operators can be provided for user-defined types by implementing the `EqWith` and `OrderedWith` interfaces. The `EqWith` interface is used to define the semantics of the `==` and `!=` operators for a given pair of types: ``` interface EqWith(U:! type) { fn Equal[self: Self](u: U) -> bool; default fn NotEqual[self: Self](u: U) -> bool { return not (self == u); } } constraint Eq { extend EqWith(Self); } ``` Given `x: T` and `y: U`: - The expression `x == y` calls `x.(EqWith(U).Equal)(y)`. - The expression `x != y` calls `x.(EqWith(U).NotEqual)(y)`. ``` class Path { private var drive: String; private var path: String; private fn CanonicalPath[self: Self]() -> String; impl as Eq { fn Equal[self: Self](other: Self) -> bool { return (self.drive, self.CanonicalPath()) == (other.drive, other.CanonicalPath()); } } } ``` The `EqWith` overload is selected without considering possible implicit conversions. To permit implicit conversions in the operands of an `==` overload, the [`like` operator](/docs/design/generics/details.md#like-operator-for-implicit-conversions) can be used: ``` class MyInt { var value: i32; fn Value[self: Self]() -> i32 { return self.value; } } impl i32 as ImplicitAs(MyInt); impl like MyInt as EqWith(like MyInt) { fn Equal[self: Self](other: Self) -> bool { return self.Value() == other.Value(); } } fn CompareBothWays(a: MyInt, b: i32, c: MyInt) -> bool { // OK, calls above implementation three times. return a == a and a != b and b == c; } ``` The behavior of `NotEqual` can be overridden separately from the behavior of `Equal` to support cases like floating-point NaN values, where two values can compare neither equal nor not-equal, and thus both functions would return `false`. However, an implementation of `EqWith` should _not_ allow both `Equal` and `NotEqual` to return `true` for the same pair of values. Additionally, these operations should have no observable side-effects. ``` impl like MyFloat as EqWith(like MyFloat) { fn Equal[self: MyFloat](other: MyFloat) -> bool { if (self.IsNaN() or other.IsNaN()) { return false; } return self.Representation() == other.Representation(); } fn NotEqual[self: MyFloat](other: MyFloat) -> bool { if (self.IsNaN() or other.IsNaN()) { return false; } return self.Representation() != other.Representation(); } } ``` Heterogeneous comparisons must be defined both ways around: ``` impl like MyInt as EqWith(like MyFloat); impl like MyFloat as EqWith(like MyInt); ``` **TODO:** Add an adapter to the standard library to make it easy to define the reverse comparison. #### Ordering The `OrderedWith` interface is used to define the semantics of the `<`, `<=`, `>`, and `>=` operators for a given pair of types. ``` choice Ordering { Less, Equivalent, Greater, Incomparable } interface OrderedWith(U:! type) { fn Compare[self: Self](u: U) -> Ordering; default fn Less[self: Self](u: U) -> bool { return self.Compare(u) == Ordering.Less; } default fn LessOrEquivalent[self: Self](u: U) -> bool { let c: Ordering = self.Compare(u); return c == Ordering.Less or c == Ordering.Equivalent; } default fn Greater[self: Self](u: U) -> bool { return self.Compare(u) == Ordering.Greater; } default fn GreaterOrEquivalent[self: Self](u: U) -> bool { let c: Ordering = self.Compare(u); return c == Ordering.Greater or c == Ordering.Equivalent; } } constraint Ordered { extend OrderedWith(Self); } // Ordering.Less < Ordering.Equivalent < Ordering.Greater. // Ordering.Incomparable is incomparable with all three. impl Ordering as Ordered; ``` **TODO:** Revise the above when we have a concrete design for enumerated types. Given `x: T` and `y: U`: - The expression `x < y` calls `x.(OrderedWith(U).Less)(y)`. - The expression `x <= y` calls `x.(OrderedWith(U).LessOrEquivalent)(y)`. - The expression `x > y` calls `x.(OrderedWith(U).Greater)(y)`. - The expression `x >= y` calls `x.(OrderedWith(U).GreaterOrEquivalent)(y)`. For example: ``` class MyWidget { var width: i32; var height: i32; fn Size[self: Self]() -> i32 { return self.width * self.height; } // Widgets are normally ordered by size. impl as Ordered { fn Compare[self: Self](other: Self) -> Ordering { return self.Size().(Ordered.Compare)(other.Size()); } } } fn F(a: MyWidget, b: MyWidget) -> bool { return a <= b; } ``` As for `EqWith`, the [`like` operator](/docs/design/generics/details.md#like-operator-for-implicit-conversions) can be used to permit implicit conversions when invoking a comparison, and heterogeneous comparisons must be defined both ways around: ``` fn ReverseOrdering(o: Ordering) -> Ordering { return Ordering.Equivalent.(Ordered.Compare)(o); } impl like MyInt as OrderedWith(like MyFloat); impl like MyFloat as OrderedWith(like MyInt) { fn Compare[self: Self](other: Self) -> Ordering { return Reverse(other.(OrderedWith(Self).Compare)(self)); } } ``` The default implementations of `Less`, `LessOrEquivalent`, `Greater`, and `GreaterOrEquivalent` can be overridden if a more efficient version can be implemented. The behaviors of such overrides should follow those of the above default implementations, and the members of an `OrderedWith` implementation should have no observable side-effects. `OrderedWith` implementations should be _transitive_. That is, given `V:! type`, `U:! OrderedWith(V)`, `T:! OrderedWith(U) & OrderedWith(V)`, `a: T`, `b: U`, `c: V`, then: - If `a <= b` and `b <= c` then `a <= c`, and moreover if either `a < b` or `b < c` then `a < c`. - If `a >= b` and `b >= c` then `a >= c`, and moreover if either `a > b` or `b > c` then `a > c`. - If `a` and `b` are equivalent, then `a.Compare(c) == b.Compare(c)`. Similarly, if `b` and `c` are equivalent, then `a.Compare(b) == a.Compare(c)`. `OrderedWith` implementations should also be _consistent under reversal_. That is, given types `T` and `U` where `T impls OrderedWith(U)` and `U impls OrderedWith(T)`, and values `a: T` and `b: U`: - If `a.(OrderedWith.Compare)(b)` is `Ordering.Greater`, then `b.(OrderedWith.Compare)(a)` is `Ordering.Less`, and the other way around. - Otherwise, `a.(OrderedWith.Compare)(b)` returns the same value as `b.(OrderedWith.Compare)(a)`. There is no expectation that an `Ordered` implementation be a total order, a weak order, or a partial order, and in particular the implementation for floating-point types is none of these because NaN values do not compare less than or equivalent to themselves. **TODO:** The standard library should provide a way to specify that an ordering is a weak, partial, or total ordering, and a way to request such an ordering in a checked generic. #### Compatibility of equality and ordering There is no requirement that a pair of types that implements `OrderedWith` also implements `EqWith`. If a pair of types does implement both, however, the equality relation provided by `x.(EqWith.Equal)(y)` should be a refinement of the equivalence relation provided by `x.(OrderedWith.Compare)(y) == Ordering.Equivalent`. #### Custom result types **TODO:** Support a lower-level extensibility mechanism that allows a result type other than `bool`. ### Default implementations for basic types In addition to being defined for standard Carbon numeric types, equality and relational comparisons are also defined for all "data" types: - [Tuples](../tuples.md) - [Struct types](../classes.md#struct-types) - [Classes implementing an interface that identifies them as data classes](../classes.md#interfaces-implemented-for-data-classes) Relational comparisons for these types provide a lexicographical ordering. In each case, the comparison is only available if it is supported by all element types. Because implicit conversions between data classes can reorder fields, the implementations for data classes do not permit implicit conversions on their arguments in general. Instead: - Equality comparisons are permitted between any two data classes that have the same _unordered set_ of field names, if each corresponding pair of fields has an `EqWith` implementation. Fields are compared in the order they appear in the left-hand operand. - Relational comparisons are permitted between any two data classes that have the same _ordered sequence_ of field names, if each corresponding pair of fields has an `OrderedWith` implementation. Fields are compared in order. Comparisons between tuples permit implicit conversions for either operand, but not both. ## Open questions The `bool` type should be treated as a choice type, and so should support equality comparisons and relational comparisons if and only if choice types do in general. That decision is left to a future proposal. ## Alternatives considered - [Alternative symbols](/proposals/p0702.md#alternative-symbols) - [Chained comparisons](/proposals/p0702.md#chained-comparisons-1) - [Convert operands like C++](/proposals/p0702.md#convert-operands-like-c) - [Provide a three-way comparison operator](/proposals/p0702.md#provide-a-three-way-comparison-operator) - [Allow comparisons as the operand of `not`](/proposals/p0702.md#allow-comparisons-as-the-operand-of-not) - [Rename `OrderedWith` to `ComparableWith`](/proposals/p1178.md#use-comparablewith-instead-of-orderedwith) ## References - Proposal [#702: Comparison operators](https://github.com/carbon-language/carbon-lang/pull/702) - Proposal [#1178: Rework operator interfaces](https://github.com/carbon-language/carbon-lang/pull/1178) - Issue [#710: Default comparison for data classes](https://github.com/carbon-language/carbon-lang/issues/710) ================================================ FILE: docs/design/expressions/if.md ================================================ # `if` expressions ## Table of contents - [Overview](#overview) - [Syntax](#syntax) - [Semantics](#semantics) - [Finding a common type](#finding-a-common-type) - [Symmetry](#symmetry) - [Same type](#same-type) - [Implicit conversions](#implicit-conversions) - [Alternatives considered](#alternatives-considered) - [References](#references) ## Overview An `if` expression is an expression of the form: > `if` _condition_ `then` _value1_ `else` _value2_ The _condition_ is converted to a `bool` value in the same way as the condition of an `if` statement. > **Note:** These conversions have not yet been decided. The _value1_ and _value2_ are implicitly converted to their [common type](#finding-a-common-type), which is the type of the `if` expression. ## Syntax `if` expressions have very low precedence, and cannot appear as the operand of any operator, except as the right-hand operand in an assignment. They can appear in other context where an expression is permitted, such as within parentheses, as the operand of a `return` statement, as an initializer, or in a comma-separated list such as a function call. The _value1_ and _value2_ expressions are arbitrary expressions, and can themselves be `if` expressions. _value2_ extends as far to the right as possible. An `if` expression can be parenthesized if the intent is for _value2_ to end earlier. ``` // OK, same as `if cond then (1 + 1) else (2 + (4 * 6))` var a: i32 = if cond then 1 + 1 else 2 + 4 * 6; // OK var b: i32 = (if cond then 1 + 1 else 2) + 4 * 6; ``` An `if` keyword at the start of a statement is always interpreted as an [`if` statement](/docs/design/control_flow/conditionals.md), never as an `if` expression, even if it is followed eventually by a `then` keyword. ## Semantics The converted _condition_ is evaluated. If it evaluates to `true`, then the converted _value1_ is evaluated and its value is the result of the expression. Otherwise, the converted _value2_ is evaluated and its value is the result of the expression. ## Finding a common type The common type of two types `T` and `U` is `(T as CommonType(U)).Result`, where `CommonType` is the `Carbon.CommonType` constraint. `CommonType` is notionally defined as follows: ``` constraint CommonType(U:! CommonTypeWith(Self)) { extend CommonTypeWith(U) where .Result == U.Result; } ``` The actual definition is a bit more complex than this, as described in [symmetry](#symmetry). The interface `CommonTypeWith` is used to customize the behavior of `CommonType`: ``` interface CommonTypeWith(U:! type) { let Result:! type where Self impls ImplicitAs(.Self) and U impls ImplicitAs(.Self); } ``` The implementation `A as CommonTypeWith(B)` specifies the type that `A` would like to result from unifying `A` and `B` as its `Result`. _Note:_ It is required that both types implicitly convert to the common type. Some blanket `impl` declaractions for `CommonTypeWith` are provided as part of the prelude. These are described in the following sections. _Note:_ The same mechanism is expected to eventually be used to compute common types in other circumstances. ### Symmetry The common type of `T` and `U` should always be the same as the common type of `U` and `T`. This is enforced in two steps: - A `SymmetricCommonTypeWith` interface implicitly provides a `B as CommonTypeWith(A)` implementation whenever one doesn't exist but an `A as CommonTypeWith(B)` implementation exists. - `CommonType` is defined in terms of `SymmetricCommonTypeWith`, and requires that both `A as SymmetricCommonTypeWith(B)` and `B as SymmetricCommonTypeWith(A)` produce the same type. The interface `SymmetricCommonTypeWith` is an implementation detail of the `CommonType` constraint. It is defined and implemented as follows: ``` interface SymmetricCommonTypeWith(U:! type) { let Result:! type where Self impls ImplicitAs(.Self) and U impls ImplicitAs(.Self); } match_first { impl forall [T:! type, U:! CommonTypeWith(T)] T as SymmetricCommonTypeWith(U) where .Result = U.Result {} impl forall [U:! type, T:! CommonTypeWith(U)] T as SymmetricCommonTypeWith(U) where .Result = T.Result {} } ``` The `SymmetricCommonTypeWith` interface is not exported, so users may not declare their own implementations of it, and only the two blanket `impl` declarations above are used. The `CommonType` constraint is then defined as follows: ``` constraint CommonType(U:! SymmetricCommonTypeWith(Self)) { extend SymmetricCommonTypeWith(U) where .Result == U.Result; } ``` When computing the common type of `T` and `U`, if only one of the types provides a `CommonTypeWith` implementation, that determines the common type. If both types provide a `CommonTypeWith` implementation and their `Result` types are the same, that determines the common type. Otherwise, if both types provide implementations but their `Result` types differ, there is no common type, and the `CommonType` constraint is not met. For example, given: ``` // Implementation #1 impl forall [T:! type] MyX as CommonTypeWith(T) where .Result = MyX {} // Implementation #2 impl forall [T:! type] MyY as CommonTypeWith(T) where .Result = MyY {} ``` `MyX as CommonTypeWith(MyY)` will select #1, and `MyY as CommonTypeWith(MyX)` will select #2, but the constraints on `MyX as CommonType(MyY)` will not be met because result types differ. ### Same type If `T` is the same type as `U`, the result is that type: ``` final impl forall [T:! type] T as CommonTypeWith(T) where .Result = T {} ``` _Note:_ This rule is intended to be considered more specialized than the other rules in this document. Because this `impl` is declared `final`, `T.(CommonType(T)).Result` is always assumed to be `T`, even in contexts where `T` involves a symbolic binding and so the result would normally be an unknown type whose facet type is `type`. ``` fn F[T:! Hashable](c: bool, x: T, y: T) -> HashCode { // OK, type of `if` expression is `T`. return (if c then x else y).Hash(); } ``` ### Implicit conversions If `T` implicitly converts to `U`, the common type is `U`: ``` impl forall [T:! type, U:! ImplicitAs(T)] T as CommonTypeWith(U) where .Result = T {} ``` _Note:_ If an implicit conversion is possible in both directions, and no more specific implementation exists, the constraints on `T as CommonType(U)` will not be met because `(T as CommonTypeWith(U)).Result` and `(U as CommonTypeWith(T)).Result` will differ. In order to define a common type for such a case, `CommonTypeWith` implementations in both directions must be provided to override the blanket `impl` declarations in both directions: ``` impl MyString as CommonTypeWith(YourString) where .Result = MyString {} impl YourString as CommonTypeWith(MyString) where .Result = MyString {} var my_string: MyString; var your_string: YourString; // The type of `also_my_string` is `MyString`. var also_my_string: auto = if cond then my_string else your_string; ``` ## Alternatives considered - [Provide no conditional expression](/proposals/p0911.md#no-conditional-expression) - Use [`cond ? expr1 : expr2`, like in C and C++](/proposals/p0911.md#use-c-syntax) syntax - Use [`if (cond) expr1 else expr2`](/proposals/p0911.md#no-then) syntax - Use [`if (cond) then expr1 else expr2`](/proposals/p0911.md#require-parentheses-around-the-condition) syntax - Allow [`1 + if cond then expr1 else expr2`](/proposals/p0911.md#never-require-enclosing-parentheses) - [Only require one `impl` to specify the common type if implicit conversions in both directions are possible](/proposals/p0911.md#implicit-conversions-in-both-directions) - [Introduce special rules for lvalue conditionals](/proposals/p0911.md#support-lvalue-conditionals) ## References - Proposal [#911: Conditional expressions](https://github.com/carbon-language/carbon-lang/pull/911). ================================================ FILE: docs/design/expressions/implicit_conversions.md ================================================ # Implicit conversions ## Table of contents - [Overview](#overview) - [Properties of implicit conversions](#properties-of-implicit-conversions) - [Lossless](#lossless) - [Semantics-preserving](#semantics-preserving) - [Examples](#examples) - [Built-in types](#built-in-types) - [Data types](#data-types) - [Same type](#same-type) - [Pointer conversions](#pointer-conversions) - [Facet types](#facet-types) - [Struct, tuple, and array types](#struct-tuple-and-array-types) - [Consistency with `as`](#consistency-with-as) - [Extensibility](#extensibility) - [Alternatives considered](#alternatives-considered) - [References](#references) ## Overview When an expression appears in a context in which an expression of a specific type is expected, the expression is implicitly converted to that type if possible. For [built-in types](#built-in-types), implicit conversions are permitted when: - The conversion is [_lossless_](#lossless): every possible value for the source expression converts to a distinct value in the target type. - The conversion is [_semantics-preserving_](#semantics-preserving): corresponding values in the source and destination type have the same abstract meaning. These rules aim to ensure that implicit conversions are unsurprising: the value that is provided as the operand of an operation should match how that operation interprets the value, because the identity and abstract meaning of the value are preserved by any implicit conversions that are applied. It is possible for user-defined types to [extend](#extensibility) the set of valid implicit conversions. Such extensions are expected to also follow these rules. ## Properties of implicit conversions ### Lossless We expect implicit conversion to never lose information: if two values are distinguishable before the conversion, they should generally be distinguishable after the conversion. It should be possible to define a conversion in the opposite direction that restores the original value, but such a conversion is not expected to be provided in general, and might be computationally expensive. Because an implicit conversion is converting from a narrower type to a wider type, implicit conversions do not necessarily preserve static information about the source value. ### Semantics-preserving We expect implicit conversions to preserve the meaning of converted values. The assessment of this criterion will necessarily be subjective, because the meanings of values generally live in the mind of the programmer rather than in the program text. However, the semantic interpretation is expected to be consistent from one conversion to another, so we can provide a test: if multiple paths of implicit conversions from a type `A` to a type `B` exist, and the same value of type `A` would convert to different values of type `B` along different paths, then at least one of those conversions must not be semantics-preserving. A semantics-preserving conversion does not necessarily preserve the meaning of particular syntax when applied to the value. The same syntax may map to different operations in the new type. For example, division may mean different things in integer and floating-point types, and member access may find different members in a derived class pointer versus in a base class pointer. ### Examples Conversion from `i32` to `Vector(i32)` by forming a vector of N zeroes is lossless but not semantics-preserving. Conversion from `i32` to `f32` by rounding to the nearest representable value is semantics-preserving but not lossless. Conversion from `String` to `StringView` is lossless, because we can compute the `String` value from the `StringView` value, and semantics-preserving because the string value denoted is the same. Conversion in the other direction may or may not be semantics-preserving depending on whether we consider the address to be a salient part of a `StringView`'s value. ## Built-in types ### Data types The following implicit numeric conversions are available: - `iN` or `uN` -> `iM` if `M` > `N` - `uN` -> `uM` if `M` > `N` - `fN` -> `fM` if `M` > `N` - `iN` or `uN` -> `fM` if every value of type `iN` or `uN` can be represented in `fM`: - `i8` or `u8` -> `f16` - `i24` or `u24` (or smaller) -> `f32` - `i48` or `u48` (or smaller) -> `f64` - `i64` or `u64` (or smaller) -> `f80` (x86 only) - `i112` or `u112` (or smaller) -> `f128` (if available) - `i232` or `u232` (or smaller) -> `f256` (if available) In each case, the numerical value is the same before and after the conversion. An integer zero is translated into a floating-point positive zero. An integer constant can be implicitly converted to any type `iM`, `uM`, or `fM` in which that value can be exactly represented. A floating-point constant can be implicitly converted to any type `fM` in which that value is between the least representable finite value and the greatest representable finite value (inclusive), and converts to the nearest representable finite value, with ties broken by picking the value for which the mantissa is even. The above conversions are also precisely those that C++ considers non-narrowing, except: - Carbon also permits integer to floating-point conversions in more cases. The most important of these is that Carbon permits `i32` to be implicitly converted to `f64`. Lossy conversions, such as from `i32` to `f32`, are not permitted. - What Carbon considers to be an integer constant or floating-point constant may differ from what C++ considers to be a constant expression. **Note:** We have not yet decided what will qualify as a constant in this context, but it will include at least integer and floating-point literals, with optional enclosing parentheses. It is possible that such constants will have singleton types; see issue [#508](https://github.com/carbon-language/carbon-lang/issues/508). In addition to the above rules, a negative integer constant `k` can be implicitly converted to the type `uN` if the value `k` + 2N can be exactly represented, and converts to that value. Note that this conversion violates the "semantics-preserving" test. For example, `(-1 as u8) as i32` produces the value `255` whereas `-1 as i32` produces the value `-1`. However, this conversion is important in order to allow bitwise operations with masks, so we allow it: ``` // We allow ^0 == -1 to convert to `u32` to represent an all-ones value. var a: u32 = ^0; // ^4 == -5 is negative, but we want to allow it to convert to u32 here. var b: u32 = a & ^4; ``` ### Same type The following conversion is available for every type `T`: - `T` -> `T` ### Pointer conversions The following pointer conversion is available: - `T*` -> `U*` if `T` is a class derived from the class `U`. Even though we can convert `Derived*` to `Base*`, we cannot convert `Derived**` to `Base**` because that would allow storing a `Derived2*` into a `Derived*`: ``` abstract class Base {} class Derived { extend base: Base; } class Derived2 { extend base: Base; } var d2: Derived2 = {}; var p: Derived*; var q: Derived2* = &d2; var r: Base** = &p; // Bad: would store q to p. *r = q; ``` ### Facet types A type `T` with [facet type](../generics/terminology.md#facet-type) `TT1` can be implicitly converted to the facet type `TT2` if `T` [satisfies the requirements](../generics/details.md#subtyping-between-facet-types) of `TT2`. ### Struct, tuple, and array types See [here](/docs/design/values.md#type-conversions). ## Consistency with `as` An implicit conversion of an expression `E` of type `T` to type `U`, when permitted, always has the same meaning as the [explicit cast expression `E as U`](as_expressions.md). Moreover, because such an implicit conversion is expected to exactly preserve the value, `(E as U) as T`, if valid, should be expected to result in the same value as produced by `E` even if the `as T` cast cannot be performed as an implicit conversion. ## Extensibility Implicit conversions can be defined for user-defined types such as [classes](../classes.md) by implementing the `ImplicitAs` interface, which extends [the `As` interface used to implement `as` expressions](as_expressions.md#extensibility): ``` interface ImplicitAs(Dest:! type) { extend As(Dest); // Inherited from As(Dest): // fn Convert[self: Self]() -> Dest; } ``` When attempting to implicitly convert an expression `x` to type `U`, the expression is rewritten to `x.(ImplicitAs(U).Convert)()`. Note that implicit conversions are not transitive. Even if an `impl A as ImplicitAs(B)` and an `impl B as ImplicitAs(C)` are both provided, an expression of type `A` cannot be implicitly converted to type `C`. Allowing transitivity would introduce the risk of ambiguity issues as code evolves and would in general require a search of a potentially unbounded set of intermediate types. ## Alternatives considered - [Provide lossy and non-semantics-preserving implicit conversions from C++](/proposals/p0820.md#c-conversions) - [Provide no implicit conversions](/proposals/p0820.md#no-conversions) - [Provide no extensibility](/proposals/p0820.md#no-extensibility) - [Apply implicit conversions transitively](/proposals/p0820.md#transitivity) - [Do not allow negative constants to convert to unsigned types](/proposals/p1191.md#converting-complements-to-unsigned-types) ## References - [Implicit conversions in C++](https://en.cppreference.com/w/cpp/language/implicit_conversion) - Proposal [#820: Implicit conversions](https://github.com/carbon-language/carbon-lang/pull/820). - Proposal [#866: Allow ties in floating literals](https://github.com/carbon-language/carbon-lang/pull/866). ================================================ FILE: docs/design/expressions/indexing.md ================================================ # Indexing ## Table of contents - [Overview](#overview) - [Details](#details) - [Examples](#examples) - [Alternatives considered](#alternatives-considered) - [References](#references) ## Overview Carbon supports indexing using the conventional `a[i]` subscript syntax. When `a` is a [durable reference expression](/docs/design/values.md#durable-reference-expressions), the result of subscripting is also a durable reference expression, but when `a` is a [value expression](/docs/design/values.md#value-expressions), the result can be a durable reference expression or a value expression, depending on which interface the type implements: - If subscripting a value expression produces a value expression, as with an array, the type should implement `IndexWith`. - If subscripting a value expression produces a durable reference expression, as with C++'s `std::span`, the type should implement `IndirectIndexWith`. `IndirectIndexWith` is a subtype of `IndexWith`, and subscript expressions are rewritten to method calls on `IndirectIndexWith` if the type is known to implement that interface, or to method calls on `IndexWith` otherwise. `IndirectIndexWith` provides a final blanket `impl` of `IndexWith`, so a type can implement at most one of those two interfaces. The `Ref` methods of these interfaces, which are used to form durable reference expressions on indexing, must return by `ref`. ## Details A subscript expression has the form "_lhs_ `[` _index_ `]`". As in C++, this syntax has the same precedence as `.`, `->`, and function calls, and associates left-to-right with all of them. Its semantics are defined in terms of the following interfaces: ``` interface IndexWith(SubscriptType:! type) { let ElementType:! type; fn At[bound self: Self](subscript: SubscriptType) -> val ElementType; fn Ref[bound ref self: Self](subscript: SubscriptType) -> ref ElementType; } interface IndirectIndexWith(SubscriptType:! type) { require Self impls IndexWith(SubscriptType); fn Ref[bound self: Self](subscript: SubscriptType) -> ref ElementType; } ``` A subscript expression where _lhs_ has type `T` and _index_ has type `I` is rewritten based on the expression category of _lhs_ and whether `T` is known to implement `IndirectIndexWith(I)`: - If `T` implements `IndirectIndexWith(I)`, the expression is rewritten to "`(` _lhs_ `).(IndirectIndexWith(I).Ref)(` _index_ `)`". - Otherwise, if _lhs_ is a [_durable reference expression_](/docs/design/values.md#durable-reference-expressions), the expression is rewritten to "`(` _lhs_ `).(IndexWith(I).Ref)(` _index_ `)`". - Otherwise, the expression is rewritten to "`(` _lhs_ `).(IndexWith(I).At)(` _index_ `)`". `IndirectIndexWith` provides a blanket `final impl` for `IndexWith`: ``` final impl forall [SubscriptType:! type, T:! IndirectIndexWith(SubscriptType)] T as IndexWith(SubscriptType) { where ElementType = T.(IndirectIndexWith(SubscriptType).ElementType); fn At[bound self: Self](subscript: SubscriptType) -> val ElementType { return self.(IndirectIndexWith(SubscriptType).Ref)(index); } fn Ref[bound ref self: Self](subscript: SubscriptType) -> ref ElementType { return self.(IndirectIndexWith(SubscriptType).Ref)(index); } } ``` Thus, a type that implements `IndirectIndexWith` need not, and cannot, provide its own definitions of `IndexWith.At` and `IndexWith.Ref`. ### Examples An array type could implement subscripting like so: ``` class Array(template T:! type) { impl as IndexWith(like i64) { let ElementType:! type = T; fn At[bound self: Self](subscript: i64) -> val T; fn Ref[bound ref self: Self](subscript: i64) -> ref T; } } ``` And a type such as `std::span` could look like this: ``` class Span(T:! type) { impl as IndirectIndexWith(like i64) { let ElementType:! type = T; fn Ref[bound ref self: Self](subscript: i64) -> ref T; } } ``` ## Alternatives considered - [Different subscripting syntaxes](/proposals/p2274.md#different-subscripting-syntaxes) - [Multiple indices](/proposals/p2274.md#multiple-indices) - [Read-only subscripting](/proposals/p2274.md#read-only-subscripting) - [Rvalue-only subscripting](/proposals/p2274.md#rvalue-only-subscripting) - [Map-like subscripting](/proposals/p2274.md#map-like-subscripting) ## References - Proposal [#2274: Subscript syntax and semantics](https://github.com/carbon-language/carbon-lang/pull/2274) - Proposal [#2006: Values, variables, and pointers](https://github.com/carbon-language/carbon-lang/pull/2006) ================================================ FILE: docs/design/expressions/literals.md ================================================ # Literal expressions > **STATUS:** Up-to-date on 2022-Dec-9. ## Table of contents - [Numeric literals](#numeric-literals) - [Numeric literal syntax](#numeric-literal-syntax) - [Defined types](#defined-types) - [Implicit conversions](#implicit-conversions) - [Examples](#examples) - [Alternatives Considered](#alternatives-considered) - [Numeric type literals](#numeric-type-literals) - [Meaning](#meaning) - [Usage](#usage) - [Alternatives considered](#alternatives-considered-1) - [String literals](#string-literals) - [References](#references) ## Numeric literals Numeric Literals are defined on Wikipedia [here](). In Carbon, numeric literals have a type derived from their value. Two integer literals have the same type if and only if they represent the same integer. Two real number literals have the same type if and only if they represent the same real number. That is: - For every integer, there is a type representing literals with that integer value. - For every rational number, there is a type representing literals with that real value. - The types for real numbers are distinct from the types for integers, even for real numbers that represent integers. For example, `1 / 2` results in `0`, due to integer arithmetic, whereas `1.0 / 2` results in `0.5`. This is due to `1` having an integral type, while `1.0` has a real number type, even though it represents the same numeric value. Primitive operators are available between numeric literals, and produce values with numeric literal types. For example, the type of `1 + 2` is the same as the type of `3`. Numeric types can provide conversions to support initialization from numeric literals. Because the value of the literal is carried in the type, a type-level decision can be made as to whether the conversion is valid. The integer types defined in the standard library permit conversion from integer literal types whose values are representable in the integer type. The floating-point types defined in the standard library permit conversion from integer and rational literal types whose values are between the minimum and maximum finite value representable in the floating-point type. ### Numeric literal syntax Numeric literal syntax is covered in the [numeric literal lexical conventions](../lexical_conventions/numeric_literals.md) doc. Both Integer and Real-Number syntax is defined, with decimal, hexadecimal and binary integer literals, and decimal and hexadecimal real number literals. ### Defined types The following types are defined in the Carbon prelude: - `Core.BigInt`, an arbitrary-precision integer type; - `Core.Rational(T:! type)`, a rational type, parameterized by a type used for its numerator and denominator -- the exact constraints on `T` are not yet decided; - `Core.IntLiteral(N:! Core.BigInt)`, a type representing integer literals; and - `Core.FloatLiteral(X:! Core.Rational(Core.BigInt))`, a type representing floating-point literals. All of these types are usable during compilation. `Core.BigInt` supports the same operations as [`Core.Int(N)`](#meaning). `Core.Rational(T)` supports the same operations as [`Core.Float(N)`](#meaning). The types `Core.IntLiteral(N)` and `Core.FloatLiteral(X)` also support primitive integer and floating-point operations such as arithmetic and comparison, but these operations are typically heterogeneous: for example, an addition between `Core.IntLiteral(N)` and `Core.IntLiteral(M)` produces a value of type `Core.IntLiteral(N + M)`. ### Implicit conversions `Core.IntLiteral(N)` converts to any sufficiently large integer type, as if by: ``` impl forall [template N:! Core.BigInt, template M:! Core.BigInt] Core.IntLiteral(N) as ImplicitAs(Core.Int(M)) if N >= Core.Int(M).MinValue as Core.BigInt and N <= Core.Int(M).MaxValue as Core.BigInt { ... } impl forall [template N:! Core.BigInt, template M:! Core.BigInt] Core.IntLiteral(N) as ImplicitAs(Core.UInt(M)) if N >= Core.UInt(M).MinValue as Core.BigInt and N <= Core.UInt(M).MaxValue as Core.BigInt { ... } ``` The above is for exposition purposes only; various parts of this syntax are not yet decided. Similarly, `Core.IntLiteral(X)` and `Core.FloatLiteral(X)` convert to any sufficiently large floating-point type, and produce the nearest representable floating-point value. Conversions in which `X` lies exactly half-way between two values are rounded to the value in which the mantissa is even, as defined in the IEEE 754 standard and as was decided in [proposal #866](https://github.com/carbon-language/carbon-lang/pull/866). Conversions in which `X` is outside the range of finite values of the floating-point type are rejected rather than saturating to the finite range or producing an infinity. ### Examples ```carbon // This is OK: the initializer is of the integer literal type with value // -2147483648 despite being written as a unary `-` applied to a literal. var x: i32 = -2147483648; // This initializes y to 2^60. var y: i64 = 1 << 60; // This forms a rational literal whose value is one third, and converts it to // the nearest representable value of type `f64`. var z: f64 = 1.0 / 3.0; // This is an error: 300 cannot be represented in type `i8`. var c: i8 = 300; fn F[template T:! type](v: T) { var x: i32 = v * 2; } // OK: x = 2_000_000_000. F(1_000_000_000); // Error: 4_000_000_000 can't be represented in type `i32`. F(2_000_000_000); // No storage required for the bound when it's of integer literal type. struct Span(template T:! type, template BoundT:! type) { var begin: T*; var bound: BoundT; } // Returns 1, because 1.3 can implicitly convert to f32, even though conversion // to f64 might be a more exact match. fn G() -> i32 { match (1.3) { case _: f32 => { return 1; } case _: f64 => { return 2; } } } // Can only be called with a literal 0. fn PassMeZero(_: Core.IntLiteral(0)); // Can only be called with integer literals in the given range. fn ConvertToByte[template N:! Core.BigInt](_: Core.IntLiteral(N)) -> i8 if N >= -128 and N <= 127 { return N as i8; } // Given any int literal, produces a literal whose value is one higher. fn OneHigher(L: Core.IntLiteral(template _:! Core.BigInt)) -> auto { return L + 1; } // Error: 256 can't be represented in type `i8`. var v: i8 = OneHigher(255); ``` ### Alternatives Considered - [Use an ordinary integer or floating-point type for literals](/proposals/p0144.md#use-an-ordinary-integer-or-floating-point-type-for-literals) - [Use same type for all literals](/proposals/p0144.md#use-same-type-for-all-literals) - [Allow leading `-` in literal tokens](/proposals/p0144.md#allow-leading---in-literal-tokens) - [Forbidding floating-point ties](/proposals/p0866.md#alternatives-considered) ## Numeric type literals Carbon has a simple keyword-like syntax of `iN`, `uN`, and `fN` for two's complement signed integers, unsigned integers, and [IEEE-754](https://en.wikipedia.org/wiki/IEEE_754) floating-point numbers, respectively. Here, `N` can be a positive multiple of 8, including the common power-of-two sizes (for example, `N = 8, 16, 32`). Examples of this syntax include: - `i16` - A 16-bit two's complement signed integer type - `u32` - A 32-bit unsigned integer type - `f64` - A 64-bit IEEE-754 binary floating-point number type ### Meaning These type literals are aliases for parameterized types defined in the `Core` package that is automatically imported by the prelude: - `iN` is an alias for `Core.Int(N)` - `uN` is an alias for `Core.UInt(N)` - `fN` is an alias for `Core.Float(N)` ### Usage ```carbon package sample; fn Sum(x: i32, y: i32) -> i32 { return x + y; } fn Run() -> i32 { return Sum(4, 2); } ``` In the above example, `Sum` has parameters `x` and `y`, each of which is typed as a 32-bit two's complement signed integer. `Main` then returns the output of `Sum` as a 32-bit two's complement signed integer. ### Alternatives considered - [C++ LP64 convention](/proposals/p2015.md#c-lp64-convention) - [Type name with length suffix](/proposals/p2015.md#type-name-with-length-suffix) - [Uppercase suffixes](/proposals/p2015.md#uppercase-suffixes) - [Additional bit sizes](/proposals/p2015.md#additional-bit-sizes) ## String literals String literal syntax is covered in the [string literal lexical conventions](../lexical_conventions/string_literals.md). No design for string types has been through the proposal process yet. ## References - Proposal [#143: Numeric literals](https://github.com/carbon-language/carbon-lang/pull/143) - Proposal [#144: Numeric literal semantics](https://github.com/carbon-language/carbon-lang/pull/144) - Question-for-leads issue [#543: pick names for fixed-size integer types](https://github.com/carbon-language/carbon-lang/issues/543) - Proposal [#866: Allow ties in floating literals](https://github.com/carbon-language/carbon-lang/pull/866) - Proposal [#2015: Numeric type literal syntax](https://github.com/carbon-language/carbon-lang/pull/2015) - Question-for-leads issue [#2113: Structure, scope, and naming of the prelude and syntax aliases](https://github.com/carbon-language/carbon-lang/issues/2113) ================================================ FILE: docs/design/expressions/logical_operators.md ================================================ # Logical operators ## Table of contents - [Overview](#overview) - [Details](#details) - [Precedence](#precedence) - [Associativity](#associativity) - [Conversions](#conversions) - [Overloading](#overloading) - [Alternatives considered](#alternatives-considered) - [References](#references) ## Overview Carbon provides three operators to support logical operations on `bool` values: - `and` provides a logical AND operation. - `x and y` evaluates to `true` if both operands are `true`. - `or` provides a logical OR operation. - `x or y` evaluates to `true` if either operand is `true`. - `not` provides a logical NOT operation. - `not x` evaluates to `true` if the operand is `false`. `and` and `or` are infix binary operators, and use [short-circuit evaluation](https://en.wikipedia.org/wiki/Short-circuit_evaluation). `not` is a prefix unary operator. ## Details ### Precedence `and` and `or` have very low precedence. When an expression appearing as the condition of an `if` uses these operators unparenthesized, they are always the lowest precedence operators in that expression. These operators permit any reasonable operator that might be used to form a `bool` value as a subexpression. In particular, comparison operators such as `<` and `==` have higher precedence than `and` and `or`. However, the precedence of `and` and `or` is not directly comparable with each other, so they cannot both be used directly in an expression without parentheses. `not` is higher precedence than `and` and `or`, but its precedence is incomparable with most other operators, including comparison operators. For example: ```carbon // ✅ Valid: `and` is lower precedence than the `<` or `==` operators. if (n + m == 3 and not n < m) { ... } // The above is equivalent to: if (((n + m) == 3) and (not (n < m))) { ... } // ❌ Invalid: `and` and `or` precedence is incomparable. if (cond1 and cond2 or cond3) { ... } // ✅ Valid: Parentheses avoid the precedence check. if (cond1 and (cond2 or cond3)) { ... } // ❌ Invalid: `not` precedence is incomparable with `==`. if (not cond1 == cond2) { ... } // ❌ Invalid: `not` precedence is incomparable with `==`. if (cond1 == not cond2) { ... } // ✅ Valid: Parentheses avoid the precedence check. if (cond1 == (not cond2)) { ... } ``` ### Associativity `and` and `or` are left-associative. A `not` expression cannot be the operand of another `not` expression; `not not b` is an error without parentheses. ``` // ✅ Valid: `and` is left-associative, and precedence is fine. if (not a and not b and not c) { ... } // The above is equivalent to: if ((not a) and ((not b) and (not c))) { ... } // ✅ Valid: Parentheses avoid the `not` associativity error. if (not (not a)) { ... } // ❌ Invalid: `not not` associativity requires parentheses. if (not not a) { ... } ``` ### Conversions > TODO: This should be addressed through a standard `bool` conversion design. The operand of `and`, `or`, or `not` is converted to a `bool` value in the same way as the condition of an `if` statement. In particular: - If we decide that certain values, such as pointers or integers, should not be usable as the condition of an `if` without an explicit comparison against null or zero, then those values will also not be usable as the operand of `and`, `or`, or `not` without an explicit comparison. - If an extension point is provided to determine how to branch on the truth of a value in an `if` (such as by supplying a conversion to a `bool` type), that extension point will also apply to `and`, `or`, and `not`. ### Overloading The logical operators `and`, `or`, and `not` are not overloadable. As noted above, any mechanism that allows types to customize how `if` treats them will also customize how `and`, `or`, and `not` treats them. ## Alternatives considered - [Use punctuation spelling for all three operators](/proposals/p0680.md#use-punctuation-spelling-for-all-three-operators) - [Precedence of AND versus OR](/proposals/p0680.md#precedence-of-and-versus-or) - [Precedence of NOT](/proposals/p0680.md#precedence-of-not) - [Punctuation form of NOT](/proposals/p0680.md#punctuation-form-of-not) - [Two forms of NOT](/proposals/p0680.md#two-forms-of-not) - [Repeated NOT](/proposals/p0680.md#repeated-not) - [AND and OR produce the decisive value](/proposals/p0680.md#and-and-or-produce-the-decisive-value) ## References - Proposal [#680: And, or, not](https://github.com/carbon-language/carbon-lang/pull/680). - Proposal [#702: Comparison operators](https://github.com/carbon-language/carbon-lang/pull/702). ================================================ FILE: docs/design/expressions/member_access.md ================================================ # Qualified names and member access ## Table of contents - [Overview](#overview) - [Member resolution](#member-resolution) - [Package and namespace members](#package-and-namespace-members) - [Types, forms, and facets](#types-forms-and-facets) - [Tuple indexing](#tuple-indexing) - [Values](#values) - [Facet binding](#facet-binding) - [Compile-time bindings](#compile-time-bindings) - [Lookup ambiguity](#lookup-ambiguity) - [`impl` lookup](#impl-lookup) - [`impl` lookup for simple member access](#impl-lookup-for-simple-member-access) - [`impl` lookup for compound member access](#impl-lookup-for-compound-member-access) - [Instance binding](#instance-binding) - [Non-instance members](#non-instance-members) - [Non-vacuous member access restriction](#non-vacuous-member-access-restriction) - [Precedence and associativity](#precedence-and-associativity) - [Alternatives considered](#alternatives-considered) - [References](#references) ## Overview > **TODO:** [p3720: Member binding operators](/proposals/p3720.md) introduces an > additional "member binding" step, redefines simple member access in terms of > compound member access, and defines compound member access in terms of calls > to user-implementable interface methods. This document must be updated to > reflect those changes. A _qualified name_ is a [word](../lexical_conventions/words.md) that is preceded by a period or a rightward arrow. The name is found within a contextually determined entity: - In a member access expression, this is the entity preceding the period. - In a pointer member access expression, this is the entity pointed to by the pointer preceding the rightward arrow. - For a designator in a struct literal, the name is introduced as a member of the struct type. A _member access expression_ allows a member of a value, type, interface, namespace, and so on to be accessed by specifying a qualified name for the member. A member access expression is either a _simple_ member access expression of the form: - _member-access-expression_ ::= _expression_ `.` _word_ - _member-access-expression_ ::= _expression_ `->` _word_ - _member-access-expression_ ::= _expression_ `.` _integer-literal_ - _member-access-expression_ ::= _expression_ `->` _integer-literal_ or a _compound_ member access of the form: - _member-access-expression_ ::= _expression_ `.` `(` _expression_ `)` - _member-access-expression_ ::= _expression_ `->` `(` _expression_ `)` The _member name_ is the _word_, _integer-literal_, or the constant value of the parenthesized _expression_ in the member access expression. Compound member accesses allow specifying a qualified member name. For example: ```carbon namespace Widgets; interface Widgets.Widget { fn Grow[ref self: Self](factor: f64); } class Widgets.Cog { var size: i32; fn Make(size: i32) -> Self; extend impl as Widgets.Widget; } fn Widgets.GrowSomeCogs() { var cog1: Cog = Cog.Make(1); var cog2: Cog = cog1.Make(2); var cog_pointer: Cog* = &cog2; let cog1_size: i32 = cog1.size; cog1.Grow(1.5); cog2.(Cog.Grow)(cog1_size as f64); cog1.(Widget.Grow)(1.1); cog2.(Widgets.Cog.(Widgets.Widget.Grow))(1.9); cog_pointer->Grow(0.75); cog_pointer->(Widget.Grow)(1.2); } ``` Pointer member access expressions are those using a `->` instead of a `.` and their semantics are exactly what would result from first dereferencing the expression preceding the `->` and then forming a member access expression using a `.`. For example, a simple pointer member access expression _expression_ `->` _word_ becomes `(` `*` _expression_ `)` `.` _word_. More details on this syntax and semantics can be found in the [pointers](/docs/design/values.md#pointers) design. The rest of this document describes the semantics using `.` alone for simplicity. A member access expression is processed using the following steps: - First, the member name to the right of the `.` is [resolved](#member-resolution) to a specific member entity, called `M` in this document. - Then, if necessary, [`impl` lookup](#impl-lookup) is performed to map from a member of an interface to a member of the relevant `impl`, potentially updating `M`. - Then, if necessary, [instance binding](#instance-binding) is performed to locate the member subobject corresponding to a field name or to build a bound method object, producing the result of the member access expression. - If [instance binding is not performed](#non-instance-members), the result is `M`. ## Member resolution The process of _member resolution_ determines which member `M` a member access expression is referring to. For a simple member access, if the first operand is a type, form, facet, package, or namespace, a search for the member name is performed in the first operand. Otherwise, a search for the member name is performed in the type of the first operand. In either case, the search must succeed. In the latter case, if the result is an instance member, then [instance binding](#instance-binding) is performed on the first operand. A search for a name within a form searches for the name in its [type component](/docs/design/values.md#expression-forms). Note that this means that the form of an expression never affects simple member access into that expression, except through its type component. For a compound member access, the second operand is evaluated as a compile-time constant to determine the member being accessed. The evaluation is required to succeed and to result in a member of a type, interface, or non-type facet, or a value of an integer or integer literal type. If the result is an instance member, then [instance binding](#instance-binding) is always performed on the first operand. ### Package and namespace members If the first operand is a package or namespace name, the expression must be a simple member access expression. The member name must be a _word_ that names a member of that package or namespace, and the result is the package or namespace member with that name. An expression that names a package or namespace can only be used as the first operand of a member access or as the target of an `alias` declaration. ``` namespace MyNamespace; fn MyNamespace.MyFunction() {} // ✅ OK, can alias a namespace. alias MyNS = MyNamespace; fn CallMyFunction() { MyNS.MyFunction(); } // ❌ Error: a namespace is not a value. let MyNS2:! auto = MyNamespace; fn CallMyFunction2() { // ❌ Error: cannot perform compound member access into a namespace. MyNamespace.(MyNamespace.MyFunction)(); } ``` The first operand may also be the keyword `package`, as in `package.Foo`, to name the `Foo` member of the current package. This can be used to disambiguate between different `Foo` definitions, as in: ```carbon // This defines `package.Foo` class Foo {} class Bar { // This defines `Bar.Foo`, or equivalently `package.Bar.Foo`. class Foo {} fn F() { // ✅ OK, `x` has type `Foo` from the outer scope. var x: package.Foo = {}; // ❌ Error: ambiguous; // `Foo` could mean `package.Foo` or `Bar.Foo`. var y: Foo = {}; } } ``` ### Types, forms, and facets If the first operand is a type, form, or facet, it must be a compile-time constant. This disallows member access into a type except during compile-time, see leads issue [#1293](https://github.com/carbon-language/carbon-lang/issues/1293). Like the previous case, types (including [facet types](/docs/design/generics/terminology.md#facet-type)) have member names, and lookup searches those names. For example: - `i32.Least` finds the member constant `Least` of the type `i32`. - `Add.Op` finds the member function `Op` of the interface `Add`. Because a facet type is a type, this is a special case of the previous bullet. Unlike the previous case, both simple and compound member access is allowed. Non-type facets, such as `T as Cowboy`, also have members. Specifically, the members of the `impl` or `impl`s that form the implementation of `T as Cowboy`. Being part of the `impl` rather than the interface, no further [`impl` lookup](#impl-lookup) is needed. ```carbon interface Cowboy { fn Draw[self: Self](); } interface Renderable { fn Draw[self: Self](); } class Avatar { extend impl Avatar as Cowboy; extend impl Avatar as Renderable; } ``` Simple member access `(Avatar as Cowboy).Draw` finds the `Cowboy.Draw` implementation for `Avatar`, ignoring `Renderable.Draw`. Similarly, a form has members, specifically the members of the form's type component. ### Tuple indexing Tuple types have member names that are *integer-literal*s, not *word*s. Each positional element of a tuple is considered to have a name that is the corresponding decimal integer: `0`, `1`, and so on. The spelling of the _integer-literal_ is required to exactly match one of those names, and the result of member resolution is an instance member that refers to the corresponding element of the tuple. ``` // ✅ `a == 42`. let a: i32 = (41, 42, 43).1; // ❌ Error: no tuple element named `0x1`. let b: i32 = (1, 2, 3).0x1; // ❌ Error: no tuple element named `2`. let c: i32 = (1, 2).2; var t: (i32, i32, i32) = (1, 2, 3); let p: (i32, i32, i32)* = &t; // ✅ `m == 3`. let m: i32 = p->2; ``` In a compound member access whose second operand is of integer or integer literal type, the first operand is required to be of tuple type or to extend a tuple type, otherwise member resolution fails. The second operand is required to be a non-negative template constant that is less than the number of tuple elements, and the result is an instance member that refers to the corresponding positional element of the tuple. ``` // ✅ `d == 43`. let d: i32 = (41, 42, 43).(1 + 1); // ✅ `e == 2`. let template e:! i32 = (1, 2, 3).(0x1); // ❌ Error: no tuple element with index 4. let f: i32 = (1, 2).(2 * 2); // ✅ `n == 3`. let n: i32 = p->(e); ``` ### Values If the first operand is not a type, form, package, namespace, or facet, it does not have member names, and a search is performed into the type of the first operand instead. ```carbon interface Printable { fn Print[self: Self](); } impl i32 as Printable; class Point { var x: i32; var y: i32; // Extending impl injects the name `Print` into // class `Point`. extend impl as Printable; } fn PrintPointTwice() { var p: Point = {.x = 0, .y = 0}; // ✅ OK, `x` found in type of `p`, namely `Point`. p.x = 1; // ✅ OK, `y` found in the type `Point`. p.(Point.y) = 1; // ✅ OK, `Print` found in type of `p`, namely `Point`. p.Print(); // ✅ OK, `Print` found in the type `Printable`, and // `Printable.Print` found in the type of `p`. p.(Printable.Print)(); } ``` ### Facet binding A search for members of a facet binding `T:! C` treats the facet binding as an [archetype](/docs/design/generics/terminology.md#archetype), and finds members of the facet `T` of facet type `C`. For example: ``` interface Printable { fn Print[self: Self](); } fn GenericPrint[T:! Printable](a: T) { // ✅ OK, type of `a` is the facet binding `T`; // `Print` found in the facet `T as Printable`. a.Print(); } ``` **Note:** If lookup is performed into a type that involves a template binding, the lookup will be performed both in the context of the template definition and in the context of the template instantiation, as described in [the "compile-time bindings" section](#compile-time-bindings). The results of these lookups are [combined](#lookup-ambiguity). #### Compile-time bindings If the value or type of the first operand depends on a checked or template generic parameter, or in fact any [compile-time binding](/docs/design/generics/terminology.md#bindings), the lookup is performed from a context where the value of that binding is unknown. Evaluation of an expression involving the binding may still succeed, but will result in a symbolic constant involving that binding. ```carbon class GenericWrapper(T:! type) { var field: T; } fn F[T:! type](x: GenericWrapper(T)) -> T { // ✅ OK, finds `GenericWrapper(T).field`. return x.field; } interface Renderable { fn Draw[self: Self](); } fn DrawChecked[T:! Renderable](c: T) { // `Draw` resolves to `(T as Renderable).Draw` or // `T.(Renderable.Draw)`. c.Draw(); } class Cowboy { fn Draw[self: Self](); } impl Cowboy as Renderable { fn Draw[self: Self](); } fn CallsDrawChecked(c: Cowboy) { // ✅ Calls member of `impl Cowboy as Renderable`. DrawChecked(c); // In contrast to this which calls member of `Cowboy`: c.Draw(); } ``` If the value or type depends on any template bindings, the lookup is redone from a context where the values of those bindings are known, but where the values of any symbolic bindings are still unknown. The lookup results from these two contexts are [combined](#lookup-ambiguity). ```carbon fn DrawTemplate[template T:! type](c: T) { // `Draw` not found in `type`, looked up in the // actual deduced value of `T`. c.Draw(); } fn CallsDrawTemplate(c: Cowboy) { // ✅ Calls member of `Cowboy`: DrawTemplate(c); // Same behavior as: c.Draw(); } ``` Since we have decided to forbid specialization of class templates, see [proposal #2200: Template generics](https://github.com/carbon-language/carbon-lang/pull/2200), the compiler can assume the body of a templated class will be the same for all argument values: ```carbon class TemplateWrapper(template T:! type) { var field: T; } fn G[template T:! type](x: TemplateWrapper(T)) -> T { // ✅ Allowed, finds `TemplateWrapper(T).field`. return x.field; } ``` In addition, the lookup will be performed again when `T` is known. This allows cases where the lookup only succeeds for specific values of `T`: ```carbon class HasField { var field: i32; } class DerivingWrapper(template T:! type) { extend base: T; } fn H[template T:! type](x: DerivingWrapper(T)) -> i32 { // ✅ Allowed, but no name `field` found in template // definition of `DerivingWrapper`. return x.field; } fn CallH(a: DerivingWrapper(HasField), b: DerivingWrapper(i32)) { // ✅ Member `field` in base class found in instantiation. var x: i32 = H(a); // ❌ Error, no member `field` in type of `b`. var y: i32 = H(b); } ``` **Note:** All lookups are done from a context where the values of any symbolic bindings that are in scope are unknown. Unlike for a template binding, the actual value of a symbolic binding never affects the result of member resolution. #### Lookup ambiguity Multiple lookups can be performed when resolving a member access expression with a [template binding](#compile-time-bindings). We resolve this the same way as when looking in multiple interfaces that are [combined with `&`](/docs/design/generics/details.md#combining-interfaces-by-anding-facet-types): - If more than one distinct member is found, after performing [`impl` lookup](#impl-lookup) if necessary, the lookup is ambiguous, and the program is invalid. - If no members are found, the program is invalid. - Otherwise, the result of combining the lookup results is the unique member that was found. ```carbon interface Renderable { fn Draw[self: Self](); } fn DrawTemplate2[template T:! Renderable](c: T) { // Member lookup finds `(T as Renderable).Draw` and the // `Draw` member of the actual deduced value of `T`, if any. c.Draw(); } class Cowboy { fn Draw[self: Self](); } impl Cowboy as Renderable { fn Draw[self: Self](); } class Pig { } impl Pig as Renderable { fn Draw[self: Self](); } class RoundWidget { impl as Renderable { fn Draw[self: Self](); } alias Draw = Renderable.Draw; } class SquareWidget { fn Draw[self: Self]() {} impl as Renderable { alias Draw = Self.Draw; } } fn FlyTemplate[template T:! type](c: T) { c.Fly(); } fn Draw(c: Cowboy, p: Pig, r: RoundWidget, s: SquareWidget) { // ❌ Error: ambiguous. `Cowboy.Draw` and // `(Cowboy as Renderable).Draw` are different. DrawTemplate2(c); // ✅ OK, lookup in type `Pig` finds nothing, so uses // lookup in facet type `Pig as Renderable`. DrawTemplate2(p); // ✅ OK, lookup in type `RoundWidget` and lookup in facet // type `RoundWidget as Renderable` find the same entity. DrawTemplate2(r); // ✅ OK, lookup in type `SquareWidget` and lookup in facet // type `SquareWidget as Renderable` find the same entity. DrawTemplate2(s); // ❌ Error: `Fly` method not found in `Pig` or // `Pig as type`. FlyTemplate(p); } ``` ## `impl` lookup `impl` lookup maps a member of an interface to the corresponding member of the relevant `impl`. It is performed when member access names an interface member, except when the member was found by a search of a facet type scope in a simple member access expression. ### `impl` lookup for simple member access For a simple member access `a.b` where `b` names a member of an interface `I`: - If the interface member was found by searching a non-[facet-type](/docs/design/generics/terminology.md#facet-type) scope `T`, for example a class or an adapter, then `impl` lookup is performed for `T as I`. - In the case where the member was found in a base class of the class that was searched, `T` is the derived class that was searched, not the base class in which the name was declared. - More generally, if the member was found in something the type extends, such as a facet type or mixin, `T` is the type that was initially searched, not what it extended. - Otherwise, `impl` lookup is not performed. The appropriate `impl T as I` implementation is located. The program is invalid if no such `impl` exists. When `T` or `I` depends on a symbolic binding, a suitable constraint must be specified to ensure that such an `impl` will exist. When `T` or `I` depends on a template binding, this check is deferred until the value for the template binding is known. `M` is replaced by the member of the `impl` that corresponds to `M`. [Instance binding](#instance-binding) may also apply if the member is an instance member. For example: ```carbon interface Addable { // #1 fn Add[self: Self](other: Self) -> Self; // #2 default fn Sum[Seq:! Iterable where .ValueType = Self](seq: Seq) -> Self { // ... } alias AliasForSum = Sum; } class Integer { extend impl as Addable { // #3 fn Add[self: Self](other: Self) -> Self; // #4, generated from default implementation for #2. // fn Sum[...](...); } alias AliasForAdd = Addable.Add; } ``` - For `Integer.Sum`, member resolution resolves the name `Sum` to \#2, which is not an instance member. `impl` lookup then locates the `impl Integer as Addable`, and determines that the member access refers to \#4. - For `i.Add(j)` where `i: Integer`, member resolution resolves the name `Add` to \#1, which is an instance member. `impl` lookup then locates the `impl Integer as Addable`, and determines that the member access refers to \#3. Finally, instance binding will be performed as described later. - `Integer.AliasForAdd` finds \#3, the `Add` member of the facet type `Integer as Addable`, not \#1, the interface member `Addable.Add`. - `i.AliasForAdd`, with `i: Integer`, finds \#3, the `Add` member of the facet type `Integer as Addable`, and performs [instance binding](#instance-binding) since the member is an instance member. - `Addable.AliasForSum` finds \#2, the member in the interface `Addable`, and does not perform `impl` lookup. **Note:** When an interface member is added to a class by an alias, `impl` lookup is not performed as part of handling the alias, but will happen when naming the interface member as a member of the class. ```carbon interface Renderable { // #5 fn Draw[self: Self](); } class RoundWidget { impl as Renderable { // #6 fn Draw[self: Self](); } // `Draw` names #5, the member of the `Renderable` interface. alias Draw = Renderable.Draw; } class SquareWidget { // #7 fn Draw[self: Self]() {} impl as Renderable { alias Draw = Self.Draw; } } fn DrawWidget(r: RoundWidget, s: SquareWidget) { // ✅ OK: In the inner member access, the name `Draw` resolves to the // member `Draw` of `Renderable`, #5, which `impl` lookup replaces with // the member `Draw` of `impl RoundWidget as Renderable`, #6. // The outer member access then forms a bound member function that // calls #6 on `r`, as described in "Instance binding". r.(RoundWidget.Draw)(); // ✅ OK: In the inner member access, the name `Draw` resolves to the // member `Draw` of `SquareWidget`, #7. // The outer member access then forms a bound member function that // calls #7 on `s`. s.(SquareWidget.Draw)(); // ❌ Error: In the inner member access, the name `Draw` resolves to the // member `Draw` of `SquareWidget`, #7. // The outer member access fails because we can't call // #7, `Draw[self: SquareWidget]()`, on a `RoundWidget` object `r`. r.(SquareWidget.Draw)(); // ❌ Error: In the inner member access, the name `Draw` resolves to the // member `Draw` of `Renderable`, #5, which `impl` lookup replaces with // the member `Draw` of `impl RoundWidget as Renderable`, #6. // The outer member access fails because we can't call // #6, `Draw[self: RoundWidget]()`, on a `SquareWidget` object `s`. s.(RoundWidget.Draw)(); } base class WidgetBase { // ✅ OK, even though `WidgetBase` does not implement `Renderable`. alias Draw = Renderable.Draw; fn DrawAll[T:! Renderable](v: Vector(T)) { for (var w: T in v) { // ✅ OK. Unqualified lookup for `Draw` finds alias `WidgetBase.Draw` // to `Renderable.Draw`, which does not perform `impl` lookup yet. // Then the compound member access expression performs `impl` lookup // into `impl T as Renderable`, since `T` is known to implement // `Renderable`. Finally, the member function is bound to `w` as // described in "Instance binding". w.(Draw)(); // ❌ Error: `Self.Draw` performs `impl` lookup, which fails // because `WidgetBase` does not implement `Renderable`. w.(Self.Draw)(); } } } class TriangleWidget { extend base: WidgetBase; impl as Renderable; } fn DrawTriangle(t: TriangleWidget) { // ✅ OK: name `Draw` resolves to `Draw` member of `WidgetBase`, which // is `Renderable.Draw`. Then impl lookup replaces that with `Draw` // member of `impl TriangleWidget as Renderable`. t.Draw(); } ``` ### `impl` lookup for compound member access For a compound member access `a.(b)` where `b` names a member of an interface `I`, `impl` lookup is performed for `T as I`, where: - If `b` is an instance member, `T` is the type of `a`. In this case, [instance binding](#instance-binding) is always performed. - Otherwise, `a` is implicitly converted to `I`, and `T` is the result of symbolically evaluating the converted expression. In this case, [instance binding](#instance-binding) is never performed. For example: ```carbon fn AddTwoIntegers(a: Integer, b: Integer) -> Integer { // Since `Addable.Add` is an instance member of `Addable`, `T` // is set to the type of `a`, and so uses `Integer as Addable`. return a.(Addable.Add)(b); // ^ impl lookup and instance binding here // Impl lookup transforms this into #3: // return a.((Integer as Addable).Add)(b); // which no longer requires impl lookup. // ❌ By the same logic, in this example, `T` is set to the // type of `Integer`, and so uses `type as Addable`, which // isn't implemented. return Integer.(Addable.Add)(...); } fn SumIntegers(v: Vector(Integer)) -> Integer { // Since `Addable.Sum` is a non-instance member of `Addable`, // `Integer` is implicitly converted to `Addable`, and so uses // `Integer as Addable`. Integer.(Addable.Sum)(v); // ^ impl lookup but no instance binding here // Impl lookup transforms this into #4: // ((Integer as Addable).Sum)(v); // which no longer requires impl lookup. var a: Integer; // ❌ This is an error since `a` does not implicitly convert to // a type. a.(Addable.Sum)(v); } ``` ## Instance binding Next, _instance binding_ may be performed. This associates an expression with a particular object or value instance. For example, this is the value bound to `self` when calling a method. For the simple member access syntax `x.y`, if `x` is an entity that has member names, such as a namespace or a type, then `y` is looked up within `x`, and instance binding is not performed. Otherwise, `y` is looked up within the type of `x` and instance binding is performed if an instance member is found. If instance binding is to be performed, the result of instance binding depends on what instance member `M` was found: - For a field member of a struct type or tuple type, `x` is converted to a struct or tuple form by [form decomposition](/docs/design/values.md#category-conversions), and the `.f` element of the result of that conversion becomes the result of `x.f`. All other elements are [discarded](/docs/design/values.md#form-conversions). - For a field member in class `C`, `x` is required to be of type `C` or of a type derived from `C`. The result is the corresponding subobject within `x`. If `x` is an [initializing expression](/docs/design/values.md#initializing-expressions), then a [temporary is materialized](/docs/design/values.md#temporary-materialization) for `x`. The result of `x.y` has the same [expression category](/docs/design/values.md#expression-categories) as the possibly materialized `x`. ```carbon class Size { var width: i32; var height: i32; } var dims: Size = {.width = 1, .height = 2}; // `dims.width` denotes the field `width` of the object `dims`. Print(dims.width); // `dims` is a reference expression, so `dims.height` is a // reference expression. dims.height = 3; fn GetSize() -> Size; // `GetSize()` returns an initializing expression, which is // materialized as a temporary on member access, so // `GetSize().width` is an ephemeral reference expression. Print(GetSize().width); ``` - For a method, the result is a _bound method_, which is a value `F` such that a function call `F(args)` behaves the same as a call to `M(args)` with the `self` parameter initialized by `x`. ```carbon class Blob { fn Mutate[ref self: Self](n: i32); } fn F(p: Blob*) { // ✅ OK, forms bound method `((*p).M)` and calls it. // This calls `Blob.Mutate` with `self` initialized by `*p` // and `n` initialized by `5`. (*p).Mutate(5); // ✅ OK, same as above. let bound_m: auto = (*p).Mutate; bound_m(5); } ``` The compound member access syntax `x.(Y)`, where `Y` names an instance member, always performs instance binding. It is an error if `Y` is already bound to an instance member. For example: ```carbon interface DebugPrint { // instance member fn Print[self:Self](); } impl i32 as DebugPrint; impl type as DebugPrint; fn Debug() { var i: i32 = 1; // Prints `1` using `(i32 as DebugPrint).Print` bound to `i`. i.(DebugPrint.Print)(); // Prints `i32` using `(type as DebugPrint).Print` bound to `i32`. i32.(DebugPrint.Print)(); // ❌ This is an error since `i32.(DebugPrint.Print)` is already // bound, and may not be bound again to `i`. i.(i32.(DebugPrint.Print))(); } ``` To get the `M` member of interface `I` for a type `T`, use `(T as I).M`, as this doesn't attempt to perform instance binding on `T`, in contrast to `T.(I.M)`. ## Non-instance members If instance binding is not performed, the result is the member `M` determined by member resolution and `impl` lookup. Evaluating the member access expression evaluates the first argument and discards the result. An expression that names an instance member, but for which instance binding is not performed, can only be used as the second operand of a compound member access or as the target of an `alias` declaration. ```carbon class C { fn StaticMethod(); var field: i32; class Nested {} } fn CallStaticMethod(c: C) { // ✅ OK, calls `C.StaticMethod`. C.StaticMethod(); // ✅ OK, evaluates expression `c`, discards the result, then // calls `C.StaticMethod`. c.StaticMethod(); // ❌ Error: name of instance member `C.field` can only be used in // a member access or alias. C.field = 1; // ✅ OK, instance binding is performed by outer member access, // same as `c.field = 1;` c.(C.field) = 1; // ✅ OK let T:! type = C.Nested; // ❌ Error: value of `:!` binding is not compile-time because it // refers to local variable `c`. let U:! type = c.Nested; } ``` ## Non-vacuous member access restriction The first operand of a member access expression must be used in some way: a compound member access must result in `impl` lookup, instance binding, or both. In a simple member access, this always holds, because the first operand is always used for lookup. ``` interface Printable { fn Print[self: Self](); } impl i32 as Printable; fn MemberAccess(n: i32) { // ✅ OK: `(i32 as Printable).Print` is the `Print` member of the // `i32 as Printable` facet corresponding to the `Printable.Print` // interface member. // `n.((i32 as Printable).Print)` is that member function bound to `n`. n.((i32 as Printable).Print)(); // ✅ Same as above, `n.(Printable.Print)` is effectively interpreted // as `n.((T as Printable).Print)()`, where `T` is the type of `n`. // Performs impl lookup and then instance binding. n.(Printable.Print)(); } interface Factory { fn Make() -> Self; } impl i32 as Factory; // ✅ OK, member `Make` of interface `Factory`. alias X1 = Factory.Make; // ❌ Error, compound access without impl lookup or instance binding. alias X2 = Factory.(Factory.Make); // ✅ OK, member `Make` of `impl i32 as Factory`. alias X3 = (i32 as Factory).Make; // ❌ Error, compound access without impl lookup or instance binding. alias X4 = i32.((i32 as Factory).Make); ``` ## Precedence and associativity Member access expressions associate left-to-right: ``` class A { class B { fn F(); } } interface B { fn F(); } impl A as B; fn Use(a: A) { // Calls member `F` of class `A.B`. (a.B).F(); // Calls member `F` of interface `B`, as implemented by type `A`. a.(B.F)(); // Same as `(a.B).F()`. a.B.F(); } ``` Member access has lower precedence than primary expressions, and higher precedence than all other expression forms. ``` // ✅ OK, `*` has lower precedence than `.`. Same as `(A.B)*`. var p: A.B*; // ✅ OK, `1 + (X.Y)` not `(1 + X).Y`. var n: i32 = 1 + X.Y; ``` ## Alternatives considered - [Separate syntax for static versus dynamic access, such as `::` versus `.`](/proposals/p0989.md#separate-syntax-for-static-versus-dynamic-access) - [Use a different lookup rule for names in templates](/proposals/p0989.md#use-a-different-lookup-rule-in-templates) - [Meaning of `Type.Interface`](/proposals/p0989.md#meaning-of-typeinterface) ## References - Proposal [#989: member access expressions](https://github.com/carbon-language/carbon-lang/pull/989) - [Question for leads: constrained template name lookup](https://github.com/carbon-language/carbon-lang/issues/949) - Proposal [#2360: Types are values of type `type`](https://github.com/carbon-language/carbon-lang/pull/2360) - Proposal [#2550: Simplified package declaration for the `Main` package](https://github.com/carbon-language/carbon-lang/pull/2550) ================================================ FILE: docs/design/expressions/pointer_operators.md ================================================ # Pointer operators ## Table of contents - [Overview](#overview) - [Details](#details) - [Precedence](#precedence) - [Alternatives considered](#alternatives-considered) - [References](#references) ## Overview Carbon provides the following operators related to pointers: - `&` as a prefix unary operator takes the address of an object, forming a pointer to it. - `*` as a prefix unary operator dereferences a pointer. Note that [member access expressions](member_access.md) include an `->` form that implicitly performs a dereference in the same way as the `*` operator. ## Details The semantic details of pointer operators are collected in the main [pointers](/docs/design/values.md#pointers) design. The syntax and precedence details are covered here. The syntax tries to remain as similar as possible to C++ pointer types as they are commonly written in code and are expected to be extremely common and a key anchor of syntactic similarity between the languages. ### Precedence These operators have high precedence. Only [member access](member_access.md) expressions can be used as an unparenthesized operand to them. The two prefix operators `&` and `*` are generally above the other unary and binary operators and can appear inside them as unparenthesized operands. For the full details, see the [precedence graph](README.md#precedence). ## Alternatives considered - [Alternative pointer syntaxes](/proposals/p2006.md#alternative-pointer-syntaxes) ## References - [Proposal #2006: Values, variables, and pointers](/proposals/p2006.md) ================================================ FILE: docs/design/expressions/type_operators.md ================================================ # Type operators ## Table of contents - [Overview](#overview) - [Details](#details) - [Precedence](#precedence) - [Alternatives considered](#alternatives-considered) - [References](#references) ## Overview Carbon provides the following operators to transform types: - `const` as a prefix unary operator produces a `const`-qualified type. - `*` as a postfix unary operator produces a pointer _type_ to some other type. The pointer type operator is also covered as one of the [pointer operators](pointer_operators.md). ## Details The semantic details of both `const`-qualified types and pointer types are provided as part of the [values](/docs/design/values.md) design: - [`const`-qualified types](/docs/design/values.md#const-qualified-types) - [Pointers](/docs/design/values.md#pointers) The syntax of these operators tries to mimic the most common appearance of `const` types and pointer types in C++. ### Precedence Because these are type operators, they don't have many precedence relationship with non-type operators. - `const` binds more tightly than `*` and can appear unparenthesized in an operand, despite being both a unary operator and having whitespace separating it. - This allows the syntax of a pointer to a `const i32` to be `const i32*`, which is intended to be familiar to C++ developers. - Forming a `const` pointer type requires parentheses: `const (i32*)`. - All type operators bind more tightly than `as` so they can be used in its type operand. - This also allows a desirable transitive precedence with `if`: `if condition then T* else U*`. ## Alternatives considered - [Alternative pointer syntaxes](/proposals/p2006.md#alternative-pointer-syntaxes) - [Alternative syntaxes for locals](/proposals/p2006.md#alternative-syntaxes-for-locals) - [Make `const` a postfix rather than prefix operator](/proposals/p2006.md#make-const-a-postfix-rather-than-prefix-operator) ## References - [Proposal #2006: Values, variables, and pointers](/proposals/p2006.md) ================================================ FILE: docs/design/functions.md ================================================ # Functions ## Table of contents - [Overview](#overview) - [Function definitions](#function-definitions) - [Return clause](#return-clause) - [`return` statements](#return-statements) - [Function declarations](#function-declarations) - [Function calls](#function-calls) - [Functions in other features](#functions-in-other-features) - [Alternatives considered](#alternatives-considered) - [References](#references) ## Overview > **TODO:** Update this document to reflect the introduction of function values, > function types, and the `Call` interface in > [p2875: Functions, function types, and function calls](/proposals/p2875.md). > **TODO:** Update this document to reflect the changes to named functions in > [p3848: Lambdas](/proposals/p3848.md). Functions are the core building block for applications. Carbon's basic function syntax is: - _parameter_: _identifier_ `:` _expression_ - _parameter-list_: _[ parameter_ `,` _parameter_ `,` _... ]_ - _return-clause_: _[_ `->` _< expression |_ `auto` _> ]_ - _signature_: `fn` _identifier_ `(` _parameter-list_ `)` _return-clause_ - _function-definition_: _signature_ `{` _statements_ `}` - _function-declaration_: _signature_ `;` - _function-call_: _identifier_ `(` _[ expression_ `,` _expression_ `,` _... ]_ `)` A function with only a signature and no body is a function declaration, or forward declaration. When the body is a present, it's a function definition. The body introduces nested scopes which may contain local variable declarations. ## Function definitions A basic function definition may look like: ```carbon fn Add(a: i64, b: i64) -> i64 { return a + b; } ``` This declares a function called `Add` which accepts two `i64` parameters, the first called `a` and the second called `b`, and returns an `i64` result. It returns the result of adding the two arguments. C++ might declare the same thing: ```cpp std::int64_t Add(std::int64_t a, std::int64_t b) { return a + b; } // Or with trailing return type syntax: auto Add(std::int64_t a, std::int64_t b) -> std::int64_t { return a + b; } ``` ### Return clause The return clause of a function specifies the return type using one of three possible syntaxes: - `->` followed by an _expression_, such as `i64`, directly states the return type. This expression will be evaluated at compile-time, so must be valid in that context. - For example, `fn ToString(val: i64) -> String;` has a return type of `String`. - `->` followed by the `auto` keyword indicates that [type inference](type_inference.md) should be used to determine the return type. - For example, `fn Echo(val: i64) -> auto { return val; }` will have a return type of `i64` through type inference. - Declarations must have a known return type, so `auto` is not valid. - The function must have precisely one `return` statement. That `return` statement's expression will then be used for type inference. - Omission indicates that the return type is the empty tuple, `()`. - For example, `fn Sleep(seconds: i64);` is similar to `fn Sleep(seconds: i64) -> ();`. - `()` is similar to a `void` return type in C++. > **TODO:** Update this section to cover return forms, as discussed > [here](values.md#function-calls-and-returns). ### `return` statements The [`return` statement](control_flow/return.md) is essential to function control flow. It ends the flow of the function and returns execution to the caller. When the [return clause](#return-clause) is omitted, the `return` statement has no expression argument, and function control flow implicitly ends after the last statement in the function's body as if `return;` were present. When the return clause is provided, including when it is `-> ()`, the `return` statement must have an expression that is convertible to the return type, and a `return` statement must be used to end control flow of the function. > **TODO:** Update this section to cover the requirements on the form of the > expression. ## Function declarations Functions may be declared separate from the definition by providing only a signature, with no body. This provides an API which may be called. For example: ```carbon // Declaration: fn Add(a: i64, b: i64) -> i64; // Definition: fn Add(a: i64, b: i64) -> i64 { return a + b; } ``` The corresponding definition may be provided later in the same file or, when the declaration is in an [API file of a library](code_and_name_organization/#libraries), in an implementation file of the same library. The signature of a function declaration must match the corresponding definition. This includes the [return clause](#return-clause); even though an omitted return type has equivalent behavior to `-> ()`, the presence or omission must match. ## Function calls Function calls use a function's identifier to pass multiple expression arguments corresponding to the function signature's parameters. For example: ```carbon fn Add(a: i64, b: i64) -> i64 { return a + b; } fn Run() { Add(1, 2); } ``` Here, `Add(1, 2)` is a function call expression. `Add` refers to the function definition's identifier. The parenthesized arguments `1` and `2` are passed to the `a` and `b` parameters of `Add`. ## Functions in other features Other designs build upon basic function syntax to add advanced features: - [Generic functions](generics/overview.md#generic-functions) adds support for deduced parameters and compile-time parameters. - [Class member functions](classes.md#member-functions) adds support for methods and class functions. ## Alternatives considered - [Function keyword](/proposals/p0438.md#function-keyword) - [Only allow `auto` return types if parameters are compile-time](/proposals/p0826.md#only-allow-auto-return-types-if-parameters-are-generic) - [Provide alternate function syntax for concise return type inference](/proposals/p0826.md#provide-alternate-function-syntax-for-concise-return-type-inference) - [Allow separate declaration and definition](/proposals/p0826.md#allow-separate-declaration-and-definition) ## References - Proposal [#438: Add statement syntax for function declarations](https://github.com/carbon-language/carbon-lang/pull/438) - Proposal [#826: Function return type inference](https://github.com/carbon-language/carbon-lang/pull/826) ================================================ FILE: docs/design/generics/README.md ================================================ # Generics This directory contains the collection of documents describing the generics feature of Carbon: - [Overview](overview.md) - A high-level description of the generics design, with pointers to other design documents that dive deeper into individual topics - [Goals](goals.md) - The motivation and principles guiding the design direction - [Terminology](terminology.md) - A glossary establishing common terminology for describing the design - [Detailed design](details.md) - In-depth description - [Appendix: Coherence](appendix-coherence.md) - Describes the rationale for Carbon's choice to have coherent generics, and the alternatives. - [Appendix: Rewrite constraints](appendix-rewrite-constraints.md) - Describes the detailed rules governing rewrite constraints, and why resolving them terminates. - [Appendix: Witness tables](appendix-witness.md) - Describes an implementation strategy for checked generics, and Carbon's rationale for only using it for dynamic dispatch. ================================================ FILE: docs/design/generics/appendix-coherence.md ================================================ # Carbon: alternatives to coherence This document explains the rationale for choosing to make [implementation coherence](terminology.md#coherence) [a goal for Carbon](goals.md#coherence), and the alternatives considered. ## Table of contents - [Approach taken: coherence](#approach-taken-coherence) - [The "Hashtable Problem"](#the-hashtable-problem) - [Rejected alternative: no orphan rule](#rejected-alternative-no-orphan-rule) - [Rejected alternative: incoherence](#rejected-alternative-incoherence) - [Incoherence means context sensitivity](#incoherence-means-context-sensitivity) - [Rejected variation: dynamic implementation binding](#rejected-variation-dynamic-implementation-binding) - [Rejected variation: manual conflict resolution](#rejected-variation-manual-conflict-resolution) ## Approach taken: coherence The main thing to understand is that coherence is a desirable property, but to get that property we need an orphan rule, and that rule has a cost. It in particular limits how much control users of a type have over how that type implements interfaces. There are a few main problematic use cases to consider: - Selecting between multiple implementations of an interface for a type. For example selecting the implementation of the `Comparable` interface for a `Song` type to support "by title", "by artist", and "by album" orderings. - Implementing an interface for a type when there is no relationship between the libraries defining the interface and the type. - When the implementation of an interface for a type relies on something that can't be referenced from the file or files where the implementation is allowed to be defined. These last two cases are highlighted as concerns in Rust in [Rust RFC #1856: orphan rules are stricter than we would like](https://github.com/rust-lang/rfcs/issues/1856). Since Carbon is bundling interface implementations into types, for the convenience and expressiveness that provides, we satisfy those use cases by giving the user control over the type of a value. This means having facilities for defining new [compatible types](terminology.md#compatible-types) with different interface implementations, and casting between those types as needed. ## The "Hashtable Problem" The "Hashtable problem" is that the specific hash function used to compute the hash of keys in a hashtable must be the same when adding an entry, when looking it up, and other operations like resizing. So a hashtable type is dependent on both the key type, and the key type's implementation of the `Hashable` interface. If the key type can have more than one implementation of `Hashable`, there needs to be some mechanism for choosing a single one to be used consistently by the hashtable type, or the invariants of the type will be violated. Without the orphan rule to enforce coherence, we might have a situation like this: - Package `Container` defines a `HashSet` type. ``` package Container; class HashSet(Key:! Hashable) { ... } ``` - A `Song` type is defined in package `SongLib`. - Package `SongHashArtistAndTitle` defines an implementation of `Hashable` for `SongLib.Song`. ``` package SongHashArtistAndTitle; import SongLib; impl SongLib.Song as Hashable { fn Hash[self: Self]() -> u64 { ... } } ``` - Package `SongUtil` uses the `Hashable` implementation from `SongHashArtistAndTitle` to define a function `IsInHashSet`. ``` package SongUtil; import SongLib; import SongHashArtistAndTitle; import Containers; fn IsInHashSet( s: SongLib.Song, h: Containers.HashSet(SongLib.Song)*) -> bool { return h->Contains(s); } ``` - Package `SongHashAppleMusicURL` defines a different implementation of `Hashable` for `SongLib.Song` than package `SongHashArtistAndTitle`. ``` package SongHashAppleMusicURL; import SongLib; impl SongLib.Song as Hashable { fn Hash[self: Self]() -> u64 { ... } } ``` - Finally, package `Trouble` imports `SongHashAppleMusicURL`, creates a hash set, and then calls the `IsInHashSet` function from package `SongUtil`. ``` package Trouble; import SongLib; import SongHashAppleMusicURL; import Containers; import SongUtil; fn SomethingWeirdHappens() { var unchained_melody: SongLib.Song = ...; var song_set: auto = Containers.HashSet(SongLib.Song).Make(); song_set.Add(unchained_melody); // Either this is a compile error or does something unexpected. if (SongUtil.IsInHashSet(unchained_melody, &song_set)) { Print("This is expected, but doesn't happen."); } else { Print("This is what happens even though it is unexpected."); } } ``` The issue is that in package `Trouble`, the `song_set` is created in a context where `SongLib.Song` has a `Hashable` implementation from `SongHashAppleMusicURL`, and stores `unchained_melody` under that hash value. When we go to look up the same song in `SongUtil.IsInHashSet`, it uses the hash function from `SongHashArtistAndTitle` which returns a different hash value for `unchained_melody`, and so reports the song is missing. **Background:** [This post](https://gist.github.com/nikomatsakis/1421744) discusses the hashtable problem in the context of Haskell, and [this 2011 Rust followup](https://www.mail-archive.com/rust-dev@mozilla.org/msg01024.html) discusses how to detect problems at compile time. ## Rejected alternative: no orphan rule In Swift an implementation of an interface, or a "protocol" as it is called in Swift, can be provided in any module. As long as any module provides an implementation, that implementation is [used globally throughout the program](https://stackoverflow.com/questions/48762971/swift-protocol-conformance-by-extension-between-frameworks). In Swift, since some protocol implementations can come from the runtime environment provided by the operating system, multiple implementations for a protocol can arise as a runtime warning. When this happens, Swift picks one implementation arbitrarily. In Carbon, we could make this a build time error. However, there would be nothing preventing two independent libraries from providing conflicting implementations. Furthermore, the error would only be diagnosed at link time. ## Rejected alternative: incoherence ### Incoherence means context sensitivity The undesirable result of incoherence is that the interpretation of source code changes based on imports. In particular, imagine there is a function call that depends on a type implementing an interface, and two different implementations are defined in two different libraries. A call to that function will be treated differently depending on which of those two libraries are imported: - If neither is imported, it is an error. - If both are imported, it is ambiguous. - If only one is imported, you get totally different code executed depending on which it is. Furthermore, this means that the behavior of a file can depend on an import even if nothing from that package is referenced explicitly. In general, Carbon is [avoiding this sort of context sensitivity](/docs/project/principles/low_context_sensitivity.md). This context sensitivity would make moving code between files when refactoring more difficult and less safe. ### Rejected variation: dynamic implementation binding One possible approach would be to bind interface implementations to a value at the point it was created. In [the example above](#the-hashtable-problem), the implementation of the `Hashable` interface for `Song` would be fixed for the `song_set` `HashSet` object based on which implementation was in scope in the body of the `SomethingWeirdHappens` function. This idea is discussed briefly in section 5.4 on separate compilation of WG21 proposal n1848 for implementing "Indiana" C++0x concepts ([1](https://citeseerx.ist.psu.edu/pdf/6e1398d22fd6a3e31aba2519d7a00a8f0d93e7e8), and [2](https://wg21.link/n1848)). This has some downsides: - It is harder to reason about. The behavior of `SongUtil.IsInHashSet` depends on the dynamic behavior of the program. At the time of the call, we may have no idea where the `HashSet` argument was created. - An object may be created far from a call that has a particular interface requirement, with no guarantee that the object was created with any implementation of the interface at all. This error would only be detected at runtime, not at type checking time. - It requires more data space at runtime because we need to store a pointer to the witness table representing the implementation with the object, since it varies instead of being known statically. - It is slower to execute from dynamic dispatch and the inability to inline. - In some cases it may not be feasible to use dynamic dispatch. For example, if the return type of an interface method involves an associated constant, we might not know the calling convention of the function without knowing some details about the value of that constant. As a result, this doesn't make sense as the default behavior for Carbon based on its [goals](/docs/project/goals.md). That being said, this could be a feature added later as opt-in behavior to either allow users to reduce code size or support use cases that require dynamic dispatch. ### Rejected variation: manual conflict resolution Carbon could alternatively provide some kind of manual disambiguation syntax to resolve problems where they arise. The problems with this approach have been [considered in the context of Rust](https://github.com/Ixrec/rust-orphan-rules#whats-wrong-with-incoherence). A specific example of this approach is called [scoped conformance](https://forums.swift.org/t/scoped-conformances/37159), where the conflict resolution is based on limiting the visibility of implementations to particular scopes. This hasn't been implemented, but it has the drawbacks described above. Depending on the details of the implementation, either: - there are incompatible values with types that have the same name, or - it is difficult to reason about the program's behavior because it behaves like [dynamic implementation binding](#rejected-variation-dynamic-implementation-binding) (though perhaps with a monomorphization cost instead of a runtime cost). In addition, this can create unsoundness when combined with dynamic downcasts and a more complex, less predictable implementation model as [discussed in Swift](https://forums.swift.org/t/retroactive-conformances-dynamic-downcast-type-unsoundness/73890). This approach would be particularly complex in Carbon due to supporting [impl specialization](terminology.md#specialization). ================================================ FILE: docs/design/generics/appendix-rewrite-constraints.md ================================================ # Carbon: Rewrite constraint details This document explains the rationale for choosing to make [implementation coherence](terminology.md#coherence) [a goal for Carbon](goals.md#coherence), and the alternatives considered. ## Table of contents - [Rewrite constraints](#rewrite-constraints) - [Combining constraints with `&`](#combining-constraints-with-) - [Combining constraints with `and`](#combining-constraints-with-and) - [Combining constraints with `extend`](#combining-constraints-with-extend) - [Combining constraints with `require` and `impls`](#combining-constraints-with-require-and-impls) - [Rewrite constraint resolution](#rewrite-constraint-resolution) - [Precise rules and termination](#precise-rules-and-termination) - [Qualified name lookup](#qualified-name-lookup) - [Type substitution](#type-substitution) - [Examples](#examples) - [Termination](#termination) ## Rewrite constraints Rewrite constraints are [`where` clauses](details.md#where-constraints) of the form `.AssociatedConstant = Value`. Given `T:! A where .B = C`, references to `T.(A.B)` are rewritten to `C`. This appendix describes the precise rules governing them. ## Combining constraints with `&` Suppose we have `X = C where .R = A` and `Y = C where .R = B`. What should `C & X` produce? What should `X & Y` produce? - Combining two rewrite rules with different rewrite targets results in a facet type where the associated constant is ambiguous. Given `T:! X & Y`, the type expression `T.R` is ambiguous between a rewrite to `A` and a rewrite to `B`. But given `T:! X & X`, `T.R` is unambiguously rewritten to `A`. - Combining a constraint with a rewrite rule with a constraint with no rewrite rule preserves the rewrite rule, so `C & X` is the same as `X`. For example, supposing that `interface Container` extends `interface Iterable`, and `Iterable` has an associated constant `Element`, the constraint `Container & (Iterable where .Element = i32)` is the same as the constraint `(Container & Iterable) where .Element = i32` which is the same as the constraint `Container where .Element = i32`. If the rewrite for an associated constant is ambiguous, the facet type is rejected during [constraint resolution](#rewrite-constraint-resolution). > **Alternative considered:** We could perhaps say that `X & Y` results in a > facet type where the type of `R` has the union of the interface of `A` and the > interface of `B`, and that `C & X` similarly results in a facet type where the > type of `R` has the union of the interface of `A` and the interface originally > specified by `C`. ## Combining constraints with `and` It's possible for one `=` constraint in a `where` to refer to another. When this happens, the facet type `C where A and B` is interpreted as `(C where A) where B`, so rewrites in `A` are applied immediately to names in `B`, but rewrites in `B` are not applied to names in `A` until the facet type is [resolved](#rewrite-constraint-resolution): ```carbon interface C { let T:! type; let U:! type; let V:! type; } class M { alias Me = Self; } // ✅ Same as `C where .T = M and .U = M.Me`, which is // the same as `C where .T = M and .U = M`. fn F[A:! C where .T = M and .U = .T.Me]() {} // ❌ No member `Me` in `A.T:! type`. fn F[A:! C where .U = .T.Me and .T = M]() {} ``` ## Combining constraints with `extend` Within an interface or named constraint, `extend` can be used to extend a constraint that has rewrites. ```carbon interface A { let T:! type; let U:! type; } interface B { extend A where .T = .U and .U = i32; } var n: i32; // ✅ Resolved constraint on `T` is // `B where .(A.T) = i32 and .(A.U) = i32`. // `T.(A.T)` is rewritten to `i32`. fn F(T:! B) -> T.(A.T) { return n; } ``` ## Combining constraints with `require` and `impls` Within an interface or named constraint, the `require T impls C` and `require Self impls C` syntaxes do not change the type of `T` or `Self`, respectively, so any `=` constraints that they specify do not result in rewrites being performed when the type `T` or `Self` is later used. Such `=` constraints are equivalent to `==` constraints: ```carbon interface A { let T:! type; let U:! type; } constraint C { extend A where .T = .U and .U = i32; } constraint D { extend A where .T == .U and .U == i32; } interface B { // OK, equivalent to `require Self impls D;` or // `require Self impls A where .T == .U and .U == i32;`. require Self impls C; } var n: i32; // ❌ No implicit conversion from `i32` to `T.(A.T)`. // Resolved constraint on `T` is // `B where T.(A.T) == T.(A.U) and T.(A.U) == i32`. // `T.(A.T)` is single-step equal to `T.(A.U)`, and // `T.(A.U)` is single-step equal to `i32`, but // `T.(A.T)` is not single-step equal to `i32`. fn F(T:! B) -> T.(A.T) { return n; } ``` Because `=` constraints are effectively treated as `==` constraints in an `require Self impls C` or `require T impls C` declaration in an interface or named constraint, it is an error to specify such a `=` constraint directly in `C`. A purely syntactic check is used to determine if an `=` is specified directly in an expression: - An `=` constraint is specified directly in its enclosing `where` expression. - If an `=` constraint is specified directly in an operand of an `&` or `(`...`)`, then it is specified directly in that enclosing expression. For example: ```carbon // Compile-time identity function. fn Identity[T:! type](x:! T) -> T { return x; } interface E { // ❌ Rewrite constraint specified directly. require Self impls A where .T = .U and .U = i32; // ❌ Rewrite constraint specified directly. require Self impls type & (A where .T = .U and .U = i32); // ✅ Not specified directly, but does not result // in any rewrites being performed. require Self impls Identity(A where .T = .U and .U = i32); } ``` The same rules apply to `where`...`impls` constraints. Note that `.T == U` constraints are also not allowed in this context, because the reference to `.T` is rewritten to `.Self.T`, and `.Self` is ambiguous. ```carbon // ❌ Rewrite constraint specified directly in `impls`. fn F[T:! A where .U impls (A where .T = i32)](); // ❌ Reference to `.T` in same-type constraint is ambiguous: // does this mean the outer or inner `.Self.T`? fn G[T:! A where .U impls (A where .T == i32)](); // ✅ Not specified directly, but does not result // in any rewrites being performed. Return type // is not rewritten to `i32`. fn H[T:! type where .Self impls C]() -> T.(A.U); // ✅ Return type is rewritten to `i32`. fn I[T:! C]() -> T.(A.U); ``` ## Rewrite constraint resolution When a facet type is used as the declared type of a facet `T`, the constraints that were specified within that facet type are _resolved_ to determine the constraints that apply to `T`. This happens: - When the constraint is used explicitly when declaring a symbolic binding, like a generic parameter or associated constant, of the form `T:! Constraint`. - When declaring that a type implements a constraint with an `impl` declaration, such as `impl T as Constraint`. Note that this does not include `require` ... `impls` constraints appearing in `interface` or `constraint` declarations. In each case, the following steps are performed to resolve the facet type's abstract constraints into a set of constraints on `T`: - Rewrites are performed on other rewrites in order to find a fixed point, where no rewrite applies within any other rewrite. If no fixed point exists, the generic parameter declaration or `impl` declaration is invalid. - Rewrites are resolved in order from left to right. - The left-most rewrite of a given associated constant is used for rewriting all other rewrite constraints that refer to it. - If multiple rewrites are specified for the same associated constant, they are required to be identical, and duplicates are discarded. Identical is taken to mean that they resolve to the same value at the time of resolution. - Rewrites are performed throughout the other constraints in the facet type -- that is, in any `==` constraints and `impls` constraints -- and the type `.Self` is replaced by `T` throughout the constraint. ```carbon interface I { let X:! type; let Y:! type; } // ✅ `.X` in `.Y = .X` is rewritten to `i32` when initially // forming the facet type. // Nothing to do during constraint resolution. fn InOrder[T:! I where .X = i32 and .Y = .X]() {} // ✅ Facet type has `.X = .Y` before constraint resolution. // That rewrite is resolved to `.X = i32`. fn Reordered[T:! I where .X = .Y and .Y = i32]() {} // ✅ Facet type has `.Y = .X` before constraint resolution. // That rewrite is resolved to `.Y = i32`. fn ReorderedIndirect[T:! (I where .X = i32) & (I where .Y = .X)]() {} // ❌ Constraint resolution fails because // no fixed point of rewrites exists. fn Cycle[T:! I where .X = .Y and .Y = .X]() {} ``` To find a fixed point, we can perform rewrites on other rewrites, cycling between them until they don't change or until a rewrite would apply to itself. In the latter case, we have found a cycle and can reject the constraint. Note that it's not sufficient to expand only a single rewrite until we hit this condition: ```carbon // ❌ Constraint resolution fails because // no fixed point of rewrites exists. // If we only expand the right-hand side of `.X`, // we find `.Y`, then `.Y**`, then `.Y****`, and so on, // and never detect a cycle. // If we alternate between them, we find // `.X = .Y*`, then `.Y = .Y**`, then `.Z = .Y***`, // then `.X = .Y**`, then detect that the `.Y` rewrite // would apply to itself. fn IndirectCycle[T:! I where .X = .Y and .Y = .Z* and .Z = .Y*](); ``` After constraint resolution, no references to rewritten associated constants remain in the constraints on `T` and there is at most one rewrite for each associated constant. The following examples each treat the two assignments of `.X` as being identical, though they are written differently: ```carbon fn Identical(T:! I where .X = () and .X = .Y and .Y = ()) {} fn IdenticalNoCycle(T:! I where .X = () and .X = .Y and .Y = .X) {} fn IdenticalNested(T:! (I where .X = ()) where .X = .Y and .Y = ()) {} ``` The rewrite constraints of the current facet type are all available, so both rewrites of `.X` can be seen to be assigning `()`. In the second example, the value of `.X` is rewritten into `.Y = .X` from the left-most rewrite of `.X`, giving `.Y = ()` rather than `.Y = .Y`, so we are able to find a fixed point. But the following does not have the rewrite of `.Y` available at the time of resolving the two rewrites of `.X`, so the rewrites are invalid: ```carbon fn NotIdentical(T:! (I where .X = () and .X = .Y) where .Y = ()) {} ``` When combining two facet types together with `&`, the rewrite constraints are concatenated together for resolution, with the rewrites from the LHS of the `&` operator coming first and the rewrite from the RHS coming second: `(I where .X = ()) & (I where .Y = ())` is resolved as `(I where .X = () and .Y = ())`. If a facet type is never used to constrain a type, it is never subject to constraint resolution, and it is possible for a facet type to be formed for which constraint resolution would always fail. For example: ```carbon package Broken; interface I { let X:! type; let Y:! type; } let Bad:! auto = (I where .X = .Y) & (I where .Y = .X); // Bad is not used here. ``` In such cases, the facet type `Broken.Bad` is not usable: any attempt to use that facet type to constrain a type would perform constraint resolution, which would always fail because it would discover a cycle between the rewrites for `.X` and for `.Y`. In order to ensure that such cases are diagnosed, a trial constraint resolution is performed for all facet types. Note that this trial resolution can be skipped for facet types that are actually used, which is the common case. ## Precise rules and termination This section explains the detailed rules used to implement rewrites. There are two properties we aim to satisfy: 1. After type-checking, no symbolic references to associated constants that have an associated rewrite rule remain. 2. Type-checking always terminates in a reasonable amount of time, ideally linear in the size of the problem. Rewrites are applied in two different phases of program analysis. - During qualified name lookup and type checking for qualified member access, if a rewritten member is looked up, the right-hand side's value and type are used for subsequent checks. - During substitution, if the symbolic name of an associated constant is substituted into, and substitution into the left-hand side results in a value with a rewrite for that constant, that rewrite is applied. In each case, we always rewrite to a value that satisfies property 1 above, and these two steps are the only places where we might form a symbolic reference to an associated cosntant, so property 1 is recursively satisfied. Moreover, we apply only one rewrite in each of the above cases, satisfying property 2. ### Qualified name lookup Qualified name lookup into either a facet parameter or into an expression whose type is a symbolic type `T` -- either a facet parameter or an associated facet -- considers names from the facet type `C` of `T`, that is, from `T`’s declared type. ```carbon interface C { let M:! i32; let U:! C; } fn F[T:! C](x: T) { // Value is C.M in all four of these let a: i32 = x.M; let b: i32 = T.M; let c: i32 = x.U.M; let d: i32 = T.U.M; } ``` When looking up the name `N`, if `C` is an interface `I` and `N` is the name of an associated constant in that interface, the result is a symbolic constant representing "the member `N` of `I`". If `C` is formed by combining interfaces with `&`, all such results are required to find the same associated constant, otherwise we reject for ambiguity. If a member of a class or interface is named in a qualified name lookup, the type of the result is determined by performing a substitution. For an interface, `Self` is substituted for the self type, and any parameters for that class or interface (and enclosing classes or interfaces, if any) are substituted into the declared type. ```carbon interface SelfIface { fn Get[self: Self]() -> Self; } class UsesSelf(T:! type) { // Equivalent to `fn Make() -> UsesSelf(T)*;` fn Make() -> Self*; impl as SelfIface; } // ✅ `T = i32` is substituted into the type of `UsesSelf(T).Make`, // so the type of `UsesSelf(i32).Make` is `fn () -> UsesSelf(i32)*`. let x: UsesSelf(i32)* = UsesSelf(i32).Make(); // ✅ `Self = UsesSelf(i32)` is substituted into the type // of `SelfIface.Get`, so the type of `UsesSelf(i32).(SelfIface.Get)` // is `fn [self: UsesSelf(i32)]() -> UsesSelf(i32)`. let y: UsesSelf(i32) = x->Get(); ``` If a facet type `C` into which lookup is performed includes a `where` clause saying `.N = U`, and the result of qualified name lookup is the associated constant `N`, that result is replaced by `U`, and the type of the result is the type of `U`. No substitution is performed in this step, not even a `Self` substitution -- any necessary substitutions were already performed when forming the facet type `C`, and we don’t consider either the declared type or value of the associated constant at all for this kind of constraint. Going through an example in detail: ```carbon interface A { let T:! type; } interface B { let U:! type; // More explicitly, this is of type `A where .(A.T) = Self.(B.U)` let V:! A where .T = U; } // Type of W is B. fn F[W:! B](x: W) { // The type of the expression `W` is `B`. // `W.V` finds `B.V` with type `A where .(A.T) = Self.(B.U)`. // We substitute `Self` = `W` giving the type of `u` as // `A where .(A.T) = W.(B.U)`. let u:! auto = W.V; // The type of `u` is `A where .(A.T) = W.(B.U)`. // Lookup for `u.T` resolves it to `u.(A.T)`. // So the result of the qualified member access is `W.(B.U)`, // and the type of `v` is the type of `W.(B.U)`, namely `type`. // No substitution is performed in this step. let v:! auto = u.T; } ``` The more complex case of ```carbon fn F2[Z:! B where .U = i32](x: Z); ``` is discussed later. ### Type substitution At various points during the type-checking of a Carbon program, we need to substitute a set of (binding, value) pairs into a symbolic constant. We saw an example above: substituting `Self = W` into the type `A where .(A.T) = Self.U` to produce the value `A where .(A.T) = W.U`. Another important case is the substitution of inferred parameter values into the type of a function when type-checking a function call: ```carbon fn F[T:! C](x: T) -> T; fn G(n: i32) -> i32 { // Deduces T = i32, which is substituted // into the type `fn (x: T) -> T` to produce // `fn (x: i32) -> i32`, giving `i32` as the type // of the call expression. return F(n); } ``` Qualified name lookup is not re-done as a result of type substitution. For a template, we imagine there’s a completely separate step that happens before type substitution, where qualified name lookup is redone based on the actual value of template arguments; this proceeds as described in the previous section. Otherwise, we performed the qualified name lookup when type-checking symbolic expressions, and do not do it again: ```carbon interface IfaceHasX { let X:! type; } class ClassHasX { class X {} } interface HasAssoc { let Assoc:! IfaceHasX; } // Qualified name lookup finds `T.(HasAssoc.Assoc).(IfaceHasX.X)`. fn F(T:! HasAssoc) -> T.Assoc.X; fn G(T:! HasAssoc where .Assoc = ClassHasX) { // `T.Assoc` rewritten to `ClassHasX` by qualified name lookup. // Names `ClassHasX.X`. var a: T.Assoc.X = {}; // Substitution produces `ClassHasX.(IfaceHasX.X)`, // not `ClassHasX.X`. var b: auto = F(T); } ``` During substitution, we might find a member access that named an opaque symbolic associated constant in the original value can now be resolved to some specific value. It’s important that we perform this resolution: ```carbon interface A { let T:! type; } class K { fn Member(); } fn H[U:! A](x: U) -> U.T; fn J[V:! A where .T = K](y: V) { // We need the interface of `H(y)` to include // `K.Member` in order for this lookup to succeed. H(y).Member(); } ``` The values being substituted into the symbolic constant are themselves already fully substituted and resolved, and in particular, satisfy property 1 given above. Substitution proceeds by recursively rebuilding each symbolic constant, bottom-up, replacing each substituted binding with its value. Doing this naively will propagate values like `i32` in the `F`/`G` case earlier in this section, but will not propagate rewrite constants like in the `H`/`J` case. The reason is that the `.T = K` constraint is in the _type_ of the substituted value, rather than in the substituted value itself: deducing `T = i32` and converting `i32` to the type `C` of `T` preserves the value `i32`, but deducing `U = V` and converting `V` to the type `A` of `U` discards the rewrite constraint. In order to apply rewrites during substitution, we associate a set of rewrites with each value produced by the recursive rebuilding procedure. This is somewhat like having substitution track a refined facet type for the type of each value, but we don’t need -- or want -- for the type to change during this process, only for the rewrites to be properly applied. For a substituted binding, this set of rewrites is the rewrites found on the type of the corresponding value prior to conversion to the type of the binding. When rebuilding a member access expression, if we have a rewrite corresponding to the accessed member, then the resulting value is the target of the rewrite, and its set of rewrites is that found in the type of the target of the rewrite, if any. Because the target of the rewrite is fully resolved already, we can ask for its type without triggering additional work. In other cases, the rewrite set is empty; all necessary rewrites were performed when type-checking the value we're substituting into. Continuing an example from [qualified name lookup](#qualified-name-lookup): ```carbon interface A { let T:! type; } interface B { let U:! type; let V:! A where .T = U; } // Type of the expression `Z` is `B where .(B.U) = i32` fn F2[Z:! B where .U = i32](x: Z) { // The type of the expression `Z` is `B where .U = i32`. // `Z.V` is looked up and finds the associated facet `(B.V)`. // The declared type is `A where .(A.T) = Self.U`. // We substitute `Self = Z` with rewrite `.U = i32`. // The resulting type is `A where .(A.T) = i32`. // So `u` is `Z.V` with type `A where .(A.T) = i32`. let u:! auto = Z.V; // The type of `u` is `A where .(A.T) = i32`. // Lookup for `u.T` resolves it to `u.(A.T)`. // So the result of the qualified member access is `i32`, // and the type of `v` is the type of `i32`, namely `type`. // No substitution is performed in this step. let v:! auto = u.T; } ``` ### Examples ```carbon interface Container { let Element:! type; } interface SliceableContainer { extend Container; let Slice:! Container where .Element = Self.(Container.Element); } // ❌ Qualified name lookup rewrites this facet type to // `SliceableContainer where .(Container.Element) = .Self.(Container.Element)`. // Constraint resolution rejects this because this rewrite forms a cycle. fn Bad[T:! SliceableContainer where .Element = .Slice.Element](x: T.Element) {} ``` ```carbon interface Helper { let D:! type; } interface Example { let B:! type; let C:! Helper where .D = B; } // ✅ `where .D = ...` by itself is fine. // `T.C.D` is rewritten to `T.B`. fn Allowed(T:! Example, x: T.C.D); // ❌ But combined with another rewrite, creates an infinite loop. // `.C.D` is rewritten to `.B`, resulting in `where .B = .B`, // which causes an error during constraint resolution. // Using `==` instead of `=` would make this constraint redundant, // rather than it being an error. fn Error(T:! Example where .B = .C.D, x: T.C.D); ``` ```carbon interface Allowed; interface AllowedBase { let A:! Allowed; } interface Allowed { extend AllowedBase where .A = .Self; } // ✅ The final type of `x` is `T`. It is computed as follows: // In `((T.A).A).A`, the inner `T.A` is rewritten to `T`, // resulting in `((T).A).A`, which is then rewritten to // `(T).A`, which is then rewritten to `T`. fn F(T:! Allowed, x: ((T.A).A).A); ``` ```carbon interface MoveYsRight; constraint ForwardDeclaredConstraint(X:! MoveYsRight); interface MoveYsRight { let X:! MoveYsRight; // Means `Y:! MoveYsRight where .X = X.Y` let Y:! ForwardDeclaredConstraint(X); } constraint ForwardDeclaredConstraint(X:! MoveYsRight) { extend MoveYsRight where .X = X.Y; } // ✅ The final type of `x` is `T.X.Y.Y`. It is computed as follows: // The type of `T` is `MoveYsRight`. // The type of `T.Y` is determined as follows: // - Qualified name lookup finds `MoveYsRight.Y`. // - The declared type is `ForwardDeclaredConstraint(Self.X)`. // - That is a named constraint, for which we perform substitution. // Substituting `X = Self.X` gives the type // `MoveYsRight where .X = Self.X.Y`. // - Substituting `Self = T` gives the type // `MoveYsRight where .X = T.X.Y`. // The type of `T.Y.Y` is determined as follows: // - Qualified name lookup finds `MoveYsRight.Y`. // - The declared type is `ForwardDeclaredConstraint(Self.X)`. // - That is a named constraint, for which we perform substitution. // Substituting `X = Self.X` gives the type // `MoveYsRight where .X = Self.X.Y`. // - Substituting `Self = T.Y` with // rewrite `.X = T.X.Y` gives the type // `MoveYsRight where .X = T.Y.X.Y`, but // `T.Y.X` is replaced by `T.X.Y`, giving // `MoveYsRight where .X = T.X.Y.Y`. // The type of `T.Y.Y.X` is determined as follows: // - Qualified name lookup finds `MoveYsRight.X`. // - The type of `T.Y.Y` says to rewrite that to `T.X.Y.Y`. // - The result is `T.X.Y.Y`, of type `MoveYsRight`. fn F4(T:! MoveYsRight, x: T.Y.Y.X); ``` ### Termination Each of the above steps performs at most one rewrite, and doesn't introduce any new recursive type-checking steps, so should not introduce any new forms of non-termination. Rewrite constraints thereby give us a deterministic, terminating type canonicalization mechanism for associated constants: in `A.B`, if the type of `A` specifies that `.B = C`, then `A.B` is replaced by `C`. Equality of types constrained in this way is transitive. However, some existing forms of non-termination may remain, such as template instantiation triggering another template instantiation. Such cases will need to be detected and handled in some way, such as by a depth limit, but doing so doesn't compromise the soundness of the type system. ================================================ FILE: docs/design/generics/appendix-witness.md ================================================ # Generics appendix: Witness tables ## Table of contents - [Overview](#overview) - [Terminology](#terminology) - [Witness tables](#witness-tables) - [Dynamic-dispatch witness table](#dynamic-dispatch-witness-table) - [Static-dispatch witness table](#static-dispatch-witness-table) - [Limitations of witness tables](#limitations-of-witness-tables) - [Associated constants](#associated-constants) - [Blanket implementations](#blanket-implementations) - [Specialization](#specialization) - [Calling templated functions](#calling-templated-functions) - [Implementing some Carbon generic features with witness tables](#implementing-some-carbon-generic-features-with-witness-tables) - [Overview](#overview-1) - [Example](#example) - [Associated facets example](#associated-facets-example) ## Overview Witness tables are a strategy for implementing generics, specifically for allowing the behavior of a generic function to vary with the values of generic parameters. They have some nice properties: - They can be used both for runtime and compile-time dispatch. - They can support separate compilation even with compile-time dispatch. However, it can be a challenge to implement some features of a generic system with witness tables. This leads to limitations on the generic system, additional runtime overhead, or both. Swift uses witness tables for both static and dynamic dispatch, accepting both limitations and overhead. Carbon and Rust only use witness tables for dynamic dispatch, and apply limitations to control the runtime overhead when using that feature. As an implementation detail, Carbon compilers might also use witness tables for static dispatch, for example when the code conforms to the limitations of what witness tables support. However, part of the point of this document is to state the limitations and obstacles of doing that. ## Terminology ### Witness tables [Witness tables](https://forums.swift.org/t/where-does-the-term-witness-table-come-from/54334/4) are an implementation strategy where values passed to a compile-time type binding are compiled into a table of required functionality. That table is then filled in for a given passed-in type with references to the implementation on the original type. The generic is implemented using calls into entries in the witness table, which turn into calls to the original type. This doesn't necessarily imply a runtime indirection: it may be a purely compile-time separation of concerns. However, it insists on a full abstraction boundary between the generic user of a type and the concrete implementation. A simple way to imagine a witness table is as a struct of function pointers, one per method in the interface. However, in practice, it's more complex because it must model things like associated facets and interfaces. Witness tables are called "dictionary passing" in Haskell. Outside of generics, a [vtable](https://en.wikipedia.org/wiki/Virtual_method_table) is very similar to a witness table, "witnessing" the specific descendant of a base class. Vtables, however, are passed as part of the object instead of separately. ### Dynamic-dispatch witness table For dynamic-dispatch witness tables, actual function pointers are formed and used as a dynamic, runtime indirection. As a result, the generic code **will not** be duplicated for different witness tables. ### Static-dispatch witness table For static-dispatch witness tables, the implementation is required to collapse the table indirections at compile time. As a result, the generic code **will** be duplicated for different witness tables. Static-dispatch may be implemented as a performance optimization for dynamic-dispatch that increases generated code size. The final compiled output may not retain the witness table. ## Limitations of witness tables ### Associated constants An interface with associated constants can use that to allow the signature of a function to vary. A similar issue arises with argument and return values involving `Self`. This adds to the cost of calling such functions, for example if they are not passed by pointer, then the generated code must support arguments and return values with a size only known at runtime. For this reason, Rust's dynamic trait dispatch system, trait objects, only works with traits that are ["dyn-compatible,"](https://doc.rust-lang.org/reference/items/traits.html#dyn-compatibility) which includes a requirement that [all the associated types have specified values](https://github.com/rust-lang/rfcs/blob/master/text/0195-associated-items.md#trait-objects). This reduces the expressivity of Rust traits to the subset that could be supported by a C++ abstract base class. Swift instead supports types with size only known at runtime for its [ABI stability and dynamic linking features](https://faultlore.com/blah/swift-abi/#what-is-abi-stability-and-dynamic-linking), and can use that to [support more generic features with dynamic dispatch](https://faultlore.com/blah/swift-abi/#polymorphic-generics). This comes with runtime overhead. ### Blanket implementations [Blanket implementations](details.md#blanket-impl-declarations) allow you define an implementation of interface `Y` for any type implementing interface `X`. This allows a function to use the functionality of `Y` while only having a requirement that `X` be implemented. This creates the problem of how to go from a witness table for `X` to a witness table for `Y`. Rust supports blanket implementations using monomorphization, but this only works with static dispatch. Swift does not support blanket implementations. This is possibly a result of the limitations of using witness tables to implement generics. ### Specialization Specialization compounds the difficulty of the previous two issues. An interface with an associated facet might be implemented using witness tables by including a reference to the associated facet's witness table in the witness table for the interface. This doesn't, though, give you a witness table for parameterized types using the associated facet as an argument. Synthesizing those witness tables is particularly tricky if the implementation is different for specific types due to specialization. Similarly, a blanket implementation can guarantee that some implementation of an interface exists. Specialization means that actual implementation of that interface for specific types is not the one given by the blanket implementation. Furthermore, that specialized implementation may be in an unrelated library. They may be found anywhere in the program, not necessarily in the dependencies of the code that needs to use a particular witness table. As a result, specialization is not supported by Swift, which uses witness tables. Specialization is being considered for Rust, and is compatible with its monomorphization model used for static dispatch. ### Calling templated functions Carbon's planned approach to support calling a templated function from a checked-generic function, decided in [issue #2153](https://github.com/carbon-language/carbon-lang/issues/2153), relies on monomorphization. Trying to rely on witness tables would result in different semantics for calling the same function with the same types, depending on which witness tables were available at the callsite. ## Implementing some Carbon generic features with witness tables ### Overview A possible model for generating code for a generic function is to use a [witness table](#witness-tables) to represent how a type implements an interface: - [Interfaces](details.md#interfaces) are types of witness tables. - An [impl](details.md#implementing-interfaces) is a witness table value. We can think of the interface as defining a struct type with a field for every interface member. An implementation of that interface for a type is a value of that struct type, which we call a witness or witness table. For example, the function and method members of an interface correspond to function pointer fields. An implementation will have function pointer values pointing to the functions defining the implementation of that interface for a given type. This is like a [vtable](https://en.wikipedia.org/wiki/Virtual_method_table), except stored separately from the object. A witness might [have references to other witness tables](#associated-facets-example), in order to support these interface features and members: - [associated facets](details.md#associated-facets) - [type parameters](details.md#parameterized-interfaces) - [interface requirements](details.md#interface-requiring-other-interfaces) It also could contain constants, to store the values of [associated constants](details.md#associated-constants), or the type's size. ### Example For example, this `Vector` interface: ```carbon interface Vector { fn Add[self: Self](b: Self) -> Self; fn Scale[self: Self](v: f64) -> Self; } ``` from [the generic details design](details.md#interfaces) could be thought of defining a witness table type like: ``` class Vector { // `Self` is the representation type, which is only // known at compile time. var Self:! type; // `fnty` is placeholder syntax for a "function type", // so `Add` is a function that takes two `Self` parameters // and returns a value of type `Self`. var Add: fnty(a: Self, b: Self) -> Self; var Scale: fnty(a: Self, v: f64) -> Self; } ``` The [`impl` definition of `Vector` for `Point_Inline`](details.md#inline-impl) would be a value of this type: ``` var VectorForPoint_Inline: Vector = { .Self = Point_Inline, // `lambda` is placeholder syntax for defining a // function value. .Add = lambda(a: Point_Inline, b: Point_Inline) -> Point_Inline { return {.x = a.x + b.x, .y = a.y + b.y}; }, .Scale = lambda(a: Point_Inline, v: f64) -> Point_Inline { return {.x = a.x * v, .y = a.y * v}; }, }; ``` Since generic arguments (where the parameter is declared using `:!`) are passed at compile time, the actual value of `VectorForPoint_Inline` can be used to generate the code for functions using that impl. ### Associated facets example The associated facet can be modeled by a witness table field in the interface's witness table. ``` interface Iterator { fn Advance[ref self: Self](); } interface Container { let IteratorType:! Iterator; fn Begin[ref self: Self]() -> IteratorType; } ``` could be represented by: ``` class Iterator { var Self:! type; var Advance: fnty(this: Self*); ... } class Container { var Self:! type; // Witness that IteratorType implements Iterator. var IteratorType:! Iterator*; // Method var Begin: fnty (this: Self*) -> IteratorType->Self; ... } ``` ================================================ FILE: docs/design/generics/details.md ================================================ # Generics: Details ## Table of contents - [Overview](#overview) - [Interfaces](#interfaces) - [Implementing interfaces](#implementing-interfaces) - [Inline `impl`](#inline-impl) - [`extend impl`](#extend-impl) - [Out-of-line `impl`](#out-of-line-impl) - [Defining an `impl` in another library than the type](#defining-an-impl-in-another-library-than-the-type) - [Forward `impl` declaration](#forward-impl-declaration) - [Implementing multiple interfaces](#implementing-multiple-interfaces) - [Avoiding name collisions](#avoiding-name-collisions) - [Qualified member names and compound member access](#qualified-member-names-and-compound-member-access) - [Access](#access) - [Checked-generic functions](#checked-generic-functions) - [Symbolic facet bindings](#symbolic-facet-bindings) - [Return type](#return-type) - [Interfaces recap](#interfaces-recap) - [Facet types](#facet-types) - [Named constraints](#named-constraints) - [Subtyping between facet types](#subtyping-between-facet-types) - [Combining interfaces by anding facet types](#combining-interfaces-by-anding-facet-types) - [Interface requiring other interfaces](#interface-requiring-other-interfaces) - [Interface extension](#interface-extension) - [`extend require` with named constraints](#extend-require-with-named-constraints) - [Diamond dependency issue](#diamond-dependency-issue) - [Use case: detecting unreachable matches](#use-case-detecting-unreachable-matches) - [Adapting types](#adapting-types) - [Adapter compatibility](#adapter-compatibility) - [Extending adapter](#extending-adapter) - [Use case: Using independent libraries together](#use-case-using-independent-libraries-together) - [Use case: Defining an impl for use by other types](#use-case-defining-an-impl-for-use-by-other-types) - [Use case: Private impl](#use-case-private-impl) - [Use case: Accessing interface names](#use-case-accessing-interface-names) - [Future work: Adapter with stricter invariants](#future-work-adapter-with-stricter-invariants) - [Associated constants](#associated-constants) - [Associated class functions](#associated-class-functions) - [Associated facets](#associated-facets) - [Parameterized interfaces](#parameterized-interfaces) - [Parameterized named constraints](#parameterized-named-constraints) - [Where constraints](#where-constraints) - [Kinds of `where` constraints](#kinds-of-where-constraints) - [Recursive constraints](#recursive-constraints) - [Rewrite constraints](#rewrite-constraints) - [Same-type constraints](#same-type-constraints) - [Implementation of same-type `ImplicitAs`](#implementation-of-same-type-implicitas) - [Manual type equality](#manual-type-equality) - [Observe declarations](#observe-declarations) - [Implements constraints](#implements-constraints) - [Implied constraints](#implied-constraints) - [Combining constraints](#combining-constraints) - [Satisfying both facet types](#satisfying-both-facet-types) - [Constraints must use a designator](#constraints-must-use-a-designator) - [Referencing names in the interface being defined](#referencing-names-in-the-interface-being-defined) - [Constraint examples and use cases](#constraint-examples-and-use-cases) - [Parameterized type implements interface](#parameterized-type-implements-interface) - [Another type implements parameterized interface](#another-type-implements-parameterized-interface) - [Must be legal type argument constraints](#must-be-legal-type-argument-constraints) - [Named constraint constants](#named-constraint-constants) - [Other constraints as facet types](#other-constraints-as-facet-types) - [Is a derived class](#is-a-derived-class) - [Type compatible with another type](#type-compatible-with-another-type) - [Same implementation restriction](#same-implementation-restriction) - [Example: Multiple implementations of the same interface](#example-multiple-implementations-of-the-same-interface) - [Example: Creating an impl out of other implementations](#example-creating-an-impl-out-of-other-implementations) - [Sized types and facet types](#sized-types-and-facet-types) - [Destructor constraints](#destructor-constraints) - [Compile-time `let`](#compile-time-let) - [Parameterized impl declarations](#parameterized-impl-declarations) - [Impl for a parameterized type](#impl-for-a-parameterized-type) - [Conditional conformance](#conditional-conformance) - [Blanket impl declarations](#blanket-impl-declarations) - [Difference between a blanket impl and a named constraint](#difference-between-a-blanket-impl-and-a-named-constraint) - [Wildcard impl declarations](#wildcard-impl-declarations) - [Combinations](#combinations) - [Lookup resolution and specialization](#lookup-resolution-and-specialization) - [Type structure of an impl declaration](#type-structure-of-an-impl-declaration) - [Orphan rule](#orphan-rule) - [Overlap rule](#overlap-rule) - [Prioritization rule](#prioritization-rule) - [Acyclic rule](#acyclic-rule) - [Termination rule](#termination-rule) - [Non-facet arguments](#non-facet-arguments) - [`final` impl declarations](#final-impl-declarations) - [Libraries that can contain a `final` impl](#libraries-that-can-contain-a-final-impl) - [Comparison to Rust](#comparison-to-rust) - [Forward declarations and cyclic references](#forward-declarations-and-cyclic-references) - [Declaring interfaces and named constraints](#declaring-interfaces-and-named-constraints) - [Declaring implementations](#declaring-implementations) - [Matching and agreeing](#matching-and-agreeing) - [Declaration examples](#declaration-examples) - [Example of declaring interfaces with cyclic references](#example-of-declaring-interfaces-with-cyclic-references) - [Interfaces with parameters constrained by the same interface](#interfaces-with-parameters-constrained-by-the-same-interface) - [Interface members with definitions](#interface-members-with-definitions) - [Interface defaults](#interface-defaults) - [`final` members](#final-members) - [Interface requiring other interfaces revisited](#interface-requiring-other-interfaces-revisited) - [Requirements with `where` constraints](#requirements-with-where-constraints) - [Observing a type implements an interface](#observing-a-type-implements-an-interface) - [Observing interface requirements](#observing-interface-requirements) - [Observing blanket impl declarations](#observing-blanket-impl-declarations) - [Observing equal to a type implementing an interface](#observing-equal-to-a-type-implementing-an-interface) - [Operator overloading](#operator-overloading) - [Binary operators](#binary-operators) - [`like` operator for implicit conversions](#like-operator-for-implicit-conversions) - [Parameterized types](#parameterized-types) - [Generic methods](#generic-methods) - [Conditional methods](#conditional-methods) - [Specialization](#specialization) - [Future work](#future-work) - [Dynamic types](#dynamic-types) - [Runtime type parameters](#runtime-type-parameters) - [Runtime type fields](#runtime-type-fields) - [Abstract return types](#abstract-return-types) - [Evolution](#evolution) - [Testing](#testing) - [Impl with state](#impl-with-state) - [Generic associated facets and higher-ranked facets](#generic-associated-facets-and-higher-ranked-facets) - [Generic associated facets](#generic-associated-facets) - [Higher-ranked types](#higher-ranked-types) - [Field requirements](#field-requirements) - [Bridge for C++ customization points](#bridge-for-c-customization-points) - [Variadic arguments](#variadic-arguments) - [Value constraints for template parameters](#value-constraints-for-template-parameters) - [References](#references) ## Overview This document goes into the details of the design of Carbon's [generics](terminology.md#generic-means-compile-time-parameterized), by which we mean generalizing some language construct with compile-time parameters. These parameters can be types, [facets](terminology.md#facet), or other values. Imagine we want to write a function with a type (or facet) parameter. Maybe our function is `PrintToStdout` and let's say we want to operate on values that have a type for which we have an implementation of the `ConvertibleToString` interface. The `ConvertibleToString` interface has a `ToString` method returning a string. To do this, we give the `PrintToStdout` function two parameters: one is the value to print, let's call that `val`, the other is the type of that value, let's call that `T`. The type of `val` is `T`, what is the type of `T`? Well, since we want to let `T` be any type implementing the `ConvertibleToString` interface, we express that in the "interfaces are facet types" model by saying the type of `T` is `ConvertibleToString`. Since we can figure out `T` from the type of `val`, we don't need the caller to pass in `T` explicitly, so it can be a [deduced parameter](terminology.md#deduced-parameter) (also see [deduced parameters](overview.md#deduced-parameters) in the Generics overview doc). Basically, the user passes in a value for `val`, and the type of `val` determines `T`. `T` still gets passed into the function though, and it plays an important role -- it defines the key used to look up interface implementations. That interface implementation has the definitions of the functions declared in the interface. For example, the types `i32` and `String` would have different implementations of the `ToString` method of the `ConvertibleToString` interface. In addition to function members, interfaces can include other members that associate a [compile-time value](/docs/design/README.md#expression-phases) for any implementing type, called _associated constants_. For example, this can allow a container interface to include the type of iterators that are returned from and passed to various container methods. The function expresses that the type argument is passed in statically, basically generating a separate function body for every different type passed in, by using the "compile-time parameter" syntax `:!`. By default, this defines a [checked-generics parameter](#checked-generic-functions) below. In this case, the interface contains enough information to [type and definition check](terminology.md#complete-definition-checking) the function body -- you can only call functions defined in the interface in the function body. Alternatively, the `template` keyword can be included in the signature to make the type a template parameter. In this case, you could just use `type` instead of an interface and it will work as long as the function is only called with types that allow the definition of the function to compile. The interface bound has other benefits: - allows the compiler to deliver clearer error messages, - documents expectations, and - expresses that a type has certain semantics beyond what is captured in its member function names and signatures. The last piece of the puzzle is calling the function. For a value of type `Song` to be printed using the `PrintToStdout` function, `Song` needs to implement the `ConvertibleToString` interface. Interface implementations will usually be defined either with the type or with the interface. They may also be defined somewhere else as long as Carbon can be guaranteed to see the definition when needed. For more on this, see [the implementing interfaces section](#implementing-interfaces) below. When the implementation of `ConvertibleToString` for `Song` is declared with `extend`, every member of `ConvertibleToString` is also a member of `Song`. This includes members of `ConvertibleToString` that are not explicitly named in the `impl` definition but have defaults. Whether the type [extends the implementation](terminology.md#extending-an-impl) or not, you may access the `ToString` function for a `Song` value `s` by a writing function call [using a qualified member access expression](terminology.md#qualified-member-access-expression), like `s.(ConvertibleToString.ToString)()`. If `Song` doesn't implement an interface or we would like to use a different implementation of that interface, we can define another type that also has the same data representation as `Song` that has whatever different interface implementations we want. However, Carbon won't implicitly convert to that other type, the user will have to explicitly cast to that type in order to select those alternate implementations. For more on this, see [the adapting type section](#adapting-types) below. We originally considered following Swift and using a witness table implementation strategy for checked generics, but ultimately decided to only use that for the dynamic-dispatch case. This is because of the limitations of that strategy prevent some features that we considered important, as described in [the witness-table appendix](appendix-witness.md). ## Interfaces An [interface](terminology.md#interface), defines an API that a given type can implement. For example, an interface capturing a linear-algebra vector API might have two methods: ```carbon interface Vector { // Here the `Self` keyword means // "the type implementing this interface". fn Add[self: Self](b: Self) -> Self; fn Scale[self: Self](v: f64) -> Self; } ``` The syntax here is to match [how the same members would be defined in a type](/docs/design/classes.md#methods). Each declaration in the interface defines an [associated entity](terminology.md#associated-entity). In this example, `Vector` has two associated methods, `Add` and `Scale`. A type [implements an interface](#implementing-interfaces) by providing definitions for all the associated entities declared in the interface, An interface defines a [facet type](terminology.md#facet-type), that is a type whose values are [facets](terminology.md#facet). Every type implementing the interface has a corresponding facet value. So if the type `Point` implements interface `Vector`, the facet value `Point as Vector` has type `Vector`. ## Implementing interfaces Carbon interfaces are ["nominal"](terminology.md#nominal-interfaces), which means that types explicitly describe how they implement interfaces. An ["impl"](terminology.md#impl-implementation-of-an-interface) defines how one interface is implemented for a type, called the _implementing type_. Every associated entity is given a definition. Different types satisfying `Vector` can have different definitions for `Add` and `Scale`, so we say their definitions are _associated_ with what type is implementing `Vector`. The `impl` defines what is associated with the implementing type for that interface. ### Inline `impl` An impl may be defined inline inside the type definition: ```carbon class Point_Inline { var x: f64; var y: f64; impl as Vector { // In this scope, the `Self` keyword is an // alias for `Point_Inline`. fn Add[self: Self](b: Self) -> Self { return {.x = self.x + b.x, .y = self.y + b.y}; } fn Scale[self: Self](v: f64) -> Self { return {.x = self.x * v, .y = self.y * v}; } } } ``` ### `extend impl` Interfaces that are implemented inline with the `extend` keyword contribute to the type's API: ```carbon class Point_Extend { var x: f64; var y: f64; extend impl as Vector { fn Add[self: Self](b: Self) -> Self { return {.x = self.x + b.x, .y = self.y + b.y}; } fn Scale[self: Self](v: f64) -> Self { return {.x = self.x * v, .y = self.y * v}; } } } var p1: Point_Extend = {.x = 1.0, .y = 2.0}; var p2: Point_Extend = {.x = 2.0, .y = 4.0}; Assert(p1.Scale(2.0) == p2); Assert(p1.Add(p1) == p2); ``` Without `extend`, those methods may only be accessed with [qualified member names and compound member access](#qualified-member-names-and-compound-member-access): ```carbon // Point_Inline did not use `extend` when // implementing `Vector`: var a: Point_Inline = {.x = 1.0, .y = 2.0}; // `a` does *not* have `Add` and `Scale` methods: // ❌ Error: a.Add(a.Scale(2.0)); ``` This is consistent with the general Carbon rule that if the names of another entity affect a class' API, then that is mentioned with an `extend` declaration in the `class` definition. **Comparison with other languages:** Rust only defines implementations lexically outside of the `class` definition. Carbon's approach results in the property that every type's API is described by declarations inside its `class` definition and doesn't change afterwards. **References:** Carbon's interface implementation syntax was first defined in [proposal #553](https://github.com/carbon-language/carbon-lang/pull/553). In particular, see [the alternatives considered](/proposals/p0553.md#interface-implementation-syntax). This syntax was changed to use `extend` in [proposal #2760: Consistent `class` and `interface` syntax](https://github.com/carbon-language/carbon-lang/pull/2760). ### Out-of-line `impl` An impl may also be defined after the type definition, by naming the type between `impl` and `as`: ```carbon class Point_OutOfLine { var x: f64; var y: f64; } impl Point_OutOfLine as Vector { // In this scope, the `Self` keyword is an // alias for `Point_OutOfLine`. fn Add[self: Self](b: Self) -> Self { return {.x = self.x + b.x, .y = self.y + b.y}; } fn Scale[self: Self](v: f64) -> Self { return {.x = self.x * v, .y = self.y * v}; } } ``` Since `extend impl` may only be used inside the class definition, out-of-line definitions do not contribute to the class's API unless there is a corresponding [forward declaration in the class definition using `extend`](#forward-impl-declaration). Conversely, being declared or defined lexically inside the class means that implementation is available to other members defined in the class. For example, it would allow implementing another interface or method that requires this interface to be implemented. **Open question:** Do implementations need to be defined lexically inside the class to get access to private members, or is it sufficient to be defined in the same library as the class? **Comparison with other languages:** Both Rust and Swift support out-of-line implementation. [Swift's syntax](https://docs.swift.org/swift-book/LanguageGuide/Protocols.html#ID277) does this as an "extension" of the original type. In Rust, all implementations are out-of-line as in [this example](https://doc.rust-lang.org/rust-by-example/trait.html). Unlike Swift and Rust, we don't allow a type's API to be modified outside its definition. So in Carbon a type's API is consistent no matter what is imported, unlike Swift and Rust. #### Defining an `impl` in another library than the type An out-of-line `impl` declaration is allowed to be defined in a different library from `Point_OutOfLine`, restricted by [the coherence/orphan rules](#orphan-rule) that ensure that the implementation of an interface can't change based on imports. In particular, the `impl` declaration is allowed in the library defining the interface (`Vector` in this case) in addition to the library that defines the type (`Point_OutOfLine` here). This (at least partially) addresses [the expression problem](https://eli.thegreenplace.net/2016/the-expression-problem-and-its-solutions). You can't use `extend` outside the class definition, so an `impl` declaration in a different library will never affect the class's API. This means that the API of a class such as `Point_OutOfLine` doesn't change based on what is imported. It would be particularly bad if two different libraries implemented interfaces with conflicting names that both affected the API of a single type. As a consequence of this restriction, you can find all the names of direct members (those available by [simple member access](terminology.md#simple-member-access)) of a type in the definition of that type and entities referenced in by an `extend` declaration in that definition. The only thing that may be in another library is an `impl` of an interface. **Rejected alternative:** We could allow types to have different APIs in different files based on explicit configuration in that file. For example, we could support a declaration that a given interface or a given method of an interface is "in scope" for a particular type in this file. With that declaration, the method could be called using [simple member access](terminology.md#simple-member-access). This avoids most concerns arising from name collisions between interfaces. It has a few downsides though: - It increases variability between files, since the same type will have different APIs depending on these declarations. This makes it harder to copy-paste code between files. - It makes reading code harder, since you have to search the file for these declarations that affect name lookup. ### Forward `impl` declaration An `impl` declaration may be forward declared and then defined later. If this is done using [`extend` to add to the type's API](#extend-impl), only the declaration in the class definition will use the `extend` keyword, as in this example: ```carbon class Point_ExtendForward { var x: f64; var y: f64; // Forward declaration in class definition using `extend`. // Signals that you should look in the definition of // `Vector` since those methods are included in this type. extend impl as Vector; } // Definition outside class definition does not. impl Point_ExtendForward as Vector { fn Add[self: Self](b: Self) -> Self { return {.x = self.x + b.x, .y = self.y + b.y}; } fn Scale[self: Self](v: f64) -> Self { return {.x = self.x * v, .y = self.y * v}; } } ``` > **TODO:** The second `impl` in this example is no longer a valid redeclaration > of the first after > [p5366: The name of an `impl` in `class` scope](/proposals/p5366.md). More about forward declaring implementations in [its dedicated section](#declaring-implementations). ### Implementing multiple interfaces To implement more than one interface when defining a type, simply include an `impl` block or forward declaration per interface. ```carbon class Point_2Extend { var x: f64; var y: f64; extend impl as Vector { fn Add[self: Self](b: Self) -> Self { ... } fn Scale[self: Self](v: f64) -> Self { ... } } extend impl as Drawable { fn Draw[self: Self]() { ... } } } ``` Since both were declared using `extend`, all the functions `Add`, `Scale`, and `Draw` end up a part of the API for `Point_2Extend`. **Note:** A type may implement any number of different interfaces, but may provide at most one implementation of any single interface. This makes the act of selecting an implementation of an interface for a type unambiguous throughout the whole program. **Open question:** Should we have some syntax for the case where you want both names to be given the same implementation? It seems like that might be a common case, but we won't really know if this is an important case until we get more experience. ```carbon class Player { var name: String; extend impl as Icon { fn Name[self: Self]() -> String { return self.name; } // ... } extend impl as GameUnit { // Possible syntax options for defining // `GameUnit.Name` as the same as `Icon.Name`: alias Name = Icon.Name; fn Name[self: Self]() -> String = Icon.Name; // ... } } ``` ### Avoiding name collisions To avoid name collisions, you can't extend implementations of two interfaces that have a name in common: ```carbon class GameBoard { extend impl as Drawable { fn Draw[self: Self]() { ... } } extend impl as EndOfGame { // ❌ Error: `GameBoard` has two methods named `Draw`. fn Draw[self: Self]() { ... } fn Winner[self: Self](player: i32) { ... } } } ``` To implement two interfaces that have a name in common, omit `extend` for one or both. You might also omit `extend` when implementing an interface for a type to avoid cluttering the API of that type or to avoid a name collision with another member of that type. A syntax for reusing method implementations allows us to include names from an implementation selectively: ```carbon class Point_ReuseMethodInImpl { var x: f64; var y: f64; // `Add()` is a method of `Point_ReuseMethodInImpl`. fn Add[self: Self](b: Self) -> Self { return {.x = self.x + b.x, .y = self.y + b.y}; } // No `extend`, so other members of `Vector` are not // part of `Point_ReuseMethodInImpl`'s API. impl as Vector { // Syntax TBD: alias Add = Point_ReuseMethodInImpl.Add; fn Scale[self: Self](v: f64) -> Self { return {.x = self.x * v, .y = self.y * v}; } } } // OR: class Point_IncludeMethodFromImpl { var x: f64; var y: f64; // No `extend`, so members of `Vector` are not // part of `Point_IncludeMethodFromImpl`'s API. impl as Vector { fn Add[self: Self](b: Self) -> Self { return {.x = self.x + b.x, .y = self.y + b.y}; } fn Scale[self: Self](v: f64) -> Self { return {.x = self.x * v, .y = self.y * v}; } } // Include `Add` explicitly as a member. alias Add = Vector.Add; } // OR: // This is the same as `Point_ReuseMethodInImpl`, // except the `impl` is out-of-line. class Point_ReuseByOutOfLine { var x: f64; var y: f64; fn Add[self: Self](b: Self) -> Self { return {.x = self.x + b.x, .y = self.y + b.y}; } } impl Point_ReuseByOutOfLine as Vector { // Syntax TBD: alias Add = Point_ReuseByOutOfLine.Add; fn Scale[self: Self](v: f64) -> Self { return {.x = self.x * v, .y = self.y * v}; } } ``` ### Qualified member names and compound member access ```carbon class Point_NoExtend { var x: f64; var y: f64; } impl Point_NoExtend as Vector { ... } ``` Given a value of type `Point_NoExtend` and an interface `Vector` implemented for that type, you can access the methods from that interface using a [qualified member access expression](terminology.md#qualified-member-access-expression) whether or not the implementation is done with an [`extend impl` declaration](#extend-impl). The qualified member access expression writes the member's _qualified name_ in the parentheses of the [compound member access syntax](/docs/design/expressions/member_access.md): ```carbon var p1: Point_NoExtend = {.x = 1.0, .y = 2.0}; var p2: Point_NoExtend = {.x = 2.0, .y = 4.0}; Assert(p1.(Vector.Scale)(2.0) == p2); Assert(p1.(Vector.Add)(p1) == p2); ``` Note that the name in the parens is looked up in the containing scope, not in the names of members of `Point_NoExtend`. So if there was another interface `Drawable` with method `Draw` defined in the `Plot` package also implemented for `Point_NoExtend`, as in: ```carbon package Plot; import Points; interface Drawable { fn Draw[self: Self](); } impl Points.Point_NoExtend as Drawable { ... } ``` You could access `Draw` with a qualified name: ```carbon import Plot; import Points; var p: Points.Point_NoExtend = {.x = 1.0, .y = 2.0}; p.(Plot.Drawable.Draw)(); ``` **Comparison with other languages:** This is intended to be analogous to, in C++, adding `ClassName::` in front of a member name to disambiguate, such as [names defined in both a parent and child class](https://stackoverflow.com/questions/357307/how-to-call-a-parent-class-function-from-derived-class-function). ### Access An `impl` must be visible to all code that can see both the type and the interface being implemented: - If either the type or interface is private to a single file, then since the only way to define the `impl` is to use that private name, the `impl` must be defined private to that file as well. - Otherwise, if the type or interface is private but declared in an API file, then the `impl` must be declared in the same file so the existence of that `impl` is visible to all files in that library. - Otherwise, the `impl` must be declared in the public API file of the library, so it is visible in all places that might use it. No access control modifiers are allowed on `impl` declarations, an `impl` is always visible to the intersection of the visibility of all names used in the declaration of the `impl`. ## Checked-generic functions Here is a function that can accept values of any type that has implemented the `Vector` interface: ```carbon fn AddAndScaleGeneric[T:! Vector](a: T, b: T, s: f64) -> T { return a.Add(b).Scale(s); } var v: Point_Extend = AddAndScaleGeneric(a, w, 2.5); ``` Here `T` is a facet whose type is `Vector`. The `:!` syntax means that `T` is a _[compile-time binding](terminology.md#bindings)_. Here specifically it declares a _symbolic binding_ since it did not use the `template` keyword to mark it as a _template binding_. > **References:** The `:!` syntax was accepted in > [proposal #676](https://github.com/carbon-language/carbon-lang/pull/676). Since this symbolic binding pattern is in a function declaration, it marks a _[checked](terminology.md#checked-versus-template-parameters) [generic parameter](terminology.md#generic-means-compile-time-parameterized)_. That means its value must be known to the caller at compile-time, but we will only use the information present in the signature of the function to type check the body of `AddAndScaleGeneric`'s definition. Note that types may also be given compile-time parameters, see the ["parameterized types" section](#parameterized-types). ### Symbolic facet bindings In our example, `T` is a facet which may be used in type position in the rest of the function. Furthermore, since it omits the keyword `template` prefix, this is a symbolic binding. so we need to be able to typecheck the body of the function without knowing the specific value `T` from the caller. This typechecking is done by looking at the constraint on `T`. In the example, the constraint on `T` says that every value of `T` implements the `Vector` interface and so has a `Vector.Add` and a `Vector.Scale` method. Names are looked up in the body of `AddAndScaleGeneric` for values of type `T` in `Vector`. This means that `AddAndScaleGeneric` is interpreted as equivalent to adding a `Vector` [qualification](#qualified-member-names-and-compound-member-access) to replace all simple member accesses of `T`: ```carbon fn AddAndScaleGeneric[T:! Vector](a: T, b: T, s: Double) -> T { return a.(Vector.Add)(b).(Vector.Scale)(s); } ``` With these qualifications, the function can be type-checked for any `T` implementing `Vector`. This type checking is equivalent to type checking the function with `T` set to an [archetype](terminology.md#archetype) of `Vector`. An archetype is a placeholder type considered to satisfy its constraint, which is `Vector` in this case, and no more. It acts as the most general type satisfying the interface. The effect of this is that an archetype of `Vector` acts like a [supertype](https://en.wikipedia.org/wiki/Subtyping) of any `T` implementing `Vector`. For name lookup purposes, an archetype is considered to [extend the implementation of its constraint](terminology.md#extending-an-impl). The only oddity is that the archetype may have different names for members than specific types `T` that don't extend the implementation of interfaces from the constraint. This difference in names can also occur for supertypes in C++, for example members in a derived class can hide members in the base class with the same name, though it is not that common for it to come up in practice. The behavior of calling `AddAndScaleGeneric` with a value of a specific type like `Point_Extend` is to set `T` to `Point_Extend` after all the names have been qualified. ```carbon // AddAndScaleGeneric with T = Point_Extend fn AddAndScaleForPoint_Extend( a: Point_Extend, b: Point_Extend, s: Double) -> Point_Extend { return a.(Vector.Add)(b).(Vector.Scale)(s); } ``` This qualification gives a consistent interpretation to the body of the function even when the type supplied by the caller does not [extend the implementation of the interface](terminology.md#extending-an-impl), like `Point_NoExtend`: ```carbon // AddAndScaleGeneric with T = Point_NoExtend fn AddAndScaleForPoint_NoExtend( a: Point_NoExtend, b: Point_NoExtend, s: Double) -> Point_NoExtend { // ✅ This works even though `a.Add(b).Scale(s)` wouldn't. return a.(Vector.Add)(b).(Vector.Scale)(s); } ``` ### Return type From the caller's perspective, the return type is the result of substituting the caller's values for the generic parameters into the return type expression. So `AddAndScaleGeneric` called with `Point_Extend` values returns a `Point_Extend` and called with `Point_NoExtend` values returns a `Point_NoExtend`. So looking up a member on the resulting value will look in `Point_Extend` or `Point_NoExtend` rather than `Vector`. This is part of realizing [the goal that generic functions can be used in place of regular functions without changing the return type that callers see](goals.md#path-from-regular-functions). In this example, `AddAndScaleGeneric` can be substituted for `AddAndScaleForPoint_Extend` and `AddAndScaleForPoint_NoExtend` without affecting the return types. This may require a conversion of the return value to the type that the caller expects, from the erased type used inside a checked-generic function. A checked-generic caller of a checked-generic function performs the same substitution process to determine the return type, but the result may be a symbolic constant. In this example of calling a checked generic from another checked generic, ```carbon fn DoubleThreeTimes[U:! Vector](a: U) -> U { return AddAndScaleGeneric(a, a, 2.0).Scale(2.0); } ``` the return type of `AddAndScaleGeneric` is found by substituting in the `U` from `DoubleThreeTimes` for the `T` from `AddAndScaleGeneric` in the return type expression of `AddAndScaleGeneric`. `U` is an archetype of `Vector`, and so acts as if it extends `Vector` and therefore has a `Scale` method. If `U` had a more specific type, the return value would have the additional capabilities of `U`. For example, given a parameterized type `GeneralPoint` implementing `Vector`, and a function that takes a `GeneralPoint` and calls `AddAndScaleGeneric` with it: ```carbon class GeneralPoint(C:! Numeric) { impl as Vector { ... } fn Get[self: Self](i: i32) -> C; } fn CallWithGeneralPoint[C:! Numeric](p: GeneralPoint(C)) -> C { // `AddAndScaleGeneric` returns `T` and in these calls `T` is // deduced to be `GeneralPoint(C)`. // ❌ Illegal: AddAndScaleGeneric(p, p, 2.0).Scale(2.0); // `GeneralPoint(C)` implements but does not extend `Vector`, // and so does not have a `Scale` method. // ✅ Allowed: `GeneralPoint(C)` has a `Get` method AddAndScaleGeneric(p, p, 2.0).Get(0); // ✅ Allowed: `GeneralPoint(C)` implements `Vector`, and so has // a `Vector.Scale` method. `Vector.Scale` returns `Self` // which is `GeneralPoint(C)` again, and so has a `Get` // method. return AddAndScaleGeneric(p, p, 2.0).(Vector.Scale)(2.0).Get(0); } ``` The result of the call to `AddAndScaleGeneric` from `CallWithGeneralPoint` has type `GeneralPoint(C)` and so has a `Get` method and a `Vector.Scale` method. But, in contrast to how `DoubleThreeTimes` works, since `Vector` is implemented without `extend` the return value in this case does not directly have a `Scale` method. ## Interfaces recap Interfaces have a name and a definition. The definition of an interface consists of a set of declarations. Each declaration defines a requirement for any `impl` that is in turn a capability that consumers of that `impl` can rely on. Typically those declarations also have names, useful for both saying how the `impl` satisfies the requirement and accessing the capability. Interfaces are ["nominal"](terminology.md#nominal-interfaces), which means their name is significant. So two interfaces with the same body definition but different names are different, just like two classes with the same definition but different names are considered different types. For example, lets say we define another interface, say `LegoFish`, with the same `Add` and `Scale` method signatures. Implementing `Vector` would not imply an implementation of `LegoFish`, because the `impl` definition explicitly refers to the name `Vector`. An interface's name may be used in a few different contexts: - to define [an `impl` for a type](#implementing-interfaces), - as a namespace name in [a qualified name](#qualified-member-names-and-compound-member-access), and - as a [facet type](terminology.md#facet-type) for [a facet binding](#symbolic-facet-bindings). While interfaces are examples of facet types, facet types are a more general concept, for which interfaces are a building block. ## Facet types A [facet type](terminology.md#facet-type) consists of a set of requirements and a set of names. Requirements are typically a set of interfaces that a type must satisfy, though other kinds of requirements are added below. The names are aliases for qualified names in those interfaces. An interface is one particularly simple example of a facet type. For example, `Vector` as a facet type has a set of requirements consisting of the single interface `Vector`. Its set of names consists of `Add` and `Scale` which are aliases for the corresponding qualified names inside `Vector` as a namespace. The requirements determine which types may be implicitly converted to a given facet type. The result of this conversion is a [facet](terminology.md#facet). For example, `Point_Inline` from [the "Inline `impl`" section](#inline-impl) implements `Vector`, so `Point_Inline` may be implicitly converted to `Vector` as considered as a type. The result is `Point_Inline as Vector`, which has the members of `Vector` instead of the members of `Point_Inline`. If the facet `Point_Inline as Vector` is used in a type position, it is implicitly converted back to type `type`, see ["values usable as types" in the design overview](/docs/design/README.md#values-usable-as-types). This recovers the original type for the facet, so `(Point_Inline as Vector) as type` is `Point_Inline` again. However, when a facet type like `Vector` is used as the binding type of a symbolic binding, as in `T:! Vector`, the [symbolic facet binding](#symbolic-facet-bindings) `T` is disassociated with whatever facet value `T` is eventually bound to. Instead, `T` is treated as an [archetype](terminology.md#archetype), with the members and [member access](/docs/design/expressions/member_access.md) determined by the names of the facet type. This general structure of facet types holds not just for interfaces, but others described in the rest of this document. ## Named constraints If the interfaces discussed above are the building blocks for facet types, [named constraints](terminology.md#named-constraints) describe how they may be composed together. Unlike interfaces which are nominal, the name of a named constraint is not a part of its value. Two different named constraints with the same definition are equivalent even if they have different names. This is because types don't have to explicitly specify which named constraints they implement, types automatically implement any named constraints they can satisfy. A named constraint definition can contain interface requirements using `require` ... `impls` declarations and names using `alias` declarations. Note that this allows us to declare the aspects of a facet type directly. ```carbon constraint VectorLegoFish { // Interface implementation requirements require impls Vector; require impls LegoFish; // Names alias Scale = Vector.Scale; alias VAdd = Vector.Add; alias LFAdd = LegoFish.Add; } ``` A `require impls` requirement may alternatively be on a named constraint, instead of an interface, to add all the requirements of another named constraint without adding any of the names: ```carbon constraint DrawVectorLegoFish { // The same as requiring both `Vector` and `LegoFish`. require impls VectorLegoFish; // A regular interface requirement. No syntactic difference. require impls Drawable; } ``` In general, Carbon makes no syntactic distinction between the uses of named constraints and interfaces, so one may be replaced with the other without affecting users. To accomplish this, Carbon allows a named constraint to be used whenever an interface may be. This includes all of these [uses of interfaces](#interfaces-recap): - A type may `impl` a named constraint to say that it implements all of the requirements of the named constraint, as [described below](#extend-require-with-named-constraints). - A named constraint may be used as a namespace name in [a qualified name](#qualified-member-names-and-compound-member-access). For example, `VectorLegoFish.VAdd` refers to the same name as `Vector.Add`. - A named constraint may be used as a [facet type](terminology.md#facet-type) for [a facet binding](#symbolic-facet-bindings). We don't expect developers to directly define many named constraints, but other constructs we do expect them to use will be defined in terms of them. For example, if `type` were not a keyword, we could define the Carbon builtin `type` as: ```carbon constraint type { } ``` That is, `type` is the facet type with no requirements (so matches every type), and defines no names. ```carbon fn Identity[T:! type](x: T*) -> T* { // Can accept values of any type. But, since we know nothing about the // type, we don't know about any operations on `x` inside this function. return x; } var i: i32 = 3; var p_i: i32* = Identity(&i); var s: String = "string"; var p_s: String = Identity(&s); ``` In general, the declarations in `constraint` definition match a subset of the declarations in an `interface`. These named constraints can be used with checked generics, as opposed to templates, and only include required interfaces and aliases to named members of those interfaces. To declare a named constraint that includes other declarations for use with template parameters, use the `template` keyword before `constraint`. Method, associated constant, and associated function requirements may only be declared inside a `template constraint`. Note that a checked-generic constraint ignores the names of members defined for a type, but a template constraint can depend on them. There is an analogy between declarations used in a `template constraint` and in an `interface` definition. If an `interface` `I` has (non-`alias`, non-`require`) declarations `X`, `Y`, and `Z`, like so: ```carbon interface I { X; Y; Z; } ``` Then a type implementing `I` would have `impl as I` with definitions for `X`, `Y`, and `Z`, as in: ```carbon class ImplementsI { // ... impl as I { X { ... } Y { ... } Z { ... } } } ``` But a `template constraint`, `S`: ```carbon template constraint S { X; Y; Z; } ``` would match any type with definitions for `X`, `Y`, and `Z` directly: ```carbon class ImplementsS { // ... X { ... } Y { ... } Z { ... } } ``` ### Subtyping between facet types There is a subtyping relationship between facet types that allows calls of one generic function from another as long as it has a subset of the requirements. Given a symbolic facet binding `T` with facet type `I1`, it satisfies a facet type `I2` as long as the requirements of `I1` are a superset of the requirements of `I2`. This means a value `x: T` may be passed to functions requiring types to satisfy `I2`, as in this example: ```carbon interface Printable { fn Print[self: Self](); } interface Renderable { fn Draw[self: Self](); } constraint PrintAndRender { require impls Printable; require impls Renderable; } constraint JustPrint { require impls Printable; } fn PrintIt[T2:! JustPrint](x2: T2) { x2.(Printable.Print)(); } fn PrintDrawPrint[T1:! PrintAndRender](x1: T1) { // x1 implements `Printable` and `Renderable`. x1.(Printable.Print)(); x1.(Renderable.Draw)(); // Can call `PrintIt` since `T1` satisfies `JustPrint` since // it implements `Printable` (in addition to `Renderable`). PrintIt(x1); } ``` ## Combining interfaces by anding facet types In order to support functions that require more than one interface to be implemented, we provide a combination operator on facet types, written `&`. This operator gives the facet type with the union of all the requirements and the union of the names. ```carbon interface Printable { fn Print[self: Self](); } interface Renderable { fn Center[self: Self]() -> (i32, i32); fn Draw[self: Self](); } // `Printable & Renderable` is syntactic sugar for this facet type: constraint { require impls Printable; require impls Renderable; alias Print = Printable.Print; alias Center = Renderable.Center; alias Draw = Renderable.Draw; } fn PrintThenDraw[T:! Printable & Renderable](x: T) { // Can use methods of `Printable` or `Renderable` on `x` here. x.Print(); // Same as `x.(Printable.Print)();`. x.Draw(); // Same as `x.(Renderable.Draw)();`. } class Sprite { // ... extend impl as Printable { fn Print[self: Self]() { ... } } extend impl as Renderable { fn Center[self: Self]() -> (i32, i32) { ... } fn Draw[self: Self]() { ... } } } var s: Sprite = ...; PrintThenDraw(s); ``` It is an error to use any names that conflict between the two interfaces. ```carbon interface Renderable { fn Center[self: Self]() -> (i32, i32); fn Draw[self: Self](); } interface EndOfGame { fn Draw[self: Self](); fn Winner[self: Self](player: i32); } fn F[T:! Renderable & EndOfGame](x: T) { // ❌ Error: Ambiguous, use either `(Renderable.Draw)` // or `(EndOfGame.Draw)`. x.Draw(); } ``` Conflicts can be resolved at the call site using a [qualified member access expression](#qualified-member-names-and-compound-member-access), or by defining a named constraint explicitly and renaming the methods: ```carbon constraint RenderableAndEndOfGame { require impls Renderable; require impls EndOfGame; alias Center = Renderable.Center; alias RenderableDraw = Renderable.Draw; alias TieGame = EndOfGame.Draw; alias Winner = EndOfGame.Winner; } fn RenderTieGame[T:! RenderableAndEndOfGame](x: T) { // ✅ Calls `Renderable.Draw`: x.RenderableDraw(); // ✅ Calls `EndOfGame.Draw`: x.TieGame(); } ``` Note that `&` is associative and commutative, and so it is well defined on sets of interfaces, or other facet types, independent of order. Note that we do _not_ consider two facet types using the same name to mean the same thing to be a conflict. For example, combining a facet type with itself gives itself, `MyTypeOfType & MyTypeOfType == MyTypeOfType`. Also, given two [interface extensions](#interface-extension) of a common base interface, the combination should not conflict on any names in the common base. To add to the requirements of a facet type without affecting the names, and so avoid the possibility of name conflicts, names, use a [`where .Self impls` clause](#implements-constraints). ``` // `Printable where .Self impls Renderable` is equivalent to: constraint { require impls Printable; require impls Renderable; alias Print = Printable.Print; } ``` You might use this to add requirements on interfaces used for [operator overloading](#operator-overloading), where merely implementing the interface is enough to be able to use the operator to access the functionality. Note that the expressions `A & B` and `A where .Self impls B` have the same requirements, and so you would be able to switch a function declaration between them without affecting callers. **Alternatives considered:** See [Carbon: Access to interface methods](https://docs.google.com/document/d/17IXDdu384x1t9RimQ01bhx4-nWzs4ZEeke4eO6ImQNc/edit?resourcekey=0-Fe44R-0DhQBlw0gs2ujNJA). **Rejected alternative:** Instead of using `&` as the combining operator, we considered using `+`, [like Rust](https://rust-lang.github.io/rfcs/0087-trait-bounds-with-plus.html). The main difference from Rust's `+` is how you [qualify names when there is a conflict](https://doc.rust-lang.org/rust-by-example/trait/disambiguating.html). See [issue #531](https://github.com/carbon-language/carbon-lang/issues/531) for the discussion. ## Interface requiring other interfaces Some interfaces depend on other interfaces being implemented for the same type. For example, in C++, [the `Container` concept](https://en.cppreference.com/w/cpp/named_req/Container#Other_requirements) requires all containers to also satisfy the requirements of `DefaultConstructible`, `CopyConstructible`, `Eq`, and `Swappable`. This is already a capability for [facet types in general](#facet-types). For consistency we use the same semantics and `require` ... `impls` syntax as we do for [named constraints](#named-constraints): ```carbon interface Equatable { fn Equals[self: Self](rhs: Self) -> bool; } interface Iterable { fn Advance[ref self: Self]() -> bool; require impls Equatable; } fn DoAdvanceAndEquals[T:! Iterable](x: T) { // `x` has type `T` that implements `Iterable`, and so has `Advance`. x.Advance(); // `Iterable` requires an implementation of `Equatable`, // so `T` also implements `Equatable`. x.(Equatable.Equals)(x); } class Iota { extend impl as Iterable { fn Advance[self: Self]() { ... } } extend impl as Equatable { fn Equals[self: Self](rhs: Self) -> bool { ... } } } var x: Iota; DoAdvanceAndEquals(x); ``` Like with named constraints, an interface implementation requirement doesn't by itself add any names to the interface, but again those can be added with `alias` declarations: ```carbon interface Hashable { fn Hash[self: Self]() -> u64; require impls Equatable; alias Equals = Equatable.Equals; } fn DoHashAndEquals[T:! Hashable](x: T) { // Now both `Hash` and `Equals` are available directly: x.Hash(); x.Equals(x); } ``` **Comparison with other languages:** [This feature is called "Supertraits" in Rust](https://doc.rust-lang.org/book/ch19-03-advanced-traits.html#using-supertraits-to-require-one-traits-functionality-within-another-trait). **Note:** The design for this feature is continued in [a later section](#interface-requiring-other-interfaces-revisited). ### Interface extension > **TODO:** Update this section as needed to reflect the fact that an impl of an > interface doesn't impl the interfaces it extends, as adopted in > [p5168: Forward `impl` declaration of an incomplete interface](/proposals/p5168.md). When implementing an interface, we allow implementing the aliased names as well. In the case of `Hashable` above, this includes all the members of `Equatable`, obviating the need to implement `Equatable` itself: ```carbon class Song { extend impl as Hashable { fn Hash[self: Self]() -> u64 { ... } fn Equals[self: Self](rhs: Self) -> bool { ... } } } var y: Song; DoHashAndEquals(y); ``` This allows us to say that `Hashable` ["extends"](terminology.md#extending-an-interface) `Equatable`, with some benefits: - This allows `Equatable` to be an implementation detail of `Hashable`. - This allows types implementing `Hashable` to implement all of its API in one place. - This reduces the boilerplate for types implementing `Hashable`. We expect this concept to be common enough to warrant dedicated `interface` syntax: > **TODO:** Update this section to reflect the new syntax adopted in > [p5337: Interface extension and `final impl` update](/proposals/p5337.md). ```carbon interface Equatable { fn Equals[self: Self](rhs: Self) -> bool; } interface Hashable { extend require impls Equatable; fn Hash[self: Self]() -> u64; } // is equivalent to the definition of Hashable from before: // interface Hashable { // require impls Equatable; // alias Equals = Equatable.Equals; // fn Hash[self: Self]() -> u64; // } ``` No names in `Hashable` are allowed to conflict with names in `Equatable` (unless those names are marked as `upcoming` or `deprecated` as in [evolution future work](#evolution)). Hopefully this won't be a problem in practice, since interface extension is a very closely coupled relationship, but this may be something we will have to revisit in the future. Examples: - The C++ [Boost.Graph library](https://www.boost.org/doc/libs/1_74_0/libs/graph/doc/) [graph concepts](https://www.boost.org/doc/libs/1_74_0/libs/graph/doc/graph_concepts.html#fig:graph-concepts) has many refining relationships between concepts. [Carbon generics use case: graph library](https://docs.google.com/document/d/15Brjv8NO_96jseSesqer5HbghqSTJICJ_fTaZOH0Mg4/edit?usp=sharing&resourcekey=0-CYSbd6-xF8vYHv9m1rolEQ) shows how those concepts might be translated into Carbon interfaces. - The [C++ concepts](https://en.cppreference.com/w/cpp/named_req) for containers, iterators, and concurrency include many requirement relationships. - Swift protocols, such as [Collection](https://developer.apple.com/documentation/swift/collection). To write an interface extending multiple interfaces, use multiple `extend` declarations. For example, the [`BinaryInteger` protocol in Swift](https://developer.apple.com/documentation/swift/binaryinteger) inherits from `CustomStringConvertible`, `Hashable`, `Numeric`, and `Stridable`. The [`SetAlgebra` protocol](https://swiftdoc.org/v5.1/protocol/setalgebra/) extends `Equatable` and `ExpressibleByArrayLiteral`, which would be declared in Carbon: ```carbon interface SetAlgebra { extend require impls Equatable; extend require impls ExpressibleByArrayLiteral; } ``` With `extend require impls I`, an interface requires an `impl` to exist for `I` when implementing the containing interface. This requires writing at least two `impl`s to implement the containing interface. When the two interfaces are tightly coupled, it's possible to have one interface gain the members of and _provide_ an implementation for the other interface instead, with `extend impl as`. Just as when used in a class, the implied `Self` between `extend impl` and `as` must be omitted. When an interface `B` contains `extend impl as A`, implementing `B` will require writing an implementation of all members of `A` inside the `impl` of `B`. And anything that implements `B` will implicitly also implement `A`, using the definitions from the `impl` of `B`. Here is an example: ```carbon interface A { let T:! type; fn F(); fn G(); } interface B { extend impl as A; fn H(); } ``` This is equivalent to writing `B` as: ```carbon interface B { let T:! type; fn F(); fn G(); fn H(); } impl forall [U:! B] U as A { let T:! type = U.(B.T); fn F() = U.(B.F); fn G() = U.(B.G); } ``` The implied `impl` definition can be made `final` by writing the reference to `A` as `extend final impl as A`. Note that this is supported only in an `interface` and not in a named [`constraint`](#named-constraints). **Alternative considered:** The `extend` declarations are in the body of the `interface` definition instead of the header so we can use [associated constants](terminology.md#associated-entity) also defined in the body in parameters or constraints of the interface being extended. ```carbon // A type can implement `ConvertibleTo` many times, // using different values of `T`. interface ConvertibleTo(T:! type) { ... } // A type can only implement `PreferredConversion` once. interface PreferredConversion { let AssociatedFacet:! type; // `extend require impls` is in the body of an `interface` // definition. This allows extending an expression // that uses an associated facet. extend require impls ConvertibleTo(AssociatedFacet); } ``` #### `extend require` with named constraints The `extend` modifier on `require` makes sense with the same meaning inside a [`constraint`](#named-constraints) definition, and so is also supported (unlike `extend impl as`). ```carbon interface Media { fn Play[self: Self](); } interface Job { fn Run[self: Self](); } constraint Combined { extend require impls Media; extend require impls Job; } ``` This definition of `Combined` is equivalent to requiring both the `Media` and `Job` interfaces being implemented, and aliases their methods. ```carbon // Equivalent constraint Combined { require impls Media; alias Play = Media.Play; require impls Job; alias Run = Job.Run; } ``` Notice how `Combined` has aliases for all the methods in the interfaces it requires. That condition is sufficient to allow a type to `impl` the named constraint: ```carbon class Song { extend impl as Combined { fn Play[self: Self]() { ... } fn Run[self: Self]() { ... } } } ``` This is equivalent to implementing the required interfaces directly: ```carbon class Song { extend impl as Media { fn Play[self: Self]() { ... } } extend impl as Job { fn Run[self: Self]() { ... } } } ``` This is just like when you get an implementation of `Equatable` by implementing `Hashable` when `Hashable` extends `Equatable`. This provides a tool useful for [evolution](#evolution). Conversely, an `interface` can extend a `constraint`: ```carbon interface MovieCodec { extend require impls Combined; fn Load[ref self: Self](filename: String); } ``` This gives `MovieCodec` the same requirements and names as `Combined`, and so is equivalent to: ```carbon interface MovieCodec { require impls Media; alias Play = Media.Play; require impls Job; alias Run = Job.Run; fn Load[ref self: Self](filename: String); } ``` #### Diamond dependency issue > **TODO:** Update this section to reflect the changes in > [p5168: Forward `impl` declaration of an incomplete interface](/proposals/p5168.md). Consider this set of interfaces, simplified from [this example generic graph library doc](https://docs.google.com/document/d/15Brjv8NO_96jseSesqer5HbghqSTJICJ_fTaZOH0Mg4/edit?usp=sharing&resourcekey=0-CYSbd6-xF8vYHv9m1rolEQ): ```carbon interface Graph { fn Source[ref self: Self](e: EdgeDescriptor) -> VertexDescriptor; fn Target[ref self: Self](e: EdgeDescriptor) -> VertexDescriptor; } interface IncidenceGraph { extend Graph; fn OutEdges[ref self: Self](u: VertexDescriptor) -> (EdgeIterator, EdgeIterator); } interface EdgeListGraph { extend Graph; fn Edges[ref self: Self]() -> (EdgeIterator, EdgeIterator); } ``` We need to specify what happens when a graph type implements both `IncidenceGraph` and `EdgeListGraph`, since both interfaces extend the `Graph` interface. ```carbon class MyEdgeListIncidenceGraph { extend impl as IncidenceGraph { ... } extend impl as EdgeListGraph { ... } } ``` The rule is that we need one definition of each method of `Graph`. Each method though could be defined in the `impl` block of `IncidenceGraph`, `EdgeListGraph`, or `Graph`. These would all be valid: - `IncidenceGraph` implements all methods of `Graph`, `EdgeListGraph` implements none of them. ```carbon class MyEdgeListIncidenceGraph { extend impl as IncidenceGraph { fn Source[self: Self](e: EdgeDescriptor) -> VertexDescriptor { ... } fn Target[self: Self](e: EdgeDescriptor) -> VertexDescriptor { ... } fn OutEdges[ref self: Self](u: VertexDescriptor) -> (EdgeIterator, EdgeIterator) { ... } } extend impl as EdgeListGraph { fn Edges[ref self: Self]() -> (EdgeIterator, EdgeIterator) { ... } } } ``` - `IncidenceGraph` and `EdgeListGraph` implement all methods of `Graph` between them, but with no overlap. ```carbon class MyEdgeListIncidenceGraph { extend impl as IncidenceGraph { fn Source[self: Self](e: EdgeDescriptor) -> VertexDescriptor { ... } fn OutEdges[ref self: Self](u: VertexDescriptor) -> (EdgeIterator, EdgeIterator) { ... } } extend impl as EdgeListGraph { fn Target[self: Self](e: EdgeDescriptor) -> VertexDescriptor { ... } fn Edges[ref self: Self]() -> (EdgeIterator, EdgeIterator) { ... } } } ``` - Explicitly implementing `Graph`. ```carbon class MyEdgeListIncidenceGraph { extend impl as Graph { fn Source[self: Self](e: EdgeDescriptor) -> VertexDescriptor { ... } fn Target[self: Self](e: EdgeDescriptor) -> VertexDescriptor { ... } } extend impl as IncidenceGraph { ... } extend impl as EdgeListGraph { ... } } ``` - Implementing `Graph` out-of-line. ```carbon class MyEdgeListIncidenceGraph { extend impl as IncidenceGraph { ... } extend impl as EdgeListGraph { ... } } impl MyEdgeListIncidenceGraph as Graph { fn Source[self: Self](e: EdgeDescriptor) -> VertexDescriptor { ... } fn Target[self: Self](e: EdgeDescriptor) -> VertexDescriptor { ... } } ``` This last point means that there are situations where we can only detect a missing method definition by the end of the file. This doesn't delay other aspects of semantic checking, which will just assume that these methods will eventually be provided. **Open question:** We could require that the `impl` of the required interface be declared lexically in the class scope in this case. That would allow earlier detection of missing definitions. ### Use case: detecting unreachable matches If interface `E` extends another interface `I`, that gives the information to the compiler that the any type implementing `E` also implements `I`. This can be used to detect unreachable code. For example, the [`impl` prioritization rule](#prioritization-rule) is used to pick between `impl` declarations based on an explicit priority ordering given by the developer. If the broader interface `I` is prioritized over the more specific interface `E`, the compiler can conclude that the more specific declaration will never be selected and report an error. Similar situations could be detected in function overloading. ## Adapting types Since interfaces may only be implemented for a type once, and we limit where implementations may be added to a type, there is a need to allow the user to switch the type of a value to access different interface implementations. Carbon therefore provides a way to create new types [compatible with](terminology.md#compatible-types) existing types with different APIs, in particular with different interface implementations, by [adapting](terminology.md#adapting-a-type) them: ```carbon interface Printable { fn Print[self: Self](); } interface Ordered { fn Less[self: Self](rhs: Self) -> bool; } class Song { extend impl as Printable { fn Print[self: Self]() { ... } } } class SongByTitle { adapt Song; extend impl as Ordered { fn Less[self: Self](rhs: Self) -> bool { ... } } } class FormattedSong { adapt Song; extend impl as Printable { fn Print[self: Self]() { ... } } } class FormattedSongByTitle { adapt Song; extend impl as Printable = FormattedSong; extend impl as Ordered = SongByTitle; } ``` This allows developers to provide implementations of new interfaces (as in `SongByTitle`), provide different implementations of the same interface (as in `FormattedSong`), or mix and match implementations from other compatible types (as in `FormattedSongByTitle`). The rules are: - You can add any declaration that you could add to a class except for declarations that would change the representation of the type. This means you can add methods, functions, interface implementations, and aliases, but not fields, base classes, or virtual functions. The specific implementations of virtual functions are part of the type representation, and so no virtual functions may be overridden in an adapter either. - The adapted type is compatible with the original type, and that relationship is an equivalence class, so all of `Song`, `SongByTitle`, `FormattedSong`, and `FormattedSongByTitle` end up compatible with each other. - Since adapted types are compatible with the original type, you may explicitly cast between them, but there is no implicit conversion between these types. Inside an adapter, the `Self` type matches the adapter. Members of the original type may be accessed either by a cast: ```carbon class SongByTitle { adapt Song; extend impl as Ordered { fn Less[self: Self](rhs: Self) -> bool { return (self as Song).Title() < (rhs as Song).Title(); } } } ``` or using a qualified member access expression: ```carbon class SongByTitle { adapt Song; extend impl as Ordered { fn Less[self: Self](rhs: Self) -> bool { return self.(Song.Title)() < rhs.(Song.Title)(); } } } ``` **Comparison with other languages:** This matches the Rust idiom called "newtype", which is used to implement traits on types while avoiding [coherence](terminology.md#coherence) problems, see [here](https://doc.rust-lang.org/book/ch19-03-advanced-traits.html#using-the-newtype-pattern-to-implement-external-traits-on-external-types) and [here](https://github.com/Ixrec/rust-orphan-rules#user-content-why-are-the-orphan-rules-controversial). Rust's mechanism doesn't directly support reusing implementations, though some of that is provided by macros defined in libraries. Haskell has a [`newtype` feature](https://wiki.haskell.org/Newtype) as well. Haskell's feature doesn't directly support reusing implementations either, but the most popular compiler provides it as [an extension](https://ghc.gitlab.haskell.org/ghc/doc/users_guide/exts/newtype_deriving.html). ### Adapter compatibility Consider a [type with a facet parameter, like a hash map](#parameterized-types): ```carbon interface Hashable { ... } class HashMap(KeyT:! Hashable, ValueT:! type) { fn Find[self: Self](key: KeyT) -> Optional(ValueT); // ... } ``` A user of this type will provide specific values for the key and value types: ```carbon class Song { extend impl as Hashable { ... } // ... } var play_count: HashMap(Song, i32) = ...; var thriller_count: Optional(i32) = play_count.Find(Song("Thriller")); ``` Since the `KeyT` and `ValueT` are symbolic parameters, the `Find` function is a checked generic, and it can only use the capabilities of `KeyT` and `ValueT` specified as requirements. This allows us to evaluate when we can convert between two different arguments to a parameterized type. Consider two adapters of `Song` that implement `Hashable`: ```carbon class PlayableSong { adapt Song; extend impl as Hashable = Song; extend impl as Media { ... } } class SongHashedByTitle { adapt Song; extend impl as Hashable { ... } } ``` `Song` and `PlayableSong` have the same implementation of `Hashable` in addition to using the same data representation. This means that it is safe to convert between `HashMap(Song, i32)` and `HashMap(PlayableSong, i32)`, because the implementation of all the methods will use the same implementation of the `Hashable` interface. Carbon permits this conversion with an explicit cast. On the other hand, `SongHashedByTitle` has a different implementation of `Hashable` than `Song`. So even though `Song` and `SongHashedByTitle` are compatible types, `HashMap(Song, i32)` and `HashMap(SongHashedByTitle, i32)` are incompatible. This is important because we know that in practice the invariants of a `HashMap` implementation rely on the hashing function staying the same. ### Extending adapter Frequently we expect that the adapter type will want to preserve most or all of the API of the original type. The two most common cases expected are adding and replacing an interface implementation. Users would indicate that an adapter starts from the original type's existing API by using the `extend` keyword before `adapt`: ```carbon class Song { extend impl as Hashable { ... } extend impl as Printable { ... } } class SongByArtist { extend adapt Song; // Add an implementation of a new interface extend impl as Ordered { ... } // Replace an existing implementation of an interface // with an alternative. extend impl as Hashable { ... } } ``` The resulting type `SongByArtist` would: - implement `Ordered`, unlike `Song`, - implement `Hashable`, but differently than `Song`, and - implement `Printable`, inherited from `Song`. The rule is that when looking up if `SongByArtist` implements an interface `I` and no implementation is found, the compiler repeats the search to see if `Song` implements `I`. If that is found, it is reused if possible. The reuse will be successful if all types that reference `Self` in the signatures of interface's functions can be cast to the corresponding type with `SongByArtist` substituted in for `Song`. Unlike the similar `class B { extend base: A; }` notation, `class B { extend adapt A; }` is permitted even if `A` is a final class. Also, there is no implicit conversion from `B` to `A`, matching `adapt` without `extend` but unlike class extension. To avoid or resolve name conflicts between interfaces, an `impl` may be declared without [`extend`](#extend-impl). The names in that interface may then be pulled in individually or renamed using `alias` declarations. ```carbon class SongRenderToPrintDriver { extend adapt Song; // Add a new `Print()` member function. fn Print[self: Self]() { ... } // Avoid name conflict with new `Print` // function by implementing the `Printable` // interface without `extend`. impl as Printable = Song; // Make the `Print` function from `Printable` // available under the name `PrintToScreen`. alias PrintToScreen = Printable.Print; } ``` ### Use case: Using independent libraries together Imagine we have two packages that are developed independently. Package `CompareLib` defines an interface `CompareLib.Comparable` and a checked-generic algorithm `CompareLib.Sort` that operates on types that implement `CompareLib.Comparable`. Package `SongLib` defines a type `SongLib.Song`. Neither has a dependency on the other, so neither package defines an implementation for `CompareLib.Comparable` for type `SongLib.Song`. A user that wants to pass a value of type `SongLib.Song` to `CompareLib.Sort` has to define an adapter that provides an implementation of `CompareLib.Comparable` for `SongLib.Song`. This adapter will probably use the [`extend` facility of adapters](#extending-adapter) to preserve the `SongLib.Song` API. ```carbon import CompareLib; import SongLib; class Song { extend adapt SongLib.Song; extend impl as CompareLib.Comparable { ... } } // Or, to keep the names from CompareLib.Comparable out of Song's API: class Song { extend adapt SongLib.Song; } impl Song as CompareLib.Comparable { ... } // Or, equivalently: class Song { extend adapt SongLib.Song; impl as CompareLib.Comparable { ... } } ``` The caller can either convert `SongLib.Song` values to `Song` when calling `CompareLib.Sort` or just start with `Song` values in the first place. ```carbon var lib_song: SongLib.Song = ...; CompareLib.Sort((lib_song as Song,)); var song: Song = ...; CompareLib.Sort((song,)); ``` ### Use case: Defining an impl for use by other types Let's say we want to provide a possible implementation of an interface for use by types for which that implementation would be appropriate. We can do that by defining an adapter implementing the interface that is parameterized on the type it is adapting. That impl may then be pulled in using the `impl as ... = ...;` syntax. For example, given an interface `Comparable` for deciding which value is smaller: ```carbon interface Comparable { fn Less[self: Self](rhs: Self) -> bool; } ``` We might define an adapter that implements `Comparable` for types that define another interface `Difference`: ```carbon interface Difference { fn Sub[self: Self](rhs: Self) -> i32; } class ComparableFromDifference(T:! Difference) { adapt T; extend impl as Comparable { fn Less[self: Self](rhs: Self) -> bool { return (self as T).Sub(rhs) < 0; } } } class IntWrapper { var x: i32; impl as Difference { fn Sub[self: Self](rhs: Self) -> i32 { return left.x - right.x; } } impl as Comparable = ComparableFromDifference(IntWrapper); } ``` **TODO:** If we support function types, we could potentially pass a function to use to the adapter instead: ```carbon class ComparableFromDifferenceFn (T:! type, Difference:! fnty(T, T)->i32) { adapt T; extend impl as Comparable { fn Less[self: Self](rhs: Self) -> bool { return Difference(self as T, rhs as T) < 0; } } } class IntWrapper { var x: i32; fn Difference(left: Self, right: Self) { return left.x - right.x; } impl as Comparable = ComparableFromDifferenceFn(IntWrapper, Difference); } ``` ### Use case: Private impl Adapter types can be used when a library publicly exposes a type, but only wants to say that type implements an interface as a private detail internal to the implementation of the type. In that case, instead of implementing the interface for the public type, the library can create a private adapter for that type and implement the interface on that instead. Any member of the class can cast its `self` parameter to the adapter type when it wants to make use of the private impl. ```carbon // Public, in API file class Complex64 { // ... fn CloserToOrigin[self: Self](them: Self) -> bool; } // Private class ByReal { extend adapt Complex64; // Complex numbers are not generally comparable, // but this comparison function is useful for some // method implementations. extend impl as Comparable { fn Less[self: Self](that: Self) -> bool { return self.Real() < that.Real(); } } } fn Complex64.CloserToOrigin[self: Self](them: Self) -> bool { var self_mag: ByReal = self * self.Conj() as ByReal; var them_mag: ByReal = them * them.Conj() as ByReal; return self_mag.Less(them_mag); } ``` ### Use case: Accessing interface names Consider a case where a function will call several functions from an interface that the type does not [extend the implementation of](terminology.md#extending-an-impl). ```carbon interface DrawingContext { fn SetPen[self: Self](...); fn SetFill[self: Self](...); fn DrawRectangle[self: Self](...); fn DrawLine[self: Self](...); ... } impl Window as DrawingContext { ... } ``` An adapter can make that more convenient by making a compatible type that does extend the implementation of the interface. This avoids having to [qualify](terminology.md#qualified-member-access-expression) each call to methods in the interface. ```carbon class DrawInWindow { adapt Window; extend impl as DrawingContext = Window; } fn Render(w: Window) { let d: DrawInWindow = w as DrawInWindow; d.SetPen(...); d.SetFill(...); d.DrawRectangle(...); ... } ``` **Note:** Another way to achieve this is to use a [local symbolic facet constant](#compile-time-let). ```carbon fn Render(w: Window) { let DrawInWindow:! Draw = Window; // Implicit conversion to `w as DrawInWindow`. let d: DrawInWindow = w; d.SetPen(...); d.SetFill(...); d.DrawRectangle(...); ... } ``` ### Future work: Adapter with stricter invariants **Future work:** Rust also uses the newtype idiom to create types with additional invariants or other information encoded in the type ([1](https://doc.rust-lang.org/rust-by-example/generics/new_types.html), [2](https://doc.rust-lang.org/book/ch19-04-advanced-types.html#using-the-newtype-pattern-for-type-safety-and-abstraction), [3](https://www.worthe-it.co.za/blog/2020-10-31-newtype-pattern-in-rust.html)). This is used to record in the type system that some data has passed validation checks, like `ValidDate` with the same data layout as `Date`. Or to record the units associated with a value, such as `Seconds` versus `Milliseconds` or `Feet` versus `Meters`. We should have some way of restricting the casts between a type and an adapter to address this use case. One possibility would be to add the keyword `private` before `adapt`, so you might write `extend private adapt Date;`. ## Associated constants > **TODO:** Update this section to reflect the new rules and guidance on > associated constants in > [p5168: Forward `impl` declaration of an incomplete interface](/proposals/p5168.md). In addition to associated methods, we allow other kinds of [associated entities](terminology.md#associated-entity). For consistency, we use the same syntax to describe a compile-time constant in an interface as in a type without assigning a value. As constants, they are declared using the `let` introducer. For example, a fixed-dimensional point type could have the dimension as an associated constant. ```carbon interface NSpacePoint { let N:! i32; // The following require: 0 <= i < N. fn Get[ref self: Self](i: i32) -> f64; fn Set[ref self: Self](i: i32, value: f64); // Associated constants may be used in signatures: fn SetAll[ref self: Self](value: Array(f64, N)); } ``` The pattern of an associated constant declaration must be a symbolic binding pattern, and unlike other `let` declarations, an associated constant declaration cannot have an initializer unless it's [preceded by `default`](#interface-defaults): _associated-constant-decl_ ::= `let` _identifier_ `:!` _expression_ `;` _associated-constant-decl_ ::= `default` `let` _identifier_ `:!` _expression_ = _expression_ `;` An implementation of an interface specifies values for associated constants with a [`where` clause](#where-constraints). For example, implementations of `NSpacePoint` for different types might have different values for `N`: ```carbon class Point2D { extend impl as NSpacePoint where .N = 2 { fn Get[ref self: Self](i: i32) -> f64 { ... } fn Set[ref self: Self](i: i32, value: f64) { ... } fn SetAll[ref self: Self](value: Array(f64, 2)) { ... } } } class Point3D { extend impl as NSpacePoint where .N = 3 { fn Get[ref self: Self](i: i32) -> f64 { ... } fn Set[ref self: Self](i: i32, value: f64) { ... } fn SetAll[ref self: Self](value: Array(f64, 3)) { ... } } } ``` Multiple assignments to associated constants may be joined using the `and` keyword. The list of assignments is subject to two restrictions: - An implementation of an interface cannot specify a value for a [`final`](#final-members) associated constant. - If an associated constant doesn't have a [default value](#interface-defaults), every implementation must specify its value. These values may be accessed as members of the type: ```carbon Assert(Point2D.N == 2); Assert(Point3D.N == 3); fn PrintPoint[PointT:! NSpacePoint](p: PointT) { var i: i32 = 0 while (i < PointT.N) { if (i > 0) { Print(", "); } Print(p.Get(i)); ++i; } } fn ExtractPoint[PointT:! NSpacePoint]( p: PointT, dest: Array(f64, PointT.N)*) { var i: i32 = 0; while (i < PointT.N) { (*dest)[i] = p.Get(i); ++i; } } ``` **Comparison with other languages:** This feature is also called [associated constants in Rust](https://doc.rust-lang.org/reference/items/associated-items.html#associated-constants). **Aside:** The use of `:!` here means these `let` declarations will only have compile-time and not runtime storage associated with them. ### Associated class functions To be consistent with normal [class function](/docs/design/classes.md#class-functions) declaration syntax, associated class functions are written using a `fn` declaration: ```carbon interface DeserializeFromString { fn Deserialize(serialized: String) -> Self; } class MySerializableType { var i: i32; extend impl as DeserializeFromString { fn Deserialize(serialized: String) -> Self { return {.i = StringToInt(serialized)}; } } } var x: MySerializableType = MySerializableType.Deserialize("3"); fn Deserialize(T:! DeserializeFromString, serialized: String) -> T { return T.Deserialize(serialized); } var y: MySerializableType = Deserialize(MySerializableType, "4"); ``` This is instead of declaring an associated constant using `let` with a function type. Together associated methods and associated class functions are called _associated functions_, much like together methods and class functions are called [member functions](/docs/design/classes.md#member-functions). > **TODO:** Document rules on where associated function implementations can be > declared, as adopted in > [p5168: Forward `impl` declaration of an incomplete interface](/proposals/p5168.md). ## Associated facets Associated facets are [associated constants](#associated-constants) that happen to have a [facet type](terminology.md#facet-type). These are particularly interesting since they can be used in the signatures of associated methods or functions, to allow the signatures of methods to vary from implementation to implementation. We already have one example of this: the `Self` type discussed [in the "Interfaces" section](#interfaces). For other cases, we can say that the interface declares that each implementation will provide a facet constant under a specified name. For example: ```carbon interface StackAssociatedFacet { let ElementType:! type; fn Push[ref self: Self](value: ElementType); fn Pop[ref self: Self]() -> ElementType; fn IsEmpty[ref self: Self]() -> bool; } ``` Here we have an interface called `StackAssociatedFacet` which defines two methods, `Push` and `Pop`. The signatures of those two methods declare them as accepting or returning values with the type `ElementType`, which any implementer of `StackAssociatedFacet` must also define. For example, maybe a `DynamicArray` [parameterized type](#parameterized-types) implements `StackAssociatedFacet`: ```carbon class DynamicArray(T:! type) { class IteratorType { ... } fn Begin[ref self: Self]() -> IteratorType; fn End[ref self: Self]() -> IteratorType; fn Insert[ref self: Self](pos: IteratorType, value: T); fn Remove[ref self: Self](pos: IteratorType); // Set the associated facet `ElementType` to `T`. extend impl as StackAssociatedFacet where .ElementType = T { fn Push[ref self: Self](value: ElementType) { self.Insert(self.End(), value); } fn Pop[ref self: Self]() -> ElementType { var pos: IteratorType = self.End(); Assert(pos != self.Begin()); --pos; returned var ret: ElementType = *pos; self.Remove(pos); return var; } fn IsEmpty[ref self: Self]() -> bool { return self.Begin() == self.End(); } } } ``` The keyword `Self` can be used after the `as` in an `impl` declaration as a shorthand for the type being implemented, including in the `where` clause specifying the values of associated facets, as in: ```carbon impl VeryLongTypeName as Add // `Self` here means `VeryLongTypeName` where .Result = Self { ... } ``` > **Alternatives considered:** See > [other syntax options considered in #731 for specifying associated facets](/proposals/p0731.md#syntax-for-associated-constants). > In particular, it was deemed that > [Swift's approach of inferring an associated facet from method signatures in the impl](https://docs.swift.org/swift-book/LanguageGuide/Generics.html#ID190) > was unneeded complexity. The definition of the `StackAssociatedFacet` is sufficient for writing a checked-generic function that operates on anything implementing that interface, for example: ```carbon fn PeekAtTopOfStack[StackType:! StackAssociatedFacet](s: StackType*) -> StackType.ElementType { var top: StackType.ElementType = s->Pop(); s->Push(top); return top; } ``` Inside the checked-generic function `PeekAtTopOfStack`, the `ElementType` associated facet member of `StackType` is an [archetype](terminology.md#archetype), like other [symbolic facet bindings](#symbolic-facet-bindings). This means `StackType.ElementType` has the API dictated by the declaration of `ElementType` in the interface `StackAssociatedFacet`. Outside the checked-generic, associated facets have the concrete facet values determined by impl lookup, rather than the erased version of that facet used inside a checked-generic. ```carbon var my_array: DynamicArray(i32) = (1, 2, 3); // PeekAtTopOfStack's `StackType` is set to `DynamicArray(i32)` // with `StackType.ElementType` set to `i32`. Assert(PeekAtTopOfStack(my_array) == 3); ``` This is another part of achieving [the goal that generic functions can be used in place of regular functions without changing the return type that callers see](goals.md#path-from-regular-functions) discussed in the [return type section](#return-type). Associated facets can also be implemented using a [member type](/docs/design/classes.md#member-type). ```carbon interface Container { let IteratorType:! Iterator; ... } class DynamicArray(T:! type) { ... extend impl as Container { class IteratorType { extend impl as Iterator { ... } } ... } } ``` For context, see ["Interface parameters and associated constants" in the generics terminology document](terminology.md#interface-parameters-and-associated-constants). **Comparison with other languages:** Both [Rust](https://doc.rust-lang.org/book/ch19-03-advanced-traits.html#specifying-placeholder-types-in-trait-definitions-with-associated-types) and [Swift](https://docs.swift.org/swift-book/LanguageGuide/Generics.html#ID189) support these, but call them "associated types." ## Parameterized interfaces Associated constants don't change the fact that a type can only implement an interface at most once. If instead you want a family of related interfaces, one per possible value of a type parameter, multiple of which could be implemented for a single type, you would use [_parameterized interfaces_](terminology.md#interface-parameters-and-associated-constants), also known as _generic interfaces_. To write a parameterized version of the stack interface, instead of using associated constants, write a parameter list after the name of the interface: ```carbon interface StackParameterized(ElementType:! type) { fn Push[ref self: Self](value: ElementType); fn Pop[ref self: Self]() -> ElementType; fn IsEmpty[ref self: Self]() -> bool; } ``` Then `StackParameterized(Fruit)` and `StackParameterized(Veggie)` would be considered different interfaces, with distinct implementations. ```carbon class Produce { var fruit: DynamicArray(Fruit); var veggie: DynamicArray(Veggie); extend impl as StackParameterized(Fruit) { fn Push[ref self: Self](value: Fruit) { self.fruit.Push(value); } fn Pop[ref self: Self]() -> Fruit { return self.fruit.Pop(); } fn IsEmpty[ref self: Self]() -> bool { return self.fruit.IsEmpty(); } } extend impl as StackParameterized(Veggie) { fn Push[ref self: Self](value: Veggie) { self.veggie.Push(value); } fn Pop[ref self: Self]() -> Veggie { return self.veggie.Pop(); } fn IsEmpty[ref self: Self]() -> bool { return self.veggie.IsEmpty(); } } } ``` Unlike associated constants in interfaces and parameters to types, interface parameters can't be deduced. For example, if we were to rewrite [the `PeekAtTopOfStack` example in the "associated facets" section](#associated-facets) for `StackParameterized(T)` it would generate a compile error: ```carbon // ❌ Error: can't deduce interface parameter `T`. fn BrokenPeekAtTopOfStackParameterized [T:! type, StackType:! StackParameterized(T)] (s: StackType*) -> T { ... } ``` This error is because the compiler can not determine if `T` should be `Fruit` or `Veggie` when passing in argument of type `Produce*`. Either `T` should be replaced by a concrete type, like `Fruit`: ```carbon fn PeekAtTopOfFruitStack [StackType:! StackParameterized(Fruit)] (s: StackType*) -> T { ... } var produce: Produce = ...; var top_fruit: Fruit = PeekAtTopOfFruitStack(&produce); ``` Or the value for `T` would be passed explicitly, using `where` constraints described [in this section](#another-type-implements-parameterized-interface): ```carbon fn PeekAtTopOfStackParameterizedImpl (T:! type, StackType:! StackParameterized(T), s: StackType*) -> T { ... } fn PeekAtTopOfStackParameterized[StackType:! type] (s: StackType*, T:! type where StackType impls StackParameterized(T)) -> T { return PeekAtTopOfStackParameterizedImpl(T, StackType, s); } var produce: Produce = ...; var top_fruit: Fruit = PeekAtTopOfStackParameterized(&produce, Fruit); var top_veggie: Veggie = PeekAtTopOfStackParameterized(&produce, Veggie); ``` > **Note:** Alternative ways of declaraing `PeekAtTopOfStackParameterized` are > described and discussed in > [#578: Value patterns as function parameters](https://github.com/carbon-language/carbon-lang/issues/578). Parameterized interfaces are useful for [operator overloads](#operator-overloading). For example, the `EqWith(T)` and `OrderedWith(T)` interfaces have a parameter that allows type to be comparable with multiple other types, as in: ```carbon interface EqWith(T:! type) { fn Equal[self: Self](rhs: T) -> bool; ... } class Complex { var real: f64; var imag: f64; // Can implement this interface more than once // as long as it has different arguments. extend impl as EqWith(f64) { ... } // Same as: impl as EqWith(Complex) { ... } extend impl as EqWith(Self) { ... } } ``` All interface parameters must be marked as "symbolic", using the `:!` binding pattern syntax. This reflects these two properties of these parameters: - They must be resolved at compile-time, and so can't be passed regular dynamic values. - We allow either symbolic or template values to be passed in. **Future work:** We might also allow `template` bindings for interface parameters, once we have a use case. **Note:** Interface parameters aren't required to be facets, but that is the vast majority of cases. As an example, if we had an interface that allowed a type to define how the tuple-member-read operator would work, the index of the member could be an interface parameter: ```carbon interface ReadTupleMember(index:! u32) { let T:! type; // Returns self[index] fn Get[self: Self]() -> T; } ``` This requires that the index be known at compile time, but allows different indices to be associated with different values of `T`. **Caveat:** When implementing an interface twice for a type, the interface parameters are required to always be different. For example: ```carbon interface Map(FromType:! type, ToType:! type) { fn Map[ref self: Self](needle: FromType) -> Optional(ToType); } class Bijection(FromType:! type, ToType:! type) { extend impl as Map(FromType, ToType) { ... } extend impl as Map(ToType, FromType) { ... } } // ❌ Error: Bijection has two different impl definitions of // interface Map(String, String) var oops: Bijection(String, String) = ...; ``` In this case, it would be better to have an [adapting type](#adapting-types) to contain the `impl` for the reverse map lookup, instead of implementing the `Map` interface twice: ```carbon class Bijection(FromType:! type, ToType:! type) { extend impl as Map(FromType, ToType) { ... } } class ReverseLookup(FromType:! type, ToType:! type) { adapt Bijection(FromType, ToType); extend impl as Map(ToType, FromType) { ... } } ``` **Comparison with other languages:** Rust calls [traits with parameters "generic traits"](https://doc.rust-lang.org/reference/items/traits.html#generic-traits) and [uses them for operator overloading](https://doc.rust-lang.org/book/ch19-03-advanced-traits.html#default-generic-type-parameters-and-operator-overloading). [Rust uses the term "type parameters"](https://github.com/rust-lang/rfcs/blob/master/text/0195-associated-items.md#clearer-trait-matching) for both interface facet parameters and associated facets. The difference is that interface parameters are "inputs" since they _determine_ which `impl` to use, and associated constants are "outputs" since they are determined _by_ the `impl`, but play no role in selecting the `impl`. ### Parameterized named constraints Carbon also allows the [named constraint](#named-constraints) construct to support parameters. Those parameters work the same way as for interfaces. ## Where constraints So far, we have restricted a [symbolic facet binding](#symbolic-facet-bindings) by saying it has to implement an interface or a set of interfaces. There are a variety of other constraints we would like to be able to express, such as applying restrictions to associated constants. This is done using the `where` operator that adds constraints to a [facet type](#facet-types). The where operator can be applied to a facet type in a declaration context: ```carbon // Constraints on generic function parameters: fn F[V:! D where ...](v: V) { ... } // Constraints on a class parameter: class S(T:! B where ...) { // Constraints on a method: fn G[self: Self, V:! D where ...](v: V); } // Constraints on an interface parameter: interface A(T:! B where ...) { // Constraints on an associated facet: let U:! C where ...; // Constraints on an associated method: fn G[self: Self, V:! D where ...](v: V); } ``` We also allow you to name constraints using a `where` operator in a `let` or `constraint` definition. The expressions that can follow the `where` keyword are described in the ["kinds of `where` constraints"](#kinds-of-where-constraints) section, but generally look like boolean expressions that should evaluate to `true`. The result of applying a `where` operator to a facet type is another facet type. Note that this expands the kinds of requirements that facet types can have from just interface requirements to also include the various kinds of constraints discussed later in this section. In addition, it can introduce relationships between different type variables, such as that a member of one is equal to a member of another. The `where` operator is not associative, so a type expression using multiple must use round parens `(`...`)` to specify grouping. > **Comparison with other languages:** Both Swift and Rust use `where` clauses > on declarations instead of in the expression syntax. These happen after the > type that is being constrained has been given a name and use that name to > express the constraint. > > Rust also supports > [directly passing in the values for associated types](https://rust-lang.github.io/rfcs/0195-associated-items.html#constraining-associated-types) > when using a trait as a constraint. This is helpful when specifying concrete > types for all associated types in a trait in order to > [make it object safe so it can be used to define a trait object type](https://rust-lang.github.io/rfcs/0195-associated-items.html#trait-objects). > > Rust is adding trait aliases > ([RFC](https://github.com/rust-lang/rfcs/blob/master/text/1733-trait-alias.md), > [tracking issue](https://github.com/rust-lang/rust/issues/41517)) to support > naming some classes of constraints. **References:** `where` constraints were added in proposal [#818: Constraints for generics (generics details 3)](https://github.com/carbon-language/carbon-lang/pull/818). ### Kinds of `where` constraints There are three kinds of `where` constraints, each of which uses a different binary operator: - _Rewrite constraints_: `where`...`=`... - _Same-type constraints_: `where`...`==`... - _Implements constraints_: `where`...`impls`... And there are two positions that `where` can be written: - At the end of an `impl as` declaration, before the body of the impl. ```carbon impl Class as Interface where .A = i32 { ... } ``` - Inside a type expression. ```carbon fn F[T: Interface where .A impls OtherInterface](t: T) { ... } ``` A rewrite constraint is written `where .A = B`, where `A` is the name of an [associated constant](#associated-constants) which is rewritten to `B`. Any use of `.A` thereafter is the same as using `B`, including direct access to the API of `B`. The "dot followed by the name of a member" construct, like `.A`, is called a _designator_. The name of the designator is looked up in the constraint, and refers to the value of that member for whatever type is to satisfy this constraint. > **Concern:** Using `=` for this use case is not consistent with other `where` > clauses that write a boolean expression that evaluates to `true` when the > constraint is satisfied. A same-type constraint is written `where X == Y`, where `X` and `Y` both name facets. The constraint is that `X as type` must be the same as `Y as type`. It would normally only be used in the type expression position. A same-type constraint does not rewrite the type on the left-hand side to the right-hand side, and they are still treated as distinct types. A value of type `X` [would need to be cast](#satisfying-both-facet-types) to `Y` in order to use the API of `Y`. So for constraint clauses that name a single facet type on the right-hand side, using a rewrite constraint is preferred. Note that switching between the two forms does not change which types satisfies the constraint, and so is a compatible change for callers. An implements constraint is written `where T impls C`, where `T` is a facet and `C` is a facet type. The constraint is that `T` satisfies the requirements of `C`. It would normally only be used in the type expression position. **References:** The definition of rewrite and same-type constraints were in [proposal #2173](https://github.com/carbon-language/carbon-lang/pull/2173). Implements constraints switched to using the `impls` keyword in [proposal #2483](https://github.com/carbon-language/carbon-lang/pull/2483). **Alternatives considered:** - [Different equality constraint operators for symbolic and constants](/proposals/p2173.md#status-quo) - [Single one-step equality constraint operators that merges constraints](/proposals/p2173.md#equal-types-with-different-interfaces) - [Restrict constraints to allow computable type equality](/proposals/p2173.md#restrict-constraints-to-allow-computable-type-equality) - [Find a fully transitive approach to type equality](/proposals/p2173.md#find-a-fully-transitive-approach-to-type-equality) - [Different syntax for rewrite constraint](/proposals/p2173.md#different-syntax-for-rewrite-constraint) - [Different syntax for same-type constraint](/proposals/p2173.md#different-syntax-for-same-type-constraint) - [Required ordering for rewrites](/proposals/p2173.md#required-ordering-for-rewrites) - [Multi-constraint `where` clauses](/proposals/p2173.md#multi-constraint-where-clauses) - [Rewrite constraints in `require` constraints](/proposals/p2173.md#rewrite-constraints-in-impl-as-constraints) #### Recursive constraints We sometimes need to constrain a type to equal one of its associated facets. In this first example, we want to represent the function `Abs` which will return `Self` for some but not all types, so we use an associated facet `MagnitudeType` to encode the return type: ```carbon interface HasAbs { extend Numeric; let MagnitudeType:! Numeric; fn Abs[self: Self]() -> MagnitudeType; } ``` For types representing subsets of the real numbers, such as `i32` or `f32`, the `MagnitudeType` will match `Self`, the type implementing an interface. For types representing complex numbers, the types will be different. For example, the `Abs()` function applied to a `Complex64` value would produce a `f32` result. The goal is to write a constraint to restrict to the first case. In a second example, when you take the slice of a type implementing `Container` you get a type implementing `Container` which may or may not be the same type as the original container type. However, taking the slice of a slice always gives you the same type, and some functions want to only operate on containers whose slice type is the same as the container type. To solve this problem, we think of `Self` as an actual associated facet member of every interface. We can then address it using `.Self` in a `where` clause, like any other associated facet member. ```carbon fn Relu[T:! HasAbs where .MagnitudeType = .Self](x: T) { // T.MagnitudeType == T so the following is allowed: return (x.Abs() + x) / 2; } fn UseContainer[T:! Container where .SliceType = .Self](c: T) -> bool { // T.SliceType == T so `c` and `c.Slice(...)` can be compared: return c == c.Slice(...); } ``` Notice that in an interface definition, `Self` refers to the type implementing this interface while `.Self` refers to the associated facet currently being defined. ```carbon interface Container; constraint SliceConstraint(E:! type, S:! Container); interface Container { let ElementType:! type; let IteratorType:! Iterator where .ElementType = ElementType; // `.Self` means `SliceType`. let SliceType:! Container where .Self impls SliceConstraint(ElementType, .Self); // `Self` means the type implementing `Container`. fn GetSlice[ref self: Self] (start: IteratorType, end: IteratorType) -> SliceType; } constraint SliceConstraint(E:! type, S:! Container) { extend Container where .ElementType = E and .SliceType = S; } ``` Note that [naming](#named-constraint-constants) a recursive constraint using the [`constraint` introducer](#named-constraints) approach, we can name the implementing type using `Self` instead of `.Self`, since they refer to the same type. Note though they are different facets with different facet types: ```carbon constraint RealAbs { extend HasAbs where .MagnitudeType = Self; // Satisfied by the same types: extend HasAbs where .MagnitudeType = .Self; // While `Self as type` is the same as `.Self as type`, // they are different as facets: `Self` has type // `RealAbs` and `.Self` has type `HasAbs`. } constraint ContainerIsSlice { extend Container where .SliceType = Self; // Satisfied by the same types: extend Container where .SliceType = .Self; // `Self` has type `ContainerIsSlice` and // `.Self` has type `Container`. } ``` The `.Self` construct follows these rules: - `X :!` introduces `.Self:! type`, where references to `.Self` are resolved to `X`. This allows you to use `.Self` as an interface parameter as in `X:! I(.Self)`. - `A where` introduces `.Self:! A` and a `.Foo` _designator_ for each member `Foo` of `A`. - It's an error to reference `.Self` if it refers to more than one different thing or isn't a facet. - You get the innermost, most-specific type for `.Self` if it is introduced twice in a scope. By the previous rule, it is only legal if they all refer to the same facet binding. - `.Self` may not be on the left side of the `=` in a rewrite constraint. So in `X:! A where ...`, `.Self` is introduced twice, after the `:!` and the `where`. This is allowed since both times it means `X`. After the `:!`, `.Self` has the type `type`, which gets refined to `A` after the `where`. In contrast, it is an error if `.Self` could mean two different things, as in: ```carbon // ❌ Illegal: `.Self` could mean `T` or `T.A`. fn F[T:! InterfaceA where .A impls (InterfaceB where .B = .Self)](x: T); ``` These two meanings can be disambiguated by defining a [`constraint`](#named-constraints): ```carbon constraint InterfaceBWithSelf { extend InterfaceB where .B = Self; } constraint InterfaceBWith(U:! InterfaceA) { extend InterfaceB where .B = U; } // `T.A impls InterfaceB where .B = T.A` fn F[T:! InterfaceA where .A impls InterfaceBWithSelf](x: T); // `T.A impls InterfaceB where .B = T` fn F[T:! InterfaceA where .A impls InterfaceBWith(.Self)](x: T); ``` #### Rewrite constraints In a rewrite constraint, the left-hand operand of `=` must be a `.` followed by the name of an associated constant. `.Self` is not permitted. ```carbon interface RewriteSelf { // ❌ Error: `.Self` is not the name of an associated constant. let Me:! type where .Self = Self; } interface HasAssoc { let Assoc:! type; } interface RewriteSingleLevel { // ✅ Uses of `A.Assoc` will be rewritten to `i32`. let A:! HasAssoc where .Assoc = i32; } interface RewriteMultiLevel { // ❌ Error: Only one level of associated constant is permitted. let B:! RewriteSingleLevel where .A.Assoc = i32; } ``` This notation is permitted anywhere a constraint can be written, and results in a new constraint with a different interface: the named member effectively no longer names an associated constant of the constrained type, and is instead treated as a rewrite rule that expands to the right-hand side of the constraint, with any mentioned parameters substituted into that type. ```carbon interface Container { let Element:! type; let Slice:! Container where .Element = Element; fn Add[ref self: Self](x: Element); } // `T.Slice.Element` rewritten to `T.Element` // because type of `T.Slice` says `.Element = Element`. // `T.Element` rewritten to `i32` // because type of `T` says `.Element = i32`. fn Add[T:! Container where .Element = i32](p: T*, y: T.Slice.Element) { // ✅ Argument `y` has the same type `i32` as parameter `x` of // `T.(Container.Add)`, which is also rewritten to `i32`. p->Add(y); } ``` Rewrites aren't performed on the left-hand side of such an `=`, so `where .A = .B and .A = C` is not rewritten to `where .A = .B and .B = C`. Instead, such a `where` clause is invalid when the constraint is [resolved](appendix-rewrite-constraints.md#rewrite-constraint-resolution) unless each rule for `.A` specifies the same rewrite. Note that `T:! C where .R = i32` can result in a type `T.R` whose behavior is different from the behavior of `T.R` given `T:! C`. For example, member lookup into `T.R` can find different results and operations can therefore have different behavior. However, this does not violate [coherence](/proposals/p2173.md#coherence) because the facet types `C` and `C where .R = i32` don't differ by merely having more type information; rather, they are different facet types that have an isomorphic set of values, somewhat like `i32` and `u32`. An `=` constraint is not merely learning a new fact about a type, it is requesting different behavior. This approach has some good properties that [same-type constraints](#same-type-constraints) have problems with: - [Equal types with different interfaces](/proposals/p2173.md#equal-types-with-different-interfaces): When an associated facet is constrained to be a concrete type, it is desirable for the associated facet to behave like that concrete type. - [Type canonicalization](/proposals/p2173.md#type-canonicalization): to enable efficient type equality. - [Transitivity of equality of types](/proposals/p2173.md#transitivity-of-equality) The precise rules governing rewrite constraints are described in [an appendix](appendix-rewrite-constraints.md). #### Same-type constraints A same-type constraint describes that two type expressions are known to evaluate to the same value. Unlike a [rewrite constraint](#rewrite-constraints), however, the two type expressions are treated as distinct types when type-checking a symbolic expression that refers to them. Same-type constraints are brought into scope, looked up, and resolved exactly as if there were a `SameAs(U:! type)` interface and a `T == U` impl corresponded to `T is SameAs(U)`, except that `==` is commutative. Further, same-type equalities apply to type components, so that `X(A, B, C)` is `SameType(X(D, E, F))` if we know that `A == D`, `B == E`, and `C == F`. Stated differently, if `F` is any pure type function, `T impls SameAs(U)` implies `F(T) impls SameAs(F(U))`. For example, if we know that `T == i32` then we also have `Vector(T)` is single-step equal to `Vector(i32)`. This relationship is not transitive, though, so it's not possible to ask for a list of types that are the same as a given type, nor to ask whether there exists a type that is the same as a given type and has some property. But it is possible to ask whether two types are (non-transitively) known to be the same. In order for same-type constraints to be useful, they must allow the two types to be treated as actually being the same in some context. This can be accomplished by the use of `==` constraints in an `impl`, such as in the built-in implementation of `ImplicitAs`: ```carbon final impl forall [T:! type, U:! type where .Self == T] T as ImplicitAs(U) { fn Convert[self: Self](other: U) -> U { ... } } ``` > **Alternative considered:** It superficially seems like it would be convenient > if such implementations were made available implicitly –- for example, by > writing `impl forall [T:! type] T as ImplicitAs(T)` -– but in more complex > examples that turns out to be problematic. Consider: > > ```carbon > interface CommonTypeWith(U:! type) { > let Result:! type; > } > final impl forall [T:! type] T as CommonTypeWith(T) where .Result = T {} > > fn F[T:! Potato, U:! Hashable where .Self == T](x: T, y: U) -> auto { > // What is T.CommonTypeWith(U).Result? Is it T or U? > return (if cond then x else y).Hash(); > } > ``` > > With this alternative, `impl` validation for `T as CommonTypeWith(U)` fails: > we cannot pick a common type when given two distinct type expressions, even if > we know they evaluate to the same type, because we would not know which API > the result should have. ##### Implementation of same-type `ImplicitAs` It is possible to implement the above `impl` of `ImplicitAs` directly in Carbon, without a compiler builtin, by taking advantage of the built-in conversion between `C where .A = X` and `C where .A == X`: ```carbon interface EqualConverter { let T:! type; fn Convert(t: T) -> Self; } fn EqualConvert[T:! type](t: T, U:! EqualConverter where .T = T) -> U { return U.Convert(t); } impl forall [U:! type] U as EqualConverter where .T = U { fn Convert(u: U) -> U { return u; } } impl forall [T:! type, U:! type where .Self == T] T as ImplicitAs(U) { fn Convert[self: Self]() -> U { return EqualConvert(self, U); } } ``` The transition from `(T as ImplicitAs(U)).Convert`, where we know that `U == T`, to `EqualConverter.Convert`, where we know that `.T = U`, allows a same-type constraint to be used to perform a rewrite. ##### Manual type equality A same-type constraint establishes [type expressions](terminology.md#type-expression) are equal, and allows implicit conversions between them. However, determining whether two type expressions are _transitively_ equal is in general undecidable, as [has been shown in Swift](https://forums.swift.org/t/swift-type-checking-is-undecidable/39024). Carbon does not combine these equalities between type expressions. This means that if two type expressions are only transitively equal, the user will need to include a sequence of casts or use an [`observe` declaration](#observe-declarations) to convert between them. Given this interface `Transitive` that has associated facets that are constrained to all be equal, with interfaces `P`, `Q`, and `R`: ```carbon interface P { fn InP[self: Self](); } interface Q { fn InQ[self: Self](); } interface R { fn InR[self: Self](); } interface Transitive { let A:! P; let B:! Q where .Self == A; let C:! R where .Self == B; fn GetA[self: Self]() -> A; fn TakesC[self: Self](c: C); } ``` A cast to `B` is needed to call `TakesC` with a value of type `A`, so each step only relies on one equality: ```carbon fn F[T:! Transitive](t: T) { // ✅ Allowed t.TakesC(t.GetA() as T.B); // ✅ Allowed let b: T.B = t.GetA(); t.TakesC(b); // ❌ Not allowed: t.TakesC(t.GetA()); } ``` The compiler may have several different `where` clauses to consider, particularly when an interface has associated facets that recursively satisfy the same interface, or mutual recursion between multiple interfaces. For example, given these `Edge` and `Node` interfaces (similar to those defined in [the section on interfaces with cyclic references](#example-of-declaring-interfaces-with-cyclic-references), but using `==` same-type constraints): ```carbon interface Edge; interface Node; private constraint EdgeFor(NodeT:! Node); private constraint NodeFor(EdgeT:! Edge); interface Edge { let N:! NodeFor(Self); fn GetN[self: Self]() -> N; } interface Node { let E:! EdgeFor(Self); fn GetE[self: Self]() -> E; fn AddE[ref self: Self](e: E); fn NearN[self: Self](n: Self) -> bool; } constraint EdgeFor(NodeT:! Node) { extend Edge where .N == NodeT; } constraint NodeFor(EdgeT:! Edge) { extend Node where .E == EdgeT; } ``` and a function `H` taking a value with some type implementing the `Node` interface, then the following would be legal statements in `H`: ```carbon fn H[N:! Node](n: N) { // ✅ Legal: argument has type `N.E`, matches parameter n.AddE(n.GetE()); // ✅ Legal: // - argument has type `N.E.N` // - `N.E` has type `EdgeFor(Self)` where `Self` // is `N`, which means `Edge where .N == N` // - so we have the constraint `N.E.N == N` // - which means the argument type `N.E.N` // is equal to the parameter type `N` using a // single `==` constraint. n.NearN(n.GetE().GetN()); // ✅ Legal: // - type `N.E.N.E.N` may be cast to `N.E.N` // using a single `where ==` clause, either // `(N.E.N).E.N == (N).E.N` or // `N.E.(N.E.N) == N.E.(N)` // - argument of type `N.E.N` may be passed to // function expecting `N`, using a single // `where ==` clause, as in the previous call. n.NearN(n.GetE().GetN().GetE().GetN() as N.E.N); } ``` That last call would not be legal without the cast, though. **Comparison with other languages:** Other languages such as Swift and Rust instead perform automatic type equality. In practice this means that their compiler can reject some legal programs based on heuristics simply to avoid running for an unbounded length of time. The benefits of the manual approach include: - fast compilation, since the compiler does not need to explore a potentially large set of combinations of equality restrictions, supporting [Carbon's goal of fast and scalable development](/docs/project/goals.md#fast-and-scalable-development); - expressive and predictable semantics, since there are no limitations on how complex a set of constraints can be supported; and - simplicity. The main downsides are: - manual work for the source code author to prove to the compiler that types are equal; and - verbosity. We expect that rich error messages and IDE tooling will be able to suggest changes to the source code when a single equality constraint is not sufficient to show two type expressions are equal, but a more extensive automated search can find a sequence that prove they are equal. ##### Observe declarations Same-type constraints are non-transitive, just like `ImplicitAs`. The developer can use an `observe` declaration to bring a new same-type constraint into scope: ```carbon observe A == B == C; ``` notionally does much the same thing as ```carbon impl A as SameAs(C) { ... } ``` where the `impl` makes use of `A impls SameAs(B)` and `B impls SameAs(C)`. In general, an `observe` declaration lists a sequence of [type expressions](terminology.md#type-expression) that are equal by some same-type `where` constraints. These `observe` declarations may be included in an `interface` definition or a function body, as in: ```carbon interface Edge { let N:! type; } interface Node { let E:! type; } interface Graph { let E:! Edge; let N:! Node where .E == E and E.N == .Self; observe E == N.E == E.N.E == N.E.N.E; // ... } fn H[G: Graph](g: G) { observe G.N == G.E.N == G.N.E.N == G.E.N.E.N; // ... } ``` Every type expression after the first must be equal to some earlier type expression in the sequence by a single `where` equality constraint. In this example, ```carbon fn J[G: Graph](g: G) { observe G.E.N == G.N.E.N == G.N == G.E.N.E.N; // ... } ``` the expression `G.E.N.E.N` is one equality away from `G.N.E.N` and so it is allowed. This is true even though `G.N.E.N` isn't the type expression immediately prior to `G.E.N.E.N`. After an `observe` declaration, all of the listed type expressions are considered equal to each other using a single `where` equality. In this example, the `observe` declaration in the `Transitive` interface definition provides the link between associated facets `A` and `C` that allows function `F` to type check. ```carbon interface P { fn InP[self: Self](); } interface Q { fn InQ[self: Self](); } interface R { fn InR[self: Self](); } interface Transitive { let A:! P; let B:! Q where .Self == A; let C:! R where .Self == B; fn GetA[self: Self]() -> A; fn TakesC[self: Self](c: C); // Without this `observe` declaration, the // calls in `F` below would not be allowed. observe A == B == C; } fn F[T:! Transitive](t: T) { var a: T.A = t.GetA(); // ✅ Allowed: `T.A` values implicitly convert to // `T.C` using `observe` in interface definition. t.TakesC(a); // ✅ Allowed: `T.C` extends and implements `R`. (a as T.C).InR(); } ``` Only the current type is searched for interface implementations, so the call to `InR()` would be illegal without the cast. However, an [`observe`...`==`...`impls` declaration](#observing-equal-to-a-type-implementing-an-interface) can be used to identify interfaces that must be implemented through some equal type. This does not [extend](terminology.md#extending-an-impl) the API of the type, that is solely determined by the definition of the type. Continuing the previous example: ```carbon fn TakesPQR[U:! P & Q & R](u: U); fn G[T:! Transitive](t: T) { var a: T.A = t.GetA(); // ✅ Allowed: `T.A` implements `P` and // includes its API, as if it extends `P`. a.InP(); // ❌ Illegal: only the current type is // searched for interface implementations. a.(Q.InQ)(); // ✅ Allowed: values of type `T.A` may be cast // to `T.B`, which extends and implements `Q`. (a as T.B).InQ(); // ✅ Allowed: `T.A` == `T.B` that implements `Q`. observe T.A == T.B impls Q; a.(Q.InQ)(); // ❌ Illegal: `T.A` still does not extend `Q`. a.InQ(); // ✅ Allowed: `T.A` implements `P`, // `T.A` == `T.B` that implements `Q` (observe above), // and `T.A` == `T.C` that implements `R`. observe T.A == T.C impls R; TakesPQR(a); } ``` Since adding an `observe`...`impls` declaration only adds non-extending implementations of interfaces to symbolic facets, they may be added without breaking existing code. #### Implements constraints An _implements constraint_ is written `where T impls C`, and expresses that the facet `T` must implement the requirements of facet type `C`. This is more flexible than using [`&` to add a constraint](#combining-interfaces-by-anding-facet-types) since it can be applied to [associated facet](#associated-facets) members as well. In the following example, normally the `ElementType` of a `Container` can be any type. The `SortContainer` function, however, takes a pointer to a type satisfying `Container` with the additional constraint that its `ElementType` must satisfy the `Ordered` interface, using an `impls` constraint: ```carbon interface Container { let ElementType:! type; ... } fn SortContainer [ContainerType:! Container where .ElementType impls Ordered] (container_to_sort: ContainerType*); ``` In contrast to a [rewrite constraint](#rewrite-constraints) or a [same-type constraint](#same-type-constraints), this does not say what type `ElementType` exactly is, just that it must satisfy the requirements of some facet type. The specific case of a clause of the form `where .AssociatedFacet impls AConstraint`, where the constraint is applied to a direct associated facet member of the facet type being constrained (similar to the restriction on [rewrite constraints](#rewrite-constraints)), gets special treatment. In this case, the type of the associated facet is [combined](#combining-interfaces-by-anding-facet-types) with the constraint. In the above example, `Container` defines `ElementType` as having type `type`, but `ContainerType.ElementType` has type `type & Ordered` (which is equivalent to `Ordered`). This is because `ContainerType` has type `Container where .ElementType impls Ordered`, not `Container`. This means we need to be a bit careful when talking about the type of `ContainerType` when there is a `where` clause modifying it. > **Future work:** We may want to use a different operator in this case, such as > `&=`, in place of `impls`, to reflect the change in the type. This is > analogous to rewrite constraints using `=` instead of `==` to visibly reflect > the different impact on the type. An implements constraint can be applied to [`.Self`](#recursive-constraints), as in `I where .Self impls C`. This has the same requirements as `I & C`, but that `where` clause does not affect the API. This means that a [symbolic facet binding](#symbolic-facet-bindings) with that facet type, so `T` in `T:! I where .Self impls C`, is represented by an [archetype](terminology.md#archetype) that implements both `I` and `C`, but only [extends](terminology.md#extending-an-impl) `I`. ##### Implied constraints Imagine we have a checked-generic function that accepts an arbitrary [`HashMap` parameterized type](#parameterized-types): ```carbon fn LookUp[KeyT:! type](hm: HashMap(KeyT, i32)*, k: KeyT) -> i32; fn PrintValueOrDefault[KeyT:! Printable, ValueT:! Printable & HasDefault] (map: HashMap(KeyT, ValueT), key: KeyT); ``` The `KeyT` in these declarations does not visibly satisfy the requirements of `HashMap`, which requires the type implement `Hashable` and other interfaces: ```carbon class HashMap( KeyT:! Hashable & Eq & Movable, ...) { ... } ``` In this case, `KeyT` gets `Hashable` and so on as _implied constraints_. Effectively that means that these functions are automatically rewritten to add a `where .Self impls` constraint on `KeyT`: ```carbon fn LookUp[ KeyT:! type where .Self impls Hashable & Eq & Movable] (hm: HashMap(KeyT, i32)*, k: KeyT) -> i32; fn PrintValueOrDefault[ KeyT:! Printable where .Self impls Hashable & Eq & Movable, ValueT:! Printable & HasDefault] (map: HashMap(KeyT, ValueT), key: KeyT); ``` In this case, Carbon will accept the definition and infer the needed constraints on the symbolic facet parameter. This is both more concise for the author of the code and follows the ["don't repeat yourself" principle](https://en.wikipedia.org/wiki/Don%27t_repeat_yourself). This redundancy is undesirable since it means if the needed constraints for `HashMap` are changed, then the code has to be updated in more locations. Further it can add noise that obscures relevant information. In practice, any user of these functions will have to pass in a valid `HashMap` instance, and so will have already satisfied these constraints. This implied constraint is equivalent to the explicit constraint that each parameter and return type [is legal](#must-be-legal-type-argument-constraints). > **Note:** These implied constraints affect the _requirements_ of a symbolic > facet parameter, but not its _member names_. This way you can always look at > the declaration to see how name resolution works, without having to look up > the definitions of everything it is used as an argument to. **Limitation:** To limit readability concerns and ambiguity, this feature is limited to a single signature. Consider this interface declaration: ```carbon interface GraphNode { let Edge:! type; fn EdgesFrom[self: Self]() -> HashSet(Edge); } ``` One approach would be to say the use of `HashSet(Edge)` in the signature of the `EdgesFrom` function would imply that `Edge` satisfies the requirements of an argument to `HashSet`, such as being `Hashable`. Another approach would be to say that the `EdgesFrom` would only be conditionally available when `Edge` does satisfy the constraints on `HashSet` arguments. Instead, Carbon will reject this definition, requiring the user to include all the constraints required for the other declarations in the interface in the declaration of the `Edge` associated facet. Similarly, a parameter to a class must be declared with all the constraints needed to declare the members of the class that depend on that parameter. **Comparison with other languages:** Both Swift ([1](https://www.swiftbysundell.com/tips/inferred-generic-type-constraints/), [2](https://github.com/apple/swift/blob/main/docs/archive/Generics.rst#constraint-inference)) and [Rust](https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=0b2d645bd205f24a7a6e2330d652c32e) support some form of this feature as part of their type inference (and [the Rust community is considering expanding support](http://smallcultfollowing.com/babysteps//blog/2022/04/12/implied-bounds-and-perfect-derive/#expanded-implied-bounds)). #### Combining constraints Constraints can be combined by separating constraint clauses with the `and` keyword. This example expresses a constraint that two associated facets are equal and satisfy an interface: ```carbon fn EqualContainers [CT1:! Container, CT2:! Container where .ElementType impls HasEquality and .ElementType = CT1.ElementType] (c1: CT1*, c2: CT2*) -> bool; ``` **Comparison with other languages:** Swift and Rust use commas `,` to separate constraint clauses, but that only works because they place the `where` in a different position in a declaration. In Carbon, the `where` is attached to a type in a parameter list that is already using commas to separate parameters. ### Satisfying both facet types If the two facet bindings being constrained to be equal, using either a [rewrite constraint](#rewrite-constraints) or a [same-type constraint](#same-type-constraints), have been declared with different facet types, then the actual type value they are set to will have to satisfy the requirements of both facet types. For example, if `SortedContainer.ElementType` is declared to have a `Ordered` requirement, then in these declarations: ```carbon // With `=` rewrite constraint: fn Contains_Rewrite [SC:! SortedContainer, CT:! Container where .ElementType = SC.ElementType] (haystack: SC, needles: CT) -> bool; // With `==` same-type constraint: fn Contains_SameType [SC:! SortedContainer, CT:! Container where .ElementType == SC.ElementType] (haystack: SC, needles: CT) -> bool; ``` the `where` constraints in both cases mean `CT.ElementType` must satisfy `Ordered` as well. However, the behavior inside the body of these two inside the body of the two functions is different. In `Contains_Rewrite`, `CT.ElementType` is rewritten to `SC.ElementType` and uses the facet type of `SC.ElementType`. In `Contains_SameType`, the `where` clause does not affect the API of `CT.ElementType`, and it would not even be considered to implement `Ordered` unless there is some declaration like `observe CT.ElementType == SC.ElementType impls Ordered`. Even then, the items from the `needles` container won't directly have a `Compare` method member. The rule is that a same-type `where` constraint between two type variables does not modify the set of member names of either type. This is in contrast to rewrite constraints like `where .ElementType = String` with a `=`, then `.ElementType` is actually set to `String` including the complete `String` API. Note that `==` constraints are symmetric, so the previous declaration of `Contains_SameType` is equivalent to an alternative declaration where `CT` is declared first and the `where` clause is attached to `SortedContainer`: ```carbon fn Contains_SameType_Equivalent [CT:! Container, SC:! SortedContainer where .ElementType == CT.ElementType] (haystack: SC, needles: CT) -> bool; ``` ### Constraints must use a designator We don't allow a `where` constraint unless it applies a restriction to the current type. This means referring to some [designator](#kinds-of-where-constraints), like `.MemberName`, or [`.Self`](#recursive-constraints). Examples: - `Container where .ElementType = i32` - `type where Vector(.Self) impls Sortable` - `Addable where i32 impls AddableWith(.Result)` Constraints that only refer to other types should be moved to the type that is declared last. So: ```carbon // ❌ Error: `where A == B` does not use `.Self` or a designator fn F[A:! type, B:! type, C:! type where A == B](a: A, b: B, c: C); ``` must be replaced by: ```carbon // ✅ Allowed fn F[A:! type, B:! type where A == .Self, C:! type](a: A, b: B, c: C); ``` This includes `where` clauses used in an `impl` declaration: ```carbon // ❌ Error: `where T impls B` does not use `.Self` or a designator impl forall [T:! type] T as A where T impls B {} // ✅ Allowed impl forall [T:! type where .Self impls B] T as A {} // ✅ Allowed impl forall [T:! B] T as A {} ``` This clarifies the meaning of the `where` clause and reduces the number of redundant ways to express a restriction, following the [one-way principle](/docs/project/principles/one_way.md). **Alternative considered:** This rule was added in proposal [#2376](https://github.com/carbon-language/carbon-lang/pull/2376), which [considered whether this rule should be added](/proposals/p2376.md#alternatives-considered). ### Referencing names in the interface being defined The constraint in a `where` clause is required to only reference earlier names from this scope, as in this example: ```carbon // ❌ Illegal: `E` references `V` declared later. interface Graph { let E: Edge where .V = V; let V: Vert where .E = E; } // ✅ Allowed: Only references to earlier names. interface Graph { let E: Edge; let V: Vert where .E = E and .Self == E.V; } ``` ### Constraint examples and use cases - **Set [associated constant](#associated-constants) to a constant:** For example in `NSpacePoint where .N = 2`, the associated constant `N` of `NSpacePoint` must be `2`. This syntax is also used to specify the values of associated constants when implementing an interface for a type, as in `impl MyPoint as NSpacePoint where .N = 2 {`...`}`. - **Set an [associated facet](#associated-facets) to a specific value:** Associated facets are treated like any other associated constant. So `Stack where .ElementType = i32` is a facet type that restricts to types that implement the `Stack` interface with integer elements, as in: ```carbon fn SumIntStack[T:! Stack where .ElementType = i32] (s: T*) -> i32 { var sum: i32 = 0; while (!s->IsEmpty()) { // s->Pop() returns a value of type // `T.ElementType` which is `i32`: sum += s->Pop(); } return sum; } ``` Note that this is a case that can use an `==` same-type constraint instead of an `=` rewrite constraint. - **One [associated constant](#associated-constants) must equal another:** For example with this definition of the interface `PointCloud`: ```carbon interface PointCloud { let Dim:! i32; let PointT:! NSpacePoint where .N = Dim; } ``` an implementation of `PointCloud` for a type `T` will have `T.PointT.N == T.Dim`. - **Equal facet bindings:** For example, we could make the `ElementType` of an `Iterator` interface equal to the `ElementType` of a `Container` interface as follows: ```carbon interface Iterator { let ElementType:! type; ... } interface Container { let ElementType:! type; let IteratorType:! Iterator where .ElementType = ElementType; ... } ``` In a function signature, this may be done by referencing an earlier parameter: ```carbon fn Map[CT:! Container, FT:! Function where .InputType = CT.ElementType] (c: CT, f: FT) -> Vector(FT.OutputType); ``` In that example, `FT.InputType` is constrained to equal `CT.InputType`. Given an interface with two associated facets ```carbon interface PairInterface { let Left:! type; let Right:! type; } ``` we can constrain them to be equal using `PairInterface where .Left = .Right`. Note that this is a case that can use an `==` same-type constraint instead of an `=` rewrite constraint. - **[Associated facet](#associated-facets) implements interface:** Given these definitions (omitting `ElementType` for brevity): ```carbon interface IteratorInterface { ... } interface ContainerInterface { let IteratorType:! IteratorInterface; // ... } interface RandomAccessIterator { extend IteratorInterface; // ... } ``` We can then define a function that only accepts types that implement `ContainerInterface` where its `IteratorType` associated facet implements `RandomAccessIterator`: ```carbon fn F[ContainerType:! ContainerInterface where .IteratorType impls RandomAccessIterator] (c: ContainerType); ``` #### Parameterized type implements interface There are times when a function will pass a [symbolic facet parameter](#symbolic-facet-bindings) of the function as an argument to a [parameterized type](#parameterized-types), and the function needs the result to implement a specific interface. ```carbon // A parameterized type class DynArray(T:! type) { ... } interface Printable { fn Print[self: Self](); } // The parameterized type `DynArray` implements interface // `Printable` only for some arguments. impl DynArray(String) as Printable { ... } // Constraint: `T` such that `DynArray(T)` implements `Printable` fn PrintThree [T:! type where DynArray(.Self) impls Printable] (a: T, b: T, c: T) { // Create a `DynArray(T)` of size 3. var v: auto = DynArray(T).Make(a, b, c); // Known to be implemented due to the constraint on `T`. v.(Printable.Print)(); } // ✅ Allowed: `DynArray(String)` implements `Printable`. let s: String = "Ai "; PrintThree(s, s, s); // ❌ Forbidden: `DynArray(i32)` doesn't implement `Printable`. let i: i32 = 3; PrintThree(i, i, i); ``` **Comparison with other languages:** This use case was part of the [Rust rationale for adding support for `where` clauses](https://rust-lang.github.io/rfcs/0135-where.html#motivation). #### Another type implements parameterized interface In this case, we need some other type to implement an interface parameterized by a [symbolic facet parameter](#symbolic-facet-bindings). The syntax for this case follows the previous case, except now the `.Self` parameter is on the interface to the right of the `impls`. For example, we might need a type parameter `T` to support explicit conversion from an `i32`: ```carbon interface As(T:! type) { fn Convert[self: Self]() -> T; } fn Double[T:! Mul where i32 impls As(.Self)](x: T) -> T { return x * ((2 as i32) as T); } ``` #### Must be legal type argument constraints Now consider the case that the symbolic facet parameter is going to be used as an argument to a [parameterized type](#parameterized-types) in a function body, but not in the signature. If the parameterized type was explicitly mentioned in the signature, the [implied constraint](#implied-constraints) feature would ensure all of its requirements were met. To say a parameterized type is allowed to be passed a specific argument, just write that it `impls type`, which all types do. This is a trivial case of a [parameterized type implements interface](#parameterized-type-implements-interface) `where` constraint. For example, a function that adds its parameters to a `HashSet` to deduplicate them, needs them to be `Hashable` and so on. To say "`T` is a type where `HashSet(T)` is legal," we can write: ```carbon fn NumDistinct[T:! type where HashSet(.Self) impls type] (a: T, b: T, c: T) -> i32 { var set: HashSet(T); set.Add(a); set.Add(b); set.Add(c); return set.Size(); } ``` This has the same advantages over repeating the constraints on `HashSet` arguments in the type of `T` as other [implied constraints](#implied-constraints). ### Named constraint constants A facet type with a `where` constraint, such as `C where `, can be named two different ways: - Using `let template` as in: ```carbon let template NameOfConstraint:! auto = C where ; ``` or, since the type of a facet type is `type`: ```carbon let template NameOfConstraint:! type = C where ; ``` - Using a [named constraint](#named-constraints) with the `constraint` keyword introducer: ```carbon constraint NameOfConstraint { extend C where ; } ``` Whichever approach is used, the result is `NameOfConstraint` is a compile-time constant that is equivalent to `C where `. ## Other constraints as facet types There are some constraints that Carbon naturally represents as named facet types. These can either be used directly to constrain a facet binding, or in a `where ... impls ...` [implements constraint](#implements-constraints) to constrain an associated facet. The compiler determines which types implement these interfaces, developers are not permitted to explicitly implement these interfaces for their own types. These facet types extend the requirements that facet types are allowed to include beyond [interfaces implemented](#facet-types) and [`where` clauses](#where-constraints). **Open question:** Are these names part of the prelude or in a standard library? ### Is a derived class Given a type `T`, `Extends(T)` is a facet type whose values are facets that are (transitively) [derived from](/docs/design/classes.md#inheritance) `T`. That is, `U:! Extends(T)` means `U` has an `extend base: T;` declaration, or there is a chain of `extend base` declarations connecting `T` to `U`. ```carbon base class BaseType { ... } fn F[T:! Extends(BaseType)](p: T*); fn UpCast[U:! type] (p: U*, V:! type where U impls Extends(.Self)) -> V*; fn DownCast[X:! type](p: X*, Y:! Extends(X)) -> Y*; class DerivedType { extend base: BaseType; } var d: DerivedType = {}; // `T` is set to `DerivedType` // `DerivedType impls Extends(BaseType)` F(&d); // `U` is set to `DerivedType` let p: BaseType* = UpCast(&d, BaseType); // `X` is set to `BaseType` // `Y` is set to facet `DerivedType as Extends(BaseType)`. Assert(DownCast(p, DerivedType) == &d); ``` **Open question:** Alternatively, we could define a new `extends` operator for use in `where` clauses: ```carbon fn F[T:! type where .Self extends BaseType](p: T*); fn UpCast[T:! type](p: T*, U:! type where T extends .Self) -> U*; fn DownCast[T:! type](p: T*, U:! type where .Self extends T) -> U*; ``` **Comparison to other languages:** In Swift, you can [add a required superclass to a type bound using `&`](https://docs.swift.org/swift-book/LanguageGuide/Protocols.html#ID282). ### Type compatible with another type Given a type `U`, define the facet type `CompatibleWith(U)` as follows: > `CompatibleWith(U)` is a facet type whose values are facets `T` such that > `T as type` and `U as type` are > [compatible types](terminology.md#compatible-types). That is values of `T` and > `U` as types can be cast back and forth without any change in representation > (for example `T` is an [adapter](#adapting-types) for `U`). `CompatibleWith` determines an equivalence relationship between types. Specifically, given two types `T1` and `T2`, they are equivalent if `T1 impls CompatibleWith(T2)`, which is true if and only if `T2 impls CompatibleWith(T1)`. **Note:** Just like interface parameters, we require the user to supply `U`, it may not be deduced. Specifically, this code would be illegal: ```carbon fn Illegal[U:! type, T:! CompatibleWith(U)](x: T*) ... ``` In general there would be multiple choices for `U` given a specific `T` here, and no good way of picking one. However, similar code is allowed if there is another way of determining `U`: ```carbon fn Allowed[U:! type, T:! CompatibleWith(U)](x: U*, y: T*) ... ``` #### Same implementation restriction In some cases, we need to restrict to types that implement certain interfaces the same way as the type `U`. > The values of facet type `CompatibleWith(U, C)` are facets satisfying > `CompatibleWith(U)` that have the same implementation of `C` as `U`. For example, if we have a type `HashSet(T)`: ```carbon class HashSet(T:! Hashable) { ... } ``` Then `HashSet(T)` may be cast to `HashSet(U)` if `T impls CompatibleWith(U, Hashable)`. The one-parameter interpretation of `CompatibleWith(U)` is recovered by letting the default for the second parameter (`C`) be `type`. #### Example: Multiple implementations of the same interface This allows us to represent functions that accept multiple implementations of the same interface for a type. ```carbon choice CompareResult { Less, Equal, Greater } interface Ordered { fn Compare[self: Self](rhs: Self) -> CompareResult; } fn CombinedLess[T:! type](a: T, b: T, U:! CompatibleWith(T) & Ordered, V:! CompatibleWith(T) & Ordered) -> bool { match ((a as U).Compare(b as U)) { case .Less => { return True; } case .Greater => { return False; } case .Equal => { return (a as V).Compare(b as V) == CompareResult.Less; } } } ``` Used as: ```carbon class Song { ... } class SongByArtist { adapt Song; impl as Ordered { ... } } class SongByTitle { adapt Song; impl as Ordered { ... } } let s1: Song = ...; let s2: Song = ...; assert(CombinedLess(s1, s2, SongByArtist, SongByTitle) == True); ``` > **Open question:** We might generalize this to a list of implementations using > variadics: > > ```carbon > fn CombinedCompare[T:! type] > (a: T, b: T, ... each CompareT:! CompatibleWith(T) & Ordered) > -> CompareResult { > ... block { > let result: CompareResult = > (a as each CompareT).Compare(b as each CompareT); > if (result != CompareResult.Equal) { > return result; > } > } > return CompareResult.Equal; > } > > assert(CombinedCompare(s1, s2, SongByArtist, SongByTitle) > == CompareResult.Less); > ``` > > However, [variadic support](#variadic-arguments) is still future work. #### Example: Creating an impl out of other implementations And then to package this functionality as an implementation of `Ordered`, we combine `CompatibleWith` with [type adaptation](#adapting-types) and [variadics](#variadic-arguments): ```carbon class ThenCompare( T:! type, ... each CompareT:! CompatibleWith(T) & Ordered) { adapt T; extend impl as Ordered { fn Compare[self: Self](rhs: Self) -> CompareResult { ... block { let result: CompareResult = (self as each CompareT).Compare(rhs as each CompareT); if (result != CompareResult.Equal) { return result; } } return CompareResult.Equal; } } } let template SongByArtistThenTitle:! auto = ThenCompare(Song, SongByArtist, SongByTitle); var s1: Song = ...; var s2: SongByArtistThenTitle = ({ ... } as Song) as SongByArtistThenTitle; assert((s1 as SongByArtistThenTitle).Compare(s2) == CompareResult.Less); ``` ### Sized types and facet types What is the size of a type? - It could be fully known and fixed at compile time -- this is true of primitive types (`i32`, `f64`, and so on), most [classes](/docs/design/classes.md), and most other concrete types. - It could be known symbolically. This means that it will be known at codegen time, but not at type-checking time. - It could be dynamic. For example, it could be a [dynamic type](#runtime-type-fields), a slice, variable-sized type (such as [found in Rust](https://doc.rust-lang.org/nomicon/exotic-sizes.html#dynamically-sized-types-dsts)), or you could dereference a pointer to a base class that could actually point to a [derived class](/docs/design/classes.md#inheritance). - It could be unknown which category the type is in. In practice this will be essentially equivalent to having dynamic size. A type is called _sized_ if it is in the first two categories, and _unsized_ otherwise. Note: something with size 0 is still considered "sized". The facet type `Sized` is defined as follows: > `Sized` is a type whose values are types `T` that are "sized" -- that is the > size of `T` is known, though possibly only symbolically Knowing a type is sized is a precondition to declaring variables of that type, taking values of that type as parameters, returning values of that type, and defining arrays of that type. Users will not typically need to express the `Sized` constraint explicitly, though, since it will usually be a dependency of some other constraint the type will need such as `Movable` or `Concrete`. Example: ```carbon // In the Carbon standard library interface DefaultConstructible { // Types must be sized to be default constructible. require impls Sized; fn Default() -> Self; } // Classes are "sized" by default. class Name { extend impl as DefaultConstructible { fn Default() -> Self { ... } } ... } fn F[T:! type](x: T*) { // T is unsized. // ✅ Allowed: may access unsized values through a pointer. var y: T* = x; // ❌ Illegal: T is unsized. var z: T; } // T is sized, but its size is only known symbolically. fn G[T: DefaultConstructible](x: T*) { // ✅ Allowed: T is default constructible, which means sized. var y: T = T.Default(); } var z: Name = Name.Default();; // ✅ Allowed: `Name` is sized and implements `DefaultConstructible`. G(&z); ``` **Open question:** Should the `Sized` facet type expose an associated constant with the size? So you could say `T.ByteSize` in the above example to get a symbolic integer value with the size of `T`. Similarly you might say `T.ByteStride` to get the number of bytes used for each element of an array of `T`. ### Destructor constraints There are four facet types related to [the destructors of types](/docs/design/classes.md#destructors): - `Concrete` types may be local or member variables. - `Deletable` types may be safely deallocated by pointer using the `Delete` method on the `Allocator` used to allocate it. - `Destructible` types have a destructor and may be deallocated by pointer using the `UnsafeDelete` method on the correct `Allocator`, but it may be unsafe. The concerning case is deleting a pointer to a derived class through a pointer to its base class without a virtual destructor. - `TrivialDestructor` types have empty destructors. This facet type may be used with [specialization](#lookup-resolution-and-specialization) to unlock specific optimizations. **Note:** The names `Deletable` and `Destructible` are [**placeholders**](/proposals/p1154.md#type-of-type-naming) since they do not conform to the decision on [question-for-leads issue #1058: "How should interfaces for core functionality be named?"](https://github.com/carbon-language/carbon-lang/issues/1058). The facet types `Concrete`, `Deletable`, and `TrivialDestructor` all extend `Destructible`. Combinations of them may be formed using [the `&` operator](#combining-interfaces-by-anding-facet-types). For example, a checked-generic function that both instantiates and deletes values of a type `T` would require `T` implement `Concrete & Deletable`. Types are forbidden from explicitly implementing these facet types directly. Instead they use [`destructor` declarations in their class definition](/docs/design/classes.md#destructors) and the compiler uses them to determine which of these facet types are implemented. ## Compile-time `let` A `let` statement inside a function body may be used to get the change in type behavior of calling a checked-generic function without having to introduce a function call. ```carbon fn SymbolicLet(...) { ... let T:! C = U; X; Y; Z; } ``` This introduces a symbolic constant `T` with type `C` and value `U`. This implicitly includes an [`observe T == U;` declaration](#observe-declarations), when `T` and `U` are facets, which allows values to implicitly convert from the concrete type `U` to the erased type `T`, as in: ```carbon let x: i32 = 7; let T:! Add = i32; // ✅ Allowed to convert `i32` values to `T`. let y: T = x; ``` > **TODO:** The implied `observe` declaration is from question-for-leads issue > [#996](https://github.com/carbon-language/carbon-lang/issues/996) and should > be approved in a proposal. This makes the `SymbolicLet` function roughly equivalent to: ```carbon fn SymbolicLet(...) { ... fn Closure(T:! C where .Self == U) { X; Y; Z; } Closure(U); } ``` The `where .Self == U` modifier captures the `observe` declaration introduced by the `let` (at the cost of changing the type of `T`). A symbolic `let` can be used to switch to the API of `C` when `U` does not extend `C`, as an alternative to [using an adapter](#use-case-accessing-interface-names), or to simplify inlining of a generic function while preserving semantics. To get a template binding instead of symbolic binding, add the `template` keyword before the binding pattern, as in: ```carbon fn TemplateLet(...) { ... let template T:! C = U; X; Y; Z; } ``` which introduces a template constant `T` with type `C` and value `U`. This is roughly equivalent to: ```carbon fn TemplateLet(...) { ... fn Closure(template T:! C) { X; Y; Z; } Closure(U); } ``` In this case, the `where .Self == U` modifier is superfluous. > **References:** > > - Proposal > [#950: Generics details 6: remove facets #950](https://github.com/carbon-language/carbon-lang/pull/950) > - Question-for-leads issue > [#996: Generic `let` with `auto`?](https://github.com/carbon-language/carbon-lang/issues/996) ## Parameterized impl declarations There are cases where an `impl` definition should apply to more than a single type and interface combination. The solution is to parameterize the `impl` definition, so it applies to a family of types, interfaces, or both. This includes: - Defining an `impl` that applies to multiple arguments to a [parameterized type](#parameterized-types). - _Conditional conformance_ where a parameterized type implements some interface if the parameter to the type satisfies some criteria, like implementing the same interface. - _Blanket_ `impl` declarations where an interface is implemented for all types that implement another interface, or some other criteria beyond being a specific type. - _Wildcard_ `impl` declarations where a family of interfaces are implemented for single type. The syntax for an out-of-line parameterized `impl` declaration is: > `impl forall [`__`]` __ `as` > _ [_ `where` _ ]_ `;` This may also be called a _generic `impl` declaration_. ### Impl for a parameterized type Interfaces may be implemented for a [parameterized type](#parameterized-types). This can be done lexically in the class's scope: ```carbon class Vector(T:! type) { impl as Iterable where .ElementType = T { ... } } ``` This is equivalent to naming the implementing type between `impl` and `as`, though this form is not allowed after `extend`: ```carbon class Vector(T:! type) { impl Vector(T) as Iterable where .ElementType = T { ... } } ``` An out-of-line `impl` declaration must declare all parameters in a `forall` clause: ```carbon impl forall [T:! type] Vector(T) as Iterable where .ElementType = T { ... } ``` The parameter for the type can be used as an argument to the interface being implemented, with or without `extend`: ```carbon class HashMap(KeyT:! Hashable, ValueT:! type) { extend impl as Has(KeyT) { ... } impl as Contains(HashSet(KeyT)) { ... } } ``` or out-of-line the same `forall` parameter can be passed to both: ```carbon class HashMap(KeyT:! Hashable, ValueT:! type) { ... } impl forall [KeyT:! Hashable, ValueT:! type] HashMap(KeyT, ValueT) as Has(KeyT) { ... } impl forall [KeyT:! Hashable, ValueT:! type] HashMap(KeyT, ValueT) as Contains(HashSet(KeyT)) { ... } ``` ### Conditional conformance [Conditional conformance](terminology.md#conditional-conformance) is expressing that we have an `impl` of some interface for some type, but only if some additional type restrictions are met. Examples where this would be useful include being able to say that a container type, like `Vector`, implements some interface when its element type satisfies the same interface: - A container is printable if its elements are. - A container could be compared to another container with the same element type using a [lexicographic comparison](https://en.wikipedia.org/wiki/Lexicographic_order) if the element type is comparable. - A container is copyable if its elements are. This may be done by specifying a more specific implementing type to the left of the `as` in the declaration: ```carbon interface Printable { fn Print[self: Self](); } class Vector(T:! type) { ... } // By saying "T:! Printable" instead of "T:! type" here, // we constrain `T` to be `Printable` for this impl. impl forall [T:! Printable] Vector(T) as Printable { fn Print[self: Self]() { for (let a: T in self) { // Can call `Print` on `a` since the constraint // on `T` ensures it implements `Printable`. a.Print(); } } } ``` Note that no `forall` clause or type may be specified when declaring an `impl` with the [`extend`](#extend-impl) keyword: ```carbon class Array(T:! type, template N:! i64) { // ❌ Illegal: nothing allowed before `as` after `extend impl`: extend impl forall [P:! Printable] Array(P, N) as Printable { ... } } ``` **Note:** This was changed in [proposal #2760](https://github.com/carbon-language/carbon-lang/pull/2760). Instead, the class can declare aliases to members of the interface. Those aliases will only be usable when the type implements the interface. ```carbon class Array(T:! type, template N:! i64) { alias Print = Printable.Print; } impl forall [P:! Printable] Array(P, N) as Printable { ... } impl String as Printable { ... } var can_print: Array(String, 2) = ("Hello ", "world"); // ✅ Allowed: `can_print.Print` resolves to // `can_print.(Printable.Print)`, which exists as long as // `Array(String, 2) impls Printable`, which exists since // `String impls Printable`. can_print.Print(); var no_print: Array(Unprintable, 2) = ...; // ❌ Illegal: `no_print.Print` resolves to // `no_print.(Printable.Print)`, but there is no // implementation of `Printable` for `Array(Unprintable, 2)` // as long as `Unprintable` doesn't implement `Printable`. no_print.Print(); ``` It is legal to declare or define a conditional impl lexically inside the class scope without `extend`, as in: ```carbon class Array(T:! type, template N:! i64) { // ✅ Allowed: non-extending impl defined in class scope may // use `forall` and may specify a type. impl forall [P:! Printable] Array(P, N) as Printable { ... } } ``` Inside the scope of this `impl` definition, both `P` and `T` refer to the same type, but `P` has the facet type of `Printable` and so has a `Print` member. The relationship between `T` and `P` is as if there was a [`where P == T` clause](#same-type-constraints). **Open question:** Need to resolve whether the `T` name can be reused, or if we require that you need to use new names, like `P`, when creating new type variables. **Example:** Consider a type with two parameters, like `Pair(T, U)`. In this example, the interface `Foo(T)` is only implemented when the two types are equal. ```carbon interface Foo(T:! type) { ... } class Pair(T:! type, U:! type) { ... } impl forall [T:! type] Pair(T, T) as Foo(T) { ... } ``` As before, you may also define the `impl` inline, but it may not be combined with the `extend` keyword: ```carbon class Pair(T:! type, U:! type) { impl Pair(T, T) as Foo(T) { ... } } ``` **Clarification:** The same interface may be implemented multiple times as long as there is no overlap in the conditions: ```carbon class X(T:! type) { // ✅ Allowed: `X(T).F` consistently means `X(T).(Foo.F)` // even though that may have different definitions for // different values of `T`. alias F = Foo.F; } impl X(i32) as Foo { fn F[self: Self]() { DoOneThing(); } } impl X(i64) as Foo { fn F[self: Self]() { DoADifferentThing(); } } ``` This allows a type to express that it implements an interface for a list of types, possibly with different implementations. However, in general, `X(T).F` can only mean one thing, regardless of `T`. **Comparison with other languages:** [Swift supports conditional conformance](https://github.com/apple/swift-evolution/blob/master/proposals/0143-conditional-conformances.md), but bans cases where there could be ambiguity from overlap. [Rust also supports conditional conformance](https://doc.rust-lang.org/rust-by-example/generics/where.html). ### Blanket impl declarations A _blanket impl declaration_ is an `impl` declaration that could apply to more than one root type, so the `impl` declaration will use a type variable for the `Self` type. Here are some examples where blanket impl declarations arise: - Any type implementing `Ordered` should get an implementation of `PartiallyOrdered`. ```carbon impl forall [T:! Ordered] T as PartiallyOrdered { ... } ``` - `T` implements `CommonType(T)` for all `T` ```carbon impl forall [T:! type] T as CommonType(T) where .Result = T { } ``` This means that every type is the common type with itself. Blanket impl declarations may never be declared using [`extend`](#extend-impl) and must always be defined lexically [out-of-line](#out-of-line-impl). #### Difference between a blanket impl and a named constraint A blanket impl declaration can be used to say "any type implementing `interface I` also implements `interface B`." Compare this with defining a `constraint C` that requires `I`. In that case, `C` will also be implemented any time `I` is. There are differences though: - There can be other implementations of `interface B` without a corresponding implementation of `I`, unless `B` has a requirement on `I`. However, the types implementing `C` will be the same as the types implementing `I`. - More specialized implementations of `B` can override the blanket implementation. ### Wildcard impl declarations A _wildcard impl declaration_ is an `impl` declaration that defines how a family of interfaces are implemented for a single `Self` type. For example, the `BigInt` type might implement `AddTo(T)` for all `T` that implement `ImplicitAs(i32)`. The implementation would first convert `T` to `i32` and then add the `i32` to the `BigInt` value. ```carbon class BigInt { impl forall [T:! ImplicitAs(i32)] as AddTo(T) { ... } } // Or out-of-line: impl forall [T:! ImplicitAs(i32)] BigInt as AddTo(T) { ... } ``` Wildcard impl declarations may never be declared using [`extend`](#extend-impl), to avoid having the names in the interface defined for the type multiple times. ### Combinations The different kinds of parameters to an `impl` declarations may be combined. For example, if `T` implements `As(U)`, then this implements `As(Optional(U))` for `Optional(T)`: ```carbon impl forall [U:! type, T:! As(U)] Optional(T) as As(Optional(U)) { ... } ``` This has a wildcard parameter `U`, and a condition on parameter `T`. ### Lookup resolution and specialization As much as possible, we want rules for where an `impl` is allowed to be defined and for selecting which `impl` definition to use that achieve these three goals: - Implementations have coherence, as [defined in terminology](terminology.md#coherence). This is [a goal for Carbon](goals.md#coherence). More detail can be found in [this appendix with the rationale and alternatives considered](appendix-coherence.md). - Libraries will work together as long as they pass their separate checks. - A checked-generic function can assume that some `impl` definition will be successfully selected if it can see an `impl` declaration that applies, even though another more specific `impl` definition may be selected. For this to work, we need a rule that picks a single `impl` definition in the case where there are multiple `impl` definitions that match a particular type and interface combination. This is called _specialization_ when the rule is that most specific implementation is chosen, for some definition of "specific." #### Type structure of an impl declaration Given an impl declaration, find the type structure by deleting deduced parameters and replacing type parameters by a `?`. The type structure of this declaration: ```carbon impl forall [T:! ..., U:! ...] Foo(T, i32) as Bar(String, U) { ... } ``` is: ```carbon impl Foo(?, i32) as Bar(String, ?) ``` To get a uniform representation across different `impl` definitions, before type parameters are replaced the declarations are normalized as follows: - For `impl` declarations that are lexically inline in a class definition, the type is added between the `impl` and `as` keywords if the type is left out. - Pointer types `T*` are replaced with `Ptr(T)`. - The `extend` keyword is removed, if present. - The `forall` clause introducing type parameters is removed, if present. - Any `where` clauses that are setting associated constants or types are removed. The type structure will always contain a single interface name, which is the name of the interface being implemented, and some number of type names. Type names can be in the `Self` type to the left of the `as` keyword, or as parameters to other types or the interface. These names must always be defined either in the current library or be publicly defined in some library this library depends on. #### Orphan rule To achieve [coherence](terminology.md#coherence), we need to ensure that any given impl can only be defined in a library that must be imported for it to apply. Specifically, given a specific type and specific interface, `impl` declarations that can match can only be in libraries that must have been imported to name that type or interface. This is achieved with the _orphan rule_. **Orphan rule:** Some name from the type structure of an `impl` declaration must be defined in the same library as the `impl`, that is some name must be _local_. Let's say you have some interface `I(T, U(V))` being implemented for some type `A(B(C(D), E))`. To satisfy the orphan rule for coherence, that `impl` must be defined in some library that must be imported in any code that looks up whether that interface is implemented for that type. This requires that `impl` is defined in the same library that defines the interface or one of the names needed by the type. That is, the `impl` must be defined with one of `I`, `T`, `U`, `V`, `A`, `B`, `C`, `D`, or `E`. We further require anything looking up this `impl` to import the _definitions_ of all of those names. Seeing a forward declaration of these names is insufficient, since you can presumably see forward declarations without seeing an `impl` with the definition. This accomplishes a few goals: - The compiler can check that there is only one definition of any `impl` that is actually used, avoiding [One Definition Rule (ODR)](https://en.wikipedia.org/wiki/One_Definition_Rule) problems. - Every attempt to use an `impl` will see the exact same `impl` definition, making the interpretation and semantics of code consistent no matter its context, in accordance with the [low context-sensitivity principle](/docs/project/principles/low_context_sensitivity.md). - Allowing the `impl` to be defined with either the interface or the type partially addresses the [expression problem](https://eli.thegreenplace.net/2016/the-expression-problem-and-its-solutions). Note that [the rules for specialization](#lookup-resolution-and-specialization) do allow there to be more than one `impl` to be defined for a type, by unambiguously picking one as most specific. > **References:** Implementation coherence is > [defined in terminology](terminology.md#coherence), and is > [a goal for Carbon generics](goals.md#coherence). More detail can be found in > [this appendix with the rationale and alternatives considered](appendix-coherence.md). Only the implementing interface and types (self type and type parameters) in the type structure are relevant here; an interface mentioned in a constraint is not sufficient since it [need not be imported](/proposals/p0920.md#orphan-rule-could-consider-interface-requirements-in-blanket-impls). Since Carbon in addition requires there be no cyclic library dependencies, we conclude that there is at most one library that can contain `impl` definitions with a particular type structure. #### Overlap rule Given a specific concrete type, say `Foo(bool, i32)`, and an interface, say `Bar(String, f32)`, the overlap rule picks, among all the matching `impl` declarations, which type structure is considered "most specific" to use as the implementation of that type for that interface. Given two different type structures of impl declarations matching a query, for example: ```carbon impl Foo(?, i32) as Bar(String, ?) impl Foo(?, ?) as Bar(String, f32) ``` We pick the type structure with a non-`?` at the first difference as most specific. Here we see a difference between `Foo(?, i32)` and `Foo(?, ?)`, so we select the one with `Foo(?, i32)`, ignoring the fact that it has another `?` later in its type structure This rule corresponds to a depth-first traversal of the type tree to identify the first difference, and then picking the most specific choice at that difference. #### Prioritization rule > **TODO:** Document the changes to prioritization adopted in > [p5337: Interface extension and `final impl` update](/proposals/p5337.md). Since at most one library can contain `impl` definitions with a given type structure, all `impl` definitions with a given type structure must be in the same library. Furthermore by the [`impl` declaration access rules](#access), they will be defined in the API file for the library if they could match any query from outside the library. If there is more than one `impl` with that type structure, they must be [defined](#implementing-interfaces) or [declared](#declaring-implementations) together in a prioritization block. Once a type structure is selected for a query, the first `impl` declaration in the prioritization block that matches is selected. > **Open question:** How are prioritization blocks written? A block starts with > a keyword like `match_first` or `impl_priority` and then a sequence of impl > declarations inside matching curly braces `{` ... `}`. > > ```carbon > match_first { > // If T is Foo prioritized ahead of T is Bar > impl forall [T:! Foo] T as Bar { ... } > impl forall [T:! Baz] T as Bar { ... } > } > ``` To increase expressivity, Carbon allows prioritization blocks to contain a mix of type structures, which is resolved using this rule: > The compiler first picks the `impl` declaration with the type structure most > favored for the query, and then picks the highest priority (earliest) matching > `impl` declaration in the same prioritization block. > **Alternatives considered:** We considered two other options: > > - "Intersection rule:" Prioritization blocks implicitly define all non-empty > intersections of contained `impl` declarations, which are then selected by > their type structure. > - "Same type structure rule:" All the `impl` declarations in a > prioritization block are required to have the same type structure, at a > cost in expressivity. This option was not chosen since it wouldn't support > the different type structures created by the > [`like` operator](#like-operator-for-implicit-conversions). > > To see the difference from the first option, consider two libraries with type > structures as follows: > > - Library B has `impl (A, ?, ?, D) as I` and `impl (?, B, ?, D) as I` in the > same prioritization block. > - Library C has `impl (A, ?, C, ?) as I`. > > For the query `(A, B, C, D) as I`, using the intersection rule, library B is > considered to have the intersection impl with type structure > `impl (A, B, ?, D) as I` which is the most specific. If we instead just > considered the rules mentioned explicitly, then `impl (A, ?, C, ?) as I` from > library C is the most specific. The advantage of the implicit intersection > rule is that if library B is changed to add an impl with type structure > `impl (A, B, ?, D) as I`, it won't shift which library is serving that query. > Ultimately we decided that it was too surprising to prioritize based on the > implicit intersection of `impl` declarations, rather than something explicitly > written in the code. > > We chose between these alternatives in > [the open discussion on 2023-07-18](https://docs.google.com/document/d/1gnJBTfY81fZYvI_QXjwKk1uQHYBNHGqRLI2BS_cYYNQ/edit?resourcekey=0-ql1Q1WvTcDvhycf8LbA9DQ#heading=h.7jxges9ojgy3). > **TODO:** This decision needs to be approved in a proposal. #### Acyclic rule A cycle is when a query, such as "does type `T` implement interface `I`?", considers an `impl` declaration that might match, and whether that `impl` declaration matches is ultimately dependent on whether that query is true. These are cycles in the graph of (type, interface) pairs where there is an edge from pair A to pair B if whether type A implements interface A determines whether type B implements interface B. The test for whether something forms a cycle needs to be precise enough, and not erase too much information when considering this graph, that these `impl` declarations are not considered to form cycles with themselves: ```carbon impl forall [T:! Printable] Optional(T) as Printable; impl forall [T:! type, U:! ComparableTo(T)] U as ComparableTo(Optional(T)); ``` **Example:** If `T` implements `ComparableWith(U)`, then `U` should implement `ComparableWith(T)`. ```carbon impl forall [U:! type, T:! ComparableWith(U)] U as ComparableWith(T); ``` This is a cycle where which types implement `ComparableWith` determines which types implement the same interface. **Example:** Cycles can create situations where there are multiple ways of selecting `impl` declarations that are inconsistent with each other. Consider an interface with two blanket `impl` declarations: ```carbon class Y {} class N {} interface True {} impl Y as True {} interface Z(T:! type) { let Cond:! type; } match_first { impl forall [T:! type, U:! Z(T) where .Cond impls True] T as Z(U) where .Cond = N { } impl forall [T:! type, U:! type] T as Z(U) where .Cond = Y { } } ``` What is `i8.(Z(i16).Cond)`? It depends on which of the two blanket impl declarations are selected. - An implementation of `Z(i16)` for `i8` could come from the first blanket impl with `T == i8` and `U == i16` if `i16 impls Z(i8)` and `(i16 as Z(i8)).Cond == Y`. This condition is satisfied if `i16` implements `Z(i8)` using the second blanket impl. In this case, `(i8 as Z(i16)).Cond == N`. - Equally well `Z(i8)` could be implemented for `i16` using the first blanket impl and `Z(i16)` for `i8` using the second. In this case, `(i8 as Z(i16)).Cond == Y`. There is no reason to prefer one of these outcomes over the other. **Example:** Further, cycles can create contradictions in the type system: ```carbon class A {} class B {} class C {} interface D(T:! type) { let Cond:! type; } match_first { impl forall [T:! type, U:! D(T) where .Cond = B] T as D(U) where .Cond = C { } impl forall [T:! type, U:! D(T) where .Cond = A] T as D(U) where .Cond = B { } impl forall [T:! type, U:! type] T as D(U) where .Cond = A { } } ``` What is `(i8 as D(i16)).Cond`? The answer is determined by which blanket impl is selected to implement `D(i16)` for `i8`: - If the third blanket impl is selected, then `(i8 as D(i16)).Cond == A`. This implies that `(i16 as D(i8)).Cond == B` using the second blanket impl. If that is true, though, then our first impl choice was incorrect, since the first blanket impl applies and is higher priority. So `(i8 as D(i16)).Cond == C`. But that means that `i16 as D(i8)` can't use the second blanket impl. - For the second blanket impl to be selected, so `(i8 as D(i16)).Cond == B`, `(i16 as D(i8)).Cond` would have to be `A`. This happens when `i16` implements `D(i8)` using the third blanket impl. However, `(i8 as D(i16)).Cond == B` means that there is a higher priority implementation of `D(i8).Cond` for `i16`. In either case, we arrive at a contradiction. The workaround for this problem is to either split an interface in the cycle in two, with a blanket implementation of one from the other, or move some of the criteria into a [named constraint](#named-constraints). **Concern:** Cycles could be spread out across libraries with no dependencies between them. This means there can be problems created by a library that are only detected by its users. **Open question:** Should Carbon reject cycles in the absence of a query? The two options here are: - Combining `impl` declarations gives you an immediate error if there exists queries using them that have cycles. - Only when a query reveals a cyclic dependency is an error reported. **Open question:** In the second case, should we ignore cycles if they don't affect the result of the query? For example, the cycle might be among implementations that are lower priority. #### Termination rule It is possible to have a set of `impl` declarations where there isn't a cycle, but the graph is infinite. Without some rule to prevent exhaustive exploration of the graph, determining whether a type implements an interface could run forever. **Example:** It could be that `A` implements `B`, so `A impls B` if `Optional(A) impls B`, if `Optional(Optional(A)) impls B`, and so on. This could be the result of a single impl: ```carbon impl forall [A:! type where Optional(.Self) impls B] A as B { ... } ``` This problem can also result from a chain of `impl` declarations, as in `A impls B` if `A* impls C`, if `Optional(A) impls B`, and so on. Determining whether a particular set of `impl` declarations terminates is equivalent to the halting problem (content warning: contains many instances of an obscene word as part of a programming language name [1](https://sdleffler.github.io/RustTypeSystemTuringComplete/), [2](https://forums.swift.org/t/two-more-undecidable-problems-in-the-swift-type-system/64814)), and so is undecidable in general. Carbon adopts an approximation that guarantees termination, but may mistakenly report an error when the query would terminate if left to run long enough. The hope is that this criteria is accurate on code that occurs in practice. Rule: the types in the `impl` query must never get strictly more complicated when considering the same `impl` declaration again. The way we measure the complexity of a set of types is by counting how many of each base type appears. A base type is the name of a type without its parameters. For example, the base types in this query `Pair(Optional(i32), bool) impls AddWith(Optional(i32))` are: - `Pair` - `Optional` twice - `i32` twice - `bool` - `AddWith` A query is strictly more complicated if at least one count increases, and no count decreases. So `Optional(Optional(i32))` is strictly more complicated than `Optional(i32)` but not strictly more complicated than `Optional(bool)`. This rule, when combined with [the acyclic rule](#acyclic-rule) that a query can't repeat exactly, [guarantees termination](/proposals/p2687.md#proof-of-termination). Consider the example from before, ```carbon impl forall [A:! type where Optional(.Self) impls B] A as B; ``` This `impl` declaration matches the query `i32 impls B` as long as `Optional(i32) impls B`. That is a strictly more complicated query, though, since it contains all the base types of the starting query (`i32` and `B`), plus one more (`Optional`). As a result, an error can be given after one step, rather than after hitting a large recursion limit. And that error can state explicitly what went wrong: we went from a query with no `Optional` to one with one, without anything else decreasing. Note this only triggers a failure when the same `impl` declaration is considered with the strictly more complicated query. For example, if the declaration is not considered since there is a more specialized `impl` declaration that is preferred by the [type-structure overlap rule](#overlap-rule), as in: ``` impl forall [A:! type where Optional(.Self) impls B] A as B; impl Optional(bool) as B; // OK, because we never consider the first `impl` // declaration when looking for `Optional(bool) impls I`. let U:! B = bool; // Error: cycle with `i32 impls B` depending on // `Optional(i32) impls B`, using the same `impl` // declaration, as before. let V:! B = i32; ``` > **Note:** > [Issue #2880](https://github.com/carbon-language/carbon-lang/issues/2880) is a > tracking bug for known issues with this "strictly more complex" rule for > `impl` termination. We are using that issue to track any code that arises in > practice that would terminate but is rejected by this rule. > **Comparison with other languages:** Rust solves this problem by imposing a > recursion limit, much like C++ compilers use to terminate template recursion. > This goes against > [Carbon's goal of predictability in generics](goals.md#predictability), > because of the concern that increasing the number of steps needed to resolve > an `impl` query could cause far away code to hit the recursion limit. > > Carbon's approach is robust in the face of refactoring: > > - It does not depend on the specifics of how an `impl` declaration is > parameterized, only on the query. > - It does not depend on the length of the chain of queries. > - It does not depend on a measure of type-expression complexity, like depth. > > Carbon's approach also results in identifying the minimal steps in the loop, > which makes error messages as short and understandable as possible. > **Alternatives considered:** > > - [Recursion limit](/proposals/p2687.md#problem) > - [Measure complexity using type tree depth](/proposals/p2687.md#measure-complexity-using-type-tree-depth) > - [Consider each type parameter in an `impl` declaration separately](/proposals/p2687.md#consider-each-type-parameter-in-an-impl-declaration-separately) > - [Consider types in the interface being implemented as distinct](/proposals/p2687.md#consider-types-in-the-interface-being-implemented-as-distinct) > - [Require some count to decrease](/proposals/p2687.md#require-some-count-to-decrease) > - [Require non-type values to stay the same](/proposals/p2687.md#require-non-type-values-to-stay-the-same) > **References:** This algorithm is from proposal > [#2687: Termination algorithm for impl selection](https://github.com/carbon-language/carbon-lang/pull/2687), > replacing the recursion limit originally proposed in > [#920: Generic parameterized impls (details 5)](https://github.com/carbon-language/carbon-lang/pull/920) > before we came up with this algorithm. ##### Non-facet arguments For non-facet arguments we have to expand beyond base types to consider other kinds of keys. These other keys are in a separate namespace from base types. - Values with an integral type use the name of the type as the key and the absolute value as a count. This means integer arguments are considered more complicated if they increase in absolute value. For example, if the values `2` and `-3` are used as arguments to parameters with type `i32`, then the `i32` key will have count `5`. - Every option of a choice type is its own key, counting how many times a value using that option occurs. Any parameters to the option are recorded as separate keys. For example, the `Optional(i32)` value of `.Some(7)` is recorded as keys `.Some` (with a count of `1`) and `i32` (with a count of `7`). - Yet another namespace of keys is used to track counts of variadic arguments, under the base type. This is to defend against having a variadic type `V` that takes any number of `i32` arguments, with an infinite set of distinct instantiations: `V(0)`, `V(0, 0)`, `V(0, 0, 0)`, ... - A `tuple` key in this namespace is used to track the total number of components of tuple values. The values of those elements will be tracked using their own keys. Non-facet argument values not covered by these cases are deleted from the query entirely for purposes of the termination algorithm. This requires that two queries that only differ by non-facet arguments are considered identical and therefore are rejected by the acyclic rule. Otherwise, we could construct an infinite family of non-facet argument values that could be used to avoid termination. ### `final` impl declarations There are cases where knowing that a parameterized impl won't be specialized is particularly valuable. This could let the compiler know the return type of a call to a generic function, such as using an operator: ```carbon // Interface defining the behavior of the prefix-* operator interface Deref { let Result:! type; fn Op[self: Self]() -> Result; } // Types implementing `Deref` class Ptr(T:! type) { ... impl as Deref where .Result = T { fn Op[self: Self]() -> Result { ... } } } class Optional(T:! type) { ... impl as Deref where .Result = T { fn Op[self: Self]() -> Result { ... } } } fn F[T:! type](x: T) { // uses Ptr(T) and Optional(T) in implementation } ``` The concern is the possibility of specializing `Optional(T) as Deref` or `Ptr(T) as Deref` for a more specific `T` means that the compiler can't assume anything about the return type of `Deref.Op` calls. This means `F` would in practice have to add a constraint, which is both verbose and exposes what should be implementation details: ```carbon fn F[T:! type where Optional(T).(Deref.Result) == .Self and Ptr(T).(Deref.Result) == .Self](x: T) { // uses Ptr(T) and Optional(T) in implementation } ``` To mark an impl as not able to be specialized, prefix it with the keyword `final`: ```carbon class Ptr(T:! type) { ... // Note: added `final` final impl as Deref where .Result = T { fn Op[self: Self]() -> Result { ... } } } class Optional(T:! type) { ... // Note: added `final` final impl as Deref where .Result = T { fn Op[self: Self]() -> Result { ... } } } // ❌ Illegal: impl Ptr(i32) as Deref { ... } // ❌ Illegal: impl Optional(i32) as Deref { ... } ``` > **TODO:** Update the following passage to reflect the relaxed overlap rule > adopted in > [p5337: Interface extension and `final impl` update](/proposals/p5337.md). This prevents any higher-priority impl that overlaps a final impl from being defined unless it agrees with the `final` impl on the overlap. Overlap is computed between two non-`template` `impl` declaration by [unifying]() the corresponding parts. For example, the intersection of these two declarations ```carbon final impl forall [T:! type] T as CommonTypeWith(T) where .Result = T {} impl forall [V:! type, U:! CommonTypeWith(V)] Vec(U) as CommonTypeWith(Vec(V)) where .Result = Vec(U.Result) {} ``` is found by unifying `T` with `Vec(U)` and `CommonTypeWith(T)` with `CommonTypeWith(Vec(V))`. In this case, the intersection is when `T == Vec(U)` and `U == V`. For templated `impl` declarations, overlap and agreement is delayed until the template is instantiated with concrete types. Since we do not require the compiler to compare the definitions of functions, agreement is only possible for interfaces without any function members. If the Carbon compiler sees a matching `final` impl, it can assume it won't be specialized so it can use the assignments of the associated constants in that impl definition. ```carbon fn F[T:! type](x: T) { var p: Ptr(T) = ...; // *p has type `T` var o: Optional(T) = ...; // *o has type `T` } ``` > **Alternatives considered:** > > - [Allow interfaces with member functions to compare equal](/proposals/p2868.md#allow-interfaces-with-member-functions-to-compare-equal) > - Mark associated constants as `final` instead of an `impl` declaration, in > proposals > [#983](/proposals/p0983.md#final-associated-constants-instead-of-final-impls) > and > [#2868](/proposals/p2868.md#mark-associated-constants-as-final-instead-of-an-impl-declaration) > - [Prioritize a `final impl` over a more specific `impl` on the overlap](/proposals/p2868.md#prioritize-a-final-impl-over-a-more-specific-impl-on-the-overlap) #### Libraries that can contain a `final` impl To prevent the possibility of two unrelated libraries defining conflicting `impl` declarations, Carbon restricts which libraries may declare an impl as `final` to only: - the library declaring the impl's interface and - the library declaring the root of the `Self` type. This means: - A blanket impl with type structure `impl ? as MyInterface(...)` may only be defined in the same library as `MyInterface`. - An impl with type structure `impl MyType(...) as MyInterface(...)` may be defined in the library with `MyType` or `MyInterface`. These restrictions ensure that the Carbon compiler can locally check that no higher-priority impl is defined superseding a `final` impl. - An impl with type structure `impl MyType(...) as MyInterface(...)` defined in the library with `MyType` must import the library defining `MyInterface`, and so will be able to see any final blanket impl declarations. - A blanket impl with type structure `impl ? as MyInterface(...ParameterType(...)...)` may be defined in the library with `ParameterType`, but that library must import the library defining `MyInterface`, and so will be able to see any `final` blanket impl declarations that might overlap. A final impl with type structure `impl MyType(...) as MyInterface(...)` would be given priority over any overlapping blanket impl defined in the `ParameterType` library. - An impl with type structure `impl MyType(...ParameterType(...)...) as MyInterface(...)` may be defined in the library with `ParameterType`, but that library must import the libraries defining `MyType` and `MyInterface`, and so will be able to see any `final` `impl` declarations that might overlap. ### Comparison to Rust Rust has been designing a specialization feature, but it has not been completed. Luckily, Rust team members have done a lot of blogging during their design process, so Carbon can benefit from the work they have done. However, getting specialization to work for Rust is complicated by the need to maintain compatibility with existing Rust code. This motivates a number of Rust rules where Carbon can be simpler. As a result there are both similarities and differences between the Carbon design and Rust plans: - A Rust `impl` defaults to not being able to be specialized, with a `default` keyword used to opt-in to allowing specialization, reflecting the existing code base developed without specialization. Carbon `impl` declarations default to allowing specialization, with restrictions on which may be declared `final`. - Since a Rust impl is not specializable by default, generic functions can assume that if a matching blanket impl declaration is found, the associated constants from that impl will be used. In Carbon, if a checked-generic function requires an associated constant to have a particular value, the function commonly will need to state that using an explicit constraint. - Carbon will not have the "fundamental" attribute used by Rust on types or traits, as described in [Rust RFC 1023: "Rebalancing Coherence"](https://rust-lang.github.io/rfcs/1023-rebalancing-coherence.html). - Carbon will not use "covering" rules, as described in [Rust RFC 2451: "Re-Rebalancing Coherence"](https://rust-lang.github.io/rfcs/2451-re-rebalancing-coherence.html) and [Little Orphan Impls: The covered rule](http://smallcultfollowing.com/babysteps/blog/2015/01/14/little-orphan-impls/#the-covered-rule). - Like Rust, Carbon does use ordering, favoring the `Self` type and then the parameters to the interface in left-to-right order, see [Rust RFC 1023: "Rebalancing Coherence"](https://rust-lang.github.io/rfcs/1023-rebalancing-coherence.html) and [Little Orphan Impls: The ordered rule](http://smallcultfollowing.com/babysteps/blog/2015/01/14/little-orphan-impls/#the-ordered-rule), but the specifics are different. - Carbon is not planning to support any inheritance of implementation between `impl` definitions. This is more important to Rust since Rust does not support class inheritance for implementation reuse. Rust has considered multiple approaches here, see [Aaron Turon: "Specialize to Reuse"](http://aturon.github.io/tech/2015/09/18/reuse/) and [Supporting blanket impls in specialization](http://smallcultfollowing.com/babysteps/blog/2016/10/24/supporting-blanket-impls-in-specialization/). - [Supporting blanket impls in specialization](http://smallcultfollowing.com/babysteps/blog/2016/10/24/supporting-blanket-impls-in-specialization/) proposes a specialization rule for Rust that considers type structure before other constraints, as in Carbon, though the details differ. - Rust has more orphan restrictions to avoid there being cases where it is ambiguous which impl should be selected. Carbon instead has picked a total ordering on type structures, picking one as higher priority even without one being more specific in the sense of only applying to a subset of types. ## Forward declarations and cyclic references > **TODO:** Update this section to distinguish between _defined_ and _complete_, > as adopted in > [p5087: Qualified lookup into types being defined](/proposals/p5087.md). Interfaces, named constraints, and their implementations may be forward declared and then later defined. This is needed to allow cyclic references, for example when declaring the edges and nodes of a graph. It is also a tool that may be used to make code more readable. The [interface](#interfaces), [named constraint](#named-constraints), and [implementation](#implementing-interfaces) sections describe the syntax for their _definition_, which consists of a declaration followed by a body contained in curly braces `{` ... `}`. A _forward declaration_ is a declaration followed by a semicolon `;`. A forward declaration is a promise that the entity being declared will be defined later. Between the first declaration of an entity, which may be in a forward declaration or the first part of a definition, and the end of the definition the interface or implementation is called _incomplete_. There are additional restrictions on how the name of an incomplete entity may be used. ### Declaring interfaces and named constraints > **TODO:** Update this section to reflect the additional things you can do with > a defined but incomplete type, as adoped in > [p5087: Qualified lookup into types being defined](/proposals/p5087.md). The declaration for an interface or named constraint consists of: - an optional access-control keyword like `private`, - the keyword introducer `interface`, `constraint`, or `template constraint`, - the name of the interface or constraint, and - the parameter list, if any. The name of an interface or constraint can not be used until its first declaration is complete. In particular, it is illegal to use the name of the interface in its parameter list. There is a [workaround](#interfaces-with-parameters-constrained-by-the-same-interface) for the use cases when this would come up. An expression forming a constraint, such as `C & D`, is incomplete if any of the interfaces or constraints used in the expression are incomplete. An interface or named constraint may be forward declared subject to these rules: - The definition must be in the same file as the declaration. - Only the first declaration may have an access-control keyword. - An incomplete interface or named constraint may be used as constraints in declarations of types, functions, interfaces, or named constraints. This includes a `require` declaration inside an interface or named constraint, but excludes specifying the values for associated constants because that would involve name lookup into the incomplete constraint. - An attempt to define the body of a generic function using an incomplete interface or named constraint in its signature is illegal. - An attempt to call a generic function using an incomplete interface or named constraint in its signature is illegal. - Any name lookup into an incomplete interface or named constraint is an error. For example, it is illegal to attempt to access a member of an interface using `MyInterface.MemberName` or constrain a member using a [`where` clause](#where-constraints). If `C` is the name of an incomplete interface or named constraint, then it can be used in the following contexts: - ✅ `T:! C` - ✅ `C & D` - There may be conflicts between `C` and `D` making this invalid that will only be discovered once they are both complete. - ✅ `interface `...` { require` ... `impls C; }` or `constraint `...` { require` ... `impls C; }` - Nothing implied by implementing `C` will be visible until `C` is complete. - ✅ `T:! C` ... `T impls C` - ✅ `T:! A & C` ... `T impls C` - This includes constructs requiring `T impls C` such as `T as C` or `U:! C = T`. - ✅ `impl `...` as C;` - Checking that all associated constants of `C` are correctly assigned values will be delayed until `C` is complete. An incomplete `C` cannot be used in the following contexts: - ❌ `T:! C` ... `T.X` - ❌ `T:! C where `... - ❌ `class `...` { extend impl as C; }` - The names of `C` are added to the class, and so those names need to be known. - ❌ `T:! C` ... `T impls A` where `A` is an interface or named constraint different from `C` - Need to see the definition of `C` to see if it implies `A`. - ❌ `impl` ... `as C {` ... `}` > **Future work:** It is currently undecided whether an interface needs to be > complete to be extended, as in: > > ```carbon > interface I { extend C; } > ``` > > There are three different approaches being considered: > > - If we detect name collisions between the members of the interface `I` and > `C` when the interface `I` is defined, then we need `C` to be complete. > - If we instead only generate errors on ambiguous use of members with the > same name, as we do with `A & B`, then we don't need to require `C` to be > complete. > - Another option, being discussed in > [#2745](https://github.com/carbon-language/carbon-lang/issues/2745), is > that names in interface `I` shadow the names in any interface being > extended, then `C` would not be required to be complete. ### Declaring implementations > **TODO:** Update this section to reflect the new rules adopted in > [p5168: Forward `impl` declaration of an incomplete interface](/proposals/p5168.md). The declaration of an interface implementation consists of: - optional modifier keyword `final`, - the keyword introducer `impl`, - an optional `forall` followed by a deduced parameter list in square brackets `[`...`]`, - an optional type, including an optional [argument list](#parameterized-types), - the keyword `as`, and - a [facet type](#facet-types), including an optional [argument list](#parameterized-interfaces) and [`where` clause](#where-constraints) assigning [associated constants](#associated-constants) including [associated facets](#associated-facets). > **TODO:** Document the redeclaration syntax `impl C.(as I)` adopted in > [p5366: The name of an `impl` in `class` scope](/proposals/p5366.md). **Note:** The type before the `as` is required except in class scope, where it defaults to `Self` as described in the [matching and agreeing section](#matching-and-agreeing). **Note:** The `extend` keyword, when present, is not part of the `impl` declaration. It precedes the `impl` declaration in class scope. When the `extend` keyword is present, the `forall` and type clauses before the `as` keyword must be omitted. An implementation of an interface for a type may be forward declared, subject to these rules: - The definition must be in the same library as the declaration. They must either be in the same file, or the declaration can be in the API file and the definition in an impl file. **Future work:** Carbon may require [parameterized `impl` definitions](#parameterized-impl-declarations) to be in the API file, to support separate compilation. - If there is both a forward declaration and a definition, only the first declaration must specify the assignment of associated constants with a `where` clause. Later declarations may omit the `where` clause by writing `where _` instead. - You can't forward declare an implementation of an incomplete interface. This allows the assignment of associated constants in the `impl` declaration to be verified with the declaration. An `impl` forward declaration may be for any declared type, whether it is incomplete or defined. - Every [extending implementation](#extend-impl) must be declared (or defined) inside the scope of the class definition. It may also be declared before the class definition or defined afterwards. Note that the class itself is incomplete in the scope of the class definition, but member function bodies defined inline are processed [as if they appeared immediately after the end of the outermost enclosing class](/docs/project/principles/information_accumulation.md#exceptions). - For [coherence](terminology.md#coherence), we require that any `impl` declaration that matches an impl lookup query in the same file, must be declared before the query. This can be done with a definition or a forward declaration. This matches the [information accumulation principle](/docs/project/principles/information_accumulation.md). ### Matching and agreeing > **TODO:** Update this section to reflect the new terminology and rules adopted > in [p3763: Matching redeclarations](/proposals/p3763.md), and the new rules > adopted in > [p5168: Forward `impl` declaration of an incomplete interface](/proposals/p5168.md). Carbon needs to determine if two declarations match in order to say which definition a forward declaration corresponds to and to verify that nothing is defined twice. Declarations that match must also agree, meaning they are consistent with each other. Interface and named constraint declarations match if their names are the same after name and alias resolution. To agree: - The introducer keyword or keywords much be the same. - The types and order of parameters in the parameter list, if any, must match. The parameter names may be omitted, but if they are included in both declarations, they must match. - Types agree if they correspond to the same expression tree, after name and alias resolution and canonicalization of parentheses. Note that no other evaluation of expressions is performed. Interface implementation declarations match if the type and interface expressions match along with [the `forall` clause](#parameterized-impl-declarations), if any: - If the type part is omitted, it is rewritten to `Self` in the context of the declaration. - `Self` is rewritten to its meaning in the scope it is used. In a class scope, this should match the type name and [optional parameter expression](#parameterized-types) after `class`. So in `class MyClass { ... }`, `Self` is rewritten to `MyClass`. In `class Vector(T:! Movable) { ... }`, `Self` is rewritten to `forall [T:! Movable] Vector(T)`. - Types match if they have the same name after name and alias resolution and the same parameters, or are the same type parameter. - Interfaces match if they have the same name after name and alias resolution and the same parameters. Note that a named constraint that is equivalent to an interface, as in `constraint Equivalent { extend MyInterface; }`, is not considered to match. > **TODO:** Document the matching rules for the redeclaration syntax > `impl C.(as I)` adopted in > [p5366: The name of an `impl` in `class` scope](/proposals/p5366.md). For implementations to agree: - The presence of the modifier keyword `final` before `impl` must match between a forward declaration and definition. - If either declaration includes a `where` clause, they must both include one. If neither uses `where _`, they must match in that they produce the associated constants with the same values considered separately. ### Declaration examples ```carbon // Forward declaration of interfaces interface Interface1; interface Interface2; interface Interface3; interface Interface4; interface Interface5; interface Interface6; // Forward declaration of class type class MyClass; // ❌ Illegal: Can't declare implementation of incomplete // interface. // impl MyClass as Interface1; // Definition of interfaces that were previously declared interface Interface1 { let T1:! type; } interface Interface2 { let T2:! type; } interface Interface3 { let T3:! type; } interface Interface4 { let T4:! type; } // Out-of-line forward declarations impl MyClass as Interface1 where .T1 = i32; impl MyClass as Interface2 where .T2 = bool; impl MyClass as Interface3 where .T3 = f32; impl MyClass as Interface4 where .T4 = String; interface Interface5 { let T5:! type; } interface Interface6 { let T6:! type; } // Definition of the previously declared class type class MyClass { // Inline definition of previously declared impl. // Note: no need to repeat assignments to associated // constants. impl as Interface1 where _ { } // Inline extending definition of previously declared // impl. // Note: `extend` only appears on the declaration in // class scope // Note: allowed even though `MyClass` is incomplete. // Note: allowed but not required to repeat `where` // clause. extend impl as Interface3 where .T3 = f32 { } // Extending redeclaration of previously declared // impl. Every extending implementation must be // declared in the class definition. extend impl as Interface4 where _; // Inline forward declaration of implementation. impl MyClass as Interface5 where .T5 = u64; // or: impl as Interface5 where .T5 = u64; // Forward declaration of extending implementation. extend impl as Interface6 where .T6 = u8; // *Not*: // extend impl MyClass as Interface6 where .T6 = u8; // No optional type after `extend impl`, it must be // followed immediately by `as` } // It would be legal to move the following definitions // from the API file to the implementation file for // this library. // Definitions of previously declared implementations. impl MyClass as Interface2 where _ { } impl MyClass as Interface5 where _ { } // Definition of previously declared extending // implementations. impl MyClass as Interface4 where _ { } impl MyClass as Interface6 where _ { } ``` ### Example of declaring interfaces with cyclic references In this example, `Node` has an `EdgeT` associated facet that is constrained to implement `Edge`, and `Edge` has a `NodeT` associated facet that is constrained to implement `Node`. Furthermore, the `NodeT` of an `EdgeT` is the original type, and the other way around. This is accomplished by naming and then forward declaring the constraints that can't be stated directly: ```carbon // Forward declare interfaces used in // parameter lists of constraints. interface Edge; interface Node; // Forward declare named constraints used in // interface definitions. private constraint EdgeFor(N:! Node); private constraint NodeFor(E:! Edge); // Define interfaces using named constraints. interface Edge { let NodeT:! NodeFor(Self); fn Head[self: Self]() -> NodeT; } interface Node { let EdgeT:! EdgeFor(Self); fn Edges[self: Self]() -> DynArray(EdgeT); } // Now that the interfaces are defined, can // refer to members of the interface, so it is // now legal to define the named constraints. constraint EdgeFor(N:! Node) { extend Edge where .NodeT = N; } constraint NodeFor(E:! Edge) { extend Node where .EdgeT = E; } ``` > **Future work:** This approach has limitations. For example the compiler only > knows `EdgeT` is convertible to `type` in the body of the `interface Node` > definition, which may not be enough to satisfy the requirements to be an > argument to `DynArray`. If this proves to be a problem, we may decided to > expand what can be done with incomplete interfaces and types to allow the > above to be written without the additional private constraints: > > ```carbon > interface Node; > > interface Edge { > let NodeT:! Node where .EdgeT = Self; > fn Head[self: Self]() -> NodeT; > } > > interface Node { > let EdgeT:! Movable & Edge where .NodeT = Self; > fn Edges[self: Self]() -> DynArray(EdgeT); > } > ``` ### Interfaces with parameters constrained by the same interface To work around [the restriction about not being able to name an interface in its parameter list](#declaring-interfaces-and-named-constraints), instead include that requirement in the body of the interface. ```carbon // Want to require that `T` satisfies `CommonType(Self)`, // but that can't be done in the parameter list. interface CommonType(T:! type) { let Result:! type; // Instead add the requirement inside the definition. require T impls CommonType(Self); } ``` Note however that `CommonType` is still incomplete inside its definition, so no constraints on members of `CommonType` are allowed, and that this `require T impls` declaration [must involve `Self`](#interface-requiring-other-interfaces-revisited). ```carbon interface CommonType(T:! type) { let Result:! type; // ❌ Illegal: `CommonType` is incomplete require T impls CommonType(Self) where .Result == Result; } ``` Instead, a forward-declared named constraint can be used in place of the constraint that can only be defined later. This is [the same strategy used to work around cyclic references](#example-of-declaring-interfaces-with-cyclic-references). ```carbon private constraint CommonTypeResult(T:! type, R:! type); interface CommonType(T:! type) { let Result:! type; // ✅ Allowed: `CommonTypeResult` is incomplete, but // no members are accessed. require T impls CommonTypeResult(Self, Result); } constraint CommonTypeResult(T:! type, R:! type) { extend CommonType(T) where .Result == R; } ``` ## Interface members with definitions Interfaces may provide definitions for members, such as a function body for an associated function or method or a value for an associated constant. If these definitions may be overridden in implementations, they are called "defaults" and prefixed with the `default` keyword. Otherwise they are called "final members" and prefixed with the `final` keyword. ### Interface defaults An interface may provide a default implementation of methods in terms of other methods in the interface. ```carbon interface Vector { fn Add[self: Self](b: Self) -> Self; fn Scale[self: Self](v: f64) -> Self; // Default definition of `Invert` calls `Scale`. default fn Invert[self: Self]() -> Self { return self.Scale(-1.0); } } ``` A default function or method may also be defined out of line, later in the same file as the interface definition: ```carbon interface Vector { fn Add[self: Self](b: Self) -> Self; fn Scale[self: Self](v: f64) -> Self; default fn Invert[self: Self]() -> Self; } // `Vector` is considered complete at this point, // even though `Vector.Invert` is still incomplete. fn Vector.Invert[self: Self]() -> Self { return self.Scale(-1.0); } ``` An impl of that interface for a type may omit a definition of `Invert` to use the default, or provide a definition to override the default. Interface defaults are helpful for [evolution](#evolution), as well as reducing boilerplate. Defaults address the gap between the minimum necessary for a type to provide the desired functionality of an interface and the breadth of API that developers desire. As an example, in Rust the [iterator trait](https://doc.rust-lang.org/std/iter/trait.Iterator.html) only has one required method but dozens of "provided methods" with defaults. Defaults may also be provided for associated constants, such as associated facets, and interface parameters, using the `= ` syntax. ```carbon interface Add(Right:! type = Self) { default let Result:! type = Self; fn DoAdd[self: Self](right: Right) -> Result; } impl String as Add() { // Right == Result == Self == String fn DoAdd[self: Self](right: Self) -> Self; } ``` Note that `Self` is a legal default value for an associated facet or facet parameter. In this case the value of those names is not determined until `Self` is, so `Add()` is equivalent to the constraint: ```carbon // Equivalent to Add() constraint AddDefault { extend Add(Self); } ``` Note also that the parenthesis are required after `Add`, even when all parameters are left as their default values. More generally, default expressions may reference other associated constants or `Self` as parameters to type constructors. For example: ```carbon interface Iterator { let Element:! type; default let Pointer:! type = Element*; } ``` Carbon does **not** support providing a default implementation of a required interface. ```carbon interface TotalOrder { fn TotalLess[self: Self](right: Self) -> bool; // ❌ Illegal: May not provide definition // for required interface. require impls PartialOrder { fn PartialLess[self: Self](right: Self) -> bool { return self.TotalLess(right); } } } ``` The workaround for this restriction is to use a [blanket impl declaration](#blanket-impl-declarations) instead: ```carbon interface TotalOrder { fn TotalLess[self: Self](right: Self) -> bool; // No `require` declaration, since implementers of // `TotalOrder` don't need to also implement // `PartialOrder`, since an implementation is provided. } // Any type that implements `TotalOrder` also has at // least this implementation of `PartialOrder`: impl forall [T:! TotalOrder] T as PartialOrder { fn PartialLess[self: Self](right: Self) -> bool { return self.TotalLess(right); } } ``` Note that by the [orphan rule](#orphan-rule), this blanket impl must be defined in the same library as `PartialOrder`. **Comparison with other languages:** Rust supports specifying defaults for [methods](https://doc.rust-lang.org/book/ch10-02-traits.html#default-implementations), [interface parameters](https://doc.rust-lang.org/book/ch19-03-advanced-traits.html#default-generic-type-parameters-and-operator-overloading), and [associated constants](https://doc.rust-lang.org/reference/items/associated-items.html#associated-constants-examples). Rust has found them valuable. ### `final` members As an alternative to providing a definition of an interface member as a default, members marked with the `final` keyword will not allow that definition to be overridden in `impl` definitions. ```carbon interface TotalOrder { fn TotalLess[self: Self](right: Self) -> bool; final fn TotalGreater[self: Self](right: Self) -> bool { return right.TotalLess(self); } } class String { extend impl as TotalOrder { fn TotalLess[self: Self](right: Self) -> bool { ... } // ❌ Illegal: May not provide definition of final // method `TotalGreater`. fn TotalGreater[self: Self](right: Self) -> bool { ... } } } interface Add(T:! type = Self) { // `AddWith` *always* equals `T` final let AddWith:! type = T; // Has a *default* of `Self` default let Result:! type = Self; fn DoAdd[self: Self](right: AddWith) -> Result; } ``` Final members may also be defined out-of-line: ```carbon interface TotalOrder { fn TotalLess[self: Self](right: Self) -> bool; final fn TotalGreater[self: Self](right: Self) -> bool; } // `TotalOrder` is considered complete at this point, even // though `TotalOrder.TotalGreater` is not yet defined. fn TotalOrder.TotalGreater[self: Self](right: Self) -> bool { return right.TotalLess(self); } ``` There are a few reasons for this feature: - When overriding would be inappropriate. - Matching the functionality of non-virtual methods in base classes, so interfaces can be a replacement for inheritance. - Potentially reduce dynamic dispatch when using the interface in a [`DynPtr`](#dynamic-types). Note that this applies to associated entities, not interface parameters. ## Interface requiring other interfaces revisited Recall that an [interface can require another interface be implemented for the type](#interface-requiring-other-interfaces), as in: ```carbon interface Iterable { require impls Equatable; // ... } ``` This states that the type implementing the interface `Iterable`, which in this context is called `Self`, must also implement the interface `Equatable`. As is done with [conditional conformance](#conditional-conformance), we allow another type to be specified between `require` and `impls` to say some type other than `Self` must implement an interface. For example, ```carbon interface IntLike { require i32 impls As(Self); // ... } ``` says that if `Self` implements `IntLike`, then `i32` must implement `As(Self)`. Similarly, ```carbon interface CommonTypeWith(T:! type) { require T impls CommonTypeWith(Self); // ... } ``` says that if `Self` implements `CommonTypeWith(T)`, then `T` must implement `CommonTypeWith(Self)`. A `require impls ` constraint in an `interface`, or `constraint`, definition must still use `Self` as either the type, or as a parameter to the [type](#parameterized-types) or an [interface](#parameterized-interfaces) in the facet type. In particular, it requires `Self` be part of the type structure of any `impl` that could satisfy that `require`. If the `` is omitted entirely, it will be implied to be `Self`. For example: - ✅ Allowed: `require impls Equatable` - ✅ Allowed: `require Self impls Equatable` - ✅ Allowed: `require Vector(Self) impls Equatable` - ✅ Allowed: `require i32 impls CommonTypeWith(Self)` - ✅ Allowed: `require impls CommonTypeWith(Self)` - ✅ Allowed: `require Self impls CommonTypeWith(Self)` - ❌ Error: `require i32 impls Equatable` - ❌ Error: `require i32 impls Equatable where .Result = Self` - ❌ Error: `require T impls Equatable` when `T` is some parameter to the interface This restriction allows the Carbon compiler to know where to look for facts about a type. If `require i32 impls Equatable` could appear in any `interface` definition, that implies having to search all of them when considering what interfaces `i32` implements. This would create a [coherence](terminology.md#coherence) problem, since then the set of facts true for a type would depend on which interfaces have been imported. When implementing an interface with an `require`...`impls` requirement, that requirement must be satisfied by an implementation in an imported library, an implementation somewhere in the same file, or a constraint in the impl declaration. Implementing the requiring interface is a promise that the requirement will be implemented. This is like a [forward declaration of an impl](#declaring-implementations) except that the definition can be broader instead of being required to match exactly. ```carbon // `Iterable` requires `Equatable`, so there must be some // impl of `Equatable` for `Vector(i32)` in this file. impl Vector(i32) as Iterable { ... } fn RequiresEquatable[T:! Equatable](x: T) { ... } fn ProcessVector(v: Vector(i32)) { // ✅ Allowed since `Vector(i32)` is known to // implement `Equatable`. RequiresEquatable(v); } // Satisfies the requirement that `Vector(i32)` must // implement `Equatable` since `i32 impls Equatable`. impl forall [T:! Equatable] Vector(T) as Equatable { ... } ``` In some cases, the interface's requirement can be trivially satisfied by the implementation itself, as in: ```carbon impl forall [T:! type] T as CommonTypeWith(T) { ... } ``` Here is an example where the requirement of interface `Iterable` that the type implements interface `Equatable` is satisfied by a constraint in the `impl` declaration: ```carbon class Foo(T:! type) {} // This is allowed because we know that an `impl Foo(T) as Equatable` // will exist for all types `T` for which this impl is used, even // though there's neither an imported impl nor an impl in this file. impl forall [T:! type where Foo(T) impls Equatable] Foo(T) as Iterable {} ``` This might be used to provide an implementation of `Equatable` for types that already satisfy the requirement of implementing `Iterable`: ```carbon class Bar {} impl Foo(Bar) as Equatable {} // Gives `Foo(Bar) impls Iterable` using the blanket impl of // `Iterable` for `Foo(T)`. ``` ### Requirements with `where` constraints An interface implementation requirement with a `where` clause is harder to satisfy. Consider an interface `B` that has a requirement that interface `A` is also implemented. ```carbon interface A(T:! type) { let Result:! type; } interface B(T:! type) { require impls A(T) where .Result == i32; } ``` An implementation of `B` for a set of types can only be valid if there is a visible implementation of `A` with the same `T` parameter for those types with the `.Result` associated facet set to `i32`. That is [not sufficient](/proposals/p1088.md#less-strict-about-requirements-with-where-clauses), though, unless the implementation of `A` can't be specialized, either because it is [marked `final`](#final-impl-declarations) or is not [parameterized](#parameterized-impl-declarations). Implementations in other libraries can't make `A` be implemented for fewer types, but can cause `.Result` to have a different assignment. ## Observing a type implements an interface An [`observe` declaration](#observe-declarations) can be used to show that two types are equal so code can pass type checking without explicitly writing casts, and without requiring the compiler to do a unbounded search that may not terminate. An `observe` declaration can also be used to show that a type implements an interface, in cases where the compiler will not work this out for itself. ### Observing interface requirements One situation where this occurs is when there is a chain of [interfaces requiring other interfaces](#interface-requiring-other-interfaces-revisited). During the `impl` validation done during type checking, Carbon will only consider the interfaces that are direct requirements of the interfaces the type is known to implement. An `observe`...`impls` declaration can be used to add an interface that is a direct requirement to the set of interfaces whose direct requirements will be considered for that type. This allows a developer to provide a proof that there is a sequence of requirements that demonstrate that a type implements an interface, as in this example: ```carbon interface A { } interface B { require impls A; } interface C { require impls B; } interface D { require impls C; } fn RequiresA[T:! A](x: T); fn RequiresC[T:! C](x: T); fn RequiresD[T:! D](x: T) { // ✅ Allowed: `D` directly requires `C` to be implemented. RequiresC(x); // ❌ Illegal: No direct connection between `D` and `A`. // RequiresA(x); // `T impls D` and `D` directly requires `C` to be // implemented. observe T impls C; // `T impls C` and `C` directly requires `B` to be // implemented. observe T impls B; // ✅ Allowed: `T impls B` and `B` directly requires // `A` to be implemented. RequiresA(x); } ``` Note that `observe` statements do not affect which impl is selected during code generation. For [coherence](terminology.md#coherence), the impl used for a (type, interface) pair must always be the same, independent of context. The [termination rule](#termination-rule) governs when compilation may fail when the compiler can't determine the `impl` definition to select. ### Observing blanket impl declarations An `observe`...`impls` declaration can also be used to observe that a type implements an interface because there is a [blanket impl declaration](#blanket-impl-declarations) in terms of requirements a type is already known to satisfy. Without an `observe` declaration, Carbon will only use blanket impl declarations that are directly satisfied. ```carbon interface A { } interface B { } interface C { } interface D { } impl forall [T:! A] T as B { } impl forall [T:! B] T as C { } impl forall [T:! C] T as D { } fn RequiresD[T:! D](x: T); fn RequiresB[T:! B](x: T); fn RequiresA[T:! A](x: T) { // ✅ Allowed: There is a blanket implementation // of `B` for types implementing `A`. RequiresB(x); // ❌ Illegal: No implementation of `D` for type // `T` implementing `A` // RequiresD(x); // There is a blanket implementation of `B` for // types implementing `A`. observe T impls B; // There is a blanket implementation of `C` for // types implementing `B`. observe T impls C; // ✅ Allowed: There is a blanket implementation // of `D` for types implementing `C`. RequiresD(x); } ``` In the case of an error, a quality Carbon implementation will do a deeper search for chains of requirements and blanket impl declarations and suggest `observe` declarations that would make the code compile if any solution is found. ### Observing equal to a type implementing an interface The [`observe`...`==` form](#observe-declarations) can be combined with the `observe`...`impls` form to show that a type implements an interface because it is equal to another type that is known to implement that interface. ```carbon interface I { fn F(); } fn G(T:! I, U:! type where .Self == T) { // ❌ Illegal: No implementation of `I` for `U`. U.(I.F)(); // ✅ Allowed: Implementation of `I` for `U` // through `T`. observe U == T impls I; U.(I.F)(); // ❌ Illegal: `U` does not extend `I`. U.F(); } ``` Multiple `==` clauses are allowed in an `observe` declaration, so you may write `observe A == B == C impls I;`. ## Operator overloading Operations are overloaded for a type by implementing an interface specific to that interface for that type. For example, types implement [the `Negate` interface](/docs/design/expressions/arithmetic.md#extensibility) to overload the unary `-` operator: ```carbon // Unary `-`. interface Negate { default let Result:! type = Self; fn Op[self: Self]() -> Result; } ``` Expressions using operators are rewritten into calls to these interface methods. For example, `-x` would be rewritten to `x.(Negate.Op)()`. The interfaces and rewrites used for a given operator may be found in the [expressions design](/docs/design/expressions/README.md). [Question-for-leads issue #1058](https://github.com/carbon-language/carbon-lang/issues/1058) defines the naming scheme for these interfaces, which was implemented in [proposal #1178](https://github.com/carbon-language/carbon-lang/pull/1178). ### Binary operators Binary operators will have an interface that is [parameterized](#parameterized-interfaces) based on the second operand. For example, to say a type may be converted to another type using an `as` expression, implement the [`As` interface](/docs/design/expressions/as_expressions.md#extensibility): ```carbon interface As(Dest:! type) { fn Convert[self: Self]() -> Dest; } ``` The expression `x as U` is rewritten to `x.(As(U).Convert)()`. Note that the parameterization of the interface means it can be implemented multiple times to support multiple operand types. Unlike `as`, for most binary operators the interface's argument will be the _type_ of the right-hand operand instead of its _value_. Consider [the interface for a binary operator like `*`](/docs/design/expressions/arithmetic.md#extensibility): ```carbon // Binary `*`. interface MulWith(U:! type) { default let Result:! type = Self; fn Op[self: Self](other: U) -> Result; } ``` A use of binary `*` in source code will be rewritten to use this interface: ```carbon var left: Meters = ...; var right: f64 = ...; var result: auto = left * right; // Equivalent to: var equivalent: left.(MulWith(f64).Result) = left.(MulWith(f64).Op)(right); ``` Note that if the types of the two operands are different, then swapping the order of the operands will result in a different implementation being selected. It is up to the developer to make those consistent when that is appropriate. The standard library will provide [adapters](#adapting-types) for defining the second implementation from the first, as in: ```carbon interface OrderedWith(U:! type) { fn Compare[self: Self](u: U) -> Ordering; // ... } class ReverseComparison(T:! type, U:! OrderedWith(T)) { adapt T; extend impl as OrderedWith(U) { fn Compare[self: Self](u: U) -> Ordering { match (u.Compare(self)) { case .Less => return .Greater; case .Equivalent => return .Equivalent; case .Greater => return .Less; case .Incomparable => return .Incomparable; } } } } impl SongByTitle as OrderedWith(SongTitle) { ... } impl SongTitle as OrderedWith(SongByTitle) = ReverseComparison(SongTitle, SongByTitle); ``` In some cases the reverse operation may not be defined. For example, a library might support subtracting a vector from a point, but not the other way around. Further note that even if the reverse implementation exists, [the `impl` prioritization rule](#prioritization-rule) might not pick it. For example, if we have two types that support comparison with anything implementing an interface that the other implements: ```carbon interface IntLike { fn AsInt[self: Self]() -> i64; } class EvenInt { ... } impl EvenInt as IntLike; impl EvenInt as OrderedWith(EvenInt); // Allow `EvenInt` to be compared with anything that // implements `IntLike`, in either order. impl forall [T:! IntLike] EvenInt as OrderedWith(T); impl forall [T:! IntLike] T as OrderedWith(EvenInt); class PositiveInt { ... } impl PositiveInt as IntLike; impl PositiveInt as OrderedWith(PositiveInt); // Allow `PositiveInt` to be compared with anything that // implements `IntLike`, in either order. impl forall [T:! IntLike] PositiveInt as OrderedWith(T); impl forall [T:! IntLike] T as OrderedWith(PositiveInt); ``` Then the compiler will favor selecting the implementation based on the type of the left-hand operand: ```carbon var even: EvenInt = ...; var positive: PositiveInt = ...; // Uses `EvenInt as OrderedWith(T)` impl if (even < positive) { ... } // Uses `PositiveInt as OrderedWith(T)` impl if (positive > even) { ... } ``` ### `like` operator for implicit conversions Because the type of the operands is directly used to select the operator interface implementation, there are no automatic implicit conversions, unlike with function or method calls. Given both a method and an interface implementation for multiplying by a value of type `f64`: ```carbon class Meters { fn Scale[self: Self](s: f64) -> Self; } // "Implementation One" impl Meters as MulWith(f64) where .Result = Meters { fn Op[self: Self](other: f64) -> Result { return self.Scale(other); } } ``` the method will work with any argument that can be implicitly converted to `f64` but the operator overload will only work with values that have the specific type of `f64`: ```carbon var height: Meters = ...; var scale: f32 = 1.25; // ✅ Allowed: `scale` implicitly converted // from `f32` to `f64`. var allowed: Meters = height.Scale(scale); // ❌ Illegal: `Meters` doesn't implement // `MulWith(f32)`. var illegal: Meters = height * scale; ``` The workaround is to define a parameterized implementation that performs the conversion. The implementation is for types that implement the [`ImplicitAs` interface](/docs/design/expressions/implicit_conversions.md#extensibility). ```carbon // "Implementation Two" impl forall [T:! ImplicitAs(f64)] Meters as MulWith(T) where .Result = Meters { fn Op[self: Self](other: T) -> Result { // Carbon will implicitly convert `other` from type // `T` to `f64` to perform this call. return self.((Meters as MulWith(f64)).Op)(other); } } // ✅ Allowed: uses `Meters as MulWith(T)` impl // with `T == f32` since `f32 impls ImplicitAs(f64)`. var now_allowed: Meters = height * scale; ``` Observe that the [prioritization rule](#prioritization-rule) will still prefer the unparameterized impl when there is an exact match. To reduce the boilerplate needed to support these implicit conversions when defining operator overloads, Carbon has the `like` operator. This operator can only be used in the type or facet type part of an `impl` declaration, as part of a forward declaration or definition, in a place of a type. ```carbon // Notice `f64` has been replaced by `like f64` // compared to "implementation one" above. impl Meters as MulWith(like f64) where .Result = Meters { fn Op[self: Self](other: f64) -> Result { return self.Scale(other); } } ``` This `impl` definition actually defines two implementations. The first is the same as this definition with `like f64` replaced by `f64`, giving something equivalent to "implementation one". The second implementation replaces the `like f64` with a parameter that ranges over types that can be implicitly converted to `f64`, equivalent to "implementation two". > **Note:** We have decided to change the following in > [a discussion on 2023-07-13](https://docs.google.com/document/d/1gnJBTfY81fZYvI_QXjwKk1uQHYBNHGqRLI2BS_cYYNQ/edit?resourcekey=0-ql1Q1WvTcDvhycf8LbA9DQ#heading=h.rs7m0kytcl4t). > The new approach is to have one parameterized implementation replacing all of > the `like` expressions on the left of the `as`, and another replacing all of > the `like` expressions on the right of the `as`. However, in > [a discussion on 2023-07-20](https://docs.google.com/document/d/1gnJBTfY81fZYvI_QXjwKk1uQHYBNHGqRLI2BS_cYYNQ/edit?resourcekey=0-ql1Q1WvTcDvhycf8LbA9DQ#heading=h.msdqbemd6axi), > we decided that this change would not affect how we handle nested `like` > expressions: `like Vector(like i32)` is still `like Vector(i32)` plus > `Vector(like i32)`. These changes have not yet gone through the proposal > process, and we may decide to reject nested `like` until we have a > demonstrated need. In general, each `like` adds one additional parameterized implementation. There is always the impl defined with all of the `like` expressions replaced by their arguments with the definition supplied in the source code. In addition, for each `like` expression, there is an automatic `impl` definition with it replaced by a new parameter. These additional automatic implementations will delegate to the main `impl` definition, which will trigger implicit conversions according to [Carbon's ordinary implicit conversion rules](/docs/design/expressions/implicit_conversions.md). In this example, there are two uses of `like`, producing three implementations ```carbon impl like Meters as MulWith(like f64) where .Result = Meters { fn Op[self: Self](other: f64) -> Result { return self.Scale(other); } } ``` is equivalent to "implementation one", "implementation two", and: ```carbon impl forall [T:! ImplicitAs(Meters)] T as MulWith(f64) where .Result = Meters { fn Op[self: Self](other: f64) -> Result { // Will implicitly convert `self` to `Meters` in // order to match the signature of this `Op` method. return self.((Meters as MulWith(f64)).Op)(other); } } ``` `like` may be used in `impl` forward declarations in a way analogous to `impl` definitions. ```carbon impl like Meters as MulWith(like f64) where .Result = Meters; } ``` is equivalent to: ```carbon // All `like`s removed. Same as the declaration part of // "implementation one", without the body of the definition. impl Meters as MulWith(f64) where .Result = Meters; // First `like` replaced with a wildcard. impl forall [T:! ImplicitAs(Meters)] T as MulWith(f64) where .Result = Meters; // Second `like` replaced with a wildcard. Same as the // declaration part of "implementation two", without the // body of the definition. impl forall [T:! ImplicitAs(f64)] Meters as MulWith(T) where .Result = Meters; ``` In addition, the generated `impl` definition for a `like` is implicitly injected at the end of the (unique) source file in which the `impl` is defined. That is, it is injected in the API file if the `impl` definition is in an API file, and in the sole impl file with the `impl` definition otherwise. If one `impl` declaration uses `like`, other declarations must use `like` in the same way to match. The `like` operator may be nested, as in: ```carbon impl like Vector(like String) as Printable; ``` Which will generate implementations with declarations: ```carbon impl Vector(String) as Printable; impl forall [T:! ImplicitAs(Vector(String))] T as Printable; impl forall [T:! ImplicitAs(String)] Vector(T) as Printable; ``` The generated implementations must be legal or the `like` is illegal. For example, it must be legal to have those `impl` definitions in this library by the [orphan rule](#orphan-rule). In addition, the generated `impl` definitions must only require implicit conversions that are guaranteed to exist. For example, there existing an implicit conversion from `T` to `String` does not imply that there is one from `Vector(T)` to `Vector(String)`, so the following use of `like` is illegal: ```carbon // ❌ Illegal: Can't convert a value with type // `Vector(T:! ImplicitAs(String))` // to `Vector(String)` for `self` // parameter of `Printable.Print`. impl Vector(like String) as Printable; ``` Since the additional implementation definitions are generated eagerly, these errors will be reported in the file with the first declaration. The argument to `like` must either not mention any type parameters, or those parameters must be able to be determined due to being repeated outside of the `like` expression. ```carbon // ✅ Allowed: no parameters impl like Meters as Printable; // ❌ Illegal: No other way to determine `T` impl forall [T:! IntLike] like T as Printable; // ❌ Illegal: `T` being used in a `where` clause // is insufficient. impl forall [T:! IntLike] like T as MulWith(i64) where .Result = T; // ❌ Illegal: `like` can't be used in a `where` // clause. impl Meters as MulWith(f64) where .Result = like Meters; // ✅ Allowed: `T` can be determined by another // part of the query. impl forall [T:! IntLike] like T as MulWith(T) where .Result = T; impl forall [T:! IntLike] T as MulWith(like T) where .Result = T; // ✅ Allowed: Only one `like` used at a time, so this // is equivalent to the above two examples. impl forall [T:! IntLike] like T as MulWith(like T) where .Result = T; ``` ## Parameterized types Generic types may be defined by giving them compile-time parameters. Those parameters may be used to specify types in the declarations of its members, such as data fields, member functions, and even interfaces being implemented. For example, a container type might be parameterized by a facet describing the type of its elements: ```carbon class HashMap( KeyT:! Hashable & Eq & Movable, ValueT:! Movable) { // `Self` is `HashMap(KeyT, ValueT)`. // Class parameters may be used in function signatures. fn Insert[ref self: Self](k: KeyT, v: ValueT); // Class parameters may be used in field types. private var buckets: DynArray((KeyT, ValueT)); // Class parameters may be used in interfaces implemented. extend impl as Container where .ElementType = (KeyT, ValueT); impl as OrderedWith(HashMap(KeyT, ValueT)); } ``` Note that, unlike functions, every parameter to a type must be a compile-time binding, either symbolic using `:!` or template using `template`...`:!`, not runtime, with a plain `:`. Two types are the same if they have the same name and the same arguments, after applying aliases and [rewrite constraints](#rewrite-constraints). Carbon's [manual type equality](#manual-type-equality) approach means that the compiler may not always be able to tell when two [type expressions](terminology.md#type-expression) are equal without help from the user, in the form of [`observe` declarations](#observe-declarations). This means Carbon will not in general be able to determine when types are unequal. Unlike an [interface's parameters](#parameterized-interfaces), a type's parameters may be [deduced](terminology.md#deduced-parameter), as in: ```carbon fn ContainsKey[KeyT:! Movable, ValueT:! Movable] (haystack: HashMap(KeyT, ValueT), needle: KeyT) -> bool { ... } fn MyMapContains(s: String) { var map: HashMap(String, i32) = (("foo", 3), ("bar", 5)); // ✅ Deduces `KeyT` = `String as Movable` from the types of both arguments. // Deduces `ValueT` = `i32 as Movable` from the type of the first argument. return ContainsKey(map, s); } ``` Note that restrictions on the type's parameters from the type's declaration can be [implied constraints](#implied-constraints) on the function's parameters. In the above example, the `KeyT` parameter to `ContainsKey` gets `Hashable & Eq` implied constraints from the declaration of the corresponding parameter to `HashMap`. > **Future work:** We may want to support optional deduced parameters in square > brackets `[`...`]` before the explicit parameters in round parens `(`...`)`. > **References:** This feature is from > [proposal #1146: Generic details 12: parameterized types](https://github.com/carbon-language/carbon-lang/pull/1146). ### Generic methods A generic type may have methods with additional compile-time parameters. For example, this `Set(T)` type may be compared to anything implementing the `Container` interface as long as the element types match: ```carbon class Set(T:! Ordered) { fn Less[U:! Container with .ElementType = T, self: Self](u: U) -> bool; // ... } ``` The `Less` method is parameterized both by the `T` parameter to the `Set` type and its own `U` parameter deduced from the type of its first argument. ### Conditional methods A method could be defined conditionally for a generic type by using a more specific type in place of `Self` in the method declaration. For example, this is how to define a dynamically sized array type that only has a `Sort` method if its elements implement the `Ordered` interface: ```carbon class DynArray(T:! type) { // `DynArray(T)` has a `Sort()` method if `T impls Ordered`. fn Sort[C:! Ordered, ref self: DynArray(C)](); } ``` **Comparison with other languages:** In [Rust](https://doc.rust-lang.org/book/ch10-02-traits.html#using-trait-bounds-to-conditionally-implement-methods) this feature is part of conditional conformance. Swift supports conditional methods using [conditional extensions](https://docs.swift.org/swift-book/LanguageGuide/Generics.html#ID553) or [contextual where clauses](https://docs.swift.org/swift-book/LanguageGuide/Generics.html#ID628). ### Specialization [Specialization](terminology.md#checked-generic-specialization) is used to improve performance in specific cases when a general strategy would be inefficient. For example, you might use [binary search](https://en.wikipedia.org/wiki/Binary_search_algorithm) for containers that support random access and keep their contents in sorted order but [linear search](https://en.wikipedia.org/wiki/Linear_search) in other cases. Types, like functions, may not be specialized directly in Carbon. This effect can be achieved, however, through delegation. For example, imagine we have a parameterized class `Optional(T)` that has a default storage strategy that works for all `T`, but for some types we have a more efficient approach. For pointers we can use a [null value](https://en.wikipedia.org/wiki/Null_pointer) to represent "no pointer", and for booleans we can support `True`, `False`, and `None` in a single byte. Clients of the optional library may want to add additional specializations for their own types. We make an interface that represents "the storage of `Optional(T)` for type `T`," written here as `OptionalStorage`: ```carbon interface OptionalStorage { let Storage:! type; fn MakeNone() -> Storage; fn Make(x: Self) -> Storage; fn IsNone(x: Storage) -> bool; fn Unwrap(x: Storage) -> Self; } ``` The default implementation of this interface is provided by a [blanket implementation](#blanket-impl-declarations): ```carbon // Default blanket implementation impl forall [T:! Movable] T as OptionalStorage where .Storage = (bool, T) { ... } ``` This implementation can then be [specialized](#lookup-resolution-and-specialization) for more specific type patterns: ```carbon // Specialization for pointers, using nullptr == None final impl forall [T:! type] T* as OptionalStorage where .Storage = Array(Byte, sizeof(T*)) { ... } // Specialization for type `bool`. final impl bool as OptionalStorage where .Storage = Byte { ... } ``` Further, libraries can implement `OptionalStorage` for their own types, assuming the interface is not marked `private`. Then the implementation of `Optional(T)` can delegate to `OptionalStorage` for anything that can vary with `T`: ```carbon class Optional(T:! Movable) { fn None() -> Self { return {.storage = T.(OptionalStorage.MakeNone)()}; } fn Some(x: T) -> Self { return {.storage = T.(OptionalStorage.Make)(x)}; } ... private var storage: T.(OptionalStorage.Storage); } ``` Note that the constraint on `T` is just `Movable`, not `Movable & OptionalStorage`, since the `Movable` requirement is [sufficient to guarantee](#lookup-resolution-and-specialization) that some implementation of `OptionalStorage` exists for `T`. Carbon does not require callers of `Optional`, even checked-generic callers, to specify that the argument type implements `OptionalStorage`: ```carbon // ✅ Allowed: `T` just needs to be `Movable` to form `Optional(T)`. // A `T:! OptionalStorage` constraint is not required. fn First[T:! Movable & Eq](v: Vector(T)) -> Optional(T); ``` Adding `OptionalStorage` to the constraints on the parameter to `Optional` would obscure what types can be used as arguments. `OptionalStorage` is an implementation detail of `Optional` and need not appear in its public API. In this example, a `let` is used to avoid repeating `OptionalStorage` in the definition of `Optional`, since it has no name conflicts with the members of `Movable`: ```carbon class Optional(T:! Movable) { private let U:! Movable & OptionalStorage = T; fn None() -> Self { return {.storage = U.MakeNone()}; } fn Some(x: T) -> Self { return {.storage = U.Make(x)}; } ... private var storage: U.Storage; } ``` > **Alternative considered:** Direct support for specialization of types was > considered in [proposal #1146](/proposals/p1146.md#alternatives-considered). ## Future work ### Dynamic types Checked-generics provide enough structure to support runtime dispatch for values with types that vary at runtime, without giving up type safety. Both Rust and Swift have demonstrated the value of this feature. #### Runtime type parameters This feature is about allowing a function's type parameter to be passed in as a dynamic (non-compile-time) parameter. All values of that type would still be required to have the same type. #### Runtime type fields Instead of passing in a single type parameter to a function, we could store a type per value. This changes the data layout of the value, and so is a somewhat more invasive change. It also means that when a function operates on multiple values they could have different real types. ### Abstract return types This lets you return an anonymous type implementing an interface from a function. In Rust this is the [`impl Trait` return type](https://rust-lang.github.io/rfcs/1522-conservative-impl-trait.html). In Swift, there are discussions about implementing this feature under the name "reverse generics" or "opaque result types": [1](https://forums.swift.org/t/improving-the-ui-of-generics/22814#heading--reverse-generics), [2](https://forums.swift.org/t/reverse-generics-and-opaque-result-types/21608), [3](https://forums.swift.org/t/se-0244-opaque-result-types/21252), [4](https://forums.swift.org/t/se-0244-opaque-result-types-reopened/22942), Swift is considering spelling this ` V` or `some Collection`. ### Evolution There are a collection of use cases for making different changes to interfaces that are already in use. These should be addressed either by describing how they can be accomplished with existing generics features, or by adding features. In addition, evolution from (C++ or Carbon) templates to checked generics needs to be supported and made safe. ### Testing The idea is that you would write tests alongside an interface that validate the expected behavior of any type implementing that interface. ### Impl with state A feature we might consider where an `impl` itself can have state. ### Generic associated facets and higher-ranked facets This would be some way to express the requirement that there is a way to go from a type to an implementation of an interface parameterized by that type. #### Generic associated facets Generic associated facets are about when this is a requirement of an interface. These are also called "[associated type constructors](https://smallcultfollowing.com/babysteps/blog/2016/11/02/associated-type-constructors-part-1-basic-concepts-and-introduction/)." Rust has [stabilized this feature](https://github.com/rust-lang/rust/pull/96709). #### Higher-ranked types Higher-ranked types are used to represent this requirement in a function signature. They can be [emulated using generic associated facets](https://smallcultfollowing.com/babysteps//blog/2016/11/03/associated-type-constructors-part-2-family-traits/). ### Field requirements We might want to allow interfaces to express the requirement that any implementing type has a particular field. This would be to match the expressivity of inheritance, which can express "all subtypes start with this list of fields." ### Bridge for C++ customization points See details in [the goals document](goals.md#bridge-for-c-customization-points). ### Variadic arguments Some facility for allowing a function to take a variable number of arguments, with the [definition checked](terminology.md#complete-definition-checking) independent of calls. Open [proposal #2240](https://github.com/carbon-language/carbon-lang/pull/2240) is adding this feature. ### Value constraints for template parameters We have planned support for predicates that constrain the value of non-facet template parameters. For example, we might support a predicate that constrains an integer to live inside a specified range. See [question-for-leads issue #2153: Checked generics calling templates](https://github.com/carbon-language/carbon-lang/issues/2153) and [future work in proposal #2200: Template generics](/proposals/p2200.md#predicates-constraints-on-values). ## References - [#553: Generics details part 1](https://github.com/carbon-language/carbon-lang/pull/553) - [#731: Generics details 2: adapters, associated types, parameterized interfaces](https://github.com/carbon-language/carbon-lang/pull/731) - [#818: Constraints for generics (generics details 3)](https://github.com/carbon-language/carbon-lang/pull/818) - [#931: Generic impls access (details 4)](https://github.com/carbon-language/carbon-lang/pull/931) - [#920: Generic parameterized impls (details 5)](https://github.com/carbon-language/carbon-lang/pull/920) - [#950: Generic details 6: remove facets](https://github.com/carbon-language/carbon-lang/pull/950) - [#983: Generic details 7: final impls](https://github.com/carbon-language/carbon-lang/pull/983) - [#989: Member access expressions](https://github.com/carbon-language/carbon-lang/pull/989) - [#990: Generics details 8: interface default and final members](https://github.com/carbon-language/carbon-lang/pull/990) - [#1013: Generics: Set associated constants using `where` constraints](https://github.com/carbon-language/carbon-lang/pull/1013) - [#1084: Generics details 9: forward declarations](https://github.com/carbon-language/carbon-lang/pull/1084) - [#1088: Generic details 10: interface-implemented requirements](https://github.com/carbon-language/carbon-lang/pull/1088) - [#1144: Generic details 11: operator overloading](https://github.com/carbon-language/carbon-lang/pull/1144) - [#1146: Generic details 12: parameterized types](https://github.com/carbon-language/carbon-lang/pull/1146) - [#1327: Generics: `impl forall`](https://github.com/carbon-language/carbon-lang/pull/1327) - [#2107: Clarify rules around `Self` and `.Self`](https://github.com/carbon-language/carbon-lang/pull/2107) - [#2138: Checked and template generic terminology](https://github.com/carbon-language/carbon-lang/pull/2138) - [Issue #2153: Checked generics calling templates](https://github.com/carbon-language/carbon-lang/issues/2153) - [#2173: Associated constant assignment versus equality](https://github.com/carbon-language/carbon-lang/pull/2173) - [#2200: Template generics](https://github.com/carbon-language/carbon-lang/pull/2200) - [#2347: What can be done with an incomplete interface](https://github.com/carbon-language/carbon-lang/pull/2347) - [#2360: Types are values of type `type`](https://github.com/carbon-language/carbon-lang/pull/2360) - [#2376: Constraints must use `Self`](https://github.com/carbon-language/carbon-lang/pull/2376) - [#2483: Replace keyword `is` with `impls`](https://github.com/carbon-language/carbon-lang/pull/2483) - [#2687: Termination algorithm for impl selection](https://github.com/carbon-language/carbon-lang/pull/2687) - [#2760: Consistent `class` and `interface` syntax](https://github.com/carbon-language/carbon-lang/pull/2760) - [#2964: Expression phase terminology](https://github.com/carbon-language/carbon-lang/pull/2964) - [#3162: Reduce ambiguity in terminology](https://github.com/carbon-language/carbon-lang/pull/3162) ================================================ FILE: docs/design/generics/goals.md ================================================ # Generics: Goals ## Table of contents - [Purpose of this document](#purpose-of-this-document) - [Background](#background) - [Compile-time parameters](#compile-time-parameters) - [Interfaces](#interfaces) - [Templates](#templates) - [Checked-generic goals](#checked-generic-goals) - [Use cases](#use-cases) - [Generic programming](#generic-programming) - [Upgrade path from C++ abstract interfaces](#upgrade-path-from-c-abstract-interfaces) - [Dependency injection](#dependency-injection) - [Checked generics instead of open overloading and ADL](#checked-generics-instead-of-open-overloading-and-adl) - [Performance](#performance) - [Better compiler experience](#better-compiler-experience) - [Encapsulation](#encapsulation) - [Predictability](#predictability) - [Dispatch control](#dispatch-control) - [Upgrade path from templates](#upgrade-path-from-templates) - [Path from regular functions](#path-from-regular-functions) - [Coherence](#coherence) - [No novel name lookup](#no-novel-name-lookup) - [Learn from others](#learn-from-others) - [Interfaces are nominal](#interfaces-are-nominal) - [Interop and evolution](#interop-and-evolution) - [Bridge for C++ customization points](#bridge-for-c-customization-points) - [What we are not doing with checked generics](#what-we-are-not-doing-with-checked-generics) - [Not the full flexibility of templates](#not-the-full-flexibility-of-templates) - [Checked generics will be checked when defined](#checked-generics-will-be-checked-when-defined) - [Implementation strategy](#implementation-strategy) - [References](#references) ## Purpose of this document This document attempts to clarify our goals for the design of the generics feature for Carbon. While these are not strict requirements, they represent the yardstick by which we evaluate design decisions. We do expect to achieve most of these goals, though some of these goals are somewhat aspirational or forward-looking. ## Background Carbon will support both [checked and template generics](terminology.md#checked-versus-template-parameters) to support generic programming by way of [compile-time parameterization of language constructs](terminology.md#generic-means-compile-time-parameterized). Carbon's checked generics will feature [early type checking](terminology.md#early-versus-late-type-checking) and [complete definition checking](terminology.md#complete-definition-checking). Carbon's [template generics](#templates), in contrast, will more closely follow the [compile-time duck typing](https://en.wikipedia.org/wiki/Duck_typing#Templates_or_generic_types) approach of C++ templates. ### Compile-time parameters Generic functions and generic types will all take some compile-time parameters, which will frequently be types, and in some cases will be [deduced](terminology.md#deduced-parameter) from the types of the values of explicit parameters. If a compile-time parameter is a type, the generic function's signature can specify constraints that the caller's type must satisfy. For example, a resizable array type (like C++'s `std::vector`) might have a compile-time type parameter with the constraint that the type must be movable and have a static size. A sort function might apply to any array whose elements are comparable and movable. A constraint might involve multiple compile-time parameters. For example, a merge function might apply to two arbitrary containers so long as their elements have the same type. ### Interfaces We need some way to express the constraints on a compile-time type parameter. In Carbon we express these "type constraints" by saying we restrict to types that implement specific [_interfaces_](terminology.md#interface). Interfaces describe an API a type could implement; for example, it might specify a set of functions, including names and signatures. A type implementing an interface may be passed as a compile-time type argument to a function that has that interface as a requirement of its compile-time type parameter. Then, the functions defined in the interface may be called in the body of the function. Further, interfaces have names that allow them to be reused. Similar compile-time and run-time constructs may be found in other programming languages: - [Rust's traits](https://doc.rust-lang.org/book/ch10-02-traits.html) - [Swift's protocols](https://docs.swift.org/swift-book/LanguageGuide/Protocols.html) - [Java interfaces]() - [C++ concepts]() (compile-time only) - [Abstract base classes]() in C++, etc. (run-time only) - [Go interfaces](https://gobyexample.com/interfaces) In addition to specifying the methods available on a type, we may in the future expand the role of interfaces to allow other type constraints, such as on size, prefix of the data layout, specified method implementations, tests that must pass, etc. This might be part of making interfaces as expressive as classes, as part of a strategy to migrate to a future version of Carbon that uses interfaces instead of, rather than in addition to, standard inheritance-and-classes object-oriented language support. For the moment, everything beyond specifying the _methods_ available is out of scope. ## Templates The entire idea of statically typed languages is that coding against specific types and interfaces is a better model and experience. Unfortunately, templates don't provide many of those benefits to programmers until it's too late, when users are consuming the API. Templates also come with high overhead, such as [template error messages](#better-compiler-experience). We want Carbon code to move towards more rigorously type-checked constructs. However, existing C++ code is full of unrestricted usage of compile-time duck-typed templates. They are incredibly convenient to write and so likely will continue to exist for a long time. Carbon will have direct support for templates in addition to checked generics. Carbon's template system will be similar to C++ templates with some specific changes: - It may have some limitations to be more compatible with checked generics, much like how we [restrict overloading](#checked-generics-instead-of-open-overloading-and-adl). - We likely will have a different method of selecting between different template instantiations, since [SFINAE](https://en.wikipedia.org/wiki/Substitution_failure_is_not_an_error) makes it difficult to deliver high quality compiler diagnostics. Other aspects of Carbon will reduce the number of situations where errors will only be detected after the template is [instantiated](terminology.md#instantiation): - Carbon's grammar won't require type information to disambiguate parsing, and so definitions may be parsed without knowing the values of template arguments. - Carbon's name resolution is more restricted. Every package has its own namespace, and there will be no [argument-dependent lookup](https://en.cppreference.com/w/cpp/language/adl). ## Checked-generic goals Our goal for checked generics support in Carbon is to get most of the expressive benefits of C++ templates and open overloading with fewer downsides. Additionally, we want to support some dynamic dispatch use cases; for example, in cases that inheritance struggles with. ### Use cases To clarify the expressive range we are aiming for, here are some specific use cases we expect Carbon checked generics to cover. #### Generic programming We in particular want to support [generic programming](https://en.wikipedia.org/wiki/Generic_programming), including: - Containers: arrays, maps, lists, and more complicated data structures like trees and graphs - Algorithms: sort, search - Wrappers: optional, variant, expected/result, smart pointers - Parameterized numeric types: `std::complex` - Configurable and parametric APIs: the storage-customized `std::chrono` APIs - [Policy-based design](https://en.wikipedia.org/wiki/Modern_C%2B%2B_Design#Policy-based_design) These would generally involve static, compile-time type arguments, and so would generally be used with [static dispatch](#dispatch-control). #### Upgrade path from C++ abstract interfaces Interfaces in C++ are often represented by abstract base classes. Checked generics should offer an alternative that does not rely on inheritance. This means looser coupling and none of the problems of multiple inheritance. Some people, such as [Sean Parent](https://sean-parent.stlab.cc/papers-and-presentations/#better-code-runtime-polymorphism), advocate for runtime polymorphism patterns in C++ that avoid inheritance because it can cause runtime performance, correctness, and code maintenance problems in some situations. Those patterns require a lot of boilerplate and complexity in C++. It would be nice if those patterns were simpler to express with Carbon checked generics. More generally, Carbon checked generics will provide an alternative for those situations inheritance doesn't handle as well. As a specific example, we would like Carbon checked generics to supplant the need to support multiple inheritance in Carbon. This is a case that would use [dynamic dispatch](#dispatch-control). #### Dependency injection Types which only support subclassing for test stubs and mocks, as in ["dependency injection"](https://en.wikipedia.org/wiki/Dependency_injection), should be able to easily migrate to checked generics. This extends outside the realm of testing, allowing general configuration of how dependencies can be satisfied. For example, checked generics might be used to configure how a library writes logs. This would allow you to avoid the runtime overhead of virtual functions, using [static dispatch](#dispatch-control) without the [poor build experience of templates](#better-compiler-experience). #### Checked generics instead of open overloading and ADL One name lookup problem we would like to avoid is caused by open overloading. Overloading is where you provide multiple implementations of a function with the same name, and the implementation used in a specific context is determined by the argument types. Open overloading is overloading where the overload set is not restricted to a single file or library. This works with [Argument-dependent lookup](https://en.wikipedia.org/wiki/Argument-dependent_name_lookup), or [ADL](https://en.cppreference.com/w/cpp/language/adl), a mechanism for enabling open overloading without having to reopen the namespace where the function was originally defined. Together these enable [C++ customization points](http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/n4381.html). This is commonly used to provide a type-specific implementation of some operation, but doesn't provide any enforcement of consistency across the different overloads. It makes the meaning of code dependent on which overloads are imported, and is at odds with being able to type check a function's definition before instantiation. Our goal is to address this use case, known more generally as [the expression problem](https://eli.thegreenplace.net/2016/the-expression-problem-and-its-solutions), with a checked-generics mechanism that does enforce consistency so that type checking is possible without seeing all implementations. This will be Carbon's replacement for open overloading. This is encapsulated in Carbon's ["one static open extension mechanism" principle](/docs/project/principles/static_open_extension.md). As a consequence, Carbon checked generics will need to be able to support operator overloading. A specific example is the absolute value function `Abs`. We would like to write `Abs(x)` for a variety of types. For some types `T`, such as `Int32` or `Float64`, the return type will be the same `T`. For other types, such as `Complex64` or `Quaternion`, the return type will be different. Checked generic functions that call `Abs` will need a way to specify whether they only operate on `T` such that `Abs` has signature `T -> T`. This does create an issue when interoperating with C++ code using open overloading, which will [need to be addressed](#bridge-for-c-customization-points). ### Performance For any real-world C++ template, there shall be an idiomatic reformulation in Carbon checked generics that has equal or better performance. [Performance is the top priority for Carbon](/docs/project/goals.md#performance-critical-software), and we expect to use checked generics pervasively, and so they can't compromise that goal in release builds. **Nice to have:** There are cases where we should aim to do better than C++ templates. For example, the additional structure of checked generics should make it easier to reduce generated code duplication, reducing code size and cache misses. ### Better compiler experience Compared to C++ templates, we expect to reduce build times, particularly in development builds. We also expect the compiler to be able to report clearer errors, and report them earlier in the build process. One source of improvement is that the bodies of checked generic functions and types can be type checked once when they are defined, instead of every time they are used. This is both a reduction in the total work done, and how errors can be reported earlier. On use, the errors can be a lot clearer since they will be of the form "argument did not satisfy function's contract as stated in its signature" instead of "substitution failed at this line of the function's implementation." **Nice to have:** In development builds, we will have the option of using [dynamic dispatch](#dispatch-control) to reduce build times. We may also be able to reduce the amount of redundant compilation work even with the [static strategy](#dispatch-control) by identifying instantiations with the same arguments or identical implementations and only generating code for them once. ### Encapsulation With a template, the implementation is part of the interface and types are only checked when the function is called and the template is instantiated. A checked-generic function is type checked when it is defined, and type checking can't use any information that is only known when the function is instantiated such as the exact argument types. Furthermore, calls to a checked-generic function may be type checked using only its declaration, not its body. ### Predictability A general property of checked generics is they are more predictable than templates. They make clear when a type satisfies the requirements of a function; they have a documented contract. Further, that contract is enforced by the compiler, not sensitive to implementation details in the function body. This eases evolution by reducing (but not eliminating) the impact of [Hyrum's law](https://www.hyrumslaw.com/). **Nice to have:** We also want well-defined boundaries between what is legal and not. This is "will my code be accepted by the compiler" predictability. We would prefer to avoid algorithms in the compiler with the form "run for up to N steps and report an error if it isn't resolved by then." For example, C++ compilers will typically have a template recursion limit. With checked generics, these problems arise due to trying to reason whether something is legal in all possible instantiations, rather than with specific, concrete types. Some of this is likely unavoidable or too costly to avoid, as most existing checked generics systems [have undecidable aspects to their type system](https://web.archive.org/web/20231223005655/https://3fx.ch/typing-is-hard.html), including [Rust](https://sdleffler.github.io/RustTypeSystemTuringComplete/) and Swift ([1](https://forums.swift.org/t/swift-type-checking-is-undecidable/39024), [2](https://forums.swift.org/t/termination-checking-for-type-substitution/64504), [3](https://forums.swift.org/t/two-more-undecidable-problems-in-the-swift-type-system/64814), [4](https://forums.swift.org/t/brainf-in-the-swift-type-system/68301)). We fully expect there to be metaprogramming facilities in Carbon that will be able to execute arbitrary Turing machines, with infinite loops and undecidable stopping criteria. We don't see this as a problem though, just like we don't worry about trying to make the compiler reliably prevent you from writing programs that don't terminate. We _would_ like to distinguish "the executed steps are present in the program's source" from "the compiler has to search for a proof that the code is legal." In the former case, the compiler can surface a problem to the user by pointing to lines of code in a trace of execution. The user could employ traditional debugging techniques to refine their understanding until they can determine a fix. What we want to avoid is the latter case, since it has bad properties: - Error messages end up in the form: "this was too complicated to figure out, I eventually gave up." - Little in the way of actionable feedback on how to fix problems. - Not much the user can do to debug problems. - If the compiler is currently right at a limit for figuring something out, it is easy to imagine a change to a distant dependency can cause it to suddenly stop compiling. If we can't find acceptable restrictions to make problems efficiently decidable, the next best solution is to require the proof to be in the source instead of derived by the compiler. If authoring the proof is too painful for the user, the we should invest in putting the proof search into IDEs or other tooling. ### Dispatch control Enable simple user control of whether to use dynamic or static dispatch. **Implementation strategy:** There are two strategies for generating code for checked-generic functions: - Static strategy: Like template parameters, the values for checked parameters must be statically known at the callsite, or known to be a compile-time parameter to the calling function. This can generate separate, specialized versions of each combination of checked and template arguments, in order to optimize for those types or values. - Dynamic strategy: This is when the compiler generates a single version of the function that uses runtime dispatch to get something semantically equivalent to separate instantiation, but likely with different size, build time, and performance characteristics. By default, we expect the implementation strategy to be controlled by the compiler, and not semantically visible to the user. For example, the compiler might use the static strategy for release builds and the dynamic strategy for development. Or it might choose between them on a more granular level based on code analysis, specific features used in the code, or profiling -- maybe some specific specializations are needed for performance, but others would just be code bloat. We require that all checked generic functions can be compiled using the static specialization strategy. For example, the values for checked parameters must be statically known at the callsite. Other limitations are [listed below](#implementation-strategy). **Nice to have:** It is desirable that the majority of functions with checked parameters also support the dynamic strategy. Specific features may prevent the compiler from using the dynamic strategy, but they should ideally be relatively rare, and easy to identify. Language features should avoid making it observable whether function code generated once or many times. For example, you should not be able to take the address of a function with checked parameters, or determine if a function was instantiated more than once using function-local static variables. There are a few obstacles to supporting dynamic dispatch efficiently, which may limit the extent it is used automatically by implementations. For example, the following features would benefit substantially from guaranteed monomorphization: - Field packing in class layout. For example, packing a `bool` into the lower bits of a pointer, or packing bit-fields with checked-parameter widths. - Allocating local variables in stack storage. Without monomorphization, we would need to perform dynamic memory allocation -- whether on the stack or the heap -- for local variables whose sizes depend on checked parameters. - Passing parameters to functions. We cannot pass values of types in registers. - Finding [specialized](terminology.md#specialization) implementations of interfaces beyond those clearly needed from the function signature. While it is possible to address these with dynamic dispatch, handling some of them might have far-reaching and surprising performance implications. We don't want to compromise our goal for predictable performance. We will allow the user to explicitly opt-in to using the dynamic strategy in specific cases. This could be just to control binary size in cases the user knows are not performance sensitive, or it could be to get the additional capability of operating on values with dynamic types. We may need to restrict this in various ways to maintain efficiency, like Rust does with object-safe traits. We also anticipate that the user may want to force the compiler to use the static strategy in specific cases. This might be to keep runtime performance acceptable even when running a development or debug build. ### Upgrade path from templates We want there to be a natural, incremental upgrade path from templated code to checked-generic code. The first step of migrating C++ template code would be to first convert it to a [Carbon template](#templates). The problem is then how to convert templates to checked generics within Carbon. This gives us these sub-goals: - Users should be able to convert a single template parameter to be checked at a time. A hybrid function with both template and checked parameters has all the limitations of a template function: it can't be completely definition checked, it can't use the dynamic strategy, etc. Even so, there are still benefits from enforcing the function's declared contract for those parameters that have been converted. - Converting from a template parameter to a checked parameter should be safe. It should either work or fail to compile, never silently change semantics. - We should minimize the effort to convert functions and types from templated to checked. Ideally it should just require specifying the type constraints, affecting just the signature of the function, not its body. - **Nice to have:** It should be legal to call templated code from checked-generic code when it would have the same semantics as if called from non-generic code, and an error otherwise. This is to allow more templated functions to be converted to checked generics, instead of requiring them to be converted specifically in bottom-up order. - **Nice to have:** Provide a way to migrate from a template to a checked generic without immediately updating all of the types used with the template. For example, if the checked-generic code requires types to implement a new interface, one possible solution would use the original template code to provide an implementation for that interface for any type that structurally has the methods used by the original template. ### Path from regular functions Replacing a regular, non-parameterized function with a checked-generic function should not affect existing callers of the function. There may be some differences, such as when taking the address of the function, but ordinary calls should not see any difference. In particular, the return type of a checked-generic function should match, without any type erasure or additional named members. ### Coherence We want the generics system to have the [_coherence_ property](terminology.md#coherence), so that the implementation of an interface for a type is well defined. Since a checked-generic function only depends on interface implementations, they will always behave consistently on a given type, independent of context. For more on this, see [this description of what coherence is and why Rust enforces it](https://github.com/Ixrec/rust-orphan-rules#what-is-coherence). Coherence greatly simplifies the language design, since it reduces the need for complicated rules to picking an implementation when there are many candidates. It also has a number of benefits for users: - It removes a way packages can conflict with each other. - It makes the behavior of code more consistent and predictable. - It means there is no need to provide a disambiguation mechanism. Disambiguation is particularly problematic since the ambiguous call is often in generic code rather than code you control. - A consistent definition of a type is useful for instantiating a C++ or Carbon template on that type. The main downside of coherence is that there are some capabilities we would like for interfaces that are in tension with having an orphan rule limiting where implementations may be defined. For example, we would like to address [the expression problem](https://eli.thegreenplace.net/2016/the-expression-problem-and-its-solutions#another-clojure-solution-using-protocols). We can get some of the way there by allowing the implementation of an interface for a type to be defined with either the interface or the type. But some use cases remain: - They should be some way of selecting between multiple implementations of an interface for a given type. For example, a _Song_ might support multiple orderings, such as by title or by artist. These would be represented by having multiple implementations of a _Ordered_ interface. - In order to allow libraries to be composed, there must be some way of saying a type implements an interface that is in another package that the authors of the type were unaware of. This is especially important since the library a type is defined in may not be able to see the interface definition without creating a dependency cycle or layering violation. We should have some mechanism for addressing these use cases. There are multiple approaches that could work: - Interface implementations could be external to types and are passed in to checked-generic functions separately. - There could be some way to create multiple types that are compatible with a given value that you can switch between using casts to select different interface implementations. This is the approach used by Rust ([1](https://doc.rust-lang.org/book/ch19-03-advanced-traits.html#using-the-newtype-pattern-to-implement-external-traits-on-external-types), [2](https://github.com/Ixrec/rust-orphan-rules#user-content-why-are-the-orphan-rules-controversial)). Alternatives to coherence are discussed in [an appendix](appendix-coherence.md). ### No novel name lookup We want to avoid adding rules for name lookup that are specific to generics. This is in contrast to Rust which has different lookup rules inside its traits. Instead, we should structure generics in a way that reuses existing name lookup facilities of the language. **Nice to have:** One application of this that would be nice to have is if the names of a type's members were all determined by a type's definition. So if `x` has type `T`, then if you write `x.y` you should be able to look up `y` in the definition of `T`. This might need to be somewhat indirect in some cases. For example, if `T` inherits from `U`, the name `y` might come from `U` and not be mentioned in the definition of `T` directly. We may have similar mechanisms where `T` gets methods that have default implementations in interfaces it implements, as long as the names of those interfaces are explicitly mentioned in the definition of `T`. ### Learn from others Many languages have implemented checked-generics systems, and we should learn from those experiences. We should copy what works and makes sense in the context of Carbon, and change decisions that led to undesirable compromises. We are taking the strongest guidance from Rust and Swift, which have similar goals and significant experience with the implementation and usability of checked generics. They both use nominal interfaces, were designed with checked generics from the start, and produce native code. Contrast with Go which uses structural interfaces, or Java which targets a virtual machine that predated its generics feature. For example, Rust has found that supporting defaults for interface methods is a valuable feature. It is useful for [evolution](#interop-and-evolution), implementation reuse, and for bridging the gap between the minimal functionality a type wants to implement and the rich API that users want to consume ([example](https://doc.rust-lang.org/std/iter/trait.Iterator.html)). We still have the flexibility to make simplifications that Rust cannot because they need to maintain compatibility. We could remove the concept of `fundamental` and explicit control over which methods may be specialized. These are complicated and [impose coherence restrictions](http://aturon.github.io/tech/2017/02/06/specialization-and-coherence/). ### Interfaces are nominal Interfaces can either be [structural](terminology.md#structural-interfaces), as in Go, or [nominal](terminology.md#nominal-interfaces), as in Rust and Swift. Structural interfaces match any type that has the required methods, whereas nominal interfaces only match if there is an explicit declaration stating that the interface is implemented for that specific type. Carbon will support nominal interfaces, allowing them to designate _semantics_ beyond the basic structure of the methods. This means that interfaces implicitly specify the intended semantics and invariants of and between those functions. Unlike the function signatures, this contract is between the implementers and the consumers of interfaces and is not enforced by Carbon itself. For example, a `Draw` method would mean different things when it is part of a `GameResult` interface versus an `Image2D` interface, even if those methods happen to have the same signature. ### Interop and evolution [Evolution is a high priority for Carbon](/docs/project/goals.md#software-and-language-evolution), and so will need mechanisms to support evolution when using checked generics. New additions to an interface might: - need default implementations - be marked "upcoming" to allow for a period of transition - replace other APIs that need to be marked "deprecated" Experience with C++ concepts has shown that interfaces are [hard to evolve](https://www.youtube.com/watch?v=v_yzLe-wnfk) without these kinds of supporting language mechanisms. Otherwise changes to interfaces need to made simultaneously with updates to types that implement the interface or functions that consume it. Another way of supporting evolution is to allow one interface to be substitutable for another. For example, a feature that lets you use an implementation of `Interface1` for a type to automatically get an implementation of `Interface2`, as well as the other way around, would help transitioning between those two interfaces. Evolution in particular means that the set of names in an interface can change, and so two interfaces that don't start with name conflicts can develop them. To handle name conflicts, interfaces should be separate, isolated namespaces. We should provide mechanisms to allow one type to implement two interfaces that accidentally use the same name for different things, and for functions to use interfaces with name conflicts together on a single type. Contrast this with Swift, where a type can only supply one associated type of a given name even when implementing multiple protocols. Similarly a function in Swift with a given name and signature can only have a single implementation for a type. Note this is possible since [interfaces are nominal](#interfaces-are-nominal). The place where types specify that they implement an interface is also the vehicle for unambiguously designating which function implementation goes with what interface. ### Bridge for C++ customization points There will need to be some bridge for C++ extension points that currently rely on open overloading or [ADL](https://en.wikipedia.org/wiki/Argument-dependent_name_lookup). For example, we need some way for C++ [customization points](http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/n4381.html) like `swap` to work on Carbon types. We might define `CPlusPlus.ADL.swap` as a Carbon interface to be that bridge. Carbon types could implement that interface to work from C++, and Carbon functions could use that interface to invoke `swap` on C++ types. Similarly, we will want some way to implement Carbon interfaces for C++ types. For example, we might have a template implementation of an `AddWith` interface for any C++ type that implements `operator+`. ## What we are not doing with checked generics What are we **not** doing with checked generics, particularly things that some other languages do? ### Not the full flexibility of templates Checked generics don't need to provide full flexibility of C++ templates: - [Carbon templates](#templates) will cover those cases that don't fit inside checked generics, such as code that relies on compile-time duck typing. - We won't allow a specialization of some interface for some particular type to actually expose a _different_ interface, with different methods or different types in method signatures. This would break modular type checking. - [Template metaprogramming](https://en.wikipedia.org/wiki/Template_metaprogramming) will not be supported by Carbon checked generics. We expect to address those use cases with metaprogramming or [templates](#templates) in Carbon. - [Expression templates](https://en.wikipedia.org/wiki/Expression_templates) are out of scope. It may be possible to express this approach in Carbon's checked-generics system, but they won't drive any accommodation in the checked-generics design. ### Checked generics will be checked when defined C++ compilers must defer full type checking of templates until they are instantiated by the user. Carbon will not defer type checking of checked-generic definitions. ### Implementation strategy We want all generic Carbon code to support [static dispatch](#dispatch-control). This means we won't support unbounded type families. Unbounded type families are when recursion creates an infinite collection of types, such as in [this example from Swift](https://forums.swift.org/t/ergonomics-generic-types-conforming-in-more-than-one-way/34589/71) or: ```carbon fn Sort[T:! Ordered](list: List(T)) -> List(T) { if (list.size() == 1) return list; var chunks: List(List(T)) = FormChunks(list, sqrt(list.size())); chunks = chunks.ApplyToEach(Sort); chunks = Sort(chunks); return MergeSortedListOfSortedLists(chunks); } ``` This, given an implementation of `Ordered` for any list with elements that are themselves `Ordered`, would recursively call itself to produce a set of types without bound. That is, calling `Sort` on a `List(Int)` would internally call `Sort` on a `List(List(Int))` and so on recursively without any static limit. We won't require all checked-generic Carbon code to support dynamic dispatch, but we would like it to be an implementation option for the compiler in the majority of cases. Lastly, runtime specialization is out of scope as an implementation strategy. That is, some language runtimes JIT a specialization when it is first needed, but it is not a goal for Carbon to support such an implementation strategy. ## References Proposals: - [#24: Generics goals](https://github.com/carbon-language/carbon-lang/pull/24) - [#950: Generic details 6: remove facets](https://github.com/carbon-language/carbon-lang/pull/950) - [#2138: Checked and template generic terminology](https://github.com/carbon-language/carbon-lang/pull/2138) ================================================ FILE: docs/design/generics/overview.md ================================================ # Generics: Overview This document is a high-level description of Carbon's generics design, with pointers to other design documents that dive deeper into individual topics. ## Table of contents - [Goals](#goals) - [Summary](#summary) - [What are generics?](#what-are-generics) - [Interfaces](#interfaces) - [Defining interfaces](#defining-interfaces) - [Contrast with templates](#contrast-with-templates) - [Implementing interfaces](#implementing-interfaces) - [Accessing members of interfaces](#accessing-members-of-interfaces) - [Facet types](#facet-types) - [Generic functions](#generic-functions) - [Deduced parameters](#deduced-parameters) - [Facet parameters](#facet-parameters) - [Requiring or extending another interface](#requiring-or-extending-another-interface) - [Combining interfaces](#combining-interfaces) - [Named constraints](#named-constraints) - [Type erasure](#type-erasure) - [Adapting types](#adapting-types) - [Interface inputs and outputs](#interface-inputs-and-outputs) - [Associated constants](#associated-constants) - [Parameterized interfaces](#parameterized-interfaces) - [Constraints](#constraints) - [Parameterized impl declarations](#parameterized-impl-declarations) - [Operator overloading](#operator-overloading) - [Future work](#future-work) - [References](#references) ## Goals Carbon [generics](terminology.md#generic-means-compile-time-parameterized) supports generalizing code to apply to more situations by adding compile-time parameters, allowing [generic programming](https://en.wikipedia.org/wiki/Generic_programming). Carbon supports both [checked and template](terminology.md#checked-versus-template-parameters) generics. Template generics provide a similar model to C++ templates, to help with interop and migration. They can be more convenient to write, and support some use cases, like [metaprogramming](https://en.wikipedia.org/wiki/Metaprogramming), that are difficult with checked generics. Checked generics are an alternative that has advantages including: - Function calls and bodies are checked independently against the function signatures. - Clearer and earlier error messages. - Fast builds, particularly development builds. - Support for both static and dynamic dispatch. Checked generics do have some restrictions, but are expected to be more appropriate at public API boundaries than templates. For more detail, see [the detailed discussion of generics goals](goals.md) and [generics terminology](terminology.md). ## Summary Summary of how Carbon generics work: - _Generics_ are compile-time parameterized functions, types, and other language constructs. Those parameters allow a single definition to apply more generally. They are used to avoid writing specialized, near-duplicate code for similar situations. - The definition of a _checked_ generic is typechecked once, without having to know the specific argument values of the generic parameters it is instantiated with. Typechecking the definition of a checked generic requires a precise contract specifying the requirements on the argument values. - For parameters that will be used as types, those requirements are written using _interfaces_. Interfaces have a name and describe methods, functions, and other entities for types to implement. - Types must explicitly _implement_ interfaces to indicate that they support their functionality. A given type may implement an interface at most once. - Implementations may be declared inline in the body of a class definition or out-of-line. - Types may _extend_ an implementation declared inline, in which case you can directly call the interface's methods on those types. - Out-of-line implementations may be defined in the library defining the interface as an alternative to the type, or [the library defining a type argument](#parameterized-impl-declarations). - Interfaces may be used as the type of a generic parameter. Interfaces are _facet types_, whose values are the subset of all types that implement the interface. Facet types in general specify the capabilities and requirements of the type. The value of a interface is called a _facet_. Facets are not types, but are usable as types. - With a template generic, the concrete argument value used by the caller is used for name lookup and typechecking. With checked generics, that is all done with the declared restrictions expressed as the types of bindings in the declaration. Inside the body of a checked generic with a facet parameter, the API of the facet is just the names defined by the facet type. - _Deduced parameters_ are parameters whose values are determined by the types of the explicit arguments. Generic facet parameters are typically deduced. - A function with a generic parameter can have the same function body as an unparameterized one. Functions can freely mix checked, template, and regular parameters. - Interfaces can require other interfaces be implemented. - Interfaces can [extend](terminology.md#extending-an-interface) required interfaces. - The `&` operation on facet types allows you conveniently combine interfaces. It gives you all the names that don't conflict. - You may also declare a new facet type directly using ["named constraints"](terminology.md#named-constraints). Named constraints can express requirements that multiple interfaces be implemented, and give you control over how name conflicts are handled. - Alternatively, you may resolve name conflicts by using a qualified member access expression to directly call a function from a specific interface using a qualified name. ## What are generics? Generics are a mechanism for writing parameterized code that applies generally instead of making near-duplicates for very similar situations, much like C++ templates. For example, instead of having one function per type-you-can-sort: ``` fn SortInt32Vector(a: Vector(i32)*) { ... } fn SortStringVector(a: Vector(String)*) { ... } ... ``` You might have one generic function that could sort any array with comparable elements: ``` fn SortVector(T:! Comparable, a: Vector(T)*) { ... } ``` The syntax above adds a `!` to indicate that the parameter named `T` is compile-time. By default compile-time parameters are _checked_, the `template` keyword may be added to make it a _template generic_. Given an `i32` vector `iv`, `SortVector(i32, &iv)` is equivalent to `SortInt32Vector(&iv)`. Similarly for a `String` vector `sv`, `SortVector(String, &sv)` is equivalent to `SortStringVector(&sv)`. Thus, we can sort any vector containing comparable elements using this single `SortVector` function. This ability to generalize makes `SortVector` a _generic_. ### Interfaces The `SortVector` function requires a definition of `Comparable`, with the goal that the compiler can perform checking. This has two pieces: - definition checking: completely type check a checked-generic definition without information from calls; - encapsulation: completely type check a call to a generic with information only from the function's signature, and not from its body. For Rust, this is called "[Rust's Golden Rule](https://steveklabnik.com/writing/rusts-golden-rule)." In this example, `Comparable` is an _interface_. Interfaces describe all the requirements needed for the type `T`. Given that the compiler knows `T` satisfies those requirements, it can type check the body of the `SortVector` function. This includes checking that the `Comparable` requirement covers all of the uses of `T` inside the function. Later, when the compiler comes across a call to `SortVector`, it can type check against the requirements expressed in the function's signature. Using only the types at the call site, the compiler can check that the member elements of the passed-in array satisfy the function's requirements. There is no need to look at the body of the `SortVector` function, since we separately checked that those requirements were sufficient. #### Defining interfaces Interfaces, then, have a name and describe methods, functions, and other entities for types to implement. Example: ``` interface Comparable { // `Less` is an associated method. fn Less[self: Self](rhs: Self) -> bool; } ``` Functions and methods may be given a default implementation by prefixing the declaration with `default` and putting the function body in curly braces `{`...`}` in place of the terminating `;` of the function declaration. To prevent that implementation from being overridden, use `final` instead of `default`. Interfaces describe functionality, but not data; no variables may be declared in an interface. #### Contrast with templates Contrast these checked generics with a Carbon or C++ template, where the compiler may be able to do some checking given a function definition, but more checking of the definition is required after seeing the call sites once all the [instantiations](terminology.md#instantiation) are known. Note: [Generics terminology](terminology.md) goes into more detail about the [differences between checked and template generic parameters](terminology.md#checked-versus-template-parameters). ### Implementing interfaces Interfaces themselves only describe functionality by way of method descriptions. A type needs to _implement_ an interface to indicate that it supports its functionality. A given type may implement an interface at most once. Consider this interface: ``` interface Printable { fn Print[self: Self](); } ``` The `interface` keyword is used to define a [_nominal interface_](terminology.md#nominal-interfaces). That means that types need to explicitly implement them, using an `impl` block, such as here: ``` class Song { // ... // Implementing `Printable` for `Song` inside the definition of `Song` // with the keyword `extend` means all names of `Printable`, such // as `F`, are included as a part of the `Song` API. extend impl as Printable { // Could use `Self` in place of `Song` here. fn Print[self: Song]() { ... } } } // Implement `Comparable` for `Song` without changing the API of `Song` // using an `impl` declaration without `extend`. This may be defined in // either the library defining `Song` or `Comparable`. impl Song as Comparable { // Could use either `Self` or `Song` here. fn Less[self: Self](rhs: Self) -> bool { ... } } ``` Implementations may be defined within the class definition itself or out-of-line. Implementations may optionally start with the `extend` keyword to say the members of the interface are also members of the class, which may only be used in a class scope. Out-of-line implementations may be defined in the library defining the class, the interface, or [a type argument](#parameterized-impl-declarations). #### Accessing members of interfaces Methods from an interface that a class extends may be called with the [simple member access syntax](terminology.md#simple-member-access). Methods of all implemented interfaces may be called with a [qualified member access expression](terminology.md#qualified-member-access-expression), whether the class extends them or not. ``` var song: Song; // `song.Print()` is allowed, unlike `song.Play()`. song.Print(); // `Less` is defined in `Comparable`, which `Song` // does not extend the implementation of. song.(Comparable.Less)(song); // Can also call `Print` using a qualified member // access expression, using the compound member access // syntax with the qualified name `Printable.Print`: song.(Printable.Print)(); ``` ### Facet types To type check a function, the compiler needs to be able to verify that uses of a value match the capabilities of the value's type. In `SortVector`, the parameter `T` is a type, but that type is a checked-generic, or _symbolic_, parameter. That means that the specific type value assigned to `T` is not known when type checking the `SortVector` function. Instead it is the constraints on `T` that let the compiler know what operations may be performed on values of type `T`. Those constraints are represented by the type of `T`, a [**_facet type_**](terminology.md#facet-type). In general, a facet type describes the capabilities of a type, while a type defines specific implementations of those capabilities. An interface, like `Comparable`, may be used as a facet type. In that case, the constraint on the type is that it must implement the interface `Comparable`. A facet type also defines a set of names and a mapping to corresponding qualified names. Those names are used for [simple member lookup](terminology.md#simple-member-access) in scopes where the value of the type is not known, such as when the type is a generic parameter. You may combine interfaces into new facet types using [the `&` operator](#combining-interfaces) or [named constraints](#named-constraints). ### Generic functions We want to be able to call generic functions just like ordinary functions, and write generic function bodies like ordinary functions. There are only a few differences, like that you can't take the address of generic functions. #### Deduced parameters This `SortVector` function is explicitly providing type information that is already included in the type of the second argument. To eliminate the argument at the call site, use a _deduced parameter_. ``` fn SortVectorDeduced[T:! Comparable](a: Vector(T)*) { ... } ``` The `T` parameter is defined in square brackets before the explicit parameter list in parenthesis to indicate it should be deduced. This means you may call the function without the type argument, just like the ordinary functions `SortInt32Vector` or `SortStringVector`: ``` SortVectorDeduced(&anIntVector); // or SortVectorDeduced(&aStringVector); ``` and the compiler deduces that the `T` argument should be set to `i32` or `String` from the type of the argument. Deduced arguments are always determined from the call and its explicit arguments. There is no syntax for specifying deduced arguments directly at the call site. ``` // ERROR: can't determine `U` from explicit parameters fn Illegal[T:! type, U:! type](x: T) -> U { ... } ``` #### Facet parameters A function with a facet parameter can have the same function body as an unparameterized one. ``` fn PrintIt[T:! Printable](p: T*) { p->Print(); } fn PrintIt(p: Song*) { p->Print(); } ``` Inside the function body, you can treat the facet parameter just like any other type. There is no need to refer to or access generic parameters differently because they are defined as generic, as long as you only refer to the names defined by [facet type](#facet-types) for the facet parameter. You may also refer to any of the methods of interfaces required by the facet type using a [qualified member access expression](#accessing-members-of-interfaces). A function can have a mix of checked, template, and regular parameters. Each kind of parameter is defined using a different syntax: a checked parameter is uses a symbolic binding pattern, a template parameter uses a template binding pattern, and a regular parameter uses a runtime binding pattern. Likewise, it's allowed to pass a symbolic or template constant value to a checked or regular parameter. _We have decided to support passing a symbolic constant to a template parameter, see [leads issue #2153: Checked generics calling templates](https://github.com/carbon-language/carbon-lang/issues/2153), but incorporating it into the design is future work._ ### Requiring or extending another interface Interfaces can require other interfaces be implemented: ``` interface Equatable { fn IsEqual[self: Self](rhs: Self) -> bool; } // `Iterable` requires that `Equatable` is implemented. interface Iterable { require Self impls Equatable; fn Advance[ref self: Self](); } ``` The `extend` keyword is used to [extend](terminology.md#extending-an-interface) another interface. If interface `Derived` extends interface `Base`, `Base`'s interface is both required and all its methods are included in `Derived`'s interface. ``` // `Hashable` extends `Equatable`. interface Hashable { extend Equatable; fn Hash[self: Self]() -> u64; } // `Hashable` is equivalent to: interface Hashable { require Self impls Equatable; alias IsEqual = Equatable.IsEqual; fn Hash[self: Self]() -> u64; } ``` A type may implement the base interface implicitly by implementing all the methods in the implementation of the derived interface. ``` class Key { // ... extend impl as Hashable { fn IsEqual[self: Key](rhs: Key) -> bool { ... } fn Hash[self: Key]() -> u64 { ... } } // No need to separately implement `Equatable`. } var k: Key = ...; k.Hash(); k.IsEqual(k); ``` ### Combining interfaces The `&` operation on facet types allows you conveniently combine interfaces. It gives you all the names that don't conflict. ``` interface Renderable { fn GetCenter[self: Self]() -> (i32, i32); // Draw the object to the screen fn Draw[self: Self](); } interface EndOfGame { fn SetWinner[ref self: Self](player: i32); // Indicate the game was a draw fn Draw[ref self: Self](); } fn F[T:! Renderable & EndOfGame](game_state: T*) -> (i32, i32) { game_state->SetWinner(1); return game_state->Center(); } ``` Names with conflicts can be accessed using a [qualified member access expression](#accessing-members-of-interfaces). ``` fn BothDraws[T:! Renderable & EndOfGame](game_state: T*) { game_state->(Renderable.Draw)(); game_state->(GameState.Draw)(); } ``` #### Named constraints You may also declare a new facet type directly using ["named constraints"](terminology.md#named-constraints). Named constraints can express requirements that multiple interfaces be implemented, and give you control over how name conflicts are handled. Named constraints have other applications and capabilities not covered here. ``` constraint Combined { require Self impls Renderable; require Self impls EndOfGame; alias Draw_Renderable = Renderable.Draw; alias Draw_EndOfGame = EndOfGame.Draw; alias SetWinner = EndOfGame.SetWinner; } fn CallItAll[T:! Combined](game_state: T*, int winner) { if (winner > 0) { game_state->SetWinner(winner); } else { game_state->Draw_EndOfGame(); } game_state->Draw_Renderable(); // Can still use a qualified member access expression // for names not defined in the named constraint. return game_state->(Renderable.Center)(); } ``` #### Type erasure Inside a generic function, the API of a facet argument is [erased](terminology.md#type-erasure) except for the names defined in the facet type. An equivalent model is to say an [archetype](terminology.md#archetype) is used for type checking and name lookup when the actual type is not known in that scope. The archetype has members dictated by the facet type. For example: If there were a class `CDCover` defined this way: ``` class CDCover { extend impl as Printable { ... } } ``` it can be passed to this `PrintIt` function: ``` fn PrintIt[T:! Printable](p: T*) { p->Print(); } ``` Inside `PrintIt`, `T` is an archetype with the API of `Printable`. A call to `PrintIt` with a value of type `CDCover` erases everything except the members or `Printable`. This includes the type connection to `CDCover`, so it is illegal to cast from `T` to `CDCover`. ### Adapting types Carbon has a mechanism called [adapting types](terminology.md#adapting-a-type) to create new types that are [compatible](terminology.md#compatible-types) with existing types but with different interface implementations. This could be used to add or replace implementations, or define implementations for reuse. In this example, we have multiple ways of sorting a collection of `Song` values. ``` class Song { ... } class SongByArtist { extend adapt Song; extend impl as Comparable { ... } } class SongByTitle { extend adapt Song; extend impl as Comparable { ... } } ``` Values of type `Song` may be cast to `SongByArtist` or `SongByTitle` to get a specific sort order. ### Interface inputs and outputs [Associated constants and interface parameters](terminology.md#interface-parameters-and-associated-constants) allow function signatures to vary with the implementing type. The biggest difference between these is that associated constants ("outputs") may be deduced from a type, and types can implement the same interface multiple times with different interface parameters ("inputs"). #### Associated constants Expect parts of function signatures that vary in an interface to be associated constants by default. Since associated constants may be deduced, they are more convenient to use. Imagine a `Stack` interface. Different types implementing `Stack` will have different element types: ``` interface Stack { let ElementType:! Movable; fn Push[ref self: Self](value: ElementType); fn Pop[ref self: Self]() -> ElementType; fn IsEmpty[ref self: Self]() -> bool; } ``` `ElementType` is an associated constant of the interface `Stack`. Types that implement `Stack` give `ElementType` a specific value that is some type (really, facet) implementing `Movable`. Functions that accept a type implementing `Stack` can deduce the `ElementType` from the stack type. ``` // ✅ This is allowed, since the type of the stack will determine // `ElementType`. fn PeekAtTopOfStack[StackType:! Stack](s: StackType*) -> StackType.ElementType; ``` #### Parameterized interfaces Parameterized interfaces are commonly associated with overloaded operators. Imagine an interface for determining if two values are equivalent that allows those types to be different. An element in a hash map might have type `Pair(String, i64)` that implements both `Equatable(String)` and `Equatable(Pair(String, i64))`. ``` interface Equatable(T:! type) { fn IsEqual[self: Self](compare_to: T) -> bool; } ``` `T` is a parameter to interface `Equatable`. A type can implement `Equatable` multiple times as long as each time it is with a different value of the `T` parameter. Functions may accept types implementing `Equatable(i32)` or `Equatable(f32)`. Functions can't accept types implementing `Equatable(T)` in general, unless some other parameter determines `T`. ``` // ✅ This is allowed, since the value of `T` is determined by the // `v` parameter. fn FindInVector[T:! type, U:! Equatable(T)](v: Vector(T), needle: U) -> Optional(i32); // ❌ This is forbidden. Since `U` could implement `Equatable` // multiple times, there is no way to determine the value for `T`. // Contrast with `PeekAtTopOfStack` in the associated constant // example. fn CompileError[T:! type, U:! Equatable(T)](x: U) -> T; ``` ### Constraints Facet types can be further constrained using a `where` clause: ``` fn FindFirstPrime[T:! Container where .Element = i32] (c: T, i: i32) -> Optional(i32) { // The elements of `c` have type `T.Element`, which is `i32`. ... } fn PrintContainer[T:! Container where .Element impls Printable](c: T) { // The type of the elements of `c` is not known, but we do know // that type satisfies the `Printable` interface. ... } ``` Constraints limit the types that the generic function can operate on, but increase the knowledge that may be used in the body of the function to operate on values of those types. Constraints are also used when implementing an interface to specify the values of associated constants. ``` class Vector(T:! Movable) { extend impl as Stack where .ElementType = T { ... } } ``` ### Parameterized impl declarations Implementations can be parameterized to apply to multiple types. Those parameters can have constraints to restrict when the implementation applies. When multiple implementations apply, there is a rule to pick which one is considered the most specific: - All parameters in each `impl` declaration are replaced with question marks `?`. This is called the type structure of the `impl` declaration. - Given two type structures, find the first difference when read from left-to-right. The one with a `?` is less specific, the one with a concrete type name in that position is more specific. - If there is more than one `impl` declaration with the most specific type structure, pick the one listed first in the priority ordering. To ensure [coherence](goals.md#coherence), an `impl` may only be declared in a library defining some name from its type structure. If a library defines multiple implementations with the same type structure, they must be listed in priority order in a prioritization block. ### Operator overloading To overload an operator, implement the corresponding interface from the standard library. For example, to define how the unary `-` operator behaves for a type, implement the `Negatable` interface for that type. The interfaces and rewrites used for a given operator may be found in the [expressions design](/docs/design/expressions/README.md). As a convenience, there is a shortcut for defining an implementation that supports any type implicitly convertible to a specified type, using `like`: ``` // Support multiplying values of type `Distance` with // values of type `f64` or any type implicitly // convertible to `f64`. impl Distance as MultipliableWith(like f64) ... ``` ## Future work - Functions should have a way to accept types that vary at runtime. - You should have the ability to mark entities as `upcoming` or `deprecated` to support evolution. - There should be a way to define generic associated and higher-ranked/kinded types. ## References - [#524: Generics overview](https://github.com/carbon-language/carbon-lang/pull/524) - [#731: Generics details 2: adapters, associated types, parameterized interfaces](https://github.com/carbon-language/carbon-lang/pull/731) - [#818: Constraints for generics (generics details 3)](https://github.com/carbon-language/carbon-lang/pull/818) - [#920: Generic parameterized impls (details 5)](https://github.com/carbon-language/carbon-lang/pull/920) - [#950: Generic details 6: remove facets](https://github.com/carbon-language/carbon-lang/pull/950) - [#1013: Generics: Set associated constants using `where` constraints](https://github.com/carbon-language/carbon-lang/pull/1013) - [#1084: Generics details 9: forward declarations](https://github.com/carbon-language/carbon-lang/pull/1084) ================================================ FILE: docs/design/generics/terminology.md ================================================ # Generics: Terminology ## Table of contents - [Generic means compile-time parameterized](#generic-means-compile-time-parameterized) - [Checked versus template parameters](#checked-versus-template-parameters) - [Polymorphism](#polymorphism) - [Parametric polymorphism](#parametric-polymorphism) - [Compile-time duck typing](#compile-time-duck-typing) - [Ad-hoc polymorphism](#ad-hoc-polymorphism) - [Constrained genericity](#constrained-genericity) - [Dependent names](#dependent-names) - [Definition checking](#definition-checking) - [Complete definition checking](#complete-definition-checking) - [Early versus late type checking](#early-versus-late-type-checking) - [Bindings](#bindings) - [Types and `type`](#types-and-type) - [Facet type](#facet-type) - [Facet](#facet) - [Type expression](#type-expression) - [Facet binding](#facet-binding) - [Deduced parameter](#deduced-parameter) - [Interface](#interface) - [Structural interfaces](#structural-interfaces) - [Nominal interfaces](#nominal-interfaces) - [Named constraints](#named-constraints) - [Associated entity](#associated-entity) - [Impl: Implementation of an interface](#impl-implementation-of-an-interface) - [Extending an impl](#extending-an-impl) - [Member access](#member-access) - [Simple member access](#simple-member-access) - [Qualified member access expression](#qualified-member-access-expression) - [Compatible types](#compatible-types) - [Subtyping and casting](#subtyping-and-casting) - [Coherence](#coherence) - [Adapting a type](#adapting-a-type) - [Type erasure](#type-erasure) - [Archetype](#archetype) - [Extending an interface](#extending-an-interface) - [Dynamic-dispatch witness table](#dynamic-dispatch-witness-table) - [Instantiation](#instantiation) - [Specialization](#specialization) - [Template specialization](#template-specialization) - [Checked-generic specialization](#checked-generic-specialization) - [Conditional conformance](#conditional-conformance) - [Interface parameters and associated constants](#interface-parameters-and-associated-constants) - [Type constraints](#type-constraints) - [References](#references) ## Generic means compile-time parameterized Generally speaking, when we talk about _generics_, either [checked or template](#checked-versus-template-parameters), we are talking about generalizing some language construct by adding a _compile-time parameter_, also called a _generic parameter_, to it. So: - a _generic function_ is a function with at least one compile-time parameter, which could be an explicit argument to the function or [deduced](#deduced-parameter); - a _generic type_ is a type with a compile-time parameter, for example a container type parameterized by the type of the contained elements; - a _generic interface_ is an [interface](#interface) with [a compile-time parameter](#interface-parameters-and-associated-constants). This parameter broadens the scope of the language construct on an axis defined by that parameter, for example it could define a family of functions instead of a single one. Note that different languages allow different things to be parameterized; for example, Rust supports [generic associated types](https://rust-lang.github.io/rfcs/1598-generic_associated_types.html). ## Checked versus template parameters When we distinguish between checked and template generics in Carbon, it is on a parameter by parameter basis. A single function can take a mix of regular, checked, and template parameters. - **Regular parameters**, or "dynamic parameters", are designated using the "\`:` \" syntax (or "\"). - **Checked parameters** are designated using `:!` between the name and the type (so it is "\`:!` \"). - **Template parameters** are designated using "`template` \`:!` \". The syntax for checked and template parameters was decided in [questions-for-leads issue #565](https://github.com/carbon-language/carbon-lang/issues/565). Expected difference between checked and template parameters:
Checked Template
bounded parametric polymorphism compile-time duck typing and ad-hoc polymorphism
constrained genericity optional constraints
name lookup resolved for definitions in isolation ("early") name lookup can use information from arguments (name lookup may be "late")
sound to typecheck definitions in isolation ("early") complete type checking may require information from calls (may be "late")
supports separate type checking; may also support separate compilation separate compilation only to the extent that C++ supports it
allowed but not required to be implemented using dynamic dispatch does not support implementation by way of dynamic dispatch, just static by way of instantiation
monomorphization is an optional optimization that cannot render the program invalid monomorphization is mandatory and can fail, resulting in the program being invalid
### Polymorphism Generics provide different forms of [polymorphism]() than object-oriented programming with inheritance. That uses [subtype polymorphism](https://en.wikipedia.org/wiki/Subtyping) where different descendants, or "subtypes", of a base class can provide different implementations of a method, subject to some compatibility restrictions on the signature. #### Parametric polymorphism Parametric polymorphism ([Wikipedia](https://en.wikipedia.org/wiki/Parametric_polymorphism)) is when a function or a data type can be written generically so that it can handle values _identically_ without depending on their type. [Bounded parametric polymorphism](https://en.wikipedia.org/wiki/Parametric_polymorphism#Bounded_parametric_polymorphism) is where the allowed types are restricted to satisfy some constraints. Within the set of allowed types, different types are treated uniformly. #### Compile-time duck typing Duck typing ([Wikipedia](https://en.wikipedia.org/wiki/Duck_typing)) is when the legal types for arguments are determined implicitly by the usage of the values of those types in the body of the function. Compile-time duck typing is when the usages in the body of the function are checked at compile-time, along all code paths. Contrast this with ordinary duck typing in a dynamic language such as Python where type errors are only diagnosed at runtime when a usage is reached dynamically. #### Ad-hoc polymorphism Ad-hoc polymorphism ([Wikipedia](https://en.wikipedia.org/wiki/Ad_hoc_polymorphism)), also known as "overloading", is when a single function name has multiple implementations for handling different argument types. There is no enforcement of any consistency between the implementations. For example, the return type of each overload can be arbitrary, rather than being the result of some consistent rule being applied to the argument types. Templates work with ad-hoc polymorphism in two ways: - A function with template parameters can be [specialized](#template-specialization) in [C++](https://en.cppreference.com/w/cpp/language/template_specialization) as a form of ad-hoc polymorphism. - A function with template parameters can call overloaded functions since it will only resolve that call after the types are known. In Carbon, we expect there to be a compile error if overloading of some name prevents a checked-generic function from being typechecked from its definition alone. For example, let's say we have some overloaded function called `F` that has two overloads: ``` fn F[template T:! type](x: T*) -> T; fn F(x: Int) -> bool; ``` A checked generic function `G` can call `F` with a type like `T*` that cannot possibly call the `F(Int)` overload for `F`, and so it can consistently determine the return type of `F`. But `G` can't call `F` with an argument that could match either overload. **Note:** It is undecided what to do in the situation where `F` is overloaded, but the signatures are consistent and so callers could still typecheck calls to `F`. This still poses problems for the dynamic strategy for compiling generics, in a similar way to impl specialization. ### Constrained genericity We will allow some way of specifying constraints as part of a function (or type or other parameterized language construct). These constraints are a limit on what callers are allowed to pass in. The distinction between constrained and unconstrained genericity is whether the body of the function is limited to just those operations that are guaranteed by the constraints. With templates using unconstrained genericity, you may perform any operation in the body of the function, and they will be checked against the specific types used in calls. You can still have constraints, but they are optional in that they could be removed and the function would still have the same capabilities. Constraints only affect the caller, which will use them to resolve overloaded calls to the template and provide clearer error messages. With checked generics using constrained genericity, the function body can be checked against the signature at the time of definition. Note that it is still perfectly permissible to have no constraints on a type; that just means that you can only perform operations that work for all types (such as manipulate pointers to values of that type) in the body of the function. ### Dependent names A name is said to be _dependent_ if it depends on some checked or template parameter. Note: this matches [the use of the term "dependent" in C++](https://www.google.com/search?q=c%2B%2B+dependent+name), not as in [dependent types](https://en.wikipedia.org/wiki/Dependent_type). ### Definition checking Definition checking is the process of semantically checking the definition of parameterized code for correctness _independently_ of any particular argument values. It includes type checking and other semantic checks. It is possible, even with templates, to check semantics of expressions that are not [dependent](#dependent-names) on any template parameter in the definition. Adding constraints to template parameters and/or switching them to be checked allows the compiler to increase how much of the definition can be checked. Any remaining checks are delayed until [instantiation](#instantiation), which can fail. #### Complete definition checking Complete definition checking is when the definition can be _fully_ semantically checked, including type checking. It is an especially useful property because it enables _separate_ semantic checking of the definition, a prerequisite to separate compilation. It also is a requirement for implementation strategies that don’t instantiate the implementation (for example, [type erasure](#type-erasure) or [dynamic-dispatch witness tables](#dynamic-dispatch-witness-table)). #### Early versus late type checking Early type checking is where expressions and statements are type checked when the definition of the function body is compiled, as part of definition checking. This occurs for regular and checked-generic values. Late type checking is where expressions and statements may only be fully typechecked once calling information is known. Late type checking delays complete definition checking. This occurs for [template-dependent](#dependent-names) values. ## Bindings _Binding patterns_ associate a name with a type and a value. This is used to declare function parameters, in `let` and `var` declarations, as well as to declare [compile-time parameters](#generic-means-compile-time-parameterized) for classes, interfaces, and so on. There are three kinds of binding patterns, corresponding to [the three expression phases](/docs/design/README.md#expression-phases): - A _runtime binding pattern_ binds to a dynamic value at runtime, and is written using a `:`, as in `x: i32`. - A _symbolic binding pattern_ binds to a compile-time value that is not known when type checking, and is used to declare [checked generic](#checked-versus-template-parameters) parameters. These binding use `:!`, as in `T:! type`. - A _template binding pattern_ binds to a compile-time value that is known when type checking, and is used to declare [template](#checked-versus-template-parameters) parameters. These bindings use the keyword `template` in addition to `:!`, as in `template T:! type`. The last two binding patterns, which are about binding a compile-time value, are called _compile-time binding patterns_, and correspond to those binding patterns that use `:!`. The name being declared, which is the identifier to the left of the `:` or `:!`, is called a _binding_, or more specifically a _runtime binding_, _compile-time binding_, _symbolic binding_, or _template binding_. The expression to the right defining the type of the binding pattern is called the _binding type expression_, a kind of [type expression](#type-expression). For example, in `T:! Hashable`, `T` is the binding (a symbolic binding in this case), and `Hashable` is the binding type expression. ## Types and `type` A _type_ is a value of type `type`. Conversely, `type` is the type of all types. Expressions in type position, for example a [binding type expression](#bindings) or the return type of a function, are implicitly cast to type `type`. This means that it is legal to put a value that is not a type where a type is expected, as long as it has an implicit conversion to `type` that may be performed at compile time. ## Facet type A _facet type_ is a [type](#types-and-type) whose values are some subset of the values of `type`, determined by a set of [type constraints](#type-constraints): - [Interfaces](#interface) and [named constraints](#named-constraints) are facet types whose constraints are that the interface or named constraint is satisfied by the type. - The values produced by `&` operations between facet types and by `where` expressions are facet types, whose set of constraints are determined by the `&` or `where` expression. - `type` is a facet type whose set of constraints is empty. A facet type is the type used when declaring some type parameter. It foremost determines which types are legal arguments for that type parameter. For template parameters, that is all a facet type does. For checked parameters, it also determines the API that is available in the body of the definition of the [generic function, class, or other entity](#generic-means-compile-time-parameterized). ## Facet A _facet_ is a value of a [facet type](#facet-type). For example, `i32 as Hashable` is a facet, and `Hashable` is a facet type. Note that all types are facets, since [`type`](#types-and-type) is considered a facet type. Not all facets are types, though: `i32 as Hashable` is of type `Hashable` not `type`, so it is a facet that is not a type. However, in places where a type is expected, for example in a [binding type expression](#bindings) or after the `->` in a function declaration, there is an automatic implicit conversion to `type`. This means that a facet may be used in those positions. For example, the facet `i32 as Hashable` will implicitly convert to `(i32 as Hashable) as type`, which is `i32`, in those contexts. ## Type expression A _type expression_ is an expression that can be used as a type. In some cases, what is written in the source code is a value, like a [facet](#facet) or tuple of types, that is not a type but has an implicit conversion to `type`. In those cases, we are concerned with the type value after the implicit conversion. ## Facet binding We use the term _facet binding_ to refer to the name introduced by a [compile-time binding pattern](#bindings) (using `:!` with or without the `template` modifier) where the declared type is a [facet type](#facet-type). In the binding pattern `T:! Hashable`, `T` is a facet binding, and the value of `T` is a [facet](#facet). ## Deduced parameter A deduced parameter is listed in the optional `[` `]` section right after the function name in a function signature: `fn` \ `[` \ `](` \ `) ->` \ Deduced arguments are determined as a result of pattern matching the explicit argument values (usually the types of those values) to the explicit parameters. See more [here](overview.md#deduced-parameters). ## Interface An interface is an API constraint used in a function signature to provide encapsulation. Encapsulation here means that callers of the function only need to know about the interface requirements to call the function, not anything about the implementation of the function body, and the compiler can check the function body without knowing anything more about the caller. Callers of the function provide a value that has an implementation of the API and the body of the function may then use that API. In the case of a checked generic, the function may _only_ use that API. ### Structural interfaces A "structural" interface is one where we say a type satisfies the interface as long as it has members with a specific list of names, and for each name it must have some type or signature. A type can satisfy a structural interface without ever naming that interface, just by virtue of having members with the right form. ### Nominal interfaces A "nominal" interface is one where we say a type can only satisfy an interface if there is some explicit statement saying so, for example by defining an [impl](#impl-implementation-of-an-interface). This allows "satisfies the interface" to have additional semantic meaning beyond what is directly checkable by the compiler. For example, knowing whether the `Draw` function means "render an image to the screen" or "take a card from the top of a deck of cards"; or that a `+` operator is commutative (and not, say, string concatenation). We use the "structural" versus "nominal" terminology as a generalization of the same terms being used in a [subtyping context](https://en.wikipedia.org/wiki/Subtyping#Subtyping_schemes). ### Named constraints Named constraints are "structural" in the sense that they match a type based on meeting some criteria rather than an explicit statement in the type's definition. The criteria for a named constraint, however, are less focused on the type's API and instead might include a set of nominal interfaces that the type must implement and constraints on the [associated entities](#associated-entity) and [interface parameters](#interface-parameters-and-associated-constants). ## Associated entity An _associated entity_ is a requirement in an interface that a type's implementation of the interface must satisfy by having a matching definition. A requirement that the type define a value for a member constant is called an _associated constant_. If the type of the associated constant is a [facet type](#facet-type), then it is called an _associated [facet](#facet)_, which corresponds to what is called an "associated type" in other languages ([Swift](https://docs.swift.org/swift-book/documentation/the-swift-programming-language/generics/#Associated-Types), [Rust](https://doc.rust-lang.org/reference/items/associated-items.html#associated-types)). Similarly, an interface can have _associated function_, _associated method_, or _associated class function_. Different types can satisfy an interface with different definitions for a given member. These definitions are _associated_ with what type is implementing the interface. An [impl](#impl-implementation-of-an-interface) defines what is associated with the type for that interface. Rust uses the term ["associated item"](https://doc.rust-lang.org/reference/items/associated-items.html) instead of associated entity. ## Impl: Implementation of an interface An _impl_ is an implementation of an interface for a specific type, called the _implementing type_. It is the place where the function bodies are defined, values for associated constants, etc. are given. Implementations are needed for [nominal interfaces](#nominal-interfaces); [structural interfaces](#structural-interfaces) and [named constraints](#named-constraints) define conformance implicitly instead of by requiring an impl to be defined. In can still make sense to explicitly implement a named constraint as a way to implement all of the interfaces it requires. ### Extending an impl A type that _extends_ the implementation of an interface has all the named members of the interface as named members of the type. This means that the members of the interface are available by way of both [simple member access and qualified member access expressions](#member-access). If a type implements an interface without extending, the members of the interface may only be accessed using [qualified member access expressions](#qualified-member-access-expression). ## Member access There are two different kinds of member access: _simple_ and _compound_. See the [member access design document](/docs/design/expressions/member_access.md) for the details. The application to generics combines compound member access with qualified names, which we call a _qualified member access expression_. ### Simple member access Simple member access has the from `object.member`, where `member` is a word naming a member of `object`. This form may be used to access members of interfaces when the type of `object` [extends the implementation](#extending-an-impl) of that interface. If `String` extends its implementation of `Printable`, then `s1.Print()` calls the `Print` method of `Printable` using simple member access. In this case, the name `Print` is used without qualifying it with the name of the interface it is a member of since it is recognized as a member of the type itself as well. ### Qualified member access expression Compound member access has the form `object.(expression)`, where `expression` is resolved in the containing scope. A compound member access where the member expression is a simple member access expression, as in `a.(context.b)`, is called a _qualified member access expression_. The member expression `context.b` may be the _qualified member name_ of an interface member, that consists of the name of the interface, possibly qualified with a package or namespace name, a dot `.` and the name of the member. For example, if the `Comparable` interface has a `Less` member method, then the qualified name of that member is `Comparable.Less`. So if `String` implements `Comparable`, and `s1` and `s2` are variables of type `String`, then the `Less` method may be called using the qualified member name by writing the qualified member access expression `s1.(Comparable.Less)(s2)`. This form may be used to access any member of an interface implemented for a type, whether or not it [extends the implementation](#extending-an-impl). ## Compatible types Two types are compatible if they have the same notional set of values and represent those values in the same way, even if they expose different APIs. The representation of a type describes how the values of that type are represented as a sequence of bits in memory. The set of values of a type includes properties that the compiler can't directly see, such as invariants that the type maintains. We can't just say two types are compatible based on structural reasons. Instead, we have specific constructs that create compatible types from existing types in ways that encourage preserving the programmer's intended semantics and invariants, such as implementing the API of the new type by calling (public) methods of the original API, instead of accessing any private implementation details. ## Subtyping and casting Both subtyping and casting are different names for changing the type of a value to a compatible type. [Subtyping](https://en.wikipedia.org/wiki/Subtyping) is a relationship between two types where you can safely operate on a value of one type using a variable of another. For example, using C++'s object-oriented features, you can operate on a value of a derived class using a pointer to the base class. In most cases, you can pass a more specific type to a function that can handle a more general type. Return types work the opposite way, a function can return a more specific type to a caller prepared to handle a more general type. This determines how function signatures can change from base class to derived class, see [covariance and contravariance in Wikipedia](). In a generics context, we are specifically interested in the subtyping relationships between [facet types](#facet-type). In particular, a facet type encompasses a set of [type constraints](#type-constraints), and you can convert a type from a more-restrictive facet type to another facet type whose constraints are implied by the first. C++ concepts terminology uses the term ["subsumes"](https://en.cppreference.com/w/cpp/language/constraints#Partial_ordering_of_constraints) to talk about this partial ordering of constraints, but we avoid that term since it is at odds with the use of the term in [object-oriented subtyping terminology](https://en.wikipedia.org/wiki/Subtyping#Subsumption). Note that subtyping is a bit like [coercion](https://en.wikipedia.org/wiki/Type_conversion), except we want to make it clear that the data representation of the value is not changing, just its type as reflected in the API available to manipulate the value. Casting is indicated explicitly by way of some syntax in the source code. You might use a cast to switch between [type adaptations](#adapting-a-type), or to be explicit where an implicit conversion would otherwise occur. For now, we are saying "`x as y`" is the provisional syntax in Carbon for casting the value `x` to the type `y`. Note that outside of generics, the term "casting" includes any explicit type change, including those that change the data representation. In contexts where an expression of one type is provided and a different type is required, an [implicit conversion](../expressions/implicit_conversions.md) is performed if it is considered safe to do so. Such an implicit conversion, if permitted, always has the same meaning as an explicit cast. ## Coherence A generics or interface system has the _implementation coherence_ property, or simply _coherence_, if there is a single answer to the question "what is the implementation of this interface for this type, if any?" independent of context, such as the libraries imported into a given file. Coherence is [a goal of Carbon checked generics](goals.md#coherence). This is enforced using two kinds of rules: - An _orphan rule_ is a restriction on which files may declare a particular implementation. This is to ensure that the implementation is imported any time it could be used. For example, if neither the type nor the interface is parameterized, the orphan rule requires that the implementation must be in the same library as the interface or type. The rule is we don't allow an _orphan_ implementation that is not with either of its parents (parent type or parent interface). - An _overlap rule_ is a way to _consistently_ select a single implementation when multiple implementations apply. In Carbon, overlap is resolved by picking a single implementation using a rule that picks the one that is considered most specialized. In Rust, by contrast, the [overlap rule or overlap check](https://rust-lang.github.io/chalk/book/clauses/coherence.html#chalk-overlap-check) instead produces an error if two implementations apply at once. The rationale for Carbon choosing coherence and alternatives considered may be found in [this appendix](appendix-coherence.md) ## Adapting a type A type can be adapted by creating a new type that is [compatible](#compatible-types) with an existing type, but has a different API. In particular, the new type might implement different interfaces or provide different implementations of the same interfaces. Unlike extending a type (as with C++ class inheritance), you are not allowed to add new data fields onto the end of the representation -- you may only change the API. This means that it is safe to [cast](#subtyping-and-casting) a value between those two types without any dynamic checks or danger of [object slicing](https://en.wikipedia.org/wiki/Object_slicing). This is called "newtype" in Rust, and is used for capturing additional information in types to improve type safety by moving some checking to compile time ([1](https://doc.rust-lang.org/rust-by-example/generics/new_types.html), [2](https://doc.rust-lang.org/book/ch19-04-advanced-types.html#using-the-newtype-pattern-for-type-safety-and-abstraction), [3](https://www.worthe-it.co.za/blog/2020-10-31-newtype-pattern-in-rust.html)) and as a workaround for [Rust's orphan rules for coherence](https://github.com/Ixrec/rust-orphan-rules#why-are-the-orphan-rules-controversial). ## Type erasure "Type erasure" is where a type's API is replaced by a subset. Everything outside of the preserved subset is said to have been "erased". This can happen in a variety of contexts including both checked generics and runtime polymorphism. For checked generics, type erasure restricts a type to just the API required by the constraints on that type stated in the signature of the function. An example of type erasure in runtime polymorphism in C++ is casting from a pointer of a derived type to a pointer to an abstract base type. Only the API of the base type is available on the result, even though the implementation of those methods still come from the derived type. The term "type erasure" can also refer to [the specific strategy used by Java to implement generics](https://en.wikipedia.org/wiki/Generics_in_Java). which includes erasing the identity of type parameters. This is not the meaning of "type erasure" used in Carbon. ## Archetype A placeholder type is used when type checking a function in place of a generic type parameter. This allows type checking when the specific type to be used is not known at type-checking time. The type satisfies just its constraint and no more, so it acts as the most general type satisfying the interface. In this way the archetype is the supertype of all types satisfying the interface. In addition to satisfying all the requirements of its constraint, the archetype also has the member names of its constraint. Effectively it is considered to [extend the implementation of the constraint](#extending-an-impl). ## Extending an interface An interface can be extended by defining an interface that includes the full API of another interface, plus some additional API. Types implementing the extended interface should automatically be considered to have implemented the narrower interface. ## Dynamic-dispatch witness table Dynamic-dispatch [witness tables](https://forums.swift.org/t/where-does-the-term-witness-table-come-from/54334/4) are an implementation strategy that uses a table accessed at runtime to allow behavior of a function to vary. This allows a function to work with any type implementing a facet type (such as an interface). For example, the witness table might contain pointers to the implementations of the functions of the interface. This can be done to reduce the size of generated code, at the expense of additional indirection at runtime. It can also allow a function to dynamically dispatch when the runtime type of a value is not known. This is the implementation strategy for [boxed protocol types in Swift](https://docs.swift.org/swift-book/documentation/the-swift-programming-language/opaquetypes/#Boxed-Protocol-Types) and [trait objects in Rust](https://doc.rust-lang.org/book/ch17-02-trait-objects.html). Note that this often comes with limitations, since for example it is much more difficult to support when the associated constants of the interface are not known. Typically a reference to the witness table will be passed separately from the object, unlike a [virtual method table](https://en.wikipedia.org/wiki/Virtual_method_table), which otherwise is very similar to a witness table, "witnessing" the specific descendant of a base class. Carbon's approach to using witness tables is detailed in an [appendix](appendix-witness.md). ## Instantiation Instantiation is the implementation strategy for templates in both C++ and Carbon. Instantiation explicitly creates a copy of the template code and replaces the template components with the concrete type and its implementation operations. It allows duck typing and lazy binding. Instantiation implies template code **will** be duplicated. Unlike static-dispatch witness tables (as in Swift) and [monomorphization (as in Rust)](https://doc.rust-lang.org/book/ch10-01-syntax.html#performance-of-code-using-generics), this is done **before** type checking completes. Only when the template is used with a concrete type is the template fully type checked, and it type checks against the actual concrete type after substituting it into the template. This means that different instantiations may interpret the same construct in different ways, and that templates can include constructs that are not valid for some possible instantiations. However, it also means that some errors in the template implementation may not produce errors until the instantiation occurs, and other errors may only happen for **some** instantiations. ## Specialization ### Template specialization Specialization in C++ is essentially overloading, or [ad-hoc polymorphism](#ad-hoc-polymorphism), in the context of a template. The template is overloaded to have a different definition for some subset of the possible template argument values. For example, the C++ type `std::vector` might have a specialization `std::vector` that is implemented in terms of `std::vector` to reduce code size. In C++, even the interface of a templated type can be changed in a specialization, as happens for `std::vector`. ### Checked-generic specialization Specialization of checked generics, or types used by checked generics, is restricted to changing the implementation _without_ affecting the interface. This restriction is needed to preserve the ability to perform type checking of generic definitions that reference a type that can be specialized, without statically knowing which specialization will be used. ## Conditional conformance Conditional conformance is when you have a parameterized type that has one API that it always supports, but satisfies additional interfaces under some conditions on the type argument. For example: `Array(T)` might implement `Comparable` if `T` itself implements `Comparable`, using lexicographical order. ## Interface parameters and associated constants _Interface parameters_ and [associated constants](#associated-entity) are both ways of allowing the types in function signatures in an interface to vary. For example, different [stacks]() will have different element types. That element type would be used as the parameter type of the `Push` function and the return type of the `Pop` function. As [in Rust](https://rust-lang.github.io/rfcs/0195-associated-items.html#clearer-trait-matching), we can distinguish these by whether they are input parameters or output parameters: - An interface parameter is a parameter or input to the interface. That means they must be specified before an implementation of the interface may be determined. - In contrast, associated constants are outputs. This means that they are determined by the implementation, and need not be specified in a [type constraint](#type-constraints). Functions using an interface as a constraint need not specify the value of its associated constants. ``` // Stack using associated facets interface Stack { let ElementType:! type; fn Push[ref self: Self](value: ElementType); fn Pop[ref self: Self]() -> ElementType; } // Works on any type implementing `Stack`. Return type // is determined by the type's implementation of `Stack`. fn PeekAtTopOfStack[T:! Stack](s: T*) -> T.ElementType { let ret: T.ElementType = s->Pop(); s->Push(ret); return ret; } class Fruit; class FruitStack { // Implement `Stack` for `FruitStack` // with `ElementType` set to `Fruit`. extend impl as Stack where .ElementType == Fruit { ... } } ``` Associated constants are particularly called for when the implementation of the interface determines the value, not the caller. For example, the iterator type for a container is specific to the container and not something you would expect a user of the interface to specify. If you have an interface with parameters, a type can have multiple matching `impl` declarations for different combinations of argument values. As a result, interface parameters may not be deduced in a function call. However, if the interface parameters are specified, a type can only have a single implementation of the given interface. This unique implementation choice determines the values of associated constants. For example, we might have an interface that says how to perform addition with another type: ``` interface AddWith(T:! type) { let ResultType:! type; fn Add[self: Self](rhs: T) -> ResultType; } ``` An `i32` value might support addition with `i32`, `u16`, and `f64` values. ``` impl i32 as AddWith(i32) where .ResultType = i32 { ... } impl i32 as AddWith(u16) where .ResultType = i32 { ... } impl i32 as AddWith(f64) where .ResultType = f64 { ... } ``` To write a generic function requiring a parameter to be `AddWith`, there needs to be some way to determine the type to add to: ``` // ✅ This is allowed, since the value of `T` is determined by the // `y` parameter. fn DoAdd[T:! type, U:! AddWith(T)](x: U, y: T) -> U.ResultType { return x.Add(y); } // ❌ This is forbidden, can't uniquely determine `T`. fn CompileError[T:! type, U:! AddWith(T)](x: U) -> T; ``` Once the interface parameters can be determined, that determines the values for associated constants, such as `ResultType` in the example. As always, calls with types for which no implementation exists will be rejected at the call site: ``` // ❌ This is forbidden, no implementation of `AddWith(Orange)` // for `Apple`. DoAdd(apple, orange); ``` The type of an interface parameters and associated constants is commonly a [facet type](#facet-type), but not always. For example, an interface parameter that specifies an array bound might have an integer type. ## Type constraints Type constraints restrict which types are legal for a [facet binding](#facet-binding), like a facet parameter or associated facet. They help define semantics under which they should be called, and prevent incorrect calls. In general there are a number of different type relationships we would like to express, for example: - This function accepts two containers. The container types may be different, but the element types need to match. - For this container interface we have associated facets for iterators and elements. The iterator type's element type needs to match the container's element type. - An interface may define an associated facet that needs to be constrained to implement some interfaces. - This type must be [compatible](#compatible-types) with another type. You might use this to define alternate implementations of a single interfaces, such as sorting order, for a single type. Note that type constraints can be a restriction on one facet parameter or associated facet, or can define a relationship between multiple facets. ## References - [#447: Generics terminology](https://github.com/carbon-language/carbon-lang/pull/447) - [#731: Generics details 2: adapters, associated types, parameterized interfaces](https://github.com/carbon-language/carbon-lang/pull/731) - [#950: Generic details 6: remove facets](https://github.com/carbon-language/carbon-lang/pull/950) - [#1013: Generics: Set associated constants using where constraints](https://github.com/carbon-language/carbon-lang/pull/1013) - [#2138: Checked and template generic terminology](https://github.com/carbon-language/carbon-lang/pull/2138) - [#2360: Types are values of type `type`](https://github.com/carbon-language/carbon-lang/pull/2360) - [#2760: Consistent `class` and `interface` syntax](https://github.com/carbon-language/carbon-lang/pull/2760) ================================================ FILE: docs/design/interoperability/README.md ================================================ # Bidirectional interoperability with C and C++ ## Table of contents - [Philosophy and goals](#philosophy-and-goals) - [Overview](#overview) - [C++ interoperability model: introduction and principles](#c-interoperability-model-introduction-and-principles) - [The successor language mandate](#the-successor-language-mandate) - [The C++ interop type](#the-c-interop-type) - [Importing C++ APIs into Carbon](#importing-c-apis-into-carbon) - [Importing C++ libraries (header-based)](#importing-c-libraries-header-based) - [TODO: Importing C++ code (inline)](#todo-importing-c-code-inline) - [Accessing built-in C++ entities (file-less)](#accessing-built-in-c-entities-file-less) - [The `Cpp` package](#the-cpp-package) - [TODO: Importing C++ macros](#todo-importing-c-macros) - [Calling C++ code from Carbon](#calling-c-code-from-carbon) - [Function call syntax and semantics](#function-call-syntax-and-semantics) - [TODO: Overload resolution](#todo-overload-resolution) - [TODO: Constructors](#todo-constructors) - [TODO: Struct literals](#todo-struct-literals) - [TODO: Accessing C++ classes, structs, and members](#todo-accessing-c-classes-structs-and-members) - [TODO: Accessing global variables](#todo-accessing-global-variables) - [TODO: Bi-directional type mapping: primitives and core types](#todo-bi-directional-type-mapping-primitives-and-core-types) - [TODO: Advanced type mapping: pointers, references, and `const`](#todo-advanced-type-mapping-pointers-references-and-const) - [TODO: Bi-directional type mapping: standard library types](#todo-bi-directional-type-mapping-standard-library-types) - [TODO: The operator interoperability model](#todo-the-operator-interoperability-model) ## Philosophy and goals The C++ interoperability layer of Carbon allows a subset of C++ APIs to be accessed from Carbon code, and similarly a subset of Carbon APIs to be accessed from C++ code. This requires expressing one language as a subset of the other. Bridge code may be needed to map some APIs into the relevant subset, but the constraints on expressivity should be loose enough to keep the amount of such bridge code sustainable. The [interoperability philosophy and goals](philosophy_and_goals.md) provide more detail. ## Overview Carbon's bidirectional interoperability with C++ is [a cornerstone of its design](/docs/project/goals.md#interoperability-with-and-migration-from-existing-c-code), enabling a gradual transition from existing C++ codebases. The goal is not just a foreign function interface (FFI), but a seamless, high-fidelity integration that supports advanced C++ features, from templates to class hierarchies. C++ APIs are imported into Carbon using an `import Cpp` directive, which makes C++ declarations available within a dedicated `Cpp` package in Carbon. This prevents name collisions and makes the origin of symbols explicit. Carbon code can then call C++ functions, instantiate C++ classes, and use C++ types, while respecting C++'s semantics, including its complex overload resolution rules and preserving the nominal distinctions between C++ types like `long` and `long long`, or `T*` and `T&`, which is critical for correct overload resolution and template instantiation. Similarly, Carbon APIs can be designed to be callable from C++. The interoperability layer is designed to be zero-cost, avoiding unnecessary allocations or copies when calling between the two languages. ## C++ interoperability model: introduction and principles ### The successor language mandate The design of Carbon's C++ interoperability is governed by its foundational goal: [to be a successor language](/README.md), not merely a language with a foreign function interface (FFI). This mandate dictates a design that moves beyond the C-style FFI adopted by most modern languages and instead provides seamless, bidirectional interoperability. The objective is to support deep integration with existing C++ code, encompassing its most complex features, from inheritance to templates. This goal has profound implications for the Carbon compiler and language semantics. It requires that C++ is not treated as a foreign entity. Instead, Carbon's semantic model must be _co-designed_ to understand, map, and interact with C++'s semantic constructs—including templates, class hierarchies, and complex overload resolution—with high fidelity. The interoperability layer must, therefore, operate at the semantic analysis level, not just at the linking (ABI) level. This document specifies the design of this semantic contract. ### The C++ interop type A core mechanism in this design is the C++ interop type. This concept defines the "trigger" that activates C++-specific semantic rules within the Carbon compiler. Any operation involving a type that is designated as a C++ interop type could invoke the specialized interoperability logic, such as C++ overload resolution or operator overload resolution that involves both Carbon and C++ operator overloads. A type is considered a C++ interop type if its definition involves an imported C++ type in any of the following ways: 1. A C++ imported type (for example, `Cpp.Widget`). 2. A pointer to a C++ interop type (for example, `Cpp.Widget*`). 3. A Carbon generic type parameterized with a C++ interop type (for example, `MyCarbonVector(Cpp.Widget)`). More generally, a C++ interop type is any type for which Carbon's [orphan rule](https://docs.carbon-lang.dev/docs/design/generics/details.html#orphan-rule) would allow an impl to be provided by a library in `package Cpp`. This "pervasive" model of C++-awareness is a fundamental design choice. The C++ semantics are not confined to a specific `unsafe` or `extern "C++"` block; they affect any Carbon type that composes them. For example, when the Carbon compiler instantiates a _Carbon_ generic type like `MyCarbonVector(Cpp.Widget)`, its type system must be aware that the `Cpp.Widget` parameter carries C++-specific rules. This mandates that Carbon's own generic system, struct layout logic, overload resolution and operator lookup must query the type system for the presence of a C++ interop type. If present, Carbon must consider C++ rules when operating over C++ interop types. This design prioritizes the goal of a seamless and intuitive user experience. ## Importing C++ APIs into Carbon ### Importing C++ libraries (header-based) The primary mechanism for importing existing, user-defined C++ code is through header file inclusion. Carbon must be able to parse and analyze C++ header files to make their declarations available within Carbon. **Syntax:** The syntax for this operation is `import Cpp library "header_name"`. This syntax is used for both standard library headers and user-defined headers: - **Standard Library:** ```carbon import Cpp library ""; ``` This import makes entities like `putchar` available. - **C++ User-Defined Header:** ```carbon import Cpp library "circle.h"; ``` This import makes user-defined declarations and definitions available. ### TODO: Importing C++ code (inline) ### Accessing built-in C++ entities (file-less) Some C++ entities, particularly built-in primitive types, are not defined in any header file. They are "intrinsic" to the C++ language. These entities are available in Carbon without an explicit `import` declaration. ### The `Cpp` package A critical design choice for managing C++ imports is the mandatory use of a containing package, `Cpp`. All imported C++ named entities (functions, types, namespaces) are contained in the `Cpp` package. - **Functions:** `Cpp.putchar(...)` - **Classes/Types:** `Cpp.Circle`, `Cpp.Point` - **Constructors:** `Cpp.Circle.Circle()` The `Cpp.` prefix makes the _origin_ of every symbol explicit and unambiguous. It ensures that C++ entities cannot collide with Carbon code. ### TODO: Importing C++ macros ## Calling C++ code from Carbon ### Function call syntax and semantics Once imported, C++ functions are invoked using standard Carbon function call syntax, prefixed with the `Cpp` name. The Carbon compiler is responsible for mapping the Carbon arguments to the types expected by the C++ function's signature. This often requires explicit casting on the Carbon side, using the `as` keyword, to satisfy the C++ function's parameter types. **Example:** The following example imports `cstdio` and calls the C function `putchar`. The Carbon `Core.Char` variable `n` must be cast first to `u8` and then to `i32` to match the `int` parameter expected by `putchar`. ```carbon import Cpp library ""; fn Run() { let hello: array(Core.Char, 6) = ('H', 'e', 'l', 'l', 'o', '!'); for (n: Core.Char in hello) { // Carbon 'as' casting is used to match the C++ signature Cpp.putchar((n as u8) as i32); } } ``` ### TODO: Overload resolution ### TODO: Constructors ### TODO: Struct literals ## TODO: Accessing C++ classes, structs, and members ## TODO: Accessing global variables ## TODO: Bi-directional type mapping: primitives and core types ## TODO: Advanced type mapping: pointers, references, and `const` ## TODO: Bi-directional type mapping: standard library types ## TODO: The operator interoperability model ================================================ FILE: docs/design/interoperability/philosophy_and_goals.md ================================================ # Interoperability philosophy and goals ## Table of contents - [Background](#background) - [Other interoperability layers](#other-interoperability-layers) - [Philosophy](#philosophy) - [Language goal influences](#language-goal-influences) - [Performance-critical software](#performance-critical-software) - [Software and language evolution](#software-and-language-evolution) - [Code that is easy to read, understand, and write](#code-that-is-easy-to-read-understand-and-write) - [Practical safety guarantees and testing mechanisms](#practical-safety-guarantees-and-testing-mechanisms) - [Fast and scalable development](#fast-and-scalable-development) - [Modern OS platforms, hardware architectures, and environments](#modern-os-platforms-hardware-architectures-and-environments) - [Interoperability with and migration from existing C++ code](#interoperability-with-and-migration-from-existing-c-code) - [Goals](#goals) - [Support mixing Carbon and C++ toolchains](#support-mixing-carbon-and-c-toolchains) - [Compatibility with the C++ memory model](#compatibility-with-the-c-memory-model) - [Minimize bridge code](#minimize-bridge-code) - [Unsurprising mappings between C++ and Carbon types](#unsurprising-mappings-between-c-and-carbon-types) - [Allow C++ bridge code in Carbon files](#allow-c-bridge-code-in-carbon-files) - [Carbon inheritance from C++ types](#carbon-inheritance-from-c-types) - [Support use of advanced C++ features](#support-use-of-advanced-c-features) - [Support basic C interoperability](#support-basic-c-interoperability) - [Non-goals](#non-goals) - [Full parity between a Carbon-only toolchain and mixing C++/Carbon toolchains](#full-parity-between-a-carbon-only-toolchain-and-mixing-ccarbon-toolchains) - [Never require bridge code](#never-require-bridge-code) - [Convert all C++ types to Carbon types](#convert-all-c-types-to-carbon-types) - [Support for C++ exceptions without bridge code](#support-for-c-exceptions-without-bridge-code) - [Cross-language metaprogramming](#cross-language-metaprogramming) - [Offer equivalent support for languages other than C++](#offer-equivalent-support-for-languages-other-than-c) - [Open questions to be resolved later](#open-questions-to-be-resolved-later) - [Carbon type inheritance from non-pure interface C++ types](#carbon-type-inheritance-from-non-pure-interface-c-types) - [CRTP support](#crtp-support) - [Object lifetimes](#object-lifetimes) - [References](#references) ## Background Interoperability with and migration from C++ are a [language goal](/docs/project/goals.md#interoperability-with-and-migration-from-existing-c-code). However, performance and evolution are [_higher_ priorities](/docs/project/goals.md#language-goals-and-priorities). This interaction of priorities is important to understanding Carbon's interoperability goals and trade-offs. ### Other interoperability layers Other language interoperability layers that may offer useful examples are: - [Java/Kotlin](https://kotlinlang.org/docs/reference/java-to-kotlin-interop.html) should be a comparable interoperability story. The languages are different, but share an underlying runtime. This may be closest to the model we desire for Carbon. - [JavaScript/TypeScript](https://www.typescriptlang.org/docs/handbook/migrating-from-javascript.html) is similar to C/C++, where one language is essentially a subset of the other, allowing high interoperability. This is an interesting reference point, but we are looking at a different approach with a clearer boundary. - [C++/Java](https://en.wikipedia.org/wiki/Java_Native_Interface) is an example of requiring specialized code for the bridge layer, making interoperability more burden on developers. The burden of the approach may be considered to correspond to the difference in language memory models and other language design choices. Regardless, the result can be considered higher maintenance for developers than we want for Carbon. - [C++/Go](https://golang.org/cmd/cgo/) is similar to C++/Java. However, Go notably allows C++ bridge code to exist in the .go files, which can ease maintenance of the bridge layer, and is desirable for Carbon. ## Philosophy The C++ interoperability layer of Carbon allows a subset of C++ APIs to be accessed from Carbon code, and similarly a subset of Carbon APIs to be accessed from C++ code. This requires expressing one language as a subset of the other. Bridge code may be needed to map some APIs into the relevant subset, but the constraints on expressivity should be loose enough to keep the amount of such bridge code sustainable. The design for interoperability between Carbon and C++ hinges on: 1. The ability to interoperate with a wide variety of code, such as classes/structs and templates, not just free functions. 2. A willingness to expose the idioms of C++ into Carbon code, and the other way around, when necessary to maximize performance of the interoperability layer. 3. The use of wrappers and generic programming, including templates, to minimize or eliminate runtime overhead. These things come together when looking at how custom data structures in C++ are exposed into Carbon, and the other way around. In both languages, it is reasonable and even common to have customized low-level data structures, such as associative containers. For example, there are numerous data structures for mapping from a key to a value that might be best for a particular use case, including hash tables, linked hash tables, sorted vectors, and btrees. Even for a given data structure, there may be slow but meaningful evolution in implementations strategies. The result is that it will often be reasonable to directly expose a C++ data structure to Carbon without converting it to a "native" or "idiomatic" Carbon data structure. Although interfaces may differ, a trivial adapter wrapper should be sufficient. Many Carbon data structures should also be able to support multiple implementations with C++ data structures being one such implementation, allowing for idiomatic use of C++ hidden behind Carbon. The reverse is also true. C++ code will often not care, or can be refactored to not care, what specific data structure is used. Carbon data structures can be exposed as yet another implementation in C++, and wrapped to match C++ idioms and even templates. For example, a C++ class template like `std::vector` should be usable without wrapper code or runtime overhead, and passing a Carbon type as `T`. The resulting type should be equally usable from either C++ or Carbon code. It should also be easy to wrap `std::vector` with a Carbon interface for transparent use in idiomatic Carbon code. ## Language goal influences ### Performance-critical software Interoperability with C++ will be frequently used in Carbon, whether it's C++ developers trying out Carbon, incrementally migrating a large C++ codebase, or continuing to use a C++ library long-term. In all cases, it must be possible to write interoperable code with zero overhead; copies must not be required. ### Software and language evolution Interoperability will require the addition of features to Carbon which exist primarily to support interoperability use cases. However, these features must not unduly impinge the overall evolution of Carbon. In particular, only a subset of Carbon features will support interoperability with C++. To do otherwise would restrict Carbon's feature set. ### Code that is easy to read, understand, and write Interoperability-related Carbon code will likely be more difficult to read than other, more idiomatic Carbon code. This is okay: aiming to make Carbon code readable doesn't mean that it needs to _all_ be trivial to read. At the same time, the extra costs that interoperability exerts on Carbon developers should be minimized. ### Practical safety guarantees and testing mechanisms Safety is important to maintain around interoperability code, and mitigations should be provided where possible. However, safety guarantees will be focused on native Carbon code. C++ code will not benefit from the same set of safety mechanisms that Carbon offers, so Carbon code calling into C++ will accept higher safety risks. ### Fast and scalable development The interoperability layer will likely have tooling limitations similar to C++. For example, Carbon aims to compile quickly. However, C++ interoperability hinges on compiling C++ code, which is relatively slow. Carbon libraries that use interoperability will see bottlenecks from C++ compile time. Improving C++ is outside the scope of Carbon. ### Modern OS platforms, hardware architectures, and environments Interoperability will apply to the intersection of environments supported by both Carbon and C++. Pragmatically, Carbon will likely be the limiting factor here. ### Interoperability with and migration from existing C++ code Carbon's language goal for interoperability will focus on C++17 compatibility. The language design must be mindful of the prioritization; trade-offs harming other goals may still be made so long as they offer greater benefits for interoperability and Carbon as a whole. Although the below interoperability-specific goals will focus on interoperability, it's also important to consider how migration would be affected. If interoperability requires complex work, particularly to avoid performance impacts, it could impair the ability to incrementally migrate C++ codebases to Carbon. ## Goals ### Support mixing Carbon and C++ toolchains The Carbon toolchain will support compiling C++ code. It will contain a customized C++ compiler that enables some more advanced interoperability features, such as calling Carbon templates from C++. Mixing toolchains will also be supported in both directions: - C++ libraries compiled by a non-Carbon toolchain will be usable from Carbon, so long as they are ABI-compatible with Carbon's C++ toolchain. - The Carbon toolchain will support, as an option, generating a C++ header and object file from a Carbon library, with an ABI that's suitable for use with non-Carbon toolchains. Mixing toolchains restricts functionality to what's feasible with the C++ ABI. For example, developers should expect that Carbon templates will be callable from C++ when using the Carbon toolchain, and will not be available when mixing toolchains because it would require a substantially different and more complex interoperability implementation. This degraded interoperability should still be sufficient for most developers, albeit with the potential of more bridge code. Any C++ interoperability code that works when mixing toolchains must work when using the native Carbon toolchain. The mixed toolchain support must not have semantic divergence. The converse is not true, and the native Carbon toolchain may have additional language support and optimizations. ### Compatibility with the C++ memory model It must be straightforward for any Carbon interoperability code to be compatible with the C++ memory model. This does not mean that Carbon must exclusively use the C++ memory model, only that it must be supported. ### Minimize bridge code The majority of simple C++ functions and types should be usable from Carbon without any custom bridge code and without any runtime overhead. That is, Carbon code should be able to call most C++ code without any code changes to add support for interoperability, even if that code was built with a non-Carbon toolchain. This includes instantiating Carbon templates or generics using C++ types. In the other direction, Carbon may need some minimal markup to expose functions and types to C++. This should help avoid requiring Carbon to generate C++-compatible endpoints unconditionally, which could have compile and linking overheads that may in many cases be unnecessary. Also, it should help produce errors that indicate when a function or type may require additional changes to make compatible with C++. Carbon's priority developers should be able to easily reuse the mature ecosystem of C++ libraries provided by third-parties. A third-party library's language choice should not be a barrier to Carbon adoption. Even for first-party libraries, migration of C++ codebases to Carbon will often be incremental due to human costs of executing and verifying source migrations. Minimizing the amount of bridge code required should be expected to simplify such migrations. ### Unsurprising mappings between C++ and Carbon types Carbon will provide unsurprising mappings for common types. **Primitive types** will have mappings with zero overhead conversions. They are frequently used, making it important that interoperability code be able to use them seamlessly. The storage and representation will need to be equivalent in both languages. For example, if a C++ `__int64` maps to Carbon's `Int64`, the memory layout of both types must be identical. Semantics need to be similar, but edge-case behaviors don't need to be identical, allowing Carbon flexibility to evolve. For example, where C++ would have modulo wrapping on integers, Carbon could instead have trapping behavior on the default-mapped primitive types. Carbon may have versions of these types with no C++ mapping, such as `Int256`. **Non-owning vocabulary types**, such as pointers and references, will have transparent, automatic translation between C++ and Carbon non-owning vocabulary types with zero overhead. **Other vocabulary types** will typically have reasonable, but potentially non-zero overhead, conversions available to map into Carbon vocabulary types. Code using these may choose whether to pay the overhead to convert. They may also use the C++ type directly from Carbon code, and the other way around. **Incomplete types** must have a mapping with similar semantics, similar to primitive types. ### Allow C++ bridge code in Carbon files Carbon files should support inline bridge code written in C++. Where bridge code is necessary, this will allow for maintenance of it directly alongside the code that uses it. ### Carbon inheritance from C++ types Carbon will support inheritance from C++ types for interoperability, although the syntax constructs may look different from C++ inheritance. This is considered necessary to address cases where a C++ library API expects users to inherit from a given C++ type. This might be restricted to pure interface types; see [the open question](#carbon-type-inheritance-from-non-pure-interface-c-types). ### Support use of advanced C++ features There should be support for most idiomatic usage of advanced C++ features. A few examples are templates, overload sets, [attributes](https://en.cppreference.com/w/cpp/language/attributes) and [ADL](https://en.wikipedia.org/wiki/Argument-dependent_name_lookup). Although these features can be considered "advanced", their use is widespread throughout C++ code, including STL. Support for such features is key to supporting migration from C++ features. ### Support basic C interoperability C interoperability support must be sufficient for Carbon code to call popular APIs that are written in C. The ability of C to call Carbon will be more restricted, limited to where it echoes C++ interoperability support. Basic C interoperability will include functions, primitive types, and structs that only contain member variables. Features where interoperability will rely on more advanced C++-specific features, such as templates, inheritance, and class functions, need not be supported for C. These would require a C-specific interoperability model that will not be included. ## Non-goals ### Full parity between a Carbon-only toolchain and mixing C++/Carbon toolchains Making mixed C++/Carbon toolchain support equivalent to Carbon-only toolchain support affects all interoperability features. Mixed toolchains will have degraded support because full parity would be too expensive. The feature of calling Carbon templates from C++ code is key when analyzing this option. Template instantiation during compilation is pervasive in C++. With a Carbon toolchain compiling both Carbon and C++ code, the C++ compiler _can_ be modified to handle Carbon templates differently. Carbon templates can be handled by exposing the Carbon compiler's AST to the C++ compiler directly, as a compiler extension. While this approach is still complex and may not always work, it should offer substantial value and ability to migrate C++ code to Carbon without requiring parallel maintenance of implementations in C++. With a mixed toolchain, the C++ compiler _cannot_ be modified to handle Carbon templates differently. The only way to support template instantiation would be by having Carbon templates converted into equivalent C++ templates in C++ headers; in other words, template support would require source-to-source translation. Supporting Carbon to C++ code translations would be a complex and high cost feature to achieve full parity for mixed toolchains. Requiring bridge code for mixed toolchains is the likely solution to avoid this cost. Note that this issue differs when considering interoperability for Carbon code instantiating C++ templates. The C++ templates must be in C++ headers for reuse, which in turn must compile with the Carbon toolchain to reuse the built C++ code, regardless of whether a separate C++ toolchain is in use. This may also be considered a constraint on mixed toolchain interoperability, but it's simpler to address and less likely to burden developers. To summarize, developers should expect that while _most_ features will work equivalently for mixed toolchains, there will never be full parity. ### Never require bridge code Corner cases of C++ will not receive equal support to common cases: the complexity of supporting any given construct must be balanced by the real world need for that support. For example: - Long-term, we expect interoperability will target all of C++, including new features as they are added, standardized, implemented, and adopted across the industry. The priority of _individual_ features will reflect how widely they are used in practice and how any gap impacts users trying to adopt Carbon. Exhaustive, high-quality support of the long-tail or corner cases of C++ features should not be assumed. - Support will be focused on idiomatic code, interfaces, and patterns used in widespread open source libraries or by other key constituencies. C++ code will have edge cases where the benefits of limiting Carbon's maintenance costs by avoiding complex interoperability outweighs the value of avoiding bridge code. - Support for low-level C ABIs may be focused on modern 64-bit ABIs, including Linux, POSIX, and a small subset of Windows' calling conventions. ### Convert all C++ types to Carbon types Non-zero overhead conversions should only be _supported_, never _required_, in order to offer reliable, unsurprising performance behaviors. This does not mean that conversions will _always_ be supported, as support is a cost-benefit decision for specific type mappings. For example, consider conversions between `std::vector` and an equivalent, idiomatic Carbon type: - Making conversions zero-overhead would require the Carbon type to mirror the memory layout and implementation semantics of `std::vector`. However, doing so would constrain the evolution of the Carbon type to match C++. Although some constraints are accepted for most primitive types, it would pose a major burden on Carbon's evolution to constrain Carbon's types to match C++ vocabulary type implementations. - These conversions may not always be present, but `std::vector` is a frequently used type. As a result, it can be expected that there will be functions supporting a copy-based conversion to the idiomatic Carbon type. - An interface which can hide the difference between whether `std::vector` or the equivalent, idiomatic Carbon type is in use may also be offered for common types. - It will still be normal to handle C++ types in Carbon code without conversions. Developers should be given the choice of when to convert. ### Support for C++ exceptions without bridge code Carbon may not provide seamless interoperability support for C++ exceptions. For example, translating C++ exceptions to or from Carbon errors might require annotations or bridge code, and those translations may have some performance overhead or lose information. Furthermore, if Carbon code calls a C++ function without suitable annotations or bridging, and that function exits with an exception, the program might terminate. ### Cross-language metaprogramming Carbon's metaprogramming design will be more restrictive than C++'s preprocessor macros. Although interoperability should handle simple cases, such as `#define STDIN_FILENO 0`, complex metaprogramming libraries may require a deep ability to understand code rewrites. It should be reasonable to have these instead rewritten to use Carbon's metaprogramming model. ### Offer equivalent support for languages other than C++ Long-term, it should be anticipated that Carbon will add interoperability with non-C++ languages. However, interoperability discussions will be focused on C++ in order to support the [language goal](/docs/project/goals.md#interoperability-with-and-migration-from-existing-c-code). Although we should work to consider extensibility when building interoperability facilities, C++ should be expected to have more robust support. Many languages do offer interoperability layers with C. Carbon's [C interoperability](#support-basic-c-interoperability) will likely offer a degree of multi-language interoperability using C as an intermediary. ## Open questions to be resolved later ### Carbon type inheritance from non-pure interface C++ types Some C++ APIs will expect that consumers use classes that inherit from a type provided by the API. It's desirable to have Carbon support, in some way, inheritance from API types in order to use these APIs. It may be sufficient to require the parent type be a pure interface, and that APIs with either use bridge code or switch implementations. That will be determined later. ### CRTP support Although [CRTP](https://en.wikipedia.org/wiki/Curiously_recurring_template_pattern) is a common technique in C++, interoperability support may require substantial work. Libraries based on use of CRTP may require bridge code or a rewrite for Carbon interoperability. More analysis should be done on the cost-benefit of supporting CRTP before making a support decision. ### Object lifetimes Carbon may have a different object lifetime design than C++. For example, Carbon may choose different rules for determining the lifetime of temporaries. This could affect idiomatic use of C++ APIs, turning code that would be safe in C++ into unsafe Carbon code, requiring developers to learn new coding patterns. More analysis should be done on object lifetimes and potential Carbon designs for it before deciding how to treat object lifetimes in the scope of interoperability. ## References - Proposal [#175: C++ interoperability goals](https://github.com/carbon-language/carbon-lang/pull/175) ================================================ FILE: docs/design/lambdas.md ================================================ # Lambdas ## Table of contents - [Syntax Overview](#syntax-overview) - [Return type](#return-type) - [Return expression](#return-expression) - [Explicit return type](#explicit-return-type) - [No return](#no-return) - [Implicit parameters in square brackets](#implicit-parameters-in-square-brackets) - [Parameters](#parameters) - [Syntax defined](#syntax-defined) - [Positional parameters](#positional-parameters) - [Positional parameter restrictions](#positional-parameter-restrictions) - [Captures](#captures) - [Capture modes](#capture-modes) - [Default capture mode](#default-capture-mode) - [Function fields in lambdas](#function-fields-in-lambdas) - [Copy semantics](#copy-semantics) - [Self and recursion](#self-and-recursion) - [Alternatives considered](#alternatives-considered) ## Syntax Overview One goal of Carbon's lambda syntax is to have continuity between lambdas and function declarations. Below are some example declarations: Implicit return types: ```carbon // In a variable: let lambda: auto = fn => T.Make(); // Equivalent in C++23: // const auto lambda = [] { return T::Make(); }; // As an argument to a function call: Foo(10, 20, fn => T.Make()); // Equivalent in C++23: // Foo(10, 20, [] { return T::Make(); }); ``` Explicit return types: ```carbon // In a variable: let lambda: auto = fn -> T { return T.Make(); }; // Equivalent in C++23: // const auto lambda = [] -> T { return T::Make(); }; // As an argument to a function call: PushBack(my_list, fn -> T { return T.Make() }); // Equivalent in C++23: // PushBack(my_list, [] { return T::Make(); }); ``` ### Return type There are three options for how a lambda expresses its return type, parallel to [how function declarations express returns](functions.md#return-clause): using a return expression, using an explicit return type, or having no return. #### Return expression A return expression is introduced with a double arrow (`=>`) followed by an expression describing the function's return value. In this case, the return type is determined by the type of the expression, as if the return type was `auto`. ```carbon // In a variable: let lambda: auto = fn => T.Make(); // Equivalent in C++23: // const auto lambda = [] { return T::Make(); }; // As an argument to a function call: Foo(fn => T.Make()); // Equivalent in C++23: // Foo([] { return T::Make(); }); ``` #### Explicit return type An explicit return type is introduced with a single arrow (`->`), followed by the return type, and finally the body of the lambda with a sequence of statements enclosed in curly braces (`{`...`}`). ```carbon // In a variable: let lambda: auto = fn -> T { return T.Make(); }; // Equivalent in C++23: // const auto lambda = [] -> T { return T::Make(); }; // As an argument to a function call: Foo(fn -> T { return T.Make(); }); // Equivalent in C++23: // Foo([] -> T { return T::Make(); }); ``` #### No return Lambdas that don't return anything end with a body of statements in curly braces (`{`...`}`). ```carbon // In a variable: let lambda: auto = fn { Print(T.Make()); }; // Equivalent in C++23: // const auto lambda = [] -> void { Print(T::Make()); }; // As an argument to a function call: Foo(fn { Print(T.Make()); }); // Equivalent in C++23: // Foo([] -> void { Print(T::Make()); }); ``` ### Implicit parameters in square brackets Lambdas support [captures](#captures), [fields](#function-fields-in-lambdas) and deduced parameters in the square brackets. ```carbon fn Foo(x: i32) { // In a variable: let lambda: auto = fn [var x, var y: i32 = 0] { Print(++x, ++y); }; // Equivalent in C++23: // const auto lambda = [x, y = int32_t{0}] mutable -> void { Print(++x, ++y); }; // As an argument to a function call: Foo(fn [var x, var y: i32 = 0] { Print(++x, ++y); }); // Equivalent in C++23: // Foo([x, y = int32_t{0}] mutable -> void { Print(++x, ++y); }); } ``` ### Parameters Lambdas also support so-called ["positional parameters"](#positional-parameters) that are defined at their point of use using a dollar sign and a non-negative integer. They are implicitly of type `auto`. ```carbon fn Foo() { let lambda: auto = fn { Print($0); }; // Equivalent in C++23: // auto lambda = [](auto _0, auto...) -> void { Print(_0); }; // Equivalent in Swift: // let lambda = { Print($0) }; } ``` Of course, lambdas can also have named parameters, but a single lambda can't have both named and positional parameters. ```carbon fn Foo() { // In a variable: let lambda: auto = fn (v: auto) { Print(v); }; // Equivalent in C++23: // const auto lambda = [](v: auto) -> void { Print(v); }; // As an argument to a function call: Foo(fn (v: auto) { Print(v); }); // Equivalent in C++23: // Foo([](v: auto) { Print(v); }); } ``` And in additional the option between positional and named parameters, deduced parameters are always permitted. ```carbon fn Foo() { let lambda: auto = fn [T:! Printable](t: T) { Print(t); }; } ``` ### Syntax defined Lambda expressions have one of the following syntactic forms (where items in square brackets are optional and independent): `fn`\[_implicit-parameters_\] \[_tuple-pattern_\] `=>` _expression_ `fn` \[_implicit-parameters_\] \[_tuple-pattern_\] \[`->` _return-type_\] `{` _statements_ `}` The first form is a shorthand for the second: "`=>` _expression_" is equivalent to "`-> auto { return` _expression_ `; }`". _implicit-parameters_ consists of square brackets enclosing a optional default capture mode and any number of explicit captures, function fields, and deduced parameters, all separated by commas. The default capture mode (if any) must come first; the other items can appear in any order. If _implicit-parameters_ is omitted, it is equivalent to `[]`. Function definitions are distinguished from lambdas by the presence of a name after the `fn` keyword. The presence of _tuple-pattern_ determines whether the function body uses named or positional parameters. The presence of "`->` _return-type_" determines whether the function body can (and must) return a value. To understand how the syntax between lambdas and function declarations is reasonably "continuous", refer to this table of syntactic positions and the following code examples. | Syntactic Position | Syntax Allowed in Given Position (optional, unless otherwise stated) | | :----------------: | :------------------------------------------------------------------------------------------------------------------: | | A1 | Required Returned Expression ([positional parameters](#positional-parameters) allowed) | | A2 | Required Returned Expression ([positional parameters](#positional-parameters) disallowed) | | B | [Default capture mode](#default-capture-mode) | | C | Explicit [Captures](#captures), [Function fields](#function-fields-in-lambdas) and Deduced Parameters (in any order) | | D | Explicit Parameters | | E1 | Body of Statements (no return value) ([positional parameters](#positional-parameters) allowed) | | E2 | Body of Statements (with return value) ([positional parameters](#positional-parameters) allowed) | | E3 | Body of Statements (no return value) ([positional parameters](#positional-parameters) disallowed) | | E4 | Body of Statements (with return value) ([positional parameters](#positional-parameters) disallowed) | | F | Required Return Type | ```carbon // Lambdas (all the following are in an expression context and are // themselves expressions) fn => A1 fn [B, C] => A1 fn (D) => A2 fn [B, C](D) => A2 fn { E1; } fn -> F { E2; } fn [B, C] { E1; } fn [B, C] -> F { E2; } fn (D) { E3; } fn (D) -> F { E4; } fn [B, C](D) { E3; } fn [B, C](D) -> F { E4; } ``` ## Positional parameters Positional parameters, denoted by a dollar sign followed by a non-negative integer (for example, $3), are auto-typed parameters defined within the lambda's body. ```carbon let lambda: auto = fn => $0 ``` They can be used in any lambda declaration that lacks an explicit parameter list (parentheses). They are variadic by design, meaning an unbounded number of arguments can be passed to any function that lacks an explicit parameter list. Only the parameters that are named in the body will be read from, meaning the highest named parameter denotes the minimum number of arguments required by the function. The lambda body is free to omit lower-numbered parameters (ex: `fn { Print($10); }`). This syntax was inpsired by Swift's [Shorthand Argument Names](https://docs.swift.org/swift-book/documentation/the-swift-programming-language/closures/#Shorthand-Argument-Names). ```carbon // A lambda that takes two positional parameters being used as a comparator Sort(my_list, fn => $0.val < $1.val); // In Swift: { $0.val < $1.val } ``` ### Positional parameter restrictions Lambdas with positional parameters have the restriction that they can only be used in a context where there is exactly one enclosing function or lambda that has no explicit parameter list. For example: ```carbon fn Foo1 { // ❌ Invalid: Foo1 is already using positional parameters let lambda: auto = fn => $0 < $1 } fn Foo2 { my_list.Sort( // ❌ Invalid: Foo2 is already using positional parameters fn => $0 < $1 ); } fn Foo3() { my_list.Sort( // ✅ Valid: Foo3 has explicit parameters fn => $0 < $1 ); } fn Foo4() { let lambda: auto = fn -> bool { // ❌ Invalid: Outer lambda is already using positional parameters return (fn => $0 < $1)($0, $1); }; } fn Foo5() { let lambda: auto = fn (x: i32, y: i32) -> bool { // ✅ Valid: Outer lambda has explicit parameters return (fn => $0 < $1)(x, y); }; } ``` ## Captures Captures in Carbon mirror the non-init captures of C++. A capture declaration consists of a capture mode (for `var` captures) followed by the name of a binding from the enclosing scope, and makes that identifier available in the inner function body. The lifetime of a capture is the lifetime of the function in which it exists. For example... ```carbon fn Foo() { let handle: Handle = Handle.Get(); var thread: Thread = Thread.Make(fn [var handle] { handle.Process(); }); thread.Join(); } ``` ```carbon fn Foo() { let handle: Handle = Handle.Get(); fn MyThread[handle]() { handle.Process(); } var thread: Thread = Thread.Make(MyThread); thread.Join(); } ``` ### Capture modes Lambdas can capture variables from their surrounding scope using `let` or `var`, just like regular bindings. Capture modes can be used as [default capture mode specifiers](#default-capture-mode) or for explicit captures as shown in the example code below. ```carbon fn Example() { var a: i32 = 0; var b: i32 = 0; let lambda: auto = fn [a, var b] { // ❌ Invalid: by-value captures are immutable (default `let`) a += 1; // ✅ Valid: `b` is a mutable copy (captured with `var`) b += 1; }; lambda(); } ``` ```carbon fn Example { fn Invalid() -> auto { var s: String = "Hello world"; return fn [s]() => s; } // ❌ Invalid: returned lambda references `s` which is no longer alive // when the lambda is invoked. Print(Invalid()()); } ``` Note: If a function object F has mutable state, either because it has a by-object capture or because it has a by-object function field, then a call to F should require the callee to be a reference expression rather than a value expression. We need a mutable handle to the function in order to be able to mutate its mutable state. ### Default capture mode By default, there is no capturing in lambdas. The lack of any square brackets is the same as an empty pair of square brackets. Users can opt into capturing behavior. This is done either by way of individual explicit captures, or more succinctly by way of a default capture mode. The default capture mode roughly mirrors the syntax `[=]` and `[&]` capture modes from C++ by being the first thing to appear in the square brackets. ```carbon fn Foo1() { let handle: Handle = Handle.Get(); fn MyThread[var]() { // `handle` is captured by-object due to the default capture // mode specifier of `var` handle.Process(); } var thread: Thread = Thread.Make(MyThread); thread.Join(); } fn Foo2() { let handle: Handle = Handle.Get(); fn MyThread[let]() { // `handle` is captured by-value due to the default capture // mode specifier of `let` handle.Process(); } var thread: Thread = Thread.Make(MyThread); thread.Join(); } ``` ## Function fields in lambdas Function fields in lambdas mirror the behavior of init captures in C++. A function field definition consists of an irrefutable pattern, `=`, and an initializer. It matches the pattern with the initializer when the lambda definition is evaluated. The bindings in the pattern have the same lifetime as the function, and their scope extends to the end of the function body. ```carbon fn Foo() { var h1: Handle = Handle.Get(); var h2: Handle = Handle.Get(); var thread: Thread = Thread.Make(fn [a: auto = h1, var b: auto = h2] { a.Process(); b.Process(); }); thread.Join(); } ``` ## Copy semantics To mirror the behavior of C++, lambdas will be as copyable as their contained function fields and function captures. This means that, if a function holds a by-object function field, if the type of the field is copyable, so too is the function that contains it. This also applies to captures. The other case is by-value function fields. Since C++ const references, when made into fields of a class, prevent the class from being copied assigned, so too should by-value function fields prevent the function in which it is contained from being copied assigned. ## Self and recursion To mirror C++'s use of capturing `this`, `self` should always come from the outer scope as a capture. `self: Self` is never permitted on lambdas. ```carbon // ❌ Not allowed let lambda: auto = fn [self: Self] { self.F(); }; // ✅ Captures `self` from outer scope let lambda: auto = fn [self] { self.F(); }; ``` Note: Following [#3720](https://github.com/carbon-language/carbon-lang/pull/3720), an expression of the form `x.(F)`, where `F` is a function with a `self` or `ref self` parameter, produces a callable that holds the value of `x`, and does not hold the value of `F`. As a consequence, we can't support combining captures and function fields with a `self` parameter. ## Alternatives considered - [Terse vs Elaborated](/proposals/p3848.md#alternative-considered-terse-vs-elaborated) - [Sigil](/proposals/p3848.md#alternative-considered-sigil) - [Additional Positional Parameter Restriction](/proposals/p3848.md#alternative-considered-additional-positional-parameter-restriction) - [Recursive Self](/proposals/p3848.md#alternative-considered-recursive-self) ================================================ FILE: docs/design/lexical_conventions/README.md ================================================ # Lexical conventions ## Table of contents - [Lexical elements](#lexical-elements) ## Lexical elements The first stage of processing a [source file](/docs/design/code_and_name_organization/source_files.md) is the division of the source file into lexical elements. A _lexical element_ is one of the following: - a maximal sequence of [whitespace](whitespace.md) characters - a [word](words.md) - a literal: - a [numeric literal](numeric_literals.md) - a [string literal](string_literals.md) - a [comment](comments.md) - a [symbolic token](symbolic_tokens.md) The sequence of lexical elements is formed by repeatedly removing the longest initial sequence of characters that forms a valid lexical element, with the following exception: - When a numeric literal immediately follows a `.` or `->` token, with no intervening whitespace, a real literal is never formed. Instead, the token will end no later than the next `.` character. For example, `tuple.1.2` is five tokens, `tuple` `.` `1` `.` `2`, not three tokens, `tuple` `.` `1.2`. However, `tuple . 1.2` is lexed as three tokens. ================================================ FILE: docs/design/lexical_conventions/comments.md ================================================ # Comments ## Table of contents - [Overview](#overview) - [Details](#details) - [Alternatives considered](#alternatives-considered) - [References](#references) ## Overview A comment is a lexical element beginning with the characters `//` and running to the end of the line. We have no mechanism for physical line continuation, so a trailing `\` does not extend a comment to subsequent lines. ## Details In the comments after the `//` a whitespace character is required to make the comment valid. Newline is a whitespace character, so a line containing only `//` is a valid comment. The end of the file also constitutes whitespace. All comments are removed prior to formation of tokens. Example: ``` // This is a comment and is ignored. \ This is not a comment. var Int: x; // error, trailing comments not allowed ``` Currently no support for block comments is provided. Commenting out larger regions of human-readable text or code is accomplished by commenting out every line in the region. ## Alternatives considered - [Intra-line comments](/proposals/p0198.md#intra-line-comments) - [Multi-line text comments](/proposals/p0198.md#multi-line-text-comments) - [Block comments](/proposals/p0198.md#block-comments-2) - [Documentation comments](/proposals/p0198.md#documentation-comments) - [Code folding comments](/proposals/p0198.md#code-folding-comments) ## References - Proposal [#198: Comments](https://github.com/carbon-language/carbon-lang/pull/198) ================================================ FILE: docs/design/lexical_conventions/numeric_literals.md ================================================ # Numeric literals ## Table of contents - [Overview](#overview) - [Details](#details) - [Integer literals](#integer-literals) - [Real-number literals](#real-number-literals) - [Digit separators](#digit-separators) - [Divergence from other languages](#divergence-from-other-languages) - [Alternatives considered](#alternatives-considered) - [References](#references) ## Overview The following syntaxes are supported: - [Integer literals](#integer-literals) - `12345` (decimal) - `0x1FE` (hexadecimal) - `0o755` (octal) - `0b1010` (binary) - [Real-number literals](#real-number-literals) - `123.456` (digits on both sides of the `.`) - `123.456e789` (optional `+` or `-` after the `e`) - `0x1.2p123` (optional `+` or `-` after the `p`) - [Digit separators](#digit-separators) (`_`) Note that real-number literals always contain a `.` with digits on both sides, and integer literals never contain a `.`. Literals are case-sensitive. Unlike in C++, literals do not have a suffix to indicate their type. ## Details ### Integer literals Decimal integers are written as a non-zero decimal digit followed by zero or more additional decimal digits, or as a single `0`. Integers in other bases are written as a `0` followed by a base specifier character, followed by a sequence of one or more digits in the corresponding base. The available base specifiers and corresponding bases are: | Base specifier | Base | Digits | | -------------- | ---- | ------------------------ | | `b` | 2 | `0` and `1` | | `o` | 8 | `0` ... `7` | | `x` | 16 | `0` ... `9`, `A` ... `F` | The above table is case-sensitive. For example, `0b1`, `0o7`, and `0x1A` are valid, and `0B1`, `0O7`, `0X1A`, and `0x1a` are invalid. A zero at the start of a literal can never be followed by another digit: either the literal is `0`, the `0` begins a base specifier, or the next character is a decimal point (see below). The `0o` prefix is used for octal literals; a C-style `0755` octal is invalid in Carbon. ### Real-number literals Real numbers are written as a decimal or hexadecimal integer followed by a period (`.`) followed by a sequence of one or more decimal or hexadecimal digits, respectively. A digit is required on each side of the period. `0.` and `.3` are both lexed as two separate tokens: `0.(Util.Abs)()` and `tuple.3` both treat the period as member or element access, not as a radix point. To support tuple indexing, a real number literal is never formed immediately following a `.` token with no intervening whitespace. Instead, the result is an integer literal. A real number can be followed by an exponent character, an optional `+` or `-` (defaulting to `+` if absent), and a character sequence matching the grammar of a decimal integer with some value _N_. For a decimal real number, the exponent character is `e`, and the effect is to multiply the given value by 10±_N_. For a hexadecimal real number, the exponent character is `p`, and the effect is to multiply the given value by 2±_N_. The exponent suffix is optional for both decimal and hexadecimal real numbers. Note that a decimal integer followed by `e` is not a real-number literal. For example, `3e10` is not a valid literal. When a real-number literal is interpreted as a value of a real-number type, its value is the representable real number closest to the value of the literal. In the case of a tie, the nearest value whose mantissa is even is selected. The decimal real number syntax allows for any decimal fraction to be expressed -- that is, any number of the form _a_ x 10-_b_, where _a_ is an integer and _b_ is a non-negative integer. Because the decimal fractions are dense in the reals and the set of values of the real-number type is assumed to be discrete, every value of the real-number type can be expressed as a real number literal. However, for certain applications, directly expressing the intended real-number representation may be more convenient than producing a decimal equivalent that is known to convert to the intended value. Hexadecimal real-number literals are provided in order to permit values of binary floating or fixed point real-number types to be expressed directly. ### Digit separators A digit separator (`_`) may occur between any two digits within a literal. For example: - Decimal integers: `1_23_456_7890` - Hexadecimal integers: `0x7_F_FF_FFFF` - Octal literals: `0o7_55` - Binary literals: `0b1_000_101_11` - Real-number literals: `2_147.48_3648e12_345` or `0x1_00CA.FE_F00Dp+2_4` ## Divergence from other languages The design provides a syntax that is deliberately close to that used both by C++ and many other languages, so it should feel familiar to developers. However, it selects a reasonably minimal subset of the syntaxes. This minimal approach provides benefits directly in line with the goal that Carbon code should be [easy to read, understand, and write](/docs/project/goals.md#code-that-is-easy-to-read-understand-and-write): - Reduces unnecessary choices for programmers. - Simplifies the syntax rules of the language. - Improves consistency of written Carbon code. That said, it still provides sufficient variations to address important use cases for the goal of not leaving room for a lower level language: - Hexadecimal, octal, and binary integer literals. - Scientific notation floating point literals. - Hexadecimal (scientific) floating point literals. ## Alternatives considered - [Integer bases](/proposals/p0143.md#integer-bases) - [Octal literals](/proposals/p0143.md#octal-literals) - [Decimal literals](/proposals/p0143.md#decimal-literals) - [Case sensitivity](/proposals/p0143.md#case-sensitivity) - [Real number syntax](/proposals/p0143.md#real-number-syntax) - [Disallow ties](/proposals/p0866.md) - [Digit separator syntax](/proposals/p0143.md#digit-separator-syntax) - [3-digit decimal groupings](/proposals/p1983.md#3-digit-decimal-groupings) - [2-digit or 4-digit hexadecimal digit groupings](/proposals/p1983.md#2-digit-or-4-digit-hexadecimal-digit-groupings) - [Disallow digit separators in fractions](/proposals/p1983.md#disallow-digit-separators-in-fractions) - [No octal literals](/proposals/p6910.md#no-octal-literals) ## References - Proposal [#143: Numeric literals](https://github.com/carbon-language/carbon-lang/pull/143) - Proposal [#866: Allow ties in floating literals](https://github.com/carbon-language/carbon-lang/pull/866) - Proposal [#1983: Weaken digit separator placement rules](https://github.com/carbon-language/carbon-lang/pull/1983) - Proposal [#6910: Support octal literals](https://github.com/carbon-language/carbon-lang/pull/6910) ================================================ FILE: docs/design/lexical_conventions/string_literals.md ================================================ # String literals ## Table of contents - [Overview](#overview) - [Details](#details) - [Simple and block string literals](#simple-and-block-string-literals) - [Escape sequences](#escape-sequences) - [Raw string literals](#raw-string-literals) - [Encoding](#encoding) - [Alternatives considered](#alternatives-considered) - [References](#references) ## Overview Carbon supports both simple literals that are single-line using one double quotation mark (`"`) and block literals that are multi-line using three single quotation marks (`'''`). A block string literal may have a file type indicator after the first `'''`; this does not affect the string itself, but may assist other tooling. For example: ```carbon // Simple string literal: var simple: String = "example"; // Block string literal: var block: String = ''' The winds grow high; so do your stomachs, lords. How irksome is this music to my heart! When such strings jar, what hope of harmony? I pray, my lords, let me compound this strife. -- History of Henry VI, Part II, Act II, Scene 1, W. Shakespeare '''; // Block string literal with file type indicator: var code_block: String = '''cpp #include int main() { std::cout << "Hello world!"; return 0; } ''' ``` The indentation of a block string literal's terminating line is removed from all preceding lines. As a consequence, in the above `code_block` example, only `std::cout` and `return` are indented in the resulting string, and by 4 spaces each. Escape sequences introduced by a backslash (`\`) and are used to express special character or code unit sequences, such as `\n` for a newline character. Raw string literals are additionally delimited with one or more `#`; these require an equal number of hash symbols (`#`) after the `\` to indicate an escape sequence. Raw string literals are used to more easily write literal `\`s in strings. Both simple and block string literals have raw forms. For example: ```carbon // Raw simple string literal with newline escape sequence: var newline: String = "line one\nline two"; // Raw simple string literal with literal `\n`, not a newline: var raw: String = #"line one\nstill line one"#; // Raw simple string literal with newline escape sequence: var raw_newline: String = #"line one\#nline two"#; ``` ## Details ### Simple and block string literals A _simple string literal_ is formed of a sequence of: - Characters other than `\` and `"`. - Only space characters (U+0020) are valid whitespace in a string literal. - Other [horizontal whitespace](whitespace.md), including tabs, are disallowed but parse as part of the string for error recovery purposes. - Vertical whitespace will not parse as part of a simple string literal. - [Escape sequences](#escape-sequences). - Each escape sequence is replaced with the corresponding character sequence or code unit sequence. - Similarly to invalid whitespace, invalid escape sequences such as `\z` parse as part of the string. This sequence is enclosed in `"`s. For example, this is a simple string literal: ```carbon var String: lucius = "The strings, my lord, are false."; ``` Adjacent string literals are disallowed, like the following: ```carbon // The three adjacent simple string literals `""`, `"abc"` and `""` are invalid. var String: block = """abc"""; ``` String literals starting with triple double quotation marks `"""` are adjacent string literals. It is important to reject and diagnose them. A _block string_ literal starts with `'''`. Characters on the same line following the `'''` are an optional file type indicator. The literal ends at the next instance of three single quotation marks whose first `'` is not part of a `\'` escape sequence. The closing `'''` shall be the first non-whitespace characters on that line. The lines between the opening line and the closing line (exclusive) are _content lines_. The content lines shall not contain `\` characters that do not form part of an escape sequence. The _indentation_ of a block string literal is the sequence of horizontal whitespace preceding the closing `'''`. Each non-empty content line shall begin with the indentation of the string literal. The content of the literal is formed as follows: - The indentation of the closing line is removed from each non-empty content line. - All trailing whitespace on each line, including the line terminator, is replaced with a single line feed (U+000A) character. - The resulting lines are concatenated. - Each [escape sequence](#escape-sequences) is replaced with the corresponding character sequence or code unit sequence. A content line is considered empty if it contains only whitespace characters. ```carbon // All block string literals contain a trailing newline by default. var String: newline_example = ''' This is a block string literal. Its first character is 'T' and its last character is a newline. It contains another newline character between 'is' and 'a'. '''; // Newlines can be suppressed using the escape character '\' var String: suppressed_newlines = ''' This is another block string literal. The newline character here \ is suppressed, along with the trailing newline here.\ '''; // This block string literal is invalid because the ''' after 'closing' terminates // the literal, but is not at the start of the line. var String: invalid = ''' error: closing ''' is not on its own line. '''; ``` A _file type indicator_ is any sequence of non-whitespace characters other than `'` or `#`. The file type indicator has no semantic meaning to the Carbon compiler, but some file type indicators are understood by the language tooling (for example, syntax highlighter, code formatter) as indicating the structure of the string literal's content. ```carbon // This is a block string literal. Its first two characters are spaces, and its // last character is a line feed. It has a file type of 'c++'. var String: starts_with_whitespace = '''c++ int x = 1; // This line starts with two spaces. int y = 2; // This line starts with two spaces. '''; ``` The file type indicator might contain semantic information beyond the file type itself, such as instructions to the code formatter to disable formatting for the code block. **Open question:** There is no concrete set of recognized file type indicators. It would be useful to informally specify a set of well-known indicators, so that tools have a common understanding of what those indicators mean, perhaps in a best practices guide. #### Escape sequences Within a string literal, the following escape sequences are recognized: | Escape | Meaning | | ------------- | -------------------------------------------------------- | | `\t` | U+0009 CHARACTER TABULATION | | `\n` | U+000A LINE FEED | | `\r` | U+000D CARRIAGE RETURN | | `\"` | U+0022 QUOTATION MARK (`"`) | | `\'` | U+0027 APOSTROPHE (`'`) | | `\\` | U+005C REVERSE SOLIDUS (`\`) | | `\0` | Code unit with value 0 | | `\0D` | Invalid, reserved for evolution | | `\xHH` | Code unit with value HH16 | | `\u{HHHH...}` | Unicode code point U+HHHH... | | `\` | No string literal content produced (block literals only) | Hex characters (`H`) must be uppercase (`\xAA`, not `\xaa`). This includes all C++ escape sequences except: - `\?`, which was historically used to escape trigraphs in string literals, and no longer serves any purpose. - `\ooo` octal escapes, which are removed because Carbon does not support octal literals; `\0` is retained as a special case, which is expected to be important for C interoperability. - `\uABCD`, which is replaced by `\u{ABCD}`. - `\U0010FFFF`, which is replaced by `\u{10FFFF}`. - `\a` (bell), `\b` (backspace), `\v` (vertical tab), and `\f` (form feed). `\a` and `\b` are obsolescent, and `\f` and `\v` are largely obsolete. These characters can be expressed with `\x07`, `\x08`, `\x0B`, and `\x0C` respectively if needed. Note that this is the same set of escape sequences supported by [Swift](https://docs.swift.org/swift-book/LanguageGuide/StringsAndCharacters.html#ID295) and [Rust](https://doc.rust-lang.org/reference/tokens.html), except that, unlike in Swift, support for `\xHH` is provided. While octal escape sequences are expected to remain not permitted (even though `\0D` is reserved), the decision to not support `\1`..`\7` or more generally `\DDDD` is _experimental_. In the above table, `H` represents an arbitrary hexadecimal character, `0`-`9` or `A`-`F` (case-sensitive). Unlike in C++, but like in Python, `\x` expects exactly two hexadecimal digits. As in JavaScript, Rust, and Swift, Unicode code points can be expressed by number using `\u{10FFFF}` notation. This accepts between 1 and 8 hexadecimal characters. Any numeric code point in the ranges 016-D7FF16 or E00016-10FFFF16 can be expressed this way. _Open question:_ Some programming languages (notably Python) support a `\N{unicode character name}` syntax. We could add such an escape sequence. Future proposals considering adding such support should pay attention to work done by C++'s Unicode study group in this area. The escape sequence `\0` shall not be followed by a decimal digit. In cases where a null byte should be followed by a decimal digit, `\x00` can be used instead: `"foo\x00123"`. The intent is to preserve the possibility of permitting decimal escape sequences in the future. A backslash followed by a line feed character is an escape sequence that produces no string contents. This escape sequence is _experimental_, and can only appear in block string literals. This escape sequence is processed after trailing whitespace is replaced by a line feed character, so a `\` followed by horizontal whitespace followed by a line terminator removes the whitespace up to and including the line terminator. Unlike in Rust, but like in Swift, leading whitespace on the line after an escaped newline is not removed, other than whitespace that matches the indentation of the terminating `'''`. A character sequence starting with a backslash that doesn't match any known escape sequence is invalid. Whitespace characters other than space and, for block string literals, new line optionally preceded by carriage return are disallowed. All other characters (including non-printable characters) are preserved verbatim. Because all Carbon source files are required to be valid sequences of Unicode characters, code unit sequences that are not valid UTF-8 can only be produced by `\x` escape sequences. The decision to disallow raw tab characters in string literals is _experimental_. ```carbon var String: fret = "I would 'twere something that would fret the string,\n" + "The master-cord on's \u{2764}\u{FE0F}!"; // This string contains two characters (prior to encoding in UTF-8): // U+1F3F9 (BOW AND ARROW) followed by U+0032 (DIGIT TWO) var String: password = "\u{1F3F9}2"; // This string contains no newline characters. var String: type_mismatch = ''' Shall I compare thee to a summer's day? Thou art \ more lovely and more temperate.\ '''; var String: trailing_whitespace = ''' This line ends in a space followed by a newline. \n\ This line starts with four spaces. '''; ``` ### Raw string literals In order to allow strings whose contents include `\`s and `"`s, the delimiters of string literals can be customized by prefixing the opening delimiter with _N_ `#` characters. A closing delimiter for such a string is only recognized if it is followed by _N_ `#` characters, and similarly, escape sequences in such string literals are recognized only if the `\` is also followed by _N_ `#` characters. A `\`, `"`, or `'''` not followed by _N_ `#` characters has no special meaning. | Opening delimiter | Escape sequence introducer | Closing delimiter | | ----------------- | ----------------------------- | ----------------- | | `"` / `'''` | `\` (for example, `\n`) | `"` / `'''` | | `#"` / `#'''` | `\#` (for example, `\#n`) | `"#` / `'''#` | | `##"` / `##'''` | `\##` (for example, `\##n`) | `"##` / `'''##` | | `###"` / `###'''` | `\###` (for example, `\###n`) | `"###` / `'''###` | | ... | ... | ... | For example: ```carbon var String: x = #''' This is the content of the string. The 'T' is the first character of the string. ''' <-- This is not the end of the string. '''#; // But the preceding line does end the string. // OK, final character is \ var String: y = #"Hello\"#; var String: z = ##"Raw strings #"nesting"#"##; var String: w = #"Tab is expressed as \t. Example: '\#t'"#; ``` ### Encoding A string literal results in a sequence of 8-bit bytes. Like Carbon source files, string literals are encoded in UTF-8. There is no guarantee that the string is valid UTF-8, however, because arbitrary byte sequences can be inserted by way of `\xHH` escape sequences. This is _experimental_, and should be revisited if we find sufficient motivation for directly expressing string literals in other encodings. Similarly, as library support for a string type evolves, we should consider including string literal syntax (perhaps as the default) that guarantees the string content is a valid UTF-8 encoding, so that valid UTF-8 can be distinguished from an arbitrary string in the type system. In such string literals, we should consider rejecting `\xHH` escapes in which HH is greater than 7F16, as in Rust. ## Alternatives considered - [Block string literals](/proposals/p0199.md#block-string-literals) - [Leading whitespace removal](/proposals/p0199.md#leading-whitespace-removal) - [Terminating newline](/proposals/p0199.md#terminating-newline) - [Escape sequences](/proposals/p0199.md#escape-sequences-1) - Unicode escape sequences: - [Allow zero digits](/proposals/p2040.md#allow-zero-digits) - [Allow any number of hexadecimal characters](/proposals/p2040.md#allow-any-number-of-hexadecimal-characters) - [Limiting to 6 digits versus 8](/proposals/p2040.md#limiting-to-6-digits-versus-8) - [Raw string literals](/proposals/p0199.md#raw-string-literals-1) - [Trailing whitespace](/proposals/p0199.md#trailing-whitespace) - [Line separators](/proposals/p0199.md#line-separators) - [Internal whitespace](/proposals/p0199.md#internal-whitespace) - [Different restrictions for file type indicators](https://github.com/carbon-language/carbon-lang/issues/2140) ## References - Proposal [#199: String literals](https://github.com/carbon-language/carbon-lang/pull/199) - Proposal [#2040: Unicode escape code length](https://github.com/carbon-language/carbon-lang/pull/2040) ================================================ FILE: docs/design/lexical_conventions/symbolic_tokens.md ================================================ # Symbolic Tokens ## Table of contents - [Overview](#overview) - [Details](#details) - [Symbolic token list](#symbolic-token-list) - [Alternatives considered](#alternatives-considered) - [References](#references) ## Overview A _symbolic token_ is one of a fixed set of [tokens](https://en.wikipedia.org/wiki/Lexical_analysis#Token) that consist of characters that are not valid in identifiers. That is, they are tokens consisting of symbols, not letters or numbers. Operators are one use of symbolic tokens, but they are also used in patterns `:`, declarations (`->` to indicate return type, `,` to separate parameters), statements (`;`, `=`, and so on), and other places (`,` to separate function call arguments). Carbon has a fixed set of symbolic tokens, defined by the language specification. Developers cannot define new symbolic tokens in their own code. Symbolic tokens are lexed using a "max munch" rule: at each lexing step, the longest symbolic token defined by the language specification that appears starting at the current input position is lexed, if any. When a symbolic token is used as an operator, the surrounding whitespace must follow certain rules: - There can be no whitespace between a unary operator and its operand. - The whitespace around a binary operator must be consistent: either there is whitespace on both sides or on neither side. - If there is whitespace on neither side of a binary operator, the token before the operator must be an identifier, a literal, or any kind of closing bracket (for example, `)`, `]`, or `}`), and the token after the operator must be an identifier, a literal, or any kind of opening bracket (for example, `(`, `[`, or `{`). These rules enable us to use a token like `*` as a prefix, infix, and postfix operator, without creating ambiguity. ## Details ### Symbolic token list The following is the initial list of symbolic tokens recognized in a Carbon source file: | Symbolic Tokens | Explanation | | --------------- | ------------------------------------------------------------------------------------------------------------ | | `+` | Addition | | `-` | Subtraction and negation | | `*` | Indirection, multiplication, and forming pointer types | | `/` | Division | | `%` | Modulus | | `^` | Complementing and Bitwise XOR | | `&` | Address-of and Bitwise AND | | `\|` | Bitwise OR | | `<<` | Arithmetic and Logical Left-shift | | `>>` | Arithmetic and Logical Right-shift | | `=` | Assignment and initialization | | `++` | Increment | | `--` | Decrement | | `+=` | Add-and-assign | | `-=` | Subtract-and-assign | | `*=` | Multiply-and-assign | | `/=` | Divide-and-assign | | `%=` | Modulus-and-assign | | `&=` | Bitwise-AND-and-assign | | `\|=` | Bitwise-OR-and-assign | | `^=` | Bitwise-XOR-and-assign | | `<<=` | Left-shift-and-assign | | `>>=` | Right-shift-and-assign | | `==` | Equality or equal to | | `!=` | Inequality or not equal to | | `>` | Greater than | | `>=` | Greater than or equal to | | `<` | Less than | | `<=` | Less than or equal to | | `->` | Return type and indirect member access | | `=>` | Match syntax | | `[` and `]` | Subscript and deduced parameter lists | | `(` and `)` | Function call, function declaration, and tuple literals | | `{` and `}` | Struct literals, blocks of control flow statements, and the bodies of definitions (classes, functions, etc.) | | `,` | Separate tuple and struct elements | | `.` | Member access | | `:` | Name binding patterns | | `:!` | Compile-time binding patterns | | `;` | Statement separator | ## Alternatives considered [Alternatives from proposal #601](/proposals/p0601.md#alternatives-considered): - lex the longest sequence of symbolic characters rather than lexing only the longest known operator - support an extensible operator set - different whitespace restrictions or no whitespace restrictions ## References - Proposal [#162: Basic Syntax](https://github.com/carbon-language/carbon-lang/pull/162) - Proposal [#339: Add `var [ = ];` syntax for variables](https://github.com/carbon-language/carbon-lang/pull/339) - Proposal [#438: Add statement syntax for function declarations](https://github.com/carbon-language/carbon-lang/pull/438) - Proposal [#561: Basic classes: use cases, struct literals, struct types, and future work](https://github.com/carbon-language/carbon-lang/pull/561) - Proposal [#601: Operator tokens](https://github.com/carbon-language/carbon-lang/pull/601) - Proposal [#676: `:!` generic syntax](https://github.com/carbon-language/carbon-lang/pull/676) - Proposal [#702: Comparison operators](https://github.com/carbon-language/carbon-lang/pull/702) - Proposal [#989: Member access expressions](https://github.com/carbon-language/carbon-lang/pull/989) - Proposal [#1083: Arithmetic expressions](https://github.com/carbon-language/carbon-lang/pull/1083) - Proposal [#1191: Bitwise operators](https://github.com/carbon-language/carbon-lang/pull/1191) - Proposal [#2188: Pattern matching syntax and semantics](https://github.com/carbon-language/carbon-lang/pull/2188) - Proposal [#2274: Subscript syntax and semantics](https://github.com/carbon-language/carbon-lang/pull/2274) - Proposal [#2511: Assignment statements](https://github.com/carbon-language/carbon-lang/pull/2511) - Proposal [#2665: Semicolons terminate statements](https://github.com/carbon-language/carbon-lang/pull/2665) ================================================ FILE: docs/design/lexical_conventions/whitespace.md ================================================ # Whitespace ## Table of contents - [Overview](#overview) - [References](#references) ## Overview The exact lexical form of Carbon whitespace has not yet been settled. However, Carbon will follow lexical conventions for whitespace based on [Unicode Annex #31](https://unicode.org/reports/tr31/). TODO: Update this once the precise rules are decided; see the [Unicode source files](/proposals/p0142.md#characters-in-identifiers-and-whitespace) proposal. Unicode Annex #31 suggests selecting whitespace characters based on the characters with Unicode property `Pattern_White_Space`, which is currently these 11 characters: - Horizontal whitespace: - U+0009 CHARACTER TABULATION (horizontal tab) - U+0020 SPACE - U+200E LEFT-TO-RIGHT MARK - U+200F RIGHT-TO-LEFT MARK - Vertical whitespace: - U+000A LINE FEED (traditional newline) - U+000B LINE TABULATION (vertical tab) - U+000C FORM FEED (page break) - U+000D CARRIAGE RETURN - U+0085 NEXT LINE (Unicode newline) - U+2028 LINE SEPARATOR - U+2029 PARAGRAPH SEPARATOR The quantity and kind of whitespace separating tokens is ignored except where otherwise specified. ## References - Proposal [#142: Unicode source files](https://github.com/carbon-language/carbon-lang/pull/142) ================================================ FILE: docs/design/lexical_conventions/words.md ================================================ # Words ## Table of contents - [Overview](#overview) - [Keywords](#keywords) - [Type literals](#type-literals) - [Identifiers](#identifiers) - [Raw identifiers](#raw-identifiers) - [Alternatives considered](#alternatives-considered) - [References](#references) ## Overview A _word_ is a lexical element formed from a sequence of letters or letter-like characters, such as `fn` or `Foo` or `Int`, optionally preceded by `r#`. The exact lexical form of words has not yet been settled. However, Carbon will follow lexical conventions for identifiers based on [Unicode Annex #31](https://unicode.org/reports/tr31/). TODO: Update this once the precise rules are decided; see the [Unicode source files](/proposals/p0142.md#characters-in-identifiers-and-whitespace) proposal. Carbon source files, including comments and string literals, are required to be in Unicode Normalization Form C (NFC). ## Keywords The following words are interpreted as keywords: - `abstract` - `adapt` - `alias` - `and` - `as` - `auto` - `base` - `break` - `Core` - `case` - `choice` - `class` - `constraint` - `continue` - `default` - `destroy` - `else` - `export` - `extend` - `final` - `fn` - `for` - `forall` - `friend` - `if` - `impl` - `impls` - `import` - `in` - `interface` - `let` - `library` - `like` - `match` - `namespace` - `not` - `observe` - `or` - `override` - `package` - `partial` - `private` - `protected` - `ref` - `require` - `return` - `returned` - `Self` - `self` - `template` - `then` - `type` - `var` - `virtual` - `where` - `while` ### Type literals A word starting with `i`, `u`, or `f`, followed by a decimal integer, is a [_numeric type literal_](/docs/design/expressions/literals.md#numeric-type-literals). ### Identifiers A word is interpreted as an _identifier_ if it is neither a keyword nor a type literal. ### Raw identifiers A _raw identifier_ is a word starting with `r#`. A raw identifier is equivalent to the word following the `r#` prefix, except that it is always interpreted as an identifier, even if it would otherwise be a keyword or type literal. Raw identifiers can be used to specify identifiers which have the same spelling as keywords; for example, `r#impl`. This can be useful when interoperating with C++ code that uses identifiers that are keywords in Carbon, and when migrating between versions of Carbon. The word doesn't need to be a keyword, in order to support forwards compatibility when a keyword is planned to be added. If `word` is an identifier, then `word` and `r#word` have the same meaning. ## Alternatives considered Overview: - [Character encoding: We could restrict words to ASCII.](/proposals/p0142.md#character-encoding-1) - [Normalization form alternatives considered](/proposals/p0142.md#normalization-forms) Type literals: - [Use C++ type keywords with LP64 convention](/proposals/p2015.md#c-lp64-convention) - [Use full type name with length suffix](/proposals/p2015.md#type-name-with-length-suffix) - [Use uppercase for type names](/proposals/p2015.md#uppercase-suffixes) - [Support additional bit widths](/proposals/p2015.md#additional-bit-sizes) Raw identifiers: - [Other raw identifier syntaxes](/proposals/p3797.md#other-raw-identifier-syntaxes) - [Restrict raw identifier syntax to current and future keywords](/proposals/p3797.md#restrict-raw-identifier-syntax-to-current-and-future-keywords) - [Don't require syntax for references to raw identifiers](/proposals/p3797.md#dont-require-syntax-for-references-to-raw-identifiers) - [Don't provide raw identifier syntax](/proposals/p3797.md#dont-provide-raw-identifier-syntax) ## References - Proposal [#142: Unicode source files](https://github.com/carbon-language/carbon-lang/pull/142) - Proposal [#2015: Numeric type literal syntax](https://github.com/carbon-language/carbon-lang/pull/2015) - Proposal [#3797: Raw identifier syntax](https://github.com/carbon-language/carbon-lang/pull/3797) ================================================ FILE: docs/design/metaprogramming.md ================================================ # Metaprogramming ## Table of contents - [TODO](#todo) - [Overview](#overview) ## TODO This is a skeletal design, added to support [the overview](README.md). It should not be treated as accepted by the core team; rather, it is a placeholder until we have more time to examine this detail. Please feel welcome to rewrite and update as appropriate. See [proposal PR 89](https://github.com/carbon-language/carbon-lang/pull/89) for context -- that proposal may replace this. ## Overview Carbon provides metaprogramming facilities that look similar to regular Carbon code. These are structured, and do not offer inclusion or arbitrary preprocessing of source text such as C and C++ do. ================================================ FILE: docs/design/name_lookup.md ================================================ # Name lookup ## Table of contents - [TODO](#todo) - [Overview](#overview) - [Unqualified name lookup](#unqualified-name-lookup) - [Alternatives](#alternatives) - [Name lookup for common, standard types](#name-lookup-for-common-standard-types) - [Open questions](#open-questions) - [Shadowing](#shadowing) ## TODO This is a skeletal design, added to support [the overview](README.md). It should not be treated as accepted by the core team; rather, it is a placeholder until we have more time to examine this detail. Please feel welcome to rewrite and update as appropriate. ## Overview Names are always introduced into some scope which defines where they can be referenced. Many of these scopes are themselves named. Carbon has a special facility for introducing a dedicated named scope just like C++, but we traverse nested names in a uniform way with `.`-separated names: ``` namespace Foo { namespace Bar { alias ??? MyInt = Int; } } fn F(x: Foo.Bar.MyInt); ``` Carbon packages are also namespaces so to get to an imported name from the `Abseil` package you would write `Abseil.Foo`. The "top-level" file scope is that of the Carbon package containing the file, meaning that there is no "global" scope. Dedicated namespaces can be reopened within a package, but there is no way to reopen a package without being a library and file _within_ that package. Note that libraries (unlike packages) do **not** introduce a scope, they share the scope of their package. This is based on the observation that in practice, a fairly coarse scoping tends to work best, with some degree of global registry to establish a unique package name. ### Unqualified name lookup Unqualified name lookup in Carbon will always find a file-local result, other than the implicit "prelude" of importing and aliasing the fundamentals of the standard library. There will be an explicit mention of the name in the file that declares the name in the current or enclosing scope, which must also precede the reference. #### Alternatives This implies that other names within your own package but not declared within the file must be found by way of the package name. It isn't clear if this is the desirable end state. We need to consider alternatives where names from the same library or any library in the same package are made immediately visible within the package scope for unqualified name lookup. ### Name lookup for common, standard types The Carbon standard library is in the `Core` package. A subset of this package, called the "prelude", is implicitly imported in every file, so the package name `Core` is always available. Some keywords and type literals, such as `bool` and `i32`, are aliases for entities in the prelude. Similarly, some of the Carbon language syntax, such as operators and `for` loops, is defined in terms of interfaces in the prelude. ## Open questions ### Shadowing We can probably disallow the use of shadowed unqualified names, but the actual design for such needs to be thought through. ================================================ FILE: docs/design/naming_conventions.md ================================================ # Naming conventions ## Table of contents - [Overview](#overview) - [Details](#details) - [Constants](#constants) - [Carbon-provided item naming](#carbon-provided-item-naming) - [Alternatives considered](#alternatives-considered) - [References](#references) ## Overview Our naming conventions are: - For idiomatic Carbon code: - `UpperCamelCase` will be used when the named entity cannot have a dynamically varying value. For example, functions, namespaces, or compile-time constant values. - `lower_snake_case` will be used when the named entity's value won't be known until runtime, such as for variables. - For Carbon-provided features: - Keywords and type literals will use `lower_snake_case`. - Other code will use the guidelines for idiomatic Carbon code. In other words: | Item | Convention | Explanation | | ------------------------- | ------------------ | ------------------------------------------------------------------------------------------ | | Packages | `UpperCamelCase` | Used for compile-time lookup. | | Types | `UpperCamelCase` | Resolved at compile-time. | | Functions | `UpperCamelCase` | Resolved at compile-time. | | Methods | `UpperCamelCase` | Methods, including virtual methods, are equivalent to functions. | | Compile-time parameters | `UpperCamelCase` | May vary based on inputs, but are ultimately resolved at compile-time. | | Compile-time constants | `UpperCamelCase` | Resolved at compile-time. See [constants](#constants) for more remarks. | | Variables | `lower_snake_case` | May be reassigned and thus require runtime information. | | Member variables | `lower_snake_case` | Behave like variables. | | Keywords | `lower_snake_case` | Special, and developers can be expected to be comfortable with this casing cross-language. | | Type literals | `lower_snake_case` | Equivalent to keywords. | | Boolean type and literals | `lower_snake_case` | Equivalent to keywords. | | Other Carbon types | `UpperCamelCase` | Behave like normal types. | | `Self` and `Base` | `UpperCamelCase` | These are similar to type members on a class. | We only use `UpperCamelCase` and `lower_snake_case` in naming conventions in order to minimize the variation in rules. ## Details ### Constants Consider the following code: ```carbon package Example; let CompileTimeConstant: i32 = 7; fn RuntimeFunction(runtime_constant: i32); ``` In this example, `CompileTimeConstant` has a singular value (`7`) which is known at compile-time. As such, it uses `UpperCamelCase`. On the other hand, `runtime_constant` may be constant within the function body, but it is assigned at runtime when `RuntimeFunction` is called. Its value is only known in a given runtime invocation of `RuntimeFunction`. As such, it uses `lower_snake_case`. ### Carbon-provided item naming Carbon-provided items are split into a few categories: - Keywords; for example, `for`, `fn`, and `var`. - Type literals; for example, `i`, `u`, and `f`. - Boolean type and literals; for example, `bool`, `true`, and `false`. - The separate categorization of booleans should not be taken as a rule that only booleans would use lowercase; it's just the only example right now. - `Self` and `Base`. - Other Carbon types; for example, `Int`, `UInt`, and `String`. Note that while other Carbon types currently use `UpperCamelCase`, that should not be inferred to mean that future Carbon types will do the same. The leads will make decisions on future naming. ## Alternatives considered - [Other naming conventions](/proposals/p0861.md#other-naming-conventions) - [Other conventions for naming Carbon types](/proposals/p0861.md#other-conventions-for-naming-carbon-types) ## References - Proposal [#861: Naming conventions](https://github.com/carbon-language/carbon-lang/pull/861) ================================================ FILE: docs/design/pattern_matching.md ================================================ # Pattern matching ## Table of contents - [Overview](#overview) - [Pattern Syntax and Semantics](#pattern-syntax-and-semantics) - [Expression patterns](#expression-patterns) - [Alternatives considered](#alternatives-considered) - [Binding patterns](#binding-patterns) - [Name binding patterns](#name-binding-patterns) - [Anonymous bindings](#anonymous-bindings) - [Alternatives considered](#alternatives-considered-1) - [`auto` and type deduction](#auto-and-type-deduction) - [Alternatives considered](#alternatives-considered-2) - [`var`](#var) - [Alternatives considered](#alternatives-considered-3) - [`unused`](#unused) - [Tuple patterns](#tuple-patterns) - [Struct patterns](#struct-patterns) - [Alternatives considered](#alternatives-considered-4) - [Alternative patterns](#alternative-patterns) - [Templates](#templates) - [Refutability, overlap, usefulness, and exhaustiveness](#refutability-overlap-usefulness-and-exhaustiveness) - [Alternatives considered](#alternatives-considered-5) - [Pattern usage](#pattern-usage) - [Pattern match control flow](#pattern-match-control-flow) - [Alternatives considered](#alternatives-considered-6) - [Guards](#guards) - [Pattern matching in local variables](#pattern-matching-in-local-variables) - [Evaluation order](#evaluation-order) - [Alternatives considered](#alternatives-considered-7) - [Open questions](#open-questions) - [Slice or array nested value pattern matching](#slice-or-array-nested-value-pattern-matching) - [Pattern matching as function overload resolution](#pattern-matching-as-function-overload-resolution) - [Alternatives considered](#alternatives-considered-8) - [References](#references) ## Overview A _pattern_ is an expression-like syntax that describes the structure of some value. The pattern may contain unknowns, so it can potentially match multiple values, and those unknowns may have names, in which case they are called _binding patterns_. When a pattern is executed by giving it a value called the _scrutinee_, it determines whether the scrutinee matches the pattern, and if so, determines the values of the bindings. A _full pattern_ is a complete input to a pattern matching operation, that is a pattern that is not a subpattern of another pattern. If it's preceded by a deduced parameter list or followed by a return type expression, those are part of the full pattern as well. ## Pattern Syntax and Semantics All expressions are patterns, but they may be either tuple patterns, struct patterns, or expression patterns, as described below. A pattern that is not an expression, because it contains pattern-specific syntax such as a binding pattern, is a _proper pattern_. Many expression forms, such as arbitrary function calls, are not permitted as proper patterns, so cannot contain binding patterns. ```carbon fn F(n: i32) -> i32 { return n; } match (F(42)) { // ❌ Error: binding can't appear in a function call. case (F(n: i32)) => {} } ``` ### Expression patterns An expression is a pattern. - _expression-pattern_ ::= _expression_ - _pattern_ ::= _expression-pattern_ The scrutinee is compared with the expression using the `==` operator: _expression_ `==` _scrutinee_. ```carbon fn F(n: i32) { match (n) { // ✅ Results in an `n == 5` comparison. // OK despite `n` and `5` having different types. case 5 => {} } } ``` As depicted here, _expression-pattern_ is ambiguous with _tuple-pattern_, _struct-pattern_, and _alternative-pattern_. In the case of _tuple-pattern_ and _struct-pattern_, the ambiguity is resolved in their favor, meaning that a tuple or struct literal in a pattern context is not interpreted as an expression pattern, but as a tuple or struct pattern whose elements are expression patterns. For example: ```carbon match (0, 1, 2) { case (F(), 0, G()) => ... } ``` Here `(F(), 0, G())` is not an expression, but three separate expressions in a tuple pattern. As a result, this code will call `F()` but not `G()`, because the mismatch between the middle tuple elements will cause pattern matching to fail before reaching `G()`. Other than this short-circuiting behavior, a tuple pattern of expression patterns behaves the same as if it were a single expression pattern. The resolution of the _alternative-pattern_ ambiguity is not specified, because _alternative-pattern_ is specified to behave the same way an expression pattern would, in the cases where they overlap. #### Alternatives considered - [Introducer syntax for expression patterns](/proposals/p2188.md#introducer-syntax-for-expression-patterns) ### Binding patterns #### Name binding patterns A name binding pattern is a pattern. - _binding-pattern_ ::= `ref`? (_identifier_ | `self`) `:` _expression_ - _binding-pattern_ ::= `template`? _identifier_ `:!` _expression_ - _pattern_ ::= _binding-pattern_ A name binding pattern declares a _binding_ with a name specified by the _identifier_, which can be used as an expression. If the binding pattern is prefixed with `ref` or enclosed by a `var` pattern, it is a _reference binding pattern_, and otherwise it is a _value binding pattern_. A binding pattern enclosed by a `var` pattern cannot have a `ref` prefix, because it would be redundant. A _variable binding pattern_ is a special kind of reference binding pattern, which is the immediate subpattern of its enclosing `var` pattern. > **TODO:** Specify the conditions under which a binding can be moved. This is > expected to be the only difference between variable binding patterns and other > reference binding patterns. If the pattern syntax uses `:` it is a _runtime binding pattern_. If it uses `:!`, it is a _compile-time binding pattern_, and it cannot appear inside a `var` pattern. A compile-time binding pattern is either a _symbolic binding pattern_ or a _template binding pattern_, depending on whether it is prefixed with `template`. The binding declared by a binding pattern has a [primitive form](values.md#expression-forms) with the following components: - The type is _expression_. - The category is "value" if the pattern is a value binding pattern, "durable entire reference" if it's a variable binding pattern, or "durable non-entire reference" if it's a non-variable reference binding pattern. - The phase is "runtime", "symbolic", or "template" depending on whether the pattern is a runtime, symbolic, or template binding pattern. During pattern matching, the scrutinee is implicitly converted as needed to have the same form, and the binding is _bound_ to (and consumes) the result of these conversions. This makes a runtime or template binding a kind of reusable alias for the converted scrutinee expression, with the same form and value. Symbolic bindings are more complex: the binding will have the same type, category, and phase as the converted scrutinee expression, but its constant value is an opaque symbol introduced by the binding, which the type system knows to be equal to the converted scrutinee expression. Note that there is no way to implicitly convert to a durable reference expression from any other category, so the scrutinee of a reference binding pattern must already be a durable reference. `var` pattern matching ensures that this is the case for the bindings nested inside it, but for `ref` binding patterns the user-provided scrutinee must meet this requirement itself. ```carbon fn F() -> i32 { match (5) { // ✅ `5` is implicitly converted to `i32`. case n: i32 => { // The binding `n` has the value `5 as i32`, // which is the value returned. return n; } } } ``` When `self` is used instead of an identifier, the pattern must appear in the implicit parameter list of a method (as discussed [here](classes.md#methods)). During pattern matching in a method call, the parameter pattern containing `self` is matched with the object that the method was invoked on. In all other respects, the `self` pattern behaves just like an ordinary binding pattern, introducing a binding named `self` into scope, just as if `self` were an identifier rather than a keyword. #### Anonymous bindings A syntax like a binding but with `_` in place of an identifier is an anonymous binding. It does not participate in name lookup (so there can be multiple such patterns in the same scope), and in all other respects it behaves as if it were wrapped in an [`unused` pattern](#unused). - _binding-pattern_ ::= `_` `:` _expression_ - _binding-pattern_ ::= `template`? `_` `:!` _expression_ ```carbon fn F(n: i32) { match (n) { // ✅ Matches and discards the value of `n`. case _: i32 => {} // ❌ Error: unreachable. default => {} } } ``` As specified in [#1084](/proposals/p1084.md), function redeclarations may replace binding names with `_`s but may not use different names. ```carbon fn G(n: i32); fn H(n: i32); fn J(n: i32); // ✅ Does not use `n`. fn G(_: i32) {} // ❌ Error: name of parameter does not match declaration. fn H(m: i32) {} ``` ##### Alternatives considered - [Commented names](/proposals/p2022.md#commented-names) - [Only short form support with `_`](/proposals/p2022.md#only-short-form-support-with-_) - [Named identifiers prefixed with `_`](/proposals/p2022.md#named-identifiers-prefixed-with-_) - [Anonymous, named identifiers](/proposals/p2022.md#anonymous-named-identifiers) - [Attributes](/proposals/p2022.md#attributes) #### `auto` and type deduction The `auto` keyword is a placeholder for a unique deduced type. - _expression_ ::= `auto` ```carbon fn F(n: i32) { var v: auto = SomeComplicatedExpression(n); // Equivalent to: var w: T = SomeComplicatedExpression(n); // ... where `T` is the type of the initializer. } ``` The `auto` keyword is only permitted in specific contexts. Currently these are: - As the return type of a function. - As the type of a binding. It is anticipated that `auto` may be permitted in more contexts in the future, for example as a placeholder argument in a parameterized type that appears in a context where `auto` is allowed, such as `Vector(auto)` or `auto*`. When the type of a binding requires type deduction, the type is deduced against the type of the scrutinee and deduced values are substituted back into the type before pattern matching is performed. ```carbon fn G[T:! Type](p: T*); class X { impl as ImplicitAs(i32*); } // ✅ Deduces `T = i32` then implicitly and // trivially converts `p` to `i32*`. fn H1(p: i32*) { G(p); } // ❌ Error, can't deduce `T*` from `X`. fn H2(p: X) { G(p); } ``` The above is only an illustration; the behavior of type deduction is not yet specified. #### Alternatives considered - [Shorthand for `auto`](/proposals/p2188.md#shorthand-for-auto) ### `var` A `var` prefix indicates that a pattern provides mutable storage for the scrutinee. - _pattern_ ::= `var` _pattern_ The scrutinee is expected to have the same type as the resolved type of the nested _pattern_, and it is expected to be a runtime-phase ephemeral entire reference expression, which therefore refers to a newly-allocated temporary object. The scrutinee expression is converted as needed to satisfy those expectations, and the `var` pattern takes ownership of the referenced object, promotes it to a _durable_ entire reference expression, and matches the nested _pattern_ with it. The lifetime of the allocated object extends to the end of scope of the `var` pattern (that is the scope that any bindings declared within it would have). ```carbon fn F(p: i32*); fn G() { match ((1, 2)) { // `n` is a mutable `i32`. case (var n: i32, 1) => { F(&n); } // `n` and `m` are the elements of a mutable `(i32, i32)`. case var (n: i32, m: i32) => { F(if n then &n else &m); } } } ``` A `var` pattern cannot be nested within another `var` pattern. The declaration syntax `var` _pattern_ `=` _expresson_ `;` is equivalent to `let` `var` _pattern_ `=` _expression_ `;`. #### Alternatives considered - [Treat all bindings under `var` as variable bindings](/proposals/p5164.md#treat-all-bindings-under-var-as-variable-bindings) - [Make `var` a binding pattern modifier](/proposals/p5164.md#make-var-a-binding-pattern-modifier) - [Initialize storage once pattern matching succeeds](/proposals/p5164.md#initialize-storage-once-pattern-matching-succeeds) ### `unused` When a name introduced by a binding is not used, a warning is issued. It is possible to avoid the warning while keeping a name, by using an `unused` marker. An `unused` marker indicates that all names in a pattern are visible for name lookup but uses are invalid. This includes situations when they cause ambiguous name lookup errors. If attempted to be used, a compiler error will be shown to the user, instructing them to either remove the `unused` qualifier or remove the use. - _proper-pattern_ ::= `unused` _proper-pattern_ An `unused` marker can be applied to any pattern and it will apply to all name bindings in a pattern. Nesting `unused` markers is an error. When an `unused` marker applies only to anonymous bindings `_` and is thus redundant, a warning is produced. `var` and `unused` may appear in any order in a pattern. As specified in [#3763](/proposals/p3763.md), `unused` markers may only appear on definitions, not on non-defining declarations. Function redeclarations that are also definitions may have difference due to `unused` markers, but they may not have different names. ```carbon fn J(n: i32); // ✅ Does not use `n`. fn J(unused n: i32) { ... }; fn G() { match ((1, 2)) { // `x` is unused case (var n: i32, unused x: i32) => { F(&n); } // `n` and `m` are both unused case unused (n: i32, m: i32) => { J(42); } } } ``` ### Tuple patterns A tuple of patterns can be used as a pattern. - _tuple-pattern_ ::= `(` [_pattern_ `,` [_pattern_ [`,` _pattern_]\* `,`? ] ] `)` - _pattern_ ::= _tuple-pattern_ The scrutinee is required to be of tuple type, with the same arity as the number of nested _patterns_. It is converted to a tuple form by [form decomposition](values.md#form-conversions), and then each nested _pattern_ is matched against the corresponding element of the converted scrutinee's [result](values.md#expression-forms). The tuple pattern matches if all of these sub-matches succeed. ### Struct patterns A struct can be matched with a struct pattern. - _struct-pattern_ ::= `{` [_field-pattern_ [`,` _field-pattern_ ]\* ] `}` - _struct-pattern_ ::= `{` [_field-pattern_ `,`]+ `_` `}` - _field-pattern_ ::= _designator_ `=` _pattern_ - _field-pattern_ ::= _binding-pattern_ - _pattern_ ::= _struct-pattern_ A struct pattern resembles a struct literal, except that the initializers can be patterns. ```carbon match ({.a = 1, .b = 2}) { // Struct literal as a pattern. case {.b = 2, .a = 1} => {} // Proper struct pattern. case {.b = n: i32, .a = m: i32} => {} } ``` The scrutinee is required to be of struct type, and every field name in the pattern must be a field name in the scrutinee. It is converted to a struct form by [form decomposition](values.md#form-conversions) and then each _field-pattern_ is matched with the same-named element of the converted scrutinee's [result](values.md#expression-forms). If the scrutinee result has any field names not present in the pattern, those sub-results are [discarded](values.md#form-conversions) in lexical order if the pattern has a trailing `_` (as in `{.a = 1, _}`), or diagnosed as an error if it does not. The struct pattern matches if all of these sub-matches succeed. In the case where a field will be bound to an identifier with the same name, a shorthand syntax is available: `a: T` is synonymous with `.a = a: T`. ```carbon match ({.a = 1, .b = 2}) { case {a: i32, b: i32} => { return a + b; } } ``` Likewise, `ref a: T` is synonymous with `.a = ref a: T`, and `var a: T` is synonymous with `.a = var a: T`. If some fields should be ignored when matching, a trailing `, _` can be added to specify this: ```carbon match ({.a = 1, .b = 2}) { case {.a = 1, _} => { return 1; } case {b: i32, _} => { return b; } } ``` This is valid even if all fields are actually named in the pattern. #### Alternatives considered - [Struct pattern syntax](/proposals/p2188.md#struct-pattern-syntax) ### Alternative patterns An alternative pattern is used to match one alternative of a choice type. - _alternative-pattern_ ::= _callee-expression_ _tuple-pattern_? - _alternative-pattern_ ::= _designator_ _tuple-pattern_? \_ _pattern_ ::= _alternative-pattern_ Here, _callee-expression_ is syntactically an expression that is valid as the callee in a function call expression, and an alternative pattern is syntactically a function call expression whose argument list may contain proper patterns. Semantically, if the argument list contains no proper patterns, it behaves like an expression pattern. Otherwise, if a _callee-expression_ is provided, it is required to name a choice type alternative that has a parameter list, and the scrutinee is implicitly converted to that choice type. Otherwise, the scrutinee is required to be of some choice type, and the designator is looked up in that type and is required to name an alternative with a parameter list if and only if a _tuple-pattern_ is specified. The pattern matches if the active alternative in the scrutinee is the specified alternative, and the arguments of the alternative match the given tuple pattern (if any). ```carbon choice Optional(T:! Type) { None, Some(T) } match (Optional(i32).None) { // ✅ `.None` resolved to `Optional(i32).None`. case .None => {} // ✅ `.Some` resolved to `Optional(i32).Some`. case .Some(n: i32) => { Print("{0}", n); } // ❌ Error, no such alternative exists. case .Other => {} } class X { impl as ImplicitAs(Optional(i32)); } match ({} as X) { // ✅ OK, but expression pattern. case Optional(i32).None => {} // ✅ OK, implicitly converts to `Optional(i32)`. case Optional(i32).Some(n: i32) => { Print("{0}", n); } } ``` Note that a pattern of the form `Optional(T).None` is an expression pattern and is compared using `==`. ### Templates Any checking of the type of the scrutinee against the type of the pattern that cannot be performed because the type of the scrutinee involves a template parameter is deferred until the template parameter's value is known. During instantiation, patterns that are not meaningful due to a type error are instead treated as not matching. This includes cases where an `==` fails because of a missing `EqWith` implementation. ```carbon fn TypeName[template T:! Type](x: T) -> String { match (x) { // ✅ OK, the type of `x` is a template parameter. case _: i32 => { return "int"; } case _: bool => { return "bool"; } case _: auto* => { return "pointer"; } default => { return "unknown"; } } } ``` Cases where the match is invalid for reasons not involving the template parameter are rejected when type-checking the template: ```carbon fn MeaninglessMatch[template T:! Type](x: T*) { match (*x) { // ✅ OK, `T` could be a tuple. case (_: auto, _: auto) => {} default => {} } match (x->y) { // ✅ OK, `T.y` could be a tuple. case (_: auto, _: auto) => {} default => {} } match (x) { // ❌ Error, tuple pattern cannot match value of non-tuple type `T*`. case (_: auto, _: auto) => {} default => {} } } ``` ### Refutability, overlap, usefulness, and exhaustiveness Some definitions: - A pattern _P_ is _useful_ in the context of a set of patterns _C_ if there exists a value that _P_ can match that no pattern in _C_ matches. - A set of patterns _C_ is _exhaustive_ if it matches all possible values. Equivalently, _C_ is exhaustive if the pattern `_: auto` is not useful in the context of _C_. - A pattern _P_ is _refutable_ if there are values that it does not match, that is, if the pattern `_: auto` is useful in the context of {_P_}. Equivalently, the pattern _P_ is _refutable_ if the set of patterns {_P_} is not exhaustive. - A set of patterns _C_ is _overlapping_ if there exists any value that is matched by more than one pattern in _C_. For the purpose of these terms, expression patterns that match a constant tuple, struct, or choice value are treated as if they were tuple, struct, or alternative patterns, respectively, and `bool` is treated like a choice type. Any expression patterns that remain after applying this rule are considered to match a single value from an infinite set of values so that a set of expression patterns is never exhaustive: ```carbon fn IsEven(n: u8) -> bool { // Not considered exhaustive. match (n) { case 0 => { return true; } case 1 => { return false; } ... case 255 => { return false; } } // Code here is considered to be reachable. } ``` ```carbon fn IsTrue(b: bool) -> bool { // Considered exhaustive. match (b) { case false => { return false; } case true => { return true; } } // Code here is considered to be unreachable. } ``` When determining whether a pattern is useful, no attempt is made to determine the value of any guards, and instead a worst-case assumption is made: a guard on that pattern is assumed to evaluate to true and a guard on any pattern in the context set is assumed to evaluate to false. We will diagnose the following situations: - A pattern is not useful in the context of prior patterns. In a `match` statement, this happens if a pattern or `default` cannot match because all cases it could cover are handled by prior cases or a prior `default`. For example: ```carbon choice Optional(T:! Type) { None, Some(T) } fn F(a: Optional(i32), b: Optional(i32)) { match ((a, b)) { case (.Some(a: i32), _: auto) => {} // ✅ OK, but only matches values of the form `(None, Some)`, // because `(Some, Some)` is matched by the previous pattern. case (_: auto, .Some(b: i32)) => {} // ✅ OK, matches all remaining values. case (.None, .None) => {} // ❌ Error, this pattern never matches. case (_: auto, _: auto) => {} } } ``` - A pattern match is not exhaustive and the program doesn't explicitly say what to do when no pattern matches. For example: - If the patterns in a `match` are not exhaustive and no `default` is provided. ```carbon fn F(n: i32) -> i32 { // ❌ Error, this `match` is not exhaustive. match (n) { case 0 => { return 2; } case 1 => { return 3; } case 2 => { return 5; } case 3 => { return 7; } case 4 => { return 11; } } } ``` - If a refutable pattern appears in a context where only one pattern can be specified, such as a `let` or `var` declaration, and there is no fallback behavior. This currently includes all pattern matching contexts other than `match` statements, but the `var`/`let`-`else` feature in [#1871](https://github.com/carbon-language/carbon-lang/pull/1871) would introduce a second context permitting refutable matches, and overloaded functions might introduce a third context. ```carbon fn F(n: i32) { // ❌ Error, refutable expression pattern `5` used in context // requiring an irrefutable pattern. var 5 = n; } // ❌ Error, refutable expression pattern `5` used in context // requiring an irrefutable pattern. fn G(n: i32, 5); ``` - When a set of patterns have no ordering or tie-breaker, it is an error for them to overlap unless there is a unique best match for any value that matches more than one pattern. However, this situation does not apply to any current language rule: - For `match` statements, patterns are matched top-down, so overlap is permitted. - We do not yet have an approved design for overloaded functions, but it is anticipated that declaration order will be used in that case too. - For a set of `impl`s that match a given `impl` lookup, argument deduction is used rather than pattern matching, but `impl`s with the same type structure are an error unless a `match_first` declaration is used to order the `impl`s. #### Alternatives considered - [Treat expression patterns as exhaustive if they cover all possible values](/proposals/p2188.md#treat-expression-patterns-as-exhaustive-if-they-cover-all-possible-values) - [Allow non-exhaustive `match` statements](/proposals/p2188.md#allow-non-exhaustive-match-statements) ## Pattern usage ### Pattern match control flow `match` is a skeletal design, added to support [the overview](README.md). Aside from [guards](#guards), it should not be treated as accepted by the core team; rather, it is a placeholder until we have more time to examine this detail. Please feel welcome to rewrite and update as appropriate. The most powerful form and easiest to explain form of pattern matching is a dedicated control flow construct that subsumes the `switch` of C and C++ into something much more powerful, `match`. This is not a novel construct, and is widely used in existing languages (Swift and Rust among others) and is currently under active investigation for C++. Carbon's `match` can be used as follows: ```carbon fn Bar() -> (i32, (f32, f32)); fn Foo() -> f32 { match (Bar()) { case (42, (x: f32, y: f32)) => { return x - y; } case (p: i32, (x: f32, _: f32)) if (p < 13) => { return p * x; } case (p: i32, _: auto) if (p > 3) => { return p * Pi; } default => { return Pi; } } } ``` There is a lot going on here. First, let's break down the core structure of a `match` statement. It accepts a value that will be inspected, here the result of the call to `Bar()`. It then will find the _first_ `case` that matches this value, and execute that block. If none match, then it executes the default block. Each `case` contains a pattern. The first part is a value pattern (`(p: i32, _: auto)` for example) optionally followed by an `if` and boolean predicate. The value pattern has to match, and then the predicate has to evaluate to `true` for the overall pattern to match. Value patterns can be composed of the following: - An expression (`42` for example), whose value must be equal to match. - An identifier to bind the value to, followed by a colon (`:`) and a type (`i32` for example). An underscore (`_`) may be used instead of the identifier to discard the value once matched. - A tuple destructuring pattern containing a tuple of value patterns (`(x: f32, y: f32)`) which match against tuples and tuple-like values by recursively matching on their elements. - An unwrapping pattern containing a nested value pattern which matches against a variant or variant-like value by unwrapping it. In order to match a value, whatever is specified in the pattern must match. Using `auto` for a type will always match, making `_: auto` the wildcard pattern. If the scrutinee expression's [form](values.md#expression-forms) contains any primitive forms with category "initializing", they are converted to ephemeral non-entire reference expressions by [materialization](values.md#temporary-materialization) before pattern matching begins, so that the result can be reused by multiple `case`s. However, the objects created by `var` patterns are not reused by multiple `case`s: ```carbon class X { destructor { Print("Destroyed!"); } } fn F(x: X) { match ((x, 1 as i32)) { // Prints "Destroyed!" here, because `y` is initialized before we reach the // expression pattern `0` and determine that this case doesn't match, // so it must be destroyed. case (var y: X, 0) => {} case (var z: X, 1) => { // Prints "Destroyed!" again at the end of the block here, when `z` goes // out of scope. } } } ``` #### Alternatives considered - [Allow variable binding patterns to alias across `case`s](/proposals/p5164.md#allow-variable-binding-patterns-to-alias-across-cases) #### Guards We allow `case`s within a `match` statement to have _guards_. These are not part of pattern syntax, but instead are specific to `case` syntax: - _case_ ::= `case` _pattern_ [`if` _expression_]? `=>` _block_ A guard indicates that a `case` only matches if some predicate holds. The bindings in the pattern are in scope in the guard: ```carbon match (x) { case (m: i32, n: i32) if m + n < 5 => { return m - n; } } ``` For consistency, this facility is also available for `default` clauses, so that `default` remains equivalent to `case _: auto`. ### Pattern matching in local variables Value patterns may be used when declaring local variables to conveniently destructure them and do other type manipulations. However, the patterns must match at compile time, so they can't use an `if` clause. ```carbon fn Bar() -> (i32, (f32, f32)); fn Foo() -> i32 { var (p: i32, _: auto) = Bar(); return p; } ``` This extracts the first value from the result of calling `Bar()` and binds it to a local variable named `p` which is then returned. ## Evaluation order A pattern matching operation's potentially-observable side effects are a series of calls to functions that might be user-defined. This includes function calls and operators in the scrutinee and in expression patterns, and also type conversions and category conversions. Note that category conversions on tuple and struct types, and type conversions between tuple and struct types, are not modeled as function calls, but are broken down into function calls on their elements. Note also that for function and operator calls in expressions, we are only considering top-level calls, that is calls that aren't inputs to other calls within the expression, because the entire sub-expression of a top-level call acts as a single unit for purposes of evaluation ordering. For example, suppose `A` is implicitly convertible to a `C` and `B` is implicitly convertible to `D`, but both conversions are value expressions (rather than initializing expressions), and consider the following code: ``` fn MakeA() -> A; fn MakeB() -> B; var cd: (C, D) = (MakeA(), MakeB()); ``` Evaluation of the last line involves 6 function calls: 1. Call `MakeA`. 2. Call `A.(Core.ImplicitAsPrimitive(C)).Convert`, to convert the `A` object to a `C` value, as part of type conversion. 3. Call `A.(Core.Copy).Op` to copy the `C` value into the storage for `cd.0`, as part of category conversion. 4. Call `MakeB`. 5. Call `B.(Core.ImplicitAsPrimitive(D)).Convert`. 6. Call `B.(Core.Copy).Op`. > **Note:** These `Core` interfaces haven't been specified yet, and their > details may change. To define the evaluation order of these calls, we have to consider the dependencies between them, which we'll model as a DAG, with function calls as nodes, and edges representing data dependencies. It will also be useful to include leaf patterns (that is, patterns that have no subpatterns) as nodes in the graph; they don't have side effects as such, so they aren't part of the evaluation order, but they do constrain the evaluation order. ```mermaid %%{init: {'themeVariables': {'fontFamily': 'monospace'}}}%% flowchart BT 2["A.(Core.ImplicitAsPrimitive(C))"]-->1[/"MakeA"\] 3["A.(Core.Copy)"]-->2 5["B.(Core.ImplicitAsPrimitive(D))"]-->4[/"MakeB"\] 6["B.(Core.Copy)"]-->5 7[\"cd: (C, D)"/]-->3 7-->6 ``` This DAG will always have a few key properties: - The sources are the primitive patterns. Only the sources can have multiple out-edges. - The sinks are function calls in the scrutinee expression, and in expression patterns. Only scrutinee expression sinks can have multiple in-edges. - The interior nodes always have one edge in and one edge out, forming a set of paths that connect a source to a sink. - The paths to calls in expression patterns are trivial: they consist of a single edge from an expression pattern source to a function call sink. Furthermore, a given source or sink has at most one such edge. - Each path to the scrutinee connects a type in the pattern to a type in the scrutinee, and together the paths uniquely cover the entire pattern and scrutinee types. Furthermore, they are minimal, in the sense that unless the path is a single edge, its source and sink types won't both be tuple or struct types. > **Future work:** this design needs to be reconciled with the design for > [user-defined sum types](sum_types.md#user-defined-sum-types), because > `Match.Op` can violate this topology. This should probably be folded into a > broader redesign of sum type customization, which we expect to be necessary > for other reasons. The order of evaluation is determined by a depth-first postorder traversal of the this DAG: while visiting a node, we recursively visit all its children, and a call occurs when we finish visiting the corresponding node (revisiting a node is a no-op). By eagerly consuming the result of each function call as soon as possible, this minimizes the number of simultaneously-live temporaries, which enables more efficient code generation. When visiting a pattern, we visit its out-paths in the scrutinee type's left-to-right source code order (recall that each path is associated with a unique part of the scrutinee type). An edge to an expression pattern call, if any, is visited last. The patterns themselves are visited in their own left-to-right source code order. So, returning to our earlier example, the 6 function calls will be evaluated in the order we listed them. In some cases, visiting the patterns in their own order may lead to visiting the types within a scrutinee call out of order, but if it would lead to visiting the scrutinee calls themselves out of order, the program is ill-formed. For example: ```carbon // ❌ Error: visiting `.c: C` first leads to evaluating `MakeA()` before // `MakeB()` var {c: C, d: D} = {.d = MakeB(), .c = MakeA()}; // ✅ OK: only one pattern, and we use scrutinee order to visit its children. var cd: {.c: C, .d: D} = {.d = MakeB(), .c = MakeA()}; // ✅ OK: only one scrutinee call, so it can't be out of order. fn MakeAB() -> {.d: B, .c: A}; var {c: C, d: D} = MakeAB(); ``` As a result, the overall evaluation order is always consistent with the written order of the patterns, and with the written order of the scrutinee expressions. Within those constraints, the order of the scrutinee types acts as a tie-breaker. Note in particular that this means the fields of a struct-type binding are not necessarily initialized in declaration order. Note that generally speaking, pattern-match evaluation stops as soon as it's known that the match will fail, in which case only a prefix of the full evaluation order will be evaluated. ### Alternatives considered - [Breadth-first evaluation order](/proposals/p5545.md#breadth-first-evaluation-order) - [Depth-first evaluation with a different "horizontal" order](/proposals/p5545.md#depth-first-evaluation-with-a-different-horizontal-order) ## Open questions ### Slice or array nested value pattern matching An open question is how to effectively fit a "slice" or "array" pattern into nested value pattern matching, or whether we shouldn't do so. ### Pattern matching as function overload resolution Need to flesh out specific details of how overload selection leverages the pattern matching machinery, what (if any) restrictions are imposed, etc. ## Alternatives considered - [Type pattern matching](/proposals/p2188.md#type-pattern-matching) - [Allow guards on arbitrary patterns](/proposals/p2188.md#allow-guards-on-arbitrary-patterns) ## References - Proposal [#2022: Unused Pattern Bindings (Unused Function Parameters)](https://github.com/carbon-language/carbon-lang/pull/2022) - Proposal [#2188: Pattern matching syntax and semantics](https://github.com/carbon-language/carbon-lang/pull/2188) ================================================ FILE: docs/design/safety/README.md ================================================ # Safety ## Table of contents - [Overview](#overview) - [Safe and unsafe code](#safe-and-unsafe-code) - [Safety modes](#safety-modes) - [Memory safety model](#memory-safety-model) - [Data races versus unsynchronized temporal safety](#data-races-versus-unsynchronized-temporal-safety) - [Safe library ecosystem](#safe-library-ecosystem) - [Build modes](#build-modes) - [Constraints](#constraints) - [Probabilistic techniques likely cannot stop attacks](#probabilistic-techniques-likely-cannot-stop-attacks) - [References](#references) ## Overview One of Carbon's core goals is [practical safety]. This is referring to _[code safety]_ as opposed to the larger space of [systems safety]. The largest aspect of code safety at the language level is [memory safety], but this also applies to other aspects of code safety such as avoiding undefined behavior in other forms. [practical safety]: /docs/project/goals.md#practical-safety-and-testing-mechanisms [code safety]: /docs/design/safety/terminology.md#code-software-or-program-safety [systems safety]: /docs/design/safety/terminology.md#safety [memory safety]: /docs/design/safety/terminology.md#memory-safety However, Carbon also has a goal of fine grained, smooth interop-with and migration-from existing C++ code, and C++ is a fundamentally unsafe language. It has pervasive sources of undefined behavior and minimal memory safety guarantees. Our safety strategy has to address how C++ code fits into it, and provide an incremental path from where the code is at today towards increasing levels of safety. Ultimately, Carbon will both provide a [memory-safe language], _and_ provide a language that is a target for mechanical migration from C++ and optimizes even further for interop with unsafe C++ with minimal friction. [memory-safe language]: /docs/design/safety/terminology.md#memory-safe-language ## Safe and unsafe code Carbon will have both _safe_ and _unsafe_ code. Safe code provides limits on the potential behavior of the program even in the face of bugs in order to prevent [safety bugs] from becoming [vulnerabilities]. Unsafe code is any code or operation which lacks limits or guarantees on behavior, and as a consequence may have undefined behavior or be a safety bug. [safety bugs]: /docs/design/safety/terminology.md#safety-bugs [vulnerabilities]: /docs/design/safety/terminology.md#vulnerability-or-security-vulnerability All things being equal, safe code constructs are preferable to unsafe constructs, but many unsafe constructs are necessary. Where unsafe constructs are needed, Carbon follows three principles to incorporate them into the language: - The unsafe capabilities provided should be semantically narrow: only the minimal unsafe operation to achieve the desired result. - The unsafe code should be syntactically narrow and separable from surrounding safe code. - There should be a reasonable way of including the keyword `unsafe` in whatever syntax is used so that this keyword can be used as a common annotation in audits and review. The result is that we don't model large regions or sections of Carbon code as "unsafe" or have a parallel "unsafe" variant language. We instead focus on the narrow and specific unsafe operations and constructs. Note that when we're talking about the narrow semantics of an unsafe capability, these are the semantics of the specific unsafe operation. For example, an unsafe type conversion shouldn't also allow unsafe out-of-lifetime access. This is separate from the _soundness_ implications of an unsafe operation, which may not be as easily narrowed. > **Future work**: More fully expand on the soundness principles and model for > safe Carbon code. This is an interesting and important area of the design that > isn't currently fleshed out in detail. ## Safety modes The _safety mode_ of Carbon governs the extent to which unsafe code must include the local `unsafe` keyword in its syntax to delineate it from safe code. _Strict Carbon_ is the mode in which all unsafe code is marked with the `unsafe` keyword. This mode is designed to satisfy the requirements of a [memory safe language]. _Permissive Carbon_ is a mode optimized for interop with C++ and automated migration from C++. In this mode, some unsafe code does not require an `unsafe` keyword: specific aspects of C++ interop or pervasive patterns that occur when migrating from C++. However, not _all_ unsafe code will omit the keyword, the permissive mode is designed to be minimal in the unsafe code allowed. Modes can be configured on an individual file as part of the package declaration, or an a function body as part of the function definition. More options such as regions of declarations or regions of statements can be explored in the future based on demand in practice when working with mixed-strictness code. More fine grained than statements is not expected given that the same core expressivity is available at that finer granularity through explicitly marking `unsafe` operations. > **Future work**: Define the exact syntax for package declaration and function > definition control of strictness. The syntax for enabling the permissive mode > must include the `unsafe` keyword as it is standing in place of more granular > use of the `unsafe` keywords and needs to still be discovered when auditing > for safety. > **Future work**: Define how to mark `import`s of permissive libraries in > various contexts, balancing ergonomic burden against the potential for > surprising amounts of unsafety in dependencies. ## Memory safety model Carbon will use a hybrid of different techniques to achieve memory safety in its safe code, largely broken down by the categories of memory safety: - [Type safety]: compile-time enforcement, the same as other statically typed languages with generic type systems. - [Initialization safety]: hybrid of run-time and compile-time enforcement. - [Spatial safety]: run-time enforcement. - [Temporal safety]: compile-time enforcement through its type system. - [Data-race safety]: compile-time enforcement through its type system. [type safety]: /docs/design/safety/terminology.md#type-safety [initialization safety]: /docs/design/safety/terminology.md#initialization-safety [spatial safety]: /docs/design/safety/terminology.md#spatial-safety [temporal safety]: /docs/design/safety/terminology.md#temporal-safety [data-race safety]: /docs/design/safety/terminology.md#data-race-safety **At this high level, this means Carbon's memory safety model will largely match Rust's.** The only minor deviation at this level from Rust is the use of run-time enforcement for initialization, where Carbon will more heavily leverage run-time techniques such as automatic initialization and dynamic "optional" semantics to improve the ergonomics in Carbon. However, Carbon and Rust will have substantial differences in the _details_ of how they approach both temporal and data-race safety. ### Data races versus unsynchronized temporal safety Carbon has the option of distinguishing between two similar but importantly different classes of bugs: data races and unsynchronized temporal safety violations. Specifically, there is no evidence from security teams that there is any significant volume of vulnerabilities that involve a data race bug but don't also involve a temporal memory safety violation. For example, despite both Go and non-strict-concurrency Swift only providing temporal safety, the rate of memory safety vulnerabilities in software written in both matches the expected low rate for memory-safe languages. As a consequence, Carbon has some flexibility while still being a [memory-safe language] according to our definition: - Carbon might choose to _not_ prevent data race bugs that are not _themselves_ also temporal safety bugs, even though the data race may lead to corruption and cause the program to later violate various other forms of memory safety. So far, evidence has not shown this to be as significant and prevalent source of _vulnerabilities_ as other forms of memory safety bugs. - However, Carbon _must_ detect and prevent cases where a lack of synchronization directly allows temporal safety bugs, such as use after free. Despite having this flexibility, preventing data race bugs remains _highly valuable_ for correctness, debugging, and achieving [fearless concurrency]. If Carbon can, it should work to prevent data races as well. [fearless concurrency]: https://doc.rust-lang.org/book/ch16-00-concurrency.html ## Safe library ecosystem Carbon will need access to memory-safe library ecosystems, both for general-purpose, multi-platform functionality and for platform-specific functionality. Currently, the industry is currently investing a massive amount of effort to build out a sufficient ecosystem of general, multi-platform software using Rust, and it is critical that Carbon does not impede, slow down, or require duplicating that ecosystem. Similarly, if any other cross-platform library ecosystems emerge in any viable memory-safe languages given our performance constraints, we should work to reuse them and avoid duplication. **Carbon's strategy for safe and generally reusable cross-platform libraries is to leverage Rust libraries through interop.** This is a major motivating reason for seamless and safe Rust interop. The Carbon project will work to avoid creating duplication between the growing Rust library ecosystem and any future Carbon library ecosystem. Carbon's ecosystem will be focused instead on libraries and functionality that would either be missing or only available in C++. Platform-specific functionality is typically developed in that platform's native language, whether that is Swift for Apple platforms or Kotlin for Android. Again, the goal of Carbon should be to avoid duplicating functionality, and instead to prioritize high quality interop with the existing platform libraries in the relevant languages on those platforms. ## Build modes Where Carbon's safety mode governs the language rules applied to unsafe code, Carbon's build modes will change the _behavior_ of unsafe code. There are two primary build modes: - **Release**: the primary build mode for programs in production, focuses on giving the best possible performance with a practical baseline of safety. - **Debug**: the primary build mode during development, focuses on high-quality developer experience. The release build will include a baseline of hardening necessary to uphold the run-time enforcement components of our [memory safety model](#memory-safety-model) above. This means, for example, that bounds checking is enabled in the release build. There is [evidence] that the cost of these hardening steps is low. Following the specific guidance of our top priority for [performance _control_], Carbon will provide ways to write unsafe code that disables the run-time enforcement, enabling the control of any overhead incurred. [evidence]: https://chandlerc.blog/posts/2024/11/story-time-bounds-checking/ [performance control]: /docs/project/goals.md#performance-critical-software The debug build will change the actual behavior of both safe and unsafe code with detectable bugs or detectable undefined or erroneous behavior to have [fail-stop] behavior and provide detailed diagnostics to enable better debugging. This mode will at least provide similar bug [detection] capabilities to [AddressSanitizer] and [MemorySanitizer]. [fail-stop]: /docs/design/safety/terminology.md#fail-stop [detection]: /docs/design/safety/terminology.md#detecting [AddressSanitizer]: https://clang.llvm.org/docs/AddressSanitizer.html [MemorySanitizer]: https://clang.llvm.org/docs/MemorySanitizer.html Carbon will also have additional build modes to provide specific, narrow capabilities that cannot be reasonably combined into either of the above build modes. Each of these is expected to be an extension of either the debug or release build for that specific purpose. For example: - Debug + [ThreadSanitizer]: detection of [data-races][data-race safety]. - Release + specialized [hardening]: some users can afford significant run-time overhead in order to use additional hardening. Carbon will have several specialized build modes in this space. Hardening techniques in this space include [Control-Flow Integrity (CFI)][cfi] and hardware-accelerated address sanitizing ([HWASAN]). [ThreadSanitizer]: https://clang.llvm.org/docs/ThreadSanitizer.html [hardening]: /docs/design/safety/terminology.md#hardening [cfi]: https://clang.llvm.org/docs/ControlFlowIntegrity.html [hwasan]: https://clang.llvm.org/docs/HardwareAssistedAddressSanitizerDesign.html Carbon will provide a way to disable the default hardening in release builds, but not in a supported way. Its use is expected to be strictly for benchmarking purposes. ## Constraints ### Probabilistic techniques likely cannot stop attacks It's expected that non-cryptographic probabilistic techniques that can be applied at the language level are attackable through a variety of techniques: - The attacker might be able to attack repeatedly until it gets through. - The attacker may be able to determine when the attack would be detected and only run the attack when it would not be. - The attacker might be able control the test condition to make detection much less likely or avoid detection completely. For example, if detection is based on the last 4 bits of a memory address, an attacker may be able to generate memory allocations, viewing the address and only attacking when there's a collision. Hardware vulnerabilities may make these attacks easier than they might otherwise appear. Future hardware vulnerabilities are difficult to predict. In general, we do not expect non-cryptographic probabilistic techniques to be an effective approach to achieving safety. While _cryptographic_ probabilistic techniques can, and typically do, work carefully to not be subject to these weaknesses, they face a very different challenge. The overhead of a cryptographically secure hash is generally prohibitive for use in language level constructs. Further, some of the defenses against hardware vulnerabilities and improvements further exacerbate these overheads. However, when these can be applied usefully such as with [PKeys], they are robust. [PKeys]: https://docs.kernel.org/core-api/protection-keys.html ## References - Proposal [#196: Language-level safety strategy](https://github.com/carbon-language/carbon-lang/pull/196). - Proposal [#5914: Updating Carbon's safety strategy](https://github.com/carbon-language/carbon-lang/pull/5914). ================================================ FILE: docs/design/safety/terminology.md ================================================ # Safety: Terminology ## Table of contents - [Core Terminology](#core-terminology) - [_Hazard_](#hazard) - [_Bug_ or _defect_](#bug-or-defect) - [_Active bug_](#active-bug) - [_Latent bug_](#latent-bug) - [_Safety_](#safety) - [_Code_, _software_, or _program safety_](#code-software-or-program-safety) - [_Safety bugs_](#safety-bugs) - [_Initial bug_](#initial-bug) - [_Fail-stop_](#fail-stop) - [Vulnerability Terminology](#vulnerability-terminology) - [_Vulnerability_ or _security vulnerability_](#vulnerability-or-security-vulnerability) - [_Vulnerability defense_](#vulnerability-defense) - [_Detecting_](#detecting) - [_Mitigating_](#mitigating) - [_Preventing_ vulnerabilities](#preventing-vulnerabilities) - [_Ensuring_ correctness](#ensuring-correctness) - [_Hardening_](#hardening) - [Memory Safety Specifics](#memory-safety-specifics) - [_Memory safety_](#memory-safety) - [_Temporal safety_](#temporal-safety) - [_Spatial safety_](#spatial-safety) - [_Type safety_](#type-safety) - [_Initialization safety_](#initialization-safety) - [_Data-race safety_](#data-race-safety) - [_Memory safety bug_](#memory-safety-bug) - [_Memory-safe platform_ or _environment_](#memory-safe-platform-or-environment) - [_Memory-safe language_](#memory-safe-language) ## Core Terminology ### _Hazard_ Unsafe coding construct that may lead to a bug or vulnerability. For example, indexing an array with a user-supplied and unvalidated index is a hazard. ### _Bug_ or _defect_ Reachable program behavior contrary to the author's intent. #### _Active bug_ Buggy behavior that is actively occurring for users of the program. #### _Latent bug_ Buggy behavior that does not currently occur for users, but is reachable. Behaviors that are reachable, and so _can_ happen, but don't happen today in practice _are always still bugs_! ### _Safety_ Absent a qualifier or narrow context, refers to _[system safety]_, and _[safety engineering]_. Always a property of a system or product as a whole, including human factors, etc. [system safety]: https://en.wikipedia.org/wiki/System_safety [safety engineering]: https://en.wikipedia.org/wiki/Safety_engineering ### _Code_, _software_, or _program safety_ Invariants or limits on program behavior in the face of bugs. - Very narrow and specific meaning. - Often necessary but not sufficient for system safety. This is a specific subset of [safety](#safety) concerns, and the ones we are most often focused on with programming language and library design. ### _Safety bugs_ Bugs where some aspect of program behavior has insufficient (often none) invariants or limits. For example, _undefined behavior_ definitionally has no invariant or limit, and reaching it is always a safety bug. ### _Initial bug_ The first behavior contrary to the author's intent, distinct from subsequent deviations. ### _Fail-stop_ The behavior of immediately terminating the program, minimizing any further business logic. This is in contrast to any form of "correct" program termination, continuing execution, or unwinding. ## Vulnerability Terminology ### _Vulnerability_ or _security vulnerability_ A bug that creates the possibility for a malicious actor to subvert a program's intended behavior in a way that violates a security policy (for example, confidentiality, integrity, availability). Vulnerabilities are often exploitable manifestations of underlying bugs. ### _Vulnerability defense_ The set of strategies and techniques employed to reduce the risks posed by vulnerabilities arising from bugs. These strategies operate at different levels and have varying degrees of effectiveness. #### _Detecting_ While still leaving the code vulnerable, a defense that attempts to recognize and potentially track when a specific bug has occurred dynamically. Requires _some_ invariant or limit, but very minimal. #### _Mitigating_ Making a vulnerability significantly more expensive, difficult, or improbable to be exploited. #### _Preventing_ vulnerabilities Making it impossible for a bug to be exploited as a vulnerability without resolving the underlying bug -- the program still doesn't behave as intended, it just cannot be exploited. Often this is done by defining behavior to [fail-stop](#fail-stop). #### _Ensuring_ correctness Ensures that if the program compiles successfully, it behaves as intended. This typically prevents a bug being written and compiled into a program in the first place. For example, statically typed languages typically ensure that the types used in the program are correct. #### _Hardening_ Combinations of mitigation, prevention, and ensured correctness to reduce the practical risk of vulnerabilities due to bugs. ## Memory Safety Specifics ### _Memory safety_ Having well-defined and predictable behavior regarding memory access, even in the face of bugs. Memory safety encompasses several key aspects: #### _Temporal safety_ Memory accesses occur only within the valid lifetime of the intended memory object. #### _Spatial safety_ Memory accesses remain within the intended bounds of memory regions. #### _Type safety_ Memory is accessed and interpreted according to its intended type, preventing type confusion. #### _Initialization safety_ Memory is properly initialized before being read, avoiding the use of uninitialized data. #### _Data-race safety_ Memory writes are synchronized with reads or writes on other threads. ### _Memory safety bug_ A safety bug that violates memory safety. ### _Memory-safe platform_ or _environment_ A computing platform or execution environment that provides mechanisms to prevent memory safety bugs in programs running on it from becoming vulnerabilities. This is a _systems_ path to achieving memory safety by providing the well-defined and predictable behavior by way of the execution environment. For example, a strongly sandboxed WebAssembly runtime environment can allow a program that is itself unsafe to be executed safely ### _Memory-safe language_ A programming language with sufficient defenses against memory safety bugs for them to not be a significant source of security vulnerabilities. This requires _preventing_ vulnerabilities or _ensuring_ correctness; mitigation is not sufficient to provide an adequate level of memory safety. We identify several key requirements for a language to be memory-safe: - The default mode or subset of the language must provide guaranteed spatial, temporal, type, and initialization memory safety. - Any unsafe subset must only be needed and only be used in rare, exceptional cases. Any use of the unsafe subset must also be well delineated and auditable. - Currently, security evidence doesn't _require_ providing guaranteed data-race safety for [data-race bugs that are not _also_ temporal memory safety bugs](/docs/design/safety#data-races-versus-unsynchronized-temporal-safety). However, the temporal memory safety guarantee must still hold. ================================================ FILE: docs/design/sum_types.md ================================================ # Sum types ## Table of contents - [Overview](#overview) - [`choice` declarations](#choice-declarations) - [User-defined sum types](#user-defined-sum-types) - [Alternatives considered](#alternatives-considered) - [References](#references) ## Overview In Carbon, a _sum type_ is a type whose values are grouped into several distinct named cases, called _alternatives_. A value of a sum type notionally consists of a _discriminator_ tag that identifies which alternative is present, together with that alternative's value if it has one. Sum types are typically handled with pattern matching. ## `choice` declarations The `choice` keyword is used to declare a sum type by specifying its interface, leaving the implementation to the compiler. A `choice` declaration consists of the `choice` keyword followed by the name of the type, and then a list of alternatives inside curly braces. An alternative declaration consists of the alternative name, optionally followed by a parameter list in parentheses. If present, the parameter list has the same syntax as in a [function declaration](README.md#functions). For example: ```carbon choice OptionalI32 { Some(value: i32), None } ``` This declares a sum type named `OptionalI32` with two alternatives: `Some`, which holds a single `i32` value (the parameter name `value` has no effect other than documentation), and `None`, which is empty. Choice types can also be parameterized, [like class types](generics/details.md#parameterized-types): ```carbon choice Optional(T:! type) { Some(value: T), None } ``` A value of a function-like alternative is specified by "calling" it like a function, and a value of an empty alternative like `None` is specified by naming it: ```carbon var my_opt: Optional(i32) = Optional(i32).None; my_opt = Optional(i32).Some(42); ``` The value of a choice type can be inspected using a `match` statement: ```carbon match (my_opt) { case .Some(the_value: i32) => { Print(the_value); } case .None => { Print("None"); } } ``` ## User-defined sum types `choice` declarations are a convenience shorthand for common use cases, but they have limited flexibility. There is no way to control the representation of a `choice` type, or define methods or other members for it (although you can extend it to implement interfaces, using an [out-of-line `impl`](generics/details.md#out-of-line-impl) or [adapter](generics/overview.md#adapting-types)). However, a `class` type can be extended to behave like a sum type. This is much more verbose than a `choice` declaration, but gives the author full control over the representation and class members. The ability to create instances of the sum type can be straightforwardly emulated with factory functions and static constants, and the internal storage layout will presumably involve untagged unions or some other low-level storage primitive which hasn't been designed yet, but the key to defining a sum type's interface is enabling it to support pattern matching. To do that, the sum type has to specify two things: - The set of all possible alternatives, including their names and parameter types, so that the compiler can typecheck the `match` body, identify any unreachable `case`s, and determine whether any `case`s are missing. - The algorithm that, given a value of the sum type, determines which alternative is present, and specifies the values of its parameters. It does so by implementing the `Match` interface, which is defined as follows: ```carbon interface Match { interface BaseContinuation { let ReturnType:! type; } let template Continuation:! type; fn Op[self: Self, C:! Continuation](continuation: C*) -> C.(BaseContinuation.ReturnType); } ``` `Continuation` must itself be an interface that extends `Match.BaseContinuation`, and its definition specifies the set of possible alternatives: each alternative is represented as a method of that interface. When compiling a proper pattern (or set of patterns that includes a proper pattern, as with the cases of a `match`) whose type is a sum type, the compiler generates an implementation of `Continuation` and passes it to `Match.Op`. The sum type's implementation of `Match.Op` is responsible for determining which alternative is present and what its parameters are, and calling the corresponding method of `continuation` with those parameters. The `Match.Op` implementation is required to call exactly one such method exactly once before returning. The compiler populates the `Continuation` method bodies with whatever code should be executed when the corresponding alternatives match. **TODO:** if Carbon has explicit support for tail calls, we should probably require that `Match.Op` invoke the continuation as a tail call. For example, here's how `Optional` can be defined as a class: ```carbon class Optional(T:! type) { // Factory functions fn Some(value: T) -> Self; let None:! Self; private var has_value: bool; private var value: T; impl as Match { interface Continuation { extend Match.BaseContinuation; fn Some[ref self: Self](value: T) -> ReturnType; fn None[ref self: Self]() -> ReturnType; } fn Op[self: Self, C:! Continuation](continuation: C*) -> C.ReturnType { if (self.has_value) { return continuation->Some(self.value); } else { return continuation->None(); } } } // Operations like destruction, copying, assignment, and comparison are // omitted for brevity. } ``` And here's how the compiler-generated implementation of `Optional.(Match.Continuation)` for the `match` statement shown earlier might look, if it were written in Carbon: ```carbon class __MatchStatementImpl { extend impl as Optional.(Match.Continuation) where .ReturnType = () { fn Some(the_value: i32) { Print(the_value); } fn None() { Print("None"); } } } my_opt.(Match.Op)({} as __MatchStatementImpl); ``` (The name `__MatchStatementImpl` is a placeholder for illustration purposes; the actual generated class will be anonymous.) The mechanism described above for proper patterns may also be used for expression patterns if they have the form of an alternative pattern. An expression pattern of type `T` has the form of an alternative pattern if `T` implements `Match`, and the expression consists of an optional expression that names `T`, followed by a designator that names a method of `T.(Match.Continuation)`, optionally followed by a tuple expression. If an expression pattern has that form, it may be matched using the mechanism above, as if it were a proper pattern, rather than by evaluating the expression and comparing it to the scrutinee using `==`. Both possible implementations must be well-formed (and this is enforced by the compiler), but it is unspecified which implementation is used to generate code. As a result, it is **strongly** recommended that user-defined sum types ensure that for every alternative there is a factory function or constant member with the same name and parameter list, such that pattern-matching on the result will correctly reproduce the arguments to the factory function. For example, the definition of `Optional` above satisfies this requirement, because for any regular type `T`, the expression `Optional(T).None` evaluates to a value that matches the pattern `Optional(T).None` (under both possible matching mechanisms), and for any `x` of type `T`, the expression `Optional(T).Some(x)` evaluates to a value that matches the pattern `Optional(T).Some(y: T)` and binds `y` to a value that's equal to `x`. Expression patterns involving a sum type that doesn't meet this requirement will fail to compile, or have behavior that observably changes depending on the compiler's implementation choices. Another corollary of this rule is that if an alternative takes no arguments, its pattern syntax is the same as its expression syntax. For example, `case Optional(i32).None() => ...` is not well-formed, because `Optional(i32).None()` has the form of an alternative pattern, but the implementation in terms of `==` is not well-formed because `Optional(i32).None()` is not a well-formed expression. If we had defined `Optional.None` as a factory function instead of a constant, `case Optional(i32).None() => ...` would be well-formed but `case Optional(i32).None => ...` would not be. Note that the compiler-generated continuation method bodies are not required to contain the code in the `case` body (or whatever code is in the scope of the pattern). For example, they might only store the parameter values and then return an index that identifies the `case` body to be executed. ## Alternatives considered - [Providing `choice` types only](/proposals/p0157.md#choice-types-only), with no support for user-defined sum types. - [Indexing alternatives by type](/proposals/p0157.md#indexing-by-type) instead of by name. - Implementing user-defined sum types in terms of [`choice` type proxies](/proposals/p0157.md#pattern-matching-proxies) rather than callbacks. - Implementing user-defined sum types in terms of invertible [pattern functions](/proposals/p0157.md#pattern-functions). ## References - Proposal [#157: Design direction for sum types](https://github.com/carbon-language/carbon-lang/pull/157) ================================================ FILE: docs/design/templates.md ================================================ # Templates ## Table of contents - [TODO](#todo) - [Overview](#overview) - [Types with template parameters](#types-with-template-parameters) - [Functions with template parameters](#functions-with-template-parameters) - [Overloading](#overloading) - [Constraining templates with interfaces](#constraining-templates-with-interfaces) ## TODO This is a skeletal design, added to support [the overview](README.md). It should not be treated as accepted by the core team; rather, it is a placeholder until we have more time to examine this detail. Please feel welcome to rewrite and update as appropriate. ## Overview Carbon templates follow the same fundamental paradigm as C++ templates: they are instantiated, resulting in late type checking, duck typing, and lazy binding. They both enable interoperability between Carbon and C++ and address some (hopefully limited) use cases where the type checking rigor imposed by generics isn't helpful. ### Types with template parameters When parameterizing a user-defined type, the parameters can be marked as template parameters. The resulting type-function will instantiate the parameterized definition with the provided arguments to produce a complete type when used. Note that only the parameters marked as having this template behavior are subject to full instantiation -- other parameters will be type checked and bound early to the extent possible. For example: ``` class Stack(template T:! type) { var storage: Array(T); fn Push[ref self: Self](value: T); fn Pop[ref self: Self]() -> T; } ``` This both defines a parameterized type (`Stack`) and uses one (`Array`). Within the definition of the type, the template type parameter `T` can be used in all of the places a normal type would be used, and it will only by type checked on instantiation. ### Functions with template parameters Both deduced and explicit function parameters in Carbon can be marked as template parameters. When called, the arguments to these parameters trigger instantiation of the function definition, fully type checking and resolving that definition after substituting in the provided (or computed if deduced) arguments. The runtime call then passes the remaining arguments to the resulting complete definition. ``` fn Convert[template T:! type](source: T, template U:! type) -> U { var converted: U = source; return converted; } fn Foo(i: i32) -> f32 { // Instantiates with the `T` deduced argument set to `i32` and the `U` // explicit argument set to `f32`, then calls with the runtime value `i`. return Convert(i, f32); } ``` Here we deduce one type parameter and explicitly pass another. It is not possible to explicitly pass a deduced type parameter, instead the call site should cast or convert the argument to control the deduction. The explicit type is passed after a runtime parameter. While this makes that type unavailable to the declaration of _that_ runtime parameter, it still is a template parameter and available to use as a type even within the remaining parts of the function declaration. ### Overloading An important feature of templates in C++ is the ability to customize how they end up specialized for specific types. Because template parameters (whether as type parameters or function parameters) are pattern matched, we expect to leverage pattern matching techniques to provide "better match" definitions that are selected analogously to specializations in C++ templates. When expressed through pattern matching, this may enable things beyond just template parameter specialization, but that is an area that we want to explore cautiously. ### Constraining templates with interfaces Because we consider only specific _parameters_ to be templated and they could be individually migrated to a constrained interface using the [checked-generics system](README.md#generics), constraining templates themselves may be less critical. Instead, we expect parameterized types and functions may use a mixture of checked and template generic parameters based on where they are constrained. However, if there are still use cases, we would like to explore applying the interface constraints of the checked-generics system directly to template parameters rather than create a new constraint system. ================================================ FILE: docs/design/tuples.md ================================================ # Tuples ## Table of contents - [Overview](#overview) - [Element access](#element-access) - [Conversion](#conversion) - [Empty tuples](#empty-tuples) - [Trailing commas and single-element tuples](#trailing-commas-and-single-element-tuples) - [Tuple of types and tuple types](#tuple-of-types-and-tuple-types) - [Operations performed field-wise](#operations-performed-field-wise) - [Pattern matching](#pattern-matching) - [Open questions](#open-questions) - [Tuple slicing](#tuple-slicing) - [Slicing ranges](#slicing-ranges) - [Alternatives considered](#alternatives-considered) - [References](#references) ## Overview The primary composite type involves simple aggregation of other types as a tuple, called a "product type" in formal type theory: ``` fn DoubleBoth(x: i32, y: i32) -> (i32, i32) { return (2 * x, 2 * y); } ``` This function returns a tuple of two integers represented by the type `(i32, i32)`. The expression to return it uses a special tuple syntax to build a tuple within an expression: `(, )`. This is actually the same syntax in both cases. The return type is a tuple expression, and the first and second elements are expressions referring to the `i32` type. The only difference is the type of these expressions. Both are tuples, but one is a tuple of types. ## Element access Element access uses a syntax similar to field access, with an element index instead of a field name: ``` fn Sum(x: i32, y: i32) -> i32 { var t: (i32, i32) = (x, y); return t.0 + t.1; } ``` A parenthesized template constant expression can also be used to index a tuple: ``` fn Choose(template N:! i32) -> i32 { return (1, 2, 3).(N % 3); } ``` ## Conversion A tuple type `Source` can be converted to a tuple type `Dest` if they have the same number of elements, and each element type of `Source` is convertible to the corresponding element type of `Dest`, and the conversion is implicit if all of the element type conversions are implicit. See [here](values.md#type-conversions) for full details. ### Empty tuples `()` is the empty tuple. This is used in other parts of the design, such as [functions](functions.md), where a type with a single value is needed. ### Trailing commas and single-element tuples The final element in a tuple literal may be followed by a trailing comma, such as `(1, 2,)`. This trailing comma is optional in tuples with two or more elements, and mandatory in a tuple with a single element: `(x,)` is a one-tuple, whereas `(x)` is a parenthesized single expression. ### Tuple of types and tuple types A tuple of types can be used in contexts where a type is needed. This is made possible by a built-in implicit conversion: a tuple can be implicitly converted to type `type` if all of its elements can be converted to type `type`, and the result of the conversion is the corresponding tuple type. For example, `(i32, i32)` is a value of type `(type, type)`, which is not a type but can be implicitly converted to a type. `(i32, i32) as type` can be used to explicitly refer to the corresponding tuple type, which is the type of expressions such as `(1 as i32, 2 as i32)`. However, this is rarely necessary, as contexts requiring a type will implicitly convert their operand to a type: ```carbon // OK, both (i32, i32) values are implicitly converted to `type`. fn F(x: (i32, i32)) -> (i32, i32); ``` ### Operations performed field-wise Like some other aggregate data types like [struct types](classes.md#struct-types), there are some operations are defined for tuples field-wise: - initialization - assignment - equality and inequality comparison - ordered comparison - implicit conversion for argument passing - destruction For binary operations, the two tuples must have the same number of components and the operation must be defined for the corresponding component types of the two tuples. ### Pattern matching Tuple values can be matched using a [tuple pattern](/docs/design/pattern_matching.md#tuple-patterns), which is written as a tuple of element patterns: ```carbon let tup: (i32, i32, i32) = (1, 2, 3); match (tup) { case (a: i32, 2, var c: i32) => { c = a; return c + 1; } } ``` ## Open questions ### Tuple slicing Tuples could support multiple indices and slicing to restructure tuple elements: ``` fn Baz(x: i32, y: i32, z: i32) -> (i32, i32) { var t1: (i32, i32, i32) = (x, y, z); var t2: (i32, i32, i32) = t1.((2, 1, 0)); return t2.(0 .. 2); } ``` This code would first reverse the tuple, and then extract a slice using a half-open range of indices. ### Slicing ranges The intent of `0 .. 2` is to be syntax for forming a sequence of indices based on the half-open range [0, 2). There are a bunch of questions we'll need to answer here: - Is this valid anywhere? Only some places? - What _is_ the sequence? - If it is a tuple of indices, maybe that solves the above issue, and unlike function call indexing with multiple indices is different from indexing with a tuple of indexes. - Do we need syntax for a closed range (`...` perhaps, unclear if that ends up _aligned_ or in _conflict_ with other likely uses of `...` in pattern matching)? - All of these syntaxes are also very close to `0.2`, is that similarity of syntax OK? - Do we want to require the `..` to be surrounded by whitespace to minimize that collision? ## Alternatives considered - [Indexing with square brackets](/proposals/p3646.md#square-bracket-notation) - [Indexing from the end of a tuple](/proposals/p3646.md#negative-indexing-from-the-end-of-the-tuple) - [Restrict indexes to decimal integers](/proposals/p3646.md#decimal-indexing-restriction) - [Alternatives to trailing commas](/proposals/p3646.md#trailing-commas) ## References - Proposal [#2188: Pattern matching syntax and semantics](https://github.com/carbon-language/carbon-lang/pull/2188) - Proposal [#2360: Types are values of type `type`](https://github.com/carbon-language/carbon-lang/pull/2360) - Proposal [#3646: Tuples and tuple indexing](https://github.com/carbon-language/carbon-lang/pull/3646) - Leads issue [#710](https://github.com/carbon-language/carbon-lang/issues/710) established rules for assignment, comparison, and implicit conversion - Leads issue [#2191: one-tuples and one-tuple syntax](https://github.com/carbon-language/carbon-lang/issues/2191) ================================================ FILE: docs/design/type_inference.md ================================================ # Type inference ## Table of contents - [Overview](#overview) - [Open questions](#open-questions) - [Inferring a variable type from literals](#inferring-a-variable-type-from-literals) - [Alternatives considered](#alternatives-considered) - [References](#references) ## Overview [Type inference](https://en.wikipedia.org/wiki/Type_inference) occurs in Carbon when the `auto` keyword is used. This may occur in [variable declarations](values.md#binding-patterns-and-local-variables-with-let-and-var) or [function declarations](functions.md). At present, type inference is very simple: given the expression which generates the value to be used for type inference, the inferred type is the precise type of that expression. For example, the inferred type for `auto` in `fn Foo(x: i64) -> auto { return x; }` is `i64`. Type inference is currently supported for [function return types](functions.md) and [declared variable types](values.md#binding-patterns-and-local-variables-with-let-and-var). ## Open questions ### Inferring a variable type from literals Using the type on the right side for `var y: auto = 1` currently results in a constant `IntLiteral(1)` value, whereas most languages would suggest a variable integer type, such as `i64`. Carbon might also make it an error. Although type inference currently only addresses `auto` for variables and function return types, this is something that will be considered as part of type inference in general, because it also affects checked generics, templates, lambdas, and return types. ## Alternatives considered - [Use `_` instead of `auto`](/proposals/p0851.md#use-_-instead-of-auto) ## References - Proposal [#851: auto keyword for vars](https://github.com/carbon-language/carbon-lang/pull/851) ================================================ FILE: docs/design/values.md ================================================ # Values, variables, and pointers ## Table of contents - [Values, objects, and expressions](#values-objects-and-expressions) - [Expression categories](#expression-categories) - [Value acquisition](#value-acquisition) - [Direct initialization](#direct-initialization) - [Copy initialization](#copy-initialization) - [Temporary materialization](#temporary-materialization) - [Binding patterns and local variables with `let` and `var`](#binding-patterns-and-local-variables-with-let-and-var) - [Local variables](#local-variables) - [Consuming function parameters](#consuming-function-parameters) - [Reference expressions](#reference-expressions) - [Entire reference expressions](#entire-reference-expressions) - [Durable reference expressions](#durable-reference-expressions) - [Ephemeral reference expressions](#ephemeral-reference-expressions) - [Value expressions](#value-expressions) - [Comparison to C++ parameters](#comparison-to-c-parameters) - [Polymorphic types](#polymorphic-types) - [Interop with C++ `const &` and `const` methods](#interop-with-c-const--and-const-methods) - [Escape hatches for value addresses in Carbon](#escape-hatches-for-value-addresses-in-carbon) - [Initializing expressions](#initializing-expressions) - [Function calls and returns](#function-calls-and-returns) - [Deferred initialization from values and references](#deferred-initialization-from-values-and-references) - [Declared `returned` variable](#declared-returned-variable) - [Expression forms](#expression-forms) - [Initializing results](#initializing-results) - [Form conversions](#form-conversions) - [Type conversions](#type-conversions) - [Category conversions](#category-conversions) - [Pointers](#pointers) - [Reference types](#reference-types) - [Pointer syntax](#pointer-syntax) - [Dereferencing customization](#dereferencing-customization) - [`const`-qualified types](#const-qualified-types) - [Lifetime overloading](#lifetime-overloading) - [Value representation and customization](#value-representation-and-customization) - [Alternatives considered](#alternatives-considered) - [References](#references) ## Values, objects, and expressions Carbon has both abstract _values_ and concrete _objects_. Carbon _values_ are things like `42`, `true`, and `i32` (a type value). Carbon _objects_ have _storage_ where values can be read and written. Storage also allows taking the address of an object in memory in Carbon. Both objects and values can be nested within each other. For example `(true, true)` is both a value and also contains two sub-values. When a two-tuple is stored somewhere, it is both a tuple-typed object and contains two subobjects. These terms are important components in the describing the semantics of Carbon code, but they aren't sufficient. We also need to explicitly and precisely talk about the Carbon _expressions_ that produce or reference values and objects. Categorizing the expressions themselves allows us to be more precise and articulate important differences not captured without looking at the expression itself. ### Expression categories There are three primary expression categories in Carbon: - [_Value expressions_](#value-expressions) produce abstract, read-only _values_ that cannot be modified or have their address taken. - [_Reference expressions_](#reference-expressions) refer to _objects_ with _storage_ where a value may be read or written and the object's address can be taken. - [_Initializing expressions_](#initializing-expressions) require a result location to be provided implicitly when evaluating the expression. The expression then initializes an object in that location. These are used to model function returns, which can construct the returned value directly in the caller's storage. Expressions in one category can be implicitly converted to any other primary category when needed. The primitive conversion steps used are: - [_Value acquisition_](#value-acquisition) forms a value expression from the current value of the object referenced by a reference expression. - [_Direct initialization_](#direct-initialization) converts a value expression into an initializing expression. - [_Copy initialization_](#copy-initialization) converts a reference expression into an initializing expression. - [_Temporary materialization_](#temporary-materialization) converts an initializing expression into a reference expression. These conversion steps combine to provide the transitive conversion table: | From: | value | reference | initializing | | ------------------: | ------------------------- | --------- | --------------------- | | to **value** | == | acquire | materialize + acquire | | to **reference** | direct init + materialize | == | materialize | | to **initializing** | direct init | copy init | == | Reference expressions are divided into 2x2 sub-categories: they can be either [_ephemeral_](#ephemeral-reference-expressions) or [_durable_](#durable-reference-expressions), and either _entire_ or _non-entire_. Ephemeral reference expressions are formed through temporary materialization, and have restrictions on how they are used. In contrast, durable reference expressions refer to storage that outlives the expression, and typically has a declared name. Entire reference expressions can only refer to complete objects, whereas non-entire reference expressions can refer to both complete objects and sub-objects (such as class fields and base class sub-objects). As a consequence, only entire reference expressions can be destructively moved. > **Future work:** This means that pointer-dereference expressions are > non-entire, but we will presumably want to be able to destructively move from > them. We need to figure out how to support that without violating the > invariant that a live object has live fields. Value acquisition and copy initialization can be applied to any reference expression, but materialization only produces ephemeral entire reference expressions. An entire reference expression can be implicitly converted to non-entire; this has no run-time effect because it merely discards static object-completeness information. Non-entire reference expressions can only be converted to entire reference expressions by round-tripping through copy-initialization and materialization. Non-durable-reference expressions cannot be implicitly converted to durable reference expressions at all. > **TODO:** Determine how these reference sub-categories relate to memory-safety > properties like uniqueness, and make sure their names are aligned with > memory-safety terminology. #### Value acquisition We call forming a value expression from a reference expression _value acquisition_. This forms a value expression that will evaluate to the value of the object in the referenced storage of the reference expression. It may do this by eagerly reading that value into a machine register, lazily reading that value on-demand into a machine register, or in some other way modeling that abstract value. See the [value expressions](#value-expressions) section for more details on the semantics of value expressions. #### Direct initialization This is the first way we have of initializing storage of an object. There may not be storage for the source of this initialization, as the value expression used for the initialization may be in a machine register or simply be abstractly modeled like a source literal. A canonical example here is zeroing an object. #### Copy initialization This initializes storage for an object based on some other object which already has initialized storage. A classic example here are types which can be copied trivially and where this is implemented as a `memcpy` of their underlying bytes. #### Temporary materialization We use temporary materialization when we need to initialize an object by way of storage, but weren't provided dedicated storage and can simply acquire the result as a value afterward. > **Open question:** The lifetimes of temporaries is not yet specified. ## Binding patterns and local variables with `let` and `var` A [_value binding pattern_](/docs/design/README.md#binding-patterns) introduces a name that is a [_value expression_](#value-expressions) and is called a _value binding_. This is the desired default for many pattern contexts, especially function parameters. Values are a good model for "input" function parameters which are the dominant and default style of function parameters: ```carbon fn Sum(x: i32, y: i32) -> i32 { // `x` and `y` are value expressions here. We can use their value, but not // modify them or take their address. return x + y; } ``` Value bindings require the matched expression to be a _value expression_, converting it into one as necessary. A _variable pattern_ is introduced with the `var` keyword. The matched expression must be an ephemeral entire reference expression (which typically requires the matched expression to be materialized); the `var` pattern takes ownership of the newly-allocated temporary storage it refers to, which extends its lifetime to the end of the enclosing scope. The subpattern is then matched against a _durable_ entire reference expression to the object in that storage. > **Open question:** This implies that `var field: T = F().field;` doesn't > perform any copies or moves on `T`. This, in turn, implies that the storage > for `field` must be laid out as part of a complete `typeof(F())` object > layout, which is initialized by the call to `F()`. All other members of that > layout are immediately destroyed, and their storage is theoretically reusable > after that point, but it's unclear if this is the right default, or how to > enable user code to override that default when it's the wrong tradeoff. A _reference binding pattern_ is a binding pattern that is nested under a `var` pattern. It introduces a name called a _reference binding_ that is a [durable reference expression](#durable-reference-expressions) to an object within the variable pattern's storage. ```carbon fn MutateThing(ptr: i64*); fn Example() { // `1` starts as a value expression, which is what a value binding expects. let x: i64 = 1; // `2` also starts as a value expression, but the variable pattern requires it // to be converted to an ephemeral entire reference expression by using the // value `2` to initialize temporary storage, which the variable pattern // takes ownership of. The reference binding pattern is then bound to a // durable reference to the newly-initialized object. var y: i64 = 2; // Allowed to take the address and mutate `y` as it is a durable reference // expression. MutateThing(&y); // ❌ This would be an error though due to trying to take the address of the // value expression `x`. MutateThing(&x); } ``` ### Local variables A local binding pattern can be introduced with either the `let` or `var` keyword. The `let` introducer begins a value pattern which works the same as the default patterns in other contexts. The `var` introducer immediately begins a variable pattern. - `let` _identifier_`:` _( expression |_ `auto` _)_ `=` _value_`;` - `var` _identifier_`:` _( expression |_ `auto` _) [_ `=` _value ]_`;` These are just simple examples of binding patterns used directly in local declarations. Local `let` and `var` declarations build on Carbon's general [pattern matching](/docs/design/pattern_matching.md) design. `var` declarations implicitly start off within a `var` pattern. `let` declarations introduce patterns that bind values by default, the same as function parameters and most other pattern contexts. The general pattern matching model also allows nesting `var` sub-patterns within a larger pattern that defaults to matching values. For example, we can combine the two local declarations above into one destructuring declaration with an inner `var` pattern here: ```carbon fn DestructuringExample() { // Both `1` and `2` start as value expressions. The `x` binding directly // matches `1`. For `2`, the variable pattern requires it to be converted to // an ephemeral entire reference expression by using the value `2` to // initialize temporary storage, which the variable pattern takes ownership // of. The reference binding `y` is then bound to a durable reference to the // newly-initialized object. let (x: i64, var y: i64) = (1, 2); // Just like above, we can take the address and mutate `y`: MutateThing(&y); // ❌ And this remains an error: MutateThing(&x); } ``` If `auto` is used in place of the type for a local binding pattern, [type inference](type_inference.md) is used to automatically determine the variable's type. These local bindings introduce names scoped to the code block in which they occur, which will typically be marked by an open brace (`{`) and close brace (`}`). ### Consuming function parameters Just as part of a `let` declaration can use a `var` prefix to become a variable pattern and bind names that will form reference expressions to the variable's storage, so can function parameters: ```carbon fn Consume(var x: SomeData) { // We can mutate and use variable that `x` refers to here. } ``` This allows us to model an important special case of function inputs -- those that are _consumed_ by the function, either through local processing or being moved into some persistent storage. Marking these in the pattern and thus signature of the function changes the expression category required for arguments in the caller. These arguments are required to be _ephemeral entire reference expressions_, potentially being converted into such an expression if necessary, whose storage will be dedicated-to and owned-by the function parameter. This pattern serves the same purpose as C++'s pass-by-value when used with types that have non-trivial resources attached to pass ownership into the function and consume the resource. But rather than that being the seeming _default_, Carbon makes this a use case that requires a special marking on the declaration. ## Reference expressions _Reference expressions_ refer to _objects_ with _storage_ where a value may be read or written and the object's address can be taken. Reference expressions can be either _durable_ or _ephemeral_. These refine the _lifetime_ of the underlying storage and provide safety restrictions reflecting that lifetime. Reference expressions can also be either _entire_ or _non-entire_, depending on whether the referenced object is known to be complete (rather than a sub-object of another object). ### Entire reference expressions An _entire reference expression_ is one that is statically known to refer to a complete object. Other references are _non-entire_. Durable and ephemeral reference expressions can both be either entire or non-entire (although non-entire ephemeral references are rare). Unless otherwise specified, an expression or operation that produces a reference produces a non-entire reference. Note that a non-entire reference expression still _might_ refer to a complete object; the language rules just don't _guarantee_ that is does. As a result, an entire reference can be implicitly converted to a non-entire reference (with the same durability), because this merely discards the knowledge that the object is complete. By the same token, there is no context that requires a non-entire reference; only contexts that accept both, that accept only entire references, or that don't accept references at all. Currently, the only context that requires an entire reference is the scrutinee of a `var` pattern, which is required to be an entire ephemeral reference (and is [converted](#category-conversions) to that category if necessary). > **Note:** This extends the lifetime of the reference, so it must be possible > to determine _which_ temporary an ephemeral entire reference refers to, so > that the implementation knows which lifetime to extend. Under the current > language rules, this can be done statically. > **Open question:** Should we extend the language in ways that would force that > determination to be dynamic? For example, should we allow > `if c then r1 else r2` to be an entire ephemeral reference expression if `r1` > and `r2` are? As a more extreme example, should we support functions that take > and return entire ephemeral references? There are several kinds of expressions that produce entire references. For example: - The name of an object introduced with a [variable binding pattern](pattern_matching.md#name-binding-patterns) (in other words, a name that was declared with `var : `) is a durable entire reference. - a member access expression `x.member` or `x.(member)` is an entire reference if `x` is an initializing or entire ephemeral reference expression with a struct or tuple type. - The result of materialization is an entire ephemeral reference. - When a [tuple pattern](pattern_matching.md#tuple-patterns) or [struct pattern](pattern_matching.md#struct-patterns) is matched with an ephemeral entire reference scrutinee, that scrutinee is destructured into ephemeral entire references to its elements, which are then matched with the corresponding subpatterns. ### Durable reference expressions _Durable reference expressions_ are those where the object's storage outlives the full expression and the address could be meaningfully propagated out of it as well. There are several contexts where durable reference expressions are required. For example: - [Assignment statements](/docs/design/assignment.md) require the left-hand-side of the `=` to be a durable reference. This stronger requirement is enforced before the expression is rewritten to dispatch into the `Carbon.Assign.Op` interface method. - [Address-of expressions](#pointer-syntax) require their operand to be a durable reference and compute the address of the referenced object. - [`ref` binding patterns](pattern_matching.md#name-binding-patterns) require their scrutinee to be a durable reference. - If a function's [return form](#function-calls-and-returns) contains `ref` tags, `return` statements require the corresponding parts of the operand to be durable reference expressions. There are also several kinds of expressions that produce durable references. For example: - Names of objects introduced with a [reference binding](#binding-patterns-and-local-variables-with-let-and-var): `x` - Dereferenced [pointers](#pointers): `*p` - Names of subobjects through member access to some other durable reference expression: `x.member` or `p->member` - [Indexing](/docs/design/expressions/indexing.md) into a type similar to C++'s `std::span` that implements `IndirectIndexWith`, or indexing into any type with a durable reference expression such as `local_array[i]`. - Calls to functions whose [return forms](#function-calls-and-returns) contain `ref`. Durable reference expressions can only be produced _directly_ by one of these expressions. They are never produced by converting one of the other expression categories into a reference expression. ### Ephemeral reference expressions We call the reference expressions formed through [temporary materialization](#temporary-materialization) _ephemeral reference expressions_. They still refer to an object with storage, but it may be storage that will not outlive the full expression, and so it can't be used where a durable reference is expected. > **Future work:** The current design does not support mutating ephemeral > references (or initializing expressions): assigning to an ephemeral reference > is disallowed directly, and invoking mutating methods is disallowed because > the `ref self` parameter can only bind to a durable reference. In C++ it's > unusual but not rare to intentionally mutate a temporary, such as in a > builder-style method chain (for example `MakeFoo().SetBar().AddBaz()`), so > Carbon will need to provide some interop and migration target for that kind of > code. There is one context that requires an ephemeral reference expression in Carbon: the scrutinee of a [`var` pattern](#binding-patterns-and-local-variables-with-let-and-var) (which also requires the reference to be entire). There are only a few ways to produce an ephemeral reference expression. Most notably: - The result of materialization is an entire ephemeral reference. - A member access expression `x.member` or `x.(member)` is an ephemeral reference if `x` is an initializing or ephemeral reference. - When a [tuple pattern](pattern_matching.md#tuple-patterns) or [struct pattern](pattern_matching.md#struct-patterns) is matched with an initializing or ephemeral reference scrutinee, that scrutinee is destructured into ephemeral references to its elements, which are then matched with the corresponding subpatterns. ## Value expressions A value cannot be mutated, cannot have its address taken, and may not have storage at all or a stable address of storage. Values are abstract constructs like function input parameters and constants. They can be formed in two ways -- a literal expression like `42`, or by reading the value of some stored object. A core goal of values in Carbon is to provide a single model that can get both the efficiency of passing by value when working with small types such as those that fit into a machine register, but also the efficiency of minimal copies when working with types where a copy would require extra allocations or other costly resources. This directly helps programmers by providing a simpler model to select the mechanism of passing function inputs. But it is also important to enable generic code that needs a single type model that will have consistently good performance. When forming a value expression from a reference expression, Carbon [acquires](#value-acquisition) the value of the referenced object. This allows immediately reading from the object's storage into a machine register or a copy if desired, but does not require that. The read of the underlying object can also be deferred until the value expression itself is used. Once an object is bound to a value expression in this way, any mutation to the object or its storage ends the lifetime of the value binding, and makes any use of the value expression an error. > Note: this is _not_ intended to ever become "undefined behavior", but instead > just "erroneous". We want to be able to detect and report such code as having > a bug, but do not want unbounded UB and are not aware of important > optimizations that this would inhibit. > > _Open issue:_ We need a common definition of erroneous behavior that we can > use here (and elsewhere). Once we have that, we should cite it here. > Note: this restriction is also **experimental** -- we may want to strengthen > or weaken it based on experience, especially with C++ interop and a more > complete memory safety story. Even with these restrictions, we expect to make values in Carbon useful in roughly the same places as `const &`s in C++, but with added efficiency in the case where the values can usefully be kept in machine registers. We also specifically encourage a mental model of a `const &` with extra efficiency. The actual _representation_ of a value when bound, especially across function boundaries, is [customizable](#value-representation-and-customization) by the type. The defaults are based around preserving the baseline efficiency of C++'s `const &`, but potentially reading the value when that would be both correct and reliably more efficient, such as into a machine register. ### Comparison to C++ parameters While these are called "values" in Carbon, they are not related to "by-value" parameters as they exist in C++. The semantics of C++'s by-value parameters are defined to create a new local copy of the argument, although it may move into this copy. Carbon's values are much closer to a `const &` in C++ with extra restrictions such as allowing copies under "as-if" and preventing taking the address. Combined, these restrictions allow implementation strategies such as in-register parameters. ### Polymorphic types Value expressions and value bindings can be used with [polymorphic types](/docs/design/classes.md#inheritance), for example: ```carbon base class MyBase { ... } fn UseBase(b: MyBase) { ... } class Derived { extend base: MyBase; ... } fn PassDerived() { var d: Derived = ...; // Allowed to pass `d` here: UseBase(d); } ``` This is still allowed to create a copy or to move, but it must not _slice_. Even if a copy is created, it must be a `Derived` object, even though this may limit the available implementation strategies. > **Future work:** The interaction between a > [custom value representation](#value-representation-and-customization) and a > value expression used with a polymorphic type needs to be fully captured. > Either it needs to restrict to a `const ref` style representation (to prevent > slicing) or it needs to have a model for the semantics when a different value > representation is used. ### Interop with C++ `const &` and `const` methods While value expressions cannot have their address taken in Carbon, they should be interoperable with C++ `const &`s and C++ `const`-qualified methods. This will in-effect "pin" some object (potentially a copy or temporary) into memory and allow C++ to take its address. Without supporting this, values would likely create an untenable interop ergonomic barrier. However, this does create some additional constraints on value expressions and a way that their addresses can escape unexpectedly. Despite interop requiring an address to implement, C++ allows `const &` parameters to bind to temporary objects where that address doesn't have much meaning and might not be valid once the called function returns. As a consequence, we don't expect C++ interfaces using a `const &` to misbehave in practice. > **Future work:** when a type customizes its > [value representation](#value-representation-and-customization), as currently > specified this will break the use of `const &` C++ APIs with such a value. We > should extend the rules around value representation customization to require > that either the representation type can be converted to (a copy) of the > customized type, or implements an interop-specific interface to compute a > `const` pointer to the original object used to form the representation object. > This will allow custom representations to either create copies for interop or > retain a pointer to the original object and expose that for interop as > desired. Another risk is exposing Carbon's value expressions to `const &` parameters in this way, as C++ allows casting away `const`. However, in the absence of `mutable` members, casting away `const` does not make it safe to _mutate_ through a `const &` parameter (or a `const`-qualified method). C++ allows `const &` parameters and `const` member functions to access objects that are _declared_ `const`. These objects cannot be mutated, even if `const` is removed, exactly the same as Carbon value expressions. In fact, these kinds of mutations [break in real implementations](https://cpp.compiler-explorer.com/z/KMhTondaK). The result is that Carbon's value expressions will work similarly to `const`-declared objects in C++, and will interop with C++ code similarly well. ### Escape hatches for value addresses in Carbon **Open question:** It may be necessary to provide some amount of escape hatch for taking the address of values. The [C++ interop](#interop-with-c-const--and-const-methods) above already takes their address functionally. Currently, this is the extent of an escape hatch to the restrictions on values. If a further escape hatch is needed, this kind of fundamental weakening of the semantic model would be a good case for some syntactic marker like Rust's `unsafe`, although rather than a region, it would seem better to tie it directly to the operation in question. For example: ```carbon class S { fn ValueMemberFunction[self: Self](); fn RefMemberFunction[ref self: const Self](); } fn F(s_value: S) { // This is fine. s_value.ValueMemberFunction(); // This requires an unsafe marker in the syntax. s_value.unsafe RefMemberFunction(); } ``` The specific tradeoff here is covered in a proposal [alternative](/proposals/p2006.md#value-expression-escape-hatches). ## Initializing expressions Storage in Carbon is initialized using _initializing expressions_. Their evaluation takes a _result location_ as an implicit input, and produces an initialized object at that location, although that object may still be _unformed_. **Future work:** More details on initialization and unformed objects should be added to the design from the proposal [#257](https://github.com/carbon-language/carbon-lang/pull/257), see [#1993](https://github.com/carbon-language/carbon-lang/issues/1993). When added, it should be linked from here for the details on the initialization semantics specifically. The simplest form of initializing expressions are value or durable reference expressions that are converted into an initializing expression. Value expressions are written directly into the storage to form a new object. Reference expressions have the object they refer to copied into a new object in the provided storage. **Future work:** The design should be expanded to fully cover how copying is managed and linked to from here. There are no syntactic contexts in Carbon that always require an initializing expression, and no expression syntax that always produces an initializing expression. By default, function call expressions are initializing expressions, and correspondingly the operand of `return` is required to be an initializing expression, but this default can be overridden by the [function signature](#function-calls-and-returns). Initializing expressions can also be created implicitly, when attempting to convert an expression into an ephemeral entire reference expression (particularly to match a `var` pattern): the expression is first converted to an initializing expression if necessary, and then temporary storage is materialized to act as its output, and as the referent of the resulting ephemeral reference expression. ### Function calls and returns The [result](#expression-forms) of a function call can have an almost arbitrary form. The return clause of a function signature consists of `->` followed by a _return form_, an expression-like syntax that specifies not only the type but also the form of the function call's result. `return` expressions in the function body are expected to have that form, and are converted to it if necessary. When a function is declared without a return clause, it behaves from the caller's point of view as if the return clause were `-> ()`, but `return` statements in the function body don't take operands (and can be omitted at the end of the function). In the common case, the return form is a type expression, in which case calls are modeled directly as initializing expressions -- they require storage as an input and when evaluated cause that storage to be initialized with an object. This means that when a function call is used to initialize some variable pattern as here: ```carbon fn CreateMyObject() -> MyType { return ; } var x: MyType = CreateMyObject(); ``` The `` in the `return` statement actually initializes the storage provided for `x`. There is no "copy" or other step. In the body of such a function, all `return` statement expressions are required to be initializing expressions and in fact initialize the storage provided to the function's call expression. This in turn causes the property to hold _transitively_ across an arbitrary number of function calls and returns. The storage is forwarded at each stage and initialized exactly once. More generally, the syntax and semantics of a return form are as follows: - _return-clause_ ::= `->` _return-form_ - _return-form_ ::= _nesting-return-form_ | _auto-return-form_ - _nesting-return-form_ ::= _expression-return-form_ | _proper-return-form_ Return forms can usually be nested, but syntaxes involving `auto` can only occur at top level. We further divide nesting return forms into expressions and "proper" return forms, but this is just a technical means of avoiding formal ambiguity in the grammar; it has no greater significance. - _category-tag_ ::= `val` | `ref` | `var` These tags are used to specify "value", "non-entire durable reference", or "initializing" expression category (respectively). Note that there is no way to express an entire or ephemeral reference category in a return form. - _auto-return-form_ ::= _category-tag_? `auto` This denotes a primitive form with runtime phase and deduced type. The category is determined by _category-tag_ if present, or "initializing" otherwise. - _proper-return-form_ ::= _category-tag_ _expression_ This denotes a primitive form with runtime phase, category _category-tag_, and type "_expression_ `as type`". - _expression-return-form_ ::= _expression_ An expression with no _category-tag_ is equivalent to "`var` _expression_". - _proper-return-form_ ::= `(` [_expression-return-form_ `,`]\* _proper-return-form_ [`,` _nesting-return-form_]\* `,`? `)` A tuple literal of return forms denotes a tuple form whose sub-forms are specified by the comma-separated elements. To avoid formal ambiguity, this grammar rule requires at least one of the sub-forms to be proper. - _expression-field-form_ ::= _designator_ `:` _expression-return-form_ - _proper-field-form_ ::= _designator_ `:` _proper-return-form_ - _field-form_ ::= _field-decl_ - _field-form_ ::= _proper-field-form_ - _proper-return-form_ ::= `{` [_expression-field-form_ `,`]\* _proper-field-form_ [`,` _field-form_]\* `}` A struct literal of return forms denotes a struct form whose field names and their forms are specified by the comma-separated field forms. To avoid formal ambiguity, this grammar rule requires at least one of the field forms to be proper. > **Open question:** Should there be a way to specify symbolic or template phase > in return forms? #### Deferred initialization from values and references TODO: This section needs to be updated to reflect the addition of `-> val` returns in [proposal #5434](/proposals/p5434.md). This section could be replaced by a statement that initializing returns may be replaced by value returns when that is safe and correct, moving much of this content into a description of how value returns works. Carbon also makes the evaluation of function calls and return statements tightly linked in order to enable more efficiency improvements. It allows the actual initialization performed by the `return` statement with its expression to be deferred from within the body of the function to the caller initializer expression if it can simply propagate a value or reference expression to the caller that is guaranteed to be alive and available to the caller. Consider the following code: ```carbon fn SelectSecond(first: Point, second: Point, third: Point) -> Point { return second; } fn UsePoint(p: Point); fn F(p1: Point, p2: Point) { UsePoint(SelectSecond(p2, p1, p2)); } ``` The call to `SelectSecond` must provide storage for a `Point` that can be initialized. However, Carbon allows an implementation of the actual `SelectSecond` function to not initialize this storage when it reaches `return second`. The expression `second` is a name bound to the call's argument value expression, and that value expression is necessarily valid in the caller. Carbon in this case allows the implementation to merely communicate that the returned expression is a name bound to a specific value expression argument to the call, and the caller _if necessary_ should initialize the temporary storage. This in turn allows the caller `F` to recognize that the value expression argument (`p1`) is already valid to pass as the argument to `UsePoint` without initializing the temporary storage from it and reading it back out of that storage. None of this impacts the type system and so an implementation can freely select specific strategies here based on concrete types without harming generic code. Even if generics were to be implemented without monomorphization, for example dynamic dispatch of object-safe interfaces, there is a conservatively correct strategy that will work for any type. This freedom mirrors that of [input values](#value-expressions) where might be implemented as either a reference or a copy without breaking genericity. Here too, many small types will not need to be lazy and simply eagerly initialize the temporary which is implemented as an actual machine register. But for large types or ones with associated allocated storage, this can reliably avoid extraneous memory allocations and other costs. Note that this flexibility doesn't avoid the call expression materializing temporary storage and providing it to the function. Whether the function needs this storage is an implementation detail. It simply allows deferring an important case of initializing that storage from a value or reference expression already available in the caller to the caller so that it can identify cases where that initialization is not necessary. **References:** This addresses an issue-for-leads about [reducing the potential copies incurred by returns](https://github.com/carbon-language/carbon-lang/issues/828). #### Declared `returned` variable The model of initialization of returns also facilitates the use of [`returned var` declarations](control_flow/return.md#returned-var). These directly observe the storage provided for initialization of a function's return. ## Expression forms We typically treat the category and type of an expression as independent properties. However, in some cases we need to deal with them as an integrated whole. The _form_ of an expression captures all of the information about it that is visible to the type system, while abstracting away all other information about it. Thus, forms are a generalization of types: what we conventionally call "types" are really the types of objects and values, whereas forms are the types of expressions and patterns. A _primitive form_ currently consists of a type, an expression category, an expression phase, and optionally a constant value (which is present if and only if the expression phase is not "runtime"). When dealing with primitive forms, which is the common case, we can treat each of those properties as independent. For convenience, in this section we will use the notation `` to represent a primitive form with type `T`, category `C`, phase `P` and value `V`, but this is not Carbon syntax. Other forms are called _composite forms_, and there are two kinds: A _tuple form_ can be thought of as a tuple of forms, just as a tuple type can be thought of as a tuple of types. The form of a tuple literal is a tuple form, whose elements are the forms of the literal elements. > **TODO:** Extend this to support variadic forms. A _struct form_ can be thought of as a struct whose fields are forms, just as a struct type can be thought of as a struct whose fields are types. The form of a struct literal is a struct form with the same field names, whose values are the forms of the corresponding fields of the struct literal. The _type component_ of a form is defined as follows: - The type component of a primitive form `` is `T`. - The type component of a tuple form is a tuple of the type components of its elements. - The type component of a struct form is a struct whose field names are the field names of the struct form and whose field types are the type components of the corresponding elements. The _category component_ and _phase component_ of a form are defined likewise. The category component of a struct form is called a _struct category_, and the category component of a tuple form is called a _tuple category_. The type of an expression is the type component of the expression's form. Evaluating an expression produces a _result_. It can be defined recursively in terms of the expression's form: - The result of an initializing expression is an [initializing result](#initializing-results). - The result of a value expression is a value. - The result of a reference expression is a reference of the same kind. - The result of an expression with tuple form is a tuple of results. - The result of an expression with struct form is a struct of results. An expression and its result always have the same form. The code that accesses the result of an expression is said to _consume_ that result, and every primitive-form result is consumed exactly once (except in certain narrow contexts where the result is known not to be initializing). If a result isn't explicitly accessed, such as when the expression is used as a statement, it is said to be _discarded_, which consumes it in the absence of an explicit consumer. Discarding an initializing result materializes and then immediately destroys it. Discarding an entire ephemeral reference destroys the object it refers to. Discarding a value or any other kind of reference is a no-op. ### Initializing results As discussed earlier, evaluation of an initializing expression takes as an input the result location that it initializes, which is implicitly provided by the context in which the evaluation takes place. In some cases, the context may obtain the location from its own context, and so on. For example: ```carbon class C { private var i: i32; fn Make() -> C { return {.i = 0}; } } fn F() -> C { return C.Make(); } fn G() { var c: C = F(); } ``` By default, a function call is an initializing expression, and a `return` statement initializes the call's result location (which is passed as a hidden output parameter). So when the declaration of `c` is evaluated, its storage is implicitly passed into `F()` as an output parameter, which is initialized by the `return` statement inside `F`. When that `return` statement is evaluated to initialize the result location, it likewise implicitly passes the storage into `C.Make()` as an output parameter, which is initialized by the `return` statement inside `C.Make`. Finally, that `return` statement initializes the result location (which is still the location of `c`'s storage) by [direct initialization](#direct-initialization) from the value expression `{.i = 0}`. Notice that the implicit storage parameter propagates "backwards", into an expression from the code that uses its result. In order to simplify the description of the language, we usually won't explicitly discuss the result locations of initializing expressions, or how they're propagated. Instead, this propagation is encapsulated inside the _initializing result_, which is the notional result of an initializing expression. Whenever an initializing result is consumed, that implicitly means that the consumer passes a result location into the evaluation of the initializing expression. The source of that location depends on the consumer: - If the consumer is a temporary materialization conversion, the result location is newly-allocated temporary storage (which the consumer may subsequently lifetime-extend to durable storage). - If the consumer is a `return` statement, and the initializing result corresponds to an initializing sub-form of the function's return form, the result location is the implicit output parameter corresponding to that initializing sub-form. ### Form conversions A conversion between forms can be broken down into up to three steps: type conversion, category conversion, and phase conversion. These convert the form to a particular target type, category, and phase component (respectively). These steps aren't fully orthogonal: type conversions can change the category and phase components as a byproduct, and category conversions can change the phase component. However, category conversions can't change the type component, and phase conversions can't change either of the other two, so converting the type, then category, then phase, ensures that we converge on the desired result. Any of these steps may be omitted, depending on whether the context imposes requirements on the corresponding component. Most commonly, an operand position requires its operand to have a primitive form with a particular category, usually with a particular type, and sometimes with a particular phase. Phase conversions cannot change the form structure; they can only apply primitive phase conversions to primitive sub-forms. Type and category conversions are more complex, and are covered in the next two sections. Note that these rules will implicitly convert between primitive and composite forms in both directions (except that a composite containing references cannot be converted to a primitive form). As a result, although the difference between primitive and composite forms is observable by way of overloading, it can't reliably carry any higher-level meaning, and should be used only as an optimization tool. Note that this section describes the _logical structure_ of form conversions. As such, it primarily describes them "breadth-first", as a sequence of operations that each applies to the whole expression by recursively operating on its parts. However, the _physical execution_ of these conversions is actually depth-first, applying as many operations as possible to a minimal subexpression before moving on to the next one. The details of that process are described [here](pattern_matching.md#evaluation-order). #### Type conversions See [here](expressions/implicit_conversions.md) for overall information about type conversions. Conversions involving struct, tuple, and array types are described here because of their unique interactions with expression forms. > **TODO:** A forthcoming proposal is expected to update the type conversion > interfaces to permit user-defined conversions to depend on the form of the > input, and customize the form of the output. Once that is done, these "built > in" conversions should be presented as implementations of those interfaces, > possibly with some "magic" for things like introspecting on struct field > names. Each of the conversions described in this section is explicit if and only if it invokes another explicit type conversion. Otherwise, it is implicit. A type conversion of a primitive-form expression to a [compatible type](generics/terminology.md#compatible-types) just re-interprets the expression's result with a new type, so it requires no run-time work, and has the same category as the input expression. A result `source` that has a struct type can be converted to a struct type `Dest` if they have the same set of field names: - If the type of `source` is `Dest`, return `source`. - If `source` is a struct result, for each field name `F` in `Dest`, type-convert `source.F` to `Dest.F`. Return a struct result where each field `F` is set to the result of the corresponding conversion. - If `source` is a primitive result, convert it to a struct result by [form decomposition](#category-conversions), and then type-convert the result to `Dest` and return the result. Note that the sub-conversions invoked here are not necessarily defined; if so, the conversion itself is not defined. There is a conversion to a class type `Dest` from a result `source` that has a struct type, if there is a conversion from `source` to a struct type that has the same field names as `Dest` (including a `.base` field if `Dest` is a derived class), with the same types, in the same order. The conversion type-converts `source` to that struct type, category-converts that to an initializing expression of the struct type, and then reinterprets it as an initializing expression of `Dest` (which is layout-compatible with the struct type by construction). Note that some fields of an object may be initialized directly by the evaluation of the source expression, while others may be initialized by the conversions described here. The conversions initialize fields in their declaration order, but the evaluation of the source expression always happens before any of the conversions, and happens in the source expression's lexical order, so the fields of an object are not necessarily initialized in declaration order. Conversions between tuple types are defined in the same way, treating tuples as structs that have fields named `.0`, `.1`, etc, in numerical order. There is a conversion to `array(T, N)` from any expression with a tuple form of exactly `N` elements, whose type components are convertible to `T`. The conversion is an initializing expression, which type-converts each source element to `T`, and initializes the corresponding array element from the result of that conversion. #### Category conversions _Form composition_ converts an expression of composite form with consistent category to a primitive form as follows (where `min` as applied to phases uses the ordering "runtime" < "symbolic" < "template"): - An expression of tuple form `(, , ... )` can be converted to a primitive form `<(T1, T2, ..., TN), C, min(P1, P2, ..., PN), (V1, V2, ... VN)>`. - An expression of struct form `{.a = , .b = , ... .z = }` can be converted to a primitive form `<{.a = Ta, .b = Tb, ... .z = Tz}, C, min(Pa, Pb, ... Pz), {.a = Va, .b = Vb, ... .z = Vz}>`. When `C` is "value", composition forms a value representation of the aggregate from value representations of the elements. When `C` is "initializing", it transforms initializing expressions for each element into a single initializing expression that initializes the whole aggregate. `C` cannot be a reference category, because an aggregate of references to independent objects can't be replaced by a reference to a single aggregate object in a single step. _Form decomposition_ is the inverse of form composition. It converts a primitive-form expression to a composite form as follows: - An expression with primitive form `<(T0, T1, ..., TN), C, P, V>` can be converted to a tuple form `(, , ... )`. - An expression with primitive form `<{.a = Ta, .b = Tb, ... .z = Tz}, C, P, V>` can be converted to a struct form `{.a = , .b = , ... .z = }`. The category `CC` of the resulting sub-forms is the same as `C`, with two exceptions: - If `C` is "durable entire reference", `CC` will be "durable non-entire reference", because the sub-forms don't refer to complete objects. This doesn't apply to ephemeral entire references, because in that case form decomposition implicitly ends the lifetime of the original aggregate, promoting its elements to complete objects with independent lifetimes. - If `C` is "initializing", the original expression is materialized before it is decomposed, so `CC` will be "ephemeral entire reference". By convention, form decomposition is a no-op when applied to an expression with struct or tuple form. _Category conversion_ converts an expression to have a given category component without changing its type. The conversion works by combining form composition and decomposition with primitive category conversions, and is defined recursively: - If the target category component is a tuple, the source form must have a tuple type with the same arity. Convert the source to a tuple form by form decomposition, and then category-convert each source sub-form to the corresponding target sub-category. - If the target category component is a struct, the source form must have a struct type with the same set of field names in the same order. Convert the source to a struct form by form decomposition, and then category-convert each source sub-form to the corresponding target sub-category. - If the target category is a primitive category `C`: - If the source form is primitive, convert to `C` by applying primitive category conversions. - If the source form is composite and `C` is a reference category, category-convert the source form to "initializing", and then convert the result to `C` by applying primitive category conversions. - If the source form is composite and `C` is not a reference category, category-convert each source sub-form to `C`, and then convert the aggregate result of these conversions to `C` by form composition. ## Pointers Pointers in Carbon are the primary mechanism for _indirect access_ to storage containing some value. Dereferencing a pointer is one of the primary ways to form a [_durable reference expression_](#durable-reference-expressions). Carbon pointers are heavily restricted compared to C++ pointers -- they cannot be null and they cannot be indexed or have pointer arithmetic performed on them. In some ways, this makes them more similar to references in C++, but they retain the essential aspect of a pointer that they syntactically distinguish between the point*er* and the point*ee*. Carbon will still have mechanisms to achieve the equivalent behaviors as C++ pointers. Optional pointers are expected to serve nullable use cases. Slice or view style types are expected to provide access to indexable regions. And even raw pointer arithmetic is expected to be provided at some point, but through specialized constructs given the specialized nature of these operations. **Future work:** Add explicit designs for these use cases and link to them here. ### Reference types TODO: This section needs to be updated to reflect [proposal #5434](/proposals/p5434.md). Unlike C++, Carbon does not currently have reference types. The only form of indirect access are pointers. There are a few aspects to this decision that need to be separated carefully from each other as the motivations and considerations are different. First, Carbon has only a single fundamental construct for indirection because this gives it a single point that needs extension and configuration if and when we want to add more powerful controls to the indirect type system such as lifetime annotations or other safety or optimization mechanisms. The designs attempts to identify a single, core indirection tool and then layer other related use cases on top. This is motivated by keeping the language scalable as it evolves and reducing the huge explosion of complexity that C++ sees due to having a large space here. For example, when there are N > 1 ways to express indirection equivalently and APIs want to accept any one of them across M different parameters they can end up with N \* M combinations. Second, with pointers, Carbon's indirection mechanism retains the ability to refer distinctly to the point*er* and the point*ee* when needed. This ends up critical for supporting rebinding and so without this property more permutations of indirection would likely emerge. Third, Carbon doesn't provide a straightforward way to avoid the syntactic distinction between indirect access and direct access. For a full discussion of the tradeoffs of these design decisions, see the alternatives considered section of [P2006]: - [References in addition to pointers](/proposals/p2006.md#references-in-addition-to-pointers) - [Syntax-free or automatic dereferencing](/proposals/p2006.md#syntax-free-or-automatic-dereferencing) - [Exclusively using references](/proposals/p2006.md#exclusively-using-references) ### Pointer syntax The type of a pointer to a type `T` is written with a postfix `*` as in `T*`. Dereferencing a pointer is a [_reference expression_] and is written with a prefix `*` as in `*p`: ```carbon var i: i32 = 42; var p: i32* = &i; // Form a reference expression `*p` and assign `13` to the referenced storage. *p = 13; ``` This syntax is chosen specifically to remain as similar as possible to C++ pointer types as they are commonly written in code and are expected to be extremely common and a key anchor of syntactic similarity between the languages. The different alternatives and tradeoffs for this syntax issue were discussed extensively in [#523] and are summarized in the [proposal](/proposals/p2006.md#alternative-pointer-syntaxes). [#523]: https://github.com/carbon-language/carbon-lang/issues/523 Carbon also supports an infix `->` operation, much like C++. However, Carbon directly defines this as an exact rewrite to `*` and `.` so that `p->member` becomes `(*p).member` for example. This means there is no overloaded or customizable `->` operator in Carbon the way there is in C++. Instead, customizing the behavior of `*p` in turn customizes the behavior of `p->`. **Future work:** As [#523] discusses, one of the primary challenges of the C++ syntax is the composition of a prefix dereference operation and other postfix or infix operations, especially when chained together such as a classic C++ frustrations of mixes of dereference and indexing: `(*(*p)[42])[13]`. Where these compositions are sufficiently common to create ergonomic problems, the plan is to introduce custom syntax analogous to `->` that rewrites down to the grouped dereference. However, nothing beyond `->` itself is currently provided. Extending this, including the exact design and scope of extension desired, is a future work area. ### Dereferencing customization Carbon should support user-defined pointer-like types such as _smart pointers_ using a similar pattern as operator overloading or other expression syntax. That is, it should rewrite the expression into a member function call on an interface. Types can then implement this interface to expose pointer-like _user-defined dereference_ syntax. The interface might look like: ```carbon interface Pointer { let ValueT:! Type; fn Dereference[self: Self]() -> ValueT*; } ``` Here is an example using a hypothetical `TaggedPtr` that carries some extra integer tag next to the pointer it emulates: ```carbon class TaggedPtr(T:! Type) { var tag: Int32; var ptr: T*; } external impl [T:! Type] TaggedPtr(T) as Pointer { let ValueT:! T; fn Dereference[self: Self]() -> T* { return self.ptr; } } fn Test(arg: TaggedPtr(T), dest: TaggedPtr(TaggedPtr(T))) { **dest = *arg; *dest = arg; } ``` There is one tricky aspect of this. The function in the interface which implements a pointer-like dereference must return a raw pointer which the language then actually dereferences to form a reference expression similar to that formed by `var` declarations. This interface is implemented for normal pointers as a no-op: ```carbon impl [T:! Type] T* as Pointer { let ValueT:! Type = T; fn Dereference[self: Self]() -> T* { return self; } } ``` Dereference expressions such as `*x` are syntactically rewritten to use this interface to get a raw pointer and then that raw pointer is dereferenced. If we imagine this language level dereference to form a reference expression as a unary `deref` operator, then `(*x)` becomes `(deref (x.(Pointer.Dereference)()))`. Carbon will also use a simple syntactic rewrite for implementing `x->Method()` as `(*x).Method()` without separate or different customization. ## `const`-qualified types Carbon provides the ability to qualify a type `T` with the keyword `const` to get a `const`-qualified type: `const T`. This is exclusively an API-subsetting feature in Carbon -- for more fundamentally "immutable" use cases, value expressions and bindings should be used instead. Pointers to `const`-qualified types in Carbon provide access to an object with an API subset that can help model important requirements like ensuring usage is exclusively by way of a _thread-safe_ interface subset of an otherwise _thread-compatible_ type. Note that `const T` is a type qualification and is generally orthogonal to expression categories or what form of pattern is used, including for object parameters. Notionally, it can occur both with `ref` and value object parameters. However, on value patterns, it is redundant as there is no meaningful distinction between a value expression of type `T` and type `const T`. For example, given a type and methods: ```carbon class X { fn Method[self: Self](); fn ConstMethod[self: const Self](); fn RefMethod[ref self: Self](); fn RefConstMethod[ref self: const Self](); } ``` The methods can be called on different kinds of expressions according to the following table: | Expression category: | `let x: X`
(value) | `let x: const X`
(const value) | `var x: X`
(reference) | `var x: const X`
(const reference) | | -------------------: | ------------------------ | ------------------------------------ | ---------------------------- | ---------------------------------------- | | `x.Method();` | ✅ | ✅ | ✅ | ✅ | | `x.ConstMethod();` | ✅ | ✅ | ✅ | ✅ | | `x.RefMethod();` | ❌ | ❌ | ✅ | ❌ | | `x.RefConstMethod()` | ❌ | ❌ | ✅ | ✅ | The `const T` type has the same representation as `T` with the same field names, but all of its field types are also `const`-qualified. Other than fields, all other members `T` are also members of `const T`, and impl lookup ignores the `const` qualification. There is an implicit conversion from `T` to `const T`, but not the reverse. Conversion of reference expressions to value expressions is defined in terms of `const T` reference expressions to `T` value expressions. It is expected that `const T` will largely occur as part of a [pointer](#pointers), as the express purpose is to form reference expressions. The precedence rules are even designed for this common case, `const T*` means `(const T)*`, or a pointer-to-const. Carbon will support conversions between pointers to `const`-qualified types that follow the same rules as used in C++ to avoid inadvertent loss of `const`-qualification. The syntax details of `const` are also covered in the [type operators](/docs/design/expressions/type_operators.md) documentation. ## Lifetime overloading One potential use case that is not obviously or fully addressed by these designs in Carbon is overloading function calls by observing the lifetime of arguments. The use case here would be selecting different implementation strategies for the same function or operation based on whether an argument lifetime happens to be ending and viable to move-from. Carbon currently intentionally leaves this use case unaddressed. There is a fundamental scaling problem in this style of overloading: it creates a combinatorial explosion of possible overloads similar to other permutations of indirection models. Consider a function with N parameters that would benefit from lifetime overloading. If each parameter benefits _independently_ from the others, as is commonly the case, we would need 2N overloads to express all the possibilities. Carbon will initially see if code can be designed without this facility. Some of the tools needed to avoid it are suggested above such as the [consuming](#consuming-function-parameters) input pattern. But it is possible that more will be needed in practice. It would be good to identify the specific and realistic Carbon code patterns that cannot be expressed with the tools in this proposal in order to motivate a minimal extension. Some candidates based on functionality already proposed here or for [classes](/docs/design/classes.md): - Allow overloading between `ref self` and `self` in methods. This is among the most appealing as it _doesn't_ have the combinatorial explosion. But it is also very limited as it only applies to the implicit object parameter. - Allow overloading between `var` and non-`var` parameters. - Allow overloading between `ref` and non-`ref` parameters in general. Perhaps more options will emerge as well. Again, the goal isn't to completely preclude pursuing this direction, but instead to try to ensure it is only pursued based on a real and concrete need, and the minimal extension is adopted. ## Value representation and customization The representation of a value expression is especially important because it forms the calling convention used for the vast majority of function parameters -- function inputs. Given this importance, it's important that it is predictable and customizable by the value's type. Similarly, while Carbon code must be correct with either a copy or a reference-based implementation, we want which implementation strategy is used to be a predictable and customizable property of the type of a value. A type can optionally control its value representation using a custom syntax similar to customizing its [destructor](/docs/design/classes.md#destructors). This syntax sets the representation to some type uses a keyword `value_rep` and can appear where a member declaration would be valid within the type: ```carbon class SomeType { value_rep = RepresentationType; } ``` **Open question:** The syntax for this is just placeholder, using a placeholder keyword. It isn't final at all and likely will need to change to read well. The provided representation type must be one of the following: - `const Self` -- this forces the use of a _copy_ of the object. - `const ref` -- this forces the use of a [_pointer_](#pointers) to the original object, but with the `const` API subset. - A custom type that is not `Self`, `const Self`, or a pointer to either. If the representation is `const Self` or `const ref`, then the type fields will be accessible as [_value expressions_](#value-expressions) using the normal member access syntax for value expressions of a type. These will be implemented by either accessing a copy of the object in the non-pointer case or a pointer to the original object in the pointer case. A representation of `const Self` requires copying to be valid for the type. This provides the builtin functionality but allows explicitly controlling which representation should be used. If no customization is provided, the implementation will select one based on a set of heuristics. Some examples: - Non-copyable types and polymorphic types would use a `const ref`. - Small objects that are trivially copied in a machine register would use `const Self`. When a custom type is provided, it must not be `Self`, `const Self`, or a pointer to either. The type provided will be used on function call boundaries and as the implementation representation for value bindings and other value expressions referencing an object of the type. A specifier of `value_rep = T;` will require that the type containing that specifier satisfies the constraint `impls ReferenceImplicitAs where .T = T` using the following interface: ```carbon interface ReferenceImplicitAs { let T:! type; fn Convert[ref self: const Self]() -> T; } ``` Converting a reference expression into a value expression for such a type calls this customization point to form a representation object from the original reference expression. When using a custom representation type in this way, no fields are accessible through a value expression. Instead, only methods can be called using member access, as they simply bind the value expression to the `self` parameter. However, one important method can be called -- `.(ImplicitAs(T).Convert)()`. This implicitly converting a value expression for the type into its custom representation type. The customization of the representation above and `impls ReferenceImplicitAs where .T = T` causes the class to have a builtin `impl as ImplicitAs(T)` which converts to the representation type as a no-op, exposing the object created by calling `ReferenceImplicitAs.Convert` on the original reference expression, and preserved as a representation of the value expression. Here is a more complete example of code using these features: ```carbon class StringView { private var data_ptr: Char*; private var size: i64; fn Make(data_ptr: Char*, size: i64) -> StringView { return {.data_ptr = data_ptr, .size = size}; } // A typical readonly view of a string API... fn ExampleMethod[self: Self]() { ... } } class String { // Customize the value representation to be `StringView`. value_rep = StringView; private var data_ptr: Char*; private var size: i64; private var capacity: i64; impl as ReferenceImplicitAs where .T = StringView { fn Op[ref self: const Self]() -> StringView { // Because this is called on the String object prior to it becoming // a value, we can access an SSO buffer or other interior pointers // of `self`. return StringView.Make(self.data_ptr, self.size); } } // We can directly declare methods that take `self` as a `StringView` which // will cause the caller to implicitly convert value expressions to // `StringView` prior to calling. fn ExampleMethod[self: StringView]() { self.ExampleMethod(); } // Or we can use a value binding for `self` much like normal, but the // implementation will be constrained because of the custom value rep. fn ExampleMethod2[self: String]() { // Error due to custom value rep: self.data_ptr; // Fine, this uses the builtin `ImplicitAs(StringView)`. (self as StringView).ExampleMethod(); } // Note that even though the `Self` type is `const` qualified here, this // cannot be called on a `String` value! That would require us to convert to a // `StringView` that does not track the extra data member. fn Capacity[ref self: const Self]() -> i64 { return self.capacity; } } ``` It is important to note that the _representation_ type of a value expression is just its representation and does not impact the name lookup or type. Name lookup and `impl` search occur for the same type regardless of the expression category. But once a particular method or function is selected, an implicit conversion can occur from the original type to the representation type as part of the parameter or receiver type. In fact, this conversion is the _only_ operation that can occur for a value whose type has a customized value representation. The example above also demonstrates the fundamental tradeoff made by customizing the value representation of a type in this way. While it provides a great deal of control, it may result in some surprising limitations. Above, a method that is classically available on a C++ `const std::string&` like querying the capacity cannot be implemented with the customized value representation because it loses access to this additional state. Carbon allows type authors to make an explicit choice about whether they want to work with a restricted API and leverage a custom value representation or not. **Open question:** Beyond the specific syntax used where we currently have a placeholder `value_rep = T;`, we need to explore exactly what the best relationship is with the customization point. For example, should this syntax immediately forward declare `impl as ReferenceImplicitAs where .T = T`, thereby allowing an out-of-line definition of the `Convert` method and `... where _` to pick up the associated constant from the syntax. Alternatively, the syntactic marker might be integrated into the `impl` declaration for `ReferenceImplicitAs` itself. ## Alternatives considered - [No `var` introducer keyword](/proposals/p0339.md#no-var-introducer-keyword) - [Name of the `var` statement introducer](/proposals/p0339.md#name-of-the-var-statement-introducer) - [Colon between type and identifier](/proposals/p0339.md#colon-between-type-and-identifier) - [Type elision](/proposals/p0339.md#type-elision) - [Type ordering](/proposals/p0618.md#type-ordering) - [Elide the type instead of using `auto`](/proposals/p0851.md#elide-the-type-instead-of-using-auto) - [Value expression escape hatches](/proposals/p2006.md#value-expression-escape-hatches) - [References in addition to pointers](/proposals/p2006.md#references-in-addition-to-pointers) - [Syntax-free or automatic dereferencing](/proposals/p2006.md#syntax-free-or-automatic-dereferencing) - [Exclusively using references](/proposals/p2006.md#exclusively-using-references) - [Alternative pointer syntaxes](/proposals/p2006.md#alternative-pointer-syntaxes) - [Alternative syntaxes for locals](/proposals/p2006.md#alternative-syntaxes-for-locals) - [Mixed expression categories](/proposals/p5545.md#mixed-expression-categories) - [Don't implicitly convert to less-primitive forms](/proposals/p5545.md#dont-implicitly-convert-to-less-primitive-forms) ## References - [Proposal #257: Initialization of memory and values][p0257] - [Proposal #339: `var` statement][p0339] - [Proposal #618: `var` ordering][p0618] - [Proposal #851: auto keyword for vars][p0851] - [Proposal #2006: Values, variables, and pointers][p2006] - [Proposal #5545: Expression form basics][p5545] [p0257]: /proposals/p0257.md [p0339]: /proposals/p0339.md [p0618]: /proposals/p0618.md [p0851]: /proposals/p0851.md [p2006]: /proposals/p2006.md [p5545]: /proposals/p5545.md ================================================ FILE: docs/design/variadics.md ================================================ # Variadics ## Table of contents - [Basics](#basics) - [Overview](#overview) - [Packs and each-names](#packs-and-each-names) - [Pack expansions](#pack-expansions) - [Pack expansion expressions and statements](#pack-expansion-expressions-and-statements) - [Pack expansion patterns](#pack-expansion-patterns) - [Additional examples](#additional-examples) - [Execution Semantics](#execution-semantics) - [Expressions and statements](#expressions-and-statements) - [Pattern matching](#pattern-matching) - [Typechecking](#typechecking) - [Tuples, packs, segments, and shapes](#tuples-packs-segments-and-shapes) - [Iterative typechecking of pack expansions](#iterative-typechecking-of-pack-expansions) - [Typechecking patterns](#typechecking-patterns) - [Typechecking pattern matches](#typechecking-pattern-matches) - [Appendix: Type system formalism](#appendix-type-system-formalism) - [Explicit deduced arities](#explicit-deduced-arities) - [Typing and shaping rules](#typing-and-shaping-rules) - [Reduction rules](#reduction-rules) - [Equivalence, equality, and convertibility](#equivalence-equality-and-convertibility) - [Pattern match typechecking algorithm](#pattern-match-typechecking-algorithm) - [Canonicalization algorithm](#canonicalization-algorithm) - [Alternatives considered](#alternatives-considered) - [References](#references) ## Basics ### Overview A "pack expansion" is a syntactic unit beginning with `...`, which is a kind of compile-time loop over sequences called "packs". Packs are initialized and referred to using "each-names", which are marked with the `each` keyword at the point of declaration and the point of use. The syntax and behavior of a pack expansion depends on its context, and in some cases on a keyword following the `...`: - In a tuple literal expression (such as a function call argument list), `...` iteratively evaluates its operand expression, and treats the values as successive elements of the tuple. - `...and` and `...or` iteratively evaluate a boolean expression, combining the values using `and` and `or`. Normal short-circuiting behavior for the resulting `and` and `or` operators applies at runtime. - In a statement context, `...` iteratively executes a statement. - In a tuple literal pattern (such as a function parameter list), `...` iteratively matches the elements of the scrutinee tuple. In conjunction with pack bindings, this enables functions to take an arbitrary number of arguments. This example illustrates many of the key concepts: ```carbon // Takes an arbitrary number of vectors with arbitrary element types, and // returns a vector of tuples where the i'th element of the vector is // a tuple of the i'th elements of the input vectors. fn Zip[... each ElementType:! type] (... each vector: Vector(each ElementType)) -> Vector((... each ElementType)) { ... var each iter: auto = each vector.Begin(); var result: Vector((... each ElementType)); while (...and each iter != each vector.End()) { result.push_back((... each iter)); ... each iter++; } return result; } ``` ### Packs and each-names A _pack_ is a sequence of a fixed number of values called "elements", which may be of different types. Packs are very similar to tuple values in many ways, but they are not first-class values -- in particular, no run-time expression evaluates to a pack. The _arity_ of a pack is a compile-time value representing the number of values in the sequence. An _each-name_ consists of the keyword `each` followed by the name of a pack, and can only occur inside a pack expansion. On the Nth iteration of the pack expansion, an each-name refers to the Nth element of the named pack. As a result, a binding pattern with an each-name, such as `each ElementType:! type`, acts as a declaration of all the elements of the named pack, and thereby implicitly acts as a declaration of the pack itself. Note that `each` is part of the name syntax, not an expression operator, so it binds more tightly than any expression syntax. For example, the loop condition `...and each iter != each vector.End()` in the implementation of `Zip` is equivalent to `...and (each iter) != (each vector).End()`. ### Pack expansions A _pack expansion_ is an instance of one of the following syntactic forms: - A statement of the form "`...` _statement_". - A tuple expression element of the form "`...` _expression_", with the same precedence as `,`. - A tuple pattern element of the form "`...` _pattern_", with the same precedence as `,`. - An implicit parameter list element of the form "`...` _pattern_", with the same precedence as `,`. - An expression of the form "`...` `and` _expression_" or "`...` `or` _expression_", with the same precedence as `and` and `or`. The statement, expression, or pattern following the `...` (and the `and`/`or`, if present) is called the _body_ of the expansion. The `...` token can also occur in a tuple expression element of the form "`...` `expand` _expression_", with the same precedence as `,`. However, that syntax is not considered a pack expansion, and has its own semantics: _expression_ must have a tuple type, and "`...` `expand` _expression_" evaluates _expression_ and treats its elements as elements of the enclosing tuple literal. This is especially useful for using non-literal tuple values as function call arguments: ```carbon fn F(x: i32, y: String); fn MakeArgs() -> (i32, String); F(...expand MakeArgs()); ``` `...and`, `...or`, and `...expand` can be trivially distinguished with one token of lookahead, and the other meanings of `...` can be distinguished from each other by the context they appear in. As a corollary, if the nearest enclosing delimiters around a `...` are parentheses, they will be interpreted as forming a tuple rather than as grouping. Thus, expressions like `(... each ElementType)` in the above example are tuple literals, even though they don't contain commas. By convention, `...` is always followed by whitespace, except that `...and`, `...or`, and `...expand` are written with no whitespace between the two tokens. This serves to emphasize that the keyword is not part of the expansion body, but rather a modifier on the syntax and semantics of `...`. All each-names in a given expansion must refer to packs with the same arity, which we will also refer to as the arity of the expansion. If an expansion contains no each-names, it must be a pattern, or an expression in the type position of a binding pattern, and its arity is deduced from the scrutinee. A pack expansion or `...expand` expression cannot contain another pack expansion or `...expand` expression. An each-name cannot be used in the same pack expansion that declares it. In most if not all cases, an each-name that violates this rule can be changed to an ordinary name, because each-names are only necessary when you need to transfer a pack from one pack expansion to another. #### Pack expansion expressions and statements A pack expansion expression or statement can be thought of as a kind of loop that executes at compile time (specifically, monomorphization time), where the expansion body is implicitly parameterized by an integer value called the _pack index_, which ranges from 0 to one less than the arity of the expansion. The pack index is implicitly used as an index into the packs referred to by each-names. This is easiest to see with statement pack expansions. For example, if `a`, `x`, and `y` are packs with arity 3, then `... each a += each x * each y;` is roughly equivalent to ```carbon a[:0:] += x[:0:] * y[:0:]; a[:1:] += x[:1:] * y[:1:]; a[:2:] += x[:2:] * y[:2:]; ``` Here we are using `[:N:]` as a hypothetical pack indexing operator for purposes of illustration; packs cannot actually be indexed in Carbon code. > **Future work:** We're open to eventually adding indexing of variadics, but > that remains future work and will need its own proposal. `...and` and `...or` behave like chains of the corresponding boolean operator, so `...and F(each x, each y)` behaves like `true and F(x[:0:], y[:0:]) and F(x[:1:], y[:1:]) and F(x[:2:], y[:2:])`. They can also be interpreted as looping constructs, although the rewrite is less straightforward because Carbon doesn't have a way to write a loop in an expression context. An expression like `...and F(each x, each y)` can be thought of as evaluating to the value of `result` after executing the following code fragment: ``` var result: bool = true; for (let i:! i32 in (0, 1, 2)) { result = result && F(x[:i:], y[:i:]); if (result == false) { break; } } ``` `...` in a tuple literal behaves like a series of comma-separated tuple elements, so `(... F(each x, each y))` is equivalent to `(F(x[:0:], y[:0:]), F(x[:1:], y[:1:]), F(x[:2:], y[:2:]))`. This can't be expressed as a loop in Carbon code, but it is still fundamentally iterative. #### Pack expansion patterns A pack expansion pattern "`...` _subpattern_" appears as part of a tuple pattern (or an implicit parameter list), and matches a sequence of tuple elements if each element matches _subpattern_. For example, in the signature of `Zip` shown earlier, the parameter list consists of a single pack expansion pattern `... each vector: Vector(each ElementType)`, and so the entire argument list will be matched against the binding pattern `each vector: Vector(each ElementType)`. Since _subpattern_ will be matched against multiple scrutinees (or none) in a single pattern-matching operation, a binding pattern within a pack expansion pattern must declare an each-name (such as `each vector` in the `Zip` example), and the Nth iteration of the pack expansion will initialize the Nth element of the named pack from the Nth scrutinee. The binding pattern's type expression may contain an each-name (such as `each ElementType` in the `Zip` example), but if so, it must be a deduced parameter of the enclosing pattern. > **Future work:** That restriction can probably be relaxed, but we currently > don't have motivating use cases to constrain the design. ### Additional examples ```carbon // Computes the sum of its arguments, which are i64s fn SumInts(... each param: i64) -> i64 { var sum: i64 = 0; ... sum += each param; return sum; } ``` ```carbon // Concatenates its arguments, which are all convertible to String fn StrCat[... each T:! ConvertibleToString](... each param: each T) -> String { var len: i64 = 0; ... len += each param.Length(); var result: String = ""; result.Reserve(len); ... result.Append(each param.ToString()); return result; } ``` ```carbon // Returns the minimum of its arguments, which must all have the same type T. fn Min[T:! Comparable & Value](first: T, ... each next: T) -> T { var result: T = first; ... if (each next < result) { result = each next; } return result; } ``` ```carbon // Invokes f, with the tuple `args` as its arguments. fn Apply[... each T:! type, F:! CallableWith(... each T)] (f: F, args: (... each T)) -> auto { return f(...expand args); } ``` ```carbon // Toy example of mixing variadic and non-variadic parameters. // Takes an i64, any number of f64s, and then another i64. fn MiddleVariadic(first: i64, ... each middle: f64, last: i64); ``` ```carbon // Toy example of using the result of variadic type deduction. fn TupleConcat[... each T1: type, ... each T2: type]( t1: (... each T1), t2: (... each T2)) -> (... each T1, ... each T2) { return (...expand t1, ...expand t2); } ``` ## Execution Semantics ### Expressions and statements In all of the following, N is the arity of the pack expansion being discussed, and `$I` is a notional variable representing the pack index. These semantics are implemented at monomorphization time, so the value of N is a known integer constant. Although the value of `$I` can vary during execution, it is nevertheless treated as a constant. A statement of the form "`...` _statement_" is evaluated by executing _statement_ N times, with `$I` ranging from 0 to N - 1. An expression of the form "`...and` _expression_" is evaluated as follows: a notional `bool` variable `$R` is initialized to `true`, and then "`$R = $R and` _expression_" is executed up to N times, with `$I` ranging from 0 to N - 1. If at any point `$R` becomes false, this iteration is terminated early. The final value of `$R` is the value of the expression. An expression of the form "`...or` _expression_" is evaluated the same way, but with `or` in place of `and`, and `true` and `false` transposed. A tuple expression element of the form "`...` _expression_" evaluates to a sequence of N values, where the k'th value is the value of _operand_ where `$I` is equal to k - 1. An each-name evaluates to the `$I`th value of the pack it refers to (indexed from zero). ### Pattern matching The semantics of pack expansion patterns are chosen to follow the general principle that pattern matching is the inverse of expression evaluation, so for example if the pattern `(... each x: auto)` matches some scrutinee value `s`, the expression `(... each x)` should be equal to `s`. These semantics are implemented at monomorphization time, so all types are known constants, and all all arities are known. A tuple pattern can contain no more than one subpattern of the form "`...` _operand_". When such a subpattern is present, the N elements of the pattern before the `...` expansion are matched with the first N elements of the scrutinee, and the M elements of the pattern after the `...` expansion are matched with the last M elements of the scrutinee. If the scrutinee does not have at least N + M elements, the pattern does not match. The remaining elements of the scrutinee are iteratively matched against _operand_, in order. In each iteration, `$I` is equal to the index of the scrutinee element being matched, minus N. On the Nth iteration, a binding pattern binds the Nth element of the named pack to the Nth scrutinee value. ## Typechecking ### Tuples, packs, segments, and shapes In order to discuss the underlying type system for variadics, we will need to introduce some pseudo-syntax to represent values and expressions that occur in the type system, but cannot be expressed directly in user code. We will use non-ASCII glyphs such as `«»‖⟬⟭` for that pseudo-syntax, to distinguish it from valid Carbon syntax. In the context of variadics, we will say that a tuple literal consists of a comma-separated sequence of _segments_, and reserve the term "elements" for the components of a tuple literal after pack expansion. For example, the expression `(... each foo)` may evaluate to a tuple value with any number of elements, but the expression itself has exactly one segment. Each segment has a type, which expresses (potentially symbolically) both the types of the elements of the segment and the arity of the segment. The type of a tuple literal is a tuple literal of the types of its segments. For example, suppose we are trying to find the type of `z` in this code: ```carbon fn F[... each T:! type]((... each x: Optional(each T)), (... each y: i32)) { let z: auto = (0 as f32, ... each x, ... each y); } ``` We proceed by finding the type of each segment. The type of `0 as f32` is `f32`, by the usual non-variadic typing rules. The type of `... each x` is `... Optional(each T)`, because `Optional(each T)` is the declared type of `each x`, and the type of a pack expansion is a pack expansion of the type of its body. The type of `... each y` is more complicated. Conceptually, it consists of some number of repetitions of `i32`. We don't know exactly how many repetitions, because it's implicitly specified by the caller: it's the arity of the second argument tuple. Effectively, that arity acts as a hidden deduced parameter of `F`. So to represent this type, we need two new pseudo-syntaxes: - `‖each X‖` refers to the deduced arity of the pack expansion that contains the declaration of `each X`. - `«E; N»` evaluates to `N` repetitions of `E`. This is called a _arity coercion_, because it coerces the expression `E` to have arity `N`. `E` must not contain any pack expansions, each-names, or pack literals (see below). Combining the two, the type of `... each y` is `... «i32; ‖each y‖»`. Thus, the type of `z` is `(f32, ... Optional(each T), ... «i32; ‖each y‖»)`. Now, consider a modified version of that example: ```carbon fn F[... each T:! type]((... each x: Optional(each T)), (... each y: i32)) { let (... each z: auto) = (0 as f32, ... each x, ... each y); } ``` `each z` is a pack, but it has the same elements as the tuple `z` in our earlier example, so we represent its type in the same way, as a sequence of segments: `⟬f32, Optional(each T), «i32; ‖each y‖»⟭`. The `⟬⟭` delimiters make this a _pack literal_ rather than a tuple literal. Notice one subtle difference: the segments of a pack literal do not contain `...`. In effect, every segment of a pack literal acts as a separate loop body. As with the tuple literal syntax, the pack literal pseudo-syntax can also be used in patterns. The _shape_ of a pack literal is a tuple of the arities of its segments, so the shape of `⟬f32, Optional(each T), «i32; ‖each y‖»⟭` is `(1, ‖each T‖, ‖each y‖)`. Other expressions and patterns also have shapes. In particular, the shape of an arity coercion `«E; A»` is `(A,)`, the shape of `each X` is `(‖each X‖,)`, and the shape of an expression that does not contain pack literals, shape coercions, or each-names is `(1,)`. The arity of an expression is the sum of the elements of its shape. See the [appendix](#typing-and-shaping-rules) for the full rules for determining the shape of an expression. If a pack literal is part of some enclosing expression that doesn't contain `...`, it can be _expanded_, which moves the outer expression inside the pack literal. For example, `... Optional(⟬each X, Y⟭)` is equivalent to `... ⟬Optional(each X), Optional(Y)⟭`. Similarly, an arity coercion can be expanded so long as the parent node is not `...`, a pattern, or a pack literal. See the [appendix](#reduction-rules) for the full rules governing this operation. _Fully expanding_ an expression or pattern that does not contain a pack expansion means repeatedly expanding any pack literals and arity coercions within it, until they cannot be expanded any further. The _scalar components_ of a fully-expanded expression `E` are a set, defined as follows: - If `E` is a pack literal, its scalar components are the union of the scalar components of the segments. - If `E` is an arity coercion `«F; S»`, the only scalar component of `E` is `F`. - Otherwise, the only scalar component of `E` is `E`. The scalar components of any other expression that does not contain `...` are the scalar components of its fully expanded form. By construction, a segment of a pack literal never has more than one scalar component. Also by construction, a scalar component cannot contain a pack literal, pack expansion, or arity coercion, but it can contain each-names, so we can operate on it using the ordinary rules of non-variadic expressions so long as we treat the names as opaque. ### Iterative typechecking of pack expansions Since the execution semantics of an expansion are defined in terms of a notional rewritten form where we simultaneously iterate over each-names, in principle we can typecheck the expansion by typechecking the rewritten form. However, the rewritten form usually would not typecheck as ordinary Carbon code, because the each-names can have different types on different iterations. Furthermore, the difference in types can propagate through expressions: if `each x` and `each y` can have different types on different iterations, then so can `each x * each y`. In effect, we have to typecheck the loop body separately for each iteration. However, at typechecking time we usually don't even know how many iterations there will be, much less what type an each-name will have on any particular iteration, because the types of the each-names are packs, which are sequences of segments, not sequences of elements. To solve that problem, we require that the types of all each-names in a pack expansion must have the same shape. This enables us to typecheck the pack expansion by simultaneously iterating over segments instead of input elements. As a result, the type of an expression or pattern within a pack expansion is a sequence of segments, or in other words a _pack_, representing the types it takes on over the course of the iteration. Note, however, that even though such an expression has a pack type, it does not evaluate to a pack value. Rather, it evaluates to a sequence of non-pack values over the course of the pack expansion loop, and its pack type summarizes the types of that sequence. Within a given iteration, typechecking follows the usual rules of non-variadic typechecking, except that when we need the type of an each-name, we use the scalar component of the current segment of its type. As noted above, we can operate on a scalar component using the ordinary rules of non-variadic typechecking. Once the body of a pack expansion has been typechecked, typechecking the expansion itself is relatively straightforward: - A statement pack expansion requires no further typechecking, because statements don't have types. - An `...and` or `...or` expression has type `bool`, and every segment of the operand's type pack must have a type that's convertible to `bool`. - For a `...` tuple element expression or pattern, the segments of the operand's type pack become segments of the type of the enclosing tuple. > **TODO:** Discuss typechecking `...expand`. ### Typechecking patterns A pack expansion pattern has _fixed arity_ if it contains at least one usage of an each-name that is not a parameter of the enclosing [full pattern](pattern_matching.md). Otherwise it has _deduced arity_. A tuple pattern can have at most one segment with deduced arity. For example: ```carbon class C(... each T:! type) { fn F[... each U:! type](... each t: each T, ... each u: each U); } ``` In the signature of `F`, `... each t: each T` has fixed arity, since the arity is determined by the arguments passed to `C`, before the call to `F`. On the other hand, `... each u: each U` has deduced arity, because the arity of `each U` is determined by the arguments passed to `F`. After typechecking a full pattern, we attempt to merge as many tuple segments as possible, in order to simplify the subsequent pattern matching. For example, consider the following function declaration: ```carbon fn Min[T:! type](first: T, ... each next: T) -> T; ``` During typechecking, we rewrite that function signature so that it only has one parameter: ```carbon fn Min[T:! type](... each args: «T; ‖each next‖+1») -> T; ``` (We represent the arity as `‖each next‖+1` to capture the fact that `each args` must match at least one element.) When the pattern is heterogeneous, the merging process may be more complex. For example: ```carbon fn ZipAtLeastOne[First:! type, ... each Next:! type] (first: Vector(First), ... each next: Vector(each Next)) -> Vector((First, ... each Next)); ``` During typechecking, we transform that function signature to the following form: ```carbon fn ZipAtLeastOne[... ⟬First, each Next⟭:! «type; ‖each next‖+1»] (... each __args: Vector(⟬First, each Next⟭)) -> Vector((... ⟬First, each Next⟭)); ``` We can then rewrite that by replacing the pack of names `⟬First, each Next⟭` with an invented name `each __Args`, so that the function has only one parameter: ```carbon fn ZipAtLeastOne[... each __Args:! «type; ‖each next‖+1»] (... each __args: Vector(each __Args)) -> Vector((... each __Args)); ``` We can replace a name pack with an invented each-name only if all of the following conditions hold: - The name pack doesn't use any name more than once. For example, we can't apply this rewrite to `⟬X, each Y, X⟭`. - The name pack contains exactly one each-name. For example, we can't apply this rewrite to `⟬X, Y⟭`. - The replacement removes all usages of the constituent names, including their declarations. For example, we can't apply this rewrite to `⟬X, each Y⟭` in this code, because the resulting signature would have return type `X` but no declaration of `X`: ```carbon fn F[... ⟬X, each Y⟭:! «type; ‖each next‖+1»] (... each __args: each ⟬X, each Y⟭) -> X; ``` - The pack expansions being rewritten do not contain any pack literals other than the name pack being replaced. For example, we can't apply this rewrite to `⟬X, each Y⟭` in this code, because the pack expansion in the deduced parameter list also contains the pack literal `⟬I, each type⟭`: ```carbon fn F[... ⟬X, each Y⟭:! ⟬I, each type⟭](... each __args: each ⟬X, each Y⟭); ``` Notice that as a corollary of this rule, all the names in the name pack must have the same type. See the [appendix](#pattern-match-typechecking-algorithm) for a more formal discussion of the rewriting process. ### Typechecking pattern matches To typecheck a pattern match between a tuple pattern and a tuple scrutinee, we try to split and merge the segments of the scrutinee type so that it has the same number of segments as the pattern type, and corresponding segments have the same arity. For example, consider this call to `ZipAtLeastOne` (as defined in the previous section): ```carbon fn F[... each T:! type](... each t: Vector(each T), u: Vector(i32)) { ZipAtLeastOne(... each t, u); } ``` The pattern type is `(... Vector(⟬First, each Next⟭))`, so we need to rewrite the scrutinee type `(... Vector(each T), Vector(i32))` to have a single tuple segment with an arity that matches `‖each Next‖+1`. We can do that by merging the scrutinee segments to obtain `(... ⟬Vector(each T), Vector(i32)⟭)`. This has a single segment with arity `‖each T‖+1`, which can match `‖each Next‖+1` because the deduced arity `‖each Next‖` behaves as a deduced parameter of the pattern, so they match by deducing `‖each Next‖ == ‖each T‖`. When merging segments of the scrutinee, we don't attempt to form name packs and replace them with invented names, but we also don't need to: we don't require a merged scrutinee segments to have a single scalar component. The search for this rewrite processes each pattern segment to the left of the segment with deduced arity, in order from left to right. For each pattern segment, it greedily merges unmatched scrutinee segments from left to right until their cumulative shape is greater than or equal to the shape of the pattern segment, and then splits off a scrutinee segment on the right if necessary to make the shapes exactly match. Pattern segments to the right of the segment with deduced arity are processed the same way, but with left and right reversed, so that segments are always processed from the outside in. See the [appendix](#appendix-type-system-formalism) for the rewrite rules that govern merging and splitting. Once we have the pattern and scrutinee segments in one-to-one correspondence, we check each scalar component of the scrutinee type against the scalar component of the corresponding pattern type segment (by construction, the pattern type segment has only one scalar component). Since we are checking scalar components against scalar components, this proceeds according to the usual rules of non-variadic typechecking. > **TODO:** Extend this approach to fall back to a complementary approach, where > the pattern and scrutinee trade roles: we maximally merge the scrutinee tuple, > while requiring each segment to have a single scalar component, and then > merge/split the pattern tuple to match it, without requiring pattern tuple > segments to have a single scalar component. This isn't quite symmetric with > the current approach, because when processing the scrutinee we can't merge > deduced parameters (scrutinees don't have any), but we can invent new `let` > bindings. ## Appendix: Type system formalism A _pack literal_ is a comma-separated sequence of segments, enclosed in `⟬⟭` delimiters. A pack literal can appear in an expression, pattern, or name context, and every segment must be valid in the context where the pack literal appears (for example, the segments of a pack literal in a name context must all be names). Pack literals cannot be nested, and cannot appear outside a pack expansion. ### Explicit deduced arities In this formalism, deduced arities are explicit rather than implicit, so Carbon code must be desugared into this formalism as follows: For each pack expansion pattern, we introduce a binding pattern `__N:! Arity` as a deduced parameter of the enclosing full pattern, where `__N` is a name chosen to avoid collisions. Then, for each binding pattern of the form `each X: T` within that expansion, if `T` does not contain an each-name, the binding pattern is rewritten as `each X: «T; __N»`. If this does not introduce any usages of `__N`, we remove its declaration. `Arity` is a compiler-internal type which represents non-negative integers. The only operation it supports is `+`, with non-negative integer literals and other `Arity`s. `Arity` is used only during type checking, so `+` has no run-time semantics, and its only symbolic semantics are that it is commutative and associative. ### Typing and shaping rules The shape of an AST node within a pack expansion is determined as follows: - The shape of an arity coercion is the value of the expression after the `;`. - The shape of a pack literal is the concatenation of the arities of its segments. - The shape of an each-name expression is the shape of the binding pattern that declared the name. - If a binding pattern's name and type components have the same number of segments, and each name segment is an each-name if and only if the corresponding type segment's shape is not 1, then the shape of the binding pattern is the shape of the type expression. Otherwise, the binding pattern is ill-shaped. - For any other AST node: - If all the node's children have shape 1, its shape is 1. - If there is some shape `S` such that all of the node's children have shape either 1 or `S`, its shape is `S`. - Otherwise, the node is ill-shaped. > **TODO:** The "well-shaped" rules as stated are slightly too restrictive. For > example, `⟬each X, Y⟭: «Z; N+1»` is well-shaped, and `(⟬each X, Y⟭, «Z; N+1»)` > is well-shaped if the shape of `each X` is `N`. The type of an expression or pattern can be computed as follows: - The type of `each x: auto` is `each __X`, a newly-invented deduced parameter of the enclosing full pattern, which behaves as if it was declared as `... each __X:! type`. - The type of an each-name expression is the type expression of the binding pattern that declared it. - The type of an arity coercion `«E; S»` is `«T; S»`, where `T` is the type of `E`. - The type of a pack literal is a pack literal consisting of the concatenated types of its segments. This concatenation flattens any nested pack literals (for example `⟬A, ⟬B, C⟭⟭` becomes `⟬A, B, C⟭`) - The type of a pack expansion expression or pattern is `...B`, where `B` is the type of its body. - The type of a tuple literal is a tuple literal consisting of the types of its segments. - If an expression or pattern `E` contains a pack literal or arity coercion that is not inside a pack expansion, the type of `E` is the type of the fully expanded form of `E`. > **TODO:** address `...expand`, `...and` and `...or`. ### Reduction rules Unless otherwise specified, all expressions in these rules must be free of side effects. Note that every reduction rule is also an equivalence: the utterance before the reduction is equivalent to the utterance after, so these rules can sometimes be run in reverse (particularly during deduction). Utterances that are reduced by these rules must be well-shaped (and the reduced form will likewise be well-shaped), but need not be well-typed. This enables us to apply these reductions while determining whether an utterance is well-typed, as in the case of typing an expression or pattern that contains a pack literal or arity coercion, above. _Singular pack removal:_ if `E` is a pack segment, `⟬E⟭` reduces to `E`. _Singular expansion removal:_ `...E` reduces to `E`, if the shape of `E` is `(1,)`. _Pack expansion splitting:_ If `E` is a segment and `S` is a sequence of segments, `...⟬E, S⟭` reduces to `...E, ...⟬S⟭`. _Pack expanding:_ If `F` is a function, `X` is an utterance that does not contain pack literals, each-names, or arity coercions, and `⟬P1, P2⟭` and `⟬Q1, Q2⟭` both have the shape `(S1, S2)`, then `F(⟬P1, P2⟭, X, ⟬Q1, Q2⟭, «Y; S1+S2»)` reduces to `⟬F(P1, X, Q1, «Y; S1»), F(P2, X, Q2, «Y; S2»)⟭`. This rule generalizes in several dimensions: - `F` can have any number of arity coercion and other non-pack-literal arguments, and any positive number of pack literal arguments, and they can be in any order. - The pack literal arguments can have any number of segments (but the well-shapedness requirement means they must have the same number of segments). - `F()` can be any expression syntax other than `...`, not just a function call. For example, this rule implies that `⟬X1, X2⟭ * ⟬Y1, Y2⟭` reduces to `⟬X1 * Y1, X2 * Y2⟭`, where the `*` operator plays the role of `F`. - `F()` can also a be a pattern syntax. For example, this rule implies that `(⟬x1: X1, x2: X2⟭, ⟬y1: Y1, y2: Y2⟭)` reduces to `⟬(x1: X1, y1: Y1), (x2: X2, y2: Y2)⟭`, where the tuple pattern syntax `( , )` plays the role of `F`. - When binding pattern syntax takes the role of `F`, the name part of the binding pattern must be a name pack. For example, `⟬x1, x2⟭: ⟬X1, X2⟭` reduces to `⟬x1: X1, x2: X2⟭`, but `each x: ⟬X1, X2⟭` cannot be reduced by this rule. _Coercion expanding:_ If `F` is a function, `S` is a shape, and `Y` is an expression that does not contain pack literals or arity coercions, `F(«X; S», Y, «Z; S»)` reduces to `«F(X, Y, Z); S»`. As with pack expanding, this rule generalizes: - `F` can have any number of non-arity-coercion arguments, and any positive number of arity coercion arguments, and they can be in any order. - `F()` can be any expression syntax other than `...` or pack literal formation, not just a function call. Unlike pack expanding, coercion expanding does not apply if `F` is a pattern syntax. _Coercion removal:_ `«E; 1»` reduces to `E`. _Tuple indexing:_ Let `I` be an integer template constant, let `X` be a tuple segment, and let `Ys` be a sequence of tuple segments. - If the arity `A` of `X` is less than `I+1`, then `(X, Ys).(I)` reduces to `(Ys).(I-A)`. - Otherwise: - If `X` is not a pack expansion, then `(X, Ys).(I)` reduces to `X`. - If `X` is of the form `...⟬«E; S»⟭`, then `(X, Ys).(I)` reduces to `E`. ### Equivalence, equality, and convertibility _Pack renaming:_ Let `Ns` be a sequence of names, let `⟬Ns⟭: «T; N»` be a name binding pattern (which may be a symbolic or template binding as well as a runtime binding), and let `__A` be an identifier that does not collide with any name that's visible where `⟬Ns⟭` is visible. We can rewrite all occurrences of `⟬Ns⟭` to `each __A` in the scope of the binding pattern (including the pattern itself) if all of the following conditions hold: - `Ns` contains at least one each-name. - No name in `Ns` is used in the scope outside of `Ns`. - No name occurs more than once in `Ns`. - No other pack literals occur in the same pack expansion as an occurrence of `⟬Ns⟭`. _Expansion convertibility:_ `...T` is convertible to `...U` if the arity of `U` equals the arity of `T`, and the scalar components of `T` are each convertible to all scalar components of `U`. _Shape equality:_ Let `(S1s)`, `(S2s)`, `(S3s)`, and `(S4s)` be shapes. `(S1s, S2s)` equals `(S3s, S4s)` if `(S1s)` equals `(S3s)` and `(S2s)` equals `(S4s)`. ### Pattern match typechecking algorithm A full pattern is in _normal form_ if it contains no pack literals, and every arity coercion is fully expanded. For example, `[__N:! Arity](... each x: Vector(«i32; __N»))` is not in normal form, but `[__N:! Arity](... each x: «Vector(i32); __N»)` is. Note that all user-written full patterns are in normal form. Note also that by construction, this means that the type of the body of every pack expansion has a single scalar component. The _canonical form_ of a full pattern is the unique normal form (if any) that is "maximally merged", meaning that every tuple pattern and tuple literal has the smallest number of segments. For example, the canonical form of `[__N:! Arity](... each x: «i32; __N», y: i32)` is `[__N:! Arity](... each __args: «i32; __N+1»)`. > **TODO:** Specify algorithm for converting a full pattern to canonical form, > or establishing that there is no such form. See next section for a start. If a function with type `F` is called with argument type `A`, we typecheck the call by converting `F` to canonical form, and then checking whether `A` is convertible to the parameter type by applying the deduction rules in the previous sections. If that succeeds, we apply the resulting binding map to the function return type to obtain the type of the call expression. > **TODO:** Specify the algorithm more precisely. In particular, discuss how to > rewrite `A` as needed to make the shapes line up, but don't rewrite `F` after > canonicalization. Typechecking for pattern match operations other than function calls is defined in terms of typechecking a function call: We check a scrutinee type `S` against a pattern `P` by checking `__F(S,)` against a hypothetical function signature `fn __F(P,)->();`. > **Future work:** Extend this approach to support merging the argument list as > well as the parameter list. #### Canonicalization algorithm The canonical form can be found by starting with a normal form, and incrementally merging an adjacent singular parameter type into the variadic parameter type. For example, consider the following function: ```carbon fn F[First:! type, Second:! type, ... each Next:! type] (first: Vector(First), second: Vector(Second), ... each next: Vector(each Next)) -> (First, Second, ... each Next); ``` First, we desugar the implicit arity: ```carbon fn F[__N:! Arity, First:! type, Second:! type, ... each Next:! «type; __N»] (first: Vector(First), second: Vector(Second), ... each next: Vector(each Next)) -> (First, Second, ... each Next); ``` Then we attempt to merge `Second` with `each Next` as follows (note that for brevity, some of the steps presented here actually contain multiple independent reductions): ```carbon // Singular pack removal (in reverse) fn F[__N:! Arity, First:! type, Second:! type, ... ⟬each Next:! «type; __N»⟭] (first: Vector(First), second: Vector(Second), ... each next: Vector(⟬each Next⟭)) -> (First, Second, ... ⟬each Next⟭); // Pack expanding fn F[__N:! Arity, First:! type, Second:! type, ... ⟬each Next:! «type; __N»⟭] (first: Vector(First), second: Vector(Second), ... each next: ⟬Vector(each Next)⟭) -> (First, Second, ... ⟬each Next⟭); // Pack expanding fn F[__N:! Arity, First:! type, Second:! type, ... ⟬each Next:! «type; __N»⟭] (first: Vector(First), second: Vector(Second), ... ⟬each next: Vector(each Next)⟭) -> (First, Second, ... ⟬each Next⟭); // Pack expansion splitting (in reverse) fn F[__N:! Arity, First:! type, ... ⟬Second:! type, each Next:! «type; __N»⟭] (first: Vector(First), ... ⟬second: Vector(Second), each next: Vector(each Next)⟭) -> (First, ... ⟬Second, each Next⟭); // Pack expanding (in reverse) fn F[__N:! Arity, First:! type, ... ⟬Second, each Next⟭:! «type; __N+1»] (first: Vector(First), ... ⟬second, each next⟭: ⟬Vector(Second), Vector(each Next)⟭) -> (First, ... ⟬Second, each Next⟭); // Pack expanding (in reverse) fn F[__N:! Arity, First:! type, ... ⟬Second, each Next⟭:! «type; __N+1»] (first: Vector(First), ... ⟬second, each next⟭: Vector(⟬Second, each Next⟭)) -> (First, ... ⟬Second, each Next⟭); // Pack renaming fn F[__N:! Arity, First:! type, ... each __A:! «type; __N+1»] (first: Vector(First), ... each __a: Vector(each __A)) -> (First, ... each __A); ``` This brings us back to a normal form, while reducing the number of tuple segments. We can now repeat that process to merge the remaining parameter type: ```carbon fn F[__N:! Arity, First:! type, ... ⟬each __A:! «type; __N+1»⟭] (first: Vector(First), ... each __a: Vector(⟬each __A⟭)) -> (First, ... ⟬each __A⟭); // Pack expanding fn F[__N:! Arity, First:! type, ... ⟬each __A:! «type; __N+1»⟭] (first: Vector(First), ... each __a: ⟬Vector(each __A)⟭) -> (First, ... ⟬each __A⟭); // Pack expanding fn F[__N:! Arity, First:! type, ... ⟬each __A:! «type; __N+1»⟭] (first: Vector(First), ... ⟬each __a: Vector(each __A)⟭) -> (First, ... ⟬each __A⟭); // Pack expansion splitting (in reverse) fn F[__N:! Arity, ... ⟬First:! type, each __A:! «type; __N+1»⟭] (... ⟬first: Vector(First), each __a: Vector(each __A)⟭) -> (... ⟬First, each __A⟭); // Pack expanding (in reverse) fn F[__N:! Arity, ... ⟬First, each __A⟭:! «type; __N+2»⟭] (... ⟬first, each __a⟭: ⟬Vector(First), Vector(each __A)⟭) -> (... ⟬First, each __A⟭); // Pack expanding (in reverse) fn F[__N:! Arity, ... ⟬First, each __A⟭:! «type; __N+2»⟭] (... ⟬first, each __a⟭: Vector(⟬First, each __A⟭)) -> (... ⟬First, each __A⟭); // Pack renaming fn F[__N:! Arity, ... __B:! «type; __N+2»⟭] (... __b: Vector(__B)) -> (... __B); ``` Here again, this is a normal form, and there is demonstrably no way to perform any further merging, so this must be the canonical form. > **TODO:** define the algorithm in more general terms, and discuss ways that > merging can fail. ## Alternatives considered - [Member packs](/proposals/p2240.md#member-packs) - [Single semantic model for pack expansions](/proposals/p2240.md#single-semantic-model-for-pack-expansions) - [Generalize `expand`](/proposals/p2240.md#generalize-expand) - [Omit `expand`](/proposals/p2240.md#omit-expand) - [Support expanding arrays](/proposals/p2240.md#support-expanding-arrays) - [Omit each-names](/proposals/p2240.md#omit-each-names) - [Disallow pack-type bindings](/proposals/p2240.md#disallow-pack-type-bindings) - [Fold expressions](/proposals/p2240.md#fold-expressions) - [Allow multiple pack expansions in a tuple pattern](/proposals/p2240.md#allow-multiple-pack-expansions-in-a-tuple-pattern) - [Allow nested pack expansions](/proposals/p2240.md#allow-nested-pack-expansions) - [Use postfix instead of prefix `...`](/proposals/p2240.md#use-postfix-instead-of-prefix-) - [Avoid context-sensitity in pack expansions](/proposals/p2240.md#avoid-context-sensitity-in-pack-expansions) - [Fold-like syntax](/proposals/p2240.md#fold-like-syntax) - [Variadic blocks](/proposals/p2240.md#variadic-blocks) - [Keyword syntax](/proposals/p2240.md#keyword-syntax) - [Require parentheses around `each`](/proposals/p2240.md#require-parentheses-around-each) - [Fused expansion tokens](/proposals/p2240.md#fused-expansion-tokens) - [No parameter merging](/proposals/p2240.md#no-parameter-merging) - [Exhaustive function call typechecking](/proposals/p2240.md#exhaustive-function-call-typechecking) ## References - Proposal [#2240: Variadics](https://github.com/carbon-language/carbon-lang/pull/2240) ================================================ FILE: docs/guides/README.md ================================================ # Guides This directory contains end-user documentation on how to use Carbon, focused on people trying to use and write code in Carbon. - [Glossary](glossary.md) ================================================ FILE: docs/guides/glossary.md ================================================ # Glossary ## entity An _entity_ is a named item with an associated name path, such as a function, type, interface, or namespace. For example, in `fn GetTime()`, `GetTime` refers to an entity which is a function. ## identifier An _identifier_ is the token which names an entity, and is also used in code to refer to the entity. For example, in `fn GetTime()`, `GetTime` is the identifier for the function. ## library A _library_ is a group of files that form an importable API and its implementation. Carbon encourages small libraries, bundled into larger packages. For example, given `package Geometry library Shapes;`, `Shapes` is a library in the `Geometry` package. ## name path A _name path_ is the dot-separated identifier list that indicates a relative or full path of a name. For example, given `fn GetArea(var Geometry.Circle: x)`, `Geometry.Circle` is a name path. `GetArea` is also a name path, albeit with only one identifier needed. ## namespace A _namespace_ is an entity that contains entities, and may be nested. For example, given a name path of `Geometry.Circle`, `Geometry` is a namespace containing `Circle`. ## package A _package_ is a group of libraries in Carbon, and is the standard unit for distribution. The package name also serves as the root namespace for all name paths in its libraries. The package name should be a single, globally-unique identifier. For example, given `package Geometry;` in a file, `Geometry` is the package and root namespace. ================================================ FILE: docs/images/snippets.md ================================================ # Front page snippets for Carbon ## Images Images are managed in [Google Drive](https://drive.google.com/drive/folders/1QrBXiy_X74YsOueeC0IYlgyolWIhvusB). ## Quicksort A sample of quicksort in Carbon. ```cpp package Sorting; fn Partition[T:! Comparable & Movable](s: Slice(T)) -> i64 { var i: i64 = -1; for (e: T in s) { if (e <= s.Last()) { ++i; Swap(&s[i], &e); } } return i; } fn QuickSort[T:! Comparable & Movable](s: Slice(T)) { if (s.Size() <= 1) { return; } let p: i64 = Partition(s); QuickSort(s[:p - 1]); QuickSort(s[p + 1:]); } ``` ## Carbon and C++ ### C++ ```cpp // C++: #include #include #include #include #include // or: import std; struct Circle { std::float32_t r; }; void PrintTotalArea(std::span circles) { std::float32_t area = 0; for (const Circle& c : circles) { area += std::numbers::pi * c.r * c.r; } std::print("Total area: {}\n", area); } auto main() -> int { std::vector circles = {{.r = 1.0}, {.r = 2.0}}; // Implicitly converts `vector` to `span`. PrintTotalArea(circles); return 0; } ``` ### Carbon ```cpp // Carbon: package Geometry; import Math; class Circle { var r: f32; } fn PrintTotalArea(circles: [Circle]) { var area: f32 = 0; for (c: Circle in circles) { area += Math.Pi * c.r * c.r; } Print("Total area: {0}", area); } fn Run() -> i32 { // A dynamically sized array, like `std::vector`. var circles: array [Circle] = ({.r = 1.0}, {.r = 2.0}); // Implicitly constructs a slice from the array. PrintTotalArea(circles); return 0; } ``` ### Mixed ```cpp // C++ code used in both Carbon and C++: #include struct Circle { std::float32_t r; }; // Carbon exposing a function for C++: package Geometry; import Cpp library "circle.h"; import Math; fn PrintTotalArea(circles: [Cpp.Circle]) { var area: f32 = 0; for (c: Cpp.Circle in circles) { area += Math.Pi * c.r * c.r; } Print("Total area: {0}", area); } // C++ calling Carbon: #include #include "circle.h" #include "geometry.carbon.h" auto main() -> int { std::vector circles = {{1.0}, {2.0}}; // A Carbon slice supports implicit construction // from `std::vector`, similar to `std::span`. Geometry::PrintTotalArea(circles); return 0; } ``` ================================================ FILE: docs/project/README.md ================================================ # Project This directory contains project-related documentation for Carbon. Information about how the project works, goals, and community information belong here. - [Goals](goals.md), and [principles](principles/README.md) derived from those goals - [Roadmap](roadmap.md) and the [process](roadmap_process.md) for updating it - Carbon's process for [evolution and governance](evolution.md) - [Groups](groups.md) used for contacting key contributors and determining access - Contributing to Carbon: - [Tools used when contributing to Carbon](contribution_tools.md) - Style guides for [language design](design_style_guide.md) and [C++ code](cpp_style_guide.md) - How Carbon does [code review](code_review.md) - [Trunk-based pull-request GitHub workflow](pull_request_workflow.md) used by Carbon ================================================ FILE: docs/project/code_review.md ================================================ # Code review ## Table of contents - [High level goals of code review](#high-level-goals-of-code-review) - [What requires review?](#what-requires-review) - [Who should review?](#who-should-review) - [GitHub pull request mechanics](#github-pull-request-mechanics) - [Code author guide](#code-author-guide) - [Write good change descriptions](#write-good-change-descriptions) - [First line](#first-line) - [Body](#body) - [Pull request labels](#pull-request-labels) - [Make small changes](#make-small-changes) - [Responding to review comments](#responding-to-review-comments) - [Responding to questions or confusion](#responding-to-questions-or-confusion) - [Understand the feedback in the comments](#understand-the-feedback-in-the-comments) - [Fixing conflicts with trunk](#fixing-conflicts-with-trunk) - [Code reviewer guide](#code-reviewer-guide) - [How quickly should you respond to a review request?](#how-quickly-should-you-respond-to-a-review-request) - [What should be covered by a review?](#what-should-be-covered-by-a-review) - [Writing review comments](#writing-review-comments) - [Approving the change](#approving-the-change) - [Pausing reviews and reassigning PRs](#pausing-reviews-and-reassigning-prs) - [Merging pull requests](#merging-pull-requests) - [Merge commit descriptions](#merge-commit-descriptions) - [Resolving an impasse or conflict](#resolving-an-impasse-or-conflict) - [Escalation](#escalation) ## High level goals of code review Code review serves several goals in the Carbon project. It directly improves the correctness, clarity, and consistency of contributions, including both code and documentation. These improvements range from the high-level functionality down through the design and implementation details. It also promotes team ownership and spreads knowledge across the team. More detailed discussions can be found in chapter 9 "Code Review" of the book _[Software Engineering at Google](https://www.amazon.com/Software-Engineering-Google-Lessons-Programming/dp/1492082791)_ and chapter 21 "Collaborative Construction" in _[Code Complete: A Practical Handbook of Software Construction](https://www.amazon.com/Code-Complete-Practical-Handbook-Construction/dp/0735619670/)_. However, these details aren't essential to understanding code review and how it works in the Carbon project. All of the important details are provided in the project documentation. ## What requires review? Every change to Carbon's repositories requires code review. Even formal [evolution decisions](evolution.md) which have been approved should have their specific changes to the repository reviewed. Many changes to Carbon repositories may _only_ require code review. Typically, these include bug fixes, and development or documentation improvements clearly in line with accepted designs. It may in some rare cases extend to exploring experimental or prototype directions whose design is under active consideration. The term "code review" in the Carbon project is not only about "code". We expect changes to any file to be reviewed, including documentation and any other material stored in the repository. ## Who should review? Everyone should feel free to review Carbon changes. Even providing small or partial review can be a good way to start contributing to Carbon. Contributors with specific domain expertise or familiarity should also try to provide review on changes touching relevant parts of the project. Additionally, at least one developer with commit access must review each change. In Carbon, developers will focus on particular areas, loosely broken down as: - [Carbon leads](groups.md#carbon-leads): [proposals](/proposals/) and other important project documents, including the [Main README](/README.md), [Code of Conduct](/CODE_OF_CONDUCT.md), [license](/LICENSE), and [goals](goals.md). - [Implementation team](groups.md#implementation-team): general changes. - We split out auto-assignment by explorer, toolchain, and other files (including documentation). [Auto-assignment](/CODEOWNERS) will help find owners, but won't always be perfect -- developers may take a PR they weren't auto-assigned in order to help review go quickly. Contributors can also request multiple reviewers, but it can be daunting to get feedback from a large number of reviewers, so we suggest keeping the number of reviewers reasonably small. Any reviews that explicitly request changes should be addressed, either with the changes or an explanation of why not, before a pull request is merged. Further, any owners who have requested changes should explicitly confirm they're happy with the resolution before the change is merged. When a team gives an affirm decision on an [evolution proposal](evolution.md), each team member should explicitly note any of their comments on the pull request that, while not blocking the _decision_, still need to be resolved as part of code review prior to it being merged. These might, for example, be trivial or minor wording tweaks or improvements. Otherwise, the decision is assumed to mean the prior review comments from members of that team are addressed; the author is free to merge once the pull request is approved, possibly with a code review separate from the proposal's review. ## GitHub pull request mechanics Carbon uses GitHub pull requests for code review, and we recommend some mechanical best practices to most effectively navigate them. - Be aware that the main thread of pull request doesn't support threaded discussions or "resolving" a comment. - If either of those would be useful, you'll probably want to comment on a file. - You can quote comments in the main conversation thread in a reply by clicking the three-dot menu on the original comment and selecting "Quote reply". - If you will want to comment on files, don't comment in the pull request conversation. - Always go to the `Files Changed` tab. - Make any in-file comments needed, but add them to a pending review rather than sending them directly. - Finish the review and add any top-level review comments there. - Don't mark reviews as requesting changes. Sometimes reviewers get busy and somebody else approves; the "request changes" feature still blocks merges until the original requester approves, which can slow reviews down. - Don't reply to in-file comment threads in the conversation view, or with direct single reply comments. - Add all replies to in-file comment threads using the `Files Changed` tab and by adding each reply to a new review, and posting them as a batch when done. - You can get to the appropriate `Files Changed` tab by clicking on the change listed in the conversation view with the incoming set of in-file comments. - This flow ensures an explicit update in the overall pull request that can help both the author and other reviewers note that new replies have arrived. - Don't reply to an in-file comment and then mark it as resolved. No one will see your reply as the thread will be hidden immediately when marked as resolved. - Generally, the person who started the comment thread should mark it as resolved when their comments are sufficiently addressed. If another reviewer is also on the thread and should also agree, just state that you're happy and the last reviewer can mark it resolved. - Trivially resolved threads can just be marked as "resolved" without further update. Examples: a suggested change that has been successfully applied, or a thread where the relevant reviewers have clearly indicated they're happy. ## Code author guide The goal of an author should be to ensure their change improves the overall code, repository, and/or project. Within the context of code review, the goal is to get a reviewer to validate that the change succeeds at this goal. That involves finding an effective reviewer given the particular nature of the change, helping them understand the change fully, and addressing any feedback they provide. ### Write good change descriptions The change description in the pull request is the first thing your reviewers will see. This sets the context for the entire review, and is very important. #### First line The first line of a commit, or the subject of the pull request, should be a short summary of specifically what is being done by that change. It should be a complete sentence, written as though it was an order. Try to keep it short, focused, and to the point. #### Body The description body may need to explain several important aspects of the change to provide context for the reviewer when it isn't obvious from the change itself: - The problem being solved by the change. - Why the approach taken is the best one. - Any issues, concerns, or shortcomings of the approach. - Any alternatives considered or attempted. - Relevant supporting data such as examples or benchmarks. Try to anticipate what information the reviewer of your change will need to have in order to be effective. Also consider what information someone else will need a year in the future when doing archaeology on the codebase and they come across your change without any context. #### Pull request labels GitHub labels are used on PRs, such as [toolchain](https://github.com/carbon-language/carbon-lang/labels/toolchain). These are automatically added based on affected directories. We do not put labels in PR descriptions; for example, we would write `Implements type deduction` instead of `[feature] Type deduction`, or `Fix crash in function imports` instead of `[bug] Crash in function imports`. Given the current size and state of Carbon, we believe the `[feature]`-style PR labeling would add too much process for their benefit. Additionally, in contrast with LLVM's [`[NFC]` label policies](https://llvm.org/docs/DeveloperPolicy.html#obtaining-commit-access), Carbon changes are always reviewed prior to commit. ### Make small changes Small changes have many benefits: - Faster review. - More thorough review. - Easier to merge. - Easier to revert if needed. The ideal size of a change is as small as possible while it remains self-contained. It should address only _one thing_. Often, this results in a change only addressing _part_ of a feature rather than the whole thing at once. This makes work more incremental, letting the reviewer understand it piece by piece. It can also make it much easier to critically evaluate whether each part of a feature is adequately tested by showing it in isolation. That said, a change should not be so small that its implications cannot easily be understood. It is fine to provide the reviewer context or a framework of a series of changes so they understand the big picture, but that will only go so far. It is still possible to shrink a change so much that it becomes nonsensical in isolation. For example, a change without appropriate tests is not self-contained. ### Responding to review comments Many comments have easy and simple responses. The easiest is **"Done"**. When the comment is a concrete suggestion that makes sense and you implement it, you can simply let the reviewer know their suggestion has been incorporated. If the _way_ you implemented the suggestion might need clarification, add that as well. For example, consider mentioning tweaks to the suggestion or when the suggestion was applied in more places. When a suggestion from the reviewer is explicitly optional, you may also have a simple response that you're not going to make the change. This is totally fine -- if it weren't, the reviewer shouldn't have listed it as optional -- but it may be helpful to explain your reasoning to the reviewer so they understand better why the optional suggestion didn't make sense to you. Sometimes comments, even optional ones, center around slight differences or preferences around the code. Consider that the reviewer may be a good proxy for future readers. If the suggestion is essentially equivalent to your original code, consider adopting it as it may make the code easier to read for others. But if you feel the current choice is _better_, even if only slightly, stand up for yourself and keep it. The reviewer can always push for a change and justify it if needed. For non-optional comments, this section provides several suggestions on how best to make progress. If none of these work, you may need to [resolve an impasse or conflict](#resolving-an-impasse-or-conflict). In response to suggestions, update the files in the pull request in new commits. Rebasing, squashing, or force-pushing commits can break GitHub's comment associations, and it makes it harder to determine what's changed since the last review. With regular pushes, GitHub can show individual deltas, giving additional flexibility to the reviewer. We squash pull requests when we merge them, so the end result is the same. It is good to reply to every comment so that the reviewer knows you saw them. Best practice is to send the reply to all of the comments at once, after the files in the pull request have been updated. If there are a lot of replies, it can be helpful to include a message saying whether the pull request is now ready for another round of review, or press the "Re-request review" button to the right of the reviewer's name: ![The re-request review button on GitHub](https://user-images.githubusercontent.com/711534/189789784-fcf18d1b-137b-48ba-959a-0d05cee36c2d.png) #### Responding to questions or confusion Some comments in code review will be questions or confusion as the reviewer tries to understand the code in question or why a particular approach was used. Don't assume that questions are a request for a change. Reviewers should be explicit if they think a change is needed rather than merely asking questions. You should assume a question or confusion is something which only needs to be clarified. However, when responding to a question or confusion, consider making changes to improve clarity in addition to responding within the review, such as by adding comments or changing code structure. The reviewer may not be the last person to need more clarity, and you should use their comments as a signal for improvement. Once done, the review response should typically focus on verifying that the clarifications made in the code are sufficient for the reviewer. #### Understand the feedback in the comments At times, review comments may be confusing or frustrating for you. While this is something we always want reviewers to minimize, it will still happen at some times and to some degree. It helps to remember that the goal of the review is to ensure the change results in the project improving over time. If the review comment doesn't make sense, ask the reviewer to help you understand the feedback better. If it isn't constructive or doesn't seem to provide any meaningful path forward, ask the reviewer to provide this. Making comments both clear and constructive are part of the reviewers' responsibilities. Once there is a clear and effectively communicated comment that you understand, it may still feel wrong or like it is unnecessarily blocking your progress. It is important to try to step back in this situation and, no matter how certain you are, genuinely consider whether there is valuable feedback. You should be asking yourself whether the reviewer might be correct, potentially in an unexpected or surprising way. If you can't decide this definitively, you may need to work to get a deeper understanding. If you are confident that the reviewer's comment is incorrect, that is _OK_. The reviewer is also only human and is certain to make mistakes and miss things. The response needs to try to explain what it is that leads you to be confident in your assessment. Lay out the information you have and how you are reasoning about the issue to arrive at the conclusion. Try not to make assumptions about what the reviewer knows or why they made the comment. Instead, focus on surfacing explicitly your perspective on the issue. These parts of a review will often be a discussion and may need to iterate a few times. That isn't intrinsically bad, but try to make sure that it doesn't result in reiterating positions or repeating things. Make sure the discussion is _progressing_ towards deeper understanding and recognize when you reach an impasse or conflict and shift strategy to [resolve that](#resolving-an-impasse-or-conflict). It is also useful to avoid long delays between these iterations. Consider discussing over Discord chat or scheduling a quick video chat on the specific issue. This can avoid multi-hour -- or multi-day -- round trips. ### Fixing conflicts with trunk If a PR has conflicts with trunk, those conflicts must be resolved before the PR can be merged. If the PR is already in review, prefer to wait until review is mostly done before fixing the conflicts. Conflicts should be fixed by way of a merge commit rather than rebasing. ## Code reviewer guide The specific goal for a particular review should always be to ensure that the overall health of the code, repository, and/or project improves over time. This requires that contributions _make progress_ -- otherwise, nothing can improve. However, the review should ensure that quality of changes does not cause the health of the project to decrease over time. The primary responsibility for ensuring that code review remains constructive, productive, and helpful resides in the _reviewer_. As a reviewer, you are in a position of power and asked to critique the authors hard work. With this power comes responsibility for conducting the review well. ### How quickly should you respond to a review request? Try to respond to code review requests as soon as you can without interrupting a focused task. At the latest, the next day you are working on the project. Note that the review isn't expected to necessarily be complete after a single review. It is more valuable to give reasonably quick but partial feedback than to delay feedback in order to complete it. If leaving partial feedback, make it clear to the author which parts are covered and which you haven't gotten to yet. Large changes are especially important to give incremental feedback on in order to do so in a timely fashion. One of the first things to consider with large changes is whether it can be split apart into smaller changes that are easier to review promptly. This timeliness guidance doesn't apply to the higher-level [evolution process](evolution.md) reviews. Evaluating those proposals will often require a larger time investment and have their own timelines spelled out in the process. Here, we are talking about simply reviewing changes themselves orthogonally to any evolutionary discussion and evaluation. ### What should be covered by a review? Things to consider and evaluate when reviewing changes: - Is the code well designed? - Is the resulting functionality, including its interface, good for the users of the code? - Does the resulting design facilitate long-term maintenance? - Can the code be simplified? Is there unnecessary complexity? - Are things being implemented that aren't yet needed and only _might_ be needed in the future? - Is the code free of bugs and well tested? - Is memory safely managed? - Is any parallel or concurrent programming done safely? - Do unit tests cover relevant behaviors and edge cases? - Do any integration tests need to be extended or added? - Do any fuzz tests need to be extended or added? - Are any tests well designed to be both thorough but also maintainable over time? - Is the code easy to read? - Are the names used in the code clear? - Are all important or non-obvious aspects of the code well commented? Do the comments focus on _why_ instead of _what_? - Is there appropriate high level documentation for the change? - Does the change adhere to all relevant style guides? - Is the change consistent with other parts of the project? ### Writing review comments These are general guidelines for writing effective code review comments: - **Be kind.** Detailed review, especially in an open source project, can be stressful and difficult for the author. As a reviewer, part of the job is to ensure the review experience ends up positive and constructive for the author. - **Stay constructive.** Focus your comments on suggesting specific ways to improve the change. If you need to explain why an improvement is necessary, focus on objective ways the improvement helps and avoid both subjective assessments and anchoring on problems with the current state. - **Explain why.** It is important for the author to understand not merely the mechanical suggested change but what motivates it and why it matters. This may help clear up misunderstandings, help the suggestion be understood and applied more effectively, and allow internalizing improvements for future contributions. - **Provide a path forward.** The author needs to understand what they will need to do to respond to your comments. For example, always provide alternatives when commenting that the current approach won't work. Keep in mind that the goal is to improve the overall health of the code, repository, and/or project over time. Sometimes, there will be pushback on review comments. Consider carefully if the author is correct -- they may be closer to the technical issues than you are and may have important insight. Also consider whether the suggestion is necessary to achieve the overall goal. If the suggestion isn't critical to make the change an overall improvement, it may be fine for it to move forward as-is. As with all communication in the Carbon project, it is critical that your comments are not unkind, unwelcoming, angry, ad-hominem attacks, or otherwise violating our community's [code of conduct](/CODE_OF_CONDUCT.md). ### Approving the change Be explicit and unambiguous at the end of your review. Select "Approve" when submitting the review to mark this in GitHub. You can always include a message, often "LGTM" or "Looks Good To Me" is often used. If you don't feel like you're in a position to approve the change and are simply helping out with review feedback, make that explicit as well. You should set the review to a "Comment" in GitHub, but also state this explicitly in the message since this is the default and doesn't indicate that your feedback _is_ addressed. For example, say that "my comments are addressed, but leaving the final review to others" to clearly indicate that you're happy but are deferring the decision to others. If you are an owner and deferring to someone else, it is essential to suggest specific other reviewers. Otherwise, we risk all the owners assuming another is going to approve the change. An important technique to make progress, especially with different working hours and timezones, is to approve changes even with outstanding comments. For example, if the comments you have are straightforward and have unambiguous fixes or suggested edits, you should give an LGTM with those comments addressed. The author can always come back to you if they have questions, and we can always revert changes if the resolution for some reason diverges wildly from your expectations. ### Pausing reviews and reassigning PRs When temporarily unavailable to review, for example due to a vacation, reviewers can either mark themselves as [busy in GitHub](https://docs.github.com/en/account-and-profile/tutorials/personalize-your-profile#setting-a-status), or ask an admin to stop assignment (using ["Never assign to certain team members"](https://docs.github.com/en/organizations/organizing-members-into-teams/managing-code-review-settings-for-your-team#configuring-auto-assignment)). A PR's reviewer can also be changed to a team, such as `carbon-language/toolchain-reviewers`, and it should be automatically reassigned. This can be done by anyone, not just a reviewer. ## Merging pull requests Pull requests are ready to be merged when reviewers have indicated they're happy (for example, "LGTM" or "Looks good to me") or have approved the pull request. While all merges require at least one approval, a reviewer might approve before others are finished reviewing; all reviewers should be given time to comment to ensure there's a consensus. Either the author or reviewer may merge and resolve conflicts. The author may indicate they want to merge by informing the reviewer and adding the `DO NOT MERGE` label. The reviewer is encouraged to coordinate with the author about merge timing if there are concerns about breaks. In either case, the developer doing the merge is expected to be available to help address post-commit issues, whether through a fix-forward or a rollback. ### Merge commit descriptions When squashing and merging, GitHub tries to generate a description, but it's recommended to use the first comment on the pull request review for the squashed commit description. Authors should keep it up-to-date so that reviewers can merge when the change is ready. Reviewers shouldn't edit or rewrite this message themselves, and instead ask the author make those changes (possibly with suggestions) just like other parts of the code review. It's important that the commit message is one the author is comfortable with when merged. When suggested edits have been merged into a pull request, GitHub will append a `Co-authored-by:` line to its default proposed commit message for each reviewer who suggested edits that were applied. These lines should be retained and appended to the message from the initial comment. ## Resolving an impasse or conflict At some point, a review may reach an impasse or a genuine conflict. While our goal is always to resolve these by building consensus in review, it may not be possible. Both the author and any reviewers should be careful to recognize when this point arrives and address it directly. Continuing the review is unlikely to be productive and has a high risk of becoming acrimonious or worse. There are two techniques to use to resolve these situations that should be tried early on: 1. Bring another person into the review to help address the specific issue. Typically they should at least be an owner, and may usefully be a [Carbon lead](groups.md#carbon-leads). 2. Ask the specific question in a broader forum, such as Discord, in order to get a broad set of perspectives on a particular area or issue. The goal of these steps isn't to override the author or the reviewer, but to get more perspectives and voices involved. Often this will clarify the issue and its trade-offs, and provide a simple resolution that all parties are happy with. However, in some cases, the underlying conflict isn't actually addressed. While there is a desire to generally bias towards the direction of the owners during reviews, reviews should _not_ turn into a voting process. The reason for proceeding in a specific direction should always be explained sufficiently that all parties on the review are satisfied by the explanation and don't feel the need to escalate. Fundamentally, both reviewers and the author need to agree on the direction to move forward. If reaching that agreement proves impossible, the review should be [escalated](#escalation). If you feel like an escalation is needed in a review, be explicit and clear in requesting it. There is nothing bad about going through this process, but it should only occur when needed and so it helps to be very clear. Once the impasse or conflict is addressed, it is _essential_ to commit to that direction. It can be especially difficult for the author to accept a direction that they initially disagree with and make changes to their code as a result. An essential skill is the ability to [disagree and commit](https://en.wikipedia.org/wiki/Disagree_and_commit). ## Escalation At the explicit request of any [Carbon lead](evolution.md#carbon-leads-1) or to resolve any fundamental impasse in a review, the change should move to a formal [proposal](evolution.md#proposals). Ultimately, the Carbon project [governance](evolution.md#governance-structure) structure is always available as an escalation path. Before escalating an impasse or conflict in code review, try asking another reviewer to help resolve the issue or bridge any communication gaps. Consider scheduling a quick video chat to discuss and better understand each other’s concerns and position. Note that the formal evolution process is heavyweight and relatively slow. The expectation is that this is rarely used and only to resolve serious and severe disagreements. If this becomes a more common problem, lighter weight processes may be needed to help ensure a reasonable rate of progress. ================================================ FILE: docs/project/commit_access.md ================================================ # Commit access ## Table of contents - [Overview](#overview) - [Getting access](#getting-access) - [Removing access](#removing-access) ## Overview First and foremost, commit access is **not required** for contributing to Carbon! Anyone can send a pull request with a change to Carbon. Very few parts of our process require commit access, and most development activity is exactly the same for folks with or without. This is an important feature of our GitHub workflow for us, and one we plan to keep. The main thing commit access allows is for developers to merge PRs into the main [carbon-lang repository](https://github.com/carbon-language/carbon-lang/). They still need review, and often a reviewer with commit access handles the merge. It also gives the ability to [approve and merge changes](code_review.md). Getting commit access can help ease the development process; for contributors who don't feel comfortable doing reviews, it can still ease the review process for their own PRs. It allows authors to automatically get GitHub Action results and merge their own PRs, including after resolving conflicts or fixing trivial PR comments post-approval. Developers with commit access are expected to make reasonable choices about when to approve or merge PRs for others. Generally, the change either needs to be trivially understood without context on the code in question (such as a cleanup or typo fix), or the developer should have some context on the code in question. If in doubt, feel free to review but leave approving or merging for someone else. Similarly, developers with commit access are expected to make reasonable choices about what changes to make to their own PRs without asking for another round of review prior to merge, even after approval. And again, if in doubt, ask for review. ## Getting access Contributors can request commit access based on their commit history. We want to make sure that developers with commit access are reasonably familiar with the style and structure of the codebase. Access will typically be granted when developers have contributed several PRs and are expecting to continue making more. After a few non-trivial PRs are merged, contributors can ask a reviewer to nominate them for commit access if they plan to keep contributing. Reviewers can also directly suggest and nominate someone. Nominations need to be approved by at least one lead. When approved, an invitation will be sent to join the [Commit access team](https://github.com/orgs/carbon-language/teams/commit-access) on GitHub. The invitation should cause a notification from GitHub, but it's also possible to go to the [invitation link](https://github.com/orgs/carbon-language/invitation) directly. Access is granted when the invitation is accepted. ## Removing access We'll periodically remove commit access from contributors who have been idle for over 6 months. We'll use a combination of sources to determine what "idle" means, including whether a developer has been either sending or reviewing PRs. Developers can ask for commit access to be restored if they plan to start contributing again or come back from a break. Any relevant past contributions will still apply and allow it to be restored trivially. For example, for `jonmeow`, GitHub searches (defaulting to PRs for convenience, but other types are included): - [repository:carbon-language/carbon-lang author:jonmeow](https://github.com/search?q=repository%3Acarbon-language%2Fcarbon-lang+author%3Ajonmeow&type=pullrequests) - [repository:carbon-language/carbon-lang commenter:jonmeow](https://github.com/search?q=repository%3Acarbon-language%2Fcarbon-lang+commenter%3Ajonmeow&type=pullrequests) ================================================ FILE: docs/project/contribution_tools.md ================================================ # Contribution tools The Carbon language project has a number of tools used to assist in preparing contributions. ## Table of contents - [Setup commands](#setup-commands) - [Debian or Ubuntu](#debian-or-ubuntu) - [Installing Bazelisk](#installing-bazelisk) - [Old `clang` versions](#old-clang-versions) - [macOS](#macos) - [Tools](#tools) - [Main tools](#main-tools) - [Running pre-commit](#running-pre-commit) - [Optional tools](#optional-tools) - [Jujutsu (`jj`)](#jujutsu-jj) - [AI assistants](#ai-assistants) - [Manually building Clang and LLVM (not recommended)](#manually-building-clang-and-llvm-not-recommended) - [Troubleshooting build issues](#troubleshooting-build-issues) - [`bazel clean`](#bazel-clean) - [Old LLVM versions](#old-llvm-versions) - [Debugging](#debugging) - [Asking for help](#asking-for-help) ## Setup commands These commands should help set up a development environment on your machine. ### Debian or Ubuntu ```shell # Update apt. sudo apt update # Check that the `clang` version is at least 19, our minimum version. That needs # the number of the `:` in the output to be over 19. For example, `1:19.0-1`. apt-cache show clang | grep 'Version:' # Install tools. sudo apt install \ clang \ gh \ libc++-dev \ libc++abi-dev \ lld \ lldb \ python3 \ pipx # Install pre-commit. pipx install pre-commit # Set up git. # If you don't already have a fork: gh repo fork --clone carbon-language/carbon-lang cd carbon-lang pre-commit install # Run tests. ./scripts/run_bazelisk.py test //...:all ``` #### Installing Bazelisk Although the `run_bazelisk` script can make it easy to get started, if you're frequently building Carbon, it can be a bit much to type. Consider either aliasing `bazel` to the `run_bazelisk.py` script, or [downloading a bazelisk release](https://github.com/bazelbuild/bazelisk), adding it to your `$PATH`, and aliasing `bazel` to it. #### Old `clang` versions If the version of `clang` is earlier than 19, you may still have version 19 available. You can use the following install instead: ```shell # Install explicitly versioned Clang tools. sudo apt install \ clang-19 \ libc++-19-dev \ libc++abi-19-dev \ lld-19 \ lldb-19 # In your Carbon checkout, tell Bazel where to find `clang`. You can also # export this path as the `CC` environment variable, or add it directly to # your `PATH`. echo "build --repo_env=CC=$(readlink -f $(which clang-19))" >> user.bazelrc ``` And if it's not available directly from the distribution, you can install Clang tools on Debian/Ubuntu from . > NOTE: Most LLVM 19+ installs should build Carbon. If you're having issues, see > [troubleshooting build issues](#troubleshooting-build-issues). ### macOS ```shell # Install Homebrew. /bin/bash -c "$(curl -fsSL \ https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)" # IMPORTANT: Make sure `brew` is added to the PATH! # Install Homebrew tools. brew install \ bazelisk \ gh \ llvm \ python@3.10 \ pre-commit # IMPORTANT: Make sure `llvm` is added to the PATH! It's separate from `brew`. # Set up git. gh repo fork --clone carbon-language/carbon-lang cd carbon-lang pre-commit install # Run tests. Note homebrew makes `bazel` an alias to `bazelisk`. bazel test //...:all ``` > NOTE: On macOS, you should end up adding rc file lines similar to: > > ``` > # For `brew`, `gh`, and other tools: > export PATH="${HOME}/.brew/bin:${PATH}" > # For `llvm`: > export PATH="$(brew --prefix llvm)/bin:${PATH}" > ``` ## Tools ### Main tools These tools are essential for work on Carbon. - Package managers - `apt` (for Debian or Ubuntu) - To upgrade versions of `apt` packages, it will be necessary to periodically run `sudo apt update && sudo apt upgrade`. - [Homebrew](https://brew.sh/) (for macOS) - To upgrade versions of `brew` packages, it will be necessary to periodically run `brew upgrade`. - [Python](https://python.org) - Carbon requires Python 3.10 or newer. - To upgrade versions of pip-installed packages, it will be necessary to periodically run `pipx list --outdated`, then `pipx install -U ` to upgrade desired packages. - When upgrading, version dependencies may mean packages _should_ be outdated, and not be upgraded. - Main tools - [Bazel](https://www.bazel.build/) - [Bazelisk](https://docs.bazel.build/versions/master/install-bazelisk.html): Downloads and runs the [configured Bazel version](/.bazelversion). - [Clang](https://clang.llvm.org/) and [LLVM](https://llvm.org/) - NOTE: Most LLVM 19+ installs should build Carbon. If you're having issues, see [troubleshooting build issues](#troubleshooting-build-issues). - [gh CLI](https://github.com/cli/cli): Helps with GitHub. - [pre-commit](https://pre-commit.com): Validates and cleans up git commits. - `autoupdate_testdata.py`: Updates expected output for tests. - Usage: `./toolchain/autoupdate_testdata.py [files...]` - This is essential when changes affect compiler output (diagnostics, SemIR, etc.). #### Running pre-commit [pre-commit](https://pre-commit.com) is typically set up using `pre-commit install`. When set up in this mode, it will check for issues when `git commit` is run. A typical commit workflow looks like: 1. `git commit` to try committing files. This automatically executes `pre-commit run`, which may fail and leave files modified for cleanup. 2. `git add .` to add the automatically modifications done by `pre-commit`. 3. `git commit` again. You can also use `pre-commit run` to check pending changes without `git commit`, or `pre-commit run -a` to run on all files in the repository. > NOTE: Some developers prefer to run `pre-commit` on `git push` instead of > `git commit` because they want to commit files as originally authored instead > of with pre-commit modifications. To switch, run > `pre-commit uninstall && pre-commit install -t pre-push`. ### Optional tools These tools aren't necessary to contribute to Carbon, but can be worth considering if they fit your workflow. - [GitHub Desktop](https://desktop.github.com/): A UI for managing GitHub repositories. - `rs-git-fsmonitor` and Watchman: Helps make `git` run faster on large repositories. - **WARNING**: Bugs in `rs-git-fsmonitor` and/or Watchman can result in `pre-commit` deleting files. If you see files being deleted, disable `rs-git-fsmonitor` with `git config --unset core.fsmonitor`. - [vim-prettier](https://github.com/prettier/vim-prettier): A vim integration for [Prettier](https://prettier.io/), which we use for formatting. - [Visual Studio Code](https://code.visualstudio.com/): A code editor. - We provide [recommended extensions](/.vscode/extensions.json) to assist Carbon development. Some settings changes must be made separately: - Python › Formatting: Provider: `black` - **WARNING:** Visual Studio Code modifies the `PATH` environment variable, particularly in the terminals it creates. The `PATH` difference can cause `bazel` to detect different startup options, discarding its build cache. As a consequence, it's recommended to use **either** normal terminals **or** Visual Studio Code to run `bazel`, not both in combination. Visual Studio Code can still be used for other purposes, such as editing files, without interfering with `bazel`. - We also provide recommended setups for debugging in VS Code with either [LLDB](/toolchain/docs/debugging.md#debugging-with-lldb) or [GDB]((/toolchain/docs/debugging.md#debugging-with-gdb) - [clangd](https://clangd.llvm.org/installation): An LSP server implementation for C/C++. - To ensure that `clangd` reports accurate diagnostics. It needs a generated file called `compile_commands.json`. This can be generated by invoking the command below: ``` ./scripts/create_compdb.py ``` - **NOTE**: This assumes you have `python` 3 installed on your system. - [`uv`](https://docs.astral.sh/uv/): A fast Python package manager. - Notably, `uv` supports automatic management of even complex Python dependencies for scripts: https://docs.astral.sh/uv/guides/scripts/ - Installation: https://docs.astral.sh/uv/getting-started/installation/ #### Jujutsu (`jj`) [Jujutsu](https://github.com/jj-vcs/jj) is a Git-compatible version control system that can be used instead of or alongside Git. See the [documentation for using Jujutsu with GitHub](https://jj-vcs.github.io/jj/latest/github/) for more information. If you use `jj`, you may find the following configuration snippets (added to `~/.config/jj/config.toml`) helpful for your workflow: ```toml [aliases] # Clean up untracked or abandoned commits. abandon-untagged = ["abandon", "all() & ~ancestors(@ | bookmarks() | remote_bookmarks())"] [ui] # Use Git-style conflict markers, which VS Code can provide merge support for. conflict-marker-style = "git" [ui.diff] # Produce Git-compatible diff format. format = "git" [remotes.origin] # Automatically track all remote bookmarks. auto-track-bookmarks = "*" [templates] # Automatically add a trailer to commits to indicate that they were AI-assisted. commit_trailers = ''' "Assisted-by: My AI Tool"''' ``` #### AI assistants When using AI assistants and reviewing terminal commands, some commands which may be helpful and reasonably safe to allowlist (assuming prefix-based allowlisting) are: ``` # Carbon development commands. bazelisk build bazelisk test bazelisk run //toolchain/testing:file_test -- clang-format pre-commit run ./toolchain/autoupdate_testdata.py # Shell commands. Note that these allow reading arbitrary files on your local # file system. cat grep head ls # VCS commands. git diff git log git show git status ``` ### Manually building Clang and LLVM (not recommended) We primarily test against [apt.llvm.org](https://apt.llvm.org) and Homebrew installations. However, you can build and install LLVM yourself if you feel more comfortable with it. The essential CMake options to pass in order for this to work reliably include: ``` -DLLVM_ENABLE_PROJECTS=clang;clang-tools-extra;lld;lldb -DLLVM_ENABLE_RUNTIMES=compiler-rt;libcxx;libcxxabi;libunwind -DRUNTIMES_CMAKE_ARGS=-DLLVM_ENABLE_PER_TARGET_RUNTIME_DIR=OFF;-DCMAKE_POSITION_INDEPENDENT_CODE=ON;-DLIBCXX_ENABLE_STATIC_ABI_LIBRARY=ON;-DLIBCXX_STATICALLY_LINK_ABI_IN_SHARED_LIBRARY=OFF;-DLIBCXX_STATICALLY_LINK_ABI_IN_STATIC_LIBRARY=ON;-DLIBCXX_USE_COMPILER_RT=ON;-DLIBCXXABI_USE_COMPILER_RT=ON;-DLIBCXXABI_USE_LLVM_UNWINDER=ON -DLLDB_ENABLE_PYTHON=ON ``` ## Troubleshooting build issues ### `bazel clean` Changes to packages installed on your system may not be noticed by `bazel`. This includes things such as changing LLVM versions, or installing libc++. Running `bazel clean` should force cached state to be rebuilt. ### Old LLVM versions Many build issues result from the particular options `clang` and `llvm` have been built with, particularly when it comes to system-installed versions. If you run `clang --version`, you should see at least version 19. If you see an older version, please update, or use the special `clang-19` instructions above. System installs of macOS typically won't work, for example being an old LLVM version or missing llvm-ar; [setup commands](#setup-commands) includes LLVM from Homebrew for this reason. Run [`bazel clean`](#bazel-clean) when changing the installed LLVM version. ### Debugging See the [toolchain documentation](/toolchain/docs/debugging.md) for guidance on how to debug problems with the toolchain itself. ### Asking for help If you're having trouble resolving issues, please ask on [#build-help](https://discord.com/channels/655572317891461132/824137170032787467), providing the output of the following diagnostic commands: ```shell echo $CC which clang which clang-19 clang --version grep llvm_bindir $(bazel info workspace)/bazel-execroot/external/+clang_toolchain_extension+bazel_cc_toolchain/clang_detected_variables.bzl # If on macOS: brew --prefix llvm ``` These commands will help diagnose potential build issues by showing which tooling is in use. ================================================ FILE: docs/project/cpp_style_guide.md ================================================ # C++ style guide ## Table of contents - [Background](#background) - [Baseline](#baseline) - [Carbon-local guidance](#carbon-local-guidance) - [General naming rules](#general-naming-rules) - [File names](#file-names) - [Syntax and formatting](#syntax-and-formatting) - [Line comments](#line-comments) - [Initialization](#initialization) - [Passing addresses](#passing-addresses) - [Naming variable types and the use of `auto`](#naming-variable-types-and-the-use-of-auto) - [Copyable and movable types](#copyable-and-movable-types) - [Static and global variables](#static-and-global-variables) - [Foundational libraries and data types](#foundational-libraries-and-data-types) - [Iterative algorithms](#iterative-algorithms) - [Suggested `.clang-format` contents](#suggested-clang-format-contents) ## Background C++ code in the Carbon project should use a consistent and well documented style guide. Where possible, this should be enacted and enforced with tooling to avoid toil both for authors of C++ code in the Carbon project and for code reviewers. However, we are not in the business of innovating significantly in the space of writing clean and maintainable C++ code, and so we work primarily to reuse existing best practices and guidelines. ## Baseline The baseline style guidance is the [Google C++ style guide](https://google.github.io/styleguide/cppguide.html). ## Carbon-local guidance We provide some local guidance beyond the baseline. These are typically motived either by specific value provided to the project, or to give simpler and more strict guidance for Carbon's narrow use of C++. ### General naming rules Carbon's C++ code tries to match the proposed Carbon naming convention as closely as is reasonable in C++ in order to better understand and familiarize ourselves with the practice of using this convention. It happens that this is fairly similar to the naming convention in the Google style guide and largely serves to simplify it. - Known, compile-time constants use `UpperCamelCase`, referencing Proper Nouns. - This includes namespaces, type names, functions, member functions (except as noted below), template parameters, `constexpr` variables, enumerators, etc. - Note that virtual member functions should be named with `UpperCamelCase`. The distinction between a virtual function and a non-virtual function should be invisible, especially at the call site, as that is an internal implementation detail. We want to be able to freely change that without updating the name. - Member functions may use `snake_case` names if they do nothing besides return a reference to a data member (or assign a value to a data member, in the case of `set_` methods), **or** if their behavior (including performance) would be unsurprising to a caller who assumes they are implemented that way. - All other names use `snake_case`, including function parameters, and non-constant local and member variables. - Private member variables should have a trailing `_`. - For acronyms and initialisms, we generally follow the [capitalization style](https://google.github.io/styleguide/cppguide.html#General_Naming_Rules) (`Api` instead of `API`). - The exceptions are `LLVM` and `IR`, which we capitalize. - For abbreviations, there is a list of [common toolchain abbreviations](/toolchain/docs/idioms.md#abbreviations-used-in-the-code-aka-carbon-abbreviation-decoder-ring). ### File names - Always use `snake_case` for files, directories, and build system rules. Avoid `-`s in these as well. - Use `.cpp` for source files, which is the most common open source extension and matches other places where "C++" is written without punctuation. ### Syntax and formatting These are minor issues where any of the options would be fine and we simply need to pick a consistent option. Where possible, [`clang-format`](#suggested-clang-format-contents) should be used to enforce these. - Always use trailing return type syntax for functions and methods, including `-> void`, for consistency with Carbon syntax. - Place the pointer `*` adjacent to the type: `TypeName* variable_name`. - Only declare one variable at a time (declaring multiple variables requires confusing repetition of part of the type). - Write `const` before the type when at the outer level: `const int N = 42;`. - Use the `using`-based type alias syntax instead of `typedef`. - Don't use `using` to support unqualified lookup on `std` types; for example, `using std::vector;`. This also applies to other short namespaces, particularly `llvm` and `clang`. - Writing `std::` gives clearer diagnostics and avoids any possible ambiguity, particularly for ADL. - An exception is made for functions like `std::swap` that are intentionally called using ADL. This pattern should be written as `{ using std::swap; swap(thing1, thing2); }`. - Always mark constructors `explicit` unless there's a specific reason to support implicit or `{}` initialization. - Always use braces for conditional, `switch`, and loop statements, even when the body is a single statement. - Within a `switch` statement, use braces after a `case` label when necessary to create a scope for a variable. - Always break the line immediately after an open brace except for empty loop bodies. - For [internal linkage](https://google.github.io/styleguide/cppguide.html#Internal_Linkage) of definitions of functions and variables, prefer `static` over anonymous namespaces. `static` minimizes the context necessary to notice the internal linkage of a definition. - Anonymous namespaces are still necessary for classes and enums. - Tests are an exception and should typically be wrapped in an anonymous namespace under the namespace of the code under test, to keep everything internal. - For [Access Control](https://google.github.io/styleguide/cppguide.html#Access_Control), specifically for test fixtures in `.cpp` files, we use `public` instead of `protected`. This is motivated by the `misc-non-private-member-variables-in-classes` tidy check. #### Line comments Only use line comments (with `//`, not `/* ... */`), on a line by themselves, except for [argument name comments](https://clang.llvm.org/extra/clang-tidy/checks/bugprone/argument-comment.html), [closing namespace comments](https://google.github.io/styleguide/cppguide.html#Namespaces), and similar structural comments. In particular, don't append comments about a line of code to the end of its line: ``` int bad = 42; // Don't comment here. // Instead comment here. int good = 42; // Closing namespace comments are structural, and both okay and expected. } // namespace MyNamespace ``` This dogfoods our planned commenting syntax for Carbon. It also provides a single, consistent placement rule. It also provides more resilience against automated refactorings. Those changes often make code longer, which forces ever more difficult formatting decisions, and can easily spread one line across multiple lines, leaving it impossible to know where to place the comment. Comments on their own line preceding such code, while still imprecise, are at least less confusing over the course of such refactorings. #### Initialization Initialization syntax has guidelines based on what compiles. See also [Abseil's tip #88](https://abseil.io/tips/88#best-practices-for-initialization), although these guidelines differ slightly. - Use assignment syntax (`=`) when initializing directly with the intended value (or with a braced initializer directly specifying that value). - Prefer braced initialization for aggregate initialization, such as structs, pairs, and initializer lists. - Use designated initializers (`{.a = 1}`) when possible for structs, but not for pairs or tuples. Prefer to only include the typename when required to compile (`WizType{.a = 1}`). This is analogous to how structs and tuples would be written in Carbon code. - Avoid braced initialization for types that define a constructor, except as an initializer list (`llvm::SmallVector v = {0, 1};`), `std::pair`, or `std::tuple`. Never use it with `auto` (`auto a = {0, 1}`). - Prefer parenthesized initialization (`FooType foo(10);`) in most other cases. - Braced initialization without `=` (`BarType bar{10}`) should be treated as a fallback, preferred only when other constructor syntax doesn't compile. #### Passing addresses When passing an object's address as an argument, use a reference unless one of the following cases applies: - If the parameter is optional, use a pointer and document that it may be null. - If it is captured and must outlive the call expression itself, use a pointer and document that it must not be null (unless it is also optional). - When storing an object's address as a non-owned member, prefer storing a pointer. For example: ```cpp class Bar { public: // `foo` must not be null. explicit Bar(Foo* foo) : foo_(foo) {} private: Foo* foo_; }; ``` ### Naming variable types and the use of `auto` We generally use `auto` for most local variables when a type can be inferred, except for primitive types such as `bool` and `int`. It is not required to use `auto`, and shorter type names such as `SemIR::InstId` are sometimes named even though they could be inferred. Naming the type can be helpful in cases where the type would be obscure and can not be explained with the variable name. Function parameters generally name the type of each parameter, though lambdas may use `auto` if it's helpful. When naming variables, we typically suffix `_id` for ID types. When needed, we can also resolve ambiguity by referring to the full type name in the variable name; for example, if there's a `ClassId`, `InstId`, and `TypeId` for the same class entity, we might call these `class_id`, `class_inst_id`, and `class_type_id`. Similarly, we might call an `Inst` `class_inst`. ### Copyable and movable types - Types should have value semantics and support both move and copy where possible. - Types that cannot be copied should still be movable where possible. - If supported, moving should be as efficient as possible. ### Static and global variables - Global and static variables, whether at file, class, or function scope, should be declared `constexpr`. ### Foundational libraries and data types - In the toolchain, prefer LLVM libraries and data structures to standard C++ ones. - These are optimized significantly for performance, especially when used without exception handling or safety requirements, and when used in patterns that tend to occur while building compilers. - They also minimize the vocabulary type friction when using actual LLVM and Clang APIs. - Do not add other third-party library dependencies to any code that might conceivably be used as part of the compiler or runtime. - Compilers and runtime libraries have unique constraints on their licensing. For simplicity, we want all transitive dependencies of these layers to be under the LLVM license that the Carbon project as a whole uses (as well as LLVM itself). ### Iterative algorithms We choose iterative over recursive algorithms, and use clang-tidy to help enforce this. We expect Carbon to be used in codebases which stress test the compiler, for example due to complex interactions of APIs or simply due to repetitive code structures from code generators. Other solutions that make recursion work better have been considered, but we prefer iteration for its performance and robustness. For example, Clang launches threads when it gets close to a stack limit; that has performance overhead and relies on developers correctly identifying recursion which may have limits. ## Suggested `.clang-format` contents See this repository's [`.clang-format` file](/.clang-format). ================================================ FILE: docs/project/design_style_guide.md ================================================ # Language design style guide ## Table of contents - [Background](#background) - [General](#general) - [Linking](#linking) - [Document structure](#document-structure) - [Overview and detailed design](#overview-and-detailed-design) - [Alternatives considered](#alternatives-considered) - [References](#references) ## Background The [language design](/docs/design) documentation in the Carbon project should use a consistent style and tone, and should read as if it were written by a single author. This document describes structural, stylistic, and formatting conventions for the language design, where they have been established. ## General The language design documentation follows the [style conventions](/CONTRIBUTING.md#google-docs-and-markdown) for Carbon documentation. ## Linking - Links to issues and to complete proposals should use the text `#nnnn`, where `nnnn` is the issue number, optionally followed by the proposal title, and should link to the issue or pull request on GitHub. For example, `[#123: widget painting](https://github.com/carbon-language/carbon-lang/pull/123)`. - Links to specific sections of a proposal should link to the repository copy of the proposal file, using the section title or other appropriate link text. For example, `[Painting details](/proposals/p0123.md#painting-details)` ## Document structure Documents within the language design should usually be divided into the following sections, with suitable level-two (`##`) headings: - **Table of contents** (auto-generated) - **TODO** (optional) - **Overview** - Zero or more detailed design sections - **Alternatives considered** - **References** ### Overview and detailed design The overview should describe the high-level concepts of this area of the design, following BLUF principles. Where the overview does not fully cover the detailed design, additional sections can be added as needed to more completely describe the design. The aim of these sections is to describe the design choices that have been made, how those choices fit into the overall design of Carbon, the rationale for those choices, and how and why those choices differ from other languages to which Carbon is likely to be compared, particularly C++, Rust, and Swift. ### Alternatives considered This section should provide bullet points briefly describing alternative designs that were considered, along with references to the proposals in which those designs were discussed. For example: ```md - [Paint widgets from bottom to top](/proposals/p0123.md#alternatives-considered). ``` ### References This section should provide bullet points linking to the following: - External documents providing background on the topic or additional useful information. - Each proposal that contributed to the design described in this document. For example: ```md - [Wikipedia example page](https://en.wikipedia.org/wiki/Wikipedia:Example) - Proposal [#123: widget painting](https://github.com/carbon-language/carbon-lang/pull/123). ``` Links to related parts of the design should be included inline, where relevant, not in the references section. ================================================ FILE: docs/project/difficulties_improving_cpp.md ================================================ # Difficulties improving C++ C++ is the dominant programming language for the performance critical software our goals prioritize. The most direct way to deliver a modern and excellent developer experience for those use cases and developers would be to improve C++. Improving C++ to deliver the kind of experience developers expect from a programming language today is difficult in part because **C++ has decades of technical debt** accumulated in the design of the language. It inherited the legacy of C, including [textual preprocessing and inclusion](https://clang.llvm.org/docs/Modules.html#problems-with-the-current-model). At the time, this was essential to C++'s success by giving it instant and high quality access to a large C ecosystem. However, over time this has resulted in significant technical debt ranging from [integer promotion rules](https://shafik.github.io/c++/2021/12/30/usual_arithmetic_confusions.html) to complex syntax with "[the most vexing parse](https://en.wikipedia.org/wiki/Most_vexing_parse)". **C++ has also prioritized backwards compatibility** including both syntax and [ABI](https://en.wikipedia.org/wiki/Application_binary_interface). This is heavily motivated by preserving its access to existing C and C++ ecosystems, and forms one of the foundations of common Linux package management approaches. A consequence is that rather than changing or replacing language designs to simplify and improve the language, features have overwhelmingly been added over time. This both creates technical debt due to complicated feature interaction, and fails to benefit from on cleanup opportunities in the form of replacing or removing legacy features. Carbon is exploring significant backwards incompatible changes. It doesn't inherit the legacy of C or C++ directly, and instead is starting with solid foundations, like a modern generics system, modular code organization, and consistent, simple syntax. Then, it builds a simplified and improved language around those foundational components that remains both interoperable with and migratable from C++, while giving up transparent backwards compatibility. This is fundamentally **a successor language approach**, rather than an attempt to incrementally evolve C++ to achieve these improvements. Another challenge to improving C++ in these ways is the current evolution process and direction. A key example of this is the committee's struggle to converge on a clear set of high-level and long-term goals and priorities aligned with [ours](https://wg21.link/p2137). When [pushed](https://wg21.link/p1863) to address [the technical debt caused by not breaking the ABI](https://wg21.link/p2028), **C++'s process [did not reach any definitive conclusion](https://cor3ntin.github.io/posts/abi/#abi-discussions-in-prague)**. This both failed to meaningfully change C++'s direction and priorities towards improvements rather than backwards compatibility, and demonstrates how the process can fail to make directional decisions. Beyond C++'s evolution direction, the mechanics of the process also make improving C++ difficult. **C++'s process is oriented around standardization rather than design**: it uses a multiyear waterfall committee process. Access to the committee and standard is restricted and expensive, attendance is necessary to have a voice, and decisions are made by live votes of those present. The committee structure is designed to ensure representation of nations and companies, rather than building an inclusive and welcoming team and community of experts and people actively contributing to the language. Carbon has a more accessible and efficient [evolution process](evolution.md) built on open-source principles, processes, and tools. Throughout the project, we explicitly and clearly lay out our [goals and priorities](goals.md) and how those directly shape our decisions. We also have a clear [governance structure](evolution.md#governance-structure) that can make decisions rapidly when needed. The open-source model enables the Carbon project to expand its scope beyond just the language. We will build a holistic collection of tools that provide a rich developer experience, ranging from the compiler and standard library to IDE tools and more. **We will even try to close a huge gap in the C++ ecosystem with a built-in package manager.** Carbon is particularly focused on a specific set of [goals](goals.md). These will not align with every user of C++, but have significant interest across a wide range of users that are capable and motivated to evolve and modernize their codebase. Given the difficulties posed by C++'s technical debt, sustained priority of backwards compatibility, and evolution process, we wanted to explore an alternative approach to achieve these goals -- through a backwards-incompatible successor language, designed with robust support for interoperability with and migration from C++. We hope other efforts to incrementally improve C++ continue, and would love to share ideas where we can. ================================================ FILE: docs/project/evolution.md ================================================ # Evolution and governance ## Table of contents - [Overview](#overview) - [Proposals](#proposals) - [Life of a proposal](#life-of-a-proposal) - [Proposal roles](#proposal-roles) - [Proposal authors](#proposal-authors) - [Community](#community) - [Active contributors](#active-contributors) - [Carbon leads](#carbon-leads) - [When to write a proposal](#when-to-write-a-proposal) - [Proposal PRs](#proposal-prs) - [What goes in the proposal document](#what-goes-in-the-proposal-document) - [Open questions](#open-questions) - [Review and RFC on proposal PRs](#review-and-rfc-on-proposal-prs) - [Blocking issues](#blocking-issues) - [Discussion on blocking issues](#discussion-on-blocking-issues) - [Governance structure](#governance-structure) - [Carbon leads](#carbon-leads-1) - [Subteams](#subteams) - [Painter](#painter) - [Adding and removing governance members](#adding-and-removing-governance-members) - [Acknowledgements](#acknowledgements) ## Overview Carbon's evolution process uses [proposals](#proposals) to evaluate and approve [significant changes](#when-to-write-a-proposal) to the project or language. This process is designed to: - Ensure these kinds of changes can receive feedback from the entire community. - Resolve questions and decide direction efficiently. - Create a clear log of the rationale for why the project and language have evolved in particular directions. When there are questions, concerns, or issues with a proposal that need to be resolved, Carbon uses its [governance](#governance-structure) system of [Carbon leads](#carbon-leads-1) to decide how to move forward. Leads are fundamentally responsible for encouraging Carbon's ongoing and healthy evolution and so also take on the critical steps of the evolution process for proposals. ## Proposals These are primarily structured as GitHub pull requests that use a somewhat more formal document structure and process to ensure changes to the project or language are well explained, justified, and reviewed by the community. ### Life of a proposal - Proposals consist of a PR (pull request) in GitHub that adds a document to the [`proposals/` directory](/proposals/) following [the template](/proposals/scripts/template.md). - Proposal PRs start in draft mode. When proposal PRs are ready, click on ["Ready for review"](https://docs.github.com/en/github/collaborating-with-issues-and-pull-requests/changing-the-stage-of-a-pull-request#marking-a-pull-request-as-ready-for-review). This will: - Route the proposal to a Carbon lead for review. - Send the proposal as a broad RFC to the community. - Add the "proposal rfc" label for tracking. - Contributors are encouraged to react with a _thumbs-up_ to proposal PRs if they are generally interested and supportive of the high-level direction based on title and summary. Similarly, other reactions are encouraged to help surface contributor's sentiment. - We use GitHub issues to discuss and track _blocking issues_ with proposals, such as open questions or alternative approaches that may need further consideration. These are assigned to carbon-leads to decide. - A [Carbon lead](#carbon-leads-1) will be assigned to a proposal PR. They are responsible for the basic review (or delegating that) as well as ultimately approving the PR. - The assigned lead should ensure that there is a reasonable degree of consensus among the contributors outside of the identified blocking issues. Contributors should have a reasonable chance to raise concerns, and where needed they should become blocking issues. Community consensus isn't intended to be perfect, and is ultimately a judgment call by the lead. When things are missed or mistakes are made here, we should just revert or fix-forward as usual. - Once a reasonable degree of community consensus is reached and the assigned lead finishes code review, the lead should [approve](/docs/project/code_review.md#approving-the-change) the PR. Any outstanding high-level concerns should be handled with blocking issues. - Optionally, the assigned lead can file a blocking issue for a one-week final comment period when they approve. This is rarely needed, and only when it is both useful and important for the proposal to give extra time for community comments. - The leads are responsible for resolving any blocking issues for a proposal PR, including the one-week comment period where resolving it indicates comments arrived which require the proposal to undergo further review. - The proposal PR can be merged once the assigned lead approves, all blocking issues have been decided, and any related decisions are incorporated. - If the leads choose to defer or reject the proposal, the reviewing lead should explain why and close the PR. ### Proposal roles It is also useful to see what the process looks like for different roles within the community. These perspectives are also the most critical to keep simple and easily understood. #### Proposal authors For proposal authors, this should feel like a code review, with some broken out issues for longer discussion: - Create a proposal document and draft PR following [the template](/proposals/scripts/template.md). - [new_proposal.py](/proposals/scripts/new_proposal.py) helps create templated PRs. - If you have open questions, filing [blocking issues](#blocking-issues) while preparing the PR can help resolve them quickly. - When ready, click on ["Ready for review"](https://docs.github.com/en/github/collaborating-with-issues-and-pull-requests/changing-the-stage-of-a-pull-request#marking-a-pull-request-as-ready-for-review) in GitHub. This will: - Route the proposal to a Carbon lead for review. - Send the proposal as a broad RFC to the community. - Add the "proposal rfc" label for tracking. - Address comments where you can and they make sense. - If you don't see an obvious way to address comments, that's OK. - It's great to engage a bit with the commenter to clarify their comment or why you don't see an obvious way to address it, just like you would [in code review](/docs/project/code_review.md#responding-to-review-comments). - If the commenter feels this is important, they can move it to a blocking issue for a longer discussion and resolution from the leads. - You don't need to try to resolve everything yourself. - Incorporate any changes needed based on the resolution of blocking issues. Once the leads have provided a resolution, it's important to make progress with that direction. - When you both have [approval](/docs/project/code_review.md#approving-the-change) from the assigned lead and the last blocking issue is addressed, merge! - If you end up making significant changes when incorporating resolved issues after the approval from the assigned lead, circle back for a fresh approval before merging, just like you would with code review. #### Community - We use the ["proposal rfc" label](https://github.com/carbon-language/carbon-lang/pulls?q=is%3Apr+is%3Aopen+label%3A%22proposal+rfc%22) to track proposals that are in RFC. - Anyone that is interested can participate once a proposal is ready for review and in RFC. - It's OK to only comment when particularly interested in a proposal, or when asked by one of the leads to help ensure thorough review. Not everyone needs to participate heavily in every RFC. - PRs that are in "draft" status in GitHub are considered works-in-progress. Check with the author before spending time reviewing these, and generally avoid distracting the author with comments unless they ask for them. The proposal may be actively undergoing edits. - Read the proposal and leave comments to try to help make the proposal an improvement for Carbon. - Note that progress and improvement are more important than perfection here! - Try to make comments on proposals [constructive](/docs/project/code_review.md#writing-review-comments). Suggest how the proposal could be better if at all possible. - If there is an open question or a critical blocking issue that needs to get resolved, move it to its own issue that the PR depends on, and focus the discussion there. - The issue should focus on surfacing the important aspects of the tradeoff represented by the issue or open question, not on advocacy. #### Active contributors Everyone actively contributing to the evolution of Carbon should try to regularly: - Give a thumbs-up or other reaction on any interesting PRs out for RFC to help surface the community's sentiment around the high level idea or direction. Don't worry about "approving" or the detailed text of the proposal here. - If interested and time permitting, dive into some RFCs and provide [community feedback](#community). #### Carbon leads [Carbon leads](#carbon-leads-1) are responsible for making decisions rapidly and ensuring proposal PRs land: - Rapidly resolve all blocking issues raised across any proposals. - When assigned a specific proposal PR: - Make sure it gets both constructive general comments and good code review. - Ideally, you should directly participate in the code review, but it's fine to ask others to help. However, ultimately you have to review and approve the PR. - Escalate any blocking issues without a resolution that are slowing down the proposal to the other leads. - Evaluate whether the community has had a reasonable chance to raise concerns and there is sufficient consensus to move forward given the decisions on the blocking issues. This doesn't need to be perfect though. Here too, we prioritize progress over perfection. We can revert or fix-forward mistakes whenever necessary, especially for low-risk changes. In rare cases, an extended final comment period can be used when warranted for a proposal. - Once ready, approve and help the author merge the proposal. ### When to write a proposal Any substantive change to Carbon -- whether the language, project, infrastructure, or otherwise -- should be done through an evolution proposal. The meaning of "substantive" is subjective, but will generally include: - Any semantic or syntactic language change that isn't fixing a bug. - Major changes to project infrastructure, including additions and removals. - Rolling back an accepted proposal, even if never executed. Changes which generally will not require a proposal are: - Fixing typos or bugs that don't change the meaning or intent. - Rephrasing or refactoring documentation for easier reading. - Minor infrastructure updates, improvements, setting changes, tweaks. If you're not sure whether to write a proposal, please err on the side of writing a proposal. A team can always ask for a change to be made directly if they believe it doesn't need review. Conversely, a reviewer might also ask that a pull request instead go through the full evolution process. ### Proposal PRs A proposal PR should use the `proposal` label, have a descriptive title, and easily understood initial summary comment. Authors and leads are encouraged to edit both as necessary to ensure they give the best high-level understanding of the proposal possible. A proposal PR will include a "P-numbered" _proposal document_, `proposals/pNNNN.md`, where `NNNN` is the pull request number. This file should be based on the [proposal template file](/proposals/scripts/template.md). When writing a proposal, try to keep it brief and focused to maximize the community's engagement in it. Beyond the above structure, try to use [Inverted Pyramid]() or [BLUF]() writing style to help readers rapidly skim the material. #### What goes in the proposal document The purpose of the proposal document is to present the case for deciding to adopt the proposal. Any information that feeds into making that decision, and that should not be maintained as part of our living design documentation, belongs in the proposal document. This includes background material to introduce the problem, comparisons to any alternative designs that were considered and any other current proposals in the same area, records of informal polls taken to determine community preferences, and rationale for the decision based on the project's goals. The proposal PR can contain related changes to the Carbon project, such as updates to the design documentation. Those changes form part of the proposal, and need not be additionally described in the proposal document beyond a mention in the "Proposal" section that such changes exist. For example: ```md ## Proposal See the proposed changes to the design documents. ``` Readers of proposals are expected to consult the PR or the git commit that merged the PR in order to understand the proposed changes. The author of a proposal is not required to include changes to the design documentation as part of a proposal, and it may in some cases be preferable to decouple the proposal process from updating the design. When accepted, the proposal would then be implemented through a series of future PRs to the rest of the project. If the proposal PR defers any documentation changes in this way, the proposal document should describe what is being proposed in enough detail to validate that those future PRs properly implement the proposed direction, and the proposal PR should add "TODO" comments in the places where significant future changes will be needed (including adding new placeholder documents if needed), with links back to the proposal document. These comments should be kept close to the passages that need to be changed. This is intended to ensure that readers of the design documentation know when what they're reading is out of date, and can easily find out what has changed. For example: ```md > **TODO:** Document the redeclaration syntax `impl C.(as I)` adopted in > [p5366](/proposals/p5366.md). ``` See the `docs/design` changes in [#5606: Keep design documents current](https://github.com/carbon-language/carbon-lang/pull/5606) for additional examples of adding those comments. As with other parts of the proposal, these comments should be included in the initial PR, and updated as needed during the review. As an exception, proposals that will have a widespread, pervasive effect on the design documents (such as proposals to replace widely-used vocabulary) usually shouldn't make those changes as part of the proposal, so that changes during the review aren't an excessive burden on the proposal author. Such proposals usually shouldn't apply "TODO" comments either, because pervasive "TODO" comments are more likely to be disruptive than helpful to the reader. Instead, the proposal author should file a GitHub issue to track the need for documentation updates, and include a link to it in the proposal document, and then follow up with pull requests to make the changes after the proposal is approved by the leads. #### Open questions Feel free to factor out open questions in a proposal to issues that you assign to the leads to resolve. You can even do this before sending the proposal for review. Even after it's resolved, an open question issue can be reopened if new information comes up during the RFC. When opening issues, label them as [leads questions](https://github.com/carbon-language/carbon-lang/issues?q=is%3Aissue+is%3Aopen+label%3A%22leads+question%22). Carbon leads use this to locate and prioritize the issue for resolution. ### Review and RFC on proposal PRs When a proposal PR is assigned to the [carbon-leads GitHub group](https://github.com/orgs/carbon-language/teams/carbon-leads), one of them will be assigned the PR. They are responsible for helping land that proposal, or explaining why the project won't move forward in that direction. The assigned lead is also ultimately responsible for the code review on the PR. Proposals sent for review are also sent as an RFC to the entire community. All active Carbon contributors are strongly encouraged to regularly skim the title and summary comment of proposals under RFC that are interesting to them. They should use GitHub reactions, including at least a thumbs-up, to show their interest and enthusiasm about the proposal, and help encourage the author. Writing proposals is _extremely hard work_, and we need to clearly show both interest in the proposed direction of Carbon and appreciation for the work put into the proposal. This is not about _approving_ the proposal, or any of its details. It is completely fine and coherent to both give a thumbs-up to a proposal _and_ provide a serious, blocking issue that needs to be resolved. _Anyone_ in the community is welcome to participate in the RFC in detail if interested. However, not everyone needs to participate in every RFC. If a proposal is already getting actively and thoroughly reviewed, feel free to focus your time on other proposals with fewer commenters. Even if there are issues or problems discovered later, we can always fix them with follow-up proposals. Both code review and high-level design comments are welcome. If an open question comes up or a high-level blocking issue is uncovered, feel free to move it to its own GitHub issue and assign it to the leads to resolve. That issue is also a good place to focus discussion on that specific topic rather than the main PR. The assigned lead should approve proposals once the following criteria are met: - It looks good from a code review perspective. - At least three thumbs-up reactions showing general community interest. - The community has had a sufficient opportunity to review the proposed change, given its scope and complexity. - Any remaining blocking issues are reasonably likely to resolve in a way that allows the proposal to move forward. It is fine if some are not fully decided, but a lead shouldn't approve a proposal that's unlikely to move forward. The last two criteria are fundamentally judgement calls for the lead to make, and we don't try to formulate a rigid or fixed bar for them. If resolving the blocking issues requires significant changes, the author should also get a fresh approval from the assigned lead after those changes, just like they would with code review. The assigned lead may also request a final comment period for the community when approving. This signals to the community that the proposal is likely to be merged once the blocking issues are resolved, and any remaining concerns need to be surfaced. The goal is to help uncover concerns that were hidden until it was clear that the proposal is likely to move forward. However, requesting a final comment period is not the default; the assigned lead should only do this when there is some reason to expect further community comment is especially important to solicit. Common cases to consider are contentious, complex, or dramatic changes to the language or project. Ultimately, whether this is important is a judgement call for the lead. This will be modeled by filing a blocking issue that resolves in one week when approving. This issue will also explain the motivation for requesting a final comment period. ### Blocking issues We use blocking GitHub issues to track open questions or other discussions that the leads are asked to resolve. Any time a blocking issue is filed, that issue forms both the primary discussion thread and where the leads signal how it is resolved. We use issues both to track that there is a specific resolution expected and that there may be dependencies. We label blocking issues as [leads questions](https://github.com/carbon-language/carbon-lang/issues?q=is%3Aissue+is%3Aopen+label%3A%22leads+question%22). These issues can be created at any time and by any one. Issues can be created while the proposal is being drafted in order to help inform specific content that should go into the proposal. It is even fine to create an issue first, even before a proposal exists, as an open question about whether to produce a particular proposal, or what a proposal that is being planned should say. For issues which don't (yet) have a specific proposal PR associated with them, at some point the leads may ask that a proposal be created to help collect in a more cohesive place a written overview of the issue and related information, but this process need not be strictly or rigidly bound to having proposal text. Avoid using issues for things that are just requests or suggestions on a proposal PR. If in doubt, start off with a simple comment on the PR and see if there is any disagreement -- everyone may already be aligned and agree. When a comment does seem worth turning into an issue, don't worry about that as the author or the commenter. Getting the leads to resolve disagreement isn't a bad thing for anyone involved. This should be seen as a friendly way to move the discussion out to its own forum where it'll get resolved, and focus the PR on improving the proposal and getting it ready to merge. When an issue is created from a discussion on a PR, and after the discussion on the _issue_ all the original parties come to a happy agreement, it's totally OK to close the issue and move back to the code review in the PR. Anyone who would prefer the leads to still chime in can re-open the issue and the leads will follow up, even if it's only to get confirmation that everyone _did_ end up happy with the resolution. At the end of the day, while it's fine to resolve an issue that _everyone_ actually ended up agreeing about (maybe once some confusion is addressed), ultimately the leads are responsible for resolving these issues and there is no pressure on anyone else to do so. #### Discussion on blocking issues Discussion on these issues, especially contentious ones, should endeavor to focus on surfacing information and highlighting the nature of the tradeoff implied by the decisions available. This is in contrast to focusing on advocacy or persuasion. The goal of the issues shouldn't be to persuade or convince the leads to make a specific decision, but to give the leads the information they need to make the best decision for Carbon. It's fine that some people have a specific belief of which decision would be best; however, framing their contributions to the discussion as surfacing the information that underpins that belief will make the discussion more constructive, welcoming, and effective. Overall, everyone should strive to focus on data-based arguments to the extent they can, minimizing their use of appeals to emotion or excessive rhetoric. None of this should preclude gathering information like polls of opinion among groups, or signaling agreement. Where community members stand and how many agree with that stance on any issue _is_ information, and useful to surface. ## Governance structure ### Carbon leads Carbon leads are responsible for reviewing proposals and [setting Carbon's roadmap](roadmap_process.md) and managing evolution. This team should broadly understand both the users of Carbon and the project itself in order to factor different needs, concerns, and pressures into a [consensus decision-making process](https://en.wikipedia.org/wiki/Consensus_decision-making). While leads may approve proposals individually, they should decide on issues raised to them using [blocking consensus](https://en.wikipedia.org/wiki/Consensus_decision-making#Blocking) with a quorum of two. Carbon's current leads are: - [chandlerc](https://github.com/chandlerc) - [KateGregory](https://github.com/KateGregory) - [zygoloid](https://github.com/zygoloid) #### Subteams As Carbon grows, the leads may decide to form subteams that provide leadership for specific areas. These subteams are expected to largely organize in a similar fashion to the Carbon leads, with a more narrow focus and scope. Subteam decisions may be escalated to the Carbon leads. ### Painter Whenever possible, we want Carbon to make syntax and other decisions based on understanding its users, data, and the underlying goals of the language. However, there will be times when those don't provide a clear cut rationale for any particular decision -- all of the options are fine/good and someone simply needs to choose which color to paint the bikeshed. The goal of the painter role is to have a simple way to quickly decide these points. Leads and teams may defer a decision to the painter if there is a consensus that it is merely a bikeshed in need of paint. They may also open an issue to revisit the color with data and/or user studies of some kind. This allows progress to be unblocked while also ensuring we return to issues later and attempt to find more definite rationale. The painter is a single person in order to keep decisions around taste or aesthetics reasonably consistent. The current painter is: - [chandlerc](https://github.com/chandlerc) ### Adding and removing governance members Any member of Carbon governance may step down or be replaced when they are no longer able to contribute effectively. The Carbon leads can nominate and decide on adding, removing, or replacing members using the usual evolution processes. ## Acknowledgements Our governance and evolution process is influenced by the [Rust](https://github.com/rust-lang/rfcs), [Swift](https://swift.org/contributing/), and C++ processes. Many thanks to these communities for providing a basis. ================================================ FILE: docs/project/faq.md ================================================ # Project FAQ ## Table of contents - [What is Carbon?](#what-is-carbon) - [What is Carbon's status?](#what-is-carbons-status) - [How soon can we use Carbon?](#how-soon-can-we-use-carbon) - [Why make Carbon public while it's still an experiment?](#why-make-carbon-public-while-its-still-an-experiment) - [How complete is Carbon's design?](#how-complete-is-carbons-design) - [How many people are involved in Carbon?](#how-many-people-are-involved-in-carbon) - [Is there a demo?](#is-there-a-demo) - [Where should I ask questions about Carbon Language?](#where-should-i-ask-questions-about-carbon-language) - [Why isn't there a Carbon Language logo?](#why-isnt-there-a-carbon-language-logo) - [Why build Carbon?](#why-build-carbon) - [Why is performance critical?](#why-is-performance-critical) - [What level of C++ interoperability is expected?](#what-level-of-c-interoperability-is-expected) - [What would migrating C++ code to Carbon look like?](#what-would-migrating-c-code-to-carbon-look-like) - [What alternatives did you consider? Why did they not work?](#what-alternatives-did-you-consider-why-did-they-not-work) - [Why not improve C++?](#why-not-improve-c) - [Why not fork C++?](#why-not-fork-c) - [Why not Rust?](#why-not-rust) - [If you can use Rust, ignore Carbon](#if-you-can-use-rust-ignore-carbon) - [Why is adopting Rust difficult for C++ codebases?](#why-is-adopting-rust-difficult-for-c-codebases) - [Why not a garbage collected language, like Java, Kotlin, or Go?](#why-not-a-garbage-collected-language-like-java-kotlin-or-go) - [How were specific feature designs chosen?](#how-were-specific-feature-designs-chosen) - [Why aren't `<` and `>` used as delimiters?](#why-arent--and--used-as-delimiters) - [Why do variable declarations have to start with `var` or `let`?](#why-do-variable-declarations-have-to-start-with-var-or-let) - [Why do variable declarations have to have types?](#why-do-variable-declarations-have-to-have-types) - [How will Carbon work?](#how-will-carbon-work) - [What compiler infrastructure is Carbon using?](#what-compiler-infrastructure-is-carbon-using) - [How will Carbon's bidirectional C++ interoperability work?](#how-will-carbons-bidirectional-c-interoperability-work) - [How do Carbon generics differ from templates?](#how-do-carbon-generics-differ-from-templates) - [What is Carbon's memory model?](#what-is-carbons-memory-model) - [How will Carbon achieve memory safety?](#how-will-carbon-achieve-memory-safety) - [How will language version upgrades work?](#how-will-language-version-upgrades-work) - [How will the Carbon _project_ work?](#how-will-the-carbon-project-work) - [Where does development occur?](#where-does-development-occur) - [How does Carbon make decisions?](#how-does-carbon-make-decisions) - [What happens when a decision was wrong?](#what-happens-when-a-decision-was-wrong) - [What license does Carbon use?](#what-license-does-carbon-use) - [Why make Carbon open source?](#why-make-carbon-open-source) - [Why does Carbon have a CLA?](#why-does-carbon-have-a-cla) - [Who pays for Carbon's infrastructure?](#who-pays-for-carbons-infrastructure) - [How can I contribute to Carbon?](#how-can-i-contribute-to-carbon) - [What are the prerequisites for contributing to Carbon Language's design and tools?](#what-are-the-prerequisites-for-contributing-to-carbon-languages-design-and-tools) - [When do we revisit decisions or reopen discussions?](#when-do-we-revisit-decisions-or-reopen-discussions) - [What can I do if I disagree with a design decision?](#what-can-i-do-if-i-disagree-with-a-design-decision) - [How can I best say "I like X" or "I don't like X"?](#how-can-i-best-say-i-like-x-or-i-dont-like-x) ## What is Carbon? The [Carbon Language](/README.md) is an experimental successor to C++. It is an effort to explore a possible future direction for the C++ language given the [difficulties improving C++](difficulties_improving_cpp.md). ## What is Carbon's status? [Carbon is still an experiment.](/README.md#project-status) There remain significant open questions that we need to answer before the project can consider becoming a production effort. For now, we're focused on exploring this direction and gaining information to begin answering these questions. - [Project status](/README.md#project-status) - [Roadmap](roadmap.md) ### How soon can we use Carbon? Carbon is still years away — even if the experiment succeeds, it's unlikely that it will be ready for serious or production use in the next few years. Everything here is part of a long-term investigation. ### Why make Carbon public while it's still an experiment? One of the critical questions we need to answer as part of this experiment is whether the direction we're exploring with Carbon has both broad and significant interest for the industry at large. We feel like this is best answered by developing the language openly, publicly, and with broad participation. ### How complete is Carbon's design? Key design questions for the initial [0.1 language design](milestones.md#milestone-01-a-minimum-viable-product-mvp-for-evaluation) have been resolved, and we are actively working on toolchain implementation. See our [roadmap](roadmap.md) for an overview of current work. References: - [Carbon design overview](/docs/design/README.md) - [Roadmap](roadmap.md) ### How many people are involved in Carbon? Prior to going public, Carbon has had a couple dozen people involved. [GitHub Insights](https://github.com/carbon-language/carbon-lang/pulse/monthly) provides activity metrics. ### Is there a demo? Yes! See the [getting started](/README.md#getting-started) tip for current advice. ### Where should I ask questions about Carbon Language? Please ask questions and hold discussions either by using [GitHub Discussions](https://github.com/carbon-language/carbon-lang/discussions) or [#language-questions on Discord](https://discord.com/channels/655572317891461132/998959756045713438). GitHub Issues should be reserved for missing features, bugs, and anything else that is fixable by way of a Pull Request. ### Why isn't there a Carbon Language logo? Establishing a Carbon Language logo isn't a priority right now. Remember that this project is an _experiment_, and so we think it's best to concentrate efforts on ensuring that the language succeeds at its goals instead. We have a few drafts in the works, but it requires a fair amount of work to get right, and getting it wrong is costly, so we won't be adding one in the near future. Don't suggest logos, because we need to be careful about how we create one. ## Why build Carbon? See the [project README](/README.md#why-build-carbon) for an overview of the motivation for Carbon. This section dives into specific questions in that space. ### Why is performance critical? Performance is critical for many users today. A few reasons are: - **Cost savings**: Organizations with large-scale compute needs [care about software performance](https://www.microsoft.com/en-us/research/publication/theres-plenty-of-room-at-the-top/) because it reduces hardware needs. - **Reliable latency**: Environments with specific latency needs or [concerns with bounding tail latency](https://research.google/pubs/pub40801/) need to be able to control and improve their latency. - **Resource constraints**: Many systems have constrained CPU or memory resources that require precise control over resource usage and performance. ### What level of C++ interoperability is expected? Carbon code will be able to call C++, and the other way around, without overhead. You will be able to migrate a single library to Carbon within a C++ application, or write new Carbon on top of your existing C++ investment. While Carbon's interoperability may not cover every last case, most C++ style guides (such as the C++ Core Guidelines or Google C++ Style Guide) steer developers away from complex C++ code that's more likely to cause issues, and we expect the vast majority of code to interoperate well. For example, considering a pure C++ application:
A snippet of C++ code. Follow the link to read it. It's possible to migrate a single function to Carbon: A snippet of mixed Carbon and C++ code. Follow the link to read it. References: - [Interoperability philosophy and goals](/docs/design/interoperability/philosophy_and_goals.md) - [How will Carbon's bidirectional C++ interoperability work?](#how-will-carbons-bidirectional-c-interoperability-work) ### What would migrating C++ code to Carbon look like? Migration support is a [key long-term goal for Carbon](goals.md#interoperability-with-and-migration-from-existing-c-code). If a migration occurs, we anticipate: - Migration tools that automatically translate C++ libraries to Carbon at the file or library level with minimal human assistance. - Bidirectional C++ interoperability that allows teams to migrate libraries in any order they choose without performance concerns or maintaining interoperability wrappers. - Test-driven verification that migrations are correct. ## What alternatives did you consider? Why did they not work? ### Why not improve C++? A lot of effort has been invested into improving C++, but [C++ is difficult to improve](difficulties_improving_cpp.md). For example, although [P2137](https://wg21.link/p2137r0) was not accepted, it formed the basis for [Carbon's goals](goals.md). ### Why not fork C++? While we would like to see C++ improve, we don't think that forking C++ is the right path to achieving that goal. A fork could create confusion about what code works with standard C++. We believe a _successor_ programming language is a better approach because it gives more freedom for Carbon's design while retaining the existing C++ ecosystem investments. ### Why not Rust? #### If you can use Rust, ignore Carbon If you want to use Rust, and it is technically and economically viable for your project, you should use Rust. In fact, if you can use Rust or any other established programming language, you should. Carbon is for organizations and projects that heavily depend on C++; for example, projects that have a lot of C++ code or use many third-party C++ libraries. We believe that Rust is an excellent choice for writing software within the pure Rust ecosystem. Software written in Rust has properties that neither C++ nor Carbon have. When you need to call other languages from Rust, RPCs are a good option. Rust is also good for using APIs implemented in a different language in-process, when the cost of maintaining the FFI boundary is reasonable. When the foreign language API is large, constantly changes, uses advanced C++ features, or [makes architectural choices that are incompatible with safe Rust](#why-is-adopting-rust-difficult-for-c-codebases), maintaining a C++/Rust FFI may not be economically viable today (but it is an area of active research: [cxx](https://crates.io/crates/cxx), [autocxx](https://crates.io/crates/autocxx), [Crubit](https://github.com/google/crubit/blob/main/docs/design/design.md)). The Carbon community is looking for a language that existing, large, monolithic C++ codebases can incrementally adopt and have a prospect of migrating away from C++ completely. We would be very happy if Rust could be this language. However, we are not certain that: - Idiomatic, safe Rust can seamlessly integrate into an existing C++ codebase, similarly to how TypeScript code can be added to a large existing JavaScript codebase. - Developers can incrementally migrate existing C++ code to Rust, just like they can migrate JavaScript to TypeScript one file at a time, while keeping the project working. See [Carbon's goals](goals.md#interoperability-with-and-migration-from-existing-c-code) for an in-depth discussion of Carbon's vision for C++/Carbon interop and migration. #### Why is adopting Rust difficult for C++ codebases? Large existing C++ codebases almost certainly made architectural choices that are incompatible with safe Rust. Specifically: - Seamless interop where existing, unmodified **C++ APIs are made callable from safe Rust** requires the C++ code to follow borrow checking rules at the API boundary. - To reduce the amount of Rust-side compile-time checking that makes interop difficult, C++ APIs can be exposed to Rust with pointers instead of references. However, that forces users to write _unsafe_ Rust, which can be even more tricky to write than C++ because it has new kinds of UB compared to C++; for example, [stacked borrows violations](https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/stacked-borrows.md). - Seamless interop where **safe Rust APIs are made callable from C++** requires C++ users to follow Rust borrow checking rules. - **Incremental migration of C++ to safe Rust** means that C++ code gets converted to Rust without major changes to the architecture, data structures, or APIs. However Rust imposes stricter rules than C++, disallowing some design choices that were valid in C++. Therefore, the original C++ code must follow Rust rules before attempting a conversion. - Original C++ code must be structured in such a way that the resulting Rust code passes borrow checking. C++ APIs and data structures are not designed with this in mind. - Migrating C++ to _unsafe_ Rust would still require the code to follow Rust's [reference exclusivity](https://doc.rust-lang.org/book/ch04-02-references-and-borrowing.html#the-rules-of-references) and stacked borrows rules. ### Why not a garbage collected language, like Java, Kotlin, or Go? If you can use one of these languages, you absolutely should. Garbage collection provides dramatically simpler memory management for developers, but at the expense of performance. The performance cost can range from direct runtime overhead to significant complexity and loss of _control_ over performance. This trade-off makes sense for many applications, and we actively encourage using these languages in those cases. However, we need a solution for C++ use-cases that require its full performance, low-level control, and access to hardware. ## How were specific feature designs chosen? Throughout the design, we include 'Alternatives considered' and 'References' sections which can be used to research the decision process for a particular design. ### Why aren't `<` and `>` used as delimiters? [One of our goals for Carbon](goals.md#fast-and-scalable-development) is that it should support parsing without contextual or semantic information, and experience with C++ has shown that using `<` as both a binary operator and an opening delimiter makes that goal difficult to achieve. For example, in C++, the expression `a(c)` could parse as either a function call with a template argument `b` and an ordinary argument `c`, or as a chained comparison `(a < b) > (c)`. In order to resolve the ambiguity, the compiler has to perform name lookup on `a` to determine whether there's a function named `a` in scope. It's also worth noting that Carbon [doesn't use _any_ kind of brackets](/docs/design/README.md#checked-and-template-parameters) to mark template- or checked-generic parameters, so if Carbon had angle brackets, they would mean something different than they do in C++, which could cause confusion. We do use square brackets to mark _deduced_ parameters, as in: ``` fn Sort[T:! Comparable](a: Vector(T)*) ``` But deduced parameters aren't the same thing as template parameters. In particular, deduced parameters are never mentioned at the callsite, so those square brackets are never part of the expression syntax. See [Proposal #676: `:!` generic syntax](/proposals/p0676.md) for more background on how and why we chose our current compile-time parameter syntax. ### Why do variable declarations have to start with `var` or `let`? In Carbon, a declaration of a single variable looks like this: ``` var the_answer: i32 = 42; ``` But this is just the most common case. The syntax between `var` and `=` can be any [irrefutable pattern](/docs/design/README.md#patterns), not just a single variable binding. For example: ``` var ((x: i32, _: i32), y: auto) = ((1, 2), (3, 4)); ``` This code is valid, and initializes `x` to `1` and `y` to `(3, 4)`. In the future, we will probably also support destructuring structs in a similar way, and many other kinds of patterns are possible. Now consider how that example would look if the `var` token were not required: ``` ((x: i32, _: i32), y: auto) = ((1, 2), (3, 4)); ``` With this example, the parser would need to look four tokens ahead to determine that it's parsing a variable declaration rather than an expression. With more deeply-nested patterns, it would have to look ahead farther. Avoiding this sort of unbounded lookahead is an important part of our [fast and scalable development](goals.md#fast-and-scalable-development) goal. ### Why do variable declarations have to have types? As discussed above, Carbon variable declarations are actually doing a form of pattern matching. In a declaration like this: ``` var the_answer: i32 = 42; ``` `the_answer: i32` is an example of a _binding pattern_, which matches any value of the appropriate type, and binds the given name to it. The `: i32` can't be omitted, because `the_answer` on its own is an expression, and any Carbon expression is also a valid pattern, which matches if the value being matched is equal to the value of the expression. So `var the_answer = 42;` would try to match `42` with the value of the expression `the_answer`, which requires a variable named `the_answer` to already exist. The pattern matching proposal details alternative [shorthand for `auto`](/proposals/p2188.md#shorthand-for-auto) and the tradeoffs that were considered. References: - [Pattern matching design](/docs/design/pattern_matching.md) ## How will Carbon work? ### What compiler infrastructure is Carbon using? Carbon is being built using LLVM, and is expected to have Clang dependencies for [interoperability](#how-will-carbons-bidirectional-c-interoperability-work). ### How will Carbon's bidirectional C++ interoperability work? The Carbon toolchain will compile both Carbon and C++ code together, in order to make the interoperability [seamless](#what-level-of-c-interoperability-is-expected). For example, for `import Cpp library ""`, Carbon will: - Call into Clang to load the AST of the `vector` header file. - Analyze the AST for public APIs, which will be turned into names that can be accessed from Carbon; for example, `std::vector` is `Cpp.std.vector` in Carbon. - Use Clang to instantiate the `Cpp.std.vector` template when parameterized references occur in Carbon code. - In other words, C++ templates will be instantiated using standard C++ mechanisms, and the instantiated versions are called by Carbon code. Some code, such as `#define` preprocessor macros, will not work as well. C++ allows arbitrary content in a `#define`, and that can be difficult to translate. As a consequence, this is likely to be a limitation of interoperability and left to migration. ### How do Carbon generics differ from templates? Carbon's [generic programming](https://en.wikipedia.org/wiki/Generic_programming) support will handle both templates (matching C++) and checked generics (common in other languages: Rust, Swift, Go, Kotlin, Java, and so on). The key difference between the two is that template arguments can only finish type-checking _during_ instantiation, whereas checked generics specify an interface with which arguments can finish type-checking _without_ instantiation. This has a couple of important benefits: - Type-checking errors for checked generics happen earlier, making it easier for the compiler to produce helpful diagnostics. - Checked-generic functions can generate less compiled output, allowing compilation with many uses to be faster. - For comparison, template instantiations are a major factor for C++ compilation latency. Although Carbon will prefer checked generics over templates, templates are provided for migration of C++ code. References: - [Generics: Goals: Better compiler experience](/docs/design/generics/goals.md#better-compiler-experience) - [Generics: Terminology: Checked versus template parameters](/docs/design/generics/terminology.md#checked-versus-template-parameters) ### What is Carbon's memory model? Carbon will match C++'s memory model closely in order to maintain zero-overhead interoperability. There may be some changes made as part of supporting memory safety, but performance and interoperability will constrain flexibility in this space. ### How will Carbon achieve memory safety? See our [safety design](/docs/design/safety). ### How will language version upgrades work? Carbon will provide tooling to assist upgrades of code in response to language syntax changes, similar to [C++ to Carbon migration tooling](#what-would-migrating-c-code-to-carbon-look-like). For example, if a new keyword `except` is added in Carbon 1.1, an upgrade tool might be provided that would accept Carbon 1.0 code and replace `except` identifier uses with `r#except` raw identifiers ([like Rust provides](https://doc.rust-lang.org/rust-by-example/compatibility/raw_identifiers.html)), automatically fixing the conflict. While Carbon remains in early development, upgrade tooling is not ready. It is instead a consideration for declaring Carbon [ready for use](#how-soon-can-we-use-carbon). This upgrade approach stands in comparison to enforcing [backwards or forwards compatibility](goals.md#backwards-or-forwards-compatibility). ## How will the Carbon _project_ work? ### Where does development occur? Carbon is using GitHub for its repository and code reviews. Most non-review discussion occurs on our [Discord server](https://discord.gg/ZjVdShJDAs). If you're interested in contributing, you can find more information in our [Contributing file](/CONTRIBUTING.md). ### How does Carbon make decisions? Any interested developer may [propose and discuss changes](evolution.md) to Carbon. The [Carbon leads](groups.md#carbon-leads) are responsible for reviewing proposals and surrounding discussion, then making decisions based on the discussion. As Carbon grows, we expect to add feature teams to distribute responsibility. The intent of this setup is that Carbon remains a community-driven project, avoiding situations where any single organization controls Carbon's direction. References: - [Contributing](/CONTRIBUTING.md) - [Evolution process](evolution.md) ### What happens when a decision was wrong? Carbon's [evolution process](evolution.md) is iterative: when we make poor decisions, we'll work to fix them. If we realize a mistake quickly, it may make sense to just roll back the decision. Otherwise, a fix will need to follow the normal evolution process, with a proposal explaining why the decision was wrong and proposing a better path forward. ### What license does Carbon use? Carbon is under the [Apache License v2.0 with LLVM Exceptions](/LICENSE). We want Carbon to be available under a permissive open source license. As a programming language with compiler and runtime library considerations, our project has the same core needs as the LLVM project for its license and we build on their work to address these by combining the [Apache License](https://spdx.org/licenses/Apache-2.0.html) with the [LLVM Exceptions](https://spdx.org/licenses/LLVM-exception.html). ### Why make Carbon open source? We believe it is important for a programming language like Carbon, if it is successful, to be developed by and for a broad community. We feel that the open source model is the most effective and successful approach for doing this. We're closely modeled on LLVM and other similar open source projects, and want to follow their good examples. We've structured the project to be attractive for industry players big and small to participate in, but also to be resilient and independent long-term. The open source model, particularly as followed by Apache and LLVM, also provides a strong foundation for handling hard problems like intellectual property and licensing with a broad and diverse group of contributors. ### Why does Carbon have a CLA? Carbon [uses a CLA](/CONTRIBUTING.md#contributor-license-agreements-clas) (Contributor License Agreement) in case we need to fix issues with the license structure in the future, something which has proven to be important in other projects. Any changes to the license of Carbon would be made very carefully and subject to the exact same decision making process as any other change to the overall project direction. Initially, Carbon is bootstrapping using Google's CLA. We are planning to create an open source foundation and transfer all Carbon-related rights to it; our goal is for the foundation setup to be similar to other open source projects, such as LLVM or Kubernetes. ### Who pays for Carbon's infrastructure? Carbon is currently bootstrapping infrastructure with the help of Google. As soon as a foundation is ready to oversee infrastructure, such as [continuous integration](https://en.wikipedia.org/wiki/Continuous_integration) and the CLA, we plan to transfer them so they are run by the community. ## How can I contribute to Carbon? There are many ways to contribute, and we appreciate all of them. Begin by reading the project's [Contributing](/CONTRIBUTING.md) page to learn more about how you can contribute. ### What are the prerequisites for contributing to Carbon Language's design and tools? **[Carbon Language isn't ready for use](#how-soon-can-we-use-carbon). This section is for people wishing to participate in designing and implementing the language.** Carbon is being designed to migrate C++ codebases and to look familiar to C++ programmers. As such, familiarity with C++ is very important. Carbon is also trying to learn from other programming languages, so having broad experience with other programming languages will be helpful to see tradeoffs in design decisions. The Carbon toolchain is being implemented in C++, and we also use Python and Starlark. As we're building off of the LLVM project, familiarity with Clang and other parts of LLVM will be advantageous, but not required. Our [contribution tools](contribution_tools.md) page documents specific tools we use when building. ### When do we revisit decisions or reopen discussions? Once a decision is made through the [evolution process](evolution.md) the community should treat it as _firmly_ decided. This doesn't mean that the decision is _definitely_ right or set in stone, it just means we'd like the community to focus time and energy on other issues in the name of progress. Sometimes, it will be appropriate to revisit a decision; for example, when there is new information introduced, a new community joins Carbon, or there is an order of magnitude growth in the community. These cases are handled as new proposals through the [evolution process](evolution.md). For example, we have done this with digit separators: we missed important _domain_ conventions and had overly restricted where separators are allowed, [an issue was filed with the new information](https://github.com/carbon-language/carbon-lang/issues/1485), and we're fixing the choice. See also the related questions [What happens when a decision was wrong?](#what-happens-when-a-decision-was-wrong), [How does Carbon make decisions?](#how-does-carbon-make-decisions), and [What can I do if I disagree with a design decision?](#what-can-i-do-if-i-disagree-with-a-design-decision). ### What can I do if I disagree with a design decision? We invite you to give us constructive feedback. Some of Carbon's design decisions are made with the _expectation_ of receiving community feedback. We understand that many decisions won't be universally popular, but we'd still like to understand the community's reaction to Carbon. We encourage you to investigate why Carbon came to be the way it is. Designs will include links to the proposals and important alternatives considered that led to them, typically linked at the bottom. Read through and understand the context and rationale behind the decisions you are concerned about. You may find that your concerns were already thoroughly discussed. If not, you will be in a better place to present your thoughts in a convincing way. Changing decisions that have come out of the [evolution process](evolution.md) involves a formal process. See [When do we revisit decisions or reopen discussions?](#when-do-we-revisit-decisions-or-reopen-discussions). For these issues in particular, please be aware that other community members may choose to not actively engage in detailed discussions, especially if the discussion seems to be revisiting points made in the past. If after reading this answer you are not sure how to proceed please feel free to ask (see [Where should I ask questions?](#where-should-i-ask-questions-about-carbon-language)). ### How can I best say "I like X" or "I don't like X"? Both Discord and GitHub Discussions allow you to give an emoji "reaction" to individual posts. If you'd like to amplify what has already been said, please use these instead of posting messages that re-state substantially the same thing. These make conversations easier to follow and understand general sentiment in discussions involving many people. ================================================ FILE: docs/project/goals.md ================================================ # Goals ## Table of contents - [Overview](#overview) - [Project goals](#project-goals) - [Community and culture](#community-and-culture) - [Language tools and ecosystem](#language-tools-and-ecosystem) - [Language goals and priorities](#language-goals-and-priorities) - [Performance-critical software](#performance-critical-software) - [Software and language evolution](#software-and-language-evolution) - [Code that is easy to read, understand, and write](#code-that-is-easy-to-read-understand-and-write) - [Practical safety and testing mechanisms](#practical-safety-and-testing-mechanisms) - [Fast and scalable development](#fast-and-scalable-development) - [Modern OS platforms, hardware architectures, and environments](#modern-os-platforms-hardware-architectures-and-environments) - [Interoperability with and migration from existing C++ code](#interoperability-with-and-migration-from-existing-c-code) - [Non-goals](#non-goals) - [Stable language and library ABI](#stable-language-and-library-abi) - [Backwards or forwards compatibility](#backwards-or-forwards-compatibility) - [Legacy compiled libraries without source code or ability to rebuild](#legacy-compiled-libraries-without-source-code-or-ability-to-rebuild) - [Support for existing compilation and linking models](#support-for-existing-compilation-and-linking-models) - [Idiomatic migration of non-modern, non-idiomatic C++ code](#idiomatic-migration-of-non-modern-non-idiomatic-c-code) - [Prioritization beyond goals](#prioritization-beyond-goals) - [Acknowledgements](#acknowledgements) ## Overview Carbon is an experiment to explore a possible, distant future for the C++ programming language designed around a specific set of goals, priorities, and use cases. A programming language is a tool, and different tools are good for different purposes. We think there is great value in priorities that differentiate Carbon from other programming languages. Stating Carbon’s priorities clearly and explicitly shapes the design of Carbon, and helps the entire community effectively evaluate and use the language. Carbon's language goals have historically been best addressed by C++, and there are large ecosystems and codebases written using C++ to these ends. Carbon should be attractive and easy for C++ developers to try out and incrementally adopt, even in individual libraries both using and used from C++ code. We expect this depends on having high-performance bidirectional interoperability with C++, excellent migration tooling, and an easy ramp-up for experienced C++ software developers. [Principles](principles/README.md) are provided to clarify these goals. Principles do not supersede goals and priorities. ## Project goals ### Community and culture Carbon has an overarching goal of promoting a healthy and vibrant community with an inclusive, welcoming, and pragmatic culture. While this may not directly affect Carbon's design, it affects how Carbon's design occurs. We cannot build a good language without a good community. As the saying goes, ["culture eats strategy for breakfast"](https://techcrunch.com/2014/04/12/culture-eats-strategy-for-breakfast/). Carbon's community, including both maintainers and users, needs to last for years and be capable of scaling up. It needs to support people working on Carbon across a wide range of companies as their full time job, but also people contributing in small fractions of their time, or as students, teachers, or as a hobby. There are several key ingredients to achieving this. **The community and project needs a code of conduct.** We want Carbon's community to be welcoming and respectful, with a deep commitment to psychological safety. We need consistent expectations for how every community member should behave, regardless of their position in the community. These expectations around conduct and behavior need to be clearly articulated both to set expectations for people joining, and to help remind and anchor us on consistent standards. It is also important that we hold ourselves accountable to these expectations and have real and meaningful mechanisms to moderate the community. When behavior steps outside of our expectations, we need tools, processes, and policies for how we will recognize and correct it. **An open, inclusive process for Carbon changes.** The community needs to be able to effectively engage in the direction and evolution of the project and language, while keeping the process efficient and effective. That means we need an open, inclusive process where everyone feels comfortable participating. Community members should understand how and why decisions are made, and have the ability to both influence them before they occur and give feedback afterward. We want to use this process to also ensure we stick to our language priorities and have clear rationales for all of our technical designs and decisions. **Being inclusive is different from including everyone.** We want to avoid excluding or marginalizing members of the community. However, we expect to inevitably make choices that benefit some Carbon community members more than others. We will provide justification for these decisions, but achieving Carbon's goals -- including that of a healthy community -- will be the guiding rule. ### Language tools and ecosystem Programming languages do not succeed in a vacuum. The Carbon project cannot merely _design_ a language in order to succeed, it must tackle the full ecosystem of tooling that makes developers effective using the language. This includes not only a compiler and standard library, but also a broad range of other tools that enable developers to be more effective, efficient, or productive. **We will provide a reference implementation.** This helps the language have a strong and consistent experience for developers and a clear onboarding process. It also enables us to carefully consider implementation considerations throughout the design of the language. However, we do _not_ want this to be seen as a replacement for a formal specification at any point. **Carbon will have a formal specification.** Fully specifying the language enables other implementations and allows us to clearly document the expected behavior of the reference implementation. This does not mean the specification defines what is "correct"; instead, the specification and reference implementation should complement each other. Any divergence is a bug that _must_ be resolved, and the specification and reference should always converge. Carbon should not have designs or specifications which do not match the practical implementation, even if that means updating designs to reflect implementation realities. Having the specification will enable better analysis of the language as a whole and the production of other partial or full implementations which match the behavior of the reference implementation. **Approachable, developer-facing documentation.** Developers shouldn't be expected to read through the specification to ramp up with Carbon. User guides and other documentation will be provided to make it easy to learn how to use Carbon. **Compelling adoption tooling.** We want to provide a compelling suite of tools out-of-the-box in order to encourage adoption of Carbon at scale where it can augment existing C++ codebases. For example, we expect a C++ -> Carbon code translator will be important. **Tooling for updating code when Carbon evolves.** As Carbon evolves over time, we expect to provide tooling to help automate and scale migrating existing Carbon code to the new version. The goal is to enable more rapid evolution of the language without the churn tax and version skew becoming unsustainable. **Developer tooling.** We need developers to be productive while reading and writing Carbon code. We expect to provide a broad suite of development oriented tools ranging from formatting and refactoring tools to [LSP](https://langserver.org/) implementations and editor integrations. We also plan to provide machine readable forms of many parts of the language, such as a grammar, to ensure consistency between tools and enable the development of tools by others. **Infrastructure to enable package management and other library ecosystem support.** The goal is to support what the ecosystem needs, regardless of the exact form this ends up taking. ## Language goals and priorities We are designing Carbon to support: - Performance-critical software - Software and language evolution - Code that is easy to read, understand, and write - Practical safety and testing mechanisms - Fast and scalable development - Modern OS platforms, hardware architectures, and environments - Interoperability with and migration from existing C++ code Many languages share subsets of these goals, but what distinguishes Carbon is their combination. Where it is necessary to make tradeoffs between these goals, we intend to prioritize them in this order. Each goal is broad, and has several facets to consider when making decisions. Below, we discuss all of these goals in more detail to give a deeper understanding of both the nature and motivation of these goals. ### Performance-critical software All software consumes resources: time, memory, compute, power, binary size, and so on. In many cases, raw resource usage is not the biggest concern. Instead, algorithmic efficiency or business logic dominates these concerns. However, there exists software where its rate of resource consumption -- its performance -- is critical to its successful operation. Another way to think about when performance is critical: would a performance regression be considered a bug? Would it even be noticed? Our goal is to support software where its performance with respect to some set of resource constraints is critical to its successful operation. This overarching goal can be decomposed into a few specific aspects. **Provide the developer control over every aspect of performance.** When faced with some performance problem, the developer should always have tools within Carbon to address it. This does not mean that the developer is necessarily concerned with ultimate performance at every moment, but in the most constrained scenarios they must be able to "open up the hood" without switching to another language. **Idiomatic code should be fast.** Developers should not regularly be required to choose between performance and readability. Although performance tuning may in rare cases require complex or surprising code, Carbon's design should ensure regular, idiomatic code usually results in high performance. **Code should perform predictably.** The reader and writer of code should be able to easily understand its expected performance, given sufficient background knowledge of the environment in which it will run. This need not be precise, but instead can use heuristics and guidelines to avoid surprise. The key priority is that performance, whether good or bad, is unsurprising to developers. Even pleasant surprises, when too frequent, can become a problem due to establishing brittle baseline performance that cannot be reliably sustained. **No need for a lower level language.** Developers should not need to leave the rules and structure of Carbon, whether to gain control over performance problems or to gain access to hardware facilities. ### Software and language evolution Titus Winters writes in "Non-Atomic Refactoring and Software Sustainability": > What is the difference between programming and software engineering? These are > nebulous concepts and thus there are many possible answers, but my favorite > definition is this: Software engineering is programming integrated over time. > All of the hard parts of engineering come from dealing with time: > compatibility over time, dealing with changes to underlying infrastructure and > dependencies, and working with legacy code or data. Fundamentally, it is a > different task to produce a programming solution to a problem (that solves the > current [instance] of the problem) versus an engineering solution (that solves > current instances, future instances that we can predict, and - through > flexibility - allows updates to solve future instances we may not be able to > predict). Carbon will prioritize being a "software engineering" language, in the above sense. We specifically are interested in dealing with the time-oriented aspects of software built in this language. We need to be prepared for substantive changes in priority over the next decade, on par with the changes experienced in the 2010s: 10x scaling of software organizations, mobile, cloud, diversification of platforms and architectures, and so on. **Support maintaining and evolving software written in Carbon for decades.** The life expectancy of some software will be long and the software will not be static or unchanging in that time. Mistakes will be made and need to be corrected. New functionality will be introduced and old functionality retired and removed. The design of Carbon must support and ease every step of this process. This ranges from emphasizing testing and continuous integration to tooling and the ability to make non-atomic changes. It also includes constraints on the design of Carbon itself: we should avoid, or at least minimize, language features that encourage unchangeable constructs. For example, any feature with a contract that cannot be strengthened or weakened without breaking the expected usage patterns is inherently hostile to refactoring. Analogously, features or conventions that require simultaneously updating all users of an API when extending it are inherently hostile towards long-term maintenance of software. **Support maintaining and evolving the language itself for decades.** We will not get the design of most language features correct on our first, second, or 73rd try. As a consequence, there must be a built-in plan and ability to move Carbon forward at a reasonable pace and with a reasonable cost. Simultaneously, an evolving language must not leave software behind to languish, but bring software forward. This requirement should not imply compatibility, but instead some migratability, likely tool-assisted. **Be mindful of legacy.** Globally, there may be as many as 50 billion lines of C++ code. Any evolution of Carbon that fails to account for human investment/training and legacy code, representing significant capital, is doomed from the start. Note that our priority is restricted to legacy source code; we do not prioritize full support of legacy object code. While that still leaves many options open, such as dedicated and potentially slower features, it does limit the degree to which legacy use cases beyond source code should shape the Carbon design. ### Code that is easy to read, understand, and write While this is perhaps the least unique among programming languages of the goals we list here, we feel it is important to state it, explain all of what we mean by it, and fit it into our prioritization scheme. Software has inherent complexity that burdens developers, especially at scale and over time. Carbon will strive to minimize that burden for reading, understanding, and writing code. The behavior of code should be easily understood, especially by those unfamiliar with the software system. Consider developers attempting to diagnose a serious outage under time pressure -- every second spent trying to understand the _language_ is one not spent understanding the _problem_. While the source code of our software may be read far more often by machines, humans are the most expensive readers and writers of software. As a consequence, we need to optimize for human reading, understanding, and writing of software, in that order. **Excellent ergonomics.** Human capabilities and limitations in the domains of perception, memory, reasoning, and decision-making affect interactions between humans and systems. Ergonomic language design takes human factors into account to increase productivity and comfort, and reduce errors and fatigue, making Carbon more suitable for humans to use. We can also say that ergonomic designs are accessible to humans. "Readability" is a related, but a more focused concept, connected to only the process of reading code. "Ergonomics" covers all activities where humans interact with Carbon: reading, writing, designing, discussing, reviewing, and refactoring code, as well as learning and teaching Carbon. A few examples: - Carbon should not use symbols that are difficult to type, see, or differentiate from similar symbols in commonly used contexts. - Syntax should be easily parsed and scanned by any human in any development environment, not just a machine or a human aided by semantic hints from an IDE. - Code with similar behavior should use similar syntax, and code with different behavior should use different syntax. Behavior in this context should include both the functionality and performance of the code. This is part of conceptual integrity. - Explicitness must be balanced against conciseness, as verbosity and ceremony add cognitive overhead for the reader, while explicitness reduces the amount of outside context the reader must have or assume. - Common yet complex tasks, such as parallel code, should be well-supported in ways that are easy to reason about. - Ordinary tasks should not require extraordinary care, because humans cannot consistently avoid making mistakes for an extended amount of time. **Support tooling at every layer of the development experience, including IDEs.** The design and implementation of Carbon should make it easy to create such tools and make them effective. Carbon should avoid syntax and textual structures that are difficult to recognize and mechanically change without losing meaning. **Support software outside of the primary use cases well.** There are surprisingly high costs for developers to switch languages. Even when the primary goal is to support performance-critical software, other kinds of software should not be penalized unnecessarily. > "The right tool for the job is often the tool you are already using -- adding > new tools has a higher cost than many people appreciate." > > -- [John Carmack](https://twitter.com/id_aa_carmack/status/989951283900514304) **Focus on encouraging appropriate usage of features rather than restricting misuse.** Adding arbitrary restrictions to prevent misuse of otherwise general features of the language can create problems when they end up interfering with unexpected or rare but still appropriate usages. Instead, Carbon should focus on enabling appropriate and effective usage of features, and creating incentives around those. What seems initially like a "misuse" of a feature may be critical for some rare or future use case. Put differently, we will not always be able to prevent developers from misusing features or writing unnecessarily complex code, and that is okay. We should instead focus on helping reduce the rate that this occurs accidentally, and enabling tooling and diagnostics that warn about dangerous or surprising patterns. **The behavior and semantics of code should be clearly and simply specified whenever possible.** Leaving behavior undefined for some cases of invalid, buggy, or non-portable code may be necessary, but it comes at a very high cost and should be avoided. Every case where behavior is left undefined should be clearly spelled out with a strong rationale for this tradeoff. The code patterns without defined behavior should be teachable and understandable by developers. Finally, there must be mechanisms available to detect undefined behavior, at best statically, and at worst dynamically with high probability and at minimal cost. **Adhere to the principle of least surprise.** Defaults should match typical usage patterns. Implicit features should be unsurprising and expected, while explicit syntax should inform the reader about any behavior which might otherwise be surprising. The core concepts of implicit versus explicit syntax are well articulated in [the Rust community](https://blog.rust-lang.org/2017/03/02/lang-ergonomics.html#implicit-vs-explicit), although we may come to different conclusions regarding the principles. **Design features to be simple to implement.** Syntax, structure, and language features should be chosen while keeping the implementation complexity manageable. Simplicity of implementation reduces bugs, and will in most cases make the features easier to understand. It's also often the best way to ensure predictable performance, although supporting peak performance may require options for more complex implementation behavior. ### Practical safety and testing mechanisms Our goal is to add as much language-level safety and security to Carbon as possible, using a hybrid strategy to balance other goals. We will do as many safety checks as we can at compile time. We will also provide dynamic runtime checking and a strong testing methodology ranging from unit tests through integration and system tests all the way to coverage-directed fuzz testing. We have specific criteria that are important for this strategy to be successful: **Make unsafe or risky aspects of Carbon code explicit and syntactically visible.** This will allow the software to use the precise flexibility needed and to minimize its exposure, while still aiding the reader. It can also help the reader more by indicating the specific nature of risk faced by a given construct. More simply, safe things shouldn't look like unsafe things and unsafe things should be easily recognized when reading code. **Common patterns of unsafe or risky code must support static checking.** Waiting until a dynamic check is too late to prevent the most common errors. A canonical example here are [thread-safety annotations](https://clang.llvm.org/docs/ThreadSafetyAnalysis.html) for basic mutex lock management to allow static checking. This handles the common patterns, and we use dynamic checks, such as TSan and deadlock detection, to handle edge cases. **All unsafe or risky operations and interfaces must support some dynamic checking.** Developers need some way to test and verify that their code using any such interface is in fact correct. Uncheckable unsafety removes any ability for the developer to gain confidence. This means we need to design features with unsafe or risky aspects with dynamic checking in mind. A concrete example of this can be seen in facilities that allow indexing into an array: such facilities should be designed to have the bounds of the array available to implement bounds checking when desirable. ### Fast and scalable development Software development iteration has a critical "edit, test, debug" cycle. Developers will use IDEs, editors, compilers, and other tools that need different levels of parsing. For small projects, raw parsing speed is essential; for large software systems, scalability of parsing is also necessary. **Syntax should parse with bounded, small look-ahead.** Syntax that requires unbounded look-ahead or fully general backtracking adds significant complexity to parsing and makes it harder to provide high quality error messages. The result is both slower iteration and more iterations, a multiplicative negative impact on productivity. Humans aren't immune either; they can be confused by constructs that appear to mean one thing but actually mean another. Instead, we should design for syntax that is fast to parse, with easy and reliable error messages. **No semantic or contextual information used when parsing.** The more context, and especially the more _semantic_ context, required for merely parsing code, the fewer options available to improve the performance of tools and compilation. Cross-file context has an especially damaging effect on the potential distributed build graph options. Without these options, we will again be unable to provide fast developer iteration as the codebase scales up. **Support separate compilation, including parallel and distributed strategies.** Iteration requires frequent rebuilds of software as part of the edit/test/debug cycle of development. The language design should enable low-latency build strategies, particularly when relatively little has changed. This minimally requires separate compilation of source files, and potentially other incremental build strategies. Separate compilation also enables better scalability options for build systems of large software. ### Modern OS platforms, hardware architectures, and environments Carbon must have strong support for all of the major, modern OS platforms, the hardware architectures they run on, and the environments in which their software runs. Carbon must also continue supporting these over time, even as which ones are major or modern evolve and change. **Provide _native_ support for the programming models of those platforms and environments.** This goes beyond enabling compile-time translations from one abstraction to several implementations. While enabling high-level synchronization primitives like mutexes and futures is good, the underlying atomic operations provided by the hardware must also be directly available. Similarly, lowering parallel constructs into a specific implementation, such as SIMD or SPMD, is good but insufficient. Multiple parallel implementations must be directly addressable in Carbon. The need for native support repeats across the landscape of OS platform, hardware, and environment distinctions; for example, concurrency versus parallelism, and desktop versus mobile. **Conversely, Carbon cannot prioritize support for historical platforms.** To use a hockey metaphor, we should not skate to where the puck is, much less where the puck was twenty years ago. We have existing systems to support those platforms where necessary. Instead, Carbon should be forward-leaning in its platform support. As these platforms evolve over time, Carbon will have to evolve as well to continue to effectively prioritize the modern and major platforms. For examples, please see Carbon's [success criteria](principles/success_criteria.md#modern-os-platforms-hardware-architectures-and-environments). ### Interoperability with and migration from existing C++ code We want developers working within existing C++ ecosystems to easily start using Carbon, without starting from scratch. Adopting Carbon should not require complete rewrites, new programming models, or building an entire new stack/ecosystem. This means integrating into the existing C++ ecosystem by supporting incremental migration from C++ to Carbon, which in turn requires high-quality interoperability with existing C++ code. We must be able to move existing _large_ C++ codebases -- some with hundreds of millions of lines of code and tens of thousands of active developers -- onto Carbon. C++ developers must also successfully switch to Carbon development. Any migration of this scale will take years, will need to be incremental, and some libraries -- particularly third-party -- may remain in C and C++. It must be possible to migrate a C++ library to Carbon without simultaneously migrating all of the libraries it depends on or all of the libraries that depend on it. We believe incremental migrations require: **Familiarity for experienced C++ developers with a gentle learning curve.** We need a feasible plan for retraining a C++ workforce to become proficient in Carbon. If long and significant study is required to be minimally proficient, meaning able to read, superficially understand, and do limited debugging or modifications, then the inertia of C++ will inevitably win. Further, we need a gentle and easily traversed learning curve to basic productivity in order for the transition to not become a chore or otherwise unsustainable for teams and individuals. **Expressivity comparable to C++.** If an algorithm or data structure or system architecture can naturally be written in C++, it should also be possible to write it naturally in Carbon. **Automated source-to-source migration of large segments of large-scale idiomatic C++ code bases with high fidelity.** We will prioritize having very [low human interaction](principles/success_criteria.md#migration-tooling) to achieve high fidelity migration results. We do not require all C++ code to be migratable in this fashion, and the resulting Carbon may be non-idiomatic. We can add reasonable constraints here if those constraints are already well established best practices for C++ development, including design patterns, testing coverage, or usage of sanitizers. Over many years, as Carbon evolves and codebases have had time to migrate, the results of the tooling may also drift further from idiomatic Carbon and have less desirable results. **Support for bi-directional interoperability with existing C++ code.** We need Carbon code to be able to call into C and C++ libraries with both reasonable API clarity and high performance. We will also need some ability to implement C++ interfaces with business logic in Carbon, although this direction can tolerate slightly more constraints both in supported features and performance overhead. In all cases, the particular performance overhead imposed by moving between C++ and Carbon will need to be easily exposed and understood by developers. While a given piece of code only needs to be migrated once, we expect interoperability to be invoked continuously to support migrated code and will thus remain important for most developers. ## Non-goals There are common or expected goals of many programming languages that we explicitly call out as non-goals for Carbon. That doesn't make these things bad in any way, but reflects the fact that they do not provide meaningful value to us and come with serious costs and/or risks. ### Stable language and library ABI We would prefer to provide better, dedicated mechanisms to decompose software subsystems in ways that scale over time rather than providing a stable ABI across the Carbon language and libraries. Our experience is that providing broad ABI-level stability for high-level constructs is a significant and permanent burden on their design. It becomes an impediment to evolution, which is one of our stated goals. This doesn't preclude having low-level language features or tools to create specific and curated stable ABIs, or even serializable protocols. Using any such facilities will also cause developers to explicitly state where they are relying on ABI and isolating it in source from code which does not need that stability. However, these facilities would only expose a restricted set of language features to avoid coupling the high-level language to particular stabilized interfaces. There is a wide range of such facilities that should be explored, from serialization-based systems like [protobufs](https://developers.google.com/protocol-buffers) or [pickling in Python](https://docs.python.org/3/library/pickle.html), to other approaches like [COM](https://docs.microsoft.com/en-us/windows/win32/com/com-objects-and-interfaces) or Swift's ["resilience"](https://swift.org/blog/library-evolution/) model. The specific approach should be designed around the goals outlined above in order to fit the Carbon language. ### Backwards or forwards compatibility Our goals are focused on _migration_ from one version of Carbon to the next rather than _compatibility_ between them. This is rooted in our experience with evolving software over time more generally and a [live-at-head model](https://abseil.io/blog/20171004-cppcon-plenary). Any transition, whether based on backward compatibility or a migration plan, will require some manual intervention despite our best efforts, due to [Hyrum's Law](http://www.hyrumslaw.com), and so we should acknowledge that upgrades require active migrations. ### Legacy compiled libraries without source code or ability to rebuild We consider it a non-goal to support legacy code for which the source code is no longer available, though we do sympathize with such use cases and would like the tooling mentioned above to allow easier bridging between ABIs in these cases. Similarly, plugin ABIs aren’t our particular concern, yet we’re interested in seeing tooling which can help bridge between programs and plugins which use different ABIs. ### Support for existing compilation and linking models While it is essential to have interoperability with C++, we are willing to change the compilation and linking model of C++ itself to enable this if necessary. Compilation models and linking models should be designed to suit the needs of Carbon and its use cases, tools, and environments, not what happens to have been implemented thus far in compilers and linkers. As a concrete example, Carbon will not support platforms that cannot update their compiler and linker alongside the language. ### Idiomatic migration of non-modern, non-idiomatic C++ code While large-scale, tool-assisted migration of C++ code to Carbon is an explicit goal, handling all C++ code with this is expressly not a goal. There is likely a great deal of C++ code that works merely by chance or has serious flaws that prevent us from understanding the developer's intent. While we may be able to provide a minimally "correct" migration to very unfriendly code, mechanically reproducing exact C++ semantics even if bizarre, even this is not guaranteed and improving on it is not a goal. Migration support will prioritize code that adheres to reasonable C++ best practices, such as avoiding undefined behavior, maintaining good test coverage, and validating tests with sanitizers. ## Prioritization beyond goals The features, tools, and other efforts of Carbon should be prioritized based on a clearly articulated rationale. This may be based on this document's overarching goals and priorities, or if those don't offer enough clarity, we will fall back on rationale such as a required implementation order or a cost-benefit analysis. **Cost-benefit will drive many choices.** We expect to measure both cost, including complexity, and benefit using the impact on the project and language as a whole. Benefit accumulates over time, which means providing incremental solutions earlier will typically increase the total benefit. It is also reasonable for the rationale of a decision to factor in both effort already invested, and effort ready to commit to the feature. This should not overwhelm any fundamental cost-benefit analysis. However, given two equally impactful features, we should focus on the solution that is moving the fastest. **Domain-motivated libraries and features are an example.** For these, the cost function will typically be the effort required to specify and implement the feature. The benefit will stem from the number of users and how much utility the feature provides. We don't expect to have concrete numbers for these, but we expect prioritization decisions between features to be expressed using this framework. ## Acknowledgements Carbon's goals are heavily based on ["Goals and priorities for C++"](https://wg21.link/p2137). Many thanks to the authors and contributors for helping us formulate our goals and priorities. ================================================ FILE: docs/project/groups.md ================================================ # Groups ## Table of contents - [Overview](#overview) - [Linked entities](#linked-entities) - [Carbon leads](#carbon-leads) - [Conduct team](#conduct-team) - [Moderators](#moderators) - [Admins](#admins) - [Implementation team](#implementation-team) - [Security](#security) - [Access groups](#access-groups) - [GitHub commit access](#github-commit-access) - [GitHub label and review access](#github-label-and-review-access) - [Google Drive access](#google-drive-access) ## Overview These are groups used by the Carbon Language project, listed here for central tracking. Membership will typically be managed by a team owner or admin; see each group's summary for information on how to join. Note that some links are admin or member restricted. We're providing public links where possible. ### Linked entities Groups are defined by their linked entities in Carbon's various forums: - [GitHub teams](https://github.com/orgs/carbon-language/teams): Used primarily for GitHub ACLs. - [GitHub organization members](https://github.com/orgs/carbon-language/people): This should only contain people in teams. - Discord roles: Used to identify team members on Discord, and for ACLs. - [Google groups](https://admin.google.com/ac/groups): Used for a mix of contact lists and ACLs. ## Carbon leads See [governance structure](evolution.md#governance-structure) for team information. - GitHub teams: [Leads](https://github.com/orgs/carbon-language/teams/leads) - Discord roles: lead - Google groups: None ## Conduct team See [Conduct team](teams/conduct_team.md) for information. - GitHub teams: None - Discord roles: None - Google groups: [conduct@carbon-lang.dev](https://groups.google.com/a/carbon-lang.dev/g/conduct/about) Used as a contact list. ### Moderators See [moderators](moderators.md) for information. - GitHub teams: [Moderators](https://github.com/orgs/carbon-language/teams/moderators) - Discord roles: moderator, senior-moderator - Google groups: [moderators](https://groups.google.com/a/carbon-lang.dev/g/moderators/about): Used for Google Drive ACLs. ## Admins Maintains infrastructure. Membership changes are handled on an as-needed basis by leads. Note that while various groups exist, the way admins are actually configured goes a little beyond this. - Github teams: [Admins](https://github.com/orgs/carbon-language/teams/admins) - Canonically, the [role:owner](https://github.com/orgs/carbon-language/people?query=role%3Aowner) search. - Discord roles: admin - Google groups: [admins](https://groups.google.com/a/carbon-lang.dev/g/conduct/about): Used for `carbon-lang.dev` security settings. ## Implementation team This team is responsible for development of Carbon's primary, reference implementation and toolchain. It also oversees other related implementation work within the Carbon project, from tooling of the language spec to test suites. There may be some overlap with [admins](#admins) -- any issue can be resolved by escalating to the [Carbon leads](#carbon-leads). Notably, this team is _not_ responsible for the _design_ of the language itself, only for its implementation. - GitHub teams: None - Discord role: implementation-team - Google groups: None ## Security Receives GitHub security reports. Membership changes are handled on an as-needed basis by leads. - GitHub teams: [Security](https://github.com/orgs/carbon-language/teams/security) - Discord roles: None - Google groups: None ## Access groups These groups are defined by the access they grant. They are not directly tied to any of the above community groups. ### GitHub commit access Developers who can merge and approve changes on GitHub. See [commit access](commit_access.md) for information. - GitHub teams: [Commit access](https://github.com/orgs/carbon-language/teams/commit-access) - Discord roles: None - Google groups: None ### GitHub label and review access Developers who can label and assign PRs and issues on GitHub, particularly proposals. See [CONTRIBUTING.md](/CONTRIBUTING.md#getting-access) for more information. - GitHub teams: [Label and review access](https://github.com/orgs/carbon-language/teams/label-and-review-access) - Discord roles: None - Google groups: None ### Google Drive access For Google Drive and the contained documents, we have separate groups for commenting and contributing (modify and create). See [CONTRIBUTING.md](/CONTRIBUTING.md#getting-access) for more information. - GitHub teams: None - Discord roles: None - Google groups: [commenters](https://groups.google.com/a/carbon-lang.dev/g/commenters/about) and [contributors](https://groups.google.com/a/carbon-lang.dev/g/contributors/about): Used for Google Drive ACLs. ================================================ FILE: docs/project/milestones.md ================================================ # Milestones ## Table of contents - [Overview](#overview) - [Milestone 0.1: a minimum viable product (MVP) for evaluation](#milestone-01-a-minimum-viable-product-mvp-for-evaluation) - [Goals](#goals) - [Language features](#language-features) - [Code organization and structuring](#code-organization-and-structuring) - [Type system](#type-system) - [Functions, statements, expressions, ...](#functions-statements-expressions-) - [Standard library components](#standard-library-components) - [Project features](#project-features) - [Milestone 0.2: feature complete product for evaluation](#milestone-02-feature-complete-product-for-evaluation) - [Features explicitly deferred until at least 0.2](#features-explicitly-deferred-until-at-least-02) - [Why are coroutines and async in this milestone?](#why-are-coroutines-and-async-in-this-milestone) - [Milestone 1.0: no longer an experiment, usable in production](#milestone-10-no-longer-an-experiment-usable-in-production) - [Features explicitly deferred beyond 0.2](#features-explicitly-deferred-beyond-02) ## Overview As Carbon progresses, we want to have some common long-term milestones that we orient our work around. The annual [roadmap](roadmap.md) provides a specific and immediate set of priorities for the year, but we want successive years to point in a coherent direction with meaningful end goals. Milestones should typically be long-term, spanning more than a year, and have some functional motivation. We also assign version numbers to our initial milestones to make them easy to refer to and incorporate into various versioning schemes. ## Milestone 0.1: a minimum viable product (MVP) for evaluation The first milestone is also the most concrete -- it is the MVP for C++ users and developers to begin evaluating Carbon seriously. We want to keep this milestone as minimal as we can while still enabling a sufficient initial round of evaluation. ### Goals From the perspective of outcomes, our goals for the 0.1 language are centered around what we expect evaluations to be able to include: - Evaluators have a clear idea of the long-term evolution strategy of Carbon and how it addresses different use cases and requirements. - Language design components are documented, cohesive, and understandable by evaluators without placeholders. - The components and language features must include the foundational core of the language. These features must also be sufficient to translate existing C++ code ([except coroutines](#why-are-coroutines-and-async-in-this-milestone)) into obvious and unsurprising Carbon code. - Also in-scope are additional features that impact API design or need early feedback, but only if they are low cost to both the design and implementation. - Example language components include: lexical structure, expressions, statements, conditions, loops, user-defined types, and their dependencies. - Example library components include: integer types, floating point types, strings, arrays, ranges, pointers, optionals, variants, heap allocation, and their dependencies. - Where these build on top of other language or library designs, those are transitively in-scope. - Design for both Carbon use of C++ and C++ use of Carbon, including all major C++ language features except for coroutines, is documented, cohesive, and understandable by evaluators without placeholders. - Evaluators can build and run tests of most C++ interoperability, including both real-world C++ code that can be built with the latest release of Clang and test Carbon code. - Gaps from the design need to be ones that don't undermine evaluation confidence. - Evaluators can effectively stress-test build speed and scaling with Carbon. - Evaluators can build some key benchmarks that include C++ interoperation in the critical path and get representative performance results. - This can in turn be a still smaller subset of all aspects of C++ interoperability based around what impacts interesting benchmarks. - Both strategy and design for memory safety allow evaluators to be confident in safe Carbon having strong memory safety protections, and being incrementally adoptable starting from existing C++ codebases. ### Language features These are focused on the core _necessary_ features for us to reach a successful 0.1 language that can address our goals. Some of these features are required directly by the above goals, others are required due to dependencies or interactions with the directly required features. However, we don't try to cover all of the features in full granularity here. There will be many minor components that are necessary for these to hold together but are not directly addressed. In general, unless something is explicitly described as partial or having exceptions, everything covered by that entry should be expected in the 0.1 language. Another important point is that this doesn't commit Carbon to any _particular_ design for any of these bullet points. Instead, it just means that the Carbon design must have _something_ that addresses each of these bullet points. That might be to add the named design to Carbon, but it equally might be a clear statement that Carbon will _not_ include this design but use some other language features to address its use cases and when rewriting C++ code using that feature into Carbon. #### Code organization and structuring - Packages - Libraries - Implementation files - Importing - Namespaces #### Type system - User-defined types - C++ interop: importing C++ types into Carbon, exporting Carbon types into C++ - Single inheritance - Virtual dispatch - C++ interop: - Bi-directional inheritance between C++ and Carbon - Type hierarchy roots in both C++ and Carbon - Mappings of inheritance features: abstract, final, virtual - Operator overloading - C++ interop: - Synthesizing Carbon overloads for imported C++ types - Exporting Carbon overloads into C++ - Sum types (discriminated unions) - Unions (un-discriminated) - C++ interop: mapping to and from C++ unions. - Generics - Both generic functions and types - Checked generics - Definition-checked variadics - Integrated templates - Including template-style structural conformance to nominal constraints, both modeling the members (like interfaces) and arbitrary predicates (like C++20 expression validity predicates) - C++ interop: - Importing C++ templates, instantiating on Carbon types - Exporting Carbon templates, instantiating on C++ types - Exporting Carbon checked generics (as templates), instantiating on C++ types - Mapping C++20 concepts into named predicates, and named predicates into C++20 concepts #### Functions, statements, expressions, ... - Functions - Separate declaration and definition - Function overloading - C++ interop: - Importing C++ functions and methods and calling them from Carbon - Exporting Carbon functions and methods and calling them from C++ - Importing C++ overload sets into Carbon overload sets where the model (closed overloading) fits - Importing C++ open-overload-sets-as-extension-points (`swap`, etc) into synthetic Carbon interfaces for common cases (likely based on heuristics) - Control flow statements - Conditions - Loops - Range-based loops - Good equivalents for a range of existing C/C++ looping constructs - Matching - Good equivalents for C/C++ uses of `switch` - Working with sum-types, especially for C++ `std::variant` and `std::optional` interop - Both positive (`if let` in Rust) and negative (`let else` in Rust) combined match control flow and variable declaration - C++ interop: support for C++'s threading and atomic primitives, memory model, and synchronization tools - Error handling - Any dedicated error handling control flow constructs - C++ interop: - Mechanisms to configure how exception handling should or shouldn't be integrated into C++ interop sufficient to address both `-fno-except` C++ dialects and standard C++ dialects - Calling C++ functions which throw exceptions from Carbon and automatically using Carbon's error handling - Export Carbon error handling using some reasonably ergonomic mapping into C++ -- `std::expected`, something roughly compatible with `std::expected`, C++ exceptions, etc. #### Standard library components Note: we expect to _heavily_ leverage the C++ standard library by way of interop for the vast majority of what is needed in Carbon initially. As a consequence, this is a surprisingly more minimal area than the language features. - Language and syntax support library components - Fundamental types (`bool`, `iN`, `fN`) - Any parts of tuple or array types needed in the library - Pointer types - Interfaces powering language syntax (operators, conversions, etc.) - Types with important language support - String and related types used with string literals - Optional - Slices - C++ interop: - Transparent mapping between Carbon fundamental types and C++ equivalents - Transparent mapping between Carbon and C++ _non-owning_ string-related types - Transparent mapping between Carbon and C++ _non-owning_ contiguous container types - Includes starting from an owning container and forming the non-owning view and then transparently mapping that between languages. - Transparent mapping between Carbon and C++ iteration abstractions ### Project features There are a few important components of the overarching Carbon project that need to be completed as part of 0.1 beyond _language_ features: - A functioning Carbon toolchain: - Supports drop-in usage as a Clang C++ toolchain with the most common Make- and CMake-derived build systems. - Implements most of the [features](#language-features) above, including C++ interop, and any remaining gaps don't undermine the ability to evaluate the remaining features or the confidence in the overall evaluation. - Installs on Windows, macOS, and Linux, and builds working programs for those platforms. - Build system integration for CMake, and documentation for integrating with Make or similar build systems. - Detailed safety strategy for Carbon - Will include specific expectations for how unsafe C++ code and unsafe Carbon code will interact with safe Carbon code. - Also includes any tradeoffs or prioritization across different kinds or levels of safety. - Detailed and concrete design for safe Carbon - Must at least include ways in which most modern C++ is safe: type and initialization safety. - Must also include spatial, temporal, and mutation safety. - Will include an analysis of how this impacts _safe_ Rust interop. - Does _not_ include having a complete implementation in 0.1. - Basic documentation for evaluators from getting started to FAQs. ## Milestone 0.2: feature complete product for evaluation The second milestone already concretely in mind is reaching a level of feature-completeness. The completeness metric here is based around the features necessary to credibly address the _existing_ needs of C++ users and developers interested in moving to Carbon, and so will be heavily driven by either features already in use in C++ or necessary features to make moving off of C++ a viable tradeoff. Ultimately, we need this milestone to be sufficiently feature complete that _users can complete their evaluation of Carbon_. The language will continue to evolve and grow features beyond this, but at this point there shouldn't be any feature gaps that are problematic for the initial target audience coming from C++, and we should be able to finalize the Carbon _experiment_ with this feature set. That said, this is about as concrete as we can get for a milestone that remains years in the future. The full scope of requirements for this milestone will be defined as we complete 0.1 and begin getting feedback on it. Currently, we just call out specific features that we are actively deferring until at least 0.2 but without being listed somewhere could cause confusion. ### Features explicitly deferred until at least 0.2 - Memory safety - Coroutines, async, generators, etc. - Comprehensive story for handling effects, and how functions can be generic across effects - Carbon-native threading - Long tail of metaprogramming features - Mixins - Properties - Inline assembly - SIMD - Some ability to define an API that communicates & shares data across language versions, build configurations, and other FFI or ABI boundaries. - Necessary parts of the standard library #### Why are coroutines and async in this milestone? Specifically, why not address them earlier in 0.1? Or if they can be deferred why not defer them further? Coroutines and async programming are large and complex topics to introduce into the language. From watching C++, Rust, Swift, Kotlin, and many other languages working in this space, we have a strong belief that trying to add these to the 0.1 language would _significantly_ increase the amount of work and likely delay when we reach that milestone. Also, given the recency of coroutines being added to C++, we expect evaluators to be able to reason about their absence and still accomplish most of the evaluation of Carbon without issue. However, we also expect that as coroutines start to be widely adopted in C++, they will become an essential feature of the language that would be extremely difficult to give up when moving to Carbon. So we expect coroutines to be a necessary feature for us to effectively decide that the Carbon experiment is a success and begin planning large-scale adoption and migration. ## Milestone 1.0: no longer an experiment, usable in production Even less concrete is the milestone that marks Carbon no longer being an experiment, but if successful, a usable language. Currently this is speculatively called 1.0 but even that is highly subject to change as we approach. Again, we simply call out features here that we _do_ expect to have in a 1.0 milestone, but want to explicitly defer beyond the 0.2 milestone. ### Features explicitly deferred beyond 0.2 - Robust language evolution strategy and plan, specifically addressing: - The need to make on-going changes to the language to address feedback and a changing landscape. - The cost on users of churn and change over time and how to manage that cost. - A mechanism to address users who need true, durable stability over long time horizons. - Package management strategy and plan, and any early groundwork needed. - Developer experience is high enough quality to enable initial production users. - Includes compiler error messages and basic developer tooling. - Everything we've learned we need as part of the evaluation of 0.1 and 0.2 ================================================ FILE: docs/project/moderators.md ================================================ # Moderators ## Overview Moderation is primarily focused on Carbon's Discord and GitHub, but generally applies to the [collaboration systems](/CONTRIBUTING.md#collaboration-systems). Moderators are empowered to improve community discussion and enforce the [Code of Conduct](/CODE_OF_CONDUCT.md). Conduct decisions are ultimately the domain of the [Conduct team](/CODE_OF_CONDUCT.md#conduct-team), although moderators will often act in order to protect the community. All moderators will have the "moderator" role on Discord. Some moderators are _senior moderators_, and will have extra privileges to help address harmful conduct. Senior moderators will have the "senior-moderator" role on Discord. ## Becoming a moderator TODO: Need to figure out right process for asking. (that is, how?) The Conduct team reviews new moderators. Community members will be considered mainly based on their history in Carbon Language community spaces (Discord, GitHub, etc.), although applicants may also note history in other communities. Prior to being granted moderation privileges, new moderators will need to attend a workshop in order to help orient them with the Carbon Language community's values and moderator expectations. ## Promotion to senior moderator The Conduct team reviews promotions to senior moderator. Moderators will be considered for promotion to senior moderators when: - They have demonstrated that they will be active moderators. - They are making decisions consistent with the [Code of Conduct](/CODE_OF_CONDUCT.md). - Senior moderators are taking actions which require senior moderator privileges at the moderator's request. ## Moderation powers There is a moderation playbook for moderators with instructions on how to use these powers. This enumerates specific actions a moderator may take. - **Discord** - **Timeout user** (all): Prevent a user from sending messages on the Discord server for up to a week. - **Delete message** (senior): Delete a message owned by another user. - **Kick user** (senior): Temporarily remove a user from the Discord server. Note that users can rejoin the server, and so banning is typically preferred. - **Ban user** (senior): Permanently remove a user from the Discord server. - **Slow-mode** (senior): Limit the rate of discussion on a channel. - Slow-mode is tied to the ability to generically edit channels. Moderators are generally expected to use the channel edit power judiciously, preferring to leave channel edits for admins. - **GitHub** - **Convert issue to discussion** (all): Moves an issue to a discussion. This is mainly organizational. - **Hide message** (all): Collapses a message so that its text is not immediately visible, reducing distractions. Recommended for moderators when a particular message is unproductive. Users can still view the original content. - This cannot be done with the first message in a conversation. - **Lock conversation** (senior): Prevent more discussion on a topic. - **Edit message** (senior): Edits content of a message. Users can still view the original content. - **Edit title** (senior): Edits the title of a thread. - **Delete message** (senior): Removes a single message from a conversation. Hiding or editing messages is preferred. - This cannot be done to the first message in a conversation. - **Delete conversation** (senior): Deletes a full GitHub conversation. Hiding or editing messages is preferred. - **Block user** (senior): Blocks a user from all repositories. - Note: We use custom roles which have [limited access options](https://docs.github.com/en/enterprise-cloud@latest/organizations/managing-peoples-access-to-your-organization-with-roles/managing-custom-repository-roles-for-an-organization) versus what [base permissions](https://docs.github.com/en/organizations/managing-access-to-your-organizations-repositories/repository-roles-for-an-organization) allow. - **Google Docs** - There are limited powers for moderators on Google Docs, but access to Google Docs is already restricted. At present, the right action is to respond or comment on an issue as needed, and report to the Conduct team if further steps are necessary. ================================================ FILE: docs/project/principles/README.md ================================================ # Principles Some language [goals](../goals.md) will have widely-applicable, high-impact, and sometimes non-obvious corollaries. We collect concrete language design principles in this directory as a way to document and clarify these. Principles clarify, but do not supersede, goals and priorities. Principles should be used as a tool in making decisions, and to clarify to contributors how decisions are expected to be made. A key difference between a principle and the design of a language feature is that a principle should inform multiple designs, whereas a feature's design is typically more focused on achieving a specific goal or set of goals. The principle can help achieve consistency across those multiple designs. Note that these principles seek to establish both the approaches the project wants to pursue, as well as those we want to exclude. - [Errors are values](error_handling.md) - [Information accumulation](information_accumulation.md) - [Low context-sensitivity](low_context_sensitivity.md) - [Prefer providing only one way to do a given thing](one_way.md) - [One static open extension mechanism](static_open_extension.md) - [Success criteria](success_criteria.md) ================================================ FILE: docs/project/principles/error_handling.md ================================================ # Principle: Errors are values ## Table of contents - [Background](#background) - [Principle](#principle) - [Applications of these principles](#applications-of-these-principles) ## Background Most nontrivial programs contain functions that can _fail_, meaning that even if all their preconditions are met, they may not be able to perform their primary behavior. For example, a function that reads data from a remote server may fail if the server is unreachable, and a function that parses a string to return an integer may fail if the input string is not a properly-formatted integer. In many cases, the function author wants these failures to be _recoverable_, meaning that a direct or transitive caller can respond to the failure in some way that enables the program to continue running. ## Principle A Carbon function that needs to report recoverable failures should return a sum type whose alternatives represent the success case and failure cases, such as `Optional(T)`, `Result(T, Error)`, or `bool`. The function's successful return value, and any metadata about the failure, should be embedded in the alternatives of the sum type, rather than reported by way of output parameters or other side channels. Carbon's design will prioritize making this form of error handling efficient and ergonomic. ## Applications of these principles Carbon errors, unlike exceptions in C++ and similar languages, will not be propagated implicitly. Instead, Carbon will very likely need to provide some explicit but syntactically lightweight means of propagating errors, such as Rust's `?` operator, so that error-propagation boilerplate doesn't make it hard for readers to follow the logic of the success path. Carbon will not have a special syntax for specifying what kind of errors a function can emit, such as `noexcept` or [dynamic exception specifications](https://en.cppreference.com/w/cpp/language/except_spec) in C++, or `throws` in Java, because that information will be embedded in the function's return type. Similarly, Carbon errors will be statically typed, because Carbon return values are statically typed. ================================================ FILE: docs/project/principles/information_accumulation.md ================================================ # Principle: Information accumulation ## Table of contents - [Background](#background) - [Principle](#principle) - [Applications of this principle](#applications-of-this-principle) - [Exceptions](#exceptions) - [Alternatives considered](#alternatives-considered) ## Background There are many different sources of information in a program, and a tool or a human interpreting code will not in general have full information, but will still want to draw conclusions about the code. Different languages take different approaches to this problem. For example: - In C, information is accumulated linearly in each source file independently, and only information from earlier in the same file is available. A program can observe that information is incomplete at one point and complete at another. - In C++, the behavior is largely similar to C, except: - Within certain contexts in a class, information from later in the class definition is available. - With C++20 modules, information from other source files can be made available. - It is easier to observe -- perhaps even accidentally -- that information is accumulated incrementally. - In Rust, all information from the entire crate is available everywhere within that crate, with exceptions for constructs like proc macros that can see the state of the program being incrementally built. - In Swift, all information from the entire source file is available within that source file. ## Principle In Carbon, information is accumulated incrementally within each source file. Carbon programs are invalid if they would have a different meaning if more information were available. Carbon source files can be interpreted top-down, without referring to information that appears substantially later in a file. Source files are expected to be organized into a topological order where that makes sense, with forward declarations used to introduce names before they are first referenced when necessary. If a program attempts to use information that has not yet been provided, the program is invalid. There are multiple options for how this can be reported: - The program can be rejected as soon as it tries to use information that might not be known yet. - For the case where the information can only be provided in the same source file, an assumption about the information can be made at the point where it is needed, and the program can be rejected only if that assumption turns out to be incorrect. Disallowing programs from changing meaning in the context of more information ensures that the program is interpreted consistently or is rejected. This is especially important to the coherence of generics and templates. ## Applications of this principle - As in C++, and unlike in Rust and Swift, name lookup only finds names declared earlier. - Classes are incomplete until the end of their definition. Unlike in C++, any attempt to observe a property of an incomplete class that is not known until the class is complete renders the program invalid. - When an `impl` needs to be resolved, only those `impl` declarations that appear earlier are considered. However, if a later `impl` declaration would change the result of any earlier `impl` lookup, the program is invalid. ## Exceptions Because a class is not complete until its definition has been fully parsed, applying this rule would make it impossible to define most member functions within the class definition. In order to still provide the convenience of defining class member functions inline, such member function bodies are deferred and processed as if they appeared immediately after the end of the outermost enclosing class, like in C++. ## Alternatives considered - Allow information to be used before it is provided [globally](/proposals/p0875.md#strict-global-consistency), [within a file](/proposals/p0875.md#context-sensitive-local-consistency), or [within a top-level declaration](/proposals/p0875.md#top-down-with-minimally-deferred-type-checking). - [Do not allow inline method bodies to use members before they are declared](/proposals/p0875.md#strict-top-down) - [Do not allow separate declaration and definition](/proposals/p0875.md#disallow-separate-declaration-and-definition) ================================================ FILE: docs/project/principles/library_apis_only.md ================================================ # Principle: All APIs are library APIs ## Table of contents - [Background](#background) - [Principle](#principle) - [Applications of this principle](#applications-of-this-principle) - [Exceptions](#exceptions) - [Alternatives considered](#alternatives-considered) ## Background Every major modern programming language comes with a standard library, which consists of APIs that are not part of the core language, but instead are written in the language (although their implementations may not be). However, different languages draw the boundary between language and library in different places. For example, Go's `map` type is built into the core language, whereas the C++ equivalent, `std::unordered_map`, is part of the standard library. In Swift, even fundamental types like integers and pointers are part of the standard library; there are no truly "built in" types. These decisions can have important consequences for the design of the language. For example, many important features of C++, such as move semantics, variadics, and coroutines, were motivated largely by their anticipated uses in a small set of standard library types. In a language with a different design philosophy, those types could have been built into the core language. This would probably have substantially simplified the language, and made those types available faster. However, that would have come at the cost of less flexibility for users outside the common case. ## Principle In Carbon, every public function is declared in some Carbon API file, and every public `interface`, `impl`, and first-class type is defined in some Carbon API file. In some cases, the bodies of public functions will not be defined as Carbon code, or will be defined as hybrid Carbon code using intrinsics that aren't available to ordinary Carbon code. However, we will try to minimize those situations. Thus, even "built-in" APIs can be used like user-defined APIs, by importing the appropriate library and using qualified names from that library, relying on the ordinary semantic rules for Carbon APIs. ## Applications of this principle We expect Carbon to have a special "prelude" library that is implicitly imported by all Carbon source files, and there might be a special name lookup rule to allow the names in the prelude to be used unqualified. However, in accordance with this principle, they will remain available to ordinary qualified name lookup as well. According to the resolutions of [#543](https://github.com/carbon-language/carbon-lang/issues/543) and [#750](https://github.com/carbon-language/carbon-lang/issues/750), Carbon will have a substantial number of type keywords, such as `i32`, `f64`, and `bool`. However, these keywords will all be aliases for ordinary type names, such as `Carbon.Int(32)`, `Carbon.Float(64)`, and `Carbon.Bool`. Furthermore, all arithmetic and logical operators will be overloadable, so that those types can be defined as class types. The member function bodies for these types will be probably not be implemented in Carbon, but this principle applies only to function declarations, not function definitions. Similarly, a pointer type such as `Foo*` will be an alias for some library class type, for example `Carbon.Ptr(Foo)`. As a result, Carbon will support overloading pointer operations like `->` and unary `*`. All Carbon operations that use function-style syntax, such as `sizeof()` and `decltype()` in C++, will be standard library functions. As above, in some cases we may choose to alias those functions with keywords, and the function bodies may not be defined in Carbon. ## Exceptions This principle applies to types only if they are _first-class_, meaning that they can be the types of run-time variables, function parameters, and return values. Carbon's type system will probably also include some types whose usage is more restricted, and this principle will not apply to them. Most importantly, function types might not be first-class types, in which case they need not be library types. Some types (such as tuples, structs, and certain integer types) will have built-in literal syntaxes for creating values of those types. Furthermore, in some cases (such as tuples and structs) the type's literal syntax will also be usable as a pattern syntax. The logic for performing those operations is arguably part of those types' public API, but will not be part of those types' class definitions. ## Alternatives considered - [Built-in primitive types](/proposals/p1280.md#built-in-primitive-types) ================================================ FILE: docs/project/principles/low_context_sensitivity.md ================================================ # Principle: Low context-sensitivity ## Table of contents - [Principle](#principle) - [Mitigations of context-sensitive costs](#mitigations-of-context-sensitive-costs) - [Visual aids](#visual-aids) - [Contextual _validity_ rather than _meaning_](#contextual-validity-rather-than-meaning) - [Reduced cost of mistakes](#reduced-cost-of-mistakes) - [Compiler-checked context](#compiler-checked-context) - [Applications of the principle](#applications-of-the-principle) - [Imports and namespaces](#imports-and-namespaces) - [Name shadowing](#name-shadowing) - [Flow-sensitive typing](#flow-sensitive-typing) - [Coherence of names and generics](#coherence-of-names-and-generics) - [Performance](#performance) ## Principle Carbon should favor designs and mechanisms that are not sensitive to context. Instead, we should favor constructs that are not ambiguous so that they don't need context for disambiguation. This is in service to the goal that [Carbon code is easy to read, understand, and write](/docs/project/goals.md#code-that-is-easy-to-read-understand-and-write). In particular, this is about prioritizing reading and understanding over writing. We should be willing to trade off conciseness, which still benefits reading as well as writing, for a sufficiently impactful reduction in the amount of context needed to read and understand code. Context can be expensive in different ways, for example: - It can be _large_: it might require looking through a lot of lines of code to find all of the relevant contextual information. - It can be _distant_: the further away from the current declaration or definition, the more expensive it is to find contextual information. This can scale from a separate definition in the same file, to a separate file, or even to a separate package. - It can be _unpredictable_: it might require careful searching of large bodies of code to locate the contextual information if its location cannot be predicted. - It can be _subtle_: the contextual clues might be easily missed or mistaken. Code that isn't context sensitive is easier to copy or move between contexts, like files or functions. It is code that needs fewer changes when it is refactored, in support of [software evolution](/docs/project/goals.md#software-and-language-evolution). In general, we should start with more restrictive constructs that limit ambiguity and see if we can make them work. If we find those restrictions are burdensome, we will then have more information to inform the next step. Ideally we would address those use cases with simple tools that solve multiple problems. The goal is to make a bunch of orthogonal mechanisms, each of which are easily understood and act in unsurprising ways. If that next step is to loosen restrictions, that is generally easier to do while maintaining compatibility with existing code than adding new restrictions. ### Mitigations of context-sensitive costs There are several ways that the potential costs of context-sensitive code can be mitigated. These techniques can and should be leveraged to help minimize and mitigate the contextual costs of Carbon features, and in some cases may provide a path to a feature that would otherwise be prohibitively costly. #### Visual aids A direct way to reduce contextual costs is through lexical and syntactic structures that form visual aids. These can both reinforce what the context is and aid the reader in the expensive aspect of navigating the context. For example, representing contexts with indentation, or IDE highlighting of matching parentheses and braces. These visual hints make it easier for developers to notice contextual elements. #### Contextual _validity_ rather than _meaning_ When the context only affects the _validity_ of code, but not its meaning, the costs are significantly reduced. In that case, understanding the meaning or behavior of the code doesn't require context, and a developer can easily rely on the compiler to check the validity. A simple example of this is contextually valid syntax, which is relatively common and inexpensive. However, reusing the same syntax with different contexts _with different meanings_ shifts the contextual information from simple validity to impacting the meaning of code. #### Reduced cost of mistakes Another mitigation for the costs of context-sensitive code is when the cost of a mistake due to the context is low. Some simple examples: - Context-sensitivity in comments is less expensive in general than in code. - In places where the general meaning is clear, developers can safely and reliably work with that general understanding, and the context only provides a minor refinement. ##### Compiler-checked context Another way the costs of mistakes can be reduced is when the compiler can reliably detect them. This is the fundamental idea behind statically type-checked languages: the compiler enforcement reduces the contextual cost of knowing what the types are. How early and effectively the compiler can detect the mistakes also plays a role in reducing this cost, which is part of the value proposition for [definition-checked](/docs/design/generics/terminology.md#definition-checking) generics. An example of this situation in Rust is that the same syntax is used for a move and a copy of the value in a variable. Those cases are distinguished by whether the type implements a specific trait, which may not be readily ascertained. The compiler verifies that the code never uses a variable that is no longer valid due to having been moved from, which is expected to catch the problems that could arise from this difference. Otherwise the semantic difference between a move and a copy is considered in Rust to be low-enough stakes for there to be no need to signal that difference in the code. However, the reasoning that makes this example a good design on balance for Rust doesn't necessarily apply to Carbon. The compiler is checking to prevent _errors_, but it can't reliably check for unpredictable _performance_. Given Carbon's priorities, that might make this level of contextual information still too expensive. More background on this area of Rust specifically is presented in [their blog post on language ergonomics](https://blog.rust-lang.org/2017/03/02/lang-ergonomics.html). ## Applications of the principle There are many parts of Carbon that could potentially be analyzed through this lens, and we can't enumerate them all here. This section focuses on several examples to help illustrate how the principle is likely to be relevant to Carbon. They focus on either cases that showcase the principle in effect or cases which make challenging tradeoffs of the costs in the principle. ### Imports and namespaces There are several parts of the way [imports](/docs/design/code_and_name_organization/#imports) and [namespaces](/docs/design/code_and_name_organization/#namespaces) are designed in Carbon that reflect applications of this principle: - Adding an import or reordering imports should never change behavior of existing code. This means the reader doesn't have to look through all the imports to understand how code behaves. This is also important for tooling, which should not have to worry about unwanted side effects when adding or sorting imports. - Carbon doesn't provide an analogy to C++'s [`using namespace`](https://en.cppreference.com/w/cpp/language/namespace#Using-directives) or a ["wildcard imports" mechanisms](/proposals/p0107.md#broader-imports-either-all-names-or-arbitrary-code) that merge the names from one namespace into another. Either would introduce ambiguity in where a name is coming from, making the code more context-sensitive. - Carbon doesn't support large blocks of code [inside a namespace declaration](/proposals/p0107.md#scoped-namespaces), where the reader would have to search for the beginning of the block to see what namespace applies. ### Name shadowing We should limit how names can be reused with shadowing rules, so the meaning of a name doesn't change in surprising ways between scopes. Further, if you find a matching declaration you don't have to keep searching to see if there is another that hides the one you found. This both expands the context you have to consider, and is an opportunity to make a mistake identifying the correct context, potentially leading to misunderstanding of the code. ### Flow-sensitive typing This principle is an argument against [flow-sensitive typing](https://en.wikipedia.org/wiki/Flow-sensitive_typing), where the type of a name can change depending on control flow. For example, [Midori used this for optional types](http://joeduffyblog.com/2016/02/07/the-error-model/#the-syntax). If we were to support this in Carbon, you could unwrap an optional value by testing it against `None`. ``` var x: Optional(Int) = ...; if (x != None) { // x has type Int. PrintInt(x); } // x is back to type Optional(Int). ``` This can be taken farther, this example has `x` taking on three different types: ``` var x: Optional(Optional(Int)) = ...; if (x != None) { // x has type Optional(Int). if (x != None) { // x has type Int. PrintInt(x); } // x has type Optional(Int). } // x has type Optional(Optional(Int)). ``` The concern here is that the context is very subtle. The type of `x` is affected by otherwise ordinary-looking `if` statements and closing braces (`}`). While we might not want to completely eliminate the possibility of flow-sensitive typing in Carbon, it would have to overcome a large hurdle. We would only want a flow-sensitive feature if it delivered sufficiently large usability, consistency, or expressivity gains. ### Coherence of names and generics Carbon [packages](/docs/design/code_and_name_organization/#packages) are designed to ensure all declared names belong to exactly one package and the compiler can enforce Carbon's equivalent [one-definition rule (ODR)](https://en.wikipedia.org/wiki/One_Definition_Rule). This avoids an issue in C++ where the ODR is not reliably checked by the compiler, which can leave the correctness of programs dependent on both distant and subtle contextual information. Similarly, Carbon generics should have [coherence, like Rust](https://github.com/Ixrec/rust-orphan-rules#what-is-coherence), where types have a single implementation of an interface. And this should be enforced by the compiler, using rules like [Rust's orphan rules](https://github.com/Ixrec/rust-orphan-rules#what-are-the-orphan-rules). ### Performance Since [Carbon's number one goal is performance](/docs/project/goals.md#performance-critical-software), it is important that the performance characteristics of code be predictable and readily determined by readers. This argues that those characteristics should not depend on expensive context. For example, Carbon should not provide a `dynamic_cast` facility with the same capabilities of C++'s where distant aspects of the inheritance structure can cause surprising performance differences. Similarly, Carbon should try to ensure normal looking method calls and data member access don't have the surprising performance costs caused by virtual inheritance in C++. More generally, Carbon should avoid features with hidden costs, particularly when they scale based on subtle aspects of the context where those features are used. ================================================ FILE: docs/project/principles/namespace_cleanliness.md ================================================ # Principle: Namespace cleanliness ## Table of contents - [Background](#background) - [Principle](#principle) - [Applications of this principle](#applications-of-this-principle) - [Exceptions](#exceptions) - [Alternatives considered](#alternatives-considered) ## Background Names and entities in a program can come from multiple sources -- from a local declaration, from an import, from the standard library, or from the prelude. Names can be imported from Carbon code or imported or derived from code written in another language such as C++ or an interface description language such as that of Protobuf, MIDL, or CORBA. Names can be selected for use in a program that language designers later decide they want to use as keywords. And in order to use a library, it is sometimes necessary to redeclare the same name that that library chose. This puts a lot of pressure on the language to support a free choice of naming for entities. Different languages make different choices in this space: - Many languages have a set of keywords that are not usable as identifiers, with no workaround. If this set collides with a name needed by user code, the user is left to solve this problem, often by rewriting the identifier in some way (`klass` or `class_`), which sometimes conflicts with the general naming convention used by the code. And conversely, suboptimal choices are made for new language keywords to avoid causing problems for existing code. - C and C++ reserve a family of identifiers, such as those beginning with an underscore and a capital letter. However, it's not clear which audiences the reserved identifiers are for, and this leads to collisions between standard library vendors and compiler authors, as well as between implementation extensions and language extensions. - MSVC provides a `__identifier(keyword)` extension that allows using a keyword as an identifier. This extension is also implemented by Clang in `-fms-extensions` mode. - GCC provides an `__asm__(symbol)` extension that allows a specific symbol to be assigned to an object or function, which provides ABI compatibility but not source compatibility with code that uses a keyword as a symbol name. This extension is also implemented by Clang. - Python reserves some identifiers but still allows them to be freely overwritten (such as `bool`) and reserves some identifiers but rejects assignment to them (such as `True`). - Rust provides a raw identifier syntax to allow most identifiers with reserved meaning to be used by a program, but [not all](https://internals.rust-lang.org/t/raw-identifiers-dont-work-for-all-identifiers/9094): `self`, `Self`, `super`, `extern`, and `crate` cannot be used as raw identifiers. Rust also predeclares a large number of library names in every file, but allows them to be shadowed by user declarations with the same name. - Swift provides a raw identifier syntax using backticks: `` `class` ``, and is [considering](https://github.com/swiftlang/swift-evolution/blob/main/proposals/0451-escaped-identifiers.md) extending this to allow arbitrary non-word-shaped character sequences between the `` ` ``s. Carbon provides [raw identifier syntax](/docs/design/lexical_conventions/words.md#raw-identifiers), for example `r#for`, to allow using keywords as identifiers. Carbon also intends to have strict shadowing rules that may make predeclared identifiers that are _not_ keywords difficult or impossible to redeclare and use in inner scopes. ## Principle In Carbon, the language does not encroach on the developer's namespace. There are no predeclared or reserved identifiers. In cases where the language gives special meaning to a word or to word-shaped syntax such as `i32`, that special meaning can always be undone with raw identifier syntax, `r#`. Conversely, when adding language keywords, we will not select an inferior keyword merely to avoid the risk of breaking existing programs. We will still take into account how often it is desirable to use the word as an identifier, including in domain-specific contexts, because that is a factor in whether it would make a good keyword, and will manage the rollout of new keywords to make it straightforward to migrate existing uses to `r#` or a different name. ## Applications of this principle - Words like `final` and `base` that only have special meaning in a few contexts, and could otherwise be made available as identifiers, are keywords in Carbon. `{.base = ...}` and `{.r#base = ...}` specify different member names. - Words like `self` that are declared by the developer but nonetheless have special language-recognized meaning are keywords in Carbon. `[self:! Self]` introduces a self parameter; `[r#self:! Self]` introduces a deduced parameter. - Words like `Self` that are implicitly declared by the language in some contexts are keywords, even though we could treat them as user-declared identifiers that are merely implicitly declared in some cases. - Words like `i32` that are treated as type literals rather than keywords can be used as identifiers with raw identifier syntax `r#i32`. - There are no predeclared identifiers imported from the prelude. If an entity is important enough to be available by default, we should add a keyword, and allow the name of the entity to be used for other purposes with `r#`. - The predeclared package name `Core` is a keyword. A package named `r#Core` is an unrelated package, and `Core.foo` always refers to members of the predeclared `Core` package. ## Exceptions For now, we reserve the package names `Main` and `Cpp`. These names aren't predeclared in any scope, and the name `Main` is not even usable from within source files to refer to the main package. However, there is currently no way to avoid collisions between the package name `Cpp` and a top-level entity named `Cpp` if they are both used in the same source file. ## Alternatives considered - [Have both predeclared identifiers and keywords](/proposals/p4864.md#have-both-predeclared-identifiers-and-keywords) - [Reserve words with a certain spelling](/proposals/p4864.md#reserve-words-with-a-certain-spelling) ================================================ FILE: docs/project/principles/one_way.md ================================================ # Principle: Prefer providing only one way to do a given thing ## Table of contents - [Background](#background) - [Principle](#principle) - [Applications of this principle](#applications-of-this-principle) - [Caveats](#caveats) - [Specialized syntax](#specialized-syntax) - [Non-obvious alternatives](#non-obvious-alternatives) - [In evolution](#in-evolution) - [Alternatives considered](#alternatives-considered) ## Background It's common in programming languages to provide multiple, similar ways of doing the same thing. Sometimes this reflects the legacy of a language, and difficulties in evolving in ways that would require changes to developer-authored code, thereby retaining backwards compatibility. Other times it reflects a desire to provide both verbose and concise versions of the same syntax. We are concerned with both forms. We also are cautious about creating alternatives that may give rise to a [paradox of choice](https://en.wikipedia.org/wiki/The_Paradox_of_Choice), wherein options are similar enough that developers actively spend time analyzing trade-offs, and the time spent that way outweighs the potential benefits of a correct choice. Where multiple, similar implementation options exist, it can sometimes give rise to style guidelines to indicate a preferential choice; sometimes because one option is objectively better, but sometimes because making a choice is better than not making one. Even with a style guide, developers may diverge in style by accident or intent, choosing different coding patterns simply because either option works. It can also become an issue as developers move between an organization that they need to learn a new style guide, and relearn habits. A couple examples of this in other languages are: - In Perl, ["There is more than one way to do it."](https://en.wikipedia.org/wiki/There%27s_more_than_one_way_to_do_it) - In Python, ["There should be one -- and preferably only one -- obvious way to do it."](https://www.python.org/dev/peps/pep-0020/) ## Principle In Carbon, we will prefer providing only one way to do a given thing. That is, given a syntax scenario where multiple design options are available, we will tend to provide _one_ option rather than providing several and letting users choose. This echoes Python's principle. Minimizing choices serves several goals: - [Language tools](/docs/project/goals.md#language-tools-and-ecosystem) should be easier to write and maintain with the lower language complexity implied by less duplication of functionality. - [Software and language evolution](/docs/project/goals.md#software-and-language-evolution) processes should find it easier to both consider existing syntax and avoid creation of new syntax conflicts. - [Understandability of code](/docs/project/goals.md#code-that-is-easy-to-read-understand-and-write) should be promoted if developers have less syntax they need to understand. This can be expected to improve code quality and productivity so long as the resulting code structures aren't overly complicated. By minimizing the overlap of language features, we hope to make work easier for both Carbon's maintainers and developers. ## Applications of this principle We can observe the application of this principle by comparing several language features to C++. There, improving understandability is frequently the primary motivation: - Where C++ allows logical operators to be written with either symbols (for example, `&&`) or text (for example, `and`), Carbon will only support one form (in this case, [text](/proposals/p0680.md)). - Where C++ allows hexadecimal numeric literals to be either lowercase (`0xaa`) or uppercase (`0xAA`), and with `x` optionally uppercase as well, Carbon will only allow the [`0xAA` casing](/proposals/p0143.md). - Where C++ provides both `struct` and `class` with the only difference is access control defaults, Carbon will only provide one (`class`, albeit with default public visibility diverging from C++). However, sometimes language tools are the primary motivation. For example, where C++ allows braces to be omitted for single-statement control flow blocks, Carbon will [require braces](/proposals/p0623.md). This offers a syntax simplification that should allow for better error detection. ## Caveats ### Specialized syntax Sometimes overlap will occur because a specialized syntax offers particular benefits, typically as a matter of convenience for either a common use-case or a particularly complex and important use-case. Some examples of why and where this occurs are: - For [performance](/docs/project/goals.md#performance-critical-software), it may at times be necessary to provide a specialized syntax that better supports optimization than a generic syntax. - For [understandability of code](/docs/project/goals.md#code-that-is-easy-to-read-understand-and-write), there may be times that a particular use-case is common enough that simplifying its syntax provides substantial benefit. - For example, `for (var x: auto in list)` could typically be written with as a `while` loop, but range-based for loops are considered to improve understandability. However, C++'s `for (;;)` syntax is sufficiently close to `while` that we expect to use `while` to address the corresponding use-cases. - For [migration and interoperability](/docs/project/goals.md#interoperability-with-and-migration-from-existing-c-code), it may be pragmatic to provide both an ideal way of doing things for new Carbon code, and a separate approach that is more C++-compatible for migration. - For example, consider generics and templates: generics are considered to be the preferred form for new code, but templates are considered a necessity for migration of C++ code. This is not an evolution situation because we do not anticipate ever removing templates. ### Non-obvious alternatives Echoing Python, there may be non-obvious alternative ways of doing a given thing, such as using `while (condition) { DoSomething(); break; }` in place of `if (condition) { DoSomething(); }`. As a more complex example, lambdas could be implemented using other code constructs; this would require significantly more code and hinder understandability. This kind of overlap may exist, but will hopefully be considered sufficiently non-idiomatic that examples won't be common in code. If a choice would not likely be based mainly on coding styles, it's likely sufficiently distinct that this principle won't apply. ### In evolution For [evolution](/docs/project/goals.md#software-and-language-evolution), it will often be necessary to temporarily provide an "old" and "new" way of doing things simultaneously. For example, if renaming a language feature, it may be appropriate to provide the same functionality under two identifiers. However, one should be marked as deprecated and eventually removed. We should be cautious of adding new, overlapping features without a plan to remove the corresponding legacy version. ## Alternatives considered - [Provide multiple ways of doing a given thing](/proposals/p0829.md#provide-multiple-ways-of-doing-a-given-thing) ================================================ FILE: docs/project/principles/progressive_disclosure.md ================================================ # Principle: Progressive disclosure ## Table of contents - [Background](#background) - [Principle](#principle) - [Applications of this principle](#applications-of-this-principle) ## Background [Progressive disclosure](https://en.wikipedia.org/wiki/Progressive_disclosure) is a UX design pattern which defers revealing information and advanced/ special-case controls to the user until they are relevant to the current task, in order to make the system easier to learn and use. Although the term originates in GUI design, it can be applied to language design as well. In particular, it's one of [Swift's core design principles](https://www.youtube.com/watch?v=CRtyWqwLM3M&t=369s). ## Principle In Carbon, we will prefer designs that support progressive disclosure, in the sense defined [here](https://www.douggregor.net/posts/swift-for-cxx-practitioners-extensions/#:~:text=By%20default%2C%20any%20code,understanding%20of%20the%20language): > ... the idea that one can ignore certain aspects of the language when starting > out, and then learn about them only at the time when you need them, without > invalidating any of your prior understanding of the language. ## Applications of this principle Types such as `i32` are designed to follow progressive disclosure: they are actually class types defined in the prelude library that support arithmetic operations by implementing certain customization interfaces. However, programmers can learn to use them by thinking of them as primitive types like their C counterparts, without needing to understand the arithmetic operator interfaces, interface implementation in general, libraries, the prelude, or even class types. Subsequently learning those concepts doesn't invalidate that original mental model, but rather shows how that model is built out of more fundamental pieces. ================================================ FILE: docs/project/principles/safety_strategy.md ================================================ # Safety strategy ## Table of contents - [Background](#background) - [What "safety" means in Carbon](#what-safety-means-in-carbon) - [Safety guarantees versus hardening](#safety-guarantees-versus-hardening) - [Philosophy](#philosophy) - [Principles](#principles) - [Details](#details) - [Incremental work when safety requires work](#incremental-work-when-safety-requires-work) - [Using build modes to manage safety checks](#using-build-modes-to-manage-safety-checks) - [Debug](#debug) - [Performance](#performance) - [Hardened](#hardened) - [Managing bugs without compile-time safety](#managing-bugs-without-compile-time-safety) - [Caveats](#caveats) - [Probabilistic techniques likely cannot stop attacks](#probabilistic-techniques-likely-cannot-stop-attacks) - [Alternatives considered](#alternatives-considered) - [Guaranteed memory safety programming models](#guaranteed-memory-safety-programming-models) - [Guaranteed compile-time memory safety using borrow checking](#guaranteed-compile-time-memory-safety-using-borrow-checking) - [Guaranteed run-time memory safety using reference counting](#guaranteed-run-time-memory-safety-using-reference-counting) - [Guaranteed run-time memory safety using garbage collection](#guaranteed-run-time-memory-safety-using-garbage-collection) - [Build mode names](#build-mode-names) - [Performance versus safety in the hardened build mode](#performance-versus-safety-in-the-hardened-build-mode) - [Add more build modes](#add-more-build-modes) ## Background Carbon's goal is to provide [practical safety and testing mechanisms](../goals.md#practical-safety-and-testing-mechanisms). ### What "safety" means in Carbon Safety is protection from software bugs, whether the protection is required by the language or merely an implementation option. Application-specific logic errors can be prevented by testing, but can lead to security vulnerabilities in production. Safety categories will be referred to using names based on the type of [security vulnerability]() they protect against. A key subset of safety categories Carbon should address are: - [**Memory safety**](https://en.wikipedia.org/wiki/Memory_safety) protects against invalid memory accesses. Carbon uses [two main subcategories](https://onlinelibrary.wiley.com/doi/full/10.1002/spe.2105) for memory safety: - _Spatial_ memory safety protects against accessing an address that's out of bounds for the source. This includes array boundaries, as well as dereferencing invalid pointers such as uninitialized pointers, `NULL` in C++, or manufactured pointer addresses. - _Temporal_ memory safety protects against accessing an address that has been deallocated. This includes use-after-free for heap and use-after-return for stack addresses. - [**Type safety**](https://en.wikipedia.org/wiki/Type_safety) protects against accessing valid memory with an incorrect type, also known as "type confusion". - [**Data race safety**](https://en.wikipedia.org/wiki/Race_condition#Data_race) protects against racing memory access: when a thread accesses (read or write) a memory location concurrently with a different writing thread and without synchronizing. ### Safety guarantees versus hardening In providing safety, the underlying goal is to prevent attacks from turning a _logic error_ into a _security vulnerability_. The three ways of doing this can be thought of in terms of how they prevent attacks: - **Safety guarantees** prevent bugs. They offer a strong requirement that a particular security vulnerability cannot exist. Compile-time safety checks are always a safety guarantee, but safety guarantees may also be done at runtime. For example: - At compile-time, range-based for loops offer a spatial safety guarantee that out-of-bounds issues cannot exist in the absence of concurrent modification of the sequence. - At runtime, garbage collected languages offer a temporal safety guarantee because objects cannot be freed while they're still accessible. - **Error detection** checks for common logic errors at runtime. For example: - An array lookup function might offer spatial memory error detection by verifying that the passed index is in-bounds. - A program can implement reference counting to detect a temporal memory error by checking whether any references remain when memory is freed. - **Safety hardening** mitigates bugs, typically by minimizing the feasibility of an attack. For example: - [Control Flow Integrity (CFI)](https://en.wikipedia.org/wiki/Control-flow_integrity) monitors for behavior which can subvert the program's control flow. In [Clang](http://clang.llvm.org/docs/ControlFlowIntegrity.html), it is optimized for use in release builds. Typically CFI analysis will only detect a subset of attacks because it can't track each possible code path separately. It should still reduce the feasibility of both spatial memory, temporal memory, and type attacks. - [Memory tagging](https://llvm.org/devmtg/2018-10/slides/Serebryany-Stepanov-Tsyrklevich-Memory-Tagging-Slides-LLVM-2018.pdf) makes each attempt at an invalid read or write operation have a high probability of trapping, while still not detecting the underlying bug in every case. Realistic attacks require many such operations, so memory tagging may stop attacks in some environments. Alternatively, the trap might be asynchronous, leaving only a tiny window of time prior to the attack being detected and program terminated. These are probabilistic hardening and reduces the feasibility of both spatial and temporal memory attacks. Under both error detection and safety hardening, even if a safety is protected, the underlying bugs will still exist and will need to be fixed. For example, program termination could be used for a denial-of-service attack. ## Philosophy Carbon's [practical safety and testing mechanisms](../goals.md#practical-safety-and-testing-mechanisms) will emphasize guaranteed safety where feasible without creating barriers to Carbon's [other goals](../goals.md), particularly performance and interoperability. This limits Carbon's options for guaranteed safety, and as a result there will be more reliance upon error detection and safety hardening. The language's design should incentivize safe programming, although it will not be required. When writing code, Carbon developers should expect to receive safety without needing to add safety annotations. Carbon will have optional safety annotations for purposes such as optimizing safety checks or providing information that improves coverage of safety checks. Carbon will favor compile-time safety checks because catching issues early will make applications more reliable. Runtime checks, either error detection or safety hardening, will be enabled where safety cannot be proven at compile-time. There will be three high-level use cases or directions that Carbon addresses through different build modes that prioritize safety checks differently: - A [debug](#debug) oriented build mode that prioritizes detecting bugs and reporting errors helpfully. - A [performance](#performance) oriented build mode that skips any dynamic safety checks to reduce overhead. - A [hardened](#hardened) oriented build mode that prioritizes ensuring sufficient safety to prevent security vulnerabilities, although it may not allow detecting all of the bugs. These high level build modes may be tuned, either to select specific nuanced approach for achieving the high level goal, or to configure orthogonal constraints such as whether to prioritize binary size or execution speed. However, there is a strong desire to avoid requiring more fundamental build modes to achieve the necessary coverage of detecting bugs and shipping software. These build modes are also not expected to be interchangeable or compatible with each other within a single executable -- they must be a global selection. Although expensive safety checks could be provided through additional build modes, Carbon will favor safety checks that can be combined into these three build modes rather than adding more. Over time, safety should [evolve](../goals.md#software-and-language-evolution) using a hybrid compile-time and runtime safety approach to eventually provide a similar level of safety to a language that puts more emphasis on guaranteed safety, such as [Rust](#guaranteed-compile-time-memory-safety-using-borrow-checking). However, while Carbon may _encourage_ developers to modify code in support of more efficient safety checks, it will remain important to improve the safety of code for developers who cannot invest into safety-specific code modifications. ## Principles - Safety must be [easy to ramp-up with](../goals.md#code-that-is-easy-to-read-understand-and-write), even if it means new developers don't receive the full safety that Carbon can offer. - Developers should benefit from Carbon's safety without needing to learn and apply Carbon-specific design patterns. Some safety should be enabled by default, without safety-specific work, although some safety will require work to opt in. Developers concerned with performance should only need to disable safety in rare edge-cases. - Where there is a choice between safety approaches, the safe option should be incentivized by making it equally easy or easier to use. If there is a default, it should be the safe option. It should be identifiable when the unsafe option is used. Incentives will prioritize, in order: 1. Guaranteed safety. 2. Error detection. 3. Safety hardening. 4. Unsafe and unmitigated code. - Language design choices should favor more efficient implementations of safety checks. They should also allow favor automation of testing and fuzzing. - Safety in Carbon must work with [interoperable or migrated C++ code](../goals.md#interoperability-with-and-migration-from-existing-c-code), so that C++ developers can readily take advantage of Carbon's improvements. - Safety mechanisms will ideally be designed to apply to automatically migrated C++ code. Providing immediate safety improvements to Carbon adopters will help motivate adoption. - In the other direction, safety mechanisms must not force manual rewriting of C++ code in order to migrate, either by creating design incompatibilities or performance degradations. Automated migration of C++ code to Carbon must work for most developers, even if it forces Carbon's safety design to take a different approach. - Carbon's safety should degrade gracefully when Carbon code calls C++ code, although this may require use of the Carbon toolchain to compile the C++ code. Applications should be expected to use interoperability. Although some safety features will be Carbon-specific, safety should not stop at the language boundary. - The rules for determining whether code will pass compile-time safety checking should be articulable, documented, and easy to understand. - Compile-time safety checks should not change significantly across different build modes. The purpose of the build modes is to determine code generation. - Each build mode will prioritize performance and safety differently: - The [debug build mode](#debug) will produce development-focused binaries that prioritize fast iteration on code with safety checks that assist in identification and debugging of errors. - The [performance build mode](#performance) will produce release-focused binaries that prioritize performance over safety. - The [hardened build mode](#hardened) will produce release-focused binaries that prioritize safety that is resistant to attacks at the cost of performance. - Safety checks should try to be identical across build modes. - There will be differences, typically due to performance overhead and detection rate trade-offs of safety check algorithms. - The number of build modes will be limited, and should be expected to remain at the named three. - Most developers will use two build modes in their work: debug for development, and either performance or hardened for releases. - It's important to focus on checks that are cheap enough to run as part of normal development. Users are not expected to want to run additional development build modes for additional sanitizers. - Limiting the number of build modes simplifies support for both Carbon maintainers, who can focus on a more limited set of configurations, and Carbon developers, who can easily choose which is better for their use-case. - Each distinct safety-related build mode (debug, performance, and hardened) cannot be combined with others in the same binary. - Cross-binary interfaces will exist in Carbon, and will need to be used by developers interested in combining libraries built under different build modes. - Although runtime safety checks should prevent logic errors from turning into security vulnerabilities, the underlying logic errors will still be bugs. For example, some safety checks would result in application termination; this prevents execution of unexpected code and still needs to be fixed. - Developers need a strong testing methodology to engineer correct software. Carbon will encourage testing and then leverage it with the checking build modes to find and fix bugs and vulnerabilities. ## Details ### Incremental work when safety requires work Carbon is prioritizing usability of the language, particularly minimizing retraining of C++ developers and easing migration of C++ codebases, over the kind of provable safety that some other languages pursue, particularly Rust. A key motivation of Carbon is to move C++ developers to a better, safer language. However, if Carbon requires manually rewriting or redesigning C++ code in order to maintain performance, it creates additional pressure on C++ developers to learn and spend time on safety. Safety will often not be the top priority for developers; as a result, Carbon must be thoughtful about how and when it forces developers to think about safety. Relying on multiple build modes to provide safety should fit into normal development workflows. Carbon can also have features to enable additional safety, so long as developers can start using Carbon in their applications _without_ learning new paradigms. Where possible, safety checks shouldn't require work on the part of Carbon developers. A safety check that requires no code edits or can be handled by automated migration may be opt-out, as there is negligible cost to developers. One which requires local code changes should be opt-in because costs will scale with codebase size. Safety check approaches which would require substantial redesign by developers will be disfavored based on adoption cost, even if the alternative is a less-comprehensive approach. ### Using build modes to manage safety checks Carbon will likely start in a state where most safety checks are done at runtime. However, runtime detection of safety violations remains expensive. In order to make as many safety checks as possible available to developers, Carbon will adopt a strategy based on three build modes that target key use-cases. #### Debug The debug build mode targets developers who are iterating on code and running tests. It will emphasize detection and debuggability, especially for safety issues. It needs to perform well enough to be run frequently by developers, but will make performance sacrifices to catch more safety issues. This mode should have runtime checks for the most common safety issues, but it can make trade-offs that improve performance in exchange for less frequent, but still reliable, detection. Developers should do most of their testing in this build mode. The debug build mode will place a premium on the debuggability of safety violations. Where safety checks rely on hardening instead of guaranteed safety, violations should be detected with a high probability per single occurrence of the bug. Detected bugs will be accompanied by a detailed diagnostic report to ease classification and root cause identification. #### Performance The performance build mode targets the typical application that wants high performance from Carbon code, where performance considers processing time, memory, and disk space. Trade-offs will be made that maximize the performance. Only safety techniques that don't measurably impact application hot path performance will be enabled by default. This is a very high bar, but is crucial for meeting Carbon's performance goals, as well as allowing migration of existing C++ systems which may not have been designed with Carbon's safety semantics in mind. #### Hardened The hardened build mode targets applications where developers want strong safety against attacks in exchange for worse performance. It will work to prevent attacks in ways that [attackers cannot work around](#probabilistic-techniques-likely-cannot-stop-attacks), even if it means using techniques that create significant performance costs. ### Managing bugs without compile-time safety Carbon's reliance on runtime checks will allow developers to manage their security risk. Developers will still need to reliably find and fix the inevitable bugs, including both safety violations and regular business logic bugs. The cornerstone of managing bugs will be strong testing methodologies, with built-in support from Carbon. Strong testing is more than good test coverage. It means a combination of: - Ensuring unsafe or risky operations and interfaces can easily be recognized by developers. - Using static analysis tools to detect common bugs, and ensuring they're integrated into build and code review workflows. These could be viewed as static testing of code. - Writing good test coverage, including unit, integration, and system tests. - Implementing coverage-directed fuzz testing to discover bugs outside of manually authored test coverage, especially for interfaces handling untrusted data. Fuzz testing is a robust way to catch bugs when APIs may be used in ways developers don't consider. - Running continuous integration, including automatic and continuous running of these tests. The checked development build mode should be validated, as well as any additional build modes necessary to cover different forms of behavior checking. - Easing automated testing and fuzzing through language features. For example, if the language encourages value types and pure functions of some sort, they can be automatically fuzzed. These practices are necessary for reliable, large-scale software engineering. Maintaining correctness of business logic over time requires continuous and thorough testing. Without it, such software systems cannot be changed and evolved over time reliably. Carbon will reuse these practices in conjunction with checking build modes to mitigate the limitations of Carbon's safety guarantees without imposing overhead on production systems. When a developer chooses to use Carbon, adhering to this kind of testing methodology is essential for maintaining safety. As a consequence, Carbon's ecosystem, including the language, tools, and libraries, will need to directly work to remove barriers and encourage the development of these methodologies. The reliance on testing may make Carbon a poor choice in some environments; in environments where such testing rigor is infeasible, a language with a greater degree of static checking may be better suited. ## Caveats ### Probabilistic techniques likely cannot stop attacks It's expected that probabilistic techniques that can be applied at the language level are attackable through a variety of techniques: - The attacker might be able to attack repeatedly until it gets through. - The attacker may be able to determine when the attack would be detected and only run the attack when it would not be. - The attacker might be able control the test condition to make detection much less likely or avoid detection completely. For example, if detection is based on the last 4 bits of a memory address, an attacker may be able to generate memory allocations, viewing the address and only attacking when there's a collision. Hardware vulnerabilities may make these attacks easier than they might otherwise appear. Future hardware vulnerabilities are difficult to predict. Note this statement focuses on what can be applied to the language level. Using a secure hash algorithm, such as SHA256, may be used to offer probabilistic defense in other situations. However, the overhead of a secure hash algorithm's calculation is significant in the context of most things that Carbon may do at the language level. Combining these issues, although it may seem like a probabilistic safety check could be proven to reliably detect attackers, it's likely infeasible to do so. For the various build modes, this means: - The debug build mode will not typically be accessible to attackers, so where a probabilistic technique provides a better developer experience, it will be preferred. - The performance build mode will often avoid safety checks in order to reach peak performance. As a consequence, even the weak protection of a probabilistic safety check may be used in order to provide _some_ protection. - The hardened build mode will prefer non-probabilistic techniques that _cannot_ be attacked. ## Alternatives considered ### Guaranteed memory safety programming models Multiple approaches that would offer guaranteed memory safety have been considered, mainly based on other languages which offer related approaches. Carbon will likely rely more on error detection and hardening because of what the models would mean for Carbon's performance and C++ migration language goals. #### Guaranteed compile-time memory safety using borrow checking Rust offers a good example of an approach for compile-time safety based on borrow checking, which provides guaranteed safety. For code which can't implement borrow checking, runtime safety using reference counting is available and provides reliable error detection. This approach still allows for [`unsafe` blocks](https://doc.rust-lang.org/rust-by-example/unsafe.html), as well as types that offer runtime safety while wrapping `unsafe` interfaces. Carbon could use a similar approach for guaranteed safety by default. Advantages: - Guaranteed safety, including against data races, is provided for the binaries. - The emphasis on compile-time safety limits the scope of the runtime memory safety costs. - With Rust, there is early evidence that there's a significant impact in reducing bugs generally. - Imitating Rust's techniques would allow building on the huge work of the Rust community, reducing the risks of implementing similar in Carbon. - Careful use of narrow `unsafe` escape hatches can be effectively encapsulated behind otherwise safe APIs. Disadvantages: - Rust's approach to compile-time safety requires use of [design patterns and idioms](https://github.com/rust-unofficial/patterns) that are substantially different from C++. - Conversion of C++ code to Rust results in either rewrites of code, or use of runtime safety checks that impair performance. - Requires fully modeling lifetime and exclusivity in the type system. - Data structures must be redesigned to avoid sharing mutable state. - Increases complexity of node and pointer based data structures, such as linked lists. - Imitating Rust's techniques may prove insufficient for achieving Carbon's [compiler performance goals](../goals.md#fast-and-scalable-development). Rust compilation performance suggests its borrow checking performance is slow, although it's difficult to determine how significant this is or whether it could be improved. - The Rust compiler [is slow](https://pingcap.com/blog/rust-compilation-model-calamity), although [much has been done to improve it](https://blog.mozilla.org/nnethercote/2020/09/08/how-to-speed-up-the-rust-compiler-one-last-time/). - Details of type checking, particularly requiring parsing of function bodies to type check signatures, as well as wide use of [monomorphization](https://doc.rust-lang.org/book/ch10-01-syntax.html) are likely significant contributors to Rust compilation performance. - LLVM codegen is also a significant cost for Rust compilation performance. - With [Fuchsia](https://fuchsia.dev/fuchsia-src/development/languages/rust) as an example, in December 2020, borrow checking and type checking combined account for around 10% of Rust compile CPU time, or 25% of end-to-end compile time. The current cost of borrow checking is obscured both because of the combination with type checking, and because Fuchsia disables some compiler parallelization due to build system incompatibility. - The complexity of using Rust's compile-time safety may incentivize unnecessary runtime checking of safety properties. For example, using [`RefCell`](https://doc.rust-lang.org/std/cell/struct.RefCell.html) or [`Rc`](https://doc.rust-lang.org/std/rc/struct.Rc.html) to avoid changing designs to fit compile-time safety models. - Some of the most essential safety tools that ease the ergonomic burden of the Rust-style lifetime model (`Rc`) introduce _semantic_ differences that cannot then be eliminated in a context where performance is the dominant priority. It's possible to modify the Rust model several ways in order to reduce the burden on C++ developers: - Don't offer safety guarantees for data races, eliminating `RefCell`. - This would likely not avoid the need for `Rc` or `Arc`, and wouldn't substantially reduce the complexity. - Require manual destruction of `Rc`, allowing safety checks to be disabled in the performance build mode to eliminate overhead. - This still requires redesigning C++ code to take advantage of `Rc`. - The possibility of incorrect manual destruction means that the safety issue is being turned into a bug, which means it is hardening and no longer a safety guarantee. - Carbon can provide equivalent hardening through techniques such as [MarkUs](https://www.cl.cam.ac.uk/~tmj32/papers/docs/ainsworth20-sp.pdf), which does not require redesigning C++ code. Overall, Carbon is making a compromise around safety in order to give a path for C++ to evolve. C++ developers must be comfortable migrating their codebases, and able to do so in a largely automated manner. In order to achieve automated migration, Carbon cannot require fundamental redesigns of migrated C++ code. While a migration tool could in theory mark all migrated code as `unsafe`, Carbon should use a safety strategy that degrades gracefully and offers improvements for C++ code, whether migrated or not. That does not mean Carbon will never adopt guaranteed safety by default, only that performance and migration of C++ code takes priority, and any design will need to be considered in the context of other goals. It should still be possible to adopt guaranteed safety later, although it will require identifying a migration path. #### Guaranteed run-time memory safety using reference counting [Reference counting](https://en.wikipedia.org/wiki/Reference_counting) is a common memory safety model, with Swift as a popular example. Advantages: - Simple model for safety, particularly as compared with Rust. - Safe for all of the most common and important classes of memory safety bugs. Disadvantages: - Safety based on reference counting introduces significant performance costs, and tools for controlling these costs are difficult. - Safety based on garbage collection has less direct performance overhead, but has a greater unpredictability of performance. - Significant design differences versus C++ still result, as the distinction between value types and "class types" becomes extremely important. - Class types are held by a reference counted pointer and are thus lifetime safe. In order to mitigate the performance overhead, Swift does have a proposal to add an option for unique ownership, although the specifics are not designed yet. The unique ownership approach is expected to require unowned and unsafe access, so it would not considered to improve the safety trade-offs. Swift was designated by Apple as the replacement for Objective-C. The safety versus performance trade-offs that it makes fit Apple's priorities. Carbon's performance goals should lead to different trade-off decisions with a higher priority on peak performance, which effectively rules out broad use of reference counting. #### Guaranteed run-time memory safety using garbage collection [Garbage collection]() is a common memory safety model, with Java as a popular example. Advantages: - This approach is among the most robust and well studied models, with decades of practical usage and analysis for security properties. - Extremely suitable for efficient implementation on top of a virtual machine, such as the JVM. Disadvantages: - Extremely high complexity to fully understand the implications of complex cases like data races. - Performance overhead is significant in terms of what Carbon would like to consider. - Garbage collection remains a difficult performance problem, even for the JVM and its extensive optimizations. - The complexity of the implementation makes it difficult to _predict_ performance; for example, Java applications experience latency spikes when garbage collection runs. Java is a good choice for many applications, but Carbon is working to focus on a set of performance priorities that would be difficult to achieve with a garbage collector. ### Build mode names The build mode concepts are difficult to name. Other names that were evaluated, and are ultimately similar, are: - "Debug" is a common term for the intended use of this build mode. Also, tooling including Visual Studio frequently uses the debug term for describing similar. - "Development" was also considered, but this term is less specific and would be better for describing all non-release builds together. For example, a "fast build" mode might be added that disables safety checks to improve iteration time, like might be controlled by way of C++'s `NDEBUG` option. - "Performance" aligns with the phrasing of the language performance goal. - "Optimized" implies that other modes would not be fully optimized, but hardened should be optimized. - "Fast" would suggest that speed is the only aspect of performance being optimizing for, but "performance" also optimizes for memory usage and binary size. - "Hardened" is the choice for succinctly describing the additional safety measures that will be taken, and is a well-known term in the safety space. It could be incorrectly inferred that "performance" has no hardening, but the preference is to clearly indicate the priority of the "hardened" build mode. - "Safe" implies something closer to guaranteed safety. However, safety bugs should be expected to result in program termination, which can still be used in other attacks, such as Denial-of-Service. - "Mitigated" is an overloaded term, and it may not be succinctly clear that it's about security mitigations. - Some terms which were considered and don't fit well into the above groups are: - "Release" is avoided because both "performance" and "hardened" could be considered to be "release" build modes. The names "performance" and "hardened" may lead to misinterpretations, with some developers who should use "hardened" using "performance" because they are worried about giving up too much performance, and the other way around. The terms try to balance the utility of well-known terminology with the succinctness of a short phrase for build modes, and that limits the expressivity. Some confusion is expected, and documentation as well as real-world experience (for example, a developer who cares about latency benchmarking both builds) should be expected to help mitigate mix-ups. ### Performance versus safety in the hardened build mode The performance cost of safety techniques are expected to be non-linear with respect to detection rates. For example, a particular vulnerability such as heap use-after-free may be detectable with 99% accuracy at 20% performance cost, but 100% accuracy at 50% performance cost. At present, build modes should be expected to evaluate such a scenario as: - The debug build mode would choose the 99% accurate approach. - Detecting safety issues is valuable for debugging. - The probabilistic detection rate won't meaningfully affect accuracy of tests. - The lower performance cost improves developer velocity. - The performance build mode would decline detection. - Safety checks with a measurable performance cost should be declined. - The hardened build mode would choose the 100% accurate approach. - Safety must be non-probabilistic in order to reliably prevent attacks. - Significant performance hits are acceptable. - This means the hardened build mode may be slower than the debug build mode. In order to achieve better performance, the hardened build mode could make trade-offs closer to the debug build mode. Rather than relying on non-probabilistic techniques, it could instead offer a probability-based chance of detecting a given attack. Advantages: - Probabilistic safety should come at lower performance cost (including CPU, memory, and disk space). - This will sometimes be significant, and as a result of multiple checks, could result in the hardened build mode being 50% slower than the performance build mode instead of being 200% slower. Disadvantages: - [Probabilistic techniques likely cannot stop attacks](#probabilistic-techniques-likely-cannot-stop-attacks). - Attackers may be able to repeat attacks until they succeed. - The variables upon which the probability is based, such as memory addresses, may be manipulable by the attacker. As a consequence, a determined attacker may be able to manipulate probabilities and not even be detected. Although performance is [Carbon's top goal](../goals.md#language-goals-and-priorities), the hardened build mode exists to satisfy developers and environments that value safety more than performance. The hardened build mode will rely on non-probabilistic safety at significant performance cost because other approaches will be insufficient to guard against determined attackers. ### Add more build modes More build modes could be added to this principle, or the principle could encourage the idea that specific designs may add more. To explain why three build modes: - The concept of debug and release (sometimes called opt) are common. For example, in [Visual Studio](https://docs.microsoft.com/en-us/visualstudio/debugger/how-to-set-debug-and-release-configurations?view=vs-2019). In Carbon, this could be considered to translate to the "debug" and "performance" build modes by default. - The hardened build mode is added in order to emphasize security. Although hardened could be implemented as a set of options passed to the standard release build mode, the preference is to focus on it as an important feature. An example of why another build mode may be needed is [ThreadSanitizer](https://clang.llvm.org/docs/ThreadSanitizer.html), which is noted as having 5-15x slowdown and 5-10x memory overhead. This is infeasible for normal use, but could be useful for some users in a separate build mode. A trade-off that's possible for Carbon is instead using an approach similar to [KCSAN](https://github.com/google/ktsan/wiki/KCSAN) which offers relatively inexpensive but lower-probability race detection. Although options to these build modes may be supported to customize deployments, the preference is to focus on a small set and make them behave well. For example, if a separate build mode is added for ThreadSanitizer, it should be considered a temporary solution until it can be merged into the debug build mode. Advantages: - Grants more flexibility for using build modes as a solution to problems. - With safety checks, this would allow providing safety checks that are high overhead but also high detection rate as separate build modes. - With other systems, there could be non-safety performance versus behavior trade-offs. Disadvantages: - Having standard modes simplifies validation of interactions between various safety checks. - Safety is the only reason that's been considered for adding build modes. - As more build modes are added, the chance of developers being confused and choosing the wrong build mode for their application increases. Any long-term additions to the set of build modes will need to update this principle, raising the visibility and requiring more consideration of such an addition. If build modes are added for non-safety-related reasons, this may lead to moving build modes out of the safety strategy. **Experiment**: This can be considered an experiment. Carbon may eventually add more than the initial three build modes, although the reluctance to add more is likely to remain. ================================================ FILE: docs/project/principles/static_open_extension.md ================================================ # Principle: One static open extension mechanism ## Table of contents - [Background](#background) - [Principle](#principle) - [Alternatives considered](#alternatives-considered) ## Background In C++, a single function may be overloaded with definitions in multiple files. The [ADL](https://en.wikipedia.org/wiki/Argument-dependent_name_lookup) name lookup rule even allows an unqualified call to resolve to functions defined in different namespaces. These rules are used to define extension points with static dispatch for operator overloading and functions like [`swap`](https://www.cplusplus.com/reference/algorithm/swap/). Nothing in C++ restricts the signatures of function overloads. This means that if overloading is used as an extension point to define an operation for a variety of types, there is no way to type check generic code that tries to invoke that operation over those types. Further, in C++, all non-member functions can be found in this way, if they are declared in the same namespace as a type that could be associated with an argument list in a call. There is no straightforward opt-out mechanism in a function declaration, and while there are opt-out mechanisms at call sites, they are rarely used. As a consequence, many non-member functions with the same name can form part of an overload set, even if they provide unrelated functionality, and there is no indication in the code of which functions in different namespaces are intended to expose the same capability. ## Principle In Carbon, [`interface`s](/docs/design/generics/overview.md) are the only static open extension mechanism. Each type may define its own implementation of each interface. Generic code can be written that works with any type implementing the interface. That code can be type checked independent of which type the generic code is instantiated with by using the fact that the interface specifies the signatures of the calls. To keep the language simple, this is the only static open extension mechanism in Carbon. This means that function overloading is limited in Carbon to only signatures defined together in the same library. It also means that to interoperate with C++, the operators and `swap` need to have corresponding interfaces on the Carbon side. The main advantage of interfaces as an open extension mechanism over open overloading is allowing generics to be type checked separately. In addition, they are less [context sensitive](low_context_sensitivity.md). Generics are [coherent](/docs/design/generics/terminology.md#coherence), while open function overloading can resolve names differently depending on what is imported. Closed overloading in Carbon also simplifies what gets exported to C++ from Carbon. Interface implementations express intent by being explicit, in contrast to how adding a function to a cross-file overload set can be accidental. Interfaces provide an way to group functions together, and express the constraint that all of the functions in the group are implemented. Consider a random-access iterator, which has a number of methods. If a C++ template function only accesses some of those methods which happens to match the subset defined for a type, the code will work temporarily but fail later when the code is changed to use a different subset. This helps achieve the Carbon Goal of [code that is easy to read, understand, and write](/docs/project/goals.md#code-that-is-easy-to-read-understand-and-write). ## Alternatives considered Another approach to operator overloading is to use methods with a specific name. In C++ these start with the [`operator` keyword](https://en.cppreference.com/w/cpp/language/operators). [Python uses method with names starting and ending with double underscores](https://docs.python.org/3/reference/datamodel.html#special-method-names). Interfaces are more flexible about where implementations may be defined. For example, with special method names, `+` on a `Vector(T)` class could only be defined as part of the `Vector(T)` definition. With interfaces, additionally `+` for `Vector(MyType)` could be implemented with `MyType`. C++ provides this flexibility by also permitting non-method operator overloads, but this brings with it the cost of selecting a best-matching operator from a potentially very large open overload set. ================================================ FILE: docs/project/principles/success_criteria.md ================================================ # Principle: Success criteria ## Table of contents - [Principle](#principle) - [Applications of these principles](#applications-of-these-principles) - [Modern OS platforms, hardware architectures, and environments](#modern-os-platforms-hardware-architectures-and-environments) - [OS platforms](#os-platforms) - [Hardware architectures](#hardware-architectures) - [Historical platforms](#historical-platforms) - [Interoperability with and migration from existing C++ code](#interoperability-with-and-migration-from-existing-c-code) - [Migration tooling](#migration-tooling) ## Principle Carbon's goals set a high-level path for where Carbon should head. However, given priorities, it's not always clear how specific features or details may end up being evaluated. Carbon's success criteria are specific, measurable, key results that we expect to use to see how Carbon is doing against its goals. Success criteria will be considered as part of Carbon's [roadmap process](../roadmap_process.md), missing them will be considered significant, and extra scrutiny will be applied on proposals that would require diminishing them. These success criteria are not exhaustive, but they are a bar that we aim to _exceed_. ## Applications of these principles > TODO: Add more metrics for various goals. ### Modern OS platforms, hardware architectures, and environments > References: > [goal](../goals.md#modern-os-platforms-hardware-architectures-and-environments) This should not be considered an exhaustive list of important platforms. #### OS platforms Our priority OS platforms are modern versions of: - Linux, including common distributions, Android and ChromeOS - FreeBSD - Windows - macOS and iOS - Fuchsia - WebAssembly - Bare metal #### Hardware architectures We expect to prioritize 64-bit little endian hardware, including: - x86-64 - AArch64, also known as ARM 64-bit - PPC64LE, also known as Power ISA, 64-bit, Little Endian - RV64I, also known as RISC-V 64-bit We believe Carbon should strive to support some GPUs, other restricted computational hardware and environments, and embedded environments. While this should absolutely include future and emerging hardware and platforms, those shouldn't disproportionately shape the fundamental library and language design while they remain relatively new and rapidly evolving. #### Historical platforms Example historical platforms that we will not prioritize support for are: - Byte sizes other than 8 bits, or non-power-of-two word sizes. - Source code encodings other than UTF-8. - Big- or mixed-endian, at least for computation; accessing encoded data remains useful. - Non-2's-complement integer formats. - Non-IEEE 754 binary floating point format and semantics for default single- and double-precision floating point types. - Source code in file systems that don’t support file extensions or nested directories. ### Interoperability with and migration from existing C++ code > References: > [goal](../goals.md#interoperability-with-and-migration-from-existing-c-code) #### Migration tooling Migrations must be mostly automatic. To that end, given an arbitrary large codebase following best practices, we aim to have less than 2% of files require human interaction. This criterion includes: - Addressing performance bugs unique to Carbon, introduced by migration tooling. - Converting complex code which migration tooling does not handle. This criterion does not include: - Cleaning up coding style to idiomatic Carbon. - For example, heavy use of C++ preprocessor macros may result in expanded code where there is no equivalent Carbon metaprogramming construct. ================================================ FILE: docs/project/pull_request_workflow.md ================================================ # Trunk-based pull-request GitHub workflow ## Table of contents - [Overview](#overview) - [Trunk based development](#trunk-based-development) - [Green tests](#green-tests) - [Always use pull requests (with review) rather than pushing directly](#always-use-pull-requests-with-review-rather-than-pushing-directly) - [Small, incremental changes](#small-incremental-changes) - [Linear history](#linear-history) - [Merging a pull request](#merging-a-pull-request) ## Overview Carbon repositories follow a few basic principles: - Development directly on the `trunk` branch and [revert to green](#green-tests). - Always use pull requests, rather than pushing directly. - Changes should be small, incremental, and review-optimized. - Preserve linear history by [squashing](https://help.github.com/en/github/collaborating-with-issues-and-pull-requests/about-pull-request-merges#squash-and-merge-your-pull-request-commits) pull requests rather than using unsquashed merge commits. These principles try to optimize for several different uses or activities with version control: - Continuous integration and bisection to identify failures and revert to green. - Code review both at the time of commit and follow-up review after commit. - Understanding how things evolve over time, which can manifest in different ways: - When were things introduced? - How does the main branch and project evolve over time? - How was a bug or surprising thing introduced? Note that this document focuses on the mechanical workflow and branch management. Details of the code review process are in their own [document](code_review.md). ## Trunk based development We work in a simple [trunk-based development](https://trunkbaseddevelopment.com/) model. This means all development activity takes place on a single common `trunk` branch in the repository (our default branch). We focus on [small, incremental changes](#small-incremental-changes) rather than feature branches or the "scaled" variations of this workflow. ### Green tests The `trunk` branch should always stay "green". That means that if tests fail or if we discover bugs or errors, we revert to a "green" state by default, where the failure or bug is no longer present. Fixing forward is fine if that will be comparably fast and efficient. The goal isn't to dogmatically avoid fixing forward, but to prioritize getting back to green quickly. We hope to eventually tool this through automatic continuous-integration powered submit queues, but even those can fail and the principle remains. ## Always use pull requests (with review) rather than pushing directly We want to ensure that changes to Carbon are always reviewed, and the simplest way to do this is to consistently follow a pull request workflow. Even if the change seems trivial, still go through a pull request -- it'll likely be trivial to review. Always wait for someone else to review your pull request rather than just merging it, even if you have permission to do so. Our GitHub repositories are configured to require pull requests and review before they are merged, so this rule is enforced automatically. ## Small, incremental changes Developing in small, incremental changes improves code review time, continuous integration, and bisection. This means we typically squash pull requests into a single commit when landing. We use two fundamental guides for deciding how to split up pull requests: 1. Ensure that each pull request builds and passes any tests cleanly when you request review and when it lands. This will ensure bisection and continuous integration can effectively process them. 2. Without violating the first point, try to get each pull request to be "just right": not too big, not too small. You don't want to separate a pattern of tightly related changes into separate requests when they're easier to review as a set or batch, and you don't want to bundle unrelated changes together. Typically you should try to keep the pull request as small as you can without breaking apart tightly coupled changes. However, listen to your code reviewer if they ask to split things up or combine them. While the default is to squash pull requests into a single commit, _during_ the review you typically want to leave the development history undisturbed until the end so that comments on any particular increment aren't lost. We typically use the GitHub squash-and-merge functionality to land things. ## Linear history We want the history of the `trunk` branch of each repository to be as simple and easy to understand as possible. While Git has strong support for managing complex history and merge patterns, we find understanding and reasoning about the history -- especially for humans -- to be at least somewhat simplified by sticking to a linear progression. As a consequence, pull requests are squashed when merging them. ## Merging a pull request Once approved, and even if all checks have not finished running yet, a pull request can be added to a [merge queue](https://docs.github.com/en/repositories/configuring-branches-and-merges-in-your-repository/configuring-pull-request-merges/managing-a-merge-queue#about-merge-queues). When queued, and after all pull request checks pass, this will automatically create a squashed version of the commits in a temporary branch on top of `trunk` and any previously queued merge. Checks run on this commit help to ensure that the `trunk` stays "green" in presence of conflicting merges. After the merge queue checks pass, the `trunk` branch pointer is updated to include the now merged changeset. The resulting commit title and description will match exactly those of the pull request, so it is important to set those appropriately before merging. Co-authors are preserved by this operation. If a failure happens at any point, the merge fails, with both `trunk` and the pull request branch kept in their original state. ================================================ FILE: docs/project/roadmap.md ================================================ # Roadmap ## Table of contents - [Objectives for 2025: demo of C++ interop and design of memory safety](#objectives-for-2025-demo-of-c-interop-and-design-of-memory-safety) - [Key results in 2025](#key-results-in-2025) - [Access most non-template C++ APIs in Carbon](#access-most-non-template-c-apis-in-carbon) - [Access non-generic Carbon APIs in C++](#access-non-generic-carbon-apis-in-c) - [Detailed safety strategy update, including expected tradeoffs and prioritization](#detailed-safety-strategy-update-including-expected-tradeoffs-and-prioritization) - [Design for compile-time temporal and mutation memory safety](#design-for-compile-time-temporal-and-mutation-memory-safety) - [Give talks at 2-3 conferences about Carbon topics, expanding our audience](#give-talks-at-2-3-conferences-about-carbon-topics-expanding-our-audience) - [Beyond 2025](#beyond-2025) - [Potential 2026 goals: ship a working 0.1 language for evaluation](#potential-2026-goals-ship-a-working-01-language-for-evaluation) - [Potential 2027-2028 goals: finish 0.2 language, stop experimenting](#potential-2027-2028-goals-finish-02-language-stop-experimenting) - [Potential goals _beyond_ 2028: ship 1.0 language & organization](#potential-goals-beyond-2028-ship-10-language--organization) ## Objectives for 2025: demo of C++ interop and design of memory safety We have two areas of focus for 2025: 1. Get a major chunk of our C++ interop working to the point where we can demonstrate it in realistic scenarios. 2. Build a concrete and specific design for memory safety in Carbon. We will scope the first one to non-template C++ APIs, and prioritize accessing C++ APIs from Carbon. This still will require major progress on the implementation of all the relevant Carbon features, and even design in some cases. The second is focused on moving from a vague direction of "we will have a memory safe dialect of Carbon that is a reasonable default", to a specific and concrete design. We want to be able to illustrate exactly what it will look like to migrate existing unsafe C++ to Carbon (possibly at large scale), and then begin incrementally adopting and integrating memory safety into that otherwise unsafe Carbon codebase. Achieving these should dramatically reduce the risk around Carbon, especially in environments where memory safety is increasingly a necessary part of any future software development plans. They will also move the project much closer to our 0.1 milestone. ## Key results in 2025 ### Access most non-template C++ APIs in Carbon Beyond excluding templates, this excludes coroutines, and any aspects that require accessing Carbon types in C++ such as templates with Carbon types as template arguments. This result includes both the implementation in the toolchain and the underlying design underpinning this implementation. It also includes implementation and design work on necessary Carbon language features that underpin the interop provided. ### Access non-generic Carbon APIs in C++ This excludes generics to make the scope more tractable, but this remains a bit of a stretch goal for 2025, and how much progress we make will depend on how many unexpected difficulties we encounter getting the other direction to work, and any other delays. ### Detailed safety strategy update, including expected tradeoffs and prioritization We haven't been focused on the safe side of Carbon for several years and will need to refresh our safety strategy to reflect the current plan, as well as expanding and making it more detailed to support building our initial memory safety design. ### Design for compile-time temporal and mutation memory safety We expect our memory safety story for temporal memory safety to at the highest level follow the direction of Rust, using the type system to ensure compile-time guarantees of safety without the runtime overhead of garbage collection or reference counting. We want our design here to cover both temporal and mutation safety. While the exact level of safety and the tradeoffs we're willing to accept will be part of updating our safety strategy, at a fundamental level we need to fully address the security requirements on memory safety, much like other modern languages including Swift, Kotlin, Go, or Rust. A significantly lower security bar won't be acceptable for the expected users of safe Carbon. ### Give talks at 2-3 conferences about Carbon topics, expanding our audience Beyond continuing to share details about Carbon with the open source and C++ communities, we also want to expand our audience reach in 2025. We want to give talks at a conference in the Asia/Pacific region, and at a conference in the broader open source world beyond LLVM and C++ specific conferences. ## Beyond 2025 Longer term goals are hard to pin down and always subject to change, but we want to give an idea of what kinds of things are expected at a high level further out in order to illustrate how the goals and priorities we have in 2025 feed into subsequent years. ### Potential 2026 goals: ship a working [0.1 language] for evaluation [0.1 language]: /docs/project/milestones.md#milestone-01-a-minimum-viable-product-mvp-for-evaluation Because we are adding a design for memory safety to our 0.1 milestone, we are also expecting to push it out by at least a year. Shipping 0.1 in 2026 will be a very ambitious goal and may not be possible, but the end of 2026 is now the _soonest_ that 0.1 could realistically be ready to ship. We expect that once we reach this milestone the community will be able to start realistically evaluating Carbon as a C++ successor language. Of course, this evaluation will take some time. ### Potential 2027-2028 goals: finish [0.2 language], stop experimenting [0.2 language]: /docs/project/milestones.md#milestone-02-feature-complete-product-for-evaluation Once Carbon is moving quickly and getting public feedback, we should be able to conclude the experiment. We should know if this is the right direction for moving C++ forward for a large enough portion of the industry and community, and whether the value proposition of this direction outweighs the cost. However, there will still be a lot of work left to make Carbon into a production quality language, even if the experiment concludes successfully. Some concrete goals that might show up in this time frame: - Self-hosting toolchain, including sufficient Carbon standard library support. - Expand design of standard library to include, at least directionally, critical and complex areas. For example: concurrency/parallelism and networking/IO. - Migration tooling sufficient to use with real-world libraries and systems. This might be used to help with self-hosting Carbon, as well as by initial early adopters evaluating Carbon. - Create a foundation or similar organization to manage the Carbon project, separate from any corporate entities that fund work on Carbon. ### Potential goals _beyond_ 2028: ship [1.0 language] & organization [1.0 language]: /docs/project/milestones.md#milestone-10-no-longer-an-experiment-usable-in-production A major milestone will be the first version of a production language. We also plan to finish transferring all governance of Carbon to an independent open source organization at that point. However, we won't know what a more realistic or clear schedule for these milestones will be until we get closer. Goals in this time frame will expand to encompass the broader ecosystem of the language: - End-to-end developer tooling and experience. - Teaching and training material. - Package management. - Etc. ================================================ FILE: docs/project/roadmap_process.md ================================================ # Roadmap process Carbon has an annual roadmap to align and focus the work of the teams and community. Teams will need to defer work on Carbon that may be good and make sense but doesn't align with the current focus and plan of the project. The core team will draft proposed roadmaps each year, going through the standard proposal review process. Objectives and key results will be based on [goals](goals.md), [success criteria](principles/success_criteria.md), and tactical features. It is expected that the core team will provide a draft decision and enter decision review first thing at the beginning of the year, concluding in an accepted plan of record for the overarching direction of the project for that year. Subteams may optionally follow a similar practice as needed for their area of Carbon. This roadmap is not strictly binding and does not need to cover everything that will happen. However, it can and should be used by teams to defer some proposals as needed to focus on proposals aligned with the current roadmap. The roadmap is also subject to change, just as any other document, with a fresh proposal. The core team should critically evaluate the direction and any new information on a quarterly basis during the initial phase of the project and adjust the roadmap as needed to stay focused on the most important things. ================================================ FILE: docs/project/teams/conduct_team.md ================================================ # Conduct Team This team owns and drives the project's Code of Conduct, questions, concerns, and conduct reports. This page is for the _team_, see our actual [Code of Conduct](/CODE_OF_CONDUCT.md) for the code and reporting details. ## Contact and current team members The Conduct Team can be reached by emailing conduct@carbon-lang.dev -- this can be used for questions, concerns, or reports of specific conduct issues. The current members of this list are: - Allison Poppe (@acpoppe on Discord and GitHub) - Céline Dedaj (@celineausberlin on Discord and GitHub) - Christopher Di Bella (@cjdb.work on Discord, @cjdb on GitHub) - Lexi Bromfield (@lexinadia on Discord and @lexi-nadia on GitHub) For more details on reporting conduct to the team, please see the [reporting conduct](/CODE_OF_CONDUCT.md#reporting-conduct) documentation. ## Team structure and updates The Conduct Team's goal is to have at least 5 trained conduct team members to deal with conduct-related escalations on behalf of and for the Carbon community. There should always be at least 3 conduct team members available to respond promptly, even factoring in vacations, getting sick, or other normal disruptions. The Conduct Team is responsible for recruiting new members as needed to achieve these goals, and when trained and ready, the team will directly update this documentation and add them to the respective infrastructure. The Conduct Team is typically recruited from among our active moderators -- if you are interested, reaching out to any of the moderators about helping with moderation is a good first step. ================================================ FILE: docs/project/transparency_reports.md ================================================ # Conduct and moderation transparency reports ## Overview Carbon regularly publishes transparency reports that cover conduct issues that have come up in our spaces as well as the moderation and other actions taken in response. We prioritize keeping the Carbon community [welcoming and inclusive](/docs/project/goals.md#community-and-culture) and believe this kind of transparency is essential to sustaining that. ## Cadence and publishing We publish a report quarterly in a GitHub discussion thread in the [transparency reports](https://github.com/carbon-language/carbon-lang/discussions/categories/transparency-reports) topic. We use the following template for each of these reports. We may publish outside of the quarterly cycle if needed for an urgent response. ## Template The Carbon community works to be welcoming and kind among itself and to others, with a deep commitment to psychological safety, and we want to ensure that doesn’t change as we grow and evolve. To that end, we have a few ground rules that we ask all community members to adhere to: - be welcoming, - be friendly and patient, - be considerate, - be kind, - be careful in the words that we choose, - when we disagree, try to understand why, and - recognize when progress has stopped, and take a step back. The following summary is intended to help the community understand what kinds of Code of Conduct (CoC) incidents were brought to our attention lately, and how we dealt with them. Publishing such transparency reports in the future will help us track progress and hold ourselves accountable to high standards of community culture. ### Summary _[overview with overall number of interventions and mention of major community related events during that period]_ Please note that some incidents may have escaped our attention. Please help us keep our spaces welcoming and fostering a spirit of collaboration, and report any situation that may require our intervention: https://github.com/carbon-language/carbon-lang/blob/trunk/CODE_OF_CONDUCT.md ### Our interventions from _[YYYY-MM-DD (start date)]_ through _[YYYY-MM-DD (end date)]_ _[mention of preventive measures if applicable]_ These are the conduct incidents that were brought to our attention during the last documentation period with the relevant sections of the Carbon community Code of Conduct when this report was published: - > _[Quote1 from the Code of Conduct related to the following interventions]_ _[Number of related incidents, and corresponding moderation interventions]_ - > _[Quote2 from the Code of Conduct related to the following interventions]_ _[Number of related incidents, and corresponding moderation interventions]_ - > _[Quote3 from the Code of Conduct related to the following interventions]_ _[Number of related incidents, and corresponding moderation interventions]_ ### Closing observations Thank you all for helping us keep such a fantastic Carbon community, -- The Carbon Language community moderation team, together with the Carbon leads. ================================================ FILE: docs/project/versioning.md ================================================ # Toolchain and language versioning [Pull request](https://github.com/carbon-language/carbon-lang/pull/4105) ## Table of contents - [Overview](#overview) - [Major version increments](#major-version-increments) - [Breaking changes](#breaking-changes) - [Exclusions to what constitutes a breaking change](#exclusions-to-what-constitutes-a-breaking-change) - [Minor version increments](#minor-version-increments) - [Patch version increments](#patch-version-increments) - [Examples:](#examples) - [Pre-release versions](#pre-release-versions) - [Release qualification pre-releases: `MAJOR.MINOR.PATCH-rc.N`](#release-qualification-pre-releases-majorminorpatch-rcn) - [Incremental development versions: `MAJOR.MINOR.PATCH-0.{nightly,dev}.N`](#incremental-development-versions-majorminorpatch-0nightlydevn) - [Nightly pre-release versions](#nightly-pre-release-versions) - [Development pre-release versions](#development-pre-release-versions) - [Relevant proposal](#relevant-proposal) ## Overview Carbon uses a single versioning scheme across both the language itself and toolchain, including the standard library, compiler, linker, and all development tools released by the main Carbon project. The scheme conforms to and is closely based on Semantic Versioning (https://semver.org/ -- version 2.0.0): - Carbon versions: `MAJOR.MINOR.PATCH` - Releases with backwards incompatible changes, including a deprecation that might trigger a build-breaking warning, increment `MAJOR` after reaching the `1.0` milestone, and increment `MINOR` before then. - Releases with only backwards compatible changes are not expected to happen. - Releases containing only bug fixes increment `PATCH`. - Pre-release suffixes: - `MAJOR.MINOR.PATCH-rc.N`: The N-th potentially viable candidate for a release. - `MAJOR.MINOR.PATCH-0.nightly.YYYY.MM.DD`: A nightly incremental development build on a particular day during development of that version. - `MAJOR.MINOR.PATCH-0.dev`: An interactive, incremental development build on a particular day by some developer during development of that version. See the sections below for the details of each aspect of these versions. ## Major version increments Aligned with SemVer, the major version must increment for any _breaking change_ to the language or any part of the toolchain. The first increment from 0 to 1 is expected to be based on achieving a desired milestone of feature completeness and quality to declare the language to have reached a stable version. Subsequent increments are expected to be done with a time-based release strategy. Features (or breaking changes) ready to ship will do so, and others will wait for the next major release. The exact cadence used is future work and should be determined based on discussions with Carbon's users in the ramp up to and after reaching the 1.0 milestone. However, just because we increment the major version for a major release and _can_ make breaking changes doesn't mean we _will_ or _should_. Breaking language changes are extraordinarily expensive for users due to the inherent scale of churn they impose. It is tempting to try to make non-breaking releases instead, but our experience with C++ language, compiler, and standard library updates is that truly making no breaking changes is extremely difficult and overly constraining. We expect most releases, especially in the early phases of the language to involve _some_ amount of breaking change and will simply work to make these as cheap to upgrade through and minimal as we can. At some future point, it is possible that Carbon will become so stable that it makes sense to consider using minor version increments for some releases. If and when this happens, we should revisit our versioning and release policies to establish a predictable and unsurprising structure. ### Breaking changes Beyond traditional breaking API changes in either standard library APIs or tool APIs, Carbon also includes breaking changes in the language or toolchain. Language and toolchain breaking changes are any that cause correct, functioning, and non-reflective code to become invalid, rejected, incorrect, or silently change behavior. ### Exclusions to what constitutes a breaking change Carbon excludes "reflective code" which is in some way detecting the Carbon version, or the presence or absence of features and as a consequence can be "broken" by detecting changes that are correctly designed to otherwise be non-breaking. We don't want adding features to be considered a breaking change and so exclude code that specifically detects such additions. Carbon also excludes breaking changes to incorrect code unless it was accepted and functioning in some useful, and typically widespread, way despite its bugs. ## Minor version increments Currently, Carbon plans to primarily use the minor version increments with a 0 major version to track our progress towards our 1.0 milestone of a feature complete initial language. As a consequence we have defined 0.1 and 0.2 milestones and may define more steps as needed. Beyond this and in a post-1.0 language world, we expect most significant features to also accompany some small and manageable breaking changes from deprecations. We may choose to revisit this in the future, but our current plan is not to make minor version releases post-1.0 and instead focus on our commitment to making those updates both easy and scalable for language users. ## Patch version increments The patch version will increment when the change is fundamentally a bug fix to a previously released version. We expect the vast majority of these to be strictly backwards compatible bug fixes. Patch releases are expected to be driven by demand, and not necessarily present if unnecessary. However, the exact schedule and process will be determined as part of getting ready for the 1.0 milestone. Before that milestone we don't commit to any particular process or cadence for patch releases as no release before that point should be considered stable. Note that we still consider restoring the _intended_ "public API" of a release to be a bug fix. When these bug fixes theoretically break new code in a way that would typically require a major version increment, they may be made with merely a patch version increment when they are in fact restoring our intended behavior for that release. However, we take the SemVer guarantees very seriously as these fixes can still be disruptive and so we work to hold a high bar for them: - They must be in some way fixing a _regressions_ for users from a previous release, not merely a missing feature. - Can even be a regression in the overall cohesion or reliability of the language or tools, which a bug in a new feature might erode. - Key is that we motivate any patch fix through the lens of a regression fix and stabilization rather than fixing forward. - The scope of disruption from the fix is demonstrably small, due to some combination of: - The short time duration of the release containing the regression. - The narrow scope of code constructs potentially impacted. - The impact of the regression is large and cannot be easily worked around, for example: - Undermining a core priority of the Carbon Language for a significant set of users. - Making the engineering cost of adopting the release uneconomical for any significant body of users. ### Examples: - We add a new feature that includes a bug which creates unsoundness and allows incorrect code to be compiled that will crash or exhibit UB when executed. - While this is a new feature and not a bug in an existing feature, it would be a _serious_ regression to the reliability of the language as a whole and the ability of users to reason about the correctness of programs. - This would be a good candidate for a patch release to address unless it is found very late (months) after the release _and_ the only ways to address would have similarly bad effects on code written since the initial release. - Even that level of user disruption could potentially be overcome if for example the bug led to security vulnerabilities. - We add a narrowly used new feature that includes a bug where some code patterns that _should_ work with the feature are rejected at compile time or crash reliably. - Unlikely this is worth a patch release to make an invasive change given the narrow use case. - A good candidate to introduce a warning or error on using the feature in the way that might lead to a crash, and possibly on using the feature at all. - We add a compiler opt-in feature behind a flag that doesn't work reliably. - Good candidate to have the flag disabled or trigger a warning message. - Not a good candidate to try to fix the feature forward. - We add a compiler opt-out feature that doesn't work reliably. - What to do likely depends on the scope of users impacted. If _very_ few users impacted, possibly just document how to opt-out. - If enough are impacted to be a nuisance and a regression in experience in general, likely worth attempting a patch release that narrowly fixes or mitigates the issue. ## Pre-release versions Beyond the major, minor, and patch versions of an actual release, SemVer provides a foundation for pre-release version management. However, it is a very open-ended foundation with a wide range of possibilities and no particular semantic model provided. Some of this is to support the wide variety of different needs across different projects. It also reflects the fundamentally less crisp and well defined criteria needed for pre-releases. That said, Carbon should try to have a small and well articulated scheme of pre-release versions to help communicate what these releases do and don't constitute and how they should be interpreted. These are selected to provide a framework that allows pre-release versions to order in a reasonably cohesive way when compared using SemVer. In descending order, Carbon pre-releases may use: - `MAJOR.MINOR.PATCH-rc.N` - `MAJOR.MINOR.PATCH-0.nightly.YYYY.MM.DD` - `MAJOR.MINOR.PATCH-0.dev` We expand on each of these below to provide criteria and details. ### Release qualification pre-releases: `MAJOR.MINOR.PATCH-rc.N` We create release candidate or "rc" pre-releases when we believe that version to be complete and ready to release and want to collect feedback. There should not be interesting or significant gaps, even known ones, from the intended release. The expectation should always be that unless some feedback arrives to the contrary, the release candidate could simply become the release. For each pre-release category, we always suffix with a sequential count `N` of the pre-releases at that version. We must start with a `.0` in order to have subsequent iterations of the same version and category of pre-release to sort after the first. ### Incremental development versions: `MAJOR.MINOR.PATCH-0.{nightly,dev}.N` These are versions that are not in any way associated with the actual expected release, but are periodically produced as an incremental tracking of development progress. Because they are not expected to be part of qualifying a specific release, they're not defined by any particular criteria of completeness or readiness. In practice, these will occur at every stage between one release and the next. #### Nightly pre-release versions These are automated incremental development versions built each night when the automated testing passes. There is _no_ attempt to provide any higher quality bar or refinement than whatever was in the tree at the time the automation runs, and whatever automated tests are present pass. It is important to emphasize that the primary use case of these pre-release versions is not to evaluate a potential release but for the developers and contributors to Carbon itself to track incremental development progress. That development-oriented goal drives how they are built and what they do and don't provide. Mechanically, we prefix the `nightly` pre-release version component with a `0` component to ensure these versions sort before any and all release qualification pre-release versions. We also add a date-derived suffix to provide a rough chronological ordering of nightly builds with otherwise identical versions. #### Development pre-release versions During development, interactive builds of Carbon need to be versioned in an unambiguous way, and we do that with the `dev` pre-release version. Much like nightly versions, these are only produced as artifacts of development activities and are never part of any actual release process. These versions are further not built expected to be automated or necessarily repeatable. They may contain in-flight edits and all manner of other variations. Mechanically, we prefix the `dev` pre-release version component with `0` in the same way as `nightly` is prefixed to ensure effective ordering. We don't add any additional information to keep the mechanics of development builds simple -- for example, there is no easy way to extract the date of the build. The exact timestamp of the build may be available but doesn't participate for simplicity and minimizing cache impact. ## Relevant proposal - [Proposal p4105](/proposals/p4105.md) ================================================ FILE: docs/spec/README.md ================================================ # Spec Eventually, this will be the home of a formal specification for the Carbon Language. We are committed to having a specification that is sufficiently detailed to allow independent implementations of the language. While we plan to have a reference implementation, we think having a specification as well is an important tool to ensure that the behavior of the language is well understood and holds together. The work-in-progress specification is available here: - [Language specification](lang) - [Library specification](lib) ================================================ FILE: docs/spec/lang/README.md ================================================ # Carbon language specification ## Program structure 1. A _program_ is a collection of one or more linkage units that are [linked](#linkage) together. 2. A _Carbon linkage unit_ is the result of [translating](#translation) a source file. A _foreign linkage unit_ is an artifact produced by a translation process for some other programming language. A linkage unit is either a Carbon linkage unit or a foreign linkage unit. 3. A _source file_ is a sequence of Unicode code points. > Note: Source files are typically stored on disk in files with a `.carbon` > file extension, encoded in UTF-8. ## Conformance 1. A program is _valid_ if it contains no constructs that violate "shall" constraints in this specification. Otherwise, the program is _invalid_. 2. An implementation is _conforming_ if it accepts all valid programs, it rejects all invalid programs for which a diagnostic is required, and the [execution](execution.md) semantics of all accepted programs is as specified in this specification. ## Translation 1. Translation of a source file into a Carbon linkage unit proceeds as follows: - [Lexical analysis](lex.md) decomposes the sequence of code points into a sequence of lexical elements. - Whitespace and text comments are discarded, leaving a sequence of [tokens](lex.md). - The tokens are [parsed](parsing.md) into an abstract syntax tree. - [Unqualified names are bound](names.md) to declarations in the abstract syntax tree. - A translated form of each imported [library](libs.md) is located and loaded. - [Semantic analysis](semantics.md) is performed: types are determined and semantic checks are performed for all non-template-dependent constructs in the abstract syntax tree, constant expressions are evaluated, and templates are instantiated and semantically analyzed. 2. > Note: After semantic analysis, an implementation may optionally > monomorphize generics by a process similar to template instantiation. 3. The resulting linkage unit comprises all entities in the translated source file that are either [external](#linkage) or are reachable from an external entity. > Note: A linkage unit can include non-monomorphized generics, but never > includes templates. Constant evaluation can eliminate references to > entities. ## Linkage 1. Two declarations declare the same entity if both declarations are in the same library and the same [scope](names.md#scopes) and declare the same [name](names.md). TODO: Linkage rules for foreign entities. TODO: Ability to declare file-local entities. 2. All declarations of an entity shall use the same type. 3. Every entity that is reachable from a linkage unit in a program shall be defined by a linkage unit in the program; no diagnostic is required unless an entity that can be referenced during the [execution](execution.md) of the program is not defined. 4. There shall not be more than one definition of an entity in a program. ================================================ FILE: docs/spec/lang/execution.md ================================================ # Execution ## Entry points TODO: Entry points (Carbon and foreign). `fn Run()`. ## Object model ## Sequential execution ## Threads and data races ================================================ FILE: docs/spec/lang/lex.md ================================================ # Lexical analysis TODO ## Lexical elements 1. The sequence of Unicode code points in a source file is partitioned into contiguous subsequences called _lexical elements_. Formation of lexical elements begins with the first code point in the source file and proceeds in code point order. 2. At each step, the longest valid lexical element that can be formed from a prefix of the remaining code points is formed, even if this would result in a failure to form a later lexical element. Repeating this process shall convert the entire source file into lexical elements. 3. Valid lexical elements are: TODO: Add a list of lexical elements once we've decided on them. ================================================ FILE: docs/spec/lang/libs.md ================================================ # Libraries and packages TODO ================================================ FILE: docs/spec/lang/names.md ================================================ # Names TODO ## Names 1. A _name_ is an [identifier](lex.md). Two names are the same if they comprise the same sequence of Unicode code points. TODO: Normalization? ## Scopes 1. A _scope_ is one of: - The top level in a source file. - A pattern scope. - A block scope. - A type definition. 2. Every construct that declares a name _binds_ the name to the declared entity within the innermost enclosing scope. ## Unqualified name lookup 1. Unqualified name lookup associates a name with an entity. The associated entity is the entity to which the name is bound in the innermost enclosing scope in which the name is bound. ================================================ FILE: docs/spec/lang/parsing.md ================================================ # Parsing TODO ================================================ FILE: docs/spec/lang/semantics.md ================================================ # Semantic analysis TODO ================================================ FILE: docs/spec/lib/README.md ================================================ # Carbon standard library specification TODO ================================================ FILE: examples/BUILD ================================================ # Part of the Carbon Language project, under the Apache License v2.0 with LLVM # Exceptions. See /LICENSE for license information. # SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception load("@bazel_binaries//:defs.bzl", "bazel_binaries") load( "@rules_bazel_integration_test//bazel_integration_test:defs.bzl", "bazel_integration_tests", "integration_test_utils", ) load("@rules_python//python:defs.bzl", "py_binary") load("//bazel/carbon_rules:defs.bzl", "carbon_binary") carbon_binary( name = "sieve", srcs = ["sieve.carbon"], ) carbon_binary( name = "hello_world", srcs = ["hello_world.carbon"], ) py_binary( name = "bazel_test_runner", testonly = True, srcs = ["bazel_test_runner.py"], data = ["//toolchain/install:install_data"], deps = [ "@bazel_tools//tools/python/runfiles", "@rules_bazel_integration_test//bazel_integration_test/py:test_base", ], ) bazel_example_files = [ "bazel/BUILD", "bazel/MODULE.bazel", "bazel/example_lib.h", "bazel/example_lib.cpp", "bazel/example.cpp", ] bazel_integration_tests( name = "bazel_min_test", bazel_binaries = bazel_binaries, bazel_versions = bazel_binaries.versions.all, # Override the default tags which assume a much more expensive test. tags = [], test_runner = ":bazel_test_runner", workspace_files = bazel_example_files, workspace_path = "bazel", ) bazel_integration_tests( name = "bazel_full_test", bazel_binaries = bazel_binaries, bazel_versions = bazel_binaries.versions.all, env = {"CARBON_BAZEL_TEST_FULL": ""}, # Override the default tags -- while running these manually is good as the # full tests are very expensive, there isn't a reason to run them # _exclusively_ as well. tags = ["manual"], test_runner = ":bazel_test_runner", workspace_files = bazel_example_files, workspace_path = "bazel", ) # Convenience test suites with a simple names to run all the different # configured Bazel versions of tests. test_suite( name = "bazel_min_tests", tests = integration_test_utils.bazel_integration_test_names( "bazel_min_test", bazel_binaries.versions.all, ), ) test_suite( name = "bazel_full_tests", # Full tests are too expensive to run by default. tags = ["manual"], tests = integration_test_utils.bazel_integration_test_names( "bazel_full_test", bazel_binaries.versions.all, ), ) ================================================ FILE: examples/advent2024/BUILD ================================================ # Part of the Carbon Language project, under the Apache License v2.0 with LLVM # Exceptions. See /LICENSE for license information. # SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception load("//bazel/carbon_rules:defs.bzl", "carbon_binary") utils = [ "io_utils.carbon", "sort.carbon", ] # A list of examples that should be excluded because they don't build any more. # For example, to exclude day3_part2, use: # excluded = ["day3_part2.carbon"] excluded = [] # Produce a binary `dayX_partY` for each matching `.carbon` file. Each binary # depends on `dayX_partY.carbon` and `dayX_common.carbon`. [ carbon_binary( name = carbon_file.removesuffix(".carbon"), srcs = [ carbon_file, carbon_file.rsplit("_", 1)[0] + "_common.carbon", ] + utils, ) for carbon_file in glob(["day*_part*.carbon"]) if carbon_file not in excluded ] ================================================ FILE: examples/advent2024/README.md ================================================ # Advent of Code 2024 This directory contains sample solutions written in Carbon for [Advent of Code 2024](https://adventofcode.com/2024/). The Carbon toolchain is in a very early state, so these samples frequently need to work around missing functionality and are not reflective of expected Carbon style and idioms. Instead, the purpose of these examples are to test the current state of the toolchain against larger code examples than those that are present in the toolchain's own tests, to find bugs in the toolchain, and to drive feature development in the toolchain by presenting somewhat realistic testcases. If one of these examples stops building after a change to the toolchain, please: - Make sure that the build break is an expected consequence of the change. - Update the `BUILD` file to exclude that example. - File an issue and assign it to @zygoloid. ================================================ FILE: examples/advent2024/day10_common.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // https://adventofcode.com/2024/day/10 library "day10_common"; import Core library "io"; import Core library "range"; import library "io_utils"; class Terrain { impl as Core.UnformedInit {} fn Read() -> Terrain { returned var me: Terrain; for (y: i32 in Core.Range(43)) { for (x: i32 in Core.Range(43)) { me.height[x][y] = ReadChar() - '0'; } SkipNewline(); } return var; } var height: array(array(i32, 43), 43); } ================================================ FILE: examples/advent2024/day10_part1.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // https://adventofcode.com/2024/day/10 import Core library "io"; import Core library "range"; import library "day10_common"; import library "io_utils"; // TODO: Add this to the prelude. fn PopCount(n: u256) -> i32 { var bit: u256 = 1; var total: i32 = 0; while (bit != 0) { if (n & bit != 0) { ++total; } bit <<= 1; } return total; } class Reachable { impl as Core.UnformedInit {} fn Make(terrain: Terrain) -> Reachable { returned var me: Reachable; var next: u256 = 1; for (y: i32 in Core.Range(43)) { for (x: i32 in Core.Range(43)) { if (terrain.height[x][y] == 0) { me.trailheads[x][y] = next; next <<= 1; } } } return var; } fn AddLevel[ref self: Self](terrain: Terrain, level: i32) { let adj: array((i32, i32), 4) = ((-1, 0), (0, -1), (1, 0), (0, 1)); for (y: i32 in Core.Range(43)) { for (x: i32 in Core.Range(43)) { if (terrain.height[x][y] == level) { var reach: u256 = 0; var i: i32 = 0; while (i < 4) { let adj_x: i32 = x + adj[i].0; let adj_y: i32 = y + adj[i].1; if (adj_x >= 0 and adj_x < 43 and adj_y >= 0 and adj_y < 43 and terrain.height[adj_x][adj_y] == level - 1) { reach = reach | self.trailheads[adj_x][adj_y]; } ++i; } self.trailheads[x][y] = reach; } } } } fn Count[self: Self](terrain: Terrain, level: i32) -> i32 { var total: i32 = 0; for (y: i32 in Core.Range(43)) { for (x: i32 in Core.Range(43)) { if (terrain.height[x][y] == level) { total += PopCount(self.trailheads[x][y]); } } } return total; } var trailheads: array(array(u256, 43), 43); } fn Run() { var terrain: Terrain = Terrain.Read(); var reachable: Reachable = Reachable.Make(terrain); for (i: i32 in Core.InclusiveRange(1, 9)) { reachable.AddLevel(terrain, i); } Core.Print(reachable.Count(terrain, 9)); } ================================================ FILE: examples/advent2024/day10_part2.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // https://adventofcode.com/2024/day/10 import Core library "io"; import Core library "range"; import library "day10_common"; import library "io_utils"; class PathsToTop { impl as Core.UnformedInit {} fn Make(terrain: Terrain) -> PathsToTop { returned var me: PathsToTop; for (y: i32 in Core.Range(43)) { for (x: i32 in Core.Range(43)) { // TODO: We shouldn't need an explicit cast here. me.paths[x][y] = if terrain.height[x][y] == 9 then 1 as i64 else 0; } } return var; } fn AddLevel[ref self: Self](terrain: Terrain, level: i32) -> i64 { var total: i64 = 0; let adj: array((i32, i32), 4) = ((-1, 0), (0, -1), (1, 0), (0, 1)); for (y: i32 in Core.Range(43)) { for (x: i32 in Core.Range(43)) { if (terrain.height[x][y] == level) { var paths: i64 = 0; // TODO: for ((adj_x: i32, adj_y: i32) in adj) { for (i: i32 in Core.Range(4)) { let adj_x: i32 = x + adj[i].0; let adj_y: i32 = y + adj[i].1; if (adj_x >= 0 and adj_x < 43 and adj_y >= 0 and adj_y < 43 and terrain.height[adj_x][adj_y] == level + 1) { paths += self.paths[adj_x][adj_y]; } } self.paths[x][y] = paths; total += paths; } } } return total; } var paths: array(array(i64, 43), 43); } fn Run() { var terrain: Terrain = Terrain.Read(); var paths: PathsToTop = PathsToTop.Make(terrain); var total: i64; for (i: i32 in Core.InclusiveRange(0, 8)) { total = paths.AddLevel(terrain, 8 - i); } PrintInt(total); } ================================================ FILE: examples/advent2024/day11_common.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // https://adventofcode.com/2024/day/11 library "day11_common"; import Core library "io"; import library "io_utils"; fn Next(n: i64) -> (i64, i64) { if (n == 0) { return (1, -1); } var pow10: i64 = 10; var pow100: i64 = 1; while (n / pow100 >= 100) { pow100 *= 100; pow10 *= 10; } if (n / pow100 >= 10) { return (n / pow10, n % pow10); } return (n * 2024, -1); } fn Count(n: i64, depth: i32) -> i32 { if (n == -1) { return 0; } if (depth == 0) { return 1; } let next: (i64, i64) = Next(n); return Count(next.0, depth - 1) + Count(next.1, depth - 1); } ================================================ FILE: examples/advent2024/day11_part1.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // https://adventofcode.com/2024/day/11 import Core library "io"; import library "day11_common"; import library "io_utils"; fn Run() { var n: i64; var total: i32 = 0; while (ReadInt(ref n)) { total += Count(n, 25); SkipSpaces(); } Core.Print(total); } ================================================ FILE: examples/advent2024/day11_part2.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // https://adventofcode.com/2024/day/11 import Core library "io"; import Core library "range"; import library "day11_common"; import library "io_utils"; class Digits { impl as Core.UnformedInit {} fn Make() -> Digits { returned var me: Digits; for (digit: i32 in Core.Range(10)) { for (depth: i32 in Core.Range(75)) { me.count[digit][depth] = 0; } } return var; } fn Print[self: Self](max_depth: i32) { for (digit: i32 in Core.Range(10)) { Core.PrintChar(((digit + 0x30) as u8) as char); Core.PrintChar(':'); for (depth: i32 in Core.Range(max_depth)) { Core.PrintChar(' '); PrintIntNoNewline(self.count[digit][depth]); } Core.PrintChar('\n'); } Core.PrintChar('\n'); } var count: array(array(i64, 75), 10); } fn ReduceToDigits(n: i64, depth: i32, multiplicity: i64, digits: Digits*) -> i64 { if (n == -1) { return 0; } if (depth == 0) { return multiplicity; } if (n < 10) { let count: i64* = &digits->count[n as i32][depth - 1]; *count += multiplicity; return 0; } let next: (i64, i64) = Next(n); return ReduceToDigits(next.0, depth - 1, multiplicity, digits) + ReduceToDigits(next.1, depth - 1, multiplicity, digits); } fn Run() { let max_depth: i32 = 75; var total: i64 = 0; var digits: Digits = Digits.Make(); var n: i64; while (ReadInt(ref n)) { total += ReduceToDigits(n, max_depth, 1, &digits); // PrintInt(total); // digits.Print(max_depth - 1); SkipSpaces(); } var depth: i32 = max_depth - 1; while (depth >= 0) { // PrintInt(total); // digits.Print(depth); for (digit: i32 in Core.Range(10)) { let m: i64 = digits.count[digit][depth]; if (m > 0) { let next: (i64, i64) = Next(digit as i64); total += ReduceToDigits(next.0, depth, m, &digits) + ReduceToDigits(next.1, depth, m, &digits); } } --depth; } PrintInt(total); } ================================================ FILE: examples/advent2024/day12_common.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // https://adventofcode.com/2024/day/12 library "day12_common"; import Core library "io"; import Core library "range"; import library "io_utils"; class Map { impl as Core.UnformedInit {} fn Read() -> Map { returned var me: Self; for (y: i32 in Core.Range(140)) { for (x: i32 in Core.Range(140)) { me.kind[x][y] = ReadChar() as i32; } SkipNewline(); } return var; } fn At[self: Self](x: i32, y: i32) -> i32 { return if x < 0 or x >= 140 or y < 0 or y >= 140 then -1 else self.kind[x][y]; } var kind: array(array(i32, 140), 140); } class DisjointSetForest { impl as Core.UnformedInit {} fn Make() -> DisjointSetForest { returned var me: Self; for (i: i32 in Core.Range(140 * 140)) { me.nodes[i].next = i; me.nodes[i].weight = 1; me.nodes[i].unions = 0; } return var; } fn Lookup[ref self: Self](a: i32) -> i32 { let next: i32 = self.nodes[a].next; if (next == a) { return next; } let resolved: i32 = self.Lookup(next); self.nodes[a].next = resolved; return resolved; } fn Unions[self: Self](canon_a: i32) -> i32 { return self.nodes[canon_a].unions; } fn Weight[self: Self](canon_a: i32) -> i32 { return self.nodes[canon_a].weight; } fn Union[ref self: Self](a: i32, b: i32) { let canon_b: i32 = self.Lookup(b); self.Set(a, canon_b); ++self.nodes[canon_b].unions; } private fn Set[ref self: Self](a: i32, canon_b: i32) { let next: i32 = self.nodes[a].next; self.nodes[a].next = canon_b; if (next == a) { if (a != canon_b) { self.nodes[canon_b].weight += self.nodes[a].weight; self.nodes[canon_b].unions += self.nodes[a].unions; } } else { self.Set(next, canon_b); } } // TODO: Consider adding ranked choice. // TODO: Make this generic in the payload data. var nodes: array({.next: i32, .weight: i32, .unions: i32}, 140 * 140); } fn MakeRegions(map: Map) -> DisjointSetForest { returned var forest: DisjointSetForest = DisjointSetForest.Make(); for (x: i32 in Core.Range(140)) { for (y: i32 in Core.Range(140)) { for (a: i32 in Core.Range(2)) { // TODO: Crashes toolchain: // let adj: (i32, i32) = if a == 0 then (x - 1, y) else (x, y - 1); // if (map.At(adj.0, adj.1) == map.At(x, y)) { // forest.Union(y * 140 + x, adj.1 * 140 + adj.0); // } let adj_x: i32 = if a == 0 then x - 1 else x; let adj_y: i32 = if a == 0 then y else y - 1; if (map.At(adj_x, adj_y) == map.At(x, y)) { forest.Union(y * 140 + x, adj_y * 140 + adj_x); } } } } return var; } ================================================ FILE: examples/advent2024/day12_part1.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // https://adventofcode.com/2024/day/12 import Core library "io"; import Core library "range"; import library "day12_common"; import library "io_utils"; fn Run() { var map: Map = Map.Read(); var regions: DisjointSetForest = MakeRegions(map); var total: i32 = 0; for (i: i32 in Core.Range(140 * 140)) { if (regions.Lookup(i) == i) { let area: i32 = regions.Weight(i); let internal_edges: i32 = regions.Unions(i); let perimeter: i32 = area * 4 - internal_edges * 2; total += area * perimeter; } } Core.Print(total); } ================================================ FILE: examples/advent2024/day12_part2.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // https://adventofcode.com/2024/day/12 import Core library "io"; import Core library "range"; import library "day12_common"; import library "io_utils"; fn CountExtensions(map: Map, regions: DisjointSetForest*) -> array(i32, 140 * 140) { returned var extensions: array(i32, 140 * 140); for (i: i32 in Core.Range(140 * 140)) { extensions[i] = 0; } var ext: array({.same: (i32, i32), .adj: (i32, i32)}, 4) = ( {.same = (-1, 0), .adj = (0, -1)}, {.same = (-1, 0), .adj = (0, 1)}, {.same = (0, -1), .adj = (-1, 0)}, {.same = (0, -1), .adj = (1, 0)}, ); for (x: i32 in Core.Range(140)) { for (y: i32 in Core.Range(140)) { let kind: i32 = map.At(x, y); for (e: i32 in Core.Range(4)) { if (map.At(x + ext[e].same.0, y + ext[e].same.1) == kind and map.At(x + ext[e].adj.0, y + ext[e].adj.1) != kind and map.At(x + ext[e].same.0 + ext[e].adj.0, y + ext[e].same.1 + ext[e].adj.1) != kind) { ++extensions[regions->Lookup(y * 140 + x)]; } } } } return var; } fn Run() { var map: Map = Map.Read(); var regions: DisjointSetForest = MakeRegions(map); var ext: array(i32, 140 * 140) = CountExtensions(map, ®ions); var total: i32 = 0; for (i: i32 in Core.Range(140 * 140)) { if (regions.Lookup(i) == i) { let area: i32 = regions.Weight(i); let internal_edges: i32 = regions.Unions(i); let extensions: i32 = ext[i]; let perimeter: i32 = area * 4 - internal_edges * 2 - extensions; total += area * perimeter; } } Core.Print(total); } ================================================ FILE: examples/advent2024/day13_common.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // https://adventofcode.com/2024/day/13 library "day13_common"; import Core library "io"; import library "io_utils"; // Returns m and n so that am + bn = gcd. fn Euclid(a: i64, b: i64) -> {.m: i64, .n: i64, .gcd: i64} { if (a < b) { let reverse: {.m: i64, .n: i64, .gcd: i64} = Euclid(b, a); return {.m = reverse.n, .n = reverse.m, .gcd = reverse.gcd}; } if (b == 0) { return {.m = 1, .n = 0, .gcd = a}; } let next: {.m: i64, .n: i64, .gcd: i64} = Euclid(b, a % b); return {.m = next.n, .n = next.m - next.n * (a / b), .gcd = next.gcd}; } class Machine { impl as Core.UnformedInit {} fn Read() -> Machine { returned var me: Machine; // "Button A: X+" SkipNChars(12); ReadInt(ref me.a.0); // ", Y+" SkipNChars(4); ReadInt(ref me.a.1); // "\nButton B: X+" SkipNChars(13); ReadInt(ref me.b.0); // ", Y+" SkipNChars(4); ReadInt(ref me.b.1); // "\nPrize: X=" SkipNChars(10); ReadInt(ref me.prize.0); // ", Y=" SkipNChars(4); ReadInt(ref me.prize.1); SkipNewline(); return var; } var a: (i64, i64); var b: (i64, i64); var prize: (i64, i64); } // Set of solutions to 'm a + n b = c'. class BezoutSolutionSet { fn Make(a: i64, b: i64, c: i64) -> BezoutSolutionSet { var e: {.m: i64, .n: i64, .gcd: i64} = Euclid(a, b); if (c % e.gcd != 0) { // Impossible. return {.m0 = -1, .n0 = -1, .m_step = -1, .n_step = -1}; } // Find an initial solution. Note that m and n might be negative. let num_gcds: i64 = c / e.gcd; e.m *= num_gcds; e.n *= num_gcds; // Pick the smallest positive m we can. let a_over_gcd: i64 = a / e.gcd; let b_over_gcd: i64 = b / e.gcd; var adj: i64 = 0; // This is e.m / b rounded towards -inf. // TODO: Should there be a way of expressing this directly? if (e.m < 0) { adj = (e.m - b_over_gcd + 1) / b_over_gcd; } else { adj = e.m / b_over_gcd; } e.m -= adj * b_over_gcd; e.n += adj * a_over_gcd; return {.m0 = e.m, .n0 = e.n, .m_step = b_over_gcd, .n_step = -a_over_gcd}; } fn Valid[self: Self]() -> bool { return self.m_step >= 0; } fn Solution[self: Self](k: i64) -> (i64, i64) { return (self.m0 + k * self.m_step, self.n0 + k * self.n_step); } // m_k * a + n_k * b == c, where: // m_k = m0 + k * m_step // n_k = n0 * k * n_step // m0 is the minimum non-negative m value, and n_step is negative. var m0: i64; var n0: i64; var m_step: i64; var n_step: i64; } // Given two sets of points s and t, find the intersection in the first quadrant // with the minimum x coordinate. Returns the intersection point, or (-1, -1) if // there is no intersection. fn FirstIntersection(s: BezoutSolutionSet, t: BezoutSolutionSet) -> (i64, i64) { if (not s.Valid() or not t.Valid()) { // One of the sets is empty. return (-1, -1); } // Easy case: lines meet at a point. This happens unless the lines are // parallel. let d: i64 = s.m_step * t.n_step - t.m_step * s.n_step; if (d != 0) { let u: i64 = (t.m0 - s.m0) * t.n_step - (t.n0 - s.n0) * t.m_step; if (u % d != 0) { // Lines don't meet at an integer point. return (-1, -1); } let j: i64 = u / d; if (j < 0 or s.n0 + j * s.n_step < 0) { // Lines don't meet in first quadrant. return (-1, -1); } return s.Solution(j); } // Hard case: lines are parallel. We also get here if either "line" is a // point, but that doesn't happen in this exercise. We know all integer points // on the line are solutions, so the first intersection is the greater of the // first point in s and the first point in t, if that point is on both lines. if (s.m0 < t.m0) { // (t.m0, t.n0) is the solution if it's in s. if ((t.m0 - s.m0) % s.m_step == 0 and (t.n0 - s.n0) % s.n_step == 0) { return (t.m0, t.n0); } } else { // (s.m0, s.n0) is the solution if it's in t. if ((s.m0 - t.m0) % t.m_step == 0 and (s.n0 - t.n0) % t.n_step == 0) { return (s.m0, s.n0); } } return (-1, -1); } fn CostIfPossible(m: Machine) -> i64 { let x: BezoutSolutionSet = BezoutSolutionSet.Make(m.a.0, m.b.0, m.prize.0); let y: BezoutSolutionSet = BezoutSolutionSet.Make(m.a.1, m.b.1, m.prize.1); let ab: (i64, i64) = FirstIntersection(x, y); if (ab.0 == -1) { return 0; } return ab.0 * 3 + ab.1; } ================================================ FILE: examples/advent2024/day13_part1.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // https://adventofcode.com/2024/day/13 import Core library "io"; import library "day13_common"; import library "io_utils"; fn Run() { var total_cost: i64 = 0; while (true) { var m: Machine = Machine.Read(); total_cost += CostIfPossible(m); if (not SkipNewline()) { break; } } PrintInt(total_cost); } ================================================ FILE: examples/advent2024/day13_part2.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // https://adventofcode.com/2024/day/13 import Core library "io"; import library "day13_common"; import library "io_utils"; fn Run() { var total_cost: i64 = 0; while (true) { var m: Machine = Machine.Read(); m.prize.0 += 10_000_000_000_000; m.prize.1 += 10_000_000_000_000; total_cost += CostIfPossible(m); if (not SkipNewline()) { break; } } PrintInt(total_cost); } ================================================ FILE: examples/advent2024/day14_common.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // https://adventofcode.com/2024/day/14 library "day14_common"; import Core library "io"; import library "io_utils"; fn ReadXY(xy: (i32, i32)*) -> bool { return ReadInt(ref xy->0) and ConsumeChar(',') and ReadInt(ref xy->1); } fn SkipString(s: str) -> bool { return SkipNChars(s.Size() as i32); } fn Mod(a: i32, d: i32) -> i32 { let rem: i32 = a % d; return if rem < 0 then rem + d else rem; } class Robot { impl as Core.UnformedInit {} fn Read() -> Robot { returned var me: Robot; SkipString("p="); ReadXY(&me.p); SkipString(" v="); ReadXY(&me.v); SkipNewline(); return var; } fn Pos[self: Self](t: i32) -> (i32, i32) { return (Mod((self.p.0 + self.v.0 * Mod(t, 101)), 101), Mod((self.p.1 + self.v.1 * Mod(t, 103)), 103)); } impl as Core.Copy { fn Op[self: Self]() -> Self { return {.p = self.p, .v = self.v}; } } var p: (i32, i32); var v: (i32, i32); } ================================================ FILE: examples/advent2024/day14_part1.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // https://adventofcode.com/2024/day/14 import Core library "io"; import library "day14_common"; import library "io_utils"; fn Run() { var q: array(array(i32, 3), 3) = ((0,0,0), (0,0,0), (0,0,0)); while (PeekChar() != CharOrEOF.EOF()) { var r: Robot = Robot.Read(); let xy: (i32, i32) = r.Pos(100); ++q[if xy.0 < 50 then 0 else if xy.0 > 50 then 2 else 1] [if xy.1 < 51 then 0 else if xy.1 > 51 then 2 else 1]; } Core.Print(q[0][0] * q[0][2] * q[2][0] * q[2][2]); } ================================================ FILE: examples/advent2024/day14_part2.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // https://adventofcode.com/2024/day/14 import Core library "io"; import Core library "range"; import library "day14_common"; import library "io_utils"; // Returns m and n so that am + bn = gcd. fn Euclid(a: i32, b: i32) -> {.m: i32, .n: i32, .gcd: i32} { if (a < b) { let reverse: {.m: i32, .n: i32, .gcd: i32} = Euclid(b, a); return {.m = reverse.n, .n = reverse.m, .gcd = reverse.gcd}; } if (b == 0) { return {.m = 1, .n = 0, .gcd = a}; } let next: {.m: i32, .n: i32, .gcd: i32} = Euclid(b, a % b); return {.m = next.n, .n = next.m - next.n * (a / b), .gcd = next.gcd}; } // Uses Chinese remainder theorem to find the least positive x that solves // x = a mod p // x = b mod q // modulo pq / gcd(p,q), if one exists (if a = b mod gcd(p,q)). fn CRT(a: i32, b: i32, p: i32, q: i32) -> i32 { let e: {.m: i32, .n: i32, .gcd: i32} = Euclid(p, q); let x: i32 = a * (q / e.gcd) * e.n + b * (p / e.gcd) * e.m; return Mod(x, p * (q / e.gcd)); } fn PrintState(r: array(Robot, 500), t: i32) { var display: array(array(char, 101), 103); for (y: i32 in Core.Range(103)) { for (x: i32 in Core.Range(101)) { display[y][x] = '.'; } } for (ri: Robot in r) { let xy: (i32, i32) = ri.Pos(t); display[xy.1][xy.0] = '#'; } for (y: i32 in Core.Range(103)) { for (x: i32 in Core.Range(101)) { Core.PrintChar(display[y][x]); } Core.PrintChar('\n'); } } fn Run() { var r: array(Robot, 500); for (i: i32 in Core.Range(500)) { r[i] = Robot.Read(); } let first_match_x: i32 = 28; let first_match_y: i32 = 84; var t: i32 = CRT(first_match_x, first_match_y, 101, 103); Core.Print(t); PrintState(r, t); } ================================================ FILE: examples/advent2024/day15_common.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // https://adventofcode.com/2024/day/15 library "day15_common"; import Core library "io"; import library "io_utils"; // TODO: Use a choice type. class Square { adapt char; impl as Core.Copy { fn Op[self: Self]() -> Self { return (self as char).(Core.Copy.Op)() as Self; } } impl as Core.EqWith(Square) { fn Equal[self: Self](other: Self) -> bool { // TODO: Use a `char` comparison when it's supported. return (self as u8) == (other as u8); } fn NotEqual[self: Self](other: Self) -> bool { // TODO: Use a `char` comparison when it's supported. return (self as u8) != (other as u8); } } fn Make(c: CharOrEOF) -> Square { // TODO: Use `match`. if (c == '#') { return ('#' as char) as Square; } if (c == 'O') { return ('O' as char) as Square; } if (c == '.') { return ('.' as char) as Square; } if (c == '@') { return ('@' as char) as Square; } return ('!' as char) as Square; } } let Wall: Square = ('#' as char) as Square; let Box: Square = ('O' as char) as Square; let Empty: Square = ('.' as char) as Square; let Robot: Square = ('@' as char) as Square; class Grid { impl as Core.UnformedInit {} fn Read() -> Grid { returned var me: Grid; var y: i32 = 0; while (y < 50) { var x: i32 = 0; while (x < 50) { me.data[x][y] = Square.Make(ReadChar()); if (me.data[x][y] == Robot) { me.robot = (x, y); } ++x; } SkipNewline(); ++y; } return var; } fn Move[ref self: Self](d: (i32, i32)) { var distance: i32 = 1; while (self.data[self.robot.0 + distance * d.0][self.robot.1 + distance * d.1] == Box) { ++distance; } if (self.data[self.robot.0 + distance * d.0][self.robot.1 + distance * d.1] == Empty) { self.data[self.robot.0][self.robot.1] = Empty; self.data[self.robot.0 + distance * d.0][self.robot.1 + distance * d.1] = Box; self.data[self.robot.0 + d.0][self.robot.1 + d.1] = Robot; self.robot.0 += d.0; self.robot.1 += d.1; } } fn Print[self: Self]() { var y: i32 = 0; while (y < 50) { var x: i32 = 0; while (x < 50) { Core.PrintChar(self.data[x][y] as char); ++x; } Core.PrintChar('\n'); ++y; } Core.PrintChar('\n'); } fn Signature[self: Self]() -> i32 { var s: i32 = 0; var y: i32 = 0; while (y < 50) { var x: i32 = 0; while (x < 50) { if (self.data[x][y] == Box) { s += y * 100 + x; } ++x; } ++y; } return s; } var data: array(array(Square, 50), 50); var robot: (i32, i32); } fn ReadAndExecuteActions(ref g: Grid) { while (true) { var m: CharOrEOF = ReadChar(); // To watch the progress, uncomment the following: // g.Print(); // Core.PrintChar(((m as i32) as u8) as char); // Core.PrintChar('\n'); // TODO: Use `match` once it's available. if (m == '^') { g.Move((0, -1)); } else if (m == '<') { g.Move((-1, 0)); } else if (m == '>') { g.Move((1, 0)); } else if (m == 'v') { g.Move((0, 1)); } else if (m == CharOrEOF.EOF()) { return; } } } ================================================ FILE: examples/advent2024/day15_part1.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // https://adventofcode.com/2024/day/15 import Core library "io"; import library "day15_common"; fn Run() { var g: Grid = Grid.Read(); ReadAndExecuteActions(ref g); Core.Print(g.Signature()); } ================================================ FILE: examples/advent2024/day1_common.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // https://adventofcode.com/2024/day/1 library "day1_common"; import Core library "range"; import library "io_utils"; // Read a sequence of lines each containing a pair of numbers into two arrays. // Returns the number of lines read. fn ReadInputs[N:! Core.IntLiteral()](ref a: array(i32, N), ref b: array(i32, N)) -> i32 { for (i: i32 in Core.Range(N)) { var av: i32; var bv: i32; if (ReadInt(ref av) and SkipSpaces() and ReadInt(ref bv) and SkipNewline()) { a[i] = av; b[i] = bv; } else { return i; } } return N; } ================================================ FILE: examples/advent2024/day1_part1.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // https://adventofcode.com/2024/day/1 import Core library "io"; import Core library "range"; import library "day1_common"; import library "sort"; fn Abs(n: i32) -> i32 { return if n < 0 then -n else n; } fn Run() { var a: array(i32, 1000); var b: array(i32, 1000); let n: i32 = ReadInputs(ref a, ref b); Quicksort(ref a, 0, n); Quicksort(ref b, 0, n); var difference: i32 = 0; for (i: i32 in Core.Range(n)) { difference += Abs(a[i] - b[i]); } Core.Print(difference); } ================================================ FILE: examples/advent2024/day1_part2.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // https://adventofcode.com/2024/day/1 import Core library "io"; import library "day1_common"; import library "sort"; fn Run() { var a: array(i32, 1000); var b: array(i32, 1000); let n: i32 = ReadInputs(ref a, ref b); Quicksort(ref a, 0, n); Quicksort(ref b, 0, n); var i: i32 = 0; var j: i32 = 0; var similarity: i32 = 0; while (i < n and j < n) { if (a[i] < b[j]) { ++i; } else { if (a[i] == b[j]) { similarity += b[j]; } ++j; } } Core.Print(similarity); } ================================================ FILE: examples/advent2024/day2_common.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // https://adventofcode.com/2024/day/2 library "day2_common"; fn Abs(n: i32) -> i32 { return if n < 0 then -n else n; } fn IsSafeDelta(from: i32, to: i32) -> bool { return from != to and Abs(from - to) <= 3; } ================================================ FILE: examples/advent2024/day2_part1.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // https://adventofcode.com/2024/day/2 import Core library "io"; import library "day2_common"; import library "io_utils"; class ReportState { fn Make() -> ReportState { return {.levels_so_far = 0, .previous = 0, .increasing = false, .safe_so_far = true}; } fn Add[ref self: Self](level: i32) { ++self.levels_so_far; if (self.levels_so_far >= 2) { if (not IsSafeDelta(self.previous, level)) { // Difference is zero or too large. self.safe_so_far = false; } var is_increase: bool = level > self.previous; if (self.levels_so_far == 2) { self.increasing = is_increase; } else if (is_increase != self.increasing) { // Not monotone. self.safe_so_far = false; } } self.previous = level; } var levels_so_far: i32; var previous: i32; var increasing: bool; var safe_so_far: bool; } fn ReadReport() -> ReportState { returned var report: ReportState = ReportState.Make(); var n: i32; while (ReadInt(ref n)) { report.Add(n); SkipSpaces(); } return var; } fn Run() { var safe_reports: i32 = 0; while (true) { var report: ReportState = ReadReport(); if (report.levels_so_far == 0) { break; } if (report.safe_so_far) { ++safe_reports; } SkipNewline(); } Core.Print(safe_reports); } ================================================ FILE: examples/advent2024/day2_part2.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // https://adventofcode.com/2024/day/2 import Core library "io"; import library "day2_common"; import library "io_utils"; // A three-element sliding window of recent levels. class Window { fn Make() -> Window { return {.data = (0,0,0), .size = 0}; } fn Add[ref self: Self](n: i32) { self.data[2] = self.data[1]; self.data[1] = self.data[0]; self.data[0] = n; ++self.size; } var data: array(i32, 3); var size: i32; } // Determines whether a transition from `from` to `to` is safe. fn IsSafe(from: i32, to: i32, want_increase: bool) -> bool { var is_increase: bool = to > from; return IsSafeDelta(from, to) and is_increase == want_increase; } class ReportState { fn Make(increasing: bool) -> ReportState { return {.increasing = increasing, .bad_edges = 0, .removable_single_bad_edge = false, .removable_double_bad_edge = false}; } fn OnAdd[ref self: Self](window: Window) { if (window.size < 2) { return; } // We label the three most recent levels as `a b c`, with `c` the most // recent. We might not have an `a`. let b: i32 = window.data[1]; let c: i32 = window.data[0]; // Keep a count of the unsafe edges. let b_to_c: bool = IsSafe(window.data[1], window.data[0], self.increasing); if (not b_to_c) { ++self.bad_edges; // We have a removable single bad edge if the first edge is unsafe. if (window.size == 2) { self.removable_single_bad_edge = true; } } if (window.size >= 3) { let a: i32 = window.data[2]; if (IsSafe(a, c, self.increasing)) { let lhs: bool = IsSafe(a, b, self.increasing); let rhs: bool = b_to_c; if (not lhs and not rhs) { // If a->b and b->c are both unsafe, but a->c is safe, // then we have a removable double bad edge. self.removable_double_bad_edge = true; } else if (not lhs or not rhs) { // If a->b or b->c is unsafe but a->c is safe, then we have a // removable single bad edge. self.removable_single_bad_edge = true; } } } } fn OnFinish[ref self: Self](window: Window) { if (window.size >= 2 and not IsSafe(window.data[1], window.data[0], self.increasing)) { // We have a removable single bad edge if the last edge is unsafe. self.removable_single_bad_edge = true; } } fn IsSafeWithRemoval[self: Self]() -> bool { return self.bad_edges == 0 or (self.bad_edges == 1 and self.removable_single_bad_edge) or (self.bad_edges == 2 and self.removable_double_bad_edge); } var increasing: bool; var bad_edges: i32; var removable_single_bad_edge: bool; var removable_double_bad_edge: bool; } fn ReadAndCheckReport() -> bool { var window: Window = Window.Make(); var increasing: ReportState = ReportState.Make(true); var decreasing: ReportState = ReportState.Make(false); var n: i32; while (ReadInt(ref n)) { window.Add(n); increasing.OnAdd(window); decreasing.OnAdd(window); SkipSpaces(); } increasing.OnFinish(window); decreasing.OnFinish(window); return window.size > 0 and (increasing.IsSafeWithRemoval() or decreasing.IsSafeWithRemoval()); } fn Run() { var safe_reports: i32 = 0; while (true) { if (ReadAndCheckReport()) { ++safe_reports; } if (not SkipNewline()) { break; } } Core.Print(safe_reports); } ================================================ FILE: examples/advent2024/day3_common.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // https://adventofcode.com/2024/day/3 library "day3_common"; import library "io_utils"; // Reads "mul(a,b)", and returns (true, a, b). // On error, stops before the first invalid character and returns (false, 0, 0). // TODO: -> Optional((i32, i32)) fn ReadMul() -> (bool, i32, i32) { var a: i32; var b: i32; if (ConsumeChar('m') and ConsumeChar('u') and ConsumeChar('l') and ConsumeChar('(') and ReadInt(ref a) and ConsumeChar(',') and ReadInt(ref b) and ConsumeChar(')')) { return (true, a, b); } return (false, 0, 0); } // Reads "do()" or "don't()", and returns (true, was_do). // On error, stops before the first invalid character and returns (false, false). fn ReadDoOrDont() -> (bool, bool) { // "do" if (not ConsumeChar('d') or not ConsumeChar('o')) { return (false, false); } var do: bool = true; // "n't" if (ConsumeChar('n')) { if (not ConsumeChar('\'') or not ConsumeChar('t')) { return (false, false); } do = false; } // "()" if (not ConsumeChar('(') or not ConsumeChar(')')) { return (false, false); } return (true, do); } ================================================ FILE: examples/advent2024/day3_part1.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // https://adventofcode.com/2024/day/3 import Core library "io"; import library "day3_common"; import library "io_utils"; fn Run() { var total: i32 = 0; while (PeekChar() != CharOrEOF.EOF()) { if (PeekChar() == 'm') { // TODO: Use `if let` when available. let result: (bool, i32, i32) = ReadMul(); if (result.0) { total += result.1 * result.2; } } else { ReadChar(); } } Core.Print(total); } ================================================ FILE: examples/advent2024/day3_part2.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // https://adventofcode.com/2024/day/3 import Core library "io"; import library "day3_common"; import library "io_utils"; fn Run() { var total: i32 = 0; var enabled: bool = true; while (PeekChar() != CharOrEOF.EOF()) { if (PeekChar() == 'm') { // TODO: Use `if let` when available. let result: (bool, i32, i32) = ReadMul(); if (result.0 and enabled) { total += result.1 * result.2; } } else if (PeekChar() == 'd') { // TODO: Use `if let` when available. let result: (bool, bool) = ReadDoOrDont(); if (result.0) { enabled = result.1; } } else { ReadChar(); } } Core.Print(total); } ================================================ FILE: examples/advent2024/day4_common.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // https://adventofcode.com/2024/day/4 library "day4_common"; import library "io_utils"; class Wordsearch { impl as Core.UnformedInit {} fn Read() -> Wordsearch { returned var s: Wordsearch; // TODO: Use for loops once they're implemented. var y: i32 = 0; while (y < 140) { var x: i32 = 0; while (x < 140) { // TODO: Assert on failure. s.grid[x][y] = ReadChar() as i32; ++x; } // TODO: Assert on failure. SkipNewline(); ++y; } return var; } fn At[self: Self](x: i32, y: i32) -> i32 { return if x < 0 or x >= 140 or y < 0 or y >= 140 then -1 else self.grid[x][y]; } // TODO: Make this generic in the length of the search query. fn Check4[self: Self](xmas: array(i32, 4), x: i32, y: i32, dx: i32, dy: i32) -> bool { var i: i32 = 0; while (i < 4) { if (self.At(x + i * dx, y + i * dy) != xmas[i]) { return false; } ++i; } return true; } fn Check3[self: Self](mas: array(i32, 3), x: i32, y: i32, dx: i32, dy: i32) -> bool { var i: i32 = 0; while (i < 3) { if (self.At(x + i * dx, y + i * dy) != mas[i]) { return false; } ++i; } return true; } var grid: array(array(i32, 140), 140); } ================================================ FILE: examples/advent2024/day4_part1.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // https://adventofcode.com/2024/day/4 import Core library "io"; import Core library "range"; import library "day4_common"; import library "io_utils"; fn Run() { var search: Wordsearch = Wordsearch.Read(); var xmas: array(i32, 4) = (0x58, 0x4D, 0x41, 0x53); var found: i32 = 0; for (y: i32 in Core.Range(140)) { for (x: i32 in Core.Range(140)) { for (dy: i32 in Core.InclusiveRange(-1, 1)) { for (dx: i32 in Core.InclusiveRange(-1, 1)) { if (search.Check4(xmas, x, y, dx, dy)) { ++found; } } } } } Core.Print(found); } ================================================ FILE: examples/advent2024/day4_part2.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // https://adventofcode.com/2024/day/4 import Core library "io"; import Core library "range"; import library "day4_common"; import library "io_utils"; fn Run() { var search: Wordsearch = Wordsearch.Read(); var mas: array(i32, 3) = (0x4D, 0x41, 0x53); var found: i32 = 0; for (y: i32 in Core.InclusiveRange(1, 138)) { for (x: i32 in Core.InclusiveRange(1, 138)) { if ((search.Check3(mas, x - 1, y - 1, 1, 1) or search.Check3(mas, x + 1, y + 1, -1, -1)) and (search.Check3(mas, x - 1, y + 1, 1, -1) or search.Check3(mas, x + 1, y - 1, -1, 1))) { ++found; } } } Core.Print(found); } ================================================ FILE: examples/advent2024/day5_common.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // https://adventofcode.com/2024/day/5 library "day5_common"; import Core library "range"; import library "io_utils"; fn PageMask(page: i32) -> Core.UInt(100) { // TODO: return (1 as Core.UInt(100)) << page; return (1 as Core.UInt(100)) << (page as Core.UInt(100)); } class Rules { impl as Core.UnformedInit {} fn Read() -> Rules { returned var rules: Rules; for (i: i32 in Core.Range(100)) { rules.disallowed_before[i] = 0; } var a: i32; var b: i32; while (ReadInt(ref a) and ConsumeChar('|') and ReadInt(ref b)) { rules.disallowed_before[a] |= PageMask(b); SkipNewline(); } return var; } fn IsValidOrder[self: Self](a: i32, b: i32) -> bool { return self.disallowed_before[b] & PageMask(a) == 0; } var disallowed_before: array(Core.UInt(100), 100); }; class PageList { impl as Core.UnformedInit {} fn Empty() -> PageList { returned var me: PageList; me.num_pages = 0; return var; } fn Read() -> PageList { returned var me: PageList = Empty(); var page: i32; if (not ReadInt(ref page)) { return var; } me.Add(page); while (ConsumeChar(',')) { ReadInt(ref page); me.Add(page); } SkipNewline(); return var; } fn Add[ref self: Self](page: i32) { self.pages[self.num_pages] = page; ++self.num_pages; } fn FollowsRules[self: Self](rules: Rules) -> bool { var seen: Core.UInt(100) = 0; for (i: i32 in Core.Range(self.num_pages)) { let page: i32 = self.pages[i]; if (seen & rules.disallowed_before[page] != 0) { return false; } seen |= PageMask(page); } return true; } fn IsPossibleFirstPage[self: Self](rules: Rules, page: i32) -> bool { for (i: i32 in Core.Range(self.num_pages)) { if (not rules.IsValidOrder(page, self.pages[i])) { return false; } } return true; } fn ExtractPossibleFirstPage[ref self: Self](rules: Rules) -> i32 { for (i: i32 in Core.Range(self.num_pages)) { var page: i32 = self.pages[i]; if (self.IsPossibleFirstPage(rules, page)) { self.pages[i] = self.pages[self.num_pages - 1]; --self.num_pages; return page; } } // TODO: Assert. return 0; } fn MiddlePage[self: Self]() -> i32 { return self.pages[self.num_pages / 2]; } var pages: array(i32, 24); var num_pages: i32; }; ================================================ FILE: examples/advent2024/day5_part1.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // https://adventofcode.com/2024/day/5 import Core library "io"; import library "day5_common"; import library "io_utils"; fn Run() { var rules: Rules = Rules.Read(); SkipNewline(); var total: i32 = 0; while (true) { var page_list: PageList = PageList.Read(); if (page_list.num_pages == 0) { break; } if (page_list.FollowsRules(rules)) { total += page_list.MiddlePage(); } } Core.Print(total); } ================================================ FILE: examples/advent2024/day5_part2.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // https://adventofcode.com/2024/day/5 import Core library "io"; import library "day5_common"; import library "io_utils"; fn Run() { var rules: Rules = Rules.Read(); SkipNewline(); var total: i32 = 0; while (true) { var page_list: PageList = PageList.Read(); if (page_list.num_pages == 0) { break; } if (page_list.FollowsRules(rules)) { continue; } var new_page_list: PageList = PageList.Empty(); while (page_list.num_pages != 0) { new_page_list.Add(page_list.ExtractPossibleFirstPage(rules)); } total += new_page_list.MiddlePage(); } Core.Print(total); } ================================================ FILE: examples/advent2024/day6_common.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // https://adventofcode.com/2024/day/6 library "day6_common"; import Core library "io"; import library "io_utils"; // TODO: Use a choice type. fn Empty() -> i8 { return 0; } fn Visited() -> i8 { return 1; } fn Wall() -> i8 { return 2; } class Maze { impl as Core.UnformedInit {} fn Read() -> Maze { returned var me: Maze; var y: i32 = 0; while (y < 130) { var x: i32 = 0; while (x < 130) { var cell: i32 = Core.ReadChar(); if (cell == 0x23) { me.data[x][y] = Wall(); } else if (cell == 0x5E) { // TODO: Handle other starting directions? me.data[x][y] = Visited(); me.loc = (x, y); me.dir = (0, -1); } else { me.data[x][y] = Empty(); } ++x; } SkipNewline(); ++y; } return var; } fn Copy[self: Self]() -> Maze { returned var copy: Maze; var y: i32 = 0; while (y < 130) { var x: i32 = 0; while (x < 130) { copy.data[x][y] = self.data[x][y]; ++x; } ++y; } copy.loc = self.loc; copy.dir = self.dir; return var; } fn Step[ref self: Self]() -> bool { let x: i32 = self.loc.0 + self.dir.0; let y: i32 = self.loc.1 + self.dir.1; if (x < 0 or x >= 130 or y < 0 or y >= 130) { return false; } if (self.data[x][y] == Wall()) { // TODO: Should this work? // self.dir = (-self.dir.1, self.dir.0); let d: (i32, i32) = self.dir; self.dir = (-d.1, d.0); } else { self.loc = (x, y); self.data[x][y] = Visited(); } return true; } fn AddObstacle[ref self: Self]() -> bool { let x: i32 = self.loc.0 + self.dir.0; let y: i32 = self.loc.1 + self.dir.1; if (x < 0 or x >= 130 or y < 0 or y >= 130) { return false; } if (self.data[x][y] != Empty()) { return false; } self.data[x][y] = Wall(); return true; } fn CountVisited[self: Self]() -> i32 { var total: i32 = 0; var y: i32 = 0; while (y < 130) { var x: i32 = 0; while (x < 130) { if (self.data[x][y] == Visited()) { ++total; } ++x; } ++y; } return total; } var data: array(array(i8, 130), 130); var loc: (i32, i32); var dir: (i32, i32); } ================================================ FILE: examples/advent2024/day6_part1.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // https://adventofcode.com/2024/day/6 import Core library "io"; import library "day6_common"; fn Run() { var maze: Maze = Maze.Read(); while (maze.Step()) { } Core.Print(maze.CountVisited()); } ================================================ FILE: examples/advent2024/day6_part2.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // https://adventofcode.com/2024/day/6 import Core library "io"; import library "day6_common"; class LoopDetector { fn Make() -> LoopDetector { return {.last = ((-1, -1), (-1, -1)), .steps = 1, .next_steps = 1}; } fn Check[ref self: Self](next: ((i32, i32), (i32, i32))) -> bool { // TODO: if (next == self.last) { if (next.0.0 == self.last.0.0 and next.0.1 == self.last.0.1 and next.1.0 == self.last.1.0 and next.1.1 == self.last.1.1) { return true; } --self.steps; if (self.steps == 0) { self.steps = self.next_steps; self.next_steps <<= 1; self.last = next; } return false; } var last: ((i32, i32), (i32, i32)); var steps: i32; var next_steps: i32; } fn AddingObstacleMakesALoop(position: Maze) -> bool { var maze: Maze = position.Copy(); var loop: LoopDetector = LoopDetector.Make(); if (not maze.AddObstacle()) { return false; } while (maze.Step()) { if (loop.Check((maze.loc, maze.dir))) { return true; } } return false; } fn Run() { var maze: Maze = Maze.Read(); var loops: i32 = 0; while (true) { if (AddingObstacleMakesALoop(maze)) { ++loops; } if (not maze.Step()) { break; } } Core.Print(loops); } ================================================ FILE: examples/advent2024/day7_common.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // https://adventofcode.com/2024/day/7 library "day7_common"; import Core library "io"; import library "io_utils"; fn Concat(a_val: i64, b_val: i64) -> i64 { var a: i64 = a_val; var b: i64 = b_val; if (b == 0) { return a * 10; } while (b != 0) { a *= 10; b /= 10; } return a + b_val; } class Equation { impl as Core.UnformedInit {} fn Read() -> Equation { returned var me: Equation; me.num_operands = 0; ReadInt(ref me.result); ConsumeChar(':'); while (ConsumeChar(' ')) { ReadInt(ref me.operands[me.num_operands]); ++me.num_operands; } while (SkipNewline()) {} return var; } fn SolveFrom[self: Self](start: i32, value: i64, concat: bool) -> bool { if (value > self.result) { return false; } if (start == self.num_operands) { return value == self.result; } return self.SolveFrom(start + 1, value + self.operands[start], concat) or self.SolveFrom(start + 1, value * self.operands[start], concat) or (concat and self.SolveFrom(start + 1, Concat(value, self.operands[start]), true)); } fn Solve[self: Self](concat: bool) -> bool { return self.SolveFrom(1, self.operands[0], concat); } var operands: array(i64, 16); var num_operands: i32; var result: i64; } ================================================ FILE: examples/advent2024/day7_part1.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // https://adventofcode.com/2024/day/7 import Core library "io"; import library "day7_common"; import library "io_utils"; fn Run() { var total: i64 = 0; while (PeekChar() != CharOrEOF.EOF()) { var eq: Equation = Equation.Read(); if (eq.Solve(false)) { total += eq.result; } } PrintInt(total); } ================================================ FILE: examples/advent2024/day7_part2.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // https://adventofcode.com/2024/day/7 import Core library "io"; import library "day7_common"; import library "io_utils"; fn Run() { var total: i64 = 0; while (PeekChar() != CharOrEOF.EOF()) { var eq: Equation = Equation.Read(); if (eq.Solve(true)) { total += eq.result; } } PrintInt(total); } ================================================ FILE: examples/advent2024/day8_common.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // https://adventofcode.com/2024/day/8 library "day8_common"; import Core library "io"; import Core library "range"; import library "io_utils"; class Grid { impl as Core.UnformedInit {} fn Read() -> Grid { returned var me: Grid; for (y: i32 in Core.Range(50)) { for (x: i32 in Core.Range(50)) { me.data[x][y] = ReadChar() as i32; } SkipNewline(); } return var; } var data: array(array(i32, 50), 50); } ================================================ FILE: examples/advent2024/day8_part1.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // https://adventofcode.com/2024/day/8 import Core library "io"; import Core library "range"; import library "day8_common"; import library "io_utils"; fn IsAntinode(grid: Grid, ox: i32, oy: i32) -> bool { for (ay: i32 in Core.Range(50)) { let by: i32 = ay * 2 - oy; if (by >= 0 and by < 50) { for (ax: i32 in Core.Range(50)) { let bx: i32 = ax * 2 - ox; if (bx >= 0 and bx < 50 and (ax != bx or ay != by)) { if (grid.data[ax][ay] != 0x2E and grid.data[ax][ay] == grid.data[bx][by]) { return true; } } } } } return false; } fn CountAntinodes(grid: Grid) -> i32 { var count: i32 = 0; for (y: i32 in Core.Range(50)) { for (x: i32 in Core.Range(50)) { if (IsAntinode(grid, x, y)) { ++count; } } } return count; } fn Run() { Core.Print(CountAntinodes(Grid.Read())); } ================================================ FILE: examples/advent2024/day8_part2.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // https://adventofcode.com/2024/day/8 import Core library "io"; import Core library "range"; import library "day8_common"; import library "io_utils"; fn MarkAndCount(marks: array(array(bool, 50), 50)*, x: i32, y: i32) -> i32 { if (not (*marks)[x][y]) { (*marks)[x][y] = true; return 1; } return 0; } fn MarkAndCountAntinodesFor(grid: Grid, marks: array(array(bool, 50), 50)*, ax: i32, ay: i32) -> i32 { var count: i32 = 0; for (by: i32 in Core.Range(50)) { for (bx: i32 in Core.Range(50)) { let dx: i32 = bx - ax; let dy: i32 = by - ay; if (grid.data[bx][by] != grid.data[ax][ay] or (dx == 0 and dy == 0)) { continue; } var x: i32 = bx; var y: i32 = by; while (x >= 0 and x < 50 and y >= 0 and y < 50) { count += MarkAndCount(marks, x, y); x += dx; y += dy; } } } return count; } fn MarkAndCountAntinodes(grid: Grid, marks: array(array(bool, 50), 50)*) -> i32 { var count: i32 = 0; for (y: i32 in Core.Range(50)) { for (x: i32 in Core.Range(50)) { if (grid.data[x][y] != 0x2E) { count += MarkAndCountAntinodesFor(grid, marks, x, y); } } } return count; } fn Run() { var marks: array(array(bool, 50), 50); for (y: i32 in Core.Range(50)) { for (x: i32 in Core.Range(50)) { marks[x][y] = false; } } Core.Print(MarkAndCountAntinodes(Grid.Read(), &marks)); } ================================================ FILE: examples/advent2024/day9_common.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // https://adventofcode.com/2024/day/9 library "day9_common"; import Core library "io"; import Core library "range"; import library "io_utils"; class SectorList { impl as Core.UnformedInit {} fn Read() -> SectorList { returned var me: SectorList; me.size = 0; var sector: i32 = 0; var used: bool = true; // TODO: An assignment expression would be useful here. var c: CharOrEOF = ReadChar(); while (c != CharOrEOF.EOF() and c != '\n') { let v: i32 = if used then sector else -1; for (_: i32 in Core.Range(c - '0')) { me.data[me.size] = v; ++me.size; } if (used) { ++sector; } used = not used; c = ReadChar(); } return var; } fn DefragBlocks[ref self: Self]() { var i: i32 = 0; var j: i32 = self.size - 1; while (j > i) { if (self.data[i] != -1) { ++i; } else if (self.data[j] == -1) { --j; } else { self.data[i] = self.data[j]; ++i; --j; } } self.size = j; } fn HasSpace[ref self: Self](start: i32, size: i32) -> bool { for (i: i32 in Core.InclusiveRange(start, start + size - 1)) { if (self.data[i] != -1) { return false; } } return true; } fn Fill[ref self: Self](start: i32, size: i32, sector: i32) { for (i: i32 in Core.InclusiveRange(start, start + size - 1)) { self.data[i] = sector; } } fn DefragFiles[ref self: Self]() { var j: i32 = self.size - 1; while (j >= 0) { // Skip empty sectors. while (j >= 0 and self.data[j] == -1) { --j; } // Find the file size and sector and delete the file. let last: i32 = j; let sector: i32 = self.data[last]; while (j >= 0 and self.data[j] == sector) { self.data[j] = -1; --j; } let first: i32 = j + 1; let size: i32 = last - first + 1; // Find the first available space for the file. var i: i32 = 0; while (not self.HasSpace(i, size)) { ++i; if (i + size > first) { // If it doesn't fit to the left of its old position, put it back // where it was. i = first; } } // Insert it. self.Fill(i, size, sector); } } fn Checksum[self: Self]() -> i64 { var total: i64 = 0; for (i: i32 in Core.Range(self.size)) { if (self.data[i] != -1) { total = total + ((i * self.data[i]) as i64); } } return total; } var data: array(i32, 200000); var size: i32; } ================================================ FILE: examples/advent2024/day9_part1.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // https://adventofcode.com/2024/day/9 import Core library "io"; import library "day9_common"; import library "io_utils"; fn Run() { var list: SectorList = SectorList.Read(); list.DefragBlocks(); PrintInt(list.Checksum()); } ================================================ FILE: examples/advent2024/day9_part2.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // https://adventofcode.com/2024/day/9 import Core library "io"; import library "day9_common"; import library "io_utils"; fn Run() { var list: SectorList = SectorList.Read(); list.DefragFiles(); PrintInt(list.Checksum()); } ================================================ FILE: examples/advent2024/io_utils.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception library "io_utils"; import Core library "io"; import Core library "range"; class EOFType {} class CharOrEOF { adapt i32; fn EOF() -> Self; } impl CharOrEOF as Core.Copy { fn Op[self: Self]() -> Self { return (self as i32).(Core.Copy.Op)() as Self; } } fn CharOrEOF.EOF() -> Self { // Ordering between `CharOrEOF` and `char` treats EOF as less than all `char` // values. return (-1 as i32) as CharOrEOF; } impl forall [U:! Core.ImplicitAs(char)] U as Core.ImplicitAs(CharOrEOF) { fn Convert[self: char]() -> CharOrEOF { return ((self as u8) as i32) as CharOrEOF; } } impl forall [U:! Core.ImplicitAs(CharOrEOF)] CharOrEOF as Core.EqWith(U) { fn Equal[self: Self](other: CharOrEOF) -> bool { return (self as i32) == (other as i32); } fn NotEqual[self: Self](other: CharOrEOF) -> bool { return (self as i32) != (other as i32); } } impl forall [U:! Core.ImplicitAs(CharOrEOF)] CharOrEOF as Core.OrderedWith(U) { fn Less[self: Self](other: CharOrEOF) -> bool { return (self as i32) < (other as i32); } fn LessOrEquivalent[self: Self](other: CharOrEOF) -> bool { return (self as i32) <= (other as i32); } fn Greater[self: Self](other: CharOrEOF) -> bool { return (self as i32) > (other as i32); } fn GreaterOrEquivalent[self: Self](other: CharOrEOF) -> bool { return (self as i32) >= (other as i32); } } impl forall [U:! Core.ImplicitAs(char)] CharOrEOF as Core.SubWith(U) where .Result = i32 { fn Op[self: Self](other: char) -> i32 { return (self as i32) - ((other as u8) as i32); } } var unread_char: Core.Optional(CharOrEOF) = Core.Optional(CharOrEOF).None(); fn ReadChar() -> CharOrEOF { if (unread_char.HasValue()) { var result: CharOrEOF = unread_char.Get(); unread_char = Core.Optional(CharOrEOF).None(); return result; } var value: i32 = Core.ReadChar(); if (value == Core.EOF()) { return CharOrEOF.EOF(); } else { return value as CharOrEOF; } } fn UnreadChar(c: CharOrEOF) { // TODO: assert(unread_char == 0); // TODO: unread_char = c; unread_char = Core.Optional(CharOrEOF).Some(c); } fn PeekChar() -> CharOrEOF { var next: CharOrEOF = ReadChar(); UnreadChar(next); return next; } fn ConsumeChar(c: char) -> bool { var next: CharOrEOF = ReadChar(); if (next != c) { UnreadChar(next); return false; } return true; } fn ReadInt[N:! Core.IntLiteral()](ref p: Core.Int(N)) -> bool { var read_any_digits: bool = false; let negative: bool = ConsumeChar('-'); // TODO: `p = 0;` crashes the toolchain, see // https://github.com/carbon-language/carbon-lang/issues/6500. p = (0 as i32) as Core.Int(N); while (true) { var c: CharOrEOF = ReadChar(); if (c < '0' or c > '9') { UnreadChar(c); break; } // TODO: Check for overflow. // TODO: p *= 10; crashes the toolchain. p *= (10 as i32) as Core.Int(N); p += (c - '0') as Core.Int(N); read_any_digits = true; } if (negative) { p = -p; if (not read_any_digits) { UnreadChar('-'); } } return read_any_digits; } fn PrintIntNoNewline[N:! Core.IntLiteral()](n_val: Core.Int(N)) { // TODO: var pow10: Core.Int(N) = 1; crashes the toolchain. var pow10: Core.Int(N) = (1 as i32) as Core.Int(N); var n: Core.Int(N) = n_val; // TODO: let ten: Core.Int(N) = 10; let ten: Core.Int(N) = (10 as i32) as Core.Int(N); while (n / ten >= pow10) { pow10 = pow10 * ten; } // TODO: while (pow10 != 0) { while (pow10 != ((0 as i32) as Core.Int(N))) { let d: Core.Int(N) = n / pow10; // TODO: Core.PrintChar('0' + d); Core.PrintChar(((d as u8) + (('0' as char) as u8)) as char); n = n % pow10; pow10 = pow10 / ten; } } fn PrintInt[N:! Core.IntLiteral()](n_val: Core.Int(N)) { PrintIntNoNewline(n_val); Core.PrintChar('\n'); } fn SkipSpaces() -> bool { var skipped_any_spaces: bool = false; while (ConsumeChar(' ')) { skipped_any_spaces = true; } return skipped_any_spaces; } fn SkipNewline() -> bool { // Optional carriage return. ConsumeChar('\r'); // Newline. // TODO: Unread the CR if it was present? return ConsumeChar('\n'); } fn SkipNChars(n: i32) -> bool { for (_: i32 in Core.Range(n)) { if (ReadChar() == CharOrEOF.EOF()) { return false; } } return true; } ================================================ FILE: examples/advent2024/sort.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception library "sort"; interface Ordered { // extend require impls Core.OrderedWith(Self); fn Less[self: Self](other: Self) -> bool; fn LessOrEquivalent[self: Self](other: Self) -> bool; fn Greater[self: Self](other: Self) -> bool; fn GreaterOrEquivalent[self: Self](other: Self) -> bool; } impl i32 as Ordered { fn Less[self: Self](other: Self) -> bool { return self < other; } fn LessOrEquivalent[self: Self](other: Self) -> bool { return self <= other; } fn Greater[self: Self](other: Self) -> bool { return self > other; } fn GreaterOrEquivalent[self: Self](other: Self) -> bool { return self >= other; } } fn Swap[T:! Core.Copy & Core.Destroy](ref from: T, ref to: T) { var tmp: T = from; from = to; to = tmp; } fn Partition [T:! Core.Copy & Core.Destroy & Ordered, N:! Core.IntLiteral()] (ref a: array(T, N), from_in: i32, to_in: i32) -> i32 { var pivot_index: i32 = from_in; let pivot: T = a[pivot_index]; var from: i32 = from_in + 1; var to: i32 = to_in; while (from < to) { // TODO: if (a[from] <= pivot) { if (a[from].(Ordered.LessOrEquivalent)(pivot)) { ++from; // TODO: } else if ((*p)[to - 1] > pivot) { } else if (a[to - 1].(Ordered.Greater)(pivot)) { --to; } else { // Element at `from` is > pivot, and // element at `to - 1` is <= pivot. Swap(ref a[from], ref a[to - 1]); ++from; --to; } } Swap(ref a[pivot_index], ref a[from - 1]); return from - 1; } fn Quicksort [T:! Core.Copy & Core.Destroy & Ordered, N:! Core.IntLiteral()] (ref a: array(T, N), from: i32, to: i32) { if (from + 1 >= to) { return; } var pivot: i32 = Partition(ref a, from, to); Quicksort(ref a, from, pivot); Quicksort(ref a, pivot + 1, to); } ================================================ FILE: examples/bazel/BUILD ================================================ # Part of the Carbon Language project, under the Apache License v2.0 with LLVM # Exceptions. See /LICENSE for license information. # SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception # # Note that this is not a BUILD file that is part of the larger Carbon project, and # is not built by running `bazel build //examples/bazel/...`. This is its own # _distinct_ example Bazel project rooted at `examples/bazel`. You will need to # `cd` into this directory to interact with it. # # For more details about how to manually interact with this example, please see # the adjacent `MODULES.bazel` file. load("@rules_cc//cc:defs.bzl", "cc_binary", "cc_library") cc_library( name = "example_lib", srcs = [ "example_lib.cpp", "example_lib.h", ], # We force static linking here so we can test compilation without performing # a full link that is more expensive with runtimes on demand. linkstatic = 1, ) cc_binary( name = "example", srcs = ["example.cpp"], deps = [":example_lib"], ) ================================================ FILE: examples/bazel/MODULE.bazel ================================================ # Part of the Carbon Language project, under the Apache License v2.0 with LLVM # Exceptions. See /LICENSE for license information. # SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception """Example Bazel module that builds C++ with the Carbon toolchain. Note that this is at the root of an example Bazel project, not part of the larger Carbon Bazel project. To interact with this example, you'll want to `cd examples/bazel` so that you can operate Bazel entirely within this example (sub-)project. Once interacting with this subdirectory's project, you can use this and build with the Carbon toolchain in a few different ways: 1) Override the module with a local installation on the command line: `bazel build --override_module=carbon_toolchain=/path/to/carbon_toolchain/installation/lib/carbon` 2) Encode a local override into this file: ``` local_path_override( module_name = "carbon_toolchain", path = "/path/to/carbon_toolchain/installation/lib/carbon", ) ``` 3) Encode a archive override into this file: ``` version = "0.0.0-0.nightly.YYYY.MM.DD" archive_override( module_name = "carbon_toolchain", strip_prefix = "carbon_toolchain-{0}/lib/carbon".format(version), urls = ["https://github.com/carbon-language/carbon-lang/releases/download/v{0}/carbon_toolchain-{0}.tar.gz".format(version)], ) ``` Note: Initially, Bazel will warn about the lack of an `integrity` field, and will print the value it found by downloading the archive which you can verify on GitHub before encoding. You can use the provided script `update_module_to_nightly.py` to generate and add an `archive_override` of the current nightly release, including integrity from GitHub. Once the Carbon toolchain has established releases in the Bazel central registry, this file will work with an updated version out of the box. """ module(name = "example") bazel_dep(name = "rules_cc", version = "0.2.14") # Declare the `carbon_toolchain` module. This is needed even if it will be # overridden with a local path or archive. bazel_dep(name = "carbon_toolchain", version = "0.0.0") register_toolchains("@carbon_toolchain//:all") ================================================ FILE: examples/bazel/example.cpp ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #include #include "example_lib.h" auto main() -> int { HelloWorld(); return EXIT_SUCCESS; } ================================================ FILE: examples/bazel/example_lib.cpp ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #include "example_lib.h" #include auto HelloWorld() -> void { std::cout << "Hello World!\n"; } ================================================ FILE: examples/bazel/example_lib.h ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #ifndef CARBON_EXAMPLES_BAZEL_EXAMPLE_LIB_H_ #define CARBON_EXAMPLES_BAZEL_EXAMPLE_LIB_H_ auto HelloWorld() -> void; #endif // CARBON_EXAMPLES_BAZEL_EXAMPLE_LIB_H_ ================================================ FILE: examples/bazel/update_module_to_nightly.py ================================================ #!/usr/bin/env python3 """Updates example module file to use the nightly toolchain release. This script computes the most recent nightly Carbon toolchain release, and updates the example module file with an `archive_override` pointing at it. Usage: # Within the `examples/bazel` directory: ./update_module_to_nightly.py For more details about using the Carbon toolchain with Bazel, see the documentation in `examples/bazel/MODULE.bazel`. """ __copyright__ = """ Part of the Carbon Language project, under the Apache License v2.0 with LLVM Exceptions. See /LICENSE for license information. SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception """ import re import os import sys import base64 import urllib.request import urllib.error import json MODULE_NAME = "carbon_toolchain" MODULE_FILENAME = "MODULE.bazel" DEP_PATTERN = re.compile( rf'^bazel_dep\s*\(\s*name\s*=\s*"{MODULE_NAME}".*?\)', re.DOTALL | re.MULTILINE, ) OVERRIDE_PATTERN = re.compile( rf'^archive_override\s*\(\s*module_name\s*=\s*"{MODULE_NAME}".*?\)', re.DOTALL | re.MULTILINE, ) # The nightly build starts at 2am UTC, and we give it up to 4 hours to complete. BUFFER_HOURS = 6 RELEASES_URL = ( "https://github.com/carbon-language/carbon-lang/releases/download" ) RELEASES_API_URL = ( "https://api.github.com/repos/carbon-language/carbon-lang/releases" ) API_HEADERS = { "Accept": "application/vnd.github+json", "X-GitHub-Api-Version": "2022-11-28", # GitHub API requires a User-Agent, urllib doesn't send one by default "User-Agent": "python-urllib", } def log(msg: str) -> None: print(f"[update_module_to_nightly] {msg}", file=sys.stderr) def get_latest_version() -> str: # Use the 'releases' list endpoint, NOT 'releases/latest'. Using the # `latest` endpoint only works for full releases, not pre-releases. Carbon's # nightly releases are classified as pre-releases so we have to get the full # list and simply take the first one. That does mean we only need the first # page of results. url = f"{RELEASES_API_URL}?per_page=1" req = urllib.request.Request(url, headers=API_HEADERS) try: with urllib.request.urlopen(req) as response: data = json.load(response) if not data: log("Error: no releases found for this repository.") sys.exit(1) # The API returns a list sorted by creation date (newest first). latest_release = data[0] except urllib.error.HTTPError as e: log(f"Error: HTTP error {e.code} fetching latest release: {e.reason}") # It's often useful to print the body for GitHub API errors (e.g. rate # limit exceeded) log(e.read().decode("utf-8")) sys.exit(1) # The release tag starts with `v` followed by the version. latest_version = str(latest_release["tag_name"]) if not latest_version.startswith("v"): log(f"Error: malformed release tag name: {latest_version}") sys.exit(1) return latest_version[1:] def get_digest(version: str, filename: str) -> str: url = f"{RELEASES_API_URL}/tags/v{version}" req = urllib.request.Request(url, headers=API_HEADERS) try: with urllib.request.urlopen(req) as response: release_data = json.load(response) except urllib.error.HTTPError as e: log(f"Error: unable to find `v{version}`: {e.code}: {e.reason}") sys.exit(1) assets = release_data.get("assets", []) for asset in assets: name = str(asset.get("name")) if name != filename: continue digest = str(asset.get("digest")) if not digest.startswith("sha256:"): log(f"Error: found invalid digest for `{filename}`: `{digest}`") sys.exit(1) # Re-encode from the GitHub format to Bazel. digest = ( "sha256-" + base64.b64encode(bytes.fromhex(digest[len("sha256:") :])).decode() ) return digest log(f"Error: unable to find a digest for `{filename}`") sys.exit(1) def generate_override(version: str) -> str: basename = f"carbon_toolchain-{version}" digest = get_digest(version, f"{basename}.tar.gz") return ( f"archive_override(\n" f' module_name = "{MODULE_NAME}",\n' f' integrity = "{digest}",\n' f' strip_prefix = "{basename}/lib/carbon",\n' f' urls = ["{RELEASES_URL}/v{version}/{basename}.tar.gz"],\n' f")" ) def main() -> None: if not os.path.exists(MODULE_FILENAME): log(f"Error: `{MODULE_FILENAME}` not found in current directory.") sys.exit(1) with open(MODULE_FILENAME, "r") as f: content = f.read() # 1. Verification (Check if dependency exists) dep_match = DEP_PATTERN.search(content) if not dep_match: log( f"Error: `bazel_dep` for `{MODULE_NAME}` not found in " f"`{MODULE_FILENAME}`." ) sys.exit(1) version = get_latest_version() new_block = generate_override(version) new_content, count = OVERRIDE_PATTERN.subn(new_block, content) if count > 0: log("Existing override found, replacing with a fresh one") else: log("No existing override found, inserting one") new_content = ( content[: dep_match.end()] + "\n\n" + new_block + content[dep_match.end() :] ) with open(MODULE_FILENAME, "w") as f: f.write(new_content) log(f"Successfully updated `{MODULE_FILENAME}` to version `{version}`") if __name__ == "__main__": try: main() except KeyboardInterrupt: sys.exit(1) ================================================ FILE: examples/bazel_test_runner.py ================================================ #!/usr/bin/env python3 """Checks various LLVM tool symlinks behave as expected.""" __copyright__ = """ Part of the Carbon Language project, under the Apache License v2.0 with LLVM Exceptions. See /LICENSE for license information. SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception """ import os import sys import time import unittest from bazel_tools.tools.python.runfiles import runfiles from bazel_integration_test.py import test_base class BazelExampleTest(test_base.TestBase): def setUp(self) -> None: test_base.TestBase.setUp(self) self.runfiles = runfiles.Create() self.install_module = self.runfiles.Rlocation( "carbon/toolchain/install/prefix/lib/carbon" ) self.startup_flags = [ "--ignore_all_rc_files", "--batch", ] self.flags = [ f"--override_module=carbon_toolchain={self.install_module}" ] def _run_bazel(self, command: list[str]) -> str: """Runs bazel with retry logic for transient errors.""" for attempt in range(5): exit_code, stdout, stderr = self.RunBazel( self.startup_flags + command + self.flags ) # Several error codes are reliably permanent, break immediately. # `1` -- The build failed. # `2` -- Command line or environment problem. # `3` -- Tests failed or timed out, we don't retry at this layer # on execution timeout. # `4` -- Test command but no tests found. # `8` -- Explicitly interrupted build. # # Note that `36` is documented as "likely permanent", but we retry # it as most of our transient failures actually produce that error # code. perm_error = (1, 2, 3, 4, 8) if exit_code in perm_error: break for line in stderr: print(line, file=sys.stderr) if exit_code == 0: return stdout # Retry transient errors with a brief delay. print(f"Attempt {attempt + 1} failed with exit code {exit_code}") time.sleep(attempt) self.AssertExitCode(exit_code, 0, stderr) def test_compile_lib(self) -> None: # TODO: Can remove this in favor of always running `test_run` if we can # make linking a binary sufficiently efficient. self._run_bazel(["build", "//:example_lib"]) @unittest.skipUnless( "CARBON_BAZEL_TEST_FULL" in os.environ, "Skipping expensive test step for minimal testing", ) def test_run(self) -> None: stdout = self._run_bazel(["run", "//:example"]) self.assertEqual(stdout, ["Hello World!"]) if __name__ == "__main__": unittest.main() ================================================ FILE: examples/hello_world.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception import Core library "io"; fn Run() { Core.PrintStr("Hello world!\n"); } ================================================ FILE: examples/interop/cpp/BUILD ================================================ # Part of the Carbon Language project, under the Apache License v2.0 with LLVM # Exceptions. See /LICENSE for license information. # SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception load("//bazel/carbon_rules:defs.bzl", "carbon_binary") carbon_binary( name = "hello_world", srcs = ["hello_world.carbon"], ) carbon_binary( name = "socket", srcs = ["socket.carbon"], ) ================================================ FILE: examples/interop/cpp/hello_world.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception import Cpp library ""; import Cpp library ""; import Cpp library ""; import Cpp library ""; // Demonstrate passing `char`s to a C++ function. fn HelloPutchar() { let message: array(char, 13) = ('H', 'e', 'l', 'l', 'o', ' ', 'w', 'o', 'r', 'l', 'd', '!', '\n'); for (c: char in message) { // TODO: u8 should probably have an implicit cast to i32. Cpp.putchar((c as u8) as i32); } // TODO: Use a loop over a string literal instead. Requires IndexWith support // for str. // // let message: str = "Hello world!\n"; // for (c: char in message) { // Cpp.putchar((c as u8) as i32); // } } // Demonstrate passing a null-terminated string to a C++ function. fn HelloStdio() { // TODO: There should be a better way to interact with functions that expect a // null-terminated string. Cpp.puts(Cpp.std.data("Hello world!\0")); // TODO: Requires variadic function support. // Cpp.printf("Hello world!\n\0"); } // Demonstrate passing a string as a void pointer to a C++ function. fn HelloWrite() { let s: str = "Hello world!\n"; Cpp.write(1, Cpp.std.data(s), Cpp.std.size(s)); } fn HelloIostreams() { Cpp.std.cout << "Hello world!\n"; } fn Run() { HelloPutchar(); HelloStdio(); HelloWrite(); HelloIostreams(); } ================================================ FILE: examples/interop/cpp/socket.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception import Core library "io"; import Cpp library ""; import Cpp library ""; import Cpp library ""; import Cpp library ""; import Cpp library ""; import Cpp library ""; import Cpp inline ''' #define SOCKADDR_IN_SIZE sizeof(struct sockaddr_in6) // Work around htons being a macro, not a function, on MacOS. // TODO: This should not be necessary once we can import function-like macros. inline unsigned short htons_wrap(unsigned short n) { return htons(n); } '''; fn Perror(s: str) { // TODO: Should we allow passing a string literal to a `const char*` // parameter? Cpp.perror(Cpp.std.data(s)); } // Basic example of using POSIX networking functions via interop. // // Opens a socket listening on port 8081, waits for a single connection, then // reads a single chunk of up to 256 bytes from it and echoes it to the // terminal. fn Run() -> i32 { // TODO: `Cpp.SOCK_STREAM as i32` should probably be allowed. // TODO: Should this be an implicit conversion? let server_fd: i32 = Cpp.socket(Cpp.AF_INET6, (Cpp.SOCK_STREAM as u32) as i32, 0); if (server_fd == -1) { Perror("socket failed"); return 1; } // TODO: This initialization should zero-initialize the struct. // TODO: We should be able to use simply `= ()` or `= {}` to get the same effect. var address: Cpp.sockaddr_in6 = Cpp.sockaddr_in6.sockaddr_in6(); Cpp.memset(&address, 0, Cpp.SOCKADDR_IN_SIZE); // TODO: Should this be valid without a cast? `AF_INET6` is defined to an // integer literal in glibc at least, but we import it as an `i32`. address.sin6_family = Cpp.AF_INET6 as Cpp.sa_family_t; // TODO: This leads to a link error referring to `in6addr_any.1`. // address.sin6_addr = Cpp.in6addr_any; let port: u16 = 8081; address.sin6_port = Cpp.htons_wrap(port); let address_ptr: Cpp.sockaddr* = &address unsafe as Cpp.sockaddr*; // TODO: Should have an implicit conversion from // T* to Optional(const T*). // TODO: Should expose `sizeof` somehow. let bind_result: i32 = Cpp.bind( server_fd, address_ptr as const Cpp.sockaddr*, Cpp.SOCKADDR_IN_SIZE as u32 ); if (bind_result == -1) { Perror("bind failed"); return 1; } if (Cpp.listen(server_fd, 1) == -1) { Perror("listen failed"); return 1; } // TODO: Add a better `Core.Print`. Core.PrintStr("Server is listening on port "); Core.Print(port as i32); var addr_len: Cpp.socklen_t = 0; let socket_fd: i32 = Cpp.accept( server_fd, address_ptr, &addr_len ); if (socket_fd == -1) { Perror("accept failed"); return 1; } Core.PrintStr("Connection accepted!\n"); var buffer: array(char, 256); let len: Cpp.ssize_t = Cpp.read(socket_fd, &buffer[0], 256); if (len < 0) { Perror("read failed"); return 1; } Cpp.write(1, &buffer[0], len as Cpp.size_t); Cpp.close(socket_fd); Cpp.close(server_fd); return 0; } ================================================ FILE: examples/re2_playground/BUILD ================================================ # Part of the Carbon Language project, under the Apache License v2.0 with LLVM # Exceptions. See /LICENSE for license information. # SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception load("//bazel/carbon_rules:defs.bzl", "carbon_binary") carbon_binary( name = "re2_playground", srcs = ["re2_playground.carbon"], flags = ["--clang-arg=-std=c++20"], tags = ["manual"], deps = [ "@llvm-project//llvm:LineEditor", "@re2", ], ) ================================================ FILE: examples/re2_playground/re2_playground.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception import Cpp library "llvm/LineEditor/LineEditor.h"; import Cpp library "llvm/Support/raw_ostream.h"; import Cpp library "re2/re2.h"; import Cpp inline "using OptionalString = std::optional;"; import Cpp inline "std::string_view AsStringView(const std::string &s) { return s; }"; import Core library "io"; class RE2 { extend adapt Cpp.re2.RE2; fn Make(re: str) -> Self { return RE2(re) as Self; } fn Match[self: Self](text: str) -> bool { return FullMatch(text, self as Cpp.re2.RE2); } } impl str as Core.ImplicitAs(Cpp.llvm.StringRef) { fn Convert[self: Self]() -> Cpp.llvm.StringRef { return Cpp.llvm.StringRef.StringRef(self); } } fn Print(var s: str) { *Cpp.llvm.outs() << s; } fn Run() -> i32 { var editor: Cpp.llvm.LineEditor = Cpp.llvm.LineEditor.LineEditor("re2_playground"); editor.setPrompt(Cpp.std.string.basic_string("re2> ")); Print(''' Usage: /regex/ -- sets the current regular expression to regex anything else -- matches the entered text against the specified regex '''); var re: RE2 = RE2.Make("^a*$"); while (true) { var line: Cpp.OptionalString = editor.readLine(); if (not line.has_value()) { break; } if (line.value()->starts_with("/") and line.value()->ends_with("/")) { re = RE2.Make(Cpp.AsStringView(line.value()->substr(1, line.value()->size() - 2))); continue; } if (re.Match(Cpp.AsStringView(*line.value()))) { Print("Match\n"); } else { Print("No match\n"); } } return 0; } ================================================ FILE: examples/sieve.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception import Core library "io"; import Core library "range"; // Compute and return the number of primes less than 1000. class Sieve { impl as Core.UnformedInit {} fn Make() -> Sieve { returned var s: Sieve; for (n: i32 in Core.Range(1000)) { s.is_prime[n] = true; } return var; } fn MarkMultiplesNotPrime[ref self: Self](p: i32) { var n: i32 = p * 2; while (n < 1000) { self.is_prime[n] = false; n += p; } } var is_prime: array(bool, 1000); } fn Run() -> i32 { var s: Sieve = Sieve.Make(); var number_of_primes: i32 = 0; for (n: i32 in Core.InclusiveRange(2, 999)) { if (s.is_prime[n]) { ++number_of_primes; Core.Print(n); s.MarkMultiplesNotPrime(n); } } return number_of_primes; } ================================================ FILE: github_tools/BUILD ================================================ # Part of the Carbon Language project, under the Apache License v2.0 with LLVM # Exceptions. See /LICENSE for license information. # SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception load("@py_deps//:requirements.bzl", "requirement") load("@rules_python//python:defs.bzl", "py_binary", "py_library", "py_test") load("@rules_python//python:pip.bzl", "compile_pip_requirements") py_library( name = "github_helpers", srcs = ["github_helpers.py"], deps = [requirement("gql")], ) compile_pip_requirements( name = "requirements", src = "requirements.in", requirements_txt = "requirements.txt", ) py_test( name = "github_helpers_test", size = "small", srcs = ["github_helpers_test.py"], deps = [":github_helpers"], ) py_binary( name = "pr_comments", srcs = ["pr_comments.py"], deps = ["github_helpers"], ) py_test( name = "pr_comments_test", size = "small", srcs = ["pr_comments_test.py"], deps = [":pr_comments"], ) ================================================ FILE: github_tools/MODULE.bazel ================================================ # Part of the Carbon Language project, under the Apache License v2.0 with LLVM # Exceptions. See /LICENSE for license information. # SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception """Bazel modules. This is set up as a separate bazel repo from the main Carbon directory in order to split up the pip dependency, which makes MODULE.bazel.lock platform-dependent. We don't commit the MODULE.bazel.lock for github_tools because it's low-value, and would shift the platform dependence costs. """ module(name = "github_tools") bazel_dep(name = "rules_python", version = "0.27.1") python = use_extension("@rules_python//python/extensions:python.bzl", "python") python.toolchain( python_version = "3.11", ) use_repo(python, "python_versions") # Create a central repo that knows about the pip dependencies. pip = use_extension("@rules_python//python/extensions:pip.bzl", "pip") pip.parse( hub_name = "py_deps", python_version = "3.11", requirements_lock = "//:requirements.txt", ) use_repo(pip, "py_deps") ================================================ FILE: github_tools/README.md ================================================ # GitHub Scripts for use with GitHub. See individual scripts for more details. ================================================ FILE: github_tools/WORKSPACE ================================================ # Part of the Carbon Language project, under the Apache License v2.0 with LLVM # Exceptions. See /LICENSE for license information. # SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception workspace(name = "github_tools") ================================================ FILE: github_tools/__init__.py ================================================ ================================================ FILE: github_tools/github_helpers.py ================================================ """GitHub GraphQL helpers. https://developer.github.com/v4/explorer/ is very useful for building queries. """ __copyright__ = """ Part of the Carbon Language project, under the Apache License v2.0 with LLVM Exceptions. See /LICENSE for license information. SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception """ import argparse from collections.abc import Generator import os from typing import Optional # https://pypi.org/project/gql/ import gql # type: ignore import gql.transport.requests # type: ignore _ENV_TOKEN = "GITHUB_ACCESS_TOKEN" # Query elements for pagination. PAGINATION = """pageInfo { hasNextPage endCursor } totalCount""" def add_access_token_arg( parser: argparse.ArgumentParser, permissions: str ) -> None: """Adds a flag to set the access token.""" access_token = os.environ.get(_ENV_TOKEN, default=None) parser.add_argument( "--access-token", metavar="ACCESS_TOKEN", default=access_token, required=not access_token, help="The access token for use with GitHub. May also be specified in " "the environment as %s. The access token should have permissions: %s" % (_ENV_TOKEN, permissions), ) class Client: """A GitHub GraphQL client.""" def __init__(self, parsed_args: argparse.Namespace): """Connects to GitHub.""" transport = gql.transport.requests.RequestsHTTPTransport( url="https://api.github.com/graphql", headers={"Authorization": "bearer %s" % parsed_args.access_token}, ) self._client = gql.Client(transport=transport) def execute(self, query: str) -> dict: """Runs a query.""" return self._client.execute(gql.gql(query)) # type: ignore def execute_and_paginate( self, query: str, path: tuple[str, ...], first_page: Optional[dict] = None, ) -> Generator[dict, None, None]: """Runs a query with pagination. Arguments: query: The GraphQL query template, which must have both 'cursor' and 'pagination' fields to fill in. The cursor should be part of the location query (with 'first'), and the pagination should be at the same level as nodes. path: A list of strings indicating the path to the nodes in the result. first_page: An optional object for the first page of results, which will otherwise automatically be collected. This exists for callers to optimize by collecting other data with the first page. """ format = {"cursor": "", "pagination": PAGINATION} count = 0 exp_count = None while True: if first_page: result = first_page first_page = None else: result = self.execute(query % format) # Follow the path to the nodes being paginated. node_parent = result for entry in path: node_parent = node_parent[entry] # Store the total count of responses. if not exp_count: exp_count = node_parent["totalCount"] # Yield each node individually. for node in node_parent["nodes"]: yield node count += 1 # Check for pagination, verifying the total count on exit. page_info = node_parent["pageInfo"] if not page_info["hasNextPage"]: assert exp_count == count, "exp %d != actual %d at path %s" % ( exp_count, count, path, ) return format["cursor"] = ' after: "%s"' % page_info["endCursor"] ================================================ FILE: github_tools/github_helpers_test.py ================================================ """Tests for github_helpers.py.""" __copyright__ = """ Part of the Carbon Language project, under the Apache License v2.0 with LLVM Exceptions. See /LICENSE for license information. SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception """ import unittest from unittest import mock import github_helpers _TEST_QUERY = """ query { top(login: "foo") { child(first: 100%(cursor)s) { nodes { login } %(pagination)s } } } """ _TEST_QUERY_PATH = ("top", "child") _EXP_QUERY_FIRST_PAGE = """ query { top(login: "foo") { child(first: 100) { nodes { login } pageInfo { hasNextPage endCursor } totalCount } } } """ _EXP_QUERY_SECOND_PAGE = """ query { top(login: "foo") { child(first: 100 after: "CURSOR") { nodes { login } pageInfo { hasNextPage endCursor } totalCount } } } """ class TestGithubHelpers(unittest.TestCase): def setUp(self): patcher = mock.patch.object( github_helpers.Client, "__init__", lambda self, parsed_args: None ) self.addCleanup(patcher.stop) patcher.start() self.client = github_helpers.Client(None) @staticmethod def mock_result(nodes, total_count=None, has_next_page=False): if total_count is None: total_count = len(nodes) end_cursor = None if has_next_page: end_cursor = "CURSOR" return { "top": { "child": { "nodes": nodes, "pageInfo": { "hasNextPage": has_next_page, "endCursor": end_cursor, }, "totalCount": total_count, } } } def test_execute_and_paginate_empty(self): self.client.execute = mock.MagicMock(return_value=self.mock_result([])) self.assertEqual( list( self.client.execute_and_paginate(_TEST_QUERY, _TEST_QUERY_PATH) ), [], ) self.client.execute.assert_called_once_with(_EXP_QUERY_FIRST_PAGE) def test_execute_and_paginate_one_page(self): self.client.execute = mock.MagicMock( return_value=self.mock_result(["foo", "bar", "baz"]) ) self.assertEqual( list( self.client.execute_and_paginate(_TEST_QUERY, _TEST_QUERY_PATH) ), ["foo", "bar", "baz"], ) self.client.execute.assert_called_once_with(_EXP_QUERY_FIRST_PAGE) def test_execute_and_paginate_one_page_count_mismatch(self): self.client.execute = mock.MagicMock( return_value=self.mock_result(["foo"], total_count=2), ) self.assertRaises( AssertionError, list, self.client.execute_and_paginate(_TEST_QUERY, _TEST_QUERY_PATH), ) self.client.execute.assert_called_once_with(_EXP_QUERY_FIRST_PAGE) def test_execute_and_paginate_two_page(self): def paging(query): if query == _EXP_QUERY_FIRST_PAGE: return self.mock_result( ["foo", "bar"], total_count=3, has_next_page=True ) elif query == _EXP_QUERY_SECOND_PAGE: return self.mock_result(["baz"], total_count="unused") else: raise ValueError("Bad query: %s" % query) self.client.execute = mock.MagicMock(side_effect=paging) self.assertEqual( list( self.client.execute_and_paginate(_TEST_QUERY, _TEST_QUERY_PATH) ), ["foo", "bar", "baz"], ) self.assertEqual(self.client.execute.call_count, 2) def test_execute_and_paginate_first_page_done(self): self.client.execute = mock.MagicMock() self.assertEqual( list( self.client.execute_and_paginate( _TEST_QUERY, _TEST_QUERY_PATH, first_page=self.mock_result(["foo"]), ) ), ["foo"], ) self.assertEqual(self.client.execute.call_count, 0) def test_execute_and_paginate_first_page_continue(self): self.client.execute = mock.MagicMock( return_value=self.mock_result(["bar"], total_count="unused") ) self.assertEqual( list( self.client.execute_and_paginate( _TEST_QUERY, _TEST_QUERY_PATH, first_page=self.mock_result( ["foo"], total_count=2, has_next_page=True ), ) ), ["foo", "bar"], ) self.client.execute.assert_called_once_with(_EXP_QUERY_SECOND_PAGE) if __name__ == "__main__": unittest.main() ================================================ FILE: github_tools/pr_comments.py ================================================ #!/usr/bin/env python3 """Figure out comments on a GitHub PR.""" __copyright__ = """ Part of the Carbon Language project, under the Apache License v2.0 with LLVM Exceptions. See /LICENSE for license information. SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception """ import argparse import datetime import hashlib import os import importlib.util import textwrap from typing import Any, Callable, Optional # Do some extra work to support direct runs. try: from github_tools import github_helpers except ImportError: github_helpers_spec = importlib.util.spec_from_file_location( "github_helpers", os.path.join(os.path.dirname(__file__), "github_helpers.py"), ) assert github_helpers_spec is not None github_helpers = importlib.util.module_from_spec(github_helpers_spec) github_helpers_spec.loader.exec_module(github_helpers) # type: ignore # The main query, into which other queries are composed. _QUERY = """ { repository(owner: "carbon-language", name: "%(repo)s") { pullRequest(number: %(pr_num)d) { author { login } createdAt title %(comments)s %(reviews)s %(review_threads)s } } } """ # Queries for comments on the PR. These are direct, non-review comments on the # PR. _QUERY_COMMENTS = """ comments(first: 100%(cursor)s) { nodes { author { login } body createdAt url } %(pagination)s } """ # Queries for reviews on the PR, which have a non-empty body if a review has # a summary comment. _QUERY_REVIEWS = """ reviews(first: 100%(cursor)s) { nodes { author { login } body createdAt url } %(pagination)s } """ # Queries for review threads on the PR. _QUERY_REVIEW_THREADS = """ reviewThreads(first: 100%(cursor)s) { nodes { comments(first: 100) { nodes { author { login } body createdAt originalPosition originalCommit { abbreviatedOid } path } } isResolved resolvedBy { createdAt login } } %(pagination)s } """ class _Comment: """A comment, either on a review thread or top-level on the PR.""" def __init__(self, author: str, timestamp: str, body: str): self.author = author self.timestamp = datetime.datetime.strptime( timestamp, "%Y-%m-%dT%H:%M:%SZ" ) self.body = body @staticmethod def from_raw_comment(raw_comment: dict) -> "_Comment": """Creates the comment from a raw comment dict.""" return _Comment( raw_comment["author"]["login"], raw_comment["createdAt"], raw_comment["body"], ) @staticmethod def _rewrap(content: str) -> str: """Rewraps a comment to fit in 80 columns with an indent.""" lines = [] for line in content.split("\n"): lines.extend( [ x for x in textwrap.wrap( line, width=80, initial_indent=" " * 4, subsequent_indent=" " * 4, ) ] ) return "\n".join(lines) def format(self, long: bool) -> str: """Formats the comment.""" if long: return "%s%s at %s:\n%s" % ( " " * 2, self.author, self.timestamp.strftime("%Y-%m-%d %H:%M"), self._rewrap(self.body), ) else: # Compact newlines down into pilcrows, leaving a space after. body = self.body.replace("\r", "").replace("\n", "¶ ") while "¶ ¶" in body: body = body.replace("¶ ¶", "¶¶") line = "%s%s: %s" % (" " * 2, self.author, body) return line if len(line) <= 80 else line[:77] + "..." class _PRComment(_Comment): """A comment on the top-level PR.""" def __init__(self, raw_comment: dict): super().__init__( raw_comment["author"]["login"], raw_comment["createdAt"], raw_comment["body"], ) self.url = raw_comment["url"] def __lt__(self, other: "_PRComment") -> bool: return self.timestamp < other.timestamp def format(self, long: bool) -> str: return "%s\n%s" % (self.url, super().format(long)) class _Thread: """A review thread on a line of code.""" def __init__(self, parsed_args: argparse.Namespace, thread: dict): self.is_resolved: bool = thread["isResolved"] comments = thread["comments"]["nodes"] first_comment = comments[0] self.line: int = first_comment["originalPosition"] self.path: str = first_comment["path"] # Link to the comment in the commit; GitHub features work better there # than in the conversation view. The diff_url allows viewing changes # since the comment, although the comment won't be visible there. template = ( "https://github.com/carbon-language/%(repo)s/pull/%(pr_num)s/" "files/%(oid)s%(head)s#diff-%(path_md5)s%(line_side)s%(line)s" ) # GitHub uses an md5 of the file's path for the link. path_md5 = hashlib.md5() path_md5.update(bytearray(self.path, "utf-8")) format_dict = { "head": "", "line_side": "R", "line": self.line, "oid": first_comment["originalCommit"]["abbreviatedOid"], "path_md5": path_md5.hexdigest(), "pr_num": parsed_args.pr_num, "repo": parsed_args.repo, } self.url: str = template % format_dict format_dict["head"] = "..HEAD" format_dict["line_side"] = "L" self.diff_url: str = template % format_dict self.comments = [ _Comment.from_raw_comment(comment) for comment in thread["comments"]["nodes"] ] if self.is_resolved: self.comments.append( _Comment( thread["resolvedBy"]["login"], thread["resolvedBy"]["createdAt"], "", ) ) def __lt__(self, other: "_Thread") -> bool: """Sort threads by line then timestamp.""" if self.line != other.line: return bool(self.line < other.line) return self.comments[0].timestamp < other.comments[0].timestamp def format(self, long: bool) -> str: """Formats the review thread with comments.""" lines = [ "%s\n - line %d; %s" % ( self.url, self.line, ("resolved" if self.is_resolved else "unresolved"), ) ] if self.diff_url: lines.append(" - diff: %s" % self.diff_url) for comment in self.comments: lines.append(comment.format(long)) return "\n".join(lines) def has_comment_from(self, comments_from: str) -> bool: """Returns true if comments has a comment from comments_from.""" for comment in self.comments: if comment.author == comments_from: return True return False def _parse_args(args: Optional[list[str]] = None) -> argparse.Namespace: """Parses command-line arguments and flags.""" parser = argparse.ArgumentParser(description="Lists comments on a PR.") parser.add_argument( "pr_num", metavar="PR#", type=int, help="The pull request to fetch comments from.", ) github_helpers.add_access_token_arg(parser, "repo") parser.add_argument( "--comments-after", metavar="LOGIN", help="Only print threads where the final comment is not from the given " "user. For example, use when looking for threads that you still need " "to respond to.", ) parser.add_argument( "--comments-from", metavar="LOGIN", help="Only print threads with comments from the given user. For " "example, use when looking for threads that you've commented on.", ) parser.add_argument( "--include-resolved", action="store_true", help="Whether to include resolved review threads. By default, only " "unresolved threads will be shown.", ) parser.add_argument( "--repo", choices=["carbon-lang"], default="carbon-lang", help="The Carbon repo to query. Defaults to %(default)s.", ) parser.add_argument( "--long", action="store_true", help="Prints long output, with the full comment.", ) return parser.parse_args(args=args) def _query( parsed_args: argparse.Namespace, field_name: Optional[str] = None ) -> str: """Returns a query for the passed field_name, or all by default.""" print(".", end="", flush=True) format = { "pr_num": parsed_args.pr_num, "repo": parsed_args.repo, "comments": "", "review_threads": "", "reviews": "", } if field_name: # Use a cursor for pagination of the field. if field_name == "comments": format["comments"] = _QUERY_COMMENTS elif field_name == "reviewThreads": format["review_threads"] = _QUERY_REVIEW_THREADS elif field_name == "reviews": format["reviews"] = _QUERY_REVIEWS else: raise ValueError("Unexpected field_name: %s" % field_name) else: # Fetch the first page of all fields. subformat = {"cursor": "", "pagination": github_helpers.PAGINATION} format["comments"] = _QUERY_COMMENTS % subformat format["review_threads"] = _QUERY_REVIEW_THREADS % subformat format["reviews"] = _QUERY_REVIEWS % subformat return _QUERY % format def _accumulate_pr_comment( parsed_args: argparse.Namespace, comments: list[_PRComment], raw_comment: dict, ) -> None: """Collects top-level comments and reviews.""" # Elide reviews that have no top-level comment body. if raw_comment["body"]: comments.append(_PRComment(raw_comment)) def _accumulate_thread( parsed_args: argparse.Namespace, threads_by_path: dict[str, list[_Thread]], raw_thread: dict, ) -> None: """Adds threads to threads_by_path for later sorting.""" thread = _Thread(parsed_args, raw_thread) # Optionally skip resolved threads. if not parsed_args.include_resolved and thread.is_resolved: return # Optionally skip threads where the given user isn't the last commenter. if ( parsed_args.comments_after and thread.comments[-1].author == parsed_args.comments_after ): return # Optionally skip threads where the given user hasn't commented. if parsed_args.comments_from and not thread.has_comment_from( parsed_args.comments_from ): return if thread.path not in threads_by_path: threads_by_path[thread.path] = [] threads_by_path[thread.path].append(thread) def _paginate( field_name: str, accumulator: Callable[[argparse.Namespace, Any, dict], None], parsed_args: argparse.Namespace, client: github_helpers.Client, main_result: dict, output: Any, ) -> None: """Paginates through the given field_name, accumulating results.""" query = _query(parsed_args, field_name=field_name) path = ("repository", "pullRequest", field_name) for node in client.execute_and_paginate( query, path, first_page=main_result ): accumulator(parsed_args, output, node) def _fetch_comments( parsed_args: argparse.Namespace, ) -> tuple[list[_PRComment], dict[str, list[_Thread]]]: """Fetches comments and review threads from GitHub.""" # Each _query call will print a '.' for progress. print( "Loading https://github.com/carbon-language/%s/pull/%d ..." % (parsed_args.repo, parsed_args.pr_num), end="", flush=True, ) client = github_helpers.Client(parsed_args) # Get the initial set of review threads, and print the PR summary. main_result = client.execute(_query(parsed_args)) pull_request = main_result["repository"]["pullRequest"] # Paginate comments, reviews, and review threads. comments: list[_PRComment] = [] _paginate( "comments", _accumulate_pr_comment, parsed_args, client, main_result, comments, ) # Combine reviews into comments for interleaving. _paginate( "reviews", _accumulate_pr_comment, parsed_args, client, main_result, comments, ) threads_by_path: dict[str, list[_Thread]] = {} _paginate( "reviewThreads", _accumulate_thread, parsed_args, client, main_result, threads_by_path, ) # Now that loading is done (no more progress indicators), print the header. print() pr_desc = _Comment( pull_request["author"]["login"], pull_request["createdAt"], pull_request["title"], ) print(pr_desc.format(parsed_args.long)) return comments, threads_by_path def main() -> None: parsed_args = _parse_args() comments, threads_by_path = _fetch_comments(parsed_args) for comment in sorted(comments): print() print(comment.format(parsed_args.long)) for path, threads in sorted(threads_by_path.items()): # Print a header for each path. print() print("=" * 80) print(path) print("=" * 80) for thread in sorted(threads): print() print(thread.format(parsed_args.long)) if __name__ == "__main__": main() ================================================ FILE: github_tools/pr_comments_test.py ================================================ """Tests for pr_comments.py.""" __copyright__ = """ Part of the Carbon Language project, under the Apache License v2.0 with LLVM Exceptions. See /LICENSE for license information. SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception """ import os import unittest from unittest import mock import github_helpers import pr_comments class TestPRComments(unittest.TestCase): def setUp(self): # Stub out the access token. os.environ[github_helpers._ENV_TOKEN] = "unused" def test_format_comment_short(self): created_at = "2001-02-03T04:05:06Z" self.assertEqual( pr_comments._Comment("author", created_at, "brief").format(False), " author: brief", ) self.assertEqual( pr_comments._Comment("author", created_at, "brief\nwrap").format( False ), " author: brief¶ wrap", ) self.assertEqual( pr_comments._Comment( "author", created_at, "brief\n\n\nwrap" ).format(False), " author: brief¶¶¶ wrap", ) self.assertEqual( pr_comments._Comment( "author", created_at, "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed " "do eiusmo", ).format(False), " author: Lorem ipsum dolor sit amet, consectetur adipiscing " "elit, sed do eiusmo", ) self.assertEqual( pr_comments._Comment( "author", created_at, "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed " "do eiusmod", ).format(False), " author: Lorem ipsum dolor sit amet, consectetur adipiscing " "elit, sed do eiu...", ) def test_format_comment_long(self): created_at = "2001-02-03T04:05:06Z" self.assertEqual( pr_comments._Comment("author", created_at, "brief").format(True), " author at 2001-02-03 04:05:\n brief", ) self.assertEqual( pr_comments._Comment("author", created_at, "brief\nwrap").format( True ), " author at 2001-02-03 04:05:\n brief\n wrap", ) body = ( "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed " "do eiusmod tempor incididunt ut labore et dolore magna " "aliqua.\n" "Ut enim ad minim veniam," ) self.assertEqual( pr_comments._Comment("author", created_at, body).format(True), " author at 2001-02-03 04:05:\n" " Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed " "do eiusmod\n" " tempor incididunt ut labore et dolore magna aliqua.\n" " Ut enim ad minim veniam,", ) @staticmethod def fake_thread(**kwargs): with mock.patch.dict(os.environ, {}): parsed_args = pr_comments._parse_args(["83"]) return pr_comments._Thread( parsed_args, TestPRComments.fake_thread_dict(**kwargs) ) @staticmethod def fake_thread_dict( is_resolved=False, path="foo.md", line=3, created_at="2001-02-03T04:05:06Z", ): thread_dict = { "isResolved": is_resolved, "comments": { "nodes": [ { "author": {"login": "author"}, "body": "comment", "createdAt": created_at, "originalCommit": {"abbreviatedOid": "abcdef"}, "originalPosition": line, "path": path, "url": "http://xyz", }, { "author": {"login": "other"}, "body": "reply", "createdAt": "2001-02-03T04:15:16Z", }, ], }, } if is_resolved: thread_dict["resolvedBy"] = { "login": "resolver", "createdAt": "2001-02-03T04:25:26Z", } return thread_dict def test_thread_format(self): self.assertEqual( self.fake_thread().format(False), "https://github.com/carbon-language/carbon-lang/pull/83/" "files/abcdef#diff-d8ca3b3d314d8209367af0eea2373b6fR3\n" " - line 3; unresolved\n" " - diff: https://github.com/carbon-language/carbon-lang/pull/83/" "files/abcdef..HEAD#diff-d8ca3b3d314d8209367af0eea2373b6fL3\n" " author: comment\n" " other: reply", ) self.assertEqual( self.fake_thread().format(True), "https://github.com/carbon-language/carbon-lang/pull/83/files/" "abcdef#diff-d8ca3b3d314d8209367af0eea2373b6fR3\n" " - line 3; unresolved\n" " - diff: https://github.com/carbon-language/carbon-lang/pull/83/" "files/abcdef..HEAD#diff-d8ca3b3d314d8209367af0eea2373b6fL3\n" " author at 2001-02-03 04:05:\n" " comment\n" " other at 2001-02-03 04:15:\n" " reply", ) self.assertEqual( self.fake_thread(is_resolved=True).format(False), "https://github.com/carbon-language/carbon-lang/pull/83/" "files/abcdef#diff-d8ca3b3d314d8209367af0eea2373b6fR3\n" " - line 3; resolved\n" " - diff: https://github.com/carbon-language/carbon-lang/pull/83/" "files/abcdef..HEAD#diff-d8ca3b3d314d8209367af0eea2373b6fL3\n" " author: comment\n" " other: reply\n" " resolver: ", ) self.assertEqual( self.fake_thread(is_resolved=True).format(True), "https://github.com/carbon-language/carbon-lang/pull/83/" "files/abcdef#diff-d8ca3b3d314d8209367af0eea2373b6fR3\n" " - line 3; resolved\n" " - diff: https://github.com/carbon-language/carbon-lang/pull/83/" "files/abcdef..HEAD#diff-d8ca3b3d314d8209367af0eea2373b6fL3\n" " author at 2001-02-03 04:05:\n" " comment\n" " other at 2001-02-03 04:15:\n" " reply\n" " resolver at 2001-02-03 04:25:\n" " ", ) def test_thread_lt(self): thread1 = self.fake_thread(line=2) thread2 = self.fake_thread() thread3 = self.fake_thread(created_at="2002-02-03T04:05:06Z") self.assertTrue(thread1 < thread2) self.assertFalse(thread2 < thread1) self.assertFalse(thread2 < thread2) self.assertTrue(thread2 < thread3) self.assertFalse(thread3 < thread2) def test_accumulate_thread(self): with mock.patch.dict(os.environ, {}): parsed_args = pr_comments._parse_args(["83"]) threads_by_path = {} review_threads = [ self.fake_thread_dict(line=2), self.fake_thread_dict(line=4), self.fake_thread_dict(path="other.md"), self.fake_thread_dict(), ] for thread in review_threads: pr_comments._accumulate_thread( parsed_args, threads_by_path, thread, ) self.assertEqual(sorted(threads_by_path.keys()), ["foo.md", "other.md"]) threads = sorted(threads_by_path["foo.md"]) self.assertEqual(len(threads), 3) self.assertEqual(threads[0].line, 2) self.assertEqual(threads[1].line, 3) self.assertEqual(threads[2].line, 4) self.assertEqual(len(threads_by_path["other.md"]), 1) @staticmethod def fake_pr_comment(**kwargs): return pr_comments._PRComment( TestPRComments.fake_pr_comment_dict(**kwargs) ) @staticmethod def fake_pr_comment_dict( body="comment", created_at="2001-02-03T04:05:06Z", ): pr_comment_dict = { "author": {"login": "author"}, "body": body, "createdAt": created_at, "url": "http://xyz", } return pr_comment_dict def test_pr_comment_format(self): self.assertEqual( self.fake_pr_comment().format(False), "http://xyz\n author: comment", ) self.assertEqual( self.fake_pr_comment().format(True), "http://xyz\n author at 2001-02-03 04:05:\n comment", ) def test_pr_comment_lt(self): pr_comment1 = self.fake_pr_comment() pr_comment2 = self.fake_pr_comment(created_at="2002-02-03T04:05:06Z") self.assertTrue(pr_comment1 < pr_comment2) self.assertFalse(pr_comment2 < pr_comment1) self.assertFalse(pr_comment2 < pr_comment2) def test_accumulate_pr_comment(self): with mock.patch.dict(os.environ, {}): parsed_args = pr_comments._parse_args(["83"]) raw_comments = [ self.fake_pr_comment_dict(body="x"), self.fake_pr_comment_dict(body=""), self.fake_pr_comment_dict( body="y", created_at="2000-02-03T04:05:06Z" ), self.fake_pr_comment_dict( body="z", created_at="2002-02-03T04:05:06Z" ), ] comments = [] for raw_comment in raw_comments: pr_comments._accumulate_pr_comment( parsed_args, comments, raw_comment ) comments.sort() self.assertEqual(len(comments), 3) self.assertEqual(comments[0].body, "y") self.assertEqual(comments[1].body, "x") self.assertEqual(comments[2].body, "z") if __name__ == "__main__": unittest.main() ================================================ FILE: github_tools/requirements.in ================================================ # Part of the Carbon Language project, under the Apache License v2.0 with LLVM # Exceptions. See /LICENSE for license information. # SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception # Python dependencies, consumed by /WORKSPACE. gql >= 2.0.0, < 3.0.0 ================================================ FILE: github_tools/requirements.txt ================================================ # # This file is autogenerated by pip-compile with Python 3.11 # by the following command: # # bazel run //:requirements.update # certifi==2024.7.4 \ --hash=sha256:5a1e7645bc0ec61a09e26c36f6106dd4cf40c6db3a1fb6352b0244e7fb057c7b \ --hash=sha256:c198e21b1289c2ab85ee4e67bb4b4ef3ead0892059901a8d5b622f24a1101e90 # via requests charset-normalizer==3.3.2 \ --hash=sha256:06435b539f889b1f6f4ac1758871aae42dc3a8c0e24ac9e60c2384973ad73027 \ --hash=sha256:06a81e93cd441c56a9b65d8e1d043daeb97a3d0856d177d5c90ba85acb3db087 \ --hash=sha256:0a55554a2fa0d408816b3b5cedf0045f4b8e1a6065aec45849de2d6f3f8e9786 \ --hash=sha256:0b2b64d2bb6d3fb9112bafa732def486049e63de9618b5843bcdd081d8144cd8 \ --hash=sha256:10955842570876604d404661fbccbc9c7e684caf432c09c715ec38fbae45ae09 \ --hash=sha256:122c7fa62b130ed55f8f285bfd56d5f4b4a5b503609d181f9ad85e55c89f4185 \ --hash=sha256:1ceae2f17a9c33cb48e3263960dc5fc8005351ee19db217e9b1bb15d28c02574 \ --hash=sha256:1d3193f4a680c64b4b6a9115943538edb896edc190f0b222e73761716519268e \ --hash=sha256:1f79682fbe303db92bc2b1136016a38a42e835d932bab5b3b1bfcfbf0640e519 \ --hash=sha256:2127566c664442652f024c837091890cb1942c30937add288223dc895793f898 \ --hash=sha256:22afcb9f253dac0696b5a4be4a1c0f8762f8239e21b99680099abd9b2b1b2269 \ --hash=sha256:25baf083bf6f6b341f4121c2f3c548875ee6f5339300e08be3f2b2ba1721cdd3 \ --hash=sha256:2e81c7b9c8979ce92ed306c249d46894776a909505d8f5a4ba55b14206e3222f \ --hash=sha256:3287761bc4ee9e33561a7e058c72ac0938c4f57fe49a09eae428fd88aafe7bb6 \ --hash=sha256:34d1c8da1e78d2e001f363791c98a272bb734000fcef47a491c1e3b0505657a8 \ --hash=sha256:37e55c8e51c236f95b033f6fb391d7d7970ba5fe7ff453dad675e88cf303377a \ --hash=sha256:3d47fa203a7bd9c5b6cee4736ee84ca03b8ef23193c0d1ca99b5089f72645c73 \ --hash=sha256:3e4d1f6587322d2788836a99c69062fbb091331ec940e02d12d179c1d53e25fc \ --hash=sha256:42cb296636fcc8b0644486d15c12376cb9fa75443e00fb25de0b8602e64c1714 \ --hash=sha256:45485e01ff4d3630ec0d9617310448a8702f70e9c01906b0d0118bdf9d124cf2 \ --hash=sha256:4a78b2b446bd7c934f5dcedc588903fb2f5eec172f3d29e52a9096a43722adfc \ --hash=sha256:4ab2fe47fae9e0f9dee8c04187ce5d09f48eabe611be8259444906793ab7cbce \ --hash=sha256:4d0d1650369165a14e14e1e47b372cfcb31d6ab44e6e33cb2d4e57265290044d \ --hash=sha256:549a3a73da901d5bc3ce8d24e0600d1fa85524c10287f6004fbab87672bf3e1e \ --hash=sha256:55086ee1064215781fff39a1af09518bc9255b50d6333f2e4c74ca09fac6a8f6 \ --hash=sha256:572c3763a264ba47b3cf708a44ce965d98555f618ca42c926a9c1616d8f34269 \ --hash=sha256:573f6eac48f4769d667c4442081b1794f52919e7edada77495aaed9236d13a96 \ --hash=sha256:5b4c145409bef602a690e7cfad0a15a55c13320ff7a3ad7ca59c13bb8ba4d45d \ --hash=sha256:6463effa3186ea09411d50efc7d85360b38d5f09b870c48e4600f63af490e56a \ --hash=sha256:65f6f63034100ead094b8744b3b97965785388f308a64cf8d7c34f2f2e5be0c4 \ --hash=sha256:663946639d296df6a2bb2aa51b60a2454ca1cb29835324c640dafb5ff2131a77 \ --hash=sha256:6897af51655e3691ff853668779c7bad41579facacf5fd7253b0133308cf000d \ --hash=sha256:68d1f8a9e9e37c1223b656399be5d6b448dea850bed7d0f87a8311f1ff3dabb0 \ --hash=sha256:6ac7ffc7ad6d040517be39eb591cac5ff87416c2537df6ba3cba3bae290c0fed \ --hash=sha256:6b3251890fff30ee142c44144871185dbe13b11bab478a88887a639655be1068 \ --hash=sha256:6c4caeef8fa63d06bd437cd4bdcf3ffefe6738fb1b25951440d80dc7df8c03ac \ --hash=sha256:6ef1d82a3af9d3eecdba2321dc1b3c238245d890843e040e41e470ffa64c3e25 \ --hash=sha256:753f10e867343b4511128c6ed8c82f7bec3bd026875576dfd88483c5c73b2fd8 \ --hash=sha256:7cd13a2e3ddeed6913a65e66e94b51d80a041145a026c27e6bb76c31a853c6ab \ --hash=sha256:7ed9e526742851e8d5cc9e6cf41427dfc6068d4f5a3bb03659444b4cabf6bc26 \ --hash=sha256:7f04c839ed0b6b98b1a7501a002144b76c18fb1c1850c8b98d458ac269e26ed2 \ --hash=sha256:802fe99cca7457642125a8a88a084cef28ff0cf9407060f7b93dca5aa25480db \ --hash=sha256:80402cd6ee291dcb72644d6eac93785fe2c8b9cb30893c1af5b8fdd753b9d40f \ --hash=sha256:8465322196c8b4d7ab6d1e049e4c5cb460d0394da4a27d23cc242fbf0034b6b5 \ --hash=sha256:86216b5cee4b06df986d214f664305142d9c76df9b6512be2738aa72a2048f99 \ --hash=sha256:87d1351268731db79e0f8e745d92493ee2841c974128ef629dc518b937d9194c \ --hash=sha256:8bdb58ff7ba23002a4c5808d608e4e6c687175724f54a5dade5fa8c67b604e4d \ --hash=sha256:8c622a5fe39a48f78944a87d4fb8a53ee07344641b0562c540d840748571b811 \ --hash=sha256:8d756e44e94489e49571086ef83b2bb8ce311e730092d2c34ca8f7d925cb20aa \ --hash=sha256:8f4a014bc36d3c57402e2977dada34f9c12300af536839dc38c0beab8878f38a \ --hash=sha256:9063e24fdb1e498ab71cb7419e24622516c4a04476b17a2dab57e8baa30d6e03 \ --hash=sha256:90d558489962fd4918143277a773316e56c72da56ec7aa3dc3dbbe20fdfed15b \ --hash=sha256:923c0c831b7cfcb071580d3f46c4baf50f174be571576556269530f4bbd79d04 \ --hash=sha256:95f2a5796329323b8f0512e09dbb7a1860c46a39da62ecb2324f116fa8fdc85c \ --hash=sha256:96b02a3dc4381e5494fad39be677abcb5e6634bf7b4fa83a6dd3112607547001 \ --hash=sha256:9f96df6923e21816da7e0ad3fd47dd8f94b2a5ce594e00677c0013018b813458 \ --hash=sha256:a10af20b82360ab00827f916a6058451b723b4e65030c5a18577c8b2de5b3389 \ --hash=sha256:a50aebfa173e157099939b17f18600f72f84eed3049e743b68ad15bd69b6bf99 \ --hash=sha256:a981a536974bbc7a512cf44ed14938cf01030a99e9b3a06dd59578882f06f985 \ --hash=sha256:a9a8e9031d613fd2009c182b69c7b2c1ef8239a0efb1df3f7c8da66d5dd3d537 \ --hash=sha256:ae5f4161f18c61806f411a13b0310bea87f987c7d2ecdbdaad0e94eb2e404238 \ --hash=sha256:aed38f6e4fb3f5d6bf81bfa990a07806be9d83cf7bacef998ab1a9bd660a581f \ --hash=sha256:b01b88d45a6fcb69667cd6d2f7a9aeb4bf53760d7fc536bf679ec94fe9f3ff3d \ --hash=sha256:b261ccdec7821281dade748d088bb6e9b69e6d15b30652b74cbbac25e280b796 \ --hash=sha256:b2b0a0c0517616b6869869f8c581d4eb2dd83a4d79e0ebcb7d373ef9956aeb0a \ --hash=sha256:b4a23f61ce87adf89be746c8a8974fe1c823c891d8f86eb218bb957c924bb143 \ --hash=sha256:bd8f7df7d12c2db9fab40bdd87a7c09b1530128315d047a086fa3ae3435cb3a8 \ --hash=sha256:beb58fe5cdb101e3a055192ac291b7a21e3b7ef4f67fa1d74e331a7f2124341c \ --hash=sha256:c002b4ffc0be611f0d9da932eb0f704fe2602a9a949d1f738e4c34c75b0863d5 \ --hash=sha256:c083af607d2515612056a31f0a8d9e0fcb5876b7bfc0abad3ecd275bc4ebc2d5 \ --hash=sha256:c180f51afb394e165eafe4ac2936a14bee3eb10debc9d9e4db8958fe36afe711 \ --hash=sha256:c235ebd9baae02f1b77bcea61bce332cb4331dc3617d254df3323aa01ab47bd4 \ --hash=sha256:cd70574b12bb8a4d2aaa0094515df2463cb429d8536cfb6c7ce983246983e5a6 \ --hash=sha256:d0eccceffcb53201b5bfebb52600a5fb483a20b61da9dbc885f8b103cbe7598c \ --hash=sha256:d965bba47ddeec8cd560687584e88cf699fd28f192ceb452d1d7ee807c5597b7 \ --hash=sha256:db364eca23f876da6f9e16c9da0df51aa4f104a972735574842618b8c6d999d4 \ --hash=sha256:ddbb2551d7e0102e7252db79ba445cdab71b26640817ab1e3e3648dad515003b \ --hash=sha256:deb6be0ac38ece9ba87dea880e438f25ca3eddfac8b002a2ec3d9183a454e8ae \ --hash=sha256:e06ed3eb3218bc64786f7db41917d4e686cc4856944f53d5bdf83a6884432e12 \ --hash=sha256:e27ad930a842b4c5eb8ac0016b0a54f5aebbe679340c26101df33424142c143c \ --hash=sha256:e537484df0d8f426ce2afb2d0f8e1c3d0b114b83f8850e5f2fbea0e797bd82ae \ --hash=sha256:eb00ed941194665c332bf8e078baf037d6c35d7c4f3102ea2d4f16ca94a26dc8 \ --hash=sha256:eb6904c354526e758fda7167b33005998fb68c46fbc10e013ca97f21ca5c8887 \ --hash=sha256:eb8821e09e916165e160797a6c17edda0679379a4be5c716c260e836e122f54b \ --hash=sha256:efcb3f6676480691518c177e3b465bcddf57cea040302f9f4e6e191af91174d4 \ --hash=sha256:f27273b60488abe721a075bcca6d7f3964f9f6f067c8c4c605743023d7d3944f \ --hash=sha256:f30c3cb33b24454a82faecaf01b19c18562b1e89558fb6c56de4d9118a032fd5 \ --hash=sha256:fb69256e180cb6c8a894fee62b3afebae785babc1ee98b81cdf68bbca1987f33 \ --hash=sha256:fd1abc0d89e30cc4e02e4064dc67fcc51bd941eb395c502aac3ec19fab46b519 \ --hash=sha256:ff8fa367d09b717b2a17a052544193ad76cd49979c805768879cb63d9ca50561 # via requests gql==2.0.0 \ --hash=sha256:35032ddd4bfe6b8f3169f806b022168932385d751eacc5c5f7122e0b3f4d6b88 \ --hash=sha256:fe8d3a08047f77362ddfcfddba7cae377da2dd66f5e61c59820419c9283d4fb5 # via -r requirements.in graphql-core==2.3.2 \ --hash=sha256:44c9bac4514e5e30c5a595fac8e3c76c1975cae14db215e8174c7fe995825bad \ --hash=sha256:aac46a9ac524c9855910c14c48fc5d60474def7f99fd10245e76608eba7af746 # via gql idna==3.7 \ --hash=sha256:028ff3aadf0609c1fd278d8ea3089299412a7a8b9bd005dd08b9f8285bcb5cfc \ --hash=sha256:82fee1fc78add43492d3a1898bfa6d8a904cc97d8427f683ed8e798d07761aa0 # via requests promise==2.3 \ --hash=sha256:dfd18337c523ba4b6a58801c164c1904a9d4d1b1747c7d5dbf45b693a49d93d0 # via # gql # graphql-core requests==2.32.4 \ --hash=sha256:27babd3cda2a6d50b30443204ee89830707d396671944c998b5975b031ac2b2c \ --hash=sha256:27d0316682c8a29834d3264820024b62a36942083d52caf2f14c0591336d3422 # via gql rx==1.6.3 \ --hash=sha256:ca71b65d0fc0603a3b5cfaa9e33f5ba81e4aae10a58491133595088d7734b2da # via graphql-core six==1.16.0 \ --hash=sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926 \ --hash=sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254 # via # gql # graphql-core # promise urllib3==2.6.3 \ --hash=sha256:1b62b6884944a57dbe321509ab94fd4d3b307075e0c2eae991ac71ee15ad38ed \ --hash=sha256:bf272323e553dfb2e87d9bfd225ca7b0f467b919d7bbd355436d3fd37cb0acd4 # via requests ================================================ FILE: proposals/README.md ================================================ # Proposals ## Proposal lists - [All proposals](https://github.com/carbon-language/carbon-lang/pulls?q=is%3Apr+label%3Aproposal) - [Accepted proposals](https://github.com/carbon-language/carbon-lang/pulls?q=is%3Apr+label%3A%22proposal%22+is%3Amerged) ## Directory structure This directory contains accepted proposals for the carbon-lang repository. For information about declined/deferred proposals, please view the proposal's original pull request. For accepted proposals, where `####` is the corresponding proposal's pull request: - `p####.md` will contain the main proposal text. - `p####` may be present as an optional subdirectory for related files (for example, images). ================================================ FILE: proposals/__init__.py ================================================ ================================================ FILE: proposals/p0024.md ================================================ # Generics goals [Pull request](https://github.com/carbon-language/carbon-lang/pull/24) ## Problem The "generics" feature of Carbon is a large design effort that needs to be broken up into manageable steps. The first thing we need is a high-level goals document: - to make sure we agree on the approach we are taking, - to clearly communicate expectations to contributors for future steps, - to encourage us to solve problems in the design in a consistent way, - to provide a yardstick for measuring different alternatives under consideration. ## Background The question of what a generics feature would look like in Carbon has been an ongoing discussion, with many alternative proposals. Of course there have been a number of use cases that we want to address with this feature, but over the course of this process we have discovered a number of desirable properties we would like to achieve with any solution. ## Proposal See the [generics goals document](/docs/design/generics/goals.md). ## Rationale The goals here well reflect Carbon's goals as applied to the specifics of the generics feature. ================================================ FILE: proposals/p0029.md ================================================ # Linear, rebase, and pull-request GitHub workflow [Pull request](https://github.com/carbon-language/carbon-lang/pull/29) ## Problem There are a variety of workflows that can be effectively used with both Git version control and GitHub. We should have a common and consistent workflow across as much of Carbon as possible. While some specific areas may end up needing specialized flows to suit their needs, these should be the exceptions and can be handled on a case-by-case basis when they arise. ## Background - Chapter 16 "Version Control and Branch Management" in the SWE book (_[Software Engineering at Google](https://www.amazon.com/Software-Engineering-Google-Lessons-Programming/dp/1492082791)_) - [Trunk Based Development](https://trunkbaseddevelopment.com/) - GitHub documentation on "[pull requests](https://help.github.com/en/github/collaborating-with-issues-and-pull-requests/about-pull-requests)" - [Configuration of their merges](https://help.github.com/en/github/administering-a-repository/configuring-pull-request-merges) - [Protected branches](https://help.github.com/en/github/administering-a-repository/about-protected-branches) ## Proposal Carbon repositories should all follow a common [workflow](/docs/project/pull_request_workflow.md) for managing and landing changes. The document outlines the specifics of the proposed workflow as well as the core motivation. ### Rename the default git branch to `trunk` This achieves two goals: 1. Replaces the term `master`. This term, while only used in isolation in Git, [was used](https://mail.gnome.org/archives/desktop-devel-list/2019-May/msg00066.html) in immediately preceding and related systems as part of extremely problematic "master/slave" terminology. That background associates the term with unacceptable historical and cultural meanings. The intent of those using or adopting the term isn't relevant to this association. The less overtly problematic term being isolated from the rest doesn't erase its history, and doesn't completely avoid painful associations. 2. It directly anchors and reinforces contributors on the trunk-based workflow. ### Longer discussion of linear history While Git can effectively bisect through complex history graphs, this is significantly harder than bisecting across linear history. Especially for any part of Carbon involving code, we expect bisection to be a pervasive tool and we can make it simpler and more effective by forcing a linear history. A linear history also makes it much easier to ask questions about whether a particular change has landed yet, or when a bug was introduced. For some people, releases are more simply understood as branching from a specific snapshot of the linear history. While tools like `git log` can provide similar functionality, it is less trivially understood. Continuous integration is simplified for many of the same reasons as bisection: the set of potential deltas is reduced to a linear sequence. Reverting to green becomes easier to understand, and testing each incremental commit has a single obvious interpretation. Requiring linear history also incentivises incremental development that is committed early to the main branch. This, in essence, ensures a single source of truth model even with the distributed version control system. Because works-in-progress are required to be rebased, they tend to merge early and often rather than forming long-lived development branches. This helps reduce the total merge resolution and testing costs as a project scales. For more details about the advantages of using a single source of truth, see the full text of the "Version Control and Branch Management" chapter in the SWE book. One concern with linear history when rebasing a sequence of changes and merging them is that the pull request associated with that sequence might not be obvious from the main branch commit log. However, there is enough information in the repository to establish the relationship, and GitHub's UI surfaces the pull request on each commit in the series. ## Alternatives considered ### LLVM model LLVM allows directly pushing/submitting to their "trunk" with post-commit review. LLVM enforces linear history for day-to-day development. Merge commits are allowed for rare events like contributing a new subproject. Advantages: - Still has linear history. - Incentivizes squashing for continuous integration and bisection. - Very low overhead for fixing trivial mistakes. Disadvantages: - Creates extremely bad incentives around code review. - Lots of patches don't get pre-commit review, even if they would benefit from it. - Very experienced contributors are much better at avoiding pre-commit review, so are rarely blocked waiting on review. - Leads to the most experienced members of the community not doing enough code reviews, or being timely enough in code reviews. - Lots of patches submitted with post-commit review are never reviewed in practice unless they break something. - UI and basic support for code reviews entirely focused on pull requests. ### Fork and merge model Classically, Git and GitHub support merging entire complex graphs of development. Advantages: - Mostly supported by pull requests, so still able to use much of that functionality. - Supports a model in which contributors do not communicate and can each develop a local, decentralized fork while still achieving overall reconciliation. - Can model much more complex history of code evolution faithfully in the tooling. - Most of the time these aren't so complex that they create problems for humans. Disadvantages: - History is harder for humans to understand and reason about in complex cases. - Bisection and continuous integration are more complex. - May create difficulty for continuous integration against mainline, because unclear what "order" they should be applied / explored. While there are technical approaches to address this, they don't seem to eliminate the complexity, merely provide a clear set of mechanics for handling it. - Reduces incentives to land small, incremental changes by allowing non-linearity to reduce the effort required for large and/or complex merges. - Makes review of the main branch's history harder due to non-linearity. ### Fork and merge, but branches can only have linear history Imagine a fork and merge model, but PRs can only have linear history. That is, branching and merging within a PR, or merging `trunk` into PR is not allowed. In this model, the only merge commits are merges of PRs into `trunk`. PRs themselves don’t contain merge commits. Advantages: - Mostly supported by GitHub pull requests, so still able to use much of that functionality. - Restricts non-linearity of history. The only non-linearity that is left is merge commits on the trunk branch. PRs themselves can’t contain merge commits. Disadvantages: - Requires a custom presubmit on GitHub that checks linearity of a PR. - The disadvantages of the fork and merge strategy regarding the complexity of history remain, but to a smaller degree, since non-linearity is restricted. ## Rationale - Easy to follow single source of truth will help foster an open and inclusive community. - Review requirements and focus on small, incremental changes match well established engineering practices for ensuring the project and codebase scale up both in size and time dimensions. - Linear history seems easier for humans to reason about. ================================================ FILE: proposals/p0042.md ================================================ # Create code review guidelines [Pull request](https://github.com/carbon-language/carbon-lang/pull/42) ## Problem Carbon should ensure that all checked-in changes to the repository are properly code reviewed, and that the process for code review is effective across a number of dimensions: - Ensure high quality of code, documentation, and other artifacts. We consider these all "code reviews" regardless of whether the final artefact is "code" in a technical sense. - Encourage broad participation and contribution to the community through code reviews. - Ensure code reviews are inclusive, respectful, and welcoming. - Have clear, discoverable, and mechanically enforced (where possible) rules for who _can_, who _should_, and who _must_ review any particular change. ## Background General code review: - Chapter 9 "Code Review" in _[Software Engineering at Google](https://www.amazon.com/Software-Engineering-Google-Lessons-Programming/dp/1492082791)_ - Chapter 21 "Collaborative Construction" in _[Code Complete: A Practical Handbook of Software Construction](https://www.amazon.com/Code-Complete-Practical-Handbook-Construction/dp/0735619670/)_ - [Respectful Code Reviews (Chromium)](https://chromium.googlesource.com/chromium/src/+/master/docs/cr_respect.md) - [Compassionate--Yet Candid--Code Reviews (video)](https://youtu.be/Ea8EiIPZvh0) - [The Standard of Code Review](https://google.github.io/eng-practices/review/reviewer/standard.html) - [How We Do Code Review](https://devblogs.microsoft.com/appcenter/how-the-visual-studio-mobile-center-team-does-code-review/) - [Code Reviewing in the Trenches: Understanding Challenges, Best Practices and Tool Needs](https://www.microsoft.com/en-us/research/wp-content/uploads/2016/05/MS-Code-Review-Tech-Report-MSR-TR-2016-27.pdf) - [The Importance of Code Reviews](https://www.sitepoint.com/the-importance-of-code-reviews/) - [10 Reasons Why Code Reviews Make Better Code and Better Teams](https://simpleprogrammer.com/why-code-reviews-make-better-code-teams/) - [Wikipedia article on code review](https://en.wikipedia.org/wiki/Code_review) - [Expectations, Outcomes, and Challenges of Modern Code Review](https://sback.it/publications/icse2013.pdf) Specific GitHub tooling: - [About code owners](https://help.github.com/en/github/creating-cloning-and-archiving-repositories/about-code-owners) - [Reviewing changes in pull requests](https://help.github.com/en/github/collaborating-with-issues-and-pull-requests/reviewing-changes-in-pull-requests) ## Proposal Add a [code review guide](/docs/project/code_review.md) to the project, and reference it from our contributing guide and pull request workflow documentation. Also create initial `CODEOWNERS` files in both this repository and the toolchain repository based on current review activity and team roles. These are really only suggested as an initial guess and should likely be iterated frequently as more people join and begin contributing. The `carbon-lang` repository file is included directly, and carbon-language/carbon-toolchain#1 updates the `carbon-toolchain` repository. ## Alternatives considered ### Post-commit review Some projects, such as LLVM, use _post-commit_ review for some changes. This has both advantages and disadvantages. Advantages: - Enables sustaining velocity in the face of high latency in code review. - Little need to rebase or resolve merge conflicts due to immediately landing patches even while under review. - Cross-developer dependencies don't create challenges. - Optimizes commit velocity amongst a small group of developers with extensive experience working together and minimal review comments on others' code. Disadvantages: - Does not meaningfully scale beyond small group of developers with preexisting shared understanding of desired form of code. - Relies on relative infrequency of needing code review comments. - Developers largely need to be well aligned and reliably writing code others would already approve. - Disincentivizes code review relative to writing code. - This effect is strong enough to create a significant fraction of commits that simply see no review in the LLVM community. - Creates significant barriers for new contributors. - In practice, existing contributors are much more likely to be able to accurately create post-commit review passing changes than new contributors. - Because of this, new contributors will have a very difficult time joining the community. - This has specifically been cited by people joining the LLVM community. ### Skipping review when no functionality is changed (NFC commits) Another practice popular in the LLVM community is to skip pre-commit review for changes for which "no functionality changes" or NFC commits. Common examples are reformatting or basic code cleanup. The idea is that these are exceedingly lower risk compared changes to functionality. Advantages: - Can avoid waiting for a review on trivial cleanups and refactorings. - May be especially useful as they tend to be merge conflict prone and likely to be lead-ups to changes sent out for review. - The advantage is lessened when using stacked reviews to parallelize them. - Avoid spending reviewer time on more trivial changes. - Unclear how much time this is as NFC changes are typically relatively fast to review due to their nature. Deciding "did it change behavior?" is easier than deciding "given that it changes behavior, is the change good?". Disadvantages: - No concrete and objective rubric for whether or not a change is NFC, and whether it perhaps is significant enough to still warrant review. - Debating this adds a new cost to the entire process. - In some cases, deciding whether a change is NFC roughly requires a code review, making it pointless to skip the code review. - A specific utility of code review is to discover when something _unexpected_ happens. The fact that the author _believes_ a change is NFC doesn't address this utility as the unexpected thing may be the functionality changed. - Loses the knowledge sharing benefit of code review for cleanups and refactorings. - Especially unfortunate as these are exactly the kinds of changes often recommend for people starting to get familiar with a project. - Fails to ensure consistency or ease of understanding of the code. - Despite not changing functionality, a change may decrease how easily understood the code is or may move it to be inconsistent with the wider codebase. - Avoiding these two things are some of the primary goals of code review. ## Rationale This proposal contains the right goals for the code review process in light of our project goals, and the proposal is well-tailored to achieve them. Specifically: - Ensure high quality of code, documentation, and other artifacts. We consider these all "code reviews" regardless of whether the final artifact is "code" in a technical sense. - Encourage broad participation and contribution to the community through code reviews. - Ensure code reviews are inclusive, respectful, and welcoming. - Have clear, discoverable, and mechanically enforced (where possible) rules for who can, who should, and who must review any particular change. We want pre-commit rather than post-commit review, and we want all changes to go through review. These guidelines are consistent with standard code review best practice, including what’s described in the cited sources. ================================================ FILE: proposals/p0044.md ================================================ # Proposal tracking [Pull request](https://github.com/carbon-language/carbon-lang/pull/44) ## Table of contents - [Problem](#problem) - [Background](#background) - [Proposal](#proposal) - [Overview](#overview) - [Open question: Do we use a Google Docs-centric or GitHub Markdown-centric flow?](#open-question-do-we-use-a-google-docs-centric-or-github-markdown-centric-flow) - [Option: Google Docs-centric flow](#option-google-docs-centric-flow) - [Overview](#overview-1) - [Flow summary](#flow-summary) - [Advantages/Disadvantages](#advantagesdisadvantages) - [Option: GitHub Markdown-centric flow](#option-github-markdown-centric-flow) - [Overview](#overview-2) - [Flow summary](#flow-summary-1) - [Advantages/Disadvantages](#advantagesdisadvantages-1) - [Details](#details) - [Carbon language shared drive](#carbon-language-shared-drive) - [Main shared drive](#main-shared-drive) - [Proposals (shared folder)](#proposals-shared-folder) - [Proposal Archive (folder in shared drive)](#proposal-archive-folder-in-shared-drive) - [Google Docs proposal ACLs](#google-docs-proposal-acls) - [Markdown-specific questions](#markdown-specific-questions) - [Open question: Where should proposals be stored in GitHub?](#open-question-where-should-proposals-be-stored-in-github) - [Option: Proposal archive GitHub repository (carbon-proposals)](#option-proposal-archive-github-repository-carbon-proposals) - [Option: Store proposals in the repository they affect](#option-store-proposals-in-the-repository-they-affect) - [Open question: Should we push comments to focus on GitHub?](#open-question-should-we-push-comments-to-focus-on-github) - [Common principles](#common-principles) - [Option: Push high-level comments to Discourse Forums](#option-push-high-level-comments-to-discourse-forums) - [Option: Push high-level comments to GitHub](#option-push-high-level-comments-to-github) - [Option: Give no guidance, see what happens](#option-give-no-guidance-see-what-happens) - [Open question: Should there be a tracking issue?](#open-question-should-there-be-a-tracking-issue) - [Common principles](#common-principles-1) - [Option: Tracking issue for all proposals](#option-tracking-issue-for-all-proposals) - [Option: Don't require tracking issues](#option-dont-require-tracking-issues) - [Open question: Should declined/deferred proposals be committed?](#open-question-should-declineddeferred-proposals-be-committed) - [Common principles](#common-principles-2) - [Option: Do not commit declined/deferred proposals](#option-do-not-commit-declineddeferred-proposals) - [Option: Commit declined/deferred proposals](#option-commit-declineddeferred-proposals) - [Alternatives considered](#alternatives-considered) - [Use a shared drive for everything](#use-a-shared-drive-for-everything) - [Appendix](#appendix) - [General concern about multiple Markdown flavors](#general-concern-about-multiple-markdown-flavors) - [Google Docs vs GitHub comment flow comparison](#google-docs-vs-github-comment-flow-comparison) - [Comment clustering](#comment-clustering) - [Basic comments](#basic-comments) - [Suggesting edits](#suggesting-edits) - [Google Docs add-ons](#google-docs-add-ons) - [Docs to Markdown](#docs-to-markdown) - [Code blocks](#code-blocks) - [Advanced Find & Replace](#advanced-find--replace) - [Markdown editing](#markdown-editing) - [StackEdit](#stackedit) - [GitHub Markdown syntax highlighting](#github-markdown-syntax-highlighting) - [Rationale](#rationale) - [Rationale for using a GitHub markdown-centric flow](#rationale-for-using-a-github-markdown-centric-flow) - [Rationale for not requiring tracking issues](#rationale-for-not-requiring-tracking-issues) - [Rationale for not committing proposals that are declined or deferred](#rationale-for-not-committing-proposals-that-are-declined-or-deferred) - [Rationale for committing a proposal to the repository it affects](#rationale-for-committing-a-proposal-to-the-repository-it-affects) - [Rationale for pushing high-level comments to GitHub](#rationale-for-pushing-high-level-comments-to-github) - [Open questions](#open-questions) - [Do we use a Google Docs-centric or GitHub Markdown-centric flow?](#do-we-use-a-google-docs-centric-or-github-markdown-centric-flow) - [Where should proposals be stored in GitHub?](#where-should-proposals-be-stored-in-github) - [Should we push comments to focus on GitHub?](#should-we-push-comments-to-focus-on-github) - [Should there be a tracking issue?](#should-there-be-a-tracking-issue) - [Should declined/deferred proposals be committed?](#should-declineddeferred-proposals-be-committed) ## Problem How should we: - Draft proposals? - Store under review proposals? - Archive proposals? Each of these is a distinct, important step. We may be able to use the same storage for multiple steps. ## Background One thing that is _not_ under question is that we will have accepted proposals in Markdown. This is critical, as we are essentially only delaying when a proposal goes to Markdown. The evolution process expects that community contributors will author proposals for review by the core team. Things to consider are: - How easy is it for an author to write a proposal? - What if there are multiple contributors to a proposal? - How easy is it to comment on a proposal? - What about small typo fixes? - How do we prevent unwanted edits during the comment process? - How much work does it take to convert a proposal into a final doc edit? - How do we prevent edits after a decision is complete? In any situation, it's necessary that proposals be clearly contributed to Carbon (even if in a draft phase, even if never accepted), and have an appropriate license attached. Although the evolution document currently makes statements about how to manage proposals, changes in approval from this doc may affect the evolution process. Explicit wording changes aren't outlined here because they are assumed to be part of executing this proposal rather than the substance of this proposal. For reference, a version of this proposal is also available in GitHub (broken link: `https://github.com/carbon-language/carbon-proposals/blob/d1946fab824742b4857fa74ffc1b9fe9af37375d/proposals/p0001.md`). ## Proposal ### Overview This proposal presents two possible flows: one Google Docs-centric, one Markdown-centric. This is an open question. In either case, it's assumed that we will want a Carbon language shared drive for any Google Docs we may create, and a Proposal archive GitHub repository. ### Open question: Do we use a Google Docs-centric or GitHub Markdown-centric flow? **Decision:** GitHub Markdown-centric flow All documents will end up in Markdown. Some members of the core team have a stated preference for reviewing proposals in Google Docs, and some have a preference for Markdown. No other clear options exist. This would yield two possible flows: #### Option: Google Docs-centric flow ##### Overview - Draft proposals: Google Docs - Under review proposals: Google Docs - Archive proposals: Google Docs + PDF - Final format: Markdown ##### Flow summary For reviewing a proposal: - Authors create a Google Doc in the Proposals shared folder. - Authors may want to trim down edit access, leaving only comment access. - When ready for review, the authors share the Google Doc on Discourse Forums. - Community comments on the Google Doc. - To move for a decision, ownership is transferred to a review manager. - The review manager trims down all access to comment access. - When a decision is reached, the review manager updates the doc and moves it to the Proposal Archive folder in the Carbon language shared drive. - A PDF is saved to the carbon-proposals GitHub repository. - The author converts any long-term documentation portions of the doc to Markdown. If the proposal needs to be checked later to figure out why a decision was made: - Pragmatically, the PDF should have the same viewable content as the Google Doc, and so can be used. - The Google Doc's comments will be a mess to read, and so should likely be treated as inaccessible later. - The edit history of a Google Doc is only visible to users with edit access. Since most people will have comment-only or view-only access to reviewed docs, we should treat edit history as not accessible. ##### Advantages/Disadvantages Advantages: - Supports collaborative editing. - Commenting in Docs is a slightly better UI, even though the commenting workflows are equivalent. - Images can easily be embedded in the Google Doc, without needing separate files. Neutral: - Has an easier [suggested edit workflow](#suggesting-edits) than GitHub. - For commenters, this may be a pro because they can see their suggested edit as they make it. - For the author, this is a mixed bag: - When a suggestion is for multiple, fragmented edits, it's harder to read. - for example, "foo ~~bar ~~baaz bang~~wiz ~~bing" -> "foo baaz bangbing" - It's easier to accept changes. - It's harder to comment on changes, as the change is still visible and hiding the original state of the doc. - This is especially true when trying to respond to comments by others on a suggested edit, as you can't _both_ have a discussion _and_ apply/reject the edit. - It's harder to make alternative edits and/or reject changes, as it effectively resolves the comment thread and hinders further discussion. - For other readers, this is likely a con: it's harder to read the original state of the doc that's been suggested on, and it's not clear how the author will respond. Disadvantages: - Documents will go through at least three different file formats (Docs, PDF, Markdown). - Docs->Markdown conversion has limited tooling support. - Comment history will be lost on conversions. - Using different formats for proposals and documentation/specification creates additional work when pulling text into a proposal to suggest modifications, and when applying those modifications. - Any differences between the text in different formats will be difficult to notice, particularly if the author makes a change when converting long-term documentation from Docs to Markdown. - Difficult to manage access control on files. - Docs does not provide access to version history (desirable) without granting full edit access (undesirable). - Shared drives combine permissions for adding files to a folder and editing files. Permissions can also only be _added_, not _removed_, from individual docs. As a result, contributors who can create new proposals will also receive the ability to edit all proposals. - Docs will not automatically mirror permissions if we use two different sharing systems (shared drive and shared folder). - Difficult to archive documents as read-only. - If we set archived documents to be view-only, users won't be able to read comments. At that point, the PDF version that we plan to commit to the Proposal archive GitHub repository is sufficient, and we could delete the proposal. - If we set archived documents to allow commenting, then users can comment on and add suggested edits to archived documents. Only review managers could close suggested edits. This could confuse the history of the doc, and stop archives from looking read-only. - In any case, archived documents would not be editable, and so revision history could not be seen. - Managing comments over time is infeasible. - It's infeasible to point people to old comment threads, to suggest they read and engage if they disagree with the original request. - Even identifying _whether_ a comment led to a particular change is infeasible. - Comments vanish if the text they're on is removed. - There is no way to quickly check for unresponded comments: instead, authors will need to repeatedly crawl the doc. This is exacerbated by process advice that the commenter be left to decide whether to resolve comments, or slow-to-respond commenters. - No support for seeing deltas. - Only editors can see the delta. However, it should be expected that only the author is an editor. - Even if deltas were accessible, Docs delta support requires extra work on the part of a doc editor to set up markers in the version history. - Restricted support for extensibility. - Proposals frequently have duplicated/boilerplate text that the authors may need to rewrite in bulk, for example when renaming something. Google Docs has built-in regex search. However, regex replace with capture groups isn't directly supported, so users would need to learn and use Google Apps Script to do the replacement. - When URLs need to be replaced, Google Docs has _no_ support. Authors must audit every URL. - No low-friction support for formatted code blocks and inline code snippets. - [Extensions exist](#google-docs-add-ons); we'll probably want to recommend a specific extension to the community. - Intra-document links break frequently, just from moving headers. - It's also difficult to determine when an intra-document link is broken. There doesn't seem to be support (built-in or add-on) for identifying issues. - It's also not supported to search through links, so even if the author knows what they're breaking, they won't be able to find it. - Google Docs comment syntax is essentially text-only. - Syntax can interfere with "\*" insertion in code snippets due to bolding confusion. - May enhance Google-centric views of Carbon. - Swift and Rust use Markdown-centric flows. - Some companies may block Google Docs, because it's a file transfer service. - It's unclear how prevalent this would be. GitHub may also be blocked under similar security rules, but it's a requirement for participation whereas Google Docs is avoidable. - Ref: [[1]](https://community.cisco.com/t5/web-security/blocking-google-docs/td-p/3376518), [[2]](https://www.reddit.com/r/sysadmin/comments/2kwyhk/block_google_docs/), [[3]](https://mybroadband.co.za/forum/threads/google-drive-blocked-at-work-work-around.809132/) - Some individuals may refuse to use Google Docs over privacy (or other) concerns. - We've considered creating a GSuite domain for Carbon contributor accounts to help address this. However, it may not address everyone's concerns. #### Option: GitHub Markdown-centric flow ##### Overview - Draft proposals: Markdown, possibly in Google Docs (but not required) - Under review proposals: Markdown with review comments by way of GitHub - Archive proposals: Markdown - Final format: Markdown ##### Flow summary For reviewing a proposal: - Authors prepare a markdown doc using their favored tooling. - This could be [a WYSIWYG markdown editor](#markdown-editing), or Google docs for collaboration. - Authors create a pull request with the markdown doc, to the carbon-proposals GitHub repository. - Community comments on the pull request. - To move for a decision, no special action is taken. - When a decision is reached, the review manager ensures the markdown doc is updated appropriately and approves the pull request. If the proposal needs to be checked later to figure out why a decision was made: - The committed markdown represents the final state of the proposal. - The pull request may be viewed to read through comments, even after it is merged. - The pull request will have edit history publicly visible. - GitHub has a nice renderer for markdown diffs. ##### Advantages/Disadvantages Advantages: - No need to convert file formats. - Markdown reviews can be committed directly, putting all history in one place. - When dealing with a proposal that results in separate long-term documentation, any differences between proposal text and long-term text will be relatively easy to see in a diff. - Access control is straightforward, determined by location. - Referring to older comments is feasible. - There may be some cases where comments disappear in certain situations, but these problems should be avoidable. - The archival copy of a document will include an easy link to comment history. - Easy to see Markdown-formatted deltas in GitHub. - The style of all documents can be updated centrally by changing a style sheet. - Multiple comments may be sent together in a review. - Comments may also be sent individually as in Docs. - GitHub comment syntax supports Markdown, allowing code in comments. - Multi-author editing is possible by using a shared GitHub branch as a source. - This may still be less convenient than Google Docs flows, but multi-author proposals could still use Google Docs to collaborate on the Markdown. Neutral: - Has a more difficult [suggested edit workflow](#suggesting-edits) than Google Docs. - For commenters, this may be a con, as it takes a little more work to add a suggested edit. This may still lead to fewer suggested edits. - For the author, this is a mixed bag: - Whereas Google Docs may often see multiple, fragmented edits for the same sentence, commenters would be more likely to suggest them together (a pro). - It's seamless to comment on changes, as it follows the normal comment flow. - It's seamless to make alternative edits and/or reject changes. - The author may need to do extra reformatting/word wrapping after accepting a Markdown change, which Docs would handle internally. - For other readers, this is likely a pro: the current state of the doc remains visible. Disadvantages: - GitHub comments on pull requests can be difficult to find in certain situations. - Comments can disappear when a pull request is rebased and force-pushed. - Comments generally don't get tracked across revisions to a pull request. - If we disallow force-pushes on pull requests, they are probably okay most of the time. - Can't comment on the rendered Markdown, only the raw Markdown. - Images need to be stored separately from the main Markdown file. - Final documentation may or may not need the images; they may only be added to explain the proposal. that is, this may be extra work without later benefit. ## Details ### Carbon language shared drive #### Main shared drive A shared drive will be used as a nexus for any Google Docs materials. This mainly offers a central point for contributors to locate materials, as most will not be able to modify contents. A shared drive should be assumed to be present, regardless of whether we choose a Google Docs-centric flow: if we allow/encourage the use of Google Docs for collaborative editing in a Markdown-centric flow, we should still provide storage. Access controls: - Managers: a minimal set (chandlerc + review managers?) to prevent accidental edits. - _Not_ the entire core team, in order to discourage the core team from using privileges not accessible to the rest of the community (including sub-teams). - Content managers: the review manager group, so that they can archive proposals. - Contributors: none - does not provide sufficiently distinct access from content managers. - Commenters: full community - Viewers: none #### Proposals (shared folder) Community members need to be able to add proposals to the shared folder. This will be available as a shortcut from the main shared drive. In shared folders, edit access to the folder allows adding/moving/removing files, but doesn't grant control over the actual _file_, which is still owned by the individual creator. Unlike shared drives, proposal authors can remove edit access from individual files. Access controls: - Owner: chandlerc - Editors: full community - Commenters/viewers: none (non-public) #### Proposal Archive (folder in shared drive) The archive will be used to store reviewed proposals. This will mainly make comments on proposals available for future reference. To allow viewing comments, comment access _must_ be granted: Google Docs comments are not visible to users with view-only access. At the same time, this means contributors will still be able to comment on archived docs: this should be discouraged. Access controls: - Inherited from Carbon language shared drive. ### Google Docs proposal ACLs In this setup, pending proposals go into the Proposals shared folder, where they'll still have their own ACLs. Note that, in the Markdown-centric flow, this is generally not relevant: using Google Docs is optional and may often be skipped. Access controls: - Owner: proposal author (transferred to review manager, for proposals going to decision) - Editors: any collaborators - Commenters: community - Viewers: none ## Markdown-specific questions ### Open question: Where should proposals be stored in GitHub? **Decision**: Store proposals in the repository they affect #### Option: Proposal archive GitHub repository (carbon-proposals) In this approach, the review managers are presumed to be responsible for commits to the proposal archive. Access controls: - Commit privileges: review managers Advantages: - Easy to find pending proposals; just look at the carbon-proposals issue tracker. - All proposals will be uniquely numbered. - No need for a proposal label: everything's a proposal. Disadvantages: - Proposals affect other repositories; may miss opportunities to combine changes. - Harder to filter for proposals that are relevant to a specific repository. #### Option: Store proposals in the repository they affect In this approach, we would store proposals in the same repository as they affect. for example, a proposal about the spec would be stored in carbon-lang, while a proposal specific to the compiler would be in carbon-toolchains. If a proposal affected multiple repositories, we'd probably choose a single primary repository for the proposal. Specific to a GitHub Markdown-centric flow, we could additionally allow the proposal's pull request to include the specific proposed changes to documentation where appropriate, allowing for the author to avoid duplicating text in the proposal itself. We would likely still want a proposal document with a summary at a minimum, even for small proposals. The summary can link to the pull request for details, creating additional breadcrumbs for reading the full (with additional document edits) proposal. Rejected proposals should commit _only_ the proposal document. A link to the pull request in the proposal document should make it easier to research rejections. Alternatively, rejected proposals could have their pull request abandoned, but that would leave only the tracking issue as a breadcrumb for history. Access controls: - Commit privileges: normal repository access, possibly with review managers getting broad access in order to finalize proposals. Advantages: - Makes it easier to demonstrate the actual changes a proposal suggests making. - Reduces possible redundant work by the author of making changes in two places (the proposal, and affected documents). - Keeps discussion about the _proposal_ and discussion about the _proposed changes_ on a single review thread, for most cases. - Makes it easy to find most/all proposals relevant to a given repository. Disadvantages: - Proposals would need to be tracked separately per-repository. - This could also end up being a pro if we get a bunch of different repositories, as it may become easier to find relevant proposals for a given repository. It's only really a con for as long as we have few repositories (which may last long-term, as having many repositories may lead to other scaling problems). - Access controls are part of the parent repository, and so will be less restricted than if we had a separate proposals repository. - Proposals won't be uniquely numbered. - We'll need to refer to proposals with the repository, for example, `carbon-lang/456`. - Need to make sure we have a proposal label, to separate from non-proposal traffic. ### Open question: Should we push comments to focus on GitHub? **Decision**: Push high-level comments to GitHub #### Common principles - Proposals will be declared on Discourse Forums at important stages; for example, asking for input on ideas, RFC, decisions, etc. - Some discussion is expected to occur on the GitHub PR. #### Option: Push high-level comments to Discourse Forums We could push for high-level comments to be added on the same thread as the Discourse Forums RFC. Advantages: - Discourse Forums offer better interfaces for pure, non-code-comment discussion. - Email notifications are easier to parse with less threading, more use of quotes, and "In Reply To" automation. - More familiar for people familiar with mixed Discourse/GitHub workflows. - Both Rust and Swift use Discourse, and are closer to this option. - May also be better for people used to using email lists to discuss proposed changes. Disadvantages: - Leads contributors to two different places for comments - some high-level discussion will inevitably be in GitHub. - Contributors must read both Discourse Forums and GitHub to get context. #### Option: Push high-level comments to GitHub We could push for high-level comments to be added to the proposal PR, with other discussion. Advantages: - The GitHub PR becomes a single hub for conversation. - More familiar for people familiar with GitHub-only workflows. Disadvantages: - Discourse Forum topics cannot have "create" without "reply" permissions, so some high-level discussion will inevitably be in Discourse Forums. - We could address this by only allowing moderators to post RFCs, but that may be overly exclusive. - Email notifications include only the lines of code affected, not what is being replied to. This will generally make it infeasible to get context from emails. - Comment threads sometimes make it unclear what's being replied to. for example, (broken link: `https://github.com/carbon-language/carbon-proposals/pull/5#discussion_r423401993`) and (broken link: `https://github.com/carbon-language/carbon-proposals/pull/5/files/a51ff951561accfb4aee403d7add6e8e69009ce1#r423401993`) are equivalent, but the replied-to-comment is only visible in the file view. - This may be particularly visible as an issue if high-level discussions are often not line-specific. - Not clear what to do about resolving high-level discussion comment threads. - If comment threads are resolved, it's harder to read them, discouraging third-party comment. - If comment threads are not resolved, they may create a bunch of noise. As noted above, GitHub manages the file view better than the discussion view. - Mixed solutions will leave it to the author to choose the balance of issues. #### Option: Give no guidance, see what happens Rather than trying to guide high-level discussion to a particular medium, we could offer no guidance. Advantages: - Less policies, more freedom. - Discover what happens, switch back and forth over time based on individual contributor preferences. Disadvantages: - Advantages of a primary hub are discarded. Disadvantages of multiple hubs should be assumed to remain. ### Open question: Should there be a tracking issue? **Decision**: Don't require tracking issues #### Common principles - Discourse Forum topics are minimally used to announce when a decision is going to RFC, going to decision, and the decision once made. - The proposal's PR may be used for discussion of the proposal. #### Option: Tracking issue for all proposals In a workflow where there's always a tracking issue: 1. Create the tracking issue, for example #123. 2. Create the PR, for example #456, naming the proposal p0123.md after the tracking issue. 1. Use GitHub features to link #123 and #456. 3. Update the status in p0123.md and labels of #123 when progressing a proposal. 4. When a decision is made, create a new PR, for example #789, containing the decision p0123-decision.md. 1. This does not replace the Discourse Forum topic announcing a decision. 2. Use GitHub features to link #123 and #789. 3. Comments on the decision may go on the decision PR, similar to the proposal PR discussion. 5. Declined/deferred proposals may be committed or not; it doesn't matter. Advantages: - Easy to find the full decision in p0123-decision.md. - The PR to create the decision is clearly visible in the associated tracking issue. Disadvantages: - The tracking issue separates more state. #### Option: Don't require tracking issues In a workflow where there's no need for a tracking issue (although contributors may create them for bucketing work, they are non-essential): 1. Create the PR, for example #456, naming the proposal p0456.md. 2. Update the labels of #456 when progressing a proposal. 1. Don't bother putting the status in p0456.md: people should rely on the PR labels since it's in the same place. 3. When a decision is made, add it as a comment to #456. 1. This does not replace the Discourse Forum topic announcing a decision. 2. Comments on the decision should go in Discourse Forums. 3. The author is asked to link to the decision in p0456.md before the commit is approved. 4. If declined/deferred proposals are committed, it would be best to add a status in p0456.md before committing. Advantages: - Lighter weight process: no tracking issue, and no need to update status in p0456.md. Disadvantages: - Harder to store the decision in a way that clearly links it to the original proposal. - In particular, finding discussion about the decision is hard to resolve. Neither below ideas clearly improve on this, so preference to keep everything in Discourse Forum topic. - Could in theory have the full decision in p0456.md. Pro is it's easy to find, con is it makes it look more like the author's writing the decision. - Could keep storing p0456-decision.md. Pro is it's easy to find, con is the lack of association with #456 and extra file+PR. Not clear that storage offers enough independent value to justify. - Restricts where discussion about a decision should occur. ### Open question: Should declined/deferred proposals be committed? **Decision**: Do not commit declined/deferred proposals #### Common principles - Accepted proposals are always committed. - We may (or may not) commit decisions for any committed proposal (accepted or otherwise). - See notes in the above open question. #### Option: Do not commit declined/deferred proposals Under this approach, declined/deferred proposals are never committed. Instead, the PR is abandoned and we at most save a link to it. Note that GitHub does (at least to some extent) retain closed PRs, even those coming from a fork. Advantages: - The proposals directory remains a list of only accepted proposals. - No need to spend effort saving declined/deferred proposals. \ Disadvantages: - Lose an easy way to check declined/deferred proposals for history. - More reliance on searching forums for history. #### Option: Commit declined/deferred proposals Under this approach, declined/deferred proposals are committed. Advantages: - Easy to skim through declined/deferred proposals. Disadvantages: - Finding accepted proposals may become more difficult. - Could put declined/deferred proposals in a different directory. - Requires a little more effort in order to save declined/deferred proposals. ## Alternatives considered ### Use a shared drive for everything Instead of adding a shared folder for proposals, we could instead use a shared drive for everything. However, this puts us in a bad situation for taking in new proposals. We would need to choose between: - Allowing **all** community member edit access to **all** proposals. - Requiring authors ask a review manager to create a blank proposal for them to edit. Neither of these feel like great situations - they are either overly-broad or overly-restrictive sharing, neither approximating what we actually would want, which is for anybody in the community to easily create proposals. ## Appendix ### General concern about multiple Markdown flavors Different markdown implementations have subtly different rendering rules for the same input. for example, per [CommonMark Spec](https://spec.commonmark.org/0.29/#), table syntax is not specified, although [GitHub uses a table extension](https://guides.github.com/features/mastering-markdown/). However, we do plan on using GitHub consistently; this only stands to confuse users of other Markdown systems. Choosing the Google Docs-centric flow does not eliminate this issue, since proposals will be archived in Markdown either way. ### Google Docs vs GitHub comment flow comparison #### Comment clustering Google Docs only allows sending comments individually. GitHub supports either sending comments individually or clustering multiple comments together, as in a review. #### Basic comments [Google Docs](https://support.google.com/docs/answer/65129?co=GENIE.Platform%3DDesktop&hl=en): 1. Follow the link to the doc 2. Select text to comment on 3. Click on "+" to add a comment (or use keyboard shortcut) 4. Enter text 5. Click "Comment" [GitHub](https://help.github.com/en/enterprise/2.14/user/articles/commenting-on-a-pull-request): 1. Follow the link to a pull request 2. Click on "+" next to line to comment on 3. Enter text 4. Click "Add single comment" #### Suggesting edits [Google Docs](https://support.google.com/docs/answer/6033474?co=GENIE.Platform%3DDesktop&hl=en): 1. Follow the link to the doc 2. Select text to suggest edit on 3. Type suggested edit [GitHub](https://help.github.com/en/github/collaborating-with-issues-and-pull-requests/commenting-on-a-pull-request): 1. Follow the link to a pull request 2. Click on "+" next to line to comment on 3. Optionally select multiple lines 4. Click on the left-most button 5. Edit quoted text 6. Click "Add single comment" ### Google Docs add-ons #### Docs to Markdown https://gsuite.google.com/marketplace/app/docs_to_markdown/700168918607 This plugin actually looks pretty good, and may actually work better than Google's internal-only equivalent. I'm not seeing obvious downsides. Advantages: - Provides easy conversion of Google Docs to Markdown. Disadvantages: - ? #### Code blocks https://gsuite.google.com/marketplace/app/code_blocks/100740430168 This is another code formatting tool, but doesn't seem to work as well as Google's internal-only equivalent. Google's internal-only equivalent happens to do some syntax highlighting, whereas Code blocks does none. Advantages: - Public code formatting. - Works with "Docs to Markdown" plugin to get ```-block escaping. Disadvantages: - Mediocre syntax highlighting for Carbon. - No inline `foo` highlighting, unlike Google's internal-only equivalent. - Different highlighting from that in the eventual Markdown document. #### Advanced Find & Replace https://gsuite.google.com/marketplace/app/advanced_find_replace/11210842879 Advanced Find & Replace offers more advanced functionality than the built-in features, particularly around URL and regexp support. Advantages: - Offers improved functionality around key Google Docs friction problems. Disadvantages: - 2 of 5 stars: we should not expect quality. - \$6 purchase price may turn off contributors. - Requires permissions that are banned by Google internally. ### Markdown editing There are multiple markdown editing tools. #### StackEdit https://stackedit.io/ StackEdit may look good on the surface, but we may effectively be restricted to using it as a WYSIWYG markdown editor, not for collaboration. For that, it's simply one option amongst many, and not necessarily the best. Advantages: - Provides preview when editing Markdown files. - Provides application-specific comment support. Disadvantages: - Cannot use shared Google workspaces with Google corp accounts, due to security restrictions. Will likely cause issues for others, too. - Google Docs only works as passive storage. - StackEdit docs aren't Google Docs, comments aren't Google Docs comments. ### GitHub Markdown syntax highlighting GitHub's syntax highlighter [allows adding third-party extensions](https://github.com/github/linguist/blob/master/CONTRIBUTING.md#adding-an-extension-to-a-language), but requires them to have significant use. Note we can't take advantage of this until Carbon is public. We may be able to find a language with sufficiently similar syntax to [override the language](https://github.com/github/linguist/#overrides) and get reasonable highlighting. ## Rationale During the decision process, several of the individual rationales were influenced by the idea that one could view the proposal process in one of two ways: 1. A PR-centric model. The review team is trying to achieve consensus around a PR as a whole. The PR may (and often will) implement the proposed changes. The proposal as essentially a description of the changes. 2. A proposal-centric model. The review team is trying to achieve consensus around a proposal. The PR may show a preview of the implementation, but it is purely informative. If one favors a PR-centric model, this steers one away from committing proposals that are not accepted, towards committing proposals to the affected repository, etc. In general, the PR-centric model was favored. ### Rationale for using a GitHub markdown-centric flow - The GitHub markdown-centric flow makes the on-ramp as smooth as possible for external contributors. - This positions the project to maximize the ease of engaging with and gaining contributions from the wider industry. - The final documents must be in markdown form, so it is best if contributors have the option to stay in markdown for the whole process. This is significantly less complex than something that converts between formats: - Less to learn - Fewer steps in the process - No outdated versions in the old format left behind - The technical flow seems on balance better than the Google Docs-based workflow. The proposal does a really good job explaining advantages and disadvantages. In summary, the Google Docs-centric workflow has a lot of disadvantages that make it difficult to work with proposals over the long term. ### Rationale for not requiring tracking issues There were several members who had no strong preference on this issue. The consensus was that until there is a compelling reason to require tracking issues, the process is more light-weight without them. ### Rationale for not committing proposals that are declined or deferred - This approach seems simpler. - When a proposal PR includes the changes put forth in the proposal (PR-centric model), the declined PR might need to be considerably changed--and might lose context--in order to be committed. - The community will put a lot of work into developing, discussing, and making a decision on a proposal. There may be valuable insight in rejected proposals, so it makes sense to archive them. However, as noted, committing the PR will not always be possible with reasonable effort if not working in a proposal-centric model, as the proposal text may not stand on its own. - While we may discover issues with this approach, it is better to try this way, see if any issues can be rectified, and propose changes as necessary. ### Rationale for committing a proposal to the repository it affects - This keeps the proposal close to the repository, and therefore, the community, that it affects. - It facilitates autonomy of (future) review teams responsible for a particular aspect of Carbon. For example, a reference implementation team responsible for the carbon-toolchain repository. - It simplifies the common case and makes it easier to find how each repository evolves over time. ### Rationale for pushing high-level comments to GitHub While opinions were not as strong, reasons given for preferring comments in GitHub: - This flow will maximize the alignment with “normal” GitHub development flow. - This both improves pulling external/new people into the flow, and will reduce the number of flows they need to learn/remember/tool for. - We will get ecosystem benefits as this flow continues to be optimized by GitHub and those using it. ### Open questions #### Do we use a Google Docs-centric or GitHub Markdown-centric flow? **Decision:** Use GitHub Markdown-centric flow. #### Where should proposals be stored in GitHub? **Decision:** Store proposals in the repository they affect. #### Should we push comments to focus on GitHub? **Decision:** Push high-level comments to GitHub, rather than focusing these discussions in Discourse. #### Should there be a tracking issue? **Decision:** Don't require tracking issues. #### Should declined/deferred proposals be committed? **Decision:** Do not commit declined/deferred proposals. ================================================ FILE: proposals/p0051.md ================================================ # Goals [Pull request](https://github.com/carbon-language/carbon-lang/pull/51) ## Table of contents - [Problem](#problem) - [Background](#background) - [Proposal](#proposal) - [Including success criteria](#including-success-criteria) - [Alternatives](#alternatives) - [Change priority of interoperability and migration](#change-priority-of-interoperability-and-migration) - [Address project goals differently](#address-project-goals-differently) - [Status quo](#status-quo) - [Completely remove the community priority from this document](#completely-remove-the-community-priority-from-this-document) - [Add project goals to the priority list](#add-project-goals-to-the-priority-list) - [Merge project goals and language goals](#merge-project-goals-and-language-goals) - [Rationale](#rationale) - [Open questions](#open-questions) ## Problem We want to have clear goals for the Carbon project, to both establish and document where we expect Carbon to be headed. These are aspirational goals, and we will try our best to achieve all of them. The documented goals should make it easier for potential users to determine whether Carbon is an appropriate fit for them. ## Background Carbon's goals are heavily based on ["Goals and priorities for C++"](https://docs.google.com/document/d/1jrpGk6Sa0bt1u1tSuZtPnVI3inr7A4c0iJC9V2I6zUA/edit). Many thanks to the authors and contributors for helping us formulate our goals and priorities. This proposal was drafted in [Docs](https://docs.google.com/document/d/1MJvVIDXQrhIj6hZ7NwMDbDch9XLO2VaYrGq29E57meU/edit). ## Proposal The proposed goals are encapsulated in [the goals changes](/docs/project/goals.md). ### Including success criteria The goals are currently a series of rough goals, with no specific metrics to measure them by. We've discussed adding metrics, and decided not to include them in the goals; the expectation is that they'll be added separately as a principle. This proposal includes [success criteria](/docs/project/principles/success_criteria.md) covering: - Platforms - Migration tooling It should be expected that more will be added in the future. ## Alternatives ### Change priority of interoperability and migration Right now, interoperability/migration is priority #7, indicating that when trade-offs are necessary, interoperability is likely to suffer. At least some parties believe it should be somewhere in the top 3 priorities, perhaps #1. Overall, we believe a consensus can be formed around keeping interoperability/migration as #7, so long as there are clear minimum expectations for interoperability. Part of the priority conflict may be an interpretation that there will only rarely be compromises between goals, irrespective of the priority of interoperability. For example, considering the readability of C++ interoperability syntax, it could be viewed in two ways: 1. An edge-case syntax that can be avoided in most code, thus not significantly affecting the readability goal. 2. A readability issue that risks making _all_ code less readable, representing the interoperability goal as subverting the readability goal, in which case either interoperability must be higher priority than readability, or we must have no interoperability. While the proposers prefer the first interpretation, the second all-or-nothing interpretation may be what's leading to the desire of treating interoperability as a higher priority. Overall, we would present the advantages and disadvantages as: Advantages: - There is a consensus that interoperability is critical. Making it the top goal emphasizes that. Disadvantages: - While interoperability is critical, the same is true of other goals; this isn't enough to determine priority. - There are trade-offs between goals where there is already a consensus that interoperability will be diminished in favor of other goals, and there aren't as many clear cases in the other direction. - Taken to extremes, making interoperability and migration a higher priority than evolution would suggest that Carbon should start with C++ and incrementally evolve from that base. We have observed areas where C++ has had trouble evolving, and Carbon would risk getting stuck on similar local maxima in order to avoid breaking existing code. For example of where trade-offs may be seen right now: - Carbon's set of primitive types won't match C++'s list; we currently expect multiple C++ types to map to single Carbon types. This could be considered a conflict with multiple priorities: - #2: Carbon's leaning for fewer types should make evolution of the language easier. - #3: Carbon code will be easier to read and understand with fewer types. - When considering `Int` vs `int`, Carbon's plans do not mirror C++. This could be considered a conflict with multiple priorities: - #1: Carbon's primary types, such as `Int`, should be allowed to replace C++-specified overflow behaviors with alternatives that are higher performance. - #3: Avoiding platform-specific types should make it easier to understand how could will function on various platforms. - #4: Carbon's plan for `Int` trapping where C++ would overflow should make code safety easier. - We expect platform support priorities to differ between C/C++ and Carbon. This is a conflict with Carbon's #6 priority, which focuses support on modern over legacy platforms. - C++'s preprocessor macros will be replaced by metaprogramming in Carbon. It's not clear what migration of code using macros will look like, but some will likely be expanded by automation. This could be considered a conflict with multiple priorities: - #2: Structured metaprogramming should make it easier to evolve software. - #3: Metaprogramming should be easier to read than preprocessor macros. - Templates have been argued as only being added for interoperability/migration, and that we could only have generics without that goal. This is not considered a conflict between goal priorities. - This could be considered a conflict with priority #3, because templates are hard to read and understand. However, templates don't constrain other parts of the language. While they may have readability or other problems, they aren't core or required in the way that other elements are, including primitive types. ### Address project goals differently A concern was raised that, while the "Governance and evolution" doc calls for all decisions to be justified by goals, there was no clear goal to justify the existence of things like a code of conduct. Similarly, if performance is the highest priority, then one might decide that retaining a performance expert may take priority over addressing their bad behavior. To address this issue, project goals were added. They are not explicitly part of the numeric priority list. However, while we expect a ranking of language goals in order to weigh tradeoffs in the language design, we see project goals as orthogonal to language design. #### Status quo The project goals could be addressed as-is in the proposal. #### Completely remove the community priority from this document We could completely remove the project goals from this document. Advantages: - Allows this to be the "language goals" document. Disadvantages: - Creates additional documents that need to be understood to parse language goals. - Makes for less clear prioritization of community, increasing ambiguity on how community vs language design conflicts can be resolved. #### Add project goals to the priority list We could make the project goals part of the priority list. Advantages: - Makes the ordering unambiguous. Disadvantages: - Makes what's currently a numerated list of language design goals less design-focused. - Prevents saying trivial things like "performance is our top priority". - Unless community isn't the top priority, re-creating the conflict risk that led to the creating of these project goals. #### Merge project goals and language goals Originally, we only had one goals and priorities section. We could re-merge project goals and language goals. Advantages: - Makes the document shorter and more concise. - Tooling can already be considered a priority under "Code that is easy to read, understand, and write", and is explicitly mentioned as part of that goal. Disadvantages: - Previous setup raised objections over why the community goal wasn't part of the priority list. ## Rationale A concrete set of goals is essential to ensure that contributors to the Carbon project are working in the same direction, and as a guide for what constitutes progress in every aspect of the project. It is important to have a coherent, agreed-upon vision and direction for a programming language, as a social contract between people who work on it and people who use it. The goals listed have been iterated on for many months by many parties. These goals reflect not only the kind of community and language that the core team would like to build, but also the kind of community and language that a large population of C++ users have been asking for from C++ itself. As a consequence, this particular set of goals seems like an important direction in which to experiment with a candidate future for C++ itself. By doing so, it will address a specific group of C++ users. While there may be some uncertainty about resolving conflicts between goals, particularly migratability and C++ interoperability, the core team feels that they are generally in alignment in this regard. The priority among goals can be revised as needed based on the experience of using it in practice. ### Open questions There were no open questions. GitHub issue [106](https://github.com/carbon-language/carbon-lang/issues/106) contains points to be considered and, if necessary, resolved in a subsequent proposal. ================================================ FILE: proposals/p0063.md ================================================ # Criteria for Carbon to go public [Pull request](https://github.com/carbon-language/carbon-lang/pull/63) ## Table of contents - [Problem](#problem) - [Background](#background) - [Proposal](#proposal) - [Alternatives considered](#alternatives-considered) - [Rationale](#rationale) ## Problem Carbon is currently private, and should have a clear plan for going public. ## Background Some of this is explained to people when they're asked if they'd like to contribute to Carbon; the purpose of this proposal is to provide documentation. ## Proposal See /docs/project/going_public.md. ## Alternatives considered We have also considered going public immediately. We believe the noted criteria are important to address before proceeding. ## Rationale "An open, inclusive process for Carbon changes" requires us to make the decision to go public in a clear and unsurprising way, with criteria that are written down. Going public too early introduces risks to the long-term evolution and maintenance of the language by increasing the costs of the community members developing it. Ensuring that Carbon meets its functional goals, especially that of interoperability and migration, will inherently require large scale experimentation that is infeasible to do without becoming public at some point. The core team is aligned on the core policy decisions, which are: - The decision to go public will go through the usual proposal process. - The structure of the proposal gives the things that we are looking for prior to going public. - We expect to delay announcing to the extent we can. - We do not expect that to be so late that everything is done and we are ready to ship. That is, we are not going to wait until version 1.0 is ready. - We don't expect this to be so late that Carbon is no longer an experiment. Changes to the text and wording that align with these items should be submitted as code reviews. The core team members chandlerc and zygloid will both approve each code review. An example change that would be covered by a code review: there will be no automatic going public just because the criteria are met -- it will be a decision of the core team. ================================================ FILE: proposals/p0074.md ================================================ # Change comment/decision timelines in proposal process [Pull request](https://github.com/carbon-language/carbon-lang/pull/74) ## Table of contents - [Problem](#problem) - [Background](#background) - [Proposal](#proposal) - [Details](#details) - [Open Question/Bikeshed](#open-questionbikeshed) - [Alternatives considered](#alternatives-considered) - [Alternative 1](#alternative-1) - [Alternative 2](#alternative-2) - [Rationale](#rationale) - [Open questions](#open-questions) - [Do we want to add the proposal to the core team meeting agenda when the deadline for comments is announced, or when the deadline is reached?](#do-we-want-to-add-the-proposal-to-the-core-team-meeting-agenda-when-the-deadline-for-comments-is-announced-or-when-the-deadline-is-reached) ## Problem In the existing approval process, the deadline for a decision is set to be at least a week after the comment period. The request for final comments currently asks that remaining comments be provided within a day, essentially setting a soft deadline for the end of the comment period unless issues arise. As such, the end of the comment period--which is where most of the decision work usually takes place--is not known very far in advance. This makes it difficult for core team members to prioritize work to get proposals fully reviewed by the end of the comment period. ## Background People tend to prioritize work based on deadlines. In the current proposal approval process, the deadline for final comments is not known until the day before they are due (if not extended). Ideally, we want the core team to have all issues with a proposal raised and addressed to the extent possible prior to entering the decision phase. As such, the work of the core team is front-loaded to the comment phase, and the decision is usually delivered shortly after the end of the comment phase and well before the decision deadline. While the comment period usually begins long before the final request for comments is issued, there are often multiple outstanding proposals (as well as non-Carbon work) competing for the attention of the core team. ## Proposal Announce the end of the comment period further in advance, while reducing the interval between the end of the comment period and the decision meeting. The advanced notification will allow team members to more easily prioritize their review work. It also reflects the importance of getting issues surfaced during the review period rather than the decision period. ## Details - A deadline for final comments will be published at least 7 calendar days (or 4 working days, if longer) in advance (instead of the current 1 day). On or before the deadline date, the deadline may be extended if the review manager determines that there is still productive discussion going on. - At the time the comment period deadline is announced, the proposal will be added to the agenda of the next core team meeting following the comment period deadline. - There must be a minimum of four working days between the end of the comment period and the day of the meeting. - If the deadline for comments is extended, the agenda item will be moved, if necessary, by the review manager. ### Open Question/Bikeshed Do we want to add the proposal to the core team meeting agenda when the deadline for comments is announced, or when the deadline is reached? The advantage of the latter is that no adjustments need to be made if the deadline is extended. The advantage of the former is that it gives people an idea of whether a core team meeting can be canceled further in advance. ## Alternatives considered ### Alternative 1 Leaving the approval process as it is. ### Alternative 2 Reducing the amount of time between the end of the comment period and the decision meeting. This option was not chosen because the shorter period could result in more meetings, as the deciders would have less time to get their decisions in. It has the potential to reduce the velocity of decisions if it results in decisions happening later than they would with an earlier meeting date (decision deadline). ## Rationale - This directly supports our community goals: not everybody is in a position to respond to events with less than a day of latency, so longer lead times before deadlines will help enable them to participate. - Longer lead time makes it more likely that we'll get substantive comments. - The answer to the open question doesn't strongly matter, and there is a preference for leaving it to the review managers' discretion rather than having the core team decide. ### Open questions #### Do we want to add the proposal to the core team meeting agenda when the deadline for comments is announced, or when the deadline is reached? The core team is okay with available options, and treating this as a bikeshed that can be resolved by review managers as part of doc changes. ================================================ FILE: proposals/p0083.md ================================================ # In-progress design overview [Pull request](https://github.com/carbon-language/carbon-lang/pull/83) ## Table of contents - [Problem](#problem) - [Goals](#goals) - [Background](#background) - [Proposal](#proposal) - [Alternatives considered](#alternatives-considered) - [Single-file design overview](#single-file-design-overview) - [No in-progress design overview](#no-in-progress-design-overview) - [No overview of designs](#no-overview-of-designs) - [Rationale](#rationale) ## Problem We need some vaguely consistent shared understanding of the language design as a whole. The goal is merely consistency in discussions, in-progress syntax, and general approach. It in no way needs reliably to reflect the end state, either desired or realized. Instead, it should evolve as each area matures and becomes concrete, while providing an overarching overview that connects the language together, and the consistent background that we all can refer back to during discussions. ## Goals This is intended to offer a reasonable starting point for: - Example code. - Conceptualizing Carbon at a high level. - Reasonable, but not necessarily final, approaches to features in README.md. - If any idea is obviously bad, we can clean it up here. This proposal is not intended to achieve: - A whole language design. - This is way too much work for a single proposal; this is a skeletal framework only. - As we work on feature-specific designs, we may decide to use other approaches. That's fine: we only need somewhere to start. - The summaries in README.md may be expected to change over time. - Feature-specific files aren't intended to be well-written or comprehensive. They are a quick jot of prior thoughts. - We want to avoid getting stuck on language details that we should consider more carefully regardless. If you're passionate about a feature, please feel free to start a new proposal for it. - Each and every aspect of the suggested overview should be subject to careful examination and justification before it becomes a settled plan of record. ## Background Many of the ideas here stem from discussions between several of the initial people working on Carbon over several years. That doesn't make them good, but may give some context on where they came from. They are also heavily informed by the experience several of us have both working on the Clang C++ frontend and several C++ codebases including those of LLVM and Clang themselves. ## Proposal See [the language design overview document](/docs/design/README.md). ## Alternatives considered ### Single-file design overview We also considered putting the full design overview in one file, as in [PR 22](https://github.com/carbon-language/carbon-lang/pull/22). This is versus the hierarchy proposed here. Advantages: - All proposed changes are in one place. - Easier for people to skim rationale and considered changes. Disadvantages: - Encourages more single-file designs. - A principle of the multi-file approach is that complex features may have subdirectories with their own README.md and files for sub-features, similar to the relationship between this design overview and features. - Single-file designs may be harder to evolve long-term, as the volume of information contained impedes reading. ### No in-progress design overview The primary alternative is to avoid even having a draft or in-progress design of this form until each constituent component is more thoroughly conceived and considered. Advantages: - Avoids anchoring design on approaches that haven't yet been fully explored. - Avoids getting stuck on discussing details where a proposal isn't fleshed out. Disadvantages: - The lack of an overview can lead to significant confusion and inconsistencies in discussion, hindering fleshing out details. - An overview offers basic shaping of the language as a whole, even as it evolves. The compromise chosen is to have the in-progress design and simply work to resist both anchoring and distraction stemming from it. We want to get the benefits we can here while minimizing the cost. ### No overview of designs The overview will result in content duplication from individual designs. At the time of this proposal, this may be significant because individual designs are not fleshed out, and should thus duplication should be expected to reduce over time. However, it should be expected to remain as the duplication is fundamental to having an overview. This duplication could be addressed by removing the overview. Instead, design/README.md could be restricted to listing existing designs, with no additional content. The proposed approach assumes that the proposed overviews offer significant value for ramp-up. Advantages: - Eliminates content duplication. - A simple index is easier to maintain long-term, with less to become stale. - It could be fully automated. Disadvantages: - No quick way to get a high-level understanding. - The overview is the only step before "reading every design". - For example, we summarize common control flow keywords, so that readers don't need to identify which documents they come from and what exists. - Harder to show relationships between various features. - While examples can show how designs relate, it may not be as obvious from a simple link, even when reading the associated design. - For example, lexical conventions come up as references for three otherwise distinct sections. If we had a simple index of files, we should expect users to need to read individual designs to understand relationships. - For example, we explain in brief the relationships between categories of types. - There's disagreement about whether the text of README.md offers any utility: [comment thread](https://github.com/carbon-language/carbon-lang/pull/83/files/25437de9e61b3a15e8ddde67b6297f1795922355..97da855dbe6023930e02473af46abea03af991e7#r444487049) ## Rationale This proposal provides some much-needed context for a lot of our discussions and deliberations about Carbon. Having a skeleton in place for the language design is an important step forward. We can't make language design decisions in a vacuum -- we need a "blurry outline" of the big picture before we can start filling in the details of any particular area. We need to establish a common frame of reference regarding the overall shape of the language, so that we can parallelize the in-depth design work. That means that, by necessity, this proposal suggests lots of concrete design decisions that have not yet had sufficient analysis for us to affirm them. There is a shared understanding that we are not committing to any of those decisions, only to the broad picture painted by the combination of those decisions, and that all such decisions need to be revisited by another proposal before we consider them to be agreed on. There is a substantial risk of anchoring how we think about Carbon -- we’ll just have to do our best to avoid taking this doc as a given when evaluating subsequent proposals. Those proposals must justify a direction that agrees with this doc as much as one that does not agree with it. This doc sets the stage for increasingly incremental steps towards a complete design. Establishing a structure for the design is especially helpful as it will show how things do or do not connect across the language. Adopting this largely work-in-progress overview in order to see the structure of things, while still needing to resolve the specifics in each area, will directly help reinforce our goal of language evolution over time. This will help us learn how to effectively iterate, and how to compensate and overcome the risks of anchoring, change aversion, and other challenges. ================================================ FILE: proposals/p0107.md ================================================ # Code and name organization [Pull request](https://github.com/carbon-language/carbon-lang/pull/107) ## Table of contents - [Problem](#problem) - [Proposal](#proposal) - [Out-of-scope issues](#out-of-scope-issues) - [Open questions for the decision](#open-questions-for-the-decision) - [Should we switch to a library-oriented structure that's package-agnostic?](#should-we-switch-to-a-library-oriented-structure-thats-package-agnostic) - [Should there be a tight association between file paths and packages/libraries?](#should-there-be-a-tight-association-between-file-paths-and-packageslibraries) - [Justification](#justification) - [Alternatives considered](#alternatives-considered) - [Packages](#packages) - [Name paths for package names](#name-paths-for-package-names) - [Referring to the package as `package`](#referring-to-the-package-as-package) - [Remove the `library` keyword from `package` and `import`](#remove-the-library-keyword-from-package-and-import) - [Rename package concept](#rename-package-concept) - [No association between the file system path and library/namespace](#no-association-between-the-file-system-path-and-librarynamespace) - [Libraries](#libraries) - [Allow exporting namespaces](#allow-exporting-namespaces) - [Allow importing implementation files from within the same library](#allow-importing-implementation-files-from-within-the-same-library) - [Alternative library separators and shorthand](#alternative-library-separators-and-shorthand) - [Single-word libraries](#single-word-libraries) - [Collapse API and implementation file concepts](#collapse-api-and-implementation-file-concepts) - [Automatically generating the API separation](#automatically-generating-the-api-separation) - [Collapse file and library concepts](#collapse-file-and-library-concepts) - [Collapse the library concept into packages](#collapse-the-library-concept-into-packages) - [Collapse the package concept into libraries](#collapse-the-package-concept-into-libraries) - [Different file type labels](#different-file-type-labels) - [Function-like syntax](#function-like-syntax) - [Inlining from implementation files](#inlining-from-implementation-files) - [Library-private access controls](#library-private-access-controls) - [Managing API versus implementation in libraries](#managing-api-versus-implementation-in-libraries) - [Multiple API files](#multiple-api-files) - [Name paths as library names](#name-paths-as-library-names) - [Imports](#imports) - [Block imports](#block-imports) - [Block imports of libraries of a single package](#block-imports-of-libraries-of-a-single-package) - [Broader imports, either all names or arbitrary code](#broader-imports-either-all-names-or-arbitrary-code) - [Direct name imports](#direct-name-imports) - [Optional package names](#optional-package-names) - [Namespaces](#namespaces) - [File-level namespaces](#file-level-namespaces) - [Scoped namespaces](#scoped-namespaces) - [Rationale](#rationale) - [Open questions](#open-questions) - [Should we switch to a library-oriented structure that's package-agnostic?](#should-we-switch-to-a-library-oriented-structure-thats-package-agnostic-1) - [Should there be a tight association between file paths and packages/libraries?](#should-there-be-a-tight-association-between-file-paths-and-packageslibraries-1) ## Problem How do developers store code for the compiler, and access it for reuse? ## Proposal Adopt an approach with tiered files, libraries, packages and namespaces in the design. ### Out-of-scope issues Related issues that are out-of-scope for this proposal are: - Access control: while there is some implicit access control from interface vs implementation considerations for libraries, they are more about addressing circular dependencies in code. - Aliasing implementation: while the `alias` keyword is critical to how easy or difficult refactoring is, it should be designed on its own. - Compilation details: this proposal sets up a framework that should enable well-designed compilation, but does not set about to design how compilation will work. - File-private identifiers: Something similar to C++ `static` functions may exist. However, that will be addressed separately. - Incremental migration and unused imports: incrementally migrating a declaration from one library to another might require an intermediate state where callers import both libraries, with consequent issues. However, it may also not require such. Whether it does, or whether tooling needs to be added to support the specific intermediary state of transitional, unused imports, is out of scope. - Name lookup, including addressing conflicting names between imports and names in the current file: the name lookup design is likely to address this better, including offering syntax that could refer to it if needed. - After discussion, we believe we do not need to support package renaming. However, that final decision should be based on name lookup addressing the issue, as implications need to be considered more deeply. - Package management: while we want to choose syntax that won't impose barriers on building package management into Carbon, we should not make assumptions about how package management should work. - Prelude package, or fundamentals: while we've discussed how to handle name lookup for things like `Int32`, this proposal mainly lays out a framework where options for addressing that are possible. This proposal should not be interpreted as addressing these issues. A separate discussion of these issues will remain necessary. ## Open questions for the decision Extended open question comparisons may be found in [the examples doc](https://docs.google.com/document/d/1J8GX9uw5AxBz5Q22MLHJOfzLq4WJqKL-q1VwnKGHG-k/edit#) in addition to the `code_and_organization.md` alternatives section. ### Should we switch to a library-oriented structure that's package-agnostic? **Decision:** No. Right now, the `package` syntax is very package-oriented. We could instead eliminate package semantics from code and organization, relying only on libraries and removing the link to distribution. This is the [collapse the package concept into libraries](#collapse-the-package-concept-into-libraries) alternative. Does the core team agree with the approach to packages and libraries? If not, does the alternative capture what the core team wants to be turned into the proposal, or is some other approach preferred? ### Should there be a tight association between file paths and packages/libraries? **Decision:** Make paths correspond to libraries for API files, not impl files. Keep `package`. Right now, the `package` syntax requires the package's own name be repeated through code. This touches on a couple alternatives: - Strict association between the file system path and library/namespace - [Referring to the package as `package`](#referring-to-the-package-as-package) The end result of taking both alternatives would be that: - The `package` and `library` would no longer need to be specified on the first line. - The `import` would still need a `library`. - The `package` keyword would always be used to refer to the current package. - Referring to the current package by name would be disallowed, to allow for easier renames of conflicting package names. ## Justification - [Software and language evolution](/docs/project/goals.md#software-and-language-evolution): - The syntax and interactions between `package` and `import` should enable moving code between files and libraries with fewer modifications to callers, easing maintenance of large codebases. - In C++ terms, `#include` updates are avoidable when moving code around. - [Code that is easy to read, understand, and write](/docs/project/goals.md#code-that-is-easy-to-read-understand-and-write): - By setting up imports so that each name in a file is unique and refers to the source package, we make the meaning of symbols clear and easier to understand. - The proposed `namespace` syntax additionally makes it clear when the package's default namespace is not being used. - This is in contrast to C++ namespaces, where the entire body of code above the line of code in question may be used to start a namespace. - Clearly marking interfaces will make it easier for both client code and IDE tab completion to more easily determine which APIs can be used from a given library. - [Fast and scalable development](/docs/project/goals.md#fast-and-scalable-development): - The structure of libraries and imports should help enable separate compilation, particularly improving performance for large codebases. - [Interoperability with and migration from existing C++ code](/docs/project/goals.md#interoperability-with-and-migration-from-existing-c-code): - The syntax of `import` should enable extending imports for use in interoperability code. ## Alternatives considered ### Packages #### Name paths for package names Right now, we only allow a single identifier for the package name. We could allow a full name path without changing syntax. Advantages: - Allow greater flexibility and hierarchy for related packages, such as `Database.Client` and `Database.Server`. - Would allow using GitHub repository names as package names. For example, `carbon-language/carbon-toolchain` could become `carbon_language.carbon_toolchain`. Disadvantages: - Multiple identifiers is more complex. - Other languages with similar distribution packages don't have a hierarchy, and so it may be unnecessary for us. - In other languages that use packages for distribution, they apply similar restrictions. For example, [Node.JS/NPM](https://www.npmjs.com/), [Python PyPi](https://pypi.org/), or [Rust Crates](https://crates.io/). - In [Rust Crates](https://crates.io/), we can observe an example `winapi-build` and `winapi-util`. - We can build a custom system for reserving package names in Carbon. At present, we are choosing to use single-identifier package names because of the lack of clear advantage towards a more complex name path. #### Referring to the package as `package` Right now, we plan to refer to the package containing the current file by name. What's important in the below example is the use of `Math.Stats`: ```carbon package Math library "Stats" api; api struct Stats { ... } struct Quantiles { fn Stats(); fn Build() { ... var Math.Stats: b; ... } } ``` We could instead use `package` as an identifier within the file to refer to the package, giving `package.Stats`. It's important to consider how this behaves for `impl` files, which expect an implicit import of the API. In other words, for `impl` files, this can be compared to an implicit `import Math;` versus an implicit `import Math as package;`. However, there may also be _explicit_ imports from the package, such as `import Math library "Trigonometry";`, which may or may not be referable to using `package`, depending on the precise option used. Advantages: - Gives a stable name to refer to the current library's package. - This reduces the amount of work necessary if the current library's package is renamed, although imports and library consumers may still need to be updated. If the library can also refer to the package by the package name, even with imports from other libraries within the package, work may not be significantly reduced. - The same syntax can be used to refer to entities with the same name as the package. - For example, in a [package named `DateTime`](https://docs.python.org/3/library/datetime.html#datetime-objects), `package.DateTime` is unambiguous, whereas `DateTime.DateTime` could be confusing. Disadvantages: - We are likely to want a more fine-grained, file-level approach proposed by [name lookup](/docs/design/name_lookup.md). - Allows package owners to name their packages things that they rarely type, but that importers end up typing frequently. - The existence of a short `package` keyword shifts the balance for long package names by placing less burden on the package owner. - Reuses the `package` keyword with a significantly different meaning, changing from a prefix for the required declaration at the top of the file, to an identifier within the file. - We don't need to have a special way to refer to the package to disambiguate duplicate names. In other words, there is likely to be other syntax for referring to an entity `DateTime` in the package `DateTime`. - Renaming to a `library` keyword has been suggested to address concerns with `package`. Given that `library` is an argument to `package`, it does not significantly change the con. - Creates inconsistencies as compared to imports from other packages, such as `package Math; import Geometry;`, and imports from the current package, such as `package Math; import Math library "Stats";`. - Option 1: Require `package` to be used to refer to all imports from `Math`, including the current file. This gives consistent treatment for the `Math` package, but not for other imports. In other words, developers will always write `package.Stats` from within `Math`, and `Math.Stats` will only be written in _other_ packages. - Option 2: Require `package` be used for the current library's entities, but not other imports. This gives consistent treatment for imports, but not for the `Math` package as a whole. In other words, developers will only write `package.Stats` when referring to the current library, whether in `api` or `impl` files. `Math.Stats` will be used elsewhere, including from within the `Math` package. - Option 3: Allow either `package` or the full package name to refer to the current package. This allows code to say either `package` or `Math`, with no enforcement for consistency. In other words, both `package.Stats` and `Math.Stats` are valid within the `Math` package. Because name lookup can be expected to address the underlying issue differently, we will not add a feature to support name lookup. We also don't want package owners to name their packages things that even _they_ find difficult to type. As part of pushing library authors to consider how their package will be used, we require them to specify the package by name where desired. #### Remove the `library` keyword from `package` and `import` Right now, we have syntax such as: ```carbon package Math library "Median" api; package Math library "Median" namespace Stats api; import Math library "Median"; ``` We could remove `library`, resulting in: ```carbon package Math.Median api; package Math.Median namespace Math.Stats api; import Math.Median; ``` Advantages: - Reduces redundant syntax in library declarations. - We expect libraries to be common, so this may add up. Disadvantages: - Reduces explicitness of package vs library concepts. - Creates redundancy of the package name in the namespace declaration. - Instead of `package Math.Median namespace Math.Stats`, could instead use `Stats`, or `this.Stats` to elide the package name. - Potentially confuses the library names, such as `Math.Median`, with namespace names, such as `Math.Stats`. - Either obfuscates or makes it difficult to put multiple libraries in the top-level namespace. - This is important because we are interested in encouraging such behavior. - For example, if `package Math.Median api;` uses the `Math` namespace, the presence of `Median` with the same namespace syntax obfuscates the actual namespace. - For example, if `package Math.Median namespace Math api` is necessary to use the `Math` namespace, requiring the `namespace` keyword makes it difficult to put multiple libraries in the top-level namespace. As part of avoiding confusion between libraries and namespaces, we are declining this alternative. #### Rename package concept In other languages, a "package" is equivalent to what we call the name path here, which includes the `namespace`. We may want to rename the `package` keyword to avoid conflicts in meaning. Alternative names could be 'bundle', 'universe', or something similar to Rust's 'crates'; perhaps 'compound' or 'molecule'. Advantages: - Avoids conflicts in meaning with other languages. - [Java](https://www.oracle.com/java/technologies/glossary.html), similar to a namespace path. - [Go](https://golang.org/doc/effective_go.html#package-names), similar to a namespace path. Disadvantages: - The meaning of `package` also overlaps a fair amount, and we would lose that context. - [Package management systems](https://en.wikipedia.org/wiki/List_of_software_package_management_systems) in general. - [NPM/Node.js](https://www.npmjs.com/), as a distributable unit. - [Python](https://packaging.python.org/tutorials/installing-packages/), as a distributable unit. - [Rust](https://doc.rust-lang.org/book/ch07-01-packages-and-crates.html), as a collection of crates. - [Swift](https://developer.apple.com/documentation/packagedescription), as a distributable unit. #### No association between the file system path and library/namespace Several languages create a strict association between the method for pulling in an API and the path to the file that provides it. For example: - In C++, `#include` refers to specific files without any abstraction. - For example, `#include "PATH/TO/FILE.h"` means there's a file `PATH/TO/FILE.h`. - In Java, `package` and `import` both reflect file system structure. - For example, `import PATH.TO.FILE;` means there's a file `PATH/TO/FILE.java`. - In Python, `import` requires matching file system structure. - For example, `import PATH.TO.FILE` means there's a file `PATH/TO/FILE.py`. - In TypeScript, `import` refers to specific files. - For example, `import {...} from 'PATH/TO/FILE';` means there's a file `PATH/TO/FILE.ts`. For contrast: - In Go, `package` uses an arbitrary name. - For example, `import "PATH/TO/NAME"` means there is a directory `PATH/TO` that contains one or more files starting with `package NAME`. In Carbon, we are using a strict association to say that `import PACKAGE library "PATH/TO/LIBRARY"` means there is a file `PATH/TO/LIBRARY.carbon` under some package root. Advantages: - The strict association makes it harder to move names between files without updating callers. - If there were a strict association of paths, it would also need to handle file system-dependent casing behaviors. - For example, on Windows, `project.carbon` and `Project.carbon` are conflicting filenames. This is exacerbated by paths, wherein a file `config` and a directory `Config/` would conflict, even though this would be a valid structure on Unix-based filesystems. Disadvantages: - A strict association between file system path and import path makes it easier to find source files. This is used by some languages for compilation. - Allows getting rid of the `package` keyword by inferring related information from the file system path. We are choosing to have some association between the file system path and library for API files to make it easier to find a library's files. We are not getting rid of the `package` keyword because we don't want to become dependent on file system structures, particularly as it would increase the complexity of distributed builds. ### Libraries #### Allow exporting namespaces We propose to not allow exporting namespaces as part of library APIs. We could either allow or require exporting namespaces. For example: ```carbon package Checksums; api namespace Sha256; ``` While this approach would mainly be syntactic, a more pragmatic use of this would be in refactoring. It implies that an aliased namespace could be marked as an `api`. For example, the below could be used to share an import's full contents: ```carbon package Translator library "Interface" api; import Translator library "Functions" as TranslatorFunctions; api alias Functions = TranslatorFunctions; ``` Advantages: - Avoids any inconsistency in how entities are handled. - Reinforces whether a namespace may contain `api` entities. - Enables new kinds of refactorings. Disadvantages: - Creates extra syntax for users to remember, and possibly forget, when declaring `api` entities. - Makes it possible to have a namespace marked as `api` that doesn't contain any `api` entities. - Allowing aliasing of entire imports makes it ambiguous which entities are being passed on through the namespace. - This may impair refactoring. - This can be considered related to [broader imports, either all names or arbitrary code](#broader-imports-either-all-names-or-arbitrary-code). This alternative is declined because it's not sufficiently clear it'll be helpful, versus impairment of refactoring. #### Allow importing implementation files from within the same library The current proposal is that implementation files in a library implicitly import their API, and that they cannot import other implementation files in the same library. We could instead allow importing implementation files from within the same library. There are two ways this could be done: - We could add a syntax for importing symbols from other files in the same library. This would make it easy to identify a directed acyclic graph between files in the library. For example: ```carbon package Geometry; import file("point.6c"); ``` - We could automatically detect when symbols from elsewhere in the library are referenced, given an import of the same library. For example: ```carbon package Geometry; import this; ``` Advantages: - Allows more separation of implementation between files within a library. Disadvantages: - Neither approach is quite clean: - Using filenames creates a common case where filenames _must_ be used, breaking away from name paths. - Detecting where symbols exist may cause separate parsing, compilation debugging, and compilation parallelism problems. - Libraries are supposed to be small, and we've chosen to only allow one API file per library to promote that concept. Encouraging implementation files to be inter-dependent appears to support a more complex library design again, and may be better addressed through inter-library ACLs. - Loses some of the ease-of-use that some other languages have around imports, such as Go. - Part of the argument towards `api` and `impl`, particularly with a single `api`, has been to mirror C++ `.h` and `.cc`. Wherein a `.cc` `#include`-ing other `.cc` files is undesirable, allowing a `impl` to import another `impl` could be considered similarly. The problems with these approaches, and encouragement towards small libraries, is how we reach the current approach of only importing APIs, and automatically. #### Alternative library separators and shorthand Examples are using `/` to separator significant terms in library names, and `//` to separate the package name in shorthand. For example, `package Time library "Timezones/Internal";` with shorthand `Time//Timezones/Internal`. Note that, because the library is an arbitrary string and shorthand is not a language semantic, this won't affect much. However, users should be expected to treat examples as best practice. We could instead use `.` for library names and `/` for packages, such as `Time/Timezones.Internal`. Advantages: - Clearer distinction between the package and library, increasing readability. - We have chosen not to [enforce file system paths](#should-there-be-a-tight-association-between-file-paths-and-packageslibraries) in order to ease refactoring, and encouraging a mental model where they may match could confuse users. Disadvantages: - Uses multiple separators, so people need to type different characters. - There is a preference for thinking of libraries like file system paths, even if they don't actually correspond. People like `/`, so we're going with `/`. ##### Single-word libraries We could stick to single word libraries in examples, such as replacing `library "Algorithms/Distance"` with `library "Distance"`. Advantages: - Encourages short library names. Disadvantages: - Users are likely to end up doing some hierarchy, and we should address it. - Consistency will improve code understandability. We might list this as a best practice, and have Carbon only expose libraries following it. However, some hierarchy from users can be expected, and so it's worthwhile to include a couple examples to nudge users towards consistency. #### Collapse API and implementation file concepts We could remove the distinction between API and implementation files. Advantages: - Removing the distinction between API and implementation would be a language simplification. - Developers will not need to consider build performance impacts of how they are distributing code between files. Disadvantages: - Serializes compilation across dependencies. - May be exacerbated because developers won't be aware of when they are adding a dependency that affects imports. - In large codebases, it's been necessary to abstract out API from implementation in languages that similarly consolidate files, such as Java. However, the lack of language-level support constrains potential benefit and increases friction for a split. - Whereas an `api`/`impl` hierarchy gives a structure for compilation, if there are multiple files we will likely need to provide a different structure, perhaps explicit file imports, to indicate intra-library compilation dependencies. - We could also effectively concatenate and compile a library together, reducing build parallelism options differently. - Makes it harder for users to determine what the API is, as they must read all the files. Requiring users to manage the `api`/`impl` split allows us to speed up compilation for large codebases. This is important for large codebases, and shouldn't directly affect small codebases that choose to only use `api` files. ##### Automatically generating the API separation We could try to address the problems with collapsing API and implementation files by automatically generating an API file from the input files for a library. For example, it may preprocess files to split out an API, reducing the number of imports propagated for _actual_ APIs. For example: 1. Extract `api` declarations within the `api` file. 2. Remove all implementation bodies. 3. Add only the imports that are referenced. Even under the proposed model, compilation will do some of this work as an optimization. However, determining which imports are referenced requires compilation of all imports that _may_ be referenced. When multiple libraries are imported from a single package, it will be ambiguous which imports are used until all have been compiled. This will cause serialization of compilation that can be avoided by having a developer split out the `impl`, either manually or with developer tooling. The `impl` files may make it easier to read code, but they will also allow for better parallelism than `api` files alone can. This does not mean the compiler will or will not add optimizations -- it only means that we cannot wholly rely on optimizations by the compiler. Automatically generating the API separation would only partly mitigate the serialization of compilation caused by collapsing file and library concepts. Most of the build performance impact would still be felt by large codebases, and so the mitigation does not significantly improve [the alternative](#collapse-api-and-implementation-file-concepts). #### Collapse file and library concepts We could collapse the file and library concepts. What this implies is: - [Collapse API and implementation file concepts](#collapse-api-and-implementation-file-concepts). - As described there, this approach significantly reduces the ability to separate compilation. - Only support having one file per library. - The file would need to contain both API and implementation together. This has similar advantages and disadvantages to [collapse API and implementation file concepts](#collapse-api-and-implementation-file-concepts). Differences follow. Advantages: - Offers a uniformity of language usage. - Otherwise, some developers will use only `api` files, while others will always use `impl` files. - The structure of putting API and implementation in a single file mimics other modern languages, such as Java. - Simplifies IDEs and refactoring tools. - Otherwise, these systems will need to understand the potential for separation of interface from implementation between multiple files. - For example, see [potential refactorings](/docs/design/code_and_name_organization/README.md#potential-refactorings). Disadvantages: - Avoids the need to establish a hierarchy between files in a library, at the cost of reducing build parallelism options further. - While both API and implementation is in the same file, it can be difficult to visually identify the API when it's mixed with a lengthy implementation. As with [collapse API and implementation file concepts](#collapse-api-and-implementation-file-concepts), we consider the split to be important for large codebases. The additional advantages of a single-file restriction do not outweigh the disadvantages surrounding build performance. #### Collapse the library concept into packages We could only have packages, with no libraries. Some other languages do this; for example, in Node.JS, a package is often similar in size to what we currently call a library. If packages became larger, that would lead to compile-time bottlenecks. Thus, if Carbon allowed large packages without library separation, we would undermine our goals for fast compilation. Even if we combined the concepts, we should expect it's by turning the "package with many small libraries" concept into "many small packages". Advantages: - Simplification of organizational hierarchy. - Less complexity for users to think about on imports. Disadvantages: - Coming up with short, unique package names may become an issue, leading to longer package names that overlap with the intent of libraries. - These longer package names would need to be used to refer to contained entities in code, affecting brevity of Carbon code. The alternative would be to expect users to always rename packages on import; some organizations anecdotally see equivalent happen for C++ once names get longer than six characters. - For example, [boost](https://github.com/boostorg) could use per-repository packages like `BoostGeometry` and child libraries like `algorithms-distance` under the proposed approach. Under the alternative approach, it would use either a monolithic package that could create compile-time bottlenecks, or packages like `BoostGeometryAlgorithmsDistance` for uniqueness. - While a package manager will need a way to specify cross-package version compatibility, encouraging a high number of packages puts more weight and maintenance cost on the configuration. - We expect libraries to be versioned at the package-level. We prefer to keep the library separation to enable better hierarchy for large codebases, plus encouraging small units of compilation. It's still possible for people to create small Carbon packages, without breaking it into multiple libraries. #### Collapse the package concept into libraries Versus [collapse the library concept into packages](#collapse-the-library-concept-into-packages), we could have libraries without packages. Under this model, we still have libraries of similar granularity as what's proposed. However, there is no package grouping to them: there are only libraries which happen to share a namespace. References to imports from other top-level namespaces would need to be prefixed with a '`.`' in order to make it clear which symbols were from imports. For example, suppose `Boost` is a large system that cannot be distributed to users in a single package. As a result, `Random` functionality is in its own distribution package, with multiple libraries contained. The difference between approaches looks like: - `package` vs `library`: - Trivial: - Proposal: `package BoostRandom;` - Alternative: `library "Boost/Random" namespace Boost;` - Multi-layer library: - Proposal: `package BoostRandom library "Uniform";` - Alternative: `library "Boost/Random.Uniform" namespace Boost;` - Specifying namespaces: - Proposal: `package BoostRandom namespace Distributions;` - Alternative: `library "Boost/Random.Uniform" namespace Boost.Random.Distributions;` - Combined: - Proposal: `package BoostRandom library "Uniform" namespace Distributions;` - Alternative: `library "Boost/Random.Uniform" namespace Boost.Random.Distributions;` - `import` changes: - Trivial: - Proposal: `import BoostRandom;` - Alternative: `import "Boost/Random";` - Multi-layer library: - Proposal: `import BoostRandom library "Uniform";` - Alternative: `import "Boost/Random.Uniform";` - Namespaces have no effect on `import` under both approaches. - Changes to use an imported entity: - Proposal: `BoostRandom.UniformDistribution` - Alternative: - If the code is in the `Boost.Random` namespace: `Uniform` - If the code is in the `Boost` package but a different namespace: `Random.Uniform` - If the code is outside the `Boost` package: `.Boost.Random.Uniform` We assume that the compiler will enforce that the root namespace must either match or be a prefix of the library name, followed by a `/` separator. For example, `Boost` in the namespace `Boost.Random.Uniform` must either match a `library "Boost"` or prefix as `library "Boost/..."`; `library "BoostRandom"` does not match because it's missing the `/` separator. There are several approaches which might remove this duplication, but each has been declined due to flaws: - We could have `library "Boost/Random.Uniform";` imply `namespace Boost`. However, we want name paths to use things listed as identifiers in files. We specifically do not want to use strings to generate identifiers in order to support understandability of code. - We could alternately have `namespace Boost;` syntax imply `library "Boost" namespace Boost;`. - This approach only helps with single-library namespaces. While this would be common enough that a special syntax would help some developers, we are likely to encourage multiple libraries per namespace as part of best practices. We would then expect that the quantity of libraries in multi-library namespaces would dominate cost-benefit, leaving this to address only an edge-case of duplication issues. - This would create an ambiguity between the file-level `namespace` and other `namespace` keyword use. We could then rename the `namespace` argument for `library` to something like `file-namespace`. - It may be confusing as to what `namespace Boost.Random;` does. It may create `library "Boost/Random"` because `library "Boost.Random"` would not be legal, but the change in characters may in turn lead to developer confusion. - We could change the library specification to use `.` instead of `/` as a separator, but that may lead to broader confusion about the difference between libraries and namespaces. Advantages: - Avoids introducing the "package" concept to code and name organization. - Retains the key property that library and namespace names have a prefix that is intended to be globally unique. - Avoids coupling package management to namespace structure. For example, it would permit a library collection like Boost to be split into multiple repositories and multiple distribution packages, while retaining a single top-level namespace. - The library and namespace are pushed to be more orthogonal concepts than packages and namespaces. - Although some commonality must still be compiler-enforced. - For the common case where packages have multiple libraries, removing the need to specify both a package and library collapses two keywords into one for both `import` and `package`. - It makes it easier to draw on C++ intuitions, because all the concepts have strong counterparts in C++. - The prefix `.` on imported name paths can help increase readability by making it clear they're from imports, so long as those imports aren't from the current top-level namespace. - Making the `.` optional for imports from the current top-level namespace eliminates the boilerplate character when calling within the same library. Disadvantages: - The use of a leading `.` to mark absolute paths may conflict with other important uses, such as designated initializers and named parameters. - Declines an opportunity to align code and name organization with package distribution. - Alignment means that if a developer sees `package BoostRandom library "Uniform";`, they know installing a package `BoostRandom` will give them the library. Declining this means that users seeing `library "Boost/Random.Uniform"`, they will still need to do research as to what package contains `Boost/Random.Uniform` to figure out how to install it because that package may not be named `Boost`. - Package distribution is a [project goal](/docs/project/goals.md#language-tools-and-ecosystem), and cannot be avoided indefinitely. - This also means multiple packages may contribute to the same top-level namespace, which would prevent things like tab-completion in IDEs from producing cache optimizations based on the knowledge that modified packages cannot add to a given top-level namespace. For example, the ability to load less may improve performance: - As proposed, a package `BoostRandom` only adds to a namespace of the same name. If a user is editing libraries in a package `BoostCustom`, then `BoostRandom` may be treated as unmodifiable. An IDE could optimize cache invalidation of `BoostRandom` at the package level. As a result, if a user types `BoostRandom.` and requests a tab completion, the system need only ensure that libraries from the `BoostRandom.` package are loaded for an accurate result. - Under this alternative, a library `Boost.Random` similarly adds to the namespace `Boost`. However, if a user is editing libraries, the IDE needs to support them adding to both `Boost` and `MyProject` simultaneously. As a result, if a user types `Boost.` and requests a tab completion, the system must have all libraries from all packages loaded for an accurate result. - Although many features can be restricted to _current_ imports, some features, such as [auto-imports](https://www.jetbrains.com/help/idea/creating-and-optimizing-imports.html), examine _possible_ imports. Large codebases may have a memory-constrained quantity of possible imports. - The string prefix enforcement between `library` and `namespace` forces duplication between both, which would otherwise be handled by `package`. - For the common case of packages with a matching namespace name, increases verbosity by requiring the `namespace` keyword. - The prefix `.` on imported name paths will be repeated frequently through code, increasing overall verbosity, versus the package approach which only affects import verbosity. - Making the `.` optional for imports from the current top-level namespace hides whether an API comes from the current library or an import. We are declining this approach because we desire package separation, and because of concerns that this will lead to an overall increase in verbosity due to the [preference for few child namespaces](/docs/design/code_and_name_organization/README.md#preference-for-few-child-namespaces), whereas this alternative benefits when `namespace` is specified more often. #### Different file type labels We're using `api` and `impl` for file types, and have `test` as an open question. We've considered using `interface` instead of `api`, but that introduces a terminology collision with interfaces in the type system. We've considered dropping `api` from naming, but that creates a definition from absence of a keyword. It also would be more unusual if both `impl` and `test` must be required, that `api` would be excluded. We prefer the more explicit name. We could spell out `impl` as `implementation`, but are choosing the abbreviation for ease of typing. We also don't think it's an unclear abbreviation. We expect `impl` to be used for implementations of `interface`. This isn't quite as bad as if we used `interface` instead of `api` because of the `api` export syntax on entities, such as `api fn DoSomething()`, which could create ambiguities as `interface fn DoSomething()`. It may still confuse people to see an `interface impl` in an `api` file. However, we're touching on related concepts and don't see a great alternative. #### Function-like syntax We could consider more function-like syntax for `import`, and possibly also `package`. For example, instead of: ```carbon import Math library "Stats"; import Algebra as A; ``` We could do: ```carbon import("Math", "Stats").Math; alias A = import("Algebra").Algebra; ``` Or some related variation. Advantages: - Allows straightforward reuse of `alias` for language consistency. - Easier to add more optional arguments, which we expect to need for [interoperability](/docs/design/code_and_name_organization/README.md#imports-from-other-languages) and [URLs](/docs/design/code_and_name_organization/README.md#imports-from-urls). - Avoids defining keywords for optional fields, such as `library`. - Interoperability and package management may add more fields long-term. Disadvantages: - It's unusual for a function-like syntax to produce identifiers for name lookup. - This could be addressed by _requiring_ alias, but that becomes verbose. - There's a desire to explicitly note the identifier being imported some way, as with `.Math` and `.Algebra` above. However, this complicates the resulting syntax. The preference is for keywords. #### Inlining from implementation files An implicit reason for keeping code in an `api` file is that it makes it straightforward to inline code from there into callers. We could explicitly encourage inlining from `impl` files as well, making the location of code unimportant during compilation. Alternately, we could add an `inline` file type which explicitly supports separation of inline code from the `api` file. Advantages: - Allows moving code out of the main API file for easier reading. Disadvantages: - Requires compilation of `impl` files to determine what can be inlined from the `api` file, leading to the transitive closure dependency problems which `impl` files are intended to avoid. We expect to only support inlining from `api` files in order to avoid confusion about dependency problems. #### Library-private access controls We currently have no special syntax for library-private APIs. However, non-exported APIs are essentially library-private, and may be in the `api` file. It's been suggested that we could either provide a special syntax or a new file type, such as `shared_impl`, to support library-private APIs. Advantages: - Allows for better separation of library-private APIs. Disadvantages: - Increases language complexity. - Dependencies are still an issue for library-private APIs. - If used from the `api` file, the dependencies are still in the transitive closure of client libraries, and any separation may confuse users about the downsides of the extra dependencies. - If only used from `impl` files, then they could be in the `impl` file if there's only one, or shared from a separate library. - Generalized access controls may provide overlapping functionality. At this point in time, we prefer not to provide specialized access controls for library-private APIs. #### Managing API versus implementation in libraries At present, we plan to have `api` versus `impl` as a file type, and also `.carbon` versus `.impl.carbon` as the file extension. We chose to use both together, rather than one or the other, because we expect some parties to strongly want file content to be sufficient for compilation, while others will want file extensions to be meaningful for the syntax split. Instead of the file type split, we could drift further and instead have APIs in any file in a library, using the same kind of [API markup](/docs/design/code_and_name_organization/README.md#exporting-entities-from-an-api-file). Advantages: - May help users who have issues with cyclical code references. - Improves compiler inlining of implementations, because the compiler can decide how much to actually put in the generated API. Disadvantages: - While allowing users to spread a library across multiple files can be considered an advantage, we see the single API file as a way to pressure users towards smaller libraries, which we prefer. - May be slower to compile because each file must be parsed once to determine APIs. - For users that want to see _only_ APIs in a file, they would need to use tooling to generate the API file. - Auto-generated documentation may help solve this problem. #### Multiple API files The proposal also presently suggests a single API file. Under an explicit API file approach, we could still allow multiple API files. Advantages: - More flexibility when writing APIs; could otherwise end up with one gigantic API file. Disadvantages: - Encourages larger libraries by making it easier to provide large APIs. - Removes some of the advantages of having an API file as a "single place" to look, suggesting more towards the markup approach. - Not clear if API files should be allowed to depend on each other, as they were intended to help resolve cyclical dependency issues. We particularly want to discourage large libraries, and so we're likely to retain the single API file limit. #### Name paths as library names We're proposing strings for library names. We've discussed also using name paths (`My.Library`) and also restricting to single identifiers (`Library`). Advantages: - Shares the form between packages (identifiers) and namespaces (name paths). - Enforces a constrained set of names for libraries for cross-package consistency of naming. Disadvantages: - Indicates that a library may be referred to in code, when only the package and namespace are used for name paths of entities. - The constrained set of names may also get in the way for some packages that can make use of more flexibility in naming. We've decided to use strings primarily because we want to draw the distinction that a library is not something that's used when referring to an entity in code. ### Imports #### Block imports Rather than requiring an `import` keyword per line, we could support block imports, as can be found in languages like Go. In other words, instead of: ```carbon import Math; import Geometry; ``` We could have: ```carbon imports { Math, Geometry, } ``` Advantages: - Allows repeated imports with less typing. Disadvantages: - Makes it harder to find files importing a package or library using tools like `grep`. One concern has been that a mix of `import` and `imports` syntax would be confusing to users: we should only allow one. This alternative has been declined because retyping `import` statements is low-cost, and `grep` is useful. #### Block imports of libraries of a single package We could allow block imports of libraries from the same package. For example: ```carbon import Containers libraries({ "FlatHashMap", "FlatHashSet", }) ``` The result of this `api alias` allowing `Containers.HashSet()` to work regardless of whether `HashSet` is in `"HashContainers"` or `"Internal"` may be clearer if both `import Containers` statements were a combined `import Containers libraries({"HashContainers", "Internal"});`. The advantages/disadvantages are similar to [block imports](#block-imports). Additional advantages/disadvantages are: Advantages: - If we limit to one import per library, then any `alias` of the package `Containers` is easier to understand as affecting all libraries. Disadvantages: - If we allow both `library` and `libraries` syntax, it's two was of doing the same thing. - Can be addressed by _always_ requiring `libraries`, removing `library`, but that diverges from `package`'s `library` syntax. This alternative has been declined for similar reasons to block imports; the additional advantages/disadvantages don't substantially shift the cost-benefit argument. #### Broader imports, either all names or arbitrary code Carbon imports require specifying individual names to import. We could support broader imports, for example by pulling in all names from a library. In C++, the `#include` preprocessor directive even supports inclusion of arbitrary code. For example: ```carbon import Geometry library "Shapes" names *; // Triangle was imported as part of "*". fn Draw(var Triangle: x) { ... } ``` Advantages: - Reduces boilerplate code specifying individual names. Disadvantages: - Loses out on parser benefits of knowing which identifiers are being imported. - Increases the risk of adding new features to APIs, as they may immediately get imported by a user and conflict with a preexisting name, breaking code. - As the number of imports increases, it can become difficult to tell which import a particular symbol comes from, or how imports are being used. - Arbitrary code inclusion can result in unexpected code execution, a way to create obfuscated code and a potential security risk. We particularly value the parser benefits of knowing which identifiers are being imported, and so we require individual names for imports. #### Direct name imports We could allow direct imports of names from libraries. For example, under the current setup we might see: ```carbon import Math library "Stats"; alias Median = Stats.Median; alias Mean = Stats.Mean; ``` We could simplify this syntax by augmenting `import`: ```carbon import Math library "Stats" name Median; import Math library "Stats" name Mean; ``` Or more succinctly with block imports of names: ```carbon import Math library "Stats" names { Median, Mean, } ``` Advantages: - Avoids an additional `alias` step. Disadvantages: - With a single name, this isn't a significant improvement in syntax. - With multiple names, this runs into similar issues as [block imports](#block-imports). #### Optional package names We could allow a short syntax for imports from the current library. For example, this code imports `Geometry.Shapes`: ```carbon package Geometry library "Operations" api; import library "Shapes"; ``` Advantages: - Reduces typing. Disadvantages: - Makes it harder to find files importing a package or library using tools like `grep`. - Creates two syntaxes for importing libraries from the current package. - If we instead disallow `import Geometry library "Shapes"` from within `Geometry`, then we end up with a different inconsistency. Overall, consistent with the decision to disallow [block imports](#block-imports), we are choosing to require the package name. ### Namespaces #### File-level namespaces We are providing entity-level namespaces. This is likely necessary to support migrating C++ code, at a minimum. It's been discussed whether we should _also_ support file-level namespaces. For example, this is the current syntax for defining `Geometry.Shapes.Circle`: ```carbon package Geometry library "Shapes" api; namespace Shapes; struct Shapes.Circle; ``` This is the proposed alternative syntax for defining `Geometry.Shapes.Circle`, and would put all entities in the file under the `Shapes` namespace: ```carbon package Geometry library "Shapes" namespace Shapes api; struct Circle; ``` Advantages: - Reduces repetitive syntax in the file when every entity should be in the same, child namespace. - Large libraries and packages are more likely to be self-referential, and may pay a disproportionate ergonomics tax that others wouldn't see. - Although library authors could also avoid this repetitive syntax by omitting the namespace, that may in turn lead to more name collisions for large packages. - Note that syntax can already be reduced with a shorter namespace alias, but the redundancy cannot be _eliminated_. - Reduces the temptation of aliasing in order to reduce verbosity, wherein it's generally agreed that aliasing creates inconsistent names which hinder readability. - Users are known to alias long names, where "long" may be considered anything over six characters. - This is a risk for any package that uses namespaces, as importers may also need to address it. Disadvantages: - Encourages longer namespace names, as they won't need to be retyped. - Increases complexity of the `package` keyword. - Creates two ways of defining namespaces, and reuses the `namespace` keyword in multiple different ways. - We generally prefer to provide one canonical way of doing things. - Does not add functionality which cannot be achieved with entity-level namespaces. However, the converse is not true: entity-level control allows a single file to put entities into multiple namespaces. - Creates a divergence between code as written by the library maintainer and code as called. - Calling code would need to specify the namespace, even if aliased to a shorter name. Library code gets to omit this, essentially getting a free alias. We are choosing not to provide this for now because we want to provide the minimum necessary support, and then see if it works out. It may be added later, but it's easier to add features than to remove them. #### Scoped namespaces Instead of including additional namespace information per-name, we could have scoped namespaces, similar to C++. For example: ```carbon namespace absl { namespace numbers_internal { fn SafeStrto32Base(...) { ... } } fn SimpleAtoi(...) { ... return numbers_internal.SafeStrto32Base(...); ... } } ``` Advantages: - Makes it easy to write many things in the same namespace. Disadvantages: - It's not clear which namespace an identifier is in without scanning to the start of the file. - It can be hard to find the end of a namespace. For examples addressing this, end-of-namespace comments are called for by both the [Google](https://google.github.io/styleguide/cppguide.html#Namespaces) and [Boost](https://github.com/boostorg/geometry/wiki/Guidelines-for-Developers) style guides. - Carbon may disallow the same-line-as-code comment style used for this. Even if not, if we acknowledge it's a problem, we should address it structurally for [readability](/docs/project/goals.md#code-that-is-easy-to-read-understand-and-write). - This is less of a problem for other scopes, such as functions, because they can often be broken apart until they fit on a single screen. There are other ways to address the con, such as adding syntax to indicate the end of a namespace, similar to block comments. For example: ```carbon { namespace absl { namespace numbers_internal fn SafeStrto32Base(...) { ... } } namespace numbers_internal fn SimpleAtoi(...) { ... return numbers_internal.SafeStrto32Base(...); ... } } namespace absl ``` While we could consider such alternative approaches, we believe the proposed contextless namespace approach is better, as it reduces information that developers will need to remember when reading/writing code. ## Rationale This proposal provides an organizational structure that seems both workable and aligns well with Carbon's goals: - Distinct and required top-level namespace -- "package"s from the proposal -- both matches software best practices for long-term evolution, and avoids complex and user-confusing corner cases. - Providing a fine-grained import structure as provided by the "library" concept supports scalable build system implementations while ensuring explicit dependencies. - The structured namespace facilities provide a clear mechanism to migrate existing hierarchical naming structures in C++ code. ### Open questions #### Should we switch to a library-oriented structure that's package-agnostic? - **Decision:** No. - **Rationale:** While this would simplify the overall set of constructs needed, removing the concept of a global namespace remained desirable and would require re-introducing much of the complexity around top-level namespaces. Overall, the simplification trade-off didn't seem significantly better. #### Should there be a tight association between file paths and packages/libraries? - **Decision:** Yes, for the API files in libraries. Specifically, the library name should still be written in the source, but it should be checked to match -- after some platform-specific translation -- against the path. - **Note:** Sufficient restrictions to result in a portable and simple translation on different filesystems should be imposed, but the Core team was happy for these restrictions to be developed as part of implementation work. - **Rationale:** This will improve usability and readability for users by making it obvious how to find the files that are being imported. Similarly, this will improve tooling by increasing the ease with which tools can find imported APIs. ================================================ FILE: proposals/p0113.md ================================================ # Add a C++ style guide [Pull request](https://github.com/carbon-language/carbon-lang/pull/113) ## Table of contents - [Problem](#problem) - [Background](#background) - [Proposal](#proposal) - [Details](#details) - [Rationale](#rationale) - [Alternatives considered](#alternatives-considered) - [Different variations on the baseline of the Google C++ style guide](#different-variations-on-the-baseline-of-the-google-c-style-guide) - [Use exceptions](#use-exceptions) - [Format functions with one-per-line arguments and parameters](#format-functions-with-one-per-line-arguments-and-parameters) - [Place `*` and `&` with the variable in pointer declarations](#place--and--with-the-variable-in-pointer-declarations) - [Place `const` after the type](#place-const-after-the-type) - [Use the LLVM coding standards](#use-the-llvm-coding-standards) - [Rationale](#rationale-1) ## Problem When writing C++ code for Carbon, we want to keep all of our code consistent, easy to learn, and help avoid spending undue code review time arguing about the same core style and idiomatic issues. ## Background - [C++ Coding Standards](https://dl.acm.org/doi/book/10.5555/1036281) - [C++ Core Guidelines](http://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines) - [Chromium C++ style guide](https://chromium.googlesource.com/chromium/src/+/master/styleguide/c++/c++.md) - [Google C++ Style Guide](https://google.github.io/styleguide/cppguide.html) - [Joint Strike Fighter Air Vehicle C++ Coding Standards](https://stroustrup.com/JSF-AV-rules.pdf) - [LLVM Coding Standards](https://llvm.org/docs/CodingStandards.html) - [Mozilla C++ Style Guide](https://firefox-source-docs.mozilla.org/code-quality/coding-style/coding_style_cpp.html) - [Programming style](https://en.wikipedia.org/wiki/Programming_style) - [The Elements of Programming Style](https://dl.acm.org/doi/book/10.5555/578130) - [WebKit Code Style Guidelines](https://webkit.org/code-style-guidelines/) ## Proposal I propose to use the Google C++ style guide as a baseline, and then make minimal, focused additions and adjustments to it to suit the needs of Carbon. ## Details The Google C++ style guide is widely used even outside of Google. Both Mozilla and Chromium use it as their base style guide. It is both comprehensive and specific, providing a strong foundation with wide coverage. However, there are a small number of places that seem worth adjusting. I propose to use the Google style guide as a baseline and then have a minimal, and focused list of changes and clarifications. The most expensive cases are those where we _actively diverge_ from this baseline. Doing this outside of mechanical formatting issues would require careful thought and justification that seems likely to have a higher cost than the benefit. I propose focusing only on specific places where there is a direct _benefit_ to Carbon by diverging. The only place where I suggest this is the naming convention. The convention suggested by Google's style guide is more complex than necessary, in part due to its desire to not invalidate a large historical codebase. That is not a concern for Carbon, and there is a specific advantage: using and learning how the proposed naming convention for Carbon itself works in practice. Thus, the [proposal](/docs/project/cpp_style_guide.md) is to replace the naming conventions suggested by the Google style guide with those suggested for Carbon. The remaining suggested modifications simply consist of selecting one of several allowed options in order to increase consistency and modernness. Many of the cases allowing multiple options only exist to retain consistency with an existing code base that isn't a factor for Carbon. I suggest we take advantage of this to have a more consistent and modern style. See the proposed [section](/docs/project/cpp_style_guide.md#carbon-local-guidance) for details. ### Rationale Some aspects of this proposal warrant specific rationale. ## Alternatives considered ### Different variations on the baseline of the Google C++ style guide #### Use exceptions Advantages: - Better aligned with standard C++, Boost, and some other C++ communities. Disadvantages: - Significant performance problems, especially of data structures that need to provide strong exception safety guarantees. - LLVM does not use exceptions and is not exception safe. #### Format functions with one-per-line arguments and parameters Note: this is likely a bikeshed that we should not invest significant time debating. Advantages: - Simpler for editors and authors of code to get correct. - Minimizes diffs during refactorings. - Regular horizontal rhythm (independent of name lengths) and no hard-to-spot additional parameters on the right, which for some subset of readers improves readability. Disadvantages: - A non-trivial change from Google's style guide. - Readability problems caused by bin packing of function parameters and arguments are typically better solved by factoring (either into variables or option structs) than a formatting change. We should not have interfaces that are hard to read due to their number of parameters. - Increases vertical space of code, which for some subset of readers is expensive, and for them may outweigh any benefit of the regular horizontal rhythm. #### Place `*` and `&` with the variable in pointer declarations Note: this is likely a bikeshed that we should not invest significant time debating. Advantages: - This spacing matches the language grammar more closely than the alternative spacing. - Declaration syntax parallels the usage syntax. Given `int *p;`, the type of `*p` is `int`. - Enables coherent declaration of multiple variables in a single declaration. - However, even with this formatting these declarations are hard to read and should not be used. Disadvantages: - Many people think of the declaration structure as ` ;` and the `*` is part of the type. - However, that intuitive understanding doesn't generalize to a few different constructs in C++. For example: arrays and function pointers. #### Place `const` after the type Note: this is likely a bikeshed that we should not invest significant time debating. Advantages: - Makes multiple layers of `const` alternating with pointers or references more obviously readable. - However, using type aliases to avoid multiple layers of `const` often provides even better readability. Disadvantages: - For declarations without any pointer in the type, `const ;` is much more conventional. - And most declarations are of this form. ### Use the LLVM coding standards Carbon will end up heavily using LLVM and Clang for its reference implementation. This will both involve interfacing with those APIs and heavily using libraries from those projects. We could adopt the LLVM coding standards to gain consistency with those APIs. Advantages: - Consistent coding style between Carbon code and any LLVM or Clang APIs used. - If it eventually becomes desirable to contribute Carbon's reference implementation to the LLVM project, it would avoid updating the code to adhere to the relevant coding standards. Disadvantages: - The LLVM coding standards are neither precise nor specific on a number of points. They leave a large number of issues to the judgement of code reviewers to stay consistent with the LLVM codebase. This isn't practical for Carbon nor is it an efficient policy. - LLVM and Clang don't _consistently_ follow the standards either, making it impossible to have complete consistency with them. - Contributing the reference implementation to LLVM seems unlikely and necessarily at least a year away which minimizes any concerns around style matching. - LLVM has not yet adopted C++17 due to existing users who still need C++14. However, Carbon has no such need to constrain its version of C++ and can effectively adopt and use more modern C++ versions. ## Rationale A common, established style guide will allow us to focus on the more important aspects of coding and code review. Once we're familiar with the rules, their consistent application will result in a more readable codebase, and rules that can largely be automated by clang-format will result in a more efficient development process. This particular ruleset attempts to align with our expected lexical rules for the Carbon language (for example, comment syntax and capitalization rules), which will also mean that we can use a largely consistent style between the aspects of the toolchain implemented in C++ and the aspects implemented in Carbon. ================================================ FILE: proposals/p0120.md ================================================ # Add idiomatic code performance and developer-facing docs to goals [Pull request](https://github.com/carbon-language/carbon-lang/pull/120) ## Table of contents - [Problem](#problem) - [Proposal](#proposal) - [Justification](#justification) - [Alternatives considered](#alternatives-considered) - [Use a principle to address performance of idiomatic code](#use-a-principle-to-address-performance-of-idiomatic-code) - [Rationale](#rationale) ## Problem [Issue #106](https://github.com/carbon-language/carbon-lang/issues/106) raises a few small issues with the goals doc as approved. Some are small, but in particular I'll emphasize the question: > Do we want to say anything about "reasonably fast by default" or "favors > constructs that can be compiled to efficient code" or something like that? I think this is something that we clearly want for performance, and should be laid out. Additionally, while considering justification for changes in [PR 80](https://github.com/carbon-language/carbon-lang/pull/80), I noted there is no explicit goal to provide developer-facing documentation. I believe this is an intended part of the community goals, and could be inferred from current ecosystem text, but may be better if explicit. ## Proposal Add paragraphs to address performance of idiomatic code and developer-facing documentation, as well as making other small fixes. ## Justification Performance of idiomatic code: Under "Performance-critical software", we establish that it should be possible to write high-performance code with Carbon. However, if taken strictly, we could be saying something like "it's okay if idiomatic code is predictably slow, as long as developers have tools to 'open up the hood'." That is not the intent, and so addressing the case of routine code performance offers the reassurance that Carbon will prioritize performance consistently, regardless of whether performance tuning is done. Developer-facing documentation: "Language tools and ecosystem" addresses the specification and tooling explicitly. However, developer-facing documentation is also part of the ecosystem, and part of supporting ramp-up training by new Carbon developers. Such documentation should be an explicit project priority. Other changes are incremental improvements to the goal text, and mainly presented in this change for consistency of review. ## Alternatives considered ### Use a principle to address performance of idiomatic code A principle is another way of addressing non-obvious conclusions based on goals. However, performance of idiomatic code seems quick to state, and not worth splitting off to a separate doc. I believe the cost-benefit favors keeping it in the goals doc. ## Rationale This addresses a number of rough edges and small missing pieces in the original proposal, providing useful clarification. This follow up is expected as part of the launch and iterate process we use to keep our velocity up. User-facing documentation and speed-by-default should be first-order priorities for Carbon. ================================================ FILE: proposals/p0140.md ================================================ # Create initial rough framework for specification [Pull request](https://github.com/carbon-language/carbon-lang/pull/140) ## Table of contents ## Table of contents - [Problem](#problem) - [Proposal](#proposal) - [Details](#details) - [Conventions](#conventions) - [Alternatives considered](#alternatives-considered) - [Maintain the specification in a different language.](#maintain-the-specification-in-a-different-language) ## Problem We need a rough layout for our specification so that we can start adding details to it once they're decided. ## Proposal Split the specification into a language and a library section. In the language section, use one file per broad area of functionality. Divide the language up based on the intended layering of the language design. For now, maintain the specification sources in Markdown. ## Details Proposed top-level structure of the `spec/` directory as of this pull request: - `README.md` Introduction to the specification - `lang` - `README.md` Language specification overview and basics - `execution.md` Execution semantics - `lex.md` Lexical analysis - `libs.md` Libraries and packages - `names.md` Names and name binding / lookup - `parsing.md` Parsing - `semantics.md` Semantic analysis - `lib` - `README.md` Library specification overview and basics This is only a starting point; the structure should be expected to change and grow as the specification is filled out. Most of the proposed files are empty or nearly-empty placeholders. ### Conventions All paragraphs within the specification are numbered so that they can be referenced more easily. Defined terms are introduced in italics. Hyperlinks between sections of the specification are used liberally. ## Alternatives considered ### Maintain the specification in a different language. Advantages: - An alternative language may provide better support for custom typesetting, representing grammars, linking to definitions, and so on. Disadvantages: - Using a different language would add complexity and inconsistency to our documentation. - There is unlikely to be any existing documentation language that is well-suited to our needs without significant customization. - Conversion from a more sophisticated language is likely to be more complex than converting from Markdown. Conversion of Markdown to another language at a later point (either manually or using a tool like Sphinx) is expected to remain a relatively low-cost option, due to the relative simplicity of Markdown-formatted documents. ================================================ FILE: proposals/p0142.md ================================================ # Unicode source files [Pull request](https://github.com/carbon-language/carbon-lang/pull/142) ## Table of contents - [Problem](#problem) - [Background](#background) - [Proposal](#proposal) - [Details](#details) - [Character encoding](#character-encoding) - [Source files](#source-files) - [Normalization](#normalization) - [Characters in identifiers and whitespace](#characters-in-identifiers-and-whitespace) - [Homoglyphs](#homoglyphs) - [Alternatives considered](#alternatives-considered) - [Character encoding](#character-encoding-1) - [Byte order marks](#byte-order-marks) - [Normalization forms](#normalization-forms) - [Rationale](#rationale) ## Problem Portable use and maintenance of Carbon source files requires a common understanding of how they are encoded on disk. Further, the decision as to what characters are valid in names and what constitutes whitespace are a complex area in which we do not expect to have local expertise. ## Background [Unicode](https://www.unicode.org/versions/latest/) is a universal character encoding, maintained by the [Unicode Consortium](https://home.unicode.org/basic-info/overview/). It is the canonical encoding used for textual information interchange across all modern technology. The [Unicode Standard Annex 31](https://www.unicode.org/reports/tr31/), "Unicode Identifier and Pattern Syntax", provides recommendations for the use of Unicode in the definitions of general-purpose identifiers. ## Proposal Carbon programs are represented as a sequence of Unicode code points. Carbon source files are encoded in UTF-8. Carbon will follow lexical conventions for identifiers and whitespace based on Unicode Annex 31. ## Details ### Character encoding Before being divided into tokens, a program starts as a sequence of Unicode code points -- integer values between 0 and 10FFFF16 -- whose meaning as characters or non-characters is defined by the Unicode standard. Carbon is based on Unicode 13.0, which is currently the latest version of the Unicode standard. Newer versions should be considered for adoption as they are released. ### Source files Program text can come from a variety of sources, such as an interactive programming environment (a so-called "Read-Evaluate-Print-Loop" or REPL), a database, a memory buffer of an IDE, or a command-line argument. The canonical representation for Carbon programs is in files stored as a sequence of bytes in a file system on disk, and such files are expected to be encoded in UTF-8. Such files may begin with an optional UTF-8 BOM, that is, the byte sequence EF16,BB16,BF16. This prefix, if present, is ignored. Regardless of how program text is concretely stored, the first step in processing any such text is to convert it to a sequence of Unicode code points -- although such conversion may be purely notional. The result of this conversion is a Carbon _source file_. Depending on the needs of the language, we may require each such source file to have an associated filename, even if the source file does not originate in anything resembling a file system. ### Normalization Background: - [wikipedia article on Unicode normal forms](https://en.wikipedia.org/wiki/Unicode_equivalence#Normal_forms) - [Unicode Standard Annex #15: Unicode Normalization Forms](https://www.unicode.org/reports/tr15/tr15-50.html) Carbon source files, including comments and string literals, are required to be in Unicode Normalization Form C ("NFC"). The Carbon source formatting tool will convert source files to NFC as necessary to satisfy this constraint. The choice to require NFC is really four choices: 1. Equivalence classes: we use a canonical normalization form rather than a compatibility normalization form or no normalization form at all. - If we use no normalization, invisibly-different ways of representing the same glyph, such as with pre-combined diacritics versus with diacritics expressed as separate combining characters, or with combining characters in a different order, would be considered different characters. - If we use a canonical normalization form, all ways of encoding diacritics are considered to form the same character, but ligatures such as `ffi` are considered distinct from the character sequence that they decompose into. - If we use a compatibility normalization form, ligatures are considered equivalent to the character sequence that they decompose into. For a fixed-width font, a canonical normalization form is most likely to consider characters to be the same if they look the same. Unicode annexes [UAX#15](https://www.unicode.org/reports/tr15/tr15-18.html#Programming%20Language%20Identifiers) and [UAX#31](https://www.unicode.org/reports/tr31/tr31-33.html#normalization_and_case) both recommend the use of Normalization Form C for case-sensitive identifiers in programming languages. See also the discussion of [homoglyphs](#homoglyphs) below. 2. Composition: we use a composed normalization form rather than a decomposed normalization form. For example, `ō` is encoded as U+014D (LATIN SMALL LETTER O WITH MACRON) in a composed form and as U+006F (LATIN SMALL LETTER O), U+0304 (COMBINING MACRON) in a decomposed form. The composed form results in smaller representations whenever the two differ, but the decomposed form is a little easier for algorithmic processing (for example, typo correction and homoglyph detection). 3. We require source files to be in our chosen form, rather than converting to that form as necessary. 4. We require that the entire contents of the file be normalized, rather than restricting our attention to only identifiers, or only identifiers and string literals. ### Characters in identifiers and whitespace We will largely follow [Unicode Annex 31](https://www.unicode.org/reports/tr31/) in our selection of identifier and whitespace characters. This Annex does not provide specific rules on lexical syntax, instead providing a framework that permits a selection of choices of concrete rules. The framework provided by Annex 31 includes suggested sets of characters that may appear in identifier, including uppercase and lowercase ASCII letters, along with reasonable extensions to many non-ASCII letters, with some characters restricted to not appear as the first character. For example, this list includes U+30EA (KATAKANA LETTER RI), but not U+2603 (SNOWMAN), both of which are permitted in identifiers in C++20. Similarly, it indicates which characters should be classified as whitespace, including all the ASCII whitespace characters plus some non-ASCII whitespace characters. It also supports language-specific "profiles" to alter these baseline character sets for the needs of a particular language -- for example, to permit underscores in identifiers, or to include non-breaking spaces as whitespace characters. This proposal does not specify concrete choices for lexical rules, nor that we will not deviate from conformance to Annex 31 in any concrete area. We may find cases where we wish to take a different direction than that of the Annex. However, we should use Annex 31 as a basis for our decisions, and should expect strong justification for deviations from it. Note that this aligns with the current direction for C++, as described in WG21 paper [P1949R6](http://wg21.link/P1949R6). #### Homoglyphs The sets of identifier characters suggested by Annex 31's `ID_Start` / `XID_Start` / `ID_Continue` / `XID_Continue` characters include many pairs of homoglyphs and near-homoglyphs -- characters that would be interpreted differently but may render identically or very similarly. This problem would also be present if we restricted the character set to ASCII -- for example, `kBa11Offset` and `kBall0ffset` may be very hard to distinguish in some fonts -- but there are many more ways to introduce such problems with the broader identifier character set suggested by Annex 31. One way to handle this problem would be by adding a restriction to name lookup: if a lookup for a name is performed in a scope and that lookup would have found nothing, but there is a confusable identifier, as defined by [UAX#39](http://www.unicode.org/reports/tr39/#Confusable_Detection), in the same scope, the program is ill-formed. However, this idea is only provided as weak guidance to future proposals and to demonstrate that UAX#31's approach is compatible with at least one possible solution for the homoglyph problem. The concrete rules for handling homoglyphs are considered out of scope for this proposal. ## Alternatives considered There are a number of different design choices we could make, as divergences from the above proposal. Those choices, along with the arguments that led to choosing the proposed design rather than each alternative, are presented below. ### Character encoding We could restrict programs to ASCII. Pro: - Reduced implementation complexity. - Avoids all problems relating to normalization, homoglyphs, text directionality, and so on. - We have no intention of using non-ASCII characters in the language syntax or in any library name. - Provides assurance that all names in libraries can reliably be typed by all developers -- we already require that keywords, and thus all ASCII letters, can be typed. Con: - An overarching goal of the Carbon project is to provide a language that is inclusive and welcoming. A language that does not permit names and comments in programs to be expressed in the developer's native language will not meet that goal for at least some of our developers. - Quoted strings will be substantially less readable if non-ASCII printable characters are required to be written as escape sequences. ### Byte order marks We could disallow byte order marks. Pro: - Marginal implementation simplicity. Con: - Several major editors, particularly on the Windows platform, insert UTF-8 BOMs and use them to identify file encoding. ### Normalization forms We could require a different normalization form. Pro: - Some environments might more naturally produce a different normalization form. - Normalization Form D is more uniform, in that characters are always maximally decomposed into combining characters; in NFC, characters may or may not be decomposed depending on whether a composed form is available. - NFD may be more suitable for certain uses such as typo correction, homoglyph detection, or code completion. Con: - The C++ standard and community is moving towards using NFC: - WG21 is in the process of adopting a NFC requirement for C++ identifiers. - GCC warns on C++ identifiers that aren't in NFC. As a consequence, we should expect that the tooling and development environments that C++ developers are using will provide good support for authoring NFC-encoded source files. - The W3C recommends using NFC for all content, so code samples distributed on webpages may be canonicalized into NFC by some web authoring tools. - NFC produces smaller encodings than NFD in all cases where they differ. We could require no normalization form and compare identifiers by code point sequence. Pro: - This is the rule in use in C++20 and before. Con: - This is not the rule planned for the near future of C++. - Different representations of the same character may result in different identifiers, in a way that is likely to be invisible in most programming environments. We could require no normalization form, and normalize the source code ourselves: Pro: - We would treat source text identically regardless of the normalization form. - Developers would not be responsible for ensuring that their editing environment produces and preserves the proper normalization form. Con: - There is substantially more implementation cost involved in normalizing identifiers than in detecting whether they are in normal form. While this proposal would require the implementation complexity of converting into NFC in the formatting tool, it would not require the conversion cost to be paid during compilation. A high-quality implementation may choose to accept this cost anyway, in order to better recover from errors. Moreover, it is possible to [detect NFC on a fast path](http://unicode.org/reports/tr15/#NFC_QC_Optimization) and do the conversion only when necessary. However, if non-canonical source is formally valid, there are more stringent performance constraints on such conversion than if it is only done for error recovery. - Tools such as `grep` do not perform normalization themselves, and so would be unreliable when applied to a codebase with inconsistent normalization. - GCC already diagnoses identifiers that are not in NFC, and WG21 is in the process of adopting an [NFC requirement for C++ identifiers](http://wg21.link/P1949R6), so development environments should be expected to increasingly accommodate production of text in NFC. - The byte representation of a source file may be unstable if different editing environments make different normalization choices, creating problems for revision control systems, patch files, and the like. - Normalizing the contents of string literals, rather than using their contents unaltered, will introduce a risk of user surprise. We could require only identifiers, or only identifiers and comments, to be normalized, rather than the entire input file. Pro: - This would provide more freedom in comments to use arbitrary text. - String literals could contain intentionally non-normalized text in order to represent non-normalized strings. Con: - Within string literals, this would result in invisible semantic differences: strings that render identically can have different meanings. - The semantics of the program could vary if its sources are normalized, which an editing environment might do invisibly and automatically. - If an editing environment were to automatically normalize text, it would introduce spurious diffs into changes. - We would need to be careful to ensure that no string or comment delimiter ends with a code point sequence that is a prefix of a decomposition of another code point, otherwise different normalizations of the same source file could tokenize differently. ## Rationale We need to specify the source file encoding in order to move on to higher-level lexical issues, and the proposal generally gives sound rationales for the specific choices it makes. UTF-8 and Unicode Annex 31 are non-controversial choices for a new language in 2020. The proposal provides a good rationale for requiring NFC. We support treating homoglyphs and confusability as out of scope. We favor of accepting of a wide range of scripts to start, in order to be welcoming to as wide a community as possible, assuming that underhanded code concerns can be adequately addressed outside of the compiler proper. ================================================ FILE: proposals/p0143.md ================================================ # Numeric literals [Pull request](https://github.com/carbon-language/carbon-lang/pull/143) ## Table of contents - [Problem](#problem) - [Background](#background) - [Proposal](#proposal) - [Details](#details) - [Integer literals](#integer-literals) - [Real number literals](#real-number-literals) - [Ties](#ties) - [Digit separators](#digit-separators) - [Open question: digit separator placement](#open-question-digit-separator-placement) - [Alternatives considered](#alternatives-considered) - [Integer bases](#integer-bases) - [Octal literals](#octal-literals) - [Decimal literals](#decimal-literals) - [Case sensitivity](#case-sensitivity) - [Real number syntax](#real-number-syntax) - [Digit separator syntax](#digit-separator-syntax) - [Rationale](#rationale) - [Painter rationale](#painter-rationale) - [Open questions](#open-questions) ## Problem This proposal specifies lexical rules for numeric constants in Carbon. ## Background We wish to cover literals for two categories of types: - Integer types, that can represent some (typically contiguous) subset of the integers, ℤ. - Real number types, that can represent some [discrete](https://en.wikipedia.org/wiki/Isolated_point) subset of the real numbers, ℝ. (Typically only rational numbers can be represented, but that doesn't matter for our purposes.) Real number types may include additional values (infinities and NaN values). We do not provide a notation to express such values. In C++, the following syntaxes are used: - Integer literals - `12345` (decimal) - `0x1FE` (hexadecimal) - `0123` (octal) - `0b1010` (binary) - Real number literals - Decimal - `123.` - `.123` - `123.456` - `123.e456` (= 123 \* 10456) - `.123e456` - `123.456e789` - `123e456` (no decimal point) - Any of the above with a `+` or `-` after `e`. - Hexadecimal - `0x123.p456` (= 12316 \* 2456) - `0x.123p456` - `0x123.456p789` - `0x123p456` (no hexadecimal point) - Any of the above with a `+` or `-` after `p`. - Digit separators (`'`) may appear between any two digits - An optional suffix defines the type - `U` (`unsigned`) and `L` (`long`) or `LL` (`long long`) for integers (order-independent, but `LUL` disallowed) - `F` (`float`) or `L` (`long double`) for real numbers - User-defined literals may have custom suffixes, starting with `_` for non-standard-library literals. C++ numeric literals are case-insensitive, except in the suffix of a user-defined literal. Negative numbers are formed by applying a unary `-` operator to a non-negative literal. The type of a literal in C++ depends primarily on its syntax and its suffix. However, for integer literals, the type also depends on the value; the language rules attempt to pick a type large enough to fit the value. An `unsigned` type is always used if a `U` suffix is present, is never used for a decimal literal without a `U` suffix, and otherwise may or may not be used depending on whether the value happens to fit into an unsigned type but not into a signed type of the same width. Other languages use somewhat different rules, but the broad lexical structure above -- an optional prefix for the base, a value, an optional exponent, and an optional suffix -- is common across a large number of languages. ## Proposal We allow these syntaxes: - Integer literals - `12345` (decimal) - `0x1FE` (hexadecimal) - `0b1010` (binary) - Real number literals - `123.456` (digits on both sides of the `.`) - `123.456e789` (optional `+` or `-` after the `e`) - `0x1.2p123` (optional `+` or `-` after the `p`) - Digit separators (`_`) may be used, but only in conventional locations Note that real number literals always contain a `.` with digits on both sides, and integer literals never contain a `.`. Literals are case-sensitive. No support is proposed for literals with type suffixes, but without prejudice: this proposal proposes neither the inclusion nor the absence of such literals. ## Details ### Integer literals Decimal integers are written as a non-zero decimal digit followed by zero or more additional decimal digits, or as a single `0`. Integers in other bases are written as a `0` followed by a base specifier character, followed by a sequence of digits in the corresponding base. The available base specifiers and corresponding bases are: | Base specifier | Base | Digits | | -------------- | ---- | ------------------------ | | `b` | 2 | `0` and `1` | | `x` | 16 | `0` ... `9`, `A` ... `F` | The above table is case-sensitive. For example, `0b1` and `0x1A` are valid, and `0B1`, `0X1A`, and `0x1a` are invalid. A zero at the start of a literal can never be followed by another digit: either the literal is `0`, the `0` begins a base specifier, or the next character is a decimal point (see below). ### Real number literals Real numbers are written as a decimal or hexadecimal integer followed by a period (`.`) followed by a sequence of one or more decimal or hexadecimal digits, respectively. A digit is required on each side of the period. `0.` and `.3` are both invalid. A real number can be followed by an exponent character, an optional `+` or `-` (defaulting to `+` if absent), and a character sequence matching the grammar of a decimal integer with some value _N_. For a decimal real number, the exponent character is `e`, and the effect is to multiply the given value by 10±_N_. For a hexadecimal real number, the exponent character is `p`, and the effect is to multiply the given value by 2±_N_. The exponent suffix is optional for both decimal and hexadecimal real numbers. Note that a decimal integer followed by `e` is not a real number literal. For example, `3e10` is not a valid literal. When a real number literal is interpreted as a value of a real number type, its value is the representable real number closest to the value of the literal. In the case of a [tie](#ties), the conversion to the real number type is invalid. The decimal real number syntax allows for any decimal fraction to be expressed -- that is, any number of the form _a_ x 10-_b_, where _a_ is an integer and _b_ is a non-negative integer. Because the decimal fractions are dense in the reals and the set of values of the real number type is assumed to be discrete, every value of the real number type can be expressed as a real number literal. However, for certain applications, directly expressing the intended real number representation may be more convenient than producing a decimal equivalent that is known to convert to the intended value. Hexadecimal real number literals are provided in order to permit values of binary floating or fixed point real number types to be expressed directly. #### Ties As described above, a real number literal that lies exactly between two representable values for its target type is invalid. Such ties are extremely unlikely to occur by accident: for example, when interpreting a literal as `Float64`, `1.` would need to be followed by exactly 53 decimal digits (followed by zero or more `0`s) to land exactly half-way between two representable values, and the probability of `1.` followed by a random 53-digit sequence resulting in such a tie is one in 553, or about 0.000000000000000000000000000000000009%. For `Float32`, it's about 0.000000000000001%, and even for a typical `Float16` implementation with 10 fractional bits, it's around 0.00001%. Ties are much easier to express as hexadecimal floating-point literals: for example, `0x1.0000_0000_0000_08p+0` is exactly half way between `1.0` and the smallest `Float64` value greater than `1.0`, which is `0x1.0000_0000_0000_1p+0`. Whether written in decimal or hexadecimal, a tie provides very strong evidence that the developer intended to express a precise floating-point value, and provided one bit too much precision (or one bit too little, depending on whether they expected some rounding to occur), so rejecting the literal seems like a better option than accepting it and making an arbitrary choice between the two possible values. ### Digit separators If digit separators (`_`) are included in literals, they must meet the respective condition: - For decimal integers, the digit separators shall occur every three digits starting from the right. For example, `2_147_483_648`. - For hexadecimal integers, the digit separators shall occur every four digits starting from the right. For example, `0x7FFF_FFFF`. - For real number literals, digit separators can appear in the decimal and hexadecimal integer portions (prior to the period and after the optional `e` or mandatory `p`) as described in the previous bullets. For example, `2_147.483648e12_345` or `0x1_00CA.FEF00Dp+24` - For binary literals, digit separators can appear between any two digits. For example, `0b1_000_101_11`. #### Open question: digit separator placement **2020-09-15: core team meeting selected Alternative 0** As an alternative to the rule proposed above, we could consider different restrictions on where digit separators can appear: **Alternative 0:** as presented above. **Alternative 1:** allow any digit groupings (for example, `123_4567_89`). Pro: - Simpler, more flexible rule, that may allow some groupings that are conventional in a specific domain. For example, `var Date: d = 01_12_1983;`, or `var Int64: time_in_microseconds = 123456_000000;`. - Culturally agnostic. For example, the Indian convention for digit separators would group the last three digits, and then every two digits before that (1,23,45,678 could be written `1_23_45_678`). Con: - Less self-checking that numeric literals are interpreted the way that the author intends. **Alternative 2:** as above, but additionally require binary digits to be grouped in 4s. Pro: - More enforcement that digit grouping is conventional. Con: - No clear, established rule for how to group binary digits. In some cases, 8 digit groups may be more conventional. - When used to express literals involving bit-fields, arbitrary grouping may be desirable. For example: ```carbon var Float32: flt_max = BitCast(Float32, 0b0_11111110_11111111111111111111111); ``` **Alternative 3:** allow any regular grouping. Pro: - Can be applied uniformly to all bases. Con: - Provides no assistance for decimal numbers with a single digit separator. - Does not allow binary literals to express an intent to initialize irregular bit-fields. ## Alternatives considered There are a number of different design choices we could make, as divergences from the above proposal. Those choices, along with the arguments that led to choosing the proposed design rather than each alternative, are presented below. ### Integer bases #### Octal literals No support is proposed for octal literals. In practice, their appearance in C and C++ code in a sample corpus consisted of (in decreasing order of commonality and excluding `0` literals): - file permissions, - cases where decimal was clearly intended (`CivilDay(2020, 04, 01)`), and - (in _distant_ third place) anything else. The number of intentional uses of octal literals, other than in file permissions, was negligible. We considered the following alternatives: **Baseline:** This proposal suggests that we do not support octal literals. Octal literals are rare and mostly obsolescent. File permissions can be supported in some other way. **Alternative 1:** Follow C and C++, and use `0` as the base prefix for octal. Pro: - More similar to C++ and other languages. Con: - Subtle and error-prone rule: for example, left-padding with zeroes for alignment changes the meaning of literals. **Alternative 2:** Use `0o` as the base prefix for octal. Pro: - Unlikely to be misinterpreted as decimal. - Follows several other languages (for example, Python). Con: - Additional language complexity. If we decide we want to introduce octal literals at a later date, use of alternative 2 is suggested. #### Decimal literals **We could permit leading `0`s in decimal integers (and in floating-point numbers).** Pro: - We would allow leading `0`s to be used to align columns of numbers. Con: - The same literal could be valid but have a different value in C++ and Carbon. **We could add an (optional) base specifier `0d` for decimal integers.** Pro: - Uniform treatment of all bases. Left-padding with `0` could be achieved by using `0d000123`. Con: - No evidence of need for this functionality. **We could permit an `e` in decimal literals to express large powers of 10.** Pro: - Many uses of (eg) `1e6` in our sample C++ corpus intend to form an integer literal instead of a floating-point literal. Con: - Would violate the expectations of many C++ programmers used to `e` indicating a floating-point constant. We suggest that this syntax is not added at this point. However, it should be reconsidered at a later date, once developers are used the requirement that real literals always contain a period. #### Case sensitivity **We could make base specifiers case-insensitive.** Pro: - More similar to C++. Con: - `0B1` is easily mistaken for `081` - `0B1` can be confused with `0xB1` - `0O17` is easily mistaken for `0017` - Allowing more than one way to write literals will lead to style divergence. **We could make the digit sequence in hexadecimal integers case-insensitive.** Pro: - More similar to C++. - Some developers will be more comfortable writing hexadecimal digits in lowercase. Some tools, such as `md5`, will print lowercase. Con: - Allowing more than one way to write literals will lead to style divergence. - Lowercase hexadecimal digits are less visually distinct from the `x` base specifier (for example, the digit sequence is more visually distinct in `0xAC` than in `0xac`). **We could require the digit sequence in hexadecimal integers to be written using lowercase letters `a`..`f`.** Pro: - Some developers will be more comfortable writing hexadecimal digits in lowercase. Some tools, such as `md5`, will print lowercase. - `B` and `D` are more likely to be confused with `8` and `0` than `b` and `d` are. Con: - Some developers will be more comfortable writing hexadecimal digits in uppercase. Some tools will print uppercase. - Lowercase hexadecimal digits are less visually distinct from the `x` base specifier (for example, the digit sequence is more visually distinct in `0xAC` than in `0xac`). ### Real number syntax **We could allow real numbers with no digits on one side of the period (`3.` or `.5`).** Pro: - More similar to C++. - Allows numbers to be expressed more tersely. Con: - Gives meaning to `tup.0` syntax that may be useful for indexing tuples. - Gives meaning to `0.ToString()` syntax that may be useful for performing member access on literals. - May harm readability by making the difference between an integer literal and a real number literal less significant. - Allowing more than one way to write literals will lead to style divergence. See also the section on [floating-point literals](https://google.github.io/styleguide/cppguide.html#Floating_Literals) in the Google style guide, which argues for the same rule. **We could allow a real number with no `e` or `p` to omit a period (`1e100`).** Pro: - More similar to C++. - Allows numbers to be expressed more tersely. Con: - Assuming that such numbers are integers rather than real numbers is a common error in C++. **We could allow the `e` or `p` to be written in uppercase.** Pro: - More similar to C++. - Most calculators use `E`, to avoid confusion with the constant `e`. Con: - Allowing more than one way to write literals will lead to style divergence. - `E` may be confused with a hexadecimal digit. **We could require a `p` in a hexadecimal real number literal.** Pro: - More similar to C++. - When explicitly writing a bit-pattern for a floating-point type, it's reasonable to always include the exponent value. Con: - Less consistent. - Makes hexadecimal floating-point values even more expert-only. **We could arbitrarily pick one of the two values when a real number is exactly half-way between two representable values.** Pro: - More similar to C++. - Would accept more cases, and it's likely that either of the two possible values would be acceptable in practice. Con: - Would either need to specify which option is chosen or, following C++, accept that programs using such literals have non-portable semantics. - Numbers specified to the exact level of precision required to form a tie are a strong signal that the programmer intended to specify a particular value. ### Digit separator syntax **2020-09-15: core team meeting chose to forward digit separator to painter** **2020-10-05: painter selected Alternative 2: `_` as digit separator** There are various different characters we could attempt to use as a digit separator. The options we considered are: **Alternative 0:** `'` as a digit separator. Pro: - Follows C++ syntax. - Used in several (mostly European) writing conventions. Con: - `'` is also likely to be used to introduce character literals. **Alternative 1:** `,` as a digit separator. Pro: - More similar to how numbers are written in English text and many other cultures. Con: - Commas are expected to widely be used in Carbon programs for other purposes, where there may be digits on both sides of the comma. For example, there could be readability problems if `f(1, 234)` called `f` with two arguments but `f(1,234)` called `f` with a single argument. - Comma is interpreted as a decimal point in the conventions of many cultures. - Unprecedented in common programming languages. **Alternative 2:** `_` as a digit separator. Pro: - Follows convention of C#, Java, JavaScript, Python, D, Ruby, Rust, Swift, ... - Culturally agnostic, because it doesn't match any common human writing convention. Con: - Underscore is not used as a digit grouping separator in any common human writing convention. **Alternative 3:** whitespace as a digit separator. Pro: - Used and understood by many cultures. - Never interpreted as a decimal point instead of a grouping separator. - Also usable to the right of a decimal point. Con: - Omitted separators in lists of numbers may result in distinct numbers being spliced together. For example, `f(1, 23, 4 567)` may be interpreted as three separate numerical arguments instead of four arguments with a missing comma. - Unprecedented in other programming languages. **Alternative 4:** `.` as digit separator, `,` as decimal point. Pro: - More familiar to cultures that write numbers this way. Con: - As with `,` as a digit separator, `,` as a decimal point is problematic. - This usage is unfamiliar and would be surprising to programmers; programmers from cultures where `,` is the decimal point in regular writing are likely already accustomed to using `.` as the decimal point in programming environments, and the converse is not true. **Alternative 5:** No digit separator syntax. Pro: - Simpler language rules. - More consistent source syntax, as there is no choice as to whether to use digit separators or not. Con: - Harms the readability of long literals. ## Rationale The proposal provides a syntax that is sufficiently close to that used both by C++ and many other languages to be very familiar. However, it selects a reasonably minimal subset of the syntaxes. This minimal approach provides benefits directly in line with both the simplicity and readability goals of Carbon: - Reduces unnecessary choices for programmers. - Simplifies the syntax rules of the language. - Improves consistency of written Carbon code. That said, it still provides sufficient variations to address important use cases for the goal of not leaving room for a lower level language: - Hexadecimal and binary integer literals. - Scientific notation floating point literals. - Hexadecimal (scientific) floating point literals. ### Painter rationale The primary aesthetic benefit of `'` to the painter is consistency with C++. However, its rare usage in C++ at this point reduces this advantage to a very small one, while there is broad convergence amongst other languages around `_`. The choice here has no risk of significant meaning or building up patterns of reading for users that might be disrupted by the change, and so it seems reasonable to simply converge with other languages to end up in the less surprising and more conventional syntax space. ### Open questions Placement restrictions of digit separators: - The core team had consensus for the proposed restricted placement rules. Use `_` or `'` as the digit separator character: - The core team deferred this decision to the painter. - The painter selected `_`. ================================================ FILE: proposals/p0144.md ================================================ # Numeric literal semantics [Pull request](https://github.com/carbon-language/carbon-lang/pull/144) ## Table of contents ## Table of contents - [Problem](#problem) - [Background](#background) - [Proposal](#proposal) - [Details](#details) - [Prelude support](#prelude-support) - [Implicit conversions](#implicit-conversions) - [Examples](#examples) - [Alternatives considered](#alternatives-considered) - [Use an ordinary integer or floating-point type for literals](#use-an-ordinary-integer-or-floating-point-type-for-literals) - [Use same type for all literals](#use-same-type-for-all-literals) - [Allow leading `-` in literal tokens](#allow-leading---in-literal-tokens) ## Problem When a numeric literal appears in a program, we need to understand its semantics: - What type does it have? - What value is produced by operations on it? - When can it validly be used to initialize an object? ## Background In C++, numeric literals have either an integral type or a floating-point type. C++ provides permission for implementations to add extended integral types, but in practice (for bad reasons relating to `intmax_t`) implementations do not do so, so there are a small finite set of types that any given numeric literal might have: - `int`, `long`, `long long`, or `unsigned` versions of these - `float`, `double`, or `long double` The choice of type is determined solely by the literal. The C++ approach is error-prone and problematic: - Lossy conversions from literals in initializers are permitted. - Lossy operations on literals are permitted; for example, on a typical implementation, `1 << 60` has value `0` because `1` is a 32-bit type. - Attempting to naturally express some values has undefined behavior; for example, `int x = -2147483648;` typically results in undefined behavior even when -2147483648 is a valid `int` value. - Integer literals with value 0 have special semantics that are lost when the integer is passed to a function: "perfect" forwarding doesn't work for such literals. - The built-in types are privileged: only the types listed above have literals. There is no syntax for a 64-bit integer literal, only for (for example) a `long int` literal, which may or may not 64 bits wide. - The type of a literal can be unpredictable in portable code, as it can depend on which type a particular value happens to fit into. ## Proposal Numeric literals have a type derived from their value, and can be converted to any type that can represent that value. Simple operations such as arithmetic that involve only literals also produce values of literal types. ## Details Numeric literals have a type derived from their value. Two integer literals have the same type if and only if they represent the same integer. Two real number literals have the same type if and only if they represent the same real number. That is: - For every integer, there is a type representing literals with that integer value. - For every rational number, there is a type representing literals with that real value. - The types for real numbers are distinct from the types for integers, even for real numbers that represent integers. `var x: i32 = 1.0;` is invalid. Primitive operators are available between numeric literals, and produce values with numeric literal types. For example, the type of `1 + 2` is the same as the type of `3`. Numeric types can provide conversions to support initialization from numeric literals. Because the value of the literal is carried in the type, a type-level decision can be made as to whether the conversion is valid. The integer types defined in the standard library permit conversion from integer literal types whose values are representable in the integer type. The floating-point types defined in the Carbon library permit conversion from integer and rational literal types whose values are between the minimum and maximum finite value representable in the floating-point type. ### Prelude support The following types are defined in the Carbon prelude: - An arbitrary-precision integer type. ``` class BigInt; ``` - A rational type, parameterized by a type used for its numerator and denominator. ``` class Rational(T:! Type); ``` The exact constraints on `T` are not yet decided. - A type representing integer literals. ``` class IntLiteral(N:! BigInt); ``` - A type representing floating-point literals. ``` class FloatLiteral(X:! Rational(BigInt)); ``` All of these types are usable during compilation. `BigInt` supports the same operations as `Int(n)`. `Rational(T)` supports the same operations as `Float(n)`. The types `IntLiteral(n)` and `FloatLiteral(x)` also support primitive integer and floating-point operations such as arithmetic and comparison, but these operations are typically heterogeneous: for example, an addition between `IntLiteral(n)` and `IntLiteral(m)` produces a value of type `IntLiteral(n + m)`. ### Implicit conversions `IntLiteral(n)` converts to any sufficiently large integer type, as if by: ``` impl [template N:! BigInt, template M:! BigInt] IntLiteral(N) as ImplicitAs(Int(M)) if N >= Int(M).MinValue as BigInt and N <= Int(M).MaxValue as BigInt { ... } impl [template N:! BigInt, template M:! BigInt] IntLiteral(N) as ImplicitAs(Unsigned(M)) if N >= Int(M).MinValue as BigInt and N <= Int(M).MaxValue as BigInt { ... } ``` The above is for exposition purposes only; various parts of this syntax are not yet decided. Similarly, `IntLiteral(x)` and `FloatLiteral(x)` convert to any sufficiently large floating-point type, and produce the nearest representable floating-point value. Conversions in which `x` lies exactly half-way between two values are rejected, as [previously decided](/docs/design/lexical_conventions/numeric_literals.md). Conversions in which `x` is outside the range of finite values of the floating-point type are also rejected, rather than saturating to the finite range or producing an infinity. ### Examples ```carbon // This is OK: the initializer is of the integer literal type with value // -2147483648 despite being written as a unary `-` applied to a literal. var x: i32 = -2147483648; // This initializes y to 2^60. var y: i64 = 1 << 60; // This forms a rational literal whose value is one third, and converts it to // the nearest representable value of type `f64`. var z: f64 = 1.0 / 3.0; // This is an error: 300 cannot be represented in type `i8`. var c: i8 = 300; fn f[template T:! Type](v: T) { var x: i32 = v * 2; } // OK: x = 2_000_000_000. f(1_000_000_000); // Error: 4_000_000_000 can't be represented in type `i32`. f(2_000_000_000); // No storage required for the bound when it's of integer literal type. struct Span(template T:! Type, template BoundT:! Type) { var begin: T*; var bound: BoundT; } // Returns 1, because 1.3 can implicitly convert to f32, even though conversion // to f64 might be a more exact match. fn G() -> i32 { match (1.3) { case _: f32 => { return 1; } case _: f64 => { return 2; } } } // Can only be called with a literal 0. fn PassMeZero(_: IntLiteral(0)); // Can only be called with integer literals in the given range. fn ConvertToByte[template N:! BigInt](_: IntLiteral(N)) -> i8 if N >= -128 and N <= 127 { return N as i8; } // Given any int literal, produces a literal whose value is one higher. fn OneHigher(L: IntLiteral(template _:! BigInt)) -> auto { return L + 1; } // Error: 256 can't be represented in type `i8`. var v: i8 = OneHigher(255); ``` ## Alternatives considered ### Use an ordinary integer or floating-point type for literals We could decide on a fixed-width type based on the form of the literal, for example using a type suffix with some rules to determine what type to pick for unsuffixed literals. Advantages: - This follows what C++ does. - Can determine the type of a floating-point number without requiring contextual information. Disadvantages: - Surprising behavior when applying an operator to a literal would result in overflow. Even if we diagnose this, a diagnostic that `-2147483648` is invalid because it overflows is surprising. - Creates additional literal syntax that users will need to understand. - May select types that don't match the programmer's expectations. - Whatever types we pick are privileged. ### Use same type for all literals We could give literals a single, arbitrary-precision type (say, `Integer` for integer literals and `Rational` for real literals). Advantages: - Only introduces two new types, not an unbounded parameterized family of types. - Writing a function that takes any integer literal can be done with more obvious syntax and less syntactic overhead. Instead of: ``` fn OneHigher(L: IntLiteral(template _:! BigInt)); ``` we could write ``` fn OneHigher(template L:! Integer); ``` However, with this proposal, a function taking any integer expression that can be evaluated to a constant can be written as ``` fn F(template N:! BigInt); ``` and such a function would accept all integer literals, as well as non-literal constants. Disadvantages: - Our mechanism for specifying the behavior of operations such as arithmetic is based on interface implementations, which are looked up by type. Supporting `impl` selection based on values would introduce substantial complexity. - If we introduce an arbitrary-precision integer type, it would be inconsistent to support it only during compilation. However, if we allow its use at runtime, programs may use it accidentally, with an invisible performance cost. For example, `var x: auto = 123;` would result in `x` having an infinite-precision type, possibly involving invisible dynamic allocation. - Under this proposal, the type of `x` is a type that can only represent the value `123`; as such, `x` is effectively immutable. The arbitrary-precision integer type introduced in this proposal can only be used explicitly by programs naming it. ### Allow leading `-` in literal tokens We could treat a leading `-` character as part of a numeric literal token, so that -- for example -- `-123` would be a single `-123` token rather than a unary negation applied to a literal `123`. Advantages: - This would narrowly solve the problem that `INT_MIN` cannot be written directly, without any of the other implications of this proposal. Disadvantages: - Makes the behavior of unary `-` less uniform. - Prevents the introduction of infix or postfix operators that bind more tightly than unary `-`, such as an infix exponentiation operator: `-2**2` may be expected to evaluate to -4, not to +4. ================================================ FILE: proposals/p0149.md ================================================ # Change documentation style guide [Pull request](https://github.com/carbon-language/carbon-lang/pull/149) ## Table of contents - [Problem](#problem) - [Background](#background) - [Common deviations](#common-deviations) - [Proposal](#proposal) - [Details](#details) - [Google developer documentation style guide](#google-developer-documentation-style-guide) - [Microsoft writing style guide](#microsoft-writing-style-guide) - [Word lists](#word-lists) - [Alternatives considered](#alternatives-considered) - [Rationale](#rationale) - [Painter rationale](#painter-rationale) - [Open questions](#open-questions) - [Which style guide?](#which-style-guide) ## Problem There's disagreement with the current Google developer documentation style guide. We should make sure the core team has a consensus on the style guide chosen. ## Background The status quo is that we use the Google style guide, per [CONTRIBUTING.md](/CONTRIBUTING.md#style). There's basic support for a style guide in order to avoid protracted debate about formatting and to maintain basic consistency. Key desires for a style guide are: - Actively maintained. - Freely available for contributors. ### Common deviations CONTRIBUTING.md notes a couple deviations from the Google style guide. These may be assumed to apply regardless of the style guide, as the relevant styles are common to Microsoft's style guide. ## Proposal A style guide should be chosen. The only candidates found are: - Google developer documentation style guide - Microsoft writing style guide This may be done with or without additional modifications, such as the em-dash exception in CONTRIBUTING.md. **Open question**: Which style guide? **Decision**: Google style guide ## Details A few key items are highlighted from both style guides for comparison. ### Google developer documentation style guide https://developers.google.com/style - **Dates and times**: [January 19, 2017 or 2017-04-15 3:45 PM](https://developers.google.com/style/dates-times). - **Lists**: [nuanced guidance for punctuation](https://developers.google.com/style/lists). - **Non-English words**: mostly covered through the word list and [abbreviations](https://developers.google.com/style/abbreviations). - **Punctuation**: - Commas: [Oxford](https://developers.google.com/style/commas). - Parentheses: [use judiciously](https://developers.google.com/style/parentheses). - Semicolons: [use judiciously](https://developers.google.com/style/semicolons). - **Word list**: - Letter links, such as [i](https://developers.google.com/style/word-list#letter-i). - Easy to skim comments on words. ### Microsoft writing style guide https://docs.microsoft.com/en-us/style-guide/welcome/ - **Dates and times**: [February 16, 2016, 3:45 PM](https://docs.microsoft.com/en-us/style-guide/global-communications/time-place#dates). [Only use digits for dates in locale-customized UIs](https://docs.microsoft.com/en-us/style-guide/a-z-word-list-term-collections/term-collections/date-time-terms). - **Lists**: [use a period if any item is a complete sentence](https://docs.microsoft.com/en-us/style-guide/scannable-content/lists). - **Non-English words**: [avoid](https://docs.microsoft.com/en-us/style-guide/word-choice/use-us-spelling-avoid-non-english-words). - **Punctuation**: - Commas: [Oxford](https://docs.microsoft.com/en-us/style-guide/punctuation/commas). - Parentheses: [no guidance, sentence-length parenthetical examples](https://docs.microsoft.com/en-us/style-guide/punctuation/formatting-punctuation). - Semicolons: [avoid](https://docs.microsoft.com/en-us/style-guide/punctuation/semicolons). - **Word list**: - Letter links and individual word links, such as [illegal](https://docs.microsoft.com/en-us/style-guide/a-z-word-list-term-collections/i/illegal). - No way to skim comments on words; each needs to be read individually. ### Word lists > NOTE: Due to the noted lack of support for skimming Microsoft's word list, it > may be that Microsoft has other noteworthy guidance for terms that Google > doesn't. above : Google: Use preceding. : Microsoft: "Use previous, preceding, or earlier." and so on : Google: See etc. : Microsoft: "Use such as or like." below : Google: Use following. : Microsoft: "Use a link, or use later or the following." blacklist : Google: Use blocklist. : Microsoft: "Use block list instead. Note that block list is two words." cons : Google: Use disadvantages. : Microsoft: No guidance. CLI : Google: Use command-line tool. : Microsoft: No guidance. client : Google: Short for "client app", say "library" for "client library". : Microsoft: Use customer to refer to people. e.g. : Google: Use for example or such as. Too many people mix up e.g. and i.e. : Microsoft: "Use for example, such as, or like, as appropriate." etc. : Google: Avoid if possible, but use instead of and so on. : Microsoft: "Use such as or like followed by an example or two." filename, file name : Google: one word. : Microsoft: two words. file name extension, extension : Google: No guidance, but "filename extension" is used in the guide. : Microsoft: Use instead of file extension. flag : Google: Use option. : Microsoft: No guidance. for instance : Google: Use for example or such as. : Microsoft: No guidance. i.e. : Google: Use that is. : Microsoft: Use that is. illegal : Google: No guidance. : Microsoft: "Don't use to mean invalid or not valid." just : Google: Don't use. : Microsoft: No guidance. master/slave : Google: Use master with caution. Don't use slave. Multiple alternatives given. : Microsoft: Don't use master/slave. Multiple alternatives given. native : Google: Avoid and use more precise terms when possible, such as "built-in". : Microsoft: Only guidance is for native language. "Don't use to refer to a computer system's machine language. Use machine language or host language instead." pros : Google: Use advantages. : Microsoft: No guidance. quick : Google: Avoid. : Microsoft: No guidance. regex : Google: Use regular expression. : Microsoft: No guidance. repo : Google: Use repository. : Microsoft: No guidance. should : Google: Avoid because it's ambiguous. : Microsoft: "Before using should or must, consider other ways to discuss recommendations or requirements." simple, simply : Google: Avoid; "what might be simple for you might not be simple for others." : Microsoft: Only guidance is for simply; nothing for simple. "Don't use to mean that something is easy to do." tl;dr : Google: Use to summarize. : Microsoft: No guidance. traditional : Google: Avoid. : Microsoft: No guidance. via : Google: Don't use. : Microsoft: No explicit guidance, but might be considered to fall under non-English words. vice versa : Google: Use other way around or conversely. : Microsoft: No explicit guidance, but might be considered to fall under non-English words. vs. : Google: Use versus. : Microsoft: Use versus. ## Alternatives considered Various offline resources like the Chicago Manual of Style may be helpful to some, but require a subscription or physical copy, and so are less useful as canonical resources for contributors. ## Rationale The core team reached consensus that there should be a style guide, but did not have a strong preference between the Google style guide and the Microsoft style guide. One advantage of the Microsoft style guide is that it is less Google-centric, which could contribute somewhat to our community goal. Given the level of experience of the core team members with these style guides, it is not clear that a well-informed decision could be made. As such, the core team decided to leave the decision of the specific style guide up to the Painter. ### Painter rationale Given the lack of a high level reason to pick one over the other, the painter looked at the available tooling for linting against the two options and spoke with people who have written substantial developer documentation against both style guides. There was no persuasive reason to change from the Google documentation style guide at this time. Specific ambiguities or problems with that style guide should instead be addressed, if necessary at all, with Carbon-specific local rule. ### Open questions #### Which style guide? The core team decided to let the Painter choose the style guide to be used. The painter selected to continue using the Google documentation markdown style guide. ================================================ FILE: proposals/p0157.md ================================================ # Design direction for sum types [Pull request](https://github.com/carbon-language/carbon-lang/pull/157) ## Table of contents ## Table of contents - [Problem](#problem) - [Background](#background) - [Proposal](#proposal) - [Shareable storage](#shareable-storage) - [User-defined pattern matching](#user-defined-pattern-matching) - ["Bare" designator syntax](#bare-designator-syntax) - [Alternatives considered](#alternatives-considered) - [Placeholder keywords](#placeholder-keywords) - [Designator types](#designator-types) - [Distinguishing pattern and expression semantics](#distinguishing-pattern-and-expression-semantics) - [Alternatives considered](#alternatives-considered-1) - [Separate syntaxes](#separate-syntaxes) - [Disambiguation by fixed priority](#disambiguation-by-fixed-priority) - [Disambiguation based on pattern content](#disambiguation-based-on-pattern-content) - [Choice types](#choice-types) - [Alternatives considered](#alternatives-considered-2) - [Different spelling for `choice`](#different-spelling-for-choice) - [Alternatives considered](#alternatives-considered-3) - [`choice` types only](#choice-types-only) - [Indexing by type](#indexing-by-type) - [Pattern matching proxies](#pattern-matching-proxies) - [Pattern functions](#pattern-functions) - [Rationale](#rationale) ## Problem Many important programming use cases involve values that are most naturally represented as having one of several alternative forms (called _alternatives_ for short). For example, - Optional values, which are pervasive in computing, take the form of either values of some underlying type, or a special "not present" value. - Functions that cannot throw exceptions often use a return type that can represent either a successfully computed result, or some description of how the computation failed. - Nodes of a parse tree often take different forms depending on the grammar production that generated them. For example, a node of a parse tree for simple arithmetic expressions might represent either a sum or product expression with two child nodes representing the operands, or a parenthesized expression with a single child node representing the contents of the parentheses. - The error codes returned by APIs like POSIX have a fixed set of named values. What unites these use cases is that the set of alternatives is fixed by the API, it is possible for user code to determine which alternative is present, and there is little or nothing you can usefully do with such a value without first making that determination. Carbon needs to support defining and working with types representing such values. Following Carbon's principles, these types need to be easy to define, understand, and use, and they need to be safe -- in ordinary usage, the type system should ensure that user code cannot accidentally access the wrong alternative. These types should be writeable as well as readable, and writing should be type-safe and efficient. In particular, it should be possible to mutate a single sub-field of an alternative, without having to overwrite the entire alternative, and without a risk of accidentally doing so when that alternative is not present. Furthermore, it needs to be possible for type owners to customize the representations of these types. For example: - Most sum types need a "discriminator" field to indicate which alternative is present, but since it typically has very few possible values, it can often be packed into padding, or even the low-order bits of a pointer. - Other sum types avoid an explicit discriminator, and instead reserve certain values to indicate separate alternatives. For example, a typical C-style pointer can be thought of as an optional type, with a special null value indicating that no pointer is present, because the platform guarantees that the null byte pattern is never the representation of a valid pointer. It must be possible to implement such customizations without changing the type's API, and hence without altering the static safety guarantees for users of the type. ## Background The terminology in this space is quite fragmented and inconsistent. This proposal will use the term _sum types_ to refer to types of the kind described in the problem statement. Note that "sum type" is not being proposed as a specific Carbon feature, or even as a precisely defined term of art; it is merely an informal way for this proposal to refer to its motivating use cases, in much the same way that a structs proposal might refer to "value types". Carbon as currently envisioned is already capable of approximating support for sum types. In particular, [pattern matching](/docs/design/pattern_matching.md) gives us a natural way to express querying which alternative is active, and then performing computations on that active alternative, which as discussed above is the primary way of interacting with a sum type. For example, a value-or-error type `Result(T, Error)` could be implemented like so: ``` // Result(T, Error) holds either a successfully-computed value of type T, // or metadata about a failure during that computation, or a singleton // "cancelled" state indicating that the computation successfully complied with // a request to halt before completion. struct Result(Type:$$ T, Type:$$ Error) { // 0 if this represents a value, 1 if this represents an error, 2 if this // represents the cancelled state. var Int: discriminator; var T: value; var Error: error; fn Success(T: value) -> Result(T, Error) { return (.discriminator = 0, .value = value, .error = Error()); } fn Failure(Error: error) -> Result(T, Error) { return (.discriminator = 1, .value = T(), .error = error); } var Result(T, Error):$$ Cancelled = (.discriminator = 2, .value = T(), .error = Error()); } ``` A typical usage might look like: ``` fn ParseAsInt(String: s) -> Result(Int, String) { var Int: result = 0; var auto: it = s.begin(); while (it != s.end()) { if (*it < '0' || *it > '9') { return Result(Int, String).Failure("String contains non-digit"); } result += *it - '0'; result *= 10; } return Result(Int, String).Success(result); } fn GetIntFromUser() -> Int { while(True) { var String: s = UserPrompt("Please enter a number"); match (ParseAsInt(s)) { case (.discriminator = 0, .value = Int: value, .error = String: _) => { return value; } case (.discriminator = 1, .value = Int: _, .error = String: error) => { Display(error); } case .Cancelled => { // We didn't request cancellation, so something is very wrong. Terminate(); } default => { // Can't happen, because the above cases are exhaustive. Assert(False); } } } } ``` However, this code has several functional deficiencies: - `.value` and `.error` must both be live throughout the `Result`'s lifetime, even when they are not meaningful. Consequently, `Success` must populate `.error` with a default-constructed dummy value (and so it won't work if `Error` is not default-constructible), `Failure` must do the same for `.value`, and `Cancelled` must do the same for both. Furthermore, `Result` is bloated by the fact that the two fields must have separately-allocated storage, even though at most one at a time actually stores any data. - The implementation details of `Result` are not encapsulated. This makes the `Result` API unsafe: nothing prevents client code from accessing `.value` even when `.discriminator` is not 0. This also makes the patterns extremely verbose. - `.discriminator` should never have any value other than 0, 1, or 2, but the compiler can't enforce that property when `Result`s are created, or exploit it when `Result`s are used. So, for example, the `match` must have a `default` case in order for the compiler and other tools to consider it exhaustive, even though that default case should never be entered. It also has a couple of ergonomic problems: - The definition of `Result` is largely boilerplate. Conceptually, the only information needed to specify this type is the names and parameter types of the two factory functions, the name of the static member `Cancelled`, plus the fact that every possible value of `Result` is uniquely described by the name and parameter values of a call to one of those two functions, or else equal to `Cancelled`. Given that information, the compiler could easily generate the rest of the struct definition. This generated implementation may not always be as efficient as a hand-coded one could be, but in a lot of cases that may not matter. - The `return` statements in `ParseAsInt` are quite verbose, due to the need to explicitly qualify the function calls with `Result(Int, String)`. In fact, developers might prefer to avoid having a function call at all, especially in the success case, and instead rely on implicit conversions to write something like `return result;`. ## Proposal To summarize, the previous section identified several missing features in Carbon, which together would enable Carbon to support efficient and ergonomic sum types: - There's no way to manually control the lifetimes of subobjects, or enable them to share storage. - There's no way for pattern matching to operate through an encapsulation boundary. - There's no way for a type to specify that a given set of patterns is exhaustive. - There's no concise way to define a sum type based on the form of its alternatives. - There's no way to return a specific alternative without restating the return type of the function. I propose supporting sum types by introducing several language features to supply the missing functionality identified above. These features are largely separable, although there are some dependencies between them, so their detailed design will be addressed in future proposals, and the details discussed here should be considered provisional. This proposal merely establishes the overall design direction for sum types, in the same way that [#83](p0083.md) established the overall design direction for the language as a whole. To support manual lifetime control and storage sharing, I propose introducing at least one and preferably both of the following: - A `Storage` type, which represents a fixed-size buffer of untyped memory and provides operations for creating and destroying objects within it. - A typed `union` facility, such as the one described in proposal [0139](https://github.com/carbon-language/carbon-lang/pull/139). To support encapsulation in pattern matching, I propose introducing a `Matchable` interface, which a type can implement in order to specify how it behaves in pattern matching, including what (if anything) constitutes an exhaustive set of patterns for that type. To allow users to concisely specify a sum type when they don't need to micro-optimize the implementation details, I propose a `choice` syntax that specifies a sum type purely in terms of its alternatives, which acts as a sugar syntax for those lower-level features. To avoid redundant boilerplate in functions that return sum types, I propose allowing statements of the form `return .X;` and `return .F();`, which are interpreted as if the function's return type appeared immediately prior to the `.` character. Cumulatively, these features will allow `Status` to be defined so that the usage portions of the above example can be rewritten as follows: ``` fn ParseAsInt(String: s) -> Result(Int, String) { var Int: result = 0; var auto: it = s.begin(); while (it != s.end()) { if (*it < '0' || *it > '9') { return .Failure("String contains non-digit"); } result += *it - '0'; result *= 10; } return .Success(result); } fn GetIntFromUser() -> Int { while(True) { var String: s = UserPrompt("Please enter a number"); match (ParseAsInt(s)) { case .Success(var Int: value) => { return value; } case .Failure(var String: error) => { Display(error); } case .Cancelled => { // We didn't request cancellation, so something is very wrong. Terminate(); } } } } ``` > **Open question:** How will user-defined sum types (and pattern matching in > general) support name bindings that allow you to mutate the underlying object? ## Shareable storage This approach to sum types imposes relatively few requirements on the language features used to implement shareable storage (meaning, storage that can be inhabited by different objects at different times), and so this proposal doesn't describe them in much detail. The primary options are typed unions along the lines of proposal [0139](https://github.com/carbon-language/carbon-lang/pull/139), and untyped byte buffers along the lines described below. Typed unions are somewhat safer and more readable, but less general. For example, they can't support use cases like implementing a small-object-optimized version of [`std::any`](https://en.cppreference.com/w/cpp/utility/any), because the set of possible types is not known in advance. This proposal takes the position that Carbon must have at least one of these two features. Whether it should have only untyped byte buffers, only typed unions, or both, is left as an **open question**, because the answer is orthogonal to the overall design direction that is the focus of this proposal. Consequently, I will not further discuss the tradeoffs between the two features. This proposal's examples focus on untyped byte buffers because they are simpler to describe, and aren't already covered by another proposal. Regardless of the form that shareable storage takes, it won't be able to intrinsically keep track of whether it currently holds any objects, or the types or offsets of those objects, because that would require it to maintain additional hidden storage, and a major goal of this design is to give the developer explicit control of the object representation. Consequently, it is not safe to copy, move, assign to, or destroy shareable storage unless it is known not to be inhabited by an object. This means that in the general case, the compiler will not be able to generate safe default implementations for any special member functions of types that have shareable storage members. For purposes of illustration in this proposal, I will treat `Storage(SizeT size, SizeT align)` as a library template representing an untyped buffer of `size` bytes, aligned to `align`. It provides `Create`, `Read`, and `Destroy` methods which create, access, and destroy an object of a specified type within the buffer. Using `Storage`, we can redefine `Status` as follows: ``` struct Result(Type:$$ T, Type:$$ Error) { var Int: discriminator; var Storage(Max(Sizeof(T), Sizeof(Error)), Max(Alignof(T), Alignof(Error))): storage; fn Success(T: value) -> Self { Self result = (.discriminator = 0); result.storage->Create(T, value); return result; } fn Failure(Error: error) -> Self { Self result = (.discriminator = 1); result.storage->Create(Error, error); return result; } var Self:$$ Cancelled = (.discriminator = 2); // Copy, move, assign, destroy, and similar operations need to be defined // explicitly, but are omitted for brevity. } ``` ## User-defined pattern matching As seen in the example above, we want to allow pattern-matching on `Status` to look like this: ``` match (ParseAsInt(s)) { case .Success(var Int: value) => { return value; } case .Failure(var String: error) => { Display(error); } case .Cancelled => { // We didn't request cancellation, so something is very wrong. Terminate(); } } ``` For this to work, `Status` needs to specify two things: - The set of all possible alternatives, including their names and parameter types, so that the compiler can typecheck the `match` body, identify any unreachable `case`s, and determine whether any `case`s are missing. - The algorithm that, given a `Status` object, determines which alternative is present, and specifies the values of its parameters. Here's how `Status` can do that under this proposal: ``` struct Result(Type:$$ T, Type:$$ Error) { var Int: discriminator; var Storage(Max(Sizeof(T), Sizeof(Error)), Max(Alignof(T), Alignof(Error))): storage; interface MatchContinuation { var Type:$$ ReturnType; fn Success(T: value) -> ReturnType; fn Failure(Error: error) -> ReturnType; fn Cancelled() -> ReturnType; } impl Matchable(MatchContinuation) { method (Ptr(Self): this) Match[MatchContinuation:$ Continuation]( Ptr(Continuation): continuation) -> Continuation.ReturnType { match (discriminator) { case 0 => { return continuation->Success(this->storage.Read(T)); } case 1 => { return continuation->Failure(this->storage.Read(Error)); } case 2 => { return continuation->Cancelled(); } default => { Assert(false); } } } } // Success() and Failure() factory functions, and the Cancelled static // constant, are defined as above. // Copy, move, assign, destroy, and similar operations need to be defined // explicitly, but are omitted for brevity. } ``` In this code, `Result` makes itself available for use in pattern matching by declaring that it implements the `Matchable` interface. `Matchable` takes an interface argument, `MatchContinuation` in this case, which specifies the set of possible alternatives by declaring a method for each one. I'll call that argument the _continuation interface_, for reasons that are about to become clear. The `Match` method of the `Matchable` interface. This method takes two parameters: the value being matched against, and an instance of the continuation interface, which the compiler generates from the `match` expression being evaluated, with method bodies that correspond to the bodies of the corresponding `case`s. Once `Match` has determined which alternative is present, and the values of its parameters, it invokes the corresponding method of the continuation object. `Match` is required to invoke exactly one continuation method, and to do so exactly once. This proposal assumes that Carbon will have support for defining and implementing generic interfaces, including interfaces that take interface parameters, and uses `interface`, `impl`, `method` etc. as **placeholder** syntax. It can probably be revised to work if interfaces can't be parameterized that way, or if we don't have a feature like this at all, but it might be somewhat more awkward. Notice that the names `Success`, `Failure`, and `Cancelled` are defined twice, once as factory functions of `Result` and once as methods of `MatchContinuation`, with the same parameter types in each case. The two effectively act as inverses of each other: the factory functions compute a `Result` from their parameters, and the methods are used to report the parameter values that compute a given `Result`. This mirroring between expression and pattern syntax is ultimately a design choice by the type author; there is no language-level requirement that the alternatives correspond to the factory functions. This approach can be extended to support non-sum patterns as well. For example, a type that wants to match a tuple-shaped pattern like `(Int: i, String: s)` could define a continuation interface like ``` interface MatchContinuation { fn operator()(Int: i, String: s); } ``` A type's continuation interface can also include a function named `operator default`, which implements the `default` case of the `match`. Consequently, any `match` on that type will be required to have a `default` case. This is valuable because it protects the type author's ability to add new alternatives in the future, without causing build failures in client code. > **Open question:** Can `Match` actually invoke `operator default`? It might > sometimes be useful to define a type that can't be matched exhaustively, and > the mere possibility that the `default` case could actually run might help > encourage client code to implement it robustly, rather than blindly providing > something like `Assert(False)`. However, if there's a language-level guarantee > that the `default` case is unreachable if all other alternatives are handled, > then we can allow code in the same library as the type to omit the `default` > case. That's desirable because code in the same library doesn't pose an > evolutionary risk, and it's often valuable to have a build-time guarantee that > code within the type's own API explicitly handles every case. Note that `Match`'s continuation parameter type must be generic rather than templated (`:$` rather than `:$$`). Template specialization is driven by the concrete values of the template arguments, but the type of the continuation parameter may depend on code generation details that aren't yet known when template specialization takes place. By the same token, we won't support overloading `Match`, because overload resolution is likewise driven by the concrete types of the function arguments, which may not be known at that point. ## "Bare" designator syntax A _designator_ is a token consisting of `.` followed by an identifier. The canonical use case for designators is member access, as in `foo.bar`, where the designator applies to the preceding expression. However, we expect Carbon will also have some use cases for "bare" designators, where there is no preceding expression. In particular, we expect to use bare designators to initialize named tuple fields, as in `(.my_int = 42, .my_str = "Foo")`, which produces a tuple with fields named `my_int` and `my_str`. I propose to also permit using bare designators to refer to the alternatives of a sum type, in cases where the sum type is clear from context. In particular, I propose that in statements of the form `return R.Alt;` or `return R.Alt();`, where `R` is the function return type, the `R` can be omitted. Similarly, I propose that patterns of the form `S.Alt` or `S.Alt()`, where `S` is the type being matched against, the `S` can be omitted. Note that both of these shorthands are allowed only at top level, not as subexpressions or subpatterns. > **Open question:** Can we also permit these shorthands to be nested? This > would be more consistent and less surprising, but could create ambiguity with > the use of bare designators in tuple initialization: does `case (.Foo, .Bar)` > match a tuple of two fields named `Foo` and `Bar`, or does it match a tuple of > two positional fields, of sum types that respectively have `.Foo` and `.Bar` > as alternatives? Furthermore, allowing such nested usages may conflict with > the > [proposed principle](https://github.com/carbon-language/carbon-lang/pull/103) > that type information should propagate only from an expression to its > enclosing context, and not vice-versa. > > The issue of type propagation is particularly acute if we want to allow > nesting of alternatives, such as `case .Foo(.Bar(_))`. The problem there is > that we are relying on the type of `Foo`'s parameter to tell us the type of > the argument expression `.Bar(_)`, but if `Foo` is overloaded, we can't > determine the type of the parameter until the overload is resolved, and to do > that we first need to know the type of the argument expression. And even if > `Foo` is not currently overloaded, an overload might be added in the future > (at least if the sum type has an `operator default`). Adding overloads is a > canonical example of the kind of software evolution that Carbon is intended to > allow, so the validity of this code can't depend on whether or not `Foo` is > overloaded. ### Alternatives considered #### Placeholder keywords Rather than allowing code to omit the type altogether, we could allow code to replace the type with a placeholder keyword, for example `case auto.Foo`. This would avoid the ambiguity with tuple initialization, but would still mean that type information is propagating into the expression from its surrounding context rather than vice-versa (which also means that `auto` may not be an appropriate spelling). Furthermore, this could wind up feeling fairly boilerplate-heavy, even if the keyword is very short. #### Designator types We could treat each bare designator as essentially defining its own type, which may then implicitly convert to a suitable sum type. For example, we could think of `.Some(42)` as having a type `DesignatedTuple("Some", (Int))`, and `Optional(Int)` would define an implicit conversion from that type. This would ensure that type information does not propagate into the expression from the context, but would not resolve the ambiguity with tuple initialization. Furthermore, this would mean that the names of bare designators do not need to be declared before they are used, and in fact can't be meaningfully declared at all. This could have very surprising consequences. For example, a typo in a line of code like `var auto: x = .Sone(42);` cannot be diagnosed at that line. Instead, the problem can't be diagnosed until `x` is used, and even then it will show up as an overload resolution error rather than a name lookup error. This is particularly problematic because it is much harder to provide useful, actionable diagnostics for overload resolution failure than for name lookup failure. Relatedly, in the case of bare designators, IDEs would not be able to implement tab-completion using Carbon's name lookup rules, but would effectively have to invent their own ad hoc name lookup rules. Note that this approach would work well with the "Indexing by type" alternative discussed below, with instances of `DesignatedTuple` (or whatever we call it) acting as tagged wrapper types. ## Distinguishing pattern and expression semantics As discussed above, we expect a well-behaved sum type to define factory functions that correspond to each of the alternatives in its continuation interface. This creates some potential ambiguity about whether a given use of an alternative name refers to the factory function or the continuation interface. For example, consider the following code: ``` var Result(Int, String): r = ...; match (r) { case Result(Int, String).Value(0) => ... ``` This could potentially be interpreted two ways: - `Result(Int, String).Value(0)` is evaluated as an ordinary function call, and the result is compared with `r` to see if they match, presumably using the `==` operator. - The whole `match` expression is evaluated by invoking `Result(Int, String)`'s implementation of `Matchable`, with a continuation object whose `.Value(Int)` method compares the `Int` parameter with 0. This can also be thought of as a name lookup problem: is `.Value` looked up in `Result(Int, String)`, or in `Result(Int, String)`'s implementation of `Matchable`? I propose to leave this choice unspecified, so that the compiler may validly generate code either way. This gives the compiler more freedom to optimize, and perhaps more importantly, it helps discourage sum type authors from intentionally making its factory function behavior inconsistent with its pattern matching behavior. By extension, I propose treating the shorthand syntax `case .Value(0)` the same way. ### Alternatives considered #### Separate syntaxes We could instead avoid the ambiguity by providing separate syntaxes for the two semantics. The more straightforward version of this would be to say that `Result(Int, String).Value(0)` is always interpreted as an ordinary function call, and patterns like `Result(Int, String).Value(Int: i)` are ill-formed because they cannot be interpreted as function calls. However, this would require us to have a syntax for matching alternatives that is disjoint from the syntax for constructing alternatives. This would be at odds with existing practice in languages like Rust, Swift, Haskell, and ML, all of which use the same syntax for constructing and matching alternatives. Note that it is tempting to use bare designators as the syntax for matching alternatives, so that `Result(Int, String).Value(0)` is an expression, but `.Value(0)` is a pattern. However, that is unlikely to be sufficient on its own, because bare designators rely on type information that may not always be available, especially in nested patterns. Hence, in order for this approach to work, we would need to introduce a separate syntax for specifying the type of a bare designator, such as `.Value(0) as Result(Int, String)`. Alternatively, we could say that code in a pattern-matching context is always interpreted as a pattern, even if it could otherwise be interpreted as an expression. We would then need to introduce a pattern operator for explicitly evaluating a subpattern as an expression, such as `is Result(Int, String).Value(0)` or `== Result(Int, String).Value(0)`. However, this would impose an educational and cognitive burden on users: FAQ entries like "What's the difference between `case .Foo` and `case is .Foo`" and "How do I choose between `case .Foo` and `case is .Foo`" seem inevitable, and would require fairly nuanced answers. It would also add syntactic noise to the use cases that correspond to C++ `switch` statements, where all of the cases are fixed values. #### Disambiguation by fixed priority We could instead specify that, when `Result(Int, String).Value(0)` appears in a context where a pattern is expected, the name `.Value` is looked up as both an ordinary function call and as a use of the `Matchable` interface, and specify one of the two as the "winner" in the case where both lookups succeed. This is effectively a variant of the previous option, except that some usages that would be build errors under that approach would be saved by the fallback interpretation here. In particular, it would still require us to introduce a second syntax for the case where the programmer wants the lower-priority of the two behaviors. Thus, it would carry largely the same drawbacks as the previous option, with the additional drawback that there wouldn't be a consistent correspondence between syntax and semantics. #### Disambiguation based on pattern content We could instead specify that `Result(Int, String).Value(0)` is always interpreted as an ordinary function call, but patterns like `Result(Int, String).Value(Int: i)` are evaluated using the `Matchable` interface, because no other implementation is possible. However, this would mean that the name-lookup behavior of a function call depends on code that can be arbitrarily deeply nested within it, which seems likely to be hostile to both programmers and tools. ## Choice types To allow users to define sum types without micromanaging the implementation details, I propose introducing `choice` as a convenient syntax for defining a sum type by specifying only the declarations of the set of alternatives. From that information, the compiler generates an appropriate object representation, and synthesizes definitions for the alternatives and special member functions. Our manual implementation of `Result` above doesn't really benefit from having direct control of the object representation, and doesn't seem to have any additional API surfaces, so it's well-suited to being defined as a choice type instead: ``` choice Result(Type:$$ T, Type:$$ Error) { Success(T: value), Failure(Error: error), Cancelled } ``` The body of a `choice` type definition consists of a comma-separated list of alternatives. These have the same syntax as a function declaration, but with `fn` and the return type omitted. If there are no arguments, the parentheses may also be omitted, as with `Cancelled`. `default` may also be included in the list, with the same meaning as `operator default` in a continuation interface: it means that pattern-matching operations on this type must be prepared to handle alternatives other than those explicitly listed. The choice type will have a static factory function corresponding to each of the alternatives with parentheses, and a static data member corresponding to each alternative with no parentheses. The choice type will also implement the `Matchable` interface, and its continuation interface will have a method corresponding to each alternative. In short, this definition of `Result` as a choice type will have the same semantics as the earlier definition of it as a struct. It will probably also have the same implementation, with a discriminator field and a storage buffer large enough to hold the argument values of the alternatives. Any alternative parameter types that are incomplete (or have unknown size for any other reason) will be represented using owning pointers; among other things, this will allow users to define recursive choice types. The implementation will be hidden, of course, and the compiler may be able to generate better code, but we will design this feature to support at least that baseline implementation strategy. One consequence is that although the alternatives of a choice type can be overloaded (as in the `Variant` example below), they cannot be templates. More precisely, the parameter types of a pattern function must be fixed without knowing the values of any of the arguments. To see why, consider a choice type like the following, which attempts to emulate `std::any`: ``` choice Any { Value[Type:$$ T](T: value) } ``` The problem is that since `T` could be any type, and a single `Any` object could hold values of different types throughout its lifetime, `Any` can't be implemented using a storage buffer within the `Any` object. Instead, the storage buffer for the `T` object would have to be allocated on the heap, but then the compiler would need to decide whether to apply a small buffer optimization, and if so what size threshold to use, etc. Allowing choice types to be implemented in terms of heap allocation would make their performance far less predictable, contrary to Carbon's performance goals, and would have little offsetting benefit: these sorts of types appear to be rare, and when needed they should be implemented in library code, where the performance tradeoffs are explicit and under programmer control. It may be possible to relax this restriction when and if we have a design for supporting non-fixed-size types, although it's worth noting that even that would not give us a way for `Any` to support assignment. Carbon will probably have some mechanism for allowing a struct to have compiler-generated default implementations of operations such as copy, move, assignment, hashing, and equality comparison, so long as the struct's members support those operations. Assuming that mechanism exists, choice types will support it as well, with the parameter types of the pattern functions taking the place of the member types. However, there are a couple of special cases: - choice types cannot be default constructible, unless we provide a separate mechanism for specifying which alternative is the default. - choice types can be assignable, regardless of whether the parameter types are assignable, because assigning to a choice type always destroys the existing alternative, rather than assigning to it. A future proposal for this mechanism will need to consider whether to require an explicit opt-in to generate these operations. **Open question:** Should `choice` provide a way to directly access the discriminator? Correspondingly, should it provide a way to specify the discriminator type, and which discriminator values correspond to which alternatives? These features would enable choice types to support all the same use cases as C++ `enum`s, and permit zero-overhead conversion between the two at language boundaries. ### Alternatives considered #### Different spelling for `choice` The Rust and Swift counterparts of `choice` are spelled `enum`. I have avoided this because these types are not really "enumerated types" in the sense of all values being explicitly enumerated in the code. I chose the spelling `choice` because "choice type" is one of the only available synonyms for "sum type" that doesn't have any potentially-misleading associations. ## Alternatives considered ### `choice` types only Rather than layering `choice` types on top of lower level features, we could make them a primitive language feature, and simply not provide a way for user code to customize the representation of sum types. However, this would mean that users who encounter performance problems with the compiler-generated code for a `choice` type would have no way to address those problems without rewriting all code that uses that type. This would be contrary to Carbon's performance and evolvability goals. Furthermore, the rewritten code would probably be substantially less readable, and less safe, because it wouldn't be able to use pattern matching. ### Indexing by type Rather than requiring each alternative to have a distinct name (or at least a distinct function signature), we could pursue a design that requires each alternative to have a distinct type. With this approach, which I'll call "type-indexed" as opposed to "name-indexed", Carbon sum types would much more closely resemble C++'s `std::variant`, rather than Swift and Rust's `enum` or the sum types of various functional programming languages. Either approach can be emulated in terms of the other. For example, we don't yet have enough of a design for variadics to give an example of a Carbon counterpart for `std::variant`, but a variant with exactly three alternative types could be written like so: ``` choice Variant(Type:$$ T1, Type:$$ T2, Type:$$ T3) { Value(T1: value), Value(T2: value), Value(T3: value) } ``` Conversely a type-indexed type like `std::variant` can model a name-indexed type like `Result(T,E)` by introducing a wrapper type for each name, leading to something like `std::variant, Error, Cancelled>` (note that `std::variant` would not work, because `T` and `E` can be the same type). In either case, emulating the other model introduces some syntactic overhead: with name-indexing, `Variant`'s factory functions must be given a name (`Value`) even though it doesn't really convey any information, and emulating `Result(T,E)` in terms of type-indexing requires separately defining the tagged wrapper templates `Value` and `Error`. The distinction between these two models of sum types seems analogous the distinction between the tuple and struct models of product types. Tuples and type-indexed sum types treat the data structurally, in terms of types and positional indices, but structs and name-indexed sum types require the components of the data to have names, which contributes to both readability and type-safety by attaching higher-level semantics to the data. It is possible that both models of sum types could coexist in Carbon, just as structs and tuples do. However, that seems unlikely to be a good idea: the coexistence of tuples and structs is necessitated by the fact that it is quite difficult to emulate either of them in terms of the other in a type-safe way, but as we've seen, it's fairly straightforward to emulate either model of sum types in terms of the other. Use cases that work best with type-indexing appear to be quite rare, just as use cases for tuples appear to be quite rare compared to use cases for structs. Consequently, if Carbon has only one form of sum types, it should probably be the name-indexed form, as proposed here. > **Open question:** Should Carbon have a native syntax for pattern matching on > the dynamic type of an object? If so, should types like `Variant` be able to > use it, instead of having the `.Value` boilerplate in every pattern? Should > this mechanism be aware of subtype relationships (so that a subtype pattern is > a better match than a supertype pattern)? If so, how are those subtype > relationships defined? ### Pattern matching proxies As a variant of the previous approach, we could allow types to specify their pattern-matching behavior in terms of a proxy type that Carbon "natively" knows how to pattern-match against. In the case of a sum type, this proxy would be a `choice` type, which means that `choice` needs to be a fundamental part of the language, rather than syntactic sugar for a sum type struct. Returning once more to the `Result` example: ``` struct Result(Type:$$ T, Type:$$ Error) { // Data members, factories, and special members same as above choice Choice { Success(T: value), Failure(Error: error), Cancelled } fn operator match(Ptr(Self): this) -> Choice { match (discriminator) { case 0 => { return .Success(this->storage.Read(T)); } case 1 => { return .Failure(this->storage.Read(Error)); } case 2 => { return .Cancelled; } default => { Assert(False); } } } } ``` This approach has several advantages: - It's somewhat simpler, because it uses return values instead of continuation-passing. - It will be easier for the compiler to reason about, because of that simplicity and the somewhat narrower API surface. This may lead to better compiler performance, and better generated code. - It could generalize more easily to allow things like user-defined types that can match list patterns (if Carbon has those). However, it also has several drawbacks: - It forces us to treat `choice` as a fundamental part of the language: in order to implement a sum type, you have to work with an object type whose layout and implementation is inherently opaque. This would be a substantial departure from C++, and it's difficult to foresee the consequences of that. Possibly the closest analogy in C++ is virtual calls, and especially virtual base classes, where fundamental operations like `->` and pointer casts can involve nontrivial generated code, and some aspects of object layout are required to be hidden from the user. However, Carbon seems to be moving away from C++ in precisely those ways. - Although both approaches carry a risk of confusion due to duplicate names, the risk is somewhat greater here: a naive reader might think that, for example, `.Success` in the body of `operator match` refers to `Self.Success` rather than `Self.Choice.Success`. Relatedly, there's some risk that the author may omit the leading `.`, and thereby invoke `Self.Success` instead of `Self.Choice.Success`. This will probably fail to build, but the errors may be confusing. - It has less expressive power, because there's no straightforward way for the library type to specify code that should run after pattern matching is complete. For example, the callback approach could allow `Match` to create a mutable local variable, pass it to the callback by pointer/reference, and then write the (possibly modified) contents of the variable back into the sum type object. - Extending this approach to support in-place mutation during pattern matching is likely to require Carbon to support reference _types_, whereas the primary proposal would probably only require reference _patterns_, which are substantially less problematic. This is a consequence of using a return type rather than a set of callback parameters (which are patterns) to define the type's pattern matching interface. I very tentatively recommend the callback approach rather than this one, primarily because of the last point above: the Carbon type system is likely to be dramatically simplified if there are no reference types, but I think the proxy approach will make reference types all but unavoidable. ### Pattern functions Rather than require user types to define both the pattern-matching logic and the factory functions, with the expectation that they will be inverses of each other, we could instead enable them to define the set of alternatives as factory functions that the compiler can invert automatically. This approach, like the primary proposal, would consist of several parts. A _pattern function_ is a function that can be invoked as part of a pattern, even with arguments that contain placeholders. Pattern functions use the introducer `pattern` instead of `fn`, and can only contain the sort of code that could appear directly in a pattern. This lets us define reusable pattern syntaxes that can do things like encapsulate hidden implementation details of the object they're matching. Next, we would introduce the concept of an `alternatives` block, which groups together a set of factory functions and designates them as a set of alternatives. They are required to be both exhaustive and unambiguous, meaning that for any possible value of the type, it must be possible to obtain it as the return value of exactly one of the alternatives. Alternatives that take no arguments, which represent singleton states such as `Cancelled`, can instead be written as static constants. An `alternatives` block can be marked `closed`, which plays the same role as omitting `operator default` in the primary proposal: it indicates that client code need not be prepared to accept alternatives other than the ones currently present. As with the primary proposal, `Storage` is used to represent a span of memory that the user can create objects within. However, with this approach we also need it to support initialization from a `ToStorage` factory function, because pattern functions can't contain procedural code. Note that for the same reason, `ToStorage` will probably need to be a language intrinsic, or implemented in terms of one. Using these features, the `Result(T, Error)` example can be written as follows: ``` struct Result(Type:$$ T, Type:$$ Error) { var Int: discriminator; var Storage(Max(Sizeof(T), Sizeof(Error)), Max(Alignof(T), Alignof(Error))): storage; closed alternatives { pattern Success(T: value) -> Self { return (.discriminator = 0, .storage = ToStorage(value)); } pattern Failure(Error: error) -> Self { return (.discriminator = 1, .storage = ToStorage(error)); } var Self:$$ Cancelled = (.discriminator = 2); } } ``` Notice that with this approach, we do not need to define the special member functions of `Result` manually. The compiler can infer appropriate definitions in the same way that it infers how to invert these functions during pattern matching. As with the primary proposal, `choice` would be available as syntactic sugar, but its syntax would mirror the syntax of an `alternatives` block: ``` closed choice Result(Type:$$ T, Type:$$ Error) { pattern Success(T: value) -> Self; pattern Failure(Error: error) -> Self; var Self:$$ Cancelled; } ``` This ensures that a sum type's API is defined using essentially the same syntax, regardless of how the type author chooses to implement it. This approach is described in much more detail in an [earlier draft](https://github.com/carbon-language/carbon-lang/blob/4dbd31d71e02895892f97a211df4b5fff8cae5c3/proposals/p0157.md) of this document, where it was the primary proposal. It has a number of advantages over the primary proposal: - Manually-defined sum types could probably be implemented with dramatically less code, because both the special member functions and the code that implements pattern matching can be generated automatically. This would not only make these types less tedious to implement, it would probably also reduce the risk of bugs, and avoid readability problems arising from the duplication of names between factory functions and continuation interface methods. - Programmers would be able to define functions that encapsulate complex pattern-matching logic behind simple interfaces. - Pattern matching would not require the special overload resolution rules that are needed to translate a pattern-matching operation into a `Match` call. - User-defined sum types would default to being open, whereas they default to being closed under the primary proposal. This is probably a better default, because an open sum type can always be closed, but a closed sum type can't be opened (much less extended with new alternatives) without the risk of breaking user code. However, this approach also carries some substantial drawbacks: - Types can't use the full power of the Carbon language when defining their pattern-matching behavior, or the corresponding factory functions. Instead, they are restricted to a very narrow subset of the language that is valid in both patterns and procedural code. This forces us to introduce intrinsics like `ToStorage` to make that subset even minimally usable, and creates a substantial risk that some user-defined sum types just won't be able to support pattern matching. - The language rules are substantially more complicated and harder to explain, because we need to define the language subset that is usable in pattern functions, and define both "forward" and "reverse" semantics for it. Relatedly, it means that during pattern matching, there will be no straightforward correspondence between the Carbon code and the generated assembly, which could substantially complicate things like debugging. - Carbon's pattern language can't allow you to write _underconstrained_ patterns, which are patterns where the values of all bindings aren't sufficient to uniquely determine the state of the object being matched. This rules out things like the `|` pattern operator, and even prevents us from using pattern matching with types that have non-salient state, like the capacity of a `std::vector`. These issues, especially the overall complexity of this approach, leads me to recommend against adopting it. ## Rationale This proposal gives us solid ground on which to continue developing features that rely on sum types and pattern matching thereof. The approach taken seems plausible, and while there's a good chance that we will revise it significantly before we finish our first pass over the complete Carbon design (for example, by switching to pattern matching proxies or by supporting mutation of matched elements), we don't think it will anchor us too much on this one particular direction. With regard to Carbon's goals: - Performance: while the approach taken herein potentially has some performance cost for a common operation that is likely to appear in performance-critical code (requiring an indirect call and the generation of continuations for user-defined pattern matching), such cost should be practically removable by inlining. We'll need to take care to ensure this abstraction penalty is reliably removed in common cases, but this seems sufficiently feasible to be worth attempting. - Evolution: software evolution is supported by allowing user-defined pattern matching to specify (by way of the presence/absence of operator default) whether the set of patterns is intended to be extended in the future. - Ergonomics: custom pattern matching for user-defined types promotes language consistency and removes boilerplate Note: At the decision meeting, it was stated that geoffromer will update the proposal to add a rationale to address austern's questions about the layered approach. ================================================ FILE: proposals/p0162.md ================================================ # Basic Syntax [Pull request](https://github.com/carbon-language/carbon-lang/pull/162) ## Table of contents ## Table of contents - [Problem](#problem) - [Background](#background) - [Proposal](#proposal) - [Details](#details) - [Expressions and Patterns](#expressions-and-patterns) - [Statements](#statements) - [Declarations](#declarations) - [Precedence and Associativity](#precedence-and-associativity) - [Abstract Syntax](#abstract-syntax) - [Abstract Syntax for Expressions](#abstract-syntax-for-expressions) - [Abstract Syntax for Statements](#abstract-syntax-for-statements) - [Abstract Syntax for Declarations](#abstract-syntax-for-declarations) - [Alternatives considered](#alternatives-considered) - [Rationale](#rationale) ## Problem The purpose of this proposal is to establish some basic syntactic elements of the Carbon language and make sure that the grammar is unambiguous and can be parsed by an LALR parser such as `yacc` or `bison`. The grammar presented here has indeed been checked by `bison`. The language features in this basic grammar include control flow by way of `if` and `while`, functions, simple structures, choice, pattern matching, and a sample of operators on Booleans and integers. The main syntactic categories are `declaration`, `statement`, and `expression`. Establishing these syntactic categories should help the other proposals choose syntax that is compatible with the rest of the language. ## Background The grammar proposed here is based on the following proposals: - [Carbon language overview](https://github.com/carbon-language/carbon-lang/tree/trunk/docs/design) - pattern matching [#87](https://github.com/carbon-language/carbon-lang/pull/87), - structs [#98](https://github.com/carbon-language/carbon-lang/pull/98), - tuples [#111](https://github.com/carbon-language/carbon-lang/pull/111), - sum types [#157](https://github.com/carbon-language/carbon-lang/pull/157), and - metaprogramming [#89](https://github.com/carbon-language/carbon-lang/pull/89). ## Proposal We summarize the four main syntactic categories here and define the grammar in the next section. - `declaration` includes function, structure, and choice definitions. - `statement` includes local variable definitions, assignment, blocks, `if`, `while`, `match`, `break`, `continue`, and `return`. - `expression` includes function calls, arithmetic, literals, and other syntaxes that evaluate to a value. In Carbon, a type is a kind of value, and Carbon makes no syntactic distinction between type-valued expressions and other kinds of expressions, so the `expression` nonterminal is used in positions where a type is expected. - `pattern` for the patterns in a `match` statement, for the left-hand side of a variable definition, and for describing the parameters of a function. The grammar treats patterns and expressions identically, but the type system will only allow pattern variables (`expression ':' identifier`) in patterns and not in value or type-producing expressions. Having the separate non-terminals for `pattern` and `expression` helps to document this distinction. The proposal also specifies the abstract syntax. When this proposal is accepted, the accompanying `flex`, `bison`, and C++ files that define the grammar and implement the parser actions will be placed in the `executable_semantics` directory of the `carbon-lang` repository. In the near future there will be proposals regarding the semantic analysis (aka. type checking) and specification of runtime behavior for this subset of Carbon with the plan to add the accompanying executable forms of those specifications to the `executable_semantics` directory. Looking towards growing the Carbon language, the intent is for other proposals to add to this grammar by modifying the `flex` and `bison` files and the abstract syntax definitions in the `executable_semantics` directory. ## Details ### Expressions and Patterns The following grammar defines the concrete syntax for expressions. Below we comment on a few aspects of the grammar. ```Bison pattern: expression ; expression: identifier | expression designator | expression '[' expression ']' | expression ':' identifier | integer_literal | "true" | "false" | tuple | expression "==" expression | expression '+' expression | expression '-' expression | expression "and" expression | expression "or" expression | "not" expression | '-' expression | expression tuple | "auto" | "fnty" tuple return_type ; designator: '.' identifier ; tuple: '(' field_list ')' ; field_list: /* empty */ | field | field ',' field_list ; field: pattern | designator '=' pattern ; return_type: /* empty */ | "->" expression ; ``` The grammar rule ```Bison expression: expression ':' identifier ``` is for pattern variables. For example, in a variable definition such as var Int: x = 0; the `Int: x` is parsed with the grammar rule for pattern variables. In the right-hand side of the above grammar rule, the `expression` to the left of the `:` must evaluate to a type at compile time. The grammar rule ```Bison expression: "fnty" tuple return_type ``` is for function types. They are meant to play the role that function pointers play in C and that `std::function` plays in C++. Regarding the grammar rule for the return type of a function: ```Bison return_type: "->" expression ``` the `expression` is expected to evaluate to a type at compile-time. The grammar rule ```Bison tuple: '(' field_list ')' ``` is primarily for constructing a tuple, but it is also used for creating tuple types and tuple patterns, depending on the context in which the expression occurs. Regarding the grammar rules for `designator` and for a named argument ```Bison designator: '.' identifier field: designator '=' pattern ``` The period enables the addition of new keywords to Carbon without colliding with field names. The period also aids integrated development environments with auto-completion. The issue is that without the period, when the programmer is typing the identifier (and not yet typed the `=`), the IDE doesn't know whether the identifier is for a positional argument, in which case it is a variable occurrence, or whether the identifier is a field name. ### Statements The following grammar defines the concrete syntax for statements. ```Bison statement: "var" pattern '=' expression ';' | expression '=' expression ';' | expression ';' | "if" '(' expression ')' statement "else" statement | "while" '(' expression ')' statement | "break" ';' | "continue" ';' | "return" expression ';' | '{' statement_list '}' | "match" '(' expression ')' '{' clause_list '}' ; statement_list: /* empty */ | statement statement_list ; clause_list: /* empty */ | clause clause_list ; clause: "case" pattern "=>" statement | "default" "=>" statement ; ``` ### Declarations The following grammar defines the concrete syntax for declarations. ```Bison declaration: "fn" identifier tuple return_type '{' statement_list '}' | "fn" identifier tuple "=>" expression ';' | "fn" identifier tuple return_type ';' | "struct" identifier '{' member_list '}' | "choice" identifier '{' alternative_list '}' ; member: "var" expression ':' identifier ';' ; member_list: /* empty */ | member member_list ; alternative: identifier tuple ';' ; alternative_list: /* empty */ | alternative alternative_list ; declaration_list: /* empty */ | declaration declaration_list ; ``` In the grammar rule for function definitions ```Bison declaration: "fn" identifier tuple return_type '{' statement_list '}' ``` the `tuple` is used as a pattern to describe the parameters of the function. The grammar for function definitions does not currently include [implicit parameters](/docs/design/generics/terminology.md#deduced-parameter), but the intent is to add them to the grammar in the future. In the rule for field declarations ```Bison member: "var" expression ':' identifier ';' ``` the `expression` must evaluate to a type at compile time. The same is true for the `tuple` in the grammar rule for an alternative: alternative: identifier tuple ';' ### Precedence and Associativity The following precedence and associativity specification is meant to approximate the definitions in the operators and precedence proposal [#168](https://github.com/carbon-language/carbon-lang/pull/168) to the extent that is possible in a `yacc`/`bison` generated parser. The ordering is from lowest to highest precedence, with operators on the same line having equal precedence. Proposal 168 differs in that the operator groups are partially ordered instead of being totally ordered. nonassoc '{' '}' nonassoc ':' ',' left "or" "and" nonassoc "==" "not" left '+' '-' left '.' "->" nonassoc '(' ')' '[' ']' ### Abstract Syntax The output of parsing is an abstract syntax tree. There are many ways to define abstract syntax. Here we simply use C-style `struct` definitions. The definition of the abstract syntax is important because it is used in the specification of the semantic analysis and the specification of runtime behavior. #### Abstract Syntax for Expressions ```c++ enum ExpressionKind { Variable, PatternVariable, Int, Bool, PrimitiveOp, Call, Tuple, Index, GetField, IntT, BoolT, TypeT, FunctionT, AutoT }; enum Operator { Neg, Add, Sub, Not, And, Or, Eq }; struct Expression { ExpressionKind tag; union { struct { string* name; } variable; struct { Expression* aggregate; string* field; } get_field; struct { Expression* aggregate; Expression* offset; } index; struct { string* name; Expression* type; } pattern_variable; int integer; bool boolean; struct { vector >* fields; } tuple; struct { Operator operator_; vector* arguments; } primitive_op; struct { Expression* function; Expression* argument; } call; struct { Expression* parameter; Expression* return_type;} function_type; } u; }; ``` The correspondence between most of the grammar rules and the abstract syntax is straightforward. However, the parsing of the `field_list` deserves some explanation. The fields can be labeled with the grammar rule: ```Bison field: designator '=' pattern ``` or unlabeled, with the rule ```Bison field: pattern ``` The unlabeled fields are given numbers (represented as strings) for field labels, starting with 0 and going up from left to right. Regarding the rule for tuples: ```Bison tuple: '(' field_list ')' ``` if the field list only has a single unlabeled item without a trailing comma, then the parse result for that item is returned. Otherwise a `tuple` AST node is created containing the parse results for the fields. In the following grammar rules, if the parse result for `tuple` is not a tuple (because the field list was a single unlabeled item without a trailing comma), then a tuple is wrapped around the expression to ensure that it is a tuple. ```Bison expression: expression tuple expression: "fnty" tuple return_type function_definition: "fn" identifier tuple return_type '{' statement_list '}' | "fn" identifier tuple DBLARROW expression ';' | "fn" identifier tuple return_type ';' alternative: identifier tuple ';' ``` #### Abstract Syntax for Statements ```c++ enum StatementKind { ExpressionStatement, Assign, VariableDefinition, If, Return, Sequence, Block, While, Break, Continue, Match }; struct Statement { StatementKind tag; union { Expression* exp; struct { Expression* lhs; Expression* rhs; } assign; struct { Expression* pat; Expression* init; } variable_definition; struct { Expression* cond; Statement* then_; Statement* else_; } if_stmt; Expression* return_stmt; struct { Statement* stmt; Statement* next; } sequence; struct { Statement* stmt; } block; struct { Expression* cond; Statement* body; } while_stmt; struct { Expression* exp; list< pair >* clauses; } match_stmt; } u; }; ``` #### Abstract Syntax for Declarations ```c++ struct FunctionDefinition { string name; Expression* param_pattern; Expression* return_type; Statement* body; }; enum MemberKind { FieldMember }; struct Member { MemberKind tag; union { struct { string* name; Expression* type; } field; } u; }; struct StructDefinition { string* name; list* members; }; enum DeclarationKind { FunctionDeclaration, StructDeclaration, ChoiceDeclaration }; struct Declaration { DeclarationKind tag; union { struct FunctionDefinition* fun_def; struct StructDefinition* struct_def; struct { string* name; list >* alts; } choice_def; } u; }; ``` ## Alternatives considered Expressions, type expressions, and patterns could instead be defined using completely disjoint grammar productions, but that would require adding more keywords or symbols to avoid ambiguity and there would be a fair amount of repetition in grammar productions. The proposal does not include declarations for uninitialized variables, leaving that to a later proposal. In this proposal, assignment is a statement. It could instead be an expression as it is in C and C++. The arguments against assignment-as-an-expression include 1) it complicates reasoning about the ordering of side-effects and 2) it can cause confusion between `=` and `==` [(SEI CERT C Coding Standard)](https://wiki.sei.cmu.edu/confluence/display/c/EXP45-C.+Do+not+perform+assignments+in+selection+statements) [Visual Studio Warning](https://docs.microsoft.com/en-us/cpp/error-messages/compiler-warnings/compiler-warning-level-4-c4706?view=vs-2019). The syntax for variable initialization uses `=`, which is the same token used in assignment. An alternative would be to use a different syntax such as `:=` to better signal the semantic differences between initialization and assignment. The author does not have a preference on this choice. The `:=` alternative does not introduce any complications regarding ambiguity despite the other uses of `:` in the grammar. Inside a `choice`, an `alternative` could instead begin with a keyword such as `alt`, `fn`, or `var`, as discussed in the proposal for sum types [#157](https://github.com/carbon-language/carbon-lang/pull/157). The author does not have a preference in this regard. The precedence for `and` and `or` are equal, following the suggestion of proposal [#168](https://github.com/carbon-language/carbon-lang/pull/168), whereas in C++ `&&` has higher precedence than `||`. The parameters of a function are described using the syntax of a pattern. There could instead be separate syntax that is special to function parameters, as there is in C++. There are open questions regarding the design of function types, so we could alternatively postpone the specification of the syntax for function types. The choice to include them is based on the idea that Carbon will need something that fills the roles of `std::function` and C-style function pointers. Also, the features were selected for the basic syntax proposal in a way that aimed to make the proposal complete in the sense that each value-oriented feature (functions, tuples, structures) comes with syntax for 1) creating values, 2) using values, and 3) a type for classifying values. The parameters of a function type are described using the syntax for type expression. There could instead be separate syntax that is special to the parameters of a function type, as there is in C++. The keyword for introducing a function type is `fnty`, which is an arbitrary choice. Other alternatives include `fn_type` and `fn`. The `fn` alternative would conflict with future plans to use `fn` for lambda expressions. Some dislike of `fn_type` was voiced, which motivated the choice of `fnty`. It would be nice to do without a keyword, but as the grammar currently stands, that introduce ambiguity. The `pattern` non-terminal is defined in terms of `expression`. It could instead be the other way around, defining `expression` in terms of `pattern`. Regarding tuples and tuple types, this proposal follows [#111](https://github.com/carbon-language/carbon-lang/pull/111) in that there is not a distinct syntax for tuple types. Instead, the intent is that when a tuple of types results from an expression in a context that expects a type, the type system will convert the tuple to a tuple type. An alternative approach has been discussed in which tuple types have a distinct syntax, such as `Tuple(Int, Bool)`. ## Rationale Using code to validate our specification is a really promising direction, and this proposal seems like a good starting point. A reference implementation that's simple enough to be part of the design iteration process should help us move faster, by quickly uncovering the places where our specifications are ambiguous, syntactically or semantically unsound, or don't give the behavior we expect. In other words, it will help us keep ourselves honest, even at the proposal stage, which will help us avoid wasting time and effort implementing designs that turn out to be unworkable. This can be considered as sort of a counterpart to [In-progress design overview #83](p0083.md), in that the design specifics are being approved in order to bootstrap the specification process. We aren't necessarily adopting the specific syntax and semantics expressed by this proposal, and those choices will need to be presented and justified from scratch by future proposals. This decision is deferring the implementation to code review. The specific tooling used to implement the syntax checker, such as Bison, is a detail which may be changed, now or later, without requiring a proposal for core team review. ================================================ FILE: proposals/p0175.md ================================================ # C++ interoperability goals [Pull request](https://github.com/carbon-language/carbon-lang/pull/175) ## Table of contents - [Problem](#problem) - [Background](#background) - [Proposal](#proposal) - [Philosophy](#philosophy) - [Language goal influences](#language-goal-influences) - [Performance-critical software](#performance-critical-software) - [Software and language evolution](#software-and-language-evolution) - [Code that is easy to read, understand, and write](#code-that-is-easy-to-read-understand-and-write) - [Practical safety guarantees and testing mechanisms](#practical-safety-guarantees-and-testing-mechanisms) - [Fast and scalable development](#fast-and-scalable-development) - [Modern OS platforms, hardware architectures, and environments](#modern-os-platforms-hardware-architectures-and-environments) - [Interoperability with and migration from existing C++ code](#interoperability-with-and-migration-from-existing-c-code) - [Goals](#goals) - [Support mixing Carbon and C++ toolchains](#support-mixing-carbon-and-c-toolchains) - [Compatibility with the C++ memory model](#compatibility-with-the-c-memory-model) - [Minimize bridge code](#minimize-bridge-code) - [Unsurprising mappings between C++ and Carbon types](#unsurprising-mappings-between-c-and-carbon-types) - [Allow C++ bridge code in Carbon files](#allow-c-bridge-code-in-carbon-files) - [Carbon inheritance from C++ types](#carbon-inheritance-from-c-types) - [Support use of advanced C++ features](#support-use-of-advanced-c-features) - [Support basic C interoperability](#support-basic-c-interoperability) - [Non-goals](#non-goals) - [Full parity between a Carbon-only toolchain and mixing C++/Carbon toolchains](#full-parity-between-a-carbon-only-toolchain-and-mixing-ccarbon-toolchains) - [Never require bridge code](#never-require-bridge-code) - [Convert all C++ types to Carbon types](#convert-all-c-types-to-carbon-types) - [Support for C++ exceptions without bridge code](#support-for-c-exceptions-without-bridge-code) - [Cross-language metaprogramming](#cross-language-metaprogramming) - [Open questions to be resolved later](#open-questions-to-be-resolved-later) - [Carbon type inheritance from non-pure interface C++ types](#carbon-type-inheritance-from-non-pure-interface-c-types) - [CRTP support](#crtp-support) - [Object lifetimes](#object-lifetimes) - [Rationale](#rationale) - [Open questions](#open-questions) ## Problem [Carbon's goals](/docs/project/goals.md) include "Interoperability with and migration from existing C++ code". This proposal aims to provide additional detail to that goal, beyond what makes sense to have in the main goals document. ## Background Interoperability and migration are key to Carbon. However, performance and evolution are _higher_ priorities. This proposal aims to outline at what this interaction of priorities should end up looking like in Carbon, as well as indicating a couple trade-offs that will occur. Other language interoperability layers that may offer useful examples are: - [Java/Kotlin](https://kotlinlang.org/docs/reference/java-to-kotlin-interop.html) should be a comparable interoperability story. The languages are different, but share an underlying runtime. This may be closest to the model we desire for Carbon. - [JavaScript/TypeScript](https://www.typescriptlang.org/docs/handbook/migrating-from-javascript.html) is similar to C/C++, where one language is essentially a subset of the other, allowing high interoperability. This is an interesting reference point, but we are looking at a different approach with a clearer boundary. - [C++/Java](https://en.wikipedia.org/wiki/Java_Native_Interface) is an example of requiring specialized code for the bridge layer, making interoperability more burden on developers. The burden of the approach may be considered to correspond to the difference in language memory models and other language design choices. Regardless, the result can be considered higher maintenance for developers than we want for Carbon. - [C++/Go](https://golang.org/cmd/cgo/) is similar to C++/Java. However, Go notably allows C++ bridge code to exist in the .go files, which can ease maintenance of the bridge layer, and is desirable for Carbon. ## Proposal ### Philosophy The C++ interoperability layer of Carbon is the section wherein a specific, restricted set of C++ APIs can be expressed in a way that's callable from Carbon, and similar for calling Carbon from C++. This requires expressing one language as a subset of the other. Our goal is that the constraint of expressivity is loose enough that the resulting amount of bridge code is sustainable. The design for interoperability between Carbon and C++ hinges on: 1. The ability to interoperate with a wide variety of code, such as classes/structs and templates, not just free functions. 2. A willingness to expose the idioms of C++ into Carbon code, and the other way around, when necessary to maximize performance of the interoperability layer. 3. The use of wrappers and generic programming, including templates, to minimize or eliminate runtime overhead. These things come together when looking at how custom data structures in C++ are exposed into Carbon, and the other way around. In both languages, it is reasonable and even common to have customized low-level data structures, such as associative containers. For example, there are numerous data structures for mapping from a key to a value that might be best for a particular use case, including hash tables, linked hash tables, sorted vectors, and btrees. Even for a given data structure, there may be slow but meaningful evolution in implementations strategies. The result is that it will often be reasonable to directly expose a C++ data structure to Carbon without converting it to a "native" or "idiomatic" Carbon data structure. Although interfaces may differ, a trivial adapter wrapper should be sufficient. Many Carbon data structures should also be able to support multiple implementations with C++ data structures being one such implementation, allowing for idiomatic use of C++ hidden behind Carbon. The reverse is also true. C++ code will often not care, or can be refactored to not care, what specific data structure is used. Carbon data structures can be exposed as yet another implementation in C++, and wrapped to match C++ idioms and even templates. For example, a C++ class template like `std::vector` should be usable without wrapper code or runtime overhead, and passing a Carbon type as `T`. The resulting type should be equally usable from either C++ or Carbon code. It should also be easy to wrap `std::vector` with a Carbon interface for transparent use in idiomatic Carbon code. ### Language goal influences #### Performance-critical software Interoperability with C++ will be frequently used in Carbon, whether it's C++ developers trying out Carbon, incrementally migrating a large C++ codebase, or continuing to use a C++ library long-term. In all cases, it must be possible to write interoperable code with zero overhead; copies must not be required. #### Software and language evolution Interoperability will require the addition of features to Carbon which exist primarily to support interoperability use cases. However, these features must not unduly impinge the overall evolution of Carbon. In particular, only a subset of Carbon features will support interoperability with C++. To do otherwise would restrict Carbon's feature set. #### Code that is easy to read, understand, and write Interoperability-related Carbon code will likely be more difficult to read than other, more idiomatic Carbon code. This is okay: aiming to make Carbon code readable doesn't mean that it needs to _all_ be trivial to read. At the same time, the extra costs that interoperability exerts on Carbon developers should be minimized. #### Practical safety guarantees and testing mechanisms Safety is important to maintain around interoperability code, and mitigations should be provided where possible. However, safety guarantees will be focused on native Carbon code. C++ code will not benefit from the same set of safety mechanisms that Carbon offers, so Carbon code calling into C++ will accept higher safety risks. #### Fast and scalable development The interoperability layer will likely have tooling limitations similar to C++. For example, Carbon aims to compile quickly. However, C++ interoperability hinges on compiling C++ code, which is relatively slow. Carbon libraries that use interoperability will see bottlenecks from C++ compile time. Improving C++ is outside the scope of Carbon. #### Modern OS platforms, hardware architectures, and environments Interoperability will apply to the intersection of environments supported by both Carbon and C++. Pragmatically, Carbon will likely be the limiting factor here. #### Interoperability with and migration from existing C++ code Carbon's language goal for interoperability will focus on C++17 compatibility. The language design must be mindful of the prioritization; trade-offs harming other goals may still be made so long as they offer greater benefits for interoperability and Carbon as a whole. Although the below interoperability-specific goals will focus on interoperability, it's also important to consider how migration would be affected. If interoperability requires complex work, particularly to avoid performance impacts, it could impair the ability to incrementally migrate C++ codebases to Carbon. ### Goals #### Support mixing Carbon and C++ toolchains The Carbon toolchain will support compiling C++ code. It will contain a customized C++ compiler that enables some more advanced interoperability features, such as calling Carbon templates from C++. Mixing toolchains will also be supported in both directions: - C++ libraries compiled by a non-Carbon toolchain will be usable from Carbon, so long as they are ABI-compatible with Carbon's C++ toolchain. - The Carbon toolchain will support, as an option, generating a C++ header and object file from a Carbon library, with an ABI that's suitable for use with non-Carbon toolchains. Mixing toolchains restricts functionality to what's feasible with the C++ ABI. For example, developers should expect that Carbon templates will be callable from C++ when using the Carbon toolchain, and will not be available when mixing toolchains because it would require a substantially different and more complex interoperability implementation. This degraded interoperability should still be sufficient for most developers, albeit with the potential of more bridge code. Any C++ interoperability code that works when mixing toolchains must work when using the native Carbon toolchain. The mixed toolchain support must not have semantic divergence. The converse is not true, and the native Carbon toolchain may have additional language support and optimizations. #### Compatibility with the C++ memory model It must be straightforward for any Carbon interoperability code to be compatible with the C++ memory model. This does not mean that Carbon must exclusively use the C++ memory model, only that it must be supported. #### Minimize bridge code The majority of simple C++ functions and types should be usable from Carbon without any custom bridge code and without any runtime overhead. That is, Carbon code should be able to call most C++ code without any code changes to add support for interoperability, even if that code was built with a non-Carbon toolchain. This includes instantiating Carbon templates or generics using C++ types. In the other direction, Carbon may need some minimal markup to expose functions and types to C++. This should help avoid requiring Carbon to generate C++-compatible endpoints unconditionally, which could have compile and linking overheads that may in many cases be unnecessary. Also, it should help produce errors that indicate when a function or type may require additional changes to make compatible with C++. Our priority is that Carbon developers should be able to easily reuse the mature ecosystem of C++ libraries provided by third-parties. A third-party library's language choice should not be a barrier to Carbon adoption. Even for first-party libraries, migration of C++ codebases to Carbon will often be incremental due to human costs of executing and verifying source migrations. Minimizing the amount of bridge code required should be expected to simplify such migrations. #### Unsurprising mappings between C++ and Carbon types Carbon will provide unsurprising mappings for common types. **Primitive types** will have mappings with zero overhead conversions. They are frequently used, making it important that interoperability code be able to use them seamlessly. The storage and representation will need to be equivalent in both languages. For example, if a C++ `__int64` maps to Carbon's `Int64`, the memory layout of both types must be identical. Semantics need to be similar, but edge-case behaviors don't need to be identical, allowing Carbon flexibility to evolve. For example, where C++ would have modulo wrapping on integers, Carbon could instead have trapping behavior on the default-mapped primitive types. Carbon may have versions of these types with no C++ mapping, such as `Int256`. **Non-owning vocabulary types**, such as pointers and references, will have transparent, automatic translation between C++ and Carbon non-owning vocabulary types with zero overhead. **Other vocabulary types** will typically have reasonable, but potentially non-zero overhead, conversions available to map into Carbon vocabulary types. Code using these may choose whether to pay the overhead to convert. They may also use the C++ type directly from Carbon code, and the other way around. **Incomplete types** must have a mapping with similar semantics, similar to primitive types. #### Allow C++ bridge code in Carbon files Carbon files should support inline bridge code written in C++. Where bridge code is necessary, this will allow for maintenance of it directly alongside the code that uses it. #### Carbon inheritance from C++ types Carbon will support inheritance from C++ types for interoperability, although the syntax constructs may look different from C++ inheritance. This is considered necessary to address cases where a C++ library API expects users to inherit from a given C++ type. This might be restricted to pure interface types; see [the open question](#carbon-type-inheritance-from-non-pure-interface-c-types). #### Support use of advanced C++ features There should be support for most idiomatic usage of advanced C++ features. A few examples are templates, overload sets, [attributes](https://en.cppreference.com/w/cpp/language/attributes) and [ADL](https://en.wikipedia.org/wiki/Argument-dependent_name_lookup). Although these features can be considered "advanced", their use is widespread throughout C++ code, including STL. Support for such features is key to supporting migration from C++ features. #### Support basic C interoperability C interoperability support must be sufficient for Carbon code to call popular APIs that are written in C. The ability of C to call Carbon will be more restricted, limited to where it echoes C++ interoperability support. Basic C interoperability will include functions, primitive types, and structs that only contain member variables. Features where interoperability will rely on more advanced C++-specific features, such as templates, inheritance, and class functions, need not be supported for C. These would require a C-specific interoperability model that will not be included. ### Non-goals #### Full parity between a Carbon-only toolchain and mixing C++/Carbon toolchains Making mixed C++/Carbon toolchain support equivalent to Carbon-only toolchain support affects all interoperability features. Mixed toolchains will have degraded support because full parity would be too expensive. The feature of calling Carbon templates from C++ code is key when analyzing this option. Template instantiation during compilation is pervasive in C++. With a Carbon toolchain compiling both Carbon and C++ code, the C++ compiler _can_ be modified to handle Carbon templates differently. Carbon templates can be handled by exposing the Carbon compiler's AST to the C++ compiler directly, as a compiler extension. While this approach is still complex and may not always work, it should offer substantial value and ability to migrate C++ code to Carbon without requiring parallel maintenance of implementations in C++. With a mixed toolchain, the C++ compiler _cannot_ be modified to handle Carbon templates differently. The only way to support template instantiation would be by having Carbon templates converted into equivalent C++ templates in C++ headers; in other words, template support would require source-to-source translation. Supporting Carbon to C++ code translations would be a complex and high cost feature to achieve full parity for mixed toolchains. Requiring bridge code for mixed toolchains is the likely solution to avoid this cost. Note that this issue differs when considering interoperability for Carbon code instantiating C++ templates. The C++ templates must be in C++ headers for reuse, which in turn must compile with the Carbon toolchain to reuse the built C++ code, regardless of whether a separate C++ toolchain is in use. This may also be considered a constraint on mixed toolchain interoperability, but it's simpler to address and less likely to burden developers. To summarize, developers should expect that while _most_ features will work equivalently for mixed toolchains, there will never be full parity. #### Never require bridge code Corner cases of C++ will not receive equal support to common cases: the complexity of supporting any given construct must be balanced by the real world need for that support. For example: - Interoperability will target C++17. Any interoperability support for future versions of C++, including features such as C++20 modules, will be based on a cost-benefit analysis. Exhaustive support should not be assumed. - Support will be focused on idiomatic code, interfaces, and patterns used in widespread open source libraries or by other key constituencies. C++ code will have edge cases where the benefits of limiting Carbon's maintenance costs by avoiding complex interoperability outweighs the value of avoiding bridge code. - Support for low-level C ABIs may be focused on modern 64-bit ABIs, including Linux, POSIX, and a small subset of Windows' calling conventions. #### Convert all C++ types to Carbon types Non-zero overhead conversions should only be _supported_, never _required_, in order to offer reliable, unsurprising performance behaviors. This does not mean that conversions will _always_ be supported, as support is a cost-benefit decision for specific type mappings. For example, consider conversions between `std::vector` and an equivalent, idiomatic Carbon type: - Making conversions zero-overhead would require the Carbon type to mirror the memory layout and implementation semantics of `std::vector`. However, doing so would constrain the evolution of the Carbon type to match C++. Although some constraints are accepted for most primitive types, it would pose a major burden on Carbon's evolution to constrain Carbon's types to match C++ vocabulary type implementations. - These conversions may not always be present, but `std::vector` is a frequently used type. As a result, it can be expected that there will be functions supporting a copy-based conversion to the idiomatic Carbon type. - An interface which can hide the difference between whether `std::vector` or the equivalent, idiomatic Carbon type is in use may also be offered for common types. - It will still be normal to handle C++ types in Carbon code without conversions. Developers should be given the choice of when to convert. #### Support for C++ exceptions without bridge code Carbon may not provide seamless interoperability support for C++ exceptions. For example, translating C++ exceptions to or from Carbon errors might require annotations or bridge code, and those translations may have some performance overhead or lose information. Furthermore, if Carbon code calls a C++ function without suitable annotations or bridging, and that function exits with an exception, the program might terminate. #### Cross-language metaprogramming Carbon's metaprogramming design will be more restrictive than C++'s preprocessor macros. Although interoperability should handle simple cases, such as `#define STDIN_FILENO 0`, complex metaprogramming libraries may require a deep ability to understand code rewrites. It should be reasonable to have these instead rewritten to use Carbon's metaprogramming model. ## Open questions to be resolved later ### Carbon type inheritance from non-pure interface C++ types Some C++ APIs will expect that consumers use classes that inherit from a type provided by the API. It's desirable to have Carbon support, in some way, inheritance from API types in order to use these APIs. It may be sufficient to require the parent type be a pure interface, and that APIs with either use bridge code or switch implementations. That will be determined later. ### CRTP support Although [CRTP](https://en.wikipedia.org/wiki/Curiously_recurring_template_pattern) is a common technique in C++, interoperability support may require substantial work. Libraries based on use of CRTP may require bridge code or a rewrite for Carbon interoperability. More analysis should be done on the cost-benefit of supporting CRTP before making a support decision. ### Object lifetimes Carbon may have a different object lifetime design than C++. For example, Carbon may choose different rules for determining the lifetime of temporaries. This could affect idiomatic use of C++ APIs, turning code that would be safe in C++ into unsafe Carbon code, requiring developers to learn new coding patterns. More analysis should be done on object lifetimes and potential Carbon designs for it before deciding how to treat object lifetimes in the scope of interoperability. ## Rationale The core team agrees with the mapping of the proposed interoperability goals to the overall Carbon goals as rationale. Embedding of C++ bridge code inside Carbon code is inspired in part by experience with CUDA and SYCL, where users provided feedback that these were preferred over alternatives such as OpenCL due to the ability to embed bridge code and device kernels in the same source file as host C++ code. It also matches the approach of bridging between C++ and C where bridge code is often embedded as `extern "C"` within C++ source files. The non-goals seem appropriately motivated by the non-interoperability priorities, without undermining the effectiveness of the interoperability as a whole. We agree with ensuring some level of C interoperability in order to effectively interoperate with both some parts of the C++ ecosystem that rely on the C ABI as well as all other languages which target C-based interoperability. ### Open questions All open questions in the document are intended to be answered later after we have more experience. ================================================ FILE: proposals/p0179.md ================================================ # Create a toolchain team. [Pull request](https://github.com/carbon-language/carbon-lang/pull/179) ## Table of contents - [Problem](#problem) - [Background](#background) - [Proposal](#proposal) - [Scope](#scope) - [Rationale](#rationale) - [Open questions](#open-questions) - [Should the name of the team be "toolchain team" or "implementation team"?](#should-the-name-of-the-team-be-toolchain-team-or-implementation-team) ## Problem The development of a toolchain for Carbon needs a team of people to oversee it. This will primarily involve code reviews and ensuring the quality of the end toolchain, and includes significant work around compile time efficiency as well as developing a cohesive set of libraries for use in developer tools and other contexts. ## Background This is largely a technical team that can fluidly evolve over time as people have time and energy to commit to developing the toolchain's codebase itself. ## Proposal Create a team from initial volunteers and evolve it over time based on who ends up contributing. Notably, contributions can be to the design, documentation, and code _review_ in addition to code or patches themselves. ### Scope Beyond the traditional components of a language toolchain, this team should also cover any needed code review and oversight for other implementation efforts within the Carbon project, such as supporting tools and test suites related to the language itself. There may be some overlap between this _language_ infrastructure and the _project_ infrastructure covered by the [admins](/docs/project/groups.md#admins). This proposal doesn't suggest a hard delineation between these, and if in doubt or there is disagreement, it should be escalated to the [core team](/docs/project/groups.md) rather than spending too much time defining precisely disjoint scopes. ## Rationale Carbon's goals include providing a reference implementation and tooling. Delivering this complex software requires a dedicated team, vision, leadership, and a degree of autonomy. ### Open questions #### Should the name of the team be "toolchain team" or "implementation team"? Some core team members expressed a preference for "implementation team", none expressed a preference for "toolchain team", all were fine either way. As gribozavr noted, and everyone at the review meeting agreed, the team should be free to choose its own name. ================================================ FILE: proposals/p0196.md ================================================ # Language-level safety strategy [Pull request](https://github.com/carbon-language/carbon-lang/pull/196) ## Table of contents - [Problem](#problem) - [Background](#background) - [Proposal](#proposal) - [Open question: probabilistic checks in the hardened build mode](#open-question-probabilistic-checks-in-the-hardened-build-mode) - [Rationale](#rationale) - [Open questions](#open-questions) - [Does the core team believe that we should put a cap on how much performance should be sacrificed for safety, putting more emphasis on probabilistic methods that would allow more attacks through?](#does-the-core-team-believe-that-we-should-put-a-cap-on-how-much-performance-should-be-sacrificed-for-safety-putting-more-emphasis-on-probabilistic-methods-that-would-allow-more-attacks-through) ## Problem Carbon needs to have a clear and consistent strategy for approaching the problems of language-level safety. These problems have been persistent and growing sources of both bugs and security vulnerabilities in C and C++ software. Failure to effectively and carefully address safety concerns is likely to undermine any hope of Carbon being a successful path forward for today's C++ users. ## Background - [Fearless Security: Memory Safety](https://hacks.mozilla.org/2019/01/fearless-security-memory-safety/) - [A proactive approach to more secure code](https://msrc-blog.microsoft.com/2019/07/16/a-proactive-approach-to-more-secure-code/) - [Chromium memory safety](https://www.chromium.org/Home/chromium-security/memory-safety) - [MemSafe](https://doi.org/10.1002/spe.2105) - Notably introduces the terms "_spatial_" and "_temporal_" safety. ## Proposal We propose a safety strategy for Carbon that aims for incrementally increasing the compile-time proven safety while allowing for dynamic checks to cover what remains. It also prioritizes dynamic safety checks that are amenable to being optimized away or being manually disabled for performance-critical use cases where the added dynamic protections are not a viable trade-off. ## Open question: probabilistic checks in the hardened build mode This proposal explicitly discourages probabilistic checks in the hardened build mode because they won't reliably prevent security attacks. Does the core team believe that we should put a cap on how much performance should be sacrificed for safety, putting more emphasis on probabilistic methods that would allow more attacks through? For example, [heap use-after-free detection](https://docs.google.com/document/d/14-_JAMoMBIVgKOUi3iZIRMHJlAyT23kqSVR8wxJPk9M/edit) with 100% accuracy is expected to be a significant expense for hardened builds. MarkUs is estimated to cost 10-50% CPU and 25% RAM in order to catch 100% of issues. For comparison, MTE is estimated to cost 0-20% CPU and 3-6% RAM in order to catch 93% of issues. The CPU and RAM cost of MarkUs is significant, even by comparison with other techniques, and costs will add up as more safety is added. 93% is a reasonably high detection rate for an performance-efficient, probabilistic technique. Would the core team expect to use MTE instead MarkUs in hardened builds? ## Rationale Most of Carbon's goals can be addressed in a somewhat piecemeal fashion. For example, there's probably no need for our designs for generics and for sum types to coordinate how they address performance or readability. Safety, on the other hand, is much more cross-cutting, and so it's important for us to approach it consistently across the whole language. This proposal gives us a common vocabulary for discussing safety, establishes some well-motivated common principles, and provides an overall strategy based on those principles, all of which will be essential to achieving that consistency. This proposal gives a solid basis for thinking about safety in future proposals. The pragmatic choice to focus on the security aspects of safety seems well-aligned with Carbon's goals. In particular, a more idealistic approach to safety, in which every language construct has bounded behavior, would be likely to result in safety being prioritized over performance. By instead considering safety largely from the perspective of security vulnerabilities, and accepting that there will be cases where the pragmatic choice is hardening rather than static or dynamic checks, we can focus on delivering practical safety without being overly distracted from other goals. It is critical that Carbon use build modes to enable writing performance-optimized code (much like C++ today) that can still be built and deployed at reasonable engineering cost with strong guarantees around memory safety for the purposes of security. We think this proposal provides that foundation. Note: The decision by the core team included a request to clarify the wording on build modes as described in chandlerc's post (broken link: `https://forums.carbon-lang.dev/t/request-for-decision-language-level-safety-strategy/196/6`) on the decision thread. ### Open questions #### Does the core team believe that we should put a cap on how much performance should be sacrificed for safety, putting more emphasis on probabilistic methods that would allow more attacks through? There is no cap at this point. Where possible, static checking should be used. In practice, there will be some who will not use the safest option until the cost gets low enough. ================================================ FILE: proposals/p0198.md ================================================ # Comments [Pull request](https://github.com/carbon-language/carbon-lang/pull/198) ## Table of contents - [Problem](#problem) - [Use cases](#use-cases) - [Background](#background) - [Line comments](#line-comments) - [Block comments](#block-comments) - [`#if 0`](#if-0) - [Proposal](#proposal) - [Details](#details) - [Overview](#overview) - [Block comments](#block-comments-1) - [Block comments rationale](#block-comments-rationale) - [Reserved comments](#reserved-comments) - [Reserved comments rationale](#reserved-comments-rationale) - [Alternatives considered](#alternatives-considered) - [Intra-line comments](#intra-line-comments) - [Multi-line text comments](#multi-line-text-comments) - [Block comments](#block-comments-2) - [Documentation comments](#documentation-comments) - [Code folding comments](#code-folding-comments) - [Rationale](#rationale) ## Problem This proposal provides a suggested concrete lexical syntax for comments. ### Use cases Comments serve a variety of purposes in existing programming languages. The primary use cases are: - _Documentation_: human-readable commentary explaining to users and future maintainers of an API what function it performs and how to use it. Such comments are typically attached to function declarations, class definitions, public member declarations, at file scope, and similar levels of granularity in an API. ``` /// A container for a collection of connected widgets. class WidgetAssembly { /// Improve the appearance of the assembly if possible. void decorate(bool repaint_all = false); // ... }; ``` - _Implementation comments_: human-readable commentary explaining intent and mechanism to future readers or maintainers of code, or summarizing the behavior of code to avoid readers or maintainers needing to read it in detail. Such comments are typically used when such details may not be readily apparent from the code itself or may require non-trivial work to infer, and tend to be short. ``` void WidgetAssembly::decorate(bool repaint_all) { // ... // Paint all the widgets that have been changed since last time. for (auto &w : widgets) { if (repaint_all || w.modified > last_foo) w.paint(); } last_decorate = now(); // ... } ``` - _Syntactic disambiguation comments_: comments that contain code or pseudocode intended to allow the human reader to more easily parse the code in the same way that the compiler does. ``` void WidgetAssembly::decorate(bool repaint_all /*= false*/) { // ... /*static*/ std::unique_ptr WidgetAssembly::make() { // ... assembly.decorate(/*repaint_all=*/true); // ... } // end namespace WidgetLibrary ``` - _Disabled code_: comments that contain regions of code that have been disabled, because the code is incomplete or incorrect, or in order to isolate a problem while debugging, or as reference material for a change in progress. It is often considered bad practice to check such comments into version control. ## Background In C++, there are three different ways in which comments are expressed in practice: ### Line comments Single-line comments (and sometimes multiline comments) are expressed in C++ using `// ...`: ``` // The next line declares a variable. int n; // This is a comment about 'n'. ``` (These are sometimes called "BCPL comments".) - Can appear anywhere (at the start of a line or after tokens). - Can contain any text (other than newline). - End at the end of the logical line. - Can be continued by ending the comment with `\` (or `??/` in C++14 and earlier). - Unambiguous with non-comment syntax. - "Nest" in that `//` within `//` has no effect. - Do not nest with other kinds of comment. This comment syntax is often used to express documentation (sometimes with a Doxygen-style `///` introducer) and implementation comments. ### Block comments Comments within lines (or sometimes multiline comments) are expressed in C++ using `/*...*/`: ``` f(/*size*/5, /*initial value*/1); ``` - Can appear anywhere (at the start of a line or after tokens). - Can contain any text (other than `*/`). - End at the `*/` delimiter (which might be separated by a `\` line continuation). - Ambiguous with non-comment syntax: `int a=1, *b=&a, c=a/*b;` though this is not a problem in practice. - Do not nest -- the first `*/` ends the comment. This comment syntax is often used to express syntactic disambiguation comments, and is sometimes used for disabled code. Some coding styles also use this comment style for longer documentation comments (sometimes with a Doxygen-style `/**` introducer). ### `#if 0` Blocks of code are often commented out in C++ programs using `#if 0`: ``` #if 0 int n; #endif ``` - Can appear only at the start of a logical line. - Can only contain sequences of preprocessing tokens (including invalid tokens such as `'`, but not including unterminated multiline string literals). - End at the matching `#endif` delimiter. - Unambiguous with any other syntax. - Nest properly, and can have other kinds of comments nested within. This syntax is generally only used for disabled code. ## Proposal We provide only one kind of comment, which starts with `//` and runs to the end of the line. No code is permitted prior to a comment on the same line, and the `//` introducing the comment is required to be followed by whitespace. This comment syntax is intended to support implementation comments and (_experimentally_) disabled code. The documentation use case is [not covered](#documentation-comments), with the intent that a separate (non-comment) facility is explored for this use case. The syntactic disambiguation use case is not covered, with the intent that the language syntax is designed in a way that avoids this use case. ## Details ### Overview A _comment_ is a lexical element beginning with the characters `//` and running to the end of the line. We have no mechanism for physical line continuation, so a trailing `\` does not extend a comment to subsequent lines. > _Experimental:_ There can be no text other than horizontal whitespace before > the `//` characters introducing a comment. Either all of a line is a comment, > or none of it. The character after the `//` is required to be a whitespace character. Newline is a whitespace character, so a line containing only `//` is a valid comment. The end of the file also constitutes whitespace. All comments are removed prior to formation of tokens. Example: ```carbon // This is a comment and is ignored. \ This is not a comment. var Int: x; // error, trailing comments not allowed ``` ### Block comments > _Experimental:_ No support for block comments is provided. Commenting out > larger regions of human-readable text or code is accomplished by commenting > out every line in the region. #### Block comments rationale There is little value in supporting block comments for the implementation comments use case. We expect such comments to typically be short, and in existing C++ codebases with long implementation comments, it is typical for line comments rather than block comments to be used. Therefore, as we consider the documentation use case to be out of scope, and intend for the syntactic disambiguation use case to be solved by language syntax, the sole purpose of block comments would be for disabled code. Block comments could provide more ergonomic support for intra-line disabled code and multiline blocks of disabled code. Existing block comment syntaxes are not a great fit for the use case of disabling code. The `/* ... */` block comment syntax does not nest in C++, and cannot be used to reliably comment out a block of code because it can be terminated by a `*/` appearing in a `//` comment or in a string literal. The `#if 0 ... #endif` syntax would not be a good fit in Carbon as we do not intend to have a preprocessor in general, and requires the text in between to consist of a mostly-valid token sequence, disallowing certain forms of incomplete code. We should be reluctant to invent something new: it is hard to justify the cost of introducing a novel syntax for the transient and rare use case of disabling code. And similarly, we should be reluctant to use existing syntax with novel semantics, such as a `/* ... */` comment that tokenizes its contents, to avoid surprise to C++ developers. The disabled code use cases can be addressed with line comments, by commenting out each line in the intended region, and reflowing or duplicating lines when disabling code within a line. That may be cumbersome, but it's unclear whether that burden is sufficient to warrant introducing another form of comment into the language. By providing no such form of comments, we aim to discover if the resulting friction warrants a language addition. ### Reserved comments Comments in which the `//` characters are not followed by whitespace are reserved for future extension. Anticipated possible extensions are block comments, documentation comments, and code folding region markers. #### Reserved comments rationale We anticipate the possibility of adding additional kinds of comment in the future. Reserving syntactic space in comment syntax, in a way that is easy for programs to avoid, allows us to add such additional kinds of comment as a non-breaking change. ## Alternatives considered ### Intra-line comments We could include a feature similar to C-style block comments, as a way to provide comments that attach to some element of the program smaller than a line. In C++ code, such comments are frequently used to annotate function parameter names and similar syntactic disambiguation use cases: ``` render(/*use_world_coords=*/true, /*draw_frame=*/false); ``` We expect these use cases to be addressed by extensions to Carbon's grammar, such as by adding named parameters or annotation syntax, to allow such utterances to be expressed as code rather than as comments, so they are meaningful to both the Carbon programmer and the Carbon language tools. We could permit trailing comments on a line that contains other content. Such comments are most frequently used in our sample C++ corpus to describe the meaning of an entity, label, or close brace on the same line: ``` namespace N { int n; // number of hats enum Mode { mode1, // first mode mode2 // second mode }; } // end namespace N ``` In all cases but the last, we expect it to be reasonable to move the comment to before the declaration. The case of the "end namespace" comment is another instance of the syntactic disambiguation use case, which we expect to be addressed by grammar changes. In general, we should avoid any syntax that would need disambiguation comments, either by promoting those comments to the language grammar or by altering the syntax until the comment is unnecessary, such as by not providing a delimited scope syntax for describing the contents of large scopes such as namespaces and packages. For example: ```carbon // This declares the namespace N but does not open a scope. namespace N; // This declares a member of namespace N. @"Number of hats." var Int: N.n; enum N.Mode { @"First mode." mode1; @"Second mode." mode2; } ``` Intra-line comments present a challenge for code formatting tools, which would need to understand what part of the program syntax the comment "attaches to" in order properly reflow the comment with the code. This concern is mitigated, but not fully eliminated, by requiring comments to always be on their own line. We could restrict text comments to appear in only certain syntactic locations to fully resolve this concern, but doing so would remove the flexibility to insert comments in arbitrary places: ``` match (x) { case .Foo(1, 2, // This might be 3 or 4 depending on the size of the Foo. Int: n) => { ... } } ``` We could allow intra-line comments and still retain some idea of what the comment syntactically attaches to by using a directionality marker in the comment: ``` match (x) { case .Foo(1, 2, //> either 3 or 4 >// Int: n) => { ... } case .Foo(2, Int: n //< either 3 or 4 { ... } } ``` Even with an understanding of how comments attach, line wrapping such comments is a complex challenge. For example, formatting in a situation with aligned trailing comments across multiple lines requires special handling: ``` var Int: quality = 3; // The quality of the widget. It should always // be between 1 and 9. var Int: blueness = 72; // The blueness of the widget, as a percentage. ``` Here, a tool that renames `blueness` to `blue_percent` may need to reflow the comment following `quality` as well as the comment following `blueness`. Moreover, if the last line becomes too long, keeping the comment on the same line as the variable may become untenable, requiring a more substantive rewriting: ``` // The blueness of the widget, as a percentage. var Int: blue_percent = Floor(ComputeBluenessRatio() * 100); ``` The decision to not support trailing and intra-line comments is **experimental** and should be revisited if we find there is a need for such comments in the context of the complete language design. ### Multi-line text comments No support is provided for multi-line text comments. Instead, the intent is that such comments are expressed by prepending each line with the same `// ` comment marker. Requiring each line to repeat the comment marker will improve readability, by removing a source of non-local state, and removes a needless source of stylistic variability. The resulting style of comment is common in other languages and well-supported by editors. Even in C and C++ code that uses `/* ... */` to comment out a block of human-readable text, it is common to include a `*` at the start of each comment continuation line. ### Block comments We considered various different options for block comments. Our primary goal was to permit commenting out a large body of Carbon code, which may or may not be well-formed (including code that contains a block comment, meaning that such comments would need to nest). Alternatives considered included: - Fully line-oriented block comments, which would remove lines without regard for whether they are nested within a string literal, with the novel feature of allowing some of the contents of a block string literal to be commented out. This alternative has the disadvantage that it would result in surprising behavior inside string literals containing Carbon code. - Fully lexed block comments, in which a token sequence between the opening and closing comment marker is produced and discarded, with the lexing rules relaxed somewhat to avoid rejecting ill-formed code. This would be analogous to C and C++'s `#if 0` ... `#endif`. This alternative has the disadvantage that it would be unable to cope with incomplete code fragments, such as an unterminated block string literal. It would also be somewhat inefficient to process compared to non-lexing syntaxes, but that's likely to be largely irrelevant given that block comments are expected to be transient. - A hybrid approach, with `//\{` and `//\}` delimiters that are invalid in non-raw string literals, and with an indentation requirement for raw string literals only. This alternative has the disadvantage of introducing additional complexity into the lexical rules by treating different kinds of string literals differently. - Use of `/*` and `*/` as comment markers. This alternative has the disadvantage that it risks confusion by using similar syntax to C and C++ but with divergent semantics. However, given the limited use cases for such comments and a desire to minimize our inventiveness, we are not pursuing any of these options in this proposal. ### Documentation comments We could add a distinct comment syntax for documentation comments, perhaps treating documentation comments as producing real tokens rather than being stripped out by the lexer. However, during discussion, there was significant support for using a syntax that does not resemble a comment for representing documentation. For example, we could introduce an attribute syntax, such as using `@ ` as a prefix to a declaration to attach attributes. Then a string literal attribute can be treated as documentation: ```carbon @"Get the size of the thing." fn GetThingSize() -> Int; @""" Rate the quality of the widget. Returns a quality factor between 0.0 and 1.0. """ fn RateQuality( @"The widget to rate." Widget: w, @"A widget quality database." QualityDB: db) -> Float; ``` This use case will be explored by a future proposal. ### Code folding comments Some code editors are able to "fold" regions of a source file in order to ease navigation. In some cases, these fold regions can be customized by the use of comment lines. For example, in VS Code, this is accomplished with comments containing `#region` and `#endregion`: ``` // #region Functions F and G fn f() { ... } fn g() { ... } // #endregion ``` Supporting such markers as normal text within line comments requires no additional effort. However, we could consider introducing a specific Carbon syntax for region comments, in order to encourage a common representation across code editors. Such support is not covered by this proposal, but could be handled by a new form of comment. ## Rationale - Some comment syntax is necessary to support software evolution, readable and understandable code, and many other goals of Carbon. - A single, simple, and consistent comment style supports Carbon's goal of easy to read and understand code, and fast development tools. - The experiment of restricting comments to be the only non-whitespace text on a line supports Carbon's goal of software evolution. - The careful open lexical space left supports Carbon's goal of language evolution. - The use of `//` as the primary syntax marking comments supports interoperability with C++-trained programmers and codebases by avoiding unnecessary and unhelpful churn of comment syntax. ================================================ FILE: proposals/p0199.md ================================================ # String literals [Pull request](https://github.com/carbon-language/carbon-lang/pull/199) ## Table of contents - [Problem](#problem) - [Background](#background) - [Existing practice](#existing-practice) - [Proposal](#proposal) - [Details](#details) - [Non-raw string literals](#non-raw-string-literals) - [Escape sequences](#escape-sequences) - [Raw string literals](#raw-string-literals) - [Encoding](#encoding) - [Alternatives considered](#alternatives-considered) - [Block string literals](#block-string-literals) - [Leading whitespace removal](#leading-whitespace-removal) - [Terminating newline](#terminating-newline) - [Escape sequences](#escape-sequences-1) - [Raw string literals](#raw-string-literals-1) - [Trailing whitespace](#trailing-whitespace) - [Line separators](#line-separators) - [Internal whitespace](#internal-whitespace) - [Rationale](#rationale) ## Problem This proposal specifies lexical rules for constant strings in Carbon. ## Background We wish to provide a syntax for writing literals containing human-readable text. Note that "human-readable text" here should be understood broadly: such text may be subject to further processing, and may in some cases be intended to be interpreted by a computer rather than by a human (such as a regular expression, program source code, or a C++ mangled name), but broadly represents a sequence of characters rather than arbitrary binary data. Such text is typically represented in an _encoding_, which is a bidirectional mapping between a sequence of characters in text and a sequence of bounded integer values known as _code units_, suitable for storage, transmission, and processing. For example, the Russian word углерод (carbon) is encoded in the UTF-8 encoding as D1168316 D016B316 D016BB16 D016B516 D1168016 D016BE16 D016B416. ### Existing practice See [Comparison of programming languages (strings) on Wikipedia](https://en.wikipedia.org/wiki/Comparison_of_programming_languages_%28strings%29). Simple string literals are specified in most programming languages as text delimited by double-quote characters, `"like this"`. Such string literals usually are restricted to begin and end on the same source line. Three additional features are commonly seen: - Escape sequences, which permit string literals to include characters that are difficult to type, are ambiguous for the reader, or that would be problematic in some way (such as whitespace characters, characters that are invisible, and characters that change how other characters are rendered), and also to include arbitrary code units. One common convention is to use `\` to introduce an escape sequence, where, for example: - `\n` represents a newline character, - `\u1234` represents the Unicode character U+1234, - `\xAB` represents the code unit AB16, - `\"` represents a single `"` character and does not terminate the string literal, - `\\` represents a single `\` character, and so on. The set of single-letter escape sequences has a lot of commonality between languages, with some variation between older and newer languages: - C++ and Python allow `\a`, `\b`, `\f`, `\n`, `\r`, `\t`, `\v` for bell, backspace, form feed, new line, carriage return, tab, and vertical tab, respectively. - JavaScript drops support for `\a`. - Java additionally drops support for `\v`. - Rust and Swift additionally drop support for `\b` and `\f`, leaving only `\n`, `\r`, and `\t`. The rules for numeric escape sequences differ between kinds of escape sequence and between languages. The rules in C++, JavaScript, Python, Rust, and Swift are as follows: - `\123` is interpreted as a octal code unit value, and up to three octal digits are consumed. In JavaScript and C++, values greater than 3778 (25510) are invalid (assuming an 8-bit character type). In Python, values greater than 3778 are interpreted modulo 256. Rust and Swift do not allow octal escapes in general, but do allow `\0` as a special case. - `\xAB` is interpreted as a hexadecimal code unit value. In C++, any nonzero number of hexadecimal digits can follow as part of the escape sequence. In Python, JavaScript, and Rust, exactly two digits are required. In Rust, the value must be less than or equal to 7F16 except for `b`-prefixed strings (byte array literals). Swift does not support this form of escape sequence. - `\uABCD` is interpreted as a hexadecimal code point value. In C++, Python, and JavaScript, exactly four hexadecimal digits can follow, but JavaScript allows any nonzero number of digits to be specified using `\u{ABCDE}` notation. Rust and Swift support only the `\u{ABCDE}` notation. - `\U0010FFFD` is interpreted as a hexadecimal code point value in C++ and Python, but not in JavaScript, Rust, or Swift, and permits exactly eight hexadecimal digits. - Raw string literals, in which escape sequences are not recognized. These are often used in situations where escape sequences are undesirable, but in which the escape character or regular string terminator is used frequently. Such literals are useful when embedding one machine-readable language in another, when those languages share some escaping conventions. Such functionality may also provide a way to customize the string delimiters. - In Python, raw string literals have an `r` prefix: `r"li\ngo"` is a six character string whose third character is `\`. - In C++, raw string literals have an `r` prefix, along with a custom delimiter (which may be empty): `r"DELIM(li\ngo)DELIM"` is a six character string (plus a nul terminator). - In Rust, raw string literals have an `r` prefix and any matching number of `#` characters enclose the string contents: the third character of `r"lo\ng"` is `\`, and the second character of `r#" " "#` is `"`. - In Swift, raw string literals are a generalization of regular string literals: literals are introduced by any number, _N_, of `#` characters followed by a `"`, terminated by `"` followed by _N_ `#` characters, and escape sequences are introduced by a `\` followed by _N_ `#` characters: `#" " # \n \#n \#\# "#` has the same contents as the C++ string literal `" \" # \\n \n \\# "`. - In Java, raw string literals are delimited by a sequence of one or more backticks instead of double quotes: the fourth character of \`\`foo\`\bar\`\` is a backtick and the fifth is a backslash. - Multiline string literals provide a mechanism for a string to easily span more than one line of source text. - In C++, Rust, and Java, raw string literals are used to represent multiline string literals. - In Python, different delimiters (`"""` or, in Python, `'''` instead of `"` or `'`) are used to represent multiline string literals, and plain `"` and `""` can thereby appear in the string contents, but these literals otherwise behave the same as regular string literals. - In Swift, different delimiters are used (`"""` instead of `"`), but unlike in Python, the string content cannot be on the same line as the delimiters, and the resulting mandatory leading and trailing newlines are not included in the string. Internal newlines can be removed by preceding them with backslashes. - In JavaScript, backtick-delimited strings can contain newlines; this syntax also allows string interpolation as described below. In addition, some languages, primarily scripting languages, also provide a mechanism for string interpolation, wherein a string value is formed by including the formatted values of some variables in a given format string. For example, `"Hello, $planet."` might produce a string value including the formatted value of the variable named `planet`. Such interpolation facilities are outside the scope of this proposal. ## Proposal - Single-line string literals are delimited by `"`s: `"hello"` - Multi-line string literals are introduced by a `"""` followed by a newline and terminated by a line beginning with a `"""`. The indentation of the terminating line is removed from all preceding lines: ```carbon var String: henry_vi = """ The winds grow high; so do your stomachs, lords. How irksome is this music to my heart! When such strings jar, what hope of harmony? I pray, my lords, let me compound this strife. -- History of Henry VI, Part II, Act II, Scene 1, W. Shakespeare """; ``` Only the final line of this string literal begins with whitespace. The opening newline is not part of the string's contents, but the trailing newline is; the first character of this example string is `T` and the last character is a newline. - The opening `"""` of a multi-line string literal can be followed by a _file type indicator_, to assist tooling in understanding the intent of the string. This indicator has no effect on the meaning of the program. ```carbon var String: cpp_snippet = """c++ #include int main() { std::cout << "hello world" << std::endl; } """; ``` - Escape sequences are introduced with a `\` character; the most common C and C++ escape sequences are supported: `"hello\nworld"`. Octal escapes (`\177`), `\a`, `\b`, `\f` and `\v` are removed. `\uNNNN` and `\U00NNNNNN` are replaced by `\u{NNNNNN}`. An escape sequence `\` is permitted in multi-line string literals, and results in no string contents. - Raw string literals are supported, for both the single and multi-line case, following the Swift convention: they are introduced by prefixing the opening delimiter with one or more `#`s, and suffixing the closing delimiter with a matching number of `#`s: `#"foo\s*bar"#` or `#"foo"bar"#`. Escape sequences can be introduced in a raw string literal by inserting a matching number of `#`s after the `\` character: `#"foo\#nbar"#` contains a newline character. - Unlike in C and C++, adjacent string literals are not implicitly concatenated. ## Details ### Non-raw string literals A _simple string literal_ is formed of a sequence of - characters other than backslashes, double quotation marks, and vertical whitespace - [escape sequences](#escape-sequences) enclosed in double quotation marks (`"`). Each escape sequence is replaced with the corresponding character sequence or code unit sequence. ```carbon var String: lucius = "The strings, my lord, are false."; ``` A _block string literal_ starts with three double quotation marks, followed by an optional file type indicator, followed by a newline, and ends at the next instance of three double quotation marks whose first `"` is not part of a `\"` escape sequence. The closing `"""` shall be the first non-whitespace characters on that line. The lines between the opening line and the closing line (exclusive) are _content lines_. The content lines shall not contain `\` characters that do not form part of an escape sequence. The _indentation_ of a block string literal is the sequence of horizontal whitespace preceding the closing `"""`. Each non-empty content line shall begin with the indentation of the string literal. The content of the literal is formed as follows: - The indentation of the closing line is removed from each non-empty content line. - All trailing whitespace on each line, including the line terminator, is replaced with a single line feed (U+000A) character. - The resulting lines are concatenated. - Each [escape sequence](#escape-sequences) is replaced with the corresponding character sequence or code unit sequence. A content line is considered empty if it contains only whitespace characters. ```carbon var String: w = """ This is a string literal. Its first character is 'T' and its last character is a newline character. It contains another newline between 'is' and 'a'. """; // This string literal is invalid because the """ after 'closing' terminates // the literal, but is not at the start of the line. var String: invalid = """ error: closing """ is not on its own line. """; ``` A _file type indicator_ is any sequence of non-whitespace characters other than `"` or `#`. The file type indicator has no semantic meaning to the Carbon compiler, but some file type indicators are understood by the language tooling (for example, syntax highlighter, code formatter) as indicating the structure of the string literal's content. ```carbon // This is a block string literal. Its first two characters are spaces, and its // last character is a line feed. It has a file type of 'c++'. var String: starts_with_whitespace = """c++ int x = 1; // This line starts with two spaces. int y = 2; // This line starts with two spaces. """; ``` The file type indicator might contain semantic information beyond the file type itself, such as instructions to the code formatter to disable formatting for the code block. **Open question:** This proposal does not suggest any concrete set of recognized file type indicators. It would be useful to informally specify a set of well-known indicators, so that tools have a common understanding of what those indicators mean, perhaps in a best practices guide. #### Escape sequences Within a string literal, the following escape sequences are recognized: | Escape | Meaning | | ------------- | -------------------------------------------------------- | | `\t` | U+0009 CHARACTER TABULATION | | `\n` | U+000A LINE FEED | | `\r` | U+000D CARRIAGE RETURN | | `\"` | U+0022 QUOTATION MARK (`"`) | | `\'` | U+0027 APOSTROPHE (`'`) | | `\\` | U+005C REVERSE SOLIDUS (`\`) | | `\0` | Code unit with value 0 | | `\xHH` | Code unit with value HH16 | | `\u{HHHH...}` | Unicode code point U+HHHH... | | `\` | No string literal content produced (block literals only) | This includes all C++ escape sequences except: - `\?`, which was historically used to escape trigraphs in string literals, and no longer serves any purpose. - `\ooo` octal escapes, which are removed because Carbon does not support octal literals; `\0` is retained as a special case, which is expected to be important for C interoperability. - `\uABCD`, which is replaced by `\u{ABCD}`. - `\U0010FFFF`, which is replaced by `\u{10FFFF}`. - `\a` (bell), `\b` (backspace), `\v` (vertical tab), and `\f` (form feed). `\a` and `\b` are obsolescent, and `\f` and `\v` are largely obsolete. These characters can be expressed with `\x07`, `\x08`, `\x0B`, and `\x0C` respectively if needed. Note that this is the same set of escape sequences supported by [Swift](https://docs.swift.org/swift-book/LanguageGuide/StringsAndCharacters.html#ID295) and [Rust](https://doc.rust-lang.org/reference/tokens.html), except that, unlike in Swift, support for `\xHH` is provided. While this proposal takes a firm stance on not permitting octal escape sequences, the decision to not allow `\1`..`\7`, and more generally to not treat `\DDDD` as a decimal escape sequence, is _experimental_. In the above table, `H` represents an arbitrary hexadecimal character, `0`-`9` or `A`-`F` (case-sensitive). Unlike in C++, but like in Python, `\x` expects exactly two hexadecimal digits. As in JavaScript, Rust, and Swift, Unicode code points can be expressed by number using `\u{10FFFF}` notation, which accepts any number of hexadecimal characters. Any numeric code point in the ranges 016-D7FF16 or E00016-10FFFF16 can be expressed this way. _Open question:_ Some programming languages (notably Python) support a `\N{unicode character name}` syntax. We could add such an escape sequence, but this proposal does not include one. Future proposals considering adding such support should pay attention to work done by C++'s Unicode study group in this area. The escape sequence `\0` shall not be followed by a decimal digit. In cases where a null byte should be followed by a decimal digit, `\x00` can be used instead: `"foo\x00123"`. The intent is to preserve the possibility of permitting decimal escape sequences in the future. A backslash followed by a line feed character is an escape sequence that produces no string contents. This escape sequence is _experimental_, and can only appear in multi-line string literals. This escape sequence is processed after trailing whitespace is replaced by a line feed character, so a `\` followed by horizontal whitespace followed by a line terminator removes the whitespace up to and including the line terminator. Unlike in Rust, but like in Swift, leading whitespace on the line after an escaped newline is not removed, other than whitespace that matches the indentation of the terminating `"""`. A character sequence starting with a backslash that doesn't match any known escape sequence is invalid. Whitespace characters other than space and, for block string literals, new line optionally preceded by carriage return are disallowed. All other characters (including non-printable characters) are preserved verbatim. Because all Carbon source files are required to be valid sequences of Unicode characters, code unit sequences that are not valid UTF-8 can only be produced by `\x` escape sequences. The choice to disallow raw tab characters in string literals is _experimental_. ```carbon var String: fret = "I would 'twere something that would fret the string,\n" + "The master-cord on's \u{2764}\u{FE0F}!"; // This string contains two characters (prior to encoding in UTF-8): // U+1F3F9 (BOW AND ARROW) followed by U+0032 (DIGIT TWO) var String: password = "\u{1F3F9}2"; // This string contains no newline characters. var String: type_mismatch = """ Shall I compare thee to a summer's day? Thou art \ more lovely and more temperate.\ """; var String: trailing_whitespace = """ This line ends in a space followed by a newline. \n\ This line starts with four spaces. """; ``` ### Raw string literals In order to allow strings whose contents include backslashes and double quotes, the delimiters of string literals can be customized by prefixing the opening delimiter with _N_ `#` characters. A closing delimiter for such a string is only recognized if it is followed by _N_ `#` characters, and similarly, escape sequences in such string literals are recognized only if the `\` is also followed by _N_ `#` characters. A `\`, `"`, or `"""` not followed by _N_ `#` characters has no special meaning. | Opening delimiter | Escape sequence introducer | Closing delimiter | | ----------------- | ----------------------------- | ----------------- | | `"` / `"""` | `\` (for example, `\n`) | `"` / `"""` | | `#"` / `#"""` | `\#` (for example, `\#n`) | `"#` / `"""#` | | `##"` / `##"""` | `\##` (for example, `\##n`) | `"##` / `"""##` | | `###"` / `###"""` | `\###` (for example, `\###n`) | `"###` / `"""###` | | ... | ... | ... | For example: ```carbon var String: x = #""" This is the content of the string. The 'T' is the first character of the string. """ <-- This is not the end of the string. """#; // But the preceding line does end the string. // OK, final character is \ var String: y = #"Hello\"#; var String: z = ##"Raw strings #"nesting"#"##; var String: w = #"Tab is expressed as \t. Example: '\#t'"#; ``` Note that both a single-line raw string literal and a multi-line raw string literal can begin with `#"""`. These cases can be distinguished by the presence or absence of additional `"`s later in the same line: - In a single-line raw string literal, there must be a `"` and one or more `#`s later in the same line terminating the string. - In a multi-line raw string literal, the rest of the line is a file type indicator, which can contain neither `"` nor `#`. ```carbon // This string is a single-line raw string literal. // The contents of this string start and end with exactly two "s. var String: ambig1 = #"""This is a raw string literal starting with """#; // This string is a block raw string literal with file-type 'This', // whose contents start with "is a ". var String: ambig2 = #"""This is a block string literal with file type 'This', first character 'i', and last character 'X': X\# """#; // This is a single-line raw string literal, equivalent to "\"". var String: ambig3 = #"""#; ``` ### Encoding A string literal results in a sequence of 8-bit bytes. Like Carbon source files, string literals are encoded in UTF-8. This proposal includes no mechanism to request that any other encoding is used. The expectation is that if another encoding is needed, the string literal can be transcoded from UTF-8 during compilation. There is no guarantee that the string is valid UTF-8, however, because arbitrary byte sequences can be inserted by way of `\xHH` escape sequences. This decision is _experimental_, and should be revisited if we find sufficient motivation for directly expressing string literals in other encodings. Similarly, as library support for a string type evolves, we should consider including string literal syntax (perhaps as the default) that guarantees the string content is a valid UTF-8 encoding, so that valid UTF-8 can be distinguished from an arbitrary string in the type system. In such string literals, we should consider rejecting `\xHH` escapes in which HH is greater than 7F16, as in Rust. ## Alternatives considered ### Block string literals We could avoid including a block string literal in general, and instead construct multi-line strings by string concatenation, with either C-style juxtaposition or with an explicit concatenation operator. But doing so would be more verbose and would make the expression of the source code be further from the programmer's intent. We could use raw string literals to provide block string literal syntax, as C++ does. However, this couples two orthogonal choices: whether escape sequences should be recognized and whether the string is intended to span multiple lines. In C++ code, the inability to use escape sequences in multi-line string literals sometimes awkward. For example: ```c++ std::string make_rule = "%s: %s\n\t$(CC) -c -o $@ $< $(CFLAGS)\n\n" "main:\n\t$(CC) %s -o %s\n"; ``` can be written under this proposal as ```carbon var String: make_rule = """make %s: %s \t$(CC) -c -o $@ $< $(CFLAGS) main: \t$(CC) %s -o %s """; ``` improving readability while still making the semantically-meaningful presence of tabs visible even in editors / code browsers that do not distinguish tabs from spaces. #### Leading whitespace removal Block string literals could use explicit characters in the body to indicate the amount of leading whitespace to be removed: ```carbon var String: x = """ | starts with two spaces. """; ``` This would allow the correct indentation to be determined as soon as the first line after the opening `"""` is seen. However, this adds lexical complexity, and harms the ability to copy-paste string contents into other contexts. #### Terminating newline We could choose to exclude the trailing newline (like in Swift). Informal surveys suggest that expectations for whether to include or exclude the trailing newline vary. The intended use case for block string literals is to represent multi-line strings. When forming a single larger string from concatenation of multi-line string literals, including the trailing newline but not the leading newline -- or, more generally, including a newline at the end of each line in the string -- provides the best alignment between the source-level syntax and the result. For example: ```carbon fn Run() { print("""c++ class X { public: X() {} """); for (var String: decl in GetMemberDecls()) { print decl; } print("""c++ private: """); print(GetFields()); print("""c++ }; """); } ``` As is, the output printed by this example can be visualized by ignoring all lines other than those between the `"""`s. If we excluded the trailing newline, additional blank lines would be required at the end of each string literal, harming the readability of the example. ### Escape sequences We could support octal escape sequences, as many C family languages do. However, they are considered antiquated in C++ code, and supporting them would be inconsistent with our decision to not support octal numeric literals. A quick informal poll suggests that many C++ programmers do not realize that `\123` is an octal escape sequence, not a decimal one. We could support `\123` as a decimal escape sequence. However, doing so may lead to surprise when migrating C++ code to Carbon. This possibility should be revisited once Carbon matures and we have a better idea of how the migration process is expected to proceed. We could allow arbitrary-length `\x` escape sequences, as C++ does, and include some explicit mechanism to terminate such a sequence. For example, we could treat `\` for an arbitrary whitespace character in the same way we treat `\`, and use `"\xAB\ C"` to terminate an escape sequence prematurely. However, this is unnecessarily inventive, and the Python approach of requiring exactly two hexadecimal characters after `\x` is adequate, assuming we do not intend to support string literal element types other than 8-bit bytes. If we do find we want to support wider element types in future (for example, if we want to add a UTF-16 or UTF-32 string literal), `\x{ABCD}` can be used. We could permit `\` even in non-block string literals to allow them to be line-wrapped, but there seems to be little benefit to doing so, as a block string literal can always be used instead. We could adopt Python's `\N{unicode character name}` syntax. But there is no pressing need to add such a syntax imminently, and concerns have been raised both over the exact ways in which characters are named and over compatibility with upcoming C++ language extensions in this area, so this syntax is not being proposed at this time. We could allow an `\e` escape sequence for the U+001C ESCAPE character. This character is a common extension in C and C++ compilers, and appears to primarily be used to hardcode ANSI terminal escape sequences, such as `"\e[32mgreen text\e[0m"`. This proposal doesn't explicitly reject this idea. However, if we consider adopting such an extension in the future, we should consider whether a library facility for simple terminal operations would be a more valuable addition than this escape sequence. We could retain the `\uNNNN` escape sequence to give a terser notation for the common case of a Unicode code point that is most naturally written as four hexadecimal digits -- that is, all code points in the Basic Multilingual Plane. This is the approach taken by JavaScript. However, following Swift and Rust in permitting only `\u{NNNN}` is simpler and avoids redundancy. We expect explicit `\u` escapes to be rare: we expect regular, printable Unicode characters that are normalized in NFC to be written directly in the source file, rather than spelled with `\u` escapes. Explicit `\u` escapes would be useful where the code point value is important -- for example, in test data -- or where special characters such as directionality markers or non-normalized characters are desired, but for such uses, the longer `\u{NNNN}` form seems adequate. ### Raw string literals The approach to raw string literals in this proposal is based on Swift's raw strings facility. We could use a different mechanism other than a sequence of `#`s to support nesting raw string literals. For example, we could adopt something like C++'s semi-arbitrary delimiters `R"foo(string contents)foo"`. However, this level of customizability seems unwarranted: raw string literals are unlikely to nest more than one or two levels deep, so using `#"..."#`, `##"..."##`, `###"..."###` for successive nesting levels seems unproblematic, and removes the need for the programmer to make an arbitrary choice. We could use a delimiter other than `#` to demarcate raw string literals. At this stage in Carbon's development, we don't know exactly which characters will be useful in operators, but it seems reasonable to assume a mostly C++-like operator set, which gives us a variety of characters that cannot appear immediately before a string literal: at least `@` `#` `$` `)` `]` `}` `\` `.` all appear likely to be available. Of these, `\` is likely to be problematic due to its use in escape sequences, closing brackets followed by string literals might one day be useful in some grammar constructs, and `.` seems a little too close to resembling designator. That leaves `@`, `#`, and `$`, and multiple existing languages have used `#` for this purpose. We could disallow use of _N_ `#`s as a delimiter if a lower value of _N_ would work. However, this would make the language brittle under maintenance: removing the last nested string literal from a quoted block of code would require changing the delimiters. We could use Rust-style raw strings, which add a leading `r`, permit zero `#`s to be used in raw strings, and do not provide a facility for escape sequences in raw strings. There are several reasons to prefer Swift-style raw strings: - Swift raw strings are not a distinct language feature; rather, they are a generalization of non-raw strings -- non-raw strings are simply the case where the number of `#` characters in the delimiters and escape sequence introducer is zero. - Permitting escape sequences even in raw strings means that there is no loss of functionality when using a raw string, and code changes under maintenance that would require use of a facility only available by way of an escape sequence (such as inclusion of trailing whitespace in a block string literal) do not force a reversion to a non-raw string literal. There are also several reasons to prefer the Rust-style approach: - In the most common case, a Rust raw string will be one character shorter. - Less lexical space is used: Swift-style raw strings remove the possibility of using `#` as a prefix operator, whereas the leading `r` in Rust-style raw strings does not. - Certain character sequences are hard to express in Swift-style raw strings. Specifically, string literals such as `"\\################"` cannot readily be expressed as a raw string. Such string literals are extremely rare, but not nonexistent, in one large sample C++ corpus. If the final issue is concerning, we have a path to address it, by specifying that `\` followed by N+1 or more `#`s is left alone, just like `\` followed by N-1 or fewer `#`s is left alone. Formally, this can be accomplished by defining `\#` as an escape sequence that expands to itself, that is, to a backslash followed by N+1 `#` characters. #### Trailing whitespace We could preserve trailing whitespace in at least raw block string literals, and perhaps in all block string literals. However, this would mean that visually identical programs could have different meanings, and even that transformations performed automatically on save by some editors (removing trailing whitespace) could change the meaning of a program. It might also mean that raw string literals are no longer a generalization of non-raw string literals. Under this proposal, trailing whitespace can be included in a block string literal by following it with `\n\`: ``` var String: authors = """markdown *Authors*: \n\ Me \n\ Someone Else """; ``` In a single-`#` raw string literal, the same can be accomplished with the more-verbose terminator `\#n\#`, and so on. #### Line separators Raw block string literals could preserve the form of vertical whitespace used to terminate each line. This would allow uncommon forms of vertical whitespace (for example, vertical tab and form feed) to be included in raw string literals, but would create a risk that the meaning of a program would be different when the source code is checked out on an operating system that uses line feed as a line terminator versus when the source code is checked out on an operating system that uses a different line terminator (such as carriage return followed by line feed). This would also mean that raw string literals are no longer a generalization of non-raw string literals. ### Internal whitespace We could allow raw tab characters in string literals. However, raw tab characters harm the readability of the program, and we would like to encourage the use of `\t` escapes instead in situations where they are available, even if this means that the more verbose form `\#t` needs to be used in raw string literals. ## Rationale This proposal supports the goal of making Carbon code [easy to read, understand, and write](/docs/project/goals.md#code-that-is-easy-to-read-understand-and-write), by ensuring that essentially every kind of string content can be represented in a Carbon string literal, in a way that is natural, toolable, and easy to read: - Multi-line strings are supported by multi-line string literals, and the rules for stripping leading indentation enhance readability by allowing those literals to avoid visually disrupting the indentation structure of the code. - Strings that make extensive use of `\` and `"` are supported by raw string literals. - Treating raw versus ordinary and single-line versus multi-line as orthogonal allows Carbon to support all 4 combinations while keeping the language simple. - The handling of `\#` within raw string literals makes it possible to use escape sequences within raw string literals when necessary, for example to embed arbitrary byte values or Unicode data. This ensures that the programmer is never prevented from using a raw string literal, or forced to assemble a single logical string by concatenating ordinary and raw literals (with the negligible and fixable exception of strings like `"\\################"`, as noted in the proposal). - "File type indicators" make it easier for tooling to understand the contents of literals, in order to provide features such as syntax highlighting, automated formatting, and potentially even certain kinds of static analysis, for code that's embedded in string literals. - Support for non-Unicode strings by way of `\x` ensures "support for software outside the primary use case". - Avoids unnecessary invention, following Rust and particularly Swift. ================================================ FILE: proposals/p0253.md ================================================ # 2021 Roadmap [Pull request](https://github.com/carbon-language/carbon-lang/pull/253) ## Objective for 2021: speed up Carbon development The main objective of the Carbon project in 2021 is to speed up the development of the project, even while it remains both an experiment and private. This will require improving at least two dimensions: - Increase the investment by existing individuals and organizations. - Increase the breadth of different individuals and organizations investing in Carbon. We expect to make progress on this objective in 2021 primarily by making Carbon's design more concrete and with a more specific and easily understood value proposition. As a result, our key results are primarily around specific artifacts that we think will help support the scaling up of Carbon development efforts. We also need to identify key missing participants and onboard them throughout 2021. ## Key results in 2021 There are several milestones that we believe are on the critical path to successfully achieving our main goal for the year, and point to concrete areas of focus for the project. ### Broaden core team representation so no organization is >50% Our goal is that no single organization makes up >50% of the core team to ensure that we are including as broad and representative a set of perspectives in the evolution of Carbon as possible. ### Example ports of C++ libraries to Carbon (100% of [woff2](https://github.com/google/woff2), 99% of [RE2](https://github.com/google/re2)) The first part of this result is that all of the woff2 library is ported to Carbon in a way that exports the same C++ API. There should be no gaps in this port given that woff2 has a very simple C++ API and uses few C++ language features. RE2 is a larger library using significantly more language features. For that part of the result, fewer than 1% of its C++ lines of code should be missing a semantically meaningful port into Carbon code. An important nuance of this goal is that it doesn't include building a complete Carbon standard library beyond the most basic necessary types. The intent is to exercise and show the interoperability layers of Carbon by reusing the C++ standard library in many cases and exporting a compatible C++ API to both woff2 and RE2's current API. While this key result isn't directly tied to the main objective, we believe it represents a critical milestone for being able to achieve this objective. It both measures our progress solidifying Carbon's design and demonstrating the value proposition of Carbon. Note that both woff2 and RE2 libraries are chosen somewhat arbitrarily and could easily be replaced with a different, more effective libraries to achieve the fundamental result of demonstrating a compelling body of cohesive design and the overarching value proposition. #### Language design covers the syntax and semantics of the example port code. We should have a clear understanding of the syntax and semantics used by these example ports. While this should include accepted proposals, it doesn't necessarily require either formal specification or implementation. ### Demo implementation of core features with working examples A core set of Carbon features should be implemented sufficiently to build working examples of those features and run them successfully. These features could include: - User-defined types, functions, namespaces, packages, and importing. - Basic generic functions and types using interfaces. - Initial/simple implementation of safety checking including at least bounds checking, simple lifetime checking, and simple initialization checking. - Sum types sufficient for optional-types to model nullable pointers. - Pattern matching sufficient for basic function overloading on types and arity, as well as unwrapping of optional types for guard statements. Stretch goals if we can hit the above: - Instantiating a basic C++ template through interop layer for use within Carbon. The demo implementation should also provide demos outside of specific language features including: - Basic benchmarking of the different phases of compilation (lexing, parsing, etc). - A basic REPL command line. Stretch goals if we can hit the above: - Automatic code formatter on top of the implementation infrastructure. - A [compiler explorer](https://compiler-explorer.com/) fork with REPL integrated. Benchmarking at this stage isn't expected to include extensive optimization. Instead, it should focus on letting us track large/high-level impact on different phases as they are developed or features are added. They may also help illustrate initial high-level performance characteristics of the implementation, but the long term focus should be on end-to-end user metrics. Automatic code formatting could be achieved many ways, but it seems useful to ensure the language and implementation both support use cases like formatting. ### Executable semantic specification for core features with test cases This should include both a human readable rendering of the formal semantics as well as an execution environment to run test cases through those semantics. The core features which should be covered by these semantics are: - User-defined types, functions, namespaces, packages, and importing. - Basic generic functions and types using interfaces. - Sum types sufficient for optional-types to model nullable pointers. This is intentionally a subset of the features covered by the demo implementation. The intent is to reflect that _completing_ coverage of the features in the specification is a slightly lower priority, and instead we should rapidly spike out as complete of a demo as possible and come back to the semantics if possible. ## Beyond 2021 Longer term goals are hard to pin down and always subject to change, but we want to give an idea of what kinds of things are expected at a high level further out in order to illustrate how the goals and priorities we have in 2021 feed into subsequent years. ### Potential 2022 goals: finish 0.1 language, make it public We expect that at some point in 2022 we will need to shift the experiment to be public. This will allow us to significantly expand both those directly involved and contributing to Carbon but also those able to evaluate and give us feedback. We don't expect Carbon to shift away from an experiment until after it becomes public and after we have been able to collect and incorporate a reasonable amount of feedback from the broader industry and community. We'll also need to start broadening our scope: - Expand the standard library to at least cover everything needed for self hosting. - Develop initial C++ to Carbon migration tooling. ### Potential 2023 goals: finish 0.2 language, stop experimenting Once Carbon is moving quickly and getting public feedback, we should be able to conclude the experiment. We should know if this is the right direction for moving C++ forward for a large enough portion of the industry and community, and whether the value proposition of this direction outweighs the cost. However, there will still be a _lot_ of work to make Carbon into a production quality language, even if the experiment concludes successfully. Some concrete goals that might show up in this time frame: - Self-hosting toolchain, including sufficient Carbon standard library support. - Expand design of standard library to include, at least directionally, critical and complex areas. For example: concurrency/parallelism and networking/IO. - Migration tooling sufficient to use with real-world libraries and systems. This might be used to help with self-hosting Carbon, as well as by initial early adopters evaluating Carbon. ### Potential 2024-2025 goals: _ship_ 1.0 language & organization A major milestone will be the first version of a production language. We should also have finished transferring all governance of Carbon to an independent open source organization at that point. However, we won't know what a more realistic or clear schedule for these milestones will be until we get closer. Another important aspect of our goals in this time frame is expanding them to encompass the broader ecosystem of the language: - End-to-end developer tooling and experience. - Teaching and training material. - Package management. - Etc. ## Rationale By its nature, we're not sure a planning document like this can directly advance Carbon's goals, but we think this roadmap does a good job of keeping us focused on the work that will help us generate concrete evidence as to whether Carbon is likely to successfully achieve those goals (and help us figure out how to course-correct if it's not). Increasing the representation of non-Googlers on the core team is an important indicator (as well as enabler) of building an "open and inclusive community", not to mention of having a broad enough user base to succeed, and the example ports, demo implementation, and executable semantics will help us concretely evaluate how well Carbon is meeting all of its language goals. In addition, as the proposal argues, concrete evidence that Carbon can succeed will indirectly help Carbon make faster progress, by motivating further investment in the project. Some aspects of the roadmap will also help accelerate Carbon's development in more direct ways. In particular, the executable semantics implementation should enable Carbon contributors to rapidly prototype potential new features, which will help us identify and solve more problems at an earlier stage, and have higher confidence in the proposals we adopt. While rapid progress is not an explicit goal of the Carbon project, Carbon can't meet any of its other goals until it exists in a usable form, and the longer it takes to reach that point, the less likely it is to reach it at all. ================================================ FILE: proposals/p0257.md ================================================ # Initialization of memory and variables [Pull request](https://github.com/carbon-language/carbon-lang/pull/257) ## Table of contents - [Problem](#problem) - [Background](#background) - [Proposal](#proposal) - [Unformed objects](#unformed-objects) - [Representation](#representation) - [Address-of and escape](#address-of-and-escape) - [Types with an unformed state](#types-with-an-unformed-state) - [Raw storage and padding](#raw-storage-and-padding) - [Necessary initialization](#necessary-initialization) - [Optional features for unformed states](#optional-features-for-unformed-states) - [Hardening initialization](#hardening-initialization) - [Queryable unformed state](#queryable-unformed-state) - [Function returns and initialization](#function-returns-and-initialization) - [Declared `returned` variable](#declared-returned-variable) - [Constructors and initializing aggregate objects](#constructors-and-initializing-aggregate-objects) - [No unformed members](#no-unformed-members) - [Rationale based on Carbon's goals](#rationale-based-on-carbons-goals) - [Alternatives considered](#alternatives-considered) - [Require compile-time-proven initialization](#require-compile-time-proven-initialization) - [Trivially required initialization, with no escape hatch](#trivially-required-initialization-with-no-escape-hatch) - [Initialize on all branches prior to use (Rust, C#, TypeScript)](#initialize-on-all-branches-prior-to-use-rust-c-typescript) - [Definitive initialization (Swift)](#definitive-initialization-swift) - [C and C++ uninitialized](#c-and-c-uninitialized) - [Allow passing unformed objects to parameters or returning them?](#allow-passing-unformed-objects-to-parameters-or-returning-them) - [Allow assigning an unformed object to another unformed object?](#allow-assigning-an-unformed-object-to-another-unformed-object) - [Fully destructive move (Rust)](#fully-destructive-move-rust) - [Completely non-destructive move (C++)](#completely-non-destructive-move-c) - [Named return variable in place of a return type](#named-return-variable-in-place-of-a-return-type) - [Allow unformed members](#allow-unformed-members) - [Summary of advantages and disadvantages](#summary-of-advantages-and-disadvantages) ## Problem How should Carbon handle uninitialized memory and variables? There are a number of goals we would like to satisfy in this space: - Minimize the overhead (in optimized builds) of unnecessary initialization. - Detect and diagnose bugs in code where variables are used without being initialized first at lower cost than a separate and unscalable build mode (MSan for C++). - Avoid the ergonomic, readability, and at times correctness issues when forced to initialize all variables at the point of declaration, even if there is no intentional value to yet use. - Minimize the security risk posed by accidental access of uninitialized memory. - Avoid unnecessary overhead for dynamic tracking of whether a variable is initialized. - Avoid unnecessarily running destructors for variables when they haven't yet been initialized or used. - Allow most types similar ergonomics and behaviors as a simple machine integer type. - Offer similar behavior regardless of storage location (stack, heap, thread-local-storage, static / global storage). - Leave a path to add more strict safety rules in the future, such as requiring explicit marking when the address of an uninitialized variable escapes. Part of our goal is also to support code patterns such as: ``` // At block scope of some function... var temporary: Int; while (...) { // ... code not using `temporary` ... temporary = ...; // ... code using `temporary` ... } ``` That is, we also don't want to force a _syntactic_ distinction when an assignment occurs to an already initialized variable. That would end up with fundamentally similar ergonomic (and potentially performance) overheads as forcing initialization as part of declaration. Throughout this document, the term "assignment" should instead be understood as the operation that can be used in this construct. These problems are also tightly related to the problems of _moved-from_ semantics. These semantics might range from completely non-destructive (as in C++) to completely destructive (as in Rust). A compelling solution for the variable initialization challenges can also provide a compelling approach for moved-from semantics that represents a middle ground between the two extremes seen in C++ and Rust. For example, we propose that the following should hold equally for moved-from objects and objects without an explicit initializer: - The object must not be used in any way other than assignment or destruction. - Note that this refers to the _object_. Taking the address is fine, but the restrictions here on how the object is used apply equally to any uses through a pointer to the object. - It must be possible to transition it to a valid, normal object through assignment. - It should be allowed to destroy them. - It should not be necessary to destroy them. ## Background - [Definitive assignment or definitive initialization](https://en.wikipedia.org/wiki/Definite_assignment_analysis) (used by Swift, Java, and so on) - [Rust checked uninitialized data](https://doc.rust-lang.org/nomicon/checked-uninit.html) - [Elements of Programming](http://elementsofprogramming.com/eop_bluelinks.pdf) - [P2025 - Guaranteed copy elision for named return objects](http://wg21.link/p2025) ## Proposal We propose two fundamental concepts: 1. An _unformed state_ for objects. 2. Raw, uninitialized storage. The first of these is a new concept and is discussed in detail below. However, uninitialized storage in Carbon should work in the same way as an uninitialized array of `unsigned char` or `std::byte` in C++. This is intended primarily to serve the needs of custom data structures such as C++'s `std::vector` or whatever Carbon ends up with as an `Optional(T)` type. With such raw storage, initialization and destruction of any objects within the storage must be managed manually and externally and the language won't provide any automatic mechanisms. The detailed syntax for working with raw storage in this manner is left to a subsequent proposal. ### Unformed objects An _unformed_ state is a state of an _object_. We both classify _objects_ based on whether they are in an unformed state or a fully formed state, as well as classify _types_ which support unformed states at all versus types that require objects to always be in a fully formed state. An unformed state for an object is one that satisfies the following properties: - Assignment from a fully formed value is correct using the normal assignment implementation for the type. - Destruction must be correct using the type's normal destruction implementation. - Destruction must be optional. The behavior of the program must be equivalent whether the destructor is run or not for an unformed object, including not leaking resources. Any operation on an unformed object _other_ than destruction or assignment to a fully formed value is an **error**, including: - Comparisons with other objects. - Passing an unformed object to a function or returning it. - Initializing or assigning _with_ an unformed object, including all forms of self assignment or self swap: ``` var unformed_x: Int; // Each line mentioning `unformed_x` below is an error: var a: Int = unformed_x; var b: Int = 42; b = unformed_x; var unformed_c: Int; unformed_c = unformed_x; unformed_x = unformed_x; // If we imagine a move operation similar to C++'s `std::move` spelled `~` // and a function `Swap` similar to C++'s `std::swap` but using pointers, // these too are errors: unformed_x = ~unformed_x; Swap(&unformed_x, &unformed_x); ``` These errors should be diagnosed as early as possible in a debug build and at least at runtime. In a performance build, it may lead to undefined behavior, but in a hardened build we have a strong mitigation that restores safety by defining the behavior using a maximally safe representation. When a variable is declared without initialization, such as: ``` var x: Int; ``` The variable is implicitly initialized to its type's unformed state and represents an unformed object. It is invalid to declare a variable without explicit initialization unless its type implements the interface to specify how to do that initialization. For types that do not support initializing the variable to an unformed state, the programmer must either explicitly initialize it to a valid and fully formed state for the type, or must change the type to something like an `Optional(T)` that introduces an unformed state (and the dynamic tracking necessary to implement it) alongside raw storage for the underlying object. #### Representation The unformed state can have the same representation as valid and fully formed states for the object (for example, an empty `Optional(T)`). While there is a semantic difference (any operation _other_ than assignment and destruction is an error for an unformed object), there is no problem reusing a representation which is also used for fully formed and valid objects provided it satisfies all three of the above constraints. The semantic restrictions remain even in this case. Using the hypothetical `Optional(T)` API, we could imagine: ``` var formed: Optional(T) = .Empty; // This is of course fine. if (formed.empty()) { ... } var unformed: Optional(T); // This would be an error even though `unformed` might have identical // in-memory representation or bit-pattern to `formed`: if (unformed.empty()) { ... } ``` #### Address-of and escape It is allowed to take the address of an unformed object and escape it to another function. This function may even be responsible for assigning a fully formed object to that location. ``` fn AssignIntTo(x: Int, destination: Int*) { *destination = x; } fn Example() { var y: Int; // `y` is unformed here. AssignIntTo(42, &y); // `y` is fully formed and usable. } ``` This in turn is part of why it is important that the destructor is _allowed_ to be run on an unformed object. This gives us a conservatively correct way to implement code where it may be impossible to know whether the object has been initialized to a fully formed object, and similarly code where it is impossible to know whether the object has been moved-from. Consider some `HashMap` container with allocated memory where the destructor _must_ be run to release the memory: ``` fn MaybeInitializes(map: HashMap*); fn MaybeMovesFrom(map: HashMap*); fn Example() { var map: HashMap; MaybeInitializes(&map); if (...) { // Don't know if we need to destroy `map`. But conservatively we can run the // destructor unconditionally. return; } // Unconditionally assign a particular value to our map. map = ...; MaybeMovesFrom(&map); // `map` could be unformed (due to being moved from) or still fully formed. // Run the destructor unconditionally to be safe. return; } ``` #### Types with an unformed state The intent is that most types have an unformed state, as it will let types "be like the `int`s" -- we design it to closely match how machine integers actually behave in modern optimizing compilers: when left "uninitialized" they can be safely _assigned_ or _destroyed_. But running the destructor is _optional_. Types can opt into this semantic model and define an explicit operation to create a type in the unformed state by implementing an interface. Because this will be defined semantically by the existence of an implementation of the relevant interface, and operationally by providing code to produce an unformed object, there is no specific bit pattern or representation encoded for the unformed state. The interface should provide a default implementation for types where all members have an implementation which recurses member-wise. It is reasonable to wonder if types where the default implementation is valid implicitly implement the interface, or should it require an explicit opt-in? Initially we suggest this should be explicit. There may grow to be a set of interfaces which should be implicitly implemented due to their prevalence, but it seems better to be conservative at first and approach that systematically. Note that this state is similar but more restrictive than the _partially formed state_ from Elements of Programming (EoP) in section 1.5. #### Raw storage and padding Raw storage and padding bytes are treated like they always consist of unformed bytes. We propose not allowing sub-byte raw storage or padding. Padding bytes have specific practical considerations. It is fine for operations that work across all bytes of raw storage, such as C-style routines like `memset` and `memcpy`, to write to padding bytes. But doing so has no observable effect, and even if they notionally set padding bytes to a known value, the padding bytes continue to behave as if they are unformed bytes. Copying _from_ padding bytes is significantly more difficult because it risks opening up the possibility of copying unformed objects more generally. That has significant downsides and we are proposing it is [not allowed](#allow-assigning-an-unformed-object-to-another-unformed-object). Instead, there will need to be a low-level language facility that specifically handles copying storage with padding bytes. Such a facility would have to either exclude the padding bytes from the copy, or provide a strong guarantee that they are only ever _directly_ copied into other padding bytes without any intermediate steps. ### Necessary initialization Some types will not provide an unformed state. Variables of such types must be initialized when declared: ``` struct NecessaryInit { var m: Mutex; // Potentially other members without a meaningful unformed state. } // This line would be an error: var error_no_init: NecessaryInit; ``` For these types, the programmer must either explicitly initialize it to a valid and fully formed state for the type, or must change the type to something like an `Optional(T)` that introduces an unformed state (and the dynamic tracking necessary to implement it) alongside raw storage for the underlying object: ``` // This is fine, it contains the boolean necessary to track initialization. var ok: Optional(NecessaryInit); ``` ### Optional features for unformed states There are two _optional_ aspects of unformed states that types may elect to explicitly specify (again by implementing interfaces, here by specializing the implementation rather than relying on the default fallback). #### Hardening initialization First, types may customize how to further initialize an object that is in the unformed state to most effectively harden it against erroneous usage. For example, the unformed state of an `Int` is trivial -- it can simply be left uninitialized. However, it can be hardened against bugs by setting it to zero. User defined types have the option of specifying a specific routine to do similar hardening initialization. The entire storage, including any padding bytes, for all types will be at least be zeroed as part of hardening initialization. This routine simply allows a user defined type to specifically customize the hardening initialization of some parts or members of the object to values other than zero. #### Queryable unformed state The second optional aspect is implementing a test for whether an object is unformed. This test cannot be run by user code -- doing so would be a bug due to referencing an unformed object. However, for types where the unformed state is intrinsically distinct from any other state, they can expose the ability to query this. That allows debug builds of Carbon to verify objects are fully formed prior to use without external tracking state. Types which do not implement this functionality will still get partial checking in debug builds through external state, but may lose dynamic enforcement across ABI boundaries where this extra state cannot be propagated. These are also the boundaries across which the hardening will be done to ensure bugs across those boundaries cannot become security vulnerabilities in the hardened build configuration. ### Function returns and initialization This proposal also has implications for returning values from functions, especially as it relates to how Carbon can provide similar functionality to guaranteed return value optimization (RVO) and named return value optimization (NRVO) in more principled ways than C++ does. While these facilities should allow Carbon to not require tricks like NRVO and the dangers that come with them, the primary motivation is to increase the expressivity of "normal" function returns in order to address complex use cases in object [construction](#constructors-and-initializing-aggregate-objects). Consider the following common initialization code: ``` fn CreateMyObject() -> MyType { return ; } var x: MyType = CreateMyObject(); ``` We propose that the `` in the `return` statement of `CreateMyObject` _initializes_ the variable `x` here. There is no copy, etc. It is precisely the same as: ``` var x: MyType = ; ``` We also propose this applies _recursively_. This matches how C++ has evolved with "guaranteed copy elision". However, while most C++ implementations provide a "named return value" optimization that applies the performance benefits of this to returning a named variable, it has proven extremely difficult to _automatically_ provide "guaranteed copy elision" when returning a named variable. The determining factors for whether it can be safely applied change significantly based on optimization levels and numerous other implementation details. The result has been repeated requests from users and proposals to provide an explicit syntax for marking a named variable as being returned and then both _guaranteeing_ the copy elision takes place, and requiring that all returns are of the variable and compatible with that elision. While these proposals have hit issues in C++ due to compatibility concerns and complexity in C++ itself, those won't apply to Carbon. We suggest that Carbon should provide a direct mechanism for this with a named return variable which obeys the same initialization rules as any other variable. An important aspect of any approach to named return variables is that, much like with normal return values, there is no copy when initializing. This means that the address of `result` in the examples below of `CreateMyObject2` and `CreateMyObject3` are the same as the address of `y` during the evaluation of `y`'s initialization expression. Only one variable is created. It is first initialized to an unformed state on entry to `CreateMyObject2` and assigned to a fully formed value internally. #### Declared `returned` variable We propose to allow a specific variable within the function body to be declared as `returned` from the function. Assuming that `MyType` has an unformed state, the alternative to the above syntax would be: ``` fn CreateMyObject2() -> MyType { returned var result: MyType; // Any code we want here. result = CreateMyObject(); // Must explicitly use the variable return syntax rather than returning an expression. return var; } ``` And we can initialize the return variable to remove the unformed requirement: ``` fn CreateMyObject3() -> MyType { // Any code we want here. returned var result: MyType = CreateMyObject(); // Potentially more code... return var; } ``` **Minor syntax variations (not proposed):** - Just use `return;` rather than `return var;` for the actual control flow return. - The ceremony here is intended to distinguish from the case of a function that doesn't have a return value at all, but it could be removed. - Require `return ;` where `` is constrained to match the declared variable name. - One concern with this is that the `` here is not an expression -- it isn't evaluated or anything and must be just a name. Worse, there might not _be_ a single name. - However, if there were good syntax, it might help with scoping. - Maybe better syntax than `returned var` and `return var`, open to follow-up proposals to tweak the exact syntax of both sides of this. We expect subsequent proposals to generalize this facility to non-`var` declarations where there is a reasonable and clear semantic. However, it should be noted that for `var`-style declarations in particular where the address of the object can be taken, the entire declared object must be marked as `returned` -- it cannot be a component of the pattern alone. We also propose a set of rules to use for the types in the pattern, and to what degree they must match the return type: whatever type would be the _allocated_ type of the variable declaration must match the return type. For declarations that produce storage such as `var`, the entire allocated object based on the pattern must match the return type. If this is generalized to non-allocating patterns, it would be fine to use any pattern that can match the return type. The initializer expression must initialize the declared return type, and then any pattern match is done. Control flow presents some challenging questions for this design. Consider code with the following structure: ``` fn ReturnEitherOr(...) -> MyType { // ... if (...) { returned var result1: MyType = ...; // ... return var; } else if (...) { returned var result2: MyType = ...; // ... } else { returned var result3: MyType = ...; // ... } // ... return var; } ``` It seems like at least `result1` should be valid. What about `result2` and `result3`? More complex control flow patterns grow the complexity this presents. For example: ``` fn ReturnVarWithControlFlow() -> Point { if ... { // Is this allowed? returned var p1: Point = ...; if ... { // This seems fine to return `p1`. return var; } } if ... { // But this would have to be an error... return var; } // And what happens here? returned var p2: Point = ...; for ... { // Or here? returned var p3: Point = ...; // Even though we might actually return... if ... { return var; } } // Definitely no way to know what happens here between p1, p2, and p3. return var; } ``` We propose a set of restrictions to give simple and understandable behavior which remains reasonably expressive. 1. Once a `returned var` is in scope, another `returned var` cannot be declared. 2. Any `return` with a `returned var` in scope must be `return var;` and returns the declared `returned var`. 3. If control flow exits the scope of a `returned var` in any way other than a `return var;`, it ends the lifetime of the declared `returned var` exactly like it would end the lifetime of a `var` declaration. 4. There must be a `returned var` declaration in scope when the function does a `return var;`. A consequence of these rules allows code like: ``` fn MaybeReturnedVar() -> Point { { returned var p: Point = ...; if ... { return var; } } return MakeMeAPoint(); } ``` ### Constructors and initializing aggregate objects Constructors of user defined aggregate types can present specific problems with initialization because they intrinsically operate on types prior to all of their invariants being established and when a valid value may not (yet) exist for subobjects such as members. These problems are discussed in detail in the blog post ["Perils of Constructors"](https://matklad.github.io/2019/07/16/perils-of-constructors.html). A primary goal in trying to solve these problems is being able to separate the phase of initializing members (or other kinds of subobjects) prior to the outer object existing and establishing any necessary invariants without creating holes in the type system soundness. An interconnected problem is correctly running destructors (in order to release any acquired resources) in the event of errors _during_ construction. The behavior of function returns provides interesting build blocks to write code that addresses these problems. They provide a natural set of tools to build two phases of a constructor: code that executes before a valid object exists and code that executes afterward. It even gives a clear demarcation between these phases. Adding `returned` variable declarations provide an important building block for solving these problems. ``` struct Point { var x: Int; var y: Int; } fn PointConstructorPreObjectPhase() -> Point { // Code that runs prior to an object existing. // Just assembles inputs needed to initialize the members. return (, ); } fn ComplexTwoPhasePointConstructor() -> Point { // Code that runs prior to an object existing. var x_init: Int = ...; var y_init: Int = ...; returned var p: Point = (x_init, y_init); // `p` is now fully formed, and its destructor *must* run. // More code that uses p can go here. return var; } ``` #### No unformed members This proposal suggests disallowing unformed members -- objects must be completely formed or completely unformed. Allowing mixing of formed and unformed members in an object introduces significant complexity into the language without advantages, and the code patterns that result scale poorly to real world examples. For details on what it would look like to allow these and how this goes wrong, see the [alternative section](#allow-unformed-members) Note that inheritance opens a set of closely related but also importantly different concerns as members here and this proposal doesn't take any position one way or another there. If and when there is a proposal for how inheritance should work in Carbon, it should address this question. ## Rationale based on Carbon's goals This model is designed to give significant control to users of Carbon for maximizing performance around variable initialization, which matches the primary goal of Carbon for performance-critical software. It then tries to provide as many ergonomic and safety tools as possible to support the goals of easy to read and understand code and providing practical safety. Leaving space for future evolution toward stricter checking of potentially unsafe code aligns with the goal for language evolution. ## Alternatives considered ### Require compile-time-proven initialization A primary alternative to the approach of unformed objects for initialization is to simply require initializing all variables at compile time. This can take a few forms across the spectrum of compile time analysis complexity. #### Trivially required initialization, with no escape hatch The simplest approach is to require initialization at the point of declaration or allocation for any variable or memory. An essential property to this approach remaining simple is that it have _no_ escape hatch. Once such an escape hatch exists, all of the semantic questions of this proposal need to be answered. Advantages: - No need to specify or support the complexity of objects that have been declared but not fully initialized in the language. - Simple, easy to teach, easy to implement. Disadvantages: - Will result in initialization with nonsensical values that if used remain a bug. We won't have any realistic way to detect these bugs. - Imposes a non-trivial performance cost in some cases and for some users without providing any effective mechanisms to address this overhead. #### Initialize on all branches prior to use (Rust, C#, TypeScript) Use a simple rule such as requiring initialization before use along all branches. Advantages: - Still avoids much of the language level complexity of unformed objects. - Very simple to implement. - Reasonably teachable. - Avoids trivially redundant initializations. Disadvantages: - Has significant expressivity limits past which initialization is forced. - When this happens, the disadvantages of always requiring initialization repeat. While it is expected to happen less often, it still happens. - Adding an unsafe escape hatch that avoids initialization even in this case returns to the same fundamental complexity and model as is proposed here. - Would still need a semantic model for moved from objects which would introduce some amount of additional complexity. #### Definitive initialization (Swift) We could do significantly more complete analysis of the dataflow of the program to prove that initialization occurs prior to use. Advantages: - Still avoids much of the language level complexity of unformed objects. Disadvantages: - Some implementation complexity to achieve strong dataflow analysis. - Significant complexity to teach -- it can be difficult to understand or explain why the data flow analysis fails in edge cases. - Can result in brittleness where changes to one part of code confuse the dataflow analysis needed by another part of code. - There still exist rare cases where the analysis fails, and a nonsensical initialization may be required and the disadvantages of those return (both bug detection and performance). - The rate at which this occurs scale inversely proportional to the complexity of the data flow analysis and the teachability. - Would still need a semantic model for moved from objects which would introduce some amount of additional complexity. ### C and C++ uninitialized We could pursue the same approach as C and C++ here. However, this path has proven to have severe problems. It has inevitably resulted in prevalent bugs and security vulnerabilities due to programmer errors, And having uninitialized variables in the language makes teaching the debug build to catch these bugs extremely difficult and potentially impossible. We essentially will need developers to heavily use an MSan-like mode (either by integrating it with the debug build or having a separate one) in order to cope with the bugs caused by uninitialized memory. This is the only thing that has allowed coping with C and C++ semantics here and even it is not very effective. If MSan remains a separate tool that needs propagation and origin tracking ([details](https://research.google.com/pubs/archive/43308.pdf)), the cost and the barrier to entry will remain very high: MSan is currently the least used and the least portable of the sanitizers; it is widely used only in two Google’s ecosystems, only on one OS, where it is maintained with a considerable effort. If we end up having uninitialized memory, we should aim at having MSan-like checking be part of the normal debug build, which means no or very limited propagation. Any form of propagation will require optional original tracking, which is doable, but will mean extra CPU/RAM overhead for meaningful diagnostics. Even without propagation, MSan-like checking requires bit-to-bit shadow that would incur a 2x RAM overhead in the debug build. Byte-to-bit shadow, which is < 12% RAM overhead, is prone to racy false positives and false negatives unless a) instrumentation uses expensive bit-wise atomics or b) the language defines data races on 8-byte granularity. Adding this restriction on data races would diverge from C++ and further complicate porting code and interoperability. ### Allow passing unformed objects to parameters or returning them? This proposal suggests **not** allowing code to pass unformed objects to other functions as arguments or to return them. They can still be escaped to another function by passing the address. Advantages of allowing this: - Would accept more code that can be written in C++ today and doesn't directly exhibit a bug. For example, unused arguments or return values in C++ could be left uninitialized, but in Carbon they would need to use an optional type instead. Disadvantages of allowing this: - Makes checking for errors in debug builds both more expensive and less helpful by potentially moving the error to a separate function rather than the one most likely containing any bug. - We have anecdotal evidence that indicates producing an error at the function boundary would be preferable to delaying the error. This evidence is a result of MSan delaying any such errors, while UBSan eagerly errors at the function boundary for a few types (`bool`s and `enum`s). Particularly for `bool` parameters the early errors from UBSan have essentially always been correctly diagnosed and bugs. The early errors from UBSan are also significantly easier to understand. - Implementing interprocedural checking either requires a more expensive ABI or using more expensive shadow-memory checks which would otherwise be unnecessary. - Equivalent functionality can be achieved safely using an optional type, or unsafely by passing the address. The only cases where passing the address would be less efficient than what C++ allows are likely to be amenable to compiler optimization to remove the overhead. ### Allow assigning an unformed object to another unformed object? This proposal suggests **not** allowing assigning an unformed object to another unformed object. - We should try _not_ allowing this at first. - This will be enforced the same way as any other use of an unformed object: at compile time when locally detectable, at runtime when sufficient runtime tracking is enabled. - If we find we must allow this in some cases, we should start by allowing it only using specially named operations. - If either can be made to work, they are significantly easier to check for bugs. Advantages of allowing this: - Avoids some surprise for users. - It seems like it should work for integers. - They may not know when the incoming object is actually unformed resulting in an error far away from the root cause. - Only some surprise is avoided given that we still can't assign unformed objects to fully-formed objects -- doing so would open up the possibility of unformed subobjects. - Makes some idiomatic patterns in C++ either invalid or inefficient. Consider the typical C++ implementation of a `swap` function: ```c++ template void swap(T& lhs, T& rhs) { T tmp = std::move(lhs); lhs = std::move(rhs); rhs = std::move(tmp); } ``` If this function is called with `lhs` and `rhs` referring _to the same object_, then the first move into a temporary leaves the object in an unformed state, and the second assignment will assign that unformed object. Whatever Carbon code ends up replicating patterns like this will require one of two options: 1. Check for `lhs` and `rhs` referring to the same object, adding a branch and its overhead to the code. 2. Require that it is never called with `lhs` and `rhs` referring to the same object, pushing the cost onto the caller. We would largely expect (2) to be the dominant choice in Carbon due to performance concerns. However, if the ergonomic burden of (2) becomes significant, we may revisit this rule to carve out enough of a facility for self swap specifically. That might either be the narrow specially-named operation mentioned above, or attempt to special case self-assignment in some way. - We could consider narrowly allowing _only_ self-move-assignment or self-swap of unformed objects to lessen the impact of precluding the move-assignment and swap of unformed objects generally. However, that carries surprising complexity cost for the language specification and creates very subtle rules. At a minimum, types with an unformed state would need to implement move assignment in a way that would be correct even when performing a self-move of an unformed object. On top of that, the simplest solution would be to allow _any_ self move assignment of an unformed object. However, that would require any move assignment that _might_ be a self-move to be implemented in a checked or sanitized build with a branch to detect that case and not error. To lower this overhead of checked builds, we would need to add language-level swap operation of some form and only exempt _that_ from checking. This would reduce the overhead, but would now make it impossible to even test the self move assignment that would still be required to work in non-checked builds. - May be important to enable specific code patterns if we allow member access. Disadvantages of allowing this: - Will require the core propagation logic and origin tracking that is a source of large complexity and cost in MSan to detect bugs. - However, object granularity and lack of arithmetic makes this still somewhat less severe than what MSan deals with. - Only a concern for detection, hardening is easily accomplished regardless. - Can only possibly support combined debug build mode if restricted to a special/designated operation rather than arbitrary assignment. - Origin tracking will still be required and have significant cost. ### Fully destructive move (Rust) Rust has truly destructive moves -- the object is gone after being moved from, and no destructor ever runs past that point. This is possible because the type system models when a variable declared in one function can be moved-from by another function. It is incompatible with allowing move-from through a pointer to a non-local object. Advantages: - Precise, safe, and efficient lifetime management. - Perfect detection of use-after-move bugs. Disadvantages: - Doesn't compose with pointers and C++ idioms which involve moving out of non-local objects through a pointer. - Would require redesigning systems to model the lifetime in the type system. ### Completely non-destructive move (C++) C++ moves are never destructive. The result is that moves simply do not impact the _lifetime_ of the moved-from object, only its state. Advantages: - Perfect match for C++ semantics and idioms, including interoperability and migrated code and systems. Disadvantages: - Pervasive user expectation that destroying a moved-from object is a no-op, but nothing in the language helps enforce this pattern. - Allows types to provide meaningful semantics for use-after-move. - Rarely used, resulting in strong user expectations that this isn't allowed but special cases where it is allowed. - Complicates generic code having to handle both patterns. - Significantly reduces the effectiveness of use-after-move bug detection. - Introduces difficulty in optimizing away no-op destruction post move. - Some cases requires complex rules around ABIs and calling conventions. - Other cases require expensive optimization techniques (inlining and memory analysis) that can fail in some circumstances. ### Named return variable in place of a return type An alternative approach to [declaring `returned` variables](#declared-returned-variable) is to extend the syntax for the return type such that it can declare a named variable that holds the return value. For example, provided that `MyType` has an unformed state: ``` fn CreateMyObject2() -> var result: MyType { // `result` here is an unformed object. // so we can assign to it here: result = CreateMyObject(); // Because there is a named return variable, we cannot // have an expression in the return statement. return; } ``` We could also explicitly initialize the return variable, which eliminates the requirement that its type has an unformed state: ``` fn CreateMyObject3() -> var result: MyType = CreateMyObject2() { // any code we want return; } var y: MyType = CreateMyObject3(); ``` **Minor syntax variations:** - Possible to use different arrow symbols (such as `=>` instead of `->`) to differentiate different kinds of returns. - However, we don't recommend this given the subtlety of the visual difference. - Potentially require `return ;` where `` must exactly match the return variable's name to make it clear what is being returned. - One concern with this is that `` can't really be an expression in general, it needs to _just_ be a name. This may be a bit surprising. - Also complicated as there is no need to have a single name for the variable: it might be `var (result: MyType, Error: error)` for example, and it becomes unclear what to put in the `return`. **Advantages:** - The primary advantage of this approach is that it avoids any and all challenging lifetime questions or restrictions on where the `returned` variable can be declared or what control flow must follow. - It is also slightly less expressive which if sufficient seems desirable. **Disadvantages:** - The syntax space of the return type is very crowded already, and this would add more complexity there rather than fitting in cleanly with existing declaration spaces within the body of the function. - Likely to be an implementation detail and valid to use on a function with a normal return _type_ in its forward declaration. This isn't obvious when placed in the declaration position, and it might well be unnecessarily added to forward declarations just because of its position. - Removes the ability to _both_ specify a specific return type and a pattern for binding parts of that object to names. Instead, the type must be inferred from the pattern. - This ended up feeling like a deep and fundamental problem where we would lose important expressivity to disambiguate the return type from patterns. For example, when the return type is some form of sum type or variant, the pattern space is both especially attractive to use and fundamentally fails to express enough information to capture the type. ### Allow unformed members Regardless of which specific strategy, using the proposed `return` facilities for constructing aggregate objects still presents some ergonomic difficulties for large objects with many members, or members that are expensive/impossible to move if we don't have the `returned var` facility. To further ease initializing members, we could allow access to unformed members of an unformed object. Superficially, this remains safe -- we aren't allowing access to uninitialized storage, and the only access allowed is what is always allowed for unformed objects: assignment (and destruction). However, making this work with destructors adds some complexity to rules around unformed objects. Disallowing access to unformed members provides a simple rule for the semantics required of the destructor: it must be valid and correct to run on an unformed object or a _fully_ formed object. Destructors always have to be valid for fully formed objects, so the only addition is the unformed state being a no-op. However, for the same reasons it might be necessary to run the destructor on an unformed object, if we allow assigning to a _member_ of an unformed object the destructor may also need to be run with this mixed state where some members are unformed and others are fully formed: ``` struct Point { var x: Int; var y: Int; } fn AssignToX(x: Int, p: Point*) { p->x = x; } fn Example() { var p: Point; if (...) { // Allowed to call the destructor of `p`, sees a fully unformed `Point`. return; } AssignToX(42, &p); if (...) { // If `AssignToX` is in another file, don't know if `p` is fully unformed, // a mixture of `x` fully formed and `y` unformed, or `p` is fully formed. // The last case can only be handled by running the destructor of `p` // so that's what we have to do here, and it must handle all the other // cases as well. return; } p->y = 13; // ... } ``` The added requirement for destructors of types that support an unformed state is that if they are called for an unformed object of that type, they must run the destructors for any members that might themselves be fully formed. - For private members, the type itself controls which members might be assigned fully formed values and the destructor run. The type's destructor merely needs to agree with the constructors and/or factories of the type to destroy any members that might have been assigned values. - For public members, the type's destructor must destroy _all_ members as it cannot know which ones might have been assigned a value. Because of these inherent restrictions around private members, types can ensure any necessary relationship between its private data members hold in order for the destructor to work. See the detailed example below for how this might work in practice. Despite these added requirements to the destructor, it isn't exempt from the rules governing unformed objects when accessing its members: any that are in fact unformed when the type's destructor runs are only allowed to be assigned to or destroyed. Lastly, we need some syntax other than assignment for marking when the members have reached a state where the entire object is fully formed. This document uses a hypothetical `?reify?` statement that is intended to be placeholder syntax only. Just like an assignment to an unformed object, after this point it is fully formed and follows those rules. Our example becomes: ``` fn EvenSimplerPointConstructor() -> var p: Point { // p is unformed and no members are referenced. // Destruction is completely optional. p.x = ...; // p.x may now be fully formed. Either p must be destroyed, // or p.x must be destroyed. p.y = ...; // Now either p must be destroyed, or both p.x and p.y must be // destroyed. ?reify? p; // Now p must be destroyed, and is a fully formed object. // ... return; } ``` Before the `?reify?` operation, the compiler can either track the members which are assigned fully formed values and run their destructors, or it can elect to run the entire object's destructor if, for example, the entire object escapes and it becomes impossible to track which members have been assigned fully formed values. But once the object has the `?reify?` operation performed, its destructor _must_ be run as it is now itself a fully formed object. Now that we have all the components of this tool, let's consider a container like `std::vector` using a hypothetical syntax for defining a type, its implementation of the necessary initialization, its destructor, and a factory function. This example specifically tries to illustrate how, for private members, the function doing construction and the destructor can work together to ensure at any point of destruction, the necessary state and invariants hold. ``` struct MyContainer(T: Type) { private var size: Int; private var capacity: Int; private var data: Optional(T*); // ... public API ... impl NecessaryInit { // Not necessarily suggesting this specific API. // Just *some* API we pick for doing the necessary init. // This API assumes that all members with an unformed state // have their implementation called already, all we need to do is // adjust to *specific* values anything that is necessary for this // type. fn Init(this: Self*) { this->data = Optional(T*).Empty; } } fn Create(count: Int, T: value) -> Optional(MyContainer(T)) { var container: MyContainer(T); // Set the capacity first so it is available for deallocation. container.capacity = count; // And clear the size so we don't need a special case. container.size = 0; // Next set the data to the allocated buffer. If the allocation fails, // this routine should just return null. container.data = AllocateRawStorage(count, T); if (container.data == null) { // Might destroy `container` here. return .Empty; } // Some loop syntax over [0, count). for (i: Int = 0 ..< count) { if (not CopyThatMightFail(value, &container.data[i])) { // Might destroy `container` here. return .Empty; } // Increment size as we go to track how many objects // need to be destroyed if the next one fails. ++container.size; } ?reify? container; // Maybe we make this implicit... somehow make it move... return .Some(container); } // However we end up naming this... fn Destructor(this: Self*) { if (this->data.IsEmpty()) { // If the buffer isn't allocated, nothing to do. return; } // Destroy any successfully created object. The `Create` // function ensures that `size` is valid for use here. for (i: Int = 0 ..< this->size) { // However we spell in-place destroy... this->data[i].Destructor(); } // Deallocate the buffer. Here as well the `Create` // function ensures that `capacity` is available for a // non-null data pointer. Deallocate(this->data, this->capacity); } } ``` This kind of type creates a close contract between construction and destruction to simplify both routines by ensuring that the necessary members are valid any time the destructor would need them to continue executing correctly. While this provides a very flexible and convenient set of tools for construction, it comes at a fairly high cost. There is no way for the language to _enforce_ any contract between the construction and destruction. I don't see any way to provide compile time checking that one or the other doesn't make a mistake. We can verify it dynamically, but at fairly high cost. It also adds non-trivial complexity. Given that this is merely an ergonomic improvement as opposed to an expressivity improvement, it isn't at all clear that we should pursue this until we have clear evidence that the ergonomic need is sufficiently large. In fact, for this kind of realistic example, the `returned` variable approach appears to be significantly _more_ ergonomic as well as simpler, undermining the purpose of pursing this direction: ``` struct MyContainer(T: Type) { private var size: Int; private var capacity: Int; private var data: Optional(T*); // ... public API ... impl NecessaryInit { // Not necessarily suggesting this specific API. // Just *some* API we pick for doing the necessary init. // This API assumes that all members with an unformed state // have their implementation called already, all we need to do is // adjust to *specific* values anything that is necessary for this // type. fn Init(this: Self*) { this->data = Optional(T*).Empty; } } // Private helper factored out of the destructor. fn DestroyElements(data: T*, count: Int) { for (i: Int = 0 ..< count) { data[i].Destructor(); } } fn Create(count: Int, T: value) -> Optional(MyContainer(T)) { // Try to allocate the storage. var data: UniquePtr(T) = AllocateRawStorage(count, T); if (data == null) { return .Empty; } // Some loop syntax over [0, count). for (i: Int = 0 ..< count) { if (not CopyThatMightFail(value, &container.data[i])) { // Here we have to destroy the copies that succeeded. // But we can easily share the logic with the destructor. DestroyElements(data, i); return .Empty; } } // Now we can just initialize the object and all invariants // hold for its destructor. returned var Optional(MyContainer(T)): result = .Some((count, count, data.Release())); return result; } // However we end up naming this... fn Destructor(this: Self*) { if (this->data.IsEmpty()) { // If the buffer isn't allocated, nothing to do. return; } // The above check is the only thing needed, and it is only needed // to support an unformed state. Once we get here, everything holds. DestroyElements(this->data, this->size); Deallocate(this->data, this->capacity); } } ``` With this approach, the necessary steps in the `Create` function are all _local_ constraints, and changes to the destructor can't break their assumptions. There is no significant added complexity in practice due to needing to form the entire object at a single point. Normal techniques for factoring common logic are easily used to address what little added logic is needed. #### Summary of advantages and disadvantages Advantages: - Allows incrementally assigning meaningful members to an object. - Simplifies some initialization patterns for objects with large numbers of members. Disadvantages: - The simplifications don't scale up to more realistic scenarios. There, they are eclipsed by the added complexity forced onto the destructor of any such type. - The design patterns that simplify the organization of the construction and destruction logic for these kinds of types are likely to remove the need for the feature, so it seems better to keep the language simpler. ================================================ FILE: proposals/p0285.md ================================================ # if/else [Pull request](https://github.com/carbon-language/carbon-lang/pull/285) ## Table of contents - [Problem](#problem) - [Background](#background) - [Proposal](#proposal) - [Details](#details) - [Executable semantics form](#executable-semantics-form) - [Caveats](#caveats) - [C++ as baseline](#c-as-baseline) - [if/else in expressions](#ifelse-in-expressions) - [Indentation](#indentation) - [Ambiguous else](#ambiguous-else) - [Alternatives considered](#alternatives-considered) - [No parentheses](#no-parentheses) - [Require braces](#require-braces) - [Rationale](#rationale) ## Problem `if`/`else` is noted in the [language overview](/docs/design/README.md), but is provisional. Control flow is important, and `if`/`else` is basic; the form is similar in many languages, even if details may change. ## Background `if`/`else` is a common [conditional](), seen in [many languages](). A few syntaxes that are likely to influence `if`/`else` are: - C++ ```c++ if (x) { printf("x is true"); } else if (y) { printf("y is true"); } else { printf("Neither was true"); } ``` - Python ```python if x: print("x is true"); elif y: print("y is true"); else: print("Neither was true"); ``` - Swift ```swift if x { print("x is true") } else if y { print("y is true") } else { print("Neither was true") } ``` - Rust -- versus other cases where `if` is a statement, Rust makes `if` an expression, allowing: ```rust let x = if y { 1 } else { 0 }; ``` ## Proposal We should make `if`/`else` syntax consistent with C and C++, rather than adopting the syntax of another language. ## Details `if`/`else` is a statement. The syntax looks like: `if` `(`_boolean expression_`)` `{` _statements evaluated when true_ `}` [ `else` `{` _statements evaluated when false_ `}` ] The braces are optional, but must be paired (`{ ... }`) if present. When there are no braces, only one statement is allowed. ### Executable semantics form ```bison statement: "if" '(' expression ')' statement optional_else | /* preexisting statements elided */ ; optional_else: /* empty */ | "else" statement ; ``` ## Caveats ### C++ as baseline This baseline syntax is based on C++, following the migration sub-goal [Familiarity for experienced C++ developers with a gentle learning curve](/docs/project/goals.md#interoperability-with-and-migration-from-existing-c-code). To the extent that this proposal anchors on a particular approach, it aims to anchor on C++'s existing syntax, consistent with that sub-goal. Alternatives will generally reflect breaking consistency with C++ syntax. While most proposals may consider alternatives more, this proposal suggests a threshold of only accepting alternatives that skew from C++ syntax if they are clearly better; the priority in this proposal is to _avoid debate_ and produce a trivial proposal. Where an alternative would trigger debate, it should be examined by an advocate in a separate proposal. ### if/else in expressions This proposal covers `if`/`else` as a statement. A Rust-like form of `if`/`else` as an expression could be supported, but is not part of this proposal because it's more complex. ### Indentation It may be desirable to require meaningful indentation of the body of an `if`/`else`, in particular to help catch errors when there are no braces. For example, this could be a compiler error due to inconsistent indentation of the `do_parse` assignment: ```carbon if (missing_data) Print("Missing data!"); do_parse = false; if (do_parse) ParseData(); ``` This is _not_ part of this proposal. ### Ambiguous else It may be desirable to reject cases where an `else` is ambiguous. For example, this could be a compiler error due to the ambiguous `else`: ```carbon if (a) if (b) f(); else g(); ``` This is _not_ part of this proposal. This proposal takes C++ syntax as a baseline, so an `else` binds to the innermost enclosing `if` that doesn't already have an `else`. This desire might also be addressed by choosing to require consistent indentation and disallowing multiple `if`s on the same line. ## Alternatives considered See [C++ as baseline](#c-as-baseline) for an explanation of how alternatives are evaluated in this proposal. ### No parentheses Parentheses could be optional (essentially not part of `if`/`else`, but addable as part of the expression), instead of required (as proposed). Advantages: - Removing parentheses gives developers less to type. - Consistent with several other languages, including Swift and Rust. Disadvantages: - Requiring parentheses is consistent with C++, and will be intuitive for C++ developers, from both a writability and readability perspective. - Parentheses help avoid ambiguities. - The Swift and Rust model is ambiguous if it's ever possible for an expression to optionally be followed by braces. That is possible in Rust, at least, where `Type{...}` is a valid expression. As a result, Rust rejects `if Type{.value = true}.value { thing1 } else { thing2 }` because it misinterprets the braces. - Parentheses allow making the braces optional without any risk of ambiguity. - Parentheses allow introducing syntactic variants in the future without ambiguity. - For example, C++'s `if constexpr (...)` wouldn't have been possible if the parentheses were optional. The benefits of this are debatable, and it should be examined by an advocate in a focused proposal. For now, we should match C++'s decision. ### Require braces Braces could be required, instead of optional (as proposed). Advantages: - Braces avoid syntax ambiguities. - For example, `if (x) if (y) { ... } else { ... }` has difficult-to-understand binding of `else`. - Braces avoid errors when adding statements. - For example, if: ```carbon if (x) do_parse = false; ``` has a statement added: ```carbon if (missing_data) Print("Missing data!"); do_parse = false; ``` Disadvantages: - Inconsistent with C++. The benefits of this are debatable, and it should be examined by an advocate in a focused proposal. For now, we should match C++'s decision. ## Rationale This proposal focuses on an uncontroversial piece that we are going to carry from C++, as a baseline for future Carbon evolution. It serves our migration goals (especially "Familiarity for experienced C++ developers with a gentle learning curve") by avoiding unnecessary deviation from C++, and instead focusing on subsetting the C++ feature. While we expect this feature to evolve somewhat, the changes we're likely to want can easily be applied incrementally, and this is a fine starting point that anchors us on favoring syntax familiar to C++ developers. ================================================ FILE: proposals/p0301.md ================================================ # Principle: Errors are values [Pull request](https://github.com/carbon-language/carbon-lang/pull/301) ## Table of contents - [Problem](#problem) - [Background](#background) - [Proposal](#proposal) - [Alternatives considered](#alternatives-considered) - [Decouple errors from return values](#decouple-errors-from-return-values) - [Implicit error propagation](#implicit-error-propagation) ## Problem Carbon needs a coherent, consistent approach to handling and propagating errors. ## Background The handling and propagation of errors is a pervasive concern in almost every nontrivial codebase, and every language provides some form of support for it. Whether that takes the form of direct language support for errors, or emerges as a special case of more general-purpose language features, it always has a powerful effect on the performance, safety, and ergonomics of the language. Consequently, Carbon's ability to meet its goals will be strongly influenced by how it supports errors. ## Proposal I propose establishing as a design principle that Carbon's error handling will be based on return values, and specifically return values of sum types, rather than exceptions or other non-return side channels. ## Alternatives considered ### Decouple errors from return values Rather than representing errors using function return values, we could convey errors by way of a separate "channel", as in Swift. This would require a separate syntax for indicating whether a function can raise errors, and unless we want to follow Swift in making errors dynamically typed, that syntax would also need to indicate the type of those errors. We would additionally need a separate syntax for stopping error propagation and resuming normal control flow, such as Swift's `do`/`catch`, because the error "channel" is invisible to ordinary code. This approach may have some performance advantages, because the compiler always statically knows whether a given object represents an error or a successful return value, although it's not clear how significant that advantage will be in Carbon. It would also make it easier to explore different implementation strategies, such as table-driven stack unwinding, that generate very different code for propagating errors than for handling ordinary return values. This approach will make it harder for Carbon code to interoperate with C++ code that uses error returns, and harder to migrate such code to Carbon. It might be an easier migration/interoperation target for C++ code that uses exceptions, but that's less certain, because this approach still differs from C++ exceptions in fairly important ways, such as the fact that typing is static and propagation is explicit. By the same token, this approach is somewhat less likely to feel familiar to C++ programmers. I recommend against this approach for the present, because the need for something like `do`/`catch` makes it more complex to design, and because the potential advantages, particularly with respect to performance, are speculative. It seems better to start with the simpler approach, and let subsequent design changes be driven by concrete experience with whatever problems it has. ### Implicit error propagation As an extension of the previous option, we could treat errors as a separate channel from return values, and allow them to propagate across stack frames implicitly, without an explicit `?` or `try` at the callsite. This is basically how C++ exceptions work, so this would ease migration and interoperation with C++ code that uses them. However, this entails a readability tradeoff: error-propagating code can be distracting boilerplate, but it can also be a vital signal about what the code actually does, depending on the needs of the reader. I recommend against this approach for the present. Instead, we should investigate ways to make explicit error propagation as syntactically lightweight as possible, in order to address the second kind of reader use case, while minimizing the burden on the first. ================================================ FILE: proposals/p0339.md ================================================ # `var` statement [Pull request](https://github.com/carbon-language/carbon-lang/pull/339) ## Table of contents - [Problem](#problem) - [Constants](#constants) - [Background](#background) - [Terminology](#terminology) - [Out of scope features](#out-of-scope-features) - [Language comparison](#language-comparison) - [Proposal](#proposal) - [Executable semantics form](#executable-semantics-form) - [Rationale based on Carbon's goals](#rationale-based-on-carbons-goals) - [Caveats](#caveats) - [`var` name may change](#var-name-may-change) - [Changing to `let mut`](#changing-to-let-mut) - [Multiple identifiers in one statement](#multiple-identifiers-in-one-statement) - [Update provisional pattern matching syntax](#update-provisional-pattern-matching-syntax) - [Update provisional `$` syntax](#update-provisional--syntax) - [Alternatives considered](#alternatives-considered) - [No `var` introducer keyword](#no-var-introducer-keyword) - [Name of the `var` statement introducer](#name-of-the-var-statement-introducer) - [Colon between type and identifier](#colon-between-type-and-identifier) - [Syntax ambiguity](#syntax-ambiguity) - [Confusion with other languages and alternatives](#confusion-with-other-languages-and-alternatives) - [Use in pattern matching](#use-in-pattern-matching) - [Advantages and disadvantages](#advantages-and-disadvantages) - [Conclusion](#conclusion) - [Type after identifier](#type-after-identifier) - [Ordering as a way to quickly answer questions](#ordering-as-a-way-to-quickly-answer-questions) - [Syntax popularity](#syntax-popularity) - [Type elision](#type-elision) - [Advantages and disadvantages](#advantages-and-disadvantages-1) - [Conclusion](#conclusion-1) ## Problem The `var` statement is noted in the [language overview](https://github.com/carbon-language/carbon-lang/tree/trunk/docs/design#ifelse), but is provisional — no justification has been provided. Variable declarations are fundamental, and it should be clear to what degree the current syntax is adopted. It's expected that after the adoption of this proposal, `var` syntax will still not be finalized: the [proposal](#proposal) is an experiment. ### Constants Although constants are naturally related to variables, this proposal does not include any syntax for constants. This is expected to be revisited later. ## Background ### Terminology In this proposal, "variable" is defined as an identifier referring to a mutable value. ### Out of scope features Questions have come up about: - The type system - Type checking - Scoping All of these are important features. However, in the interest of small proposals, they are out of scope of this proposal. ### Language comparison Variables are standard in many languages. Some various forms to consider are: - C++: ```c++ int x; int y = 0; bool a = true, *b = nullptr, c; ``` - Python: ```python x = None y = 0 z: int = 7 # Added by PEP 526. ``` - Swift: ```swift var x = 0 var y: Int = 0 var z: Int ``` - TypeScript ```ts let y: Number = 0; var x = 0; # Legacy from JavaScript. ``` - Rust ```rust let mut x = 0; let mut y: i32 = 0; let mut z: i32; ``` - Go ```go var x = 0 y := 0 var z int var a, b = true, false ``` - Visual Basic ```vb Dim x As Integer = 3 ``` ## Proposal Carbon should adopt `var [ = ];` syntax for variable statements. Considerations for this syntax are: - **Type and identifier ordering:** The ordering of `` before `` reflects the typical C++ ordering syntax. - Some C++ syntax can put type information after the identifier, such as `int x[6];`. Carbon should be expected to place that as part of the type. - **`var` introducer keyword:** The use of `var` makes it clearer for readers to skim and see where variables are being declared. It also reduces complexity and potential ambiguity in language parsing. - **One variable:** In C++, multiple variables can be declared in a single statement. An equivalent Carbon syntax may end up looking like `var (Int x, String y) = (0, "foo");`, so limiting to one declaration is not fundamentally restrictive. However, by breaking with C++ and requiring the full type to be specified with each identifier, we achieve two important things: - It's clear what the full type is, preventing difficult-to-read statements with a mix of stack variables, pointers, and similar. - As the language grows, a function returning a tuple may be assigned to distinctly named and typed variables. **Experiment:** The ordering of type and identifier will be researched. For more information, see the [alternative](#type-after-identifier). ### Executable semantics form Example bison syntax for executable semantics is: ```bison statement: "var" expression identifier optional_assignment; | /* preexisting statements elided */ ; optional_assignment: /* empty */ | "=" expression ; ``` ## Rationale based on Carbon's goals Carbon needs variables in order to be writable by developers. That functionality needs a syntax. Relevant goals are: - [3. Code that is easy to read, understand, and write](/docs/project/goals.md#code-that-is-easy-to-read-understand-and-write): - Adding a keyword makes it easy for developers to visually identify functions. - [5. Fast and scalable development](/docs/project/goals.md#fast-and-scalable-development): - The addition of a keyword should make parsing easy. - [7. Interoperability with and migration from existing C++ code](/docs/project/goals.md#interoperability-with-and-migration-from-existing-c-code): - Keeping syntax close to C++ will make it easier for developers to transition. ## Caveats ### `var` name may change The name `var` could still change. However, it's used with similar meaning in other languages including Swift, Go, and TypeScript, and so it's reasonable to expect it will not. #### Changing to `let mut` The idea that `var` may change includes the possibility that `var` may become something like `let mut` in Rust. However, this is not assumed by this proposal: - This proposal [omits constant syntax](#constants). - It would need to be considered whether the syntax tax of `let mut` appropriately [focuses on encouraging appropriate usage of features rather than restricting misuse](/docs/project/goals.md#code-that-is-easy-to-read-understand-and-write)). - Lower verbosity syntax for variables is more [consistent with C++](p0285.md#c-as-baseline), even if constants are made less verbose by way of `let`. ### Multiple identifiers in one statement Although `var (Int x, String y) = (0, "foo");` syntax is mentioned, this proposal is not intended to propose such a syntax. It's noted primarily to explain the likely path, that this does not _rule out_ abbreviated syntax such as that. That should probably be covered as part of tuples. ### Update provisional pattern matching syntax Pattern matching syntax in [the overview](/docs/design/README.md) uses syntax similar to `Int: x`. As part of removing the [colon between type and identifier](#colon-between-type-and-identifier) from the provisional `var` syntax, that syntax should be changed to remove the `:`. Details should be resolved as part of the eventual pattern matching proposal, but if changes are needed to add a separator, the `var` syntax should be updated to remain consistent. The precise form of that implementation will be part of normal Carbon evolution. For example, replacing `fn Sum(Int: a, Int: b) -> Int;` with `fn Sum(Int a, Int b) -> Int;` and `case (Int: p, (Float: x, Float: _)) if (p < 13) => {` with `case (Int p, (Float x, Float _)) if (p < 13) => {`. ### Update provisional `$` syntax Variables using `Type:$` and similar should drop the `:`, as in `Type$`. ## Alternatives considered Noted alternatives are key differences from C++ variable declaration syntax. ### No `var` introducer keyword The intent of the `var` statement is to improve readability and parsability, and it's related to `fn` for functions. Although code is more succinct without introducers, the noted benefits are expected to be significant. Most other modern languages use similar introducers, and so this break from C++ is adopting a different norm. ### Name of the `var` statement introducer `var` is used with a similar meaning in several other languages, including Swift, Go, and JavaScript. `let` is used by TypeScript. `let mut` is used by Rust, with `let` used for constants (this use of `let` alone is consistent with other languages). In general `var` appears to be a more common choice. ### Colon between type and identifier The use of a colon (`:`) between the type and identifier is intended to reduce potential parsing ambiguity, and to make reading code easier. As proposed, there is no colon between the type and identifier. #### Syntax ambiguity Using a colon or other separator could make it easier to avoid certain kinds of ambiguities. For example, suppose we decided to use a postfix `*` operator to form pointer types, as in C++. In such a setup, we could have code like the following: ```carbon var T * x = 3; var T * x = 3 y; ``` In the first statement, `*` is a unary operator and so `T*` is the type and `x` is the identifier. However, in the second statement, `*` is a binary operator and so `T * x = 3` is the type, and `y` is the identifier; the resulting compiler errors may be confusing to users. Furthermore, the place of `3` could be taken by an arbitrarily complex expression; this could cause resolving the ambiguity between unary and binary `*` to require unbounded look-ahead, adversely impacting [code compilation time goals](/docs/project/goals.md#fast-and-scalable-development). Consider instead the code: ```carbon var T *: x = 3; var T * x = 3: y; ``` The colon makes it unambiguous whether the `*` in each case is unary or binary with only one token of look-ahead. More importantly, this syntax immediately calls the reader's attention to the fact that the second declaration has a highly unusual type. There are other ways of resolving ambiguities like this. For example, we could avoid allowing the same operator to have both postfix and infix forms, or we could distinguish them by the presence or absence of whitespace. However, even if we avoid _formal_ ambiguity by such means, a separator like `:` may be useful for reducing _visual_ ambiguity for human readers. #### Confusion with other languages and alternatives One of the disadvantages of `:` is that with `var Int: x`, ordering is inconsistent with other languages using `:`, such as Rust and Swift, which would say `var x: Int`. It may be worth considering other syntax options. A few to consider are: ```carbon var(Int) x; var Int# x; var Int @x; var Int -> x; ``` These aren't part of the proposed syntax mainly because it's not clear any would gain as much support as `:`. However, this is an opportunity to make suggestions and see if there's a good compromise. #### Use in pattern matching The [old draft pattern matching proposal](https://github.com/carbon-language/carbon-lang/pull/87) used `:` as a separator. In pattern matching, the `:` may be particularly important to distinguish between value matching and type name matching. However, the pattern matching proposal should examine these choices and alternatives before we reach a conclusion that `:` is necessary for pattern matching. Per [syntax ambiguity](#syntax-ambiguity), it is expected that `:` has some advantages, but may not turn out to make a compiler difference due to prevailing constraints on type expression syntax. This proposal suggests we [update provisional pattern matching syntax](#update-provisional-pattern-matching-syntax) to match the proposed `var` syntax. #### Advantages and disadvantages Advantages: - Reduces syntax ambiguity. - This should improve readability and parsability. - It should make it easier to debug issues during development. Disadvantages: - Deviates from the common syntax used by most languages with the type before the identifier, including C, C++, Java, and C#. - Changing from C++ is especially significant because of Carbon's goals for interoperability and migration which will mean an especially large portion of Carbon developers will be actively reading both Carbon and C++ code. - Other notable languages that us `:` in variable statements, including Swift, put the type after the identifier. - It may be worth considering [alternatives to `:`](#confusion-with-other-languages-and-alternatives). - If the alternative of [type after identifier](#type-after-identifier) is adopted, it's likely a `:` separator will be adopted. #### Conclusion Right now the proposal is to not have anything between the type and identifier in order to avoid cross-language ambiguity, and to retain syntax that is closer to C++. However, the ultimate decision may hinge on type and identifier ordering, as well as related future evolution. ### Type after identifier There are many languages that put the type after the identifier. A common format used by Swift and Rust is `var x: Int`. It's worth considering the sentence-like readings: - `var x Int` (or `var x: Int`) may be read as "declare x as an int" or "make a variable x and give it int storage". - `var Int x` may be read as "declare an int called x" or "make a variable with int storage called x". These readings might be of similar quality, and are presented to offer different perspectives on how to read the possible statement orderings. #### Ordering as a way to quickly answer questions Ordering is essentially a question of pairing identifiers and types. This can be cast as asking which question developers consider more important when reading code: 1. What is the type of variable `x`? 2. What is the identifier of the `Int` variable? We assert the first question is the more important one: developers will see an identifier in later code, and want to know its type. However, how do we determine which order is better for this purpose? Unfortunately, little research has been done on this. All we're aware of right now is a study from an unpublished undergraduate project from Germany. The study was done in Java with 50 students. Its data indicates that it's faster to answer question 1 if the type comes first, and faster to answer question 2 if the identifier comes first. We do not want to make decisions based on the study because it isn't published, studied a small group, and doesn't directly compare possible `var` syntaxes; however, it still influences our thoughts. #### Syntax popularity When considering what to use for now, we can consider the popularity of various languages. The top 10 on several sources (with percentages noted by sources that have them) are: | TIOBE | Pct | GitHut | Pct | PYPL | Pct | Octoverse | | ------------ | --: | ---------- | --: | ----------- | --: | ---------- | | C | 16% | JavaScript | 19% | Python | 30% | JavaScript | | Java | 11% | Python | 16% | Java | 17% | Python | | Python | 11% | Java | 11% | JavaScript | 8% | Java | | C++ | 7% | Go | 8% | C# | 7% | TypeScript | | C# | 4% | C++ | 7% | C and C++ | 7% | C# | | Visual Basic | 4% | Ruby | 7% | PHP | 6% | PHP | | JavaScript | 2% | TypeScript | 7% | R | 4% | C++ | | PHP | 2% | PHP | 6% | Objective-C | 4% | C | | SQL | 2% | C# | 4% | Swift | 2% | Shell | | Assembly | 2% | C | 3% | TypeScript | 2% | Ruby | Sources: - [TIOBE](https://www.tiobe.com/tiobe-index/) as of 2021-02 - [GitHut](https://madnight.github.io/githut/) as of 2020 Q4 - [PYPL](https://pypl.github.io/PYPL.html) as of 2021-02 - [Octoverse](https://octoverse.github.com/) as of 2020-10 For these languages: - C, C++, C#, Objective-C, and Java put the identifier after the type. - These use ` `, with no keyword. - Python, Go, TypeScript, Visual Basic, SQL, and Swift put the type after the identifier. - Python uses `: `, with no keyword. This was added in [Python 3.6](https://www.python.org/dev/peps/pep-0526/), and reflects language evolution. - Go uses `var `, with no colon. - TypeScript and Swift use `var : `. - Visual Basic uses `Dim as `. - SQL uses `DECLARE @ AS `, where `AS` is optional. - JavaScript, R, Ruby, PHP, Shell, and Assembly language do not specify types in the same ways as other languages. ### Type elision First, with Carbon it's been discussed to require use of `auto` (or a similar explicit syntax marker) instead of allowing developers to elide the type entirely from a `var` statement. In other words, while `var Int x = 0;` is valid, and `var auto x = 0;` is equivalent, there is no form such as `var x = 0;` which removes the type entirely. Most languages that write `var x: Int` also allow eliding the type when assigning a value. For example, Go allows `x := 0` and Swift allows `var x = 0;`. As a result, there is no need for an `auto` keyword. This would be more surprising with `var Int x` syntax because removing the `Int` now places the identifier immediately after `var`, where the type normally is. This may be subtly confusing to developers. However, if `auto` is required for explicitness, the issue is moot. Retaining `auto` does not eliminate the need to consider type elision as part of advantages and disadvantages: if the type is put after the identifier and `var x: auto` syntax is used, it now becomes an inconsistency with other languages. This inconsistency would be a Carbon innovation that may confuse developers, leading to long-term pressure to remove `auto` for consistency with similar languages, and thus a disadvantage. #### Advantages and disadvantages Advantages: - Consistent with languages that put a `:` in the type declaration. - Notable languages using `x: Int` syntax include Swift, Rust, Kotlin and TypeScript. Go is similar but does not include `:`. - Swift puts [argument labels](https://docs.swift.org/swift-book/LanguageGuide/Functions.html#ID166) before variable declaration. Keeping the identifier first allows consistency with Swift's argument label syntax while also keeping names adjacent. - More opportunity to unify a concept of putting the identifier immediately after the keyword. - `fn`, `import`, and `package` will all have the identifier immediately after the keyword, with non-identifier content following. - It's expected that a typical function declaration will look like `fn Foo(Int bar)`, with `var` only added when a storage for a copy is _required_. Thus, this advantage is primarily about the function identifier `Foo`, not parameter identifiers. - Other cases, such as `alias`, are likely flexible and could follow `var` in concept by putting the resulting name at the end. That is, `alias To = From;` for consistency with `var x: Int;` versus `alias From as To;` similar to `var Int x;` ordering. Disadvantages: - If Carbon doesn't support [type elision](#type-elision), it creates an inconsistency with other notable languages using `x: Int` syntax. - It may be worth considering [alternatives to `:`](#confusion-with-other-languages-and-alternatives). - The ordering of `x: Int` is inconsistent with C++ variable syntax. - Negatively affects [Familiarity for experienced C++ developers with a gentle learning curve](/docs/project/goals.md#interoperability-with-and-migration-from-existing-c-code). - It is consistent with other parts of C++ syntax, particularly `using To = From;`, although not `typedef From To;`. - Early signs are that putting the identifier first makes it slower for developers to answer the question, "what is the type of variable `x`?" - This indicates worse readability in spite of the sentence ordering, the essential part of [Code that is easy to read, understand, and write](/docs/project/goals.md#code-that-is-easy-to-read-understand-and-write). - Popular languages tend to use `int x` syntax, including C, Java, C++, and C#. Other notable languages include Groovy and Dart. #### Conclusion We should conduct a larger study on the topic of type and identifier ordering and syntax. Until then, we should adopt C++-like syntax. This meets the migration sub-goal [Familiarity for experienced C++ developers with a gentle learning curve](/docs/project/goals.md#interoperability-with-and-migration-from-existing-c-code), and allows applying the higher-priority goal [Code that is easy to read, understand, and write](/docs/project/goals.md#code-that-is-easy-to-read-understand-and-write) if supporting evidence is found. **Experiment**: The ordering of type and identifier will be researched. ================================================ FILE: proposals/p0340.md ================================================ # while loops [Pull request](https://github.com/carbon-language/carbon-lang/pull/340) ## Table of contents - [Problem](#problem) - [Background](#background) - [Proposal](#proposal) - [Details](#details) - [Executable semantics form](#executable-semantics-form) - [Caveats](#caveats) - [C++ as baseline](#c-as-baseline) - [`do`/`while`](#dowhile) - [Rationale based on Carbon's goals](#rationale-based-on-carbons-goals) - [Alternatives considered](#alternatives-considered) - [Non-C++ syntax](#non-c-syntax) - [Initializing variables in the `while`](#initializing-variables-in-the-while) ## Problem `while` is noted in the [language overview](/docs/design/README.md#while), but is provisional. Control flow is important, and `while` is basic; the form is similar in many languages, even if details may change. ## Background - C++: A couple example languages following C++'s syntax closely are Java and TypeScript. ```c++ while (x) { DoSomething(); } do { DoSomethingElse(); } while (y); ``` - Python: Python does not provide `do`/`while`. However, the `else` syntax for having code execute if the condition is _never_ true may be of interest. ```python while x: DoSomething() while True: DoSomethingElse() if not y: break while z: print("z is true") else: print("z was never true") ``` - Swift: Swift uses `repeat` instead of `do`. ```swift while x { DoSomething() } repeat { DoSomethingElse() } while y ``` - Rust: Rust provides only a basic `while` loop, relying on the condition-less `loop` to achieve `do`/`while`-like behavior. ```rust while x { DoSomething(); } loop { DoSomethingElse(); if (!y) { break; } } ``` - Go: Go has no `while` loops, only `for` loops. However, a `for` can be written similar to a `while`. ```go for x { DoSomething() } for { DoSomethingElse(); if !y { break; } } ``` ## Proposal Carbon should adopt `while` loop syntax consistent with C and C++. In particular, it should adopt these three kinds of statements: - `while`: declares that we're doing a loop, containing the condition. - `continue`: continues with the next loop iteration, starting with the loop condition. - `break`: breaks out of the loop, without testing the loop condition. ## Details Loop syntax looks like: - `while (` _boolean expression_ `) {` _statements_ `}` While will evaluate the loop condition before each pass of the loop, only continuing if the loop condition is true. When the loop condition evaluates to false, the loop completes. Similar to the [`if`/`else` proposal](https://github.com/carbon-language/carbon-lang/pull/285), the braces are optional and must be paired (`{ ... }`) if present. When there are no braces, only one statement is allowed. `continue` will continue with the next loop iteration directly, skipping any other statements in the loop body. The next loop iteration behaves as normal, starting with the condition being tested. `break` exits the loop immediately, without testing the condition. All of this is consistent with C and C++ behavior. ### Executable semantics form ``` %token WHILE %token CONTINUE %token BREAK statement: WHILE '(' expression ')' statement | CONTINUE ';' | BREAK ';' | /* preexisting statements elided */ ; ``` Note that `continue` and `break` should only be valid in a loop context. ## Caveats ### C++ as baseline This baseline syntax is based on C++, following the migration sub-goal [Familiarity for experienced C++ developers with a gentle learning curve](/docs/project/goals.md#interoperability-with-and-migration-from-existing-c-code). To the extent that this proposal anchors on a particular approach, it aims to anchor on C++'s existing syntax, consistent with that sub-goal. Alternatives will generally reflect breaking consistency with C++ syntax. While most proposals may consider alternatives more, this proposal suggests a threshold of only accepting alternatives that skew from C++ syntax if they are clearly better; the priority in this proposal is to _avoid debate_ and produce a trivial proposal. Where an alternative would trigger debate, it should be examined by an advocate in a separate proposal. ### `do`/`while` `do`/`while` is omitted from this proposal because of disagreement about whether it should be included in Carbon. It's better to have `do`/`while` considered separately as a result, in order to separate review of the non-contentious `while`. ## Rationale based on Carbon's goals Relevant goals are: - [3. Code that is easy to read, understand, and write](/docs/project/goals.md#code-that-is-easy-to-read-understand-and-write): - `while` loops are easy to read and very helpful. - [7. Interoperability with and migration from existing C++ code](/docs/project/goals.md#interoperability-with-and-migration-from-existing-c-code): - Keeping syntax close to C++ will make it easier for developers to transition. ## Alternatives considered Both alternatives from the [`if`/`else` proposal](https://github.com/carbon-language/carbon-lang/pull/285) apply to `while` as well: we could remove parentheses, require braces, or both. The conclusions mirror here in order to avoid a divergence in syntax. Additional alternatives follow. ### Non-C++ syntax Various non-C++ features that came up and are not suggested by this proposal because they aren't in C++ are: - `else` on `while`, as in Python. - A `loop` statement with `while(true)` behavior, as in Rust. - Labeled break and continue statements, as in Java or TypeScript. These may be added later, but they are not part of the [C++ baseline](#c-as-baseline), and have not received much consideration beyond that adopting the proposed syntax would not significantly impair adoption of such features. ### Initializing variables in the `while` This proposal does not offer a way to initialize variables in the `while`. For comparison, C++ does allow declaring a variable in the condition, such as: ```c++ while (optional next = get_next()) { ... } ``` In addition, [Selections statements with initializer](http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/p0305r1.html) could be inferred to suggest a corresponding `while (init; cond)` syntax. Neither of these is suggested in this proposal because we are likely to consider a _different_ route of allowing declaration of a variable in expressions. For example, the following would be legal not because `while` would use a `condition` semantic that allows variable declarations as a form, but because it uses `expression` semantics and `var` would be part of `expression` semantics: ```carbon while (var optional next = get_next()) { ... } ``` In particular, this would also allow more flexible usage addressing more complex use-cases that C++ does not, such as: ```carbon while ((var status_code c = bar()) != SUCCESS) { ... }` ``` This breaks slightly from the [C++ baseline](#c-as-baseline) by offering a subset of C++ functionality. However, we can choose to add related functionality later if `expression` semantics end up not including `var`. Temporarily omitting `condition` functionality avoids having to reconcile it later if we pursue the `expression` route, and it is not crucial to `while` loop functionality. ================================================ FILE: proposals/p0353.md ================================================ # `for` loops [Pull request](https://github.com/carbon-language/carbon-lang/pull/353) ## Table of contents - [Problem](#problem) - [Background](#background) - [C++](#c) - [Java](#java) - [TypeScript and JavaScript](#typescript-and-javascript) - [Python, Swift, and Rust](#python-swift-and-rust) - [Go](#go) - [Proposal](#proposal) - [Details](#details) - [Range inputs](#range-inputs) - [Executable semantics form](#executable-semantics-form) - [Caveats](#caveats) - [C++ as baseline](#c-as-baseline) - [Semisemi support](#semisemi-support) - [Range literals](#range-literals) - [Enumerating containers](#enumerating-containers) - [Rationale based on Carbon's goals](#rationale-based-on-carbons-goals) - [Alternatives considered](#alternatives-considered) - [Include semisemi `for` loops](#include-semisemi-for-loops) - [Writing `in` instead of `:`](#writing-in-instead-of-) - [Multi-variable bindings](#multi-variable-bindings) ## Problem Control flow is documented at [language overview](/docs/design/README.md#control-flow). `for` loops are common in C++, and Carbon should consider providing some form of it. ## Background ### C++ There are two forms of `for` loops in C++: - **Semisemi** (semicolon, semicolon): `for (int i = 0; i < list.size(); ++i)` - **Range-based**: `for (auto x : list)` Semisemi `for` loops have been around for a long time, and are in C. Range-based `for` loops were added in C++11. For example, here is a basic semisemi: ```c++ for (int i = 0; i < list.size(); ++i) { printf("List at %d: %s\n", i, list[i].name); } ``` An equivalent semisemi using iterators and the comma operator may look like: ```c++ int i = 0; for (auto it = list.begin(); it != list.end(); ++it, ++i) { printf("List at %d: %s\n", i, it->name); } ``` Range-based syntax can be simpler, but can also make it more difficult if there are multiple pieces of interesting information: ```c++ int i = 0; for (const auto& x : list) { printf("List at %d: %s\n", i, x.name); ++i; } ``` ### Java Java provides equivalent syntax to C++. Although Java doesn't have a comma operator, it does provide for comma-separated statements in the first and third sections of semisemi for loops. ### TypeScript and JavaScript Both TypeScript and JavaScript offer three kinds of for loops: - Semisemi, mirroring C++. - `for (x of list)`, mirroring range-based for loops. - `for (x in list)`, returning indices. For example, here is an `in` loop: ```javascript for (i in list) { console.log('List at ' + i + ': ' + list[i].name); } ``` ### Python, Swift, and Rust Python, Swift, and Rust all only support range-based for loops, using `for x in list` syntax. ### Go Go uses `for` as its primary looping construct. It has: - Semisemi, mirroring C++. - `for i < list.size()` condition-only loops, mirroring C++ `while` loops. - `for {` infinite loops. ## Proposal Carbon should adopt C++-style range-based `for` loops syntax. Semisemi `for` loops should be addressed through a different mechanism. Related keywords are: - `for` - `continue`: continues with the next loop iteration. - `break`: breaks out of the loop. ## Details For loop syntax looks like: `for (` `var` _type_ _variable_ `:` _expression_ `) {` _statements_ `}` Similar to the [if/else proposal](https://github.com/carbon-language/carbon-lang/pull/285), the braces are optional and must be paired (`{ ... }`) if present. When there are no braces, only one statement is allowed. `continue` will continue with the next loop iteration directly, skipping any other statements in the loop body. `break` exits the loop immediately. All of this is consistent with C and C++ behavior. ### Range inputs The syntax for inputs is not being defined in this proposal. However, we can still establish critical things to support: - Interoperable C++ objects that work with C++'s range-based `for` loops, such as containers with iterators. - Carbon arrays and other containers. - Range literals. These are not proposed, but for an example seen in other languages, `0..2` may indicate the set of integers [0, 2). ### Executable semantics form ```bison %token FOR statement: FOR "(" pattern ":" expression ")" statement | /* preexisting statements elided */ ; ``` The `continue` and `break` statements are intended to be added as part of the [while proposal](https://github.com/carbon-language/carbon-lang/pull/340). ## Caveats ### C++ as baseline This baseline syntax is based on C++, following the migration sub-goal [Familiarity for experienced C++ developers with a gentle learning curve](/docs/project/goals.md#interoperability-with-and-migration-from-existing-c-code). To the extent that this proposal anchors on a particular approach, it aims to anchor on C++'s existing syntax, consistent with that sub-goal. Alternatives will generally reflect breaking consistency with C++ syntax. While most proposals may consider alternatives more, this proposal suggests a threshold of only accepting alternatives that skew from C++ syntax if they are clearly better; the priority in this proposal is to _avoid debate_ and produce a trivial proposal. Where an alternative would trigger debate, it should be examined by an advocate in a separate proposal. ### Semisemi support Carbon will not provide semisemi support. This decision will be contingent upon a better alternative loop structure which is not currently provided by `while` or `for` syntax. If Carbon doesn't evolve a better solution, semisemi support will be added later. For details, see [the alternative](#include-semisemi-for-loops). ### Range literals Range literals are important to the ergonomics of range-based `for` loops, and should be added. However, they should be examined separately as part of limiting the scope of this proposal. ### Enumerating containers Several languages have the concept of providing an index with the object in a range-based for loop: - Python does `for i, item in enumerate(items)`, with a global function. - Go does `for i, item := range items`, with a keyword. - Swift does `for (i, item) in items.enumerated()`, having removed a `enumerate()` global function. - Rust does `for (i, item) in items.enumerate()`. An equivalent pattern for Carbon should be examined separately as part of limiting the scope of this proposal. ## Rationale based on Carbon's goals Relevant goals are: - [3. Code that is easy to read, understand, and write](/docs/project/goals.md#code-that-is-easy-to-read-understand-and-write): - Range-based `for` loops are easy to read and very helpful. - Semisemi `for` syntax is complex and can be error prone for cases where range-based loops work. Avoiding it, even by providing equivalent syntax with a different loop structure, should discourage its use and direct engineers towards better options. The alternative syntax should also be easier to understand than semisemi syntax, otherwise we should just keep semisemi syntax. - [7. Interoperability with and migration from existing C++ code](/docs/project/goals.md#interoperability-with-and-migration-from-existing-c-code): - Keeping syntax close to C++ will make it easier for developers to transition. ## Alternatives considered Both alternatives from the [`if`/`else` proposal](https://github.com/carbon-language/carbon-lang/pull/285) apply to `while` as well: we could remove parentheses, require braces, or both. The conclusions mirror here in order to avoid a divergence in syntax. Additional alternatives follow. ### Include semisemi `for` loops We could include semisemi for loops for greater consistency with C++. This is in part important because switching from a semisemi `for` loop to a `while` loop is not always straightforward due to how `for` evaluates the third section of the semisemi. The inter-loop evaluation of the third section is important given how it interacts with `continue`. In particular, consider the loops: ```c++ for (int i = 0; i < 3; ++i) { if (i == 1) continue; printf("%d\n", i); } int j = 0; while (j < 3) { if (j == 1) continue; printf("%d\n", j); ++j; } int k = 0; while (k < 3) { ++k; if (k == 1) continue; printf("%d\n", k); } int l = 0; while (l < 3) { if (l == 1) { ++l; continue; } printf("%d\n", l); ++l; } ``` To explain the differences between these loops: - The first loop will print 0 and 2. - The second loop will print 0, then loop infinitely because the increment is never reached. - The third loop will only print 2 because the increment happens too early. - Only the fourth loop is equivalent to the first loop, and it duplicates the increment. There is no easy place to put the increment in a `while` loop. Advantages: - We need a plan for [migrating both developers and code from C++](/docs/project/goals.md#interoperability-with-and-migration-from-existing-c-code) semisemis `for` loops, and providing them in Carbon is the easiest solution. - Semisemis remain common in C++ code. - Semisemis are much more flexible than range-based `for` loops. - `while` loops do not offer a sufficient alternative. Disadvantages: - Semisemi loops can be error prone, such as `for (int i = 0; i < 3; --i)`. - Syntax such as `for (int x : range(0, 3))` leaves less room for developer mistakes. - Removing semisemi syntax will likely improve [understandability of Carbon code](/docs/project/goals.md#code-that-is-easy-to-read-understand-and-write), a language goal. - If we add semisemi loops, it would be very difficult to get rid of them. - Code using them should be expected to accumulate quickly, from both migrated code and developers familiar with C++ idioms. If we want to remove `for` loops, we should avoid adding them. We do need to ensure that developers are _happy_ with the replacement, although that should be achievable through providing strong range support, including range literals. A story for migrating developers and code is still required. For developers, it would be ideal if we could have a compiler error that detects semisemi loops and advises the preferred Carbon constructs. For both developers and code, we need a suitable loop syntax that is easy to use in cases that remain hard to write in `while` or range-based `for` loops. This will depend on a separate proposal, but there's at least presently interest in this direction. ### Writing `in` instead of `:` Range-based for loops could write `in` instead of `:`, such as: ```carbon for (x in list) { ... } ``` An argument for switching _now_, instead of using [C++ as a baseline](#c-as-baseline), would be that `var` syntax has been discussed as using a `:`, and avoiding `:` in range-based for loops may reduce syntax ambiguity risks. However, the [current `var` proposal](https://github.com/carbon-language/carbon-lang/pull/339) does not use a `:`, and so this risk is only a potential future concern: it's too early to require further evaluation. Because the benefits of this alternative are debatable and would diverge from C++, adopting `in` would run contrary to [using C++ as a baseline](#c-as-baseline). Any divergence should be justified and reviewed as a separate proposal. ### Multi-variable bindings C++ allows `for (auto [x, y] : range_of_pairs)` which is not explicitly part of the syntax here. Carbon is likely to support this through tuples, so adding special `for` syntax for this would likely be redundant. ================================================ FILE: proposals/p0415.md ================================================ # Syntax: `return` [Pull request](https://github.com/carbon-language/carbon-lang/pull/415) ## Table of contents - [Problem](#problem) - [Background](#background) - [Proposal](#proposal) - [Details](#details) - [Deferred questions](#deferred-questions) - [Alternatives considered](#alternatives-considered) - [Implicit or expression returns](#implicit-or-expression-returns) ## Problem Carbon has functions. We should write down how those functions indicate their code should stop running and, if applicable, what result should be provided back to their caller. ## Background The Carbon overview [contains](/docs/design/README.md#return) a "skeletal design" for `return`. Carbon aims for [_familiarity for experienced C++ developers with a gentle learning curve_](/docs/project/goals.md#interoperability-with-and-migration-from-existing-c-code). C++ returns from functions with a `return` statement, which is documented well [here](https://en.cppreference.com/w/cpp/language/return). ## Proposal Carbon will have a `return` statement that is unsurprising to C++ programmers. Each of these is a valid Carbon function: ``` // A function that returns no value fn NoReturn() { // This function intentionally left blank } // A function that returns no value in a different way fn ReturnNoValue() { // Usually no expression in a function with no return value. return; } // A function that returns a value fn ReturnFive() -> Int { // Must include an expression "convertible" to `Int`. // The (lack of) definition for "convertible" is addressed later. return 5; } ``` ## Details We define a `return` statement in Carbon. It can occur any place a statement is allowed. It takes one optional expression, the _return value_. Functions may contain multiple `return` statements. A function that returns `Void` may have `return` statements. Most will have no return value, though some may provide a `Void`-type expression, for example the result of another `Void`-returning function. > This last affordance is provided to avoid making `Void` a special case in > templating. Functions returning `Void` are also defined to end with an implicit `return` statement. > This implicit `return` is intended to make it easier for later designs to > refer to "the (now always present) `return` statement" rather than the less > well-defined "the point the function returns". A function that returns anything other than `Void` must have at least one `return` statement. All `return` statements in the function must provide an expression "convertible" to the function's return type. If a control flow path reaches the end of the function without executing a `return` statement, it is a compile error. ### Deferred questions This proposal is limited to the syntax and simple static semantics of the `return` statement. Some questions about the full validity and meaning of a `return` statement in context are left unresolved, in the expectation they will be addressed when adjacent parts of the language are specified. Those include: - **What does "convertible" mean?** When a function returns a value, we say the `return` statement takes an expression "convertible" to the function's return type. As a coarse approximation, this may mean "assignable to a variable of the function's return type", but there are likely to be subtleties. We assume this question will be addressed as the type system develops. - **What optimizations are encouraged or guaranteed for return values?** C++ has [copy elision](https://en.cppreference.com/w/cpp/language/copy_elision) and it's a good bet Carbon will too. But first we need to decide what copying means. - **What code, if any, runs between `return` and _returning_?** Carbon is likely to have approaches for deterministic cleanup that trigger when a function's code stops executing -- analogues to C++ destructors, Golang deferred functions, or Java's `finally` blocks. Once we know what they are and how we intend them to work, we can determine how to slot them in next to `return`. ## Alternatives considered ### Implicit or expression returns Some C-family languages allow an unadorned expression in the right context to serve as a return value. In Rust, for example, function bodies are [block expressions](https://doc.rust-lang.org/reference/expressions/block-expr.html), which may have an expression as their final clause and which take the value of that expression. This Rust function returns `5`: ```rust fn return_five() -> i32 { perhaps_unrelated_action(); 5 } ``` Rust still has a `return` statement, so this is entirely an ergonomic feature. We can choose to add a similar feature to Carbon in the future as long as we can unambiguously discern expressions and statements in parsing. ================================================ FILE: proposals/p0426.md ================================================ # Governance & evolution revamp [Pull request](https://github.com/carbon-language/carbon-lang/pull/426) ## Table of contents - [Problem](#problem) - [Proposal](#proposal) - [Governance](#governance) - [Evolution process](#evolution-process) - [Specific changes to members](#specific-changes-to-members) - [Evolution process details](#evolution-process-details) - [Proposal PRs](#proposal-prs) - [Review and RFC on proposal PRs](#review-and-rfc-on-proposal-prs) - [Open questions or blocking issues with proposal PRs](#open-questions-or-blocking-issues-with-proposal-prs) - [Rationale for the proposal based Carbon's goals](#rationale-for-the-proposal-based-carbons-goals) - [Alternatives considered](#alternatives-considered) - [Status-quo](#status-quo) - [Retain a large (8+ people) core team, change role and process](#retain-a-large-8-people-core-team-change-role-and-process) - [Retain consistent use of fixed duration comment periods](#retain-consistent-use-of-fixed-duration-comment-periods) ## Problem Today's governance and evolution process are not meeting the current needs of the Carbon project in a few ways: - Expensive both in effort expended and in wall clock latency to make decisions, at a time when both resources are in short supply on the Carbon project. - This won't allow Carbon to achieve its goals given the time available. - These costs are not proportional. The high constant overhead places a relatively higher cost on simple, uncontroversial proposals. - Proposal authors get a disproportionately large amount of feedback (exacerbating the costs for them of pushing the proposal forward) and a disproportionate amount of _negative_ feedback. - Because there are always many more core team members than authors, even with the best intentions of all involved there will inevitably be an amplification of even accidental negative or unconstructive feedback. - Doesn't allow for specific intermediate questions to come up and be resolved at a smaller granularity than "a proposal". - Sometimes, this results in paying the full overhead of a proposal to merely answer an intermediate question. - Other times this has resulted in a near combinatorial explosion of complexity in the proposal due to a collection of intersecting open questions rather than serializing them eagerly and simplifying accordingly. - The mechanisms used for the evolution process spread elements of the discussion across a number of different tools and "inboxes". While each of these has specific features lacking in the others, the spread carries with it high cost, especially for new community members. An orthogonal problem that is worth solving while here is to update the set of people providing governance for Carbon. This is not a pressing problem, but the set should grow to reflect the many new and active members of the project. ## Proposal This proposal makes significant changes to both the governance model and evolution process. Rather than describing the delta, it focuses on the newly proposed model and process. ### Governance The primary governance model for Carbon shifts to be handled directly by the arbiters from the prior system. There remain three of them, and they make decisions by blocking consensus with a quorum of two. The group is renamed to the "Carbon Leads". They may in the future create other similarly small groups of leads and delegate specific domains or decisions. The core team, as well as other teams, are replaced with a directory of experts. This is an informal list of experts with different backgrounds, experience, expertise, and interest. It is indexed by these different areas and focuses on breadth. People listed should be actively interested in Carbon, invested in its success, and available to engage on their relevant topic area. The intent is to both surface different interested parties, and provide resources for contributors to Carbon for advice, information, or insight on specific areas. ### Evolution process An overview of the new evolution process, but note that details are saved for the detailed section below: - Proposals consist of a PR (pull request) in GitHub that adds a new document to the `proposals/` directory of the project, using a similar template to our current one. - Sending a _proposal_ PR for review also signifies a RFC (request for comment) from the entire community. - One of the three leads should be added as the _assigned_ reviewer. - Contributors should react with a _thumbs-up_ to the proposal PR if they are generally interested and supportive of the high level direction based on title and summary. - Comments during the RFC that represent a _blocking concern_ are moved to their own GitHub issue, and assigned to the leads to decide. - The assigned lead should ensure that at least three contributors (possibly including the lead) are generally supportive and react with thumbs-up. If a proposal doesn't have these thumbs-up, the leads together need to decide whether to move forward, and if so provide those thumbs-up. - If the lead chooses to defer or reject the proposal, they should explain why and close the PR. - Once the thumbs-up are present and the assigned lead finishes code review, the lead should [approve](/docs/project/code_review.md#approving-the-change) the PR. Any outstanding high-level concerns should be handled with blocking issues. - Optionally, the assigned lead can file a blocking issue for a one week final comment period when they approve if it is both useful and important for the proposal. - The proposal PR can be submitted once the assigned lead approves, all blocking issues have been decided, and any related decisions are incorporated. It is also useful to see what the process looks like for different roles within the community. These perspectives are also the most critical to keep simple and easily understood. **Proposal author:** this should feel like a code review, with some broken out issues for longer discussion: - Create a proposal document and PR following the template, potentially using a script. - When ready, send this for review to the leads GitHub team to get an assigned reviewer. - This will also send the proposal as a broad RFC to the community. - Address comments where you can and they make sense. - If you don't see an obvious way to address comments, that's OK. - It's great to engage a bit with the commenter just like you would in code review to clarify their comment or why you don't see an obvious way to address it. - If the commenter feels this is important, they can move it to a blocking issue for a longer discussion and resolution from the leads. - You don't need to try to resolve everything yourself. - Incorporate any changes needed based on the resolution of blocking issues. Once the leads have provided a resolution, it's important to make progress with that direction. - When you both have an [LGTM](/docs/project/code_review.md#approving-the-change) from the assigned lead and the last blocking issue is addressed, submit! - If you end up making significant changes when incorporating resolved issues after the LGTM from the assigned lead, circle back for a fresh LGTM before landing, just like you would with code review. **Community:** anyone that is interested can participate in the RFC: - It's OK to only do this when particularly interested in a proposal, or when asked by one of the leads to help ensure thorough review. Not everyone needs to participate heavily in every RFC. - Once a proposal is sent for review and RFC, read it and leave comments to try to help make the proposal an improvement for Carbon. - Note that progress and improvement are more important than perfection here! - Try to make comments on proposals constructive. Suggest how the proposal could be better if at all possible. - If there is an open question or a critical blocking issue that needs to get resolved, move it to its own issue that the PR depends on, and focus the discussion there. - The issue should focus on surfacing the important aspects of the tradeoff represented by the issue or open question, not on advocacy. **Active contributors:** everyone actively contributing to the evolution of Carbon should try to regularly: - Give a thumbs-up or other reaction on any interesting PRs out for RFC to help surface general enthusiasm for the high level idea / direction. Don't worry about "approving" or the details here. - If interested and time permitting, dive into some RFCs with community feedback. **Carbon Leads:** responsible for making decisions rapidly and ensuring proposal PRs land: - Rapidly resolve all blocking issues raised across any proposals. - When assigned a specific proposal PR: - Make sure it gets both constructive general comments and good code review. - Ideally, you should directly participate in the code review at least, but it's fine to ask others to help. However, ultimately you have to give an LGTM. - Escalate any blocking issues without a resolution that are slowing down the proposal to the other leads. - Evaluate whether an extended final comment period is important for the community given the nature of the proposal. ### Specific changes to members This proposal asks Kate Gregory to join the leads, replacing Titus Winters who doesn't currently have the bandwidth to participate as actively. Given the shift to the leads handling a more direct share of the evolution process, it seems prudent to make this change at the same time. No specific composition of the experts directory is proposed here. Instead, it is suggested to collaboratively build that with interested parties adding themselves and helping structure the domains / areas to be covered. Those changes shouldn't require any formal proposal, and can simply be approved by any one of the leads. ## Evolution process details ### Proposal PRs A proposal PR should use the `proposal` label, have a descriptive title, and easily understood initial summary comment. Authors and leads are encouraged to edit both as necessary to ensure they give the best high-level understanding of the proposal possible. The proposals should then use the template markdown file to describe itself fully. This template will have an additional section to contain the rationale for the proposal being accepted. The purpose of the rationale section will match the prior process's rationale: it must explain how the proposal furthers the goals for Carbon. It is important that proposals stay aligned with Carbon's goals, and where they need to change over time we change the goals themselves rather than inventing new rationale incrementally. While the author of the proposal should suggest an initial rationale, the reviewers and leads can also help improve this to make sure it captures the basis on which the proposal is accepted. Authors shouldn't stress about getting the rationale right out of the box, this is among the easiest parts to improve with help from the community. Proposal PRs can also include changes to the rest of the Carbon project as part of the PR, in subsequent PRs that are referenced for context, or they can be stand-alone changes that are implemented through a series of future PRs to the rest of the project. All of these options are fine. There won't be a separate "decision" file for proposals as the rationale will be incorporated, and the decision will be marked by closing the PR. Existing proposals should be updated to merge their decisions into the document so that we have a simpler layout of the proposals tree and can have simple and easy tooling around it. PRs that are in "draft" status in GitHub are considered works-in-progress (rather than prior `WIP` label). Check with the author before spending time reviewing these, and generally avoid distracting the author with comments unless they ask for them. The proposal may be actively undergoing edits. Feel free to factor out open questions in a proposal to issues that you assign to the leads to resolve. You can even do this before sending the proposal for review. Even after resolved, an open question issue can be reopened if new information comes up during the RFC. ### Review and RFC on proposal PRs When a proposal PR is sent for review by the leads, one of them will be assigned the PR and is responsible for helping land that proposal, or explaining why the project won't move forward in that direction. The assigned lead is also ultimately responsible for the code review on the PR. Proposals sent for review are also sent as an RFC to the entire community. All active Carbon contributors are strongly encouraged to regularly skim the title and summary comment of proposals under RFC that are interesting to them. They should use GitHub reactions, including at least a thumbs-up, to show their interest and enthusiasm about the proposal, and help encourage the author. Writing proposals is _extremely hard work_, and we need to clearly show both interest in the proposed direction of Carbon and appreciation for the work put into the proposal. This is not about _approving_ the proposal, or any of its details. It is completely fine and coherent to both give a thumbs-up to a proposal _and_ provide a serious, blocking issue that needs to be resolved. _Anyone_ in the community, of course including active contributors, is encouraged to participate in the RFC in detail if interested. However, not everyone needs to participate in every RFC. If a proposal is already getting actively and thoroughly reviewed, feel free to focus your time on other proposals with fewer comments. Even if there are issues or problems discovered later, we can always fix them with follow-up proposals. Both code review and high-level design comments are welcome. If an open question comes up or a high level blocking issue is uncovered, feel free to move it to its own issue and assign it to the leads to resolve. That issue is also a good place to focus discussion on that specific topic rather than the main PR. The assigned lead should provide an LGTM on proposals once the following criteria are met: - It looks good from a code review perspective. - At least three thumbs-up reactions showing general community interest. - The community has had a sufficient opportunity to review the proposed change, given its scope and complexity. - Any remaining blocking issues are reasonably likely to resolve in a way that allows the proposal to move forward. It is fine if some are not fully decided, but a lead shouldn't provide an LGTM for a proposal unlikely to move forward. The last two criteria are fundamentally judgement calls for the lead to make, and we don't try to formulate a rigid or fixed bar for them. If resolving the blocking issues requires significant changes, the author should also get a fresh LGTM from the assigned lead after those changes, just like they would with code review. The assigned lead may also request a final comment period for the community when giving an LGTM. This signals to the community that the proposal is likely to move forward once the blocking issues are resolved, and any remaining concerns need to be surfaced. The goal is to help uncover concerns that were hidden until it was clear that the proposal is likely to move forward. However, this is not the default. The assigned lead should only do this when there is some reason to expect further community comment is especially important to solicit. Common cases to consider are contentious, complex, or dramatic changes to the language or project. Ultimately, whether this is important is a judgement call for the lead. This will be modeled by filing a blocking issue that resolves in one week when giving the LGTM. This issue will also explain the motivation for requesting a final comment period. ### Open questions or blocking issues with proposal PRs Any time an open question or a blocking issue is filed for the leads to resolve, that issue forms both the primary discussion thread and where the leads signal how it is resolved. We use issues both to track that there is a specific resolution expected and that there may be dependencies. These issues can be created at any time and by any one. Issues can be created while the proposal is a WIP or draft in order to help inform specific content that should go into the proposal. It is even fine to create an issue first, even before a proposal exists, as an open question about whether to produce a particular proposal, or what a proposal that is being planned should say. For issues which don't (yet) have a specific proposal PR associated with them, at some point the leads may ask that a proposal be created to help collect in a more cohesive place a written overview of the issue and related information, but this process need not be strictly or rigidly bound to having proposal text. Discussion on these issues, especially contentious ones, should endeavor to focus on surfacing information and highlighting the nature of the tradeoff implied by the decisions available. This is in contrast to focusing on advocacy or persuasion. The goal of the issues shouldn't be to persuade or convince the leads to make a specific decision, but to give the leads the information they need to make the best decision for Carbon. It is of course fine that some people have a specific belief of which decision would be best. However, by framing their contributions to the discussion as surfacing the information that underpins that belief the discussion is more likely to be constructive, welcoming, and effective. Overall, everyone should strive to minimize their use of [rhetoric](https://en.wikipedia.org/wiki/Rhetoric) or other [persuasive methods](https://en.wikipedia.org/wiki/Persuasion#List_of_methods) to the extent they can. However, none of this should preclude gathering information like polls of opinion among groups, or signaling agreement. Where community members stand and how many agree with that stance on any issue _is_ information, and useful to surface. Avoid using issues for things that are just requests or suggestions on a proposal PR. If in doubt, start off with a simple comment on the PR and see if there is any disagreement -- everyone may already be aligned and agree. When a comment does seem worth turning into an issue, don't worry about that as the author or the commenter. Getting the leads to resolving a disagreement isn't a bad thing for anyone involved. This should be seen as a friendly way to move the discussion with more disagreement out to its own forum where it'll get resolved, and focus the PR on improving the proposal and getting it ready to land. When an issue is created from a discussion on a PR, and after the discussion on the _issue_ all the original parties actually come to a happy agreement, it's totally ok to just close the issue and move back to the code review in the PR. Anyone who would prefer the leads to still chime in can just re-open the issue and the leads will follow up, even if its just to get confirmation that everyone _did_ end up happy with the resolution. At the end of the day, while it's fine to resolve an issue that _everyone_ actually ended up agreeing about (maybe once some confusion is addressed), ultimately the leads are responsible for resolving these issues and there is no pressure on anyone else to do so. ## Rationale for the proposal based Carbon's goals Our goals document identifies that "[t]he community needs to be able to effectively engage in the direction and evolution of the project and language, while keeping the process efficient and effective. That means we need an open, inclusive process where everyone feels comfortable participating. Community members should understand how and why decisions are made, and have the ability to both influence them before they occur and give feedback afterward." Our prior process, in a well-intentioned effort to ensure we have scope for community engagement, is not as efficient and effective as we would like, and that lack of efficiency ironically has a negative impact on community engagement that outweighs the benefits. The proposed process removes several artificial delays from our process and enables much earlier feedback and decision-making as part of proposal development, ideally without introducing a negative impact on any of the other aspects of this goal. ## Alternatives considered ### Status-quo We could simply not make a change here. Advantages: - No need to make a change. Disadvantages: - Does not solve any of the problems. ### Retain a large (8+ people) core team, change role and process We could attempt to solve the problems cited while keeping the larger core team structure. This would largely involve carefully changing the role that members of the core team fill when making decisions, and the process used to make those decisions. Advantages: - Less dramatic change to the governance structure - Retains a leadership group that can have some realistic breadth across the community Disadvantages: - Extremely difficult to formulate a role that both serves a purpose and addresses the cited problems. - Every idea for both process and role that seemed to potentially help the problems listed also ended up heavily leaning on the arbiters and thus reducing the utility of the role. - Continues to require forming and sustaining the core team. - Would inherently result in slower progress. ### Retain consistent use of fixed duration comment periods These were added based on the repeated failure modes seen in other language communities where members of the community didn't feel they had sufficient opportunity to participate in discussions. We could retain them with the new process. Advantages: - Defends against failure modes seen in other language evolution processes. - Ensures a predictable amount of time for the community to comment. Disadvantages: - Would dramatically slow the rate of progress. - Reduces the ability of proposal authors to fully feel satisfied and rewarded for their effort by separating all of their work from any of the benefits being accrued. - This kind of delay decreases human feelings of satisfaction with work, which in turn disincentives people making proposals and seeing them through to completion. - Unclear whether the failure modes observed in other language evolution processes will actually occur for Carbon. - Many other aspects of the projects and communities are sufficiently different to make the prediction difficult. - Initially, the size of the project alone makes these concerns a non-issue. - Makes it too easy to delay commenting until the end of the period. - This in turn can cause comments to be rushed to avoid the period expiring. ================================================ FILE: proposals/p0438.md ================================================ # Functions [Pull request](https://github.com/carbon-language/carbon-lang/pull/438) ## Table of contents - [Problem](#problem) - [Background](#background) - [C++ syntax](#c-syntax) - [Other languages](#other-languages) - [Forward declarations](#forward-declarations) - [Proposal](#proposal) - [Functions](#functions-1) - [Forward declarations](#forward-declarations-1) - [Rationale based on Carbon's goals](#rationale-based-on-carbons-goals) - [Open questions](#open-questions) - [Calling functions defined later in the same file](#calling-functions-defined-later-in-the-same-file) - [Optional argument names](#optional-argument-names) - [Alternatives considered](#alternatives-considered) - [Function keyword](#function-keyword) - [Type](#type) - [`-` (dash)](#--dash) - [`def`](#def) - [`fn`](#fn) - [`fun`](#fun) - [`func`](#func) - [`function`](#function) - [Conclusion](#conclusion) ## Problem We currently have [placeholder guidance on functions](/docs/design/functions.md). The intent of this proposal is to establish agreement on the basics, providing a baseline for future evolution. ## Background ### C++ syntax C++ syntax for function declarations comes in two forms: ```cpp std::int64_t Sum(std::int64_t a, std::int64_t b) { return a + b; } // Or with trailing return type syntax: auto Sum(std::int64_t a, std::int64_t b) -> std::int64_t { return a + b; } ``` Bodies are always wrapped by braces. ### Other languages To summarize keyword use from other languages: - Type: C# and Java - `-`: Objective-C - `def`: Python and Ruby - `fn`: Rust - `fun`: Kotlin - `func`: Go and Swift - `function`: JavaScript, MatLab, PHP, R, and TypeScript For exhaustive function examples: - C# ```csharp int Sum(int a, int b) { return a + b; } ``` - Go ```go func add(a int, b int) int { return a + b } ``` - Java ```java Int Sum(Int a, Int b) { return a + b; } ``` - JavaScript ```javascript function Sum(a, b) { return a + b; } ``` - Kotlin ```kotlin fun add(a: Int, b: Int): Int { return a + b } ``` - Matlab ```matlab function s = sum(a,b) s = a+b; end ``` - Objective-C ```objc - (int)sum:(int)a (int)b { return a + b; } ``` - PHP ```php function sum(int $a, int $b) { return $a + $b; } ``` - Python ```python def sum(a, b): return a + b def sum(a: int, b: int) -> int: return a + b ``` - R ```r sum <- function(a, b) { a + b } ``` - Ruby ```ruby def sum(a, b) return a + b end ``` - Rust ```rust fn sum(a: i64, b: i64) -> i64 { a + b } ``` - Swift ```swift func sum(a: Int, b: Int) -> Int { return a + b } ``` - TypeScript ```typescript function sum(a: number, b: number): number { return a + b; } ``` ### Forward declarations [Forward declarations](https://en.wikipedia.org/wiki/Forward_declaration) of functions are largely unique to C++. Objective-C also has this. However, most other languages being discussed do not. ## Proposal ### Functions Function declarations and definitions should look like: `fn` _function name_ `(` _type identifier \[_ `,` _type identifier ]..._ `)` _[_ `->` _return type_ _] [_ `{` _body_ `}` _|_ `;` _]_ Arguments should be comma-separated and imitate [`var` syntax](https://github.com/carbon-language/carbon-lang/pull/339), although `var` itself is not used. For example: ```carbon fn Sum(Int a, Int b) -> Int { } fn UnusedArgument(Int _) -> Int { } ``` The return type may optionally be omitted if `()`. For example, these two function declarations both return `()`: ```carbon fn Print(String s) -> (); fn Print(String s); ``` ### Forward declarations Forward declarations will be supported in order to support separation of API and implementation, as explained in [code and name organization](https://github.com/carbon-language/carbon-lang/tree/trunk/docs/design/code_and_name_organization). For example: ```carbon package Math api; fn Sum(Int a, Int b) -> Int; ``` Forward declarations will experimentally _only_ be allowed in `api` files, and _only_ when the definition is in the library's `impl` file. The intent is to minimize use, consistent with most other languages that have no forward declaration support at all. ## Rationale based on Carbon's goals Carbon needs functions in order to be writable by developers. That functionality needs a syntax. Relevant goals are: - [3. Code that is easy to read, understand, and write](/docs/project/goals.md#code-that-is-easy-to-read-understand-and-write): - Adding a keyword makes it easy for developers to visually identify functions. - Trailing return syntax should be easier to understand than C++'s older preceding return syntax. Only allowing one avoids requiring developers to recognize and choose between equivalent syntaxes. - [5. Fast and scalable development](/docs/project/goals.md#fast-and-scalable-development): The addition of a keyword should make parsing easy. - [7. Interoperability with and migration from existing C++ code](/docs/project/goals.md#interoperability-with-and-migration-from-existing-c-code): Keeping syntax close to C++ will make it easier for developers to transition. ## Open questions ### Calling functions defined later in the same file C++ supports forward declaring functions in a `.cpp` file, and this may be useful for handling interdependencies between functions. In Carbon, we should instead aim to allow having a function able to call a function defined later in the same file without requiring a forward declaration. Advantages: - Minimize accidents with forward declarations not matching implementation. - Fewer forward declarations for developers to find while reading. Disadvantages: - Shifts burden onto how code is parsed and executed. We'll need to evaluate how this works. There is tracked by [#472](https://github.com/carbon-language/carbon-lang/issues/472). This does not need to block this proposal, as we can allow it later if that's the decision; it may also be helpful to give more time to consider how name lookup should work. ### Optional argument names Argument names are required under this proposal. It's likely that developers will want a way to indicate unused arguments. This is tracked by [#476](https://github.com/carbon-language/carbon-lang/issues/476). ## Alternatives considered ### Function keyword We may have anchored on `fn` without much consideration. A key piece of this proposal is to suggest reconsidering the choice, even if we end up with the same. #### Type Advantages: - Echoes C++, C#, and Java. - Simpler to write; a type will often be present for the return regardless. Disadvantages: - Makes function syntax more difficult to parse. - In C++, it's notable that functions with a trailing return still start with `auto` to indicate a "type". There's a consensus that _some_ keyword should be used in order to simplify parsing, so this option is not expected to receive much support. #### `-` (dash) Advantages: - Echoes Objective-C instance method syntax. - Class methods use `+` and we could echo that too. - This is the most succinct option. Disadvantages: - Easy to miss, and hard to read as a result. - Might be ambiguous with the `-` operator. Although this alternative is mentioned, it is not expected to receive much support due to its readability problem. #### `def` Advantages: - Echoes Python and Ruby. Disadvantages: - `def` is presumably short for "define", which may not be as obvious as a "function" derivative. #### `fn` Advantages: - Echoes Rust. - The shortest "function" derivative. - Likely comes from the [fn key](https://en.wikipedia.org/wiki/Fn_key). Disadvantages: - Abbreviation by removing letters in the middle of a word is [disallowed by Carbon's C++ style](https://google.github.io/styleguide/cppguide.html#General_Naming_Rules). - Even though the abbreviation is in Wikipedia, it is not associated with [functions in programming](https://en.wikipedia.org/wiki/Subroutine). - Would likely be the only keyword abbreviated this way. #### `fun` Advantages: - Echoes Kotlin. - Could be used with `var` as a push towards three letter abbreviations. - Not clear there are other examples of three letter abbreviations. - `let` may be used in a similar way, although it's not an abbreviation. Disadvantages: - "fun" is a common English word and may be a mildly confusing choice as a result. - When wrapping function definitions, the function name and wrapped types would end up on the same column when indenting by 4 spaces. These use the same casing, so it may make it slower to understand code. - For example: ```carbon fun Print( String message) { ... } // Similar confusion if the body of the function is indented 4 spaces. fun Print(String message) { SomeFunctionCall(); ... } ``` - This is also true for `var`, but wrapping arguments is expected to be more common for functions, and the casing more likely to match. #### `func` Advantages: - Echoes Go and Swift. Disadvantages: - The longest abbreviated form of "function". #### `function` Advantages: - Echoes JavaScript, MatLab, PHP, R, and TypeScript. - Clear meaning without any abbreviation. Disadvantages: - The longest "function" derivative. ### Conclusion `fn` and `func` are the favored options: - Desire to abbreviate "function": - `fn` is clearly shorter than `func`. Both are shorter than "function". - Consistency with other abbreviations: - We are using abbreviations like `var`, and something like `mut` or `const` seems likely. `ptr` is possible. - `func` is consistent with abbreviation by removing letters at the end. - `fn` is more consistent with abbreviations like `ptr` that remove letters in the middle, but a three letter abbreviation like `fcn` would be more consistent. - We are using [Google C++ style](https://google.github.io/styleguide/cppguide.html#General_Naming_Rules) as a base, which explicitly discourages abbreviating by deleting letters within a word. - `fn` deletes letters in the middle of "function", `func` does not. - Familiarity for developers: - `func` is used by Go and Swift, both of which are common languages. - [`func`](https://www.google.com/search?q=func) is very searchable. - `fn` is only used by Rust, which by most measures has less adoption than either Go or Swift at present. However, it is used in [Hungarian notation](https://docs.microsoft.com/en-us/windows/win32/stg/coding-style-conventions), so some C++ developers will be familiar with `fn` for function pointers. - It's also used for the [Fn key](https://en.wikipedia.org/wiki/Fn_key), although assumptions about the meaning of "Fn" as abbreviation for "function" versus "F-number key" (as in F5) may mislead some developers. - [`fn`](https://www.google.com/search?q=fn) is difficult to search for. - Pronunciation: - Both may be pronounced as "function", but may also be pronounced by their abbreviation. - `func` could be as "funk", defined as a music genre or state of depression. - `fn` could be as: - "eff en", which has no direct meaning but could be misheard as "effing", or profanity. However, it's not clear that this potential mishearing is a pragmatic issue. - "fun", defined as enjoyment. Note that both are argued here as reasonable solutions that would satisfy many. This was asked on [#463](https://github.com/carbon-language/carbon-lang/issues/463). We are using `fn`. ================================================ FILE: proposals/p0444.md ================================================ # GitHub Discussions [Pull request](https://github.com/carbon-language/carbon-lang/pull/444) ## Table of contents - [Problem](#problem) - [Background](#background) - [Proposal](#proposal) - [Alternatives considered](#alternatives-considered) - [Keep using Discourse Forums](#keep-using-discourse-forums) - [Try to maintain history](#try-to-maintain-history) - [Caveats](#caveats) - [Rationale based on Carbon's goals](#rationale-based-on-carbons-goals) ## Problem Right now we run our own Discourse Forums (broken link: `https://forums.carbon-lang.dev/`). [GitHub Discussions](https://docs.github.com/en/discussions) was recently released, and should be evaluated for appropriateness as a replacement. ## Background [GitHub Discussions](https://docs.github.com/en/discussions) is a new forum solution offered by GitHub. Although it was initially only for public repositories, as of March, it is [available to private repositories](https://github.blog/2021-03-09-github-discussions-now-available-for-private-repositories/). Developers can [preview in carbon-lang](https://github.com/carbon-language/carbon-lang/discussions). Discourse Forums (broken link: `https://forums.carbon-lang.dev/`) are currently used by Carbon. These run on a Google Cloud instance owned by Carbon team. ## Proposal We should shut down the Discourse Forums and adapt GitHub Discussions to our needs. GitHub Discussions categories and documentation should be updated accordingly, alongside evolution process changes. Discourse Forums should be frozen and eventually shut down once we are confident in the switch. History can manually copied from the frozen instance as needed, and discarded otherwise. ## Alternatives considered ### Keep using Discourse Forums Advantages: - Discourse's UI is better in some places: - Easy drag-and-click UI for quoting. - Easy to navigate to the original post of a quote. - Less noisy notifications because it's separate from other discussion, such as issues and PRs. - However, notifications will all be in one place. - Preview is visible while writing, whereas GitHub uses a tab. - More advanced search support. - Discourse Forums has categories and subcategories. - GitHub Discussions only has categories. - Discourse Forums has controllable settings for many things. - GitHub Discussions offers fewer options, although simplicity is sometimes better. - Keeps existing history. - [Swift](https://forums.swift.org/) and [Rust](https://users.rust-lang.org/) are prominent examples also using Discourse. - Avoids handing more control to GitHub. Disadvantages: - GitHub's UI is better in some places: - Responses to responses are threaded in-place, which may be appreciated by some. - Links to issues and PRs will work more seamlessly, with tooltips. - Discourse Forums has nuisance warnings about similar posts and @-ing to many people in a group, which cannot be disabled. These get in the way of frequent posters. - When copying links, Discourse Forums' URL updates made it easy to accidentally link to a specific post; GitHub makes that require a deliberate action. - There is direct support for filing issues based on discussions. - Supports voting on posts and sorting by vote, for more types of discussions, particularly Q&A. - Discourse Forums is a now unnecessary split in communication mechanisms. - GitHub is the only possible direction of consolidation: we already use GitHub for issues and PR discussion, and moving that to Discourse is infeasible. - Running Discourse Forums ourselves requires significant maintenance, and [still has configuration issues](https://github.com/carbon-language/carbon-lang/issues/356). ### Try to maintain history We've deliberately not put much into Discourse Forums, and GitHub issues are already a focus of discussion. There's not enough history to justify significant investment into making a copy. #### Caveats The contributor wiki (broken link: `https://forums.carbon-lang.dev/t/contributor-directory-wiki/134/2`) can be moved to [GitHub's wiki](https://github.com/carbon-language/carbon-lang/wiki). ## Rationale based on Carbon's goals - [Community and culture](/docs/project/goals.md#community-and-culture): - GitHub Discussions offers a way to consolidate tools and make community forums easier to find. ================================================ FILE: proposals/p0447.md ================================================ # Generics terminology [Pull request](https://github.com/carbon-language/carbon-lang/pull/447) ## Problem To talk about generics as a programming language feature, you need a lot of specialized terminology. We need to agree on the words we are using and their meaning before we can meaningfully talk about the design of the feature itself. There a number of problems a glossary solves: - Not everyone knows every term, so having a single place to look them up will improve the ease of understanding, ease of contributing, and accessibility of the project. - There may not be widespread agreement on the meaning of some terms. In particular, individual programming languages tend to assign very specific meanings to terms used within their ecosystem. - Some terms may be used in multiple ways, but we only use the term with one specific meaning. - Some terms are our invention and we need to introduce them. ## Proposal See the [generics terminology document](../docs/design/generics/terminology.md). ## Rationale This gives a common vocabulary for discussing the design of the generics feature. ================================================ FILE: proposals/p0524.md ================================================ # Generics overview [Pull request](https://github.com/carbon-language/carbon-lang/pull/524) ## Table of contents - [Problem](#problem) - [Background](#background) - [Proposal](#proposal) - [Rationale based on Carbon's goals](#rationale-based-on-carbons-goals) - [Alternatives considered](#alternatives-considered) ## Problem We want to Carbon to have a high quality generics feature that achieves the goals set out in [#24](https://github.com/carbon-language/carbon-lang/pull/24). This is too big a feature to land in a single proposal. This proposal specifies the feature at the level of an overview description. This overview document is intended to act as a starting point for people interested in the generics feature by providing: - a high-level description of the generics feature, and - pointers to documents that go deeper into individual topics. ## Background This is a follow on to these previous generics proposals: - [Generics goals #24](https://github.com/carbon-language/carbon-lang/pull/24) - [Generics terminology #447](https://github.com/carbon-language/carbon-lang/pull/447) The content for this proposal was extracted from a larger [Generics combined draft proposal](https://github.com/carbon-language/carbon-lang/pull/36). ## Proposal This is a proposal to add [this overview document](/docs/design/generics/overview.md). ## Rationale based on Carbon's goals Much of this rationale was captured in the [Generics goals proposal](https://github.com/carbon-language/carbon-lang/pull/24). ## Alternatives considered Alternatives considered will be in a future proposal. Some of them can be seen in a rough form in [#36](https://github.com/carbon-language/carbon-lang/pull/36). ================================================ FILE: proposals/p0538.md ================================================ # `return` with no argument [Pull request](https://github.com/carbon-language/carbon-lang/pull/538) ## Table of contents - [Problem](#problem) - [Use cases](#use-cases) - [What's wrong with the C++ rule?](#whats-wrong-with-the-c-rule) - [Background](#background) - [Proposal](#proposal) - [Details](#details) - [Relation to upcoming proposals](#relation-to-upcoming-proposals) - [Rationale based on Carbon's goals](#rationale-based-on-carbons-goals) - [Alternatives considered](#alternatives-considered) - [Retain the C++ rule](#retain-the-c-rule) - [Fully divorce functions and procedures](#fully-divorce-functions-and-procedures) ## Problem ### Use cases We wish to support the following use cases: - Functions that return values. This should be supported for any type for which a value can be formed, and such support should be uniform and independent of whether the return type is `()`. A return value must always be produced. - Functions that do not return a value ("procedures"). In such functions, we do not need or want to be able to return a value. No return value is needed, so if control flow reaches the end of a procedure, it should return to its caller. - Functions with parameterized return types, that may be either of the above, depending on the parameterization. For example, a call wrapper might be parameterized by the type of its callee, and might return the same type that its callee returns, or a type computed based on that type. ### What's wrong with the C++ rule? C++ treats `void` as a special case in a number of ways. We want to minimize the impact of this special-case treatment for the corresponding Carbon types. One way that this special treatment is visible in C++ is that functions with the return type `void` obey different rules: the operand of `return` becomes optional (but is still permitted), and reaching the end of the function becomes equivalent to `return;`. In a function template, this can even happen invisibly, with some instantiations having an implicit `return;` and others not. This interferes with the ability to reason about programs. Consider: ``` template auto f() { if (T::cond()) return T::run(); } ``` Here, it is possible for control flow to reach the end of the function. However, a compiler can't warn on this without false positives, because it's possible that `T::run()` has type `void`, in which case this function has an implicit `return;` added before its `}`, and indeed, it might be the case that `T::run()` always has return type `void` for any `T` where `T::cond()` returns `false`. ## Background See the following issues: - [Proposal: function declaration syntax](https://github.com/carbon-language/carbon-lang/pull/438) - [Proposal: `return` statements](https://github.com/carbon-language/carbon-lang/pull/415) - [Leads question: what is the relationship between `Void` and `()`?](https://github.com/carbon-language/carbon-lang/issues/443) - [Leads question: should we allow `return;` in functions with a `Void` return type?](https://github.com/carbon-language/carbon-lang/issues/518) ## Proposal Instead of applying special-case rules based on whether the return type of a function is `()`, we apply special-case rules based on whether a return type is provided. ## Details A function with no declared return type is a _procedure_. The return type of a procedure is implicitly `()`, and a procedure always returns the value `()` if and when it returns. Inside a procedure, `return` must have no argument, and if control flow reaches the end of a procedure, the behavior is as if `return;` is executed. ``` // F is a procedure. fn F() { if (cond) { return; } if (cond2) { // Error: cannot return a value from a procedure. return F(); } // Implicitly `return;` here. } ``` A function with a declared return type is treated uniformly regardless of whether that return type happens to be `()`. Every `return` statement must return a value. There is no implicit `return` at the end of the function, and instead a compile error is produced if control flow can reach the end of the function, even if the return type is `()` -- or any other unit type. ``` fn G() -> () { if (cond) { // OK, F() returns (). return F(); } if (cond2) { // Error: return without value in value-returning function. return; } // Error: control flow can reach end of value-returning function. } ``` From the caller's perspective, there is no difference between a function declared as ``` fn DoTheThing() -> (); ``` and a function declared as ``` fn DoTheThing(); ``` As a result, the choice to include or omit the `-> ()` in the definition is an implementation detail, and the syntax used in a forward declaration is not required to match that used in a definition. The use of `-> ()` in idiomatic Carbon code is expected to be rare, but it is permitted for uniformity, and in case there is a reason to desire the value-returning-function rules or to emphasize to the reader that `()` is the return type or similar. ### Relation to upcoming proposals Issue [#510](https://github.com/carbon-language/carbon-lang/issues/510) asks whether we should support named return variables: ``` fn F() -> var ReturnType: x { // initialize x return; } ``` If we do, functions using that syntax should follow the rules for procedures in this proposal, including the implicit `return;` if control reaches the end of the function. In particular, ``` fn F() { ... } ``` would be exactly equivalent to ``` fn F() -> var (): _ = () { ... } ``` ## Rationale based on Carbon's goals - **Software and language evolution** - This proposal may decrease the number of places in which the return type of a procedure is used, by discouraging the use of `return Procedure();`. This in turn may make it easier to change a return type from `()` to something else, but this proposal by itself is insufficient to ensure that is always possible. - **Code that is easy to read, understand, and write** - The conceptual integrity of the Carbon language is improved by making the same syntax result in the same semantics, regardless of whether a type happens to be `()` or not, and symmetrically by using different syntax for different semantics. - The readability of Carbon code is improved and a source of surprise is eliminated by removing the possibility of `return F();` being mixed with `return;` in the same function. - **Practical safety guarantees and testing mechanisms** - By making the presence or absence of an implicit `return` in a function be determined based on syntax alone, we permit checks for missing `return` statements to be provided in the definition of a template or generic, without needing to know the arguments. This is important for generics in particular, because we do not want monomorphization to be able to fail and because we do not in general guarantee that monomorphization will be performed. - **Fast and scalable development** - The correct syntax for a `return` statement can be detected while parsing, using only syntactic information rather than contextual, semantic information. In practice, we will likely parse both kinds of `return` statement in all functions and check the return type from a context that has the semantic information, but the ability to do these checks syntactically may be useful for simple tools and editor integration. - **Interoperability with and migration from existing C++ code** - This proposal rejects some constructs that would be valid in C++: ``` return F(); ``` in a function with `void` return type would no longer be valid in a corresponding Carbon function with no specified return type, and would need to be translated into ``` F(); return; ``` (possibly with braces added). However, the fact that this construct is valid in C++ is surprising to many, and the constructs that would be idiomatic in C++ are still valid under these rules. ## Alternatives considered ### Retain the C++ rule The advantages of this approach compared to maintaining the C++ rule are discussed above. The advantage of maintaining the C++ rule would be that Carbon is more closely aligned with C++. However, the removed functionality -- specifically, the ability to return an expression of type `void` from a `void` returning function -- is still available, albeit with a more verbose syntax, and the existence of that functionality in C++ is a source of surprise to C++ programmers. ### Fully divorce functions and procedures We could treat the choice of function with `()` return type versus procedure as being part of the interface rather than being an implementation detail. ``` // F is a procedure. fn F(); // F is a function returning (). fn G() -> (); // ... // Error, procedure redeclared as a function. fn F() -> () { return (); } // Error, function redeclared as a procedure. fn G() { } ``` Then, we could disallow any use of a procedure call in a context that depends on its return value, treating a procedure call as a statement rather than as an expression that can be used as a subexpression or an operand of an operator. ``` fn Func() -> (); fn Proc(); // OK, x is of type (). auto x = Func(); // Error, Proc is a procedure. auto y = Proc(); ``` Advantages: - Removes all special treatment of `()` in this context. Procedures no longer need to say that their return type is implicitly `()` nor that they implicitly return `()`. - Adding a return type to a procedure -- converting it to a function -- would be a non-breaking change. - But we don't have evidence that this is a common problem. - Prevents a source of programming error where a returned `()` value is stored and used. - But it's not clear that this would be a frequent error, and it would likely be caught in other ways due to the limited API of `()`. Disadvantages: - Having distinct notions of function versus procedure would be a surprise for those coming from C++. - Supporting `auto x = F();` regardless of whether `F` is a function or procedure may be important for generic code. - When migrating C++ code to Carbon, this makes the choice of function versus procedure load-bearing, as there may be uses that depend on a return value existing, for example in templates. ================================================ FILE: proposals/p0540.md ================================================ # Remove `Void` [Pull request](https://github.com/carbon-language/carbon-lang/pull/540) ## Table of contents - [Problem](#problem) - [Background](#background) - [Proposal](#proposal) - [Rationale based on Carbon's goals](#rationale-based-on-carbons-goals) - [Alternatives considered](#alternatives-considered) ## Problem The `Void` type as [currently specified](https://github.com/carbon-language/carbon-lang/blob/4bf396b8f6e7f5289c170c5ad9dda64c5c680d4a/docs/design/README.md#primitive-types) is redundant with `()`, the type of a tuple with no elements. ## Background [Issue 443](https://github.com/carbon-language/carbon-lang/issues/443) contains further discussion of the problem, and possible solutions. The consensus of the Carbon leads was that `Void` should be removed. ## Proposal Remove `Void` from the Carbon design. ## Rationale based on Carbon's goals Eliminating `Void` will make Carbon code [easier to read, understand, and write](/docs/project/goals.md#code-that-is-easy-to-read-understand-and-write). The main advantage of `Void` is that it is recognizable and familiar to C++ programmers. However, we haven't yet found any use cases where using `Void` results in clearer code, even to programmers transitioning from C++. In particular, omitting a function's return type is more concise and at least as clear as explicitly specifying `-> Void`. In most other use cases, the appearance of familiarity is more likely to mislead than to clarify: most other use cases for C++ `void`, such as using `void*` to mean "pointer to anything", will not work with Carbon's `Void`, and most other use cases for Carbon's `Void`, such as using it as the type of a variable, would not work with C++'s `void`, ## Alternatives considered - Define `Void` as an alias for `()`. This is workable, but forces users to understand both spellings, and make a style choice between them. - Define `Void` as a distinct type from `()` with the same semantics. This forces users to know "which kind of nothing" to use in any given context - Define `Void` as a distinct type from `()`, with more C++-like semantics. This would reproduce the problems of C++'s `void`, for no clear benefit. - Eliminate `()`. This would needlessly complicate programming with tuples, especially in variadic settings. See [issue 443](https://github.com/carbon-language/carbon-lang/issues/443) for details. ================================================ FILE: proposals/p0553.md ================================================ # Generics details part 1 [Pull request](https://github.com/carbon-language/carbon-lang/pull/553) ## Table of contents - [Problem](#problem) - [Background](#background) - [Proposal](#proposal) - [Rationale based on Carbon's goals](#rationale-based-on-carbons-goals) - [Alternatives considered](#alternatives-considered) - [Interface implementation syntax](#interface-implementation-syntax) - [`for` instead of `as` in external `impl`](#for-instead-of-as-in-external-impl) - [No `as` for inline `impl`](#no-as-for-inline-impl) - [No `external` for external `impl`](#no-external-for-external-impl) - [Out-of-line impl](#out-of-line-impl) - [`extend` blocks](#extend-blocks) - [Others](#others) ## Problem We want to Carbon to have a high quality generics feature that achieves the goals set out in [#24](https://github.com/carbon-language/carbon-lang/pull/24). This is too big to land in a single proposal. This proposal goes into the details of the core of the feature, and provides an outline covering future work. It covers: - interfaces - implementing interfaces for types - resolving name conflicts - facet types - type-types as the way of describing type variables - structural interfaces - combining interfaces - interface requirements and extension - type compatibility ## Background This is a follow on to these previous generics proposals: - [Generics goals #24](https://github.com/carbon-language/carbon-lang/pull/24) - [Generics terminology #447](https://github.com/carbon-language/carbon-lang/pull/447) - [Generics overview #524](https://github.com/carbon-language/carbon-lang/pull/524) The content for this proposal was extracted from a larger [Generics combined draft proposal](https://github.com/carbon-language/carbon-lang/pull/36). ## Proposal This is a proposal to add [this detailed design document](/docs/design/generics/details.md). ## Rationale based on Carbon's goals Much of this rationale was captured in the [Generics goals proposal](https://github.com/carbon-language/carbon-lang/pull/24). ## Alternatives considered ### Interface implementation syntax The interface implementation syntax was decided in [question-for-leads issue #575](https://github.com/carbon-language/carbon-lang/issues/575). ``` struct Song { // data and methods ... impl as Printable { method (me: Self) Print() { ... } } } external impl Song as Comparable { ... } ``` This proposal includes additional discussion and additional alternatives. #### `for` instead of `as` in external `impl` In this option, the interface name comes before the type name. ``` struct Song { ... } external impl Comparable for Song { ... } ``` Advantage: - This ordering used by Rust. Disadvantages: - We prefer the type name before the interface name (using `as`), since having the type first and outer is consistent with those implemented in `struct` declarations. It also seems more natural to express the parameters to the interface in terms of the parameters and associated items of the type than the other way around. - The `Song as Comparable` phrase is the name of the facet type that is being implemented. #### No `as` for inline `impl` ``` struct Song { // data and methods ... impl Printable { method (me: Self) Print() { ... } } } ``` Advantage: - More concise, so less to read and write. Disadvantage: - Less consistent with the `external impl` syntax. - Less consistent with the planned inline conditional impl syntax. #### No `external` for external `impl` ``` struct Song { ... } impl Song as Comparable { ... } ``` Advantage: - More concise, so less to read and write. Disadvantages: - Less explicit that the methods of this impl definition are not contributing to unqualified API of the type. - This kind of implementation is naturally referred to as "external", especially when contrasting with "inline impl". #### Out-of-line impl We considered an out-of-line syntax for declaring and defining interface `impl` blocks, to be consistent with the `external impl` declarations. For example: ``` struct Song { ... } impl Printable for Song { ... } external impl Comparable for Song { ... } ``` The main advantage of this syntax was that it was uniform across many cases, including [conditional conformance](/docs/design/generics/details.md#conditional-conformance). It wasn't ideal across a number of dimensions though. - It repeated the type name which was redundant and verbose - It could affect the API of the type outside of the type definition. #### `extend` blocks Instead of the `external impl` statement, we considered putting all external implementations in an `expand` block. ``` struct Song { impl Printable { ... } } expand Song { impl Comparable { ... } } ``` Advantages: - This option is most similar to the [approach used by Swift](https://docs.swift.org/swift-book/LanguageGuide/Protocols.html#ID277). - Easier to copy-paste an `impl` between a `struct` definition and an `expand` block. The `expand` approach had some disadvantages: - Implementations were indented more than the `external impl` approach. - Extra ceremony in the case of only implementing one type for an interface. This case is expected to be common since external implementations will most often be defined with the interface. - When implementing multiple interfaces in a single `expand` block, the name of the type being expanded could be far from the `impl` declaration and hard to find. We originally used `extend` instead of `expand` but that collided with using `extends` for interface extension and derived classes. ### Others Other alternatives considered will be in a future proposal. Some of them can be seen in a rough form in [#36](https://github.com/carbon-language/carbon-lang/pull/36). ================================================ FILE: proposals/p0555/figures.py ================================================ #! /usr/bin/env python __copyright__ = """ Part of the Carbon Language project, under the Apache License v2.0 with LLVM Exceptions. See /LICENSE for license information. SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception """ fmt = "svg" def escape(s): return ( s.replace("&", "&") .replace("<", "<") .replace(">", ">") .replace("[", "[") .replace("]", "]") ) def tablejoin(items, separator): data = ("%s" % separator).join( "%s" % item for item in items ) return '%s
' % data def code(s): # FIXME: GraphViz's handling of font metrics appears to be pretty broken. # Add a little extra width to each character with a non-code-font space to # compensate. codefont = "".join( ( '%s ' ) % escape(part) for part in s ) return ( '
%s
' % codefont ) def math(s): # Render math in italics but otherwise unchanged. return "%s" % s def raw(s): return s LtR = ' shape="rarrow"' RtL = ' shape="larrow"' NonAssoc = "" out = None num = 0 def group(ops, assoc=NonAssoc, style=code): global num num = num + 1 name = "op%d" % num print( " %s [label=<%s>%s]" % ( name, tablejoin((style(op) for op in ops), ", "), assoc, ), file=out, ) return name def edge(a, b): print(" %s -> %s" % (a, b), file=out) def combine(name, items): if len(items) <= 1: return items print(" %s [label=<%s> shape=ellipse]" % (name, name), file=out) res = name for i in items: edge(i, name) return [res] def graph(f): import subprocess outfile = open(f.__name__ + "." + fmt, "w") process = subprocess.Popen( ["dot", "-T" + fmt], stdin=subprocess.PIPE, stdout=outfile, encoding="utf8", # ["cat"], stdin=subprocess.PIPE, stdout=outfile, encoding='utf8' ) global out out = process.stdin # print >>out, ' node [shape="rectangle" style="rounded" fontname="Arial"]' print( """ digraph { layout = dot rankdir = TB rank = "min" node [shape="none" fontsize="12" height="0" fontname="BlinkMacSystemFont,Segoe UI,Helvetica,Arial,sans-serif"] edge [dir="none"] """.strip(), file=out, ) f() print("}", file=out) process.communicate() return f @graph def example(): term = group(["(...)"], NonAssoc) mul = group(["a * b"], LtR) add = group(["a + b"], LtR) shl = group(["a << b"], NonAssoc) compare = group(["a == b"], NonAssoc) edge(term, mul) edge(mul, add) edge(term, shl) edge(add, compare) edge(shl, compare) ================================================ FILE: proposals/p0555/yacc-parser/Makefile ================================================ # Part of the Carbon Language project, under the Apache License v2.0 with LLVM # Exceptions. See /LICENSE for license information. # SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception example: example.l example.y flex example.l bison example.y --defines clang example.tab.c lex.yy.c -o example clean: rm -f example.tab.c example.tab.h lex.yy.c example ================================================ FILE: proposals/p0555/yacc-parser/example.l ================================================ /* Part of the Carbon Language project, under the Apache License v2.0 with LLVM Exceptions. See /LICENSE for license information. SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ %option noyywrap %{ #include #define YY_DECL int yylex() #include "example.tab.h" %} %% [0-9]+ { yylval = atoi(yytext); return INT; } "*" { return '*'; } "+" { return '+'; } "<<" { return LSH; } "==" { return EQEQ; } "(" { return '('; } ")" { return ')'; } ";" { return ';'; } %% ================================================ FILE: proposals/p0555/yacc-parser/example.y ================================================ /* Part of the Carbon Language project, under the Apache License v2.0 with LLVM Exceptions. See /LICENSE for license information. SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ %{ #include extern int yylex(); extern int yyparse(); extern FILE* yyin; void yyerror(const char *s) { fprintf(stderr, "%s\n", s); } int main() { while (yyparse()) {} } %} %define api.value.type {int} %token INT LSH EQEQ %% interpreter: %empty | interpreter expression ';' { printf("%d\n", $2); } expression: compare_expression | compare_operand; compare_expression: compare_lhs EQEQ compare_operand { $$ = ($1 == $3); }; compare_lhs: compare_expression | compare_operand; compare_operand: add_expression | multiply_expression | shift_expression | primary_expression; add_expression: add_lhs '+' add_operand { $$ = ($1 + $3); }; add_lhs: add_expression | add_operand; add_operand: multiply_expression | multiply_operand; multiply_expression: multiply_lhs '*' multiply_operand { $$ = ($1 * $3); }; multiply_lhs: multiply_expression | multiply_operand; multiply_operand: primary_expression; shift_expression: shift_lhs LSH shift_operand { $$ = ($1 << $3); }; shift_lhs: shift_expression | shift_operand; shift_operand: primary_expression; primary_expression: INT | '(' expression ')' { $$ = $2; }; %% ================================================ FILE: proposals/p0555.md ================================================ # Operator precedence [Pull request](https://github.com/carbon-language/carbon-lang/pull/555) ## Table of contents - [Problem](#problem) - [Background](#background) - [Proposal](#proposal) - [Details](#details) - [Notational convention](#notational-convention) - [When to add precedence edges](#when-to-add-precedence-edges) - [Parsing with a partial precedence order](#parsing-with-a-partial-precedence-order) - [Rationale based on Carbon's goals](#rationale-based-on-carbons-goals) - [Alternatives considered](#alternatives-considered) - [Total order](#total-order) - [Different precedence for different operands](#different-precedence-for-different-operands) - [Require less than a partial order](#require-less-than-a-partial-order) ## Problem Most expression-oriented languages use a strict hierarchy of precedence levels. That approach is error-prone, as it assigns meaning to programs that developers may either not understand or may misunderstand. ## Background Given an expression, we need to be able to infer its structure: what are the operands of each of the operators? This may be ambiguous in the absence of rules that determine which operator is preferred, such as in the expression `a $ b ^ c`: is this `(a $ b) ^ c` or `a $ (b ^ c)`? Starting with a sequence of operators and non-operator terms, we can completely determine the structure of an expression by determining which operator in our sequence will be the root of the parse tree, splitting the expression at that point, and recursively determining the structure of each subexpression. The operator that forms the root of the parse tree is said to have the lowest precedence in the expression. Traditionally, this is accomplished by assigning a precedence level to each operator and devising a total ordering over precedence levels. For example, we could assign a higher precedence level to an infix `*` operator than an infix `+` operator. With that choice of precedence levels, an infix `*` operator would bind tighter than an infix `+` operator, regardless of the order in which they appear. This approach is well-understood, but is problematic. For example, in C++, expressions such as `a & b << c * 3` are valid, but the meaning of such an expression is unlikely to be readily apparent to many developers. Worse, for cases such as `a & 3 == 3`, there is a clear intended meaning, namely `(a & 3) == 3`, but the actual meaning is something else -- in this case, `a & (3 == 3)`. Because the precedence rules are not widely known and are sometimes quite surprising, parentheses are used as a matter of course for certain kinds of C++ expressions. However, the absence of such parentheses is not diagnosed in all cases, even by many linting tools, and forgetting those parentheses can lead to subtle bugs. ## Proposal Do not have a total ordering of precedence levels. Instead, define a partial ordering of precedence levels. Expressions using operators that lack relative orderings must be disambiguated by the developer, for example by adding parentheses; when a program's meaning depends on an undefined relative ordering of two operators, it will be rejected due to ambiguity. The default behavior for any new operator is for it to be unordered with respect to all other operators, thereby requiring parentheses when combining that operator with any other operator. Precedence rules should be added only if it is reasonable to expect most or all developers who regularly use Carbon to reliably remember the precedence rule. ## Details ### Notational convention For pedagogical purposes, our documentation will use [Hasse diagrams](https://en.wikipedia.org/wiki/Hasse_diagram) to represent operator precedence partial orders, where operators with lower precedence are considered less than (and therefore depicted lower than and connected to) operators with higher precedence. In our diagrams, an enclosing arrow will be used to show associativity within precedence groups, if there is any, with a left-to-right arrow meaning a left-associative operator. For example:
Example operator precedence diagram
... would depict a higher-precedence `*` operator and a lower-precedence `+` operator, both of which are left-associative, and a non-associative `<<` operator. The `==` operator is lower precedence than all of those operators, and parentheses are higher precedence than all of those operators. With those precedence rules: - `a + b * c` would be parsed as `a + (b * c)`, because `+` has lower precedence than `*`. - `a + b << c` would be an error, requiring parentheses, because the precedence levels of `+` and `<<` are unordered. A [python script](p0555/figures.py) to generate these diagrams is included with this proposal. ### When to add precedence edges Given a program whose meaning is ambiguous to a reader, it is preferable to reject the program rather than to arbitrarily pick a meaning. For Carbon's operators, we should only add an ordering between two operators if there is a logical reason for that ordering, not merely to provide _some_ answer. **Goal: for every combination of operators, either it should be reasonable to expect most or all developers who regularly use Carbon to reliably remember the precedence, or there should not be a precedence rule.** As an example, consider the expression `a * b ^ c`, where `*` is assumed to be a multiplication operator and `^` is assumed to be a bitwise XOR operation. We should reject this expression because there is no logical reason to perform either operator first and it would be unreasonable to expect Carbon developers to remember an arbitrary tie-breaker between the two options. This still leaves open the question of how high a bar of knowledge we put on our developers (what is reasonable for us to expect?). We can use experience from C++ to direct this decision: just as many developers who regularly use C++ do not remember the relative precedence of `&&` vs `||`, and `&` vs `|`, and `&` vs `<<`, and so on, we shouldn't expect them to remember similar precedence rules in Carbon. If we are in doubt, omitting a precedence rule and waiting for real-world experience should be preferred. ### Parsing with a partial precedence order A traditional, totally-ordered precedence scheme can be implemented by an [operator precedence parser](https://en.wikipedia.org/wiki/Operator-precedence_parser): - Keep track of the current left-hand-side operand and an ambient precedence level. The ambient precedence level is the precedence of the operator whose operand is being parsed, or a placeholder "lowest" precedence level when parsing an expression that is not the operand of an operator. - When a new operator is encountered, its precedence is compared to the ambient precedence level: - If its precedence is higher than the ambient precedence level, then recurse ("shift") with that as the new ambient precedence level to form the right-hand side of the new operator. After forming the right-hand side, build an operator expression from the current left-hand side operand and the right-hand side operand; that is the new current left-hand side. - If its precedence is equal to the ambient precedence level, then use the associativity of that precedence level to determine what to do: - If the operator is left-associative, build an operator expression. - If the operator is right-associative, recurse. - If the operator is non-associative, produce an error. - If its precedence is lower than the ambient precedence level, return the expression formed so far; it's the complete operand to an earlier operator. This is, for example, the strategy [currently used in Clang](https://github.com/llvm/llvm-project/blob/5f0903e9bec97e67bf34d887bcbe9d05790de934/clang/lib/Parse/ParseExpr.cpp#L396). The above algorithm is only suited to parsing in the case where precedence levels are totally ordered, because it does not say what to do if the new precedence is not comparable with the ambient precedence. However, the algorithm can easily be adapted to also parse with a partial precedence order by adding one more case: - If the precedence level of the new operator is not comparable with the ambient precedence level, produce an ambiguity error. The key observation here is that, if we ever see `... a * b ^ c ...`, where `*` and `^` have incomparable precedence, no later tokens can ever resolve the ambiguity, so we can diagnose it immediately. Sketch proof: If there were a valid parse tree for this expression, one of `*` and `^` must end up as an ancestor of the other. But in a valid parse tree, along the path from one operator to the other, precedences monotonically increase, so by transitivity of the precedence partial ordering, the ancestor operator has lower precedence than the descendent operator. An operator precedence parser with a partial ordering of precedence levels [has been implemented](https://github.com/carbon-language/carbon-lang/commit/b8afadb3c6af5e68192d585232fee759180ea1e3) as a proof-of-concept in the Carbon toolchain. Operator precedence partial ordering can also be implemented in yacc / bison parser generators by using a variant of the [precedence climbing method](https://en.wikipedia.org/wiki/Operator-precedence_parser#Precedence_climbing_method). For example, here is a yacc grammar for the Hasse diagram shown above: ``` expression: compare_expression | compare_operand; compare_expression: compare_lhs EQEQ compare_operand { $$ = ($1 == $3); }; compare_lhs: compare_expression | compare_operand; compare_operand: add_expression | multiply_expression | shift_expression | primary_expression; add_expression: add_lhs '+' add_operand { $$ = ($1 + $3); }; add_lhs: add_expression | add_operand; add_operand: multiply_expression | multiply_operand; multiply_expression: multiply_lhs '*' multiply_operand { $$ = ($1 * $3); }; multiply_lhs: multiply_expression | multiply_operand; multiply_operand: primary_expression; shift_expression: shift_lhs LSH shift_operand { $$ = ($1 << $3); }; shift_lhs: shift_expression | shift_operand; shift_operand: primary_expression; primary_expression: INT | '(' expression ')' { $$ = $2; }; ``` Note that some care must be taken to avoid grammar ambiguities. Under the precedence climbing method, a `primary_expression` would be a `shift_expression`, a `multiply_expression`, and an `add_expression`, and therefore interpreting a `primary_expression` as an `expression` would be ambiguous: we could take either the `shift_expression` path or the `multiply_expression` path through the grammar. The above formulation avoids this ambiguity by excluding `primary_expression` from `add_expression` and `shift_expression`, and instead listing it as a distinct production for `compare_operand`. A yacc grammar such as the above can be produced systematically for any precedence partial ordering. A complete example of a yacc parser with operator precedence partial ordering is available [alongside this proposal](p0555/yacc-parser). ## Rationale based on Carbon's goals - Software and language evolution - The advice to not supply an operator precedence relationship if in doubt is based on the idea that it's easier to add a precedence rule as an evolutionary step than to remove one. - Code that is easy to read, understand, and write - This proposal aims to support this goal by ensuring that the operator expressions that are used in programs are readily understood by practitioners, by making unreadable constructs invalid. ## Alternatives considered ### Total order We could provide a total order over operator precedence. This proposal is not strictly in conflict with doing so, if every ordering relationship is justified, but in practice we expect there to be pairs of operators for which there is no obvious precedence relationship. For: - This is established practice across most languages. Against: - This practice is a common source of bugs in the case where an arbitrary or bad choice is made. ### Different precedence for different operands We could provide different precedence relationships for the left and right sides of infix operators. For example, we could allow multiplication on the left of a `<<` operator but not on the right. This is precedented in C++: the `?` in a `?:` allows a comma operator on its right but not on its left. For: - This may allow some additional cases that would be clear and unsurprising. Against: - The resulting rules would be more challenging to learn, and it seems likely that they would fail the test that most developers who regularly use Carbon know the rules. This proposal is not incompatible with adopting such a direction in future if we find motivation to do so. ### Require less than a partial order We could require something weaker than a partial ordering of precedence levels. This proposal assumes the following two points are useful for human comprehension of operator precedence: - The lowest-precedence operator does not depend on the relative order of operators in the expression (except as a tie-breaker when there are multiple operators with the same precedence, where the associativity of the operator is considered). - If an `^` expression can appear indirectly (but unparenthesized) within an `$` expression, then an `^` expression can appear directly within an `$` expression. - If the lowest-precedence operator in `a $ b ^ c` is `$`, and the lowest-precedence operator in `b ^ c # d` is `^`, then the lowest-precedence operator in `a $ b ^ c # d` is `$`. These assumptions lead to the conclusion that operator precedence should form a partial order over equivalence classes of operators. However, these assumptions could be wrong. If we find motivation to select rules that violate the above assumptions, we should reconsider the approach of using a partial precedence ordering, but no motivating case is currently known. ================================================ FILE: proposals/p0561.md ================================================ # Basic classes: use cases, struct literals, struct types, and future work [Pull request](https://github.com/carbon-language/carbon-lang/pull/561) ## Table of contents - [Problem](#problem) - [Background](#background) - [Proposal](#proposal) - [Rationale based on Carbon's goals](#rationale-based-on-carbons-goals) - [Alternatives considered](#alternatives-considered) - [Earlier proposal](#earlier-proposal) - [Interfaces implemented for anonymous data classes](#interfaces-implemented-for-anonymous-data-classes) - [Access control](#access-control) - [Introducer for structural data class types](#introducer-for-structural-data-class-types) - [Terminology](#terminology) ## Problem We need to say how you define new types in Carbon. This proposal is specifically about [record types](). The proposal is not intended to be a complete story for record types, but enough to get agreement on direction. It primarily focuses on: - use cases including: data classes, encapsulated types with virtual and non-virtual methods and optional single inheritance, interfaces as base classes that support multiple inheritance, and mixins for code reuse; - anonymous structural data types for record literals used to initialize class values and ad-hoc parameter and return types with named components; and - future work, including the provisional syntax in use for features that have not been decided. ## Background This is a replacement for earlier proposal [#98](https://github.com/carbon-language/carbon-lang/pull/98). ## Proposal This proposal adds an initial design for record types called "classes", including _structural data classes_ called _struct types_ as well as struct literals. The design is replacing the skeletal design for what were called "struct" types with a [new document on classes](/docs/design/classes.md). ## Rationale based on Carbon's goals This particular proposal is focusing on [the Carbon goal](/docs/project/goals.md#code-that-is-easy-to-read-understand-and-write) that code is "easy to read, understand, and write." Future proposals will address other aspects of the class type design such as performance. ## Alternatives considered ### Earlier proposal There was an earlier proposal [#98](https://github.com/carbon-language/carbon-lang/pull/98), that made a number of different choices, including: - Tuples were given named components instead of having separate struct literals. - No form of multiple inheritance was proposed. - Operators were define using methods like C++ instead of by implementing interfaces like Rust. - Constructors had a special form like C++ instead of being regular functions like Rust. - Tuples and classes were both considered example of record types. - Members of classes could be individually left uninitialized. - Coverage of nominal types, inheritance, etc. were considered in much more detail. ### Interfaces implemented for anonymous data classes Whether we would support implementing interfaces for specific anonymous data classes was [discussed on Discord](https://discord.com/channels/655572317891461132/709488742942900284/867471671089561643). [The conclusion](https://discord.com/channels/655572317891461132/709488742942900284/867516894029938710) was "yes", reasoning that we would support that for the same reason as a number of other cases such as tuple and pointer types. [A specific use case](https://discord.com/channels/655572317891461132/709488742942900284/867517209026756630) would be implementing interface ``` interface ConstructWidgetFrom { fn Construct(Self) -> Widget; } ``` for type `{.kind: WidgetKind, .size: Int}`. ### Access control [Issue #665](https://github.com/carbon-language/carbon-lang/issues/665) decided that by default members of a class would be publicly accessible. There were a few reasons: - The readability of public members is the most important, since we expect most readers to be concerned with the public API of a type. - The members that are most commonly private are the data fields, which have relatively less complicated definitions that suffer less from the extra annotation. Additionally, there is precedent for this approach in modern object-oriented languages such as [Kotlin](https://kotlinlang.org/docs/visibility-modifiers.html) and [Python](https://docs.python.org/3/tutorial/classes.html), both of which are well regarded for their usability. It further decided that members would be given more restricted access using a local annotation on the declaration itself rather than a block or region approach such as used in C++. This is primarily motivated by a desire to reduce context sensitivity, following [the principle](/docs/project/principles/low_context_sensitivity.md) introduced in [#646](https://github.com/carbon-language/carbon-lang/pull/646). It helps readers to more easily determine the accessibility of a member in large classes, say when they have jumped to a specific definition in their IDE. ### Introducer for structural data class types [Issue #653](https://github.com/carbon-language/carbon-lang/issues/653) discussed whether structural data class types should have an introducer to distinguish them from structural data class literals. Ultimately we decided no introducer was needed: - Outside of `{}`, types could be distinguished from literal values by the presence of a `:` after the first field name. - This creates a sort of consistency: introducers are frequently used when introducing new names, as in `fn`, `var`, `interface`, and so on. Struct type declarations don't introduce new names so they don't require an introducer. - It avoids having a different introducer for things that are still treated as classes for many purposes. This means we won't have to frequently say "struct or class" in documentation. - We do want to use these type expressions in contexts that will benefit from being more concise, such as inside function and variable declarations. This does cause an issue that `{}` is both an empty struct literal and empty struct type. However, we've already accepted that complexity with tuples, so this choice is more consistent. If we find that we need an introducer for tuple types to distinguish the empty tuple from its type, we expect to find that we have the same problem with empty struct literals, and the other way around. We are explicitly to choosing to accept the risk that this won't work out in order to have a more concise syntax in case it does. ### Terminology Do literals have "class" type or are they some other kind of type? [Issue #651](https://github.com/carbon-language/carbon-lang/issues/651) decided that all of these types were different kinds of classes: - Literals like `{.a = 2}` are "structural data class literals" or "struct literals" for short. Here "structural" means that two types are considered equal if their fields match. They are not "nominal" since they don't have a name to use for type equality. - The types of those literals like `{.a: i64}` would be "structural data classes" or "struct types" for short. - There would also be "nominal data classes" that are declared with a syntax more similar to other nominal classes. We preferred to refer to all of these as class types, rather than have to frequently refer to "struct or class types", adding additional words to name more specific subsets, like "data classes". In contrast, tuple types are not considered classes, but classes and tuples together form _product types_. The term "class" was chosen over "struct" since they generally support object-oriented features like encapsulation, inheritance, and dynamic dispatch, and how C++ programmers generally refer to their record types. These were considered more significant than the C++ distinction that classes default to private access control. Since we plan to use a different syntax in Carbon to specify access restrictions, the different default seemed straightforward to teach. ================================================ FILE: proposals/p0601.md ================================================ # Operator tokens [Pull request](https://github.com/carbon-language/carbon-lang/pull/601) ## Table of contents - [Problem](#problem) - [Background](#background) - [Proposal](#proposal) - [Details](#details) - [Two kinds of operator tokens](#two-kinds-of-operator-tokens) - [Symbolic token list](#symbolic-token-list) - [Whitespace](#whitespace) - [Rationale based on Carbon's goals](#rationale-based-on-carbons-goals) - [Alternatives considered](#alternatives-considered) ## Problem Carbon needs a set of tokens to represent operators. ## Background Some languages have a fixed set of operator tokens. For example: - [C++ operators](https://eel.is/c++draft/lex.operators) - The keyword operators `and`, `or`, etc. are lexical synonyms for corresponding symbolic operators `&&`, `||`, etc. - [Rust operators](https://doc.rust-lang.org/book/appendix-02-operators.html) Other languages have extensible rules for defining operators, including the facility for a developer to define operators that aren't part of the base language. For example: - [Swift operator rules](https://docs.swift.org/swift-book/ReferenceManual/LexicalStructure.html#ID418) - [Haskell operator rules](https://www.haskell.org/onlinereport/haskell2010/haskellch2.html#dx7-18008) Operators tokens can be formed by various rules, for example: - At each lexing step, form the longest known operator token possible from the remaining character sequence. For example, in C++, `a += b` is 3 tokens and `a =+ b` is four tokens, because there are `+`, `=`, and `+=` operators, but there is no `=+` operator. This approach is sometimes known as "max munch". - At each lexing step, treat the longest sequence of operator-like characters possible as an operator. The program is invalid if there is no such operator. For example, in a C++-like language using this approach, `a =+ b` would be invalid instead of meaning `a = (+b)`. - Use semantic information to determine how to split a sequence of operator characters into one or more operators, for example based on the types of the operands. ## Proposal Carbon has a fixed set of tokens that represent operators, defined by the language specification. Developers cannot define new tokens to represent new operators; there may be facilities to overload operators, but that is outside the scope of this proposal. There are two kinds of tokens that represent operators: - _Symbolic tokens_ consist of one or more symbol characters. In particular, such a token contains no characters that are valid in identifiers, no quote characters, and no whitespace. - _Keywords_ follow the lexical rules for words. Symbolic tokens are lexed using a "max munch" rule: at each lexing step, the longest symbolic token defined by the language specification that appears starting at the current input position is lexed, if any. Not all uses of symbolic tokens within the Carbon grammar will be as operators. For example, we will have `(` and `)` tokens that serve to delimit various grammar productions, and we may not want to consider `.` to be an operator, because its right "operand" is not an expression. When a symbolic token is used as an operator, we use the presence or absence of whitespace around the symbolic token to determine its fixity, in the same way we expect a human reader to recognize them. For example, we want `a* - 4` to treat the `*` as a unary operator and the `-` as a binary operator, while `a * -4` results in the reverse. This largely requires whitespace on only one side of a unary operator and on both sides of a binary operator. However, we'd also like to support binary operators where a lack of whitespace reflects precedence such as`2*x*x + 3*x + 1` where doing so is straightforward. The rules we use to achieve this are: - There can be no whitespace between a unary operator and its operand. - The whitespace around a binary operator must be consistent: either there is whitespace on both sides or on neither side. - If there is whitespace on neither side of a binary operator, the token before the operator must be an identifier, a literal, or any kind of closing bracket (for example, `)`, `]`, or `}`), and the token after the operator must be an identifier, a literal, or any kind of opening bracket (for example, `(`, `[`, or `{`). This proposal includes an initial set of symbolic tokens covering only the grammar productions that have been approved so far. This list should be extended by proposals that use additional symbolic tokens. ## Details ### Two kinds of operator tokens Two kinds of operator tokens are proposed. These two kinds are intended for different uses, not as alternate spellings of the same functionality: - Symbolic tokens are intended to be used for widely-recognized operators, such as the mathematical operators `+`, `*`, `<`, and so on. - Symbolic tokens used as operators would generally be expected to also be meaningful for some user-defined types, and should be candidates for being made overloadable once we support operator overloading. - Keywords are intended to be used for cases such as the following: - Operators that perform flow control, such as `and`, `or`, `throw`, `yield`, and operators closely connected to these, such as `not`. It is important that these stand out from other operators as they have action that goes beyond evaluating their operands and computing a value. - Operators that are rare and that we do not want to spend our finite symbolic token budget on, such as perhaps xor or bit rotate. - Operators with very low precedence, and perhaps certain operators with very high precedence. - Special-purpose operators for which there is no conventional established symbol and for which we do not want to invent one, such as `as`. The example operators in this section are included only to motivate the two kinds of operator token; those specific operators are not proposed as part of this proposal. ### Symbolic token list The following is the initial list of symbolic tokens recognized in a Carbon source file: | | | | | | | | --- | ---- | ---- | --- | --- | --- | | `(` | `)` | `{` | `}` | `[` | `]` | | `,` | `.` | `;` | `:` | `*` | `&` | | `=` | `->` | `=>` | | | | This list is expected to grow over time as more symbolic tokens are required by language proposals. ### Whitespace We wish to support the use of the same symbolic token as a prefix operator, an infix operator, and a postfix operator, in some cases. In particular, we have [decided in #523](https://github.com/carbon-language/carbon-lang/issues/523) that the `*` operator should support all three uses; this operator will be introduced in a future proposal. In order to support such usage, we want a rule that allows us to simply and unambiguously parse operators that might have all three fixities. For example, given the expression `a * - b`, there are two possible parses: - As `a * (- b)`, multiplying `a` by the negation of `b`. - As `(a *) - b`, subtracting `b` from the pointer type `a *`. Our chosen rule to distinguish such cases is to consider the presence or absence of whitespace, as we think this strikes a good balance between simplicity and expressiveness for the programmer and simplicity and good support for error recovery in the implementation. `a * -b` uses the first interpretation, `a* - b` uses the second interpretation, and other combinations (`a*-b`, `a *- b`, `a* -b`, `a * - b`, `a*- b`, `a *-b`) are rejected as errors. In general, we require whitespace to be present or absent around the operator to indicate its fixity, as this is a cue that a human reader would use to understand the code: binary operators have whitespace on both sides, and unary operators lack whitespace between the operator and its operand. We also make allowance for omitting the whitespace around a binary operator in cases where it aids readability to do so, such as in expressions like `2*x*x + 3*x + 1`: for an operator with whitespace on neither side, if the token immediately before the operator indicates it is the end of an operand, and the token immediately after the operator indicates it is the beginning of an operand, the operator is treated as binary. We define the set of tokens that constitutes the beginning or end of an operand as: - Identifiers, as in `x*x + y*y`. - Literals, as in `3*x + 4*y` or `"foo"+s`. - Brackets of any kind, facing away from the operator, as in `f()*(n + 3)` or `args[3]*{.real=4, .imag=1}`. For error recovery purposes, this rule functions best if no expression context can be preceded by a token that looks like the end of an operand and no expression context can be followed by a token that looks like the start of an operand. One known exception to this is in function definitions: ``` fn F(p: Int *) -> Int * { return p; } ``` Both occurrences of `Int *` here are erroneous. The first is easy to detect and diagnose, but the second is more challenging, if `{...}` is a valid expression form. We expect to be able to easily distinguish between code blocks starting with `{` and expressions starting with `{` for all cases other than `{}`. However, the code block `{}` is not a reasonable body for a function with a return type, so we expect errors involving a combination of misplaced whitespace and `{}` to be rare, and we should be able to recover well from the remaining cases. From the perspective of token formation, the whitespace rule means that there are four _variants_ of each symbolic token: - A symbolic token with whitespace on both sides is a _binary_ variant of the token. - A symbolic token with whitespace on neither side, where the preceding token is an identifier, literal, or closing bracket, and the following token is an identifier, literal, or `(`, is also a _binary_ variant of the token. - A symbolic token with whitespace on neither side that does not satisfy the preceding rule is a _unary_ variant of the token. - A symbolic token with whitespace on the left side only is a _prefix_ variant of the token. - A symbolic token with whitespace on the right side only is a _postfix_ variant of the token. When used in non-operator contexts, any variant of a symbolic token is acceptable. When used in operator contexts, only a binary variant of a token can be used as a binary operator, only a prefix or unary variant of a token can be used as a prefix operator, and only a postfix or unary variant of a token can be used as a postfix operator. This whitespace rule has been [implemented in the Carbon toolchain](https://github.com/carbon-language/carbon-lang/pull/576) for all operators by tracking the presence or absence of trailing whitespace as part of a token, and [in executable semantics](https://github.com/carbon-language/carbon-lang/commit/04d3a885ae01a779aadb19f51ec7a5a12ffe295c) for the `*` operator by forming four different token variants as described above. The choice to disallow whitespace between a unary operator and its operand is _experimental_. ## Rationale based on Carbon's goals - Software and language evolution - By not allowing user-defined operators, we reduce the possibility that operators added to the language later will conflict with existing uses in programs. Due to the use of a max munch rule, we might add an operator that causes existing code to be interpreted differently, but such problems will be easy to detect and resolve, because we know the operator set in advance. - Code that is easy to read, understand, and write - The fixed operator set means that developers don't need to understand an unbounded and extensible number of operators and precedence rules. The fixed operator set encourages functionality that does not correspond to a well-known operator symbol to be exposed by way of a named operation instead of a symbol, improving readability among developers not familiar with a codebase. - Requiring whitespace to be used consistently around operators reduces the possibility for confusing formatting. - Permitting whitespace on either both sides of a binary operator or on neither side allows expressions such as `2*x*x + 3*x + 1` to use the absence of whitespace to improve readability. Because the language officially sanctions both choices, the formatting tool can be expected to preserve the user's choice. - The choice to lex the longest known symbolic token rather than the longest sequence of symbolic characters makes it easier to write expressions involving a series of prefix or postfix operators, such as `x = -*p;`. - Interoperability with and migration from existing C++ code - The fixed operator set makes a mapping between Carbon operators and C++ operators easier, by avoiding any desire to map arbitrary user-defined Carbon operators into a C++ form. - The choice of a fixed operator set and a "max munch" rule will be familiar to C++ developers, as it is the same approach taken by C++. - The whitespace rule permits the `*` operator to be used for all of multiplication, dereference, and pointer type formation, as in C++, while still permitting Carbon to treat type expressions as expressions. ## Alternatives considered We could lex the longest sequence of symbolic characters rather than lexing only the longest known operator. Advantages: - Adding new operators could be done without any change to the lexing rules. - If unknown operators are rejected, adding new operators would carry no risk of changing the meaning of existing valid code. Disadvantages: - Sequences of prefix or postfix operators would require parentheses or whitespace. For example, `Int**` would lex as `Int` followed by a single `**` token, and `**p` would lex as a single `**` token followed by `p`, if there is no `**` operator. While we could define `**`, `***`, and so on as operators, doing so would add complexity and inconsistency to the language rules. We could support an extensible operator set, giving the developer the option to add new operators. Advantages: - This would increase expressivity, especially for embedded domain-specific languages. Disadvantages: - This would harm readability, at least for those unfamiliar with the code using the operators. - This could harm our ability to evolve the language, by admitting the possibility of a custom operator colliding with a newly-introduced standard operator, although this risk could be reduced by providing a separate lexical syntax for custom operators. - We would need to either lex the longest sequence of symbolic characters we can, which has the same disadvantage discussed for that approach above, or use a more sophisticated rule to determine how to split operators -- perhaps based on what operator overloads are in scope -- increasing complexity. We could apply different whitespace restrictions or no whitespace restrictions. See [#520](https://github.com/carbon-language/carbon-lang/issues/520) for discussion of the alternatives and the leads decision. We could require whitespace around a binary operator followed by `[` or `{`. In particular, for examples such as: ``` fn F() -> Int*{ return Null; } var n: Int = pointer_to_array^[i]; ``` ... this would allow us to form a unary operator instead of a binary operator, which is likely to be more in line with the developer's expectations. Advantages: - Room to add a postfix `^` dereference operator, or similarly any other postfix operator producing an array, without creating surprises for pointers to arrays. - Allows the whitespace before the `{` of a function body to be consistently omitted if desired. Disadvantages: - The rule would be more complex, and would be asymmetric: we must allow closing square brackets before unspaced binary operators to permit things like `arr[i]*3`. - Would interact badly with expression forms that begin with a `[` or `{`, for example `Time.Now()+{.seconds = 3}` or `names+["Lrrr"]`. ================================================ FILE: proposals/p0618.md ================================================ # var ordering [Pull request](https://github.com/carbon-language/carbon-lang/pull/618) ## Table of contents - [Problem](#problem) - [Background](#background) - [Proposal](#proposal) - [Rationale based on Carbon's goals](#rationale-based-on-carbons-goals) - [Alternatives considered](#alternatives-considered) - [Type ordering](#type-ordering) - [`: `](#type-name) - [` `](#type-name-1) - [`: `](#name-type) - [`:` versus `in`](#-versus-in) ## Problem As stated by on [Re-evaluate core variable & parameter identifier/type order (including a default for parameters) #542](https://github.com/carbon-language/carbon-lang/issues/542): > Somewhat condensed, bullet-point-y background for this question: > > - We've been using first `Type: variable` and then `Type variable` syntax in > variables, parameters, and other declarations. > - This was primarily based on a _lack of compelling data_ to select a better > syntax, with trying to stay similar to C++ as a fallback. > - It was _specifically_ intended to be revisited. The expected trigger for > this was some form of broader user data (surveys at least of decent #s of > developers, or potentially real user studies). > - However, we have gained specific new information as we've filled out a > great deal of the surrounding syntax. We have also gotten some data on > parsing challenges (although perhaps surmountable challenges) of > `Type variable`. > - We also don't have a short/definite timeline to start getting useful data. > - The leads should re-evaluate the core variable syntax based on the new > information we have, but _without_ trying to wait for data. > - We can always re-evaluate again if and when data arrives and indicates > any need for it. > - The leads should do this ASAP and make a decision so that we can focus our > energy, reduce frustrating discussions, and have consistent syntax in > examples and proposals. ## Background Background may be found in the related [#542](https://github.com/carbon-language/carbon-lang/issues/542) and [Docs](https://docs.google.com/document/d/1EhZA3AlY9TaCMho9jz2ynFxK-6eS6BwMAkE5jNYQzEA/edit?usp=sharing&resourcekey=0-QXEoh-b4_sQG2u636gIa1A). ## Proposal Two changes: - Switch to `: `, replacing `: `. - Use `in` instead of `:` in range-based for loops. Note these changes were largely implemented by [#563](https://github.com/carbon-language/carbon-lang/pull/563). ## Rationale based on Carbon's goals Both of these changes are done for consistency with other modern languages, particularly Swift and Rust. The switch from `:` to `in` is for ease of understanding and parsing. ## Alternatives considered ### Type ordering Alternatives are pulled from [Docs](https://docs.google.com/document/d/1EhZA3AlY9TaCMho9jz2ynFxK-6eS6BwMAkE5jNYQzEA/edit?usp=sharing&resourcekey=0-QXEoh-b4_sQG2u636gIa1A). #### `: ` `var String: message = "Hello world";` Advantages: - Roughly matches the order of C, C++, C#, D and Java, except with extra `var` and `:`. - Type at the beginning puts most important information up front. - Name followed by default matches assignment statements. Disadvantages: - Existing languages that use a `:` put the name before and the type after ([universally](http://rosettacode.org/wiki/Variables)). - Beyond simple inconsistency, the overlap of `:` in this syntax with different order will add confusion for people working/familiar with multiple languages. - Does not end up having a syntax that is consistent with using colons for marking labelled parameters and arguments, such as how Swift does. - We currently do not plan to use a colon syntax for labelled parameters and arguments, regardless of the decision here. Opinions vary: - Not friendly to optionally dropping the `type:` to represent auto type deduction. #### ` ` `var String message = "Hello world";` Advantages - Matches C, C++, C#, D and Java the closest. Disadvantages: - Creates parse ambiguity, particularly when we start adding syntax to the name to indicate that a parameter is labeled, etc. Currently hard to see how we can make this work, since it isn't compatible with other choices, detailed in [Docs](https://docs.google.com/document/d/1EhZA3AlY9TaCMho9jz2ynFxK-6eS6BwMAkE5jNYQzEA/edit?usp=sharing&resourcekey=0-QXEoh-b4_sQG2u636gIa1A). #### `: ` `var message: String = "Hello world";` Advantages: - Matches [Swift](http://rosettacode.org/wiki/Variables#Swift), [Rust](https://doc.rust-lang.org/stable/rust-by-example/primitives.html), [Kotlin](http://rosettacode.org/wiki/Variables#Kotlin), [Python3](https://docs.python.org/3/library/typing.html), and many smaller languages ([Ada](http://rosettacode.org/wiki/Variables#Ada), [Pascal languages like Delphi](http://rosettacode.org/wiki/Variables#Delphi) and [Modula-3](http://rosettacode.org/wiki/Variables#Modula-3), [Eiffel](http://rosettacode.org/wiki/Variables#Eiffel), [Nim](https://nim-lang.org/docs/tut1.html#the-var-statement), [Pony](http://rosettacode.org/wiki/Variables#Pony), [Zig](https://ziglang.org/documentation/0.7.1/#Variables)). - Names will line up better with method names in a `struct` definition. Disadvantages: - Name separated from initializer; default doesn't match assignment statements. - Further from the simplistic appearance of common C and C++ variable declarations. Opinions vary: - Existing languages typically make the "`: type`" part optional when an "`= value`" clause is present. ### `:` versus `in` The `:` operator for range-based for loops becomes harder to read, and more likely to cause ambiguity, when `:` is also used for `var`. That is, `for (var i: Int : list)` is just harder to understand than `for (var i: Int in list)`. `in` is a favorable choice for its use in other languages. ================================================ FILE: proposals/p0623.md ================================================ # Require braces [Pull request](https://github.com/carbon-language/carbon-lang/pull/623) ## Table of contents - [Problem](#problem) - [Background](#background) - [Consistency](#consistency) - [C++ style rules](#c-style-rules) - [`goto fail`](#goto-fail) - [Proposal](#proposal) - [Rationale based on Carbon's goals](#rationale-based-on-carbons-goals) - [Alternatives considered](#alternatives-considered) - [Optional braces](#optional-braces) - [Optional parentheses](#optional-parentheses) - [`elif`](#elif) ## Problem In C++, braces are often optional, such as in: ``` for (Shape x : shapes) Draw(x); ``` Carbon adopted this design choice by default in proposals [#285](/proposals/p0285.md), [#340](/proposals/p0340.md), and [#353](/proposals/p0353.md). But should we keep it? ## Background ### Consistency We were adopting the C++ syntax primarily for easy consistency with C++. For other languages: - Some modern languages require braces, including Go, Rust, and Swift. Note some which require braces make parentheses optional. - Kotlin is an example modern language which does not require braces. - Older languages tend to make braces optional, including Java, JavaScript, and C#. ### C++ style rules There is a [CERT rule](https://wiki.sei.cmu.edu/confluence/display/c/EXP19-C.+Use+braces+for+the+body+of+an+if%2C+for%2C+or+while+statement) for when to use braces. ### `goto fail` Apple had an infamous `goto fail` bug: - https://www.imperialviolet.org/2014/02/22/applebug.html - https://dwheeler.com/essays/apple-goto-fail.html ## Proposal Carbon should require braces when they might otherwise be optional. We should continue to require parentheses. This currently comes up in `if`/`else`, `while`, and `for`. We will allow `else if` as a special structure for repeated `if` statements due to their frequency. For example, `if (...) { ... } else if (...) { ... }` behaves identically to `if (...) { ... } else { if (...) { ... } }`. ## Rationale based on Carbon's goals - **Software and language evolution**: Reducing parsing ambiguity of curly braces is important for expanding the syntactic options for Carbon. - **Code that is easy to read, understand, and write**: We have a preference to give only one way to do things, and optional braces are inconsistent with that. It's also easier to understand and parse code that uses braces, and defends against the possibility of `goto fail`-style bugs, whether accidental or malicious. - **Interoperability with and migration from existing C++ code**: While C++ does make braces optional in related situations, we believe this isn't fundamental to migration or familiarity for C++ developers, so this goal is not meaningfully impacted by this change. ## Alternatives considered ### Optional braces We could instead keep braces optional, such as: ```carbon if (x) return y; ``` Advantages: - Consistent with C++. - Allows for greater brevity in code, such as `if (!success) return error;`. Disadvantages: - Gives two ways of doing the same thing. - Style guides will make choices, creating more contextual coding style. - Some community members opined that requiring braces was the only solution which is both appealing and easy to automate formatting for. - A contrary opinion is that single-line if statements without braces are more appealing, and not significantly more difficult to automate. - More complex and harder to parse. - Nested `if` statements can have unclear `else` bindings. For example: ```carbon if (x) if (y) DoIfY(); else DoIfNotY(); else DoIfNotX(); ``` - If Carbon were to reuse `if` and `else` keywords for a ternary operator, that could omit braces in order to avoid ambiguity. For example, `int x = if y then 3 else 7;`. - Developers are known to make mistakes adding statements to conditionals missing braces, keeping consistent indentation, and missing the incorrect behavior due to cognitive load. For example: ```carbon if (x) return y; ``` -> ```carbon if (x) print("Returning y"); return y; ``` - Developers commonly want to add debugging statements to conditionals, and missing braces can lead to these kinds of errors. - It's possible that, over time, adding braces to add debug statements then removing the debug statements without removing statements will lead to braces remaining when braces are optional. - This can be addressed by style guides and automated formatting that inserts or removes braces as appropriate, cleaning up after temporary edits. - Some people feel the churn of adding braces and later removing them is a significant toil that can be avoided by always adding them, such as [here](https://wiki.c2.com/?AlwaysUseBracesOnIfThen). - It can more easily lead to bugs like `goto fail;`. - However, as the blog post points out, one can have incorrect indentation with braces too. ### Optional parentheses Languages which make braces required can make parentheses optional, or even disallow them, such as: ```carbon if x { return y; } ``` Advantages: - Exchanges verbosity in syntax, requiring `{}` but removing `()`. - Particularly useful in that many conditionals have `{}` regardless of optionality, but all conditionals could in theory remove `()`. - Cross-language consistency with languages that require `{}`. Disadvantages: - Visual inconsistency with C++. - Parentheses make parsing less likely to encounter ambiguity. - Optional parentheses lead to two ways of doing the same thing, although disallowing them entirely could address this. ### `elif` We could make `else if` a single token, such as `elseif` or `elif`. Advantages: - Can parse as a single token. Disadvantages: - Visual inconsistency with C++. - `else if` also appears in some languages that require braces, such as Go, Rust, and Swift. ================================================ FILE: proposals/p0646.md ================================================ # Low context-sensitivity principle [Pull request](https://github.com/carbon-language/carbon-lang/pull/646) ## Table of contents - [Problem](#problem) - [Background](#background) - [Proposal](#proposal) - [Rationale based on Carbon's goals](#rationale-based-on-carbons-goals) ## Problem Carbon needs a consistent policy on how willing we are to make syntax dependent on context. ## Background Rust has [a statement of their ergonomic principles](https://blog.rust-lang.org/2017/03/02/lang-ergonomics.html) that talks about their approach to context dependence. ## Proposal We propose adding (`docs/project/principles/low_context_sensitivity.md`)[/docs/project/principles/low_context_sensitivity.md], which has the details. ## Rationale based on Carbon's goals This proposal supports the goal of making Carbon code [easy to read, understand, and write](/docs/project/goals.md#code-that-is-easy-to-read-understand-and-write) particularly prioritizing "read and understand" over "write". It supports [software evolution](/docs/project/goals.md#software-and-language-evolution) by allowing code to be moved or refactored with fewer changes. Further it supports developing [performance-critical software](/docs/project/goals.md#performance-critical-software) in Carbon by making performance predictable. ================================================ FILE: proposals/p0676.md ================================================ # `:!` generic syntax [Pull request](https://github.com/carbon-language/carbon-lang/pull/676) ## Table of contents - [Problem](#problem) - [Background](#background) - [Proposal](#proposal) - [Rationale based on Carbon's goals](#rationale-based-on-carbons-goals) - [Alternatives considered](#alternatives-considered) - [Default based on context](#default-based-on-context) - [Square brackets](#square-brackets) - [Other spellings that were considered](#other-spellings-that-were-considered) - [`Template` as a type-of-type](#template-as-a-type-of-type) - [Alternatives not considered](#alternatives-not-considered) ## Problem Carbon design docs provisionally used `:$` to mark generic parameters. Since then, [issue #565](https://github.com/carbon-language/carbon-lang/issues/565) decided to use `:!` more permanently. This proposal is to implement that decision. ## Background Most popular languages put generic parameters inside angle brackets (`<`...`>`), as can be seen on rosettacode.org: [1](http://rosettacode.org/wiki/Generic_swap), [2](http://rosettacode.org/wiki/Constrained_genericity). ## Proposal Generic parameters will be marked using `:!` instead of `:` in the parameter list. They are listed with the regular parameters if they are to be specified explicitly by callers. ``` fn Zero(T:! ConvertFrom(Int)) -> T; var zf: Float32 = Zero(Float32); ``` If they are instead deduced from the (types of) the regular parameters, they are listed in square brackets (`[`...`]`) before the parameter list in round parens (`(`...`)`). ``` fn Swap[T:! Movable](a: T*, b: T*); var i: Int = 1; var j: Int = 2; Swap(&i, &j); ``` Template parameters use both a `template` keyword before the parameter and `:!` in place of `:`. ``` fn FieldNames(template T:! Type) -> String; Assert(FieldNames(struct {.x: Int, .y: Int}) == "x, y"); ``` For both generic and template parameters, the `!` means "compile time." There is some precedent for this meaning; Rust uses `!` to mark macro calls, that is calls that happen at compile time. ## Rationale based on Carbon's goals We are attempting to choose a syntax that advances Carbon's goal of having [code that is easy to read, understand, and write](/docs/project/goals.md#code-that-is-easy-to-read-understand-and-write). This option was chosen since it has the advantage of being very simple and not relying on any context, in accordance with the [#646: low-context-sensitivity principle](https://github.com/carbon-language/carbon-lang/pull/646). For ease of parsing, we've been trying to avoid using angle brackets (`<`...`>`) outside of declarations. This is primarily a concern for parameterized types that can appear in expressions alongside comparison operators (`<` and `>`). The choice to mark template parameters with a keyword was to make it very visible when that powerful and dangerous feature was being used. We also liked the similarities to how a `template` keyword also introduces a C++ template declaration. The choice to use the specific symbols `:!` over `:$` or other possibilities was just a matter of taste. ## Alternatives considered There were a few other options considered to designate generics in [issue #565](https://github.com/carbon-language/carbon-lang/issues/565). Note that we at first considered the possibility that type parameters might accidentally be declared as dynamic if that was the default. We eventually decided that we could forbid dynamic type parameters for now, and revisit this problem if and when we decided to add a dynamic type parameter feature. ### Default based on context In a given syntactic context, one option is going to be more common than others: - Regular explicit parameters would most commonly be dynamic. - Deduced parameters would most commonly be generic. - Parameters to interfaces and types would most commonly be generic. We considered making `x: T` be generic or dynamic based on this context. In cases where this default was not what was intended, there would be a keyword (`dynamic`, `generic`, or `template`) to explicitly pick. There were a few variations about whether to treat parameters used in types differently. This had the downside of being harder to discern at a glance. The main benefits of this approach were: - It handled dynamic type parameters being allowed but uncommon more gracefully. - Users could use `:` and it would generally do the right thing. - Keywords are generally easier to find in search engines, and more self-explanatory. - Template parameters in particular were highlighted, a property shared with the approach recommended by this proposal. The main objections to this approach was that it was context-sensitive and there was a lack of syntactic consistency in the context. That is, there were two kinds of context, generic and dynamic, and two kinds of brackets, square and parens, but sometimes the parens would be generic and sometimes not. #### Square brackets Using `[`...`]` for generics creates the opposite problem of the brackets being inconsistent with the deduced or explicit distinction. ``` // `T` is an explicit generic parameter to `Vector` class Vector[T: Type] { ... } // `T` and `DestT` are generic parameters, with `T` deduced and // `DestT` explicit. fn CastAVector[T: Type](v: Vector[T], generic DestT: Type) -> Vector[DestT]; ``` ### Other spellings that were considered There were a number of other options considered. None of them were compelling, though this mostly came down to taste. ``` class Vector() { ... } fn CastAVector(v: Vector(T), ) -> Vector(DestT); var from: Vector(i32) = ...; var to: Vector(i64) = CastAVector(from, i64); ``` - not trivial to parse, but doable - too much punctuation - nice that `<`...`>` is associated with generics, but with enough differences to be concerning ``` fn CastAVector[T: Type](v: Vector(T), [DestT: Type]) -> Vector(DestT); class Vector([T: Type]) { ... } var from: Vector(i32) = ...; var to: Vector(i64) = CastAVector(from, i64); ``` - There was no reason to prefer this over the previous option, since `<`...`>` is more associated with generics than `[`...`]`. Other different spellings of the `:!` position that came up during brainstorming but were not found to be compelling included: - `: Type` - `id:# Type` - `id:<> Type` - `id: ` - `generic id: Type` ### `Template` as a type-of-type We talked about the alternative of using a keyword like `Auto` or `Template` as a type-of-type to indicate that a parameter was a template. ``` fn FieldNames(T: Template) -> String; ``` This would be able to be combined with other type-of-types using the `&` operator to constrain the allowed types, as in `Container & Template`. The idea is that `Auto` or `Template` would act like an interface that contained any calls used by the function that were not in any other interface constraint for that parameter. It had two downsides: - This approach only worked for type parameters. We would need something else for non-type template parameters. - It didn't seem like you would want to be able to hide an `& Auto` or `& Template` clause by declaring it in a constant: ``` let CT = Container & Template; fn SurpriseIHaveATemplateParam[T: CT](c: T); ``` That suggests that the `Auto` or `Template` keyword would not act like other type-of-type expressions and would need special treatment. ## Alternatives not considered We never really broke out of the idea that `[`...`]` were for deduced parameters. As a result we didn't really consider options where type expressions used square brackets for generic parameters, as in `Vector[Int]`, even though that addresses the parsing problems of angle brackets. For example, if we were to revisit this decision, we might use three kinds of brackets for the three different cases: - `<`...`>` for deduced and generic parameters - `[`...`]` for explicit and generic parameters - `(`...`)` for explicit and dynamic parameters Code would most commonly use square brackets both when declaring and using a parameterized type or an interface. Parens would be required for functions, with generic parameters typically specified using angle brackets. ``` class Vector[T: Type] { ... } fn CastAVector[DestT: CastFrom[T]](v: Vector[T]) -> Vector[DestT]; var from: Vector[i32] = ...; var to: Vector[i64] = CastAVector[i64](from); ``` One concern is that use of square brackets will be in the same contexts as `[`...`]` will be used for indexing. Another concern is that there is [some motivation](http://open-std.org/JTC1/SC22/WG21/docs/papers/2019/p1045r1.html) for putting generic and template parameters together with regular parameters in the `(`...`)` parameter list [Go ultimately did decided to use square brackets for generics](https://go.googlesource.com/proposal/+/refs/heads/master/design/43651-type-parameters.md). This is even though Go also uses square brackets for slices and indexing. ================================================ FILE: proposals/p0680.md ================================================ # And, or, not [Pull request](https://github.com/carbon-language/carbon-lang/pull/680) ## Table of contents - [Problem](#problem) - [Background](#background) - [Usage in existing languages](#usage-in-existing-languages) - [Known issues with punctuation operators](#known-issues-with-punctuation-operators) - [Proposal](#proposal) - [Details](#details) - [Precedence](#precedence) - [Associativity](#associativity) - [Conversions](#conversions) - [Overloading](#overloading) - [Rationale based on Carbon's goals](#rationale-based-on-carbons-goals) - [Alternatives considered](#alternatives-considered) - [Use punctuation spelling for all three operators](#use-punctuation-spelling-for-all-three-operators) - [Precedence of AND versus OR](#precedence-of-and-versus-or) - [Precedence of NOT](#precedence-of-not) - [Punctuation form of NOT](#punctuation-form-of-not) - [Two forms of NOT](#two-forms-of-not) - [Repeated NOT](#repeated-not) - [AND and OR produce the decisive value](#and-and-or-produce-the-decisive-value) ## Problem Logical AND, OR, and NOT are important building blocks for working with Boolean values. Carbon should support them. ## Background ### Usage in existing languages Most mainstream programming languages use one (or both) of two syntaxes for these operators: - `&&`, `||`, and `!`. The `&&` and `||` operators were [first used by C](https://www.bell-labs.com/usr/dmr/www/chist.html) to distinguish short-circuiting behavior from that of regular operators. They are now seen in C, C++, C#, Swift, Rust, Haskell, and many more languages. `&&` and `||` invariably short-circuit. - `and`, `or`, and `not`. These are often used by languages with more emphasis on readability and scripting, such as Python, Pascal, Nim, SQL, and various variants of BASIC. In Python, the `and` and `or` operators short-circuit; in Pascal and Visual Basic, they do not, but variants of them do: - `and then` and `or else` in Pascal short-circuit. - `AndAlso` and `OrElse` in Visual Basic short-circuit. C++ recognizes `and`, `or`, and `not` as keywords and treats them as lexical synonyms for `&&`, `||`, and `!`. C provides an `` standard header that exposes `and`, `or`, and `not` as macros expanding to `&&`, `||`, and `!`. Perl, Ruby, and Raku permit both syntaxes, giving the punctuation forms higher precedence and the keyword forms lower precedence. Both forms short-circuit. Raku also provides `andthen` and `orelse`, but these differ from `and` and `or` in what value is produced on short-circuiting, not in whether they short-circuit. Zig provides `and`, `or`, and `!`. ### Known issues with punctuation operators The use of `&&` and `||` for short-circuiting logical operators is a common source of error due to the potential for confusion with the bitwise `&` and `|` operators. See: - [CERT rule EXP46-C: Do not use a bitwise operator with a Boolean-like operand](https://wiki.sei.cmu.edu/confluence/display/c/EXP46-C.+Do+not+use+a+bitwise+operator+with+a+Boolean-like+operand) - ChromiumOS bug prevents login due to `&&` / `&` typo ([news coverage](https://www.theregister.com/2021/07/23/chromebork_bug_google/)). We have anecdotal evidence that the `!` operator is hard to see for some audiences in some contexts, particularly when adjacent to similar characters: parentheses, `I`, `l`, `1`. ## Proposal Carbon will provide three operators to support logical operations on Boolean values. - `and` provides a short-circuiting logical AND operation. - `or` provides a short-circuiting logical OR operation. - `not` provides a logical NOT operation. Note that these operators are valid alternative spellings in C++; PR [#682](https://github.com/carbon-language/carbon-lang/pull/682) provides a demonstration of what this proposal would look like if applied to the C++ code in the Carbon project. ## Details `and` and `or` are infix binary operators. `not` is a prefix unary operator. ### Precedence `and`, `or`, and `not` have very low precedence. When an expression appearing as the condition of an `if` uses these operators unparenthesized, they are always the lowest precedence operators in that expression. These operators permit any reasonable operator that might be used to form a boolean value as a subexpression. In particular, comparison operators such as `<` and `==` have higher precedence than all logical operators. A `not` operator can be used within `and` and `or`, but `and` cannot be used directly within `or` without parentheses, nor the other way around. For example: ``` if (n + m == 3 and not n < m) { ``` can be fully parenthesized as ``` if (((n + m) == 3) and (not (n < m))) { ``` and ``` if (cond1 == not cond2) { // ... if (cond1 and cond2 or cond3) { ``` are both errors, requiring parentheses. ### Associativity `and` and `or` are left-associative. A `not` expression cannot be the operand of another `not` expression -- `not not b` is an error without parentheses. ``` // OK if (not a and not b and not c) { ... } if (not (not a)) { ... } // Error if (not a or not b and not c) { ... } if (not not a) { ... } ``` ### Conversions The operand of `and`, `or`, or `not` is converted to a Boolean value in the same way as the condition of an `if` expression. In particular: - If we decide that certain values, such as pointers or integers, should not be usable as the condition of an `if` without an explicit comparison against null or zero, then those values will also not be usable as the operand of `and`, `or`, or `not` without an explicit comparison. - If an extension point is provided to determine how to branch on the truth of a value in an `if` (such as by supplying a conversion to a Boolean type), that extension point will also apply to `and`, `or`, and `not`. ### Overloading The logical operators `and`, `or`, and `not` are not overloadable. As noted above, any mechanism that allows types to customize how `if` treats them will also customize how `and`, `or`, and `not` treats them. ## Rationale based on Carbon's goals - _Code that is easy to read, understand, and write:_ - The choice of `and` and `or` improves readability by avoiding the potential for visual confusion between `&&` and `&`. - The choice of `not` improves readability by avoiding the use of a small and easy-to-miss punctuation character for logical negation. - Having no precedence rule between `and` and `or` avoids readability problems with expressions involving both operators, by requiring the use of parentheses. - The use of a keyword rather than punctuation for these operators helps emphasize that they are not regular operators but instead have control-flow semantics. - Using the same precedence for `not` as for `and` and `or` allows conditions to quickly be visually scanned and for the overall structure and its nested conditions to be identified. - _Interoperability with and migration from existing C++ code:_ - The use of the operators `and`, `or`, and `not`, which are keywords with the same meaning in C++, avoids problems with a Carbon keyword colliding with a valid C++ identifier and allows early adoption of the Carbon syntax in C++ codebases. - While these operators are overloadable in C++, `&&` and `||` are nearly the least-overloaded operators, and `!` is generally only overloaded as a roundabout way of converting to `bool`. There is no known need for Carbon code to call overloaded C++ `operator&&`, `operator||`, or `operator!`, nor for Carbon code to provide such operators to C++. We will need a mechanism for `if`, `and`, `or`, and `not` to invoke (possibly-`explicit`) `operator bool` defined in C++, but that's outside the scope of this proposal. ## Alternatives considered ### Use punctuation spelling for all three operators We could follow the convention established by C and spell these operators as `&&`, `||`, and `!`. Advantage: - This would improve familiarity for people comfortable with this operator set. Disadvantages: - The use of keywords rather than punctuation gives a hint that `and` and `or` are not merely performing a computation -- they also affect control flow. - The `!` operator is known to be hard to see for some readers. - If we also support `&` and `|` operators, there is a known risk of confusion between `&&` and `&` and between `||` and `|`. - If we want to change the precedence rules, using a different syntax can serve as a reminder that these operators do not behave like the `&&`, `||`, and `!` operators. - `&&`, `||`, and `!` are likely to be harder to type than `and`, `or`, and `not` on at least most English keyboard layouts, due to requiring the use of the Shift key and reaching outside the letter keys. ### Precedence of AND versus OR Most languages give their AND operator higher precedence than their OR operator: ```c++ if (a && b || c && d) { ... } // ... means ... if ((a && b) || (c && d)) { ... } ``` This makes sense when viewed the right way: `&&` is the multiplication of Boolean algebra and `||` is the addition, and multiplication usually binds tighter than addition. We could do the same. However, this precedence rule is not reliably known by a significant fraction of developers despite having been present across many languages for decades, leading to common recommendations to enable compiler warnings for the first form in the above example, suggesting to rewrite it as the second form. This therefore fails the test from proposal [#555: when to add precedence edges](/proposals/p0555.md#when-to-add-precedence-edges). ### Precedence of NOT We could give the `not` operator high precedence, mirroring the behavior in C++. In this proposal, the `not` operator has the same precedence rules as `and` and `or`, which may be inconvenient for some uses. For example: ``` var x: Bool = cond1 == not cond2; ``` is invalid in this proposal and requires parentheses, and ``` var y: Bool = not cond1 == cond2; ``` is equivalent to ``` var y: Bool = cond1 != cond2; ``` which may not be what the developer intended. However, giving `not` higher precedence would result in a more complex rule and would break the symmetry between `and`, `or`, and `not`. ### Punctuation form of NOT We could use the spelling `!` for the NOT operator instead of `not`. The syntax of the `not` operator may be harder to read than a `!` operator for some uses. For example, when its operand is parenthesized, because it contains a nested `and` or `or`: ``` if (not (thing1 and thing2) or not (thing3 and thing4)) { ``` Also, the spelling of the `!=` operator is harder to justify if there is no `!` operator. However: - Changing the syntax would break the symmetry between `and`, `or`, and `not`. - Python uses this set of keywords and has a `!=` operator, and that doesn't appear to cause confusion in practice. - `not` may be easier to read than `!` in some uses, and easier to pick out of surrounding context. - `!` is being used to indicate early substitution in generics, so avoiding its use here may avoid overloading the same syntax for multiple purposes. - A function-like `not (...)` may better conform to reader expectations than a `!(...)` expression. ### Two forms of NOT We could combine the two previous alternatives, and introduce both a low-precedence `not` operator and a high-precedence `!` operator. This would allow us to provide both a convenient `!` operator for use in subexpressions and a consistent `not` keyword that looks and behaves like `and` and `or`. However, including both operators would either lead to one of the forms being unused or to the extra developer burden of style rules suggesting when to use each. Also, it may be surprising to provide a `!` operator but not `&&` and `||` operators. ### Repeated NOT We could permit the syntax `not not x` as an idiom for converting `x` to a Boolean type. However, we hope that a clearer syntax for that will be available, such as perhaps `x as Bool`. In the presence of such syntax, `not not x` would be an anti-pattern, and may indicate a bug due to an unintentionally repeated `not` operator. Per [#555: when to add precedence edges](/proposals/p0555.md#when-to-add-precedence-edges), when in doubt, we omit the precedence rule and wait for real-world experience. ### AND and OR produce the decisive value We could make the AND and OR operator produce the (unconverted) value that determined the result, rather than producing a Boolean value. For example, given nullable pointers `p` and `q`, and assuming that we can test a pointer value for nonnullness with `if (p)`, we could treat `p or q` as producing `p` if `p` is nonnull and `q` otherwise. This is the approach taken by several scripting languages, notably Python, Perl, and Raku. It is also the approach taken by `std::conjunction` and `std::disjunction` in C++. Advantages: - Provides a syntactic method to compute the first "truthy" or last "falsy" value in a list. Disadvantages: - The resulting code may be harder to read and understand, especially when the value is passed onto a function call. - Requires all conditions in a chained conditional to have a common type, or the use of a fallback rule in the case where there is no common type. - When combined with type deduction, an unexpected type will often be deduced; for example, `var x: auto = p and q;` may deduce `x` as having pointer type instead of Boolean type. ================================================ FILE: proposals/p0702.md ================================================ # Comparison operators [Pull request](https://github.com/carbon-language/carbon-lang/pull/702) ## Table of contents - [Problem](#problem) - [Background](#background) - [Terminology](#terminology) - [Usage in existing languages](#usage-in-existing-languages) - [Three-way comparisons](#three-way-comparisons) - [Chained comparisons](#chained-comparisons) - [Proposal](#proposal) - [Details](#details) - [Precedence](#precedence) - [Associativity](#associativity) - [Built-in comparisons and implicit conversions](#built-in-comparisons-and-implicit-conversions) - [Consistency with implicit conversions](#consistency-with-implicit-conversions) - [Comparisons with constants](#comparisons-with-constants) - [Performance](#performance) - [Overloading](#overloading) - [Default implementations for basic types](#default-implementations-for-basic-types) - [Rationale based on Carbon's goals](#rationale-based-on-carbons-goals) - [Alternatives considered](#alternatives-considered) - [Alternative symbols](#alternative-symbols) - [Chained comparisons](#chained-comparisons-1) - [Convert operands like C++](#convert-operands-like-c) - [Provide a three-way comparison operator](#provide-a-three-way-comparison-operator) - [Allow comparisons as the operand of `not`](#allow-comparisons-as-the-operand-of-not) ## Problem We need to be able to compare values for equality, and to compare ordered values for relative ordering. ## Background ### Terminology We refer to tests that check whether two values are the same or different as _equality_ comparisons, and to tests that determine the relative ordering of two values as _relational_ comparisons. ### Usage in existing languages There is near-universal convention on the use of the following symbols for relational operators: - `<`, `<=`, `>`, and `>=` perform ordered comparisons (less than, less than or equal to, greater than, greater than or equal to). There are rare exceptions in somewhat esoteric languages: some languages use `≤` and `≥`, but these are not straightforward to type for many potential Carbon developers. For equality operators, there is some divergence but still a very strong trend: - C-family languages, Rust, Swift, Kotlin, Zig, Nim, Ruby, etc. use `==` for equality comparison and `!=` for inequality comparison. - Some languages, such as ALGOL, APL, BASIC, and PL/I, use `=` as equality comparison, with some using a different symbol (such as `:=` or `<-`) for assignment and others distinguishing assignment from equality comparison based on context. - Haskell and Fortran use `==` for "equal to" and `/=` for "not equal to". The latter is intended to resemble a ≠ symbol. - Some languages, such as Pascal and BASIC, use `<>` for inequality comparison. Python 2 permits this as a synonym for `!=`. - Perl uses `eq` and `ne` for string comparisons; some shells and UNIX `test` use `-eq` and `-ne` for integer comparisons. Some languages support multiple different kinds of equality comparison, such as both a value comparison (typically `==`) and an object identity comparison (typically `===` or `is`). Some languages that freely convert between numbers and strings have different operators to perform a string comparison versus a numeric comparison. Fortran has custom `.eqv.` and `.neqv.` for equality comparisons of Boolean values. Some languages have synonyms for equality operators. For example, Fortran allows `.eq.`, `.ne.`, `.gt.`, and so on, as synonyms for `==`, `/=`, `>`, and so on. This appears to be historical: FORTRAN 77 had only the dotted forms of these operators. ### Three-way comparisons C++ has three-way comparisons, written using the `<=>` operator. These provide a useful mechanism to allow overloading the behavior of relational comparisons without defining four separate operator overloads for relational comparisons. Similarly, Python provides a `__cmp__` special method that can be used to implement all equality and relational comparisons. ### Chained comparisons Python permits comparisons to be chained: that is, `a < b <= c` is interpreted as `a < b and b <= c`, except that `b` is evaluated only once. In most C-family languages, that expression is instead interpreted as `(a < b) <= c`, which computes the value of `a < b`, maps `false` to `0` and `true` to `1`, then compares the result to `c`. ## Proposal Carbon will provide the following operators: - Equality comparison operators: `==` and `!=`. - Relational comparison operators: `<`, `<=`, `>`, `>=`. Each has the obvious mathematical meaning, where `==` means =, `!=` means ≠, `<=` means ≤, and `>=` means ≥. There will be no three-way comparison operator symbol. The interface used to support overloading comparison operators will provide a named function to perform three-way comparisons. Chained comparisons are an error: a comparison expression cannot appear as an unparenthesized operand of another comparison operator. For built-in types, we will follow these rules: - Behave consistently with implicit conversions: if an operation is valid between built-in types `T` and `U`, then it is valid between built-in types that implicitly convert to `T` and `U`. - Never invent an intermediate type that is larger than both operand types. - A comparison produces either a mathematically correct answer or a compilation error. The first two rules are expected to also apply for other built-in operators, such as arithmetic. The third rule is specific to comparisons. One consequence of the first rule is that we do not convert operands in a way that might lose information. This is generally also implied by the third rule. ## Details All six operators are infix binary operators. For standard Carbon types, they produce a `Bool` value. ### Precedence The comparison operators are all at the same precedence level. This level is lower than operators used to compute (non-Boolean) values, higher than the logical operators `and` and `or`, and incomparable with the precedence of `not`. For example, this is OK: ``` if (n + m * 3 < n * n and 3 < m and m < 6) {} ``` ... but these are errors: ``` // Error, ambiguous: `(not a) == b` or `not (a == b)`? if (not a == b) {} // Error, requires parentheses: `a == (not b)`. if (a == not b) {} // Error, requires parentheses: `not (f < 5.0)`. if (not f < 5.0) {} ``` ### Associativity The comparison operators are non-associative. For example: ``` // Error, need `3 < m and m < 6`. if (3 < m < 6) {} // Error, need `a == b and b == c`. if (a == b == c) {} // Error, need `(m > 1) == (n > 1)`. if (m > 1 == n > 1) {} ``` ### Built-in comparisons and implicit conversions Built-in comparisons are permitted: - when both operands are of standard Carbon integer types (`Int(n)` or `Unsigned(n)`), or - when both operands are of standard Carbon floating-point types (`Float(n)`), or - when one operand is of floating-point type and the other is of integer type, if all values of the integer type can be exactly represented in the floating-point type In each case, the result is the mathematically-correct answer. This applies even when comparing `Int(n)` with `Unsigned(m)`. For example: ``` // The value of `v` is True, because `a` is less than `b`, even though the // result of either an `i32` comparison or a `u32` comparison would be False. fn f(a: i32, b: u32) -> Bool { return a < b; } let v: Bool = f(-1, 4_000_000_000); // This does not compile, because `i64` values in general (and 10^18 in // particular) are not exactly representable in the type `f32`. let f: f32 = 1.0e18; let n: i64 = 1_000_000_000_000_000_000; let w: Bool = f == n; ``` Comparisons involving integer and floating-point constants are not covered by these rules and are [discussed separately](#comparisons-with-constants). #### Consistency with implicit conversions As specified in [#820](https://github.com/carbon-language/carbon-lang/pull/820), we support the following implicit conversions: - from `Int(n)` to `Int(m)` if `m > n`, - from `Unsigned(n)` to `Int(m)` or `Unsigned(m)` if `m > n`, - from `Float(n)` to `Float(m)` if `m > n`, and - from `Int(n)` to `Float(m)` if `Float(m)` can represent all values of `Int(n)`. These rules can be summarized as: a type `T` can be converted to `U` if every value of type `T` is a value of type `U`. Additionally [#820](https://github.com/carbon-language/carbon-lang/pull/820) permits conversions from certain kinds of integer and floating-point constants to `Int(n)` and `Float(n)` types if the constant can be represented in the type. All built-in comparisons can be viewed as performing implicit conversions on at most one of the operands in order to reach a suitable pair of identical or very similar types, and then performing a comparison on those types. The target types for these implicit conversions are, for each suitable value `n`: - `Int(n)` vs `Int(n)` - `Unsigned(n)` vs `Unsigned(n)` - `Int(n)` vs `Unsigned(n)` - `Unsigned(n)` vs `Int(n)` - `Float(n)` vs `Float(n)` There will in general be multiple combinations of implicit conversions that will lead to one of the above forms, but we will arrive at the same result regardless of which is selected, because all comparisons are mathematically correct and all implicit conversions are lossless. Implementations are expected to do whatever is most efficient: for example, for `u16 < i32` it is likely that the best choice would be to promote the `u16` to `i32`, not `u32`. Because we only ever convert at most one operand, we never use an intermediate type that is larger than both input types. For example, both `i32` and `f32` can be implicitly converted to `f64`, but we do not permit comparisons between `i32` and `f32` even though we could perform those comparisons in `f64`. If such comparisons were permitted, the results could be surprising: ``` // OK, i32 can exactly represent this value. var n: i32 = 2_000_000_001; // OK, this value is within the representable range for f32. var f: f32 = 2_000_000_001.0; // This comparison could compare unequal, because f32 cannot exactly represent // the value 2,000,000,001. if (n == f) { ... } // OK with explicit cast, but may still compare unequal. if (n == f as f64) { ... } if (n as f64 == f) { ... } ``` The two kinds of mixed-type comparison may be [less efficient](#performance) than the other kinds due to the slightly wider domain. Note that this approach diverges from C++, which would convert both operands to a common type first, sometimes performing a lossy conversion potentially giving an incorrect result, sometimes converting both operands, and sometimes using a wider type than either of the operand types. #### Comparisons with constants As described in [#820](https://github.com/carbon-language/carbon-lang/pull/820), integer constants can be implicitly converted to any integer or floating-point type that can represent their value, and floating-point constants can be implicitly converted to any floating-point type that can represent their value. We permit the following comparisons involving constants: - A constant can be compared with a value of any type to which it can be implicitly converted. - Any two constants can be compared, even if there is no type that can represent both. Note that this disallows comparisons between, for example, `i32` and an integer literal that cannot be represented in `i32`. Such comparisons would always be tautological. This decision should be revisited if it proves problematic in practice, for example in templated code where the literal is sometimes in range. #### Performance The choice to give correct results for signed/unsigned comparisons has a performance impact in practice, because it exposes operations that some processors do not currently directly support. [Sample microbenchmarks](https://quick-bench.com/q/1_xA8G_jXci_yeOKt0WgCc6eGN4) for implementations of several operations show the following performance on x86_64: | Operation | Mathematical comparison time | C++ comparison time | Ratio | | ----------- | ---------------------------- | ------------------- | ----- | | `i64 < u64` | 1636 | 798 | 2.0x | | `u64 < i64` | 1956 | 798 | 2.5x | The execution times here are computed as operation time minus no-op time. The mixed-type operations typically have 2-2.5x the execution time of the same-type operations. However, this is a predictable performance change, and can be controlled by the developer by converting the operands to a suitable type prior to the conversion if a faster same-type comparison is preferred over a correct mixed-type comparison. The above comparison attempts to demonstrate a worst-case difference. In many cases, better code can be generated for the mixed-type comparison. For example, when [branching on the result of the comparison](https://quick-bench.com/q/mXJiHK3_RcCH4fgB88phQscLu88), the difference is significantly reduced: | Operation | Mathematical comparison time | C++ comparison time | Ratio | | ----------- | ---------------------------- | ------------------- | ----- | | `i64 < u64` | 996 | 991 | 1.0x | | `u64 < i64` | 1973 | 997 | 2.0x | ### Overloading Separate interfaces will be provided to permit overloading equality and relational comparisons. The exact design of those interfaces is left to a future proposal. As non-binding design guidance for such a proposal: - The interface for equality comparisons should primarily provide the ability to override the behavior of `==`. The `!=` operator can optionally also be overridden, with a default implementation that returns `not (a == b)`. Overriding `!=` separately from `==` is expected to be used to support floating-point NaN comparisons and for C++ interoperability. - The interface for relational comparisons should primarily provide the ability to specify a three-way comparison operator. The individual relational comparison operators can optionally be overridden separately, with a default implementation in terms of the three-way comparison operator. This facility is expected to be used primarily to support C++ interoperability. - Overloaded comparison operators may wish to produce a type other than `Bool`, for uses such as a vector comparison producing a vector of `Bool` values. We should decide whether we wish to support such uses. ### Default implementations for basic types In addition to being defined for standard Carbon numeric types, equality and relational comparisons are also defined for all "data" types: - Tuples. - Structs (structural data classes). - Classes implementing an interface that identifies them as data classes. Relational comparisons for these types provide a lexicographical ordering. This proposal defers to [#710](https://github.com/carbon-language/carbon-lang/issues/710) for details on comparison support for classes. In each case, the comparison is only available if it is supported by all element types. The `Bool` type should be treated as a choice type, and so should support equality comparisons and relational comparisons if and only if choice types do in general. That decision is left to a future proposal. ## Rationale based on Carbon's goals - _Performance-critical software:_ - The use of a three-way comparison as the central primitive for overloading relational comparisons provides predictable, composable performance for comparing hierarchical data structures. - The performance of mixed comparisons may be slower than in C++, but this is because it's performing a different operation. This performance change is predictable, and can be controlled by the programmer by performing suitable non-value-preserving casts to a common type prior to the comparison. - _Code that is easy to read, understand, and write:_ - The chosen precedence and associativity rules aim to avoid bugs and ensure the code does what it appears to do, requiring parentheses in cases where the intent is unclear. - The choice to not perform lossy conversions on operands of a comparison operator removes a source of bugs caused by unintended lossy conversions. - _Interoperability with and migration from existing C++ code:_ - The use of the chosen operator symbols exactly matches C++, reducing friction for developers and code moving between the two languages, and for interoperability. ## Alternatives considered ### Alternative symbols We could use `/=` instead of `!=` for not-equal comparisons. Advantages: - Avoids overloading `!` for both "not equals" and template/generic use in `:!` bindings. - There is no other usage of `!` meaning "not" in the language because we use a `not` operator. Disadvantages: - Unfamiliar to C++ programmers. - `a /= b` would likely be expected to mean an `a = a / b` compound assignment. - Breaks consistency with Python, which uses `not` for logical negation and `!=` for inequality comparison. We could use `=/=` instead of `!=` for not-equal comparisons. Advantages: - As above; also `=/=` looks like an `==` with a line through the middle. Disadvantages: - This would be inventive and unlike all other languages. As above, breaks consistency with Python. - This would make `=/=` one character longer, and harder to type on US-ASCII keyboards because the keys are distant but likely to be typed with the same finger. ### Chained comparisons We could support Python-like chained comparisons. Advantages: - Small ergonomic improvement for range comparisons. - Middle operand is evaluated only once. Disadvantages: - Using the middle expression as an argument to two different functions may create problems, as the value will need to be stored somewhere, potentially changing the semantics of the operator expression as we can no longer move from the operand. - Both short-circuiting behavior and non-short-circuiting behavior will be surprising and unintuitive to some. The short-circuiting option will introduce control flow without a keyword to announce it, which goes against our design decision to use a keyword for `and` and `or` to announce the control flow. The non-short-circuiting option will evaluate subexpressions unnecessarily, which creates a tension with our performance goal. - Experienced C++ developers may expect a different behavior, such as `a < b == cmp` comparing the result of `a < b` against the Boolean value `cmp`. See also the ongoing discussion in [#451](https://github.com/carbon-language/carbon-lang/issues/451). ### Convert operands like C++ We could convert the operands of comparison operators in a way that's equivalent to C++'s behavior. Advantages: - May ease migration from C++. - May allow programmers to reuse some intuition, for example when comparing floating-point values against integer values. - Allows more efficient machine code to be generated for source code that takes no special care about the types of comparison operands. - Improves performance predictability for C++ developers unfamiliar with Carbon's rules. Disadvantages: - Produces incorrect results. - Does not provide a simple syntax for correct mixed-type comparisons. ### Provide a three-way comparison operator We could provide a symbol for three-way comparisons, such as C++20's `<=>`. Advantages: - The use of a symbol rather than a named member of an interface for this functionality may ease migration from C++20. Disadvantages: - Reserves a symbol for an operation that should not be used directly except in special circumstances, and that will produce a nuanced type even when comparing standard Carbon types such as `f32`. ### Allow comparisons as the operand of `not` We could permit comparisons to appear as the immediate operand of `not` without parentheses. Advantages: - Provides an easier syntax for floating-point comparisons where the desired result for a NaN operand is `True` rather than `False`: `not f < 5.0`. Disadvantages: - Introduces ambiguity when comparing Boolean values: `not cond1 == cond2` might intend to compare `not cond1` to `cond2` rather than comparing `cond1 != cond2`. ================================================ FILE: proposals/p0720.md ================================================ # Property naming in C++ [Pull request](https://github.com/carbon-language/carbon-lang/pull/720) ## Table of contents - [Problem](#problem) - [Background](#background) - [Style](#style) - [Properties](#properties) - [Proposal](#proposal) - [Rationale based on Carbon's goals](#rationale-based-on-carbons-goals) - [Alternatives considered](#alternatives-considered) - [Disallow "setters"](#disallow-setters) - [Disallow mutable accessors](#disallow-mutable-accessors) - [Looser restrictions on property method performance](#looser-restrictions-on-property-method-performance) - [Require trailing `_` only for property data members](#require-trailing-_-only-for-property-data-members) ## Problem This proposal aims to address two problems: - Our current C++ style rules are prone to name conflicts, particularly between types and accessors. - There is preliminary interest in Carbon providing "properties" as a language feature. It would be useful to be able to experiment with the style and API effects of such a feature in our C++ code. ## Background ### Style Our C++ style rules currently require all functions (including accessors) and all types to have `CamelCase` names. However, this creates a nontrivial risk of name collisions. To take two examples: - The base class [`Carbon::Pattern`](https://github.com/carbon-language/carbon-lang/blob/ebd6c7afa91a1a02961b72d619fba630d8fbfbff/executable_semantics/ast/pattern.h#L25) defines a `Kind` enum, which identifies the concrete type of a `Pattern` object, as part of its support for [LLVM-style RTTI](https://llvm.org/docs/HowToSetUpLLVMStyleRTTI.html). It also needs to provide an accessor that exposes the `Kind` of a `Pattern` object, so that client code can `switch` on it rather than using virtual dispatch. The most natural name for that accessor would be `Kind`, but C++ doesn't allow us to use the same name for both a type and a function in the same scope. Instead, the accessor is called `Tag`, but that's an entirely ad-hoc solution: there happened to be a synonymous term available, so we arbitrarily chose one for the type and the other for the function. - The class [`Carbon::ExpressionPattern`](https://github.com/carbon-language/carbon-lang/blob/ebd6c7afa91a1a02961b72d619fba630d8fbfbff/executable_semantics/ast/pattern.h#L181) adapts an `Expression` to behave as a `Pattern`. The underlying `Expression` is exposed by an accessor, which is named `Expression`. That function name shadows the type name within the class, so the type must instead be referred to by its fully-qualified name `Carbon::Expression`. As these examples illustrate, there are occasional situations where the best name for an accessor is the same word (or words) as the name of its return type, because in context, that type fully describes what the accessor returns. Since our style rules require those words to be turned into identifiers in the same way (`CamelCase` rather than `snake_case`), this results in shadowing, or an outright name collision. ### Properties Some programming languages allow classes to define [property members](), which are accessed using data member syntax, but allow the type to implement those accesses using procedural code. Properties can thus offer the clarity and convenience of public data members, while retaining many of the advantages of private data with "getters" and "setters". However, the fact that properties can be implemented using arbitrary procedural code means that they are capable of behaving in ways that would be very surprising to a user who thinks of them as ordinary data members, such as performing large amounts of computation, blocking on I/O, or causing observable side effects when the property is being read. Usage of properties therefore involves a style tradeoff between expressive power and user surprise. Properties are a popular feature of several of Carbon's peer languages, so we are likely to eventually explore the possibility of adding them to Carbon. When we do, it may be useful to have some experience with the style tradeoffs that properties create. ## Proposal I propose to allow C++ classes in the Carbon project to provide methods that are named like properties, so long as they behave like properties. I also propose to require data members to have a trailing `_`. See the changes to `cpp_style_guide.md` for further specifics. ## Rationale based on Carbon's goals Avoiding the name conflicts will incrementally advance our "Language tools and ecosystem" goals by making those tools somewhat easier to implement. Early experimentation with properties in C++ could help us design Carbon properties to better address several of Carbon's goals, especially "code that is easy to read, understand, and write". ## Alternatives considered ### Disallow "setters" We could narrow the style rule to disallow `set_` methods. Advantages: - This minimizes the likely syntactic divergence from Carbon properties, because reading a Carbon property will probably look very much like an accessor call, minus the `()`, but assigning to a Carbon property will probably look like an assignment, not a `set_` method call. Disadvantages: - This would lead to inconsistencies where a `foo_bar()` accessor is paired with a `SetFooBar()` setter. - This would prevent us from gaining experience that could shed light on the design of mutable properties in Carbon. ### Disallow mutable accessors We could narrow the style rule to disallow property accessors that provide non-const access to the property. Advantages: - It would be a more narrow feature to allow in our C++ code. - Carbon doesn't yet have a property design and we don't know the extent or mechanisms it might use for mutable access. - In C++, mutable accessors directly expose the underlying storage, removing most abstraction opportunities. Disadvantages: - This would prevent us from resolving name collisions in cases that involve in-place mutation, and lead to inconsistencies where a `foo_bar()` const accessor is paired with a `FooBar()` (or perhaps `MutableFooBar()`) non-const accessor. - This would prevent us from gaining experience that could shed light on the design of mutable properties in Carbon. ### Looser restrictions on property method performance We could allow methods to use property-like naming, even if their performance is substantially worse than the cost of accessing a data member, so long as the overall performance of the code calling the method is likely to be comparable. For example, this could permit property methods to return strings by value, even though that requires linear time in the length of the string, because the subsequent usage of the string will very likely be linear time as well, in which case there is no overall asymptotic slowdown. Advantages: - It would allow us to define methods that behave like "computed properties" for types like strings that are not fixed-size, but that are typically treated as single values rather than collections. Disadvantages: - It would be a substantially more subjective rule, since it involves guesswork about what usages are likely. - It would carry a greater risk of user surprise. ### Require trailing `_` only for property data members It's necessary to have trailing `_`s (or some equivalent convention) on data members that provide storage for properties, because otherwise their names would collide with the names of member accessors. However, we could continue to forbid a trailing `_` on all other data members. Advantages: - This would be a more minimal change to our existing style, and in particular it would avoid putting any existing code out of compliance with the style guide. Disadvantages: - It might be harder to predict and internalize which data members have a trailing `_`. - The trailing `_` can provide readability benefits even for non-property members, by explicitly marking member accesses in a way that distinguishes them from local variable accesses. ================================================ FILE: proposals/p0722.md ================================================ # Nominal classes and methods [Pull request](https://github.com/carbon-language/carbon-lang/pull/722) ## Table of contents - [Problem](#problem) - [Background](#background) - [Proposal](#proposal) - [Rationale based on Carbon's goals](#rationale-based-on-carbons-goals) - [Alternatives considered](#alternatives-considered) - [Method syntax](#method-syntax) - [Rationale](#rationale) - [Full receiver type](#full-receiver-type) - [Method names line up](#method-names-line-up) - [Receiver in square brackets](#receiver-in-square-brackets) - [Receiver parameter is named `me`](#receiver-parameter-is-named-me) - [Keyword to indicate pass by address](#keyword-to-indicate-pass-by-address) - [Marking mutating methods at the call site](#marking-mutating-methods-at-the-call-site) - [Differences between functions and methods](#differences-between-functions-and-methods) - [Specifying linkage as part of the access modifier](#specifying-linkage-as-part-of-the-access-modifier) - [Nominal data class](#nominal-data-class) - [Let constants](#let-constants) ## Problem We need facilities for defining new nominal [record types]() in Carbon. They will need to support object-oriented features such as methods. ## Background This is a follow up to [#561: Basic classes: use cases, struct literals, struct types, and future work](https://github.com/carbon-language/carbon-lang/pull/561) which laid a foundation. ## Proposal The proposal is to update [docs/design/classes.md](/docs/design/classes.md) as described in [this PR](https://github.com/carbon-language/carbon-lang/pull/701). ## Rationale based on Carbon's goals This particular proposal is focusing on [the Carbon goal](/docs/project/goals.md#code-that-is-easy-to-read-understand-and-write) that code is "easy to read, understand, and write." Future proposals will address other aspects of the class type design such as performance. ## Alternatives considered ### Method syntax The proposed method syntax was decided in question-for-leads issue [#494: Method syntax](https://github.com/carbon-language/carbon-lang/issues/494). A wide variety of alternatives were considered: - Using a different introducer such as `method` to distinguish methods from functions. - Putting a pattern to match the receiver before the method name. - Omitting either the receiver name or the receiver type. - Allowing the user to specify the receiver name instead of using a parameter named `me` to indicate it is a method. - Using syntax between `fn` and the method name to indicate that it is a method, and whether the method takes a value or the address of the receiver. - Using a keyword to indicate that the first parameter is the receiver, as in [C++'s "Deducing `this`" proposal](http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2021/p0847r6.html). - Using a delimiter in the parameter list after the receiver type, but the main choice of `;` seemed pretty subtle. - Putting the receiver pattern in a separate set of brackets. This could be an additional set of parens `(`...`)` before the explicit parameter list. Or the deduced parameters could be put in angle brackets `<`...`>` and the receiver in square brackets `[`...`]`. Examples: ``` method (this: Self*) Set(n: Int); fn [Me*].Set(n: Int); fn ->Set(n: Int); fn& Set(n: Int); fn &Set(s: Self*, n: Int); fn Set(this self: Self*, n: Int); fn Set[this self: Self*](n: Int); fn Set(self: Self*; n: Int); fn Set(self: Self*)(n: Int); ``` Note: These examples are written using the parameter syntax decided in [issue #542](https://github.com/carbon-language/carbon-lang/issues/542), though that does not match the syntax used in the discussion at the time. #### Rationale ##### Full receiver type We wanted the option to specify the type of the receiver. In C++, the type of this is controlled by way of special syntax, such as putting a `const` keyword at the end of the method declaration. We wanted to treat the receiver type more consistently with other parameter types, like is being considered in [C++'s "Deducing `this`" proposal](http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2021/p0847r6.html). That proposal also showed the value of including the entire type, rather than just part such as whether the parameter would be passed by value or address. We already plan to use this feature to conditionally include methods based on whether parameters to the type satisfy additional constraints. In this example, `FixedArray(T, N)` has a `Print()` method if `T` is `Printable`: ``` class FixedArray(T:! Type, N:! Int) { // ... fn Print[me: FixedArray(P:! Printable, N)]() { ... } } ``` We did consider the option of making the type of the receiver optional. This is not what we decided to try first, but is an idea we would consider in the future. ##### Method names line up Using the same introducer `fn` as functions, as decided in [issue #463](https://github.com/carbon-language/carbon-lang/issues/463), along with the method name immediately afterwards means that function and method names line up in the same column visually. ``` struct IntContainer { // Non-methods for building instances fn MakeFromInts ... -> IntContainer; fn MakeRepeating ... -> IntContainer; // Methods fn Size ... -> Int; fn First ... -> Int; fn Clear ...; fn Append ...; } ``` This is to ease scanning and to put the most important information up front. We also considered using another 2-character introducer that would distinguish methods from associated functions, like `me`, but it did not garner much support. The name `me` was not evocative enough of "method", and people generally preferred matching other function declarations. We also considered using different introducers to communicate whether the receiver was passed by value or by address, for example `ro` for "read-only" and `rw` for "read-write". ##### Receiver in square brackets Putting the receiver pattern inside the explicit parameter list, as is done in Python, would lead to an unfortunate mismatch between the arguments listed at the call and the function declaration. That motivated putting the receiver pattern inside square brackets (`[`...`]`) with deduced parameters. There were concerns, however, that this parameter did not act like deduced parameters. ##### Receiver parameter is named `me` Having the receiver name be fixed is helpful for consistency. This benefits readers, and simplifies copying or moving code between functions. A fixed receiver name also allows us to identify the receiver pattern in the function declaration, and identify it as a method instead of an ordinary function associated with the type, like [static methods in C++](). Finally, we wanted the bodies of methods to access members of the object using an explicit member access. This means we need the name used to access members to be short, and so we chose the name `me`. ##### Keyword to indicate pass by address We considered using just whether the receiver type was a pointer type to indicate whether the receiver was passed by address, but we preferred type information to flow in one direction. Otherwise this would cause a problem if we want to allow _deduction_ of the object parameter type. This deduction can't be used to select between pointer and not pointer. We also considered supporting reference types that could bind to lvalues without explicitly taking the address of the object, but references would have added a lot of complexity to the type system and we believed that they are not otherwise necessary. We decided to adopt an approach where the parameter declaration can contain a marker for matching an argument's [value category](https://en.cppreference.com/w/cpp/language/value_category) separately from its type. This marker would be present for methods that mutate the object, and so requires that the receiver be an lvalue to match the pattern. The marker means "first take the address of the argument and then match the rest of the pattern." We considered a few different possible ways to write this: ``` fn Set[&me: Self*](n: Int); fn Set[*(me: Self*)](n: Int); fn Set[ref me: Self*](n: Int); ``` We eventually settled on using a keyword `addr`. ``` fn Set[addr me: Self*](n: Int); ``` This doesn't use up a symbol, makes it easier to find in search engines, and allows us to add more keywords in the future for other calling conventions, such as `inout`. We may even find that those other keywords have preferable semantics so we may eventually drop `addr`. This keyword approach also is consistent with how we are marking template parameters with the `template` keyword, as decided in [issue #565 on generic syntax](https://github.com/carbon-language/carbon-lang/issues/565). ### Marking mutating methods at the call site We considered making it visible at the call site when the receiver was passed by address, with the address-of operator `&`. ``` (&x).Set(4); ``` This would in effect make the mutating methods be methods on the pointer type rather than the class type. ### Differences between functions and methods Question-for-leads issue [#494: Method syntax](https://github.com/carbon-language/carbon-lang/issues/494) also considered what would be different between functions and methods: - Methods use a different calling syntax: `x.F(n)`. - Methods distinguish between taking the receiver (`x`) by value or by pointer, without changing the call syntax. - Methods have to be declared in the body of the class. - Methods and associated functions both have access to private members of the class. - Only methods can opt in to using dynamic dispatch. - The receiver parameter to a method varies covariantly in inheritance unlike other parameter types. ### Specifying linkage as part of the access modifier We considered various access modifiers such as `internal` or `private.internal` that would enforce internal linkage in addition to restricted visibility. We decided to postpone such considerations for the time being. Even if we _do_ need explicit controls here, we did not feel the need to front-load that complexity right now and with relatively less time or experience to evaluate the tradeoffs. If and when linkage becomes a visible issue and important to discuss, we can add it. Until then, we can see how much mileage we can get out of purely implementation techniques. ### Nominal data class We needed some way of opting a nominal class into the fieldwise behavior of a data class. [In Kotlin](https://kotlinlang.org/docs/data-classes.html), you precede the `class` declaration with a `data` keyword. Carbon already has a way of marking types as having specific semantic properties, implementing interfaces. This leverages the support for interfaces in the language to be able to express things like "here is a blanket implementation of an interface for all data classes" in a consistent way. We chose the name `Data` rather than `DataClass` for the interface since tuples implicitly implement the interface and were "product types" rather than "classes". ### Let constants We wanted a consistent interpretation for `let` declarations in classes and function bodies. Experience from C++ `const int` variables, where we try constant-evaluation and then give the program different semantics based on whether such evaluation happened to work, suggested we wanted to instead require `:!` in all places where a value that's usable during compilation is introduced. ================================================ FILE: proposals/p0731.md ================================================ # Generics details 2: adapters, associated types, parameterized interfaces [Pull request](https://github.com/carbon-language/carbon-lang/pull/731) ## Table of contents - [Problem](#problem) - [Background](#background) - [Proposal](#proposal) - [Rationale based on Carbon's goals](#rationale-based-on-carbons-goals) - [Alternatives considered](#alternatives-considered) - [`adaptor` instead of `adapter`](#adaptor-instead-of-adapter) - [Syntax for associated constants](#syntax-for-associated-constants) - [Omitting types](#omitting-types) - [Inferring associated types from method signatures](#inferring-associated-types-from-method-signatures) - [Value patterns](#value-patterns) - [Deduced interface parameters](#deduced-interface-parameters) - [Rationale for the rejection](#rationale-for-the-rejection) - [Impl lookup rules with deducible interface parameters](#impl-lookup-rules-with-deducible-interface-parameters) - [Only associated types, no interface parameters](#only-associated-types-no-interface-parameters) - [Others](#others) ## Problem We want to Carbon to have a high quality generics feature that achieves the goals set out in [#24](https://github.com/carbon-language/carbon-lang/pull/24). This is too big to land in a single proposal. This proposal continues [#553](https://github.com/carbon-language/carbon-lang/pull/553) defining the details of: - adapters - associated types and other constants - parameterized interfaces ## Background This is a follow on to these previous generics proposals: - [#24: Generics goals](https://github.com/carbon-language/carbon-lang/pull/24) - [#447: Generics terminology](https://github.com/carbon-language/carbon-lang/pull/447) - [#524: Generics overview](https://github.com/carbon-language/carbon-lang/pull/524) - [#553: Generics details part 1](https://github.com/carbon-language/carbon-lang/pull/553) The content for this proposal was extracted from a larger [Generics combined draft proposal](https://github.com/carbon-language/carbon-lang/pull/36). ## Proposal This is a proposal to add multiple sections to [this design document on generics details](/docs/design/generics/details.md). ## Rationale based on Carbon's goals Much of this rationale was captured in the [Generics goals proposal](https://github.com/carbon-language/carbon-lang/pull/24). ## Alternatives considered ### `adaptor` instead of `adapter` We considered replacing the `adapter` keyword with the alternate spelling of "adaptor". Both spellings can be used for the intended meaning, but the "-er" spelling is more common in English text and in code. The final deciding factor was that the [GoF Design Patterns book](https://en.wikipedia.org/wiki/Design_Patterns) spells the ["adapter pattern"](https://en.wikipedia.org/wiki/Adapter_pattern) with the ["-er" spelling](https://springframework.guru/gang-of-four-design-patterns/adapter-pattern/). ### Syntax for associated constants Issue [#739: Associated type syntax](https://github.com/carbon-language/carbon-lang/issues/739) decided that the syntax for assigning a value to an associated constant, such as an associated type. The decision was to use `let` with `:!` to express that these are compile-time values, matching the use in classes described in proposal [#772](p0722.md#let-constants). ``` interface Stack { let ElementType:! Type; fn Push[addr me: Self*](value: ElementType); ... } class DynamicArray(T:! Type) { ... impl as Stack { let ElementType:! Type = T; fn Push[addr me: Self*](value: ElementType); ... } } ``` One advantage was this opened the door for a type to satisfy the associated types of two interfaces with the same name with a single `let` declaration using constraints satisfying the requirements of both interfaces. This type can be replaced with `auto`, to have it determined automatically. ``` class DynamicArray(T:! Type) { ... impl as Stack { let ElementType:! auto = T; fn Push[addr me: Self*](value: ElementType); ... } } ``` This would avoid needing to change the `impl` when the constraints in the interface changed as long as the value to the right of the `=` satisfied the new constraints. Otherwise if the constraints are being weakened, first functions relying on the capabilities being removed would have to change, then they would be changed in the interface, and finally the implementations for types. If the constraints are being strengthened, the implementations for types would have to change first followed by the interface. #### Omitting types We also considered omitting the type in the `impl`, always using the type declared in the interface. ``` class DynamicArray(T:! Type) { ... impl as Stack { let ElementType = T; fn Push[addr me: Self*](value: ElementType); ... } } ``` This would provide the advantage of reducing the number of changes when changing the constraint specified in the interface. If the constraints were being weakened, then functions that used the capability that was being removed would break or need to be modified. If the constraints were being strengthened, then only type implementations that didn't satisfy the new constraints would break or need to be modified. The biggest difference from the selected option is when adding a constraint. In that case the selected option would have more churn, because all implementations would be updated even if they already satisfied the new constraint. This comes with the advantage of making it easier _incrementally enforce_ greater constraints. On the whole, it seems like both could be made to work. You could explicitly specify constraints with this option by using an alias to a normal `let ..:! TypeOfType` declaration that has extra constraints. Conversely, you can specify `auto` as the constraints in the selected option. But on balance, it seemed better to try putting the explicit constraints into the implementations so that we have more tools to incrementally roll out changes to interface constraints even though those rollouts will as a consequence be more noisy in some cases. If experience shows that this is a really bad tradeoff, we should revisit it. #### Inferring associated types from method signatures The last option considered is used by Swift. [Swift allows the value of an associated type to be omitted when it can be determined from the method signatures in the implementation](https://docs.swift.org/swift-book/LanguageGuide/Generics.html#ID190). For the above example, this would mean figuring out `ElementType == T` from context: ``` class DynamicArray(T:! Type) { ... impl as Stack { // Not needed: let ElementType:! Type = T; fn Push[addr me: Self*](value: T); ... } } ``` One benefit is that it allows an interface to evolve by adding an associated type, without having to then modify all implementations of that interface. One concern is this might be a little more complicated in the presence of method overloads with [default implementations](/docs/design/generics/details.md#interface-defaults), since it might not be clear how they should match up, as in this example: ``` interface Has2OverloadsWithDefaults { let T:! StackAssociatedType; fn F[me: Self](x: DynamicArray(T), y: T) { ... } fn F[me: Self](x: T, y: T.ElementType) { ... } } class S { impl as Has2OverloadsWithDefaults { // Unclear if T == DynamicArray(Int) or // T == DynamicArray(DynamicArray(Int)). fn F[me: Self]( x: DynamicArray(DynamicArray(Int)), y: DynamicArray(Int)) { ... } } } ``` Not to say this can't be resolved, but it does add complexity. [Swift considered](https://github.com/apple/swift/blob/main/docs/GenericsManifesto.md#associated-type-inference) removing this feature because it was the one thing in Swift that required global type inference, which they otherwise avoided. They [ultimately decided to keep the feature](https://github.com/apple/swift-evolution/blob/main/proposals/0108-remove-assoctype-inference.md). This option was only very briefly discussed and not preferred because: - It came with complexity of inference. - It seemed unnecessary. ### Value patterns We considered an alternative to the `type_of` approach from [the parameterized interfaces section](/docs/design/generics/details.md#parameterized-interfaces) for binding `T` to a type mentioned later in the parameter list. We could instead allow functions to have value patterns without a `:`, as in: ``` fn PeekAtTopOfStackParameterized [T:! Type, StackType:! StackParameterized(T)] (s: StackType*, T) -> T { ... } ``` However, we don't want to allow value patterns more generally so we can reject declarations like `fn F(Int)` when users almost certainly meant `fn F(i: Int)`. ### Deduced interface parameters The Carbon team considered and then rejected the idea that we would have two kinds of interface parameters. "Multi" parameters would work as described in the [detailed design document](/docs/design/generics/details.md#parameterized-interfaces). "Deducible" type parameters would only allow one implementation of an interface, not one per interface & type parameter combination. These deducible type parameters could be inferred like [associated types](/docs/design/generics/details.md#associated-facets) are. For example, we could make a `Stack` interface that took a deducible `ElementType` parameter. You would only be able to implement that interface once for a type, which would allow you to infer the `ElementType` parameter like so: ``` fn PeekAtTopOfStack[ElementType:! Type, StackType:! Stack(ElementType)] (s: StackType*) -> ElementType { ... } ``` This can result in more concise code for interfaces where you generally need to talk about some parameter anytime you use that interface. For example, `NTuple(N, type)` is much shorter without having to specify names with the arguments. #### Rationale for the rejection - Having only one type of parameter simplifies the language. - Multi parameters express something we need, while deducible parameters can always be changed to associated types. - One implementation per interface & type parameter combination is more consistent with other parameterized constructs in Carbon. For example, parameterized types `Foo(A)` and `Foo(B)` are distinct, unconnected types. - It would be hard to give clear guidance on when to use associated types versus deducible type parameters, since which is best for a particular use is more of a subtle judgement call. - Deducible parameters in structural interfaces require additional rules to ensure they can be deduced unambiguously. In addition, deducible interface parameters would complicate the lookup rules for impls. ##### Impl lookup rules with deducible interface parameters Interface implementation is Carbon's only language construct that allows open extension, and this sort of open extension is needed to address the "expression problem" in programming language design. However, we need to limit which libraries can implement an interface for a type so we can be guaranteed to see the implementation when we try and use it. So the question becomes: can we allow an implementation of a parameterized interface `I(T)` for a type `A` to be in the same library as `T`, or can it only be provided with `I` or `A`? The answer is "yes" if `T` is "multi" and "no" if `T` is deducible. The problem with defining the implementation with a deducible `T` is that it would allow users to violate [coherence](/docs/design/generics/goals.md#coherence). Consider this collection of libraries, where there are implementations for an interface `I(T)` for a type `A`, and those implementations are in the libraries defining the type parameter: ``` package X library "I and A" api; interface I(Type:$ T) { ... } struct A { ... } ``` ``` package Y library "T1" api; import X library "I and A"; struct T1 { ... } // Type `X.A` has an implementation for `X.I(T)` for `T == Y.T1`. impl X.I(T1) for X.A { ... } ``` ``` package Z library "T2" api; import X library "I and A"; struct T2 { ... } // Type `X.A` has an implementation for `X.I(T)` for `T == Z.T2`. impl X.I(T2) for X.A { ... } ``` ``` package Main api; import X library "I and A"; // Consider what happens if we include different combinations // of the following two statements: // import Y library "T1"; // import Z library "T2"; // Function `F` is called with value `a` with type `U`, // where `U` implements interface `X.I(T)` for some type `T`. fn F[Type:$ T, X.I(T):$ U](U:$ a) { ... } fn Main() { var X.A: a = X.A.Init(); F(a); } ``` (In the example, each library is in a different package, but the packages are not the important part here.) The `F(a)` call triggers a lookup for implementations of the interface `X.I(T)` for some `T`. There exists such implementations in both libraries `Y.T1` and `Z.T2` for different values of `T`. This has a number of sad consequences: - "Import what you use" is hard to measure: libraries `Y.T1` and `Z.T2` are important and used even though `Y` and `Z` are not mentioned outside the `import` statement. - The call `F(a)` has different interpretations depending on what libraries are imported: - If neither is imported, it is an error. - If both are imported, it is ambiguous. - If only one is imported, you get totally different code executed depending on which it is. - We have no way of enforcing a "one implementation per interface" rule that would prevent the call to `F` from being ambiguous. Basically, there is nothing guaranteeing that we import libraries defining the types that are used as interface parameters if we allow the interface parameters to be deduced. ### Only associated types, no interface parameters This is what Swift does, but doesn't allow us to use interfaces to express operator overloads. For example, a vector should be able to be added to either a vector or a point. So we follow Rust, which has trait parameters in addition to associated types and uses them to define the behavior of operators. ### Others Other alternatives considered will be in a future proposal. Some of them can be seen in a rough form in [#36](https://github.com/carbon-language/carbon-lang/pull/36). ================================================ FILE: proposals/p0752.md ================================================ # `api` file default-`public` [Pull request](https://github.com/carbon-language/carbon-lang/pull/752) ## Table of contents - [Problem](#problem) - [Background](#background) - [Proposal](#proposal) - [Rationale based on Carbon's goals](#rationale-based-on-carbons-goals) - [Alternatives considered](#alternatives-considered) - [Default `api` to private](#default-api-to-private) - [Default `impl` to public](#default-impl-to-public) - [Make keywords either optional or required in separate definitions](#make-keywords-either-optional-or-required-in-separate-definitions) ## Problem Question for leads [#665: private vs public _syntax_ strategy, as well as other visibility tools like external/api/etc.](https://github.com/carbon-language/carbon-lang/issues/665) decided that methods on classes should default to public. Should `api` echo the similar strategy? ## Background - In C++, `struct` members default public, while `class` members default `public`. - In proposal [#107: Code and name organization](https://github.com/carbon-language/carbon-lang/pull/107), an `api` keyword was used to indicate public APIs within an `api` file. - In [#665](https://github.com/carbon-language/carbon-lang/issues/665), it was decided that Carbon class members should default `public`. - This issue was reopened to discuss alternatives in this proposal. ## Proposal APIs in the `api` file should default public, without need for an additional `api` keyword. `private` may be specified to designate APIs that are internal to the library, and only visible to `impl` files. Nothing is necessary within `impl` files, and APIs there will be private unless forward declared in the `api` file. ## Rationale based on Carbon's goals - [Code that is easy to read, understand, and write](/docs/project/goals.md#code-that-is-easy-to-read-understand-and-write): It will be easier for developers to understand code if APIs have semantically similar behavior when comparing the visibility of class methods to other code, and the library to other packages. ## Alternatives considered ### Default `api` to private Default private is what was implied by `api`, and was the previous state. Advantages: - Decreases the likelihood that developers will accidentally expose APIs, because it's an explicit choice. - Can move functions between `api` and `impl` without visibility changing. Disadvantages: - The `api` file's primary purpose is to expose APIs, and so it may be more natural for developers to assume things there are public. - Inconsistent with "default public" behavior on classes. We are switching to default public in `api` files for consistency with class behaviors. ### Default `impl` to public Noting that we default `api` to public, we could similarly default `impl` to public. Advantages: - Can move functions between `api` and `impl` without visibility changing. Disadvantages: - Everything in an `impl` file must be private unless it's a separate definition of an `api` declaration. As a consequence, everything declared in the `impl` file would need to be explicitly `private`. In order to avoid the toil of explicitly declaring everything in the `impl` as `private`, `impl` will be `private` by default. As a consequence of being the default behavior, no `private` should be specified, just as `public` is not allowed in `api` files. Note the visibility behavior can be described as making declarations the most visible possible for its context, which in `api` files is `public`, and in `impl` is `private`. ### Make keywords either optional or required in separate definitions When a prior declaration exists, keywords are _disallowed_ in separate definitions. We could instead allow keywords, making them either optional or required. This would allow developers to determine visibility when reading a definition. The downside of this is that optional keywords can be confusing. For example: - `api` file: ``` class Foo { private fn Bar(); private fn Wiz(); }; ``` - `impl` file: ``` fn Foo.Bar() { ...impl... } private fn Foo.Wiz() { ...impl... } fn Baz() { ...impl... } ``` In an "optional" setup, the above is valid code. However, the lack of a `private` keyword on `Foo.Bar` may lead a developer to conclude that it's public without checking the `api` file (particularly because `Foo.Wiz` is explicitly private), when it's actually private. This is an accident that could also occur on refactoring; for example, removing the keyword on the `impl` version of `Foo.Wiz` would be valid but does not make it public. A response may be to make keywords required to match, so that reading the `impl` file would have a compiled guarantee of correctness, avoiding confusion. However, consider a similar example: - `api` file: ``` class Foo { fn Bar(); private fn Wiz(); }; ``` - `impl` file: ``` fn Foo.Bar() { ...impl... } private fn Foo.Wiz() { ...impl... } fn Baz() { ...impl... } ``` In this example, `Foo.Bar` is public, and that may lead developers to conclude that `Baz` is public. This could be corrected by requiring `private` on `Baz`, but we are hesitant to do that per [Default `impl` to public](#default-impl-to-public). There is still some risk of confusion if the forward declaration and separate definition are both in the `api` file. For example: ``` private fn PrintLeaves(Node node); fn PrintNode(Node node) { Print(node.value); PrintLeaves(node); ); fn PrintLeaves(Node node) { for (Node leaf : node.leaves) { PrintNode(leaf); } } ``` In this, a reader may read the `PrintLeaves` definition and incorrectly conclude that it is implicitly `public` because (a) it has no keywords and (b) it is in the `api` file. This will be addressed as part of [Open question: Calling functions defined later in the same file #472](https://github.com/carbon-language/carbon-lang/issues/472#issuecomment-915407683). Overall, the decision to _disallow_ keywords on separate definitions means that `impl` files shouldn't have any visibility keywords at the file scope (they will on classes), which is considered a writability improvement while keeping the `api` as the single source of truth for `public` entities, addressing readability. ================================================ FILE: proposals/p0777.md ================================================ # Inheritance [Pull request](https://github.com/carbon-language/carbon-lang/pull/777) ## Table of contents - [Problem](#problem) - [Background](#background) - [Proposal](#proposal) - [Rationale based on Carbon's goals](#rationale-based-on-carbons-goals) - [Alternatives considered](#alternatives-considered) - [Classes are final by default](#classes-are-final-by-default) - [Allow keywords to be written when they would have no effect](#allow-keywords-to-be-written-when-they-would-have-no-effect) - [Allow `final class`](#allow-final-class) - [Allow `partial FinalClass`](#allow-partial-finalclass) - [Different virtual override keywords](#different-virtual-override-keywords) - [Different virtual override keyword placement](#different-virtual-override-keyword-placement) - [Final methods](#final-methods) - [Constructors](#constructors) - [Different keyword than `partial`](#different-keyword-than-partial) - [Partial facet for extensible classes](#partial-facet-for-extensible-classes) - [Derived constructors set base fields](#derived-constructors-set-base-fields) - [No partial facet](#no-partial-facet) - [Only mixins and interfaces](#only-mixins-and-interfaces) - [Swift approach](#swift-approach) - [C# approach](#c-approach) - [Construct function](#construct-function) - [Implicit abstract classes](#implicit-abstract-classes) - [No extensible objects with non-virtual destructors](#no-extensible-objects-with-non-virtual-destructors) - [Separate "exact" and "or derived" variations on types](#separate-exact-and-or-derived-variations-on-types) - [Separate "exact" and "or derived" variations on pointers](#separate-exact-and-or-derived-variations-on-pointers) ## Problem We would like to address the use cases for inheritance described in proposal [#561: Basic classes: use cases, struct literals, struct types, and future work](https://github.com/carbon-language/carbon-lang/pull/561), including providing a migration path for C++ types and programmers currently using inheritance. ## Background This is a follow up to these previous proposals defining classes: - [#561: Basic classes: use cases, struct literals, struct types, and future work](https://github.com/carbon-language/carbon-lang/pull/561) - [#722: Nominal classes and methods](https://github.com/carbon-language/carbon-lang/pull/722) ## Proposal The proposal is to update [docs/design/classes.md](/docs/design/classes.md) as described in [this PR](https://github.com/carbon-language/carbon-lang/pull/777). ## Rationale based on Carbon's goals This particular proposal is focusing on these Carbon goals: - [That code is easy to read, understand, and write](/docs/project/goals.md#code-that-is-easy-to-read-understand-and-write), particularly addressing mechanisms for writing object-oriented code familiar to C++ programmers. We have attempted to include support for most C++ usage patterns, rather than a lot of safety restrictions following the maxim that Carbon should "focus on encouraging appropriate usage of features rather than restricting misuse". - [That Carbon supports writing performance-critical software](/docs/project/goals.md#performance-critical-software). This includes removing cruft from the produced binaries by limiting support for multiple and virtual inheritance, avoiding redundantly setting the vptr when constructing objects, and making classes final by default. - [That Carbon has practical safety and testing mechanisms](/docs/project/goals.md#practical-safety-and-testing-mechanisms). This proposal addresses safety concerns with non-virtual destructors by making the unsafe case syntactically visible, and providing the tools for safer alternative approaches by using [final classes](). ## Alternatives considered ### Classes are final by default This is a divergence from C++, but has a number of benefits: - Classes are not in general safe for derivation unless they are explicitly designed to be. One example is that a class `X` may assume that any value of type `X*` should be treated as a pointer to exactly that type. - Final classes are easier to evolve, since there are no concerns that a newly added name will conflict with one in a derived class. - It is only safe to delete a pointer to a class that is final or has a virtual destructor. Making classes final by default means we can provide good ergonomics without sacrificing safety. - We want to encourage users to use composition or interfaces instead of inheritance unless they consciously decide that is the right solution for their use case. - Labelling a class that supports inheritance as a `base class` puts information about the class important to readers up front, rather than leaving them wondering whether the class supports inheritance or just accidentally used the default. - The compiler can easily diagnose that a class is mistakenly final when you attempt to extend it. It is much more difficult to determine that a class is mistakenly a base. - We expect there are some performance and code size benefits. ### Allow keywords to be written when they would have no effect In both of these cases, we decided it was better that there was only one way to write the code, than allow a keyword to be written in a situation where it only acted as a comment without changing the meaning of the code. #### Allow `final class` We considered allowing `final class` as a synonym for `class` without a `base` prefix, but we didn't feel it would provide benefit justifying the additional complexity. #### Allow `partial FinalClass` We considered allowing `partial` to be used for all constructor functions. For a final class, `partial FinalClass` would be an alias for `FinalClass`. FIXME **Answer: No** ### Different virtual override keywords Instead of `virtual` we considered `base`. This would create a parallel structure between `abstract` and `base` classes on one hand, and `abstract` and `base` methods on the other. However, we felt like this was an important case to maintain continuity with C++. Instead of `abstract` we considered: - `virtual` ... `= 0` - `required` - `pure virtual` - `virtual` ... `pure` We didn't like using a suffix like `= 0` or `= pure`, since it is in place of an implementation but we wouldn't put it out of line like an implementation. We didn't like `= 0` despite it being consistent with C++ because it didn't reflect the meaning in the way a keyword could, and keywords are easier to look up in search engines. We might reconsider `required` if we decide that we want to use that keyword in other places, such as in a mixin. In the end, we went with `abstract` since it is used in other languages, such as Java, and could stand on its own without having to be paired with `virtual`. Instead of `impl` we considered using `override` as done in C++, with the difference that the keyword would be mandatory in Carbon. There were a few concerns with using `override`: - It doesn't match the case where the base class doesn't have an implementation to override because it is abstract or pure virtual. - Concern about confusion between overriding and overloading. The choice of `impl` is intended to draw a parallel with implementing interfaces. If we went with `override`, we might change the other keywords to match, using `must_override` instead of `abstract` and `overridable` instead of `virtual`. We might consider switching to `overridable` if we decide that is a keyword we would use in other contexts that allow overriding without using runtime dispatch using a virtual table, for example interfaces or mixins. ### Different virtual override keyword placement We considered putting the virtual override keyword after the function's signature: ``` base class MyBaseClass { fn Overridable[me: Self]() -> i32 virtual { return 7; } } ``` Rationale for putting the keyword to the right: - This is less salient information than the name and signature of the function, particularly for callers of the API. - This choice allows the function names to line up vertically. - This keyword is about the implementation. For example, it says whether there is an implementation of this method in this class at all. Unless you are extending the class, callers would not notice replacing a virtual function with a non-virtual function calling a private virtual function. The concern was that while this choice makes the API easier to read for users calling methods on the base class, it makes it significantly harder to read for users extending the base class. And extending the base class was a common enough and important enough use case that this change was not worth also trading off familiarity from C++. **Reference:** This was decided in issue [#754: Placement of the word virtual](https://github.com/carbon-language/carbon-lang/issues/754). ### Final methods We considered allowing `final` to be used as a virtual override keyword, to mark [non-overridable methods](). This is something we might change in the future, based on demonstrated need, but for now we didn't see the use cases for it occurring in practice that would justify its addition to the language. This was based on a few reasons. - Even though this exists in C++ code, it can be dropped without changing meaning. - We expect you can get similar performance benefits from profile guided optimizations and devirtualization. - We imagined that we might use this keyword in the future with a different meaning, such as "no shadow". - We saw little harm in omitting this for now, even if we decide to add it in later if and when we see that it would be useful for Carbon programmers. Note that if we were to add final methods, they would be allowed to be implemented in the partial facet type of a base class. ### Constructors [Perils of Constructors](https://matklad.github.io/2019/07/16/perils-of-constructors.html) gives a great overview of the challenges with constructors. It expresses the advantages of the factory function approach used by Rust, but observes that there are some difficulties making it work with inheritance and placement. Proposal [#257: Initialization of memory and variables](https://github.com/carbon-language/carbon-lang/pull/257/files) addresses the placement component of construction, and this proposal extends that approach to work with inheritance using the `partial` facet. This approach has some benefits: - Simplicity by not having constructors with different rules from other functions. - No initializer list [shadow world](https://gbracha.blogspot.com/2014/09/a-domain-of-shadows.html). - No distinction between constructors and other factory functions. - No need for rules and syntax for [delegating](https://docs.microsoft.com/en-us/cpp/cpp/delegating-constructors?view=msvc-160) or [convenience](https://docs.swift.org/swift-book/LanguageGuide/Initialization.html#ID217) constructors. - No need to have special handling of errors. - Ability to write code in a derived constructor before calling its base constructor. - No static analysis of code, potentially with control flow, to ensure that all fields are initialized. We considered several alternatives, particularly in issue [#741: Constructing an object of a derived type](https://github.com/carbon-language/carbon-lang/issues/741). #### Different keyword than `partial` In issue [#741](https://github.com/carbon-language/carbon-lang/issues/741), we considered other keywords instead of `partial` to designate the facet of the type for construction. - `base`: Intended to indicate a [base class subobject](https://en.cppreference.com/w/cpp/language/derived_class), but was confusing with other uses of the word "base" to mean "the base class of a type." - `as_base`: Intended to address the confusion around using `base` by adding a preposition that indicates this isn't the "base of the type." However, it introduces confusion with the `as` operator used to cast. - `bare`: Too far from the intended meaning. - `impl`: This keyword is already being used for other things that are too different from this use. - `novirt`: Describes something about the effect of this keyword, but not why you are using it. - `exact`: Intended to suggest this is the is a use of the exact type, not a possibly derived type. This, like `novirt`, was too focused on the effect of the keyword and wasn't suggestive enough of why it was being used. Also this didn't capture why this keyword would allow you to instantiate an abstract base class. - `ctor`, `construct`, `constructor`: These were the wrong part of speech. The type is not the constructor, the function returning this type is. - `under_construction`: Too long. For the construction-related options, there were also concerns that we might also use this type during destruction of an object. #### Partial facet for extensible classes In issue [#741](https://github.com/carbon-language/carbon-lang/issues/741), we considered recommending using the `partial` facet in constructors of extensible classes more strongly than the current proposal. Ultimately we decided it wasn't necessary: - The behavior was more consistent with C++. - The consequences of not using `partial` are small enough, matching C++ instead of a possible improvement. - The rule for when to use `partial` was too subtle and hard to explain. - Ending up with a `partial` type because you declared a variable with type `auto` seemed like a bad user experience. - Writing both a `protected` constructor returning a `partial` type for descendants and a public constructor returning the full type improved the ergonomics for using the class but seemed like painful boilerplate for authors of the class. #### Derived constructors set base fields In issue [#741](https://github.com/carbon-language/carbon-lang/issues/741), we considered making the constructor of a derived class explicitly set the fields of the base class without delegating to the base constructor, avoiding the problem of trying to instantiate a base class that might be abstract. It had some clear disadvantages including: - There would be no way to make members of a base class private, particularly their names. - It wasn't clear how to interoperate with C++. #### No partial facet In issue [#741](https://github.com/carbon-language/carbon-lang/issues/741), we considered allowing instantiating abstract base classes so they could be used to initialize derived classes, but this was a safety regression from C++. #### Only mixins and interfaces In issue [#741](https://github.com/carbon-language/carbon-lang/issues/741), we considered splitting inheritance into separate mechanisms for subtyping and implementation reuse. Interfaces would represent APIs for subtyping purposes and implementations would be defined in mixins. This was a major divergence from C++ and would likely cause problems for both programmers and interoperation. #### Swift approach [Swift initialization](https://docs.swift.org/swift-book/LanguageGuide/Initialization.html) requires the user to define a special `init` method that initializes the fields of the object before calling any methods on it. For a [derived class](https://docs.swift.org/swift-book/LanguageGuide/Initialization.html#ID216), this is then followed by a call to the base's `init` method. After that is done, there is a [second phase of initialization](https://docs.swift.org/swift-book/LanguageGuide/Initialization.html#ID220) that can operate on an object that at least has all fields initialized. This means method calls are allowed, even though it is possible that not all invariants of the class have been established for the object. ``` class MyClass extends BaseClass { fn init(...) { me.derived_field = ...; super.init(base_arguments); phase_2_after_fields_are_set(); } } ``` This approach has some nice properties, for example it supports calling virtual methods in the base class' `init` method and getting the derived implementation. However it has some disadvantages for our purposes: - Relies on potentially fragile static analysis of the code to determine that all fields are initialized. - Init methods have a number of restrictions, such as no method calls in the first phase, to avoid potentially unsafe use of a partially initialized value. So init methods are not ordinary method calls even though they superficially look quite similar. - Unclear what destructors to run if there is a failure partway through construction. - Would not interoperate well with C++, since derived fields are initialized before base fields. This also means that the initial value for derived fields can't be set using the values of the base fields set in the base's init method. #### C# approach [C# Constructors](https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/classes-and-structs/constructors) have names that match their class, and the constructor of a derived class starts with a call to the base class' constructor using this `: base(...)` syntax between the parameter list and function body: ``` class MyClass extends BaseClass { fn MyClass(...) : base(base_arguments) { me.derived_field = ...; phase_2_after_fields_are_set(); } } ``` Alternatively, a constructor can delegate to another constructor using `: this(...)` syntax instead. Disadvantages for our purposes: - Doesn't allow you to write code before calling the base class' constructor. - Constructors have a special syntax and are not ordinary functions. However those differences would be familiar to C++ programmers. - Relies on potentially fragile static analysis of the code to determine that all fields are initialized and that it is safe to call methods. - Unclear what destructors to run if there is a failure partway through construction. #### Construct function We rejected prior Carbon proposal [#98](https://github.com/carbon-language/carbon-lang/pull/98), where the user's initialization function called a compiler-provided function to create the object once the base constructor arguments and derived field values were known. ``` class MyClass extends BaseClass { fn operator create(...) -> Self { ... returned var result: Self = construct(base_arguments, {.derived_field = ...}); phase_2_after_fields_are_set(); return result; } } ``` This avoids giving a name to the object being constructed until its fields have been initialized, without relying on static analysis, making it clearer what destructors should run in the case of failure, though the current proposal is still clearer. Disadvantages for our purposes: - Complexity when initializing fields that depended on the address of the object being constructed. - Constructors are special, not ordinary functions. Note that this prevents using the values assigned to the fields in the base's constructor to determine the initial values for the derived fields. We could address this concern by splitting the special `construct` function into two pieces: ``` class MyClass extends BaseClass { fn operator create(...) -> Self* { ... var parent: BaseClass* = create_base(base_arguments); // Can determine the values for derived fields here. var result: Self* = construct(parent, {.derived_field = ...}); phase_2_after_fields_are_set(); return result; } } ``` This adds some complexity, but interoperates better with C++. ### Implicit abstract classes We considered following C++'s approach of making classes abstract based on having any pure virtual methods. This leads to awkward workarounds where you might mark a class' destructor as pure virtual even though it is still implemented. We decided to use a different introducer for abstract classes since this is very important for readers, helping them determine the role of the class and whether this is the class they are looking for. We thought that if you were to change a class to being abstract, you would likely also update its description and rename it at the same time, since that was such an important change to the interface of the class. ### No extensible objects with non-virtual destructors We considered forbidding constructing extensible objects with non-virtual destructors. This was to avoid getting into a state where a type could be used as a local variable but not allocated on the heap. It was also identified as an advanced use case that didn't need to be as convenient to write, and so the overhead of using both a final and an abstract type in place of an extensible type might be more acceptable and would give much more clarity to what a given type represented. However, this was a noticeable divergence from C++ where extensible objects are the default. We decided that consistency with both C++ and extensible classes with virtual methods was more valuable. The error when deleting a base class with a non-virtual destructor would be very clear and offer useful alternatives: making the destructor virtual, making a final class, or using `unsafe_delete`. This matched the idea that [Carbon should "focus on encouraging appropriate usage of features rather than restricting misuse"](/docs/project/goals.md#code-that-is-easy-to-read-understand-and-write). This topic was discussed in issue [#652: Extensible classes with or without vtables](https://github.com/carbon-language/carbon-lang/issues/652) and [on Discord](https://discord.com/channels/655572317891461132/708431657849585705/880471907407888404). ### Separate "exact" and "or derived" variations on types Issue [#652](https://github.com/carbon-language/carbon-lang/issues/652) considered many variations on ways to have two different types for values depending on whether they represented a value with an exact type, or a value that could be a derived type. We ultimately decided that asking users to use both types would be too much cognitive overhead, and would be a usability regression from C++. ### Separate "exact" and "or derived" variations on pointers Issue [#652](https://github.com/carbon-language/carbon-lang/issues/652) considered instead having two kinds of pointers. One would point to a value of a specific known type, and the other would point to a value of some derived type. This has two disadvantages compared to having the variations be on the types of the values. - The distinction between pointer types is meaningless for non-extensible types. - We still need the distinction for value types to give the right type to the result of dereferencing the pointer. ================================================ FILE: proposals/p0818/regular_equivalence_classes.md ================================================ # Regular equivalence classes ## Problem For generics, users will define _interfaces_ that describe types. These interfaces may have associated types (see [Swift](https://docs.swift.org/swift-book/LanguageGuide/Generics.html#ID189), [Rust](https://doc.rust-lang.org/rust-by-example/generics/assoc_items/types.html)): ``` interface Container { let Elt:! Type; let Slice:! Container; let Subseq:! Container; } ``` The next step is to express constraints between these associated types. - `Container.Slice.Elt == Container.Elt` - `Container.Slice.Slice == Container.Slice` - `Container.Subseq.Elt == Container.Elt` - `Container.Subseq.Slice == Container.Slice` - `Container.Subseq.Subseq == Container.Subseq` Languages commonly express these constraints using `where` clauses, as in [Swift](https://docs.swift.org/swift-book/LanguageGuide/Generics.html#ID557) and [Rust](https://doc.rust-lang.org/rust-by-example/generics/where.html), that directly represent these constraints as equations. ``` interface Container { let Elt:! Type; let Slice:! Container where .Elt == Elt and .Slice == Slice; let Subseq:! Container where .Elt == Elt and .Slice == Slice and .Subseq == Subseq; } ``` These relations give us a semi-group, where in general deciding whether two sequences are equivalent is undecidable, see [Swift type checking is undecidable - Discussion - Swift Forums](https://forums.swift.org/t/swift-type-checking-is-undecidable/39024). If you allow the full undecidable set of `where` clauses, there are some unpleasant options: - Possibly the compiler won't realize that two expressions are equal even though they should be. - Possibly the compiler will reject some interface declarations as "too complicated", due to "running for too many steps", based on some arbitrary threshold. Furthermore, the algorithms are expensive and perform extensive searches. ### Goal The goal is to identify some restrictions such that: - We have a terminating algorithm to decide if a set of constraints meets the restrictions. - For constraints that meet the restrictions we have a terminating algorithm for deciding which expressions are equal. - Expected use cases satisfy the restrictions. The expected cases are things of these forms: - `X == Y` - `X == Y.Z` - `X == X.Y` - `X == Y.X` - and _some_ combinations and variations For ease of exposition, I'm going to assume that all associated types are represented by single letters, and omit the dots `.` between them. ## Idea Observe that the equivalence class of words you can rewrite to in these expected cases are described by a regular expression: - `X` can be rewritten to `(X|Y)` using the rewrite `X == Y` - `X` can be rewritten to `XY*` using the rewrite `X == XY` - `X` can be rewritten to `Y*X` using the rewrite `X == YX` These regular expressions can be used instead of the rewrite rules: anything matching the regular expression can be rewritten to anything else matched by the same regular expression. This means we can take any sequence of letters, replace anything that matches the regular expression by the regular expression itself, and get a regular expression that matches the original sequence of letters. We can even get a shortest/smallest representative of the class by dropping all the things with a `*` and picking the shortest/smallest between alternatives separated by a `|`. ### Question Given a set of rewrite equations, is there a terminating algorithm that either: - gives a finite set of regular expressions (or finite automata) that describes their equivalence classes, or - rejects the rewrites. We would be willing to reject cases where there are more equivalence classes than rewrite rules. An example of this would be the rewrite rules `A == XY` and `B == YZ` have equivalence classes `A|XY`, `B|YZ`, `XYZ|AZ|XB`. ## Problem In particular, we've identified two operations for combining two finite automata depending on how they overlap: - **Containment operation:** If we have automatas `X` and `Y`, where `Y` completely matches a subsequence matched by `X`, then we need to extend `X` to include all the rewrites offered by `Y`. For example, if `X` is `AB*` and `Y` is `(B|C)`, then we can extend `X` to `A(B|C)*`. - **Union operation:** If we have automatas `X` and `Y`, and there is a string `ABC` such that `X` matches the prefix `AB` and `Y` matches the suffix `BC`, then we need to make sure there is a rule for matching `ABC`. Even though it is straightforward to translate a single rewrite rule into a regular expression that matches at least some portion of the equivalence class, to match the entire equivalence classes we need to apply these two operations in ways that can run into difficulties. One concern is that an individual regular expression might not cover the entire equivalence for a rewrite rule. We may need to apply the union and containment operations to combine the automata _with itself_. For example, we may convert the rewrite rule `AB == BA` into the regular expression `AB|BA`, but this doesn't completely describe the set of equivalence classes that this rule creates, since it triggers the union operation twice to add `AAB|ABA|BAA` and `ABB|BAB|BBA`. These would also trigger the union operation, and so on indefinitely, so the rewrite rule `AB == BA` does not have a finite set of regular equivalence classes. Similarly the rule `A == BCB` has an infinite family of equivalence classes: `BCB|A`, `BCBCB|ACB|BCA`, `BCBCBCB|ACBCB|ACA|BCACB|BCBCA`, ... The other concern is that even in the case that each rule by itself can be faithfully translated into a regular expression that captures the equivalence classes of the rewrite, attempts to combine multiple rules using the two operations might never reach a fixed point. In both cases, we are looking for criteria that we can use to decide if the application of the operations will terminate. If it would not terminate, then we need to report an error to the user instead of applying those two operations endlessly. ## Examples ### Regular single rules - Anything where there are no shared letters between the two sides, and no side has a prefix matching a suffix - `A == B` => `A|B` - `A == BC` => `A|BC` - `A == BBC` => `A|BBC` - `A == BCC` => `A|BCC` - `A == AB` => `AB*` - `A == BA` => `B*A` - `A == AA` => `AA*` or `A*A` or `A+` - `A == ABC` => `A(BC)*` - `A == ABB` => `A(BB)*` - `A == AAA` => `A(AA)*` - `A == BCA` => `(BC)*A` - `A == BBA` => `(BB)*A` - `A == ACA` => `A(CA)*` or `(AC)*A` ### Regular combinations - `A==AB` + `A==AC` => `AB*` + `AC*` => `A(B|C)*` - `A==AB` + `B==BC` => `AB*` + `BC*` => `A(B|C)*`, `BC*` with relation `A(B|C)* BC* == A(B|C)*`; Note: `A == AB == ABC == AC` - `A==AB` + `C==CB` => `AB*`, `CB*`, no relations - `A==AB` + `A==CA` => `AB*` + `C*A` => `C*AB*` - `A==AB` + `C==BC` => `AB*` + `B*C` => `AB*`, `B*C`, where if you have `AB*C` it doesn't matter how you split the `B`s between `AB*` and `B*C`. - `A==AB` + `B==AB` => `AB*` + `A*B` => `(A|B)+` - `A==AB` + `B==CB` => `AB*` + `C*B` => `A(C*B)*`, `C*B` with `A(C*B)* C*B` -> `A(C*B)*` - `A==AB` + `B==CB` + `C==CD` => `AB*` + `C*B` + `CD*` => `A((CD*)*B)*`, `(CD*)*B`, `CD*` - `A==ABC` + `D==CB` => `A(BC)*` + `(CB|D)` => `A(BD*C)*`, `A(BD*C)*BD*`, `(CB|D)` - `A==ABBBBB` + `A==ABBBBBBBB` (or `A==AB^5` + `A==AB^8`)=> `A(BBBBB)*` + `A(BBBBBBBB)*` => `AB*`, since `A=AB^8=AB^16=AB^11=AB^6=AB` ### Not regular - `AB == BA`, has equivalence classes: `AB|BA`, `AAB|ABA|BAA`, `ABB|BAB|BBA`, ... - Similarly: `AB==C` + `C==BA` - `ABC == CBA`, has equivalence classes: `ABC|CBA`, `ABABC|ABCBA|CBABA`, `ABCBC|CBABC|CBCBA`, ... - `A == BAB`, involves back references or counting the number of `B`s: `A|BAB|BBABB|BBBABBB|`... - `A == BAC`, involves matching the number of `B`s to `C`s: `A|BAC|BBACC|BBBACCC|`... - `A == BCB`, has equivalence classes: `BCB|A`, `BCBCB|ACB|BCA`, `BCBCBCB|ACBCB|ACA|BCACB|BCBCA`, ... - `A == BB`, has equivalence classes: `A|BB`, `BBB|AB|BA`, `(A|BB)(A|BB)|BAB`, ... - `A == BBB`, has equivalence classes: `A|BBB`, `BBBB|AB|BA`, `BBBBB|ABB|BAB|BBA`, ... - `AA == BB`, has equivalence classes: `AA|BB`, `AAA|ABB|BBA`, `BAA|BBB|AAB`, ... - `AB == AA`, has equivalence classes: `A(A|B)`, `A(A|B)(A|B)`, `A(A|B)(A|B)(A|B)`, ... - `AB == BB`, similarly - `AB == BC`, has equivalence classes: `(AB|BC)`, `(AAB|ABC|BCC)`, ..., `A`^i`BC`^(N-i) - `A == AAB` and `A == BAA` ## References See these notes from discussions: - [Regular equivalence classes 2021-09-23](https://docs.google.com/document/d/1iyL7ZDfWT6ZmDSz6Fp8MrLcl5nOQc4ItQbeL3QlVXYU/edit#) - [Carbon minutes (rolling): Open discussions: 2021-09-27](https://docs.google.com/document/d/1UelNaT_j61G8rYp6qQZ-biRddTuGcxJtqXxrVbjB9rA/edit#heading=h.qh4b1gy7o5el) - [Carbon minutes (rolling): Open discussions: 2021-09-28](https://docs.google.com/document/d/1UelNaT_j61G8rYp6qQZ-biRddTuGcxJtqXxrVbjB9rA/edit#heading=h.9a1r39mla9ho) - [Regular equivalence classes 2 2021-10-01](https://docs.google.com/document/d/1xGDRSV6q534-CoDZnzuZJmzhty2yPki2ZfDDPLSHYek/edit#) ================================================ FILE: proposals/p0818.md ================================================ # Constraints for generics (generics details 3) [Pull request](https://github.com/carbon-language/carbon-lang/pull/818) ## Table of contents - [Problem](#problem) - [Background](#background) - [Proposal](#proposal) - [Rationale based on Carbon's goals](#rationale-based-on-carbons-goals) - [Alternatives considered](#alternatives-considered) - [Alternatives to `where` clauses modifying type-of-types](#alternatives-to-where-clauses-modifying-type-of-types) - [Different keyword than `where`](#different-keyword-than-where) - [Inline constraints instead of `.Self`](#inline-constraints-instead-of-self) - [Self reference instead of `.Self`](#self-reference-instead-of-self) - [Implied constraints across declarations](#implied-constraints-across-declarations) - [No implied constraints](#no-implied-constraints) - [Type inequality constraints](#type-inequality-constraints) - [`where .Self is ...` could act like an external impl](#where-self-is--could-act-like-an-external-impl) - [Other syntax for external impl](#other-syntax-for-external-impl) - [Other syntax for must be legal type constraints](#other-syntax-for-must-be-legal-type-constraints) - [Using only `==` instead of also `=`](#using-only--instead-of-also-) - [Automatic type equality](#automatic-type-equality) - [Restricted equality constraints](#restricted-equality-constraints) - [No explicit restrictions](#no-explicit-restrictions) ## Problem We want Carbon to have a high quality generics feature that achieves the goals set out in [#24](https://github.com/carbon-language/carbon-lang/pull/24). This is too big to land in a single proposal. This proposal continues [#553](https://github.com/carbon-language/carbon-lang/pull/553) defining the details of: - adapters - associated types and other constants - parameterized interfaces ## Background This is a follow on to these previous generics proposals: - [#24: Generics goals](https://github.com/carbon-language/carbon-lang/pull/24) - [#447: Generics terminology](https://github.com/carbon-language/carbon-lang/pull/447) - [#524: Generics overview](https://github.com/carbon-language/carbon-lang/pull/524) - [#553: Generics details part 1](https://github.com/carbon-language/carbon-lang/pull/553) - [#731: Generics details 2: adapters, associated types, parameterized interfaces](https://github.com/carbon-language/carbon-lang/pull/731) Some of the content for this proposal was extracted from a larger [Generics combined draft proposal](https://github.com/carbon-language/carbon-lang/pull/36). ## Proposal This is a proposal to add two sections to [this design document on generics details](/docs/design/generics/details.md). ## Rationale based on Carbon's goals Much of this rationale for generics was captured in the [Generics goals proposal](https://github.com/carbon-language/carbon-lang/pull/24). The specific decisions for this constraint design were motivated by: - [Software and language evolution](/docs/project/goals.md#software-and-language-evolution) - The manual generic type equality approach being proposed could be extended to automatically observe more types as being equal without breaking existing code. - Adding or growing an `observe` statement has been designed to be non-breaking since they can't introduce name conflicts, only make more code legal. - This constraint system is expressive and does not have artificial limits that would be needed to support automatic type equality. - [Code that is easy to read, understand, and write](/docs/project/goals.md#code-that-is-easy-to-read-understand-and-write) - Attaching `where` clauses to type-of-types addresses more use cases than attaching them to declarations, allowing the design to be smaller, see [#780](https://github.com/carbon-language/carbon-lang/issues/780). - The manual generic type equality approach is very simple and easy to understand how it works. It unfortunately is a bit more verbose, leading to more to read and write. - [Fast and scalable development](/docs/project/goals.md#fast-and-scalable-development) - The manual generic type equality approach does much less work at compile time than the automatic approach. The extra work would only be done to produce an error message that includes the changes to the source needed to avoid repeating that work. In effect, the source code holds a cache of the facts needed to compile it. ## Alternatives considered ### Alternatives to `where` clauses modifying type-of-types Issue [#780: How to write constraints](https://github.com/carbon-language/carbon-lang/issues/780) considered other forms that constraints could be written. One alternative is to place `where` clauses on declarations instead of types. ``` fn F[W:! Type, V:! D & E](v:V) -> W where V.X == W, V.Y == W, V.Z = i32; ``` The constraints are written after the names of types are complete, and so constraints are naturally written in terms of those names. This is a common approach used by several languages including Swift and Rust, and so is likely familiar to a significant fraction of our users. The downside of this approach is it doesn't let you put constraints on types that never get a names, such as in ["protocols as types" or "existential types" in Swift](https://docs.swift.org/swift-book/LanguageGuide/Protocols.html#ID275) and "trait objects" in Rust ([1](https://doc.rust-lang.org/book/ch17-02-trait-objects.html), [2](https://doc.rust-lang.org/reference/types/trait-object.html)). This is a problem in practice, since those are cases where constraints are needed, since for type safety any associated types need to be constrained to be a specific concrete type. This motivated Rust to add another syntax just for specifying specific concrete types for associated types in the argument passing style. It is also motivation for Swift to consider a feature they call ["generalized existentials"](https://github.com/apple/swift/blob/main/docs/GenericsManifesto.md#generalized-existentials), that is very similar to this proposal. Another alternative is to generalize the argument passing approach Rust allows to handle more kinds of constraints. ``` fn F[W:! Type, V:! D{.X = W} & E{.Y = W, .Z = i32}](v: V) -> W; ``` This would treat constraints on interface parameters and associated types in a more uniform way, but doesn't handle the full breadth of constraints we would like to express. We at some point believed that this framing of the constraints made it harder to express undecidable type equality problems and easier to enforce restrictions that would make the constraints decidable, but we eventually discovered that this formulation had the essentially the same difficulties. We considered a variation of argument passing that we called "whole expression constraint intersections." ``` fn F[W:! Type, V:! D & E & {.X = W, .Y = W, .Z = i32}](v: V) -> W; ``` This variation made it easy to set associated types from two interfaces with the same name to the same value, but introduced concerns that it would stop `&` from being associative and commutative. It was not chosen because it had the same downsides as the argument passing approach. ### Different keyword than `where` We considered other keywords for introducing constraints, such as: - `requires`, like [C++](https://en.cppreference.com/w/cpp/language/constraints) - `with` - `if`, like [D](http://rosettacode.org/wiki/Constrained_genericity#D) - `when`, like [F#](http://rosettacode.org/wiki/Constrained_genericity#F.23). The most common choice across popular languages is `where`, including: - [C#](http://rosettacode.org/wiki/Constrained_genericity#C.23) - [Haskell](http://rosettacode.org/wiki/Constrained_genericity#Haskell) - [Rust](https://doc.rust-lang.org/rust-by-example/generics/where.html) - Swift ([1](https://docs.swift.org/swift-book/LanguageGuide/Generics.html#ID553), [2](https://docs.swift.org/swift-book/LanguageGuide/Generics.html#ID557)) While C++ is particularly important to our target userbase, Carbon's constraints work more similarly to languages with generics like Rust and Swift. ### Inline constraints instead of `.Self` We considered the possibility of using [named constraints](/docs/design/generics/details.md#named-constraints) instead of `.Self` for [recursive constraints](/docs/design/generics/details.md#recursive-constraints). This comes under consideration since `.Self` outside the named constraint is the same as `Self` inside. However, you can't always avoid using `.Self`, since naming the constraint before using it doesn't allow you to define this `Container` interface: ``` interface Container { ... let SliceType:! Container where .SliceType == .Self; ... } ``` The problem that arises is to avoid using `.Self`, we would need to define the named constraint using `Container` before `Container` is defined: ``` constraint ContainerIsSlice { // Error: forward reference extends Container where Container.SliceType == Self; } interface Container { ... let SliceType:! ContainerIsSlice; ... } ``` To work around this problem, we could allow the named constraint to be defined inline in the `Container` definition: ``` interface Container { ... constraint ContainerIsSlice { extends Container where Container.SliceType == Self; } let SliceType:! ContainerIsSlice; ... } ``` This alternative seems too cumbersome. ### Self reference instead of `.Self` Another alternative instead of using `.Self` for [recursive constraints](/docs/design/generics/details.md#recursive-constraints), is to use the name of the type being declared inside the type declaration, as in `T:! HasAbs(.MagnitudeType = T)`. This had two downsides: - Using the name of the type before it is finished being defined created questions about what that name meant. Using the reserved token sequence `.Self` instead makes it clearer that it obeys different rules than other identifier references. For example, that we don't allow members to be accessed. - It doesn't address use cases where we are defining a type-of-type that isn't associated with a type that has a name. ### Implied constraints across declarations If constraints on an associated type could be implied by any declaration in the interface, readers and the type checker would be required to scan the entire interface definition and perform many type declaration lookups to understand the constraints on that associated type. This is particularly important when those constraints can be obscured in recursive references to the same interface: ``` interface I { let A:! Type; let B:! Type; let C:! Type; let D:! Type; let E:! Type; let SwapType:! I where .A == B and .B == A and .C == C and .D == D and .E == E; let CycleType:! I where .A == B and .B == C and .C == D and .D == E and .E == A; fn LookUp(hm: HashMap(D, E)*) -> E; fn Foo(x: Bar(A, B)); } ``` This applies equally to parameters: ``` interface I(A:! Type, B:! Type, C:! Type, D:! Type, E:! Type) { let SwapType:! I(B, A, C, D, E); let CycleType:! I(B, C, D, E, A); fn LookUp(hm: HashMap(D, E)*) -> E; fn Foo(x: Bar(A, B)); } ``` All of the type arguments to `I` must actually implement `Hashable`, since [an adjacent swap and a cycle generate the full symmetry group on 5 elements](https://www.mathcounterexamples.net/generating-the-symmetric-group-with-a-transposition-and-a-maximal-length-cycle/)). And additional restrictions on those types would depend on the definition of `Bar`. For example, this definition ``` class Bar(A:! Type, B:! ComparableWith(A)) { ... } ``` would imply that all the type arguments to `I` would have to be comparable with each other. This propagation problem means that allowing constraints to be implied in this context is substantial, and potentially unbounded, work for the compiler and human readers. From this we conclude that we need the initial declaration part of an `interface`, type definition, or associated type declaration to include a complete description of all needed constraints. It is possible that this reasoning will apply more generally, but we will wait until we implement type checking to see what restrictions we actually need. For example, we might need to restate "parameterized type implements interface" constraints when it is on an associated type in a referenced interface, as in: ``` interface HasConstraint { let T:! Type where Vector(.Self) is Printable; } interface RestatesConstraint { // This works, since it restates the constraint on // `HasConstraint.T` that `U` is equal to. let U:! Type where Vector(.Self) is Printable; // This doesn't work: // ❌ let U:! Type; let V:! HasConstraint where .T == U; } ``` ### No implied constraints Issue [#809](https://github.com/carbon-language/carbon-lang/issues/809) considered whether Carbon would support implied constraints. The conclusion was that implied constraints was an important ergonomic improvement. The framing as a rewrite to a `where` restriction that did not affect the generic type parameter's unqualified API seemed like something that could be explained to users. Potential problems where a specialization of a generic type might allow that type to be instantiated for types that don't satisfy the normal constraints will be considered in the future along with the specialization feature. We also considered the alternative where the user would need to explicitly opt in to this behavior by adding `& auto` or `& implied_requirements` to their type constraint, as in: ``` fn LookUp[KeyType:! Type & auto](hm: HashMap(KeyType, i32)*, k: KeyType) -> i32; fn PrintValueOrDefault[KeyType:! Printable & auto, ValueT:! Printable & HasDefault] (map: HashMap(KeyType, ValueT), key: KeyT); ``` The feeling was that implied constraints were natural enough that they didn't need to be signaled by additional marking, with the accompanying ergonomic and brevity loss. ### Type inequality constraints You might want an inequality type constraint, for example, to control overload resolution: ``` fn F[T:! Type](x: T) -> T { return x; } fn F(x: Bool) -> String { if (x) return "True"; else return "False"; } fn G[T:! Type where .Self != Bool](x: T) -> T { // We need T != Bool for this to type check. return F(x); } ``` There are some problems with supporting this feature, however. [Negative reasoning in general has been a source of difficulties in the implementation of Rust's type system](http://aturon.github.io/tech/2017/04/24/negative-chalk/). This is a type of constraint that is more difficult to use since it would have to be repeated by any generic caller, since nothing else can generically establish two types are different. Our manual type equality approach will not be able to detect situations where the two sides of the inequality in fact have to be equal due to a sequence of equality constraints. In those situations, no type will be ever be able to satisfy the inequality constraint. We may be able to overcome these difficulties, but we don't expect this feature is required since neither Rust nor Swift support it. ### `where .Self is ...` could act like an external impl We considered making `T:! A where .Self is B` mean something different than `T:! A & B`. In particular, in the former case we considered whether `B` might be considered to be implemented externally for `T`. The advantage of this alternative is it gives a convenient way of not polluting the members of `T` with the members of `B`, however it wasn't clear how common that use case would be. Furthermore, it introduced an inconsistency with other associated types. For example: - `T:! Container where .ElementType = i32` means that `T.ElementType` has type `i32`. - Following that, given `T:! Container where .ElementType is Printable` and `x: T`, we expect that `x.Front().Print()` to be legal. - To be consistent with the last point, it seems like given `T:! Container where .Self is Printable` and `x: T`, then `x.Print()` should be legal as well. This matches Rust, where `T: Container` is considered a synonym for `T where T is Container`. [See the Discord discussion](https://discord.com/channels/655572317891461132/708431657849585705/895794334723637258). #### Other syntax for external impl In a [follow up Discord discussion](https://discord.com/channels/655572317891461132/708431657849585705/902728293080530954), we considered allowing developers to write `where .Foo as Bar is Type` to mean "`.Foo` implements interface `Bar` externally." We would consider this feature in the future once we saw a demonstrated need. ### Other syntax for must be legal type constraints Instead of `where HashSet(.Self) is Type` to say `.Self` must satisfy the constraints on being an argument to `HashSet`, we considered other syntaxes like `where HashSet(.Self) legal` and `where HashSet(.Self)`. The current choice is intended to be consistent with the syntax for "Parameterized type implements interface" and how implied constraints ensure that parameters to types automatically are given their needed constraints. ### Using only `==` instead of also `=` Originally this proposal only used `==` for both setting an associated type to a specific concrete type and saying it was equal to another type variable. The two cases affected the type differently. In the specific concrete type case, the original type-of-type for the associated type doesn't affect the API, you just get the API of the type. When equating two type variables, though, the unqualified member names are unaffected. The only change was some interfaces may be implemented externally. In [this Discord discussion](https://discord.com/channels/655572317891461132/708431657849585705/902713789735116821), we decided it might be clearer to use two different operators to reflect the different effect. The compiler or a tool can easily correct cases where the developer used the wrong one. However, using `=` comes with the downside that it doesn't follow the pattern of other constraints of looking like a boolean expression returning `true` when the constraint is satisfied. We would consider a different pair of operators here to address this point, perhaps `~` for type variables and `==` for concrete type case. ### Automatic type equality Other languages, such as Swift and Rust, don't require `observe` declarations or casting for the compiler to recognize two types as transitively equal. The problem is that there is no way to know how many equality constraints need to be considered before being able to conclude whether two type expressions are equal. In fact, the equality relations form a semi-group, where in general deciding whether two sequences are equivalent is undecidable, see [Swift type checking is undecidable - Discussion - Swift Forums](https://forums.swift.org/t/swift-type-checking-is-undecidable/39024). #### Restricted equality constraints One possible approach to this problem is to apply limitations to the equality constraints developers are allowed to express. The goal is to identify some restrictions such that: - We have a terminating algorithm to decide if a set of constraints meets the restrictions. - For constraints that meet the restrictions we have a terminating algorithm for deciding which expressions are equal. - Expected use cases satisfy the restrictions. The expected cases are things of these forms: - `X == Y` - `X == Y.Z` - `X == X.Y` - `X == Y.X` - and _some_ combinations and variations For example, here are some interfaces that have been translated to Carbon syntax from Swift standard library protocols: ``` interface IteratorProtocol { let Element:! Type; } interface Sequence { let Element:! Type; let Iterator:! IteratorProtocol where .Element == Element; } interface Collection { extends Sequence; let Index:! Type; let SubSequence:! Collection where .Element == Element and .SubSequence == SubSequence and .Index == Index; let Indices:! Collection where .Element == Index and .Index == Index and .SubSequence == Indices; } ``` One approach we considered is [regular equivalence classes](p0818/regular_equivalence_classes.md), however we have not yet been able to figure out how to ensure the algorithm terminates. This is an approach we would like to reconsider if we find solutions to this problem once can share this problem more widely. Other approaches we considered worked in simple cases but had requirements that could not be validated, since for example they were equivalent to solving the same word problem that makes transitive equality undecidable in general. #### No explicit restrictions If you allow the full undecidable set of `where` clauses, there are some unpleasant options: - Possibly the compiler won't realize that two expressions are equal even though they should be. - Possibly the compiler will reject some interface declarations as "too complicated", due to "running for too many steps", based on some arbitrary threshold. Either way, the compiler will have to perform a lot more work at compile time, slowing down builds. There is also a danger that composition of things that separately work or incremental evolution of working code could end up over a complexity or search depth threshold. In effect, there are still restrictions on what combinations of equality constraints are allowed, it is just that those restrictions are implicit in the specifics of the algorithm chosen and execution limits used. One approach that has been suggested by [Slava Pestov](https://forums.swift.org/u/Slava_Pestov) in the context of Swift, is to [formalize type equality as a term rewriting system](https://forums.swift.org/t/formalizing-swift-generics-as-a-term-rewriting-system/45175). Then the equality constraints in an interface or function declaration can be completed by running the Knuth-Bendix completion algorithm ([1](https://en.wikipedia.org/wiki/Knuth%E2%80%93Bendix_completion_algorithm#Description_of_the_algorithm_for_finitely_presented_monoids), [2](https://academic.oup.com/comjnl/article/34/1/2/427931)) for some limited number of steps. If the algorithm completes successfully, type expressions may then be efficiently canonicalized. However the algorithm can fail, or fail to terminate before hitting the threshold number of steps, in which case the source code would have to be rejected. See [this example implementation](https://gist.github.com/slavapestov/75dbec34f9eba5fb4a4a00b1ee520d0b). ================================================ FILE: proposals/p0820.md ================================================ # Implicit conversions [Pull request](https://github.com/carbon-language/carbon-lang/pull/820) ## Table of contents - [Problem](#problem) - [Background](#background) - [Proposal](#proposal) - [Rationale based on Carbon's goals](#rationale-based-on-carbons-goals) - [Alternatives considered](#alternatives-considered) - [C++ conversions](#c-conversions) - [Array-to-pointer conversions](#array-to-pointer-conversions) - [Function-to-pointer conversions](#function-to-pointer-conversions) - [Qualification conversions](#qualification-conversions) - [Integral promotions](#integral-promotions) - [Floating-point promotions](#floating-point-promotions) - [Integral conversions](#integral-conversions) - [Floating-point conversions](#floating-point-conversions) - [Pointer conversions](#pointer-conversions) - [Pointer-to-member conversions](#pointer-to-member-conversions) - [Function pointer conversions](#function-pointer-conversions) - [Boolean conversions](#boolean-conversions) - [No conversions](#no-conversions) - [No extensibility](#no-extensibility) - [Transitivity](#transitivity) ## Problem Frequently, an expression provided as input to an operation has a type that does not exactly match the expected type. To improve the language ergonomics, we do not want to require explicit conversions in all such cases. However, there is strong evidence from C++ that allowing certain kinds of implicit conversion is dangerous and harmful in practice. We need to find a reasonable balance. ## Background C++ permits many kinds of implicit conversion, some of which are generally considered good, and others are sometimes harmful. For example: - `int` implicitly converts to `long`. This is useful and seldom harmful. - `long` implicitly converts to `int` and to `unsigned int`. This can result in data loss. - `int*` implicitly converts to `bool`. This can be useful in some contexts, such as `if (p)`, but surprising and harmful in others. See also [implicit conversions in C++](https://en.cppreference.com/w/cpp/language/implicit_conversion). ## Proposal See changes to design. ## Rationale based on Carbon's goals - [Software and language evolution](/docs/project/goals.md#software-and-language-evolution) - Disallowing implicit conversions that lose information reduces the risk that existing code will be reinterpreted in a harmful way as libraries in use evolve. - [Code that is easy to read, understand, and write](/docs/project/goals.md#code-that-is-easy-to-read-understand-and-write) - Permitting a limited, safe set of implicit conversions reduces the boilerplate work necessary to write code. - Generics rely on performing implicit conversions between different type-of-types for deduced type parameters. Applying the same rules consistently for all expressions makes the language simpler. - [Interoperability with and migration from existing C++ code](/docs/project/goals.md#interoperability-with-and-migration-from-existing-c-code) - Providing some of the same implicit conversions as C++ reduces the need to add explicit casts when migrating. However, explicit casts will still be required when the C++ code was performing an operation that we don't consider safe. - Support for implicit conversions provides a path to expose converting constructors and conversion functions defined in C++ code to Carbon. ## Alternatives considered ### C++ conversions We could permit more of the conversions that C++ does. This section considers each kind of implicit conversion in C++ and provides a description of the deviation and a rationale. #### Array-to-pointer conversions Array types have not yet been designed yet, so this is out of scope for now. One possible design would be for pointers to not support arithmetic, and for arrays to provide "array iterators" that do supply such arithmetic. In this design, an implicit conversion from arrays to array iterators would likely be surprising. #### Function-to-pointer conversions Function pointer types have not been designed yet, and might not exist in the same form as in C++, so this is out of scope for now. One possible design would be to have no function pointer types, and instead model functions as values of a unique type that implements a certain `Callable` interface. Then a function pointer could be modeled as a type-erased generic implementing `Callable`. In this model, there would be an implicit conversion from a function value to such a type-erased generic value. #### Qualification conversions So far, Carbon has no notion of cv-qualification. However, these conversions would likely be covered by the permission to convert from `T*` to `U*` if `T` is a subtype of `U`. #### Integral promotions Carbon disallows implicit conversion from `bool` to integral types. We could permit such implicit conversions. Advantages: - Improves C++ compatibility. - Permits constructs to count how many of a set of predicates were true: `if (cond1 + cond2 + cond3 >= 2)`. Disadvantages: - Treating truth values as the integers 0 and 1 results in code that is harder to read and understand. - This conversion can result in unexpected overloads being called, when a `bool` argument is passed to a parameter of some other type. #### Floating-point promotions This conversion is permitted. #### Integral conversions These conversions are only permitted when they are known to preserve the original value. These are the conversions that are considered non-narrowing in C++. We could permit narrowing integer conversions. Advantages: - Improves C++ compatibility. - Allows implicitly undoing implicit widening in constructs such as `char n; char c = '0' + n;` where C++ promotes `'0' + n` to `int`. - However, Carbon is unlikely to implicitly widen to `i32` here. Disadvantages: - Introduces the potential for implicit data loss. #### Floating-point conversions Carbon disallows implicit conversion from a more-precise floating-point type to a less-precise floating-point type, such as from `f64` to `f32`. We could permit these implicit conversions. Advantages: - Improves C++ compatibility. - Allows implicitly undoing implicit widening in constructs such as `float a, b; float c = a + b;` where C++ promotes `a + b` to `double`. - However, Carbon might not implicitly widen to `f64` here. Disadvantages: - Introduces the potential for implicit loss of precision. - Introduces the risk that a low-precision operation might be selected when given higher-precision operands. #### Pointer conversions Carbon permits the equivalent conversions, except for the conversion from `nullptr` to pointer type. We anticipate that Carbon pointers will not be nullable by default. Once nullable pointers are designed, we would expect an expression representing the null state would be implicitly convertible to the nullable pointer type. #### Pointer-to-member conversions Carbon does not yet have pointer-to-member types. This is out of scope for now. #### Function pointer conversions Carbon does not yet have function pointer types. This is out of scope for now. #### Boolean conversions An implicit conversion from arithmetic types and pointer types to `bool` is not provided. Pointer types are expected to not be nullable by default, so that part is out of scope for now. We could permit implicit conversion from arithmetic types to `bool`. Advantages: - Improves C++ compatibility and familiarity to C++ programmers. Disadvantages: - Harms type safety by permitting an implicit lossy conversion. - Invites bugs where the wrong overload is selected, where an argument of arithmetic type is passed to a `bool` parameter. - Harms the mental model of `bool` being a choice type rather than an integer type. - Allowing an implicit conversion would permit this kind of conversion everywhere, whereas it is likely only desirable in a select few places, such as where C++ performs a "contextual conversion to `bool`". ### No conversions We could permit no implicit conversions at all, or restrict the set of conversions from those proposed. Advantages: - Code might be easier to understand, because all conversions would be fully explicit. Disadvantages: - Code is likely to be harder to read and harder to write due to casts being inserted frequently. - Creates tension for generics, where implicit conversions between type-of-types are a central part of the model. ### No extensibility We could provide only built-in conversions and no user-defined implicit conversions. Advantages: - Ensures that programmers don't add irresponsible implicit conversions. Disadvantages: - Creates an artificial distinction between built-in and user-defined types. - Creates problems for interoperation with C++ and migration from C++, because certain forms of user-defined implicit conversion are common in C++ code. - Disallows useful functionality without sufficient justification. ### Transitivity We could apply implicit conversions transitively. If an implicit conversion from `A` to `B` is provided and an implicit conversion from `B` to `C` is provided, we could try to infer an implicit conversion from `A` to `C`. This leads to practical problems, as there would be an unbounded search space for intermediate `B` types. For example: ``` impl [T:! Constraint1] A as ImplicitAs(T); impl [T:! Constraint2] T as ImplicitAs(B); let x: A = ...; let y: B = x as B; ``` There is a potentially unbounded space of types to search here (anything that satisfies both `Constraint1` and `Constraint2` at once. Similarly: ``` class X(N: i32, M: i1) {} impl [template N:! i32] X(N, 0) as ImplicitAs(X(N+1, 0)); impl [template N:! i32] X(N, 0) as ImplicitAs(X(N+1, 1)); impl [template N:! i32] X(N, 1) as ImplicitAs(X(N+1, 1)); let z: auto = ({} as X(0, 0)) as X(100, 0); ``` This could lead to a very long implicit conversion sequence (which will presumably need exponential runtime to find). We could support partial transitivity, for only unparameterized intermediate types, by ignoring all blanket impls. But that would be arbitrary, and we can provide better results by first matching the overall source and destination types and then asking them what intermediate type we should be converting to, which is supported by this proposal. For example, for `Optional`: ``` impl [T:! Type, U:! ImplicitAs(T)] U as ImplicitAs(Optional(T)) { fn Convert[me: T]() -> Optional(T) { return ...; } } ``` ================================================ FILE: proposals/p0826.md ================================================ # Function return type inference [Pull request](https://github.com/carbon-language/carbon-lang/pull/826) ## Table of contents - [Problem](#problem) - [Background](#background) - [Lambdas](#lambdas) - [`auto` keyword](#auto-keyword) - [`const` qualification](#const-qualification) - [Proposal](#proposal) - [Details](#details) - [Explicit return required](#explicit-return-required) - [Executable semantics changes](#executable-semantics-changes) - [Disallow direct recursion](#disallow-direct-recursion) - [Indirect recursion](#indirect-recursion) - [Rationale based on Carbon's goals](#rationale-based-on-carbons-goals) - [Open questions](#open-questions) - [Multiple returns](#multiple-returns) - [`const` qualification](#const-qualification-1) - [Alternatives considered](#alternatives-considered) - [Only allow `auto` return types if parameters are generic](#only-allow-auto-return-types-if-parameters-are-generic) - [Provide alternate function syntax for concise return type inference](#provide-alternate-function-syntax-for-concise-return-type-inference) - [Allow separate declaration and definition](#allow-separate-declaration-and-definition) ## Problem Should there be a shorter way of declaring a function? This embodies two questions: - Should there be a way for a declarer to ask that the return type of a function be inferred from returned values? - Should there be an alternate function syntax that provides briefer function declarations for simple cases of return type inference? ## Background Under [Proposal #438: Add statement syntax for function declarations](https://github.com/carbon-language/carbon-lang/pull/438), the syntax approved was: > `fn` _identifier_ `(` _args_ `)` _[_ `->` _expression ]_ `{` _statements_ `}` Executable semantics currently supports the syntax: > `fn` _identifier_ `(` _args_ `) =>` _expression_ In C++, there is similar automatic type inference for return types, although the use of `=>` reflects syntax tentatively discussed for matching use. ### Lambdas Lambdas are also under discussion for Carbon: however, a different syntax is likely to arise. This proposal does not try to address lambda syntax, although it is possible that a decision on lambda syntax may lead to an alternate syntax for declaring functions in order to maintain syntax parity. ### `auto` keyword This is the first proposal to formally include a use of `auto`; although [`var` statement #339](p0339.md) mentions it in an alternative, the proposal did not explicitly propose the keyword. However, the `var x: auto` use-case is expected in Carbon, and so uses of `auto` here can be considered in that context. Note the `auto` keyword name and behavior can generally be considered consistent with C++. ### `const` qualification C++ initially used the matching return type for lambdas. However, it switched to its `auto` variable rules, which are defined in terms of template argument deduction and discard `const` qualifiers. This yields subtle behavioral differences. Note that `const` is not yet defined in Carbon. ## Proposal Support automatic type inference in Carbon with a new `auto` keyword, as in: > `fn` _identifier_ `(` _args_ `) -> auto {` _statements_ `}` For now, only one return statement is allowed: we will avoid defining rules for handling differing return types. Separate declarations and definitions are not supported with `auto` return types because the declaration must have a known return type. ## Details ### Explicit return required Functions using `-> auto` must return an expression; using the implicit return or `return;` is invalid. This is consistent with the [design for returning empty tuples](/docs/design/control_flow/return.md#returning-empty-tuples). ### Executable semantics changes Executable semantics will remove the `=>` function syntax. In practice, this means the current executable semantics syntax: ``` fn Add(x: i32, y: i32) => x + y; ``` Becomes: ``` fn Add(x: i32, y: i32) -> auto { return x + y; } ``` ### Disallow direct recursion Direct recursive calls can create complexities in inferring the return type. For example: ``` // In the return statement. fn Factorial(x: i32) -> auto { return (if x == 1 then x else x * Factorial(x - 1)); } // Before the return statement, but affecting the return type. fn Factorial(x: i32) -> auto { var x: auto = (if x == 1 then x else x * Factorial(x - 1)); return x; } ``` As a consequence, direct recursion is rejected: that is, a function with an `auto` return type may not call itself. #### Indirect recursion Indirect recursion will typically look like: ``` fn ExplicitReturn() -> i32; fn AutoReturn() -> auto { return ExplicitReturn(); } fn ExplicitReturn() -> i32 { return AutoReturn(); } ``` This is valid because the return type of `AutoReturn()` can be calculated to be `i32` from the forward declaration of `ExplicitReturn`. Due to how name lookup works, there is no risk of indirect recursion causing problems for typing: - A similar separate declaration of an `auto` return is [disallowed](#proposal). - Without a separate declaration, name lookup would fail. That is, the below has a name lookup error at `return B();`: ``` fn A() -> auto { return B(); } fn B() -> auto { return A(); } ``` As a consequence, no specific rules about indirect recursion are needed, unlike direct recursion. ## Rationale based on Carbon's goals - [Code that is easy to read, understand, and write](/docs/project/goals.md#code-that-is-easy-to-read-understand-and-write) - We are avoiding providing an alternative function syntax in order to provide users less syntax they'll need to understand. - As a practical measure, there are likely to be cases in generic code that are more feasible to write. - [Interoperability with and migration from existing C++ code](/docs/project/goals.md#interoperability-with-and-migration-from-existing-c-code) - The intent is to have `auto` as a return type work similarly to C++'s type inference, in order to ease migration. ## Open questions ### Multiple returns It's likely that we should support `auto` on functions with multiple returns. This proposal declines to address that use-case because of the complexities which arise when return types may differ. Instead, the use-case is left for future proposals to handle. ### `const` qualification As noted in background, [`const` qualification](#const-qualification) has seen changes in C++. While we should work to choose a story that can remain long-term, this will likely be revisited when rules around deduction for templates are addressed. ## Alternatives considered ### Only allow `auto` return types if parameters are generic `auto` return types are likely to be a negative impact to readability, because a reader must read the function body in order to determine the return type. We could detect whether parameters used in determining an `auto` return type are generic, and only allow `auto` to be used when they are. Advantages: - Limits the readability impact of `auto`. - `auto` could not be used where the return type is clearly deterministic, and thus easy to write. In these cases, readers would never need to look at the function body to determine the return. - `auto` could be used where the return type may be difficult to easily write as a consequence of potential generic parameters. Disadvantages: - Increases the rule set around use of `auto` in return types. - Breaks C++ compatibility. The decision to allow `auto` in more cases is primarily for C++ compatibility. ### Provide alternate function syntax for concise return type inference Executable semantics currently demonstrates an alternate function syntax with: ``` fn Add(x: i32, y: i32) => x + y; ``` This is a more concise syntax that builds upon return type inference. We could keep that, or come up with some other syntax. Advantages: - For short functions, provides a more succinct manner of defining the function. - `=>` use echoes tentative matching syntax. Disadvantages: - Creates an additional syntax which can be used for equivalent behavior. - Overlaps with lambda use-cases, but lambdas will likely end up looking different; that is, we should not assume the syntax will converge. - We may in particular take a more sharply divergent syntax for lambdas, in order to allow for significant brevity in typing, which may not make sense to support for function declarations. - Limits use of `=>` for other purposes. - To the extent that `=>` might primarily be replacing `-> auto`, assuming Carbon adopted Rust-style [block expression returns](https://doc.rust-lang.org/reference/expressions/block-expr.html), this offers limited value for the additional token use. This proposal suggests not adding an inference-focused alternate function syntax at this time; while consistent with tentative matching syntax, the split from function syntax is significant. The [one way principle](/docs/project/principles/one_way.md) applies here. If an alternate function syntax is added, it should be done in tandem with a lambda proposal in order to ensure consistency in syntax. ### Allow separate declaration and definition The return type must be possible to determine from the declaration. As a consequence, the declaration and definition of a function returning `auto` cannot be significantly separated: a caller must be able to see the definition. Thus, although a separate declaration would be insufficient for a call, we could allow using `auto` with a separate declaration and definition when the caller can see _both_. This example would be valid because `CallAdd` can see the `Add` definition: ``` fn Add(x: i32, y: i32) -> auto; fn Add(x: i32, y: i32) -> auto { return x + y; } fn CallAdd() -> i32 { return Add(1, 2); } ``` However, the following example would be invalid because `CallAdd` can only see the `Add` declaration (even though the definition may be in the same file), and it would need the definition to determine the return type: ``` fn Add(x: i32, y: i32) -> auto; fn CallAdd() -> i32 { return Add(1, 2); } fn Add(x: i32, y: i32) -> auto { return x + y; } ``` Advantages: - Separating the declaration and definition could be used in files to cluster brief declarations, then put definitions below. - A particularly common use-case of this might be in class declarations, where a user might want to offer a declaration and define it out of line where it doesn't interrupt the class's API. - It may be preferred to only use `auto` return types with short functions, which limits this advantage as a short function could easily be inlined. Disadvantages: - Cannot be used to break call cycles, which is the usual use of separate declarations and definitions. - A separate declaration still cannot be called until after the definition is provided. - This may be particularly confusing to humans. The decision is to not support separate declarations and definitions with `auto` return types due to the limited utility. ================================================ FILE: proposals/p0829.md ================================================ # One way principle [Pull request](https://github.com/carbon-language/carbon-lang/pull/829) ## Table of contents - [Problem](#problem) - [Proposal](#proposal) - [Alternatives considered](#alternatives-considered) - [Provide multiple ways of doing a given thing](#provide-multiple-ways-of-doing-a-given-thing) ## Problem We have repeatedly run into cases where we could offer equivalent functionality multiple ways. ## Proposal Add a principle noting the preference is to only provide one way of doing things. ## Alternatives considered ### Provide multiple ways of doing a given thing Carbon could focus on providing a lower bar for overlapping functionality, encouraging overlapping syntax. This could be considered as similar to Perl's ["There is more than one way to do it."](https://en.wikipedia.org/wiki/There%27s_more_than_one_way_to_do_it). Overlapping syntax should still receive some scrutiny, but use-cases with small marginal benefits might be considered sufficient to create divergent syntax. For example: - For matching C++ legacy: - We might include `for (;;)`. - We might re-add support for optional braces on `if` and similar. - We might support `class` and `struct` with their C++-equivalent default visibility rules. - We might support both `and` and `&&`, `0xa` and `0XA`, etc. - We might make `;` optional, as there's precedent in modern languages to do so. - Both templates and generics might be embraced with feature parity such that both should be good solutions for generic programming problems, as much as possible. It's worth noting Perl also has a related motto of "There is more than one way to do it, but sometimes consistency is not a bad thing either." It's best to avoid interpreting this alternative to the extreme: for example, this is not encouraging providing all of `extensible`, `extendable`, and `extendible` as equivalent keywords, as that is divergence with very minimal benefit (particularly `extendable` versus `extendible`). Advantages: - More likely that developers will find syntax that they like. - Can make it easier to migrate code because we can actively support a C++-like dialect in addition to a more Carbon-centric dialect. Disadvantages: - Developers would either accept personal styles, or create more style guides. - Either can be considered a language dialect, whether at the personal or organizational level. - Increases the difficulty of building syntax parsing and typo correction in the language, as there are more options that need to be corrected between, multiple of which may be valid in a given context. We are declining this alternative because we value the language simplicity provided by minimizing overlap of features. ================================================ FILE: proposals/p0845.md ================================================ # `as` expressions [Pull request](https://github.com/carbon-language/carbon-lang/pull/845) ## Table of contents - [Problem](#problem) - [Background](#background) - [Proposal](#proposal) - [Rationale based on Carbon's goals](#rationale-based-on-carbons-goals) - [Future work](#future-work) - [Provide a mechanism for unsafe conversions](#provide-a-mechanism-for-unsafe-conversions) - [Casting operator for conversions with domain restrictions](#casting-operator-for-conversions-with-domain-restrictions) - [Alternatives considered](#alternatives-considered) - [Allow `as` to perform some unsafe conversions](#allow-as-to-perform-some-unsafe-conversions) - [Allow `as` to perform two's complement truncation](#allow-as-to-perform-twos-complement-truncation) - [`as` only performs implicit conversions](#as-only-performs-implicit-conversions) - [Integer to bool conversions](#integer-to-bool-conversions) - [Bool to integer conversions](#bool-to-integer-conversions) ## Problem We would like to provide a notation for the following operations: - Requesting a type conversion in order to select an operation to perform, or to resolve an ambiguity between possible operations: ``` fn Ratio(a: i32, b: i32) -> f64 { // Note that a / b would invoke a different / operation. return a / (b as f64); } ``` - Specifying the type that an expression will have or will be converted into, for documentation purposes. ``` class Thing { var id: i32; } fn PrintThing(t: Thing) { // 'as i32' reminds the reader what type we're printing. Print(t.id as i32); } ``` - Specifying the type that an expression is expected to have, potentially after implicit conversions, as a form of static assertion. ``` fn Munge() { // I expect this expression to produce a Widget but I'm getting compiler // errors and I'd like to narrow down why. F(Some().Complex().Expression() as Widget); } ``` In general, the developer wants to specify that an expression should be considered to produce a value of a particular type, and that type might be more general than the type of the expression, the same as the type of the expression, or perhaps might represent a different way of viewing the value. The first of the above problems is especially important in Carbon due to the use of facet types for generics. Explicit conversions of types to interfaces will be necessary in order to select the meaning of operations, because the same member name on different facet types for the same underlying type will in general have different meanings. For this proposal, the following are out of scope: - Requesting a type conversion that changes the value, such as by truncation. - Converting a value to a narrower type or determining whether such a conversion is possible -- `try_as` or `as?` operations. ## Background C++ provides a collection of different kinds of casts and conversions from an expression `x` to a type `T`: - Copy-initialization: `T v = x;` - Direct-initialization: `T v(x);` - Named casts: - `static_cast(x)` - `const_cast(x)` - `reinterpret_cast(x)` - `dynamic_cast(x)` - C-style casts: `T(x)` or equivalently `(T)x` - These can do anything that `static_cast`, `const_cast`, and `reinterpret_cast` can do, but ignore access control on base classes. - List-initialization: `T{x}` - This can do anything that implicit conversion can do, and can also initialize a single -- real or notional -- subobject of `T`. - Narrowing conversions are disallowed. These conversions are all different, and each of them has some surprising or unsafe behavior. Swift provides four forms of type casting operation: - `x as T` performs a conversion from subtype to supertype. - `pattern as T` in a pattern matching context converts a pattern that matches a subtype to a pattern that matches a supertype, by performing a runtime type test. This effectively results in a checked supertype to subtype conversion. - `x as! T` performs a conversion from supertype to subtype, with the assumption that the value inhabits the subtype. - `x as? T` performs a conversion from supertype to subtype, producing an `Optional`. - `T(x)` and similar construction expressions are used to convert between types without a subtyping relationship, such as between integer and floating-point types. In Swift, `x as T` is always unsurprising and safe. Rust provides the following: - `x as T` performs a conversion to type `T`. - When there is no corresponding value, some specified value is produced: this conversion will perform 2's complement truncation on integers and will saturate when converting large floating-point values to integers. - Conversions between distinct pointer types, and between pointers and integers, are permitted. Rust treats accesses through pointers as unsafe, but not pointer arithmetic or casting. This cast can perform some conversions with surprising results, such as integer truncation. It can also have surprising performance implications, because it defines the behavior of converting an out-of-range value -- for example, when converting from floating-point to integer -- in ways that aren't supported across all modern targets. Haskell and Scala support type ascription notation, `x : T`. This has also been proposed for Rust. This notation constrains the type checker to find a type for the expression `x` that is consistent with `T`, and is used: - for documentation purposes, - to guide the type checker to select a particular meaning of the code in the presence of ambiguity, and - as a diagnostic tool when attempting to understand type inference failures. ## Proposal Carbon provides a binary `as` operator. `x as T` performs an unsurprising and safe conversion from `x` to type `T`. - This can be used to perform any implicit conversion explicitly. As in Swift, this can therefore be used to convert from subtype to supertype. - This can also be used to perform an unsurprising and safe conversion that cannot be performed implicitly because it's lossy, such as from `i32` to `f32`. This operator does not perform conversions with domain restrictions, such as converting from `f32` to `i64`, where sufficiently large values can't be converted. It does not perform operations in which there are multiple different reasonable interpretations, such as converting from `i64` to `i32`, where a two's complement truncation might sometimes be reasonable but where the intent is more likely that it is an error to convert a value that does not fit into an `i32`. See changes to the design for details. ## Rationale based on Carbon's goals - [Code that is easy to read, understand, and write](/docs/project/goals.md#code-that-is-easy-to-read-understand-and-write) - Providing only unsurprising built-in `as` conversions, and encouraging user-defined types to do the same, makes code easier to understand. - [Practical safety and testing mechanisms](/docs/project/goals.md#practical-safety-and-testing-mechanisms) - Syntactically distinguishing between always-safe `as` conversions and potentially-unsafe conversions being performed by other syntax makes it clearer which code should be the subject of more scrutiny when reasoning about safety. - [Interoperability with and migration from existing C++ code](/docs/project/goals.md#interoperability-with-and-migration-from-existing-c-code) - The `As` interface provides the same functionality as single-argument `explicit` constructors and `explicit` conversion functions in C++, and can be used to expose those operations for interoperability purposes and as a replacement for those operations during migration. ## Future work ### Provide a mechanism for unsafe conversions We need to provide additional conversions beyond those proposed for `as`. In particular, to supply the same set of conversions as C++, we would need at least the following conversions that don't match the rules for `as`: Conversions with a domain restriction: - Conversions from pointer-to-supertype to pointer-to-subtype. - Conversions from floating-point to integer types that assume the input is in-range. - (Not in C++.) Conversions between any two integer types that assume the input is in-range. Conversions that modify some values: - Truncating conversions between any two integer types. Conversions that reinterpret values: - Conversions between arbitrary pointer types. - Conversions between integers and pointers. - Bit-casts between arbitrary, sufficiently-trivial types of the same size. Special cases: - Some analogue of `dynamic_cast`. - Some analogue of `const_cast`. We will need to decide which of these we wish to provide -- in particular, depending on our plans for mutability and RTTI, `const_cast` and `dynamic_cast` may or may not be appropriate. For the operations we do supply, we could provide either named functions or dedicated language syntax. While this proposal suggests that the `as` operator should not be the appropriate language syntax for the above cases, that decision should be revisited once we have more information from examining the alternatives. #### Casting operator for conversions with domain restrictions We could provide an additional casting operator, such as `assume_as` or `unsafe_as`, to model conversions that have a domain restriction, such as `i64 -> i32` or `f32 -> i64` or `Base*` -> `Derived*`. Advantage: - Provides additional important but unsafe functionality. - Gives this functionality the appearance of being a central language feature. - Separates safe conversions from unsafe ones. Disadvantage: - Increases complexity. - The connection between these conversions may not be obvious, and the kind and amount of unsafety in practice differs substantially between them. If we don't follow this direction, we will need to provide these operations by another mechanism, such as named function calls. ## Alternatives considered ### Allow `as` to perform some unsafe conversions We could provide a single type-casting operator that can perform some conversions that have a domain restriction, treating values out of range as programming errors. One particularly appealing option would be to permit `as` to convert freely between integer and floating-point types, but not permit it to convert from supertype to subtype. Advantage: - Developers many not want to be reminded about the possibility of overflow in conversions to integer types. - This would make `as` more consistent with arithmetic operations, which will likely have no overt indication that they're unsafe in the presence of integer overflow. - If we don't do this, then code mixing differently-sized types will need to use a syntactic notation other than `as`, even if all conversions remain in-bounds. If such code is common, as it is in C++ (for example, when mixing `int` and `size_t`), developers may become accustomed to using that "assume in range" notation and not consider it to be a warning sign, thereby eroding the advantage of using a distinct notation. Disadvantage: - If we allow this conversion, there would be no clear foundation for which conversions can be performed by `as` and which cannot in general. - An `as` expression would be less suitable for selecting which operation to perform if it can be unsafe. - Under maintenance, every usage of `as` would need additional scrutiny because it's not in general a safe operation. - This risks being surprising to developers coming from C and C++ where integer type conversions are always safe. The choice to not provide these operations with `as` is experimental, and should be revisited when we have more information about the design of integer types and their behavior. ### Allow `as` to perform two's complement truncation We could allow `as` to convert between any two integer types, performing a two's complement conversion between these types. Advantage: - Familiar to developers from C++ and various other systems programming languages. Disadvantage: - Makes `as` conversions have behavior that diverges from the behavior of arithmetic, where we expect at least signed overflow to be considered a programming error rather than being guaranteed to wrap around. - Introducing a common and easy notation for conversion with wraparound means that this notation will also be used in the -- likely much more common -- case of wanting to truncate a value that is already known to be in-bounds. Compared to having distinct notation for these two operations: - This removes the ability to distinguish between programming errors due to overflow and intentional wraparound by using the same syntax for both, both for readers of the code and for automated checks in debugging builds. - This removes the ability to optimize on the basis of knowing that a value is expected to be in-bounds when performing a narrowing conversion. The choice to not provide these operations with `as` is experimental, and should be revisited when we have more information about the design of integer types and their behavior. ### `as` only performs implicit conversions We could limit `as` to performing only implicit conversions. This would mean that `as` cannot perform lossy conversions. Advantage: - One fewer set of rules for developers to be aware of. Disadvantage: - Converting between integer and floating-point types is common, and providing built-in syntax for it seems valuable. ### Integer to bool conversions We could allow a conversion of integer types (and perhaps even floating-point types) to `bool`, converting non-zero values to `true` and converting zeroes to `false`. Advantage: - This treatment of non-zero values as being "truthy" and zero values as being "falsy" is familiar to developers of various other languages. - Uniform treatment of types that can be notionally converted to a Boolean value may be useful in templates and generics in some cases. Disadvantage: - The lossy treatment of all non-zero values as being "truthy" is somewhat arbitrary and can be confusing. - An `as bool` conversion is less clear to a reader than a `!= 0` test. - An `as bool` conversion is more verbose than a `!= 0` test. ### Bool to integer conversions We could disallow conversions from `bool` to `iN` types. Advantage: - More clearly demarcates the intended semantics of `bool` as a truth value rather than as a number. - Avoids making a choice as to whether `true` should map to 1 (zero-extension) or -1 (sign-extension). - But there is a strong established convention of using 1. - Such conversions are a known source of bugs, especially when performed implicitly. `as` conversions will likely be fairly common and routine in Carbon code due to their use in generics. As such, they may be written without much thought and not given much scrutiny in code review. ``` var found: bool = false; var total_found: i32 = 0; for (var (key: i32, value: i32) in list) { if (key == expected) { found = true; total_found += value; } } // Include an explicit `as i64` to emphasize that we're widening the // total at this point. // Bug: meant to pass `total_found` not `found` here. add_to_total(found as i64); ``` Disadvantage: - Removes a sometimes-useful operation for which there isn't a similarly terse alternative expression form. - But we could add a member function `b.AsBit()` if we wanted. - Does not expose the intended connection between the `bool` type and bits. We could disallow conversion from `bool` to `i1`. Advantage: - Avoids a surprising behavior where this conversion converts `true` to -1 whereas all others convert `true` to 1. Disadvantage: - Results in non-uniform treatment of conversion from `bool`, and an awkward special case that may get in the way of generics. - A conversion from `bool` that produces -1 for a `true` value is useful when producing a mask, for example in `(b as i1) as u32`. ================================================ FILE: proposals/p0851.md ================================================ # Variable type inference [Pull request](https://github.com/carbon-language/carbon-lang/pull/851) ## Table of contents - [Problem](#problem) - [Background](#background) - [Variable type inference in other languages](#variable-type-inference-in-other-languages) - [Carbon equivalents](#carbon-equivalents) - [Pattern matching](#pattern-matching) - [Carbon equivalents](#carbon-equivalents-1) - [Pattern matching on `if`](#pattern-matching-on-if) - [Proposal](#proposal) - [Open questions](#open-questions) - [Inferring a variable type from literals](#inferring-a-variable-type-from-literals) - [Rationale based on Carbon's goals](#rationale-based-on-carbons-goals) - [Alternatives considered](#alternatives-considered) - [Elide the type instead of using `auto`](#elide-the-type-instead-of-using-auto) - [Use `_` instead of `auto`](#use-_-instead-of-auto) ## Problem Inferring variable types is a common, and desirable, use-case. In C++, the use of `auto` for this purpose is prevalent. We desire this enough for Carbon that we already make prevalent use in example code. ## Background Although [#826: Function return type inference](p0826.md) introduced the `auto` keyword for `fn`, the use type inference in `var` should be expected to be more prevalent. In C++, `auto` can be used in variables as in: ```cpp auto x = DoSomething(); ``` In Carbon, we're already using `auto` extensively in examples in a similar fashion. However, it's notable that in C++ the use of `auto` replaces the actual type, and is likely there as a matter of backwards compatibility. ### Variable type inference in other languages [#618: var ordering](p0618.md) chose the ordering of var based on other languages. Most of these also provide inferred variable types. Where the `: ` syntax matches, here are a few example inferred types: - [Kotlin](https://kotlinlang.org/docs/basic-syntax.html#variables): `var x = 5` - [Python](https://docs.python.org/3/tutorial/introduction.html): `x = 5` - [Rust](https://doc.rust-lang.org/std/keyword.mut.html): `let mut x = 5;` - [Swift](https://docs.swift.org/swift-book/LanguageGuide/TheBasics.html): `var x = 5` Ada appears to [require](https://learn.adacore.com/courses/intro-to-ada/chapters/strongly_typed_language.html#what-is-a-type) a variable type in declarations. For different syntax, [Go](https://tour.golang.org/basics/14) switches from `var x int = 5` to `x := 5`, using the `:=` to trigger type inference. #### Carbon equivalents In Carbon, we have generally discussed: ```carbon var x: auto = 5; ``` However, given precedent from other languages, we could omit this: ```carbon var x = 5; ``` ### Pattern matching As we consider variable type inference, we need to consider how pattern matching would be affected. [Swift's patterns](https://docs.swift.org/swift-book/ReferenceManual/Patterns.html) support: - Value-binding patterns, as in: ```swift switch point { case let (x, y): ... } ``` - Expression patterns, as in: ```swift switch point { case (0, 0): ... case (x, y): ... } ``` - **Combining** the above, as in: ```swift switch point { case (x, let y): ... } ``` #### Carbon equivalents With Carbon, we have generally discussed: - Value-binding patterns, as in: ```carbon match (point) { case (x: auto, y: auto): ... } ``` - Expression patterns, as in: ```carbon match (point) { case (0, 0): ... case (x, y): ... } ``` - **Combining** the above, as in: ```carbon match (point) { case (x, y: auto): ... } ``` However, it may be possible to mirror Swift's syntax more closely; for example: - Value-binding patterns, as in: ```carbon match (point) { case let (x, y): ... } ``` - Expression patterns, as in: ```carbon match (point) { case (0, 0): ... case (x, y): ... } ``` - **Combining** the above, as in: ```carbon match (point) { case (x, let y): ... } ``` In the above, this presumes to allow `let` inside a tuple in order to indicate the variable-binding for one member of a tuple. ### Pattern matching on `if` In Carbon, it's been suggested that `if` could support testing pattern matching when there's only one case, for example with `auto`: ```carbon match (point) { case (x, y: auto): // Pattern match succeeded, `y` is initialized. default: // Pattern match failed. } => if ((x, y: auto) = point) { // Pattern match succeeded, `y` is initialized. } else { // Pattern match failed. } ``` A `let` approach may still look like: ```carbon match (point) { case (x, let y): // Pattern match succeeded, `y` is initialized. default: // Pattern match failed. } => if ((x, let y) = point) { // Pattern match succeeded, `y` is initialized. } else { // Pattern match failed. } ``` Some caveats should be considered for pattern matching tests inside `if`: - There is syntax overlap with C++, which allows code such as: ```cpp if (Derived* derived = dynamic_cast(base)) { // dynamic_cast succeeded, `derived` is initialized. } else { // dynamic_cast failed. } ``` - Syntax is similar to `match` itself; it may be worth considering whether the feature is [sufficiently distinct and valuable](/docs/project/principles/one_way.md) to support. ## Proposal Carbon should offer variable type inference using `auto` in place of the type, as in: ```carbon var x: auto = DoSomething(); ``` At present, variable type inference will simply use the type on the right side. In particular, this means that in the case of `var y: auto = 1`, the type of `y` is `IntLiteral(1)` rather than `i64` or similar. This [may change](#inferring-a-variable-type-from-literals), but is the simplest answer for now. ## Open questions ### Inferring a variable type from literals Using the type on the right side for `var y: auto = 1` currently results in a constant `IntLiteral` value, whereas most languages would suggest a variable integer value. This is something that will be considered as part of type inference in general, because it also affects generics, templates, lambdas, and return types. ## Rationale based on Carbon's goals - [Code that is easy to read, understand, and write](/docs/project/goals.md#code-that-is-easy-to-read-understand-and-write) - Frequently code will have the type on the line, as in `var x: auto = CreateGeneric[Type](args)`. Avoiding repetition of the type will reduce the amount of code that must be read, and should allow readers to comprehend more quickly. - [Interoperability with and migration from existing C++ code](/docs/project/goals.md#interoperability-with-and-migration-from-existing-c-code) - The intent is to have `auto` work similarly to C++'s type inference, in order to ease migration. ## Alternatives considered ### Elide the type instead of using `auto` As discussed in background, [other languages allow eliding the type](#variable-type-inference-in-other-languages). Carbon could do similar, allowing: ```carbon var y = 1; ``` Advantages: - Forms a cross-language syntax consistency with languages that put the type on the right side of the identifier. - This is particularly strong with Kotlin and Swift, because they also use `var`. - Use of `auto` is currently unique to C++; Carbon will be extending its use. Disadvantages: - Type inference becomes the _lack_ of a type, rather than the presence of something different. - C++'s syntax legacy may have needed `auto` in order to provide type inference, where Carbon does not. - Removes syntactic distinction between binding of a new name and reference to an existing name, [particularly for pattern matching](#pattern-matching). Reintroducing this distinction would likely require additional syntax, such as Swift's nested `let`. We expect there will be long-term pushback over the cross-language inconsistency, particularly as the `var : auto` syntax form will be unique to Carbon. However, there's a strong desire not to have the lack of a type mean the type will be inferred. ### Use `_` instead of `auto` We have discussed using `_` as a placeholder to indicate that an identifier would be unused, as in: ```carbon fn IgnoreArgs(_: i32) { // Code that doesn't need the argument. } ``` We could use `_` instead of `auto`, leading to: ```carbon var x: _ = 1; ``` However, removing `auto` entirely would also require using `_` when inferring function return types. For example: ```carbon fn InferReturnType() -> _ { return 3; } ``` Advantages: - Reduces the number of keywords in Carbon. - Less to type. - The incremental convenience may ameliorate the decision to require a keyword for type inference. Disadvantages: - There's a feeling that `_` means "discard", which doesn't match the semantics of inferring a return type, since the type is not discarded. - There may be some ambiguities in handling, such as `var c: (_, _) = (a, b)`. - The reduction of typing is unlikely to address arguments that the keyword is not technically required. The sense of "discard" versus "infer" semantics is why we are using `auto`. ================================================ FILE: proposals/p0861.md ================================================ # Naming conventions [Pull request](https://github.com/carbon-language/carbon-lang/pull/861) ## Table of contents - [Problem](#problem) - [Background](#background) - [Cross-language precedent](#cross-language-precedent) - [Carbon artifacts](#carbon-artifacts) - [Proposal](#proposal) - [Details](#details) - [Constants](#constants) - [Carbon-provided item naming](#carbon-provided-item-naming) - [Open questions](#open-questions) - [Rationale based on Carbon's goals](#rationale-based-on-carbons-goals) - [Alternatives considered](#alternatives-considered) - [Other naming conventions](#other-naming-conventions) - [Other conventions for naming Carbon types](#other-conventions-for-naming-carbon-types) ## Problem The goal of this proposal is to establish naming conventions for: - Idiomatic Carbon code. - Carbon-provided features, including: - Keywords, such as `fn` or `for`. - Type literals, such as `i32`. - Types, such as `bool` or `String`. The reason for resolving this through proposal is to ensure we're headed in a reasonably consistent path as we write early documentation and example code. ## Background ### Cross-language precedent Naming conventions are an issue frequently addressed by style guides. A few examples of language-provided guidelines are: - [Effective Go](https://golang.org/doc/effective_go#names) - [Python's PEP 8](https://www.python.org/dev/peps/pep-0008/#naming-conventions) - Note PEP 8 offers a list of [naming styles](https://www.python.org/dev/peps/pep-0008/#descriptive-naming-styles). - [Rust API Guidelines](https://rust-lang.github.io/api-guidelines/naming.html) - [Swift's API Design Guidelines](https://swift.org/documentation/api-design-guidelines/#general-conventions) ### Carbon artifacts Related issues: - Issue [#543: pick names for fixed-size integer types](https://github.com/carbon-language/carbon-lang/issues/543) covers numeric type literal naming. - Issue [#750: Naming conventions for Carbon-provided features](https://github.com/carbon-language/carbon-lang/issues/750) was used for initial naming convention discussion. Related proposals: - Proposal [#720: Property naming in C++](https://github.com/carbon-language/carbon-lang/pull/720) covers nuances of property naming. ## Proposal Only `UpperCamelCase` and `lower_snake_case` conventions will be used, in order to minimize the variation in rules. - For idiomatic Carbon code: - `UpperCamelCase` will be used when the named entity cannot have a dynamically varying value. For example, functions, namespaces, or compile-time constant values. - `lower_snake_case` will be used when the named entity's value won't be known until runtime, such as for variables. - For Carbon-provided features: - Keywords and type literals will use `lower_snake_case`. - Other code will use the guidelines for idiomatic Carbon code. In other words: | Item | Convention | Explanation | | ------------------------- | ------------------ | ------------------------------------------------------------------------------------------ | | Packages | `UpperCamelCase` | Used for compile-time lookup. | | Types | `UpperCamelCase` | Resolved at compile-time. | | Functions | `UpperCamelCase` | Resolved at compile-time. | | Methods | `UpperCamelCase` | Methods, including virtual methods, are equivalent to functions. | | Generic parameters | `UpperCamelCase` | May vary based on inputs, but are ultimately resolved at compile-time. | | Compile-time constants | `UpperCamelCase` | Resolved at compile-time. See [constants](#constants) for more remarks. | | Variables | `lower_snake_case` | May be reassigned and thus require runtime information. | | Member variables | `lower_snake_case` | Behave like variables. | | Keywords | `lower_snake_case` | Special, and developers can be expected to be comfortable with this casing cross-language. | | Type literals | `lower_snake_case` | Equivalent to keywords. | | Boolean type and literals | `lower_snake_case` | Equivalent to keywords. | | Other Carbon types | `UpperCamelCase` | Behave like normal types. | | `Self` and `Base` | `UpperCamelCase` | These are similar to type members on a class. | ## Details ### Constants Supposing `let` might be used to designate a constant, consider the following code: ```carbon package Example; let CompileTimeConstant: i32 = 7; fn RuntimeFunction(runtime_constant: i32); ``` In this example, `CompileTimeConstant` has a singular value (`7`) which is known at compile-time. As such, it uses `UpperCamelCase`. On the other hand, `runtime_constant` may be constant within the function body, but it is assigned at runtime when `RuntimeFunction` is called. Its value is only known in a given runtime invocation of `RuntimeFunction`. As such, it uses `lower_snake_case`. ### Carbon-provided item naming Carbon-provided items are split into a few categories: - Keywords; for example, `for`, `fn`, and `var` - Type literals; for example, `i`, `u`, and `f` - Boolean type and literals; for example, `bool`, `true`, and `false` - The separate categorization of booleans should not be taken as a rule that only booleans would use lowercase; it's just the only example right now. - `Self` and `Base` - Other Carbon types; for example, `Int`, `UInt`, and `String` Note that while other Carbon types currently use `UpperCamelCase`, that should not be inferred to mean that future Carbon types will do the same. The leads will make decisions on future naming. ## Open questions ## Rationale based on Carbon's goals - [Code that is easy to read, understand, and write](/docs/project/goals.md#code-that-is-easy-to-read-understand-and-write) - The intent is that limiting to `UpperCamelCase` and `lower_snake_case` will offer developers a limited number of style rules to learn. - There is a desire to maintain good ergonomics for developers. - [Interoperability with and migration from existing C++ code](/docs/project/goals.md#interoperability-with-and-migration-from-existing-c-code) - There is a general desire to maintain naming consistency with C++, and this can be seen with keywords such as `for` as well as booleans. ## Alternatives considered ### Other naming conventions A couple naming conventions that were discussed briefly are: - `lowerCamelCase` names came up, but are hard to distinguish from `lower_snake_case` for single-word identifiers. By contrast, `UpperCamelCase` and `lower_snake_case` are distinct, whether a single word or multiple. - `ALL_CAPS_SNAKE_CASE` is used in C++ code, such as for macros and compile-time constants. With Carbon, we hope the language is simple enough that the readability benefit of an additional naming convention wouldn't outweigh the cost of giving developers more naming conventions to learn. ### Other conventions for naming Carbon types In detail, individual naming decisions that had alternative patterns or options discussed were: - Type literals use `lower_snake_case` as described in issue [#543: pick names for fixed-size integer types](https://github.com/carbon-language/carbon-lang/issues/543). - `i8` is more ergonomic than `Int8` for brevity. - `i32` and `i64` maintain the same length as C++'s `int`. - We don't want to use `I32` because `I32` can be hard to visually distinguish with `132` or `l32`, even though it may help screen readers. - Booleans use `bool`, `true`, and `false` mainly because there's a desire not to skew from C++. - While leads are okay with some things being shadowed, such as `Self` and `Base`, there is a desire not to allow shadowing of booleans. - `Self` and `Base` are `UpperCamelCase` because leads want them to look more like normal types. - They may be implemented as a hybrid approach, using something similar to lookup in classes so they aren't quite keywords, but somewhat similar to keywords because leads may want to reject them as identifiers in non-class contexts. - `String` and potentially other names follow idiomatic naming conventions. - Note that, in this case, divergence from C++ is accepted. Taking that into consideration, and that we are likely to keep `lower_snake_case` for keywords and type literals in particular, a couple options discussed would have mainly affect `self`, `base`, and `string`: - Anything language-provided without needing an `import` is always `lower_snake_case`. - `i32`, `bool`, `true`, `self`, `base`, `string.is_empty()` - As above, but prelude class members follow standard naming conventions. - `i32`, `bool`, `true`, `self`, `base`, `string.IsEmpty()` The understanding is that the difference between `bool` being treated similarly to a keyword and `Self` being treated similarly to a type (both regardless of implementation) is somewhat arbitrary. The decision not to use `lower_snake_case` consistently depends mainly on the leaning of the leads, that `self` and `base` felt like they shouldn't strictly be treated as keywords, and `string` felt like the point where users should expect something more like an idiomatic class. Future borderline cases may need to be decided by leads whether they should be treated as more keyword-like or type-like. ================================================ FILE: proposals/p0866.md ================================================ # Allow ties in floating literals [Pull request](https://github.com/carbon-language/carbon-lang/pull/866) ## Table of contents - [Problem](#problem) - [Background](#background) - [Proposal](#proposal) - [Details](#details) - [Rationale based on Carbon's goals](#rationale-based-on-carbons-goals) - [Alternatives considered](#alternatives-considered) ## Problem Proposal [#143](https://github.com/carbon-language/carbon-lang/pull/143) suggested that we do not allow ties in floating-point literals. That is, given a literal whose value lies exactly half way between two representable values, we should reject rather than arbitrarily picking one of the two possibilities. However, the [statistical argument](p0143.md#ties) presented in that proposal misses an important fact: the distribution of the values that are exactly half way between representable values includes several values of the form A x 10B, where A and B are small integers. For example, the current rule rejects this very reasonable looking code: ``` var v: f32 = 9.0e9; ``` ... because 9 x 109 lies exactly half way between the nearest two representable values of type `f32`, namely 8999999488 and 9000000512. Similar examples exist for larger floating point types: ``` // Error, half way between two exactly representable values. var w: f64 = 5.0e22; ``` We would also reject an attempted workaround such as: ``` var v: f32 = 5 * 1.0e22; ``` ... because the literal arithmetic would be performed exactly, resulting in the same tie. A workaround such as ``` var v1: f32 = 5.0e22 + 1.0; var v2: f32 = 5.0e22 - 1.0; ``` ... to request rounding upwards and downwards, respectively, would work. However, these seem cumbersome and burden the Carbon developer with floating-point minutiae about which they very likely do not care. ## Background For background on the ties-to-even rounding rule, see [this Wikipedia article](https://en.wikipedia.org/wiki/Rounding#Round_half_to_even). The ties-to-even rule is the default rounding mode specified by ISO 60559 / IEEE 754. ## Proposal Instead of rejecting exact ties, we use the default IEEE floating point rounding mode: we round to even. ## Details See design changes. ## Rationale based on Carbon's goals - [Code that is easy to read, understand, and write](/docs/project/goals.md#code-that-is-easy-to-read-understand-and-write) - This improves the ease of both reading and writing floating-point literals that would result in ties. - This improves the language consistency, by performing the same rounding when converting literals as is performed by default when converting runtime values. - [Practical safety and testing mechanisms](/docs/project/goals.md#practical-safety-and-testing-mechanisms) - It is unlikely that making an arbitrary but consistent rounding choice will harm safety or program correctness. - [Fast and scalable development](/docs/project/goals.md#fast-and-scalable-development) - [Modern OS platforms, hardware architectures, and environments](/docs/project/goals.md#modern-os-platforms-hardware-architectures-and-environments) - [Interoperability with and migration from existing C++ code](/docs/project/goals.md#interoperability-with-and-migration-from-existing-c-code) - This rule, likely because it is the IEEE default rounding mode, already appears to be used by major C++ compilers such as Clang, GCC, MSVC, and ICC. ## Alternatives considered We could round to even only for decimal floating-point literals, and still use the rule that ties are rejected for hexadecimal floating point. In the latter case, a tie means that too many digits were specified, and the trailing digits were exactly `80000...`. However, because we support arithmetic on literals, forming other literals, this would mean that whether a literal was originally written in hexadecimal would form part of its value and thereby part of its type. There would also be problems with literals produced by arithmetic. The complexity involved here far outweighs any perceived benefit of diagnosing mistyped literals. ================================================ FILE: proposals/p0875.md ================================================ # Principle: information accumulation [Pull request](https://github.com/carbon-language/carbon-lang/pull/875) ## Table of contents - [Problem](#problem) - [Background](#background) - [Single-pass "splat" compilation](#single-pass-splat-compilation) - [Global consistency](#global-consistency) - [The C++ compromise](#the-c-compromise) - [Separate declarations and definitions](#separate-declarations-and-definitions) - [Proposal](#proposal) - [Details](#details) - [Goals](#goals) - [Choice of alternative](#choice-of-alternative) - [Rationale based on Carbon's goals](#rationale-based-on-carbons-goals) - [Alternatives considered](#alternatives-considered) - [Separate declaration and definition](#separate-declaration-and-definition) - [Allow separate declaration and definition](#allow-separate-declaration-and-definition) - [Analysis](#analysis) - [Disallow separate declaration and definition](#disallow-separate-declaration-and-definition) - [Analysis](#analysis-1) - [Information flow](#information-flow) - [Strict top-down](#strict-top-down) - [Analysis](#analysis-2) - [Strict global consistency](#strict-global-consistency) - [Block-scope exception](#block-scope-exception) - [Analysis](#analysis-3) - [Top-down with minimally deferred type checking](#top-down-with-minimally-deferred-type-checking) - [Analysis](#analysis-4) - [Top-down with deferred method bodies](#top-down-with-deferred-method-bodies) - [Analysis](#analysis-5) - [Context-sensitive local consistency](#context-sensitive-local-consistency) - [Analysis](#analysis-6) ## Problem We should have consistent rules describing what information about a program is visible where. ## Background Information in a source file is provided incrementally, with each source utterance providing a small piece of the overall picture. Different languages have different rules for which information is available where. ### Single-pass "splat" compilation In C and other languages of a similar age, single-pass compilation was highly desirable, due to resource limits and performance concerns. In these languages: - Information is accumulated top-down, and can only be used lexically after it appears. - Most information can be discarded soon after it is provided: function bodies don't need to be kept around once they've been converted to the output format, and no information on local variable or parameter names needs to persist past the end of the variable's scope. However, the types of globals and the contents of type definitions must be retained. - The behavior of an entity can be different at different places in the same source file. An early use may fail if it depends on information that's provided later, and in some cases a later use may fail when an earlier use succeeded because the use is invalid in a way that was not visible at the point of an earlier use. ### Global consistency In more modern languages such as C#, Rust, Java, and Swift, there is no lexical information ordering. In these languages: - Information is effectively accumulated and processed in separate passes. - The language design and implementation ensure that the behavior of an entity is the same everywhere: both before its definition, after its definition, within its definition, and in any other source file in which it was made visible. - Dependency cycles between program properties are carefully avoided by the language designers. ### The C++ compromise In C++, a hybrid approach is taken. There is a C-like lexical information ordering rule, but this rule is subverted within classes by -- effectively -- reordering certain parts of a class that appear within the class definition so that they are processed after the class definition. This primarily applies to the bodies of member functions. Here: - Information is mostly accumulated top-down, and is accumulated fully top-down after the reordering step. - The behavior of a class is the same within member function bodies that are defined inside the class as it is within member function bodies defined lexically after the class. - The language designers need to ensure that the bounds of the member function bodies and similar constructs can be determined without parsing them, so that the late-parsed portions can be separated from the early-parsed portions. In C++, this was not done successfully, and there are constructs for which this determination is very hard or impossible. ### Separate declarations and definitions Somewhat separate from the direction of information flow is the ability to separate the information about an entity into multiple distinct regions of source files. In C and C++, entities can be separately declared and defined. As a consequence, these languages need rules to determine whether two declarations declare the same entity. In C++, especially for templated declarations, these rules can be incredibly complex, and even now, more than 30 years after the introduction of templates in C++, [basic questions are not fully answered](https://wg21.link/cwg2), and implementations disagree about which declarations declare the same entity in fairly simple examples. One key benefit of this separation is in reduction of _physical dependencies_: in order to validate a usage of an entity, we need only see a source file containing a declaration of that entity, and need never consider the source file containing its definition. This both reduces the number of steps required for an incremental rebuild and reduces the input information and processing required for each individual step. The ability to break physical dependencies is limited to the cases where information can actually be hidden from the users of the entity. For example, if the user actually needs a function body, either because they will evaluate a call to the function during compilation or because they will inline it prior to linking, it cannot be physically isolated from the user of that information. As a consequence, in C++, a programmer must carefully manage which information they put in the source files that are exposed to client code and which information is kept separate. Another key benefit is that the exported interface of a source file can become more readable, by presenting an interface that contains only the facts that are salient to a user and not the implementation details. ## Proposal Carbon will use a [top-down approach with deferred method bodies](#top-down-with-deferred-method-bodies), with [forward declarations](#allow-separate-declaration-and-definition). In any case where the [strict global consistency](#strict-global-consistency) model with the [block-scope exception](#block-scope-exception) would consider the program to be invalid or would assign it a different meaning, the program is invalid. Put another way: we accept exactly the cases where those two models both accept and give the same result, and reject all other cases. ## Details Each entity has only one meaning and only one set of properties; that meaning and those properties do not depend on which source file you are within or where in that source file you are. However, not all information will be available everywhere, and it is an error to attempt to use information that is not available -- either because it's in a different source file and not exported or not imported, or because it appears later in the same source file. For example, a full definition of a class `Widget` may be available in some parts of the program, with only a forward declaration available in other parts of the program. In this case, any attempt to use `Widget` where only a forward declaration is available will always either result in the program being invalid or doing the same thing that would happen if the full information were available. This is a stronger guarantee than is provided by C++'s One Definition Rule, which guarantees that every use sees an equivalent definition or no definition, but does not guarantee that every use has the same behavior -- in C++ a function could do one thing if `Widget` is defined and a different thing if `Widget` is only forward declared, and that will not be possible in Carbon. Name lookup only provides earlier-declared names. However, the bodies of class member functions that are defined inside the class are reordered to be separately defined after the class, so all class member names are declared before the class body is seen. Unlike in C++, classes are either incomplete or complete, with no intermediate state. Any attempt to perform name lookup into an incomplete class is an error, even if the name being looked up has already been declared. ``` class A { fn F() { // OK: A and B are complete class types here. var b: A.B; } class B {} // Error: cannot look up B within incomplete class A. var p: A.B*; } ``` Forward declarations are permitted in order to allow separation of interface and implementation whenever the developer wishes to do so, and introduce names early in order to break cycles or when the developer does not wish to define entities in topological order. All declarations of an entity are required to be part of the same library, but can be in different source files. If a point within a source file can only see a declaration of an entity and not its definition, some uses of that entity will be invalid that would be valid if the definition were visible. For example, without a class definition, we may be unable to create instances of the class, and without a function definition, we may be unable to use the function to compute a compile-time constant. ### Goals For this proposal, we have the following goals as refinements of the overall Carbon goals: - _Comprehensibility._ Our rules should be understandable, and should minimize surprise and gotchas. Our behavior should be self-consistent, and explainable in only a few sentences. - _Ergonomics._ It should be easy to express common developer desires, without a lot of boilerplate or repetitive code. - _Readability._ Code written using our rules should be as straightforward as possible for Carbon developers to read and reason about. - _Efficient and simple compilation._ It should be relatively straightforward to implement our semantic rules. Implementation heroics shouldn't be required, and the number of special cases required should be minimized. - _Diagnosability._ An implementation should be able to explain coding errors in ways that are easy to understand and are well-correlated with the error and its remedy. Diagnostics should appear in an order and style that guides the developer through logical steps to fix their mistakes. - _Toolability._ Relatively simple tools should be able to understand simpler properties of Carbon code. It should ideally be possible to identify which names can be used in a particular context and what those names mean without full processing. It should ideally be possible to gather useful and mostly complete information about a potentially-invalid source file that is currently being edited, for which it may be desirable to assume there is a "hole" in the source file at the cursor position that will be filled by unknown code. ### Choice of alternative We have a large space of options here, all of which have both benefits and drawbacks. The rationale for our choice is as follows: The first consideration is whether to provide a [globally consistent view](#strict-global-consistency) of code. Providing such a view would require us to find a compatible build process that can scale to large projects; see the [detailed analysis](#analysis-3) for details. Moreover, this is likely to be a choice that is relatively hard to evolve away from. A non-scalable build process would be an existential threat to the Carbon language under its stated goals, so we set this option aside. This should be revisited if a complete design for a scalable build process with a globally-consistent semantic model is proposed. Once we step away from strict global consistency, there will be cases where the meaning of an entity differs in different parts of the program. In order for programs to behave consistently and predictably, especially in the presence of templates and generics, we do not want to allow the same operation to have two or more different behaviors depending on which information is available. So we choose to make programs invalid if they would make use of information that is not available. Even though we have chosen not to provide a globally consistent view, we could still choose to provide a consistent view throughout a package, library, or source file, such as in the [context-sensitive local consistency](#context-sensitive-local-consistency) model. However, again for build performance reasons, we want to avoid package-at-a-time or even library-at-a-time compilation, and would like a file-at-a-time compilation strategy. The cost of having different views in different files is substantially reduced given that we have already chosen to have different views at least in different packages. So we allow the known information about an entity to vary between source files, even in the same library. This implies that we at least need forward declarations for entities that are declared in an `api` file for a library and defined in an `impl` file. However, this leaves the question of what happens within a single source file: do entities need to be manually written in dependency order, or is the implementation expected to use information that appears after its point of declaration? Neither option guarantees that an entity will behave consistently wherever it's used, because we already gave up that guarantee for entities visible across source files, and we need to cope with the resulting risk of incoherence anyway. The simplest option for implementation, and the one most similar to C++, would then be to accumulate information top-down. This is also the choice that is most consistent with the cross-source-file behavior: moving a prefix of a source file into a separate, imported file should generally preserve the validity of the program. Further, this choice gives us the most opportunity for future language evolution, as it is the most restrictive option, and hence it is forward-compatible with the other rules under consideration, as well as supporting paths for evolution that require a top-down view, such as some approaches to metaprogramming. This choice implies that we need forward declarations, both for declarations in different source files from their definitions and to resolve cycles or cases where the developer cannot or does not wish to write code in topological order. Allowing separate declaration and definition may also be valuable to allow the code author to hide implementation details from readers of the interface, and present a condensed catalog or table of contents for the library. This is also an important aspect in providing a language that is familiar to C++ developers. So we choose to [allow separate declaration and definition](#allow-separate-declaration-and-definition) in general. Given the above decisions, class member functions could not be meaningfully defined inline under a strict top-down rule, because the class would not yet be complete. We make an ergonomic affordance of reordering the bodies of such functions as if they appear after the class definition. This also improves consistency with C++, which has a similar rule. ## Rationale based on Carbon's goals - [Language tools and ecosystem](/docs/project/goals.md#language-tools-and-ecosystem) - See "Toolability" goal. - [Software and language evolution](/docs/project/goals.md#software-and-language-evolution) - Ensuring that the program interpretation is consistent with an interpretation with full information makes it easier to evolve code, as changing the amount of visible information -- for example, by reordering code or by adding or removing imports -- cannot change the meaning of a program from one valid meaning to another. - Selecting a rule that disallows information from flowing backwards keeps open more language evolution paths: - We could allow more information to flow backwards. - We could expose metaprogramming constructs that can inspect the state of a partial source file without creating inconsistency in our model. - [Code that is easy to read, understand, and write](/docs/project/goals.md#code-that-is-easy-to-read-understand-and-write) - See "Readability", "Ergonomics", and "Comprehensibility" gaols. - [Fast and scalable development](/docs/project/goals.md#fast-and-scalable-development) - See "Efficient and simple compilation" and "Diagnosability" goals. - [Interoperability with and migration from existing C++ code](/docs/project/goals.md#interoperability-with-and-migration-from-existing-c-code) - The chosen rule is mostly the same as the corresponding rule in C++, both in accumulating information top-down and in treating inline method bodies as a special case. This should keep migration simple and improve familiarity for experienced C++ developers. It should be noted that the decision between the model presented here and the leading alternative, which was [context-sensitive local consistency](#context-sensitive-local-consistency), was very close, and came down to minor details. For each of our goals, neither alternative had a significant advantage. The similarity to C++ and evolvability of the alternative in this proposal were ultimately the deciding factors. ## Alternatives considered Below, various alternatives are presented and rated according to the [goals](#goals) for this proposal. Generally we need to pick a rule for allowing or disallowing separate declaration and definition, and a rule for information flow. These choices are not independent: some information flow rules require separate declaration and definition. ### Separate declaration and definition #### Allow separate declaration and definition We could allow two or more declarations of each entity, with at most one declaration providing a definition and the other declarations merely introducing the existence of the entity and its basic properties: ``` // Forward declaration. fn F(); // ... // Another forward declaration of the same function. fn F(); // One and only definition. fn F() {} // Yet another forward declaration. fn F(); ``` This could be done in either a free-for-all fashion as in the above example, where any number of declarations is permitted, or in a more restricted fashion, where there can be at most one declaration introducing an entity, and, if that declaration is not a definition, at most one separate implementation providing the definition. In this case, the separate definition could contain a syntactic marker indicating that a prior declaration should exist: ``` // Forward declaration. fn F(); // ... // Separate definition, with `impl` marker to indicate that this is just an // implementation of an entity that was already declared. impl fn F() {} ``` ##### Analysis _Comprehensibility:_ In general, determining whether two declarations declare the same entity may not be straightforward, particularly for a redeclaration of an overloaded function. We have some options, but they all have challenges for comprehensibility or ergonomics of the rule: - We could require token-for-token identical declarations, but this may result in ergonomic problems -- developers may wish to leave out information in an interface that isn't relevant for consumers of that interface, or spell the same type or parameter name differently in an implementation. - We could allow the declarations to differ so long as they have the same meaning. However, there may be cases, as there are in C++, where it's unclear whether two declarations are "sufficiently similar" so as to declare the same entity. - We could require all overloads of a function to be declared together in a group, perhaps with some dedicated syntax, and then match up redeclarations based on position in the group rather than signature. Such an approach is likely to lead to developers, especially those familiar with the C++ rule, being surprised. It will likely also interact poorly with cases where some but not all of the overloads are defined in their first declaration and the rest are defined separately, or where it is desirable for different overloads to be defined in different implementation files. _Ergonomics:_ Any case where the declaration and definition are separated results in repetitive code. However, this repetition may serve a purpose to the extent that it's presenting an API description or acting as a compilation barrier. _Readability:_ Readability of code may be substantially improved by separating a declaration of an API from its definition, and permitting an API user to read, in a small localized portion of a source file, only the information they care about, without regard for implementation details. _Efficient and simple compilation:_ Allowing multiple declarations of an entity introduces some implementation complexity, as the implementation must look up, validate, and link together multiple declarations of the same entity. If redeclarations are permitted across source files -- such as between the interface and implementation of an API, or between multiple implementation files -- then this may also require some stable mechanism for identifying the declaration, such as a name mangling scheme. This may be especially complex in the case of overloaded function templates, which might differ in arbitrary Carbon expressions appearing in the function's declaration. This complexity could be greatly reduced if we require all overloads to be declared together -- at least in the same source file -- as we can then identify them by name and index. This approach supports physical separation of implementation from interface, potentially leading to build time wins through reduced recompilation and through reducing the amount of information fed into each compilation step. _Diagnosability:_ Identifying errors where a redeclaration doesn't exactly match a prior declaration is not completely straightforward. This is especially the case when function overloads can be added by the same syntax with which a definition of a prior declaration would be introduced. Even if declarations and definitions use a different syntax, diagnosing a mismatch between a definition of an overloaded function and a prior set of declarations requires some amount of fuzzy matching to infer which one was probably intended. _Toolability:_ Supporting separate declarations and definitions will present the same kinds of complexity for non-compiler tools as for compilers. Tools will need to be able to reason about multiple declarations providing distinct information about an entity and the effect of that on how the entity can be used at different source locations. #### Disallow separate declaration and definition We could require each entity to be declared in exactly one place, and reject any cases where the same entity is declared more than once. ##### Analysis _Comprehensibility:_ This rule is easy to explain and easy to reason about. _Ergonomics:_ Minimizes the work required by the developer to implement new functionality and maintain existing functionality. Reduces the chance that a change in one place will be forgotten in another. _Readability:_ Each entity has only one declaration, giving a single canonical location to include documentation, and no question as to where information about the entity might be found. This will likely improve the navigability of source code and the ease with which all the information about an entity can be gathered, and reduce the chance that information in the declaration or definition will be incomplete or that they will be inconsistent with each other. However, there would be no ability to present a function or class as source code without also including implementation details. Such implementation details cannot be physically separated from the API as presented to clients. The ability to read and understand an interface will depend more heavily on tools that can hide implementation details, such as documentation generators and IDEs with outlining, and implementation techniques such as using `interface`s to present the API of a class separate from its implementation. For developers familiar with C++ in particular, the absence of the ability to separate declaration from definition may create friction, due to the familiarity of that feature and having their thinking about code layout shaped around it. Given that Carbon aims to be familiar to those coming from C++, an absence of forward declarations will work against that goal to some extent, although empirical evidence suggests that the extent to which this is an issue varies widely between existing C++ developers. _Efficient and simple compilation:_ This approach leads to a simpler compilation strategy. However, in the absence of forward declarations, every compilation referring to an entity needs to have a build dependency on the full definition of that entity, meaning there is no physical separation between the APIs of entities and their definitions. This would impose a potentially-significant build time cost due to increased recompilations when the definition of an entity changes. There are possibilities to reduce this build time cost. For example, instead of each declaration identifying whether it is public or private to a library, we could add a third visibility level to say that the declaration but not the definition is public, and use a stripping tool as part of the build process to avoid dependencies on non-public definitions from cascading into unnecessary rebuilds. This approach is also unlikely to be acceptable from an ergonomic standpoint unless declarations within a source file are visible throughout at least the entire library containing that file, although this is strictly an independent choice. Making definitions from a source file visible throughout its library would presumably require a library-at-a-time build strategy, which is also expected to introduce build time costs due to changes to a library requiring more code in that library to be recompiled. This cost could be reduced by using a reactive compilation model, recompiling only the portions of a library that are affected by a code change, at some complexity cost. _Diagnosability:_ Because each entity can only be declared once, there is no significant challenge in diagnosing redeclaration issues. However, there is still some work required to diagnose conflicts between similar or functionally identical function overloads. _Toolability:_ Having a unique source location for each entity allows for somewhat simpler tooling. For example, there is no need to distinguish between "jump to declaration" and "jump to definition", or to decide which declaration should be consulted to find documentation comments, parameter names, and so on. ### Information flow #### Strict top-down Carbon could accumulate information top-down. We could require that each program utterance is type-checked and fully validated before any later code is considered. In order to support this and still permit cyclic references between entities, we would need to [allow separate declaration and definition](#allow-separate-declaration-and-definition). ##### Analysis _Comprehensibility:_ This rule is simple to explain, and has no special cases. However, the inability to look at information from later in the source file is likely to result in gotchas: ``` class Base { var n: i32; } class Derived extends Base { // Returns me.(Base.n), not me.(Derived.n), because the latter has not // been declared yet. fn Get[me: Self]() -> i32 { return me.n; } var n: i32; } ``` It might be possible to require a diagnostic in such cases, when we find a declaration that would change the meaning of prior code if it had appeared earlier, but that would result in implementation complexity, and the fact that such cases are rejected would still be a surprise. _Ergonomics:_ The developer is required to topologically sort their source files in dependency order, manually breaking cycles with forward declarations. Common refactoring tasks such as reorganizing code may require effort or tooling assistance in order to preserve a topological order. Developers could adapt to this ruleset by beginning each source file with a collection of forward declarations. This would mitigate the need to produce a topological ordering, except within those forward declarations themselves, and other declarations required to provide those forward declarations. For example, a forward declaration of a class member will likely only be possible within a class definition, and the order in which that class definition is given can be relevant to the validity of other class definitions. However, our experience with C++ indicates that this will not be done, and instead an ad-hoc and undisciplined combination of a topological sort and occasional forward declarations will be used. Indeed, including these additional forward declarations would increase the maintenance burden and introduce an additional opportunity for errors due to mismatches between declaration and definition. _Readability:_ Developers wishing to understand code have the advantage that they need only consider prior code, and there is no possibility that a later source utterance could change the meaning of the code they're reading. However, it is rare to read code top-down, so the effect of this advantage may be modest. This advantage leads to a significant disadvantage: the behaviour of an entity can be different at different places within a source file. For example, a type can be incomplete in one place and complete in another, or can fail to implement an interface when inspected early and then found to implement that interface later. This can lead to very subtle incoherent behavior. In practice, the topological ordering constraint tends to lead to good locality of information: helpers for particular functionality are often located near to the functionality. However, this is not a universal advantage, and the topological constraint sometimes leads to internal helpers being ordered immediately before their first use instead of in a more logical position near correlated functionality. _Efficient and simple compilation:_ This rule is mostly simple and efficient to implement, and even allows single-pass processing of source files. _Diagnosability:_ Because information is provided top-down, diagnostics can also be provided top-down and in every case the diagnostic will be caused by an error at the given location or earlier. Fixing errors should require little or no backtracking by the developer. However, an implementation that strictly confines its processing to top-down order and produces diagnostics eagerly cannot deliver diagnostics that react intelligently to contextual cues that appear after the point of the diagnostic. This approach diminishes the ability for an implementation to pinpoint the cause of the error and describe it in a developer-oriented fashion. _Toolability:_ Limiting information flow to top-down means that tools such as code completion tools need only consider context prior to the cursor, and they can be confident that if all the code prior to the cursor is valid that it can be type-checked and suitable completions offered. However, in the case where the user wants to refer to a later-declared entity, such tools would not be able to use this strategy. They would need to parse as if there were not a top-down rule in order to find such later-declared entities, and would likely additionally need the ability to add forward declarations or to reorder declarations in order to satisfy the ordering requirement. #### Strict global consistency Carbon could follow an approach of requiring the behavior of every entity to be globally consistent. In this approach, the behavior of every entity would be as if the entire program could be consulted to determine facts about that entity. In practice, to make this work, we would need to limit where those facts can be declared. For example, we limit implementations of interfaces to appear only in source files that must already be in use wherever the question "does this type implement that interface?" can be asked. In addition, we need to reject at least the case where some property of the program recursively depends upon itself: ``` struct X { var a: Array(sizeof(X), i8); } ``` In order to give globally consistent semantics to, for example, a package name, we would likely need to process all source files comprising a package at the same time. This is likely to encourage packages to be small, whereas we have designed package support on the assumption that the scope of a package matches that of a complete source code repository. This alternative can be considered either with or without the ability to separate declarations from definitions. If we permit separate declaration and definition, we would likely require declarations of the same entity to appear in the same library; however, that limitation is desirable regardless of which strategy we choose. ##### Block-scope exception Applying this rule to local name lookup in block scope does result in some surprises. For example, C# uses this approach, and combined with its disallowance of shadowing of local variables, this [confuses some developers](https://stackoverflow.com/questions/1196941/variable-scope-confusion-in-c-sharp). As a variant of this alternative, it would be reasonable and in line with likely programmer expectations to not apply this rule to names in block scope. We call this the _block-scope exception_. For example: ``` var m: i32 = 1; fn F(b: bool) -> i32 { if (b) { var n: i32 = 2; // With the block-scope exception: // * `n` is unambiguously the variable declared on the line above, and // * `m` is unambiguously the global. // // Without the block-scope exception: // * depending on the rules we choose for shadowing, `n` might name the // variable above, might be ambiguous, or might be invalid because it // shadows the variable `n` declared below, and // * `m` would name the local variable declared below. return n + m; } var n: i32 = 3; var m: i32 = 4; return n * m; } ``` ##### Analysis _Comprehensibility:_ This rule is simple to explain, and has no special cases, other than perhaps the block scope variant. The disallowance of semantic cycles is likely to be unsurprising as it is a logical necessity in any rule. _Ergonomics:_ The developer can organize or arrange their code in any way they desire. There is never a requirement to forward-declare or repeat an interface declaration. Refactoring and code reorganization do not require any non-obvious changes, because the same code means the same thing regardless of how it is located relative to other code. _Readability:_ Reasoning about code is simple in this model, as such reasoning is largely not context-sensitivity. Instead of questioning "what does this do here?" we can instead consider "what does this do?". Some context sensitivity may remain, for example due to access and name bindings differing in different contexts. However, to developers accustomed to a top-down semantic model, the ability to defer giving key information about an entity -- or even declaring it at all -- until long after it is first used may hinder readability in some circumstances, particularly when reading code top-down. _Efficient and simple compilation:_ This model forces the compilation process to operate in multiple stages rather than as a single pass. Some form of cycle detection is necessary if cycles are possible. However, such a mechanism is likely to be necessary for template instantiation too, so this is likely not a novel requirement for Carbon. Forcing all files within a package to be compiled together in order to provide consistent semantics for the package name may place an undesirable scalability barrier on the build system. This will also tend to push up build latency, especially for incremental builds, by decreasing the granule size of compilation. It may increase the scope of recompilations due to additional physical dependencies unless we implement a mechanism to detect whether a library has changed in an "important" way. We do not have an existence proof of an efficient build strategy for a Carbon-like language that follows this model. For example, Rust has known build scalability issues, as does Java for large projects unless a tool like [ijar](https://github.com/bazelbuild/bazel/tree/master/third_party/ijar) is used to automatically create something resembling a C++ header file from a source file. There is a significant risk that such an automated tool would be very hard to create for Carbon: because the definitions of classes and functions can be exposed in various ways through compile-time function evaluation, templates, and metaprogramming, it seems challenging to put any useful bound on which entities can be stripped without restricting the capabilities of client code. _Diagnosability:_ The implementation is likely to have more contextual information when providing diagnostics, improving their quality. However, the diagnostics may appear in a confusing order: if an early declaration needs information from a later declaration in order to type-check, diagnostics associated with that later declaration may be produced first, or may be interleaved with diagnostics for the earlier declaration, leading to the programmer potentially revisiting the same code multiple times during a compile-edit cycle. _Toolability:_ This model requires tools to consider the whole file as context, because code may refer to entities that are only introduced later. For an IDE scenario, where the cursor represents a location where an arbitrary chunk of code may be missing, this presents a challenge of determining how to resynchronize the input in order to determine how to interpret the portion of the source file following the cursor. Sophisticated tooling for a top-down model may wish to inspect the trailing portion of the file anyway, in order to provide a better developer experience, but this complexity would be forced upon tools with this model. #### Top-down with minimally deferred type checking We could follow a top-down approach generally, but defer type-checking some or all top-level entities until we reach the end of that entity. For example, we would check an entire class as a single unit, following the same principles as in the globally-consistent rule, but using only information provided prior to the end of the class definition. This would allow class members to use other members that have not yet been declared, while not permitting a function definition preceding the class definition to use such members. Following C++'s lead, we would apply this to at least classes and interfaces, but perhaps to nothing else. We may still want to apply the top-down rule to function definitions appearing within a class or interface, as the C# behavior that the scope of a local variable extends backwards before its declaration may be surprising. ##### Analysis This approach generally has the properties as [strict top-down](#strict-top-down), except as follows: _Comprehensibility:_ Slightly reduced due to additional special-casing of top-level declarations. Gotchas would be rarer, but may not be fully eliminated. For example: ``` import X; // G from package X, not G from class X below. fn F() { X.G(); } class X { fn G() {} } class Y { // G from class X below, not G from package X. fn F() { X.G(); } class X { fn G() {} } } ``` _Ergonomics:_ Improved within classes, as members can now be named everywhere within the class. _Readability:_ The ability to read and understand code may be somewhat harmed, as the rules for behavior across top-level declarations aren't the same as the rules for behavior within a top-level declaration. It may be especially jarring that code that is valid within a class definition is not valid at the top level: ``` class A { // OK var b: B; class B {} } // Error, I have not heard of B. var b: B; class B {} ``` However, the behavior of an entity no longer differs at different points within the same declaration of that entity, so the ability to reason about the behavior of an entity is somewhat improved compared to the strict top-down approach. For entities without a separate declaration and definition, the behavior of those entities is the same everywhere. _Efficient and simple compilation:_ This has the same complications as the strict global consistency approach, except that it does not apply to contexts that span multiple files, so it doesn't have the build time disadvantages of requiring all source files in a library to be built together. _Diagnosability:_ Diagnostics should be encountered by the implementation in the top-down order of the top-level declaration containing the error, but may in principle appear in any order within that top-level declaration, if there are forward references that require later code to be analyzed before earlier code is. Diagnostics can be caused by errors appearing both before and after the location reported, but not arbitrarily far after: later top-level declarations cannot affect an earlier diagnostic. _Toolability:_ Tools that want to correctly model Carbon semantics would be required to deal with incomplete source code in the vicinity of the cursor. This is probably the hardest kind of incomplete source code to handle, as the region of "damaged" code is most likely to be relevant context. In practice, this is likely to present the same difficulties as tooling in the [strict global consistency](#strict-global-consistency) model. #### Top-down with deferred method bodies **This is the proposed approach.** We could follow a top-down approach generally, but defer all processing of bodies of class member functions until we have finished processing the class, at which point we would process those member function bodies in the order they appeared within the class. For example, we would reinterpret: ```carbon class A { fn F[me: Self]() -> i32 { return me.n; } fn Make() -> A { return {.n = 5}; } var n: i32; } ``` exactly as if it were written with the member function bodies out of line: ```carbon class A { fn F[me: Self]() -> i32; fn Make() -> A; var n: i32; } fn A.F[me: Self]() -> i32 { return me.n; } fn A.Make() -> A { return {.n = 5}; } ``` The [strict top-down](#strict-top-down) would be applied to this rewritten form of the program. As in C++, member functions of nested classes would be deferred until the outermost class is complete. This deferral would apply only to the bodies of member functions, unlike in C++ where it also applies to default arguments, default member initializers, and exception specifications, and unlike [top-down with minimally deferred type checking](#top-down-with-minimally-deferred-type-checking), where it also applies to member function signatures and the declarations and definitions of non-function members. ##### Analysis This approach generally has the properties as [strict top-down](#strict-top-down), except as follows: _Comprehensibility:_ Slightly reduced due to additional special-casing of class member functions, but these rules are well-aligned with the rules from C++, which do not seem to suffer from major comprehensibility problems in this area. _Ergonomics:_ Improved within classes, as members can now be named within member function bodies. _Readability:_ The ability to read and understand code may be somewhat harmed, as the rules for behavior within a member function body are different from the rules everywhere else in the language. However, the very similar rule in C++ is not known to cause readability problems, so no major concerns are anticipated. _Efficient and simple compilation:_ The complexity of this approach is not substantially greater than the simple top-down approach. Processing of member function bodies must be deferred, but this can be done either by storing and replaying the tokens, or by forming a parse tree but deferring the semantic analysis and type-checking until the end of the class. _Diagnosability:_ Diagnostics for errors in member functions may appear after diagnostics for later code in the same class. Developers may be confused by diagnostics claiming that a class member function body is not available yet and referring to a point in the program text that lexically follows the member function definition. It is likely possible to special-case such diagnostics to explain the nature of the problem. _Toolability:_ Tools that want to parse incomplete Carbon code would need to cope with member function bodies containing errors. For the most part, skipping a brace-balanced function body should be straightforward, but tools will also need to consider whether they attempt to detect and recover from mismatched braces within member functions. #### Context-sensitive local consistency We could use different behaviors in different contexts, as follows: - For contexts that are fundamentally ordered, such as function bodies, a top-down rule is used. - For contexts that are defined across multiple source files, such as packages and namespaces, we guarantee consistent behavior within each source file, but the behavior may be inconsistent across source files: different source files may see different sets of names within a package or namespace, depending on what they have imported. - For contexts that are defined within a single source file, such as a class or an interface, we guarantee globally consistent behavior. Compared to the [strict global consistency](#strict-global-consistency) model, this would not guarantee that the contents of a package or namespace are the same everywhere: you only see the names that you declare or import into a package or namespace. Also, a top-down rule is used within function bodies, as that likely better matches programmer expectations. Compared to the [top-down with minimally deferred type checking](#top-down-with-minimally-deferred-type-checking) and [top-down with deferred method bodies](#top-down-with-deferred-method-bodies) models, this would not require forward declarations for namespace and package members that are declared or defined later in the same source file. ##### Analysis _Comprehensibility:_ This model is probably a little easier to understand than the minimally-deferred type-checking model, because there is no difference between top-level declarations and nested declarations. Because the behavior of every entity is consistent within a file, and every name lookup is consistent within a scope -- other than the top-down behavior within function scopes -- there are fewer opportunities for surprises than in the top-down approaches, and nearly as few as in the strict global consistency model. _Ergonomics:_ As with the strict global consistency model, code can be organized as the developer desires, and refactoring doesn't have any surprises due to disrupting a topological order. _Readability:_ As with the strict global consistency model, readability is improved by not being context-sensitive. However, it is file-sensitive, and a different set of imports may lead to different behavior. Similarly, as with the strict global consistency model, developers accustomed to the top-down model may find it harder to understand code in which a name can be referenced before it is declared. _Efficient and simple compilation:_ This model has the same general costs as the top-down with minimal deferred type-checking model. The implementation needs to be able to separate parsing from type-checking, and type-check lazily in order to handle forward references. However, more state needs to be accumulated before type-checking can begin. This model supports separate compilation of source files, as no attempt is made to ensure consistency across source files, only within a source file. _Diagnosability:_ The compilation process will be split up into multiple phases, and it is likely that diagnostics from one phase will precede those from another. For example, a parsing error for a later construct may precede a type error for an earlier one. If an earlier phase fails, it may not be feasible to diagnose later phases. For example, if parsing encounters an unrecoverable failure, type errors for the successfully-parsed portion cannot be generated, because they may depend on constructs appearing later in the same source file. However, this may also mean that better diagnostics are produced for cases such as a missing `}`, where the syntactic error will precede or prevent diagnostics for semantic errors caused by the misinterpretation of the program resulting from that missing `}`. Within a particular phase of processing, diagnostics will generally be produced in a topological order, and the processing can be arranged such that they are produced in top-down order except where a forward reference requires that a later declaration is checked earlier. _Toolability:_ Largely the same as the strict global consistency model, except that tools only need to use the current source file as context. Also largely the same as the top-down with minimally deferred type checking model. ================================================ FILE: proposals/p0911.md ================================================ # Conditional expressions [Pull request](https://github.com/carbon-language/carbon-lang/pull/911) ## Table of contents - [Problem](#problem) - [Background](#background) - [Proposal](#proposal) - [Details](#details) - [Chaining](#chaining) - [Precedence and ambiguity](#precedence-and-ambiguity) - [Rationale based on Carbon's goals](#rationale-based-on-carbons-goals) - [Alternatives considered](#alternatives-considered) - [No conditional expression](#no-conditional-expression) - [Use C syntax](#use-c-syntax) - [No `then`](#no-then) - [Require parentheses around the condition](#require-parentheses-around-the-condition) - [Never require enclosing parentheses](#never-require-enclosing-parentheses) - [Variable-precedence `if`](#variable-precedence-if) - [Implicit conversions in both directions](#implicit-conversions-in-both-directions) - [Support lvalue conditionals](#support-lvalue-conditionals) - [Future work](#future-work) - [Too many user-facing interfaces](#too-many-user-facing-interfaces) - [Incompatible `CommonType` implementations diagnosed late](#incompatible-commontype-implementations-diagnosed-late) - [`impl` ordering depends on operand order](#impl-ordering-depends-on-operand-order) ## Problem Programs need to be able to select between multiple different paths of execution and multiple different values. In a rich expression language, developers expect to be able to do this within a subexpression of some overall expression. ## Background C-family languages provide a `cond ? value1 : value2` operator. - This operator has confusing syntax, because both `cond` and `value2` are undelimited, and it's often unclear to developers how much of the adjacent expressions are part of the conditional expression. For example: ``` int n = has_thing1 && cond ? has_thing2 : has_thing3 && has_thing4; ``` is parsed as ``` int n = (has_thing1 && cond) ? has_thing2 : (has_thing3 && has_thing4); ``` Also, `value1` and `value2` are parsed with different rules: ``` cond ? f(), g() : h(), i(); ``` is parsed as ``` (cond ? f(), g() : h()), i(); ``` - In C++, this operator has confusing semantics, due to having a complicated set of rules governing how the target type is determined. - Despite the complications of the rules, the result type of `?:` is not customizable. Instead, C++ invented a `std::common_type` trait that models what the result of `?:` should have been. Rust allows most statements to be used as expressions, with `if` statements being an important case of this: `Use(if cond { v1 } else { v2 })`. - This has a number of behaviors that would be surprising to developers coming from C++ and C, such as a final `;` in a `{...}` making a semantic difference. - The expression semantics leak into the statement semantics. For example, Rust rejects: ``` fn f() {} fn g() -> i32 {} fn main() { if true { f() } else { g() }; return; } ``` ... because the two arms of the `if` don't have the same type. - We have already [decided](https://github.com/carbon-language/carbon-lang/issues/430) that we do not want Carbon to treat statements such as `if` as being expressions without some kind of syntactic distinction. ## Proposal Provide a conditional expression with the syntax: > ``` > if cond then value1 else value2 > ``` `then` is a new keyword introduced for this purpose. ## Details ### Chaining This syntax can be chained like `if` statements: ``` Print(if guess < value then "Too low!" else if guess > value then "Too high!" else "Correct!") ``` Unlike with `if` statements, this doesn't require a special rule. ### Precedence and ambiguity An `if` expression can be used as a top-level expression, or within parentheses or a comma-separated list such as a function call. They have low precedence, so cannot be used as the operand of any operator, with the exception of assignment (if assignment is treated as an operator), but they can appear in other contexts where an arbitrary expression is permitted, for example as the operand of `return`, the initializer of a variable, or even as the condition of another `if` expression or `if` statement. ``` // Error, can't use `if` here. var v: i32 = 1 * if cond then 2 else 3 + 4; ``` `value2` extends as far to the right as possible: ``` var v: i32 = if cond then 2 else 3 + 4; ``` is the same as ``` var v: i32 = if cond then 2 else (3 + 4); ``` not ``` var v: i32 = (if cond then 2 else 3) + 4; ``` The intent is that an `if` expression is used to produce a value, not only for its side-effects. If only the side-effects are desired, an `if` statement should be used instead. Because `value2` extends as far to the right as possible, if an `if` expression appeared at the start of a statement, its value could never be used: ``` if cond then value1 else value2; ``` For this reason and to avoid the need for lookahead or disambiguation, an `if` keyword appearing at the start of a statement is always interpreted as beginning an `if` statement and never as beginning an `if` expression. ## Rationale based on Carbon's goals - [Language tools and ecosystem](/docs/project/goals.md#language-tools-and-ecosystem) - The `if ... then ... else` syntax should be easier to format automatically in an unsurprising way than a `?:` syntax because it is clear that the `then` and `else` keywords should be wrapped to the start of a new line when wrapping the overall conditional expression. - [Code that is easy to read, understand, and write](/docs/project/goals.md#code-that-is-easy-to-read-understand-and-write) - Including such an expression is expected to improve ergonomics. - An explicit delimiter for the start of the condition expression makes it easier to read, correctly write, and understand the precedence of conditional expressions. - Making the `value2` portion as long as possible gives a simple rule that it seems feasible for every Carbon developer to remember. This rule is expected to be unsurprising both due to using the same rule for `value1` and `value2`, and because it means that `if` consistently behaves like a very low precedence prefix operator. - The use of an explicit `if` keyword for flow control makes the distinction between flow control and linear computation clearer. - The readability of a multi-line `if` expression is improved by having a `then` and `else` keyword of the same length - [Interoperability with and migration from existing C++ code](/docs/project/goals.md#interoperability-with-and-migration-from-existing-c-code) - Migration is improved by providing an operator set that largely matches the C++ operator set. ## Alternatives considered ### No conditional expression We could provide no conditional expression, and instead ask people to use a different mechanism to achieve this functionality. Some options include: - Use of an `if` statement: ``` var v: Result; if (cond) { v = value1; } else { v = value2; } Use(v); ``` - A function call syntax: ``` Use(cond.Select(value1, value2)); ``` or, with short-circuiting and lambdas: ``` Use(cond.LazySelect($(value1), $(value2))); ``` - An `if` statement in a lambda: ``` Use(${ if (cond) { return value1; } else { return value2; } }); ``` The above assumes a placeholder `$(...)` syntax for a single-expression lambda, and a `${...}` syntax for a lambda with statements as its body. Advantages: - No new dedicated syntax. Disadvantages: - Conditional expressions are commonly used, commonly desired, and Carbon developers -- especially those coming from C++ and C -- will be disappointed by their absence. - Readability and ergonomics will be harmed by making this common operation more verbose, even if an idiom is established. ### Use C syntax We could use the C `cond ? value1 : value2` syntax. Advantages: - Familiar to developers coming from C++ and C. Disadvantages: - These operators have serious precedence problems in C++ and C. We could address those by making more cases ambiguous, at the cost of harming familiarity and requiring parentheses in more cases. - The `:` token is already in use in name binding; using it as part of a conditional expression would be confusing. - The `?` token is likely to be desirable for use in optional unwrapping and error handling. ### No `then` We could use > ``` > if (cond) value1 else value2 > ``` instead of > ``` > if cond then value1 else value2 > ``` Note that we cannot avoid parentheses in this formulation without risking syntactic ambiguities. Advantages: - Looks more like an `if` statement, albeit one with unbraced operands. - Slightly shorter. - Better line-wrapping for chained `if` expressions: ``` Print(if (guess < value) "Too low!" else if (guess > value) "Too high!" else "Correct!") ``` may be more readable than ``` Print(if guess < value then "Too low!" else if guess > value then "Too high!" else "Correct!") ``` or ``` Print(if guess < value then "Too low!" else if guess > value then "Too high!" else "Correct!") ``` Disadvantages: - Potentially worse line wrapping. The `else` would presumably be wrapped onto a line by itself, wasting vertical space, whereas `then` and `else` when paired can both comfortably precede their values on the same line; consider ``` F(if (cond) value1 else value2) ``` occupies more space than ``` F(if cond then value1 else value2) ``` - May create confusion between `if` statements and `if` expressions by resembling an `if` statement but not matching the semantics. - May cause evolutionary problems due to syntactic conflict if we ever make the braces or parentheses in `if` statements optional. - Requires parentheses, and hence additional presses of "Shift" on US keyboards, making it slightly harder to type. ### Require parentheses around the condition We could use: > ``` > if (cond) then value1 else value2 > ``` However, it's not clear that there is value in requiring both parentheses and a new keyword. It also seems jarring that this so closely resembles an `if` statement but adds a `then` keyword that the `if` statement lacks. ### Never require enclosing parentheses We could allow an `if` expression to appear anywhere a parenthesized expression can appear, and retain the rule that `value2` extends as far to the right as possible. Advantages: - Removes extra ceremony from a construct that is already more verbose than the corresponding `?:` construct in C++. - The requirement to include parentheses may be irritating in cases where there is no other possible interpretation, such as `1 + (if cond then 2 else 3)`. Disadvantages: - Visually ambiguous where `value2` ends in some cases, and violates precedence rules. - Hard for a simple yacc/bison parser to handle, due to ambiguity of `if` at the start of a statement and ambiguity when parsing `value2`. ### Variable-precedence `if` We could allow an `if` expression to appear anywhere a parenthesized expression can appear, and parse the `value1` and `value2` as if they appeared in place of the `if` expression: ```carbon var n: i32 = 1 + if cond then 2 * 3 else 4 * 5 + 6; // ... is interpreted as ... var n: i32 = (1 + (if cond then (2 * 3) else (4 * 5))) + 6; // Error: expected `else` but found `+ 4`. var m: i32 = 1 + if cond then 2 * 3 + 4 else 5 + 6; ``` Advantages: - Same as previous option. Disadvantages: - Confusing to readers, because it's not clear locally where the expression after `else` ends, and discovering this requires looking backwards to before the `if`. - Hard for a simple yacc/bison parser to handle, due to needing at least one production for an `if` statement for each precedence level. Also, those productions will result in grammar ambiguities that will need to be resolved. ### Implicit conversions in both directions Suppose we have two types where implicit conversions in both directions are possible: ```carbon class A {} class B {} impl A as ImplicitAs(B) { ... } impl B as ImplicitAs(A) { ... } ``` By default, an expression `if cond then {} as A else {} as B` would be ambiguous. If the author of `A` or `B` wishes to change this behavior: - If the common type should be `A`, then `impl A as CommonTypeWith(B)` must be provided specifying the common type is `A`. - If the common type should be `B`, then `impl B as CommonTypeWith(A)` must be provided specifying the common type is `B`. - If the common type should be something else, then both `impl`s need to be provided: ``` impl A as CommonTypeWith(B) { let Result:! Type = C; } impl B as CommonTypeWith(A) { let Result:! Type = C; } ``` We could change the rules so instead, in any of the above cases, implementing either `A as CommonTypeWith(B)` or `B as CommonTypeWith(A)` would suffice. Advantages: - Simplifies the user experience in this case. Disadvantages: - Introduces non-uniformity: the blanket `impl` of `CommonTypeWith` in terms of `ImplicitAs` would get this special treatment, but other blanket `impl`s would not. - Introduces complexity, which might not be fully hidden from users. At minimum, we would need to explain that `ImplicitAs` is treated specially here. - The case in which two `impl`s are required is a corner case. It's somewhat uncommon for implicit conversions to be possible in both directions between two types. In those cases, it's more uncommon for there to be a clear best "common type". And even then, most of the time the common type will be one of the two types being unified. From a more abstract perspective: the process of finding a common type involves asking each type to implicitly convert to the destination type that it thinks is best, and then failing if both sides didn't convert to the same type. If `A` implicitly converts to `B` and the other way around, then both sides of this process should be overridden in order to get both types to implicitly convert to `C` instead. ### Support lvalue conditionals Carbon doesn't formally have a notion of lvalue or rvalue yet; this notion is expected to be added by [#821: Values, variables, pointers, and references](https://github.com/carbon-language/carbon-lang/pull/821). In any case, we certainly intend to distinguish between expressions that represent values and expressions that represent locations where values could appear. We therefore need to decide whether a conditional expression can ever be in the latter category. For example: ```carbon var a: String; var b: String; var c: bool; // Valid? (if c then a else b) = "Hello"; ``` We could permit this, as C++ does. For example, we could say: > If both _value1_ and _value2_ are lvalues then > `if cond then value1 else value2` is rewritten to > `*(if cond then &value1 else &value2)` if those pointer types have a common > type. The other reason we might want to consider this alternative is performance. In C++, this code avoids making a `std::string` copy: ```c++ std::string a; std::string b; std::string c; bool cond; // ... bool equal = c == (cond ? a : b); ``` ... by treating the conditional expression as an lvalue of type `std::string` rather than as a prvalue. However, in Carbon, following #821, we would expect that the equivalent of a prvalue of type `std::string` would not necessarily imply that a copy is made. Rather, Carbon's equivalent of prvalues would represent either a set of instructions to initialize a value (as in C++), or the location of some existing value that we are temporarily "borrowing". With that in mind: Advantages: - More similar to C++. - Permits certain operations that have an obvious intended meaning, such as assignment to a conditional. Disadvantages: - Modification through an lvalue conditional is seldom used in C++, indicating that this is not an important feature. The other benefits of a conditional producing an lvalue are expected to be obtained by #821. - Mutable inputs to operations ("out parameters") in Carbon are expected to be expressed as pointers under #821, so there will be a `&` somewhere anyway; given the choice between an lvalue conditional: ``` F(&(if cond then a else b)); ``` and an rvalue-only conditional: ``` F(if cond then &a else &b); ``` the latter option would likely be preferred even if the former were available. - This would create an inconsistency in behavior, which would be particularly visible in a generic when determining what constraints are necessary to type-check an `if` expression -- the constraints would depend not only on operand types, but also on value category, and may result in a hard to express constraint such as "either `T*` and `U*` have a common type or `T` and `U` have a common type". - Certain kinds of lvalue conditional expression have turned out to be hard to implement in C++, such as a conditional involving bit-field lvalues. We can entirely avoid that class of implementation problems by treating conditional expressions as rvalues. This should be revisited if the direction in #821 changes substantially from the assumptions described above. ## Future work There are some known issues with the way that the extensibility mechanism works in this proposal. It is hoped that extensions to Carbon's generics mechanism will provide simple ways to resolve these issues. This design should be revisited once those mechanisms are available. ### Too many user-facing interfaces We provide both `CommonTypeWith`, as an extension point, and `CommonType`, as a constraint. It would be preferable to provide only a single name that functions both as the extension point and as a constraint, but we don't have a good way to automatically make `impl`s symmetric and avoid `impl` cycles if we use only one interface. ### Incompatible `CommonType` implementations diagnosed late Example: ``` class A {} class B {} impl A as CommonTypeWith(B) where .Result = A {} impl B as CommonTypeWith(A) where .Result = B {} fn F(a: A, b: B) -> auto { return if true then a else b; } ``` The definition of function `F` is rejected, because `A` and `B` have no (consistent) common type. It would be preferable to reject the `impl` definitions. ### `impl` ordering depends on operand order Example: ``` class A(T:! Type) {} class B(T:! Type) {} interface Fungible {} impl A(T:! Type) as Fungible {} impl B(T:! Type) as Fungible {} // #1 impl A(T:! Type) as CommonTypeWith(U:! Fungible) where .Result = A(T) {} // #2 impl B(T:! Type) as CommonTypeWith(A(T)) where .Result = T {} fn F(a: A(i32), b: B(i32)) -> auto { return if true then a else b; } ``` Here, reversed #2 is a better match than #1, because it matches both `A(?)` and `B(?)`, so #2 should be consider the best-matching `impl`. However, we never compare reversed #2 against non-reversed #1. Instead, we look for: 1. `impl A(i32) as SymmetricCommonTypeWith(B(i32))`, which selects #1 as being better than the blanket `impl` that reverses operand order. 2. `impl B(i32) as SymmetricCommonTypeWith(A(i32))`, which selects #2 as being better than the blanket `impl` that reverses operand order. So we decide that the `if` in `F` is ambiguous, even though there is a unique best `CommonTypeWith` match. If either #1 or #2 is written with the operand order reversed, then `F` would be accepted. ================================================ FILE: proposals/p0920.md ================================================ # Generic blanket impls (details 5) [Pull request](https://github.com/carbon-language/carbon-lang/pull/920) ## Table of contents - [Problem](#problem) - [Background](#background) - [Proposal](#proposal) - [Rationale based on Carbon's goals](#rationale-based-on-carbons-goals) - [Alternatives considered](#alternatives-considered) - [Different syntax](#different-syntax) - [Inconsistent syntax](#inconsistent-syntax) - [Members defined out-of-line](#members-defined-out-of-line) - [Context sensitivity](#context-sensitivity) - [Orphan rule could consider interface requirements in blanket impls](#orphan-rule-could-consider-interface-requirements-in-blanket-impls) - [Child trumps parent rule](#child-trumps-parent-rule) - [Impls could limit specialization](#impls-could-limit-specialization) - [Libraries for orphan impls](#libraries-for-orphan-impls) - [Different traversal order for overlap rule](#different-traversal-order-for-overlap-rule) - [Intersection rule in addition to prioritization](#intersection-rule-in-addition-to-prioritization) - [Name lookup into conditional impls](#name-lookup-into-conditional-impls) - [Automatic implicit conversions](#automatic-implicit-conversions) ## Problem There are cases where an impl definition should apply to more than a single type and interface combination. The solution is to parameterize the impl definition, so it applies to a family of types, interfaces, or both. This includes: - Declaring an impl for a parameterized type, which may be external or declared out-of-line. - "Conditional conformance" where a parameterized type implements some interface if the parameter to the type satisfies some criteria, like implementing the same interface. - "Blanket" impls where an interface is implemented for all types that implement another interface, or some other criteria beyond being a specific type. - "Wildcard" impls where a family of interfaces are implemented for single type. In addition to a syntax for defining parameterized impls, we need rules for coherence: - Orphan rules ensure that an impl is imported in any code that might use it. - Overlap rules pick a specific impl when more than one impl declaration matches a specific query about whether a type implements an interface. ## Background Rust supports parameterized impls, but has not stabilized the specialization feature that resolves multiple impls applying. ## Proposal This is a proposal to add [a "parameterized impls" section to the generics design details](/docs/design/generics/details.md#parameterized-impl-declarations). ## Rationale based on Carbon's goals Much of this rationale for generics was captured in the [Generics goals proposal](https://github.com/carbon-language/carbon-lang/pull/24). This proposal is particularly concerned with achieving the goal of making generics [coherent](/docs/design/generics/goals.md#coherence). ## Alternatives considered ### Different syntax This conditional interface implementation syntax was decided in [issue #575](https://github.com/carbon-language/carbon-lang/issues/575), and clarified in [a Discord discussion in #syntax](https://discord.com/channels/655572317891461132/709488742942900284/903036676371259432). A large part of what was decided in that issue is the syntax used for non-parameterized impls, and incorporated into the Carbon generics design in [proposal #553: Generics details part 1](https://github.com/carbon-language/carbon-lang/pull/553). #### Inconsistent syntax Our primary concern with the syntax for parameterized impls was that it be consistent with non-parameterized impls, and consistent between lexically inline and out-of-line definitions. Consistency was the basis for rejecting a number of conditional conformance options: - The `extend` syntax inline in a class definition to establish a more specific `Self` type for conditional conformance choice was eliminated along with [the `extend` syntax for external impls](p0553.md#extend-blocks). - Consistency excluded using deduced arguments in square brackets after the `impl` keyword, and an `if` or `where` clause to add constraints: ``` class FixedArray(T:! Type, N:! Int) { impl as [U:! Printable] Printable if T == U { // Here `T` and `U` have the same value and so you can freely // cast between them. The difference is that you can call the // `Print` method on values of type `U`. } } class Pair(T:! Type, U:! Type) { impl as Foo(T) if T == U { // Can cast between `Pair(T, U)` and `Pair(T, T)` since `T == U`. } } ``` This was too different from how those same impls would be declared out-of-line. - Another approach that was too different between inline and out-of-line, is to use pattern matching instead of boolean conditions. This might look like: ``` class FixedArray(T:! Type, N:! Int) { @if let P:! Printable = T { impl as Printable { ... } } } interface Foo(T:! Type) { ... } class Pair(T:! Type, U:! Type) { @if let Pair(T, T) = Self { impl as Foo(T) { ... } } } ``` #### Members defined out-of-line We rejected options that declared unqualified member names outside of the scope defining the class. This would have allowed us to consistently use an out-of-line syntax, but starting with something other than `external` when it affected the names in the class. #### Context sensitivity We rejected options for conditional conformance where the meaning of names declared in the class scope would have a more specific meaning in an inner scope. This would be much like how a language with [flow-sensitive typing](https://en.wikipedia.org/wiki/Flow-sensitive_typing) might affect the types in an inner scope as part of control flow. For example, a `where` clause on an `impl` declaration might add constraints to a class parameter only inside the scope of the `impl` it was applied to: ``` class FixedArray(T:! Type, N:! Int) { impl as Printable where T is Printable { // Inside this scope, `T` has type `Printable` instead of `Type`. } } ``` These options were rejected based on the [low-context-sensitivity principle](/docs/project/principles/low_context_sensitivity.md). ### Orphan rule could consider interface requirements in blanket impls The orphan rule states that a rule like ``` impl [T:! Interface1] T as Interface2 { ... } ``` can only live in the library that defines `Interface2`, not the library that defines `Interface1`. To see that the alternative is not coherent, consider this example where we have three libraries, with one a common library imported by the other two: - Library `Common` ``` interface ICommon { ... } class S { ... } ``` - Library `A`: ``` import Common interface IA { ... } // Local interface only used as a constraint impl [T:! IA] T as ICommon { ... } // Fine: implementation of a local interface impl S as IA { ... } ``` - Library `B`: ``` import Common interface IB { ... } // Local interface only used as a constraint impl [T:! IB] T as ICommon { ... } // Fine: implementation of a local interface impl S as IB { ... } ``` Inside another library that imports the `Common` library: - Does `S` implement `ICommon`? If you just import `ICommon`, no implementations are visible - Does the answer change if you import libraries `A` or `B`? - Which implementation of `ICommon` should `S` use if you import both? We avoid these problems by requiring the use of a local type or interface **outside** of constraints. ### Child trumps parent rule [Rust has considered a "child trumps parent" rule](http://aturon.github.io/tech/2017/02/06/specialization-and-coherence/). This rule would say that a library `Child` importing library `Parent` is enough to prioritize `impl` definitions in `Child` over `Parent` when they would otherwise overlap without one matching a strict subset of the other. The goal would be to resolve overlaps in a way that is both easy to understand and more often matches what implementations users actually want prioritized. One caveat of this rule is that a simple interpretation is not transitive. If we define three impls in three different libraries, with these type structures: - `impl (A, ?, ?) as I` - `impl (?, B, ?) as I` - `impl (?, ?, C) as I` then the type structure rule would prioritize `A` over `B` over `C`. If the library with `C` had a dependency on the one with `A`, though, then `C` would have priority over `A`, and we would not be able to decide which impl to use for `(A, B, C)`. The fix is to change the rule to be "Child trumps parent on their intersection". With that rule, it would be as if there was another implementation defined on the intersection of `(A, ?, ?)` and `(?, ?, C)`, that is it would match `(A, ?, C)`, that had the highest priority and delegated to the `(?, ?, C)` impl for the definition of the body. Without some child trumps parent rule: If I define a new type, then all impl lookup for interfaces implemented by that type as `Self` will consider impl from my library first, at the time I define it until some other library adds an impl of that type as `Self`. However, adding the "child trumps parent on their intersection" rule removes this property. This is something we would consider in the future once we have more experience. Note that this rule has not yet been implemented in Rust, so we don't know how it works out in practice. ### Impls could limit specialization This would allow greater reasoning in generic functions, requiring less specification. For example, there may be a blanket implementation of `PartiallyOrdered` provided for types that implement `Ordered`. If that blanket implementation could not be specialized, a generic function could rely on the implementations of the two interfaces being consistent with each other. This feature has been left for a future proposal. Note this feature needs to be limited to those cases where the implementation that limits specialization will be guaranteed to be a dependency of all implementations that are prioritized as more specific. ### Libraries for orphan impls The [orphan rule](/docs/design/generics/details.md#orphan-rule) means that there may be no library that can define an impl with a particular type structure. Rust has encountered this problem already, see [Rust RFC #1856: "Orphan rules are stricter than we would like"](https://github.com/rust-lang/rfcs/issues/1856). Carbon does not currently address this problem, but we would consider future changes that do as long as coherence was maintained. For example, we'd consider a mechanism that allowed libraries to be defined that must be imported, either implicitly or explicitly, depending on whether specific other libraries are imported or linked into the project. ### Different traversal order for overlap rule The [overlap rule](/docs/design/generics/details.md#overlap-rule) uses a depth-first traversal of the type tree to identify the first difference between two different type structures. We could also do another order, such as breadth-first, but this order is both simple and reflects some experience from the Rust community that the `Self` type is particularly important to prioritize. ### Intersection rule in addition to prioritization Another approach for [selecting between impls with the same type structure](/docs/design/generics/details.md#prioritization-rule) is to require that there is an impl matching the intersection of any two impls with the same type structure, and then prioritize by containment. This approach is being considered for Rust, see [Baby Steps: "Intersection Impls"](http://smallcultfollowing.com/babysteps/blog/2016/09/24/intersection-impls/) and [Aaron Turon: "Specialization, coherence, and API evolution](http://aturon.github.io/tech/2017/02/06/specialization-and-coherence/). Instead of requiring that impls with the same type structure are always in the same prioritization block, that requirement could be only when the intersection isn't defined. The advantage of the prioritization block is that it can express everything that intersection impls can express, and generally can do so without having to write an exponential number of impl declarations. Prioritization blocks do require you to write all the impls together in a block, though, so we might want to support the option of allowing developers to explicitly write out intersections instead. This might be convenient in cases where the intersection is defined anyway, such as when there is already strict subset relationship between the impls. ### Name lookup into conditional impls We considered the possibility that name lookup would fail to find the members of that impl when a conditional impl's condition does not apply. This would allow ``` class X(T:! Type) { impl X(i32) as Foo { fn F[me: Self](); } impl X(i64) as Bar { fn F[me: Self](n: i64); } } ``` where `X(T).F` means different things for different `T`, which could be an unpleasant surprise. This seemed against the Carbon philosophy of making the code behave consistently and predictably, and didn't have a motivating use case. ### Automatic implicit conversions We considered adding two features to support operator overloading use cases, where the language would select the interface parameter using the type of the second argument without considering implicit conversions. The intent of these features was to, for example, make it convenient to support addition with anything that implicitly converted to `i32` by implementing addition with just `i32`. One feature was to support implementing an interface using a function with a different signature, as long as the compiler could automatically generate a wrapper that bridged the two signatures using implicit conversions. This raised concerns about accidentally implementing interfaces with functions that had the wrong signature, evolution problems when new overloads were introduced, and surprises and confusing errors from a single function considered to have two different signatures. The other feature was to allow a wildcard implementation as long as it could be implemented using functions that did not vary with the wildcard parameter, by implicitly converting to a common type. This introduced concerns about what to do with other members of the interface, particularly those with defaults. Perhaps we would allow functions as long as the interface parameters could be deduced from the types of the arguments? Perhaps we would require explicit qualification to call functions where the interface parameter couldn't be deduced? In particular we were worried about evolution, and allowing users to add functions to an interface as long as they had defaults. These problems were discussed in [2021-12-08 open discussion](https://docs.google.com/document/d/1YhwNKLxQsWf8NPVaRm9PvgPmSM3PIK_KlD1gpNuUfwY/edit#heading=h.njnqpdcjp5h0). We concluded that we will later find other mechanisms to support this use case. ================================================ FILE: proposals/p0931.md ================================================ # Generic impls access (details 4) [Pull request](https://github.com/carbon-language/carbon-lang/pull/931) ## Table of contents - [Problem](#problem) - [Background](#background) - [Proposal](#proposal) - [Rationale based on Carbon's goals](#rationale-based-on-carbons-goals) - [Alternatives considered](#alternatives-considered) - [Private impls for public types](#private-impls-for-public-types) - [Private interfaces in public API files](#private-interfaces-in-public-api-files) ## Problem Should a type be able to declare an `impl` to be private, like it can for its data and methods? There are some use cases for this feature, but those use cases generally could also be addressed by implementing the interface on a private adapter type instead. ## Background Private impls have been considered but not implemented for Swift in the [scoped conformances pitch](https://forums.swift.org/t/scoped-conformances/37159). ## Proposal This is a proposal to add to [this design document on generics details](/docs/design/generics/details.md). The additions are: - to say impls do not allow access control modifiers and are always as public as the names used in the signature of the impl; and - to document how to use private adapter types instead. ## Rationale based on Carbon's goals We decided to make generics coherent as part of accepting proposal [#24: generics goals](https://github.com/carbon-language/carbon-lang/pull/24). This approach is consistent with broader Carbon goals that Carbon have [code that is easy to read, understand, and write](/docs/project/goals.md#code-that-is-easy-to-read-understand-and-write). In particular, we favor making code [less context sensitive](/docs/project/principles/low_context_sensitivity.md). ## Alternatives considered ### Private impls for public types We considered supporting private impls for public types. This was motivated by using the conversion of a `struct` type to a `class` type to construct class values. In the case that the class had private data members, we wanted to restrict that conversion so it was only available in the bodies of class members or friends of the class. We considered achieving that goal by saying that the implementation of the conversion would be private in that case. Allowing impls to generally be private, though, led to a number of coherence concerns that the same code would behave differently in different files. Our solution was to address these conversions using a different approach that only addressed the conversion use case: - Users could not implement `struct` to `class` conversions themselves. - The compiler would generate `struct` to `class` conversions for constructing class values itself. - Those conversion impls will always be as visible as the class type, and will still be selected using the same rules as other impls. - When one of these compiler-generated conversion impls is selected, the compiler would perform an access control check to see if it was allowed. We believe that other use cases for restricting access to an impl are better accomplished by using a private adapter type than supporting private impls. ### Private interfaces in public API files A private interface may only be implemented by a single library, which gives the library full control. We considered and rejected the idea that developers could put that interface declaration in an API file to allow it to be referenced in named constraints available to users. All impls for that interface would also have to be declared in the API file, unless they were for a private type declared in that library. This would allow you to express things like: - A named constraint that is satisfied for any type **not** implementing interface `Foo`: ``` class TrueType {} class FalseType {} private interface NotFooImpl { let IsFoo:! Type; } impl [T:! Foo] T as NotFooImpl { let IsFoo:! Type = TrueType; } impl [T:! Type] T as NotFooImpl { let IsFoo:! Type = FalseType; } constraint NotFoo { extends NotFooImpl where .IsFoo == FalseType; } ``` - A named constraint that ensures `CommonType` is implemented symmetrically: ``` // For users to implement for types interface CommonTypeWith(U:! Type) { let Result:! Type where ...; } // internal library detail private interface AutoCommonTypeWith(U:! Type) { let Result:! Type where ...; } // To use as the requirement for parameters constraint CommonType(U:! AutoCommonTypeWith(Self)) { extends AutoCommonTypeWith(U) where .Result == U.Result; } match_first { impl [T:! Type, U:! ManualCommonTypeWith(T)] T as AutoCommonTypeWith(U) { let Result:! auto = U.Result; } impl [T:! Type, U:! ManualCommonTypeWith(T)] U as AutoCommonTypeWith(T) { let Result:! auto = U.Result; } } ``` This feature is something we might consider adding in the future. ================================================ FILE: proposals/p0950.md ================================================ # Generics details 6: remove facets [Pull request](https://github.com/carbon-language/carbon-lang/pull/950) ## Table of contents - [Problem](#problem) - [Proposal](#proposal) - [Details](#details) - [Rationale based on Carbon's goals](#rationale-based-on-carbons-goals) - [Alternatives considered](#alternatives-considered) - [Status quo: name lookup only in the type-of-type](#status-quo-name-lookup-only-in-the-type-of-type) - [Status quo: facet types](#status-quo-facet-types) - [Name lookup in type and type-of-type](#name-lookup-in-type-and-type-of-type) - [No generic 'let'](#no-generic-let) - [Generic 'let' erases connection to original type](#generic-let-erases-connection-to-original-type) ## Problem There were some concerns about facet types leaking out of generic code in return types. Some initial fixes for this were done in [PR #900](https://github.com/carbon-language/carbon-lang/pull/900), but there remain concerns, for example when associated types are involved. In particular, given an interface method with return type using an associated type, as in: ``` interface Deref { let Result:! Type; fn DoDeref[me: Self]() -> Result; } class IntHandle { impl Deref { let Result:! Type = i32; fn DoDeref[me: Self]() -> Result { ... } } } ``` Since `Result` has type `Type`, we had the problem that `IntHandle.DoDeref` would have to return `i32 as Type`, instead of the desired `i32`. We also think we can simplify the model by eliminating the facet type concept and syntax. ## Proposal This proposal removes facet types, introduces archetypes in their place, clarifies how associated types work outside of a generic function, and specifies how a generic `let` statement in a function body works. ## Details The details of this proposal are in the changes to these generics design documents: - [Overview](/docs/design/generics/overview.md) - [Goals](/docs/design/generics/goals.md) - [Terminology](/docs/design/generics/terminology.md) - [Details](/docs/design/generics/details.md) ## Rationale based on Carbon's goals This proposal [adds a goal](/docs/design/generics/goals.md#path-from-regular-functions) about minimizing the differences between a regular function and one with a generic parameter from the perspective of the caller. This is to support the [software and language evolution](/docs/project/goals.md#software-and-language-evolution) goal by reducing the amount of changes needed to source code when generalizing a function. This change is also a simplification to the programming model, removing a concept that developers have to learn and reducing the number of types a program deals with. This is in support of the [code that is easy to read, understand, and write](/docs/project/goals.md#code-that-is-easy-to-read-understand-and-write) goal. ## Alternatives considered This design was the conclusion of a number of discussions and proposals: - [#typesystem discussion in Discord on Nov 2](https://discord.com/channels/655572317891461132/708431657849585705/905248525028323368) - Nov 3, 2021 document by `zygoloid` titled [Member lookup in generic and non-generic contexts](https://docs.google.com/document/d/1-vw39x5YARpUZ0uD2xmKepLEKG7_u122CUJ67hNz3hk/edit#) - Nov 4, 2021 document by `josh11b` and `mconst` titled [Carbon: facets exposed from generics](https://docs.google.com/document/d/1C1eIzd6JY0ooE1rDjW1vx7e3i7sgGugCA9bPMRhwWM0/edit#) - [Open discussion on 2021-11-08](https://docs.google.com/document/d/1YhwNKLxQsWf8NPVaRm9PvgPmSM3PIK_KlD1gpNuUfwY/edit#heading=h.ec285oam2okw) - [Open discussion 2021-11-11](https://docs.google.com/document/d/1YhwNKLxQsWf8NPVaRm9PvgPmSM3PIK_KlD1gpNuUfwY/edit#heading=h.8vuatm82d1mk) The main alternatives we evaluated are summarized below. ### Status quo: name lookup only in the type-of-type The main problem with the status quo is that we would look up names in the type-of-type whether or not the type value was known. This meant that there was no way to have a type variable that didn't alter the API of the type it held, even when the value of that variable was known at compile time. It also created problems putting a particular facet type into an associated type, as was desired when computing the `CommonType` of two facets of the same type, since the type-of-type of the associated type would overwrite the facet. This last concern was raised in this [#typesystem discussion in Discord on Nov 2](https://discord.com/channels/655572317891461132/708431657849585705/905248525028323368). ### Status quo: facet types Facet types as a user-visible type expression `MyType as MyConstraint` have a few downsides: - Facet types introduce extra types for the user to concern themselves with. - Facet types introduce extra types for instantiation purposes. - We don't want the possibility of facet types leaking outside of generic code. Archetypes have replaced using facet types to type check a function with a generic parameter. Generic `let` and adapter types address trying to repeatedly access methods from an interface implemented externally. We considered making the other changes in this proposal with a different way of forming a facet type than `MyType as MyConstraint` [in this document](https://docs.google.com/document/d/1C1eIzd6JY0ooE1rDjW1vx7e3i7sgGugCA9bPMRhwWM0/edit#). Ultimately we all agreed that we did not want to instantiate templates called with a generic type with a facet type, and the removal of facet types simplifies the design. ### Name lookup in type and type-of-type [`zygoloid` proposed](https://docs.google.com/document/d/1-vw39x5YARpUZ0uD2xmKepLEKG7_u122CUJ67hNz3hk/edit#) a solution of looking up names in both the type and the type-of-type, resolving conflicts in the same way `&` would between two type-of-types. This has the nice property of using the type information when it is available, but still allowing the type-of-type to affect name lookup. This made the code inside and outside a generic consistent as long as there were no name conflicts. The downside is that this still involved types potentially changing when you added a generic parameterization, even though the changes were smaller, and didn't have the simplification advantages of removing facet types entirely. We did like the idea from [that proposal](https://docs.google.com/document/d/1-vw39x5YARpUZ0uD2xmKepLEKG7_u122CUJ67hNz3hk/edit#) that we would perform look up in the type when it was a constant, since that addressed the main problem we were trying to address. ### No generic 'let' Generic `let` has two main use cases: manual monomorphizing a generic and making repeated calls to members of an interface implemented externally more convenient. The former may be performed by using qualified member names, and the latter by an adapter type, so this feature is not strictly needed. However, `chandlerc` believes including generic `let` is more consistent, for example with the use of generic `let` to declare associated types in interfaces and implementations. It make replicating the change in type behavior of calling a generic function much more straightforward, without having to introduce a function call or rewriting all of the member accesses to add qualification. Removing generic `let` is a simplification we would consider in the future. ### Generic 'let' erases connection to original type We considered the idea that a generic `let` would fully erase type identity, [on discord as "option A"](https://discord.com/channels/655572317891461132/708431657849585705/908834806551445554). This didn't have any clear advantages other than making a generic `let` in a function body be more similar to a generic function parameter. Erasing the type identity would have made generic `let` much harder to use, though, without any clear ways to get values of the new type. The option we chose is more similar to generic `let` used to declare an associated type. ================================================ FILE: proposals/p0981.md ================================================ # Implicit conversions for aggregates [Pull request](https://github.com/carbon-language/carbon-lang/pull/981) ## Table of contents - [Problem](#problem) - [Background](#background) - [Proposal](#proposal) - [Details](#details) - [Rationale based on Carbon's goals](#rationale-based-on-carbons-goals) - [Alternatives considered](#alternatives-considered) - [Field order is not significant](#field-order-is-not-significant) - [Different field orders are incompatible](#different-field-orders-are-incompatible) - [Explicit instead of implicit conversions](#explicit-instead-of-implicit-conversions) ## Problem What operations are supported by default between two data classes with the same fields in different orders? What implicit conversions are allowed between aggregates, such as arrays, tuples, and data classes? ## Background - Comparison operators more generally were added to Carbon in [proposal #702: Comparison operators](https://github.com/carbon-language/carbon-lang/pull/702) - An initial take on these questions was originally in proposal [proposal #561: Basic classes: use cases, struct literals, struct types, and future work](https://github.com/carbon-language/carbon-lang/pull/561) but postponed until we had agreement on the right approach. - The discussion that eventually reached an agreement took place in [question-for-leads issue #710: Default comparison for data classes](https://github.com/carbon-language/carbon-lang/issues/710) ## Proposal We propose that we should permissively allow operations between data classes and other aggregates with different field orders and types where we can. Field order in data classes is salient, but mostly determines the order that operations are performed. The only case where different field orders will forbid an operation is with ordering comparisons, where the field order determines the answer returned, not just the order of execution. ## Details Changes have been made to: - [docs/design/classes.md](/docs/design/classes.md), - [docs/design/tuples.md](/docs/design/tuples.md). These changes are intended to apply to all aggregate types, including arrays. ## Rationale based on Carbon's goals This proposal advances Carbon's goals: - [Software and language evolution](/docs/project/goals.md#software-and-language-evolution): This proposal allows changes to field order and type to be made incrementally. - [Code that is easy to read, understand, and write](/docs/project/goals.md#code-that-is-easy-to-read-understand-and-write): This proposal provides useful and expected facilities for data classes and other aggregate types by default. ## Alternatives considered Alternatives were considered in: - [question-for-leads issue #710: Default comparison for data classes](https://github.com/carbon-language/carbon-lang/issues/710) - [open discussion on 2021-11-29](https://docs.google.com/document/d/1YhwNKLxQsWf8NPVaRm9PvgPmSM3PIK_KlD1gpNuUfwY/edit#heading=h.6komy889g3hc) ### Field order is not significant We considered not making field order significant in struct types, making them into unordered collections of named fields. This view is consistent with order not being significant in initializers in prior class proposal [#561](https://github.com/carbon-language/carbon-lang/pull/561). This had a number of consequences: - Users would not have control over the order of operations performed field-by-field, such as comparisons, unless they use a nominal class type and implement those operations explicitly. - Order comparison operators, like `<` and `<=`, would not be supported. We considered defining an unspecified-but-fixed ordering, for use in things like binary search, accessed in some other way than the ordinary comparison operators. Ultimately, we decided that field order was a salient property of struct types, at least determining the layout of the data in memory, and we should use it to determine the order of operations and how to compare lexicographically. **Reference:** See [this comment by `geoffromer` on #710](https://github.com/carbon-language/carbon-lang/issues/710#issuecomment-893866801). ### Different field orders are incompatible Rather than picking the left-hand argument's field order when the orders were different, we considered requiring the field order to match when performing all comparisons, including equality comparisons. An explicit conversion to a common type would be required to perform a comparison when the field orders did not match. The current proposal is more convenient for users, and has the property that executing `a = b` results in the condition `a == b` being true, even when `a` and `b` have different field orders. We also believed that operations like assignment between structs with different field orders would be more efficiently implemented using field-by-field assignment rather than a conversion to the left-hand type followed by assignment, and so it was natural to support the former directly. ### Explicit instead of implicit conversions We expected a lot of code trying to pass values between functions using different field orders would use destructuring instead of direct conversion. As a result, we thought it might be safer to require explicit conversions to avoid silently converting, say, 10,000 `i8` values to `i64`. However, there were some important use cases for performing the conversion implicitly, such as `(1, 1)` converting to an `(i8, i8)` value. We did not want rules that distinguish this case from other implicit conversions, for simplicity. Similarly, we wanted the set of conversions to be consistent across aggregate types, including tuples, arrays, and data classes. We can amend these rules in the future to address cases that are surprising to users in practice. **Reference:** See [this comment by `chandlerc` on #710](https://github.com/carbon-language/carbon-lang/issues/710#issuecomment-983579560). ================================================ FILE: proposals/p0983.md ================================================ # Generics details 7: final impls [Pull request](https://github.com/carbon-language/carbon-lang/pull/983) ## Table of contents - [Problem](#problem) - [Background](#background) - [Proposal](#proposal) - [Details](#details) - [Rationale based on Carbon's goals](#rationale-based-on-carbons-goals) - [Alternatives considered](#alternatives-considered) - [No `final`, all parameterized impls may be specialized](#no-final-all-parameterized-impls-may-be-specialized) - [`final` associated constants instead of `final` impls](#final-associated-constants-instead-of-final-impls) ## Problem Allowing an impl to be specialized can lead to higher performance if there are parameter values for which a more optimized version can be written. However, not all impls will be specialized and there are some benefits when that is known: - The values of associated types can be assumed to come from the impl. In many cases this means leaking fewer implementation details into the signature of a function using generics. - The bodies of functions from the impl could be inlined into the caller even when using a more dynamic implementation strategy rather than monomorphization. However, not all impls can opt-out of specialization, since this can create incompatibilities between unrelated libraries. For example, consider two libraries that both import parameterized type `TA` and interface `I`: - Library `LB` that defines type `TB` can define an impl with type structure `impl TA(TB, ?) as I`. - Library `LC` that defines type `TC` can define an impl with type structure `impl TA(?, TC) as I`. Both of these are allowed under [Carbon's current orphan rules](/docs/design/generics/details.md#orphan-rule). A library `LD` that imports both `LB` and `LC` could then query for the implementation of `I` by `TA(TB, TC)` and would use the definition from library `LB`, which would be a conflict if library `LC` marked its impl definition as not specializable. ## Background Rust currently does not support specialization, so for backwards compatibility impls are final by default in Rust's specialization proposal. ## Proposal We propose that impls can be declared `final`, but only in libraries that must be imported by any file that would otherwise be able to define a higher-priority impl. ## Details Details are in [the added `final` impl section to the generics details design document](/docs/design/generics/details.md#final-impl-declarations). ## Rationale based on Carbon's goals This proposal supports the following of Carbon's goals: - [Performance-critical software](/docs/project/goals.md#performance-critical-software): the ability to inline functions defined in `final` impls will in some cases improve performance. - [Software and language evolution](/docs/project/goals.md#software-and-language-evolution): reducing how much implementation details are exposed in a generic function's signature allows that function to evolve. - [Code that is easy to read, understand, and write](/docs/project/goals.md#code-that-is-easy-to-read-understand-and-write): reducing the list of requirements in a generic function signature is an improvement to both readability and writability. Furthermore, `final` impls are a tool for making code more predictable. For example, making the dereferencing impl for pointers final means it always does the same thing and produces a value of the expected type. ## Alternatives considered ### No `final`, all parameterized impls may be specialized In addition to the [problems listed above](#problem), we ran into problems in proposal [#911](https://github.com/carbon-language/carbon-lang/pull/911) trying to use the `CommonType` interface to define the type of a conditional expression, `if then else `. The idea is that `CommonType` implementations would specify how to combine the types of the `` and `` expressions using an associated type. In generic code, however, there was nothing to guarantee that there wouldn't be a specialization that would change the result. As a result, nothing could be concluded about the common type if either expression was generic. If at least the common type of two equal types was guaranteed, then you could use an explicit cast to make sure the types were as expected. Some method of limiting specialization was needed. We considered other approaches, such as using the fact that the compiler could see all implementations of private interfaces, but that didn't address other use cases. For example, we don't want users to be able to customize dereferencing pointers for their types so that dereferencing pointers behaves predictably in generic and regular code. ### `final` associated constants instead of `final` impls We considered allowing developers to mark individual items in an impl as `final` instead. This gave developers more control, but we didn't have examples where that extra control was needed. It also introduced a number of complexities and concerns. The value for a `final let` could be an expression dependent on other associated constants which could be `final` or not. Checking that a refining impl adheres to that constraint is possible, but subtle and possibly tricky to diagnose mistakes clearly. If an impl matches a subset of an impl with a `final let`, how should the narrower impl comply with the restriction from the broader? ``` interface A { let T:! type; } impl [U:! Type] Vector(U) as A { final let T:! Type = i32; } impl Vector(f32) as A { // T has to be `i32` because of the `final let` // from the previous impl. What needs to be // written here? } ``` We considered two different approaches, neither of which was satisfying: - **Restate approach:** It could restate the `let` with a consistent value. This does not give any indication that the `let` value is constrained, and what impl is introducing that constraint, leading to spooky action at a distance. It was unclear to us whether the restated `let` should use `final` as well, or maybe some other keyword? - **Inheritance approach:** We could have a concept of inheriting from an impl, and require that any impl refining an impl with `final` members must inherit those values rather than declaring them. Inheritance between impls might be a useful feature in its own right, but requires there be some way to name the impl being inherited from. Consider two overlapping impls that both use `final let`. The compiler would need to validate that they are consistent on their overlap, a source of complexity for the user. An impl that overlaps both would have to be consistent with both, but would not be able to inherit from both, a problem with using the inheritance approach. Ultimately we decided that this approach had a lot of complexity, concerns, and edge cases and we could postpone trying to solve these problems until such time as we determined there was a need for the greater expressivity of being able to mark individual items as `final`. This discussion occurred in: - [Document examining an extended example using specialization](https://docs.google.com/document/d/1w-kRC338Jc1ibTu7Vf0pOlGKdrpumfz63bzUIxEj9jY/edit) - [2021-11-29 open discussion](https://docs.google.com/document/d/1YhwNKLxQsWf8NPVaRm9PvgPmSM3PIK_KlD1gpNuUfwY/edit#heading=h.6komy889g3hc) - [Carbon's #typesystem channel on Discord](https://discord.com/channels/655572317891461132/708431657849585705/910681126236987495) ================================================ FILE: proposals/p0989.md ================================================ # Member access expressions [Pull request](https://github.com/carbon-language/carbon-lang/pull/989) ## Table of contents - [Problem](#problem) - [Background](#background) - [Proposal](#proposal) - [Details](#details) - [Rationale based on Carbon's goals](#rationale-based-on-carbons-goals) - [Alternatives considered](#alternatives-considered) - [Separate syntax for static versus dynamic access](#separate-syntax-for-static-versus-dynamic-access) - [Use a different lookup rule in templates](#use-a-different-lookup-rule-in-templates) - [Meaning of `Type.Interface`](#meaning-of-typeinterface) ## Problem We need syntaxes for a number of closely-related operations: - Given an expression denoting a package, namespace, class, interface, or similar, and the name of one of its members, form an expression denoting the member. In C++ and Rust, this is spelled `Container::MemberName`. In many other languages, it is spelled `Container.MemberName`. - Given an expression denoting an object and a name of one of its fields, form an expression denoting the corresponding subobject. This is commonly written as `object.field`, with very little deviation across languages. - Given an expression denoting an object and a name of one of its methods, form an expression that calls the function on the object. This is commonly written as `object.function(args)`. - Given an expression denoting a type, and an expression denoting a member of an interface, form an expression denoting the corresponding member in the `impl` of that interface for that type. Further, we need rules describing how the lookup for the member name is performed, and how this lookup behaves in generics and in templates in cases where the member name depends on the type or value of the first operand. ## Background C++ and Rust distinguish between the first use case and the rest. Other languages, such as Swift and C#, do not, and model all of these use cases as some generalized form of member access, where the member might be a namespace member, an interface member, an instance member, or similar. See also: - [Exploration of member lookup in generic and non-generic contexts](https://docs.google.com/document/d/1-vw39x5YARpUZ0uD2xmKepLEKG7_u122CUJ67hNz3hk/edit) - [Question for leads: constrained template name lookup](https://github.com/carbon-language/carbon-lang/issues/949) ## Proposal All these operations are performed using `.`: ```carbon fn F() { // Can perform lookup inside the package or namespace. var x: Package.Namespace.Class; // Can perform lookup inside the type of the value. x.some_field = x.SomeFunction(1, 2, 3); } ``` When the type of the left-hand operand is a generic type parameter, lookup is performed in its type-of-type instead. Effectively, a generic type parameter behaves as an archetype: ```carbon interface Hashable { let HashValue:! Type; fn Hash[me: Self]() -> HashValue; fn HashInto[me: Self](s: HashState); } fn G[T:! Hashable](x: T) { // Can perform lookup inside the type-of-type if the type is // a generic type parameter. x.Hash(); } ``` When the type of the left-hand operand is a template parameter, the lookup is performed both in the actual type corresponding to that template parameter and in the archetype, as described above. If a result is found in only one lookup, or the same result is found in both lookups, that result is used. Otherwise, the member access is invalid. ```carbon class Potato { fn Mash[me: Self](); fn Hash[me: Self](); alias HashValue = Hashable.HashValue; } external impl Potato as Hashable where .HashValue = u32 { // ... } fn H[template T:! Hashable](x: T, s: HashState) { // When called with T == Potato: // ❌ Ambiguous, could be `Potato.Hash` or `Hashable.Hash`. x.Hash(); // ✅ OK, found only in `Potato`. x.Mash(); // ✅ OK, found only in `Hashable`. x.HashInto(s); // ✅ OK, same `HashValue` found in both `Potato` and `Hashable`; // `Hashable.Hash` unambiguously names the interface member. var v: T.HashValue = x.(Hashable.Hash)(); // ✅ OK, unambiguously names the type member. x.(Potato.Hash)(); } ``` ## Details See [the changes to the design](https://github.com/carbon-language/carbon-lang/pull/989/files). ## Rationale based on Carbon's goals - [Software and language evolution](/docs/project/goals.md#software-and-language-evolution) - Rejecting cases in a template where a generic interpretation and an interpretation with specific types would lead to different meanings supports incremental migration towards generics by way of a template, where the compiler will help you find places that would change meaning. - [Code that is easy to read, understand, and write](/docs/project/goals.md#code-that-is-easy-to-read-understand-and-write) - Using a single, familiar `container.member` notation for all the member access use cases minimizes the complexity of this portion of the language syntax. - [Interoperability with and migration from existing C++ code](/docs/project/goals.md#interoperability-with-and-migration-from-existing-c-code) - The behavior of templates is aligned with that in C++, simplifying both comprehension for C++ developers and migration of C++ code. ## Alternatives considered ### Separate syntax for static versus dynamic access We could follow C++ and Rust, and use `::` for static lookup, reserving `.` for instance binding: ``` var x: Package::Namespace::Class; Class::Function(); x.field = x.Function(); x.(Interface::Method)(); ``` Advantages: - Visually separates operations that readers may think of as being distinct: a `::` path statically identifies an object whereas a `.` path dynamically identifies a subobject or forms a bound method. - Improves familiarity for those coming from C++. - Removes most of the need for parenthesized member access: `a.(b.c)` would generally become `a.b::c`, like in C++. Disadvantages: - Adds a new token and a new operation. - Swift, C#, and Java do not distinguish these operations syntactically, and we have no evidence that this lack of syntactic distinction creates problems for them in practice. - Likely to result in complexity and inconsistency for operations falling between the two options. For example, in C++: ``` struct A { static void F(); enum { e }; }; enum class B { e }; void G(A a, B b) { a.F(); // OK, but static dispatch, like A::F(). a.e; // OK, but static dispatch, like A::e. b.e; // Error. } ``` - Does not provide an obvious syntax for `impl` lookup. `Type::Interface::method` would be ambiguous and `Type.Interface::method` would be inconsistent with using `::` for static lookup, so we would likely end up with `Type::(Interface::method)` syntax or similar. - May create the suggestion that `.`s imply a performance-relevant operation and `::`s do not. This will typically not be the case, as `.`s will typically result in, at worst, a constant offset. However, `impl` lookup, which may be performed by either a `.` or a `::`, may require a memory access in cases where dynamic dispatch is in use. ### Use a different lookup rule in templates See [question for leads: constrained template name lookup](https://github.com/carbon-language/carbon-lang/issues/949) for more in-depth discussion and leads decision. Given a situation where the same name can be found in both a type and a constraint when instantiating a template, and resolves to two different things, we could use various different rules to pick the outcome: ``` class Potato { fn Bake[me: Self](); fn Hash[me: Self](); } interface Hashable { fn Hash[me: Self]() -> HashState; fn HashInto[me: Self](s: HashState); } external impl Potato as Hashable; fn MakePotatoHash[template T:! Hashable](x: T, s: HashState) { x.Bake(); x.Hash(); x.HashInto(s); } ``` We considered the following options: | Option | Type only: `x.Bake()` | Both: `x.Hash()` | Constraint only: `x.HashInto(s)` | | --------------------- | --------------------- | ---------------- | -------------------------------- | | Type | -> Type | -> Type | ❌ Rejected | | Type over constraint | -> Type | -> Type | -> Constraint | | Type minus conflicts | -> Type | -> Type | ❌ Rejected | | Union minus conflicts | -> Type | ❌ Rejected | -> Constraint | | Constraint over type | -> Type | -> Constraint | -> Constraint | | Constraint | ❌ Rejected | -> Constraint | -> Constraint | Of these rules: - "Type" and "type over constraint" mean the constraints in a constrained template do not guide the meaning of the program, which creates a surprising discontinuity when migrating from templates to generics. - "Type minus conflicts" does not present a valuable improvement over "union minus conflicts". - "Union minus conflicts" makes the type-only case behave like a non-template, and the constraint-only case behave like a generic. This means that explicit qualification is necessary for all qualified names in a template if it wants to defend against ambiguity from newly-added names, whereas all the earlier options require qualification only for names intended to be found in the constraint, and all the later options require qualification for names intended to be found in the type. However, most of the other rules require explicit qualification in the same cases to defend against names being _removed_. - "Constraint over type" means there is potential for a discontinuity in behavior depending on whether we're able to symbolically resolve the type or not: if semantic analysis can determine a type symbolically, you get the behavior from the constraint, and if not, you get the behavior from the type. This may lead to surprising and hard-to-understand program behavior. - "Constraint" means that a constrained template behaves essentially the same as a generic, which harms the ability to use constrained templates as an incremental, evolutionary stepping stone from non-constrained templates into generics. No rule provides ideal behavior. The most significant disadvantage of the chosen rule, "union minus conflicts", is that it requires explicit qualification with either the type or the constraint in a fully-robust template. However, the other leading contender, "constraint over type", also requires qualification of all names to prevent silent changes in behavior if a constraint is changed, and "union minus conflict" seems preferable to "constraint over type" in other ways. ### Meaning of `Type.Interface` In this proposal, `impl` lookup is performed when a member of an interface appears on the right of a `.`. We could also consider applying `impl` lookup when the name of an interface appears on the right of a `.`. Under that alternative, `Class.(Interface)` would be a name for the `impl`, that is, for `impl Class as Interface`. Because we have previously decided we don't want facet types, such a name would be restricted to only appear in the same places where package and namespace names can appear: on the left of a `.` or the right of an `alias`. For example: ``` interface MyInterface { fn F(); fn G[me: Self](); } class MyClass { alias InterfaceAlias = MyInterface; impl as MyInterface { fn F(); fn G[me: Self](); } } fn G(x: MyClass) { // OK with this proposal and the alternative. MyClass.(MyInterface.F)(); // Error with this proposal, OK with the alternative. MyClass.(MyInterface).F(); // Names the interface with this proposal. // Names the `impl` with the alternative. alias AnotherInterfaceAlias = MyClass.InterfaceAlias; // Error with this proposal, OK with the alternative. MyClass.InterfaceAlias.F(); // OK with this proposal, error with the alternative. MyClass.(MyClass.InterfaceAlias.F)(); // Error under this proposal, OK with the alternative. x.MyInterface.F(); // Error under both this proposal. // Also error under the alternative, unless we introduce // a notion of a "bound `impl`" so that `x.MyInterface` // remembers its receiver object. x.MyInterface.G(); // OK under this proposal and the alternative. x.(MyInterface.G)(); } ``` Advantages: - Gives a way to name an `impl`. Disadvantages: - It's not clear that we need a way to name an `impl`. - Presents a barrier to supporting member interfaces, because `MyClass.MemberInterface` would name the `impl MemberInterface as MyClass`, not the interface itself. - Reintroduces facet types, without the ability to use them as a type. Having a way of naming an `impl` may lead to confusion over whether they are first-class entities. - Would either surprisingly reject constructs like `x.MyInterface.G()` or require additional complexity in the form of a "bound `impl`" value. The value of such a type would presumably be equivalent to a facet type. As a variant of this alternative, we could disallow `Type.Interface` for now, in order to reserve syntactic space for a future decision. However, it's not clear that the cost of evolution nor the likelihood of such a change is sufficiently high to warrant including such a rule. ================================================ FILE: proposals/p0990.md ================================================ # Generics details 8: interface default and final members [Pull request](https://github.com/carbon-language/carbon-lang/pull/990) ## Table of contents - [Problem](#problem) - [Background](#background) - [Proposal](#proposal) - [Rationale based on Carbon's goals](#rationale-based-on-carbons-goals) - [Alternatives considered](#alternatives-considered) - [Defaulting to less specialized impls](#defaulting-to-less-specialized-impls) - [Allow default implementations of required interfaces](#allow-default-implementations-of-required-interfaces) - [Don't support `final`](#dont-support-final) ## Problem Rust has found that allowing interfaces to define default values for its associated entities is valuable: - Helps with evolution by reducing the changes needed to add new members to an interface. - Reduces boilerplate when some value is more common than others. - Addresses the gap between the minimum necessary for a type to provide the desired functionality of an interface and the breadth of API that user's desire. Carbon would benefit in the same ways. ## Background Rust supports specifying defaults for [methods](https://doc.rust-lang.org/book/ch10-02-traits.html#default-implementations), [interface parameters](https://doc.rust-lang.org/book/ch19-03-advanced-traits.html#default-generic-type-parameters-and-operator-overloading), and [associated constants](https://doc.rust-lang.org/reference/items/associated-items.html#associated-constants-examples). ## Proposal This proposal defines both how defaults for interface members are specified in Carbon code as well as final interface members in the [generics details design doc](/docs/design/generics/details.md#interface-defaults). ## Rationale based on Carbon's goals This proposal advances these goals of Carbon: - [Performance-critical software](/docs/project/goals.md#performance-critical-software): Final members of interfaces can avoid some dynamic dispatch overhead. - [Software and language evolution](/docs/project/goals.md#software-and-language-evolution): Defaults simplify adding new members to an interface without having to simultaneously update all impls of that interface. - [Code that is easy to read, understand, and write](/docs/project/goals.md#code-that-is-easy-to-read-understand-and-write): Defaults both reduce boilerplate, making code easier to read and write. Marking interface members as `final` makes the code more predictable to users of that member. ## Alternatives considered ### Defaulting to less specialized impls Rust has observed ([1](https://rust-lang.github.io/rfcs/1210-impl-specialization.html#default-impls), [2](http://aturon.github.io/tech/2015/09/18/reuse/)) that interface defaults could be generalized into a feature for reusing definitions between impls. This would involve allowing more specific implementations to be incomplete and reuse more general implementations for anything unspecified. However, [they also observed](http://smallcultfollowing.com/babysteps/blog/2016/09/29/distinguishing-reuse-from-override/): > [To be sound,] if an impl A wants to reuse some items from impl B, then impl A > must apply to a subset of impl B's types. ... This implies we will have to > separate the concept of "when you can reuse" (which requires subset of types) > from "when you can override" (which can be more general). This is a source of complexity that we don't want in Carbon. If we do eventually support inheritance of implementation between impls in Carbon, it will do this by explicitly identifying the impl being reused instead of having it be determined by their specialization relationship. ### Allow default implementations of required interfaces Here are the reasons we considered for not allowing interfaces to provide default implementations of interfaces they require: - This feature would lead to incoherence unless types implementing `TotalOrder` also must explicitly implement `PartialOrder`, possibly with an empty definition. The problem arises since querying whether `PartialOrder` is implemented for a type does not require that an implementation of `TotalOrder` be visible. - It would be unclear how to resolve the ambiguity of which default to use when two different interfaces provide different defaults for a common interface requirement. - It would be ambiguous whether the required interface should be external or [internal](/docs/design/generics/terminology.md#extending-an-interface) unless `PartialOrder` is implemented explicitly. - There would be a lot of overlap between default impls and blanket impls. Eliminating default impls keeps the language smaller and simpler. The rules for blanket impls already provide resolution of the questions about coherence and priority and make it clear that the provided definition of the required interface will be external. ### Don't support `final` There are a few reasons to support `final` on associated entities in the interface: - Clarity of intent when default methods are just to provide an expanded API for the convenience of callers, reducing the uncertainty about what code is called. - Matches the functionality available to base classes in C++, namely non-virtual functions. - Could reduce the amount of dynamic dispatch needed when using an interface in a `DynPtr`. The main counter-argument is that you could achieve something similar using a `final` impl: ``` interface I { fn F(); final fn CallF() { F(); } } ``` could be replaced by: ``` interface IImpl { fn F(); } interface I { extends IImpl; fn CallF(); } final impl (T:! IImpl) as I { fn CallF() { F(); } } ``` This is both verbose and a bit awkward to use since you would need to `impl as IImpl` but use `I` in constraints. ================================================ FILE: proposals/p0998.md ================================================ # Principle: One static open extension mechanism [Pull request](https://github.com/carbon-language/carbon-lang/pull/998) ## Table of contents - [Problem](#problem) - [Proposal](#proposal) - [Details](#details) - [Rationale based on Carbon's goals](#rationale-based-on-carbons-goals) - [Alternatives considered](#alternatives-considered) ## Problem There are a few ways of approaching open extension, such as defining how operators are overload for every type. For Carbon, with its [focus on performance](/docs/project/goals.md#performance-critical-software), we are particularly interested in those that support static dispatch, to avoid the runtime overhead of dynamic dispatch. The three main options are: - Open function overloading, where new overloads for a given name can be defined broadly, as is done in C++. - Interfaces, as is done in [Rust](https://doc.rust-lang.org/rust-by-example/trait/ops.html). - Special method names, as are used in [C++](https://en.cppreference.com/w/cpp/language/operators) and [Python](https://docs.python.org/3/reference/datamodel.html#special-method-names). We would prefer to use a single mechanism, if possible, for simplicity. ## Proposal Proposal is to use interfaces as the single open extension mechanism. ## Details Details are in the added principle doc: [docs/project/principles/static_open_extension.md](/docs/project/principles/static_open_extension.md). ## Rationale based on Carbon's goals This proposal is pursuing Carbon's goal of having [code that is easy to read, understand, and write](/docs/project/goals.md#code-that-is-easy-to-read-understand-and-write). ## Alternatives considered Early arguments for this approach were put forth in [this document](https://docs.google.com/document/d/1uvX_hmw5DVs1SFjnehUUizGnI6C099vqIGfUhfCwBIo/edit#). ================================================ FILE: proposals/p1013.md ================================================ # Generics: Set associated constants using `where` constraints [Pull request](https://github.com/carbon-language/carbon-lang/pull/1013) ## Table of contents - [Problem](#problem) - [Background](#background) - [Proposal](#proposal) - [Rationale based on Carbon's goals](#rationale-based-on-carbons-goals) - [Alternatives considered](#alternatives-considered) - [Status quo](#status-quo) - [`with` and `,` instead of `where` and `and`](#with-and--instead-of-where-and-and) - [Future work](#future-work) ## Problem There are a variety of contexts that currently use the keyword `let`: - declaring associated constants or types in an interface, - defining associated constants or types in an implementation, - defining local constant in a function body, and - defining class constants. In all but the implementation case, the semantics are generally similar to the semantics of passing a value into a function, with some erasing of the specific value passed and using the type to determine how the name can legally be used. However, [proposal #950](https://github.com/carbon-language/carbon-lang/pull/950) has changed the `let` in an implementation to use the value specified, not its type, creating an inconsistency with the other uses of `let`. Furthermore, we have come to the realization that we still want to specify the values of associated constants and types for an implementation even in an API file where we only want to make a forward declaration. This makes that information available to clients that only look at the API file, who need to know those values for type checking, but otherwise don't need to see the full definition of the implementation. This suggests that those assignments should be declared outside of definition block's curly braces `{`...`}`. Lastly, there is a bit of redundancy in Carbon since `where` clauses are also a way of specifying the values of associated constants and types in other Carbon contexts. ## Background The `let` syntax for setting an associated type in an interface implementation was originally decided in issue [#739: Associated type syntax](https://github.com/carbon-language/carbon-lang/issues/739) and implemented in proposal [#731: Generics details 2: adapters, associated types, parameterized interfaces](https://github.com/carbon-language/carbon-lang/pull/731). Proposal [#950: Generics details 6: remove facets](https://github.com/carbon-language/carbon-lang/pull/950) made two relevant changes: - The type part of a `let` in an `impl` block is no longer "load bearing": the only legal types are `auto` and whatever was in the corresponding interface. In particular, the `let` in an `impl` block does not erase. - There is now a defined meaning for a generic `let` statement in a function body that can erase depending on the type specified. Combined with the `let` in an interface giving you an erased type, or archetype, this has made the meaning of `let` in an `impl` block inconsistent with other places using `let`. ## Proposal The suggested change is to use a `where` clause as part of an `impl` declaration to specify associated constants and types instead of `let` declarations inside of the `impl` definition. In effect, it removes `let` declarations from `impl` blocks in exchange for allowing an `impl` declaration to implement a constraint expression instead of a simple interface or named constraint. This proposal updates the following design docs on the generics feature to reflect this change: - [docs/design/generics/overview.md](/docs/design/generics/overview.md) - [docs/design/generics/terminology.md](/docs/design/generics/terminology.md) - [docs/design/generics/details.md](/docs/design/generics/details.md) ## Rationale based on Carbon's goals As a simplification, this proposal advances the goal of having Carbon [code that is easy to read, understand, and write](/docs/project/goals.md#code-that-is-easy-to-read-understand-and-write). In particular, having a simple specification and be simple to implement. This is an example of [the "prefer providing only one way to do a given thing" principle](/docs/project/principles/one_way.md), by switching to a single way of specifying associated constants and values. ## Alternatives considered ### Status quo The main alternative considered was the status quo. We did have two concerns with this proposal, however we felt that this behavior would not be surprising to developers in practice. **Concern:** Due to interface defaults, it is possible for copy-pasting the type-of-type expression from an `impl` block in a `class` into a constraint in a function signature to give a constraint that is weaker than what that impl block actually delivers. **Concern:** Because a specialization of an `impl` can change the values of associated constants, a type might not actually satisfy a constraint that it appears to implement when that constraint specifies the values of associated constants. In this example: ``` interface Bar { let X:! Type; } class Foo(T:! Type) { impl as Bar where .X = T { ... } } ``` it appears that `Foo(T)` satisfies the constraint that `Bar where .X = T`, but there could be specializations that set `.X` to different values for some specific values of `T`. ### `with` and `,` instead of `where` and `and` Instead of matching the syntax used when specifying constraints, we could have used a different syntax to highlight that this is assigning instead of constraining. The suggestion that came up in discussion was using `with` instead of `where` and a comma `,` instead of `and` to join multiple clauses. We decided that it would not be good to have two syntaxes that were very similar but different, and that there was some benefit to be able to copy-paste between the constraint context and the implementation context. ## Future work This proposal will allow us to support declaring that a type implements an interface inside an API file separate from the definition of the `impl`, even for internal `impl`s. However, that feature is waiting on resolution of [#472: Open question: Calling functions defined later in the same file](https://github.com/carbon-language/carbon-lang/issues/472) and proposal [#875: Principle: information accumulation](https://github.com/carbon-language/carbon-lang/pull/875). If and when we do add support declaration of impls without definition, we will need to answer the question: do you have to repeat `where` constraints from a forward declaration of an impl when it is later defined? ``` class Vector(T:! Type) { impl as Container where .Element = T and .Iter = VectIter(T); } // Probably okay: fn Vector(T:! Type).(Container.Begin)[me: Self]() ... // Maybe okay: class Vector(T:! Type) { // Not repeating constraints on .Element and .Iter above: impl as Container { fn Begin[me: Self]() ... } } ``` ================================================ FILE: proposals/p1025.md ================================================ # Roadmap for 2022 [Pull request](https://github.com/carbon-language/carbon-lang/pull/1025) ## Table of contents - [Problem](#problem) - [Background](#background) - [Proposal](#proposal) - [Retrospective on 2021](#retrospective-on-2021) - [Broaden core team representation so no organization is >50%](#broaden-core-team-representation-so-no-organization-is-50) - [Example ports of C++ libraries to Carbon (100% of woff2, 99% of RE2)](#example-ports-of-c-libraries-to-carbon-100-of-woff2-99-of-re2) - [Demo implementation of core features with working examples](#demo-implementation-of-core-features-with-working-examples) - [Executable semantic specification for core features with test cases](#executable-semantic-specification-for-core-features-with-test-cases) - [Rationale based on Carbon's goals](#rationale-based-on-carbons-goals) - [Alternatives considered](#alternatives-considered) ## Problem It's (past) time to update our roadmap for 2022, following Carbon's annual [roadmap process](/docs/project/roadmap_process.md). ## Background Carbon has an annual roadmap to align and focus the work of the teams and community. For 2021, our main objective was to speed up the development of the Carbon project while it remains a private experiment, by: - Increasing the investment by existing individuals and organizations. - Increasing the breadth of different individuals and organizations investing in Carbon. ## Proposal We have two primary goals for 2022: - Shift the experiment to being public. - Reach the point where the core language design is substantially complete. See the [updated roadmap](https://github.com/carbon-language/carbon-lang/pull/1025/files) for more details. ## Retrospective on 2021 As we plan for 2022, we should look at how well we did at achieving our [objectives for 2021 and their key results](https://github.com/carbon-language/carbon-lang/blob/9523ac97bf5c3b7e52fa14299c1391c62dd907f5/docs/project/roadmap.md). ### Broaden core team representation so no organization is >50% > Our goal is that no single organization makes up >50% of the core team to > ensure that we are including as broad and representative a set of perspectives > in the evolution of Carbon as possible. In 2021, we dissolved the core team, and introduced a set of three leads. Two of those leads represent the same organization. The leads now have more organizational diversity than the core team did at the start of 2021, so this is a partial success. (And, vacuously, no organization makes up any part of the core team any more!) In the wider Carbon community, most active participants and nearly all proposals are still from a single organization, but we are seeing significant and increasing contribution outside that organization. The spirit of this goal has been retained for 2022, but it has been reformulated to better fit our current organization and governance model, and somewhat reduced in scope. Instead of looking for <50% of the Carbon leadership from any one organization, we're now looking for <50% of active participants from any one organization. In future years, we hope to also reach the point where <50% of the Carbon leads are from any one organization, but that doesn't seem like a realistic goal for 2022. ### Example ports of C++ libraries to Carbon (100% of woff2, 99% of RE2) We did not make much progress on this goal in 2021, in part because we made less progress on the Carbon language design in 2021 than anticipated. It remains important that we do this work, for the same reasons as in 2021: it both measures our progress solidifying Carbon's design and demonstrating the value proposition of Carbon. For 2022, as we move towards going public and completing the language design, our expectations for these ports become higher, and we now aim to not only provide the ported example code but also to have a sufficiently complete executable semantics implementation that parts of it can be demonstrated to work correctly. ### Demo implementation of core features with working examples > A core set of Carbon features should be implemented sufficiently to build > working examples of those features and run them successfully. The toolchain supports parsing for many basic features, such as functions, variables, operators, and so on, but no type-checking or code generation. > Basic benchmarking of the different phases of compilation (lexing, parsing, > etc). We have some benchmarks accompanying the toolchain, but the coverage here is incomplete. This takes us some of the way to our intended outcome, but there's a lot more to do. ### Executable semantic specification for core features with test cases > This should include both a human readable rendering of the formal semantics as > well as an execution environment to run test cases through those semantics. > [...] In the 2021 roadmap, we prioritized completing the demo toolchain implementation over work on executable semantics. We did not end up following that ethos throughout 2021, and the outcome is that most of the implementation work was performed in executable semantics rather than in the toolchain. It would not be unfair to say that executable semantics has ended up as a better model of a demo Carbon implementation than the toolchain. Nonetheless, we made great progress here. Executable semantics supports a broad subset of the currently approved Carbon feature set, and some things beyond that feature set. What is less clear is whether our experiment of having a clear and precise formal specification expressed as an implementation that favors clarity of exposition over all else is successful. ## Rationale based on Carbon's goals - [Community and culture](/docs/project/goals.md#community-and-culture) - A roadmap is an important tool for setting community expectations and direction. - Going public is fundamental to the open community we aim to have. - Broadening participation is an essential factor in building a diverse and welcoming community. ## Alternatives considered ================================================ FILE: proposals/p1083.md ================================================ # Arithmetic expressions [Pull request](https://github.com/carbon-language/carbon-lang/pull/1083) ## Table of contents - [Problem](#problem) - [Background](#background) - [Symbols](#symbols) - [Semantics](#semantics) - [Proposal](#proposal) - [Rationale based on Carbon's goals](#rationale-based-on-carbons-goals) - [Choice of operator symbols](#choice-of-operator-symbols) - [Signed integer semantics](#signed-integer-semantics) - [Alternatives considered](#alternatives-considered) - [Use a sufficiently wide result type to avoid overflow](#use-a-sufficiently-wide-result-type-to-avoid-overflow) - [Guarantee that the program never proceeds with an incorrect value after overflow](#guarantee-that-the-program-never-proceeds-with-an-incorrect-value-after-overflow) - [Guarantee that all integer arithmetic is two's complement](#guarantee-that-all-integer-arithmetic-is-twos-complement) - [Treat overflow as an error but don't optimize on it](#treat-overflow-as-an-error-but-dont-optimize-on-it) - [Don't let `Unsigned` arithmetic wrap](#dont-let-unsigned-arithmetic-wrap) - [Provide separate wrapping types](#provide-separate-wrapping-types) - [Do not provide an ordering or division for `uN`](#do-not-provide-an-ordering-or-division-for-un) - [Give unary `-` lower precedence](#give-unary---lower-precedence) - [Include a unary plus operator](#include-a-unary-plus-operator) - [Floating-point modulo operator](#floating-point-modulo-operator) - [Provide different division operators](#provide-different-division-operators) - [Use different division and modulo semantics](#use-different-division-and-modulo-semantics) - [Use different precedence groups for division and multiplication](#use-different-precedence-groups-for-division-and-multiplication) - [Use the same precedence group for modulo and multiplication](#use-the-same-precedence-group-for-modulo-and-multiplication) - [Use a different spelling for modulo](#use-a-different-spelling-for-modulo) - [Future work](#future-work) - [Provide separate wrapping operators](#provide-separate-wrapping-operators) - [Provide separate operations to detect overflow](#provide-separate-operations-to-detect-overflow) ## Problem Carbon needs a set of arithmetic operators in order to perform basic calculations on various kinds of built-in and user-defined numbers and number-like values: integers, real numbers, complex numbers, vectors, matrices, and so on. ## Background ### Symbols Conventions for arithmetic operators have a very long tradition. The following symbols have common meaning across C, C++, Java, JavaScript, Rust, Swift, and many other languages: - `+` and `-` mean addition and subtraction, as in mathematics. Unary `-` forms a negated value -- an additive inverse. Sometimes, unary `+` is permitted, as a no-op. - `*` means multiplication, diverging from the use of '×', '⋅', or simply juxtaposition in normal mathematical notation. However, this symbol does have some visual similarity to '×'. - `/` means division, diverging from the use of '÷', fraction notation, or an exponent of -1 in mathematics. However, this symbol does somewhat visually resemble fractional notation that has "fallen over" to the left. - `%` means remainder, diverging from the use of the word "mod" in mathematics, and, perhaps confusingly, resembling the '÷' division operator from mathematics. ### Semantics Efficient integer types often have finite bounds on the numbers they can represent, and we expect Carbon's to be no different. This presents a problem for operations that might produce values outside those bounds: what should the behavior of a primitive arithmetic operation be if the result cannot be represented? Moreover, some operations, such as division by zero, have no mathematically-defined result. Different languages take different approaches to this problem. - In C and C++, signed integer overflow and division by zero -- including floating-point division by zero -- have undefined behavior, meaning that there are no constraints on the behavior of a program that performs such a calculation. - In [Rust](https://rust-lang.github.io/rfcs/0560-integer-overflow.html), these conditions are classified as being errors, which, depending on various factors, will either panic or give a two's complement result. Rust also provides a `Wrapping` type, where `T` is a signed or unsigned integer type, that provides arithmetic with guaranteed two's complement wrapping semantics. - In Swift, overflow triggers a runtime fatal error. Arithmetic operators can be prefixed with `&`, such as `big_num &* other_big_num`, to request two's complement wrapping behavior instead. - In Java, integer arithmetic has two's complement wrapping behavior. Division by zero throws an exception. - In JavaScript, all arithmetic is (at least notionally) performed in IEEE 754 double-precision floating-point, so all operations have defined results, although some integer operations may produce non-integer results, such as infinities or NaNs, and some produce incorrect integer results. For example, in JavaScript, `100000001 * 100000001` evaluates to `10000000200000000` not to `10000000200000001`. - In LLVM, the result of overflow is a poison value, which results in undefined behavior only when it is observed, for example by a branch, and otherwise propagates to dependent values. This permits speculative execution of arithmetic that might overflow. ## Proposal Carbon will provide the five usual binary arithmetic operators -- `+`, `-`, `*`, `/`, and `%` -- with their usual semantics. A unary minus `-` operator will also be provided, but no unary plus `+` operator. | Notation | Meaning | | -------- | ------------------ | | `-a` | Negation | | `a + b` | Addition | | `a - b` | Subtraction | | `a * b` | Multiplication | | `a / b` | Division | | `a % b` | Modulo / remainder | These operators follow the same precedence rules as in C-family languages -- multiplicative operators bind tighter than additive operators, and negation binds tighter than multiplicative operators. Unlike in other C-family languages, no precedence is defined between `%` and other binary operators, so `a + b % c * d` is an error, and does not mean `a + (b % c) * d` as it would in C++. Unlike in C++, lossy conversions are never performed. Instead, built-in arithmetic is not permitted unless one of the operand types can represent all the values of the other operand. The calculation is performed in that operand type; there is no implicit widening to Carbon's equivalent of `int`. Unsigned integer types `uN` model arithmetic modulo 2`N`, and we strongly advise that they are not used except when those semantics are desired, such as in random number generation, cryptography, hashing, and similar domains. For signed integer types `iN`, it is a programming error if overflow occurs. We guarantee that in development build modes such errors will result in a runtime trap, and that in hardened build modes the result will either be a trap or the two's complement value. Initially, no support will be provided for wrapping signed integer arithmetic, nor for non-wrapping unsigned integer arithmetic. This can be added by a future proposal if we find there is sufficient demand for either. Floating-point types use IEEE 754 semantics, with round-to-nearest rounding mode, no signaling NaNs, and no floating-point exceptions. This proposal takes no position on whether the `+` operator is supported on strings to perform concatenation. See the changes to the design for more details; the rest of this proposal will focus on the rationale and alternatives. ## Rationale based on Carbon's goals - [Language tools and ecosystem](/docs/project/goals.md#language-tools-and-ecosystem) - Treating overflow as a programming error empowers static and dynamic analysis tools to distinguish arithmetic bugs from intentional wraparound. - [Performance-critical software](/docs/project/goals.md#performance-critical-software) - Allowing the optimizer to assume that signed integer overflow does not occur in performance build modes allows certain important loop optimizations to fire that would otherwise be incorrect. - Avoiding extending into larger types when performing arithmetic on small operands aids in the ability to vectorize. - [Software and language evolution](/docs/project/goals.md#software-and-language-evolution) - Each subexpression of an arithmetic expression is given a specific type, rather than being computed in a type of sufficient width for any possible value, in order to make it simple to factor out subexpressions. - [Code that is easy to read, understand, and write](/docs/project/goals.md#code-that-is-easy-to-read-understand-and-write) - Using the same operators as other languages, with largely the same semantics as C++, should aid readability and writability especially for those with less Carbon-specific knowledge. - Performing built-in arithmetic in the larger operand type and refusing cases where a lossy conversion would be performed in C++ reduces the scope for confusion and surprises. - [Practical safety and testing mechanisms](/docs/project/goals.md#practical-safety-and-testing-mechanisms) - Treating signed integer overflow as an error condition, and having it produce a runtime error in some build modes, will assist with certain forms of testing and with analysis of program behavior. For example, fuzz testing can be used to locate overflow bugs, with confidence that any overflow detected is unintentional. - [Modern OS platforms, hardware architectures, and environments](/docs/project/goals.md#modern-os-platforms-hardware-architectures-and-environments) - The choice of division and modulo semantics aims to provide fast execution and small code size on modern hardware architectures. - [Interoperability with and migration from existing C++ code](/docs/project/goals.md#interoperability-with-and-migration-from-existing-c-code) - Using the same operator set as C++ may aid migration of C++ developers and make code using interop between Carbon and C++ easier to follow. - Making similar choices for the behavior of signed and unsigned types will make a correct but unidiomatic conversion of C++ into Carbon easier; however, in idiomatic Carbon, signed types are expected to be used more frequently. ### Choice of operator symbols We want Carbon to provide a close analogue of standard mathematical expression notation, with some unsurprising set of operators. While accessibility to beginners is important, the most important audience for which the operators set should be unsurprising is programmers with C++ or Rust experience. The choice of `+`, `-`, `*`, `/`, and `%` is ubiquitous among programming languages, and any significant change to this symbol set for the benefit of programming novices with a mathematical background would be a detriment to those with a background in other programming languages. Therefore we choose the conventional symbol set. ### Signed integer semantics Carbon prioritizes fast and predictable performance above all other factors. Allowing an optimizer to assume that certain forms of arithmetic do not result in overflow can improve program performance and allow the generation of smaller code. Moreover, while in general we only want to provide the means for accessing the fastest possible implementation of an operation, we expect integer arithmetic to be so ubiquitous that it's important that the most efficient option be the default option. At the same time, it's important that Carbon code can be used in safety-critical scenarios where unbounded undefined behavior on integer arithmetic is problematic, so in a hardened build mode, we provide stronger guarantees. ## Alternatives considered ### Use a sufficiently wide result type to avoid overflow We could define that integer operators always produce a sufficiently wide result type such that overflow never occurs, and defer all overflow handling -- checking for out-of-bounds values, wrapping around, or a programmer assertion that overflow simply doesn't happen -- until the end of the computation or some explicit step. For example: ``` fn F(a: i32, b: i32, c: i32) { // a * b is computed in i63, // ... + c is computed in i64. // =! is an assertion that the value is in-range. let d: i32 =! a * b + c; // Same, but =% says to wrap around. let e: i32 =% a * b + c; // If the value doesn't fit in the specified type, pattern-matching fails. let f: i32 = a * b + c else { return; } } ``` We could put a limit on how large an intermediate type can be, and reject if it would require a larger intermediate operand than the largest we can efficiently support. This approach seems quite promising, but close inspection finds a number of non-trivial issues. Advantages: - No implicit undefined behavior or incorrect results on overflow. - Forces developer to think about the possibility of overflow and write down what they intend to happen. - If the final assignment is `=%` or `=!`, all intermediate arithmetic other than division and remainder can be done in the result type, avoiding the need for wide computations. Disadvantages: - This approach is novel and it's unknown whether developers would accept its ergonomic burden. - Decreases the uniformity of the model. While we can generalize `=!` to mean "assign assuming the RHS fits into the LHS", there doesn't seem to be any good generalization of `=%` to other situations and types. - The largest type for which we can truly efficiently perform arithmetic is `i64` / `u64`. While 128-bit arithmetic is typically available on our target architectures, use of it will often be less efficient and may increase register pressure. Calculations as simple as `(a + b) / c` may be rejected if the operands are already in the largest efficient type. - Operations such as negation and division can increase the width of the operand: `-i32.Min` and `i32.Min / -1` don't fit in `i32`. The following approaches to this problem were considered: - Remove the `Min` value from `iN` types, so the negative and positive range are identical. However, this would severely violate programmer expectations, for example in some important bit-manipulation cases. - Give integer types a range of values rather than simply a bit-width. For example, we can say that negation on `i32` produces a type that can represent [-231+1, 231], which still fits in 32 bits. However, this would add significant complexity to the type system, and with this approach, division would still increase the bit width: for example, `a / b`, where `a` and `b` are `iN`s, has 2`N`+1 distinct possible values. This is especially surprising because integer division is usually expected to make a number smaller! - Refactoring code becomes more challenging, as the appropriate intermediate type must be determined. Mitigating this, the type system would inform the programmer when they make a mistake. - The `!` and `%` annotations become quite viral: they would be needed not only in initialization and assignment, but likely also in `case`, `return`, and other initialization contexts not using `=`. As an alternative, the annotation could be put on the outermost arithmetic operator, but that is likely to be syntactically awkward, especially in unparenthesized expressions such as `a + b +% c`. - It will likely become idiomatic in many communities to either always use `%` or always use `!`. - Always using `!` means that the developers see no benefit compared to the behavior as proposed here, and nonetheless pay an ergonomic cost. - Always using `%` means that we lose any ability to distinguish between intentional wraparound and overflow, making a class of bugs that would otherwise be easily detectible be undetectable without making the program behavior correct. - Division by zero is still not handled. ### Guarantee that the program never proceeds with an incorrect value after overflow We could say that overflow errors always result in program termination, possibly with the permission for the compiler to give the mathematically correct result instead if it so chooses. This would result in slower and larger code being generated in a lot of cases. Some optimizations would still be possible if the optimizer could ensure that it only increased the set of cases for which the correct result is given, but current optimizers are not well-suited to perform that task. Given Carbon's emphasis on performance, this approach is rejected without prejudice until we have a demonstration that no important performance metric is harmed by it. ### Guarantee that all integer arithmetic is two's complement Instead of making overflow a programming error, we could define it as two's complement. This is, for example, the approach taken by Java. The major problem with this approach is that it makes erroneous wrapping and intentional wrapping indistinguishable. This would make finding such bugs much harder for readers of the code, and all but impossible for static and dynamic analysis tools. Making overflow issues programming errors allows problems to be caught earlier and more reliably. ### Treat overflow as an error but don't optimize on it We could follow Rust and say that overflow is an error, but that we promise we'll either catch it or give two's complement semantics. This approach is currently rejected for the same reason we reject [guaranteeing we catch all cases where we can't give a correct result](#guarantee-that-the-program-never-proceeds-with-an-incorrect-value-after-overflow). ### Don't let `Unsigned` arithmetic wrap We could treat `Unsigned(N)` like `Integer(N)`, and make it an error by default if it overflows. However, this doesn't seem like a great fit for the problem domain. There are, broadly speaking, two different classes of use cases we want to support: - Cases where the developer wants a number. They might have some expectation of the range of the number -- eg, non-negative, or strictly positive, or between -1 and 100 -- or they might just want a number and not have explicit bounds. We expect `iN` to be used for all such cases, and do not want to treat the non-negative cases as a distinct and special type. - Cases where the developer wants the ring ℤ/2nℤ of integers modulo 2n, for example in cryptography, hashing, or random number generation. The second class is certainly rarer than the first, but contains many important use cases. The typical arguments for using an unsigned type for the first class of use cases are: - Better expression of developer intent. It is generally preferable to make invalid states unrepresentable, and if negative numbers are invalid, then unsigned types are better suited than signed types. However, supporting this use case with the same types used to support the wrapping use cases results in a situation where one side or the other has to make compromises. The alternative would be to have three different kinds of type: signed, unsigned, and modulo. But in that setup, it's not clear that the value added by unsigned types is worthwhile. Also, it's common to want to take the difference of such unsigned quantities, and it's generally preferable for such subtractions to produce a negative result rather than a subtle bug. Moreover, while a restriction to non-negative values is common, supporting only the case of a range restriction to [0, 2N-1], but not any other range, does not do a good job of addressing the general desire to capture intent and to make invalid states unrepresentable. - Ability to reduce storage size. Spending a sign bit every time a number is stored, even when it's known to be non-negative is wasteful. This is an important concern, and one we should address, but it's thought to be better to address this by annotating a field with a description of how it should be packed -- such as in a bit-field -- rather than by changing the type of the data and possibly the behavior of operations on it. Additionally, providing unsigned types for which overflow is an error introduces a much larger risk of that error state being inadvertently reached than for signed types, because calculations typically involve numbers that are close to zero, which is far from overflowing in signed types but near to overflowing in unsigned types. For example, a calculation such as `a - b + c` may be known to always produce a non-negative result, and the developer may be tempted to use non-negative non-wrapping types for `a`, `b`, and `c`, but doing so introduces the risk that the intermediate `a - b` calculation is negative. By contrast, overflow would only occur in a signed calculation if the numbers involved were very large. ### Provide separate wrapping types We could provide distinct types for wrapping versus non-wrapping use cases, independent of the choice of signedness. This approach is being followed by Rust. Advantages: - Avoids coupling two decisions that are logically independent. - Allows developer intent to be expressed more explicitly, in the case where the intent is a non-negative but non-wrapping integer. Disadvantages: - Twice as many kinds of integer types, likely meaning twice as many `impl`s need to be defined for each operation, twice as many overloads in each overload set, and so on. - The presence of unsigned types for which overflow is an error has a greater risk of inadvertent overflow, as described in the previous section. - Less familiar to those coming from C++. ### Do not provide an ordering or division for `uN` For arithmetic purposes, we treat `uN` as the integers modulo 2N. There is no single canonical total order for that ring, and because multiplication by even numbers loses information by discarding the high-order bit, not all non-zero numbers have [multiplicative inverses](https://en.wikipedia.org/wiki/Modular_multiplicative_inverse), and so division is not well-defined. We could be more mathematically pure by refusing to implement `<` and friends for `uN`, and similarly refusing to support `/`. However, the `uN` types exist as much for pragmatic purposes as for mathematical ones. Making the choice to treat all `uN` values as non-negative is somewhat arbitrary, but is both unsurprising to those coming from C++ and useful for the cases where some ordering is desired. ### Give unary `-` lower precedence In this proposal, `- a * b` is interpreted as `(-a) * b`. We could instead interpret it as `- (a * b)`. Advantages: - Can be argued as better following mathematical convention. Disadvantages: - Unfamiliar to people coming from most other programming languages, including C++. - If we followed our normal precedence partial ordering rule, this would mean that `a * -b` is invalid, because `*` has higher precedence than `-`. Comparison to other languages: - C and C++ give all unary operators (including unary `-` and unary `+`) higher precedence than any binary operator. - Go, Rust, and Swift give unary `-` higher precedence than multiplication. - Python binds unary `-` more tightly than multiplication but less tightly than exponentiation, so `- a * b` is `(- a) * b` but `- a ** b` is `- (a ** b)`. - Haskell gives unary `-` the same precedence as binary `-` and rejects `a * - b`. ### Include a unary plus operator C and C++ include a unary `+` operator. In principle, this operator permits lists of numbers to be written with an operator attached to each: ```cpp int arr[] = { -100, -50, +20, +400 }; ``` ... but in practice this use case is rare at best, and unary `+` in C++ is instead mainly used to coerce operands to prvalues of built-in types. For example, `auto *p = +[]{ /*...*/ };` might be used to create a function pointer (`auto` deduction would fail without the unary `+` operator), and `min(+Class::static_member, x)` might be used to force `Class::static_member` to be loaded, to avoid requiring the static member to be defined. Such usage of `+` is a design wart that we need not replicate. If we want a mechanism to decay an operand, we can pick a better name for it than `+`. ### Floating-point modulo operator It would be possible and sometimes useful to support the `%` operator for floating-point types. Advantages: - When desired, `f1 % f2` would be a more concise notation than a call to an `fmod` function (however it is named). Disadvantages: - Uses of this operation would likely be rare and unfamiliar enough that people would assume an integer operation is being performed when they see the operator. - This operation is not available in hardware in many modern architectures, and providing it as a built-in operator may create a false impression of its implementation as a non-trivial library function. - We lack sufficient evidence of utility to propose it at this time. ### Provide different division operators We could follow Python3 and provide an `/` operator for integers that produces a floating-point (or perhaps rational) type. This would be mathematically clean: ignoring overflow and precision loss, all standard properties for division would be maintained. Or we could follow Haskell and refuse to provide an `/` operator between integers on the basis that such division is not mathematically defined for that type. Or we could provide a division operator that produces a pair of dividend and modulo, or an `Optional(Int)` that is absent whenever the division is inexact. In each case, functionality not available through operators could be provided with named functions instead. All of these options are likely to be surprising to programmers coming from C++, to a level that outweighs the perceived benefit. ### Use different division and modulo semantics There are multiple somewhat-reasonable ways to define division operations for signed integers (whether we provide those operations as operators or library functions). Assuming operator notation for now, and that we define modulo as `a % b == a - a / b * b`, the following options all have merit: | Property | Round towards zero (truncating division) | Round towards negative infinity (floor division) | Round based on sign of divisor\[1] (Euclidean division) | | ------------------------------------ | -------- | -------- | --------- | | `(-a) / b ==`
` a / (-b)` | :+1: Yes | :+1: Yes | No | | `(-a) / b ==`
` -(a / b)` | :+1: Yes | No | :+1: Yes | | `a / (-b) ==`
` -(a / b)` | :+1: Yes | No | No | | `(a + k * b) / b`
` == a / b + k` | No | :+1: Yes | :+1: Yes | | Sign of `a % b` | Same as `a` | :+1: Same as `b` | :+1: Never negative | | x86 instruction? | :+1: Yes: `cqo` (or similar) + `idiv` | First option + fixup:
`s * (a / (s * b))` where `s` is `sign(a) * sign(b)`[2] | First option + fixup:
`a / b - (a % b < 0)` | | LLVM IR + optimization support | :+1: Yes | No | No | | Use in existing languages | C, C++, Rust, Swift
`quotRem` in Haskell | `//` and `%` in Python
`/` in Python 2 only
`divMod` in Haskell | None? | The cells marked :+1: suggest generally desirable properties. For further reading, see [this Microsoft Research paper](http://research.microsoft.com/pubs/151917/divmodnote-letter.pdf). Our options here are as follows: - Pick one of the two interpretations as the meaning of `/`, and (optionally) pick one of the above interpretations as the meaning of `%`); perhaps provide the others as library functions or as additional operators. - Do not provide any of these operators for integers and provide only named functions. Note that we are not required to provide a `%` that is consistent with our chosen `/` operator. (We could pick truncate-towards-zero for `/` and truncate-towards-negative-infinity for `%`, for example.) There is long-established tradition here, but it's unclear to what extent practicing programmers really care about the relationship between `/` and `%`. It is likely that most Carbon code that performs division and modulo between signed integer types does not actually care about what happens when either operand is negative. Therefore, following our goals of supporting high-performance code and current CPU architectures, we will choose to implement `/` and `%` as division with truncation towards zero. The other variants can be provided by a library function, if at all. [1]: That is: for positive divisors, round towards negative infinity; for negative divisors, round towards positive infinity. [2]: Here, `sign(a)` is 1 if `a >= 0` and is -1 if `a < 0`. ### Use different precedence groups for division and multiplication Under this proposal, division and multiplication are in the same precedence group, and are left-associative: `a * b / c * d` is `((a * b) / c) / d`, and not `(a * b) / (c * d)` or some other grouping. It's not feasible to provide a different interpretation here due to the risk of confusing developers migrating from other languages. However, we could reject such expressions and require explicit parentheses. While the value of accepting code such as this is relatively low, the fact that both existing programming languages and common mathematical education treat multiplication and division as the same, left-associative, precedence group means that it's unlikely to be a significant burden to expect Carbon developers to remember this precedence rule. ### Use the same precedence group for modulo and multiplication In most languages with the set of arithmetic operators discussed in this proposal, `%` is considered a multiplicative operator, and so a sequence of `*`, `/`, and `%` operators is processed left-to-right. In some sense this is reasonable: `%` is notionally performing a division, after all. Moreover, in a code search, I was unable to find evidence that it's common for precedence errors with `%` to be checked in to source control, and C++ compilers don't have warnings for mixing `%` with `+` without parentheses. Giving `%` and `/` the same precedence also allows some kinds of code to be written symmetrically: ```c++ char two_digit_number[] = { '0' + m / 10, '0' + m % 10, 0 }; char four_digit_number[] = { '0' + n / 1000, '0' + n / 100 % 10, '0' + n / 10 % 10, '0' + n % 10, 0 }; ``` With minimal parentheses, that example would be written as follows under this proposal: ```carbon var two_digit_number: Array(Char, 3) = ( '0' + m / 10, '0' + (m % 10), 0 ); var four_digit_number: Array(Char, 5) = ( '0' + n / 1000, '0' + ((n / 100) % 10), '0' + ((n / 10) % 10), '0' + (n % 10), 0 ); ``` We could use the same precedence rule as other languages, and permit examples similar to the above to be written without any parentheses. However, our [rule for precedence](p0555.md#when-to-add-precedence-edges) is: > For every combination of operators, either it should be reasonable to expect > most or all developers who regularly use Carbon to reliably remember the > precedence, or there should not be a precedence rule. It is not clear that a precedence rule that gives a defined meaning to, for example, `a + b % c + d` would satisfy this rule. Moreover, in mathematics, the "mod" operator generally binds very loosely: in 'n = m + 1 (mod 5)', the '(mod 5)' applies to the entire equality, certainly not to the '1'. Therefore, we do not give modulo higher precedence than addition, and require parentheses when mixing the two. This decision should be revisited if it is a significant source of friction in practice. ### Use a different spelling for modulo We could use a different spelling for the modulo operation. For example, we could spell it as `mod`. Advantages: - The percent sign has no relation to modulo or remainder. The only known justification for this particular choice of symbol is that it resembles the '÷' symbol; however, that symbol means division, not remainder. - Would free up the `%` symbol for other uses that may be more prevalent than modulo. However, we would need to be cautious when adding any alternative uses to avoid confusion for people who expect `%` to mean modulo. Disadvantages: - This would be unfamiliar to developers coming from C++ and other languages with similar operator sets. - There is no other common established symbol for this operation. Using a keyword such as `mod` would break our loose convention of using keywords for non-overloadable operators and operator symbols for overloadable operators. Using a function would substantially increase the verbosity of certain kinds of code. ## Future work ### Provide separate wrapping operators We could provide distinct operators with wrapping semantics for types where overflow is normally a programming error. For example, Swift provides [overflow operators](https://docs.swift.org/swift-book/LanguageGuide/AdvancedOperators.html#ID37) `&+`, `&-`, and `&*` for this purpose. This proposal takes no position on whether this would be useful. However, given that in this proposal, unsigned types already have wrapping semantics, the pressure to provide operators for signed types with those semantics is somewhat reduced. ### Provide separate operations to detect overflow It would be useful to provide a way to perform an arithmetic operation if possible, and to provide a separate codepath to handle the case where the arithmetic would have overflowed. For example, we could imagine the Carbon standard library providing a facility such as: ``` fn AddWithOverflow[N:! BigInt](a: Integer(N), b: Integer(N)) -> Optional(Integer(N)); ``` While this would undoubtedly be useful, this proposal provides no facility for this operation. ================================================ FILE: proposals/p1084.md ================================================ # Generics details 9: forward declarations [Pull request](https://github.com/carbon-language/carbon-lang/pull/1084) ## Table of contents - [Problem](#problem) - [Background](#background) - [Proposal](#proposal) - [Rationale based on Carbon's goals](#rationale-based-on-carbons-goals) - [Alternatives considered](#alternatives-considered) - [No `default` keyword on interface members](#no-default-keyword-on-interface-members) - [Declaring an implementation of an incomplete interface](#declaring-an-implementation-of-an-incomplete-interface) - [Allow definition of private interfaces in separate impl file](#allow-definition-of-private-interfaces-in-separate-impl-file) - [No implementations for incomplete types](#no-implementations-for-incomplete-types) - [No forward declaration of named constraints](#no-forward-declaration-of-named-constraints) - [Repeating `private` in both declaration and definition](#repeating-private-in-both-declaration-and-definition) - [Allow function bodies using incomplete interfaces](#allow-function-bodies-using-incomplete-interfaces) - [Don't require parameter names to match](#dont-require-parameter-names-to-match) - [Allow deduced parameters to vary](#allow-deduced-parameters-to-vary) ## Problem Developers want to organize their code for readability and convenience. For example, they may want to present the public API of their type in a concise way. That includes the ability to say a type implements an interface without repeating the full contents of that interface. The Carbon compiler can give better diagnostics if it can assume every identifier it encounters refers to some earlier declaration in the file. However, sometimes multiple entities will reference each other in a cycle so no one entity can be defined first. ## Background We have decided to tackle these problems in a manner similar to C++ by supporting forward declarations: - [issue #472: Open question: Calling functions defined later in the same file](https://github.com/carbon-language/carbon-lang/issues/472) - [proposal #875: Principle: information accumulation](https://github.com/carbon-language/carbon-lang/pull/875). Use of the `default` keyword in `interface` definitions to allow defaulted members to be defined out-of-line was originally proposed in [withdrawn proposal #1034](https://github.com/carbon-language/carbon-lang/pull/1034). This proposal implements the decisions in [issue #1132: How do we match forward declarations with their definitions?](https://github.com/carbon-language/carbon-lang/issues/1132) as they apply to generic interfaces, implementations, and so on. ## Proposal This proposal makes changes to these sections of the [generics details design document](/docs/design/generics/details.md): - [Forward declarations and cyclic references](/docs/design/generics/details.md#forward-declarations-and-cyclic-references) section added - [Interface members with definitions](/docs/design/generics/details.md#interface-members-with-definitions) section added to ## Rationale based on Carbon's goals Forward declarations are intended to advance these goals: - [Language tools and ecosystem](/docs/project/goals.md#language-tools-and-ecosystem), by making Carbon easier to interpret by tooling in a single top-down pass. - [Code that is easy to read, understand, and write](/docs/project/goals.md#code-that-is-easy-to-read-understand-and-write), by allowing developers to separate declaration from definition when organizing the presentation of their code, and imposing constraints that allow readers to interpret the code with less skipping around. - [Fast and scalable development](/docs/project/goals.md#fast-and-scalable-development) from potential build performance improvements that come from allowing an `impl` to be defined in the `impl` file instead of the `api` file. The rationale behind using forward declarations are covered in more detail in: - [issue #472: Open question: Calling functions defined later in the same file](https://github.com/carbon-language/carbon-lang/issues/472) - [proposal #875: Principle: information accumulation](https://github.com/carbon-language/carbon-lang/pull/875). ## Alternatives considered ### No `default` keyword on interface members Without the `default` keyword, default definitions would always have to be inline. We discussed this in [the #syntax channel on Discord](https://discord.com/channels/655572317891461132/709488742942900284/941408009689641010) which eventually led to the [question-for-leads issue #1082: Use `default` keyword in interface defaults?](https://github.com/carbon-language/carbon-lang/issues/1082). The conclusion was that we did want to support forward declarations of default interface members. To make it so that users would have a single place to look to see whether the member had a definition even when it might be out of line, we decided to use a `default` keyword as a prefix of the declaration. We considered putting the keyword at the end of the declaration, but we decided it was more readable if it wasn't next to the return type. It was also more consistent with `final`, an alternative to `default`, which also now supports forward declaration. ### Declaring an implementation of an incomplete interface We did not have any use cases for forward declaring an impl of an incomplete interface, and so we took the conservative position of forbidding that. We could add this feature in the future if use cases were found, but clearly we can't have impl definitions until the interface is defined. ### Allow definition of private interfaces in separate impl file This proposal requires the definition of an interface to be in the same file as any declaration of it. We [anticipate](https://github.com/carbon-language/carbon-lang/pull/1084#discussion_r824214281) the possibility that we will find a use case for declaring a private interface in an API file that is defined in the corresponding impl file. An example where this may arise is if the constraint is only used when defining private members of an exported class. We would be willing to change if we see demand for this in the future. ### No implementations for incomplete types For simplicity, generally Carbon entities should either be "incomplete" or "defined" and never "partially defined". However, the set of interfaces implemented for a type is by necessity only ever partially known by the nature of being the [one static open extension mechanism](https://github.com/carbon-language/carbon-lang/pull/998) in Carbon. As a result, we felt there was more leeway for implementing interfaces for incomplete types. This happens incidentally when implementing the interface inline in the scope of a class definition. We also wanted to allow it in the case where there was only a forward declaration of the type in an API file. ### No forward declaration of named constraints We considered omitting the ability to forward declare named constraints, but we discovered that ability made declaring interfaces with cyclic dependencies easier and cleaner. Without this feature, [the graph example of cyclic references](/docs/design/generics/details.md#example-of-declaring-interfaces-with-cyclic-references) looked like this instead: ``` // Forward declaration of interface interface EdgeInterface; // Definition that only uses the declaration of // `EdgeInterface`, not its definition. interface NodeBootstrap { let EdgeType:! EdgeInterface; fn Edges[me: Self]() -> Vector(EdgeType); } // Now can define `EdgeInterface` in terms of // `NodeBootstrap`. interface EdgeInterface { let NodeType:! NodeBootstrap where .EdgeType == Self; fn Head[me: Self]() -> NodeType; } // Make `NodeInterface` a named constraint defined in // terms of `NodeBootstrap`, adding in constraints that // couldn't be written until `EdgeInterface` was defined. constraint NodeInterface { extends NodeBootstrap where .EdgeType.NodeType == Self; } ``` We did not like how the definition of `NodeInterface` was split into two pieces, making it harder to understand what it contained. This question was discussed in [the #generics channel on Discord](https://discord.com/channels/655572317891461132/941071822756143115/951288264315265114). ### Repeating `private` in both declaration and definition We considered repeating the access-control keyword `private` as a prefix of all `impl` declarations and definitions. The [current rule](/docs/design/generics/details.md#declaring-interfaces-and-named-constraints) only marks the first declaration or definition, which is consistent with [the policy of not repeating access-control keywords stated in an API file in an impl file](/docs/design/code_and_name_organization#exporting-entities-from-an-api-file). This was discussed in [the #syntax channel on Discord](https://discord.com/channels/655572317891461132/709488742942900284/951520959544823868), but this decision should be considered provisional since it was not considered deeply. We would be open to revisiting this decision in the future, once we had some experience with it. ### Allow function bodies using incomplete interfaces We [considered](https://docs.google.com/document/d/1UelNaT_j61G8rYp6qQZ-biRddTuGcxJtqXxrVbjB9rA/edit#heading=h.oqmpxtubjmkm) allowing a function definition to use an incomplete interface. One concern was whether the criteria for when the function body depended on something in the interface's definition would be too subtle for developers to reason about. We eventually concluded that, unless using a monomorphization compilation strategy, efficient code generation for a generic function would need to use the interface's definition. For example, an interface that represented a single function call might use a function pointer instead of a witness table. This same argument led to the requirement that the interface's definition be visible at call sites as well. ### Don't require parameter names to match We decided to diverge from C++ in requiring parameter names to match between declarations for a few reasons: - wanting to avoid the confusion that we've experienced when they don't match, noting that common C++ lint tools ask to make them match; - wanting reflection to return a single parameter name for a parameter; and - wanting the parameter names to be consistent with the single docstring we expect to associate with a function. This was discussed in [open discussion on 2022-03-14](https://docs.google.com/document/d/1UelNaT_j61G8rYp6qQZ-biRddTuGcxJtqXxrVbjB9rA/edit#heading=h.oqmpxtubjmkm) and [question-for-leads issue #1132](https://github.com/carbon-language/carbon-lang/issues/1132). ### Allow deduced parameters to vary We decided to apply [the same matching requirements for other parameter names](#dont-require-parameter-names-to-match) to deduced parameters for consistency. We may in the future allow some rewrites between equivalent expressions, such as between `Vector(T:! Type)` and `[T:! Type] Vector(T)`, but for now we are starting with the more restrictive rule. This was discussed in [open discussion on 2022-03-24](https://docs.google.com/document/d/1UelNaT_j61G8rYp6qQZ-biRddTuGcxJtqXxrVbjB9rA/edit#heading=h.w4zgqvarhnbn) and in [#syntax channel on Discord](https://discord.com/channels/655572317891461132/709488742942900284/953798170750615622). ================================================ FILE: proposals/p1088.md ================================================ # Generic details 10: interface-implemented requirements [Pull request](https://github.com/carbon-language/carbon-lang/pull/1088) ## Table of contents - [Problem](#problem) - [Background](#background) - [Proposal](#proposal) - [Rationale based on Carbon's goals](#rationale-based-on-carbons-goals) - [Alternatives considered](#alternatives-considered) - [Less strict about requirements with `where` clauses](#less-strict-about-requirements-with-where-clauses) - [Don't require `observe...is` declarations](#dont-require-observeis-declarations) ## Problem This proposal is to add the capability for an interface to require other types to be implemented, not just the `Self` type. The interface-implemented requirement feature also has some concerns: - If the interface requirement has a `where` clause, there are [concerns](#less-strict-about-requirements-with-where-clauses) about being able to locally check whether impls satisfy that requirement. - A function trying to make use of the fact that a type implements an interface due to an interface requirement, or a blanket impl, may require the compiler perform a search that we don't know will be bounded. ## Background The first version of interface-implemented requirements for interfaces was introduced in proposal [#553: Generics details part 1](https://github.com/carbon-language/carbon-lang/pull/553). ## Proposal This proposal adds two sections to the [generics details design document](/docs/design/generics/details.md): - [Interface requiring other interfaces revisited](/docs/design/generics/details.md#interface-requiring-other-interfaces-revisited) - [Observing a type implements an interface](/docs/design/generics/details.md#observing-a-type-implements-an-interface) ## Rationale based on Carbon's goals This proposal advances these goals of Carbon: - [Language tools and ecosystem](/docs/project/goals.md#language-tools-and-ecosystem): The motivation for this expressive power of interface requirements comes from discussions about how to achieve symmetric behavior with interfaces like `CommonTypeWith` from [proposal #911: Conditional expressions](https://github.com/carbon-language/carbon-lang/pull/911). - [Fast and scalable development](/docs/project/goals.md#fast-and-scalable-development): The requirement that the source provide the proof of any facts that would require a recursive search using `observe` declarations means that the expense of that search is avoided except in the case where there is a compiler error. If the search is successful, the results of the search can be copied into the source, and afterward the search need not be repeated. ## Alternatives considered ### Less strict about requirements with `where` clauses We could allow [requirements with `where` constraints](/docs/design/generics/details.md#requirements-with-where-constraints) to be satisfied by implementations that could be specialized, as long as the constraints were still satisfied. Unfortunately, this is not a condition that can be checked locally. Continuing the example from that section, consider four packages - A package defining the two interfaces ``` package Interfaces api; interface A(T:! Type) { let Result:! Type; } interface B(T:! Type) { impl as A(T) where .Result == i32; } ``` - A package defining a type that is used as a parameter to interfaces `A` and `B` in blanket impls: ``` package Param api; import Interfaces; class P {} external impl [T:! Type] T as Interfaces.A(P) where .Result = i32 { } // Question:Is this blanket impl of `Interfaces.A(P)` sufficient // to allow us to make this blanket impl of `Interfaces.B(P)`? external impl [T:! Type] T as Interfaces.B(P) { } ``` - A package defining a type that implements the interface `A` with a wildcard impl: ``` package Class api; import Interfaces; class C {} external impl [T:! Type] C as Interfaces.A(T) where .Result = bool { } ``` - And a package that tries to use the above packages together: ``` package Main; import Interfaces; import Param; import Class; fn F[V:! Interfaces.B(Param.P)](x: V); fn Run() { var c: Class.C = {}; // Does Class.C implement Interfaces.B(Param.P)? F(c); } ``` Package `Param` has an implementation of `Interfaces.B(Param.P)` for any `T`, which should include `T == Class.C`. The requirement in `Interfaces.B` in this case is that `T == Class.C` must implement `Interfaces.A(Param.P)`, which it does, and `Class.C.(Interfaces.A(Param.P).Result)` must be `i32`. This would hold using the blanket implementation defined in `Param`, but the wildcard impl defined in package `Class` has higher priority and sets the associated type `.Result` to `bool` instead. The conclusion is that this problem would only be detected during monomorphization, and could cause independent libraries to be incompatible with each other even when they work separately. These were significant enough downsides that we wanted to see if we could live with the restrictions that allowed local checking first. We don't know if developers will want to declare their parameterized implementations `final` in this situation anyway, even with [the limitations on `final`](/docs/design/generics/details.md#libraries-that-can-contain-a-final-impl). This problem was discussed in [the #generics channel on Discord](https://discord.com/channels/655572317891461132/941071822756143115/941089885475962940). ### Don't require `observe...is` declarations We could require the Carbon compiler to do a search to discover all interfaces that are transitively implied from knowing that a type implements a set of interfaces. However, we don't have a good way of bounding the depth of that search. In fact, this search combined with conditional conformance makes the question "is this interface implemented for this type" undecidable [in Rust](https://sdleffler.github.io/RustTypeSystemTuringComplete/). Note: it is possible that [the acyclic rule](/docs/design/generics/details.md#acyclic-rule) would avoid this problem in Carbon for blanket impls, but it doesn't apply to interface requirements. This problem was observed in [a discussion in #typesystem on Discord](https://discord.com/channels/655572317891461132/708431657849585705/938167784565792848). ================================================ FILE: proposals/p1144.md ================================================ # Generic details 11: operator overloading [Pull request](https://github.com/carbon-language/carbon-lang/pull/1144) ## Table of contents - [Problem](#problem) - [Background](#background) - [Proposal](#proposal) - [Rationale based on Carbon's goals](#rationale-based-on-carbons-goals) - [Alternatives considered](#alternatives-considered) - [Weak impls instead of adapters for reverse implementations](#weak-impls-instead-of-adapters-for-reverse-implementations) - [Default impls instead of adapters for reverse implementations](#default-impls-instead-of-adapters-for-reverse-implementations) - [Allow an impl declaration with `like` to match one without](#allow-an-impl-declaration-with-like-to-match-one-without) - [Where are the impl definitions from `like` generated?](#where-are-the-impl-definitions-from-like-generated) - [Support marking interfaces or their members as `external`](#support-marking-interfaces-or-their-members-as-external) ## Problem C++ supports [operator overloading](https://en.wikipedia.org/wiki/Operator_overloading), and we would like Carbon to as well. This proposal is about the general problem, not the specifics application to any particular operator. This proposal does not attempt to define a mechanism by which we can ensure that `a < b` has the same value as `b > a`. ## Background The generics feature is the [single static open extension mechanism](/docs/project/principles/static_open_extension.md) in Carbon, and so will be what we use operator overloading. We have already started specifying the ability to extend or customize the behavior of operators by implementing interfaces, as in these proposals: - [#820: Implicit conversions](https://github.com/carbon-language/carbon-lang/pull/820) - [#845: as expressions](https://github.com/carbon-language/carbon-lang/pull/845) - [#911: Conditional expressions](https://github.com/carbon-language/carbon-lang/pull/911) - [#1083: Arithmetic expressions](https://github.com/carbon-language/carbon-lang/pull/1083) Proposal [#702: Comparison operators](https://github.com/carbon-language/carbon-lang/pull/702) specified [using interfaces for overloading the comparison operators](p0702.md#overloading), but did not pin down specifically what those interfaces are. ## Proposal This proposal adds an ["Operator overloading" section](/docs/design/generics/details.md#operator-overloading) to the [detailed design of generics](/docs/design/generics/details.md). ## Rationale based on Carbon's goals This proposal advances Carbon's goals: - [Code that is easy to read, understand, and write](/docs/project/goals.md#code-that-is-easy-to-read-understand-and-write), by making common constructs more concise, and allowing the syntax to more closely mirror notation used math or the application domain. - [Software and language evolution](/docs/project/goals.md#software-and-language-evolution), since this allows standard types to implement operators using the same mechanisms that user types do, this allows changes between what is built-in versus provided in a library without user-visible impact. ## Alternatives considered The current proposal requires the user to define a reverse implementation, and recommends using an adapter to do that more conveniently. We also considered approaches that would provide the reverse implementation more automatically. ### Weak impls instead of adapters for reverse implementations We proposed [weak impls](https://github.com/carbon-language/carbon-lang/pull/1027) as a way of defining blanket impls for the reverse impl that did not introduce [cycles](/docs/design/generics/details.md#acyclic-rule). We rejected that approach due to giving the reverse implementation the wrong priority. This meant that there were many situations where `a < b` and `b > a` would give different answers. ### Default impls instead of adapters for reverse implementations We then proposed [default impls](https://github.com/carbon-language/carbon-lang/pull/1034) as a way to define reverse implementations. These were rejected because they had a lot of overlap with blanket impls, making it difficult to describe when to use one over the other, and because they introduced a lot of complexity without fully solving the priority problem. Most of the complexity was from the criteria for determining whether the default implementation would be used. As [noted](/docs/design/generics/details.md#binary-operators), the current proposal still has some priority issues, but this way the relevant impls are visible in the source which will hopefully make it clearer why it happens. The capability provided by default impls -- the ability to conveniently give implementations of other interfaces -- may prove useful enough that we would reconsider this decision in the future. ### Allow an impl declaration with `like` to match one without We considered allowing an impl declared with `like` to match the equivalent impls without `like`. The main concern was there would not be a canonical form without `like`, particularly of how the newly introduced parameter would be written. We thought we might say that the `like` declaration, since it omits a spelling of the parameter, is allowed to match any spelling of the parameter. However, there would still be a question of whether to use a deduced parameter, as in `[T:! ImplicitAs(i64)] Vector(T)` or not as in `Vector(T:! ImplicitAs(i64))`. We also considered the canonical form of `Vector(_:! ImplicitAs(i64))` without naming the parameter. In the end, we decided to start with a restrictive approach with the knowledge that we could change once we gained experience. The main use case for allowing declarations in a different form, which may motivate changes in the future, is to prioritize the different implementations generated by the `like` shortcut separately in `match_first` blocks. This was discussed in [open discussion on 2022-03-24](https://docs.google.com/document/d/1UelNaT_j61G8rYp6qQZ-biRddTuGcxJtqXxrVbjB9rA/edit#). ### Where are the impl definitions from `like` generated? We considered whether the additional impl definitions would be generated with the first declaration of an impl using `like` or with its definition. We ultimately decided on the former approach for two reasons: - The generated impl definitions are parameterized even if the explicit definition is not, and parameterized impl definitions may need to be in the API file to allow separate compilation. - This will make the code doing implicit conversions visible to callers, allowing it to be inlined, matching how the caller does implicit conversions for method calls. This was discussed in [the #generics channel on Discord](https://discord.com/channels/655572317891461132/941071822756143115/962059164014739526). ### Support marking interfaces or their members as `external` We [discussed on 2022-03-28](https://docs.google.com/document/d/1UelNaT_j61G8rYp6qQZ-biRddTuGcxJtqXxrVbjB9rA/edit#heading=h.sk06n1ggoa3o) the idea that operator interfaces might be marked `external`. This would either mean that types would only be able to implement them using `external impl` or that even if they were implemented internally, they would not add the names of the interface members to the type. Alternatively, individual members might be marked `external` to indicate that their names are not added to implementing types, which might also be useful for making changes to an interface in a compatible way. We were not sure if this feature was needed, so we left this as future work. ================================================ FILE: proposals/p1146.md ================================================ # Generic details 12: parameterized types [Pull request](https://github.com/carbon-language/carbon-lang/pull/1146) ## Table of contents - [Problem](#problem) - [Background](#background) - [Proposal](#proposal) - [Rationale based on Carbon's goals](#rationale-based-on-carbons-goals) - [Alternatives considered](#alternatives-considered) ## Problem Most aspects of generic parameterization are the same between functions and types, but there are a few things specific to types. In particular: - the declaration of a type can contain a greater variety of members, and - types have identity which affects type comparisons, deduced parameters, and implied constraints. We also want a [generic specialization](/docs/design/generics/terminology.md#checked-generic-specialization) story that works well for types, without giving up the ability to type check users of a type without knowing which specializations apply. ## Background C++ supports specialization, including partial specialization, for templated types and functions. ## Proposal This proposal adds a ["parameterized types" section](/docs/design/generics/details.md#parameterized-types) to the [detailed design of generics](/docs/design/generics/details.md). Of note, it proposes not to support specialization of types or functions since those use cases can be handled by delegating to interfaces, which already support specialization. ## Rationale based on Carbon's goals Specialization is important for allowing code to be generic without sacrificing [performance](/docs/project/goals.md#performance-critical-software). Since there is already a way to support specialization use cases without adding direct support for specializing types, this proposal follows the Carbon principle to [prefer providing only one way to do a given thing](/docs/project/principles/one_way.md). By avoiding another way of customizing behavior for specific types, it makes interfaces the [single static open extension mechanism](/docs/project/principles/static_open_extension.md). This proposal maintains consistency between generic parameterization of types and functions, in support of [code that is easy to read, understand, and write](/docs/project/goals.md#code-that-is-easy-to-read-understand-and-write). ## Alternatives considered We considered supporting specialization for types directly. To support type checking in a generic context, the API of the type needs to be defined independent of which specialization is selected. This would have introduced complexity into the language: - Would the API of the type be represented by an `interface`? - Would the type's API be explicitly declared or inferred from the type declaration by some process, at the risk of including details that are not necessarily stable? - How would public data members be handled, since interfaces (currently) don't support them? - How would we support non-monomorphizing generic strategies? With the current proposal, the layout of a parameterized type is known unless it uses an associated type. The main disadvantage of the proposed approach is that the author of the type needs to define the ways that the type can be customized. We will need to see if this ends up being a problem in practice. It may turn out to be a benefit, by giving more information about the implementation of a class to readers. ================================================ FILE: proposals/p1154.md ================================================ # Destructors [Pull request](https://github.com/carbon-language/carbon-lang/pull/1154) ## Table of contents - [Problem](#problem) - [Background](#background) - [Proposal](#proposal) - [Rationale based on Carbon's goals](#rationale-based-on-carbons-goals) - [Alternatives considered](#alternatives-considered) - [Types implement destructor interface](#types-implement-destructor-interface) - [Prevent virtual function calls in destructors](#prevent-virtual-function-calls-in-destructors) - [Allow functions to act as destructors](#allow-functions-to-act-as-destructors) - [Allow private destructors](#allow-private-destructors) - [Allow multiple conditional destructors](#allow-multiple-conditional-destructors) - [Allow direct implementation of `TrivialDestructor`](#allow-direct-implementation-of-trivialdestructor) - [Type-of-type naming](#type-of-type-naming) - [Other approaches to extensible classes without vtables](#other-approaches-to-extensible-classes-without-vtables) - [Don't distinguish safe and unsafe delete operations](#dont-distinguish-safe-and-unsafe-delete-operations) - [Don't allow unsafe delete](#dont-allow-unsafe-delete) - [Allow final destructors](#allow-final-destructors) ## Problem C++ code supports defining custom [destructors]() for user-defined types, and C++ developers will expect to use that tool. Carbon should support destructors, but should make some changes to protect against deleting a value with a derived type through a pointer to the base class when the base class destructor is not virtual. We also need some mechanism to allow generic code to identify types that are safe to destroy, or have trivial destructors and don't need to be explicitly destroyed. ## Background Destructors may only be customized for nominal classes, which were introduced in proposal [#722](https://github.com/carbon-language/carbon-lang/pull/722). Destructors interact with inheritance, which was introduced in proposal [#777](https://github.com/carbon-language/carbon-lang/pull/777). Destructors were discussed in open discussion on these dates: - [2021-07-12](https://docs.google.com/document/d/14vAcURDKeH6LZ_TQCMRGpNJrXSZCACQqDy29YH19XGo/edit#heading=h.40jlsrcgp8mr) - [2021-08-30](https://docs.google.com/document/d/1YhwNKLxQsWf8NPVaRm9PvgPmSM3PIK_KlD1gpNuUfwY/edit#heading=h.4dobu6v1cdam) - [2021-10-04](https://docs.google.com/document/d/1YhwNKLxQsWf8NPVaRm9PvgPmSM3PIK_KlD1gpNuUfwY/edit#heading=h.a0rffns9r10n) discussed not all types being destructible, and that some types are `TriviallyDestructible` - [2021-10-18](https://docs.google.com/document/d/1YhwNKLxQsWf8NPVaRm9PvgPmSM3PIK_KlD1gpNuUfwY/edit#heading=h.uz59mgk5ezch) - [2022-03-24](https://docs.google.com/document/d/1UelNaT_j61G8rYp6qQZ-biRddTuGcxJtqXxrVbjB9rA/edit#heading=h.cy8m79pgct1v) As part of [proposal 777: Inheritance](https://github.com/carbon-language/carbon-lang/pull/777), we decided to support extensible classes, that is non-abstract base classes, [including with non-virtual destructors](p0777.md#no-extensible-objects-with-non-virtual-destructors). ["Extensible classes" Google doc](https://docs.google.com/document/d/1gbQJN_IMJBnquOUUd2orbHLlAIqZ4pL0Vt7h34DkQjg/edit?resourcekey=0-0lkEvh0umUU206ASFlWc7A#) from that time considered options for making deleting extensible classes safer. ## Proposal This proposal adds two sections to the design: - ["Destructors"](/docs/design/classes.md#destructors) to the [classes design](/docs/design/classes.md), and - ["Destructor constraint"](/docs/design/generics/details.md#destructor-constraints) to the [detailed generics design](/docs/design/generics/details.md). ## Rationale based on Carbon's goals Destructors advance these goals: - [Practical safety and testing mechanisms](/docs/project/goals.md#practical-safety-and-testing-mechanisms), since destructors are about making sure clean-up actions are performed by doing them automatically, which is helpful for avoiding safety-related bugs. - [Interoperability with and migration from existing C++ code](/docs/project/goals.md#interoperability-with-and-migration-from-existing-c-code), since destructors are relied upon in C++. ## Alternatives considered ### Types implement destructor interface We considered letting developers implement a destructor interface, as is done in [Rust](https://doc.rust-lang.org/std/ops/trait.Drop.html). This would be more consistent with the other customization points for classes, but had some downsides that we ultimately decided were too large: - Destructors are relatively common, and implementing an interface involves more boilerplate than writing a destructor in C++, so we wanted a concise syntax. - Destructors need access to the private members of a class, so generally would have to be part of the class. - We didn't have any use cases where an author of a type used as a type argument to a class should be able to override the destructor for the class, so users would have to mark destructors as `final` or risk surprises. - Abstract classes without virtual destructors may have code that should run when the type is destroyed, but don't implement any destructor type-of-types. - More generally, we wanted the compiler to enforce that the correct type-of-types were implemented. This was considered in the open discussions on [2021-08-30](https://docs.google.com/document/d/1YhwNKLxQsWf8NPVaRm9PvgPmSM3PIK_KlD1gpNuUfwY/edit#heading=h.4dobu6v1cdam) and [2021-10-18](https://docs.google.com/document/d/1YhwNKLxQsWf8NPVaRm9PvgPmSM3PIK_KlD1gpNuUfwY/edit#heading=h.uz59mgk5ezch). We decided to go with the proposed approach in [the open discussion on 2022-03-24](https://docs.google.com/document/d/1UelNaT_j61G8rYp6qQZ-biRddTuGcxJtqXxrVbjB9rA/edit#heading=h.cy8m79pgct1v). ### Prevent virtual function calls in destructors We considered disallowing virtual calls to be made while executing the destructor. This would avoid having to assign to the vtable pointer between each level of the class hierarchy's destructor calls. This seemed like an avoidable expense since you can't call a derived method implementation from a destructor anyway. We observed that this assignment, which happens in C++, is unsynchronized and could cause race conditions when there was synchronization inside a destructor. In Carbon, we expect to mitigate that somewhat by doing the assignment at the end of the destructor instead of the beginning, which is safe since we won't have multiple inheritance. This means that at least the most derived class' destructor can acquire locks before any unsynchronized writes occur. We didn't decide to include this approach in this proposal, since we felt like it would likely be too burdensome to prevent calling member functions from a destructor unless the developer did some extra work to mark all functions it transitively calls. We would consider this as a potential future extension. As noted in the design, this could be achieved by using the `partial` type. This was considered in the open discussions on [2021-08-30](https://docs.google.com/document/d/1YhwNKLxQsWf8NPVaRm9PvgPmSM3PIK_KlD1gpNuUfwY/edit#heading=h.4dobu6v1cdam) and [2021-10-18](https://docs.google.com/document/d/1YhwNKLxQsWf8NPVaRm9PvgPmSM3PIK_KlD1gpNuUfwY/edit#heading=h.uz59mgk5ezch). ### Allow functions to act as destructors [We considered, on 2021-08-30](https://docs.google.com/document/d/1YhwNKLxQsWf8NPVaRm9PvgPmSM3PIK_KlD1gpNuUfwY/edit#heading=h.4dobu6v1cdam), how we might support returning a value, for example a failure, from a destructor. This would require that the destructor be called explicitly, rather than implicitly as part of a `Allocator.Delete()` call or a variable leaving scope. This led to a design where you could have "named destructors" that were ordinary methods except that they consumed their `me` parameter, as is possible in Rust. As a result, the `me` parameter would be marked with a keyword like `destroy` or `consume`, like: ``` class MyClass { fn MyDestroyer[destroy me: Self](x: i32) -> bool; } ``` We would need some way to make the ending of the lifetime visible in the caller, perhaps: ``` var c: MyClass = ...; // `~c` indicates lifetime of `c` is ending. var b: bool = (~c).MyDestroyer(3); ``` There were questions about how this would interact with unformed state. We decided to keep this idea in mind, but there is not a pressing need for this feature now since this doesn't mirror a capability of C++. We might reconsider as part of error handling strategy, or a desire to model resources that can be consumed. ### Allow private destructors C++ allows destructors to be marked `private`, which has some use cases such as controlling deletes of a reference-counted value. This introduces [context-sensitivity](/docs/project/principles/low_context_sensitivity.md). It is particularly concerning if whether a type implements a constraint depends on which function is the enclosing scope. This was considered in the [open discussion on 2021-08-30](https://docs.google.com/document/d/1YhwNKLxQsWf8NPVaRm9PvgPmSM3PIK_KlD1gpNuUfwY/edit#heading=h.4dobu6v1cdam). ### Allow multiple conditional destructors A parameterized type might want to specialize a destructor based on properties of the parameter. For example, it might do something more efficient if its parameter is a `TriviallyDestructible` type. An approach consistent with [conditional methods](/docs/design/generics/details.md#conditional-methods) and the [prioritization rule](/docs/design/generics/details.md#prioritization-rule) would be to write something like: ``` class Vector(T:! Movable) { // Prioritize using `match_first` match_first { // Express conditions by using a // specific `Self` type. destructor [U:! TriviallyDestructible, me: Vector(U)]; // If first doesn't apply: destructor [me: Self]; } } ``` #### Allow direct implementation of `TrivialDestructor` For the specific case where the developer wants to specialize the destructor to be trivial under certain circumstances, we could allow the developer to directly implement `TrivialDestructor`. That implementation would include the criteria when the trivial destructor should be used. For example, we might express that `Optional` has a trivial destructor when its argument does by writing: ``` class Optional(T:! Concrete) { var value: Storage(T); var holds_value: bool; // It's perfectly safe, I assure you impl [U:! TrivialDestructor] Optional(U) as TrivialDestructor {} destructor [me: Self] { if (me.holds_value) value.Destroy(); } } ``` ### Type-of-type naming We considered other names for the type-of-types introduced in this proposal. The name `Deletable` is inconsistent with [the decision on #1058](https://github.com/carbon-language/carbon-lang/issues/1058), so we considered alternatives like `Delete`, `CanDelete`, `DeleteInterface`, `DeleteConstraint`, or `SafeToDelete`. We agreed that it should match whatever term was used in the allocation interface, and finalizing that was out of scope for this proposal. So we left it at `Deletable` for now, with the understanding that this was only a placeholder and not the final name. Instead of `Destructible`, which is inconsistent with [the decision on #1058](https://github.com/carbon-language/carbon-lang/issues/1058), we considered: - `DestructorConstraint`: a bit too long - `HasDestructor`: means something slightly different - `CanDestroy`: also inconsistent with [the decision on #1058](https://github.com/carbon-language/carbon-lang/issues/1058) We ultimately decided that the best name is likely `Unsafe` followed by the name we chose in place of `Deletable`, and would wait until that is decided. Originally `TrivialDestructor` was spelled `TriviallyDestructible`, but the word "destructor" was more aligned with the `destructor` keyword being used in declarations. We also considered replacing "trivial" with something like "no-op" or "empty" to emphasize that the destructor does literally nothing and can be completely omitted, but decided to stay consistent with C++ for now. Instead of `Concrete`, we also considered `Instantiable`, but this choice is both more concise and more clearly the opposite of "abstract." ### Other approaches to extensible classes without vtables There were a few alternatives we considered that were specifically concerned about how to handle the unsafe case of deleting a pointer to a base class that does not have a virtual destructor, but may be pointing to a derived value. We [decided to allow this case](p0777.md#no-extensible-objects-with-non-virtual-destructors) as part of [proposal 777: Inheritance](https://github.com/carbon-language/carbon-lang/pull/777). We decided to go with the proposed approach in [the open discussion on 2022-03-24](https://docs.google.com/document/d/1UelNaT_j61G8rYp6qQZ-biRddTuGcxJtqXxrVbjB9rA/edit#heading=h.cy8m79pgct1v). #### Don't distinguish safe and unsafe delete operations We could, like C++, have a single operation that includes both the safe and unsafe cases. We decided we would like to try the safer option to see if it is viable or if it causes problems with interoperation with C++. We expect that migrated C++ code can fall-back to using `UnsafeDelete` if using `Delete` is too burdensome. This option was considered in the ["Option: C++ DWIM model" section of "Extensible classes" doc](https://docs.google.com/document/d/1gbQJN_IMJBnquOUUd2orbHLlAIqZ4pL0Vt7h34DkQjg/edit?resourcekey=0-0lkEvh0umUU206ASFlWc7A#heading=h.d2ybn2szry11). #### Don't allow unsafe delete For even more safety, we considered preventing deletion of extensible classes without virtual destructors entirely. However, extensible classes without virtual destructors were not that rare in C++ code we examined. It did not seem likely this would provide enough C++ interop, and there were concerns that C++ developers would generally want to have an escape hatch for performing operations that they could prove to themselves were safe even if the compiler could not. This option was considered in the ["Option: Like C++ but forbid unsafe" section of "Extensible classes" doc](https://docs.google.com/document/d/1gbQJN_IMJBnquOUUd2orbHLlAIqZ4pL0Vt7h34DkQjg/edit?resourcekey=0-0lkEvh0umUU206ASFlWc7A#heading=h.718ogac3yb9l). #### Allow final destructors One option we [considered early on](https://docs.google.com/document/d/14vAcURDKeH6LZ_TQCMRGpNJrXSZCACQqDy29YH19XGo/edit#heading=h.40jlsrcgp8mr) for extensible classes with non-virtual destructors, was to require the same thing we do from non-virtual methods. That is, require that the implementation in the base class is appropriate for derived classes. We ultimately decided on a different approach, but we could still provide that as an option. You would declare the destructor as `final`, and that would impose the necessary restrictions on any derived class: - No custom destructor code. - Either no added data members, or, if we are willing to support unsized deletes, no added data members with non-trivial destructors. Base classes with `final` destructors would be `Deletable`, like base classes with `virtual` destructors. This would be a safe option without the overhead of including a vtable in your object, but fairly restrictive in its applicability. [We decided](https://discord.com/channels/655572317891461132/708431657849585705/958595054707023964) is viable but not pressing since this isn't a feature present in C++, and we would wait and see if this would fill a common need. ================================================ FILE: proposals/p1178.md ================================================ # Rework operator interfaces [Pull request](https://github.com/carbon-language/carbon-lang/pull/1178) ## Table of contents - [Problem](#problem) - [Background](#background) - [Proposal](#proposal) - [Details](#details) - [Rationale](#rationale) - [Alternatives considered](#alternatives-considered) - [Use `ComparableWith` instead of `OrderedWith`](#use-comparablewith-instead-of-orderedwith) ## Problem Our operator interface names need to be updated to match the decision in [#1058](https://github.com/carbon-language/carbon-lang/issues/1058). Further, we are missing a description of the interfaces used to overload comparison operators, and the rules are not up to date with the decision in [#710](https://github.com/carbon-language/carbon-lang/issues/710). ## Background See the two leads issues for background and discussion of options. ## Proposal See changes to the design. ## Details Beyond establishing names for interfaces, this proposal also establishes: - We will have high-level interfaces for equality and relational comparison. The equality interface provides both `==` and `!=`. The relational comparison interface provides all of `<`, `<=`, `>`, and `>=`. - Following the convention established for arithmetic operators, we provide both a heterogeneous comparison interface and a homogeneous constraint. For example, `T is EqWith(T)` is equivalent to `T is Eq`. - The high-level interfaces always return `bool`. - The high-level interfaces have expected semantics associated with them. It is intended that we also provide low-level interfaces, to directly control individual operators and to allow a result type other than `bool`. These are not included in this proposal, as it's not yet clear how they should be specified, and it's more important to get the high-level interfaces decided at this point. ## Rationale - [Language tools and ecosystem](/docs/project/goals.md#language-tools-and-ecosystem) - High-level semantics allow tools to reason about the intended meaning of Carbon code. For example, a tool could statically or dynamically determine that an implementation of `Ordered` doesn't satisfy the expected rules and produce a warning. - [Performance-critical software](/docs/project/goals.md#performance-critical-software) - We expect `==` and ordering to be customized separately, in order to avoid cases where a suboptimal `==` is constructed in terms of an ordering. See [C++ committee paper P1190R0](http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p1190r0.html) for details on the problem. - [Code that is easy to read, understand, and write](/docs/project/goals.md#code-that-is-easy-to-read-understand-and-write) - Combining all comparison operators of the same kind -- equality or relational -- into a single interface makes it both easier to implement them and easier to write a generic constraint for them. This approach is also expected to be easy to teach, with the low-level interfaces only explained to a more advanced audience. - [Practical safety and testing mechanisms](/docs/project/goals.md#practical-safety-and-testing-mechanisms) - While there are rules for the comparison interfaces, violating those rules does not result in immediate unbounded undefined behavior. However, implementations should still attempt to detect violations of these rules and report them where that is feasible. - [Interoperability with and migration from existing C++ code](/docs/project/goals.md#interoperability-with-and-migration-from-existing-c-code) - The intent to provide a low-level interface for individual operators is directly motivated by the desire to provide strong interoperability with operators defined in C++. While this functionality is not part of this proposal, it's expected to follow once the interactions with generics are worked out. ## Alternatives considered ### Use `ComparableWith` instead of `OrderedWith` We could use the term "comparable" for relational comparisons instead of "ordered". There is existing practice for both: for example, Rust and Haskell use `Ord`, and Swift uses `Comparable`. The main argument for using "ordered" instead of "comparable" is that `==` and `!=` are also a form of comparison but aren't part of `OrderedWith`, and the word "ordered" distinguishes relational comparison from equality comparison. ================================================ FILE: proposals/p1190.md ================================================ # Reviewer-merged PRs [Pull request](https://github.com/carbon-language/carbon-lang/pull/1190) ## Table of contents - [Problem](#problem) - [Background](#background) - [Proposal](#proposal) - [Details](#details) - [Rationale](#rationale) - [Alternatives considered](#alternatives-considered) - [Never merge PRs from developers with merge access](#never-merge-prs-from-developers-with-merge-access) - [Grant all potential contributors merge access](#grant-all-potential-contributors-merge-access) - [Allow reviewers to clean up pull request descriptions](#allow-reviewers-to-clean-up-pull-request-descriptions) - [Only have authors resolve merge conflicts](#only-have-authors-resolve-merge-conflicts) - [Implement a two-person rule for source code changes](#implement-a-two-person-rule-for-source-code-changes) ## Problem We've been having authors merged PRs, but that's not going to work when we get contributors who don't have merge access. We need a solution. ## Background It's been mentioned that LLVM favors having authors merge due to the risks of breaking build bots. The LLVM community's leaning is to favor author merges so that the author can decide whether to try rolling back or fixing forward. At present Carbon has essentially been using a [shared repository model](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/getting-started/about-collaborative-development-models#shared-repository-model) where authors merge their own PRs, but that's difficult to extend to a fully public setup. The [fork and pull model](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/getting-started/about-collaborative-development-models#fork-and-pull-model) is the other main git collaboration model, and is popular with open source projects. ## Proposal Encourage reviewers to merge when they feel okay doing so. Let reviewers make that choice. Let authors say they'll merge themselves. This is a [fork and pull model](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/getting-started/about-collaborative-development-models#fork-and-pull-model) which encourages reviewer merges. ## Details See changes to [code review](/docs/project/code_review.md). ## Rationale - [Community and culture](/docs/project/goals.md#community-and-culture) - Defines a process for accepting contributions from developers who don't have merge access. ## Alternatives considered ### Never merge PRs from developers with merge access We could tell reviewers to never merge PRs from developers with merge access. This is also a [fork and pull model](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/getting-started/about-collaborative-development-models#fork-and-pull-model), but minimizing reviewer merges. Advantages: - While this proposal suggest authors could opt out from having the reviewer merge, minimizing reviewer merges avoids a gray area when an author forgets or the reviewer misses it (even if enforced, the author might do it wrong). Disadvantages: - Relies on the reviewer figuring out whether the author has merge access, which they may forget to do. The most likely consequence is that outside contributors will need to ping to get PRs merged. - Makes reviewer merges less common. This in turn can make them less consistently done well due to unfamiliarity. - Contributors without merge access would be following a different process, and as a consequence it increases the chance that a new contributor would be first to discover a problem, which in turn can discourage contributions. In the future, build bots may cause more issues, and we may lean more in this direction. It could also be that we end up here through standard practice of coordinating with the reviewer is concerned the author may need to fix a break post-commit. However, it doesn't seem necessary to make it a hard rule at present. ### Grant all potential contributors merge access We could grant the public merge access; in other words, continue with a [shared repository model](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/getting-started/about-collaborative-development-models#shared-repository-model), but fully public instead of private. This way, reviewers would not need to consider access. Advantages: - Authors can always merge, removing the burden on reviewers. Disadvantages: - Relies on approvals heavily for code security, which constrains decisions [regarding whether to keep CODEOWNERS](https://github.com/carbon-language/carbon-lang/issues/413). - This kind of setup is likely atypical for GitHub projects, and so may be more surprising than alternatives. - Harder for reviewers to discern between frequent contributors and new contributors. - GitHub has an option, ["Dismiss stale pull request approvals when new commits"](https://docs.github.com/en/repositories/configuring-branches-and-merges-in-your-repository/defining-the-mergeability-of-pull-requests/managing-a-branch-protection-rule): we would likely want to enable it to reduce the weight on reviewers, although it means reviewers would need to approve again for any change (likely including merge commits). - May create issues when a novice contributor breaks something. - There must not be an expectation that novice contributors should understand processes, unless demonstrating an understanding becomes a prerequisite for review. ### Allow reviewers to clean up pull request descriptions We could allow reviewers to clean up pull request descriptions, rather than asking the author to. Advantages: - Decreases the number of review round-trips. Disadvantages: - May lead to PR descriptions not being in the authors voice, frustrating authors. The preference is to let authors control pull request descriptions. ### Only have authors resolve merge conflicts We could only have authors resolve merge conflicts, instead of having reviewers do it. Advantages: - Lowers the chance of an incorrect merge, because authors are likely to better understand the conflict. - Lets ambiguous resolves be handled with the author's voice. - If a bad resolve introduces a bug, it's the author's fault, rather than being the reviewer's fault but _attributed_ to the author. Disadvantages: - Increases the number of review round-trips. - Pull requests by authors lacking merge access would have an extra round-trip, because the author would resolve conflicts then the reviewer would merge the pull request. In a worst case this may bounce back and forth due to new conflicts being added before the reviewer merged. The preference is to minimize review round-trips. However, this could still be a real-world outcome if reviewers generally only merge when there are no outstanding merge conflicts. ### Implement a two-person rule for source code changes We could implement a [two-person rule](https://en.wikipedia.org/wiki/Two-man_rule) for source code changes, where both an author _and_ reviewer must see the merged code. GitHub supports this with ["Dismiss stale pull request approvals when new commits are pushed"](https://docs.github.com/en/repositories/configuring-branches-and-merges-in-your-repository/defining-the-mergeability-of-pull-requests/managing-a-branch-protection-rule), although we may also need to require 2 approvers per pull request. Advantages: - Gives stronger code security, eliminating situations where either the author or reviewer could merge unreviewed changes. Disadvantages: - Increases the number of review round-trips. - Right now, reviewers can approve with minor comments ("fix typo"). Fixing those would require a new commit, which in turn would require fresh approval. - Truly preventing this issue may require setting 2 approvers per pull request, so that a reviewer couldn't push a commit to the pull request then approve and merge. Requiring 2 approvers also increases review overhead. The preference is to minimize review round-trips. ================================================ FILE: proposals/p1191.md ================================================ # Bitwise and shift operators [Pull request](https://github.com/carbon-language/carbon-lang/pull/1191) ## Table of contents - [Problem](#problem) - [Background](#background) - [Overflow in shift operators](#overflow-in-shift-operators) - [Proposal](#proposal) - [Details](#details) - [Rationale](#rationale) - [Alternatives considered](#alternatives-considered) - [Use different symbols for bitwise operators](#use-different-symbols-for-bitwise-operators) - [Use a multi-character spelling](#use-a-multi-character-spelling) - [Don't provide an xor operator](#dont-provide-an-xor-operator) - [Use `~`, or some other symbol, for complement](#use--or-some-other-symbol-for-complement) - [Provide different operators for arithmetic and logical shifts](#provide-different-operators-for-arithmetic-and-logical-shifts) - [Provide rotate operators](#provide-rotate-operators) - [Guarantee behavior of large shifts](#guarantee-behavior-of-large-shifts) - [Support shifting a constant by a variable](#support-shifting-a-constant-by-a-variable) - [Converting complements to unsigned types](#converting-complements-to-unsigned-types) ## Problem Carbon needs operations for working with bit representations of values. ## Background C++ provides four bitwise operations for Boolean algebra: complement (`~`), and (`&`), or (`|`), and xor (`^`). These are all useful in bit-manipulation code (although `^` is used substantially less than the others). In addition, C++ provides two bit-shift operators `<<` and `>>` that can perform three different operations: left shift, arithmetic right shift, and logical right shift. The meaning of `>>` is determined by the signedness of the first operand. C and Swift use the same set of operators as C++. Rust uses most of the same operators, but uses `!` instead of `~` for complement, unifying it with the logical not operator, which is spelled `not` in Carbon and as `!` in Rust and C++. Go uses most of the same operators as C++, but uses unary prefix `^` instead of `~` for complement, mirroring binary `^` for xor. In addition to the operators provided by C and C++, bit-rotate operators are present in some languages, and a short notation for them may be convenient. Finally, there are other non-degenerate binary Boolean operations with no common operator symbol: - The "implies" operator (equivalent to `~a | b`). - The "implied by" operator (equivalent to `a | ~b`). - The complement of each of the other operators (NAND, NOR, XNOR, "does not imply", "is not implied by"). ### Overflow in shift operators The behavior of shift operators in C++ had a turbulent past. The behavior of shift operators has always been undefined if the right operand is out of range -- not between zero inclusive and the bit-width of the left operator exclusive -- but other restrictions have varied: - Unsigned left shift has never had any restrictions on the first operand. - For signed left shift: - In C++98, the result was fully unspecified. - In C++11, the result was specified only if the first operand was non-negative and the result fit into the destination type -- that is, if no 1 bit is shifted into the sign bit. - In C++14, the result was specified only if the first operand was non-negative and the result fit into the unsigned type corresponding to the destination type -- that is, if no 1 bit is shifted out of the sign bit. - In C++20 onwards, there are no restrictions beyond a range restriction on the right operand, and the result is otherwise always specified, even if the left operand is negative. - Unsigned right shift has never had any restrictions on the first operand. - For signed right shift: - In C++17 and earlier, if the left operand is negative, the result is implementation-defined. - In C++20 onwards, the result is always specified, even if the left operand is negative. There is a clear trend towards defining more cases, following two's complement rules. ## Proposal Use the same operator set as C++, but replace `~` with unary prefix `^`. Define the behavior for all cases of `<<` and `>>` except where the right operand is either negative or is greater than or equal to the bit-width of the left operand. ## Details See changes to the design, and in particular [the new section on bitwise and shift operators](/docs/design/expressions/bitwise.md). ## Rationale - [Performance-critical software](/docs/project/goals.md#performance-critical-software) - Bit operations are important low-level primitives. Providing operators for them is important in order to allow low-level high-performance code to be written elegantly in Carbon. - By not defining semantics for `<<` and `>>` when the right-hand operand is out of range, we can directly use hardware instructions for these operations whose behavior in these cases vary between architectures. - [Code that is easy to read, understand, and write](/docs/project/goals.md#code-that-is-easy-to-read-understand-and-write) - Using operator notation rather than function call notation for bitwise operators improves readability in code making heavy use of these operations. - [Practical safety and testing mechanisms](/docs/project/goals.md#practical-safety-and-testing-mechanisms) - Carbon follows C++ in treating `<<` and `>>` as programming errors when the right hand operand is out of range, but Carbon guarantees that such errors will not directly result in unbounded misbehavior in hardened builds. - [Modern OS platforms, hardware architectures, and environments](/docs/project/goals.md#modern-os-platforms-hardware-architectures-and-environments) - All hardware architectures we care to support are natively two's complement architectures, and that assumption allows us to define the semantics of shift operators in the way that makes the most sense for such architectures. - Our bitwise operations make no assumptions about the endianness of the hardware architecture, although the shift operators make the most sense on a little-endian or big-endian architecture, which are the only endiannesses we expect to see in modern hardware platforms. - [Interoperability with and migration from existing C++ code](/docs/project/goals.md#interoperability-with-and-migration-from-existing-c-code) - The same set of operators is provided as in C++, making it easy for programmers and programs to migrate, with the exception that the `~` operator is mechanically replaced with `^`. This change is expected to be easy for programmers to accommodate, especially given that Rust's choice to replace `~` with `!` does not seem to be a source of sustained complaints. - The extensibility support reflects the full scope of operator overloading in C++, permitting separate overloading of each of the bitwise operators with custom return types. This should allow smooth interoperability with C++ overloaded operators. ## Alternatives considered ### Use different symbols for bitwise operators The operator syntax for bitwise operators was decided in [#545](https://github.com/carbon-language/carbon-lang/issues/545). Some of the specific alternatives considered are discussed below. #### Use a multi-character spelling We considered using various multi-character spellings for the bitwise and, or, xor, and complement operators: - `&:`, `|:`, `^:`, `~:` - `.&.`, `.|.`, `.^.`, `.~.` - `.*.`, `.+.`, `.!=.`, `.!.` - `/\`, `\/`, `(+)`, `-|` - `bitand`, `bitor`, `bitxor`, `compl` The advantage of switching to such a set of operators is that this would free up the single-character `&`, `|`, `^`, and `~` tokens for other uses that may occur more frequently in Carbon programs. We have some candidate uses for these operators already: - `&` is used for combining interfaces and as a unary address-of operator. - `|` may be useful for sum types, for alternatives in patterns, or as another kind of bracket as in [Ruby's lambda notation](https://ruby-doc.org/docs/ruby-doc-bundle/Manual/man-1.4/syntax.html#iter). - `~` may be useful as a destructive move notation. - `^` may be useful as a postfix pointer dereference operator. Other motivations for switching to a different set of spellings include: - There are some concerns that `<` and `<<` are visually similar, analogous to `&` and `&&`. - Carbon has moved away from `&&` and other punctuation based _logical_ operators and towards keywords like `and`. Bitwise operators could similarly switch to keywords like `bitand`. However, moving substantially away from the C++ operator set comes with a set of concerns: - There are strong established expectations and intuitions about these operators and their spellings among C++ practitioners. - In some of the code that uses these operators, they are used a lot, and a more cumbersome operator may consequently cause an outsized detriment on readability. - These operations are used particularly in the area of low-level, high-performance code, which is an area for which we want Carbon to be especially appealing. Using short operators for these operations demonstrates our commitment to providing good support for such code. - Even if we didn't use these operators as bit operators, we would still want to exercise caution when using them for some other purpose to avoid surprising C++ developers. - While some visual similarity exists such as between `<` and `<<`, the contexts in which these operators are used are sufficiently different to avoid any serious concerns. - The primary motivation of using `and` instead of `&&` doesn't apply for bitwise operators: the _logical_ operator represents _control flow_. Separating logical and bitwise "and" operations more visibly seems especially important because of this control flow semantic difference. Without any control flow and with the keywords being significantly longer for bitwise operations, the above considerations were the dominant ones that led us to stick with familiar `&` and `|` spellings. #### Don't provide an xor operator We considered omitting the `^` operator, providing this functionality in some other way, such as a named function or an `xor` keyword, while keeping the `&` and `|` symbols for bitwise operations. We could take a similar approach for the complement operation, such as by using a `compl` keyword. The primary motivation was to avoid spending two precious operator characters on relatively uncommon operations. However, we did not want to apply the same change to `&` and `|`, and it seemed important to maintain consistency between the three binary bitwise operators from C++. Using `^` for both operations provides some of the benefits here, allowing us to reclaim `~`, without introducing the inconsistency that would result from using keywords. #### Use `~`, or some other symbol, for complement We could follow C++ and use `~` as the complement operator. However, using `~` for this purpose spends a precious operator character on a relatively uncommon operation, and `~` is often visually confusible with `-`, creating the potential for readability problems. Also, in C++, `~` is overloaded to also refer to destruction, and we may want to use the same notation for destruction or destructive move semantics in Carbon. We found `^` to be a satisfying alternative with a good rationale and mnemonic: `^` is a bit-flipping operator -- `a ^ b` flips the bits in `a` that are specified in `b` -- and complement is an operator that flips _all_ the bits. `^a` is equivalent to `a ^ n`, where `n` is the all-one-bits value in the type of `a`. We also considered using `!` for complement, like Rust does. Unlike in Rust, this would not be a generalization of `!` on `bool`, because we use `not` for `bool` negation, and repurposing `!` in this way compared to C++ seemed confusing. ### Provide different operators for arithmetic and logical shifts We could provide different operators for arithmetic right shift and logical right shift. This might allow programmers to better express their intent. However, it seems unnecessary, as using the type of the left operand is a strategy that doesn't appear to have caused significant problems in practice in the languages that have followed it. Basing the kind of shift on the signedness of the left operand also follows from viewing a negative number as having an infinite number of leading 1 bits, which is the underlying mathematical model behind the two's complement representation. ### Provide rotate operators We could provide bitwise rotation operators. However, there doesn't seem to be a sufficient need to justify adding another operator symbol for this purpose. ### Guarantee behavior of large shifts Logically, the behavior of shifts is meaningful for all values of the second operand: - A shift by an amount greater than or equal to the bit-width of the first operand will shift out all of the original bits, producing a result where all value bits are the same. - A shift in one direction by a negative amount is treated as a shift in the opposite direction by the negation of that amount. Put another way, we can view the bits of the first operand as an N-bit window into an infinite sequence of bits, with infinitely many leading sign bits (all zeroes for an unsigned value) and infinitely many trailing zero bits after a notional binary point, and a shift moves that window around. Or equivalently, a shift is always a multiplication by 2N followed by rounding and wrapping. We could provide the correct result for all shifts, regardless of the magnitude of the second operand. This is the approach taken by Python, except that Python rejects negative shift counts. The primary reason we do not do this is lack of hardware support. For example, x86 does not have an instruction to perform this operation. Rather, x86 provides shift instructions that mask off all bits of the second operand except for the bottom 5 or 6, meaning that a left shift of a 64-bit operand by 64 will return the operand unchanged rather than producing zero. We could instead provide x86-like behavior, guaranteeing to consider only the lowest `N` bits of the second operand when the first operand is an `iN` or `uN`. This is the approach taken by Java for its 32-bit `int` type and 64-bit `long` type, where the second operand is taken modulo 32 or 64, respectively, and in JavaScript, where operands of bitwise and bit-shift operators are treated as 32-bit integers and the second operand of a shift is taken modulo 32. This approach would provide an operation that can be implemented by a single instruction on x86 platforms when `N` is 32 or 64, and for all smaller types and for all other platforms the operation can be implemented with two instructions: a mask and a shift. For larger types, single-instruction support may not be available, but nonetheless the performance will be close to optimal, requiring at most one additional mask. There is still some performance cost in some cases, but the primary reason we do not do this is the same reason we choose to not define signed integer overflow: this masked result is unlikely to be the value that the developer actually wanted. Instead of the above options, Carbon treats a second operand that is not in the interval [0, N) as a programming error, just like signed integer overflow: - Debugging builds can detect and report this error without the risk of false positives. - Performance builds can optimize on the basis that this situation will not occur, and can in particular use the dedicated x86 instructions that ignore the high order bits of the second operand. - Optimized builds guarantee that either the programming error results in program termination or that _some_ value is produced, and moreover that said value is the result of applying _some_ mathematical shift to the input. For example, it's valid for an `i32` shift to be implemented by an x86 64-bit shift that will produce 0 if the second operand is in [32, 63) but that will treat a second operand of, say, 64 or -64 the same as 0. ### Support shifting a constant by a variable We considered various ways to support ``` var a: i32 = ...; var b: i32 = 1 << a; var c: i32 = 1234 >> a; ``` with no explicit type specified for the first operand of a bit-shift operator. We considered the following options: - Use the type of the second operand as the result type. This would be surprising, because the type of the second operand doesn't otherwise influence the result type of a built-in bit-shift operator. - Use some suitable integer type that can fit the first operand. However, this is unlikely to do the right thing for a left-shift, and will frequently pick either a type that's too large, resulting in the program being rejected due to narrowing, or a type that's too small, resulting in a program that has undefined behavior due to the second operand being too large. We could apply this approach only for right shifts, but it was deemed too inconsistent to use different rules for left and right shifts. - We could find a way to defer picking the type in which the operation is performed until later. For example, we could treat `1 << a` as a value of a new type that carries its left-hand operand as a type parameter and its right-hand operand as runtime state, and allow that type to be converted in the same way as its integer constant. However, this would introduce substantial complexity: reasonable and expected uses such as ``` var mask: u32 = (1 << a) - 1; ``` would require a second new type for a shifted value plus an offset, and general support would require a facility analogous to [expression templates](https://en.wikipedia.org/wiki/Expression_templates). Further, this facility would allow implicit conversions that notionally overflow, such as would happen in the above example when `a` is greater than 32. In the absence of a good approach, we disallow such conversions for now. The above example can be written as: ``` var a: i32 = ...; var b: i32 = (1 as i32) << a; var c: i32 = (1234 as i32) >> a; ``` ### Converting complements to unsigned types We view an integer constant has having infinitely many high-order sign bits followed by some number of lower-order value bits. As a consequence, the complement of a positive integer constant is negative. As a result, some important forms of initialization use a negative integer constant initializer for an unsigned type: ``` // Initializer here is the integer value -8. var mask: u32 = ^7; ``` We considered some options for handling this: - We could allow negative integer constants to convert to unsigned types if doing so only discards sign bits. This violates the "semantics-preserving" rule for implicit conversions. - We could change our model of integer constants to distinguish between "masks" -- numbers with infinitely many 1 bits preceding the value bits that are nonetheless not considered to be negative. This was considered to introduce too much complexity. - We could allow conversions to unsigned types from signed types and negative constants in general, or at least in cases where the signed operand is no wider than the unsigned type, and perform wrapping. The latter option seems plausible, but we don't have sufficient motivation for it, and were worried about a risk of bugs from allowing an implicit conversion at runtime that converts a negative value to an unsigned type. - We could reject such initializations, with an explicit conversion required to convert such values to unsigned types. This seems to present unacceptable ergonomics for code performing bit-manipulation. On balance, our preferred option was to permit implicit conversions from negative literals to unsigned types so long as we only discard sign bits. ================================================ FILE: proposals/p1270.md ================================================ # Update and expand `README` content and motivation for Carbon [Pull request](https://github.com/carbon-language/carbon-lang/pull/1270) ## Table of contents - [Problem](#problem) - [Background](#background) - [Proposal](#proposal) - [Details](#details) - [Rationale](#rationale) - [Alternatives considered](#alternatives-considered) - [Avoid discussion of motivations](#avoid-discussion-of-motivations) - [Avoid summarizing key language design areas](#avoid-summarizing-key-language-design-areas) - [Avoid discussing the difficulty of directly and incrementally improving C++](#avoid-discussing-the-difficulty-of-directly-and-incrementally-improving-c) ## Problem Feedback from folks outside of the immediate team working on Carbon surfaced both some problems with the exact phrasing of our main README content, but more importantly some major _gaps_ in our overall documentation. Specifically, we failed to really explain our motivations for building Carbon and why this approach might make sense. Given the significance of the new content and the importance of these specific topics, this level of change seems important to go through the proposal process. ## Background We've been trying to polish and improve the positioning and explanation of Carbon to help understand whether it makes sense to shift the project towards being a _public_ experiment instead of private one. ## Proposal This PR includes a significant update to the [`README`](/README.md) content, as well as adding [a new document](/docs/project/difficulties_improving_cpp.md) to explain the difficulties with incrementally improving C++. It also tweaks the wording of our goals to try to further reduce confusion. ## Details See the pull request for the detailed change. ## Rationale - [Community and culture](/docs/project/goals.md#community-and-culture) - We should document clearly our motivations to ensure that aspect of the project remains transparent and clear. - [Software and language evolution](/docs/project/goals.md#software-and-language-evolution) - Understanding the _motivations_ of the Carbon project will be important for future language evolution efforts. ## Alternatives considered ### Avoid discussion of motivations We could instead choose to avoid discussion of the project's motivations. This has largely been the status-quo prior to this change. Advantages: - Less text. - Fewer opportunities for a misunderstanding to develop. Disadvantages: - Fails to be transparent. We _do_ have motivations, and we can't realistically pretend otherwise. - Because we all _do_ have motivations, failing to write them down will largely result in an inconsistent and lower-quality presentation of them in informal discussions and forums. ### Avoid summarizing key language design areas This proposal suggests some brief summaries around both [generics](/README.md#generics) and [memory safety](/README.md#memory-safety). We could instead skip these or only have a brief mention of these. Advantages: - Less text. - May be inaccurate and will run the risk of drifting out of date. - This was a larger concern previously when for example generics was undergoing more active development. Disadvantages: - Fails to give people an easily consumed entry into some of the really exciting aspects of the language design. - Memory safety at least will likely be an immediate question for readers where we can front-load a well considered answer. ### Avoid discussing the difficulty of directly and incrementally improving C++ Previously we didn't go into details about the difficulties with incrementally improving C++ itself that are an essential component of the motivation for Carbon. We could stay with that approach. Advantages: - Less text. - A very contentious subject that will have many divergent, well-reasoned, and strongly held positions. - Prior attempts to articulate this have ended up being easily misunderstood or implying significantly more than was intended in a way that actually reduced alignment between different readers rather than building alignment and shared understanding. Disadvantages: - Despite the _difficulty_ of articulating this, it remains _important_. We shouldn't avoid doing the work here merely because it is difficult. - Omitting the discussion of these difficulties runs a risk of seeming disingenuous -- the premise of the Carbon project makes it clear that there is a significant motivation here. - Overcoming the difficulty of articulating these difficulties well and in an understandable form will significantly strengthen the Carbon project's overall motivation and how it can engage with the broader industry. ================================================ FILE: proposals/p1280.md ================================================ # Principle: All APIs are library APIs [Pull request](https://github.com/carbon-language/carbon-lang/pull/1280) ## Table of contents - [Problem](#problem) - [Background](#background) - [Proposal](#proposal) - [Details](#details) - [Rationale](#rationale) - [Alternatives considered](#alternatives-considered) - [Built-in primitive types](#built-in-primitive-types) ## Problem We need a clear and consistent "division of labor" between the core language and the standard library. ## Background See [the principle doc](/docs/project/principles/library_apis_only.md#background). See also [#57](https://github.com/carbon-language/carbon-lang/pull/57), a previous iteration of a similar idea. ## Proposal In Carbon, every public function will be declared in some Carbon `api` file, and every public `interface`, `impl`, and first-class type will be defined in some Carbon `api` file. In some cases, the bodies of public functions will not be defined as Carbon code, or will be defined as hybrid Carbon code using intrinsics that aren't available to ordinary Carbon code. However, we will try to minimize those situations. Thus, even "built-in" APIs can be used like user-defined APIs, by importing the appropriate library and using qualified names from that library, relying on the ordinary semantic rules for Carbon APIs. ## Details See [the principle doc](/docs/project/principles/library_apis_only.md). ## Rationale This principle facilitates [software evolution](/docs/project/goals.md#software-and-language-evolution), by helping to ensure that code written in terms of a Carbon-provided type can be migrated to use a suitable user-defined type instead. It also facilitates evolution of the language itself, by enabling more of that evolution to take place in library code, which doesn't require compiler expertise. This principle helps make Carbon code [easy to read, understand, and write](/docs/project/goals.md#code-that-is-easy-to-read-understand-and-write), because user-defined APIs can match the ergonomics of language-defined APIs, and the syntax, language rules, and core concepts are consistent between the two. This principle indirectly helps Carbon support [performance-critical software](/docs/project/goals.md#performance-critical-software): by using Carbon's API abstraction mechanisms for even the most fundamental types, we ensure that those mechanisms do not impose any performance overhead. ## Alternatives considered ### Built-in primitive types We could follow the general outline of C++, where arithmetic and pointer types are built-in. However, that would substantially erode the advantages outlined above. We expect Carbon to have multiple kinds of pointers (for example, to represent different kinds of ownership), and multiple kinds of arithmetic types (for example, to handle overflow in different ways). They can't all be built-in, so putting even the common-case types in the library helps ensure that Carbon has enough expressive power for the uncommon-case library types. ================================================ FILE: proposals/p1327.md ================================================ # Generics: `impl forall` [Pull request](https://github.com/carbon-language/carbon-lang/pull/1327) ## Table of contents - [Problem](#problem) - [Background](#background) - [Proposal](#proposal) - [Rationale](#rationale) - [Alternatives considered](#alternatives-considered) ## Problem We have an ambiguity in the grammar between declaring a parameterized impl and an impl for an array type. ``` // Parameterized impl external impl [T:! Printable] Vector(T) as Printable { ... } // Impl for an array type external impl [i32; 5] as Printable { ... } ``` When the parser sees `impl [`, it doesn't know which kind of impl declaration it is parsing without more lookahead than we plan to support. For the same reason, these declarations are easy for humans to confuse. ## Background Parameterized impls introduced in [#920: Generic parameterized impls (details 5)](https://github.com/carbon-language/carbon-lang/pull/920). Array syntax has not been finalized, but the leading contender is `[i32; 5]`, similar to [Rust](https://doc.rust-lang.org/rust-by-example/primitives/array.html). This is what is currently provisionally implemented in Explorer. Some other contenders also start with `[` and have the same problem. This problem was discussed and resolved in question-for-leads issue [#1192: Parameterized impl syntax](https://github.com/carbon-language/carbon-lang/issues/1192). ## Proposal This proposal implements the decision in [#1192](https://github.com/carbon-language/carbon-lang/issues/1192) to write parameterized impls using this syntax: > `impl forall [`_generic parameters_`]` _type_ `as` _constraint_ ... and to remove the option to includes bindings in the _type_ `as` _constraint_ part of the declaration. This PR includes the changes to [the generics details design doc](/docs/design/generics/details.md). ## Rationale This decision favored approaches that did not require more lookahead. This is to simplify compiler and tool development and to make it easier for humans to read the code, in support of these goals: - [Language tools and ecosystem](/docs/project/goals.md#language-tools-and-ecosystem) - [Code that is easy to read, understand, and write](/docs/project/goals.md#code-that-is-easy-to-read-understand-and-write), particularly "Excellent ergonomics", "Support tooling at every layer of the development experience, including IDEs", and "Design features to be simple to implement." ## Alternatives considered @zygoloid listed some options for addressing this problem [in the #syntax channel on Discord](https://discord.com/channels/655572317891461132/709488742942900284/963191891334168628): > Summary of options for implicit parameters / arrays ambiguity discussed so > far: > > 1. Just make it work as-is: `impl [a; b]` parses as an array type, > `impl [a, b]` parses as an implicit parameter. Theoretically this is > unambiguous given that a `;` is required inside the `[`...`]` in the former > and disallowed in the latter. Concerns: it's likely to be visually > ambiguous. > 2. Add mandatory parentheses: `impl [T:! Type] (Vector(T) as Container)`. > Concerns: it's hard to avoid requiring them in cases that don't start with > a `[` if we want an unambiguous grammar. Requiring them always would impose > a small ergonomic hit. > 3. Add an introducer keyword for implicit parameters: > `impl where [T:! Type] Vector(T) as Container`. Unambiguous. Concerns: > still some visual ambiguity due to reuse of `[`...`]`, concern over whether > we'd uniformly use this syntax (`fn F where [T:! Type](x: T)`) or have > non-uniform syntax for implicit parameters. > 4. Use a different syntax for array types in general: > `impl Array(T) as Container` or `impl Array[N] as Container`. Concerns: may > want a first-class syntax here, especially if (per @geoffromer 's variadics > work, we want some special behavior for a deduced bound), and there's a > strong convention to use `[`...`]` for this. The latter syntax is messy > because of our types-as-expressions approach, but we could imagine > providing a `impl Type as Indexable where .Result = Type` to construct > array types. `T[]` might be a special case of some kind. > 5. Use a different syntax for implicit parameters in general: > `impl Vector(T) as Container`. Concerns: we don't have many > delimiter options unless we start using multi-character delimiters; `()`, > `[]`, and `{}` are all used for types, leaving `<>` as the only remaining > bracket. Use of `<>` as brackets as a long history but not a good one. ... > 6. Remove the implicit parameter list from impls and force them to be > introduced where they're first used: `impl Vector(T:! Type) as Container`. > Concerns: harms readability in some cases, eg > `impl Optional(T:! As(U:! Type)) as As(Optional(U))` versus > `impl [U:! Type, T:! As(U)] Optional(T) as As(Optional(U))`. > 7. Move the implicit parameter list before the impl keyword, perhaps with an > introducer: `generic [T:! Type] impl Vector(T) as Container`. Concerns: > increases verbosity; would be inconsistent if we put everything but me > there, and surprising if we put me there. Also not clear what a good > keyword is, given that the existence of deduced parameters isn't the same > as an entity being generic. Ultimately we adopted approach 3, but changed to the new keyword `forall` to avoid overloading the meaning of a keyword used for something else. ================================================ FILE: proposals/p1344.md ================================================ # Remove LLVM from the repository, and clean up history. [Pull request](https://github.com/carbon-language/carbon-lang/pull/1344) ## Table of contents - [Problem](#problem) - [Proposal](#proposal) - [Details](#details) - [Implementation](#implementation) - [Patching LLVM](#patching-llvm) - [Updating forks and clones](#updating-forks-and-clones) - [Archive your existing fork and clones](#archive-your-existing-fork-and-clones) - [Create a fresh fork and clone](#create-a-fresh-fork-and-clone) - [Porting in-progress branches from your archived clone](#porting-in-progress-branches-from-your-archived-clone) - [Updating pending PRs](#updating-pending-prs) - [Review comments may be disrupted](#review-comments-may-be-disrupted) - [Rationale](#rationale) - [Alternatives considered](#alternatives-considered) - [Do nothing](#do-nothing) - [Don't rewrite the repository history.](#dont-rewrite-the-repository-history) - [Go back to submodules](#go-back-to-submodules) - [Rename the repository, and create a new one](#rename-the-repository-and-create-a-new-one) - [Manually extract and archive some review comments](#manually-extract-and-archive-some-review-comments) ## Problem We have tried both Git submodules and subtrees to manage accessing the LLVM source code as part of Carbon. We have had significant issues with both of these over time. Git submodules are well supported in general, but make the repository significantly less user-friendly. There are a number of common operations, not least the initial clone of the repository, that are made frustratingly more complex in the presence of a submodule. In some cases (switching branches), it can even cause hard to diagnose errors for users. It also doesn't directly help with carrying local patches. Because of all of these, we switched to subtrees. Git subtrees in some ways work much better once set up as they are simply a normal directory for most users. They also make it especially easy to make and carry local patches. However, setting up the subtree and updating the subtree are bug prone and interact very poorly with GitHub's pull request workflow. The result is that updating LLVM is currently both very difficult and error prone. Another fundamental problem subtrees expose is that the LLVM repository is _huge_. It includes large and complex projects that Carbon is unlikely to ever use, and it doesn't make sense for us to start our repository off with all of that history and space if we can avoid it. Including LLVM causes things like `git status` or cloning the repository to be dramatically more expensive without significant benefit. ## Proposal We should switch to using Bazel to download a snapshot of LLVM during the build, and do a destructive history re-write to the Carbon repository to completely remove LLVM from it. All of the building against LLVM will continue to work seamlessly. However, the repository will become tiny and fast to work with, and the snapshot download is significantly cheaper than cloning or working with the full-history of LLVM. The only way to capture the benefit here is to do a destructive update to the repository's history. This is unfortunate, but essential to do as soon as possible and before we shift to be public. Despite the cost of making this change, the cost of _not_ making it will grow without bound for the life of the project. **Important:** This _will_ require a manual update of some kind for every fork and clone. The steps to update them are provided below. ## Details ### Implementation This will be accomplished using the [`git-filter-repo` tool](https://github.com/newren/git-filter-repo). This makes it very easy to prune any trees from the history, replaying the history left and resulting in a clean and clear result. We will remove three other trees while here that are no longer used: - `src/jekyll/` - `src/firebase/` - `website/` Once we're taking a history break, we should capture the value we can and have a minimal history of the repository we are actually using. The largest of these is `src/jekyll` (988kb even packed), but it seems cleanest to remove the three together. ### Patching LLVM We still need to support patching LLVM. Initially, we will start with a simple approach of applying a collection of patch files from the Carbon repository to the LLVM snapshot. This is especially convenient while Carbon's repositories are not public. Eventually, we can switch to having a fork of LLVM that we snapshot and developing any needed patches there. Having a more robust long-term solution is expected to be important if larger scale development is needed on Clang when building interoperability support. Developing patches is also very easy when using Bazel. You can pass `--override_repository=llvm-project=` ([docs](https://bazel.build/reference/command-line-reference#flag--override_repository)) to have Bazel use a local checkout of LLVM (with any patches you are testing) rather than downloading it. If doing significant development, this can be added to your `user.bazelrc` file to consistently use your development area. ### Updating forks and clones If you have a fork and any clones of the repository with work that you want to save, we suggest the following process: #### Archive your existing fork and clones 1. Go to the archived repository: (broken link: `https://github.com/carbon-language/archived-carbon-lang`) 2. Fork this archived repository just like you normally would (into your _personal_ space). It is important to fork the `carbon-language` hosted archived repository so you pick up the ACLs. You should **not** create your own personal repository. 3. Update any clones to use these archived remotes to avoid accidentally trying to interact with the new repository. Example commands assuming the remote `origin` points to your fork and `upstream` points to the main repository. You may also need to adjust from the SSH-style URLs to HTTPS ones to match: ``` git remote set-url upstream git@github.com:carbon-language/archived-carbon-lang.git git@github.com:carbon-language/carbon-lang.git git remote set-url origin git@github.com:$USER/archived-carbon-lang.git git@github.com:$USER/carbon-lang.git ``` 4. For each clone, mirror everything into the new `origin` (your fork): ``` git push --mirror origin ``` If you have multiple clones with different but overlapping branches, this may require some extra steps to avoid collisions when doing the mirror push. At this point, your clones should all point to an archival fork and should be able to function "normally" cleanly. The goal here is just archival and making sure you don't lose anything. #### Create a fresh fork and clone Delete your existing fork of `carbon-lang` (_not_ `archived-carbon-lang`): 1. Go to your fork's settings page: `https://github.com/$USER/carbon-lang/settings`. Be certain this is your _fork_ and not the `carbon-language` organization. 2. Scroll to the bottom, and click `Delete this repository`. Once deleted, create a fresh fork from the now-filtered main repository: https://github.com/carbon-language/carbon-lang/fork Clone this fork and you're ready to go with the new repository structure. #### Porting in-progress branches from your archived clone For any branch in your archive clones that you want to import, there are two approaches. The simplest way is to extract the branch as a patch file and apply it in the new repository: ``` # If your old clone is in the `archived-carbon-lang` directory and your new # clone is in `carbon-lang`, both of them on the `trunk` branch: cd archived-carbon-lang git switch $BRANCH git diff upstream/trunk... > ../$BRANCH.patch cd ../carbon-lang git switch -c $BRANCH patch -p1 < ../$BRANCH.patch git commit ``` This will just flatten the branch into a single commit in the new clone. If you want to preserve the commit history, you can do that using `git format-patch`. Some points to remember here: - Make sure you've rebased the branch into a clean series of commits on top of the latest trunk in the archived repository. - Use `git format-patch` to get a series of patches in a directory. See its help for detailed usage. - Use `git am` to apply the series of patches in your new clone under some branch. Again, see its help for detailed usage. #### Updating pending PRs The pending PRs should end up closed automatically when you delete your fork above. If not, the simplest approach is to close them and just create a new PR. In-flight discussion comment threads will be awkward, but there isn't much else to do. Rename the proposal document if needed. We will explore whether we can re-open PRs with a fresh forced push from the fresh fork, but aren't confident in this working. It also isn't necessary, and we only have 12 PRs open at the moment. ### Review comments may be disrupted When we edit the repository, some PR comments (especially pending PRs that are updated to follow a freshly created fork) may lose their association with the line of code they were made against, and it is possible we may be unable to find some comments. This will at most apply to inline comments within the code of PRs, but that is the common case for PRs. We expect most of these to still be somewhat visible in the conversation view of the pull request, but they may be hard to find due to no longer being attached to a line. ## Rationale - [Community and culture](/docs/project/goals.md#community-and-culture) - This will make it even easier and smoother for folks to clone, build, and even contribute. - It will make clones significantly cheaper, especially those not building code but just working on documentation. - [Language tools and ecosystem](/docs/project/goals.md#language-tools-and-ecosystem) - Preserving the ability to build with LLVM and develop patches will be important as we expand the set of language tools and ecosystem being developed. ## Alternatives considered ### Do nothing We could simply continue using the moderately broken Git subtree approach. Advantages: - No need to change our approach. - No destructive operation on the repository requiring everyone to update. Disadvantages: - Every update of LLVM remains difficult, manual, and error prone. - We're currently carrying an LLVM patch, and it's easy to lose (and has been lost) in updates. - Will continually hit bugs in Git subtree as it seems both brittle and not a priority. We will have to struggle to understand and fix or work around them. - The repository remains massive, slow, and contains significant LLVM code that we will never use. - If we ever have users that wish to import the Carbon repository into some other environment, they will have to pay the cost of LLVM or remove it somehow. It may even cause them to have two copies of LLVM. We think this problem is worth solving. ### Don't rewrite the repository history. We could fix this without rewriting history. If we choose not to rewrite history now, it should be noted that the cost of rewriting history only grows and so we should expect to _never_ rewrite history. Advantages: - No need for manual steps to update forks, clones, in-flight patches, etc. - Commit hashes remain stable. - All code review comment information associated with commit hashes will be retained. Disadvantages: - We pay the cost of having imported LLVM forever. Even if this cost is incrementally small (some amount of repository space when cloning with history), it is a cost we will never stop paying. While the immediate costs are high, the unbounded time frame for which we will pay for leaving LLVM in the history means that will eventually be the dominant cost and we should just rewrite history, and the sooner the better. ### Go back to submodules Rather than switching to use Bazel downloaded snapshots, we could go back to using Git submodules. The original motivation to move away from submodules was needing to carry a local patch. Submodules and the proposed direction have the same options there, and our approach is discussed above. Advantages: - Somewhat more of a Git-native approach. - Would avoid needing to invent another approach if we add a non-Bazel build system. Disadvantages: - Much of the cloning cost and other Git command costs we see with subtrees would still be present. - It makes working with the Git repository even more tricky to get right. - While less esoteric than subtrees, it still exposes a less polished surface of Git that we will have to cope with. - Most notably, this will re-introduce the stumbling block of users first encountering Carbon not having a seamless experience. We think we should try something simpler here, even if a bit less of a native-Git solution. It is also especially important for us to optimize the initial new contributor flow, and the Bazel approach is expected to be more seamless. ### Rename the repository, and create a new one Rather than editing the repository in place, this would move it aside and create a new one with the edited history. Advantages: - Some disruptive aspects of the in-place history edit would be avoided, specifically parts of PRs that are associated with commit hashes would likely continue working due to the commit hashes being preserved. Disadvantages: - For most cases, this would be equally disruptive -- forks and clones would have the same update needed as they reference the repository by name. - We would either need to build a system to carefully re-create the exact issue numbering and PR numbering, which may not even be possible, or we would need to allow all issue numbers and PR numbers to churn. - The PR numbers churning is especially disruptive as the commit log references them to connect commits to the code review that led to the commit. They are also the basis of the proposal numbers. - There isn't likely a way to move the PRs back to the main repository, so browsing the historical code reviews would become problematic. This seems to largely trade off preservation of some of the comment history within PRs for preserving the location, numbers, and links of all PRs and issues. That doesn't seem like the right tradeoff. ### Manually extract and archive some review comments We could attempt to extract and archive review comments in case they are lost or made hard to find by the change. Advantages: - Defense in depth against any information loss here. Disadvantages: - Would be a decent amount of work to get this right. - May not lose much information here. We think the comments will still be in the conversation view, but maybe hard to find. - Unclear whether any of these will actually have value, especially extracted and out of context from the review where they were made. - Serious concerns about arbitrarily scraping and moving comments other folks authored outside of the system (GitHub) that they authored them within. Overall, while there is some risk here, we don't think it is too high and the cost of trying to mitigate this seems unreasonable given the relatively small total amount of data at issue here. ================================================ FILE: proposals/p1360.md ================================================ # Change raw string literal syntax: `[#]\*"` represents single-line string and `[#]\*'''` represents block string [Pull request](https://github.com/carbon-language/carbon-lang/pull/1360) ## Table of contents - [Problem](#problem) - [Background](#background) - [Proposal](#proposal) - [Details](#details) - [Rationale](#rationale) - [Alternatives considered](#alternatives-considered) - [The current implementation](#the-current-implementation) - [Using `"""` to start block string literals](#using--to-start-block-string-literals) - [Non-quote marker after the open quote](#non-quote-marker-after-the-open-quote) - [Use different quotes to allow `#'"'#`](#use-different-quotes-to-allow-) ## Problem Under current design of string literals, users may make assumptions that a starting `[#]*"""` represents a block string and misunderstand the syntax. ## Background The design of [string literals](https://github.com/carbon-language/carbon-lang/blob/trunk/docs/design/lexical_conventions/string_literals.md) specifies a block string literal to start with `[#]*"""`. Users may take for granted the other way around, where any string literal starting with `[#]*"""` is a block string literal. This does not hold true, however. Two counter-cases are `"""abc"""` represents three tokens `""`, `"abc"` and `""`, and `#"""#` which is equivalent to `"\""`. Neither is a block string literal and may be visually confusing. ## Proposal Interpret `[#]*"` as the start of single-line string literals, and `[#]*'''` as the start of block string literals. Disallow adjacent string literals like `"""abc"""`. ## Details Users can easily distinguish single-line string literals from block string literals with the proposed change. Confusion on `"""abc"""` will be eliminated because adjacent string literals are invalid in the proposal. On the other hand, `#"""#` will be clear to the user of representing `"\""`, as `"""` does not represent a block string literal any more. More details can be found [here](/docs/design/lexical_conventions/string_literals.md). ## Rationale This principle helps make Carbon code [easy to read, understand, and write](/docs/project/goals.md#code-that-is-easy-to-read-understand-and-write), because it avoids confusion on the type of certain string literals. ## Alternatives considered ### The current implementation In addition to the confusion described above, the [current implementation](https://github.com/carbon-language/carbon-lang/blob/trunk/docs/design/lexical_conventions/string_literals.md) complicates the lexing. When the lexer sees `[#]+"""`, it temporarily accepts syntax of both string types because the type of the string is undecided. Specifically, it accepts vertical whitespaces even if they are not allowed in single-line strings. The type of the string won't be decided until the lexer sees a closing `"#` or a new line. In case of a closing `"#` where the string is single-line, the lexer will look back on the scanned characters for vertical whitespaces to decide if the single-line string is valid. ### Using `"""` to start block string literals This approach loses some convenience in using raw string literals while addressing the problem. For example, as discussed in [issue #1359](https://github.com/carbon-language/carbon-lang/issues/1359), `#"""#` is a natural way to write a string of `"`. ### Non-quote marker after the open quote Although something similar to C++ style like `"(` solves the problem, the syntax becomes complicated and hurts readability. In addition, `"(")"` is no simpler than `"\""` or `#"""#`. ### Use different quotes to allow `#'"'#` When disallowing adjacent string literals, we can additionally allow `[#]+'` on single-line string literals. Another option is to use `[#]+'` for single-line string literals and `[#]+"` for block string literals. In general, `#'"'#` is visually confusing with character `'"'` and hurts readability. ================================================ FILE: proposals/p1363.md ================================================ # Make the Carbon experiment public [Pull request](https://github.com/carbon-language/carbon-lang/pull/1363) ## Table of contents - [Problem](#problem) - [Background](#background) - [Proposal](#proposal) - [Why go public now?](#why-go-public-now) - [Planned process for going public](#planned-process-for-going-public) - [Public site and communications plan](#public-site-and-communications-plan) - [Plan for ACLs once public](#plan-for-acls-once-public) - [Risks and mitigations](#risks-and-mitigations) - [Too many cooks in the kitchen](#too-many-cooks-in-the-kitchen) - [Community management overload](#community-management-overload) - [Added distraction or confusion to the C++ evolution process](#added-distraction-or-confusion-to-the-c-evolution-process) - [Added distractions from existing new programming languages.](#added-distractions-from-existing-new-programming-languages) - [Friction with existing LLVM and Clang communities](#friction-with-existing-llvm-and-clang-communities) - [Labeled as vaporware](#labeled-as-vaporware) - [Rationale](#rationale) - [Alternatives considered](#alternatives-considered) - [Delay going public until we have working interop demonstrated](#delay-going-public-until-we-have-working-interop-demonstrated) - [Delay going public until Carbon is a compelling option for C++ developers to adopt](#delay-going-public-until-carbon-is-a-compelling-option-for-c-developers-to-adopt) ## Problem Open, transparent, and public development is often the best way to build a developer community. Long-term evolution of Carbon behind closed doors does not align with our core principles. While early exploration with a limited group of participants has been effective at bootstrapping, a primary goal for the Carbon experiment is to establish whether there will be **broad industry interest and participation** in this direction. We expect this kind of interest and participation to ultimately result in widespread adoption if the technical components of the experiment succeed. We both want to see how the industry reacts to Carbon and think that reaction will be much more positive if the industry can actively participate and help shape the language. ## Background Historically, Carbon has followed a "quiet" development model. While developed on GitHub using an open source process, the project was not publicly visible or discussed outside the invited set of early participants. Carbon developed an initial set of criteria that we expected to signal the correct time for the project to become public: - Broader field experience required - Sustained interest from multiple organizations and individuals - A prototype implementation - A demonstration of potential - Learning material - Prepared for broader contributions and feedback - Prepared for a launch event See the removed `going_public.md` document in this [proposal](https://github.com/carbon-language/carbon-lang/pull/1363) for the full historical criteria and analysis. ## Proposal We should make the Carbon experiment public as soon as reasonably possible, specifically at the upcoming [C++ North](https://cppnorth.ca) conference. Many of the criteria outlined previously have not yet been met, and this proposal specifically suggests shifting Carbon to be public without waiting for them. Instead, we should make Carbon public largely as it is today. We should be open and transparent about the current status. We should frame this as making a _nascent_ project public in order to _build it in the open_, rather than suggesting it is "done" or "ready" even for evaluation. ### Why go public now? There is no perfect time to shift the project public, it is a tradeoff. The earlier we move the project to be public, the earlier we can gain more broad feedback and participation from the industry. However, it comes at the cost of introducing people to the project at an earlier stage with less material to help them get started. We believe Carbon has crossed the point where this tradeoff suggests sooner would be overall better for the project than later. **The Carbon experiment has accomplished what it can** to explore industry interest and participation with limited and targeted outreach to experts. We have built the critical community and design process and framework to support an open technical discussion. We have also developed the key language design ideas sufficiently to show what Carbon would look like clearly and unambiguously. To broaden or deepen the feedback, we will need frank and public discussion with wider groups of users and stakeholders. We will need to iterate with a larger community and a broader set of stakeholders. **The needs of the C++ community remain unmet, and they are eager** and asking for exactly Carbon's approach. The technical approach of Carbon directly addresses concrete and current requests from influential C++ developers. These needs are specifically not met by C or C++ as they exist or by Rust or other options, and we have a strategic opportunity to help drive a solution in this space. A recent example on Twitter: > Speaking as a C/C++ developer, when I write in C/C++, I am doing so due to > ecosystem/practical/legacy code constraints I have no control over, which > means that being given a superior choice makes no difference to me because I > didn't get a chance to choose C/C++ to start with > > -- [@mcclure111](https://twitter.com/mcclure111/status/1484987221136580610) A recent HackerNews discussion directly suggested essentially the direction we are exploring: > The permanent-ABI compatibility decision was effectively the death-knell for > C++. Can't improve the standard library performance, making it effectively > useless for the real world. Can't correct mistakes - the language and library > warts grow and grow as the years pass by, making it impossible for beginners > to pick up and learn the language. > > -- [lenkite](https://news.ycombinator.com/item?id=31227269) Last but not least, **the Carbon experiment will accelerate thinking about more dramatic ways to improve C++ across the industry**. ### Planned process for going public Our planned process is anchored around a few key principles: - Replicate LLVM's open-source approach and strategy. This has proven broadly successful over many years. - Use existing events and spaces where possible to engage with the C++ community and industry. - Emphasize that it's an experiment, not a finished product. - Leverage existing C++ ecosystems rather than creating new ecosystems from scratch. We expect three phases to move things to be public: - Get technical components ready: demo, documentation, etc. - Do an incremental "road show" with experts, organizations, and WG21 in weeks leading up to an official public announcement to seed early awareness. - Announce and make everything public at C++North. During the second phase, we plan to engage with the following individuals and groups: - Industry partners already active in C++ evolution. - Influential experts, especially those actively driving C++ evolution and prominently writing blog posts or speaking on C++. - Community organizations like #include and Boost. Our intent is to invite large portions of these groups to join community spaces to help engage and establish the community culture. The third phase will be oriented around a keynote at CppNorth: - Introduce the audience to the motivation for Carbon and its goals. - Explain the project, community, and process. - Walk through the core language design so far, and touch on exciting future efforts. - Provide an extended Q&A to help kick off active discussions, ideally with as many of the current Carbon contributors on stage as possible. - Will open the GitHub project _read_ ACLs either right before or during the talk so that the audience can explore during the Q&A and afterward. - Will open the _contribution_ ACLs and Discord Chat at the _end_ so that the maximum team bandwidth is available to engage with folks as they begin discussing. #### Public site and communications plan - Planning to use a GitHub project and GitHub hosted markdown documentation. - Work with existing C++ community blogs to post information about the project after announcement. We are specifically not planning on a website, blog, or other more "official" presence. This should anchor on Carbon being an experiment and attracting contributors and participants rather than users. #### Plan for ACLs once public GitHub will move to the normal for a public repository: - Anyone can create PRs, issues, discussions. This will be fully public. - A fairly small number of committers (initially the current set) can merge PRs. Discord will be fully public, using the community features to gate entry on CoC and CLA. The shared Google drive and docs: - Make viewable publicly. - Use the existing group (carbon-lang-contributors@googlegroups.com) to provide comment and edit access. - New contributors will need to request to join the group to begin contributing in this space, but only to avoid spam. This will be a very low-risk and trivially handed out access. Meetings and calendar: - As currently, the meeting links would be posted to Discord and anyone there can click through and join. - The shared calendar and weekly meeting can also be directly made available to the same group as the drive. - In the future, we can explore enabling live-streaming from the weekly meeting and providing a fully public live stream link for those interested. ### Risks and mitigations Making Carbon public does raise some significant risks that we need to be aware of and work to mitigate. #### Too many cooks in the kitchen - While opening up to the public, we may get a huge influx of feedback and even contributions. - This can quickly turn into a situation of too many folks all trying to drive Carbon forward in their own direction. Planned mitigations: - We're hoping that many of the critical parts of the language design we've prioritized have enough maturity to show a reasonably focused direction that folks should focus on to be useful. - We will likely work to avoid immediately reconsidering directions that are already well under way to stay focused, capturing any feedback and marking it as deferred for a time to revisit. - Critical aspects of the language design that are still green fields, we're hoping to clearly document as being intentionally deferred in part just to reduce the level of churn. - However, we can and should carefully watch for sustained contributors who are looking for an area to drive. _That_ needs to be identified and enabled. We just want to make sure it can be a focused effort. - We should make an extra effort to use the more formal process of raising questions for the Carbon Leads to direct the incoming flow. That process is set up in a way that should enable it to queue and process things without ending up in total chaos. - Where possible, teams working on Carbon should start making contingency plans to pull in more resources if needed to sustain the engagement. #### Community management overload - We may have a sudden influx of community members, with all of them trying to figure out how to collaborate and communicate effectively. This may even include publicity and other factors. - Responding to and engaging with this will be both time consuming, and difficult. - It will be tempting for the Carbon Leads to attempt to drive these responses, in turn starving other parts of the project. Planned mitigations: - Provide early access to a growing set of people, especially those interested and effective and helping with community and communication. - Contract with a dedicated community engagement specialist. - Work with them to recruit moderation teams and other scalable groups to tackle these issues. - Leverage moderation scaling tools across our communication platforms to keep the technical work focused and moving. #### Added distraction or confusion to the C++ evolution process - Carbon is a very different direction from the continued C++ evolution in WG21. - Bringing the C++ community into Carbon and getting them engaged and participating will at least reduce the available resources for WG21 in the C++ community. - The existence, and especially any excitement around such a different approach may distract from important work that still needs to get done in WG21. Planned mitigations: - Carbon's documentation and all of our public communication should include a key message: **If C++ fully meets your needs today, you should keep using it.** Carbon is _only_ exploring a direction to address specific concerns and problems some users have with C++ today. For other users whose needs are fully met, they should stay focused on the thing that exists and works today: C++. - Carbon is building on top of the C++ ecosystem, and we need to be very clear that means it is important to not distract from critical work that needs to happen there. We should be active and vocal in supporting folks who are continuing to drive WG21 forward. - We need to be extremely explicit and clear at every stage that Carbon is still an experiment that may not work out. - We do not want critical work that needs to happen for C++ and WG21 to stay healthy to be diverted towards Carbon. - We plan to engage with committee members actively so they can learn as much as possible from the Carbon experiment and incorporate any and all of our ideas into C++ where they see a path to do so. #### Added distractions from existing new programming languages. - Another programming language in the world might dilute some of the efforts going towards new and exciting but existing languages, especially ones with similar performance goals such as Rust. - It may also distract users who are unsure of which language to use. Planned mitigations: - Throughout Carbon's documentation and any public communication, we will take a specific stance to avoid and minimize this effect: - **If you can use an existing language like Go, Kotlin, or Rust, do so.** This experiment is exploring an option for when existing languages _don't_ work, particularly due to needing to interoperate with a large body of C++ code. - We should be careful in the tone whenever discussing existing languages or comparing with them, to approach them positively. We should be actively supporting other languages, and our focus should be trying to fill a _gap_ in that ecosystem rather than directly competing. #### Friction with existing LLVM and Clang communities While somewhat unlikely, we don't want to create unnecessary friction with the LLVM and especially the Clang community, as they have heavily invested in a C++ compiler and tooling stack and supporting the C++ language. Planned mitigations: - Need to carefully emphasize that we are build on top of the tremendous work done by the LLVM and Clang communities. - LLVM is a critical part of our ability to make a high-performance implementation and have no performance overhead when interoperating with C++ by using a shared compilation environment. - Clang is the key to our C++ interoperability implementation plans -- without a production quality frontend with broad cross-platform support it would be impossible to achieve the level of interop we need between C++ and Carbon. - Many of the insights that have led to Carbon's design come from experimentation with Clang and C++ extensions, as well as our experience working on the LLVM and Clang codebases themselves. - We will clearly and publicly drive improvements to LLVM and Clang upstream when needed for Carbon rather than carrying local patches for long durations. - We will also actively engage members of the LLVM community in the Carbon experiment and ensure they are able to participate where interested. - Lastly, all Carbon code uses the same license as LLVM so that any of it can be immediately merged into LLVM if useful to the broader project or community. #### Labeled as vaporware - We are intentionally moving public while Carbon is still very nascent. - Many parts of the language still need to be built. - We are front-loading design work heavily, which means we have designed significantly more than is implemented. Planned mitigations: - Our communication around Carbon will be focused around _building_ the Carbon experiment to emphasize both that it is _not_ yet even a complete experiment, and that our goal is to get help shaping it rather than suggest it is ready for evaluation. - We also plan to have clear roadmaps and other artifacts that help give a realistic and credible story around the path from where Carbon is to being a more complete experiment. ## Rationale - [Community and culture](/docs/project/goals.md#community-and-culture) - Becoming a fully open project has always been a goal for the project. - Moving public will substantially broaden the feedback we get, strengthening our community and directly furthering our goal of being an welcoming and inclusive community. - [Interoperability with and migration from existing C++ code](/docs/project/goals.md#interoperability-with-and-migration-from-existing-c-code) - We need to ensure we can actively engage with users of different C++ codebases to effectively build a compelling interoperability and migration story. This kind of broad engagement is only possible with a public project. ## Alternatives considered ### Delay going public until we have working interop demonstrated An appealing time to go public would be when we have a toolchain that implements the core language design _and_ provides a demonstration of the banner feature of Carbon -- C++ interop -- working effectively. If we made this our top priority, we expect we could complete this in the order of a year. Advantages: - Make the project much more "real" when people first learn of it, and avoid the perception of it being vaporware. - Avoids spending significant time and effort engaging with a large community and industry when the project is new and small. That effort could be directed at furthering the design and implementation. Disadvantages: - It would be difficult or impossible to expand the group working on Carbon significantly, which will at some point limit the rate at which we can execute on the project. - We would be have significantly less broad input on the design of Carbon itself and especially of its C++ interop, risking that the design would not actually address the needs of the broader industry. - We wouldn't be able to build Carbon in the open and develop trust of the community and industry. - We wouldn't be able to involve the broader community and industry in the design and development of Carbon, which might lessen both interest and enthusiasm. - We won't start to understand how broadly the industry is interested in the Carbon direction until significantly more effort has been invested. The desire to build Carbon in the open, develop strong trust with the community, and understand the breadth of industry interest outweigh the costs and risks of making Carbon public earlier. ### Delay going public until Carbon is a compelling option for C++ developers to adopt We could delay still further and build Carbon into a compelling and ready-to-use programming language before going public. Advantages: - This would in some ways be the most _efficient_ strategy, as it would keep everyone working on Carbon focused without distraction. - We would have the best possible first impression on developers by having a largely complete and high quality language ready to go. Disadvantages: - We still wouldn't know whether the experiment was a success -- both developers and the industry at large might not be interested in Carbon. The result is that this would have the maximum sunk cost at the point where we begin to learn about the industry interest. - It is unlikely we could sustain the effort required to reach this point without any incremental milestones. - We would likely have very little trust from the community due the prolonged secrecy. - Given the timeline, it is very likely that Carbon would leak and lose some of the advantages. - This would maximize the risk of some aspect of Carbon being designed in a way that doesn't meet the larger industry needs due to a lack of feedback. It also maximizes the cost of correcting this once discovered. This has never been seen as a realistic approach for Carbon, and that doesn't seem to have changed. While it does have some advantages, the tradeoff seems sharply wrong for the needs of the Carbon project. ================================================ FILE: proposals/p1367.md ================================================ # Remove CODEOWNERS [Pull request](https://github.com/carbon-language/carbon-lang/pull/1367) ## Table of contents - [Problem](#problem) - [Background](#background) - [Current CODEOWNERS](#current-codeowners) - [CODEOWNERS issues](#codeowners-issues) - [Proposal](#proposal) - [Details](#details) - [Group access controls](#group-access-controls) - [rm CODEOWNERS](#rm-codeowners) - [Auto-assignment](#auto-assignment) - [Stacked PRs](#stacked-prs) - [Rationale](#rationale) - [Alternatives considered](#alternatives-considered) - [Fine-grained CODEOWNERS](#fine-grained-codeowners) - [Broaden CODEOWNERS](#broaden-codeowners) ## Problem We want to restrict who can merge files, in particular to ensure reviews are done. Currently we use CODEOWNERS for this purpose. However, CODEOWNERS can lead to review bottlenecks, creating possibly unnecessary hindrances to development, as discussed in the background. ## Background ### Current CODEOWNERS We currently use, in brief: ```CODEOWNERS * @carbon-language/implementation-team /* @carbon-language/implementation-team /*.md @carbon-language/carbon-leads @carbon-language/implementation-team /docs/ @carbon-language/carbon-leads @carbon-language/implementation-team /proposals/ @carbon-language/carbon-leads /LICENSE @carbon-language/carbon-leads CODEOWNERS @carbon-language/carbon-leads ``` ### CODEOWNERS issues [Issue #413: Consider removing CODEOWNERS -- at least for now](https://github.com/carbon-language/carbon-lang/issues/413) was used to discuss this issue. This can lead to a few issues: - CODEOWNERS defaults to assignment to the group, which can lead to a "tragedy of the commons" situation where everybody expects someone else to review. - In assigning to the group, CODEOWNERS also makes for noisy PR notifications. - Note both of these issues can be addressed with [GitHub review settings](https://docs.github.com/en/organizations/organizing-members-into-teams/managing-code-review-settings-for-your-team). - CODEOWNERS doesn't accurately handle the difference between a "major" change and a "minor" change. - For example, a new proposal should only be approved by carbon-leads, while a typo fix to a proposal would be fine for anyone to approve. - Decisions to make minor proposal edits more common, as in [Issue #1009: Should doc edits maintain proposal links?](https://github.com/carbon-language/carbon-lang/issues/1009), exacerbate this issue. ## Proposal Remove CODEOWNERS, and instead use commit access to gate PRs. ## Details ### Group access controls - Base: read (unchanged) - Should be the standard public permission. - Contributors: "contributor" (previously write) - The "Contributors" group should be easy to get access to. On top of read, this should include the ability to add/remove labels and projects (may require some work to get right). - Versus "triage", "triage" seems to have powers including deleting conversations, etc, that may not make sense for this group. - Implementation team: write (unchanged) - For people expected to be reviewing commits. - Moderators: triage - Includes [separate GitHub moderation permissions](https://docs.github.com/en/communities/moderating-comments-and-conversations). - Leads: maintain (unchanged) - Admin team: admin (unchanged) For people who have access, settings are at: - [Organization repository roles](https://github.com/organizations/carbon-language/settings/roles) - [Organization moderators](https://github.com/organizations/carbon-language/settings/moderators) - [Repository collaborators and teams](https://github.com/carbon-language/carbon-lang/settings/access) ### rm CODEOWNERS This should only be done after group access controls are updated. Once done, group access controls are the last word on who can commit PRs. ### Auto-assignment This PR [introduces auto-assignment](https://github.com/carbon-language/carbon-lang/pull/1367/files#diff-5e5db53c34ec04ba9da95c70d5c797f1be9f390f7e2bf18ca3149cbd99c277df) in order to ensure PRs aren't lost. It provides categories of assignment, and a fallback for other PRs that don't have explicit assignment. ### Stacked PRs Advice for stacked PRs is no longer "just ask an admin"; it is "commit access required". This is because removing CODEOWNERS removes boundary between merge access _to a feature branch_ and merge access _to trunk_. ## Rationale - [Community and culture](/docs/project/goals.md#community-and-culture) - The intent is to make it easier to get PRs reviewed and reduce bottlenecks. ## Alternatives considered ### Fine-grained CODEOWNERS We could keep the [current fine-grained CODEOWNERS](#current-codeowners). In this setup, we would try to have specific directories owned by specific teams. We've ended up on this path so far so that we have some mechanical enforcement of who's reviewing. We may need to change up the groups a little, but a reasonable long-term expectation would be something like a docs team, an explorer team, a toolchain team, etc, with each owning their own files. For example, /docs is owned only by the docs team, and there are people in the docs team who aren't in other teams (and the other way around). As previously mentioned, if we were using this option, we may want to change [GitHub review settings](https://docs.github.com/en/organizations/organizing-members-into-teams/managing-code-review-settings-for-your-team) in order to fix email/assignment. Advantages: - Relies on built-in GitHub CODEOWNERS featureset, intended for this purpose. - Can use built-in features for review assignment. - Works with current stacked PR advice. Disadvantages: - Retains team-specific bottlenecks: if none of a directory's owners are available, other contributors cannot step in. - This could also be considered to be an advantage by some. - GitHub's built-in features for review assignment are not as feature-rich as some custom tooling. It's mainly because of the relative inflexibility of CODEOWNERS that we aren't doing this. ### Broaden CODEOWNERS We could broaden the CODEOWNERS to something basic, like a single group owning everything. Reviews in a particular area become best practice, not mechanically enforced. Advantages: - Simple to understand. - Avoids custom tooling. Disadvantages: - We expect multiple subprojects within the carbon-lang repository, and this approach would make it difficult to determine when to review a PR. - A large group means GitHub's built-in auto-assignment would assign to arbitrary CODEOWNERS. - The [notification issues](#codeowners-issues) couldn't be addressed. This option is likely worse for our purposes than [fine-grained CODEOWNERS](#fine-grained-codeowners). ================================================ FILE: proposals/p1382.md ================================================ # Rename `me` -> `self` [Pull request](https://github.com/carbon-language/carbon-lang/pull/1382) ## Table of contents - [Problem](#problem) - [Background](#background) - [Proposal](#proposal) - [Rationale](#rationale) - [Alternatives considered](#alternatives-considered) - [Don't change anything](#dont-change-anything) - [`this`](#this) ## Problem We've tried the `fn MethodName[me: Self]()` syntax for a while, and from our experience the brevity of `me` is not worth doing something novel in this space. We have found that `me` doesn't read well in practice. ## Background The current method syntax, including these choices, was decided in questions-for-leads issue [#494: Method syntax](https://github.com/carbon-language/carbon-lang/issues/494) and implemented in proposal [#722: Nominal classes and methods](https://github.com/carbon-language/carbon-lang/pull/722). Looking at other languages that use reserved word for the receiver value: | When | Language | Receiver when
accessing members | Receiver value | Receiver type | | ---------- | ---------- | ------------------------------------ | -------------- | ------------- | | 1983 | C++ | implicit | `this` | --- | | 1991 | Python | explicit | `self` | --- | | 1995 | Java | implicit | `this` | --- | | 1995 | JavaScript | explicit | `this` | --- | | 2000 | C# | implicit | `this` | --- | | 2009 | Go | explicit | (see below) | --- | | 2010 | Rust | explicit | `self` | `Self` | | 2011 | Kotlin | implicit | `this` | --- | | 2012 | TypeScript | explicit | `this` | `this` | | 2014 | Swift | implicit | `self` | `Self` | | previously | Carbon | explicit | `me` | `Self` | | proposed | Carbon | explicit | `self` | `Self` | In detail: - C++ uses [`this` for address of the receiver value](https://en.cppreference.com/w/cpp/language/this). C++23 includes [an explicit `this` facility](https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2021/p0847r7.html). Examples in the proposal frequently use `self` as the name of the parameter, and `Self` as its type. - Swift uses [`self` for the receiver value](https://docs.swift.org/swift-book/LanguageGuide/Methods.html#ID238). and [`Self` for its type](https://docs.swift.org/swift-book/ReferenceManual/Types.html#ID610). - Rust uses [`self` for the receiver value](https://doc.rust-lang.org/std/keyword.self.html) and [`Self` for its type](https://doc.rust-lang.org/rust-by-example/fn/methods.html). - C# uses [`this` for a reference to the receiver value](https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/this). - Python as a convention uses `self` for the receiver value, but it's almost universally followed. - Go conventionally uses an abbreviation of the type name. Some history about the use of `self` in programming languages is documented in [this StackOverflow answer](https://stackoverflow.com/a/1080192/624900), including that `self` is also used in Smalltalk, Modula-3, Delphi/Object Pascal, and Objective-C. ## Proposal Use `self` instead of `me` to be consistent with Swift and Rust. ## Rationale This is consistent with Carbon's goal to make [Code that is easy to read, understand, and write](/docs/project/goals.md#code-that-is-easy-to-read-understand-and-write) by choosing a keyword for this role that is less surprising to users. ## Alternatives considered ### Don't change anything We could stay with the status quo, which has the benefit that `me` is shorter than `self`. There are two considerations: - For accessing members of the current object, the chart in [the background section](#background) shows plenty of precedent for requiring a 4 character explicit keyword. - We would also like to reduce ceremony when declaring the signature of a method. For this concern, both `me: Self` and `addr me: Self*` are already longer than what other languages use in practice. It would probably be better to solve this problem with a shortcut approach like Rust ([1](https://doc.rust-lang.org/book/ch05-03-method-syntax.html), [2](https://doc.rust-lang.org/rust-by-example/fn/methods.html)), where `&self` is short for `self: &Self` and `&mut self` is short for `self: &mut Self`. ### `this` We could also switch to `this`, primarily to benefit [C++ users](https://en.cppreference.com/w/cpp/language/this). This had a few disadvantages: - We are worried that it frequently not being a pointer would be surprising to those C++ users. - As noted in [the background section](#background), C++23 code using explicit this frequently uses the name `self`. - We view it as an advantage to use the same spelling for the variable `self` as for the type `Self`, and while `this` might make an acceptable name for the object parameter, `Self` is much more strongly established as the name for the current class, for example in PL research, and there is no precedent for a type named `This`. ================================================ FILE: proposals/p1885.md ================================================ # `for` statement and user types [Pull request](https://github.com/carbon-language/carbon-lang/pull/1885) ## Table of contents - [Abstract](#abstract) - [Problem](#problem) - [Background](#background) - [Other languages](#other-languages) - [C++](#c) - [Python](#python) - [Rust](#rust) - [Typescript](#typescript) - [Go](#go) - [Proposal](#proposal) - [Details](#details) - [`for` with immutable values](#for-with-immutable-values) - [`Iterate` interface](#iterate-interface) - [`let` by default](#let-by-default) - [Lifetime of rvalues on right-hand side of `:`](#lifetime-of-rvalues-on-right-hand-side-of-) - [Use cases](#use-cases) - [Mutable and immutable elements](#mutable-and-immutable-elements) - [Random access containers](#random-access-containers) - [Maps, hash maps, and other containers](#maps-hash-maps-and-other-containers) - [R-value containers](#r-value-containers) - [Synthetic data](#synthetic-data) - [Large containers](#large-containers) - [Filtering, transforming, and reversing](#filtering-transforming-and-reversing) - [Interoperability with C++ types and containers](#interoperability-with-c-types-and-containers) - [Future work](#future-work) - [`for` with mutable values](#for-with-mutable-values) - [Speculative mixin usage](#speculative-mixin-usage) - [Mutable values](#mutable-values) - [Alternative views](#alternative-views) - [Large elements and Optional](#large-elements-and-optional) - [C++ interoperability](#c-interoperability) - [Iterating over C++ types in Carbon](#iterating-over-c-types-in-carbon) - [Iterating over Carbon types in C++](#iterating-over-carbon-types-in-c) - [Inversion of control](#inversion-of-control) - [Rationale](#rationale) - [Alternatives considered](#alternatives-considered) - [Atomic methods for `Iterate`](#atomic-methods-for-iterate) - [Using an iterator instead of a cursor](#using-an-iterator-instead-of-a-cursor) - [Support getter for both `T` and `T*` with `Iterate`](#support-getter-for-both-t-and-t-with-iterate) ## Abstract This proposes an interface that can be implemented by user types to support range-based iteration with `for`. ## Problem The current `for` proposal does not define a way for types to support range-based `for` loops. Examples of use cases for `for` loops include iterating over: - Mutable and immutable elements - Random access containers - Maps, hash maps, and complex containers - R-value containers - Synthetic data - Large containers, and large elements - Filtering, transforming, and reversing - Interoperability with C++ types and containers The goals for the solution includes: - Easy to support for user types, with minimal requirements - Simple and clear usage - Minimal performance overhead ## Background The original proposal of `for` loops were discussed in [p0353](/proposals/p0353.md), and the basic design documented in [control_flow/loops#for](/docs/design/control_flow/loops.md#for). The resulting syntax was: > `for (` _var declaration_ `in` _expression_ `) {` _statements_ `}` We now need a way to allow supporting this syntax for user and library container types. ### Other languages This section highlights some examples of how iterators and range-based for user-defined types is addressed in different languages. #### C++ C++ requires either `.begin()`/`.end()` methods, or `begin()`/`end()` to be supported for [range-based `for`](https://en.cppreference.com/w/cpp/language/range-for). #### Python Python requires 2 dunder methods, `__iter__` on the container and `__next__` on the iterator, to be usable with a `for in :` construct. [Generator functions](https://peps.python.org/pep-0255/) act like iterators. They are executed until the next `yield` statement, whose argument is returned as the next value to the `for` loop. Iteration ends when the function returns. ```python def generate_fibonacci(): yield 0; n1, n2 = 0, 1; while True: n1, n2 = n2, n1+n2; yield n1; for n in generate(): print(n); ``` #### Rust Rust supports iterating over a container with [for](https://doc.rust-lang.org/rust-by-example/flow_control/for.html) using - ranges `for n in 1..100` - iterators `for element in container.iter()` or `.iter_mut()` The `Iterator` type can be used with custom types using the [following syntax](https://doc.rust-lang.org/rust-by-example/trait/iter.html) (though specific): ```rust struct Fibonacci { curr: u32, next: u32, } impl Iterator for Fibonacci { type Item = u32; fn next(&mut self) -> Option { // calculate and yield value [...] } } for i in fibonacci().take(4) { ... } ``` #### Typescript Typescript uses the [`.iterator` property](https://www.typescriptlang.org/docs/handbook/iterators-and-generators.html#iterables), along with [two forms](https://www.typescriptlang.org/docs/handbook/iterators-and-generators.html#forof-statements) of range-based for loops: `for..in` and `for..of`. ```ts let pets = new Set(['Cat', 'Dog', 'Hamster']); pets['species'] = 'mammals'; for (let pet in pets) { console.log(pet); // "species" } for (let pet of pets) { console.log(pet); // "Cat", "Dog", "Hamster" } ``` Using the `.iterator` property, Typescript allow to lazily generate data easily using functions, classes, or objects: ```ts const reverse = (arr) => ({ [Symbol.iterator]() { let i = arr.length; return { next: () => ({ value: arr[--i], done: i < 0, }), }; }, }); ``` #### Go Go does not officially support customizing `for` for user types. ## Proposal Provide interfaces that can be implemented by user types to enable support for ranged-for loops. We propose: - A new `Iterate` interface to support for loops - Using a cursor-based approach - Making the `for` loop value `let` by default, that is, non-reassignable Below is a high-level example of this concept: ```carbon // Add support for `for` class MyRange { impl as Iterate where .ElementType = i32 and .CursorType = i32 { // Implement `Iterate` } } // Usage let my_range: MyRange = {...}; for (item: auto in my_range) { // Do something with `item` } ``` ## Details ### `for` with immutable values #### `Iterate` interface This proposal exposes a new `Iterate`. This interface: - Relies on a cursor-based approach: minimize implementation effort for developers, could support R-values. - Expects an `ElementType` and `CursorType`. - Combines "advance", "get", and "bounds check" into a single `Next(cursor)` method that returns an optional value. The `Iterate` interface is: ```carbon interface Iterate { let ElementType:! type; let CursorType:! type; fn NewCursor[self: Self]() -> CursorType; fn Next[self: Self](cursor: CursorType*) -> Optional(ElementType); } ``` A naive Carbon implementation of the for loop could be: ```carbon var cursor: range.(Iterate.CursorType) = range.(Iterate.NewCursor)(); var iter: Optional(range.(Iterate.ElementType)) = range.(Iterate.Next)(&cursor); // A. Possible implementation while (iter.HasValue()) { ExecuteForBlock(iter.Get()); iter = container.(Iterate.Next)(&cursor); } // B. Possible alternative depending on the API for Optional(T): while (true) { match (iter) { case .None => { break; } case .Some(let value: range.(Iterate.ElementType)) => { ExecuteForBlock(value); iter = container.(Iterate.Next)(&cursor); } } } ``` The cursor tracks progression, and the `Next` method advances the cursor and returns an `Optional` value. An empty `Optional` indicates that we have reached the end. Below is a sample implementation of that interface for a user type. ```carbon class MyIntContainer { var data: ... fn Size[self: Self]() -> i32 { return 4; } impl as Iterate where .ElementType = i32 and .CursorType = i32 { fn NewCursor[self: Self]() -> i32 { return 0; } fn Next[self: Self](cursor: i32*) -> Optional(i32) { // Advance and return value, or return empty if (*cursor < self.Size()) { let index: i32 = *cursor; *cursor = index + 1; return Optional(i32).Create(self.data[index]); } else { return Optional(i32).CreateEmpty(); } } } } ``` Which can be used as follow: ```carbon fn Main() -> i32 { var container: MyIntContainer = {.data = (1, 2, 3, 4)}; // Implicit `let` value declaration for (value: i32 in container) { Print("{0}", value); } return 0; } ``` #### `let` by default To keep usage simple and non-surprising, we propose defaulting variable declarations to `let` declarations. This is consistent with function parameters and scrutinees for [pattern matching](https://github.com/carbon-language/carbon-lang/pull/2188), which are immutable by default. ```carbon // Implicitly `let item: T` for (item: T in my_range) { // `item` cannot be reassigned } ``` This default can be overridden by adding the `var` keyword: ```carbon for (var item: T in my_range) { // `item` can be modified, but this will not modify `my_range` // because it is a copy of the element with its own storage } ``` #### Lifetime of rvalues on right-hand side of `:` It will be important to ensure that temporary entities on the right-hand side of `:` are alive during the execution of the `for` loop to prevent invalid memory access (Note: this was only recently addressed for C++ by [P2644](https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2022/p2644r0.pdf)). This applies to Carbon and C++ interoperability. ```carbon // Example using a temporary range, with `GetElements()` returning an object that implements `Iterate` for (item: T in bucket.GetElements()) { // We need to make sure the object is alive for the duration of the `for` loop } ``` ### Use cases This section covers the use cases highlighted in the introduction, and usage with the proposed design. #### Mutable and immutable elements This proposal covers strictly immutable elements, and proposes defaulting element declaration to `let`, to minimize surprises and clarify the intent. Mutable elements are mentioned in the [Future work](#future-work) section. #### Random access containers For random access containers, the cursor would represent a numerical index, incremented within `Next()`. #### Maps, hash maps, and other containers Because the cursor type is defined by the developer, and opaque to the user, it could be a hashmap index, a string, or pointer to a node, without changing the usage for users. The `ElementType` can be a tuple, such as a `(key, value)` for maps, or a single value. See [Future work][#future-work] for other examples. #### R-value containers R-values are likely to be limited in terms of addressing and mutation. The impact is undefined until the [corresponding design](https://github.com/carbon-language/carbon-lang/pull/2006) is finalized. The cursor approach used for `Iterate` has the slight advantage of not requiring pointers (and addressing) for some container types. This may be beneficial as a first iteration, until the dependent design is updated, at which point we will have to define how to handle this special case. #### Synthetic data `Next()` can be trivially implemented to return a single computed value on-demand based on the cursor, without the need for storage. If the sequence is infinite, `Next()` will never return an empty `Optional`, and the `for` loop will continue until interrupted by the user. ```carbon class SquaredSequence { impl as Iterate where .ElementType = i32 and .CursorType = i32 { fn NewCursor[self: Self]() -> i32 { return -1; } fn Next[self: Self](cursor: i32*) -> Optional(i32) { // Advance and return computed value *cursor = *cursor + 1; return Optional(i32).Create((*cursor) * (*cursor)); } } } ``` #### Large containers The proposal does not present any limitation regarding large collections. The cursor can be adapted to the collection size, and no copy of the container should occur both for immutable collections, and future mutable elements. #### Filtering, transforming, and reversing Provided the traversal order is the same, generic filters and transformers are supported by implementing a proxy `Iterate` interface, internally or externally. This example illustrates a simple proxy interface. ```carbon class BasicFilter(T:! Iterate) { var seq: T*; var value: T.ElementType; impl as Iterate where .ElementType = T.ElementType and .CursorType = T.CursorType { fn NewCursor[self: Self]() -> T.ElementType { return self.seq->NewCursor(); } fn Next[self: Self](cursor: T.CursorType*) -> Optional(T.ElementType) { var res: auto = self.seq->Next(cursor); while (res.has_value() && res.get() == value) { res = self.seq->Next(cursor); } return res; } } } fn Main() -> i32 { let container: MyIntContainer = MyIntContainer.Create(0,1,0,2,3,0); let no_zeros: BasicFilter(MyIntContainer) = { .seq = &container, .value = 0 }; // Prints "123" for (v: i32 in no_zeros) { Print("{0}", v); } return 0; } ``` On the other hand, changing how the collection is traversed (for example reverse), requires internal knowledge about the data layout, and a custom implementation. One approach for containers that support reverse iteration would be to implement a different interface with a `Prev()` method. A proxy could be exposed to make it usable with `for` directly. Below is an example of what such an interface could look like: ```carbon interface IterateBidirectional; class ReverseProxy(T:! IterateBidirectional); interface IterateBidirectional { extends Iterate; fn NewEndCursor[self: Self]() -> CursorType; fn Prev[self: Self](cursor: CursorType*) -> Optional(ElementType); final fn Reversed[self: Self]() -> ReverseProxy(Self); } class ReverseProxy(T:! IterateBidirectional) { var container: T; impl as Iterate where .ElementType = T.ElementType and .CursorType = T.CursorType { fn NewCursor[self: Self]() -> CursorType { return self.container.NewEndCursor(); } fn Next[self: Self](cursor: CursorType*) -> Optional(ElementType) { return self.container.Prev(cursor); } } } fn IterateBidirectional.Reversed[self: Self]() -> ReverseProxy(Self) { return {.container = self}; } /// Usage class MyContainer { fn Create(...) { ... } impl as Iterate ... { ... } impl as IterateBidirectional ... { ... } } var container: MyContainer = MyContainer.Create(...); for (v: container.(Iterate.ElementType) in container.Reversed()) {...} ``` #### Interoperability with C++ types and containers The cursor approach deviates from the C++ iterator approach, requiring some adaptation. Given the current state of the design, C++ interoperability is yet to be defined, and suggestions have been highlighted in the [Future work](#future-work) section. ## Future work ### `for` with mutable values Supporting mutable values could be achieved using a second interface that provides a proxy or view for the data, exposing an `Iterate` interface with `ElementType*` ```carbon // Object implementing `MutableIterate` returned by a function for (p: T* in my_range.AddrRange()) {} ``` The example `MutableIterate` interface below acts as a proxy for the collection, and implements our `Iterate` interface. ```carbon interface MutableIterate { impl as Iterate; alias ElementType = Iterate.ElementType; let ProxyType:! Iterate where .ElementType = ElementType* and .CursorType is ImplicitAs((Self as Iterate).CursorType); fn AddrRange[addr self: Self*]() -> ProxyType; } class MyIntContainer { //... impl as Iterate ... external impl as MutateIterate where .ProxyType = Self { ... } } ``` ### Speculative mixin usage Mixins are currently in early design stages. This section highlights possible uses speculating on the final design. Some may include: - Improved semantics for views compared to getter methods: no direct side effect from using the view - Facilitate code reuse, compared to reimplementing an interface - Direct access to `self`, limiting needs for pointers and address resolution #### Mutable values Named mixins, used as programmable members, could expose a view with mutable values: ```carbon // Using a named mixin to expose mutable values for (p: my_range.(Iterate.ElementType)* in my_range.mutable) {} ``` Used in this way, this clarifies that this a view of the data, like an attribute, with no direct side effects, when compared to a method. #### Alternative views Similarly, mixins could allow supporting other views into the container. For example, maps could expose a view with "key" and "value" only, in addition to the default (key, value) tuple. ```carbon // Using a named mixin to expose mutable values for (k: my_dict.(Iterate.CursorType) in my_dict.keys) {} ``` And "reversed" view could also be used for reverse iteration: ```carbon // Reversing for (v: my_dict.(Iterate.ElementType) in my_dict.reversed) {} ``` ### Large elements and Optional Large elements would have a negative impact if the `Optional` cannot leverage unused bit patterns within the type, nor present a view instead of a copy. If this proves difficult, we can let implementers of the container choose whether to expose values or pointers to values. When electing to "pointers to values", an adapter can be provided to support transforming the range of pointers into a range a values. This choice allows supporting either iteration over container r-values (`ElementType = T`), or efficient iteration (`ElementType = T*`), but not both. While acceptable as an intermediate solution, a better alternative will be needed in the future. ```carbon adapter IterateByValue(T:! Iterate where .CursorType impls Deref) for T { impl as Iterate where .CursorType = T.CursorType and .ElementType = T.CursorType.(Deref.Result) { ... } } // Used like: var frames: NetworkFramesContainerByPtr = ... for (i: NetworkFrame in frames as IterateByValue(NetworkFramesContainerByPtr)) { ... } ``` Alternatively, we can allow the binding in the `for` loop to be an `addr` pattern, implemented by calling into a separate interface on the container. This puts the choice of using values or pointers to values in the hands of the `for` loop author. While this deviates from the ergonomics of `let` (where the compiler can choose to copy or alias the value depending on what is more efficient), this option would provide parity with C++ `for` loops (where the author chooses between value and reference). ### C++ interoperability #### Iterating over C++ types in Carbon For reference, range-based `for` loops in C++ requires: - `begin()` and `end()` methods or free functions, and - the type returned supports pre-increment `++`, indirection `*`, and inequality `!=` operations See [range-based for statement](https://eel.is/c++draft/stmt.iter#stmt.ranged) for more details. A limitation of the approach proposed in this document is the need for adaptation to interoperate with the C++ iterator model. The adapter would be a Carbon type that satisfies the Cursor interface, implemented in terms of a C++ iterator. - `NewCursor` providing the "start iterator" returned by `begin()` - `Next` doing the "increment, bounds check, and returning value" Two different approaches are identified: - One is to have a templated `impl` of `Iterate`, for anything with the right method signatures (`begin`, `end`, and so on) - Another is a templated adapter that implements `Iterate` when requested ```carbon // Template impl approach template constraint CppIterator { ... } template constraint CppContainer { // Speculative for_unique IteratorType: CppIterator; fn begin() -> IteratorType; fn end() -> IteratorType; } external impl forall [template T:! CppContainer] T as Iterate where .CursorType = T.(CppContainer.IteratorType) and .ElementType = .CursorType.ElementType { fn NewCursor[self: Self]() -> CursorType { return self.(CppContainer.begin)(); } fn Next[self: Self](cursor: CursorType*) -> Optional(ElementType) { if (*cursor == self.(CppContainer.end)()) { return Optional(ElementType).MakeEmpty(); } let value: ElementType = **cursor; ++*cursor; return Optional(ElementType).MakeValue(value); } } // Used like: var v: Cpp.std.vector(i32) = ... for (i: i32 in v) { ... } ``` ```carbon // Adapter approach adapter CppIterate(template T:! type) for T { impl as Iterate // Speculative syntax. Alternatively, a cursor type parameter // would avoid the need for deduction. where .CursorType = typeof(T.begin()) and .ElementType = .CursorType.(Deref.Result) { fn NewCursor[self: Self]() -> CursorType { return self.begin(); } fn Next[self: Self](cursor: CursorType*) -> Optional(ElementType) { if (*cursor == self.end()) { return Optional(ElementType).MakeEmpty(); } let value: ElementType = **cursor; ++*cursor; return Optional(ElementType).MakeValue(value); } } } // Used like: var v: Cpp.std.vector(i32) = ... for (i: i32 in v as CppIterate(Cpp.std.vector(i32))) { ... } ``` These two options could allow interoperability with a compatible C++ type, either implicitly (template approach) or explicitly (adapter approach). #### Iterating over Carbon types in C++ There need to be `begin()` and `end()` methods or free functions accessible to C++ for any Carbon type that implements the `Iterate` interface, in addition to overloads of the `*`, `++`, and `!=` operators for the types returned. The `Iterate` interface does not expose a way to have a cursor nor an iterator to the past-last element. An option would be to have `end()` return a predefined invalid value, to satisfy iterator comparison when `Next()` returns an empty `Optional`. Below is sample implementation to iterate on a Carbon `Iterate`. Note that we need to copy and cache the last value, to allow for `*it` to be called. This puts a requirement on `ElementType` to be copyable for this interoperability to be usable. ```cpp namespace Carbon { // A C++ input iterator for any type that implements the Carbon `Iterate` interface. template class IterateIterator { public: // Returns an "Invalid" iterator representing `end()` static auto Invalid(const Iterate& container) -> IterateIterator { return IterateIterator(container, std::nullopt); } IterateIterator(const Iterate& container, std::optional cursor) : container_(&container), cursor_(cursor) { if (cursor_) { next(); } } IterateIterator(const IterateIterator& other) : container_(other.container_), cursor_(other.cursor_), value_(other) {} auto operator*() const -> const typename Iterate::ElementType& { assert(cursor_); return value_; } auto operator++() -> IterateIterator& { next(); return *this; } auto operator==(const IterateIterator& rhs) -> bool { return container_ == rhs.container_ && cursor_ == rhs.cursor_; } auto operator!=(const IterateIterator& rhs) -> bool { return !(*this == rhs); } private: void next() { assert(cursor_); if (const auto v = container_->Next(*cursor_); v) { value_ = *std::move(v); } else { cursor_ = std::nullopt; } } const Iterate* container_; std::optional cursor_; typename Iterate::ElementType value_; }; // The C++ side of a custom Carbon type implementing // `Iterate` with CursorType `C`, and ElementType `T` // could have `begin()` and `end()` methods defined as: class MyIterateType { // [...] auto begin() -> IterateIterator> { return IterateIterator>(*this, NewCursor()); } auto end() -> IterateIterator> { return IterateIterator>::Invalid(*this); } }; } // namespace Carbon // Usage: Carbon::MyIterateType container; for (auto s : container) { /*...*/ } ``` ### Inversion of control Using an `Optional` has downsides, discussed in the ["alternatives considered"](#alternatives-considered) section. A possible approach would be to invert the control of the `for` loop, by having the container call in the `for` block for each value, passing the value as argument. This would work similarly to [Python generator functions](#python). This has the following advantages: - Removes the need for an `Optional`, and the associated overheads, copies, and unwrapping. - No boundary checks needed at the `for` level - Compatible with R-value containers Limitations: - The context needed by the `for` body will needs to be available or provided - No optimizer known to the team can reliably produce efficient code for this construct. ## Rationale This proposal offers a first step to support a needed way to iterate using a `for` loop. More specifically: - Requires a single interface to be implemented with a combined `Next` method, uses cursors instead of their more complex iterator counterpart, and defines loop values as `let` by default to be less error prone ([Code that is easy to read, understand, and write](https://github.com/carbon-language/carbon-lang/blob/trunk/docs/project/goals.md#code-that-is-easy-to-read-understand-and-write)). - Makes use of [Library APIs only](https://github.com/carbon-language/carbon-lang/blob/trunk/docs/project/principles/library_apis_only.md). ## Alternatives considered ### Atomic methods for `Iterate` An option was to define methods such as `Get`, `Advance`, `IsAtEnd` on `Iterate`, instead of a unique `Next` methods. While being more atomic, the concern is that these individual methods will all need to be called separately while iterating, while a single call to `Next` performs these three operations together, providing more opportunities for optimization. Using a single operation to return an optional is also consistent with Rust’s approach. The combined `Next` method forces us to return an `Optional`, which has downsides: - potential performance overhead to wrapping and unwrapping, - storage overhead for `Optional` itself, and - may require additional copying of the value wrapped. Those may be mitigated by leveraging unused bit patterns, passing a view of the value, or [inverting control](#inversion-of-control). ### Using an iterator instead of a cursor The iterator approach was considered but proved to have key limitations that a cursor approach does not have: - It requires implementing 2 interfaces instead of 1, and is more complex due to having the iteration logic separated from the container itself - More difficult to harden or troubleshoot, compared to a cursor that allows bounds checking in debug & hardened build modes - Can pose problems with ranges that consumes their input (See Barry Revzin’s "take(5)" presentation from C++Now 2023), when compared to a combined `Next()` approach. - Higher overhead - Proves to be difficult to support for R-values On the other hand, the cursor approach deviates more from C++ than iterator, and may require some more effort for C++ interoperability. ### Support getter for both `T` and `T*` with `Iterate` Because some collections will be read-only, we want to separate mutable and immutable interfaces. A single interface would force developers to implement or stub the mutable portion of the interface, even if it is not applicable. ================================================ FILE: proposals/p1891.md ================================================ # Are We Explorer Yet? [Pull request](https://github.com/carbon-language/carbon-lang/pull/1891) ## Table of contents - [Abstract](#abstract) - [Problem](#problem) - [Background](#background) - [AreWeYet](#areweyet) - [Carbon Explorer Status Brainstorm](#carbon-explorer-status-brainstorm) - [Proposal](#proposal) - [Details](#details) - [Are We Explorer Yet?](#are-we-explorer-yet-1) - [Alternatives considered](#alternatives-considered) ## Abstract It is currently difficult to see the status of Carbon explorer and where effort is needed. We propose creating a AreWeYet-styled dashboard to address this. ## Problem While the Carbon project has many published documents, it can be a challenge to get a high-level picture of work that has been done and work that remains to be done. This can make it difficult to both track its progress and identify contribution areas that will have the biggest impact. Left unchecked, this can lead to the team equivalent of micro-optimization where resources are deployed in a way that isn’t consistent with high-level goals. ## Background ### AreWeYet Mozilla, a non-profit behind many Open Source projects, has created and utilized what is known as the AreWeYet meme as an approach to solve the aforementioned problem. In this approach, a highly structured dashboard is built that succinctly states a goal, what its status is, and links to issues or other means to track subgoals. One example is [“Are We XBL Still?”](https://bgrins.github.io/xbl-analysis/)) which tracks a project to remove all XBL bindings from Firefox. The general format is a question, “Are we X still?”, followed by a list of items that, when complete, means the answer to the question becomes yes. Other examples can be found on Mozilla’s [Areweyet page](https://wiki.mozilla.org/Areweyet). The success of this methodology has led to other projects, such as Rust, adopting it. ### Carbon Explorer Status Brainstorm A portion of the June 29th, 2022 Carbon weekly sync was used as a brainstorming session to identify both work that has been accomplished and work that remains for the Carbon explorer tool. While a comprehensive snapshot in time, there wasn’t an explicit agreement on how to utilize this document or keep it updated going forward. This proposal is the logical next step in this work. ## Proposal We propose creating and utilizing an AreWeYet for the explorer project. The methodology has already been successful elsewhere and we already have the data collected for the explorer project. Although this proposal narrowly targets Carbon explorer, experience with this methodology may indicate value for its use elsewhere. ## Details In summary, we propose the creation of a new page on the carbon-lang Wiki called “Are We Explorer Yet?”, population of this page with results of the explorer status brainstorm, and the creation of issues for parts that are not complete. The following section will be used as the basis for the “Are We Explorer Yet?” page. Items that are specifically out of scope for this AreWeYet are C++ interop (likely never a target for explorer), metaprogramming, parallel programming, and coroutines. The last three are likely substantial enough to merit their own AreWeYet pages. ### Are We Explorer Yet? - ❌ Structured programming - ✅ While loops - ✅ Variable declarations - ❌ Variable initialization tracking - ❌ Returned var - ❌ Variadics - ❌ User defined types - ✅ structs - ✅ classes - ❌ choice - ✅ Alias system - ❌ OO programming - ❌ Inheritance - ❌ Parameterized class methods w/ inheritance - ❌ Destructors - ✅ Methods - ✅ Static functions / Class functions - ❌ Generic programming - ✅ Generic classes - ❌ Generic methods - ✅ Generic functions - ✅ Interfaces - ✅ Generic Interfaces - ✅ Impls - ✅ Generic Impls - ❌ Impl specialization - ❌ Templates - ❌ Operator overloading - ✅ == - ❌ /= - ❌ Other operators - ❌ Constraints - ✅ Implicit “as” - ❌ Error handling - ✅ Prelude - ✅ Print function - ❌ Types - ✅ i32 - ❌ Other integral types - ❌ Integral types as library types instead of native - ✅ Tuples - ✅ Pointer - ✅ Functions - ✅ Bool - ✅ String - ❌ Floating point types - ❌ Raw string literals - ❌ Code organization - ❌ Mixins - ❌ Imports - ❌ Separate packages - ❌ Modules - ❌ Namespaces ## Alternatives considered The default alternative is status quo where carbon explorer progress isn’t being tracked from a high-level. Due to the problems outlined in the problems section, this seems like an undesirable option. Another alternative is to use github issue tags and come up with a custom search to see the outstanding issues. This has the benefit of being fully automated. On the other hand, it would be difficult to get a hierarchical view which is useful to being able to grasp the big picture. ================================================ FILE: proposals/p1964.md ================================================ # Character literals [Pull request](https://github.com/carbon-language/carbon-lang/pull/1964) ## Table of contents - [Abstract](#abstract) - [Problem](#problem) - [Background](#background) - [Proposal](#proposal) - [Details](#details) - [Types](#types) - [Operations](#operations) - [Rationale](#rationale) - [Alternatives considered](#alternatives-considered) - [No distinct character types](#no-distinct-character-types) - [No distinct character literal](#no-distinct-character-literal) - [Supporting prefix declarations](#supporting-prefix-declarations) - [Allowing numeric escape sequences](#allowing-numeric-escape-sequences) - [Supporting formulations of grapheme clusters and non-code-point code-units](#supporting-formulations-of-grapheme-clusters-and-non-code-point-code-units) - [Future Work](#future-work) - [UTF code unit types proposal](#utf-code-unit-types-proposal) ## Abstract This proposal specifies lexical rules for constant characters in Carbon: Put character literals in single quotes, like `'a'`. Character literals work like numeric literals: - Every different literal value has its own type. - The literal itself doesn't have a bit width as a consequence. Instead, variables use explicitly sized character types and character literals can be converted to these types when representable. - A character literal must contain exactly one code point. Follows the plan from open design idea [#1934: Character Literals](https://github.com/carbon-language/carbon-lang/issues/1934). ## Problem Carbon currently has no lexical syntax for character literals, and only provides string literals and numeric literals. We wish to provide a distinct lexical syntax for character literals versus string literals. The advantage of having an explicit character type fundamentally comes down to characters being represented as integers whereas strings are represented as buffers. This will allow characters to have different operations, and be more familiar to use. For example: ``` if (c >= 'A' and c <= 'Z') { c += 'a' - 'A'; } ``` The example above shows how we would be able to use operations similar to integers. Being able to use the comparison operations and supporting arithmetic operations provides an intuitive approach to using characters. This allows us to remove unnecessary logic of type conversion and other control flow logic, that is needed to work with a single element string. See [Rationale](#rationale) for more examples showing more appropriate use of characters over using strings. ## Background Character Literals by definition is a type of literal in programming for the representation of a single character's value within the source code of a computer program. Character literals between languages have some minor nuances but are fundamentally designed for the same purpose. Languages that have a dedicated character data type generally include character literals, for example C++, Java, Swift to name a few. Whereas other languages that lack distinct character type, like Python use strings of length one to serve the same purpose a character data type. For more information see [Character Literals Wiki](https://en.wikipedia.org/wiki/Character_literal), [Character Literals DBpedia](https://dbpedia.org/page/Character_literal) ## Proposal Put character literals in single quotes, like `'a'`. Character literals work like numeric literals: - Every different literal value has its own type. - The literal itself doesn't have a bit width as a consequence. Instead, variables use explicitly sized character types and character literals can be converted to these types when representable. Follows the plan from #1934. - A character literal will model single Unicode code points that have a single concrete numerical representation. We will not be supporting other formulations like code unit sequences or grapheme clusters as these will be modeled with normal string literals. ## Details - A character literal is a sequence enclosed with single quotes delimiter ('), of UTF-8 code units that must be a valid encoding. This matches [the UTF-8 encoding of Carbon source files](https://github.com/carbon-language/carbon-lang/blob/trunk/proposals/p0142.md#character-encoding). - A character literal must encode exactly one code point. - It supports addition and subtraction, [as described below](#operations). - Character literals will support the relevant subset of the backslash (`\`) escape sequences in string literals, including `\t`, `\n`, `\r`, `\"`, `\'`, `\\`, `\0`, and `\u{HHHH...}`. See [String Literals: Escape sequence](https://github.com/carbon-language/carbon-lang/blob/trunk/proposals/p0199.md#escape-sequences). - Escape sequences which would result in non-UTF-8 encodings or more than one code point are not included. - The escape of an embedded newline is also excluded as it isn't expected to be relevant for character literals. We will not support: - character literals that don't contain exactly one Unicode code point; - multi-line literals; - "raw" literals (using #'x'#); - `\x` escape sequences; - character literals with a single quote (`'`) or back-slash (`\`), except as part of an escape sequence; - empty character literals (`''`); - a backslash followed by an (unescaped) newline; - ASCII control codes (0...31), including whitespace characters other than word space (tab, line feed, carriage return, form feed, and vertical tab), except when specified with an escape sequence. ### Types For the time being, Carbon will support three character types: `Char8`, `Char16`, and `Char32`. These types are capable of representing both code units and code points. It’s important to note that the support for different UTF-encoding code unit types will be addressed in a separate proposal. Please refer to the [UTF code unit types proposal](#utf-code-unit-types-proposal)for more information on that topic. In Carbon, the type `CharN` represents a character, where `N` corresponds to the bit size of the character type (`8`, `16`, or `32`). We will only allow character literals that map directly to a complete value of a code point. Here are examples of character literals for each specific type: - `Char8`: The character literal consists of a single Unicode code point that can be represented within 8 bits. For example: `let allowed: Char8 = ‘a’ ` In this example, the character literal `’a’` corresponds to the Unicode code point `97`, which is within the valid range of `Char8` since `97` is less than or equal to `0x7F`. - `Char16`: The character literal represents a Unicode code point that can be represented within 16 bits. Here’s an example: `let smiley: Char16 = ‘\u{1F600}’` The character literal `’\u{1F600}’` represents the smiley face emoji, which has the Unicode code point `128512`. Since `128512` can be represented within 16 bits, it can be assigned to a variable of type `Char16`. - `Char32`: This character type allows the representation of Unicode code points within 32 bits. Here’s an example: `let musicalNote: Char32 = ‘🎵’` In this case, the character literal `’🎵’` corresponds to the musical note emoji with the Unicode code point `127925`. Since `127925` falls within the range that can be represented by `Char32`, it can be assigned to a variable of type `Char32`. By restricting character literals to those that can be directly mapped to code points within the specific character types, we ensure accurate representation and compatibility with the chosen character encoding scheme. ### Operations Character literals representing a single code point support the following operators: - Comparison: `<`, `>`, `<=`, `>=` `==` - Plus: `+`. This doesn't concatenate, but allows numerically adjusting the value: - Only one operand may be a character literal, the other must be an integer literal. - The result is the character literal whose numeric value is the sum of numeric value of the operands. If that sum is not a valid Unicode code point, it is an error. - Subtract: `-`. This will subtract the value of the two characters, or a character followed by an integer literal: - If the `-` is used between two character literals, the result will be an integer constant. For example, `'z' - 'a'` is equivalent to `25`. - If the `-` is used between a character literal followed by a integer literal, this will produce a character constant. For example `'z' - 4` is equivalent to `'v'`. - If the `-` is used between a integer literal followed by a character literal `100 - 'a'`, this will be rejected unless the integer is cast to a character. There is intentionally no implicit conversion from character literals to integer types, but explicit conversions are permitted between character literals and integer types. Carbon will separate the integer types from character types entirely. ## Rationale This proposal supports the goal of making Carbon code [easy to read, understand, and write](/docs/project/goals.md#code-that-is-easy-to-read-understand-and-write). Adding support for a specific character literal supports clean, readable, concise use and is a much more familiar concept that will make it easier to adopt Carbon coming from other languages. Have a distinct character literal will also allow us support useful operations designed to manipulate the literal's value. When working with an explicit character type we can use operators that have unique behavior, for example say we wanted to advance a character to the next literal. In other languages the `+` operator is often used for concatenation, so using a `String` will produce a type error: `"a" + 1`. However with a character literal, we can support operations for these use cases: ``` var b: u8; b = 'a' + 1; b + 1 == 'c'; ``` See [Operations](#operations) and [No Distinct Character Literal](#no-distinct-character-literal) for more information. Further, this design follows other standards set in place by previous proposals. For example following the [String Literals: Escaping Sequence](https://github.com/carbon-language/carbon-lang/blob/trunk/proposals/p0199.md#escape-sequences-1) and representing characters as integers with the behaviour inline with [Integer Literals](https://github.com/carbon-language/carbon-lang/blob/trunk/proposals/p0143.md). This also supports our goal for [Interoperability with and migration from existing C++ code](/docs/project/goals.md#interoperability-with-and-migration-from-existing-c-code) by ensuring that every kind of character literal that exists in C++ can be represented in a Carbon character literal. This is done in a way that is natural to adopt, understand, easy to read by having explicit character types mapped to the C++ character types and the correct associated encoding. Finally, the choice to use Unicode and UTF-8 by default reflects the Carbon goal to prioritize [modern OS platforms, hardware architectures, and environments](/docs/project/goals.md#modern-os-platforms-hardware-architectures-and-environments). This reflects the [growing adoption of UTF-8](https://en.wikipedia.org/wiki/UTF-8#Adoption). ## Alternatives considered ### No distinct character types Unlike C++, Carbon will separate the integer and the character types. We considered using `u8`, `u16`, and `u32` instead of `Char8`, `Char16`, and `Char32` to reduce the number of different types users needed to be aware of and convert between. We decided against it because it came with a number of disadvantages: - `u8`, `u16`, and `u32` have the wrong arithmetic semantics: we don't want wrapping, and many `uN` operations, like multiplication, division, and shift, are not meaningful on code units. There may be rare cases where you want to use those operations, such as if you're implementing a conversion to or from code units. But in those rare cases it would be reasonable for the user to convert to an integer type to perform that operation and convert back when done. - Some operations want to be able to tell the difference between values that are intended to be UTF-8 instead of having no specified encoding. - Some operations want to be able to know that they've been given text rather than random bytes of data. For example, `Print(0x41 as u8)` would be expected to print `"65"` while `Print('\u{41}')` and `Print(0x41 as Char8)` would be expected to print `"A"`. - It's useful for developers to document the intended meaning of a value, and using a distinct type is one way to do that. See [UTF code unit types proposal](#utf-code-unit-types-proposal) for more information about UTF encoding types for a future proposal. ### No distinct character literal In principle, a character literal can be represented by reusing string literals similar to how Python handles character literals, however this would prevent performing operations on characters as integers. For example, the `+` operator on strings is used for concatenation, but `+` on a character would change its value. ``` // `digit` must be in the range 0..9. fn DigitToChar(digit: i32) -> Char8 { return '0' + digit; } ``` Furthermore, many properties of Unicode characters are defined on ranges of code points, motivating supporting comparison operators on code points. ``` fn IsDingBatCodePoint(c: Char32) -> bool { return c >= '\u{2700}' and c <= '\u{27BF}'; } ``` ### Supporting prefix declarations No support is proposed for prefix declarations like `u`, `U`, or `L`. In practice they are used to specify the character literal types and their encoding in languages like C and C++. There are a several benefits to omitting prefix declarations; improved readablitly, simplifying how a character's type is determined, and how we are encoding character literals. When declaring a character literal, the type is based on the contents of the character so that `var c: u8 = 'a'` is a valid character that can be converted to `u8`, in order to support prefix declarations we would need to extend our type system to have other explicit type checks like in C++; a UTF-16 `u'`, UTF-32 `U'`, and wide characters `L'`. This would be more familiar for individuals coming to Carbon from a C++ background, and simplify our approach for C++ Interoperability. At the cost of diverge from existing standards, for example [Proposal 142](https://github.com/carbon-language/carbon-lang/blob/trunk/proposals/p0142.md#character-encoding) states all of Carbon source code should be UTF-8 encoded. Prefix declarations would detract the readability of the character literals and increase the complexity of character literal [Types](#types). ### Allowing numeric escape sequences This proposal does not support numeric escape sequences using `\x`. This simplifies the design of character types and literals, making them only represent code points and not code units. However this does come with the disadvantage of less consistency of character literals with string literals, since they now accept different escape sequences. We don't want to remove numeric escape sequence from string literals, so we can support string use cases like representing invalid encodings. This approach has the additional concern that if character literals don't support numeric escape sequences, developers may choose to use numeric literals instead, at a cost of type-safety and readability. For example, it isn't clear in `var first_digit: Char8 = 0;` whether `0` is supposed to be a `NUL` character or the encoding of the `'0'` character (48). We addressed this concern, and type safety concerns about distinguishing numbers and characters, by making the integer to character conversions explicit. ### Supporting formulations of grapheme clusters and non-code-point code-units Rather than explicitly limiting characters literals to a more integer-like representation of a single Unicode code point, we could represent characters literal formulations of grapheme clusters and non-code-point code units. What humans tend to think of as a "character" corresponds to a "grapheme cluster." The encoding of a grapheme cluster can be arbitrarily long and complex, which would sacrifice the ability to perform integer operations. If we wanted to add support for other character formulations, we would need to use separate spellings to represent a small set of operations that are today expressed with integer-based math on C++'s character literals. This includes things like converting an integer between 0 and 9 into the corresponding digit character, or computing the difference between two digits/two other characters. For these reasons, we have decided to start out by representing character literals as single Unicode code points following a more integer-like model. However this topic should be revisited if we find that there is a significant need for the additional functionality and attendant complexity for these other character formulations. ## Future Work ### UTF code unit types proposal There have been several ideas and discussions around how we would like to handle UTF code units. This section will hopefully provide some guidance for a future proposal when the topic is revisited for how we would like to build out encoding/decoding for character literals. We will have the types `Char8`, `Char16`, and `Char32` representing code units in UTF-8, UTF-16, and UTF-32, but we will not support all code units, but only those which map directly to the complete value of a code point. However, character literals will use their own types distinct from these: - We will support value preserving implicit conversions from character literals to code point or code unit types. In particular, a character literal converts to a `Char8` UTF-8 code unit if it is less than or equal to 0x7F, and `Char16` UTF-16 code unit if it is less than or equal to 0xFFFF. - Conversions from string or character literals to a non-value-preserving encoding must be explicit. - Conversions from string literals to Unicode strings are implicit, even though the numeric values of the encoding may change. We can see whether the particular literal is represented in the variable's type by only looking at the types. ``` let allowed: Char8 = 'a'; ``` The above is allowed because the type of `'a'` is the character literal consisting of the single Unicode code point 97, which can be converted to `Char8` since 97 is less than or equal to 0x7F. ``` let error1: Char8 = '😃'; let error2: Char8 = 'AB'; ``` However these should produce errors. The type of `'😃'` is the character literal consisting of the single Unicode code point `0x1F603`, which is greater than 0x7F. The type of `'AB'` is a character literal that is a sequence of two Unicode code points, which has no conversion to a type that only handles a single UTF-8 code unit. All of `'\n'`, and `'\u{A}'` represent the same character and so have the same type. However, explicitly converting this character literal to another character set might result in a character with a different value, but that still represents the newline character. ================================================ FILE: proposals/p1983.md ================================================ # Weaken digit separator placement rules [Pull request](https://github.com/carbon-language/carbon-lang/pull/1983) ## Table of contents - [Abstract](#abstract) - [Problem](#problem) - [Background](#background) - [Proposal](#proposal) - [Rationale](#rationale) - [Alternatives considered](#alternatives-considered) - [3-digit decimal groupings](#3-digit-decimal-groupings) - [2-digit or 4-digit hexadecimal digit groupings](#2-digit-or-4-digit-hexadecimal-digit-groupings) - [Disallow digit separators in fractions](#disallow-digit-separators-in-fractions) ## Abstract [Proposal #143: Numeric literals](/proposals/p0143.md) added digit separators with strict rules for placement. It missed some use-cases. In order to address this, remove placement rules for numeric literals. ## Problem Digit separator placement rules are too strict: - For integers, while the original proposal mentioned Indian digit groupings, Chinese, Japanese, and Korean cultures use 4-digit groupings. This oversight is likely due to the description on [wikipedia](https://en.wikipedia.org/wiki/Decimal_separator#Digit_grouping): "but the delimiter commonly separates every three digits". - There are microformats where different placement rules may be desirable. See [alternatives](#alternatives-considered) for more specific examples. ## Background [Proposal #143: Numeric literals](/proposals/p0143.md) added digit separators with strict rules for placement: - For decimal integers, the digit separators shall occur every three digits starting from the right. For example, `2_147_483_648`. - For hexadecimal integers, the digit separators shall occur every four digits starting from the right. For example, `0x7FFF_FFFF`. - For real number literals, digit separators can appear in the decimal and hexadecimal integer portions (prior to the period and after the optional `e` or mandatory `p`) as described in the previous bullets. For example, `2_147.483648e12_345` or `0x1_00CA.FEF00Dp+24` - For binary literals, digit separators can appear between any two digits. For example, `0b1_000_101_11`. This was asked on [Issue #1485: Reconsider digit separators](https://github.com/carbon-language/carbon-lang/issues/1485), where a proposal was requested. ## Proposal Switch to simple rules: a single digit separator can appear between any two digits. For example: - Decimal literals: `1_2_3` - Hexadecimal literals: `0xA_B_C` - Real literals: `1_2.3_4e5_6` - Binary literals are unaffected. ## Rationale - [Community and culture](/docs/project/goals.md#community-and-culture) - Removing restrictions on decimal literals provides flexibility for developers who don't use 3-digit groupings. - [Code that is easy to read, understand, and write](/docs/project/goals.md#code-that-is-easy-to-read-understand-and-write) - This allows for mistakes in groupings, which is undesirable. However, it's useful for microformats to be able to provide their own particular groupings. - [Interoperability with and migration from existing C++ code](/docs/project/goals.md#interoperability-with-and-migration-from-existing-c-code) - Reduces migration hurdles for C++ code by providing a rule that's more consistent with the C++ rule. ## Alternatives considered ### 3-digit decimal groupings For decimal separators, we could enforce 3-digit groupings if digit separators are used at all. Advantages: - More enforcement of digit groupings can prevent bugs and misreadings resulting from accidentally incorrect groupings. Disadvantages: - Indian, Chinese, Japanese, and Korean digit grouping conventions don't fit under this rule. - These are worth considering similar to how Carbon allows [Unicode identifiers](/docs/design/lexical_conventions/words.md): although keywords are in English, we can be more flexible for both identifiers and literals. - Microformats could also pose issues. For example, microseconds (`1_00000`), dates (`01_12_1983`), credit cards (`1234_5678_9012_3456`), or identity numbers such as US social security numbers (`123_45_6789`). - Since the original proposal, more cases have been raised. Note that any regular grouping rule can present similar issues for Indian digit grouping conventions. [Proposal #143: Numeric literals](/proposals/p0143.md) chose 3-digit decimal groupings. Given there are overall advantages to not enforcing regular digit conventions, including for hex digits, it seems unnecessary to conform to the currently established decimal conventions. While this removes the enforcement, the resulting accidents are considered a reasonable risk. In theory a code linter could be told to prefer certain formats with options to change behavior, although that may remain too low benefit to implement. ### 2-digit or 4-digit hexadecimal digit groupings Hexadecimal digit groupings could be enforced along two axes: - Every 2 (`F_FF`) or 4 (`F_FFFF`) characters, corresponding to one or two bytes respectively. - Regular or irregular placement. For example, if a digit separator is placed every byte (every other digit), we could require regular placement every byte as in `FF_FF_FF_FF`, or irregular as in `FF_FFFFF_FF` which skips one placement. [Proposal #143: Numeric literals](/proposals/p0143.md) chose 4-digit with regular placement. Advantages: - More enforcement of digit groupings can prevent bugs and misreadings resulting from accidentally incorrect groupings. Disadvantages: - Again, microformats become an issue. - As noted in background, MAC addresses are typically in byte pairs (`00_B0_D0_63_C2_26`) and UUIDs have particular groupings (`123E4567_E89B_12D3_A456_426614174000`). - If all of the other literal formats (decimal, real, and binary) allow arbitrary digit separator placement, enforcing placement in hexadecimal numbers would be an outlier. While it may be that enforcing 2 character (one byte) groupings with irregular placement would prevent sufficient errors versus the developer inconvenience risks, it risks becoming an outlier and for only very loose enforcement. Similar to the preceding conclusion, leaving these issues to code linters may be best. ### Disallow digit separators in fractions [Proposal #143: Numeric literals](/proposals/p0143.md) appears to disallow digit separators in fractions. That is, in `1.2345`, `1.23_45` is disallowed. This proposal changes that. Advantages: - Reduces the chance of confusion resulting from mistaking a `.` for another `_` separator, as in `1_234_567.890_123_456`. Disadvantages: - While sometimes commas aren't written for digit separators, it's notable that SI advice is to use digit spacing instead ([Digit spacing, rule #16](https://physics.nist.gov/cuu/Units/checklist.html)). As a consequence, it's likely common to want some kind of digit separator after the decimal point. - Would create an inconsistency in placement rules. We'll allow digit separators in fractions. ================================================ FILE: proposals/p2006.md ================================================ # Values, variables, pointers, and references [Pull request](https://github.com/carbon-language/carbon-lang/pull/2006) ## Table of contents - [Abstract](#abstract) - [Problem](#problem) - [Conceptual integrity between local variables and parameters](#conceptual-integrity-between-local-variables-and-parameters) - [Background](#background) - [Proposal](#proposal) - [Values, objects, and expressions](#values-objects-and-expressions) - [Patterns, `var`, `let`, and local variables](#patterns-var-let-and-local-variables) - [Pointers, dereferencing, and references](#pointers-dereferencing-and-references) - [Indexing](#indexing) - [`const`-qualified types](#const-qualified-types) - [Interop with `const &` and `const` methods](#interop-with-const--and-const-methods) - [Customization](#customization) - [Rationale](#rationale) - [Contrasting the rationale for pointers with `goto`](#contrasting-the-rationale-for-pointers-with-goto) - [Alternatives considered](#alternatives-considered) - [Value expression escape hatches](#value-expression-escape-hatches) - [References in addition to pointers](#references-in-addition-to-pointers) - [Syntax-free or automatic dereferencing](#syntax-free-or-automatic-dereferencing) - [Syntax-free address-of](#syntax-free-address-of) - [Exclusively using references](#exclusively-using-references) - [Alternative pointer syntaxes](#alternative-pointer-syntaxes) - [Pointer type alternative syntaxes](#pointer-type-alternative-syntaxes) - [Pointer dereference syntax alternatives](#pointer-dereference-syntax-alternatives) - [Pointer syntax conclusion](#pointer-syntax-conclusion) - [Alternative syntaxes for locals](#alternative-syntaxes-for-locals) - [Make `const` a postfix rather than prefix operator](#make-const-a-postfix-rather-than-prefix-operator) - [Appendix: background, context, and use cases from C++](#appendix-background-context-and-use-cases-from-c) - [`const` references versus `const` itself](#const-references-versus-const-itself) - [Pointers](#pointers) - [References](#references) - [Special but critical case of `const T&`](#special-but-critical-case-of-const-t) - [R-value references and forwarding references](#r-value-references-and-forwarding-references) - [Mutable operands to user-defined operators](#mutable-operands-to-user-defined-operators) - [User-defined dereference and indexed access syntax](#user-defined-dereference-and-indexed-access-syntax) - [Member and subobject accessors](#member-and-subobject-accessors) - [Non-null pointers](#non-null-pointers) - [Syntax-free dereference](#syntax-free-dereference) ## Abstract Introduce a concrete design for how Carbon values, objects, storage, variables, and pointers will work. This includes fleshing out the design for: - The expression categories used in Carbon to represent values and objects, how they interact, and terminology that anchors on their expression nature. - An expression category model for readonly, abstract values that can efficiently support input to functions. - A customization system for value expression representations, especially as seen on function boundaries in the calling convention. - An expression category model for references instead of a type system model. - How patterns match different expression categories. - How initialization works in conjunction with function returns. - Specific pointer syntax, semantics, and library customization mechanisms. - A `const` type qualifier for use when the value expression category system is too abstracted from the underlying objects in storage. ## Problem Carbon needs a design for how values, variables, objects, pointers, and references work within the language. These designs are heavily interdependent and so they are presented together here. The design also needs to provide a compelling solution for a wide range of use cases in the language: - Easy to use, and use correctly, function input and in/out parameters. - Clear separation of read-only use cases like function inputs from mutable use cases. - Local names bound both to fixed values and mutable variables. - Indirect (and mutable) access to objects (by way of pointers or references). - Extensible models for dereferencing and indexing. - A delineated method subset which forms the API for read-only or input objects. - A delineated method subset which forms the APIs for indirect access to shared objects in thread-compatible ways. - Interoperation between function input parameters and C++ `const &` parameters. - Complex type designs which expose both interior pointers and exterior pointers transparently for extended dereferencing or indexing. ### Conceptual integrity between local variables and parameters An additional challenge that this design attempts to address is retaining the conceptual integrity between local variables and parameters. Two of the most fundamental refactorings in software engineering are _inlining_ and _outlining_ of regions of code. These operations introduce or collapse one of the most basic abstraction boundaries in the language: functions. These refactorings translate between local variables and parameters in both directions. In order to ensure these translations are unsurprising and don't face significant expressive gaps or behavioral differences, it is important to have strong semantic consistency between local variables and function parameters. While there are some places that these need to differ, there should be a strong overlap of the core facilities, design, and behavior. ## Background Much of this is informed by the experience of working with increasingly complex "value categories" (actually categorizing expressions) and parameter passing in C++ and how the language arrived there. Some background references on this area of C++ and the problems encountered: - https://en.cppreference.com/w/cpp/language/value_category - http://wg21.link/basic.lval - https://www.scs.stanford.edu/~dm/blog/decltype.html - https://medium.com/@barryrevzin/value-categories-in-c-17-f56ae54bccbe - http://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#Rf-conventional - https://www.youtube.com/watch?v=Tz5drzXREW0 I've also written up a detailed walk-through of the different use cases and considerations that touch on the space of values, references, function inputs, and more across C++ in an [appendix](#appendix-background-context-and-use-cases-from-c). Leads questions which informed the design proposed here: - [What syntax should we use for pointer types? (#523)][#523] It also builds on the design of the proposal ["Initialization of memory and variables"][p0257] ([#257]), implementing part of [#1993]. [#257]: https://github.com/carbon-language/carbon-lang/pull/257 [#523]: https://github.com/carbon-language/carbon-lang/issues/523 [#1993]: https://github.com/carbon-language/carbon-lang/issues/1993 [p0257]: /proposals/p0257.md ## Proposal This section provides a condensed overview of the proposal. The details are covered in the updated content in the design, and each section links into the relevant content there. While this overview both duplicates and summarizes content, it isn't intending anything different from the updates to the design content, and the design content should be considered authoritative as it will also continue to be maintained going forward. ### Values, objects, and expressions Carbon has both abstract _values_ and concrete _objects_. Carbon _values_ are things like `42`, `true`, and `i32` (a type value). Carbon _objects_ have _storage_ where values can be read and written. Storage also allows taking the address of an object in memory in Carbon. Both objects and values can be nested within each other. For example `(true, true)` is both a value and also contains two sub-values. When a two-tuple is stored somewhere, it is both a tuple-typed object and contains two subobjects. > Details: > [Values, objects, and expressions](/docs/design/values.md#values-objects-and-expressions) Expressions are categorized in a way that explains how they produce values or refer to objects: - [_Value expressions_](/docs/design/values.md#value-expressions) produce abstract, read-only _values_ that cannot be modified or have their address taken. - [_Reference expressions_](/docs/design/values.md#reference-expressions) refer to _objects_ with _storage_ where a value may be read or written and the object's address can be taken. - [_Durable reference expressions_](/docs/design/values.md#durable-reference-expressions) are reference expressions which cannot refer to _temporary_ storage, but must refer to some storage that outlives the full expression. - [_Ephemeral reference expressions_](/docs/design/values.md#ephemeral-reference-expressions) are reference expressions which _can_ refer to temporary storage. - [_Initializing expressions_](/docs/design/values.md#initializing-expressions) which require storage to be provided implicitly when evaluating the expression. The expression then initializes an object in that storage. These are used to model function returns, which can construct the returned value directly in the caller's storage. > Details: [Expression categories](/docs/design/values.md#expression-categories) ### Patterns, `var`, `let`, and local variables Patterns are by default _value patterns_ and match _value expressions_, but can be introduced with the `var` keyword to create a _variable pattern_ that has _storage_ and matches _initializing expressions_. Names bound in value patterns become value expressions, and names bound in a variable pattern become _durable reference expressions_ referring to an object in that pattern's storage. Local patterns can be introduced with `let` to get the default behavior of a readonly pattern, or they can be directly introduced with `var` to form a variable pattern and declare mutable local variables. > Details: > [Binding patterns and local variables with `let` and `var`](/docs/design/values.md#binding-patterns-and-local-variables-with-let-and-var) ### Pointers, dereferencing, and references Pointers in Carbon are the primary mechanism for _indirect access_ to storage containing some object. Dereferencing a pointer forms a [_durable reference expression_](/docs/design/values.md#durable-reference-expressions) to the object. Carbon pointers are heavily restricted compared to C++ pointers -- they cannot be null and they cannot be indexed or have pointer arithmetic performed on them. Carbon will have dedicated mechanisms that still provide this functionality, but those are future work. > Details: [Pointers](/docs/design/values.md#pointers) The syntax for working with pointers is similar to C++: ```carbon var i: i32 = 42; var p: i32* = &i; // Form a reference expression `*p` and assign `13` to the referenced storage. *p = 13; ``` > Details: > > - [Pointer syntax](/docs/design/values.md#pointer-syntax) > - [Pointer operators](/docs/design/expressions/pointer_operators.md) Carbon doesn't have reference types, just reference expressions. API designs in C++ that use references (outside of a few common cases like `const &` function parameters) will typically use pointers in Carbon. The goal is to simplify and focus the type system on a primary model of indirect access to an object. > Details: [Reference types](/docs/design/values.md#reference-types) ### Indexing Carbon supports indexing that both accesses directly contained storage like an array and indirect storage like C++'s `std::span`. As a result, the exact interfaces used for indexing reflect the expression category of the indexed operand and the specific interface its type implements. This proposal just updates and refines this design with the new terminology. > Details: [Indexing](/docs/design/expressions/indexing.md) ### `const`-qualified types Carbon provides the ability to qualify a type `T` with the keyword `const` to get a `const`-qualified type: `const T`. This is exclusively an API-subsetting feature in Carbon -- for more fundamentally "immutable" use cases, value expressions and bindings should be used instead. Pointers to `const`-qualified types in Carbon provide a way to reference an object with an API subset that can help model important requirements like ensuring usage is exclusively by way of a _thread-safe_ interface subset of an otherwise _thread-compatible_ type. > Details: > [`const`-qualified types](/docs/design/values.md#const-qualified-types) ### Interop with `const &` and `const` methods Carbon makes value expressions interoperable with `const &` function parameters. There are two dangers of this approach: - Those constructs in C++ _do_ allow the address to be taken, so Carbon will synthesize an object and address for value expressions for the duration of the C++ call as needed. C++ code already cannot rely on the address of `const &` parameters continuing to exist once the call completes. - C++ also allows casting away `const`. However, this doesn't allow a method to safely _mutate_ a `const &` parameter, except its `mutable` members. In both cases, correct C++ code should already respect the limitations Carbon needs for this interop. The same applies to the implicit `this` parameter of `const` methods in C++. > Details: > [Interop with C++ `const &` and `const` methods](/docs/design/values.md#interop-with-c-const--and-const-methods) ### Customization Carbon will provide a way to customize the implementation representation of a value expression by nominating some other type to act as its representation. This will be indicated through an explicit syntactic marker in the class definition, and will require the type to `impl` a customization interface `ReferenceImplicitAs` to provide the needed functionality. The result will be to form this custom type when forming a value expression from a reference expression, and will restrict the operations on such a value expression to implicitly converting to the nominated type. > Details: > [Value representation and customization](/docs/design/values.md#value-representation-and-customization) Carbon will also allow customizing the behavior of a dereference operation on a type to allow building "smart pointers" or other pointer-like types in the library. This will be done in a similar fashion to overloading other operators, where the overloaded operation returns some other pointer that is then dereferenced to form a reference expression. > Details: > [Dereferencing customization](/docs/design/values.md#dereferencing-customization) ## Rationale - Pointers are a fundamental components of all modern computer hardware -- they are abstractly random-access machines -- and being able to directly model and manipulate this is necessary for [performance-critical software](/docs/project/goals.md#performance-critical-software). - Simplifying the type system by avoiding both pointers and references is expected to make [code easier to read, understand, and write](/docs/project/goals.md#code-that-is-easy-to-read-understand-and-write). - Creating space in both the syntax and type system to introduce ownership and lifetime information is important to be able to address long term [safety](/docs/project/goals.md#practical-safety-and-testing-mechanisms) needs. - Pointers are expected to be deeply familiar to C++ programmers and easily [interoperate with C++ code](/docs/project/goals.md#interoperability-with-and-migration-from-existing-c-code). ### Contrasting the rationale for pointers with `goto` There is an understandable concern about Carbon deeply incorporating pointers into its design -- pointers in C and C++ have been the root of systematic security and correctness bugs. There is an amazing wealth of "gotchas" tied to pointers that it can seem unreasonable to build Carbon on top of these foundations. By analogy, the `goto` control-flow construct is similarly considered _deeply_ problematic and there is a wealth of literature in computer science and programming language design on how and why to build languages on top of structured control flow instead. It would be very concerning to build `goto` into the fundamental design of Carbon as an integral and pervasive component of its control flow. However, there are two important distinctions between pointers and `goto`. First, C and C++ pointers are _very_ different from Carbon pointers, and their rightly earned reputation as a source of bugs and confusion stems precisely from these differences: - C pointers can be null, the ["billion dollar mistake"][null-mistake], Carbon's cannot be. Carbon requires null-ness to be explicitly modeled in the type system and dealt with prior to dereference. - C pointers can be indexed even when not pointing to an array. Carbon's pointers are to singular objects. Carbon handles indexing with a separate construct and types to distinguish arrays where indexing is semantically meaningful from the cases where it is not. - C pointers support deeply surprising indexing patterns such as `3[pointer]`. Carbon restricts to unsurprising indexing. - C pointers don't enforce lifetime or any other memory safety properties. While Carbon does not yet support these deeper protections, tackling memory safety is an explicit plan of Carbon's and may yet result in changes to the pointer type to reflect the safety risks posed by them. [null-mistake]: https://www.infoq.com/presentations/Null-References-The-Billion-Dollar-Mistake-Tony-Hoare/ While these are described in terms of C's pointers, C++ inherits these without meaningful improvement from C. The core point here is that while Carbon is building pointers into its foundations, it is building in the least problematic and most useful aspect of pointers and leaving almost all of the legacy and risk behind. That is a critical part of what makes pointers viable in Carbon. The second important contrast with `goto` is the lack of a comprehensive alternative to pointers. Structured control flow has been thoroughly studied and shown to address the practical needs of expressing control flow. As a consequence, we have _good_ tools to replace `goto` within languages, and we should use them. In contrast, Carbon programs are still expected to map onto [von Neumann architecture](https://en.wikipedia.org/wiki/Von_Neumann_architecture) machines and need to model the fundamental construct of a pointer to data. We have no comprehensive alternative to solve all of the practical needs surrounding indirect access to data on such an architecture. So despite including pointers as one of the building blocks of Carbon, we don't need for `goto` to be the surfaced and visible building block of Carbon's control flow. Even if we decide to support some limited forms of `goto` to handle edge cases where structured constructs end up suboptimal, we can still build the foundations in a structured way without impairing the use of Carbon to work with the low-level hardware effectively. ## Alternatives considered ### Value expression escape hatches We could provide escape hatches for value expressions that (unsafely) take the address or even perform mutations through a value expression. This would more easily match patterns like `const_cast` in C++. However, there seem to be effective ways of rewriting the code to avoid this need so this proposal suggests not adding these escape hatches now. We will instead provide a more limited escape exclusively for [interop](#interop-with-const--and-const-methods). We can add more later if experience proves this is an important pattern to support without the contortions of manually creating a local copy (or changing to pointers). ### References in addition to pointers The primary and most obvious alternative to the design proposed here is the one used by C++: have _references_ in addition to pointers in the type system. This initially allows zero-syntax modeling of L-values, which can in turn address many use cases here much as they do in C++. Similarly, adding different kinds of references can allow modeling more complex situations such as different lifetime semantics. However, this approach has two fundamental downsides. First, it would add overall complexity to the language as references don't form a superset of the functionality provided by pointers -- there is still no way to distinguish between the reference and the referenced object. This results in confusion where references are understood to be syntactic sugar over a pointer, but cannot be treated as such in several contexts. Second, this added complexity would reside exactly in the position of the type system where additional safety complexity may be needed. We would like to leave this area (pointers and references to non-local objects) as simple and minimal as possible to ease the introduction of important safety features going forward in Carbon. ### Syntax-free or automatic dereferencing One way to make pointers behave very nearly the same as references without adding complexity to the type system is to automatically dereference them in the relevant contexts. This can, if done carefully, preserve the ability to distinguish between the pointer and the pointed-to object while still enabling pointers to be seamlessly used without syntactic overhead as L-values. This proposal does not currently provide a way to dereference with zero syntax, even on function interface boundaries. The presence of a clear level of indirection can be an important distinction for readability. It helps surface that an object that may appear local to the caller is in fact escaped and referenced externally to some degree. However, it can also harm readability by forcing code that doesn't _need_ to look different to do so anyway. In the worst case, this can potentially interfere with being generic. Currently, Carbon prioritizes making the distinction here visible. Reasonable judgement calls about which direction to prefer may differ, but Carbon's principle of [preferring lower context sensitivity](/docs/project/principles/low_context_sensitivity.md) leans (slightly) toward explicit dereferencing instead. That is the current proposed direction. It may prove desirable in the future to provide an ergonomic aid to reduce dereferencing syntax within function bodies, but this proposal suggests deferring that in order to better understand the extent and importance of that use case. If and when it is considered, a direction based around a way to bind a name to a reference expression in a pattern appears to be a promising technique. Alternatively, there are various languages with implicit- or automatic-dereference designs that might be considered in the future such as Rust. #### Syntax-free address-of A closely related concern to syntax-free dereference is syntax-free address-of. Here, Carbon supports one very narrow form of this: implicitly taking the address of the implicit object parameter of member functions. Currently that is the only place with such an implicit affordance. It is designed to be syntactically sound to extend to other parameters, but currently that is not planned as we don't yet have enough experience to motivate it and it may prove surprising. ### Exclusively using references While framed differently, this is essentially equivalent to automatic dereferencing of pointers. The key is that it does not add both options to the type system but addresses the syntactic differences separately and uses different operations to distinguish between the reference and the referenced object when necessary. The same core arguments against automatic dereferencing applies equally to this alternative -- this would remove the explicit visual marker for where non-local memory is accessed and potentially mutated. ### Alternative pointer syntaxes The syntax both for declaring a pointer type and dereferencing a pointer has been extensively discussed in the leads question [#523]. [#523]: https://github.com/carbon-language/carbon-lang/issues/523 The primary sources of concern over a C++-based syntax: 1. A prefix dereference operator composes poorly with postfix and infix operations. - This is highlighted by the use of `->` for member access due to the poor composition with `.`: `(*pointer).member`. 2. Even without replicating the ["inside out"][inside-out] C/C++ challenges, we would end up with a prefix, postfix, and infix operator `*`. [inside-out]: https://faculty.cs.niu.edu/~mcmahon/CS241/Notes/reading_declarations.html The second issue was resolved in [#520], giving us at least the flexibility to consider `*` both for dereference and pointer type, but we still considered numerous alternatives given the first concerns. These were discussed in detail in a [document][pointer-syntax-doc] but the key syntax alternatives are extracted with modern syntax below. [pointer-syntax-doc]: https://docs.google.com/document/d/1gsP74fLykZBCWZKQua9VP0GnQcADCkn5-TIC1JbUvdA/edit?resourcekey=0-8MsUybUvHDCuejrzadrbMg #### Pointer type alternative syntaxes **Postfix `*`:** ``` var p: i32* = &i; ``` Advantages: - Most familiar to C/C++ programmers - Doesn't collide with prefix `*` for dereference so can have that familiarity as well. Disadvantages: - Requires the [#520] logic to resolve parsing ambiguities. - Visually, `*` is used for both pointers and multiplication. - Interacts poorly with prefix array type syntaxes. - Looks like a C++ pointer but likely has different semantics. **Prefix `*`:** ``` var p: *i32 = &i; ``` Advantages: - Used by Rust, Go, Zig, [Jai](https://github.com/BSVino/JaiPrimer/blob/master/JaiPrimer.md#memory-management). - Go in particular has good success transitioning C/C++ programmers to this syntax. - Allows reading types left-to-right. Disadvantages: - "Pointer to T" and "Dereference T" would not be distinguished grammatically, could particularly be a problem in template code - `*` used for both pointers and multiplication - Uncanny valley -- it is very close to C++'s syntax but not quite the same. **Prefix `&`:** ``` var p: &i32 = &i; ``` Advantages: - "Type of a pointer is a pointer to the type" - Allows reading types left-to-right. Disadvantages: - Visual ambiguity: ``` let X:! type = i32; // Can't actually write this, but there is a visual collision between // whether this is the address of `X` or pointer-to-`i32`. var y: auto = &X; ``` **Prefix `^`:** ``` var p: ^i32 = &i; ``` Advantages: - Used by the Pascal family of languages like [Delphi](http://rosettacode.org/wiki/Pointers_and_references#Delphi), and a few others like [BBC_BASIC](http://rosettacode.org/wiki/Pointers_and_references#BBC_BASIC) - `^` looks pointy - `^` operator is not heavily used otherwise (as a binary op it could be bit-xor or raise-to-power). - Allows reading types left-to-right. **`Ptr(T)`:** ``` var p: Ptr(i32) = &i; ``` Advantages: - It is simple and unambiguous. - Better supports a variety of pointer types. - Allows reading types left-to-right. Disadvantages: - Ends up making common code verbose. - Not widely used in other languages. #### Pointer dereference syntax alternatives **Prefix `*`:** ``` *p = *p * *p + (*q)[3] + r->x; ``` Advantages: - Matches C/C++, and will be familiar to our initial users. - When _not_ composed with other operations, prefix may be easier to recognize visually than postfix. Disadvantages: - `*` is used for both pointers and multiplication. - Need parens or operator `->` to resolve precedence issues when composing with postfix and infix operations, which is common. **Postfix `^`:** ``` p^ = p^ * p^ + q^[3] + r^.x; ``` Advantages: - Generally fewer precedence issues. - No need for `->` operator. - Used by Pascal family of languages. Disadvantages: - Not familiar to C/C++ users. - Potential conflict with `^` as xor or exponentiation. - Non-ergonomic: would be very frequently typed, but also a stretching motion for typing. **Postfix `[]`:** ``` p[] = p[] * p[] + q[][3] + r[].x; ``` Advantages: - Generally fewer precedence issues. - No need for `->` operator. - Used by Swift. Disadvantages: - Two characters is a bit heavier visually. - Possibly confusing given that plain pointers are used for non-array pointees. Maybe should pair this with prefix `[]` to make pointer types? Would also need to distinguish slice types and maybe dynamically-sized-array types. #### Pointer syntax conclusion Ultimately, the decision in [#523] was to keep Carbon's syntax familiar to C++'s. The challenges that presents and advantages of changes weren't sufficient to overcome the advantage of familiarity and for the specific challenges we have effective solutions. ### Alternative syntaxes for locals Several other options for declaring locals were considered, but none ended up outweighing the proposed option on balance. - `let` and `let mut`, based on the Rust names - While nicely non-inventive, there was both concern that `mut` didn't as effectively communicate the requirement of storage and concern around not being as obviously or unambiguously a good default with `let`. Put differently, the `mut` keyword feels more fitting in its use with mutable borrows than as a declaration introducer the way it would work in Carbon. - There was a desire, for locals especially, to have both cases be similarly easy to spell. While mutation being visible does seem helpful, this specific approach seemed to go too far into being a tax. - `val` and `var` - Appealing to use `val` instead of `let` given that these form value expression bindings. These are also likely to be taught and discussed as "local values" which would align well with the `val` introducer. - Not completely inventive, used in Kotlin for example. But rare, overall. - However, _very_ visually similar to `var` which makes code harder to read at a glance. - Also difficult when speaking or listening to pronounce or hear the distinction. - `const` and `var` - Some familiarity for C++ developers given the use of `const` there. - `const` is used by other languages in a similar way to Carbon's `let`. - However, very concerned about reusing `const` but having it mean something fairly different from C++ as a declaration introducer. For example, nesting a `var` pattern within `const` might be especially surprising. Ultimately, the overwhelming most popular introducer for immutable local values across popular languages that have such a distinction is `let`. Using that makes Carbon unsurprising in the world of programming languages which is a good thing. Using `var` to help signify the allocation of storage and given it also having widespread usage across popular languages. ### Make `const` a postfix rather than prefix operator Using a prefix operator for `const` and a postfix operator for `*` causes them to require parentheses for complex nesting. We could avoid that by using a postfix `const` and this would also allow more combinations in Carbon to be valid in C++ with the same meaning as Carbon such as `T* const`. This direction isn't pursued because: - We expect pointers-to-`const` to be significantly more common in code than `const`-pointers-to-non-`const`, even more-so than is already the case in C++. And for `pointers-to-const`, we lean towards matching more widespread convention of `const T*` rather than `T const*`. - We are aware that some developers have a stylistic preference for "East `const`" which would write `T const*` in C++. However, that preference and advocacy for this style hasn't yet caused it to become more widespread or widely adopted style. - We don't expect the parenthesized form of `const (T*)` to be _confusing_ to C++ developers even though C++ doesn't allow this approach to forming a `const`-pointer-to-non-`const`. This alternative was only lightly considered and can be revisited if we get evidence that motivates a change here. ## Appendix: background, context, and use cases from C++ This appendix provides an examination of C++'s fundamental facilities in the space involving `const` qualification, references (including R-value references), and pointers. Beyond the expression categorization needed to provide a complete model for the language, these use cases help inform the space that should be covered by the proposed design. ### `const` references versus `const` itself C++ provides overlapping but importantly separable semantic models which interact with `const` references. 1. An _immutable view_ of a value 2. A _thread-safe interface_ of a [thread-compatible type][] [thread-compatible type]: https://abseil.io/blog/20180531-regular-types#:~:text=restrictions%20or%20both,No%20concurrent%20call Some examples of the immutable view use case are provided below. These include `const` reference parameters and locals, as well as `const` declared local and static objects. ```cpp void SomeFunction(const int &id) { // Here `id` is an immutable view of some value provided by the caller. } void OtherFunction(...) { // ... const int &other_id = ; // Cannot mutate `other_id` here either, it is just a view of the result of // `` above. But we can pass it along to another // function accepting an immutable view: SomeFunction(other_id); // We can also pass ephemeral values: SomeFunction(other_id + 2); // Or values that may be backed by read-only memory: static const int fixed_id = 42; SomeFunction(fixed_id); } ``` The _immutable view_ `id` in `SomeFunction` can be thought of as requiring that the semantics of the program be exactly the same whether it is implemented in terms of a view of the initializing expression or a copy of that value, perhaps in a register. The implications of the semantic equivalence help illustrate the requirements: - The input value must not change while the view is visible, or else a copy would hide those changes. - The view must not be used to mutate the value, or those mutations would be lost if made to a copy. - The identity of the object must not be relevant, or else inspection of its address would reveal whether a copy was used. Put differently, these restrictions make a copy valid under the [as-if rule](https://en.cppreference.com/w/cpp/language/as_if). The _thread-safe interface_ use case is the more prevalent use of `const` in APIs. It is most commonly seen with code that looks like: ```cpp class MyThreadCompatibleType { public: // ... int Size() const { return size; } private: int size; // ... }; void SomeFunction(const MyThreadCompatibleType *thing) { // .... // Users can expect calls to `Size` here to be correct even if running on // multiple threads with a shared `thing`. int thing_size = thing->Size(); // ... } ``` The first can seem like a subset of the second, but this isn't really true. There are cases where `const` works for the first use case but doesn't work well for thread-safety: ```cpp void SomeFunction(...) { // ... // We never want to release or re-allocate `data` and `const` makes sure that // doesn't happen. But the actual data is completely mutable! const std::unique_ptr data = ComputeBigData(); // ... } ``` These two use cases can also lead to tension between shallow const and deep const: - Immutability use cases will tend towards shallow(-er) const, like pointers. - Thread safety use cases will tend towards deep(-er) const. ### Pointers The core of C++'s indirect access to an object stored somewhere else comes from C and its lineage of explicit pointer types. These create an unambiguous separate layer between the pointer object and the pointee object, and introduce dereference syntax (both the unary `*` operator and the `->` operator). C++ makes an important extension to this model to represent _smart pointers_ by allowing the dereference operators to be overloaded. This can be seen across a wide range of APIs such as `std::unique_ptr`, `std::shared_ptr`, `std::weak_ptr`, etc. These user-defined types preserve a fundamental property of C++ pointers: the separation between the pointer object and the pointee object. The distinction between pointer and pointee is made syntactically explicit in C++ both when _dereferencing_ a pointer, and when _forming_ the pointer or taking an object's address. These two sides can be best illustrated when pointers are used for function parameters. The caller code must explicitly take the address of an object to pass it to the function, and the callee code must explicitly dereference the pointer to access the caller-provided object. ### References C++ provides for indirection _without_ the syntactic separation of pointers: references. Because a reference provides no syntactic distinction between the reference and the referenced object--that is their point!--it is impossible to refer to the reference itself in C++. This creates a number of restrictions on their design: - They _must_ be initialized when declared - They cannot be rebound or unbound. - Their address cannot be taken. References were introduced originally to enable operator overloading, but have been extended repeatedly and as a consequence fill a wide range of use cases. Separating these and understanding them is essential to forming a cohesive proposal for Carbon -- that is the focus of the rest of our analysis of references here. #### Special but critical case of `const T&` As mentioned above, one form of reference in C++ has unique properties: `const T&` for some type `T`, or a _`const` reference_. The primary use for these is also the one that motivates its unique properties: a zero-copy way to provide an input function parameter without requiring the syntactic distinction in the caller and callee needed when using a pointer. The intent is to safely emulate passing by-value without the cost of copying. Provided the usage is immutable, this emulation can safely be done with a reference and so a `const` reference fits the bill here. However, to make zero-copy, pass-by-value to work in practice, it must be possible to pass a _temporary_ object. That works well with by-value parameters after all. To make this work, C++ allows a `const` reference to bind to a temporary. However, the rules for parameters and locals are the same in C++ and so this would create serious lifetime bugs. This is fixed in C++ by applying _lifetime extension_ to the temporary. The result is that `const` references are quite different from other references, but they are also quite useful: they are the primary tool used to fill the _immutable view_ use case of `const`. One significant disadvantage of `const` references is that they are observably still references. When used in function parameters, they cannot be implemented with in-register parameters, etc. This complicates the selection of readonly input parameter type for functions, as both using a `const` reference and a by-value parameter force a particular form of overhead. Similarly, range based `for` loops in C++ have to choose between a reference or value type when each would be preferable in different situations. #### R-value references and forwarding references Another special set of use cases for references are R-value and forwarding references. These are used to capture _lifetime_ information in the type system in addition to binding a reference. By doing so, they can allow overload resolution to select C++'s _move semantics_ when appropriate for operations. The primary use case for move semantics in function boundaries was to model _consuming input_ parameters. Because move semantics were being added to an existing language and ecosystem that had evolved exclusively using copies, modeling consumption by moving into a by-value parameter would have forced an eager and potentially expensive copy in many cases. Adding R-value reference parameters and overloading on them allowed code to gracefully degrade in the absence of move semantics -- their internal implementation could minimally copy anything non-movable. These overloads also helped reduce the total number of moves by avoiding moving first into the parameter and then out of the parameter. This kind of micro-optimization of moves was seen as important because some interesting data structures, especially in the face of exception safety guarantees, either implemented moves as copies or in ways that required non-trivial work like memory allocation. Using R-value references and overloading also provided a minor benefit to C++: the lowest-level mechanics of move semantics such as move construction and assignment easily fit into the function overloading model that already existed for these special member functions. These special member functions are just a special case of a more general pattern enabled by R-value references: designing interfaces that use _lifetime overloading_ to _detect_ whether a move would be possible and change implementation strategy based on how they are called. Both the move constructor and the move-assignment operator in C++ work on this principle. However, other use cases for this design pattern are so far rare. For example, Google's C++ style [forbids](https://google.github.io/styleguide/cppguide.html#Rvalue_references) R-value references outside of an enumerated set of use cases, which has been extended incrementally based on demonstrated need, and has now been stable for some time. While overloading on lifetime is one of the allowed use cases, that exemption was added almost four years after the initial exemption of move constructors and move assignment operators. #### Mutable operands to user-defined operators C++ user-defined operators have their operands directly passed as parameters. When these operators require _mutable operands_, references are used to avoid the syntactic overhead and potential semantic confusion of taking their address explicitly. This use case stems from the combined design decisions of having operators that mutate their operands in-place and requiring the operand expression to be directly passed as a normal function parameter. #### User-defined dereference and indexed access syntax C++ also allows user-defined operators that model dereference (or indirecting in the C++ standard) and indexed access (`*` and `[]`). Because these operators specifically model forming an L-value and because the return of the operator definition is directly used as the expression, it is necessary to return a reference to the already-dereferenced object. Returning a pointer would break genericity with builtin pointers and arrays in addition to adding a very significant syntactic overhead. #### Member and subobject accessors Another common use of references is in returns from member functions to provide access to a member or subobject, whether const or mutable. This particular use case is worth calling out specially as it has an interesting property: this is often not a fully indirect access. Instead, it is often simply selecting a particular member, field, or other subobject of the data structure. As a consequence, making subsequent access transparent seems especially desirable. However, it is worth noting that this particular use case is also an especially common source of lifetime bugs. A classic and pervasive example can be seen when calling such a method on a temporary object. The returned reference is almost immediately invalid. #### Non-null pointers A common reason for using mutable references outside of what has already been described is to represent _non-null pointers_ with enforcement in the type system. Because the canonical pointer types in C++ are allowed to be null, systems that forbid a null in the type system use references to induce any null checks to be as early as possible. This causes a "[shift left](https://en.wikipedia.org/wiki/Shift-left_testing)" of handling null pointers, both moving the error closer to its cause logically and increasing the chance of moving earlier in the development process by making it a static property enforced at compile time. References are imperfectly suited to modeling non-null pointers because they are missing many of the fundamental properties of pointers such as being able to rebind them, being able to take their address, etc. Also, references cannot be safely made `const` in the same places that pointers can because that might unintentionally change their semantics by allowing temporaries or extending lifetimes. #### Syntax-free dereference Beyond serving as a non-null pointer, the other broad use case for references is to remove the syntactic overhead of taking an address and dereferencing pointers. In other words, they provide a way to have _syntax-free dereferences_. Outside of function parameters, removing this distinction may provide a genericity benefit, as it allows using the same syntax as would be used with non-references. In theory code could simply use pointers everywhere, but this would add syntactic overhead compared to local variables and references. For immutable accesses, the syntactic overhead seems unnecessary and unhelpful. However, having distinct syntax for _mutable_ iteration, container access, and so on often makes code more readable. There are several cases that have come up in the design of common data structures where the use of distinct syntaxes immutable and mutable operations provides clear benefit: copy-on-write containers where the costs are dramatically different, and associative containers which need to distinguish between looking up an element and inserting an element. This tension should be reflected in how we design _indexed access syntax_. Using mutable references for parameters to reduce syntactic overhead also doesn't seem particularly compelling. For passing parameters, the caller syntax seems to provide significant benefit to readability. When using _non-local_ objects in expressions, the fact that there is a genuine indirection into memory seems to also have high value to readability. These syntactic differences do make inline code and outlined code look different, but that reflects a behavior difference in this case. ================================================ FILE: proposals/p2015.md ================================================ # Numeric type literal syntax [Pull request](https://github.com/carbon-language/carbon-lang/pull/2015) ## Table of contents - [Problem](#problem) - [Background](#background) - [Proposal](#proposal) - [Non-goals](#non-goals) - [Details](#details) - [Syntax](#syntax) - [Usage](#usage) - [Rationale](#rationale) - [Alternatives considered](#alternatives-considered) - [C++ LP64 convention](#c-lp64-convention) - [Type name with length suffix](#type-name-with-length-suffix) - [Uppercase suffixes](#uppercase-suffixes) - [Additional bit sizes](#additional-bit-sizes) ## Problem We want to establish a syntax for fixed-size scalar number types. These types include the two's complement signed integer, the unsigned integer, and the floating-point number. As these types are pervasive throughout the language, our goal here is to align on a terse, convenient, yet understandable, and ergonomic syntax to the author. ## Background For developer convenience, names are given to number types that map to native machine register widths. These sizes typically include 8-bit, 16-bit, 32-bit, 64-bit, and, more recently, 128-bit widths. For example, in [C++11+](https://en.cppreference.com/w/cpp/types/integer), integer types such as `int8_t` (8-bit two's complement signed integer type) and `uint16_t` (16-bit unsigned integer type) exist, among similar types for 32- and 64-bit values. Correspondingly, you have the `i8` and `u16` ([among others](https://doc.rust-lang.org/book/ch03-02-data-types.html#scalar-types)) scalar integer types in Rust. And in Swift, the `Int8` and `UInt16` ([among others](https://developer.apple.com/documentation/swift/uint8)) integer value types. In each case, the intent is to provide a clear and pragmatic syntax. Additional discussion around this proposal's background can be found in [#543](https://github.com/carbon-language/carbon-lang/issues/543). ## Proposal We introduce a simple keyword-like syntax of `iN`, `uN`, and `fN` for two's complement integers, unsigned integers, and floating-point numbers, respectively. Where `N` can be a positive multiple of 8, including the common power-of-two sizes (for example, `N = 8, 16, 32`). We think of these as "type literals" just like `7` is a "numeric literal." This structure follows the successful precedent set by Rust and LLVM development communities and potentially saves 40% or more on characters required compared to other options such as `IntN` (for example, `i16` versus `Int16`). While bit sizes greater than 128-bits will be well-supported, some operations like division will not be available on these large sizes. ### Non-goals - This does not address any considerations around the `bool` type - This does not provide a formal plan for the shape or mapping of the underlying types ([#767 comments](https://github.com/carbon-language/carbon-lang/issues/767#issuecomment-1214153375)) - This does not prescribe an official grammar for parsing these types - This proposal does not address other, non-multiple of 8 bit sizes, such as those used in a bit field ## Details ### Syntax The syntax for a two's complement signed integer, the unsigned integer, and the floating-point number corresponds to a lowercase 'i', 'u', or 'f' character, respectively, indicating the type followed by a numeric value specifying the width. As a regular expression, this can be illustrated as: ```re ([iuf])([1-9][0-9]*) ``` Capture group 1 indicates either an 'i' for a two's complement signed integer type, a 'u' for an unsigned integer type, or an 'f' for an [IEEE-754](https://en.wikipedia.org/wiki/IEEE_754) binary floating-point number type. Capture group 2 specifies the width in bits. Note that this bit width is restricted to a multiple of 8. Examples of this syntax include: - `i16` - A 16-bit two's complement signed integer type - `u32` - A 32-bit unsigned integer type - `f64` - A 64-bit IEEE-754 binary floating-point number type ### Usage ```carbon package sample api; fn Sum(x: i32, y: i32) -> i32 { return x + y; } fn Main() -> i32 { return Sum(4, 2); } ``` In the above example, `Sum` has parameters `x` and `y`, each of which is typed as a 32-bit two's complement signed integer. `Main` then returns the output of `Sum` as a 32-bit two's complement signed integer. ## Rationale Following Carbon's goal to facilitate ["Code that is easy to read, understand, and write"](https://github.com/carbon-language/carbon-lang/blob/trunk/docs/project/goals.md#code-that-is-easy-to-read-understand-and-write), an explicit goal is to provide excellent ergonomics. Highlighting relevant aspects of this from the project goals: - _Carbon should not use symbols that are difficult to type, see, or differentiate from similar symbols in commonly used contexts._ - _Syntax should be easily parsed and scanned by any human in any development environment, not just a machine or a human aided by semantic hints from an IDE._ - _Explicitness must be balanced against conciseness, as verbosity and ceremony add cognitive overhead for the reader, while explicitness reduces the amount of outside context the reader must have or assume._ The type system syntax must also complement Carbon's target for ["Performance-critical software"](https://github.com/carbon-language/carbon-lang/blob/trunk/docs/project/goals.md#performance-critical-software) Specifically, there should be "No need for a lower level language." - _Developers should not need to leave the rules and structure of Carbon, whether to gain control over performance problems or to gain access to hardware facilities._ ## Alternatives considered As discussed in [#543](https://github.com/carbon-language/carbon-lang/issues/543), four other options were considered: ### C++ LP64 convention Where `char` is the 8-bit type, `short` is the 16-bit type, `int` is the 32-bit type, `long` is the 64-bit type. Advantages: - The type name indicates its use to the reader - There is an existing precedent of this pattern in many programming languages, including C++ - In the case of a typo, potentially better compiler checks versus an abbreviated form (for example, `i332`) Disadvantages: - The type names themselves, as compared to the actual width and potentially use often can be arbitrary and confusing - The names themselves can be longer than the other syntax options - Some common C++ implementations use other models, which may create confusion when interoperating with C++ code. For example, Windows uses the LLP64 model, where `long` is a 32-bit type, so Carbon code and C++ on Windows would have different and incompatible definitions for `long`. ### Type name with length suffix Complete type name with a length-specifying suffix - `int8`, `int16`, `int32`, `int64`, `uint32`, `float64`. Advantages: - Are more explicit than an abbreviated version - Stand out against similar variable names, for example, `i8` versus `i = 8`) Disadvantages: - Contain additional verbosity for potentially a non-significant amount of clarity - There are precedents from other communities (for example, Rust) that indicate authors enjoy a more compact syntax ### Uppercase suffixes The suffix can be upper - `Int8`, `UInt8`, `Float16`; `I8`, `U8`, `F16`. Advantages: - May help screen readers distinguish the type Disadvantages: - Can be visually similar to other values, for example, `I8` versus `l8` (second is a lowercase L) ### Additional bit sizes Support for additional bit sizes such as all bit sizes or common powers of two. Advantages: - Adds flexibility and convenience for further use cases such as bit fields Disadvantages: - May increase chances of typos without strong compiler guards, for example, `i32` versus `i22` versus `i23` - Variables such as `i1` and `i2` already exist in C++ code in practice ([example1](https://github.com/google/googletest/blob/main/googlemock/include/gmock/gmock-matchers.h#L878), [example2](https://chromium.googlesource.com/external/github.com/abseil/abseil-cpp/+/HEAD/absl/container/btree_test.cc#2772), [example3](https://sourcegraph.com/search?q=context:global+lang:c%2B%2B+%5Ei1%24+type:symbol&patternType=regexp&case=yes)) - Adds complexity through additional size rules, for example, we can't support pointers to arbitrary bits - Adds confusion in syntactical overlap, for example, `i1`, `il`, `i18`, and `i18n` ================================================ FILE: proposals/p2022.md ================================================ # Unused Pattern Bindings (Unused Function Parameters) [Pull request](https://github.com/carbon-language/carbon-lang/pull/2022) ## Table of contents - [Abstract](#abstract) - [Problem](#problem) - [Background](#background) - [Proposal](#proposal) - [Details](#details) - [The behavior of `unused` name bindings](#the-behavior-of-unused-name-bindings) - [Examples](#examples) - [Rationale](#rationale) - [Alternatives considered](#alternatives-considered) - [Commented names](#commented-names) - [Only short form support with `_`](#only-short-form-support-with-_) - [Named identifiers prefixed with `_`](#named-identifiers-prefixed-with-_) - [Anonymous, named identifiers](#anonymous-named-identifiers) - [Attributes](#attributes) ## Abstract This proposal specifies how unused pattern bindings are written in the Carbon programming language. This is a more general problem statement of "how do users specify unused function parameters" as function parameter declarations are a more specific form of pattern. Related issue: [#1996](https://github.com/carbon-language/carbon-lang/issues/1996). ## Problem "How does a user of Carbon declare an unused pattern binding?" Given that function parameters are a specific type of pattern binding, a more specific question that will have the same answer is "How does a user of Carbon declare an unused function parameter?" Bindings that can be specified as unused makes code explicit and unambiguous. Authors of code can clearly state that a value is not intended to be used. Tools such as compilers or linters can explicitly handle a parameter that is specified as unused, but later used, or a parameter that is unused despite not being specified as unused. ## Background See [the overall design](https://github.com/carbon-language/carbon-lang/blob/09e4417431b77c05ceb2a98dd38833276514a1ff/docs/design/README.md#binding-patterns) for the current state of things with regards to name bindings and the underscore character, `_`. See [issue #476: Optional argument names (unused arguments)](https://github.com/carbon-language/carbon-lang/issues/476) for the previous conversation on this topic. Note: These are not exhaustive lists and not intended to be thoroughly investigated, rather a brief overview of some prior art. C and C++ style guides, linters, and other tools have addressed unused function parameters specifically with varying levels of clarity for readers. - [Standard C++ Foundation's guidelines on unused parameters](https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#Rf-unused) - [Google's C++ style guide recommendations on unused parameters](https://google.github.io/styleguide/cppguide.html#Function_Declarations_and_Definitions) - [C++ `maybe_unused` attribute](https://en.cppreference.com/w/cpp/language/attributes/maybe_unused) - [GCC C `unused` attribute](https://gcc.gnu.org/onlinedocs/gcc-4.7.2/gcc/Function-Attributes.html#index-g_t_0040code_007bunused_007d-attribute_002e-2640) More generally, various languages use the underscore character (`_`) to signal an unused binding or pattern. - [Rust `_` Patterns](https://doc.rust-lang.org/book/ch18-03-pattern-syntax.html#ignoring-values-in-a-pattern) - [Golang Blank Identifiers](https://go.dev/ref/spec#Blank_identifier) - [PyLint allows parameters starting with `_`, `ignored_`, or `unused_` to be unused](https://pylint.pycqa.org/en/latest/user_guide/configuration/all-options.html#ignored-argument-names) - [Scala Wildcard Patterns](https://www.scala-lang.org/files/archive/spec/2.11/10-pattern-matching.html#variable-patterns) - [Crystal `_` Cases](https://crystal-lang.org/reference/1.5/syntax_and_semantics/case.html#underscore-allowed) ## Proposal Users can explicitly declare a value in a pattern as unused in two forms: - `_: i32` : Using the underscore token, `_`, in place of a name. - `unused size: i32` : Using the leading `unused` keyword followed by the name. ## Details Introducing two syntaxes satisfies the desire to have a terse way to discard values, but still provides authors with a more verbose, explicit syntax that preserves the name. Both approaches are unambiguous -- to human readers and authors as well as programmatic interpretations. The inclusion of an explicit `unused` keyword allows authors to preserve the name of a value for documentation purposes, while still explicitly marking the value as discarded in an interpretable way to humans and programs alike. The `unused` keyword would be a new keyword in Carbon. This keyword would only be valid when preceding a name in a pattern binding and the keyword would tightly bind to the following name, disallowing specifying an entire sub-pattern as `unused`. ### The behavior of `unused` name bindings Names that are qualified with the `unused` keyword are visible for name lookup but uses are invalid, including when they cause ambiguous name lookup errors. If attempted to be used, a compiler error will be shown to the user, instructing them to either remove the `unused` qualifier or remove the use. The inverse, where a name is not qualified by the `unused` qualifier, but never used, will cause the compiler to emit a warning diagnostic, informing the user that a given name was not used and suggesting to either remove the binding or mark it as unused. ### Examples Examples with an unused function parameter ```carbon // Function declaration (may be in API file) fn Sum(x: List(i32), size: i32) -> i32; // Implementation that doesn't use the size parameter fn Sum(x: List(i32), _: i32) -> i32 { ... } // or: fn Sum(x: List(i32), unused size: i32) -> i32 { ... } ``` Examples with an unused variable ```carbon fn Bar() -> (i32, i32); fn Foo() -> i32 { var (x: i32, _: i32) = Bar(); // or: var (x: i32, unused y: i32) = Bar(); return x; } ``` Examples with an unused case binding ```carbon fn Bar() -> (i32, i32); fn Foo() -> i32 { match (Bar()) { case (42, y: i32) => { return y; } case (x: i32, _: i32) => { return x; } // or: case (x: i32, unused y: i32) => { return x; } } } ``` ## Rationale - This proposal supports the Carbon goal of having [Code that is easy to read, understand, and write](/docs/project/goals.md#code-that-is-easy-to-read-understand-and-write) - Carbon should not use symbols that are difficult to type, see, or differentiate from similar symbols in commonly used contexts. - Syntax should be easily parsed and scanned by any human in any development environment, not just a machine or a human aided by semantic hints from an IDE. - Explicitness must be balanced against conciseness, as verbosity and ceremony add cognitive overhead for the reader, while explicitness reduces the amount of outside context the reader must have or assume. This proposal uses the underscore, `_`, character to denote an unused value, a meaning used across various other programming languages. A lone underscore character only has a single meaning in Carbon and will be unambiguous across contexts. Both syntaxes are easily read by humans, either by seeing the `_` character alone, or by introducing a keyword that allows code to read naturally such as `unused size : i32`. The inclusion of two syntaxes allows authors to decide when they will favor conciseness or explicitness. ## Alternatives considered ### Commented names C++ allows function parameters to be unnamed, allowing function declarations such as ``` int Foo(int) { ... } int Foo(int /*unused_name*/) { ... } ``` Advantages: - Consistency with C++ Disadvantages: - Carbon does not intend to support `/* */` comments, so this option is effectively a non-starter ### Only short form support with `_` Carbon could provide only a single way to discard a value with the underscore, `_`, token ``` // Function declaration (may be in API file) fn Sum(x: List(i32), size: i32) -> i32; // Implementation that doesn't use the size parameter fn Sum(x: List(i32), _: i32) -> i32 { ... } ``` Advantages: - A smaller language, one less keyword, and a single way to write code Disadvantages: - Less expressiveness, less documentation through names ### Named identifiers prefixed with `_` Carbon could treat identifiers prefixed with `_` as unused identifiers and discard their names. ``` // Function declaration (may be in API file) fn Sum(x: List(i32), size: i32) -> i32; // Implementation that doesn't use the size parameter fn Sum(x: List(i32), _size: i32) -> i32 { ... } ``` Advantages: - Reuse of the underscore character in both short and long form bindings - Functionally similar to commented names in C++ Disadvantages: - Tying the semantics of a name being unused to the particular spelling of the identifier (starting with a `_` character), even while it remains an identifier seems more subtle and indirect than necessary. - We shouldn't use identifier spellings as a side-channel for semantic information. - Semantics, including used versus unused, should be conveyed in an orthogonal manner to the name rather than tying them together. - Would require the name to change when shifting between being used or unused, which could undermine some of its utility such as remaining for annotations, metaprogramming, or diagnostics. ### Anonymous, named identifiers A potential syntax for naming unused bindings while retaining the underscore, `_`, token is an optional name suffix following the underscore token. ``` // Function declaration (may be in API file) fn Sum(x: List(i32), size: i32) -> i32; // Implementation that doesn't use the size parameter fn Sum(x: List(i32), _ size: i32) -> i32 { ... } ``` Advantages: - Reuse of the underscore token in both short and long form bindings Disadvantages: - The underscore token, while consistent, may be less human readable than a dedicated keyword such as `unused` ### Attributes While attributes aren't designed yet, there's a possibility that in the future, the Carbon language will have some mechanism to attach metadata to parts of source code to inform readers, compilers, and other tools. Its conceivable that there could be an `unused` attribute which could be use to implement similar semantics as the proposed `unused` keyword. Advantages: - Opens the inspection and subsequent actions taken to whatever ecosystem and programmatic support is introduced by attributes. - Solves a specific problem with a generic approach - Removes the introduction of a new keyword Disadvantages: - Given the current proposal where `unused` bindings specify unambiguous, absolute behavior for the compiler's handling of names, similar to the `private` keyword, using attributes as the transport for this semantic information is indirect. Given that attributes are not designed and may go in a number of unknown directions, it might be worth revisiting this option once attributes are fully designed. ================================================ FILE: proposals/p2040.md ================================================ # Unicode escape code length [Pull request](https://github.com/carbon-language/carbon-lang/pull/2040) ## Table of contents - [Abstract](#abstract) - [Problem](#problem) - [Background](#background) - [Proposal](#proposal) - [Rationale](#rationale) - [Alternatives considered](#alternatives-considered) - [Allow zero digits](#allow-zero-digits) - [Allow any number of hexadecimal characters](#allow-any-number-of-hexadecimal-characters) - [Limiting to 6 digits versus 8](#limiting-to-6-digits-versus-8) ## Abstract The `\u{HHHH...}` can be an arbitrary length, potentially including `\u{}`. Restrict to 1 to 8 characters. ## Problem [Proposal #199: String literals](https://github.com/carbon-language/carbon-lang/pull/199) says "any number of hexadecimal characters" is valid for `\u{HHHH}`. This is undesirable, because it means `\u{000 ... 000E9}` is a valid escape sequence, for any number of `0` characters. Additionally, it's not clear if `\u{}` is meant to be valid. ## Background [Proposal #199: String literals](https://github.com/carbon-language/carbon-lang/pull/199) says: > As in JavaScript, Rust, and Swift, Unicode code points can be expressed by > number using `\u{10FFFF}` notation, which accepts any number of hexadecimal > characters. Any numeric code point in the ranges > 016-D7FF16 or E00016-10FFFF16 can > be expressed this way. When it comes to the number of digits, the languages differ: - In [JavaScript](https://262.ecma-international.org/13.0/#prod-CodePoint), between 1 and 6 digits are supported, and it must be less than or equal to `10FFFF`. - In [Rust](https://doc.rust-lang.org/reference/tokens.html), between 1 and 6 digits are supported. - In [Swift](https://docs.swift.org/swift-book/LanguageGuide/StringsAndCharacters.html), between 1 and 8 digits are supported. Unicode's codespace is 0 to [`10FFFF`](https://unicode.org/glossary/#codespace). ## Proposal The `\u{H...}` syntax is only valid for 1 to 8 unicode characters. ## Rationale - [Code that is easy to read, understand, and write](/docs/project/goals.md#code-that-is-easy-to-read-understand-and-write) - This restriction does not affect the ability to write valid Unicode. Instead, it restricts the ability to write confusing or invalid unicode, which should make it easier to detect errors. - [Fast and scalable development](/docs/project/goals.md#fast-and-scalable-development) - Simplifies tooling by reducing the number of syntaxes that need to be supported, and allowing early failure on obviously invalid inputs. ## Alternatives considered ### Allow zero digits We could allow `\u{}` as a version of `\u{0}`. However, as shorthand, it doesn't save much and `\x00` is both equal length and clearer. Rather than allowing this syntax, we prefer to disallow it for consistency with other languages. ### Allow any number of hexadecimal characters We could allow any number of digits in the `\u` escape. However, this has the consequence of requiring parsing of escapes of completely arbitrary length. This creates unnecessary complexity in the parser because we need to consider what happens if the result is greater than 32 bits, significantly larger than unicode's current `10FFFF` limit. One way to do this would be to store the result in a 32-bit integer and keep parsing until the value goes above `10FFFF`, then error as invalid if that's exceeded. This would allow an arbitrary number of leading `0`'s to correctly parse. It should make it easier to write a simple parser if we instead limit the number of digits to a reasonable amount. ### Limiting to 6 digits versus 8 A limit of 6 digits offers a reasonable limit as the minimum needed to represent Unicode's codespace. A limit of 8 digits offers a reasonable limit as a standard 4-byte value, and roughly matches UTF-32. While it seems a weak advantage, this proposal leans towards 8. ================================================ FILE: proposals/p2107.md ================================================ # Clarify rules around `Self` and `.Self` [Pull request](https://github.com/carbon-language/carbon-lang/pull/2107) ## Table of contents - [Abstract](#abstract) - [Problem](#problem) - [Background](#background) - [Proposal](#proposal) - [Details](#details) - [Rationale](#rationale) - [Alternatives considered](#alternatives-considered) - [`Self` not a keyword](#self-not-a-keyword) - [Make `Self` a member of all types](#make-self-a-member-of-all-types) - [`where` operator could be associative](#where-operator-could-be-associative) ## Abstract A number of smaller changes grouped together in one proposal: - Make `Self` a keyword. - Clarify that `Self` refers to the current type in a base class and in impl declarations. - Clarify when `.Self` is legal, and what type it has. - Also specify that `where` is not an associative operator. ## Problem There were a number of gaps found in the design when @zygoloid went to implement these features in [the explorer codebase](https://github.com/carbon-language/carbon-lang/tree/76c68153a2cf5b35249be3e9b8097238f3dc1ee1/explorer), for example [#1311: Basic support for `.Self` within `:!` bindings and `where` expressions](https://github.com/carbon-language/carbon-lang/pull/1311). ## Background `Self` was introduced for interfaces and implementations in [#524: Generics overview](https://github.com/carbon-language/carbon-lang/pull/524) and [#553: Generics details part 1](https://github.com/carbon-language/carbon-lang/pull/553). `Self` was introduced for class types and methods in [#494: Method syntax](https://github.com/carbon-language/carbon-lang/issues/494) and [#722: Nominal classes and methods](https://github.com/carbon-language/carbon-lang/pull/722). Constraints using `where` and `.Self` were introduced in [#818: Constraints for generics (generics details 3)](https://github.com/carbon-language/carbon-lang/pull/818). The use of `where` to set associated types was introduced in [#1013: Generics: Set associated constants using `where` constraints](https://github.com/carbon-language/carbon-lang/pull/1013). The type of `.Self` and where it would be introduced grammatically was discussed [#generics channel on Discord on 2022-06-07](https://discord.com/channels/655572317891461132/708431657849585705/1013904969335836732). ## Proposal This proposal implements a number of changes and clarifications about the use of `Self` and `.Self` with generics: - `Self` is now a keyword, and may not be used as an identifier even in contexts where `Self` has no meaning. If `Self` is used in a C++ API, Carbon will use the same mechanism for interop as other Carbon keywords. - Clarify that `Self` always refers to the current type, even for virtual functions implemented in a derived class, not the type implementing the method. - `Self` in an `impl` declaration may be used after the `as` to refer to the type being implemented. This could be the type named before `as` when specified, otherwise it is the enclosing type. - Clarify that `.Self` is legal after `:!` and `where`, as long as it only refers to a single type variable. - Specify the type of `.Self` as `Type` after `:!`, or `MyConstraint` after `MyConstraint where`. In addition, this proposal specifies that `where` is not an associative operator. ## Details `Self` was added as a keyword to [`docs/design/lexical_conventions/words.md`](/docs/design/lexical_conventions/words.md). The other rules were added to [`docs/design/classes.md`](/docs/design/classes.md) and [`docs/design/generics/details.md`](/docs/design/generics/details.md). ## Rationale This proposal fills in gaps with an aim to make things consistent and simplify by having fewer rules where possible. For example, the rule saying that it is an error if `.Self` could mean two different things is consistent with other name lookup rules, such as those from [#989: Member access expressions](https://github.com/carbon-language/carbon-lang/pull/989) and [#2070: Always `==` not `=` in `where` clauses](https://github.com/carbon-language/carbon-lang/pull/2070). Simplicity benefits Carbon's [language tools and ecosystem](/docs/project/goals.md#language-tools-and-ecosystem) and consistency leads to [code that is easy to read, understand, and write](/docs/project/goals.md#code-that-is-easy-to-read-understand-and-write). ## Alternatives considered ### `Self` not a keyword An alternative considered was forbidding identifiers to be equal to `Self`. The big concern with that approach is that we would need some other way to interoperate with C++ code, particularly classes, that had a `Self` member. If we were adding `Self` to the language later as part of evolution, we would make it a keyword. That would allow us to use the same evolution strategy as other keywords -- we would automatically update the code to change existing uses of `Self` to the raw identifier syntax. Note that at this time [no raw identifier syntax has been approved](https://github.com/carbon-language/carbon-lang/pull/93), but Rust uses a `r#` prefix. If Carbon used the same syntax, existing uses of `Self` would be changed to `r#Self`, and so `r#Self` should still be a legal identifier. ### Make `Self` a member of all types We considered making `Self` a member of all types. From this uses of `Self` and `.Self` would follow naturally. It would have other consequences as well: - `T.Self == T` for all types `T`. - `x.Self`, where `x` is a non-type value with type `T`, would be `T`. This is because under the normal member-access rules, since `Self` is not a member of `x`, it would look in `T` and find `T.Self`. This raised the question of whether `Self` is a member of type-of-types like `Type`. That would seem to introduce an ambiguity for `i32.Self`. Furthermore, using `x.Self` to get the type of `x` seemed tricky, it would be better to have something that used the word "type" to do that. Since [`Self` is a keyword](#self-not-a-keyword), we don't need to make it follow the normal member-access rules. So we instead only define what it means in places where we have a use case. This was discussed [on 2022-08-29 in the #typesystem channel in Discord](https://discord.com/channels/655572317891461132/708431657849585705/1013904969335836732). ### `where` operator could be associative We considered making the `where` operator associative, since an expression like ``` Interface1 where .AssocType1 is Interface2 where .AssocType2 == i32 ``` would more usefully be interpreted as: ``` Interface1 where .AssocType1 is (Interface2 where .AssocType2 == i32) ``` than the alternative. However, this is expected to be a rare case and so it seemed much friendlier to humans to require parentheses or a separate named constraint declaration. This way they can easily visually disambiguate how it is interpreted without having to remember a rule that won't commonly be relevant. This was discussed in [the #syntax channel on Discord on 2022-05-27](https://discord.com/channels/655572317891461132/709488742942900284/979869282903130153) and [the weekly sync meeting on 2022-06-01](https://docs.google.com/document/d/1dwS2sJ8tsN3LwxqmZSv9OvqutYhP71dK9Dmr1IXQFTs/edit?resourcekey=0-NxBWgL9h05yD2GOR3wUisg#heading=h.qarzfirrcrgf). ================================================ FILE: proposals/p2138.md ================================================ # Checked and template generic terminology [Pull request](https://github.com/carbon-language/carbon-lang/pull/2138) ## Table of contents - [Abstract](#abstract) - [Problem](#problem) - [Proposal](#proposal) - [Rationale](#rationale) - [Alternatives considered](#alternatives-considered) - [Status quo: "generic" means "checked"](#status-quo-generic-means-checked) - [Use the name "templates" instead of "template generics"](#use-the-name-templates-instead-of-template-generics) - [Use the name "unchecked generics" instead of "template generics"](#use-the-name-unchecked-generics-instead-of-template-generics) ## Abstract Change terminology from "generics" and "templates" to "checked generics" and "template generics". Afterwards, "generics" will be an umbrella term that include templates. ## Problem The C++ community does "generic programming" using templates in C++, and so thinks of templates as a mechanism for implementing generics. In Carbon, templates and other generic features are more similar than they are different, so it is worthwhile to have an easy way to talk about things that apply to both. ## Proposal Change terminology from "generics" and "templates" to "checked generics" and "template generics". Afterwards, "generics" will be an umbrella term that include templates. Similarly, for parameters, use "checked generic parameter" and "template generic parameter", with the umbrella term "generic parameter" describing both kinds. The word "generic" can be omitted from these terms if it's clear from context, which will usually be the case for template parameters. Some existing design docs give examples of the difference before and after this proposal: - The [generics overview](/docs/design/generics/overview.md), for example, currently demonstrates the current approach of using "generics" and "templates". - The [generics section of the design overview](/docs/design/README.md#generics) demonstrates the use of the proposed new terminology. ## Rationale Using the terminology expected by the community supports these goals: - [Code that is easy to read, understand, and write](/docs/project/goals.md#code-that-is-easy-to-read-understand-and-write) since our documentation governing the interpretation of that code will be more easily understood and with greater accuracy. - [Interoperability with and migration from existing C++ code](/docs/project/goals.md#interoperability-with-and-migration-from-existing-c-code) since there will be a smaller terminology gap between Carbon and C++. ## Alternatives considered ### Status quo: "generic" means "checked" Use "generic" for checked generics, "template" for template generics, and "template or generic" when we mean both. Advantages: - The terms "generic" and "template" are more concise than "checked generic" and "template generic". - The term "template" is more familiar than "template generic" to people coming from C++. Disadvantages: - Does not provide a unifying term for templates and generics, leading to our needing to talk about both in various places, given that they are very similar concepts in Carbon. - Does not acknowledge that templates are used for generic programming and as such are a kind of generic. - Carbon's template generics aren't exactly the same as C++'s templates, so this may give the wrong intuition for people coming from C++ in some cases. ### Use the name "templates" instead of "template generics" Use "checked generic" for checked generics, "template" for template generics, and "generic" when we mean both. Advantages: - More concise term than in this proposal. - More familiar to people coming from C++. - People will likely use this term anyway rather than saying "template generics". Disadvantages: - Does not parallel the term "checked generics". - Loses the implication that a template generic is a kind of generic. - Carbon's template generics aren't exactly the same as C++'s templates, so this may give the wrong intuition for people coming from C++ in some cases. ### Use the name "unchecked generics" instead of "template generics" Use "checked generic" for checked generics, "unchecked generic" for template generics, and "generic" when we mean both. Advantages: - Creates a better parallel between "checked generics" and "unchecked generics". - Describes the semantics rather than the implementation strategy, especially given that templating / monomorphization is also the implementation strategy we intend to use for unchecked generics. Disadvantages: - Unfamiliar to people coming from C++. - Would either need to rename the `template` keyword or pick new non-keyword syntax for it, or have a mismatch between keywords and terminology. - Template generics still involve checking, it just can't complete until later when the argument values are known. This was suggested in [#1443](https://github.com/carbon-language/carbon-lang/pull/1443). ================================================ FILE: proposals/p2173.md ================================================ # Associated constant assignment versus equality [Pull request](https://github.com/carbon-language/carbon-lang/pull/2173) ## Table of contents - [Abstract](#abstract) - [Problem](#problem) - [Equal types with different interfaces](#equal-types-with-different-interfaces) - [Type canonicalization](#type-canonicalization) - [Transitivity of equality](#transitivity-of-equality) - [Coherence](#coherence) - [Background](#background) - [Proposal](#proposal) - [Details](#details) - [Rewrite constraints](#rewrite-constraints) - [Combining constraints with `&`](#combining-constraints-with-) - [Combining constraints with `and`](#combining-constraints-with-and) - [Combining constraints with `extends`](#combining-constraints-with-extends) - [Combining constraints with `impl as` and `is`](#combining-constraints-with-impl-as-and-is) - [Constraint resolution](#constraint-resolution) - [Precise rules and termination](#precise-rules-and-termination) - [Qualified name lookup](#qualified-name-lookup) - [Type substitution](#type-substitution) - [Examples](#examples) - [Termination](#termination) - [Same type constraints](#same-type-constraints) - [Implementation of same-type `ImplicitAs`](#implementation-of-same-type-implicitas) - [Observe declarations](#observe-declarations) - [Implementing an interface](#implementing-an-interface) - [Ergonomics](#ergonomics) - [Implementation experience](#implementation-experience) - [Rationale](#rationale) - [Alternatives considered](#alternatives-considered) - [Status quo](#status-quo) - [Restrict constraints to allow computable type equality](#restrict-constraints-to-allow-computable-type-equality) - [Find a fully transitive approach to type equality](#find-a-fully-transitive-approach-to-type-equality) - [Different syntax for rewrite constraint](#different-syntax-for-rewrite-constraint) - [Different syntax for same-type constraint](#different-syntax-for-same-type-constraint) - [Required ordering for rewrites](#required-ordering-for-rewrites) - [Multi-constraint `where` clauses](#multi-constraint-where-clauses) - [Rewrite constraints in `impl as` constraints](#rewrite-constraints-in-impl-as-constraints) ## Abstract Split the `where A == B` constraint in two: `where .A = B` produces a new constraint from an old one, where the value of `.A` in the new constraint is known and eagerly rewritten to `B`, and `where A == B`, which does not cause `A` and `B` to be considered as identical by language rules but does permit implicit (no-op) conversion between them. This aims to provide an efficiently-computable and human-understandable type equality rule, with type canonicalization and therefore transitive type equality, without sacrificing too much in the way of ergonomics and without sacrificing determinism, while still providing the full power of a general type constraint system in a less ergonomic form. ## Problem ### Equal types with different interfaces When an associated type is constrained to be a concrete type, it is desirable for the associated type to behave like that concrete type: ``` interface Hashable { fn Hash[me: Self]() -> u128; } interface X { let R:! Hashable; fn Make() -> R; } fn F[T:! X where .R == i32](x: T) -> i32 { return T.Make() + 1; } ``` Here, we want to be able to use the result of `F.Make()` in all regards like an `i32`. Assuming an `i32.Abs` function exists, and `i32` implements `Hashable` externally, it is more desirable for `F.Make().Abs()` to work than it is for `F.Make().Hash()` to work. We are [currently aiming to address this](https://github.com/carbon-language/carbon-lang/pull/2070) by saying that when two type expressions are constrained to be equal, a value whose type is either of those type expressions provides the (disjoint) union of their interfaces. This leads to a surprise: ``` fn G[T:! X where .R == i32](x: T) -> u128 { let n: i32 = 1; let m: i64 = 2; return n.Hash() ^ m.Hash(); } ``` Here, the call to `n.Hash()` is valid but the call to `m.Hash()` is invalid, because the type `i32` of `n` is constrained to be equal to `T.R` which is of type `Hashable`, but the type `i64` is not constrained in that way and does not implement `Hashable` internally. This suggests that there might be two different behaviors that we want when adding a constraint: either we are changing the interface, or not. This is analogous to internal versus external implementation of an interface. ### Type canonicalization There are a variety of contexts in which types are compared for equality. In some of those contexts, it might be acceptable to perform a search and a computation, and it might be acceptable to have false negatives. For example, when analyzing an argument being passed to a function in a generic, it might be OK to say "we can't prove that the argument has a type that is equal to (or convertible to) the type of the parameter", so long as the developer has some way to demonstrate that the types are the same if that is indeed the case. However, not all comparisons of generic types have that property. As an extreme and hopefully avoidable example, suppose a C++-like linkage model is used, with name mangling. We may need to determine whether two type expressions are equal at link time, by reducing them both to strings that are guaranteed to be identical if the types are equal and non-identical otherwise. This requires some form of type canonicalization: reducing all equal type expressions to some common representation where we can faithfully perform equality comparisons. Even if the semantics of Carbon don't require canonicalization, it is still a very useful property for implementation purposes. For example, if type canonicalization is possible to perform efficiently, building a data structure mapping from a type to some value becomes much simpler, and if the representation of a type expression carries its canonical form then type equality comparisons can be done in constant time. ### Transitivity of equality For ergonomic purposes, it is valuable for the notion of type expression equality to be transitive: if type A is known to be the same as type B, and type B is known to be the same as type C, then shouldn't we be able to use an A where a C is required? See [#1369](https://github.com/carbon-language/carbon-lang/issues/1369) for an explanation of why this is important. However, if we permit arbitrary constraints and have a transitive equality rule, then type equality is isomorphic to the [word problem for finitely-presented monoids](https://en.wikipedia.org/wiki/Word_problem_for_groups), which is undecidable, and certainly not computable, in general. Therefore we must impose a restriction somewhere: either not all constraints are permissible, or we do not have transitivity, or we impose some other constraint on the language to avoid the hard cases. Some languages merely impose some kind of depth limit on their search for a proof of equality. Note that if we can perform type canonicalization, then type equality is necessarily transitive: if all equal _pairs_ of types canonicalize to the same representation, then _all_ equal types canonicalize to the same representation transitively. ### Coherence In order to avoid surprises in the language behavior, we want Carbon to have the property that learning more about the type of a value should not cause the behavior of an expression to change from one valid behavior to another valid behavior. It's important to distinguish here between learning more about a type, such as learning that it implements a particular interface – for example, from an added import – and changing the type, such as by writing a different expression after a `:` or `:!`. Changing the type of a value is expected to be able to change the behavior of an expression using that value. ## Background There has been a lot of work in this space. Some previous relevant proposals: - [#731: generics details 2](https://github.com/carbon-language/carbon-lang/pull/731) introduced associated types, with the intent that a mechanism to constrain their value would be provided in a later proposal. - [#818: generics details 3](https://github.com/carbon-language/carbon-lang/pull/818) introduced `where` constraints for generics, with an open question for whether and how we might restrict constraints in order to give a decidable type equality rule. - #818 represents the status quo of approved proposals. It has both `=` and `==` where constraints, which are similar to, but somewhat different from, those in this proposal. - `=` constraints require a concrete type on their right-hand side. The left-hand side is not restricted. The facet type of the left-hand side type is changed to match the right-hand side. - `==` constraints do not affect the facet type of either operand. - As an answer to the question of how to produce a decidable type equality rule, both kinds of constraints are applied automatically, but only at a depth of one constraint. Neither form is transitive but both can be transitively extended using `observe` declarations. - [#2070: always `==` not `=` in `where` clauses](https://github.com/carbon-language/carbon-lang/pull/2070) describes an attempt to unify the syntax and semantics of `=` and `==` constraints, under which we would always merge the facet types of the operands. That proposal has not yet been accepted, and this proposal intends to supersede it. ## Proposal Replace the semantics of the existing `where A == B` and `where A = B` constraints and restrict the syntax of `where A = B` as follows: - `where .A = T` specifies a _rewrite_ constraint. The left-hand side is required to name an associated constant. If that associated constant `.A` is named as a member of a type declared with this constraint, it is rewritten to `T`. This means that the interface of the type changes, and the behavior is in all respects as if `T` had been specified directly. - `where X == Y` specifies a _same-type_ constraint. This is treated much like `where X is SameAs(Y)`. Here, `SameAs` is a hypothetical constraint that is reflexive (`T is SameAs(T)` for all `T`), commutative (`T is SameAs(U)` if and only if `U is SameAs(T)`), and not implementable by the developer. Moreover, if `F` is any pure type function, `T is SameAs(U)` implies `F(T) is SameAs(F(U)`). `observe` statements can be used to form transitive `SameAs` relations. Same-type constraints are not used automatically by the language rules for any purpose, but a blanket `ImplicitAs` impl permits conversions between types that are known to be the same: ``` impl forall [T:! type, U:! type where .Self == T] T as ImplicitAs(U); ``` A type implements `C where .A = T` if and only if it implements `C where .A == T`, assuming both constraints are valid – the inhabitants of these two facet types are the same, but they are different facet types and provide different interfaces for corresponding type values. ## Details ### Rewrite constraints In a rewrite constraint, the left-hand operand of `=` must be a `.` followed by the name of an associated constant. `.Self` is not permitted. ``` interface RewriteSelf { // ❌ Error: `.Self` is not the name of an associated constant. let Me:! type where .Self = Self; } interface HasAssoc { let Assoc:! type; } interface RewriteSingleLevel { // ✅ Uses of `A.Assoc` will be rewritten to `i32`. let A:! HasAssoc where .Assoc = i32; } interface RewriteMultiLevel { // ❌ Error: Only one level of associated constant is permitted. let B:! RewriteSingleLevel where .A.Assoc = i32; } ``` This notation is permitted anywhere a constraint can be written, and results in a new constraint with a different interface: the named member effectively no longer names an associated constant of the constrained type, and is instead treated as a rewrite rule that expands to the right-hand side of the constraint, with any mentioned parameters substituted into that type. ``` interface Container { let Element:! type; let Slice:! Container where .Element = Element; fn Add[addr me: Self*](x: Element); } // `T.Slice.Element` rewritten to `T.Element` // because type of `T.Slice` says `.Element = Element`. // `T.Element` rewritten to `i32` // because type of `T` says `.Element = i32`. fn Add[T:! Container where .Element = i32](p: T*, y: T.Slice.Element) { // ✅ Argument `y` has the same type `i32` as parameter `x` of // `T.(Container.Add)`, which is also rewritten to `i32`. p->Add(y); } ``` Rewrites aren't performed on the left-hand side of such an `=`, so `where .A = .B and .A = C` is not rewritten to `where .A = .B and .B = C`. Instead, such a `where` clause is invalid when the constraint is [resolved](#constraint-resolution) unless each rule for `.A` specifies the same rewrite. Note that `T:! C where .R = i32` can result in a type `T.R` whose behavior is different from the behavior of `T.R` given `T:! C`. For example, member lookup into `T.R` can find different results and operations can therefore have different behavior. However, this does not violate coherence because the facet types `C` and `C where .R = i32` don't differ by merely having more type information; rather, they are different facet types that have an isomorphic set of values, somewhat like `i32` and `u32`. An `=` constraint is not merely learning a new fact about a type, it is requesting different behavior. ### Combining constraints with `&` Suppose we have `X = C where .R = A` and `Y = C where .R = B`. What should `C & X` produce? What should `X & Y` produce? We could perhaps say that `X & Y` results in a facet type where the type of `R` has the union of the interface of `A` and the interface of `B`, and that `C & X` similarly results in a facet type where the type of `R` has the union of the interface of `A` and the interface originally specified by `C`. However, this proposal suggests a simpler rule: - Combining two rewrite rules with different rewrite targets results in a facet type where the associated constant is ambiguous. Given `T:! X & Y`, the type expression `T.R` is ambiguous between a rewrite to `A` and a rewrite to `B`. But given `T:! X & X`, `T.R` is unambiguously rewritten to `A`. - Combining a constraint with a rewrite rule with a constraint with no rewrite rule preserves the rewrite rule. For example, supposing that `interface Container` extends `interface Iterable`, and `Iterable` has an associated constant `Element`, the constraint `Container & (Iterable where .Element = i32)` is the same as the constraint `(Container & Iterable) where .Element = i32` which is the same as the constraint `Container where .Element = i32`. If the rewrite for an associated constant is ambiguous, the facet type is rejected during [constraint resolution](#constraint-resolution). ### Combining constraints with `and` It's possible for one `=` constraint in a `where` to refer to another. When this happens, the facet type `C where A and B` is interpreted as `(C where A) where B`, so rewrites in `A` are applied immediately to names in `B`, but rewrites in `B` are not applied to names in `A` until the facet type is [resolved](#constraint-resolution): ``` interface C { let T:! type; let U:! type; let V:! type; } class M { alias Me = Self; } // ✅ Same as `C where .T = M and .U = M.Me`, which is // the same as `C where .T = M and .U = M`. fn F[A:! C where .T = M and .U = .T.Me]() {} // ❌ No member `Me` in `A.T:! type`. fn F[A:! C where .U = .T.Me and .T = M]() {} ``` ### Combining constraints with `extends` Within an interface or named constraint, `extends` can be used to extend a constraint that has rewrites. ``` interface A { let T:! type; let U:! type; } interface B { extends A where .T = .U and .U = i32; } var n: i32; // ✅ Resolved constraint on `T` is // `B where .(A.T) = i32 and .(A.U) = i32`. // `T.(A.T)` is rewritten to `i32`. fn F(T:! B) -> T.(A.T) { return n; } ``` ### Combining constraints with `impl as` and `is` Within an interface or named constraint, the `impl T as C` syntax does not permit `=` constraints to be specified directly. However, such constraints can be specified indirectly, for example if `C` is a named constraint that contains rewrites. Because these constraints don't change the type of `T`, rewrite constraints in this context will never result in a rewrite being performed, and instead are equivalent to `==` constraints. ``` interface A { let T:! type; let U:! type; } constraint C { extends A where .T = .U and .U = i32; } constraint D { extends A where .T == .U and .U == i32; } interface B { // OK, equivalent to `impl as D;` or // `impl as A where .T == .U and .U == i32;`. impl as C; } var n: i32; // ❌ No implicit conversion from `i32` to `T.(A.T)`. // Resolved constraint on `T` is // `B where T.(A.T) == T.(A.U) and T.(A.U) = i32`. // `T.(A.T)` is single-step equal to `T.(A.U)`, and // `T.(A.U)` is single-step equal to `i32`, but // `T.(A.T)` is not single-step equal to `i32`. fn F(T:! B) -> T.(A.T) { return n; } ``` A purely syntactic check is used to determine if an `=` is specified directly in an expression: - An `=` constraint is specified directly in its enclosing `where` expression. - If an `=` constraint is specified directly in an operand of an `&` or `(`...`)`, then it is specified directly in that enclosing expression. In an `impl as C` or `impl T as C` declaration in an interface or named constraint, `C` is not allowed to directly specify any `=` constraints: ``` // Compile-time identity function. fn Identity[T:! type](x:! T) -> T { return x; } interface E { // ❌ Rewrite constraint specified directly. impl as A where .T = .U and .U = i32; // ❌ Rewrite constraint specified directly. impl as type & (A where .T = .U and .U = i32); // ✅ Not specified directly, but does not result // in any rewrites being performed. impl as Identity(A where .T = .U and .U = i32); } ``` The same rules apply to `is` constraints. Note that `.T == U` constraints are also not allowed in this context, because the reference to `.T` is rewritten to `.Self.T`, and `.Self` is ambiguous. ``` // ❌ Rewrite constraint specified directly in `is`. fn F[T:! A where .U is (A where .T = i32)](); // ❌ Reference to `.T` in same-type constraint is ambiguous: // does this mean the outer or inner `.Self.T`? fn G[T:! A where .U is (A where .T == i32)](); // ✅ Not specified directly, but does not result // in any rewrites being performed. Return type // is not rewritten to `i32`. fn H[T:! type where .Self is C]() -> T.(A.U); // ✅ Return type is rewritten to `i32`. fn I[T:! C]() -> T.(A.U); ``` ### Constraint resolution When a facet type is used as the declared type of a type `T`, the constraints that were specified within that facet type are _resolved_ to determine the constraints that apply to `T`. This happens: - When the constraint is used explicitly, when declaring a generic parameter or associated constant of the form `T:! Constraint`. - When declaring that a type implements a constraint with an `impl` declaration, such as `impl T as Constraint`. Note that this does not include `impl ... as` constraints appearing in `interface` or `constraint` declarations. In each case, the following steps are performed to resolve the facet type's abstract constraints into a set of constraints on `T`: - If multiple rewrites are specified for the same associated constant, they are required to be identical, and duplicates are discarded. - Rewrites are performed on other rewrites in order to find a fixed point, where no rewrite applies within any other rewrite. If no fixed point exists, the generic parameter declaration or `impl` declaration is invalid. - Rewrites are performed throughout the other constraints in the facet type -- that is, in any `==` constraints and `is` constraints -- and the type `.Self` is replaced by `T` throughout the constraint. ``` // ✅ `.T` in `.U = .T` is rewritten to `i32` when initially // forming the facet type. // Nothing to do during constraint resolution. fn InOrder[A:! C where .T = i32 and .U = .T]() {} // ✅ Facet type has `.T = .U` before constraint resolution. // That rewrite is resolved to `.T = i32`. fn Reordered[A:! C where .T = .U and .U = i32]() {} // ✅ Facet type has `.U = .T` before constraint resolution. // That rewrite is resolved to `.U = i32`. fn ReorderedIndirect[A:! (C where .T = i32) & (C where .U = .T)]() {} // ❌ Constraint resolution fails because // no fixed point of rewrites exists. fn Cycle[A:! C where .T = .U and .U = .T]() {} ``` To find a fixed point, we can perform rewrites on other rewrites, cycling between them until they don't change or until a rewrite would apply to itself. In the latter case, we have found a cycle and can reject the constraint. Note that it's not sufficient to expand only a single rewrite until we hit this condition: ``` // ❌ Constraint resolution fails because // no fixed point of rewrites exists. // If we only expand the right-hand side of `.T`, // we find `.U`, then `.U*`, then `.U**`, and so on, // and never detect a cycle. // If we alternate between them, we find // `.T = .U*`, then `.U = .U**`, then `.V = .U***`, // then `.T = .U**`, then detect that the `.U` rewrite // would apply to itself. fn IndirectCycle[A:! C where .T = .U and .U = .V* and .V = .U*](); ``` After constraint resolution, no references to rewritten associated constants remain in the constraints on `T`. If a facet type is never used to constrain a type, it is never subject to constraint resolution, and it is possible for a facet type to be formed for which constraint resolution would always fail. For example: ``` package Broken api; interface X { let T:! type; let U:! type; } let Bad:! auto = (X where .T = .U) & (X where .U = .T); // Bad is not used here. ``` In such cases, the facet type `Broken.Bad` is not usable: any attempt to use that facet type to constrain a type would perform constraint resolution, which would always fail because it would discover a cycle between the rewrites for `.T` and for `.U`. In order to ensure that such cases are diagnosed, a trial constraint resolution is performed for all facet types. Note that this trial resolution can be skipped for facet types that are actually used, which is the common case. ### Precise rules and termination This section explains the detailed rules used to implement rewrites. There are two properties we aim to satisfy: 1. After type-checking, no symbolic references to associated constants that have an associated rewrite rule remain. 2. Type-checking always terminates in a reasonable amount of time, ideally linear in the size of the problem. Rewrites are applied in two different phases of program analysis. - During qualified name lookup and type checking for qualified member access, if a rewritten member is looked up, the right-hand side's value and type are used for subsequent checks. - During substitution, if the symbolic name of an associated constant is substituted into, and substitution into the left-hand side results in a value with a rewrite for that constant, that rewrite is applied. In each case, we always rewrite to a value that satisfies property 1 above, and these two steps are the only places where we might form a symbolic reference to an associated cosntant, so property 1 is recursively satisfied. Moreover, we apply only one rewrite in each of the above cases, satisfying property 2. #### Qualified name lookup Qualified name lookup into either a type parameter or into an expression whose type is a symbolic type `T` -- either a type parameter or an associated type -- considers names from the facet type `C` of `T`, that is, from `T`’s declared type. ``` interface C { let M:! i32; let U:! C; } fn F[T:! C](x: T) { // Value is C.M in all four of these let a: i32 = x.M; let b: i32 = T.M; let c: i32 = x.U.M; let d: i32 = T.U.M; } ``` When looking up the name `N`, if `C` is an interface `I` and `N` is the name of an associated constant in that interface, the result is a symbolic value representing "the member `N` of `I`". If `C` is formed by combining interfaces with `&`, all such results are required to find the same associated constant, otherwise we reject for ambiguity. If a member of a class or interface is named in a qualified name lookup, the type of the result is determined by performing a substitution. For an interface, `Self` is substituted for the self type, and any parameters for that class or interface (and enclosing classes or interfaces, if any) are substituted into the declared type. ``` interface SelfIface { fn Get[me: Self]() -> Self; } class UsesSelf(T:! type) { // Equivalent to `fn Make() -> UsesSelf(T)*;` fn Make() -> Self*; impl as SelfIface; } // ✅ `T = i32` is substituted into the type of `UsesSelf(T).Make`, // so the type of `UsesSelf(i32).Make` is `fn () -> UsesSelf(i32)*`. let x: UsesSelf(i32)* = UsesSelf(i32).Make(); // ✅ `Self = UsesSelf(i32)` is substituted into the type // of `SelfIface.Get`, so the type of `UsesSelf(i32).(SelfIface.Get)` // is `fn [me: UsesSelf(i32)]() -> UsesSelf(i32)`. let y: UsesSelf(i32) = x->Get(); ``` None of this is new in this proposal. This proposal adds the rule: If a facet type `C` into which lookup is performed includes a `where` clause saying `.N = U`, and the result of qualified name lookup is the associated constant `N`, that result is replaced by `U`, and the type of the result is the type of `U`. No substitution is performed in this step, not even a `Self` substitution -- any necessary substitutions were already performed when forming the facet type `C`, and we don’t consider either the declared type or value of the associated constant at all for this kind of constraint. Going through an example in detail: ``` interface A { let T:! type; } interface B { let U:! type; // More explicitly, this is of type `A where .(A.T) = Self.(B.U)` let V:! A where .T = U; } // Type of T is B. fn F[T:! B](x: T) { // The type of the expression `T` is `B`. // `T.V` finds `B.V` with type `A where .(A.T) = Self.(B.U)`. // We substitute `Self` = `T` giving the type of `u` as // `A where .(A.T) = T.(B.U)`. let u:! auto = T.V; // The type of `u` is `A where .(A.T) = T.(B.U)`. // Lookup for `u.T` resolves it to `u.(A.T)`. // So the result of the qualified member access is `T.(B.U)`, // and the type of `v` is the type of `T.(B.U)`, namely `type`. // No substitution is performed in this step. let v:! auto = u.T; } ``` The more complex case of ``` fn F2[T:! B where .U = i32](x: T); ``` is discussed later. #### Type substitution At various points during the type-checking of a Carbon program, we need to substitute a set of (binding, value) pairs into a symbolic value. We saw an example above: substituting `Self = T` into the type `A where .(A.T) = Self.U` to produce the value `A where .(A.T) = T.U`. Another important case is the substitution of inferred parameter values into the type of a function when type-checking a function call: ``` fn F[T:! C](x: T) -> T; fn G(n: i32) -> i32 { // Deduces T = i32, which is substituted // into the type `fn (x: T) -> T` to produce // `fn (x: i32) -> i32`, giving `i32` as the type // of the call expression. return F(n); } ``` Qualified name lookup is not re-done as a result of type substitution. For a template, we imagine there’s a completely separate step that happens before type substitution, where qualified name lookup is redone based on the actual value of template arguments; this proceeds as described in the previous section. Otherwise, we performed the qualified name lookup when type-checking the generic, and do not do it again: ``` interface IfaceHasX { let X:! type; } class ClassHasX { class X {} } interface HasAssoc { let Assoc:! IfaceHasX; } // Qualified name lookup finds `T.(HasAssoc.Assoc).(IfaceHasX.X)`. fn F(T:! HasAssoc) -> T.Assoc.X; fn G(T:! HasAssoc where .Assoc = ClassHasX) { // `T.Assoc` rewritten to `ClassHasX` by qualified name lookup. // Names `ClassHasX.X`. var a: T.Assoc.X = {}; // Substitution produces `ClassHasX.(IfaceHasX.X)`, // not `ClassHasX.X`. var b: auto = F(T); } ``` During substitution, we might find a member access that named an opaque symbolic associated constant in the original value can now be resolved to some specific value. It’s important that we perform this resolution: ``` interface A { let T:! type; } class K { fn Member(); } fn H[U:! A](x: U) -> U.T; fn J[V:! A where .T = K](y: V) { // We need the interface of `H(y)` to include // `K.Member` in order for this lookup to succeed. H(y).Member(); } ``` The values being substituted into the symbolic value are themselves already fully substituted and resolved, and in particular, satisfy property 1 given above. Substitution proceeds by recursively rebuilding each symbolic value, bottom-up, replacing each substituted binding with its value. Doing this naively will propagate values like `i32` in the `F`/`G` case earlier in this section, but will not propagate rewrite constants like in the `H`/`J` case. The reason is that the `.T = K` constraint is in the _type_ of the substituted value, rather than in the substituted value itself: deducing `T = i32` and converting `i32` to the type `C` of `T` preserves the value `i32`, but deducing `U = V` and converting `V` to the type `A` of `U` discards the rewrite constraint. In order to apply rewrites during substitution, we associate a set of rewrites with each value produced by the recursive rebuilding procedure. This is somewhat like having substitution track a refined facet type for the type of each value, but we don’t need -- or want -- for the type to change during this process, only for the rewrites to be properly applied. For a substituted binding, this set of rewrites is the rewrites found on the type of the corresponding value prior to conversion to the type of the binding. When rebuilding a member access expression, if we have a rewrite corresponding to the accessed member, then the resulting value is the target of the rewrite, and its set of rewrites is that found in the type of the target of the rewrite, if any. Because the target of the rewrite is fully resolved already, we can ask for its type without triggering additional work. In other cases, the rewrite set is empty; all necessary rewrites were performed when type-checking the value we're substituting into. Continuing an example from [qualified name lookup](#qualified-name-lookup): ``` interface A { let T:! type; } interface B { let U:! type; let V:! A where .T = U; } // Type of the expression `T` is `B where .(B.U) = i32` fn F2[T:! B where .U = i32](x: T) { // The type of the expression `T` is `B where .U = i32`. // `T.V` is looked up and finds the associated type `(B.V)`. // The declared type is `A where .(A.T) = Self.U`. // We substitute `Self = T` with rewrite `.U = i32`. // The resulting type is `A where .(A.T) = i32`. // So `u` is `T.V` with type `A where .(A.T) = i32`. let u:! auto = T.V; // The type of `u` is `A where .(A.T) = i32`. // Lookup for `u.T` resolves it to `u.(A.T)`. // So the result of the qualified member access is `i32`, // and the type of `v` is the type of `i32`, namely `type`. // No substitution is performed in this step. let v:! auto = u.T; } ``` #### Examples ``` interface Container { let Element:! type; } interface SliceableContainer { extends Container; let Slice:! Container where .Element = Self.(Container.Element); } // ❌ Qualified name lookup rewrites this facet type to // `SliceableContainer where .(Container.Element) = .Self.(Container.Element)`. // Constraint resolution rejects this because this rewrite forms a cycle. fn Bad[T:! SliceableContainer where .Element = .Slice.Element](x: T.Element) {} ``` ``` interface Helper { let D:! type; } interface Example { let B:! type; let C:! Helper where .D = B; } // ✅ `where .D = ...` by itself is fine. // `T.C.D` is rewritten to `T.B`. fn Allowed(T:! Example, x: T.C.D); // ❌ But combined with another rewrite, creates an infinite loop. // `.C.D` is rewritten to `.B`, resulting in `where .B = .B`, // which causes an error during constraint resolution. // Using `==` instead of `=` would make this constraint redundant, // rather than it being an error. fn Error(T:! Example where .B = .C.D, x: T.C.D); ``` ``` interface Allowed; interface AllowedBase { let A:! Allowed; } interface Allowed { extends AllowedBase where .A = .Self; } // ✅ The final type of `x` is `T`. It is computed as follows: // In `((T.A).A).A`, the inner `T.A` is rewritten to `T`, // resulting in `((T).A).A`, which is then rewritten to // `(T).A`, which is then rewritten to `T`. fn F(T:! Allowed, x: ((T.A).A).A); ``` ``` interface MoveYsRight; constraint ForwardDeclaredConstraint(X:! MoveYsRight); interface MoveYsRight { let X:! MoveYsRight; // Means `Y:! MoveYsRight where .X = X.Y` let Y:! ForwardDeclaredConstraint(X); } constraint ForwardDeclaredConstraint(X:! MoveYsRight) { extends MoveYsRight where .X = X.Y; } // ✅ The final type of `x` is `T.X.Y.Y`. It is computed as follows: // The type of `T` is `MoveYsRight`. // The type of `T.Y` is determined as follows: // - Qualified name lookup finds `MoveYsRight.Y`. // - The declared type is `ForwardDeclaredConstraint(Self.X)`. // - That is a named constraint, for which we perform substitution. // Substituting `X = Self.X` gives the type // `MoveYsRight where .X = Self.X.Y`. // - Substituting `Self = T` gives the type // `MoveYsRight where .X = T.X.Y`. // The type of `T.Y.Y` is determined as follows: // - Qualified name lookup finds `MoveYsRight.Y`. // - The declared type is `ForwardDeclaredConstraint(Self.X)`. // - That is a named constraint, for which we perform substitution. // Substituting `X = Self.X` gives the type // `MoveYsRight where .X = Self.X.Y`. // - Substituting `Self = T.Y` with // rewrite `.X = T.X.Y` gives the type // `MoveYsRight where .X = T.Y.X.Y`, but // `T.Y.X` is replaced by `T.X.Y`, giving // `MoveYsRight where .X = T.X.Y.Y`. // The type of `T.Y.Y.X` is determined as follows: // - Qualified name lookup finds `MoveYsRight.X`. // - The type of `T.Y.Y` says to rewrite that to `T.X.Y.Y`. // - The result is `T.X.Y.Y`, of type `MoveYsRight`. fn F4(T:! MoveYsRight, x: T.Y.Y.X); ``` #### Termination Each of the above steps performs at most one rewrite, and doesn't introduce any new recursive type-checking steps, so should not introduce any new forms of non-termination. Rewrite constraints thereby give us a deterministic, terminating type canonicalization mechanism for associated constants: in `A.B`, if the type of `A` specifies that `.B = C`, then `A.B` is replaced by `C`. Equality of types constrained in this way is transitive. However, some existing forms of non-termination may remain, such as template instantiation triggering another template instantiation. Such cases will need to be detected and handled in some way, such as by a depth limit, but doing so doesn't compromise the soundness of the type system. ### Same type constraints A same-type constraint describes that two type expressions are known to evaluate to the same value. Unlike a rewrite constraint, however, the two type expressions are treated as distinct types when type-checking a generic that refers to them. Same-type constraints are brought into scope, looked up, and resolved exactly as if there were a `SameAs(U:! type)` interface and a `T == U` impl corresponded to `T is SameAs(U)`, except that `==` is commutative. As such, it's not possible to ask for a list of types that are the same as a given type, nor to ask whether there exists a type that is the same as a given type and has some property. But it is possible to ask whether two types are (non-transitively) the same. In order for same-type constraints to be useful, they must allow the two types to be treated as actually being the same in some context. This can be accomplished by the use of `==` constraints in an `impl`, such as in the built-in implementation of `ImplicitAs`: ``` final impl forall [T:! type, U:! type where .Self == T] T as ImplicitAs(U) { fn Convert[me: Self](other: U) -> U { ... } } ``` It superficially seems like it would be convenient if such implementations were made available implicitly – for example, by writing `impl forall [T:! type] T as ImplicitAs(T)` – but in more complex examples that turns out to be problematic. Consider: ``` interface CommonTypeWith(U:! type) { let Result:! type; } final impl forall [T:! type] T as CommonTypeWith(T) where .Result = T {} fn F[T:! Potato, U:! Hashable where .Self == T](x: T, y: U) -> auto { // What is T.CommonTypeWith(U).Result? Is it T or U? return (if cond then x else y).Hash(); } ``` With this proposal, `impl` validation for `T as CommonTypeWith(U)` fails: we cannot pick a common type when given two distinct type expressions, even if we know they evaluate to the same type, because we would not know which API the result should have. #### Implementation of same-type `ImplicitAs` It is possible to implement the above `impl` of `ImplicitAs` directly in Carbon, without a compiler builtin, by taking advantage of the built-in conversion between `C where .A = X` and `C where .A == X`: ``` interface EqualConverter { let T:! type; fn Convert(t: T) -> Self; } fn EqualConvert[T:! type](t: T, U:! EqualConverter where .T = T) -> U { return U.Convert(t); } impl forall [U:! type] U as EqualConverter where .T = U { fn Convert(u: U) -> U { return u; } } impl forall [T:! type, U:! type where .Self == T] T as ImplicitAs(U) { fn Convert[self: Self]() -> U { return EqualConvert(self, U); } } ``` The transition from `(T as ImplicitAs(U)).Convert`, where we know that `U == T`, to `EqualConverter.Convert`, where we know that `.T = U`, allows a same-type constraint to be used to perform a rewrite. This implementation is in use in Carbon Explorer. ### Observe declarations Same-type constraints are non-transitive, just like other constraints such as `ImplicitAs`. The developer can use an `observe` declaration to bring a new same-type constraint into scope: ``` observe A == B == C; ``` notionally does much the same thing as ``` impl A as SameAs(C) { ... } ``` where the `impl` makes use of `A is SameAs(B)` and `B is SameAs(C)`. ### Implementing an interface When implementing an interface, the `=` syntax must be used, and each associated constant without a default must be explicitly assigned-to: ``` impl IntVec as Container where .Element = i32 { ... } ``` not ``` impl IntVec as Container where .Element == i32 { ... } ``` The latter would be treated as ``` impl IntVec as Container where IntVec.Element is SameAs(i32) { ... } ``` ... which only checks the value of `Element` rather than specifying it. As in other contexts, `Self.Element` is rewritten to `i32` within the context of the `impl`. ### Ergonomics This proposal can be viewed as dividing type constraints into two categories: - Ergonomic, but quite restricted, constraints using `=`. - Non-ergonomic, but fully general, constraints using `==`. In order for this to be an effective and ergonomic strategy, we require both that the difference between these two kinds of constraints are readily understood by developers, and that the more-ergonomic `=` constraints cover sufficiently many scenarios that the developer seldom needs to write code to request a conversion between types that are known to be the same. Ideally, we hope that `=` constraints will cover all real situations, and `==` constraints will never need to be written, outside of the one `ImplicitAs` implementation described above. If this turns out to be true in practice, we should consider removing `==` syntax from the language. However, this will not remove `==` semantics, both because of the behavior of `ImplicitAs` and because `impl T as C where .A = B` constraints in an interface or named constraint give rise to `==` semantics. ### Implementation experience This proposal has been mostly implemented in Carbon Explorer, and appears to behave as expected. However, Explorer does not yet support forward declarations of interfaces and constraints, which means that some of the more interesting cases can't be tested. ## Rationale - [Language tools and ecosystem](/docs/project/goals.md#language-tools-and-ecosystem) - This rule is easy to implement, and even a naive implementation should be efficient. - [Software and language evolution](/docs/project/goals.md#software-and-language-evolution) - The status quo causes coincidentally equal types to have the same interface. Under this proposal, the interface of a type is not affected by coincidental equality, only by intentional assignment to an associated type. - [Code that is easy to read, understand, and write](/docs/project/goals.md#code-that-is-easy-to-read-understand-and-write) - Using `=` rather than `==` in `impl`s is easier to read and write. - The interface available on a type is more predictable in this proposal than in the status quo ante, making code easier to understand. ## Alternatives considered ### Status quo The [Problem](#problem) section describes some concerns with the status quo approach. This proposal aims to address those concerns. ### Restrict constraints to allow computable type equality We could somehow restrict which constraints can be specified, in order to ensure that our type equality rule is efficiently computable, perhaps even in a way that permits a deterministic computation of a canonical type. The exact details of how we would do this have not been worked out, but this might lead to a coherent rule that has transitive type equality. However, this would not solve the "equal types with different interfaces" problem. ### Find a fully transitive approach to type equality This proposal makes type equality transitive, but still has non-transitive "same-type" constraints describing types that are known to always eventual be the same after substitution, but which are not treated as the same type value while type-checking a generic. This proposal also imposes a directionality on rewrites, a restriction to only rewrite at a depth of exactly one associated constant, and a restriction that only one value can be specified as equal to a given associated constant. We could seek a type equality rule that would avoid having two different kinds of equality and would avoid some or all of the restrictions placed on rewrite constraints. One candidate approach is to accept that the full generality of the equality problem is [hard](https://link.springer.com/content/pdf/10.1007/3-540-17220-3_7.pdf), but attempt to solve it anyway. Because of the close correspondence between Turing machines and string rewriting systems, this results in a type system with no upper bound on its runtime. We consider this unacceptable for Carbon, but it is the approach taken by some other modern languages: - Swift has an [undecidable type system](https://forums.swift.org/t/swift-type-checking-is-undecidable/39024) due to this, and handles the problem by placing a limit on the complexity of cases they will accept, but that seems unsuited to Carbon's goals, as the rule cannot be readily understood by a developer nor effectively worked around. - Rust also has an [undecidable type system](https://sdleffler.github.io/RustTypeSystemTuringComplete/) (content warning: contains many instances of an obscene word as part of a programming language name) for the same reason, and similarly has a recursion limit. See also [Typing is Hard](https://web.archive.org/web/20231223005655/https://3fx.ch/typing-is-hard.html), which lists similar information for a variety of other languages. Note that most languages listed have an undecidable type system. Another candidate approach is to find a way to reduce the inputs to the type-checking step as a set of non-quantified equalities. The resulting system of equalities can then be [solved efficiently](https://dl.acm.org/doi/pdf/10.1145/322186.322198) to determine whether two types are known to be the same. This approach appears sufficient to cope with locally declared constraints, but when constraints appear within interfaces, they can give rise to an infinite family of equalities, and picking any finite subset risks the result being non-transitive in general, if a different subset of equalities might be considered in a different type-checking context. ### Different syntax for rewrite constraint We could use a different syntax, such as `~>`, for rewrite constraints. This would have the advantage of not using assignment for an operation that's not always doing something that developers will think of as assignment. It also avoids hard-to read code in cases where a binding has an initializer: ``` // This proposal. let T:! Add where .Result = i32 = i32; // Alternative syntax. let T:! Add where .Result ~> i32 = i32; ``` However, it seems valuable to use only a single syntax for specifying these constraints, and equality is the appropriate mental model most of the time. Moreover, in an `impl` declaration, we really are assigning to the associated constant in the sense of setting a value, rather than merely constraining a value. This decision should be revisited if a superior syntax that avoids the above visual ambiguity is suggested. ### Different syntax for same-type constraint We considered various options for the spelling of a same-type constraint: - `where A == B` - `where A ~ B` - some other operator symbol - `where A is SameType(B)`, or some other constraint name The advantage of `==` is that it has a lot of desirable implications: it's familiar, symmetric, and connotes the left and right operand being the same. However, it also might be taken as suggesting that the `==` overloaded operator is used to determine equality. A different spelling would also indicate that this is an unusual constraint, and would be easier to search for. Concerns were also raised that `==` might suggest transitivity if taken to mean "same value" rather than something like the Carbon `==` binary operator, and the transitivity of `==` constraints is not automatic. The `~` operator is currently not in use as a binary operator. However, that makes it quite valuable syntactic real estate, and it may see little use in this context in practice. A longer operator could be used, but any choice other than `==` will present an unfamiliarity barrier, and longer operators will be harder to remember. A named constraint is appealing, but this operator does not behave like a named constraint in that it is automatically symmetrized, not implementable, and especially in its interactions with `observe` declarations. It is unclear how `observe` declarations would be written with a named `SameType` constraint: ``` // Maybe something like this special-case and verbose syntax? observe A is SameType(B) and B is SameType(C) => A is SameType(C); ``` The fundamental connection between same-type constraints and `observe` suggests that dedicated syntax is justified. For now, we use `==`, despite it having a different meaning in the context of a constraint than in expression contexts. This is analogous to how `=` and `and` have different meanings in this context from in expressions, so the behavior divergence is at least consistent. It's also not clear at this stage how much usage same-type constraints will have, compared to rewrite constraints, so it's hard to judge the other arguments in favor of and against other operators. It would be reasonable to revisit this decision when we have more usage information. ### Required ordering for rewrites We considered a rule where a rewrite would need to be specified in a constraint before the associated constant is used. For example: ``` interface C { let T:! type; let U:! type; } // Could reject, `.U` referenced before rewrite is defined. fn G[A:! C where .T = .U and .U = i32]() {} ``` This would remove the need to perform constraint resolution. Instead, we could apply rewrites as we form the constraint, and constraints formed in this way would always satisfy the property that they don't refer to associated constants that have a rewrite. It may also be less surprising to use this rule. Because Carbon uses top-down processing for type-checking in general, it may be unexpected that a rewrite rule would rewrite an earlier occurrence of its rewrite target, even though this happens in practice at a later point in time, during constraint resolution. However, such an approach would not give a satisfactory outcome for cases such as: ``` constraint X { extends C where .T = .U; } constraint Y { extends C where .U = i32; } // Desired behavior is that `.T` and `.U` both rewrite to `i32`. fn G[A:! X & Y]() -> A.T { return 0; } ``` Performing earlier-appearing rewrites in later constraints and additionally performing a constraint resolution step to apply rewrites throughout the constraint seems to give better outcomes for the examples we considered. ### Multi-constraint `where` clauses Instead of treating `A where B and C` as `(A where B) where C`, we could model it as `(A where B) & (A where C)`. Specifically, given ``` interface C { let T:! type; let U:! type; } ``` this would treat `C where .T = A and .U = B` as `(C where .T = A) & (C where .U = B)`, where the type of `.Self` would be `C` in both clauses, with both constraints applied "simultaneously" to `C`. This would mean that the order of clauses doesn’t matter. However, if we do this, then neither right-hand side is rewritten by name lookup, and thus we would reject cases such as `C where .T = i32 and .U = .T.(Add.Result)`. This case seems reasonable, and we would prefer to accept it. Therefore, the rule we select is that, as soon as a rewrite is declared, it is in scope, and later references to that associated constant refer to the rewritten value with its rewritten type. We still reject cases like `C where .U = .T.(Add.Result) and .T = i32`, because we do not know that `T is Add` from the declared type of `C.T`. The same rule applies to `is` constraints: we accept `C where .T is Add and .U = .T.(Add.Result)`, but reject `C where .U = .T.(Add.Result) and .T is Add`. ### Rewrite constraints in `impl as` constraints A rewrite constraint can appear indirectly in an `impl as` constraint in an interface or named constraint: ``` interface A { let T:! type; } constraint AInt { extends A where .T = i32; } interface B { // Or, `impl as A where .T = i32;` impl as AInt; } ``` When this happens, the rewrite constraint does not result in rewrites being performed when the associated constant is mentioned in a member access into that interface or named constraint: ``` // Return type is not rewritten to `i32`, // but there is still a same-type constraint. fn F(T:! B) -> T.(A.T) { // OK, `i32` implicitly converts to `T.(A.T)`. return 0 as i32; } ``` This may be surprising: `B` says that its `.(A.T)` has a rewrite to `i32`, but that rewrite is not performed. In contrast, rewrites in an `extends` constraint do become part of the enclosing interface or named constraint. ``` interface C { extends AInt; } // Return type is rewritten to `i32`. fn G(T:! C) -> T.T; ``` Something similar happens with `impl T as` constraints. The following two interfaces `A1` and `A2` are not equivalent: ``` interface A1 { let X:! Add where .Result = i32; } constraint AddToi32 { extends Add where .Result = i32; } interface A2 { let X:! Add; impl X as AddToi32; } ``` In the former case, references to `A.X.Result` are rewritten to `i32`, and in the latter case, they are not, because the rewrite is not part of the type of `X`. The general principle here is that rewrites are part of the declared type of a name, and can't be changed after the fact by another declaration. For the `impl T as` case, this behavior is also a necessary part of the termination argument. If rewrites from `impl T as` constraints were used, it would be possible to form a rewrite cycle: ``` interface C { let X:! type; } interface A { let U:! C; let T:! C; } interface B1 { extends A; impl T as C where .X = U.X; } interface B2 { extends A; impl U as C where .X = T.X; } // `V.T.X` ~> `V.U.X` ~> `V.T.X` ~> ... fn F(V:! B1 & B2) -> V.T.X; ``` We could allow the specific case of `impl as` (not `impl T as`) to introduce rewrites. The advantage of this change is that this behavior may be less surprising in some cases. However, this would mean that an `impl as` sometimes changes the interface of the enclosing constraint, and behaves inconsistently from an `impl T as` constraint, so this proposal does not adopt that behavior. We could allow `impl T as` to introduce rewrites for `T` in general, but we would need to find some way to fix the resulting holes in the termination argument. To minimize the scope of confusion, we currently disallow rewrites from appearing _syntactically_ within an `impl as` or `impl T as` constraint. We could allow these constructs. However, a `.T = V` constraint would be equivalent to a `.T == V` constraint, and the latter more clearly expresses the resulting semantics, so disallowing the misleading form seems preferable. ================================================ FILE: proposals/p2187.md ================================================ # Update sum types design [Pull request](https://github.com/carbon-language/carbon-lang/pull/2187) ## Table of contents - [Abstract](#abstract) - [Problem](#problem) - [Proposal](#proposal) - [Alternatives considered](#alternatives-considered) - [Keep the continuation interface as a parameter](#keep-the-continuation-interface-as-a-parameter) - [Require the continuation to execute the whole `case` body](#require-the-continuation-to-execute-the-whole-case-body) ## Abstract This proposal updates the design of [#157](p0157.md) to reflect subsequent evolution of the language. ## Problem Some aspects of the design of #157 have become inconsistent with the rest of the language, or can be made more precise in light of subsequent language development. ## Proposal - Make the continuation interface an associated type rather than an interface parameter. - For patterns that can be matched using either `Match` or `==`, require both implementations to be well-formed. - Use `name: Type` order instead of `Type: name` order in alternative declarations. - Use current syntax and semantics for generics and class types. - Rename `Matchable.Match` to `Match.Op`, following the resolution of [#1058](https://github.com/carbon-language/carbon-lang/issues/1058). ## Alternatives considered ### Keep the continuation interface as a parameter Making the continuation interface a parameter of `Match` could in principle allow a single type to support pattern matching in multiple ways, by implementing `Match` for multiple continuation interfaces. However, that would require something like overload resolution on interfaces, to choose the implementation of `Match(C)` on the sum type for which `C` best matches the continuation constructed by the compiler. No such overloading mechanism is planned, and we don't have sufficiently compelling use cases to motivate it. ### Require the continuation to execute the whole `case` body We will probably want to support in-place mutation of alternative parameters (for example so you can call a mutable method on the value stored in an `Optional(Foo)`), and we might even want to extend that to cases where the underlying parameter isn't represented as an lvalue of that type, but has to be unpacked by `Match.Op`. The only way I see to make that work is to have `Match.Op` unpack the parameter to a local lvalue, pass it to the continuation, and then pack the possibly-mutated value back into the sum object after the continuation returns. That would mean the compiler has to execute the case body inside the continuation, not after `Match.Op` returns. However, that's fairly speculative, and wouldn't apply to the read-only cases that we currently support, so we need not preemptively constrain the compiler here. ================================================ FILE: proposals/p2188.md ================================================ # Pattern matching syntax and semantics [Pull request](https://github.com/carbon-language/carbon-lang/pull/2188) ## Table of contents - [Abstract](#abstract) - [Problem](#problem) - [Background](#background) - [Proposal](#proposal) - [Details](#details) - [Expressions versus proper patterns](#expressions-versus-proper-patterns) - [Expression patterns](#expression-patterns) - [Bindings](#bindings) - [Name bindings](#name-bindings) - [Wildcard](#wildcard) - [Generic bindings](#generic-bindings) - [`auto` and type deduction](#auto-and-type-deduction) - [`var`](#var) - [Tuple patterns](#tuple-patterns) - [Struct patterns](#struct-patterns) - [Alternative patterns](#alternative-patterns) - [Templates](#templates) - [Guards](#guards) - [Refutability, overlap, usefulness, and exhaustiveness](#refutability-overlap-usefulness-and-exhaustiveness) - [Rationale](#rationale) - [Alternatives considered](#alternatives-considered) - [Shorthand for `auto`](#shorthand-for-auto) - [Struct pattern syntax](#struct-pattern-syntax) - [Type pattern matching](#type-pattern-matching) - [Introducer syntax for expression patterns](#introducer-syntax-for-expression-patterns) - [Allow guards on arbitrary patterns](#allow-guards-on-arbitrary-patterns) - [Treat expression patterns as exhaustive if they cover all possible values](#treat-expression-patterns-as-exhaustive-if-they-cover-all-possible-values) - [Allow non-exhaustive `match` statements](#allow-non-exhaustive-match-statements) - [Future work](#future-work) - [Or patterns](#or-patterns) - [As patterns](#as-patterns) - [Matching classes by dynamic type](#matching-classes-by-dynamic-type) - [User-defined pattern matching](#user-defined-pattern-matching) - [Matching classes with struct patterns](#matching-classes-with-struct-patterns) - [Matching by reference](#matching-by-reference) - [Type deduction](#type-deduction) - [Match expressions](#match-expressions) - [Examples](#examples) - [Examples from P0095R1](#examples-from-p0095r1) - [Figure 1. Declaration of a command data structure](#figure-1-declaration-of-a-command-data-structure) - [Figure 2. Implementation of an output operator](#figure-2-implementation-of-an-output-operator) - [Figure 3. Switching an enum](#figure-3-switching-an-enum) - [Figure 4. Expression datatype](#figure-4-expression-datatype) - [Figure 5. `struct` inspection](#figure-5-struct-inspection) - [Example from P1371R5: Red-black tree rebalancing](#example-from-p1371r5-red-black-tree-rebalancing) - [With P1371R5 pattern matching](#with-p1371r5-pattern-matching) - [With this proposal](#with-this-proposal) - [With this proposal plus #2187](#with-this-proposal-plus-2187) ## Abstract This paper proposes concrete syntax and semantic choices for Carbon patterns. ## Problem Carbon uses patterns wherever a value should be given a name, decomposed, or matched against: in function parameters, variable declarations, `match` statement `case`s, `for` statement loop variables, and so on. Simplified forms of patterns, required to be just a simple name binding, appear in additional contexts, such as fields in classes and implicit parameter lists. While we have syntax specified for some of these constructs, we do not have an approved proposal describing the syntax or semantics of patterns. ## Background See [pattern matching](https://en.wikipedia.org/wiki/Pattern_matching) on wikipedia for a broad overview of the subject. We refer to the value being matched by a pattern as the _scrutinee_. ## Proposal Patterns in Carbon are a generalization of the expression grammar. Compared to expressions, patterns add: - Bindings, of the form `name: type`, which give a name for the scrutinee. - `var` _pattern_, which creates a separate object to hold the value of the scrutinee, and causes any nested bindings to be mutable lvalues instead of immutable rvalues. - Additional syntax to make matching against structs more convenient. ## Details ### Expressions versus proper patterns Expressions are patterns, as described below. A pattern that is not an expression, because it contains pattern-specific syntax such as a binding, is a _proper pattern_. Many expression forms, such as arbitrary function calls, are not permitted as proper patterns, so cannot contain bindings. - _pattern_ ::= _proper-pattern_ ``` fn F(n: i32) -> i32 { return n; } match (F(42)) { // ❌ Error: binding can't appear in a function call. case (F(n: i32)) => {} } ``` ### Expression patterns An expression is a pattern. - _pattern_ ::= _expression_ The pattern is compared with the expression using the `==` operator: _pattern_ `==` _scrutinee_. ``` fn F(n: i32) { match (n) { // ✅ Results in an `n == 5` comparison. // OK despite `n` and `5` having different types. case 5 => {} } } ``` Any `==` operations performed by a pattern match occur in lexical order, but for repeated matches against the same _pattern_, later comparisons may be skipped by reusing the result from an earlier comparison: ``` class ChattyIntMatcher { external impl as EqWith(i32) { fn Eq[me: ChattyIntMatcher](other: i32) { Print("Matching {0}", other); return other == 1; } } } fn F() { // Prints `Matching 1` then `Matching 2`, // may or may not then print `Matching 1` again. match ((1, 2)) { case ({} as ChattyIntMatcher, 0) => {} case (1, {} as ChattyIntMatcher) => {} case ({} as ChattyIntMatcher, 2) => {} } } ``` ### Bindings #### Name bindings A name binding is a pattern. - _binding-pattern_ ::= `unused`? _identifier_ `:` _expression_ - _proper-pattern_ ::= _binding-pattern_ The type of the _identifier_ is specified by the _expression_. The scrutinee is implicitly converted to that type if necessary. ``` fn F() -> i32 { match (5) ( // ✅ `5` is implicitly converted to `i32`. // Returns `5 as i32`. case n: i32 => { return n; } } } ``` When a new object needs to be created for the binding, the lifetime of the bound value matches the scope of the binding. ``` class NoisyDestructor { fn Make() -> Self { return {}; } external impl i32 as ImplicitAs(NoisyDestructor) { fn Convert[me: i32]() -> Self { return Make(); } } destructor { Print("Destroyed!"); } } fn G() { // Does not print "Destroyed!". let n: NoisyDestructor = NoisyDestructor.Make(); Print("Body of G"); // Prints "Destroyed!" here. } fn H(n: i32) { // Does not print "Destroyed!". let (v: NoisyDestructor, w: i32) = (n, n); Print("Body of H"); // Prints "Destroyed!" here. } ``` As specified in [#2022](/proposals/p2022.md#the-behavior-of-unused-name-bindings), the `unused` keyword indicates that the binding is intended to not be used. #### Wildcard A syntax like a binding but with `_` in place of an identifier can be used to ignore part of a value. - _binding-pattern_ ::= `_` `:` _expression_ See [#2022](/proposals/p2022.md) for details. The behavior is similar to that of an `unused` binding with a unique name. ``` fn F(n: i32) { match (n) { // ✅ Matches and discards the value of `n`. case _: i32 => {} // ❌ Error: unreachable. default => {} } } ``` As specified in [#1084](/proposals/p1084.md), function redeclarations may replace named bindings with wildcards but may not use different names. ``` fn G(n: i32); fn H(n: i32); fn J(n: i32); // ✅ Does not use `n`. fn G(_: i32) {} // ❌ Error: name of parameter does not match declaration. fn H(m: i32) {} // ✅ Does not use `n`. fn J(unused n: i32); ``` #### Generic bindings A `:!` can be used in place of `:` for a binding that is usable at compile time. - _generic-pattern_ ::= `unused`? `template`? _identifier_ `:!` _expression_ - _generic-pattern_ ::= `template`? `_` `:!` _expression_ - _proper-pattern_ ::= _generic-pattern_ ``` // ✅ `F` takes a generic type parameter `T` and a parameter `x` of type `T`. fn F(T:! Type, x: T) { var v: T = x; } ``` The `template` keyword indicates the binding is introducing a template parameter, so name lookups into the parameter should be deferred until its value is known. #### `auto` and type deduction The `auto` keyword is a placeholder for a unique deduced type. - _expression_ ::= `auto` ``` fn F(n: i32) { var v: auto = SomeComplicatedExpression(n); // Equivalent to: var w: T = SomeComplicatedExpression(n); // ... where `T` is the type of the initializer. } ``` The `auto` keyword is only permitted in specific contexts. Currently these are: - As the return type of a function. - As the type of a binding. It is anticipated that `auto` may be permitted in more contexts in the future, for example as a generic argument in a parameterized type that appears in a context where `auto` is allowed, such as `Vector(auto)` or `auto*`. When the type of a binding requires type deduction, the type is deduced against the type of the scrutinee and deduced values are substituted back into the type before pattern matching is performed. ``` fn G[T:! Type](p: T*); class X { external impl as ImplicitAs(i32*); } // ✅ Deduces `T = i32` then implicitly and // trivially converts `p` to `i32*`. fn H1(p: i32*) { G(p); } // ❌ Error, can't deduce `T*` from `X`. fn H2(p: X) { G(p); } ``` The above is only an illustration; the behavior of type deduction is not specified in this proposal. ### `var` A `var` prefix indicates that a pattern provides mutable storage for the scrutinee. - _proper-pattern_ ::= `var` _proper-pattern_ A `var` pattern matches when its nested pattern matches. The type of the storage is the resolved type of the nested _pattern_. Any bindings within the nested pattern refer to portions of the corresponding storage rather than to the scrutinee. ``` fn F(p: i32*); fn G() { match ((1, 2)) { // `n` is a mutable `i32`. case (var n: i32, 1) => { F(&n); } // `n` and `m` are the elements of a mutable `(i32, i32)`. case var (n: i32, m: i32) => { F(if n then &n else &m); } } } ``` Pattern matching precedes the initialization of the storage for any `var` patterns. An introduced variable is only initialized if the complete pattern matches. ``` class X { destructor { Print("Destroyed!"); } } fn F(x: X) { match ((x, 1 as i32)) { case (var y: X, 0) => {} case (var z: X, 1) => {} // Prints "Destroyed!" only once, when `z` is destroyed. } } ``` A `var` pattern cannot be nested within another `var` pattern. The declaration syntax `var` _pattern_ `=` _expresson_ `;` is equivalent to `let` `var` _pattern_ `=` _expression_ `;`. ### Tuple patterns A tuple of patterns can be used as a pattern. - _tuple-pattern_ ::= `(` [_expression_ `,`]\* _proper-pattern_ [`,` _pattern_]\* `,`? `)` - _proper-pattern_ ::= _tuple-pattern_ A _tuple-pattern_ containing no commas is treated as grouping parens: the contained _proper-pattern_ is matched directly against the scrutinee. Otherwise, the behavior is as follows. A tuple pattern is matched left-to-right. The scrutinee is required to be of tuple type. Note that a tuple pattern must contain at least one _proper-pattern_. Otherwise, it is a tuple-valued expression. However, a tuple pattern and a corresponding tuple-valued expression are matched in the same way because `==` for a tuple compares fields left-to-right. ### Struct patterns A struct can be matched with a struct pattern. - _proper-pattern_ ::= `{` [_field-init_ `,`]\* _proper-field-pattern_ [`,` _field-pattern_]\* `}` - _proper-pattern_ ::= `{` [_field-pattern_ `,`]+ `_` `}` - _field-init_ ::= _designator_ `=` _expression_ - _proper-field-pattern_ ::= _designator_ `=` _proper-pattern_ - _proper-field-pattern_ ::= _binding-pattern_ - _field-pattern_ ::= _field-init_ - _field-pattern_ ::= _proper-field-pattern_ A struct pattern resembles a struct literal, with at least one field initialized with a proper pattern: ``` match ({.a = 1, .b = 2}) { // Struct literal as an expression pattern. case {.b = 2, .a = 1} => {} // Struct pattern. case {.b = n: i32, .a = m: i32} => {} } ``` The scrutinee is required to be of struct type, and to have the same set of field names as the pattern. The pattern is matched left-to-right, meaning that matching is performed in the field order specified in the pattern, not in the field order of the scrutinee. This is consistent with the behavior of matching against a struct-valued expression, where the expression pattern becomes the left operand of the `==` and so determines the order in which `==` comparisons for fields are performed. In the case where a field will be bound to an identifier with the same name, a shorthand syntax is available: `a: T` is synonymous with `.a = a: T`. ``` match ({.a = 1, .b = 2}) { case {a: i32, b: i32} => { return a + b; } } ``` If some fields should be ignored when matching, a trailing `, _` can be added to specify this: ``` match ({.a = 1, .b = 2}) { case {.a = 1, _} => { return 1; } case {b: i32, _} => { return b; } } ``` This is valid even if all fields are actually named in the pattern. ### Alternative patterns An alternative pattern is used to match one alternative of a choice type. - _proper-pattern_ ::= _callee-expression_ _tuple-pattern_ - _proper-pattern_ ::= _designator_ _tuple-pattern_? Here, _callee-expression_ is syntactically an expression that is valid as the callee in a function call expression, and an alternative pattern is syntactically a function call expression whose argument list contains at least one _proper-pattern_. If a _callee-expression_ is provided, it is required to name a choice type alternative that has a parameter list, and the scrutinee is implicitly converted to that choice type. Otherwise, the scrutinee is required to be of some choice type, and the designator is looked up in that type and is required to name an alternative with a parameter list if and only if a _tuple-pattern_ is specified. The pattern matches if the active alternative in the scrutinee is the specified alternative, and the arguments of the alternative match the given tuple pattern (if any). ``` choice Optional(T:! Type) { None, Some(T) } match (Optional(i32).None) { // ✅ `.None` resolved to `Optional(i32).None`. case .None => {} // ✅ `.Some` resolved to `Optional(i32).Some`. case .Some(n: i32) => { Print("{0}", n); } // ❌ Error, no such alternative exists. case .Other => {} } class X { external impl as ImplicitAs(Optional(i32)); } match ({} as X) { // ✅ OK, but expression pattern. case Optional(i32).None => {} // ✅ OK, implicitly converts to `Optional(i32)`. case Optional(i32).Some(n: i32) => { Print("{0}", n); } } ``` Note that a pattern of the form `Optional(T).None` is an expression pattern and is compared using `==`. ### Templates Any checking of the type of the scrutinee against the type of the pattern that cannot be performed because the type of the scrutinee involves a template parameter is deferred until the template parameter's value is known. During instantiation, patterns that are not meaningful due to a type error are instead treated as not matching. This includes cases where an `==` fails because of a missing `EqWith` implementation. ``` fn TypeName[template T:! Type](x: T) -> String { match (x) { // ✅ OK, the type of `x` is a template parameter. case _: i32 => { return "int"; } case _: bool => { return "bool"; } case _: auto* => { return "pointer"; } default => { return "unknown"; } } } ``` Cases where the match is invalid for reasons not involving the template parameter are rejected when type-checking the template: ``` fn MeaninglessMatch[template T:! Type](x: T*) { match (*x) { // ✅ OK, `T` could be a tuple. case (_: auto, _: auto) => {} default => {} } match (x->y) { // ✅ OK, `T.y` could be a tuple. case (_: auto, _: auto) => {} default => {} } match (x) { // ❌ Error, tuple pattern cannot match value of non-tuple type `T*`. case (_: auto, _: auto) => {} default => {} } } ``` ### Guards We allow `case`s within a `match` statement to have _guards_. These are not part of pattern syntax, but instead are specific to `case` syntax: - _case_ ::= `case` _pattern_ [`if` _expression_]? `=>` _block_ A guard indicates that a `case` only matches if some predicate holds. The bindings in the pattern are in scope in the guard: ``` match (x) { case (m: i32, n: i32) if m + n < 5 => { return m - n; } } ``` For consistency, this facility is also available for `default` clauses, so that `default` remains equivalent to `case _: auto`. ### Refutability, overlap, usefulness, and exhaustiveness Some definitions: - A pattern _P_ is _useful_ in the context of a set of patterns _C_ if there exists a value that _P_ can match that no pattern in _C_ matches. - A set of patterns _C_ is _exhaustive_ if it matches all possible values. Equivalently, _C_ is exhaustive if the pattern `_: auto` is not useful in the context of _C_. - A pattern _P_ is _refutable_ if there are values that it does not match, that is, if the pattern `_: auto` is useful in the context of {_P_}. Equivalently, the pattern _P_ is _refutable_ if the set of patterns {_P_} is not exhaustive. - A set of patterns _C_ is _overlapping_ if there exists any value that is matched by more than one pattern in _C_. For the purpose of these terms, expression patterns that match a constant tuple, struct, or choice value are treated as if they were tuple, struct, or alternative patterns, respectively, and `bool` is treated like a choice type. Any expression patterns that remain after applying this rule are considered to match a single value from an infinite set of values so that a set of expression patterns is never exhaustive: ``` fn IsEven(n: u8) -> bool { // Not considered exhaustive. match (n) { case 0 => { return true; } case 1 => { return false; } ... case 255 => { return false; } } // Code here is considered to be reachable. } ``` ``` fn IsTrue(b: bool) -> bool { // Considered exhaustive. match (b) { case false => { return false; } case true => { return true; } } // Code here is considered to be unreachable. } ``` When determining whether a pattern is useful, no attempt is made to determine the value of any guards, and instead a worst-case assumption is made: a guard on that pattern is assumed to evaluate to true and a guard on any pattern in the context set is assumed to evaluate to false. We will diagnose the following situations: - A pattern is not useful in the context of prior patterns. In a `match` statement, this happens if a pattern or `default` cannot match because all cases it could cover are handled by prior cases or a prior `default`. For example: ``` choice Optional(T:! Type) { None, Some(T) } fn F(a: Optional(i32), b: Optional(i32)) { match ((a, b)) { case (.Some(a: i32), _: auto) => {} // ✅ OK, but only matches values of the form `(None, Some)`, // because `(Some, Some)` is matched by the previous pattern. case (_: auto, .Some(b: i32)) => {} // ✅ OK, matches all remaining values. case (.None, .None) => {} // ❌ Error, this pattern never matches. case (_: auto, _: auto) => {} } } ``` - A pattern match is not exhaustive and the program doesn't explicitly say what to do when no pattern matches. For example: - If the patterns in a `match` are not exhaustive and no `default` is provided. ``` fn F(n: i32) -> i32 { // ❌ Error, this `match` is not exhaustive. match (n) { case 0 => { return 2; } case 1 => { return 3; } case 2 => { return 5; } case 3 => { return 7; } case 4 => { return 11; } } } ``` - If a refutable pattern appears in a context where only one pattern can be specified, such as a `let` or `var` declaration, and there is no fallback behavior. This currently includes all pattern matching contexts other than `match` statements, but the `var`/`let`-`else` feature in [#1871](https://github.com/carbon-language/carbon-lang/pull/1871) would introduce a second context permitting refutable matches, and overloaded functions might introduce a third context. ``` fn F(n: i32) { // ❌ Error, refutable expression pattern `5` used in context // requiring an irrefutable pattern. var 5 = n; } // ❌ Error, refutable expression pattern `5` used in context // requiring an irrefutable pattern. fn G(n: i32, 5); ``` - When a set of patterns have no ordering or tie-breaker, it is an error for them to overlap unless there is a unique best match for any value that matches more than one pattern. However, this situation does not apply to any current language rule: - For `match` statements, patterns are matched top-down, so overlap is permitted. - We do not yet have an approved design for overloaded functions, but it is anticipated that declaration order will be used in that case too. - For a set of `impl`s that match a given `impl` lookup, argument deduction is used rather than pattern matching, but `impl`s with the same type structure are an error unless a `match_first` declaration is used to order the `impl`s. (This is a pre-existing rule and is unchanged by this proposal.) ## Rationale - [Software and language evolution](/docs/project/goals.md#software-and-language-evolution) - The `, _` syntax for struct patterns enables a style where adding a struct member is not a breaking change. - The requirement that matches be exhaustive makes it easier to add new cases to a choice type, by requiring the compiler to detect places where the new value is not handled. - [Code that is easy to read, understand, and write](/docs/project/goals.md#code-that-is-easy-to-read-understand-and-write) - Pattern syntax makes it easier to match complex values. - Modeling pattern syntax after expressions eases the burden of learning a new sub-language for pattern-matching: patterns are an extension of expressions, and expressions are a special case of patterns. - Requiring exhaustiveness for matches makes control flow easier to understand as there is never a value for which the `match` skips all cases. - [Interoperability with and migration from existing C++ code](/docs/project/goals.md#interoperability-with-and-migration-from-existing-c-code) - The rules for matching a templated value can be used to replace `if constexpr` in many cases. ## Alternatives considered ### Shorthand for `auto` We could provide a shorter syntax for `name: auto`. [Proposal #851](https://github.com/carbon-language/carbon-lang/blob/trunk/proposals/p0851.md#elide-the-type-instead-of-using-auto) considered the following shorthands and decided against using them: ``` var n: _ = init; var n = init; ``` A novel suggestion that avoids some of the disadvantages of those syntaxes would be to use: ``` var n:= init; ``` Advantages: - Shorter syntax for variables with a deduced type. - Potentially allows removal of the `auto` keyword. Disadvantages: - Appears to introduce a `:=` syntax, but that only arises in cases where an initializer immediately follows the name. - Cases such as `var (a:, b:) = my_pair;` would either be invalid or would not use the `:=` syntax. - If we accept such cases, there is a risk of grammar ambiguities. - If we reject such cases, we may still want to keep `auto` around for them, creating inconsistency. - Not a complete replacement for `auto` if we want to also allow things like `v: Vector(auto)`. C++ doesn't allow the equivalent syntax currently, but it was part of the Concepts TS and seems likely to return at some point. - No syntactic difference between accidentally omitting a type entirely and requesting type deduction. However, the mistake of omitting a type but retaining the `:` seems unlikely, and the `:` followed by the absence of a type is a signal that something is happening, so this seems to be less of a concern than for the `var n = init;` syntax. See discussion topics [1](https://github.com/carbon-language/carbon-lang/discussions/1495) and [2](https://github.com/carbon-language/carbon-lang/discussions/1988). ### Struct pattern syntax We could omit the `, _` syntax. This would simplify struct patterns, but at the cost of removing a feature that can be useful for reducing verbosity and making library evolution easier. We could always allow a struct pattern to match a struct with more fields, without requiring a `, _` suffix. This would aid evolution by reducing the cases where adding a field to a struct can be a breaking change, but such cases would still exist. Further, this would make matches against a struct-valued expression inconsistent with matches against a struct pattern. We could use a different syntax instead of `, _`. Other options that were explicitly considered: - `, ...` seems visually evocative of "and more stuff", but risks conflicting with variadic syntax or at least being confusing when used in a variadic context, given that variadics are expected to claim `...` for pack expansion. - `, ._` suggests matching a field without specifying a name, but might create an impression of matching just one field, and three low punctuation characters in a row seems to be pushing the limits of readability. On balance, `, _` harmonizes well with the use of `_` to introduce a wildcard, without being too visually confusing given that the other use of `_` has a following `:`. We could remove the `{field: type}` shorthand and require `{.field = field: type}`. This may avoid encouraging reusing the field name even when it is not appropriate, and would remove some syntactic sugar that's not formally necessary. However, we expect this to be a common case whose ergonomics are important. We could use a different syntax for `{field: type}` that is less divergent from other struct syntaxes: - `{.field: type}` seems like a contender, but doesn't work because that is already recognized as a struct type literal. Also, the use of normal binding syntax means that every locally-introduced name is always introduced by `name: type` where `name` is not preceded by `.`. - `{.=field: type}` might be a reasonable mnemonic shorthand for `{.field = field: type}`, but looks a little surprising. This is probably the best choice if concerns are found with `{field: type}` syntax. ### Type pattern matching We could treat type deduction as a form of pattern matching. For example, we could allow ``` fn F(a: Vector(T:! Type)) -> T { return a[0]; } fn G() -> i32 { let v: Vector(i32) = (1, 2, 3); // Deduces `T = i32`. return F(v); } ``` where the value of `T` is determined by pattern-matching `Vector(T:! Type)` against the supplied type `Vector(i32)` of `v`. And symmetrically: ``` fn H[m: i32, n: i32](k: i32, (m, n)) { return m + n; } fn I() { // Deduces `m = 2`, `n = 3`. H(1, (2, 3)); } ``` This would ensure consistency between pattern matching and deduction, potentially reducing the number of rules that Carbon developers need to learn. We find that use of pattern-matching in type position can harm readability. For example, the first of these two examples may be easier to read due to having less nesting: ``` fn F[T:! Type](x: T); fn F(x: (T:! Type)); ``` Having distinct syntax for type-level matching and value-level matching helps guide the reader to the correct interpretation, even though the underlying matching process is expected to be similar or identical. As a result, we keep type deduction syntax and pattern matching syntax separate for now: - In pattern matching, bindings and wildcards are introduced by nested `:` / `:!` patterns, and the right-hand side of a binding pattern is never a proper pattern. - In type deduction, deduced values are specified separately and an expression written in terms of those bindings describes the type. There are some contexts where there is no syntactic location for introducing deduced values, such as in `case` labels. For syntactic consistency, such cases should be addressed by adding `forall` syntax, if there is motivation to support deduction: ``` match (templated_value) { case forall [template T:! Type] (p: T*) => { heap.Delete(p); } } ``` ### Introducer syntax for expression patterns We could have some separate introducer syntax to distinguish expression patterns from other kinds of patterns: ``` match ((a, b)) { case is (1, 2) => {} case (is 3, n: i32) => {} case (m: i32, is 4) => {} } ``` This would reduce the chance of confusion in cases where an expression and a similar-looking pattern are treated differently: ``` class TupleLike { external impl as (i32, i32); } fn MatchTupleLike(t: TupleLike) { match (t) { // ✅ OK, expression pattern; // `t` implicitly converted to `(i32, i32)` by // built-in `impl EqWith` for tuples. case (1, 2) => {} // ❌ Error, `t` is not a tuple. case (n: i32, 3) => {} } } ``` This would also permit the same, or overlapping, syntax to be used in expressions and patterns with different meanings, for example: ``` fn F() { // Here, `[...]` is an array type. var array: [i32; 5]; match (&array) { // Here, `[...]` could introduce deduced parameters. case [T:! Type] x: T* => {} } } ``` Similarly, if we added a `&PATT` pattern to match the address of a value, a construct like `&n` would be ambiguous without in introducer: ``` var n: i32 = 5; fn MatchPointerOrPointee(p: i32*) { match (p) { case is &n => { Print("given pointer to n"); } // Here, the pattern `&PATT` could match the address of // a value that matches `PATT`. case &(is n) => { Print("given pointer to i32 whose value equals the value of n"); } case &(m: i32) => { Print("given pointer to value {0}", m); } } } ``` Such an introducer would also denote the portions of a pattern that are evaluated at runtime rather than at compile time, benefitting Carbon's goals of readability and of predictable performance, and would also add syntactic separation between the parts of a pattern that have full exhaustiveness checking and the parts that do not. However, this would also introduce additional ceremony for the common case where part of a pattern is a specific value. This could be mitigated by permitting certain kinds of value as patterns without an introducer, such as numeric literals and `true` and `false`, at the cost of introducing more complexity and more confusion over which cases require `is` and which do not. We have also not identified a good choice for the introducer syntax, should we pursue this direction. `is` is not an ideal choice, because elsewhere in Carbon syntax, it is a relation between a value and its type, so `is T` may be misread as matching values whose type is `T`. `==` _expression_ has been suggested, but that would imply the opposite operand order of that in this proposal -- _scrutinee_ `==` _expression_ rather than _expression_ `==` _scrutinee_ -- which would compare struct fields in a surprising order that diverges from the order of comparison for a struct pattern. A prefix `==` may also be visually surprising. For now, we do not add such an introducer, but this decision is expected to be revisited by a future proposal. See: - [2022-09-16 Discord discussion in #syntax](https://discord.com/channels/655572317891461132/709488742942900284/1020559500597538886) - [2022-12-05 open discussion](https://docs.google.com/document/d/1tEt4iM6vfcY0O0DG0uOEMIbaXcZXlNREc2ChNiEtn_w/edit#bookmark=id.utvzosvvfg80) ### Allow guards on arbitrary patterns We could treat guards as part of pattern syntax instead of as part of `case` syntax. However, since guards make a pattern refutable, this wouldn't allow them anywhere other than in `case`s in the current language design. It would allow them to be nested within cases: ``` match (x) { case (n: i32 if n > 5, "some string") => { ... } } ``` Such nesting might allow an expensive later check to be avoided. For example, in the above case we can avoid an `==` comparison on a string if a cheaper comparison of `n > 5` fails. However, this would introduce complexity into the grammar, and it's not clear that this feature would add sufficient value to justify that complexity. An additional concern is that if we add `let`...`else` syntax, this would presumably permit things like: ``` let n: i32 if n > 5 = 20 else { return 0; }; ``` ... where it would be easy to misparse the `if ... else` as being a single construct, where the intended parse would be: ``` let ((n: i32) if n > 5) = 20 else { return 0; }; ``` See also a [related Discord discussion](https://discord.com/channels/655572317891461132/748959784815951963/981691016040030279). ### Treat expression patterns as exhaustive if they cover all possible values We could do more work to treat a set of expression patterns as being exhaustive, if each pattern has a constant value and between those constant values, all possible values of the type are covered. The advantage of this would be that we improve the precision of our language rules. This change in rules has some disadvantages and problems: - It would add some complexity to the rules and to implementations in order to track whether all possible values have been created. - For even the simplest types where this would apply, such as `i8`, it seems unlikely that a `match` covering all possible values would be written, due to the large number of patterns required. - In many cases, the value being matched will carry an invariant so that matching a subset of the representable values would match all meaningful values. We would still have imprecise rules in those cases. - Expression patterns are matched with `==`, and for an arbitrary `==` it is not computable in general to tell whether a given set of values is exhaustive, so we would only be able to apply this in some subset of cases. This restriction is straightforward to work around by adding a final unreachable `default`, or by replacing the final value match with a `default` or `_`. ### Allow non-exhaustive `match` statements We could permit `match` statements that are not exhaustive, and execute none of the case blocks if none of the patterns match. This is a very common choice in the design of such language features. However, it is also a common source of errors, for example when matching a sum type and an alternative is missed. By making this a language rule, we ensure that developers can rely on such mistakes being caught. There is an easy syntactic way to disable this check, by adding an explicit `default => {}` case. If we made the opposite choice, we would not automatically have an easy way to request that the check be performed. We could add syntax to request it, but such syntax would likely be forgotten and the default behavior would be that errors are silently permitted. This seems sufficient to outweigh the potential ergonomic cost of requiring the `default => {}` case to be written explicitly. Another motivation for requiring exhaustiveness is that it simplifies other language rules. For example, when determining whether control flow can reach the end of a function with a declared return type, a separate exhaustiveness analysis is not necesasry. One concern with exhaustiveness checking is that it will cause the addition of an alternative to a choice type to be a breaking change by default. However, this is also one of the main advantages, and the design of choice types is intended to eventually provide a mechanism to specify that a choice type is extensible, which if used would mean that a set of patterns for that choice type would only be considered exhaustive if it includes a wildcard pattern. ## Future work ### Or patterns We could provide "or patterns", allowing matching of one pattern or another with the same handler: ``` match (x) { case (m: i32, 0) | (0, m: i32) => { return m; } } ``` See the [red-black tree rebalancing example](#example-from-p1371r5-red-black-tree-rebalancing) for a real-world example where this would result in a simplification. ### As patterns We could provide ["as-patterns"](https://en.wikibooks.org/wiki/Haskell/Pattern_matching#As-patterns) as a convenient way to give a name to a value while still matching parts of that value. Following Haskell, we could use: - _pattern_ ::= _identifier_ `@` _pattern_ For example: ``` match (x) { // `s` names the first element of the tuple. case (s@{.a = n: i32, .b = 12}, 4) if n > 1 => { return s; } } ``` ### Matching classes by dynamic type We could provide a way to match polymorphic class objects based on their dynamic type. This might be the default when matching a polymorphic class, or might require opt-in. The behavior in this proposal is that only the static type of the operand is considered. For example, we could default to matching the static type, and allow a `dyn` _pattern_ syntax for matching a pointer to a polymorphic class type, meaning that we match the dynamic type rather than the static type of the pointer: ``` abstract class Base { virtual fn F[me: Self](); } class Derived1 extends Base {} class Derived2 extends Base {} fn PrintType(b: Base*) { match (b) { // `case d1: Derived*` would be invalid here, // because it could never match. case dyn d1: Derived1* => { Print("Derived1"); } case dyn d2: Derived2* => { Print("Derived2"); } default => { Print("Unknown derived class"); } } } fn PrintTemplateType[template T:! Type](p: T*) { match (p) { // OK, dispatch is based on the static type. case b: Base* => { Print("Base"); } case d1: Derived1* => { Print("Derived1"); } case d2: Derived2* => { Print("Derived2"); } default => { Print("Unknown class"); } } } ``` However, at this time we do not have a design for a checked down-cast, so it's not clear how this matching operation would fit into the design of classes, either syntactically or semantically. ### User-defined pattern matching We plan to provide a mechanism for allowing a user-defined type to specify how it can be matched by patterns. See [proposal #157](/proposals/p0157.md) for details. ### Matching classes with struct patterns We could allow a class to be matched by a struct pattern that matches its fields. This would make sense especially for data classes, and would be consistent with the behavior of `==` in the case where a struct is implicitly convertible to the class type. However, without a design for user-defined pattern matching and matching on dynamic type, there is a significant risk that this would conflict with the rules there, so it is deferred for now. ### Matching by reference When the scrutinee is an lvalue, it is sometimes desirable to form a mutable binding to it. For example, see the [`struct` inspection example](#figure-5-struct-inspection) below. We currently support this only for the `me` binding in a method, using the `addr` keyword, but could allow this more generally. ```carbon fn takeDamage(p: player*) { match (*p) { case {.hitpoints = 0, .lives = 0, _} => { gameOver(); } case {.hitpoints = addr hp: i32*, .lives = addr l: i32*, _} if *hp == 0 => { *hp = 10; --*l; } case {.hitpoints = addr hp: i32*, _} if *hp <= 3 => { --*hp; messageAlmostDead(); } case {.hitpoints = addr hp: i32*, _} => { --*hp; } } } ``` Work in this area will need to consider whether we can provide this feature ergonomically without introducing reference-like behavior. ### Type deduction This proposal does not cover type deduction, instead considering it to be a separate topic from pattern matching syntax, even though the semantic behavior of the two may be quite similar or identical. We will need a proposal to explore type deduction and describe its functioning. ### Match expressions As demonstrated in the [switching an enum](#figure-3-switching-an-enum) example below, it would be valuable to have an expression `match` syntax in addition to the statement `match` syntax. We could follow the same approach as for `if` statements, and say that a `match` that appears at the start of a statement is a statement `match` and any other `match` is an expression `match`. As candidate syntax, braced `case` bodies could be replaced by an expression followed by a comma. For example: ``` let opengl_color: Vec3 = match (c) { case .red => Vec3.Make(1.0, 0.0, 0.0), case .yellow => Vec3.Make(1.0, 1.0, 0.0), case .green => Vec3.Make(0.0, 1.0, 0.0), case .blue => Vec3.Make(0.0, 0.0, 1.0) }; ``` ## Examples These examples are translations of examples in WG21 paper [P0095R1](https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/p0095r1.html) and [P1371R3](https://www.open-std.org/JTC1/SC22/WG21/docs/papers/2020/p1371r3.pdf), with permission from the author of those examples, David Sankel. Thank you, David! ### Examples from P0095R1 #### Figure 1. Declaration of a command data structure
C++P0095R1This proposal
```c++ struct set_score { std::size_t value; }; struct fire_missile {}; struct fire_laser { unsigned intensity; }; struct rotate { double amount; }; struct command { std::variant< set_score, fire_missile, fire_laser, rotate > value; }; ``` ```c++ lvariant command { std::size_t set_score; std::monostate fire_missile; unsigned fire_laser; double rotate; }; ``` ```carbon choice command { set_score(u64), fire_missile, fire_laser(u32), rotate(f64) } ```
#### Figure 2. Implementation of an output operator
C++P0095R1This proposal
```c++ namespace { struct Output { std::ostream& operator()(std::ostream& stream, const set_score& ss) const { return stream << "Set the score to " << ss.value << ".\n"; } std::ostream& operator()(std::ostream& stream, const fire_missile&) const { return stream << "Fire a missile.\n"; } std::ostream& operator()(std::ostream& stream, const fire_laser& fl) const { return stream << "Fire a laser with " << fl.intensity << " intensity.\n"; } std::ostream& operator()(std::ostream& stream, const rotate& r) const { return stream << "Rotate by " << r.degrees << " degrees.\n" } }; } std::ostream& operator<<(std::ostream& stream, const command& cmd) { return std::visit(std::bind(Output(), std::ref(stream), std::placeholders::_1), cmd.value); } ``` ```c++ std::ostream& operator<<(std::ostream& stream, const command& cmd) { return inspect(cmd) { set_score value => stream << "Set the score to " << value << ".\n" fire_missile _ => stream << "Fire a missile.\n" fire_laser intensity => stream << "Fire a laser with " << intensity << " intensity.\n" rotate degrees => stream << "Rotate by " << degrees << " degrees.\n" } } ``` ```carbon impl command as Printable { fn Print[me: Self](stream: Cpp.std.ostream*) { match (me) { case .set_score(value: u64) => { stream->Print("Set the score to {0}.\n", value); } case .fire_missile => { stream->Print("Fire a missile.\n"); } case .fire_laser(intensity: u32) => { stream->Print("Fire a laser with {0} intensity.\n", intensity); } case .rotate(degrees: f64) => { stream->Print("Rotate by {0} degrees.\n", degrees); } } } } ```
#### Figure 3. Switching an enum
C++P0095R1This proposal
```c++ enum color { red, yellow, green, blue }; ``` ```carbon choice color { red, yellow, green, blue } ```
```c++ const Vec3 opengl_color = [&c] { switch(c) { case red: return Vec3(1.0, 0.0, 0.0); break; case yellow: return Vec3(1.0, 1.0, 0.0); break; case green: return Vec3(0.0, 1.0, 0.0); break; case blue: return Vec3(0.0, 0.0, 1.0); break; default: std::abort(); }(); ``` ```c++ const Vec3 opengl_color = inspect(c) { red => Vec3(1.0, 0.0, 0.0) yellow => Vec3(1.0, 1.0, 0.0) green => Vec3(0.0, 1.0, 0.0) blue => Vec3(0.0, 0.0, 1.0) }; ``` ```carbon // Carbon has neither expression-match nor lambdas yet, // so this can't easily be done in line. fn GetOpenGLColor(c: color) -> Vec3 { match (c) { case .red => { return Vec3.Make(1.0, 0.0, 0.0); } case .yellow => { return Vec3.Make(1.0, 1.0, 0.0); } case .green => { return Vec3.Make(0.0, 1.0, 0.0); } case .blue => { return Vec3.Make(0.0, 0.0, 1.0); } } } let opengl_color: Vec3 = GetOpenGLColor(c); ```
#### Figure 4. Expression datatype
C++P0095R1This proposal
```c++ struct expression; struct sum_expression { std::unique_ptr left_hand_side; std::unique_ptr right_hand_side; }; struct expression { std::variant value; }; expression simplify(const expression & exp) { if(sum_expression const * const sum = std::get_if(&exp)) { if( int const * const lhsInt = std::get_if( sum->left_hand_side.get() ) && *lhsInt == 0 ) { return simplify(*sum->right_hand_side); } else if( int const * const rhsInt = std::get_if( sum->right_hand_side.get() ) && *rhsInt == 0 ) { return simplify(*sum->left_hand_side); } else { return {sum_expression{ std::make_unique(simplify(*sum->left_hand_side)), std::make_unique(simplify(*sum->right_hand_side))}} } } return exp; } void simplify2(expression & exp) { if(sum_expression * const sum = std::get_if(&exp)) { if( int * const lhsInt = std::get_if( sum->left_hand_side.get() ) && *lhsInt == 0 ) { expression tmp(std::move(*sum->right_hand_side)); exp = std::move(tmp); simplify(exp); } else if( int * const rhsInt = std::get_if( sum->right_hand_side.get() ) && *rhsInt == 0 ) { expression tmp(std::move(*sum->left_hand_side)); exp = std::move(tmp); simplify(exp); } else { simplify(*sum->left_hand_side); simplify(*sum->right_hand_side); } } return exp; } ``` ```c++ lvariant expression; struct sum_expression { std::unique_ptr left_hand_side; std::unique_ptr right_hand_side; }; lvariant expression { sum_expression sum; int literal; std::string var; }; expression simplify(const expression & exp) { return inspect(exp) { sum {*(literal 0), *rhs} => simplify(rhs) sum {*lhs , *(literal 0)} => simplify(lhs) sum {*lhs , *rhs} => expression::sum{ std::make_unique(simplify(lhs)), std::make_unique(simplify(rhs))}; _ => exp }; } void simplify2(expression & exp) { inspect(exp) { sum {*(literal 0), *rhs} => { expression tmp(std::move(rhs)); exp = std::move(tmp); simplify2(exp); } sum {*lhs , *(literal 0)} => { expression tmp(std::move(lhs)); exp = std::move(tmp); simplify2(exp); } sum {*lhs , *rhs} => { simplify2(lhs); simplify2(rhs); } _ => ; }; } ``` ```carbon choice expression { sum(UniquePtr(expression), UniquePtr(expression)), literal(i32), var: String } // This assumes that UniquePtr provides the matching // functionality described in #2187. fn simplify(exp: expression) -> expression { match (exp) { case .sum(.PtrTo(.literal(0)), .PtrTo(rhs: expression)) => { return simplify(rhs); } case .sum(.PtrTo(lhs: expression), .PtrTo(.literal(0))) => { return simplify(lhs); } case .sum(.PtrTo(lhs: expression), .PtrTo(rhs: expression)) => { return expression.sum(MakeUnique(simplify(lhs)), MakeUnique(simplify(rhs))); } default => { return exp; } } } ```
#### Figure 5. `struct` inspection
C++P0095R1This proposal
```c++ struct player { std::string name; int hitpoints; int lives; }; ``` ```carbon class player { var name: String; var hitpoints: i32; var lives: i32; } ```
```c++ void takeDamage(player &p) { if(p.hitpoints == 0 && p.lives == 0) gameOver(); else if(p.hitpoints == 0) { p.hitpoints = 10; p.lives--; } else if(p.hitpoints <= 3) { p.hitpoints--; messageAlmostDead(); } else { p.hitpoints--; } } ``` ```c++ void takeDamage(player &p) { inspect(p) { {hitpoints: 0, lives:0} => gameOver(); {hitpoints:hp@0, lives:l} => hp=10, l--; {hitpoints:hp} if (hp <= 3) => { hp--; messageAlmostDead(); } {hitpoints:hp} => hp--; } } ``` ```carbon fn takeDamage(p: player*) { match (*p) { case {.hitpoints = 0, .lives = 0, _} => { gameOver(); } case {.hitpoints = 0, _} => { p->hitpoints = 10; --p->lives; } case {.hitpoints = hp: i32, _} if hp <= 3 => { --p->hitpoints; messageAlmostDead(); } default => { --p->hitpoints; } } } ```
### Example from P1371R5: Red-black tree rebalancing #### With P1371R5 pattern matching ```c++ enum Color { Red, Black }; template struct Node { void balance(); Color color; std::shared_ptr lhs; T value; std::shared_ptr rhs; }; template void Node::balance() { *this = inspect (*this) { [case Black, (*?) [case Red, (*?) [case Red, a, x, b], y, c], z, d] => Node{Red, std::make_shared(Black, a, x, b), y, std::make_shared(Black, c, z, d)}; [case Black, (*?) [case Red, a, x, (*?) [case Red, b, y, c]], z, d] // left-right case => Node{Red, std::make_shared(Black, a, x, b), y, std::make_shared(Black, c, z, d)}; [case Black, a, x, (*?) [case Red, (*?) [case Red, b, y, c], z, d]] // right-left case => Node{Red, std::make_shared(Black, a, x, b), y, std::make_shared(Black, c, z, d)}; [case Black, a, x, (*?) [case Red, b, y, (*?) [case Red, c, z, d]]] // right-right case => Node{Red, std::make_shared(Black, a, x, b), y, std::make_shared(Black, c, z, d)}; self => self; }; } ``` #### With this proposal ```carbon choice Color { Red, Black } class Node(T:! Type) { fn balance[addr me: Self*](); var color: Color; var lhs: SharedPtr(Node); var value: T; var rhs: SharedPtr(Node); } fn MakeBalanced[T:! Type](a: SharedPtr(Node(T)), x: T, b: SharedPtr(Node(T)), y: T, c: SharedPtr(Node(T)), z: T, d: SharedPtr(Node(T))) -> Node(T) { return {.color = Color.Red, .lhs = MakeShared({.color = Color.Black, .lhs = a, .value = x, .rhs = b}), .value = y, .rhs = MakeShared({.color = Color.Black, .lhs = c, .value = z, .rhs = d})}; } fn Node(T:! Type).balance[addr me: Self*]() { match (*me) { {.color = .Black, .lhs = .PtrTo( {.color = .Red, .lhs = .PtrTo( {.color = .Red, .lhs = a: auto, .value = x: T, .rhs = b: auto}), .value = y: T, .rhs = c: auto}), .value = z: T, .rhs = d: auto} => { *me = MakeBalanced(a, x, b, y, c, z, d); } {.color = .Black, .lhs = .PtrTo( {.color = .Red, .lhs = a: auto, .value = x: T, .rhs = .PtrTo( {.color = .Red, .lhs = b: auto, .value = y: T, .rhs = c: auto})}), .value = z: T, .rhs = d: auto} => { *me = MakeBalanced(a, x, b, y, c, z, d); } {.color = .Black, .lhs = a: auto, .value = x: T, .rhs = .PtrTo( {.color = .Red, .lhs = .PtrTo( {.color = .Red, .lhs = b: auto, .value = y: T, .rhs = c: auto}), .value = z: T, .rhs = d: auto})} => { *me = MakeBalanced(a, x, b, y, c, z, d); } {.color = .Black, .lhs = a: auto, .value = x: T, .rhs = .PtrTo( {.color = .Red, .lhs = b: auto, .value = y: T, .rhs = .PtrTo( {.color = .Red, .lhs = c: auto, .value = z: T, .rhs = d: auto})})} => { *me = MakeBalanced(a, x, b, y, c, z, d); } default => {} }; } ``` #### With this proposal plus #2187 ```carbon choice Color { Red, Black } class Node(T:! Type) { fn balance[addr me: Self*](); var color: Color; var lhs: SharedPtr(Self); var value: T; var rhs: SharedPtr(Self); external impl as Match { interface Continuation { extends Match.BaseContinuation; fn Red[addr me: Self*](lhs: SharedPtr(Self), value: T, rhs: SharedPtr(Self)) -> ReturnType; fn Black[addr me: Self*](lhs: SharedPtr(Self), value: T, rhs: SharedPtr(Self)) -> ReturnType; } fn Op[me: Self, C:! Continuation](continuation: C*) -> C.ReturnType { match (me.color) { case .Red => { return continuation->Red(me.lhs, me.value, me.rhs); } case .Black => { return continuation->Black(me.lhs, me.value, me.rhs); } } } } } fn MakeBalanced[T:! Type](a: SharedPtr(Node(T)), x: T, b: SharedPtr(Node(T)), y: T, c: SharedPtr(Node(T)), z: T, d: SharedPtr(Node(T))) -> Node(T) { return {.color = Color.Red, .lhs = MakeShared({.color = Color.Black, .lhs = a, .value = x, .rhs = b}), .value = y, .rhs = MakeShared({.color = Color.Black, .lhs = c, .value = z, .rhs = d})}; } fn Node(T:! Type).balance[addr me: Self*]() { match (*me) { .Black(.PtrTo(.Red(.PtrTo(.Red(a: auto, x: T, b: auto)), y: T, c: auto)), z: T, d: auto) => { *me = MakeBalanced(a, x, b, y, c, z, d); } .Black(.PtrTo(.Red(a: auto, x: T, .PtrTo(.Red(b: auto, y: T, c: auto)))), z: T, d: auto) => { *me = MakeBalanced(a, x, b, y, c, z, d); } .Black(a: auto, x: T, .PtrTo(.Red(.PtrTo(.Red(b: auto, y: T, c: auto)), z: T, d: auto))) => { *me = MakeBalanced(a, x, b, y, c, z, d); } .Black(a: auto, x: T, .PtrTo(.Red(b: auto, y: T, .PtrTo(.Red(c: auto, z: T, d: auto))))) => { *me = MakeBalanced(a, x, b, y, c, z, d); } default => {} }; } ``` ================================================ FILE: proposals/p2200.md ================================================ # Template generics [Pull request](https://github.com/carbon-language/carbon-lang/pull/2200) ## Table of contents - [Abstract](#abstract) - [Problem](#problem) - [Background](#background) - [Terminology](#terminology) - [Proposal](#proposal) - [Details](#details) - [Syntax](#syntax) - [Branching on type identity](#branching-on-type-identity) - [Value phases](#value-phases) - [`auto`](#auto) - [Template constraints](#template-constraints) - [Name lookup](#name-lookup) - [Transition from C++ templates to Carbon checked generics](#transition-from-c-templates-to-carbon-checked-generics) - [To template Carbon with structural constraints](#to-template-carbon-with-structural-constraints) - [To interface constraints](#to-interface-constraints) - [To checked generic](#to-checked-generic) - [Validity can depend on value](#validity-can-depend-on-value) - [Template dependent](#template-dependent) - [Simple member access](#simple-member-access) - [Compound member access](#compound-member-access) - [Impl lookup](#impl-lookup) - [Use of dependent value is dependent](#use-of-dependent-value-is-dependent) - [Rationale](#rationale) - [Alternatives considered](#alternatives-considered) - [Only checked generics](#only-checked-generics) - [SFINAE](#sfinae) - [Ad hoc API specialization](#ad-hoc-api-specialization) - [Value phase of bindings determined by initializer](#value-phase-of-bindings-determined-by-initializer) - [Simpler template dependent rules that delayed more checking](#simpler-template-dependent-rules-that-delayed-more-checking) - [Future work](#future-work) - [Expanded template constraints](#expanded-template-constraints) - [Predicates: constraints on values](#predicates-constraints-on-values) - [Checked generics calling templates](#checked-generics-calling-templates) - [Which expressions will be evaluated at compile time](#which-expressions-will-be-evaluated-at-compile-time) ## Abstract Add template generics, with optional constraints but no [SFINAE](https://en.cppreference.com/w/cpp/language/sfinae), to Carbon. Template generics allows the compiler to postpone type checking of expressions dependent on a template parameter until the function is called and the value of that parameter is known. Example usage: ```carbon fn Identity[template T:! Type](x: T) -> T { return x; } ``` ## Problem Starting with [#24: Generics goals](https://github.com/carbon-language/carbon-lang/pull/24), we have assumed templates (also known as "template generics" in Carbon) will be a feature of Carbon, but it has not been an accepted part of the design. We now understand enough about how they should fit into the language to decide that we are including the feature in the language, and what form they should take. Template generics will address these use cases: - They provide a step in the transition from C++ templates to Carbon checked generics. - They provide a generics programming model familiar to C++ developers. - They allow Carbon to separate features that we don't want to expose by default in checked generics. These are features that pierce abstraction boundaries that we want to discourage for software engineering reasons or at least mark when they are in use. Examples in this category include: - Compile-time duck typing features that use the structural properties of types, like having a method with a particular name, rather than semantic properties like implementing an interface. - Branching in code based on type identity. Out of scope for this proposal are any questions about passing a checked generic argument value to a template parameter. See question-for-leads issue [#2153: Generics calling templates](https://github.com/carbon-language/carbon-lang/issues/2153). ## Background Templates are the mechanism for performing [generic programming](https://en.wikipedia.org/wiki/Generic_programming) in C++, see [cppreference.com](https://en.cppreference.com/w/cpp/language/templates). There have been a number of prior proposals and questions-for-leads issues on template generics on which this proposal builds: - Proposal [#24: Generics goals](https://github.com/carbon-language/carbon-lang/pull/24) talked about the reasons for templates, without committing Carbon to including them. These reasons include making it easier to transition C++ template code to Carbon and providing functionality outside of what we want to support with checked generics. - Proposal [#447: Generics terminology](https://github.com/carbon-language/carbon-lang/pull/447) defined terminology. This included some of the differences between checked and template generics, and definitions for terms like _instantiation_. - Proposal [#553: Generic details part 1](https://github.com/carbon-language/carbon-lang/pull/553) defined `auto` as a template construct, and described how templates do not require constraints to find member names. - Question-for-leads issue [#565: Generic syntax to replace provisional `$`s](https://github.com/carbon-language/carbon-lang/issues/565) implemented in proposal [#676: `:!` generic syntax](https://github.com/carbon-language/carbon-lang/pull/676) defined the syntax for template bindings. - Proposal [#731: Generics details 2: adapters, associated types, parameterized interfaces](https://github.com/carbon-language/carbon-lang/pull/731) included that template values may be passed to generic parameters. - Proposal [#818: Constraints for generics](https://github.com/carbon-language/carbon-lang/pull/818) included `template constraint` to defined named constraints with fewer restrictions for use with template parameters. - Proposal [#875: Principle: information accumulation](https://github.com/carbon-language/carbon-lang/pull/875) considered how the principle benefited and was impacted by templates. - Question-for-leads issue [#949: Constrained template name lookup](https://github.com/carbon-language/carbon-lang/issues/949) implemented in proposal [#989: Member access expressions](https://github.com/carbon-language/carbon-lang/pull/989) defined how name lookup works for template parameters. It provided a path to incrementally adopt constraints on template parameters, a stepping stone to transitioning to checked generics. - Proposal [#950: Generics details 6: remove facets](https://github.com/carbon-language/carbon-lang/pull/950) included the impact on the semantics of templates in its rationale. - Proposal [#1146: Generic details 12: parameterized types](https://github.com/carbon-language/carbon-lang/pull/1146) allowed template type parameters. - Proposal [#1270: Update and expand README content and motivation for Carbon](https://github.com/carbon-language/carbon-lang/pull/1270) advertised that Carbon would support templates for "seamless C++ interop." - Terminology was updated in proposal [#2138: Checked and template generic terminology](https://github.com/carbon-language/carbon-lang/pull/2138). TODO: Update if proposal [#2188: Pattern matching syntax and semantics](https://github.com/carbon-language/carbon-lang/pull/2188) is accepted first. ## Terminology - A _template dependent_ name or expression is one whose meaning depends on, that is varies with, some template generic parameter. Specifically this refers to an expression that can not be fully checked until the value of the parameter is known. This is consistent with the meaning of this term in [C++](https://en.cppreference.com/w/cpp/language/dependent_name), but is different from ["dependent types"](https://en.wikipedia.org/wiki/Dependent_type). The specifics of how this works in Carbon are being proposed in [a later section](#template-dependent). - [_Instantiation_](/docs/design/generics/terminology.md#instantiation), _substitution_, or [_monomorphizaton_](https://en.wikipedia.org/wiki/Monomorphization) is the process of duplicating the implementation of a function and then substituting in the values of any (checked or template) generic arguments. - Errors that are only detected once the argument value from the call site is known are called _monomorphization errors_. These mostly occur in expressions dependent on some template parameter, but can also occur for other reasons like hitting an implementation limit. - _SFINAE_ stands for "Substitution failure is not an error", which is the policy in C++, see [cppreference](https://en.cppreference.com/w/cpp/language/sfinae), [wikipedia](https://en.wikipedia.org/wiki/Substitution_failure_is_not_an_error). It means that functions from an overload set with monorphization errors, or "substitution failure," within their signatures are simply ignored instead of causing compilation to fail. ## Proposal We propose that template generics are included as an official feature of Carbon. In many ways, template generic parameters work like checked generic parameters. The following are true for any kind of generic parameter: - The value passed to a generic parameter must be able to be evaluated at compile time. - Generic parameters may have constraints that will be enforced by the compiler on the value supplied by the caller. - The compiler may choose to generate multiple copies of a generic function for different values of the generic parameters. The main differences between checked and templated generics are: - Member lookup into a templated type looks in the actual type value provided by the caller in addition to in any constraints on that type; see [proposal #989](https://github.com/carbon-language/carbon-lang/pull/989). - A templated parameter may be used in ways where the validity of the result depends on the value of the parameter, not just its type. - Impl lookup is delayed until all templated types, interfaces, and parameters are known. As a consequence of these differences, type checking of any expression dependent on a templated parameter may not be completed until its value is known. In addition, templated generics support branching on the value of a templated type. In contrast with C++ templates, with Carbon template generics: - Substitution failure is an error. In C++, [the SFINAE rule](#terminology) will skip functions in overload resolution that fail to instantiate. Instead, Carbon template parameters use constraints to control when the function is available. - Carbon template specialization does not allow ad hoc changes to the API of the function or type being specialized, only its implementation. This is in contrast to C++, where [C++'s `std::vector`](https://en.cppreference.com/w/cpp/container/vector_bool) has different return types for certain methods. Anything that can vary in an API must be explicitly marked using associated types of an interface, as is described in the ["parameterized type specialization" design](/docs/design/generics/details.md#specialization). - Constraints on a Carbon template type affect how lookup is done into that type, as described in [proposal #989](https://github.com/carbon-language/carbon-lang/pull/989). ## Details ### Syntax Template generic bindings are declared using the `template` keyword in addition to the `:!` of all generic bindings. This includes `let` declarations, as in: ```carbon // `N` is a constant that may be used in types. let template N:! i64 = 4; var my_array: [u8; N] = (255, 128, 64, 255); ``` Function parameters also default to a `let` context and may use `template`: ```carbon // `U` is a templated type parameter that must be specified // explicitly by the caller. fn Cast[template T:! Type](x: T, template U:! Type) -> U { // OK, check for `T is As(U)` delayed until values of `T` and `U` are known. return x as U; } let x: i32 = 7; // Calls `Cast` with `T` set to `i32` and `U` set to `i64`. let y: auto = Cast(x, i64); // Type of `y` is `i64`. ``` Note that generic bindings, checked or template, can only be used in `let` context to produce r-values, not in a `var` context to produce l-values. ```carbon // ❌ Error: Can't use `:!` with `var`. Can't be both a // compile-time constant and a variable. var N:! i64 = 4; // ❌ Error: Can't use `template :!` with `var` for the // same reason. var template M:! i64 = 5; ``` #### Branching on type identity Branching on the value of a templated type will be done using a `match` statement, but is outside the scope of this proposal. See instead pending proposal [#2188: Pattern matching syntax and semantics](https://github.com/carbon-language/carbon-lang/pull/2188). ### Value phases R-values are divided into three different _value phases_: - A _constant_ has a value known at compile time, and that value is available during type checking, for example to use as the size of an array. These include literals (integer, floating-point, string), concrete type values (like `f64` or `Optional(i32*)`), expressions in terms of constants, and values of `template` parameters. - A _symbolic value_ has a value that will be known at the code generation stage of compilation when monomorphization happens, but is not known during type checking. This includes checked-generic parameters, and type expressions with checked-generic arguments, like `Optional(T*)`. - A _runtime value_ has a dynamic value only known at runtime. So: - A `let template T:! ...` or `fn F(template T:! ...)` declaration binds `T` with constant value phase, - A `let T:! ...` or `fn F(T:! ...)` declaration binds `T` with symbolic value phase, - A `let x: ...` or `fn F(x: ...)` declaration binds `x` with runtime value phase. **Note:** The naming of value phases is the subject of open question-for-leads issue [#1391: New name for "constant" value phase](https://github.com/carbon-language/carbon-lang/issues/1391). This terminology comes from [a discussion in #typesystem on Discord](https://discord.com/channels/655572317891461132/708431657849585705/992817321074774098), in particular [this message](https://discord.com/channels/655572317891461132/708431657849585705/994396535561408623). **Note:** This reflects the resolution of question-for-leads issue [#1371: Is `let` referentially transparent?](https://github.com/carbon-language/carbon-lang/issues/1371) that the value phase of a binding is determined by the kind of binding, and not anything about the initializer. **Note:** The situations in which a value with one phase can be used to initialize a binding with a different value phase is future work, partially considered in question-for-leads issue [#2153: Generics calling templates](https://github.com/carbon-language/carbon-lang/issues/2153) in addition to [#1371: Is `let` referentially transparent?](https://github.com/carbon-language/carbon-lang/issues/1371). **Note:** Exactly which expressions in terms of constants result in constants is an open question that is not resolved by this proposal. In particular, which function calls will be evaluated at compile time is not yet specified. See [future work](#which-expressions-will-be-evaluated-at-compile-time). ### `auto` The `auto` keyword is a shortcut for an unnamed templated type, as in: ```carbon // Type of `x` is the same as the return type of function `F`. let x: auto = F(); ``` This was first added to Carbon in proposal [#553: Generic details part 1](https://github.com/carbon-language/carbon-lang/pull/553) and further specified by open proposal [#2188: Pattern matching syntax and semantics](https://github.com/carbon-language/carbon-lang/pull/2188). The `auto` keyword may also be used to omit the return type, as specified in [#826: Function return type inference](https://github.com/carbon-language/carbon-lang/pull/826). The semantics of `let x:! auto = ...` is the subject of open question-for-leads issue [#996: Generic `let` with `auto`?](https://github.com/carbon-language/carbon-lang/issues/996). ### Template constraints Template constraints have already been introduced in proposal [#818: Constraints for generics](https://github.com/carbon-language/carbon-lang/pull/818). In brief, a `template constraint` declaration is like a `constraint` declaration, except that it may also contain function and field declarations, called _structural constraints_. Only types with matching declarations will satisfy the template constraint. Note that the declarations matching the structural constraints must be found by member lookups in the type. It is not sufficient for them to be declared only in an external impl. ```carbon interface A { fn F[me: Self](); } interface B { fn F[me: Self](); } class C { } external impl C as A; external impl C as B; template constraint HasF { fn F[me: Self](); } fn G[template T:! HasF](x: T); var y: C = {}; // Can't call `G` with with `y` since it doesn't have any internal // implementation of a method `F` satisfying `HasF`, even though `C` // externally implements both `A` and `B` with such an `F`. May // define an adapter for `C` to get a type that implements `HasF`, // with `A.F`, `B.F`, or some other definition. ``` This was discussed in [#generics-and-templates on 2022-09-20](https://discord.com/channels/655572317891461132/941071822756143115/1021903925613449316). Structural constraints do not affect [name lookup](#name-lookup) into template type parameters. They guarantee that a name will be available in the type, but don't change the outcome. ```carbon template constraint HasF { fn F[me: Self](); } class C { fn F[me: Self](); } fn G[template T:! HasF](x: T) { x.F(); } var y: C = {}; // Call to `F` inside `G` is not ambiguous since // `C.F` and `HasF.F` refer to the same function. G(y); class D extends C { alias F = C.(A.F); } // OK, `z.(HasF.F)` will resolve to `z.(C.(A.F))`. fn Run(z: D) { G(z); } ``` Whether template constraints may be used as constraints on checked-generic parameters is being considered in question-for-leads issue [#2153: Generics calling templates](https://github.com/carbon-language/carbon-lang/issues/2153). Even if we allow a checked-generic parameter to use a template constraint, we want to focus checked generics on semantic properties encapsulated in interfaces, not structural properties tested by template constraints. So we would not allow lookup into a checked-generic type to find type members outside of an interface: ```carbon template constraint HasF { fn F[me: Self](); } // ❓ If we allow a checked generic to use a template // constraint, as in: fn H[T:! HasF](x: T) { // We still will not support calling `F` on `x`: // ❌ x.F(); } ``` These members would only be found using a template type parameter. [Expanding the kinds of template constraints](#expanded-template-constraints) and [defining a way to put constraints on values](#predicates-constraints-on-values) are both [future work](#future-work). ### Name lookup Name lookup for templates has already been decided in question-for-leads issue [#949: Constrained template name lookup](https://github.com/carbon-language/carbon-lang/issues/949) and proposal [#989: Member access expressions](https://github.com/carbon-language/carbon-lang/pull/989). Briefly, name lookup is done both in the actual type value supplied at the call site and the interface constraints on the parameter. If the name is found in both, it is an error if they resolve to different entities. Look up into the calling type gives _compile-time duck typing_ behavior, much like C++ templates, as in: ```carbon fn F[template T:! Type](x: T) { // Calls whatever `M` is declared in `T`, and will // fail if `T` does not have a matching member `M`. x.M(); } class C1 { fn M[me: Self](); } var x1: C1 = {}; // Calls `F` with `T` equal to `C1`, which succeeds. F(x1); class C2 { fn M[addr me: Self*](); } var x2: C2 = {}; // Calls `F` with `T` equal to `C2`, which fails, // since `x` is an r-value in `F` and `C2.M` requires // an l-value. F(x2); class C3 { fn M[me: Self](p: i32); } var x3: C3 = {}; // Calls `F` with `T` equal to `C3`, which fails, // since `C3.M` must be passed an argument value. F(x3); class C4 { fn M[me: Self](p: i32 = 4); } var x4: C4 = {}; // Calls `F` with `T` equal to `C4`, which succeeds, // using the default value of `4` for `p` when // calling `C4.M`. F(x4); class C5 { var v: i32; } var x5: C5 = {.v = 5}; // Calls `F` with `T` equal to `C5`, which fails, // since `T` has no member `M`. F(x5); ``` Note that in some cases of looking up a qualified name, lookup will not depend on the value of the template parameter and can be checked before instantiation, as in: ```carbon interface A { fn F[me: Self](); } fn G[template T:! A](x: T) { // No question what this resolves to, can be checked // when `G` is defined: x.(A.F)(); // Will generate a monomorphization error if // `T.F` means something different than `T.(A.F)`, // can only be checked when `G` is called: x.F(); } ``` ### Transition from C++ templates to Carbon checked generics We have a specific [goal for generics](/docs/design/generics/goals.md#upgrade-path-from-templates) that we have a smooth story for transitioning from C++ templates to Carbon checked generics. Adding template generics to Carbon allows this to be done in steps. These steps serve two purposes. One is to allow any updates needed for callers and types used as parameters to be done incrementally. The second is to avoid any silent changes in semantics that would occur from jumping directly to Carbon checked generics. Each step will either preserve the meaning of the code or result in compile failures. #### To template Carbon with structural constraints The first step is to convert the C++ function with one or more template parameters to a Carbon function with template generic parameters. Any [non-type template parameters](https://en.cppreference.com/w/cpp/language/template_parameters#Non-type_template_parameter) can be converted to template generic parameters with the equivalent type, as in: ``` // This C++ function: void F_CPlusPlus(); // gets converted to Carbon: fn F_Carbon(template N:! i32); ``` Other template parameters can either be declared without constraints, using `template T:! Type`, or using [structural constraints](#template-constraints). To see if this transition can cause silent changes in meaning, consider how this new Carbon function will be different from the old C++ one: - The conversion of the body of the code in the function could introduce differences, but only template concerns are in scope for this proposal. - The C++ code could use ad hoc API specialization. The only way to translate that to Carbon is through [explicit parameterization of the API](/docs/design/generics/details.md#specialization), which is not expected to introduce silent changes in meaning. - The C++ code could rely on [SFINAE](#terminology). C++ uses of `std::enable_if` should be translated to equivalent [template constraints](#template-constraints). Generally making substitution failure an error is expected to make less code compile, not introduce silent changes in meaning. - As long as the constraints on template type parameters are [structural](#template-constraints) and not interface constraints, the name lookup rules into those type parameters will consistently look in the type for both C++ and Carbon. #### To interface constraints The next step is to switch from structural constraints to interface constraints. The interfaces that are providing the functionality that the function relies on must be identified or created. In some cases this could be done automatically when names are resolved consistently to interface methods in the types currently being used to instantiate the function. Once that is done, there are two approaches: - Implement the interface for every instantiating type. Once that is done, the function's constraint can be updated. - Alternatively, a blanket implementation of the interface could be defined for any type implementing the structural constraints so that the function's constraint can be updated first. After that, the interface can be implemented for types individually, overriding the blanket implementation until the blanket implementation is no longer needed. This second choice requires changes to the library defining the interface, and is most appropriate when it is a new interface specifically created for this function. In either case, the compiler will give an error if the interface is not implemented for some types before the step is finished. An example of the second approach, starting with a templated function with a structural constraint: ```carbon template constraint HasF { fn F[me: Self](); } fn G[template T:! HasF](x: T) { x.F(); } class C { fn F[me: Self](); } var y: C = {}; G(y); ``` First, a new interface is created with a blanket implementation and the function's constraints are updated to use it instead. Calls in the function body should be qualified to avoid ambiguity errors. ```carbon template constraint HasF { fn F[me: Self](); } // New interface interface NewF { fn DoF[me: Self](); } // Blanket implementation external impl forall [template T:! HasF] T as NewF { // If the functions are identical, can instead do: // alias DoF = T.F; fn DoF[me: Self]() { me.F(); // Or: me.(T.F)(); } } // Changed constraint fn G[template T:! NewF](x: T) { // Call function from interface x.(NewF.DoF)(); // Could use `x.DoF();` instead, but that will // give a compile error if `T` has a definition // for `DoF` in addition to the one in `NewF`. } class C { fn F[me: Self](); } var y: C = {}; // Still works since `C` implements `NewF` // from blanket implementation. G(y); ``` Then the interface is implemented for types used as parameters: ```carbon // ... class C { impl as NewF { // `NewF.DoF` will be called by `G`, not `C.F`. fn DoF[me: Self](); } // No longer needed: fn F[me: Self](); } // ... ``` Once all types have implemented the new interface, the blanket implementation can be removed: ```carbon // Template constraint `HasF` no longer needed. // New interface interface NewF { fn DoF[me: Self](); } // Blanket implementation no longer needed. fn G[template T:! NewF](x: T) { x.(NewF.DoF)(); } class C { impl as NewF { fn DoF[me: Self](); } } var y: C = {}; // `C` implements `NewF` directly. G(y); ``` The [name lookup rules](#name-lookup) ensure that unqualified names will only have one possible meaning, or the compiler will report an error. This error may be resolved by adding qualifications, which is done with the context of an ambiguity for a specific type. This avoids silent changes in the meaning of the code. Once the disambiguating qualifications have been added, the transition to checked generic becomes safe. #### To checked generic Once all needed qualifications are in place, the `template` keyword can be removed. After this, names will only be looked up in the interface constraints, not the type. If this does not cover the names used by the function, the compiler will report an error and this step can be rolled back and the previous step can be repeated to cover what was missed. Qualifications in the body of the function can be removed at this point, if desired, since the compiler will complain if this introduces an ambiguity from two different interfaces using that name. It would be reasonable to leave the qualifications in if the type parameter has multiple interface constraints, both as documentation for readers and to protect against future name collisions if the interfaces are changed. ### Validity can depend on value A templated parameter may be used in ways where the validity of the result depends on the value of the parameter, not just its type. As an example, whether two array types are compatible depends on whether they have the same size. With a symbolic constant sizes, they will only be considered equal if the compiler can show that the two sizes are always equal symbolically. If the size is a template parameter, the checking will be delayed until the value of the template parameter is known. ### Template dependent Expressions fall under three categories: - Expressions that are valid and have meaning determined without knowing the value of any template parameter are _not template dependent_. - Expressions whose meaning and validity requires knowing the value of a template parameter are _template dependent_. Template dependent expressions are not fully type checked until the template is instantiated, which can result in monomorphization errors. Further, template dependent subexpressions commonly cause a containing expression to also be dependent, as described in the ["use of dependent value is dependent" section](#use-of-dependent-value-is-dependent). - Expressions that have a meaning without knowing the value of any template parameter, assuming it is valid, but whose validity requires knowing the value of a template parameter are _template validity dependent_. These expressions can trigger a monomorphization error, but are not considered template dependent for purposes of a containing expression. The compiler will type check expressions that are not template dependent when the function is defined, and they won't trigger monomorphization errors. Note that an expression may not be template dependent even though it has a template-dependent sub-expression. For example, a function may have a value that is function dependent, but calling that function only needs the type of the function (meaning the function's signature) to not be template dependent. #### Simple member access There are three cases when performing unqualified member-name lookup into a templated type: ```carbon fn F[template T:! I](x: T) { x.G(); } ``` - If generic name lookup would succeed, in this example it would be because `G` is a member of `I`, then the result of name lookup is template validity dependent. This means that template instantiation may fail if `T` has a member `G` different than `T.(I.G)`. Assuming it succeeds, though, it will definitely have meaning determined by `I.G`. There may still be ambiguity making the result dependent if it is not known whether `x` is a type and `I.G` is a method and so has an implicit `me` parameter. - If the member name is not found in the constraint, lookup may still succeed once the type is known, so the result is template dependent. - If the lookup is ambiguous prior to knowing the value of the type, for example if `G` has two distinct meanings in `I`, then the code is invalid. The value of the expression will be dependent. For example, if `U` is an associated type of `I`, then `T.U` as an expression is template validity dependent, but the value of that expression is dependent. The value is not always needed to perform checking, for example `x.G()` can be checked without ever determining the value of `x.G` as long as its signature can be determined, if it is valid. #### Compound member access Adding qualifier to the member name, as in `x.(U.V)`, can make the lookup less dependent on the template parameter: - If `x` is dependent, and `U` is an interface, the value of the expression is dependent, but the type of the expression is not dependent unless the type of `U.V` involves `Self`. The lookup itself follows proposal [#2360](https://github.com/carbon-language/carbon-lang/pull/2360): - If `U.V` is an _instance_ member, and the type of `x` is known to implement `U`, then the lookup is not dependent. For example, there could be a requirement on `x` or a sufficiently general implementation of `U` that includes all possible types of `x`. The resulting value is dependent unless the implementation of `U` is `final`, see [the impl lookup section](#impl-lookup). - Otherwise, the lookup is template validity dependent. ```carbon interface Serializable { fn Serialize[me: Self](); } interface Printable { fn Print[me: Self](); } interface Hashable { let HashType:! Type; fn Hash[me: Self]() -> HashType; } external impl forall [T:! Serializable] T as Hashable; fn F[template T:! Serializable](x: T) { // `T` is required to implement `Serializable` and // `Serialize` is an instance member, so this is not // dependent. x.(Serializable.Serialize)(); // Any `T` implementing `Serializable` also implements // `Hashable`, since there is a blanket implementation, // so this is not dependent. Note: there may be a // specialization of this impl, so we can't rely on // knowing how `T` implements `Hashable`. x.(Hashable.Hash)(); // Unclear whether `T` implements `Printable`, but if // does, clear what this means, so this is template // validity dependent x.(Printable.Print)(); match (x.(Hashable.Hash)()) { // Uses the value of the associated type // `Hashable.HashType` that is template dependent. case _: u64 => { ... } default => { ... } } } ``` - If `U.V` is dependent, then the entire expression is dependent. #### Impl lookup If the validity of an expression requires that an impl exist for a type, and that can't be determined until the value of a template parameter is known, then the expression is template validity dependent. For example, in the example from the previous section, `x.(Printable.Print)()` is valid if `T` implements `Printable`. This can also occur without a qualified lookup, for example: ```carbon fn F[T:! Printable](x: T); fn G[template T:! Type](x: T) { // Valid if `T` implements `Printable`, so this // expression is template validity dependent. F(x); } ``` The values of members of an impl for a template-dependent type are template dependent, unless they can be resolved to a not template-dependent expression using checked-generic impl resolution. ``` final external impl [T:! Type] T* as D where .Result = T and .Index = i32; fn F[T:! Type](p: T*) { // `(T*).(D.Index)` uses the final impl of `D` for `T*`, // and so equals `i32`, which is not dependent. // `(T*).(D.Result)` is recognized as equal to `T`, which // is template dependent. } ``` #### Use of dependent value is dependent To match the expectations of C++ templates, uses of dependent values are also template dependent, propagating dependence from subexpressions to enclosing expressions. For example, if `expr` is a dependent expression, each of these is dependent: - `(expr)` - `F(expr)` - `expr + 1` - `if a then expr else b` - `a as expr` In some cases, an expression's type and value category can be determined even when a subexpression is dependent. This makes the expression template validity dependent, rather than dependent. For example, the types of these expressions are not dependent, even when `expr` is a dependent subexpression: - `if expr then a else b` - `expr as T` For `match`, which `case` body is executed may be dependent on the type of the match expression. For example: ``` fn TypeName[template T:! Type](x: T) -> String { match (x) { // Each entire case body is dependent case _: i32 => { return "int"; } case _: bool => { return "bool"; } case _: auto* => { return "pointer"; } // Allowed even though body of case is invalid for // `T != Vector(String)` case _: Vector(String) => { return x.front(); } default => { return "unknown"; } } } ``` ## Rationale This proposal advances these Carbon goals: - [Performance-critical software](/docs/project/goals.md#performance-critical-software), by providing an alternative to checked generics that has greater access to the specific value of the parameter. - [Code that is easy to read, understand, and write](/docs/project/goals.md#code-that-is-easy-to-read-understand-and-write) by specifically marking code that is using features that should receive greater scruitiny. - [Interoperability with and migration from existing C++ code](/docs/project/goals.md#interoperability-with-and-migration-from-existing-c-code), specifically migrating C++ code using templates, as detailed in the ["transition from C++ templates to Carbon checked generics" section](#transition-from-c-templates-to-carbon-checked-generics). ## Alternatives considered ### Only checked generics Like Rust, Carbon could use only checked generics and not support templates. The reasons for this approach are detailed in the ["problem" section](#problem). ### SFINAE We could use the [SFINAE rule](#terminology), to match C++. While familiar, it prevents the compiler from being able to distinguish between "this code is not meant for this case" from "this code has an error." The goal of eliminating SFINAE is to get away from the verbose and unclear errors that templates are infamous for. C++ itself, with [concepts](https://en.cppreference.com/w/cpp/language/constraints), is moving toward stating requirements up front to improve the quality of error diagnostics. ### Ad hoc API specialization Consider a type with a template type parameter: ```carbon class Vector(template T:! Type) { fn GetPointer[addr me: Self*](index: i32) -> T*; // ... } ``` To be able to type check a checked generic function using that type: ```carbon fn SetFirst[T:! Type](vec: Vector(T)*, val: T) { let p: T* = vec->GetPointer(0); *p = val; } ``` we need a guarantee that the function signature used to type check the function is correct. This won't in general be true if ad hoc specialization is allowed: ```carbon // ❌ Not legal Carbon, no ad hoc specialization class Vector(bool) { // `let p: T* = vec->GetPointer(0)` won't type check // in `SetFirst` with `T == bool`. fn GetPointer[addr me: Self*](index: i32) -> BitProxy; // ... } ``` Specialization is still important for performance, but Carbon's [existing approach to specialization of parameterized types](/docs/design/generics/details.md#specialization) makes it clear what parts of the signature can vary, and what properties all specializations will have. In this example, `Vector` would have to be declared alongside an interface, and implementations of that interface, as in: ```carbon class Vector(template T:! Type); interface VectorSpecialization { let PointerType: Deref(Self); fn GetPointer(p: Vector(Self)*, index: i32) -> PointerType; } // Blanket implementation provides default when there is // no specialization. impl forall [T:! Type] T as VectorSpecialization where .PointerType = T* { ... } // Specialization for `bool`. impl bool as VectorSpecialization where .PointerType = BitProxy { ... } class Vector(template T:! Type) { // Return type of `GetPointer` varies with `T`, but must // implement `Deref(T)`. fn GetPointer[addr me: Self*](index: i32) -> T.(VectorSpecialization.PointerType) { return T.(VectorSpecialization.GetPointer)(me, index); } // ... } ``` ### Value phase of bindings determined by initializer We considered allowing a `let` binding to result in a name with [constant value phase](#value-phases) if the initializer was a constant, even if it was not declared using the `template` keyword. That would mean that `let x: i32 = 5;` would declare `x` as constant, rather than a runtime value. For non-type values, this would be a strict improvement in usability. With the proposal, there is a choice between writing the concise form `let x: i32 = 5;` and `let template x:! i32 = 5;` that lets the compiler use the value of `x` directly. The `let template` form is both longer and uses `template` which in other contexts might merit closer scrutiny, but is generally either desirable or harmless in this context. The problem is that for type values, changing from a symbolic value to a constant results in a change to the [name lookup](#name-lookup) rules, which is not going to always be desired. This decision was the result of a discussion in open discussions on [2022-09-09](https://docs.google.com/document/d/1tEt4iM6vfcY0O0DG0uOEMIbaXcZXlNREc2ChNiEtn_w/edit#heading=h.g1xcokypbstm). ### Simpler template dependent rules that delayed more checking We considered simpler rules for which expressions were considered [template dependent](#template-dependent), like that any expression involving a template parameter was template dependent. This had the downside that it would have delayed checking of more expressions, and would have resulted in greater differences between template and checked generic semantics. Ultimately we thought that the developer experience would be better if errors were delivered earlier. This was discussed in [open discussion on 2022-10-10](https://discord.com/channels/655572317891461132/941071822756143115/1029411854277165137) and on [Discord #generics-and-templates starting 2022-10-11](https://discord.com/channels/655572317891461132/941071822756143115/1029411854277165137). ## Future work ### Expanded template constraints [Template constraints](#template-constraints) will need to support other kinds of structural constraints. In particular, the kinds of constraints that can be expressed in C++20 Concepts: - [Constraints and concepts (since C++20)](https://en.cppreference.com/w/cpp/language/constraints) - [Requires expression (since C++20)](https://en.cppreference.com/w/cpp/language/requires) This is both to allow the constraints of existing C++ code to be migrated, and because we expect constraints that were found to be useful in C++ will also be useful for Carbon. ### Predicates: constraints on values We will need some mechanism to express that the value of a non-type template parameter meets some criteria. For example, the size parameter of an array must not be less than 0. We are considering a construct called _predicates_ to represent these kinds of constraints, see the question-for-leads issue [#2153: Generics calling templates](https://github.com/carbon-language/carbon-lang/issues/2153). ### Checked generics calling templates For checked generics interoperation with existing templates, and to allow templates to be migrated to checked generics in any order, we want Carbon to support supplying a symbolic constant argument value, such as from a checked generic function, to a function taking a template parameter. One approach that already works is using a template implementation of an interface, as in this example: ```carbon fn TemplateFunction[template T:! Type](x: T) -> T; // `Wrapper` is an interface wrapper around // `TemplateFunction`. interface Wrapper { fn F[me: Self]() -> Self; } external impl forall [template T:! Type] T as Wrapper { fn F[me: Self]() -> Self { TemplateFunction(me); } } // ✅ Allowed: fn CheckedGeneric[T:! Wrapper](z: T) -> T { return z.(Wrapper.F)(); } // ⚠️ Future work, see #2153: fn CheckedGenericDirect[T:! Type](z: T) -> T { return TemplateFunction(z); } ``` More direct interoperation is being considered in question-for-leads issue [#2153: Generics calling templates](https://github.com/carbon-language/carbon-lang/issues/2153). ### Which expressions will be evaluated at compile time The section on [value phases](#value-phases) and the ["value phase of bindings determined by initializer" alternative](#value-phase-of-bindings-determined-by-initializer) still leave open some questions about how value phases interact, and what gets evaluated at compile time. For example, what happens when there is a function call in the initializer of a `let template`, as in: ```carbon let x: i32 = 5; let template Y:! i32 = F(x); ``` Is the function call evaluated at compile time in order to determine a value for `Y`, or is this an error? Does it depend on something about `F`, such as its definition being visible to the caller and being free of side effects? Is this only allowed since the value of `x` can be determined at compile time, even though it is a runtime value, or would the declaration of `x` have to change? If we allow this construction, observe that the parameter to `F` has different value phases when called at compile time compared to run time, which might affect the interpretation of the body of `F`. ================================================ FILE: proposals/p2240.md ================================================ # Variadics [Pull request](https://github.com/carbon-language/carbon-lang/pull/2240) ## Table of contents - [Abstract](#abstract) - [Problem](#problem) - [Background](#background) - [Proposal](#proposal) - [Examples](#examples) - [Comparisons](#comparisons) - [Rationale](#rationale) - [Alternatives considered](#alternatives-considered) - [Member packs](#member-packs) - [Single semantic model for pack expansions](#single-semantic-model-for-pack-expansions) - [Generalize `expand`](#generalize-expand) - [Omit `expand`](#omit-expand) - [Support expanding arrays](#support-expanding-arrays) - [Omit each-names](#omit-each-names) - [Disallow pack-type bindings](#disallow-pack-type-bindings) - [Fold expressions](#fold-expressions) - [Allow multiple pack expansions in a tuple pattern](#allow-multiple-pack-expansions-in-a-tuple-pattern) - [Allow nested pack expansions](#allow-nested-pack-expansions) - [Use postfix instead of prefix `...`](#use-postfix-instead-of-prefix-) - [Avoid context-sensitity in pack expansions](#avoid-context-sensitity-in-pack-expansions) - [Fold-like syntax](#fold-like-syntax) - [Variadic blocks](#variadic-blocks) - [Keyword syntax](#keyword-syntax) - [Require parentheses around `each`](#require-parentheses-around-each) - [Fused expansion tokens](#fused-expansion-tokens) - [No parameter merging](#no-parameter-merging) - [Exhaustive function call typechecking](#exhaustive-function-call-typechecking) ## Abstract Proposes a set of core features for declaring and implementing generic variadic functions. A "pack expansion" is a syntactic unit beginning with `...`, which is a kind of compile-time loop over sequences called "packs". Packs are initialized and referred to using "each-names", which are marked with the `each` keyword at the point of declaration and the point of use. The syntax and behavior of a pack expansion depends on its context, and in some cases by a keyword following the `...`: - In a tuple literal expression (such as a function call argument list), `...` iteratively evaluates its operand expression, and treats the values as successive elements of the tuple. - `...and` and `...or` iteratively evaluate a boolean expression, combining the values using `and` and `or`, and ending the loop early if the underlying operator short-circuits. - In a statement context, `...` iteratively executes a statement. - In a tuple literal pattern (such as a function parameter list), `...` iteratively matches the elements of the scrutinee tuple. In conjunction with pack bindings, this enables functions to take an arbitrary number of arguments. ## Problem Carbon needs a way to define functions and parameterized types that are _variadic_, meaning they can take a variable number of arguments. ## Background C has long supported variadic functions through the "varargs" mechanism, but that's heavily disfavored in C++ because it isn't type-safe. Instead, C++ provides a separate feature for defining variadic _templates_, which can be functions, classes, or even variables. However, variadic templates currently suffer from several shortcomings. Most notably: - They must be templates, which means they cannot be definition-checked, and suffer from a variety of other costs such as needing to be defined in header files, and code bloat due to template instantiation. - It is inordinately difficult to define a variadic function whose parameters have a fixed type, and the signature of such a function does not clearly communicate that fixed type to readers. - The design encourages using recursion rather than iteration to process the elements of a variadic parameter list. This results in more template instantiations, and typically has at least quadratic overhead in the size of the pack (at compile time, and sometimes at run time). In recent versions of C++ it is also possible to iterate over packs procedurally, using a [fold expressions](https://en.cppreference.com/w/cpp/language/fold) over the comma operator, but that technique is awkward to use and not widely known. There have been a number of C++ standard proposals to address some of these issues, and improve variadic templates in other ways, such as [P1219R2: Homogeneous variadic function parameters](https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2019/p1219r2.html), [P1306R1: Expansion Statements](http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2019/p1306r1.pdf), [P1858R2: Generalized Pack Declaration and Usage](http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2020/p1858r2.html), and [P2277R0: Packs Outside of Templates](http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2021/p2277r0.html). However, C++ has chosen not to pursue definition-checking even for non-variadic functions, so definition-checked variadics seem out of reach. The most recent proposal to support fixed-type parameter packs was [rejected](https://github.com/cplusplus/papers/issues/297). A proposal to support iterating over parameter packs was inactive for several years, but has very recently been [revived](https://github.com/cplusplus/papers/issues/156). Swift supports [variadic parameters](https://docs.swift.org/swift-book/documentation/the-swift-programming-language/functions/#Variadic-Parameters) so long as all elements have the same type, and has recently approved [SE-0393: Value and Type Parameter Packs](https://github.com/apple/swift-evolution/blob/main/proposals/0393-parameter-packs.md), which adds support for definition-checked, heterogeneous variadic parameters (with a disjoint syntax). [SE-0404: Pack Iteration](https://github.com/simanerush/swift-evolution/blob/se-0404-pack-iteration/proposals/0404-pack-iteration.md), which extends that to support iterating through a variadic parameter list, has been positively received, but hasn't yet been approved. There have been several attempts to add such a feature to Rust, but that work is [currently inactive](https://github.com/rust-lang/rfcs/issues/376#issuecomment-830034029). ## Proposal See `/docs/design/variadics.md` in this pull request. ### Examples ```carbon // Computes the sum of its arguments, which are i64s fn SumInts(... each param: i64) -> i64 { var sum: i64 = 0; ... sum += each param; return sum; } ``` ```carbon // Concatenates its arguments, which are all convertible to String fn StrCat[... each T:! ConvertibleToString](... each param: each T) -> String { var len: i64 = 0; ... len += each param.Length(); var result: String = ""; result.Reserve(len); ... result.Append(each param.ToString()); return result; } ``` ```carbon // Returns the minimum of its arguments, which must all have the same type T. fn Min[T:! Comparable & Value](var result: T, ... each next: T) -> T { ... if (each next < result) { result = each next; } return result; } ``` ```carbon // Invokes f, with the tuple `args` as its arguments. fn Apply[... each T:! type, F:! CallableWith(... each T)] (f: F, args: (... each T)) -> auto { return f(...expand args); } ``` ```carbon // Takes an arbitrary number of vectors with arbitrary element types, and // returns a vector of tuples where the i'th element of the vector is // a tuple of the i'th elements of the input vectors. fn Zip[... each ElementType:! type] (... each vector: Vector(each ElementType)) -> Vector((... each ElementType)) { ... var each iter: auto = each vector.Begin(); var result: Vector((... each ElementType)); while (...and each iter != each vector.End()) { result.push_back((... each iter)); ... each iter++; } return result; } ``` ```carbon // Toy example of mixing variadic and non-variadic parameters. // Takes an i64, any number of f64s, and then another i64. fn MiddleVariadic(first: i64, ... each middle: f64, last: i64); ``` ```carbon // Toy example of using the result of variadic type deduction. fn TupleConcat[... each T1: type, ... each T2: type]( t1: (... each T1), t2: (... each T2)) -> (... each T1, ... each T2) { return (...expand t1, ...expand t2); } ``` ### Comparisons The following table compares selected examples of Carbon variadics against equivalent code written in C++20 (with and without the extensions discussed [earlier](#background)) and Swift.
Carbon C++20 C++20 with extensions Swift with extensions
```carbon // Computes the sum of its arguments, which are i64s fn SumInts(... each param: i64) -> i64 { var sum: i64 = 0; ... sum += each param; return sum; } ``` ```cpp template requires (std::convertible_to && ...) int64_t SumInts(const Params&... params) { return (static_cast(params) + ... + 0); } ``` With P1219R2: ```C++ int64_t SumInts(int64... params) { return (static_cast(params) + ... + 0); } ``` (No extensions) ```swift func SumInts(_ params: Int64...) { var sum: Int64 = 0 for param in params { sum += param } return sum } ```
```carbon fn Min[T:! Comparable & Value](first: T, ... each next: T) -> T { var result: T = first; ... if (each next < result) { result = each next; } return result; } ``` ```cpp template requires std::totally_ordered && std::copyable && (std::same_as && ...) T Min(T first, Params... rest) { if constexpr (sizeof...(rest) == 0) { // Base case. return first; } else { T min_rest = Min(rest...); if (min_rest < first) { return min_rest; } else { return first; } } } ``` With P1219R2 and P1306R2 ```cpp template requires std::totally_ordered && std::copyable T Min(const T& first, const T&... rest) { T result = first; template for (const T& t: rest) { if (t < result) { result = t; } } return result; } ``` (No extensions) ```swift func Min(_ first: T, _ rest: T...) -> T { var result: T = first; for t in rest { if (t < result) { result = t } } return result } ```
```carbon fn StrCat[... each T:! ConvertibleToString](... each param: each T) -> String { var len: i64 = 0; ... len += each param.Length(); var result: String = ""; result.Reserve(len); ... result.Append(each param.ToString()); return result; } ``` ```cpp template std::string StrCat(const Ts&... params) { std::string result; result.reserve((params.Length() + ... + 0)); (result.append(params.ToString()), ...); return result; } ``` With P1306R2 ```cpp template std::string StrCat(const Ts&... params) { std::string result; result.reserve((params.Length() + ... + 0)); template for (auto param: params) { result.append(param.ToString()); } return result; } ``` With SE-0393 and SE-404 ```swift func StrCat(_ param: repeat each T) -> String { var len: Int64 = 0; for param in repeat each param { len += param.Length() } var result: String = "" result.reserveCapacity(len) for param in repeat each param { result.append(param.ToString()) } return result } ```
## Rationale Carbon needs variadics to effectively support [interoperation with and migration from C++](/docs/project/goals.md#interoperability-with-and-migration-from-existing-c-code), where variadic templates are fairly common. Variadics also make code [easier to read, understand, and write](/docs/project/goals.md#code-that-is-easy-to-read-understand-and-write), because some APIs (such as `printf`) can't be naturally expressed in terms of a fixed number of parameters. Furthermore, Carbon needs to support _generic_ variadics for the same reasons it needs to support generic non-variadic functions: for example, definition-checking makes APIs [easier to read, understand, and write](/docs/project/goals.md#code-that-is-easy-to-read-understand-and-write), and [easier to evolve](/docs/project/goals.md#software-and-language-evolution). Furthermore, the language as a whole is easier to understand and write code in if separate features like variadics and generics compose in natural ways, rather than being mutually exclusive. Variadics are also important for supporting [performance-critical software](/docs/project/goals.md#performance-critical-software), because variadic APIs can be more efficient than their non-variadic counterparts. For example, `StrCat` is fundamentally more efficient than something like a chain of `operator+` calls on `std::string`, because it does not need to materialize a series of partial results, and it can pre-allocate a buffer large enough for the final result. Variadics are also needed to support the principle that [all APIs are library APIs](/docs/project/principles/library_apis_only.md), because the library representations of types such as tuples and callables will need to be variadic. This proposal may appear to deviate from that principle in some ways, but that appearance is misleading: - The design of pack expansion expressions treats the tuple literal syntax as built-in, but this isn't a problem because literal syntaxes are explicitly excluded from the principle. - The design of pack expansion patterns treats tuple types as built-in. This is arguably consistent with the principle, if we regard a tuple pattern as a kind of tuple literal (note that they have identical syntax). This proposal also revises the text of the principle to make that more explicit. - Pack types themselves are built-in types, with no library API. However, the principle only applies to first-class types, and pack types are decidedly not first-class: they cannot be function return types, they cannot even be named, and an expression cannot evaluate to a value with a pack type unless it's within a pack expansion _and_ it has compile-time expression phase (and even that narrow exception only exists to make the formalism more convenient). ## Alternatives considered ### Member packs We could potentially support declaring each-names as class members. However, this raises some novel design issues. In particular, pack bindings currently rely exclusively on type deduction for information like the arity of the pack, but for class members, there usually isn't an initializer available to drive type deduction. In addition, it's usually if not always possible to work around the lack of member packs by using members with tuple or array types instead. Consequently, this feature is deferred to future work. ### Single semantic model for pack expansions There's a subtle discrepancy in how this proposal models expression pack expansions: at run time, all pack expansions are modeled as procedural loops that successively evaluate the expansion body for each element of the input pack, and within each iteration, expressions have scalar values. However, in the type system, expressions within a pack expansion are notionally evaluated once, producing a pack value. In effect, this treats pack expansions like SIMD code, with expressions operating on "vectors" of data in parallel, rather than iteratively executing the code on a series of scalar values. This discrepancy leads to an impedance mismatch where the two models meet. In particular, it leads to the result that expressions within a pack expansion have pack types, but do not evaluate to pack values. This contravenes one of the basic expectations of a type system, that the type of an expression equals (or is at least a supertype of) the type of its value. It's tempting to resolve the inconsistency by applying the parallel model at run time as well as in the type system. However, that isn't feasible, because the parallel model has the same limitation for variadics as it does for SIMD: it can't model branching control flow. For example, consider `(... if (expand cond) then F(expand param) else G(expand param))`: if `expand param` truly evaluated to a pack value, then evaluating this expression would require N calls to _both_ `F` and `G`, rather than N calls to _either_ `F` or `G`. Even for expressions that don't contain control flow, the same problem applies when they occur within a statement pack expansion that does. We can't even statically detect these problems, because a branch could be hidden inside a function call. And this isn't just a performance problem -- if `F` or `G` have side effects, it can also be a correctness problem. An earlier version of this proposal tried to address this problem in a more limited way by saying that expressions within a pack expansion don't have types at all, but instead have "type packs". This shift in terminology nominally avoids the problem of having expressions that don't evaluate to a value of the expression's type, but it doesn't seem to be very clarifying in practice, and it doesn't address the substance of the problem. ### Generalize `expand` The syntax "`...expand` _expression_" behaves like syntactic sugar for `... each x`, where `x` is an invented pack binding in the same scope, defined as if by "`let (... each x: auto) =` _expression_". We could generalize that by saying that `expand` is a prefix operator with the same precedence as `*` that can be used anywhere in a pack expansion, where "`expand` _expression_" is syntactic sugar for `each x` (with `x` defined as before, in the scope containing the pack expansion). This would make `expand` more useful, and also resolve the anomaly where `...expand` is the only syntax that begins with `...` but is not a pack expansion. It is also a precondition for several of the alternatives discussed below. However, those semantics could be very surprising in practice. For example: ```carbon ...if (Condition()) { var x: auto = expand F(y); } ``` In this code, `F(y)` is evaluated before the pack expansion is entered, which means that it is evaluated unconditionally, and it cannot refer to names declared inside the `if` block. We can avoid the name-resolution issue by disallowing `expand` in statement pack expansions, but the sequencing of evaluation could still be surprising, particularly with `if` expressions. ### Omit `expand` As noted above, `...expand` is fundamentally syntactic sugar, so we could omit it altogether. This would somewhat simplify the design, and avoid the anomaly of having one syntax that starts with `...` but isn't a pack expansion. However, that would make it substantially less ergonomic to do things like expand a tuple into an argument list, which we expect to be relatively common. ### Support expanding arrays Statically-sized arrays are very close to being a special case of tuple types: the only difference between an array type `[i32; 2]` (using Rust syntax) and a tuple type `(i32, i32)` is that the array type can be indexed with a run-time subscript. Consequently, it would be fairly natural to allow `expand` to operate on arrays as well as tuples, and even to allow arrays of types to be treated as tuple types (in the same way that tuples of types can be treated as tuple types). This functionality is omitted from the current proposal because we have no motivating use cases, but it could be added as an extension. Note that there are important motivating use cases under some of the alternatives considered below. ### Omit each-names Rather than having packs be distinguished by their names, we could instead distinguish them by their types. For example, under the current proposal, the signature of `Zip` is: ```carbon fn Zip[... each ElementType:! type] (... each vector: Vector(each ElementType)) -> Vector((... each ElementType)); ``` With this alternative, it could instead be written: ```carbon fn Zip[ElementTypes:! [type;]] (... vectors: Vector(expand ElementTypes)) -> Vector((... expand ElementTypes)); ``` This employs several features not in the primary proposal: - In cases where the declared type of the each-name does not vary across iterations (like `ElementType`), we can re-express it as an array binding if [`expand` supports arrays](#support-expanding-arrays), and if [`expand` is a stand-alone operator](#generalize-expand). Note that we only need this in type position of a binding pattern, where we could more easily restrict `expand` to avoid the problems discussed earlier. - In cases where the declared type of the binding does vary, that fact alone implies that the binding refers to a pack, so we can effectively infer the presence of `each` from the type, rather than make the user spell it out explicitly. This slight change in syntax belies a much larger shift in the underlying semantics: since these are ordinary bindings, a given call to `Zip` must bind each of them to a single value that represents the whole sequence of arguments (which is why their names are now plural). In the case of `ElementTypes`, that follows straightforwardly from its type: it represents the argument types as an array of `type`s. The situation with `vectors` is more subtle: we have to interpret `Vector(expand ElementTypes)` as the type of the whole sequence of argument values, rather than as a generic description of the type of a single argument. In other words, we have to interpret it as a pack type, and that means `vectors` notionally binds to a run-time pack value. Consequently, when `vectors` is used in the function body, it doesn't need an `each` prefix: we've chosen to express variadicity in terms of types, and it already has a pack type, so it can be directly used as an expansion site. This approach has a few advantages: - We don't have to introduce the potentially-confusing concept of a binding that binds to multiple values simultaneously. - It avoids the anomaly where we have pack types in the type system, but no actual values of those types. - Removing the `each` keyword makes it more natural to spell `expand` as a symbolic token (earlier versions of this proposal used `[:]`), which is more concise and doesn't need surrounding whitespace. - For fully homogeneous variadics (such as `SumInts` and `Min`) it's actually possible to write the function body as an ordinary loop with no variadics, by expressing the signature in terms of a non-pack binding with an array type. However, it also has some major disadvantages: - The implicit expansion of pack-type bindings hurts readability. For example, it's easy to overlook the fact that the loop condition `while (...and expand iters != vectors.End())` in `Zip` has two expansion sites, not just one. This problem is especially acute in cases where a non-local name has a pack type. - We have to forbid template-dependent names from having pack types (see [leads issue #1162](https://github.com/carbon-language/carbon-lang/issues/1162)), because the possibility that an expression might be an expansion site in some instantiations but not others would cause serious readability and implementability issues. - A given _use_ of such a binding really represents a single value at a time, in the same way that the iteration variable of a for-each loop does, so giving the binding a plural name and a pack type creates confusion in that context rather than alleviating it. It's also worth noting that we may eventually want to introduce operations that treat the sequence of bound values as a unit, such as to determine the length of the sequence (like `sizeof...` in C++), or even to index into it. This approach might seem more amenable to that, because it conceptually treats the sequence of values as a value in itself, which could have its own operations. However, this approach leaves no "room" in the syntax to spell those operations, because any mention of a pack-type binding implicitly refers to one of its elements. Conversely, the status quo proposal seems to leave a clear syntactic opening for those operations: you can refer to the sequence as a whole by omitting `each`, so `each vector.Size()` refers to the size of the current iteration's `vector`, whereas `vector.Size()` could refer to the size of the sequence of bound values. However, this could easily turn out to be a "wrong default": omitting `each` seems easy to do by accident, and easy to misread during code review. There are other solutions to this problem that work equally well with the status quo or this alternative. In particular, it's already possible to express these operations outside of a pack expansion by converting to a tuple, as in `(... each vector).Size()` (status quo) or `(... vectors).Size()` (this alternative). That may be sufficient to address those use cases, especially if we relax the restrictions on nesting pack expansions. Failing that, variadic-only spellings for these operations (like `sizeof...` in C++) would also work with both approaches. So this issue does not seem like an important differentiator between the two approaches. #### Disallow pack-type bindings As a variant of the above approach, it's possible to omit both each-names and pack-type bindings, and instead rely on variadic tuple-type bindings. For example, the signature of `Zip` could instead be: ```carbon fn Zip[ElementTypes:! [type;]] (... expand vectors: (... Vector(expand ElementTypes))) -> Vector((... expand ElementTypes)); ``` This signature doesn't change the callsite semantics, but within the function body `vectors` will be a tuple rather than a pack. This avoids or mitigates all of the major disadvantages of pack-type bindings, but it comes at a substantial cost: the function signature is substantially more complex and opaque. That seems likely to be a bad tradeoff -- the disadvantages of pack-type bindings mostly concern the function body, but readability of variadic function signatures seems much more important than readability of variadic function bodies, because the signatures will be read far more often, and by programmers who have less familiarity with variadics. This approach requires us to relax the ban on nested pack expansions. This does create some risk of confusion about which pack expansion a given `expand` belongs to, but probably much less than if we allowed unrestricted nesting. The leads chose not to pursue this approach in [leads issue #1162](https://github.com/carbon-language/carbon-lang/issues/1162). ### Fold expressions We could generalize the `...and` and `...or` syntax to support a wider variety of binary operators, and to permit specifying an initial value for the chain of binary operators, as with C++'s [fold expressions](https://en.cppreference.com/w/cpp/language/fold). This would be more consistent with C++, and would give users more control over associativity and over the behavior of the arity-zero case. However, fold expressions are arguably too general in some respects: folding over a non-commutative operator like `-` is more likely to be confusing than to be useful. Similarly, there are few if any plausible use cases for customizing the arity-zero behavior of `and` or `or`. Conversely, fold expressions are arguably not general enough in other respects, because they only support folding over a fixed set of operators, not over functions or compound expressions. Furthermore, in order to support folds over operator tokens that can be either binary or prefix-unary (such as `*`), we would need to choose a different syntax for tuple element lists. Otherwise, `...*each foo` would be ambiguous between `*foo[:0:], *foo[:1:],` etc. and `foo[:0:] * foo[:1:] *` etc. Note that even if Carbon supported more general C++-like fold expressions, we would still probably have to give `and` and `or` special-case treatment, because they are short-circuiting. As a point of comparison, C++ fold expressions give special-case treatment to the same two operators, along with `,`: they are the only ones where the initial value can be omitted (such as `... && args` rather than `true && ... && args`) even if the pack may be empty. Furthermore, folding over `&&` appears to have been the original motivation for adding fold expressions to C++; it's not clear if there are important motivating use cases for the other operators. Given that we are only supporting a minimal set of operators, allowing `...` to occur in ordinary binary syntax has few advantages and several drawbacks: - It might conflict with a future general fold facility. - It would invite users to try other operators, and would probably give less clear errors if they do. - It would substantially complicate parsing and the AST. - It would force users to make a meaningless choice between `x or ...` and `... or x`, and likewise for `and`. See also the discussion [below](#fold-like-syntax) of using `...,` and `...;` in place of the tuple and statement forms of `...`. This is inspired by fold expressions, but distinct from them, because `,` and `;` are not truly binary operators, and it's targeting a different problem. ### Allow multiple pack expansions in a tuple pattern As currently proposed, we allow multiple `...` expressions within a tuple literal expression, but only allow one `...` pattern within a tuple pattern. It is superficially tempting to relax this restriction, but fundamentally infeasible. Allowing multiple `...` patterns would create a potential for ambiguity about where their scrutinees begin and end. For example, given a signature like `fn F(... each xs: i32, ... each ys: i32)`, there is no way to tell where `xs` ends and `ys` begins in the argument list; every choice is equally valid. That ambiguity can be avoided if the types are different, but that would make type _non_-equality a load-bearing part of the pattern. That's a very unusual thing to need to reason about in the type system, so it's liable to be a source of surprise and confusion for programmers, and in particular it looks difficult if not impossible to usefully express with generic types, which would greatly limit the usefulness of such a feature. Function authors can straightforwardly work around this restriction by adding delimiters. For example, the current design disallows `fn F(... each xs: i32, ... each ys: i32)`, but it allows `fn F((... each xs: i32), (... each ys: i32))`, which is not only easier to support, but makes the callsite safer and more readable, since the boundary between the `xs` and `ys` arguments is explicitly marked. By contrast, if we disallowed multiple `...` expressions in a function argument list, function callers who ran into that restriction would often find it difficult or impossible to work around. Note, however, that this workaround presupposes that function signatures can have bindings below top-level, which is [currently undecided](https://github.com/carbon-language/carbon-lang/issues/1229). To take a more abstract view of this situation: when we reuse expression syntax as pattern syntax, we are effectively inverting expression evaluation, by asking the language to find the operands that would cause an expression to evaluate to a given value. That's only possible if the operations involved are invertible, meaning that they do not lose information. When a tuple literal contains multiple `...` expressions, evaluating it effectively discards structural information about for example where `xs` ends and `ys` begins. The operation of forming a tuple from multiple packs is not invertible, and consequently we cannot use it as a pattern operation. Our rule effectively says that if the function needs that structural information, it must ask the caller to provide it, rather than asking the compiler to infer it. ### Allow nested pack expansions Earlier versions of this design allowed pack expansions to contain other pack expansions. This is in some ways a natural generalization, but it added nontrivial complexity to the design. In particular, when an each-name is lexically within two or more pack expansions, we need a rule for determining which pack expansion iterates over it, in a way that is unsurprising and supports the intended use cases. However, we have few if any motivating use cases for it, which made it difficult to evaluate that aspect of the design. Consequently, this proposal does not support nested pack expansions, although it tries to avoid ruling them out as a future extension. ### Use postfix instead of prefix `...` `...` is a postfix operator in C++, which aligns with the natural-language use of "…", so it would be more consistent with both if `...`, `...and`, and `...or` were postfix operators spelled `...`, `and...`, and `or...`, and likewise if statement pack expansions were marked by a `...` at the end rather than the beginning. However, prefix syntaxes are usually easier to parse (particularly for humans), because they ensure that by the time you start parsing an utterance, you already know the context in which it is used. This is clearest in the case of statements: the reader might have to read an arbitrary amount of code in the block before realizing that the code they've been reading will be executed variadically, so that seems out of the question. The cases of `and`, `or`, and `,` are less clear-cut, but we have chosen to make them all prefix operators for consistency with statements. ### Avoid context-sensitity in pack expansions This proposal "overloads" the `...` token with multiple different meanings (including different precedences), and the meaning depends in part on the surrounding context, despite Carbon's principle of [avoiding context-sensitivity](/docs/project/principles/low_context_sensitivity.md). We could instead represent the different meanings using separate syntaxes. There are several variants of this approach, but they all have substantial drawbacks (see the following subsections). Furthermore, the problems associated with context-sensitivity appear to be fairly mild in this case: the difference between a tuple literal context and a statement context is usually quite local, and is usually so fundamental that confusion seems unlikely. #### Fold-like syntax We could use a modifier after `...` to select the expansion's meaning (as we already do with `and` and `or`). In particular, we could write `...,` to iteratively form elements of a tuple, and write `...;` to iteratively execute a statement. This avoids context-sensitivity (apart from `...,` having a dual role in expressions and patterns, like many other syntaxes), and has an underlying unity: `...,`, `...;` `...and`, and `...or` represent "folds" over the `,`, `;`, `and`, and `or` tokens, respectively. As a side benefit, this would preserve the property that a tuple literal always contains a `,` character (unlike the current proposal). However, this approach has major readability problems. Using `...;` as a prefix operator is completely at odds with the fact that `;` marks the end of a statement, not the beginning. Furthermore, it would probably be surprising to use `...;` in contexts where `;` is not needed, because the end of the statement is marked with `}`. The problems with `...,` are less severe, but still substantial. In this syntax `,` does not behave like a separator, but our eyes are trained to read it as one, and that habit is difficult to unlearn. For example, most readers have found that they can't help automatically reading `(..., each x)` as having two sub-expressions, `...` and `each x`. This effect is particularly disruptive when skimming a larger body of code, such as: ```carbon fn TupleConcat[..., each T1: type, ..., each T2: type]( t1: (..., each T1), t2: (..., each T2)) -> (..., each T1, ..., each T2) { return (..., expand t1, ..., expand t2); } ``` #### Variadic blocks We could replace the statement form of `...` with a variadic block syntax such as `...{ }`. However, this doesn't give us an alternative for the tuple form of `...`, and yet heightens the problems with it: `...{` could read as as applying the `...` operator to a struct literal. Furthermore, it gives us no way to variadically declare a variable that's visible outside the expansion (such as `each iter` in the `Zip` example). This can be worked around by declaring those variables as tuples, but this adds unnecessary complexity to the code. #### Keyword syntax We could drop `...` altogether, and use a separate keyword for each kind of pack expansion. For example, we could use `repeat` for variadic lists of tuple elements, `do_repeat` for variadic statements, and `all_of` and `any_of` in place of `...and` and `...or`. This leads to code like: ```carbon // Takes an arbitrary number of vectors with arbitrary element types, and // returns a vector of tuples where the i'th element of the vector is // a tuple of the i'th elements of the input vectors. fn Zip[repeat each ElementType:! type] (repeat each vector: Vector(each ElementType)) -> Vector((repeat each ElementType)) { do_repeat var each iter: auto = each vector.Begin(); var result: Vector((repeat each ElementType)); while (all_of each iter != each vector.End()) { result.push_back((repeat each iter)); repeat each iter++; } return result; } ``` This approach is heavily influenced by [Swift variadics](https://github.com/swiftlang/swift-evolution/blob/main/proposals/0393-parameter-packs.md), but not quite the same. It has some major advantages: the keywords are more consistent with `each` (and `expand` to some extent), substantially less visually noisy than `...`, and they may also be more self-explanatory. However, it does have some substantial drawbacks. Most notably, there is no longer any syntactic commonality between the different tokens that mark the root of an expansion. That makes it harder to visually identify expansions, and could also make variadics harder to learn, because the spelling does not act as a mnemonic cue. And while it's already not ideal that under the primary proposal a tuple literal is identified by the presence of either `,` or `...`, it seems even worse if one of those two tokens is instead a keyword. Relatedly, the keywords have less clear precedence relationships, because `all_of` and `any_of` can't as easily "borrow" their precedence from their non-variadic counterparts. For example, consider this line from `Zip`: ```carbon while (...and each iter != each vector.End()) { ``` Under this alternative, that becomes: ```carbon while (all_of each iter != each vector.End()) { ``` I find the precedence relationships in the initial `all_of expand iters !=` more opaque than in `...and expand iters !=`, to the extent that we might need to require additional parentheses: ```carbon while (all_of (expand iters != each vectors.End())) { ``` That avoids outright ambiguity, but obliging readers to maintain a mental stack of parentheses in order to parse the expression creates its own readability problems. It's appealing that the `repeat` keyword combines with `each` to produce code that's almost readable as English, but it creates a temptation to read `expand` the same way, which will usually be misleading. For example, `repeat expand foo` sounds like it is repeatedly expanding `foo`, but in fact it expands it only once. It's possible that a different spelling of `expand` could avoid that problem, but I haven't been able to find one that does so while also avoiding the potential for confusion with `each`. This is somewhat mitigated by the fact that `expand` expressions are likely to be rare. It's somewhat awkward, and potentially even confusing, to use an imperative word like `repeat` in a pattern context. By design, the pattern language is descriptive rather than imperative: it describes the values that match rather than giving instructions for how to match them. As a result, in a pattern like `(repeat each param: i64)`, it's not clear what action is being repeated. Finally, it bears mentioning that the keywords occupy lexical space that could otherwise be used for identifiers. Notably, `all_of`, `any_of`, and `repeat` are all names of functions in the C++ standard library. This is not a fundamental problem, because we expect Carbon to have some way of "quoting" a keyword for use as an identifier (such as Rust's [raw identifiers](https://doc.rust-lang.org/rust-by-example/compatibility/raw_identifiers.html)), but it is likely to be a source of friction. ### Require parentheses around `each` We could give `each` a lower precedence, so that expressions such as `each vector.End()` would need to be written as `(each vector).End()`. This could make the code clearer for readers, especially if they are new to Carbon variadics. However, this would make the code visually busier, and might give the misleading impression that `each` can be applied to anything other than an identifier. I propose that we wait and see whether the unparenthesized syntax has readability problems in practice, before attempting to solve those problems. We have discussed a more general solution to this kind of problem, where a prefix operator could be embedded in a `->` token, in order to apply the prefix operator to the left-hand operand without needing parentheses. However, this approach is much more appealing when the prefix operator is a symbolic token: `x-?>y` may be a plausible alternative to `(?x).y`, but `x-each>y` seems much harder to visually parse. Furthermore, this approach is hard to reconcile with treating `each` as fundamentally part of the name, rather than an operator applied to the name. ### Fused expansion tokens Instead of treating `...and` and `...or` as two tokens with whitespace discouraged between them, we could treat them as single tokens. This might more accurately reflect the fact that they are semantically different operations than `...`, and reduce the potential for readability problems in code that doesn't follow our recommended whitespace conventions. However, that could lead to a worse user experience if users accidentally insert a space after the `...`. ### No parameter merging Under the current proposal, the compiler attempts to merge function parameters in order to support use cases like this one, where merging the parameters of `Min` enables us to pair each argument with a single logical parameter that will match it: ```carbon fn Min[T:! type](first: T, ... each next: T) -> T; fn F(... each arg: i32) { Min(... each arg, 0 as i32); } ``` However, this approach makes typechecking hard to understand (and predict), because the complex conditions governing merging mean that subtle differences in the code can cause dramatic differences in the semantics. For example: ```carbon fn F[A:! I, ... each B:! I](a: A, ... each b: each B); fn G[A:! I, ... each B:! I](a: A, ... each b: each B) -> A; ``` These two function signatures are identical other than their return types, but they actually have different requirements on their arguments: `G` requires the first argument to be singular, whereas `F` only requires _some_ argument to be singular. It seems likely to be hard to teach programmers that the function's return type sometimes affects whether a given argument list is valid. Relatedly, it's hard to see how a diagnostic could concisely explain why a given call to `G` is invalid, in a way that doesn't seem to also apply to `F`. We could solve that problem by omitting parameter merging, and interpreting all of the above signatures as requiring that the first argument must be singular, because the first parameter is singular. Thus, there would be a clear and predictable connection between the parameter list and the requirements on the argument list. In order to support use cases like `Min` where the author doesn't intend to impose such a requirement, we would need to provide some syntax for declaring `Min` so that it has a single parameter, but can't be called with no arguments. More generally, this syntax would probably need to support setting an arbitrary minimum number of arguments, not just 1. For example, an earlier version of this proposal used `each(>=N)` to require that a parameter match at least N arguments, so `Min` could be written like this: ```carbon fn Min[T:! type](... each(>=1) param: T) -> T; ``` However, this alternative has several drawbacks: - We haven't been able to find a satisfactory arity-constraint syntax. In addition to its aesthetic problems, `each(>=1) param` disrupts the mental model where `each` is part of the name, and it's conceptually awkward because the constraint actually applies to the pack expansion as a whole, not to the each-name in particular. However, it's even harder to find an arity-constraint syntax that could attach to `...` without creating ambiguity. Furthermore, any arity-constraint syntax would be an additional syntax that users need to learn, and an additional choice they need to make when writing a function signature. - Ideally, generic code should typecheck if every possible monomorphization of it would typecheck. This alternative does not live up to that principle -- see, for example, the above example of `Min`. The current design does not fully achieve that aspiration either, but it's far more difficult to find plausible examples where it fails. - The first/rest style will probably be more natural to programmers coming from C++, and if they define APIs in that style, there isn't any plausible way for them to find out that they're imposing an unwanted constraint on callers, until someone actually tries to make a call with the wrong shape. ### Exhaustive function call typechecking The current proposal uses merging and splitting to try to align the argument and parameter lists so that each argument has exactly one parameter than can match it. We also plan to extend this design to also try the opposite approach, aligning them so that each parameter has exactly one argument that it can match. However, it isn't always possible to align arguments and parameters in that way. For example: ```carbon fn F[... each T:! type](x: i32, ... each y: each T); fn G(... each z: i32) { F(... each z, 0 as i16); } ``` Every possible monomorphization of this code would typecheck, but we can't merge the parameters because they have different types, and we can't merge the arguments for the same reason. We also can't split the variadic parameter or the variadic argument, because either of them could be empty. The fundamental problem is that, although every possible monomorphization typechecks, some monomorphizations are structurally different from others. For example, if `each z` is empty, the monomorphized code converts `0 as i16` to `i32`, but otherwise `0 as i16` is passed into `F` unmodified. We could support such use cases by determining which parameters can potentially match which arguments, and then typechecking each pair. For example, we could typecheck the above code by cases: - If `each z` is empty, `x: i32` matches `0 as i16` (which typechecks because `i16` is convertible to `i32`), and `each y: each T` matches nothing. - If `each z` is not empty, `x: i32` matches its first element (which typechecks because `i32` is convertible to `i32`), and `each y: each T` matches the remaining elements of `each z`, followed by `0 as i16` (which typechecks by binding `each T` to `⟬«i32; ‖each z‖-1», i16⟭`). More generally, this approach works by identifying all of the structurally different ways that arguments could match parameters, typechecking them all in parallel, and then combining the results with logical "and". However, the number of such cases (and hence the cost of typechecking) grows quadratically, because the number of cases grows with the number of parameters, and the case analysis has to be repeated for each variadic argument. [Fast development cycles](/docs/project/goals.md#fast-and-scalable-development) are a priority for Carbon, so if at all possible we want to avoid situations where compilation costs grow faster than linearly with the amount of code. Furthermore, typechecking a function call doesn't merely need to output a boolean decision about whether the code typechecks. In order to typecheck the code that uses the call, and support subsequent phases of compilation, it needs to also output the type of the call expression, and that can depend on the values of deduced parameters of the function. These more complex outputs make it much harder to combine the results of typechecking the separate cases. To do this in a general way, we would need to incorporate some form of case branching directly into the type system. For example: ```carbon fn P[T:! I, ... each U:! J](t: T, ... each u: each U) -> T; fn Q[X:! I&J, ... each Y:! I&J](x: X, ... each y: each Y) -> auto { return P(... each y, x); } fn R[A:! I&J ... each B:! I&J](a: A, ... each b: each B) { Q(... each b, a); } ``` The typechecker would need to represent the type of `P(... each x, y)` as something like `(... each Y, X).0`. That subscript `.0` acts as a disguised form of case branching, because now any subsequent code that depends on `P(... each y, x)` needs to be typechecked separately for the cases where `... each Y` is and is not empty. In this case, that even leaks back into the caller `R` through `Q`'s return type, which compounds the complexity: the type of `Q(... each b, a)` would need to be something like `((... each B, A).(1..‖each B‖), (... each B, A).0).0` (where `.(M..N)` is a hypothetical tuple slice notation). All of this may be feasible, but the cost in type system complexity and performance would be daunting, and the benefits are at best unclear, because we have not yet found plausible motivating use cases that benefit from this kind of typechecking. ================================================ FILE: proposals/p2274.md ================================================ # Subscript syntax and semantics [Pull request](https://github.com/carbon-language/carbon-lang/pull/2274) ## Table of contents - [Abstract](#abstract) - [Problem](#problem) - [Background](#background) - [Proposal](#proposal) - [Details](#details) - [Examples](#examples) - [Rationale based on Carbon's goals](#rationale-based-on-carbons-goals) - [Alternatives considered](#alternatives-considered) - [Different subscripting syntaxes](#different-subscripting-syntaxes) - [Multiple indices](#multiple-indices) - [Read-only subscripting](#read-only-subscripting) - [Rvalue-only subscripting](#rvalue-only-subscripting) - [Map-like subscripting](#map-like-subscripting) - [Usage-based rewriting](#usage-based-rewriting) ## Abstract Adds support for subscripting using the conventional square-bracket syntax, with support for both array-like and slice-like semantics. Cloned from [#1356](https://github.com/carbon-language/carbon-lang/pull/1356), which was cloned from [#1059](https://github.com/carbon-language/carbon-lang/pull/1059). ## Problem Carbon needs a convenient way of indexing into objects like arrays and slices. ## Background For purposes of this proposal, a "slice" is an object that refers to some region of an array, and provides access to it, such as a `std::string_view` or `std::span` in C++. Slices have subtly different indexing API semantics than arrays: indexing into an array can produce an l-value only if the array itself is an l-value, but indexing into a slice can produce an l-value regardless of the slice's value category. Interfaces are Carbon's [only static open extension mechanism](/docs/project/principles/static_open_extension.md), so user-defined indexing must be defined in terms of interfaces. In order to support [definition checking of generic functions](/docs/design/generics/goals.md#checked-generics-will-be-checked-when-defined), it must be possible to fully typecheck indexing operations using only the information exposed by those interfaces. And in order to provide [coherence](/docs/design/generics/goals.md#coherence), a given indexing expression must execute the same code regardless of how much type information we have about it, so long as we have enough type information to establish that the expression is valid. The primary problem this proposal needs to solve is how to support both array-like and slice-like indexing semantics while satisfying those requirements. ## Proposal Carbon will support indexing using the conventional `a[i]` subscript syntax. When `a` is an l-value, the result of subscripting will always be an l-value, but when `a` is an r-value, the result can be an l-value or an r-value, depending on which interface the type implements: - If subscripting an r-value produces an r-value result, as with an array, the type should implement `IndexWith`. - If subscripting an r-value produces an l-value result, as with a slice, the type should implement `IndirectIndexWith`. `IndirectIndexWith` is a subtype of `IndexWith`, and subscript expressions are rewritten to method calls on `IndirectIndexWith` if the type is known to implement that interface, or to method calls on `IndexWith` otherwise. A type can implement at most one of these two interfaces. `IndirectIndexWith` provides a final blanket `impl` of `IndexWith`, which ensures coherence. One implication of this design is that the requirement of coherence must apply to value categories as well as types (and for those purposes, "l-value" is a subcategory of "r-value"). This implies that for any valid expression containing an r-value sub-expression, replacing that sub-expression with an l-value expression cannot change the validity or semantics of the outer expression, so long as the r-value expression has the same type and evaluates to the same value. This, in turn, ensures that once we know a value implements `IndexWith`, additionally learning that it implements `IndirectIndexWith` can't invalidate code that previously typechecked, or change its semantics. ## Details A subscript expression has the form "_lhs_ `[` _index_ `]`". As in C++, this syntax has the same precedence as `.`, `->`, and function calls, and associates left-to-right with all of them. Its semantics are defined in terms of the following interfaces: ``` interface IndexWith(SubscriptType:! Type) { let ElementType:! Type; fn At[me: Self](subscript: SubscriptType) -> ElementType; fn Addr[addr me: Self*](subscript: SubscriptType) -> ElementType*; } interface IndirectIndexWith(SubscriptType:! Type) { impl as IndexWith(SubscriptType); fn Addr[me: Self](subscript: SubscriptType) -> ElementType*; } ``` A subscript expression where _lhs_ has type `T` and _index_ has type `I` is rewritten based on the value category of _lhs_ and whether `T` is known to implement `IndirectIndexWith(I)`: - If `T` implements `IndirectIndexWith(I)`, the expression is rewritten to "`*((` _lhs_ `).(IndirectIndexWith(I).Addr)(` _index_ `))`". - Otherwise, if _lhs_ is an l-value, the expression is rewritten to "`*((` _lhs_ `).(IndexWith(I).Addr)(` _index_ `))`". - Otherwise, the expression is rewritten to "`(` _lhs_ `).(IndexWith(I).At)(` _index_ `)`". On their own, these rules would oblige `IndirectIndexWith` types to define three methods to support a single syntax. Worse, they would permit those types to define those methods in inconsistent ways, which would violate coherence. To avoid those problems, `IndirectIndexWith` provides a blanket `final impl` for `IndexWith`: ``` final external impl forall [SubscriptType:! Type, T:! IndirectIndexWith(SubscriptType)] T as IndexWith(SubscriptType) { let ElementType:! Type = T.(IndirectIndexWith(SubscriptType)).ElementType; fn At[me: Self](subscript: SubscriptType) -> ElementType { return *(me.(IndirectIndexWith(SubscriptType).Addr)(index)); } fn Addr[addr me: Self*](subscript: SubscriptType) -> ElementType* { return me->(IndirectIndexWith(SubscriptType).Addr)(index); } } ``` Thus, a type that implements `IndirectIndexWith` need not, and cannot, provide its own definitions of `IndexWith.At` and `IndexWith.Addr`. ### Examples An array type could implement subscripting like so: ``` class Array(template T:! Type) { external impl as IndexWith(like i64) { let ElementType:! Type = T; fn At[me: Self](subscript: i64) -> T; fn Addr[addr me: Self*](subscript: i64) -> T*; } } ``` And a slice type could look like this: ``` class Slice(T:! Type) { external impl as IndirectIndexWith(like i64) { let ElementType:! Type = T; fn Addr[me: Self](subscript: i64) -> T*; } } ``` We can even approximate support for indexing on tuple types. We don't have enough of a design for metaprogramming or variadics to specify it generically, but we can illustrate how it would look for a particular tuple type, such as `(i8, f64)`: ``` external impl (i8, f64) as IndexWith(typeof(0)) { let ElementType:! Type = i8; fn At[me: Self](0) -> i8; fn Addr[addr me: Self*](0) -> i8*; } external impl (i8, f64) as IndexWith(typeof(1)) { let ElementType:! Type = f64; fn At[me: Self](1) -> f64; fn Addr[addr me: Self*](1) -> f64*; } ``` However, this approach presupposes that every integer literal has a distinct type, which is not yet settled. It also only supports indexing with integer literals, not with constant values of ordinary integer types. This is somewhat contrary to Carbon's metaprogramming philosophy: Carbon treats types as values in order to make metaprogramming more like ordinary programming, but here we're doing the reverse, treating values as types. Consequently, the eventual design for tuple-style indexing may look very different. ## Rationale based on Carbon's goals Indexing is a fundamental operation in procedural programming, and is especially common in performance-critical code, so supporting it with a concise, familiar syntax helps us make [code easy to read, understand, and write](/docs/project/goals.md#code-that-is-easy-to-read-understand-and-write), and support [performance-critical software](/docs/project/goals.md#performance-critical-software). Using the same syntax as C++, even for slice-like types, supports [interoperability with and migration from existing C++ code](/docs/project/goals.md#interoperability-with-and-migration-from-existing-c-code). ## Alternatives considered ### Different subscripting syntaxes The reason that slices have different semantics from arrays is that a slice represents an indirection, like a pointer. Indexing behaves the same for slice l-values and r-values for the same reason that dereferencing behaves the same for pointer l-values and r-values. Thus, our attempts to make a single subscripting syntax work for both arrays and slices are arguably analogous to trying to make the same method-call syntax work for both pointers and objects. Or, to put it another way, the difficulty with slices is that they behave like _references_. From that point of view, it would be more consistent to have different subscripting syntaxes for the two cases. The most naive version of that would be to say that only array-like types support subscripting, and slice-like types should instead support dereferencing, so indexing into a slice would look like `(*s)[i]`. However, that would be syntactically onerous, and it's not clear how to define the type of `*s` in a way that doesn't beg the question. Instead, we could define a distinct syntax for slice subscripting, such as `s*[i]`, and define separate interfaces for the two subscript syntax. The two interfaces would be very similar to `IndexWith` and `IndirectIndexWith`, but neither would extend the other, and the rewrite from subscript syntax to method calls would not depend on type information (although it would depend on value category). However, any such syntax would be quite novel and unfamiliar, and there seem to be few good choices for how to spell it. `s*[i]` is almost the only syntax that seems both viable and somewhat mnemonic, but it would add further complexity to the already-fraught parsing of `*` (for humans as well as tools). ### Multiple indices This proposal does not support multiple comma-separated indices (such as `a[i, j]`), which is desirable for types like multidimensional arrays. We do support syntax like `a[(i, j)]`, which is a single index whose type is a tuple, but the extra parens are syntactically noisy. We could add those parens implicitly, but that would effectively move the syntactic noise to the implementation, even for the single-index case (for example `impl Foo as IndexWith((i64,))`). It should be much cleaner to make the interfaces and their methods variadic, once we have a design for variadics. ### Read-only subscripting This proposal does not provide an obvious path for supporting types like C++'s `std::string_view` or `std::span`, whose subscripting operations expose read-only access to their contents. It is tempting to try to extend this proposal to support those use cases, both because of their inherent importance and because it already has to deal with read-only versus read-write access in order to support array r-values. However, there's a fundamental difference between the true immutability of something like an array r-value, and the contextual lack of mutable _access_ provided by something like `string_view`. While value categories can express the former, they are not well-suited to expressing the latter. To address these use cases, Carbon will probably need something like C++'s `const` type system, but that should be largely orthogonal to this proposal. ### Rvalue-only subscripting This proposal does not support subscripting operations that can't produce l-values. In particular, this means it does not support using subscript syntax to form slices, as in Python's `a[i:j]` or Swift's `a[i...j]`. To support this, we would need a separate pair of interfaces that return by value: ``` interface RValueIndexWith(SubscriptType:! Type) { let ElementType:! Type; fn Addr[addr me: Self*](subscript: SubscriptType) -> ElementType; } interface RValueIndirectIndexWith(SubscriptType:! Type) { extends RValueIndexWith(SubscriptType); let ElementType:! Type; fn Addr[me: Self](subscript: SubscriptType) -> ElementType; } final external impl [SubscriptType:! Type, T:! RValueIndirectIndexWith(SubscriptType)] T as IndexWith(SubscriptType) { let ElementType:! Type = T.(RValueIndirectIndexWith(SubscriptType)).ElementType; fn Addr[addr me: Self*](subscript: SubscriptType) -> ElementType { return me->Addr(subscript); } } ``` Note that we still need a pair of interfaces, and a blanket final `impl` to enforce consistency, because arrays and slices have different semantics in this context as well: taking a slice of an r-value array is invalid, because taking a slice is equivalent to (and presumably implemented in terms of) taking an address. On the other hand, taking a slice of an r-value slice is valid and should be supported. We would likewise need to extend the rewrite rules for subscript syntax to detect and use implementations of these interfaces. This should not lead to a combinatorial explosion of cases, though; if `T` implements both `IndexWith(I)` and `RValueIndexWith(I)`, the program should be ill-formed due to ambiguity. However, it's not clear if this would provide enough benefit to justify the added complexity. ### Map-like subscripting This proposal does not support subscripting operations that insert new elements into a collection, as in C++'s `std::map` and `std::unordered_map`, because it requires subscriptable types to support subscripting of r-values, which are immutable. We could support this with an additional interface for types that are subscriptable only as l-values, and a corresponding extension to the rewrite rules. However, it's debatable whether such insertion behavior is desirable; it has not been a clear-cut success in C++. Code like `x = m[i];` looks like it reads from `m`, and the fact that it can write to `m` is surprising, and easy for even experienced programmers to overlook. Even for wary readers, it doesn't convey the author's intent, because it's not clear whether the author assumed `i` is present, or is relying on the implicit insertion. Furthermore, the implicit insertion means that `x = m[i];` won't compile when `m` is const. On the other hand, while it's relatively unsurprising that `m[i] = x;` might insert, that insertion is also potentially inefficient, since it must default-construct a new value before assigning `x` to it. We could fix most of these problems by giving special treatment to syntax of the form `m[i] = x;`, and defining separate methods for it: ``` interface IndexWith(SubscriptType:! Type) { let ElementType:! Type; fn At[me: Self](subscript: SubscriptType) -> ElementType; fn Addr[addr me: Self*](subscript: SubscriptType) -> ElementType*; fn Assign[addr me: Self*](subscript: SubscriptType, element: ElementType) { (*me->Addr(index)) = element; } } interface IndirectIndexWith(SubscriptType:! Type) { extends IndexWith(SubscriptType); let ElementType:! Type; fn Addr[me: Self](subscript: SubscriptType) -> ElementType*; fn Assign[me: Self](subscript: SubscriptType, element: ElementType) { me->Addr(subscript) = element; } } final external impl [SubscriptType:! Type, T:! IndirectIndexWith(SubscriptType)] T as IndexWith(SubscriptType) { ... fn Assign[addr me: Self*](subscript: SubscriptType, element: ElementType) { me->(IndirectIndexWith.AssignAsRValue)(subscript, element); } } ``` With this approach, expressions of the form `m[i] = x` would be rewritten to call the appropriate `Assign` method, while all other subscript expressions are rewritten as in the primary proposal. However, this does add some complexity to both the interfaces and the rewrite rules (for example, we presumably want `(m[i]) = x` to be treated the same as `m[i] = x`), so I'm leaving this as a potential future extension. #### Usage-based rewriting As an alternative way of getting `std::map`-like behavior, we could rewrite subscript expressions to use `At` instead of `Addr` when the usage context only needs an r-value, even if the operand is an l-value, in much the same way that Rust rewrites subscript expressions depending on whether the usage expects the result to be mutable. This would enable type authors to make `m[i] = x;` potentially-inserting while ensuring that `x = m[i];` is not, by making `Addr` but not `At` potentially-inserting. However, this would mean that `m[i].Method()` is potentially inserting if `Method` takes `me` by pointer, and would have the same performance drawback as in C++. Furthermore, it would violate the principle (which we have discussed but not yet adopted) that type information should flow from subexpressions to the parent expression, but never the reverse. A variant of this approach would be to allow the compiler to choose between `At` and `Addr` when the operand is an l-value but the usage context only needs an r-value. This might enable the compiler to make the generated code more efficient and/or safer, and might create pressure for libraries to ensure `At` and `Addr` are consistent with each other. However, if the compiler predictably chooses `At` under those conditions, that could create a temptation for library authors to leverage that implementation detail to try to implement a `std::map`-like API, by having `Addr` implicitly insert while `At` does not. That would have all the same drawbacks as if we supported it intentionally, plus the evolutionary drawbacks of having user code depend on unsupported implementation details. ================================================ FILE: proposals/p2287.md ================================================ # Allow unqualified name lookup [Pull request](https://github.com/carbon-language/carbon-lang/pull/####) ## Table of contents - [Abstract](#abstract) - [Problem](#problem) - [Background](#background) - [Proposal](#proposal) - [Classes](#classes) - [Interfaces](#interfaces) - [Namespaces](#namespaces) - [Open question](#open-question) - [Implicit instance binding to `me`](#implicit-instance-binding-to-me) - [Out-of-line definitions for impls](#out-of-line-definitions-for-impls) - [Rationale](#rationale) - [Alternatives considered](#alternatives-considered) - [No unqualified lookup when defining outside a scope](#no-unqualified-lookup-when-defining-outside-a-scope) ## Abstract Allow unqualified name lookup in multiple situations: - For classes and interfaces, whether inside the class scope or within an out-of-line function definition. - For namespaces, when the namespace is used in a declaration. ## Problem [Member access](/docs/design/expressions/member_access.md) defines certain member access behaviors. However, it doesn't cover what happens if an unqualified name lookup occurs within a class, particularly for an out-of-line member function definition, or other situations. ## Background The [member access design](/docs/design/expressions/member_access.md) and [information accumulation principle](/docs/project/principles/information_accumulation.md) affect this. This will also work similarly to [unqualified lookup within C++](https://en.cppreference.com/w/cpp/language/unqualified_lookup). ## Proposal Allow unqualified name lookup which will use the appropriate scope. Implicit instance binding to `me` is not proposed; it is left as an [open question](#implicit-instance-binding-to-me). ### Classes This proposal updates [the class design](/docs/design/classes.md) to address classes. ### Interfaces ```carbon interface Vector { fn Scale[me: Self](v: f64) -> Self; // Default definition of `Invert` calls `Scale`. default fn Invert[me: Self]() -> Self; } // `Self` is valid here because it's doing unqualified name lookup into // `Vector`. default fn Vector.Invert[me: Self]() -> Self { // `Scale` is valid here because it does unqualified name lookup into // `Vector`, then an instance binding with `me`. return me.(Scale)(-1.0); } ``` ### Namespaces More generally, this should also be true of other scopes used in declarations. In particular, namespaces should also follow the same rule. However, since [name lookup](/docs/design/name_lookup.md) has not been fleshed out, this won't make much of an update to it. An example for namespaces would be: ```carbon namespace Foo; var Foo.a: i32 = 0; class Foo.B {} // `B` and `a` are valid here because unqualified name lookup occurs within // `Foo`. fn Foo.C(B b) -> i32 { return a; } ``` ## Open question ### Implicit instance binding to `me` In C++, unqualified name lookup can implicitly do instance binding to `this`. In other words, `this->Member()` and `Member()` behave similarly inside a method definition. In Carbon, the current design hasn't fleshed out whether `me` would behave similarly. Most design documentation assumes it will not, but it hasn't been directly considered in a proposal, and [implicit scoped function parameters](https://github.com/carbon-language/carbon-lang/issues/1974) might offer a way to make it work in a language-consistent manner. This proposal takes no stance on unqualified name lookup resolving `me`: it is not intended to change behavior from previous proposals. ### Out-of-line definitions for impls Issue [#2377](https://github.com/carbon-language/carbon-lang/issues/2377) asks how unqualified lookup should work for `impl`. The [generics design](/docs/design/generics/details.md) also doesn't appear to give syntax for out-of-line definitions of other impls. ## Rationale - [Code that is easy to read, understand, and write](/docs/project/goals.md#code-that-is-easy-to-read-understand-and-write) - Performing unqualified name lookup for class members should be fairly unsurprising to readers, and should allow for more concise code when working within a namespace. - [Interoperability with and migration from existing C++ code](/docs/project/goals.md#interoperability-with-and-migration-from-existing-c-code) - This behavior will be similar to how C++ works. ## Alternatives considered ### No unqualified lookup when defining outside a scope We could decide not to support unqualified lookup when defining something that is presented within the top-level scope of the file. Note this has subtle implications. If `Foo.C` in the namespace example is considered to be outside the `Foo` scope for this purpose, it means the function would need to look like: ``` fn Foo.C(Foo.B b) -> i32 { return Foo.a; } ``` It could also mean that, on a class, an inline declaration `fn Foo() -> ClassMember` is valid, while an out-of-line definition `fn Class.Foo() -> ClassMember` is not, requiring `Class.ClassMember`. Advantages: - Explicit in access. - For example, name lookup results could be mildly confusing if both `package.a` and `package.Foo.a` are defined but `package.Foo.a` is hidden in code while `package.a` is easy to find. It's likely that `package.Foo.a` would be considered unambiguous for unqualified name lookup. Disadvantages: - Very verbose, and could prove un-ergonomic for developers. ================================================ FILE: proposals/p2295.md ================================================ # Begin publishing CoC and moderation transparency reports [Pull request](https://github.com/carbon-language/carbon-lang/pull/2295) ## Table of contents - [Abstract](#abstract) - [Problem](#problem) - [Background](#background) - [Proposal](#proposal) - [Details](#details) - [Rationale](#rationale) - [Alternatives considered](#alternatives-considered) - [Automated dashboards](#automated-dashboards) - [Different cadence](#different-cadence) - [Different publishing options](#different-publishing-options) ## Abstract Proposes a documented cadence, publishing target, and template for Code of Conduct and moderation transparency reports. ## Problem The Carbon community wants to be an example in terms of diversity, equity and inclusion. One of the best practices that supports such ambition is publishing transparency reports about misconduct within the community and actions taken in response. There is no previous experience with reporting back to the community about this so far. ## Background Transparency reports are not very common practice, but already around. So there are some ideas to grab from other communities, with their permission. ## Proposal We will benchmark relevant transparency reports to find inspiration to create our own. We will look at their scope, their format, their regularity and possibly community feedback we have access to. We will create a template for Carbon's transparency reports and maintain that as part of the project. Our Code of Conduct team will then create and publish regular reports in the GitHub discussion forum based on that template. ## Details Some examples of transparency reports in which conduct incidents had been reported: Conferences: - PyCon US 2022: https://pycon.blogspot.com/2022/06/pycon-us-2022-transparency-report.html - CppCon 2021: https://cppcon.org/cppcon-2021-transparency-report/ - PyConDE 2019: https://2019.pycon.de/blog/code-of-conduct-transparency-report/ Other: - LLVM CoC July 15, 2022: https://llvm.org/coc-reports/2022-07-15-report.html - Linux Foundation 2021: https://www.linuxfoundation.org/blog/blog/linux-foundation-events-code-of-conduct-transparency-report-2021-event-summary We propose a [template](/docs/project/transparency_reports.md#template) for our own CoC transparency report, which has been greatly inspired by transparency reports issued by the Python community worldwide. Thank you, Pythonistas! We also document there the [cadence and target of publishing](/docs/project/transparency_reports.md#cadence-and-publishing). ## Rationale Transparent moderation and handling of conduct issues is essential to sustaining a healthy [community and culture](/docs/project/goals.md#community-and-culture), one of Carbon's primary goals. We need to clearly and transparently surface the conduct of our community and how we respond to it. ## Alternatives considered ### Automated dashboards An automated dashboard (for example “X days since AutoMod detected a “guys” in our Discord”, “Y incidents of harmful language”) would be great, but we are still figuring out the basics and principles together before we can automate more. Such a dashboard could be an attractive alternative for the future. ### Different cadence Publishing quarterly may result in some quarters with very little or even nothing to publish. That seems fine. We considered a slower cadence of annually, but we worry that would be too slow for folks in the community to hold the conduct team and leadership accountable as things start to be forgotten. We also thought about how to respond rapidly when needed but would prefer to publish out-of-band when needed rather than run an even more rapid cadence. ### Different publishing options We considered different places to publish the individual transparency reports. They don't seem to belong as part of our version controlled repository as they aren't something that should be persistent -- they reflect a report at a moment in time. And while we hope to never need it, we should retain the ability to easily redact information or make permanent edits to them if necessary. GitHub discussions seem like a good fit overall compared to alternatives like the main repository, the wiki, etc. Using GitHub discussions also allows comments and discussion where folks could for example ask questions or get more information about how and why we are taking our moderation approaches. While there is some risk of these discussions being unproductive, we hope to be able to moderate them as well and still provide a place where productive and on-topic discussion can still take place. ================================================ FILE: proposals/p2347.md ================================================ # What can be done with an incomplete interface [Pull request](https://github.com/carbon-language/carbon-lang/pull/2347) ## Table of contents - [Abstract](#abstract) - [Problem](#problem) - [Background](#background) - [Proposal](#proposal) - [Rationale](#rationale) - [Alternatives considered](#alternatives-considered) - [Allow declaration and definition in different files](#allow-declaration-and-definition-in-different-files) - [No specific restriction on `where` clauses on incomplete constraints](#no-specific-restriction-on-where-clauses-on-incomplete-constraints) - [Don't allow combinations of incomplete constraints as in `C & D`](#dont-allow-combinations-of-incomplete-constraints-as-in-c--d) ## Abstract Clarify what can and cannot be done with a incomplete interface or named constraint. ## Problem For purposes of this proposal, the term _constraints_ refers to interfaces and named constraints. Interfaces do not require any different treatment from named constraints, and treating them the same is good for simplicity and uniformity. We want to support forward declaring constraints so that they may be used before they are allowed to be defined, due to recursion or a reference cycle. Some uses require the definition of the constraint to be visible to be checked, however. In some cases, those checks may be delayed at the cost of complexity in the implementation of the compiler. This proposal aims to specify what uses are allowed, particularly those cases that were not covered by [the previous proposal on forward declarations](https://github.com/carbon-language/carbon-lang/pull/1084). ## Background Proposal [#1084: Generics details 9: forward declarations](https://github.com/carbon-language/carbon-lang/pull/1084) originally laid out the rules for incomplete constraints. Since then, the [discussion on 2022-10-12](https://docs.google.com/document/d/1tEt4iM6vfcY0O0DG0uOEMIbaXcZXlNREc2ChNiEtn_w/edit#heading=h.q7afaawbc5k) considered how we needed to update and fill in those rules, as part of an effort to implement those rules in the Carbon Explorer. ## Proposal The new rule details have been added by this proposal PR to the [Forward declarations and cyclic references: declaring interfaces and named constraints](/docs/design/generics/details.md#declaring-interfaces-and-named-constraints) section of [Generics: details design document](/docs/design/generics/details.md). ## Rationale This proposal is balancing the expressivity needed to make code easier to write with implementation simplicity, both concerns of Carbon's ["code that is easy to read, understand, and write"](/docs/project/goals.md#code-that-is-easy-to-read-understand-and-write) goal. ## Alternatives considered ### Allow declaration and definition in different files In particular, we considered allowing the declaration of an interface or named constraint in an API file and the definition in the impl file of the same library. We considered some use cases that might benefit from this in [#931](p0931.md#private-interfaces-in-public-api-files) and [#971](https://github.com/carbon-language/carbon-lang/issues/971). In [#generics-and-templates on 2022-10-24](https://discord.com/channels/655572317891461132/941071822756143115/1034207895392358431), we decided those use cases would be okay including the definition of the private interface in the API file. So to support checking for invalid uses of an incomplete interfaces with information local to that file, we reaffirmed the decision in #971 and implemented in proposal [#1084](https://github.com/carbon-language/carbon-lang/pull/1084) to require the definition to be in the same file as the declaration. ### No specific restriction on `where` clauses on incomplete constraints We considered not having a specific rule preventing any `where` clause on an incomplete constraint. The idea is that the problematic cases, such as those that access members of the constraint like `where .X = T`, are already forbidden. By removing this rule, we would allow constructions like `C where Vector(.Self) is Hashable` for incomplete `C`. We decided to wait until we had a motivating use case to allow this, and start with the more restrictive rule. ### Don't allow combinations of incomplete constraints as in `C & D` We considered forbidding the combination of incomplete constraints with the `&` operator, because otherwise we would not be able to diagnose conflicts between the two constraints. For example, ```carbon constraint C; constraint D; // Can't tell if there is a conflict between `C` and `D`. fn F[T:! C & D](x: T); // These definitions of `C` and `D` conflict. constraint C { extends I where .X = i32; } constraint D { extends I where .X = bool; } ``` Since our existing examples of using forward declarations to define a graph with cyclic references between the edge and node interfaces needed this feature, we decided to support it. This motivated the requirement that [the definition be in the same file as the declaration](#allow-declaration-and-definition-in-different-files), so the error could be detected once the compiler reached the definitions. ================================================ FILE: proposals/p2360.md ================================================ # Types are values of type `type` [Pull request](https://github.com/carbon-language/carbon-lang/pull/2360) ## Table of contents - [Abstract](#abstract) - [Problem](#problem) - [Tuples behave inconsistently](#tuples-behave-inconsistently) - [Qualified access to constraint members is inconsistent](#qualified-access-to-constraint-members-is-inconsistent) - [Background](#background) - [Proposal](#proposal) - [Details](#details) - [Terminology](#terminology) - [`type` and `Core.Type`](#type-and-coretype) - [Compound member access](#compound-member-access) - [Instance binding](#instance-binding) - [Impl lookup](#impl-lookup) - [Examples](#examples) - [Tuple and struct literals](#tuple-and-struct-literals) - [Name lookup into values with constrained types](#name-lookup-into-values-with-constrained-types) - [Types in diagnostics](#types-in-diagnostics) - [Rationale](#rationale) - [Teachability](#teachability) - [Alternatives considered](#alternatives-considered) - [Alternative member access rules](#alternative-member-access-rules) - [Alternative terminology](#alternative-terminology) - [`Core.Type` is an empty named constraint](#coretype-is-an-empty-named-constraint) - [Write tuple types differently than tuples](#write-tuple-types-differently-than-tuples) ## Abstract Define a "type" to be a value of type `type`. Contexts expecting a type perform an implicit conversion to `type`. Values like `()`, `(i32, i32)`, and `{}` are no longer types, but instead implicitly convert to `type`. Values of interface or constraint type (now called "facets") are similarly not formally types but implicitly convert to `type`. The practical impact to the language semantics is relatively small. This proposal primarily establishes clearer, more principled terminology and design concepts with a few narrow improvements to the design's consistency. ## Problem ### Tuples behave inconsistently Currently, the type of `(i32, i32)` is `(Type, Type)`, and similarly the type of `(Type, Type)` is also `(Type, Type)`. This leads to an inconsistency: for most values, asking for the type, then the type-of-type, and so on, quickly leads to `Type`, whose type is `Type`. But for a few values, notably tuples and empty structs, we instead bottom out at a different value. This leads to implementation complexity and a lack of clarity around what is or is not a type. It is easy to find examples where explorer misbehaves or crashes due to getting tuple values and types mixed up. Moreover, this inconsistency is limited to a few built-in types. There is no way to design a class type so it behaves like a tuple type. ### Qualified access to constraint members is inconsistent Given the expression `x.(Interface.Function)`, where the function is an instance method, the behavior depends on whether `x` is a type. - If `x` is not a type, but a value of type `T`, this requires `T is Interface` and looks for `Function` in the corresponding `impl`. Instance binding is performed. - If `x` is a type, this requires `x` to have symbolic value phase and requires that `x is Interface`. The result is an unbound member name. This dual meaning has some problematic consequences: - Even if `Interface.Function` is non-dependent, if `x` depends on a template parameter then we must treat the compound member access as being dependent. Qualification cannot be used to avoid dependence in templates. - Worse, if `x` is a template parameter, the interpretation will differ between instantiations, meaning that a template and a checked generic will have a discontinuity in behavior when `x` happens to be a type. - While it is possible to `impl Type as ...`, this rule means it is not possible to directly invoke methods that have been implemented this way. For example, given an `impl Type as DebugPrintable`, `i32.(DebugPrintable.Print)()` will look for `i32 as DebugPrintable` not `Type as DebugPrintable`. ## Background - [#989: Member access expressions](/proposals/p0989.md) introduced the current compound member access rules. - [Issue #495](https://github.com/carbon-language/carbon-lang/issues/495) suggested adding an implicit conversion from tuples of types to type `Type`. - [Issue #508](https://github.com/carbon-language/carbon-lang/issues/508) raised the question of whether we want tuples like `((), (), ())` to be self-typed. - [Issue #2113](https://github.com/carbon-language/carbon-lang/issues/2113) gave us spelling and behavior guidelines for predefined names like `type`. ## Proposal Instead of treating type-like values as being types regardless of the type of those values, we define a _type_ as being a value of type `type`. This has the following consequences: - `()` is a value of type `() as type`, not a value of type `()` (because the latter is not a type). - `() as type` is a value of type `type`. - Similarly, `(i32, i32)` is of type `(type, type) as type`, which is of type `type`. - Similarly, `{}` is of type `{} as type`, which is of type `type`. - More generally, _the type of any type is `type`_. - Given `T:! Hashable`, `T` is formally not a type, because it is not of type `type`. Indeed, member lookup into `type`, and hence into types, finds nothing, but member lookup into `T` finds members of `Hashable`. - Contexts in which a type is required, such as on the right hand side of a `:` or `:!` binding or the return type of a function, perform an implicit conversion to type `type`. - This means that a user-defined type can implement `ImplicitAs(type)`, annotated in whatever way we eventually decide to annotate functions that are usable at compile time, and values of that type will be usable as types, in the same way that `()` and `{}` are. - There are no singleton type-of-types. It's still possible to have, for example, the type of `i32` be a type whose only value is `i32`. The value `i32` would then not formally be a type, as its type would not be `type`, but it could still implicitly convert to `type`, as `()` does, if we so desire. - This extends to other cases: we could say that for integer literals, which we have recently been describing as having type `IntLiteral(n)`, there is an `impl IntLiteral(n) as ImplicitAs(type)` which converts a value of type `IntLiteral(n)` to the `type` value `IntLiteral(n)`. This would then permit `let v: 0 = 0;`. - Such changes to `i32` or integer literals are not part of this proposal. ## Details ### Terminology The changes to the language rules in this proposal are fairly small, but there is a significant change in how we describe those rules. We now use the following terminology: - A _type_ is a value of type `type`. - A _facet type_ is a type whose values are some subset of the values of `type`, determined by a set of constraints. - Interface types and named constraint types are facet types whose constraints are that the interface or named constraint is satisfied by the type. - The values produced by `&` operations between facet types and by `where` expressions are facet types, whose set of constraints are determined by the `&` or `where` expression. - `type` is a facet type whose set of constraints is empty. We previously referred to a facet type as a type-of-type, but that is no longer accurate, as the type of any type is now by definition `type`. - A _facet_ is a value of a facet type. For example, `i32 as Hashable` is a facet, and `Hashable` is a facet type. Note that all types are facets. - A facet value can be thought of as a tuple of a possibly-symbolic identity for a type, plus any witnesses needed to demonstrate that the type satisfies the constraints of the facet type. A type is then a facet value that has no witnesses. - We use the term _generic type_ to refer to a type or facet introduced by a `:!` binding, such as in a generic parameter or associated constant. - This gives rise to terms such as _generic type parameter_ and _associated generic type_. - It is worth noting that a generic type is not necessarily a type. It may be a facet, and its type may be a facet type other than `type`. ### `type` and `Core.Type` This proposal introduces the name `type` as a keyword -- a type literal -- that names the facet type with an empty set of constraints. This keyword replaces the provisional name `Type` for this functionality. In [issue #2113](https://github.com/carbon-language/carbon-lang/issues/2113), it was decided that such functionality may be an alias for a name in the prelude, such as `Core.Type`. This proposal does not introduce a corresponding prelude name. The facet type `type` is primitive and provided by the compiler, and not an alias. [As noted in the generics design](/docs/design/generics/details.md#named-constraints), a declaration such as `constraint MyVersionOfType {}` would introduce a facet type that is equivalent to `type`. This proposal does not change this decision. Because such a named constraint `MyVersionOfType` is equivalent to `type`, that is, because they are the same facet type, a value `T:! MyVersionOfType` is a type under this proposal. There is an open [issue for leads](https://github.com/carbon-language/carbon-lang/issues/2409) on whether this is the right behavior or if we'd prefer a different rule. ### Compound member access #### Instance binding For the syntax `x.y`, if `x` is an entity that has member names, such as a namespace or a type, then `y` is looked up within `x`, and instance binding is not performed. Otherwise, `y` is looked up within the type of `x` and instance binding is performed if an instance member is found. The syntax `x.(Y)`, where `Y` names an instance member, always performs instance binding. Therefore, for a suitable `DebugPrintable`: - `1.(DebugPrintable.Print)()` still prints `1`, as before. - `i32.(DebugPrintable.Print)()` now prints `i32`, rather than forming a member name. - `1.(i32.(DebugPrintable.Print))()` is now an error, rather than printing `1`. In order to get the old effect of `x.(MyType.(MyInterface.InstanceMember))()`, one can now write `x.((MyType as MyInterface).InstanceMember)()`. This behaves the same as the member access in: ``` fn F(MyType:! MyInterface, x: MyType) -> auto { return x.(MyType.InstanceMember)(); } ``` ... because it is the same thing. `MyType as InstanceMember` is a facet, which roughly corresponds to a fully-instanstiated `impl`, and lookup into a facet finds members of that corresponding `impl`. And if `MyType` is declared as `MyType:! MyInterface` instead of as `MyType:! type`, then `MyType` and `MyType as MyInterface` are equivalent. #### Impl lookup Prior to this proposal, when a member access names a member of interface `I`, the rules for `impl` lookup also depend on whether the left hand operand is a type: - If the left hand operand is a type-of-type (now, facet type), `impl` lookup is not performed. - If the left hand operand evaluates to a type `T`, then `impl` lookup is performed for `T as I`. - Otherwise, the first operand is an expression of some type `T`, and `impl` lookup is performed for `T as I`. Due to the non-uniform treatment of types and non-types, this rule is also changed to the following: - For a simple member access `a.b` where `b` names a member of an interface `I`: - If the interface member was found by searching a non-facet-type scope `T`, for example a class or an adapter, then `impl` lookup is performed for `T as I`. For example: - `MyClass.AliasForInterfaceMember` finds the member of the `impl MyClass as I`, not the interface member. - `my_value.AliasForInterfaceMember`, with `my_value: MyClass`, finds the member of the `impl MyClass as I`, and performs instance binding if the member is an instance member. - In the case where the member was found in a base class of the class that was searched, `T` is the derived class that was searched, not the base class in which the name was declared. - Otherwise, `impl` lookup is not performed: - `MyInterface.AliasForInterfaceMember` finds the member in the interface `MyInterface` and does not perform `impl` lookup. - For a compound member access `a.(b)` where `b` names a member of an interface `I`, `impl` lookup is performed for `T as I`, where: - If `b` is an instance member, `T` is the type of `a`. - `my_value.(Interface.InstanceInterfaceMember)()`, where `my_value` is of type `MyClass`, uses `MyClass as Interface`. - `MyClass.(Interface.InstanceInterfaceMember)()` uses `type as Interface`. - Otherwise, `a` is implicitly converted to `I`, and `T` is the result of symbolically evaluating the converted expression. - `MyClass.(Interface.NonInstanceInterfaceMember)()` uses `MyClass as Interface`. - `my_value.(Interface.NonInstanceInterfaceMember)()` is an error unless `my_value` implicitly converts to a type. This approach aims to change as little as possible of the existing `impl` lookup behavior while removing inconsistencies and behavioral differences based on whether a value is a type. #### Examples ``` interface Iface { fn Static(); fn Method[me: Self](); } impl type as Iface { ... } fn F[T:! Iface](x: T) { // ✅ Uses `T as Iface`. x.Static(); T.Static(); // ❌ Uses `T as Iface`, but method call is missing an instance. T.Method(); // ✅ OK, instance is provided. Uses `T as Iface`. x.Method(); x.(T.Method)(); // ❌ Would use `(x as Iface).Static`. // Error because `x` is not a symbolic constant. x.(Iface.Static)(); // ✅ Uses `(T as Iface).Static`. T.(Iface.Static)(); // ✅ Uses `(type as Iface).Static`. type.(Iface.Static)(); // ✅ Uses `(T as Iface).Method`, with `me = x`. x.(Iface.Method)(); // ✅ Uses `(type as Iface).Method`, with `me = T`. T.(Iface.Method)(); } base class Base { fn Static(); fn Method[me: Self](); alias IfaceStatic = Iface.Static; alias IfaceMethod = Iface.Method; } external impl Base as Iface { ... } fn G(b: Base) { // ✅ OK b.Static(); Base.Static(); // ❌ Uses `Base as Iface`, but method call is missing an instance. Base.Method(); // ✅ OK b.Method(); b.(Base.Method)(); // ✅ OK, same as `(Base as Iface).Static()`. b.IfaceStatic(); Base.IfaceStatic(); // ❌ Uses `Base as Iface`, but method call is missing an instance. Base.IfaceMethod(); // ✅ OK, uses `Base as Iface`. b.IfaceMethod(); b.(Base.IfaceMethod)(); b.((Base as Iface).Method)(); b.(Iface.Method)(); } class Derived extends Base {} external impl Derived as Iface { ... } fn H(d: Derived) { // ✅ OK, calls `Base.Static`. d.Static(); Derived.Static(); // ❌ Uses `Derived as Iface`, but method call is missing an instance. Derived.Method(); // ✅ OK, calls `Base.Method`. d.Method(); d.(Base.Method)(); d.(Derived.Method)(); // ✅ OK, same as `(Derived as Iface).Static()`. d.IfaceStatic(); Derived.IfaceStatic(); // ❌ Uses `Derived as Iface`, but method call is missing an instance. Derived.IfaceMethod(); // ✅ OK, uses `Derived as Iface`. d.IfaceMethod(); d.(Derived.IfaceMethod)(); d.((Derived as Iface).Method)(); d.(Iface.Method)(); // ✅ OK, uses `Base as Iface`. d.(Base.IfaceMethod)(); d.((Base as Iface).Method)(); } ``` ### Tuple and struct literals A tuple literal such as `(1, 2, 3)` produces a tuple value. Its type cannot be written directly as a literal: the literal `(i32, i32, i32)` also produces a tuple value, not a type value. However, the type of `(1, 2, 3)` is easy to express: it is the result of implicitly converting `(i32, i32, i32)` to a type, so for example `(i32, i32, i32) as type` evaluates to the type of `(1, 2, 3)`. There is no conversion from a tuple type to a tuple value, because tuple types are values of type `type`, and the type `type` does not implicitly convert to a tuple type. For metaprogramming, it is likely that we will benefit from having a mechanism to convert a tuple type into a tuple of types, but this is likely to be possible using a variadic: ``` // Takes a tuple of values. Returns a tuple containing the types of those values. fn TupleTypeToTupleOfTypes[Types:! type,...](x: (Types,...)) -> auto { return (Types,...); } ``` ### Name lookup into values with constrained types Given a generic function with a generic type parameter, uses of that parameter will be implicitly converted to type `type`. For example: ``` fn F[T:! Printable](x: T) { x.Print(); } ``` ... is equivalent to ... ``` fn F[T:! Printable](x: T as type) { x.Print(); } ``` As a result, we can no longer describe lookup for `x` as looking into the type-of-type, that is, the type of the expression after `x:`, because after implicit conversion, that expression is of type `type`. Instead, lookup will look into the type of `x`, which symbolically evaluates to the value corresponding to `T` in type `type`, and will look at that value to determine where else to look. Because that value is symbolically the name of a generic type parameter, we will look in the type of `T`, which is `Printable`. ### Types in diagnostics Concern has been raised that this proposal could lead to `as type` appearing frequently in diagnostics when types are printed. This is not expected to be the case. When a type appears in a diagnostic, it will generally be in one of two forms: - A reflection of source-level syntax that the programmer wrote. - A string generated by the compiler to describe the type to the programmer. In both cases, we expect the type of a variable such as `var v: (i32, i32)` to be displayed as `(i32, i32)`, not as `(i32, i32) as type`, unless either the context requires the `as type` suffix for disambiguation or the programmer wrote it in the source code. This is analogous to how integer literals behave: when describing the value `1` of type `i32` in a diagnostic, the type is usually treated as assumed context, so the diagnostic would say simply `1` rather than `1 as i32`. Similarly, C++ compilers typically avoid printing things like `(short)5` when including values of type `short`, for which there is no literal syntax, in diagnostics. ## Rationale - [Language tools and ecosystem](/docs/project/goals.md#language-tools-and-ecosystem) - Making the behavior of tuples and empty structs more like other types is easier for tooling to handle. - [Software and language evolution](/docs/project/goals.md#software-and-language-evolution) - The transition between templates and generics is made smoother by changing compound member access to behave consistently regardless of whether the left-hand operand is a type. - [Code that is easy to read, understand, and write](/docs/project/goals.md#code-that-is-easy-to-read-understand-and-write) - Bottoming out at the same type, `type`, for all types is likely to be easier to understand. However, having `(i32, i32)` not be the type of a pair of integers, but instead be _implicitly convertible to_ the type of a pair of integers, is likely to be harder to understand. It's unclear which of these will be more dominant, but the new behavior is more consistent. ### Teachability One of the bigger concerns with this proposal is how teachable it is. The details of this model are likely to be challenging for new-comers to Carbon, even ones with experience in C++. When explaining this model, we suggest focusing on the goal and how it is achieved, rather than what happens under the covers. For example, we can say > You can use `var n: (i32, i32)` to declare that `n` is a pair of `i32`s without mentioning that the actual type of `(i32, i32)` is `(type, type)`, and that an implicit conversion is performed here. This is analogous to how we can say > You can use `n = (1, 2);` to assign `1` to the first element of `n` and `2` to > the second element without mentioning that again an implicit conversion is performed. In practice, this is a relatively small change from Carbon's design prior to this proposal, and there are also teachability concerns with the ability to treat a tuple of types as a type despite it _not_ being a value of type `type`. Should there prove to be sustained problems with teachability, we should consider making more significant changes, such as adopting a distinct syntax for tuple types and empty structs. ## Alternatives considered ### Alternative member access rules We considered rules where `T.(Interface.Member)` would either always mean `(T as Interface).Member` or always mean `(typeof(T) as Interface).Member`, but that did not allow aliases to work as expected. We wanted an alias to an interface method or class function defined inside of a class to be usable as a method or class function member of the class. ``` interface A { fn F[me: Self](); fn S(); } class B { external impl as A; alias G = A.F; fn H[me: Self](); alias T = A.S; fn U(); } ``` We want `B.G` to act like a method of `B`, like `B.H`, and `B.T` to act like a static class function of `B`, like `B.U`. We could have accomplished this by requiring those aliases to be written in a different way, for example `alias G = (Self as A).F`, but needing to do that was surprising. We made it work by making the interpretation of `T.(Interface.Member)` depend on whether `Member` was an _instance_ member of `Interface`, with an implicit `me` parameter, or not. This was discussed in [the #syntax channel on discord on 2022-11-07](https://discord.com/channels/655572317891461132/709488742942900284/1039298322625744987) and in [open discussion on 2022-11-10](https://docs.google.com/document/d/1tEt4iM6vfcY0O0DG0uOEMIbaXcZXlNREc2ChNiEtn_w/edit#heading=h.1y65biik2vr). ### Alternative terminology We considered various alternatives for the terminology choices listed above in [open discussion on 2022-10-27](https://docs.google.com/document/d/1tEt4iM6vfcY0O0DG0uOEMIbaXcZXlNREc2ChNiEtn_w/edit#heading=h.lecxe8qywdxa). Instead of "facet type" we considered: - constraint type - Concern: a "foo type" can mean "a type that is also a foo" like "class type" or it can mean "a type that holds foos" like "integer type". - constrained type - Concern: for some of us, this name seemed to better describe facets than facet types. - constraint - Concern: this may sound like it should refer to a boolean predicate, `T is C`, not `C` itself. - kind - Concern: while this terminology is used to describe the type of a type in computer science literature, that usage is different from this one in that it generally refers to the arity of type constructors rather than classifying subsets of the set of types. Instead of "generic type" we considered: - archetype - Concern: the usage here didn't match some of our intuitions based on existing usage of this term. - Concern: while this might fit usage within the scope of the parameter, it fits usage from outside the scope less well. It seemed awkward to say that a class has an archetype parameter, as opposed to our chosen terminology of a generic type parameter. - constrained type variable - Concern: for some of us, the name "constrained type" seemed to better describe facet types than facets. - type-like - Concern: derived terms like "generic type-like parameter" and "associated type-like constant" are awkward. - kinded - Concern: derived terms like "generic kinded parameter" and "associated kinded constant" are awkward. Instead of "facet" we considered: - type - The idea here is that, because facets can be used in type contexts, calling them types is reasonable. - Concern: we want `i32 as Sortable` and `i32 as Hashable` to be different, but we do not want to say that they are "different types". - Concern: we use `type` to refer to types, not facets, and it's important that conversion to type `type` erases the facet type from a facet. If we call facets "types" then we mean different things by "type" and "`type`", and for example conversion of a type to type `type` would change its meaning, which seems surprising. - impl - Concern: facets are analogous to `impl`s but are not in direct correspondence, because a facet can be for a constraint that involves multiple interfaces and hence potentially multiple `impl`s, and an `impl` can be for a constraint that involves multiple interfaces and hence a facet can refer to only _part of_ an `impl`. - archetype - Concern: this seems inappropriate for non-generic facets, such as `i32 as Hashable`. - witness - Concern: doesn't quite represent what we mean: a facet is more like a pair of a type and a witness that that type implements the facet's type. ### `Core.Type` is an empty named constraint We considered making `type` an alias for an empty type constraint defined in the prelude, `Core.Type`. While it is possible to define a constraint that is equivalent to `type` as a named constraint, we decided not to define `type` as an alias to such a named constraint in order to reduce complexity: - Type-checking the prelude itself becomes more challenging if `type` is a named constraint, due to the ubiquity of its usage. - If `type` is an alias to `Core.Type`, then either the implementation would need to ensure that it introduces no member names and no constraints, or it would need to faithfully look into `Core.Type` every type a property of `type` is consulted, adding either compile-time cost or complexity to the implementation. Given that we have no current desire to add member names or constraints to `type`, we do not get any benefit from defining it in the Carbon library. If we later choose to make `type` imply some other constraints by default, such as `Sized` or `Movable`, this decision should be revisited. This was discussed in: - [#naming on 2022-10-31](https://discord.com/channels/655572317891461132/963846118964350976/1036750048899366954) - [#syntax on 2022-10-27](https://discord.com/channels/655572317891461132/709488742942900284/1035222720864071740) - [open discussion on 2022-10-31](https://docs.google.com/document/d/1tEt4iM6vfcY0O0DG0uOEMIbaXcZXlNREc2ChNiEtn_w/edit#heading=h.9x8m0qwhuux) ### Write tuple types differently than tuples We considered having the way to write tuple types be different than tuples. So instead of `var x: (i32, i32) = (1, 2);` you might write: ```carbon var x: TupleType(i32, i32) = (1, 2); // Or, with dedicated syntax: var y: (|i32, i32|) = (1, 2); ``` There are a few reasons we decided not to go with this approach: - There is a significant cost to having more balanced delimiters, particularly ones consisting of multiple characters. - Since types are values in Carbon, `(i32, i32)` is a valid and meaningful expression. Users would be surprised if they cannot use it as a type. - Allowing tuples in pattern syntax but not in type syntax may be surprising. For example, given that `var (x: i32, y: i32);` is valid, it may be surprising if `var p: (i32, i32);` is not valid. - We expect that, when teaching or speaking casually, we would not need to explain that `(i32, i32)` was not a type but was usable as a type. That distinction would generally not affect users, who would generally be able to treat the type of a tuple as the tuple of the types of its elements. - This would prevent operations from working generically on both tuples values and tuple types. We would need to instead duplicate the set of operations. For example, there would need to be separate `TupleCat` and `TupleTypeCat` operations to perform concatenation. This would be extra machinery, and a burden for users to know about all of the functions and call the right one. This was discussed [in the #syntax channel on 2022-11-03](https://discord.com/channels/655572317891461132/708431657849585705/1037793379813167166) and [in open discussion on 2022-12-07](https://docs.google.com/document/d/1gnJBTfY81fZYvI_QXjwKk1uQHYBNHGqRLI2BS_cYYNQ/edit?resourcekey=0-ql1Q1WvTcDvhycf8LbA9DQ#heading=h.qu6e01wbzrhz). ================================================ FILE: proposals/p2365.md ================================================ # Remove artificial version ceiling on C++ interop. [Pull request](https://github.com/carbon-language/carbon-lang/pull/2365) ## Table of contents - [Abstract](#abstract) - [Problem](#problem) - [Proposal](#proposal) - [Details](#details) - [Rationale](#rationale) - [Alternatives considered](#alternatives-considered) - [Do nothing](#do-nothing) - [Enumerate specific versions and feature sets](#enumerate-specific-versions-and-feature-sets) ## Abstract Remove a confusing mention of a specific version of C++ (C++17) from the interoperability goals. Expand the content of the goals to make it clear that we have a moving and ongoing target of C++ as it continues to evolve. Also emphasize that we will prioritize among the different features during Carbon's development based on how they impact the overall project. However, this intends to preserve the fact that there may exist long-tail or corner-case features in C++ that never end up with high quality or exhaustive support in our interop story simply because their impact on Carbon users is sufficiently small that it doesn't justify the cost. ## Problem The text referring to C++17 was bound to become out-of-date as time marched onward. As a result, it was increasingly confusing and anchored us in the past. It was also easily misunderstood in several cases as foreclosing _any_ effort to interoperate with future versions or features of C++. While at the time written, this may not have been a priority, it seems to increasingly be a source of confusion or friction for users without benefit. ## Proposal Remove the specific version and replace it with an attempt to explain the long-term goal and how we will prioritize within that long-term goal. Eventually, we should be aiming to support all major features and versions of C++ that are used in the industry. However, we should be pragmatic in our prioritization by focusing on those features and versions with the maximum impact on the project. ## Details See the updated text in the [interop goals](/docs/design/interoperability/philosophy_and_goals.md#never-require-bridge-code). ## Rationale - [Community and culture](/docs/project/goals.md#community-and-culture) - This change should address confusion across the community, especially as that community has grown. - [Interoperability with and migration from existing C++ code](/docs/project/goals.md#interoperability-with-and-migration-from-existing-c-code) - Our goal is overall interoperability with C++ and the new wording should better capture that. ## Alternatives considered ### Do nothing We could not make this change, and live with some amount of confusion. However, that seems to be enough confusion to be a distraction and so it seems worth updating our documentation. ### Enumerate specific versions and feature sets We could try to enumerate specific versions and feature sets but it seems likely for this list to change frequently and be a new source of confusion or maintenance burden. Given the current stage of the project, it seems preferable to instead describe the high level goal and how we expect to prioritize features. The actual work to design interop can in turn present the specific features covered. ================================================ FILE: proposals/p2376.md ================================================ # Constraints must use `Self` [Pull request](https://github.com/carbon-language/carbon-lang/pull/2376) ## Table of contents - [Abstract](#abstract) - [Problem](#problem) - [Background](#background) - [Proposal](#proposal) - [Details](#details) - [Rationale](#rationale) - [Alternatives considered](#alternatives-considered) ## Abstract Require `impl as` constraints in an `interface` or `constraint` definition to mention `Self` implicitly or explicitly. Require `where` clauses to refer to `.Self` directly, or through a designator like `.Foo`. ## Problem When trying to implement constraints in Carbon Explorer, we [came up with an example](https://discord.com/channels/655572317891461132/941071822756143115/986054308179103765) that raised questions: ```carbon interface A {} interface B {} external impl forall [T:! Type] T as A where T is B {} ``` There were multiple possible interpretations for what the meaning of that `where` clause was. - It could be equivalent to `external impl forall [T:! Type where .Self is B] T as A {}`. That is, this introduces an implementation of A for only those T that satisfy the where condition. - It could be equivalent to `external impl forall [T:! Type] T as A {}` but invalid if there is no `impl forall [T:! Type] T as B`. that is, this requires an implementation of `B` to exist for all `T`. - It could be equivalent to `external impl forall [T:! Type] T as A & B {}`. That is, this introduces an implementation of `B` for all `T`. - It could be invalid for various reasons. That advantage of making this construction invalid is that it would force the code into a form with a clearer meaning. Other cases suggested that constraints that were modifying other types were in general surprising, [for example](https://discord.com/channels/655572317891461132/941071822756143115/986061766226214932): ```carbon fn F[A:! Type, B:! Type, C:! Type where A == B](a: A, b: B, c: C); ``` would be better written as: ```carbon fn F[A:! Type, B:! Type where A == .Self, C:! Type](a: A, b: B, c: C); ``` so the relationship between types `A` and `B` would be established from their two declarations, not later modified by the declaration of `C`. In summary, we ended up with a number of reasons to say a `where` clause should be a constraint on the type being modified: - We prefer there only be [one way](/docs/project/principles/one_way.md) to write constraints. We believe that the examples that don't meet this restriction can always be rewritten to a form that meets this restriction. - We believe that after the rewrite, there is less ambiguity about what the code means. - We think it is valuable that the constraints on a type are complete when the type's declaration is complete. We found similar restrictions are valuable for `impl as` constraints in an `interface` or `constraint` definition. The restriction that they always involve the `Self` type means that the search that compiler has to do to find relevant constraints is limited to a finite number of definitions. Furthermore, without this restriction, the set of interfaces known to implement a type would change depending on which interfaces definitions are imported and known to be satisfied, which is a coherence problem. This restriction also allows interfaces and named constraints to be used while incomplete, which allows some use cases that involve circular references, including self reference. The logic goes like this: - we want to limit when information from a constraint is found, - to increase the cases where we don't need to look in a constraint, - so constraints are allowed to be incomplete in those cases. Proposal [#2347](https://github.com/carbon-language/carbon-lang/pull/2347) lists conditions when we want to allow constraints to be incomplete. ## Background There are a number of earlier proposals related to or modified by this proposal: - [#553: Generics details part 1](https://github.com/carbon-language/carbon-lang/pull/553) introduced `impl as` restrictions in interfaces and named constraints. - [#818: Constraints for generics (generics details 3)](https://github.com/carbon-language/carbon-lang/pull/818) introduce `where` constraints. - [#1013: Generics: Set associated constants using `where` constraints](https://github.com/carbon-language/carbon-lang/pull/1013) switched to using `where` constraints in `impl` declarations to specify associated constants. - [#1084: Generics details 9: forward declarations](https://github.com/carbon-language/carbon-lang/pull/1084) allowed forward declaration of interfaces and named constraints, explicitly supporting incomplete interfaces and named constraints beyond when they were being defined. - [#2107: Clarify rules around `Self` and `.Self`](https://github.com/carbon-language/carbon-lang/pull/2107) established some rules around `Self` and `.Self`, which this proposal adds to. - [#2347: What can be done with an incomplete interface](https://github.com/carbon-language/carbon-lang/pull/2347) clarifies what can be done with an incomplete interface or named constraint. Those rules rely on this proposal to be implementable. ## Proposal `where` clauses must use a designator, either `.Self` or `.Foo` for some member `Foo`. The designator may be used directly, or supplied as an argument to a type, interface, or named constraint used in the `where` clause, as in these examples: - `Container where .ElementType = i32` - `Type where Vector(.Self) is Sortable` - `Addable where i32 is AddableWith(.Result)` `impl as` declarations in interfaces and named constraints must always involve `Self`: - Can be the implicit `Self` when no type is specified, as in `impl as ...`, or the equivalent declarations with `Self` declared explicitly, as in `impl Self as ...` - Can be an argument to a type. The type can be what is to the left of the `as`, as in `impl Vector(Self) as ...`, or a type argument to the interface or constraint, as in `impl Vector(i32) as AddWith(Vector(Self))`. - Can be a parameter to the interface or constraint to the right of the `as`, as in `impl T as Bar(Self)`. When the compiler looks to see if any constraints imply that an impl exists, the only place it needs to look are the places that involve the type the impl is for (`Self`). This means the compiler never needs to look in forward-declared (or otherwise incomplete) constraints that don't involve that type. This applies recursively. This allows incomplete interfaces and named constraints as described in proposal [#2347](https://github.com/carbon-language/carbon-lang/pull/2347). This solves a problem: when doing impl lookup, what is the set of imlps that you can look up? There may be an infinite set of constraints reachable through interfaces, but with this rule, you only need to consider a finite subset. ## Details The ["Generics: Details" design document](/docs/design/generics/details.md) has been updated with this proposal. It includes clarification in the [conditional conformance section](/docs/design/generics/details.md#conditional-conformance) that an `impl` in a `class` definition can only be for the type being defined. ## Rationale These restrictions are in support of the ["prefer providing only one way to do a given thing" principle](/docs/project/principles/one_way.md), by reducing the number of equivalent ways of expressing a constraint. As described in the [problem section](#problem), these restrictions make code [easier to read and understand](/docs/project/goals.md#code-that-is-easy-to-read-understand-and-write) by avoiding confusing or ambiguous constructions. These restrictions reduce the search the compiler needs to perform to find relevant constraints during impl lookup, in support of [fast and scalable development](/docs/project/goals.md#fast-and-scalable-development). ## Alternatives considered The main alternative we considered, was not imposing these restrictions. We decided these restrictions were a good idea in these conversations: - [#generics-and-templates on 2022-06-13](https://discord.com/channels/655572317891461132/941071822756143115/986061509815844864) - [Open discussion on 2022-10-12](https://docs.google.com/document/d/1tEt4iM6vfcY0O0DG0uOEMIbaXcZXlNREc2ChNiEtn_w/edit#heading=h.q7afaawbc5k) - [#generics-and-templates 2022-10-24](https://discord.com/channels/655572317891461132/941071822756143115/1034198851059466292) - [2022-10-24 open discussion](https://docs.google.com/document/d/1tEt4iM6vfcY0O0DG0uOEMIbaXcZXlNREc2ChNiEtn_w/edit#heading=h.hb5qukkw7d3l) The advantages of this proposal are outlined in the [problem section](#problem). The main disadvantage of this proposal that [we considered](https://discord.com/channels/655572317891461132/941071822756143115/986063589016215614) is that it removes the option to use another name for the type than `.Self`. The concern was that `.Self` might be seen as an advanced feature that is difficult to understand, or it might be longer. ================================================ FILE: proposals/p2483.md ================================================ # Replace keyword `is` with `impls` [Pull request](https://github.com/carbon-language/carbon-lang/pull/2483) ## Table of contents - [Abstract](#abstract) - [Problem](#problem) - [Background](#background) - [Proposal](#proposal) - [Rationale](#rationale) - [Alternatives considered](#alternatives-considered) - [`T as C`](#t-as-c) - [`T: C`](#t-c) ## Abstract Use the keyword `impls` instead of `is` when writing a `where` constraint that a type variable needs to implement an interface or named constraint. What was previously (provisionally) written: ``` fn Sort[T:! Container where .ElementType is Ordered](x: T*); ``` will now be written: ``` fn Sort[T:! Container where .ElementType impls Ordered](x: T*); ``` ## Problem The `is` keyword has been used as a placeholder in `where` constraints expressing that a type variable is required to implement an interface or named constraint. It has been an open question what word should be used in that position since its original adoption in [#818](https://github.com/carbon-language/carbon-lang/pull/818). Since then, reasons to use a different word in this position have been discovered: - The word "is" is unspecific and could represent many different relationships. - This specific relationship is not symmetric. - We potentially want to use `is` as a keyword for another purpose. - The precedent for `is` came from Swift where `x is T` means "`x` has the type `T`". With the changes to Carbon generic semantics, particularly [#2360: Types are values of type `type`](https://github.com/carbon-language/carbon-lang/pull/2360), that is increasingly a poor fit for what we mean by this condition. ## Background The `is` keyword as a constraint operator was introduced in [#818: Constraints for generics (generics details 3)](https://github.com/carbon-language/carbon-lang/pull/818), along with the open question about how to spell it. The choice of `is` in that proposal followed [`is` being Swift's type check operator](https://docs.swift.org/swift-book/LanguageGuide/TypeCasting.html#ID340), where `x is T` is `true` if `x` has type `T`. Note that there are differences between the `is` operator in Swift and what we have used it for in Carbon. In Swift, it is used to test whether a value dynamically has a specific derived type, when you have a value of a base class type and are using inheritance. In Carbon: - it is used on types instead of values; - it is about conformance to an interface (the equivalent of Swift's protocols), and not about inheritance; and - it is resolved at compile time. ## Proposal Use the keyword `impls` instead of `is` when writing a `where` constraint that at type variable needs to implement an interface or named constraint. The specific changes are included in [the same PR as this proposal](https://github.com/carbon-language/carbon-lang/pull/2483). ## Rationale This proposal is working towards Carbon's [code that is easy to read, understand, and write](/docs/project/goals.md#code-that-is-easy-to-read-understand-and-write) goal: - Examples read naturally using "implements" in the place of the `impls` keyword, commonly matching how a comment would describe the constraint. - More clearly communicates the relationship between the two sides and that the relationship is not symmetric. - If a function has a `where T impls C` constraint that is not satisfied for some calling type `T`, the fix is for the caller to add an `impl T as C` definition. ## Alternatives considered One concern with using `impls` is the potential for confusion with the plural of `impl`, meaning "implementations," rather than acting as the verb "implements." We hope to mitigate that concern by avoiding use of "impls" to mean anything other than `impls` in our documentation. For example, we would say "`impl` declarations" instead of "`impl`s". Alternatives were considered in [#2495: Keyword to use in place of `is` in `where`...`is` constraints](https://github.com/carbon-language/carbon-lang/issues/2495). A number of alternatives were considered: - `T is C` - `T isa C` - `T impls C` - `T implements C` - `T ~ C` - `T: C` - `T as C` - `T impl C` - `T impl as C` - `impl T as C` The reasons against `is` were outlined in the [problem](#problem) and [rationale](#rationale) sections. Reasons against other alternatives: - `isa` seems (much) too rooted in inheritance. - `implements` is long but otherwise fine. This is a specific place where being verbose has worrisome negative impact. - `~` is already being considered for something a bit more fitting -- bidirectional convertibility of our type "equality" constraints. - `impl` seems a bit clunky, and surprising to see in this position when it usually isn't. - `impl as` seems even more clunky - `impl T as C` also seems even more clunky, and would be confusing with the rules around rewrite constraints (different here from the use of `impl T as C` in a type). In general there were concerns that the alternatives were confusing and risk appearing to mean something other than what it does. ### `T as C` The keyword `as` received more consideration: - Already a thing, and names the _facet_ that is required to exist. - Already used in a facet-like-but-not-facet context for `impl T as C { ... }`. - Short, easy to read, etc. It had some disadvantages, though: - Doesn't connect readers as directly and effectively to the need for an `impl` to satisfy the constraint. - Doesn't read as nicely in context: `T:! C where C.ElementType as AddWith(i32)`. This was a big consideration in deciding against using `as`. - Underlying the above disadvantage, it doesn't fit into the model of a boolean expression that should be true. Instead, it is a cast that should be _possible_ or _meaningful_, which is somewhat different from the rest of the things in a `where` clause. - However, "rewrite" constraints don't quite fit this model either. - When using `==` constraints, they don't actually imply any boolean expression that would return true. In fact, at least my understanding is that the `T == U` constraint _could_ be written as a boolean expression but it would return _false_ even when the constraint holds. ### `T: C` `T: C` matches how Swift and Rust write this, and is similar to the way you'd write the same constraint in an ordinary declaration, `T:! I`. This syntactic similarity is also a liability due to the differences in semantics when used in a `where` clause: the `where` clause doesn't make the names from `I` available in `T`, and it doesn't propagate `where .A = B` rewrites from `I` to `T`. There's also some concern that the use of `:` would make parameter lists hard to read when they contain embedded `where` clauses, like `T:! Container where .ElementType: Printable, U:! OtherConstraint`. ================================================ FILE: proposals/p2511.md ================================================ # Assignment statements [Pull request](https://github.com/carbon-language/carbon-lang/pull/2511) ## Table of contents - [Abstract](#abstract) - [Problem](#problem) - [Background](#background) - [Proposal](#proposal) - [Details](#details) - [Rationale](#rationale) - [Alternatives considered](#alternatives-considered) - [Allow assignment as a subexpression](#allow-assignment-as-a-subexpression) - [Allow chained assignment](#allow-chained-assignment) - [Do not provide increment and decrement](#do-not-provide-increment-and-decrement) - [Treat increment as syntactic sugar for adding `1`](#treat-increment-as-syntactic-sugar-for-adding-1) - [Define `$` in terms of `$=`](#define--in-terms-of-) - [Do not allow overloading the behavior of `=`](#do-not-allow-overloading-the-behavior-of-) - [Treat the left hand side of `=` as a pattern](#treat-the-left-hand-side-of--as-a-pattern) - [Different names for interfaces](#different-names-for-interfaces) ## Abstract Assignment is permitted only as a complete statement, not as a subexpression. Assignment and compound assignment syntax follow C++ in all other respects. Pre-increment is provided. Post-increment is not. Uses of all of these operators are translated into calls to interface members. ## Problem Assignment is a cornerstone of imperative programming. Carbon does not currently have an approved proposal describing the syntax and interpretation of assignment. ## Background In C-family languages, there are three kinds of assignment-like operators: - Simple assignment: `variable = value` - Compound assignment: `variable $= value`, for some binary operators `$`, meaning `variable = variable $ value`, except that `variable` is evaluated only once. - Increment and decrement: `++variable` and `--variable` meaning `variable += 1` and `variable -= 1`, and `variable++` and `variable--` acting similarly but returning the prior value of `variable`. These operators behave mostly like other binary operators, and in particular the above expression forms can be used as subexpressions of other expressions. Chained assignment is supported, and associates from right to left: `a = b = 1` assigns `1` to `b`, then assigns the result of that assignment to `a`. In C++, that result is typically an lvalue referring to `b`, but in C, it is an rvalue containing the result of converting `1` to the type of `b`. Note that all other operators in C and C++ associate in the other direction. These operators have a collection of known issues, including: - Confusion between assignment and comparison, for example in constructs such as `if (variable = 3) { ... }`. This is sufficiently rife that a de facto compiler-warning-enforced convention has arisen of using additional parentheses for the rare cases when assignment is intended: `if ((variable = 3))`. - Risk of unsequenced modification and access to the same variable, resulting in undefined behavior. For example, `n = a + n++;` has undefined behavior in C and C++ for this reason, because the act of incrementing `n` is not sequenced with respect to the rest of the computation, including the assignment. - In C++, post-increment can be a performance trap, because it is expected to return the old value of the variable, which might otherwise not be preserved. The additional copying may be optimized away if the `operator++` can be inlined, at the cost of additional work for the compiler. - In C++, overloading an operator `$` does not automatically provide a matching `$=`, resulting in additional work or incomplete operator sets. ## Proposal C-family assignment operators are provided as statements: - `variable = value;` is a simple assignment statement. - `variable $= value;` is a compound assignment for each binary operator `$`, other than comparisons. `<=` and `>=` mean "less than or equal to" and "greater than or equal to", not "compare and assign". - `++variable;` and `--variable;` are supported as increment and decrement syntax. Because these are statements, there is no distinction between pre- and post-increment, and post-increment is not provided. These operations are translated into calls on interfaces. ## Details See the changes to the design. This proposal does not define the semantics of assignment that are provided for classes by default. Leads issue [#686](https://github.com/carbon-language/carbon-lang/issues/686) gives some rules, but those rules are not part of this proposal. ## Rationale - [Language tools and ecosystem](/docs/project/goals.md#language-tools-and-ecosystem) - The values of variables can only change if either the address is taken, including implicitly by `addr` self parameters, or at assignment statements, making it easier to reason about where the value of a variable can change in the control flow of a function. - [Software and language evolution](/docs/project/goals.md#software-and-language-evolution) - This approach is conservative and can evolve to support assignment as a subexpression. - [Code that is easy to read, understand, and write](/docs/project/goals.md#code-that-is-easy-to-read-understand-and-write) - Assignments in subexpressions tend to be hard for humans to read and understand. Disallowing them at the language level avoids the potential for confusion, making code easier to read at the cost of making certain constructs such as `n = arr[i++];` a little harder to write. - Easier to write a complete operator set because both `$` and `$=` can be provided by implementing a single interface. - [Practical safety and testing mechanisms](/docs/project/goals.md#practical-safety-and-testing-mechanisms) - Replaces correctness warning on `if (a = b)` with a language rule. - [Interoperability with and migration from existing C++ code](/docs/project/goals.md#interoperability-with-and-migration-from-existing-c-code) - Providing largely the same set of symbols makes migration easier. - Some cost may be imposed by forcing a translation from assignment in subexpressions to assignment as separate statements. ## Alternatives considered ### Allow assignment as a subexpression We could allow some or all forms of assignment as subexpressions, either with the same syntax or with some other syntax. This proposal does not support assignment as a subexpression because the utility of this feature in C++ is very limited and leads to problems where equality comparison and assignment are easily confused. Modeling assignment as a statement also makes it easier to treat it as the transition point between a variable being in an unformed state and in a fully-formed state, as it prevents such transitions from happening at a not-fully-sequenced point within the evaluation of an expression. To avoid the syntactic confusion between assignment and comparison, we could allow assignment as a subexpression with some other syntax. For example, we could adopt Python's "walrus operator" `variable := value`. For now, we are choosing to not pursue this option in order to determine how much motivation there is for such a feature. If we allow a three-term form of `for` statements, `for (init; cond; incr)`, we could allow assignment in the `incr` term. However, we currently do not support this syntax. The absence of assignment as a subexpression can be worked around with an ergonomic cost, either by using a direct function call `x.(Assign.Op)(y)` or with a lambda wrapping an assignment. Post-increment `a++` could be transformed into `${var b: auto = a; ++a; return b;}`, where `${...}` is a placeholder for Carbon's eventual lambda syntax. Workarounds that are not assignment statements would likely be treated as capturing `x` for static analysis purposes, not as definitely initializing `x`, so unformedness checks and other similar checks we might choose to include in the Carbon language design may treat these workarounds conservatively. ``` var a: i32; var b: i32; var c: i32; // ✅ OK, per pending proposal #2006. a = 1; // 🤷 Might result in an error or warning; // `b` is in an unformed state. b.(Assign.Op)(1); // 🤷 Undecided whether we will guarantee that // assignment to `c` precedes read from `c`. a += c.(Assign.Op)(1) + c; ``` ### Allow chained assignment As a special case of assignment as a subexpression, we could allow chained assignment: ``` a = b = c = 0; ``` We could restrict this to only apply to simple assignment, not cases like `a += b -= c *= 2;` In leads issue [#451](https://github.com/carbon-language/carbon-lang/issues/451) it was decided that we would initially not support chained assignment, although this was largely the result of a lack of arguments in favor of support that would justify the complexity of adding a special case, and should be reconsidered if compelling arguments in favor of chained assignment are uncovered. ### Do not provide increment and decrement We could remove `++var;` and `--var;` in favor of `var += 1;` and `var -= 1;`. However, developers coming from C-family languages will expect these operators to exist, and they may more directly convey the intended semantics of counting and navigating in a one-dimensional granular space than a `+=` operation would. ### Treat increment as syntactic sugar for adding `1` We could treat `++a;` as being syntactic sugar for `a += 1;`, in the same way that we treat `a += 1;` as being syntactic sugar for `a = a + 1;`, and similarly for `--a;`. This would mean that floating-point types gain increment and decrement operators, as in C++. The literal `1` has its own type, which means that this approach would not require a type to support adding integers in general in order to support increment and decrement, and types such as non-random-access iterators could provide an `it + 1` operation without exposing a non-constant-time `it + n` operation. One potential advantage of this approach is that a generic constraint that is sufficient to allow `a = a + 1;` to type-check would also allow `++a;` to type-check. For example, `T:! Assign & Add where i32 is ImplicitAs(.Self)` would suffice to allow such an increment, as would a facet type with a narrower `ImplicitAs` constraint that permits only the literal `1`. However, this benefit is minor, given that writing `a += 1;` instead is not a major burden. The cost of making this change would be that the semantics of increment and decrement are tied to a very numerical notion of "adding 1". That may not be appropriate for all types that want to support a more general notion of "move to the next value" or "move to the previous value", such as a more generalized notion of cursor. It may also not be appropriate for all types that support addition of exactly 1 to support increment; for example: - For floating-point types, adding exactly 1 does not necessarily produce a different number, and there is a different meaningful notion of "move to the next value" -- namely, moving to the next _representable_ value -- which may be intended instead. In C++ code, where increment of floating-point types is permitted, it is vanishingly rare. - For complex numbers, for example in a Gaussian integer type, having `++c;` move one unit in the real direction seems arbitrary. - For a rational number type, as for floating-point types, adding exactly 1 seems unlikely to be a common "navigation" operation, even though there is no other reasonable notion of "move to the next value". ### Define `$` in terms of `$=` Instead of defining `$=` in terms of `$` and `=`, we could define `$` in terms of `$=` and copying. Our experience from C++ is that `$=` can frequently be implemented more efficiently than `$`, by operating in-place and reusing allocated storage from the left-hand operand, so this might be a better default. There are a few reasons why we choose to not do this: - The direction in this proposal is expected to be less surprising. Defining `$=` in terms of `$` and `=` seems more in line with programmer expectations based both on the morphology of the token and on how it is generally taught. - Under the rules in this proposal, an `Add & Assign` constraint ends up being effectively equivalent to an `AddAssign` constraint, due to the blanket implementation of `AddAssign` in terms of `Add` and `Assign`. This means that constraining a type to provide both `Add` and `Assign` is sufficient to use `+=`, which seems desirable. If the defaults were reversed, this would not be achievable. - If the reverse rule were adopted, two separate `impl`s would still be required in order to permit implicit conversions on the left hand side of the `$` for a type, but not on the left hand side of a `$=`. - There are cases where `$` can be implemented more efficiently than making a copy and performing a `$=` operation. For example, if the type is large, implementing `$` in terms of `$=` can require two passes over the destination instead of one, which may increase the constant factor performance of the operation. ### Do not allow overloading the behavior of `=` We could define that an `=` expression always carries out these steps: - Initialize a value of the left-hand operand's type from the right-hand operand. - Destroy the left-hand operand. - Move the value created earlier into the left-hand operand. However, this removes some flexibility and could harm performance, for example in the case where the left-hand operand has a buffer that it could reuse to store its new value. This would also be a significant deviation from C++, where some types take advantage of this additional flexibility, and would be expected to harm interoperability and migration. ### Treat the left hand side of `=` as a pattern We could allow pattern-like syntax on the left hand side of `=` instead of an expression. ``` fn GCD(var a: i32, var b: i32) -> i32 { if (b < a) { // Swap `a` and `b`. (a, b) = (b, a); } while (a != 0) { // Calculate both `b % a` and `a`, // then assign to both `a` and `b`. (a, b) = (b % a, a); } return b; } ``` However, this would be a novel interpretation of pattern syntax: there is no mechanism in the current pattern syntax to assign to an existing variable. Pattern-matching `(a, b)` against `(b % a, a)` would instead compare `a` to `b % a` and compare `b` to `a` in the current mechanism. It is not clear that this level of novelty is justified by the value added by this functionality. ### Different names for interfaces We considered various different names for the interfaces in this proposal. Differences from the proposed names are highlighted: - `Assign`, `AssignWith`, `OpAssign`, `OpAssignWith` (proposed) - Consistently uses `With` suffix to describe the right-hand type. - These names have a direct connection to the lexical operator syntax: the interface for `$=` is named as the interface for `$` followed by the interface for `=`. - The word order in the name describes the order in which the operations are notionally performed: first `Op`, then `Assign`. - Compound assignment interfaces will group alphabetically with the corresponding operator, rather than with assignments, which is likely to be better for people searching for items in a sorted list. - Matches the choice made in Rust. - `Assign`, **_`AssignFrom`_**, `OpAssign`, `OpAssignWith` - This reads more naturally in English. `With` isn't really the right word to use in this context, and may be confusing. - Violates the consistency of using `...With` for all the parameterized operator interfaces. - Given how common this interface is expected to be compared to the rest, the inconsistency of using `AssignFrom` might be acceptable, but for now we will use `AssignWith`. If there are sustained concerns with this name (if we don't "get used to it"), we should reconsider. - `Assign`, `AssignWith`, **_`AssignOp`_**, **_`AssignOpWith`_** - Consistently uses `With` suffix to describe the right-hand type. - The name `AssignOpWith(U)` decomposes as `Assign` + `OpWith(U)` in a way that describes the two operations being performed. - When written as function calls, the behavior is `Assign(x, Op(x, y))`, which again uses the `AssignOp` word order. - `Assign`, **_`AssignGiven`_**, `OpAssign`, **_`OpAssignGiven`_** - `Given` might be a less surprising word for simple assignment than `With`, but is still not ideal. - This choice seems slightly worse in most existing cases that use `With`, and we didn't consider the benefit of `AssignGiven` over `AssignWith` to be sufficient to justify that cost. - `Assign`, `AssignWith` or **_`AssignFrom`_**, **_`InPlaceOp`_**, **_`InPlaceOpWith`_** - Might make `AssignFrom` more viable by making `Assign` / `AssignFrom` no longer parallel `InPlaceOp` / `InPlaceOpWith` so closely. - Might better match how these operations are described in everyday parlance. Overall, the proposed set of names seem like the best choice, despite some of the names not reading completely naturally. The English readability concern is probably not much worse than for `LeftShiftWith`, where we already decided that consistency was more important. ================================================ FILE: proposals/p2550.md ================================================ # Simplified package declaration for the `Main` package [Pull request](https://github.com/carbon-language/carbon-lang/pull/2550) ## Table of contents - [Abstract](#abstract) - [Problem](#problem) - [Background](#background) - [Proposal](#proposal) - [Details](#details) - [Rationale](#rationale) - [Alternatives considered](#alternatives-considered) - [Require the use of the identifier `Main` in package declarations](#require-the-use-of-the-identifier-main-in-package-declarations) - [Permit the use of the identifier `Main` in package declarations](#permit-the-use-of-the-identifier-main-in-package-declarations) - [Distinguish file scope from package scope](#distinguish-file-scope-from-package-scope) - [Keep the package name in imports](#keep-the-package-name-in-imports) - [Make the main package be unnamed](#make-the-main-package-be-unnamed) - [Use a different name for the entry point](#use-a-different-name-for-the-entry-point) - [Use a different name for the main package](#use-a-different-name-for-the-main-package) - [Acknowledgements](#acknowledgements) ## Abstract Make the preamble of simple programs more ergonomic, by removing the `package Main` from the main package and removing the `package` declaration entirely from the main source file. Imports within a single package no longer need to, and are not permitted to, specify the package name. ## Problem Every Carbon source file is required to start with a `package` declaration: ``` package Main impl; ``` This introduces complexity and syntactic overhead to very simple programs, such as might be encountered by beginners learning Carbon. This is also inconvenient for slide code and small examples in teaching material, where the author must choose between including boilerplate or providing an example that Carbon implementations will reject. In addition, imports within a single package are required to restate the name of the package: ``` package Geometry library "Shapes" api; import Geometry library "Points"; ``` This creates additional syntactic overhead for a common operation, and misses the opportunity to visually distinguish between different categories of imports -- "our library" versus "their library". Additionally, under [#1136](https://github.com/carbon-language/carbon-lang/issues/1136), imports from the same package have different semantics than imports from a different package, so giving the two operations the same syntax seems confusing. Given that every package has an arbitrary, developer-selected identifier name, it is also unclear how to determine which package contains the entry point of the program. The name `Main` could be reserved for this, but no such decision has been made. ## Background Proposal [#107: Code and name organization](https://github.com/carbon-language/carbon-lang/pull/107/files) introduced our current `package` syntax. In issue [#1869: What is the main entry point for execution? `Main` or `Run`?](https://github.com/carbon-language/carbon-lang/issues/1869) the leads decided that the entry point of a Carbon program is named `Main.Run`. That decision is implemented by this proposal. In issue [#1136: what is the top-level scope in a source file, and what names are found there?](https://github.com/carbon-language/carbon-lang/issues/1136), the leads decided that a package's name should not be injected into the scope of its files, and that same-package `import`s should not mention the package name. Those decisions are also implemented by this proposal. Note that other decisions were also made in #1136 regarding name access (`private`) and the behavior of unqualified name lookup that are not part of this proposal. ## Proposal A Carbon program has a `Main` package, which is the package that contains the entry point of the program. The entry point is a function named `Run`. It is also possible to link Carbon libraries into programs written in other languages, and in particular, Carbon libraries can be used from C++ programs. In this case, there will be no Carbon `Run` function, and the C++ program will provide a `main` function that is used as the entry point. In the `Main` package, the package declaration does not explicitly specify a package name. The package declaration syntax becomes: - `package` _Foo_ [`library "`_Bar_`"`] \(`api` | `impl`) `;`, unchanged from #107, for a file that is part of a package other than the `Main` package. - `library "`_Bar_`"` (`api` | `impl`) `;` for a library that is part of the `Main` package. - Omitted entirely for an `impl` file in the `Main` package that is not part of a named library. There is no way to define an `api` file for the `Main` package that is not part of a named library -- there is no equivalent of `package Main api;`. The import syntax becomes: - `import` _Foo_ `;` to import the default library of package _Foo_, or `import` _Foo_ `library "`_Bar_`"` `;` to import library _Bar_. This introduces the name _Foo_ in the current source file as a name for that package, containing whatever subset of the package was imported. `Foo` is not allowed to be the name of the current package. - `import library "`_Bar_`";` to import library _Bar_ of the current package. This introduces the names from that library at file scope. _Bar_ is not allowed to be the name of the current library. - `import library default;` to import the default library of the current package. This is not allowed within the default library. It is an error to explicitly specify the package name `Main` in a package declaration or an import declaration. As a result, there is no way to import libraries of the `Main` package from any other package. It is permitted for a Carbon program to contain a source file that has no `package` declaration and does not contain a `Run` function -- or even to contain multiple such source files. Such a source file cannot implement any importable functionality, as there is no corresponding `api` file, but may still be useful to support Carbon features that have not yet been designed, such as: - Global registration mechanisms. - Some way of defining functions with well-known symbols, analogous to `extern "C"` declarations in C++. ## Details See design changes. ## Rationale - [Goal: Community and culture](/docs/project/goals.md#community-and-culture) - This change allows small complete programs and source files to be discussed in Carbon forums without the overhead of `package` declarations. Cumbersome top-level syntax hampers technical discussion. - [Goal: Language tools and ecosystem](/docs/project/goals.md#language-tools-and-ecosystem) - Test cases for language tools are made unnecessarily verbose by the mandatory inclusion of a `package` declaration and a `Main` function. This proposal makes these components optional. - [Goal: Code that is easy to read, understand, and write](/docs/project/goals.md#code-that-is-easy-to-read-understand-and-write) - Simple programs, such as `"hello, world"` examples written by beginners, have less boilerplate. - Reduction of verbosity that is not contributing to increased understanding of the program. - [Goal: Interoperability with and migration from existing C++ code](/docs/project/goals.md#interoperability-with-and-migration-from-existing-c-code) - This proposal explicitly acknowledges and permits the entry point for a program that includes Carbon code to be written in a different language. - The use of the name `Main.Run` for the entry point is intended to be similar enough to the use of the name `main` in C++ to be familiar. - [Principle: Prefer providing only one way to do a given thing](https://github.com/carbon-language/carbon-lang/blob/trunk/docs/project/principles/one_way.md) - Each `package` and `import` declaration has only one valid spelling. We do not allow redundantly specifying the current package in an `import`, nor using `library default` in any context where the `library` stanza can be omitted with the same meaning, and we do not allow explicitly writing a `package` declaration within the `Main` package. ## Alternatives considered ### Require the use of the identifier `Main` in package declarations Instead of the name `Main` being implied in `package` declarations, we could require it to be explicitly written: ``` package Main impl; import SimpleIO; fn Main() { SimpleIO.Print("Hello, Carbon!"); } ``` This would keep the Carbon language more consistent, and remove a special case. However, it would also increase verbosity in the simplest of programs, where the added verbosity has the most cost. ### Permit the use of the identifier `Main` in package declarations We could allow the name `Main` to be used in package declarations and treat it the same as if the name were omitted, making the other forms merely shorthand. However, this would introduce a syntactic choice that doesn't correspond to any difference in intent, creating the opportunity for meaningless stylistic divergence, and we [prefer to leave only one syntactic choice](https://github.com/carbon-language/carbon-lang/blob/trunk/docs/project/principles/one_way.md) in order to enforce consistency. ### Distinguish file scope from package scope Under this proposal, the names from the current package that are visible, either by being declared locally or by being imported, are visible at the top level file scope. We could pick a different rule that distinguishes package scope from file scope, such as by introducing only locally-declared names into file scope and requiring a `PackageName.` prefix on all other names in the package. This was discussed at length in [#1136](https://github.com/carbon-language/carbon-lang/issues/1136). We found that distinguishing the package from the subset of the package visible at file scope led to problems that were more substantial than the potential simplification of treating imports of the current package as being the same as imports of any other package. See that issue for the full discussion. In [#1136](https://github.com/carbon-language/carbon-lang/issues/1136), it was also decided to add a `package.Name` syntax to allow shadowed names from the package scope to be referenced. That is not part of this proposal; until we add such a mechanism, its absence can be worked around by using private aliases: ``` fn Foo(); private alias OuterFoo = Foo; class Bar { fn Foo() { // 🤷 Either ambiguous or calls `Bar.Foo`. Foo(); // Calls `Foo` from package scope. OuterFoo(); } } ``` ### Keep the package name in imports We could consistently name the package targeted by an import in the `import` declaration: ``` package Foo api; import Foo library "Bar"; ``` This would lead to a more uniform syntax and, as noted in [a very similar alternative considered by #107](/proposals/p0107.md#optional-package-names), would make it easier to search for all imports of a given library with a simple tool. There are two reasons to want to avoid this: - Use of the same syntax suggests that same-package imports and cross-package imports have the same semantics. But they do not: a same-package import introduces the set of public names in the nominated library, whereas a cross-package import of package `Bar` introduces exactly the name `Bar`. - This would require writing a name for the `Main` package, in order to allow libraries of that package to be imported within the same package. Adding that name would provide a syntax to import those libraries from another package, which we wanted to disallow. ### Make the main package be unnamed We don't allow writing the name `Main` of the main package in `package` declarations nor in `import` declarations, so we could say that the main package has no name or has the empty name. We chose to give it a specific name for a few reasons: - Conversationally and in documentation, it is clearer to talk about the main package than the unnamed package, and we expect people to call it "the main package" or similar regardless of which name we pick. - There may still be reasons we need an identifier name to refer to this package. For example, this need may arise in name mangling, in attributes, in error messages, in conventions for naming source files, and in other technical contexts that require an identifier referring to the package. One downside of picking a name is that it causes us to reserve an identifier and assign it special meaning, but we felt that using the name `Main` for any other package would be confusing, so this cost is small. Ultimately this decision was marginal, and the painter made the choice to use a named package in [#1869](https://github.com/carbon-language/carbon-lang/issues/1869). ### Use a different name for the entry point We could use a variety of different names for the entry point and for the package that contains it. `Main` was the obvious first choice, as it is the name used in C and C++. However, we generally want to use verbs and verb phrases as function names, because functions describe actions and we find that verb phrases are easier to read and understand as function names as a result. Some other function names were considered but rejected: - `Start` -- we preferred to use a word that describes the entire action of the function, and the `Main.Run` function covers the complete execution of the program, not just the beginning of that execution. - `Entry` or `Entrypoint` -- same problems as `Start`, plus less discoverable, potentially longer, not a familiar term to beginners, and not verbs. - `Exec` or `Execute` -- we were concerned about a semantic collision between the action of executing _some other_ program, as is performed by the C `exec` function, and the action of executing the current program. See [#1869](https://github.com/carbon-language/carbon-lang/issues/1869) for more information about this decision. ### Use a different name for the main package We considered other options for the main package name. The primary option considered was `Program`, but there were no decisive technical arguments to select one name over another. The painter selected `Main` with the following rough rationale: > - We weren't using `Main` for the function name, so it seemed available. > - Given that it is the "main package" and in fact contains the entry point, > it didn't seem likely to have any confusion with `main` functions in other > languages. > - It is shorter than `Program`. > - I guess that it will work better to indicate a conventional filename of > `main.carbon`. > - For folks looking for a `main` function, it may help them find our > version. See [#1869](https://github.com/carbon-language/carbon-lang/issues/1869) for more information about this decision. ## Acknowledgements Thanks to [Allison Poppe](https://github.com/acpoppe) for authoring proposal [#2265 Name of application entry point](https://github.com/carbon-language/carbon-lang/pull/2265) which explored another option for naming the entry point. ================================================ FILE: proposals/p2551.md ================================================ # Roadmap for 2023 and retrospective for 2022 [Pull request](https://github.com/carbon-language/carbon-lang/pull/2551) ## Table of contents - [Abstract](#abstract) - [Proposal](#proposal) - [Retrospective on 2022](#retrospective-on-2022) - [Broaden participation so no organization >50%](#broaden-participation-so-no-organization-50) - [Example ports of C++ libraries to Carbon (100% of woff2, 99% of RE2)](#example-ports-of-c-libraries-to-carbon-100-of-woff2-99-of-re2) - [Carbon explorer implementation of core features with test cases](#carbon-explorer-implementation-of-core-features-with-test-cases) - [Demo implementation of core features with working examples](#demo-implementation-of-core-features-with-working-examples) ## Abstract We propose a roadmap for 2023 focus on: - Progressing towards a concrete goal of an MVP / 0.1 language. - Engaging more broadly and deeply with the C++ community. We also reflect on our overly ambitious [roadmap for 2022](https://github.com/carbon-language/carbon-lang/blob/3d90a85f2439bb74b71a553c5017012369ec0f63/docs/project/roadmap.md) and how the year went. ## Proposal Our primary goals for 2023 are: - Define a concrete set of milestones for our Minimum Viable Product or MVP. - Because Carbon is an experiment, our MVP is focused on the _evaluation_ of the Carbon language, not any other usage. - We consider this MVP-for-evaluation our 0.1 language. - Complete all of the 0.1 design and feature milestones. - Complete as many of the 0.1 implementation milestones as we can. - Realistically, we don't expect to finish all of them in 2023. - Begin actively engaging and sharing Carbon's design and ideas with the C++ community. See our [updated roadmap](/docs/project/roadmap.md) for more details and how we expect to measure our success. ## Retrospective on 2022 Our [roadmap for 2022](https://github.com/carbon-language/carbon-lang/blob/3d90a85f2439bb74b71a553c5017012369ec0f63/docs/project/roadmap.md) was in retrospect wildly optimistic. We're sorry about that, and are going to work to set more realistic goals and milestones going forward. That said, we still achieved a tremendous amount and it's useful to look in some detail at how everything went. We had two primary goals for 2022 and somewhat split the difference between them: - Make the Carbon experiment public: **100%** as we [announced](https://youtu.be/omrY53kbVoA) Carbon publicly in July at [CppNorth](https://cppnorth.ca/)! - Complete the language design: lots of progress, but nowhere near the finish line here. This ended up also being poorly defined in some cases, which we're going to try to address going forward. We can also measure the key results we had in mind with more precision. ### Broaden participation so no organization >50% > Our goal is that no single organization makes up >50% of participation in the > Carbon project, to ensure that we are including as broad and representative a > set of perspectives in the evolution of Carbon as possible. As a proxy for the > amount of participation, we will count the number of active participants from > each organization in 2022, with the aim that each organization is represented > by less than 50% of all active participants. The simplest participation measures are from commits to the repository over 2022 which shows 92 contributors over the year, and definitely fewer than 46 of those from a single organization. But it's hard to consider a single typo fix in July as being an _active_ participant. Some other measures: - During the last quarter of 2022 we had 14 authors contributing to patches, and likely the largest single organization was only 5 of them. - We had 171 issue authors this year, and 43 filing more than one issue. Well below 50% from any single organization. - We had 258 issue commenters, and 64 making over 4 comments over the course of the year, both _far_ below 50% from a single organization. - Looking at the weekly meetings in August, September, and October, we had just under 50% of the meetings with less than 50% of attendees from a single organization. Largely, we feel we hit this goal solidly. However, we still see specific areas where we need to broaden participation, for example: - Language proposal authors (as opposed to other kinds of PRs and commits). - Design discussion and meeting participation. Some of this will likely need Carbon to make substantial progress beyond experimentation in order to have more organizations devote the significant resources that can be necessary for more in-depth participation. ### Example ports of C++ libraries to Carbon (100% of woff2, 99% of RE2) We ended up de-prioritizing this entirely so we could focus on the design and moving the project public. ### Carbon explorer implementation of core features with test cases Like the top-level goals for 2022, this specific result was much too ambitious. However, we have made tremendous progress on the design and the Carbon Explorer. For example, we have a [detailed mapping](https://github.com/carbon-language/carbon-lang/wiki/Are-we-explorer-yet%3F) of the implementation status of the designed features, with approximately 50% of these implemented. The Carbon Explorer is also now integrated into the amazing [compiler explorer](https://carbon.godbolt.org/)! This largely addresses the core of the "repl" and experimentation access goals, and our focus has otherwise been on completing the implementation. ### Demo implementation of core features with working examples We ended up prioritizing the Carbon Explorer over the toolchain in 2022 in order to have an easier path to a minimal demo implementation. However, we did begin fleshing out more of the toolchain implementation that will eventually be used to compile Carbon into working binaries, and it has started to provide the first pieces of semantic analysis along with a much improved parser for Carbon. ================================================ FILE: proposals/p2665.md ================================================ # Semicolons terminate statements [Pull request](https://github.com/carbon-language/carbon-lang/pull/2665) ## Table of contents - [Abstract](#abstract) - [Problem](#problem) - [Background](#background) - [Discussion in Carbon](#discussion-in-carbon) - [In other languages](#in-other-languages) - [Requiring semicolons](#requiring-semicolons) - [Optional semicolons](#optional-semicolons) - [Proposal](#proposal) - [Rationale](#rationale) - [Alternatives considered](#alternatives-considered) - [Optional semicolons](#optional-semicolons-1) ## Abstract Statements, declarations, and definitions will terminate with either a semicolon (`;`) or a close curly brace (`}`). Semicolons are never optional. For example, with a semicolon, `x = x + 2;` or `class C;`. With a close curly brace, `for ( ... ) { ... }`, or `class C { ...}`. This does not affect any approved proposal; rather, it makes an important assumption explicit. ## Problem Statements need some system for separation. There are two main options for this: 1. Require semicolons to terminate statements. 2. Automatically determine where statements terminate. - Some languages, such as Python, define a syntax where a newline terminates statements. - Other languages, such as Javascript, require semicolons but define rules for semicolon insertion. Although Carbon's design currently assumes semicolons are required, it hasn't been directly addressed by a proposal. ## Background ### Discussion in Carbon This was discussed on leads issue [#1924: Semicolon](https://github.com/carbon-language/carbon-lang/issues/1924). Some rationale is provided there, stemming from discussion [#1739: Semicolon](https://github.com/carbon-language/carbon-lang/discussions/1739). ### In other languages [This blog](https://pling.jondgoodwin.com/post/semicolon-inference/) provides a similar survey of multiple languages. #### Requiring semicolons In C++, C#, and Java, semicolons are always required. In Rust, semicolons are generally required, but may be omitted for an [implicit return](https://doc.rust-lang.org/std/keyword.return.html). Because [blocks are expressions](https://doc.rust-lang.org/reference/expressions/block-expr.html), there are [ambiguities in expression statements](https://doc.rust-lang.org/reference/statements.html#expression-statements) between parsing as a standalone statement and parsing as part of an expression. #### Optional semicolons In Python, a line is a [simple statement](https://docs.python.org/3/reference/simple_stmts.html), and parentheses are an idiomatic way to create multi-line statements. Semicolons may be used to explicitly separate statements. For example: ```python value = ( "text" ) a = 1; b = 2; c = 3 ``` Swift allows some statements to wrap lines, although multiple statements on the same line (`x = 1 x = 1`) require a semicolon. The detailed rules aren't documented so it's difficult to assess other than that Swift developers are generally happy with the results. Swift's [statements section](https://docs.swift.org/swift-book/documentation/the-swift-programming-language/statements) doesn't define statement boundaries, and the [grammar](https://docs.swift.org/swift-book/documentation/the-swift-programming-language/summaryofthegrammar/) documents that line-breaks are treated as whitespace. However, there are observable ways the behavior can lead to small mistakes; these may may often be caught by the compiler, but will sometimes be missed. For example: ```swift // One statement in Swift, but two in Python and Kotlin. var x = 1 + 1 // Two statements in Swift because of whitespace sensitivity. Second statement // is a compiler warning. var x = 1 +1 // Two calls, the second on the return value of the first. Make() () // A single call followed by an empty tuple. Second statement is valid. Make() () ``` Kotlin permits a newline to be used to terminate statements instead of a semicolon. Kotlin's grammar [explicitly enumerates](https://kotlinlang.org/spec/syntax-and-grammar.html) all the places where newlines can appear (see mentions of `NL` in the grammar), and doesn't allow newlines in places where they would introduce ambiguity. ```kotlin // This is unambiguously parsed as two statements, because // a newline is not permitted before a `+` operator. var x = 1 + 1 ``` In JavaScript and TypeScript, semicolons are part of the formal syntax, and ECMAScript provides [Automatic Semicolon Insertion (ASI)](https://tc39.es/ecma262/#sec-automatic-semicolon-insertion). Note ECMAScript also documents [Interesting Cases](https://tc39.es/ecma262/#sec-interesting-cases-of-automatic-semicolon-insertion) which may lead to confusion for developers. In Go, semicolons are similarly part of the formal syntax, and [certain tokens cause a semicolon insertion](https://go.dev/ref/spec#Semicolons). This is also used to enforce style, for example by requiring the opening `{` of an `if` body to be on the same line in order to avoid semicolon insertion. ## Proposal As described in the abstract, Carbon will require semicolons to terminate statements and forward declarations. Examples with a semicolon include: - Most statements, such as `Foo();` and `x = x + 2;`. - `var` statements and declarations, such as `var x: i32 = 0;` - Forward declarations, such as `class C;` or `fn Foo();`. Examples with a close curly brace include: - Statement grammars that terminate with a curly brace, such as `if ( ... ) { ... }` or `match ( ... ) { ... }`. - Declarations that include a definition, such as `class C { ... }` or `fn Foo() { ... }`. - This is partly in contrast with C++, which would requires a semicolon in `class C { ... };`. Carbon's current design has been written assuming the above; this is making requiring semicolons an explicit decision. ## Rationale - [Language tools and ecosystem](/docs/project/goals.md#language-tools-and-ecosystem) - We expect it to be easier to write tools that parse and operate on source code if semicolons are required. - [Software and language evolution](/docs/project/goals.md#software-and-language-evolution) - Requiring semicolons leaves open the most evolutionary paths; any optional semicolon approach means the design would need to be more thoughtful about handling ambiguities. - [Code that is easy to read, understand, and write](/docs/project/goals.md#code-that-is-easy-to-read-understand-and-write) - Semicolons are a [visual aid](/docs/project/principles/low_context_sensitivity.md#visual-aids) that reinforces statement termination, even though they might be viewed as a nuisance to write or visually unnecessary for some developers. - Carbon weighs readability more heavily because of the expectation that code will be read more often. - [Interoperability with and migration from existing C++ code](/docs/project/goals.md#interoperability-with-and-migration-from-existing-c-code) - The use of semicolons is expected to improve familiarity for C++ developers, even for developers who might prefer optional semicolons. ## Alternatives considered ### Optional semicolons Semicolons could be made optional. This would most likely be with an approach similar to Python, based mainly on newlines. Advantages: - Languages with optional semicolons are very popular. Python is either the most, or the 2nd most, widely used programming language by most measures ([1](https://pypl.github.io/PYPL.html) [2](https://octoverse.github.com/2022/top-programming-languages) [3](https://www.tiobe.com/tiobe-index/)). - Echoes the direction of evolution in other languages. - For example, Swift and Kotlin are recently designed languages that make semicolons optional in ways that work well for developers in practice. - Compile-time validation and errors on no-op statements could be used to detect some of the issues that arise with optional semicolons in Python and JavaScript. - For example, TypeScript may improve the handling of ASI ambiguities by [increasing detectability of mistakes](https://medium.com/@eugenkiss/dont-use-semicolons-in-typescript-474ccfe4bdb3). - While optional semicolons seem to get fewer complaints, requiring semicolons is likely to lead to ongoing friction due to the overall trend. This can be seen for languages like Rust ([1](https://github.com/rust-lang/rust/issues/27116) [2](https://internals.rust-lang.org/t/make-some-separators-optional/4846) [3](https://github.com/rust-lang/rfcs/issues/2583) [4](https://users.rust-lang.org/t/why-semicolons/25074)) or C# ([1](https://github.com/dotnet/roslyn/issues/5355) [2](https://github.com/dotnet/csharplang/discussions/496) [3](https://github.com/dotnet/csharplang/discussions/5655)). Disadvantages: - Semicolons are a visual anchor for statement termination when scanning code. - Requiring semicolons leaves more evolutionary paths available for Carbon. This includes both syntactic changes without introducing ambiguity and implicit returns as in Rust. - Although it's not clear Carbon will fully adopt implicit returns, similar syntactic choices may arise for lambdas. - Semicolons are a signal to the compiler about where statements were intended to terminate, and can be used to provide better error detection as a consequence. - For contrast, optional semicolons may lead to unintended statements. While ASI's problems are [documented](https://tc39.es/ecma262/#sec-automatic-semicolon-insertion), we expect any optional semicolon approach will lead to some increase in bugs that the compiler cannot detect, if only because fewer mistakes are necessary in order to produce valid but incorrect code. - Making code with no semicolons idiomatic may increase the "strangeness" for C++ developers, who are the primary target for Carbon. Semicolons are expected to be a net benefit, as explained by the [rationale](#rationale). ================================================ FILE: proposals/p2687.md ================================================ # Termination algorithm for impl selection [Pull request](https://github.com/carbon-language/carbon-lang/pull/2687) ## Table of contents - [Abstract](#abstract) - [Problem](#problem) - [Background](#background) - [Proposal](#proposal) - [Details](#details) - [Non-type arguments](#non-type-arguments) - [Proof of termination](#proof-of-termination) - [Rationale](#rationale) - [Alternatives considered](#alternatives-considered) - [Measure complexity using type tree depth](#measure-complexity-using-type-tree-depth) - [Consider each type parameter in an `impl` declaration separately](#consider-each-type-parameter-in-an-impl-declaration-separately) - [Consider types in the interface being implemented as distinct](#consider-types-in-the-interface-being-implemented-as-distinct) - [Require some count to decrease](#require-some-count-to-decrease) - [Require non-type values to stay the same](#require-non-type-values-to-stay-the-same) ## Abstract This proposal replaces the termination algorithm for `impl` selection. The previous algorithm relied on a recursion limit, which is counter to [our goal for predictability](/docs/design/generics/goals.md#predictability). The replacement is to terminate if any `impl` lookup performed while considering an `impl` declaration depends transitively on the same `impl` declaration with a "strict superset" of the types in the query. ## Problem Consider this `impl` declaration: ``` interface I; impl forall [T:! type where Optional(.Self) is I] T as I; ``` A type like `i32` is a valid value of `T`, and so implements `I`, if `Optional(i32)` implements `I`. This `impl` declaration could also possibly be used to give an implementation of `Optional(i32)` for interface `I`, but only if there was an implementation of `Optional(Optional(i32))` for interface `I`. The job of the termination rule is to report an error instead of being caught in an infinite loop in this situation. Ideally, a termination rule would identify the loop in a minimal way. This has a few benefits, including reducing compile times and making error messages as short and understandable as possible. One downside of the original recursion limit rule is its tendency to only detect a problem after the loop had been repeated many times. This problem is worse if the recursion limit is large. Another concern with using a recursion limit is that refactorings that would otherwise be legal can increase the depth of recursion, causing spurious failures. The workaround for this and other spurious failures is to increase the recursion limit. This makes the other problems with using a recursion limit worse. Note that determining whether a particular set of `impl` declarations terminates is [equivalent to the halting problem](https://sdleffler.github.io/RustTypeSystemTuringComplete/) (content warning: contains many instances of an obscene word as part of a programming language name), and so is undecidable in general. So any termination rule will have some false positives or spurious failures, where it reports an error even though it would in fact complete if allowed to continue running. We would like a criteria that correctly classifies the examples that arise in practice. ## Background The first termination rule was introduced in proposal [#920: Generic parameterized impls (details 5)](https://github.com/carbon-language/carbon-lang/pull/920), following Rust and C++. The problems with using a recursion limit were [recognized at the time that proposal was written](https://github.com/carbon-language/carbon-lang/blob/f282bca20e41e2f8dc05881d9d6b38213d6c6c87/docs/design/generics/details.md#termination-rule), but no alternative was known. Alternatives termination rules have since been discussed: - in open discussion on [2022-04-13](https://docs.google.com/document/d/1tEt4iM6vfcY0O0DG0uOEMIbaXcZXlNREc2ChNiEtn_w/edit#heading=h.cja3fkwzv9tr), prompted by a question on [#1088: Generic details 10: interface-implemented requirements](https://github.com/carbon-language/carbon-lang/pull/1088); and - in issue [#2458: Infinite recursion during impl selection](https://github.com/carbon-language/carbon-lang/issues/2458), which includes summaries of discussions including those on [2023-02-07](https://docs.google.com/document/d/1gnJBTfY81fZYvI_QXjwKk1uQHYBNHGqRLI2BS_cYYNQ/edit?resourcekey=0-ql1Q1WvTcDvhycf8LbA9DQ#heading=h.9u2u6078figt). PR [#2602: Implement the termination algorithm for impl selection described in #2458](https://github.com/carbon-language/carbon-lang/pull/2602) implements the termination rule of this proposal in Explorer. ## Proposal We replace the termination criteria with a rule that the types in the `impl` query must never get strictly more complicated when considering the same `impl` declaration again. The way we measure the complexity of a set of types is by counting how many of each base type appears. A base type is the name of a type without its parameters. For example, the base types in this query `Pair(Optional(i32), bool) impls AddWith(Optional(i32))` are: - `Pair` - `Optional` twice - `i32` twice - `bool` - `AddWith` A query is strictly more complicated if at least one count increases, and no count decreases. So `Optional(Optional(i32))` is strictly more complicated than `Optional(i32)` but not strictly more complicated than `Optional(bool)`. This rule, when combined with [the acyclic rule](/docs/design/generics/details.md#acyclic-rule) that a query can't repeat exactly, [guarantees termination](#proof-of-termination). This rule is expected to identify a problematic sequence of `impl` declaration instantiations in a way that is easier for the user to understand. Consider the example from before, ``` interface I; impl forall [T:! type where Optional(.Self) is I] T as I; ``` This `impl` declaration matches the query `i32 impls I` as long as `Optional(i32) impls I`. That is a strictly more complicated query, though, since it contains all the base types of the starting query (`i32` and `I`), plus one more (`Optional`). As a result, an error can be given after one step, rather than after hitting a large recursion limit. And that error can state explicitly what went wrong: we went from a query with no `Optional` to one with one, without anything else decreasing. Note this only triggers a failure when the same `impl` declaration is considered with the strictly more complicated query. For example, if the declaration is not considered since there is a more specialized `impl` declaration that is preferred by the [type-structure overlap rule](/docs/design/generics/details.md#overlap-rule), as in: ``` impl forall [T:! type where Optional(.Self) is I] T as I; impl Optional(bool) as I; // OK, because we never consider the first `impl` // declaration when looking for `Optional(bool) impls I`. let U:! I = bool; // Error: cycle with `i32 impls I` depending on // `Optional(i32) impls I`, using the same `impl` // declaration, as before. let V:! I = i32; ``` The rule is also robust in the face of refactoring: - It does not depend on the specifics of how an `impl` declaration is parameterized, only on the query. - It does not depend on the length of the chain of queries. - It does not depend on a measure of type-expression complexity, like depth. ## Details ### Non-type arguments For non-type arguments we have to expand beyond base types to consider other kinds of keys. These other keys are in a separate namespace from base types. - Values with an integral type use the name of the type as the key and the absolute value as a count. This means integer arguments are considered more complicated if they increase in absolute value. For example, if the values `2` and `-3` are used as arguments to parameters with type `i32`, then the `i32` key will have count `5`. - Every option of a choice type is its own key, counting how many times a value using that option occurs. Any parameters to the option are recorded as separate keys. For example, the `Optional(i32)` value of `.Some(7)` is recorded as keys `.Some` (with a count of `1`) and `i32` (with a count of `7`). - Yet another namespace of keys is used to track counts of variadic arguments, under the base type. This is to defend against having a variadic type `V` that takes any number of `i32` arguments, with an infinite set of distinct instantiations: `V(0)`, `V(0, 0)`, `V(0, 0, 0)`, ... - A `tuple` key in this namespace is used to track the total number of components of tuple values. The values of those elements will be tracked using their own keys. Non-type argument values not covered by these cases are deleted from the query entirely for purposes of the termination algorithm. This requires that two queries that only differ by non-type arguments are considered identical and therefore are rejected by the acyclic rule. Otherwise, we could construct an infinite family of non-type argument values that could be used to avoid termination. ### Proof of termination Let's call a (finite or infinite) sequence of type expressions "good" if no later element is strictly more complex than an earlier element, and no type expression is repeated. We would like to prove that any good sequence of type expressions with a finite set of keys is finite. We can restrict to good sequences that don't repeat any multiset of keys, since there are only a finite number of types with a given multiset of keys. Proof: If none of the types have a variadic parameter list, then there is at most one type for every distinct permutation of base types. If some types are variadic, then we can get a conservative finite upper bound by multiplying the number of distinct permutations by the number of different possible arity combinations. The number of arity combinations is finite since, ignoring non-type arguments, the total arity must equal the number of base types in the type minus 1. The proof of termination is by induction on the number `N` of distinct keys. - If `N == 1`, then types map to a multiset of a single key, which can be represented by the count of the number of times that key appears. That number must be non-negative and decreasing in the sequence, and so the length of the sequence is bounded by the value of the first element. So good sequences with `N == 1` must be finite. - Assuming that good sequences with `N` distinct keys must be finite, consider a good sequence with `N+1` distinct keys. Its first element will be represented by a non-negative integer `(N+1)`-tuple, `(i_0, i_1, ..., i_N)`. Every element after that will be in at least one of the `i_0 + i_1 + ... + i_N` hyperplanes (co-dimension 1) given by these equations: - `x_0 = 0`, `x_0 = 1`, ..., `x_0 = i_0 - 1` (`i_0` different equations, each defining a separate hyperplane) - `x_1 = 0`, `x_1 = 1`, ..., `x_1 = i_1 - 1` - ... - `x_N = 0`, `x_N = 1`, ..., `x_N = i_N - 1` - Any point not in one of those hyperplanes has components all >= the first element, and so can't be in the sequence if it is good. - The restriction of the sequence to the subsequence in each of those hyperplanes is finite, by the induction hypothesis. - The sequence visits points in this finite union of finite sets without repetition, and so must be finite. - Conclusion: Any good sequence with `N+1` distinct keys is finite, completing the induction. This bound given by this construction is not completely tight, since there is overlap between the hyperplanes. It is tight once that overlap is taken into account, though. We can construct sequences that reach the upper bound by visiting the points in the union of the hyperplanes in descending order of their L1-norm (sum of the components). Note: The text of this argument was derived from comments on [issue #2458: Infinite recursion during impl selection](https://github.com/carbon-language/carbon-lang/issues/2458). ## Rationale This proposal advances these [Carbon goals](/docs/project/goals.md): - [Language tools and ecosystem](/docs/project/goals.md#language-tools-and-ecosystem) by improving the quality of diagnostics. - [Software and language evolution](/docs/project/goals.md#software-and-language-evolution) since we've chosen alternatives that avoid introducing failures as the result of refactorings, particularly those outside the files changed in the refactoring. - [Code that is easy to read, understand, and write](/docs/project/goals.md#code-that-is-easy-to-read-understand-and-write) by having a relatively simple language rule, that is predictable when authoring code, and allows independent modules to compose without triggering errors. ## Alternatives considered ### Measure complexity using type tree depth We [considered](https://github.com/carbon-language/carbon-lang/issues/2458#issuecomment-1371412985) a rule which would ensure termination by forbidding the depth of the type tree in the query from increasing. This depth could either be measured in the query or in the values of types used to parameterize `impl` declaration. Either way, this raises a concern that otherwise safe refactorings might trigger spurious termination errors. Specifically, refactorings that replace a type, like `String`, with an alias to a parameterized type, like `BasicString(Char8)`, could change the tree depths in `impl` declarations in files that were not part of the refactoring. ### Consider each type parameter in an `impl` declaration separately Instead of measuring the complexity of the `impl` query as a whole, we considered measuring the complexity of the argument values of parameters in an individual `impl` declaration. The advantage of this would be fewer spurious failures due to the termination rule. We decided against it because it is a more complex rule and it is sensitive to the specifics of how `impl` declarations are parameterized. This raises concerns about refactorings introducing termination rule failures. We did not want to incorporate this change without evidence that those spurious failures would be a problem in practice. ### Consider types in the interface being implemented as distinct Instead of measuring the complexity of the entire `impl` query together, we could consider keys in the type and interface parts of the query to be in distinct namespaces. This would reduce the spurious failures due to the termination rule, but not as much as the previous alternative. It avoids the problem of the previous alternative, since it is not sensitive to the specifics of how `impl` declarations are parameterized. We are not choosing this alternative now since it is a more complicated rule to explain. But we would consider this alternative in the future if we find it beneficial in practice to support the additional cases this rule permits. ### Require some count to decrease We considered a rule that would forbid repeating the multiset of types. This would simplify the termination argument. However, we thought it important to support `impl` declarations that effectively shuffled the terms around into some canonical form, as in this example: ``` impl forall [T:! I(Optional(.Self))] Optional(T) as I(T); ``` Here, `Optional(bool) impls I(bool)` if `bool impls I(Optional(bool))`. This rule can only be applied a finite number of times, and is something we imagine might arise naturally, so it seemed good to support. ### Require non-type values to stay the same We considered different handling for [non-type argument values](#non-type-arguments) that did not have an integral or choice type. The alternative rule required the value to stay constant. This led to [a number of edge cases](https://github.com/carbon-language/carbon-lang/pull/2687#discussion_r1151028867) to consider, like how to identify the same argument when the type constructor may be called a different number of times. The rule we chose to use instead has the advantages of being simpler and also accepting more cases. With the current rule, the value of those arguments may change freely, they just don't create different type expressions for purposes of detecting termination. ================================================ FILE: proposals/p2759.md ================================================ # Defining the 0.1 language [Pull request](https://github.com/carbon-language/carbon-lang/pull/2759) ## Table of contents - [Abstract](#abstract) - [Problem](#problem) - [Background](#background) - [Proposal](#proposal) - [Details](#details) - [Rationale](#rationale) - [Alternatives considered](#alternatives-considered) - [Narrowing the proposed milestone definitions to just 0.1 initially.](#narrowing-the-proposed-milestone-definitions-to-just-01-initially) - [Make a more incremental, less ambitious initial milestone.](#make-a-more-incremental-less-ambitious-initial-milestone) - [Skip the 0.1 milestone and aim for feature completeness.](#skip-the-01-milestone-and-aim-for-feature-completeness) ## Abstract Provides a clear definition of our goals for the 0.1 Carbon language, and a concrete feature-set that is expected to satisfy these goals. The high level goal proposed for the 0.1 language is to reach an evaluation-MVP (Minimal Viable Product): it should be sufficiently complete to evaluate its suitability specifically with respect to fitness as a C++ successor language. The features proposed for 0.1 language in turn focus on C++ interoperability and a minimal subset of foundational aspects of the language. Beyond the language itself, the other project features and milestones proposed focus on enabling evaluation of the language design and interoperating with C++ in practice. ## Problem The initial set of concrete goals for the Carbon project have focused on establishing the project itself (governance, evolution, community, infrastructure, etc.) and exploratory work on both the language design and implementation. The project is now reasonably healthy and executing effectively in the open, and we've made enough exploratory progress on both design and implementation that we need to pick a much more specific target to prioritize further work effectively. The obvious next goal, as outlined in our 2023 roadmap, is to prepare Carbon for detailed and in-depth evaluation by potential users to see if it could potentially meet their needs for a C++ successor language. While some aspects of our potential value proposition can be considered abstractly, many of the hard questions around "will it work with my code?" and "how well will it work?" need direct evaluation to gain confidence. We need to prioritize a specific set of features and milestones that are sufficient for this evaluation. Once this 0.1 language is "done" we can start advocating for in-depth evaluation. ## Background - [Carbon's 2023 roadmap](https://github.com/carbon-language/carbon-lang/blob/trunk/docs/project/roadmap.md) ## Proposal We suggest a set of initial milestones for the Carbon project with version numbers to make them easy to talk about. For each milestone, we try to outline what the theme is, provide some concrete goals that should be addressed, and a list of concrete features we expect to be complete. While the proposal only suggests three milestones initially, it isn't precluding adding additional milestones when and where they make sense as the project progresses. This proposal is suggesting a concrete and near-complete set of features for the first milestone. While this list may be updated based on new information, we are reasonably confident in its completeness. However, subsequent milestones are intentionally left incomplete and open-ended. They will be filled in as they get closer. The intent in having them here is to allow us to explicitly defer things to later milestones without leaving them out entirely in cases where there is specific interest in knowing when a major feature is expected. **Summary of proposed milestones:** - "0.1" -- the MVP (Minimum Viable Product) language for the _evaluation_ of Carbon by C++ community and developers. - "0.2" -- feature complete for initial users to finish their evaluation and the project to conclude its experiment - "1.0" -- the result of a successful experiment and production-ready One important detail is that a core goal of Carbon is to be a language prepared for on-going evolution. This means that even if Carbon gets to 1.0, that isn't expected to be a _final_ version in any sense, nor is any version expected to be final. Carbon should continue to evolve and develop going forward, and a comprehensive plan for that is [part of our later milestones](/docs/project/milestones.md#features-explicitly-deferred-beyond-02). ## Details This proposal adds a [milestones](/docs/project/milestones.md) document to the Carbon project with the full details of the initial milestones. ## Rationale This proposal is advancing Carbon's goal for the community and culture, and on-going language evolution: - [Community and culture](/docs/project/goals.md#community-and-culture) - We need to engage the Carbon community and the broader C++ community in evaluating the Carbon experiment, and this proposal identifies concrete steps we expect to take in order to ensure we can do this. - [Software and language evolution](/docs/project/goals.md#software-and-language-evolution) - Clearly define milestones help set direction and make language evolution effective. These milestones will provide that directional clarity for Carbon and markers where we can measure how effectively evolution is proceeding. ## Alternatives considered ### Narrowing the proposed milestone definitions to just 0.1 initially. While this would narrow the scope of the proposal and remove some of the more vague aspects, it would make it difficult for readers to understand when something is missing entirely versus when it is merely deferred for a later milestone. We also expect that explicitly deferring things in this way will make it easier to focus our energy and efforts on the next milestone by avoiding distractions of features that _might_ be interesting absent that deferral. ### Make a more incremental, less ambitious initial milestone. The initial milestone currently proposed is relatively ambitious, and much larger than most programming language MVPs. Carbon could have a much less aggressive initial milestone and hit it both sooner and with higher confidence. Unfortunately, a more incremental milestone doesn't seem to add much value due to the inherent goal of evaluating Carbon _in the context of C++ as it exists_. That context forces us to have a much larger initial feature set in order to not end up in somewhat of an "apples versus oranges" comparison where the feature sets are so different as to thwart any attempt at in-depth comparison and evaluation. ### Skip the 0.1 milestone and aim for feature completeness. We could have fewer milestones overall and aim for the more ambitious goal. We know that 0.1 will be insufficient to finish most real evaluations of the Carbon experiment, and we might waste time putting it out and then iterating towards 0.2. However, in general software and project planning is not hindered by having more incremental milestones. If anything, having more incremental milestones is often useful for staying focused and making rapid progress. Moreover, we expect to be able to parallelize a reasonably large amount of time during the initial evaluation with completing 0.2. By putting 0.1 out first we hope to significantly reduce the latency on getting initial feedback from users on whether the Carbon experiment is making sense for their use. ================================================ FILE: proposals/p2760.md ================================================ # Consistent `class` and `interface` syntax [Pull request](https://github.com/carbon-language/carbon-lang/pull/2760) ## Table of contents - [Abstract](#abstract) - [Problem](#problem) - [Background](#background) - [Proposal](#proposal) - [Details](#details) - [Class inheritance](#class-inheritance) - [Class implementing an interface](#class-implementing-an-interface) - [Class conditional implementation](#class-conditional-implementation) - [Adapters](#adapters) - [Interfaces](#interfaces) - [Future work: mixins](#future-work-mixins) - [Rationale](#rationale) - [Alternatives considered](#alternatives-considered) - [Use `extends` instead of `extend`](#use-extends-instead-of-extend) - [Allow interfaces to `require` another interface without writing `Self impls`](#allow-interfaces-to-require-another-interface-without-writing-self-impls) - [Allow other kinds of `where` clauses after `require`](#allow-other-kinds-of-where-clauses-after-require) - [Continue to use `impl as` for interface requirements](#continue-to-use-impl-as-for-interface-requirements) - [Continue to use `adapter` or `adaptor` instead of `adapt`](#continue-to-use-adapter-or-adaptor-instead-of-adapt) - [Use some other syntax for extending adapters](#use-some-other-syntax-for-extending-adapters) - [Continue to have some term for "external" or non-extended implementations](#continue-to-have-some-term-for-external-or-non-extended-implementations) - [More direct support for conditionally implementing internal interfaces](#more-direct-support-for-conditionally-implementing-internal-interfaces) - [Use `external` consistently instead of `extend`](#use-external-consistently-instead-of-extend) - [Use `mix` keyword for mixins](#use-mix-keyword-for-mixins) - [Allow more control over access to mixins](#allow-more-control-over-access-to-mixins) - [List base class in class declaration](#list-base-class-in-class-declaration) ## Abstract Update syntax of `class` and `interface` definitions to be more consistent. Constructs that add names to the class or interface from another definition are always prefixed by the `extend` keyword. Implements the decisions in: - [#995: Generics external impl versus extends](https://github.com/carbon-language/carbon-lang/issues/995), - [#1159: adaptor versus adapter may be harder to spell than we'd like](https://github.com/carbon-language/carbon-lang/issues/1159), - [#2580: How should Carbon handle conditionally implemented internal interfaces](https://github.com/carbon-language/carbon-lang/issues/2580), and - [#2770: Terminology for internal and external implementations](https://github.com/carbon-language/carbon-lang/issues/2770). ## Problem Classes and adapters, prior to this proposal, use `impl` to say that an interface is [implemented internally](https://github.com/carbon-language/carbon-lang/blob/trunk/docs/design/generics/terminology.md#internal-impl), which means that the names that are members of the interface are included as names of the class. The keyword `external` is added to indicate the names should not be included. Interfaces and named constraints, in contrast, use `impl` to mean another interface is required, but its names are not included. Instead, to include the names, the `extends` keyword used instead of `impl`. | Include names: | Yes | No | | ------------------------- | --------- | --------------- | | `class`, `adapter` | `impl` | `external impl` | | `interface`, `constraint` | `extends` | `impl` | In the time since this syntax has been introduced, we have found `external` in particular easy to accidentally omit. In addition to resolving this inconsistency, it would be an advantage if readers of a class could quickly scan the definition to identify other places to look for members that contribute to the class' API. ## Background These proposals that defined the syntax for these entities that are being modified: - [#553: Generics details part 1](https://github.com/carbon-language/carbon-lang/pull/553) defined the syntax for classes implementing interfaces, internally or externally, and the syntax for named constraints (then "structural interfaces") and interfaces requiring or extending other interfaces. - [#731: Generics details 2: adapters, associated types, parameterized interfaces](https://github.com/carbon-language/carbon-lang/pull/731) defined the syntax for adapters and extending adapters. - [#1084: Generics details 9: forward declarations](https://github.com/carbon-language/carbon-lang/pull/1084) allowed forward declaration of implementations, so internal `impl` declarations may appear outside of a class definition, and `external impl` declarations may appear inside. - [#777: Inheritance](https://github.com/carbon-language/carbon-lang/pull/777) defined the syntax for a class to extend a base class. No proposal so far has defined how forward declarations work for classes. The rule used for forward `interface`, `constraint`, and `impl` declarations is that the declaration part of the definition is everything up to the opening `{` of the definition body. See [the forward declaration section of the Generics details design doc](https://github.com/carbon-language/carbon-lang/blob/trunk/docs/design/generics/details.md#forward-declarations-and-cyclic-references) added in proposal [#1084: Generics details 9: forward declarations](https://github.com/carbon-language/carbon-lang/pull/1084). This proposal incorporates the decisions made in these question-for-leads issues: - [#995: Generics external impl versus extends](https://github.com/carbon-language/carbon-lang/issues/995), - [#1159: adaptor versus adapter may be harder to spell than we'd like](https://github.com/carbon-language/carbon-lang/issues/1159), - [#2580: How should Carbon handle conditionally implemented internal interfaces](https://github.com/carbon-language/carbon-lang/issues/2580), and - [#2770: Terminology for internal and external implementations](https://github.com/carbon-language/carbon-lang/issues/2770). Some of thinking around the resolution of #995 was documented in [issue #2293: reconsider syntax for internal / external implementation of interfaces](https://github.com/carbon-language/carbon-lang/issues/2293), which was closed as a duplicate of #995. In addition to modifying syntax from previous proposals, [#995](https://github.com/carbon-language/carbon-lang/issues/995) also gives a syntax for using a mixin in a class. Mixins are described as a use case in [#561: Basic classes: use cases, struct literals, struct types, and future work](https://github.com/carbon-language/carbon-lang/pull/561), but have not been added in any proposal. Question-for-leads issue [#1000: Mixins: base classes or data members?](https://github.com/carbon-language/carbon-lang/issues/1000), does state that a class will treat a mixin syntactically like a data member instead of a base class. ## Proposal Any declaration that adds the names from another entity shall start with the (new) `extend` keyword. This includes: - _Inheritance_: A class now indicates that it inherits from a base class using an > `extend base:` _base-class_ `;` declaration inside the class definition. The `extend` keyword indicates that the API of the base class is included. - _Adapters_: Adapter types are now declared as a class, with an > [ `extend` ] `adapt` _adapted-class_ `;` declaration inside the definition, as an alternative to a base class declaration. The optional `extend` keyword controls whether the API of the adapted class is included. - _Implementations_: Internal implementations are marked with the `extend` keyword on the declaration inside the class. Only the declaration inside the class, which is required for internal implementations, uses the `extend` keyword. External implementations are not marked. > [ `extend` ] `impl` ... - _Interfaces_: The `extends` declaration in an interface definition is replaced by an `extend` declaration, with no change except removing the `s` from the end of the keyword. Other interface requirements are now written using a `require` declaration, with a constraint that matches a `where` clause. This means > `impl as` _required-interface_ `;` will now be written as > `require Self impls` _required-interface_ `;` and > `impl` _type-expression_ `as` _required-interface_ `;` will now be written as > `require` _type-expression_ `impls` _required-interface_ `;` For now, only the `impls` forms of `where` clauses are permitted after `require`. In summary: | Before | After | | ------------------------------- | --------------------------------------- | | `class D extends B { ... }` | `class D { extend base: B; ... }` | | `external impl C as Sub;` | `impl C as Sub;` | | `class C { impl as Sortable; }` | `class C { extend impl as Sortable; }` | | `adapter A for C { ... }` | `class A { adapt C; ... }` | | `adapter A extends C { ... }` | `class A { extend adapt C; ... }` | | `interface I { impl as J; }` | `interface I { require Self impls J; }` | | `interface I { impl T as J; }` | `interface I { require T impls J; }` | | `interface I { extends J; }` | `interface I { extend J; }` | None of `adapter`, `extends`, `external` will continue to be keywords. To match these changes, "internal implementations" will now be referred to as "extended implementations," and we will no longer use "external" to refer to implementations. In addition, we drop the syntax for conditionally implemented internal interfaces. Instead, an external interface implementation can be combined with aliases to the members of the interface. ## Details ### Class inheritance What was previously written: ``` base class B; class D extends B; class D extends B { ... } ``` is now written: ``` base class B; class D; class D { extend base: B; ... } ``` An extend base class declaration may appear in the body of a class definition, and has this form: > `extend` `base` `:` _type-expression_ `;` The `extend base: B;` declaration must appear before any other data member declaration, including any [mixin declaration](#future-work-mixins), once those are added. This reflects both the importance of the information, and the fact that the base subobject appears first in the memory layout of objects. Note that `base` is already a keyword, for example used in `base class` declarations. The colon in `base: B` is to indicate that `base` acts like a data member for [purposes of initialization](https://github.com/carbon-language/carbon-lang/blob/trunk/docs/design/classes.md#constructors). This makes the part of a class definition that is used in forward declarations be exactly the part before the curly braces (`{`...`}`). Before this proposal, class forward declarations would exclude the `extends` clause from the first line of the class definition. This change makes classes consistent with other entities that may be forward declared. ### Class implementing an interface What was previously written: ``` interface Sortable; interface Add; interface Sub; class C; // Forward declaration says whether external. impl C as Sortable; external impl C as Add; class C { // Internal impl contributes to the API. impl as Sortable; // External impl of an operator. external impl as Add; } // External impl of an operator. external impl C as Sub; // Definition of `impl` declared earlier. impl C as Sortable { ... } external impl C as Add { ... } ``` is now written: ``` interface Sortable; interface Add; interface Sub; class C; // Forward declaration same whether extended or not. impl C as Sortable; impl C as Add; class C { // Extended impl contributes to the API. extend impl as Sortable; // (Non-extended) Impl of an operator. impl as Add; } // (Non-extended) Impl of an operator. impl C as Sub; // Definition of `impl` declared earlier. impl C as Sortable { ... } impl C as Add { ... } ``` Whether an interface is extended or not is now only reflected in its declaration inside the class body, not in any declaration or definition outside. An `impl` declaration, with this proposal, must have one of these two forms: - Without an `extend` keyword prefix, used for non-extended `impl` declarations and for all `impl` declarations outside of a class body: > `impl` [`forall` `[` _deduced-parameters_ `]`] [_type-expression_] `as` > _facet-type-expression_ (`;`|`{` _impl-body_ `}`) The _type-expression_ is required outside of a class body, otherwise it defaults to `Self`. - With an `extend` keyword prefix, to indicate this implementation is extended, only in a class body: > `extend` `impl` `as` _facet-type-expression_ (`;`|`{` _impl-body_ `}`) Note that this form does not allow either a `forall` clause nor a _type-expression_ before the `as` keyword. This reflects the restriction that [wildcard `impl` declarations must never be extended (formerly: "always be external")](https://github.com/carbon-language/carbon-lang/blob/trunk/docs/design/generics/details.md#wildcard-impl-declarations), and that this proposal removes support for [extended (formerly "internal") conditional implementation](#class-conditional-implementation). ### Class conditional implementation We remove direct support for conditionally implemented extended (formerly "internal") interfaces, called [conditional conformance](https://github.com/carbon-language/carbon-lang/blob/trunk/docs/design/generics/details.md#conditional-conformance). We can work around this restriction by using an non-extended interface implementation and aliases to the members of the interface. What was previously written: ``` interface Printable { fn Print[self: Self](); } class Vector(T:! type) { // ... impl forall [U:! Printable] Vector(U) as Printable { fn Print[self: Self](); } } ``` is now written: ``` interface Printable { fn Print[self: Self](); } class Vector(T:! type) { // ... alias Print = Printable.Print; } impl forall [U:! Printable] Vector(U) as Printable { fn Print[self: Self](); } ``` The way this works is that `Vector.Print` is equivalent to `Vector.(Printable.Print)`, which may or may not be defined. The name `Vector.Print` can no longer be conditional, and the meaning of that name is fixed. However, the implementation of `Printable` for `Vector(T)` may not exist for some types `T`. ### Adapters What was previously written: ``` class C; // Forward declarations of adapters. adapter A for C; adapter E extends C; // Definitions of an adapter. adapter A for C { ... } // Definition of an extending adapter. adapter E extends C { ... } ``` is now written: ``` class C; // Forward declarations of adapters. class A; class E; // Definitions of an adapter. class A { adapt C; ... } // Definition of an extending adapter. class E { extend adapt C; ... } ``` Note: - Adapters are now a special case of classes, not a distinct top-level declaration. - Classes with `adapt` still must not contain anything that was previously forbidden for adapters: no fields, no base class, no virtual methods, no implementations of virtual methods, and so on. - The `adapt` declaration must appear before [mixin declarations](#future-work-mixins), if any. - The syntax for an `adapt` declaration inside a class body is: > [`extend`] `adapt` _type-expression_ `;` ### Interfaces What was previously written: ``` interface A { let T:! Type; } interface B { let U:! Type; } interface C(V:! Type) { } interface I { // `A`'s interface is incorporated into `I`: extends A where .T = i32; // No impact on `I`s interface, but an // implementation must exist: impl as B where .U = i32; // Implementation must exist on another type: impl i32 as C(Self); } ``` is now written: ``` interface A { let T:! Type; } interface B { let U:! Type; } interface C(V:! Type) { } interface I { // `A`'s interface is incorporated into `I`: extend A where .T = i32; // No impact on `I`s interface, but an // implementation must exist: require Self impls B where .U = i32; // Implementation must exist on another type: require i32 impls C(Self); } ``` Notes: - The same change applies to named constraints, and is intended to be used in the future for named predicates used as template constraints. - One syntax for constraints in either `where` clauses or `require` declarations. - Want to open up the syntax to expressing more general constraints. - Syntax for a `require` declaration in an interface or named constraint: > `require` _type-expression_ `impls` _facet-type-expression_ `;` As [with `impl`...`as` declarations before](/docs/design/generics/details.md#interface-requiring-other-interfaces-revisited), a `require` declaration must use `Self`, either to the left or right of `impls`. Note that `require` only supports this subset of `where` clause expressions. Adding other kinds of constraints is future work. - Syntax for an `extend` declaration in an interface or named constraint: > `extend` _facet-type-expression_ `;` ## Future work: mixins Mixins have not been defined in a proposal so far. However, part of the process of resolving [issue #995](https://github.com/carbon-language/carbon-lang/issues/995) was deciding on a syntax for including a mixin in a class. This was done in order to make sure that class declarations that included names from another entity were treated consistently, for example always starting with the `extend` keyword. ``` // Mixin declarations and definitions are // outside the scope of this proposal. mixin M1; mixin M2; class C { // Mixing in mixin M1 extend m: M1; // Mixing in mixin M2. This member is not named. // Initialized using `M2.MakeDefault()`. extend _: M2 = M2.MakeDefault(); // Alternative to the above `M2` that uses a // private name instead of no name: extend private m2: M2; } ``` The declaration that a class uses a mixin is called a "mix" declaration. The syntax of a mix declaration is: > `extend` [`private`|`protected`] (`_`|_id_) `:` _mixin-expression_ [`=` > _initializer-expression_] `;` The _id_ part of the mix declaration defines the name assigned to that mixin subobject. This name is may be used to access members of the mixin and to initialize the mixin in a constructor for the class. The optional `private` or `protected` access specifier controls the access to this name. With this proposal, base class declarations appear in the body of the class definition, like data members, so decision of whether mixins are more like base classes or data members of issue [#1000: Mixins: base classes or data members?](https://github.com/carbon-language/carbon-lang/issues/1000) is less significant. Like base classes, the mix declaration syntax begins with `extend`. Like data members, a class may have multiple mix declarations and they may be intermixed with field declarations. The layout of the memory of an object reflects the order of the declarations in the class body, defining the order of the mixin and field subobjects. ## Rationale The main reason for the new syntax is consistency and simplification: - The use of the `extend` keyword is the consistent way to mark what other entities are consulted during name lookup. - The `class` declaration is simplified by moving more into the definition body. - Making adapters a kind of class removes a kind of top-level declaration, a simplification, and matches how base classes are declared, a consistency. - Dropping the class conditional implementation is a simplification. These consistency and simplification improvements help: - ease the implementation of [language tools and ecosystem](/docs/project/goals.md#language-tools-and-ecosystem) by reducing the size of the language, and providing regularities implementations can use to reuse code or more easily identify relevant parts of the code for queries; - make Carbon code [easy to read, understand, and write](/docs/project/goals.md#code-that-is-easy-to-read-understand-and-write). - teaching the language, since there is less to learn. ## Alternatives considered ### Use `extends` instead of `extend` Keyword `extend` was chosen over `extends` to parallel `impl`, a declaration, instead of `impls`, a binary predicate, decided in [issue #2495](https://github.com/carbon-language/carbon-lang/issues/2495) and accepted in [proposal #2483](https://github.com/carbon-language/carbon-lang/pull/2483). We chose to use `require` instead of `requires` and `adapt` instead of `adapts` for the same consistency. ### Allow interfaces to `require` another interface without writing `Self impls` We [considered](https://github.com/carbon-language/carbon-lang/issues/995#issuecomment-1418387971) allowing `interface I { require J; }` as a short-hand for `interface I { require Self impls J; }`. This is something we would consider adding in the future based on experience with the current approach, but for now we wanted to maintain consistency with the constraint syntax of `where` clauses. This decision and rationale was described in [this comment in #995](https://github.com/carbon-language/carbon-lang/issues/995#issuecomment-1416659207). ### Allow other kinds of `where` clauses after `require` The decision on [#995](https://github.com/carbon-language/carbon-lang/issues/995), see [1](https://github.com/carbon-language/carbon-lang/issues/995#issuecomment-1439336141) and [2](https://github.com/carbon-language/carbon-lang/issues/995#issuecomment-1496781087), called for the same constraint syntax after `require` as we are using after `where`. This proposal _only_ allows `impls` clauses after `require`, since there were concerns about the other kinds of clauses: - We had questions about what syntax to use to refer to members of the interface, such as associated types, to constrain them. Must they start with `Self.` or `.`? See [this comment thread on #2760](https://github.com/carbon-language/carbon-lang/pull/2760#discussion_r1213789364). - Modifying the constraints on an associated type after the declaration for that associated type was not something we clearly wanted to allow. Allowing that would mean having to read the whole interface definition body to understand a single member. - It was unclear if rewrite constraints (using `=` instead of `==`), would be allowed in a `require` declaration, and if they were, whether they would be changed into equality constraints (as if they were declared using `==`). See [this comment on #2760](https://github.com/carbon-language/carbon-lang/pull/2760#discussion_r1213791304). - It would have provided more different ways of declaring equivalent interfaces. This concern was raised [in this comment on #2760](https://github.com/carbon-language/carbon-lang/pull/2760#discussion_r1214571215). For now, we only needed to replace the existing uses of `impl as` constraints, which had none of these concerns. We did not want to block this proposal, so we made sure the `require` clauses were consistent with that _subset_ of `where` clauses. ### Continue to use `impl as` for interface requirements There were a few reasons motivating the change to use the new `require` declarations in interfaces and named constraints, instead of using `impl as` to match how a type could satisfy that requirement. These mostly came down to some observed breaks in the parallel structure between the requirement in interfaces and the satisfaction of that requirement in types. - Whether an interface `I` extends or just requires another interface `J` is independent of whether a type implementing `I` extends or just implements `J`. - If `R` requires interface `I`, the implementation of `R` for a type won't have the implementation of `I` as a nested sub-block. - With the change in [#2173: Associated constant assignment versus equality](https://github.com/carbon-language/carbon-lang/pull/2173), the behavior of `impl as` in interfaces is different from in classes with respect to rewrites of associated types, motivating a change to make those look more different. Furthermore, we had a desire to be able to express the full range of constraints in `where` clauses in named constraints, and we wanted the transformation from a `where` clause to a named constraint to be straightforward. We also wanted the syntax for constraints to be the same between interfaces and named constraints, at least for all constraints that were allowed in both. This was discussed in [#generics-and-templates on 2023-01-30](https://discord.com/channels/655572317891461132/941071822756143115/1069691751968817283) and in [issue #995](https://github.com/carbon-language/carbon-lang/issues/995#issuecomment-1412478644). ### Continue to use `adapter` or `adaptor` instead of `adapt` We didn't want to allow both `adapter` and `adaptor`, since that adds complexity for readers and tooling, but neither seemed clearly dominant enough in usage to pick one over the other. By moving the declaration into the body of the class definition, we were able to switch from the noun form to the verb form of `adapt`, which doesn't have an alternate form in common usage. See [#1159: adaptor versus adapter may be harder to spell than we'd like](https://github.com/carbon-language/carbon-lang/issues/1159) for the discussion. We also considered using `adapts` in a class declaration, as in `class PlayableSong adapts Song { ... }`, see [this comment in #1159](https://github.com/carbon-language/carbon-lang/issues/1159#issuecomment-1316669416). This would have also worked, but was not consistent with our resolution of [#995: Generics external impl versus extends](https://github.com/carbon-language/carbon-lang/issues/995). ### Use some other syntax for extending adapters In the [open discussion on 2023-02-27](https://docs.google.com/document/d/1gnJBTfY81fZYvI_QXjwKk1uQHYBNHGqRLI2BS_cYYNQ/edit?resourcekey=0-ql1Q1WvTcDvhycf8LbA9DQ#heading=h.9steyq834zuq), we discussed some alternatives to `extend adapt` _adapted-class_ `;`: - Adding `and` to make it read more like fluent English: > `extend and adapt` _adapted-class_ `;` However, this felt arbitrary and not compositional. - Make the extending and adapting be separate declarations: > `extend` _adapted-class_ `;`
`adapt` _adapted-class_ `;` This felt too repetitive. - Make the only way to make an extending adapter be trying to inherit from a final base class > `extend base:` _adapted-class_ `;` However, this meant using the same syntax for two different things that could only be distinguished by looking at the declaration of the adapted class, which could be far away. It also would have meant making an extending adapter of a non-final class much more cumbersome. Ultimately, we decided that `extend adapt` would be the most compositional way of combining `extend` and `adapt`, so that is what users would expect. Reading like natural English was not considered essential. ### Continue to have some term for "external" or non-extended implementations The fact that there were some different rules for external implementations was brought up in [this post in #2770](https://github.com/carbon-language/carbon-lang/issues/2770#issuecomment-1509288478). However, [this reply](https://github.com/carbon-language/carbon-lang/issues/2770#issuecomment-1515522070) pointed out those rules could be clearly stated in terms of where you are allowed to write `extend`. That same post made the convincing argument that to get the maximum benefit of the decision on [#995](https://github.com/carbon-language/carbon-lang/issues/995), we should treat `extend` and `impl` as separate orthogonal concepts as much as possible. ### More direct support for conditionally implementing internal interfaces We considered two different ways of more directly supporting conditionally extending a class by an interface: - Prior to this proposal, both name lookup and the implementation were conditional on the same condition. This has the disadvantage of entangling the concerns of name lookup and implementation, and was considered a complex and difficult model. - We also considered fully separating these features, and allowing a type to separately extend its API with an interface and then only conditionally implement that interface. This would allow name lookup to succeed unconditionally, but in cases where the names were in fact not implemented it would produce an error. Both of these approaches would have required some support for expressing this in the new syntax. None of the syntactic approaches we considered were found to be satisfactory. By making name lookup never conditional, it made it much easier to have a consistent marker for declarations that extended name lookup. Ultimately, the alternative of not having a dedicated syntax to support this case seemed the simplest in the short term, given the workaround of conditional external (non-extending) implementation paired with aliases that provide the name lookup, unconditionally. We can always add dedicated syntax later, given sufficient motivating information. The options were considered in [#2580: How should Carbon handle conditionally implemented internal interfaces](https://github.com/carbon-language/carbon-lang/issues/2580). ### Use `external` consistently instead of `extend` We considered making "extending name lookup" the default and overriding that default with the `external` keyword. The argument for this option rested on it being more convenient to express conditional internal implementation, and wasn't seen as attractive once that feature was removed. In the discussion, we generally preferred marking additional places to consult in name lookup since that was something we expected readers of the code to want to specifically look for. This option was proposed as the third option in the [original #995 question](https://github.com/carbon-language/carbon-lang/issues/995#issue-1085174338), and was considered in [this comment](https://github.com/carbon-language/carbon-lang/issues/995#issuecomment-1006096692). ### Use `mix` keyword for mixins We considered a variety of different syntax options for using a mixin for a type in [this comment on #995](https://github.com/carbon-language/carbon-lang/issues/995#issuecomment-1315948818). Many of them used the `mix` keyword, but we ultimately decided that `mix` was redundant with saying `extend`, which we wanted to be included with all constructs extending the type's API by another entity. ### Allow more control over access to mixins There were three different aspects of mixins that we considered giving control over access: - the inclusion of names exported by the mixin into the mixer class, - the ability to cast between the mixin and the mixer class types, and - the ability to use the name of the mixin given by the mixer class to access members of the mixin. This was particularly relevant when we were considering separate declarations for declaring the mixin member and the API extension ([1](https://github.com/carbon-language/carbon-lang/issues/995#issuecomment-1315948818), [2](https://github.com/carbon-language/carbon-lang/issues/995#issuecomment-1317598076)). This approach had the problem that the common case for mixins was extending the API, which would not have been the default. The only examples we had where the mixer class would not want to include the names exported by the mixin were cases where the mixin had nothing to export. This led to [the position](https://github.com/carbon-language/carbon-lang/issues/995#issuecomment-1325746398) that the mixin would control which of its member would be included into the mixer class' API -- that is the mixin would "inject" members rather than "export" them and leave it up to the mixer class to import them. We had concerns that there might be name conflicts, but we thought those might be handled by some other mechanism. This is being considered in question-for-leads issue [#2745: Name conflicts beyond inheritance](https://github.com/carbon-language/carbon-lang/issues/2745). We wanted mixin member names to behave consistently like other class member names, and so default to public but can have a `private` modifier to make private, following [#665: `private` vs `public` _syntax_ strategy, as well as other visibility tools like `external`/`api`/etc.](https://github.com/carbon-language/carbon-lang/issues/665). We decided to put the `private` keyword _between_ the `extend` keyword and the member name for two reasons: - to make it easier to scan for all uses of `extend` in a class, and - to make it clearer that the `private` access control only applies to the member name, not what the `extend` controls. We did not see a use case for controlling the ability to cast between the mixin and the mixer class types separately from being able to access the name the mixin member of the mixin class. This was consistent with our desire to limit declarations to a single access control specifier per declaration, see [this update in #995](https://github.com/carbon-language/carbon-lang/issues/995#issuecomment-1322892195). ### List base class in class declaration By moving the base class into the class body, we accomplished three things: - made forward declarations shorter, - made it possible to inherit from a type declared inside the body of the class definition, and - allowed greater consistency, all API extensions are now declarations in the body of the class definition starting with `extend`. This was decided in [this comment on #995](https://github.com/carbon-language/carbon-lang/issues/995#issuecomment-1317598076). This left open the question of what keyword introducer to use in base class declarations, since using `base` would cause an ambiguity with declaring a member class that could be extended, as considered in [this comment](https://github.com/carbon-language/carbon-lang/issues/995#issuecomment-1320614313). Ultimately we avoided this problem by requiring base class declarations to always begin with `extend`, not support any form of private or protected inheritance (see [this comment](https://github.com/carbon-language/carbon-lang/issues/995#issuecomment-1325746398)), and not support any combination of an `extend` declaration with a member class declaration. ================================================ FILE: proposals/p2868.md ================================================ # Allow overlap with a `final impl` if identical [Pull request](https://github.com/carbon-language/carbon-lang/pull/2868) ## Table of contents - [Abstract](#abstract) - [Problem](#problem) - [Background](#background) - [Proposal](#proposal) - [Rationale](#rationale) - [Alternatives considered](#alternatives-considered) - [Allow interfaces with member functions to compare equal](#allow-interfaces-with-member-functions-to-compare-equal) - [Mark associated constants as `final` instead of an `impl` declaration](#mark-associated-constants-as-final-instead-of-an-impl-declaration) - [Allow type inequality constraints](#allow-type-inequality-constraints) - [Prioritize a `final impl` over a more specific `impl` on the overlap](#prioritize-a-final-impl-over-a-more-specific-impl-on-the-overlap) ## Abstract Allow an `impl` to overlap with a `final impl` if they agree on the overlap. Agreement is defined as all values comparing equal, and functions never comparing equal. Implements the decision in question-for-leads [issue #1077: find a way to permit impls of CommonTypeWith where the LHS and RHS type overlap](https://github.com/carbon-language/carbon-lang/issues/1077). ## Problem The [current design](https://github.com/carbon-language/carbon-lang/blob/trunk/docs/design/expressions/if.md#same-type) includes a `final impl` declaration for the `CommonTypeWith(T)` interface: ```carbon final impl forall [T:! type] T as CommonTypeWith(T) where .Result = T {} ``` [Marking an `impl` declaration `final`](/docs/design/generics/details.md#final-impl-declarations), prevents any overlapping implementation that would be considered more specific by the [overlap rule](/docs/design/generics/details.md#overlap-rule). This includes cases where the overlap is harmless, such as: ```carbon impl forall [U:! type, T:! CommonTypeWith(U)] Vec(T) as CommonTypeWith(Vec(U)) where .Result = Vec(T.Result) {} ``` This is an implementation we would like to define, along with a number of similar cases. And this `impl` declaration doesn't actually conflict with the previous `final impl` because the value of `Result`, the only member of the `CommonTypeWith` interface, agrees where the two implementations overlap. ## Background The `CommonTypeWith(T)` interface and `final impl` above were introduced in [proposal #911: Conditional expressions](https://github.com/carbon-language/carbon-lang/pull/911). [Proposal #983: Generics details 7: final impls](https://github.com/carbon-language/carbon-lang/pull/983) introduced and defined the rules for `final impl` declarations. The overlap rule was introduced in [proposal #920](https://github.com/carbon-language/carbon-lang/pull/920). There were a number of different resolutions for this problem considered in question-for-leads [issue #1077: find a way to permit impls of CommonTypeWith where the LHS and RHS type overlap](https://github.com/carbon-language/carbon-lang/issues/1077). This proposal codifies the resolution of that issue. ## Proposal We allow an `impl` declaration to overlap with a `final impl` declaration if it agrees on the overlap. Since we do not require the compiler to compare the definitions of functions, agreement is only possible for interfaces without any function members. The details about how the intersection is computed and how templated `impl` declarations are handled have been added to [the section on `final` impl declarations in the generics design doc](/docs/design/generics/details.md#final-impl-declarations). ## Rationale This proposal is intentionally keeping the language small by making a simple rule that addresses the only identified use case and nothing more. This benefits - [Language tools and ecosystem](/docs/project/goals.md#language-tools-and-ecosystem) - [Code that is easy to read, understand, and write](/docs/project/goals.md#code-that-is-easy-to-read-understand-and-write) by relying on Carbon's commitment to [software and language evolution](/docs/project/goals.md#software-and-language-evolution) to update our approach as needed, rather then trying to proactively address concerns ahead of time. ## Alternatives considered ### Allow interfaces with member functions to compare equal There are some specific cases where the compiler can verify that two functions are the same without having to compare their definitions. For example, two implementations that don't implement a function and instead inherit the default from the interface could be considered equal. This creates an evolution hazard, though, that copying the definition from the interface into the implementation means that the interface could now compare not equal without any change in behavior. For now, the simple rule that we don't compare functions at all is sufficient for our identified [use case](#problem). This is something we would reconsider given new use cases. ### Mark associated constants as `final` instead of an `impl` declaration The biggest benefit from knowing that an `impl` declaration won't be specialized is being able to use the values of the associated constants, particularly associated types. Thus, it is natural to focus on associated constants, which don't have the same concerns as functions with comparing for equality. However, for the [motivating use case](#problem), we would still need this proposal, just restricted to the associated constants that are declared `final`. So we may still add this feature, if it is warranted by demand, but we did not yet have that justification. This is essentially the same position as when this feature was considered in [proposal #983](/proposals/p0983.md#final-associated-constants-instead-of-final-impls). ### Allow type inequality constraints Another approach would be to provide type inequality constraints so the more specialized implementation could exclude the overlapping cases. This has some downsides: - The more specialized implementation has to be aware of the `final` impl to specifically exclude it. This would add extra steps to the development process since this discovery is likely to occur as the result of a failed compile. - The more specialized implementation becomes more verbose, with extra conditions that don't add any value. - The current approach for establishing whether two types are equal doesn't in general provide a way to show two types are not equal in generic code. ### Prioritize a `final impl` over a more specific `impl` on the overlap This was a possible fix, but was seen as a bigger change that we didn't yet have justification for. ================================================ FILE: proposals/p2875.md ================================================ # Functions, function types, and function calls [Pull request](https://github.com/carbon-language/carbon-lang/pull/2875) ## Table of contents - [Abstract](#abstract) - [Problem](#problem) - [Background](#background) - [Proposal](#proposal) - [Details](#details) - [Functions and function types](#functions-and-function-types) - [Bound methods](#bound-methods) - [Call syntax](#call-syntax) - [Direct calls](#direct-calls) - [Generic callable parameters](#generic-callable-parameters) - [Function types and `Call`](#function-types-and-call) - [Overloaded call operator](#overloaded-call-operator) - [Rationale](#rationale) - [Alternatives considered](#alternatives-considered) - [Signature-based function types](#signature-based-function-types) - [Make direct and indirect calls behave uniformly](#make-direct-and-indirect-calls-behave-uniformly) - [Future work](#future-work) - [Overloading](#overloading) - [Overloading on expression category and phase](#overloading-on-expression-category-and-phase) - [Variadics](#variadics) - [Lambdas](#lambdas) - [Function pointers](#function-pointers) ## Abstract Specify the behavior of function calls and the type and behavior of the entity introduced by a function declaration. ## Problem Functions are a primitive building block through which we define Carbon's execution semantics, but we don't currently have a specification for what a function is in Carbon nor how function calls work. We have open questions around whether Carbon has first-class function types, how types can overload the function call operation, and how callable entities such as the name of a parameterized class fit into the picture -- are they functions or something else? ## Background This proposal assumes the reader understands how functions and function pointers work in C or C++. ## Proposal A function declaration in Carbon introduces a new, unique, stateless type, called a _function type_. The function name is bound to a value of that function type. Function calls use C-like syntax: an expression naming a callable is followed by a syntax resembling a tuple of arguments. There are several kinds of callable: - Functions, and more generally [values of function types](#functions-and-function-types). - [Bound methods](#bound-methods), such as `my_vector.Begin`. - Lambdas, once they are introduced to Carbon. - Parameterized entities, such as a generic class `Vector` or a generic interface `AddWith`. - Values of dependent types that are [constrained to be callable](#generic-callable-parameters). - User-defined class types that [overload function call syntax](#overloaded-call-operator). In the first four cases, argument deduction is used to determine the values of deduced arguments, and then pattern matching is used to bind parameter patterns to argument values. In the remaining cases, the built-in `Call` interface is used to determine the semantics of the call, and the call `F(args)` is translated into `F.(Call(ArgTypes).Op)(args)`. Note that this is itself a bound member function call, and `Call(ArgTypes)` is a call to a parameterized entity, so this transformation is only performed once. ## Details ### Functions and function types A function declaration such as ``` fn F[T:! type](x: T) -> T { return x; } ``` introduces a value named `F`, whose type is a unique _function type_. Distinct functions have distinct function types, even if they have the same signature. A function type is an empty, trivial type. There is no way to name a function type other than asking for the type of the function value. ``` // Compile-time function. fn TypeOf[T:! type](x: T) -> type { return T; } // ✅ `F` is a first-class value with a first-class type. let template FType:! type = TypeOf(F); var my_f: FType = F; ``` Function values are regular values that can be stored in variables, passed to functions, and so on. ``` fn G() -> i32 { // ✅ `my_f` has function type `FType`. This is a direct call to `F`. return my_f(1); } fn Sort[template F:! type, T:! type](v: Vector(T)*, f: F); fn Compare(a: i32, b: i32) -> Ordering { return a.(Ordered.Compare)(b); } fn SortInts(v: Vector(i32)*) { Sort(v, Compare); } ``` For the purpose of the [orphan rule](/docs/design/generics/details.md#orphan-rule), a function type is considered to be declared by the function declaration that introduces the function value. ### Bound methods For each function type corresponding to a method, there is a corresponding _bound method type_. When a member access is performed on an object of class type to access a method, the result is a _bound method value_ of bound method type. A bound method type describes the callee in a method call, and a bound method value describes the `self` parameter of the call. ``` class HasMember { // `HasMember.F` has a stateless function type, with signature // `[self: Self](n: i32) -> i32`. fn F[self: Self](n: i32) -> i32; } fn F(h1: HasMember, h2: HasMember) -> i32 { // ✅ `h1.F` is a bound method value whose type is a bound method type, // with signature `(n: i32) -> i32`. var hf: auto = h1.F; // ✅ `h1.F` and `h2.F` are of the same bound method type. hf = h2.F; // ✅ Same as `h2.F(4)`. return hf(4); } ``` ### Call syntax Calls take the form `a(b, c, d)` or `a(b, c, d,)`, where: - `a` is the callee, which can be a name, a literal, a member access, or some more complex expression enclosed parentheses. - `b`, `c`, `d` are any number of argument expressions. Arguments are separated by commas, and if the argument list is not empty, an optional trailing comma is permitted but not required after the final argument. Call syntax is syntactically equivalent to a primary expression followed by a tuple literal, except that a tuple literal requires a trailing comma to form a single-element tuple `(b,)`, whereas in call syntax both `a(b)` and `a(b,)` are permitted. ### Direct calls A call expression is a _direct call_ when the callee: - is the name of a parameterized entity, like a generic class or interface, or - has a function type or bound method type. In a direct call, a call signature is available which is used to check the given arguments against the callee's declared implicit and explicit parameters. This checking proceeds as follows: - Argument deduction is performed by comparing the declared parameter types against the actual argument types and deducing values for implicit arguments that make the types equal. - Then, for each binding in the explicit parameter list in turn, all argument values that have been deduced are substituted into the parameter. - If the parameter is a `template :!` binding, the argument expression is converted to have the same type as the binding and template constant expression phase. - If the parameter is a symbolic `:!` binding, the argument expression is converted to have the same type as the binding and symbolic constant expression phase. - Otherwise, the parameter is pattern-matched against the argument. If a parameter is a `:!` binding, its corresponding converted argument expression is evaluated, and its value is added to the list of deduced argument values before any later parameters are processed. The result of the call expression depends on the callee: - If the callee is a parameterized entity such as a generic class or a generic interface, the result is the specific instance of that generic, such as a class or interface, and the call is a value expression of type `type`. - If the callee is a function value, the call is an initializing expression whose type is the substituted return type of the function. When evaluated, the call expression will invoke the function and produce whatever value it returns. - If the callee is a bound method value, it behaves the same as a function value, except that the `self` parameter of the called function is bound to the `self` value in the bound method value. ### Generic callable parameters A generic parameter can be constrained to be a callable type using the `Call` interface: ``` interface Call(Args: type) { let Result:! type; fn Op[self: Self](args: Args) -> Result; } ``` > **TODO:** `Call` should be variadic. For now, we model it as taking a tuple > type, and we model `Call.Op` as taking a tuple value. For example: ``` fn Sort[T:! type, F:! Call((T, T)) where .Result = Ordering] (v: Vector(T)*, cmp: F) { ``` A call expression that is not a direct call is translated into an invocation of `Call(Args).Op`, where `Args` is the type of the call's argument tuple. ``` // In Sort... auto ord: auto = cmp((*v)[i], (*v)[j]); // ... is translated into ... auto ord: auto = cmp.(Call((T, T)).Op)((*v)[i], (*v)[j]); ``` Note that the types of the call arguments are modeled as an interface parameter. This permits the call interface to model function overloading. However, the return type is an associated type, not a parameter -- we do not permit overloading on return types, and we don't want type information to propagate from the context in which a call expression appears inwards to the call. ### Function types and `Call` A function type or bound method type implements the `Call` interface for every set of runtime argument types that a direct call to the function or bound method would accept. The behavior of `Call.Op` is to call the function or bound method with the provided argument list. ``` fn Select[T:! type](b: bool, x: T, y: T) -> T { return if b then x else y; } // Generated: impl forall [T:! type, BType:! ImplicitAs(bool)] Select as Call((BType, T, T)) where .Result = T { fn Op[self: Self](args: (BType, T, T)) { let (b: bool, x: T, y: T) = args; return Select(b, x, y); } } ``` Implicit conversions are permitted for parameters whose types do not involve deduced parameters. The intent is for the `impl` to support indirect calls in the same cases where the function supports direct calls, with the same meaning. ``` fn TakeI32Fn[F:! Call(i32)](f: F); fn I64Fn(n: i64); fn Run() { // ✅ `I64Fn` can be called with an `i32`, because // `i32 impls ImplicitAs(i64)`. TakeI32Fn(I64Fn); } ``` > **Note:** A call made using the `Call` interface always takes its arguments as > value expressions and the call is always an initializing expression. If the > function takes its arguments using `var`, or if a language mechanism is added > that permits a direct function call to not be an initializing expression, > additional conversions will be required. The `Call` interface is treated as > not being implemented in cases where those conversions are not possible, for > example because an argument or return value is not copyable. If we add a > mechanism to allow an `impl` to specify a different expression category for > its parameters or result than the one in the interface, for example by taking > a `var` parameter where the interface took a `let` parameter, that would > automatically also be available for overloaded function calls. > > We anticipate revisiting these constraints in a future proposal, and expanding > the function call interface to provide the same generality that is provided by > `fn` declarations. This is described more in the > [future work](#overloading-on-expression-category-and-phase) section. The `Call` interface only models function calls for which arbitrary runtime values of the given parameter types can be passed to the function. If the signature of the function has compile-time parameters in its explicit argument list or has any refutable pattern matching between the call arguments and function parameters, the function type will not implement `Call`. ``` fn Runtime[T:! type](x: T); fn CompileTime(T:! type, x: T); // 🤷 Undecided whether this is valid. fn RefutablePattern(1 as i32); fn Run() { // ✅ Calls `Runtime(0)`. Runtime.(Call(i32).Op)(0); // ❌ Can't call `CompileTime` this way, it can't implement `Call(type, i32)` // because the type would be passed at runtime. CompileTime.(Call(type, i32).Op)(i32, 0); // ❌ Can't call `RefutablePattern` this way, it can't implement `Call(i32)` // for arbitrary i32 arguments. RefutablePattern.(Call(i32).Op)(0); } ``` ### Overloaded call operator The `Call` interface can be implemented to overload the meaning of the function call operator for a type. ``` class Func(Arg:! type) { impl as Call((Arg,)) where .Result = () { fn Op[self: Self](arg: (Arg,)) { Print("hello, world"); } } } fn Run() { let f: Func(i32) = {}; // ✅ Prints "hello, world". f(42); } ``` There are no constraints on the callee type, beyond the normal constraints for implementing an interface. ``` class X { var n: i32; } // ✅ OK, but inadvisable. impl {.a: X} as Call(()) where .Result = i32 { fn Op[self: Self](args: ()) -> i32 { return self.a.n; } } fn Run() -> i32 { // Returns 1. return {.a = {.n = 1} as X}(); } ``` There is no way to directly define a function-like callable class type that takes a compile-time argument. This is similar to the situation in C++ where there is no way to define an `operator()` for a value `x` of class type so that `x()` passes `T` to `operator()` at compile time. However, this can be worked around by putting the compile-time value in the type of an argument instead: ``` class Wrap(T:! type) {} class Callable { impl forall [T:! Printable] as Call((Wrap(T), T)) where .Result = () { fn Op[self: Self](args: (Wrap(T), T)) { let (_: auto, v: T) = args; Print(v); } } } fn CallIt[F:! Call(Wrap(i32), i32)](f: F) { f({} as Wrap(i32), 0); } fn Run() { CallIt({} as Callable); } ``` ## Rationale Principles: - [Principle: one static open extension mechanism](/docs/project/principles/static_open_extension.md). - Overloaded calls are supported by implementing an interface. - [Principle: Prefer providing only one way to do a given thing](/docs/project/principles/one_way.md). - There is only one obvious way to pass a callable to a generic function, and it works efficiently whether the argument is a function, a callable object, or (eventually) a lambda. Goals: - [Performance-critical software](/docs/project/goals.md#performance-critical-software) - Passing around functions is no less efficient than passing around callable objects. - [Code that is easy to read, understand, and write](/docs/project/goals.md#code-that-is-easy-to-read-understand-and-write) - Function types in C and C++ are notoriously hard to read. We avoid this problem by not having signature-based function types. - [Interoperability with and migration from existing C++ code](/docs/project/goals.md#interoperability-with-and-migration-from-existing-c-code) - As [future work](#function-pointers) we have the basis for a design for function pointers. C++ function pointers can be modeled in Carbon code as values that implement `Call`, as can member function pointers. ## Alternatives considered ### Signature-based function types We could give each function signature a distinct type, as is done in C and C++. This would provide a story for [function pointers](#function-pointers) that does not rely on generics or type erasure or fancy representation optimizations. The main down side of this approach is one we see frequently in C++: passing a function to a template generic would be less efficient than passing a function object to the same template. For example, in C++, given ``` std::vector v; bool cmp(int a, int b) { return simple_calculation(a, b); } ``` ... calling `ranges::sort(v, cmp)` may be much less efficient than calling `ranges::sort(v, [](int a, int b) { return cmp(a, b); })`, because the latter performs a direct call and the former passes a function pointer, resulting in an indirect call. Making functions result in unique types means that calls to functions, lambdas, and function-like objects have semantics that are more similar and have similar efficiency properties. ### Make direct and indirect calls behave uniformly It would be desirable to remove the difference between direct and indirect calls. Unfortunately, that doesn't seem to be practical, for a number of reasons: - Indirect calls need to be transformed into another call expression. We need a foundation for our semantics at some point, where we make an actual function call rather than transforming one function call into another. - We have chosen that [interfaces are the only static open extension mechanism](/docs/project/principles/static_open_extension.md). This means that any general mechanism we provide to overload function calls must fit within the boundaries of Carbon's `interface` and `impl` mechanisms. - We could try to support passing compile-time parameters to functions by specifying in the `impl` query whether each argument can be passed at compile time, but any such query will need to be able to fall back to passing each argument at runtime, which can result in an exponential-time search for a matching `impl`. ## Future work ### Overloading We intend to support function overloading, and although it outside the scope of this proposal, it is important to ensure that we have reasonable paths forward to support overloading within this framework. An overloaded function has multiple different signatures -- sets of implicit and explicit parameters -- that will be checked against the provided arguments. In this proposal, that means that a set of overloaded functions introduces a single function type that supports being called in different ways. Depending on how we approach overloading, overload selection might be done by checking each candidate in turn until we find one that matches, or checking them all and somehow selecting the best match, and our current intent is to use the former approach. Translated into this proposal, one important consequence is that the function type corresponding to the set of overloaded functions has multiple parameterized implementations of the `Call(...)` interface, and the implementation selected for a particular call will need to follow whatever rules the overloading mechanism picks. For example, if function overloading picks the first matching function, then given the following, using placeholder syntax: ``` overloaded fn Abs[T:! Unsigned](x: T) -> T { return x; } overloaded fn Abs[T:! Floating](x: T) -> T { return x < 0 ? -x : x } ``` we would synthesize implementations that behave as follows: ``` match_first { impl forall [T:! Unsigned] Abs as Call((T,)) where .Result = T { ... } impl forall [T:! Floating] Abs as Call((T,)) where .Result = T { ... } } ``` For a type that is `Unsigned & Floating`, the former `impl` will be selected, matching the behavior of overload selection. ### Overloading on expression category and phase We should consider whether calls can be overloaded on the expression category and expression phase of the callee and arguments. For compile-time arguments, we may also wish to support overloading on the constant value. Other languages support overloading on the expression category of the callable object. Rust provides separate `Fn`, `FnMut`, and `FnOnce` to distinguish between different ways of passing the callable object into an overloaded function call, and C++ permits `operator()` to take `*this` as `const`, non-`const`, or even `&&`. In Carbon, we could follow the Rust approach and provide multiple `Call` interfaces to handle different kinds of `self` parameter, modeled in a similar way to the `IndexWith` and `IndirectIndexWith` interfaces used for subscripting. A more general, ambitious and ergonomic approach that we have started exploring would be to allow the function call signature to be described as part of the call interface: ```carbon impl MyCallable as call(T:! type, x: T) -> T; ``` This might be a shorthand syntax for some way of expressing the signature as an interface, or a new form of first-class language primitive. The options in this space are currently being explored in issue [#3154](https://github.com/carbon-language/carbon-lang/issues/3154). ### Variadics Once Carbon supports variadics, the overloaded call mechanism should be revised to make use of them, instead of using a tuple type to approximate a variadic parameter list. ### Lambdas A lambda is expected to behave much like a function under this proposal, with the primary difference being that lambdas can be stateful, whereas functions are stateless. ### Function pointers For interoperability with C++, and for purposes of cheaply storing references to stateless functions, function pointers should be supported. A function pointer could be modeled as: ``` alias FunctionPtr(Args:! type, Result:! type) = DynPtr(Stateless & Call(Args) where .Result = Result); ``` where: - `Stateless` is an interface describing types that are empty, do not have a notion of identity, and for which instances can be created and destroyed trivially. - `DynPtr` is a [type that performs type erasure and runtime dispatch](/docs/design/generics/details.md#dynamic-types) for a given facet type. - `DynPtr` has an optimization that `DynPtr(Stateless & I)`, where `I` is an interface with exactly one associated function, is represented as a pointer to that function. ================================================ FILE: proposals/p2922.md ================================================ # Introduce a new Conduct Team [Pull request](https://github.com/carbon-language/carbon-lang/pull/####) ## Table of contents - [Abstract](#abstract) - [Problem](#problem) - [Background](#background) - [Proposal](#proposal) - [Details](#details) - [Strategy for a sustainable Conduct Team](#strategy-for-a-sustainable-conduct-team) - [What we are looking for in Conduct team members](#what-we-are-looking-for-in-conduct-team-members) - [Initial team members](#initial-team-members) - [Process](#process) - [Rationale](#rationale) - [Alternatives considered](#alternatives-considered) - [Make no changes](#make-no-changes) - [Have some overlap between Carbon leads and the Conduct team](#have-some-overlap-between-carbon-leads-and-the-conduct-team) - [Keep the restriction on new conduct team members accessing prior reports](#keep-the-restriction-on-new-conduct-team-members-accessing-prior-reports) ## Abstract The Carbon Project relied on the three leads to handle conduct concerns initially as it bootstrapped its community team and expertise. We now have an active and effective community lead and team of moderators. Our community lead has worked to train and ramp up a new and independent conduct team. This proposal both provides an overview of the process and hands off conduct handling to the new team! Going forward, we expect routine updates to the conduct team to happen without full proposals as they allow trained folks to rotate in and out of this difficult but essential role on the project. Last but not least, making these changes uncovered a restriction in the Code of Conduct itself that we expect to be problematic to adhere to going forward. While well intentioned, it has a bunch of unanticipated effects that made both current and new conduct teams want to remove it. A related section has had its wording strengthened to try and address the underlying motivation at least partially. ## Problem - Our Code of Conduct team is made up of those with the highest power in the community (Carbon leads), and our current framework does not allow for clear accountability in terms of their own conduct due to this lack of separation. - The diversity of backgrounds and perspectives is limited among our current CoC team. - The Carbon leads have limited time and availability to intervene in urgent and important misconduct situations. So, it is time to build a specialized team to deal with escalations which is not subordinate to the Carbon leads, and is able to handle diverse misconduct escalations in an adequate and timely manner. This means we can recruit and/or train people from diverse backgrounds to handle such escalations for the Carbon community, and provide competent support to the leads, the rest of the moderation team, and everyone else in the community. ## Background Our Code of Conduct: https://github.com/carbon-language/carbon-lang/blob/trunk/CODE_OF_CONDUCT.md Our current CoC team: - Chandler Carruth (@chandlerc on Discord and GitHub) - Kate Gregory (@KateGregory on Discord and GitHub) - Richard Smith (@zygoloid on Discord and GitHub) ## Proposal Create a new [Conduct Team](/docs/project/teams/conduct_team.md) as follows: - Recruit and train 5+ potential new conduct team members to deal with conduct-related escalations on behalf of and for the Carbon community, and replace the current CoC team as soon as enough members are ready. - We should always have at least 3 conduct team members available to respond promptly, even factoring in vacations, getting sick, or other normal disruptions. - Subsequent updates to the exact members will be managed by the new conduct team themselves, and without full proposals but just PRs to update the relevant documentation. Also remove a restriction on how reports are shared with new conduct team members going forward (but _not_ retroactively, which would violate our past promise). While this restriction was well intentioned, it isn't a restriction that the conduct team can effectively uphold going forward. For example, it would prevent informing new conduct team members of people banned because of bad-faith reports. We understand that reports are sensitive, and have strengthened our statements around keeping them confidential and only using them as needed. ## Details ### Strategy for a sustainable Conduct Team The Carbon community needs a **distinct, qualified, empowered, and diverse** Code of Conduct team. This crucial and particularly straining work needs to be shared among the team, so all members get enough support and rest. The proposal is for a member rotation strategy. Carbon's long-term goal should be to have 5+ trained Conduct Team members, so at least 3 are available to discuss and act upon incident reports in a timely and adequate manner. The initial team was recruited by our Community lead and pulls from the current trained moderator pool. We propose that the Conduct team itself is responsible to continue recruiting both new Conduct team members and new moderators to make both efforts sustainable. They will work toward better representation, embracing both broad and intersectional diversity. ### What we are looking for in Conduct team members Team members need a track record showing: - Effective and constructive engagement with online, technical, and/or open-source software communities - Advanced moderation and mediation skills - Intercultural competence - Extensive knowledge of human diversity and systemic discrimination forms - Proficient written English communication skills Other essential things we prioritize for all team members: - Kindness - Thoughtfulness, including while dealing with highly sensitive information - Prioritizing the most vulnerable - Readiness to have uncomfortable conversations, including sparking them - Resilience in the face of abuse, including when becoming a target - Verified Discord and GitHub user account - Readiness to use Google Drive collaboration tools - Readiness to deal with every escalation reported to the team while on duty High-value things we look for but are not necessary: - Personal experience of marginalization in tech communities (for example based on ability/disability, race/ethnicity, gender identity, lifestyle, first language, etc.) - Already active in the C++ or Carbon community - Figure of authority in some relevant domain - Non-corporate working experience Note that it is _not_ important to be good at coding or a native English speaker to be effective as a member of the team. ### Initial team members - Allison Poppe (@acpoppe on Discord and GitHub) - Céline Dedaj (@celineausberlin on Discord and GitHub) - Christopher Di Bella (@cjdb on Discord and GitHub) - Lexi Bromfield (@lexinadia on Discord and @lexi-nadia on GitHub) - flysand (@flysand on Discord and @flysand7 on GitHub) ### Process Selection and sourcing: - Candidates were identified and recruited by our Community lead. - Candidates met with both Carbon leads and the Community lead individually to make sure everyone was comfortable with the role and responsibilities involved. Onboarding and support: - New members will have access to coaching and mentoring from the Community lead as a diversity, equity and inclusion specialist, and general support by a senior moderation team member. - The project organizes regular informal moderation meetups to learn with and from each other, including a book club. Exit: - This work tends to be straining and that each person's involvement may only be temporary. - Both the Carbon leads and the newly formed conduct team will make sure that individual conduct team members feel safe to leave the team and community when they need to step out by developing a bench of candidates. ## Rationale - [Community and culture](/docs/project/goals.md#community-and-culture) - The Carbon project needs to have an effective and scalable process for handling conduct concerns to continue to be an inclusive and welcoming community. - Having a separate team focused on these concerns both enables more specialized skills and can provide a more effective path to hold the Carbon leads accountable to the same conduct standards. ## Alternatives considered ### Make no changes This would leave the fundamental problems of both scale and external accountability unresolved. ### Have some overlap between Carbon leads and the Conduct team We considered having some amount of overlap between the leads and the conduct team on an ongoing basis. While this would help ensure that the Carbon leads are aware and attending to the community, culture, and conduct needs of the project, it ultimately had a number of disadvantages that made us decide to have a fully separate team: - The Conduct team can and should focus their energy and leverage their special skills at evaluating conduct challenges. This is different from the focus, skills, and priorities of the leads. - The leads want to encourage the conduct team to hold the leads own conduct accountable to the rest of the community, and so want to keep the conduct team especially as independent as possible. - Even if in practice no conflict arises, having a _visible_ separation is valuable to the community to build trust. - Having two fully independent groups helps facilitate them each helping, supporting, empowering, and when necessary, challenging the other. ### Keep the restriction on new conduct team members accessing prior reports We considered this, as we are very sympathetic to ensuring community members feel safe making reports, even though the composition of the conduct team might change in the future. Unfortunately, we weren't able to find an effective restriction in this space that still allowed the conduct team to effectively respond to conduct concerns. We have tried to further clarify the expectations on the narrow and limited ways in which it is ever appropriate for this information to be used. Misusing conduct reports, much like other abuses of the Code of Conduct's process, is itself a serious violation of the code. ================================================ FILE: proposals/p2964.md ================================================ # Expression phase terminology [Pull request](https://github.com/carbon-language/carbon-lang/pull/2964) ## Table of contents - [Abstract](#abstract) - [Problem](#problem) - [Background](#background) - [Proposal](#proposal) - [Rationale](#rationale) - [Alternatives considered](#alternatives-considered) ## Abstract Update terminology around expression properties: - "value phase" -> "expression phase" - "symbolic value" -> "symbolic constant" - "constant" -> "template constant" - "constant or symbolic value" -> "constant" Implements the decision in [#1391](https://github.com/carbon-language/carbon-lang/issues/1391). ## Problem There are a few concerns with the current terminology: - We were not happy with it at the time it was introduced in [proposal #1378](https://github.com/carbon-language/carbon-lang/pull/1378/files/198c7bd152bc4e223f1fc2484455e17ba31f19f9#r921592116), but didn't want to block that proposal on finding better names. - It is a property of expressions, not values. We are switching from "value categories" to "expression categories" for the same reason. See [proposal #2006](https://github.com/carbon-language/carbon-lang/pull/2006) and [PR #2744](https://github.com/carbon-language/carbon-lang/pull/2744). - No good term for the bindings that use `:!`, which share a number of properties, and often we want to refer to those together. The concerns about the names of the individual phases led to [questions-for-leads issue #1391: New name for "constant" value phase](https://github.com/carbon-language/carbon-lang/issues/1391). This proposal implements the resolution from that issue. ## Background The current names were introduced in [proposal #1378: Design overview update part 7: values](https://github.com/carbon-language/carbon-lang/pull/1378) based on a discussion in [#typesystem on Discord](https://discord.com/channels/655572317891461132/708431657849585705/996547601451204630). I am not aware of corresponding terminology in other programming languages. ## Proposal We update terminology around expression properties, specifically what we previously referred to as "value phase" and values it can have, as follows: - "value phase" -> "expression phase": since this is a property of expressions, not values. - "symbolic value" -> "symbolic constant": for symbolic compile-time values like checked-generic parameters. - "constant" -> "template constant": for compile-time values where the value is available during type checking, like literals and `template` parameters. By making these last two terms both end in "constant," we allow their combination (including all compile-time values) to be collectively referred to using the term "constant." For example, either kind of constant may be passed to a function with either kind of constant binding. ## Rationale The goals of this proposal are: - clearer and more concise communication, and - making it easier for people to learn Carbon by using more consistent terminology. This supports Carbon's goal of having [Code that is easy to read, understand, and write](/docs/project/goals.md#code-that-is-easy-to-read-understand-and-write). ## Alternatives considered We were happy with this option and did not spend time coming up with more alternatives. ================================================ FILE: proposals/p3162.md ================================================ # Reduce ambiguity in terminology [Pull request](https://github.com/carbon-language/carbon-lang/pull/3162) ## Table of contents - [Abstract](#abstract) - [Problem](#problem) - [Background](#background) - [Proposal](#proposal) - [Details](#details) - [Rationale](#rationale) - [Alternatives considered](#alternatives-considered) ## Abstract Change terminology away from terms that are ambiguous: - Reserve "generic type" for types with (compile-time) parameters, like `Vector` in `Vector(T:! type)`. Don't use that term to refer to `T`, as it would with [#2360](/proposals/p2360.md#terminology). - Use the term "compile-time" instead of "constant" to mean "template or symbolic." Expand the term "constant" to include values, such as from `let` bindings. ## Problem Right now, the term "generic type" has two meanings. In this example: ``` class Vector(T:! type); ``` Both `Vector` and `T` could be called a "generic type." It would be much less confusing if one of those two would have a different name. Similarly, "constant" can currently mean multiple things: - "evaluation at compile time," as in "constant evaluation" - "not variable," as in `let` instead of `var` - "non-mutating view," as in `const T*` In practice, this has resulted in confusion. For example, the term "constant bindings" doesn't include all `let` bindings, even though they are not variable bindings. ## Background The two meanings of "generic type" come from: - Proposal [#2360](/proposals/p2360.md#terminology) defines a generic type to be a type or facet introduced by a `:!` binding, such as in a generic parameter or associated constant. - Other uses of the term generic, such as in generic function, mean a language construct with a compile-time parameter (as in [Rust](https://doc.rust-lang.org/rust-by-example/generics.html)). This is the usage in the broader programming language community, and includes calling parameterized types "generic types" (as in [Java](https://docs.oracle.com/javase/tutorial/java/generics/types.html), [.NET](https://learn.microsoft.com/en-us/dotnet/standard/generics/#terminology), [Swift](https://docs.swift.org/swift-book/documentation/the-swift-programming-language/generics/#Generic-Types)). Issue [#1391: New name for "constant" value phase](https://github.com/carbon-language/carbon-lang/issues/1391) implemented in proposal [#2964: Expression phase terminology](https://github.com/carbon-language/carbon-lang/pull/2964), expanded the term "constant" from referring to just template constants to also include symbolic constants from checked generics. Since then proposal [#2006: Values, variables, pointers, and references](https://github.com/carbon-language/carbon-lang/pull/2006) introduced the `const` modifier on types, providing a read-only view. ## Proposal We make these changes: - Reserve "generic type" for types with (compile-time) parameters, like `Vector` in `Vector(T:! type)`. Don't use that term to refer to `T`, as it would with [#2360](https://github.com/carbon-language/carbon-lang/blob/trunk/proposals/p2360.md#terminology). - Expand "constant binding" to include all `let` bindings. - Use the term "compile-time binding" to refer to the collection of template and symbolic bindings, such as from generic parameters and associated constants. - Similarly "compile-time constants" to refer to template and symbolic constants, as opposed to just "constants." - The term "compile-time parameter" may be used instead of "generic parameter." For now, both terms will be used, but in the future it might be clearer to only use "generic" to mean "has compile-time parameters." - Only use "symbolic binding" and "template binding" not "symbolic constant binding" nor "template constant binding." - Where applicable, switch from talking about parameters to bindings, since almost everything that applies to compile-time parameters also applies to compile-time bindings. For example, see ["binding patterns"](/docs/design/generics/terminology.md#bindings) and ["facet binding"](/docs/design/generics/terminology.md#facet-binding) in the generics terminology doc. ## Details The following design documents have been updated in this proposal to reflect these changes: - [Language design overview](/docs/design/README.md) - [Generics terminology](/docs/design/generics/terminology.md) - [Member access expressions](/docs/design/expressions/member_access.md) Some of these changes have already been implemented in: - [PR #3048: Update Generics terminology document](https://github.com/carbon-language/carbon-lang/pull/3048) - [PR #3061: Update generics overview](https://github.com/carbon-language/carbon-lang/pull/3061) ## Rationale This proposal advances these goals of Carbon: - [Language tools and ecosystem](/docs/project/goals.md#language-tools-and-ecosystem): Having clear and unambiguous terminology is important for making a precise language specification and as well as other design and developer documentation. - [Code that is easy to read, understand, and write](/docs/project/goals.md#code-that-is-easy-to-read-understand-and-write): particularly the sub-goal that "The behavior and semantics of code should be clearly and simply specified whenever possible." ## Alternatives considered The main alternative considered was the status quo. This was discussed: - [starting 2023-Jul-14 in #naming](https://discord.com/channels/655572317891461132/963846118964350976/1129542605538074777) - [2023-Jul-27 in open discussion](https://docs.google.com/document/d/1gnJBTfY81fZYvI_QXjwKk1uQHYBNHGqRLI2BS_cYYNQ/edit?resourcekey=0-ql1Q1WvTcDvhycf8LbA9DQ#heading=h.3tki3lncihf) - [2023-Aug-28 in #naming](https://discord.com/channels/655572317891461132/963846118964350976/1145790947083423777) During those discussions, we also considered "symbolic type" instead of "generic type". This did not work out, though, since it conflicted with the term "symbolic" used as an expression phase. We considered other possibilities: "figurative type", "computed type", "hole type", "open type", and "placeholder type" (though that might be better applied to `auto`). It also came up that we did not want to use the term "generic binding" to mean a compile-time binding, because that term would be better applied to a parameterized binding, see [#naming on 2023-Jul-31](https://discord.com/channels/655572317891461132/963846118964350976/1135704682128494712). Those are not currently supported in Carbon, but are something we are likely to add. For example, [Rust has generic associated types](https://rust-lang.github.io/generic-associated-types-initiative/explainer/motivation.html) as of [v1.65](https://blog.rust-lang.org/2022/10/28/gats-stabilization.html). ================================================ FILE: proposals/p3403.md ================================================ # Change Main//default to an api file [Pull request](https://github.com/carbon-language/carbon-lang/pull/3403) ## Table of contents - [Abstract](#abstract) - [Problem](#problem) - [Background](#background) - [Proposal](#proposal) - [Rationale](#rationale) - [Alternatives considered](#alternatives-considered) - [Default to `Main//default impl` instead of `Main//default api`](#default-to-maindefault-impl-instead-of-maindefault-api) ## Abstract When there is no `package` directive, default to `Main//default api` instead of `Main//default impl`. This has two user-visible consequences: - The extension will be `.carbon`, not `.impl.carbon`. - Only one `Main//default api` is allowed. ## Problem [Code and name organization](/docs/design/code_and_name_organization/#libraries) says: - API filenames must have the `.carbon` extension. They must not have a `.impl.carbon` extension. - Implementation filenames must have the `.impl.carbon` extension. The `Main//default` library is currently specified as an `impl` file. This means that it should be something like `main.impl.carbon`. ## Background More generally in Carbon, a single-file library can be an `api` file, not an `impl` file. This comes as a side-effect from `impl` files implicitly importing the `api`, which would fail if there were an `impl` file without an `api`. On the other hand, there is no requirement that an `api` file have an `impl`. Proposal [#2550: Simplified package declaration for the `Main` package](https://github.com/carbon-language/carbon-lang/pull/2550) chose `impl` as the default, providing an empty `api` file that cannot otherwise be imported. The proposal doesn't provide rationale for this choice; it likely wasn't considered key to the proposal. In C++, we often see `main.cpp`. This might be where the `impl` choice for `Main//default` comes from, as it has a more equivalent feel. ## Proposal Omitting the package directive means `Main//default api`, rather than `impl`. As a consequence: - The `.carbon` extension applies instead of the `.impl.carbon` extension. - An `api` can only be defined once, so there is a limit of one such file per executable. Mentions of `Main//default api` as being an empty file are removed. The rules preventing use of `Main//default` in `package`, `library`, or `import` remain. The library can only be defined by omission of `package` and `library`, and cannot be imported. ## Rationale - [Code that is easy to read, understand, and write](/docs/project/goals.md#code-that-is-easy-to-read-understand-and-write) - Writing `Run` logic in a `main.carbon` file is expected to be more intuitive than `main.impl.carbon`. ## Alternatives considered ### Default to `Main//default impl` instead of `Main//default api` `Main//default impl`, the status quo, is now a declined alternative. Key considerations are: - Using `Main//default api` is consistent with single-file libraries in other contexts. - `main.carbon` is preferred over `main.impl.carbon`, and using `Main//default api` allows the `main.carbon` extension without special-casing the file extension choices. - The special-case definition of `Main//default api` as an empty file is no longer needed. - Being able to have multiple `Main//default impl` files is of limited utility. `Run` could only be defined in one such file, and the `Main//default api` was defined as empty so no sharing was allowed. ================================================ FILE: proposals/p3407.md ================================================ # Clarify name bindings in namespaces. [Pull request](https://github.com/carbon-language/carbon-lang/pull/3407) ## Table of contents - [Abstract](#abstract) - [Problem](#problem) - [Background](#background) - [Proposal](#proposal) - [Rationale](#rationale) - [Alternatives considered](#alternatives-considered) - [Allow prefixing a tuple binding pattern with a namespace](#allow-prefixing-a-tuple-binding-pattern-with-a-namespace) - [Allow binding patterns to declare names in multiple namespaces](#allow-binding-patterns-to-declare-names-in-multiple-namespaces) - [Allow declaring names in namespaces not owned by the current scope](#allow-declaring-names-in-namespaces-not-owned-by-the-current-scope) - [Allow declaring namespaces in scopes other than the file scope](#allow-declaring-namespaces-in-scopes-other-than-the-file-scope) ## Abstract - Require namespace members be declared in the same name scope as the namespace is declared. - Declaring namespaces outside file scope is disallowed, so this means only at file scope. - Allow binding patterns to directly declare names in namespaces. - Disallow introducing bindings in different namespaces in the same pattern. ## Problem While the trivial case of `class NS.C` seems to be supported as a consequence of proposal [#107: Code and name organization](https://github.com/carbon-language/carbon-lang/pull/107), it lacks detail. For example, there are a couple syntactic options when binding multiple names. Also, there's no clear decision around code such as: ```carbon namespace NS; class ClassT { // Is this a class member accessed through `NS`, or a file scope member inside // `NS`? What is its lifetime? var NS.a: i32 = 0; } ``` This proposal mainly aims to remove ambiguities. ## Background Namespaces are covered in [code and name organization](/docs/design/code_and_name_organization/#namespaces). Binding patterns are covered in [pattern matching](/docs/design/pattern_matching.md#binding-patterns). There's some discussion of `var` in [values, variables, and pointers](/docs/design/values.md), but it's specific to locals. That doesn't address other use cases, such as globals or member variables. ## Proposal When used to declare names in binding patterns, as in `var` or `let`, all names must be in the same namespace. `namespace` members must be declared from within the same scope that declared the `namespace`. There was uncertainty about whether namespaces could be declared outside of file scopes; for now, disallow it. See the changes to [code and name organization](/docs/design/code_and_name_organization/#namespaces) for reference. ## Rationale - [Code that is easy to read, understand, and write](/docs/project/goals.md#code-that-is-easy-to-read-understand-and-write) - Requiring that declarations of multiple names use `NS.a` syntax is consistent with the single variable case. - Requiring namespace members be declared while in the same name scope as the namespace itself makes lifetimes clearer. ## Alternatives considered ### Allow prefixing a tuple binding pattern with a namespace We could use the namespace to prefix the binding tuple. For example: ```carbon var NS.(a: i32, b: i32) = (3, 4); ``` It's rare that we would have a single statement declare multiple names. As a consequence, the separation of the namespace qualifier from the declared identifier might end up unique to this syntax. In that context, we prefer `NS.a` for consistency with other cases, such as `class NS.class`. ### Allow binding patterns to declare names in multiple namespaces We could allow binding patterns to declare names in multiple namespaces. For example: ```carbon namespace NS; var (NS.a: i32, b: i32) = InitData(); ``` Mixing namespaces could be confusing: for example, `b` could be misunderstood to be declared in `NS`. We lack data that would demonstrate benefits to offset that. We disallow mixing namespaces in a single declaration for simplicity. ### Allow declaring names in namespaces not owned by the current scope We could allow declaring names in namespaces not owned by the current scope. For example: ```carbon namespace NS; class ClassT { var NS.val: i32; class NS.ChildT {} } ``` Here, `package.NS.val` would be a global, but `ClassT.NS.val` looks more like an instance member. It's also unclear whether `ClassT.NS.val` (or `instance.NS.val`) could be used to reference the produced variable, since `NS` is not inside `ClassT`'s name scope. The naming problems extend to non-binding declarations such as `NS.ChildT`, too. Disallowing using namespaces to cross name scopes is consistent with rules that generally prevent declaring names in other name scopes, such as: ```carbon class A { class B { // `C` must be declared directly inside `A`. class A.C; } } // `D` must be declared within `A`, even if separately defined. class A.D {} ``` Both the `namespace` declaration and names declared within the `namespace` must be written in the same name scope. This avoids name lookup ambiguities, and builds consistency in name scope boundaries across declarations. ### Allow declaring namespaces in scopes other than the file scope We could allow declaring namespaces in scopes other than the file scope. It's ambiguous what path should have resulted from proposal [#107: Code and name organization](https://github.com/carbon-language/carbon-lang/pull/107), although examples all focus on file scope, so other scopes weren't carefully considered. It might prove useful in some situations. For example, perhaps a complex class would find a member namespace useful: ```carbon class Complex { namespace OptionSet1; class OptionSet1.MemberClassA; class OptionSet1.MemberClassB; namespace OptionSet2; class OptionSet2.MemberClassC; class OptionSet2.MemberClassD; namespace Vars; var Vars.a; } ``` This proposal takes a stance against declaring namespaces other than the file scope because: - Proposal [#107: Code and name organization](https://github.com/carbon-language/carbon-lang/pull/107) only mentions file scope namespaces, implicitly disallowing it in other scopes. - Disallowing namespaces in other scopes is consistent with C++. If allowed, it would be necessary to decide whether `Complex.Vars.a` would have instance or global lifetime. For now, namespaces may only be declared at file scope, which gives consistency with C++. This decision may be reevaluated in a future proposal. ================================================ FILE: proposals/p3532.md ================================================ # Focus implementation effort on the toolchain [Pull request](https://github.com/carbon-language/carbon-lang/pull/3532) ## Table of contents - [Abstract](#abstract) - [Problem](#problem) - [Background](#background) - [Proposal](#proposal) - [Details](#details) - [Alternatives considered](#alternatives-considered) - [Do nothing](#do-nothing) - [Front-load porting the functionality and output](#front-load-porting-the-functionality-and-output) ## Abstract Proposal to focus implementation effort for the next 1-2 years on the Carbon toolchain instead of the explorer. This will impact the explorer in a few ways: - We will keep the explorer's code in place, building, and passing its basic tests. It can remain a good baseline for exploring Carbon's language features. - We won't prioritize expanding the explorer's coverage of Carbon features or other improvements -- it is good enough for what we need until the toolchain catches up. - We will stop actively fuzzing and expanding test coverage for the explorer. - Eventually, when we want to resume work on the explorer, we'll evaluate the best platform to build on -- the current explorer codebase or on top of the toolchain's semantic IR. ## Problem Carbon is still a relatively small project, and we need to avoid spreading ourselves out too much over too many efforts. Currently, we're maintaining two implementation codebases -- the Carbon Explorer and the Carbon Toolchain. While historically these have served importantly different needs of the project, the current state has changed. We're now moving slower as a consequence of spreading out our energy across both. ## Background Originally, the Carbon Explorer served two major purposes: 1. A high-level or "abstract machine" executable semantic model for the design of the language. 2. A rapid prototyping platform with a generated parser and maximally simple & traditional internal architecture (ASTs, etc.). The first purpose and use case remains extremely important and something that we should support. However, long-term it may make more sense to build on the same core internal representation as the toolchain in order to avoid duplicated effort and maximize its utility. We'll understand the tradeoffs there better once the toolchain is similarly feature complete. The second use case is no longer critical. At the time, the toolchain was nascent and using a highly experimental architecture with many unknowns. It was hard to be confident it would work at all, much less add a feature to it. We also had a large number of design features for Carbon that needed to have some form of implementation experience in order to validate the designs themselves. The current state of the project is very different. The driving purpose of prototyping many of the core language features, from inheritance, to generics, to expression category, has been achieved. While there are large designs that are not yet in the explorer, we have proven out the most critical components and gotten critical implementation feedback. The toolchain is also rapidly maturing and its core architecture is holding up well. While implementing a new feature on top of the toolchain's architecture is significantly more expensive than on the explorer's architecture, it isn't starting from nothing and the most uncertain aspects of the core architecture and design have become concrete. It is also increasingly necessary for us to get features implemented here so that we can evaluate them in a realistic compilation context and integrate them with C++ interop which we only anticipate to be able to build in the toolchain architecture. The toolchain has also developed a Semantics IR that is an especially interesting potential platform for building support for the executable semantics use case. When the toolchain reaches feature coverage and maturity, it seems important to carefully consider whether that's the best approach to take, and what the tradeoffs are there. ## Proposal We should focus all of our efforts for the next 1-2 years on the toolchain, in order to make as much progress as possible. This in turn is likely to give us the best evaluation of the Carbon experiment in the shortest time frame. We should keep the Explorer's code in place, building, and passing its basic regression tests because the built artifacts of the Explorer remain really valuable given its coverage of our design's feature sets. But we shouldn't take on new work, or drive significant fuzzing, refactoring, or code cleanups in the explorer. We should also explicitly consider re-building the core functionality of the Explorer's abstract machine semantic execution on top of the toolchain's Semantics IR model. If this is the right approach given all the tradeoffs, it should still leverage the existing work done and lessons learned on the Explorer for how to model these semantics and how to render them in an understandable way for users. This is another reason why we should keep all of the code in place, building and passing its tests, until we complete this. While the code itself is unlikely to just directly port across in this way, having the artifacts ensures we'll have a good point of comparison and reference for the ideas and designs. We also may find that it isn't the right direction, and instead resume work on top of the explorer's model directly. ## Details The short term practical details of this proposal are essentially: - Update all of our documentation to direct contributors to the toolchain rather than the Explorer. - Document that the explorer's code is largely an archive and we're not planning to do significant bug fixes, much less feature development in it. - Document that we plan to eventually rebuild the explorer's core functionality on top of the toolchain's semantic model, once that model is sufficiently feature complete. And that we will _not_ remove the explorer's code until that replacement is ready. - Close the various bugs open for working on the explorer with links to this proposal & documentation updates. - Close open PRs and new PRs for the explorer linking to this proposal for details. Note that closed PRs aren't deleted and so we can revisit them if our priorities change and they end up remaining relevant. - Update the issue form to make it clear that we're not planning to do more work on the explorer codebase as-is. Long term, we need to plan to figure out how we want to pursue the core functionality of the explorer, whether in its current implementation or on top of the toolchain's Semantic IR model. Even if there isn't much code that can be directly reused, we should still heavily learn from and incorporate the relevant ideas of the design of the explorer's abstract machine model, output rendering, and test suite. ## Alternatives considered ### Do nothing We could simply leave things in the status-quo. However, this has the serious downside that the contributors to Carbon are sufficiently stretched currently that they are foregoing work on the explorer already. In some ways, at the current size of the project, it isn't clear that we can sustain the status quo in practice even if we don't update our documentation. ### Front-load porting the functionality and output We could immediately try to port the functionality and output so that the explorer tooling and workflow is immediately available on top of the toolchain's semantic representation. Unfortunately there is a significant amount of implementation work left for the toolchain to catch up in terms of feature completeness with the explorer. We need to complete the functionality before we can really pursue this path. And this proposal is trying to maximize how much energy the project can devote to that. Beyond this fundamental limit, the current project roadmap and priorities focus on getting C++ interop working with the toolchain above expanding functionality of the explorer. As a consequence, it seems better to focus on that priority in the toolchain and to revisit rebuilding the explorer on top of the toolchain's semantic model later, at least after the main interop goals are achieved. At that point, it is also likely that most if not all of the feature gaps will have been closed. The proposed approach to essentially archiving the explorer code seems like the best way to align the effort and energy of the project contributors with these priorities. ================================================ FILE: proposals/p3564.md ================================================ # Roadmap for 2024 and a retrospective for 2023 [Pull request](https://github.com/carbon-language/carbon-lang/pull/3564) ## Table of contents - [Abstract](#abstract) - [Proposal](#proposal) - [Retrospective on 2023](#retrospective-on-2023) ## Abstract We propose a roadmap for 2024 focused on a working Carbon toolchain that supports Carbon ↔ C++ interop. ## Proposal We propose the following goals for 2024: - Implementing enough in the toolchain to build meaningful code on the Carbon side of interop. - Teaching the toolchain to compile enough C++ code (by way of Clang) to build meaningful code on the C++ side of interop. - Implementing the interop itself between Carbon and C++ code. - Sharing various aspects of this with the broader Carbon, C++, and open source communities. This focuses significantly more on implementation than we have done in previous years, which is a response to feedback that folks looking at Carbon and trying to evaluate or contribute are increasingly blocked on the lack of implementation support -- either of language features or compiler functionality. We also think the design is far enough along to support this level of implementation, making it an effective time to adopt this more focused roadmap. The full details are in the updated [roadmap] document. [roadmap]: /docs/project/roadmap.md ## Retrospective on 2023 Our [roadmap for 2023] focused on getting both the language and tools ready for evaluation, and building of context in the C++ community to support that evaluation. We executed on this really well, but with mixed results. [Roadmap for 2023]: https://github.com/carbon-language/carbon-lang/blob/840cb1bed7cf9bd57e000cb4a61e986c383d3038/docs/project/roadmap.md On getting ready for evaluation, we made fantastic progress on getting the language (design) ready. We have [milestone definitions], and closed the most critical gaps in the design from the start of the year. The remaining gaps are either lower risk, almost finished, or really need interop to effectively explore. [milestone definitions]: /docs/project/milestones.md The tooling side also made fantastic progress, but has a lot further to go and forms the focus for 2024. We also engaged more deeply with the C++ community on the technical underpinnings, but the response highlighted that until we have (much) more complete tooling, we're going to struggle to deepen our engagement with the external community, and especially struggle to broaden that engagement to larger communities. Both the amount remaining on the tooling side of things, and the fact that it has proved a limiting factor in external engagement, are driving our priorities for this year. Breaking down the specific key results we aimed at for 2023: - A concrete definition of our MVP for evaluation, the 0.1 language - [Done][mvp-milestone]! - Complete design coverage of the 0.1 language's necessary features - High risk, non-interop features are mostly done! The rest is taking a back seat to getting the toolchain & interop ready. - Complete 0.1 language implementation in the Carbon Explorer - There was nuance in this result: we only needed to complete as much design validation as needed to have confidence in the design's cohesion and behavior. We think the explorer got there in 2023 and have [pivoted][toolchain-pivot] fully to the toolchain, so this is somewhat by construction done. - A toolchain that can build a minimal mixed C++ and Carbon program - As called out in the 2023 roadmap, we didn't expect to be able to complete this by the end of 2023, and indeed, this has turned into our focus for 2024. - Give talks at 2-3 conferences covering 3-4 different Carbon topics - We only got to one conference, but we did cover 3+ topics this year. We're hoping as we have a more complete toolchain we'll have more relevant topics for more conferences. [mvp-milestone]: /docs/project/milestones.md#milestone-01-a-minimum-viable-product-mvp-for-evaluation [toolchain-pivot]: /proposals/p3532.md Overall, 2023 was pretty amazing, and we hit some of the most important milestones in the project over the past year. ================================================ FILE: proposals/p3646.md ================================================ # Tuples and tuple indexing [Pull request](https://github.com/carbon-language/carbon-lang/pull/3646) ## Table of contents - [Abstract](#abstract) - [Problem](#problem) - [Background](#background) - [Proposal](#proposal) - [Details](#details) - [Lexing](#lexing) - [Indexes as names](#indexes-as-names) - [Precedence](#precedence) - [Expression operand](#expression-operand) - [Bounds](#bounds) - [Tuple slicing](#tuple-slicing) - [Rationale](#rationale) - [Alternatives considered](#alternatives-considered) - [Alternative lexing rule](#alternative-lexing-rule) - [Decimal indexing restriction](#decimal-indexing-restriction) - [Square bracket notation](#square-bracket-notation) - [Negative indexing from the end of the tuple](#negative-indexing-from-the-end-of-the-tuple) - [Trailing commas](#trailing-commas) ## Abstract Add support for extracting elements of a tuple by their numerical index. Also formally add the well-established basic syntactic and semantic rules for tuples, for which we have had leads issues but no proposal, into the design. ## Problem Currently, the only way to access the elements of a tuple is through pattern matching. While this handles many cases well, it is sometimes desirable to access an element of a tuple more succinctly, especially in cases where only a single element's value is needed. ## Background In Python, tuple indexing is performed using square brackets: ```python tup = (1, 2, 3) # Prints 2. print(tup[1]) ``` In C++, `std::pair` is indexed using `.first` and `.second`, and `std::tuple` is indexed using `std::get`. In Rust and Swift, a tuple is indexed using `.N`, where `N` is a decimal integer literal. - Rust disallows digit separators and base prefixes in `N`, but allows certain literal suffixes [for historical reasons](https://github.com/rust-lang/rust/issues/60210). - Swift disallows digit separators and base prefixes in `N`. `swiftc` allows leading `0` digits, although this appears to be an unintentional consequence of `llvm::StringRef::getAsInteger` allowing them. The current Carbon documentation suggests using `tuple[i]` for tuple indexing, but this has not been the subject of an approved proposal. ## Proposal Formally, we have not yet approved a proposal that says that Carbon has tuple types, although we have approved several proposals that explicitly include support for tuples. So, this proposal does that: tuples exist in Carbon, and are product types with unnamed positional elements. This proposal also updates the design to match other decisions that have been made in leads issues but not captured by a proposal, specifically: - Leads issue #2191 (one-tuples and one-tuple syntax), despite being focused on one-tuples, established the syntax for tuples of all arities. - Leads issue #710 established rules for assignment, comparison, and implicit conversion of tuples. These operations are performed elementwise, with relational comparisons being performed lexicographically. Finally, the main intent of this proposal is to add support for indexing tuples, using the following syntaxes: - `.` _N_, where _N_ is an integer literal, and - `.` `(` _expr_ `)`, where _expr_ is a template constant of integer type. For pointers to tuples, `->` _N_ and `->` `(` _expr_ `)` are also supported. ## Details ### Lexing Multi-level tuple indexing will result in constructs such as `tuple_of_tuples.1.2`. It's important that these are lexed as two tuple indexing operations, not as `tuple_of_tuples` `.` `1.2`, as it would be under the current lexical rules, so a new rule is introduced: - When a `.` or `->` token is followed immediately by a digit, it is lexed as a `.` or `->` token followed by an integer literal, never a real literal. Note that this results in lexing being slightly contextual: the rule to lex a token after a `.` or `->` is different from the rule to lex a token in any other context. However, there is an alternative equivalent formulation of the rule that is not context-sensitive: that `.integer` is treated as a single lexeme that produces two tokens, and likewise for `->integer`. ### Indexes as names The elements of a tuple are treated as if they had decimal integers as their names: `.0`, `.1`, and so on. It is an error to use a different spelling of that integer in a simple member access, because that spelling would not match the element name. For example, `(1, 2).0x0` is invalid, as is `large_tuple.1_2`. These spellings can be used as an [expression operand](#expression-operand) as described below: `(1, 2).(0x0)` and `large_tuple.(1_2)` are both valid. ### Precedence The `.` _N_ syntax has the same precedence as postfix member access syntax, `.` _name_, and can be combined in the same expression: `a.0.x.1` is valid. The `.` `(` _expr_ `)` syntax is not new in this proposal, and continues to have the same precedence as `.` _name_. ### Expression operand In the `.` `(` _expr_ `)` syntax, if the first operand is a tuple and the second operand is a constant of any integer type, the result is the corresponding tuple element, as if specified by a decimal integer literal. This rule is built into the language; the `.` `(` ... `)` notion is not currently overloadable. ### Bounds If the tuple index is not between 0 and one less than the number of elements in the tuple, inclusive, the indexing is invalid. ### Tuple slicing The current skeleton design suggests using `tuple[a .. b]` to slice tuples. For example, `tuple[0 .. 2]` could be used to extract the first two elements of a tuple. Tuple slicing support is not covered by this proposal, but could be added in the future with syntax such as `tuple.(0 .. 2)`. However, note that there is a risk that this syntax may lead to an incorrect theory about how Carbon works: namely, that `tuple.__` gives an element whereas `tuple.(__)` gives a tuple. ## Rationale Goals: - [Language tools and ecosystem](/docs/project/goals.md#language-tools-and-ecosystem) - The lexing rule is relatively simple to implement. Tools such as syntax highlighters can treat `.i` as a distinct kind of token rather than implementing any kind of context-sensitive lexing. - [Software and language evolution](/docs/project/goals.md#software-and-language-evolution) - Consistent use of tuple field indexes can be used to support code that adds new tuple elements over time. - [Code that is easy to read, understand, and write](/docs/project/goals.md#code-that-is-easy-to-read-understand-and-write) - This feature allows tuple access to be written more concisely than pattern matching would allow. - Lexing `.1.2` as four tokens rather than two avoids a surprise that would make chained member access hard to write. - For simple member access, requiring a decimal integer with no digit separators allows the member access to be treated as an element name, making the indexing easier to understand. - [Interoperability with and migration from existing C++ code](/docs/project/goals.md#interoperability-with-and-migration-from-existing-c-code) - This feature provides a migration syntax for existing use of `.first`, `.second`, and `std::get`. The permission to use expressions rather than only literals supports migration of `std::get`. Principles: - [Low context sensitivity](/docs/project/principles/low_context_sensitivity.md). - We look only at the character immediately before a numeric literal to determine whether it is lexed as a tuple index that stops before the next `.` or as a general numeric literal. ## Alternatives considered ### Alternative lexing rule We could lex `.0`, `.1`, ... as a single token rather than as separate `.` and `0`, `1`, ... tokens. This would somewhat simplify the lexing rules, because they would no longer be contextual. We choose to not do this because: - This would be inconsistent with our handling of `struct.fieldname`. - Either `tuple . 0` would be invalid, unlike `struct.fieldname`, or it would need to use a distinct grammar production from `tuple.0`. We could lex an integer literal when the previous token is `.`, regardless of whether the literal follows the `.` immediately. For example, we could treat ```carbon let n: i32 = ((1, 2, 3), 4) . 0.1; ``` as tuple indexing, rather than as a tuple followed by a `.` and a real literal. This is what Swift does. We choose to not do this because: - The `0.1` literal in this case looks like a real literal, not tuple indexing, so this would likely cause surprise for readers. - This would make the context-sensitive lexing be non-local. The chosen rule can be interpreted as lexing `.[0-9]*` as a single lexeme, but forming two tokens from it, whereas this alternative rule would be much more firmly a context-sensitive lexing rule. We could get a similar result in other ways: - We could allowing a real literal after a `.`, and split it into a pair of member accesses when needed. This is [what `rustc` does](https://github.com/rust-lang/rust/pull/71322). - We could lex a real literal as three tokens: an integer token, a `.` token, and a suffix token, and merge them back together in the parser. This is [what `intellij` does](https://github.com/intellij-rust/intellij-rust/commit/f82f6cd68567e574bf1e30f5e0d263ee15d1d36e) when parsing Rust. Note that these approaches are not entirely equivalent to each other. In Rust, for example, the difference is observable in proc macros. Also, using any kind of token merging or splitting approach would result in the token stream not matching the interpretation of the program, which is problematic for tooling. For example, many common Rust syntax highlighters do not properly highlight chained tuple indexing. ### Decimal indexing restriction Carbon follows Rust and Swift in restricting tuple indexes to being decimal integers: ```carbon // OK let a: i32 = (1, 2, 3).0; // Error, invalid index for tuple element. let b: i32 = (1, 2, 3).0x0; ``` This restriction introduces an inconsistency between `.0x0` and `.(0x0)`, and we could easily drop it. However, the restriction allows us to consider `.0`, `.1`, and so on to simply be the names of the tuple elements, analogous to struct field names, and there isn't a clear utility for permitting a base prefix or a digit separator in a tuple index. ### Square bracket notation Instead of `tuple.0` and `tuple.(IndexConstant)`, we could use `tuple[0]` and `tuple[IndexConstant]`. This would result in more consistent syntax for indexing with a constant versus with an expression, but would make accessing an element of a tuple less consistent with accessing an element of a struct. We expect tuple access with a non-literal index to be a rare operation, so the consistency with that syntax seems to have lower value. Also, the use of `.` notation aims to convey the intent of the developer better: we intend `x[n]` notation to be used primarily for _homogenous_ indexing, whereas `.` notation is used for _heterogenous_ access. This also reflects the difference in phase: tuple indexing requires a constant index in the same way that struct member access requires a constant name, whereas array or container indexing would typically be expected to permit a runtime index. The `.N` notation can also be extended to perform member indexing into a struct or class, at least the latter of which would not be reasonable to support with `[]` notation. However, such support is not part of this proposal. Use of `[]` notation has the advantage of reducing visual ambiguity for cases such as `O.0`, `l.0`, and `Z.0`, which might be visually confused with `0.0`, `1.0`, and `2.0`, respectively. However, we're not aware of this being a problem in practice in Rust or Swift, which use this notation, and the same problem exists even without the `.0` suffix: `F(O, l, Z)` may resemble `F(0, 1, 2)`. ### Negative indexing from the end of the tuple We could support `tuple.-1`, or perhaps `tuple.(-1)`, as a notation for "the last element of the tuple", as used for example in Python. We choose not to support this at this time because such notation can be confusing and has awkward edge cases. An off-by-one error, or an attempt to access a one-past-the-start element, will sometimes be accepted and silently do the wrong thing. If a future proposal introduces tuple slicing, it should revisit this question, because this kind of indexing from the end is often desirable when forming a slice. The possibility of using a different notation for this operation should be considered, such as `tuple.(.size - 1)`. ### Trailing commas Carbon permits optional trailing commas in tuples, with mandatory trailing commas for one-tuples. Alternatives to this choice were considered in [leads issue #2191](https://github.com/carbon-language/carbon-lang/issues/2191). ================================================ FILE: proposals/p3720.md ================================================ # Member binding operators [Pull request](https://github.com/carbon-language/carbon-lang/pull/3720) ## Table of contents - [Abstract](#abstract) - [Problem](#problem) - [Background](#background) - [Proposal](#proposal) - [Details](#details) - [Inheritance and other implicit conversions](#inheritance-and-other-implicit-conversions) - [Data fields](#data-fields) - [Generic type of a class member](#generic-type-of-a-class-member) - [Methods](#methods) - [Fields](#fields) - [C++ pointer to member](#c-pointer-to-member) - [Instance interface members](#instance-interface-members) - [Non-instance interface members](#non-instance-interface-members) - [C++ operator overloading](#c-operator-overloading) - [Future work](#future-work) - [Future: tuple indexing](#future-tuple-indexing) - [Future: properties](#future-properties) - [Future: building block for language features such as API extension](#future-building-block-for-language-features-such-as-api-extension) - [Rationale](#rationale) - [Alternatives considered](#alternatives-considered) - [Swap the member binding interface parameters](#swap-the-member-binding-interface-parameters) - [Member binding to references produces a value that wraps a pointer](#member-binding-to-references-produces-a-value-that-wraps-a-pointer) - [Separate interface for compile-time member binding instead of type member binding](#separate-interface-for-compile-time-member-binding-instead-of-type-member-binding) - [Non-instance members are idempotent under member binding](#non-instance-members-are-idempotent-under-member-binding) - [Separate `Result` types for `BindToValue` and `BindToRef`](#separate-result-types-for-bindtovalue-and-bindtoref) - [`BindToValue` is a subtype of `BindToRef`](#bindtovalue-is-a-subtype-of-bindtoref) - [Directly rewrite all calls to interface member functions to method call intrinsics](#directly-rewrite-all-calls-to-interface-member-functions-to-method-call-intrinsics) ## Abstract Define the member binding operation used to compute the result of `x.y`, `p->y`, `x.(C.y)`, and `p->(C.y)` as calling a method from user-implementable interfaces. ## Problem What happens when member binding is performed between an object instance and a member of its type? We'd like to define the semantics in a way that is simple, orthogonal, supports the use cases from C++, allows users to express their intent in code in a natural and predictable way that is consistent with other Carbon constructs, and is consistent with Carbon's goals. Consider a class with a method and a field: ```carbon class C { fn M[self: Self](); var f: i32; } var x: C = {.f = 2}; ``` The expressions `C.M` and `C.f` correspond roughly to [C++ pointers to members](https://en.cppreference.com/w/cpp/language/pointer#Pointers_to_members). They may be used to access the members of `x` using Carbon's [compound member syntax](/docs/design/expressions/member_access.md), as in `x.(C.M)` or `x.(C.f)`. What is their type? Can they be passed to a function separately from the instance of `C` to bind with it? The expression `x.M` on the other hand doesn't have a trivial correspondence in C++ despite being a useful to bind a specific instance and produce a stand-alone callable object. We would like a model that allows `x.M` to be meaningful in a way that is consistent with the existing meaning of `x.f` and generalizes well across different kinds of methods and callables. Another issue is how we clearly delineate the `self` associated with a method signature as separate from the `self` of function values. ## Background Member access has been specified in two proposals: - [Proposal #989: Member access expressions](https://github.com/carbon-language/carbon-lang/pull/989) - [Proposal #2360: Types are values of type `type`](https://github.com/carbon-language/carbon-lang/pull/2360) The results of these proposals is recorded in the ["qualified names and member access" design document](/docs/design/expressions/member_access.md). Notably, there is the process of [instance binding](/docs/design/expressions/member_access.md#instance-binding), that can convert a method into a bound method. This is described as an uncustomizable process, with the members of classes being non-first-class names. With [proposal #3646: Tuples and tuple indexing](https://github.com/carbon-language/carbon-lang/pull/3646), tuple indexing also uses the member-access syntax, except with numeric names for the fields. The currently accepted proposals for functions, most notably [Proposal #2875: Functions, function types, and function calls](https://github.com/carbon-language/carbon-lang/pull/2875), don't support all of the different function signatures for the `Call` interface. For example, it does not support `addr self` or explicit compile-time parameters. That is out of scope of this proposal, and will be addressed separately, and means that `addr self` methods won't be considered here. The difference between functions and methods, however, is in scope. Other languages, such as [C#](https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/delegates/using-delegates) and [D](https://tour.dlang.org/tour/en/basics/delegates), have constructs that represent bound and unbound methods, such as "delegates". ## Proposal We propose that Carbon defines the compound member access operator, specifically `x.(y)`, in terms of rewrites to invoking an interface method, like other operators. There are three different interfaces used, depending on whether `x` is a value expression, a reference expression, or a facet: ```carbon // This determines the type of the result of member binding. It is // a separate interface shared by `BindToValue` and `BindToRef` to // ensure they produce the same result type. We don't want the // type of an expression to depend on the expression category // of the arguments. interface Bind(T:! type) { let Result:! type; } // For a value expression `x` with type `T` and an expression // `y` of type `U`, `x.(y)` is `y.((U as BindToValue(T)).Op)(x)` interface BindToValue(T:! type) { extend Bind(T); fn Op[self: Self](x: T) -> Result; } // For a reference expression `x` using a member binding `var x: T` // and an expression `y` of type `U`, `x.(y)` is // `*y.((U as BindToRef(T)).Op)(&x)` interface BindToRef(T:! type) { extend Bind(T); fn Op[self: Self](p: T*) -> Result*; } // For a facet value, which includes all type values, `T` and // an expression `y` of type `U`, `T.(y)` is // `y.((U as BindToType(T)).Op)()`. interface BindToType(T:! type) { let Result:! type; fn Op[self: Self]() -> Result; } ``` > **Note:** `BindToType` is its own interface since the members of a type are > defined by their values, not by their type. Observe that this means that a > generic function might not use `BindToType` on a symbolic value that was not > known to be a facet, where it would use `BindToType` on the concrete value. The other member access operators -- `x.y`, `x->y`, and `x->(y)` -- are defined by how they rewrite into the `x.(y)` form using these two rules: - `x.y` is interpreted using the existing [member resolution rules](/docs/design/expressions/member_access.md#member-resolution). For example, `x.y` is treated as `x.(T.y)` for non-type values `x` with type `T`. - Simple member access of a facet `T`, as in `T.y`, is not rewritten into the `T.(`\_\_\_`)` form. - `x->y` and `x->(y)` are interpreted as `(*x).y` and `(*x).(y)` respectively. ## Details To use instance members of a class, we need to go through the additional step of _member binding_. Consider a class `C`: ```carbon class C { fn F[self: Self]() -> i32 { return self.x + 5; } fn Static() -> i32 { return 2; } var x: i32; } ``` Each member of `C` with a distinct name will have a corresponding type (like `__TypeOf_C_F`) and value of that type (like `__C_F`). There are two more types for each member function (either static class function or method), though, that [adapt](/docs/design/generics/terminology.md#adapting-a-type) `C` and represent the type of binding that member with either a `C` value or variable. ```carbon class __TypeOf_C_F {} let __C_F:! __TypeOf_C_F = {}; class __Binding_C_F { adapt C; } // and similarly for Static ``` These are the types that result from [instance binding](/docs/design/expressions/member_access.md#instance-binding) an instance of `C` with these member names. They define the bound method value and bound method type of [proposal #2875](/proposals/p2875.md#bound-methods). For example, ```carbon let v: C = {.x = 3}; Assert(v.F() == 8); Assert(v.Static() == 2); var r: C = {.x = 4}; Assert(r.F() == 9); Assert(r.Static() == 2); ``` is interpreted as: ```carbon let v: C = {.x = 3}; Assert((v as __Binding_C_F).(Call(()).Op)() == 8); Assert((v as __Binding_C_Static).(Call(()).Op)() == 2); var r: C = {.x = 4}; Assert((r as __Binding_C_F).(Call(()).Op)() == 9); Assert((r as __Binding_C_Static).(Call(()).Op)() == 2); ``` How does this arise? 1. First the simple member access is resolved using the type of the receiver: \ `v.F` -> `v.(C.F)`, `v.Static` -> `v.(C.Static)`, `r.F` -> `r.(C.F)`, `r.Static` -> `r.(C.Static)`. \ Note that `C.F` is `__C_F` with type `__TypeOf_C_F`, and `C.Static` is `__C_Static` with type `__TypeOf_C_Static`. 2. It then looks at the expression to the left of the `.`: - If it is a facet value, the "member binding to type" (`BindToType`) operator is applied. - If it is a reference expression, the "member binding to reference" (`BindToRef`) operator is applied. - If it is a value expression, the "member binding to value" (`BindToValue`) operator is applied. 3. The result of the member binding has a type that implements the call interface. > **Note:** The current wording in > [member_access.md](/docs/design/expressions/member_access.md) says that > `v.(C.Static)` and `r.(C.Static)` are both invalid, because they don't perform > member name lookup, instance binding, nor impl lookup -- the `v.` and `r.` > portions are redundant. That rule is removed by this proposal. > > Instead, tools such as linters can highlight such code as suspicious on a > best-effort basis, particularly when the issue is contained in a single > expression. Such tools may still allow code that performs the same operation > across multiple statements, as in: > > ```carbon > let M:! auto = C.Static; > v.(M)(); > r.(M)(); > ``` > > Note that if `M` is an overloaded name, it could be an instance member in some > cases and a non-instance member in others, depending on the arguments passed. > This is another reason to delegate this to linters analyzing a whole > expression on a best-effort basis, rather than a strict rule just about member > binding. The member binding operators are defined using three dedicated interfaces -- `BindToValue`, `BindToRef`, and `BindToType` -- [as defined in the "proposal" section](#proposal). These member binding operations are implemented for the types of the class members: ```carbon impl __TypeOf_C_F as BindToValue(C) where .Result = __Binding_C_F { fn Op[unused self: Self](x: C) -> __Binding_C_F { return x as __Binding_C_F; } } // Note that the `Result` type has to match, since // it is an associated type in the `Bind(C)` interface // that both `BindToValue(C)` and `BindToRef(C)` extend. impl __TypeOf_C_F as BindToRef(C) where .Result = __Binding_C_F { fn Op[unused self: Self](p: C*) -> __Binding_C_F* { return p as __Binding_C_F*; } } ``` > **Note:** `BindToType` is used for > [non-instance interface members](#non-instance-interface-members). Those implementations are how we get from `__C_F` with type `__TypeOf_C_F` to `v as __Binding_C_F` or `&r as __Binding_C_F*`, conceptually following these steps: ```carbon // `v` is a value and so uses `BindToValue` v.F() == v.(C.F)() == v.(__C_F)() == __C_F.((__TypeOf_C_F as BindToValue(C)).Op)(v)() == (v as __Binding_C_F)() // `r` is a reference expression and so uses `BindToRef` r.F() == r.(C.F)() == r.(__C_F)() == (*__C_F.((__TypeOf_C_F as BindToRef(C)).Op)(&r))() == (*(&r as __Binding_C_F*))() ``` However, to avoid recursive application of these same rules, we need to avoid expressing this in terms of evaluating `__C_F.(`...`)`. Instead the third step uses an intrinsic compiler primitive, as in: ```carbon // `v` is a value and so uses `BindToValue` v.F() == v.(C.F)() == v.(__C_F)() == inlined_method_call_compiler_intrinsic( , __C_F, (v))() == (v as __Binding_C_F)() // `r` is a reference expression and so uses `BindToRef` r.F() == r.(C.F)() == r.(__C_F)() == (*inlined_method_call_compiler_intrinsic( , __C_F, (&r)))() == (*(&r as __Binding_C_F*))() ``` At this point we have resolved the member binding, and are left with an expression of type `__Binding_C_F` followed by `()`. In the first case, that expression is a value expression. In the second case, it is a reference expression. The last ingredient is the implementation of the call interfaces for these bound types. ```carbon // Member binding with `C.F` produces something with type // `__Binding_C_F` whether it is a value or reference // expression. Since `C.F` takes `self: Self` it can be // used in both cases. impl __Binding_C_F as Call(()) with .Result = i32 { fn Op[self: Self]() -> i32 { // Calls `(self as C).(C.F)()`, but without triggering // member binding again. return inlined_method_call_compiler_intrinsic( , self as C, ()); } } // `C.Static` works the same as `C.F`, except it also // implements the call interfaces on `__TypeOf_C_Static`. // This allows `C.Static()` to work, in addition to // `v.Static()` and `r.Static()`. impl __Binding_C_Static as Call(()) with .Result = i32 { // Other implementations of `Call(())` are the same. fn Op[unused self: Self]() -> i32 { // Calls `C.Static()`, without triggering member binding again. return inlined_call_compiler_intrinsic( , ()); } } impl __TypeOf_C_Static as Call(()) where .Result = i32; ``` Going back to `v.F()` and `r.F()`, after member binding the next step is to resolve the call. As described in [proposal #2875](https://github.com/carbon-language/carbon-lang/pull/2875), this call is rewritten to an invocation of the `Op` method of the `Call(())` interface, using the implementations just defined. Note: - Passing `*(&r as __Binding_C_F*)` to the `self` parameter of `Call(()).Op` converts the reference expression to a value. Note that mutating (`addr self`) methods are [out of scope for this proposal](#background). - The `Call` interface is special. We don't [rewrite](#instance-interface-members) calls to `Call(__).Op` to avoid infinite recursion. ```carbon v.F() == (v as __Binding_C_F)() == (v as __Binding_C_F).((__Binding_C_F as Call(())).Op)() == inlined_method_call_compiler_intrinsic( , v as __Binding_C_F, ()); == inlined_method_call_compiler_intrinsic( , (v as __Binding_C_F) as C, ()) == inlined_method_call_compiler_intrinsic( , v, ()) r.F() == (*(&r as __Binding_C_F*))() == (*(&r as __Binding_C_F*)).((__Binding_C_F as Call(())).Op)() == inlined_method_call_compiler_intrinsic( , *(&r as __Binding_C_F*) , ()); == inlined_method_call_compiler_intrinsic( , *(&r as __Binding_C_F*) as C, ()) == inlined_method_call_compiler_intrinsic( , r , ()) ``` > **Note:** This rewrite results in compiler intrinsics for calling. This is to > show that no more rewrites are applied. ### Inheritance and other implicit conversions Now consider methods of a base class: ```carbon base class B { fn F[self: Self](); virtual fn V[self: Self](); } class D { extend base: B; impl fn V[self: Self](); } var d: D = {} d.(B.F)(); d.(B.V)(); ``` To allow this to work, we need the implementation of the member binding interfaces to allow implicit conversions: ```carbon impl [T:! ImplicitAs(B)] __TypeOf_B_F as BindToValue(T) where .Result = __Binding_B_F { fn Op[self: Self](x: T) -> __Binding_B_F { return (x as B) as __Binding_B_F; } } impl [T:! type where .Self* impls ImplicitAs(B*)] __TypeOf_B_F as BindToRef(T) where .Result = __Binding_B_F { fn Op[self: Self](p: T*) -> __Binding_B_F* { return (p as B*) as __Binding_B_F*; } } ``` This matches the expected semantics of method calls, even for methods of final classes. Note that the implementation of the member binding interfaces is where the `Self` type of a method is used. If that type is different from the class it is being defined in, as considered in [#1345](https://github.com/carbon-language/carbon-lang/issues/1345), that will be reflected in the member binding implementations. ```carbon class C { // Note: not `self: Self` or `self: C`! fn G[self: Different](); } let c: C = {}; // `c.G()` is only allowed if there is an implicit // conversion from `C` to `Different`. let d: Different = {}; // Allowed: d.(C.G)(); ``` results in an implementation using `Different` instead of `C`: ```carbon // `C.G` will only member bind to values that can implicitly convert // to type `Different`. impl [T:! ImplicitAs(Different)] __TypeOf_C_G as BindToValue(T) where .Result = __Binding_C_G; ``` ### Data fields The same `BindToValue` and `BindToRef` operations allow us to define access to the data fields in an object, without any additional changes. For example, given a class with a data member `m` with type `i32`: ```carbon class C { var m: i32; } ``` we want the usual operations to work, with `x.m` equivalent to `x.(C.m)`: ```carbon let v: C = {.m = 4}; var x: C = {.m = 3}; x.m += 5; Assert(x.(C.m) == v.m + v.(C.m)); ``` To accomplish this we will, as before, associate an empty (stateless or zero-sized) type with the `m` member of `C`, that just exists to support the member binding operation. However, this time the result type of member binding is simply `i32`, the type of the variable, instead of a new, dedicated type. ```carbon class __TypeOf_C_m {} let __C_m:! __TypeOf_C_m = {}; impl __TypeOf_C_m as BindToValue(C) where .Result = i32 { fn Op[self: Self](x: C) -> i32 { // Effectively performs `x.m`, but without triggering member binding again. return value_compiler_intrinsic(x, __OffsetOf_C_m, i32) } } impl __TypeOf_C_m as BindToRef(C) where .Result = i32 { fn Op[self: Self](p: C*) -> i32* { // Effectively performs `&p->m`, but without triggering member binding again, // by doing something like `((p as byte*) + __OffsetOf_C_m) as i32*` return offset_compiler_intrinsic(p, __OffsetOf_C_m, i32); } } ``` These definitions give us the desired semantics: ```carbon // For value `v` with type `T` and `y` of type `U`, // `v.(y)` is `y.((U as BindToValue(T)).Op)(v)` v.m == v.(C.m) == v.(__C_m) == v.(__C_m as (__TypeOf_C_m as BindToValue(C))) == __C_m.((__TypeOf_C_m as BindToValue(C)).Op)(v) == value_compiler_intrinsic(v, __OffsetOf_C_m, i32) // For reference expression `var x: T` and `y` of type `U`, // `x.(y)` is `*y.(U as BindToRef(T)).Op(&x)` x.m == x.(C.m) == x.(__C_m) == *__C_m.((__TypeOf_C_m as BindToRef(C)).Op)(&x) == *offset_compiler_intrinsic(&x, __OffsetOf_C_m, i32) // Note that this requires `x` to be a reference expression, // so `&x` is valid, and produces a reference expression, // since it is the result of dereferencing a pointer. ``` The fields of [tuple types](/docs/design/tuples.md) and [struct types](/docs/design/classes.md#struct-types) operate the same way. ```carbon let t_let: (i32, i32) = (3, 6); Assert(t_let.(((i32, i32) as type).0) == 3); var t_var: (i32, i32) = (4, 8); Assert(t_var.(((i32, i32) as type).1) == 8); t_var.(((i32, i32) as type).1) = 9; Assert(t_var.1 == 9); let s_let: {.x: i32, .y: i32} = {.x = 5, .y = 10}; Assert(s_let.({.x: i32, .y: i32}.x) == 5); var s_var: {.x: i32, .y: i32} = {.x = 6, .y = 12}; Assert(s_var.({.x: i32, .y: i32}.y) == 12); s_var.({.x: i32, .y: i32}.y) = 13; Assert(s_var.y == 13); ``` For example, `{.x: i32, .y: i32}.x` is a value `__Struct_x_i32_y_i32_Field_x`, analogous to `__C_m`, of a type `__TypeOf_Struct_x_i32_y_i32_Field_x` (that is zero-sized / has no state), analogous to `__TypeOf_C_m`, that implements the member binding interfaces for any type that implicitly converts to `{.x: i32, .y: i32}`. Note that for tuples, the `as type` is needed since `(i32, i32)` on its own is a tuple, not a type. In particular `(i32, i32)` is not the type of `t_let` or `t_var`. `(i32, i32).0` is just `i32`, and isn't the name of the first element of an `(i32, i32)` tuple. ### Generic type of a class member Given the above, we can now write a constraint on a symbolic parameter to match the names of an unbound class member. There are a two cases: methods and fields. #### Methods Restricting to value methods, since mutating (`addr self`) methods are [out of scope for this proposal](#background), the receiver object may be passed by value. To be able to call the method, we must include a restriction that the result of `BindToValue` implements `Call(())`: ```carbon // `m` can be any method object that implements `Call(())` once bound. fn CallMethod [T:! type, M:! BindToValue(T) where .Result impls Call(())] (x: T, m: M) -> auto { // `x.(m)` is rewritten to a call to `BindToValue(T).Op`. The // constraint on `M` ensures the result implements `Call(())`. return x.(m)(); } ``` This will work with any value method or static class function. This will also work with inheritance and virtual methods, using [the support for implicit conversions of self](#inheritance-and-other-implicit-conversions). ```carbon base class X { virtual fn V[self: Self]() -> i32 { return 1; } fn B[self: Self]() -> i32 { return 0; } } class Y { extend base: X; impl fn V[self: Self]() -> i32 { return 2; } } class Z { extend base: X; impl fn V[self: Self]() -> i32 { return 3; } } var (x: X, y: Y, z: Z); // Respects inheritance Assert(CallMethod(x, X.B) == 0); Assert(CallMethod(y, X.B) == 0); Assert(CallMethod(z, X.B) == 0); // Respects method overriding Assert(CallMethod(x, X.V) == 1); Assert(CallMethod(y, X.V) == 2); Assert(CallMethod(z, X.V) == 3); ``` #### Fields Fields can be accessed, given the type of the field ```carbon fn GetField [T:! type, F:! BindToValue(T) where .Result = i32] (x: T, f: F) -> i32 { // `x.(f)` is rewritten to `f.((F as BindToValue(T)).Op)(x)`, // and `(F as BindToValue(T)).Op` is a method on `f` with // return type `i32` by the constraint on `F`. return x.(f); } fn SetField [T:! type, F:! BindToRef(T) where .Result = i32] (x: T*, f: F, y: i32) { // `x->(f)` is rewritten to `(*x).(f)`, which then // becomes: `*f.((F as BindToRef(T)).Op)(&*x)` // The constraint `F` says the return type of // `(F as BindToRef(T)).Op` is `i32*`, which is // dereferenced to get an `i32` reference expression // which may then be assigned. x->(f) = y; } class C { var m: i32; var n: i32; } var c: C = {.m = 5, .n = 6}; Assert(GetField(c, C.m) == 5); Assert(GetField(c, C.n) == 6); SetField(&c, C.m, 42); SetField(&c, C.n, 12); Assert(GetField(c, C.m) == 42); Assert(GetField(c, C.n) == 12); ``` ### C++ pointer to member In [the generic type of member section](#generic-type-of-a-class-member), the names of members, such as `D.K`, `X.B`, `X.V`, and `C.n`, refer to zero-sized / stateless objects where all the offset information is encoded in the type. However, the definitions of `CallMethod`, `SetField`, and `GetField` do not depend on that fact and will be usable with objects, such as C++ pointers-to-members, that include the offset information in the runtime object state. So we can define member binding implementations for them so that they may be used with Carbon's `.(`**`)` and `->(`**`)` operators. For example, this is how we expect C++ code to call the above Carbon functions: ```cpp struct C { int F() const { return m + 1; } int m; }; int main() { // pointer to data member `m` of class C int C::* p = &C::m; C c = {2}; assert(c.*p == 2); assert(Carbon::GetField(c, p) == 2); Carbon::SetField(&c, p, 4); assert(c.m == 4); // pointer to method `F` of class C int (C::*q)() const = &C::F; assert(Carbon::CallMethod(&c, q) == 5); } ``` ### Instance interface members Instance members of an interface, such as methods, can use this framework. For example, given these declarations: ```carbon interface I { fn F[self: Self](); } class C { impl as I; } let c: C = {}; ``` Then `I.F` is its own value with its own type: ```carbon class __TypeOf_I_F {} let __I_F:! __TypeOf_I_F = {}; ``` That type implements `BindToValue` for any type that implements the interface `I`: ```carbon class __Binding_I_F(T:! I) { adapt T; } impl forall [T:! I] __TypeOf_I_F as BindToValue(T) where .Result = __Binding_I_F(T) { fn Op[self: Self](x: T) -> __Binding_I_F(T) { // Valid since `__Binding_I_F(T)` adapts `T`: return x as __Binding_I_F(T); } } ``` The actual dispatch to the `I.F` method of `C` happens in the implementation of the `Call` interface of this adapter type that is the result of member binding to a value. So, this implementation of `C as I`: ```carbon impl C as I { fn F[self: Self]() { Fanfare(self); } } ``` Results in this implementation: ```carbon impl __Binding_I_F(C) as Call(()) where .Result = () { fn Op[self: Self]() { inlined_method_call_compiler_intrinsic( , self as C, ()); } } ``` A call such as `c.(I.F)()` goes through these rewrites: ```carbon c.(I.F)() == c.(__I_F)() == __I_F.((__TypeOf_I_F as BindToValue(C)).Op)(c)() == (c as __Binding_I_F(C))() == (c as __Binding_I_F(C)).((__Binding_I_F(C) as Call(())).Op)() ``` Which results in invoking the above implementation that will ultimately call `Fanfare(c)`. > **Note:** The `Call` interface gets special treatment and does not get these > rewrites to avoid recursing forever. ### Non-instance interface members Non-instance members use the `BindToType` interface instead. For example, if `G` is a non-instance function of an interface `J`: ```carbon interface J { fn G(); } impl C as J; ``` Again the member is given its own type and value: ```carbon class __TypeOf_J_G {} let __J_G:! __TypeOf_J_G = {}; ``` Since this is a non-instance member, this type implements `BindToType` instead of `BindToValue`: ```carbon class __TypeBinding_J_G(T:! J) {} impl forall [T:! J] __TypeOf_J_G as BindToType(T) where .Result = __TypeBinding_J_G(T) { fn Op[self: Self]() -> __TypeBinding_J_G(T) { return {}; } } ``` So, this implementation of `C as J`: ```carbon impl C as J { fn G() { Fireworks(); } } ``` Results in this implementation: ```carbon impl __TypeBinding_J_G(C) as Call(()) where .Result = () { fn Op[self: Self]() { Fireworks(); } } ``` A call such as `C.(J.G)()` goes through these rewrites: ```carbon C.(J.G)() == C.(__J_G)() == __J_G.((__TypeOf_J_G as BindToType(C)).Op)()() == ({} as __TypeBinding_J_G(C))() == (({} as __TypeBinding_J_G(C)) as Call(())).Op() ``` Which calls the above implementation that calls `Fireworks()`. > **Note:** Member binding for non-instance members doesn't work with > `BindToValue`, we need `BindToType`. Otherwise there is no way to get the > value `C` into the result type. Furthermore, we want `BindToType` > implementation no matter which facet of the type is used in the code. ### C++ operator overloading C++ does not support customizing the behavior of `x.y`. It does support customizing the behavior of `operator*` and `operator->` which is frequently used to support smart pointers and iterators. There is, however, nothing restricting the implementations of those two operators to be consistent, so that `(*x).y` and `x->y` are the same. Carbon instead will only have a single interface for customizing dereference, corresponding to `operator*` not `operator->`. All uses of `x->y` will be rewritten to use `(*x).y` instead. This may cause some friction when porting C++ code where those operators are not consistent. If the C++ code is just missing the definition of `operator*` corresponding to an `operator->`, a workaround would be just to define `operator*`. Other cases of divergence between those operators should be rare, since that is both surprising to users and for the common case of iterators, violates the C++ requirements. If necessary, we can in the future introduce a specific construct just for C++ interop that invokes the C++ arrow operator, such as `CppArrowOperator(x)`, that returns a pointer. **Context:** This was discuseed in [2024-02-29 open discussion](https://docs.google.com/document/d/1s3mMCupmuSpWOFJGnvjoElcBIe2aoaysTIdyczvKX84/edit?resourcekey=0-G095Wc3sR6pW1hLJbGgE0g&tab=t.0#heading=h.5vj8ohrvqjqh) and in [a comment on this proposal](https://github.com/carbon-language/carbon-lang/pull/3720/files#r1507917882). ## Future work ### Future: tuple indexing We can reframe the use of the compound member access syntax for tuple fields as an implementation of member binding of tuples with compile-time integer expressions. The specifics of how this works will be resolved later, once we address how compile-time interacts with interfaces. ### Future: properties If there was a way to implement the member binding operator to only produce values, even when the expression to the left of the `.` was a reference expression, then that could be used to implement read-only properties. This would support something like: ```carbon let Pi: f64 = 3.1415926535897932384626433832795; class Circle { var radius: f64; read_property area -> f64 { return Pi * self.radius * self.radius; } } let c: Circle = {.radius = 2}; Assert(NearlyEqual(c.area, 4 * Pi)); ``` In this example, the member binding of `c` of type `Circle` to `Circle.area` would perform the computation and return the result as an `f64`. If there was some way to customize the result of member binding, this could be extended to support other kinds of properties, such as mutable properties that use `get` and `set` methods to access and mutate the value. The main obstacle to any support for properties with member binding is how the customization would be done. The most natural way to support this customization would be to have multiple interfaces. The compiler would try them in a specified order and use the one it found first. This has the downside of the possibility of different behavior in a checked generic context where only some of the implementations are visible. Our choice to [make the result type the same `Result` associated type of the `Bind` interface](#proposal) independent of whether the `BindToValue` or `BindToRef` interface is used makes this less concerning. Only the phase of the result, not the type, would depend on which implementations were found, similar to [how indexing works](/docs/design/expressions/indexing.md). ### Future: building block for language features such as API extension We should be able to express other language features, such as API extension, in terms of customized member binding, plus possibly some new language primitives. This should be explored in a future proposal. ## Rationale This proposal is about: - Orthogonality: separating the member binding process as a distinct and independent step of using the members of a type. - Being consistent with our overall strategy for defining operators in terms of interface implementations. - Allows member-binding-related functionality to be defined through [library APIs](/docs/project/principles/library_apis_only.md). - Increases uniformity by making member names into ordinary values with types. - Adds expressiveness, enabling member forwarding, passing a member as an argument, and other use cases. These benefits advance Carbon's goals including: - [Language tools and ecosystem](/docs/project/goals.md#language-tools-and-ecosystem): by making it easier to reason about more Carbon entities within Carbon itself, and reducing the number of different concepts that have to be modeled. - [Code that is easy to read, understand, and write](/docs/project/goals.md#code-that-is-easy-to-read-understand-and-write): through increased consistency, uniformity, and expressiveness. - [Interoperability with and migration from existing C++ code](/docs/project/goals.md#interoperability-with-and-migration-from-existing-c-code): by adding support for pointer-to-member constructs. ## Alternatives considered ### Swap the member binding interface parameters We considered instead making the receiver object the `Self` type of the interface, and using the member type as the parameter to the interface. This would have the advantage of matching the order that they appear in the source, consistent with other operators. > **Alternative:** > > ```carbon > // For value `x` with type `T` and `y` of type `U`, > // `x.(y)` is `x.((T as ValueBind(U)).Op)(y)` > interface ValueBind(U:! type) { > extend Bind(U); > fn Op[self: Self](x: U) -> Result; > } > > // For reference expression `var x: T` and `y` of type `U`, > // `x.(y)` is `*x.((T as RefBind(U)).Op)(y)` > interface RefBind(U:! type) { > extend Bind(U); > fn Op[addr self: Self*](x: U) -> Result*; > } > ``` This had some disadvantages however: - The binding property is more associated with the member than the receiver. - Some patterns are more awkward in the alternative syntax. As an example of this last point, consider a function that takes multiple (or even a variadic list) methods to call on a receiver object. With the proposed approach, each method type is constrained: ```carbon // `m1`, `m2`, and `m3` are methods on class `T`. fn Call3Methods[T:! type, M1:! BindToValue(T) where .Result impls Call(()), M2:! BindToValue(T) where .Result impls Call(()), M3:! BindToValue(T) where .Result impls Call(())] (x: T, m1: M1, m2: M2, m3: M3) -> auto; ``` With the alternative, the type of the receiver would be constrained, and the deduced types would be written in a different order: > **Alternative:** > > ```carbon > // `m1`, `m2`, and `m3` are methods on class `T`. > fn Call3MethodsAlternative1 > [M1:! type, M2:! type, M3:! type, > T:! ValueBid(M1) & ValueBind(M2) & ValueBind(M3) > where .(ValueBind(M1).Result) impls Call(()) > and .(ValueBind(M2).Result) impls Call(()) > and .(ValueBind(M3).Result) impls Call(())] > (x: T, m1: M1, m2: M2, m3: M3) -> auto; > ``` Or, the constraints can be moved to the method types at the cost of additional length: > **Alternative:** > > ```carbon > // `m1`, `m2`, and `m3` are methods on class `T`. > fn Call3MethodsAlternative2 > [T:! type, > M1:! type where T impls (ValueBind(.Self) where .Result impls Call(())), > M2:! type where T impls (ValueBind(.Self) where .Result impls Call(())), > M3:! type where T impls (ValueBind(.Self) where .Result impls Call(()))] > (x: T, m1: M1, m2: M2, m3: M3) -> auto; > ``` ### Member binding to references produces a value that wraps a pointer Consider a mutating method on a class: ```carbon class Counter { var count: i32 = 0; fn Increment[addr self: Self*]() { self->count += 1; } } var c: Counter = {}; ``` This proposal says `c.Increment` is a reference expression with a type that adapts `Counter`. For `c.Increment()` to affect the value of `c.count`, there needs to be some way for the `Call` operator to mutate `c`. The current definition of `Call` takes `self` by value, though, so this doesn't work. Addressing this is [out of scope of the current proposal](#background). We could instead make `c.Increment` be a value holding `&c`. That would allow `Call` to work even when taking `self` by value. This is the solution likely implied by the current [proposal #2875](/proposals/p2875.md#bound-methods), though that proposal does not say what the bound method type is at all. It leaves two other problems, however: - We will still need a way to support function objects that are mutated by calling them. This comes up, for example, with C++ types that define `operator()`. - We want the proposed behavior when member binding a [field](#data-fields). As a result, it would be better to evaluate this alternative later as part of considering mutation in calls. ### Separate interface for compile-time member binding instead of type member binding The first proposed way to handle [non-instance interface members](#non-instance-interface-members) was in [the #typesystem channel on Discord on 2024-03-07](https://discord.com/channels/655572317891461132/708431657849585705/1215405895752618134). The suggestion was to have a `CompileBind` interface used for any compile-time value to the left of the `.`. It would have access to the value, which is needed when accessing the members of a type. We eventually concluded that the special treatment was specifically needed for types, not all compile-time values. The [insight](https://docs.google.com/document/d/1s3mMCupmuSpWOFJGnvjoElcBIe2aoaysTIdyczvKX84/edit?resourcekey=0-G095Wc3sR6pW1hLJbGgE0g&tab=t.0#heading=h.1k8r5fhfwyh6) was that types are special because their members are defined by their values, not by their type ([which is always `type`](https://github.com/carbon-language/carbon-lang/pull/2360)). ### Non-instance members are idempotent under member binding In the current proposal, member binding of non-instance members results in an adapter type, the same as an instance member. For example, ```carbon class C { fn Static() -> i32; } ``` is translated into something like: > **Current proposal:** > > ```carbon > class __TypeOf_C_Static {} > let __C_Static:! __TypeOf_C_Static = {}; > > class __Binding_C_Static { > adapt C; > } > > impl __TypeOf_C_Static as BindToValue(C) > where .Result = __Binding_C_Static; > > impl __TypeOf_C_Static as BindToRef(C) > where .Result = __Binding_C_Static; > ``` An alternative is that member binding of a non-instance member is idempotent, so there is no `__Binding_C_Static` type and `BindToValue(C)` results in a value of type `__TypeOf_C_Static` instead: > **Alternative:** > > ```carbon > class __TypeOf_C_Static {} > // Might need to be a `var` instead? > let __C_Static:! __TypeOf_C_Static = {}; > > impl __TypeOf_C_Static as BindToValue(C) > where .Result = __TypeOf_C_Static; > impl __TypeOf_C_Static as BindToRef(C) > where .Result = __TypeOf_C_Static; > ``` There are a few concerns with this alternative: - This is less consistent with the instance member case. - There would be a discontinuity when adding an instance overload to a name that was previously only a non-instance member. - Member binding to a reference is trickier, since it would have to return the address of an object of type `__TypeOf_C_Static`. Perhaps a global variable? - The current proposal rejects `v.(v.(C.Static))`, which is desirable. This was discussed in [this comment on #3720](https://github.com/carbon-language/carbon-lang/pull/3720/files#r1513681915). ### Separate `Result` types for `BindToValue` and `BindToRef` An earlier iteration of this proposal had separate `Result` associated types for `BindToValue` and `BindToRef`, as in: ```carbon interface BindToValue(T:! type) { let Result:! type; fn Op[self: Self](x: T) -> Result; } interface BindToRef(T:! type) { let Result:! type; fn Op[self: Self](p: T*) -> Result*; } ``` However, this results in the type of a member binding depending on the what [category](/docs/design/values.md#expression-categories) the expression to the left of the dot has. This could change the interpretation of code using [indexing](/docs/design/expressions/indexing.md), such as an expression like `a[b].F()`, when the type of `a` is changed from or to a checked generic. This is because the the expression is legal as long as the type of `a` implements `IndexWith(typeof(b))`, but category of `a[b]` depends on whether the type of `a` is known to implement `IndirectIndexWith(typeof(b))`. To avoid this problem, we make the result type of the member binding the same whether it is binding to a value or reference. See [this comment on #3720](https://github.com/carbon-language/carbon-lang/pull/3720/files/99fb69aaaa24bd502d71cd4259f37cfa8346b244#r1597317217). ### `BindToValue` is a subtype of `BindToRef` We could make `BindToValue` be a subtype of `BindToRef`, as suggested in [this comment on #3720](https://github.com/carbon-language/carbon-lang/pull/3720/files/99fb69aaaa24bd502d71cd4259f37cfa8346b244#r1597317217). This would be a step beyond just saying they have to have the same `Result` type, which is achieved by that type being defined in the `Bind` interface they both extend. This approach would rule out the use case where value binding _computes_ a new value rather than returning an existing one -- that is, a read-only property. That use case isn't currently well supported by this proposal -- while you can make `x.ComputeSize` work when `x` is a value expression, you can't make it work when `x` is a reference expression. However, that use case can be supported with the approach described in [future work](#future-properties). ### Directly rewrite all calls to interface member functions to method call intrinsics In this proposal, the `Call` interface is given special treatment, in that invoking its method is rewritten into a primitive operation rather than going through the customizable member binding that other interfaces use. This is described in the [Details](#details) and [Instance interface members](#instance-interface-members) sections. In a comment on #3720 ([1](https://github.com/carbon-language/carbon-lang/pull/3720#discussion_r1597324225), [2](https://github.com/carbon-language/carbon-lang/pull/3720/files#r1597324225)), we considered the possibility that invoking any interface member would be directly rewritten into a primitive operation. We realized the downside of this approach in [open discussion on 2024-05-16](https://docs.google.com/document/d/1s3mMCupmuSpWOFJGnvjoElcBIe2aoaysTIdyczvKX84/edit?resourcekey=0-G095Wc3sR6pW1hLJbGgE0g&tab=t.0#heading=h.3qlpp5e56u46), that this would not allow interface members to support overloading. ================================================ FILE: proposals/p3762.md ================================================ # Merging forward declarations [Pull request](https://github.com/carbon-language/carbon-lang/pull/3762) ## Table of contents - [Abstract](#abstract) - [Problem](#problem) - [Background](#background) - [Prior discussion](#prior-discussion) - [Forward declarations in current design](#forward-declarations-in-current-design) - [ODR (One definition rule)](#odr-one-definition-rule) - [Requiring matching declarations to merge](#requiring-matching-declarations-to-merge) - [Proposal](#proposal) - [Details](#details) - [`extern` keyword](#extern-keyword) - [When declarations are allowed](#when-declarations-are-allowed) - [No forward declarations after declarations](#no-forward-declarations-after-declarations) - [Files must either use an imported declaration or declare their own](#files-must-either-use-an-imported-declaration-or-declare-their-own) - [Libraries cannot both define an entity and declare it `extern`](#libraries-cannot-both-define-an-entity-and-declare-it-extern) - [`impl` files with a forward declaration must contain the definition](#impl-files-with-a-forward-declaration-must-contain-the-definition) - [Type scopes may contain both a forward declaration and definition](#type-scopes-may-contain-both-a-forward-declaration-and-definition) - [Using `extern` declarations in `extern impl`](#using-extern-declarations-in-extern-impl) - [`impl` lookup involving `extern` types](#impl-lookup-involving-extern-types) - [Modifier keywords](#modifier-keywords) - [Rationale](#rationale) - [Alternatives considered](#alternatives-considered) - [Other modifier keyword merging approaches](#other-modifier-keyword-merging-approaches) - [No `extern` keyword](#no-extern-keyword) - [Looser restrictions on declarations](#looser-restrictions-on-declarations) - [`extern` naming](#extern-naming) - [Default `extern` to private](#default-extern-to-private) - [Opaque types](#opaque-types) - [Require a library provide its own `extern` declarations](#require-a-library-provide-its-own-extern-declarations) - [Allow cross-package `extern` declarations](#allow-cross-package-extern-declarations) - [Appendix](#appendix) - [Migration of C++ forward declarations](#migration-of-c-forward-declarations) ## Abstract - Add the `extern` keyword for forward declarations in libraries that don't provide the definition. - Treat repeated forward declarations as redundant. - Allow them when they prevent a dependence on an imported name. - Clarify rules for when modifier keywords should be on forward declarations and definitions. ## Problem A forward declaration can be merged with a definition when they match. However, there is ambiguity about behavior: - Whether a forward declaration can be repeated, including after a definition. - Whether a keyword is required to make a forward declaration separate from the implementing library. - Whether modifier keywords need to match between forward declarations and definitions. ## Background ### Prior discussion - [Proposal #1084: Generics details 9: forward declarations](https://github.com/carbon-language/carbon-lang/pull/1084) covered specifics for `impl` and `interface`. - [Issue #1132: How do we match forward declarations with their definitions?](https://github.com/carbon-language/carbon-lang/issues/1132) - [Issue #3384: are abstract / base specifiers permitted or required on class forward declarations?](https://github.com/carbon-language/carbon-lang/issues/3384) asks one question about which keywords exist on declarations versus definitions. There has also been discussion about what happens for member functions and other situations. At present there is no decision on #3384. - The [2023-11-21 toolchain meeting](https://docs.google.com/document/d/1s3mMCupmuSpWOFJGnvjoElcBIe2aoaysTIdyczvKX84/edit?resourcekey=0-G095Wc3sR6pW1hLJbGgE0g&tab=t.0#heading=h.y377x4v44h2c) had some early discussion regarding forward declarations and the need for a syntactic difference, which evolved to `extern` here. - [On Discord's #syntax on 2024-01-29](https://discord.com/channels/655572317891461132/709488742942900284/1201693766449102878), there was discussion about the question "What is the intended interaction between declaration modifiers and qualified declaration names?" - [On Discord's #syntax on 2024-02-23](https://discord.com/channels/655572317891461132/709488742942900284/1210694116955000932), there was discussion that started about how `alias` would resolve forward declaration conflicts. This continued to forward declarations in general, and has discussion about `extern` use. - This discussion led to a substantial fraction of this proposal. ### Forward declarations in current design Example rules for forward declarations in the current design: - [High-level](https://github.com/carbon-language/carbon-lang/blob/trunk/docs/design/README.md#declarations-definitions-and-scopes) - [Classes](https://github.com/carbon-language/carbon-lang/blob/trunk/docs/design/classes.md#forward-declaration) - [Functions](https://github.com/carbon-language/carbon-lang/blob/trunk/docs/design/functions.md#function-declarations) - Generics: - [`impl`](https://github.com/carbon-language/carbon-lang/blob/trunk/docs/design/generics/details.md#forward-impl-declaration) - [`interface`](https://github.com/carbon-language/carbon-lang/blob/trunk/docs/design/generics/details.md#declaring-interfaces-and-named-constraints) - [Matching and agreeing](https://github.com/carbon-language/carbon-lang/blob/trunk/docs/design/generics/details.md#matching-and-agreeing) ### ODR (One definition rule) [C++'s ODR](https://en.cppreference.com/w/cpp/language/definition) requires each entity to have only one definition. C++ has trouble detecting issues at compile time, and linking has trouble catching linker issues too. ODR violation detection is an active problem (https://maskray.me/blog/2022-11-13-odr-violation-detection). This similarly applies to Carbon. In Carbon, only one library can define an entity; other libraries can make forward declarations of it, but those don't constitute a definition. Part of the goal in declaration syntax (`extern` in particular) is to be able to better diagnose ODR violations based on declarations. ### Requiring matching declarations to merge When two declarations have the same name, either within the same file or through imports, they need to "match", or will be diagnosed as a conflict. This is laid out at under [Matching and agreeing](https://github.com/carbon-language/carbon-lang/blob/trunk/docs/design/generics/details.md#matching-and-agreeing) in generics. This proposal does not significantly affect these type and name restrictions. For example: - `fn F(); fn F() {}` could merge because the function signatures match, forward declaring a function is valid, and only one definition is provided. - `fn F(); fn F(x: i32);` won't merge because they differ in parameters, resulting in a diagnostic. ## Proposal 1. Add `extern` declarations. 1. `extern` is used to mark forward declarations of entities defined in a different library. - The defining library must declare the entity as public in an `api` file. - It is invalid for the defining library to declare as `extern`. - `extern` must only be used for declarations in libraries that don't contain the definition. 2. In modifiers, `extern` comes immediately after access control keywords, and before other keywords. 3. Declaring an entity as `extern` anywhere in a file means it must be used as `extern` _throughout_ that file. - The entity cannot be used _at all_ prior to the `extern` declaration, even if there is an imported declaration. - This allows refactoring to add and remove declarations without impacting files that have an `extern`. 4. If a library declares an entity as non-`extern` in either the `api` or `impl`, it is invalid to declare the same entity as `extern` elsewhere within the library. 2. An entity may only be forward declared once in a given file. A forward declaration is only allowed before a definition. 3. Modifier keywords will be handled on a case-by-case basis for merging declarations. ## Details ### `extern` keyword An `extern` modifier keyword is added. It modifies a forward declaration to indicate that the entity is not defined by the declaring library. A library using `extern` to declare an entity cannot define that entity. Only the library which defines an entity can omit `extern`, and omitting `extern` means it must provide a definition. An `extern` declaration forms an entity which has no definition or storage. For example: - `extern fn` forms a function that can be called. - `extern class` forms an incomplete type. - `extern interface` forms an undefined interface. - `extern var` or `extern let` will bind the name without allocating storage. Initializers are disallowed. The `extern` keyword is invalid on declarations that have _only_ a declaration syntax and lack storage, such as `alias` or `namespace`. It is only valid on namespace-scoped names; it is invalid on type-scoped names (`class Foo { extern fn Member(); }`). In declaration modifiers, `extern` comes immediately after access control keywords, and before other keywords. For example, `private extern class B;`. At present, when `extern` is on a declaration, only access modifiers are valid (see [Modifier keywords](#modifier-keywords)). ### When declarations are allowed When considering whether a declaration is allowed, we apply the rules: 1. A declaration should always add new information. - No declarations after a definition. 2. Only one library can declare an entity without `extern`. 3. Support moving declarations between already-imported `api` files without affecting compilation of client libraries. #### No forward declarations after declarations In a file, a forward declaration must never follow a forward declaration or definition for the same entity. For example: ```carbon class A { ... } // Invalid: Disallowed after the definition. class A; class B; // Invalid: Disallowed due to repetition. class B; class C; // Valid: Allowed because the definition is added. class C { ... } ``` #### Files must either use an imported declaration or declare their own In a file, if a declaration or definition of an entity is imported, the file must choose between either using that version or declaring its own. It cannot do both. For example: ```carbon package Foo library "a" api; class C { ... } ``` ```carbon package Foo library "b" api; import library "a"; extern class C; // Valid: Uses the incomplete type of the extern declaration. fn Foo(c: C*); ``` ```carbon package Foo library "c" api; import library "a"; // Valid: Uses the complete type of the imported definition. fn Foo(c: C); ``` ```carbon package Foo library "d" api; import library "a"; fn Foo(c: C); // Invalid: `Foo` used the imported `C`, so an `extern` declaration of `C` is now // invalid. extern class C; ``` #### Libraries cannot both define an entity and declare it `extern` In a library, if the `impl` defines an entity, the `api` must not use `extern` when declaring it. For example: ```carbon package Wiz library "a" api; extern class C; ``` ```carbon package Wiz library "a" impl; // Invalid: The `api` file declared `C` as `extern`. class C { ... } ``` In a library, the `api` might make an `extern` declaration that the `impl` imports and uses the definition of. This is consistent because the `impl` file is not declaring the entity. #### `impl` files with a forward declaration must contain the definition In an `impl` file, if the `impl` file forward declares an entity, it must also provide the definition. In libraries with multiple `impl` files, this means that using an entity in one `impl` file when it's defined in a different `impl` file requires a (possibly `private`) forward declaration in the `api` file. An `extern` declaration cannot be used for this purpose because the library defines the entity. This allows Carbon to provide a compile-time diagnostic if an entity declared in the `impl` is not defined locally. Note that an entity declared in the `api` may still not get a compile-time diagnostic unless the compiler is told it's seeing _all_ available `impl` files. For example: ```carbon package Bar library "a" api; class C; class D { ... } ``` ```carbon package Bar library "a" impl; // Invalid: Missing a definition in the `impl` file, but if one were added, then // this would be valid. class C; // Invalid: The `api` defines `D`. As a consequence, there is no way to make // this forward declaration valid. class D; ``` This doesn't prevent `impl` from providing a forward declaration. It might when it also provides the definition, which can be useful to unravel dependency cycles: ```carbon package Bar library "a" api; class D; ``` ``` package Bar library "a" impl; class D; class E { fn F[self: Self](d: D*) { ... } } class D { fn G[self: Self](e: E) { ... } } ``` Here, the `impl` could not use the imported forward declaration in the `api` because of the rule [Files must either use an imported declaration or declare their own](#files-must-either-use-an-imported-declaration-or-declare-their-own). Without `class D;` present, the definition of `F` would be invalid. #### Type scopes may contain both a forward declaration and definition The combination of a forward declaration and a definition is allowed in type scopes. For example: ```carbon class C { class D; fn F() -> D; class D { fn G() -> Self { return C.F(); } var x: i32; } fn F() -> D { return {.x = 42}; } } ``` This is necessary because type bodies are not automatically moved out-of-line, unlike function bodies. ### Using `extern` declarations in `extern impl` It is invalid for a non-`extern` `impl` declaration to use an `extern` type in its type structure. Consider two libraries, one defining `A` and declaring `B` as `extern`, and the other defining `B` and declaring `A` as `extern`. Neither should be able to define an `impl` involving both `A` and `B`, otherwise both could. ### `impl` lookup involving `extern` types If `impl` lookup involving `extern` types finds a non-`final` parameterized `impl`, the result is that the lookup succeeds, but none of the values of the associated entities of interface are known. This is because there may be another more specialized `impl` that applies that is not visible (as can also happen with constrained generics). For example: ``` library "a" api; extern class C; extern class D(T:! type); extern impl forall [T:! type] D(T) as I where .Result = i32; ``` In the above, `D(C)` impls `I`, but with unknown `.Result`, since it might not be `i32`. ### Modifier keywords When considering various modifiers on a forward declaration versus definition: - `extern` is only valid on a forward declaration. Rules are detailed above. - `extend` in `extend impl` is only on the declaration in the class body (whether that is a forward declaration or definition), as described at [Forward `impl` declaration](https://github.com/carbon-language/carbon-lang/blob/trunk/docs/design/generics/details.md#forward-impl-declaration). - Other class, impl, and interface modifiers (`abstract`, `base`, `final`) exist only on the definition, not on the forward declaration. - Function modifiers (`impl`, `virtual`, `default`, `abstract`, `final`) must match between forward declaration and definition. - This only affects type-scoped names because they are invalid on namespace names. - `abstract` won't have a definition so is moot here. - Access modifiers (`private` and `protected`) must match. - As an exception, an `extern` name may be `private` when the actual name is public. - This allows a library to forward declare another library's type without allowing clients to depend on its forward declaration. - On merging, the more public declaration will take precedence, hiding `private extern` declarations. - This affects both type-scoped names and namespace names. ## Rationale - [Software and language evolution](/docs/project/goals.md#software-and-language-evolution) - This proposal supports moving classes between libraries without affecting the compilation of clients. - Allowing a redundant `extern` declaration when a non-`extern` declaration is imported allows _adding_ the class to an already-imported library where `extern` declarations were previously present. - Requiring the use of a local `extern` declaration prevents accidental uses that might hinder _removing_ the class from an imported library. - Requiring keywords on `extern` declarations only when they affect calling conventions means that, in most cases, keywords can be added and removed from declarations in the defining library without breaking `extern` declarations in other libraries. - [Code that is easy to read, understand, and write](/docs/project/goals.md#code-that-is-easy-to-read-understand-and-write) - Explicitly flagging `extern` will assist readers in understanding when a library is working with a type in order to avoid dependency loops, with minimal impact on writing code. - Preventing redundant forward declaration removes a potential avenue for confusion by making the meaning of entities clearer. - Requiring keywords be repeated for type-scoped functions is intended to improve readability. - [Practical safety and testing mechanisms](/docs/project/goals.md#practical-safety-and-testing-mechanisms) - Requiring `extern` declarations be clearly marked should improve our ability to diagnose ODR violations. This will help developers by improving detection of a subtle correctness issue. - [Fast and scalable development](/docs/project/goals.md#fast-and-scalable-development) - `extern` declarations are considered essential to supporting separate compilation of libraries, which in turn supports scaling compilation. - [Interoperability with and migration from existing C++ code](/docs/project/goals.md#interoperability-with-and-migration-from-existing-c-code) - `extern` is chosen for consistency with C++, and carries a similar -- albeit slightly different -- meaning. - [Principle: Prefer providing only one way to do a given thing](https://github.com/carbon-language/carbon-lang/blob/trunk/docs/project/principles/one_way.md) - Setting _requirements_ for whether a keyword belongs on a forward declaration or with the definition, instead of making places _optional_, supports developers making conclusions based on which keywords they see -- either by presence or absence. ## Alternatives considered ### Other modifier keyword merging approaches There has been intermittent discussion about which modifiers to allow or require on forward declarations versus definitions. There are advantages and disadvantages about redundancy and being able to copy-paste declarations. There might be strict requirements for some modifiers to be present in order to correctly use a forward declaration. This proposal suggests a partial decision here, at least for a reasonable starting point that we can implement towards. This will likely evolve in future proposals, particularly as more keywords are added. However, this still offers a baseline. The trade-offs we consider are: - Consistency in when a modifier keyword is expected (if applicable) is valuable. - Adding a keyword to an entity with a separate definition may require adding the keyword to the forward declaration, the separate definition, or both. It is disallowed where not required to be added. - For example, `base` is added to `class C { ... }`, and disallowed on `class C;`. - Although we could make keywords optional where it would not affect semantics, we prefer for the presence or absence of a keyword to carry a clear meaning. - For example, if `base` were optional to allow `base class C;`, then an adjacent `class D;` lends itself to being incorrectly interpreted as meaning "`D` is not a base class" when it actually means "`D` may or may not be a base class". - Access control has a certain necessity for consistency, so that a consumer of a forward declaration would still be allowed to use the definition if refactored. - We could require similar consistency on `extern`, and should have more nuanced rules if the access control rules go beyond public and `private`. For example, a package-private type shouldn't be allowed to be made public through an `extern` declaration; but package-private isn't actually part of the language right now. - For a type, we are choosing to have minimal modifiers on the declaration. - The modifiers we disallow (`abstract`, `base`, `final`, and `default`) have no effect on uses because the forward declared type is incomplete. - Requiring them would end up leaking an implementation detail and create toil. - Requiring them would be somewhat inconsistent with C++. - `extern` is a special-case where its presence is intrinsic to the keyword's semantics. - For type-scoped members, we are choosing to duplicate modifiers between the declaration and definition. - For example: ``` class A { private class B; } // `private` is required here. private class A.B { ... } ``` - There is more emphasis on being able to copy-paste a function declaration. This in particular may help developers more than classes because all the parameters must also be copied. Things such as `static` in C++ being declaration-only is also something we view as a source of friction rather than a benefit. - Modifiers such as `virtual` affect the calling convention of functions, and as a consequence _must_ be on the first declaration. - A downside of this approach is that it means the class name is inserted in the middle of the out-of-scope definition, rather than near the front. The most likely alternative would be to disallow most modifiers on out-of-line definitions after a forward declaration, for type member functions in specific. This would be because, unlike other situations, a member must have a forward declaration if there is an out-of-line definition. We would probably want to drop these collectively in order to maximize copy-paste ability (ideally, everything before `fn` is dropped). However, it would shift understandability of the definition in a way that may be harmful. For now this proposal suggests adopting the more verbose approach and seeing how it goes. Another alternative is that we could allow flexibility to choose which modifier keywords are provided where. For example, keywords on a function definition must be some subset of the keywords on the declaration; keywords on a type declaration must be some subset of the keywords on the definition. This would allow authors to choose when they expect keywords will be most relevant. However, it could also serve to detract from readability: two functions in the same file might be declared similarly, but have different keywords on the definition, implying a difference in behavior that would not exist. With consideration for the [Principle: Prefer providing only one way to do a given thing](https://github.com/carbon-language/carbon-lang/blob/trunk/docs/project/principles/one_way.md), this proposal takes the more prescriptive approach, rather than offering flexibility. Note a common aspect between types and functions in the proposed model is that modifiers on the definition are typically a superset of modifiers on the declaration (`extern` as an exception). While looking at the declaration always gives an incomplete view, looking at the definition can give a complete view. ### No `extern` keyword If we had no `extern` keyword, then a declaration wouldn't give any hint of whether a library contains the definition. Given two forward declarations in different `api` files, either both or neither could have a definition in their `impl` file. Sometimes this would be detected during linking, particularly if both are linked together. However, providing an `extern` keyword gives a hint about the intended behavior, allowing us to evaluate more cases for warnings. It also gives a hint to the reader, about whether an entity is expected to be declared later in the library (even if not in the same file). For example: ```carbon package Foo library "a" api; class C; ``` ```carbon package Foo library "a" impl; class C {} ``` ```carbon package Foo library "b" api; import library "a"; class C {}; ``` Without the `extern` keyword, this code should be expected to compile. Ideally it would be caught during linking that there are two definitions of `class C`, but that relies on some tricks to catch issues. When `extern` is added, then the processing of library "b" results in a conflict between the `class C;` forward declared in the api of library "a". Note this probably does not fundamentally alter the amount that can be diagnosed, but will mainly allow some diagnostics to occur during compilation that otherwise would either be linker diagnostics or missed. The `extern` keyword is being added mainly for diagnostics and readability. ### Looser restrictions on declarations We are being restrictive with declarations and when they're allowed. Primarily, we want to avoid confusion with code such as: ```carbon class C { ... } // This declaration has no effect. class C; ``` But, we could also allow code such as: ```carbon package Foo library "a" api; class C { ... } ``` ```carbon package Foo library "b" api; import library "a"; fn F(c: C) { ... } extern class C; fn G(c: C*) { ... } ``` Here, `F` requires the imported definition of `C`. But, is `G` seeing an incomplete type from the `extern`, or is the `extern` redundant and `G` sees the imported definition of `C`? Would a library importing library "b" see `C` as an incomplete type, or a complete type? In order to eliminate potential understandability issues with the choices we may make, we are choosing the more restrictive approaches which disallow both of these. In the first case, the redeclaration after a definition in the same file is simply disallowed. In the second case, library "b" cannot declare `C` as `extern` after using the imported definition. Restrictions such as these should make code clearer by helping developers catch redundant, and possibly incorrect, code. ### `extern` naming Beyond `extern`, we also considered `external`. `extern` implies external linkage in C++. We're choosing `extern` mainly for the small consistency with C++. ### Default `extern` to private The `extern` keyword could have an access control implication equivalent to `private`. Then `extern` would need explicit work to export the symbol. The `export` keyword was proposed for this purpose, with the idea that `export import` syntax might also be provided to re-export all symbols of an imported library. 1. `extern` and `export` semantic consistency This would mean that `extern` declarations have different access control than other declarations, which is a different visibility model to understand, and may also not be intuitive from the "external" meaning. The use of `export` matches C++'s `export` keyword, which may also imply to developers that other semantics match C++, such as `export` being necessary for all declared names. 2. Interaction with additional access control features We'll probably also want more granular access control than just public and private for API names. Adding package-private access modifier seems useful (for example, Java provides this as `package`): in C++, this is sometimes achieved through an "internal" or "details" namespace. If Carbon only supports library-private symbols, that still addresses some of these use-cases, but will sometimes require private implementation details to exist in a single `api` file in order to get language-supported visibility restrictions. In some cases this will result in an unwieldy amount of code for a single file. For example of how package-private visibility might be used, gtest has [an internal header directory](https://github.com/google/googletest/blob/main/googletest/include/gtest/internal) that contains thousands of lines of code. If this needed to migrate to `api` files in Carbon, it would be ideal if users could not access the names by importing an internal library. For example of how this would interact: | Default visibility | Public (proposed) | Private (alternative) | | ------------------------------ | ------------------------- | -------------------------------- | | Public | `extern class C;` | `export extern class C;` | | Library-private | `private extern class C;` | `extern class C;` | | Package-private (hypothetical) | `package extern class C;` | `export package extern class C;` | 3. Risks of a public extern When making an `extern fn`, it is callable. This creates a risk of a function being declared as `extern` for internal use, but accidentally allowing dependencies on the function. This risk could be mitigated by requiring access control of an `extern` to be either equal to or more restrictive than the original symbol (which might be hard to validate, but could be validated when both symbols are seen together). When making an `extern class`, it's an incomplete type. It cannot be instantiated, but pointers and references may be declared. This is only really useful if there are functions which take a pointer as a parameter, but an instance of the pointer could only be created by either unsafe casts or if there's a function that returns the pointer type. Unsafe casts carry an inherent risk. In the case of a returned pointer, that type could be captured by `auto`. Having `extern class` default to private does not prevent the type's use. Considering the trade-offs involved combination of these three points, this proposal suggests using the regular access control semantic. That choice may be reconsidered later, based on how the semantics work out and any issues that arise, particularly around access control. ### Opaque types Omitting the `extern` modifier means a definition is required in a library. There may be a use-case for opaque types which are "owned" by a library and have no definition. If so, there are possible solutions such as a modifier keyword to indicate an opaque type. For now, an empty definition in the `impl` file of a library should have a similar effect: `api` users would not be able to provide a definition. For example: ```carbon package Foo library "a" api; // An opaque type which can be imported by other libraries. class C; ``` ```carbon package Foo library "a" impl; // An empty definition. This could be in its own file, or at the end after logic, // to prevent misuse. class C {} ``` This proposal requires a definition to exist. That choice may be reconsidered later, based on use-cases. ### Require a library provide its own `extern` declarations As proposed, any library can provide `extern` declarations for other libraries in the same package. It was proposed that this should be restricted so that a library would need to make `extern` declarations available, either through a separate `extern` file (similar to `api` and `impl`) or through additional markup in the `api` file which could be used to automatically generate an `extern`-only subset of the `api` (still requiring entities which _should_ be `extern` to be explicitly marked). Advantages: - Centralizes ownership of `extern` declarations. - We are already planning to require a package to provide `extern` declarations. This goes a step further, requiring the `extern` declaration be provided by the same library that's defining the entity, providing a clear, central ownership. - Simplifies refactoring. - The current plan of record is to require that `extern` declarations agree with the library's declaration of the definition. This includes small details such as parameter names. A consequence of this is that changing these details in one declaration requires changing them in _all_ declarations, atomically. There's a desire to limit the scope of atomic refactorings; for example, similar package-scope atomic refactoring requirements lead us to _allow_ certain redundant forward declarations elsewhere in this proposal. - Reduces complexity for migrating C++ forward declarations. - As described in [Migration of C++ forward declarations](#migration-of-c-forward-declarations), we expect migrating forward declarations to be difficult. Several of the steps needed already can produce results similar to this alternative. Under this alternative, we would make the handling of an entity defined in a different library identical to as if it were in a different package: a reduction of one case. Disadvantages: - Makes it difficult for libraries which want to use `extern` declarations to use minimal imports for a declaration. - If a library provides multiple `extern` declarations, the `extern` file's imports would be a superset of the imports for those declarations. If a client library only wants one of those `extern` declarations, it would still get the full set of dependencies; under the proposed syntax, only the single declaration's dependencies are required. This allows for fewer dependencies. - Does not support use-cases that may occur in C++. - It's possible that an extern declaration may depend on a defined entity, where that entity is being defined in the same file. For example, `class C { ... }; extern fn F(c: C);`. Under the proposed syntax, this is supported; under the alternative syntax, another solution would need to be found. There are potential ways to fix this through refactoring, including moving one entity to a different library, changing the `extern` declaration to use only `extern` declarations, or using generic programming to create an indirection. However, each of these is a refactoring that may hinder adoption. At present, the disadvantages are considered to outweigh the advantages. It's possible that this may be revisited later if we get more code and libraries using these tools and recognize some patterns that we can better or more directly support. However, we should be hesitant to provide a second syntax for `extern` if it's not adding substantial value, under [Principle: Prefer providing only one way to do a given thing](https://github.com/carbon-language/carbon-lang/blob/trunk/docs/project/principles/one_way.md). ### Allow cross-package `extern` declarations We could choose a syntax for `extern` declarations that allows cross-package `extern` declarations. These are effectively supported in C++, where there are no package boundaries. Dropping support will create a migration barrier. However, there is a strong desire to restrict the use of cross-package declarations in order to reduce the difficult and complex refactoring costs that result from cross-package declarations: requiring that a particular name not change its declaration category (a `class` must remain a `class`, preventing `alias`) and that parameters (either function or generic) must remain the same, not even allowing implicit conversions. The package boundaries serve an important purpose in balancing costs for refactoring. ## Appendix ### Migration of C++ forward declarations This is not proposing a particular approach to migration. However, for consideration of the proposal, it can be helpful to consider how migration will work. It's expected under this approach that migration of a forward declaration will require identifying the Carbon library that defines the entity. Then: - The forward declaration will need to be adjusted based on library and package boundaries: - If the forward declaration is disallowed in Carbon, it may need to be removed. - If the forward declaration is in the same library as the defining library, then no `extern` is required. - If the forward declaration is in a different library but the same package, then `extern` is added. - If the forward declaration is in a different package, the forward declaration must be removed. To replace it, there are a couple options which would need to be chosen by heuristic: 1. Add a dependency on the actual definition. This might be infeasible when the defining library has many complex dependencies. 2. Add a library to the other package that provides the necessary `extern` declaration. This might be infeasible when the package is not owned by the package being migrated. - If the forward declared code is in C++, we need to retain a forward declaration in C++. A couple examples of how we might achieve that are: - Provide Carbon syntax for an in-file forward declaration of C++ code (for example, `extern cpp `). - Create a small C++ header providing the forward declaration, and depend on it. - Fix meaningful differences in parameter names, for example by updating the forward declaration's parameter names to match the definition. - Fix meaningful differences in modifier keywords, for example by adding a function forward declaration's modifiers to the definition. ================================================ FILE: proposals/p3763.md ================================================ # Matching redeclarations [Pull request](https://github.com/carbon-language/carbon-lang/pull/3763) ## Table of contents - [Abstract](#abstract) - [Problem](#problem) - [Background](#background) - [#1132 rule](#1132-rule) - [C++ rule](#c-rule) - [One-definition rule](#one-definition-rule) - [Where declarations can appear](#where-declarations-can-appear) - [Proposal](#proposal) - [Details](#details) - [Syntactic vs semantic matching](#syntactic-vs-semantic-matching) - [Unqualified name lookup](#unqualified-name-lookup) - [`impl`s that have not yet been declared](#impls-that-have-not-yet-been-declared) - [Declaration modifiers](#declaration-modifiers) - [`_` parameter names and `unused` modifier](#_-parameter-names-and-unused-modifier) - [Scope differences](#scope-differences) - [`impl` declarations](#impl-declarations) - [Redeclarations](#redeclarations) - [Differences between `impl` declarations](#differences-between-impl-declarations) - [Out-of-line definitions of associated functions](#out-of-line-definitions-of-associated-functions) - [`impl` members vs `interface` members](#impl-members-vs-interface-members) - [`virtual` functions](#virtual-functions) - [`extern` declarations](#extern-declarations) - [`let` and `var` declarations](#let-and-var-declarations) - [Rationale](#rationale) - [Alternatives considered](#alternatives-considered) - [Use a partially or fully semantic rule](#use-a-partially-or-fully-semantic-rule) - [Use package-wide name poisoning](#use-package-wide-name-poisoning) - [Allow shadowing in implementation file after use in API file](#allow-shadowing-in-implementation-file-after-use-in-api-file) ## Abstract Require exact syntactic matching in redeclarations. Provide new terminology for redeclaration matching and agreement. Specify non-redeclaration rules for the other contexts where we require multiple declarations to match, such as `impl`s of `interfaces`, `impl`s of `virtual fn`s. ## Problem When we see two declarations that might declare the same entity, we need to know: - Do they actually declare the same entity? - Are they similar enough to be _valid_ declarations of the same entity? [Leads issue #1132](https://github.com/carbon-language/carbon-lang/issues/1132) has rules for this, and those rules were partially incorporated into the design by [#1084 Generics details 9: forward declarations](/proposals/p1084.md). However: - #1084 only covers generics, not the whole scope of the language, and - #1132's hybrid approach of using a syntactic rule but allowing syntactic deviations for aliases has proven challenging and awkward to implement in the Carbon toolchain. ## Background Related issues and proposals: - [Issue #472: Open question: Calling functions defined later in the same file](https://github.com/carbon-language/carbon-lang/issues/472) - [Issue #1132: How do we match forward declarations with their definitions?](https://github.com/carbon-language/carbon-lang/issues/1132) - [Proposal #875: Principle: information accumulation](https://github.com/carbon-language/carbon-lang/pull/875) - [Proposal #1084: Generics details 9: forward declarations](https://github.com/carbon-language/carbon-lang/pull/1084) - [Proposal #3762: Merging forward declarations](https://github.com/carbon-language/carbon-lang/pull/3762) ### #1132 rule Under issue #1132, as fleshed out by proposal #1084: Two declarations declare the same entity if they _match_, which in most cases simply means that they have the same name and same scope. For `impl` declarations, which don't have names, there is a more complicated rule. Two declarations that declare the same entity _agree_ if they are similar enough to be valid redeclarations of that entity. This requires them to have the same syntactic form, with some exceptions: 1. Parameter names can be replaced by `_` if the parameter is not used in the declaration. 2. Grouping parentheses can be present in one declaration and absent in another if it doesn't change the parse result. 3. An alias can be used in one declaration, where the alias target or a different alias to the same target is used in the other declaration. [Concerns have been raised](https://discord.com/channels/655572317891461132/963846118964350976/1214372759224983562) that the terminology in use here is problematic: - Declarations are said to "match" even when they are quite dissimilar -- for example, functions with different signatures are said to match. - The term "match" is easy to confuse with pattern-matching terminology. - The term "agree" sounds like it is expressing an opinion. - These terms don't fit well into diagnostics. For example, we considered diagnostic text similar to "Declaration X does not agree with matching declaration Y", which doesn't clearly describe the problem. While it's not necessary for us to use the formal, technical terminology in diagnostics, it would be useful to have terminology that also works in that context. The chosen rule has also been found to be problematic to implement in practice, because it combines the purely-syntactic concern of whether the same syntax is used with the semantic, scope-based concern of the name lookup result, at least for aliases. ### C++ rule In C++, two function or function template declarations declare the same entity if they have the same: - scope - name - template parameter lists - return type, if a function template - parameter types and trailing ellipsis In each case, the component of the declaration can be written in different ways between declaration and definition. In general, names of parameters can be arbitrarily different. Types can be written in different ways, so long as they resolve to the same thing. However, parts of the declaration that depend on template parameters must be written with the same token sequence, which must be interpreted in the same way, or the dragons of "ill-formed, no diagnostic required" may burn your program to the ground. There is also a grey area between cases where token-by-token matching is required and more liberal matching is permitted, where it's not clear what rule to apply. The oldest still-open core language issue against C++, [CWG issue 2](https://www.open-std.org/jtc1/sc22/wg21/docs/cwg_active.html#2), concerns such a case, but there are many more examples. In C++, because the scope of a declaration is only entered part-way through declaring it -- specifically, after the return type -- such problems are hard to avoid. There are constructs that must be written differently in an in-class declaration versus in an out-of-line definition. ```c++ struct A { struct B {}; B f(); }; // Can't write this as `B f()`. A::B A::f() {} ``` In Carbon, there is no such problem, so a simpler rule is available to us. Indeed, it's desirable to support a simple syntactic rule for transforming an in-class declaration into an out-of-class definition, both for tooling and for programmers. ### One-definition rule Proposal #3762 establishes the following rules: If an entity is declared in the `api` file of a library, it can be redeclared in `impl` files subject to other rules on redundant redeclarations, and a definition is required in exactly one `impl` file. If the entity is _not_ declared in the `api` file, and is instead introduced as non-`extern` in an `impl` file, then it must be defined in that `impl` file. Note that this means that such an entity can only be declared in a single `impl` file; if an entity is intended to be shared by multiple `impl` files, it must be declared in the `api` file, typically as a `private` member of the library. ### Where declarations can appear Because declarations always declare their entity within the lexically-enclosing package, and the package forms part of the scope, declarations for an entity can only appear in a single package. As specified in #3762, entities can be declared in more than one library: Each entity has an owning library, and all declarations of that entity outside the owning library are declared `extern`. All declarations of an entity without the `extern` keyword are required to appear in the same library. ## Proposal Replace the terminology ("match" and "agree") and corresponding rules here as follows: - Two named declarations _declare the same entity_ if they have the same scope and the same name. - One declaration _redeclares_ another if they declare the same entity and the second declaration appears after the first, including the case where the first declaration is imported. In this case, the second declaration is said to be a _redeclaration_ of the first. - Two declarations _differ_ if the sequence of tokens in the declaration following the introducer keyword and the optional scope, up to the semicolon or open brace, is different, except for `unused` modifiers on parameters. - The program is invalid if it contains two declarations of the same entity that differ. ```carbon class A { fn F(n: i32); fn G(n: i32); } // ❌ Error, parameter name differs. fn A.F(m: i32) {} // ❌ Error, parameter type differs syntactically. fn A.G(n: (i32)) {} ``` ## Details ### Syntactic vs semantic matching Redeclarations are primarily checked syntactically. The intention is that whenever the syntax matches, the semantics must also match. This is largely true, and is supported by the [information accumulation principle](https://github.com/carbon-language/carbon-lang/pull/875) that it is an error if information learned later would have affected the meaning of earlier code. Two specific cases are worth calling out here: - [Unqualified name lookup](#unqualified-name-lookup) - [`impl`s that have not yet been declared](#impls-that-have-not-yet-been-declared) Because the semantics of declarations should always match if the syntax matches, we expect implementations of Carbon to be able to call out semantic differences between redeclarations -- for example, "type of parameter `x` in redeclaration is different from type in previous declaration" -- not only point to where the syntax diverged. All non-`extern` declarations of an entity are in the same library, and all non-`extern` declarations see the first non-`extern` declaration of the entity, which is either in the same file or, for a declaration in an implementation file, in the API file, so we can always perform a precise syntactic redeclaration check if we persist syntactic information from the API file to implementation files. #### Unqualified name lookup ```carbon namespace N; class C; fn N.F(x: C); class N.C; fn N.F(x: C) {} ``` Here, it appears that the unqualified reference to `C` resolves to two different classes in the two declarations. However, following the information accumulation principle, we consider the declaration of `N.C` invalid because it would change the meaning of the unqualified name `C` that has already been used. Specifically, we add the following rule: - In a declarative scope, it is an error if a name is first looked up and not found, and later introduced. Names for which unqualified lookup is performed and fails are said to be _poisoned_ in that scope. _Declarative scopes_ here are scopes like namespaces, classes, and interfaces. Another way of looking at this rule is that reordering the declarations in a declarative scope should never result in a program that is still valid but has a different meaning. It is also possible to declare an entity in a non-declarative scope, such as in a function body. We call non-declarative scopes _sequential scopes_, and use a different rule: - It is an error to redeclare an entity in a sequential scope. This rule is provisional: no alternative has been suggested, and we don't know whether there is strong motivation for supporting redeclaration in sequential scopes. In C++, such redeclarations are permitted, but we are not aware of any instance of this functionality being used. There are likely other good options to use here, but we consider them outside the scope of this proposal. In order to ensure that two declarations with the same syntax have the same semantics when one declaration is in an API file and a redeclaration is in an implementation file of the same library, we start each implementation file with the name lookup state from the end of its API file. In particular: - All names declared in the API file are visible in the implementation file, even if they're `private`. - All imports in the API file are visible in the implementation file, even if they're not `export`ed. - Poisoned names are persisted from the API file to the implementation file: if a name is looked up by unqualified name lookup in an API file, but not found in that scope, the name cannot be declared in that scope in an implementation file. Note that the above only apply within the same library. It is permitted for a name to be poisoned in one library but declared in another. Some [additional considerations](#extern-declarations) apply to `extern` declarations, where it is possible for name lookups in two syntactically identical declarations to have different results. #### `impl`s that have not yet been declared It is possible for a declaration of an entity to have one meaning because `impl` lookup finds a broad `impl`, and for a redeclaration to have a second meaning because `impl` lookup finds a narrower `impl`. However, in such a case, the declaration of the narrower `impl` declaration is invalid, because it would change the meaning of an earlier `impl` lookup. From #875: > When an `impl` needs to be resolved, only those `impl` declarations that have > that appear earlier are considered. However, if a later `impl` declaration > would change the result of any earlier `impl` lookup, the program is invalid. ### Declaration modifiers Declaration modifier keywords appear prior to the introducer keyword and scope in a declaration, so are not involved in checking whether two declarations differ. There may be other rules that determine whether modifiers may or must be repeated on a redeclaration, but they are out of scope for this proposal. ```carbon class A { virtual fn F[self: Self](); } // ✅︎ OK, `virtual` not required to match. fn A.F[self: Self]() {} ``` ### `_` parameter names and `unused` modifier Issue #1132 permitted redeclarations of a function to differ by having one declaration of a function name a parameter and the other leave it unnamed with `_`. That flexibility is removed by this proposal. This change improves the consistency of redeclarations, and also closes a hole in the syntactic matching rule: ```carbon fn F(T:! type, x: T); alias T = i32; // ❌ Not equivalent to previous declaration, but same syntax // other than replacing parameter name with `_`. fn F(_:! type, x: T); ``` However, it is important that a function definition can choose to not use its parameter, in a way that is not visible in the API. Therefore we exclude `unused` modifiers from the syntactic difference check. ```carbon // Now equivalent to the first declaration above. // However, this is invalid because `T` is declared unused // but is used in the type of `x`. fn F(unused T:! type, x: T): ``` For simplicity, and to avoid any need to check `unused` annotations match between declaration and definition, we disallow `unused` from appearing on a parameter in a non-defining declaration. More generally, annotations on a declaration that are only relevant to the definition should be excluded from the check. At the moment, the `unused` annotation is the only such case, but we anticipate more cases emerging in the future. Attributes on function parameters are another example that may need special-casing. ### Scope differences In addition to comparing whether the name and parameter portion of a declaration differ from that of another declaration, we also need rules governing the scope portion. The rule we use for a qualified declaration is: - Take the portion of the declaration from the introducer up to the end of the scope. - Replace the introducer keyword with the introducer keyword of the scope. - Replace the trailing `.` with a `;`. - The result must be a valid declaration of the scope, ignoring restrictions on how often the scope can be redeclared. Put another way: each portion of the qualified name must not differ from the declaration of the corresponding entity. For example: ```carbon namespace N; class N.C(T:! type) { class D(U:! type) { fn F(a: T, b: U); } } fn N.C(T:! type).D(U:! type).F(a: T, b: U) {} ``` In this function definition: - `F(a: T, b: U)` does not differ from the declaration of `F`. - `class N.C(T:! type).D(U:! type);` would be a valid redeclaration of `D`, because: - `D(U:! type)` does not differ from the declaration of `D`. - `class N.C(T:! type);` would be a valid redeclaration of `C`, because: - `C(T:! type)` does not differ from the declaration of `C`. - `namespace N;` would be a valid redeclaration of `N`. So this is a valid definition of `F`. Note that this means that, for example, all members of a class must use the same name for each generic parameter of that class. It cannot be `T` in one out-of-line member definition and `ElementType` in another, or the scope in the out-of-line definition would not match. > **Future work:** This rule does not permit aliases to be used for top-level > names in a declaration. This may be reasonable in most cases, because all > declarations other than perhaps an `extern` declaration will be in the same > library. However, names within namespaces may be declared anywhere within a > package, and a mechanism to permit renaming of a namespace without making an > atomic change to the entire package would be useful. ### `impl` declarations #### Redeclarations The rules from #1084 for `impl` declarations are largely unchanged in this proposal, except that the allowance for `alias`es to be expanded is removed. We cannot use the declaration name to determine whether two `impl` declarations declare the same entity. Instead, two `impl` declarations declare the same entity if the portion of the declaration from the introducer keyword until the `;` or `{` does not differ, except: - If the constraint type in the `impl` is of the syntactic form `expression where constraints`, then the `constraints` portion is not considered. - `impl as` is rewritten to `impl Self as` before looking for a previous declaration and checking for differences from any previous declaration that is found. ``` interface I { let T:! type; } class A { impl as I where .T = (); // ✅︎ Redeclaration of previous declaration. impl Self as I where _ {} } ``` > **Note:** These rules assume that `impl` declarations have an associated > scope, and can only be redeclared in that scope, like all other declarations. > This was > [the consensus in discussion on 2024-03-11](https://docs.google.com/document/d/1s3mMCupmuSpWOFJGnvjoElcBIe2aoaysTIdyczvKX84/edit?resourcekey=0-G095Wc3sR6pW1hLJbGgE0g&tab=t.0#heading=h.p69b78lovqb7) > but has not yet been incorporated into a design proposal. This will be the > subject of a separate proposal. > > In the same discussion, the consensus was to permit such an `impl` to be > redeclared out-of-line, with parentheses added around the name of the `impl`. > That would lead to the following behavior under this proposal: > > ```carbon > // ✅︎ Redeclaration of the `impl` from the previous example. > impl A.(Self as I) where _; > > // ✅︎ Rewritten to `impl A.(Self as I) where _;` before redeclaration check. > // Same as previous example. > impl A.(as I) where _; > ``` > > However, this syntax change for `impl`s is not part of this proposal. #### Differences between `impl` declarations As specified in #1084, an `impl` declaration can be of the form > `impl [...] as interface where _;` with an `_` replacing the constraints in the `where` expression. For such a declaration, a prior declaration must be found or the `impl` is invalid. The `_` is replaced by the constraints in the prior declaration. After this transformation, the normal rule is applied: the `impl` declaration cannot differ from the previous declaration. For an implementation that performs a semantic replacement of `_` rather than a syntactic one, it can terminate the comparison when it reaches the `_`. #### Out-of-line definitions of associated functions A small change is made to the rule for [scopes](#scope-differences) for `impl` members: parentheses are added around the corresponding portion of the scope. For example: ```carbon impl Type as Interface { fn F(); } // Not `fn Type as Interface.F() {}`. fn (Type as Interface).F() {} ``` Similarly for parameterized `impl`s: ```carbon impl forall [T:! type] T as Interface(T) { fn F(); } fn (forall [T:! type] T as Interface(T)).F() {} ``` And for class-scope `impl` members: ```carbon class Class { impl as Interface { fn F(); fn G(); } } // ✅︎ OK fn Class.(Self as Interface).F() {} // ✅︎ Rewritten to `Self as Interface`. fn Class.(as Interface).G() {} ``` > **Note:** As part of associating `impl` declarations with a scope, > [the consensus in our discussion](https://docs.google.com/document/d/1s3mMCupmuSpWOFJGnvjoElcBIe2aoaysTIdyczvKX84/edit?resourcekey=0-G095Wc3sR6pW1hLJbGgE0g&tab=t.0#heading=h.p69b78lovqb7) > was to also change the `impl forall` syntax to: > > ``` > impl [T:! type](T as Interface(T)); > ``` > > If that change is made, the scope syntax will similarly change to reflect the > new syntax: > > ``` > fn [T:! type](T as Interface(T)).F() {} > ``` > > However, that change is out of scope for this proposal. #### `impl` members vs `interface` members We need rules governing how associated functions in an `impl` are permitted to differ from the corresponding declarations in the `interface`: ```carbon interface I { fn F(s: Self); } impl i32 as I { // Is this valid? fn F(s: i32); } ``` It is tempting to base the rules here on the rules we use to check redeclarations. However, this turns out to be a poor choice: - The redeclaration rule would syntactically couple the declaration in the `interface` to the definition in the `impl`. But these declarations could be in different libraries, or even different packages, so such coupling would make refactorings that change the way that code is expressed but not its meaning either difficult or impossible. - The associated function in an `impl` is expected to have different syntax than that in the interface in some cases. The two declarations are in different scopes, so will refer to the same types in different ways. And the declaration in the `impl` is declared with knowledge of the `Self` type and associated constants for the interface, which it may be reasonable to use directly in the declaration of the function, as in the preceding example. Therefore, different rules are used. In the specific case where an associated function declaration in the `impl` can be used directly to satisfy a requirement introduced by a function declaration in the `interface`, it is used directly. For example, this is necessary to avoid infinite recursion when implementing the `Call` interface. The function in the `impl` is used directly when: - Each parameter in the `impl` function has the same type as the parameter in the `interface`. This includes the `self` parameter, which must be present in both functions if it is present in either. - Each parameter in the `impl` has the same category -- either `var` or not -- as the parameter in the `interface`. - The return type in the `interface` and `impl` are the same type. > **Note:** More constraints are expected to appear here over time. The key > property we aim to identify is whether the two functions have the same calling > convention. Otherwise, a synthetic function called a _thunk_ is generated: - The declaration of the thunk is formed by substituting the `Self` type of the `impl` into the declaration in the `interface`. This implicitly also provides values for any associated constants used in the declaration. - The body of the thunk calls the function in the `impl`, passing in the arguments to the thunk, and, if a return type is specified in the interface, returning the value returned by the call. If the function in the interface does not have a return type, the program is invalid if the function in the `impl` specifies a return type. > **Note:** Another rule might work better here. For example, we could allow > `impl`s to add a return type in anticipation of the `interface` later adding > one. However, such a feature is considered out of scope for this proposal. Implicit conversions are performed as necessary to initialize the parameters of the function in the `impl` from the parameters of the thunk, and to initialize the return value of the function from the result of the call. It is an error if a thunk is needed to wrap a function declaration with a `var` parameter, because otherwise a copy would always be performed when initializing the parameter. > **Note:** Here as well another rule may work better and we should revisit in > the future. Specifically, it might be better to allow these cases and simply > move from the outer `var` to the inner `var` even though this still leaves two > allocations in principle. > **Note:** These rules do not cover the case where the function declaration in > the `interface` or `impl` has implicit generic bindings. That case is > considered to be out of scope for this proposal. ### `virtual` functions For a `virtual` function, the same approach is taken as for `impl` functions: a thunk is generated that differs from the declaration in the base class by replacing the type of `self` with the derived class, unless the function chosen for a class can be used directly. When a virtual function is used directly in a base class and not overridden in the derived class, it is also used directly in the derived class, even though its declared `self` parameter does not have a matching type. ```carbon base class B { // No thunk used. virtual fn F[addr self: B*](); virtual fn G[addr self: B*](); } base class C { extend B; // No thunk used: `self` has expected type `C*`. impl fn F[addr self: C*](); // Uses a thunk due to unexpected `self` type. impl fn G[addr self: B*](); } class D { // No thunk for `F`, because no thunk was used in `C`. // Uses thunk for `F`, because thunk was used in `C`. extend C; } ``` Note that this supports covariant return types automatically, as well as any other case where the return value from the derived class function can be implicitly converted to the base class function's return type. However, an `impl fn` doesn't introduce a new name lookup result, so the return type of a call expression is always that of the `virtual fn`, which means this feature is not useful. > **Future work:** It might be useful to allow a declaration to both implement > an existing virtual function and introduce a new one. This would allow > introducing functions with covariant return types that work as expected. This > could be achieved with syntax such as: > > ```carbon > base class A { > virtual fn Clone[self: Self]() -> A*; > } > base class B { > virtual impl fn Clone[self: Self]() -> B*; > } > ``` > > Here, a call to `b->Clone()` would find `B.Clone` rather than `A.Clone`, and > so would have return type `B*`. The downside is that the vtable for `B` would > have two `Clone` slots, for `A.Clone` and `B.Clone`, whereas a covariant > return in C++ would only need a single vtable slot to express the same thing. It is an error for a class with a custom value representation to declare or implement a virtual function that passes `self` by value. ### `extern` declarations For `extern` declarations, some additional considerations apply to the rule disallowing redeclarations from syntactically differing: - This rule cannot in general be checked during compilation. For example, there may be no file that imports both an `extern` declaration and the library that owns the entity. - Despite being syntactically identical, `extern` declarations can have different meanings from the entity they redeclare due to unqualified lookup results differing. - A constraint that the declarations be syntactically identical is a greater burden, because it introduces syntactic coupling across libraries that may make refactoring harder. We address these points as follows: - Checking whether `extern` declarations properly redeclare their targets during compilation is best-effort. - `extern` declarations are expected to be fully checked at link time, including both syntactic and semantic checks, by emitting extra information into the object files. - Some level of semantic checks may be important for an implementation in order to ensure its data structures are consistent. For example, constant evaluation may cross between file boundaries, and it may be important that types used in compile-time functions have the same meaning in the caller and callee in order to support such calls. - Any further compile-time checking is optional and best-effort. Carbon implementations are encouraged to perform a reasonable set of checks during compilation in order to improve the quality of diagnostics and produce diagnostics earlier, but can defer harder cases until link time. For example, an implementation could choose to not track the syntactic form of a declaration, and perform only semantic checks during compilation, deferring syntactic checking until link time. - To support `extern` declarations, an additional constraint is imposed: the lookup results for names used in each declaration of an entity are required to be the same. This rule actually applies to all declarations of all entities, but it only has an effect -- and needs to be checked -- for `extern` declarations. - It is an error if an `extern` declaration mentions any `private` entity that is not also `extern`. Such a declaration could never match an entity owned by another library. - This check will in general need to be performed at link time, in addition to the link time syntactic checks. - For now, we will try this restrictive rule, even though it creates refactoring burden. - This was discussed and tentatively agreed in the [open discussion on 2024-03-05](https://docs.google.com/document/d/1s3mMCupmuSpWOFJGnvjoElcBIe2aoaysTIdyczvKX84/edit?resourcekey=0-G095Wc3sR6pW1hLJbGgE0g&tab=t.0#heading=h.q9kh9bx2nmli) - Creating an `extern` declaration necessarily creates additional coupling between libraries. Even a very permissive matching rule would not fully address the potential problems here. ### `let` and `var` declarations Per [issue #2590: syntax for declaring global variables in a namespace](https://github.com/carbon-language/carbon-lang/issues/2590), there are two different forms for `let` and `var` declarations in declarative scopes: ```carbon // An optional scope, and a single name binding. let Scope.A: Type = Value; // An arbitrary pattern that is not a name binding. let (A: Type1, B: Type2) = Value; ``` Note that this presentation is slightly different from #2590, which divided the cases into qualified name bindings and arbitrary patterns, but the set of cases is the same. The former of these two cases is treated the same as any other declaration with an introducer, an optional scope, a name, and a body, except that the end of the declaration is at the `=` or `;` rather than at the `}` or `;`. If `let` and `var` declarations can be redeclared -- which is a decision that is out of scope for this proposal -- then these declarations follow the normal rules for redeclarations, and the type of the variable is in the declaration portion, so is not permitted to differ between declarations. The latter case, with an arbitrary pattern that is not a single binding, does not permit redeclarations. ## Rationale Goals: - [Language tools and ecosystem](/docs/project/goals.md#language-tools-and-ecosystem) - Matching of declarations by simple tools is possible without sophisticated semantic analysis. A simple token comparison is sufficient to match an out-of-line definition to a declaration. - Moving a definition out of line is similarly a straightforward syntactic transformation. - [Software and language evolution](/docs/project/goals.md#software-and-language-evolution) - Function declarations that differ only in some subtle way are disallowed, leaving the maximum room available for future function overloading features. - Functions in `impl`s and `interface`s are allowed to differ so long as conversions are available, allowing `interface`s to be changed in compatible ways. - [Code that is easy to read, understand, and write](/docs/project/goals.md#code-that-is-easy-to-read-understand-and-write) - Requires writing code consistently between declarations. - [Interoperability with and migration from existing C++ code](/docs/project/goals.md#interoperability-with-and-migration-from-existing-c-code) - Provides a framework within which C++ virtual functions with covariant return types can be supported. Principles: - [Information accumulation](https://github.com/carbon-language/carbon-lang/pull/875) - Unqualified lookups follow the information accumulation rule: reordering declarations can't result in a different but valid program. ## Alternatives considered ### Use a partially or fully semantic rule We could allow declarations to differ more arbitrarily. We would need a rule to support comparisons of portions of declarations that are dependent on template parameters, and such comparisons likely need to be partially or fully syntactic. Advantages: - Allows the interface to be presented in a way that is suitable for a reader of the interface, and the implementation to be presented in a different way that is suitable for a reader of the implementation. - Additional flexibility for refactorings that are partially or incrementally applied. Disadvantages: - A syntactic rule would still be needed sometimes, but would be applied inconsistently. - Drawing a line between the syntactic and semantic checks would be complicated and difficult, as evidenced by such a line not having been successfully drawn in C++. - Allowing divergence between the syntax used in redeclarations makes code harder to read and understand, and can permit unintended divergence, or even bugs such as function parameters of the same type being swapped. ### Use package-wide name poisoning Instead of name poison only propagating from the API file to implementation files of the same library, we could poison names across the whole package if an unqualified lookup in a scope fails. If fully enforced, this would improve the ability to move code between libraries, as it wouldn't be possible for unqualified lookup results to change depending on where the code is. Full enforcement of this rule would require a link-time check, but it could be partially enforced at compile time, in cases where the poisoning lookup and the declaration are both visible in some compilation step. Whether this option might be appealing may depend on our position on name shadowing. In addition to the option commonly used in other languages, of allowing names in inner scopes to hide names in outer scopes, we have been considering a more restrictive option: always look in all enclosing scopes, and diagnose an ambiguity if an unqualified name is found in more than one enclosing scope. We do not yet have a proposal with a name shadowing rule. If we pick the more restrictive option, the impact of package-wide name poisoning is also quite restrictive: for example, a name used as a local variable in a function in one library cannot be used as a public name in any enclosing namespace in any library in that package. If we pick the less-restrictive rule, then package-wide poisoning may become more palatable and should be reconsidered. With either shadowing rule, some special accommodation for private names might be reasonable. A use of a name in one library that results in the name being poisoned should probably not conflict with a private declaration in another library, because the two uses of the name cannot interact. ### Allow shadowing in implementation file after use in API file In this proposal, we say it is an error for a name to be shadowed in an implementation file after it is used in an API file: ```carbon library "foo" api; namespace N; class A {} fn N.F(x: A); ``` ```carbon library "foo" impl; // ❌ Shadows `class A`. Cannot declare `N.A` after we already // looked for it in the declaration of `N.F`. class N.A {} fn N.F(x: A) {} ``` We could allow such code. However, this would mean that the redeclaration check for `N.F` would not be purely syntactic. We considered other ways of addressing this. In particular, we considered disallowing the declaration of `N.F` in the implementation file because it uses a local name to declare a non-local function. However, this doesn't resolve the problem: the class `N.A` could be imported from a different library instead of being declared locally. Variations of this were considered, but none of them seemed to work adequately. ================================================ FILE: proposals/p3797.md ================================================ # Raw identifier syntax [Pull request](https://github.com/carbon-language/carbon-lang/pull/3797) ## Table of contents - [Abstract](#abstract) - [Problem](#problem) - [Background](#background) - [Prior discussion](#prior-discussion) - [Other languages](#other-languages) - [Proposal](#proposal) - [Diagnostics](#diagnostics) - [Rationale](#rationale) - [Alternatives considered](#alternatives-considered) - [Other raw identifier syntaxes](#other-raw-identifier-syntaxes) - [Restrict raw identifier syntax to current and future keywords](#restrict-raw-identifier-syntax-to-current-and-future-keywords) - [Don't require syntax for references to raw identifiers](#dont-require-syntax-for-references-to-raw-identifiers) - [Don't provide raw identifier syntax](#dont-provide-raw-identifier-syntax) ## Abstract We want to support legacy identifiers that overlap with new keywords (for example, `base`). This is being called "raw identifier syntax" using `r#`, and is based on [Rust](https://doc.rust-lang.org/reference/identifiers.html). Note this proposal is derived from [Proposal #17: Lexical conventions](https://github.com/carbon-language/carbon-lang/pull/17). ## Problem One of Carbon's most important goals is to support program and language evolution. We know that the set of keywords in Carbon will grow over time, and the easiest kind of language change from an evolutionary perspective is one that is known to break no programs, that lets programs migrate incrementally to the new language rule, and that either has no migration cost or only imposes automatable migration cost on the code that intends to use the new feature. ## Background ### Prior discussion We have proposals that discussed using `r#` but did not make a decision in favor of it: - [Proposal #17: Lexical conventions](https://github.com/carbon-language/carbon-lang/pull/17) originally proposed it, but when it was split into multiple proposals, raw identifiers were not retained. - This proposal copies substantial parts of its text from here. - [Proposal #2107: Clarify rules around `Self` and `.Self`](https://github.com/carbon-language/carbon-lang/pull/2107) mentions `r#` syntax as proposed but not in use. ### Other languages [Rust](https://doc.rust-lang.org/reference/identifiers.html) provides this as "Raw identifiers", using `r#` as a prefix (`r#self`). The documented syntax is: ``` RAW_IDENTIFIER : r# IDENTIFIER_OR_KEYWORD Except crate, self, super, Self ``` [C#](https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/tokens/verbatim) provides this as "vebatim identifiers", using `@` as a prefix (`@self`). The [documented syntax](https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/language-specification/lexical-structure#643-identifiers) is: ``` fragment Escaped_Identifier // Includes keywords and contextual keywords prefixed by '@'. // See note below. : '@' Basic_Identifier ; ``` [Swift](https://docs.swift.org/swift-book/documentation/the-swift-programming-language/lexicalstructure/#Identifiers) provides this as part of the identifier grammar, using backticks (\`self\`). The documented syntax is: ``` identifier → `identifier-head identifier-characters?` ``` ## Proposal A _raw identifier_ can be specified by prefixing a word with `r#`, such as `r#requires`. Raw identifiers can be used to introduce and use names that are lexically identical to keywords. The declaration of a raw identifier does not prevent the base word from being interpreted as a keyword; otherwise, they behave identically to the word formed by removing the `r#` prefix. ### Diagnostics In diagnostics, if there is a keyword `r#`, then raw identifiers should be expected to print with the `r#` prefix. Otherwise, they will typically use the non-prefixed identifier name for consistency. ## Rationale - [Software and language evolution](/docs/project/goals.md#software-and-language-evolution) - Raw identifier syntax provides a way to add keywords to the language while still offering code a reasonable upgrade path, which can also be automated. - [Code that is easy to read, understand, and write](/docs/project/goals.md#code-that-is-easy-to-read-understand-and-write) - The `r#` syntax is consistent with raw string literals, and should be representative to readers that something unusual is being done. - [Interoperability with and migration from existing C++ code](/docs/project/goals.md#interoperability-with-and-migration-from-existing-c-code) - C++ code using identifiers that are keywords in Carbon can use raw identifier syntax. - The converse does not work: if Carbon code has an identifier that is a C++ keyword, it needs to be renamed for use from C++ code. ## Alternatives considered ### Other raw identifier syntaxes For considering other syntaxes, a couple initial considerations for `r#identifier` prefixing is: - We use `#` prefixes for [string literals](/docs/design/lexical_conventions/string_literals.md), and it's likely we'll support syntax similar to `f#"..."` for interpolated string literals. The `r#` syntax offers consistency with this, and will hopefully be recognizable to users. - Consistency with Rust. - Rust uses `r#"..."` for raw string literals, whereas Carbon uses `#"..."`. - Introduces another code execution path in lexing identifiers. This likely causes a slowdown; [PR #3044](https://github.com/carbon-language/carbon-lang/pull/3344) indicates roughly 2%, although that was run on a system with noisy benchmarks -- details would require a better system for benchmark. Note 2% could represent that `r` is 1-in-55 identifiers with a 100% slowdown with linear cost scaling for other similar code, or it could indicate that the additional code path causes incremental slowdown but if other code (such as `f#"..."`) used the same codepath it may instead have constant cost scaling (negligible incremental cost). This may also be either reduced or become more significant if we enable tail calls and other optimizations. As a consequence, the precise overhead is difficult to quantify at this time. Various other prefixes have been discussed, mostly using a special character prefix in order to restrict the lexing impact. In particular: - `\` prefix, as in `\identifier`. - Similar to `\` escaping in strings. - More intuitive "escaping" semantic for some developers versus `r#`. - Creates a different meaning for `\n` as an identifier versus `\n` as a character escape. - Some of this could be addressed by restricting `\` raw identifiers to only keywords in the language, meaning `\n` would only be a character escape. The alternative [Restrict raw identifier syntax to current and future keywords](#restrict-raw-identifier-syntax-to-current-and-future-keywords) applies to this solution. - `#` prefix without `r`, as in `#identifier`. - Would be more consistent with string literals, and avoid the lexing overhead. - We are considering using a `#` prefix for metaprogramming, so the `r` offers a way to keep the `#` prefix available for other purposes. - `#if` may look to C++ developers like a compiler directive, rather than a raw identifier for `if`. - `@` prefix, as in `@identifier`. - Consistent with C#. - We've also discussed using a `@` prefix for attributes, similar to Python. Similar to `#`, this would be conflicting. - `` ` `` wrapping, as in `` `identifier` ``. - Consistent with Swift. - We prefer not to use backticks for Carbon syntax so that it is easy to write in Markdown, which uses backticks for inline code. For example, to render a backtick there are a couple options: - Use more backticks: ``` `` ` `` ``` - Use inline HTML: ``\``` - Other currently unused characters as prefix, such as `~identifier`, `$identifier`, or `%identifier`. - We expect raw identifiers to be relatively rare. There may be future uses for these characters that allow us to serve a broader use-case. - While we could change raw string literal syntax to use the same character, it would be helpful if raw string literal syntax had some degree of cross-language syntactic consistency in order to reduce learning curves. Raw identifier syntax is expected to be an edge case of the language. As a consequence, it should probably be expected that developers reading it will be more likely to rely on their understanding of the syntax either from other parts of Carbon, or from other languages. This means it's helpful if the syntax can be understood on its own, but if it's confusable with C++ syntax, the relative rarity could exacerbate understandability issues. If performance of the `r#` prefix is prohibitive, that would be a justification for changing approaches. ### Restrict raw identifier syntax to current and future keywords We had discussed maintaining a list of current and future keywords, and only allowing raw identifier syntax in those cases. If this were done as part of the toolchain, releases would need to push versions that "declare" future keywords without turning them into actual keywords. For a library that used those identifiers, it would initially be compatible with compiler versions up to and including the "future" keyword version; upon using raw identifier syntax, that would become the minimum compiler version. This creates a compiler versioning dependency that it might be helpful to avoid. As an alternative approach, Carbon could provide a command line option which libraries could use to specify future keywords that are used in the program. While some systems such as `bazel` allow libraries to indicate options they need for compilation, other build systems such as `cmake` might require library users to update their dependencies as well. The consequence would be that library users might need to more carefully monitor options when updating compilers. ### Don't require syntax for references to raw identifiers We could say that, in a scope where a raw identifier has been declared, the token without `r#` now refers to the identifier instead of the keyword. If the user actually needs the keyword within that scope, they could instead use `k#` or something similar. A particular example of this can be seen with the `base` keyword: ``` class C { // `base` now means this name in the scope of `C`. var r#base: i32; // To extend, `k#base` is now required. extend k#base: T; } fn MakeC() -> C { // The struct literal's `base` is outside the scope of `C`, so must use // `r#base`. var c: C = {.r#base = 0, .base = { ... }}; // A member reference could use the identifier-default for `base` in `C`. c.base = 1; c.k#base = {...}; return c; } ``` The equivalent under proposed syntax (uniformly using `r#base`) is: ``` class C { var r#base: i32; extend base: T; } fn MakeC() -> C { var c: C = {.r#base = 0, .base = { ... }}; c.r#base = 1; c.base = {...}; return c; } ``` At present we are deciding this is unnecessary complexity, and it's better to require `r#` in all references to the identifier. ### Don't provide raw identifier syntax We could omit raw identifier syntax. It introduces a novel risk of underhanded code that appears to mean one thing but means a different thing, by shadowing a keyword with an identifier. This risk is discussed in [Initial Analysis of Underhanded Source Code (Wheeler 2020)](https://www.ida.org/-/media/feature/publications/i/in/initial-analysis-of-underhanded-source-code/d-13166.ashx) (page 4-2). This concern is considered non-blocking. ================================================ FILE: proposals/p3833.md ================================================ # SemIR fidelity when representing rewrite semantics [Pull request](https://github.com/carbon-language/carbon-lang/pull/3833) ## Table of contents - [Abstract](#abstract) - [Problem](#problem) - [Background](#background) - [Proposal](#proposal) - [Work to incorporate needed simplifications directly into the design](#work-to-incorporate-needed-simplifications-directly-into-the-design) - [Keep a full fidelity mode for any other optimizations](#keep-a-full-fidelity-mode-for-any-other-optimizations) - [Example: overloaded operators](#example-overloaded-operators) - [Rationale](#rationale) - [Alternatives considered](#alternatives-considered) - [Strictly adhere to a full-fidelity model](#strictly-adhere-to-a-full-fidelity-model) - [Directly implement the optimized model](#directly-implement-the-optimized-model) ## Abstract The toolchain's [Semantic IR][semir] should start off modeling the full, complex, and rich library-based and generic extension point semantics of Carbon without eliding any layers or rewrites for compile time efficiency. We shouldn't front-load elision or optimization when implementing the designs. Once we have a full-fidelity implementation, we should work to build an efficient elision, short-circuit, or common case simplification into the design itself sufficient to make the SemIR model efficient. Only if we cannot find a reasonable approach for that should we diverge the SemIR model to optimize its efficiency, and we should preserve full fidelity in an optional mode. [semir]: https://docs.google.com/document/d/1RRYMm42osyqhI2LyjrjockYCutQ5dOf8Abu50kTrkX0/edit?resourcekey=0-kHyqOESbOHmzZphUbtLrTw#heading=h.503m6lfcnmui ## Problem Carbon's design heavily leverages types and APIs defined in the `Core` package and implicitly imported with the prelude. It also defines rich extension points and generic semantic models for the language, typically through rewrites from the user-facing syntax into more complex syntax that operates through `interface`s and `impl`s that provide the generic model and customization points. Modeling this at full fidelity in the toolchain's [Semantic IR][semir] is expected to create an unreasonably expensive representation for common and pervasive patterns in code. On the other hand, every divergence between the SemIR model and the design adds both risk and cost. When we can keep the two in sync, we get the best engineering tradeoffs, provided it is reasonably efficient. ## Background Several proposals have started to surface these kinds of challenge, but to a lesser degree. Some examples: - [Proposal #820 - Implicit conversions](https://github.com/carbon-language/carbon-lang/pull/820) - [Proposal #845 - `as` expressions](https://github.com/carbon-language/carbon-lang/pull/845) - [Proposal #1083 - Arithmetic expressions](https://github.com/carbon-language/carbon-lang/pull/1083) The most significant step that motivates capturing an explicit strategy here is [proposal #3720 - Binding operators](https://github.com/carbon-language/carbon-lang/pull/3720). ## Proposal Initially, the toolchain should have SemIR model the full semantics of Carbon, with all rewrites, expressive hooks, and library references. This will serve two purposes as we build out the toolchain's support for all of Carbon's features. ### Work to incorporate needed simplifications directly into the design First, this will allow us to study the resulting SemIR, and understand exactly how and where the efficiency explodes for expected coding patterns. We should use this to try to adjust the design to build in targeted common-case simplifications and elisions that make precise modeling of the design sufficiently cheap in practice. We see this pattern emerging in good ways already today in the toolchain with implicit conversions: when calling functions, we need to allow for implicit conversions. But we don't want to have an identity implicit conversion on all the arguments which already are the correct type. Rather than notionally "always" implicitly converting, we make the design itself specify that the implicit conversion only occurs when needed and can then have the desired minimal representation in SemIR. Beyond simplifying the SemIR model, it also ends up addressing an important aspect of the semantics -- we don't want to create unnecessary temporaries to initialize rather than directly initializing. The toolchain in fact follows similar patterns to model expression categories for similar reasons -- the minimal semantic model is also the desired design. We can even incorporate other kinds of design changes to allow the full fidelity SemIR model to be efficient, such as assuring that we can build and reuse a cached representation repeatedly and cheaply rather than requiring it to be rebuilt on each instance of a specific pattern. ### Keep a full fidelity mode for any other optimizations We may still ultimately end up unable to achieve the desired SemIR efficiency even after making all the simplifications we can to the design itself. If and when we reach this point, we will still want to have the full fidelity implementation and be able to use it behind some option. We can use it to understand how the design works in complex cases and to debug unexpected behavior in the toolchain. This means starting with that implementation doesn't result in sunk cost. ### Example: overloaded operators > Note: this example isn't trying to capture all the nuance of the current > design or suggest any change to the design. It is merely trying to provide an > illustration of how we might follow the proposal in practice. As an example to help illustrate this, how should the toolchain model overloaded operators? This proposal suggests that _initially_, the implementation should aim to fully model the rewrite-based dispatch through interfaces in the prelude. That is, each use of `x + y` should turn into roughly equivalent SemIR as would be used to model the rewritten semantics of `x.(package#Core.AddWith(typeof(y)).Op)(y)`. This in turn would dispatch to an exact-type implementation which would provide any implicit conversions, and so on. And importantly, this would occur even when we know that the types of `x` and `y` are exactly `i32` and we could alternatively directly hard code the toolchain to use the desired builtin implementation. To be precise, the expectation is that the SemIR for `x + y` should as a consequence model all of: - Looking up the `package#Core.AddWith` interface, using the imagined syntax `package#` for directly looking up a specific package name (as opposed to doing unqualified name lookup). - Passing the type of `y` as a parameter to the interface, which is illustrated with the imagined syntax `typeof()` but would likely directly use the type rather than forming an actual `typeof` expression (see below). - Looking up the `Op` method. - Calling that `Op` method on `x` with `y` as its parameter just as we would model any call to `object.(Interface.Method)(y)`. > Note: this isn't a proposal for either the `package#` or > `typeof()` imagined syntaxes. If we want these syntaxes, that will > be a separate proposal. However, specific things that would still be reasonable without deviating from the full semantic modeling here: - Not representing a user-written `typeof` style expression to compute the type, or duplicating the `y` expression. - Caching and reusing SemIR for immutable things like successfully resolved name lookup. Concretely, reusing a single cached lookup of `package#Core.AddWith` would still provide the full model. And similarly, reusing an already computed parameterization such as `package#Core.AddWith(i32)` repeatedly, and the lookup of `Op` within that parameterization. The reason is that these don't change the semantics in any way -- they are constructively the same results. This should be the initial implementation _goal_, not a requirement for any incremental progress on implementation. Short-circuiting or other approximations used in incrementally building towards this goal should always be reasonable to consider based on the tradeoffs at hand. But they should be seen as temporary, incremental measures and where possible removed so we can evaluate the full semantic model when the implementation is complete. Based on the practical costs that this kind of model incurs once we have it implemented, we should first look for ways to adjust the design itself to avoid forming the complex semantic expansion in enough cases to make the model scalable. And only when we've exhausted those options begin adding long-term shortcuts in the SemIR model for common cases with a option to disable them for testing and debugging. ## Rationale - [Code that is easy to read, understand, and write](/docs/project/goals.md#code-that-is-easy-to-read-understand-and-write) - It's easy when specifying the design of the language with rewrite semantics in a way that accidentally has a cycle -- where the rewrite result is still a construct that should be rewritten. Without the proposed constraint, these accidental cycles are easily resolved in the implementation by exiting at a convenient place. However, this proposal will ensure that the rule for where the cycle is broken is actually surfaced into the design, making the design more robust and complete in describing the expected behavior. - [Language tools and ecosystem](/docs/project/goals.md#language-tools-and-ecosystem) - Preserving a full fidelity model of the language design in SemIR helps ensure we can build rich and powerful tools that reason about even complex or typically "hidden" parts of Carbon's design. This has regularly proven critical in the design of effective tooling for C++ and other languages. - [Fast and scalable development](/docs/project/goals.md#fast-and-scalable-development) - We can't afford for full fidelity representation of Carbon's design to make the practical use of the toolchain have poor compile times and slow down the development experience with Carbon. We need to have a strategy for addressing the expected overhead of the level of customization and layered design that we're building into Carbon. ## Alternatives considered ### Strictly adhere to a full-fidelity model We could simply insist on never deviating from the full-fidelity model. However, this would force us to compromise at least one of our top-level goals for Carbon: either we would have to regress the speed and scalability of development, or lose some of the simplicity and cohesive design. Both of those seem worse outcomes than having some limited divergence between SemIR and the design, and there exist reasonable mitigations such as a option to switch between modes. ### Directly implement the optimized model We could directly jump to an optimized model that we expect to match the common case behavior, and add opt-in complexity when needed for non-common-cases. However, this would both remove a useful tool in driving simplification of the design itself that can result in a overall superior tradeoff. It would also create risk of divergence, and the need to eventually implement mode to disable the optimization. In some cases, we would end up discovering a need to simplify the design for semantic reasons, and the divergence would have resulted in wasted effort in the toolchain. Starting with the full fidelity model seems to more reliably converge the design early and result in a minimal set of effective optimizations. ================================================ FILE: proposals/p3848.md ================================================ # Lambdas [Pull request](https://github.com/carbon-language/carbon-lang/pull/3848) ## Table of contents - [Abstract](#abstract) - [Syntax Overview](#syntax-overview) - [Syntax Defined](#syntax-defined) - [Introducer](#introducer) - [Positional Parameters](#positional-parameters) - [Positional Parameter Restrictions](#positional-parameter-restrictions) - [Function Captures](#function-captures) - [Capture Modes](#capture-modes) - [Default Capture Mode](#default-capture-mode) - [Function Fields](#function-fields) - [Copy Semantics](#copy-semantics) - [Self and Recursion](#self-and-recursion) - [Rationale](#rationale) - [Alternatives Considered](#alternatives-considered) - [Alternative Considered: Terse vs Elaborated](#alternative-considered-terse-vs-elaborated) - [Alternative Considered: Sigil](#alternative-considered-sigil) - [Alternative Considered: Additional Positional Parameter Restriction](#alternative-considered-additional-positional-parameter-restriction) - [Alternative Considered: Recursive Self](#alternative-considered-recursive-self) - [Future Work](#future-work) - [Future Work: Reference Captures](#future-work-reference-captures) ## Abstract This document proposes a path forward to add lambdas to Carbon. It further proposes augmenting function declarations to create a more continuous syntax between the two categories of functions. In short, both lambdas and function declarations will be introduced with the `fn` keyword. The presence of a name distinguishes a function declaration from a lambda expression, and the rest of the syntax applies to both kinds. By providing a valid lambda syntax in Carbon, migration from from C++ to Carbon will be made easier and more idiomatic. In C++, lambdas are defined at their point of use and are often anonymous, meaning replacing them solely with function declarations would create an ergonomic burden compounded by the need for the migration tool to select a name. Associated discussion docs: - [Lambdas Discussion 1](https://docs.google.com/document/d/1rZ9SXL4Voa3z20EQz4UgBMOZg8xc8xzKqA1ufPQdTao/) - [Lambdas Discussion 2](https://docs.google.com/document/d/14K_YLjChWyyNv3wv5Mn7uLFHa0JZTc21v_WP8RzC8M4/) - [Lambdas Discussion 3](https://docs.google.com/document/d/1VVOlRuPGt8GQpjsygMwH2B7Wd0mBsS3Qif8Ve2yhX_A/) - [Lambdas Discussion 4](https://docs.google.com/document/d/1Sevhvjo06Bc6wTigNL1pK-mlF3IXvzmU1lI2X1W9OYA/) # Background Refer to the following documentation about lambdas in other languages. What separates these three and makes them more analegous to Carbon's direction is the use of "captures" such that the lambda has state, a lifetime, etc. - [Lambdas in C++](https://en.cppreference.com/w/cpp/language/lambda) - [Closures in Swift](https://docs.swift.org/swift-book/documentation/the-swift-programming-language/closures/) - [Closures in Rust](https://doc.rust-lang.org/rust-by-example/fn/closures.html) ## Syntax Overview **Proposal**: A largely continuous syntax between lambdas and function declarations. At a high level, lambdas and function declarations will look like the following. ``` // In a variable: let lambda: auto = fn => T.Make(); // Equivalent in C++23: // const auto lambda = [] { return T::Make(); }; // In a function call: Foo(10, 20, fn => T.Make()); // Equivalent in C++23: // Foo(10, 20, [] { return T::Make(); }); ``` ``` // In a variable: let lambda: auto = fn -> T { return T.Make(); }; // Equivalent in C++23: // const auto lambda = [] -> T { return T::Make(); }; // In a function call: PushBack(my_list, fn => T.Make()); // Equivalent in C++23: // PushBack(my_list, [] { return T::Make(); }); ``` ``` fn FunctionDeclaration => T.Make(); // Equivalent in C++23: // auto FunctionDeclaration() { return T.Make(); } ``` ``` fn FunctionDeclaration -> T { return T.Make(); } // Equivalent in C++23: // auto FunctionDeclaration() -> T { return T::Make(); } ``` There are functions which return an expression, such that the return type is `auto`. ``` // In a variable: let lambda: auto = fn => T.Make(); // Equivalent in C++23: // const auto lambda = [] { return T::Make(); }; // In a function call: Foo(fn => T.Make()); // Equivalent in C++23: // Foo([] { return T::Make(); }); ``` ``` fn FunctionDeclaration => T.Make(); // Equivalent in C++23: // auto FunctionDeclaration() { return T::Make(); } ``` And there are functions with an explicit return type that provide a body of statements. ``` // In a variable: let lambda: auto = fn -> T { return T.Make(); }; // Equivalent in C++23: // const auto lambda = [] -> T { return T::Make(); }; // In a function call: Foo(fn -> T { return T.Make(); }) // Equivalent in C++23: // Foo([] -> T { return T::Make(); }); ``` ``` fn FunctionDeclaration -> T { return T.Make(); } // Equivalent in C++23: // auto FunctionDeclaration() -> T { return T::Make(); } ``` There are even functions that provide a body of statements but no return value. ``` // In a variable: let lambda: auto = fn { Print(T.Make()); }; // Equivalent in C++23: // const auto lambda = [] -> void { Print(T::Make()); }; // In a function call: Foo(fn { Print(T.Make()); }); // Equivalent in C++23: // Foo([] -> void { Print(T::Make()); }); ``` ``` fn FunctionDeclaration { Print(T.Make()); } // Equivalent in C++23: // auto FunctionDeclaration() -> void { Print(T::Make()); } ``` Functions support [captures](#function-captures), [fields](#function-fields) and deduced parameters in the square brackets. In addition, `self: Self` or `addr self: Self*` can be added to the square brackets of function declarations that exist inside class or interface definitions. ``` fn Foo(x: i32) { // In a variable: let lambda: auto = fn [var x, var y: i32 = 0] { Print(++x, ++y); }; // Equivalent in C++23: // const auto lambda = [x, y = int32_t{0}] mutable -> void { Print(++x, ++y); }; // In a function call: Foo(fn [var x, var y: i32 = 0] { Print(++x, ++y); }); // Equivalent in C++23: // Foo([x, y = int32_t{0}] mutable -> void { Print(++x, ++y); }); fn FunctionDeclaration[var x, var y: i32 = 0] { Print(++x, ++y); } // Equivalent in C++23: // auto FunctionDeclaration = [x, y = int32_t{0}] mutable -> void { Print(++x, ++y); }; } ``` Functions also support so-called ["positional parameters"](#positional-parameters) that are defined at their point of use using a dollar sign and a non-negative integer. They are implicitly of type `auto`. ``` fn Foo() { let lambda: auto = fn { Print($0); }; // Equivalent in C++23: // auto lambda = [](auto _0, auto...) -> void { Print(_0); }; // Equivalent in Swift: // let lambda = { Print($0) }; fn FunctionDeclaration { Print($0); } // Equivalent in C++23: // auto FunctionDeclaration = [](auto _0, auto...) -> void { Print(_0); }; // Equivalent in Swift: // let FunctionDeclaration = { Print($0) }; } ``` Of course, functions can also have named parameters, but a single function can't have both named and positional parameters. ``` fn Foo() { // In a variable: let lambda: auto = fn (v: auto) { Print(v); }; // Equivalent in C++23: // const auto lambda = [](v: auto) -> void { Print(v); }; // In a function call: Foo(fn (v: auto) { Print(v); }); // Equivalent in C++23: // Foo([](v: auto) { Print(v); }); fn FunctionDeclaration(v: auto) { Print(v); } // Equivalent in C++23: // auto FunctionDeclaration(v: auto) -> void { Print(v); } } ``` And in additional the option between positional and named parameters, deduced parameters are always permitted. ``` fn Foo() { let lambda: auto = fn [T:! Printable](t: T) { Print(t); }; fn FunctionDeclaration[T:! Printable](t: T) { Print(t); } } ``` ### Syntax Defined Function definitions and lambda expressions have one of the following syntactic forms (where items in square brackets are optional and independent): `fn` \[_name_\] \[_implicit-parameters_\] \[_tuple-pattern_\] `=>` _expression_ \[`;`\] `fn` \[_name_\] \[_implicit-parameters_\] \[_tuple-pattern_\] \[`->` _return-type_\] `{` _statements_ `}` The first form is a shorthand for the second: "`=>` _expression_ `;`" is equivalent to "`-> auto { return` _expression_ `; }`". _implicit-parameters_ consists of square brackets enclosing a optional default capture mode and any number of explicit captures, function fields, and deduced parameters, all separated by commas. The default capture mode (if any) must come first; the other items can appear in any order. If _implicit-parameters_ is omitted, it is equivalent to `[]`. The presence of _name_ determines whether this is a function definition or a lambda expression. The trailing `;` in the first form is required for a function definition, but is not part of the syntax of a lambda expression. The presence of _tuple-pattern_ determines whether the function body uses named or positional parameters. The presence of "`->` _return-type_" determines whether the function body can (and must) return a value. To understand how the syntax between lambdas and function declarations is reasonably "continuous", refer to this table of syntactic positions and the following code examples. | Syntactic Position | Syntax Allowed in Given Position (optional, unless otherwise stated) | | :----------------: | :----------------------------------------------------------------------------------------------------------------: | | A1 | Required Returned Expression ([positional parameters](#positional-parameters) allowed) | | A2 | Required Returned Expression ([positional parameters](#positional-parameters) disallowed) | | B | [Default Capture Mode](#default-capture-mode) | | C | Explicit [Captures](#function-captures), [Function Fields](#function-fields) and Deduced Parameters (in any order) | | D | Explicit Parameters | | E1 | Body of Statements (no return value) ([positional parameters](#positional-parameters) allowed) | | E2 | Body of Statements (with return value) ([positional parameters](#positional-parameters) allowed) | | E3 | Body of Statements (no return value) ([positional parameters](#positional-parameters) disallowed) | | E4 | Body of Statements (with return value) ([positional parameters](#positional-parameters) disallowed) | | F | Required Return Type | | G | Function Declaration Name | ``` // Lambdas (all the following are in an expression context and are // themselves expressions) fn => A1 fn [B, C] => A1 fn (D) => A2 fn [B, C](D) => A2 fn { E1; } fn -> F { E2; } fn [B, C] { E1; } fn [B, C] -> F { E2; } fn (D) { E3; } fn (D) -> F { E4; } fn [B, C](D) { E3; } fn [B, C](D) -> F { E4; } ``` ``` // Function Declarations (all the following are allowed as statements in a // function body or as declarations in other scopes) fn G => A1; fn G[B, C] => A1; fn G(D) => A2; fn G[B, C](D) => A2; fn G { E1; } fn G -> F { E2; } fn G[B, C] { E1; } fn G[B, C] -> F { E2; } fn G(D) { E3; } fn G(D) -> F { E4; } fn G[B, C](D) { E3; } fn G[B, C](D) -> F { E4; } ``` ## Introducer **Proposal**: Introduce with the `fn` keyword to mirror function declarations. If a statement or declaration begins with `fn`, a name is required and it becomes a function declaration. Otherwise, if in an expression context, `fn` introduces a lambda. ``` let lambda1: auto = fn => T.Make(); let lambda2: auto = fn -> T { return T.Make(); }; fn FunctionDeclaration1 => T.Make(); fn FunctionDeclaration2 -> T { return T.Make(); } ``` ## Positional Parameters **Proposal**: Positional parameters, introduced in the body of a function by way of the dollar sign and a corresponding non-negative parameter position integer (ex: `$3`), are `auto` parameters to the function in which they are defined. They can be used in any lambda or function declaration that lacks an explicit parameter list (parentheses). They are variadic by design, meaning an unbounded number of arguments can be passed to any function that lacks an explicit parameter list. Only the parameters that are named in the body will be read from, meaning the highest named parameter denotes the minimum number of arguments required by the function. The function body is free to omit lower-numbered parameters (ex: `fn { Print($10); }`). This syntax was inpsired by Swift's [Shorthand Argument Names](https://docs.swift.org/swift-book/documentation/the-swift-programming-language/closures/#Shorthand-Argument-Names). ``` // A lambda that takes two positional parameters being used as a comparator Sort(my_list, fn => $0.val < $1.val); // In Swift: { $0.val < $1.val } ``` ### Positional Parameter Restrictions **Proposal**: There are two restrictions applied to functions with positional parameters. The first restriction is that the definitions of function declarations must be attached to the declarations. The second restriction is that positional parameters can only be used in a context where there is exactly one enclosing function without an explicit parameter list. For example... ``` fn Foo1 { fn Bar1 {} // ❌ Invalid: Foo1 is already using positional parameters } fn Foo2 { Print($0); fn Bar2 {} // ❌ Invalid: Foo2 is already using positional parameters } fn Foo3 { fn Bar3 { Print($0); // ❌ Invalid: Foo3 is already using positional parameters } } fn Foo4() { fn Bar4 { Print($0); // ✅ Valid: Foo4 has explicit parameters } } fn Foo5 { fn Bar5() {} // ✅ Valid: Bar5 has explicit parameters } fn Foo6() { my_list.Sort( fn => $0 < $1 // ✅ Valid: Foo6 has explicit parameters ); } ``` ## Function Captures **Proposal**: Function captures in Carbon mirror the non-init captures of C++. A function capture declaration consists of a capture mode (for `var` captures) followed by the name of a binding from the enclosing scope, and makes that identifier available in the inner function body. The lifetime of a capture is the lifetime of the function in which it exists. For example... ``` fn Foo() { let handle: Handle = Handle.Get(); var thread: Thread = Thread.Make(fn [var handle] { handle.Process(); }); thread.Join(); } ``` ``` fn Foo() { let handle: Handle = Handle.Get(); fn MyThread[handle]() { handle.Process(); } var thread: Thread = Thread.Make(MyThread); thread.Join(); } ``` ### Capture Modes **Proposal**: `let` and `var` can appear as function captures. They behave as they would in regular bindings. To prevent ambiguities, captures can only exist on functions where the definition is attached to the declaration. This means they are supported on lambdas (which always exist in an expression context) and they are supported on function declarations that are immediately defined inside the body of another function (which is in a statement context), but they are not supported on forward-declared functions nor are they supported as class members where `self: Self` is permitted. Capture modes can be used as [default capture mode specifiers](#default-capture-mode) or for explicit captures as shown in the example code below. ``` fn Example { var a: i32 = 0; var b: i32 = 0; let lambda: auto = fn [a, var b] { a += 1; // ❌ Invalid: by-value captures are immutable b += 1; // ✅ Valid: Modifies the captured copy of the by-object capture }; lambda(); } ``` ``` fn Example { fn Invalid() -> auto { var s: String = "Hello world"; return fn [s]() => s; } // ❌ Invalid: returned lambda references `s` which is no longer alive // when the lambda is invoked. Print(Invalid()()); } ``` Note: If a function object F has mutable state, either because it has a by-object capture or because it has a by-object function field, then a call to F should require the callee to be a reference expression rather than a value expression. We need a mutable handle to the function in order to be able to mutate its mutable state. ### Default Capture Mode **Proposal**: By default, there is no capturing in functions. The lack of any square brackets is the same as an empty pair of square brackets. Users can opt into capturing behavior. This is done either by way of individual explicit captures, or more succinctly by way of a default capture mode. The default capture mode roughly mirrors the syntax `[=]` and `[&]` capture modes from C++ by being the first thing to appear in the square brackets. ``` fn Foo1() { let handle: Handle = Handle.Get(); fn MyThread[var]() { handle.Process(); // `handle` is captured by-object due to the default capture // mode specifier of `var` } var thread: Thread = Thread.Make(MyThread); thread.Join(); } fn Foo2() { let handle: Handle = Handle.Get(); fn MyThread[let]() { handle.Process(); // `handle` is captured by-value due to the default capture // mode specifier of `let` } var thread: Thread = Thread.Make(MyThread); thread.Join(); } ``` ## Function Fields **Proposal**: Function fields mirror the behavior of init captures in C++. A function field definition consists of an irrefutable pattern, `=`, and an initializer. It matches the pattern with the initializer when the function definition is evaluated. The bindings in the pattern have the same lifetime as the function, and their scope extends to the end of the function body. To prevent ambiguities, function fields can only exist on functions where the definition is attached to the declaration. This means they are supported on lambdas (which always exist in an expression context) and they are supported on function declarations that are immediately defined inside the body of another function (which is in a statement context), but they are not supported on forward-declared functions nor are they supported as class members where `self: Self` is permitted. ``` fn Foo() { var h1: Handle = Handle.Get(); var h2: Handle = Handle.Get(); var thread: Thread = Thread.Make(fn [a: auto = h1, var b: auto = h2] { a.Process(); b.Process(); }); thread.Join(); } ``` ## Copy Semantics **Proposal**: To mirror the behavior of C++, function declarations and lambdas will be as copyable as their contained function fields and function captures. This means that, if a function holds a by-object function field, if the type of the field is copyable, so too is the function that contains it. This also applies to captures. The other case is by-value function fields. Since C++ const references, when made into fields of a class, prevent the class from being copied assigned, so too should by-value function fields prevent the function in which it is contained from being copied assigned. ## Self and Recursion **Proposal**: To mirror C++'s use of capturing `this`, `self` should always come from the outer scope as a capture. `self: Self` is never permitted on lambdas. For function declarations, it is only permitted when the function is a member of a class type or an interface, such that it refers to the class/interface and not to the function itself. Note: Given the direction in [#3720](https://github.com/carbon-language/carbon-lang/pull/3720), an expression of the form `x.(F)`, where `F` is a function with a `self` or `addr self` parameter, produces a callable that holds the value of `x`, and does not hold the value of `F`. As a consequence, we can't support combining captures and function fields with a `self` parameter. ## Rationale Lambdas in Carbon serve two purposes. The primary purpose is in support of the ["Code that is easy to read, understand, and write"](/docs/project/goals.md#code-that-is-easy-to-read-understand-and-write) goal. It is because of this goal that we leverage syntactic features such as the returned expression (indicated by `=>`) and positional parameters (indicated by the lack of a tuple pattern of explicit parameters as well as the use of `$N` in the body of such functions). In addition, Lambdas serve to support the [Interoperability with and migration from existing C++ code](/docs/project/goals.md#interoperability-with-and-migration-from-existing-c-code) goal. They are defined at their point of use and are often anonymous, meaning replacing C++ lambdas solely with function declarations will create an ergonomic burden compounded by the need for the migration tool to select a name. ## Alternatives Considered ### Alternative Considered: Terse vs Elaborated Proposed above is a continuous syntax between lambdas and function declarations. Alternatively, Carbon could adopt a few different categories of functions, as was considered in a previous discussion doc ([Lambdas Discussion 2](https://docs.google.com/document/d/14K_YLjChWyyNv3wv5Mn7uLFHa0JZTc21v_WP8RzC8M4/)). These categories would be terse lambdas, elaborated lambdas, and function declarations. Unfortunately, separating these categories out presented a syntactic challenge in the form of cliffs, explained below. As a result, they were decided against. Terse lambdas were slated to be the most compact form of a lambda. Combined with a [sigil introducer](#alternative-considered-sigil), they would be syntactically minimal. One way in which syntax was minimized was the granting of an **implicit** default [capture](#function-captures) mode. If no square brackets were present, by-value captures would be allowed. This, combined with the lack of an arrow to signify a return value, created syntax of the following form (being passed into the filter function below). ``` let zero: i32 = 0; let list_all: List(i32) = GetAllValues(); let list_positive: List(i32) = list_all.Filter( @ $0 > zero ); ``` To give users more control over the feature set in a lambda, the next step up was an elaborated lambda. This provided the ability to add both square brackets and explicit parameters to lambdas at the cost of more syntax. Unfortunately, this also meant there was a bit of a _syntactic cliff_ and a stumbling block. It was considered desirable for empty square brackets to mean capturing is disabled. But since the no-square-brackets form needed to support capturing for terse lambdas, elaborated lambdas needed to both add the square brackets and also add an explicit default capture mode at the same time just to maintain the existing capturing behavior. The net result was code that looked like the following (being passed into the filter function again). ``` let zero: i32 = 0; let list_all: List(i32) = GetAllValues(); let list_positive: List(i32) = list_all.Filter( @[let](x: auto) x > zero ); ``` Finally, if a user wanted to upgrade a lambda to a function declaration, this created another cliff where they needed to switch from the sigil to the `fn` keyword, on top of adding a name. Ultimately these downsides suggested that a continuous syntax was the better path forward, despite the face that the shortest spellable lambda would be a bit less terse than the alternative considered. ### Alternative Considered: Sigil Proposed above is the use of `fn` as the [introducer](#introducer) for all functions/lambdas. An alternative considered was to tntroduce with a sigil, such as `$` or `@`. Since introducer punctuation is such a scarce resource, and since there was no consensus on what sigil would best represent a lambda, and since there was a desire to create a more continuous syntax between lambdas and function declarations, this alternative was decided against. It would have looked like the following: ``` let lambda1: auto = @ => T.Make(); let lambda2: auto = @[]() -> T { return T.Make(); }; ``` ### Alternative Considered: Additional Positional Parameter Restriction In addition to [the above proposed restrictions](#positional-parameter-restrictions) to positional parameters, an additional restriction was considered. That being, visibility of functions with positional parameters could be restricted to only non-public interfaces. This alternative was considered by way of a leads question ([#3860](https://github.com/carbon-language/carbon-lang/issues/3860)) and was decided against, with the speculation that such a restriction may be enforced by way of an HOA rule as opposed to a compiler error. ### Alternative Considered: Recursive Self Proposed above is a deliniation between function declarations that can provide a `self` parameter and functions declarations (plus lambdas) which cannot. An alternative was considered such that, for use in recursion, `self: Self` could be permitted on all functions and lambdas and refer to the function itself. Unfortunately, it created a bit of a discontinuity between class members and non-class members and was thus decided against. ## Future Work ### Future Work: Reference Captures Much discussion has been had so far about the implications of capturing by reference. For now, such behavior is supported not through captures but instead through function fields formed from the address of an object in the outer scope. It is **imperative** that more work be done in this area to address the ergonomic concerns of the current solution. ================================================ FILE: proposals/p3927.md ================================================ # More consistent package syntax [Pull request](https://github.com/carbon-language/carbon-lang/pull/3927) ## Table of contents - [Abstract](#abstract) - [Problem](#problem) - [Background](#background) - [Proposal](#proposal) - [Details](#details) - [Rationale](#rationale) - [Alternatives considered](#alternatives-considered) - [Mandatory `api` or `impl` as suffix](#mandatory-api-or-impl-as-suffix) - [Put the `impl` modifier before `library`](#put-the-impl-modifier-before-library) ## Abstract Change the syntax for `package` declarations from: ```carbon [package Foo] [library "bar"] api; [package Foo] [library "bar"] impl; ``` to ```carbon [package Foo] [library "bar"]; impl [package Foo] [library "bar"]; ``` ## Problem The `package` declaration is currently inconsistent with other Carbon declarations: - Modifier keywords for other declarations precede the introducer keyword. However, for package declarations, the `api` / `impl` modifier appears at the end of the declaration. - For most declarations describing the public interface of a library, we default to `public` because we prioritize the readability of the public interface over other concerns. However, the `api` tag in a `package` API declaration is mandatory, making the library interface more verbose than necessary. ## Background [Proposal #107: Code and name organization](/proposals/p0107.md) introduced the current syntax. It did [consider the possibility of omitting the `api` keyword](https://github.com/carbon-language/carbon-lang/blob/trunk/proposals/p0107.md#different-file-type-labels): > We've considered dropping `api` from naming, but that creates a definition > from absence of a keyword. It also would be more unusual if both `impl` and > `test` must be required, that api would be excluded. We prefer the more > explicit name. However, this argument did not and could not consider the inconsistencies between the choice made for package declaration and the choices made for other declarations, because those inconsistencies were created by later changes: - #107 used the `api` keyword as a marker for exporting names from an API file. Later, [proposal #752: api file default public](/proposals/p0752.md) removed this use of the `api` keyword, with the new rule being that declarations are in the public API by default, with an explicit keyword used to mark non-public declarations. This removed all uses of the `api` keyword other than in package declarations. - Rules for modifier keywords were added incrementally by various proposals, with the common syntactic approach that modifier keywords precede an introducer keyword in a declaration. In addition, the idea of having `test` files in addition to `api` and `impl` files has not yet been realised, and we have no current plans to add such a feature. While that may be an interesting avenue to pursue in future, using `test` as a modifier keyword may also be an interesting avenue to explore in that case too -- for example, to allow functions and types within an API file to be declared as test-only with a `test` modifier -- and so the possibility of `test` files isn't a robust rationale for choosing the syntax for `api` and `impl`. ## Proposal Remove the `api` keyword from `package` and `library` declarations. Consistent with #752, the way we define a public interface is by saying nothing at all. Turn the `impl` keyword on such declarations into a modifier keyword, consistent with its use in `impl fn` declarations. This reorders the `impl` keyword to the start of the declaration. In documentation, we refer to API files as "API files", not as "`api` files". For consistency, we also refer to implementation files as "implementation files", not as "`impl` files". We were previously inconsistent and used both terms. ## Details See design changes. ## Rationale - [Code that is easy to read, understand, and write](/docs/project/goals.md#code-that-is-easy-to-read-understand-and-write) - Small readability and writability gain for API files. - More consistency between different kinds of declaration. - Minor risk that an `impl` file will be interpreted as an API file due to missing the `impl` modifier. However, this is likely to be caught quickly, whether by file naming conventions, the lack of an implicit import of the "real" API file, or by duplicate API file detection in the toolchain. - [Interoperability with and migration from existing C++ code](/docs/project/goals.md#interoperability-with-and-migration-from-existing-c-code) - Marginally more consistent with C++ modules, which use `module Foo` vs `export module Foo` for the two cases -- with a leading keyword. However, C++ defaults to not exporting, so the case with a keyword is reversed, both here and in all other declarations. ## Alternatives considered ### Mandatory `api` or `impl` as suffix The rationale for changing from the status quo ante of having a mandatory `impl` or `api` keyword as a suffix is documented above. This proposal also harmonizes the `impl package` syntax with the `import package` syntax: ```carbon impl package Foo library "bar"; import package Baz library "quux"; ``` We consistently use a prefix for the package declaration for both of these cases. We also anticipate doing so for the `import reexport package ...` or `export import package ...` syntax that is currently under discussion. As a trivial side benefit, the degenerate case of the package declaration for the default library in the Main package would now be expressed as simply `;` rather than `api;`. We retain the rule that the package declaration is omitted entirely in this case, which is slightly easier to justify given that the omitted declaration would comprise only a semicolon. ### Put the `impl` modifier before `library` Because an implementation file is implementing part of a library, we can consider placing the `impl` keyword before the `library` keyword -- as a modifier on the `library` portion of the declaration -- instead of at the start of the overall package declaration. This leads to constructs such as: ```carbon package Foo impl library "bar"; package Foo impl; impl library "bar"; ``` While there is a logical rationale and consistent explanation for this approach, it results in the `impl` keyword's positioning appearing inconsistent: sometimes at the start, middle, or end of the declaration. This also doesn't make the `impl` declaration consistent with `import`, where the same argument can be made: an `import` imports the library, not the package. As a result, we do not pursue this direction. ================================================ FILE: proposals/p3938.md ================================================ # Exporting imported names [Pull request](https://github.com/carbon-language/carbon-lang/pull/3938) ## Table of contents - [Abstract](#abstract) - [Problem](#problem) - [Background](#background) - [Carbon exports](#carbon-exports) - [Other languages](#other-languages) - [Proposal](#proposal) - [Source file introduction](#source-file-introduction) - [Future work](#future-work) - [Namespaces](#namespaces) - [Rationale](#rationale) - [Alternatives considered](#alternatives-considered) - [Other `export` syntax structures](#other-export-syntax-structures) - [Other `export name` placements](#other-export-name-placements) - [Re-exporting cross-package](#re-exporting-cross-package) ## Abstract In order to support exporting imported names, add `export import library ` and `export ` syntax. ## Problem As we develop libraries such as the prelude, we want the ability to indicate that an imported name should be re-exported for indirect use. At present, we can use the prototype `alias` to expose names on a case-by-case basis (`alias Foo = Bar;`), but it doesn't work to export the _same_ name (`alias Foo = Foo;` is a name conflict), and we want to be able to more broadly forward a library's exported names. For example: ``` package Foo library "internal"; // Declare C. class C; ``` ``` package Foo; // We want the ability to expose everything imported here. import library "internal"; ``` ``` import library Foo; // Uses C by way of the default library. var c: Foo.C; ``` ## Background Some of the syntax options were discussed [on Discord](https://discord.com/channels/655572317891461132/1217182321933815820/1234534411350048810). ### Carbon exports Names declared in a Carbon file are currently exported by default. A `private` keyword may be used to prevent that export. Note C++ and TypeScript use private-by-default behavior, so the syntax choices that make sense elsewhere may not make as much sense for Carbon. As described in the problem statement, `alias` offers incomplete re-export support. However, `alias` is not fully designed; it's modeled on informal discussions. ### Other languages In [C++ modules](https://en.cppreference.com/w/cpp/language/modules), this is `export import ...`. In [TypeScript modules](https://www.typescriptlang.org/docs/handbook/2/modules.html), similar syntax might look like: ```typescript import * from '' export * from '' ``` In Python, names in a module are generally public, and imported names are accessible too. For example, given `import datetime`, the module makes the name `datetime` available to clients. There is interest in [more explicit `export` syntax](https://discuss.python.org/t/add-the-export-keyword-to-python/28444). In other languages, such as Java, Kotlin, or Go, direct re-exports aren't supported. Instead, the expectation seems to be that either a copy of the entity would be exported, or it should just be moved. ## Proposal Support the `export` keyword as a modifier to `import library ` (excluding cross-package imports). This is `export import` for short. For example: ```carbon export import library "lib"; ``` Additionally, support the `export` keyword on individual, file-scoped or namespace-scoped entities (excluding entities in other packages, and namespaces themselves). This is `export name` for short. For example: ```carbon // Export an entity: export Foo; // Export an entity inside a namespace: export NS.Bar; // Invalid: exporting namespaces is disallowed. export NS; ``` Although exporting cross-package names is disallowed, note that `alias` can be used to add a package-local name that originates from another package, which then is valid for export. For example: ```carbon import package Other; // Invalid: cross-package exports are disallowed. export Other.Obj; // This introduces a package-local name. The alias is exported, and other // libraries importing this library may export `Obj`. alias Obj = Other.Obj; ``` The `export` keyword is only valid in files which are valid to import. It is invalid in files which cannot be imported: implementation files and `Main//default`. ### Source file introduction In the [source file introduction](/docs/design/code_and_name_organization/README.md#source-file-introduction), `export import` directives are intermingled with other `import` directives. `export name` directives are normal code and cannot be intermingled with any `import` directives, including `export import` directives. This allows: ``` import library "foo"; export import library "wiz"; import library "bar"; export FooType; class C { ... }; export BarType; ``` This disallows: ``` import library "foo"; // Invalid: All `import` directives must come before other code, including // `export name`. export FooType; import library "bar"; class C { ... }; // Invalid: `export import` must be grouped with `import` directives. export import library "wiz"; ``` ## Future work ### Namespaces Namespaces are not valid arguments to `export`; entities in namespaces must be individually exported. This keeps open a future design option of having `export` on a namespace export all imported names inside the namespace, such as `export NS;`. This could also be achieved with `*` syntax, such as `export NS.*;`. There hasn't been discussion of this option, and this proposal takes no stance on the option. ## Rationale - [Software and language evolution](/docs/project/goals.md#software-and-language-evolution) - `export ` allows moving entities between libraries without needing to make modifications to clients, enabling more incremental refactorings. - [Code that is easy to read, understand, and write](/docs/project/goals.md#code-that-is-easy-to-read-understand-and-write) - Export logic in general is intended to support factoring large or complex APIs into multiple, smaller files. For example, with the prelude, we'll have many types, interfaces, and implementations: concise re-exporting logic will make it easy to provide a singular `prelude.carbon` that exports all related functionality. ## Alternatives considered ### Other `export` syntax structures We discussed several different syntax choices. A couple placement alternatives discussed were: - Put `export` before `library`. For example, `import export library "lib"`. - An advantage of this is that if we support cross-package re-exports, `import Foo export library "lib"` could make it clearer the library is being re-exported, rather than the package. - A disadvantage is that we would probably not put other keywords between the package and library. - Put `export` as a suffix. For example, `import library "lib" export`. - An advantage of this is that it makes `import` statements line up better when some may not have the `export` modifier. The current design uses `export` as a prefix. This is for consistency with how we put other modifier keywords, such as `private` or `extern`, prior to the introducer keyword. A couple keyword alternatives discussed (alongside placement options) were: - `reexport` - An advantage noted is that it may read more intuitively for some developers. - This proposal suggests `export` because it mirrors `import`, and it's consistent with multiple other languages. It's also shorter, and Carbon often chooses keywords for shortness. - `exported` and `reexported` - These didn't seem to read as clearly as `export` or `reexport`. ### Other `export name` placements We see several options for `export name` placement. This compares them, focusing on advantages and disadvantages for each option. 1. `export name` with `import`s `export name` can (only) appear in the preamble, with the imports, and cannot appear with the other declarations in the library. Note this option could either have `export name` refer to earlier `import`s (creating an ordering consistency issue), or expend additional effort in order to track whether a name was already imported at the site of the `export`. Advantages: - No need to teach developers they cannot (don't need to) `export` locally introduced names. Disadvantages: - Although the restricted placement might imply placement is tied to specific libraries, that's not the case. This could mislead developers. - In theory, we could enforce this, but then we could end up breaking code if the path a name is imported through changes. 2. `export name` with other declarations `export name` can only appear after imports. This means that all names valid for `export` will already be made available. Advantages: - `import` remains very special. - Makes it unambiguous that names valid for `export` are already imported. Disadvantages: - Prevents placing `export name` next to the import that is expected to add the name. - Means `export import` and `export name` will be in different sections: no single place to look for re-exports. 3. No ordering for `export name` Let developers choose what the prefer. Advantages: - Maximum flexibility, HOA rule. Disadvantages: - Most inconsistent with the desire to treat `import` as special. We're choosing option (2). The name lookup issues avoided by requiring `export` be below `import` directives seem worthwhile. A possible option to (2) might be to create an additional section dedicated to `export name` below the `import` section. This proposal suggests avoiding that in order to avoid increasing the amount of enforced ordering in Carbon files. ### Re-exporting cross-package As proposed, re-exporting names from other packages will not be supported. This is done to continue maintaining package boundaries, and so that names aren't unexpectedly introduced. For example: ```carbon package Foo; class C; ``` ```carbon package Bar; export import Foo; ``` ```carbon package Wiz; import Bar; ``` In the last package `Wiz`, it might be confusing if the name `Foo.C` should be introduced: typically importing `Bar` would put everything under the `Bar` namespace. We may choose to re-examine this choice, but this proposal does not include support. ================================================ FILE: proposals/p3980.md ================================================ # Singular `extern` declarations [Pull request](https://github.com/carbon-language/carbon-lang/pull/3980) ## Table of contents - [Abstract](#abstract) - [Problem](#problem) - [Background](#background) - [Proposal](#proposal) - [Declarations](#declarations) - [Owning `extern` declarations](#owning-extern-declarations) - [Details](#details) - [Type coherency](#type-coherency) - [Impact on indirect imports](#impact-on-indirect-imports) - [Indirect imports of non-`extern` types](#indirect-imports-of-non-extern-types) - [Using imported declarations](#using-imported-declarations) - [`private extern`](#private-extern) - [Validation for non-owning `extern library` declarations](#validation-for-non-owning-extern-library-declarations) - [No syntactic matching for `extern library` declarations](#no-syntactic-matching-for-extern-library-declarations) - [Versus proposal #3762](#versus-proposal-3762) - [Rationale](#rationale) - [Future work](#future-work) - [`extern` and template interactions](#extern-and-template-interactions) - [Alternatives considered](#alternatives-considered) - [Allow multiple non-owning declarations, remove the import requirement, or both](#allow-multiple-non-owning-declarations-remove-the-import-requirement-or-both) - [Total number of allowed declarations (owning and non-owning)](#total-number-of-allowed-declarations-owning-and-non-owning) - [Do not restrict the number of forward declarations](#do-not-restrict-the-number-of-forward-declarations) - [Allow up to two declarations total](#allow-up-to-two-declarations-total) - [Allow up to four declarations total](#allow-up-to-four-declarations-total) - [Don't require a modifier on the owning declarations](#dont-require-a-modifier-on-the-owning-declarations) - [Only require `extern` on the first owning declaration](#only-require-extern-on-the-first-owning-declaration) - [Separate require-direct-import from non-owning declarations](#separate-require-direct-import-from-non-owning-declarations) - [Other `extern` syntaxes](#other-extern-syntaxes) - [Have types with `extern` members re-export them](#have-types-with-extern-members-re-export-them) - [Require syntactic matching for `extern library` declarations](#require-syntactic-matching-for-extern-library-declarations) ## Abstract An entity may be declared `extern` (such as `extern class Foo;`); this means that its type is only complete if the definition is directly imported. It also allows for a single declaration in a different library, which must be marked as `extern library ""` (such as `extern library "Bar" class Foo;`). Also, establish a different rule of thumb for when modifier keywords are required: modifier keywords are required when, if prior optional declarations were removed, the lack of the modifier keyword would change behavior. ## Problem In the `extern` model from [#3762: Merging forward declarations](https://github.com/carbon-language/carbon-lang/pull/3762), multiple `extern` declarations are allowed. [#3763: Matching redeclarations](https://github.com/carbon-language/carbon-lang/pull/3763) further evolved the `extern` keyword. The prior `extern` model assumed that the `extern` and non-`extern` declarations of a class formed two different types, which could be merged. [As discussed on #packages-and-libraries](https://discord.com/channels/655572317891461132/1217182321933815820/1230990636073881693), this runs into an issue with code such as: ``` library "a"; class C {} ``` ``` library "b"; extern class C; extern fn F() -> C*; ``` ``` library "c"; import library "a"; extern fn F() -> C*; ``` Here, the return types of `F` differ. This proposal aims to address the differing return types by unifying the type of `C` regardless of whether it's `extern`. This could be done under multiple different approaches, and this proposal aims for one which enables efficient implementation strategies. ## Background Proposals: - [#3762: Merging forward declarations](https://github.com/carbon-language/carbon-lang/pull/3762) - [#3763: Matching redeclarations](https://github.com/carbon-language/carbon-lang/pull/3763) Discussions: - [#packages-and-libraries: `extern` type coherency](https://discord.com/channels/655572317891461132/1217182321933815820/1230990636073881693) - [#packages-and-libraries: When to allow/disallow redeclarations](https://discord.com/channels/655572317891461132/1217182321933815820/1236016051632865421) - [Open discussion 2024-05-09: Number of allowed redeclarations](https://docs.google.com/document/d/1s3mMCupmuSpWOFJGnvjoElcBIe2aoaysTIdyczvKX84/edit?resourcekey=0-G095Wc3sR6pW1hLJbGgE0g&tab=t.0#heading=h.bu7djkos4xo) - [Issue #3986: Alternative naming for `has_extern` keyword](https://github.com/carbon-language/carbon-lang/issues/3986) - [Issue #4025: Handling of indirect access of `extern` types](https://github.com/carbon-language/carbon-lang/issues/4025) - [#typesystem: Will `&` have an extension point?](https://discord.com/channels/655572317891461132/708431657849585705/1258150877714452581) ## Proposal ### Declarations A given entity may have up to three declarations: - An optional, non-owning `extern library ""` declaration - It must be in a separate library from the definition. - The owning library's API file must import the `extern` declaration, and must also contain a declaration. - An optional, owning forward declaration - This must come before the definition. The API file is considered to be before the implementation file. - A required, owning definition The consequential changes to the [problem example](#problem) are then: ``` library "a"; // This proposal makes the import required. import library "b"; // This proposal makes `extern` required here. extern class C {} ``` ``` library "b"; // This proposal makes `library "a"` required here. extern library "a" class C; extern fn F() -> C*; ``` ``` library "c"; import library "a"; extern fn F() -> C*; ``` ### Owning `extern` declarations On an owning `extern` declaration, such as `extern class C {}`, there are two key effects: 1. The declaration must be explicitly imported in order to be complete. - An "explicit import" means some import path exists where the name is available to name lookup, including `export import` and `export `. 2. A non-owning `extern library "` declaration is allowed, but not required. If _either_ owning declaration has the `extern` modifier, _both_ must have it. ## Details ### Type coherency In the context of the example that is the [problem](#problem), `C` will produce the same type regardless of whether `C` is the owning or non-owning declaration. This means that both function signatures have identical types. We do this by only producing a complete type if the owning definition of `C` is imported by name: either directly through `import library "a"`, or indirectly through a chain of `export import library "a"` and `export C;`. Otherwise, an incomplete type is used. This does mean that adding `extern` to an owning declaration changes the import semantic. As a consequence, it is a potentially breaking change for API consumers that didn't explicitly import the time. In the presence of `extern library "a" class C;`, the required `import library "b"` means that all owning `extern class C` declarations are able to see the `extern library "a" class C` declaration as a name collision, which is merged. This allows the compiler to easily apply the same type to all declarations. That in turn will be used to ensure libraries which import both understand the type equality. #### Impact on indirect imports An entity marked as `extern` is only complete when the definition is explicitly imported. In the following, examples of indirect, non-explicit uses are given inside `library "o"`. ``` library "m"; extern class C { fn Member(); } ``` ``` library "n"; import library "m"; fn F() -> C; var c: C = {}; var pc: C* = &c; ``` ``` library "o"; import library "n"; // Invalid: The return type of `C` is incomplete, making the function signature // invalid. fn G() { F(); } // Invalid: Accessing members requires `C` to be complete. fn UseC() { c.Member(); } // Valid: Taking the address of `C` doesn't require it to be complete. This is // possible because `&` doesn't have an extension point. var indirect_pc: auto = &c; // Invalid: Copying `C` requires the complete type. var copy_c: auto = c; // Valid: Pointer-to-pointer copies are okay. var copy_pc: auto = pc; ``` #### Indirect imports of non-`extern` types The above rules explicitly do not apply for non-`extern` types, as decided in [Issue #4025](https://github.com/carbon-language/carbon-lang/issues/4025). In other words: ``` library "a"; class C { fn F(); } ``` ``` library "b"; import library "a"; fn G() -> C; ``` ``` library "c"; import library "b"; // Valid: `C` is complete here, even though it's not in name lookup. G().F(); ``` ### Using imported declarations Since `extern library "a" class C;` must be imported by the owning library, we now allow uses of the imported name prior to its declaration within the same file. This is a divergence from [#3762](https://github.com/carbon-language/carbon-lang/pull/3762). It means the following now works: ``` library "extern"; extern library "use_extern" class MyType; ``` ``` library "use_extern"; import library "extern" // Uses the `extern library` declaration. fn Foo(val: MyType*); extern class MyType { fn Bar[addr self: Self*]() { Foo(self); } } ``` ### `private extern` Previously, in [#3762](https://github.com/carbon-language/carbon-lang/pull/3762), a non-owning `private extern` was valid to declare something as extern without exposing the name. In this proposal, that would be a non-owning `private extern library ""` for an owning public `extern` declaration. However, rather than supporting this version of the syntax, it will instead be invalid because the name would never be visible to the owning library. Instead, visibility must match between an `extern library ""` declaration and the owning `extern` declaration. Note, because an owning `extern` declaration can be used independently of `extern library ""`, an owning `private extern` declaration is valid in an API file. It has no special behaviors about it, and is merged as normal. ### Validation for non-owning `extern library` declarations We should offer some validation that the library in `extern library` is correct. When the owning library is incorrect, it's very likely to be detected in two cases: - A compile-time error when the owning library imports the non-owning library, when the owning declaration is evaluated. - A link-time error as a fallback. Other cases, such as when both libraries are independently imported, may or may not be caught, dependent upon the cost of validation. ### No syntactic matching for `extern library` declarations The non-owned `extern library` declarations will only use semantic matching for redeclarations, not syntactic matching. Details of syntactic matching laid out in [#3763](https://github.com/carbon-language/carbon-lang/pull/3763) will only apply to owned declarations in the same library, which may include owned `extern` declarations. ### Versus proposal #3762 Versus proposal [#3762](https://github.com/carbon-language/carbon-lang/pull/3762), the `extern` feature is essentially rewritten. No part of `extern` should be assumed to still apply. ## Rationale - [Software and language evolution](/docs/project/goals.md#software-and-language-evolution) - Unifying the type of `extern` entities addresses a type coherency issue. - The `extern` behavior of requiring an explicit import is intended to assist library authors in carefully managing the dependencies on their API. - [Fast and scalable development](/docs/project/goals.md#fast-and-scalable-development) - Requiring the non-owning `extern library` declaration be imported by the owning library should improve compiler performance. This proposal makes a trade-off with [Interoperability with and migration from existing C++ code](/docs/project/goals.md#interoperability-with-and-migration-from-existing-c-code). The restriction of a unique `extern` declaration is expected to require additional work in migration, because C++ `extern` declarations will need to be consolidated. This is currently counter-balanced by the trade-offs involved, although it may result in a reevaluation of that aspect of this proposal. ## Future work ### `extern` and template interactions We've only loosely discussed template interactions with `extern`. Right now, what we expect is that when a template declaration uses an `extern` type, the _instantiation_ still occurs in the calling file. Thus, the `extern` type's name would need to be imported in both the file declaring the template, and the file calling the template. When the template is in the same package as the `extern` type, it could re-export it. However, we don't support re-exporting names cross-package, and something like `let template ExternType:! auto = OwningPackage.ExternType;` would not actually forward the _completeness_ of `ExternType`. This is expected to be inconvenient, but it may be okay if `extern` sees limited use. It may also be that the template model ends up different from expected. ## Alternatives considered ### Allow multiple non-owning declarations, remove the import requirement, or both We limit to one non-owning `extern library` declaration. Continuing to allow multiple `extern library` declarations (the previous state) is feasible. Similarly, we could not require the owning `extern` declaration to import the non-owning `extern library` declaration; this could be done with or without multiple non-owning `extern library` declarations. For this set of alternatives, the issues which would arise are similar. In the compiler, we want to be able to determine that two types are equal through a unique identifier, such as a 32-bit integer. When one declaration sees another directly, as through an import, we identify the redeclaration by name, and reuse the unique identifier. This deduplication can occur once per declaration. Indirect imports can continue to use the unique identifier. We could instead support unifying declarations that did not see each other. However, this would require canonicalizing all types by name instead of by unique identifier. For example, consider: ``` package Other library "type"; extern class MyType { fn Print(); }; ``` ``` package Other library "use_type"; import library "type"; fn Make() -> MyType*; ``` ``` package Other library "extern"; extern library "type" class MyType; ``` ``` package Other library "use_extern"; import library "extern"; fn Print(val: MyType*); ``` ``` library "merge"; import Other library "use_type"; import Other library "use_extern"; Other.Print(Other.Make()); ``` Here, the "merge" library doesn't see either declaration of `MyType` directly. However, `Print(Make())` requires that both declarations of `MyType` be determined as equivalent. This particular indirect use also means that the names will not have been added to name lookup, so there is no reason for the two declarations to be associated by name. In order to do merge these declarations, we would need to identify that fully qualified names and other structural details are equivalent when the type is used (including non-explicit uses, such as interface lookup). We could achieve this, for example, by having a name lookup table for in-use types, managed per library. Each library would also need to validate that declarations were semantically equivalent, versus the current approach validating as part of the redeclaration. The cost of a per-library approach is expected to have a significant impact on the amount of work done as part of semantic analysis. We may end up wanting to do similar work in order to improve diagnostics for invalid cases where the non-owning `extern library` is not correctly declared and imported. However, additional work building good diagnostics for already-identified invalid code is less of a concern than additional work on fully valid code. In order to maintain a high-performance compiler, we are taking a restrictive approach that makes it simpler to associate type information. ### Total number of allowed declarations (owning and non-owning) A few options were considered regarding the number of allowed declarations. We limit to two owning declarations: the optional forward declaration, and required definition. The need to provide interface implementations (for example, `impl MyType as Add`) is considered to constrain this choice. In this category, alternatives considered were: - Do not restrict the number of declarations - Allow up to two declarations total - Allow up to four declarations total Details for why each alternative was declined are below. #### Do not restrict the number of forward declarations We could not restrict the number of forward declarations, allowing an arbitrary amount -- possibly also after the definition. This would be consistent with C++. One thing to consider here is modifier keyword behavior. If we require modifier keywords to match across all declarations, that could become a maintenance burden for developers. If we don't, it makes the meaning of a given forward declaration more ambiguous. This option is declined due to the lack of clear benefit. #### Allow up to two declarations total Under this option, we would only allow one forward declaration, treating the non-owning `extern library` declaration as a forward declaration. This would mean two declarations overall, instead of three. For this, the main concern was interactions between file placement of the definition, and file placement of interface implementations. Interface implementations must generally be in API files in order to be seen by other libraries. For example: ``` library "i"; interface I {} ``` ``` library "e"; import library "i"; extern library "o" class C; extern library "o" impl C as I; ``` ``` library "o"; import library "e"; extern class C { } extern impl C as I; ``` ``` impl library "o"; extern impl C as I { } ``` If the definition is required to be in the API file in order to allow the interface implementations in the API file, the API file would need to import libraries required to construct the definition. That could create issues for separation of build dependencies, and could also make it more difficult to unravel some dependency cycles between libraries. If the definition was allowed to be in the implementation file even when there were interface implementations in the API file, the ambiguity of seeing a non-owning `extern library` declaration and being unsure of whether this was the owning library could have negative consequences for evaluation of interface constraints. The purpose of allowing a forward declaration when there is a non-owning `extern` declaration is to make it clear for interface implementations that they exist in the owning library, while processing the API file. #### Allow up to four declarations total The four declarations would be: 1. Non-owning `extern library` declaration 2. Forward declaration in API file 3. Forward declaration in implementation file 4. Definition The number of forward declarations allowed is consistent with the current state from [#3762](https://github.com/carbon-language/carbon-lang/pull/3762). This would allow for clarity when defining in the implementation file, to also be able to put a forward declaration above -- even when the forward declaration is pulled from the API file. If we're allowing declarations from another file (including the non-owning `extern library` declaration) to be used before an entity is declared in the same file, the motivating factor for allowing a repeat forward declaration in an implementation file is removed. Previously, that was required for an entity to be referenced prior to its definition. In discussion of this option, it was considered unclear why we would allow two forward declarations, but not allow even more. The more popular choice seemed to be not restricting, which was also declined. ### Don't require a modifier on the owning declarations Instead of requiring an `extern` modifier on owning declarations, we could infer from the presence of a non-owning `extern library` declaration. We had declined allowing a definition to control whether `extern library` was allowed in discussion of [#3762](https://github.com/carbon-language/carbon-lang/pull/3762), although this is not directly mentioned in the proposal. At the time, it was dropped because the owning library didn't need to include `extern` declarations, and so having the definition opt-in to allowing `extern` was viewed as low benefit. However, now that the owning library must import the `extern` declaration, there is a tighter association and so we reevaluated. The `extern` modifier offers a benefit for being able to verify the association between non-owning and owning declarations, and offers additional parity in modifiers. It also makes it easy for a tool to know if it's missing a declaration. ### Only require `extern` on the first owning declaration At present, we require `extern` on _all_ owning declarations. We could instead only require `extern` on the first owning declaration and, if there's a separate forward declaration and definition, infer it for the definition. For example: ``` // `extern` on the forward declaration. extern class C; // Infer `extern` for the definition. class C {} ``` The decision to require `extern` on all owning declarations is based on wanting the forward declaration to be optional. A rule of thumb was discussed wherein if a forward declaration could be removed without breaking the definition (as defined by it being in the same lexical scope), keywords should be duplicated to the definition. This is not proposed as a rule because it's not clear whether we'll generally follow it, but it's why this particular choice is taken. ### Separate require-direct-import from non-owning declarations At present, an `extern` modifier on an owning declaration serves two purposes: 1. Indicates that a non-owning `extern library` declaration _can_ exist. 2. Indicates the declaration must be directly imported in order to be complete. This means that: - The presence of `extern` on an owning declaration cannot be used to determine whether a non-owning declaration exists. - Because the location of a non-owning declaration isn't explicit in the owning code, this may lead to a developer failing to find the non-owning declaration and misunderstanding that as the non-existence of a non-owning declaration. - Libraries which happen to be imported by the owning declaration may freely add or remove non-owning `extern library` declarations without modifying the owning library. We could give distinct syntax to the two purposes, so that they could be managed separately. The preference at present is to use a single syntax for both purposes, rather than emphasizing control or correspondence. ### Other `extern` syntaxes [Issue #3986](https://github.com/carbon-language/carbon-lang/issues/3986) discussed other syntaxes for `extern` + `extern library`. These were mainly `has_extern`/`is_extern`/`externed` + `extern`. Breaking down `extern`, there are two features which could have been provided separately: 1. Declaring an entity has a forward declaration in a separate library. - Also, declaring that forward declaration in a separate library: `extern library ""`. 2. Declaring an entity must be imported directly. Although (1) must depend on (2), a different design could provide (2) without making (1) possible, for example with different keywords to differentiate between intended usage (`has_extern class C;` meaning (1) and (2), `must_import` meaning (2) only). However, the `extern` keyword approach means developers have all or nothing. Considering that, the trade-offs are viewed as: - The primary motivation is to provide feature (1). - Leads wanted a syntax on the owning declaration that states something positive about the owning declaration itself, rather than expressing that other declarations exist, which suggests that the syntax on the owning declaration should provide feature (2). - Leads consider it valuable, though secondary, to support (2) separate from (1), and find it acceptable to make (1) optional to achieve this (in other words, making the `extern library ""` declaration optional). - It's okay that that `extern library ""` can be added and removed from imported libraries without modifying the owning library. - If a developer considers it important to disambiguate the intended use of a declaration `extern class C;` and whether there should be a declaration in a separate library, they can add comments. - `extern` seemed like an acceptable name for this approach, and alternative names seemed significantly less good. - Using `extern` for both features still only creates one new keyword, versus multi-keyword approaches. - Adding the owning library with `extern library ""` will hopefully improve diagnostics and human understandability of the code. - It is _very_ verbose, but this verbosity goes on the forward declaration in the non-owning library. When it's read, which will hopefully be less often than the actual declaration, it will provide the reader directions to find the actual declaration. - If in practice we find the verbosity becomes a significant issue, we can revisit syntaxes to address that specifically. For example, if we have significant repetiton, we might consider a grouping structure such as `extern library "..." { }`. ### Have types with `extern` members re-export them We expect there will be types that have `extern` members; these types are only truly complete if their members are complete. We discussed having such types automatically re-export the `extern` members, possibly requiring the types to also be `extern` in order to be allowed to have `extern` members. For example: ``` library "a" extern library "b" class A; ``` ``` library "b" import library "a" extern class A {} // B re-exports A so that it's complete on use. class B { var a: A; } ``` ``` library "c" import library "b" // Importing this function declaration gets B, which again, re-exports A so that // it's complete on use. fn F() -> B { ... } ``` ``` library "d" // This import loads the incomplete name for A. import library "a" // This import loads F, which loads B, which loads the definition of A. import library "c" // Because of the import behaviors, this is valid. var a: A; ``` We consider this action-at-a-distance. Type coherency means the `A` member of `B` is the same as the `A` in name lookup; we could make them behave slightly differently, but then we get into provenance tracking of type information. Several various forms of this have been discussed as part of the `extern` design, and it's something we've decided to avoid. Although it's more inconvenient, we will require `A` to be deliberately imported in order for `B` to be complete. ### Require syntactic matching for `extern library` declarations We will not require syntactic matching for `extern library` declarations, but we could. When a redeclaration is in the same library, we've designed name lookup in a way such that syntactic matching is effectively a superset of semantic matching. However, that relies on poisoning entries in name lookup, with later redeclarations seeing identical name lookup data. Because different libraries have different name lookup data, syntactic matching _not_ a superset of semantic matching cross-library. We address this schism by only requiring semantic matching. Semantic matching will include parameter names. The difference is primarily in whether different ways of producing the same type information are considered invalid or not. For example: ``` library "a"; class A {} namespace NS; extern library "c" fn NS.F() -> A; ``` ``` library "b"; namespace NS; class A {} ``` ``` library "c"; import library "a" import library "b" extern fn NS.F() -> NS.A {} ``` Semantically, `NS.F` in libraries "a" and "c" are identical. Syntactically, they differ because of `NS.A` in "c". Writing `A` in "c" is invalid because it would use `NS.A` from "b". But in "a", there is nothing to make the declaration invalid: it would only be invalid after completing cross-library compilation. However, we could also have code such as: ``` library "d"; class D {} namespace NS; extern library "e" fn NS.G() -> D; ``` ``` library "e"; namespace NS; alias NS.D = D; extern fn NS.G() -> D {} ``` Here, the semantics and syntax match, but this would be invalid in a normal redeclaration due to the different name lookup result for `D`. This additionally gets into a different statement made in [#3763](https://github.com/carbon-language/carbon-lang/pull/3763) to justify synactic matching: "The intention is that whenever the syntax matches, the semantics must also match." Due to the differences in name lookup, syntax matching does not mean semantics must match; instead of `alias NS.D = D;`, that could have been `alias NS.D = i32;` and the syntax would have still matched. This only works in a library because "...we persist syntactic information from the API file to implementation files." We cannot persist syntactic information cross-library, across imports. Due to the differences in the guarantees that syntactic matching provides for owned declarations versus non-owned declarations, we will not enforced syntactic matching on the non-owned `extern library` declarations. ================================================ FILE: proposals/p4075.md ================================================ # Change operator precedence [Pull request](https://github.com/carbon-language/carbon-lang/pull/4075) ## Table of contents - [Abstract](#abstract) - [Problem](#problem) - [Background](#background) - [Proposal](#proposal) - [Details](#details) - [Rationale](#rationale) - [Alternatives considered](#alternatives-considered) - [`as` and `where` could be peers of `if`...`then`...`else`](#as-and-where-could-be-peers-of-ifthenelse) - [Make `T as I where R` mean `T as (I where R)`](#make-t-as-i-where-r-mean-t-as-i-where-r) - [Make fewer changes](#make-fewer-changes) - [Different `where` syntax](#different-where-syntax) ## Abstract Update the operator precedence to achieve a few goals: - Form operators into groups which behave similarly - Make the group of operators ("top-level operators") that capture everything to the right, like `if`...`then`...`else`, behave similarly to the left, so that rearranging expressions won't change how they group. - Add the `where` operator, used to specify constraints on facet types, to the precedence chart, to define how it interacts with other operators. - Make the operator precedence diagram prettier, so that it eventually can be made into a poster that Carbon programmers can hang on their walls. ## Problem The `where` operator is particularly tricky: - It is used in an `impl` declaration to specify the values of associated constants (such as associated types). In that context, `impl T as I where R` is interpreted conceptually as `impl T as (I where R)`. It would be nice if `T as I where R` would mean the same thing in other contexts. If not, we'd rather it to be invalid rather than meaning `(T as I) where R`. That is, That is, considered in isolation, we would prefer `T as (I where R)` over invalid over `(T as I) where R`. - The `where` operator will frequently be used with the binary `&` operator, since that is how facet types are combined. It is desirable that `I & J where R` be interpreted as `(I & J) where R`. If not, we'd rather it be invalid than be interpreted as `I & (J where R)`. This usage of `&` with `where` is expected to be more common than combining `where` and `as` outside of an `impl` declaration. - The "restriction" on the right side of a `where` uses operators that mean something else in an expression context: `and`, `==`, `=`. We would like to minimize the confusion when both kinds of uses of those operators appear in the same expression. These goals are in conflict with the current precedence partial order. ## Background The initial operator precedence approach, including using a partial precedence ordering instead of a total ordering as found in most languages, was established by [propsoal #555](https://github.com/carbon-language/carbon-lang/pull/555). [PR #1070](https://github.com/carbon-language/carbon-lang/pull/1070) established the current precedence chart, which has been incrementally added to since then. ## Proposal We are making a number of changes: - `x as T` is no longer allowed on either side of a comparison operator, or the short-circuiting operators `and` & `or`. - `x where R` is a peer to `as`, but its arguments can be binary operators (like `&`). This matches the comparison operators, which are either illegal or reinterpreted as an argument to `where`. - The type constructors `T*` and `const T` are no longer separate from the other unary operators, and can now be the argument of any binary operator. ## Details Please see the new precedence diagram in [docs/design/expressions/README.md](/docs/design/expressions/README.md). ## Rationale Precedence is about [Code that is easy to read, understand, and write](/docs/project/goals.md#code-that-is-easy-to-read-understand-and-write). We don't want to require parentheses too often since that makes the code harder to write, and if it goes too far even reading becomes difficult. However, we do want parentheses to mark code that would otherwise be misinterpreted. This is a balancing act we expect to have to refine with experience. ## Alternatives considered ### `as` and `where` could be peers of `if`...`then`...`else` We considered making all the "top-level" operators act the same for precedence, but we expect users to want to use `as` to force the two branches of an `if`...`then`...`else` expression to a common type often enough, and we didn't expect the result of doing that to be confusing to read. ### Make `T as I where R` mean `T as (I where R)` We wanted to make `T as I where R` mean the same facet type as that same sequence of tokens in an `impl` declaration. However, this was in conflict with the arguments to `where` being the same as the arguments to comparison operators. We didn't want to allow an expression mixing binary operators with `as` since we expected users to expect that to mean performing the operation with that casted-to type. For example, `x + y as i64` would mean `(x + y) as i64`, which would perform the addition and only then cast to `i64`, which is probably not what would be intended by that expression. We thought it better to make `x + y as i64` illegal to force users to use parentheses, even if that meant also using parentheses with `T as I where R` in an expression context. ### Make fewer changes We considered making fewer changes to precedence, but that lead to an operator precedence diagram with crossing edges (it was [non-planar](https://en.wikipedia.org/wiki/Planar_graph)). This was felt to be a sign that the graph was too complex, making it harder for humans to understand and remember. It was suggested that developers using Carbon may want to have the precedence graph posted for reference, and a planar graph would make a more-appealing poster. This was [discussed in open discussion on 2024-06-20](https://docs.google.com/document/d/1s3mMCupmuSpWOFJGnvjoElcBIe2aoaysTIdyczvKX84/edit?resourcekey=0-G095Wc3sR6pW1hLJbGgE0g&tab=t.0#heading=h.p524bg7cnd32). ### Different `where` syntax We considered other ways of marking the end of a `where` restriction expression, such as requiring parens `(`...`)` (either around the argument or the whole `where` expression) or having a keyword at the end. We ultimately decided none of those options were satisfactory since they added noise that reduced clarity, and decided to go with a greedy approach ("all the way to the right") instead. This was discussed in [open discussion on 2024-06-13](https://docs.google.com/document/d/1s3mMCupmuSpWOFJGnvjoElcBIe2aoaysTIdyczvKX84/edit?resourcekey=0-G095Wc3sR6pW1hLJbGgE0g&tab=t.0#heading=h.p46elxrmhh8x) ================================================ FILE: proposals/p4105.md ================================================ # Establish toolchain and language versioning [Pull request](https://github.com/carbon-language/carbon-lang/pull/4105) ## Table of contents - [Abstract](#abstract) - [Problem](#problem) - [Background](#background) - [Proposal](#proposal) - [Details](#details) - [Directional sketch for the future](#directional-sketch-for-the-future) - [Language evolution and breaking changes](#language-evolution-and-breaking-changes) - [Long-Term Stable (LTS) versions and standardization](#long-term-stable-lts-versions-and-standardization) - [Rationale](#rationale) - [Alternatives considered](#alternatives-considered) - [Do nothing, or just talk about a minimal nightly version.](#do-nothing-or-just-talk-about-a-minimal-nightly-version) - [Make no breaking changes past 1.0.](#make-no-breaking-changes-past-10) - [Version different parts of the language separately.](#version-different-parts-of-the-language-separately) - [Use a custom versioning scheme rather than SemVer.](#use-a-custom-versioning-scheme-rather-than-semver) - [Include more pre-release variations](#include-more-pre-release-variations) ## Abstract Proposal for how Carbon version numbers work: - A single version across language, standard library, compiler, linker, etc. - Semantic Versioning (SemVer) based - Details of how SemVer criteria for major, minor, and patch should apply to Carbon - Details of how we will operate before 1.0 and how this connects to Carbon's milestones - Directional guidance for future work including post-1.0 versions, LTS versions, and standardization ## Problem We need a versioning scheme for Carbon both for the language and the reference toolchain implementing that language. This is important even before we reach any specific milestone, as we want to define the schema and implement it _before_ it becomes useful for marking specific milestones. ## Background - [Semantic Versioning](https://semver.org/) - [Rust Editions](https://doc.rust-lang.org/edition-guide/editions/) ## Proposal First, Carbon should have a single versioning scheme across both the language itself and toolchain, including the standard library, compiler, linker, and all development tools released by the main Carbon project. Second, the Carbon versioning scheme should conform to and be closely based on Semantic Versioning (SemVer), which is the de-facto standard for versioning schemes in software today. Beyond that, it needs to clarify how the standards laid out in SemVer map into a programming language context as programming languages and standard libraries have an extraordinarily broad and tightly coupled "API" to their users -- all of the source code written in the language. Carbon needs to provide extra clarity around what constitutes our "public API" for SemVer purposes and the criteria for changes. Third, SemVer provides a schema for pre-release versions, but is largely open-ended on their semantics. Carbon should have a specific set of well defined pre-release versions with clearly communicated purpose, nature, and meaning to avoid confusion. Fourth, language versioning is an especially important area for the long-term evolution and so Carbon should have some directional guidance around the future work expected in the versioning front. This should speak to specific use cases and needs that may be left seemingly unaddressed otherwise. Summarizing the proposed outcome of these together: - Carbon versions: `MAJOR.MINOR.PATCH` - `MAJOR` increments on backwards incompatible changes, including a deprecation that might trigger a build-breaking warning. - Doesn't make it free to make such changes. Each change must pay for its adoption and churn cost. But when a change is needed and well justified, this signifies its introduction. - Used to establish our milestones for the language. - Some explicit carve-outs of things designated to not be in the "public API" of the language. - `MINOR` increments only expected during early development with a major version of `0`. - If Carbon some day stabilizes sufficiently to motivate it, we may revisit this and begin to use the minor version number to signal backwards compatible releases. - `PATCH` increments represent bug fixes only. - Goal is always fully backwards compatible. - When fixing the bug makes code written against the release with the bug break, may be unavoidable as the intent was never the buggy behavior. But this is rare and we hold a very high bar for such bug fixes due to their disruptive nature. - Pre-release suffixes: - `MAJOR.MINOR.PATCH-rc.N`: The N-th potentially viable candidate for a release. - `MAJOR.MINOR.PATCH-0.nightly.YYYY.MM.DD`: A nightly incremental development build on a particular day during development of that version. - `MAJOR.MINOR.PATCH-0.dev`: An interactive, incremental development build on a particular day by some developer during development of that version. ## Details See the added [versioning document](/docs/project/versioning.md). ## Directional sketch for the future The mechanics outlined above provide a good basis for the initial versions of the language (up to 1.0) and any necessary mechanics and tooling around those versions. However, beyond 1.0 we expect the needs of the language and project to expand and more detailed versioning and evolution tools to become critical. We lay out directional sketches here for where Carbon should go in the future to address these needs, but these are just directional guidance and will need their own carefully considered proposals when the time comes. ### Language evolution and breaking changes We don't expect a simple version number to be sufficient long-term for the evolution needs of the Carbon language. We should plan to at least map these major versions into Rust-edition-like controls within source code itself to allow incremental adoption across a codebase of fixes for breaking changes or adoption of new language features with a single toolchain version. That is, some code will want to compile using previous major version semantics even with the new compiler. The approach taken in Rust and proposed for C++ to address this are "editions" that source code opts into in order to allow the compiler to support a mixture of code in a codebase during incremental adoption. Carbon will need at least something equivalent to this, and may want to explore a more fine-grained system of opting into specific functionality sets similar to how pragma-based extension usage or Circle works. Regardless of the specifics, a key is that breaking changes are not forcibly coupled in their roll-out to updates to the Carbon toolchain. Each step needs to be incrementally tackled. ### Long-Term Stable (LTS) versions and standardization SemVer alone isn't sufficient to address some user needs for language stability. It is enough to _detect_ the issues when they arise, but Carbon should also plan for how to _address_ these issues. The suggested direction here is towards designated LTS versions based on a particular level of completeness and quality and the user demand. These versions will likely need even longer time horizons of support than Linux distro LTS releases. The direction should be to embrace this and the potential for multi-decade support windows to support users' needs. As the windows of LTSes expand, their frequency should reduce to avoid supporting an unsustainable diversity of versions. Exactly how a version is designated as LTS is left to the future work here, but it should not be expected to change the schema and structure of the versioning, just the support policy applied to the specific release version in question. Some users may even require standardization of a programming language to make it usable in their environment. Carbon should again embrace this need and see the standardization as an analogous process to promoting a normal release into an LTS. Some relevant and effective LTS should be selected and taken through whatever process is identified to create a standard reflecting that LTS version of Carbon. Updates to the standard should in turn track as updates to a newer LTS. The specifics of how to do this are left to the future work, and they may change exactly how this works. Note that the goal of this future direction isn't to constrain how Carbon can arrive effectively at either an LTS release or a standard. Instead, the goal is to make it clear that we _should_ be open and planning to achieve these in order to meet the needs of candidate Carbon users. ## Rationale - [Language tools and ecosystem](https://github.com/carbon-language/carbon-lang/blob/trunk/docs/project/goals.md#language-tools-and-ecosystem) - Carbon needs a coherent versioning scheme across the language itself as well as its ecosystem. Especially as the language is developing rapidly, being able to sync across all of these with a single, simple versioning scheme is especially important to have the tooling and ecosystem agree about features of the language. - [Software and language evolution](https://github.com/carbon-language/carbon-lang/blob/trunk/docs/project/goals.md#software-and-language-evolution) - Users of Carbon need a clear model for understanding the language's evolution, tracking it, and responding to significant aspects of it. - The versioning scheme needs to support a wide variety of versions built in the process of evolving the language without creating confusion. ## Alternatives considered ### Do nothing, or just talk about a minimal nightly version. Advantages: - Carbon is at a very early stage and is a long way from needing release candidates for stable releases or incrementing its major version numbers. - No need to imagine distant future scenarios. Disadvantages: - We already discuss version numbers in various places in the Carbon project, including our [milestones](/docs/project/milestones.md) and [roadmap](/docs/project/roadmap.md). - Establishing how the Carbon project will communicate its updates with version number changes in advance of those updates makes that communication more effective. - Lets us telegraph our intentions and how we are thinking about releasing and cadence to the community and get earlier feedback. A key to adopting the more detailed versioning plan is that we can change any and all of this if and when we need to. This does not lock Carbon into using this exact versioning scheme. We will listen to any feedback from potential users and can adapt our approach if needed. ### Make no breaking changes past 1.0. Advantages: - Stable languages have the lowest churn costs for users, and are easier to learn at scale. Disadvantages: - This would be in direct opposition to our language goal of supporting software and language evolution. - This relieves pressure on adding things to the language by avoiding an unreasonably high bar. The pressure is still large due to the very real churn costs, but we avoid amplifying that further with an absolute restriction on fixing issues. - We expect Carbon to be a reasonably complex language in order to succeed at its goals, ranging from C++ interop to incremental memory safety. This complexity inherently comes with an increased risk and importance of being able to improve and fix issues. ### Version different parts of the language separately. Advantages: - There will be eventually be many differences between changes to the toolchain and changes to the language itself. We might be able to capitalize on those to have a better cadence or versioning scheme for these independently. Disadvantages: - We would have to carefully define and maintain a compatibility matrix between the different components. Increasingly in modern languages and development, the compiler, language, standard library, and tooling are all deeply interdependent. - Experience rolling out major updates to Clang and GCC in large codebases and software ecosystems show even compiler-only or toolchain-only changes easily become as disruptive as smaller updates to the C++ language itself have been over the years. As a consequence, while it is tempting to hope for a sharp difference here we don't in practice anticipate one. ### Use a custom versioning scheme rather than SemVer. Advantages: - We don't actually use all parts of SemVer, which results in awkward unused component of our version number. - SemVer doesn't actually provide an opinionated versioning scheme, merely a relaxed schema that many versioning schemes can fit into. Disadvantages: - Fitting into SemVer ends up needing only very cosmetic changes to a scheme that is meaningful for Carbon. And we can easily specify the open-ended parts of the scheme. - Allows us to easily fit into scripts and people's understanding using SemVer as a baseline model. - Avoids confusion when people from outside the community first encounter Carbon versions as their most likely intuition about the meaning of various aspects of the number will be a reasonable starting point. ### Include more pre-release variations Initially there was a discussion of potentially defining `alpha` and `beta` pre-release versions along with release candidate versions, nightly, and development versions. The specific idea was to document versioning we could use for longer-lived releases we want to make when not yet ready to call it a release candidate. Advantages: - If we end up wanting longer-lived releases prior to arriving at a numbered milestone such as `0.1`, this would provide a ready-made solution. - Specifically, communicating the option for this early might help others avoid being confused by what exactly the state of such a pre-release would be. Disadvantages: - There's no real indication we would ever want to make such a release. It seems easy to imagine the combination of nightly builds and pre-releases completely covering all of the use cases we end up with in practice. - Keeping the infrastructure and documentation for such improbable use cases is drag and friction that doesn't buy us enough to be worthwhile. ================================================ FILE: proposals/p4246.md ================================================ # Getting commit access [Pull request](https://github.com/carbon-language/carbon-lang/pull/4246) ## Table of contents - [Abstract](#abstract) - [Problem](#problem) - [Background](#background) - [Commit access](#commit-access) - [Related policies](#related-policies) - [Proposal](#proposal) - [Rationale](#rationale) - [Alternatives considered](#alternatives-considered) - [Longer idle times](#longer-idle-times) ## Abstract Establish a process for getting commit access. We will: - Grant access based on a developer's commit history. - Someone with commit access should nominate, and a contributor may ask. - A lead will approve nominations. Only one lead is needed. - Remove commit access once someone is idle for 6 months. - "Idle" means no significant project activity on any of GitHub, Discord, or in meetings. - Access removed due to being idle will be restored on request. ## Problem Right now, we have an undocumented process for getting commit access, and no real agreement for when to remove it. Commit access is important, so rather than just making a documentation edit, I'm submitting this as a proposal. ## Background ### Commit access When we say "commit access", what we mean is the ability to push commits to the main `carbon-lang` repository, regardless of branch. Some details about the implications: - Review and approvals would remain required for pushes to `trunk`. - This does grant access to push to other branches, although we will continue to encourage fork-based workflows. - Pushing PRs becomes easier with commit access. - Action workflows will automatically execute, instead of requiring a per-commit approval by someone with commit access. - Modifying PRs after review approval is possible. - This is both good (for small updates such as fixing typos and resolving conflicts) and bad (particularly for code security). - Communication delays can make it hard for an author to resolve conflicts and reviewer to approve and merge before more conflicts are introduced. - A possible solution to the conflict problem is to have the reviewer merge PRs more frequently, and regardless of any decision here, we may eventually need to adopt that approach. - Commit access functionally means the ability to approve and merge PRs from others. - As alternatives, we could use either a separate GitHub team for approvals or [CODEOWNERS](https://docs.github.com/en/repositories/managing-your-repositorys-settings-and-features/customizing-your-repository/about-code-owners). We have avoided these so far because we're small, having backup approvers can be helpful, and mistakes are easy to undo. - This does not include the ability to push to other repositories. While there are a few in the `carbon-language` organization, only the `carbon-lang` repository is actively used. - People with commit access in effect have access to secrets, can make releases, and so on. ### Related policies This proposal does not supersede other project policies, in particular: - [CLA](/CONTRIBUTING.md#contributor-license-agreements-clas) - [Code of Conduct](/CODE_OF_CONDUCT.md) - [Review process](/docs/project/code_review.md) ## Proposal The key things I think should be covered in this proposal are: - Whether we want to require a lead to approve commit access additions. - We're choosing to do a 6 month inactive period for removal. Things I think we should be okay iterating on without going through evolution include: - Exactly how we define non-idle activity beyond merging and approving PRs, both of which mechanically require this access. - The detailed process for additions, including nominating. - We want a good starting point, but it may not be worth sending all changes through the proposal process. - The detailed process for removals, such as whether leads are approving removals. - This is something it's been suggested to fully automate, preventing review of removals. See the new [Commit access](/docs/project/commit_access.md) document for details. ## Rationale - [Community and culture](/docs/project/goals.md#community-and-culture) - Establishing the processes around commit access, and making sure they're reasonable, is important to maintaining the community. ## Alternatives considered ### Longer idle times We discussed using a longer idle time, like 1, 2, or 3 years. We're leaning towards the shorter 6 month period because of concerns about forgotten access causing issues. We're hoping 6 months is a minimum of inconvenience, and want it to be easy to get access back on request. ================================================ FILE: proposals/p4682.md ================================================ # The Core.Array type for direct-storage immutably-sized buffers [Pull request](https://github.com/carbon-language/carbon-lang/pull/4682) ## Table of contents - [Abstract](#abstract) - [Problem](#problem) - [Background](#background) - [Rust](#rust) - [Swift](#swift) - [Safe C++](#safe-c) - [Goals](#goals) - [Privileging the most common type names](#privileging-the-most-common-type-names) - [Absence of syntax should make clear defaults](#absence-of-syntax-should-make-clear-defaults) - [Avoiding confusion with other languages](#avoiding-confusion-with-other-languages) - [Avoiding confusion with other domains](#avoiding-confusion-with-other-domains) - [No predeclared identifiers](#no-predeclared-identifiers) - [Proposal](#proposal) - [Rationale](#rationale) - [Future work](#future-work) - [Namespacing the `Core` package](#namespacing-the-core-package) - [Alternatives considered](#alternatives-considered) - [`[T; N]` builtin syntax](#t-n-builtin-syntax) - [`array [T; N]` builtin syntax](#array-t-n-builtin-syntax) - [Just the `Core.Array(T, N)` library type](#just-the-corearrayt-n-library-type) - [Implicitly importing `Core.Array(T, N)` to the file scope](#implicitly-importing-corearrayt-n-to-the-file-scope) ## Abstract We propose to add `Core.Array(T, N)` as a library type in the `prelude` library of the `Core` package. Since arrays are a very frequent type, we propose to privilege use of this type by providing a builtin keyword `array(T, N)` that resolves to the `Core.Array(T, N)` type. ## Problem Carbon's current syntax for a fixed-size, direct storage array (hereafter called "array") is the provisional `[T; N]` and there is no syntax yet for a mutably-sized indirect storage buffer (hereafter called "heap-buffer"). Arrays and heap-buffers are some of the most commonly used types, after fundamental types. The syntax, whatever it is, will be incredibly frequent in Carbon source code. We explore and propose a new syntax for arrays that addresses design issues with the provisional syntax that allows for writing each of the following in clear ways: slice, compile-time sized slice, array, and pointer to array. And that leaves clear room for a sibling indirect-storage type. ## Background We have developed a matrix for enumerating and describing the vocabulary of owning array and buffer types. Direct refers to an in-place storage buffer, as with arrays. Indirect refers to heap allocation, where the type itself holds storage of a pointer to the buffer, as with heap-buffers. Here we are discussing the location of storage (direct vs indirect) as a way to categorize types. Indirect-storage types may, for small payloads, store state directly in its fields (such as with the Small String Optimization), but this is an optimization for specific payloads and the category of the type remains an indirect-storage type. To provide familiarity, here is the table for the C++ language as a baseline: | Owning type | Runtime Sized | Compile-time Sized | | ------------------------ | ---------------------- | --------------------------- | | Direct, Immutable Size | - | `T[N]` / `std::array` | | Indirect, Immutable Size | `std::unique_ptr` | `std::unique_ptr` | | Indirect, Mutable Size | `std::vector` | - | ### Rust The Rust vocabulary is as follows: | Owning type | Runtime Sized | Compile-time Sized | | ------------------------ | ------------- | ------------------ | | Direct, Immutable Size | - | `[T; N]` | | Indirect, Immutable Size | `Box<[T]>` | `Box<[T; N]>` | | Indirect, Mutable Size | `Vec` | - | There are a few things of note when comparing to C++: - The Rust `Box` and `Vec` types are part of `std` but are imported into the current scope automatically, so they do not need any prefix. - The `[T]` type represents a fixed-runtime-size buffer. The type itself is not instantiable since its size is not known at compile time. `Box` is specialized for the type to store a runtime size in its own type. - The array type syntax matches the Carbon provisional syntax. - The heap-buffer type name matches the C++ `vector` type, but it is privileged with a shorter name. The `Vec` type name is at most the same length as an array type name (for the same `T`). ### Swift The Swift vocabulary is significantly smaller, to support automatic refcounting: | Owning type | Runtime Sized | Compile-time Sized | | ------------------------ | ------------------ | ------------------ | | Direct, Immutable Size | `InlineArray` | - | | Indirect, Immutable Size | - | - | | Indirect, Mutable Size | `Array` / `[T]` | - | Because there was historically no direct storage option, only one name was needed, and "Array" was used to refer to a heap-buffer. On [Feb 5 2025](https://forums.swift.org/t/accepted-with-modifications-se-0453-inlinearray-formerly-vector-a-fixed-size-array/77678), a proposal was accepted to [introduce `InlineArray` for the direct storage immutably sized array type](https://github.com/swiftlang/swift-evolution/blob/main/proposals/0453-vector.md). Because "Array" is already taken, the original proposal called this new type "Vector" in reference to mathematical vectors. The choice of name was [heavily discussed](https://forums.swift.org/t/second-review-se-0453-vector-a-fixed-size-array/76412) however, due to the confusion with C++'s `std::vector` and Rust's `std::vec::Vec`. It was [provisionally renamed to `Slab`](https://github.com/swiftlang/swift/pull/76438) but settled on `InlineArray`. ### Safe C++ The [Safe C++ proposal](https://safecpp.org/draft.html#tuples-arrays-and-slices) introduces array syntax very similar to Rust: | Owning type | Runtime Sized | Compile-time Sized | | ------------------------ | --------------------- | ------------------- | | Direct, Immutable Size | - | `[T; N]` | | Indirect, Immutable Size | `std2::box<[T; dyn]>` | `std2::box<[T; N]>` | | Indirect, Mutable Size | `std2::vector` | - | There are a few things of note: - While Rust omits a size to indicate the size is known only at runtime, Safe C++ uses a `dyn` keyword indicate the same. - The heap-buffer type name is unchanged from C++, sticking with `vector`. ### Goals It will help to establish some goals in order to weigh alternatives against. These goals are based on the [open discussion from 2024-12-05](https://docs.google.com/document/d/1Iut5f2TQBrtBNIduF4vJYOKfw7MbS8xH_J01_Q4e6Rk/edit?usp=sharing&resourcekey=0-mc_vh5UzrzXfU4kO-3tOjA#heading=h.h0tg34pzq5yz), where we discussed the [Pointers, Arrays, Slices](https://docs.google.com/document/d/1hdYyCLmzEOj9gDulm7Eo1SVNc0pY7zbMvFmEzenMhYE/edit?usp=sharing) document. The goals here are largely informed by and trying to achieve the top-level goal of ["Code that is easy to read, understand, and write"](/docs/project/goals.md#code-that-is-easy-to-read-understand-and-write). We define some more specific targets here as relate to the specifics of the array syntax. #### Privileging the most common type names - "Explicitness must be balanced against conciseness, as verbosity and ceremony add cognitive overhead for the reader, while explicitness reduces the amount of outside context the reader must have or assume." The more common it will be for a type to be used, the shorter we would like the name to be. This follows from the presumption that we weigh conciseness as increasingly valuable for types that will appear more frequently in Carbon code. We expect the ordering of frequency in Carbon code to be: - fundamental types ≈ tuples >> heap-buffers > arrays >> everything else[^1]. Where fundamental types are: machine-sized integers (8 bit, 16 bit, etc.), machine-sized floating points, and pointers including slices[^2]. Function parameters/arguments are an example of tuples. From this, we derive that we want: - Fundamental types and tuples to have the most concise names. - We can lean on special syntax or keywords as needed to make them concise but descriptive. - Heap-buffers to have a concise name, even more so than arrays. - We could use special syntax or keywords if needed to achieve conciseness. - Arrays to have a concise name, but they do not need to be comparably concise to fundamental types and tuples. - We should try to avoid special syntax. - Everything else should be written as idiomatic types with descriptive names. [^1]: "[chandlerc] Prioritize: slices first, then [resizable storage], then compile-time sized storage, then everything else is vastly less common. Between those three, the difference in frequency between the first two is the biggest." from [open discussion on 2024-12-05](https://docs.google.com/document/d/1Iut5f2TQBrtBNIduF4vJYOKfw7MbS8xH_J01_Q4e6Rk/edit?resourcekey=0-mc_vh5UzrzXfU4kO-3tOjA&tab=t.0) [^2]: Slices are included with fundamental types for simplicity, since they will take the place of many pointers in C++, giving them similar frequency to pointers, and can be logically thought of as a bounded pointer. #### Absence of syntax should make clear defaults One way to write arrays and compile-time-sized slices is like we see in Rust: `[T; N]` and `&[T; N]`. This suggests a relationship where array is like slice, and the default form. But they are very different types, rather than a modification of a single type, and this can be confusing[^3] for developers learning the language. [^3]: https://fire.asta.lgbt/notes/a1iay7r3e7or0a59 (content-warning: swearing) We want to avoid the situation where [absence of syntax](https://www.youtube.com/watch?v=-Hb-9TUyjoo), such as a missing pointer indicator, changes the entire meaning of the remaining syntax or is otherwise confusing. #### Avoiding confusion with other languages The most general meaning of "array" is a range of consecutive values in memory. However in many languages it is used, either in formally or informally, to refer to a direct-storage, immutably-sized memory range: - C, [colloquial](https://en.wikibooks.org/wiki/C_Programming/Arrays_and_strings) - C++, colloquial (from C) and [`std::array`](https://en.cppreference.com/w/cpp/container/array) - Go, [colloquial](https://go.dev/tour/moretypes/6) - Rust, [colloquial](https://doc.rust-lang.org/std/primitive.array.html)[^4] In particular, this is the usage in the languages which Carbon will most frequently interoperate, and/or from which code will be migrated to Carbon and thus comments and variable names would use these terms in this way. [^4]: Maybe this is more formal than colloquial, but the name is not part of the typename/syntax. Languages which require shared ownership _don't have direct-storage arrays_, so the same term gets used for indirect storage: - Swift, [`Array`](https://developer.apple.com/documentation/swift/array) - As noted earlier, Swift is in the processes of adding a direct-storage array called [`InlineArray`](https://github.com/swiftlang/swift-evolution/blob/main/proposals/0453-vector.md). Backwards compatibility prevents the use of `Array` for this. - Javascript, [`Array`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array) - Java and Kotlin, [`ArrayList`](https://docs.oracle.com/javase/8/docs/api/java/util/ArrayList.html) And some languages use array to refer to both direct and indirect storage types. - Dlang has direct-storage arrays [colloqually](https://dlang.org/spec/arrays.html) and the indirect-storage [`Array`](https://dlang.org/phobos/std_container_array.html) type. - Pascal uses the presence or absence of a size to determine if [`Array`](https://www.freepascal.org/docs-html/ref/refsu14.html) uses direct (immutably-sized) or indirect (mutably-sized) storage. In sum, languages which have direct-storage immutably-sized arrays use the term "array" to refer to those, and most then use a separate name for the indirect-storage type. #### Avoiding confusion with other domains The term "vector" in mathematics refers to a fixed-size set of numbers. This leads to confusion with the C++ type `std::vector` since it holds a mutably-sized set of values. Developers coming from other domains must learn a new and contradictory term of art. The Rust language chose naming that derives from C++, with `std::vec::Vec`. These type names conflict with names in mathematical and graphics libraries, which want to use vector in its mathematical sense. In Rust this leads to `Vec` for a mutably-sized array, and [`Vec3`](https://docs.rs/bevy/latest/bevy/prelude/struct.Vec3.html), [`Vec4`](https://docs.rs/bevy/latest/bevy/prelude/struct.Vec4.html), and so on for fixed-size mathematical vectors. While not fatal, this does create ambiguity that must be overcome by developers. ### No predeclared identifiers Recently the proposal [p4864: No predeclared identifiers, Core is a keyword](https://docs.carbon-lang.dev/proposals/p4864.html) clarified a direction for the Carbon language, wherein there will not be implicit imports from the `Core` library. Anything accessible directly in the language, rather than through a package name, is done so through a builtin keyword. This ensures that raw identifier syntax is always available for those same names in Carbon code. ## Proposal The [All APIs are library APIs principle](/docs/project/principles/library_apis_only.md) states: > In Carbon, every public function is declared in some Carbon API file. As such, we propose a `Core` library type for a direct-storage immutably-sized array, and then a builtin shorthand for referring to that library type. In line with other languages surveyed above, given the presence of a direct-storage immutably-sized array in Carbon, we will reserve the unqualified name "array" for this type. In full, its name is `Core.Array(T, N)`, where `T` is the type of elements in the array, and `N` is the number of elements. Notably this leaves room for supporting multi-dimensional arrays by adding further optional size parameters, either in the `Array` type or in a similar sibling type. Here is a provisional vocabulary table to compare with other languages: | Owning type | Runtime Sized | Compile-time Sized | | ------------------------ | ------------- | ---------------------------------- | | Direct, Immutable Size | - | `array(T, N)` / `Core.Array(T, N)` | | Indirect, Immutable Size | ? | `Core.Box(Array(T, N))` | | Indirect, Mutable Size | `Core.Buf(T)` | - | Carbon does not have proposed names for heap-allocated storage, so we use some placeholders here, in order to show where `Array` fits into the picture: - `Box(T)` for a heap-allocated `T` value. - `Buf(T)` for a heap-buffer of `T` values. An indirect, immutably-sized buffer does not have a clearly expressible syntax at the moment. `Box([T])` is the closest fit with the current provisional syntax for slices. But `[T]` is a sized pointer, which would make this type a heap-allocated sized pointer, rather than a heap-allocated fixed-size array. This is in contrast with Rust where `&[T]` is a slice, and `[T]` is a fixed-size buffer; so it then follows that `Box<[T]>` is a heap-allocated fixed-size buffer. Because arrays will be very common in Carbon code, we want to privilege their usage. There are at least two ways in which we can do so. The first is to include them in the `prelude` library of the `Core` package. This ensures they are available in every Carbon file as `Core.Array(T, N)`. The second is by making the type available through a shorthand without being qualfiied by the `Core` package name. We propose the `array(T, N)` builtin keyword as that shorthand. ## Rationale As this proposal is addressing the question of introducing a new `prelude` library type in `Core`, it is mostly focused on the goal [Code that is easy to read, understand, and write](/docs/project/goals.md#code-that-is-easy-to-read-understand-and-write) This proposal aims to make code easy to understand by using a name that is consistent across systems programming languages, and avoiding names that have conflicting meaning. It also uses a standard type syntax, with a type in the `Core` package, making the type and its documentation maximally discoverable without requiring special-casing. We introduced some more specific sub-goals above: 1. Privileging the most common type names This proposal privileges `Core.Array` as it will appear frequently in code, by placing it in the `prelude` library. This avoids the need for developers to `import` another `Core` library in order to access the type. Since array types are expected to be very frequent, we also propose an `array` builtin keyword as a shorthand. This is in line with the [No predeclared identifiers](https://docs.carbon-lang.dev/proposals/p4864.html) proposal, and uses a lowercase spelling to mark the word as a builtin. The spelling of `array(T, N)` will resolve to the library type `Core.Array(T, N)`. In this proposal, we avoid introducing additional syntax (such as with `[T; N]` or `(1, 2)`) because the frequency of use of arrays will be lower than that of fundamental types and tuples. 2. Absence of syntax should make clear defaults We introduce a type name, with a keyword that has a clear relationship to the generic type name, rather than making arrays look more like slices but without being a pointer. This is maent to avoid the confusion raised when removing syntax changes the meaning significantly, and especially in ways that differ from defaults/options for a single language concept. 3. Avoiding confusion with other languages We propose using the `Array` type name, and `array` shorthand, in line with how other languages use the same term. When a direct-storage array type is part of the language, it's consistently referred to as an "array" without qualifications. Most importantly, the name is consistent with the meaning in C++ and its standard library (`std::array`) as well as with Rust, the languages which we expect Carbon code to interact with the most. 4. Avoiding confusion with other domains The name `Vector` is a possible choice for a fixed-length set of values, due to its mathematical meaning, as was originally proposed for the direct-storage immutably-sized array type in Swift. However any use of the name `Vector` in a core systems programming language construct is fraught. Either the name is to be incorrectly confused with a mathematical vector or with a C++ `std::vector`. We avoid the confusion by avoiding this name. ## Future work ### Namespacing the `Core` package At this time, the `Core` package remains small, but there will come a time where the names within need to be split into smaller namespaces. Then the name `Core.Array`, among others, will become longer and the act of previleging the name through the `array` keyword will become more pronounced and helpful. At this time, we don't propose to put `Array` into a namespace in `Core` as there's no such existing structure to point to yet. ## Alternatives considered ### `[T; N]` builtin syntax This is the current syntax used by the toolchain, however it had the following problems raised: - It's very similar to the syntax for slices, which is `[T]`, but very different in nature, being storage instead of a reference to storage. - Given `[T]` is a slice, `[T; N]` would better suit a compile-time-sized slice. The syntax for a slice may also be changed, we discussed [adding a pointer annotation](https://docs.google.com/document/d/1hdYyCLmzEOj9gDulm7Eo1SVNc0pY7zbMvFmEzenMhYE/edit?tab=t.0#heading=h.fahgww8db6f0) to it, such as `[T]*` and `[T; N]*`. Some downsides remained: - The `[T; N]*` syntax would be a fixed-size slice, rather than a pointer to an array. This leaves no room for writing a pointer to an array, which can indicate a different intent, that it always includes the full memory range of the array. Without this distinction, we can't model both `std::span` and `std::array*` in code migrated from C++ to Carbon and would need to collapse these to a single type. - Removing the pointer annotation would change the meaning of the type expression more then we'd like, since it would change from a slice into an array, rather than pointer-to-an-array into an array. ### `array [T; N]` builtin syntax This introduces a keyword as a modifier of a fixed-size slice, rather than a builtin forwarding type. While arrays will be very common, it's not clear that they rise to the level of requiring breaking the languages naming rules (using a lowercase name) in order to provide a shorthand. And the shorthand is longer in the end than the `Array(T, N)` being proposed here. So this uses a larger weirdness budget for privileging the type while achieving less conciseness. This has a similar issue as with `[T; N]` but in the reverse. Removing the `array` modifier keyword changes the meaning of the type expression in ways that are larger than a default/modifier relationship. Fixed-size slices are not the more-default array. The use of a lowercase keyword also costs us by preventing users from using the word `array` in variables, a name which is quite common. ### Just the `Core.Array(T, N)` library type Providing just the library type is possible, but arrays will be one of the most common types in Carbon code, as described earlier. Privileging them with a shorthand that avoids `Core.` will help make Carbon code significantly more concise, due to the frequency, without hurting understandability. This makes it worth the tradeoff of putting a name into the file scope (by way of a builtin type). ### Implicitly importing `Core.Array(T, N)` to the file scope We considered a direction where some subset of names in the `Core` packages's `prelude` library are imported automatically to the file scope. This would be similar to the [Rust `std::prelude` module](https://doc.rust-lang.org/stable/std/prelude/index.html), which names aliases that are pulled into the global scope. Importing names poses challenges for migrating code to Carbon. Names imported from a library may allow shadowing in other scopes, which would create ambiguity about the meaning of these names. And names imported from a library would not be avoidable using the raw identifier syntax: `Array` and `r#Array` would both refer to the same imported `Core.Array` type. This would present a challenge for code migrated to Carbon which uses the name `Array` in its own types or variables. Whereas with a builtin keyword, `array` and `r#array` refer to different things: The first is the keyword, and the second is a name that the developer can use freely for other purposes. The [No predeclared identifiers, Core is a keyword](https://docs.carbon-lang.dev/proposals/p4864.html) proposal discusses in more detail why this approach was not taken. ================================================ FILE: proposals/p4864.md ================================================ # No predeclared identifiers, `Core` is a keyword [Pull request](https://github.com/carbon-language/carbon-lang/pull/4864) ## Table of contents - [Abstract](#abstract) - [Problem](#problem) - [Background](#background) - [Proposal](#proposal) - [Details](#details) - [Rationale](#rationale) - [Future work](#future-work) - [Package name `Cpp`](#package-name-cpp) - [Package name `Main`](#package-name-main) - [Alternatives considered](#alternatives-considered) - [Have both predeclared identifiers and keywords](#have-both-predeclared-identifiers-and-keywords) - [Reserve words with a certain spelling](#reserve-words-with-a-certain-spelling) ## Abstract Introduce a principle that the Carbon language should not encroach on the developer's namespace. Satisfy this principle by making `Core` a keyword. ## Problem Ongoing design work needs rules for how to expose types such as a primitive array type to Carbon code, and in particular, if we choose to make it available by default, whether that should be accomplished by a keyword or a predeclared identifier. ## Background See the [Background section of the added principle](/docs/project/principles/namespace_cleanliness.md#background). ## Proposal We choose to not have any predeclared identifiers in Carbon. If a word has special meaning to the language, then that word is a keyword, and a plain identifier with no special meaning is always available using raw identifier syntax. ## Details See [the principle document](/docs/project/principles/namespace_cleanliness.md) for details of the added principle. In addition, we make one change and one clarification: - `Core` is changed from being an identifier that happens to be the name of the Carbon standard library, and happens to be predeclared in every source file as naming that library, to being a keyword. The keyword can only be used: - When importing the `Core` package. - When implementing the `Core` package as part of the language implementation. - As a keyword naming the `Core` package, much like the `package` keyword. The identifier `r#Core` can be used freely and does not conflict with the keyword. This includes use of `r#Core` as the name of a package. Language constructs that are defined in terms of entities in the `Core` package refer specifically to the package named with the _keyword_ `Core`, not to any other entity named `Core`. - The `self` keyword is now included in the list of keywords. It is already treated as a keyword by the toolchain. ## Rationale - [Language tools and ecosystem](/docs/project/goals.md#language-tools-and-ecosystem) - Code generation tools can have a uniform handling for all words with special meaning, with no need to alter the spelling of names from other languages. - Language tools can determine the meaning of `Core.` without needing to do any name lookup or sophisticated analysis. - [Software and language evolution](/docs/project/goals.md#software-and-language-evolution) - Migration between versions of Carbon with a changed set of reserved words can be done uniformly. - Adding names to the prelude remains a non-breaking change. Adding new predeclared names requires adding a keyword, with the same cost and value tradeoffs regardless of whether the keyword names a library declaration or introduces new language syntax. - [Code that is easy to read, understand, and write](/docs/project/goals.md#code-that-is-easy-to-read-understand-and-write) - Syntax highlighting tools can easily distinguish between words with special meaning and words with program-defined meaning. - The meaning of core language constructs can be defined as a rewrite in terms of `Core.` without concern that `Core` may have some different local interpretation. - [Interoperability with and migration from existing C++ code](/docs/project/goals.md#interoperability-with-and-migration-from-existing-c-code) - All C++ identifiers are nameable from Carbon code without conflicts. Virtual functions introduced in C++ can be overridden in Carbon regardless of their name. C++ code can be migrated to Carbon even if its name in C++ has special meaning in Carbon. - [Principle: Prefer providing only one way to do a given thing](/docs/project/principles/one_way.md) - This proposal specifies that there is only one way to give words special meaning in Carbon, and one way to resolve issues if that special meaning conflicts with another desired meaning. ## Future work ### Package name `Cpp` The special package name `Cpp` that refers to code written in C++ is not made a keyword by this proposal, but this proposal is also not deciding that it should _not_ be a keyword. While this name has special meaning to the language, it's not predeclared in any context, so it's considered to be out of scope. A future proposal that describes the details of C++ import should determine whether this name becomes a keyword. Notably, making `Cpp` a keyword would also allow an `import Cpp` declaration to have custom syntax, which may be useful. ### Package name `Main` The special package name `Main` that is currently reserved in all package name contexts is not made a keyword in this proposal either. There would be no meaning in making it a keyword, as it is never used as a special package name in Carbon source files. However, we could consider using an empty package name as the name of the main package, and unreserving the package name `Main`, if it becomes a concern that we reserve this name. ## Alternatives considered ### Have both predeclared identifiers and keywords We could provide both predeclared identifiers and keywords. Many languages follow this path. However, predeclared identifiers have some problems compared to keywords: - In order to locally declare a name matching a predeclared identifier, the name would need to be shadowed. - Such shadowing may be invalid, depending on how the name is used. - Readability is harmed by using a name used as basic vocabulary with a different, local meaning. - Shadowing a predeclared identifier typically makes the original name hard to access -- an alias or similar must be established in advance. - There need to be two different stories for how to deal with adding a new word with special meaning to the language, depending on whether it is a keyword. - For each word with special meaning, we must make an arbitrary decision as to which kind it is, resulting in a largely meaningless distinction that nonetheless is visible and would need to be known by developers in some contexts. ### Reserve words with a certain spelling We could reserve words with certain spellings for future use as keywords or as vendor extensions. Some languages do this: - C reserves words starting with an underscore followed by a capital letter or an underscore. - C++ additionally reserves words containing a double underscore anywhere. - Python uses the `__name__` namespace for certain special names, and by convention these names are reserved for that purpose. In Carbon we could accomplish this by saying that all words of the reserved forms are keywords, with no meaning ascribed to them yet. However, we do not have a clear need for such reserved words at this time, and we would not want to use such spellings when we do add language keywords later. Moreover, C++ programs frequently declare reserved words in practice, and we should expect the same in Carbon. Without enforcement, the names are not effectively reserved. If we find a need at a later time to introduce vendor-specific language extension keywords, we can revisit this, but should also consider alternatives such as a `k#foo` spelling to turn what is normally an identifier into a (potentially vendor-specific) keyword. ================================================ FILE: proposals/p4880.md ================================================ # Safety milestones and a 2025 roadmap [Pull request](https://github.com/carbon-language/carbon-lang/pull/4880) ## Table of contents - [Abstract](#abstract) - [Proposal](#proposal) - [Retrospective on 2024](#retrospective-on-2024) ## Abstract We propose updating our milestones to accelerate design and implementation of memory safety in Carbon, and a roadmap for 2025 reflecting this change. We also provide a retrospective for 2024's progress. ## Proposal Recently, we have seen several new contributors join the Carbon project and a corresponding up-tick in activity across the project. We are all really excited both by the new folks participating and seeing things move faster. However, we have also had some good conversations with potential users and other interested parties about the project and our roadmap. Everyone has been very happy to see the rapid progress on a realistic toolchain, but we have also gotten even stronger feedback than previously around memory safety, and we propose updating our plans to better address that feedback. For the past two years we have been working on the C++-interop focused aspect of Carbon, and deferring the work to build out a strong memory safety story. While there was strong interest in memory safety, and for many an essential requirement long term, it seemed reasonable to tackle first interop, and then look at safety. This was heavily informed both by having a very small set of contributors and a desire to ship an 0.1 milestone with C++ interop as quickly as possible. Now we both have a larger set of contributors, and even stronger and more specific feedback asking for a concrete and detailed design for memory safety in Carbon sooner rather than later. We propose as a consequence to add a concrete _design_ for memory safe Carbon to our 0.1 milestone, and begin working on this in parallel to the toolchain in 2025. Our projected timeline for reaching 0.1 was historically based on growing the set of contributors while keeping all of them focused on the smaller prior 0.1 milestone. Adding a memory safety design to our target for 0.1 will make it impossible to ship in 2025 even with the larger contributor base. We are shifting our target from 2025 for 0.1 to, at the soonest, the end of 2026; and as always with these projections, they should be understood as a lower bound. With these updated milestones, we propose the following goals for 2025: - Complete design and implementation for remaining Carbon features needed for non-template C++ interop - Complete most of the interop layer for non-template Carbon ↔ C++, prioritizing calling/using C++ APIs from Carbon, and then exposing Carbon APIs back to C++. - Full support for compiling C++ code with the Carbon toolchain using Clang - Update our safety strategy, and establish a detailed strategy for memory safety - Design for compile-time & type-system based temporal and mutation safety Both the C++ interop and the memory safety designs are expected to have dependencies that we will need to prioritize in 2025. The C++ interop will depend on finishing the implementation of many parts of the Carbon language in our toolchain, and the safety designs will build on top of the existing Carbon type-system design, including refinements to it from our implementation experience. As with previous years, we will also continually share our progress and what we learn with the broader open source and C++ communities. ## Retrospective on 2024 Our [roadmap for 2024] was an ambitious pivot to focus heavily on implementing the Carbon language in a realistic compiler and toolchain. At a high level, this pivot was _very_ successful. Carbon's compiler has made massive strides over the year, and all of the active contributors to Carbon have thoroughly ramped up on our implementation and are making meaningful contributions to it. [roadmap for 2024]: https://github.com/carbon-language/carbon-lang/blob/10189bbb78db7b143a6d9d62797fc9698363fe4d/docs/project/roadmap.md Going into 2024, Carbon's compiler didn't have any support for generics, importing, expression categories, debug info, mangling, a prelude, or so many other things now in flight. Constant evaluation didn't know about aggregates, and classes couldn't be initialized at all. We had no examples, much less working ones. By the end of 2024, with a surprisingly minimal number of hacks, it was possible to solve much of Advent of Code! The progress here has been phenomenal, and we made awesome strides towards our goal of a working toolchain. Looking in more detail at our key results for 2024: - [Carbon's toolchain implements enough of the language to build realistic code](https://github.com/carbon-language/carbon-lang/blob/10189bbb78db7b143a6d9d62797fc9698363fe4d/docs/project/roadmap.md#carbons-toolchain-implements-enough-of-the-language-to-build-realistic-code) - A smashing success, overall. - We have imports, a working prelude, generic types and functions. - We have many of the building blocks of dispatching through interfaces for operator overloading, but there are still a few gaps left. Despite the gaps, we're actually using our generic building blocks effectively, including with a working `Core.Int` generic integer type that backs `i32` instead of a hard coded type! - We didn't get to the more stretch parts of this goal like templates though. - [Carbon's toolchain can build C++ code](https://github.com/carbon-language/carbon-lang/blob/10189bbb78db7b143a6d9d62797fc9698363fe4d/docs/project/roadmap.md#carbons-toolchain-can-build-c-code) - We have Clang integrated into the toolchain! - It works reasonably well for C++ code without system `#include`s. - But we still need to get some critical headers and other data that Clang depends on to get this fully working with system `#include`s. - [Carbon's toolchain works with existing, simple C++ build systems](https://github.com/carbon-language/carbon-lang/blob/10189bbb78db7b143a6d9d62797fc9698363fe4d/docs/project/roadmap.md#carbons-toolchain-works-with-existing-simple-c-build-systems) - Until we have all the system `#include` support work done, we can't drop our toolchain into a C++ build system. =/ This one ended up largely not landing this year. - We did end up integrating Carbon's toolchain into very simple Bazel build rules that we use to continuously build and test a collection of example Carbon code. This made sure the compilation model does work in a realistic toolchain situation for Carbon code. - [Carbon has a design and toolchain implementation of basic C++ interop](https://github.com/carbon-language/carbon-lang/blob/10189bbb78db7b143a6d9d62797fc9698363fe4d/docs/project/roadmap.md#carbon-has-a-design-and-toolchain-implementation-of-basic-c-interop) - This was too ambitious of a goal, but we actually got very close. The first importing of C++ headers (but without doing anything) has already landed, so we weren't too far away from the most basic parts of this. - [Give talks at 2-3 conferences covering 3-4 different Carbon topics](https://github.com/carbon-language/carbon-lang/blob/10189bbb78db7b143a6d9d62797fc9698363fe4d/docs/project/roadmap.md#give-talks-at-2-3-conferences-covering-3-4-different-carbon-topics) - We had a really strong year delivering talks at both C++ and LLVM conferences: 5 talks on different topics plus a panel session. - [Start building our initial tutorial and introductory material](https://github.com/carbon-language/carbon-lang/blob/10189bbb78db7b143a6d9d62797fc9698363fe4d/docs/project/roadmap.md#start-building-our-initial-tutorial-and-introductory-material) - We got an important start here with newsletters every other month, packaged pre-releases, and even some "first look" courses introducing people to Carbon. - However, tutorial material still seems somewhat far away. Overall, we achieved the majority of what we set out to for 2024. While our roadmaps are always a bit ambitious to push us, this year didn't seem excessive and is likely to reflect roughly how ambitious we want to be in our roadmaps. ================================================ FILE: proposals/p5017.md ================================================ # Destructor syntax [Pull request](https://github.com/carbon-language/carbon-lang/pull/5017) ## Table of contents - [Abstract](#abstract) - [Problem](#problem) - [Background](#background) - [Proposal](#proposal) - [Not directly callable](#not-directly-callable) - [Future work](#future-work) - [Extend syntax to allow explicit marking of _trivial_ destructors](#extend-syntax-to-allow-explicit-marking-of-trivial-destructors) - [Decide whether to desugar destructors to interfaces](#decide-whether-to-desugar-destructors-to-interfaces) - [Copy and move functions](#copy-and-move-functions) - [Rationale](#rationale) - [Alternatives considered](#alternatives-considered) - [Destructor syntax options](#destructor-syntax-options) - [Destructor name options](#destructor-name-options) ## Abstract Fix destructor syntax ambiguity by switching to `fn destroy` mirroring standard function syntax. This is a purely syntactic change, maintaining destructor semantics. ## Problem The [accepted destructor syntax](https://github.com/carbon-language/carbon-lang/blob/trunk/docs/design/classes.md#destructors) includes out-of-line definitions such as: ```carbon class MyClass { destructor [addr self: Self*]; } destructor MyClass [addr self: Self*] { ... } ``` The implicit parameter here could be interpreted as either an implicit parameter for `MyClass` or an implicit parameter for the destructor. How should ambiguities like this be resolved? For comparison, note a generic might look like: ```carbon class GenericClass[T:! type](N:! T) { ... } destructor GenericClass[T:! type](N:! T) [addr self: Self*] { ... } ``` The toolchain is able to parse this in constant time, but only because the lexer will pair brackets, so we can do lookahead at the bracket in `GenericClass[` for the closing `]`, and look past that for the `(` versus `{`. However, this is arbitrary lookahead and may be significantly less efficient in other parsers that people might want to use with Carbon, such as tree-sitter. ## Background - Proposal [#1154: Destructors](https://github.com/carbon-language/carbon-lang/pull/1154) - Leads question [#4999: Out-of-line destructor syntax ambiguity](https://github.com/carbon-language/carbon-lang/issues/4999) - [2025-02-25 Toolchain minutes](https://docs.google.com/document/d/1Iut5f2TQBrtBNIduF4vJYOKfw7MbS8xH_J01_Q4e6Rk/edit?resourcekey=0-mc_vh5UzrzXfU4kO-3tOjA&tab=t.0#heading=h.vootuzze8e8e) In particular, we are discussing destruction as possibly similar to copy and move syntax, and trying to create a consistency between the functions. ## Proposal Destructor syntax will use standard function syntax, with `destroy` as a keyword for the function name. For example, in contrast with [problem examples](#problem): ```carbon class MyClass { fn destroy[addr self: Self*](); } fn MyClass.destroy[addr self: Self*]() { ... } class GenericClass[T:! type](N:! T) { ... } fn GenericClass[T:! type](N:! T).destroy[addr self: Self*]() { ... } ``` It is invalid to add other implicit or explicit parameters to the `destroy` function. ### Not directly callable Although the syntax of `fn destroy` looks similar to a regular function, the functions are not designed to be directly callable. This does not add support for `my_var.destroy()`. See Proposal #1154, alternative [Allow functions to act as destructors](/proposals/p1154.md#allow-functions-to-act-as-destructors) for details. ## Future work ### Extend syntax to allow explicit marking of _trivial_ destructors Discussion has indicated potential utility in syntax to make the expectation of a trivial destructor _explicit_. This would allow a declarative way of ensuring no member accidentally caused a type to have non-trivial destruction. Still, this requires a further extension of syntax that isn't proposed at this time. Both determining syntax for such a feature and motivating it fully are left as future work. ### Decide whether to desugar destructors to interfaces Under this proposal, `fn destroy` remains a special function. We may want to make it desugar to an interface implementation, but even if we do so, the terse destructor syntax seems likely to remain. There are concerns about the ergonomics of requiring an `impl` in order to add a destructor to a type, and decisions would need to be made for how virtual destructors should be handled. ### Copy and move functions This proposal is set up for consistency with a possible `fn copy` and `fn move`, but those will be evaluated as part of copy and move semantics. ## Rationale - [Software and language evolution](/docs/project/goals.md#software-and-language-evolution) - Eliminates ambiguity in `destructor` syntax, by creating consistency with `fn` syntax. - Claiming `destroy` as a keyword is considered to be a good balance. - Syntax choices, particularly with the keyword as a function name, should not create a barrier for desugaring to an interface approach for destructions. - [Code that is easy to read, understand, and write](/docs/project/goals.md#code-that-is-easy-to-read-understand-and-write) - Consistency with `fn` syntax should improve readability. - Features that impact data layout are consistently written like member declarations. ## Alternatives considered ### Destructor syntax options The ambiguity between `destructor MyClass [...]` out-of-line destructor syntax and implicit parameters for generics is a sufficient barrier to change syntax. We do not want parsing Carbon to require arbitrary lookahead. `fn destroy` was preferred because it builds on existing `fn` syntax. Although adding a `.`, as in `destructor MyClass.[...]`, was brought up, it didn't present interesting advantages over `fn destroy`. ### Destructor name options We expect more name conflicts with C++ code using the `destroy` keyword than with the `destructor` keyword, for example with [`std::allocator::destroy`](https://en.cppreference.com/w/cpp/memory/allocator/destroy), or visible [searching LLVM code](https://github.com/search?q=repository%3Allvm%2Fllvm-project+language%3Ac%2B%2B+symbol%3A%2F%28%3F-i%29%5Edestroy%24%2F&type=code). Still, the phrasing of `destroy`, particularly if we have `copy` and `move` to match, is preferred. Raw identifier syntax (`r#destroy`) is expected to be sufficient for name conflicts. `fn delete` was mentioned as an option reusing current keywords, but declined due to the "heap allocated" implication of `delete`. Non-keyword names were considered as part of proposal [#1154: Destructors](https://github.com/carbon-language/carbon-lang/pull/1154), and the trade-off considerations still apply. ================================================ FILE: proposals/p5087.md ================================================ # Qualified lookup into types being defined [Pull request](https://github.com/carbon-language/carbon-lang/pull/5087) ## Table of contents - [Abstract](#abstract) - [Problem](#problem) - [Background](#background) - [Proposal](#proposal) - [Details](#details) - [Uses requiring a complete type](#uses-requiring-a-complete-type) - [Lookups into an incomplete generic](#lookups-into-an-incomplete-generic) - [Rationale](#rationale) - [Alternatives considered](#alternatives-considered) - [Require completeness for qualified name lookup](#require-completeness-for-qualified-name-lookup) - [Do not require a definition for name lookup](#do-not-require-a-definition-for-name-lookup) ## Abstract Allow qualified name lookup into classes and interfaces as soon as we reach the `{` of the definition, rather than disallowing such lookups until we reach the `}`. ## Problem We allow unqualified lookups within a type definition to find names that were already declared, but not qualified lookups: ```carbon class A { class B {} // ✅ OK, `B` names `A.B`. fn F() -> B; // ❌ Error (before this proposal): `A` is not complete. fn G() -> A.B; } interface I { let T:! type; // ✅ OK, `T` names `Self.T`. fn F() -> T; // ❌ Error (before this proposal): type `I` of `Self` is not complete. fn G() -> Self.T; } ``` This is inconsistent and prevents useful code: ```carbon interface Container { let ValueType:! type // ❌ Error (before this proposal): type `Container` of `.Self` // is not complete in implicit access to `.Self.ValueType`. // Implicit access to `Self.ValueType` is OK though. let SliceType:! Container where .ValueType == ValueType; } ``` An additional inconsistency is that namespaces already support qualified lookup before the full list of names is known -- indeed, the full list of names in a namespace is never known. ## Background Proposal [#3763](https://github.com/carbon-language/carbon-lang/blob/trunk/proposals/p3763.md) introduces a name poisoning rule: > In a declarative scope, it is an error if a name is first looked up and not > found, and later introduced. This is achieved by _poisoning_ a name in a scope when we perform a failed lookup for that name in that scope, and diagnosing if the name is later declared in a scope where it is poisoned. With that rule, there is no risk in allowing name lookups into a scope to succeed even before the scope is complete. If the name lookup's meaning would be changed by a later declaration, an error is issued. The motivation for this proposal and the proposed rule change were discussed in open discussion on [2025-02-21](https://docs.google.com/document/d/1Iut5f2TQBrtBNIduF4vJYOKfw7MbS8xH_J01_Q4e6Rk/edit?resourcekey=0-mc_vh5UzrzXfU4kO-3tOjA&tab=t.0#heading=h.ix77am1xk6po), [2025-02-27a](https://docs.google.com/document/d/1Iut5f2TQBrtBNIduF4vJYOKfw7MbS8xH_J01_Q4e6Rk/edit?resourcekey=0-mc_vh5UzrzXfU4kO-3tOjA&tab=t.0#heading=h.ank09kkr0tnn), and [2025-03-03b](https://docs.google.com/document/d/1Iut5f2TQBrtBNIduF4vJYOKfw7MbS8xH_J01_Q4e6Rk/edit?resourcekey=0-mc_vh5UzrzXfU4kO-3tOjA&tab=t.0#heading=h.19yyjdek3asm). ## Proposal Allow name lookups into a type once it is defined, even before it is complete. ## Details We adopt the following terminology: - An entity is _defined_ at the point where we reach the `{` of its definition. _Exception:_ A namespace is defined by its first declaration. - An entity is _complete_ at the point where we reach the `}` of its definition. Instead of qualified name lookup into the scope of a type requiring the type to be complete, we now only require it to be defined. Therefore, qualified lookup within the braces of the type definition are now permitted. Such a name lookup only finds names that were declared prior to the lookup, in line with the [information accumulation principle](/docs/project/principles/information_accumulation.md). If the name is first declared after the point at which it is looked up, the later declaration of the name is rejected by to the poisoning rule [described earlier](#background]. ```carbon base class A { class Inner {} } class B { extend base: A; // ✅ OK for now, `Inner` names `A.Inner` because // no results were found directly in scope of `B`. var i: B.Inner; // ❌ Error: name `Inner` is poisoned due to prior lookup in this scope. class Inner {} } ``` ### Uses requiring a complete type While this proposal permits qualified name lookup into types that are incomplete, many uses of the names found by such lookups will still require completeness. For example: - Instance binding for a class instance member requires a complete type. ```carbon class X[T:! type](v: T) {} class A { var n: i32; fn F() -> A; // ❌ Error: `A` is not complete. var m: X(fn (A a) => F().n); } ``` - Some uses of interfaces require the interface to be complete, although determining which uses require this is outside the scope of this proposal. ### Lookups into an incomplete generic It is possible to perform qualified name lookup into a generic type before the type is complete. ```carbon class X[T:! type](x: T) {} class A(T:! type) { fn F() -> A(T); // OK, argument to `X` is the `F` function in `A(i32)`. var v: X(A(i32).F); } ``` This may require extra work to handle in the toolchain. In particular, we currently cannot form a specific for the class definition until the class is complete. We have at least two viable implementation strategies to handle this: - Ensure all declarations that can be nested within a type have their own corresponding generic. This is already the case for most such declarations, with field declarations and alias declarations being notable exceptions. - Support evaluating an incomplete eval block for a generic, and finish evaluating each incomplete eval block at the end of the generic definition. ## Rationale - [Code that is easy to read, understand, and write](/docs/project/goals.md#code-that-is-easy-to-read-understand-and-write) - This change removes an ergonomic hurdle and an inconsistency in the language rules. - [Interoperability with and migration from existing C++ code](/docs/project/goals.md#interoperability-with-and-migration-from-existing-c-code) - This rule is more closely aligned with the C++ rule for lookup of class members within the class definition, which allows members to be named before the class is complete. ## Alternatives considered ### Require completeness for qualified name lookup The status quo ante is to disallow qualified access into types until they are complete. The rationale for rejecting that alternative is described in this proposal. ### Do not require a definition for name lookup We could permit qualified and unqualified name lookup into types that are merely declared and not yet defined. Such a lookup would find nothing. This allowance could be used in a case where a type's scope can be extended before the type is defined, which is itself currently not permitted. For example: ```carbon interface I; interface J { let T:! type; } interface K { extend I; extend J; let Unqual:! T; let Qual:! K.T; } ``` We could choose to permit this, and make the lookups for the name `T` in the scope of `I` find nothing and poison the name `T` in `I`, so that the name can be resolved immediately to `J.T`. If `I` were to later introduce a name `T`, that would result in an error. However, this seems like it may be a step too far, and isn't justified by the motivations for this change. From an implementation standpoint, it would also require tracking a list of poisoned names in a type that doesn't even have a scope yet, although that is likely straightforward to handle. ================================================ FILE: proposals/p5164.md ================================================ # Updates to pattern matching for objects [Pull request](https://github.com/carbon-language/carbon-lang/pull/5164) ## Table of contents - [Abstract](#abstract) - [Problem](#problem) - [Proposal](#proposal) - [Alternatives considered](#alternatives-considered) - [Alternative approaches for declaring movable bindings](#alternative-approaches-for-declaring-movable-bindings) - [Treat all bindings under `var` as variable bindings](#treat-all-bindings-under-var-as-variable-bindings) - [Make `var` a binding pattern modifier](#make-var-a-binding-pattern-modifier) - [Alternative approaches to the other problems](#alternative-approaches-to-the-other-problems) - [Initialize storage once pattern matching succeeds](#initialize-storage-once-pattern-matching-succeeds) - [Allow variable binding patterns to alias across `case`s](#allow-variable-binding-patterns-to-alias-across-cases) ## Abstract This proposal re-affirms (with additional rationale) that a `var` pattern declares a durable complete object, and refines the terminology for binding patterns in a `var` pattern to be more explicit about the intended semantics. It also makes several other changes and clarifications to the semantics of pattern matching on objects: - The storage for a variable pattern is initialized eagerly, rather than being deferred until the end of pattern matching. - Any initializing expressions in the scrutinee of a `match` statement are materialized before matching the `case`s. - An initializing expression can only initialize temporary storage or a single variable pattern, not a tuple/struct pattern or a subobject of a variable pattern. Removing this limitation is left as future work. Finally, as a drive-by fix, it clarifies what parts of the `match` design are still placeholders. ## Problem Discussions arising from the implementation of `var` patterns have surfaced some problems with how pattern matching deals with objects: - If binding patterns bind to subobjects of an enclosing `var` object, destructively moving them will lead to double-destruction. - Deferring initialization of variable bindings contradicts our specification of copy/move elision, and makes `if`-guards much less useful. - It was unclear what happens when a `match` statement's scrutinee is an initializing expression, since the result of an initializing expression can't be reused. - It was unclear whether and how copy/move elision applies when initializing a subobject of a variable binding or tuple/struct pattern. The first point deserves some elaboration. We don't yet have a concrete proposal for move semantics, but it's possible to discern the overall direction well enough to see a problem with how it interacts with `var`. The following sketch of move semantics should be considered a **placeholder**, not an approved design. `~x` is a _move_ from the reference expression `x`. It is an initializing expression that initializes an object with the value that `x` held prior to the move, while arbitrarily mutating `x` to make the move more efficient. By default `~x` is _destructive_, meaning that it ends the lifetime of `x`; under some conditions it may instead leave `x` in an [unformed state](/docs/design/README.md#unformed-state), or make a copy of `x`, but only if the type supports doing so. Consider the following code, where `X` and `Y` are types that are movable but not copyable, have nontrivial destructors, and do not have unformed states: ```carbon fn A() -> (X, Y); fn B(var x: X); fn F() { var (x: X, y: Y) = A(); B(~x); } ``` Under the current design of pattern matching, the first line of `F` declares a complete object of type `(X, Y)`, and binds `x` and `y` to its elements. At the end of the body of `F`, that tuple object is still live, so its destructor will run, which will recursively run the destructors for its elements. However, its first element was already destroyed by `~x`, so this would result in double-destruction of that sub-object. In order to avoid that problem, the expression `~x` must be ill-formed. More generally, if a `var` pattern contains a tuple or struct subpattern, the bindings it declares cannot be moved from (unless, possibly, their types permit them to be non-destructively moved, and/or safely destroyed twice). In order to be well-formed, the first line of `F` must be rewritten as: ```carbon let (var x: X, var y: Y) = A(); ``` This makes the code more verbose, and may be surprising to users. ## Proposal The decision that `var` declares a single complete object is reaffirmed, notwithstanding the problem described above. To be more explicit about the intended meaning, we will adjust the terminology: - The term "variable binding pattern" is now limited to a binding pattern that binds to the entire object declared by an enclosing `var` pattern. - We introduce the term "reference binding pattern" to refer to any binding pattern that has an enclosing `var` pattern. Thus, every variable binding pattern is a reference binding pattern, but not vice-versa. To address the other problems: - The storage for a variable binding pattern is initialized eagerly, rather than being deferred until the end of pattern matching. - Any initializing expressions in the scrutinee of a `match` statement are materialized before matching the `case`s. - An initializing expression can only initialize temporary storage or a single variable binding, not a tuple/struct pattern or a subobject of a variable binding. Removing this limitation is left as future work. ## Alternatives considered ### Alternative approaches for declaring movable bindings See [leads issue #5250](https://github.com/carbon-language/carbon-lang/issues/5250) for further discussion of this problem. #### Treat all bindings under `var` as variable bindings We could treat all bindings under a `var` pattern as variable bindings, instead of limiting that to the case of a single binding pattern immediately under the `var` pattern. This would mean that all such bindings declare complete objects, and hence are movable. By the same token, it would mean that a `var` pattern does not declare a complete object. However, we are likely to eventually need a way for a pattern to declare a complete object and bind names to its parts, for example to support Rust-style [@-bindings](https://doc.rust-lang.org/book/ch19-03-pattern-syntax.html#-bindings) or to support in-place destructuring of user-defined types (where destroying the object in order to make its subojects movable could have unwanted side effects). This approach would make it difficult to do that with good ergonomics, because both `var` and the hypothetical complete-object pattern syntax would change the meanings of nested bindings, and the behavior of destructuring, in conflicting ways. This approach also expresses the programmer's intent less clearly, which could harm readability, because a `var` with several nested bindings could be intended to make those bindings movable, or just to make them mutable. That also makes this option less future-proof. If we start with this approach and later migrate to the status quo because we need the extra expressive power, we would need to somehow infer the missing intent information. Conversely, if we start with the status quo and later conclude we aren't going to make its extra expressive power observable, existing valid code will remain valid, with the same behavior, after switching to this approach. The only major advantage of this approach over the status quo is that it's less verbose when declaring multiple movable bindings, and may be less surprising to users because it's less restrictive by default. However, those advantages aren't significant enough to offset those costs. #### Make `var` a binding pattern modifier The current design makes binding patterns fairly context-sensitive, which we generally [try to avoid](/docs/project/principles/low_context_sensitivity.md): a binding pattern declares a variable binding pattern if it's the immediate child of a `var` pattern, a non-variable reference binding pattern if it's an indirect descendant of a `var` pattern, and a value binding pattern otherwise. We could avoid context-sensitivity by making `var` a modifier on binding patterns, like `template`. That would mean there are no reference binding patterns, and the distinction between variable and value binding patterns is always purely local. However, allowing `var` to apply to many bindings at once improves the ergonomics of destructuring into variables, which is likely to be a common use case. Furthermore, the offsetting costs of allowing that are fairly minimal: - We are likely to eventually need reference bindings anyway, so introducing those semantics now isn't adding a cost, it's just incurring that cost sooner. - The cost of context-sensitivity in patterns is comparatively low, because patterns are generally quite small, and there's rarely much need to factor subpatterns out of their initial context. ### Alternative approaches to the other problems #### Initialize storage once pattern matching succeeds Prior to this proposal, the status quo was that the storage associated with a pattern is not initialized until we know that the complete pattern matches. This helps avoid situations where a `case` that does not match nevertheless has visible side effects. However, this approach has several major drawbacks: - It contradicts or at least greatly complicates the guarantee that declarations like `var x: X = F();` do not require any temporary storage, because it would imply that `F()` is not even evaluated until we know the pattern matches. That's feasible for irrefutable patterns like this one, but not for refutable patterns. Even if we were willing to limit that guarantee to contexts that require irrefutable patterns, it would complicate the implementation, because the underlying logic would have major structural differences in the two cases. - It precludes using a variable binding before its enclosing complete pattern is known to match, because that variable would not be initialized. In particular, that means an [`if`-guard](/docs/design/pattern_matching.md#guards) cannot use variable bindings from the pattern it guards. Practically all the motivating use cases for `if`-guards involve using bindings from the guarded pattern, so this is tantamount to making `var` and `if` mutually exclusive. #### Allow variable binding patterns to alias across `case`s Consider the following code: ```carbon fn F() -> X; fn G() -> i32; match ((F(), G())) { case (var x: X, 0) => { ... } case (var x: X, 1) => { ... } case (var x: X, 2) => { ... } case (var x: X, 3) => { ... } ... } ``` Under this proposal, the result of `F()` is materialized in temporary storage, and then copied into the storage for `x` as part of matching each `case` (it can't be moved, because it must remain available for the next `case`). As a result, this code may make as many copies of `X` as there are `case`s, and doesn't even compile if `X` isn't copyable. We could instead treat those `var x: X` declarations as aliases for the materialized temporary. However, in order to generalize that approach we would need to answer questions like: - Do the bindings alias between `case (var x: X, 0)` and `case var (x: X, 1)`? - Do the bindings alias if their names are different? - Do the bindings alias if the two `case`s are separated by another `case` that doesn't have a binding in that position? - Do the bindings alias if they have the same type as each other, but not the same type as the scrutinee? Furthermore, the behavior of these rules would need to be intuitive and unsurprising, because any change that prevents aliasing (in even one case) could break the build or cause surprising performance changes. Even in the best case, this approach is inherently limited. If the bindings have different types, at most one of them can be an alias for the scrutinee; the other must be initialized by a copying conversion. This approach would open up the possibility of a non-taken `case` mutating the state of the scrutinee, by invoking a mutating operation on a variable binding in an expression pattern or an `if`-guard. We may be able to statically forbid such mutations, but that will be easier to assess once we understand the role of mutability in our broader safety story. We conjecture that in most if not all cases where aliasing would be desirable, it will be straightforward to rewrite the code to avoid the problem. For example, the example above can be rewritten as: ```carbon fn F() -> X; fn G() -> i32; var x: X = F(); match (G()) { case 0 => { ... } case 1 => { ... } case 2 => { ... } case 3 => { ... } ... } ``` We can revisit this alternative if that conjecture turns out to be incorrect in practice. ================================================ FILE: proposals/p5168.md ================================================ # Forward `impl` declaration of an incomplete interface [Pull request](https://github.com/carbon-language/carbon-lang/pull/5168) ## Table of contents - [Abstract](#abstract) - [Problem](#problem) - [Background](#background) - [Related work and past discussion](#related-work-and-past-discussion) - [Terminology](#terminology) - [Other considerations](#other-considerations) - [Proposal](#proposal) - [Prior to this proposal](#prior-to-this-proposal) - [Proposed rules](#proposed-rules) - [Details](#details) - [An `impl` implements a single interface](#an-impl-implements-a-single-interface) - [Associated constants may be assigned in the body of an `impl` definition](#associated-constants-may-be-assigned-in-the-body-of-an-impl-definition) - [An `impl` may be forward declared without the interface being complete](#an-impl-may-be-forward-declared-without-the-interface-being-complete) - [Interface requirements](#interface-requirements) - [Examples](#examples) - [Future work](#future-work) - [Addressing the subsumption use case previously addressed by `extend` in interfaces](#addressing-the-subsumption-use-case-previously-addressed-by-extend-in-interfaces) - [Opt-in to using the interface's default definition of an associated function](#opt-in-to-using-the-interfaces-default-definition-of-an-associated-function) - [Rationale](#rationale) - [Alternatives considered](#alternatives-considered) - [Allow implementing multiple interfaces with a single `impl` declaration](#allow-implementing-multiple-interfaces-with-a-single-impl-declaration) - [An `impl` of an interface also implements the interfaces it extends](#an-impl-of-an-interface-also-implements-the-interfaces-it-extends) - [Use the contents of definitions if available](#use-the-contents-of-definitions-if-available) - [Delayed checking of incomplete types](#delayed-checking-of-incomplete-types) - [Allow `impl` declarations with rewrites of defined but not complete interfaces](#allow-impl-declarations-with-rewrites-of-defined-but-not-complete-interfaces) - [Can omit function declarations from `impl` body](#can-omit-function-declarations-from-impl-body) - [Different introducer for assigning associated constants in an `impl` definition](#different-introducer-for-assigning-associated-constants-in-an-impl-definition) ## Abstract Revise rules for what is required and provided by declarations and definitions of interfaces and impls. In particular: - allow `impl` declarations of incomplete interfaces, and - shift from a "use the information from the type definition if it happens to be complete" model to a "only use the information from the definition in contexts where it is required to be defined or complete" model. Resolves questions-for-leads issues [#4566](https://github.com/carbon-language/carbon-lang/issues/4566), [#4672](https://github.com/carbon-language/carbon-lang/issues/4672), [#4579](https://github.com/carbon-language/carbon-lang/issues/4579). ## Problem There are several kinds of entities in Carbon, and most of them support separate forward declaration from definition. You might declare an entity before defining it for a few reasons: - Non-generic runtime functions may be called using only a declaration in an api file, as long as they are defined in the corresponding impl file. This hides implementation details, and allows build work to be parallelized by way of separate compilation. - By [the information accumulation principle](https://docs.carbon-lang.dev/docs/project/principles/information_accumulation.html), we don't want code to rely on information written later in the file. For example, names can't be used before the declaration that introduces that name. This requires the developer to order their code in a way that satisfies those constraints. For example, functions and types (classes, interfaces, named constraints, and so on) can reference the names of any kind of type in their declaration and definition. In some cases, two types will reference each other, requiring forward declarations: A [more complex example with interfaces](/docs/design/generics/details.md#example-of-declaring-interfaces-with-cyclic-references) can be found in the generics design, that requires introducing named constraint forward declarations to represent constraints that can't be expressed yet. We don't want to create a situation where Carbon is introducing a lot of [accidental complexity](https://en.wikipedia.org/wiki/No_Silver_Bullet) into the programming process by creating a declaration ordering puzzle that is difficult or impossible to solve. We would like ordering rules that are straightforward to satisfy, and simple to remember. There are a few different options for ordering requirements, all of which have downsides: - Light requirements are easy to satisfy, but don't provide as much information to use. For example, if you don't require that an interface is defined at a point, then you can't use the interface requirements from its definition. - Strong requirements are difficult to satisfy, and in the worst case can create dependency cycles that can't be satisfied at all. - We could have light requirements, but then use additional information when it is available. This creates complexity. There is complexity in the implementation since there are many cases to consider, and if something goes wrong it could be for many different possible reasons. It increases the danger of introducing coherence concerns, where reordering declarations and definitions could change the meaning of the code. This gives some (situational) flexibility to the developer to solve ordering problems. The design prior to this proposal often uses this last option, creating implementation complexity. We would like to find an alternate approach that gives similar flexibility without the downsides. ## Background ### Related work and past discussion - [Proposal #722](https://github.com/carbon-language/carbon-lang/pull/722): Nominal classes and methods - [Proposal #818](https://github.com/carbon-language/carbon-lang/pull/818): Constraints for generics (generics details 3) - Introduced implied constraints. - [PR #1026](https://github.com/carbon-language/carbon-lang/pull/1026): Clarify class declaration syntax - [Proposal #1084](https://github.com/carbon-language/carbon-lang/pull/1084): Generics details 9: forward declarations - See [the section in the generic details design document](https://github.com/carbon-language/carbon-lang/blob/trunk/docs/design/generics/details.md#forward-declarations-and-cyclic-references) - [Proposal #2760](https://github.com/carbon-language/carbon-lang/pull/2760): Consistent class and interface syntax - [Proposal #3762](https://github.com/carbon-language/carbon-lang/pull/3762): Merging forward declarations - [Proposal #3763](https://github.com/carbon-language/carbon-lang/pull/3763): Matching redeclarations - Updated the rules of `impl` declarations and matching - [Proposal #3980](https://github.com/carbon-language/carbon-lang/pull/3980): Singular extern declarations - [PR #4230](https://github.com/carbon-language/carbon-lang/pull/4230): Add documentation for entity declaration design work - Pending [leads question #4566](https://github.com/carbon-language/carbon-lang/issues/4566): Implementing multiple interfaces with a single impl definition - The currently proposed resolution includes `impl`ing an interface does not `impl` the interfaces it `extend`s, reducing the need to see the `interface` definition in `impl` declarations. - Pending [leads question #4579](https://github.com/carbon-language/carbon-lang/issues/4579): When are interface requirements enforced for an `impl`? - Considers what is required and provided by `impl` forward declarations and definitions. This proposal offers a resolution to these questions. - Pending [leads question #4672](https://github.com/carbon-language/carbon-lang/issues/4672): Declaration and definition of impls and their associated functions - The currently proposed resolution includes moving assignment of associated constants into the `impl` definition in the normal case, reducing the need to see the `interface` definition in `impl` declarations. - [Proposal #5087](https://github.com/carbon-language/carbon-lang/pull/5087): Qualified lookup into types being defined - Allows member access inside a type's definition, in addition to afterwards. Introduces the _defined_ terminology. ### Terminology - A _declaration_ specifies information about an entity. Declarations are either _forward declarations_ (which generally end with a semicolon `;`) or _definitions_ (which generally end with a body enclosed in curly braces `{`...`}`). - We say an entity is _declared_ by the first declaration textually in the source. If that declaration is a definition, we say the entity is declared at the point the body begins (at the open curly `{`). - We say an entity is _defined_ once the body of its definition is started (at the open curly `{`). We allow name lookup into an entity once it is defined. See [proposal #5087: Qualified lookup into types being defined](https://github.com/carbon-language/carbon-lang/pull/5087). - We say an entity is _complete_ at the end of its definition (at the close curly `}`). - We say a facet type is _identified_ if all the interfaces it references are declared and all of its named constraints are complete. An identified facet type is associated with a known set of interfaces. - An _implied constraint_ is a condition that must hold if a type is used in a given type expression, which we enforce in some way other than checking that the condition holds at that point. For example: ``` interface Hash { ... } class HashSet(T:! Hash) { ... } // `U` must satisfy the constraints on the `T` parameter to `HashSet`. // In this case, we enforce it using an implied constraint, so we don't // require `U impls Hash` beforehand (say by being declared `U:! Hash`). fn Contains[U:! type](needle: U, haystack: HashSet(U)) -> bool; // In this case, `Contains` is equivalent to this declaration that // doesn't use implied constraints: fn Contains[U:! type where .Self impls Hash] (needle: U, haystack: HashSet(U)) -> bool; // In this example, the implied constraint is on something more // complicated than a single symbolic parameter. fn F[V:! type, W:! type](x: HashSet(Pair(V, W))); // is equivalent to: fn F[V:! type, W:! type where Pair(V, .Self) impls Hash] (x: HashSet(Pair(V, W))); ``` - We generally require that there always be a way to explicitly declare constraints so implied constraints are not needed. - Implied constraints allow declarations to be more concise, particularly we hope that common requirements can be omitted since they are implied. - Implied constraints are also a tool we can choose to use to say that a requirement on a parameter can be enforced at a later point. In this example, ``` interface B {} interface A; class C(T:! B); fn F(U:! A, x: C(U)); interface A { require Self impls B; } ``` the symbolic type parameter `U` has a constraint that it impls `A`, and we can add an implied constraint that it also impls `B` because it is used as an argument to `C`. This constraint might be redundant with a requirement from the definition of `A`, which may or may not have been available at the point where `F` is declared. Callers have to provide arguments that satisfy the additional requirement if it turns out that `A` alone is not sufficient. ### Other considerations Different kinds of entities have different requirements (ignoring `extern` declarations): - A compile-time function must be defined before a call to it is evaluated. - A generic function must be defined before the end of each file in which a call to it is type-checked. - Non-generic runtime functions may be defined separately. They may be forward declared in an api file and defined in a corresponding impl file. - Types must be complete before they are used in a function definition. This includes the parameter types, the return type, the argument types of functions that are called in the body, and the types of local variables. - Interfaces and named constraints must be defined in the same file they are declared. - We want to allow some uses of classes that are declared in an api file and defined in a corresponding impl file -- in particular, we treat the "pointer to `C`" type `C*` to be complete even if `C` is not. Our main tool for making ordering easier is forward declarations. Declarations necessarily come after all the declarations of entities named in its parameter and return types. So declarations need to be ordered in some [topological sorted order](https://en.wikipedia.org/wiki/Topological_sorting) that respects those dependencies. This is unavoidable, though, as long as we are not allowing forward references, following [the information accumulation principle](https://docs.carbon-lang.dev/docs/project/principles/information_accumulation.html). Definitions will have all the dependencies of a declaration, plus perhaps some more. As a result we will want to put them later in the file. This is possible if a forward declaration is good enough for other declarations to use that name in all the ways that they need to. ## Proposal The goal is to allow forward declarations in more situations, and allow them to satisfy requirements in more situations. In particular, we make these changes: - We accept the proposed resolution of [leads question #4566](https://github.com/carbon-language/carbon-lang/issues/4566): - An `impl` of an interface `I` does not `impl` any interface `I` extends. - A single `impl` can be for only a single interface. - We accept the proposed resolution of [leads question #4672](https://github.com/carbon-language/carbon-lang/issues/4672): - Assignment of associated constants is moved into the `impl` definition in the normal case, though may still be present in a `where` clause at the end of the declaration. - Associated constants are assigned using `where X = value;` declarations, with semantics matching rewrite constraints in `where` clauses. - No part of the `impl` declaration will be excluded from syntactic match. - Every `impl` must be defined in the same file (not just the same library) as its declaration. - However, the definitions of its member functions may be separate in the impl file, out of line, as provided in [proposal #3763](/proposals/p3763.md#out-of-line-definitions-of-associated-functions). - An `impl` may be forward declared without the interface being defined. - This is enabled by not needing access to the definition to see other interfaces required or extended or to see the associated constants that need to be given values. - Instead we require the interface to be _identified_. This means that if the facet type to the right of the `as` is given by a named constraint, that named constraint needs to be complete so we can see which interface it corresponds to. (It is an error if it doesn't correspond to a single interface, by the resolution of [#4566](https://github.com/carbon-language/carbon-lang/issues/4566).) - However, an `impl` declaration of a facet type with `.A =`... rewrite constraints (for example in a `where` clause), does still require the interface to be complete. - We answer questions from [leads issue #4579](https://github.com/carbon-language/carbon-lang/issues/4579): - Since `impl` forward declarations do not require the interface to be defined, any requirements that other interfaces be defined from the interface definition are ignored. - An `impl` definition of an interface `I` requires first establishing that the type implements any interfaces required by `I`, but that can be satisfied by an `impl` declaration. - We shift from a "use the information from the type definition if it happens to be complete" model to a "only use the information from the definition in contexts where it is required to be defined or complete" model - However, an interface `I` containing a `require Self impls J` or `extend J` declaration adds a fact that "types `T` satisfying `T impls I` also satisfy `T impls J`" to the environment. These changes are intended to allow developers to write their api files in an order that roughly follows: - namespace declarations in any order; - other declarations in any topological dependency order; - type (interface, constraint, class) definitions in any order; - impl definitions in any order; - function definitions, including both those that are needed in the api file plus those the developer wishes to include, in any order. This would be sufficient except we have a few additional constraints, which remain: - Performing member access into a type requires the type to be defined, not just declared. - Members of an effectively final impl may only be accessed after their value has been established. - A function that is called at compile time must be defined before that call is executed by the compiler. ### Prior to this proposal | | prereq | provides | complete by [EOF](https://en.wikipedia.org/wiki/End-of-file) | | :---------------------------------------------------------------------------------- | :------------------------------------------------------------------------------------- | :------------------------------------------------------------------------------------- | :----------------------------------------------------------- | | `impl C as Y;` | **`Y` complete** | `C impls Y` | | | `impl C as Y where .A =` ... | `Y` complete | `C impls Y` | | | `impl C as Y {` ... `}` | `Y` complete | `C impls Y` | `C` | | `interface I;` | | `I` declared | `I` | | `interface Y {`
` require Self impls Z;`
`}` | **`Z` declared** | **`Y` complete** | `Z` | | `interface Y {`
` require Self impls Z;`
`}`
`impl C as Y {` ... `}` | **open question [\#4579](https://github.com/carbon-language/carbon-lang/issues/4579)** | **open question [\#4579](https://github.com/carbon-language/carbon-lang/issues/4579)** | | | `fn F[T:! I](x: T);` | `I` declared | | `F`, `I` | | `fn F[T:! I](x: T) {` ... `}` | `I` complete | `F` complete | | | `interface I;`
`class C;`
`class D(T:! I);`
`fn F(x: D(C));` | `C impls I` | | `I`, `C`?, `D` | | `interface I;`
`class D(T:! I);`
`fn F[U:! type](x: D(U));` | | `U impls I`
(implied constraint) | `I`, `D`, `F` | ### Proposed rules | | prereq | provides | complete by [EOF](https://en.wikipedia.org/wiki/End-of-file) | | :---------------------------------------------------------------------------------- | :----------------- | :------------------------------------------------------------------------------------- | :----------------------------------------------------------- | | `impl C as Y;` | **`Y` identified** | `C impls Y` | **`Y`, `impl C as Y`** | | `impl C as Y where .A =` ... | `Y` complete | `C impls Y` | **`impl C as Y` ...** | | `impl C as Y {` ... `}` | `Y` complete | `C impls Y` | `C` | | `interface I;` | | `I` declared | `I` | | `interface Y {`
` require Self impls Z;`
`}` | **`Z` identified** | **for any symbolic type `T`,
`T impls Y` implies `T impls Z`;
`Y` complete** | `Z` | | `interface Y {`
` require Self impls Z;`
`}`
`impl C as Y {` ... `}` | **`C impls Z`** | **`C impls Y`** | | | `fn F[T:! I](x: T);` | `I` declared | | `F`, `I` | | `fn F[T:! I](x: T) {` ... `}` | `I` complete | `F` complete | | | `interface I;`
`class C;`
`class D(T:! I);`
`fn F(x: D(C));` | `C impls I` | | `I`, `C`?, `D` | | `interface I;`
`class D(T:! I);`
`fn F[U:! type](x: D(U));` | | `U impls I`
(implied constraint) | `I`, `D`, `F` | > Whether classes need to be complete by the end of the file is not the subject > of this proposal. ## Details ### An `impl` implements a single interface We resolve [leads question #4566](https://github.com/carbon-language/carbon-lang/issues/4566) with two rules: - Implementing an interface does not implement any of the interfaces it extends. - An impl declaration should implement a single interface. We allow facet type expressions to the right of `impl`...`as`, as long as that facet type corresponds to a single interface (ignoring interfaces it extends, which incidentally seems to [match Rust supertraits](https://stackoverflow.com/questions/75847426/why-do-we-need-a-separate-impl-for-a-supertrait)). This supports the use case of `Core.Add` actually being a named constraint defined in terms of `Core.AddWith`. This requires that the facet type is identified, at a minimum, when used in an `impl` declaration, so it can be resolved into the single interface actually being implemented. The "subsumption" use case of "when an interface X is implemented, Y will be implemented without the user having to write a separate impl definition" will instead be handled using [blanket implementations](/docs/design/generics/details.md#blanket-impl-declarations). There are a couple of variations: - One interface is a strict superset of the other. In this case it would be a lot less confusing/surprising if the interfaces use the same implementations of functions that overlap. This is accomplished using a [`final`](https://github.com/carbon-language/carbon-lang/blob/trunk/docs/design/generics/details.md#final-impl-declarations) blanket implementation. For example, we will define `final impl forall [T:! type, U:! ImplicitAs(T)] U as As(T)` and have `As(T).Convert` forward to `ImplicitAs(T).Convert`. This way types will either implement `ImplicitAs(T)` or `As(T)`, and will get an error if they try to implement both. - One interface can be implemented in terms of the other. For example, if `Ordered` implies an implementation of `IsEqual`, types might still want to provide an explicit definition of `IsEqual` when they can do so more efficiently. This would use a non-`final` blanket implementation. Note that the orphan rule prevents this blanket impl from being written unless the two interfaces in a subsumption relationship are in the same library. Support for these use cases is [future work](#addressing-the-subsumption-use-case-previously-addressed-by-extend-in-interfaces). For now, `extend I;` in an interface definition continues to mean that `I` is required and the names of `I` are included as aliases, matching the meaning in named constraints, see "interface extension" in [proposal #553](https://github.com/carbon-language/carbon-lang/pull/553). ### Associated constants may be assigned in the body of an `impl` definition We resolve [leads question #4672](https://github.com/carbon-language/carbon-lang/issues/4672) with the following updated rules: - The `impl` definition must be in the same file as its owning declaration. - The first declaration establishes that the type implements the interface. - That declaration may optionally specify a subset of the associated constants in a `where` clause. The `where` clause syntax is the same as before this proposal, but the requirement that all associated constants be given their final values (or accept their defaults where available) is removed. - This adds some flexibility to ordering, allowing a developer to say that a type implements an interface before being able to specify the values of its associated constants, while also allowing a subset to be specified if some later declaration needs to access their values. - Redeclarations of an `impl` no longer have any special treatment of `where` clauses. - Each redeclaration is required to match syntactically, so all will have the same `where` clause, if any. - The part between `impl` and the end of the declaration (marked by a `{` or `;`) is the name of the `impl`. This will be used in places where we want to name the `impl`, such as in an impl priority ("match first") block or out-of-line definitions of `impl` members. - Associated constant assignments should be encouraged to be placed in the `impl` definition rather than in a `where` clause in the declaration, unless needed in the declaration to resolve dependencies. - This makes the name of the `impl` shorter and easier to state. - The syntax for specifying the value of an associated constant in an `impl` definition is `where X = value;`, just like a rewrite clause in a `where` expression, except that `where` in this case is an introducer rather than a binary operator, and the names of the associated constants are in scope, so there is no need to prefix them with a period (`.`). - The non-function associated constants are fixed at the closing curly `}` of the `impl` definition. At that point, each associated constant that has not been assigned a value is given its default value. If it does not have a default, an error diagnostic is issued. - The associated function declarations are fixed by the end of the `impl` definition. - Only associated functions with defaults may be omitted from the `impl` definition. - Not mentioning an associated function with a default in the `impl` definition means the default version from the interface will be used. - Associated functions declared in the `impl` definition must have a matching definition. - If the declared signature of an associated function in the `impl` definition does not match the signature given in the `interface` definition, but are compatible, a stub function that does the conversion is generated to bridge between the two versions. - Out-of-line function bodies are allowed in the impl file, even when the `impl` definition is in the api file. - This parallels how function definitions are not part of the definition of classes. - Missing function body definitions are diagnosed at link time. ### An `impl` may be forward declared without the interface being complete We have removed the reasons to require that the interfaces being implemented are defined for an `impl` forward declaration: - We no longer need to see which interfaces are required or extended from the definition of the interface, since the `impl` no longer implements those. - We no longer need to see which associated constants are members of the interface to see if a declaration specifies values for all of them. - If a `where` clause is used in the declaration, the interface must be defined in order to name the associated constants, but `where` clauses are discouraged. However, if the facet type being implemented is a named constraint, we do need that to be complete so we can resolve the interface it resolves to. (It is an error if it doesn't correspond to a single interface, by the resolution of [#4566](https://github.com/carbon-language/carbon-lang/issues/4566).) This means that generally we can establish that a type implements an interface right after they are both declared. ### Interface requirements [Leads issue #4579](https://github.com/carbon-language/carbon-lang/issues/4579) concerns itself with interfaces that require other interfaces to be implemented, as in: ``` interface Z; interface Y { require Self impls Z; } ``` There are two sides to this: what do you have to establish about `Z` before an `impl` declaration or definition that a type implements `Y`, and what does a declaration or definition that a type implements `Y` establish about `Z`? We adopt the following rules: - Since `impl` forward declarations do not require the interface to be defined, any requirements that other interfaces be defined from the interface definition are ignored. ``` class C1; // ✅ Allowed impl C1 as Y; // `class C1` and `impl C1 as Y` must be defined in the same file. ``` - An `impl` definition of an interface `I` requires first establishing that the type implements any interfaces required by `I`, but that can be satisfied by an `impl` declaration. ``` class C2; // ❌ Invalid, must first establish `C2 impls Z`. Still invalid if // `impl C2 as Z` appears later in the file. impl C2 as Y {} class C3; // ✅ Allowed impl C3 as Z; impl C3 as Y {} class C4; // ✅ Allowed impl C4 as Z {} impl C4 as Y {} // The classes and `impl C3 as Z` must be defined in the same file. ``` This is is aligned with the shift from a "use the information from the type definition if it happens to be complete" model to a "only use the information from the definition in contexts where it is required to be defined or complete" model that this proposal this is switching to. To recapture some of that context sensitivity, we say that the definition of an interface with requirements, like `Y` above, introduces the extra information that "symbolic types `T` that satisfy `T impls Y` also satisfy `T impls J`." For example: ``` interface Z; interface Y { require Self impls Z; } class C(T:! Z); class D(U:! Y) { // ✅ U impls Y so it also impls Z. // // Wouldn't use implied constraints here since `U` is // from a containing scope. fn F(x: C(U)); } ``` This rule only applies to symbolic types, since we want to only say that concrete types implement an interface if we have a declared `impl` to witness that fact. Symbolic types depend on the value of some generic parameters and we accept that some accesses of interface members will result in symbolic values that will only have known values once concrete argument values are supplied for the generic parameters. For concrete types, the access is performed immediately, and it is an error if we don't have an `impl` declaration we can point to with the witness. For concrete types, there is no later point where an argument is supplied to delay these checks. For example: ``` interface Z; interface Y { require Self impls Z; } class C(T:! Z); class D {} // ✅ Allowed, since `impl` declarations are allowed for declared // entities. Interface requirements are not considered. impl D as Y; // ❌ Error: D is not known to implement Z. The fact that Y // requires Z is not used since D is a concrete type. fn F(x: C(D)); // Too late to affect the previous declaration, by the information // accumulation principle. impl D as Z; ``` Similarly, it is also an error to access a member of an `impl` of a concrete type that doesn't have a known value, even if it is given a value later in the file. For example: ``` interface I { let A:! type; let B:! type; } fn F(T:! I) -> T.A; class C {} class D {} impl C as I; fn G1() { // ❌ Error: C impls I, but C.(I.A) is unknown. var x: auto = F(C); // ❌ Error: C.(I.A) is unknown. let y: C.(I.A) = 0; // ❌ Error: C.(I.B) is unknown. let b: C.(I.B) = false; } impl D as I where .A = i32; fn H() { // ✅ Allowed: D.(I.A) = i32; var x: auto = F(D); var y: D.(I.A) = 0; // ❌ Error: D.(I.B) is unknown. let b: D.(I.B) = false; } impl C as I { where A = i32; where B = bool; } fn G2() { // ✅ Allowed: C.(I.A) = i32; var x: auto = F(C); let y: C.(I.A) = 0; // ✅ Allowed: C.(I.B) = bool; let b: C.(I.B) = false; } ``` ### Examples This example shows using incomplete entities following the rules of this proposal: ``` interface X; // ✅ Allowed to use incomplete interfaces in function declarations. fn F(U:! X); class C; // ✅ Allowed to use incomplete types and interfaces in impl declarations. impl C as X; interface Y; interface X { // ✅ Allowed to use an incomplete interface. require Self impls Y; } // Classes must be defined before being used in a function definition. class C { ... } fn G() { // ✅ Allowed since C is complete and we have a declaration `impl C as X;` F(C); } // The above declarations require that `interface Y`, `fn F` (since it is // generic), and `impl C as X` are defined in the same file. interface Y { ... } fn F(U:! X) { ... } // Required for `impl C as X` definition. impl C as Y; impl C as X { ... } impl C as Y { } ``` This example demonstrates using associated constants and interface requirements: ``` interface X2 { let A:! type; let B:! type; } interface Y2 { require Self impls X2; // ✅ Allowed since `X2` is required and we can access // its `A` member since `X2` is complete. fn F() -> X2.A; } class C2 {} impl C2 as X2 where .A = i32; // ✅ Allowed since `C2` and `Y2` are complete, and // `C2 impls X2` so `C2` satisfies the requirement in `Y2`. impl C2 as Y2 { // ✅ Allowed since the value of `C2.(X2.A)` is known to // be `i32`, even though `impl C2 as X2` is not complete. fn F() -> i32; } // There needs to be a definition of `C2 as X2 where .A = i32` // in the same file. The `where` clause needs to be repeated // verbatim, since redeclaration requires a syntactic match. impl C2 as X2 where .A = i32 { // Remaining members of `X2` that do not have default // values need to be assigned. where B = i32; } ``` ## Future work ### Addressing the subsumption use case previously addressed by `extend` in interfaces We do expect to have collections of interfaces that have a stronger "extending" relationship than is provided by the current `extend` declaration in interfaces. For example, a type implementing `ImplicitAs` should not have to have a separate declaration that it also implements `As`. Addressing this use case is out of scope of this proposal, though, and will be addressed in a future proposal. This future proposal may include: - A feature to copy the members of an interface into another to make the subsumption use case easier to write and involve less duplication. - A feature to define an implementation of an interface in terms of another by forwarding, for similar reasons. - A `final` version of the `match_first`/`impl_priority` feature to resolve conflicts when multiple interfaces want to subsume a common interface. We likely want a feature like this for function overloading as well. - Some way of handling an `impl` that could overlap with a `final impl`, but doesn't in practice. - Possible support for implementing multiple interfaces with a single impl definition, as the result of using an `&` operator or named constraint to the right of `as`, as in [the considered alternative](#allow-implementing-multiple-interfaces-with-a-single-impl-declaration). For now, we leave `extend` as meaning "requires plus include aliases of the names," matching the behavior in named constraints. But this will be reconsidered once we have support for this other use case. ### Opt-in to using the interface's default definition of an associated function We've considered that we may want to allow an `impl` to opt into using the default definition of a function from the interface by writing `= default;` instead of an inline body in curly braces `{`...`}`. We will see if that is a desirable construct to add with experience. This idea was suggested in [issue #4672](https://github.com/carbon-language/carbon-lang/issues/4672#issuecomment-2539845620). ## Rationale The specific solution was chosen to align with [the information accumulation principle](https://docs.carbon-lang.dev/docs/project/principles/information_accumulation.html). In particular, allowing `impl` declarations for incomplete interfaces gives additional flexibility to developers to satisfy those constraints. By reducing the different behavior based on whether a previous declaration was a definition, this proposal reduces complexity in the toolchain and tools that operate on Carbon source code. This benefits the [Language tools and ecosystem](/docs/project/goals.md#language-tools-and-ecosystem) and [Fast and scalable development](/docs/project/goals.md#fast-and-scalable-development) [Carbon goals](/docs/project/goals.md). This is intended to also help humans have a simpler mental model of the compiler, to help the [Code that is easy to read, understand, and write](/docs/project/goals.md#code-that-is-easy-to-read-understand-and-write) goal. ## Alternatives considered The trade offs and alternatives were discussed in [this document](https://docs.google.com/document/d/1NNhBU5tywGkeneLmTQ4DwHyCp5vYlwTiR_wmnjn9wnM/edit) and in open discussion meetings on these dates: - [2025-02-07](https://docs.google.com/document/d/1Iut5f2TQBrtBNIduF4vJYOKfw7MbS8xH_J01_Q4e6Rk/edit?resourcekey=0-mc_vh5UzrzXfU4kO-3tOjA&tab=t.0#heading=h.6ziehwl15x6a) - [2025-03-04](https://docs.google.com/document/d/1Iut5f2TQBrtBNIduF4vJYOKfw7MbS8xH_J01_Q4e6Rk/edit?resourcekey=0-mc_vh5UzrzXfU4kO-3tOjA&tab=t.0#heading=h.22fvs78tr2ej) - [2025-03-05](https://docs.google.com/document/d/1Iut5f2TQBrtBNIduF4vJYOKfw7MbS8xH_J01_Q4e6Rk/edit?resourcekey=0-mc_vh5UzrzXfU4kO-3tOjA&tab=t.0#heading=h.x0brmxoi93n4) - [2025-03-06](https://docs.google.com/document/d/1Iut5f2TQBrtBNIduF4vJYOKfw7MbS8xH_J01_Q4e6Rk/edit?resourcekey=0-mc_vh5UzrzXfU4kO-3tOjA&tab=t.0#heading=h.lsqxlvgwdvoh) - [2025-03-13](https://docs.google.com/document/d/1Iut5f2TQBrtBNIduF4vJYOKfw7MbS8xH_J01_Q4e6Rk/edit?resourcekey=0-mc_vh5UzrzXfU4kO-3tOjA&tab=t.0#heading=h.lsufbqunqul0) - [2025-03-18](https://docs.google.com/document/d/1Iut5f2TQBrtBNIduF4vJYOKfw7MbS8xH_J01_Q4e6Rk/edit?resourcekey=0-mc_vh5UzrzXfU4kO-3tOjA&tab=t.0#heading=h.v0stu27nansb) - [2025-03-20](https://docs.google.com/document/d/1Iut5f2TQBrtBNIduF4vJYOKfw7MbS8xH_J01_Q4e6Rk/edit?resourcekey=0-mc_vh5UzrzXfU4kO-3tOjA&tab=t.0#heading=h.jagz2i1dynjx) ### Allow implementing multiple interfaces with a single `impl` declaration This was considered in [leads question #4566](https://github.com/carbon-language/carbon-lang/issues/4566). There are definite use cases for this feature, particularly arising from evolution. For example, you might want to split an interface into two new interfaces, and have a named constraint with the original name extend both so that existing code continues to work the same. With this proposal, those changes will be harder and have more steps. Two specific approaches to implementing multiple interfaces were eliminated from consideration in that issue: - The "all or nothing" approach where the `impl` definition is used for all of the interfaces, or none of them are. This would create too much uncertainty about whether an `impl` is applicable, particularly since constraints in generic code are not sensitive to whether something is specialized. - The "Constrained impls" approach where an `impl` of multiple interfaces is treated as a collection of `impl`s of the individual interfaces with the additional constraint that no specialization changes the values of any non-function associated constants of any of the interfaces. Those constraints though are ultimately circular and not well defined. The remaining "independent impls" approach seemed possible. In this approach an impl of multiple interfaces is treated as a collection of impls of the individual interfaces. In particular, the definition of a member of one interface can assume that the other interfaces are implemented, but not that the associated types (or other non-function associated constants) have expected values. This would introduce some complexities and a number of questions would need to be answered around how a single `impl` definition would be split into definitions of the individual interfaces, how dependencies between those pieces would be resolved, and how these restrictions would be exposed to the user in diagnostics. ### An `impl` of an interface also implements the interfaces it extends This was the design before this proposal, but in [leads question #4566](https://github.com/carbon-language/carbon-lang/issues/4566) we found a number of problems with that approach: - A parameterized interface extending a non-parameterized interface, or an interface with fewer parameters, leads to multiple implementations of the extended interface. - There are multiple possible semantics you might want, and having a single `impl` does not provide the affordances for choosing between those options, where one `impl` per interface would. For example, in `impl forall [T:! type] C(T) as I & J where .(I.x) = i32 and .(J.y) = .(I.x)`, if there is a specialization of `C(T)` for `I`, will `J.y` have the value `i32` or the `I.x` from the specialization? In practice, the semantics of rewrites mean that `.(I.x)` is replaced with `i32` at an early stage in the compiler (to support things like `.(J.y) = .(I.x).D`), and so only the first option is consistent. This is a particular concern for the "Independent impls" option above. If this `impl` is split into two, then the different possible meanings have different spellings: - `impl forall [T:! type] C(T) as J where .(J.y) = i32` means `J.y` will be `i32` independent of any specialization of `C(T)` for `I` - `impl forall [T:! type where C(T) impls I] C(T) as J where .(J.y) = .(I.x)` means `J.y` matches `I.x` even if `C(T)` is specialized - `impl forall [T:! type where C(T) impls (I where .x = i32)] C(T) as J where .(J.y) = .(I.x)` means this impl won't be used unless `I.x` is `i32`. Note this last form approximates the "Constrained impls" approach above, but with an explicit ordering to determine the semantics, and the existing language rules preventing the code from declaring cycles that would make it ambiguous. - If an interface `J` extends `I` but they are defined in distinct libraries, there is no guarantee that an implementation of `J` belongs in the same library as an implementation of `I` for the same type due to the orphan rule. - The current documented rule for which interfaces an impl implements is those whose members are defined in the interface definition. This rule is ambiguous for empty interfaces or interfaces where all the associated functions have defaults. It also requires [a lot of context](https://github.com/carbon-language/carbon-lang/blob/trunk/docs/project/principles/low_context_sensitivity.md) to answer that question (this was intentional, to allow options for refactoring an interface without having to update implementations of it). In addition to being the source of readability concerns, this muddies the meaning of `impl` declarations, and make the compiler implementation much trickier (the compiler can't say what an impl declaration provides at the point where it is written, making it hard to give that declaration a clear type in the SemIR). - Ideas we considered to determine the interfaces implemented from only the impl declaration ran into problems. Without being able to control which interfaces an impl is defining, then it isn't clear how to handle implementing two interfaces that have common interface they both extend unless you implement them both in a single impl definition (which may not even be possible due to the orphan rule). Another idea we had in this space was a way to say "this interface minus one of the interfaces it extends" (maybe `J \ I`?). We considered some restrictions on `extend` to address some of these concerns: - Perhaps implementing an interface only implemented the interfaces it extends that have to be defined in this library by the orphan rule. - Perhaps an interface may only extend an interface defined in the same library. - Perhaps an interface may only be extended by a single interface. This precluded motivating use cases for `extend`, though, like the subtyping relationships between different kinds of graphs used in [the Boost Graph library](https://docs.google.com/document/d/15Brjv8NO_96jseSesqer5HbghqSTJICJ_fTaZOH0Mg4/edit?resourcekey=0-CYSbd6-xF8vYHv9m1rolEQ&tab=t.0)). Changing the meaning of `extend` is left for a future proposal, when we address [the subsumption use case previously addressed by `extend` in interfaces](#addressing-the-subsumption-use-case-previously-addressed-by-extend-in-interfaces). ### Use the contents of definitions if available In the course of implementing the existing design, uses of "TryToCompleteType" function were found to be prone to leading to coherence issues. If being incomplete does not lead to an error, we need to establish that the results of the definition appearing before and after that test are the same. When there were multiple types involved, this led to an explosion of combinations to test. The new model has less conditional logic, and as a result less complexity. ### Delayed checking of incomplete types We could delay checking uses of incomplete types until some later point, either when the type is complete, a use that requires that type to be complete, or the end of the file where we know the most about it. This is an option, and we might adopt it if we found that it was too hard in practice to satisfy the constraints adopted by this proposal. However it would significantly increase the complexity of the toolchain implementation, which would lead to a corresponding increase in the difficulty of understanding how the code will be interpreted. ### Allow `impl` declarations with rewrites of defined but not complete interfaces We at first did not have a rule saying that `I` needed to be complete in `impl C as I where .X = ...`. The rationale was that accessing members of `I` only needed `I` to be defined, not complete. However, an `impl` declaration inside the definition of the interface being implemented could ultimately never be defined, even if we allowed the declaration. This is because the `impl` definition would have to be in a different scope than its declaration, as can be seen in this example that uses a lambda function to get a scope that can have an `impl` declaration inside an `interface` definition: ``` interface I { let U:! type; default let T:! type = (fn() -> type { class C { } // ✅ Allowed since `I` is declared, with the exception that // this requires a definition before the end of the file. impl C as I; // ✅ Would be allowed since `I` is defined, including its // member `U`. Again this requires a definition in this file. impl C as I where .U = C; // ❌ Neither of the above impls may be defined: // - Can't be defined here since `I` is not complete. // - Can't define after `I` is complete, since redeclarations // must match syntactically and have no hope of naming this // `C` that way. // - In general, the impl definition would have to re-enter the // same scope. impl C as I where .U = C {} return C; })(); } ``` This simplifies the toolchain implementation of this feature, since it means we can create an `impl` witness for declarations that use rewrite constraints with the full knowledge of the interface's definition. ### Can omit function declarations from `impl` body We considered saying that an `impl` definition does not need to include declarations that are unchanged from the interface definition. However, this raised a number of questions and problems: - The `interface` definition is in a different scope from the `impl`, and potentially a different file. This, combined with the evolution problems of depending on the specific token sequence used in the `interface` definition, suggest that we would need a different matching rule that was semantic instead of syntactic. - Having a different declaration matching rule was anticipated as creating a bunch of difficulties once function overloading is added to Carbon, particularly overloaded functions in interfaces. - Without a declaration in the `impl` body, there were a number of problems resolving how to handle a function in the `impl` that was compatible with but a different signature from the interface, and when that different signature would be used. - These issues seemed to magnify if the interface had a default definition for the function. ### Different introducer for assigning associated constants in an `impl` definition Other options we considered were `provides`, `alias`, and whatever we eventually choose for [#5028](https://github.com/carbon-language/carbon-lang/issues/5028). We ultimately liked matching how you would assign associated constants in the declaration using the `where` operator and a rewrite constraint, particularly since we wanted to use the same semantics. ================================================ FILE: proposals/p5233.md ================================================ # Towards more async "sync"-ing [Pull request](https://github.com/carbon-language/carbon-lang/pull/5233) ## Table of contents - [Abstract](#abstract) - [Problem](#problem) - [Background](#background) - [Proposal](#proposal) - [Details](#details) - [Last Week In Carbon](#last-week-in-carbon) - [Discord channel to surface interesting topics](#discord-channel-to-surface-interesting-topics) - [Discussion / demo session every two months](#discussion--demo-session-every-two-months) - [Rationale](#rationale) - [Alternatives considered](#alternatives-considered) - [Keep the existing structure](#keep-the-existing-structure) - [Drop the discussion sessions entirely](#drop-the-discussion-sessions-entirely) - [Drop the weekly summaries entirely](#drop-the-weekly-summaries-entirely) - [Select a slightly different format or structure](#select-a-slightly-different-format-or-structure) ## Abstract Proposal to switch our week-to-week Carbon project development syncs to be more async. - Start of each week, create a summary of what happened last week. - Publish this in GitHub discussions for async reading and further discussion. - Stop our weekly meeting focused on these summaries. - Start up a new discussion meetings every two months. - Structure will be a 10-minutes-or-less update, and a "demo". - Demo may be traditional: showcase a newly landed thing in Carbon. - Or demo may showcase an interesting top-of-mind language design discussion. - Either way, goal will be to field lots of questions about the topic and have a good discussion everyone understands, not to reach some "conclusion" or "decision". ## Problem The weekly Carbon meeting is providing value by aggregating all the activity across our forums and helping both active contributors and folks following the Carbon project keep up-to-date on the different discussions that take place. However, it has stopped being a forum with active discussions where we dive deeply into any specific topics. The summaries are also presented live and synchronously which isn't adding much value on top of having them written up in our notes. Somewhat tongue-in-cheek version of the problem: our weekly meeting in its current format should probably be an email. ;] But we don't want to just get rid of the meeting and lose the value of aggregating activity across the project. We need a new structure that is closer to that of an email. And ideally, we should re-capture some of the discussion benefits that we have had in the past. ## Background Currently, Carbon has a [weekly sync](https://github.com/carbon-language/carbon-lang/blob/trunk/CONTRIBUTING.md#:~:text=The%20weekly%20sync%2C%20where%20contributors%20are%20welcome.) meeting for contributors, that opens with a detailed summary of all the discussions and activity over the past week. ## Proposal We propose to restructure our weekly sync meeting to be an asynchronous process that still provides weekly cross-project and cross-contributor visibility. We suggest a "Last Week in Carbon" structure based on the weekly summary structure we are already using in our weekly syncs. But these will instead be posted towards the beginning of the week in a GitHub Discussion thread. We also suggest adding a less frequent discussion meetings with a structure designed to help elicit broader discussion across the community. We suggest a cadence based on the Carbon Copy newsletter of every two months. The start will be a _very_ brief 10-minute update on major developments in the project, and then a 45-minute "demo" of some kind. Anyone working on Carbon who has a good idea for something to demo can sign up to do so. If we don't have a technical demo, we'll pick an active and interesting language design discussion and demo our language design process by having a small part of that discussion live. We'll encourage everyone to ask any questions needed to follow along and focus on letting folks understand a slice of what is happening in Carbon development over reaching a specific resolution or outcome. ## Details ### Last Week In Carbon The goal is to base these heavily on the existing weekly sync meeting minute summaries of discussion and activity in the Carbon project and weekly newsletters like LLVM Weekly. Note, these should _not_ be prose newsletters. They should focus on structured information in a bulleted format that is easily skimmed. The exact structure, process, and format used should be heavily driven by the contributors producing these posts, but we suggest using a collaborative tool (docs) for drafting, and then posting the results to GitHub Discussions for broader visibility and discussion. We also suggest that these be time boxed to a few hours of work total and the content filtered as needed to fit that time box rather than pushing to include every topic for summarizing. Beyond that high-level guidance, the exact format is up to those authoring it. We do encourage authors to pull in other contributors to help wherever useful. We specifically suggest that only issues-for-leads and proposals be _exhaustively_ summarized. Whoever is creating these summaries should use their own judgement to filter to the most relevant things to summarize while trying to catch things that might otherwise be missed. As this is imperfect, we will also create some specific ways that individuals can help surface a specific thing they would like to see included in these. #### Discord channel to surface interesting topics We would like an easy way for folks to flag things that are interesting to include into the summaries, especially PRs that might otherwise be missed. We suggest one or both of two options far flagging these: 1. Create a dedicated discord channel that folks can post links / snippets to that they find interesting. 2. Create a discord channel that automatically gets bot postings for each PR that is merged, and encourage folks to use reactions to signal interest in that being included in the summary. ### Discussion / demo session every two months The goal is to provide an easy way for a much wider audience to follow along with Carbon's development and get to see interesting details about how that development is progressing. We suggest starting this off with a brief overview of project progress since the previous session, but keep this overview _very_ brief: strictly less than 10 minutes of the meeting. Then we suggest soliciting a specific "demo" of something recent and interesting in the project. These can take a wide variety of forms, but some primary ones we anticipate initially: - Demo of recently working functionality in the toolchain (or a recent & interesting bug). - "Demo" of our design process by selecting a current and relevant discussion topic and holding an initial discussion on that topic live for folks to observe and participate if interested. We will explicitly encourage folks to persistently ask questions for context and background they don't understand, as answering these questions is an effective informal way of spreading basic understanding of Carbon as it develops. The goal is not to resolve questions about the design or decide anything in these sessions, but to give a wider audience an opportunity to see and join some aspects of our development process periodically while also getting a very brief update on the overall project status. Initially we suggest scheduling these for every two months with the goal of aligning them to newsletters. However, this is not a strict scheduling goal and we should consider adjusting frequency as needed. We should also reschedule as needed to maximize folks' availability and freely skip any where we don't have a good demo ready-to-hand. ## Rationale - [Community and culture](/docs/project/goals.md#community-and-culture) - Creates a more timezone and working schedule inclusive way to track Carbon progress week-over-week. - Creates a more easily accessed synchronous meeting by having it be scheduled infrequently to minimize schedule disruption. - Makes the synchronous meeting a more welcoming and engaging environment by focusing on a relevant topic and using a structure that lets people engage in different ways. ## Alternatives considered ### Keep the existing structure We could make no change, but there has been a consistent lack of fully utilizing the current meeting structure and so it seems like some change is motivated based on how people are engaging. The current structure is also especially expensive for the team, and so it seems important to at least explore some alternatives that may open up better scaling, either in team size or timezones. ### Drop the discussion sessions entirely A different approach would be to completely drop having a synchronous discussion meeting. However, this would lose one of the historically impactful aspects of the weekly meeting that we hope to recover with this approach: telegraphing to the broader potential Carbon community (largely the existing C++ community) how we are going about developing Carbon. We have had several instances of specifically positive feedback from observers or more casual participants in historical weekly meetings where deeper design discussions took place or where we showcased specific issues or capabilities. However, these kinds of topics are not always available and so not something we can sustain at the pace of every week. The proposal picks the alternative of reducing the frequency and adjusting the structure of the synchronous meeting to try and capture this value at a much more sustainable cost and with a larger and more consistent audience. If the audience doesn't materialize or we get feedback that this impact isn't landing, we should revisit this aspect of the structure. ### Drop the weekly summaries entirely This would lose the main value that we currently get from the weekly meeting, and one that many across the team have repeatedly cited: both a way to catch up and a way to avoid missing discussions happening in a forum or at a time that they missed. Keeping this, and making it _even more_ asynchronous in nature, further our goal of making the Carbon development processes and community open and welcoming of people from different locations, working on different schedules, or with different levels of involvement. ### Select a slightly different format or structure There is a wide range of possible formats and structures for both weekly summaries and a discussion meeting. This proposal picks two based on trying to closely fit the distinct goals and value propositions of each component of our historically weekly meeting. But there are many others that could potentially be considered. Currently, the suggestion is to try these out and iterate on these based on the experience. When making this significant of a change it seems hard to predict accurately how the details will land, and simply selecting a plausible starting structure is more important than getting the _best_ structure. We should make minor adjustments to both as needed based on our experience with the new format. ================================================ FILE: proposals/p5270.md ================================================ # Move explorer out of toolchain git repository [Pull request](https://github.com/carbon-language/carbon-lang/pull/5270) ## Table of contents - [Abstract](#abstract) - [Problem](#problem) - [Background](#background) - [Proposal](#proposal) - [Rationale](#rationale) - [Alternatives considered](#alternatives-considered) - [Use a branch to refer to the explorer codebase](#use-a-branch-to-refer-to-the-explorer-codebase) - [Do nothing](#do-nothing) ## Abstract The explorer codebase is no longer actively developed or maintained, but retains value as a place to look for implementations of various parts of the design. Having both the explorer and toolchain in the working git repository causes confusion for developers and users, and increases our incremental maintenance burden. We will move explorer out of the working git repository so that it remains accessible but is clearly deliniated from the toolchain and is more clearly frozen or archived. ## Problem Users have expressed confusion when they come across examples or tests for the explorer that involve `Main()`, whereas the design and implementation of the toolchain have moved on to `Run()` as the entry point. Developers have attempted to update the explorer codebase to fix style guide issues[^1], as tools like grep or clang-tidy do not differentiate our active toolchain codebase from the frozen explorer one. As we update Bazel, Clang, Clang-tidy, etc, we also have to update the explorer codebase to keep it building cleanly. As we modify the `//testing` harness, we have to accommodate and work around the explorer tests[^2][^3][^4]. [^1]: https://github.com/carbon-language/carbon-lang/pull/5224 [^2]: https://github.com/carbon-language/carbon-lang/pull/4979 [^3]: https://github.com/carbon-language/carbon-lang/pull/5025 [^4]: https://github.com/carbon-language/carbon-lang/pull/5036 ## Background - Previous proposal: [#3532: Focus implementation effort on the toolchain](https://github.com/carbon-language/carbon-lang/blob/trunk/proposals/p3532.md) - Kick-off of this discussion [#5224](https://github.com/carbon-language/carbon-lang/pull/5224#pullrequestreview-2730512195) - Discord discussion: [2025-04-02 #explorer](https://discord.com/channels/655572317891461132/763516049710120960/1356979197070803095) ## Proposal 1. Add a tag `explorer-archived` in the main `carbon-lang` git repository. 2. Create a new `explorer` repository under the `carbon-language` organization that only contains the `//explorer` and `//installers` directories and their dependencies at head. 3. Locally ensure the explorer tests build and pass under the `explorer` repository. 4. Add a `README.md` to the `explorer` repository that explains explorer is archived and not under active development. 5. Stop building, or remove the "Explorer (trunk)" compiler option from [carbon.compiler-explorer.com](https://carbon.compiler-explorer.com). 6. Delete `//explorer` and `//installers` in the main `carbon-lang` repository. 7. Archive the `explorer` repository in GitHub, making it read-only. Note that fuzzer test cases from the explorer are already relocated under `//toolchain/*/fuzzer_corpus/`. ## Rationale The primary purpose of the explorer codebase at this time is as a demonstration of past work implementing the carbon language design. This can help to inform the implementation of the toolchain as it catches up in various areas, as long as the design has not deviated from the explorer implementation. Searching in, and providing links to the explorer codebase comes up in design discussions occasionally [^5][^6][^7][^8][^9][^10], and we should maintain the ability of toolchain developers to look through the explorer easily. The primary place they do so is on GitHub, where the code can be linked to. GitHub search only works [on the main branch](https://docs.github.com/en/search-github/github-code-search/understanding-github-code-search-syntax#using-qualifiers) of a repository, so the `trunk` branch for the `carbon-lang` repository. To maintain searchability, the explorer codebase must either remain on `trunk` in `carbon-lang` or in a sibling repository. GitHub search does continue to work in archived repositories. Proposal [p3532](https://github.com/carbon-language/carbon-lang/blob/trunk/proposals/p3532.md) directed to keep the explorer codebase active in the main repository, with its tests building and running: > We should keep the Explorer's code in place, building, and passing its basic > regression tests because the built artifacts of the Explorer remain really > valuable given its coverage of our design's feature sets. However the [problems](#problem) discussed above result from this situation. We can gain the benefit of access to the codebase while reducing its impact on developers and users by moving it into a separate git repository. [^5]: https://discord.com/channels/655572317891461132/998959756045713438/1225116234199203860 [^6]: https://discord.com/channels/655572317891461132/998959756045713438/1237143981150830673 [^7]: https://discord.com/channels/655572317891461132/709488742942900284/1250577021474443376 [^8]: https://discord.com/channels/655572317891461132/748959784815951963/1255669935439482993 [^9]: https://discord.com/channels/655572317891461132/655578254970716160/1302033729761443963 [^10]: https://discord.com/channels/655572317891461132/941071822756143115/1349523309682753606 ## Alternatives considered ### Use a branch to refer to the explorer codebase We could create a branch in the `carbon-lang` repository that contains explorer, but delete it from `trunk`. This would also allow the code to be found and linked to, however GitHub search does not support searching in a branch. The main purpose of the explorer code while archived is for reading the implementation, and search is an important part of that. ### Do nothing In the future, we may want to restart development of the explorer codebase. At that time we would benefit from keeping the codebase building as we upgrade Bazel, Clang, Clang-tidy, etc. However we don't currently have the resources to build two implementations of the language, and there's no plan in our roadmaps to restart explorer development. So the cost of letting the explorer codebase become stale is outweighed by the costs of keeping it active. ================================================ FILE: proposals/p5337.md ================================================ # Interface extension and `final impl` update [Pull request](https://github.com/carbon-language/carbon-lang/pull/5337) ## Table of contents - [Abstract](#abstract) - [Problem](#problem) - [Background](#background) - [Proposal](#proposal) - [Details](#details) - [`extend require`](#extend-require) - [`extend impl as` with parameterized interfaces](#extend-impl-as-with-parameterized-interfaces) - [`extend impl as` restrictions](#extend-impl-as-restrictions) - [Name conflicts](#name-conflicts) - [Final `impl` priority](#final-impl-priority) - [Overlapping final `impl`s](#overlapping-final-impls) - [Using associated constants from `impl`s in a `final match_first`](#using-associated-constants-from-impls-in-a-final-match_first) - [`impl` selection algorithm](#impl-selection-algorithm) - [An `impl` that can never match is an error](#an-impl-that-can-never-match-is-an-error) - [Impl names for prioritization](#impl-names-for-prioritization) - [Rationale](#rationale) - [Alternatives considered](#alternatives-considered) - [Allow overlap between a non-final and final impl but only if no queries pick the non-final on the overlap](#allow-overlap-between-a-non-final-and-final-impl-but-only-if-no-queries-pick-the-non-final-on-the-overlap) - [Forbid overlap between final impls](#forbid-overlap-between-final-impls) - [Prioritize between final impls using type structure](#prioritize-between-final-impls-using-type-structure) - [Allow mixing final and non-final impls in the same `match_first` block](#allow-mixing-final-and-non-final-impls-in-the-same-match_first-block) - [No default `Self` in `require Self impls I`](#no-default-self-in-require-self-impls-i) - [Different rules for prioritizing between final impls](#different-rules-for-prioritizing-between-final-impls) - [Different `final match_first` associated constant rules](#different-final-match_first-associated-constant-rules) ## Abstract We make 5 changes: - Allow `require Self impls I` in an `interface` or `constraint` scope to omit the `Self`, so it can be written `require impls I`. - Rename `extend I` to `extend require impls I` in an `interface` or `constraint` scope. - Define `extend impl as I` and `extend final impl as I` in an `interface` scope to copy the members of `I` and define an `impl` of `I` in terms of the extending interface. - Allow a non-final `impl` to overlap a final `impl` as long as it isn't subsumed by the final `impl`. The final `impl` will be given priority on the overlap. - Allow `final` on a `match_first` block, used to declare overlapping final impls, which are required to be in a single file. These features work together to allow a form of interface extension where: - Types only need to `impl` the extending interface to also get an `impl` of the extended interface. - Multiple interfaces can extend the same interface. - An interface can extend multiple interfaces. ## Problem As part of investigating [leads issue #4566](https://github.com/carbon-language/carbon-lang/issues/4566), we discovered problems with using a single `impl` to implement multiple interfaces. [Proposal #5168 (Forward `impl` declaration of an incomplete interface)](https://github.com/carbon-language/carbon-lang/pull/5168) removed that, but this changed the experience of `impl` of an interface that uses `extend`. Consider two closely coupled interfaces, such as: ```carbon interface As(T:! type) { fn Convert[self: Self]() -> T; } interface ImplicitAs(T:! type) { extend As(T); } ``` With #5168, you can no longer just `impl` `ImplicitAs(T)` with something like ```carbon impl ThisType as ImplicitAs(ThatType) { fn Convert[self: Self]() -> ThatType { ... } } ``` Instead, two `impl` definitions are needed: ```carbon impl ThisType as As(ThatType) { fn Convert[self: Self]() -> ThatType { ... } } impl ThisType as ImplicitAs(ThatType) { } ``` This is additional friction, which will increase with the depth of the interface hierarchy, which we would like to eliminate. The suggestion in [proposal #5168](https://github.com/carbon-language/carbon-lang/pull/5168), is to support the `ImplicitAs` use case with a `final` blanket `impl` of the `As(T)` interface for any type that `impls ImplicitAs(T)`, as in: ```carbon interface As(T:! type) { fn Convert[self: Self]() -> T; } interface ImplicitAs(T:! type) { fn Convert[self: Self]() -> T; } final impl forall [T:! type, U:! ImplicitAs(T)] U as As(T) { fn Convert[self: Self]() -> T = U.(ImplicitAs(T).Convert); } ``` This means that types that `impl as ImplicitAs(T)` automatically get an `impl` of `As(T)`, and can't `impl as As(T)` themselves. However, there are some concerns: - There is significant ceremony to express this relationship. - These semantics are closer to what we expect users will think `extend` in an interface will do. - We still want a way to get the current meaning of `extend` in an `interface` scope for a few reasons: - This `final impl` requires the two interfaces to be defined in the same file, which won't always be possible. - A `final impl` doesn't give much flexibility for situations where you need more control, for example to [reuse an impl of a specific interface](/docs/design/generics/details.md#use-case-defining-an-impl-for-use-by-other-types). - Also the current `extend` matches the semantics of `extend` in a named constraint. - Having a `final impl` of `interface As(T)` currently makes it quite difficult to have other `impl`s of the same interface. On this last point, under the current rules the following example would be rejected: ```carbon class C(T:! type) { ... } interface I {} impl forall [T:! I] C(T) as As(T) { ... } ``` The issue is that this `impl` overlaps with the `final impl` of `interface As(T)` for any type `C(T)` that implements `ImplicitAs(T)`. This `impl` is rejected preemptively because it conflicts on the overlap, whether or not anything inhabits that overlap in the developer's program. This avoids conflicts only being discovered by some consuming library. ## Background - [Proposal #553](https://github.com/carbon-language/carbon-lang/pull/553) introduced interface extension - [Proposal #983](https://github.com/carbon-language/carbon-lang/pull/983) introduced `final impl` - [Proposal #2760](https://github.com/carbon-language/carbon-lang/pull/2760) introduced `require Self impls I` in an `interface` or `constraint` scope, replacing the previous spelling of `impl as I` from [proposal #553](https://github.com/carbon-language/carbon-lang/pull/553). - [Proposal #2868](https://github.com/carbon-language/carbon-lang/pull/2868) allowed an `impl` to overlap a `final impl` as long as agreed on the overlap. ## Proposal First, to make `require Self impls I` usage more consistent with `impl Self as I` (and since we will be using this more after this proposal), we allow the `Self` to be omitted. So: - `require Self impls I` ✅ allowed, same meaning as before - `require impls I` ✅ allowed, same as `require Self impls I` - `require T impls I` ✅ allowed, same meaning as before Next, since we are creating another way that an `interface` can `extend` another interface, we change the existing usage of `extend` from being an introducer of its own declaration, to a modifier on a `require` declaration. In this case, the only type that can be constrained is `Self`, since that is the type being extended. To match the restriction that an `extend impl` must be followed by `as` in `class` scope, `extend require` can only be followed by `impls` in `interface` scope: - `extend require Self impls I` ❌ forbidden, `Self` must be omitted since no other value allowed - `extend require impls I` ✅ allowed - `extend require T impls I` ❌ forbidden `extend require impls I` after this proposal has the same meaning as `extend I` did before this proposal, so: ```carbon interface A { fn F(); } interface B { extend require impls A; fn G(); } ``` is equivalent to: ```carbon interface A { fn F(); } interface B { require impls A; alias F = A.F; fn G(); } ``` We then add another way to use `extend` as a modifier in an `interface` scope. We define the meaning of `extend impl as I;` in an `interface J` scope to do two things: - Copy the members of `I` to form new members of `J`. - Define a blanket `impl` that anything that `impls J` also `impls I`, by forwarding to the corresponding members of `J`. These semantics are intended to be similar to `extend impl` in a `class`, except that the definition of the blanket `impl` is generated automatically (and `impl` without `extend` is not allowed in an interface). In this example: ```carbon interface I { let T:! type; fn F(); fn G(); } interface J { extend impl as I; fn H(); } ``` The definition of `interface J` is equivalent to: ```carbon interface J { let T:! type; fn F(); fn G(); fn H(); } impl forall [U:! J] U as I { let T:! type = U.(J.T); fn F() = U.(J.F); fn G() = U.(J.G); } ``` The keyword `final` can be added between `extend` and `impl` to make the generated `impl` definition `final`. For example, the `final impl forall [T:! type, U:! ImplicitAs(T)] U as As(T)` from [the "Problem" section](#problem) would be generated from this: ```carbon interface ImplicitAs(T:! type) { extend final impl as As(T); } ``` To allow a non-final `impl` to overlap a final `impl`, we say the final `impl` is prioritized over a non-final `impl` anytime they overlap. This addresses a [problem](#problem) caused by the fact that we don't support the negative constraints that would allow a developer to avoid the overlap -- and the only sound choice is to prefer the final `impl`. However, if a non-final `impl` can never be used because it is completely subsumed by a final `impl`, that is an error. To allow multiple interfaces extending the same interface, we also want to support a way to declare overlapping final `impl`s. We do this by listing the names of the overlapping `impl`s in a `final match_first` block instead of putting `final` on the `impl` declarations. Note this requires all of the final `impl`s to be in the same file. The `impl` generated by an `extend impl as Foo` in an interface `Bar` can be named as `impl Bar.(as Foo)`. This name can be used to put such an `impl` into a `match_first` block (or a `final match_first` block). This example inspired by [C++ iterator categories](https://en.cppreference.com/w/cpp/iterator) shows using these pieces together: ```carbon interface Iterator { fn Increment[addr self: Self*](); } interface InputIterator { let Element:! type; extend impl as Iterator; fn Get[addr self: Self*]() -> Element; } interface OutputIterator { let Element:! type; extend impl as Iterator; fn Set[addr self: Self*](x: Element); } // Makes both impls final and prioritizes them. final match_first { impl InputIterator.(as Iterator); impl OutputIterator.(as Iterator); } interface ForwardIterator { extend final impl as InputIterator; fn Copy[self: Self]() -> Self; fn Equal[self: Self](compare: Self) -> bool; } interface OutputForwardIterator { // Need to be careful with `Element`, see the // "Name conflicts" section. let Element:! type; extend final impl as ForwardIterator where .Element = Element; extend final impl as OutputIterator where .Element = Element; } class MyIntIterator { ... } // An impl of OutputForwardIterator also implements: // Iterator, InputIterator, OutputIterator, and // ForwardIterator. impl MyIntIterator as OutputForwardIterator { where Element = i32; fn Increment[addr self: Self*]() { ... } fn Get[addr self: Self*]() -> i32 { ... } fn Set[addr self: Self*](x: i32) { ... } fn Copy[self: Self]() -> Self { ... } fn Equal[self: Self](compare: Self) -> bool { ... } } ``` ## Details Note that `extend` being first, before `final` or `impl`, follows [our other uses of `extend`](https://github.com/carbon-language/carbon-lang/issues/995#issuecomment-1417391051). ### `extend require` Some notes and clarifications on `extend require`: - To be consistent with other uses of `extend`, we use the same name lookup rule: name lookup into a scope that extends other scopes first looks in that scope, and then if it finds nothing, it looks into all of the extended scopes, and the lookup is an error if there's more than one different result. Example: ```carbon interface A { fn F(); fn G(); fn H(); } interface B { fn G(); fn J(); fn K(); } interface C { extend require impls A; extend require impls B; fn F(); fn J(); } ``` This definition of `interface C` means `A` and `B` are required and name lookup into `C` for: - `F` finds `C.F`, hiding `A.F`; - `G` is ambiguous, due to the conflict between `A.G` and `B.G`; - `H` finds `A.H`; - `J` finds `C.J`, hiding `B.J`; - `K` finds `B.K`. Note that if we had two more interfaces: ```carbon interface D { fn G(); } interface E { extend require impls C; extend require impls D; } ``` then lookup into `E` for `G` is also ambiguous, since the ambiguous lookup into `C` for `G` still counts as a conflict with `D.G`. - If interface `B` has `extend require impls A`, then any `impl C as B` will require an `impl C as A`. We no longer support implementing the members of `A` in an `impl` of `B`, see [leads issue #4566](https://github.com/carbon-language/carbon-lang/issues/4566) and [proposal #5168](https://github.com/carbon-language/carbon-lang/pull/5168). - If we accept [the `extend api`/`extend alias` proposal #3802](https://github.com/carbon-language/carbon-lang/pull/3802), then `extend require impls A;` becomes equivalent to `require impls A;` plus `extend api A;` (or `extend alias A;`, depending on the syntax we choose). ### `extend impl as` with parameterized interfaces When using `extend impl as` in a parameterized interface, those parameters end up in the `forall` clause in the generated `impl`. For example: ```carbon interface PointerContainer(T:! type) { extend impl as Container(T*); } ``` generates this `impl`: ```carbon impl forall [T:! type, U:! PointerContainer(T)] U as Container(T*) { ... } ``` ### `extend impl as` restrictions `extend impl as` can only be used if the generated `impl` would be legal. This includes the [the orphan rule](/docs/design/generics/details.md#orphan-rule), so the interface or type argument must be in the same file. For example: ```carbon class BigInt { ... } interface IntLike { // Only valid in the same library as `ImplicitAs` // or `BigInt`. extend impl as ImplicitAs(BigInt); } ``` The [additional restrictions on final impls](/docs/design/generics/details.md#libraries-that-can-contain-a-final-impl) mean that `extend final impl as` can only be used in the same library as the interface being extended, not the type arguments. Similarly, the expression to the right of the `as` must correspond to a single interface, due to [leads issue #4566](https://github.com/carbon-language/carbon-lang/issues/4566) and [proposal #5168](https://github.com/carbon-language/carbon-lang/pull/5168). ### Name conflicts If two associated constants or functions have the same name in the two interfaces, this is an error since there is no way to assign values to both in an `impl` of the extending interface. Example with an associated function: ```carbon interface A { fn F(); } interface B { fn F(); // Name conflict with `F`. extend impl as A; } class C { impl as B { // Probably refers to the `F` explicitly declared in `B` -- // no way to implement the `F` that comes from `A`. fn F() { ... } } } ``` Example with an associated constant: ```carbon interface A { let T:! type; } interface B { let T:! type; // Name conflict with `T`. extend impl as A; } class C { impl as B { // Two different associated constants named `T` and no way // to distinguish them. where T = i32; } } ``` Notice that the rewrite to put the members of `A` into `B` would result in a conflict. Further, if there is only one member with that name after the rewrite in `B`, then there is no way to write the generated `impl` to set the member of `A` from the values of the associated constants in `B`. However, if the extending interface gives a value to the associated constant, there is no need to specify that value in an `impl`, so that is not an error. ```carbon interface A { let T:! type; fn F() -> T; } interface B { let T:! type; // Okay extend impl as A where .T = T*; } class C { impl as B { // `T` here is `B.T` where T = i32; // `F` here is `B.F` and `A.F`. fn F() -> i32*; } } ``` This is equivalent to: ```carbon interface A { let T:! type; fn F() -> T; } interface B { let T:! type; // Skip A.T due to conflict. fn F() -> T*; } impl forall [U:! B] U as A where .T = .(B.T)* { fn F() -> T = U.(B.F); } class C { impl as B { where T = i32; fn F() -> i32*; } } ``` This was used in the `OutputForwardIterator` example in [the "Proposal" section](#proposal). Name conflicts are not expected to generally be a problem in practice since this form of extension often requires the two interfaces to be defined in the same file. ### Final `impl` priority With the design prior to this proposal, a final `impl` can only overlap another (final or non-final) `impl` if they have the same definition on their overlap. This means that if there is a `final` blanket impl of an interface, as is generated by `extend final impl as` in the interface's scope, then other `impl`s of that interface may be overly restricted. This example: ```carbon package Core; interface As(T:! type) { fn Convert[self: Self]() -> T; } interface ImplicitAs(T:! type) { extend final impl as As(T); } ``` is equivalent to: ```carbon package Core; interface As(T:! type) { fn Convert[self: Self]() -> T; } interface ImplicitAs(T:! type) { fn Convert[self: Self]() -> T; } final impl forall [T:! type, U:! ImplicitAs(T)] U as As(T) { fn Convert[self: Self]() -> T = U.(ImplicitAs(T).Convert); } ``` Any other `impl` of `As(T)` is going to overlap unless we could establish that the type does not implement `ImplicitAs(T)`. However, in a generic context, we don't support (and don't intend to support) that sort of negative constraint. So there would be no way to prove the following `impl` of `As(T)` doesn't overlap the `final impl` of `As(T)`: ```carbon import Core; class X(T:! type) { impl as As(T); } ``` In particular, an unrelated library defining `MyType` could define `impl X(MyType) as ImplicitAs(MyType)`, which would create a conflict between the `impl` in `X(T)` and the `final impl` associated with `ImplicitAs(T)`. This motivates a change to the rules: we want to allow a non-final `impl` to overlap with a final `impl`. The question is what to do for queries in the overlap. Now consider this second example: ```carbon import Core; class Array(Element:! type) { ... } // Impl 1 impl forall [T:! type, U:! As(T)] Array(U) as As(Array(T)) { ... } // Impl 2 impl forall [T:! type, U:! ImplicitAs(T)] Array(U) as ImplicitAs(Array(T)) { ... } ``` This creates a conflict: 1. Impl 1 provides `impl Array(U) as As(Array(T))`. 2. Impl 2 provides `impl Array(U) as ImplicitAs(Array(T))`. 3. The [`Core` package defined at the beginning of this section](#final-impl-priority) has `final impl forall [T:! type, U:! ImplicitAs(T)] U as As(T)`. 4. Combining 2 and 3 provides another definition of `impl Array(U) as As(Array(T))` which is final. Impl 1 has a more specific [type structure](/docs/design/generics/details.md#overlap-rule), though. Furthermore, this example is realistic: - The final blanket `impl` defined in the `Core` package is the definition we expect to use. - We need impl 1 to support types `T` that only `impl` `As(U)` and not `ImplicitAs(U)`. - We need impl 2 to support types `T` that `impl` `ImplicitAs(U)`. - Carbon will define an implicit conversion from `i32` to `i64`, and it would be reasonable to ask if `Array(i32)` implicitly converts to `Array(i64)`, which would be a query in the overlap. Any generic code that sees the final `impl` can assume it applies whenever it is used. If we don't want to forbid this overlap situation (either when defining a non-final `impl` with a more-specific type structure or when doing an `impl` query in the overlap that selects the non-final one), the only sound choice is to use the final `impl`. And we don't want to give an error here since: - It would reduce expressiveness, since there isn't a good workaround to prevent the overlap. - It would reduce the ability to compose libraries. - We want to support examples like this interaction between `Array`, `As`, and `ImplicitAs`. Note that prioritizing the final `impl` over the non-final `impl` (even when the non-final `impl` has a preferred type structure) produces the desired result in the example above. By writing `final` we ensure that `Convert` in `ImplicitAs` always matches `Convert` in `As`, when they are both defined. Also note that this gives the desired behavior that this `final impl` of the `CommonTypeWith` interface: ``` final impl forall [T:! type] T as CommonTypeWith(T) where .Result = T {} ``` is prioritized as if it is more specialized than user-written `impl`s of `CommonTypeWith`, [as desired](https://github.com/carbon-language/carbon-lang/blob/trunk/docs/design/expressions/if.md#same-type). #### Overlapping final `impl`s The next question is what to do if there are overlapping final `impl`s. We considered options that allowed them to be in different files, but this led to prioritization that was inconsistent with the [type structure rules](/docs/design/generics/details.md#overlap-rule) for non-final `impl`s. In particular, the restrictions on [which libraries can define a final `impl`](/docs/design/generics/details.md#libraries-that-can-contain-a-final-impl) mean there are at most two files that can contain two final `impl` definitions that both match a particular (type, interface) query: the one defining the root type and the one defining the root interface. The only way to have the final `impl`s in different files is if there is a [blanket impl](/docs/design/generics/details.md#blanket-impl-declarations) of the interface in the file defining the interface and the other in the file defining the type. Generic code may only see the file with the interface, and since it is marked "final" will assume it is safe to use. As a result, the only sound solution is to prioritize the final blanket `impl` over the final `impl` that specifies the type, the opposite of how non-final impls prioritize using type structure. Furthermore, there would be queries that would match the final `impl` that specifies the type but would not select that `impl`, a property we would like to have. So we require that overlapping final `impl`s be declared in the same file, a restriction that the compiler can enforce due to the file limitations. > **Future work:** If we determine that we do want to support overlapping final > impls across the two files, we thought the second file could use > `extend final match_first` to say "these entries are notionally added to the > earlier `final match_first`. Within a file, we have two tools here we could use to select between the candidates: type structure and [`impl_priority`/`match_first` blocks](/docs/design/generics/details.md#prioritization-rule). (We will use the placeholder term of "`match_first` blocks" to refer to these without asserting how they will be spelled.) The decision here is to only use `match_first` blocks, not type structure to choose between overlapping final `impl` definitions, for a few reasons: - Type structure is needed to pick betewen `impl` defined in different files, which we are not allowing for final impls. - A `match_first` block is very explicit way of prioritizing, and with final `impl`s in particular it is helpful to be clear about the conditions where they won't be selected even when they match. With this decision not to use type structure to prioritize final `impl`s, it doesn't make sense to mix `final` and non-final `impl`s in the same `match_first` block. Furthermore, until the compiler sees the `match_first` block, we can't treat an `impl` as final since we don't know if another final `impl` will be appear earlier in the `match_first` block and used instead. Which leads to the decision to put the `final` modifier on the `match_first` block instead of the `impl` declaration, when it appears in a `match_first` block. Example: ```carbon // Compiler does not treat this as final, so it // won't use `A = i32`. impl forall [T:! Z] T as J { where A = i32; // ... } fn F(T:! Z) { // Can assume `T impls J`. // Can't assume `T.(J.A)` is `i32`. // If we were to mark the above `impl` as `final`, // this code would assume it is selected, and we // would have to poison the impl lookup to prevent // the following conflicting `impl` and // `match_first` declarations. } // Also not treated as final yet, so the compiler // won't use `A = bool`. impl forall [U:! Y] U as J { where A = bool; // ... } // Matching `bool` assignment to `J.A` as the // previous impl. impl forall [V:! W] V as J { where A = bool; // ... } final match_first { impl forall [U:! Y] U as J; impl forall [V:! W] V as J; impl forall [T:! Z] T as J; } // Now all are considered final, with the // understanding that `impl forall [T:! Z] T as J` // may not be used even when it matches. The // compiler can now tell if it can use `A = bool` // from `impl forall [U:! Y] U as J`. fn G(U:! Y) { // Can assume `U impls J` and `U.(J.A)` is `bool`. } fn H(V:! W) { // Don't know if `V impls Y`, so we don't know // which `impl` is selected. We use a symbolic // witness for `V` and `V.(J.A)` is unknown // (despite that it would be `bool` in either // case). } ``` > Note: `final match_first` was considered in > [open discussion on 2023-09-13](https://docs.google.com/document/d/1s3mMCupmuSpWOFJGnvjoElcBIe2aoaysTIdyczvKX84/edit?resourcekey=0-G095Wc3sR6pW1hLJbGgE0g&tab=t.0#heading=h.4r37mjr7c42h) > as a part of function overloading, and other times since. This leads us to these rules: - An `impl` is considered "final" if is declared with the `final` keyword modifier or if it is named in a `final match_first` block. - In the first case (a `final impl` declaration), that `impl` may not appear in any `match_first` block. - An `impl` may appear in at most one `match_first` block. - This is true whether or not the block is marked `final`. - Two final `impl`s in the same file must appear in the same `final match_first` block if they overlap, as determined by their type structure. #### Using associated constants from `impl`s in a `final match_first` As established by [proposal #5168](https://github.com/carbon-language/carbon-lang/pull/5168), concrete `impl` queries resolve to a concrete `impl` witness, with the associated constants from a single `impl` definition. A symbolic `impl` query can in some cases use the associated constants from a final `impl`. If an `impl` query matches an `impl` marked `final` (as opposed to one in a `final match_first`), then that `impl` will definitely be used. In that case, the associated constants from the `impl` can be used. If a symbolic query selects an `impl` from a `final match_first` block, though, that `impl`'s associated constants may only be used if it is the first that _could_ match in its `final match` block, as determined by the type structures of the `impl`s that appear earlier. Phrased another way, a query will use the associated constants of a final `impl` if it both matches and has no overlap with the type structure of any of the `impl`s that appear before it in its `final match` first block. For example: ```carbon interface L(T:! type) { let B:! type; } class C(T:! type) {} final match_first { // Can use the associated constants from this impl, // since it is the first, so nothing could match ahead // of it. impl forall [U:! Y] C(U) as L(bool) where .B = i32; // Queries that match this impl and can't match the // previous impl's type structure can use the // associated constants from this impl. In this case, // that happens if the self type (left of the `as`) of // the query can't match `C(?)`. impl forall [V:! W] V as L(bool) where .B = bool; // Can use the associated constants from this impl, // since any query that matches `T as L(i32)` won't // match the type structure of the earlier impls in // the `final match_first`, since they all have // `L(bool)` to the right of `as`. impl forall [T:! Z] T as L(i32) where .B = f32; } class D(X:! type) {} impl forall [X:! type] D(X) as W {} // D(X) as L(bool) uses the middle `impl forall` from // the `final match_first` so the return type is `bool`. // Note that we can conclude `D(X)` does not match the // type structure of the first `impl forall` of // `C(?) as`... even though the query is not concrete. fn F(X:! type) -> (D(X) as L(bool)).B { return true; } class E(T:! type) {} impl forall [T:! type] C(E(T)) as W {} // `C(E(T))` does impl `W`, so it impls `L(bool)`. But // since we don't know whether `E(T)` impls `Y`, we // don't know which impl it will ultimately use, and so // `(C(E(T)) as L(bool)).B` is unknown. ``` #### `impl` selection algorithm In conclusion, we prioritize final `impl`s over non-final `impl`s. In the case that there are multiple matching final `impl`s defined in the same file, they overlap so they must be in a single `final match_first` block. In that case, pick the first matching one listed in the `final match_first` block. If no final `impl` declarations match the query, we fall back to the original rules: - The non-final `impl` declarations matching the query with the most preferred type structure must be in the same non-final `match_first` block, or a single non-final `impl` declaration not in a `match_first` can match. - In the latter case, that `impl` is selected. Alternatively, `impl` declarations not in any `match_first` block could be considered to be the only member of their own `match_first` block. - If a non-final `match_first` block is chosen, the first matching `impl` in that block is selected. Since it may be observable which `impl` declarations are considered during impl lookup, due to the [acyclic rule](/docs/design/generics/details.md#acyclic-rule) and [termination rule](/docs/design/generics/details.md#termination-rule), we specify this more precisely: - Only (final or non-final) `impl`s with a type structure compatible with the query are considered. - Final `impl`s are considered in the order they appear in the `final match_first` block, if any. - The first matching final `impl` considered is returned, skipping the remaining steps. - Non-final `impl`s are considered most specific type structure first. - Non-final `impl`s with the same type structure are considered in the order they appear in their common `match_first` block. - Once the first matching non-final `impl` is found: - If it is not in a `match_first` block, it can be returned, skipping the remaining steps. - The non-final `impl`s earlier in the same `match_first` block with compatible type structure are considered in the order they appear in the `match_first` block - We skip those with an equal or preferred type structure compared to the matching `impl`, since those have already been considered and determined to not be matching. - The first matching `impl` is selected. If no `impl` matches before the one used to select the `match_first` block, that `impl` is selected and no later `impl`s will be considered. #### An `impl` that can never match is an error Instead of the current rule that prevents a (final or non-final) `impl` from overlapping an existing final `impl`, we have a replacement rule that says a (final or non-final) `impl` that will never be selected due to being subsumed by a final `impl` is an error. The compiler can detect whether an `impl` declaration is subsumed by performing an `impl` lookup (restricting to final impls) for the query represented by its declaration (ignoring any trailing `where` clause setting associated constants). For the declaration `impl forall [T:! I] X(T) as J where .A = bool`, as an example, the query would be "`X(T) impls J` with `T impls I`". Note that [the rules for which libraries can define a final `impl`](/docs/design/generics/details.md#libraries-that-can-contain-a-final-impl) mean that a subsuming final `impl` is either visible or is later in the same file (and such will be diagnosed due to poisoning when we look for a subsuming final `impl`). In order to detect the subsumption, the query for the final `impl` should either be delayed to the end of the file, or poisoned. > _**Concern**_: Consider a situation where a library defines a broad final > `impl`, but has determined it is a bad idea and wants to remove it in the > future. One strategy would be to ask clients of the library to define their > own non-final `impl`s first. This non-final `impl` would be ignored before the > transition since the final `impl` has higher priority, but would be used once > the final `impl` is removed. In this case, we would like to suppress the error > for the subsumed non-final `impl` defined in the client. This is future work, > but our current idea is to associate the final `impl` with some build > configuration constant to mark it as conditionally available. A final `impl` > marked in that way would not be considered to subsume any `impl` not marked > the same way. This could also be used to allow temporary changes made while > debugging. ### Impl names for prioritization The generated `impl` from an `extend impl` in an interface is given a name similar to an `impl` defined in a `class` scope, as [discussed on 2024-03-11](https://docs.google.com/document/d/1s3mMCupmuSpWOFJGnvjoElcBIe2aoaysTIdyczvKX84/edit?resourcekey=0-G095Wc3sR6pW1hLJbGgE0g&tab=t.0#heading=h.p69b78lovqb7), mentioned in [proposal #3763](/proposals/p3763.md#redeclarations), and proposed in pending [proposal #5366](https://github.com/carbon-language/carbon-lang/pull/5366). For example, these names can be used to allow multiple interfaces to extend a single interface: ```carbon interface A(T:! type) { fn F() -> T; } interface B { extend impl as A(i32); // Produces impl with name `B.(as A(i32))`. } interface C1 { let U:! type; extend impl as A(U); // Produces impl with name `C1.(as A(U))`. } interface C2 { let U:! type; // Only difference from C1 is using `Self.U` instead of `U`. extend impl as A(Self.U); // Produces impl with name `C2.(as A(Self.U))`. } interface D(V:! type) { extend impl as A(V); // Produces impl with name `D(V:! type).(as A(V))`. } interface E {} impl forall [W:! E] W as A(W) { ... } class F {} impl F as A(F) { ... } // Placeholder impl priority syntax match_first { impl B.(as A(i32)); impl C1.(as A(U)); impl C2.(as A(Self.U)); // Can't write `impl C1.(as A(Self.U))` or `impl C2.(as A(U))`. impl D(V:! type).(as A(V)); impl forall [W:! E] W as A(W); impl F as A(F); } ``` Note how the expression after the `as` has to match syntactically between the two declarations. Without the `impl` prioritization, these would be conflicting `impl`s of the interface `A`. Note that by the [overlapping final `impl` rules](#overlapping-final-impls), at most one interface can `final extend impl as` a given interface. `final match_first` must be used instead of multiple `final impl`s, so that there is an explicit prioritization between them, as in this example: ```carbon interface I { fn IFn(); } interface J { // Can't mark as `final` here. extend impl as I; fn JFn(); } interface K { // Can't mark as `final` here. extend impl as I; fn KFn(); } // Makes both impls final and prioritizes them. final match_first { impl J.(as I); impl K.(as I); } ``` When the extending interface is parameterized, following the approach of [proposal #3763: Matching redeclarations](https://github.com/carbon-language/carbon-lang/pull/3763), the same sequence of tokens (between the `interface` and `{`) is used to name the interface, allowing a syntactic match. For example: ``` interface Z(T:! type) { ... } interface Y(T:! type) { extend impl as Z(T*); } // ... final match_first { impl Y(T:! type).(as Z(T*)); // ... } ``` ## Rationale This proposal's end goal is to support a form of interface extension that matches user's expectations, to aid the ["Code that is easy to read, understand, and write" goal](/docs/project/goals.md#code-that-is-easy-to-read-understand-and-write). This goal also informed the choice to be explicit about prioritizing overlapping final impls. The choice to not treat an `impl` as final when it overlaps another final impl until we see how to choose between them on the overlap is in accord with the [information accumulation principle](/docs/project/principles/information_accumulation.md). ## Alternatives considered ### Allow overlap between a non-final and final impl but only if no queries pick the non-final on the overlap This question was considered in the ["final `impl` priority" section](#final-impl-priority). ### Forbid overlap between final impls The downside of allowing them to overlap is we no longer have the property that a final `impl` is selected anytime it matches. This puts limits on when the compiler can use the values of associated constants from a final `impl`. However, the use cases, such as having multiple interfaces extend a single interface, were compelling. ### Prioritize between final impls using type structure This question was considered in the ["Overlapping final `impl`s" section](#overlapping-final-impls). ### Allow mixing final and non-final impls in the same `match_first` block There were some ways we could imagine supporting a mix of final and non-final impls in the same `match_first` block. With [the decision to not use type structure to prioritize between final impls](#prioritize-between-final-impls-using-type-structure), though, reconciling the differences seemed like it would lead to a lot of complexity and surprising behavior, like priority inversions. We thought the approach of putting `final` on the `match_first` block resulted in simpler rules. ### No default `Self` in `require Self impls I` Shortening of `require Self impls I` to just `require I` was considered in [proposal #2760](p2760.md#allow-interfaces-to-require-another-interface-without-writing-self-impls). It was not chosen to be consistent with `where` clauses, but the door was left open if it was found to be too verbose. The `require impls I` approach adopted by this proposal, though, could be extended to work with `where` as well. The consideration of that change has been left to future proposals, and is out of scope for this one. ### Different rules for prioritizing between final impls We considered five options in discussions in [a GitHub comment on this proposal](https://github.com/carbon-language/carbon-lang/pull/5337/files#r2072024118), [#generics-and-templates on Discord](https://discord.com/channels/655572317891461132/941071822756143115/1367982721141440734) on 2025-05-02, and [open discussion on 2025-05-05](https://docs.google.com/document/d/1Yt-i5AmF76LSvD4TrWRIAE_92kii6j5yFiW-S7ahzlg/edit?tab=t.0#heading=h.g213t2menkod). 1. Final `impl`s in the file with the interface are prioritized over the final `impl`s in the file with root self type of the `impl` (when they are different). 2. Blanket final `impl`s with a `?` for the whole self type in the type structure are prioritized over final `impl`s with a concrete root self type. 3. Final `impl`s are prioritized using the reverse of type structure prioritization. 4. The syntax of final `impl`s associates them with either the interface (`final match_first`) or the root self type (`extend final match_first`). Those associated with the interface are prioritized first, and then those associated with the type are prioritized after. Ties are resolved by type structure and then `match_first`. 5. Final `impl` overlap is only allowed within a `final match_first` block. Note: Each final `impl` [must be declared either in the file with the interface or the root type](/docs/design/generics/details.md#libraries-that-can-contain-a-final-impl). The concern with option 1 was that concatenating files that contain overlapping final `impl`s, such as when creating a reproduction of a bug, would necessitate `match_first` changes. The concern with option 2 was that it was a new rule to learn, unrelated to the rule for non-final impls. It has the advantage of using explicit `match_first` to prioritize almost as much as option 1, while allowing files to be concatenated with fewer changes. The concern with option 3 was that it might not do what developers expect, due to being the opposite of non-final `impl` prioritization. In general, options 1-4 were more complex than option 5. Option 5 maximized the use of explicit `match_first` to resolve priority on final `impl` overlap, rather than an implicit prioritization. We were also interested these two properties: - If we have two impls A and B where A is preferred over B, and then we make them both final, we should not make them valid but with B preferred over A. - A final `impl` is selected for any query that it matches. which options 1-3 did not satisfy. The main concern with option 5 was that it might not be able to express desired use cases. We noted, though, that you could create additional interfaces to support different prioritization policies. For example: ```carbon interface ImplicitAs(T:! type) { ... } interface HighPriorityImplicitAs(T:! type) { ... } interface LowerPriorityFrom(U:! type) { ... } final match_first { impl forall [T:! type] T as ImplicitAs(T); impl forall [T:! type, U:! HighPriorityImplicitAs(T)] U as ImplicitAs(T); impl forall [U:! type, T:! LowerPriorityFrom(U)] U as ImplicitAs(T); } ``` Notes: - This gives control over which type is given special treatment as the self type when implementing one of the auxiliary added interfaces. - This `final match_first` is with the interfaces, but that does not preclude a `final impl` of `HighPriorityImplicitAs` or `LowerPriorityFrom` in the files with the self root types. ### Different `final match_first` associated constant rules We considered [in discussion on 2025-05-06](https://docs.google.com/document/d/1Yt-i5AmF76LSvD4TrWRIAE_92kii6j5yFiW-S7ahzlg/edit?tab=t.0#heading=h.mygxcb6k4wpm) a couple of alternative rules for determining when associated constants were considered to be known, and what value to use, when matching an `impl` from a `final match_first`. One approach was to say that the `final match_first` would be allowed to have a `where` clause specifying the value of a subset of the associated constants of the interface. Every `impl` named in the body would have to have a consistent assignment to those associated constants in their definitions. This had two downsides: - It did not support the anticipated overloading use cases well, where the return type would vary across overloads. - Supporting associated constants whose value varied with the value of a type or interface parameter would have added a lot of complexity. Another approach we considered was to say an associated constant would have a value if every impl that could match gave the same value. For example, in [the first example from the "Overlapping final `impl`s" section](#overlapping-final-impls), the first two impls in the `final match_first` both set `A.J` to `bool`, so anything matching either of them could use `bool` for the value of `A.J`. This seemed hard to reason about. Also, there were concerns that this would be very sensitive to changes, introduce additional fragility in the case of evolution. We also considered only using associated constants that were included in the `impl`'s name (using a `where` clause in the facet type instead of a `where` declaration in the `impl` definition body). This had the advantage that the logic for determining the values of those associated constants could be done without the `impl` definitions. This would introduce implementation complexity, and would cause `impl` names to be longer increasing verbosity. It would also introduce a difference between a `final impl` and a single `impl` by itself inside a `final match_first`. ================================================ FILE: proposals/p5366.md ================================================ # The name of an `impl` in `class` scope [Pull request](https://github.com/carbon-language/carbon-lang/pull/5366) ## Table of contents - [Abstract](#abstract) - [Problem](#problem) - [Background](#background) - [Proposal](#proposal) - [Details](#details) - [Optional `Self` before `as`](#optional-self-before-as) - [Rationale](#rationale) - [Alternatives considered](#alternatives-considered) - [Use semantic match for the scope](#use-semantic-match-for-the-scope) ## Abstract ``` class C { impl as I; } ``` is redeclared ``` impl C.(as I) ``` for purposes of `match_first`/`impl_priority` blocks and definitions. ## Problem An `impl` declaration can be declared in `class` scope: ``` class C { alias T = bool; impl as As(T); } ``` or in file scope: ``` class C { alias T = bool; } alias T = i32; impl C as As(T); ``` These `impl` declarations need to be named so they may be redeclared in a definition or [`match_first`/`impl_priority` block](/docs/design/generics/details.md#prioritization-rule). Under the current rules introduced in [proposal #1084](https://github.com/carbon-language/carbon-lang/pull/1084) and modified in [#3763](https://github.com/carbon-language/carbon-lang/pull/3763), an `impl` redeclaration must match syntactically, and that only works if the redeclaration enters the same scope as the original declaration. This problem is demonstrated in the example above. We need some indication whether to lookup `T` in the file or the class scope, otherwise these both would be redeclared as `impl C as As(T)`. ```carbon class C(T:! type) { class E {} impl E as I(C(T), E); } // No ability to do syntactic match // C(T) does not match C(T:! type) // C(T).E does not match E impl forall [T:! type] C(T).E as I(C(T), C(T).E); ``` ## Background The need for forward declarations of entities comes from the [information accumulation principle](/docs/project/principles/information_accumulation.md). [Leads issue #1132](https://github.com/carbon-language/carbon-lang/issues/1132) defined the initial rules for matching forward declarations to their definitions. Those rules were partially incorporated into the design by [proposal #1084, "Generics details 9: forward declarations"](/proposals/p1084.md). A replacement approach was [discussed on 2024-03-11](https://docs.google.com/document/d/1s3mMCupmuSpWOFJGnvjoElcBIe2aoaysTIdyczvKX84/edit?resourcekey=0-G095Wc3sR6pW1hLJbGgE0g&tab=t.0#heading=h.p69b78lovqb7) and mentioned in [proposal #3763](/proposals/p3763.md#redeclarations). This is the approach of syntactic matching and re-entering the same scope. The syntax adopted by this proposal was first suggested in those. [Proposal #3762: Merging forward declarations](https://github.com/carbon-language/carbon-lang/pull/3762) and [proposal #3980: Singular `extern` declarations](https://github.com/carbon-language/carbon-lang/pull/3980) refined the rules for forward declarations, including the rules for `extern` declarations. [Leads issue #5251: `impl` declarations in a generic class context](https://github.com/carbon-language/carbon-lang/issues/5251) is (pending resolution) saying that an `impl` declaration in class scope must use `Self` in a deducible position. ## Proposal An `impl` declaration is associated with the scope it is first declared in, and can only be redeclared in that scope, matching all other declarations. Consider how a function is redeclared outside the scope of a class in which it was originally defined: ``` class Z(T:! type) { // Forward declaration of a function. fn F(); } // Definition of the function that was forward declared. fn Z(T:! type).F() { ... } ``` To redeclare an `impl` after the end of the scope it was declared in, that scope may be re-entered as part of the `impl` redeclaration, in the same way, except with parentheses around the name of the `impl`, as in: ```carbon class X { // Forward declaration that `X impls Y`: impl as Y; } // Definition of the `impl` that `X impls Y` // that was forward declared in `X`: impl X.(as Y) { ... } ``` More generally, in a `class` scope ```carbon class __X__ { impl __Y__; } ``` is redeclared `impl __X__.(__Y__)` outside of that `class` scope. Here `__X__` is whatever sequence of tokens appears in that position in the `class` declaration, and `__Y__` is the sequence of tokens in the `impl` declaration. These declarations are matched syntactically, and anything in `__Y__` is interpreted as if it appeared in the scope of `__X__` like it was first declared. ## Details Here are some examples of this in practice: ```carbon class F {} class A { impl as As(i32); impl Self as As(bool); impl A as As(f64); impl F as As(A); class G {} impl G as As(A); } impl A.(as As(i32)) { ... } impl A.(Self as As(bool)) { ... } impl A.(A as As(f64)) { ... } impl A.(F as As(A)) { ... } impl A.(G as As(A)) { ... } ``` Parameterized classes: ```carbon class B(T:! type) { impl B(i32) as AddWith(A(T)); } impl B(T:! type).(B(i32) as AddWith(A(T))) { ... } ``` Parameterized impl: ```carbon class C { impl forall [T:! type] as I(T); } impl C.(forall [T:! type] as I(T)); ``` Putting the `forall` inside the parens both simplifies the syntactic match and means that any mentions of names in that clause are in scope. For example: ``` class D(T:! type) { class E {} impl forall [U:! J(E)] as I(U); } impl D(T:! type).(forall [U:! J(E)] as I(U)); ``` Notice how the `E` in the constraint on `U` is found in the `D(T:! type)` scope. Nested classes: ``` class C1 { class C2 { class C3 { impl as I; class C4 {} } } } // Defining impl that was forward declared within the `C3` definition: impl C1.C2.C3.(as I) { ... } // Defining a new impl: impl C1.C2.C3.C4 as I { ... } ``` Notice that we don't know which form will be used until we see: - the open paren (`(`) after a `.`, - a parameter pattern, - a non-parameter argument, or - the `as`. ### Optional `Self` before `as` The normalization to add `Self` before `as` when that type is omitted before performing syntactic match is preserved from proposals [#1084](https://github.com/carbon-language/carbon-lang/pull/1084) and [#3763](https://github.com/carbon-language/carbon-lang/pull/3763). Note that the `Self` is inserted in the parentheses when the `as` appears there. So: ``` class A { // First impl declaration is equivalent // to `impl Self as As(i32);` impl as As(i32); // Second impl declaration. impl Self as As(bool); } // Redeclaration of the first impl declaration. impl A.(Self as As(i32)) { ... } // Since this is equivalent to // `impl A.(Self as As(bool)) { ... }`, is a // valid redeclaration of the second impl // declaration. impl A.(as As(bool)) { ... } ``` ## Rationale The need for this proposal comes from supporting forward declarations for the [information accumulation principle](/docs/project/principles/information_accumulation.md). The specifics of this proposal were chosen comply with these [Carbon goals](/docs/project/goals.md): - Unambiguous rules that are simple to implement benefit [language tools and ecosystem](/docs/project/goals.md#language-tools-and-ecosystem). - [Code that is easy to read, understand, and write](/docs/project/goals.md#code-that-is-easy-to-read-understand-and-write) benefits by having rules that are simple to state and validate. The syntactic match rule means redeclarations are close to a copy of the original declaration, which makes authoring straightforward and automatable. ## Alternatives considered ### Use semantic match for the scope [Leads issue #5367: `impl` in `class` redeclaration syntax with parameterization](https://github.com/carbon-language/carbon-lang/issues/5367) considered an alternative where ```carbon // Alternative impl forall [T:! type] B(T).(as I) { ... } ``` instead of: ```carbon // This proposal impl B(T:! type).(as I) { ... } ``` It avoided putting a `forall` clause inside the parentheses, but that both greatly limited the syntactic matching that we could do, and meant examples like: ```carbon // This proposal impl D(T:! type).(forall [U:! J(E)] as I(U)); ``` had to instead be written with more qualfiers: ```carbon // Alternative impl forall [T:! type, U:! J(D(T).E)] D(T).(as I(U)); ``` It is not just helpful for the compiler: being able to make fewer and more mechanical changes after copy-pasting the `impl` declaration to make the redeclaration makes authoring the code easier, and simplifies tooling to automate the process. ================================================ FILE: proposals/p5434.md ================================================ # `ref` parameters, arguments, returns and `val` returns [Pull request](https://github.com/carbon-language/carbon-lang/pull/5434) ## Table of contents - [Abstract](#abstract) - [Problem](#problem) - [Background](#background) - [Proposal](#proposal) - [`ref` bindings](#ref-bindings) - [`ref` and `val` returns](#ref-and-val-returns) - [Details](#details) - [Compound return forms and patterns](#compound-return-forms-and-patterns) - [Nested binding patterns](#nested-binding-patterns) - [Mututation restriction on objects bound to a value](#mututation-restriction-on-objects-bound-to-a-value) - [No optimization on erroneous behavior](#no-optimization-on-erroneous-behavior) - [`bound` parameters](#bound-parameters) - [How addresses interact with `ref`](#how-addresses-interact-with-ref) - [Improved interop and migration with C++ references](#improved-interop-and-migration-with-c-references) - [Part of the expression type system, not object types](#part-of-the-expression-type-system-not-object-types) - [Interaction with `returned var`](#interaction-with-returned-var) - [Use case: `Deref` interface](#use-case-deref-interface) - [Use case: indexing interfaces](#use-case-indexing-interfaces) - [Use case: member binding interfaces](#use-case-member-binding-interfaces) - [Use case: class accessors](#use-case-class-accessors) - [Type completeness](#type-completeness) - [Pointer value representation](#pointer-value-representation) - [Future work](#future-work) - [Temporary lifetimes](#temporary-lifetimes) - [`ref` bindings in lambdas](#ref-bindings-in-lambdas) - [Interaction with effects](#interaction-with-effects) - [More precise lifetimes](#more-precise-lifetimes) - [Combining with compile-time](#combining-with-compile-time) - [Interaction with `Call` or other interfaces](#interaction-with-call-or-other-interfaces) - [Destructuring assignment](#destructuring-assignment) - [Restore `addr`](#restore-addr) - [Rationale](#rationale) - [Alternatives considered](#alternatives-considered) - [No `ref`, only pointers](#no-ref-only-pointers) - [Remove pointers after adding references](#remove-pointers-after-adding-references) - [Allow `ref` bindings in the fields of classes](#allow-ref-bindings-in-the-fields-of-classes) - [No call-site annotation](#no-call-site-annotation) - [Top-level `ref` introducer](#top-level-ref-introducer) - [Allow immutable value semantic bindings nested within variable patterns](#allow-immutable-value-semantic-bindings-nested-within-variable-patterns) - [Remove `var` as a top-level statement introducer](#remove-var-as-a-top-level-statement-introducer) - [`ref` as a type qualifier](#ref-as-a-type-qualifier) - [`bound` would change the default return to `val`](#bound-would-change-the-default-return-to-val) - [Other return conventions](#other-return-conventions) - [`return var` with compound return forms](#return-var-with-compound-return-forms) - [Other syntax for compound return forms](#other-syntax-for-compound-return-forms) - [`ref` parameters allow aliasing](#ref-parameters-allow-aliasing) - [`let` to mark value returns instead of `val`](#let-to-mark-value-returns-instead-of-val) - [`=>` infers form, not just type](#-infers-form-not-just-type) ## Abstract - A parameter binding can be marked `ref` instead of `var` or the default. It will bind to reference argument expressions in the caller and produces a reference expression in the callee. - Unlike pointers, a `ref` binding can't be rebound to a different object. - This replaces `addr`, and is not restricted to the `self` parameter. - A `ref` binding, like a value binding, can't be used in fields of classes or structs. - When calling functions, arguments to non-`self` `ref` parameters are also marked with `ref`. - The return of a function can optionally be marked `ref`, `val`, or `var`. These control the category of the call expression invoking the function, and how the return expression is returned. - These may be mixed for functions returning tuple or struct forms. - Any parameters whose lifetime needs to contain the lifetime of the return must be marked `bound`. - The address of a `ref` binding is `nocapture` and `noalias`. - We mark parameters of a function that may be referenced by the return value with `bound`. ## Problem Reference bindings have come up multiple times: - as a better alternative to `addr self: Self*`; - for use in [lambda captures](/docs/design/lambdas.md); - to model different kinds of C++ references for interop and migration; - to support nested bindings within a destructured `var`, see [issue #5250](https://github.com/carbon-language/carbon-lang/issues/5250) and [proposal #5164](https://github.com/carbon-language/carbon-lang/pull/5164); - for forwarding arguments while preserving [expression category](/docs/design/README.md#expression-categories); - to add a feature to pattern matching to modify things after they have been matched; - to support refactoring code without changing all the uses of a name, a problem we are already seeing with `self` and `addr self`, and would be a point of friction in local pattern matching in the future; and - to support breaking up an expression into pieces without altering the expression category of individual pieces. Reference returns have also come up before, particularly to support operators such as indexing `[`...`]` and other functions that should produce a reference expression. It is desirable, though, that this not introduce new memory unsafety concerns, due to returning a reference to something with insufficient lifetime. In addition, we have been interested in adding other return mechanisms that support returning values in registers in cases that our current convention won't. ## Background - Carbon has [reference expressions](/docs/design/values.md#reference-expressions). - Using [the `addr` keyword on mutating methods to get a `self` with a pointer type](/docs/design/classes.md#methods) was introduced in [proposal #722: "Nominal classes and methods"](/proposals/p0722.md#keyword-to-indicate-pass-by-address). - [Leads issue #5261: "We should add `ref` bindings to Carbon, paralleling reference expressions"](https://github.com/carbon-language/carbon-lang/issues/5261) supports adding `ref` bindings to Carbon. - [LLVM's `noalias` attribute](https://llvm.org/docs/LangRef.html#function-attributes) is used to mark a pointer as being aliased in only limited ways to enable optimization. Also see [LLVM's pointer aliasing rules](https://llvm.org/docs/LangRef.html#pointer-aliasing-rules). - Marking a pointer as not captured, to allow optimizations, was originally done with [LLVM's `nocapture` attribute](https://releases.llvm.org/11.0.0/docs/LangRef.html#parameter-attributes), which has become [`captures(none)` and `captures(ret: address, provenance)`](https://llvm.org/docs/LangRef.html#function-attributes), which is governed by [pointer capture rules](https://llvm.org/docs/LangRef.html#pointer-capture). - Clang allows C++ code to use the [`clang::lifetimebound` attribute](https://clang.llvm.org/docs/AttributeReference.html#lifetimebound) to mark parameters that may be referenced by the return value, in order to detect some classes of use-after-free memory-safety bugs. - [C++ has reference types](https://en.cppreference.com/w/cpp/language/reference). ## Proposal ### `ref` bindings We introduce a new keyword `ref`. This may be added to a `:` binding to mark it as binding to a reference expression, as in: ```carbon fn F(ptr: i32*) { // A reference binding `x`. let ref x: i32 = *ptr; // Use of `x` is a reference expression that // refers to the same object as `*ptr`. Assert(&x == ptr); // Equivalent to `*ptr += 1;`. x += 1; } fn G() { var y: i32 = 2; F(&y); Assert(y == 3); } ``` The use of the name (`x` in the example) of a `ref` binding forms a durable reference expression. We ensure that reference expressions formed by way of reference bindings _do not dangle_. A `ref` binding may only bind to a durable reference expression or an expression that can be converted to one. The bound durable reference expression must outlive the `ref` binding. The address of a `ref` bound name gives the address of the bound object, so `&x == ptr` above. The reference itself does not have an address, and unlike a pointer can't be rebound to reference a different object. We remove `addr`, and use instead use `ref` for the `self` parameter when an object is required. Note that the type will change from `Self*` to `Self` in this case. In the future, we might [re-add `addr` back if needed](#restore-addr). ```carbon class C { // ❌ No longer valid. fn OldMethod[addr self: Self*]() { // Previously would dereference `self` in // the body of the method. self->x += 3; } // ✅ Now valid. fn NewMethod[ref self: Self]() { // Now `self` is a reference expression, // and is not dereferenced. self.x += 3; } // ✅ Other uses are unchanged. fn Get[self: Self]() -> i32 { return self.x; } var x: i32; } ``` Potentially abbreviating the syntax further (to allow `ref self` as a short form of `ref self: Self`) is left as future work. The `ref` modifier is allowed on `:` bindings that are not: - inside a `var` pattern, - a field of a `class` type, or - a field of a struct type. ```carbon fn AddTwoToRef(ref x: i32) { x += 1; let ref y: i32 = x; y += 1; } // Equivalent to: fn AddTwoToRef(ref x: i32) { x += 1; let y_ptr: i32* = &x; *y_ptr += 1; } ``` We add support for `ref` and `var` in a [struct pattern](/docs/design/pattern_matching.md#struct-patterns) when using the shorthand `a: T` syntax for `.a = a: T`: ```carbon let {var a: i32, ref b: i32} = ...; // Now equivalent to: let {.a = var a: i32, .b = ref b: i32} = ...; ``` > Note: This takes us one step closer to `{` ambiguity. Previously we could > distinguish between a struct literal/pattern and a non-empty block with only > up to two tokens of lookahead (the struct cases start with `.` or `_` or > identifier followed by `:`, and the block cases don't). Now we have things > like: > > ```carbon > fn F() -> X { var a: i32 = 0; } > ``` > > ... where we're getting incrementally closer to ambiguity. We've got a few > more steps before we get there, though, since we don't have an `X{...}` > expression yet, and `var ...` is only allowed in struct patterns rather than > struct expressions. So we're still fine, but this is cutting down our options > for future syntactic expansion a little. The `ref` modifier is forbidden on the bindings in `class` or struct type fields. ``` var outer_size: i32 = 123; class Invalid { // ❌ Invalid. We don't currently have runtime `let` bindings in classes, // or `ref` on `var`s, but the intent is to not have `ref` bindings as fields. let ref invalid_ref_field: i32 = outer_size; } // ❌ Invalid. var invalid_struct_type_field: {ref .invalid: i32} = {.invalid = outer_size}; ``` In a function argument list, arguments to non-`self` `ref` parameters are also marked with `ref`. Continuing the example: ```carbon var z: i32 = 3; AddTwoToRef(ref z); Assert(z == 5); // No `ref` though on the `self` argument. var c: C = {.x = 4}; c.NewMethod(); Assert(c.Get() == 7); ``` > **Note:** It is important that this restriction is syntactic, not just > semantic, because it means that `ref` is never the first token of a full > expression, and so we know without lookahead that a `ref` in a pattern context > must be the start of a binding pattern, not the start of an expression > pattern. Normally an argument to a non-`ref` parameter should not be marked `ref`, but it is allowed in a generic context where the parameter may sometimes be `ref`. Expression operators will mostly not take `ref` parameters, with these exceptions: - [the address-of operator](/docs/design/expressions/pointer_operators.md) `&`; - the first operand of [the indexing operator](/docs/design/expressions/indexing.md) `[`...`]`; and - [the member access operator](#use-case-member-binding-interfaces) introduced in [proposal #3720: "Member binding operators"](https://github.com/carbon-language/carbon-lang/pull/3720) `.`. The statement operators now use `ref` instead of pointers: - the left-hand operand of [assignment operators](/docs/design/assignment.md) such as `=` and `+=`. - [the `++` and `--` operators](/docs/design/assignment.md). Even in these cases, the arguments will not be marked with `ref` at the call site. (Generally the `ref` parameter is the `self` parameter, and so wouldn't be marked. The exception is `BindToRef`, but we don't want to mark its argument with `ref`.) As an _experiment_, we are saying a pointer formed by taking the address of a `ref` bound name is LLVM-`captures(none)` and LLVM-`noalias`. This means that while a `ref` parameter could be passed into a function by address, the restrictions also allow a "move-in-move-out" approach (once we define the move operation), assuming it is not [marked `bound`](#bound-parameters). The intent here is to leave the door open to a calling convention using registers and less indirection for small-enough objects. This means that the following code is invalid: ```carbon fn F(ref a: i32, ref b: i32) -> bool; fn G() -> bool { var v: i32 = 1; return F(ref v, ref v); } ``` Enforcing this restriction will be part of the memory safety story. Until then, doing this is erroneous behavior. This [means](#no-optimization-on-erroneous-behavior) that the compiler won't use those LLVM attributes unless the compiler can itself prove that the restrictions hold. ### `ref` and `val` returns The return of a function can optionally be marked `ref` or `val`. These control the category of the call expression invoking the function, and how the return expression is returned. ```carbon var global: i32 = 2; fn ReturnRef() -> ref i32 { // ❌ Invalid: return 2; // ✅ Valid: return a reference expression with // sufficient lifetime. return global; } // Call `ReturnRef` and use the resulting reference. ReturnRef() += 3; Assert(global == 5); // Result of `ReturnRef` can be bound using a `ref` // binding. fn AddFive() { let ref r: i32 = ReturnRef(); r += 5; } AddFive(); Assert(global == 10); fn ReturnVal() -> val i32 { return 2; } // ReturnVal() is a value expression. let l: i32 = ReturnVal(); // Returning an initializing expression is the // default. fn ReturnDefault() -> i32 { return 2; } // ReturnDefault() is an initializing expression. var j: i32 = ReturnDefault(); // Use `var` to explicitly specify returning an // initializing expression. fn ReturnVar() -> var i32 { return 2; } // `ReturnVar()` is the same as `ReturnDefault()`. ``` - A call to a function declared `-> ref T` is a durable reference expression. The generated code for that function will return the address of a `T` object. - A call to a function declared `-> val T` is a value expression. The function will return the value representation of `T`. Since values have no address, the value representation may be returned in registers. - The behavior of a call to a function declared `-> T` is unchanged. It is an initializing expression, returning in place or by copy depending on the initializing representation of `T`. This is the same behavior as `-> var T`. - The behavior of `auto` as the return type is unchanged, but now supports an optional `ref`, `val`, or `var` between the `->` and `auto`. `-> auto` continues to return an initializing expression, as does `-> var auto`. `-> val auto` returns a value expression, and `-> ref auto` returns a durable reference expression. - Using `=>` to specify a return continues to return an initializing expression, as before. See [this relevant alternative considered](#-infers-form-not-just-type). A function may have multiple returns, each with their own marker, by using a tuple or struct compound return form. ```carbon fn TupleReturn() -> (val bool, ref i32, C) { return (true, global, {.x = 3}); } fn StructReturn() -> {.a: val bool, .b: ref i32, .c: C} { return {.a = true, .b = global, .c = {.x = 3}}; } ``` If the return of a function may reference the storage of one or more parameters to the function, those parameters must be marked `bound`. This allows the compiler to diagnose if the function's return is used after the lifetime of the `bound` parameter ends. The semantics of `bound` are intended to match the [`clang::lifetimebound` attribute](https://clang.llvm.org/docs/AttributeReference.html#id8). ```carbon fn Member(bound ref c: C) -> ref i32 { return c.x; } // Lifetime of a pointer includes the lifetime // of what it points to. fn Deref(bound p: i32*) -> ref i32 { return *p; } fn Both(bound pc: C*) -> ref i32 { return p->x; } fn Invalid1() -> ref i32 { var x: i32 = 4; // ❌ Error: returning reference to `x` // whose lifetime ends when this function // returns. return x; } fn Invalid2() -> ref i32 { var c: C = {.x = 1}; // ❌ Error: returning reference bound to `c` // whose lifetime ends when this function // returns. return Member(c) } ``` The address of a `bound ref` parameter is the [LLVM attribute `captures(ret: address, provenance)`](#background) instead of [`captures(none)`](#background). ## Details The intent is that we would encourage using references instead of pointers when possible. Their benefits are related to their limitations, so to get those benefits we should use them when a use is restricted enough to be within those limitations. ### Compound return forms and patterns Mirroring the [tuple](/docs/design/pattern_matching.md#tuple-patterns) and [struct](/docs/design/pattern_matching.md#struct-patterns) pattern forms, we also support tuple and struct return forms. ```carbon // `->` begins a "return form" fn F() -> ; // Within any return form, if the first token is // `val`, `ref`, `var`, `(`, or `{`, it is not // treated as type expression: // Value return, with type as specified fn Val() -> val // Reference return, with type as specified fn Ref() -> ref // Initializing return, with type as specified fn Var() -> var // Tuple compound return, with a list of // return forms. fn TupleCompound() -> ( , ... ) // Tuple return, with a list of type // expressions. Used if all members of the // list are type expressions. fn Tuple() -> ( , ... ) // Struct compound return, with a mapping from // designators to return forms. fn StructCompound() -> { .: , ... } // Struct return, used if all of the members // are type expressions. fn Struct() -> { .: , ... } // Otherwise, implicit `var` means returns // an initializing expression. fn Other() -> ``` Note that in the absence of `val`, `ref`, and `var` keywords, the implicit `var` is placed in the outermost position, minimizing the number of primitive forms returned. So `fn F() -> (i32, i32)` means `fn F() -> var (i32, i32)` not `fn F() -> (var i32, var i32)`. Generally the `var` is left off if not required, and so will be rare in return forms, to minimize confusion with `val`. ```carbon fn TupleReturn(...) -> (val bool, ref i32, C); // Equivalent to: // -> (val bool, ref i32, var C); let (a: bool, ref b: i32, var c: C) = TupleReturn(...); fn StructReturn(...) -> {.a: val bool, .b: ref i32, .c: C}; // Equivalent to: // -> {.a: val bool, // .b: ref i32, // .c: var C}; // Binds to the names `x`, `y`, `z`: let {.a = x: bool, .b = ref y: i32, .c = var z: C} = StructReturn(...); // Binds to the names `a`, `b`, `c`: let {a: bool, ref b: i32, var c: C} = StructReturn(...); // Above two can be mixed, binding to // names `a`, `y`, `z`. let {a: bool, .b = ref y: i32 .c = var z: C} = StructReturn(...); ``` Only types are allowed after a `-> val`, `-> ref`, or `-> var`, not a compound return form. Examples: ```carbon // Returns a tuple of type // `(bool, f32, C, i32)`. fn OneTupleReturn(...) -> (bool, f32, C, i32); // Returns a compound tuple form fn CompoundReturn(...) -> (bool, val f32); // Equivalent to: // -> (var bool, val f32); // ❌ Invalid, can't specify `ref` inside // of `val`. fn Invalid(...) -> val (bool, ref f32); ``` The compound return forms may be nested, as in: ```carbon fn CompoundInParens(...) -> ({.a: bool, .b: val f32}, C, ref i32); // Equivalent to: // -> ({.a: var bool, .b: val f32}, var C, ref i32); let ({.a = var x: bool, .b = val y: f32}, var c: C, ref d: i32) = CompoundInParens(...); // or without renaming: let ({var a: bool, b: f32}, var c: C, ref d: i32) = CompoundInParens(...); // Contrast with a compound tuple form containing // a struct type (not compound): fn StructInParens(...) -> ({.a: bool, .b: f32}, C, ref i32); // Equivalent to: // -> (var {.a: bool, .b: f32}, var C, ref i32); fn CompoundInBraces(...) -> {.a: bool, .b: (val f32, C), .c: ref i32}; // Equivalent to: -> {.a: var bool, .b: (val f32, var C), .c: ref i32}; let {a: bool, .b = (x: f32, var y: C), ref c: i32} = ParensInBraces(...); // Contrast with a compound struct form containing // a tuple type (not compound): fn TupleInBraces(...) -> {.a: bool, .b: (f32, C), .c: ref i32}; // Equivalent to: -> {.a: var bool, .b: var (f32, C), .c: ref i32}; ``` This feature is intended to support cases like `enumerate` that will want to return a value for the index but a reference to the element of the sequence being enumerated. ### Nested binding patterns Since a `ref` binding may only bind to a durable reference expression, it can't be used to bind the result of a function returning an initializing expression. However, if the initializing expression is bound to a `var`, any nested patterns are reference binding patterns bound to the subobject, following [proposal #5164: "Updates to pattern matching for objects"](https://github.com/carbon-language/carbon-lang/pull/5164). For example: ```carbon fn F() -> (bool, (C, i32)); let (b: bool, var (c: C, i: i32)) = F(); ``` is equivalent to: ```carbon fn F() -> (bool, (C, i32)); let (b: bool, var v: (C, i32)) = F(); let ref c: C = v.0; let ref i: i32 = v.1; ``` Note that `ref` is disallowed inside `var` since that would be redundant. ### Mututation restriction on objects bound to a value Mutation of objects with a non-copy-value representation in an active value binding ("borrowed objects") is erroneous behavior. - Our plan is to prevent mutation of borrowed objects in Carbon's strict safe dialect. - We should only relax our stance here and consider making such mutation _allowed_ if we discover difficulty with this that we cannot overcome. - But we _should_ revisit the underlying idea of mutation being erroneous if enforcing it in the strict mode proves fundamentally untenable due to ergonomic costs. - There will always be the potential for unchecked code, either unsafe Carbon code or C++ code by way of interop, to mutate a borrowed object, hence the need to define it as erroneous behavior. - There is no need to ever make it anything more than erroneous behavior, see below. - If we can prove the mutation doesn't occur, then we can use that to optimize under "as-if", and we don't need anything else. - We are deferring the decision of whether strict enforcement is enabled in Carbon's current C++-friendly mode, when not explicitly marking the code as "unsafe." This was [discussed 2025-05-22](https://docs.google.com/document/d/1Yt-i5AmF76LSvD4TrWRIAE_92kii6j5yFiW-S7ahzlg/edit?tab=t.0#heading=h.uot97ukynlsi) and then made the subject of [leads issue #5524](https://github.com/carbon-language/carbon-lang/issues/5524). ### No optimization on erroneous behavior The Carbon compiler should not optimize on erroneous behavior, ever, unless the compiler literally proves that it does not occur, ever. In which case, we don't need any license to start optimizing on this, as it falls under "as-if". The fact that undefined behavior ("UB", [cppreference](https://en.cppreference.com/w/cpp/language/ub.html), [wikipedia](https://en.wikipedia.org/wiki/Undefined_behavior)) provides "as-if" _without_ a proof is precisely the risk of using UB for any semantics, and why we don't use it here and elsewhere we use erroneous behavior. ### `bound` parameters It is erroneous behavior to return something that references a local object that won't live once the function returns, even if it is a parameter marked `bound`. Local objects include local variables, temporary objects, and `var` parameters. ```carbon fn Invalid1() -> i32* { var x: i32 = 4; // ❌ Invalid. return &x; } fn Invalid2(bound x: i32) -> ref i32 { var y: i32 = x; // ❌ Invalid. return y; } // ✅ Valid fn Valid1(bound p: i32*) -> ref i32 { return *p; } fn Invalid3(bound var x: i32) -> ref i32 { // ❌ Invalid: lifetime of `var` parameter // ends when function returns. return x; } class ReturnMember { // ✅ Valid fn ValidRef[bound ref self: Self]() -> ref i32 { return self.m; } // ❌ Invalid: can't return reference to value. fn InvalidVal[bound self: Self]() -> ref i32 { return self.m; } // ❌ Invalid: `var self` lifetime ends. fn InvalidVar[bound var self: Self]() -> ref i32 { return self.m; } var m: i32; } class DerefPointerMember { // ✅ Valid fn ValidRef[bound ref self: Self]() -> ref i32 { return *self.pm; } // ✅ Valid fn ValidVal[bound self: Self]() -> ref i32 { return *self.pm; } // ✅ Valid fn ValidVar[bound var self: Self]() -> ref i32 { return *self.pm; } var pm: i32*; } ``` Otherwise, `bound` parameters and global variables are the sources of storage that can be referenced by a return, but need not be referenced, particularly not on every code path. ```carbon // Result references `r` if `b` is true, and `p` // otherwise. Valid as long as both are marked `bound`. fn Conditional(b: bool, bound ref r: C, bound p: C*) -> ref C { if (b) { return r; } else { return *p; } } ``` The parameters of functions defined in an interface may also be marked as `bound`. The `impl` of that interface for a type can omit occurrences of `bound` from the interface, but cannot add new ones. ```carbon interface I { fn F[bound ref self: Self] (a: Self, bound b: Self, bound c: Self*) -> ref Self; } impl C1 as I { // ✅ Valid: matches interface fn F[bound ref self: Self] (a: Self, bound b: Self, bound c: Self*) -> ref Self; } impl C2 as I { // ✅ Valid: proper subset of `bound` params fn F[ref self: Self] (a: Self, bound b: Self, c: Self*) -> ref Self; } impl C2 as I { // ❌ Invalid: `a` is not bound in `I.F`. fn F[ref self: Self] (bound a: Self, b: Self, c: Self*) -> ref Self; } ``` Like `[[clang::lifetimebound]]` in C++, `bound` does not affect semantics or calling conventions, just what code is legal. This helps avoid mismatches between typechecking against the signatures in an interface when the `impl` functions are different. Exception: the question of whether `bound` affects the lifetime of temporaries is [future work](#temporary-lifetimes). Note that all combinations of a `val`/`ref`/default return can be bound to a value/`ref`/`var` parameter. Examples: ```carbon fn RefToVal(bound ref x: C) -> val D { return x.d; } fn ValToRef(bound y: C) -> ref D { return *y.ptr; } fn VarToRef(bound var p: i32*) -> ref i32 { return *p; } fn VarToDefault(bound var p: i32*) -> i32* { return p; } ``` For full safety, we need each bound parameter to be immutable for the duration of the lifetime of the returned result. However, the objective for now is only matching `[[clang::lifetimebound]]`, which has the goal of preventing some classes of bugs, not full memory safety. We will reconsider this with the memory safety design. Clang's `lifetimebound` attribute also only applies to the immediately pointed to objects (by pointers or reference parameters, or pointers or reference subobjects of an aggregate parameter). We suggest a simpler, transitive model here that is more restrictive but should be compatible. That said, pinning down the exact and firm semantics of `bound`, especially in these complex cases, is deferred to the full memory safety design as well. ### How addresses interact with `ref` The address of a `ref` binding is `noalias` and either `captures(none)` or `captures(ret: address, provenance)`, depending on whether the binding is marked `bound`. - `noalias` means like C `restrict`; you can't observe mutations through aliases; mutation through a restricted pointer is not observable through another pointer - `captures(none)` means there is no transitive escape: you can pass a nocapture pointer to another nocapture function, but you can't store to memory or return - `captures(ret: address, provenance)`: is like `captures(none)` but may be referenced by a return. The combination of `noalias` and `captures(none)` semantics are the minimum for the "move-in-move-out" optimization. But this condition is hard to check, so safe code will use a stricter criteria. Unsafe code will be required to adhere to just the `noalias` restrictions, but will not be checked (except possibly by a sanitizer at runtime). The details here will be tackled as part of the memory safety design. Optimizations will only be performed based on information that is enforced or checked by the compiler, so these attributes won't be passed to LLVM unless their requirements can be established. This avoids introducing undefined behavior, which we particularly don't want to do in situations where C++ doesn't. The goal of these rules it to nudge us towards function boundaries that don't constructively create aliasing in their API boundary and don't capture pointers unnecessarily. These restrictions are experimental, and we should keep track of everything we end up needing to do to work around these restrictions so any reconsideration can be properly informed. ### Improved interop and migration with C++ references We expect this to improve interop and migration by allowing significantly more interface similarity between Carbon and C++. Previously, many things in C++ that used references on interface boundaries would be forced to switch to pointers. This adds ergonomic friction both at a basic level because of the forced change but also a deeper level because it will make it significantly harder to see the parallel usage across the boundary between C++ and Carbon. With reference bindings, the vast majority of this dissonance will be removed. This does create a migration concern, raised in [open discussion on 2025-05-01](https://docs.google.com/document/d/1Yt-i5AmF76LSvD4TrWRIAE_92kii6j5yFiW-S7ahzlg/edit?tab=t.0#heading=h.dffumsu6wzlc), that the `nocapture` and `noalias` modifiers don't match C++ restrictions, particularly on the `this` parameter that we are going to require migrate to `ref self`. We may have to add back in `addr` to allow a different pointer type for those cases. > **Future work:** Addressing how we model the various kinds of C++ references > that Carbon code may need to interact with is > [something we are actively considering](https://docs.google.com/document/d/1l5TbNuwZEcwm96ejGPLn9GdoQO1fByUW0tFRLU9BqXE/edit?tab=t.0) > and will be tackled in a future proposal. ### Part of the expression type system, not object types Much like value/`val` and `var` bindings, `ref` binding and the new return forms are are part of the type system, but only through expression categories, patterns (function parameters and so on), and returns. Specifically, we don't expect them to be part of the _object types_ in Carbon. Like value bindings, we retain a great deal of implementation flexibility around layout, and the specifics of how they are lowered. This specifically means we will need to incorporate `ref` bindings into the `Call` interface and we will be adding complexity there that will need to be handled by overloading. The changes to the `Call` interface is future work, and overloading, once we add support, will need to carry additional complexity to handle `ref`. ### Interaction with `returned var` The rule is: `returned var` may only be used when there is a single atomic return form, and it is the default `var` category. ```carbon // ✅ Allowed fn F(...) -> V { returned var v: V = ...; // ... return var; } fn F(...) -> {var .a : T} { // ❌ Invalid: composite form returned var ret: T = ...; // ... } fn F(...) -> val T { // ❌ Invalid: value return returned var ret: T = ...; // ... } ``` We can revisit and expand this later if this does not handle use cases we would like to support. ### Use case: `Deref` interface To support customization of the prefix-`*` dereferencing operator, we introduce the `Deref` interface. ```carbon interface Deref { let Result:! type; fn Op[bound ref self: Self]() -> ref Result; } final impl forall [T:! type] T* as Deref { where Result = T; fn Op[bound self: Self]() -> ref T = "builtin.deref"; } ``` Then `*p` is rewritten to `p.(Deref.Op)()`, and `p->m` is rewritten to `p.(Deref.Op)().m`. For example, this might be used by a smart pointer: ``` class SmartPtr(T:! type) { fn Make(p: T*) -> Self { return {.ptr = p}; } impl as Deref { where Result = T; fn Op[bound ref self: Self]() -> ref Result { return *self.ptr; } } private var ptr: T*; } ``` ### Use case: indexing interfaces [Proposal #2274: "Subscript syntax and semantics"](https://github.com/carbon-language/carbon-lang/pull/2274) added the interfaces used to support indexing with the subscripting operator `[`...`]`. We change these in the following ways: - The `addr self` parameters are changed to `bound ref self`, to allow the result to reference the `self` object. - The `At` method returns by `val`. - The `Addr` methods are renamed `Ref` and return a reference instead of a pointer that is automatically dereferenced. [This proposal's PR](https://github.com/carbon-language/carbon-lang/pull/5434) makes those changes to the [indexing design](/docs/design/expressions/indexing.md). ### Use case: member binding interfaces The member binding interface used for reference expressions from [proposal #3720](https://github.com/carbon-language/carbon-lang/pull/3720) can now be changed to use references instead of pointers. Before: ```carbon // For a reference expression `x` with type `T` // and an expression `y` of type `U`, `x.(y)` is // `*y.((U as BindToRef(T)).Op)(&x)` interface BindToRef(T:! type) { extend impl as Bind(T); fn Op[self: Self](p: T*) -> Result*; } ``` After: ```carbon // For a reference expression `x` with type `T` // and an expression `y` of type `U`, `x.(y)` is // `y.((U as BindToRef(T)).Op)(x)` interface BindToRef(T:! type) { extend impl as Bind(T); fn Op[self: Self](bound ref p: T) -> ref Result; } ``` Similarly, the `BindToValue` interface is changed to use a `val`/value return, potentially avoiding a copy of large objects. Before: ```carbon interface BindToValue(T:! type) { extend Bind(T); fn Op[self: Self](x: T) -> Result; } ``` After: ```carbon interface BindToValue(T:! type) { extend Bind(T); fn Op[self: Self](bound x: T) -> val Result; } ``` ### Use case: class accessors A `ref` return can be used to expose the state of an object in a way that can be mutated: ```carbon class Four { fn Get[self: Self](i: i32) -> i32 { Assert(i >= 0 and i < 4); return self.m[i]; } fn GetMut[bound ref self: Self](i: i32) -> ref i32 { Assert(i >= 0 and i < 4); return self.m[i]; } private var m: array(i32, 4); } var x: HasMember = {.m = (0, 2, 4, 6)}; x.GetMut(2) += 1; fn Check(y: Four) { Assert(y.Get(2) == 5); } Check(x); ``` **Future work**: this will in the future often be done with an overloaded method, as in: ```carbon class Four { overload Access { fn [bound ref self: Self](i: i32) -> ref i32 { Assert(i >= 0 and i < 4); return self.m[i]; } fn [self: Self](i: i32) -> i32 { Assert(i >= 0 and i < 4); return self.m[i]; } } private var m: array(i32, 4); } var x: HasMember = {.m = (0, 2, 4, 6)}; x.Access(2) += 1; fn Check(y: Four) { Assert(y.Access(2) == 5); } Check(x); ``` This may be a common enough use case that we will want to introduce a dedicated syntax: ```carbon class HasMember { fn Access[bound ref? self: Self](i: i32) -> ref? i32 { Assert(i >= 0 and i < 4); return self.m[i]; } private var m: array(i32, 4); } ``` ### Type completeness Not a change by this proposal, but note that our existing rules will require the type in a `ref` binding to be complete in situations where it would not need to be if you were using a value binding with a pointer type instead. We may need to change this in the future to match C++ which treats reference types more like pointer types for completeness purposes. After this change, a `ref` binding to type `T` will require `T` to be complete in the same situations that other bindings to type `T` require `T` to be complete. ### Pointer value representation Purely as a change in syntax, the way to specify that [the value representation of a class](/docs/design/values.md#value-representation-and-customization) uses a pointer is changed from writing `const Self *` to `const ref`. ## Future work ### Temporary lifetimes For safety, we need bindings and returns that reference storage to only be used while that storage remains valid. When the referenced storage is owned by a temporary, we have a choice to either control the lifetime of the temporary or diagnose when the lifetime of the temporary is insufficient. Deciding on our policy is future work. Note that in many cases we can explicitly provide storage in a variable instead of referencing a temporary. For example, using `var x: ... = ReturnsATemporary();` instead of `let ref x: ... = ReturnsATemporary();`. This won't apply in all situations, though, such as temporaries that are reachable transitively through pointers. ### `ref` bindings in lambdas We have already identified [future work to support reference captures in lambdas as part of proposal #3848](/proposals/p3848.md#future-work-reference-captures). This might be a reason to support `ref` bindings as fields of objects, with all the restrictions that comes with that. ### Interaction with effects We still need to determine how references and the other return types interact with effects, like `Optional`, errors, co-routines, and so on. For example, we don't want to give up the benefits of being able to directly return a reference when a function has an error path. It is unclear if this will mean putting references into the object type system, but we may be able to handle this with additional types or the ability to customize return representations. For example, we might have an alternate version of the `Optional` type that holds a reference: ```carbon class OptionalRef(T:! type) { fn Make(bound ref r: T) -> Self { return {.p = &r}; } fn MakeEmpty() -> Self { return {.p = Optional(T*).MakeEmpty()}; } fn HasValue[self: Self]() -> bool { return p.HasValue(); } fn Get[bound ref self: Self]() -> ref Result { Assert(self.HasValue()); return *self.p.Get(); } private var p: Optional(T*); } ``` ### More precise lifetimes More precise lifetime tracking will be considered with the memory safety design. For example, the `bound` approach does not distinguish different components of a compound return, or different parts of a parameter object that might have different lifetimes. ### Combining with compile-time We plan to support references to compile-time state when executing a function at compile time. That will be part of a future proposal. ### Interaction with `Call` or other interfaces For now, `ref` is not represented in the `Call` interface introduced in [proposal #2875: Functions, function types, and function calls](https://github.com/carbon-language/carbon-lang/pull/2875). This will be tackled together in a future proposal with other aspects of bindings not represented by the type, such as `var` and compile-time, along with being generic across these aspects of bindings. ### Destructuring assignment Having more support for multiple returns from a function opens the question of how to do different things with the different returns. We may want a syntax for saying some of the returns are bound to new names, and some are used in assignments to existing variables. One possibility would be to have some pattern syntax for re-initializing an existing object, as in: ```carbon fn F() -> (-> bool, ->T); fn G() { var x: T = ...; Consume(~x); let (b: bool, init x) = F(); // Continue to use `x`... } ``` This was discussed in [the #syntax channel on Discord on 05-19-2025](https://discord.com/channels/655572317891461132/709488742942900284/1374126123595727000). ### Restore `addr` There are two reasons we might restore `addr` as an alternative to `ref` for the implicit `self` parameter: - As a way to express uses of `self` that we want to disallow for `ref` bindings generally but need an escape hatch for migrated C++ code that relies on these patterns. - To allow the `self` parameter to use one of the pointer semantics we create as part of the upcoming memory safety design, that can't be achieved with `ref`. ## Rationale This proposal tries to advance these [Carbon goals](/docs/project/goals.md): - [Performance-critical software](/docs/project/goals.md#performance-critical-software) - Having a "move-in-move-out" option as a calling convention is a potential performance improvement for using `ref` parameters instead of pointers. - Giving additional options for the return convention gives opportunities for improved performance. Having this set by explicit return markings is about giving control and predictability to the code author. - [Code that is easy to read, understand, and write](/docs/project/goals.md#code-that-is-easy-to-read-understand-and-write) - `ref` bindings and returns avoid the ceremony of round-tripping through pointers. - [Practical safety and testing mechanisms](/docs/project/goals.md#practical-safety-and-testing-mechanisms) - Checking that reference bindings are not dangling is important for avoiding use-after-free bugs. - `bound` markings on parameters to allow safety equivalent to Clang's `[[clang::lifetimebound]]` when returning a reference. - [Interoperability with and migration from existing C++ code](/docs/project/goals.md#interoperability-with-and-migration-from-existing-c-code) - Including references in Carbon allows for less mismatch for C++ code using references. ## Alternatives considered These ideas were discussed in open discussion on: - [2025-05-01](https://docs.google.com/document/d/1Yt-i5AmF76LSvD4TrWRIAE_92kii6j5yFiW-S7ahzlg/edit?tab=t.0#heading=h.dffumsu6wzlc) - [2025-05-06](https://docs.google.com/document/d/1Yt-i5AmF76LSvD4TrWRIAE_92kii6j5yFiW-S7ahzlg/edit?tab=t.0#heading=h.s42g5iv67d3c) - 2025-05-07 [a](https://docs.google.com/document/d/1Yt-i5AmF76LSvD4TrWRIAE_92kii6j5yFiW-S7ahzlg/edit?tab=t.0#heading=h.sfx9d7ltud5) [b](https://docs.google.com/document/d/1Yt-i5AmF76LSvD4TrWRIAE_92kii6j5yFiW-S7ahzlg/edit?tab=t.0#heading=h.4zbo49wg5rmk) - [2025-05-08](https://docs.google.com/document/d/1Yt-i5AmF76LSvD4TrWRIAE_92kii6j5yFiW-S7ahzlg/edit?tab=t.0#heading=h.vdognq1upsf5) - [2025-05-12](https://docs.google.com/document/d/1Yt-i5AmF76LSvD4TrWRIAE_92kii6j5yFiW-S7ahzlg/edit?tab=t.0#heading=h.1mjh6unumnwu) - [2025-05-13](https://docs.google.com/document/d/1Yt-i5AmF76LSvD4TrWRIAE_92kii6j5yFiW-S7ahzlg/edit?tab=t.0#heading=h.bdznj2d0by2g) - [2025-05-14](https://docs.google.com/document/d/1Yt-i5AmF76LSvD4TrWRIAE_92kii6j5yFiW-S7ahzlg/edit?tab=t.0#heading=h.52tb7l2he343) They were also discussed in the [#pointers-and-references channel in Discord starting 2025-05-05](https://discord.com/channels/655572317891461132/753021843459538996/1369085231038074901), and [#syntax on 2025-05-14](https://discord.com/channels/655572317891461132/709488742942900284/1372285365162872943). ### No `ref`, only pointers The rationale to add `ref` instead of staying with pointers was discussed on [2025-05-01](https://docs.google.com/document/d/1Yt-i5AmF76LSvD4TrWRIAE_92kii6j5yFiW-S7ahzlg/edit?tab=t.0#heading=h.dffumsu6wzlc). In addition to the motivating problems given in [the "Problem" section](#problem), that discussion included some additional depth to the reasons to add reference bindings. There is a tension between wanting to have mutating expressions and only having pointers. You need some concept like a reference in order to mutate an object with an expression. The question is how small a box the references are restricted to, and where the line is drawn. C has lvalues, which contain references but are restricted to a quite small box. Reference bindings specifically are about keeping a small box around references while still adding enough expressivity to support our use cases. We have started with a model similar to C, but it fell down when it comes down to composition. Decomposing an expression into pieces loses the tools the expression provided to you. The missing tool for that was reference bindings. We saw how much we were leaning on value bindings. The asymmetry between having value binding but not reference bindings when have value expressions and reference expressions was creating pressure. For example, when accessing members of an object, we had to escape to pointers in that operator. One downside of this change is that before indirections were more visible in the code. Also, this does fundamentally mean that we now have another kind of "pointer", potentially adding complexity to any memory-safety story. However, this ship already sailed to some extent with value bindings. Fundamentally, bindings are allowed to have pointer-like semantics from a lifetime perspective, and so will need to be considered as a pointer-like thing as we build out lifetime safety. ### Remove pointers after adding references If we removed pointers after adding references, we would need something rebindable for assignable objects. The viable path forward without separate pointers and references is to have something rebindable like pointers but automatically dereferenced like references, which is the approach Rust takes. See [this comment on issue #5261](https://github.com/carbon-language/carbon-lang/issues/5261#issuecomment-2786462775). One of the features of a reference is what it cannot do, and we would have to remove those restrictions to be able to satisfy the pointer use cases with references. ### Allow `ref` bindings in the fields of classes A type with reference binding fields would need a lot of restrictions since reference bindings are not assignable. We did not see enough motivation to put references into objects, given the complexity that it would introduce, so we are keeping references out of types for now. This could change to support lambda reference captures. ### No call-site annotation This question was discussed on [2025-05-07](https://docs.google.com/document/d/1Yt-i5AmF76LSvD4TrWRIAE_92kii6j5yFiW-S7ahzlg/edit?tab=t.0#heading=h.sfx9d7ltud5). We decided that the marking is not about lifetime, but ability to mutate. A `val` may reference an object in a similar way to a `ref`, restricting operations on the original object, but we are not going to mark `val`s since those restrictions are enforced by the compiler. We thought the ability to mutate, though, was something important enough to highlight to readers of the code, even at the expense of extra work for the writer. Swift `inout` parameters are [marked at caller with an `&` before the argument](https://docs.swift.org/swift-book/documentation/the-swift-programming-language/functions/#In-Out-Parameters). Jordon Rose has published [a regret](https://belkadan.com/blog/2021/12/Swift-Regret-inout-Syntax/) that they didn't use `inout` to mark the argument instead of `&`. On the other hand, not marking is not known to be a source of bugs. This is a "try it and see how well it works" sort of decision. ### Top-level `ref` introducer For now, we don't believe `let ref` to be so common as to need a shorter way to write, unlike what we do for `var`. This was considered in [leads issue #5523](https://github.com/carbon-language/carbon-lang/issues/5523), which provided this rationale: > I feel like this would often be used for non-local mutation due to it > fundamentally deporting mutable value semantics and instead having reference > semantics. Unlike the local mutation, that seems more worthwhile to have > incentives around minimizing. > > However, this seems the easiest of all to revisit later if we discover that > the added verbosity in practice is costing more than is worth any improvements > from explicitly flagging mutable reference semantics, or if we find code is > reaching for antipatterns due to the incentive. In addition, [this comment on leads issue #5522](https://github.com/carbon-language/carbon-lang/issues/5522#issuecomment-2972029100) argued that it would be more consistent for `ref` and `val` to only apply to bindings, and not introduce patterns, like `let` and `var`. ### Allow immutable value semantic bindings nested within variable patterns This was considered in [leads issue #5523](https://github.com/carbon-language/carbon-lang/issues/5523), which provided this rationale: > While it may be an obvious point of orthogonality, I think it adds choice > without sufficient motivation, and even _having_ that choice does add some > complexity to the language. > > It also seems like we could add this later if there is sufficient demand when > we have larger usage experience body to pull from with the rest of the Carbon > language. Currently, the affordance that feels more natural to me is what we > have. > I think we're happy to see motivating use cases and revisit this. At the > moment, we've just not seen motivating use cases -- everything has seemed a > bit too contrived. ### Remove `var` as a top-level statement introducer This was considered in [leads issue #5523](https://github.com/carbon-language/carbon-lang/issues/5523), which provided this rationale: > Locals are important, frequent, and frequently mutable. I don't think forcing > varying locals to go through `let var` for orthogonality aids readability > enough to offset the verbosity cost of added keywords on a reasonably common > pattern. > > I'm still currently in the position that locally owned objects being mutable > should not be "discouraged" or "disincentivized" by the language. And I think > adding artificial incentives to try and avoid needing a mutable local variable > would either have no effect beyond verbosity, or if it _did_ have effect, it > wouldn't be a net positive effect due to code being written in a less > straightforward manner in order to avoid mutation. > > To be clear, this is based on intuition and judgement based on my experience, > not in any way based on data or specific motivating examples. I can imagine > data or evidence or even a new perspective changing my position here, but so > far the discussion we've had haven't done that. ### `ref` as a type qualifier The big concern is that any effect that is represented by a type, like `Optional` or `Result`, will want to compose with reference returns. This could be done by allowing `ref` to create an object type that could be used as a parameter to those, as in `Optional(ref T)`, but [we are trying to avoid going down that path](https://github.com/carbon-language/carbon-lang/issues/5261#issuecomment-2790421894). We have [future work](#interaction-with-effects) to tackle this problem specifically. There was also [a concern that we might need `ref` types to represent argument lists with tuples](https://github.com/carbon-language/carbon-lang/issues/5261#issuecomment-2790506515), but tuples already can't represent `var` or compile-time parameters. We have other plans for this, instead of trying to stretch tuples to encompass these use cases. We also noted that including references in the type system led to a number of inconsistencies in C++, such as no there not being references of references. ### `bound` would change the default return to `val` We considered saying that `bound` would change the default return to use the `->val` return convention. This was discussed on [2025-05-01](https://docs.google.com/document/d/1Yt-i5AmF76LSvD4TrWRIAE_92kii6j5yFiW-S7ahzlg/edit?tab=t.0#heading=h.dffumsu6wzlc) and [2025-05-08](https://docs.google.com/document/d/1Yt-i5AmF76LSvD4TrWRIAE_92kii6j5yFiW-S7ahzlg/edit?tab=t.0#heading=h.vdognq1upsf5). The idea is that `val` is expected to be efficient, so we should encourage using it, but we can't always use `val`, since some types have a reference value representation, but `bound` alleviates that concern. Once we realized that `bound` is relevant for all return conventions, we reconsidered that approach, since has a number of concerns: - Changing defaults is action at a distance, changing the behavior without changing the code in the relevant location. - We don't want to have to make changes to the return category of copy-paste of a function from an interface to an `impl` of it when removing `bound`. - Lifetimes in Rust and Clang's `[[clang::lifetimebound]]` don't change calling conventions, only what code is valid. Going with an approach where less depends on `bound` makes sense for now, since we are going to reconsider these issues as part of our upcoming memory safety work. ### Other return conventions We also considered other conventions for returning from functions, in [a comment on #5434](https://github.com/carbon-language/carbon-lang/pull/5434/files#r2099145225), on [2025-05-08](https://docs.google.com/document/d/1Yt-i5AmF76LSvD4TrWRIAE_92kii6j5yFiW-S7ahzlg/edit?tab=t.0#heading=h.vdognq1upsf5) and on [2025-05-12](https://docs.google.com/document/d/1Yt-i5AmF76LSvD4TrWRIAE_92kii6j5yFiW-S7ahzlg/edit?tab=t.0#heading=h.1mjh6unumnwu), most notably: - **in place**: This convention was like `->`, but always using the "in place" convention where the caller allocates storage and provides the callee with its address, the callee initializes the storage at that address, and the caller is responsible for destroying after the return. - **var without storage**: The callee returns a pointer to the storage of a subobject of a `bound var` parameter, that caller is then responsible for destroying. A call to this function is reference expression, but with additional responsibility to destroy. - **hybrid**: If the type has a copy value representation or trivial destructive move then return the object representation directly; otherwise caller passes a pointer and callee initializes it. There were also some variations on what the conditions for returning in registers using the default return convention. We seriously considered "var without storage", but the fact that it couldn't reliably be used to initialize a variable, particularly in the middle of an object, meant it did not seem valuable enough to include. It seemed more valuable to support the "in place" return convention. That return form allows you to guarantee knowing the address of the object being constructed, and was a good match for `returned var`. However, we realized that `var` declarations shouldn't always be associated with in-memory storage, in particular for types that may be trivially moved. For example, a `var` parameter with the C++ type `std::unique_ptr` should be passed in registers. A function returning a `std::unique_ptr` in place would not be as efficient as returning it by moving it into registers. ### `return var` with compound return forms We considered various syntax options on [2025-05-12](https://docs.google.com/document/d/1Yt-i5AmF76LSvD4TrWRIAE_92kii6j5yFiW-S7ahzlg/edit?tab=t.0#heading=h.1mjh6unumnwu), but none of them seemed good enough to justify inclusion at this time: ```carbon fn F(...) -> (ref R, val L, V) { // No longer a `var` being returned. Ideally these // shouldn't have to be initialized together. returned ??? (ref r: R, val l: L, var v: V) = ...? return var; } // We could restrict to one `var` return component, // but this is a lot of machinery for a small increase // in expressiveness and applicability. fn F(...) -> (ref R, val L, V) { returned var v: V = ...; let l: L = ...; return (*r, l, var); } fn F(...) -> (ref R, val L, V) { // These don't have the right category, and ideally // shouldn't have to be initialized together. returned var ret: (R, L, V) = ...? return var; } fn F(...) -> (ref R, val L, V) { returned var (_, _, var v: V) = ; } ``` There was another approach we considered for `returned var` originally: ```carbon fn F(...) -> (ref R, val L, v1: V1, var v2: V2) { // ... // Must use the same names for the `var` (implicit or explicit) returns // with bound names. return (r, l, v1, v2); } ``` But this had downsides that still apply: - Requires `V1` and `V2` to have unformed states. Otherwise, `v1` and `v2` would need be initialized when they are declared. - This does not support only having some branches use `return var`. Our current approach handles our main use case for `returned var`: factory functions. We could support an "only `var`s" approach in the future if we want: ```carbon fn F(...) -> (var V1, var V2, var V3) { returned (var v1: V1, var v2: V2, var v3: V3) = ...; // ... return var; } ``` ### Other syntax for compound return forms We considered other options for the syntax of compound return forms on [2025-05-13](https://docs.google.com/document/d/1Yt-i5AmF76LSvD4TrWRIAE_92kii6j5yFiW-S7ahzlg/edit?tab=t.0#heading=h.bdznj2d0by2g), [#syntax in Discord on 2025-05-14](https://discord.com/channels/655572317891461132/709488742942900284/1372285365162872943), and [2025-05-14](https://docs.google.com/document/d/1Yt-i5AmF76LSvD4TrWRIAE_92kii6j5yFiW-S7ahzlg/edit?tab=t.0#heading=h.52tb7l2he343). The option of omitting the `->` in each component did not distinguish tuples from tuple return forms sufficiently: ```carbon -> (ref i32, var i32) -> (bool, ref i32) // Is this a single return of a tuple, or a triple // return using the default return convention? -> (i32, i32, i32) ``` We also considered an approach where compound return forms would start with `->?`, but this raised concerns about what the meaning of that syntax would be and whether we want to expose users to that in cases we might be able to avoid it. The original proposed syntax used an arrow `->` in each component of a compound form. ```carbon fn TupleReturn(...) -> (->val bool, ->ref i32, -> C); fn StructReturn(...) -> {->val .a: bool, ->ref .b: i32, -> .c: C}; ``` This avoided ambiguity, but was verbose and visually noisy. An alternative was suggested in [discussion on 2025-06-13](https://docs.google.com/document/d/1Yt-i5AmF76LSvD4TrWRIAE_92kii6j5yFiW-S7ahzlg/edit?tab=t.0#heading=h.52ru7ner80b4). This alternative used a default of compound returns for paren `(`...`)` and brace `{`...`}` expressions, which you could opt out of by using one of the three category keywords such as `var` to introduce a type expression that would not be considered a compound return. This had the downside that `-> T` would be interpreted as `-> var T` even if `T` was a tuple type like `(i32, i64)`. However, textually substituting in `(i32, i64)` in for `T` to get `-> (i32, i64)` would instead be interpreted as `-> (var i32, var i64)`. To overcome this problem, in [discussion on 2025-06-16](https://docs.google.com/document/d/1Yt-i5AmF76LSvD4TrWRIAE_92kii6j5yFiW-S7ahzlg/edit?tab=t.0#heading=h.rdbzk5jnin3x), we switched to the approach from this proposal. ### `ref` parameters allow aliasing If requiring `ref` parameters to be `noalias` ends up being too restrictive, we could instead have the "move-in-move-out" optimization be done only when the compiler can prove it safe. One strategy would be to generate an alternate version of the function that is only used in the cases where the `noalias` conditions can be shown to hold statically. ### `let` to mark value returns instead of `val` This proposal initially used `let` instead of `val` to mark [immutable value returns](#ref-and-val-returns). However, `let` is used, in Carbon and other languages, primarily to bind names. In Carbon, the default binding is a value binding, but that was not considered a close enough to connect the `let` keyword to value semantics. There was also a concern about reusing `let` in multiple contexts to mean different things, and having separate keywords that were only used to mark the category of the binding was deemed better separation of concerns. We considered making a parallel change to use `init` instead of `var`, but this had some problems: - By making initializing returns the default, there is little expected usage, so perhaps not worth spending another keyword on. - The `init` keyword would be particularly expensive, because C++ code commonly use that word in APIs. This question was considered in [leads issue #5522](https://github.com/carbon-language/carbon-lang/issues/5522). ### `=>` infers form, not just type There was support for the idea that the `=>` return syntax introduced in [proposal #3848](https://github.com/carbon-language/carbon-lang/pull/3848) should deduce the form of the return, not just its type. This was discussed in [#lambdas on 2025-05-20](https://discord.com/channels/655572317891461132/999638000126394370/1374462658723450981). However, trying to infer the expression category from the category of the expression after the `=>` runs into the problem of this often requiring the parameters to be marked `bound`. A more complicated rule, for example using whether any parameter is marked `bound`, could be adopted in the future, if the simple rule proves inadequate. Alternatively, the compiler could infer which parameters should be marked `bound` in this case. That is something to consider with the memory safety design. ================================================ FILE: proposals/p5448.md ================================================ # Carbon <-> C++ Interop: Primitive Types [Pull request](https://github.com/carbon-language/carbon-lang/pull/5448) ## Table of contents - [Abstract](#abstract) - [Problem](#problem) - [Background](#background) - [Data models](#data-models) - [Carbon Primitive Types](#carbon-primitive-types) - [C++ Fundamental Types](#c-fundamental-types) - [void](#void) - [std::nullptr_t](#stdnullptr_t) - [std::byte](#stdbyte) - [Character types](#character-types) - [Signed integer types](#signed-integer-types) - [Unsigned integer types](#unsigned-integer-types) - [Floating-point types](#floating-point-types) - [Proposal](#proposal) - [Details](#details) - [C++ -> Carbon mapping details](#c---carbon-mapping-details) - [Carbon -> C++ mapping details](#carbon---c-mapping-details) - [Rationale](#rationale) - [Alternatives considered](#alternatives-considered) - [Open questions](#open-questions) ## Abstract Define the type mapping of the primitive types between Carbon and C++. ## Problem Interoperability of Carbon with C++ is one of the Carbon language goals (see [Interoperability with and migration from existing C++ code](/docs/project/goals.md#interoperability-with-and-migration-from-existing-c-code)). Providing [unsurprising mappings between C++ and Carbon types](/docs/design/interoperability/philosophy_and_goals.md#unsurprising-mappings-between-c-and-carbon-types) is one of it's sub goals. This proposal addresses the type mapping between the two languages to support achieving this goal. ## Background ### Data models The following data models are widely accepted: - 32-bit systems: - `LP32` (Win16 API): `int` 16-bit; `long` 32-bit; `pointer` 32-bit. - `ILP32` (Win32 API; Unix and Unix-like systems): `int` 32-bit; `long` 32-bit; `pointer` 32-bit. - 64-bit systems: - `LLP64` (Win32 API: 64-bit ARM or x86-64): `int` 32-bit; `long` 32-bit; `pointer` 64-bit. - `LP64` (Unix and Unix-like systems (Linux, macOS)): `int` 32-bit; `long` 64-bit; `pointer` 64-bit. [Carbon supported platforms](/docs/project/principles/success_criteria.md#modern-os-platforms-hardware-architectures-and-environments) Carbon will prioritize supporting modern OS, 64-bit little endian platforms (for example [LLP64](/proposals/p5448.md#data-models), [LP64](/proposals/p5448.md#data-models)). Historic platforms like [LP32](/proposals/p5448.md#data-models) won't be supported. For clarity, the text below omits [LP32](/proposals/p5448.md#data-models) relevant information and focuses only on the Carbon supported platforms. ### Carbon Primitive Types Carbon has the following [primitive types](https://github.com/carbon-language/carbon-lang/blob/trunk/docs/design/README.md#primitive-types): - `bool`: boolean type taking `true` or `false` - integer types: - signed integer types: `iN` (`N` - bit width, a positive multiple of 8) - `i8`, `i16`, `i32`, `i64`, `i128`, `i256` - unsigned integer types: `uN` (`N` - bit width, a positive multiple of 8) - `u8`, `u16`, `u32`, `u64`, `u128`, `u256` - floating-point types: `fN` (`N` - bit width, a positive multiple of 8), IEEE-754 format - `f16`, `f32`, and `f64` - always available - `f80`, `f128`, or `f256` may be available, depending on the platform ### C++ Fundamental Types C++ calls the primitive types [fundamental types](https://en.cppreference.com/w/cpp/language/types). The following fundamental types exist in C++: - `void` - `std::nullptr_t` - `std::byte` - integral types (also integer types): - `bool` - character types: - narrow character types: `signed char`, `unsigned char`, `char`, `char8_t` (c++20) - wide character types: `char16_t`, `char32_t`, `wchar_t` - signed integer types: - standard signed integer types: `signed char`, `short`, `int`, `long`, `long long` - extended signed integer types (implementation-defined) - unsigned integer types: - standard unsigned integer types: `unsigned char`, `unsigned short`, `unsigned int`, `unsigned long`, `unsigned long long` - extended unsigned integer types - floating-point types: - standard floating-point types: `float`, `double`, `long double` - extended floating-point types: - fixed width floating-point types (since C++23): `float16_t`, `float32_t`, `float64_t`, `float128_t`, `bfloat16_t` - other implementation-defined extended floating-point types #### void Objects of type `void` are not allowed, neither are arrays of `void`, nor references to `void`. Pointers to `void` and functions returning `void` are allowed. #### std::nullptr_t The type of `nullptr` (the null pointer literal). It's a distinct type that is not itself a pointer type. #### std::byte | Type | Width in bits | Notes | | ----------- | ------------- | ---------------------------------------------------------------------------------------------------------------------- | | `std::byte` | 8-bit | can be used to access raw memory, same as `unsigned char`, but it's not a character type and is not an arithmetic type | #### Character types | Type | Width in bits | Notes | | --------------- | ---------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `char` | 8-bit | multibyte characters; same representation, alignment and signedness as either `signed char` or `unsigned char` (platform-dependent), but it's a distinct type | | `signed char` | 8-bit | signed character representation | | `unsigned char` | 8-bit | unsigned character representation; raw memory access | | `char8_t` | 8-bit | UTF-8 character representation; same size, alignment and signedness as `unsigned char`, but a distinct type | | `char16_t` | 16-bit | UTF-16 character representation; same size, alignment and signedness as `std::uint_least16_t`, but a distinct type | | `char32_t` | 32-bit | UTF-32 character representation; same size, alignment and signedness as `std::uint_least32_t`, but a distinct type | | `wchar_t` | 32-bit on Linux, 16-bit on Windows | wide character representation, holds UTF-32 on Linux and other non-Windows platforms, UTF-16 on Windows. | #### Signed integer types **Standard signed integer types** | Type | Width in bits | | ------------- | ------------------------------------------------------------------------------------------------- | | `signed char` | 8-bit | | `short` | 16-bit | | `int` | 32-bit | | `long` | [LLP64](/proposals/p5448.md#data-models): 32-bit; [LP64](/proposals/p5448.md#data-models): 64-bit | | `long long` | 64-bit | **Exact-width integer types** Typically aliases of the standard integer types. | Type | Width in bits | Defined as | | -------------- | ------------- | ---------------------------------------------------------------------------- | | `std::int8_t` | 8-bit | `typedef signed char int8_t` | | `std::int16_t` | 16-bit | `typedef signed short int16_t` | | `std::int32_t` | 32-bit | `typedef signed int int32_t` | | `std::int64_t` | 64-bit | [LLP64](/proposals/p5448.md#data-models): `typedef signed long long int64_t` | | | | [LP64](/proposals/p5448.md#data-models): `typedef signed long int64_t` | **Fastest minimum-width integer types** Integer types that are usually fastest to operate with among all integer types that have the minimum specified width. | Type | Width in bits | Defined as | | ------------------- | ------------- | --------------------------------------------------------------------------------- | | `std::int_fast8_t` | >=8-bit | `typedef signed char int_fast8_t` | | `std::int_fast16_t` | >=16-bit | implementation dependent | | `std::int_fast32_t` | >=32-bit | implementation dependent | | `std::int_fast64_t` | >=64-bit | [LLP64](/proposals/p5448.md#data-models): `typedef signed long long int_fast64_t` | | | | [LP64](/proposals/p5448.md#data-models): `typedef signed long int_fast64_t` | **Minimum-width integer types** Smallest signed integer type with width of at least N-bits. | Type | Width in bits | Defined as | | -------------------- | ------------- | ---------------------------------------------------------------------------------- | | `std::int_least8_t` | >=8-bit | `typedef signed char int_least8_t` | | `std::int_least16_t` | >=16-bit | `typedef short int_least16_t` | | `std::int_least32_t` | >=32-bit | `typedef int int_least32_t` | | `std::int_least64_t` | >=64-bit | [LLP64](/proposals/p5448.md#data-models): `typedef signed long long int_least64_t` | | | | [LP64](/proposals/p5448.md#data-models): `typedef signed long int_least64_t` | **Greatest-width integer types** Maximum-width signed integer type. | Type | Width in bits | Defined as | | --------------- | ------------- | ----------------------------------------------------------------------------- | | `std::intmax_t` | >=32-bit | [LLP64](/proposals/p5448.md#data-models): `typedef signed long long intmax_t` | | | | [LP64](/proposals/p5448.md#data-models): `typedef signed long intmax_t` | **Integer types capable of holding object pointers** Signed integer type, capable of holding any pointer. | Type | Width in bits | Defined as | | --------------- | ------------- | -------------------------------------------------------------------- | | `std::intptr_t` | >=16-bit | most platforms: `typedef long intptr_t` | | | | some [ILP32](/proposals/p5448.md#data-models):`typedef int intptr_t` | **Other signed integer types** | Type | Width in bits | Defined as | | ----------- | ------------- | ------------------------------------------------- | | `ptrdiff_t` | >=16-bit | most platforms: `typedef std::intptr_t ptrdiff_t` | | | | Holds the result of subtracting two pointers. | #### Unsigned integer types The unsigned integer types have the same sizes as their [signed counterparts](/proposals/p5448.md#signed-integer-types). | Type | Width in bits | Defined as | | -------- | ------------- | ------------------------------------------ | | `size_t` | >=16-bit | most platforms: `typedef uintptr_t size_t` | | | | Holds the result of the `sizeof` operator. | #### Floating-point types **Standard floating-point types** | Type | Format | Width in bits | Note | | ------------- | ------------------------------------------------------------------------------------------------------------------- | ---------------- | --------------------------------------------------------------------------- | | `float` | usually [IEEE-754 binary32](https://en.wikipedia.org/wiki/Single-precision_floating-point_format) | 32-bits | The format or the size can vary depending on the compiler and the platform. | | `double` | usually [IEEE-754 binary64](https://en.wikipedia.org/wiki/Double-precision_floating-point_format) | 64-bits | The format or the size can vary depending on the compiler and the platform. | | `long double` | [IEEE-754 binary128](https://en.wikipedia.org/wiki/Quadruple-precision_floating-point_format) | 128-bit | used by some SPARC, MIPS, ARM64 implementations. | | | [IEEE-754 binary64-extended format](https://en.wikipedia.org/wiki/Extended_precision) | 80-bit or 64-bit | 80-bit (most x86 and x86-64 implementations); 64-bit used by MSVC. | | | [`double-double`](https://en.wikipedia.org/wiki/Quadruple-precision_floating-point_format#Double-double_arithmetic) | 128-bit | used on PowerPC. | **Fixed-width floating-point types (C++23)** They aren’t aliases to the standard floating-point types (`float`, `double`, `long double`), but to an extended floating-point type. | Type | Width in bits | Defined as | | ----------------- | ------------- | ------------------------------ | | `std::float16_t` | 16-bit | `using float16_t = _Float16` | | `std::float32_t` | 32-bit | `using float32_t = _Float32` | | `std::float64_t` | 64-bit | `using float64_t = _Float64` | | `std::float128_t` | 128-bit | `using float128_t = _Float128` | | `std::bfloat16_t` | 16-bit | | ## Proposal - The C++ fixed-width integer types `intN_t` will be the same type as Carbon integer types `iN`. Likewise for `uintN_t` <-> `uN`. - A C++ `builtin type` will be available in Carbon as `Cpp.builtin_type`, for the standard C++ signed/unsigned integer and floating-point types. - A C++ integer `builtin type` that is not the same as `intN_t` or `uintN_t` for any N, will be nameable in Carbon only as `Cpp.builtin_type`. - Different C++ types will be considered different in Carbon, so C++ overload resolution can be handled without issues. ## Details The table of Carbon <-> C++ mappings is as follows: | Carbon | C++ | | ------------------------ | -------------------------------------- | | `()` as a return type | `void` | | `bool` | `bool` | | `i8` | `int8_t` | | `i16` | `int16_t` | | `i32` | `int32_t` | | `i64` | `int64_t` | | `i128` | `int128_t` | | `u8` | `uint8_t` | | `u16` | `uint16_t` | | `u32` | `uint32_t` | | `u64` | `uint64_t` | | `u128` | `uint128_t` | | `Cpp.signed_char` | `signed char` | | `Cpp.short` | `short` | | `Cpp.int` | `int` | | `Cpp.long` | `long` | | `Cpp.long_long` | `long long` | | `Cpp.unsigned_char` | `unsigned char` | | `Cpp.unsigned_short` | `unsigned short` | | `Cpp.unsigned_int` | `unsigned int` | | `Cpp.unsigned_long` | `unsigned long` | | `Cpp.unsigned_long_long` | `unsigned long long` | | `Cpp.float` | `float` | | `Cpp.double` | `double` | | `Cpp.long_double` | `long double` | | `f16` | `std::float16_t (_Float16)` | | `f128` | `std::float128_t (_Float128)` | | TBD | `float32_t`, `float64_t`, `bfloat16_t` | | TBD | `char`, `charN_t`, `wchar_t` | | TBD | `std::byte` | | TBD | `std::nullptr_t` | In addition to the exact mappings above, the following are expected to be the same type due to the different spellings of the types in C++ being the same: | Carbon type | C++ type | | ----------- | --------------------------------------- | | `i8` | `signed char` | | `u8` | `unsigned char` | | `i16` | `short` | | `u16` | `unsigned short` | | `i32` | `int` | | `u32` | `unsigned int` | | `i64` | `long` or `long long` | | `u64` | `unsigned long` or `unsigned long long` | ### C++ -> Carbon mapping details - C++ `intN_t` type will be considered the same type as Carbon's `iN` type. Likewise for `uintN_t` <-> `uN`. - C++ `builtin type` will be available in Carbon inside the `Cpp` namespace under the name `Cpp.builtin_type`, for the standard signed/unsigned integer and floating-point types. - The names will follow the pattern: - `Cpp.[unsigned_](long_long|long|int|short|double|float)` that is signedness, then size keyword(s), then a type keyword only if there are no size keywords. For example `Cpp.unsigned_int` not `Cpp.unsigned`, `Cpp.long` not `Cpp.long_int`. - They will be available when an `import Cpp` declaration is present. - Name collision: This naming may cause name collisions if such a name already exist in the unnamed C++ namespace. We consider this not to be a common case and would not support such cases, for the benefit of having the C++-specific stuff in the package `Cpp`. - `Cpp.builtin_type` will be the same type as `iN`/`uN`, if the corresponding C++ `builtin type` is the same as `intN_t`/`uintN_t` on that platform. Otherwise it will be available in Carbon as a new, distinct type that is compatible with some of the `iN`/`uN` types. For example: - If `int32_t` is the same type as `int`, then `Cpp.int` will be the same type as `i32`. - If `int64_t` is the same type as `long`, then `Cpp.long` will be the same type as `i64`. `Cpp.long_long` will be a different type, compatible with `i64`. - `Cpp.float` and `Cpp.double` will be the same type as `f32` and `f64` correspondingly. - The type aliases `[u]int_fastN_t`, `[u]int_leastN_t`, `[u]intmax_t`, `[u]intptr_t`, `ptrdiff_t` and `size_t` will be available in Carbon in the `Cpp` namespace if the C++ header declaring them is imported (for example ``, `` etc), with names like `Cpp.[u]int_fastN_t`, `Cpp.[u]int_leastN_t`, `Cpp.size_t` etc. No special support will be provided. ### Carbon -> C++ mapping details - Same as above, Carbon `iN`/`uN` types will map to the C++ `intN_t`/`uintN_t` types. - `f32`/`f64` will map to `float`/`double` correspondingly. - `f16`/`f128` will map to `std::float16_t (_Float16)`/`std::float128_t (_Float128)` correspondingly. - Some Carbon types may not have direct mappings in C++: `i256`, `u256` , `f80`, `f256`. ## Rationale One of Carbon's goals is seamless interoperability with C++ (see [Interoperability with and migration from existing C++ code](/docs/project/goals.md#interoperability-with-and-migration-from-existing-c-code)), calling for clarity of the calls and high performance. The proposal maps the Carbon types to their direct equivalents in C++, with zero overhead, supporting the request for unsurprising mappings between C++ and Carbon types with high performance. ## Alternatives considered Naming of new types: - Allow all keyword permutations. - Reason not to do this: unnecessary and complicated. - Only include the keywords, and provide some syntax for combining them (eg, `Cpp.unsigned` & `Cpp.long` or `Cpp.unsigned(Cpp.long)`). - Reason to do this: avoids taking any identifiers from Cpp that are not C++ keywords. - Reason not to do this: overly complicated. - Use `Core.Cpp.T` instead of `Cpp.T`. - Reason to do this: avoid name collisions with C++ code. - Reason not to do this: The name collisions should not be a problem in practice, and would prefer to keep C++-specific stuff in package Cpp. `long` - `Cpp.long` and `Cpp.long_long` both map to Carbon types that are distinct from `iN` for any `N`, but are compatible with either `i32` or `i64` as appropriate. - Reason to not do this: unnecessary conversions and handling `long` and `long long` differently than the other C++ types. - Provide platform-dependent conversion functions for `long`. - Reason to do this: the conversions will be clearly outlined. - Reason not to do this: performance overhead for certain platforms. - Map `long` always to a fixed-sized Carbon type depending on the platform (for example to either `i32` or `i64`) - Reason to do this: all the code will be using fixed-sized types. - Reason not to do this: the same C++ function may map differently on different platforms and the Carbon code should compensate for that to make the code compile. `float32_t`, `float64_t` - Map `f32` <-> `float32_t` and `f64` <-> `float64_t` - Reason to do this: follow the same analogy as for the integer types (`iN` <-> `intN_t`) - Reason not to do this: - `float32_t`, `float64_t` are new types since C++23, so this won't be directly achievable, but the corresponding `_FloatN` types will need to be used for the older C++ versions. - they are not aliases for the standard floating-point types (`float`, `double`, `long double`), but for extended floating-point types, so type conversions will be needed for the standard types. ## Open questions The mapping of the following types remains open and will be discussed at a later point: - `char`, `char8_t`, `char16_t`, `char32_t`, `wchar_t` - Carbon still doesn't have character types, so the mapping of these types will be discussed once they are available. - These are all distinct types in C++, which should be taken into account to prevent any issues for overloading. - `std::byte` - `std::nullptr_t` - `void*` - `Cpp.long_double` - details of this new type is still to be discussed. - `float32_t`, `float64_t`, `bfloat16_t`. ================================================ FILE: proposals/p5545.md ================================================ # Expression form basics [Pull request](https://github.com/carbon-language/carbon-lang/pull/5545) ## Table of contents - [Abstract](#abstract) - [Problem](#problem) - [Background](#background) - [Proposal](#proposal) - [Details](#details) - [Rationale](#rationale) - [Alternatives considered](#alternatives-considered) - [Mixed expression categories](#mixed-expression-categories) - [Don't implicitly convert to less-primitive forms](#dont-implicitly-convert-to-less-primitive-forms) - [Breadth-first evaluation order](#breadth-first-evaluation-order) - [Depth-first evaluation with a different "horizontal" order](#depth-first-evaluation-with-a-different-horizontal-order) - [Support binding `ref self` to ephemeral references](#support-binding-ref-self-to-ephemeral-references) ## Abstract This proposal introduces the concept of a _form_, which is a generalization of "type" that encompasses all of the information about an expression that's visible to the type system, including type and expression category. Forms can be composed into _tuple forms_ and _struct forms_, which lets us track the categories of individual tuple and struct literal elements. ## Problem It's unclear what expression category tuple and struct literals should have. For example, this code can only compile if the tuple literal is an initializing expression: ```carbon var t: (NonMovable, NonMovable) = (MakeNonMovable(), MakeNonMovable()) ``` But this code can only compile if the tuple literal is a value expression: ```carbon let x: NonCopyable = MakeNonCopyable(); let t: (NonCopyable, NonCopyable) = (x, MakeNonCopyable()); ``` And there's plausible code that can't compile if the tuple literal has _any_ single expression category: ```carbon let x: NonCopyable = MakeNonCopyable(); let (a: NonCopyable, var b: NonMovable) = (x, MakeNonMovable()); ``` At present it's always possible to rewrite examples like that to avoid the problem by disaggregating the tuple patterns into separate statements. However, when the copy and move operations in question are expensive rather than outright disabled, those examples will result in silent inefficiency rather than a noisy build failure, which is less harmful but easier to overlook. ## Background The Carbon toolchain already implements a solution to this problem: it treats tuple and struct literals as having a "mixed" expression category, and when individual elements of the literal are accessed (such as during pattern matching), the element's original category is propagated. Proposal [#5434](https://github.com/carbon-language/carbon-lang/pull/5434) introduces plausible use cases that cannot compile if we assign any single expression category to a tuple or struct literal, and there is no way to avoid the problem by rewriting. For example: ```carbon fn F() -> (ref NonCopyable, NonMovable); let (a: NonCopyable, var b: NonMovable) = F(); ``` ## Proposal This proposal solves that problem by introducing the concept of a _form_, which is a generalization of "type" that encompasses all of the information about an expression that's visible to the type system, including type and expression category. In the common case, an expression has a _primitive form_ which consists of a type, an expression category, and a few other properties. However, a tuple literal has a _tuple form_, which is a tuple of the forms of its elements. This allows us to directly represent the fact that different elements have different categories, and propagate that difference into operations that access those elements. In order to help describe the semantics of forms, this proposal also formalizes the concept of the _result_ of an expression evaluation. Results are a generalization of values and references in the same way that forms are a generalization of types. In this proposal they are primarily a descriptive convenience, but they are also intended to function as the thing that a form-generic binding binds to, when that is proposed. The results of initializing expressions, called _initializing results_, have somewhat subtle semantics. Results present an idealized model of expression evaluation where information flows from each expression to the context where it is used, but initializing expressions require information to flow in both directions: the context supplies a storage location, and then the expression supplies the contents of that storage location. We finesse this "impedance mismatch" by saying that the initializing result represents an obligation on the context to supply a storage location (somewhat like a callback or `std::promise`), which it must fulfill by either materializing or transferring the result. Furthermore, even though this formally happens after the expression is evaluated, it is constrained in such a way that it can actually be computed beforehand and passed to the expression's hidden output parameter. This proposal also integrates type, category, and phase conversions into a unified set of rules for form conversions. Notably, those rules call for conversions to be evaluated depth-first (along with the expressions they convert from and the pattern-matches they feed into), and for struct conversions to use the field order of the source, not the target. Finally, this proposal splits the reference expression categories into _entire_ and _non-entire_ references, where an entire reference is known to refer to a complete object. This lets us decouple materialization (which now produces an ephemeral entire reference) from `var` binding (which now expects an ephemeral entire reference), which lets us resolve a TODO to allow an initializing expression to be destructured into multiple `var` bindings. In the process of doing this, it became clear that the special case that allowed `ref self` patterns to match ephemeral references was not internally consistent, so that special case has been removed. We will need some way of supporting the use cases that were intended to be covered by that rule, but that is being left as future work. ## Details See the edits in the [pull request](https://github.com/carbon-language/carbon-lang/pull/5545) associated with this proposal, particularly in `values.md`. ## Rationale This proposal supports [performance-critical software](/docs/project/goals.md#performance-critical-software) and making [code easy to read, understand, and write](/docs/project/goals.md#code-that-is-easy-to-read-understand-and-write) by ensuring that tuple and struct literals don't introduce unnecessary category conversions (which may cause build failures and performance overhead). ## Alternatives considered ### Mixed expression categories This proposal models an expression like `(x, MakeNonMovable())` from the earlier example as having a tuple form consisting of primitive forms with `NonCopyable` and `NonMovable` types (respectively) and "value" and "initializing" categories (respectively). We could instead support composite expression categories, so that it has type `(NonCopyable, NonMovable)` and expression category `(value, initializing)`. This would avoid the need to introduce the concept of "form", and preserve the existing separation between types and categories. That separation would be somewhat superficial (for example, an expression couldn't have a tuple expression category if it doesn't have a tuple type), but no more so than the separation between types and values. However, we expect to need an explicit syntax to express these properties of expressions, for example to define functions that return tuples whose elements have different categories. A syntax consisting of separate type and category tuples will be much less ergonomic, and much easier to misuse, than a syntax that combines both in a single tuple (for example, [#5434](https://github.com/carbon-language/carbon-lang/pull/5434) represents the form of `(x, MakeNonMovable())` as `(val NonCopyable, NonMovable)`). Furthermore, we anticipate needing to support code that is generic with respect to forms, not just with respect to types. We plan to achieve that with parameters of a special "form" type together with ways of deducing and using them. It might be possible to instead support category parameters that are deduced and used in conjunction with type parameters, but that would be syntactically onerous, and oblige users to keep each category correctly paired with the corresponding type, in order to bring them together at the point of use. Those challenges will be further compounded when/if it becomes possible to manipulate types and categories by way of metaprogramming. Given that we need to present forms as an integrated whole at the syntactic and metaprogramming levels, there is very little to be gained by decoupling them at the level of language semantics. ### Don't implicitly convert to less-primitive forms Consider the following possible ways of initializing an array, where there is an implicit conversion from integer literals to `X`: ```carbon impl Y as Core.ImplicitAs(X); let a: array(X, 3) = (MakeY(), MakeY(), MakeY()); let x_tuple: (X, X, X) = (MakeY(), MakeY(), MakeY()); let b: array(X, 3) = x_tuple; ``` Under this proposal, both `a` and `b` are valid. However, some people's intuition is that a tuple is different enough from an array that there should not be implicit conversions between them in general. In this mental model, a tuple-form expression wouldn't represent a tuple per se; rather, it would abstractly represent a sequence of results, which can be used to initialize either a tuple or an array (or a user-defined type). Similarly, a struct-form expression wouldn't represent a struct per se, but rather a more abstract sequence of _named_ results. Consequently, there would be no implicit conversion from a tuple value to a tuple form, and so `array` could disallow `b` by requiring its initializer to have a tuple form. Perhaps more importantly, making this distinction could simplify C++ interop, because we could map C++ braced initializer lists to tuple and struct forms, while mapping C++ `std::pair`s and `std::tuple`s to primitive-form Carbon tuples, and mapping C++ structs to primitive-form Carbon structs. We cannot do that under the status quo, because Carbon's implicit conversions from primitive to composite forms would have no C++ counterpart, and so overload resolution behavior would change too radically when crossing the language boundary. However, consider the following examples: ```carbon let c: array(X, 3) = (MakeY(), MakeY(), MakeY()) as (X, X, X); let y_tuple: (Y, Y, Y) = (MakeY(), MakeY(), MakeY()); let d: array(X, 3) = y_tuple as (X, X, X); ``` Presumably we would want the declaration of `c` to be valid, because it just makes the implicit type conversion from `a` explicit. That result emerges pretty naturally, because the `as` conversion operates element-wise on its tuple-form input, so its output would likewise have a tuple form. On the other hand, the declaration of `d` should not be valid, because it just makes the implicit type conversion from `b` explicit. That result does not emerge naturally; instead, we have to add a final form composition step at the end of the type conversion, to ensure the result has a primitive form (but only when the input had a primitive form). But that means we need to choose the category of that primitive form, that is whether the conversion from `(Y, Y, Y)` to `(X, X, X)` is a value expression or an initializing expression (form composition can't produce reference expressions). Upcoming proposals are expected to enable the user to define the implicit conversion from `Y` to `X` to have any category, so suppose that the conversion from `Y` to `X` is a reference expression. In that case, the requirement to convert to a primitive form breaks some use cases that would otherwise be valid: ```carbon let (ref s1: X, ref s2: X, ref r3: X) = y_tuple as (X, X, X); ``` Even for usages that are not broken outright, this conversion may add substantial inefficiency, depending on which category we convert to: ```carbon let (p1: X, p2: X, p3: X) = y_tuple as (X, X, X); ``` This requires 3 value acquisitions in either case, but if we convert to an initializing expression it also requires 3 copy-init and materialization steps. ```carbon let (var q1: X, var q2: X, var q3: X) = y_tuple as (X, X, X); ``` In either case we're starting and ending with the object representation of `X`, passing through the initializing representation (which is typically very closely tied to the object representation), but if the type conversion produces a value expression we also pass through the value representation (which can be much less trivial to convert to and from). Finally, examples like this one are less efficient if we have to convert to a primitive form, regardless of which category we convert to: ```carbon let (r1: X, var r2: X, var r3: X) = y_tuple as (X, X, X); ``` Some but not all of these problems can be mitigated by making the target category an input to the type conversion, but that has a number of unwelcome consequences: - It adds complexity to the conversion APIs that user-defined types will almost never need. - We would need to change the syntax for `x as T` so that it specifies the category as well as the type. - It would enable user-defined conversions to produce different values depending on the target category, which we definitely don't want. - It makes overload resolution more complicated and more surprising. This alternative was considered and rejected in [leads issue #6160](https://github.com/carbon-language/carbon-lang/issues/6160). ### Breadth-first evaluation order Under this proposal, the observable effects of a pattern-matching operation take place in depth-first order (with respect to tuple and struct elements), but breadth-first order would have several advantages: - It would help enable us to define tuple type conversions as ordinary impls of the type conversion APIs, because modeling conversion as a function call is inherently breadth-first. Opting for depth-first evaluation seems to rule that out. - It would give us more options for supporting struct and class conversions where the field orders doesn't match. For example, the previously-preferred approach was to evaluate the source expression in its own field order, but then perform conversions in the target's field order. This seemed to provide a good balance of efficiency and ergonomics, but it's inherently breadth-first. With the depth-first approach, we have to use a single field order. - It would simplify the specification, because the logical semantics of conversion and pattern matching are much more naturally described breadth-first, so opting for depth-first creates an "impedance mismatch" between the logical and physical semantics. However, breadth-first evaluation order has a crucial efficiency cost: in all but the most trivial use cases, it forces (and in some sense even maximizes) lifetime overlap between the results of the function calls that make up the pattern-matching operation. That means it requires more temporary storage, and more work to manage that storage (particularly at the register level). For example, consider this [generated code](https://cpp.compiler-explorer.com/z/Pxaar4edb) for C++ approximating the two options. The `LayerWise` function templates model the breadth-first evaluation order, while the `ElementWise` model the proposed depth-first order. Looking at the two element case for ARM: ```asm void LayerWise(S1, S1): stp x29, x30, [sp, #-32]! stp x20, x19, [sp, #16] mov x29, sp mov x19, x1 bl Convert1(S1) mov x20, x0 mov x0, x19 bl Convert1(S1) mov x19, x0 mov x0, x20 bl Convert2(S2) mov x20, x0 mov x0, x19 bl Convert2(S2) mov x1, x0 mov x0, x20 ldp x20, x19, [sp, #16] ldp x29, x30, [sp], #32 b void Target(S1, S1) void ElementWise(S1, S1): stp x29, x30, [sp, #-32]! stp x20, x19, [sp, #16] mov x29, sp mov x19, x1 bl Convert1(S1) bl Convert2(S2) mov x20, x0 mov x0, x19 bl Convert1(S1) bl Convert2(S2) mov x1, x0 mov x0, x20 ldp x20, x19, [sp, #16] ldp x29, x30, [sp], #32 b void Target(S1, S1) ``` The breadth-first approach forces significantly more data movement (the extra moves between `x20` and `x19`) and temporary registers. This distinction continues in versions with more elements, making the problem more and more severe. This is an inherent cost, and not something we can realistically expect an optimizing compiler to recover. Furthermore, we don't see any general way for developers to work around this problem, and achieve the performance they would get automatically with depth-first evaluation. On the other hand, with depth-first evaluation within pattern-matching operations, developers can still ensure a breadth-first evaluation order relatively easily, by expressing each "layer" as a separate pattern-matching operation (for example with a sequence of statements, or a chain of function calls). In some cases, this may involve move operations that could be elided in a language-native breadth-first evaluation, but that depends on the to-be-determined design of move semantics. In short, the ergonomic costs of depth-first evaluation appear to be manageable, whereas the performance cost of breadth-first evaluation is unavoidable (and carries more weight, because supporting [performance-critical software](/docs/project/goals.md#performance-critical-software) is our top goal for the language). This alternative was considered and rejected in [leads issue #6456](https://github.com/carbon-language/carbon-lang/issues/6456). ### Depth-first evaluation with a different "horizontal" order To order operations that don't have a dependency relationship, this proposal uses a hybrid scheme that follows both the lexical order of the primitive patterns and the lexical order of the scrutinee function calls (diagnosing an error if they conflict), and also follows the lexical order of the scrutinee's declared type to the extent that it doesn't conflict with the primitive pattern order. One potential drawback of this rule is it means that it means that, unlike C++, Carbon doesn't guarantee that the fields of an object are initialized in declaration order (or in any fixed order), and so it can't guarantee that they'll be destroyed in reverse order of initialization. We could solve that problem by guaranteeing to evaluate in the scrutinee type's field order. However, that would mean evaluating function calls out of lexical order in cases like this: ```carbon var ab: {.a: A, .b: B} = {.b = MakeB(), .a = MakeA()}; ``` Evaluating `MakeA()` before `MakeB()` risks causing surprises, or even bugs, and it would mean that the evaluation order within a single expression depends on how it's used. Instead, we adopt a rule that ensures side effects are evaluated in lexical order within both the pattern and the scrutinee. We expect that to be sufficient for struct types, where destruction order is unlikely to be an issue. Class types may be more sensitive to these ordering issues, so we may also need some way for the class to disallow initializers whose field order doesn't match the class, but this is left as future work. ### Support binding `ref self` to ephemeral references `ref` patterns can only match durable reference expressions, but prior to this proposal, `ref self` patterns could match ephemeral references as a special-case exception. This was intended to support certain C++ idioms that rely on materializing a temporary and then mutating it in place, such as fluent builders. For example: ```carbon class FooBuilder { // These methods mutate `self` and then return a reference to it. fn SetBar[ref self: Self]() -> ref Self; fn SetBaz[ref self: Self]() -> ref Self; fn Build[ref self: Self]() -> Foo; } fn MakeFoo() -> FooBuilder; let foo: Foo = MakeFoo().SetBar().SetBaz().Build(); ``` Prior to this proposal, this code would be valid: `MakeFoo()` is an initializing expression, but when it is matched with `ref self: Self` as part of the `SetBar` call, it is implicitly converted to an ephemeral reference, and then the special-case rule allows `ref self: Self` to bind to the materialized temporary. However, those rules also imply that code like this would be valid: ```carbon let builder: FooBuilder = MakeFoo(); builder.SetBar(); builder.SetBaz(); let foo: Foo = builder.Build(); ``` Here the programmer has accidentally used `let` instead of `var`, so `builder` is an immutable value. But a value expression can be implicitly converted to an initializing expression by direct initialization, and as we already saw, it's valid to call `MakeFoo()` and `MakeBar()` on an initializing expression. So this code repeatedly materializes, mutates, and then discards a copy of `builder`, and then ultimately initializes `foo` with the state returned by `MakeFoo()`, which is surely not what the programmer intended. This sort of "lost mutation" bug is exactly what the distinction between durable and ephemeral references was intended to prevent, but the `ref self` special case combines with the transitivity of category conversions to defeat that protection. A proper resolution of this issue seems beyond the scope of this proposal, so this proposal removes that special case without replacement, leaving the problem of supporting idioms like fluent builders as future work. ================================================ FILE: proposals/p5606.md ================================================ # Keep design documents current [Pull request](https://github.com/carbon-language/carbon-lang/pull/5606) ## Table of contents - [Abstract](#abstract) - [Problem](#problem) - [Proposal](#proposal) - [Details](#details) - [Rationale](#rationale) - [Alternatives considered](#alternatives-considered) - [Require documentation updates as part of the proposal](#require-documentation-updates-as-part-of-the-proposal) - [One TODO per document](#one-todo-per-document) - [Link to the PR instead of the proposal document](#link-to-the-pr-instead-of-the-proposal-document) ## Abstract Require language design proposals to either update the design documents to reflect the proposed changes, or add "TODO" comments to mark where those changes will be needed, with links back to the proposal. This is intended to ensure that the design documentation accurately informs readers about the current language design, without excessively burdening the proposal process. ## Problem The current evolution process allows us to adopt language design changes while deferring the corresponding changes in `/docs/design`. We have no process for ensuring that those follow-up changes actually happen, and in practice some adopted proposals have gone for well over a year without corresponding design document changes. This problem is not limited to proposals for new features, but also applies to proposals to change existing, documented features. As a result, the design documents are not merely incomplete, but in some cases actually misleading, which has led to miscommunication and wasted effort within the Carbon team. ## Proposal This proposal modifies our evolution process to require proposals to either implement the corresponding changes to the design documents, or mark the places that will need to be changed with "TODO" comments that point back to the proposal. The presence of those comments should help ensure that readers are not misled by the design documentation, and the links to the proposals should help ensure readers can discover the actual design for a given feature with reasonable effort. The proposal PR also adds "TODO" comments to `/docs/design`, both as an illustration of what this policy asks for, and because the policy needs to be applied retroactively in order to actually solve the problem. However, these changes are necessarily best-effort, because it wasn't feasible for me to fully evaluate every proposal against the current state of the docs. In particular, I didn't look at proposals numbered below 2000, and I assumed that proposals fully updated `/docs/design` if they touched it at all. In addition, I did not add "TODO" comments for the terminology changes in [p2964](/proposals/p2964.md), because they would be pervasive, and probably add little value for the reader. Instead, I filed issue [#5599](https://github.com/carbon-language/carbon-lang/issues/5599) to track the task of making those changes. ## Details See the changes in the proposal PR. ## Rationale This proposal will help advance our goal of [promoting a healthy and vibrant community with an inclusive, welcoming, and pragmatic culture](/docs/project/goals.md#community-and-culture): in order to effectively participate in extending, implementing, or evaluating the design of Carbon, people need to be able to find accurate information about that design (and avoid inaccurate information) with reasonable effort. ## Alternatives considered ### Require documentation updates as part of the proposal We could require all language design proposals to implement the corresponding changes in `/docs/design`. This would more thoroughly address the risk of readers being misled, because you might mistakenly read documentation that's marked with a "TODO" comment, but you can't mistakenly read documentation that doesn't exist anymore. This would also push proposals to be more concrete, detailed, and fully integrated with the rest of the language. However, that's a double-edged sword: it could help us identify problems with a proposal before it's adopted, but it could also close off the option of deferring those problems to future work in order to make incremental progress. More mundanely, it would also increase both the up-front cost of creating a proposal, and the cost of iteratively changing it during review. The loss of agility from those factors is likely to outweigh the somewhat tenuous and speculative benefits of this approach, at least at this early stage of Carbon's development. ### One TODO per document This policy intentionally encourages fairly granular TODO comments attached to the specific passages where changes are needed (see especially the proposed changes in [`details.md`](https://github.com/carbon-language/carbon-lang/pull/5606/files#diff-b84aebe5ad22a2be2b4c222cf68fd93981ebcd2451bafb56ed5fe46ec186a3c8)). We could instead encourage having a single TODO comment per document, in order to reduce the burden on proposal authors. However, this would have several related drawbacks for readers of the documentation: - It increases the risk that the reader will overlook the TODO comment altogether, because it may be quite far away from the passage they're reading. - It increases the risk of false positives, where passages that remain valid are caught up in the scope of a file-level TODO comment. - It may force the TODO comment to be less specific about how the proposal changes the content of the document. This compounds the previous problem, because it means the reader has to do more work to distinguish true from false positives. Together, these drawbacks could severely undermine the purpose of this proposal. By contrast, the burden of granular TODOs on proposal authors seems relatively marginal. For example, I was able to add TODO comments for 8 proposals in a few hours, despite in most cases being quite unfamiliar with their contents. ### Link to the PR instead of the proposal document This policy specifies that the TODOs should include a link to the proposal document, but we could instead link to the proposal PR. This would be more consistent with existing documentation, which rarely links to proposal documents (among other things, this makes it easier to find all references to a given proposal). The PR also surfaces some information not available in the proposal document, such as the discussion associated with a proposal, and the other places where TODOs were added. Finally, the PR UI gives you access to a rendered view of the proposal document (accessible through "View file" in the drop-down menu for the file), in which links to other documents take you to the versions that existed as of when the proposal was committed, which may help clarify the historical context of the proposal if those documents have changed in the meantime. However, the TODO links serve a very different purpose than other proposal links: when we link to a proposal in a "References" or "Alternatives considered" section of a document, we're providing interested readers with supplementary information about the rationale and history of the design being documented, and for those purposes the additional information in the PR is directly relevant. Here, by contrast, the proposal is acting as the sole documentation for _the design itself_, and for that purpose all the relevant information is in the proposal document itself. Finally, in the event that the reader wants additional information from the PR, every proposal document has a prominent PR link at the top, so the additional burden on those readers is a single click. By contrast, the link from a proposal PR to the corresponding rendered document is hidden in a drop-down menu somewhere in the middle of the PR's "files changed" page. ================================================ FILE: proposals/p5661.md ================================================ # Progressive disclosure principle [Pull request](https://github.com/carbon-language/carbon-lang/pull/5661) ## Table of contents - [Abstract](#abstract) - [Problem](#problem) - [Proposal](#proposal) - [Rationale](#rationale) ## Abstract This proposal codifies our preference for designs that support "progressive disclosure", meaning that programmers can ignore a given language concept (or even be unaware of it) until it is directly relevant to the task they're doing. ## Problem Carbon's design choices have been motivated in part by a desire for "progressive disclosure", but that approach has not been codified as a design principle. ## Proposal See (`docs/project/principles/progressive_disclosure.md`)[/docs/project/principles/progressive_disclosure.md], which is introduced by this proposal. ## Rationale In order to have a vibrant [community and culture](/docs/project/goals.md#community-and-culture), Carbon needs to be effectively teachable and learnable. Progressive disclosure supports that goal by minimizing the amount that must be learned at any one time, and by enabling concepts to be introduced when they are most salient to the programmer. Relatedly, although ["lies-to-children"](https://en.wikipedia.org/wiki/Lie-to-children) can be an appropriate teaching tool, they also risk undermining the health of the community by creating distance between those who know the full truth and those who have only been taught the "lie". This principle helps minimize that risk, through the expectation that progressive disclosure not invalidate the programmer's prior understanding of the language. Finally, this principle helps make Carbon code [easy to read, understand, and write](/docs/project/goals.md#code-that-is-easy-to-read-understand-and-write), by minimizing the amount of Carbon knowledge that a programmer needs in order to read, understand, or write a given piece of code. ================================================ FILE: proposals/p5670.md ================================================ # Guidance on AI coding tools [Pull request](https://github.com/carbon-language/carbon-lang/pull/5670) ## Table of contents - [Abstract](#abstract) - [Problem](#problem) - [Background](#background) - [Proposal](#proposal) - [Rationale](#rationale) - [Alternatives considered](#alternatives-considered) - [Place more restrictions around AI-based tool usage when contributing](#place-more-restrictions-around-ai-based-tool-usage-when-contributing) ## Abstract Establish some guidance on using AI coding tools when contributing to the Carbon Language project. These tools have growing popularity and interest, and it would be good to have a clear and actively documented set of guidance for folks interested or already using them. ## Problem AI-based coding tools are wildly popular at this point and we should have active guidance about how and when to use them when contributing to Carbon rather than being reactive. ## Background - [LLVM Developer Policy guidance on AI generated code](https://llvm.org/docs/DeveloperPolicy.html#ai-generated-contributions) ## Proposal All submissions to Carbon need to follow our Contributor License Agreement (CLA), in which contributors agree that their contribution is an original work of authorship. This doesn’t prohibit the use of coding assistance tools, but what’s submitted does need to be a contributor’s original creation. Carbon's license was also selected specifically to maintain full compatibility between any contributions to Carbon and contributions to LLVM so that we can potentially move things between these projects easily. We want the same to be true regarding the use of AI-based coding tools, and so any contributions to Carbon should also abide by the guidance in the [LLVM Developer Policy around AI generated code](https://llvm.org/docs/DeveloperPolicy.html#ai-generated-contributions) This proposal updates our contributing documentation to contain both these points. ## Rationale - [Community and culture](/docs/project/goals.md#community-and-culture) - Explicitly documenting what is and isn't required when contributing to Carbon makes the project more open and welcoming to new contributors. ## Alternatives considered ### Place more restrictions around AI-based tool usage when contributing We could consider adopting more restrictions on how AI-based tools can be used as part of contributing to Carbon. However, at this point we don't have a compelling rationale for this. There are real concerns around the quality of code output from AI-based tools in some cases. However, we should not rely on avoiding the tools to provide protection from low-quality code. Instead, as we already do, we have a system of code review, coding standards, and extensive testing to ensure high-quality contributions regardless of the tools used. **The quality, correctness, and utility expectations of contributions are true regardless of which tools are used as part of authoring the contribution.** We have also seen abuse where large volumes of automatically generated "contributions" have been sent to projects, overwhelming their community. Again, this is never OK, regardless of which tools are used to achieve it. We hold the contributors responsible for using any and all tools responsibly and making useful and constructive contributions with them. Last but not least, we also understand that there may contributors who do not wish to use these tools. Currently, we are not proposing any tools in the required workflow of the project. ================================================ FILE: proposals/p5689.md ================================================ # Semantic Identity and Order-Dependent Resolution for Rewrite Constraints [Pull request](https://github.com/carbon-language/carbon-lang/pull/5689) ## Table of contents - [Abstract](#abstract) - [Problem](#problem) - [Background](#background) - [Proposal](#proposal) - [Rationale](#rationale) - [Canonicalization](#canonicalization) - [Alternatives considered](#alternatives-considered) - [Order independence](#order-independence) - [Syntactic equivalence](#syntactic-equivalence) ## Abstract The rules for rewrite constraint resolution say that when there are multiple assignments to the same associated constant in a facet type, they must all be identical. But it does not clearly identify whether that means syntactically identical or semantically. And if semantically, whether that is before or after substituting from the RHS of other rewrite constraints. ## Problem Previously we had been working with the intention of requiring syntactical equivalence for all rewrite constraints within a facet type that assign to the same associated constant. However this is highly problematic for implementation in the toolchain, since we lose syntactic information when we create the semir of the rewrite constraints. In particular, once a facet type is fully constructed, all syntactic information is lost, as individual instructions are evaluated and their canonical constant values are what is stored in the facet type in order to make facet types themselves canonical. This problem becomes most visible when applying [the `&` operation](https://docs.carbon-lang.dev/docs/design/generics/details.html#combining-interfaces-by-anding-facet-types) to combine two facet types with rewrite constraints of the same associated constant, such as: `(I where .X = ()) & (I where .X = .Y and .Y = ())`. Here the facet type on the right will be resolved to `I where .X = () and .Y = ()` before applying the `&` operation, so we have lost syntactic knowledge that `.X` was being assigned `.Y`. That makes things inconsistent if we consider it an error to give `.X` two differently-written values in a single facet type, such as `I where .X = () and .X = .Y and .Y = ()`. Therefore we would like to consider a semantic identity requirement instead, however there are soundness issues, implementation-specific inconsistencies, or diagnostic inconsistencies, unless we carefully specify how rewrite constraints are applied through substitution when checking for conflicting assignments and cycles between rewrite constraints. For example, given `I where .Y = () and .Y = .X and .X = .Y`: - If `.X = .Y` is resolved to `.X = ()` first, then the two assignments to `.Y` are not in conflict and can be considered identical, and no cycle is found. - If `.Y = .X` is resolved to `.Y = .Y` first, however, we find a cycle and diagnose this as an error. - If `.X = .Y` is resolved to `.X = .X` by using the second constraint for `.Y` instead of the first one, we also find a cycle and diagnose an error. That leaves us with the question of whether this facet type contains a cycle `.Y = .Y` after being resolved, or contains two identical assignments of `.Y = ()` and one of `.X = .Y`. One question is whether we should have any reliance on ordering of rewrite constraints in resolution. In other words, whether it's okay that reordering the rewrite constraints changes whether a cycle is found or not. If ordering should not affect the determination of cycles, then we have a choice for what to look for: - Should we always use an ordering that finds a cycle, if such an ordering exists? - Should we always use an ordering that avoids a cycle, if such an ordering exists? If we are okay with the ordering of rewrite constraints affecting the detection of cycles, then we need to precisely specify how the ordering works in order to make implementations consistent: - In what order are the RHS of rewrite constraints resolved? - When substituting for a reference to another rewrite constraint, in what order do we choose a constraint to substitute from? ## Background - [Design: Rewrite constraint resolution](https://github.com/carbon-language/carbon-lang/blob/67b67af7a61b9cea1d47c3a1009f78ac00790a47/docs/design/generics/appendix-rewrite-constraints.md?plain=1#L195-L304). - [Open discussion notes from 2025-06-17](https://docs.google.com/document/d/1Yt-i5AmF76LSvD4TrWRIAE_92kii6j5yFiW-S7ahzlg/edit?tab=t.0#heading=h.qti4vn50zwy). - Conversation on [this proposal's PR](https://github.com/carbon-language/carbon-lang/pull/5689), in particular [this comment thread](https://github.com/carbon-language/carbon-lang/pull/5689#discussion_r2164881833). - Discord discussion from #generics-and-templates on [2025-06-17](https://discord.com/channels/655572317891461132/941071822756143115/1384632814196101171) and [2025-06-25](https://discord.com/channels/655572317891461132/941071822756143115/1387499929056055469). ## Proposal We propose to allow ordering of rewrite constraints to affect cycle detection, with the following orderings specified when resolving: - Rewrite constraints should be resolved left-to-right. - When a reference to another rewrite constraint is found, it should be substituted with the first matching rewrite constraint found from left-to-right. - When combining two facet types `LHS & RHS`, the LHS facet type's rewrite constraints are ordered before the RHS facet type's constraints when resolving the combined facet type. Effectively, the rewrite constraints from the RHS are concatenated onto the constraints from the LHS, then the facet type is resolved. ## Rationale We believe that order dependence is in line with the design direction of the language overall. In particular, this aligns with the [information accumulation principle](/docs/project/principles/information_accumulation.md). The principle says: > If a program attempts to use information that has not yet been provided, the > program is invalid. This suggests that looking ahead for a rewrite constraint to avoid a cycle is an invalid operation. However, it also states that: > Carbon programs are invalid if they would have a different meaning if more > information were available. This might seem to suggest that the meaning can not change when the order in which code is written changes. But here we take _meaning_ to refer to the resolved state of all associated constants when there are no errors present. Using ordering to avoid cycles and prevent error diagnostics is a common practice in Carbon, such as with the use of forward declarations. This proposal makes the ordering effect whether a cycle is found and diagnosed, but in the absence of a cycle, the ordering does not affect the resulting values of the associated constants. Using a prescribed ordering to resolve and find cycles simplifies the implementation, avoiding the need to consider all `n!` orderings of `n` constraints. It also simplifies explaining the language rules, as you can step through the resolving algorithm without excessive branching choices, much like you would step through lines of code in a debugger. Using a left-to-right order is consistent with how Carbon is parsed and executed generally. If there were multiple statements written on the same line, they would also be executed left-to-right. ### Canonicalization A key part of toolchain implementation is the ability to canonicalize constant values, including facet types. This means that once resolved, the order of the rewrite constraints must be able to be effectively lost, as different orderings would all result in the same canonical facet type. After being resolved, a facet type rewrite constraints have two key properties: - Each associated constant appears at most once on the LHS of a rewrite constraint. There are no duplicate assignments. - Each associated constant that appears on the RHS of a rewrite constraint does not appear on the LHS of any rewrite constraint. There are no relationships between rewrite constraints. When combining two facet types with `LHS & RHS` we said that the rewrite constraints of the LHS come before the rewrite constraints of the RHS. Since the LHS and RHS are each fully resolved before being combined, canonicalization requires that the rewrite constraints within the LHS and the RHS can be ordered in arbitrary ways without changing whether a cycle is found. Assume that we have two orderings such that one produces a cycle, and one does not. That means we have a branching point from an associated constant, so there are two rewrite constraints for that same constant, so one must appear on the LHS and one on the RHS of the `&` operator. Now their order is fixed, as we have specified that all rewrite constraints from the LHS come before those from the RHS. So we will always choose the same constraint when replacing that associated constant, and consistently find a cycle or not, regardless of how constraints are ordered within the LHS and RHS facet types. ## Alternatives considered ### Order independence This has two possibilities: eagerly finding cycles or eagerly avoiding cycles. In either case, this complicates the toolchain implementation without making the language more expressive. The user can always write constraints in a way that does not produce a cycle in a left-to-right ordering if the constraints are valid. Eagerly finding cycles produces errors when unnecessary, which seems more hostile than needed. Eagerly avoiding cycles could be seen as being helpful, as we find a way to complete the concrete assignments whenever possible. In that case, given a cycle of rewrite constraints, adding a concrete value for any associated constant in the cycle, written anywhere in the facet type, breaks the cycle. Whereas with left-to-right ordering, the concrete value needs to be the first rewrite constraint for the associated constant. With eager cycle breaking, this cycle `I where .Y = .X and .X = .Y` is broken by adding `.X = ()` anywhere in the facet type: `I where .Y = .X and .X = .Y and .X = ()`. For a human reading left to right, we still read a cycle, but have to keep reading to understand why it's not. With left-to-right cycle breaking, the same expressivity is kept, but the concrete value must be written before other rewrites of the same associated constant: `I where .Y = .X and .X = () and .X = .Y`. Now for a human reading from left to right, we can read that `.Y = () and .X = ()` and can disregard other later assignments to `.X`. ### Syntactic equivalence We could choose to diagnose `I where .X = () and .X = .Y and .Y = ()` as a conflicting assignment to `.X`, since the RHS of the two assignments are written differently, even though the resolved value of the RHS can be found to be `()` in both of them. This is perhaps the simplest rule for humans to understand, but requires maintaining syntactic information throughout the toolchain, complicating the implementation and is at odds with canonicalization. Given the following example: ``` (I where .X = .Y and .Y = ()) & (I where .X = ()) ``` The combined facet type combines `.X = .Y` and `.X = ()` which are syntactically different. But the facet type on either side of the `&` operator are resolved and canonicalized. Canonicalization requires that `I where .X = .Y and .Y = ()` is the same type as `I where .X = () and .Y = ()`, but when combined with `I where .X = ()` one violates the syntactic rule and one does not. Thus we would need to only apply syntactic identity within a single facet type, but use a semantic identity when combining facet types. This makes for a more inconsistent experience and implementation. ================================================ FILE: proposals/p5914.md ================================================ # Updating Carbon's safety strategy [Pull request](https://github.com/carbon-language/carbon-lang/pull/5914) ## Table of contents - [Abstract](#abstract) - [Problem](#problem) - [Background](#background) - [Goals and requirements](#goals-and-requirements) - [Proposal](#proposal) - [Direction for temporal and data-race memory-safe type system model](#direction-for-temporal-and-data-race-memory-safe-type-system-model) - [Rationale](#rationale) - [Alternatives considered](#alternatives-considered) - [Defer beginning the concrete safety design](#defer-beginning-the-concrete-safety-design) - [Pursue an alternative strategy towards memory safety specifically](#pursue-an-alternative-strategy-towards-memory-safety-specifically) - [Adopt a memory safety strategy more closely based on Rust](#adopt-a-memory-safety-strategy-more-closely-based-on-rust) ## Abstract Carbon is accelerating and adjusting its safety strategy, specifically to flesh out its memory safety strategy and reflect simplifying developments in the safety space. This proposal replaces the previous directional safety strategy with a new concrete and updated framework for the safety design. It includes a specific framework for memory safety, simplified build modes, specific "safety modes", and terminology. This proposal also provides a _directional_ suggestion for temporal and data-race safety specifically. In addition to fully building out the above directional component, there are several other aspects of our safety design that will follow in subsequent proposals. The hope is to establish the initial framework here. ## Problem Carbon's safety strategy pre-dates the increased importance and urgency of having a memory safety strategy, and our subsequent efforts to accelerate this part of Carbon's design. It also was written at a time with deep uncertainty about the performance costs of both safety and hardening efforts. And it left major areas as future work that are now needed such as a specific model for delineating when Carbon code has memory safety enforced. We need an updated, more concrete, and more precise strategy now that we're beginning to directly develop safety designs. ## Background - [Basic Concepts and Taxonomy of Dependable and Secure Computing](https://doi.org/10.1109%2FTDSC.2004.2) - [Secure by Design: Google's Perspective on Memory Safety](https://research.google/pubs/secure-by-design-googles-perspective-on-memory-safety/) - [Story-time: C++, bounds checking, performance, and compilers](https://chandlerc.blog/posts/2024/11/story-time-bounds-checking/) ## Goals and requirements Our end goal for Carbon's memory safety strategy is to allow large-scale, existing C++ codebases to _incrementally and scalably_ begin writing new code in a memory-safe language without loss of their existing legacy codebase. Handling real-world C++ codebases also means handling large-scale dependencies on C++, but also on C and even Rust. We plan to support a two step migration process: 1. Highly automated, minimal supervision migration from C++ to a dialect of Carbon designed for C++ interop and migration. 2. Incremental refactoring of the Carbon code to adopt memory safe designs, patterns, and APIs. From this framing of our problem statement and end-goal, we can extract detailed requirements on the memory safety design in Carbon: - The ability to write Carbon as a "rigorously memory safe programming language" (defined in the "Secure by Design" paper). - Requires temporal, spatial, and type errors to be rejected at compile time or detected at runtime with fail-stop behavior. - Requires initialization errors to not have undefined behavior, but allows, for example, zero-initialization rather than fail-stop behavior. - This does not require complete elimination of data races, but does require [addressing temporal memory safety even in the face of concurrency](/docs/design/safety#data-races-versus-unsynchronized-temporal-safety). - We directly refer to this as simply a ["memory-safe language"](/docs/design/safety/terminology.md#memory-safe-language) in Carbon. - Seamless integration between Carbon's memory safe code and the memory unsafe code resulting from migrating existing C++. - Safe and unsafe code should be freely mixed, with incremental checking of the strictly safe aspects of the code. - A smooth, incremental path for this memory unsafe Carbon code to become more memory safe over time. - Must resonate with users as _significantly_ more incremental than migrating directly to Rust. That is, the granularity of increasing safety needs to be significantly smaller than moving an entire file from C++ to Rust. - Similar level of runtime spatial safety checks as Rust with similar runtime overhead. - No reference counting or garbage collection by default -- we want to preserve C++ (and Rust) precise and explicit control over the lifetime of resources. - Minimal temporal safety runtime overhead: in aggregate and in real-world application benchmarks, any performance overhead would need to be under 1%. - We expect to spend 1-2% of runtime overhead achieving spatial safety, and these would be cumulative. - There has been strong, persistent resistance to over 2% runtime overhead in broad contexts. - Rust shows that this is achievable using primarily compile time techniques. We also have some less critical but still _highly desirable_ goals: - Seamless _safe_ Rust interop -- able to reliably and consistently avoid unsafe on the boundary with Rust code - Should be no worse than the best Rust/C++ interop which uses extra safety annotations for the C++ API - Data race prevention Whether we fully achieve these secondary goals or not, we need to clearly demonstrate where we end up and what any remaining gap there is towards these goals. ## Proposal See the new [proposed safety design](/docs/design/safety/README.md) and [safety terminology](/docs/design/safety/terminology.md) for the core proposed strategy and model. ## Direction for temporal and data-race memory-safe type system model > Note: this is a _directional_ component of the proposal, and should be treated > as provisional until dedicated proposals fully establish our model here. We expect Carbon to tackle temporal and data-race safety through its type system, much as Rust and strict Swift do. However, we believe there are substantial opportunities to select a model that: - provides comparable and compatible levels of safety; - improves the ergonomics of interop with existing C++ APIs; and - enables significantly more incremental adoption when starting from C++ code. However, these benefits aren't free: they come at the cost of added complexity in the type system itself. Fundamentally, while we will strive to keep Carbon as simple as we can within its design constraints, we expect Rust to be a simpler language than Carbon, and for Carbon's improved interop model and incremental adoption to always come at the cost of complexity. We also don't expect this complexity to be something that can be fully hidden in implementation details of standard libraries; some of it will be fundamental and visible to user code in order to allow the greater degree of flexibility. There are at least five relevant places where we expect Carbon to differ from Rust in this category: - Including constructs parallel to C++ constructs that are not present in Rust. - More precise modeling using more pointer types, to allow _safe_ and _ergonomic_ mutable aliasing. - More incremental enforcement of data-race safety. - Aligning concepts and idioms with C++. - Having fewer assumptions about unsafe code. First, Carbon is going to include language constructs to match C++ that Rust avoids completely, and this comes at a complexity cost. A canonical example is inheritance: Carbon will have inheritance and virtual dispatch built into the language, allowing straightforward C++ interop and migration for APIs that use it. These features increase the size of the language overall, and through interaction increase the complexity of safety features. The second of these is the most significant change to safety: supporting safe, shared mutation. We think this will open up the door to significantly more flexible code while still providing memory safety. With Rust, there are two kinds of safe pointers: shared borrows (`&`), and mutable borrows (`&mut`). The latter are required to be exclusive -- no other safe pointers alias the mutable object. We expect to have more distinguished types of pointers in Carbon to be able to model more C++ coding patterns safely, including both shared mutable pointers, and exclusive mutable pointers -- this is where the complexity increases. While the exact design of this needs to be carefully fleshed out, our current idea is to build on the concepts of [safe, shared mutability in the Ante Language](https://antelang.org/blog/safe_shared_mutability/). Building illustrative examples of how this impacts C++ code migrating towards memory safety is an important part of our planned deliverables for 2025. Third, we expect to have more incremental enforcement of data-race safety in Carbon, similar to how [Swift allowed code to incrementally adopt the necessary patterns and restrictions](https://www.swift.org/documentation/concurrency/) to be data-race safe. The necessary infrastructure was provided as independently adoptable APIs, and enforcement was then available optionally prior to Swift 6 making this a requirement. Fourth, we will express Carbon's core concepts around mutation in terms of C++ idioms and concepts. For example, in a container, we expect iterators can use shared mutable pointers, and the C++ concept of "iterator invalidation" will be captured by methods that require exclusive mutable access, precluding any outstanding shared mutable pointers. Lastly, Carbon's safety model will make fewer assumptions about what unsafe code is allowed to do than Rust. To avoid undefined behavior, the optimization of safe code will not rely on properties that are not established locally. For example, there won't be an assumption that a call to C++ code won't save a copy of pointers to passed-in objects. When there are restrictions on what unsafe code can do, we will endeavor to make violations into [erroneous behavior](https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2023/p2795r2.html#bigpic), where the behavior is well-defined while still considered a diagnosable programming mistake. This reduces the risks of unsafe code during its incremental migration to memory safe code. It also leaves unsafe Carbon code as reasonable to work in and maintain over a longer period, which can enable more widespread migration of C++ to unsafe Carbon. The amount of change and effort required to convert a piece of code from unsafe Carbon to safe Carbon is much less than the amount of change required to also change languages, introduce an interop boundary, and make any API changes needed to enable memory safety all at once. We believe this will significantly reduce churn effort along the boundaries of memory safety during incremental migration from unsafe Carbon to memory safe Carbon code as opposed to moving from C++ directly to a memory safe language. Some of these differences may eventually be compelling directions for Rust to improve its C++ interop, and we plan to develop these in active collaboration with the Rust community. For these areas of potential overlap, Carbon largely provides an open field for experimentation and proving out ideas. However, we expect the majority of the differences we can achieve here would introduce complexity and might change the nature of Rust making it difficult to retrofit. As a consequence, there are likely to remain durable tradeoff differences between Carbon and Rust going forward. ## Rationale - [Performance-critical software](/docs/project/goals.md#performance-critical-software) - Without memory safety, it will not be reasonably secure to continue to write high performance software in low-level languages that provide the desired performance control. - [Practical safety and testing mechanisms](/docs/project/goals.md#practical-safety-and-testing-mechanisms) - Our understanding of practical safety techniques, especially new details of this uncovered over the past several years, directly form the need for an update to our safety strategy. - [Interoperability with and migration from existing C++ code](/docs/project/goals.md#interoperability-with-and-migration-from-existing-c-code) - A primary motivation and constraint across the surface of safety features is to better enable C++ code interop and migration. ## Alternatives considered ### Defer beginning the concrete safety design The current directional strategy has served us well, and it would be technically possible to continue to defer turning this into a concrete design. However, we have had strong feedback from multiple interested users that they need the safety component to be concrete to do any further evaluation of Carbon, and we are accelerating our work to meet this need. ### Pursue an alternative strategy towards memory safety specifically There are many different approaches to memory safety that we could pursue. We haven't exhaustively listed them, however most of the major candidates are excluded by our specific goals. For example, a GC-based strategy for solving temporal memory safety is a non-starter for users who specifically need non-GC memory management. Rather than exhaustively list the alternatives that are excluded, we have tried to list the specific [goals](#goals-and-requirements) that led to the proposed direction. ### Adopt a memory safety strategy more closely based on Rust An especially appealing alternative is to base our memory safety model _precisely_ on Rust's. It would give us a strong existence proof, a good basis to understand soundness, etc. Many of the most difficult questions have already been answered. This was a seriously considered direction. However, Rust already exists and is an excellent language. Replicating Rust is _not_ a goal of Carbon. Our goal is to specifically address use cases where Rust is not viable at the moment, specifically due to the need for pervasive C++ interop or automated migration from a large, existing C++ codebase. This directly motivates pursuing a memory safety model that attempts to further optimize the ergonomics and incrementality of adopting memory safety when starting in this position. ================================================ FILE: proposals/p6008.md ================================================ # Replace `impl fn` with `override fn` [Pull request](https://github.com/carbon-language/carbon-lang/pull/6008) ## Table of contents - [Abstract](#abstract) - [Problem](#problem) - [Background](#background) - [Proposal](#proposal) - [Rationale](#rationale) ## Abstract This proposal renames the syntax used to mark an overriding definition of a virtual method from `impl fn` to `override fn` to avoid ambiguity. ## Problem The phrase `impl fn` was introduced to mark overriding virtual functions, however, it is now ambiguous: besides indicating an overriding virtual function, it can be parsed as an "impl" declaration when the construct following "impl" begins with a lambda introduced by "fn". ## Background The original syntax was adopted in [proposal #777](https://github.com/carbon-language/carbon-lang/pull/777) to emphasize that implementing interfaces and overriding virtual functions are similar operations. ## Proposal This proposal is to replace the `impl fn` syntax with `override fn` for method overriding in class inheritance. Note that `impl` is still a modifier keyword for library and package declarations (`impl library ...` and `impl package ...` respectively). The former is unambiguous and the latter is easy to disambiguate, so no change is needed for these two cases. ## Rationale This proposal is focused on the [That code is easy to read, understand, and write](/docs/project/goals.md#code-that-is-easy-to-read-understand-and-write) Carbon goal. Changing the keyword from `impl` to `override` makes the intent clearer, resolves syntax ambiguity, and adheres to existing C++ conventions. ================================================ FILE: proposals/p6177.md ================================================ # C++ Interop: Mapping `std::string_view` to `Core.Str` [Pull request](https://github.com/carbon-language/carbon-lang/pull/6177) ## Table of contents - [Abstract](#abstract) - [Problem](#problem) - [Background](#background) - [Proposal](#proposal) - [Details](#details) - [Rationale](#rationale) - [Alternatives considered](#alternatives-considered) - [1. Provide a Wrapper Type](#1-provide-a-wrapper-type) - [2. Do Nothing](#2-do-nothing) ## Abstract This proposal defines a direct, zero-cost mapping between C++'s `std::string_view` and Carbon's `Core.Str` for C++ interoperability. The goal is to make C++ APIs that use `std::string_view` feel native and seamless when used from Carbon. This mapping relies on the two types having an identical memory representation, a condition that we will work to ensure across all supported platforms. ## Problem Seamless interoperability with C++ is a core goal for Carbon. `std::string_view` has become a ubiquitous, fundamental type in modern C++ for passing non-owning string data. Without a direct mapping, Carbon developers would be forced to work with ABI-incompatible wrapper types or manually unpack `std::string_view` instances into pointers and sizes. This creates significant friction: 1. **Ergonomics:** C++ APIs would not feel idiomatic. Developers would need to perform constant, boilerplate conversions. 2. **Performance:** Any wrapper-based solution would break the zero-cost abstraction principle, potentially introducing overhead at the boundary. 3. **Adoption:** The lack of seamless integration for such a basic type would be a significant barrier to migrating or integrating C++ codebases. To provide a truly smooth migration path and interoperability story, Carbon must treat `std::string_view` as a first-class citizen, ideally as the same type as its native string view. ## Background Carbon's `Core.Str` is, per [#5969](https://github.com/carbon-language/carbon-lang/issues/5969), the language's fundamental non-owning view of a sequence of bytes. It is [currently implemented as a pair of a pointer to the data and a 64-bit integer representing the size in bytes](https://github.com/carbon-language/carbon-lang/blob/7c13bddc92be8ceac758189df76ebbb048e1a9d5/core/prelude/types/string.carbon#L19-L22). The assumed memory layout is the pointer followed by the size. C++'s `std::string_view` serves the same purpose. However, its memory layout is not standardized and varies between standard library implementations. This is critical for ABI compatibility. The layouts for major C++ standard library implementations are as follows: | Standard Library | Platform/Compiler | Member Order | Size Type (`size_t`) | Notes | Source | | ---------------- | -------------------- | -------------------- | -------------------- | ----------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------- | | **libc++** | Clang (macOS, etc.) | `__data_`, `__size_` | 64-bit (on 64-bit) | Pointer first, then size. | [`string_view`](https://github.com/llvm/llvm-project/blob/fd5bc6033e521b946f04cb9c473d9cca3da2da9b/libcxx/include/string_view#L711-L712) | | **MSVC STL** | Microsoft Visual C++ | `_Mydata`, `_Mysize` | 64-bit (on 64-bit) | Pointer first, then size. | [`__msvc_string_view.hpp`](https://github.com/microsoft/STL/blob/ba64eaaa8592c700949f3c09a0d8570b932828f5/stl/inc/__msvc_string_view.hpp#L1924-L1925) | | **libstdc++** | GCC (Linux, etc.) | `_M_len`, `_M_str` | 64-bit (on 64-bit) | **Size first, then pointer.** | [`string_view`](https://github.com/gcc-mirror/gcc/blob/6b999bf40090f356c5bb5ff8a82e7e0dc4c4ae05/libstdc%2B%2B-v3/include/std/string_view#L590-L591) | As the table shows, there is a key difference in member ordering, with `libstdc++` being the outlier compared to the assumed layout of `Core.Str`. ## Proposal We propose to map C++ `std::string_view` directly to Carbon's `Core.Str` when importing C++ headers. This means the Carbon compiler will treat `std::string_view` as a type alias for `Core.Str` at the ABI level. To achieve this, the following conditions must be met: 1. **Identical Representation:** The memory layout (sequence of fields, size, and alignment) of `std::string_view` and `Core.Str` must be identical for the target platform and C++ standard library. 2. **Platform-Wide Compatibility:** The ultimate goal is for this mapping to work seamlessly across all Carbon-supported architectures. Initially, this direct mapping will be enabled only for targets where the representation is known to match. For other platforms, we will pursue one of two strategies: - Work with standard library vendors (for example, `libstdc++`) to align on a consistent representation for `std::string_view` across architectures, falling back to providing a patched version of these libraries if necessary. - Adapt the memory layout of Carbon's `Core.Str` on a per-platform basis to match the target's native `std::string_view` ABI. This ensures that while the initial implementation may be constrained, the long-term design is for universal, zero-cost compatibility. ## Details The initial implementation will assume `Core.Str` has a `(pointer, size)` layout. This means the direct mapping will work out-of-the-box on platforms using `libc++` (Clang) and MSVC STL. For platforms using `libstdc++`, the current `(size, pointer)` layout is incompatible. The direct mapping will be disabled on these platforms by default until compatibility is achieved. Our strategy is to first align `Core.Str`'s layout with the dominant `(pointer, size)` convention used by Clang and MSVC. We will then engage with the `libstdc++` community to explore standardizing this layout for better cross-compiler compatibility. Furthermore, `Core.Str` is defined with a 64-bit size field. C++ `std::string_view` uses `size_t`, which is 32-bit on 32-bit targets. Therefore, this direct mapping will initially be restricted to 64-bit targets, which are Carbon's primary focus. It is essential to recognize that both `Core.Str` and `std::string_view` are fundamentally views over bytes, not Unicode characters. This proposal maintains that semantic alignment. If Carbon requires a string type that understands character boundaries, it should be a separate, distinct type in the standard library and not interfere with this fundamental C++ interoperability mechanism. ## Rationale This proposal directly supports the following Carbon goals: - **Interoperability with and migration from existing C++ code:** `std::string_view` is one of the most common types in modern C++ interface design. A seamless mapping is not a luxury but a requirement for effective interoperability. - **Performance-critical software:** By ensuring a direct, zero-cost mapping, we avoid any performance penalties at the C++/Carbon boundary for string data. This is critical for systems programming where such overhead is unacceptable. - **Code that is easy to read, understand, and write:** A direct mapping allows developers to think of `std::string_view` and `Core.Str` as the same concept, reducing cognitive load and eliminating the need for manual conversions. - **Naming of `Core.Str`:** The choice of `Str` over `String` for the non-owning view type is intentional. It avoids confusion with owning types like C++'s `std::string`. `Str` is introduced as a new term of art for Carbon, providing a concise and readable name for this fundamental type. The shorter name is preferred for its clarity and reduced verbosity, especially for a type that will be used frequently. This is based on the decision in leads question [#5969](https://github.com/carbon-language/carbon-lang/issues/5969). By defining a clear path toward universal ABI compatibility for this type, we are building a solid foundation for deep and performant integration with the existing C++ ecosystem. ## Alternatives considered ### 1. Provide a Wrapper Type We could choose to always import `std::string_view` as an opaque Carbon struct, for example, `Core.Cpp.string_view`. - **Advantages:** This would be ABI-safe on all platforms immediately. - **Disadvantages:** This approach is not seamless. It would require explicit conversions between `Core.Str` and `Core.Cpp.string_view`, adding boilerplate and potential performance overhead. It violates the goal of making C++ APIs feel native to Carbon. ### 2. Do Nothing We could leave it to the developer to manually handle `std::string_view` by accepting it as an opaque type and using C++ helper functions to extract the pointer and size. - **Advantages:** Simplest to implement in the compiler. - **Disadvantages:** This provides a terrible developer experience and runs directly counter to Carbon's core goal of excellent C++ interoperability. It would make using a vast number of modern C++ libraries prohibitively difficult. The proposed approach of a direct mapping is superior as it prioritizes the long-term goals of performance and ergonomics, even if it requires a phased implementation to achieve full platform support. ================================================ FILE: proposals/p6231.md ================================================ # Disambiguate "value binding" [Pull request](https://github.com/carbon-language/carbon-lang/pull/6231) ## Table of contents - [Abstract](#abstract) - [Problem](#problem) - [Proposal](#proposal) - [Details](#details) - [Rationale](#rationale) - [Alternatives considered](#alternatives-considered) - [Rename "a binding declared by a value binding pattern"](#rename-a-binding-declared-by-a-value-binding-pattern) - [Other names for "primitive conversion from reference to value"](#other-names-for-primitive-conversion-from-reference-to-value) ## Abstract This proposal removes the definition of the term "value binding" as a primitive category conversion from reference to value, replacing it with the term "value acquisition". The other meaning of "value binding", a binding declared by a value binding pattern, is unchanged. ## Problem The design docs currently define "value binding" in two conflicting ways: it can mean the binding declared by a value binding pattern, or it can mean a primitive category conversion from reference to value. The two can usually be disambiguated based on context, but it's not always straightforward, and the double meaning complicates naming within the toolchain implementation. ## Proposal This proposal removes the definition of the term "value binding" as a primitive category conversion from reference to value, replacing it with the term "value acquisition". ## Details See the changes elsewhere in the [proposal PR](https://github.com/carbon-language/carbon-lang/pull/6231). ## Rationale Using unambiguous terminology advances our [community and culture](/docs/project/goals.md#community-and-culture) goals, by facilitating clear communication. ## Alternatives considered ### Rename "a binding declared by a value binding pattern" We could instead rename the other meaning of "value binding", but that would be considerably more difficult because that meaning appears to be more common, and because it's part of a cluster of other heavily-used terms, such as "reference binding" and "binding pattern", which we would need to rename for consistency. ### Other names for "primitive conversion from reference to value" We considered several alternative names before settling on "value acquisition": - "Value borrowing" highlights the close analogy to Rust borrowing, which similarly forbids mutation of the object for the lifetime of the borrow. However, this naming choice somewhat prejudges the safety story for this operation. - "Value snapshotting" and "value observation" may not effectively communicate the ongoing coupling between the object and the value. - "Value capturing" reuses and extends the existing meaning of "capturing" in lambdas. However, it may be confusing that a lambda can have value captures that are not initialized by value capturing (for example because the initializer is a value, not a reference). - "Value expression conversion" is straightforward and hard to misunderstand. However, we sometimes use "value binding" to refer to the _result_ of a reference-to-value conversion, for example "the lifetime of a value binding". "Value expression conversion" doesn't seem to lend itself to that usage, possibly because it's too generic to be a recognizable term of art. ================================================ FILE: proposals/p6254.md ================================================ # C++ Interop: Toolchain Implementation for Function Calls [Pull request](https://github.com/carbon-language/carbon-lang/pull/6254) ## Table of contents - [Abstract](#abstract) - [Problem](#problem) - [Background](#background) - [Proposal](#proposal) - [Details](#details) - [Importing C++ functions](#importing-c-functions) - [Overload resolution](#overload-resolution) - [Direct calls versus thunks](#direct-calls-versus-thunks) - [Thunk generation](#thunk-generation) - [Parameter and return value handling](#parameter-and-return-value-handling) - [Member function calls](#member-function-calls) - [Operator calls](#operator-calls) - [Rationale](#rationale) - [Alternatives considered](#alternatives-considered) - [Require manual C++ wrappers](#require-manual-c-wrappers) - [Mandate Carbon ABI compatibility with C++](#mandate-carbon-abi-compatibility-with-c) ## Abstract This proposal details the toolchain implementation for calling imported C++ functions from Carbon. It covers how C++ overload sets are handled, the process of overload resolution leveraging Clang, and the generation of "thunks" (intermediate functions) when necessary to bridge Application Binary Interface (ABI) differences between Carbon and C++. ## Problem Seamless, high-performance interoperability with C++ [is a fundamental goal of Carbon](https://github.com/carbon-language/carbon-lang/blob/f9bd01536b97961039257cc10fb20b495f7a9b33/docs/project/goals.md#interoperability-with-and-migration-from-existing-c-code). The Carbon language design for C++ interoperability, particularly for function calls, is described in the [Carbon calling convention design](https://docs.google.com/document/d/1KUxumZtNe3mY3TsjW2s_ZADOlAaFlrtsLKHVILtqIaM). To implement that design, the Carbon toolchain must be able to translate Carbon-side calls to C++ functions into instructions the C++ side can understand. Several challenges arise at the toolchain level: - C++ supports function overloading, requiring the toolchain to resolve calls to the correct C++ function within an overload set. - C++ types do not always have identical representations or ABIs to their Carbon counterparts (see [Carbon <-> C++ Interop: Primitive Types](https://github.com/carbon-language/carbon-lang/blob/44b2f60c90df5c1b0ce86f97bb0ece2a94eb50ea/proposals/p5448.md)). For example, parameter passing conventions (by value, by pointer) or return value handling (direct return versus return slot) might differ. This may require the toolchain to synthesize adapter code. - C++ member functions require special handling of the `this` pointer. - C++ supports features like default arguments which need a defined mapping. A clear, robust implementation strategy is needed to handle these complexities, ensuring both correctness and performance. ## Background [Carbon's C++ interoperability philosophy](https://github.com/carbon-language/carbon-lang/blob/01e12111a8a685694ccd2c9deb2779f907917543/docs/design/interoperability/philosophy_and_goals.md) aims to minimize bridge code and provide unsurprising mappings. When Carbon code imports a C++ header, the functions declared within become potentially callable entities. C++ overload resolution rules are complex, and replicating them perfectly within Carbon would be difficult and likely divergent over time. Furthermore, direct calls are only possible when the ABI conventions of the Carbon call site precisely match the expectations of the C++ callee. ## Proposal 1. **Import:** C++ functions and methods, including overload sets, are imported into Carbon and represented internally (conceptually, as specific overload set instructions in SemIR). 2. **Overload Resolution:** When a call to an imported C++ function or overload set occurs in Carbon, Carbon leverages Clang's overload resolution mechanism. Carbon argument types are mapped to hypothetical C++ types / expressions, and Clang's `Sema` determines the best viable function. 3. **ABI Bridging (Thunks):** - If the selected C++ function's ABI (parameter types, return type handling, calling convention) matches the Carbon call site's ABI based on defined type mappings, a direct call is generated. - If the ABIs mismatch, Carbon generates an intermediate function, called a **C++ thunk**. This thunk has a "simple" ABI callable directly from Carbon (typically using only pointers and basic integer types like `i32`/`i64`). The thunk internally calls the actual C++ function, performing necessary argument conversions (for example, loading a value from a pointer) and handling return value conventions (for example, managing a return slot). 4. **Call Execution:** The Carbon code either calls the C++ function directly or calls the generated C++ thunk. ## Details ### Importing C++ functions When a C++ header is imported using `import Cpp`, declarations within that header are made available. Function declarations, including member functions and overloaded functions, are represented internally within Carbon's SemIR. An overload set from C++ is represented as a single callable entity in Carbon, associated with the set of C++ candidate functions. ### Overload resolution To resolve a call like `Cpp.MyNamespace.MyFunc(arg1, arg2)` where `MyFunc` might be an overload set imported from C++: 1. **Map Arguments:** Carbon argument instructions (`arg1`, `arg2`) are mapped to placeholder C++ expressions (conceptually similar to [`clang::OpaqueValueExpr`](https://github.com/llvm/llvm-project/blob/1e99026b45b048a52f8372399ab83d488132842e/clang/include/clang/AST/Expr.h#L1178)). The types of these expressions are determined by mapping the Carbon argument types to corresponding C++ types ([Carbon <-> C++ Interop: Primitive Types](https://github.com/carbon-language/carbon-lang/blob/44b2f60c90df5c1b0ce86f97bb0ece2a94eb50ea/proposals/p5448.md)). 2. **Invoke Clang Sema:** Carbon invokes Clang's overload resolution logic ([`clang::OverloadCandidateSet::BestViableFunction()`](https://github.com/llvm/llvm-project/blob/1e99026b45b048a52f8372399ab83d488132842e/clang/include/clang/Sema/Overload.h#L1456)) with the mapped C++ name, the candidate functions from the imported overload set, and the placeholder argument expressions. 3. **Select Candidate:** Clang determines the best viable C++ function based on C++ rules (implicit conversions, template argument deduction if applicable later, etc.). If resolution fails (no viable function, ambiguity), Clang's diagnostics are surfaced as Carbon diagnostics. 4. **Access Check:** After selecting a function, Carbon checks if the function is accessible based on C++ access specifiers (`public`, `protected`, `private`) in the context of the call. ### Direct calls versus thunks A direct call from Carbon to C++ is possible only if the ABI matches exactly. A **C++ thunk** is required if: - **Type Representation Mismatch:** A parameter or the return type has a different representation in Carbon than expected by the C++ ABI, requiring conversion. For example, a Carbon `bool` (`i1`) passed to a C++ `bool` (often `i8`), or complex struct types. - **Return Convention Mismatch:** The C++ function returns a non-trivial type by value, which typically requires a hidden return slot parameter in the ABI, whereas Carbon might expect a direct return value. - **Parameter Convention Mismatch:** C++ expects a parameter by way of pointer/reference where Carbon provides a value, or vice-versa. - **Default Arguments:** The Carbon call omits arguments that have default values in C++. The thunk provides the default values. - **Variadic arguments:** (Future work) Calling [C++ variadic arguments](https://en.cppreference.com/w/cpp/language/variadic_arguments.html) functions. If a thunk is _not_ required, Carbon emits a direct call instruction targeting the mangled name of the C++ function. ### Thunk generation If a thunk is required for a C++ function `CppOriginalFunc()`, Carbon generates a new internal function, conceptually `CppOriginalFunc__carbon_thunk()`: 1. **Signature:** The thunk has an ABI that is simple and directly callable from Carbon. - Parameters corresponding to C++ parameters with complex ABIs are passed by pointer (`T*`). - Parameters with simple ABIs (like `i32`, `i64`, raw pointers) are passed directly. - If `CppOriginalFunc` uses a return slot, the thunk takes a pointer parameter for the return slot. Its LLVM return type becomes `void`. - If `CppOriginalFunc` returns a simple type directly, the thunk returns the same simple type directly. 2. **Body:** The thunk body performs the following: - Loads values from pointer arguments passed by Carbon where necessary. - Performs necessary type conversions between Carbon simple ABI types and C++ expected types (for example, `i1` to `i8` for `bool`). - Calls `CppOriginalFunc` with the converted arguments, potentially passing the return slot address. - If `CppOriginalFunc` returned directly, the thunk returns that value. If it used a return slot, the thunk returns `void`. 3. **Attributes:** The thunk is typically marked `always_inline` to encourage the optimizer to remove the indirection. It is given a predictable mangled name based on the original function's mangled name plus a suffix. The Carbon call site then calls the thunk instead of the original C++ function. ### Parameter and return value handling - **Arguments:** When calling a C++ function (directly or by way of a thunk), Carbon arguments undergo implicit conversions as needed to match the parameter types determined by overload resolution. For calls requiring a thunk, additional conversions might occur at the call site (for example, taking the address of an object to pass by pointer to the thunk) and within the thunk (for example, loading the object from the pointer). - **Return Values:** If the C++ function returns `void`, the Carbon call expression has type `()`. If it returns a simple type directly, the Carbon call has the corresponding mapped Carbon type. If the C++ function uses a return slot, the Carbon call is modeled as initializing the storage designated by the return slot argument (often a temporary created at the call site), and the overall call expression typically results in the initialized value. ### Member function calls - **Instance Methods:** When `object.CppMethod()` is called, `object` becomes the implicit `this` argument. Clang's overload resolution handles the qualification (for example, `const`). The `this` pointer is passed as the first argument, either directly or to the thunk. - **Static Methods:** Calls like `CppClass::StaticMethod()` are treated like free function calls; no `this` pointer is involved. ### Operator calls Calls to overloaded C++ operators are handled similarly to function calls. Carbon identifies the operator call, looks up potential C++ operator functions (both member and non-member), and uses Clang's overload resolution to select the best candidate. Thunks may be generated if required by the selected operator function's ABI. ## Rationale - **Leverages Clang:** Reusing Clang's overload resolution avoids reimplementing complex C++ rules and ensures consistency. - **Performance:** Direct calls are used when possible. Thunks are designed to be minimal and aggressively inlined, minimizing overhead. - **Correctness:** Thunks handle ABI mismatches systematically, ensuring correct data marshalling between Carbon and C++. - **Developer Experience:** Aims for C++ calls to feel natural in Carbon, hiding much of the complexity of ABI bridging. - **Interop Goal:** Directly supports the core goal of seamless C++ interoperability. ## Alternatives considered ### Require manual C++ wrappers Instead of generating thunks automatically, Carbon could require developers to write C++ wrapper functions with simple C-like ABIs for any C++ function whose ABI doesn't directly match Carbon's expectations. - **Rejected because:** This places a significant burden on the developer, increases boilerplate, hinders rapid iteration, and makes C++ libraries feel less integrated. It violates the goal of minimizing bridge code. ### Mandate Carbon ABI compatibility with C++ Carbon could define its types and calling conventions to always match a specific C++ ABI (for example, Itanium). - **Rejected because:** This would heavily constrain Carbon's own evolution and design choices. It wouldn't solve the problem entirely, as C++ ABIs themselves vary (for example, between platforms, compilers, or even libraries like libc++ vs libstdc++ for `string_view`). It conflicts with the goal of software and language evolution. ================================================ FILE: proposals/p6333.md ================================================ # CLI and separate compilation [Pull request](https://github.com/carbon-language/carbon-lang/pull/6333) ## Table of contents - [Abstract](#abstract) - [Problem](#problem) - [Background](#background) - [Look-and-feel](#look-and-feel) - [Bazel rule design](#bazel-rule-design) - [Proposal](#proposal) - [Details](#details) - [Command changes](#command-changes) - [Compile command](#compile-command) - [Build command](#build-command) - [Link command](#link-command) - [Mapping packaging directives to filenames](#mapping-packaging-directives-to-filenames) - [Support for other packages](#support-for-other-packages) - [Disallow ambiguous library names](#disallow-ambiguous-library-names) - [Example interaction with Bazel](#example-interaction-with-bazel) - [carbon_library and carbon_binary](#carbon_library-and-carbon_binary) - [Indirect API exposure](#indirect-api-exposure) - [Core package rules](#core-package-rules) - [Future work](#future-work) - [Caching checked IR, C++ AST, and other possible compile artifacts](#caching-checked-ir-c-ast-and-other-possible-compile-artifacts) - [Rationale](#rationale) - [Alternatives considered](#alternatives-considered) - [Naming of commands and rules](#naming-of-commands-and-rules) - [Support a full-fledged build system](#support-a-full-fledged-build-system) - [Don't support packaging directive to filename mappings](#dont-support-packaging-directive-to-filename-mappings) - [Distribute pre-compiled versions of Core files](#distribute-pre-compiled-versions-of-core-files) - [Create an explicit mapping from packaging directives to files](#create-an-explicit-mapping-from-packaging-directives-to-files) ## Abstract - Change the look-and-feel of the `carbon` compilation command set to use `compile`, `link`, and `build`. - Build library-to-file discovery for `Core`, but support it in a general manner. ## Problem The current command line is still a prototype, and lacks support for regular use. For example: - `carbon compile` produces one object file per input file. When `--output-file` is specified and there are multiple inputs, the output is repeatedly overwritten. - `carbon compile` doesn't provide a trivial way to produce object files for the prelude. The `carbon_binary` rule is, behind the scenes, separately compiling all the prelude files individually and doing its own custom linking with those. - When writing a small test program (for example "hello world") it would be nice to have a single command to run to produce a program. Right now, `carbon compile` and `carbon link` must be used in combination. Essentially, we have a decent setup for testing, but not one that's easy to use in real-world situations. ## Background In C++, `clang++ main.cpp -o program` is a way to produce `program`. This is trying to reach a similar goal to make it easy to build and test small programs. Key commands related to this proposal are `carbon compile`, `carbon clang`, and `carbon link`. The end result will likely compose multiple command elements in order to build the output. ### Look-and-feel Note the goal here is to align on look-and-feel of separate compilation. Although the `carbon` CLI is important to the language, most details aren't necessary to address through the proposal process. For example, we want to get flag names right here, but also we wouldn't expect a proposal for flag name changes. ### Bazel rule design This is a proposal for the command line. Bazel rules are mentioned because it can help illustrate interactions with build systems. However, this proposal is not intended to decide Bazel design, and the existing Bazel rules have not been through the proposal process. ## Proposal Restructure compilation into: - `carbon compile`: Take a single input to build, and produce a single output `.o`. - `carbon build`: Take multiple inputs in order to produce a linked binary. - Overlaps with `carbon compile` and `carbon link`. These are intended to accept flexible inputs: - Support passing in standard C++ file extensions to any of these for compilation. - For `carbon build` in particular, it should not be necessary to pass in `Core` files that are required. - We will require a correlation between library names inside `Core` and directory structure. For example, `prelude/types` [maps to](#mapping-packaging-directives-to-filenames) `core/prelude/types.carbon`. - The same strict correlation will be supported for other packages. At the end, it should be possible to: - Run `carbon build program.carbon` with non-prelude `Core` imports, and get an executable program. - Have Bazel rules that mix C++ code and Carbon code. For example: ```bazel carbon_library( name = "foo", srcs = ["foo.cpp", "foo.impl.carbon"], apis = ["foo.carbon"], ) carbon_binary( name = "bar", srcs = ["main.cpp"], deps = [":carbon_library"], ) ``` ## Details ### Command changes #### Compile command The `carbon compile` command is intended to be a straightforward single input, single output command. Dependencies will be provided through a combination of: - Given a package name to directory mapping, a [filename mapping](#mapping-packaging-directives-to-filenames) based on the library name. - Potentially other input files passed through a flag, for use in imports (not producing their own object files). - A single input source file for primary compilation. - A single optional output file, which for `.carbon` will default to `.o` (including `.impl.carbon` becoming `.impl.o`). As part of supporting a mix of C++ and Carbon files, we will support `carbon compile foo.cpp` with results similar to `carbon clang -- -c foo.cpp`. #### Build command The `carbon build` command will be the new, simple way to compile, as a replacement for `carbon compile`. It will: - Load provided files. - For packages with directory mappings, particularly `Core`, add all `.carbon` files as inputs. - For `Core`, we expect `.o` files to be produced in the same way as for `carbon link`. - For other packages, all files in the directory will be compiled, although there may be some support added for using pre-compiled state (not explicitly proposed). - Do something similar to the appropriate series of `carbon compile` invocations. - A key divergence is that we should avoid re-checking files that would be used across multiple `carbon compile` invocations. - Run the equivalent of `carbon link` over produced inputs. While the build command will default to providing an executable program, we may also want it to be capable of producing `.a` and `.so` files. However, we can decide whether `carbon build` should be required for these kinds of outputs as an implementation detail. #### Link command The `carbon link` command will change to make the following work: ```sh carbon compile foo.carbon -o foo.o carbon link foo.o -o program ``` It will be typical to link multiple object files into a single output file. The output file flag will be optional, defaulting to `program`, possibly with a target-specific extension; for example, `program.exe` for Windows. This requires that `Core` files (not just the prelude) will have been compiled, so that their object files can be included in output. It's expected that this will be provided through on-demand runtimes. It should be possible to opt out of including these, for example so that the Bazel `carbon_binary` rule can use `carbon link` while also providing its own `Core` object files. However, it should be on-by-default. ### Mapping packaging directives to filenames When we need a file for a packaging directive: - The package name will correspond to a root directory. For example, `package Core ...` could correspond to `lib/carbon/core/...`. - The library name will correspond to a path under that, suffixed by `.carbon`. For example, `package Core library "prelude/types";` could correspond to `lib/carbon/core/prelude/types.carbon`. - The default library will use the name `default.carbon`. For example, `package Core;` could correspond to `lib/carbon/core/default.carbon`. Suppose we have some command line `carbon compile a.carbon`, and in `a.carbon`, it does `import Core library "map";`. This needs to load `core/map.carbon`, and without parsing every file matching `core/**/*.carbon`. In order to achieve this: - The `compile` command will have a built-in directory mapping for the `Core` package, for example to `/usr/share/carbon/core` (when installed to the `/usr` prefix). - The `map` library name will need to match the filename, so `/usr/share/carbon/core/map.carbon`. - Slashes may be provided in the library name, for subdirectories. - If `map.carbon` has other `Core` imports, they will be recursively loaded once parsed. - Checking isn't required to process imports from a file. We never need to map `impl` files by library name to a filename, or the other way around; they cannot be discovered through an `import`, and we always need to parse them in order to discover their imports. As a consequence, there is no need to define rules mapping libraries to `.impl.carbon` files. #### Support for other packages Because we'll build this for Core, it would probably be straightforward to expose this for other packages, too. So for example, we could support `--package-path=MyPackage:/my/package` for getting API files. However, that is secondary to the `Core` behavior, so any support may become more of an implementation detail for what makes sense. #### Disallow ambiguous library names For imports which rely on the implicit mapping (not in general), we will disallow ambiguous library names. This includes an explicit `library "default"` string name, which can be ambiguous with the implicit `default` library (both would map to `default.carbon`). ## Example interaction with Bazel ### carbon_library and carbon_binary The Bazel build rules will expose `carbon compile` and `carbon link` behaviors in a slightly more Bazel-idiomatic way. For example, given: ```bazel carbon_library( name = "lib", srcs = ["a.impl.carbon", "b.impl.carbon", "b.carbon"], apis = ["a.carbon"], ) carbon_binary( name = "bin", srcs = ["main.carbon"], deps = [":lib"], ) ``` The way this will approximately work is: - `carbon_library` will have an implicit dependency on a set of `Core` libraries (such as a build target `//carbon/lang:core`). - This will have a network of `carbon_library` rules, some of which may look like `lib`. - For `lib`: - Invoke `carbon compile` four times, producing a `.o` file for each input. - The API files will be additional inputs to the `impl` file compilations. - For `bin`: - Source files will be compiled similarly to `lib`. - The `deps` means `a.carbon` and `b.carbon` will be additional inputs, but it should ideally be an error if `b.carbon` is imported directly. This is required because `a.carbon` can expose `b.carbon` on the import boundary, meaning an indirect import of `b.carbon` must work. - Link object files into an executable. It's possible that we may use `carbon build` where `carbon compile` is mentioned, but if so, it should not make a significant difference in the user-visible behavior. For both, there should be an implicit dependency on the full Core package, not just the prelude. This is because we want the Core package to be easy to access. #### Indirect API exposure The `apis` attribute is suggested to support only _direct_ dependencies. For example: ```bazel carbon_library( name = "a", apis = ["a.carbon"], ) carbon_library( name = "b", apis = ["b.carbon"], deps = [":a"], ) carbon_library( name = "c", srcs = ["c.carbon"], deps = [":b"], ) ``` If `c.carbon` imports `a.carbon`, the build should error that `a.carbon` requires a direct dependency. We should allow forwarding, so that the same could compile without requiring `c` to have a direct dependency on `a`. This should look like `exports = [":a"]`, added to `b` (and superseding the need to list `:a` in `deps`). This feature may see frequent use, for example in `Core` to allow writing it as multiple libraries instead of one large glob. But it's probably also something that can be delayed a little, because we can just use a big glob and force direct dependencies. #### Core package rules In the `core/` directory, we will set up corresponding `carbon_library` rules. These will need to pass flags to opt-out of normal behaviors, in particular the dependency on the prelude library. ## Future work ### Caching checked IR, C++ AST, and other possible compile artifacts As designed, every time any of the `build`, `compile`, or `link` commands are used, all prelude files and possibly more of the `Core` package will be re-checked, along with C++ ASTs being reproduced. Instead, Carbon could serialize checked IR, store produced C++ ASTs, and so on. C++ ASTs in particular could be substantially constructed based on parsed Carbon state, rather than checked Carbon state, allowing more build parallelism. In distributed or cached build systems, being able to reuse portions of the build may increase performance. The specific build outputs we want to store may substantially affect how we would set up a build process. The absence of a decision may lead to the implementation diverging from what's actually needed, meaning parts will be reimplemented later. This isn't expected to be too high cost. There are also ways to improve build performance without taking these steps. [Clang modules](https://clang.llvm.org/docs/Modules.html) might be used for improving Clang compile performance without significant support from Carbon. For now we will rely on whatever caching Bazel does for the `.a` output of a `carbon_library`. No other outputs will be made available. That may change, but leads want to spend our limited development and review time on other features for the 0.1 milestone. ## Rationale - [Language tools and ecosystem](/docs/project/goals.md#language-tools-and-ecosystem) - `carbon build` should support easy experimentation with Carbon, and also small projects. - Other build support is intended to scale up for larger codebases. - [Interoperability with and migration from existing C++ code](/docs/project/goals.md#interoperability-with-and-migration-from-existing-c-code) - The intent is to be able to migrate a CMake, Makefile, or other build at relatively low cost. An invocation to `clang` can typically be replaced with `carbon clang`, linking a binary becomes `carbon link`, and so on. - Similarly, `carbon_library` and `carbon_binary` are important to us for Bazel support and a migration from `cc_library` and `cc_binary`. ## Alternatives considered ### Naming of commands and rules For `carbon compile` and `carbon build`, this is trying to split apart concepts. Some considered alternatives are: - Merge `compile`, and possibly also `link`, into `build`. Flags could be used to differentiate between the versions desired, rather than subcommand names. - We expect that splitting these apart makes it easier to turn them into replacements in C++ builds, and easier to understand even in Carbon-specific builds. - Have `carbon build` produce `a.out` - `a.out` is the default output of most C++ compilers, but it reflects a legacy executable file format. Using the legacy name may reflect backwards compatibility that Carbon doesn't plan. - Changing the default output name is probably low-cost, and people will get used to it. ### Support a full-fledged build system The `build` command as proposed here is intended to be sufficient for quick testing and simple tools. However, it's not intended to be flexible with custom rules, plugins, and so on. These are features offered by systems such as CMake or Bazel. Instead, we could provide a full build system. Multiple other languages have gone in that direction: - In Rust, `cargo` combines a [build system](https://doc.rust-lang.org/cargo/commands/cargo-build.html) and package manager. - In Swift, [SwiftPM](https://www.swift.org/documentation/server/guides/building.html) provides a similar offering as to `cargo`. - In Zig, there are [multiple build system](https://ziglang.org/learn/build-system/) commands. Carbon's [project goal](/docs/project/goals.md#interoperability-with-and-migration-from-existing-c-code) is migration of existing C++ developers, particularly "This means integrating into the existing C++ ecosystem by supporting incremental migration from C++ to Carbon." The expectation is that C++ users will already be using a fully featured build system, such as CMake. Migration should be easier if users can retain their existing build system, particularly since a typical migration can be expected to mix both Carbon and C++ code. While Carbon could provide _both_ a separate compilation system _and_ a fully featured build system, a build system is a substantial undertaking and we expect C++ developers to already have one. ### Don't support packaging directive to filename mappings Instead of making a mapping from packaging directives to filenames, we could generate a list specific to the `Core` package, and not expose that for other packages. We shouldn't manually maintain a mapping for the `Core` package; it should be automated. It's likely that whatever we do in this space, however we would support a mapping, would be of interest to small projects. It will probably be low cost for us to build support for things other than `Core`, so we should just do that. ### Distribute pre-compiled versions of Core files Instead of building object files for `Core` on demand, we could distribute them as part of Carbon. The upside of this is it would make builds a little faster; the downside is that we'd end up in more of a situation where supported target platforms were enumerated, or perhaps where special platforms could be built on-demand in a bespoke manner. We can probably add limited caching where it'd help, and support all platforms using similar logic that way with little performance penalty. ### Create an explicit mapping from packaging directives to files The current `package` and `library` directive design means a given `api` file may have 0 or more `impl` files. We could make it clear from the declaration in an `api` file what `impl` files exist. This would require a split to describe the possible situations. For example: - `library "foo";`: The common case of 1 `impl` file. - `library "foo" api_only;`: Add a single keyword that indicates this is a library with no `impl` file. - `library "foo" multi_impl 3;`: Indicates this is an unusual library with 3 `impl` files. - Multiple impl files are expected to be rare. - We could require numbered filenames (such as `a.impl.carbon`, `a.1.impl.carbon`, `a.2.impl.carbon`), but even knowing how many exist would allow compiles to do validation. If we didn't do this, then it may be equivalent to not require specifying the number of `impl` files (in the example, `multi_impl;` instead of `multi_impl 3;`). Some advantages are: - In the common cases of API-only or 1 impl file, we could avoid scanning the file system for more files. In other words, it reduces file I/O for better performance. - Changes most "missing definition" failures from linker errors to compile-time. - For example at present, if a forward declaration is in an `api` file, then even if we find an `impl` file that is missing the definition we don't know if there's another `impl` file that contains the definition. With this feature, we could diagnose while compiling the common 0 or 1 `impl` file cases. - Allows diagnosing unexpected or missing `impl` files, which can indicate a developer mistake in the build. - If multi-`impl` filenames were constrained to be numbered, we could: - When building, look for specific filenames, instead of doing a file system glob for `impl` filenames. - Loosen the ambiguity constraint on library names to only disallow library names ending with `\.\d+`. Some disadvantages are: - Adds more keywords to the packaging declaration. - Requires updating the API file's declaration in order to modify the number of `impl` files. This has been discussed in the past, but does not seem to be outlined in any proposals as a considered alternative, and this proposal adds new trade-offs for file mappings. Leads have declined this option in order to keep packaging directives simple. ================================================ FILE: proposals/p6357.md ================================================ # C++ Interop: Mapping pointer types [Pull request](https://github.com/carbon-language/carbon-lang/pull/6357) ## Table of contents - [Abstract](#abstract) - [Problem](#problem) - [Background](#background) - [Proposal](#proposal) - [Details](#details) - [Non-nullable pointers to object types](#non-nullable-pointers-to-object-types) - [Inconsistent type sugar](#inconsistent-type-sugar) - [Non-nullable pointers to void](#non-nullable-pointers-to-void) - [Nullable pointers and `Core.Optional`](#nullable-pointers-and-coreoptional) - [`nullptr_t`](#nullptr_t) - [`nullptr`](#nullptr) - [Indexing and pointer arithmetic](#indexing-and-pointer-arithmetic) - [Rationale](#rationale) - [Future work](#future-work) - [Alternatives considered](#alternatives-considered) - [Map all pointers to `T*`](#map-all-pointers-to-t) - [Map `void*` to `()*`](#map-void-to-) - [Map `void*` to `u8*`](#map-void-to-u8) - [Map `void f()` to `fn Cpp.f() -> Cpp.void`](#map-void-f-to-fn-cppf---cppvoid) - [Use the name `Core.CppCompat.Void` instead of `Core.CppCompat.VoidBase`](#use-the-name-corecppcompatvoid-instead-of-corecppcompatvoidbase) - [Map `Cpp.void` to `()` instead of `Core.CppCompat.VoidBase`](#map-cppvoid-to--instead-of-corecppcompatvoidbase) - [Do not provide `Cpp.void` at all](#do-not-provide-cppvoid-at-all) ## Abstract This proposal defines direct, zero-overhead mappings from C++ object pointer types and `std::nullptr_t` to corresponding Carbon types. ## Problem In order to support interoperability between C++ and Carbon, we need to map types in each language to the other language. Currently we do not have a defined mapping for pointer types, which are an important concept in both languages. However, a direct mapping is not appropriate, as pointer types have different semantics in Carbon versus in C++. ## Background Pointer types in Carbon and in C++ have different semantics. C++ pointers can be null, can be indexed if they point into an array, treat any non-array object as pointing to an array of a single element for indexing purposes, and can point to a position one past the last element of an array. Carbon pointers allow none of these things, and always point to an object. There are three kinds of pointer type in C++: - A _pointer to object type_ is a pointer whose pointee type is an object type, such as a scalar type or a class type. - A _pointer to void_ is a pointer type whose pointee type is (possibly `const` and/or `volatile`) `void`. - Collectively, pointer to object types and pointer to void types are called _object pointer types_, despite `void` not being an object type. - A _pointer to function type_, or equivalently _function pointer type_, is a pointer whose pointee type is a function type. C++ pointers to object types support arithmetic operations: - Addition and subtraction are supported, treating pointers as an affine space over the integers. Formally, arithmetic is only permitted between pointers that point to elements of the same array object, but in practice these operations are used substantially more broadly. - Pointers can be compared relationally, if they point to subobjects of the same object. The exact rules are a little more subtle than this, but formally, comparisons across distinct complete objects have unspecified results and most comparisons within a complete object have specified results. In practice, pointer comparisons on modern architectures are treated as a total order, except that the status of comparisons of bitwise-equal pointers where one points past the end of one complete object and the other points to the start of a distinct complete object are nebulous. - Pointers support array indexing notation `p[i]` -- and, to the surprise of many and the delight of few, `i[p]` -- which is mostly equivalent to `*(p + i)`. In addition, the type of `nullptr`, known commonly by its standard library alias `std::nullptr_t`, is important to modern C++ as a mechanism for forming null pointer values. This type is not a pointer type, but implicitly converts to every pointer type, forming a null pointer value of that type. C++ also has pointers to members, `T C::*`, which are a distinct type from pointer types in C++. ## Proposal This proposal defines a mapping from C++ object pointer types and `std::nullptr_t` into Carbon types, introducing new Carbon types as necessary to provide the mapping: | C++ type | Carbon type | Notes | | ---------------- | ----------------------------------------------- | ------------------------------------------- | | `T*` | `Core.Optional(T*)` | null pointers map to `None` | | `T* _Nonnull` | `T*` | non-nullable pointers don't need `Optional` | | `const T*` | `Core.Optional(const T*)` | | | `void*` | `Core.Optional(Core.CppCompat.VoidBase*)` | `void` maps to `()` in some other contexts | | `void* _Nonnull` | `Core.CppCompat.VoidBase*` | | | `const void*` | `Core.Optional(const Core.CppCompat.VoidBase*)` | | | `nullptr_t` | `Core.CppCompat.NullptrT` | | Function pointer types and pointer to member types are out of scope for this propopsal. ## Details ### Non-nullable pointers to object types C++ doesn't have non-nullable pointer types, but C++ implementations do, in various forms: | C++ type | Supported by | Behavior if null | | ----------------------------------------- | ------------ | ---------------- | | `T* _Nonnull` | Clang | Erroneous | | `T* __attribute__((nonnull))` | Clang | Undefined | | `void f(T*) __attribute__((nonnull))` | GCC, Clang | Undefined | | `T* f() __attribute__((returns_nonnull))` | GCC, Clang | Undefined | | `void f(_Notnull_ T*)` | MSVC | Defined | | `_Ret_notnull_ T* f()` | MSVC | Defined | We will support and encourage use of Clang's `_Nonnull` annotation, as it is the only form that applies to a general pointer type rather than to a function parameter or return type. Clang's `T* _Nonnull` maps to Carbon's `T*`. The `__attribute__((nonnull))` and `__attribute__((returns_nonnull))` forms will also be mapped into non-nullable Carbon pointers, as these attributes are widespread in existing code. A pointer type that is the type of a function parameter or the return type of a function that is annotated with these attributes maps to Carbon's `T*`. The MSVC attributes are intended for use by static analysis tools only, not as compiler inputs, so are not suitable for our uses, and are mentioned here only for completeness. #### Inconsistent type sugar The `_Nonnull` annotation is treated as _type sugar_, not as producing a different type. This has some significant consequences for its use: - Type sugar is easily lost through incautious type navigation. Any operation that maps to a canonical type will lose the annotation. This is largely just a constraint that the toolchain implementation be careful, except... - ... type sugar is lost across template instantiations. There is work in Clang to preserve type sugar across template instantiations, but [at this time it is not complete](https://godbolt.org/z/hP35776zj). This means that in templates, the distinction between non-nullable pointers and nullable pointers should be expected to be lost. - The annotation can differ between redeclarations of the same entity. Inconsistent annotations may lead to a type being treated as either nullable or non-nullable. Clang will warn on this in some cases, but the fragility of type sugar means that such warnings should not be expected to be entirely robust. The consequences in C++ for losing or incorrectly determining nullability are mostly not too severe -- loss of best-effort diagnostics, and the compiler treating the program as having defined behavior when the rules say its behavior is undefined. But if it causes a type to map to a different type in Carbon, that is potentially a larger issue, as it may affect whether the Carbon program compiles. For now we will use these type sugar anontations to inform our type mapping, but will revisit this decision if they are too problematic in practice. ### Non-nullable pointers to void `void` broadly has two different meanings in C++: - A complete type with an empty representation, a size and alignment of 1, and a single unique value. This is the meaning that is used by `void f()`, `static_cast(expr)`, and arguably by the special case `int f(void)`. - An incomplete type that is a supertype of all other types (sometimes known as a top type ⊤, or a universal type). This is the meaning that is used by `void*`, as well as related ideas like `dynamic_cast`. While C++ models this form of `void` as incomplete, it would be more accurate to consider it to be abstract. There have been long-lived attempts to replace the second meaning in C++ with the first, but so far they have not succeeded. If they do succeed, our approach will need to shift to accommodate that change. We map the first kind of `void` to Carbon's `()` type. In particular, a function that returns `void` in C++ returns `()` in Carbon. While these types don't have the same representation in general, they do have the same representation as a return type, with both types corresponding to nothing being returned. For the second kind of `void`, we introduce a new compatibility type: ```carbon abstract class Core.CppCompat.VoidBase {} ``` Notionally, we think of every Carbon type, including types imported from C++, as inheriting from this type, but practically we support a conversion from any pointer type to a pointer to `VoidBase`, and we support a conversion from a value of any type to a value of type `VoidBase`. Other inheritance-based language properties should decide whether to treat `VoidBase` as a supertype of all other types by considering how C++ treats `void` in similar contexts. This new type is our primary mapping for `void`; the mapping of `void` return types is treated as a special case that applies only in return position. Because `void` in C++ can only appear in very limited positions, this means that `VoidBase` is used as the mapping for `void` in only the following situations: - As the pointee type of a pointer type. - As a type template argument. - As a type of a typedef or type alias. This seems like the best balance: for example, given ```c++ typedef void OpaqueObject; void Call(OpaqueObject* handle); ``` ... it seems useful to map `Cpp.OpaqueObject*` to `VoidBase*`, not to `()*`. And given: ```c++ template T ReturnAT() { return T(); } ``` ... the call `Cpp.ReturnAT(VoidBase)` will still have a return type of `()` rather than an abstract return type, because the type appeared in return position after instantiation. As a convenience shorthand, the name `Cpp.void` is added to the `Cpp` package and refers to `Core.CppCompat.VoidBase`. C++'s `void* _Nonnull` maps to `Cpp.void*`, and similarly `const void* _Nonnull` maps to `const Cpp.void*`. ### Nullable pointers and `Core.Optional` Nullable object pointers, including nullable pointers to `void`, map to `Core.Optional(T*)`. Null pointer values map to the optional's "none" value. This places a constraint on the implementation of `Core.Optional(T*)` that it has the same ABI as a C++ pointer, including that the "none" state is represented with a C++ null pointer representation. While we do not yet have an approved design for the `Core.Optional` type, it is already in use in the design of `for` statements. Adding a design for `Core.Optional` is out of scope for this proposal. ### `nullptr_t` The C++ null pointer type, `decltype(nullptr)`, is exposed in the standard library as `std::nullptr_t`. This type is a scalar type built into the C++ language, and as such, we need a custom mapping for it -- our mappings for other categories of C++ types don't cover it. C++'s `nullptr_t` has the same representation as `void*`, but its representation comprises only padding bits. This doesn't correspond to any existing type in Carbon, so we introduce a new compatibility type to model it: ```carbon class Core.CppCompat.NullptrT { adapt MaybeUnformed(VoidBase*); } ``` The C++ type maps to the above type, so after `import Cpp library "";`, it can also be named as `Cpp.std.nullptr_t`. #### `nullptr` The name `Cpp.nullptr` is added to the `Cpp` package, and refers to the (empty) constant value of type `Core.Cpp.NullptrT`. ### Indexing and pointer arithmetic This proposal provides no support for indexing or pointer arithmetic on Carbon pointers mapped from C++ pointers. Carbon pointers do not support indexing, and the result of mapping a C++ pointer into Carbon would be a pointer that does not support indexing. In the other direction, no mechanism prevents a Carbon pointer from being passed into C++ code and then indexed, even though that is not an operation that would be possible in pure, safe Carbon code. Eventually we will need to provide some way to take a C++ pointer and perform the equivalent of indexing into it in Carbon code. However, this mechanism will require additional Carbon language features to be designed before it can be specified. In particular, we will need a safety story for bounds safety, and a type for representing an indexable location in some way, such as an array iterator or array cursor type. As a result, we leave support for indexing and pointer arithmetic as future work. ## Rationale Goals: - [Performance-critical software](/docs/project/goals.md#performance-critical-software) - C++ types are mapped to Carbon types with the same representation. This avoids the need for any deep copies on the interop boundary. - [Code that is easy to read, understand, and write](/docs/project/goals.md#code-that-is-easy-to-read-understand-and-write) - The convenience names `Cpp.void` and `Cpp.nullptr` make it easier to write code that interoperates with C++ void pointers and C++ nullable pointers. - [Practical safety and testing mechanisms](/docs/project/goals.md#practical-safety-and-testing-mechanisms) - C++ null pointers cannot "leak" into Carbon non-nullable pointer types, except by violating a "nonnull" annotation in the C++ code. If `_Nonnull` is violated, the result is not undefined behavior unless the pointer is used in a context that would result in undefined behavior in C++, such as a load or store through the pointer. - [Modern OS platforms, hardware architectures, and environments](/docs/project/goals.md#modern-os-platforms-hardware-architectures-and-environments) - We do not assume that a null pointer has an all-zero-bits representation. This is not true in practice on some modern heterogeneous compute architectures. - [Interoperability with and migration from existing C++ code](/docs/project/goals.md#interoperability-with-and-migration-from-existing-c-code) - This proposal significantly enhances interoperability with C++ code, by providing a mapping for very common vocabulary types in C++. Principles: - [All APIs are library APIs](/docs/project/principles/library_apis_only.md) - The types added to support C++ are all ordinary class types provided in the Carbon prelude. - [Low context-sensitivity](/docs/project/principles/low_context_sensitivity.md) - The Carbon `Cpp.void` type has only one meaning, rather than having a context-sensitive meaning. The C++ `void` type still has two different meanings, and therefore two different mappings into Carbon, but that problem is outside our domain. - [Namespace cleanliness](/docs/project/principles/namespace_cleanliness.md) - The names we are adding to the `Cpp` package, `void` and `nullptr`, are C++ keywords, and so do not conflict with any C++ identifier. While Clang does provide an extension to define entities with these names, for example `int __identifier(void);`, interoperting with such code is not a priority. ## Future work - Add a design for `Core.Optional`, as we now have multiple language proposals that depend upon it. - Design interop for function pointers. - Design interop for pointers to members. - Design an approach for providing the functionality that C++ exposes as pointer indexing and pointer arithmetic. ## Alternatives considered ### Map all pointers to `T*` We could avoid wrapping nullable pointers with `Core.Optional`. However, doing so opens a large hole in Carbon's story for pointers, wherein pointers are not nullable. ### Map `void*` to `()*` We could map `void` to `()` in all contexts, and map C++'s `void*` to Carbon's `()*` -- or rather, to `Core.Optional(()*)`. However, in order to support passing arbitrary Carbon pointers to C++ `void*` parameters, we would need to allow `T*` to implicitly convert to `void*` in Carbon, which means we would need to allow `T*` to implicitly convert to `()*`. Therefore the C++ modeling of `void` as a supertype of all other types leaks out into pure Carbon code. This seems undesirable. ### Map `void*` to `u8*` We could map `void*` to `u8*` or to a pointer to some other byte-like type, to reflect that it represents a pointer to storage. This would result in an N:1 mapping from C++ types to Carbon types, because both `void*` and `uint8_t*` would map to the same Carbon type. The same would happen if we picked any other Carbon type that has a corresponding C++ type that is not `void`. It's strongly desirable that our mapping between C++ and Carbon types fully round trips, because otherwise passing types between the two languages, such as in metaprogramming or by way of template argument deduction, would be lossy. For example, if both a `vector` and a `vector` map to the same Carbon type `buf(u8*)`, then passing an object of that type from C++ into Carbon and then back into C++ must result in a type that mismatches at least one of the original types. It's possible that we could accept some N:1 mappings, but given how common `void*` is on C and C++ API boundaries, the risk of problems seems particularly significant in this case. ### Map `void f()` to `fn Cpp.f() -> Cpp.void` We could use the custom `Cpp.void` type even as a function return type, removing the non-uniformity of mapping it to `()` in function returns and to `Cpp.void` elsewhere. However, `Cpp.void` is an abstract type, so there should not exist initializing expressions of this type. We could address that by instead mapping `void` to `partial Cpp.void`, or aliasing `Cpp.void` to `partial Core.CppCompat.VoidBase`, but either way that means that `void*` maps to a type whose pointee doesn't have the abstract / incomplete behavior that we desire. ### Use the name `Core.CppCompat.Void` instead of `Core.CppCompat.VoidBase` We could use a simpler name for the compatibillity type. However, given that there are two different meanings of `void` in C++, having some extra clarity about which meaning is intended seems useful. ### Map `Cpp.void` to `()` instead of `Core.CppCompat.VoidBase` We could pick the other meaning of `void` as the meaning of `Cpp.void`. However, the `()` meaning is only really interesting as a function return type, and there is no reason to reach for `Cpp.void` if that meaning is desired. So mapping `Cpp.void` to `VoidBase` is more useful. Also, we want the mappings between C++ and Carbon types to be bidirectional, to the extent that's possible. Mapping Carbon's `()` to C++'s `void` type would mean that we can't consistently map all Carbon tuple types to the same family of C++ tuple types, such as `std::tuple`. ### Do not provide `Cpp.void` at all We could ask developers to write out the name of `VoidBase` when needed. But it's long and cumbersome, and we expect most other C++ types with a corresponding keyword to be provided in the `Cpp` package, so providing it is both useful and improves language consistency. ================================================ FILE: proposals/p6358.md ================================================ # C++ Interop: API importing and semantics [Pull request](https://github.com/carbon-language/carbon-lang/pull/6358) ## Table of contents - [Abstract](#abstract) - [Problem](#problem) - [Background](#background) - [Proposal](#proposal) - [Details](#details) - [The `Cpp` package and namespace mapping](#the-cpp-package-and-namespace-mapping) - [`import Cpp library` directive](#import-cpp-library-directive) - [C++ built-in types](#c-built-in-types) - [Rationale](#rationale) - [Alternatives considered](#alternatives-considered) ## Abstract This proposal defines the concrete technical mechanisms for C++ interoperability. It specifies the precise syntax and semantics for importing C++ APIs. This includes the `import Cpp library "..."` and implicitly importing C++ built-in entities, and the establishment of the `Cpp` package as the dedicated namespace for all imported entities. ## Problem While Carbon has a stated goal of seamless C++ interoperability, and a high-level direction has been agreed upon, there is currently no concrete, specified mechanism for developers to actually import and use C++ APIs. This proposal aims to address that by defining the specific syntax and semantics for C++ interoperability. ## Background One of Carbon's primary goals is to be a successor language. This strategy is entirely dependent on seamless, bidirectional interoperability with C++ to enable large-scale adoption and migration for existing C++ codebases. This proposal provides the necessary details on how C++ APIs should be imported. ## Proposal We propose to formalize the following specific design elements for C++ interoperability: 1. **The `Cpp` Package:** All imported C++ entities, whether from built-ins or library headers (see below), will be nested within a dedicated `Cpp` package. This prevents name collisions with Carbon code and makes the language boundary explicit. ```carbon fn UseCppTypes() { // Access C++ types and functions by way of the Cpp package var circle: Cpp.Circle = Cpp.GenerateCircle(); Cpp.PrintCircle(circle); } ``` 2. **Importing C++ Header-Defined APIs:** To import C++ APIs from a specific library header file (for example, `` or `"my_library.h"`), Carbon code will use the `import Cpp library "..."` directive. ```carbon import Cpp library ""; import Cpp library "circle.h"; ``` 3. **Importing C++ Built-in Entities:** To access fundamental C++ types (such as `int`, `bool`, etc.), no explicit importing is needed and writing `Cpp.int` and `Cpp.bool` would just work. ## Details ### The `Cpp` package and namespace mapping All C++ declarations will be imported into the `Cpp` package. C++ namespaces will be mapped to nested packages within `Cpp`. For example, a C++ function `std::string::find` would be accessible in Carbon as `Cpp.std.string.find`. The C++ global namespace will be mapped to the `Cpp` package itself. So a function `MyGlobalFunction` in the C++ global namespace will be `Cpp.MyGlobalFunction` in Carbon. ### `import Cpp library` directive The `import Cpp library "..."` directive will instruct the Carbon compiler to parse the specified C++ header file. The compiler will use the standard C++ include paths to locate the header. Additional paths can be provided through compiler flags. The Carbon compiler will leverage a C++ front-end, like Clang, to parse the headers. This ensures a high degree of compatibility with existing C++ code. Only the declarations from the header will be imported, not the definitions (unless they are inline). ### C++ built-in types A set of fundamental C++ types will be available within the `Cpp` package without any explicit `import` directive. Mapping examples: | C++ Type | Carbon Type | | -------------- | ------------------ | | `int` | `Cpp.int` | | `unsigned int` | `Cpp.unsigned_int` | | `double` | `Cpp.double` | | `float` | `Cpp.float` | | `bool` | `Cpp.bool` | | `char` | `Cpp.char` | This automatic availability of built-in types is designed to make basic interoperability tasks as smooth as possible. ## Rationale - [Code that is easy to read, understand, and write](/docs/project/goals.md#code-that-is-easy-to-read-understand-and-write) - **Explicitness and Clarity:** The `import Cpp library "..."` directives make all dependencies on C++ headers. - **Preventing Name Collisions:** The `Cpp` package is a critical design element. It provides a clean, unambiguous namespace for all imported C++ code. - [Interoperability with and migration from existing C++ code](/docs/project/goals.md#interoperability-with-and-migration-from-existing-c-code) - This proposal defines a foundation for seamless C++ interoperability. ## Alternatives considered - **Alternative: Explicitly importing built-ins:** We considered making C++ built-in types (like `int`) require some `import Cpp` directive like `import Cpp;`. - **Reason for Rejection:** Since `Cpp` is a special package, it should be implicitly imported, similar to Carbon's prelude. ================================================ FILE: proposals/p6395.md ================================================ # Type completeness in extend [Pull request](https://github.com/carbon-language/carbon-lang/pull/6395) ## Table of contents - [Abstract](#abstract) - [Problem](#problem) - [Background](#background) - [Proposal](#proposal) - [Details](#details) - [Rationale](#rationale) - [Alternatives considered](#alternatives-considered) ## Abstract Require any target scopes in an `extend` declaration to be complete, since `extend` changes the scopes which are looked in for name lookup to include those named in the declaration. ## Problem Proposal [#5168](https://github.com/carbon-language/carbon-lang/blob/trunk/proposals/p5168.md) laid out rules for when a facet type needs to be identified or complete. When `require impls X` is written, then `X` must be identified. However it does not specify any rule for `extend require impls X`. And we lack completeness rules for other `extend` declarations, including `extend impl as X`, `extend base: X`, and `extend adapt X`. An `extend` declaration always names one or more target entities which will be included in the search for names when looking in the enclosing scope of the `extend` declaration. In order to do name lookup into an entity, it must be complete to avoid poisoning names in the entity. ## Background - Proposal [#5168](https://github.com/carbon-language/carbon-lang/blob/trunk/proposals/p5168.md): Forward `impl` declaration of an incomplete interface - Proposal [#2760](https://github.com/carbon-language/carbon-lang/blob/trunk/proposals/p2760.md): Consistent `class` and `interface` syntax - Proposal [#0777](https://github.com/carbon-language/carbon-lang/blob/trunk/proposals/p0777.md): Inheritance ## Proposal An `extend` declaration declares that the enclosing scope extends the scope associated with any target entity named in the declaration. That is to say name lookups into the enclosing scope will also look into the scopes which are nominated by the `extend` declaration. The `extend` declaration requires that the target scopes named in an `extend` declaration are complete, or if the target is a generic parameter, requires the type of the parameter to be complete. The scope of a facet type formed by a `where` declaration extends the scope of its first operand. And the scope of a facet type formed by a `&` operator extends the scopes of both of its operands. In either case, the scope of the facet type is complete if every scope it extends is complete. The facet type `type` is associated with an empty scope and is complete. | Facet type | Requirement | | ---------------------------- | ------------------------------------- | | `I` | Requires `I` is complete. | | `I & J` | Requires `I` and `J` are complete. | | `type where U impls J` | Requires `type` is complete. | | `I & (type where U impls J)` | Requires `I` and `type` are complete. | ## Details To `extend` an entity `Y` with another `Z` means that name lookups into `Y` will also look into `Z`. Immediately after the `extend` operation, members of `Z` should also be found when doing name lookup into `Y`, both from outside and from inside the definition of `Y`. In order to be able to perform lookups into `Z`, we require that `extend` operations only target scopes that are complete. This requirement functions recursively. Given an interface `B` that extends another interface `A`: By naming `A` in an extend declaration, we require `A` is complete. This provides that its entire definition is known, and thus its `extend` relationship to `B`. The `extend` relationship there also provides that `B` is complete. If the target scope of an `extend` declaration is a generic parameter, its type must be complete where the `extend` declaration is written, as name lookups into the extended scope will look into the type of the generic parameter. ```carbon interface I { fn F(); } class C(T:! I) { extend base: T; // `F` names `T.F` here, found in `I`. fn G() { F(); } } ``` As any generic parameter in the enclosing scope is replaced by a more specific value, extended scopes that depend on a generic parameter must remain complete. This includes forming a specific for the extended scope involving the parameter in order to surface any monomorphization errors in the resulting specific. In the next example, the `extend` declaration in interface `A(N)` names a symbolic facet type which can produce monomorphization errors when a negative value is provided for `N`. When a more specific value for the target `B(N)` is provided, we require the specific value to be complete as well by forming the specific. A diagnostic error would be produced while checking `C(-1)` for completeness, as it requires `A(-1)` to be complete, which requires `B(array(i32, -1))` to be complete, and that contains an invalid type. ```carbon interface B(T:! type) {} interface A(N:! i32) { // Requires `B(N)` to be complete. extend require impls B(array(i32, N)) {} } class C(N! i32) { // Requires `A(N)` to be complete, which requires `B(N)` to be complete. extend impl as A(N); } fn F() { // Requires `C(-1)` to be complete, which requires `A(-1)` to be complete, which requires `B(array(i32, -1))` to be complete. var c: C(-1); } ``` These rules prohibit an `extend` declaration from naming its enclosing scope, since by being part of the definition of that scope, it is implied that the enclosing scope is not complete. This seems reasonable as all names available inside the enclosing interface or named constraint are already available or would conflict with the ones that are. ## Rationale This is based on the principle of [Code that is easy to read, understand, and write](/docs/project/goals.md#code-that-is-easy-to-read-understand-and-write). For code to be easy to write, the rules need to be consistent. Once an `extend` declaration has been written, the names inside should become available through the enclosing scope immediately. If we allow an interface named in an `extend` declaration to be incomplete, then name lookup will fail ambiguously. Those same names may become valid later once the interface is defined. ## Alternatives considered We considered not requiring that the scope named in an `extend` declaration be complete where it is written, but only when the enclosing scope is required to be complete. This is more flexible, allowing entities to be defined later in the program. However this does not allow the use of names from the target scope in the `extend` to be used from within the enclosing definition scope. They would not become available until the enclosing definition scope was closed and complete. In particular, we want this to work: ```carbon interface Z { let X:! type; fn F() -> X; } class C { extend impl as Z where .X = () { // Names `X` directly. fn F() -> X; } // Names `X` and `F` directly. fn G() -> X { return F(); } } // Now `C` is complete, `C.X` and `C.F` are also available. fn H() -> C.X { return C.F(); } ``` ================================================ FILE: proposals/p6641.md ================================================ # Clang IRGen in Carbon [Pull request](https://github.com/carbon-language/carbon-lang/pull/6641) ## Table of contents - [Abstract](#abstract) - [Problem](#problem) - [Background](#background) - [Proposal](#proposal) - [Rationale](#rationale) - [Alternatives considered](#alternatives-considered) - [PR5543 More closely mimic the Clang compilation](#pr5543-more-closely-mimic-the-clang-compilation) - [Status Quo with Improvements](#status-quo-with-improvements) - [Upstream Clang Changes to use Phase Based Lowering](#upstream-clang-changes-to-use-phase-based-lowering) ## Abstract Document a principled and robust approach to Clang interop with respect to the conflict between Clang's continuous lowering approach and Carbon's phase based lowering. ## Problem Clang performs the equivalent of Carbon's `lower` progressively, interleaved with parsing/semantic analysis. This is in conflict with Carbon's phase-based approach and leads to bugs in missing functionality in Clang's generated IR during Carbon/C++ interop. ## Background A review of different ways Carbon and other tools use Clang APIs is written up [here](../toolchain/docs/design/clang_api.md). While Swift looks like the most comparable, its hand-crafted reimplementation of the Clang Sema/IRGen interaction seems like a maintenance risk. ## Proposal Carbon should use Clang is such a way that Clang can have the `clang::CodeGenerator` attached throughout Clang's Sema phase, to ensure parity with existing Clang usage/behavior. This means Clang will diverge from Carbon's strict phase-based approach (Clang will be creating LLVM IR during `check` despite Carbon deferring all LLVM IR lowering for Carbon itself to the `lower` phase). This divergence seems worthwhile to keep Carbon as compatible with Clang's functionality as possible now and in the face of possible changes to Clang in the future. (practically speaking, this is implemented in [PR6569](https://github.com/carbon-language/carbon-lang/pull/6569)) ## Rationale - [Interoperability with and migration from existing C++ code](/docs/project/goals.md#interoperability-with-and-migration-from-existing-c-code) - Establishing a design that leaves us as consistent with Clang's C++ behavior as possible both now, and in the future, with as little maintenance needed when Clang changes are made. ## Alternatives considered ### [PR5543](https://github.com/carbon-language/carbon-lang/pull/5543) More closely mimic the Clang compilation This looks closer/identical to Clang's API usage. But the problem is that Clang’s `FrontendAction` API (down through… `CreateFrontendBaseAction`, `EmitObjAction`, `ASTFrontendAction`, `ParseAST`) is a closed system (does all the work from the start to the end) whereas Carbon wants to incrementally use Clang while parsing more Carbon, calling back into C++, etc, before finishing the C++ parsing. To address that atomicity, PR5543 uses a background thread (without actual concurrency) \- doing part of the `FrontendAction` work, pausing, doing some Carbon work, then finishing up in lowering: - `check` executes the `clang::FrontendAction` on a background thread - This runs up until `handleTranslationUnit` and blocks - `check` does things to the AST, trigger template instantiation, etc - `lower` triggers the background thread to continue to IR generation from the AST However, using a background thread to achieve this requires a great deal of complexity. We have to both spawn and maintain the background thread, as well as inject cross-thread synchronization to orchestrate each phase of Clang's execution. Especially with many different C++ compilations, this complexity and overhead would be increasingly concerning. ### Status Quo with Improvements Keep Clang parsing without an attached `clang::CodeGenerator`, use our own ASTConsumer to gather whatever we seem to need during parsing/sema that will be needed during lowering. The code snippets above show some functionality we’re missing based on the callbacks that aren’t implemented/replayed in Carbon’s current implementation. Risk: Missing Clang features because we didn’t save the right things for lowering either now or with Clang changes in the future. ### Upstream Clang Changes to use Phase Based Lowering Change Clang to no longer lower during parsing/sema, but in a single pass after that work. This could benefit Swift and similar API users, might simplify Clang/improve its performance/make Clang’s IRGen more flexible (it wouldn’t struggle so much when AST properties chang (currently that’s not allowed, but even when a definition is found after a previous declaration - updates have to be made, etc - that wouldn’t be a problem if all IRGen was done late)). Risk: Upstream changes are slower, require more social engineering, buy in/consensus building, and the benefit to Carbon (being able to do Clang lowering at the same time as Carbon lowering) seems limited. ================================================ FILE: proposals/p6668.md ================================================ # C++ interop type mapping for integer and floating-point literals [Pull request](https://github.com/carbon-language/carbon-lang/pull/6668) ## Table of contents - [Abstract](#abstract) - [Problem](#problem) - [Background](#background) - [C++ literals](#c-literals) - [Integer literals](#integer-literals) - [Type of C++ integer literals](#type-of-c-integer-literals) - [Clang](#clang) - [Gcc](#gcc) - [Floating-point literals](#floating-point-literals) - [Carbon literals](#carbon-literals) - [Integer literals](#integer-literals-1) - [Floating-point literals](#floating-point-literals-1) - [Literal types](#literal-types) - [No suffixes](#no-suffixes) - [Proposal](#proposal) - [Details](#details) - [Carbon to C++ type mapping](#carbon-to-c-type-mapping) - [Integer literals](#integer-literals-2) - [Floating-point literals](#floating-point-literals-2) - [C++ to Carbon type mapping](#c-to-carbon-type-mapping) - [Integer literals](#integer-literals-3) - [Floating-point literals](#floating-point-literals-3) - [Future work](#future-work) - [More robust hexadecimal and binary literals in Carbon](#more-robust-hexadecimal-and-binary-literals-in-carbon) - [Rationale](#rationale) - [Alternatives considered](#alternatives-considered) - [Carbon to C++ type mapping](#carbon-to-c-type-mapping-1) - [Use the C++ standard rules for hexadecimal and binary literals](#use-the-c-standard-rules-for-hexadecimal-and-binary-literals) - [Use the Clang way of determining the type](#use-the-clang-way-of-determining-the-type) - [C++ to Carbon type mapping](#c-to-carbon-type-mapping-1) - [Use Carbon literal types](#use-carbon-literal-types) ## Abstract Provides bidirectional mappings for types of integer and floating-point literals between Carbon and C++. For example, given a literal `123`, defines the interop type. ## Problem This document addresses Carbon <-> C++ type mapping of integer and floating literals. This comes to use for example during overloading resolution of C++ functions, when a C++ function is called from Carbon with a literal as a call argument. It also appears for example when importing C++ macros to Carbon for example macros which define constants where the constant is a literal. ## Background ### C++ literals #### Integer literals ##### Type of C++ integer literals As specified in the [C++ standard](https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2023/n4950.pdf#page=31&zoom=100,85,693), the type of the literal is the first type from the list below in which the value can fit based on its **suffix** and the **base** of the literal. | Suffix | Decimal bases | Binary, octal or hexadecimal bases | | --------------------------------------- | ------------------------------------------------- | ----------------------------------------------------------------------------- | | (none) | `int`, `long`, `long long` | `int`, `unsigned`, `long`, `unsigned long`, `long long`, `unsigned long long` | | `u` or `U` | `unsigned`, `unsigned long`, `unsigned long long` | `unsigned`, `unsigned long`, `unsigned long long` | | `l` or `L` | `long`, `long long` | `long`, `unsigned long`, `long long`, `unsigned long long` | | `u` or `U` and `l` or `L` | `unsigned long`, `unsigned long long` | `unsigned long`, `unsigned long long` | | `ll` or `LL` | `long long` | `long long`, `unsigned long long` | | `u` or `U` and `ll` or `LL` | `unsigned long long` | `unsigned long long` | | `z` or `Z` (since C++23) | the signed version of `std::size_t` | the signed version of `std::size_t`, `std::size_t` | | `u` or `U` and `z` or `Z` (since C++23) | `std::size_t` | `std::size_t` | For details on `std::size_t`, see [cppreference](https://en.cppreference.com/w/cpp/types/size_t). If the value is too big to fit in any of these types, and an extended integer type exists, then it may be assigned an extended type. If all types are signed, then it may fit into a signed extended type. If all types are unsigned then it may be fitted into an unsigned extended type. If there are both signed and unsigned types in the list, then both signed and unsigned extended types are possible types. If the value can’t fit in any of the types, the program is ill-formed. ##### Clang Clang diverges from the standard in that if the decimal integer literal doesn’t fit to a type from the list, instead of assigning an extended integer type, it assigns `unsigned long long`. Example: ```cpp #include inline auto foo(long a) -> void { printf("hello from foo_long(%ld)", a); } inline auto foo(unsigned long a) -> void { printf("hello from foo_unsigned_long(%lu)", a); } inline auto foo(long long a) -> void { printf("hello from foo_long_long(%lld)", a); } inline auto foo(unsigned long long a) -> void { printf("hello from foo_unsigned_long_long(%llu)", a); } inline auto foo(__int128 a) -> void { printf("hello from foo___int128");} int main() { foo(9223372036854775808); // MAX_LONG + 1 return 0; } ``` Output: ``` :12:9: warning: integer literal is too large to be represented in a signed integer type, interpreting as unsigned [-Wimplicitly-unsigned-literal] 12 | foo(9223372036854775808); | ^ 1 warning generated. Execution build compiler returned: 0 Program returned: 0 hello from foo_unsigned_long_long(9223372036854775808) ``` ##### Gcc Gcc shows a warning that the value will be treated as an unsigned integer, but it actually assigns the type `__int128` to it. Output of the example above: ``` :12:9: warning: integer constant is so large that it is unsigned 12 | foo(9223372036854775808); | ^~~~~~~~~~~~~~~~~~~ Execution build compiler returned: 0 Program returned: 0 hello from foo___int128 ``` #### Floating-point literals The type of a floating literal is `double` unless explicitly specified by a suffix. When there is a suffix, then the suffix determines the type. | Suffix | Floating-point literal type | | ------------------------------ | --------------------------- | | (none) | `double` | | `f` or `F` | `float` | | `l` or `L` | `long double` | | `f16` or `F16` (since C++23) | `std::float16_t` | | `f32` or `F32` (since C++23) | `std::float32_t` | | `f64` or `F64` (since C++23) | `std::float64_t` | | `f128` or `F128` (since C++23) | `std::float128_t` | | `bf16` or `BF16` (since C++23) | `std::bfloat16_t` | If the value doesn’t fit the type, the program is ill-formed. ### Carbon literals #### Integer literals Carbon has **decimal, hexadecimal and binary** integer literals. Example: - `123` (decimal) - `0x1FE` (hexadecimal) - `0b10` (binary) There are no suffixes for the integer literal types. #### Floating-point literals Carbon supports **decimal and hexadecimal** floating-point literals. Example: 1. Decimal: - `123.456` - `1.23456e791` 2. Hexadecimal: - `0x1.Ap123` #### Literal types Carbon has literal types, currently `Core.IntLiteral` and `Core.FloatLiteral`. These are convertible to any type where they fit without any truncation or loss of precision. At present, type deduction would retain the type of the literal. For example, `let x: auto = 1;` would result in `x` having type `Core.IntLiteral`. This is not the desired behavior, and so can be expected to change in the future. #### No suffixes Carbon literals have no suffix. The reasons for this are covered in [Proposal #144's alternative "Use an ordinary integer or floating-point type for literals"](p0144.md#use-an-ordinary-integer-or-floating-point-type-for-literals). ## Proposal - **Carbon literal to C++ type**: A Carbon literal will be given a C++ type according to the C++ rules when used in C++ context, such as calling a C++ function. - **C++ literal to Carbon type**: A C++ literal will be given a C++ type following the C++ rules, which will then be mapped to a Carbon type as defined in [primitive types mapping](p5448.md#carbon-primitive-types). - For example, `1` becomes `int` according to C++ rules, and `int` maps to `i32` in Carbon. ## Details ### Carbon to C++ type mapping #### Integer literals There are no suffixes in Carbon for the integer types, so a Carbon decimal integer literal will follow the C++ rules for decimal integers. Carbon also doesn't make a distinction between hexadecimal and binary literals, so these literals will also follow the C++ rules for decimal integers without a suffix. The first type from this list in which the value can fit will be selected: - `int` - `long` - `long long` - `__int128` If the value doesn’t fit in any of these types, the program will be ill-formed and diagnosed with an error. This is intended to match the C++ standard behavior, instead of Clang's `-Wimplicitly-unsigned-literal` behavior. Decimal numbers are most commonly used as integer literals, so this should match most of the existing C++ calls. To match the C++ calls with a non-decimal literal argument, an explicit `unsigned` type will need to be provided. For example, `0xDEADBEEF as u32`. #### Floating-point literals As there are no suffixes in Carbon for the floating-point literals, a Carbon floating-point literal will map to C++ `double`. If the value doesn’t fit to double, the program is ill-formed. ### C++ to Carbon type mapping #### Integer literals A C++ integer literal will be given a C++ integer type following the C++ rules, which will then be mapped to a Carbon type as defined in [primitive types mapping](p5448.md#carbon-primitive-types). | C++ literal suffix | Carbon type with decimal C++ integer literals | Carbon type with hexadecimal, binary and octal C++ integer literals | | --------------------------------------- | ----------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------- | | (none) | `Cpp.int`, `Cpp.long`, `Cpp.long_long` | `Cpp.int`, `Cpp.unsigned_int`, `Cpp.long`, `Cpp.unsigned_long`, `Cpp.long_long`, `Cpp.unsigned_long_long` | | `u` or `U` | `Cpp.unsigned_int`, `Cpp.unsigned_long`, `Cpp.unsigned_long_long` | `Cpp.unsigned_int`, `Cpp.unsigned_long`, `Cpp.unsigned_long_long` | | `l` or `L` | `Cpp.long`, `Cpp.long_long` | `Cpp.long`, `Cpp.unsigned_long`, `Cpp.long_long`, `Cpp.unsigned_long_long` | | `u` or `U` and `l` or `L` | `Cpp.unsigned_long`, `Cpp.unsigned_long_long` | `Cpp.unsigned_long`, `Cpp.unsigned_long_long` | | `ll` or `LL` | `Cpp.long_long` | `Cpp.long_long`, `Cpp.unsigned_long_long` | | `u` or `U` and `ll` or `LL` | `Cpp.unsigned_long_long` | `Cpp.unsigned_long_long` | | `z` or `Z` (since C++23) | `Cpp.uintptr_t` | `Cpp.uintptr_t`, `Cpp.size_t` | | `u` or `U` and `z` or `Z` (since C++23) | `Cpp.size_t` | `Cpp.size_t` | #### Floating-point literals A C++ floating literal will be given a C++ type following the C++ rules, which will then be mapped to a Carbon type as defined in [primitive types mapping](p5448.md#carbon-primitive-types). That means: | C++ floating-point literal suffix | Carbon floating-point literal type | | --------------------------------- | ---------------------------------- | | (none) | `f64` | | `f` or `F` | `f32` | | `l` or `L` | `Cpp.long_double` | | `f16` or `F16` (since C++23) | `f16` | | `f32` or `F32` (since C++23) | TBD | | `f64` or `F64` (since C++23) | TBD | | `f128` or `F128` (since C++23) | `f128` | | `bf16` or `BF16` (since C++23) | TBD | ## Future work ### More robust hexadecimal and binary literals in Carbon We may want to use C++ standard rules for Carbon's hexadecimal and binary literals, per the [alternative below](#use-the-c-standard-rules-for-hexadecimal-and-binary-literals). However, that requires addressing how that should be handled in the integer literal space; for example, either adding more information to `Core.IntLiteral` or a new type, as well as addressing what kind of type is produced when performing arithmetic on mixed literal types. That may be desirable, but addressing it here would substantially increase the scope of this proposal. ## Rationale This work will contribute to Carbon’s goal for seamless interoperability with C++ ([Interoperability with and migration from existing C++ code](/docs/project/goals.md#interoperability-with-and-migration-from-existing-c-code)), by keeping the consistency with the existing C++ usage. ## Alternatives considered ### Carbon to C++ type mapping #### Use the C++ standard rules for hexadecimal and binary literals For example, when dealing with `0xFFFF_FFFF`, we could make that be interpreted as `unsigned` instead of `long`. This will be discernible in cases of overload resolution and type deduction. For example: ```cpp import Cpp inline ''' void f(unsigned); void g(unsigned); void g(long); template void h(T); '''; fn CallF() { // OK in both Carbon and C++. // We select the f(unsigned) overload, and can call it. Cpp.f(0xFFFF_FFFF); } fn CallG() { // Would call g(unsigned) in C++. // Calls g(long) in Carbon. Cpp.g(0xFFFF_FFFF); } fn CallH() { // Would call h in C++. // Calls h in Carbon. Cpp.h(0xFFFF_FFFF); } ``` Advantages: - This would allow using the unsigned types for the cases where it’s important (for example bit manipulation). This affects understandability for interactions on interop boundaries, where developers might reasonably expect that `0xFFFF_FFFF` would behave identically in Carbon and C++. Disadvantages: - The current integer literal design doesn't support this distinction. - These cases seem to be less often used than the decimal literals. This decision is probably good enough for now, although we should consider it for [future work](#more-robust-hexadecimal-and-binary-literals-in-carbon). #### Use the Clang way of determining the type As described in [background](#clang), we could use the biggest unsigned integer type currently supported for C++ (`unsigned __int128`) if the value doesn’t fit to `int`, `long`, or `long long`. Advantages: - This will match the C++ calls for the users that use Clang. Disadvantages: - Using `__int128` gives a consistently signed type interpretation regardless of bit width. This is also consistent to how we plan to require an explicit `unsigned` conversion. It's not clear how much this issue would arise, so we believe a consistent signed type interpretation is a good default. ### C++ to Carbon type mapping #### Use Carbon literal types In other words, import a literal to a Carbon literal type; `Core.IntLiteral` or `Core.FloatLiteral`. Advantages: - This would allow determining the type later according to the Carbon rules. Disadvantages: - This may not be always feasible. For example, sometimes a variable or constant needs to be initialized with the value of the literal. Rather than trying to detect feasibility, we are choosing to assign a sized numeric type. ================================================ FILE: proposals/p6676.md ================================================ # Carbon/C++ Interop: Importing C/C++ object-like macros [Pull request](https://github.com/carbon-language/carbon-lang/pull/6676) ## Table of contents - [Abstract](#abstract) - [Problem](#problem) - [Background](#background) - [Macros in C++](#macros-in-c) - [Object-like macros](#object-like-macros) - [Function-like macros](#function-like-macros) - [Predefined macros](#predefined-macros) - [Swift / C interop [GitHub][documentation]](#swift--c-interop-githubdocumentation) - [Proposal](#proposal) - [Details](#details) - [Namespace](#namespace) - [Constant type](#constant-type) - [Constant value](#constant-value) - [Constant expressions](#constant-expressions) - [Empty macros](#empty-macros) - [Implementation](#implementation) - [Rationale](#rationale) - [Alternatives considered](#alternatives-considered) - [Open questions](#open-questions) - [Object-like macros](#object-like-macros-1) - [Function-like macros](#function-like-macros-1) - [Predefined macros](#predefined-macros-1) ## Abstract This proposal addresses importing object-like C/C++ macros into Carbon. ## Problem C/C++ object-like macros are used to define constants, conditional compilation, header guards, etc. and are common for low-level, cross-platform libraries, with C like APIs. Despite the recommendations for replacing them in C++ with safer techniques (for example `constexpr`), they remain to be present in C++ code bases. Of particular interest for the interoperability are cases such as macros present in the APIs of the standard C++ library (for example error codes in ``), which can't be easily changed, but remain to be widely used in low-level C++ libraries. A mechanism for properly importing and handling these macros is necessary for a seamless interoperability. ## Background ### Macros in C++ Macros are defined with the `#define` directive and processed by the preprocessor, which replaces the occurrences of the defined identifier with a replacement-list. There are two types of macros: _object-like_ and _function-like_ macros. The directive `#undef` undefines a defined macro. #### Object-like macros ``` #define identifier replacement-list (optional) ``` Replacement-lists can have elements that are: - Literals: integer, floating-point, string, char, bool etc. - Non-literal types: structs, classes etc. - Identifiers: pointing to other macros, variables, types, keywords etc. - Operators: `+, -, <<, >>, |` etc. with one or more operands. #### Function-like macros ``` #define identifier(parameters) replacement-list (optional) #define identifier(parameters, ...) replacement-list (optional) #define identifier(...) replacement-list (optional) ``` The syntax of function-like macros is similar to the syntax of a function call. They accept a list of arguments and replace their occurrence in the replacement-list. As this is only a text replacement, it can lead to unexpected behaviour if the arguments are not properly separated with brackets. In function-like macros, the operators `#` and `##` enable: - `#operator (stringification)` - the arguments of the macro are converted to a string literal without expanding the argument. When `#` is used in front of a parameter in a macro definition (`#param`), the preprocessor replaces `#param` with the argument tokens as a string literal. - `##operator (concatenation or token pasting)` - two tokens are merged in a single token during a macro expansion. For example, when `a##b` is used as part of the macro definition, the preprocessor merges `a` and `b`, removing any white spaces in between to form a single token. When some of the tokens on either side of the `##` operator are parameter names, they are replaced by the actual argument before the execution of `##`. This is used for example to create new identifiers like variables, function names etc. and in general to avoid creating repeated boilerplate code. #### Predefined macros There are also predefined macros available in every translation unit. Examples include: `__cplusplus`, `__FILE__`, `__LINE__`, `__DATE__`, `__TIME__` etc. ### Swift / C interop [[GitHub](https://github.com/swiftlang/swift/blob/main/lib/ClangImporter/ImportMacro.cpp)][[documentation](https://developer.apple.com/documentation/swift/using-imported-c-macros-in-swift)] Swift supports importing object-like C macros as global constants. Macros that use integer, floating-point and string literals are supported. Also simple operators like `+, -, <<, >>` etc. between literals or macros are supported. Function-like macros are not supported. Instead, using Swift functions and generics is recommended. ## Proposal An object-like macro that evaluates to a constant expression is imported from C++ as a constant in Carbon. For example: C++: ``` #define BUFFER_SIZE 4096 ``` Carbon: ``` let a: i32 = Cpp.BUFFER_SIZE; // Cpp.BUFFER_SIZE is imported as an int value of type i32 with a value of 4096 ``` ## Details ### Namespace The macro is evaluated in the global Cpp namespace and accessible as Cpp.BUFFER_SIZE. ### Constant type The type of the imported constant is deduced by Clang by evaluating the constant expression and then mapped to a Carbon type following the existing [Carbon <-> C++ type mapping rules](https://github.com/carbon-language/carbon-lang/blob/trunk/proposals/p5448.md). ### Constant value The value is deduced by evaluating the tokens of a replacement list as a constant expression. ### Constant expressions The replacement list in the object-like macro expanding to a constant expression can contain: - **Operators**: arithmetic: `+, -, *, /`; bitwise: `|, &, ^, <<, >>` ; logical: `||, &&`; comparison: `<, >, <=, >=, ==`; casts etc, with arbitrary number of operands. For example: ``` #define ADDITION 1+2+3 ``` - **Chained macros**: macros that expand to another macro which evaluates to a constant. For example: ``` #define VALUE 123 #define MY_VALUE VALUE ``` - **Enum constants and `constexpr` variables**: if a macro's replacement list refers to a named constant, such as an enum constant or a `constexpr` variable, it is imported as an alias rather than as a literal value. This allows Carbon to preserve the specific type of the constant (such as `Color` in the example below). In the case of `constexpr` variables, importing as an alias also preserves addressability (that the constant is an lvalue), which would be lost if only the value were imported. For example: C++: ``` enum class Color { Red = 1, Green = 2 }; #define GREEN_COLOR Color::Green constexpr int kValue = 123; #define VALUE kValue ``` Carbon: ``` // Cpp.GREEN_COLOR is an alias to Cpp.Color.Green which has a type Cpp.Color. let b: Cpp.Color = Cpp.GREEN_COLOR; // Cpp.VALUE is an alias to kValue. let a: i32 = Cpp.VALUE; ``` The macro will be evaluated by default in the global namespace (for example `Cpp.VALUE`). Evaluating it in a child namespace (`Cpp.SomeNamespace.VALUE`) may also be possible, though details about that are outside of the scope of this proposal. ### Empty macros The macro should have at least one value in the replacement-list so that it’s imported. For example, the following macro won’t have a Carbon equivalent: ``` #define EMPTY ``` ### Implementation 1. _Name lookup_: When a C++ macro name is encountered in Carbon it is looked-up before any other name. Following the C++ rules, this allows the macro to be found in case there is a non-macro with the same name (for example named variable). 2. _Macro import_: If a macro is found, it is imported as a constant to Carbon, by parsing the tokens of the replacement list to a constant expression and evaluating the result. For example, given a macro: ``` #define MY_MACRO ``` The following constexpr will be (effectively) generated and imported: ``` constexpr inline decltype(auto) __carbon_import_MY_MACRO = (MY_MACRO); ``` That is, we would try to generate such a `constexpr` and import the macro if that succeeds. ## Rationale This work contributes to the Carbon’s goal for seamless interoperability with C++ ([Interoperability with and migration from existing C++ code](https://github.com/carbon-language/carbon-lang/blob/trunk/docs/project/goals.md#interoperability-with-and-migration-from-existing-c-code)), by enabling reusing and migrating existing macros, while adhering to the best practices to use constant variables. ## Alternatives considered - Implementation a) Reuse Swift/C implementation - _Reason to do this_: use existing implementation. - _Reason not to do this_: this implementation is much more limited, longer and harder to maintain than the proposed one, as it manually scans the replacement tokens and tries to evaluate them, instead of using Clang for that. Limitations include for example type of supported operators, number and type of operands etc. Also, due to the licensing, it is not directly available. b) Manually scanning tokens (own implementation) - _Reason not to do this_: this is a much longer implementation compared to the proposed one, with much more limitations compared to using Clang to do that. - Importing macros that refer to enum constants and `constexpr` variables as constant values, instead of aliases in Carbon - _Reason to do this_: This would be a simpler implementation and consistent with how other object-like macros are imported. - _Reason not to do this_: This would not preserve the enum's type (for example, `Color::Green` would be imported as an integer literal `2` instead of as a constant of type `Color`) or the addressability of `constexpr` variables (which are lvalues). ## Open questions ### Object-like macros The support for the following cases still needs to be clarified: - non-constant variable name ``` int x; #define VAL x ``` - types: ``` #define SHORT_TYPE short ``` - Statements or keywords: ``` #define RET return #define FOREVER for(;;) ``` - Expanding macros in child namespaces (`Cpp.SubNamespace.MACRO`) ### Function-like macros The support for function-like macros will still need to be discussed in detail. The current proposal could be extended for function-like macros in the following direction: Given a function-like macro: ``` #define MY_MACRO(a, b) ((a) + (b)) ``` A function template whose body invokes the macro could be (eventually) generated and imported: ``` constexpr decltype(auto) __carbon_import_MY_MACRO(auto a, auto b) { return (MY_MACRO(a, b)); } ``` ### Predefined macros Details on the support for predefined macros remains open as well. ================================================ FILE: proposals/p6699.md ================================================ # Diagnostic sorting [Pull request](https://github.com/carbon-language/carbon-lang/pull/6699) ## Table of contents - [Abstract](#abstract) - [Problem](#problem) - [Background](#background) - [Proposal](#proposal) - [Details](#details) - [Rationale](#rationale) - [Alternatives considered](#alternatives-considered) - [Don't sort diagnostics](#dont-sort-diagnostics) - [Sort by line and column](#sort-by-line-and-column) - [Sort by last processed token](#sort-by-last-processed-token) ## Abstract Change `SortingConsumer` from sorting by last processed token (per-phase) to additionally allow diagnostics to request sorting by start position (line and column) when the last processed token is the same. ## Problem Diagnostics in many toolchains are emitted in the order they are discovered during code traversal. While this naturally reflects the causal relationship between errors (for example, an error in a macro expansion causing subsequent parse errors), it can lead to a confusing experience for developers if the diagnostics jump back and forth through the file. Conversely, sorting purely by source location (line and column) can break causality, presenting a consequence before its cause. We need a sorting strategy that feels natural to humans but respects the underlying toolchain logic. ## Background Carbon's processing of code in stages (lex, parse, check) causes diagnostics to be produced in that order. In contrast, Clang interleaves parse and check, and as a consequence the diagnostics produced are similarly interleaved. A more detailed overview of Carbon's diagnostic infrastructure can be found in [diagnostics.md](/toolchain/docs/diagnostics.md). ## Proposal In addition to sorting by the last processed token (which `SortingConsumer` already does), add a way to sort based on the start position (line and column) by request. This is being called "on-scope" because current cases we've discussed are scope-related. See [SortingConsumer](/toolchain/docs/diagnostics.md#sortingconsumer) for more documentation. ## Details This was already implemented in [PR #6687](https://github.com/carbon-language/carbon-lang/pull/6687). ## Rationale This proposal advances Carbon's goal of providing [Language tools and ecosystem](/docs/project/goals.md#language-tools-and-ecosystem) and creating [Code that is easy to read, understand, and write](/docs/project/goals.md#code-that-is-easy-to-read-understand-and-write) by making the developer experience with error messages more predictable and logical. It respects the inherent causal order of the toolchain while tailoring the output to human expectations. ## Alternatives considered ### Don't sort diagnostics We could just print diagnostics in the order they are produced. This would print all lex errors, then all parse errors, then all check errors. This would be simple, but might be confusing when a parse error at the end of a file comes before check errors, and fixing the check errors would fix the parse error. ### Sort by line and column We could sort diagnostics purely by their line and column. This runs into issues with cases such as: ```carbon fn F(x: i32, y: i32); fn G() { F(1 2); } ``` Here, the diagnostic for an invalid parse of `1 2` would appear after the diagnostic that `F` expects two arguments, not one. This is confusing because the missing comma is the root cause of the incorrect argument count. ### Sort by last processed token We could sort diagnostics purely by the last token that was processed when the diagnostic was emitted. This runs into issues with cases such as: ```carbon fn F(x: i32, y: i32) {} ``` Here, both `x` and `y` would be diagnosed as unused at the `}`. The order would be non-deterministic, hindering golden tests. This could be partially addressed by sorting the diagnostics locally (for example, sorting each `unused` diagnostic together), but this is an incomplete solution because we may introduce further scope-related checks, particularly flow checking (for example, checking if there are provable out-of-bounds accesses). These would all have the same last processed tokens. It also would likely lead to sorting regardless of whether sorting was requested by the tool user, a performance overhead we want to avoid. We believe sorting by the last processed token is a partial solution, which we're building on. ================================================ FILE: proposals/p6710.md ================================================ # `char` redesign [Pull request](https://github.com/carbon-language/carbon-lang/pull/6710) ## Table of contents - [Abstract](#abstract) - [Problem](#problem) - [Background](#background) - [Proposal](#proposal) - [Details](#details) - [Add a `char` type literal](#add-a-char-type-literal) - [Escape sequences](#escape-sequences) - [Add a `Core.CharLiteral` type for character literals](#add-a-corecharliteral-type-for-character-literals) - [Operators](#operators) - [Conversion operators](#conversion-operators) - [Comparison operators](#comparison-operators) - [Arithmetic operators](#arithmetic-operators) - [`char` integer parameters](#char-integer-parameters) - [Overflow semantics](#overflow-semantics) - [Preferring i32 returns](#preferring-i32-returns) - [Revoke and replace proposal #1964: Character Literals](#revoke-and-replace-proposal-1964-character-literals) - [Rationale](#rationale) - [Future work](#future-work) - [Alternatives considered](#alternatives-considered) - [Align `char` fully with C++, or make it fully valid](#align-char-fully-with-c-or-make-it-fully-valid) - [Raw character literals](#raw-character-literals) - [Disallow hex escape sequences in character literals](#disallow-hex-escape-sequences-in-character-literals) - [Allow grapheme clusters in character literals](#allow-grapheme-clusters-in-character-literals) - [Reuse string literal syntax for character literals](#reuse-string-literal-syntax-for-character-literals) - [Treat single-character string literals as a third "text literal" type](#treat-single-character-string-literals-as-a-third-text-literal-type) ## Abstract - Add a `char` type literal mapping to `Core.Char` and equivalent to C++'s `char`. - 8 bits, unsigned, treated as a single UTF-8 [code unit](https://en.wikipedia.org/wiki/Character_encoding#Code_unit). - Add a `Core.CharLiteral` type for character literals, similar to `Core.IntLiteral`. - Allow operations for `char` and `Core.CharLiteral` which reinforce the "character" concept, versus an integer value. - Revokes and replaces [#1964: Character Literals](https://github.com/carbon-language/carbon-lang/pull/1964). ## Problem `char` is an important type due to its common use in C++ code. However, the related proposal [#1964: Character Literals](https://github.com/carbon-language/carbon-lang/pull/1964) has several issues, including: - Lacks a decision for `char` handling; it is not mentioned in proposal #1964. - Similarly, decides there are character literals, but more detail is needed for implementation. - Type literal naming no longer reflects naming consensus. - `Char8` seems potentially more equivalent to `std::char8_t` instead of `char`, and for interop purposes these are slightly different types. Similar applies to `Char16` and `Char32`. - As a design direction, we have been lowercasing type literals (such as `u8`). - Conflicting statements about behavior. - For example, "Rationale" states that `var b: u8 = 'a' + 1` would be supported, while "Operations" states that `+` is returning a character literal (not a `u8`). - For character literals, states "Escape sequences which would result in non-UTF-8 encodings or more than one code point are not included." However, it goes on to say that `let smiley: Char16 = '\u{1F600}'` is valid even though `1F600` would require multiple code units in both UTF-8 and UTF-16. - Unclear that it gives us a good UTF plan. - Does not decide what a single character in a Carbon string is. - No consideration regarding interop with the `std::char32_t` family of types or [ICU](https://github.com/unicode-org/icu) compatibility. In other words, it's likely we want something similar to `Char32`, but it may be named something like `Core.Char32` and have slightly different type behaviors than decided in #1964. On the other hand, we need something compatible with the C++ `char` in order to proceed with basic C++ interop, and #1964 doesn't provide that. ## Background - [Proposal #1964: Character Literals](https://github.com/carbon-language/carbon-lang/pull/1964) is fundamental, and a lot of the underlying thoughts still apply. In particular, we still want character types to be distinct from numeric types. - [Proposal #199: String literals](https://github.com/carbon-language/carbon-lang/pull/199) is important because we want character and string literals to have mirrored escaping concepts. - [Proposal #5448: Carbon <-> C++ Interop: Primitive Types](https://github.com/carbon-language/carbon-lang/pull/5448) left the question of character type mappings open. This proposal aims to answer it for `char`. - [Issue #5903: Built-in character type questions](https://github.com/carbon-language/carbon-lang/issues/5903) addressed type questions. - [Issue #5922: Built-in character operators](https://github.com/carbon-language/carbon-lang/issues/5922) addressed operators. ## Proposal The way `char` will work is: - Add a `char` type literal. - Carbon's `str` type will use `char` for elements. - For interop, map Carbon's `char` to C++'s `char`. - Add a `Core.CharLiteral` type for character literals, similar to `Core.IntLiteral`. - Provide operators which are consistent with the character concept. This proposal additionally revokes and replaces proposal #1964, rather than trying to define which parts we are keeping and which are changing. ## Details ### Add a `char` type literal `char` is intended to offer a basic construct for Carbon's strings that is both compatible with UTF-8, and has high fidelity with C++ strings. In support of that, important notes are: - `char` itself will be a [type literal](https://github.com/carbon-language/carbon-lang/blob/trunk/docs/design/lexical_conventions/words.md#type-literals). - `char` notionally represents a UTF-8 code unit. - It can contain invalid code units, as long as it remains 8 bits. We do not assume runtime validation. - `char` will be backed by `Core.Char`, in the prelude. - `Core.Char` will adapt `u8`. - C++ interoperability will transparently map `char` and `Cpp.char` on API boundaries. - When used with Carbon, C++ `char` will be unsigned by default (`-funsigned-char`). A program can switch back to signed (`-fno-unsigned-char`), and Carbon will maintain interoperability but bits will be interpreted differently in each language. #### Escape sequences Escape sequences are the same as for a string literal. Only one escape sequence may be provided in a character literal. ### Add a `Core.CharLiteral` type for character literals `Core.CharLiteral` is the type of a character literal, similar to how `Core.IntLiteral` is the type of integer literals. It abstractly represents a single Unicode code point. This gives us a compile-time structure for characters that can be typed and referred to in programs. Semantics of a character literal will be equivalent to a [simple string literal](https://github.com/carbon-language/carbon-lang/blob/trunk/docs/design/lexical_conventions/string_literals.md#simple-and-block-string-literals), except that: - A character literal has a validated Unicode code point value. - The enclosing character is `'`. - The contents are precisely one character or escape sequence. - The `\x` escape sequence is limited to values up to `7F`, where the UTF-8 code unit and Unicode code point values are identical. An important detail of the character literal type is it gives us a way to track constant values at compile time. For example, `'a' + 1` has a constant value of `b`. This means we can diagnose uses of character literals that don't represent a valid Unicode code point, such as `'a' + 0xFFFFFF`. ### Operators The goal of provided operators is to provide a set of operators which map to common operations a user would want to do. It is a non-goal to support use of `char` as an arbitrary byte or integer: developers should use `u8` for that. In general, `char` and `Core.CharLiteral` operators are intended to be mirrors of each other. #### Conversion operators - `char` - `ImplicitAs`: None - `ExplicitAs`: To/from `u8`, plus the set of `ImplicitAs` for `u8`. - For example, `u8` has `ImplicitAs` to `u16`, so `char` has `ExplicitAs` to `u16`. - `Core.CharLiteral` - `ImplicitAs`: to `char` only - `ExplicitAs`: To/from the set of `ImplicitAs` for `i32` and `u32`. - For example, `i32` has `ImplicitAs` to `i64`, so `Core.CharLiteral` has `ExplicitAs` to `i64`. - For example, `i64` does not have `ImplicitAs` to `i32`; conversion requires two casts, `((i64_val as i32) as Core.CharLiteral)`. Casting from a `char` to a `Core.CharLiteral` is not supported. See also [implicit numeric conversions](https://github.com/carbon-language/carbon-lang/blob/trunk/docs/design/expressions/implicit_conversions.md#data-types). #### Comparison operators - `char` - `EqWith` and `OrderedWith` when both operands are `char`. - `ImplicitAs` should allow substituting one operand with `Core.CharLiteral`. - `Core.CharLiteral` - `EqWith` and `OrderedWith` when operands are `Core.CharLiteral`. #### Arithmetic operators - `char` - `AddWith`: `char + <integer> -> char` (with reversible operands) - Equivalent to `(((char as i16) + <integer>) as u8) as char)` - `SubWith`: - `char - <integer> -> char` (non-reversible operands) - Equivalent to `(((char as i16) - <integer>) as u8) as char)` - `char - char -> i32` - Equivalent to `(lhs as i32) - (rhs as i32)`. - `ImplicitAs` should allow substituting one operand with `Core.CharLiteral`. - `Core.CharLiteral` - `AddWith`: `Core.CharLiteral + <integer> -> Core.CharLiteral` (with reversible operands) - `SubWith`: - `Core.CharLiteral - <integer> -> Core.CharLiteral` (non-reversible operands) - `Core.CharLiteral - Core.CharLiteral -> i32` - Provides a unicode code point delta. ##### `char` integer parameters Arbitrary integers are supported for most of these operations. For example, we want to allow addition of negative numbers, even though the representation of `char` is unsigned, without requiring additional casts. ##### Overflow semantics Operations will use error overflow semantics, [similar to signed integers](https://github.com/carbon-language/carbon-lang/blob/trunk/docs/design/expressions/arithmetic.md#overflow-and-other-error-conditions). For example, `(('a' as char) + 500)` is invalid code because it causes `char` overflow. That's why conversions are to signed values (for example, `char as i16`). ##### Preferring i32 returns In arithmetic, `i32` returns are preferred for deltas because they should be valid for unicode code points. Even though `char` is only 8-bits, using `i32` for returns there too creates consistency with `Core.CharLiteral`. ### Revoke and replace proposal #1964: Character Literals This revokes proposal #1964 for simplicity. Rather than trying to detail which decisions still apply and which don't, this proposal is acting from an assumption that all decisions there no longer apply. We can still benefit by pointing towards the rationale in explicitly maintaining decisions, but we want to go through that step. ## Rationale - [Performance-critical software](https://github.com/carbon-language/carbon-lang/blob/trunk/docs/project/goals.md#performance-critical-software) - The intent is that Carbon's main string type privileges UTF-8 over other potential encodings. A `char` represents a single code unit within that, and is consequently efficient to access. It can also be invalid, meaning we don't guarantee performing runtime validation for users (avoiding performance overhead), and that users might be able to use it for other encodings. - [Software and language evolution](https://github.com/carbon-language/carbon-lang/blob/trunk/docs/project/goals.md#software-and-language-evolution) - `Core.CharLiteral` is designed as a Unicode code point, and even though this design doesn't include a way to use values over `7F`, we anticipate those will be added in the future. It's being provided as a building block for more elaborate Unicode functionality, including both UTF-16 and UTF-32, even as we prioritize UTF-8. - [Code that is easy to read, understand, and write](https://github.com/carbon-language/carbon-lang/blob/trunk/docs/project/goals.md#code-that-is-easy-to-read-understand-and-write) - Character literal syntax mirrors string literal syntax. The main divergence is `\x80` and higher similar escapes, which are not supported due to potentially ambiguous behavior, still in furtherance of this goal. - [Practical safety and testing mechanisms](https://github.com/carbon-language/carbon-lang/blob/trunk/docs/project/goals.md#practical-safety-and-testing-mechanisms) - Restricting the set of operators valid for `char` gives us a way to do different sorts of validation that can be more character-oriented than if we treated it as an arbitrary byte. - Treating `Core.CharLiteral` as a valid Unicode character allows us to provide static checking for some operations, such as `'a' + 1` resulting in another valid Unicode code point; more is also transitively possible, including involving `char`. - [Interoperability with and migration from existing C++ code](https://github.com/carbon-language/carbon-lang/blob/trunk/docs/project/goals.md#interoperability-with-and-migration-from-existing-c-code) - Modeling `char` as a UTF-8 code unit creates behavior which is very similar to C++, but still shifts towards a more character-oriented approach. We do expect some migration friction as a consequence (as use-cases might need either more casts, or to switch to a byte type). ## Future work There's still significant future work, including: - `signed char`, `unsigned char` - `std::char8_t`, `std::char16_t`, `std::char32_t` - UTF-16 and UTF-32 support It should not be assumed that there's any restriction on the designs of those features, particularly no restrictions from #1964. ## Alternatives considered ### Align `char` fully with C++, or make it fully valid Alternatives were discussed in [zygoloid's comment on #5903](https://github.com/carbon-language/carbon-lang/issues/5903#issuecomment-3494068591). The comment notes that three options were proposed: 1. `char` is fully aligned with C++. There is no universal convention for what the value in a `char` means, and the numerical encoding of Unicode characters into `char` sequences might even be platform-dependent. For example, we might use some code page on Windows, EBCDIC on some IBM targets, and probably UTF-8 everywhere else. Likely the encoding would match what a character literal in C++ code would do for that target. Even when the target normally uses UTF-8, it would be reasonable to use an array of `char` as the type of the output buffer when transcoding from UTF-8 to some other encoding, and generally an encoded text buffer (in any encoding) would typically be represented as an array of `char`. It might also be reasonable to use an array of `char` for things that aren't necessarily text, such as file contents. 2. `char` models a UTF-8 code unit, although it may not necessarily be valid, and may appear in a sequence that is not a valid UTF-8 encoding. As with the first option, `char` can represent an integer in [0, 255], although it is not an integer type. Higher-level abstractions would likely (eventually) be provided to represent different views of the code unit sequence as (for example) a sequence of code points or a sequence of graphemes, but the fundamental model exposes the encoding. Functions taking `char` or `char` sequences would assume UTF-8 encoding, and would need to consider how to handle invalid `char`s and invalid `char` sequences. 3. Use a foundation that enforces Unicode string validity, for some definition of "Unicode string validity". The `char` type is a Unicode character. Strings would notionally be a sequence of Unicode characters, possibly also maintaining some higher-level string invariants. String indexing, if it exists, would likely treat the string as a sequence of Unicode characters. String invariants would be enforced by type conversion into the string type rather than within the string operations, and certain classes of invalid strings would be unrepresentable. Rationale as evaluated are: - **Privilege UTF-8 over other encodings:** UTF-8 is [typically the best choice](https://utf8everywhere.org/) for representing text, even when targeting languages where characters are 3 bytes in UTF-8 but 2 in UTF-16, and even on Windows where the system APIs typically operate primarily in UTF-16 or UCS-2. We should create affordances that encourage use of UTF-8 (such as having the `char` type be conventionally UTF-8). - Our overall goal to support (only) modern environments and a general desire for consistency and portability argues against supporting non-Unicode encodings for character types. - Having _some_ convention for the meaning of the value of a `char` seems important, and the lack of one in C++ has been a notable problem over time, leading to the addition of `char8_t` et al, which have not been entirely satisfactory solutions due to the existing widespread usage of plain `char`. - **Do not privilege any particular meaning of "validity":** There are many different ways in which you can view a sequence of UTF-8 code units as being valid or invalid. For example: Can a string start with a combining character? Can it have mismatched LRE/RLE/PDF characters in it? Can it be unnormalized, or must it be in NFC, or in NFD? Can it contain unassigned Unicode characters? Can it contain PUA characters? Can it contain non-characters? Picking any set of answers to these questions as being our canonical notion of "validity" is somewhat arbitrary. - **Do not privilege any particular level for accessing elements of the string other than code units:** There are many different layers of abstraction at which you can interpret the contents of a string. The atoms that users want to interact with, such as glyphs or grapheme clusters in rendering, or combining characters when editing or performing substring searches, aren't in one-to-one correspondence with Unicode characters any more than they're in one-to-one correspondence with UTF-8 code units. So it's not clear that privileging Unicode-character-oriented access (or indeed any of the other higher-level Unicode views) is appropriate. However, code units are in direct correspondence with bytes of memory, which is directly relevant for low-level operations, so there is a reason to provide direct access to byte-level / code-unit-level operations. - If string indexing operates on Unicode characters, it would either be non-constant-time or would require not storing strings as just a sequence of UTF-8. Having a constant-time indexing operation on strings seems very important (especially for interop and for meeting C++ developers where they are), even though a lot of the desired functionality (perhaps all of it) can be provided with iterator- or cursor-like machinery instead. - **Enforcing validity is problematic for existing API structures:** Requiring strings to be valid UTF-8 presents difficulties when moving text into or out of other sources. For example, when reading text from a validly-encoded UTF-8 file into a text buffer, one would need to deal with a read that ends in the middle of an encoding of a character. I don't know how Rust deals with this, but it seems like it would create significant impedance mismatch with C-like buffered I/O utilities. Similarly, when interoperating with C++, it would create friction if our string representation requires strings to be valid UTF-8 encodings. - **We can allow additional invariants without requiring them:** For a known-to-be-valid UTF-8 sequence, a higher-level abstraction can be built, and similarly, yet-higher-level abstractions can be built for whatever other invariants we want to enforce. So using option 2 rather than option 3 as our foundation doesn't prevent enforcing invariants in the type system (but nor does it encourage doing so). This proposal is choosing option 2, that `char` models a UTF-8 code unit without validation. In some sense, option 2 is still "fully aligned with C++", but with C++'s `char8_t` rather than with C++'s `char`. ### Raw character literals [Raw string literals](https://github.com/carbon-language/carbon-lang/blob/trunk/docs/design/lexical_conventions/string_literals.md#raw-string-literals) use a `#` prefix. There's limited use for this in character literals; technically, `'\\'` could instead be `#'\'#`, but that's longer and extra characters may prove distracting. Raw string literals are more useful when there's a longer character sequence, whereas character literals have one character by definition. For simplicity, character literals won't have raw syntax. ### Disallow hex escape sequences in character literals A `\x##` escape sequence abstractly represents a UTF-8 code unit. Whereas values over 7F are valid in string literals (allowing arbitrary byte values), these are disallowed in character literals because we want a more validated Unicode behavior. Developers could instead rely on `\u` escapes for `\x`. It can still be useful to allow `\x` escapes for low-range values because some developers will still need to specify [ANSI escapes](https://en.wikipedia.org/wiki/ANSI_escape_code). Carbon [drops support for some escape sequences](https://github.com/carbon-language/carbon-lang/blob/trunk/docs/design/lexical_conventions/string_literals.md#escape-sequences), such as `\a`, and specifically advises `\x` as an alternative for developers that need it. Requiring `\a` -> `\x07` -> `\u{07}` is incrementally more verbose syntax, and developers may be confused why `"\x1B"` is allowed for strings but `'\u{1B}'` is required for characters. Values over 7F are ambiguous between an arbitrary byte value and a Unicode code point, and so should be invalid. However, where both interpretations are identical for UTF-8 (values up to and including 7F), we will allow `\x` escape sequences. ### Allow grapheme clusters in character literals This proposal carries forward the decision in #1964 [to not support grapheme clusters](https://github.com/carbon-language/carbon-lang/pull/1964/files#diff-192d5568d8c1d15e68abe0c46cc52cc0b375a372d1dad8d2154d09f8b29666c5R340) in character literals. ### Reuse string literal syntax for character literals Instead of using single quotes (for example, `'a'`), we could use string literal syntax with a conversion (for example, `"a" as char`) for character literals. This was proposed because it would free up the single quote for other, unspecified syntax uses. For background, character literals are common in C++. For example, in SourceGraph search statistics (some of these are in comments -- a search limitation): - `'(.|\\.)'`: [46.2 million](https://sourcegraph.com/search?q=context:global+lang:c%2B%2B+count:50000000+/%27%28.%7C%5C%5C.%29%27/&patternType=keyword&sm=0) - `<<`: [over 100 million](https://sourcegraph.com/search?q=context:global+lang:c%2B%2B+count:100000000+/+%3C%3C+/&patternType=keyword&sm=0) - `>>`: [10.4 million](https://sourcegraph.com/search?q=context:global+lang:c%2B%2B+count:50000000+/+%3E%3E+/&patternType=keyword&sm=0) - `%`: [5.3 million](https://sourcegraph.com/search?q=context:global+lang:c%2B%2B+count:10000000+/+%25+/&patternType=keyword&sm=0) This creates several disadvantages for removing character literals in Carbon: - **Migrating C++ developers to Carbon:** The frequency of use can be expected to have trained developers to expect single quotes to be used for characters, especially the C++ developers that Carbon is targeting. Repurposing them would create a friction for C++ developers to need to understand the different meanings of the same syntax in each of C++ and Carbon, something Carbon prefers to avoid. - **Increased runtime error risks:** Runtime errors could take the form of simple increased overhead, such as converting a string literal to a `str` then to a `char`. However, they could also be more insidious, such as doing `[0]` on a string literal and not validating that the string is exactly one character (this would also likely return a null byte for `""[0]`). By having a character literal type, Carbon encourages developers to stay within guide rails that make it easier to get compile-time behavior and program validation. - **Block string literal use:** We already have another use for single quotes in Carbon: [block string literals](/docs/design/lexical_conventions/string_literals.md). The syntax may need to change along with removing character literals, to make room for other uses of single quotes. - If retained, it would constrain uses of single quotes. For example, a unary operator syntax has overlap (that is, if `'a` and `''a` are valid, then `'''a` is ambiguous). - The choice of single quotes in proposal [#1360: Change raw string literal syntax](https://github.com/carbon-language/carbon-lang/pull/1360) was made accounting for single quotes in character literals, and that commonality would be lost. - **Tooling:** The prevalence of single quotes being used for either strings or characters also affects their treatment in tools not specialized to Carbon: they expect them to be used for strings. For example, Rust's use of single quotes for lifetime annotations has been observed to break language-agnostic syntax highlighting. While a compelling proposal for a different use of single quotes may come up in the future, freeing up the character for other purposes is insufficient to justify a different syntax for character literals. #### Treat single-character string literals as a third "text literal" type A related alternative with the same goal of eliminating single quotes for character literals is that, rather than requiring single-character string literals be explicitly converted to `char`, they could instead have a third type of text literal. This would implicitly cast to either `str` or `char`. This approach would lead to three literal types: `StrLiteral`, `CharLiteral`, and `TextLiteral`. The distinction of `CharLiteral` is important because we still want to support arithmetic on character literals, such as `'a' + 1` (which we would not want to be allowed for `StrLiteral`). The existence of a third type would be important for generic code, even when not trying to use character literals. For example: ```carbon fn StoreValue[U:! type](ref a: Optional(U), b: U) { a = b; } fn StrLogic[T:! type](a: T) { var x: Optional(T) = a; StoreValue(x, "str"); } fn F() { StrLogic("a"); } ``` Here, `T` is deduced to be `TextLiteral`. However, `U` has no valid value: it's passed `Optional(TextLiteral)`, while `"str"` is a `StrLiteral` (which should not be convertible to `TextLiteral`). As a consequence, this code is invalid, even though the same code would be valid if there were not `TextLiteral` type. Advantages: - Avoids an explicit cast. Disadvantages: - Shares most of the disadvantages of the primary explicit conversion approach. - This includes the risk that developers will write `"..."[0]` instead of `"..." as char` when they need a character, although the frequency may be reduced. - Having additional types in common literals could lead to programmer errors in deducing generic types, as described above. - Implicit casts cause more operator ambiguity. - How are operators that have different meanings for string and character literals handled, such as `Cpp.std.cout <<` or `<=>`? - In Carbon, we'd probably still want string operators to work; for example, `"a" + "b" => "ab"`, and that can be compile-time. Is `"a" + 1` a pointer to the null byte as it is in C++ (similar to `&("a"[1])`), a character addition (`'a' + 1 => 'b'`), or does it require an explicit cast in order to ensure behavior is deliberate? ================================================ FILE: proposals/p6716.md ================================================ # Move toolchain alternatives to proposals [Pull request](https://github.com/carbon-language/carbon-lang/pull/6716) ## Table of contents - [Abstract](#abstract) - [Problem](#problem) - [Proposal](#proposal) - [Rationale](#rationale) - [Alternatives considered](#alternatives-considered) - [Lex](#lex) - [Bracket matching in parser](#bracket-matching-in-parser) - [Parse](#parse) - [Restrictive parsing](#restrictive-parsing) - [Check](#check) - [Using a traditional AST representation](#using-a-traditional-ast-representation) - [Coalescing generic functions emitted when lowering to LLVM IR](#coalescing-generic-functions-emitted-when-lowering-to-llvm-ir) - [Coalescing in the front-end vs back-end?](#coalescing-in-the-front-end-vs-back-end) - [When to do coalescing in the front-end?](#when-to-do-coalescing-in-the-front-end) - [Compile-time trade-offs](#compile-time-trade-offs) - [Coalescing duplicate non-specific functions](#coalescing-duplicate-non-specific-functions) ## Abstract As part of using the evolution process with the toolchain, alternatives should be in proposals. This proposal migrates existing alternatives here. ## Problem Leads want to use the evolution process with more of the toolchain changes. Historically, alternatives were documented as part of the toolchain design, not going through evolution. Switching leaves those older alternatives in the toolchain design, while putting newer alternatives in the proposals directory. ## Proposal Move existing alternatives to this proposal. Future proposals will more naturally have alternatives in the proposal document itself. ## Rationale This is in support of the [evolution process](/docs/project/goals.md#software-and-language-evolution), aligning the toolchain documentation with design documentation. ## Alternatives considered ### Lex #### Bracket matching in parser Bracket matching could have also been implemented in the parser, with some awareness of parse state. However, that would shift some of the complexity of recovery in other error situations, such as where the parser searches for the next comma in a list. That needs to skip over bracketed ranges. We don't think the trade-offs would yield a net benefit, so any change in this direction would need to show concrete improvement, for example better diagnostics for common issues. ### Parse #### Restrictive parsing The toolchain will often parse code that could theoretically be rejected, instead allowing the check phase to reject incorrect structures. For example, consider the code `abstract var x: i32 = 0;`. When parsing the `abstract` modifier, parse could do single-token lookahead to see `var`, and error in the parse (`abstract var` is never valid). Instead, we save the modifier and diagnose it during check. The problem is that code isn't always this simple. Considering the above example, there could be other modifiers, such as `abstract private returned var x: i32 = 0;`, so single-token lookahead isn't a general solution. Some modifiers are also contextually valid; for example, `abstract fn` is only valid inside an `abstract class` scope. As a consequence, a form of either arbitrary lookahead or additional context would be necessary in parse in order to reliably diagnose incorrect uses of `abstract`. In contrast with parse, check will have that additional context. Rejecting incorrect code during parsing can also have negative consequences for diagnostics. The additional information that check has about semantics may produce better diagnostics. Alternately, sometimes check will produce diagnostics equivalent to what parse could, but with less work overall. As a consequence, at times we will defer to the check phase to produce diagnostics instead of trying to produce those same diagnostics during parse. Some examples of why we might diagnose in check instead of parse are: - To issue better diagnostics based on semantic information. - To diagnose similar invalid uses in one place, versus partly in check and partly in parse. - To support syntax highlighting for IDEs in near-correct code, still being typed. Some examples of why we might diagnose in parse are: - When it's important to distinguish between multiple possible syntaxes. - When permitting the syntax would require more work than rejecting it. A few examples of parse designs to avoid are: - Using arbitrary lookahead. - Looking ahead one or two tokens is okay. However, we should never have arbitrary lookahead. - This includes approaches which would require using the mapping of opening brackets to closing brackets that is produced by `TokenizedBuffer`. Those are helpful for error recovery. - Building complex context. - We want parsing to be faster and lighter weight than check. - Duplicating diagnostics between parse and check. - When there are closely related invalid variants of syntax, only some of which can be diagnosed during parse, consider diagnosing all variants during check. This is a balance. We don't want to unnecessarily shift costs from parse onto check, and we don't try to allow clearly invalid constructs. Parse still tries to produce a reasonable parse tree. However, parse leans more towards a permissive parse, and an error-free parse tree does not mean the code is grammatically correct. ### Check #### Using a traditional AST representation Clang creates an AST as part of compilation. In Carbon, it's something we could do as a step between parsing and checking, possibly replacing the SemIR. It's likely that doing so would be simpler, amongst other possible trade-offs. However, we think the SemIR approach is going to yield higher performance, enough so that it's the chosen approach. ### Coalescing generic functions emitted when lowering to LLVM IR #### Coalescing in the front-end vs back-end? An alternative considered was not doing any coalescing in the front-end and relying on LLVM to make the analysis and optimization. The current choice was made based on the expectation that such an [LLVM pass](https://llvm.org/docs/MergeFunctions.html) would be more costly in terms of compile-time. The relative cost has not yet been evaluated. #### When to do coalescing in the front-end? The analysis and coalescing could be done prior to lowering, after specialization. The advantage of that choice would be avoiding to lower duplicate LLVM functions and then removing the duplicates. The disadvantage of that choice would be duplicating much of the lowering logic, currently necessary to make the equivalence determination. #### Compile-time trade-offs Not doing any coalescing is also expected to increase the back-end codegen time more than performing the analysis and deduplication. This can be evaluated in practice and the feature disabled if found to be too costly. #### Coalescing duplicate non-specific functions We could coalesce duplicate functions in non-specific cases, similar to lld's [Identical Code Folding](https://lld.llvm.org/NewLLD.html#glossary) or LLVM's [MergeFunctions pass](https://llvm.org/docs/MergeFunctions.html). This would require fingerprinting all instructions in all functions, whereas specific coalescing can focus on cases that only Carbon's front-end knows about. Carbon would also be restricted to coalescing functions in a single compilation unit, which would require replacing function definitions that allow external calls with a placeholder that calls the coalesced definition. We don't expect sufficient advantages over existing support. ================================================ FILE: proposals/p6910.md ================================================ # Support octal literals [Pull request](https://github.com/carbon-language/carbon-lang/pull/6910) ## Table of contents - [Abstract](#abstract) - [Problem](#problem) - [Background](#background) - [Proposal](#proposal) - [Future work](#future-work) - [File permissions API](#file-permissions-api) - [Rationale](#rationale) - [Alternatives considered](#alternatives-considered) - [No octal literals](#no-octal-literals) ## Abstract Support octal literals, mainly for migrating Unix file permissions. Reflects leads decision [#6821](https://github.com/carbon-language/carbon-lang/issues/6821). ## Problem Carbon currently does not support octal numeric literals, because they're very rare, as previously decided in proposal [#143: Numeric literals](https://github.com/carbon-language/carbon-lang/pull/143). However, as part of interoperability with POSIX file system calls such as [`umask`](https://pubs.opengroup.org/onlinepubs/9699919799/functions/umask.html), we want an easy way to express octal file permissions. ## Background Leads discussed support of octal literals in issue [#6821: Support octal literals](https://github.com/carbon-language/carbon-lang/issues/6821). Unix file permissions written in octal are familiar to both programmers and non-programmers who have experience administering Unix-like machines. For example: - `chmod OCTAL-MODE FILE...` - [POSIX `mode_t` values](https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/sys_stat.h.html), including the argument to [`umask`](https://pubs.opengroup.org/onlinepubs/9699919799/functions/umask.html). However, they are are not likely to be readable to those unfamiliar with them, nor are they very common in code. We also expect these to be the primary use of octal numeric literals in Carbon. Given these issues, proposal #143 [rejected octal literals](/proposals/p0143.md#octal-literals). Now, we're testing interoperability of POSIX file system calls, and we are considering octal literals as a potential solution. Proposal #143 [discussed the Carbon-style `0o` versus C++-style `0` prefix for octal literals. The same logic still applies, so we will not address it here. ## Proposal Introduce support for octal literals using the `0o` prefix (for example, `0o755`), followed by one or more octal digits (`0-7`). This provides a very simple lexical space for octal numbers, mapping clearly from C++ and building consistently off the existing `0x...` syntax for hexadecimal and `0b...` syntax for binary. ## Future work ### File permissions API We may still provide a file permissions API in Carbon, for example as part of a `Core` file system API. This proposal takes no stance on what that API should look like. The only decision being made right now is that supporting octal literals is worthwhile for interoperability and migration. ## Rationale This proposal effectively advances Carbon's goals by focusing on: - [Interoperability with and migration from existing C++ code](/docs/project/goals.md#interoperability-with-and-migration-from-existing-c-code): Providing a direct counterpart for C++ octal literals simplifies the migration of Unix file system code without needing to wait for a better file permission API. - [Code that is easy to read, understand, and write](/docs/project/goals.md#code-that-is-easy-to-read-understand-and-write): The explicit `0o` prefix avoids the frequent confusion caused by C++'s leading `0` syntax, while maintaining consistency with hex and binary prefixes. - [Software and language evolution](/docs/project/goals.md#software-and-language-evolution): The `0o` octal literal syntax is consistent with other literals. We don't expect it to hinder future language features. ## Alternatives considered ### No octal literals Proposal #143 rejected octal literals. The main argument was that they are rarely used. However, the cost of octal literal syntax is low, and the benefit for C++ interoperability and migration is enough that we should add them. ================================================ FILE: proposals/scripts/BUILD ================================================ # Part of the Carbon Language project, under the Apache License v2.0 with LLVM # Exceptions. See /LICENSE for license information. # SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception load("@rules_python//python:defs.bzl", "py_binary", "py_test") py_binary( name = "new_proposal", srcs = ["new_proposal.py"], python_version = "PY3", ) py_test( name = "new_proposal_test", size = "small", srcs = ["new_proposal_test.py"], data = ["template.md"], python_version = "PY3", deps = [":new_proposal"], ) ================================================ FILE: proposals/scripts/README.md ================================================ # Scripts Scripts for use with proposals. See individual scripts for more details. ================================================ FILE: proposals/scripts/__init__.py ================================================ ================================================ FILE: proposals/scripts/new_proposal.py ================================================ #!/usr/bin/env python3 """Prepares a new proposal file and PR.""" __copyright__ = """ Part of the Carbon Language project, under the Apache License v2.0 with LLVM Exceptions. See /LICENSE for license information. SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception """ import argparse import os import re import shlex import shutil import subprocess import sys from pathlib import Path from typing import List, Optional _PROMPT = """This will: - Create and switch to a new branch named '%s'. - Create a new proposal titled '%s'. - Create a PR for the proposal. Continue? (Y/n) """ _LINK_TEMPLATE = """Proposal links (add links as proposal evolves): - Evolution links: - [Proposal PR](https://github.com/carbon-language/carbon-lang/pull/%s) - `[RFC topic](TODO)` - `[Decision topic](TODO)` - `[Decision PR](TODO)` - `[Announcement](TODO)` - Related links (optional): - `[Idea topic](TODO)` - `[TODO](TODO)` """ def _parse_args(args: Optional[List[str]] = None) -> argparse.Namespace: """Parses command-line arguments and flags.""" parser = argparse.ArgumentParser( description="Generates a branch and PR for a new proposal with the " "specified title." ) parser.add_argument( "title", metavar="TITLE", help="The title of the proposal.", ) parser.add_argument( "--branch", metavar="BRANCH", help="The name of the branch. Automatically generated from the title " "by default.", ) parser.add_argument( "--remote", metavar="REMOTE", default="origin", help="The git remote name where the branch will be pushed. Defaults to " "'origin'.", ) parser.add_argument( "--repo_root", metavar="REPO_ROOT", default=Path(__file__).parents[2], help="The repository root, mainly for testing cross-repository. " "Automatically found by default.", ) parser.add_argument( "--branch-start-point", metavar="BRANCH_START_POINT", default="trunk", type=str, help="The starting point for the new branch.", ) return parser.parse_args(args=args) def _calculate_branch(parsed_args: argparse.Namespace) -> str: """Returns the branch name.""" if parsed_args.branch: assert isinstance(parsed_args.branch, str) return parsed_args.branch # Only use the first 20 chars of the title for branch names. return "proposal-%s" % (parsed_args.title.lower().replace(" ", "-")[0:20]) def _find_tool(tool: str) -> str: """Checks if a tool is present.""" tool_path = shutil.which(tool) if not tool_path: exit("ERROR: Missing the '%s' command-line tool." % tool) return tool_path def _fill_template(template_path: str, title: str, pr_num: int) -> str: """Fills out template TODO fields.""" with open(template_path) as template_file: content = template_file.read() content = re.sub(r"^# TODO\n", "# %s\n" % title, content) content = re.sub( r"(https://github.com/[^/]+/[^/]+/pull/)####", r"\g<1>%d" % pr_num, content, ) content = re.sub(r"\n## TODO(?:.|\n)*?(\n## )", r"\1", content) return content def _run( argv: List[str], check: bool = True, get_stdout: bool = False ) -> Optional[str]: """Runs a command.""" cmd = " ".join([shlex.quote(x) for x in argv]) print("\n+ RUNNING: %s" % cmd, file=sys.stderr) stdout_pipe = None if get_stdout: stdout_pipe = subprocess.PIPE p = subprocess.Popen(argv, stdout=stdout_pipe) stdout, _ = p.communicate() if get_stdout: out = stdout.decode("utf-8") print(out, end="") if check and p.returncode != 0: exit("ERROR: Command failed: %s" % cmd) if get_stdout: return out return None def _run_pr_create(argv: List[str]) -> int: """Runs a command and returns the PR#.""" out = _run(argv, get_stdout=True) assert out is not None match = re.search( r"^https://github.com/[^/]+/[^/]+/pull/(\d+)$", out, re.MULTILINE ) if not match: exit("ERROR: Failed to find PR# in output.") return int(match.group(1)) def main() -> None: parsed_args = _parse_args() # Switch to repo root early for consistent execution. os.chdir(parsed_args.repo_root) title = parsed_args.title branch = _calculate_branch(parsed_args) # Verify tools are available. gh_bin = _find_tool("gh") if Path(".jj").is_dir(): jj_bin = _find_tool("jj") git_bin = None else: git_bin = _find_tool("git") jj_bin = None precommit_bin = _find_tool("pre-commit") # Verify there are no uncommitted changes (jj has no equivalent). if git_bin: p = subprocess.run([git_bin, "diff-index", "--quiet", "HEAD", "--"]) if p.returncode != 0: exit("ERROR: There are uncommitted changes in your git repo.") # Prompt before proceeding. response = "?" while response not in ("y", "n", ""): response = input(_PROMPT % (branch, title)).lower() if response == "n": exit("ERROR: Cancelled") # Create a proposal branch. if git_bin: _run( [ git_bin, "switch", "--create", branch, parsed_args.branch_start_point, ] ) _run([git_bin, "push", "-u", parsed_args.remote, branch]) else: assert jj_bin # For mypy. _run([jj_bin, "new", parsed_args.branch_start_point]) _run([jj_bin, "bookmark", "create", branch]) _run( [ jj_bin, "bookmark", "track", branch, "--remote", parsed_args.remote, ] ) # Copy template.md to a temp file. template_path = "proposals/scripts/template.md" temp_path = "proposals/new-proposal.tmp.md" shutil.copyfile(template_path, temp_path) initial_desc = "Creating new proposal: %s" % title if git_bin: _run([git_bin, "add", temp_path]) _run([git_bin, "commit", "-m", initial_desc]) else: assert jj_bin # For mypy. _run([jj_bin, "describe", "-m", initial_desc]) # Create a PR with WIP+proposal labels. if git_bin: _run([git_bin, "push"]) user = _run([git_bin, "config", "get", "user.name"], get_stdout=True) else: assert jj_bin # For mypy. _run([jj_bin, "git", "push"]) user = _run([jj_bin, "config", "get", "user.name"], get_stdout=True) assert user # For mypy. user = user.strip() pr_num = _run_pr_create( [ gh_bin, "pr", "create", "--head", "%s:%s" % (user, branch), "--draft", "--label", "proposal", "--label", "proposal draft", "--repo", "carbon-language/carbon-lang", "--title", title, "--body", "TODO: add summary and links here", ] ) # Remove the temp file, create p####.md, and fill in PR information. os.remove(temp_path) final_path = "proposals/p%04d.md" % pr_num content = _fill_template(template_path, title, pr_num) with open(final_path, "w") as final_file: final_file.write(content) # Run pre-commit for a ToC update, then push the PR update. final_desc = "Filling out template with PR %d" % pr_num if git_bin: _run([git_bin, "add", temp_path, final_path]) _run([precommit_bin, "run"], check=False) _run([git_bin, "commit", "--amend", "-m", final_desc]) _run([git_bin, "push", "--force-with-lease"]) else: assert jj_bin # For mypy. _run( [precommit_bin, "run", "--files", "$(jj diff --name-only)"], check=False, ) _run([jj_bin, "describe", "-m", final_desc]) _run([jj_bin, "git", "push"]) print( "\nCreated PR %d for %s. Make changes to:\n %s" % (pr_num, title, final_path) ) if __name__ == "__main__": main() ================================================ FILE: proposals/scripts/new_proposal_test.py ================================================ #!/usr/bin/env python3 """Tests for new_proposal.py.""" __copyright__ = """ Part of the Carbon Language project, under the Apache License v2.0 with LLVM Exceptions. See /LICENSE for license information. SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception """ import os import unittest from proposals.scripts import new_proposal class TestNewProposal(unittest.TestCase): def test_calculate_branch_short(self): parsed_args = new_proposal._parse_args(["foo bar"]) self.assertEqual( new_proposal._calculate_branch(parsed_args), "proposal-foo-bar" ) def test_calculate_branch_long(self): parsed_args = new_proposal._parse_args( ["A really long long long title"] ) self.assertEqual( new_proposal._calculate_branch(parsed_args), "proposal-a-really-long-long-l", ) def test_calculate_branch_flag(self): parsed_args = new_proposal._parse_args(["--branch=wiz", "foo"]) self.assertEqual(new_proposal._calculate_branch(parsed_args), "wiz") def test_fill_template(self): # Switch directories for the test. parsed_args = new_proposal._parse_args(["foo"]) os.chdir(parsed_args.repo_root) content = new_proposal._fill_template( "proposals/scripts/template.md", "TITLE", 123, ) self.assertTrue(content.startswith("# TITLE\n\n"), content) self.assertTrue( "[Pull request](https://github.com/carbon-language/carbon-lang/" "pull/123)" in content, content, ) self.assertTrue( "\n\n## Abstract\n\n" in content, content ) def test_run_success(self): new_proposal._run(["true"]) def test_run_failure(self): self.assertRaises(SystemExit, new_proposal._run, ["false"]) if __name__ == "__main__": unittest.main() ================================================ FILE: proposals/scripts/template.md ================================================ # TODO [Pull request](https://github.com/carbon-language/carbon-lang/pull/####) ## Table of contents - [TODO: Initial proposal setup](#todo-initial-proposal-setup) - [Abstract](#abstract) - [Problem](#problem) - [Background](#background) - [Proposal](#proposal) - [Details](#details) - [Rationale](#rationale) - [Alternatives considered](#alternatives-considered) ## TODO: Initial proposal setup > TIP: Run `./new_proposal.py "TITLE"` to do new proposal setup. 1. Copy this template to `new.md`, and create a commit. 2. Create a GitHub pull request, to get a pull request number. - Add the `proposal draft` label to the pull request. 3. Rename `new.md` to `/proposals/p####.md`, where `####` should be the pull request number. 4. Update the title of the proposal (the `TODO` on line 1). 5. Update the link to the pull request (the `####` on line 11). 6. Delete this section. TODOs indicate where content should be updated for a proposal. See [Carbon Governance and Evolution](/docs/project/evolution.md) for more details. ## Abstract TODO: Describe, in a succinct paragraph, the gist of this document. This paragraph should be reproduced verbatim in the PR summary. ## Problem TODO: What problem are you trying to solve? How important is that problem? Who is impacted by it? ## Background TODO: Is there any background that readers should consider to fully understand this problem and your approach to solving it? ## Proposal TODO: Briefly and at a high level, how do you propose to solve the problem? Why will that in fact solve it? ## Details TODO: Fully explain the details of the proposed solution. ## Rationale TODO: How does this proposal effectively advance Carbon's goals? Rather than re-stating the full motivation, this should connect that motivation back to Carbon's stated goals and principles. This may evolve during review. Use links to appropriate sections of [`/docs/project/goals.md`](/docs/project/goals.md), and/or to documents in [`/docs/project/principles`](/docs/project/principles). For example: - [Community and culture](/docs/project/goals.md#community-and-culture) - [Language tools and ecosystem](/docs/project/goals.md#language-tools-and-ecosystem) - [Performance-critical software](/docs/project/goals.md#performance-critical-software) - [Software and language evolution](/docs/project/goals.md#software-and-language-evolution) - [Code that is easy to read, understand, and write](/docs/project/goals.md#code-that-is-easy-to-read-understand-and-write) - [Practical safety and testing mechanisms](/docs/project/goals.md#practical-safety-and-testing-mechanisms) - [Fast and scalable development](/docs/project/goals.md#fast-and-scalable-development) - [Modern OS platforms, hardware architectures, and environments](/docs/project/goals.md#modern-os-platforms-hardware-architectures-and-environments) - [Interoperability with and migration from existing C++ code](/docs/project/goals.md#interoperability-with-and-migration-from-existing-c-code) ## Alternatives considered TODO: What alternative solutions have you considered? ================================================ FILE: pyproject.toml ================================================ # Part of the Carbon Language project, under the Apache License v2.0 with LLVM # Exceptions. See /LICENSE for license information. # SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception [tool.black] line-length = 80 target-version = ['py310'] include = '\.pyi?$' ================================================ FILE: scripts/BUILD ================================================ # Part of the Carbon Language project, under the Apache License v2.0 with LLVM # Exceptions. See /LICENSE for license information. # SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception load("@rules_python//python:defs.bzl", "py_test") load("//bazel/cc_rules:defs.bzl", "cc_library") py_test( name = "no_op_test", size = "small", srcs = ["no_op_test.py"], main = "no_op_test.py", ) cc_library( name = "deps_for_clangd_tidy", srcs = ["deps_for_clangd_tidy.cpp"], deps = [ # `@boost.unordered` uses `strip_prefix`, which results in # `_virtual_includes` being part of the `compile_commands.json`. To # support tools like `clangd-tidy`, this is intended to generate that directory. # # `@boost.unordered` uses `strip_prefix`, which results in # bazel-out//bin/external/+_repo_rules+boost.unordered/_virtual_includes/boost.unordered "@boost.unordered", ], ) ================================================ FILE: scripts/bazel_mod_deps.py ================================================ #!/usr/bin/env python3 """Run `bazel mod deps` for pre-commit. Lets pre-commit handle modifications.""" __copyright__ = """ Part of the Carbon Language project, under the Apache License v2.0 with LLVM Exceptions. See /LICENSE for license information. SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception """ import subprocess import scripts_utils def main() -> None: scripts_utils.chdir_repo_root() bazel = scripts_utils.locate_bazel() subprocess.run([bazel, "mod", "deps"]) if __name__ == "__main__": main() ================================================ FILE: scripts/bench_runner.py ================================================ #!/usr/bin/env -S uv run --script # /// script # requires-python = ">=3.10" # dependencies = [ # "numpy", # "rich", # "scipy", # "quantiphy", # ] # /// """Script to run GoogleBenchmark binaries repeatedly and render results. This script helps run benchmarks repeatedly and render the resulting measurements in a way that effectively surfaces noisy benchmarks and provides statistically significant information about the measurements. There are two primary modes: 1) Running a single experiment benchmark binary repeatedly to understand that benchmark's performance. 2) Running both an experiment and a baseline benchmark binary that include the same benchmark names to understand the change in performance for each named benchmark. Across all of these modes, when rendering a specific metric for a benchmark, we also render the confidence intervals based on the specified `--alpha` parameter. For mode (1) when running a single benchmark binary, there is additional support for passing regular expressions that describe a set of comparable benchmarks for some main benchmark. When used, the comparable benchmarks for each main one are rendered as a delta of the main rather than as completely independent metrics. For mode (2) when running an experiment and baseline binary, every benchmark is rendered as a delta of the experiment vs. the baseline. Whenever rendering a delta, this script will flag statistically significant (according to the provided `--alpha`) improvements or regressions, compute the improvement or regression, and display the resulting p-value. This script uses non-parametric U-test for statistical significance, the same as Go's benchmark comparison tools, based on the large body of evidence that benchmarks rarely if ever tend to adhere to a normal or other known distribution. A non-parametric statistical model instead provides a much more realistic basis for comparing two measurements. The reported metrics themselves are also classified into "speed" vs. "cost" metrics in order to model whether larger is an improvement or a regression. The script uses `uv` to run it rather than Python directly, which manages and caches its dependencies. For installation instructions for `uv` see: - Carbon's documentation: https://docs.carbon-lang.dev/docs/project/contribution_tools.html#optional-tools - UV's documentation: https://docs.astral.sh/uv/getting-started/installation/ """ from __future__ import annotations __copyright__ = """ Part of the Carbon Language project, under the Apache License v2.0 with LLVM Exceptions. See /LICENSE for license information. SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception """ import argparse import json import math import numpy as np # type: ignore import re import scipy as sp # type: ignore import subprocess import sys from collections import defaultdict from dataclasses import dataclass, field from enum import Enum from pathlib import Path from quantiphy import Quantity # type: ignore from rich.console import Console from rich.padding import Padding from rich.progress import track from rich.table import Column, Table from rich.text import Text from rich.theme import Theme from typing import Optional def parse_args(args: Optional[list[str]] = None) -> argparse.Namespace: """Parsers command-line arguments and flags.""" parser = argparse.ArgumentParser(description=__doc__) parser.add_argument( "--exp_benchmark", metavar="BINARY", required=True, type=Path, help="The experiment benchmark binary to run", ) parser.add_argument( "--base_benchmark", metavar="BINARY", type=Path, help=""" The baseline benchmark binary to run. Passing this flag will enable both a baseline and experiment, and change the analysis to compute and display any statistically significant delta as well as the before and after values of the each benchmark run. """.strip(), ) parser.add_argument( "--benchmark_args", action="append", default=[], metavar="ARG", help="Extra arguments to both the experiment and baseline benchmark", ) parser.add_argument( "--exp_benchmark_args", action="append", default=[], metavar="ARG", help="Extra arguments to the experiment benchmark", ) parser.add_argument( "--base_benchmark_args", action="append", default=[], metavar="ARG", help="Extra arguments to the baseline benchmark", ) parser.add_argument( "--benchmark_comparable_re", metavar="PATTERN", action="append", default=[], help=""" A regular expression that is used to match sets of benchmarks that should be compared with each other. This flag may be specified multiple times with different regular expressions to handle multiple different grouping schemes or structures. May not be combined with `base_benchmark`. Each regular expression is used to group together benchmark names distinguished by a "tag" substring in the name. Either the regex as a whole or a `tag` symbolic capture group within the regex designates this substring. Further, a `main` symbolic capture group _must_ be included and only match when the specific substring is the main benchmark name and other matching ones should be viewed as comparisons against it. When rendering, only the name matching the main capture group will be rendered, with others rendered as comparisons against it based on the tag, and with statistical significance to evaluate the comparison. Example regex: `(?P(?P
Carbon)|Abseil|LLVM)HashBench` This produces three tags, `Carbon`, `Abseil`, and `LLVM`. The main tag is `Carbon`. TODO: This is only currently supported without a base benchmark to provide relative comparisons within a single benchmark binary. There are good models for handling this and surfacing delta-of-delta information with a base benchmark binary. """.strip(), ) parser.add_argument( "--runs", default=10, metavar="N", type=int, help="Number of runs of the benchmark", ) parser.add_argument( "--wall_time", action="store_true", help="Use wall-clock time instead of CPU time", ) parser.add_argument( "--show_iterations", action="store_true", help="Show the iteration counts", ) parser.add_argument( "--extra_metrics_filter", metavar="PATTERN", type=str, help="A regex filter on the names of extra metrics to display.", ) parser.add_argument( "--alpha", default=0.05, metavar="𝛂", type=float, help=""" Threshold for P-values to be considered statistically significant. Also used to compute the confidence intervals for individual metrics. """.strip(), ) parser.add_argument( "--output", choices=["console", "json"], default="console", help=""" Output format to use, note that `json` output doesn't do any analysis of the results, and just dumps the aggregate JSON data from the repeated runs. """.strip(), ) return parser.parse_args(args=args) # Default arguments that will be passed even when arguments are passed with # `--benchmark_args` to the script. These can be undone by overriding them in # explicitly passed arguments. DEFAULT_BENCHMARK_ARGS = [ # Randomize the order in which the benchmarks run to avoid skewed results # due to a specific order. "--benchmark_enable_random_interleaving", # Reduce the default minimum time to 0.1s as it's more effective to use # multiple runs to improve confidence in measurements. "--benchmark_min_time=0.1s", ] # Pre-compiled regexes to match metrics that measure _speed_: larger is better. SPEED_METRIC_PATTERNS = [ re.compile(p) for p in [ r"(?i)rate", r"(?i).*per[\s_](second|ms|ns)", ] ] # Pre-compiled regexes to match metrics that measure _cost_: smaller is better. COST_METRIC_PATTERNS = [ re.compile(p) for p in [ r"(?i)cycles", r"(?i)instructions", r"(?i)time", ] ] # Theme for use with the Rich `Console` printing. THEME = Theme( { "base_median": "cyan", "exp_median": "magenta", "base_conf": "cyan", "exp_conf": "magenta", "slower": "bright_red", "faster": "bright_green", } ) # The set of benchmark keys we ignore in the JSON data structure. Most of these # are things are incidental, but a few are more surprising. See comments on # specific entries for details. IGNORED_BENCHMARK_KEYS = set( [ "name", "family_index", "per_family_instance_index", "run_name", "run_type", "repetitions", "repetition_index", "threads", # We don't render `iterations` because we instead directly compute # statistical error bars using the multiple iterations. This removes the # need for manually considering the iteration count. "iterations", # We ignore the time and time unit metrics here because we directly # access and special case these metrics in order to apply the unit to # the times. "real_time", "cpu_time", "time_unit", ] ) class DeltaKind(Enum): """Models the relevant kinds of deltas that we end up wanting to render.""" IMPROVEMENT = "[faster]👍[/faster]" NEUTRAL = "~" REGRESSION = "[slower]👎[/slower]" NOISE = "" def __str__(self) -> str: return self.value @dataclass class RenderedDelta: """Rendered delta and pvalue for some metric.""" kind: DeltaKind delta: str pvalue: str @dataclass class RenderedMetric: """Rendered non-delta metric and its confidence interval.""" median: str conf: str @dataclass class BenchmarkRunMetrics: """The main data class used to collect metrics for benchmark runs. The data is read in using a JSON format that isn't organized in a convenient way to analyze and render, so we re-organize it into this data class and use that for analysis. Each object of this class corresponds to a specific named benchmark. """ # The main metrics for this named benchmark, or the "experiment". This field # is always populated. exp: list[Quantity] = field(default_factory=lambda: []) # The metrics for this named benchmark in the base execution. May be empty # if no base execution was provided to compute a delta against. base: list[Quantity] = field(default_factory=lambda: []) # Any comparable benchmark metrics, indexed by the tag name to use when # rendering the comparison. May be empty if there are no comparable # benchmarks for the main one this represents. comps: defaultdict[str, list[Quantity]] = field( default_factory=lambda: defaultdict(list) ) @dataclass class ComparableBenchmarkMapping: """Organizes any comparable benchmarks. Constructed with the list of benchmark names and regexes that describe comparable name structures. Names that match one of these regexes are organized into the main name in `main_benchmark_names`, and the comparable names in various mappings to allow computing comparisons metrics between the main and comparable names. Names that don't match any of the regexes are just directly included in `main_benchmark_names`. """ # Names that are considered "main" benchmarks after filtering. main_benchmark_names: list[str] # Maps a comparison benchmark name to its base name (tag removed). name_to_base: dict[str, str] # Maps a base name to its main benchmark name. base_to_main_name: dict[str, str] # Maps a comparison benchmark name to its tag. name_to_comp_tag: dict[str, str] # Maps a main benchmark name to a list of its comparison tags. main_name_to_comp_tags: dict[str, list[str]] def __init__( self, original_benchmark_names: list[str], comparable_re_strs: list[str], console: Console, ): """Identify main and comparable benchmarks.""" self.main_benchmark_names = [] self.name_to_base = {} self.base_to_main_name = {} self.name_to_comp_tag = {} self.main_name_to_comp_tags = {} comp_res = [ re.compile(comparable_re_str) for comparable_re_str in comparable_re_strs ] for comp_re in comp_res: if "main" not in comp_re.groupindex: console.log( "ERROR: No main capture group in the " "`--benchmark_comparable_re` flag!" ) sys.exit(1) for name in original_benchmark_names: comp_match = next( (m for comp_re in comp_res if (m := comp_re.search(name))), None ) if not comp_match: # Non-comparable benchmark self.main_benchmark_names.append(name) continue tag_group = 0 if "tag" in comp_match.re.groupindex: tag_group = comp_match.re.groupindex["tag"] tag = comp_match.group(tag_group) tag_begin, tag_end = comp_match.span(tag_group) base_name = name[:tag_begin] + name[tag_end:] self.name_to_base[name] = base_name if comp_match.group("main"): self.base_to_main_name[base_name] = name self.main_benchmark_names.append(name) else: self.name_to_comp_tag[name] = tag # Verify that for all the comparable benchmarks we actually found a main # benchmark name. We can't do this while processing initially as we # don't know the relative order of main and comparable benchmark names. # # Also collect a list of all the comparison tags for a given main name. # self.main_name_to_comp_tags: dict[str, list[str]] = {} for comp, comp_tag in self.name_to_comp_tag.items(): base_name = self.name_to_base[comp] main_name = self.base_to_main_name[base_name] if not main_name: console.log( f"ERROR: Comparable benchmark `{comp}` has no corresponding" " main benchmark name!" ) sys.exit(1) if comp_tag in self.main_name_to_comp_tags.get(main_name, []): console.log( f"ERROR: Duplicate comparison tag `{comp_tag}` for main " f"benchmark `{main_name}`!" ) sys.exit(1) self.main_name_to_comp_tags.setdefault(main_name, []).append( comp_tag ) def float_ratio(nom: float, denom: float) -> float: """Translate a ratio of floats into a float, handling divide by zero.""" if denom != 0.0: return nom / denom elif nom > 0.0: return math.inf elif nom < 0.0: return -math.inf else: return 0.0 def render_fixed_width_float(x: float) -> str: """Renders a floating point value into a fixed width string.""" if math.isinf(x): return f"{x:>4f}{'':<3}" frac, whole = math.modf(x) frac_str = f"{math.fabs(frac):<4.3f}"[1:] return f"{int(whole):> 3}{frac_str}" def render_ratio(ratio: float) -> str: """Renders a ratio into a human-friendly string form. This uses a % for ratios with a magnitude less than 1.0. For ratios with a larger magnitude, they are rendered as a fixed width floating point number with an `x` suffix. """ if ratio > 1.0 or ratio < -1.0: return f"{render_fixed_width_float(ratio)}x" else: return f"{render_fixed_width_float(ratio * 100.0)}%" def render_metric( alpha: float, times: list[Quantity], is_base: bool ) -> RenderedMetric: """Render a non-delta metric. Computes the string to use for both the metric itself and the string to show the confidence interval for that metric. Args: alpha: The alpha value to use for the confidence interval. times: The list of measurements. is_base: Whether to use the "baseline" or "experiment" theme in the rendered strings. """ if is_base: style_prefix = "base_" else: style_prefix = "exp_" units = times[0].units if all(x == times[0] for x in times): with Quantity.prefs(number_fmt="{whole:>3}{frac:<4} {units}"): return RenderedMetric( f"[{style_prefix}median]{times[0]:.3}[/{style_prefix}median]", "", ) median = Quantity(np.median(times), units=units) median_test = sp.stats.quantile_test(times, q=median) median_ci = median_test.confidence_interval(confidence_level=(1.0 - alpha)) ci_str = "?" if not math.isnan(median_ci.low) and not math.isnan(median_ci.high): low_delta = median - median_ci.low high_delta = median_ci.high - median assert low_delta >= 0.0, high_delta >= 0.0 delta = max(low_delta, high_delta) ci_str = render_ratio(float_ratio(delta, median)) with Quantity.prefs(number_fmt="{whole:>3}{frac:<4} {units}"): return RenderedMetric( f"[{style_prefix}median]{median:.3}[/{style_prefix}median]", f"[{style_prefix}conf]{ci_str:9}[/{style_prefix}conf]", ) def render_delta( metric: str, alpha: float, base: list[Quantity], exp: list[Quantity] ) -> RenderedDelta: """Render a delta metric. This handles computing the delta, its statistical significance, and whether that delta is an improvement or a regression based on the specific metric name. Args: metric: The name of the metric to guide whether bigger or smaller is an improvement. alpha: The alpha value to use for the confidence interval. base: The baseline measurements. exp: The experiment measurements. """ # Skip any delta when all the data is zero. This typically occurs for # uninteresting metrics or metrics that weren't collected for a given run. if all(b == 0 for b in base) and all(e == 0 for e in exp): return RenderedDelta(DeltaKind.NEUTRAL, "", "") if any(speed_pat.search(metric) for speed_pat in SPEED_METRIC_PATTERNS): bigger_style = "faster" smaller_style = "slower" bigger_kind = DeltaKind.IMPROVEMENT smaller_kind = DeltaKind.REGRESSION elif any(cost_pat.search(metric) for cost_pat in COST_METRIC_PATTERNS): bigger_style = "slower" smaller_style = "faster" bigger_kind = DeltaKind.REGRESSION smaller_kind = DeltaKind.IMPROVEMENT else: return RenderedDelta(DeltaKind.NEUTRAL, "", "") u_test = sp.stats.mannwhitneyu(base, exp) if u_test.pvalue >= alpha: return RenderedDelta( DeltaKind.NOISE, " ?? ", f"p={u_test.pvalue:.3}" ) kind = DeltaKind.NEUTRAL base_median = np.median(base) exp_median = np.median(exp) exp_ratio = float_ratio(exp_median, base_median) # TODO: Maybe the threshold of "interesting" should be configurable instead # of being fixed at 0.1%. if exp_ratio >= 1.001: style = bigger_style kind = bigger_kind elif exp_ratio <= 0.999: style = smaller_style kind = smaller_kind else: style = "default" if exp_ratio >= 2.0 or exp_ratio <= 0.5: return RenderedDelta( kind, f"[{style}]{render_fixed_width_float(exp_ratio)}x[/{style}]", f"p={u_test.pvalue:.3}", ) # Use a percent-delta for smaller ratios to make the delta more easily # understood by readers. exp_delta_percent = ( float_ratio(exp_median - base_median, base_median) * 100.0 ) return RenderedDelta( kind, f"[{style}]{render_fixed_width_float(exp_delta_percent)}%[/{style}]", f"p={u_test.pvalue:.3}", ) def render_metric_column( metric: str, alpha: float, runs: list[BenchmarkRunMetrics], ) -> Table: """Render the column of the benchmark results table for a given metric. We render a single column for each metric, and use a careful line-oriented layout within the column to ensure "rows" line up for each individual benchmark. Within the column, we use a nested table to layout the different rendered strings. A key goal of the rendering throughout is to arrange for rendered numbers to have the decimal point in a consistent column so that it isn't confusing for readers to identify the position of the decimal point and magnitude of the number rendered. Args: metric: The name of the metric to render. alpha: The alpha value to use for the confidence interval. runs: The list of benchmark runs. """ t = Table.grid( Column(), # It might seem like we want the left column here to be right-aligned, # but we're going to carefully align the digits in the format string, # and we can't easily control the length of units. So we left-align to # simplify the digit layout. Column(justify="left"), Column(justify="center"), Column(justify="left"), padding=(0, 1), ) for run in runs: if len(run.base) != 0: # We have a baseline run to compare against, so compute the delta # between it and the experiment as well as the specific baseline run # metric. rendered_delta = render_delta(metric, alpha, run.base, run.exp) rendered_base = render_metric(alpha, run.base, is_base=True) # Add the delta as the first row, then the baseline metric. t.add_row( str(rendered_delta.kind), rendered_delta.delta, "", rendered_delta.pvalue, ) t.add_row("", rendered_base.median, "±", rendered_base.conf) # Now render the experiment metric and add its row. rendered_exp = render_metric(alpha, run.exp, is_base=False) t.add_row("", rendered_exp.median, "±", rendered_exp.conf) # If we have any comparable benchmarks, render each of them as first a # delta and then the specific comparable metric as its own kind of # baseline. # # TODO: At some point when we support combining baseline _runs_ with # comparable metrics, we'll need to change this to render both baseline # and experiment comparables and a delta-of-delta. But currently we # don't support combining these which simplifies the rendering here. for name, comp in sorted(run.comps.items()): rendered_delta = render_delta(metric, alpha, comp, run.exp) t.add_row( str(rendered_delta.kind), rendered_delta.delta, "", rendered_delta.pvalue, ) rendered_comp = render_metric(alpha, comp, is_base=True) t.add_row("", rendered_comp.median, "±", rendered_comp.conf) # Lastly, if we had a baseline run or any comparable metrics we will # have rendered multiple lines of data. Add a blank line so that these # form a visual group. if len(run.base) != 0 or len(run.comps) != 0: t.add_row() return t def run_benchmark_binary( binary_path: Path, common_args: list[str], specific_args: list[str], num_runs: int, console: Console, ) -> list[dict]: """Runs a benchmark binary multiple times and collects results. The results are parsed out of the JSON output from each run, and returned as a list of dictionaries. Each dictionary represents one run. This will log the command being run, show a progress bar for each run performed, and then log de-duplicated `stderr` output from the runs. """ # If the binary path has no directory components and exists as a relative # file, add `./` as a prefix. Otherwise, we want to pass the name unchanged # for `PATH` search. binary_str = str(binary_path) if len(binary_path.parts) == 1 and binary_path.exists(): binary_str = f"./{binary_str}" run_cmd = ( [binary_str] + DEFAULT_BENCHMARK_ARGS + common_args + specific_args # Pass the format flag last as it is required and can't be overridden. + ["--benchmark_format=json"] ) console.log(f"Executing: {' '.join(run_cmd)}") runs_data = [] unique_stderr: list[bytes] = [] for _ in track( range(num_runs), description=f"Running {binary_path.name}..." ): p = subprocess.run( run_cmd, check=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, ) runs_data.append(json.loads(p.stdout)) stderr = p.stderr.strip() if len(stderr) != 0 and stderr not in unique_stderr: unique_stderr.append(stderr) for stderr_output in unique_stderr: # Decode stderr, replacing errors in case of non-UTF-8 characters. console.log( f"{binary_path.name} stderr:\n" f"{stderr_output.decode('utf-8', errors='replace')}" ) return runs_data def print_run_context( console: Console, num_runs: int, exp_runs: list[dict], has_baseline: bool, ) -> None: """Prints the context from the benchmark runs. This replicates the useful context information from Google Benchmark's default output, such as CPU information and cache sizes. TODO: Print differently when context of base and experiment runs differ. Args: console: The rich console to print to. num_runs: The number of times the benchmarks were run. exp_runs: The results from the experiment benchmark runs. has_baseline: Whether a baseline benchmark was also run. """ if has_baseline: runs_description = f"Ran baseline and experiment {num_runs} times" else: runs_description = f"Ran {num_runs} times" context = exp_runs[0]["context"] console.print( f"{runs_description} on " f"{context['num_cpus']} x {context['mhz_per_cpu']} MHz CPUs" ) console.print("CPU caches:") for cache in context["caches"]: size = Quantity(cache["size"], binary=True) console.print(f" L{cache['level']} {cache['type']} {size:b}") console.print( f"Load avg: {' '.join([str(load) for load in context['load_avg']])}" ) def get_benchmark_names_and_metrics( console: Console, parsed_args: argparse.Namespace, exp_runs: list[dict], base_runs: list[dict], ) -> tuple[list[str], list[str]]: """Extracts benchmark names and metrics from benchmark run results. This function determines the list of unique benchmark names and the metrics to be displayed based on the benchmark output and command-line arguments. Args: parsed_args: The parsed command-line arguments. exp_runs: A list of benchmark run results for the experiment binary. base_runs: A list of benchmark run results for the baseline binary. Returns: - The list of unique benchmark names, maintaining their order. - The list of metrics to display. """ # Start with the base time and iteration metrics requested. metrics: list[str] = [] if parsed_args.wall_time: metrics.append("real_time") else: metrics.append("cpu_time") if parsed_args.show_iterations: metrics.append("iterations") # Compile a regex for filtering extra metrics, if provided. if metrics_filter_str := parsed_args.extra_metrics_filter: metrics_filter = re.compile(metrics_filter_str) else: metrics_filter = None # We only need to inspect the first run to find all benchmark and metric # names. We combine benchmarks from both experiment and baseline runs to get # a complete set. one_run_benchmarks = exp_runs[0]["benchmarks"] if parsed_args.base_benchmark: one_run_benchmarks += base_runs[0]["benchmarks"] benchmark_name_set: set[str] = set() benchmark_name_indices: dict[str, tuple[int, int]] = {} for benchmark in one_run_benchmarks: name = benchmark["name"] benchmark_name_set.add(name) indices = ( benchmark["family_index"], benchmark["per_family_instance_index"], ) if name not in benchmark_name_indices: benchmark_name_indices[name] = indices else: if benchmark_name_indices[name] != indices: console.print( f"ERROR: Inconsintent indices {indices} and " f"{benchmark_name_indices[name]} for benchmark `{name}`." ) sys.exit(1) # Add any extra metrics from this benchmark. for key in benchmark.keys(): if key in metrics or key in IGNORED_BENCHMARK_KEYS: continue if metrics_filter and not re.search(metrics_filter, key): continue metrics.append(key) benchmark_names = sorted( list(benchmark_name_set), key=lambda name: benchmark_name_indices[name] ) return benchmark_names, metrics def collect_benchmark_metrics( benchmark_names: list[str], metrics: list[str], exp_runs: list[dict], base_runs: list[dict], comp_mapping: ComparableBenchmarkMapping, ) -> dict[str, dict[str, BenchmarkRunMetrics]]: """Collects and organizes all benchmark metrics from raw run data. This function takes the raw benchmark run data and organizes it into a structured format suitable for analysis and rendering. It initializes the main data structure, handles the mapping of comparable benchmarks, and populates the metrics for both experiment and baseline runs. Args: benchmark_names: The initial list of unique benchmark names. metrics: A list of all metric names to be collected. exp_runs: A list of benchmark run results for the experiment binary. base_runs: A list of benchmark run results for the baseline binary. comp_mapping: The mapping of comparable benchmarks. Returns: A dictionary where keys are metric names. The values are another dictionary where keys are benchmark names and values are BenchmarkRunMetrics objects containing the collected measurements. """ # Initialize the data structure to hold all collected metrics. benchmark_metrics: dict[str, dict[str, BenchmarkRunMetrics]] = { metric: {name: BenchmarkRunMetrics() for name in benchmark_names} for metric in metrics } # Populate metrics from the experiment runs. for run in exp_runs: for b in run["benchmarks"]: name = b["name"] for metric in metrics: # Time metrics have a `time_unit` field that needs to be # appended for correct parsing by the Quantity library. unit = b.get("time_unit", "") if "time" in metric else "" # If this is a comparable benchmark, add its metrics to the # 'comps' list of its corresponding main benchmark. if maybe_comp_tag := comp_mapping.name_to_comp_tag.get(name): main_name = comp_mapping.base_to_main_name[ comp_mapping.name_to_base[name] ] benchmark_metrics[metric][main_name].comps[ maybe_comp_tag ].append(Quantity(f"{b[metric]}{unit}")) # Otherwise, add it to the 'exp' list of its own entry if it's # a main benchmark. elif name in benchmark_names: benchmark_metrics[metric][name].exp.append( Quantity(f"{b[metric]}{unit}") ) # Populate metrics from the baseline runs. for run in base_runs: for b in run["benchmarks"]: name = b["name"] # Baseline runs don't have comparable benchmarks, so we only need # to populate the 'base' list for main benchmarks. if name in benchmark_names: for metric in metrics: unit = b.get("time_unit", "") if "time" in metric else "" benchmark_metrics[metric][name].base.append( Quantity(f"{b[metric]}{unit}") ) return benchmark_metrics def print_metric_key( console: Console, alpha: float, has_baseline: bool, comp_mapping: ComparableBenchmarkMapping, ) -> None: """Prints a legend for the metrics table. This explains the format of the output table, including what the delta, median, and confidence interval values represent. Args: console: The rich console to print to. alpha: The alpha value for statistical significance. has_baseline: Whether a baseline benchmark was run. """ console.print("Metric key:") conf = int(100 * (1.0 - alpha)) name = "BenchmarkName..." delta_icon = str(DeltaKind.IMPROVEMENT) delta = "[faster][/faster]" p = "p=" base_median = "[base_median][/base_median]" base_conf = f"[base_conf]<% at {conf}th conf>[/base_conf]" exp_median = "[exp_median][/exp_median]" exp_conf = f"[exp_conf]<% at {conf}th conf>[/exp_conf]" key_table = Table.grid( Column(justify="right"), Column(), Column(), Column(), Column(), padding=(0, 1), ) if has_baseline: key_table.add_row(name, delta_icon, delta, "", p) key_table.add_row("baseline:", "", base_median, "±", base_conf) key_table.add_row("experiment:", "", exp_median, "±", exp_conf) else: key_table.add_row(name, "", exp_median, "±", exp_conf) # Only display comparable key if we have comparables to display. if bool(comp_mapping.name_to_comp_tag): key_table.add_row("vs Comparable:", delta_icon, delta, p) key_table.add_row("", "", base_median, "±", base_conf) console.print(Padding(key_table, (0, 0, 1, 3))) def print_results_table( console: Console, alpha: float, has_baseline: bool, metrics: list[str], benchmark_names: list[str], benchmark_metrics: dict[str, dict[str, BenchmarkRunMetrics]], comp_mapping: ComparableBenchmarkMapping, ) -> None: """Builds and prints the main results table. This function constructs a rich `Table` to display the benchmark results, including deltas, medians, and confidence intervals for each metric. It then prints this to the provided `console`. Args: console: The rich console to print to. metrics: A list of metric names to be displayed as columns. benchmark_names: A list of main benchmark names for the rows. alpha: The alpha value for statistical significance. benchmark_metrics: A nested dictionary containing the collected metrics for each benchmark and metric. has_baseline: Whether a baseline benchmark was run. comp_mapping: The mapping of comparable benchmarks. """ METRIC_TITLES = { "real_time": "Wall Time", "cpu_time": "CPU Time", "iterations": "Iterations", } name_width = max( ( len(name) for name in ( benchmark_names + [ f"vs {tag}:" for tag in comp_mapping.name_to_comp_tag.values() ] + ["experiment:"] ) ) ) table = Table(show_edge=False) # The benchmark name column we want to justify right for the sub-labels, but # we will fill the name in the column completed and the name will visually # be justified to the left, so force the heading to justify left unlike the # column text. We also disable wrapping because we manually fill the column # and require line-precise layout. table.add_column( Text("Benchmark", justify="left"), justify="right", no_wrap=True ) for metric in metrics: title = Text(METRIC_TITLES.get(metric, metric), justify="center") table.add_column(title, justify="left", no_wrap=True) name_t = Table.grid(Column(justify="right", no_wrap=True), expand=True) for name in benchmark_names: name_t.add_row(f"{name}{'.' * (name_width - len(name))}") if has_baseline: name_t.add_row("baseline:") name_t.add_row("experiment:") name_t.add_row() elif comp_tags := comp_mapping.main_name_to_comp_tags.get(name): for tag in comp_tags: name_t.add_row(f"vs {tag}:") name_t.add_row() name_t.add_row() row = [name_t] for metric in metrics: metric_runs = benchmark_metrics[metric] row.append( render_metric_column( metric, alpha, [metric_runs[name] for name in benchmark_names] ) ) table.add_row(*row) console.print(table) def main() -> None: parsed_args = parse_args() console = Console(theme=THEME) Quantity.set_prefs(spacer=" ", map_sf=Quantity.map_sf_to_greek) if parsed_args.base_benchmark and parsed_args.benchmark_comparable_re: console.print( "ERROR: Cannot mix a base benchmark binary with benchmark " "comparisons." ) sys.exit(1) # Run the benchmark(s) and collect the results into a data structure for # processing. num_runs = parsed_args.runs base_runs: list[dict] = [] has_baseline = bool(parsed_args.base_benchmark) if has_baseline: base_runs = run_benchmark_binary( parsed_args.base_benchmark, parsed_args.benchmark_args, parsed_args.base_benchmark_args, num_runs, console, ) exp_runs = run_benchmark_binary( parsed_args.exp_benchmark, parsed_args.benchmark_args, parsed_args.exp_benchmark_args, num_runs, console, ) # If JSON output is requested, just dump the data without further # processing. if parsed_args.output == "json": console.log("Printing JSON results...") console.print_json(json.dumps(exp_runs)) if has_baseline: console.print_json(json.dumps(base_runs)) return print_run_context(console, num_runs, exp_runs, has_baseline) # Collect the benchmark names and metric names. benchmark_names, metrics = get_benchmark_names_and_metrics( console, parsed_args, exp_runs, base_runs ) # Build any mappings between main benchmark names and comparables, and reset # our benchmark names to the main ones. comp_mapping = ComparableBenchmarkMapping( benchmark_names, parsed_args.benchmark_comparable_re, console ) benchmark_names = comp_mapping.main_benchmark_names # Collect and organize the actual benchmark metrics from the raw JSON # structures across the runs. This pivots the data into an easy to analyze # and render structure, but doesn't do the analysis itself. benchmark_metrics = collect_benchmark_metrics( benchmark_names, metrics, exp_runs, base_runs, comp_mapping ) # Analyze and render a readable table of the collected metrics. This is # where we do statistical analysis and render confidence intervals, # significance, and other helpful indicators based on the collected data. We # also print relevant keys to reading and interpreting the rendered data. alpha = parsed_args.alpha console.print( "Computing statistically significant deltas only where" f"the P-value < 𝛂 of {alpha}" ) print_metric_key(console, alpha, has_baseline, comp_mapping) print_results_table( console, alpha, has_baseline, metrics, benchmark_names, benchmark_metrics, comp_mapping, ) if __name__ == "__main__": main() ================================================ FILE: scripts/calculate_release_shas.py ================================================ #!/usr/bin/env python3 """Prints sha information for tracked tool releases.""" __copyright__ = """ Part of the Carbon Language project, under the Apache License v2.0 with LLVM Exceptions. See /LICENSE for license information. SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception """ import scripts_utils def main() -> None: scripts_utils.calculate_release_shas() if __name__ == "__main__": main() ================================================ FILE: scripts/check_build_graph.py ================================================ #!/usr/bin/env python3 """Verify that the bazel build graph is in a valid state, for pre-commit.""" __copyright__ = """ Part of the Carbon Language project, under the Apache License v2.0 with LLVM Exceptions. See /LICENSE for license information. SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception """ import subprocess import scripts_utils def main() -> None: scripts_utils.chdir_repo_root() bazel = scripts_utils.locate_bazel() subprocess.check_call([bazel, "build", "--nobuild", "//..."]) if __name__ == "__main__": main() ================================================ FILE: scripts/check_header_guards.py ================================================ #!/usr/bin/env python3 """Checks for missing or incorrect header guards.""" __copyright__ = """ Part of the Carbon Language project, under the Apache License v2.0 with LLVM Exceptions. See /LICENSE for license information. SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception """ from collections.abc import Iterable from pathlib import Path import re import sys from typing import NamedTuple, Optional class Guard(NamedTuple): """A guard line in a file.""" line: int guard: str def find_guard( lines: list[str], pattern: str, from_end: bool ) -> Optional[Guard]: """Searches the lines for something matching the pattern.""" lines_range: Iterable[str] = lines if from_end: lines_range = reversed(lines) for index, line in enumerate(lines_range): m = re.match(pattern, line) if m: if from_end: index = len(lines) - index - 1 return Guard(index, m[1]) return None def maybe_replace( lines: list[str], old_guard: Guard, guard_prefix: str, guard: str ) -> None: """Replaces a header guard in the file if needed.""" if guard != old_guard.guard: line = lines[old_guard.line].rstrip("\n") print(f"- Replacing line {old_guard.line}: `{line}`", file=sys.stderr) lines[old_guard.line] = f"{guard_prefix} {guard}\n" def check_path(path: Path) -> bool: """Checks the path for header guard issues.""" if path.suffix != ".h": print(f"Not a header: {path}", file=sys.stderr) return True with path.open() as f: lines = f.readlines() guard_path = str(path).upper().replace("/", "_").replace(".", "_") guard = f"CARBON_{guard_path}_" ifndef = find_guard(lines, "#ifndef ([A-Z0-9_]+_H_)", False) define = find_guard(lines, "#define ([A-Z0-9_]+_H_)", False) endif = find_guard(lines, "#endif(?: // ([A-Z0-9_]+_H_))?", True) if ifndef is None or define is None or endif is None: print(f"Incomplete header guard in {path}:", file=sys.stderr) if ifndef is None: print(f"- Missing `#ifndef {guard}`", file=sys.stderr) if define is None: print(f"- Missing `#define {guard}`", file=sys.stderr) if endif is None: print(f"- Missing `#endif // {guard}`", file=sys.stderr) return True if ifndef.line + 1 != define.line: print( f"Non-consecutive header guard in {path}: " f"#ifndef on line {ifndef.line + 1}, " f"#define on line {define.line + 1}.", file=sys.stderr, ) return True if endif.line != len(lines) - 1: print( f"Misordered header guard in {path}: #endif on line {endif.line}, " f"should be on last line ({len(lines) - 1}).", file=sys.stderr, ) return True if guard != ifndef.guard or guard != define.guard or guard != endif.guard: print(f"Fixing header guard in {path} to {guard}:", file=sys.stderr) maybe_replace(lines, ifndef, "#ifndef", guard) maybe_replace(lines, define, "#define", guard) maybe_replace(lines, endif, "#endif //", guard) with path.open("w") as f: f.writelines(lines) return True return False def main() -> None: has_errors = False for arg in sys.argv[1:]: if check_path(Path(arg)): has_errors = True if has_errors: exit(1) if __name__ == "__main__": main() ================================================ FILE: scripts/check_sha_filenames.py ================================================ #!/usr/bin/env python3 """Requires files be named for their SHA1. We name fuzzer corpus files for their SHA1. The choice of SHA1 is for consistency with git. This maintains the current extension for .textproto, but at some point we might want to specify the extension by path. """ __copyright__ = """ Part of the Carbon Language project, under the Apache License v2.0 with LLVM Exceptions. See /LICENSE for license information. SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception """ import hashlib from pathlib import Path import sys def main() -> None: has_errors = False for arg in sys.argv[1:]: path = Path(arg) with path.open("rb") as f: content = f.read() if len(content) == 0: want = "empty" else: want = hashlib.sha1(content).hexdigest() want_path = path.parent.joinpath(want).with_suffix(path.suffix) if path != want_path: print(f"Renaming {path} to {want_path}", file=sys.stderr) path.rename(want_path) has_errors = True if has_errors: exit(1) if __name__ == "__main__": main() ================================================ FILE: scripts/create_compdb.py ================================================ #!/usr/bin/env python3 """Create a compilation database for Clang tools like `clangd`. If you want `clangd` to be able to index this project, run this script from the workspace root to generate a rich compilation database. After the first run, you should only need to run it if you encounter `clangd` problems, or if you want `clangd` to build an up-to-date index of the entire project. Note that in the latter case you may need to manually clear and rebuild clangd's index after running this script. Note that this script will build generated files in the Carbon project and otherwise touch the Bazel build. It works to do the minimum amount necessary. Once setup, generally subsequent builds, even of small parts of the project, different configurations, or that hit errors won't disrupt things. But, if you do hit errors, you can get things back to a good state by fixing the build of generated files and re-running this script. """ __copyright__ = """ Part of the Carbon Language project, under the Apache License v2.0 with LLVM Exceptions. See /LICENSE for license information. SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception """ import argparse import json import subprocess import sys from typing import Any, Dict import scripts_utils def _build_generated_files( bazel: str, logtostderr: bool, dump_files: bool, extra_bazel_flags: list[str] = [], ) -> None: print("Building the generated files so that tools can find them...") # Collect the generated file labels. Include some rules which generate # files but aren't classified as "generated file". kinds_query = ( "filter(" ' ".*\\.(h|hpp|hxx|cpp|cc|c|cxx|def|inc|s|S)$",' ' kind("(.*generate.*|manifest_as_cpp)",' # tree_sitter is excluded here because it causes the query to failure on # `@platforms`. " deps(//... except //utils/tree_sitter/...))" ")" ) log_to = None if not logtostderr: log_to = subprocess.DEVNULL generated_file_labels = subprocess.check_output( [bazel, "query"] + extra_bazel_flags + ["--keep_going", "--output=label", kinds_query], stderr=log_to, encoding="utf-8", ).splitlines() if dump_files: for f in sorted(generated_file_labels): print(f) sys.exit(0) print(f"Found {len(generated_file_labels)} generated files...", flush=True) # Directly build these labels so that indexing can find them. Allow this to # fail in case there are build errors in the client, and just warn the user # that they may be missing generated files. subprocess.check_call( [bazel, "build"] + extra_bazel_flags + ["--keep_going", "--remote_download_outputs=toplevel"] + generated_file_labels # We also need the Bazel C++ runfiles that aren't "generated", but are # not linked into place until built. + ["@bazel_tools//tools/cpp/runfiles:runfiles"] ) def _get_config_for_entry(entry: Dict[str, Any]) -> str: """Returns the configuration for a compile command entry.""" arguments = entry.get("arguments") # Only handle files where the object file argument is easily found as # the last argument, which matches the expected structure from Bazel. if not arguments or len(arguments) < 2 or arguments[-2] != "-o": return "unknown" obj_file = arguments[-1] # The configuration is the name of the subdirectory of `bazel-out`. if not obj_file.startswith("bazel-out/"): return "unknown" return str(obj_file.split("/")[1]) def _filter_compilation_database(file_path: str) -> None: """Filters out duplicate exec-config entries from the database.""" print("Filtering out duplicate exec-configuration entries...") try: with open(file_path, "r") as f: commands = json.load(f) except FileNotFoundError: print(f"Error: The file '{file_path}' was not found.") sys.exit(1) except json.JSONDecodeError: print(f"Error: The file '{file_path}' is not a valid JSON file.") sys.exit(1) # We want to skip compiles that were in the "exec" configuration for tools. # Because we generate compile commands for every bazel cc_* target in the # main configuration, even if only used by tools, their sources should be # covered and the exec configuration would simply be a duplicate. # # Detecting this based on the `-exec-` string in the configuration name of # the directory is a bit of a hack, but even using the `--notool_deps` # argument, Bazel seems to sometimes include this configuration in the query # that produces the compilation database. filtered_commands = [ entry for entry in commands if "-exec-" not in _get_config_for_entry(entry) ] with open(file_path, "w") as f: # Use indent=4 for a human-readable, pretty-printed output file json.dump(filtered_commands, f, indent=4) print( "Filtered out " f"{len(commands) - len(filtered_commands)} " "duplicate entries..." ) def main() -> None: parser = argparse.ArgumentParser( description=__doc__, allow_abbrev=False, ) parser.add_argument( "--alsologtostderr", action="store_true", help="Prints subcommand errors to stderr (default: False)", ) parser.add_argument( "--dump-files", action="store_true", help="Dumps the full list of generated files (default: False)", ) parser.add_argument( "--extra-bazel-flag", action="append", default=[], help=( "Extra flag to pass to Bazel invocations, may be specified more " "than once" ), ) args = parser.parse_args() scripts_utils.chdir_repo_root() bazel = scripts_utils.locate_bazel() _build_generated_files( bazel, args.alsologtostderr, args.dump_files, args.extra_bazel_flag ) print( "Generating compile_commands.json (may take a few minutes)...", flush=True, ) subprocess.run( [ bazel, "run", ] + args.extra_bazel_flag + [ "@hedron_compile_commands//:refresh_all", "--", ] + args.extra_bazel_flag + [ "--notool_deps", ] ) _filter_compilation_database("compile_commands.json") if __name__ == "__main__": main() ================================================ FILE: scripts/deps_for_clangd_tidy.cpp ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // This file is only validating expected includes exist. See the BUILD target // for more information. #include ================================================ FILE: scripts/fix_cc_deps.py ================================================ #!/usr/bin/env python3 """Automatically fixes bazel C++ dependencies. Bazel has some support for detecting when an include refers to a missing dependency. However, the ideal state is that a given build target depends directly on all #include'd headers, and Bazel doesn't enforce that. This automates the addition for technical correctness. """ __copyright__ = """ Part of the Carbon Language project, under the Apache License v2.0 with LLVM Exceptions. See /LICENSE for license information. SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception """ import re import subprocess from typing import Callable, NamedTuple from xml.etree import ElementTree import scripts_utils class ExternalRepo(NamedTuple): # A function for remapping files to #include paths. remap: Callable[[str], str] # The target expression to gather rules for within the repo. target: str # Whether to use "" or <> for the include. use_system_include: bool = False class RuleChoice(NamedTuple): # Whether to use "" or <> for the include. use_system_include: bool # Possible rules that may be used. rules: set[str] # Maps external repository names to a method translating bazel labels to file # paths for that repository. EXTERNAL_REPOS: dict[str, ExternalRepo] = { # llvm:include/llvm/Support/Error.h ->llvm/Support/Error.h # clang-tools-extra/clangd:URI.h -> clang-tools-extra/clangd/URI.h "@llvm-project": ExternalRepo( lambda x: re.sub(":", "/", re.sub("^(.*:(lib|include))/", "", x)), "...", ), # tools/cpp/runfiles:runfiles.h -> tools/cpp/runfiles/runfiles.h "@bazel_tools": ExternalRepo(lambda x: re.sub(":", "/", x), "..."), # absl/flags:flag.h -> absl/flags/flag.h "@abseil-cpp": ExternalRepo(lambda x: re.sub(":", "/", x), "..."), # :re2/re2.h -> re2/re2.h "@re2": ExternalRepo(lambda x: re.sub(":", "", x), ":re2"), # :googletest/include/gtest/gtest.h -> gtest/gtest.h "@googletest": ExternalRepo( lambda x: re.sub(":google(?:mock|test)/include/", "", x), ":gtest", use_system_include=True, ), "@boost.unordered": ExternalRepo( lambda x: re.sub("^(.*:include)/", "", x), ":boost.unordered", use_system_include=True, ), } IGNORE_SOURCE_FILE_REGEX = re.compile( r"^(third_party/clangd.*|common/version.*\.cpp" r"|.*_autogen_manifest\.cpp" r"|toolchain/base/llvm_tools.def" r"|toolchain/base/runtimes_build_info.h)$" ) class Rule(NamedTuple): # For cc_* rules: # The hdrs + textual_hdrs attributes, as relative paths to the file. hdrs: set[str] # The srcs attribute, as relative paths to the file. srcs: set[str] # The deps attribute, as full bazel labels. deps: set[str] # For genrules: # The outs attribute, as relative paths to the file. outs: set[str] def remap_file(label: str) -> str: """Remaps a bazel label to a file.""" repo, _, path = label.partition("//") if not repo: return path.replace(":", "/") # Ignore the version, just use the repo name. repo = repo.split("~", 1)[0] assert repo in EXTERNAL_REPOS, repo return EXTERNAL_REPOS[repo].remap(path) def get_bazel_list(list_child: ElementTree.Element, is_file: bool) -> set[str]: """Returns the contents of a bazel list. The return will normally be the full label, unless `is_file` is set, in which case the label will be translated to the underlying file. """ results: set[str] = set() for label in list_child: assert label.tag in ("label", "output"), label.tag value = label.attrib["value"] if is_file: value = remap_file(value) results.add(value) return results def get_rules(bazel: str, targets: str, keep_going: bool) -> dict[str, Rule]: """Queries the specified targets, returning the found rules. keep_going will be set to true for external repositories, where sometimes we see query errors. The return maps rule names to rule data. """ args = [ bazel, "query", "--output=xml", f"kind('(cc_binary|cc_library|cc_test|genrule)', set({targets}))", ] if keep_going: args.append("--keep_going") p = subprocess.run( args, stdout=subprocess.PIPE, stderr=subprocess.PIPE, encoding="utf-8" ) # 3 indicates incomplete results from --keep_going, which is fine here. if p.returncode not in {0, 3}: print(p.stderr) exit(f"bazel query returned {p.returncode}") rules: dict[str, Rule] = {} for rule_xml in ElementTree.fromstring(p.stdout): assert rule_xml.tag == "rule", rule_xml.tag rule_name = rule_xml.attrib["name"] hdrs: set[str] = set() srcs: set[str] = set() deps: set[str] = set() outs: set[str] = set() rule_class = rule_xml.attrib["class"] for list_child in rule_xml.findall("list"): list_name = list_child.attrib["name"] if rule_class in ("cc_library", "cc_binary", "cc_test"): if list_name in ("hdrs", "textual_hdrs"): hdrs = hdrs.union(get_bazel_list(list_child, True)) elif list_name == "srcs": srcs = get_bazel_list(list_child, True) elif list_name == "deps": deps = get_bazel_list(list_child, False) elif rule_class == "genrule": if list_name == "outs": outs = get_bazel_list(list_child, True) elif rule_class in ("tree_sitter_cc_library", "cc_library_wrapper"): # Note that `cc_library_wrapper` isn't a general rule, it is a # specialized rule from inside LLVM wrapping some of its # dependencies and injecting configuration macro defines. continue else: exit(f"unexpected rule type: {rule_class}") rules[rule_name] = Rule(hdrs, srcs, deps, outs) return rules def map_headers( header_to_rule_map: dict[str, RuleChoice], rules: dict[str, Rule] ) -> None: """Accumulates headers provided by rules into the map. The map maps header paths to rule names. """ for rule_name, rule in rules.items(): repo, _, path = rule_name.partition("//") use_system_include = False if repo in EXTERNAL_REPOS: use_system_include = EXTERNAL_REPOS[repo].use_system_include for header in rule.hdrs: if header in header_to_rule_map: header_to_rule_map[header].rules.add(rule_name) if ( use_system_include != header_to_rule_map[header].use_system_include ): exit( "Unexpected use_system_include inconsistency in " f"{header_to_rule_map[header]}" ) else: header_to_rule_map[header] = RuleChoice( use_system_include, {rule_name} ) def get_missing_deps( header_to_rule_map: dict[str, RuleChoice], generated_files: set[str], rule: Rule, ) -> tuple[set[str], bool]: """Returns missing dependencies for the rule. On return, the set is dependency labels that should be added; the bool indicates whether some where omitted due to ambiguity. """ missing_deps: set[str] = set() ambiguous = False rule_files = rule.hdrs.union(rule.srcs) for source_file in rule_files: if source_file in generated_files: continue if IGNORE_SOURCE_FILE_REGEX.match(source_file): continue with open(source_file, "r") as f: file_content = f.read() file_content_changed = False for header_groups in re.findall( r'^(#include (?:(["<])([^">]+)[">]))', file_content, re.MULTILINE, ): full_include, include_open, header = header_groups is_system_include = include_open == "<" if header in rule_files: continue if header not in header_to_rule_map: if is_system_include: # Don't error for unexpected system includes. continue exit( f"Missing rule for " f"'{full_include}' in '{source_file}'" ) rule_choice = header_to_rule_map[header] if not rule_choice.rules.intersection(rule.deps): if len(rule_choice.rules) > 1: print( f"Ambiguous dependency choice for " f"'{full_include}' in '{source_file}': " f"{', '.join(rule_choice.rules)}" ) ambiguous = True # Use the single dep without removing it. missing_deps.add(next(iter(rule_choice.rules))) # If the include style should change, update file content. if is_system_include != rule_choice.use_system_include: if rule_choice.use_system_include: new_include = f"#include <{header}>" else: new_include = f'#include "{header}"' print( f"Fixing include format in '{source_file}': " f"'{full_include}' to '{new_include}'" ) file_content = file_content.replace(full_include, new_include) file_content_changed = True if file_content_changed: with open(source_file, "w") as f: f.write(file_content) return missing_deps, ambiguous def main() -> None: scripts_utils.chdir_repo_root() bazel = scripts_utils.locate_bazel() print("Querying bazel for Carbon targets...") carbon_rules = get_rules(bazel, "//...", False) print("Querying bazel for external targets...") external_repo_query = " ".join( [f"{repo}//{EXTERNAL_REPOS[repo].target}" for repo in EXTERNAL_REPOS] ) external_rules = get_rules(bazel, external_repo_query, True) print("Building header map...") header_to_rule_map: dict[str, RuleChoice] = {} map_headers(header_to_rule_map, carbon_rules) map_headers(header_to_rule_map, external_rules) print("Building generated file list...") generated_files: set[str] = set() for rule in carbon_rules.values(): generated_files = generated_files.union(rule.outs) print("Parsing headers from source files...") all_missing_deps: list[tuple[str, set[str]]] = [] any_ambiguous = False for rule_name, rule in carbon_rules.items(): missing_deps, ambiguous = get_missing_deps( header_to_rule_map, generated_files, rule ) if missing_deps: all_missing_deps.append((rule_name, missing_deps)) if ambiguous: any_ambiguous = True if any_ambiguous: exit("Stopping due to ambiguous dependency choices.") if all_missing_deps: print("Checking buildozer availability...") buildozer = scripts_utils.get_release(scripts_utils.Release.BUILDOZER) print("Fixing dependencies...") SEPARATOR = "\n- " for rule_name, missing_deps in sorted(all_missing_deps): friendly_missing_deps = SEPARATOR.join(missing_deps) print( f"Adding deps to {rule_name}:{SEPARATOR}{friendly_missing_deps}" ) args = [ buildozer, f"add deps {' '.join(missing_deps)}", rule_name, ] subprocess.check_call(args) print("Done!") if __name__ == "__main__": main() ================================================ FILE: scripts/forbid_llvm_googletest.py ================================================ #!/usr/bin/env python3 """Detects and prevents dependencies on LLVM's googletest. Carbon uses googletest directly, and it's a significantly more recent version than is provided by LLVM. Using both versions in the same binary leads to problems, so this detects dependencies. We also have some dependency checking at //bazel/check_deps. This is a separate script because check_deps relies on being able to validate specific binaries which change infrequently, whereas this effectively monitors all cc_test rules, the set of which is expected to be altered more often. """ __copyright__ = """ Part of the Carbon Language project, under the Apache License v2.0 with LLVM Exceptions. See /LICENSE for license information. SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception """ import subprocess import scripts_utils _MESSAGE = """\ Dependencies on @llvm-project//llvm:gtest are forbidden, but a dependency path was detected: %s Carbon uses GoogleTest through @googletest, which is a different version than LLVM uses at @llvm-project//llvm:gtest. As a consequence, dependencies on @llvm-project//llvm:gtest must be avoided. """ def main() -> None: scripts_utils.chdir_repo_root() args = [ scripts_utils.locate_bazel(), "query", "somepath(" # tree_sitter is excluded here because it causes the query to failure on # `@platforms`. + "//... except //utils/tree_sitter/..., " + "@llvm-project//third-party/unittest:gtest)", ] p = subprocess.run( args, stdout=subprocess.PIPE, stderr=subprocess.PIPE, encoding="utf-8" ) if p.returncode != 0: print(p.stderr) exit(f"bazel query returned {p.returncode}") if p.stdout: exit(_MESSAGE % p.stdout) print("Done!") if __name__ == "__main__": main() ================================================ FILE: scripts/lldbinit.py ================================================ #!/usr/bin/env python3 """Initialization for lldb.""" __copyright__ = """ Part of the Carbon Language project, under the Apache License v2.0 with LLVM Exceptions. See /LICENSE for license information. SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception """ # This script is only meant to be used from LLDB. import lldb # type: ignore import os import re from typing import Any project_root = os.path.dirname(os.path.realpath(__file__)) ci = lldb.debugger.GetCommandInterpreter() result = lldb.SBCommandReturnObject() def RunCommand(cmd: str, print_command: bool = True) -> Any: """Runs a command and prints it to the console to show that it ran.""" if print_command: print(f"(lldb) {cmd}") ci.HandleCommand(cmd, result) return result.GetOutput() RunCommand(f"settings append target.source-map . {project_root}") RunCommand(f"settings append target.source-map /proc/self/cwd {project_root}") # Matches the output of `print Dump(...)` and captures the stuff from inside the # std::string while discarding the std::string type. dump_re = re.compile(r'\(std::string\) "([\s\S]+)"', re.MULTILINE) # A helper to ease calling the Dump() free functions. def cmd_dump(debugger: Any, command: Any, result: Any, dict: Any) -> None: def print_usage() -> None: print(""" Dumps the value of an associated ID, using the C++ Dump() functions. Usage: dump [|-- || ] Args: CONTEXT is the dump context, such a SemIR::Context reference, a SemIR::File, a Parse::Context, or a Lex::TokenizeBuffer. EXPR is a C++ expression such as a variable name. Use `--` to prevent it from being treated as a TYPE and ID. TYPE can be `inst`, `constant`, `generic`, `impl`, `entity_name`, etc. See the `Label` string in `IdBase` classes to find possible TYPE names, though only Id types that have a matching `Make...Id()` function are supported. ID is an integer number, such as `42`, in hex, such as in `inst6000000A`. It can come with a `0x` prefix, allowing easier copy-paste from raw printed hex values (such as via the `p/x` lldb command). Example usage: # Dumps the `inst_id` local variable, with a `context` local variable. dump context inst_id # Dumps the instruction with id 42, with a `context()` method for accessing # the `Check::Context&`. dump context() inst42 """) args = command.split(" ") if len(args) < 2: print_usage() return context = args[0] # The set of "Make" functions in dump.cpp. id_types = { "class": "SemIR::MakeClassId", "constant": "SemIR::MakeConstantId", "constraint": "SemIR::MakeNamedConstraintId", "symbolic_constant": "SemIR::MakeSymbolicConstantId", "entity_name": "SemIR::MakeEntityNameId", "facet_type": "SemIR::MakeFacetTypeId", "function": "SemIR::MakeFunctionId", "generic": "SemIR::MakeGenericId", "impl": "SemIR::MakeImplId", "inst_block": "SemIR::MakeInstBlockId", "inst": "SemIR::MakeInstId", "interface": "SemIR::MakeInterfaceId", "name": "SemIR::MakeNameId", "name_scope": "SemIR::MakeNameScopeId", "identified_facet_type": "SemIR::MakeIdentifiedFacetTypeId", "require_block": "SemIR::MakeRequireImplsBlockId", "require": "SemIR::MakeRequireImplsId", "specific": "SemIR::MakeSpecificId", "specific_interface": "SemIR::MakeSpecificInterfaceId", "struct_type_fields": "SemIR::MakeStructTypeFieldsId", "type": "SemIR::MakeTypeId", } def print_dump(context: str, expr: str) -> None: cmd = f"p Dump({context}, {expr})" out = RunCommand(cmd, print_command=False) if m := re.match(dump_re, out): # Use the `dump_re` match to print just the interesting part of the # dump output. print(m[1]) else: # Unexpected output, show the command that was run. print(f"(lldb) {cmd}") print(out) # Try to find a type + id from the input args. If not, the id will be passed # through directly to C++, as it can be a variable name. found_id_type = False # Look for as a single argument. if m := re.fullmatch("([a-z_]+)(?:0x)?([0-9A-Fa-f]+)", args[1]): if m[1] in id_types: if len(args) != 2: print_usage() return make_id_fn = id_types[m[1]] id = int(m[2], 16) print_dump(context, f"{make_id_fn}({id})") found_id_type = True # Look for as two arguments. if args[1] in id_types: if len(args) != 3: print_usage() return if m := re.fullmatch("(?:0x)?([0-9A-Fa-f]+)", args[2]): make_id_fn = id_types[args[1]] id = int(m[1], 16) print_dump(context, f"{make_id_fn}({id})") found_id_type = True if not found_id_type: # Use `--` to escape a variable name like `inst22`. if args[1] == "--": expr = " ".join(args[2:]) else: expr = " ".join(args[1:]) print_dump(context, expr) def __lldb_init_module(debugger: Any, internal_dict: Any) -> None: RunCommand("command script add -f lldbinit.cmd_dump dump") ================================================ FILE: scripts/no_op_test.py ================================================ #!/usr/bin/env python3 """No-op test that should always pass. This is designed to have the fewest avoidable dependencies to make no-op build and test runs in CI as inexpensive as possible. """ __copyright__ = """ Part of the Carbon Language project, under the Apache License v2.0 with LLVM Exceptions. See /LICENSE for license information. SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception """ ================================================ FILE: scripts/query_module_versions.py ================================================ #!/usr/bin/env python3 """Queries latest module versions from MODULE.bazel.""" __copyright__ = """ Part of the Carbon Language project, under the Apache License v2.0 with LLVM Exceptions. See /LICENSE for license information. SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception """ import json import re import subprocess import urllib.error import urllib.request def _query_bazel_deps(module_text: str) -> None: """Query BCR for `bazel_dep` rule versions.""" bazel_dep_re = re.compile( r'bazel_dep\([^)]*name\s*=\s*"([^"]+)"[^)]*version\s*=' ) packages = sorted([m[1] for m in bazel_dep_re.finditer(module_text)]) print("- BCR:") for pkg in packages: try: url = f"https://bcr.bazel.build/modules/{pkg}/metadata.json" with urllib.request.urlopen(url) as response: data = json.loads(response.read().decode()) versions = data.get("versions", []) print(f" - {pkg}: {versions[-1] if versions else 'Unknown'}") except Exception as e: print(f" - {pkg}: Error {e}") def _query_git_overrides(module_text: str) -> None: """Query GitHub for `git_override` rule versions.""" git_re = re.compile( r"git_override\(\s*" r'module_name\s*=\s*"([^"]+)".*?' r'remote\s*=\s*"([^"]+)"', re.DOTALL, ) git_repos = sorted([(m[1], m[2]) for m in git_re.finditer(module_text)]) print("- Git HEAD:") for pkg, url in git_repos: try: output = subprocess.check_output( ["git", "ls-remote", url, "HEAD"], text=True ) commit = output.split()[0] print(f" - {pkg}: {commit}") except Exception as e: print(f" - {pkg}: Error {e}") def main() -> None: with open("MODULE.bazel", "r") as f: module_text = f.read() _query_bazel_deps(module_text) _query_git_overrides(module_text) if __name__ == "__main__": main() ================================================ FILE: scripts/run_bazel.py ================================================ #!/usr/bin/env python3 """Runs bazel on arguments. This is provided for other scripts to run bazel without requiring it be manually installed. """ __copyright__ = """ Part of the Carbon Language project, under the Apache License v2.0 with LLVM Exceptions. See /LICENSE for license information. SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception """ import argparse import subprocess import sys import time import scripts_utils def main() -> None: parser = argparse.ArgumentParser(description="Runs bazel.") parser.add_argument( "--attempts", metavar="COUNT", type=int, default=1, help="The number of attempts to execute the command, automatically " "retrying errors that may be transient.", ) parser.add_argument( "--jobs-on-last-attempt", metavar="COUNT", type=int, help="Sets the number of jobs in user.bazelrc on the last attempt. If " "there is only one attempt, this will be set immediately.", ) parser.add_argument( "--retry-all-errors", action="store_true", help="Retries permanent errors in addition to transient.", ) script_args, bazel_args = parser.parse_known_args() bazel = scripts_utils.locate_bazel() attempt = 0 while True: attempt += 1 if attempt == script_args.attempts and script_args.jobs_on_last_attempt: with open("user.bazelrc", "a") as bazelrc: bazelrc.write( f"build --jobs={script_args.jobs_on_last_attempt}\n" ) p = subprocess.run([bazel] + bazel_args) # If this was the last attempt, or it succeeded, we're done. if attempt == script_args.attempts or p.returncode == 0: exit(p.returncode) # Several error codes are reliably permanent, break immediately. # `1` -- The build failed. # `2` -- Command line or environment problem. # `3` -- Tests failed or timed out, we don't retry at this layer # on execution timeout. # `4` -- Test command but no tests found. # `8` -- Explicitly interrupted build. # # Note that `36` is documented as "likely permanent", but we retry # it as most of our transient failures actually produce that error # code. perm_error = (1, 2, 3, 4, 8) if not script_args.retry_all_errors and p.returncode in perm_error: exit(p.returncode) print( f"Retrying exit code {p.returncode} because it may be transient..." ) # Also sleep a bit to try to skip over transient machine load. time.sleep(attempt) if __name__ == "__main__": try: main() except KeyboardInterrupt: sys.exit(1) ================================================ FILE: scripts/run_bazelisk.py ================================================ #!/usr/bin/env python3 """Runs bazelisk with arbitrary arguments.""" __copyright__ = """ Part of the Carbon Language project, under the Apache License v2.0 with LLVM Exceptions. See /LICENSE for license information. SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception """ import os import sys import scripts_utils def main() -> None: bazelisk = scripts_utils.get_release(scripts_utils.Release.BAZELISK) os.execv(bazelisk, [bazelisk] + sys.argv[1:]) if __name__ == "__main__": main() ================================================ FILE: scripts/run_buildifier.py ================================================ #!/usr/bin/env python3 """Runs buildifier on passed-in BUILD files, mainly for pre-commit.""" __copyright__ = """ Part of the Carbon Language project, under the Apache License v2.0 with LLVM Exceptions. See /LICENSE for license information. SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception """ import os import sys import scripts_utils def main() -> None: buildifier = scripts_utils.get_release(scripts_utils.Release.BUILDIFIER) os.execv(buildifier, [buildifier] + sys.argv[1:]) if __name__ == "__main__": main() ================================================ FILE: scripts/run_buildozer.py ================================================ #!/usr/bin/env python3 """Runs buildozer on arguments. This is provided for other scripts to run buildozer without requiring it be manually installed. """ __copyright__ = """ Part of the Carbon Language project, under the Apache License v2.0 with LLVM Exceptions. See /LICENSE for license information. SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception """ import os import sys import scripts_utils def main() -> None: buildozer = scripts_utils.get_release(scripts_utils.Release.BUILDOZER) os.execv(buildozer, [buildozer] + sys.argv[1:]) if __name__ == "__main__": main() ================================================ FILE: scripts/scripts_utils.py ================================================ """Utilities for scripts.""" __copyright__ = """ Part of the Carbon Language project, under the Apache License v2.0 with LLVM Exceptions. See /LICENSE for license information. SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception """ from enum import Enum import fcntl import hashlib import os from pathlib import Path import platform import shutil import tempfile import time from typing import NamedTuple, Optional import urllib.request # The tools we track releases for. class Release(Enum): BAZELISK = "bazelisk" BUILDIFIER = "buildifier" BUILDOZER = "buildozer" TARGET_DETERMINATOR = "target-determinator" class ReleaseInfo(NamedTuple): # The base URL for downloads. Should include the version. url: str # The separator in a binary's name, either `-` or `.`. separator: str _BAZEL_TOOLS_URL = ( "https://github.com/bazelbuild/buildtools/releases/download/v8.5.1/" ) # Structured information per release tool. _RELEASES = { Release.BAZELISK: ReleaseInfo( "https://github.com/bazelbuild/bazelisk/releases/download/v1.28.1/", "-" ), Release.BUILDIFIER: ReleaseInfo(_BAZEL_TOOLS_URL, "-"), Release.BUILDOZER: ReleaseInfo(_BAZEL_TOOLS_URL, "-"), Release.TARGET_DETERMINATOR: ReleaseInfo( "https://github.com/bazel-contrib/target-determinator/releases/download/v0.32.0/", # noqa: E501 ".", ), } # Shas for the tools. # # To update, change the version in a tool's URL and use # `calculate_release_shas.py`. This is maintained separate from _RELEASES just # to make copy-paste updates simpler. _RELEASE_SHAS = { Release.BAZELISK: { "darwin-amd64": "023225736cea5dc88f2b0807d5b1af4eb0f69a4ed45e3994b2c18c263bc80e48", # noqa: E501 "darwin-arm64": "dea3f3f5de2dbc5e269e0132cdd369d5efe738f7b973d5d4eb2b4f7055a97b39", # noqa: E501 "linux-amd64": "22e7d3a188699982f661cf4687137ee52d1f24fec1ec893d91a6c4d791a75de8", # noqa: E501 "linux-arm64": "8ded44b58a0d9425a4178af26cf17693feac3b87bdcfef0a2a0898fcd1afc9f2", # noqa: E501 "windows-amd64.exe": "b9d65a1f7c2d7af885a96a4fd5aa36b40fb41816d30944390569eef908bdc954", # noqa: E501 }, Release.BUILDIFIER: { "darwin-amd64": "31de189e1a3fe53aa9e8c8f74a0309c325274ad19793393919e1ca65163ca1a4", # noqa: E501 "darwin-arm64": "62836a9667fa0db309b0d91e840f0a3f2813a9c8ea3e44b9cd58187c90bc88ba", # noqa: E501 "linux-amd64": "887377fc64d23a850f4d18a077b5db05b19913f4b99b270d193f3c7334b5a9a7", # noqa: E501 "linux-arm64": "947bf6700d708026b2057b09bea09abbc3cafc15d9ecea35bb3885c4b09ccd04", # noqa: E501 "windows-amd64.exe": "f4ecb9c73de2bc38b845d4ee27668f6248c4813a6647db4b4931a7556052e4e1", # noqa: E501 }, Release.BUILDOZER: { "darwin-amd64": "b85b9ad59c1543999a5d8bc8bee6e42b9f025be3ff520bc2d090213698850b43", # noqa: E501 "darwin-arm64": "d0cf2f6e11031d62bfd4584e46eb6bb708a883ff948be76538b34b83de833262", # noqa: E501 "linux-amd64": "2b745ca2ad41f1e01673fb59ac50af6b45ca26105c1d20fad64c3d05a95522f5", # noqa: E501 "linux-arm64": "87ee1d2d81d08ccae8f9147fc58503967c85878279e892f2990912412feef1a1", # noqa: E501 "windows-amd64.exe": "e177155c2c8ef41569791de34f13077cefe3e5623f9f02e099347232bc028901", # noqa: E501 }, Release.TARGET_DETERMINATOR: { "darwin.amd64": "289c61f8f4553a29d6ad2fbf1779a83180e7504c278196851becfd3f4163f6f4", # noqa: E501 "darwin.arm64": "6e688292b43f99f7b76d0af0fc32ac1eec8c110571a323c7e30bcfc9ac41275c", # noqa: E501 "linux.amd64": "792ed4c6f53aad60e8255686c272f28d3c039eaaef03518dbbde33519713802a", # noqa: E501 "linux.arm64": "339d0b7d3c72734f435d67e9b66936261c58c02be4ce64bdb76c85f1683e8084", # noqa: E501 "windows.amd64.exe": "457aba640e737edaf06d3922fa33914109dbe6382af951dfe6a99dbdf50c714c", # noqa: E501 }, } def chdir_repo_root() -> None: """Change the working directory to the repository root. This is done so that scripts run from a consistent directory. """ os.chdir(Path(__file__).parents[1]) def _get_hash(file: Path) -> str: """Returns the sha256 of a file.""" digest = hashlib.sha256() with file.open("rb") as f: while True: chunk = f.read(1024 * 64) if not chunk: break digest.update(chunk) return digest.hexdigest() def _download(url: str, local_path: Path) -> Optional[int]: """Downloads the URL to the path. Returns an HTTP error code on failure.""" with urllib.request.urlopen(url) as response: if response.code != 200: return int(response.code) with local_path.open("wb") as f: shutil.copyfileobj(response, f) return None def _get_cached_binary(name: str, url: str, want_hash: str) -> str: """Returns the path to the cached binary. If the matching version is already cached, returns it. Otherwise, downloads from the URL and verifies the hash matches. """ cache_dir = Path.home().joinpath(".cache", "carbon-lang-scripts") cache_dir.mkdir(parents=True, exist_ok=True) # Hold a lock while checksumming and downloading the path. Otherwise, # parallel runs by pre-commit may conflict with one another with # simultaneous downloads. with open(cache_dir.joinpath(f"{name}.lock"), "w") as lock_file: fcntl.lockf(lock_file.fileno(), fcntl.LOCK_EX) # Check if there's a cached file that can be used. local_path = cache_dir.joinpath(name) if local_path.is_file() and want_hash == _get_hash(local_path): return str(local_path) # Download the file. retries = 5 while True: err = _download(url, local_path) if err is None: break retries -= 1 if retries == 0: exit(f"Failed to download {url}: HTTP {err}.") time.sleep(1) local_path.chmod(0o755) # Verify the downloaded hash. found_hash = _get_hash(local_path) if want_hash != found_hash: exit( f"Downloaded {url} but found sha256 " f"{found_hash} ({local_path.stat().st_size} bytes), wanted " f"{want_hash}" ) return str(local_path) def _get_machine() -> str: machine = platform.machine() if machine == "x86_64": machine = "amd64" elif machine == "aarch64": machine = "arm64" return machine def _get_platform_ext() -> str: if platform.system() == "Windows": return ".exe" else: return "" def _select_hash(hashes: dict[str, str], version: str) -> str: # Ensure the platform version is supported and has a hash. if version not in hashes: # If this because a platform support issue, we may need to print errors. exit(f"No release available for platform: {version}") return hashes[version] def get_release(release: Release) -> str: """Install a tool to carbon-lang's cache and return its path. release: The release to cache. """ info = _RELEASES[release] shas = _RELEASE_SHAS[release] # Translate platform information into Bazel's release form. ext = _get_platform_ext() platform_label = ( f"{platform.system().lower()}{info.separator}{_get_machine()}{ext}" ) url = f"{info.url}/{release.value}{info.separator}{platform_label}" want_hash = _select_hash(shas, platform_label) return _get_cached_binary(f"{release.value}{ext}", url, want_hash) def calculate_release_shas() -> None: """Prints sha information for tracked tool releases.""" print("_RELEASE_SHAS = {") for release, info in _RELEASES.items(): shas = _RELEASE_SHAS[release] print(f" {release}: {{") for platform_label in shas.keys(): url = f"{info.url}/{release.value}{info.separator}{platform_label}" with tempfile.NamedTemporaryFile() as f: path = Path(f.name) _download(url, path) hash = _get_hash(path) print(f' "{platform_label}": "{hash}", # noqa: E501') print(" },") print("}") def locate_bazel() -> str: """Returns the bazel command. In order, try: 1. The `BAZEL` environment variable. 2. `bazelisk` 3. `bazel` 4. `run_bazelisk.py` """ bazel = os.environ.get("BAZEL") if bazel: return bazel for cmd in ("bazelisk", "bazel"): target = shutil.which(cmd) if target: return target return str(Path(__file__).parent / "run_bazelisk.py") ================================================ FILE: scripts/source_stats.py ================================================ #!/usr/bin/env python3 """Script to compute statistics about source code.""" from __future__ import annotations __copyright__ = """ Part of the Carbon Language project, under the Apache License v2.0 with LLVM Exceptions. See /LICENSE for license information. SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception """ import argparse from alive_progress import alive_bar # type: ignore import math from multiprocessing import Pool import re import termplotlib as tpl # type: ignore from pathlib import Path from typing import Optional from dataclasses import dataclass, field, asdict from collections import Counter BLANK_RE = re.compile(r"\s*") COMMENT_RE = re.compile(r"\s*///*\s*") LINE_RE = re.compile( r""" (?P\b(class|struct)\s+(?P\w+)\b)| (?P{\s*(?P//.*)?)| (?P//.*)| (?P/\*.*\*/)| (?P"([^"]|\\")*"|'([^']|\\')*')| (?P\b(0[xb][0-9a-fA-F']*|[0-9][0-9']*)\.[0-9a-fA-F']*([eEpP][0-9a-fA-F']*)?)| (?P\b(0[xb][0-9a-fA-F']+|[0-9][0-9']*)([eEpP][0-9a-fA-F']*)?)| (?P[\[\]{}(),.;]|[-+=!@#$%^&*/?|<>]+)| (?P\b(auto|bool|break|case|catch|char|class|const|continue|default|do|double|else|enum|explicit|extern|false|float|for|friend|goto|if|inline|int|long|mutable|namespace|new|nullptr|operator|private|protected|public|return|short|signed|sizeof|static|struct|switch|template|this|throw|true|try|typedef|union|unsigned|using|virtual|void|while)\b)| (?P\b\w+\b) """, re.X, ) @dataclass class Stats: """Stats collected while scanning source files""" lines: int = 0 blank_lines: int = 0 comment_lines: int = 0 empty_comment_lines: int = 0 comment_line_widths: Counter[int] = field(default_factory=lambda: Counter()) lines_with_trailing_comments: int = 0 classes: int = 0 internal_comments: int = 0 string_literals: int = 0 string_literals_per_line: Counter[int] = field( default_factory=lambda: Counter() ) int_literals: int = 0 int_literals_per_line: Counter[int] = field( default_factory=lambda: Counter() ) float_literals: int = 0 float_literals_per_line: Counter[int] = field( default_factory=lambda: Counter() ) symbols: int = 0 symbols_per_line: Counter[int] = field(default_factory=lambda: Counter()) keywords: int = 0 keywords_per_line: Counter[int] = field(default_factory=lambda: Counter()) identifiers: int = 0 identifier_widths: Counter[int] = field(default_factory=lambda: Counter()) ids_per_line: Counter[int] = field(default_factory=lambda: Counter()) unique_ids_per_ten_lines: Counter[int] = field( default_factory=lambda: Counter() ) def accumulate(self, other: Stats) -> None: self.lines += other.lines self.blank_lines += other.blank_lines self.empty_comment_lines += other.empty_comment_lines self.comment_lines += other.comment_lines self.comment_line_widths.update(other.comment_line_widths) self.lines_with_trailing_comments += other.lines_with_trailing_comments self.classes += other.classes self.internal_comments += other.internal_comments self.string_literals += other.string_literals self.string_literals_per_line.update(other.string_literals_per_line) self.int_literals += other.int_literals self.int_literals_per_line.update(other.int_literals_per_line) self.float_literals += other.float_literals self.float_literals_per_line.update(other.float_literals_per_line) self.symbols += other.symbols self.symbols_per_line.update(other.symbols_per_line) self.keywords += other.keywords self.keywords_per_line.update(other.keywords_per_line) self.identifiers += other.identifiers self.identifier_widths.update(other.identifier_widths) self.ids_per_line.update(other.ids_per_line) self.unique_ids_per_ten_lines.update(other.unique_ids_per_ten_lines) def scan_file(file: Path) -> Stats: """Scans the provided file and accumulates stats.""" stats = Stats() unique_ids = set() for line in file.open(): # Strip off the line endings. line = line.rstrip("\r\n") # Skip over super long lines that are often URLs or structured data that # doesn't match "normal" source code patterns. if len(line) > 80: continue stats.lines += 1 if re.fullmatch(BLANK_RE, line): stats.blank_lines += 1 continue if m := re.match(COMMENT_RE, line): stats.comment_lines += 1 if m.end() == len(line): stats.empty_comment_lines += 1 else: stats.comment_line_widths[len(line)] += 1 continue line_string_literals = 0 line_int_literals = 0 line_float_literals = 0 line_symbols = 0 line_keywords = 0 line_identifiers = 0 for m in re.finditer(LINE_RE, line): if m.group("trailing_comment"): stats.lines_with_trailing_comments += 1 break if m.group("class_intro"): stats.classes += 1 line_keywords += 1 line_identifiers += 1 stats.identifier_widths[len(m.group("class_name"))] += 1 elif m.group("end_open_curly"): line_symbols += 1 elif m.group("internal_comment"): stats.internal_comments += 1 elif m.group("string_literal"): line_string_literals += 1 elif m.group("int_literal"): line_int_literals += 1 elif m.group("float_literal"): line_float_literals += 1 elif m.group("symbol"): line_symbols += 1 elif m.group("keyword"): line_keywords += 1 else: assert m.group("id"), "Line is '%s', and match is '%s'" % ( line, line[m.start() : m.end()], ) line_identifiers += 1 stats.identifier_widths[len(m.group("id"))] += 1 unique_ids.add(m.group("id")) stats.string_literals += line_string_literals stats.string_literals_per_line[line_string_literals] += 1 stats.int_literals += line_int_literals stats.int_literals_per_line[line_int_literals] += 1 stats.float_literals += line_float_literals stats.float_literals_per_line[line_float_literals] += 1 stats.symbols += line_symbols stats.symbols_per_line[line_symbols] += 1 stats.keywords += line_keywords stats.keywords_per_line[line_keywords] += 1 stats.identifiers += line_identifiers stats.ids_per_line[line_identifiers] += 1 if stats.lines > 0: stats.unique_ids_per_ten_lines[ math.ceil((len(unique_ids) * 10) / stats.lines) ] += 1 return stats def parse_args(args: Optional[list[str]] = None) -> argparse.Namespace: """Parsers command-line arguments and flags.""" parser = argparse.ArgumentParser(description=__doc__) parser.add_argument( "files", metavar="FILE", type=Path, nargs="+", help="A file to scan while collecting statistics.", ) return parser.parse_args(args=args) def main() -> None: parsed_args = parse_args() stats = Stats() with alive_bar(len(parsed_args.files)) as bar: with Pool() as p: for file_stats in p.imap_unordered(scan_file, parsed_args.files): stats.accumulate(file_stats) bar() print(""" ## Stats ## Lines: %(lines)d Blank lines: %(blank_lines)d Comment lines: %(comment_lines)d Empty comment lines: %(empty_comment_lines)d Lines with trailing comments: %(lines_with_trailing_comments)d Classes: %(classes)d Internal comments: %(internal_comments)d String literals: %(string_literals)d Int literals: %(int_literals)d Float literals: %(float_literals)d Symbols: %(symbols)d Keywords: %(keywords)d IDs: %(identifiers)d""" % asdict(stats)) tokens = ( stats.string_literals + stats.int_literals + stats.float_literals + stats.symbols + stats.keywords + stats.identifiers ) print(f""" Fraction of blank lines: {stats.blank_lines / stats.lines} Fraction of comment lines: {stats.comment_lines / stats.lines} Total counted tokens: {tokens} Fraction string literals: {stats.string_literals / tokens} Fraction int literals: {stats.int_literals / tokens} Fraction float literals: {stats.float_literals / tokens} Fraction symbols: {stats.symbols / tokens} Fraction keywords: {stats.keywords / tokens} Fraction IDs: {stats.identifiers / tokens} """) def print_histogram( title: str, data: dict[int, int], column_format: str ) -> None: print() key_min = min(data.keys()) key_max = max(data.keys()) + 1 values = [data.get(k, 0) for k in range(key_min, key_max)] keys = [column_format % k for k in range(key_min, key_max)] total = sum(values) median = key_min p90 = key_min p95 = key_min p99 = key_min count = total for k in range(key_min, key_max): count -= data.get(k, 0) if median == key_min and count <= total / 2: median = k if p90 == key_min and count <= total / 10: p90 = k if p95 == key_min and count <= total / 20: p95 = k if p99 == key_min and count <= total / 100: p99 = k print( title + f" (median: {median}, p90: {p90}, p95: {p95}, p99: {p99})" ) fig = tpl.figure() fig.barh(values, keys) fig.show() print_histogram( "## Comment line widths ##", stats.comment_line_widths, "%d columns" ) print_histogram( "## String literals per line ##", stats.string_literals_per_line, "%d literals", ) print_histogram( "## Int literals per line ##", stats.int_literals_per_line, "%d literals", ) print_histogram( "## Float literals per line ##", stats.float_literals_per_line, "%d literals", ) print_histogram( "## Symbols per line ##", stats.symbols_per_line, "%d symbols" ) print_histogram( "## Keywords per line ##", stats.keywords_per_line, "%d keywords" ) print_histogram("## ID widths ##", stats.identifier_widths, "%d characters") print_histogram("## IDs per line ##", stats.ids_per_line, "%d ids") print_histogram( "## Unique IDs per 10 lines ##", stats.unique_ids_per_ten_lines, "%d ids", ) if __name__ == "__main__": main() ================================================ FILE: scripts/sync_repos.sh ================================================ #!/usr/bin/env bash # # Part of the Carbon Language project, under the Apache License v2.0 with LLVM # Exceptions. See /LICENSE for license information. # SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception # # Sync directories in the main Carbon repository into dedicated child # repositories to better match repository-oriented installing and tooling. set -eux ORIGIN_DIR="$PWD" COMMIT_SHA="$(git rev-parse --short $GITHUB_SHA)" COMMIT_SUMMARY="Original $(git show -s --pretty=full "$COMMIT_SHA") $(git diff --summary "${COMMIT_SHA}^!") " # Setup global git configuration. GIT_USERNAME="CarbonInfraBot" git config --global user.email "carbon-external-infra@google.com" git config --global user.name "$GIT_USERNAME" declare -A MIRRORS MIRRORS["utils/vim"]="vim-carbon-lang" for dir in "${!MIRRORS[@]}"; do SRC_DIR="$dir" DEST_REPO="${MIRRORS[$SRC_DIR]}" DEST_REPO_URL="https://$GIT_USERNAME:$API_TOKEN_GITHUB@github.com/carbon-language/$DEST_REPO.git" DEST_CLONE_DIR="$(mktemp -d)" git clone --single-branch "$DEST_REPO_URL" "$DEST_CLONE_DIR" cd "$DEST_CLONE_DIR" # Print out the destination repository to help with debugging failures in # GitHub's actions. ls -al # Remove all the existing files to rebuild it from scratch. We ignore when # this matches no files to handle freshly created repositories. Also print the # status afterward for debugging. git rm --ignore-unmatch -r . git status # Copy the basic framework from the origin repository. cp "$ORIGIN_DIR/.gitignore" \ "$ORIGIN_DIR/CODE_OF_CONDUCT.md" \ "$ORIGIN_DIR/LICENSE" \ . # Copy the mirrored directory. We use `rsync` to get a more reliable way of # handling the mirroring of the contents of a directory. We also make this # verbose to help with debugging action failures on GitHub. rsync -av "$ORIGIN_DIR/$SRC_DIR/" . # Add back all the files now, and print the status for debugging. git add -A git status # See if there is anything to commit and push. This works the same way as # diff(1) and so exits zero when there are no changes. if ! git diff --cached --quiet; then # Commit the new state. git commit -F- < None: print(s, file=sys.stderr) def filter_targets(bazel: Path, targets: str) -> str: # Need to quote targets for inclusion in another query. quoted_targets = "\n".join([f'"{t}"' for t in targets.splitlines()]) with tempfile.NamedTemporaryFile(mode="w+") as tmp: query = ( f"let t = set({quoted_targets}) in " "kind(rule, $t) except attr(tags, manual, $t)\n" ) query_lines = query.splitlines() if len(query_lines) <= 10: query_snippet = "\n".join(query_lines) else: query_snippet = "\n".join( query_lines[:5] + ["..."] + query_lines[-5:] ) log(f"Bazel query snippet:\n```\n{query_snippet}\n```") tmp.write(query) tmp.flush() try: p = subprocess.run( [str(bazel), "query", f"--query_file={tmp.name}"], stdout=subprocess.PIPE, stderr=subprocess.PIPE, check=True, encoding="utf-8", ) return p.stdout except subprocess.CalledProcessError as err: log(err.stderr) log("Full query file:\n```") with open(tmp.name) as f: log(f.read()) log("```") raise def main() -> None: parser = argparse.ArgumentParser(__doc__) parser.add_argument( "baseline", nargs=1, help="Git commit of the diff baseline." ) parser.add_argument( "args", nargs="*", help="Remaining args to forward to the underlying tool.", ) parsed_args = parser.parse_args() scripts_utils.chdir_repo_root() bazel = Path(scripts_utils.locate_bazel()) target_determinator = scripts_utils.get_release( scripts_utils.Release.TARGET_DETERMINATOR ) p = subprocess.run( [ target_determinator, f"--bazel={bazel}", parsed_args.baseline[0], ] + parsed_args.args, check=True, stdout=subprocess.PIPE, encoding="utf-8", ) targets = p.stdout if targets.strip() != "": targets = filter_targets(bazel, targets) log(f"Found {len(targets.splitlines())} impacted targets!") print(targets.rstrip()) if __name__ == "__main__": main() ================================================ FILE: scripts/workspace_status.py ================================================ #!/usr/bin/env python3 """Bazel `--workspace_status_command` script. This script is designed to be used in Bazel`s `--workspace_status_command` and generate any desirable status artifacts. """ __copyright__ = """ Part of the Carbon Language project, under the Apache License v2.0 with LLVM Exceptions. See /LICENSE for license information. SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception """ import subprocess def git_commit_sha() -> str: return subprocess.check_output( ["git", "rev-parse", "--short", "HEAD"], encoding="utf-8" ).strip() def git_dirty_suffix() -> str: status = subprocess.check_output( ["git", "status", "--porcelain"], encoding="utf-8" ).strip() return ".dirty" if len(status) > 0 else "" def main() -> None: print("STABLE_GIT_COMMIT_SHA " + git_commit_sha()) print("STABLE_GIT_DIRTY_SUFFIX " + git_dirty_suffix()) if __name__ == "__main__": main() ================================================ FILE: setup.cfg ================================================ # Part of the Carbon Language project, under the Apache License v2.0 with LLVM # Exceptions. See /LICENSE for license information. # SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception [flake8] max-line-length = 80 # E203: This warning is not PEP 8 compliant. extend-ignore = E203 [mypy] pretty = True # Define flags relative to strict mode. strict = True # TODO: Look at updating code to fix these. disallow_any_generics = False ================================================ FILE: testing/README.md ================================================ # Testing Testing-specific libraries. ================================================ FILE: testing/base/BUILD ================================================ # Part of the Carbon Language project, under the Apache License v2.0 with LLVM # Exceptions. See /LICENSE for license information. # SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception # # Trivial, single-file testing libraries. More complex libraries should get # their own directory. load("//bazel/cc_rules:defs.bzl", "cc_binary", "cc_library", "cc_test") package(default_visibility = ["//visibility:public"]) # This does extra initialization on top of googletest's gtest_main in order to # provide stack traces on unexpected exits, because we normally rely on LLVM # code for that. # # This replaces "@googletest//:gtest_main"; # "@googletest//:gtest" should still be used directly. cc_library( name = "gtest_main", testonly = 1, srcs = ["gtest_main.cpp"], deps = [ ":global_exe_path", "//common:init_llvm", "@googletest//:gtest", "@llvm-project//llvm:Support", ], ) # This does extra initialization on top of Google benchmark's main in order to # provide stack traces and setup LLVM. # # This replaces `@google_benchmark//:benchmark_main`; # `@google_benchmark//:benchmark` should still be used directly. cc_library( name = "benchmark_main", testonly = 1, srcs = ["benchmark_main.cpp"], deps = [ ":global_exe_path", "//common:init_llvm", "@abseil-cpp//absl/flags:parse", "@google_benchmark//:benchmark", "@llvm-project//llvm:Support", ], ) cc_library( name = "capture_std_streams", testonly = 1, srcs = ["capture_std_streams.cpp"], hdrs = ["capture_std_streams.h"], deps = [ "//common:ostream", "@googletest//:gtest", ], ) cc_library( name = "source_gen_lib", testonly = 1, srcs = ["source_gen.cpp"], hdrs = ["source_gen.h"], deps = [ "//common:check", "//common:map", "//common:raw_string_ostream", "//common:set", "//toolchain/lex:token_kind", "@abseil-cpp//absl/random", "@llvm-project//llvm:Support", ], ) cc_test( name = "source_gen_test", size = "small", srcs = ["source_gen_test.cpp"], deps = [ ":global_exe_path", ":gtest_main", ":source_gen_lib", "//common:all_llvm_targets", "//common:set", "//toolchain/base:install_paths_test_helpers", "//toolchain/driver", "@googletest//:gtest", "@llvm-project//llvm:Support", ], ) cc_binary( name = "source_gen", testonly = 1, srcs = ["source_gen_main.cpp"], deps = [ ":source_gen_lib", "//common:bazel_working_dir", "//common:command_line", "//common:init_llvm", "//common:ostream", "@llvm-project//llvm:Support", ], ) cc_library( name = "file_helpers", testonly = 1, srcs = ["file_helpers.cpp"], hdrs = ["file_helpers.h"], deps = [ "//common:error", "@googletest//:gtest", ], ) cc_library( name = "global_exe_path", testonly = 1, srcs = ["global_exe_path.cpp"], hdrs = ["global_exe_path.h"], deps = [ "//common:check", "//common:exe_path", "@llvm-project//llvm:Support", ], ) cc_test( name = "global_exe_path_test", size = "small", srcs = ["global_exe_path_test.cpp"], deps = [ ":global_exe_path", ":gtest_main", "@googletest//:gtest", "@llvm-project//llvm:Support", ], ) cc_library( name = "unified_diff_matcher", testonly = 1, hdrs = ["unified_diff_matcher.h"], deps = [ "//common:check", "@googletest//:gtest", "@llvm-project//llvm:Support", ], ) cc_test( name = "unified_diff_matcher_test", size = "small", srcs = ["unified_diff_matcher_test.cpp"], deps = [ ":gtest_main", ":unified_diff_matcher", "@googletest//:gtest", "@llvm-project//llvm:Support", ], ) ================================================ FILE: testing/base/benchmark_main.cpp ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #include #include "absl/flags/parse.h" #include "common/init_llvm.h" #include "testing/base/global_exe_path.h" auto main(int orig_argc, char** orig_argv) -> int { // Do LLVM's initialization first, this will also transform UTF-16 to UTF-8. Carbon::InitLLVM init_llvm(orig_argc, orig_argv); Carbon::Testing::SetExePath(orig_argv[0]); // Inject a flag to override the defaults for benchmarks. This can still be // disabled by user arguments. llvm::SmallVector injected_argv_storage(orig_argv, orig_argv + orig_argc + 1); char injected_flag[] = "--benchmark_counters_tabular"; injected_argv_storage.insert(injected_argv_storage.begin() + 1, injected_flag); char** argv = injected_argv_storage.data(); int argc = injected_argv_storage.size() - 1; benchmark::Initialize(&argc, argv); absl::ParseCommandLine(argc, argv); benchmark::RunSpecifiedBenchmarks(); benchmark::Shutdown(); return 0; } ================================================ FILE: testing/base/capture_std_streams.cpp ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #include "testing/base/capture_std_streams.h" #include #include #include #include #include "common/ostream.h" namespace Carbon::Testing::Internal { // While these are marked as "internal" APIs, they seem to work and be pretty // widely used for their exact documented behavior. using ::testing::internal::CaptureStderr; using ::testing::internal::CaptureStdout; using ::testing::internal::GetCapturedStderr; using ::testing::internal::GetCapturedStdout; auto BeginStdStreamCapture() -> void { CaptureStderr(); CaptureStdout(); } auto EndStdStreamCapture(std::string& out, std::string& err) -> void { // No need to flush stderr. err = GetCapturedStderr(); llvm::outs().flush(); out = GetCapturedStdout(); } } // namespace Carbon::Testing::Internal ================================================ FILE: testing/base/capture_std_streams.h ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #ifndef CARBON_TESTING_BASE_CAPTURE_STD_STREAMS_H_ #define CARBON_TESTING_BASE_CAPTURE_STD_STREAMS_H_ #include namespace Carbon::Testing { // Implementation details. namespace Internal { auto BeginStdStreamCapture() -> void; auto EndStdStreamCapture(std::string& out, std::string& err) -> void; } // namespace Internal // Calls the provided function while capturing both `stdout` and `stderr`. The // `out` and `err` strings are set to whatever is captured after the function // returns. // // Note that any output that is captured will not be visible when running, which // can make debugging tests that use this routine difficult. Make sure to either // print back out or otherwise expose any of the contents of the captured output // that are needed when debugging. template static auto CallWithCapturedOutput(std::string& out, std::string& err, FnT function) -> auto { Internal::BeginStdStreamCapture(); auto result = function(); Internal::EndStdStreamCapture(out, err); return result; } } // namespace Carbon::Testing #endif // CARBON_TESTING_BASE_CAPTURE_STD_STREAMS_H_ ================================================ FILE: testing/base/file_helpers.cpp ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #include "testing/base/file_helpers.h" #include #include #include #include #include #include namespace Carbon::Testing { auto GetTempDirectory() -> std::filesystem::path { if (char* tmpdir_env = getenv("TEST_TMPDIR"); tmpdir_env != nullptr) { return tmpdir_env; } else { return std::filesystem::temp_directory_path(); } } auto ReadFile(std::filesystem::path path) -> ErrorOr { std::ifstream file_stream(path); if (file_stream.fail()) { return Error(llvm::formatv("Error opening file: {0}", path)); } std::stringstream buffer; buffer << file_stream.rdbuf(); if (file_stream.fail()) { return Error(llvm::formatv("Error reading file: {0}", path)); } return buffer.str(); } auto WriteTestFile(llvm::StringRef name, llvm::StringRef contents) -> ErrorOr { std::filesystem::path test_tmpdir = GetTempDirectory(); const auto* unit_test = ::testing::UnitTest::GetInstance(); const auto* test_info = unit_test->current_test_info(); std::filesystem::path test_file = test_tmpdir / llvm::formatv("{0}_{1}_{2}", test_info->test_suite_name(), test_info->name(), name) .str(); // Make debugging a bit easier by cleaning up any files from previous runs. // This is only necessary when not run in Bazel's test environment. std::filesystem::remove(test_file); if (std::filesystem::exists(test_file)) { return Error( llvm::formatv("Unable to remove an existing file: {0}", test_file)); } std::error_code ec; llvm::raw_fd_ostream test_file_stream(test_file.string(), ec); if (ec) { return Error(llvm::formatv("Test file error: {0}", ec.message())); } test_file_stream << contents; return test_file; } } // namespace Carbon::Testing ================================================ FILE: testing/base/file_helpers.h ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #ifndef CARBON_TESTING_BASE_FILE_HELPERS_H_ #define CARBON_TESTING_BASE_FILE_HELPERS_H_ #include #include #include "common/error.h" namespace Carbon::Testing { // Returns a directory that should be used to hold temporary files created by // test execution. auto GetTempDirectory() -> std::filesystem::path; // Reads a file to string. auto ReadFile(std::filesystem::path path) -> ErrorOr; // Writes a test file to disk and returns an error or the full path to the file. // // Note that this expects to be run from within a GoogleTest test, and relies on // global state of GoogleTest. // // This locates a suitable temporary directory for the test, creates a file with // the requested name in that directory, and writes the provided content to that // file. The full path to the written file is returned. // // Where possible, this will use a Bazel-provided test temporary directory. // However, if unavailable, it falls back to a system temporary directory. This // helps tests be runnable outside of Bazel, for example under a debugger. It // also works to create test file names that are unlikely to conflict with other // tests when run. auto WriteTestFile(llvm::StringRef name, llvm::StringRef contents) -> ErrorOr; } // namespace Carbon::Testing #endif // CARBON_TESTING_BASE_FILE_HELPERS_H_ ================================================ FILE: testing/base/global_exe_path.cpp ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #include "testing/base/global_exe_path.h" #include #include #include "common/check.h" #include "common/exe_path.h" static constinit std::optional exe_path = {}; namespace Carbon::Testing { auto GetExePath() -> llvm::StringRef { CARBON_CHECK( exe_path, "Must not query the executable path until after it has been set!"); return *exe_path; } auto SetExePath(const char* argv_zero) -> void { CARBON_CHECK(!exe_path, "Must not call `SetExePath` more than once!"); exe_path.emplace(Carbon::FindExecutablePath(argv_zero)); } } // namespace Carbon::Testing ================================================ FILE: testing/base/global_exe_path.h ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #ifndef CARBON_TESTING_BASE_GLOBAL_EXE_PATH_H_ #define CARBON_TESTING_BASE_GLOBAL_EXE_PATH_H_ #include "llvm/ADT/StringRef.h" // When using the Carbon `main` function for GoogleTest, we export some extra // information about the test binary that can be accessed with this header. namespace Carbon::Testing { // The executable path of the test binary. auto GetExePath() -> llvm::StringRef; // Sets the executable path of a test binary from its `argv[0]`. // // This function must only be called once for an execution, and before any // callers to `GetExePath`. Typically, it is called from within `main`. auto SetExePath(const char* argv_zero) -> void; } // namespace Carbon::Testing #endif // CARBON_TESTING_BASE_GLOBAL_EXE_PATH_H_ ================================================ FILE: testing/base/global_exe_path_test.cpp ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #include "testing/base/global_exe_path.h" #include #include #include "llvm/Support/FileSystem.h" namespace Carbon::Testing { namespace { using ::testing::StrNe; TEST(TestExePathTest, Test) { llvm::StringRef exe_path = GetExePath(); EXPECT_THAT(exe_path, StrNe("")); EXPECT_TRUE(llvm::sys::fs::exists(exe_path)); EXPECT_TRUE(llvm::sys::fs::can_execute(exe_path)); } } // namespace } // namespace Carbon::Testing ================================================ FILE: testing/base/gtest_main.cpp ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #include #include "common/init_llvm.h" #include "testing/base/global_exe_path.h" auto main(int argc, char** argv) -> int { // Initialize LLVM first, as that will also handle ensuring UTF-8 encoding. Carbon::InitLLVM init_llvm(argc, argv); Carbon::Testing::SetExePath(argv[0]); testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); } ================================================ FILE: testing/base/source_gen.cpp ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #include "testing/base/source_gen.h" #include #include #include #include #include #include "common/raw_string_ostream.h" #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/Sequence.h" #include "llvm/ADT/SmallVector.h" #include "llvm/ADT/StringExtras.h" #include "llvm/Support/FormatVariadic.h" #include "toolchain/lex/token_kind.h" namespace Carbon::Testing { auto SourceGen::Global() -> SourceGen& { static SourceGen global_gen; return global_gen; } SourceGen::SourceGen(Language language) : language_(language) {} // Heuristic numbers used in synthesizing various identifier sequences. static constexpr int MinClassNameLength = 5; static constexpr int MinMemberNameLength = 4; // The shuffled state used to generate some number of classes. // // This state encodes everything used to generate class definitions. The state // will be consumed until empty. // // Detailed comments for out-of-line methods are on their definitions. class SourceGen::ClassGenState { public: ClassGenState(SourceGen& gen, int num_classes, const ClassParams& class_params, const TypeUseParams& type_use_params); auto public_function_param_counts() -> llvm::SmallVectorImpl& { return public_function_param_counts_; } auto public_method_param_counts() -> llvm::SmallVectorImpl& { return public_method_param_counts_; } auto private_function_param_counts() -> llvm::SmallVectorImpl& { return private_function_param_counts_; } auto private_method_param_counts() -> llvm::SmallVectorImpl& { return private_method_param_counts_; } auto class_names() -> llvm::SmallVectorImpl& { return class_names_; } auto member_names() -> llvm::SmallVectorImpl& { return member_names_; } auto param_names() -> llvm::SmallVectorImpl& { return param_names_; } auto type_names() -> llvm::SmallVectorImpl& { return type_names_; } auto AddValidTypeName(llvm::StringRef type_name) -> void { valid_type_names_.Insert(type_name); } auto GetValidTypeName() -> llvm::StringRef; private: auto BuildClassAndTypeNames(SourceGen& gen, int num_classes, int num_types, const TypeUseParams& type_use_params) -> void; llvm::SmallVector public_function_param_counts_; llvm::SmallVector public_method_param_counts_; llvm::SmallVector private_function_param_counts_; llvm::SmallVector private_method_param_counts_; llvm::SmallVector class_names_; llvm::SmallVector member_names_; llvm::SmallVector param_names_; llvm::SmallVector type_names_; Set valid_type_names_; int last_type_name_index_ = 0; }; // A helper to sum elements of a range. template static auto Sum(const T& range) -> int { return std::accumulate(range.begin(), range.end(), 0); } // Given a number of class definitions and the params with which to generate // them, builds the state that will be used while generating that many classes. // // We build the state first and across all the class definitions that will be // generated so that we can distribute random components across all the // definitions. SourceGen::ClassGenState::ClassGenState(SourceGen& gen, int num_classes, const ClassParams& class_params, const TypeUseParams& type_use_params) { public_function_param_counts_ = gen.GetShuffledInts(num_classes * class_params.public_function_decls, 0, class_params.public_function_decl_params.max_params); public_method_param_counts_ = gen.GetShuffledInts(num_classes * class_params.public_method_decls, 0, class_params.public_method_decl_params.max_params); private_function_param_counts_ = gen.GetShuffledInts(num_classes * class_params.private_function_decls, 0, class_params.private_function_decl_params.max_params); private_method_param_counts_ = gen.GetShuffledInts(num_classes * class_params.private_method_decls, 0, class_params.private_method_decl_params.max_params); int num_members = num_classes * (class_params.public_function_decls + class_params.public_method_decls + class_params.private_function_decls + class_params.private_method_decls + class_params.private_field_decls); member_names_ = gen.GetShuffledIdentifiers( num_members, /*min_length=*/MinMemberNameLength); int num_params = Sum(public_function_param_counts_) + Sum(public_method_param_counts_) + Sum(private_function_param_counts_) + Sum(private_method_param_counts_); param_names_ = gen.GetShuffledIdentifiers(num_params); BuildClassAndTypeNames(gen, num_classes, num_members + num_params, type_use_params); } auto SourceGen::ClassGenState::GetValidTypeName() -> llvm::StringRef { // Check that we don't completely wrap the type names by tracking where we // started. int initial_last_type_name_index = last_type_name_index_; // Now search the type names, starting from the last used index, to find the // first valid name. for (;;) { if (last_type_name_index_ == 0) { last_type_name_index_ = type_names_.size(); } --last_type_name_index_; llvm::StringRef& type_name = type_names_[last_type_name_index_]; if (valid_type_names_.Contains(type_name)) { // Found a valid type name, swap it with the back and pop that off. std::swap(type_names_.back(), type_name); return type_names_.pop_back_val(); } CARBON_CHECK(last_type_name_index_ != initial_last_type_name_index, "Failed to find a valid type name with {0} candidates, an " "initial index of {1}, and with {2} classes left to emit!", type_names_.size(), initial_last_type_name_index, class_names_.size()); } } // Build both the class names this file will declare and a list of type // references to use throughout those classes. // // We combine a list of fixed types in the `type_use_params` with the list of // class names that will be defined to form the spelling of all the referenced // types. The `type_use_params` provides weights for each fixed type as well as // an overall weight for referencing class names that are being declared. We // build a set of type references so that its histogram will roughly match these // weights. // // For each of the fixed types, `type_use_params` provides a spelling for both // Carbon and C++. // // We distribute our references to declared class names evenly to the extent // possible. // // Before all the references are formed, the class names are kept their original // unshuffled order. This ensures that any uneven sampling of names is done // deterministically. At the end, we randomly shuffle the sequences of both the // declared class names and type references to provide an unpredictable order in // the generated output. auto SourceGen::ClassGenState::BuildClassAndTypeNames( SourceGen& gen, int num_classes, int num_types, const TypeUseParams& type_use_params) -> void { // Initially get the sequence of class names without shuffling so we can // compute our type name pool from them prior to any shuffling. class_names_ = gen.GetUniqueIdentifiers(num_classes, /*min_length=*/MinClassNameLength); type_names_.reserve(num_types); // Compute the sum of weights and pre-process the fixed types. int type_weight_sum = type_use_params.declared_types_weight; for (const auto& fixed_type_weight : type_use_params.fixed_type_weights) { type_weight_sum += fixed_type_weight.weight; // Add all the fixed type spellings as immediately valid. valid_type_names_.Insert(gen.IsCpp() ? fixed_type_weight.cpp_spelling : fixed_type_weight.carbon_spelling); } // Compute the number of declared types used. We expect to have a decent // number of repeated names, so we repeatedly append the entire sequence of // class names until there is some remainder of names needed. int num_declared_types = num_types * type_use_params.declared_types_weight / type_weight_sum; for ([[maybe_unused]] auto _ : llvm::seq(num_declared_types / num_classes)) { llvm::append_range(type_names_, class_names_); } // Now append the remainder number of class names. This is where the class // names being un-shuffled is essential. We're going to have one extra // reference to some fraction of the class names and we want that to be a // stable subset. type_names_.append(class_names_.begin(), class_names_.begin() + (num_declared_types % num_classes)); CARBON_CHECK(static_cast(type_names_.size()) == num_declared_types); // Use each fixed type weight to append the expected number of copies of that // type. This isn't exact however, and is designed to stop short. for (const auto& fixed_type_weight : type_use_params.fixed_type_weights) { int num_fixed_type = num_types * fixed_type_weight.weight / type_weight_sum; type_names_.append(num_fixed_type, gen.IsCpp() ? fixed_type_weight.cpp_spelling : fixed_type_weight.carbon_spelling); } // If we need a tail of types to hit the exact number, simply round-robin // through the fixed types without any weighting. With reasonably large // numbers of types this won't distort the distribution in an interesting way // and is simpler than trying to scale the distribution down. while (static_cast(type_names_.size()) < num_types) { for (const auto& fixed_type_weight : llvm::ArrayRef(type_use_params.fixed_type_weights) .take_front(num_types - type_names_.size())) { type_names_.push_back(gen.IsCpp() ? fixed_type_weight.cpp_spelling : fixed_type_weight.carbon_spelling); } } CARBON_CHECK(static_cast(type_names_.size()) == num_types); last_type_name_index_ = num_types; // Now shuffle both the class names and the type names. std::shuffle(class_names_.begin(), class_names_.end(), gen.rng_); std::shuffle(type_names_.begin(), type_names_.end(), gen.rng_); } // Some heuristic numbers used when formatting generated code. These heuristics // are loosely based on what we expect to make Carbon code readable, and might // not fit as well in C++, but we use the same heuristics across languages for // simplicity and to make the output in different languages more directly // comparable. static constexpr int NumSingleLineFunctionParams = 3; static constexpr int NumSingleLineMethodParams = 2; static constexpr int MaxParamsPerLine = 4; static auto EstimateAvgFunctionDeclLines(SourceGen::FunctionDeclParams params) -> double { // Currently model a uniform distribution [0, max] parameters. Assume a line // break before the first parameter for >3 and after every 4th. int param_lines = 0; for (int num_params : llvm::seq_inclusive(0, params.max_params)) { if (num_params > NumSingleLineFunctionParams) { param_lines += (num_params + MaxParamsPerLine - 1) / MaxParamsPerLine; } } return 1.0 + static_cast(param_lines) / (params.max_params + 1); } static auto EstimateAvgMethodDeclLines(SourceGen::MethodDeclParams params) -> double { // Currently model a uniform distribution [0, max] parameters. Assume a line // break before the first parameter for >2 and after every 4th. int param_lines = 0; for (int num_params : llvm::seq_inclusive(0, params.max_params)) { if (num_params > NumSingleLineMethodParams) { param_lines += (num_params + MaxParamsPerLine - 1) / MaxParamsPerLine; } } return 1.0 + static_cast(param_lines) / (params.max_params + 1); } // Note that this should match the heuristics used when formatting. // TODO: See top-level TODO about line estimates and formatting. static auto EstimateAvgClassDefLines(SourceGen::ClassParams params) -> double { // Comment line, and class open line. double avg = 2.0; // One comment line and blank line per function, plus the function lines. avg += (2.0 + EstimateAvgFunctionDeclLines(params.public_function_decl_params)) * params.public_function_decls; avg += (2.0 + EstimateAvgMethodDeclLines(params.public_method_decl_params)) * params.public_method_decls; avg += (2.0 + EstimateAvgFunctionDeclLines(params.private_function_decl_params)) * params.private_function_decls; avg += (2.0 + EstimateAvgMethodDeclLines(params.private_method_decl_params)) * params.private_method_decls; // A blank line and all the fields (if any). if (params.private_field_decls > 0) { avg += 1.0 + params.private_field_decls; } // No need to account for the class close line, we have an extra blank line // count for the last of the above. return avg; } auto SourceGen::GenApiFileDenseDecls(int target_lines, const DenseDeclParams& params) -> std::string { RawStringOstream source; // Figure out how many classes fit in our target lines, each separated by a // blank line. We need to account the comment lines below to start the file. // Note that we want a blank line after our file comment block, so every class // needs a blank line. constexpr int NumFileCommentLines = 4; double avg_class_lines = EstimateAvgClassDefLines(params.class_params); CARBON_CHECK(target_lines > NumFileCommentLines + avg_class_lines, "Not enough target lines to generate a single class!"); int num_classes = static_cast(target_lines - NumFileCommentLines) / (avg_class_lines + 1); int expected_lines = NumFileCommentLines + num_classes * (avg_class_lines + 1); source << "// Generated " << (!IsCpp() ? "Carbon" : "C++") << " source file.\n"; source << llvm::formatv( "// {0} target lines: {1} classes, {2} expected lines", target_lines, num_classes, expected_lines) << "\n"; source << "//\n// Generating as an API file with dense declarations.\n"; // Carbon uses an implicitly imported prelude to get builtin types, but C++ // requires header files so include those. if (IsCpp()) { source << "\n"; // Header for specific integer types like `std::int64_t`. source << "#include \n"; // Header for `std::pair`. source << "#include \n"; } auto class_gen_state = ClassGenState(*this, num_classes, params.class_params, params.type_use_params); for ([[maybe_unused]] auto _ : llvm::seq(num_classes)) { source << "\n"; GenerateClassDef(params.class_params, class_gen_state, source); } // Make sure we consumed all the state. CARBON_CHECK(class_gen_state.public_function_param_counts().empty()); CARBON_CHECK(class_gen_state.public_method_param_counts().empty()); CARBON_CHECK(class_gen_state.private_function_param_counts().empty()); CARBON_CHECK(class_gen_state.private_method_param_counts().empty()); CARBON_CHECK(class_gen_state.class_names().empty()); CARBON_CHECK(class_gen_state.type_names().empty()); return source.TakeStr(); } auto SourceGen::GetShuffledIdentifiers(int number, int min_length, int max_length, bool uniform) -> llvm::SmallVector { llvm::SmallVector idents = GetIdentifiers(number, min_length, max_length, uniform); std::shuffle(idents.begin(), idents.end(), rng_); return idents; } auto SourceGen::GetShuffledUniqueIdentifiers(int number, int min_length, int max_length, bool uniform) -> llvm::SmallVector { CARBON_CHECK(min_length >= 4, "Cannot trivially guarantee enough distinct, unique identifiers " "for lengths <= 3"); llvm::SmallVector idents = GetUniqueIdentifiers(number, min_length, max_length, uniform); std::shuffle(idents.begin(), idents.end(), rng_); return idents; } auto SourceGen::GetIdentifiers(int number, int min_length, int max_length, bool uniform) -> llvm::SmallVector { llvm::SmallVector idents = GetIdentifiersImpl( number, min_length, max_length, uniform, [this](int length, int length_count, llvm::SmallVectorImpl& dest) { llvm::append_range(dest, GetSingleLengthIdentifiers(length, length_count)); }); return idents; } auto SourceGen::GetUniqueIdentifiers(int number, int min_length, int max_length, bool uniform) -> llvm::SmallVector { CARBON_CHECK(min_length >= 4, "Cannot trivially guarantee enough distinct, unique identifiers " "for lengths <= 3"); llvm::SmallVector idents = GetIdentifiersImpl(number, min_length, max_length, uniform, [this](int length, int length_count, llvm::SmallVectorImpl& dest) { AppendUniqueIdentifiers(length, length_count, dest); }); return idents; } auto SourceGen::GetSingleLengthIdentifiers(int length, int number) -> llvm::ArrayRef { llvm::SmallVector& idents = identifiers_by_length_.Insert(length, {}).value(); if (static_cast(idents.size()) < number) { idents.reserve(number); for ([[maybe_unused]] auto _ : llvm::seq(idents.size(), number)) { auto ident_storage = llvm::MutableArrayRef(reinterpret_cast(storage_.Allocate( /*Size=*/length, /*Alignment=*/1)), length); GenerateRandomIdentifier(ident_storage); llvm::StringRef new_id(ident_storage.data(), length); idents.push_back(new_id); } CARBON_CHECK(static_cast(idents.size()) == number); } return llvm::ArrayRef(idents).slice(0, number); } static auto IdentifierStartChars() -> llvm::ArrayRef { static llvm::SmallVector chars = [] { llvm::SmallVector chars; for (char c : llvm::seq_inclusive('A', 'Z')) { chars.push_back(c); } for (char c : llvm::seq_inclusive('a', 'z')) { chars.push_back(c); } return chars; }(); return chars; } static auto IdentifierChars() -> llvm::ArrayRef { static llvm::SmallVector chars = [] { llvm::ArrayRef start_chars = IdentifierStartChars(); llvm::SmallVector chars(start_chars.begin(), start_chars.end()); chars.push_back('_'); for (char c : llvm::seq_inclusive('0', '9')) { chars.push_back(c); } return chars; }(); return chars; } static constexpr llvm::StringRef NonCarbonCppKeywords[] = { "asm", "do", "double", "float", "int", "long", "new", "signed", "std", "try", "unix", "unsigned", "xor", "NAN", "M_E", "M_PI", }; // Returns a random identifier string of the specified length. // // Ensures this is a valid identifier, avoiding any overlapping syntaxes or // keywords both in Carbon and C++. // // This routine is somewhat expensive and so is useful to cache and reduce the // frequency of calls. However, each time it is called it computes a completely // new random identifier and so can be useful to eventually find a distinct // identifier when needed. auto SourceGen::GenerateRandomIdentifier( llvm::MutableArrayRef dest_storage) -> void { llvm::ArrayRef start_chars = IdentifierStartChars(); llvm::ArrayRef chars = IdentifierChars(); llvm::StringRef ident(dest_storage.data(), dest_storage.size()); do { dest_storage[0] = start_chars[absl::Uniform(rng_, 0, start_chars.size())]; for (int i : llvm::seq(1, dest_storage.size())) { dest_storage[i] = chars[absl::Uniform(rng_, 0, chars.size())]; } } while ( // TODO: Clean up and simplify this code. With some small refactorings and // post-processing we should be able to make this both easier to read and // less inefficient. llvm::any_of( Lex::TokenKind::KeywordTokens, [ident](auto token) { return ident == token.fixed_spelling(); }) || llvm::is_contained(NonCarbonCppKeywords, ident) || ident.ends_with("_t") || ident.ends_with("_MIN") || ident.ends_with("_MAX") || ident.ends_with("_C") || (llvm::is_contained({'i', 'u', 'f'}, ident[0]) && llvm::all_of(ident.substr(1), [](const char c) { return llvm::isDigit(c); }))); } // Appends a number of unique, random identifiers with a particular length to // the provided destination vector. // // Uses, and when necessary grows, a cached sequence of random identifiers with // the specified length. Because these are cached, this is efficient to call // repeatedly, but will not produce a different sequence of identifiers. auto SourceGen::AppendUniqueIdentifiers( int length, int number, llvm::SmallVectorImpl& dest) -> void { auto& [count, unique_idents] = unique_identifiers_by_length_.Insert(length, {}).value(); // See if we need to grow our pool of unique identifiers with the requested // length. if (count < number) { // We'll need to insert exactly the requested new unique identifiers. All // our other inserts will find an existing entry. unique_idents.GrowForInsertCount(count - number); // Generate the needed number of identifiers. for ([[maybe_unused]] auto _ : llvm::seq(count, number)) { // Allocate stable storage for the identifier so we can form stable // `StringRef`s to it. auto ident_storage = llvm::MutableArrayRef(reinterpret_cast(storage_.Allocate( /*Size=*/length, /*Alignment=*/1)), length); // Repeatedly generate novel identifiers of this length until we find a // new unique one. for (;;) { GenerateRandomIdentifier(ident_storage); auto result = unique_idents.Insert(llvm::StringRef(ident_storage.data(), length)); if (result.is_inserted()) { break; } } } count = number; } // Append all the identifiers directly out of the set. We make no guarantees // about the relative order so we just use the non-deterministic order of the // set and avoid additional storage. // // TODO: It's awkward the `ForEach` here can't early-exit. This just walks the // whole set which is harmless if inefficient. We should add early exiting // the loop support to `Set` and update this code. unique_idents.ForEach([&](llvm::StringRef ident) { if (number > 0) { dest.push_back(ident); --number; } }); CARBON_CHECK(number == 0); } // An array of the counts that should be used for each identifier length to // produce our desired distribution. // // Note that the zero-based index corresponds to a 1-based length, so the count // for identifiers of length 1 is at index 0. static constexpr std::array IdentifierLengthCounts = [] { std::array ident_length_counts; // For non-uniform distribution, we simulate a distribution roughly based on // the observed histogram of identifier lengths, but smoothed a bit and // reduced to small counts so that we cycle through all the lengths // reasonably quickly. We want sampling of even 10% of NumTokens from this // in a round-robin form to not be skewed overly much. This still inherently // compresses the long tail as we'd rather have coverage even though it // distorts the distribution a bit. // // The distribution here comes from a script that analyzes source code run // over a few directories of LLVM. The script renders a visual ascii-art // histogram along with the data for each bucket, and that output is // included in comments above each bucket size below to help visualize the // rough shape we're aiming for. // // 1 characters [3976] ███████████████████████████████▊ ident_length_counts[0] = 40; // 2 characters [3724] █████████████████████████████▊ ident_length_counts[1] = 40; // 3 characters [4173] █████████████████████████████████▍ ident_length_counts[2] = 40; // 4 characters [5000] ████████████████████████████████████████ ident_length_counts[3] = 50; // 5 characters [1568] ████████████▌ ident_length_counts[4] = 20; // 6 characters [2226] █████████████████▊ ident_length_counts[5] = 20; // 7 characters [2380] ███████████████████ ident_length_counts[6] = 20; // 8 characters [1786] ██████████████▎ ident_length_counts[7] = 18; // 9 characters [1397] ███████████▏ ident_length_counts[8] = 12; // 10 characters [ 739] █████▉ ident_length_counts[9] = 12; // 11 characters [ 779] ██████▎ ident_length_counts[10] = 12; // 12 characters [1344] ██████████▊ ident_length_counts[11] = 12; // 13 characters [ 498] ████ ident_length_counts[12] = 5; // 14 characters [ 284] ██▎ ident_length_counts[13] = 3; // 15 characters [ 172] █▍ // 16 characters [ 278] ██▎ // 17 characters [ 191] █▌ // 18 characters [ 207] █▋ for (int i = 14; i < 18; ++i) { ident_length_counts[i] = 2; } // 19 - 63 characters are all <100 but non-zero, and we map them to 1 for // coverage despite slightly over weighting the tail. for (int i = 18; i < 64; ++i) { ident_length_counts[i] = 1; } return ident_length_counts; }(); // A template function that implements the common logic of `GetIdentifiers` and // `GetUniqueIdentifiers`. Most parameters correspond to the parameters of those // functions. Additionally, an `AppendFunc` callable is provided to implement // the appending operation. // // The main functionality provided here is collecting the correct number of // identifiers from each of the lengths in the range [min_length, max_length] // and either in our default representative distribution or a uniform // distribution. auto SourceGen::GetIdentifiersImpl(int number, int min_length, int max_length, bool uniform, llvm::function_ref append) -> llvm::SmallVector { CARBON_CHECK(min_length <= max_length); CARBON_CHECK( uniform || max_length <= 64, "Cannot produce a meaningful non-uniform distribution of lengths longer " "than 64 as those are exceedingly rare in our observed data sets."); llvm::SmallVector idents; idents.reserve(number); // First, compute the total weight of the distribution so we know how many // identifiers we'll get each time we collect from it. int num_lengths = max_length - min_length + 1; auto length_counts = llvm::ArrayRef(IdentifierLengthCounts).slice(min_length - 1, num_lengths); int count_sum = uniform ? num_lengths : Sum(length_counts); CARBON_CHECK(count_sum >= 1); int number_rem = number % count_sum; // Finally, walk through each length in the distribution. for (int length : llvm::seq_inclusive(min_length, max_length)) { // Scale how many identifiers we want of this length if computing a // non-uniform distribution. For uniform, we always take one. int scale = uniform ? 1 : IdentifierLengthCounts[length - 1]; // Now we can compute how many identifiers of this length to request. int length_count = (number / count_sum) * scale; if (number_rem > 0) { int rem_adjustment = std::min(scale, number_rem); length_count += rem_adjustment; number_rem -= rem_adjustment; } append(length, length_count, idents); } CARBON_CHECK(number_rem == 0, "Unexpected number remaining: {0}", number_rem); CARBON_CHECK(static_cast(idents.size()) == number, "Ended up with {0} identifiers instead of the requested {1}", idents.size(), number); return idents; } // Returns a shuffled sequence of integers in the range [min, max]. // // The order of the returned integers is random, but each integer in the range // appears the same number of times in the result, with the number of // appearances rounded up for lower numbers and rounded down for higher numbers // in order to exactly produce `number` results. auto SourceGen::GetShuffledInts(int number, int min, int max) -> llvm::SmallVector { llvm::SmallVector ints; ints.reserve(number); // Evenly distribute to each value between min and max. int num_values = max - min + 1; for (int i : llvm::seq_inclusive(min, max)) { int i_count = number / num_values; i_count += i < (min + (number % num_values)); ints.append(i_count, i); } CARBON_CHECK(static_cast(ints.size()) == number); std::shuffle(ints.begin(), ints.end(), rng_); return ints; } // A helper to pop series of unique identifiers off a sequence of random // identifiers that may have duplicates. // // This is particularly designed to work with the sequences of non-unique // identifiers produced by `GetShuffledIdentifiers` with the important property // that while popping off unique identifiers found in the shuffled list, we // don't change the distribution of identifier lengths. // // The uniqueness is only per-instance of the class, and so an instance can be // used to extract a series of names that share a scope. // // It works by scanning the sequence to extract each unique identifier found, // swapping it to the back and popping it off the list. This does shuffle the // order, but it isn't expected to do so in an interesting way. // // It also provides a fallback path in case there are no unique identifiers left // which computes fresh, random identifiers with the same length as the next one // in the sequence until a unique one is found. // // For simplicity of the fallback path, the lifetime of the identifiers produced // is bound to the lifetime of the popper instance, and not the generator as a // whole. If this is ever a problematic constraint, we can start copying // fallback identifiers into the generator's storage. class SourceGen::UniqueIdentifierPopper { public: explicit UniqueIdentifierPopper(SourceGen& gen, llvm::SmallVectorImpl& data) : gen_(&gen), data_(&data), it_(data_->rbegin()) {} // Pop the next unique identifier that can be found in the data, or synthesize // one with a valid length. Always consumes exactly one identifier from the // data. // // Note that the lifetime of the underlying identifier is that of the popper // and not the underlying data. auto Pop() -> llvm::StringRef { for (auto end = data_->rend(); it_ != end; ++it_) { auto insert = set_.Insert(*it_); if (!insert.is_inserted()) { continue; } if (it_ != data_->rbegin()) { std::swap(*data_->rbegin(), *it_); } CARBON_CHECK(insert.key() == data_->back()); return data_->pop_back_val(); } // Out of unique elements. Overwrite the back, preserving its length, // generating a new identifiers until we find a unique one and return that. // This ensures we continue to consume the structure and produce the same // size identifiers even in the fallback. int length = data_->pop_back_val().size(); auto fallback_ident_storage = llvm::MutableArrayRef(reinterpret_cast(gen_->storage_.Allocate( /*Size=*/length, /*Alignment=*/1)), length); for (;;) { gen_->GenerateRandomIdentifier(fallback_ident_storage); auto fallback_id = llvm::StringRef(fallback_ident_storage.data(), length); if (set_.Insert(fallback_id).is_inserted()) { return fallback_id; } } } private: SourceGen* gen_; llvm::SmallVectorImpl* data_; llvm::SmallVectorImpl::reverse_iterator it_; Set set_; }; // Generates a function declaration and writes it to the provided stream. // // The declaration can be configured with a function name, private modifier, // whether it is a method, the parameter count, an how indented it is. // // This is also provided a collection of identifiers to consume as parameter // names -- it will use a unique popper to extract unique parameter names from // this collection. auto SourceGen::GenerateFunctionDecl( llvm::StringRef name, bool is_private, bool is_method, int param_count, llvm::StringRef indent, llvm::SmallVectorImpl& param_names, llvm::function_refllvm::StringRef> get_type_name, llvm::raw_ostream& os) -> void { os << indent << "// TODO: make better comment text\n"; if (!IsCpp()) { os << indent << (is_private ? "private " : "") << "fn " << name; if (is_method) { os << "[self: Self]"; } } else { os << indent; if (!is_method) { os << "static "; } os << "auto " << name; } os << "("; if (param_count > (is_method ? NumSingleLineMethodParams : NumSingleLineFunctionParams)) { os << "\n" << indent << " "; } UniqueIdentifierPopper unique_param_names(*this, param_names); for (int i : llvm::seq(param_count)) { if (i > 0) { if ((i % MaxParamsPerLine) == 0) { os << ",\n" << indent << " "; } else { os << ", "; } } if (!IsCpp()) { os << unique_param_names.Pop() << ": " << get_type_name(); } else { os << get_type_name() << " " << unique_param_names.Pop(); } } os << ")"; os << " -> " << get_type_name(); os << ";\n"; } // Generate a class definition and write it to the provided stream. // // The structure of the definition is guided by the `params` provided, and it // consumes the provided state. auto SourceGen::GenerateClassDef(const ClassParams& params, ClassGenState& state, llvm::raw_ostream& os) -> void { llvm::StringRef name = state.class_names().pop_back_val(); os << "// TODO: make better comment text\n"; os << "class " << name << " {\n"; if (IsCpp()) { os << " public:\n"; } // Field types can't be the class we're currently declaring. We enforce this // by collecting them before inserting that type into the valid set. llvm::SmallVector field_type_names; field_type_names.reserve(params.private_field_decls); for ([[maybe_unused]] auto _ : llvm::seq(params.private_field_decls)) { field_type_names.push_back(state.GetValidTypeName()); } // Mark this class as now a valid type now that field type names have been // collected. We can reference this class from functions and methods within // the definition. state.AddValidTypeName(name); UniqueIdentifierPopper unique_member_names(*this, state.member_names()); llvm::ListSeparator line_sep("\n"); for ([[maybe_unused]] auto _ : llvm::seq(params.public_function_decls)) { os << line_sep; GenerateFunctionDecl( unique_member_names.Pop(), /*is_private=*/false, /*is_method=*/false, state.public_function_param_counts().pop_back_val(), /*indent=*/" ", state.param_names(), [&] { return state.GetValidTypeName(); }, os); } for ([[maybe_unused]] auto _ : llvm::seq(params.public_method_decls)) { os << line_sep; GenerateFunctionDecl( unique_member_names.Pop(), /*is_private=*/false, /*is_method=*/true, state.public_method_param_counts().pop_back_val(), /*indent=*/" ", state.param_names(), [&] { return state.GetValidTypeName(); }, os); } if (IsCpp()) { os << "\n private:\n"; // Reset the separator. line_sep = llvm::ListSeparator("\n"); } for ([[maybe_unused]] auto _ : llvm::seq(params.private_function_decls)) { os << line_sep; GenerateFunctionDecl( unique_member_names.Pop(), /*is_private=*/true, /*is_method=*/false, state.private_function_param_counts().pop_back_val(), /*indent=*/" ", state.param_names(), [&] { return state.GetValidTypeName(); }, os); } for ([[maybe_unused]] auto _ : llvm::seq(params.private_method_decls)) { os << line_sep; GenerateFunctionDecl( unique_member_names.Pop(), /*is_private=*/true, /*is_method=*/true, state.private_method_param_counts().pop_back_val(), /*indent=*/" ", state.param_names(), [&] { return state.GetValidTypeName(); }, os); } os << line_sep; for (llvm::StringRef type_name : field_type_names) { if (!IsCpp()) { os << " private var " << unique_member_names.Pop() << ": " << type_name << ";\n"; } else { os << " " << type_name << " " << unique_member_names.Pop() << ";\n"; } } os << "}" << (IsCpp() ? ";" : "") << "\n"; } } // namespace Carbon::Testing ================================================ FILE: testing/base/source_gen.h ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #ifndef CARBON_TESTING_BASE_SOURCE_GEN_H_ #define CARBON_TESTING_BASE_SOURCE_GEN_H_ #include #include "absl/random/random.h" #include "common/map.h" #include "common/set.h" #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/StringRef.h" #include "llvm/Support/Allocator.h" namespace Carbon::Testing { // Provides source code generation facilities. // // This class works to generate valid but random & meaningless source code in // interesting patterns for benchmarking. It is very incomplete. A high level // set of long-term goals: // // - Generate interesting patterns and structures of code that have emerged as // toolchain performance bottlenecks in practice in C++ codebases. // - Generate code that includes most Carbon language features (and whatever // reasonable C++ analogs could be used for comparative purposes): // - Functions // - Classes with class functions, methods, and fields // - Interfaces // - Checked generics and templates // - Nested and unnested impls // - Nested classes // - Inline and out-of-line function and method definitions // - Imports and exports // - API files and impl files. // - Be random but deterministic. The goal is benchmarking and so while this // code should strive for not producing trivially predictable patterns, it // should also strive to be consistent and suitable for benchmarking. Wherever // possible, it should permute the order and content without randomizing the // total count, size, or complexity. // // Note that the default and primary generation target is interesting Carbon // source code. We have a best-effort to alternatively generate comparable C++ // constructs to the Carbon ones for comparative benchmarking, but there is no // goal to cover all the interesting C++ patterns we might want to benchmark, // and we don't aim for perfectly synthesizing C++ analogs. We can always drop // fidelity for the C++ code path if needed for simplicity. // // TODO: There are numerous places where we hard code a fixed quantity. Instead, // we should build a rich but general system to easily encode a discrete // distribution that is sampled. We have a specialized version of this for // identifiers that should be generalized. class SourceGen { public: enum class Language : uint8_t { Carbon, Cpp, }; struct FunctionDeclParams { // TODD: Arbitrary default, should switch to a distribution from data. int max_params = 4; }; struct MethodDeclParams { // TODD: Arbitrary default, should switch to a distribution from data. int max_params = 4; }; // Parameters used to generate a class in a generated file. // // Currently, this uses a fixed number of each kind of declaration, with // arbitrary defaults chosen. The defaults currently skew towards large // classes with lots of nested declarations. // TODO: Switch these to distributions based on data. // // TODO: Add support for generating definitions and parameters to control // them. // // TODO: Add heuristic for how many functions have return types. struct ClassParams { int public_function_decls = 4; FunctionDeclParams public_function_decl_params = {.max_params = 8}; int public_method_decls = 10; MethodDeclParams public_method_decl_params; int private_function_decls = 2; FunctionDeclParams private_function_decl_params = {.max_params = 6}; int private_method_decls = 8; MethodDeclParams private_method_decl_params = {.max_params = 6}; int private_field_decls = 6; }; // Parameters used to select type _uses_, as opposed to definitions. // // These govern what distribution of types are used for function parameters, // returns, and fields. // // Mainly these provide a coarse histogram of weights to shape the // distribution of different type options, and try to fit that as closely as // possible. // // The default weights in the histogram were arbitrarily selected based on // intuition about importance for benchmarking and not based on any // measurement. We arrange for them to sum to 100 so that the weights can be // view as %s of the type uses. // // The specific builtin type options used in the weights were also selected // arbitrarily. // // TODO: Improve the set of builtin types and the weighting if real world code // ends up sharply different. // // TODO: Add a heuristic to make some % of type references via pointers (or // other compound types). struct TypeUseParams { // The weights in the histogram start with a sequence fixed types described // with a Carbon and C++ string, and their associated weight. struct FixedTypeWeight { llvm::StringRef carbon_spelling; llvm::StringRef cpp_spelling; int weight; }; llvm::SmallVector fixed_type_weights = { // Combined weight of 65 for a core set of builtin types. {.carbon_spelling = "bool", .cpp_spelling = "bool", .weight = 25}, {.carbon_spelling = "i32", .cpp_spelling = "int", .weight = 20}, {.carbon_spelling = "i64", .cpp_spelling = "std::int64_t", .weight = 10}, {.carbon_spelling = "i32*", .cpp_spelling = "int*", .weight = 5}, {.carbon_spelling = "i64*", .cpp_spelling = "std::int64_t*", .weight = 5}, // A weight of 5 distributed across tuple structures {.carbon_spelling = "(bool, i64)", .cpp_spelling = "std::pair", .weight = 2}, {.carbon_spelling = "(i32, i64*)", .cpp_spelling = "std::pair", .weight = 3}, }; // The weight for using types declared in the file. These will be randomly // shuffled references, and when there are more type references than // declared, include repeated references. int declared_types_weight = 30; }; // Parameters used to generate a file with dense declarations. struct DenseDeclParams { // TODO: Add more parameters to control generating top-level constructs // other than class definitions. // Parameters used when generating class definitions. ClassParams class_params = {}; // Parameters used to guide the selection of types for use in declarations. TypeUseParams type_use_params = {}; }; // Access a global instance of this type to generate Carbon code for // benchmarks, tests, or other places where sharing a common instance is // useful. Note that there is nothing thread safe about this instance or type. static auto Global() -> SourceGen&; // Construct a source generator for the provided language, by default Carbon. explicit SourceGen(Language language = Language::Carbon); // Generate an API file with dense classes containing function forward // declarations. // // Accepts a number of `target_lines` for the resulting source code. This is a // rough approximation used to scale all the other constructs up and down // accordingly. For C++ source generation, we work to generate the same number // of constructs as Carbon would for the given line count over keeping the // actual line count close to the target. // // TODO: Currently, the formatting and line breaks of generating code are // extremely rough still, and those are a large factor in adherence to // `target_lines`. Long term, the goal is to get as close as we can to any // automatically formatted code while still keeping the stability of // benchmarking. auto GenApiFileDenseDecls(int target_lines, const DenseDeclParams& params) -> std::string; // Get some number of randomly shuffled identifiers. // // The identifiers start with a character [A-Za-z], other characters may also // include [0-9_]. Both Carbon and C++ keywords are excluded along with any // other non-identifier syntaxes that overlap to ensure all of these can be // used as identifiers. // // The order will be different for each call to this function, but the // specific identifiers may remain the same in order to reduce the cost of // repeated calls. However, the sum of the identifier sizes returned is // guaranteed to be the same for every call with the same number of // identifiers so that benchmarking all of these identifiers has predictable // and stable cost. // // Optionally, callers can request a minimum and maximum length. By default, // the length distribution used across the identifiers will mirror the // observed distribution of identifiers in C++ source code and our expectation // of them in Carbon source code. The maximum length in this default // distribution cannot be more than 64. // // Callers can request a uniform distribution across [min_length, max_length], // and when it is requested there is no limit on `max_length`. auto GetShuffledIdentifiers(int number, int min_length = 1, int max_length = 64, bool uniform = false) -> llvm::SmallVector; // Same as `GetShuffledIdentifiers`, but ensures there are no collisions. auto GetShuffledUniqueIdentifiers(int number, int min_length = 4, int max_length = 64, bool uniform = false) -> llvm::SmallVector; // Returns a collection of un-shuffled identifiers, otherwise the same as // `GetShuffledIdentifiers`. // // Usually, benchmarks should use the shuffled version. However, this is // useful when deterministic access to the identifiers is needed to avoid // introducing noise, or if there is already a post-processing step to shuffle // things, since shuffling is very expensive in debug builds. auto GetIdentifiers(int number, int min_length = 1, int max_length = 64, bool uniform = false) -> llvm::SmallVector; // Returns a collection of un-shuffled unique identifiers, otherwise the same // as `GetShuffledUniqueIdentifiers`. // // Usually, benchmarks should use the shuffled version. However, this is // useful when deterministic access to the identifiers is needed to avoid // introducing noise, or if there is already a post-processing step to shuffle // things, since shuffling is very expensive in debug builds. auto GetUniqueIdentifiers(int number, int min_length = 1, int max_length = 64, bool uniform = false) -> llvm::SmallVector; // Returns a shared collection of random identifiers of a specific length. // // For a single, exact length, we have an even cheaper routine to return // access to a shared collection of identifiers. The order of these is a // single fixed random order for a given execution. The returned array // reference is only valid until the next call any method on this generator. auto GetSingleLengthIdentifiers(int length, int number) -> llvm::ArrayRef; private: class ClassGenState; friend ClassGenState; class UniqueIdentifierPopper; friend UniqueIdentifierPopper; using AppendFn = auto(int length, int number, llvm::SmallVectorImpl& dest) -> void; auto IsCpp() -> bool { return language_ == Language::Cpp; } auto GenerateRandomIdentifier(llvm::MutableArrayRef dest_storage) -> void; auto AppendUniqueIdentifiers(int length, int number, llvm::SmallVectorImpl& dest) -> void; auto GetIdentifiersImpl(int number, int min_length, int max_length, bool uniform, llvm::function_ref append) -> llvm::SmallVector; auto GetShuffledInts(int number, int min, int max) -> llvm::SmallVector; auto GenerateFunctionDecl( llvm::StringRef name, bool is_private, bool is_method, int param_count, llvm::StringRef indent, llvm::SmallVectorImpl& param_names, llvm::function_refllvm::StringRef> get_type_name, llvm::raw_ostream& os) -> void; auto GenerateClassDef(const ClassParams& params, ClassGenState& state, llvm::raw_ostream& os) -> void; absl::BitGen rng_; llvm::BumpPtrAllocator storage_; Map> identifiers_by_length_; Map>> unique_identifiers_by_length_; Language language_; }; } // namespace Carbon::Testing #endif // CARBON_TESTING_BASE_SOURCE_GEN_H_ ================================================ FILE: testing/base/source_gen_main.cpp ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #include #include #include "common/bazel_working_dir.h" #include "common/command_line.h" #include "common/init_llvm.h" #include "common/ostream.h" #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/SmallVector.h" #include "llvm/ADT/StringRef.h" #include "llvm/Support/FileSystem.h" #include "testing/base/source_gen.h" namespace Carbon::Testing { namespace { constexpr CommandLine::CommandInfo Info = { .name = "source_gen", .help = R"""( A source generator for Carbon. )""", }; constexpr CommandLine::ArgInfo OutputArgInfo = { .name = "output", .value_name = "FILE", .help = R"""( Writes the generate source code to a file rather than stdout. )""", }; constexpr CommandLine::ArgInfo LinesArgInfo = { .name = "lines", .value_name = "N", .help = R"""( The number of lines of code to target for a generated source file. )""", }; constexpr CommandLine::ArgInfo LanguageArgInfo = { .name = "language", //.value_name = "[carbon|cpp]", .help = R"""( The language of source code to generate. The C++ source generation is best effort to try to provide as much comparable benchmarking as possible, but the primary language focus is generating Carbon. )""", }; auto Run(llvm::ArrayRef args) -> bool { // Default to outputting to stdout and writing 10k lines of source code. llvm::StringRef output_filename = "-"; int lines = 10'000; SourceGen::Language language; auto parse_result = CommandLine::Parse( args, llvm::outs(), Info, [&](CommandLine::CommandBuilder& b) { b.AddStringOption(OutputArgInfo, [&](auto& arg_b) { arg_b.Set(&output_filename); }); b.AddIntegerOption(LinesArgInfo, [&](auto& arg_b) { arg_b.Set(&lines); }); b.AddOneOfOption(LanguageArgInfo, [&](auto& arg_b) { arg_b.SetOneOf( { arg_b.OneOfValue("carbon", SourceGen::Language::Carbon) .Default(true), arg_b.OneOfValue("cpp", SourceGen::Language::Cpp), }, &language); }); // No-op action as there is only one operation for this command. b.Do([] {}); }); if (!parse_result.ok()) { llvm::errs() << "error: " << *parse_result << "\n"; return false; } else if (*parse_result == CommandLine::ParseResult::MetaSuccess) { // Fully handled by the CLI library. return true; } std::optional output_file; llvm::raw_fd_ostream* output = &llvm::outs(); if (output_filename != "-") { std::error_code ec; output_file.emplace(output_filename, ec, llvm::sys::fs::OF_None); if (ec) { llvm::errs() << "ERROR: Unable to open output file '" << output_filename << "': " << ec.message() << "\n"; return false; } output = &*output_file; } SourceGen gen(language); *output << gen.GenApiFileDenseDecls(lines, SourceGen::DenseDeclParams{}); output->flush(); return true; } } // namespace } // namespace Carbon::Testing auto main(int argc, char** argv) -> int { // Do LLVM's initialization first, this will also transform UTF-16 to UTF-8. Carbon::InitLLVM init_llvm(argc, argv); Carbon::SetWorkingDirForBazelRun(); llvm::SmallVector args(argv + 1, argv + argc); bool success = Carbon::Testing::Run(args); return success ? EXIT_SUCCESS : EXIT_FAILURE; } ================================================ FILE: testing/base/source_gen_test.cpp ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #include "testing/base/source_gen.h" #include #include #include #include "common/set.h" #include "testing/base/global_exe_path.h" #include "toolchain/base/install_paths_test_helpers.h" #include "toolchain/driver/driver.h" namespace Carbon::Testing { namespace { using ::testing::AllOf; using ::testing::ContainerEq; using ::testing::Contains; using ::testing::Each; using ::testing::Eq; using ::testing::Ge; using ::testing::Gt; using ::testing::Le; using ::testing::MatchesRegex; using ::testing::SizeIs; // Tiny helper to sum the sizes of a range of ranges. Uses a template to avoid // hard coding any specific types for the two ranges. template static auto SumSizes(const T& range) -> ssize_t { ssize_t sum = 0; for (const auto& inner_range : range) { sum += inner_range.size(); } return sum; } TEST(SourceGenTest, Identifiers) { SourceGen gen; auto idents = gen.GetShuffledIdentifiers(1000); EXPECT_THAT(idents.size(), Eq(1000)); for (llvm::StringRef ident : idents) { EXPECT_THAT(ident, MatchesRegex("[A-Za-z][A-Za-z0-9_]*")); } // We should have at least one identifier of each length [1, 64]. The exact // distribution is an implementation detail designed to vaguely match the // expected distribution in source code. for (int size : llvm::seq_inclusive(1, 64)) { EXPECT_THAT(idents, Contains(SizeIs(size))); } // Check that identifiers 4 characters or shorter are more common than longer // lengths. This is a very rough way of double checking that we got the // intended distribution. for (int short_size : llvm::seq_inclusive(1, 4)) { int short_count = llvm::count_if(idents, [&](auto ident) { return static_cast(ident.size()) == short_size; }); for (int long_size : llvm::seq_inclusive(5, 64)) { EXPECT_THAT(short_count, Gt(llvm::count_if(idents, [&](auto ident) { return static_cast(ident.size()) == long_size; }))); } } // Check that repeated calls are different in interesting ways, but have the // exact same total bytes. ssize_t idents_size_sum = SumSizes(idents); for ([[maybe_unused]] auto _ : llvm::seq(10)) { auto idents2 = gen.GetShuffledIdentifiers(1000); EXPECT_THAT(idents2, SizeIs(1000)); // Should be (at least) a different shuffle of identifiers. EXPECT_THAT(idents2, Not(ContainerEq(idents))); // But the sum of lengths should be identical. EXPECT_THAT(SumSizes(idents2), Eq(idents_size_sum)); } // Check length constraints have the desired effect. idents = gen.GetShuffledIdentifiers(1000, /*min_length=*/10, /*max_length=*/20); EXPECT_THAT(idents, Each(SizeIs(AllOf(Ge(10), Le(20))))); } TEST(SourceGenTest, UniformIdentifiers) { SourceGen gen; // Check that uniform identifier length results in exact coverage of each // possible length for an easy case, both without and with a remainder. auto idents = gen.GetShuffledIdentifiers(100, /*min_length=*/10, /*max_length=*/19, /*uniform=*/true); EXPECT_THAT(idents, Contains(SizeIs(10)).Times(10)); EXPECT_THAT(idents, Contains(SizeIs(11)).Times(10)); EXPECT_THAT(idents, Contains(SizeIs(12)).Times(10)); EXPECT_THAT(idents, Contains(SizeIs(13)).Times(10)); EXPECT_THAT(idents, Contains(SizeIs(14)).Times(10)); EXPECT_THAT(idents, Contains(SizeIs(15)).Times(10)); EXPECT_THAT(idents, Contains(SizeIs(16)).Times(10)); EXPECT_THAT(idents, Contains(SizeIs(17)).Times(10)); EXPECT_THAT(idents, Contains(SizeIs(18)).Times(10)); EXPECT_THAT(idents, Contains(SizeIs(19)).Times(10)); idents = gen.GetShuffledIdentifiers(97, /*min_length=*/10, /*max_length=*/19, /*uniform=*/true); EXPECT_THAT(idents, Contains(SizeIs(10)).Times(10)); EXPECT_THAT(idents, Contains(SizeIs(11)).Times(10)); EXPECT_THAT(idents, Contains(SizeIs(12)).Times(10)); EXPECT_THAT(idents, Contains(SizeIs(13)).Times(10)); EXPECT_THAT(idents, Contains(SizeIs(14)).Times(10)); EXPECT_THAT(idents, Contains(SizeIs(15)).Times(10)); EXPECT_THAT(idents, Contains(SizeIs(16)).Times(10)); EXPECT_THAT(idents, Contains(SizeIs(17)).Times(9)); EXPECT_THAT(idents, Contains(SizeIs(18)).Times(9)); EXPECT_THAT(idents, Contains(SizeIs(19)).Times(9)); } // Largely covered by `Identifiers` and `UniformIdentifiers`, but need to check // for uniqueness specifically. TEST(SourceGenTest, UniqueIdentifiers) { SourceGen gen; auto unique = gen.GetShuffledUniqueIdentifiers(1000); EXPECT_THAT(unique.size(), Eq(1000)); Set set; for (llvm::StringRef ident : unique) { EXPECT_THAT(ident, MatchesRegex("[A-Za-z][A-Za-z0-9_]*")); EXPECT_TRUE(set.Insert(ident).is_inserted()) << "Colliding identifier: " << ident; } // Check single length specifically where uniqueness is the most challenging. set.Clear(); unique = gen.GetShuffledUniqueIdentifiers(1000, /*min_length=*/4, /*max_length=*/4); for (llvm::StringRef ident : unique) { EXPECT_TRUE(set.Insert(ident).is_inserted()) << "Colliding identifier: " << ident; } } // Check that the source code doesn't have compiler errors. auto TestCompile(llvm::StringRef source) -> bool { llvm::IntrusiveRefCntPtr fs = new llvm::vfs::InMemoryFileSystem; InstallPaths installation( InstallPaths::MakeForBazelRunfiles(Testing::GetExePath())); Driver driver(fs, &installation, /*input_stream=*/nullptr, &llvm::outs(), &llvm::errs()); AddPreludeFilesToVfs(installation, fs); fs->addFile("test.carbon", /*ModificationTime=*/0, llvm::MemoryBuffer::getMemBuffer(source)); return driver.RunCommand({"compile", "--phase=check", "test.carbon"}).success; } TEST(SourceGenTest, GenApiFileDenseDeclsTest) { SourceGen gen; std::string source = gen.GenApiFileDenseDecls(1000, SourceGen::DenseDeclParams{}); // Should be within 1% of the requested line count. EXPECT_THAT(source, Contains('\n').Times(AllOf(Ge(950), Le(1050)))); // Make sure we generated valid Carbon code. EXPECT_TRUE(TestCompile(source)); } TEST(SourceGenTest, GenApiFileDenseDeclsCppTest) { SourceGen gen(SourceGen::Language::Cpp); // Generate a 1000-line file which is enough to have a reasonably accurate // line count estimate and have a few classes. std::string source = gen.GenApiFileDenseDecls(1000, SourceGen::DenseDeclParams{}); // Should be within 10% of the requested line count. EXPECT_THAT(source, Contains('\n').Times(AllOf(Ge(900), Le(1100)))); // TODO: When the driver supports compiling C++ code as easily as Carbon, we // should test that the generated C++ code is valid. } } // namespace } // namespace Carbon::Testing ================================================ FILE: testing/base/unified_diff_matcher.h ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #ifndef CARBON_TESTING_BASE_UNIFIED_DIFF_MATCHER_H_ #define CARBON_TESTING_BASE_UNIFIED_DIFF_MATCHER_H_ #include #include #include #include #include #include "common/check.h" #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/Sequence.h" #include "llvm/ADT/SmallVector.h" namespace Carbon::Testing { // Matcher that compares the elements of two containers and produces a unified // diff on failure. template class UnifiedDiffMatcher { public: explicit UnifiedDiffMatcher(Container expected) : expected_(std::move(expected)) {} // Matches `actual` against `expected_`. Returns true on a match; returns // false and prints a unified diff to `listener` on a mismatch. template auto MatchAndExplain(const ActualContainer& actual, testing::MatchResultListener* listener) const -> bool; auto DescribeTo(std::ostream* os) const -> void { *os << "matches elements with unified diff"; } auto DescribeNegationTo(std::ostream* os) const -> void { *os << "does not match elements with unified diff"; } private: // A 2D array, stored contiguously. Rows correspond to `expected_`'s elements, // and columns correspond to the actual container's elements. template class Table; // The result of a `Matches` check between an expected and actual element. enum class MatchResult : uint8_t { Unknown, Matches, DoesNotMatch }; // Checks whether `actual_element` matches `expected_[expected_index]`. It // first checks whether a cached result exists. If not, it evaluates the // match and stores the result in `match_results`. template auto IsElementMatch(size_t expected_index, size_t actual_index, const ActualElement& actual_element, Table& match_results) const -> bool { MatchResult cached_result = match_results.Get(expected_index, actual_index); if (cached_result != MatchResult::Unknown) { return cached_result == MatchResult::Matches; } bool is_match = testing::MatcherCast(expected_[expected_index]) .Matches(actual_element); match_results.Set( expected_index, actual_index, is_match ? MatchResult::Matches : MatchResult::DoesNotMatch); return is_match; } // Returns true if every element in `expected_` matches the corresponding // element in `actual`. Stores comparisons in `match_results`. template auto IsEqual(const ActualContainer& actual, Table& match_results) const -> bool; // Populates `subsequences` with the longest common matching subsequences // found when comparing `actual` and `expected_`. Stores comparisons in // `match_results`. template auto GetLongestCommonSubsequences(const ActualContainer& actual, Table& match_results, Table& subsequences) const -> void; // Prints the unified diff. template auto PrintDiff(const ActualContainer& actual, Table& match_results, const Table& subsequences, testing::MatchResultListener* listener) const -> void; // The expected elements. Container expected_; }; // Returns a polymorphic matcher that acts similarly to // ElementsAreArray but produces a unified diff on failure. template auto ElementsAreArrayWithUnifiedDiff(Container expected) { return testing::MakePolymorphicMatcher( UnifiedDiffMatcher(std::move(expected))); } // ----------------------------------------------------------------------------- // Internal implementation details follow. // ----------------------------------------------------------------------------- template template class UnifiedDiffMatcher::Table { public: // Constructs a table with dimensions of expected_size and actual_size, // corresponding to the containers being compared. Table(int expected_size, int actual_size, T default_value) : actual_size_(actual_size), data_(expected_size * actual_size, default_value) {} // Sets the value at the given expected_index and actual_index. auto Set(int expected_index, int actual_index, T value) -> void { data_[expected_index * actual_size_ + actual_index] = std::move(value); } // Gets the value at the given expected_index and actual_index. auto Get(int expected_index, int actual_index) const -> T { return data_[expected_index * actual_size_ + actual_index]; } private: // The actual_size of the table. int actual_size_; // The contiguous data storage for the table. llvm::SmallVector data_; }; template template auto UnifiedDiffMatcher::MatchAndExplain( const ActualContainer& actual, testing::MatchResultListener* listener) const -> bool { Table match_results(expected_.size(), std::size(actual), MatchResult::Unknown); if (IsEqual(actual, match_results)) { return true; } if (listener->IsInterested()) { Table subsequences(expected_.size() + 1, std::size(actual) + 1, 0); GetLongestCommonSubsequences(actual, match_results, subsequences); PrintDiff(actual, match_results, subsequences, listener); } return false; } template template auto UnifiedDiffMatcher::IsEqual( const ActualContainer& actual, Table& match_results) const -> bool { if (expected_.size() != std::size(actual)) { return false; } for (auto [i, actual_element] : llvm::enumerate(actual)) { if (!IsElementMatch(i, i, actual_element, match_results)) { return false; } } return true; } template template auto UnifiedDiffMatcher::GetLongestCommonSubsequences( const ActualContainer& actual, Table& match_results, Table& subsequences) const -> void { for (auto expected_index : llvm::seq(expected_.size())) { for (auto [actual_index, actual_element] : llvm::enumerate(actual)) { int subsequence_value; if (IsElementMatch(expected_index, actual_index, actual_element, match_results)) { // If the elements match, the LCS length increases by 1 relative to // the prefixes where both elements are excluded. subsequence_value = subsequences.Get(expected_index, actual_index) + 1; } else { // Otherwise, the LCS length is the maximum of the LCS lengths // relative to the prefixes where one element is excluded. subsequence_value = std::max(subsequences.Get(expected_index, actual_index + 1), subsequences.Get(expected_index + 1, actual_index)); } subsequences.Set(expected_index + 1, actual_index + 1, subsequence_value); } } } template template auto UnifiedDiffMatcher::PrintDiff( const ActualContainer& actual, Table& match_results, const Table& subsequences, testing::MatchResultListener* listener) const -> void { // A line in the diff output. struct DiffLine { enum class Kind { Match, ActualOnly, ExpectedOnly }; Kind kind; // Only used for `Match` and `ActualOnly`. const ActualContainer::value_type* actual_value; int expected_index; }; llvm::SmallVector diff; // Reserve a quick upper bound of the size. diff.reserve(expected_.size() + std::size(actual)); // Reconstruct the diff by backtracking from the end of the table. int expected_index = expected_.size() - 1; int actual_index = std::size(actual) - 1; auto actual_it = std::end(actual) - 1; while (expected_index >= 0 || actual_index >= 0) { auto match_result = (expected_index >= 0 && actual_index >= 0) ? match_results.Get(expected_index, actual_index) : MatchResult::DoesNotMatch; CARBON_CHECK(match_result != MatchResult::Unknown); if (match_result == MatchResult::Matches) { // The element is in both lists for the diff. diff.push_back({.kind = DiffLine::Kind::Match, .actual_value = &*actual_it, .expected_index = expected_index}); --expected_index; --actual_index; --actual_it; } else if (actual_index >= 0 && (expected_index < 0 || subsequences.Get(expected_index + 1, actual_index) >= subsequences.Get(expected_index, actual_index + 1))) { // Dropping an element from `actual` preserves the LCS length, so treat it // as an insertion. diff.push_back({.kind = DiffLine::Kind::ActualOnly, .actual_value = &*actual_it, .expected_index = std::max(0, expected_index)}); --actual_index; --actual_it; } else { // Otherwise, treat it as a deletion from `expected`. diff.push_back({.kind = DiffLine::Kind::ExpectedOnly, .actual_value = nullptr, .expected_index = expected_index}); --expected_index; } } struct PrintRange { int begin; int end; }; llvm::SmallVector print_ranges; constexpr int ContextLines = 3; for (auto [i, line] : llvm::reverse(llvm::zip_equal(llvm::seq(diff.size()), diff))) { if (line.kind != DiffLine::Kind::Match) { PrintRange range = { .begin = std::max(0, i - ContextLines), .end = std::min(diff.size() - 1, i + ContextLines)}; if (print_ranges.empty() || print_ranges.back().begin > range.end + 1) { print_ranges.push_back(range); } else { // Merge diffs with overlapping context. print_ranges.back().begin = range.begin; } } } *listener << "unified diff (- expected, + actual):\n"; for (const auto& range : print_ranges) { *listener << "=== diff in expected elements " << diff[range.end].expected_index + 1 << " to " << diff[range.begin].expected_index + 1 << " (1-based index):\n"; for (auto i : llvm::reverse(llvm::seq_inclusive(range.begin, range.end))) { const auto& line = diff[i]; if (line.kind == DiffLine::Kind::Match) { *listener << " " << *line.actual_value << "\n"; } else if (line.kind == DiffLine::Kind::ActualOnly) { *listener << "+ " << *line.actual_value << "\n"; } else { *listener << "- "; expected_[line.expected_index].DescribeTo(listener->stream()); *listener << "\n"; } } } *listener << "=== diff end\n"; } } // namespace Carbon::Testing #endif // CARBON_TESTING_BASE_UNIFIED_DIFF_MATCHER_H_ ================================================ FILE: testing/base/unified_diff_matcher_test.cpp ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #include "testing/base/unified_diff_matcher.h" #include #include #include #include "llvm/ADT/SmallVector.h" namespace Carbon::Testing { namespace { using ::testing::Matcher; using ::testing::StrEq; // Asserts that when expected does not match actual, the string // representation of the produced diff equals expected_diff. auto ExpectUnifiedDiff(const llvm::SmallVector& actual, const llvm::SmallVector>& expected, const std::string& expected_diff) -> void { testing::StringMatchResultListener listener; EXPECT_FALSE(testing::ExplainMatchResult( ElementsAreArrayWithUnifiedDiff(expected), actual, &listener)); EXPECT_THAT(listener.str(), testing::Eq(expected_diff)); } TEST(UnifiedDiffMatcherTest, Matches) { llvm::SmallVector actual = {"A", "B", "C"}; llvm::SmallVector> expected = {StrEq("A"), StrEq("B"), StrEq("C")}; EXPECT_THAT(actual, ElementsAreArrayWithUnifiedDiff(expected)); } TEST(UnifiedDiffMatcherTest, MismatchMissing) { constexpr char ExpectedDiff[] = R"(unified diff (- expected, + actual): === diff in expected elements 1 to 3 (1-based index): A - is equal to "B" C === diff end )"; ExpectUnifiedDiff({"A", "C"}, {StrEq("A"), StrEq("B"), StrEq("C")}, ExpectedDiff); } TEST(UnifiedDiffMatcherTest, MismatchExtra) { constexpr char ExpectedDiff[] = R"(unified diff (- expected, + actual): === diff in expected elements 1 to 2 (1-based index): A + B C === diff end )"; ExpectUnifiedDiff({"A", "B", "C"}, {StrEq("A"), StrEq("C")}, ExpectedDiff); } TEST(UnifiedDiffMatcherTest, MismatchBoth) { constexpr char ExpectedDiff[] = R"(unified diff (- expected, + actual): === diff in expected elements 1 to 2 (1-based index): A - is equal to "C" + B === diff end )"; ExpectUnifiedDiff({"A", "B"}, {StrEq("A"), StrEq("C")}, ExpectedDiff); } TEST(UnifiedDiffMatcherTest, MismatchMultiple) { constexpr char ExpectedDiff[] = R"(unified diff (- expected, + actual): === diff in expected elements 1 to 5 (1-based index): A - is equal to "B" + X C - is equal to "D" + Y E === diff end )"; ExpectUnifiedDiff( {"A", "X", "C", "Y", "E"}, {StrEq("A"), StrEq("B"), StrEq("C"), StrEq("D"), StrEq("E")}, ExpectedDiff); } TEST(UnifiedDiffMatcherTest, MismatchLongContext) { constexpr char ExpectedDiff[] = R"(unified diff (- expected, + actual): === diff in expected elements 2 to 8 (1-based index): 1 2 3 - is equal to "X" + 4 5 6 7 === diff end )"; ExpectUnifiedDiff({"0", "1", "2", "3", "4", "5", "6", "7", "8"}, {StrEq("0"), StrEq("1"), StrEq("2"), StrEq("3"), StrEq("X"), StrEq("5"), StrEq("6"), StrEq("7"), StrEq("8")}, ExpectedDiff); } TEST(UnifiedDiffMatcherTest, Mismatch5LineContext) { constexpr char ExpectedDiff[] = R"(unified diff (- expected, + actual): === diff in expected elements 1 to 7 (1-based index): - is equal to "X" + 0 1 2 3 4 5 - is equal to "Y" + 6 === diff end )"; ExpectUnifiedDiff({"0", "1", "2", "3", "4", "5", "6"}, {StrEq("X"), StrEq("1"), StrEq("2"), StrEq("3"), StrEq("4"), StrEq("5"), StrEq("Y")}, ExpectedDiff); } TEST(UnifiedDiffMatcherTest, Mismatch6LineContext) { constexpr char ExpectedDiff[] = R"(unified diff (- expected, + actual): === diff in expected elements 1 to 8 (1-based index): - is equal to "X" + 0 1 2 3 4 5 6 - is equal to "Y" + 7 === diff end )"; ExpectUnifiedDiff({"0", "1", "2", "3", "4", "5", "6", "7"}, {StrEq("X"), StrEq("1"), StrEq("2"), StrEq("3"), StrEq("4"), StrEq("5"), StrEq("6"), StrEq("Y")}, ExpectedDiff); } TEST(UnifiedDiffMatcherTest, Mismatch7LineContext) { constexpr char ExpectedDiff[] = R"(unified diff (- expected, + actual): === diff in expected elements 1 to 4 (1-based index): - is equal to "X" + 0 1 2 3 === diff in expected elements 6 to 9 (1-based index): 5 6 7 - is equal to "Y" + 8 === diff end )"; ExpectUnifiedDiff({"0", "1", "2", "3", "4", "5", "6", "7", "8"}, {StrEq("X"), StrEq("1"), StrEq("2"), StrEq("3"), StrEq("4"), StrEq("5"), StrEq("6"), StrEq("7"), StrEq("Y")}, ExpectedDiff); } TEST(UnifiedDiffMatcherTest, MismatchEmptyExpected) { constexpr char ExpectedDiff[] = R"(unified diff (- expected, + actual): === diff in expected elements 1 to 1 (1-based index): + A === diff end )"; ExpectUnifiedDiff({"A"}, {}, ExpectedDiff); } TEST(UnifiedDiffMatcherTest, MismatchEmptyActual) { constexpr char ExpectedDiff[] = R"(unified diff (- expected, + actual): === diff in expected elements 1 to 1 (1-based index): - is equal to "A" === diff end )"; ExpectUnifiedDiff({}, {StrEq("A")}, ExpectedDiff); } TEST(UnifiedDiffMatcherTest, MismatchLongDifference) { constexpr char ExpectedDiff[] = R"(unified diff (- expected, + actual): === diff in expected elements 1 to 4 (1-based index): 1 - is equal to "2" - is equal to "3" + X + Y + Z 4 === diff end )"; ExpectUnifiedDiff({"1", "X", "Y", "Z", "4"}, {StrEq("1"), StrEq("2"), StrEq("3"), StrEq("4")}, ExpectedDiff); } TEST(UnifiedDiffMatcherTest, MismatchGreedyResyncActualMissing) { constexpr char ExpectedDiff[] = R"(unified diff (- expected, + actual): === diff in expected elements 1 to 6 (1-based index): 1 2 - is equal to "3" + X + 7 4 5 6 === diff end )"; ExpectUnifiedDiff({"1", "2", "X", "7", "4", "5", "6", "7", "8", "9"}, {StrEq("1"), StrEq("2"), StrEq("3"), StrEq("4"), StrEq("5"), StrEq("6"), StrEq("7"), StrEq("8"), StrEq("9")}, ExpectedDiff); } TEST(UnifiedDiffMatcherTest, MismatchGreedyResyncExpectedMissing) { constexpr char ExpectedDiff[] = R"(unified diff (- expected, + actual): === diff in expected elements 1 to 7 (1-based index): 1 2 - is equal to "X" - is equal to "7" + 3 4 5 6 === diff end )"; ExpectUnifiedDiff( {"1", "2", "3", "4", "5", "6", "7", "8", "9"}, {StrEq("1"), StrEq("2"), StrEq("X"), StrEq("7"), StrEq("4"), StrEq("5"), StrEq("6"), StrEq("7"), StrEq("8"), StrEq("9")}, ExpectedDiff); } } // namespace } // namespace Carbon::Testing ================================================ FILE: testing/file_test/BUILD ================================================ # Part of the Carbon Language project, under the Apache License v2.0 with LLVM # Exceptions. See /LICENSE for license information. # SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception load("//bazel/cc_rules:defs.bzl", "cc_library", "cc_test") load("rules.bzl", "file_test") package(default_visibility = ["//visibility:public"]) cc_library( name = "autoupdate", testonly = 1, srcs = ["autoupdate.cpp"], hdrs = [ "autoupdate.h", "line.h", ], deps = [ "//common:check", "//common:ostream", "//common:raw_string_ostream", "//testing/base:file_helpers", "@abseil-cpp//absl/strings", "@abseil-cpp//absl/strings:string_view", "@llvm-project//llvm:Support", "@re2", ], ) cc_library( name = "file_test_base", testonly = 1, srcs = [ "file_test_base.cpp", "run_test.cpp", "run_test.h", "test_file.cpp", ], hdrs = [ "file_test_base.h", "test_file.h", ], deps = [ ":autoupdate", ":manifest", "//common:build_data", "//common:check", "//common:error", "//common:exe_path", "//common:find", "//common:init_llvm", "//common:ostream", "//common:pretty_stack_trace_function", "//common:raw_string_ostream", "//common:set", "//testing/base:file_helpers", "//testing/base:unified_diff_matcher", "@abseil-cpp//absl/flags:flag", "@abseil-cpp//absl/flags:parse", "@abseil-cpp//absl/strings", "@googletest//:gtest", "@llvm-project//llvm:Support", ], ) cc_test( name = "test_file_test", size = "small", srcs = ["test_file_test.cpp"], deps = [ ":file_test_base", ":manifest_impl", "//common:error_test_helpers", "//testing/base:file_helpers", "@googletest//:gtest", ], ) file_test( name = "file_test_base_test", size = "small", srcs = ["file_test_base_test.cpp"], data = glob(["testdata/include_files/**"]), tests = glob( ["testdata/**"], exclude = ["testdata/include_files/**"], ), deps = [ ":file_test_base", "//common:ostream", "@googletest//:gtest", "@llvm-project//llvm:Support", ], ) # Note this is separate from the implementation; see the .h file. cc_library( name = "manifest", testonly = 1, hdrs = ["manifest.h"], deps = ["@llvm-project//llvm:Support"], ) # Used through `file_test` in rules.bzl. cc_library( name = "manifest_impl", testonly = 1, srcs = ["manifest.cpp"], deps = [":manifest"], ) ================================================ FILE: testing/file_test/README.md ================================================ # file_test ## BUILD A typical BUILD target will look like: ```starlark load("rules.bzl", "file_test") file_test( name = "my_file_test", srcs = ["my_file_test.cpp"], tests = glob(["testdata/**"]), deps = [ ":my_lib", "//testing/file_test:file_test_base", "@googletest//:gtest", "@llvm-project//llvm:Support", ], ) ``` ## Implementation A typical implementation will look like: ```cpp #include "my_library.h" #include "testing/file_test/file_test_base.h" namespace Carbon::Testing { namespace { class MyFileTest : public FileTestBase { public: using FileTestBase::FileTestBase; // Called as part of individual test executions. auto Run(const llvm::SmallVector& test_args, const llvm::SmallVector& test_files, FILE* input_stream, llvm::raw_pwrite_stream& output_stream, llvm::raw_pwrite_stream& error_stream) -> ErrorOr override { return MyFunctionality(test_args, input_stream, output_stream, error_stream); } // Provides arguments which are used in tests that don't provide ARGS. auto GetDefaultArgs() -> llvm::SmallVector override { return {"default_args", "%s"}; } }; } // namespace // Registers for the framework to construct the tests. CARBON_FILE_TEST_FACTORY(MyFileTest); } // namespace Carbon::Testing ``` ## Filename `fail_` prefixes When a run fails, information about what pieces failed are returned on `RunResult`. This affects whether a `fail_` prefix on the file is required, including in combination with split-file tests (using the `// --- ` comment marker). The main test file and any split-files must have a `fail_` prefix if and only if they have an associated error. An exception is that the main test file may omit `fail_` when it contains split-files that have a `fail_` prefix. ## Content replacement Some keywords can be inserted for content: - ``` [[@LSP::]] [[@LSP-CALL::]] [[@LSP-NOTIFY::]] [[@LSP-REPLY::]] ``` Produces JSON for an LSP method call, notification, or reply. Each includes the `Content-Length` header. The `:` is optional, and may be omitted. The difference between the replacements mirrors LSP calling conventions: - `LSP` does a minimal JSON format, and is primarily intended for testing errors. - `method`: Assigned from ``. - `LSP-CALL` maps to [Request Message](https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#requestMessage). - `method`: Assigned from ``. - `id`: Automatically incremented across calls. - `params`: Optionally assigned from ``. - `LSP-NOTIFY` maps to [Notification Message](https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#notificationMessage). - `method`: Assigned from ``. - `params`: Optionally assigned from ``. - `LSP-REPLY` maps to [Response Message](https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#responseMessage). - `id`: Assigned from ``. - `result`: Optionally assigned from ``. Additional substitutions are made to `` in the following cases: - ``` [[@LSP-NOTIFY:textDocument/didOpen:"textDocument": { "uri": "file:/", "text": "FROM_FILE_SPLIT" }]] ``` The keyword `FROM_FILE_SPLIT` is substituted with the content of the file split ``. All other properties are unchanged. - ``` [[@TEST_NAME]] ``` Replaces with the test name, which is the filename with the extension and any `fail_` or `todo_` prefixes removed. For split files, this is based on the split filename. The `[[@` string is reserved for future replacements, but `[[` is allowed in content (comment markers don't allow `[[`). ## Comment markers Settings in files are provided in comments, similar to `FileCheck` syntax. `bazel run :file_test -- --autoupdate` automatically constructs compatible CHECK:STDOUT: and CHECK:STDERR: lines. Supported comment markers are: - ``` // AUTOUDPATE // NOAUTOUPDATE ``` Controls whether the checks in the file will be autoupdated if `--autoupdate` is passed. Exactly one of these markers must be present. If the file uses splits, the marker must currently be before any splits. When autoupdating, `CHECK`s will be inserted starting below `AUTOUPDATE`. When a `CHECK` has line information, autoupdate will try to insert the `CHECK` immediately next to the line it's associated with, with stderr `CHECK`s preceding the line and stdout `CHECK`s following the line. When that happens, any subsequent `CHECK` lines without line information, or that refer to lines appearing earlier, will immediately follow. As an exception, if no `STDOUT` check line refers to any line in the test, all `STDOUT` check lines are placed at the end of the file instead of immediately after `AUTOUPDATE`. When using split files, if the last split file is named `// --- AUTOUPDATE-SPLIT`, all `CHECK`s will be added there; no line associations occur. - ``` // ARGS: ``` Provides a space-separated list of arguments, which will be passed to RunWithFiles as test_args. These are intended for use by the command as arguments. Supported replacements within arguments are: - `%s` Replaced with the list of files. Currently only allowed as a standalone argument, not a substring. - `%t` Replaced with `${TEST_TMPDIR}/temp_file`. - `%{identifier}` Replaces some implementation-specific identifier with a value. (Mappings provided by way of an optional `MyFileTest::GetArgReplacements`) `ARGS` can be specified at most once. If not provided, the `FileTestBase` child is responsible for providing default arguments. - ``` // EXTRA-ARGS: ``` Same as `ARGS`, including substitution behavior, but appends to the argument list instead of replacing it. `EXTRA-ARGS` can be repeated, and provided with `ARGS`. - ``` // INCLUDE-FILE: ``` Includes the specified file in the test's virtual file system and adds the included file's content to the current file's splits. Included files can use: - `// ARGS` - `// EXTRA-ARGS` - Included `EXTRA-ARGS` will come before `EXTRA-ARGS` in the main test file. - `// INCLUDE-FILE` - `// --- ` Included files will use `include_files/` for their name when a split isn't provided. This can be used to provide a minimal `Core` package (and `prelude` library) in toolchain tests. See the [toolchain docs](/toolchain/docs/adding_features.md) for how. - ``` // SET-CAPTURE-CONSOLE-OUTPUT ``` By default, stderr and stdout are expected to be piped through provided streams. Adding this causes the test's own stderr and stdout to be captured and added as well. This should be avoided because we are partly ensuring that streams are an API, but is helpful when wrapping Clang, where stderr is used directly. `SET-CAPTURE-CONSOLE-OUTPUT` can be specified at most once. - ``` // SET-CHECK-SUBSET ``` By default, all lines of output must have a `CHECK` match. Adding this as a option sets it so that non-matching lines are ignored. All provided `CHECK:STDOUT:` and `CHECK:STDERR:` lines must still have a match in output. `SET-CHECK-SUBSET`can be specified at most once. - ``` // --- ``` By default, all file content is provided to the test as a single file in `test_files`. Using this marker allows the file to be split into multiple files which will all be passed to `test_files`. Files are not created on disk; instead, content is passed in through the `fs` passed to `Run`. If the filename is `STDIN`, it will be provided as `input_stream` instead of in `test_files`. Currently, autoupdate can place `CHECK` lines in the `STDIN` split; use `AUTOUPDATE-SPLIT` to avoid that (see `AUTOUPDATE` for information). - ``` // CHECK:STDOUT: // CHECK:STDERR: ``` These provide a match for output from the command. See `SET-CHECK-SUBSET` for how to change from full to subset matching of output. Output line matchers may contain `[[@LINE+offset]` and `{{regex}}` syntaxes, similar to `FileCheck`. - ``` // TIP: ``` Tips like this are added by autoupdate, for example providing commands to run the test directly. Tips have no impact on validation; the marker informs autoupdate that it can update or remove them as needed. ================================================ FILE: testing/file_test/autoupdate.cpp ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #include "testing/file_test/autoupdate.h" #include #include #include #include "absl/strings/str_replace.h" #include "absl/strings/string_view.h" #include "common/check.h" #include "common/ostream.h" #include "common/raw_string_ostream.h" #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/StringExtras.h" #include "llvm/Support/FormatVariadic.h" #include "testing/base/file_helpers.h" namespace Carbon::Testing { // Converts a matched line number to an int, trimming whitespace. Returns 0 if // there is no line number, to assist early placement. static auto ParseLineNumber(absl::string_view matched_line_number) -> int { llvm::StringRef trimmed = matched_line_number; trimmed = trimmed.trim(); if (trimmed.empty()) { return 0; } // NOLINTNEXTLINE(google-runtime-int): API requirement. long long val; CARBON_CHECK(!llvm::getAsSignedInteger(trimmed, 10, val), "{0}", matched_line_number); return val; } FileTestAutoupdater::FileAndLineNumber::FileAndLineNumber( const LineNumberReplacement* replacement, int file_number, absl::string_view line_number) : replacement(replacement), file_number(file_number), line_number(ParseLineNumber(line_number)) {} auto FileTestAutoupdater::CheckLine::RemapLineNumbers( const llvm::DenseMap& file_to_number_map, const llvm::DenseMap, int>& output_line_remap, const llvm::SmallVector& new_last_line_numbers) -> void { // Only need to do remappings when there's a line number replacement. if (!replacement_) { return; } // Use a cursor for the line so that we can't keep matching the same // content, which may occur when we keep a literal line number. int line_offset = 0; while (true) { // Rebuild the cursor each time because we're editing the line, which // could cause a reallocation. absl::string_view line_cursor = line_; line_cursor.remove_prefix(line_offset); // Look for a line number to replace. There may be multiple, so we // repeatedly check. absl::string_view matched_filename; absl::string_view matched_line_number; if (replacement_->has_file) { RE2::PartialMatch(line_cursor, *replacement_->re, &matched_filename, &matched_line_number); } else { RE2::PartialMatch(line_cursor, *replacement_->re, &matched_line_number); } if (matched_line_number.empty()) { return; } // Map the matched filename to its file number. auto matched_file_number = file_number(); if (replacement_->has_file) { auto it = file_to_number_map.find(matched_filename); if (it != file_to_number_map.end()) { matched_file_number = it->second; } } // Calculate the new line number (possibly with new CHECK lines added, or // some removed). int old_line_number = ParseLineNumber(matched_line_number); int new_line_number = -1; if (auto remapped = output_line_remap.find({matched_file_number, old_line_number}); remapped != output_line_remap.end()) { // Map old non-check lines to their new line numbers. new_line_number = remapped->second; } else { // We assume unmapped references point to the end-of-file. new_line_number = new_last_line_numbers[matched_file_number]; } std::string replacement; if (matched_file_number == output_file_number_) { int offset = new_line_number - output_line_number_; // Update the line offset in the CHECK line. const char* offset_prefix = offset < 0 ? "" : "+"; replacement = llvm::formatv( replacement_->line_formatv.c_str(), llvm::formatv("[[@LINE{0}{1}]]", offset_prefix, offset)); } else { // If the CHECK was written to a different file from the file that it // refers to, leave behind an absolute line reference rather than a // cross-file offset. replacement = llvm::formatv(replacement_->line_formatv.c_str(), new_line_number); } auto line_number_offset = matched_line_number.data() - line_.data(); line_.replace(line_number_offset, matched_line_number.size(), replacement); // Resume matching from the end of the replacement line number. line_offset = line_number_offset + replacement.size(); } } auto FileTestAutoupdater::GetFileAndLineNumber( const llvm::DenseMap& file_to_number_map, int default_file_number, const std::string& check_line) -> FileAndLineNumber { for (const auto& replacement : line_number_replacements_) { if (replacement.has_file) { absl::string_view filename; absl::string_view line_number; if (RE2::PartialMatch(check_line, *replacement.re, &filename, &line_number)) { if (auto it = file_to_number_map.find(filename); it != file_to_number_map.end()) { return FileAndLineNumber(&replacement, it->second, line_number); } else { return FileAndLineNumber(default_file_number); } } } else { // There's no file association, so we only look at the line, and assume // it refers to the default file. absl::string_view line_number; if (RE2::PartialMatch(check_line, *replacement.re, &line_number)) { return FileAndLineNumber(&replacement, default_file_number, line_number); } } } return FileAndLineNumber(default_file_number); } auto FileTestAutoupdater::BuildCheckLines(llvm::StringRef output, bool is_stderr) -> CheckLines { if (output.empty()) { return CheckLines({}); } // %t substitution means we may see the temporary directory's path in output. std::filesystem::path tmpdir_path = GetTempDirectory(); llvm::StringRef tmpdir = tmpdir_path.native(); llvm::SmallVector lines(llvm::split(output, '\n')); // It's typical that output ends with a newline, but we don't want to add a // blank CHECK for it. if (lines.back().empty()) { lines.pop_back(); } auto label = is_stderr ? llvm::StringLiteral("STDERR") : llvm::StringLiteral("STDOUT"); // The default file number for when no specific file is found. int default_file_number = 0; CheckLineArray check_lines; for (const auto& line : lines) { // This code is relatively hot in our testing, and because when testing it // isn't run with an optimizer we benefit from making it use simple // constructs. For this reason, we avoid `llvm::formatv` and similar tools. std::string check_line; check_line.reserve(line.size() + label.size() + strlen("// CHECK:: ")); check_line.append("// CHECK:"); check_line.append(label); check_line.append(":"); if (!line.empty()) { check_line.append(" "); check_line.append(line); } // \r and \t are invisible characters worth marking. // {{ and [[ are autoupdate syntax which we need to escape. check_line = absl::StrReplaceAll(check_line, {{"\r", R"({{\r}})"}, {"\t", R"({{\t}})"}, {"{{", R"({{\{\{}})"}, {"[[", R"({{\[\[}})"}}); // Add an empty regex to call out end-of-line whitespace. if (check_line.ends_with(' ')) { check_line.append("{{}}"); } // Ignore mentions of the temporary directory in output. if (auto pos = check_line.find(tmpdir); pos != std::string::npos) { check_line.replace(pos, tmpdir.size(), "{{.+}}"); } do_extra_check_replacements_(check_line); if (check_line.empty()) { continue; } if (default_file_re_) { absl::string_view filename; if (RE2::PartialMatch(line, *default_file_re_, &filename)) { auto it = file_to_number_map_.find(filename); CARBON_CHECK(it != file_to_number_map_.end(), "default_file_re had unexpected match in '{0}' (`{1}`)", line, default_file_re_->pattern()); default_file_number = it->second; } } auto file_and_line = GetFileAndLineNumber(file_to_number_map_, default_file_number, check_line); check_lines.push_back(CheckLine(file_and_line, check_line)); } finalize_check_lines_(check_lines, is_stderr); return CheckLines(check_lines); } auto FileTestAutoupdater::AddRemappedNonCheckLine() -> void { new_lines_.push_back(non_check_line_); CARBON_CHECK(output_line_remap_ .insert({{non_check_line_->file_number(), non_check_line_->line_number()}, ++output_line_number_}) .second); } auto FileTestAutoupdater::AddTips() -> void { CARBON_CHECK(tips_.empty(), "Should only add tips once"); tips_.reserve(4); // This puts commands on a single line so that they can be easily copied. tips_.emplace_back("// TIP: To test this file alone, run:"); tips_.emplace_back("// TIP: " + test_command_); tips_.emplace_back("// TIP: To dump output, run:"); tips_.emplace_back("// TIP: " + dump_command_); for (const auto& tip : tips_) { new_lines_.push_back(&tip); ++output_line_number_; } } auto FileTestAutoupdater::ShouldAddCheckLine(const CheckLines& check_lines, bool to_file_end) const -> bool { return !autoupdate_split_file_ && check_lines.cursor != check_lines.lines.end() && (check_lines.cursor->file_number() < output_file_number_ || (check_lines.cursor->file_number() == output_file_number_ && (to_file_end || check_lines.cursor->line_number() <= non_check_line_->line_number()))); } auto FileTestAutoupdater::AddCheckLines(CheckLines& check_lines, bool to_file_end) -> void { for (; ShouldAddCheckLine(check_lines, to_file_end); ++check_lines.cursor) { new_lines_.push_back(check_lines.cursor); check_lines.cursor->SetOutputLine( to_file_end ? "" : non_check_line_->indent(), output_file_number_, ++output_line_number_); } } auto FileTestAutoupdater::FinishFile(bool is_last_file) -> void { bool include_stdout = any_attached_stdout_lines_ || is_last_file; // At the end of each file, print any remaining lines which are associated // with the file. if (ShouldAddCheckLine(stderr_, /*to_file_end=*/true) || (include_stdout && ShouldAddCheckLine(stdout_, /*to_file_end=*/true))) { // Ensure there's a blank line before any trailing CHECKs. if (!new_lines_.empty() && !new_lines_.back()->is_blank()) { new_lines_.push_back(&blank_line_); ++output_line_number_; } AddCheckLines(stderr_, /*to_file_end=*/true); if (include_stdout) { AddCheckLines(stdout_, /*to_file_end=*/true); } } new_last_line_numbers_.push_back(output_line_number_); } auto FileTestAutoupdater::StartSplitFile() -> void { // Advance the file. ++output_file_number_; output_line_number_ = 0; CARBON_CHECK(output_file_number_ == non_check_line_->file_number(), "Non-sequential file: {0}", non_check_line_->file_number()); // Each following file has precisely one split line. CARBON_CHECK(non_check_line_->line_number() < 1, "Expected a split line, got {0}", *non_check_line_); // The split line is ignored when calculating line counts. new_lines_.push_back(non_check_line_); // Add any file-specific but line-unattached STDOUT messages here. STDERR is // handled through the main loop because it's before the next line. if (any_attached_stdout_lines_) { AddCheckLines(stdout_, /*to_file_end=*/false); } ++non_check_line_; } auto FileTestAutoupdater::Run(bool dry_run) -> bool { // Print everything until the autoupdate line. while (non_check_line_->line_number() != autoupdate_line_number_) { CARBON_CHECK(non_check_line_ != non_check_lines_.end() && non_check_line_->file_number() == 0, "Missed autoupdate?"); AddRemappedNonCheckLine(); ++non_check_line_; } // Add the AUTOUPDATE line along with any early STDERR lines, so that the // initial batch of CHECK lines have STDERR before STDOUT. This also ensures // we don't insert a blank line before the STDERR checks if there are no more // lines after AUTOUPDATE. AddRemappedNonCheckLine(); AddTips(); if (!autoupdate_split_file_) { AddCheckLines(stderr_, /*to_file_end=*/false); if (any_attached_stdout_lines_) { AddCheckLines(stdout_, /*to_file_end=*/false); } } ++non_check_line_; // Loop through remaining content. while (non_check_line_ != non_check_lines_.end()) { if (output_file_number_ < non_check_line_->file_number()) { FinishFile(/*is_last_file=*/false); StartSplitFile(); if (output_file_number_ == autoupdate_split_file_) { break; } continue; } // STDERR check lines are placed before the line they refer to, or as // early as possible if they don't refer to a line. Include all STDERR // lines until we find one that wants to go later in the file. AddCheckLines(stderr_, /*to_file_end=*/false); AddRemappedNonCheckLine(); // STDOUT check lines are placed after the line they refer to, or at the // end of the file if none of them refers to a line. if (any_attached_stdout_lines_) { AddCheckLines(stdout_, /*to_file_end=*/false); } ++non_check_line_; } // Clear out the autoupdate split, which would otherwise prevent check lines // being written to the autoupdate file. When autoupdate_split_file_ was set, // this will result in all check lines (and only check lines) being added to // the split by FinishFile. We don't use autoupdate_split_file_ past this // point. autoupdate_split_file_ = std::nullopt; FinishFile(/*is_last_file=*/true); for (auto& check_line : stdout_.lines) { check_line.RemapLineNumbers(file_to_number_map_, output_line_remap_, new_last_line_numbers_); } for (auto& check_line : stderr_.lines) { check_line.RemapLineNumbers(file_to_number_map_, output_line_remap_, new_last_line_numbers_); } // Generate the autoupdated file. RawStringOstream new_content_stream; for (const auto& line : new_lines_) { new_content_stream << *line << '\n'; } std::string new_content = new_content_stream.TakeStr(); // Update the file on disk if needed. if (new_content == input_content_) { return false; } if (!dry_run) { std::ofstream out(file_test_path_); out << new_content; } return true; } } // namespace Carbon::Testing ================================================ FILE: testing/file_test/autoupdate.h ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #ifndef CARBON_TESTING_FILE_TEST_AUTOUPDATE_H_ #define CARBON_TESTING_FILE_TEST_AUTOUPDATE_H_ #include #include #include "common/check.h" #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/SmallVector.h" #include "llvm/ADT/StringRef.h" #include "re2/re2.h" #include "testing/file_test/line.h" namespace Carbon::Testing { class FileTestAutoupdater { public: struct LineNumberReplacement { bool has_file; // The line replacement. The pattern should match lines. If has_file, // pattern should have a file and line group; otherwise, only a line group, // but default_file_re should be provided. // // Uses shared_ptr for storage in SmallVector. std::shared_ptr re; // line_formatv should provide {0} to substitute with [[@LINE...]] deltas. std::string line_formatv; }; // The file and line number that a CHECK line refers to, and the // replacement from which they were determined, if any. struct FileAndLineNumber { explicit FileAndLineNumber(int file_number) : file_number(file_number) {} explicit FileAndLineNumber(const LineNumberReplacement* replacement, int file_number, absl::string_view line_number); const LineNumberReplacement* replacement = nullptr; int file_number; int line_number = -1; }; // A CHECK line which is integrated into autoupdate output. // // `final` because we use pointer arithmetic on this type. class CheckLine final : public FileTestLineBase { public: explicit CheckLine(FileAndLineNumber file_and_line_number, std::string line) : FileTestLineBase(file_and_line_number.file_number, file_and_line_number.line_number), replacement_(file_and_line_number.replacement), line_(std::move(line)) {} auto Print(llvm::raw_ostream& out) const -> void override { out << indent_ << line_; } // When the location of the CHECK in output is known, we can set the indent // and its line. auto SetOutputLine(llvm::StringRef indent, int output_file_number, int output_line_number) -> void { indent_ = indent; output_file_number_ = output_file_number; output_line_number_ = output_line_number; } // When the location of all lines in a file are known, we can set the line // offset based on the target line. auto RemapLineNumbers( const llvm::DenseMap& file_to_number_map, const llvm::DenseMap, int>& output_line_remap, const llvm::SmallVector& new_last_line_numbers) -> void; auto line() const -> llvm::StringRef { return line_; } auto is_blank() const -> bool override { return false; } private: const LineNumberReplacement* replacement_; std::string line_; llvm::StringRef indent_; int output_file_number_ = -1; int output_line_number_ = -1; }; using CheckLineArray = llvm::SmallVector; explicit FileTestAutoupdater( const std::filesystem::path& file_test_path, std::string test_command, std::string dump_command, llvm::StringRef input_content, const llvm::SmallVector& filenames, int autoupdate_line_number, bool autoupdate_split, const llvm::SmallVector& non_check_lines, llvm::StringRef actual_stdout, llvm::StringRef actual_stderr, const std::optional& default_file_re, const llvm::SmallVector& line_number_replacements, std::functionvoid> do_extra_check_replacements, std::functionvoid> finalize_check_lines) : file_test_path_(file_test_path), test_command_(std::move(test_command)), dump_command_(std::move(dump_command)), input_content_(input_content), autoupdate_line_number_(autoupdate_line_number), non_check_lines_(non_check_lines), default_file_re_(default_file_re), line_number_replacements_(line_number_replacements), do_extra_check_replacements_(std::move(do_extra_check_replacements)), finalize_check_lines_(std::move(finalize_check_lines)), autoupdate_split_file_( autoupdate_split ? std::optional(filenames.size()) : std::nullopt), file_to_number_map_(BuildFileToNumberMap(filenames)), // BuildCheckLines should only be called after other member // initialization. stdout_(BuildCheckLines(actual_stdout, /*is_stderr=*/false)), stderr_(BuildCheckLines(actual_stderr, /*is_stderr=*/true)), any_attached_stdout_lines_(llvm::any_of( stdout_.lines, [&](const CheckLine& line) { return line.line_number() != -1; })), non_check_line_(non_check_lines_.begin()) { for (const auto& replacement : line_number_replacements_) { CARBON_CHECK(replacement.has_file || default_file_re_, "For replacement with pattern `{0}` to have has_file=false, " "override GetDefaultFileRE.", replacement.re->pattern()); CARBON_CHECK(replacement.re->ok(), "Invalid line replacement RE2: {0}", replacement.re->error()); } } // Automatically updates CHECKs in the provided file when dry_run=false. // Returns true if generated file content differs from actual file content. auto Run(bool dry_run) -> bool; private: // A TIP line added by autoupdate. Not associated with any line in output. class TipLine : public FileTestLineBase { public: explicit TipLine(std::string line) : FileTestLineBase(-1, -1), line_(std::move(line)) {} auto Print(llvm::raw_ostream& out) const -> void override { out << line_; } auto is_blank() const -> bool override { return line_.empty(); } private: std::string line_; }; // Clusters information for stdout and stderr. struct CheckLines { explicit CheckLines(CheckLineArray lines) : lines(std::move(lines)), cursor(this->lines.begin()) {} // The full list of check lines. CheckLineArray lines; // An iterator into check_lines. CheckLine* cursor; }; // Looks for the patterns in the line. Returns the first match, or defaulted // information if not found. auto GetFileAndLineNumber( const llvm::DenseMap& file_to_number_map, int default_file_number, const std::string& check_line) -> FileAndLineNumber; // Builds a mapping from file name to file number. auto BuildFileToNumberMap(const llvm::SmallVector& filenames) -> llvm::DenseMap { llvm::DenseMap file_to_number_map; for (auto [number, name] : llvm::enumerate(filenames)) { file_to_number_map.insert({name, number}); } return file_to_number_map; } // Builds CheckLine lists for autoupdate. auto BuildCheckLines(llvm::StringRef output, bool is_stderr) -> CheckLines; // Adds a non-check line to the new_lines and output_line_remap. The caller // still needs to advance the cursor when ready. auto AddRemappedNonCheckLine() -> void; // Adds TIP lines for file_test usage. auto AddTips() -> void; // Returns true if there's a CheckLine that should be added at // `to_line_number`. auto ShouldAddCheckLine(const CheckLines& check_lines, bool to_file_end) const -> bool; // Adds check_lines until output reaches: // - If not to_file_end, non_check_line. // - If to_file_end, the end of the file. auto AddCheckLines(CheckLines& check_lines, bool to_file_end) -> void; // Adds remaining check lines for the current file. stderr is always included, // but stdout is only included when either any_attached_stdout_lines_ or // is_last_file is true. auto FinishFile(bool is_last_file) -> void; // Starts a new split file, updating file and line numbers. Advances past the // split line. auto StartSplitFile() -> void; // Passed-in state. const std::filesystem::path& file_test_path_; std::string test_command_; std::string dump_command_; llvm::StringRef input_content_; int autoupdate_line_number_; const llvm::SmallVector& non_check_lines_; const std::optional& default_file_re_; const llvm::SmallVector& line_number_replacements_; std::functionvoid> do_extra_check_replacements_; std::functionvoid> finalize_check_lines_; // If we have an autoupdate split that still needs to be processed, the file // number of the autoupdate split. Otherwise, this is nullopt. std::optional autoupdate_split_file_; // Generated TIP lines, from AddTips. llvm::SmallVector tips_; // Mapping from file names to file numbers. llvm::DenseMap file_to_number_map_; // The constructed CheckLine list and cursor. CheckLines stdout_; CheckLines stderr_; // Whether any stdout lines have an associated line number. bool any_attached_stdout_lines_; // Iterators for the main Run loop. const FileTestLine* non_check_line_; // Tracks the new last line numbers for each file. llvm::SmallVector new_last_line_numbers_; // A reusable blank line. new_lines_ can contain a reference back to it. const FileTestLine blank_line_ = FileTestLine(-1, -1, ""); // Stitched-together content. llvm::SmallVector new_lines_; // Maps {file_number, original line number} to a new line number. llvm::DenseMap, int> output_line_remap_; // The current output file number; mainly used for tracking progression. int output_file_number_ = 0; // The current output line number in stitched content. int output_line_number_ = 0; }; } // namespace Carbon::Testing #endif // CARBON_TESTING_FILE_TEST_AUTOUPDATE_H_ ================================================ FILE: testing/file_test/autoupdate_testdata.sh ================================================ #!/usr/bin/env bash # Part of the Carbon Language project, under the Apache License v2.0 with LLVM # Exceptions. See /LICENSE for license information. # SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception "$(dirname "$0")/../../scripts/run_bazel.py" \ run --experimental_convenience_symlinks=ignore \ --ui_event_filters=-info,-stdout,-stderr,-finish \ //testing/file_test:file_test_base_test -- --autoupdate "$@" ================================================ FILE: testing/file_test/file_test_base.cpp ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // Implementation-wise, this: // // - Uses the registered `FileTestFactory` to construct `FileTestBase` // instances. // - Constructs a `FileTestCase` that wraps each `FileTestBase` instance to // register with googletest, and to provide the actual `TestBody`. // - Using `FileTestEventListener`, runs tests in parallel prior to normal // googletest execution. // - This is required to support `--gtest_filter` and access `should_run`. // - Runs each `FileTestBase` instance to cache the `TestFile` on // `FileTestInfo`. // - Determines whether autoupdate would make changes, autoupdating if // requested. // - When googletest would normally execute the test, `FileTestCase::TestBody` // instead uses the cached state on `FileTestInfo`. // - This only occurs when neither autoupdating nor dumping output. #include "testing/file_test/file_test_base.h" #include #include #include #include #include #include #include #include #include #include #include #include #include "absl/flags/flag.h" #include "absl/flags/parse.h" #include "absl/strings/str_join.h" #include "common/build_data.h" #include "common/check.h" #include "common/error.h" #include "common/exe_path.h" #include "common/init_llvm.h" #include "common/raw_string_ostream.h" #include "llvm/ADT/StringExtras.h" #include "llvm/Support/CrashRecoveryContext.h" #include "llvm/Support/FormatVariadic.h" #include "llvm/Support/MemoryBuffer.h" #include "llvm/Support/PrettyStackTrace.h" #include "llvm/Support/Process.h" #include "llvm/Support/ThreadPool.h" #include "testing/base/unified_diff_matcher.h" #include "testing/file_test/autoupdate.h" #include "testing/file_test/run_test.h" #include "testing/file_test/test_file.h" ABSL_FLAG(std::vector, file_tests, {}, "A comma-separated list of repo-relative names of test files. " "Similar to and overrides `--gtest_filter`, but doesn't require the " "test class name to be known."); ABSL_FLAG(bool, autoupdate, false, "Instead of verifying files match test output, autoupdate files " "based on test output."); ABSL_FLAG(unsigned int, threads, 0, "Number of threads to use when autoupdating tests, or 0 to " "automatically determine a thread count."); ABSL_FLAG(bool, dump_output, false, "Instead of verifying files match test output, directly dump output " "to stderr."); ABSL_FLAG(int, print_slowest_tests, 5, "The number of tests to print when showing slowest tests. Set to 0 " "to disabling printing. Set to -1 to print all tests."); namespace Carbon::Testing { // Information for a test case. struct FileTestInfo { // The name. std::string test_name; // A factory function for creating the test object. std::functionstd::unique_ptr> factory_fn; // gtest's information about the test. ::testing::TestInfo* registered_test; // The test result, set after running. std::optional> test_result; // Whether running autoupdate would change (or when autoupdating, already // changed) the test file. This may be true even if output passes test // expectations. bool autoupdate_differs = false; // Time spent in the test total, including processing and autoupdate. std::chrono::milliseconds elapsed_ms = std::chrono::milliseconds(0); }; // Adapts a `FileTestBase` instance to gtest for outputting results. class FileTestCase : public testing::Test { public: explicit FileTestCase(FileTestInfo* test_info) : test_info_(test_info) {} // Runs a test and compares output. This keeps output split by line so that // issues are a little easier to identify by the different line. auto TestBody() -> void final; private: FileTestInfo* test_info_; }; // Splits outputs to string_view because gtest handles string_view by default. static auto SplitOutput(llvm::StringRef output) -> llvm::SmallVector { if (output.empty()) { return {}; } llvm::SmallVector lines; llvm::StringRef(output).split(lines, "\n"); return llvm::SmallVector(lines.begin(), lines.end()); } // Verify that the success and `fail_` prefix use correspond. Separately handle // both cases for clearer test failures. static auto CompareFailPrefix(llvm::StringRef filename, bool success) -> void { if (success) { EXPECT_FALSE(filename.starts_with("fail_")) << "`" << filename << "` succeeded; if success is expected, remove the `fail_` " "prefix."; } else { EXPECT_TRUE(filename.starts_with("fail_")) << "`" << filename << "` failed; if failure is expected, add the `fail_` prefix."; } } // Returns the requested bazel command string for the given execution mode. auto FileTestBase::GetBazelCommand(BazelMode mode) -> std::string { RawStringOstream args; args << "bazel " << ((mode == BazelMode::Test) ? "test" : "run") << " " << BuildData::TargetName << " "; switch (mode) { case BazelMode::Autoupdate: args << "-- --autoupdate "; break; case BazelMode::Dump: args << "-- --dump_output "; break; case BazelMode::Test: args << "--test_arg="; break; } args << "--file_tests="; args << test_name(); return args.TakeStr(); } // Runs the FileTestAutoupdater, returning the result. static auto RunAutoupdater(FileTestBase* test_base, const TestFile& test_file, bool dry_run) -> bool { if (!test_file.autoupdate_line_number) { return false; } llvm::SmallVector filenames; filenames.reserve(test_file.non_check_lines.size()); if (test_file.has_splits) { // There are splits, so we provide an empty name for the first file. filenames.push_back({}); } for (const auto& file : test_file.file_splits) { filenames.push_back(file.filename); } llvm::ArrayRef expected_filenames = filenames; if (filenames.size() > 1) { expected_filenames.consume_front(); } return FileTestAutoupdater( std::filesystem::absolute(test_base->test_name().str()), test_base->GetBazelCommand(FileTestBase::BazelMode::Test), test_base->GetBazelCommand(FileTestBase::BazelMode::Dump), test_file.input_content, filenames, *test_file.autoupdate_line_number, test_file.autoupdate_split, test_file.non_check_lines, test_file.actual_stdout, test_file.actual_stderr, test_base->GetDefaultFileRE(expected_filenames), test_base->GetLineNumberReplacements(expected_filenames), [&](std::string& line) { test_base->DoExtraCheckReplacements(line); }, [&](FileTestBase::CheckLineArray& lines, bool is_stderr) { test_base->FinalizeCheckLines(lines, is_stderr); }) .Run(dry_run); } auto FileTestCase::TestBody() -> void { if (absl::GetFlag(FLAGS_autoupdate) || absl::GetFlag(FLAGS_dump_output)) { return; } CARBON_CHECK(test_info_->test_result, "Expected test to be run prior to TestBody: {0}", test_info_->test_name); ASSERT_TRUE(test_info_->test_result->ok()) << test_info_->test_result->error(); auto test_filename = std::filesystem::path(test_info_->test_name).filename(); // Check success/failure against `fail_` prefixes. TestFile& test_file = **(test_info_->test_result); if (test_file.run_result.per_file_success.empty()) { CompareFailPrefix(test_filename.string(), test_file.run_result.success); } else { bool require_overall_failure = false; for (const auto& [filename, success] : test_file.run_result.per_file_success) { CompareFailPrefix(filename, success); if (!success) { require_overall_failure = true; } } if (require_overall_failure) { EXPECT_FALSE(test_file.run_result.success) << "There is a per-file failure expectation, so the overall result " "should have been a failure."; } else { // Individual files all succeeded, so the prefix is enforced on the main // test file. CompareFailPrefix(test_filename.string(), test_file.run_result.success); } } // Check results. Include a reminder for NOAUTOUPDATE tests. std::unique_ptr scoped_trace; if (!test_file.autoupdate_line_number) { scoped_trace = std::make_unique( __FILE__, __LINE__, "This file is NOAUTOUPDATE, so expected differences require manual " "updates."); } if (test_file.check_subset) { EXPECT_THAT(SplitOutput(test_file.actual_stdout), IsSupersetOf(test_file.expected_stdout)) << "Actual text:\n" << test_file.actual_stdout; EXPECT_THAT(SplitOutput(test_file.actual_stderr), IsSupersetOf(test_file.expected_stderr)) << "Actual text:\n" << test_file.actual_stderr; } else { EXPECT_THAT(SplitOutput(test_file.actual_stdout), ElementsAreArrayWithUnifiedDiff(test_file.expected_stdout)); EXPECT_THAT(SplitOutput(test_file.actual_stderr), ElementsAreArrayWithUnifiedDiff(test_file.expected_stderr)); } if (HasFailure()) { llvm::errs() << "\nTo test this file alone, run:\n " << test_info_->factory_fn()->GetBazelCommand( FileTestBase::BazelMode::Test) << "\n\n"; if (!test_file.autoupdate_line_number) { llvm::errs() << "\nThis test is NOAUTOUPDATE.\n\n"; } } if (test_info_->autoupdate_differs) { ADD_FAILURE() << "Autoupdate would make changes to the file content. Run:\n" << test_info_->factory_fn()->GetBazelCommand( FileTestBase::BazelMode::Autoupdate); } } auto FileTestBase::GetLineNumberReplacements( llvm::ArrayRef filenames) const -> llvm::SmallVector { return {{.has_file = true, .re = std::make_shared( llvm::formatv(R"(({0}):(\d+)?)", llvm::join(filenames, "|"))), .line_formatv = R"({0})"}}; } // If `--file_tests` is set, transform it into a `--gtest_filter`. static auto MaybeApplyFileTestsFlag(llvm::StringRef factory_name) -> void { if (absl::GetFlag(FLAGS_file_tests).empty()) { return; } RawStringOstream filter; llvm::ListSeparator sep(":"); for (const auto& file : absl::GetFlag(FLAGS_file_tests)) { filter << sep << factory_name << "." << file; } absl::SetFlag(&FLAGS_gtest_filter, filter.TakeStr()); } // Loads tests from the manifest file, and registers them for execution. The // vector is taken as an output parameter so that the address of entries is // stable for the factory. static auto RegisterTests(FileTestFactory* test_factory, llvm::StringRef exe_path, llvm::SmallVectorImpl& tests) -> ErrorOr { // Prepare the vector first, so that the location of entries won't change. for (auto& test_name : GetFileTestManifest()) { tests.push_back({.test_name = test_name}); } // Amend entries with factory functions. for (auto& test : tests) { const std::string& test_name = test.test_name; test.factory_fn = [test_factory, exe_path, &test_name]() { return test_factory->factory_fn(exe_path, test_name); }; test.registered_test = testing::RegisterTest( test_factory->name, test_name.c_str(), nullptr, test_name.c_str(), __FILE__, __LINE__, [&test]() { return new FileTestCase(&test); }); } return Success(); } // Implements the parallel test execution through gtest's listener support. class FileTestEventListener : public testing::EmptyTestEventListener { public: explicit FileTestEventListener(llvm::MutableArrayRef tests) : tests_(tests) {} // Runs test during start, after `should_run` is initialized. This is // multi-threaded to get extra speed. auto OnTestProgramStart(const testing::UnitTest& /*unit_test*/) -> void override; private: llvm::MutableArrayRef tests_; }; // Returns true if the main thread should be used to run tests. This is if // either --dump_output is specified, or only 1 thread is needed to run tests. static auto SingleThreaded(llvm::ArrayRef tests) -> bool { if (absl::GetFlag(FLAGS_dump_output) || absl::GetFlag(FLAGS_threads) == 1) { return true; } bool found_test_to_run = false; for (const auto& test : tests) { if (!test.registered_test->should_run()) { continue; } if (found_test_to_run) { // At least two tests will run, so multi-threaded. return false; } // Found the first test to run. found_test_to_run = true; } // 0 or 1 test will be run, so single-threaded. return true; } // Runs the test in the section that would be inside a lock, possibly inside a // CrashRecoveryContext. static auto RunSingleTestHelper(FileTestInfo& test, FileTestBase& test_instance) -> void { Timer timer; // Add a crash trace entry with the single-file test command. std::string test_command = test_instance.GetBazelCommand(FileTestBase::BazelMode::Test); llvm::PrettyStackTraceString stack_trace_entry(test_command.c_str()); if (auto err = RunTestFile(test_instance, absl::GetFlag(FLAGS_dump_output), **test.test_result); !err.ok()) { test.test_result = std::move(err).error(); } test.elapsed_ms += timer.elapsed_ms(); } // Runs a single test. Uses a CrashRecoveryContext, and returns false on a // crash. For test_elapsed_ms, try to exclude time spent waiting on // output_mutex. static auto RunSingleTest(FileTestInfo& test, bool single_threaded, std::mutex& output_mutex) -> bool { std::unique_ptr test_instance(test.factory_fn()); if (absl::GetFlag(FLAGS_dump_output)) { std::unique_lock lock(output_mutex); llvm::errs() << "\n--- Dumping: " << test.test_name << "\n\n"; } else if (single_threaded) { std::unique_lock lock(output_mutex); llvm::errs() << "\nTEST: " << test.test_name << ' '; } // Load expected output. Timer process_timer; test.test_result = ProcessTestFile(test_instance->test_name(), absl::GetFlag(FLAGS_autoupdate)); test.elapsed_ms = process_timer.elapsed_ms(); if (test.test_result->ok()) { // Execution must be serialized for either serial tests or console // output. std::unique_lock output_lock; if ((*test.test_result)->capture_console_output || !test_instance->AllowParallelRun()) { output_lock = std::unique_lock(output_mutex); } if (single_threaded) { RunSingleTestHelper(test, *test_instance); } else { // Use a crash recovery context to try to get a stack trace when // multiple threads may crash in parallel, which otherwise leads to the // program aborting without printing a stack trace. llvm::CrashRecoveryContext crc; crc.DumpStackAndCleanupOnFailure = true; if (!crc.RunSafely([&] { RunSingleTestHelper(test, *test_instance); })) { return false; } } } if (!test.test_result->ok()) { std::unique_lock lock(output_mutex); if (!single_threaded) { llvm::errs() << "\n" << test.test_name << ": "; } llvm::errs() << test.test_result->error().message() << "\n"; return true; } Timer autoupdate_timer; test.autoupdate_differs = RunAutoupdater(test_instance.get(), **test.test_result, /*dry_run=*/!absl::GetFlag(FLAGS_autoupdate)); test.elapsed_ms += autoupdate_timer.elapsed_ms(); std::unique_lock lock(output_mutex); if (absl::GetFlag(FLAGS_dump_output)) { llvm::outs().flush(); const TestFile& test_file = **test.test_result; llvm::errs() << "\n--- Exit with success: " << (test_file.run_result.success ? "true" : "false") << "\n--- Autoupdate differs: " << (test.autoupdate_differs ? "true" : "false") << "\n"; } else { llvm::errs() << (test.autoupdate_differs ? "!" : "."); } return true; } auto FileTestEventListener::OnTestProgramStart( const testing::UnitTest& /*unit_test*/) -> void { bool single_threaded = SingleThreaded(tests_); std::unique_ptr pool; if (single_threaded) { pool = std::make_unique(); } else { // Enable the CRC for use in `RunSingleTest`. llvm::CrashRecoveryContext::Enable(); llvm::ThreadPoolStrategy thread_strategy = { .ThreadsRequested = absl::GetFlag(FLAGS_threads), // Disable hyper threads to reduce contention. .UseHyperThreads = false}; pool = std::make_unique(thread_strategy); } if (!absl::GetFlag(FLAGS_dump_output)) { llvm::errs() << "Running tests with " << pool->getMaxConcurrency() << " thread(s)\n"; } // Guard access to output (stdout and stderr). std::mutex output_mutex; std::atomic crashed = false; Timer all_timer; int run_count = 0; for (auto& test : tests_) { if (!test.registered_test->should_run()) { continue; } ++run_count; pool->async([&] { // If any thread crashed, don't try running more. if (crashed) { return; } if (!RunSingleTest(test, single_threaded, output_mutex)) { crashed = true; } }); } pool->wait(); if (crashed) { // Abort rather than returning so that we don't get a LeakSanitizer report. // We expect to have leaked memory if one or more of our tests crashed. std::abort(); } // Calculate the total test time. auto all_elapsed_ms = all_timer.elapsed_ms(); auto total_elapsed_ms = std::chrono::milliseconds(0); for (auto& test : tests_) { total_elapsed_ms += test.elapsed_ms; } llvm::errs() << "\nRan " << run_count << " tests in " << all_elapsed_ms.count() << " ms wall time, " << total_elapsed_ms.count() << " ms across threads\n"; // When there are multiple tests, give additional timing details, particularly // slowest tests. auto print_slowest_tests = absl::GetFlag(FLAGS_print_slowest_tests); if (run_count > 1 && print_slowest_tests != 0) { // Sort in a copy so that `FileTestCase` pointers to the original tests // remain stable. llvm::SmallVector sorted_tests( llvm::make_pointer_range(tests_)); llvm::sort(sorted_tests, [](const FileTestInfo* lhs, const FileTestInfo* rhs) { return lhs->elapsed_ms > rhs->elapsed_ms; }); llvm::errs() << " Slowest tests:\n"; int count = print_slowest_tests > 0 ? print_slowest_tests : run_count; for (const auto* test : llvm::ArrayRef(sorted_tests).take_front(count)) { std::chrono::milliseconds run_ms(0); if (test->test_result && test->test_result->ok()) { run_ms = test->test_result.value()->run_elapsed_ms; } llvm::errs() << " - " << test->test_name << ": " << test->elapsed_ms.count() << " ms, " << run_ms.count() << " ms in Run\n"; } } } // Implements main() within the Carbon::Testing namespace for convenience. static auto Main(int argc, char** argv) -> ErrorOr { // Default to brief because we expect lots of tests, and `FileTestBase` // provides some summaries. Note `--test_arg=--gtest_brief=0` works to restore // output. absl::SetFlag(&FLAGS_gtest_brief, 1); Carbon::InitLLVM init_llvm(argc, argv); testing::InitGoogleTest(&argc, argv); auto args = absl::ParseCommandLine(argc, argv); if (args.size() > 1) { ErrorBuilder b; b << "Unexpected arguments:"; for (char* arg : llvm::ArrayRef(args).drop_front()) { b << " " << FormatEscaped(arg); } return b; } std::string exe_path = FindExecutablePath(argv[0]); // Tests might try to read from stdin. Ensure those reads fail by closing // stdin and reopening it as /dev/null. Note that STDIN_FILENO doesn't exist // on Windows, but POSIX requires it to be 0. if (std::error_code error = llvm::sys::Process::SafelyCloseFileDescriptor(0)) { return Error("Unable to close standard input: " + error.message()); } if (std::error_code error = llvm::sys::Process::FixupStandardFileDescriptors()) { return Error("Unable to correct standard file descriptors: " + error.message()); } if (absl::GetFlag(FLAGS_autoupdate) && absl::GetFlag(FLAGS_dump_output)) { return Error("--autoupdate and --dump_output are mutually exclusive."); } auto test_factory = GetFileTestFactory(); MaybeApplyFileTestsFlag(test_factory.name); // Inline 0 entries because it will always be too large to store on the stack. llvm::SmallVector tests; CARBON_RETURN_IF_ERROR(RegisterTests(&test_factory, exe_path, tests)); testing::TestEventListeners& listeners = testing::UnitTest::GetInstance()->listeners(); if (absl::GetFlag(FLAGS_autoupdate) || absl::GetFlag(FLAGS_dump_output)) { // Suppress all of the default output. delete listeners.Release(listeners.default_result_printer()); } // Use a listener to run tests in parallel. listeners.Append(new FileTestEventListener(tests)); return RUN_ALL_TESTS(); } } // namespace Carbon::Testing auto main(int argc, char** argv) -> int { if (auto result = Carbon::Testing::Main(argc, argv); result.ok()) { return *result; } else { llvm::errs() << result.error() << "\n"; return EXIT_FAILURE; } } ================================================ FILE: testing/file_test/file_test_base.h ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #ifndef CARBON_TESTING_FILE_TEST_FILE_TEST_BASE_H_ #define CARBON_TESTING_FILE_TEST_FILE_TEST_BASE_H_ #include #include #include #include #include "common/error.h" #include "common/ostream.h" #include "llvm/ADT/SmallString.h" #include "llvm/ADT/SmallVector.h" #include "llvm/ADT/StringMap.h" #include "llvm/ADT/StringRef.h" #include "llvm/Support/VirtualFileSystem.h" #include "testing/file_test/autoupdate.h" #include "testing/file_test/manifest.h" namespace Carbon::Testing { // A framework for testing files. See README.md for documentation. class FileTestBase { public: // Provided for child class convenience. using LineNumberReplacement = FileTestAutoupdater::LineNumberReplacement; using CheckLineArray = FileTestAutoupdater::CheckLineArray; // The result of Run(), used to detect errors. Failing test files should be // named with a `fail_` prefix to indicate an expectation of failure. // // If per_file_success is empty: // - The main file has a `fail_` prefix if !success. // - The prefix of split files is unused. // // If per_file_success is non-empty: // - Each file has a `fail_` prefix if !per_file_success[i].second. // - Files may be in per_file_success that aren't part of the main test // file. This allows tracking success in handling files that are // well-known, such as standard libraries. It is still the responsibility // of callers to use a `fail_` prefix if !per_file_success[i].second. // - If any file has a `fail_` prefix, success must be false, and the prefix // of the main file is unused. // - If no file has a `fail_` prefix, the main file has a `fail_` prefix if // !success. struct RunResult { bool success; // Per-file success results. May be empty. llvm::SmallVector> per_file_success; }; explicit FileTestBase(llvm::StringRef test_name) : test_name_(test_name) {} virtual ~FileTestBase() = default; // Implemented by children to run the test. The framework will validate the // content written to `output_stream` and `error_stream`. Children should use // `fs` for file content, and may add more files. // // If there is a split test file named "STDIN", then its contents will be // provided at `input_stream` instead of `fs`. Otherwise, `input_stream` will // be null. // // This cannot be used for test expectations, such as EXPECT_TRUE. // // The return value should be an error if there was an abnormal error, and // RunResult otherwise. virtual auto Run(const llvm::SmallVector& test_args, llvm::IntrusiveRefCntPtr& fs, FILE* input_stream, llvm::raw_pwrite_stream& output_stream, llvm::raw_pwrite_stream& error_stream) const -> ErrorOr = 0; // Returns default arguments. Only called when a file doesn't set ARGS. virtual auto GetDefaultArgs() const -> llvm::SmallVector = 0; // Returns a map of string replacements to implement `%{key}` -> `value` in // arguments. virtual auto GetArgReplacements() const -> llvm::StringMap { return {}; } // Returns a regex to match the default file when a line may not be present. // May return `std::nullopt` if unused. If `GetLineNumberReplacements` returns // an entry with `has_file=false`, this is required. virtual auto GetDefaultFileRE(llvm::ArrayRef /*filenames*/) const -> std::optional { return std::nullopt; } // Returns replacement information for line numbers. See LineReplacement for // construction. virtual auto GetLineNumberReplacements( llvm::ArrayRef filenames) const -> llvm::SmallVector; // Optionally allows children to provide extra replacements for autoupdate. virtual auto DoExtraCheckReplacements(std::string& /*check_line*/) const -> void {} // Optionally allows children to perform tweaks on check lines before // they are merged into the output file. virtual auto FinalizeCheckLines(CheckLineArray& /*check_lines*/, bool /*is_stderr*/) const -> void {} // Whether to allow running the test in parallel, particularly for autoupdate. // This can be overridden to force some tests to be run serially. At any given // time, all parallel tests and a single non-parallel test will be allowed to // run. virtual auto AllowParallelRun() const -> bool { return true; } // Modes for GetBazelCommand. enum class BazelMode : uint8_t { Autoupdate, Dump, Test, }; // Returns the requested bazel command string for the given execution mode. auto GetBazelCommand(BazelMode mode) -> std::string; auto test_name() const -> llvm::StringRef { return test_name_; } private: llvm::StringRef test_name_; }; // Aggregate a name and factory function for tests using this framework. struct FileTestFactory { // The test fixture name. const char* name; // A factory function for tests. std::functionstd::unique_ptr> factory_fn; }; // Must be implemented by the individual file_test to initialize tests. // // We can't use INSTANTIATE_TEST_CASE_P because of ordering issues between // container initialization and test instantiation by InitGoogleTest, but this // also allows us more flexibility in execution. // // The `CARBON_FILE_TEST_FACTOR` macro below provides a standard, convenient way // to implement this function. extern auto GetFileTestFactory() -> FileTestFactory; // Provides a standard GetFileTestFactory implementation. #define CARBON_FILE_TEST_FACTORY(Name) \ auto GetFileTestFactory() -> FileTestFactory { \ return {#Name, [](llvm::StringRef exe_path, llvm::StringRef test_name) { \ return std::make_unique(exe_path, test_name); \ }}; \ } } // namespace Carbon::Testing #endif // CARBON_TESTING_FILE_TEST_FILE_TEST_BASE_H_ ================================================ FILE: testing/file_test/file_test_base_test.cpp ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #include "testing/file_test/file_test_base.h" #include #include #include #include #include #include #include "common/ostream.h" #include "llvm/ADT/StringExtras.h" #include "llvm/ADT/StringSwitch.h" #include "llvm/Support/FormatVariadic.h" namespace Carbon::Testing { namespace { class FileTestBaseTest : public FileTestBase { public: FileTestBaseTest(llvm::StringRef /*exe_path*/, llvm::StringRef test_name) : FileTestBase(test_name) {} auto Run(const llvm::SmallVector& test_args, llvm::IntrusiveRefCntPtr& fs, FILE* input_stream, llvm::raw_pwrite_stream& output_stream, llvm::raw_pwrite_stream& error_stream) const -> ErrorOr override; auto GetArgReplacements() const -> llvm::StringMap override { return {{"replacement", "replaced"}}; } auto GetDefaultArgs() const -> llvm::SmallVector override { return {"default_args", "%s"}; } auto GetDefaultFileRE(llvm::ArrayRef filenames) const -> std::optional override { return std::make_optional( llvm::formatv(R"(file: ({0}))", llvm::join(filenames, "|"))); } auto GetLineNumberReplacements(llvm::ArrayRef filenames) const -> llvm::SmallVector override { auto replacements = FileTestBase::GetLineNumberReplacements(filenames); auto filename = std::filesystem::path(test_name().str()).filename(); if (llvm::StringRef(filename).starts_with("file_only_re_")) { replacements.push_back({.has_file = false, .re = std::make_shared(R"(line: (\d+))"), .line_formatv = "{0}"}); } return replacements; } }; // Prints arguments so that they can be validated in tests. static auto PrintArgs(llvm::ArrayRef args, llvm::raw_pwrite_stream& output_stream) -> void { llvm::ListSeparator sep; output_stream << args.size() << " args: "; for (auto arg : args) { output_stream << sep << "`" << arg << "`"; } output_stream << "\n"; } // Verifies arguments are well-structured, and returns the files in them. static auto GetFilesFromArgs(llvm::ArrayRef args, llvm::vfs::InMemoryFileSystem& fs) -> ErrorOr> { if (args.empty() || args.front() != "default_args") { return ErrorBuilder() << "missing `default_args` argument"; } args.consume_front(); for (auto arg : args) { if (!fs.exists(arg)) { return ErrorBuilder() << "Missing file: " << arg; } } return args; } // Parameters used to by individual test handlers, for easy value forwarding. struct TestParams { // These are the arguments to `Run()`. llvm::vfs::InMemoryFileSystem& fs; FILE* input_stream; llvm::raw_pwrite_stream& output_stream; llvm::raw_pwrite_stream& error_stream; // This is assigned after construction. llvm::ArrayRef files; }; // Prints and returns expected results for alternating_files.carbon. static auto TestAlternatingFiles(TestParams& params) -> ErrorOr { params.output_stream << "unattached message 1\n" << "a.carbon:2: message 2\n" << "b.carbon:5: message 3\n" << "a.carbon:2: message 4\n" << "b.carbon:5: message 5\n" << "unattached message 6\n"; params.error_stream << "unattached message 1\n" << "a.carbon:2: message 2\n" << "b.carbon:5: message 3\n" << "a.carbon:2: message 4\n" << "b.carbon:5: message 5\n" << "unattached message 6\n"; return {{.success = true}}; } // Prints and returns expected results for capture_console_output.carbon. static auto TestCaptureConsoleOutput(TestParams& params) -> ErrorOr { llvm::errs() << "llvm::errs\n"; params.error_stream << "params.error_stream\n"; llvm::outs() << "llvm::outs\n"; params.output_stream << "params.output_stream\n"; return {{.success = true}}; } // Prints and returns expected results for escaping.carbon. static auto TestEscaping(TestParams& params) -> ErrorOr { params.error_stream << "carriage return\r\n" "{one brace}\n" "{{two braces}}\n" "[one bracket]\n" "[[two brackets]]\n" "end of line whitespace \n" "\ttabs\t\n"; return {{.success = true}}; } // Prints and returns expected results for example.carbon. static auto TestExample(TestParams& params) -> ErrorOr { int delta_line = 10; params.output_stream << "something\n" << "\n" << "example.carbon:" << delta_line + 1 << ": Line delta\n" << "example.carbon:" << delta_line << ": Negative line delta\n" << "+*[]{}\n" << "Foo baz\n"; return {{.success = true}}; } // Prints and returns expected results for fail_example.carbon. static auto TestFailExample(TestParams& params) -> ErrorOr { params.error_stream << "Oops\n"; return {{.success = false}}; } // Prints and returns expected results for // file_only_re_multi_file.carbon. static auto TestFileOnlyREMultiFile(TestParams& params) -> ErrorOr { int msg_count = 0; params.output_stream << "unattached message " << ++msg_count << "\n" << "file: a.carbon\n" << "unattached message " << ++msg_count << "\n" << "line: 3: attached message " << ++msg_count << "\n" << "unattached message " << ++msg_count << "\n" << "line: 8: late message " << ++msg_count << "\n" << "unattached message " << ++msg_count << "\n" << "file: b.carbon\n" << "line: 2: attached message " << ++msg_count << "\n" << "unattached message " << ++msg_count << "\n" << "line: 7: late message " << ++msg_count << "\n" << "unattached message " << ++msg_count << "\n"; return {{.success = true}}; } // Prints and returns expected results for file_only_re_one_file.carbon. static auto TestFileOnlyREOneFile(TestParams& params) -> ErrorOr { params.output_stream << "unattached message 1\n" << "file: file_only_re_one_file.carbon\n" << "line: 1\n" << "unattached message 2\n"; return {{.success = true}}; } // Prints and returns expected results for no_line_number.carbon. static auto TestNoLineNumber(TestParams& params) -> ErrorOr { params.output_stream << "a.carbon: msg1\n" "msg2\n" "b.carbon: msg3\n" "msg4\n" "a.carbon: msg5\n"; return {{.success = true}}; } // Prints and returns expected results for unattached_multi_file.carbon. static auto TestUnattachedMultiFile(TestParams& params) -> ErrorOr { params.output_stream << "unattached message 1\n" << "unattached message 2\n"; params.error_stream << "unattached message 3\n" << "unattached message 4\n"; return {{.success = true}}; } // Prints and returns expected results for: // - fail_multi_success_overall_fail.carbon // - multi_success.carbon // - multi_success_and_fail.carbon // // Parameters indicate overall and per-file success. static auto HandleMultiSuccessTests(bool overall, bool a, bool b) -> ErrorOr { FileTestBaseTest::RunResult result = {.success = overall}; result.per_file_success.push_back({a ? "a.carbon" : "fail_a.carbon", a}); result.per_file_success.push_back({b ? "b.carbon" : "fail_b.carbon", b}); return result; } // Echoes back non-comment file content. Used for default file handling. static auto EchoFileContent(TestParams& params) -> ErrorOr { // By default, echo non-comment content of files back. for (auto test_file : params.files) { // Describe file contents to stdout to validate splitting. auto file = params.fs.getBufferForFile(test_file, /*FileSize=*/-1, /*RequiresNullTerminator=*/false); if (file.getError()) { return Error(file.getError().message()); } llvm::StringRef buffer = file.get()->getBuffer(); for (int line_number = 1; !buffer.empty(); ++line_number) { auto [line, remainder] = buffer.split('\n'); if (!line.empty() && !line.starts_with("//")) { params.output_stream << test_file << ":" << line_number << ": " << line << "\n"; } buffer = remainder; } } if (params.input_stream) { params.error_stream << "--- STDIN:\n"; constexpr int ReadSize = 1024; char buf[ReadSize]; while (feof(params.input_stream) == 0) { auto read = fread(&buf, sizeof(char), ReadSize, params.input_stream); if (read > 0) { params.error_stream.write(buf, read); } } } return {{.success = true}}; } auto FileTestBaseTest::Run( const llvm::SmallVector& test_args, llvm::IntrusiveRefCntPtr& fs, FILE* input_stream, llvm::raw_pwrite_stream& output_stream, llvm::raw_pwrite_stream& error_stream) const -> ErrorOr { PrintArgs(test_args, output_stream); auto filename = std::filesystem::path(test_name().str()).filename(); if (filename == "args.carbon" || filename == "include_args.carbon" || filename == "include_extra_args.carbon" || filename == "include_args_and_extra_args.carbon") { // These files are testing argument behavior, which doesn't work with the // default test logic. return {{.success = true}}; } // Choose the test function based on filename. auto test_fn = llvm::StringSwitchErrorOr>>( filename.string()) .Case("alternating_files.carbon", &TestAlternatingFiles) .Case("capture_console_output.carbon", &TestCaptureConsoleOutput) .Case("escaping.carbon", &TestEscaping) .Case("example.carbon", &TestExample) .Case("fail_example.carbon", &TestFailExample) .Case("file_only_re_one_file.carbon", &TestFileOnlyREOneFile) .Case("file_only_re_multi_file.carbon", &TestFileOnlyREMultiFile) .Case("no_line_number.carbon", &TestNoLineNumber) .Case("unattached_multi_file.carbon", &TestUnattachedMultiFile) .Case("fail_multi_success_overall_fail.carbon", [&](TestParams&) { return HandleMultiSuccessTests(/*overall=*/false, /*a=*/true, /*b=*/true); }) .Case("multi_success.carbon", [&](TestParams&) { return HandleMultiSuccessTests(/*overall=*/true, /*a=*/true, /*b=*/true); }) .Case("multi_success_and_fail.carbon", [&](TestParams&) { return HandleMultiSuccessTests(/*overall=*/false, /*a=*/true, /*b=*/false); }) .Default(&EchoFileContent); // Call the appropriate test function for the file. TestParams params = {.fs = *fs, .input_stream = input_stream, .output_stream = output_stream, .error_stream = error_stream}; CARBON_ASSIGN_OR_RETURN(params.files, GetFilesFromArgs(test_args, *fs)); return test_fn(params); } } // namespace CARBON_FILE_TEST_FACTORY(FileTestBaseTest) } // namespace Carbon::Testing ================================================ FILE: testing/file_test/line.h ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #ifndef CARBON_TESTING_FILE_TEST_LINE_H_ #define CARBON_TESTING_FILE_TEST_LINE_H_ #include "common/ostream.h" #include "llvm/ADT/StringRef.h" namespace Carbon::Testing { // Interface for lines. class FileTestLineBase : public Printable { public: explicit FileTestLineBase(int file_number, int line_number) : file_number_(file_number), line_number_(line_number) {} virtual ~FileTestLineBase() = default; // Prints the autoupdated line. virtual auto Print(llvm::raw_ostream& out) const -> void = 0; virtual auto is_blank() const -> bool = 0; auto file_number() const -> int { return file_number_; } auto line_number() const -> int { return line_number_; } void set_location(int file_number, int line_number) { file_number_ = file_number; line_number_ = line_number; } private: int file_number_; int line_number_; }; // A line in the original file test. // // `final` because we use pointer arithmetic on this type. class FileTestLine final : public FileTestLineBase { public: explicit FileTestLine(int file_number, int line_number, llvm::StringRef line) : FileTestLineBase(file_number, line_number), line_(line) {} auto Print(llvm::raw_ostream& out) const -> void override { out << line_; } auto is_blank() const -> bool override { return line_.empty(); } auto indent() const -> llvm::StringRef { return line_.substr(0, line_.find_first_not_of(" \n")); } private: llvm::StringRef line_; }; } // namespace Carbon::Testing #endif // CARBON_TESTING_FILE_TEST_LINE_H_ ================================================ FILE: testing/file_test/manifest.cpp ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #include "testing/file_test/manifest.h" #include // The test manifest, produced by `manifest_as_cpp`. // NOLINTNEXTLINE(readability-identifier-naming): Constant in practice. extern const char* CarbonFileTestManifest[]; namespace Carbon::Testing { auto GetFileTestManifest() -> llvm::SmallVector { llvm::SmallVector manifest; for (int i = 0; CarbonFileTestManifest[i]; ++i) { manifest.push_back(CarbonFileTestManifest[i]); } return manifest; } } // namespace Carbon::Testing ================================================ FILE: testing/file_test/manifest.h ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #ifndef CARBON_TESTING_FILE_TEST_MANIFEST_H_ #define CARBON_TESTING_FILE_TEST_MANIFEST_H_ #include #include "llvm/ADT/SmallVector.h" namespace Carbon::Testing { // Returns the manifest path, which is provided by rules.bzl and manifest.cpp. // This is exposed separately so that the explorer sharding approach can use a // different implementation. auto GetFileTestManifest() -> llvm::SmallVector; } // namespace Carbon::Testing #endif // CARBON_TESTING_FILE_TEST_MANIFEST_H_ ================================================ FILE: testing/file_test/rules.bzl ================================================ # Part of the Carbon Language project, under the Apache License v2.0 with LLVM # Exceptions. See /LICENSE for license information. # SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception """Rules for building file tests. file_test uses the tests_as_input_file rule to transform test dependencies into a file which can be accessed as a list. This avoids long argument parsing. """ load("//bazel/cc_rules:defs.bzl", "cc_test") load("//bazel/manifest:defs.bzl", "manifest_as_cpp") def file_test( name, tests, srcs = [], deps = [], data = [], args = [], **kwargs): """Generates tests using the file_test base. There will be one main test using `name` that can be sharded, and includes all files. Additionally, per-file tests will be generated as `name.file_path`; these per-file tests will be manual. Args: name: The base name of the tests. tests: The list of test files to use as data, typically a glob. srcs: Passed to cc_test. deps: Passed to cc_test. data: Passed to cc_test. args: Passed to cc_test. **kwargs: Passed to cc_test. """ # Ensure tests are always a filegroup for tests_as_input_file_rule. manifest_cpp = "{0}_autogen_manifest.cpp".format(name) manifest_as_cpp( name = manifest_cpp, var_name = "CarbonFileTestManifest", srcs = tests, testonly = 1, ) cc_test( name = name, srcs = srcs + [":" + manifest_cpp], deps = deps + ["//testing/file_test:manifest_impl"], data = tests + data, args = args, **kwargs ) ================================================ FILE: testing/file_test/run_test.cpp ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #include "testing/file_test/run_test.h" #include #include #include #include "common/pretty_stack_trace_function.h" #include "llvm/ADT/ScopeExit.h" #include "llvm/ADT/SmallVector.h" #include "llvm/ADT/StringMap.h" #include "llvm/ADT/StringRef.h" #include "llvm/Support/PrettyStackTrace.h" #include "testing/base/file_helpers.h" #include "testing/file_test/file_test_base.h" #include "testing/file_test/test_file.h" namespace Carbon::Testing { // While these are marked as "internal" APIs, they seem to work and be pretty // widely used for their exact documented behavior. using ::testing::internal::CaptureStderr; using ::testing::internal::CaptureStdout; using ::testing::internal::GetCapturedStderr; using ::testing::internal::GetCapturedStdout; static constexpr llvm::StringLiteral StdinFilename = "STDIN"; // Does replacements in ARGS for %s and %t. static auto DoArgReplacements(llvm::SmallVector& test_args, const llvm::StringMap& replacements, llvm::ArrayRef split_files) -> ErrorOr { for (auto* it = test_args.begin(); it != test_args.end(); ++it) { auto percent = it->find("%"); if (percent == std::string::npos) { continue; } if (percent + 1 >= it->size()) { return ErrorBuilder() << "% is not allowed on its own: " << *it; } char c = (*it)[percent + 1]; switch (c) { case 's': { if (*it != "%s") { return ErrorBuilder() << "%s must be the full argument: " << *it; } it = test_args.erase(it); for (const auto& split : split_files) { const std::string& filename = split->filename; if (filename == StdinFilename || filename.ends_with(".h")) { continue; } it = test_args.insert(it, filename); ++it; } // Back up once because the for loop will advance. --it; break; } case 't': { std::filesystem::path tmpdir = GetTempDirectory(); it->replace(percent, 2, llvm::formatv("{0}/temp_file", tmpdir)); break; } case '{': { auto end_brace = it->find('}', percent); if (end_brace == std::string::npos) { return ErrorBuilder() << "%{ without closing }: " << *it; } llvm::StringRef substr(&*(it->begin() + percent + 2), end_brace - percent - 2); auto replacement = replacements.find(substr); if (replacement == replacements.end()) { return ErrorBuilder() << "unknown substitution: %{" << substr << "}: " << *it; } it->replace(percent, end_brace - percent + 1, replacement->second); break; } default: return ErrorBuilder() << "%" << c << " is not supported: " << *it; } } return Success(); } // Collects captured output when enabled. static auto CollectOutputIfCapturing(TestFile& test_file) -> void { if (!test_file.capture_console_output) { return; } // No need to flush stderr. llvm::outs().flush(); test_file.actual_stdout += GetCapturedStdout(); test_file.actual_stderr += GetCapturedStderr(); } auto RunTestFile(const FileTestBase& test_base, bool dump_output, TestFile& test_file) -> ErrorOr { llvm::SmallVector all_splits; for (auto& split : test_file.include_file_splits) { all_splits.push_back(&split); } for (auto& split : test_file.file_splits) { all_splits.push_back(&split); } // Process arguments. if (test_file.test_args.empty()) { test_file.test_args = test_base.GetDefaultArgs(); } test_file.test_args.append(test_file.extra_args); CARBON_RETURN_IF_ERROR(DoArgReplacements( test_file.test_args, test_base.GetArgReplacements(), all_splits)); // stdin needs to exist on-disk for compatibility. We'll use a pointer for it. FILE* input_stream = nullptr; auto erase_input_on_exit = llvm::scope_exit([&input_stream]() { if (input_stream) { // fclose should delete the tmpfile. fclose(input_stream); input_stream = nullptr; } }); // Create the files in-memory. llvm::IntrusiveRefCntPtr fs = new llvm::vfs::InMemoryFileSystem; for (const auto& split : all_splits) { if (split->filename == StdinFilename) { input_stream = tmpfile(); fwrite(split->content.c_str(), sizeof(char), split->content.size(), input_stream); CARBON_CHECK(!fseek(input_stream, 0, SEEK_SET)); } else if (!fs->addFile(split->filename, /*ModificationTime=*/0, llvm::MemoryBuffer::getMemBuffer( split->content, split->filename, /*RequiresNullTerminator=*/false))) { return ErrorBuilder() << "File is repeated: " << split->filename; } } // Convert the arguments to StringRef and const char* to match the // expectations of PrettyStackTraceProgram and Run. llvm::SmallVector test_args_ref; llvm::SmallVector test_argv_for_stack_trace; test_args_ref.reserve(test_file.test_args.size()); test_argv_for_stack_trace.reserve(test_file.test_args.size() + 1); for (const auto& arg : test_file.test_args) { test_args_ref.push_back(arg); test_argv_for_stack_trace.push_back(arg.c_str()); } // Add a trailing null so that this is a proper argv. test_argv_for_stack_trace.push_back(nullptr); // Add a stack trace entry for the test invocation. llvm::PrettyStackTraceProgram stack_trace_entry( test_argv_for_stack_trace.size() - 1, test_argv_for_stack_trace.data()); // Conditionally capture console output. We use a scope exit to ensure the // captures terminate even on run failures. if (test_file.capture_console_output) { CaptureStderr(); CaptureStdout(); } // Prepare string streams to capture output. In order to address casting // constraints, we split calls to Run as a ternary based on whether we want to // capture output. llvm::raw_svector_ostream output_stream(test_file.actual_stdout); llvm::raw_svector_ostream error_stream(test_file.actual_stderr); // Dump any available captured output if `Run` crashes. PrettyStackTraceFunction stack_trace_streams([&](llvm::raw_ostream& out) { CollectOutputIfCapturing(test_file); auto dump_stream = [&](llvm::SmallString<16> stream) { if (stream.empty()) { out << " (none)\n"; } else { out << "\n" << stream << "\n"; } }; out << "Test stdout:"; dump_stream(test_file.actual_stdout); out << "\tTest stderr:"; dump_stream(test_file.actual_stderr); }); Timer timer; ErrorOr run_result = dump_output ? test_base.Run(test_args_ref, fs, input_stream, llvm::outs(), llvm::errs()) : test_base.Run(test_args_ref, fs, input_stream, output_stream, error_stream); test_file.run_elapsed_ms = timer.elapsed_ms(); // Collect captured stdout/stderr, even when discarded on error. CollectOutputIfCapturing(test_file); if (!run_result.ok()) { return std::move(run_result).error(); } test_file.run_result = std::move(*run_result); return Success(); } } // namespace Carbon::Testing ================================================ FILE: testing/file_test/run_test.h ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #ifndef CARBON_TESTING_FILE_TEST_RUN_TEST_H_ #define CARBON_TESTING_FILE_TEST_RUN_TEST_H_ #include "common/error.h" #include "testing/file_test/file_test_base.h" #include "testing/file_test/test_file.h" namespace Carbon::Testing { // Runs the test, updating `test_file`. auto RunTestFile(const FileTestBase& test_base, bool dump_output, TestFile& test_file) -> ErrorOr; } // namespace Carbon::Testing #endif // CARBON_TESTING_FILE_TEST_RUN_TEST_H_ ================================================ FILE: testing/file_test/test_file.cpp ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #include "testing/file_test/test_file.h" #include #include #include #include #include "common/check.h" #include "common/error.h" #include "common/find.h" #include "common/raw_string_ostream.h" #include "common/set.h" #include "llvm/ADT/StringExtras.h" #include "llvm/Support/JSON.h" #include "testing/base/file_helpers.h" #include "testing/file_test/line.h" namespace Carbon::Testing { using ::testing::Matcher; using ::testing::MatchesRegex; using ::testing::StrEq; // Represents the different kinds of version-control conflict markers that are // relevant for the autoupdater. One key concern here is the distinction between // "snapshot" and "diff" conflict regions. Snapshot regions are the more // traditional kind, where the entire region between two markers represents the // exact state of a region of the underlying file at some snapshot (e.g. the // base commit or one of the conflicting commits). Diff regions are // produced by jj. They show the diff between the base and one side of the // conflict, using a prefix character on each line: '+' indicates an added line, // '-' indicates a removed line, and ' ' indicates an unchanged line. Note that // a single conflict may contain both snapshot and diff regions. // // See https://docs.jj-vcs.dev/latest/conflicts/ for more information. enum class MarkerKind { // Represents a line that is not a conflict marker. None, // Marks the start of a conflict, and potentially a snapshot region. Start, // Marks the end of a conflict. End, // Marks the start of a snapshot region. Snapshot, // Marks the start of a diff region. Diff }; // Processes conflict markers, including tracking the previous conflict marker. // Returns true if the line is consumed. static auto TryConsumeConflictMarker(bool running_autoupdate, llvm::StringRef line, llvm::StringRef line_trimmed, MarkerKind& previous_marker) -> ErrorOr { MarkerKind new_marker; if (line.starts_with("<<<<<<<")) { new_marker = MarkerKind::Start; } else if (line.starts_with(">>>>>>>")) { new_marker = MarkerKind::End; } else if (line.starts_with("=======") || line.starts_with("|||||||") || line.starts_with("+++++++") || line.starts_with("-------")) { // git uses "=======" and "|||||||" to mark boundaries between conflict // regions (which are always snapshots). jj uses "+++++++" and "-------" to // mark the start of different kinds of snapshot regions. new_marker = MarkerKind::Snapshot; } else if (line.starts_with("%%%%%%%") || line.starts_with(R"(\\\\\\\)")) { // jj uses "%%%%%%%" to mark the start of a diff region, and "\\\\\\\" to // add a second line to a "%%%%%%%" marker for formatting purposes. new_marker = MarkerKind::Diff; } else { new_marker = MarkerKind::None; } // When running the test, any conflict marker is an error. if (!running_autoupdate && (new_marker != MarkerKind::None)) { return ErrorBuilder() << "Conflict marker found:\n" << line; } bool inside_conflict_marker = [&] { switch (previous_marker) { case MarkerKind::None: case MarkerKind::End: return false; case MarkerKind::Start: case MarkerKind::Snapshot: case MarkerKind::Diff: return true; } }(); switch (new_marker) { case MarkerKind::End: case MarkerKind::Snapshot: case MarkerKind::Diff: if (!inside_conflict_marker) { return ErrorBuilder() << "Unexpected conflict marker outside conflict:\n" << line; } previous_marker = new_marker; return true; case MarkerKind::Start: if (inside_conflict_marker) { return ErrorBuilder() << "Unexpected conflict marker inside conflict:\n" << line; } previous_marker = new_marker; return true; case MarkerKind::None: if (!inside_conflict_marker) { return false; } if (previous_marker == MarkerKind::Diff) { if (!line.consume_front(" ") && !line.consume_front("+") && !line.consume_front("-")) { return ErrorBuilder() << "Line inside diff-style conflict doesn't " "start with '+', '-', or ' ':\n" << line; } line_trimmed = line.ltrim(); } // Look for CHECK and TIP lines, which can be discarded. if (line_trimmed.starts_with("// CHECK:STDOUT:") || line_trimmed.starts_with("// CHECK:STDERR:") || line_trimmed.starts_with("// TIP:")) { return true; } return ErrorBuilder() << "Autoupdate can't discard non-CHECK lines " "inside conflicts:\n"; } } // State for file splitting logic: TryConsumeSplit and FinishSplit. struct SplitState { auto has_splits() const -> bool { return file_index > 0; } auto add_content(llvm::StringRef line) -> void { content.append(line.str()); content.append("\n"); } // Whether content has been found. Only updated before a file split is found // (which may be never). bool found_code_pre_split = false; // The current file name, considering splits. Empty for the default file. llvm::StringRef filename = ""; // The accumulated content for the file being built. This may elide some of // the original content, such as conflict markers. std::string content; // The current file index. int file_index = 0; }; // Given a `file:/` URI, returns the filename. static auto ExtractFilePathFromUri(llvm::StringRef uri) -> ErrorOr { static constexpr llvm::StringRef FilePrefix = "file:/"; if (!uri.starts_with(FilePrefix)) { return ErrorBuilder() << "uri `" << uri << "` is not a file uri"; } return uri.drop_front(FilePrefix.size()); } // When `FROM_FILE_SPLIT` is used in path `textDocument.text`, populate the // value from the split matching the `uri`. Only used for // `textDocument/didOpen`. static auto AutoFillDidOpenParams(llvm::json::Object& params, llvm::ArrayRef splits) -> ErrorOr { auto* text_document = params.getObject("textDocument"); if (text_document == nullptr) { return Success(); } auto attr_it = text_document->find("text"); if (attr_it == text_document->end() || attr_it->second != "FROM_FILE_SPLIT") { return Success(); } auto uri = text_document->getString("uri"); if (!uri) { return Error("missing uri in params.textDocument"); } CARBON_ASSIGN_OR_RETURN(auto file_path, ExtractFilePathFromUri(*uri)); const auto* split = FindIfOrNull(splits, [&](const TestFile::Split& split) { return split.filename == file_path; }); if (!split) { return ErrorBuilder() << "No split found for uri: " << *uri; } attr_it->second = split->content; return Success(); } // Reformats `[[@LSP:` and similar keyword as an LSP call with headers. Returns // the position to start a find for the next keyword. static auto ReplaceLspKeywordAt(std::string& content, size_t keyword_pos, int& lsp_call_id, llvm::ArrayRef splits) -> ErrorOr { llvm::StringRef content_at_keyword = llvm::StringRef(content).substr(keyword_pos); auto [keyword, body_start] = content_at_keyword.split(":"); if (keyword.size() == content_at_keyword.size()) { return ErrorBuilder() << "Missing `:` for `" << content_at_keyword.take_front(10) << "`"; } // Whether the first param is a method or id. llvm::StringRef method_or_id_label = "method"; // Whether to attach the `lsp_call_id`. bool use_call_id = false; // The JSON label for extra content. llvm::StringRef extra_content_label; if (keyword == "[[@LSP-CALL") { use_call_id = true; extra_content_label = "params"; } else if (keyword == "[[@LSP-NOTIFY") { extra_content_label = "params"; } else if (keyword == "[[@LSP-REPLY") { method_or_id_label = "id"; extra_content_label = "result"; } else if (keyword != "[[@LSP") { return ErrorBuilder() << "Unrecognized @LSP keyword at `" << keyword.take_front(10) << "`"; } static constexpr llvm::StringLiteral LspEnd = "]]"; auto [body, rest] = body_start.split("]]"); if (body.size() == body_start.size()) { return ErrorBuilder() << "Missing `" << LspEnd << "` after `" << keyword << "`"; } auto [method_or_id, extra_content] = body.split(":"); llvm::json::Value parsed_extra_content = nullptr; if (!extra_content.empty()) { std::string extra_content_as_object = llvm::formatv("{{{0}}", extra_content); auto parse_result = llvm::json::parse(extra_content_as_object); if (auto err = parse_result.takeError()) { return ErrorBuilder() << "Error parsing extra content: " << err; } parsed_extra_content = std::move(*parse_result); CARBON_CHECK(parsed_extra_content.kind() == llvm::json::Value::Object); if (extra_content_label == "params" && method_or_id == "textDocument/didOpen") { CARBON_RETURN_IF_ERROR( AutoFillDidOpenParams(*parsed_extra_content.getAsObject(), splits)); } } // Form the JSON. RawStringOstream buffer; llvm::json::OStream json(buffer); json.object([&] { json.attribute("jsonrpc", "2.0"); json.attribute(method_or_id_label, method_or_id); if (use_call_id) { json.attribute("id", ++lsp_call_id); } if (parsed_extra_content != nullptr) { if (!extra_content_label.empty()) { json.attribute(extra_content_label, parsed_extra_content); } else { for (const auto& [key, value] : *parsed_extra_content.getAsObject()) { json.attribute(key, value); } } } }); // Add the Content-Length header. The `2` accounts for extra newlines. int content_length = buffer.size() + 2; auto json_with_header = llvm::formatv("Content-Length: {0}\n\n{1}\n", content_length, buffer.TakeStr()) .str(); size_t keyword_len = rest.data() - keyword.data(); content.replace(keyword_pos, keyword_len, json_with_header); return keyword_pos + json_with_header.size(); } // Replaces `[[@0xAB]]` with the raw byte with value 0xAB. Returns the position // to start a find for the next keyword. static auto ReplaceRawByteKeywordAt(std::string& content, size_t keyword_pos) -> ErrorOr { llvm::StringRef content_at_keyword = llvm::StringRef(content).substr(keyword_pos); auto [keyword, rest] = content_at_keyword.split("]]"); if (keyword.size() == content_at_keyword.size()) { return ErrorBuilder() << "Missing `]]` after " << keyword.take_front(10) << "`"; } unsigned char byte_value; if (keyword.substr(std::size("[[@0x") - 1).getAsInteger(16, byte_value)) { return ErrorBuilder() << "Invalid raw byte specifier `" << keyword.take_front(10) << "`"; } content.replace(keyword_pos, keyword.size() + 2, 1, byte_value); return keyword_pos + 1; } // Replaces the keyword at the given position. Returns the position to start a // find for the next keyword. static auto ReplaceContentKeywordAt(std::string& content, size_t keyword_pos, llvm::StringRef test_name, int& lsp_call_id, llvm::ArrayRef splits) -> ErrorOr { auto keyword = llvm::StringRef(content).substr(keyword_pos); // Line replacements aren't handled here. static constexpr llvm::StringLiteral Line = "[[@LINE"; if (keyword.starts_with(Line)) { // Just move past the prefix to find the next one. return keyword_pos + Line.size(); } // Replaced with the actual test name. static constexpr llvm::StringLiteral TestName = "[[@TEST_NAME]]"; if (keyword.starts_with(TestName)) { content.replace(keyword_pos, TestName.size(), test_name); return keyword_pos + test_name.size(); } if (keyword.starts_with("[[@LSP")) { return ReplaceLspKeywordAt(content, keyword_pos, lsp_call_id, splits); } if (keyword.starts_with("[[@0x")) { return ReplaceRawByteKeywordAt(content, keyword_pos); } return ErrorBuilder() << "Unexpected use of `[[@` at `" << keyword.substr(0, 5) << "`"; } // Replaces the content keywords. // // This handles content keywords such as [[@TEST_NAME]] and [[@LSP*]]. Unknown // content keywords are diagnosed. static auto ReplaceContentKeywords(llvm::StringRef filename, std::string& content, llvm::ArrayRef splits) -> ErrorOr { static constexpr llvm::StringLiteral Prefix = "[[@"; auto keyword_pos = content.find(Prefix); // Return early if not finding anything. if (keyword_pos == std::string::npos) { return Success(); } // Construct the test name by getting the base name without the extension, // then removing any "fail_" or "todo_" prefixes. llvm::StringRef test_name = filename; if (auto last_slash = test_name.rfind("/"); last_slash != llvm::StringRef::npos) { test_name = test_name.substr(last_slash + 1); } if (auto ext_dot = test_name.find("."); ext_dot != llvm::StringRef::npos) { test_name = test_name.substr(0, ext_dot); } // Note this also handles `fail_todo_` and `todo_fail_`. test_name.consume_front("todo_"); test_name.consume_front("fail_"); test_name.consume_front("todo_"); // A counter for LSP calls. int lsp_call_id = 0; while (keyword_pos != std::string::npos) { CARBON_ASSIGN_OR_RETURN( auto keyword_end, ReplaceContentKeywordAt(content, keyword_pos, test_name, lsp_call_id, splits)); keyword_pos = content.find(Prefix, keyword_end); } return Success(); } // Adds a file. Used for both split and unsplit test files. static auto AddSplit(llvm::StringRef filename, std::string& content, llvm::SmallVector& file_splits) -> ErrorOr { CARBON_RETURN_IF_ERROR( ReplaceContentKeywords(filename, content, file_splits)); file_splits.push_back( {.filename = filename.str(), .content = std::move(content)}); content.clear(); return Success(); } // Process file split ("---") lines when found. Returns true if the line is // consumed. `non_check_lines` is only provided for the main file, and will be // null for includes. static auto TryConsumeSplit(llvm::StringRef line, llvm::StringRef line_trimmed, bool missing_autoupdate, int& line_index, SplitState& split, llvm::SmallVector& file_splits, llvm::SmallVector* non_check_lines) -> ErrorOr { if (!line_trimmed.consume_front("// ---")) { if (!split.has_splits() && !line_trimmed.starts_with("//") && !line_trimmed.empty()) { split.found_code_pre_split = true; } // Add the line to the current file's content (which may not be a split // file). split.add_content(line); return false; } if (missing_autoupdate) { // If there's a split, all output is appended at the end of each file // before AUTOUPDATE. We may want to change that, but it's not // necessary to handle right now. return Error( "AUTOUPDATE/NOAUTOUPDATE setting must be in " "the first file."); } // On a file split, add the previous file, then start a new one. if (split.has_splits()) { CARBON_RETURN_IF_ERROR( AddSplit(split.filename, split.content, file_splits)); } else { split.content.clear(); if (split.found_code_pre_split) { // For the first split, we make sure there was no content prior. return Error( "When using split files, there must be no content before the first " "split file."); } } ++split.file_index; split.filename = line_trimmed.trim(); if (split.filename.empty()) { return Error("Missing filename for split."); } // The split line is added to non_check_lines for retention in autoupdate, but // is not added to the test file content. line_index = 0; if (non_check_lines) { non_check_lines->push_back( FileTestLine(split.file_index, line_index, line)); } return true; } // Converts a `FileCheck`-style expectation string into a single complete regex // string by escaping all regex characters outside of the designated `{{...}}` // regex sequences, and switching those to a normal regex sub-pattern syntax. static auto ConvertExpectationStringToRegex(std::string& str) -> void { for (int pos = 0; pos < static_cast(str.size());) { switch (str[pos]) { case '(': case ')': case '[': case ']': case '}': case '.': case '^': case '$': case '*': case '+': case '?': case '|': case '\\': { // Escape regex characters. str.insert(pos, "\\"); pos += 2; break; } case '{': { if (pos + 1 == static_cast(str.size()) || str[pos + 1] != '{') { // Single `{`, escape it. str.insert(pos, "\\"); pos += 2; break; } // Replace the `{{...}}` regex syntax with standard `(...)` syntax. str.replace(pos, 2, "("); for (++pos; pos < static_cast(str.size() - 1); ++pos) { if (str[pos] == '}' && str[pos + 1] == '}') { str.replace(pos, 2, ")"); ++pos; break; } } break; } default: { ++pos; } } } } // Transforms an expectation on a given line from `FileCheck` syntax into a // standard regex matcher. static auto TransformExpectation(int line_index, llvm::StringRef in) -> ErrorOr> { if (in.empty()) { return Matcher{StrEq("")}; } if (!in.consume_front(" ")) { return ErrorBuilder() << "Malformated CHECK line: " << in; } // Check early if we have a regex component as we can avoid building an // expensive matcher when not using those. bool has_regex = in.find("{{") != llvm::StringRef::npos; // Now scan the string and expand any keywords. Note that this needs to be // `size_t` to correctly store `npos`. size_t keyword_pos = in.find("[["); // If there are neither keywords nor regex sequences, we can match the // incoming string directly. if (!has_regex && keyword_pos == llvm::StringRef::npos) { return Matcher{StrEq(in)}; } std::string str = in.str(); // First expand the keywords. while (keyword_pos != std::string::npos) { llvm::StringRef line_keyword_cursor = llvm::StringRef(str).substr(keyword_pos); CARBON_CHECK(line_keyword_cursor.consume_front("[[")); static constexpr llvm::StringLiteral LineKeyword = "@LINE"; if (!line_keyword_cursor.consume_front(LineKeyword)) { return ErrorBuilder() << "Unexpected [[, should be {{\\[\\[}} at `" << line_keyword_cursor.substr(0, 5) << "` in: " << in; } // Allow + or - here; consumeInteger handles -. line_keyword_cursor.consume_front("+"); int offset; // consumeInteger returns true for errors, not false. if (line_keyword_cursor.consumeInteger(10, offset) || !line_keyword_cursor.consume_front("]]")) { return ErrorBuilder() << "Unexpected @LINE offset at `" << line_keyword_cursor.substr(0, 5) << "` in: " << in; } std::string int_str = llvm::Twine(line_index + offset).str(); int remove_len = (line_keyword_cursor.data() - str.data()) - keyword_pos; str.replace(keyword_pos, remove_len, int_str); keyword_pos += int_str.size(); // Find the next keyword start or the end of the string. keyword_pos = str.find("[[", keyword_pos); } // If there was no regex, we can directly match the adjusted string. if (!has_regex) { return Matcher{StrEq(str)}; } // Otherwise, we need to turn the entire string into a regex by escaping // things outside the regex region and transforming the regex region into a // normal syntax. ConvertExpectationStringToRegex(str); return Matcher{MatchesRegex(str)}; } // Once all content is processed, do any remaining split processing. static auto FinishSplit(llvm::StringRef filename, bool is_include_file, SplitState& split, llvm::SmallVector& file_splits) -> ErrorOr { if (split.has_splits()) { return AddSplit(split.filename, split.content, file_splits); } else { // If no file splitting happened, use the main file as the test file. // There will always be a `/` unless tests are in the repo root. std::string split_name = std::filesystem::path(filename.str()).filename(); if (is_include_file) { split_name.insert(0, "include_files/"); } return AddSplit(split_name, split.content, file_splits); } } // Process CHECK lines when found. Returns true if the line is consumed. // `expected_stdout` and `expected_stderr` are null in included files, where // it's an error to use `CHECK`. static auto TryConsumeCheck( bool running_autoupdate, int line_index, llvm::StringRef line, llvm::StringRef line_trimmed, llvm::SmallVector>* expected_stdout, llvm::SmallVector>* expected_stderr) -> ErrorOr { if (!line_trimmed.consume_front("// CHECK")) { return false; } if (!expected_stdout) { return ErrorBuilder() << "Included files can't add CHECKs: " << line_trimmed; } // Don't build expectations when doing an autoupdate. We don't want to // break the autoupdate on an invalid CHECK line. if (!running_autoupdate) { llvm::SmallVector>* expected; if (line_trimmed.consume_front(":STDOUT:")) { expected = expected_stdout; } else if (line_trimmed.consume_front(":STDERR:")) { expected = expected_stderr; } else { return ErrorBuilder() << "Unexpected CHECK in input: " << line.str(); } CARBON_ASSIGN_OR_RETURN(Matcher check_matcher, TransformExpectation(line_index, line_trimmed)); expected->push_back(check_matcher); } return true; } // Processes ARGS and EXTRA-ARGS lines when found. Returns true if the line is // consumed. static auto TryConsumeArgs(llvm::StringRef line, llvm::StringRef line_trimmed, llvm::SmallVector& args) -> ErrorOr { if (!line_trimmed.consume_front("// ARGS: ")) { return false; } if (!args.empty()) { return ErrorBuilder() << "ARGS specified multiple times: " << line.str(); } // Split the line into arguments. std::pair cursor = llvm::getToken(line_trimmed); while (!cursor.first.empty()) { args.push_back(std::string(cursor.first)); cursor = llvm::getToken(cursor.second); } return true; } static auto TryConsumeExtraArgs(llvm::StringRef line_trimmed, llvm::SmallVector& extra_args) -> ErrorOr { if (!line_trimmed.consume_front("// EXTRA-ARGS: ")) { return false; } // Split the line into arguments. std::pair cursor = llvm::getToken(line_trimmed); while (!cursor.first.empty()) { extra_args.push_back(std::string(cursor.first)); cursor = llvm::getToken(cursor.second); } return true; } static auto TryConsumeIncludeFile(llvm::StringRef line_trimmed, llvm::SmallVector& include_files) -> ErrorOr { if (!line_trimmed.consume_front("// INCLUDE-FILE: ")) { return false; } include_files.push_back(line_trimmed.str()); return true; } // Processes AUTOUPDATE lines when found. Returns true if the line is consumed. // `found_autoupdate` and `autoupdate_line_number` are only provided for the // main file; it's an error to have autoupdate in included files. static auto TryConsumeAutoupdate(int line_index, llvm::StringRef line_trimmed, bool* found_autoupdate, std::optional* autoupdate_line_number) -> ErrorOr { static constexpr llvm::StringLiteral Autoupdate = "// AUTOUPDATE"; static constexpr llvm::StringLiteral NoAutoupdate = "// NOAUTOUPDATE"; if (line_trimmed != Autoupdate && line_trimmed != NoAutoupdate) { return false; } if (!found_autoupdate) { return ErrorBuilder() << "Included files can't control autoupdate: " << line_trimmed; } if (*found_autoupdate) { return Error("Multiple AUTOUPDATE/NOAUTOUPDATE settings found"); } *found_autoupdate = true; if (line_trimmed == Autoupdate) { *autoupdate_line_number = line_index; } return true; } // Processes SET-* lines when found. Returns true if the line is consumed. // If `flag` is null, we're in an included file where the flag can't be set. static auto TryConsumeSetFlag(llvm::StringRef line_trimmed, llvm::StringLiteral flag_name, bool* flag) -> ErrorOr { if (!line_trimmed.consume_front("// ") || line_trimmed != flag_name) { return false; } if (!flag) { return ErrorBuilder() << "Included files can't set flag: " << line_trimmed; } if (*flag) { return ErrorBuilder() << flag_name << " was specified multiple times"; } *flag = true; return true; } // Process content for either the main file (with `test_file` and // `found_autoupdate` provided) or an included file (with those arguments null). // // - `found_autoupdate` is set to true when either `AUTOUPDATE` or // `NOAUTOUPDATE` are found. // - `args` is set from `ARGS`. // - `extra_args` accumulates `EXTRA-ARGS`. // - `splits` accumulates split form for the test (`// --- `, or the // full file named as `filename` when there are no splits in the file). // - `include_files` accumulates `INCLUDE-FILE`. static auto ProcessFileContent(llvm::StringRef filename, llvm::StringRef content_cursor, bool running_autoupdate, TestFile* test_file, bool* found_autoupdate, llvm::SmallVector& args, llvm::SmallVector& extra_args, llvm::SmallVector& splits, llvm::SmallVector& include_files) -> ErrorOr { // The index in the current test file. Will be reset on splits. int line_index = 0; // When autoupdating, we track whether we're inside conflict markers. // Otherwise conflict markers are errors. auto previous_conflict_marker = MarkerKind::None; SplitState split_state; while (!content_cursor.empty()) { auto [line, next_cursor] = content_cursor.split("\n"); content_cursor = next_cursor; auto line_trimmed = line.ltrim(); bool is_consumed = false; CARBON_ASSIGN_OR_RETURN( is_consumed, TryConsumeConflictMarker(running_autoupdate, line, line_trimmed, previous_conflict_marker)); if (is_consumed) { continue; } // At this point, remaining lines are part of the test input. // We need to consume a split, but the main file has a little more handling. bool missing_autoupdate = false; llvm::SmallVector* non_check_lines = nullptr; if (test_file) { missing_autoupdate = !*found_autoupdate; non_check_lines = &test_file->non_check_lines; } CARBON_ASSIGN_OR_RETURN( is_consumed, TryConsumeSplit(line, line_trimmed, missing_autoupdate, line_index, split_state, splits, non_check_lines)); if (is_consumed) { continue; } ++line_index; // TIP lines have no impact on validation. if (line_trimmed.starts_with("// TIP:")) { continue; } CARBON_ASSIGN_OR_RETURN( is_consumed, TryConsumeCheck(running_autoupdate, line_index, line, line_trimmed, test_file ? &test_file->expected_stdout : nullptr, test_file ? &test_file->expected_stderr : nullptr)); if (is_consumed) { continue; } if (test_file) { // At this point, lines are retained as non-CHECK lines. test_file->non_check_lines.push_back( FileTestLine(split_state.file_index, line_index, line)); } CARBON_ASSIGN_OR_RETURN(is_consumed, TryConsumeArgs(line, line_trimmed, args)); if (is_consumed) { continue; } CARBON_ASSIGN_OR_RETURN(is_consumed, TryConsumeExtraArgs(line_trimmed, extra_args)); if (is_consumed) { continue; } CARBON_ASSIGN_OR_RETURN(is_consumed, TryConsumeIncludeFile(line_trimmed, include_files)); if (is_consumed) { continue; } CARBON_ASSIGN_OR_RETURN( is_consumed, TryConsumeAutoupdate( line_index, line_trimmed, found_autoupdate, test_file ? &test_file->autoupdate_line_number : nullptr)); if (is_consumed) { continue; } CARBON_ASSIGN_OR_RETURN( is_consumed, TryConsumeSetFlag( line_trimmed, "SET-CAPTURE-CONSOLE-OUTPUT", test_file ? &test_file->capture_console_output : nullptr)); if (is_consumed) { continue; } CARBON_ASSIGN_OR_RETURN( is_consumed, TryConsumeSetFlag(line_trimmed, "SET-CHECK-SUBSET", test_file ? &test_file->check_subset : nullptr)); if (is_consumed) { continue; } } CARBON_RETURN_IF_ERROR(FinishSplit(filename, /*is_include_file=*/!test_file, split_state, splits)); if (test_file) { test_file->has_splits = split_state.has_splits(); } return Success(); } auto ProcessTestFile(llvm::StringRef test_name, bool running_autoupdate) -> ErrorOr { TestFile test_file; // Store the original content, to avoid a read when autoupdating. CARBON_ASSIGN_OR_RETURN(test_file.input_content, ReadFile(test_name.str())); // Whether either AUTOUDPATE or NOAUTOUPDATE was found. bool found_autoupdate = false; // INCLUDE-FILE uses, accumulated across both the main file and any includes // (recursively). llvm::SmallVector include_files; // Store the main file's `EXTRA-ARGS` so that they can be put after any that // come from `INCLUDE-FILE`. llvm::SmallVector main_extra_args; // Process the main file. CARBON_RETURN_IF_ERROR(ProcessFileContent( test_name, test_file.input_content, running_autoupdate, &test_file, &found_autoupdate, test_file.test_args, main_extra_args, test_file.file_splits, include_files)); if (!found_autoupdate) { return ErrorBuilder() << "Missing AUTOUPDATE/NOAUTOUPDATE setting"; } constexpr llvm::StringLiteral AutoupdateSplit = "AUTOUPDATE-SPLIT"; // Validate AUTOUPDATE-SPLIT use, and remove it from test files if present. if (test_file.has_splits) { for (const auto& test_file : llvm::ArrayRef(test_file.file_splits).drop_back()) { if (test_file.filename == AutoupdateSplit) { return Error("AUTOUPDATE-SPLIT must be the last split"); } } if (test_file.file_splits.back().filename == AutoupdateSplit) { if (!test_file.autoupdate_line_number) { return Error("AUTOUPDATE-SPLIT requires AUTOUPDATE"); } test_file.autoupdate_split = true; test_file.file_splits.pop_back(); } } // Assume there is always a suffix `\n` in output. if (!test_file.expected_stdout.empty()) { test_file.expected_stdout.push_back(StrEq("")); } if (!test_file.expected_stderr.empty()) { test_file.expected_stderr.push_back(StrEq("")); } // Process includes. This can add entries to `include_files`. Set processed_includes; for (size_t i = 0; i < include_files.size(); ++i) { const auto& filename = include_files[i]; if (!processed_includes.Insert(filename).is_inserted()) { // Ignore repeated includes, mainly so that included files can include the // same file (i.e., repeated indirectly). continue; } CARBON_ASSIGN_OR_RETURN(std::string content, ReadFile(filename)); // Note autoupdate never touches included files. CARBON_RETURN_IF_ERROR(ProcessFileContent( filename, content, /*running_autoupdate=*/false, /*test_file=*/nullptr, /*found_autoupdate=*/nullptr, test_file.test_args, test_file.extra_args, test_file.include_file_splits, include_files)); } for (const auto& split : test_file.include_file_splits) { if (split.filename == AutoupdateSplit) { return Error("AUTOUPDATE-SPLIT is disallowed in included files"); } } // Copy over `EXTRA-ARGS` from the main file (after includes). test_file.extra_args.append(main_extra_args); return std::move(test_file); } } // namespace Carbon::Testing ================================================ FILE: testing/file_test/test_file.h ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #ifndef CARBON_TESTING_FILE_TEST_TEST_FILE_H_ #define CARBON_TESTING_FILE_TEST_TEST_FILE_H_ #include #include #include #include "llvm/ADT/SmallString.h" #include "llvm/ADT/SmallVector.h" #include "testing/file_test/file_test_base.h" #include "testing/file_test/line.h" namespace Carbon::Testing { // A small timer for getting elapsed durations. class Timer { public: explicit Timer() : start_(std::chrono::steady_clock::now()) {} auto elapsed_ms() -> std::chrono::milliseconds { return std::chrono::duration_cast( std::chrono::steady_clock::now() - start_); } private: std::chrono::steady_clock::time_point start_; }; // Encapsulates test context generated by processing and running. // // Note this should remain internal to `FileTestBase`, not exposed to individual // tests. struct TestFile { // Represents a split within the test file. struct Split { friend auto PrintTo(const Split& f, std::ostream* os) -> void { // Print content escaped. llvm::raw_os_ostream os_wrap(*os); os_wrap << "Split(" << f.filename << ", \"" << FormatEscaped(f.content) << "\")"; } std::string filename; std::string content; }; // The input test file content. Other parts may reference this. std::string input_content; // Lines which don't contain CHECKs, and thus need to be retained by // autoupdate. Their file and line numbers are attached. // // If there are splits, then the splitting line is in the respective file. // For N splits, the 0th file is the parts of the input file which are not // in any split, plus one file per split file. llvm::SmallVector non_check_lines; // Whether there are splits. bool has_splits = false; // Arguments for the test, generated from ARGS. llvm::SmallVector test_args; // Extra arguments for the test, generated from EXTRA-ARGS. Unlike ARGS, // setting EXTRA-ARGS does not suppress the default arguments. llvm::SmallVector extra_args; // Files in the test, generated by content and splits. llvm::SmallVector file_splits; // Files pulled in by `INCLUDE-FILE`. They're combined with `file_splits` for // execution. llvm::SmallVector include_file_splits; // The location of the autoupdate marker, for autoupdated files. std::optional autoupdate_line_number; // Whether there should be an AUTOUPDATE-SPLIT. bool autoupdate_split = false; // Whether to capture stderr and stdout that would head to console, // generated from SET-CAPTURE-CONSOLE-OUTPUT. bool capture_console_output = false; // Whether checks are a subset, generated from SET-CHECK-SUBSET. bool check_subset = false; // stdout and stderr based on CHECK lines in the file. llvm::SmallVector> expected_stdout; llvm::SmallVector> expected_stderr; // stdout and stderr from Run. 16 is arbitrary but a required value. llvm::SmallString<16> actual_stdout; llvm::SmallString<16> actual_stderr; FileTestBase::RunResult run_result = {.success = false}; // Time spent inside FileTestBase::Run. std::chrono::milliseconds run_elapsed_ms = std::chrono::milliseconds(0); }; // Processes the test input, producing test files and expected output. auto ProcessTestFile(llvm::StringRef test_name, bool running_autoupdate) -> ErrorOr; } // namespace Carbon::Testing #endif // CARBON_TESTING_FILE_TEST_TEST_FILE_H_ ================================================ FILE: testing/file_test/test_file_test.cpp ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #include "testing/file_test/test_file.h" #include #include #include "common/error_test_helpers.h" #include "testing/base/file_helpers.h" #include "testing/file_test/file_test_base.h" // The current BUILD structure of this library means that in order to unit-test // test_file.h, we have to pretend to be a file_test. That means there has to // be a definition of CarbonFileTestManifest (normally generated by a file_test // Bazel rule), and there has to be a registered FileTestFactory, so we define // them both as dummies. // TODO: restructure BUILD rules to avoid the need for this hack. // Dummy empty manifest. // NOLINTNEXTLINE(readability-identifier-naming): manifest.cpp dictates spelling const char* CarbonFileTestManifest[] = {nullptr}; namespace Carbon::Testing { // Dummy unusable FileTestBase. class DummyFileTest : public FileTestBase { public: DummyFileTest(llvm::StringRef /*exe_path*/, llvm::StringRef test_name) : FileTestBase(test_name) {} auto Run(const llvm::SmallVector& /*test_args*/, llvm::IntrusiveRefCntPtr& /*fs*/, FILE* /*input_stream*/, llvm::raw_pwrite_stream& /*output_stream*/, llvm::raw_pwrite_stream& /*error_stream*/) const -> ErrorOr override { CARBON_FATAL("Called method of dummy object"); } auto GetDefaultArgs() const -> llvm::SmallVector override { CARBON_FATAL("Called method of dummy object"); } }; CARBON_FILE_TEST_FACTORY(DummyFileTest) namespace { using ::testing::_; using ::testing::StrEq; // Returns the non-check lines of test_file as a string. auto NonCheckFileContents(const TestFile& test_file) -> std::string { std::string buffer; llvm::raw_string_ostream out(buffer); for (const auto& line : test_file.non_check_lines) { line.Print(out); out << "\n"; } return buffer; } // Returns a string consisting of 7 repetitions of `c`, for use as a simulated // merge conflict marker. We use this in test inputs instead of writing the // conflict markers directly in the string literals so that tools don't treat // them as genuine merge conflicts in this file. auto Marker(char c) -> std::string { return std::string(7, c); } TEST(AutoupdateTest, SnapshotMergeConflict) { std::string initial_contents = R"( // AUTOUPDATE Some text )" + Marker('<') + R"( // CHECK:STDOUT: side 1 )" + Marker('=') + R"( // CHECK:STDOUT: side 2 )" + Marker('>') + R"( More text )"; constexpr std::string_view ExpectedContents = R"( // AUTOUPDATE Some text More text )"; auto file_path = *WriteTestFile("tmp", initial_contents); auto test_file = ProcessTestFile(file_path.c_str(), /*running_autoupdate=*/true); ASSERT_THAT(test_file, IsSuccess(_)) << test_file.error(); EXPECT_THAT(NonCheckFileContents(*test_file), StrEq(ExpectedContents)); } TEST(AutoupdateTest, SnapshotTheeWayMergeConflict) { std::string initial_contents = R"( // AUTOUPDATE Some text )" + Marker('<') + R"( // CHECK:STDOUT: side 1 )" + Marker('|') + R"( // CHECK:STDOUT: base )" + Marker('=') + R"( // CHECK:STDOUT: side 2 )" + Marker('>') + R"( More text )"; constexpr std::string_view ExpectedContents = R"( // AUTOUPDATE Some text More text )"; auto file_path = *WriteTestFile("tmp", initial_contents); auto test_file = ProcessTestFile(file_path.c_str(), /*running_autoupdate=*/true); ASSERT_THAT(test_file, IsSuccess(_)) << test_file.error(); EXPECT_THAT(NonCheckFileContents(*test_file), StrEq(ExpectedContents)); } TEST(AutoupdateTest, DiffMergeConflict) { std::string initial_contents = R"( // AUTOUPDATE Some text )" + Marker('<') + R"( )" + Marker('%') + R"( )" + Marker('\\') + R"( // CHECK:STDOUT: unchanged -// CHECK:STDOUT: base +// CHECK:STDOUT: side 1 )" + Marker('+') + R"( // CHECK:STDOUT: side 2 )" + Marker('>') + R"( More text )"; constexpr std::string_view ExpectedContents = R"( // AUTOUPDATE Some text More text )"; auto file_path = *WriteTestFile("tmp", initial_contents); auto test_file = ProcessTestFile(file_path.c_str(), /*running_autoupdate=*/true); ASSERT_THAT(test_file, IsSuccess(_)) << test_file.error(); EXPECT_THAT(NonCheckFileContents(*test_file), StrEq(ExpectedContents)); } TEST(AutoupdateTest, NonCheckInDiffRegion) { std::string initial_contents = R"( // AUTOUPDATE Some text )" + Marker('<') + R"( )" + Marker('%') + R"( )" + Marker('\\') + R"( // unchanged -// CHECK:STDOUT: base +// CHECK:STDOUT: side 1 )" + Marker('+') + R"( // CHECK:STDOUT: side 2 )" + Marker('>') + R"( More text )"; auto file_path = *WriteTestFile("tmp", initial_contents); auto test_file = ProcessTestFile(file_path.c_str(), /*running_autoupdate=*/true); ASSERT_THAT(test_file, IsError(_)); } } // namespace } // namespace Carbon::Testing ================================================ FILE: testing/file_test/testdata/alternating_files.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //testing/file_test:file_test_base_test --test_arg=--file_tests=testing/file_test/testdata/alternating_files.carbon // TIP: To dump output, run: // TIP: bazel run //testing/file_test:file_test_base_test -- --dump_output --file_tests=testing/file_test/testdata/alternating_files.carbon // CHECK:STDERR: unattached message 1 // CHECK:STDOUT: 3 args: `default_args`, `a.carbon`, `b.carbon` // CHECK:STDOUT: unattached message 1 // --- a.carbon // CHECK:STDERR: a.carbon:[[@LINE+1]]: message 2 aaa // CHECK:STDOUT: a.carbon:[[@LINE-1]]: message 2 // --- b.carbon // CHECK:STDERR: b.carbon:[[@LINE+4]]: message 3 // CHECK:STDERR: a.carbon:2: message 4 // CHECK:STDERR: b.carbon:[[@LINE+2]]: message 5 // CHECK:STDERR: unattached message 6 bbb // CHECK:STDOUT: b.carbon:[[@LINE-1]]: message 3 // CHECK:STDOUT: a.carbon:2: message 4 // CHECK:STDOUT: b.carbon:[[@LINE-3]]: message 5 // CHECK:STDOUT: unattached message 6 bbbbbb ================================================ FILE: testing/file_test/testdata/args.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // ARGS: abc file=%t %s prefix_%{replacement}_suffix // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //testing/file_test:file_test_base_test --test_arg=--file_tests=testing/file_test/testdata/args.carbon // TIP: To dump output, run: // TIP: bazel run //testing/file_test:file_test_base_test -- --dump_output --file_tests=testing/file_test/testdata/args.carbon // CHECK:STDOUT: 4 args: `abc`, `file={{.+}}/temp_file`, `args.carbon`, `prefix_replaced_suffix` ================================================ FILE: testing/file_test/testdata/autoupdate_split.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //testing/file_test:file_test_base_test --test_arg=--file_tests=testing/file_test/testdata/autoupdate_split.carbon // TIP: To dump output, run: // TIP: bazel run //testing/file_test:file_test_base_test -- --dump_output --file_tests=testing/file_test/testdata/autoupdate_split.carbon // --- a.carbon aaa // --- b.carbon bbb // --- AUTOUPDATE-SPLIT // CHECK:STDOUT: 3 args: `default_args`, `a.carbon`, `b.carbon` // CHECK:STDOUT: a.carbon:1: aaa // CHECK:STDOUT: b.carbon:1: bbb ================================================ FILE: testing/file_test/testdata/autoupdate_split_standalone.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //testing/file_test:file_test_base_test --test_arg=--file_tests=testing/file_test/testdata/autoupdate_split_standalone.carbon // TIP: To dump output, run: // TIP: bazel run //testing/file_test:file_test_base_test -- --dump_output --file_tests=testing/file_test/testdata/autoupdate_split_standalone.carbon // --- AUTOUPDATE-SPLIT // CHECK:STDOUT: 1 args: `default_args` ================================================ FILE: testing/file_test/testdata/capture_console_output.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // SET-CAPTURE-CONSOLE-OUTPUT // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //testing/file_test:file_test_base_test --test_arg=--file_tests=testing/file_test/testdata/capture_console_output.carbon // TIP: To dump output, run: // TIP: bazel run //testing/file_test:file_test_base_test -- --dump_output --file_tests=testing/file_test/testdata/capture_console_output.carbon // CHECK:STDERR: params.error_stream // CHECK:STDERR: llvm::errs // CHECK:STDOUT: 2 args: `default_args`, `capture_console_output.carbon` // CHECK:STDOUT: params.output_stream // CHECK:STDOUT: llvm::outs ================================================ FILE: testing/file_test/testdata/escaping.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // SET-CAPTURE-CONSOLE-OUTPUT // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //testing/file_test:file_test_base_test --test_arg=--file_tests=testing/file_test/testdata/escaping.carbon // TIP: To dump output, run: // TIP: bazel run //testing/file_test:file_test_base_test -- --dump_output --file_tests=testing/file_test/testdata/escaping.carbon // CHECK:STDERR: carriage return{{\r}} // CHECK:STDERR: {one brace} // CHECK:STDERR: {{\{\{}}two braces}} // CHECK:STDERR: [one bracket] // CHECK:STDERR: {{\[\[}}two brackets]] // CHECK:STDERR: end of line whitespace {{}} // CHECK:STDERR: {{\t}}tabs{{\t}} // CHECK:STDOUT: 2 args: `default_args`, `escaping.carbon` ================================================ FILE: testing/file_test/testdata/example.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // Can't autoupdate due to the `{{.+}}` check. // NOAUTOUPDATE // CHECK:STDOUT: 2 args: `default_args`, `example.carbon` // CHECK:STDOUT: something // CHECK:STDOUT: // CHECK:STDOUT: example.carbon:[[@LINE+1]]: Line delta // CHECK:STDOUT: example.carbon:[[@LINE-1]]: Negative line delta // CHECK:STDOUT: +*[]{} // CHECK:STDOUT: F{{.+}}z ================================================ FILE: testing/file_test/testdata/fail_example.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //testing/file_test:file_test_base_test --test_arg=--file_tests=testing/file_test/testdata/fail_example.carbon // TIP: To dump output, run: // TIP: bazel run //testing/file_test:file_test_base_test -- --dump_output --file_tests=testing/file_test/testdata/fail_example.carbon // CHECK:STDERR: Oops // CHECK:STDOUT: 2 args: `default_args`, `fail_example.carbon` ================================================ FILE: testing/file_test/testdata/fail_multi_success_overall_fail.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //testing/file_test:file_test_base_test --test_arg=--file_tests=testing/file_test/testdata/fail_multi_success_overall_fail.carbon // TIP: To dump output, run: // TIP: bazel run //testing/file_test:file_test_base_test -- --dump_output --file_tests=testing/file_test/testdata/fail_multi_success_overall_fail.carbon // --- a.carbon aaa // --- b.carbon bbb // CHECK:STDOUT: 3 args: `default_args`, `a.carbon`, `b.carbon` ================================================ FILE: testing/file_test/testdata/file_only_re_multi_file.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //testing/file_test:file_test_base_test --test_arg=--file_tests=testing/file_test/testdata/file_only_re_multi_file.carbon // TIP: To dump output, run: // TIP: bazel run //testing/file_test:file_test_base_test -- --dump_output --file_tests=testing/file_test/testdata/file_only_re_multi_file.carbon // CHECK:STDOUT: 3 args: `default_args`, `a.carbon`, `b.carbon` // CHECK:STDOUT: unattached message 1 // --- a.carbon // CHECK:STDOUT: file: a.carbon // CHECK:STDOUT: unattached message 2 aaa // CHECK:STDOUT: line: [[@LINE-1]]: attached message 3 // CHECK:STDOUT: unattached message 4 // CHECK:STDOUT: line: [[@LINE+1]]: late message 5 // CHECK:STDOUT: unattached message 6 // --- b.carbon // CHECK:STDOUT: file: b.carbon bbb // CHECK:STDOUT: line: [[@LINE-1]]: attached message 7 // CHECK:STDOUT: unattached message 8 // CHECK:STDOUT: line: [[@LINE+1]]: late message 9 // CHECK:STDOUT: unattached message 10 ================================================ FILE: testing/file_test/testdata/file_only_re_one_file.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //testing/file_test:file_test_base_test --test_arg=--file_tests=testing/file_test/testdata/file_only_re_one_file.carbon // TIP: To dump output, run: // TIP: bazel run //testing/file_test:file_test_base_test -- --dump_output --file_tests=testing/file_test/testdata/file_only_re_one_file.carbon // CHECK:STDOUT: 2 args: `default_args`, `file_only_re_one_file.carbon` // CHECK:STDOUT: unattached message 1 // CHECK:STDOUT: file: file_only_re_one_file.carbon // CHECK:STDOUT: line: [[@LINE-12]] // CHECK:STDOUT: unattached message 2 ================================================ FILE: testing/file_test/testdata/include_args.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: testing/file_test/testdata/include_files/args.carbon // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //testing/file_test:file_test_base_test --test_arg=--file_tests=testing/file_test/testdata/include_args.carbon // TIP: To dump output, run: // TIP: bazel run //testing/file_test:file_test_base_test -- --dump_output --file_tests=testing/file_test/testdata/include_args.carbon // CHECK:STDOUT: 3 args: `foo`, `bar`, `baz` ================================================ FILE: testing/file_test/testdata/include_args_and_extra_args.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: testing/file_test/testdata/include_files/extra_args.carbon // ARGS: foo // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //testing/file_test:file_test_base_test --test_arg=--file_tests=testing/file_test/testdata/include_args_and_extra_args.carbon // TIP: To dump output, run: // TIP: bazel run //testing/file_test:file_test_base_test -- --dump_output --file_tests=testing/file_test/testdata/include_args_and_extra_args.carbon // CHECK:STDOUT: 3 args: `foo`, `bar`, `baz` ================================================ FILE: testing/file_test/testdata/include_empty.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: testing/file_test/testdata/include_files/empty.carbon // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //testing/file_test:file_test_base_test --test_arg=--file_tests=testing/file_test/testdata/include_empty.carbon // TIP: To dump output, run: // TIP: bazel run //testing/file_test:file_test_base_test -- --dump_output --file_tests=testing/file_test/testdata/include_empty.carbon // CHECK:STDOUT: 3 args: `default_args`, `empty.carbon`, `include_empty.carbon` ================================================ FILE: testing/file_test/testdata/include_extra_args.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // EXTRA-ARGS: foo // INCLUDE-FILE: testing/file_test/testdata/include_files/extra_args.carbon // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //testing/file_test:file_test_base_test --test_arg=--file_tests=testing/file_test/testdata/include_extra_args.carbon // TIP: To dump output, run: // TIP: bazel run //testing/file_test:file_test_base_test -- --dump_output --file_tests=testing/file_test/testdata/include_extra_args.carbon // CHECK:STDOUT: 6 args: `default_args`, `include_files/extra_args.carbon`, `include_extra_args.carbon`, `bar`, `baz`, `foo` ================================================ FILE: testing/file_test/testdata/include_files/args.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // ARGS: foo bar baz ================================================ FILE: testing/file_test/testdata/include_files/empty.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // --- empty.carbon ================================================ FILE: testing/file_test/testdata/include_files/extra_args.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // EXTRA-ARGS: bar baz ================================================ FILE: testing/file_test/testdata/include_files/no_split.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception no split ================================================ FILE: testing/file_test/testdata/include_files/recursive.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // INCLUDE-FILE: testing/file_test/testdata/include_files/split.carbon // --- c.carbon c // --- d.carbon d ================================================ FILE: testing/file_test/testdata/include_files/split.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // --- a.carbon a // --- b.carbon b ================================================ FILE: testing/file_test/testdata/include_no_split.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: testing/file_test/testdata/include_files/no_split.carbon // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //testing/file_test:file_test_base_test --test_arg=--file_tests=testing/file_test/testdata/include_no_split.carbon // TIP: To dump output, run: // TIP: bazel run //testing/file_test:file_test_base_test -- --dump_output --file_tests=testing/file_test/testdata/include_no_split.carbon // CHECK:STDOUT: 3 args: `default_args`, `include_files/no_split.carbon`, `include_no_split.carbon` // CHECK:STDOUT: include_files/no_split.carbon:5: no split ================================================ FILE: testing/file_test/testdata/include_recursive.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: testing/file_test/testdata/include_files/recursive.carbon // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //testing/file_test:file_test_base_test --test_arg=--file_tests=testing/file_test/testdata/include_recursive.carbon // TIP: To dump output, run: // TIP: bazel run //testing/file_test:file_test_base_test -- --dump_output --file_tests=testing/file_test/testdata/include_recursive.carbon // CHECK:STDOUT: 6 args: `default_args`, `c.carbon`, `d.carbon`, `a.carbon`, `b.carbon`, `include_recursive.carbon` // CHECK:STDOUT: c.carbon:2: c // CHECK:STDOUT: d.carbon:2: d // CHECK:STDOUT: a.carbon:2: a // CHECK:STDOUT: b.carbon:2: b ================================================ FILE: testing/file_test/testdata/include_repeated.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: testing/file_test/testdata/include_files/no_split.carbon // INCLUDE-FILE: testing/file_test/testdata/include_files/no_split.carbon // INCLUDE-FILE: testing/file_test/testdata/include_files/no_split.carbon // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //testing/file_test:file_test_base_test --test_arg=--file_tests=testing/file_test/testdata/include_repeated.carbon // TIP: To dump output, run: // TIP: bazel run //testing/file_test:file_test_base_test -- --dump_output --file_tests=testing/file_test/testdata/include_repeated.carbon // CHECK:STDOUT: 3 args: `default_args`, `include_files/no_split.carbon`, `include_repeated.carbon` // CHECK:STDOUT: include_files/no_split.carbon:5: no split ================================================ FILE: testing/file_test/testdata/include_split.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: testing/file_test/testdata/include_files/split.carbon // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //testing/file_test:file_test_base_test --test_arg=--file_tests=testing/file_test/testdata/include_split.carbon // TIP: To dump output, run: // TIP: bazel run //testing/file_test:file_test_base_test -- --dump_output --file_tests=testing/file_test/testdata/include_split.carbon // CHECK:STDOUT: 4 args: `default_args`, `a.carbon`, `b.carbon`, `include_split.carbon` // CHECK:STDOUT: a.carbon:2: a // CHECK:STDOUT: b.carbon:2: b ================================================ FILE: testing/file_test/testdata/lsp_autofill.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //testing/file_test:file_test_base_test --test_arg=--file_tests=testing/file_test/testdata/lsp_autofill.carbon // TIP: To dump output, run: // TIP: bazel run //testing/file_test:file_test_base_test -- --dump_output --file_tests=testing/file_test/testdata/lsp_autofill.carbon // --- foo.carbon class Foo { fn foo(); fn bar() {} } // --- STDIN [[@LSP-NOTIFY:textDocument/didOpen: "textDocument": { "uri": "file:/foo.carbon", "languageId": "carbon", "text": "FROM_FILE_SPLIT" } ]] // --- AUTOUPDATE-SPLIT // CHECK:STDERR: --- STDIN: // CHECK:STDERR: Content-Length: 182 // CHECK:STDERR: // CHECK:STDERR: {"jsonrpc":"2.0","method":"textDocument/didOpen","params":{"textDocument":{"languageId":"carbon","text":"class Foo {\n fn foo();\n fn bar() {}\n}\n\n","uri":"file:/foo.carbon"}}} // CHECK:STDERR: // CHECK:STDERR: // CHECK:STDOUT: 2 args: `default_args`, `foo.carbon` // CHECK:STDOUT: foo.carbon:1: class Foo { // CHECK:STDOUT: foo.carbon:2: fn foo(); // CHECK:STDOUT: foo.carbon:3: fn bar() {} // CHECK:STDOUT: foo.carbon:4: } ================================================ FILE: testing/file_test/testdata/lsp_keywords.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //testing/file_test:file_test_base_test --test_arg=--file_tests=testing/file_test/testdata/lsp_keywords.carbon // TIP: To dump output, run: // TIP: bazel run //testing/file_test:file_test_base_test -- --dump_output --file_tests=testing/file_test/testdata/lsp_keywords.carbon // --- STDIN [[@LSP:foo:]] [[@LSP:foo]] [[@LSP:bar:"content": 0]] [[@LSP:baz: "multi": 0, "line": 1 ]] [[@LSP-CALL:bar:"content": 0]] [[@LSP-CALL:baz: "multi": 0, "line": 1]] [[@LSP-REPLY:7]] [[@LSP-REPLY:8:"bar": 0]] [[@LSP-NOTIFY:exit]] // --- AUTOUPDATE-SPLIT // CHECK:STDERR: --- STDIN: // CHECK:STDERR: Content-Length: 34 // CHECK:STDERR: // CHECK:STDERR: {"jsonrpc":"2.0","method":"foo"} // CHECK:STDERR: // CHECK:STDERR: Content-Length: 34 // CHECK:STDERR: // CHECK:STDERR: {"jsonrpc":"2.0","method":"foo"} // CHECK:STDERR: // CHECK:STDERR: Content-Length: 46 // CHECK:STDERR: // CHECK:STDERR: {"jsonrpc":"2.0","method":"bar","content":0} // CHECK:STDERR: // CHECK:STDERR: Content-Length: 53 // CHECK:STDERR: // CHECK:STDERR: {"jsonrpc":"2.0","method":"baz","multi":0,"line":1} // CHECK:STDERR: // CHECK:STDERR: Content-Length: 64 // CHECK:STDERR: // CHECK:STDERR: {"jsonrpc":"2.0","method":"bar","id":1,"params":{"content":0}} // CHECK:STDERR: // CHECK:STDERR: Content-Length: 71 // CHECK:STDERR: // CHECK:STDERR: {"jsonrpc":"2.0","method":"baz","id":2,"params":{"line":1,"multi":0}} // CHECK:STDERR: // CHECK:STDERR: Content-Length: 28 // CHECK:STDERR: // CHECK:STDERR: {"jsonrpc":"2.0","id":"7"} // CHECK:STDERR: // CHECK:STDERR: Content-Length: 47 // CHECK:STDERR: // CHECK:STDERR: {"jsonrpc":"2.0","id":"8","result":{"bar":0}} // CHECK:STDERR: // CHECK:STDERR: Content-Length: 35 // CHECK:STDERR: // CHECK:STDERR: {"jsonrpc":"2.0","method":"exit"} // CHECK:STDERR: // CHECK:STDERR: // CHECK:STDOUT: 1 args: `default_args` ================================================ FILE: testing/file_test/testdata/multi_success.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //testing/file_test:file_test_base_test --test_arg=--file_tests=testing/file_test/testdata/multi_success.carbon // TIP: To dump output, run: // TIP: bazel run //testing/file_test:file_test_base_test -- --dump_output --file_tests=testing/file_test/testdata/multi_success.carbon // --- a.carbon aaa // --- b.carbon bbb // CHECK:STDOUT: 3 args: `default_args`, `a.carbon`, `b.carbon` ================================================ FILE: testing/file_test/testdata/multi_success_and_fail.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //testing/file_test:file_test_base_test --test_arg=--file_tests=testing/file_test/testdata/multi_success_and_fail.carbon // TIP: To dump output, run: // TIP: bazel run //testing/file_test:file_test_base_test -- --dump_output --file_tests=testing/file_test/testdata/multi_success_and_fail.carbon // --- a.carbon aaa // --- fail_b.carbon bbb // CHECK:STDOUT: 3 args: `default_args`, `a.carbon`, `fail_b.carbon` ================================================ FILE: testing/file_test/testdata/multiple_file_refs_in_line.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //testing/file_test:file_test_base_test --test_arg=--file_tests=testing/file_test/testdata/multiple_file_refs_in_line.carbon // TIP: To dump output, run: // TIP: bazel run //testing/file_test:file_test_base_test -- --dump_output --file_tests=testing/file_test/testdata/multiple_file_refs_in_line.carbon // CHECK:STDOUT: 3 args: `default_args`, `a.carbon`, `b.carbon` // --- a.carbon a.carbon:1: b.carbon:3: a.carbon:3: hello // CHECK:STDOUT: a.carbon:[[@LINE-1]]: a.carbon:[[@LINE-1]]: b.carbon:3: a.carbon:[[@LINE+1]]: hello // --- b.carbon a.carbon:1: b.carbon:3: a.carbon:3: hello // CHECK:STDOUT: b.carbon:[[@LINE-1]]: a.carbon:1: b.carbon:[[@LINE-1]]: a.carbon:3: hello a.carbon:1: b.carbon:5: a.carbon:3: hello // CHECK:STDOUT: b.carbon:[[@LINE-1]]: a.carbon:1: b.carbon:[[@LINE-1]]: a.carbon:3: hello a.carbon:1: b.carbon:7: a.carbon:3: hello // CHECK:STDOUT: b.carbon:[[@LINE-1]]: a.carbon:1: b.carbon:[[@LINE-1]]: a.carbon:3: hello ================================================ FILE: testing/file_test/testdata/no_line_number.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //testing/file_test:file_test_base_test --test_arg=--file_tests=testing/file_test/testdata/no_line_number.carbon // TIP: To dump output, run: // TIP: bazel run //testing/file_test:file_test_base_test -- --dump_output --file_tests=testing/file_test/testdata/no_line_number.carbon // CHECK:STDOUT: 3 args: `default_args`, `a.carbon`, `b.carbon` // --- a.carbon // CHECK:STDOUT: a.carbon: msg1 // CHECK:STDOUT: msg2 aaa // --- b.carbon // CHECK:STDOUT: b.carbon: msg3 // CHECK:STDOUT: msg4 // CHECK:STDOUT: a.carbon: msg5 bbb ================================================ FILE: testing/file_test/testdata/not_split.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //testing/file_test:file_test_base_test --test_arg=--file_tests=testing/file_test/testdata/not_split.carbon // TIP: To dump output, run: // TIP: bazel run //testing/file_test:file_test_base_test -- --dump_output --file_tests=testing/file_test/testdata/not_split.carbon // CHECK:STDOUT: 2 args: `default_args`, `not_split.carbon` not split // CHECK:STDOUT: not_split.carbon:[[@LINE-1]]: not split ================================================ FILE: testing/file_test/testdata/replace_content.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //testing/file_test:file_test_base_test --test_arg=--file_tests=testing/file_test/testdata/replace_content.carbon // TIP: To dump output, run: // TIP: bazel run //testing/file_test:file_test_base_test -- --dump_output --file_tests=testing/file_test/testdata/replace_content.carbon // CHECK:STDOUT: 2 args: `default_args`, `replace_content.carbon` library "[[@TEST_NAME]]"; // CHECK:STDOUT: replace_content.carbon:[[@LINE-1]]: library "replace_content"; var x: str = "[[@0x48]][[@0x65]][[@0x6C]][[@0x6C]][[@0x6F]]"; // CHECK:STDOUT: replace_content.carbon:[[@LINE-1]]: var x: str = "Hello"; ================================================ FILE: testing/file_test/testdata/replace_split_content.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //testing/file_test:file_test_base_test --test_arg=--file_tests=testing/file_test/testdata/replace_split_content.carbon // TIP: To dump output, run: // TIP: bazel run //testing/file_test:file_test_base_test -- --dump_output --file_tests=testing/file_test/testdata/replace_split_content.carbon // CHECK:STDOUT: 6 args: `default_args`, `a.carbon`, `b.impl.carbon`, `todo_c.carbon`, `todo_fail_d.carbon`, `triplicate.carbon` // --- a.carbon library "[[@TEST_NAME]]"; // CHECK:STDOUT: a.carbon:[[@LINE-1]]: library "a"; // --- b.impl.carbon library "[[@TEST_NAME]]"; // CHECK:STDOUT: b.impl.carbon:[[@LINE-1]]: library "b"; // --- todo_c.carbon library "[[@TEST_NAME]]"; // CHECK:STDOUT: todo_c.carbon:[[@LINE-1]]: library "c"; // --- todo_fail_d.carbon library "[[@TEST_NAME]]"; // CHECK:STDOUT: todo_fail_d.carbon:[[@LINE-1]]: library "d"; // --- triplicate.carbon [[@TEST_NAME]][[@TEST_NAME]][[@TEST_NAME]] // CHECK:STDOUT: triplicate.carbon:[[@LINE-1]]: triplicatetriplicatetriplicate ================================================ FILE: testing/file_test/testdata/stdin.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //testing/file_test:file_test_base_test --test_arg=--file_tests=testing/file_test/testdata/stdin.carbon // TIP: To dump output, run: // TIP: bazel run //testing/file_test:file_test_base_test -- --dump_output --file_tests=testing/file_test/testdata/stdin.carbon // --- a.carbon // --- STDIN S t d i n // --- AUTOUPDATE-SPLIT // CHECK:STDERR: --- STDIN: // CHECK:STDERR: // CHECK:STDERR: S // CHECK:STDERR: t // CHECK:STDERR: d // CHECK:STDERR: i // CHECK:STDERR: n // CHECK:STDERR: // CHECK:STDOUT: 2 args: `default_args`, `a.carbon` ================================================ FILE: testing/file_test/testdata/two_files.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //testing/file_test:file_test_base_test --test_arg=--file_tests=testing/file_test/testdata/two_files.carbon // TIP: To dump output, run: // TIP: bazel run //testing/file_test:file_test_base_test -- --dump_output --file_tests=testing/file_test/testdata/two_files.carbon // CHECK:STDOUT: 3 args: `default_args`, `a.carbon`, `b.carbon` // --- a.carbon aaa // CHECK:STDOUT: a.carbon:[[@LINE-1]]: aaa // --- b.carbon bbb // CHECK:STDOUT: b.carbon:[[@LINE-1]]: bbb ================================================ FILE: testing/file_test/testdata/unattached_multi_file.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //testing/file_test:file_test_base_test --test_arg=--file_tests=testing/file_test/testdata/unattached_multi_file.carbon // TIP: To dump output, run: // TIP: bazel run //testing/file_test:file_test_base_test -- --dump_output --file_tests=testing/file_test/testdata/unattached_multi_file.carbon // CHECK:STDERR: unattached message 3 // CHECK:STDERR: unattached message 4 // --- a.carbon aaa // --- b.carbon bbb // CHECK:STDOUT: 3 args: `default_args`, `a.carbon`, `b.carbon` // CHECK:STDOUT: unattached message 1 // CHECK:STDOUT: unattached message 2 ================================================ FILE: testing/fuzzing/BUILD ================================================ # Part of the Carbon Language project, under the Apache License v2.0 with LLVM # Exceptions. See /LICENSE for license information. # SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception load("//bazel/cc_rules:defs.bzl", "cc_library") package(default_visibility = ["//visibility:public"]) # Header for LibFuzzer, does not provide the implementation which should come # from some other source such as a fuzz test target. cc_library( name = "libfuzzer_header", testonly = 1, hdrs = ["libfuzzer.h"], ) ================================================ FILE: testing/fuzzing/libfuzzer.h ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #ifndef CARBON_TESTING_FUZZING_LIBFUZZER_H_ #define CARBON_TESTING_FUZZING_LIBFUZZER_H_ #include namespace Carbon::Testing { // Declaration for the LLVM libfuzzer API that we implement in our fuzz tests. // This is useful to ensure we get the API correct and avoid warnings about // defining an undeclared extern function due to a Clang warning bug: // https://github.com/llvm/llvm-project/issues/94138 // NOLINTNEXTLINE: Match the documented fuzzer entry point declaration style. extern "C" int LLVMFuzzerTestOneInput(const unsigned char* data, size_t size); // Optional API that can be implemented but isn't required. This allows fuzzers // to observe the `argv` during initialization. // NOLINTNEXTLINE: Match the documented fuzzer entry point declaration style. extern "C" int LLVMFuzzerInitialize(int* argc, char*** argv); } // namespace Carbon::Testing #endif // CARBON_TESTING_FUZZING_LIBFUZZER_H_ ================================================ FILE: testing/fuzzing/rules.bzl ================================================ # Part of the Carbon Language project, under the Apache License v2.0 with LLVM # Exceptions. See /LICENSE for license information. # SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception """Rules for building fuzz tests.""" load("//bazel/cc_rules:defs.bzl", "cc_test") def _cc_fuzz_test(corpus, args, data, **kwargs): """Generates a single test target. Append the corpus files to the test arguments. When run on a list of files rather than a directory, libFuzzer-based fuzzers will perform a regression test against the corpus. """ cc_test( data = data + corpus, args = args + ["$(location %s)" % file for file in corpus], **kwargs ) def cc_fuzz_test( name, corpus, args = [], data = [], features = [], tags = [], deps = [], shard_count = 1, **kwargs): """Macro for C++ fuzzing test. In order to run tests on a single file, run the fuzzer binary under bazel-bin directly. That will avoid the args being passed by Bazel. Args: name: The main fuzz test rule name. corpus: List of files to use as a fuzzing corpus. args: Will have the locations of the corpus files added and passed down to the fuzz test. data: Will have the corpus added and passed down to the fuzz test. features: Will have the "fuzzer" feature added and passed down to the fuzz test. tags: Will have "fuzz_test" added and passed down to the fuzz test. deps: Will have "@llvm-project//compiler-rt:FuzzerMain" added and passed down to the fuzz test. shard_count: Provides sharding of the fuzz test. **kwargs: Remaining arguments passed down to the fuzz test. """ # Add relevant tag and feature if necessary. if "fuzz_test" not in tags: tags = tags + ["fuzz_test"] if "fuzzer" not in features: features = features + ["fuzzer"] if "@llvm-project//compiler-rt:FuzzerMain" not in deps: deps = deps + ["@llvm-project//compiler-rt:FuzzerMain"] # The FuzzerMain library doesn't support sharding based on inputs, so we # general separate test targets in order to shard execution. if shard_count == 1: # When there's one shard, only one target is needed. _cc_fuzz_test( corpus, args, data, name = name, features = features, tags = tags, deps = deps, **kwargs ) else: # Calculate the number of inputs per shard. This is equivalent to # ceiling division, so that the corpus subsetting doesn't miss odd # files. shard_size = len(corpus) // shard_count if shard_count * shard_size < len(corpus): shard_size += 1 # Create separate targets for each shard. shards = [] for shard in range(shard_count): shard_name = "{0}.shard{1}".format(name, shard) shards.append(shard_name) _cc_fuzz_test( corpus[shard * shard_size:(shard + 1) * shard_size], args, data, name = shard_name, features = features, tags = tags, deps = deps, **kwargs ) # Create a suite containing all shards. native.test_suite( name = name, tests = shards, ) # Create one target that includes the full corpus. _cc_fuzz_test( corpus, args, data, name = "{0}.full_corpus".format(name), features = features, tags = tags + ["manual"], deps = deps, **kwargs ) ================================================ FILE: third_party/README.md ================================================ # Third-party The `/third_party` directory is for any code that doesn't follow Carbon's normal contribution rules, and thus requires special care. The intent is that code _not_ under `/third_party` should not require special contribution rules. Examples of code belonging under `/third_party` include: - Code not licensed under the standard [Apache-2.0 WITH LLVM-exception license](/LICENSE). - Code which is maintained externally or imported from another repository. - Code which is not fully under [Carbon's CLA](/CONTRIBUTING.md#contributor-license-agreements-clas). ================================================ FILE: third_party/examples/README.md ================================================ # Third-party examples This directory contains third-party examples of C++ libraries and how they might be ported to Carbon. The goal is to illustrate both how Carbon works using real-world code as well as relevant and expected steps when migrating from C++ to Carbon. TODO: Example submodules need to be replaced ([issue #1067](https://github.com/carbon-language/carbon-lang/issues/1067)). ================================================ FILE: third_party/examples/re2/LICENSE ================================================ // Copyright (c) 2009 The RE2 Authors. All rights reserved. // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: // // * Redistributions of source code must retain the above copyright // notice, this list of conditions and the following disclaimer. // * Redistributions in binary form must reproduce the above // copyright notice, this list of conditions and the following disclaimer // in the documentation and/or other materials provided with the // distribution. // * Neither the name of Google Inc. nor the names of its // contributors may be used to endorse or promote products derived from // this software without specific prior written permission. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ================================================ FILE: third_party/examples/re2/re2.carbon ================================================ // Copyright 2003-2009 The RE2 Authors. All Rights Reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // TODO: Package name conflicts with member class RE2! package RE2 api; // C++ interface to the re2 regular-expression library. // RE2 supports Perl-style regular expressions (with extensions like // \d, \w, \s, ...). // // ----------------------------------------------------------------------- // REGEXP SYNTAX: // // This module uses the re2 library and hence supports // its syntax for regular expressions, which is similar to Perl's with // some of the more complicated things thrown away. In particular, // backreferences and generalized assertions are not available, nor is \Z. // // See https://github.com/google/re2/wiki/Syntax for the syntax // supported by RE2, and a comparison with PCRE and PERL regexps. // // For those not familiar with Perl's regular expressions, // here are some examples of the most commonly used extensions: // // "hello (\\w+) world" -- \w matches a "word" character // "version (\\d+)" -- \d matches a digit // "hello\\s+world" -- \s matches any whitespace character // "\\b(\\w+)\\b" -- \b matches non-empty string at word boundary // "(?i)hello" -- (?i) turns on case-insensitive matching // "/\\*(.*?)\\*/" -- .*? matches . minimum no. of times possible // // The double backslashes are needed when writing C++ string literals. // However, they should NOT be used when writing C++11 raw string literals: // // R"(hello (\w+) world)" -- \w matches a "word" character // R"(version (\d+))" -- \d matches a digit // R"(hello\s+world)" -- \s matches any whitespace character // R"(\b(\w+)\b)" -- \b matches non-empty string at word boundary // R"((?i)hello)" -- (?i) turns on case-insensitive matching // R"(/\*(.*?)\*/)" -- .*? matches . minimum no. of times possible // // When using UTF-8 encoding, case-insensitive matching will perform // simple case folding, not full case folding. // // ----------------------------------------------------------------------- // MATCHING INTERFACE: // // The "FullMatch" operation checks that supplied text matches a // supplied pattern exactly. // // Example: successful match // CHECK(RE2::FullMatch("hello", "h.*o")); // // Example: unsuccessful match (requires full match): // CHECK(!RE2::FullMatch("hello", "e")); // // ----------------------------------------------------------------------- // UTF-8 AND THE MATCHING INTERFACE: // // By default, the pattern and input text are interpreted as UTF-8. // The RE2::Latin1 option causes them to be interpreted as Latin-1. // // Example: // CHECK(RE2::FullMatch(utf8_string, RE2(utf8_pattern))); // CHECK(RE2::FullMatch(latin1_string, RE2(latin1_pattern, RE2::Latin1))); // // ----------------------------------------------------------------------- // MATCHING WITH SUBSTRING EXTRACTION: // // You can supply extra pointer arguments to extract matched substrings. // On match failure, none of the pointees will have been modified. // On match success, the substrings will be converted (as necessary) and // their values will be assigned to their pointees until all conversions // have succeeded or one conversion has failed. // On conversion failure, the pointees will be in an indeterminate state // because the caller has no way of knowing which conversion failed. // However, conversion cannot fail for types like string and StringPiece // that do not inspect the substring contents. Hence, in the common case // where all of the pointees are of such types, failure is always due to // match failure and thus none of the pointees will have been modified. // // Example: extracts "ruby" into "s" and 1234 into "i" // int i; // std::string s; // CHECK(RE2::FullMatch("ruby:1234", "(\\w+):(\\d+)", &s, &i)); // // Example: fails because string cannot be stored in integer // CHECK(!RE2::FullMatch("ruby", "(.*)", &i)); // // Example: fails because there aren't enough sub-patterns // CHECK(!RE2::FullMatch("ruby:1234", "\\w+:\\d+", &s)); // // Example: does not try to extract any extra sub-patterns // CHECK(RE2::FullMatch("ruby:1234", "(\\w+):(\\d+)", &s)); // // Example: does not try to extract into NULL // CHECK(RE2::FullMatch("ruby:1234", "(\\w+):(\\d+)", NULL, &i)); // // Example: integer overflow causes failure // CHECK(!RE2::FullMatch("ruby:1234567891234", "\\w+:(\\d+)", &i)); // // NOTE(rsc): Asking for substrings slows successful matches quite a bit. // This may get a little faster in the future, but right now is slower // than PCRE. On the other hand, failed matches run *very* fast (faster // than PCRE), as do matches without substring extraction. // // ----------------------------------------------------------------------- // PARTIAL MATCHES // // You can use the "PartialMatch" operation when you want the pattern // to match any substring of the text. // // Example: simple search for a string: // CHECK(RE2::PartialMatch("hello", "ell")); // // Example: find first number in a string // int number; // CHECK(RE2::PartialMatch("x*100 + 20", "(\\d+)", &number)); // CHECK_EQ(number, 100); // // ----------------------------------------------------------------------- // PRE-COMPILED REGULAR EXPRESSIONS // // RE2 makes it easy to use any string as a regular expression, without // requiring a separate compilation step. // // If speed is of the essence, you can create a pre-compiled "RE2" // object from the pattern and use it multiple times. If you do so, // you can typically parse text faster than with sscanf. // // Example: precompile pattern for faster matching: // RE2 pattern("h.*o"); // while (ReadLine(&str)) { // if (RE2::FullMatch(str, pattern)) ...; // } // // ----------------------------------------------------------------------- // SCANNING TEXT INCREMENTALLY // // The "Consume" operation may be useful if you want to repeatedly // match regular expressions at the front of a string and skip over // them as they match. This requires use of the "StringPiece" type, // which represents a sub-range of a real string. // // Example: read lines of the form "var = value" from a string. // std::string contents = ...; // Fill string somehow // StringPiece input(contents); // Wrap a StringPiece around it // // std::string var; // int value; // while (RE2::Consume(&input, "(\\w+) = (\\d+)\n", &var, &value)) { // ...; // } // // Each successful call to "Consume" will set "var/value", and also // advance "input" so it points past the matched text. Note that if the // regular expression matches an empty string, input will advance // by 0 bytes. If the regular expression being used might match // an empty string, the loop body must check for this case and either // advance the string or break out of the loop. // // The "FindAndConsume" operation is similar to "Consume" but does not // anchor your match at the beginning of the string. For example, you // could extract all words from a string by repeatedly calling // RE2::FindAndConsume(&input, "(\\w+)", &word) // // ----------------------------------------------------------------------- // USING VARIABLE NUMBER OF ARGUMENTS // // The above operations require you to know the number of arguments // when you write the code. This is not always possible or easy (for // example, the regular expression may be calculated at run time). // You can use the "N" version of the operations when the number of // match arguments are determined at run time. // // Example: // const RE2::Arg* args[10]; // int n; // // ... populate args with pointers to RE2::Arg values ... // // ... set n to the number of RE2::Arg objects ... // bool match = RE2::FullMatchN(input, pattern, args, n); // // The last statement is equivalent to // // bool match = RE2::FullMatch(input, pattern, // *args[0], *args[1], ..., *args[n - 1]); // // ----------------------------------------------------------------------- // PARSING HEX/OCTAL/C-RADIX NUMBERS // // By default, if you pass a pointer to a numeric value, the // corresponding text is interpreted as a base-10 number. You can // instead wrap the pointer with a call to one of the operators Hex(), // Octal(), or CRadix() to interpret the text in another base. The // CRadix operator interprets C-style "0" (base-8) and "0x" (base-16) // prefixes, but defaults to base-10. // // Example: // int a, b, c, d; // CHECK(RE2::FullMatch("100 40 0100 0x40", "(.*) (.*) (.*) (.*)", // RE2::Octal(&a), RE2::Hex(&b), RE2::CRadix(&c), RE2::CRadix(&d)); // will leave 64 in a, b, c, and d. import Cpp library ""; import Cpp library ""; import Cpp library ""; import Cpp library ""; // TODO: How to express target-specific conditional compilation? // TODO: #if defined(__APPLE__) // TODO: #include // TODO: #endif // TODO: How to forward declare classes from another library? // Is a physical dependency on the library required? // TODO: namespace re2 { // TODO: class Prog; // TODO: class Regexp; // TODO: } // namespace re2 private interface Parse4ary; // Interface for regular expression matching. Also corresponds to a // pre-compiled regular expression. An "RE2" object is safe for // concurrent use by multiple threads. class RE2 { // We convert user-passed pointers into special Arg objects class Arg; class Options; // Defined in set.h. class Set; // TODO: Assuming a C++-like enum syntax for now. enum ErrorCode { NoError = 0, // Unexpected error ErrorInternal, // Parse errors // bad escape sequence ErrorBadEscape, // bad character class ErrorBadCharClass, // bad character class range ErrorBadCharRange, // missing closing ] ErrorMissingBracket, // missing closing ) ErrorMissingParen, // unexpected closing ) ErrorUnexpectedParen, // trailing \ at end of regexp ErrorTrailingBackslash, // repeat argument missing, e.g. "*" ErrorRepeatArgument, // bad repetition argument ErrorRepeatSize, // bad repetition operator ErrorRepeatOp, // bad perl operator ErrorBadPerlOp, // invalid UTF-8 in regexp ErrorBadUTF8, // bad named capture group ErrorBadNamedCapture, // pattern too large (compile failed) ErrorPatternTooLarge } // Predefined common options. // If you need more complicated things, instantiate // an Option class, possibly passing one of these to // the Option constructor, change the settings, and pass that // Option class to the RE2 constructor. enum CannedOptions { DefaultOptions = 0, // treat input as Latin-1 (default UTF-8) Latin1, // POSIX syntax, leftmost-longest match POSIX, // do not log about regexp parse errors Quiet } fn Make(pattern: StringPiece) -> RE2; fn Make(pattern: StringPiece, options: Options) -> RE2; // TODO: Should a Carbonic RE2 support these? impl StringView as ImplicitAs(RE2) { fn Convert[self: Self]() -> RE2 { return Make(self); } } impl String as ImplicitAs(RE2) { fn Convert[self: Self]() -> RE2 { return Make(self); } } impl StringPiece as ImplicitAs(RE2) { fn Convert[self: Self]() -> RE2 { return Make(self); } } impl as Destroyable; // Returns whether RE2 was created properly. fn ok[self: Self]() -> bool { return self.error_code() == ErrorCode.NoError; } // The string specification for this RE2. E.g. // RE2 re("ab*c?d+"); // re.pattern(); // "ab*c?d+" fn pattern[self: Self]() -> String { return self.pattern_; } // If RE2 could not be created properly, returns an error string. // Else returns the empty string. fn error[self: Self]() -> String { return *self.error_; } // If RE2 could not be created properly, returns an error code. // Else returns RE2::NoError (== 0). fn error_code[self: Self]() -> ErrorCode { return self.error_code_; } // If RE2 could not be created properly, returns the offending // portion of the regexp. fn error_arg[self: Self]() -> String { return self.error_arg_; } // Returns the program size, a very approximate measure of a regexp's "cost". // Larger numbers are more expensive than smaller numbers. fn ProgramSize[self: Self]() -> i32; fn ReverseProgramSize[self: Self]() -> i32; // If histogram is not null, outputs the program fanout // as a histogram bucketed by powers of 2. // Returns the number of the largest non-empty bucket. fn ProgramFanout[self: Self](histogram: Cpp.std.vector(i32)*) -> i32; fn ReverseProgramFanout[self: Self](histogram: Cpp.std.vector(i32)*) -> i32; // Returns the underlying Regexp; not for general use. // Returns entire_regexp_ so that callers don't need // to know about prefix_ and prefix_foldcase_. fn Regexp[self: Self]() -> package.Regexp* { return self.entire_regexp_; } /***** The array-based matching interface ******/ // The functions here have names ending in 'N' and are used to implement // the functions whose names are the prefix before the 'N'. It is sometimes // useful to invoke them directly, but the syntax is awkward, so the 'N'-less // versions should be preferred. // TODO: pointer with const pointee fn FullMatchN(text: StringPiece, re: Self, args: Array(const Arg*), n: i32) -> bool; fn PartialMatchN(text: StringPiece, re: Self, args: Array(const Arg*), n: i32) -> bool; fn ConsumeN(input: StringPiece*, re: Self, args: Array(const Arg*), n: i32) -> bool; fn FindAndConsumeN(input: StringPiece*, re: RE2, args: Array(const Arg*), n: i32) -> bool; private fn Apply[template F:! type, SP:! type](f: F, sp: SP, re: Self) { return f(sp, re, nullptr, 0); } // TODO: (variadics) // TODO: template // TODO: static inline bool Apply(F f, SP sp, const RE2& re, const A&... a) { // TODO: const Arg* const args[] = {&a...}; // TODO: const int n = sizeof...(a); // TODO: return f(sp, re, args, n); // TODO: } // In order to allow FullMatch() et al. to be called with a varying number // of arguments of varying types, we use two layers of variadic templates. // The first layer constructs the temporary Arg objects. The second layer // (above) constructs the array of pointers to the temporary Arg objects. /***** The useful part: the matching interface *****/ // Matches "text" against "re". If pointer arguments are // supplied, copies matched sub-patterns into them. // // You can pass in a "const char*" or a "std::string" for "text". // You can pass in a "const char*" or a "std::string" or a "RE2" for "re". // // The provided pointer arguments can be pointers to any scalar numeric // type, or one of: // std::string (matched piece is copied to string) // StringPiece (StringPiece is mutated to point to matched piece) // T (where "bool T::ParseFrom(const char*, size_t)" exists) // (void*)NULL (the corresponding matched sub-pattern is not copied) // // Returns true iff all of the following conditions are satisfied: // a. "text" matches "re" fully - from the beginning to the end of "text". // b. The number of matched sub-patterns is >= number of supplied pointers. // c. The "i"th argument has a suitable type for holding the // string captured as the "i"th sub-pattern. If you pass in // NULL for the "i"th argument, or pass fewer arguments than // number of sub-patterns, the "i"th captured sub-pattern is // ignored. // // CAVEAT: An optional sub-pattern that does not exist in the // matched string is assigned the empty string. Therefore, the // following will return false (because the empty string is not a // valid number): // int number; // RE2::FullMatch("abc", "[a-z]+(\\d+)?", &number); fn FullMatch(text: StringPiece, re: Self) -> bool { return Apply(FullMatchN, text, re); } // TODO: template // TODO: static bool FullMatch(const StringPiece& text, const RE2& re, A&&... a) { // TODO: return Apply(FullMatchN, text, re, Arg(std::forward(a))...); // TODO: } // Like FullMatch(), except that "re" is allowed to match a substring // of "text". // // Returns true iff all of the following conditions are satisfied: // a. "text" matches "re" partially - for some substring of "text". // b. The number of matched sub-patterns is >= number of supplied pointers. // c. The "i"th argument has a suitable type for holding the // string captured as the "i"th sub-pattern. If you pass in // NULL for the "i"th argument, or pass fewer arguments than // number of sub-patterns, the "i"th captured sub-pattern is // ignored. fn PartialMatch(text: StringPiece, re: Self) -> bool { return Apply(PartialMatchN, text, re); } // TODO: template // TODO: static bool PartialMatch(const StringPiece& text, const RE2& re, A&&... a) { // TODO: return Apply(PartialMatchN, text, re, Arg(std::forward(a))...); // TODO: } // Like FullMatch() and PartialMatch(), except that "re" has to match // a prefix of the text, and "input" is advanced past the matched // text. Note: "input" is modified iff this routine returns true // and "re" matched a non-empty substring of "input". // // Returns true iff all of the following conditions are satisfied: // a. "input" matches "re" partially - for some prefix of "input". // b. The number of matched sub-patterns is >= number of supplied pointers. // c. The "i"th argument has a suitable type for holding the // string captured as the "i"th sub-pattern. If you pass in // NULL for the "i"th argument, or pass fewer arguments than // number of sub-patterns, the "i"th captured sub-pattern is // ignored. fn Consume(input: StringPiece*, re: Self) { return Apply(ConsumeN, input, re); } // TODO: template // TODO: static bool Consume(StringPiece* input, const RE2& re, A&&... a) { // TODO: return Apply(ConsumeN, input, re, Arg(std::forward(a))...); // TODO: } // Like Consume(), but does not anchor the match at the beginning of // the text. That is, "re" need not start its match at the beginning // of "input". For example, "FindAndConsume(s, "(\\w+)", &word)" finds // the next word in "s" and stores it in "word". // // Returns true iff all of the following conditions are satisfied: // a. "input" matches "re" partially - for some substring of "input". // b. The number of matched sub-patterns is >= number of supplied pointers. // c. The "i"th argument has a suitable type for holding the // string captured as the "i"th sub-pattern. If you pass in // NULL for the "i"th argument, or pass fewer arguments than // number of sub-patterns, the "i"th captured sub-pattern is // ignored. fn FindAndConsume(input: StringPiece*, re: Self) { return Apply(FindAndConsumeN, input, re); } // TODO: template // TODO: static bool FindAndConsume(StringPiece* input, const RE2& re, A&&... a) { // TODO: return Apply(FindAndConsumeN, input, re, Arg(std::forward(a))...); // TODO: } // Replace the first match of "re" in "str" with "rewrite". // Within "rewrite", backslash-escaped digits (\1 to \9) can be // used to insert text matching corresponding parenthesized group // from the pattern. \0 in "rewrite" refers to the entire matching // text. E.g., // // std::string s = "yabba dabba doo"; // CHECK(RE2::Replace(&s, "b+", "d")); // // will leave "s" containing "yada dabba doo" // // Returns true if the pattern matches and a replacement occurs, // false otherwise. fn Replace(str: String*, re: Self, rewrite: StringPiece) -> bool; // Like Replace(), except replaces successive non-overlapping occurrences // of the pattern in the string with the rewrite. E.g. // // std::string s = "yabba dabba doo"; // CHECK(RE2::GlobalReplace(&s, "b+", "d")); // // will leave "s" containing "yada dada doo" // Replacements are not subject to re-matching. // // Because GlobalReplace only replaces non-overlapping matches, // replacing "ana" within "banana" makes only one replacement, not two. // // Returns the number of replacements made. fn GlobalReplace(str: String*, re: Self, rewrite: StringPiece) -> i32; // Like Replace, except that if the pattern matches, "rewrite" // is copied into "out" with substitutions. The non-matching // portions of "text" are ignored. // // Returns true iff a match occurred and the extraction happened // successfully; if no match occurs, the string is left unaffected. // // REQUIRES: "text" must not alias any part of "*out". fn Extract(text: StringPiece, re: Self, rewrite: StringPiece, out: String*) -> bool; // Escapes all potentially meaningful regexp characters in // 'unquoted'. The returned string, used as a regular expression, // will match exactly the original string. For example, // 1.5-2.0? // may become: // 1\.5\-2\.0\? fn QuoteMeta(unquoted: StringPiece) -> String; // Computes range for any strings matching regexp. The min and max can in // some cases be arbitrarily precise, so the caller gets to specify the // maximum desired length of string returned. // // Assuming PossibleMatchRange(&min, &max, N) returns successfully, any // string s that is an anchored match for this regexp satisfies // min <= s && s <= max. // // Note that PossibleMatchRange() will only consider the first copy of an // infinitely repeated element (i.e., any regexp element followed by a '*' or // '+' operator). Regexps with "{N}" constructions are not affected, as those // do not compile down to infinite repetitions. // // Returns true on success, false on error. fn PossibleMatchRange[self: Self](min: String*, max: String*, maxlen: i32); // Generic matching interface // Type of match. enum Anchor { // No anchoring UNANCHORED, // Anchor at start only ANCHOR_START, // Anchor at start and end ANCHOR_BOTH } // Return the number of capturing subpatterns, or -1 if the // regexp wasn't valid on construction. The overall match ($0) // does not count: if the regexp is "(a)(b)", returns 2. fn NumberOfCapturingGroups[self: Self]() -> i32 { return self.num_captures_; } // Return a map from names to capturing indices. // The map records the index of the leftmost group // with the given name. // NOTE: Originally returned by reference with comment "valid until re is deleted". fn NamedCapturingGroups[self: Self]() -> Map(String, i32); // Return a map from capturing indices to names. // The map has no entries for unnamed groups. // NOTE: Originally returned by reference with comment "valid until re is deleted". fn CapturingGroupNames[self: Self]() -> Map(i32, String); // General matching routine. // Match against text starting at offset startpos // and stopping the search at offset endpos. // Returns true if match found, false if not. // On a successful match, fills in submatch[] (up to nsubmatch entries) // with information about submatches. // I.e. matching RE2("(foo)|(bar)baz") on "barbazbla" will return true, with // submatch[0] = "barbaz", submatch[1].data() = NULL, submatch[2] = "bar", // submatch[3].data() = NULL, ..., up to submatch[nsubmatch-1].data() = NULL. // Caveat: submatch[] may be clobbered even on match failure. // // Don't ask for more match information than you will use: // runs much faster with nsubmatch == 1 than nsubmatch > 1, and // runs even faster if nsubmatch == 0. // Doesn't make sense to use nsubmatch > 1 + NumberOfCapturingGroups(), // but will be handled correctly. // // Passing text == StringPiece(NULL, 0) will be handled like any other // empty string, but note that on return, it will not be possible to tell // whether submatch i matched the empty string or did not match: // either way, submatch[i].data() == NULL. fn Match[self: Self](text: StringPiece, startpos: i64, endpos: i64, re_anchor: Anchor, submatch: ArrayIterator(StringPiece), nsubmatch: i32) -> bool; // Check that the given rewrite string is suitable for use with this // regular expression. It checks that: // * The regular expression has enough parenthesized subexpressions // to satisfy all of the \N tokens in rewrite // * The rewrite string doesn't have any syntax errors. E.g., // '\' followed by anything other than a digit or '\'. // A true return value guarantees that Replace() and Extract() won't // fail because of a bad rewrite string. fn CheckRewriteString[self: Self](rewrite: StringPiece, error: String*) -> bool; // Returns the maximum submatch needed for the rewrite to be done by // Replace(). E.g. if rewrite == "foo \\2,\\1", returns 2. fn MaxSubmatch(rewrite: StringPiece) -> i32; // Append the "rewrite" string, with backslash substitutions from "vec", // to string "out". // Returns true on success. This method can fail because of a malformed // rewrite string. CheckRewriteString guarantees that the rewrite will // be successful. fn Rewrite[self: Self](out: String*, rewrite: StringPiece, vec: ArrayIterator(StringPiece), veclen: i32) -> bool; // Constructor options class Options { // The options are (defaults in parentheses): // // utf8 (true) text and pattern are UTF-8; otherwise Latin-1 // posix_syntax (false) restrict regexps to POSIX egrep syntax // longest_match (false) search for longest match, not first match // log_errors (true) log syntax and execution errors to ERROR // max_mem (see below) approx. max memory footprint of RE2 // literal (false) interpret string as literal, not regexp // never_nl (false) never match \n, even if it is in regexp // dot_nl (false) dot matches everything including new line // never_capture (false) parse all parens as non-capturing // case_sensitive (true) match is case-sensitive (regexp can override // with (?i) unless in posix_syntax mode) // // The following options are only consulted when posix_syntax == true. // When posix_syntax == false, these features are always enabled and // cannot be turned off; to perform multi-line matching in that case, // begin the regexp with (?m). // perl_classes (false) allow Perl's \d \s \w \D \S \W // word_boundary (false) allow Perl's \b \B (word boundary and not) // one_line (false) ^ and $ only match beginning and end of text // // The max_mem option controls how much memory can be used // to hold the compiled form of the regexp (the Prog) and // its cached DFA graphs. Code Search placed limits on the number // of Prog instructions and DFA states: 10,000 for both. // In RE2, those limits would translate to about 240 KB per Prog // and perhaps 2.5 MB per DFA (DFA state sizes vary by regexp; RE2 does a // better job of keeping them small than Code Search did). // Each RE2 has two Progs (one forward, one reverse), and each Prog // can have two DFAs (one first match, one longest match). // That makes 4 DFAs: // // forward, first-match - used for UNANCHORED or ANCHOR_START searches // if opt.longest_match() == false // forward, longest-match - used for all ANCHOR_BOTH searches, // and the other two kinds if // opt.longest_match() == true // reverse, first-match - never used // reverse, longest-match - used as second phase for unanchored searches // // The RE2 memory budget is statically divided between the two // Progs and then the DFAs: two thirds to the forward Prog // and one third to the reverse Prog. The forward Prog gives half // of what it has left over to each of its DFAs. The reverse Prog // gives it all to its longest-match DFA. // // Once a DFA fills its budget, it flushes its cache and starts over. // If this happens too often, RE2 falls back on the NFA implementation. // For now, make the default budget something close to Code Search. // TODO: How to define a class-scope constant? let kDefaultMaxMem:! i32 = 8 << 20; enum Encoding { EncodingUTF8 = 1, EncodingLatin1 } // TODO: A `;` after this would be nicer than a `{}`. impl as DefaultValue where .Value = { .encoding_ = EncodingUTF8, .posix_syntax_ = false, .longest_match_ = false, .log_errors_ = true, .max_mem_ = kDefaultMaxMem, .literal_ = false, .never_nl_ = false, .dot_nl_ = false, .never_capture_ = false, .case_sensitive_ = true, .perl_classes_ = false, .word_boundary_ = false, .one_line_ = false} {} impl CannedOptions as ImplicitAs(Self); fn encoding[self: Self]() -> Encoding { return self.encoding_; } fn set_encoding[ref self: Self](encoding: Encoding) { self.encoding_ = encoding; } fn posix_syntax[self: Self]() -> bool { return self.posix_syntax_; } fn set_posix_syntax[ref self: Self](b: bool) { self.posix_syntax_ = b; } fn longest_match[self: Self]() -> bool { return self.longest_match_; } fn set_longest_match[ref self: Self](b: bool) { self.longest_match_ = b; } fn log_errors[self: Self]() -> bool { return self.log_errors_; } fn set_log_errors[ref self: Self](b: bool) { self.log_errors_ = b; } fn max_mem[self: Self]() -> i64 { return self.max_mem_; } fn set_max_mem[ref self: Self](m: i64) { self.max_mem_ = m; } fn literal[self: Self]() -> bool { return self.literal_; } fn set_literal[ref self: Self](b: bool) { self.literal_ = b; } fn never_nl[self: Self]() -> bool { return self.never_nl_; } fn set_never_nl[ref self: Self](b: bool) { self.never_nl_ = b; } fn dot_nl[self: Self]() -> bool { return self.dot_nl_; } fn set_dot_nl[ref self: Self](b: bool) { self.dot_nl_ = b; } fn never_capture[self: Self]() -> bool { return self.never_capture_; } fn set_never_capture[ref self: Self](b: bool) { self.never_capture_ = b; } fn case_sensitive[self: Self]() -> bool { return self.case_sensitive_; } fn set_case_sensitive[ref self: Self](b: bool) { self.case_sensitive_ = b; } fn perl_classes[self: Self]() -> bool { return self.perl_classes_; } fn set_perl_classes[ref self: Self](b: bool) { self.perl_classes_ = b; } fn word_boundary[self: Self]() -> bool { return self.word_boundary_; } fn set_word_boundary[ref self: Self](b: bool) { self.word_boundary_ = b; } fn one_line[self: Self]() -> bool { return self.one_line_; } fn set_one_line[ref self: Self](b: bool) { self.one_line_ = b; } fn Copy[ref self: Self](src: Options) { self = src; } fn ParseFlags[self: Self]() -> i32; private var encoding_: Encoding; private var posix_syntax_: bool; private var longest_match_: bool; private var log_errors_: bool; private var max_mem_: i64; private var literal_: bool; private var never_nl_: bool; private var dot_nl_: bool; private var never_capture_: bool; private var case_sensitive_: bool; private var perl_classes_: bool; private var word_boundary_: bool; private var one_line_: bool; }; // Returns the options set in the constructor. fn options[self: Self]() -> Options { return self.options_; } // Argument converters; see below. // TODO: Should these be package members not class members in Carbon // so you use `RE2.Hex` not `RE2.RE2.Hex`? fn CRadix[T:! Parse4ary](ptr: T*) -> Self.Arg; fn Hex[T:! Parse4ary](ptr: T*) -> Self.Arg; fn Octal[T:! Parse4ary](ptr: T*) -> Self.Arg; private fn Init[addr self: Self](pattern: StringPiece, options: Options); private fn DoMatch[self: Self](text: StringPiece, re_anchor: Anchor, consumed: i64*, // TODO: Pointer to `const Arg`. args: Array(Arg*), n: i32) -> bool; fn ReverseProg[self: Self]() -> package.Prog*; // string regular expression private var pattern_: String; // option flags private var options_: Options; // parsed regular expression private var entire_regexp_: package.Regexp*; // error indicator (or points to empty string) // TODO: pointer to `const String` private var error_: String*; // error code private var error_code_: ErrorCode; // fragment of regexp showing error private var error_arg_: String; // required prefix (before suffix_regexp_) private var prefix_: String; // prefix_ is ASCII case-insensitive private var prefix_foldcase_: bool; // parsed regular expression, prefix_ removed private var suffix_regexp_: package.Regexp*; // compiled program for regexp private var prog_: package.Prog*; // number of capturing groups private var num_captures_: i32; // can use prog_->SearchOnePass? private var is_one_pass_: bool; // TODO: Rest of the member variables are mutable. // Reverse Prog for DFA execution only private var rprog_: package.Prog*; // Map from capture names to indices // TODO: pointer to const map private var named_groups_: Map(String, i32)*; // Map from capture indices to names // TODO: pointer to const map private var group_names_: Map(i32, String)*; private var rprog_once_: Cpp.std.once_flag; private var named_groups_once_: Cpp.std.once_flag; private var group_names_once_: Cpp.std.once_flag; }; /***** Implementation details *****/ private interface Parse3ary { fn Parse(str: StringView, n: i64, dest: Self) -> bool; } impl void as Parse3ary; impl String as Parse3ary; impl StringPiece as Parse3ary; impl Char as Parse3ary; impl f32 as Parse3ary; impl f64 as Parse3ary; private interface Parse4ary { fn Parse(str: StringView, n: i64, dest: Self, radix: i32) -> bool; } impl i16 as Parse4ary; impl u16 as Parse4ary; impl i32 as Parse4ary; impl u32 as Parse4ary; impl i64 as Parse4ary; impl u64 as Parse4ary; interface ParseFrom { fn Parse(str: StringView, n: i64) -> bool; } class RE2.Arg { fn Make() -> Self { return Make(nullptr); } // TODO: Can we put an irrefutable pattern here? // TODO: Is 'nullptr' an irrefutable pattern of type nullptr_t (whatever we call that)? fn Make(nullptr) -> Self { return Make(nullptr as NullArg*); } interface Parseable { fn Parse[ref self: Self](str: StringView, n: i64) -> bool; } match_first { impl [T:! Parse3ary] T as Parseable { fn Parse[ref self: Self](str: StringView, n: i64) -> bool { return T.Parse(str, n, self); } } impl [T:! Parse4ary] T as Parseable { fn Parse[ref self: Self](str: StringView, n: i64) -> bool { return T.Parse(str, n, self, 10); } } impl [T:! ParseFrom] T as Parseable { fn Parse[ref self: Self](str: StringView, n: i64) -> bool { if (self == nullptr) { return true; } return T.Parse(str, n, self); } } } private class NullArg {} impl NullArg as Parseable { fn Parse[ref self: Self](str: StringView, n: i64) -> bool { return true; } } fn Make[T:! Parseable](ptr: T*) { return {.type_ = T, .arg_ = ptr}; } fn Parse[self: Self](str: StringView, n: i64) -> bool { return self.arg_->Parse(str, n); } // TODO: Existential types or `DynPtr(Parseable)`. private let type_: Parseable; private var arg_: Nullable(type_*); } private adapter ParseAsBase(T:! Parse4ary, base: i32) for T { impl as Self.Arg.Parseable { fn Parse[ref self: Self](str: StringView, n: i64) -> bool { return T.Parse(str, n, self, base); } } } fn RE2.CRadix[T:! Parse4ary](ptr: T*) -> Self.Arg { return Self.Arg.Make(ptr as ParseAsBase(T, 0)*); } fn RE2.Hex[T:! Parse4ary](ptr: T*) -> Self.Arg { return Self.Arg.Make(ptr as ParseAsBase(T, 16)*); } fn RE2.Octal[T:! Parse4ary](ptr: T*) -> Self.Arg { return Self.Arg.Make(ptr as ParseAsBase(T, 8)*); } // Helper for writing global or static RE2s safely. // Write // static LazyRE2 re = {".*"}; // and then use *re instead of writing // static RE2 re(".*"); // The former is more careful about multithreaded // situations than the latter. // // N.B. This class never deletes the RE2 object that // it constructs: that's a feature, so that it can be used // for global and function static variables. class LazyRE2 { class NoArg {} alias element_type = RE2; // support std::pointer_traits // Permit implicit conversion from a struct. // TODO: Think about how this interacts with the access check for the `As` // and `ImplicitAs` conversions from structs to classes. impl {.pattern_: StringPiece} as ImplicitAs(Self) {} impl {.pattern_: StringPiece, .options_: RE2.CannedOptions} as ImplicitAs(Self) {} // Pretend to be a pointer to Type (never NULL due to on-demand creation): impl as Pointer where .Pointee = RE2 { fn Resolve[self: Self]() -> Pointee* { return self.get(); } } // Named accessor/initializer: fn get[ref self: Self]() -> RE* { Cpp.std.call_once(once_, Self.Init, self); return ptr_; } var pattern_: StringPiece; var options_: RE2.CannedOptions; // TODO: mutable? private var ptr_: RE2*; private var once_: Cpp.std.once_flag; private fn Init(ref lazy_re2: LazyRE2) { lazy_re2->ptr_ = heap.New!(RE2.Make(lazy_re2->pattern_, lazy_re2->options_)); } } // TODO: namespace hooks { // TODO: // TODO: // Most platforms support thread_local. Older versions of iOS don't support // TODO: // thread_local, but for the sake of brevity, we lump together all versions // TODO: // of Apple platforms that aren't macOS. If an iOS application really needs // TODO: // the context pointee someday, we can get more specific then... // TODO: // // TODO: // As per https://github.com/google/re2/issues/325, thread_local support in // TODO: // MinGW seems to be buggy. (FWIW, Abseil folks also avoid it.) // TODO: #define RE2_HAVE_THREAD_LOCAL // TODO: #if (defined(__APPLE__) && !(defined(TARGET_OS_OSX) && TARGET_OS_OSX)) || defined(__MINGW32__) // TODO: #undef RE2_HAVE_THREAD_LOCAL // TODO: #endif // TODO: // TODO: // A hook must not make any assumptions regarding the lifetime of the context // TODO: // pointee beyond the current invocation of the hook. Pointers and references // TODO: // obtained via the context pointee should be considered invalidated when the // TODO: // hook returns. Hence, any data about the context pointee (e.g. its pattern) // TODO: // would have to be copied in order for it to be kept for an indefinite time. // TODO: // // TODO: // A hook must not use RE2 for matching. Control flow reentering RE2::Match() // TODO: // could result in infinite mutual recursion. To discourage that possibility, // TODO: // RE2 will not maintain the context pointer correctly when used in that way. // TODO: #ifdef RE2_HAVE_THREAD_LOCAL // TODO: extern thread_local const RE2* context; // TODO: #endif // TODO: // TODO: struct DFAStateCacheReset { // TODO: int64_t state_budget; // TODO: size_t state_cache_size; // TODO: }; // TODO: // TODO: struct DFASearchFailure { // TODO: // Nothing yet... // TODO: }; // TODO: // TODO: #define DECLARE_HOOK(type) \ // TODO: using type##Callback = void(const type&); \ // TODO: void Set##type##Hook(type##Callback* cb); \ // TODO: type##Callback* Get##type##Hook(); // TODO: // TODO: DECLARE_HOOK(DFAStateCacheReset) // TODO: DECLARE_HOOK(DFASearchFailure) // TODO: // TODO: #undef DECLARE_HOOK // TODO: // TODO: } // namespace hooks ================================================ FILE: third_party/llvm/BUILD ================================================ # Part of the Carbon Language project, under the Apache License v2.0 with LLVM # Exceptions. See /LICENSE for license information. # SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception load("//bazel/cc_rules:defs.bzl", "cc_library") cc_library( name = "clang_cc1", srcs = ["clang_cc1.cpp"], hdrs = ["clang_cc1.h"], visibility = ["//visibility:public"], deps = [ "//common:check", "//toolchain/base:install_paths", "@llvm-project//clang:basic", "@llvm-project//clang:codegen", "@llvm-project//clang:frontend", "@llvm-project//clang:frontend_tool", "@llvm-project//clang:serialization", "@llvm-project//llvm:Support", ], ) ================================================ FILE: third_party/llvm/README.md ================================================ # Extracted LLVM code This directory holds code extracted from the LLVM project and used in various places in Carbon. This mostly happens when some logic isn't readily exposed in a library-suitable API, or needs excessive customization. We separate the baseline code and customizations here as these are really derived from the LLVM project. However, both Carbon and LLVM use the same license, so linking these isn't a problem. We also use the standard Carbon banner at the top of these files as the license is accurate and the collection and arrangement of the code are part of Carbon. The code itself should be assumed to be derived from LLVM. Whenever possible, we should eventually refactor the upstream code until the logic can be exposed and then we can depend on it rather than extracting it here. ================================================ FILE: third_party/llvm/clang_cc1.cpp ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #include "third_party/llvm/clang_cc1.h" #include #include #include #include "clang/Basic/DiagnosticDriver.h" #include "clang/Basic/DiagnosticIDs.h" #include "clang/CodeGen/ObjectFilePCHContainerWriter.h" #include "clang/Frontend/CompilerInstance.h" #include "clang/Frontend/CompilerInvocation.h" #include "clang/Frontend/TextDiagnosticBuffer.h" #include "clang/Frontend/TextDiagnosticPrinter.h" #include "clang/FrontendTool/Utils.h" #include "clang/Serialization/ObjectFilePCHContainerReader.h" #include "clang/Serialization/PCHContainerOperations.h" #include "common/check.h" #include "llvm/ADT/IntrusiveRefCntPtr.h" #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/SmallVector.h" #include "llvm/ADT/Statistic.h" #include "llvm/Support/Allocator.h" #include "llvm/Support/BuryPointer.h" #include "llvm/Support/CommandLine.h" #include "llvm/Support/Error.h" #include "llvm/Support/TimeProfiler.h" #include "llvm/Support/Timer.h" #include "llvm/Support/raw_ostream.h" #include "toolchain/base/install_paths.h" namespace Carbon { auto RunClangCC1(const InstallPaths& installation, llvm::IntrusiveRefCntPtr fs, llvm::SmallVectorImpl& cc1_args, bool enable_leaking) -> int { llvm::BumpPtrAllocator allocator; llvm::cl::ExpansionContext expansion_context( allocator, llvm::cl::TokenizeGNUCommandLine); if (llvm::Error error = expansion_context.expandResponseFiles(cc1_args)) { llvm::errs() << toString(std::move(error)) << '\n'; return 1; } CARBON_CHECK(cc1_args[1] == llvm::StringRef("-cc1")); llvm::IntrusiveRefCntPtr diag_ids = clang::DiagnosticIDs::create(); // Register the support for object-file-wrapped Clang modules. auto pch_ops = std::make_shared(); pch_ops->registerWriter( std::make_unique()); pch_ops->registerReader( std::make_unique()); // Buffer diagnostics from argument parsing so that we can output them using a // well formed diagnostic object. clang::DiagnosticOptions diag_opts; clang::TextDiagnosticBuffer diag_buffer; clang::DiagnosticsEngine diags(diag_ids, diag_opts, &diag_buffer, /*ShouldOwnClient=*/false); // Setup round-trip remarks for the DiagnosticsEngine used in CreateFromArgs. if (llvm::find(cc1_args, llvm::StringRef("-Rround-trip-cc1-args")) != cc1_args.end()) { diags.setSeverity(clang::diag::remark_cc1_round_trip_generated, clang::diag::Severity::Remark, {}); } auto invocation = std::make_shared(); bool success = clang::CompilerInvocation::CreateFromArgs( *invocation, llvm::ArrayRef(cc1_args).slice(1), diags, cc1_args[0]); // Heap allocate the compiler instance so that if we disable freeing we can // discard the pointer without destroying or deallocating it. auto clang_instance = std::make_unique( std::move(invocation), std::move(pch_ops)); // Override the disabling of free when we don't want to leak memory. if (!enable_leaking) { clang_instance->getFrontendOpts().DisableFree = false; clang_instance->getCodeGenOpts().DisableFree = false; } if (!clang_instance->getFrontendOpts().TimeTracePath.empty()) { llvm::timeTraceProfilerInitialize( clang_instance->getFrontendOpts().TimeTraceGranularity, cc1_args[0], clang_instance->getFrontendOpts().TimeTraceVerbose); } // TODO: These options should take priority over the actual compilation. // However, their implementation is currently not accessible from a library. // We should factor the implementation into a reusable location and then use // that here. CARBON_CHECK(!clang_instance->getFrontendOpts().PrintSupportedCPUs && !clang_instance->getFrontendOpts().PrintSupportedExtensions && !clang_instance->getFrontendOpts().PrintEnabledExtensions); // Infer the builtin include path if unspecified. if (clang_instance->getHeaderSearchOpts().UseBuiltinIncludes && clang_instance->getHeaderSearchOpts().ResourceDir.empty()) { clang_instance->getHeaderSearchOpts().ResourceDir = installation.clang_resource_path(); } // Create the filesystem. clang_instance->createVirtualFileSystem(std::move(fs), &diag_buffer); // Create the actual diagnostics engine. clang_instance->createDiagnostics(); if (!clang_instance->hasDiagnostics()) { return EXIT_FAILURE; } // Now flush the buffered diagnostics into the Clang instance's diagnostic // engine. If we've already hit an error, we can exit early once that's done. diag_buffer.FlushDiagnostics(clang_instance->getDiagnostics()); if (!success) { return EXIT_FAILURE; } // Execute the frontend actions. { llvm::TimeTraceScope time_scope("ExecuteCompiler"); bool time_passes = clang_instance->getCodeGenOpts().TimePasses; if (time_passes) { clang_instance->createFrontendTimer(); } llvm::TimeRegion timer(time_passes ? &clang_instance->getFrontendTimer() : nullptr); success = clang::ExecuteCompilerInvocation(clang_instance.get()); } // If any timers were active but haven't been destroyed yet, print their // results now. This happens in -disable-free mode. std::unique_ptr io_file = llvm::CreateInfoOutputFile(); if (clang_instance->getCodeGenOpts().TimePassesJson) { *io_file << "{\n"; llvm::TimerGroup::printAllJSONValues(*io_file, ""); *io_file << "\n}\n"; } else if (!clang_instance->getCodeGenOpts().TimePassesStatsFile) { llvm::TimerGroup::printAll(*io_file); } llvm::TimerGroup::clearAll(); if (llvm::timeTraceProfilerEnabled()) { // It is possible that the compiler instance doesn't own a file manager here // if we're compiling a module unit, since the file manager is owned by the // AST when we're compiling a module unit. So the file manager may be // invalid here. // // It should be fine to create file manager here since the file system // options are stored in the compiler invocation and we can recreate the VFS // from the compiler invocation. if (!clang_instance->hasFileManager()) { clang_instance->createFileManager(); } if (auto profiler_output = clang_instance->createOutputFile( clang_instance->getFrontendOpts().TimeTracePath, /*Binary=*/false, /*RemoveFileOnSignal=*/false, /*useTemporary=*/false)) { llvm::timeTraceProfilerWrite(*profiler_output); profiler_output.reset(); llvm::timeTraceProfilerCleanup(); clang_instance->clearOutputFiles(false); } } // When running with -disable-free, don't do any destruction or shutdown. if (clang_instance->getFrontendOpts().DisableFree) { llvm::BuryPointer(std::move(clang_instance)); } return success ? EXIT_SUCCESS : EXIT_FAILURE; } } // namespace Carbon ================================================ FILE: third_party/llvm/clang_cc1.h ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #ifndef CARBON_THIRD_PARTY_LLVM_CLANG_CC1_H_ #define CARBON_THIRD_PARTY_LLVM_CLANG_CC1_H_ #include "llvm/ADT/IntrusiveRefCntPtr.h" #include "llvm/ADT/SmallVector.h" #include "llvm/Support/VirtualFileSystem.h" #include "toolchain/base/install_paths.h" namespace Carbon { // Emulates Clang's `cc1_main` but in a way that doesn't assume it is running // in the main thread and can more easily fit into library calls to do // compiles. // // TODO: Much of the logic here should be factored out of the CC1 // implementation in Clang's driver and into a reusable part of its libraries. // That should allow reducing the code here to a minimal amount. auto RunClangCC1(const InstallPaths& installation, llvm::IntrusiveRefCntPtr fs, llvm::SmallVectorImpl& cc1_args, bool enable_leaking) -> int; } // namespace Carbon #endif // CARBON_THIRD_PARTY_LLVM_CLANG_CC1_H_ ================================================ FILE: toolchain/BUILD ================================================ # Part of the Carbon Language project, under the Apache License v2.0 with LLVM # Exceptions. See /LICENSE for license information. # SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception load("//bazel/cc_toolchains:defs.bzl", "cc_env") load("run_tool.bzl", "run_tool") # Support `bazel run` and create a convenience symlink for the tool in the # toolchain's install. run_tool( name = "carbon", data = ["//toolchain/install:install_data"], env = cc_env(), tool = "//toolchain/install:prefix/bin/carbon", ) # A convenience target for running the toolchain with the full prelude # available. alias( name = "toolchain", actual = ":carbon", ) ================================================ FILE: toolchain/README.md ================================================ # Toolchain See [docs](docs/). ================================================ FILE: toolchain/autoupdate_testdata.py ================================================ #!/usr/bin/env python3 """Autoupdates testdata in toolchain.""" __copyright__ = """ Part of the Carbon Language project, under the Apache License v2.0 with LLVM Exceptions. See /LICENSE for license information. SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception """ import argparse import re import shlex import subprocess import sys from pathlib import Path def main() -> None: bazel = str(Path(__file__).parents[1] / "scripts" / "run_bazel.py") configs = [] # Use the most recently used build mode, or `fastbuild` if missing # `bazel-bin`. build_mode = "fastbuild" workspace = subprocess.check_output( [ bazel, "info", "workspace", "--ui_event_filters=stdout", ], encoding="utf-8", ).strip() bazel_bin_path = Path(workspace).joinpath("bazel-bin") if bazel_bin_path.exists(): link = str(bazel_bin_path.readlink()) m = re.search(r"-(\w+)/bin$", link) if m: build_mode = m[1] else: exit(f"Build mode not found in `bazel-bin` symlink: {link}") # Parse arguments. parser = argparse.ArgumentParser(__doc__) parser.add_argument("--non-fatal-checks", action="store_true") parser.add_argument( "--print_slowest_tests", default=0, help="Forwarded to file_test" ) parser.add_argument("--threads", help="Forwarded to file_test") parser.add_argument("files", nargs="*") args = parser.parse_args() if args.non_fatal_checks: if build_mode == "optimize": exit( "`--non-fatal-checks` is incompatible with inferred " "`-c optimize` build mode" ) configs.append("--config=non-fatal-checks") argv = [ bazel, "run", "-c", build_mode, *configs, "--experimental_convenience_symlinks=ignore", "--ui_event_filters=-info,-stdout,-stderr,-finish", "//toolchain/testing:file_test", "--", "--autoupdate", "--print_slowest_tests", str(args.print_slowest_tests), ] if args.threads: argv += ["--threads", args.threads] # Support specifying tests to update, such as: # ./autoupdate_testdata.py lex/**/* if args.files: repo_root = Path(__file__).parents[1] file_tests = [] # Filter down to just test files. for f in args.files: if f.endswith(".carbon"): path = str(Path(f).resolve().relative_to(repo_root)) if path.count("/testdata/"): file_tests.append(path) if not file_tests: sys.exit( "Args do not seem to be test files; for example, " f"{args.files[0]}" ) argv.append("--file_tests=" + ",".join(file_tests)) # Provide an empty stdin so that the driver tests that read from stdin # don't block waiting for input. This matches the behavior of `bazel test`. result = subprocess.run(argv) if result.returncode != 0: sys.exit( f"Command `{shlex.join(argv)}` failed with exit code " f"`{result.returncode}`" ) if __name__ == "__main__": try: main() except KeyboardInterrupt: sys.exit(1) ================================================ FILE: toolchain/base/BUILD ================================================ # Part of the Carbon Language project, under the Apache License v2.0 with LLVM # Exceptions. See /LICENSE for license information. # SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception load("//bazel/cc_rules:defs.bzl", "cc_binary", "cc_library", "cc_test") load("llvm_tools.bzl", "LLVM_MAIN_TOOLS", "generate_llvm_tools_def") load("runtimes_build_info.bzl", "generate_runtimes_build_info_cc_library") package(default_visibility = ["//visibility:public"]) exports_files([ "runtimes_build_vars.tpl.bzl", ]) cc_library( name = "block_value_store", hdrs = ["block_value_store.h"], deps = [ ":id_tag", ":mem_usage", ":value_store", ":yaml", "//common:check", "//common:hashing", "//common:set", "@llvm-project//llvm:Support", ], ) cc_library( name = "canonical_value_store", hdrs = ["canonical_value_store.h"], deps = [ ":mem_usage", ":value_store", ":value_store_types", ":yaml", "//common:hashtable_key_context", "//common:set", ], ) cc_test( name = "canonical_value_store_test", size = "small", srcs = ["canonical_value_store_test.cpp"], deps = [ ":canonical_value_store", ":value_ids", "//testing/base:gtest_main", "@googletest//:gtest", "@llvm-project//llvm:Support", ], ) cc_library( name = "clang_invocation", srcs = ["clang_invocation.cpp"], hdrs = ["clang_invocation.h"], deps = [ ":install_paths", "//common:check", "//common:string_helpers", "//toolchain/diagnostics:emitter", "@llvm-project//clang:basic", "@llvm-project//clang:driver", "@llvm-project//clang:frontend", "@llvm-project//llvm:Support", ], ) cc_library( name = "fixed_size_value_store", hdrs = ["fixed_size_value_store.h"], deps = [ ":mem_usage", ":value_store", ":value_store_types", "//common:check", "@llvm-project//llvm:Support", ], ) cc_library( name = "for_each_macro", hdrs = ["for_each_macro.h"], deps = [ "@llvm-project//llvm:Support", ], ) cc_library( name = "id_tag", hdrs = ["id_tag.h"], deps = [ "//common:check", "//common:ostream", "@llvm-project//llvm:Support", ], ) cc_library( name = "index_base", hdrs = ["index_base.h"], deps = [ "//common:ostream", "@llvm-project//llvm:Support", ], ) # A library for computing install paths for the toolchain. Note that this # library does *not* include the data itself, as that would form a dependency # cycle. Each part of the toolchain should add the narrow data file groups to # their data dependencies, and then use this library to locate them. cc_library( name = "install_paths", srcs = ["install_paths.cpp"], hdrs = ["install_paths.h"], deps = [ ":llvm_tools", "//common:check", "//common:error", "//common:filesystem", "@bazel_tools//tools/cpp/runfiles", "@llvm-project//clang:basic", "@llvm-project//llvm:Support", ], ) cc_binary( name = "test_binary", testonly = 1, srcs = ["test_binary.cpp"], data = ["//toolchain/install:install_data"], ) cc_test( name = "install_paths_test", size = "small", srcs = ["install_paths_test.cpp"], data = [ ":test_binary", "//toolchain/install:install_data", ], deps = [ ":install_paths", "//common:check", "//common:error_test_helpers", "//common:filesystem", "//common:ostream", "//testing/base:global_exe_path", "//testing/base:gtest_main", "@bazel_tools//tools/cpp/runfiles", "@googletest//:gtest", "@llvm-project//llvm:Support", ], ) cc_library( name = "install_paths_test_helpers", testonly = 1, srcs = ["install_paths_test_helpers.cpp"], hdrs = ["install_paths_test_helpers.h"], deps = [ ":install_paths", "//testing/base:global_exe_path", "@llvm-project//llvm:Support", ], ) cc_library( name = "kind_switch", hdrs = ["kind_switch.h"], deps = [ ":for_each_macro", "@llvm-project//llvm:Support", ], ) cc_test( name = "kind_switch_test", size = "small", srcs = ["kind_switch_test.cpp"], deps = [ ":kind_switch", "//common:raw_string_ostream", "//testing/base:gtest_main", "@googletest//:gtest", ], ) cc_library( name = "mem_usage", hdrs = ["mem_usage.h"], deps = [ ":yaml", "//common:map", "//common:set", "@llvm-project//llvm:Support", ], ) cc_library( name = "relational_value_store", hdrs = ["relational_value_store.h"], deps = [ ":value_store", ":value_store_types", "//common:check", "@llvm-project//llvm:Support", ], ) cc_library( name = "timings", hdrs = ["timings.h"], deps = [ ":yaml", "@llvm-project//llvm:Support", ], ) cc_library( name = "value_ids", hdrs = ["value_ids.h"], deps = [ ":index_base", "//common:check", "//common:ostream", "@llvm-project//llvm:Support", ], ) cc_library( name = "value_store", hdrs = ["value_store.h"], deps = [ ":id_tag", ":mem_usage", ":value_store_types", ":yaml", "//common:check", "//common:hashtable_key_context", "//common:ostream", "//common:set", "@llvm-project//llvm:Support", ], ) cc_test( name = "value_store_test", size = "small", srcs = ["value_store_test.cpp"], deps = [ ":value_ids", ":value_store", "//common:raw_string_ostream", "//testing/base:gtest_main", "@googletest//:gtest", ], ) cc_library( name = "value_store_types", hdrs = ["value_store_types.h"], deps = [ "@llvm-project//llvm:Support", ], ) cc_library( name = "int", srcs = ["int.cpp"], hdrs = ["int.h"], deps = [ ":canonical_value_store", ":index_base", ":mem_usage", ":value_store", ":yaml", "//common:check", "//common:hashtable_key_context", "//common:ostream", "//common:set", "@llvm-project//llvm:Support", ], ) cc_test( name = "int_test", size = "small", srcs = ["int_test.cpp"], deps = [ ":int", "//common:raw_string_ostream", "//testing/base:gtest_main", "//toolchain/testing:yaml_test_helpers", "@googletest//:gtest", ], ) generate_llvm_tools_def( name = "llvm_tools_def", out = "llvm_tools.def", ) config_setting( name = "is_macos", constraint_values = ["@platforms//os:macos"], ) cc_library( name = "llvm_tools", srcs = ["llvm_tools.cpp"], hdrs = ["llvm_tools.h"], linkopts = select({ # TODO: This should be moved upstream to LLVM's tool libraries that # require it. ":is_macos": [ "-framework", "CoreFoundation", ], "//conditions:default": [], }), deps = [ ":llvm_tools_def", "//common:command_line", "//common:enum_base", "@llvm-project//llvm:Support", ] + [info.lib for info in LLVM_MAIN_TOOLS.values()], ) generate_runtimes_build_info_cc_library(name = "runtimes_build_info") cc_library( name = "shared_value_stores", hdrs = ["shared_value_stores.h"], deps = [ ":canonical_value_store", ":int", ":mem_usage", ":value_ids", ":value_store", ":yaml", "@llvm-project//llvm:Support", ], ) cc_test( name = "shared_value_stores_test", size = "small", srcs = ["shared_value_stores_test.cpp"], deps = [ ":shared_value_stores", "//common:raw_string_ostream", "//testing/base:gtest_main", "//toolchain/testing:yaml_test_helpers", "@googletest//:gtest", ], ) cc_library( name = "yaml", hdrs = ["yaml.h"], deps = [ "//common:check", "//common:ostream", "@llvm-project//llvm:Support", ], ) ================================================ FILE: toolchain/base/block_value_store.h ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #ifndef CARBON_TOOLCHAIN_BASE_BLOCK_VALUE_STORE_H_ #define CARBON_TOOLCHAIN_BASE_BLOCK_VALUE_STORE_H_ #include #include "common/check.h" #include "common/set.h" #include "llvm/Support/Allocator.h" #include "toolchain/base/id_tag.h" #include "toolchain/base/mem_usage.h" #include "toolchain/base/value_store.h" #include "toolchain/base/yaml.h" namespace Carbon::SemIR { // Provides a block-based ValueStore, which uses slab allocation of added // blocks. This allows references to values to outlast vector resizes that might // otherwise invalidate references. // // BlockValueStore is used as-is, but there are also children that expose the // protected members for type-specific functionality. template class BlockValueStore : public Yaml::Printable> { public: using IdType = IdT; using IdTagType = IdTag; using ElementType = ElementT; using RefType = llvm::MutableArrayRef; using ConstRefType = llvm::ArrayRef; explicit BlockValueStore(llvm::BumpPtrAllocator& allocator, IdTagType::TagIdType tag_id, int32_t initial_reserved_ids = 0) requires(!IdTagIsUntagged) : allocator_(&allocator), values_(tag_id, initial_reserved_ids) { auto empty = RefType(); auto empty_val = canonical_blocks_.Insert( empty, [&] { return values_.Add(empty); }, KeyContext(this)); CARBON_CHECK(empty_val.key() == IdT::Empty); } // Adds a block with the given content, returning an ID to reference it. auto Add(ConstRefType content) -> IdT { if (content.empty()) { return IdT::Empty; } return values_.Add(AllocateCopy(content)); } // Returns the requested block. auto Get(IdT id) const -> ConstRefType { return values_.Get(id); } // Returns a mutable view of the requested block. This operation should be // avoided where possible; we generally want blocks to be immutable once // created. auto GetMutable(IdT id) -> RefType { return values_.Get(id); } // Returns a new block formed by applying `transform(elem_id)` to each element // in the specified block. template auto Transform(IdT id, TransformFnT transform) -> IdT { llvm::SmallVector block(llvm::map_range(Get(id), transform)); return Add(block); } // Adds a block or finds an existing canonical block with the given content, // and returns an ID to reference it. auto AddCanonical(ConstRefType content) -> IdT { if (content.empty()) { return IdT::Empty; } auto result = canonical_blocks_.Insert( content, [&] { return Add(content); }, KeyContext(this)); return result.key(); } // Promotes an existing block ID to a canonical block ID, or returns an // existing canonical block ID if the block was already added. The specified // block must not be modified after this point. auto MakeCanonical(IdT id) -> IdT { // Get the content first so that we don't have unnecessary translation of // the `id` into the content during insertion. auto result = canonical_blocks_.Insert( Get(id), [id] { return id; }, KeyContext(this)); return result.key(); } auto OutputYaml() const -> Yaml::OutputMapping { return Yaml::OutputMapping([&](Yaml::OutputMapping::Map map) { for (auto [block_id, block] : values_.enumerate()) { map.Add(PrintToString(block_id), Yaml::OutputMapping([&](Yaml::OutputMapping::Map map) { for (auto [i, elem_id] : llvm::enumerate(block)) { map.Add(llvm::itostr(i), Yaml::OutputScalar(elem_id)); } })); } }); } // Collects memory usage of members. auto CollectMemUsage(MemUsage& mem_usage, llvm::StringRef label) const -> void { mem_usage.Collect(MemUsage::ConcatLabel(label, "values_"), values_); mem_usage.Collect(MemUsage::ConcatLabel(label, "canonical_blocks_"), canonical_blocks_, KeyContext(this)); } auto size() const -> int { return values_.size(); } auto GetRawIndex(IdT id) const -> int { return values_.GetRawIndex(id); } protected: // Allocates a copy of the given data using our slab allocator. auto AllocateCopy(ConstRefType data) -> RefType { auto result = AllocateUninitialized(data.size()); std::uninitialized_copy(data.begin(), data.end(), result.begin()); return result; } // Allocates an uninitialized array using our slab allocator. auto AllocateUninitialized(size_t size) -> RefType { // We're not going to run a destructor, so ensure that's OK. static_assert(std::is_trivially_destructible_v); auto storage = static_cast( allocator_->Allocate(size * sizeof(ElementType), alignof(ElementType))); return RefType(storage, size); } // Allow children to have more complex value handling. auto values() -> ValueStore& { return values_; } private: class KeyContext; llvm::BumpPtrAllocator* allocator_; ValueStore values_; Set canonical_blocks_; }; template class BlockValueStore::KeyContext : public TranslatingKeyContext { public: explicit KeyContext(const BlockValueStore* store) : store_(store) {} auto TranslateKey(IdT id) const -> ConstRefType { return store_->Get(id); } private: const BlockValueStore* store_; }; } // namespace Carbon::SemIR #endif // CARBON_TOOLCHAIN_BASE_BLOCK_VALUE_STORE_H_ ================================================ FILE: toolchain/base/canonical_value_store.h ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #ifndef CARBON_TOOLCHAIN_BASE_CANONICAL_VALUE_STORE_H_ #define CARBON_TOOLCHAIN_BASE_CANONICAL_VALUE_STORE_H_ #include "common/hashtable_key_context.h" #include "common/set.h" #include "toolchain/base/mem_usage.h" #include "toolchain/base/value_store.h" #include "toolchain/base/value_store_types.h" #include "toolchain/base/yaml.h" namespace Carbon { // A wrapper for accumulating immutable values with deduplication, providing IDs // to later retrieve the value. // // `ValueT` represents the type being stored. // // `KeyT` can optionally be different from `ValueT`, and if so is used for the // argument to `Lookup`. In this case, `ValueT` must provide a `GetAsKey` member // function that returns the corresponding key. template class CanonicalValueStore { public: using IdType = IdT; using IdTagType = IdTag; using KeyType = std::remove_cvref_t; using ValueType = ValueStoreTypes::ValueType; using RefType = ValueStoreTypes::RefType; using ConstRefType = ValueStoreTypes::ConstRefType; CanonicalValueStore() = default; template explicit CanonicalValueStore(Id id, int32_t initial_reserved_ids = 0) : values_(id, initial_reserved_ids) {} // Stores a canonical copy of the value and returns an ID to reference it. If // the value is already in the store, returns the ID of the existing value. auto Add(ValueType value) -> IdT; // Returns the value for an ID. auto Get(IdT id) const -> ConstRefType { return values_.Get(id); } // Looks up the canonical ID for a value, or returns `None` if not in the // store. auto Lookup(KeyType key) const -> IdT; // Reserves space. auto Reserve(size_t size) -> void; // These are to support printable structures, and are not guaranteed. auto OutputYaml() const -> Yaml::OutputMapping { return values_.OutputYaml(); } auto values() const [[clang::lifetimebound]] -> ValueStore::Range { return values_.values(); } auto size() const -> size_t { return values_.size(); } auto enumerate() const -> auto { return values_.enumerate(); } // Collects memory usage of the values and deduplication set. auto CollectMemUsage(MemUsage& mem_usage, llvm::StringRef label) const -> void { mem_usage.Collect(MemUsage::ConcatLabel(label, "values_"), values_); auto bytes = set_.ComputeMetrics(KeyContext(&values_)).storage_bytes; mem_usage.Add(MemUsage::ConcatLabel(label, "set_"), bytes, bytes); } auto GetRawIndex(IdT id) const -> int32_t { return values_.GetRawIndex(id); } auto GetIdTag() const -> IdTagType { return values_.GetIdTag(); } private: class KeyContext; static auto GetAsKey(ConstRefType value) -> ConstRefType requires std::same_as { return value; } template static auto GetAsKey(T&& value) -> decltype(value.GetAsKey()) { return value.GetAsKey(); } ValueStore values_; Set set_; }; template class CanonicalValueStore::KeyContext : public TranslatingKeyContext { public: explicit KeyContext(const ValueStore* values) : values_(values) {} // Note that it is safe to return a reference here as the underlying object's // lifetime is provided by the `ValueStore`. auto TranslateKey(IdT id) const -> decltype(GetAsKey(std::declval())) { return GetAsKey(values_->Get(id)); } private: const ValueStore* values_; }; template auto CanonicalValueStore::Add(ValueType value) -> IdT { auto make_key = [&] { return IdT(values_.Add(std::move(value))); }; return set_.Insert(GetAsKey(value), make_key, KeyContext(&values_)).key(); } template auto CanonicalValueStore::Lookup(KeyType key) const -> IdT { if (auto result = set_.Lookup(key, KeyContext(&values_))) { return result.key(); } return IdT::None; } template auto CanonicalValueStore::Reserve(size_t size) -> void { // Compute the resulting new insert count using the size of values -- the // set doesn't have a fast to compute current size. if (size > values_.size()) { set_.GrowForInsertCount(size - values_.size(), KeyContext(&values_)); } values_.Reserve(size); } } // namespace Carbon #endif // CARBON_TOOLCHAIN_BASE_CANONICAL_VALUE_STORE_H_ ================================================ FILE: toolchain/base/canonical_value_store_test.cpp ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #include "toolchain/base/canonical_value_store.h" #include #include #include #include "llvm/ADT/APFloat.h" #include "llvm/ADT/StringRef.h" #include "toolchain/base/value_ids.h" namespace Carbon::Testing { namespace { using ::testing::Eq; using ::testing::Not; TEST(CanonicalValueStore, Float) { llvm::APFloat float1(1.0); llvm::APFloat float2(2.0); CanonicalValueStore floats; FloatId id1 = floats.Add(float1); FloatId id2 = floats.Add(float2); ASSERT_TRUE(id1.has_value()); ASSERT_TRUE(id2.has_value()); EXPECT_THAT(id1, Not(Eq(id2))); EXPECT_THAT(floats.Get(id1).compare(float1), Eq(llvm::APFloatBase::cmpEqual)); EXPECT_THAT(floats.Get(id2).compare(float2), Eq(llvm::APFloatBase::cmpEqual)); } TEST(CanonicalValueStore, Identifiers) { std::string a = "a"; std::string b = "b"; CanonicalValueStore identifiers; // Make sure reserve works, we use it with identifiers. identifiers.Reserve(100); auto a_id = identifiers.Add(a); auto b_id = identifiers.Add(b); ASSERT_TRUE(a_id.has_value()); ASSERT_TRUE(b_id.has_value()); EXPECT_THAT(a_id, Not(Eq(b_id))); EXPECT_THAT(identifiers.Get(a_id), Eq(a)); EXPECT_THAT(identifiers.Get(b_id), Eq(b)); EXPECT_THAT(identifiers.Lookup(a), Eq(a_id)); EXPECT_THAT(identifiers.Lookup("c"), Eq(IdentifierId::None)); } TEST(CanonicalValueStore, StringLiterals) { std::string a = "a"; std::string b = "b"; CanonicalValueStore string_literals; auto a_id = string_literals.Add(a); auto b_id = string_literals.Add(b); ASSERT_TRUE(a_id.has_value()); ASSERT_TRUE(b_id.has_value()); EXPECT_THAT(a_id, Not(Eq(b_id))); EXPECT_THAT(string_literals.Get(a_id), Eq(a)); EXPECT_THAT(string_literals.Get(b_id), Eq(b)); EXPECT_THAT(string_literals.Lookup(a), Eq(a_id)); EXPECT_THAT(string_literals.Lookup("c"), Eq(StringLiteralValueId::None)); } } // namespace } // namespace Carbon::Testing ================================================ FILE: toolchain/base/clang_invocation.cpp ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #include "toolchain/base/clang_invocation.h" #include #include #include "clang/Driver/CreateInvocationFromArgs.h" #include "clang/Frontend/CompilerInstance.h" #include "clang/Frontend/Utils.h" #include "common/string_helpers.h" #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/SmallVector.h" #include "llvm/ADT/StringRef.h" #include "llvm/Support/FormatVariadic.h" namespace Carbon { // The fake file name to use for the synthesized includes file. static constexpr const char IncludesFileName[] = ""; auto ClangDriverDiagnosticConsumer::HandleDiagnostic( clang::DiagnosticsEngine::Level diag_level, const clang::Diagnostic& info) -> void { DiagnosticConsumer::HandleDiagnostic(diag_level, info); llvm::SmallString<256> message; info.FormatDiagnostic(message); switch (diag_level) { case clang::DiagnosticsEngine::Ignored: case clang::DiagnosticsEngine::Note: case clang::DiagnosticsEngine::Remark: { // TODO: Emit notes and remarks. break; } case clang::DiagnosticsEngine::Warning: case clang::DiagnosticsEngine::Error: case clang::DiagnosticsEngine::Fatal: { CARBON_DIAGNOSTIC(CppInteropDriverWarning, Warning, "{0}", std::string); CARBON_DIAGNOSTIC(CppInteropDriverError, Error, "{0}", std::string); emitter_->Emit(diag_level == clang::DiagnosticsEngine::Warning ? CppInteropDriverWarning : CppInteropDriverError, message.str().str()); break; } } } auto BuildClangInvocation(Diagnostics::Consumer& consumer, llvm::IntrusiveRefCntPtr fs, const InstallPaths& install_paths, llvm::StringRef target_str, llvm::ArrayRef extra_args) -> std::unique_ptr { Diagnostics::ErrorTrackingConsumer error_tracker(consumer); Diagnostics::NoLocEmitter emitter(&error_tracker); ClangDriverDiagnosticConsumer diagnostics_consumer(&emitter); llvm::SmallVector args; args.push_back("--start-no-unused-arguments"); AppendDefaultClangArgs(install_paths, target_str, args); args.push_back("--end-no-unused-arguments"); args.append({ llvm::formatv("--target={0}", target_str).str(), // Add our include file name as the input file, and force it to be // interpreted as C++. "-x", "c++", IncludesFileName, }); // The clang driver inconveniently wants an array of `const char*`, so convert // the arguments. llvm::BumpPtrAllocator alloc; llvm::SmallVector cstr_args = BuildCStrArgs( install_paths.clang_path().native(), args, extra_args, alloc); // Build a diagnostics engine. Note that we don't have any diagnostic options // yet; they're produced by running the driver. clang::DiagnosticOptions driver_diag_opts; llvm::IntrusiveRefCntPtr driver_diags( clang::CompilerInstance::createDiagnostics(*fs, driver_diag_opts, &diagnostics_consumer, /*ShouldOwnClient=*/false)); // Ask the driver to process the arguments and build a corresponding clang // frontend invocation. auto invocation = clang::createInvocation(cstr_args, {.Diags = driver_diags, .VFS = fs}); // If Clang produced an error, throw away its invocation. if (error_tracker.seen_error()) { return nullptr; } if (invocation) { // Do not emit Clang's name and version as the creator of the output file. invocation->getCodeGenOpts().EmitVersionIdentMetadata = false; invocation->getCodeGenOpts().DiscardValueNames = false; } return invocation; } auto AppendDefaultClangArgs(const InstallPaths& install_paths, llvm::StringRef target_str, llvm::SmallVectorImpl& args) -> void { args.append({ // Enable PIE by default, but allow it to be overridden by Clang // arguments. Clang's default is configurable, but we'd like our // defaults to be more stable. // TODO: Decide if we want this. "-fPIE", // Enable function and data sections by default, and don't waste object // file size on unique section names. Allow these to be overridden by // Clang arguments. "-ffunction-sections", "-fdata-sections", "-fno-unique-section-names", // Override runtime library defaults. // // TODO: We should consider if there is a reasonable way to build Clang // with its configuration macros set to establish these defaults rather // than doing it with runtime flags. "-rtlib=compiler-rt", "-unwindlib=libunwind", "-stdlib=libc++", // Override the default linker to use. "-fuse-ld=lld", }); // Add target-specific flags. llvm::Triple triple(target_str); switch (triple.getOS()) { case llvm::Triple::Darwin: case llvm::Triple::MacOSX: // On macOS we need to set the sysroot to a viable SDK. Currently, this // hard codes the path to be the unversioned symlink. The prefix is also // hard coded in Homebrew and so this seems likely to work reasonably // well. Homebrew and I suspect the Xcode Clang both have this hard coded // at build time, so this seems reasonably safe but we can revisit if/when // needed. args.push_back( "--sysroot=/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk"); // We also need to insist on a modern linker, otherwise the driver tries // too old and deprecated flags. The specific number here comes from an // inspection of the Clang driver source code to understand where features // were enabled, and this appears to be the latest version to control // driver behavior. // // TODO: We should replace this with use of `lld` eventually. args.push_back("-mlinker-version=705"); break; default: break; } // Append our exact header search paths for the various parts of the C++ // standard library headers as we don't build a single unified tree. for (const std::filesystem::path& runtime_path : {install_paths.libunwind_path(), install_paths.libcxx_path(), install_paths.libcxxabi_path()}) { args.push_back( llvm::formatv("-stdlib++-isystem{0}", runtime_path / "include").str()); } } } // namespace Carbon ================================================ FILE: toolchain/base/clang_invocation.h ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #ifndef CARBON_TOOLCHAIN_BASE_CLANG_INVOCATION_H_ #define CARBON_TOOLCHAIN_BASE_CLANG_INVOCATION_H_ #include #include "clang/Basic/Diagnostic.h" #include "clang/Frontend/CompilerInvocation.h" #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/IntrusiveRefCntPtr.h" #include "llvm/Support/VirtualFileSystem.h" #include "toolchain/base/install_paths.h" #include "toolchain/diagnostics/emitter.h" namespace Carbon { // Converts diagnostics from the Clang driver to Carbon diagnostics. class ClangDriverDiagnosticConsumer : public clang::DiagnosticConsumer { public: // Creates an instance with the location that triggers calling Clang. // `context` must not be null. explicit ClangDriverDiagnosticConsumer(Diagnostics::NoLocEmitter* emitter) : emitter_(emitter) {} // Generates a Carbon warning for each Clang warning and a Carbon error for // each Clang error or fatal. auto HandleDiagnostic(clang::DiagnosticsEngine::Level diag_level, const clang::Diagnostic& info) -> void override; private: // Diagnostic emitter. Note that driver diagnostics don't have meaningful // locations attached. Diagnostics::NoLocEmitter* emitter_; }; // Builds and returns a clang `CompilerInvocation` to use when building code for // interop, from a list of clang driver arguments. Emits diagnostics to // `consumer` if the arguments are invalid. auto BuildClangInvocation(Diagnostics::Consumer& consumer, llvm::IntrusiveRefCntPtr fs, const InstallPaths& install_paths, llvm::StringRef target_str, llvm::ArrayRef extra_args = {}) -> std::unique_ptr; // Appends the default Clang command line arguments used when building a // Carbon-compatible Clang invocation. // // Where possible, code should use `BuildClangInvocation` above. However, when // invoking Clang directly, this can be used to get the core compatible flags. auto AppendDefaultClangArgs(const InstallPaths& install_paths, llvm::StringRef target_str, llvm::SmallVectorImpl& args) -> void; } // namespace Carbon #endif // CARBON_TOOLCHAIN_BASE_CLANG_INVOCATION_H_ ================================================ FILE: toolchain/base/fixed_size_value_store.h ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #ifndef CARBON_TOOLCHAIN_BASE_FIXED_SIZE_VALUE_STORE_H_ #define CARBON_TOOLCHAIN_BASE_FIXED_SIZE_VALUE_STORE_H_ #include #include #include "common/check.h" #include "llvm/ADT/SmallVector.h" #include "llvm/ADT/StringRef.h" #include "llvm/ADT/iterator_range.h" #include "toolchain/base/mem_usage.h" #include "toolchain/base/value_store.h" #include "toolchain/base/value_store_types.h" namespace Carbon { // A value store with a predetermined size. template class FixedSizeValueStore { public: using IdType = IdT; using IdTagType = IdTag; using ValueType = ValueStoreTypes::ValueType; using RefType = ValueStoreTypes::RefType; using ConstRefType = ValueStoreTypes::ConstRefType; // Makes a ValueStore of the specified size, but without initializing values. // Entries must be set before reading. static auto MakeForOverwriteWithExplicitSize(size_t size, IdTagType::TagIdType tag_id, int32_t initial_reserved_ids = 0) -> FixedSizeValueStore requires(!IdTagIsUntagged) { FixedSizeValueStore store(IdTagType(tag_id, initial_reserved_ids)); store.values_.resize_for_overwrite(size); return store; } static auto MakeForOverwriteWithExplicitSize(size_t size) -> FixedSizeValueStore requires(IdTagIsUntagged) { FixedSizeValueStore store; store.values_.resize_for_overwrite(size); return store; } // Makes a ValueStore of the same size as a source `ValueStoreT`, but without // initializing values. Entries must be set before reading. template requires(std::same_as && !IdTagIsUntagged) static auto MakeForOverwrite(const ValueStoreT& size_source) -> FixedSizeValueStore { FixedSizeValueStore store(size_source.GetIdTag()); store.values_.resize_for_overwrite(size_source.size()); return store; } // Makes a ValueStore of the specified size, initialized to a default. static auto MakeWithExplicitSize(size_t size, IdTagType tag, ConstRefType default_value) -> FixedSizeValueStore requires(!IdTagIsUntagged) { FixedSizeValueStore store(tag); store.values_.resize(size, default_value); return store; } static auto MakeWithExplicitSize(size_t size, ConstRefType default_value) -> FixedSizeValueStore requires(IdTagIsUntagged) { FixedSizeValueStore store; store.values_.resize(size, default_value); return store; } // Makes a ValueStore of the specified size, initialized to values returned // from a callback. This allows initializing non-copyable values. static auto MakeWithExplicitSizeFrom( size_t size, llvm::function_refValueT> callable) -> FixedSizeValueStore { FixedSizeValueStore store; store.values_.reserve(size); for (auto _ : llvm::seq(size)) { store.values_.push_back(callable()); } return store; } // Makes a ValueStore of the same size as a source `ValueStoreT`. This is // the safest constructor to use, since it ensures everything's initialized to // a default, and verifies a matching `IdT` for the size. template requires(std::same_as && !IdTagIsUntagged && !IdTagIsUntagged) explicit FixedSizeValueStore(const ValueStoreT& size_source, ConstRefType default_value) : tag_(size_source.GetIdTag()) { values_.resize(size_source.size(), default_value); } template requires(std::same_as && IdTagIsUntagged && IdTagIsUntagged) explicit FixedSizeValueStore(const ValueStoreT& size_source, ConstRefType default_value) { values_.resize(size_source.size(), default_value); } explicit FixedSizeValueStore(IdTagType tag) : tag_(tag) {} // Makes a ValueStore using a mapped range of `source`. The `factory_fn` // receives each enumerated entry for construction of `ValueType`. template requires(std::same_as && !IdTagIsUntagged && !IdTagIsUntagged) explicit FixedSizeValueStore( const ValueStoreT& source, llvm::function_ref< auto(IdT, typename ValueStoreT::ConstRefType)->ValueType> factory_fn) : values_(llvm::map_range(source.enumerate(), factory_fn)), tag_(GetIdTag(source)) {} template requires(std::same_as && IdTagIsUntagged && IdTagIsUntagged) explicit FixedSizeValueStore( const ValueStoreT& source, llvm::function_ref< auto(IdT, typename ValueStoreT::ConstRefType)->ValueType> factory_fn) : values_(llvm::map_range(source.enumerate(), factory_fn)) {} // Move-only. FixedSizeValueStore(FixedSizeValueStore&&) noexcept = default; auto operator=(FixedSizeValueStore&&) noexcept -> FixedSizeValueStore& = default; // Sets the value for an ID. auto Set(IdT id, ValueType value) -> void { CARBON_DCHECK(id.index >= 0, "{0}", id); auto index = tag_.Remove(id); values_[index] = value; } // Returns a mutable value for an ID. auto Get(IdT id) -> RefType { CARBON_DCHECK(id.index >= 0, "{0}", id); auto index = tag_.Remove(id); return values_[index]; } // Returns the value for an ID. auto Get(IdT id) const -> ConstRefType { CARBON_DCHECK(id.index >= 0, "{0}", id); auto index = tag_.Remove(id); return values_[index]; } // Collects memory usage of the values. auto CollectMemUsage(MemUsage& mem_usage, llvm::StringRef label) const -> void { mem_usage.Collect(label.str(), values_); } auto size() const -> size_t { return values_.size(); } auto values() -> llvm::iterator_range::iterator> { return llvm::make_range(values_.begin(), values_.end()); } auto values() const -> llvm::iterator_range< typename llvm::SmallVector::const_iterator> { return llvm::make_range(values_.begin(), values_.end()); } private: // Allow default construction for `Make` functions. FixedSizeValueStore() = default; // Storage for the `ValueT` objects, indexed by the id. llvm::SmallVector values_; IdTagType tag_; }; } // namespace Carbon #endif // CARBON_TOOLCHAIN_BASE_FIXED_SIZE_VALUE_STORE_H_ ================================================ FILE: toolchain/base/for_each_macro.h ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #ifndef CARBON_TOOLCHAIN_BASE_FOR_EACH_MACRO_H_ #define CARBON_TOOLCHAIN_BASE_FOR_EACH_MACRO_H_ /// CARBON_FOR_EACH() will apply `macro` to each argument in the variadic /// argument list, putting the output of `sep()` between each one. /// /// The `sep` should be a function macro that returns a separator. Premade /// separataors are provided as CARBON_FOR_EACH_XYZ() macros. #define CARBON_FOR_EACH(macro, sep, ...) \ __VA_OPT__(CARBON_INTERNAL_FOR_EACH_EXPAND( \ CARBON_INTERNAL_FOR_EACH(macro, sep, __VA_ARGS__))) #define CARBON_FOR_EACH_COMMA() , #define CARBON_FOR_EACH_SEMI() ; #define CARBON_FOR_EACH_CONCAT() // Internal helpers #define CARBON_INTERNAL_FOR_EACH(macro, sep, a1, ...) \ macro(a1) __VA_OPT__(sep()) __VA_OPT__( \ CARBON_INTERNAL_FOR_EACH_AGAIN CARBON_INTERNAL_FOR_EACH_PARENS( \ macro, sep, __VA_ARGS__)) #define CARBON_INTERNAL_FOR_EACH_PARENS () #define CARBON_INTERNAL_FOR_EACH_AGAIN() CARBON_INTERNAL_FOR_EACH #define CARBON_INTERNAL_FOR_EACH_EXPAND(...) \ CARBON_INTERNAL_FOR_EACH_EXPAND1( \ CARBON_INTERNAL_FOR_EACH_EXPAND1(CARBON_INTERNAL_FOR_EACH_EXPAND1( \ CARBON_INTERNAL_FOR_EACH_EXPAND1(__VA_ARGS__)))) #define CARBON_INTERNAL_FOR_EACH_EXPAND1(...) \ CARBON_INTERNAL_FOR_EACH_EXPAND2( \ CARBON_INTERNAL_FOR_EACH_EXPAND2(CARBON_INTERNAL_FOR_EACH_EXPAND2( \ CARBON_INTERNAL_FOR_EACH_EXPAND2(__VA_ARGS__)))) #define CARBON_INTERNAL_FOR_EACH_EXPAND2(...) \ CARBON_INTERNAL_FOR_EACH_EXPAND3( \ CARBON_INTERNAL_FOR_EACH_EXPAND3(CARBON_INTERNAL_FOR_EACH_EXPAND3( \ CARBON_INTERNAL_FOR_EACH_EXPAND3(__VA_ARGS__)))) #define CARBON_INTERNAL_FOR_EACH_EXPAND3(...) __VA_ARGS__ #endif // CARBON_TOOLCHAIN_BASE_FOR_EACH_MACRO_H_ ================================================ FILE: toolchain/base/id_tag.h ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #ifndef CARBON_TOOLCHAIN_BASE_ID_TAG_H_ #define CARBON_TOOLCHAIN_BASE_ID_TAG_H_ #include #include #include "common/check.h" #include "common/ostream.h" #include "llvm/Support/MathExtras.h" namespace Carbon { // A sentinel type to construct an IdTag without tagging. struct Untagged : Printable { auto Print(llvm::raw_ostream& out) const -> void { out << ""; } }; // A wrapper type used as the template argument to IdTag, in order to mark the // tag type as such. template struct Tag {}; template struct GetTagIdType { static_assert(false, "IdTag with TagT that is neither Untagged nor Tag"); }; template <> struct GetTagIdType { using TagIdType = Untagged; }; template struct GetTagIdType> { using TagIdType = TagIdT; }; // Tests if an `IdTag` type is untagged. template concept IdTagIsUntagged = std::same_as; // A tagged Id. It is used to add a tag into the unused bits of the id, in order // to verify ids are used in the correct context. The tag type must be `Tag` or // `Untagged`. template struct IdTag { using IdType = IdT; using TagIdType = GetTagIdType::TagIdType; IdTag() requires(IdTagIsUntagged) = default; IdTag(TagIdType tag, int32_t initial_reserved_ids) requires(!IdTagIsUntagged) : // Shift down by 1 to get out of the high bit to avoid using any // negative ids, since they have special uses. Shift down by another 1 // to free up the second highest bit for a marker to indicate whether // the index is tagged (& needs to be untagged) or not. Add one to the // index so it's not zero-based, to make it a bit less likely this // doesn't collide with anything else (though with the // second-highest-bit-tagging this might not be needed). tag_(llvm::reverseBits((((tag.index + 1) << 1) | 1) << 1)), initial_reserved_ids_(initial_reserved_ids) {} auto Apply(int32_t index) const -> IdT { CARBON_DCHECK(index >= 0, "{0}", index); if (index < initial_reserved_ids_) { return IdT(index); } // TODO: Assert that tag_ doesn't have the second highest bit set. auto tagged_index = index ^ tag_; CARBON_DCHECK(tagged_index >= 0, "{0}", tagged_index); return IdT(tagged_index); } auto Remove(IdT id) const -> int32_t { CARBON_DCHECK(id.index >= 0, "{0}", id); if (!HasTag(id.index)) { CARBON_DCHECK(id.index < initial_reserved_ids_, "This untagged index is outside the initial reserved ids " "and should have been tagged."); return id.index; } auto untagged_index = id.index ^ tag_; CARBON_DCHECK(untagged_index >= initial_reserved_ids_, "When removing tagging bits, found an index that " "shouldn't've been tagged in the first place."); return untagged_index; } // Gets the value unique to this IdTag instance that is added to indices in // Apply, and removed in Remove. auto GetContainerTag() const -> TagIdType { if constexpr (IdTagIsUntagged) { return TagIdType(); } else { return TagIdType((llvm::reverseBits(tag_) >> 2) - 1); } } // Returns whether `tagged_index` has an IdTag applied to it, from this IdTag // instance or any other one. static auto HasTag(int32_t tagged_index) -> bool { return (llvm::reverseBits(2) & tagged_index) != 0; } struct TagAndIndex { TagIdType tag; int32_t index; }; static auto DecomposeWithBestEffort(IdT id) -> TagAndIndex { if constexpr (IdTagIsUntagged) { return {TagIdType(), id.index}; } else { if (!id.has_value()) { return {TagIdType::None, id.index}; } if (!HasTag(id.index)) { return {TagIdType::None, id.index}; } int length = 0; int location = 0; for (int i = 0; i != 32; ++i) { int current_run = 0; int location_of_current_run = i; while (i != 32 && (id.index & (1 << i)) == 0) { ++current_run; ++i; } if (current_run != 0) { --i; } if (current_run > length) { length = current_run; location = location_of_current_run; } } if (length < 8) { return {TagIdType::None, id.index}; } auto index_mask = llvm::maskTrailingOnes(location); auto tag = (llvm::reverseBits(id.index & ~index_mask) >> 2) - 1; auto index = id.index & index_mask; return {.tag = TagIdType(static_cast(tag)), .index = static_cast(index)}; } } // Converts an IdTag to be used for a different ID type. This is only valid // when the id indices are interchangeable, as they will have the same tag and // the same reserved ids. template requires(!IdTagIsUntagged) auto ToEquivalentIdType() -> IdTag> { return {GetContainerTag(), initial_reserved_ids_}; } private: int32_t tag_ = 0; int32_t initial_reserved_ids_ = std::numeric_limits::max(); }; } // namespace Carbon #endif // CARBON_TOOLCHAIN_BASE_ID_TAG_H_ ================================================ FILE: toolchain/base/index_base.h ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #ifndef CARBON_TOOLCHAIN_BASE_INDEX_BASE_H_ #define CARBON_TOOLCHAIN_BASE_INDEX_BASE_H_ #include #include #include #include #include "common/ostream.h" #include "llvm/ADT/iterator.h" #include "llvm/Support/Format.h" namespace Carbon { template class DataIterator; // Non-templated portions of `IdBase`. struct AnyIdBase { static constexpr int32_t NoneIndex = -1; AnyIdBase() = delete; constexpr explicit AnyIdBase(int index) : index(index) {} constexpr auto has_value() const -> bool { return index != NoneIndex; } int32_t index; }; // A lightweight handle to an item identified by an opaque ID. // // This class is intended to be derived from by classes representing a specific // kind of ID, whose meaning as an integer is an implementation detail of the // type that vends the IDs. Typically this will be a vector index. // // Classes derived from IdBase are designed to be passed by value, not // reference or pointer. They are also designed to be small and efficient to // store in data structures. // // This uses CRTP for the `Print` function. Children should have: // static constexpr llvm::StringLiteral Label = "my_label"; // Children can also define their own `Print` function, removing the dependency // on `Label`. template struct IdBase : public AnyIdBase, public Printable { using AnyIdBase::AnyIdBase; // Provide a standard `None` mapping to `NoneIndex`. // // This uses a `&` to trigger slightly different instantiation behaviors in // Clang. For context on why this is needed, see http://wg21.link/CWG2800. // NOLINTNEXTLINE(readability-identifier-naming) static const IdT& None; auto Print(llvm::raw_ostream& out) const -> void { out << IdT::Label; if (has_value()) { out << llvm::format_hex_no_prefix(index, 0, /*Upper=*/true); } else { out << ""; } } // Support simple equality comparison for ID types. friend constexpr auto operator==(IdBase lhs, IdBase rhs) -> bool { return lhs.index == rhs.index; } }; template constexpr const IdT& IdBase::None = IdT(NoneIndex); // A lightweight handle to an item that behaves like an index. // // Unlike IdBase, classes derived from IndexBase are not completely opaque, and // provide at least an ordering between indexes that has meaning to an API // user. Additional semantics may be specified by the derived class. template struct IndexBase : public IdBase { using IdBase::IdBase; // Support relational comparisons for index types. friend auto operator<=>(IndexBase lhs, IndexBase rhs) -> std::strong_ordering { return lhs.index <=> rhs.index; } // Print indexed ids in decimal, since they won't have tagging (because, as // the class comment explains, these ids are not entirely opaque). auto Print(llvm::raw_ostream& out) const -> void { out << IdT::Label; if (this->has_value()) { out << this->index; } else { out << ""; } } }; // A random-access iterator for arrays using IndexBase-derived types. template class IndexIterator : public llvm::iterator_facade_base, std::random_access_iterator_tag, const IndexT, int>, public Printable> { public: IndexIterator() = delete; explicit IndexIterator(IndexT index) : index_(index) {} friend auto operator==(const IndexIterator& lhs, const IndexIterator& rhs) -> bool { return lhs.index_ == rhs.index_; } friend auto operator<=>(const IndexIterator& lhs, const IndexIterator& rhs) -> std::strong_ordering { return lhs.index_ <=> rhs.index_; } auto operator*() const -> const IndexT& { return index_; } using llvm::iterator_facade_base::operator-; friend auto operator-(const IndexIterator& lhs, const IndexIterator& rhs) -> int { return lhs.index_.index - rhs.index_.index; } auto operator+=(int n) -> IndexIterator& { index_.index += n; return *this; } auto operator-=(int n) -> IndexIterator& { index_.index -= n; return *this; } // Prints the raw index. auto Print(llvm::raw_ostream& output) const -> void { output << index_.index; } private: IndexT index_; }; } // namespace Carbon #endif // CARBON_TOOLCHAIN_BASE_INDEX_BASE_H_ ================================================ FILE: toolchain/base/install_paths.cpp ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #include "toolchain/base/install_paths.h" #include #include #include #include "clang/Basic/Version.h" #include "common/check.h" #include "common/filesystem.h" #include "llvm/ADT/StringExtras.h" #include "llvm/ADT/StringRef.h" #include "llvm/Support/FileSystem.h" #include "llvm/Support/Path.h" #include "llvm/Support/VirtualFileSystem.h" #include "tools/cpp/runfiles/runfiles.h" namespace Carbon { // The location within our Bazel output tree of the install root. static constexpr llvm::StringLiteral BazelRoot = "carbon/toolchain/install/prefix/lib/carbon/"; // Path within an install root for our marker of a valid install. static constexpr llvm::StringLiteral MarkerPath = "carbon_install.txt"; auto InstallPaths::MakeExeRelative(llvm::StringRef exe_path) -> InstallPaths { InstallPaths paths; // Double check the exe was present. auto exe_access_result = Filesystem::Cwd().Access(exe_path.str()); if (!exe_access_result.ok()) { paths.SetError(llvm::Twine("Failed to test for access executable: ") + exe_access_result.error().ToString()); return paths; } else if (!*exe_access_result) { paths.SetError(llvm::Twine("Unable to access executable: ") + exe_path); return paths; } return MakeFromFile(exe_path.str()); } auto InstallPaths::MakeForBazelRunfiles(llvm::StringRef exe_path) -> InstallPaths { using bazel::tools::cpp::runfiles::Runfiles; std::string runtimes_error; std::unique_ptr runfiles( Runfiles::Create(exe_path.str(), &runtimes_error)); CARBON_CHECK(runfiles != nullptr, "Failed to find runtimes tree: {0}", runtimes_error); std::string relative_marker_path = (BazelRoot.str() + MarkerPath).str(); std::filesystem::path runtimes_marker_path = runfiles->Rlocation(relative_marker_path); // Directly use the marker file's path. return MakeFromFile(std::move(runtimes_marker_path)); } auto InstallPaths::Make(llvm::StringRef install_root) -> InstallPaths { InstallPaths paths(install_root.str()); auto open_result = Filesystem::Cwd().OpenDir(paths.root_); if (!open_result.ok()) { paths.SetError(open_result.error().ToString()); } else { paths.root_dir_ = *std::move(open_result); paths.CheckMarkerFile(); } return paths; } auto InstallPaths::ReadPreludeManifest() const -> ErrorOr> { return ReadManifest(core_package(), "prelude_manifest.txt"); } auto InstallPaths::ReadClangHeadersManifest() const -> ErrorOr> { // TODO: This is the only place where we read from outside of the install // root. Consider whether this manifest should be within the install or // consider moving the code to access it to be separate and specific to the // infrastructure needing it. return ReadManifest(root_ / "../../..", "clang_headers_manifest.txt"); } auto InstallPaths::ReadManifest(std::filesystem::path manifest_path, std::filesystem::path manifest_file) const -> ErrorOr> { // This is structured to avoid a vector copy on success. ErrorOr> result = llvm::SmallVector(); // TODO: It would be nice to adjust the manifests to be within the install // root and use that open directory to access the manifest. Also to update // callers to be able to use the relative paths via an open directory rather // than having to form absolute paths for all the entries. auto read_result = Filesystem::Cwd().ReadFileToString(manifest_path / manifest_file); if (!read_result.ok()) { result = ErrorBuilder() << "Loading manifest `" << (manifest_path / manifest_file) << "`: " << read_result.error(); return result; } // The manifest should have one file per line. llvm::StringRef buffer = *read_result; while (true) { auto [token, remainder] = llvm::getToken(buffer, "\n"); if (token.empty()) { break; } result->push_back((manifest_path / std::string_view(token)).native()); buffer = remainder; } if (result->empty()) { result = ErrorBuilder() << "Manifest `" << (manifest_path / manifest_file) << "` is empty"; } return result; } auto InstallPaths::MakeFromFile(std::filesystem::path file_path) -> InstallPaths { // TODO: Add any custom logic needed to detect the correct install root on // Windows once we have support for that platform. // // We assume the provided path is either the marker file itself or the busybox // executable that is adjacent to the marker file. That means the root is just // the directory of this path. InstallPaths paths(std::move(file_path).remove_filename()); auto open_result = Filesystem::Cwd().OpenDir(paths.root_); if (!open_result.ok()) { paths.SetError(open_result.error().ToString()); return paths; } paths.root_dir_ = *std::move(open_result); paths.CheckMarkerFile(); return paths; } auto InstallPaths::SetError(llvm::Twine message) -> void { // Use an empty root on error as that should use the working directory which // is the least likely problematic. root_ = ""; root_dir_ = Filesystem::Dir(); error_ = {message.str()}; } auto InstallPaths::CheckMarkerFile() -> void { auto access_result = root_dir_.Access(MarkerPath.str()); if (!access_result.ok()) { SetError(access_result.error().ToString()); return; } if (!*access_result) { SetError(llvm::Twine("No install marker at path: ") + (root_ / std::string_view(MarkerPath)).native()); return; } // Success! } auto InstallPaths::core_package() const -> std::filesystem::path { // TODO: Adjust this to work equally well on Windows. return root_ / "core"; } auto InstallPaths::llvm_install_bin() const -> std::filesystem::path { // TODO: Adjust this to work equally well on Windows. return root_ / "llvm/bin"; } auto InstallPaths::clang_path() const -> std::filesystem::path { // TODO: Adjust this to work equally well on Windows. return llvm_install_bin() / "clang"; } auto InstallPaths::lld_path() const -> std::filesystem::path { // TODO: Adjust this to work equally well on Windows. return llvm_install_bin() / "lld"; } auto InstallPaths::ld_lld_path() const -> std::filesystem::path { // TODO: Adjust this to work equally well on Windows. return llvm_install_bin() / "ld.lld"; } auto InstallPaths::ld64_lld_path() const -> std::filesystem::path { // TODO: Adjust this to work equally well on Windows. return llvm_install_bin() / "ld64.lld"; } auto InstallPaths::llvm_tool_path(LLVMTool tool) const -> std::filesystem::path { // TODO: Adjust this to work equally well on Windows. return llvm_install_bin() / std::string_view(tool.bin_name()); } auto InstallPaths::clang_resource_path() const -> std::filesystem::path { // TODO: Adjust this to work equally well on Windows. return root_ / "llvm/lib/clang/" CLANG_VERSION_MAJOR_STRING; } auto InstallPaths::runtimes_root() const -> std::filesystem::path { // TODO: Adjust this to work equally well on Windows. return root_ / "runtimes"; } auto InstallPaths::libunwind_path() const -> std::filesystem::path { // TODO: Adjust this to work equally well on Windows. return runtimes_root() / "libunwind"; } auto InstallPaths::libcxx_path() const -> std::filesystem::path { // TODO: Adjust this to work equally well on Windows. return runtimes_root() / "libcxx"; } auto InstallPaths::libcxxabi_path() const -> std::filesystem::path { // TODO: Adjust this to work equally well on Windows. return runtimes_root() / "libcxxabi"; } auto InstallPaths::libc_path() const -> std::filesystem::path { // TODO: Adjust this to work equally well on Windows. return runtimes_root() / "libc"; } auto InstallPaths::digest_path() const -> std::filesystem::path { // TODO: Adjust this to work equally well on Windows. return root_ / "install_digest.txt"; } } // namespace Carbon ================================================ FILE: toolchain/base/install_paths.h ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #ifndef CARBON_TOOLCHAIN_BASE_INSTALL_PATHS_H_ #define CARBON_TOOLCHAIN_BASE_INSTALL_PATHS_H_ #include #include "common/error.h" #include "common/filesystem.h" #include "llvm/ADT/SmallString.h" #include "llvm/ADT/StringRef.h" #include "llvm/ADT/Twine.h" #include "toolchain/base/llvm_tools.h" namespace Carbon { // Locates the toolchain installation and provides paths to various components. // // The Carbon toolchain expects to be installed into a tree rooted at `root_`. // This root contains the marker file and the busy box binary. // // In a Unix-like filesystem environment, the root is typically located as // `/lib/carbon`, with symlinks in the other parts of the FHS-based // layout back to entries below this tree. However, this class and the toolchain // itself should only directly use things below the installation root to support // non-FHS usage. // // When locating an install, we verify it with `CheckMarkerFile`. When errors // occur, `SetError` makes `error()` available for diagnostics and clears the // install root, leaving things minimally functional but with the installation // root of the current working directory. // // The factory methods locate the install root based on their use-case: // // - `MakeExeRelative` for command line tools in an install. // - `MakeForBazelRunfiles` for locating through Bazel's runfile tree. // - `Make` for an explicit path, for example in tests. // // An instance of this class provides methods that query for specific paths // within the install. Note that we want to abstract away any platform // differences in the installation layout. When a specific part of the install // is needed, a dedicated accessor should be added that computes the path for // that component. // // TODO: Need to check the installation structure of LLVM on Windows and figure // out what Carbon's should be within a Windows prefix and how much of the // structure we can share with the Unix-y layout of the prefix. // // TODO: InstallPaths is typically called from places using a VFS (both tests // and the Driver), but does not use a VFS itself. It currently only supports // using the real filesystem, but should probably support a VFS. class InstallPaths { public: // Provide the current executable's path to detect the correct installation // root. This assumes the toolchain to be in its installed layout. static auto MakeExeRelative(llvm::StringRef exe_path) -> InstallPaths; // Provide the current executable's path, and use that to detect a Bazel or // Bazel-compatible runfiles install root. This should only be used where it // is reasonable to rely on this rather than a fixed install location such as // for internal development purposes or other Bazel users of the Carbon // library. // // This method of construction also ensures the result is valid. If detection // fails for any reason, it will `CARBON_CHECK` fail with the error message. static auto MakeForBazelRunfiles(llvm::StringRef exe_path) -> InstallPaths; // Provide an explicit install paths root. This is useful for testing or for // using Carbon in an environment with an unusual path to the installed files. static auto Make(llvm::StringRef install_root) -> InstallPaths; // Returns the contents of the prelude manifest file. This is the list of // files that define the prelude, and will always be non-empty on success. auto ReadPreludeManifest() const -> ErrorOr>; // Returns the contents of the clang builtin headers manifest file. This is // the list of header files that are installed as part of the clang compiler, // and will always be non-empty on success. auto ReadClangHeadersManifest() const -> ErrorOr>; // Check for an error detecting the install paths correctly. // // A nullopt return means no errors encountered and the paths should work // correctly. // // A string return means there was an error, and details of the error are // in the `StringRef` for inclusion in any user report. [[nodiscard]] auto error() const -> std::optional { return error_; } // The path to the root of this installation. auto root() const -> std::filesystem::path { return root_; } // The directory containing the `Core` package. Computed on demand. auto core_package() const -> std::filesystem::path; // The directory containing LLVM install binaries. Computed on demand. auto llvm_install_bin() const -> std::filesystem::path; // The path to `clang`. auto clang_path() const -> std::filesystem::path; // The path to `lld' and various aliases of `lld`. auto lld_path() const -> std::filesystem::path; auto ld_lld_path() const -> std::filesystem::path; auto ld64_lld_path() const -> std::filesystem::path; // The path to any of the LLVM tools. auto llvm_tool_path(LLVMTool tool) const -> std::filesystem::path; // The path to the Clang resources. auto clang_resource_path() const -> std::filesystem::path; // The path to the root of the runtimes. auto runtimes_root() const -> std::filesystem::path; // The path to `libunwind` runtime. auto libunwind_path() const -> std::filesystem::path; // The path to `libunwind` runtime. auto libcxx_path() const -> std::filesystem::path; // The path to `libunwind` runtime. auto libcxxabi_path() const -> std::filesystem::path; // The path to the LLVM `libc` runtime. auto libc_path() const -> std::filesystem::path; // The installation digest path. // // This file contains a digest of the installation. auto digest_path() const -> std::filesystem::path; private: friend class InstallPathsTestPeer; InstallPaths() { SetError("No root provided!"); } explicit InstallPaths(std::filesystem::path root) : root_(std::move(root)) {} static auto MakeFromFile(std::filesystem::path file) -> InstallPaths; // Set an error message on the install paths and reset the `root_` to empty, // which should use the current working directory. auto SetError(llvm::Twine message) -> void; // Check that the install paths have a marker file at // `root()/lib/carbon/carbon_install.txt". If not, calls `SetError` with the // relevant error message. auto CheckMarkerFile() -> void; // Read a manifest file. auto ReadManifest(std::filesystem::path manifest_path, std::filesystem::path manifest_file) const -> ErrorOr>; // The computed installation root. In the event of an error, this will be the // empty string. // // When run from Bazel (for example, in unit tests or development binaries) // this will look like: // `bazel-bin/some/bazel/target.runfiles/_main/toolchain/install/prefix/lib/carbon` // // When installed, it's expected to be similar to the CMake install prefix, // followed by `lib/carbon`: // // - `/usr/lib/carbon` or `/usr/local/lib/carbon` on Linux and most BSDs. // - `/opt/homebrew/lib/carbon` or similar on macOS with Homebrew. // - TODO: Figure out if this is `C:/Program Files/Carbon` or something else // on Windows. // // See https://cmake.org/cmake/help/latest/variable/CMAKE_INSTALL_PREFIX.html // for more details. While we don't build the toolchain with CMake, we expect // our installation to behave in a similar and compatible way. // // The hierarchy of files beneath the install root can be found in the // BUILD's `install_dirs` entry for `lib/carbon`. std::filesystem::path root_; // The opened root directory. Filesystem::Dir root_dir_; std::optional error_; }; } // namespace Carbon #endif // CARBON_TOOLCHAIN_BASE_INSTALL_PATHS_H_ ================================================ FILE: toolchain/base/install_paths_test.cpp ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #include "toolchain/base/install_paths.h" #include #include #include #include #include #include "common/check.h" #include "common/error_test_helpers.h" #include "common/filesystem.h" #include "llvm/ADT/SmallString.h" #include "llvm/Support/FormatVariadic.h" #include "testing/base/global_exe_path.h" #include "tools/cpp/runfiles/runfiles.h" namespace Carbon { class InstallPathsTestPeer { public: static auto GetRoot(const InstallPaths& paths) -> std::filesystem::path { return paths.root_; } }; namespace { using ::bazel::tools::cpp::runfiles::Runfiles; using ::testing::_; using ::testing::EndsWith; using ::testing::Eq; using ::testing::HasSubstr; using Testing::IsSuccess; using ::testing::Optional; using ::testing::StartsWith; class InstallPathsTest : public ::testing::Test { public: InstallPathsTest() { std::string error; test_runfiles_.reset(Runfiles::Create(Testing::GetExePath().str(), &error)); CARBON_CHECK(test_runfiles_ != nullptr, "{0}", error); } // Test the install paths found with the given `exe_path`. Will check that // the detected install root path has the expected location relative to the // FHS layout, and then check that the path accessors point to the right kind // of file or directory. auto TestInstallPaths(const InstallPaths& paths) -> void { std::filesystem::path root_path = InstallPathsTestPeer::GetRoot(paths); SCOPED_TRACE(llvm::formatv("Install root: '{0}'", root_path)); // Open the root directory. auto root_result = Filesystem::Cwd().OpenDir(root_path); ASSERT_THAT(root_result, IsSuccess(_)); Filesystem::Dir root = *std::move(root_result); // Check that the root is located in the expected part of the FHS layout. // TODO: Adjust this to work equally well on Windows. EXPECT_THAT(root_path.native(), EndsWith("lib/carbon/")); EXPECT_THAT( root.Access("../../bin/carbon", Filesystem::AccessCheckFlags::Execute), IsSuccess(Eq(true))) << "path: " << (root_path / "../../bin/carbon"); std::filesystem::path core_package_path = paths.core_package(); ASSERT_THAT(core_package_path, StartsWith(root_path)); EXPECT_THAT(Filesystem::Cwd().Access(core_package_path / "prelude.carbon"), IsSuccess(Eq(true))) << "path: " << core_package_path; std::filesystem::path llvm_bin_path = paths.llvm_install_bin(); ASSERT_THAT(llvm_bin_path, StartsWith(root_path)); auto open_result = Filesystem::Cwd().OpenDir(llvm_bin_path); ASSERT_THAT(open_result, IsSuccess(_)); Filesystem::Dir llvm_bin = *std::move(open_result); for (std::filesystem::path bin_name : {"ld.lld", "ld64.lld"}) { EXPECT_THAT( llvm_bin.Access(bin_name, Filesystem::AccessCheckFlags::Execute), IsSuccess(Eq(true))) << "path: " << (llvm_bin_path / bin_name); } } std::unique_ptr test_runfiles_; }; TEST_F(InstallPathsTest, RootBusybox) { std::string installed_busybox_path = test_runfiles_->Rlocation( "carbon/toolchain/install/prefix/lib/carbon/carbon-busybox"); auto paths = InstallPaths::MakeExeRelative(installed_busybox_path); ASSERT_THAT(paths.error(), Eq(std::nullopt)) << *paths.error(); TestInstallPaths(paths); } TEST_F(InstallPathsTest, RootExplicit) { std::string marker_path = test_runfiles_->Rlocation( "carbon/toolchain/install/prefix/lib/carbon/carbon_install.txt"); llvm::StringRef root_path = marker_path; CARBON_CHECK(root_path.consume_back("carbon_install.txt"), "Unexpected suffix of the marker path: {0}", marker_path); auto paths = InstallPaths::Make(root_path); ASSERT_THAT(paths.error(), Eq(std::nullopt)) << *paths.error(); TestInstallPaths(paths); } TEST_F(InstallPathsTest, TestRunfiles) { auto paths = InstallPaths::MakeForBazelRunfiles(Testing::GetExePath()); ASSERT_THAT(paths.error(), Eq(std::nullopt)) << *paths.error(); TestInstallPaths(paths); } TEST_F(InstallPathsTest, BinaryRunfiles) { std::filesystem::path test_binary_path = test_runfiles_->Rlocation("carbon/toolchain/base/test_binary"); ASSERT_THAT(Filesystem::Cwd().Access(test_binary_path, Filesystem::AccessCheckFlags::Execute), IsSuccess(Eq(true))) << "path: " << test_binary_path; auto paths = InstallPaths::MakeForBazelRunfiles(test_binary_path.native()); ASSERT_THAT(paths.error(), Eq(std::nullopt)) << *paths.error(); TestInstallPaths(paths); } TEST_F(InstallPathsTest, Errors) { auto paths = InstallPaths::Make("/foo/bar/baz"); EXPECT_THAT(paths.error(), Optional(HasSubstr("foo/bar/baz"))); EXPECT_THAT(InstallPathsTestPeer::GetRoot(paths), Eq("")); paths = InstallPaths::MakeExeRelative("foo/bar/baz"); EXPECT_THAT(paths.error(), Optional(HasSubstr("foo/bar/baz"))); EXPECT_THAT(InstallPathsTestPeer::GetRoot(paths), Eq("")); // Note that we can't test the runfiles code path from within a test because // it succeeds some of the time even with a bogus executable name. } } // namespace } // namespace Carbon ================================================ FILE: toolchain/base/install_paths_test_helpers.cpp ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #include "toolchain/base/install_paths_test_helpers.h" #include #include #include "testing/base/global_exe_path.h" namespace Carbon::Testing { // Prepares the VFS with prelude files from the real filesystem. Primarily for // tests. auto AddPreludeFilesToVfs( const InstallPaths& install_paths, llvm::IntrusiveRefCntPtr& vfs) -> void { // Load the prelude into the test VFS. auto real_fs = llvm::vfs::getRealFileSystem(); auto prelude = install_paths.ReadPreludeManifest(); CARBON_CHECK(prelude.ok(), "{0}", prelude.error()); for (const auto& path : *prelude) { llvm::ErrorOr> file = real_fs->getBufferForFile(path); CARBON_CHECK(file, "Error getting file: {0}", file.getError().message()); bool added = vfs->addFile(path, /*ModificationTime=*/0, std::move(*file)); CARBON_CHECK(added, "Duplicate file: {0}", path); } } } // namespace Carbon::Testing ================================================ FILE: toolchain/base/install_paths_test_helpers.h ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #ifndef CARBON_TOOLCHAIN_BASE_INSTALL_PATHS_TEST_HELPERS_H_ #define CARBON_TOOLCHAIN_BASE_INSTALL_PATHS_TEST_HELPERS_H_ #include "llvm/Support/VirtualFileSystem.h" #include "toolchain/base/install_paths.h" namespace Carbon::Testing { // Prepares the VFS with prelude files from the real filesystem. auto AddPreludeFilesToVfs( const InstallPaths& install_paths, llvm::IntrusiveRefCntPtr& vfs) -> void; } // namespace Carbon::Testing #endif // CARBON_TOOLCHAIN_BASE_INSTALL_PATHS_TEST_HELPERS_H_ ================================================ FILE: toolchain/base/int.cpp ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #include "toolchain/base/int.h" #include #include namespace Carbon { auto IntStore::CanonicalBitWidth(int significant_bits) -> int { // For larger integers, we store them in as a signed APInt with a canonical // width that is the smallest multiple of the word type's bits, but no // smaller than a minimum of 64 bits to avoid spurious resizing of the most // common cases (<= 64 bits). static constexpr int WordWidth = llvm::APInt::APINT_BITS_PER_WORD; return std::max( MinAPWidth, ((significant_bits + WordWidth - 1) / WordWidth) * WordWidth); } auto IntStore::CanonicalizeSigned(llvm::APInt value) -> llvm::APInt { return value.sextOrTrunc(CanonicalBitWidth(value.getSignificantBits())); } auto IntStore::CanonicalizeUnsigned(llvm::APInt value) -> llvm::APInt { // We need the width to include a zero sign bit as we canonicalize to a // signed representation. return value.zextOrTrunc(CanonicalBitWidth(value.getActiveBits() + 1)); } auto IntStore::AddLarge(int64_t value) -> IntId { auto ap_id = values_.Add(llvm::APInt(CanonicalBitWidth(64), value, /*isSigned=*/true)); return MakeIndexOrNone(ap_id.index); } auto IntStore::AddSignedLarge(llvm::APInt value) -> IntId { auto ap_id = values_.Add(CanonicalizeSigned(value)); return MakeIndexOrNone(ap_id.index); } auto IntStore::AddUnsignedLarge(llvm::APInt value) -> IntId { auto ap_id = values_.Add(CanonicalizeUnsigned(value)); return MakeIndexOrNone(ap_id.index); } auto IntStore::LookupLarge(int64_t value) const -> IntId { auto ap_id = values_.Lookup( llvm::APInt(CanonicalBitWidth(64), value, /*isSigned=*/true)); return MakeIndexOrNone(ap_id.index); } auto IntStore::LookupSignedLarge(llvm::APInt value) const -> IntId { auto ap_id = values_.Lookup(CanonicalizeSigned(value)); return MakeIndexOrNone(ap_id.index); } auto IntStore::OutputYaml() const -> Yaml::OutputMapping { return values_.OutputYaml(); } auto IntStore::CollectMemUsage(MemUsage& mem_usage, llvm::StringRef label) const -> void { mem_usage.Collect(std::string(label), values_); } } // namespace Carbon ================================================ FILE: toolchain/base/int.h ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #ifndef CARBON_TOOLCHAIN_BASE_INT_H_ #define CARBON_TOOLCHAIN_BASE_INT_H_ #include "common/check.h" #include "llvm/ADT/APInt.h" #include "llvm/ADT/SmallVector.h" #include "toolchain/base/canonical_value_store.h" #include "toolchain/base/index_base.h" #include "toolchain/base/mem_usage.h" #include "toolchain/base/value_store.h" #include "toolchain/base/yaml.h" namespace Carbon { // Forward declare a testing peer so we can friend it. namespace Testing { struct IntStoreTestPeer; } // namespace Testing // Corresponds to a canonicalized integer value. This is used both for integer // literal tokens, and integer values in SemIR. These always represent the // abstract mathematical value -- signed and regardless of the needed precision. // // Small values are internalized into the ID itself. Large values are // represented as an index into an array of `APInt`s with a canonicalized bit // width. The ID itself can be queried for whether it is a value-embedded-ID or // an index ID. The ID also provides APIs for extracting either the value or an // index. // // ## Details of the encoding scheme ## // // We need all the values from a maximum to minimum, as well as a healthy range // of indices, to fit within the token ID bits. // // We represent this as a signed `TokenIdBits`-bit 2s compliment integer. The // sign extension from TokenIdBits to a register size can be folded into the // shift used to extract those bits from compressed bitfield storage. // // We then divide the smallest 1/4th of that bit width's space to represent // indices, and the larger 3/4ths to embedded values. For 23-bits total this // still gives us 2 million unique integers larger than the embedded ones, which // would be difficult to fill without exceeding the number of tokens we can lex // (8 million). For non-token based integers, the indices can continue downward // to the 32-bit signed integer minimum, supporting approximately 1.998 billion // unique larger integers. // // Note the `None` ID can't be used with a token. This is OK as we expect // invalid tokens to be *error* tokens and not need to represent an invalid // integer. class IntId : public Printable { public: static constexpr llvm::StringLiteral Label = "int"; // The encoding of integer IDs ensures that IDs associated with tokens during // lexing can fit into a compressed storage space. We arrange for // `TokenIdBits` to be the minimum number of bits of storage for token // associated IDs. The constant is public so the lexer can ensure it reserves // adequate space. // // Note that there may still be IDs either not associated with // tokens or computed after lexing outside of this range. static constexpr int TokenIdBits = 23; static const IntId None; static auto MakeFromTokenPayload(uint32_t payload) -> IntId { // Token-associated IDs are signed `TokenIdBits` integers, so force sign // extension from that bit. return IntId(static_cast(payload << TokenIdBitsShift) >> TokenIdBitsShift); } // Construct an ID from a raw 32-bit ID value. static constexpr auto MakeRaw(int32_t raw_id) -> IntId { return IntId(raw_id); } // Tests whether the ID is an embedded value ID. // // Note `None` is not an embedded value, so this implies `has_value()` is // true. constexpr auto is_embedded_value() const -> bool { return id_ > ZeroIndexId; } // Tests whether the ID is an index ID. // // Note `None` is represented as an index ID, so this is *not* sufficient to // test `has_value()`. constexpr auto is_index() const -> bool { return id_ <= ZeroIndexId; } // Test whether a value is present. // // This does not distinguish between embedded values and index IDs, only // whether some value is present. constexpr auto has_value() const -> bool { return id_ != NoneId; } // Converts an ID to the embedded value. Requires that `is_embedded_value()` // is true. constexpr auto AsValue() const -> int { CARBON_DCHECK(is_embedded_value()); return id_; } // Converts an ID to an index. Requires that `is_index()` is true. // // Note `None` is represented as an index ID, and can be converted here. constexpr auto AsIndex() const -> int { CARBON_DCHECK(is_index()); return ZeroIndexId - id_; } // Returns the ID formatted as a lex token payload. constexpr auto AsTokenPayload() const -> uint32_t { uint32_t payload = id_; // Ensure this ID round trips as the token payload. CARBON_DCHECK(*this == MakeFromTokenPayload(payload)); return payload; } constexpr auto AsRaw() const -> int32_t { return id_; } auto Print(llvm::raw_ostream& out) const -> void { out << Label << "("; if (is_embedded_value()) { out << "value: " << AsValue(); } else if (is_index()) { out << "index: " << AsIndex(); } else { CARBON_CHECK(!has_value()); out << ""; } out << ")"; } friend constexpr auto operator==(IntId lhs, IntId rhs) -> bool { return lhs.id_ == rhs.id_; } friend constexpr auto operator<=>(IntId lhs, IntId rhs) -> std::strong_ordering { return lhs.id_ <=> rhs.id_; } private: friend class IntStore; friend Testing::IntStoreTestPeer; // The shift needed when adjusting a between a `TokenIdBits`-width integer and // a 32-bit integer. static constexpr int TokenIdBitsShift = 32 - TokenIdBits; // The maximum embedded value in an ID. static constexpr int32_t MaxValue = std::numeric_limits::max() >> TokenIdBitsShift; // The ID value that represents an index of `0`. This is the first ID value // representing an index, and all indices are `<=` to this. // // `ZeroIndexId` is the first index ID, and we encode indices as successive // negative numbers counting downwards. The setup allows us to both use a // comparison with this ID to distinguish value and index IDs, and to compute // the actual index from the ID. // // The computation of an index in fact is just a subtraction: // `ZeroIndexId - id_`. Subtraction is *also* how most CPUs implement the // comparison, and so all of this ends up carefully constructed to enable very // small code size when testing for an embedded value and when that test fails // computing and using the index. static constexpr int32_t ZeroIndexId = std::numeric_limits::min() >> (TokenIdBitsShift + 1); // The minimum embedded value in an ID. static constexpr int32_t MinValue = ZeroIndexId + 1; // The `None` ID, which needs to be placed after the largest index, which // count downwards as IDs so below the smallest index ID, in order to optimize // the code sequence needed to distinguish between integer and value IDs and // to convert index IDs into actual indices small. static constexpr int32_t NoneId = std::numeric_limits::min(); // The `None` index. This is the result of converting a `None` ID into an // index. We ensure that conversion can be done so that we can simplify the // code that first tries to use an embedded value, then converts to an index // and checks that the index is still `None`. static const int32_t NoneIndex; // Document the specific values of some of these constants to help visualize // how the bit patterns map from the above computations. // // clang-format off: visualizing bit positions // // Each bit is either `T` for part of the token or `P` as part // of the available payload that we use for the ID: // // 0bTTTT'TTTT'TPPP'PPPP'PPPP'PPPP'PPPP'PPPP static_assert(MaxValue == 0b0000'0000'0011'1111'1111'1111'1111'1111); static_assert(ZeroIndexId == 0b1111'1111'1110'0000'0000'0000'0000'0000); static_assert(MinValue == 0b1111'1111'1110'0000'0000'0000'0000'0001); static_assert(NoneId == 0b1000'0000'0000'0000'0000'0000'0000'0000); // clang-format on constexpr explicit IntId(int32_t id) : id_(id) {} int32_t id_; }; inline constexpr IntId IntId::None(IntId::NoneId); // Note that we initialize the `None` index in a constexpr context which // ensures there is no UB in forming it. This helps ensure all the ID -> index // conversions are correct because the `None` ID is at the limit of that range. inline constexpr int32_t IntId::NoneIndex = None.AsIndex(); // A canonicalizing value store with deep optimizations for integers. // // This stores integers as abstract, signed mathematical integers. The bit width // of specific `APInt` values, either as inputs or outputs, is disregarded for // the purpose of canonicalization and the returned integer may use a very // different bit width `APInt` than was used when adding. There are also // optimized paths for adding integer values representable using native integer // types. // // Because the integers in the store are canonicalized with only a minimum bit // width, there are helper functions to coerce them to a specific desired bit // width for use. // // This leverages a significant optimization for small integer values -- rather // than canonicalizing and making them unique in a `ValueStore`, they are // directly embedded in the `IntId` itself. Only larger integers are stored in // an array of `APInt` values and represented as an index in the ID. class IntStore { public: // We rely on `APInt` values having a minimum bit width. static constexpr int MinAPWidth = 64; // The maximum supported bit width of an integer type. // TODO: Pick a maximum size and document it in the design. For now // we use 2^^23, because that's the largest size that LLVM supports. static constexpr int MaxIntWidth = 1 << 23; // Pick a canonical bit width for the provided number of significant bits. static auto CanonicalBitWidth(int significant_bits) -> int; // Accepts a signed `int64_t` and uses the mathematical signed integer value // of it as the added integer value. // // Returns the ID corresponding to this integer value, storing an `APInt` if // necessary to represent it. auto Add(int64_t value) -> IntId { // First try directly making this into an ID. if (IntId id = TryMakeValue(value); id.has_value()) [[likely]] { return id; } // Fallback for larger values. return AddLarge(value); } // Returns the ID corresponding to this integer value. auto Add(llvm::APSInt value) -> IntId { return value.isSigned() ? AddSigned(value) : AddUnsigned(value); } // Returns the ID corresponding to this signed integer value, storing an // `APInt` if necessary to represent it. auto AddSigned(llvm::APInt value) -> IntId { // First try directly making this into an ID. if (IntId id = TryMakeSignedValue(value); id.has_value()) [[likely]] { return id; } // Fallback for larger values. return AddSignedLarge(std::move(value)); } // Returns the ID corresponding to an equivalent signed integer value for the // provided unsigned integer value, storing an `APInt` if necessary to // represent it. auto AddUnsigned(llvm::APInt value) -> IntId { // First try directly making this into an ID. if (IntId id = TryMakeUnsignedValue(value); id.has_value()) [[likely]] { return id; } // Fallback for larger values. return AddUnsignedLarge(std::move(value)); } // Returns the value for an ID. // // This will always be a signed `APInt` with a canonical bit width for the // specific integer value in question. auto Get(IntId id) const -> llvm::APInt { if (id.is_embedded_value()) [[likely]] { return llvm::APInt(MinAPWidth, id.AsValue(), /*isSigned=*/true); } return values_.Get(APIntId(id.AsIndex())); } // Returns the value for an ID adjusted to a specific bit width. // // Note that because we store canonical mathematical integers as signed // integers, this always sign extends or truncates to the target width. The // caller can then use that as a signed or unsigned integer as needed. auto GetAtWidth(IntId id, int bit_width) const -> llvm::APInt { llvm::APInt value = Get(id); if (static_cast(value.getBitWidth()) != bit_width) { value = value.sextOrTrunc(bit_width); } return value; } // Returns the value for an ID adjusted to the bit width specified with // another integer ID. // // This simply looks up the width integer ID, and then calls the above // `GetAtWidth` overload using the value found for it. See that overload for // more details. auto GetAtWidth(IntId id, IntId bit_width_id) const -> llvm::APInt { const llvm::APInt bit_width = Get(bit_width_id); CARBON_CHECK( bit_width.isStrictlyPositive() && bit_width.isSignedIntN(MinAPWidth), "Invalid bit width value: {0}", bit_width); return GetAtWidth(id, bit_width.getSExtValue()); } // Accepts a signed `int64_t` and uses the mathematical signed integer value // of it as the integer value to lookup. Returns the canonical ID for that // value or returns `None` if not in the store. auto Lookup(int64_t value) const -> IntId { if (IntId id = TryMakeValue(value); id.has_value()) [[likely]] { return id; } // Fallback for larger values. return LookupLarge(value); } // Looks up the canonical ID for this signed integer value, or returns `None` // if not in the store. auto LookupSigned(llvm::APInt value) const -> IntId { if (IntId id = TryMakeSignedValue(value); id.has_value()) [[likely]] { return id; } // Fallback for larger values. return LookupSignedLarge(std::move(value)); } // Output a YAML description of this data structure. Note that this will only // include the integers that required storing, not those successfully embedded // into the ID space. auto OutputYaml() const -> Yaml::OutputMapping; auto values() const [[clang::lifetimebound]] -> auto { return values_.values(); } auto size() const -> size_t { return values_.size(); } // Collects the memory usage of the separately stored integers. auto CollectMemUsage(MemUsage& mem_usage, llvm::StringRef label) const -> void; private: friend struct Testing::IntStoreTestPeer; // Used for `values_`; tracked using `IntId`'s index range. struct APIntId : IdBase { static constexpr llvm::StringLiteral Label = "ap_int"; static const APIntId None; using IdBase::IdBase; }; static auto MakeIndexOrNone(int index) -> IntId { CARBON_DCHECK(index >= 0 && index <= IntId::NoneIndex); return IntId(IntId::ZeroIndexId - index); } // Tries to make a signed 64-bit integer into an embedded value in the ID, and // if unable to do that returns the `None` ID. static auto TryMakeValue(int64_t value) -> IntId { if (IntId::MinValue <= value && value <= IntId::MaxValue) { return IntId(value); } return IntId::None; } // Tries to make a signed APInt into an embedded value in the ID, and if // unable to do that returns the `None` ID. static auto TryMakeSignedValue(llvm::APInt value) -> IntId { if (value.sge(IntId::MinValue) && value.sle(IntId::MaxValue)) { return IntId(value.getSExtValue()); } return IntId::None; } // Tries to make an unsigned APInt into an embedded value in the ID, and if // unable to do that returns the `None` ID. static auto TryMakeUnsignedValue(llvm::APInt value) -> IntId { if (value.ule(IntId::MaxValue)) { return IntId(value.getZExtValue()); } return IntId::None; } // Canonicalize an incoming signed APInt to the correct bit width. static auto CanonicalizeSigned(llvm::APInt value) -> llvm::APInt; // Canonicalize an incoming unsigned APInt to the correct bit width. static auto CanonicalizeUnsigned(llvm::APInt value) -> llvm::APInt; // Helper functions for handling values that are large enough to require an // allocated `APInt` for storage. Creating or manipulating that storage is // only a few lines of code, but we move these out-of-line because the // generated code is big and harms performance for the non-`Large` common // case. auto AddLarge(int64_t value) -> IntId; auto AddSignedLarge(llvm::APInt value) -> IntId; auto AddUnsignedLarge(llvm::APInt value) -> IntId; auto LookupLarge(int64_t value) const -> IntId; auto LookupSignedLarge(llvm::APInt value) const -> IntId; // Stores values which don't fit in an IntId. These are always signed. CanonicalValueStore values_; }; inline constexpr IntStore::APIntId IntStore::APIntId::None( IntId::None.AsIndex()); } // namespace Carbon #endif // CARBON_TOOLCHAIN_BASE_INT_H_ ================================================ FILE: toolchain/base/int_test.cpp ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #include "toolchain/base/int.h" #include #include #include #include namespace Carbon::Testing { struct IntStoreTestPeer { static constexpr int MinAPWidth = IntStore::MinAPWidth; static constexpr int32_t MaxIdEmbeddedValue = IntId::MaxValue; static constexpr int32_t MinIdEmbeddedValue = IntId::MinValue; }; namespace { using ::testing::Eq; static constexpr int MinAPWidth = IntStoreTestPeer::MinAPWidth; static constexpr int32_t MaxIdEmbeddedValue = IntStoreTestPeer::MaxIdEmbeddedValue; static constexpr int32_t MinIdEmbeddedValue = IntStoreTestPeer::MinIdEmbeddedValue; TEST(IntStore, Basic) { IntStore ints; IntId id_0 = ints.Add(0); IntId id_1 = ints.Add(1); IntId id_2 = ints.Add(2); IntId id_42 = ints.Add(42); IntId id_n1 = ints.Add(-1); IntId id_n42 = ints.Add(-42); IntId id_nines = ints.Add(999'999'999'999); IntId id_max64 = ints.Add(std::numeric_limits::max()); IntId id_min64 = ints.Add(std::numeric_limits::min()); for (IntId id : {id_0, id_1, id_2, id_42, id_n1, id_n42, id_nines, id_max64, id_min64}) { ASSERT_TRUE(id.has_value()); } // Small values should be embedded. EXPECT_THAT(id_0.AsValue(), Eq(0)); EXPECT_THAT(id_1.AsValue(), Eq(1)); EXPECT_THAT(id_2.AsValue(), Eq(2)); EXPECT_THAT(id_42.AsValue(), Eq(42)); EXPECT_THAT(id_n1.AsValue(), Eq(-1)); EXPECT_THAT(id_n42.AsValue(), Eq(-42)); // Rest should be indices as they don't fit as embedded values. EXPECT_TRUE(!id_nines.is_embedded_value()); EXPECT_TRUE(id_nines.is_index()); EXPECT_TRUE(!id_max64.is_embedded_value()); EXPECT_TRUE(id_max64.is_index()); EXPECT_TRUE(!id_min64.is_embedded_value()); EXPECT_TRUE(id_min64.is_index()); // And round tripping all the way through the store should work. EXPECT_THAT(ints.Get(id_0), Eq(0)); EXPECT_THAT(ints.Get(id_1), Eq(1)); EXPECT_THAT(ints.Get(id_2), Eq(2)); EXPECT_THAT(ints.Get(id_42), Eq(42)); EXPECT_THAT(ints.Get(id_n1), Eq(-1)); EXPECT_THAT(ints.Get(id_n42), Eq(-42)); EXPECT_THAT(ints.Get(id_nines), Eq(999'999'999'999)); EXPECT_THAT(ints.Get(id_max64), Eq(std::numeric_limits::max())); EXPECT_THAT(ints.Get(id_min64), Eq(std::numeric_limits::min())); } // Helper struct to hold test values and the resulting IDs. struct APAndId { llvm::APInt ap; IntId id = IntId::None; }; TEST(IntStore, APSigned) { IntStore ints; llvm::APInt big_128_ap = llvm::APInt(128, 0x1234'abcd'1234'abcd, /*isSigned=*/true) * 0xabcd'0000; llvm::APInt max_embedded_ap(MinAPWidth, MaxIdEmbeddedValue, /*isSigned=*/true); llvm::APInt min_embedded_ap(MinAPWidth, MinIdEmbeddedValue, /*isSigned=*/true); APAndId ap_and_ids[] = { {.ap = llvm::APInt(MinAPWidth, 1, /*isSigned=*/true)}, {.ap = llvm::APInt(MinAPWidth, 2, /*isSigned=*/true)}, {.ap = llvm::APInt(MinAPWidth, 999'999'999'999, /*isSigned=*/true)}, {.ap = big_128_ap}, {.ap = -big_128_ap}, {.ap = big_128_ap.sext(512) * big_128_ap.sext(512) * big_128_ap.sext(512)}, {.ap = -big_128_ap.sext(512) * big_128_ap.sext(512) * big_128_ap.sext(512)}, {.ap = max_embedded_ap}, {.ap = max_embedded_ap + 1}, {.ap = min_embedded_ap}, {.ap = min_embedded_ap - 1}, }; for (auto& [ap, id] : ap_and_ids) { id = ints.AddSigned(ap); ASSERT_TRUE(id.has_value()) << ap; } for (const auto& [ap, id] : ap_and_ids) { // The sign extend here may be a no-op, but the original bit width is a // reliable one at which to do the comparison. EXPECT_THAT(ints.Get(id).sext(ap.getBitWidth()), Eq(ap)); } } TEST(IntStore, APUnsigned) { IntStore ints; llvm::APInt big_128_ap = llvm::APInt(128, 0xabcd'abcd'abcd'abcd) * 0xabcd'0000'abcd'0000; llvm::APInt max_embedded_ap(MinAPWidth, MaxIdEmbeddedValue); APAndId ap_and_ids[] = { {.ap = llvm::APInt(MinAPWidth, 1)}, {.ap = llvm::APInt(MinAPWidth, 2)}, {.ap = llvm::APInt(MinAPWidth, 999'999'999'999)}, {.ap = llvm::APInt(MinAPWidth, std::numeric_limits::max())}, {.ap = llvm::APInt(MinAPWidth + 1, std::numeric_limits::max()) + 1}, {.ap = big_128_ap}, {.ap = big_128_ap.zext(512) * big_128_ap.zext(512) * big_128_ap.zext(512)}, {.ap = max_embedded_ap}, {.ap = max_embedded_ap + 1}, }; for (auto& [ap, id] : ap_and_ids) { id = ints.AddUnsigned(ap); ASSERT_TRUE(id.has_value()) << ap; } for (const auto& [ap, id] : ap_and_ids) { auto stored_ap = ints.Get(id); // Pick a bit width wide enough to represent both whatever is returned and // the original value as a *signed* integer without any truncation. int width = std::max(stored_ap.getBitWidth(), ap.getBitWidth() + 1); // We sign extend the stored value and zero extend the original number. This // ensures that anything added as unsigned ends up stored as a positive // number even when sign extended. EXPECT_THAT(stored_ap.sext(width), Eq(ap.zext(width))); } } } // namespace } // namespace Carbon::Testing ================================================ FILE: toolchain/base/kind_switch.h ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #ifndef CARBON_TOOLCHAIN_BASE_KIND_SWITCH_H_ #define CARBON_TOOLCHAIN_BASE_KIND_SWITCH_H_ #include #include "llvm/ADT/STLExtras.h" #include "toolchain/base/for_each_macro.h" // This library provides switch-like behaviors for Carbon's kind-based types. // // An expected use case is to mix regular switch `case` statements and // `CARBON_KIND`. However, the `switch` must be defined using // `CARBON_KIND_SWITCH`. For example: // // CARBON_KIND_SWITCH(untyped_inst) { // case CARBON_KIND(SomeInstType inst): { // return inst.typed_field; // } // case CARBON_KIND_ANY(AnyKind, any_inst): { // return any_inst.typed_field; // } // // case OtherType1::Kind: // case OtherType2::Kind: // return value; // default: // return default_value; // } // // When used on kind-like types, this requires: // // - The type passed to `CARBON_KIND_SWITCH` has `.kind()` to switch on, and // `.As` for `CARBON_KIND` to cast to. // - Each type passed to `CARBON_KIND` (`CaseT` above) provides // `CaseT::Kind`, which is passed to the `case` keyword. // `CaseT::Kind::RawEnumType` is the type returned by `.kind()`. // - Each type passed to `CARBON_KIND_ANY` must have a macro of the form: // ``` // #define AnyKind_CARBON_KIND_ANY_EXPAND \ // CARBON_KIND_ANY_EXPAND_BEGIN() CARBON_KIND_ANY_EXPAND_CASE(Kind1) \ // CARBON_KIND_ANY_EXPAND_SEP() CARBON_KIND_ANY_EXPAND_CASE(Kind2) \ // ... // CARBON_KIND_ANY_EXPAND_SEP() CARBON_KIND_ANY_EXPAND_CASE(KindN) // ``` // Note the prefix `,` is required. // // When used with `std::variant` (e.g., `CARBON_KIND_SWITCH(variant_value)`), // members of the variant are passed to `CARBON_KIND`, instead of types that // have a `::Kind` member. // Produces a switch statement on value.kind(). #define CARBON_KIND_SWITCH(value) \ switch ( \ auto&& carbon_internal_kind_switch_value = value; \ ::Carbon::Internal::Kind::SwitchOn(carbon_internal_kind_switch_value)) // Produces a case-compatible block of code that also instantiates a local typed // variable. typed_variable_decl looks like `int i`; the `CARBON_KIND` value // will be cast to the provided type. // // Because of the dangling else, braces should always be used with a `case // CARBON_KIND`. Otherwise, only the first statement is going to see the // variable. Even if that sometimes works, it can lead to confusing issues when // statements are added, and `if`/`else` coding style already requires braces. #define CARBON_KIND(typed_variable_decl) \ CARBON_KIND_INTERNAL_CASE_VALUE(typed_variable_decl) \ : CARBON_KIND_INTERNAL_DECLARE(typed_variable_decl) // Macros for clients to add support for `Type_CARBON_KIND_ANY_EXPAND` (see // example above). Empty parameters are used to allow delaying macro expansion. #define CARBON_KIND_ANY_EXPAND_CASE(X) CARBON_KIND_INTERNAL_CASE_VALUE(X) #define CARBON_KIND_ANY_EXPAND_BEGIN() , #define CARBON_KIND_ANY_EXPAND_SEP() : case // Produces a case-compatible block of code that also instantiates a local typed // variable. Versus `CARBON_KIND(int i)`, note this requires a comma after the // type, as in `CARBON_KIND_ANY(AnyKind, i)`. #define CARBON_KIND_ANY(Type, variable_name) \ CARBON_KIND_ANY_INTERNAL_WITH_SUFFIX(Type variable_name, \ Type##_CARBON_KIND_ANY_EXPAND) // ----------------------------------------------------------------------------- // Internal implementation details follow. // ----------------------------------------------------------------------------- namespace Carbon::Internal::Kind { template constexpr bool IsStdVariantValue = false; template constexpr bool IsStdVariantValue> = true; template concept IsStdVariant = IsStdVariantValue>; #define CARBON_INTERNAL_KIND_IDENTIFIER(name) T##name // Turns a list of numbers into a list `T0, T1, ...`. #define CARBON_INTERNAL_KIND_IDENTIFIERS(...) \ CARBON_FOR_EACH(CARBON_INTERNAL_KIND_IDENTIFIER, CARBON_FOR_EACH_COMMA, \ __VA_ARGS__) #define CARBON_INTERNAL_KIND_TYPENAME(name) \ typename CARBON_INTERNAL_KIND_IDENTIFIER(name) // Turns a list of numbers into a list `typename T0, typename T1, ...`. #define CARBON_INTERNAL_KIND_TYPENAMES(...) \ CARBON_FOR_EACH(CARBON_INTERNAL_KIND_TYPENAME, CARBON_FOR_EACH_COMMA, \ __VA_ARGS__) #define CARBON_INTERNAL_KIND_ENUM_NAME(n) VariantType##n##NotHandledInSwitch // Turns a list of numbers into a list `VariantType0NotHandledInSwitch, ...`. #define CARBON_INTERNAL_KIND_TYPES_TO_ENUM_NAMES(...) \ CARBON_FOR_EACH(CARBON_INTERNAL_KIND_ENUM_NAME, CARBON_FOR_EACH_COMMA, \ __VA_ARGS__) // Turns a list of numbers into a set of template specializations of the // variable `EnumType EnumValue`, with each specialization having the Nth value // in the EnumType (as defined by CARBON_INTERNAL_KIND_TYPES_TO_ENUM_NAMES). #define CARBON_INTERNAL_KIND_TYPE_TO_ENUM_NAME(n) \ template <> \ constexpr EnumType EnumValue = \ EnumType::CARBON_INTERNAL_KIND_ENUM_NAME(n) // Used to provide a reason in the compiler error from `ValidCaseType`, which // will state that "T does not satisfy TypeFoundInVariant". template concept TypeFoundInVariant = false; // Used to cause a compler error, which will state that "ValidCaseType was not // satisfied" for T and std::variant<...>. template requires TypeFoundInVariant struct ValidCaseType; template struct StdVariantTypeMap; #define CARBON_INTERNAL_KIND_TYPE_MAP(...) \ template \ struct StdVariantTypeMap< \ std::variant> { \ /* An enum with a value for each type in the std::variant. The switch will \ * be on this enum so that we get a warning if one of the enum values is \ * not handled. They are named in a way to help explain the warning, that \ * it means a type in the std::variant<...> type list does not have a \ * matching case statement. \ */ \ enum class EnumType { \ CARBON_INTERNAL_KIND_TYPES_TO_ENUM_NAMES(__VA_ARGS__) \ }; \ /* A mapping from a single type in the std::variant<...> type list to a \ * value in the EnumType. This value is only used in the case a type is \ * queried which is not part of the type list, and ValidCaseType is used \ * to produce a diagnostic explaining the situation. */ \ template \ static constexpr EnumType EnumValue = ValidCaseType< \ Tn, std::variant>(); \ /**/ \ CARBON_FOR_EACH(CARBON_INTERNAL_KIND_TYPE_TO_ENUM_NAME, \ CARBON_FOR_EACH_SEMI, __VA_ARGS__); \ } template struct StdVariantTypeMap> { // The number here should match the number of arguments in the largest // `CARBON_INTERNAL_KIND_TYPE_MAP` invocation below. static_assert(sizeof...(Ts) <= 24, "CARBON_KIND_SWITCH supports std::variant with up to 24 types. " "Add more if needed."); }; // Generate StdVariantTypeMap specializations for each number of types in the // std::variant<...> type list. The numbers here represent which number is // printed in diagnostics stating a type in the variant has no matching case // statement. Duplicate numbers would create an error. CARBON_INTERNAL_KIND_TYPE_MAP(0); CARBON_INTERNAL_KIND_TYPE_MAP(0, 1); CARBON_INTERNAL_KIND_TYPE_MAP(0, 1, 2); CARBON_INTERNAL_KIND_TYPE_MAP(0, 1, 2, 3); CARBON_INTERNAL_KIND_TYPE_MAP(0, 1, 2, 3, 4); CARBON_INTERNAL_KIND_TYPE_MAP(0, 1, 2, 3, 4, 5); CARBON_INTERNAL_KIND_TYPE_MAP(0, 1, 2, 3, 4, 5, 6); CARBON_INTERNAL_KIND_TYPE_MAP(0, 1, 2, 3, 4, 5, 6, 7); CARBON_INTERNAL_KIND_TYPE_MAP(0, 1, 2, 3, 4, 5, 6, 7, 8); CARBON_INTERNAL_KIND_TYPE_MAP(0, 1, 2, 3, 4, 5, 6, 7, 8, 9); CARBON_INTERNAL_KIND_TYPE_MAP(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10); CARBON_INTERNAL_KIND_TYPE_MAP(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11); CARBON_INTERNAL_KIND_TYPE_MAP(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12); CARBON_INTERNAL_KIND_TYPE_MAP(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13); CARBON_INTERNAL_KIND_TYPE_MAP(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14); CARBON_INTERNAL_KIND_TYPE_MAP(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15); CARBON_INTERNAL_KIND_TYPE_MAP(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16); CARBON_INTERNAL_KIND_TYPE_MAP(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17); CARBON_INTERNAL_KIND_TYPE_MAP(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18); CARBON_INTERNAL_KIND_TYPE_MAP(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19); CARBON_INTERNAL_KIND_TYPE_MAP(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20); CARBON_INTERNAL_KIND_TYPE_MAP(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21); CARBON_INTERNAL_KIND_TYPE_MAP(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22); CARBON_INTERNAL_KIND_TYPE_MAP(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23); #undef CARBON_INTERNAL_KIND_IDENTIFIER #undef CARBON_INTERNAL_KIND_IDENTIFIERS #undef CARBON_INTERNAL_KIND_TYPENAME #undef CARBON_INTERNAL_KIND_TYPENAMES #undef CARBON_INTERNAL_KIND_ENUM_NAME #undef CARBON_INTERNAL_KIND_TYPES_TO_ENUM_NAMES #undef CARBON_INTERNAL_KIND_TYPE_TO_ENUM_NAME #undef CARBON_INTERNAL_KIND_TYPE_MAP // Uses the above `CARBON_INTERNAL_KIND_TYPE_MAP` expansions to make an enum // with a value for each type in a std::variant<...> type list. template using StdVariantEnum = StdVariantTypeMap>::EnumType; // Uses the `CARBON_INTERNAL_KIND_TYPE_MAP` expanstions to find the enum value // in `StdVariantEnum` for a given type `T` in the type list of a // std::variant<...>. template constexpr auto CaseValueOfTypeInStdVariant = StdVariantTypeMap>::template EnumValue; // Given `CARBON_KIND_SWITCH(value)` this returns the actual value to switch on. // // For types with a `kind()` accessor, this is the just the value of `kind()`. // The type returned from `kind()` is expected to be a `TypeEnum`, as it // is required to have its API, including a nested `RawEnumType`. // // For std::variant<...> this is an enum synthesized from the types in the // variant's type list. template constexpr auto SwitchOn(SwitchT&& switch_value) -> auto { if constexpr (IsStdVariant) { return static_cast>(switch_value.index()); } else { return switch_value.kind(); } } // Given `CARBON_KIND(CaseT name)` this generates the case value to compare // against the switch value from `SwitchOn`. // // For types with a `kind()` accessor that returns a `TypeEnum`, // this gets the `TypeEnum<...>::RawTypeEnum` for the case type `CaseT`. // // For std::variant<...> this returns the value corresponding to the case type // from the enum synthesized (in `SwitchOn`) for the types in the variant's // type list. template consteval auto ForCase() -> auto { using CaseT = llvm::function_traits::template arg_t<0>; if constexpr (IsStdVariant) { using NoRefCaseT = std::remove_cvref_t; return CaseValueOfTypeInStdVariant; } else { using KindT = llvm::function_traits< decltype(&std::remove_cvref_t::kind)>::result_t; return static_cast(KindT::template For); } } // Given `CARBON_KIND_SWITCH(value)` and `CARBON_KIND(CaseT name)` this converts // the `value` to `CaseT`. // // For types with a `kind()` accessor this uses `value.As`. // // For `std::variant<...>` this uses `std::get(value)`. template auto Cast(SwitchT&& kind_switch_value) -> decltype(auto) { using CaseT = llvm::function_traits::template arg_t<0>; if constexpr (IsStdVariant) { using NoRefCaseT = std::remove_cvref_t; return std::get(std::forward(kind_switch_value)); } else { return kind_switch_value.template As(); } } #define CARBON_INTERNAL_KIND_MERGE(Prefix, Line) Prefix##Line #define CARBON_INTERNAL_KIND_LABEL(Line) \ CARBON_INTERNAL_KIND_MERGE(carbon_internal_kind_case_, Line) // To extract the type from an argument, we wrap it in a lambda and will use // `function_traits` to extract the type. This supports `typed_param` either // being `Type name`, or just `Type`. #define CARBON_KIND_INTERNAL_WRAP_TYPE(typed_param) \ decltype([]([[maybe_unused]] typed_param) {}) // Produces the value for a `case` statement on the type of `typed_param`. #define CARBON_KIND_INTERNAL_CASE_VALUE(typed_param) \ ::Carbon::Internal::Kind::ForCase< \ decltype(carbon_internal_kind_switch_value), \ CARBON_KIND_INTERNAL_WRAP_TYPE(typed_param)>() // Produces a declaration using `typed_variable_decl`. // // This uses `if` to scope the variable, and provides a dangling `else` in order // to prevent accidental `else` use. The label allows `:` to follow the macro // name, making it look more like a typical `case`. #define CARBON_KIND_INTERNAL_DECLARE(typed_variable_decl) \ if (typed_variable_decl = \ ::Carbon::Internal::Kind::Cast( \ std::forward( \ carbon_internal_kind_switch_value)); \ false) { \ } else [[maybe_unused]] \ CARBON_INTERNAL_KIND_LABEL(__LINE__) // Helper for `CARBON_KIND_ANY` that expands the now-suffixed macro. #define CARBON_KIND_ANY_INTERNAL_WITH_SUFFIX(typed_variable_decl, \ Type_CARBON_KIND_ANY_EXPAND) \ CARBON_KIND_ANY_INTERNAL_IMPL(typed_variable_decl, \ Type_CARBON_KIND_ANY_EXPAND) // Helper for `CARBON_KIND_ANY` that forms the final case setup. The variadic // arguments are the expansion of `Type_CARBON_KIND_ANY_EXPAND`, which may // contain commas. // // As a consequence of the macro suffixing along with the required prefix comma // in `Type_CARBON_KIND_ANY_EXPAND`, input of `Namespace::Type` will have become // `Namespace::, Type_CARBON_KIND_ANY_EXPAND`, and `DiscardNamespace` exists to // discard `Namespace::`. #define CARBON_KIND_ANY_INTERNAL_IMPL(typed_variable_decl, DiscardNamespace, \ ...) \ __VA_ARGS__: \ CARBON_KIND_INTERNAL_DECLARE(typed_variable_decl) } // namespace Carbon::Internal::Kind #endif // CARBON_TOOLCHAIN_BASE_KIND_SWITCH_H_ ================================================ FILE: toolchain/base/kind_switch_test.cpp ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #include "toolchain/base/kind_switch.h" #include #include #include #include "common/raw_string_ostream.h" namespace Carbon { namespace { TEST(KindSwitch, Variant) { auto f = [](std::variant v) -> std::string { CARBON_KIND_SWITCH(v) { case CARBON_KIND(int n): { return llvm::formatv("int = {0}", n); } case CARBON_KIND(float f): { return llvm::formatv("float = {0}", f); } case CARBON_KIND(char c): { return llvm::formatv("char = {0}", c); } } }; EXPECT_EQ(f(int{1}), "int = 1"); EXPECT_EQ(f(float{2}), "float = 2.00"); EXPECT_EQ(f(char{'h'}), "char = h"); } TEST(KindSwitch, VariantUnusedValue) { auto f = [](std::variant v) -> std::string { CARBON_KIND_SWITCH(v) { case CARBON_KIND(int n): { return llvm::formatv("int = {0}", n); } case CARBON_KIND(float _): // The float value is not used, we see that using `_` works. return "float"; } }; EXPECT_EQ(f(int{1}), "int = 1"); EXPECT_EQ(f(float{2}), "float"); } } // namespace } // namespace Carbon ================================================ FILE: toolchain/base/llvm_tools.bzl ================================================ # Part of the Carbon Language project, under the Apache License v2.0 with LLVM # Exceptions. See /LICENSE for license information. # SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception """Provides variables and rules to automate working with LLVM's CLI tools.""" load("//bazel/cc_rules:defs.bzl", "cc_library") # The main LLVM command line tools, including their "primary" name, binary name, # and the library dependency required to use them. LLVM_MAIN_TOOLS = { "ar": struct(bin_name = "llvm-ar", lib = "@llvm-project//llvm:llvm-ar-lib"), "cgdata": struct(bin_name = "llvm-cgdata", lib = "@llvm-project//llvm:llvm-cgdata-lib"), "cxxfilt": struct(bin_name = "llvm-cxxfilt", lib = "@llvm-project//llvm:llvm-cxxfilt-lib"), "debuginfod-find": struct(bin_name = "llvm-debuginfod-find", lib = "@llvm-project//llvm:llvm-debuginfod-find-lib"), "dsymutil": struct(bin_name = "dsymutil", lib = "@llvm-project//llvm:dsymutil-lib"), "dwp": struct(bin_name = "llvm-dwp", lib = "@llvm-project//llvm:llvm-dwp-lib"), "gsymutil": struct(bin_name = "llvm-gsymutil", lib = "@llvm-project//llvm:llvm-gsymutil-lib"), "ifs": struct(bin_name = "llvm-ifs", lib = "@llvm-project//llvm:llvm-ifs-lib"), "libtool-darwin": struct(bin_name = "llvm-libtool-darwin", lib = "@llvm-project//llvm:llvm-libtool-darwin-lib"), "lipo": struct(bin_name = "llvm-lipo", lib = "@llvm-project//llvm:llvm-lipo-lib"), "ml": struct(bin_name = "llvm-ml", lib = "@llvm-project//llvm:llvm-ml-lib"), "mt": struct(bin_name = "llvm-mt", lib = "@llvm-project//llvm:llvm-mt-lib"), "nm": struct(bin_name = "llvm-nm", lib = "@llvm-project//llvm:llvm-nm-lib"), "objcopy": struct(bin_name = "llvm-objcopy", lib = "@llvm-project//llvm:llvm-objcopy-lib"), "objdump": struct(bin_name = "llvm-objdump", lib = "@llvm-project//llvm:llvm-objdump-lib"), "rc": struct(bin_name = "llvm-rc", lib = "@llvm-project//llvm:llvm-rc-lib"), "readobj": struct(bin_name = "llvm-readobj", lib = "@llvm-project//llvm:llvm-readobj-lib"), "sancov": struct(bin_name = "sancov", lib = "@llvm-project//llvm:sancov-lib"), "size": struct(bin_name = "llvm-size", lib = "@llvm-project//llvm:llvm-size-lib"), "symbolizer": struct(bin_name = "llvm-symbolizer", lib = "@llvm-project//llvm:llvm-symbolizer-lib"), } # A collection of additional alias names that should be available for the main # tools. The key is the main tool with the support for these names, followed by # a list of the aliased names. # # Note that we don't track separate binary names for the alias names as those # are always formed by prepending `llvm-` for the aliases. LLVM_TOOL_ALIASES = { "ar": ["ranlib", "lib", "dlltool"], "objcopy": ["bitcode-strip", "install-name-tool", "strip"], "objdump": ["otool"], "rc": ["windres"], "readobj": ["readelf"], "symbolizer": ["addr2line"], } _DEF_FILE_TEMPLATE = """ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // This is a generated X-macro header for defining LLVM tools. It does not use // `#include` guards, and instead is designed to be `#include`ed after the // x-macro is defined in order for its inclusion to expand to the desired // output. Macro definitions are cleaned up at the end of this file. // // Each X-macro takes four arguments: // - `Id` is the identifier-shaped PascalCased tool name. // - `Name` is a string literal of the tool name. // - `BinName` is a string literal of the binary name of the tool when installed // as a stand-alone command line tool. // - `MainFn` is the function symbol name used to run the tool as-if its `main`. // // There are three X-macros available: // - `CARBON_LLVM_TOOL` is available for every tool. // - `CARBON_LLVM_MAIN_TOOL` is available for each tool with a distinct // `MainFn` symbol name. // - `CARBON_LLVM_ALIAS_TOOL` is available for each tool that is an alias of // some other tool. It's `MainFn` will be the alias-target symbol name. // // See toolchain/driver/llvm_tools.bzl for more details. #ifndef CARBON_LLVM_TOOL #define CARBON_LLVM_TOOL(Id, Name, BinName, MainFn) #endif #ifndef CARBON_LLVM_MAIN_TOOL #define CARBON_LLVM_MAIN_TOOL(Id, Name, BinName, MainFn) \\ CARBON_LLVM_TOOL(Id, Name, BinName, MainFn) #endif #ifndef CARBON_LLVM_ALIAS_TOOL #define CARBON_LLVM_ALIAS_TOOL(Id, Name, BinName, MainFn) \\ CARBON_LLVM_TOOL(Id, Name, BinName, MainFn) #endif {} #undef CARBON_LLVM_TOOL #undef CARBON_LLVM_MAIN_TOOL #undef CARBON_LLVM_ALIAS_TOOL """ _DEF_MACRO_TEMPLATE = """ CARBON_LLVM_{kind}TOOL({id}, "{name}", "{bin_name}", {main_fn}) """.strip() def _build_def_macro(kind, name, bin_name, main_info): id = "".join([w.title() for w in name.split("-")]) main_fn = main_info.bin_name.replace("-", "_") + "_main" return _DEF_MACRO_TEMPLATE.format( kind = kind, id = id, name = name, bin_name = bin_name, main_fn = main_fn, ) def _generate_llvm_tools_def_rule(ctx): def_lines = [] for name, tool_info in LLVM_MAIN_TOOLS.items(): def_lines.append(_build_def_macro("MAIN_", name, tool_info.bin_name, tool_info)) for target, aliases in LLVM_TOOL_ALIASES.items(): tool_info = LLVM_MAIN_TOOLS[target] for alias in aliases: bin_name = "llvm-" + alias def_lines.append(_build_def_macro("ALIAS_", alias, bin_name, tool_info)) def_file = ctx.actions.declare_file(ctx.label.name) ctx.actions.write(def_file, _DEF_FILE_TEMPLATE.format("\n".join(def_lines))) return [DefaultInfo(files = depset([def_file]))] generate_llvm_tools_def_rule = rule( implementation = _generate_llvm_tools_def_rule, attrs = {}, ) def generate_llvm_tools_def(name, out, **kwargs): """Generates the LLVM tools `.def` file. This first generates the `.def` file into the `out` filename, and then synthesizes a `cc_library` rule exporting that file in its `textual_hdrs`. The `cc_library` rule name is the provided `name` and should be depended on by code that includes the generated file. The `kwargs` are expanded into the `cc_library` in case other attributes need to be configured there. The two-step process is necessary to avoid trying to compile or otherwise process the generated file as something other than a textual header. """ generate_llvm_tools_def_rule(name = out) cc_library( name = name, textual_hdrs = [out], **kwargs ) ================================================ FILE: toolchain/base/llvm_tools.cpp ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #include "toolchain/base/llvm_tools.h" #include "common/command_line.h" // NOLINTBEGIN(readability-identifier-naming): External library name. #define CARBON_LLVM_MAIN_TOOL(Identifier, Name, BinName, MainFn) \ extern auto MainFn(int argc, char** argv, const llvm::ToolContext& context) \ -> int; #include "toolchain/base/llvm_tools.def" // NOLINTEND(readability-identifier-naming): External library name. namespace Carbon { CARBON_DEFINE_ENUM_CLASS_NAMES(LLVMTool) { #define CARBON_LLVM_TOOL(Identifier, Name, BinName, MainFn) Name, #include "toolchain/base/llvm_tools.def" }; constexpr llvm::StringLiteral LLVMTool::BinNames[] = { #define CARBON_LLVM_TOOL(Identifier, Name, BinName, MainFn) BinName, #include "toolchain/base/llvm_tools.def" }; constexpr LLVMTool::MainFnT* LLVMTool::MainFns[] = { #define CARBON_LLVM_TOOL(Identifier, Name, BinName, MainFn) &::MainFn, #include "toolchain/base/llvm_tools.def" }; constexpr CommandLine::CommandInfo LLVMTool::SubcommandInfos[] = { #define CARBON_LLVM_TOOL(Identifier, Name, BinName, MainFn) \ {.name = Name, \ .help = "Runs the LLVM " Name \ " command line tool with the provided arguments."}, #include "toolchain/base/llvm_tools.def" }; } // namespace Carbon ================================================ FILE: toolchain/base/llvm_tools.h ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #ifndef CARBON_TOOLCHAIN_BASE_LLVM_TOOLS_H_ #define CARBON_TOOLCHAIN_BASE_LLVM_TOOLS_H_ #include #include "common/command_line.h" #include "common/enum_base.h" #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/StringRef.h" #include "llvm/Support/LLVMDriver.h" namespace Carbon { CARBON_DEFINE_RAW_ENUM_CLASS(LLVMTool, uint8_t) { #define CARBON_LLVM_TOOL(Identifier, Name, BinName, MainFn) \ CARBON_RAW_ENUM_ENUMERATOR(Identifier) #include "toolchain/base/llvm_tools.def" }; // An enum-like class for each of the LLVM tools. // // This can be used like an enum to track a specific one of the LLVM tools. It // also has a collection of methods to access various aspects of the tools // themselves, including the symbol used to invoke the given tool. // // The instances of this class are generated from `llvm_tools.bzl`, see that // file for more details. class LLVMTool : public CARBON_ENUM_BASE(LLVMTool) { public: #define CARBON_LLVM_TOOL(Identifier, Name, BinName, MainFn) \ CARBON_ENUM_CONSTANT_DECL(Identifier) #include "toolchain/base/llvm_tools.def" static const llvm::ArrayRef Tools; using MainFnT = auto(int argc, char** argv, const llvm::ToolContext& context) -> int; using EnumBase::EnumBase; auto bin_name() const -> llvm::StringLiteral { return BinNames[AsInt()]; } auto main_fn() const -> MainFnT* { return MainFns[AsInt()]; } auto subcommand_info() const -> const CommandLine::CommandInfo& { return SubcommandInfos[AsInt()]; } private: static const LLVMTool ToolsStorage[]; static const llvm::StringLiteral BinNames[]; static MainFnT* const MainFns[]; static const CommandLine::CommandInfo SubcommandInfos[]; }; #define CARBON_LLVM_TOOL(Identifier, Name, BinName, MainFn) \ CARBON_ENUM_CONSTANT_DEFINITION(LLVMTool, Identifier) #include "toolchain/base/llvm_tools.def" inline constexpr LLVMTool LLVMTool::ToolsStorage[] = { #define CARBON_LLVM_TOOL(Identifier, Name, BinName, MainFn) \ LLVMTool::Identifier, #include "toolchain/base/llvm_tools.def" }; inline constexpr llvm::ArrayRef LLVMTool::Tools = ToolsStorage; } // namespace Carbon #endif // CARBON_TOOLCHAIN_BASE_LLVM_TOOLS_H_ ================================================ FILE: toolchain/base/mem_usage.h ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #ifndef CARBON_TOOLCHAIN_BASE_MEM_USAGE_H_ #define CARBON_TOOLCHAIN_BASE_MEM_USAGE_H_ #include #include "common/map.h" #include "common/set.h" #include "llvm/ADT/STLFunctionalExtras.h" #include "llvm/ADT/SmallVector.h" #include "llvm/ADT/StringRef.h" #include "llvm/Support/FormatVariadic.h" #include "toolchain/base/yaml.h" namespace Carbon { // Helps track memory usage for a compile. // // Users will mix `Add` and `Collect` calls, using `ConcatLabel` to label // allocation sources. Typically we'll collect stats for growable, potentially // large data types (such as `SmallVector`), ignoring small fixed-size members // (such as pointers or `int32_t`). // // For example: // // auto CollectMemUsage(MemUsage& mem_usage, llvm::StringRef label) const // -> void { // // Explicit tracking. // mem_usage.Add(MemUsage::ConcatLabel(label, "data_"), data_.used_bytes(), // data_.reserved_bytes()); // // Common library types like `Map` and `llvm::SmallVector` have // // type-specific support. // mem_usage.Add(MemUsage::Concat(label, "array_"), array_); // // Implementing `CollectMemUsage` allows use with the same interface. // mem_usage.Collect(MemUsage::Concat(label, "obj_"), obj_); // } class MemUsage { public: // Adds tracking for used and reserved bytes, paired with the given label. auto Add(std::string label, int64_t used_bytes, int64_t reserved_bytes) -> void { mem_usage_.push_back({.label = std::move(label), .used_bytes = used_bytes, .reserved_bytes = reserved_bytes}); } // Adds memory usage for a `llvm::BumpPtrAllocator`. auto Collect(std::string label, const llvm::BumpPtrAllocator& allocator) -> void { Add(std::move(label), allocator.getBytesAllocated(), allocator.getTotalMemory()); } // Adds memory usage for a `Map`. template auto Collect(std::string label, const Map& map, KeyContextT key_context = KeyContextT()) -> void { // These don't track used bytes, so we set the same value for used and // reserved bytes. auto bytes = map.ComputeMetrics(key_context).storage_bytes; Add(std::move(label), bytes, bytes); } // Adds memory usage for a `Set`. template auto Collect(std::string label, const Set& set, KeyContextT key_context = KeyContextT()) -> void { // These don't track used bytes, so we set the same value for used and // reserved bytes. auto bytes = set.ComputeMetrics(key_context).storage_bytes; Add(std::move(label), bytes, bytes); } // Adds memory usage of an array's data. This ignores the possible overhead of // a SmallVector's in-place storage; if it's used, it's going to be tiny // relative to scaling memory costs. // // This uses SmallVector in order to get proper inference for T, which // ArrayRef misses. template auto Collect(std::string label, const llvm::SmallVectorImpl& array) -> void { Add(std::move(label), array.size_in_bytes(), array.capacity_in_bytes()); } // Adds memory usage for an object that provides `CollectMemUsage`. // // The expected signature of `CollectMemUsage` is above, in MemUsage class // comments. template requires requires(MemUsage& mem_usage, llvm::StringRef label, const T& arg) { { arg.CollectMemUsage(mem_usage, label) }; } auto Collect(std::string label, const T& arg) -> void { arg.CollectMemUsage(*this, label); } // Constructs a label for memory usage, handling the `.` concatenation. // We don't expect much depth in labels per-call. static auto ConcatLabel(llvm::StringRef label, llvm::StringRef child_label) -> std::string { return llvm::formatv("{0}.{1}", label, child_label); } static auto ConcatLabel(llvm::StringRef label, llvm::StringRef child_label1, llvm::StringRef child_label2) -> std::string { return llvm::formatv("{0}.{1}.{2}", label, child_label1, child_label2); } auto OutputYaml(llvm::StringRef filename) const -> Yaml::OutputMapping { // Explicitly copy the filename. return Yaml::OutputMapping([&, filename](Yaml::OutputMapping::Map map) { map.Add("filename", filename); int64_t total_used = 0; int64_t total_reserved = 0; for (const auto& entry : mem_usage_) { total_used += entry.used_bytes; total_reserved += entry.reserved_bytes; map.Add(entry.label, Yaml::OutputMapping([&](Yaml::OutputMapping::Map byte_map) { byte_map.Add("used_bytes", entry.used_bytes); byte_map.Add("reserved_bytes", entry.reserved_bytes); })); } map.Add("Total", Yaml::OutputMapping([&](Yaml::OutputMapping::Map byte_map) { byte_map.Add("used_bytes", total_used); byte_map.Add("reserved_bytes", total_reserved); })); }); } private: // Memory usage for a specific label. struct Entry { std::string label; int64_t used_bytes; int64_t reserved_bytes; }; // The accumulated data on memory usage. llvm::SmallVector mem_usage_; }; } // namespace Carbon #endif // CARBON_TOOLCHAIN_BASE_MEM_USAGE_H_ ================================================ FILE: toolchain/base/relational_value_store.h ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #ifndef CARBON_TOOLCHAIN_BASE_RELATIONAL_VALUE_STORE_H_ #define CARBON_TOOLCHAIN_BASE_RELATIONAL_VALUE_STORE_H_ #include #include "common/check.h" #include "toolchain/base/value_store.h" #include "toolchain/base/value_store_types.h" namespace Carbon { // A ValueStore that builds a 1:1 relationship between two IDs. // * `RelatedStoreT` represents a related ValueStore with ids that can be used // to find values in this store. // * `IdT` is the actual ID of values in this store, and `IdT::ValueType` is the // value type being stored. // // The value store builds a mapping so that either ID can be used later to find // a value. And the user can query if a related `RelatedStoreT::IdType` has been // used to add a value to the store or not. // // When adding to the store, the user provides the related // `RelatedStoreT::IdType` along with the value being stored, and gets back the // ID of the value in the store. // // This store requires more storage space than normal ValueStore does, as it // requires storing a bit for presence of each `RelatedStore::IdType`. And it // allocates memory for values for all IDs up largest ID present in the store, // even if they are not yet used. template class RelationalValueStore { public: using RelatedIdType = RelatedStoreT::IdType; using RelatedIdTagType = RelatedStoreT::IdTagType; using RelatedTagIdType = RelatedIdTagType::TagIdType; using ValueType = ValueStoreTypes::ValueType; using ConstRefType = ValueStoreTypes::ConstRefType; explicit RelationalValueStore(const RelatedStoreT* related_store) : values_(related_store->GetIdTag()), related_store_(related_store) {} // Given the related ID and a value, stores the value and returns a mapped ID // to reference it in the store. auto Add(RelatedIdType related_id, ValueType value) -> IdT { auto related_index = related_store_->GetRawIndex(related_id); values_.Resize(related_index + 1, std::nullopt); auto& opt = values_.Get(related_id); CARBON_CHECK(!opt.has_value(), "Add with `related_id` that was already added to the store"); opt.emplace(std::move(value)); return IdT(related_store_->GetIdTag().Apply(related_index).index); } // Returns the ID of a value in the store if the `related_id` was previously // used to add a value to the store, or None. auto TryGetId(RelatedIdType related_id) const -> IdT { auto related_index = related_store_->GetRawIndex(related_id); if (static_cast(related_index) >= values_.size()) { return IdT::None; } auto& opt = values_.Get(related_id); if (!opt.has_value()) { return IdT::None; } return IdT(related_store_->GetIdTag().Apply(related_index).index); } // Returns a value for an ID. auto Get(IdT id) const -> ConstRefType { return *values_.Get(RelatedIdType(id.index)); } private: ValueStore, Tag> values_; const RelatedStoreT* related_store_; }; } // namespace Carbon #endif // CARBON_TOOLCHAIN_BASE_RELATIONAL_VALUE_STORE_H_ ================================================ FILE: toolchain/base/runtimes_build_info.bzl ================================================ # Part of the Carbon Language project, under the Apache License v2.0 with LLVM # Exceptions. See /LICENSE for license information. # SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception """Provides variables and rules to work with Clang's runtime library sources. These are organized into groups based on the Clang runtimes providing them and how they are built: - CRT: The C language runtimes not provided by the C standard library, currently just infrastructure for global initialization and teardown. - Builtins: The compiler builtins library mirroring `libgcc` that provides function definitions for operations not reliably available in hardware but needed by Clang. - Libc++ and libc++abi: The C++ standard library and its ABI components. - Libunwind: The unwinding library. Future runtimes we plan to add support for but not yet included: - Sanitizers - Profiling runtimes """ load("@llvm-project//:vars.bzl", "LLVM_VERSION_MAJOR") load("@llvm-project//compiler-rt:compiler-rt.bzl", "builtins_copts", "crt_copts") load("@llvm-project//libcxx:libcxx_library.bzl", "libcxx_and_abi_copts") load("@llvm-project//libunwind:libunwind_library.bzl", "libunwind_copts") load("//bazel/cc_rules:defs.bzl", "cc_library") CRT_FILES = { "crtbegin_src": "@llvm-project//compiler-rt:builtins_crtbegin_src", "crtend_src": "@llvm-project//compiler-rt:builtins_crtend_src", } BUILTINS_SRCS_FILEGROUPS = [ "@llvm-project//compiler-rt:builtins_aarch64_srcs", "@llvm-project//compiler-rt:builtins_i386_srcs", "@llvm-project//compiler-rt:builtins_x86_64_srcs", ] BUILTINS_TEXTUAL_SRCS_FILEGROUPS = [ "@llvm-project//compiler-rt:builtins_aarch64_textual_srcs", "@llvm-project//compiler-rt:builtins_i386_textual_srcs", "@llvm-project//compiler-rt:builtins_x86_64_textual_srcs", ] RUNTIMES_HDRS_FILEGROUPS = [ "@llvm-project//libc:libcxx_shared_headers_hdrs", "@llvm-project//libcxx:libcxx_hdrs", "@llvm-project//libcxxabi:libcxxabi_hdrs", "@llvm-project//libunwind:libunwind_hdrs", ] RUNTIMES_SRCS_FILEGROUPS = [ "@llvm-project//libcxx:libcxx_linux_srcs", "@llvm-project//libcxx:libcxx_macos_srcs", "@llvm-project//libcxx:libcxx_win32_srcs", "@llvm-project//libcxxabi:libcxxabi_srcs", "@llvm-project//libunwind:libunwind_srcs", ] RUNTIMES_TEXTUAL_SRCS_FILEGROUPS = [ "@llvm-project//libcxxabi:libcxxabi_textual_srcs", ] RUNTIMES_PREFIXES = { "libcxx_hdrs": "libcxx/", "libcxx_linux_srcs": "libcxx/", "libcxx_macos_srcs": "libcxx/", "libcxx_shared_headers_hdrs": "libc/internal/", "libcxx_win32_srcs": "libcxx/", "libcxxabi_hdrs": "libcxxabi/", "libcxxabi_srcs": "libcxxabi/", "libcxxabi_textual_srcs": "libcxxabi/", "libunwind_hdrs": "libunwind/", "libunwind_srcs": "libunwind/", } def _get_name(target): return target.split(":")[1] def _format_one_per_line(list): return "\n" + "\n".join([ ' "{0}",'.format(item) for item in list ]) + "\n" def _builtins_path(file): """Returns the install path for a file in CompilerRT's builtins library.""" path = file.path # Skip to the relative path below the workspace root. path = path.rpartition(file.owner.workspace_root + "/")[2] # And now we can predictably remove the `compiler-rt/lib` prefix. path = path.removeprefix("compiler-rt/lib/") if not path.startswith("builtins/"): fail("Not a builtins-relative path for: {0}".format(file.path)) return path def _runtimes_path(file): """Returns the install path for a file in a normal runtimes library.""" return file.owner.name def _get_path(file_attr, to_path_fn): files = file_attr[DefaultInfo].files.to_list() if len(files) > 1: fail("Expected a single file and got {0} files.".format(len(files))) return '"{0}"'.format(to_path_fn(files[0])) def _get_paths(files_attr, to_path_fn, prefix = ""): files = [] for src in files_attr: files.extend(src[DefaultInfo].files.to_list()) files.extend(src[DefaultInfo].default_runfiles.files.to_list()) return _format_one_per_line([ "{0}{1}".format(prefix, to_path_fn(f)) for f in files ]) def _get_substitutions(ctx): key_attr = lambda k: getattr(ctx.attr, "_" + k) return { "BUILTINS_COPTS": _format_one_per_line(builtins_copts), "CRT_COPTS": _format_one_per_line(crt_copts), "LIBCXX_AND_ABI_COPTS": _format_one_per_line(libcxx_and_abi_copts), "LIBUNWIND_COPTS": _format_one_per_line(libunwind_copts), "LLVM_VERSION_MAJOR": LLVM_VERSION_MAJOR, } | { k.upper(): _get_path(key_attr(k), _builtins_path) for k in CRT_FILES.keys() } | { name.upper(): _get_paths(key_attr(name), _builtins_path) for name in [_get_name(g) for g in ( BUILTINS_SRCS_FILEGROUPS + BUILTINS_TEXTUAL_SRCS_FILEGROUPS )] } | { # Other runtimes are installed under separate directories named the # same as their key. name.upper(): _get_paths( key_attr(name), _runtimes_path, RUNTIMES_PREFIXES[name], ) for name in [_get_name(g) for g in ( RUNTIMES_HDRS_FILEGROUPS + RUNTIMES_SRCS_FILEGROUPS + RUNTIMES_TEXTUAL_SRCS_FILEGROUPS )] } _common_runtimes_rule_attrs = { "_" + k: attr.label(default = v, allow_single_file = True) for k, v in CRT_FILES.items() } | { "_" + _get_name(g): attr.label_list(default = [g], allow_files = True) for g in ( BUILTINS_SRCS_FILEGROUPS + BUILTINS_TEXTUAL_SRCS_FILEGROUPS + RUNTIMES_HDRS_FILEGROUPS + RUNTIMES_SRCS_FILEGROUPS + RUNTIMES_TEXTUAL_SRCS_FILEGROUPS ) } def _generate_runtimes_build_info_h_rule(ctx): h_file = ctx.actions.declare_file(ctx.label.name) ctx.actions.expand_template( template = ctx.file._template_file, output = h_file, substitutions = _get_substitutions(ctx), ) return [DefaultInfo(files = depset([h_file]))] generate_runtimes_build_info_h = rule( implementation = _generate_runtimes_build_info_h_rule, attrs = _common_runtimes_rule_attrs | { "_template_file": attr.label( default = "runtimes_build_info.tpl.h", allow_single_file = True, ), }, ) def generate_runtimes_build_info_cc_library(name, deps = [], **kwargs): """Generates a `runtimes_build_info.h` header and a `cc_library` rule. This first generates the header file with variables describing the runtimes build info from Clang, and then a `cc_library` that exports that header. The `cc_library` rule name is the provided `name` and should be depended on by code that includes the generated header. The `kwargs` are expanded into the `cc_library` in case other attributes need to be configured there. """ generate_runtimes_build_info_h(name = "runtimes_build_info.h") cc_library( name = name, hdrs = ["runtimes_build_info.h"], deps = [ # For StringRef.h "@llvm-project//llvm:Support", ] + deps, **kwargs ) def _generate_runtimes_build_vars_rule(ctx): file = ctx.actions.declare_file(ctx.label.name) ctx.actions.expand_template( template = ctx.file._template_file, output = file, substitutions = _get_substitutions(ctx), ) return [DefaultInfo(files = depset([file]))] generate_runtimes_build_vars = rule( implementation = _generate_runtimes_build_vars_rule, attrs = _common_runtimes_rule_attrs | { "_template_file": attr.label( default = "runtimes_build_vars.tpl.bzl", allow_single_file = True, ), }, ) ================================================ FILE: toolchain/base/runtimes_build_info.tpl.h ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // Header file template expanded with strings describing build info for Carbon's // runtimes. // // See toolchain/base/runtimes_build_info.bzl for more details. #ifndef CARBON_TOOLCHAIN_BASE_RUNTIMES_BUILD_INFO_TPL_H_ #define CARBON_TOOLCHAIN_BASE_RUNTIMES_BUILD_INFO_TPL_H_ #include "llvm/ADT/StringRef.h" namespace Carbon::RuntimesBuildInfo { inline constexpr llvm::StringLiteral CrtBegin = CRTBEGIN_SRC; inline constexpr llvm::StringLiteral CrtEnd = CRTEND_SRC; inline constexpr llvm::StringLiteral CrtCopts[] = {CRT_COPTS}; // Prevent wrapping these lines -- the expansion of the variables will add line // breaks. // // clang-format off inline constexpr llvm::StringLiteral BuiltinsAarch64Srcs[] = {BUILTINS_AARCH64_SRCS}; // NOLINTNEXTLINE(readability-identifier-naming) inline constexpr llvm::StringLiteral BuiltinsX86_64Srcs[] = {BUILTINS_X86_64_SRCS}; inline constexpr llvm::StringLiteral BuiltinsI386Srcs[] = {BUILTINS_I386_SRCS}; // clang-format on inline constexpr llvm::StringLiteral BuiltinsCopts[] = {BUILTINS_COPTS}; inline constexpr llvm::StringLiteral LibcxxLinuxSrcs[] = {LIBCXX_LINUX_SRCS}; inline constexpr llvm::StringLiteral LibcxxMacosSrcs[] = {LIBCXX_MACOS_SRCS}; inline constexpr llvm::StringLiteral LibcxxWin32Srcs[] = {LIBCXX_WIN32_SRCS}; inline constexpr llvm::StringLiteral LibcxxabiSrcs[] = {LIBCXXABI_SRCS}; inline constexpr llvm::StringLiteral LibcxxCopts[] = {LIBCXX_AND_ABI_COPTS}; inline constexpr llvm::StringLiteral LibunwindSrcs[] = {LIBUNWIND_SRCS}; inline constexpr llvm::StringLiteral LibunwindCopts[] = {LIBUNWIND_COPTS}; } // namespace Carbon::RuntimesBuildInfo #endif // CARBON_TOOLCHAIN_BASE_RUNTIMES_BUILD_INFO_TPL_H_ ================================================ FILE: toolchain/base/runtimes_build_vars.tpl.bzl ================================================ # Part of the Carbon Language project, under the Apache License v2.0 with LLVM # Exceptions. See /LICENSE for license information. # SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception """A Starlark file exporting Carbon toolchain runtimes build info variables. This file is a template that is expanded into a starlark file for the installed Carbon toolchain that provides a trivial textual definition of the relevant build info in variables. """ llvm_version_major = LLVM_VERSION_MAJOR crtbegin_src = CRTBEGIN_SRC crtend_src = CRTEND_SRC crt_copts = [CRT_COPTS] builtins_aarch64_srcs = [BUILTINS_AARCH64_SRCS] builtins_x86_64_srcs = [BUILTINS_X86_64_SRCS] builtins_i386_srcs = [BUILTINS_I386_SRCS] builtins_aarch64_textual_srcs = [BUILTINS_AARCH64_TEXTUAL_SRCS] builtins_x86_64_textual_srcs = [BUILTINS_X86_64_TEXTUAL_SRCS] builtins_i386_textual_srcs = [BUILTINS_I386_TEXTUAL_SRCS] builtins_copts = [BUILTINS_COPTS] libcxx_hdrs = [LIBCXX_HDRS] libcxx_linux_srcs = [LIBCXX_LINUX_SRCS] libcxx_macos_srcs = [LIBCXX_MACOS_SRCS] libcxx_win32_srcs = [LIBCXX_WIN32_SRCS] libc_internal_libcxx_hdrs = [LIBCXX_SHARED_HEADERS_HDRS] libcxxabi_hdrs = [LIBCXXABI_HDRS] libcxxabi_srcs = [LIBCXXABI_SRCS] libcxxabi_textual_srcs = [LIBCXXABI_TEXTUAL_SRCS] libcxx_copts = [LIBCXX_AND_ABI_COPTS] libunwind_hdrs = [LIBUNWIND_HDRS] libunwind_srcs = [LIBUNWIND_SRCS] libunwind_copts = [LIBUNWIND_COPTS] ================================================ FILE: toolchain/base/shared_value_stores.h ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #ifndef CARBON_TOOLCHAIN_BASE_SHARED_VALUE_STORES_H_ #define CARBON_TOOLCHAIN_BASE_SHARED_VALUE_STORES_H_ #include "llvm/ADT/APFloat.h" #include "llvm/ADT/StringRef.h" #include "toolchain/base/canonical_value_store.h" #include "toolchain/base/int.h" #include "toolchain/base/mem_usage.h" #include "toolchain/base/value_ids.h" #include "toolchain/base/value_store.h" #include "toolchain/base/yaml.h" namespace Carbon { // Stores that will be used across compiler phases for a given compilation unit. // This is provided mainly so that they don't need to be passed separately. class SharedValueStores : public Yaml::Printable { public: // Provide types that can be used by APIs to forward access to these stores. using IntStore = IntStore; using RealStore = ValueStore; using FloatStore = CanonicalValueStore; using IdentifierStore = CanonicalValueStore; using StringLiteralStore = CanonicalValueStore; explicit SharedValueStores() = default; // Not copyable or movable. SharedValueStores(const SharedValueStores&) = delete; auto operator=(const SharedValueStores&) -> SharedValueStores& = delete; auto identifiers() -> IdentifierStore& { return identifiers_; } auto identifiers() const -> const IdentifierStore& { return identifiers_; } auto ints() -> IntStore& { return ints_; } auto ints() const -> const IntStore& { return ints_; } auto reals() -> RealStore& { return reals_; } auto reals() const -> const RealStore& { return reals_; } auto floats() -> FloatStore& { return floats_; } auto floats() const -> const FloatStore& { return floats_; } auto string_literal_values() -> StringLiteralStore& { return string_literals_; } auto string_literal_values() const -> const StringLiteralStore& { return string_literals_; } auto OutputYaml(std::optional filename = std::nullopt) const -> Yaml::OutputMapping { return Yaml::OutputMapping([&, filename](Yaml::OutputMapping::Map map) { if (filename) { map.Add("filename", *filename); } map.Add("shared_values", Yaml::OutputMapping([&](Yaml::OutputMapping::Map map) { map.Add("ints", ints_.OutputYaml()); map.Add("reals", reals_.OutputYaml()); map.Add("floats", floats_.OutputYaml()); map.Add("identifiers", identifiers_.OutputYaml()); map.Add("strings", string_literals_.OutputYaml()); })); }); } // Collects memory usage for the various shared stores. auto CollectMemUsage(MemUsage& mem_usage, llvm::StringRef label) const -> void { mem_usage.Collect(MemUsage::ConcatLabel(label, "ints_"), ints_); mem_usage.Collect(MemUsage::ConcatLabel(label, "reals_"), reals_); mem_usage.Collect(MemUsage::ConcatLabel(label, "floats_"), floats_); mem_usage.Collect(MemUsage::ConcatLabel(label, "identifiers_"), identifiers_); mem_usage.Collect(MemUsage::ConcatLabel(label, "string_literals_"), string_literals_); } private: IntStore ints_; RealStore reals_; FloatStore floats_; IdentifierStore identifiers_; StringLiteralStore string_literals_; }; } // namespace Carbon #endif // CARBON_TOOLCHAIN_BASE_SHARED_VALUE_STORES_H_ ================================================ FILE: toolchain/base/shared_value_stores_test.cpp ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #include "toolchain/base/shared_value_stores.h" #include #include #include "common/raw_string_ostream.h" #include "toolchain/testing/yaml_test_helpers.h" namespace Carbon::Testing { namespace { using ::testing::ElementsAre; using ::testing::IsEmpty; using ::testing::Pair; auto MatchSharedValues(testing::Matcher ints, testing::Matcher reals, testing::Matcher floats, testing::Matcher identifiers, testing::Matcher strings) -> auto { return Yaml::IsYaml(Yaml::Sequence(ElementsAre(Yaml::Mapping(ElementsAre(Pair( "shared_values", Yaml::Mapping(ElementsAre(Pair("ints", Yaml::Mapping(ints)), Pair("reals", Yaml::Mapping(reals)), Pair("floats", Yaml::Mapping(floats)), Pair("identifiers", Yaml::Mapping(identifiers)), Pair("strings", Yaml::Mapping(strings)))))))))); } TEST(SharedValueStores, PrintEmpty) { SharedValueStores value_stores; RawStringOstream out; value_stores.Print(out); EXPECT_THAT( Yaml::Value::FromText(out.TakeStr()), MatchSharedValues(IsEmpty(), IsEmpty(), IsEmpty(), IsEmpty(), IsEmpty())); } TEST(SharedValueStores, PrintVals) { SharedValueStores value_stores; llvm::APInt apint(64, 8, /*isSigned=*/true); value_stores.ints().AddSigned(apint); value_stores.ints().AddSigned(llvm::APInt(64, 999'999'999'999)); value_stores.reals().Add( Real{.mantissa = apint, .exponent = apint, .is_decimal = true}); value_stores.identifiers().Add("a"); value_stores.string_literal_values().Add("foo'\"baz"); RawStringOstream out; value_stores.Print(out); EXPECT_THAT(Yaml::Value::FromText(out.TakeStr()), MatchSharedValues( ElementsAre(Pair("ap_int0", Yaml::Scalar("999999999999"))), ElementsAre(Pair("real0", Yaml::Scalar("8*10^8"))), IsEmpty(), ElementsAre(Pair("identifier0", Yaml::Scalar("a"))), ElementsAre(Pair("string0", Yaml::Scalar("foo'\"baz"))))); } } // namespace } // namespace Carbon::Testing ================================================ FILE: toolchain/base/test_binary.cpp ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception auto main() -> int { return 0; } ================================================ FILE: toolchain/base/timings.h ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #ifndef CARBON_TOOLCHAIN_BASE_TIMINGS_H_ #define CARBON_TOOLCHAIN_BASE_TIMINGS_H_ #include #include "llvm/ADT/SmallVector.h" #include "llvm/ADT/StringRef.h" #include "toolchain/base/yaml.h" namespace Carbon { // Helps track timings for a compile. class Timings { public: // Records a timing for a scope, if the timings object is present. class ScopedTiming { public: // The `timings` may be null, in which case the `ScopedTiming` is a no-op. explicit ScopedTiming(Timings* timings, llvm::StringRef label) : timings_(timings), label_(label), start_(timings ? std::chrono::steady_clock::now() : std::chrono::steady_clock::time_point::min()) {} ~ScopedTiming() { if (timings_) { timings_->Add(label_, std::chrono::steady_clock::now() - start_); } } private: Timings* timings_; llvm::StringRef label_; std::chrono::steady_clock::time_point start_; }; // Adds tracking for nanoseconds, paired with the given label. auto Add(llvm::StringRef label, std::chrono::nanoseconds nanoseconds) -> void { timings_.push_back( {.label = std::string(label), .nanoseconds = nanoseconds}); } auto OutputYaml(llvm::StringRef filename) const -> Yaml::OutputMapping { // Explicitly copy the filename. return Yaml::OutputMapping([&, filename](Yaml::OutputMapping::Map map) { map.Add("filename", filename); map.Add("nanoseconds", Yaml::OutputMapping([&](Yaml::OutputMapping::Map label_map) { std::chrono::nanoseconds total_nanoseconds(0); for (const auto& entry : timings_) { total_nanoseconds += entry.nanoseconds; label_map.Add(entry.label, static_cast( entry.nanoseconds.count())); } label_map.Add("Total", static_cast(total_nanoseconds.count())); })); }); } private: // Timing for a specific label. struct Entry { std::string label; std::chrono::nanoseconds nanoseconds; }; // The accumulated data on timings. llvm::SmallVector timings_; }; } // namespace Carbon #endif // CARBON_TOOLCHAIN_BASE_TIMINGS_H_ ================================================ FILE: toolchain/base/value_ids.h ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #ifndef CARBON_TOOLCHAIN_BASE_VALUE_IDS_H_ #define CARBON_TOOLCHAIN_BASE_VALUE_IDS_H_ #include "common/check.h" #include "common/ostream.h" #include "llvm/ADT/APInt.h" #include "llvm/ADT/StringExtras.h" #include "llvm/Support/YAMLParser.h" #include "toolchain/base/index_base.h" namespace Carbon { // The value of a real literal token. // // This is either a dyadic fraction (mantissa * 2^exponent) or a decadic // fraction (mantissa * 10^exponent). // // These values are not canonicalized, because we don't expect them to repeat. // We use RealIds in SemIR::FloatLiteralValues, and this results in all real // literals being distinct constants, even if they represent the same value. // TODO: Address this by using a different representation in SemIR. struct Real : public Printable { auto Print(llvm::raw_ostream& output_stream) const -> void { mantissa.print(output_stream, /*isSigned=*/false); output_stream << "*" << (is_decimal ? "10" : "2") << "^" << exponent; } // The mantissa, represented as an unsigned integer. llvm::APInt mantissa; // The exponent, represented as a signed integer. llvm::APInt exponent; // If false, the value is mantissa * 2^exponent. // If true, the value is mantissa * 10^exponent. // TODO: This field increases Real from 32 bytes to 40 bytes. Consider // changing how it's tracked for space savings. bool is_decimal; }; // Corresponds to a float value represented by an APFloat. This is used for // floating-point values in SemIR. struct FloatId : public IdBase { static constexpr llvm::StringLiteral Label = "float"; static const FloatId None; using IdBase::IdBase; }; inline constexpr FloatId FloatId::None(FloatId::NoneIndex); // Corresponds to a Real value. struct RealId : public IdBase { // TODO: We don't use Diagnostics::TypeInfo here for layering reasons. struct DiagnosticType { using StorageType = std::string; }; static constexpr llvm::StringLiteral Label = "real"; static const RealId None; using IdBase::IdBase; }; inline constexpr RealId RealId::None(RealId::NoneIndex); // Corresponds to StringRefs for identifiers. // // `NameId` relies on the values of this type other than `None` all being // non-negative. struct IdentifierId : public IdBase { static constexpr llvm::StringLiteral Label = "identifier"; static const IdentifierId None; using IdBase::IdBase; }; inline constexpr IdentifierId IdentifierId::None(IdentifierId::NoneIndex); // The name of a package, which is either an identifier or the special `Core` // package name. // // TODO: Consider also treating `Main` and `Cpp` as special package names. struct PackageNameId : public IdBase { static constexpr llvm::StringLiteral Label = "package"; static const PackageNameId None; static const PackageNameId Core; static const PackageNameId Cpp; // Returns the PackageNameId corresponding to a particular IdentifierId. static auto ForIdentifier(IdentifierId id) -> PackageNameId { return PackageNameId(id.index); } using IdBase::IdBase; // Returns the IdentifierId corresponding to this PackageNameId, or `None` if // this is a special package name. auto AsIdentifierId() const -> IdentifierId { return index >= 0 ? IdentifierId(index) : IdentifierId::None; } // Returns the special package name corresponding to this PackageNameId. // Requires that this name is not an identifier name. auto AsSpecialName() const -> llvm::StringLiteral { CARBON_CHECK(index <= NoneIndex); if (*this == None) { return "Main"; } if (*this == Core) { return "Core"; } if (*this == Cpp) { return "Cpp"; } CARBON_FATAL("Unknown special package name kind {0}", index); } auto Print(llvm::raw_ostream& out) const -> void { if (index <= NoneIndex) { out << Label << AsSpecialName(); } else { IdBase::Print(out); } } }; inline constexpr PackageNameId PackageNameId::None(PackageNameId::NoneIndex); inline constexpr PackageNameId PackageNameId::Core(PackageNameId::NoneIndex - 1); inline constexpr PackageNameId PackageNameId::Cpp(PackageNameId::NoneIndex - 2); // Corresponds to StringRefs for string literals. struct StringLiteralValueId : public IdBase { static constexpr llvm::StringLiteral Label = "string"; static const StringLiteralValueId None; using IdBase::IdBase; }; inline constexpr StringLiteralValueId StringLiteralValueId::None( StringLiteralValueId::NoneIndex); } // namespace Carbon #endif // CARBON_TOOLCHAIN_BASE_VALUE_IDS_H_ ================================================ FILE: toolchain/base/value_store.h ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #ifndef CARBON_TOOLCHAIN_BASE_VALUE_STORE_H_ #define CARBON_TOOLCHAIN_BASE_VALUE_STORE_H_ #include #include #include #include #include #include #include "common/check.h" #include "common/ostream.h" #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/Sequence.h" #include "llvm/ADT/SmallVector.h" #include "llvm/Support/MemAlloc.h" #include "toolchain/base/id_tag.h" #include "toolchain/base/mem_usage.h" #include "toolchain/base/value_store_types.h" #include "toolchain/base/yaml.h" namespace Carbon { namespace Internal { // Used as a parent class for non-printable types. This is just for // std::conditional, not as an API. class ValueStoreNotPrintable {}; } // namespace Internal // A simple wrapper for accumulating values, providing IDs to later retrieve the // value. This does not do deduplication. template class ValueStore : public std::conditional, ValueT>, Yaml::Printable>, Internal::ValueStoreNotPrintable> { public: using IdType = IdT; using IdTagType = IdTag; using ValueType = ValueStoreTypes::ValueType; using RefType = ValueStoreTypes::RefType; using ConstRefType = ValueStoreTypes::ConstRefType; // A range over references to the values in a ValueStore, returned from // `ValueStore::values()`. Hides the complex type name of the iterator // internally to provide a type name (`Range`) that can be // referred to without auto and templates. class Range { public: explicit Range(const ValueStore& store [[clang::lifetimebound]]) : flattened_range_(MakeFlattenedRange(store)) {} auto begin() const -> auto { return flattened_range_.begin(); } auto end() const -> auto { return flattened_range_.end(); } private: // Flattens the range of `Chunk`s of `ValueType`s into a single // range of `ValueType`s. static auto MakeFlattenedRange(const ValueStore& store) -> auto { // Because indices into `ValueStore` are all sequential values from 0, we // can use llvm::seq to walk all indices in the store. return llvm::map_range(llvm::seq(store.size_), [&](int32_t i) -> ConstRefType { return store.Get(IdType(store.tag_.Apply(i))); }); } using FlattenedRangeType = decltype(MakeFlattenedRange(std::declval())); FlattenedRangeType flattened_range_; }; // Default constructor, only valid when the IdTag's tag type is Untagged. ValueStore() requires(IdTagIsUntagged) = default; // Construct a ValueStore sharing the IdTag from another ValueStore. Useful // for when two ValueStores are sharing the same ID types. explicit ValueStore(IdTagType tag) requires(!IdTagIsUntagged) : tag_(tag) {} // Construct a ValueStore with a given tag and set of untagged (reserved) ids. explicit ValueStore(IdTagType::TagIdType id, int32_t initial_reserved_ids = 0) requires(!IdTagIsUntagged) : tag_(id, initial_reserved_ids) {} // Stores the value and returns an ID to reference it. auto Add(ValueType value) -> IdType { // This routine is especially hot and the check here relatively expensive // for the value provided, so only do this in non-optimized builds to make // tracking down issues easier. CARBON_DCHECK(size_ < std::numeric_limits::max(), "Id overflow"); IdType id = tag_.Apply(size_); auto [chunk_index, pos] = RawIndexToChunkIndices(size_); ++size_; CARBON_DCHECK(static_cast(chunk_index) <= chunks_.size(), "{0} <= {1}", chunk_index, chunks_.size()); if (static_cast(chunk_index) == chunks_.size()) { chunks_.emplace_back(); } CARBON_DCHECK(pos == chunks_[chunk_index].size()); chunks_[chunk_index].Add(std::move(value)); return id; } // Returns a mutable value for an ID. auto Get(IdType id) -> RefType { CARBON_DCHECK(id.index >= 0, "{0}", id); auto [chunk_index, pos] = IdToChunkIndices(id); return chunks_[chunk_index].Get(pos); } // Returns the value for an ID. auto Get(IdType id) const -> ConstRefType { CARBON_DCHECK(id.index >= 0, "{0}", id); auto [chunk_index, pos] = IdToChunkIndices(id); return chunks_[chunk_index].Get(pos); } // Returns the value for an ID, or a specified default value if a value has // not yet been added for this ID. auto GetWithDefault(IdType id, // ConstRefType default_value [[clang::lifetimebound]]) const -> ConstRefType { CARBON_DCHECK(id.index >= 0, "{0}", id); auto index = tag_.Remove(id); if (index >= size_) { return default_value; } auto [chunk_index, pos] = RawIndexToChunkIndices(index); return chunks_[chunk_index].Get(pos); } // Reserves space. auto Reserve(int32_t size) -> void { if (size <= size_) { return; } auto [final_chunk_index, _] = RawIndexToChunkIndices(size - 1); chunks_.resize(final_chunk_index + 1); } // Grows the ValueStore to `size`. Fills entries with `default_value`. auto Resize(int32_t size, ConstRefType default_value) -> void { if (size <= size_) { return; } auto [begin_chunk_index, begin_pos] = RawIndexToChunkIndices(size_); // Use an inclusive range so that if `size` would be the next chunk, we // don't try doing something with it. auto [end_chunk_index, end_pos] = RawIndexToChunkIndices(size - 1); chunks_.resize(end_chunk_index + 1); // If the begin and end chunks are the same, we only fill from begin to end. if (begin_chunk_index == end_chunk_index) { chunks_[begin_chunk_index].UninitializedFill(end_pos - begin_pos + 1, default_value); } else { // Otherwise, we do partial fills on the begin and end chunk, and full // fills on intermediate chunks. chunks_[begin_chunk_index].UninitializedFill( Chunk::Capacity() - begin_pos, default_value); for (auto i = begin_chunk_index + 1; i < end_chunk_index; ++i) { chunks_[i].UninitializedFill(Chunk::Capacity(), default_value); } chunks_[end_chunk_index].UninitializedFill(end_pos + 1, default_value); } // Update size. size_ = size; } // These are to support printable structures, and are not guaranteed. auto OutputYaml() const -> Yaml::OutputMapping { return Yaml::OutputMapping([&](Yaml::OutputMapping::Map map) { for (auto [id, value] : enumerate()) { map.Add(PrintToString(id), Yaml::OutputScalar(value)); } }); } // Collects memory usage of the values. auto CollectMemUsage(MemUsage& mem_usage, llvm::StringRef label) const -> void { mem_usage.Add(label.str(), size_ * sizeof(ValueType), Chunk::CapacityBytes * chunks_.size()); } auto size() const -> size_t { return size_; } // Makes an iterable range over references to all values in the ValueStore. auto values() [[clang::lifetimebound]] -> auto { return llvm::map_range(llvm::seq(size_), [&](int32_t i) -> RefType { return Get(tag_.Apply(i)); }); } auto values() const [[clang::lifetimebound]] -> Range { return Range(*this); } // Makes an iterable range over pairs of the index and a reference to the // value for each value in the store. // // The range is over references to the values in the store, even if used with // `auto` to destructure the pair. In this example, the `value` is a // `ConstRefType`: // ``` // for (auto [id, value] : store.enumerate()) { ... } // ``` auto enumerate() const [[clang::lifetimebound]] -> auto { // For `it->val`, writing `const std::pair` is required; otherwise // `mapped_iterator` incorrectly infers the pointer type for `PointerProxy`. // NOLINTNEXTLINE(readability-const-return-type) auto index_to_id = [&](int32_t i) -> const std::pair { IdType id = tag_.Apply(i); return std::pair(id, Get(id)); }; // Because indices into `ValueStore` are all sequential values from 0, we // can use llvm::seq to walk all indices in the store. return llvm::map_range(llvm::seq(size_), index_to_id); } auto GetIdTag() const -> IdTagType { return tag_; } auto GetRawIndex(IdT id) const -> int32_t { CARBON_DCHECK(id.index >= 0, "{0}", index); auto index = tag_.Remove(id); #ifndef NDEBUG if (index >= size_) { // Attempt to decompose id.index to include extra detail in the check // here. // // TODO: Teach ValueStore the type of the tag id with a template, then we // can print it with proper formatting instead of just as an integer. auto [id_tag, id_untagged_index] = IdTagType::DecomposeWithBestEffort(id); CARBON_DCHECK( index < size_, "Untagged index was outside of container range. Tagged index {0}. " "Best-effort decomposition: Tag: {1}, Index: {2}. " "Container size: {3}. " "Expected Tag for this container: {4}.", id.index, id_tag, id_untagged_index, size_, tag_.GetContainerTag()); } #endif return index; } private: // A chunk of `ValueType`s which has a fixed capacity, but variable size. // Tracks the size internally for verifying bounds. struct Chunk { public: // The max size of each chunk allocation for `ValueStore`. This is based on // TLB page sizes for the target platform. // // See https://docs.kernel.org/admin-guide/mm/hugetlbpage.html // // A 4K chunk size outperforms a 1M chunk size on Linux-x64 and MacOS-arm64 // in benchmarks and when running file_test. // // Linux-x64: x64 CPUs support 4K and 2M page sizes, but we see 1M is slower // than 4K with tcmalloc in opt builds for our tests. // // Mac-arm64: arm64 CPUs support 4K, 8K, 64K, 256K, 1M, 4M and up. Like for // Linux-x64, 4K outperformed 1M. We didn't try other sizes yet. // // TODO: Is there a more optimize size for Mac-arm64? What should // Linux-arm64 and Mac-x64 use? What should Windows use? // // TODO: The previous SmallVector seems to outperform 4K chunks // (they may be slower by up to 5%) in benchmarks. Find ways to make // chunking faster. Should successive chunks get larger in size? That will // greatly complicate math for choosing a chunk though. static constexpr auto MaxAllocationBytes() -> int32_t { #if !defined(NDEBUG) || LLVM_ADDRESS_SANITIZER_BUILD // Use a small size in unoptimized builds to ensure multiple chunks get // used. And do the same in ASAN builds to reduce bookkeeping overheads. // Using large allocations (e.g. 1M+) incurs a 10x runtime cost for our // tests under ASAN. return sizeof(ValueType) * 5; #else return 4 * 1024; #endif } // The number of elements stored in each chunk allocation. // // The number must be a power of two so that that there are no unused values // in bits indexing into the allocation. static constexpr auto Capacity() -> int32_t { constexpr auto MaxElements = MaxAllocationBytes() / sizeof(ValueType); return std::bit_floor(MaxElements); } // The number of bits needed to index each element in a chunk allocation. static constexpr auto IndexBits() -> int32_t { static_assert(Capacity() > 0); return std::bit_width(uint32_t{Capacity() - 1}); } static constexpr auto CapacityBytes = Capacity() * sizeof(ValueType); explicit Chunk() : buf_(reinterpret_cast( llvm::allocate_buffer(CapacityBytes, alignof(ValueType)))) {} // Moving leaves nullptr behind in the moved-from object so that the // destructor is a no-op (preventing double free). Chunk(Chunk&& rhs) noexcept : buf_(std::exchange(rhs.buf_, nullptr)), num_(rhs.num_) {} auto operator=(Chunk&& rhs) noexcept -> Chunk& { buf_ = std::exchange(rhs.buf_, nullptr); num_ = rhs.num_; return *this; } ~Chunk() { if (buf_) { if constexpr (!std::is_trivially_destructible_v) { std::destroy_n(buf_, num_); } llvm::deallocate_buffer(buf_, CapacityBytes, alignof(ValueType)); } } auto Get(int32_t i) -> ValueType& { CARBON_DCHECK(i < num_, "{0}", i); return buf_[i]; } auto Get(int32_t i) const -> const ValueType& { CARBON_DCHECK(i < num_, "{0}", i); return buf_[i]; } auto Add(ValueType&& value) -> void { CARBON_DCHECK(num_ < Capacity()); std::construct_at(buf_ + num_, std::move(value)); ++num_; } // Fills `fill_count` entries with `default_value`, increasing the size // respectively. auto UninitializedFill(int32_t fill_count, ConstRefType default_value) -> void { CARBON_DCHECK(num_ + fill_count <= Capacity()); std::uninitialized_fill_n(buf_ + num_, fill_count, default_value); num_ += fill_count; } auto size() const -> int32_t { return num_; } private: // Verify using an `int32_t` for `num_` is sound. static_assert(Capacity() <= std::numeric_limits::max()); ValueType* buf_; int32_t num_ = 0; }; // Converts a raw index into an index into the set of chunks, and an offset // into that specific chunk. Looks for index overflow in non-optimized builds. static auto RawIndexToChunkIndices(int32_t index) -> std::pair { constexpr auto LowBits = Chunk::IndexBits(); // Verify there are no unused bits when indexing up to the `Capacity`. This // ensures that ids are contiguous values from 0, as if the values were all // stored in a single array, and allows using the ids to index into other // arrays. static_assert((1 << LowBits) == Chunk::Capacity()); // Simple check to make sure nothing went wildly wrong with the `Capacity`, // and we have some room for a chunk index, and that shifting by the number // of bits won't be UB in an int32_t. static_assert(LowBits < 30); // The index of the chunk is the high bits. auto chunk = index >> LowBits; // The index into the chunk is the low bits. auto pos = index & ((1 << LowBits) - 1); return {chunk, pos}; } // Converts an id into an index into the set of chunks, and an offset into // that specific chunk. auto IdToChunkIndices(IdType id) const -> std::pair { return RawIndexToChunkIndices(GetRawIndex(id)); } // Number of elements added to the store. The number should never exceed what // fits in an `int32_t`, which is checked in non-optimized builds in Add(). int32_t size_ = 0; IdTagType tag_; // Storage for the `ValueType` objects, indexed by the id. We use a vector of // chunks of `ValueType` instead of just a vector of `ValueType` so that // addresses of `ValueType` objects are stable. This allows the rest of the // toolchain to hold references into `ValueStore` without having to worry // about invalidation and use-after-free. We ensure at least one Chunk is held // inline so that in the case where there is only a single Chunk (i.e. small // files) we can avoid one indirection. llvm::SmallVector chunks_; }; } // namespace Carbon #endif // CARBON_TOOLCHAIN_BASE_VALUE_STORE_H_ ================================================ FILE: toolchain/base/value_store_test.cpp ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #include "toolchain/base/value_store.h" #include #include #include #include "toolchain/base/value_ids.h" namespace Carbon::Testing { namespace { using ::testing::Eq; using ::testing::Not; TEST(ValueStore, Real) { Real real1{.mantissa = llvm::APInt(64, 1), .exponent = llvm::APInt(64, 11), .is_decimal = true}; Real real2{.mantissa = llvm::APInt(64, 2), .exponent = llvm::APInt(64, 22), .is_decimal = false}; ValueStore reals; RealId id1 = reals.Add(real1); RealId id2 = reals.Add(real2); ASSERT_TRUE(id1.has_value()); ASSERT_TRUE(id2.has_value()); EXPECT_THAT(id1, Not(Eq(id2))); const auto& real1_copy = reals.Get(id1); EXPECT_THAT(real1.mantissa, Eq(real1_copy.mantissa)); EXPECT_THAT(real1.exponent, Eq(real1_copy.exponent)); EXPECT_THAT(real1.is_decimal, Eq(real1_copy.is_decimal)); const auto& real2_copy = reals.Get(id2); EXPECT_THAT(real2.mantissa, Eq(real2_copy.mantissa)); EXPECT_THAT(real2.exponent, Eq(real2_copy.exponent)); EXPECT_THAT(real2.is_decimal, Eq(real2_copy.is_decimal)); } } // namespace } // namespace Carbon::Testing ================================================ FILE: toolchain/base/value_store_types.h ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #ifndef CARBON_TOOLCHAIN_BASE_VALUE_STORE_TYPES_H_ #define CARBON_TOOLCHAIN_BASE_VALUE_STORE_TYPES_H_ #include #include #include "llvm/ADT/StringRef.h" namespace Carbon { // Common calculation for ValueStore types. template class ValueStoreTypes { public: using ValueType = std::remove_cvref_t; // Typically we want to use `ValueType&` and `const ValueType& to avoid // copies, but when the value type is a `StringRef`, we assume external // storage for the string data and both our value type and ref type will be // `StringRef`. This will preclude mutation of the string data. using RefType = std::conditional_t, llvm::StringRef, ValueType&>; using ConstRefType = std::conditional_t, llvm::StringRef, const ValueType&>; }; } // namespace Carbon #endif // CARBON_TOOLCHAIN_BASE_VALUE_STORE_TYPES_H_ ================================================ FILE: toolchain/base/yaml.h ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #ifndef CARBON_TOOLCHAIN_BASE_YAML_H_ #define CARBON_TOOLCHAIN_BASE_YAML_H_ #include "common/check.h" #include "common/ostream.h" #include "llvm/ADT/StringRef.h" #include "llvm/Support/YAMLTraits.h" // This file provides adapters for outputting YAML using llvm::yaml's APIs. It // only supports output, not input. However, it addresses the mix of const and // non-const expectations of the llvm::yaml that make it difficult to otherwise // use the trait-based approach. namespace Carbon::Yaml { // Helper for printing YAML, to maintain a consistent configuration. template inline auto Print(llvm::raw_ostream& out, T yaml) -> void { llvm::yaml::Output yout(out, /*Ctxt=*/nullptr, /*WrapColumn=*/80); yout << yaml; } // Similar to the standard Printable, but relies on OutputYaml for printing. template class Printable : public Carbon::Printable { public: auto Print(llvm::raw_ostream& out) const -> void { Carbon::Yaml::Print(out, static_cast(this)->OutputYaml()); } }; // Adapts a function for outputting YAML as a scalar. This currently assumes no // scalars passed through this should be quoted. class OutputScalar { public: template explicit OutputScalar(const T& val) : output_([&](llvm::raw_ostream& out) -> void { out << val; }) {} explicit OutputScalar(const llvm::APInt& val) : output_([&](llvm::raw_ostream& out) -> void { // Carbon's plain APInt storage is typically unsigned. val.print(out, /*isSigned=*/false); }) {} explicit OutputScalar(std::functionvoid> output) : output_(std::move(output)) {} auto Output(llvm::raw_ostream& out) const -> void { output_(out); } private: std::functionvoid> output_; }; // Adapts a function for outputting YAML as a mapping. class OutputMapping { public: class Map { public: // `io` must not be null. explicit Map(llvm::yaml::IO* io) : io_(io) {} // Maps a value. This mainly takes responsibility for copying the value, // letting mapRequired take `&value`. template auto Add(llvm::StringRef key, T value) -> void { io_->mapRequired(key.data(), value); } private: llvm::yaml::IO* io_; }; explicit OutputMapping(std::functionvoid> output) : output_(std::move(output)) {} auto Output(llvm::yaml::IO& io) -> void { output_(Map(&io)); } private: std::functionvoid> output_; }; } // namespace Carbon::Yaml // Link OutputScalar to the llvm::yaml::IO API. template <> struct llvm::yaml::ScalarTraits { static auto output(const Carbon::Yaml::OutputScalar& value, void* /*ctxt*/, llvm::raw_ostream& out) -> void { value.Output(out); } static auto input(StringRef /*scalar*/, void* /*ctxt*/, Carbon::Yaml::OutputScalar& /*value*/) -> StringRef { CARBON_FATAL("Input is unsupported."); } static auto mustQuote(StringRef /*value*/) -> QuotingType { return QuotingType::None; } }; static_assert(llvm::yaml::has_ScalarTraits::value); // Link OutputMapping to the llvm::yaml::IO API. template <> struct llvm::yaml::MappingTraits { static auto mapping(IO& io, Carbon::Yaml::OutputMapping& mapping) -> void { mapping.Output(io); } }; static_assert(llvm::yaml::has_MappingTraits::value); #endif // CARBON_TOOLCHAIN_BASE_YAML_H_ ================================================ FILE: toolchain/check/BUILD ================================================ # Part of the Carbon Language project, under the Apache License v2.0 with LLVM # Exceptions. See /LICENSE for license information. # SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception load("//bazel/cc_rules:defs.bzl", "cc_library") load("//testing/fuzzing:rules.bzl", "cc_fuzz_test") package(default_visibility = ["//visibility:public"]) filegroup( name = "testdata", srcs = glob(["testdata/**/*.carbon"]), ) cc_library( name = "context", srcs = [ "action.cpp", "call.cpp", "class.cpp", "context.cpp", "control_flow.cpp", "convert.cpp", "cpp/access.cpp", "cpp/call.cpp", "cpp/constant.cpp", "cpp/context.cpp", "cpp/custom_type_mapping.cpp", "cpp/generate_ast.cpp", "cpp/impl_lookup.cpp", "cpp/import.cpp", "cpp/location.cpp", "cpp/macros.cpp", "cpp/operators.cpp", "cpp/overload_resolution.cpp", "cpp/thunk.cpp", "cpp/type_mapping.cpp", "custom_witness.cpp", "decl_name_stack.cpp", "deduce.cpp", "deferred_definition_worklist.cpp", "eval.cpp", "eval_inst.cpp", "facet_type.cpp", "function.cpp", "generic.cpp", "global_init.cpp", "impl.cpp", "impl_lookup.cpp", "impl_validation.cpp", "import.cpp", "import_ref.cpp", "inst.cpp", "inst_block_stack.cpp", "interface.cpp", "keyword_modifier_set.cpp", "literal.cpp", "member_access.cpp", "merge.cpp", "modifiers.cpp", "name_component.cpp", "name_lookup.cpp", "name_ref.cpp", "name_scope.cpp", "operator.cpp", "pattern.cpp", "pattern_match.cpp", "pointer_dereference.cpp", "return.cpp", "scope_stack.cpp", "subst.cpp", "thunk.cpp", "type.cpp", "type_completion.cpp", "type_structure.cpp", "unused.cpp", ], hdrs = [ "action.h", "call.h", "class.h", "context.h", "control_flow.h", "convert.h", "cpp/access.h", "cpp/call.h", "cpp/constant.h", "cpp/context.h", "cpp/custom_type_mapping.h", "cpp/generate_ast.h", "cpp/impl_lookup.h", "cpp/import.h", "cpp/location.h", "cpp/macros.h", "cpp/operators.h", "cpp/overload_resolution.h", "cpp/thunk.h", "cpp/type_mapping.h", "custom_witness.h", "decl_introducer_state.h", "decl_name_stack.h", "deduce.h", "deferred_definition_worklist.h", "diagnostic_helpers.h", "eval.h", "eval_inst.h", "facet_type.h", "full_pattern_stack.h", "function.h", "generic.h", "global_init.h", "impl.h", "impl_lookup.h", "impl_validation.h", "import.h", "import_ref.h", "inst.h", "inst_block_stack.h", "interface.h", "keyword_modifier_set.h", "lexical_lookup.h", "literal.h", "member_access.h", "merge.h", "modifiers.h", "name_component.h", "name_lookup.h", "name_ref.h", "name_scope.h", "operator.h", "param_and_arg_refs_stack.h", "pattern.h", "pattern_match.h", "pending_block.h", "pointer_dereference.h", "region_stack.h", "return.h", "scope_index.h", "scope_stack.h", "subst.h", "thunk.h", "type.h", "type_completion.h", "type_structure.h", "unused.h", ], deps = [ ":core_identifier", ":node_stack", "//common:array_stack", "//common:check", "//common:concepts", "//common:emplace_by_calling", "//common:enum_mask_base", "//common:find", "//common:growing_range", "//common:map", "//common:move_only", "//common:ostream", "//common:raw_string_ostream", "//common:set", "//common:vlog", "//toolchain/base:canonical_value_store", "//toolchain/base:index_base", "//toolchain/base:int", "//toolchain/base:kind_switch", "//toolchain/base:shared_value_stores", "//toolchain/base:value_ids", "//toolchain/base:value_store", "//toolchain/check:generic_region_stack", "//toolchain/diagnostics:emitter", "//toolchain/diagnostics:format_providers", "//toolchain/lex:token_info", "//toolchain/lex:token_kind", "//toolchain/lex:tokenized_buffer", "//toolchain/parse:node_kind", "//toolchain/parse:tree", "//toolchain/sem_ir:absolute_node_id", "//toolchain/sem_ir:clang_decl", "//toolchain/sem_ir:cpp_file", "//toolchain/sem_ir:expr_info", "//toolchain/sem_ir:file", "//toolchain/sem_ir:formatter", "//toolchain/sem_ir:typed_insts", "@llvm-project//clang:ast", "@llvm-project//clang:basic", "@llvm-project//clang:codegen", "@llvm-project//clang:frontend", "@llvm-project//clang:lex", "@llvm-project//clang:parse", "@llvm-project//clang:sema", "@llvm-project//clang:tooling", "@llvm-project//llvm:Support", ], ) cc_library( name = "dump", srcs = ["dump.cpp"], deps = [ ":context", "//common:check", "//common:ostream", "//common:raw_string_ostream", "//toolchain/lex:dump", "//toolchain/lex:tokenized_buffer", "//toolchain/parse:dump", "//toolchain/parse:tree", "//toolchain/sem_ir:dump", "//toolchain/sem_ir:file", ], # Always link dump methods. alwayslink = 1, ) cc_library( name = "check", srcs = [ "check.cpp", "check_unit.cpp", "check_unit.h", "handle.h", "node_id_traversal.cpp", "node_id_traversal.h", ] + # Glob handler files to avoid missing any. glob([ "handle_*.cpp", ]), hdrs = ["check.h"], deps = [ ":context", ":core_identifier", ":dump", ":emitter", "//common:check", "//common:error", "//common:find", "//common:growing_range", "//common:map", "//common:ostream", "//common:pretty_stack_trace_function", "//common:vlog", "//toolchain/base:fixed_size_value_store", "//toolchain/base:kind_switch", "//toolchain/base:shared_value_stores", "//toolchain/base:timings", "//toolchain/diagnostics:emitter", "//toolchain/diagnostics:format_providers", "//toolchain/lex:token_index", "//toolchain/lex:token_kind", "//toolchain/lex:tokenized_buffer", "//toolchain/parse:node_category", "//toolchain/parse:node_kind", "//toolchain/parse:tree", "//toolchain/sem_ir:absolute_node_id", "//toolchain/sem_ir:entry_point", "//toolchain/sem_ir:expr_info", "//toolchain/sem_ir:file", "//toolchain/sem_ir:formatter", "//toolchain/sem_ir:typed_insts", "@llvm-project//clang:frontend", "@llvm-project//clang:sema", "@llvm-project//llvm:Support", ], ) cc_fuzz_test( name = "check_fuzzer", size = "small", srcs = ["check_fuzzer.cpp"], corpus = glob(["fuzzer_corpus/*"]), deps = [ "//common:exe_path", "//testing/fuzzing:libfuzzer_header", "//toolchain/driver", "@llvm-project//llvm:Support", ], ) cc_library( name = "generic_region_stack", srcs = ["generic_region_stack.cpp"], hdrs = ["generic_region_stack.h"], deps = [ "//common:array_stack", "//common:check", "//common:map", "//common:ostream", "//common:vlog", "//toolchain/sem_ir:file", "//toolchain/sem_ir:typed_insts", "@llvm-project//llvm:Support", ], ) cc_library( name = "node_stack", srcs = ["node_stack.cpp"], hdrs = ["node_stack.h"], deps = [ "//common:check", "//common:ostream", "//common:vlog", "//toolchain/parse:node_kind", "//toolchain/parse:tree", "//toolchain/sem_ir:typed_insts", "@llvm-project//llvm:Support", ], ) cc_library( name = "core_identifier", srcs = ["core_identifier.cpp"], hdrs = ["core_identifier.h"], textual_hdrs = ["core_identifier.def"], deps = [ "//common:enum_base", "//toolchain/base:shared_value_stores", "//toolchain/base:value_ids", "//toolchain/sem_ir:typed_insts", ], ) cc_library( name = "emitter", srcs = ["diagnostic_emitter.cpp"], hdrs = ["diagnostic_emitter.h"], deps = [ ":context", "//common:ostream", "//common:raw_string_ostream", "//toolchain/diagnostics:emitter", "//toolchain/lex:token_index", "//toolchain/parse:tree", "//toolchain/sem_ir:absolute_node_id", "//toolchain/sem_ir:diagnostic_loc_converter", "//toolchain/sem_ir:file", "//toolchain/sem_ir:stringify", "//toolchain/sem_ir:typed_insts", "@llvm-project//llvm:Support", ], ) ================================================ FILE: toolchain/check/action.cpp ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #include "toolchain/check/action.h" #include "toolchain/base/kind_switch.h" #include "toolchain/check/generic_region_stack.h" #include "toolchain/check/inst.h" #include "toolchain/check/type.h" #include "toolchain/sem_ir/constant.h" #include "toolchain/sem_ir/id_kind.h" #include "toolchain/sem_ir/inst.h" #include "toolchain/sem_ir/typed_insts.h" namespace Carbon::Check { auto PerformAction(Context& context, SemIR::LocId loc_id, SemIR::RefineTypeAction action) -> SemIR::InstId { return AddInst( context, loc_id, {.type_id = context.types().GetTypeIdForTypeInstId(action.inst_type_inst_id), .source_id = action.inst_id}); } static auto OperandIsDependent(Context& context, SemIR::ConstantId const_id) -> bool { // A type operand makes the instruction dependent if it is a // template-dependent constant. if (!const_id.is_symbolic()) { return false; } return context.constant_values().GetSymbolicConstant(const_id).dependence == SemIR::ConstantDependence::Template; } auto OperandIsDependent(Context& context, SemIR::TypeId type_id) -> bool { // A type operand makes the instruction dependent if it is a // template-dependent type. return OperandIsDependent(context, context.types().GetConstantId(type_id)); } auto OperandIsDependent(Context& context, SemIR::InstId inst_id) -> bool { // An instruction operand makes the instruction dependent if its type or // constant value is dependent. return OperandIsDependent(context, context.insts().Get(inst_id).type_id()) || OperandIsDependent(context, context.constant_values().Get(inst_id)); } auto OperandIsDependent(Context& context, SemIR::TypeInstId inst_id) -> bool { // An instruction operand makes the instruction dependent if its type or // constant value is dependent. TypeInstId has type `TypeType` which is // concrete, so we only need to look at the constant value. return OperandIsDependent(context, context.constant_values().Get(inst_id)); } static auto OperandIsDependent(Context& context, SemIR::Inst::ArgAndKind arg) -> bool { CARBON_KIND_SWITCH(arg) { case CARBON_KIND(SemIR::InstId inst_id): { return OperandIsDependent(context, inst_id); } case CARBON_KIND(SemIR::MetaInstId inst_id): { return OperandIsDependent(context, inst_id); } case CARBON_KIND(SemIR::TypeInstId inst_id): { return OperandIsDependent(context, inst_id); } case SemIR::IdKind::None: case SemIR::IdKind::For: case SemIR::IdKind::For: return false; default: // TODO: Properly handle different argument kinds. CARBON_FATAL("Unexpected argument kind for action"); } } auto ActionIsDependent(Context& context, SemIR::Inst action_inst) -> bool { if (auto refine_action = action_inst.TryAs()) { // `RefineTypeAction` can be performed whenever the type is non-dependent, // even if we don't know the instruction yet. return OperandIsDependent(context, refine_action->inst_type_inst_id); } if (OperandIsDependent(context, action_inst.type_id())) { return true; } return OperandIsDependent(context, action_inst.arg0_and_kind()) || OperandIsDependent(context, action_inst.arg1_and_kind()); } static auto AddDependentActionSpliceImpl(Context& context, SemIR::LocIdAndInst action, SemIR::TypeInstId result_type_inst_id) -> SemIR::InstId { auto inst_id = AddDependentActionInst(context, action); if (!result_type_inst_id.has_value()) { result_type_inst_id = AddDependentActionTypeInst( context, action.loc_id, SemIR::TypeOfInst{.type_id = SemIR::TypeType::TypeId, .inst_id = inst_id}); } return AddInst( context, action.loc_id, SemIR::SpliceInst{.type_id = context.types().GetTypeIdForTypeInstId( result_type_inst_id), .inst_id = inst_id}); } // Refine one operand of an action. Given an argument from a template, this // produces an argument that has the template-dependent parts replaced with // their concrete values, so that the action doesn't need to know which specific // it is operating on. static auto RefineOperand(Context& context, SemIR::LocId loc_id, SemIR::Inst::ArgAndKind arg) -> int32_t { if (auto inst_id = arg.TryAs()) { auto inst = context.insts().Get(*inst_id); if (inst.Is()) { // The argument will evaluate to the spliced instruction, which is already // refined. return arg.value(); } // If the type of the action argument is dependent, refine to an instruction // with a concrete type. if (OperandIsDependent(context, inst.type_id())) { auto type_inst_id = context.types().GetTypeInstId(inst.type_id()); inst_id = AddDependentActionSpliceImpl( context, SemIR::LocIdAndInst( loc_id, SemIR::RefineTypeAction{.type_id = GetSingletonType( context, SemIR::InstType::TypeInstId), .inst_id = *inst_id, .inst_type_inst_id = type_inst_id}), type_inst_id); } // TODO: Handle the case where the constant value of the instruction is // template-dependent. return inst_id->index; } return arg.value(); } // Refine the operands of an action, ensuring that they will refer to concrete // instructions that don't have template-dependent types. static auto RefineOperands(Context& context, SemIR::LocId loc_id, SemIR::Inst action) -> SemIR::Inst { auto arg0 = RefineOperand(context, loc_id, action.arg0_and_kind()); auto arg1 = RefineOperand(context, loc_id, action.arg1_and_kind()); action.SetArgs(arg0, arg1); return action; } auto AddDependentActionSplice(Context& context, SemIR::LocIdAndInst action, SemIR::TypeInstId result_type_inst_id) -> SemIR::InstId { action.inst = RefineOperands(context, action.loc_id, action.inst); return AddDependentActionSpliceImpl(context, action, result_type_inst_id); } auto Internal::BeginPerformDelayedAction(Context& context) -> void { // Push an `InstBlock` to hold any instructions created by the action. // Note that we assume that actions don't need to create multiple blocks. If // this changes, we should push a region too. context.inst_block_stack().Push(); } auto Internal::EndPerformDelayedAction(Context& context, SemIR::InstId result_id) -> SemIR::InstId { // If the only created instruction is the result, then we can use it directly. auto contents = context.inst_block_stack().PeekCurrentBlockContents(); if (contents.size() == 1 && contents[0] == result_id) { context.inst_block_stack().PopAndDiscard(); return result_id; } // Otherwise, create a splice_block to represent the sequence of instructions // created by the action. auto result = context.insts().GetWithLocId(result_id); return AddInstInNoBlock( context, result.loc_id, SemIR::SpliceBlock{.type_id = result.inst.type_id(), .block_id = context.inst_block_stack().Pop(), .result_id = result_id}); } } // namespace Carbon::Check ================================================ FILE: toolchain/check/action.h ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #ifndef CARBON_TOOLCHAIN_CHECK_ACTION_H_ #define CARBON_TOOLCHAIN_CHECK_ACTION_H_ #include "toolchain/check/context.h" #include "toolchain/check/inst.h" #include "toolchain/sem_ir/ids.h" #include "toolchain/sem_ir/inst.h" namespace Carbon::Check { // Performs a member access action. Defined in member_access.cpp. auto PerformAction(Context& context, SemIR::LocId loc_id, SemIR::AccessMemberAction action) -> SemIR::InstId; auto PerformAction(Context& context, SemIR::LocId loc_id, SemIR::AccessOptionalMemberAction action) -> SemIR::InstId; // Performs a conversion action. Defined in convert.cpp. auto PerformAction(Context& context, SemIR::LocId loc_id, SemIR::ConvertToValueAction action) -> SemIR::InstId; // Performs a type refinement action, by creating a conversion from an // instruction with a template-dependent symbolic type to the corresponding // instantiated type. auto PerformAction(Context& context, SemIR::LocId loc_id, SemIR::RefineTypeAction action) -> SemIR::InstId; // Determines whether the given action depends on a template parameter in a way // that means it cannot be performed immediately. auto ActionIsDependent(Context& context, SemIR::Inst action_inst) -> bool; // Determines whether the given action operand depends on a template parameter // in a way that means the action cannot be performed immediately. auto OperandIsDependent(Context& context, SemIR::InstId inst_id) -> bool; // Determines whether the given action operand depends on a template parameter // in a way that means the action cannot be performed immediately. auto OperandIsDependent(Context& context, SemIR::TypeInstId inst_id) -> bool; // Determines whether the given type depends on a template parameter // in a way that means the action cannot be performed immediately. auto OperandIsDependent(Context& context, SemIR::TypeId type_id) -> bool; // Adds an instruction to the current block to splice in the result of // performing a dependent action. auto AddDependentActionSplice(Context& context, SemIR::LocIdAndInst action, SemIR::TypeInstId result_type_inst_id) -> SemIR::InstId; // Convenience wrapper for `AddDependentActionSplice`. template auto AddDependentActionSplice(Context& context, LocT loc, InstT inst, SemIR::TypeInstId result_type_inst_id) -> SemIR::InstId { return AddDependentActionSplice(context, SemIR::LocIdAndInst(loc, inst), result_type_inst_id); } // Handles a new action. If the action is not dependent, it is performed // immediately. Otherwise, adds the action to the enclosing template's eval // block and creates an instruction to splice in the result of the action. template auto HandleAction(Context& context, SemIR::LocId loc_id, ActionT action_inst, SemIR::TypeInstId result_type_inst_id = SemIR::TypeInstId::None) -> SemIR::InstId { if (ActionIsDependent(context, action_inst) || (result_type_inst_id.has_value() && OperandIsDependent(context, result_type_inst_id))) { return AddDependentActionSplice( context, SemIR::LocIdAndInst(loc_id, action_inst), result_type_inst_id); } return PerformAction(context, loc_id, action_inst); } namespace Internal { // Performs setup steps for performing a delayed action. This is an // implementation detail of PerformDelayedAction and should not be called // directly. auto BeginPerformDelayedAction(Context& context) -> void; // Performs cleanup steps for performing a delayed action. This is an // implementation detail of PerformDelayedAction and should not be called // directly. auto EndPerformDelayedAction(Context& context, SemIR::InstId result_id) -> SemIR::InstId; } // namespace Internal // Performs an action as a result of evaluation of a template's eval block. template auto PerformDelayedAction(Context& context, SemIR::LocId loc_id, ActionT action_inst) -> SemIR::InstId { if (ActionIsDependent(context, action_inst)) { return SemIR::InstId::None; } Internal::BeginPerformDelayedAction(context); auto inst_id = PerformAction(context, loc_id, action_inst); return Internal::EndPerformDelayedAction(context, inst_id); } } // namespace Carbon::Check #endif // CARBON_TOOLCHAIN_CHECK_ACTION_H_ ================================================ FILE: toolchain/check/call.cpp ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #include "toolchain/check/call.h" #include #include "toolchain/base/kind_switch.h" #include "toolchain/check/context.h" #include "toolchain/check/control_flow.h" #include "toolchain/check/convert.h" #include "toolchain/check/cpp/call.h" #include "toolchain/check/cpp/thunk.h" #include "toolchain/check/deduce.h" #include "toolchain/check/facet_type.h" #include "toolchain/check/function.h" #include "toolchain/check/import_ref.h" #include "toolchain/check/inst.h" #include "toolchain/check/name_ref.h" #include "toolchain/check/thunk.h" #include "toolchain/check/type.h" #include "toolchain/diagnostics/format_providers.h" #include "toolchain/sem_ir/builtin_function_kind.h" #include "toolchain/sem_ir/entity_with_params_base.h" #include "toolchain/sem_ir/function.h" #include "toolchain/sem_ir/ids.h" #include "toolchain/sem_ir/inst.h" #include "toolchain/sem_ir/typed_insts.h" namespace Carbon::Check { namespace { // Entity kinds, for diagnostics. Converted to an int for a select. enum class EntityKind : uint8_t { Function = 0, GenericClass = 1, GenericInterface = 2, GenericNamedConstraint = 3, }; } // namespace // Resolves the callee expression in a call to a specific callee, or diagnoses // if no specific callee can be identified. This verifies the arity of the // callee and determines any compile-time arguments, but doesn't check that the // runtime arguments are convertible to the parameter types. // // `self_id` and `arg_ids` are the self argument and explicit arguments in the // call. // // Returns a `SpecificId` for the specific callee, `SpecificId::None` if the // callee is not generic, or `nullopt` if an error has been diagnosed. static auto ResolveCalleeInCall(Context& context, SemIR::LocId loc_id, const SemIR::EntityWithParamsBase& entity, EntityKind entity_kind_for_diagnostic, SemIR::SpecificId enclosing_specific_id, SemIR::InstId self_id, llvm::ArrayRef arg_ids) -> std::optional { // Check that the arity matches. auto params = context.inst_blocks().GetOrEmpty(entity.param_patterns_id); if (arg_ids.size() != params.size()) { CARBON_DIAGNOSTIC(CallArgCountMismatch, Error, "{0} argument{0:s} passed to " "{1:=0:function|=1:generic class|=2:generic " "interface|=3:generic constraint}" " expecting {2} argument{2:s}", Diagnostics::IntAsSelect, Diagnostics::IntAsSelect, Diagnostics::IntAsSelect); CARBON_DIAGNOSTIC(InCallToEntity, Note, "calling {0:=0:function|=1:generic class|=2:generic " "interface|=3:generic constraint}" " declared here", Diagnostics::IntAsSelect); context.emitter() .Build(loc_id, CallArgCountMismatch, arg_ids.size(), static_cast(entity_kind_for_diagnostic), params.size()) .Note(entity.latest_decl_id(), InCallToEntity, static_cast(entity_kind_for_diagnostic)) .Emit(); return std::nullopt; } // Perform argument deduction. auto specific_id = SemIR::SpecificId::None; if (entity.generic_id.has_value()) { specific_id = DeduceGenericCallArguments( context, loc_id, entity.generic_id, enclosing_specific_id, entity.implicit_param_patterns_id, entity.param_patterns_id, self_id, arg_ids); if (!specific_id.has_value()) { return std::nullopt; } } return specific_id; } // Performs a call where the callee is the name of a generic class, such as // `Vector(i32)`. static auto PerformCallToGenericClass(Context& context, SemIR::LocId loc_id, SemIR::ClassId class_id, SemIR::SpecificId enclosing_specific_id, llvm::ArrayRef arg_ids) -> SemIR::InstId { const auto& generic_class = context.classes().Get(class_id); auto callee_specific_id = ResolveCalleeInCall(context, loc_id, generic_class, EntityKind::GenericClass, enclosing_specific_id, /*self_id=*/SemIR::InstId::None, arg_ids); if (!callee_specific_id) { return SemIR::ErrorInst::InstId; } return GetOrAddInst(context, loc_id, {.type_id = SemIR::TypeType::TypeId, .class_id = class_id, .specific_id = *callee_specific_id}); } static auto EntityFromInterfaceOrNamedConstraint( Context& context, SemIR::InterfaceId interface_id) -> const SemIR::EntityWithParamsBase& { return context.interfaces().Get(interface_id); } static auto EntityFromInterfaceOrNamedConstraint( Context& context, SemIR::NamedConstraintId named_constraint_id) -> const SemIR::EntityWithParamsBase& { return context.named_constraints().Get(named_constraint_id); } // Performs a call where the callee is the name of a generic interface or named // constraint, such as `AddWith(i32)`. template requires SameAsOneOf static auto PerformCallToGenericInterfaceOrNamedConstaint( Context& context, SemIR::LocId loc_id, IdT id, SemIR::SpecificId enclosing_specific_id, llvm::ArrayRef arg_ids) -> SemIR::InstId { const auto& entity = EntityFromInterfaceOrNamedConstraint(context, id); auto entity_kind_for_diagnostic = EntityKind::GenericInterface; if constexpr (std::same_as) { entity_kind_for_diagnostic = EntityKind::GenericNamedConstraint; } auto callee_specific_id = ResolveCalleeInCall(context, loc_id, entity, entity_kind_for_diagnostic, enclosing_specific_id, /*self_id=*/SemIR::InstId::None, arg_ids); if (!callee_specific_id) { return SemIR::ErrorInst::InstId; } std::optional facet_type; if constexpr (std::same_as) { facet_type = FacetTypeFromInterface(context, id, *callee_specific_id); } else { facet_type = FacetTypeFromNamedConstraint(context, id, *callee_specific_id); } return GetOrAddInst(context, loc_id, *facet_type); } // Builds an appropriate specific function for the callee, also handling // instance binding. static auto BuildCalleeSpecificFunction( Context& context, SemIR::LocId loc_id, SemIR::InstId callee_id, SemIR::InstId callee_function_self_type_id, SemIR::SpecificId callee_specific_id) -> SemIR::InstId { auto generic_callee_id = callee_id; // Strip off a bound_method so that we can form a constant specific callee. auto bound_method = context.insts().TryGetAs(callee_id); if (bound_method) { generic_callee_id = bound_method->function_decl_id; } // Form a specific callee. if (callee_function_self_type_id.has_value()) { // This is an associated function in an interface; the callee is the // specific function in the impl that corresponds to the specific function // we deduced. callee_id = GetOrAddInst(context, SemIR::LocId(generic_callee_id), SemIR::SpecificImplFunction{ .type_id = GetSingletonType( context, SemIR::SpecificFunctionType::TypeInstId), .callee_id = generic_callee_id, .specific_id = callee_specific_id}); } else { // This is a regular generic function. The callee is the specific function // we deduced. callee_id = GetOrAddInst(context, SemIR::LocId(generic_callee_id), SemIR::SpecificFunction{ .type_id = GetSingletonType( context, SemIR::SpecificFunctionType::TypeInstId), .callee_id = generic_callee_id, .specific_id = callee_specific_id}); } // Add the `self` argument back if there was one. if (bound_method) { callee_id = GetOrAddInst(context, loc_id, {.type_id = bound_method->type_id, .object_id = bound_method->object_id, .function_decl_id = callee_id}); } return callee_id; } auto PerformCallToFunction(Context& context, SemIR::LocId loc_id, SemIR::InstId callee_id, const SemIR::CalleeFunction& callee_function, llvm::ArrayRef arg_ids, bool is_operator_syntax) -> SemIR::InstId { // If the callee is a generic function, determine the generic argument values // for the call. auto callee_specific_id = ResolveCalleeInCall( context, loc_id, context.functions().Get(callee_function.function_id), EntityKind::Function, callee_function.enclosing_specific_id, callee_function.self_id, arg_ids); if (!callee_specific_id) { return SemIR::ErrorInst::InstId; } if (callee_specific_id->has_value()) { callee_id = BuildCalleeSpecificFunction(context, loc_id, callee_id, callee_function.self_type_id, *callee_specific_id); } auto& callee = context.functions().Get(callee_function.function_id); auto return_type_id = callee.GetDeclaredReturnType(context.sem_ir(), *callee_specific_id); if (!return_type_id.has_value()) { return_type_id = GetTupleType(context, {}); } llvm::SmallVector return_arg_ids; for (auto return_pattern_id : context.inst_blocks().GetOrEmpty(callee.return_patterns_id)) { Diagnostics::AnnotationScope annotate_diagnostics( &context.emitter(), [&](auto& builder) { CARBON_DIAGNOSTIC(IncompleteReturnTypeHere, Note, "return type declared here"); builder.Note(return_pattern_id, IncompleteReturnTypeHere); }); auto arg_type_id = CheckFunctionReturnPatternType( context, loc_id, return_pattern_id, *callee_specific_id); if (arg_type_id == SemIR::ErrorInst::TypeId) { return_type_id = SemIR::ErrorInst::TypeId; } switch (SemIR::InitRepr::ForType(context.sem_ir(), arg_type_id).kind) { case SemIR::InitRepr::InPlace: case SemIR::InitRepr::Dependent: // Tentatively use storage for a temporary as the return argument. // This will be replaced if necessary when we perform initialization. return_arg_ids.push_back(AddInst( context, loc_id, {.type_id = arg_type_id})); break; case SemIR::InitRepr::None: case SemIR::InitRepr::ByCopy: case SemIR::InitRepr::Incomplete: case SemIR::InitRepr::Abstract: return_arg_ids.push_back(SemIR::InstId::None); break; } } // Convert the arguments to match the parameters. auto converted_args_id = ConvertCallArgs( context, loc_id, callee_function.self_id, arg_ids, return_arg_ids, callee, *callee_specific_id, is_operator_syntax); switch (callee.special_function_kind) { case SemIR::Function::SpecialFunctionKind::Thunk: { // If we're about to form a direct call to a thunk, inline it. LoadImportRef(context, callee.thunk_decl_id()); // Name the thunk target within the enclosing scope of the thunk. auto thunk_ref_id = BuildNameRef(context, loc_id, callee.name_id, callee.thunk_decl_id(), callee_function.enclosing_specific_id); // This recurses back into `PerformCall`. However, we never form a thunk // to a thunk, so we only recurse once. return PerformThunkCall(context, loc_id, callee_function.function_id, context.inst_blocks().Get(converted_args_id), thunk_ref_id); } case SemIR::Function::SpecialFunctionKind::HasCppThunk: { return PerformCppThunkCall(context, loc_id, callee_function.function_id, context.inst_blocks().Get(converted_args_id), callee.cpp_thunk_decl_id()); } case SemIR::Function::SpecialFunctionKind::None: case SemIR::Function::SpecialFunctionKind::Builtin: case SemIR::Function::SpecialFunctionKind::CoreWitness: { return GetOrAddInst(context, loc_id, {.type_id = return_type_id, .callee_id = callee_id, .args_id = converted_args_id}); } } } // Performs a call where the callee is a generic type. If it's not a generic // type, produces a diagnostic. static auto PerformCallToNonFunction(Context& context, SemIR::LocId loc_id, SemIR::InstId callee_id, llvm::ArrayRef arg_ids) -> SemIR::InstId { auto type_inst = context.types().GetAsInst(context.insts().Get(callee_id).type_id()); CARBON_KIND_SWITCH(type_inst) { case CARBON_KIND(SemIR::CppTemplateNameType template_name): { return PerformCallToCppTemplateName(context, loc_id, template_name.decl_id, arg_ids); } case CARBON_KIND(SemIR::GenericClassType generic_class): { return PerformCallToGenericClass(context, loc_id, generic_class.class_id, generic_class.enclosing_specific_id, arg_ids); } case CARBON_KIND(SemIR::GenericInterfaceType generic_interface): { return PerformCallToGenericInterfaceOrNamedConstaint( context, loc_id, generic_interface.interface_id, generic_interface.enclosing_specific_id, arg_ids); } case CARBON_KIND(SemIR::GenericNamedConstraintType generic_constraint): { return PerformCallToGenericInterfaceOrNamedConstaint( context, loc_id, generic_constraint.named_constraint_id, generic_constraint.enclosing_specific_id, arg_ids); } default: { CARBON_DIAGNOSTIC(CallToNonCallable, Error, "value of type {0} is not callable", TypeOfInstId); context.emitter().Emit(loc_id, CallToNonCallable, callee_id); return SemIR::ErrorInst::InstId; } } } auto PerformCall(Context& context, SemIR::LocId loc_id, SemIR::InstId callee_id, llvm::ArrayRef arg_ids, bool is_operator_syntax) -> SemIR::InstId { // Try treating the callee as a function first. auto callee = GetCallee(context.sem_ir(), callee_id); CARBON_KIND_SWITCH(callee) { case CARBON_KIND(SemIR::CalleeError _): { return SemIR::ErrorInst::InstId; } case CARBON_KIND(SemIR::CalleeFunction fn): { return PerformCallToFunction(context, loc_id, callee_id, fn, arg_ids, is_operator_syntax); } case CARBON_KIND(SemIR::CalleeNonFunction _): { return PerformCallToNonFunction(context, loc_id, callee_id, arg_ids); } case CARBON_KIND(SemIR::CalleeCppOverloadSet overload): { return PerformCallToCppFunction( context, loc_id, overload.cpp_overload_set_id, overload.self_id, arg_ids, is_operator_syntax); } } } } // namespace Carbon::Check ================================================ FILE: toolchain/check/call.h ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #ifndef CARBON_TOOLCHAIN_CHECK_CALL_H_ #define CARBON_TOOLCHAIN_CHECK_CALL_H_ #include "toolchain/check/context.h" #include "toolchain/sem_ir/ids.h" namespace Carbon::Check { // Checks and builds SemIR for a call to `callee_id` with arguments `args_id`, // where the callee is a function. `is_operator_syntax` indicates that this call // was generated from an operator rather than from function call syntax, so // arguments to `ref` parameters aren't required to have `ref` tags. auto PerformCallToFunction(Context& context, SemIR::LocId loc_id, SemIR::InstId callee_id, const SemIR::CalleeFunction& callee_function, llvm::ArrayRef arg_ids, bool is_operator_syntax) -> SemIR::InstId; // Checks and builds SemIR for a call to `callee_id` with arguments `args_id`. // `is_operator_syntax` indicates that this call // was generated from an operator rather than from function call syntax, so // arguments to `ref` parameters aren't required to have `ref` tags. auto PerformCall(Context& context, SemIR::LocId loc_id, SemIR::InstId callee_id, llvm::ArrayRef arg_ids, bool is_operator_syntax = false) -> SemIR::InstId; } // namespace Carbon::Check #endif // CARBON_TOOLCHAIN_CHECK_CALL_H_ ================================================ FILE: toolchain/check/check.cpp ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #include "toolchain/check/check.h" #include #include #include "common/check.h" #include "common/map.h" #include "common/pretty_stack_trace_function.h" #include "toolchain/check/check_unit.h" #include "toolchain/check/context.h" #include "toolchain/check/cpp/import.h" #include "toolchain/check/diagnostic_emitter.h" #include "toolchain/check/diagnostic_helpers.h" #include "toolchain/diagnostics/consumer.h" #include "toolchain/diagnostics/diagnostic.h" #include "toolchain/diagnostics/format_providers.h" #include "toolchain/lex/token_kind.h" #include "toolchain/parse/node_ids.h" #include "toolchain/parse/tree.h" #include "toolchain/sem_ir/file.h" #include "toolchain/sem_ir/formatter.h" #include "toolchain/sem_ir/typed_insts.h" namespace Carbon::Check { // The package and library names, used as map keys. using ImportKey = std::pair; // Returns a key form of the package object. file_package_id is only used for // imports, not the main package declaration; as a consequence, it will be // `None` for the main package declaration. static auto GetImportKey(UnitAndImports& unit_info, PackageNameId file_package_id, Parse::Tree::PackagingNames names) -> ImportKey { auto* stores = unit_info.unit->value_stores; PackageNameId package_id = names.package_id.has_value() ? names.package_id : file_package_id; llvm::StringRef package_name; if (package_id.has_value()) { auto package_ident_id = package_id.AsIdentifierId(); package_name = package_ident_id.has_value() ? stores->identifiers().Get(package_ident_id) : package_id.AsSpecialName(); } llvm::StringRef library_name = names.library_id.has_value() ? stores->string_literal_values().Get(names.library_id) : ""; return {package_name, library_name}; } static constexpr llvm::StringLiteral MainPackageName = "Main"; static auto RenderImportKey(ImportKey import_key) -> std::string { if (import_key.first.empty()) { import_key.first = MainPackageName; } if (import_key.second.empty()) { return import_key.first.str(); } return llvm::formatv("{0}//{1}", import_key.first, import_key.second).str(); } // Marks an import as required on both the source and target file. // // The ID comparisons between the import and unit are okay because they both // come from the same file. static auto TrackImport(Map& api_map, Map* explicit_import_map, UnitAndImports& unit_info, Parse::Tree::PackagingNames import, bool fuzzing) -> void { if (import.package_id == PackageNameId::Cpp) { if (!explicit_import_map) { // Don't diagnose the implicit import in `impl package Cpp`, because we'll // have diagnosed the use of `Cpp` in the declaration. return; } if (fuzzing) { // Clang is not crash-resilient. CARBON_DIAGNOSTIC(CppInteropFuzzing, Error, "`Cpp` import found during fuzzing"); unit_info.emitter.Emit(import.node_id, CppInteropFuzzing); return; } unit_info.cpp_imports.push_back(import); return; } if (import.inline_body_id.has_value()) { CARBON_DIAGNOSTIC(InlineImportNotCpp, Error, "`inline` import not in package `Cpp`"); unit_info.emitter.Emit(import.node_id, InlineImportNotCpp); return; } const auto& packaging = unit_info.parse_tree().packaging_decl(); PackageNameId file_package_id = packaging ? packaging->names.package_id : PackageNameId::None; const auto import_key = GetImportKey(unit_info, file_package_id, import); // True if the import has `Main` as the package name, even if it comes from // the file's packaging (diagnostics may differentiate). bool is_explicit_main = import_key.first == MainPackageName; // Explicit imports need more validation than implicit ones. We try to do // these in an order of imports that should be removed, followed by imports // that might be valid with syntax fixes. if (explicit_import_map) { // Diagnose redundant imports. if (auto insert_result = explicit_import_map->Insert(import_key, import.node_id); !insert_result.is_inserted()) { CARBON_DIAGNOSTIC(RepeatedImport, Error, "library imported more than once"); CARBON_DIAGNOSTIC(FirstImported, Note, "first import here"); unit_info.emitter.Build(import.node_id, RepeatedImport) .Note(insert_result.value(), FirstImported) .Emit(); return; } // True if the file's package is implicitly `Main` (by omitting an explicit // package name). bool is_file_implicit_main = !packaging || !packaging->names.package_id.has_value(); // True if the import is using implicit "current package" syntax (by // omitting an explicit package name). bool is_import_implicit_current_package = !import.package_id.has_value(); // True if the import is using `default` library syntax. bool is_import_default_library = !import.library_id.has_value(); // True if the import and file point at the same package, even by // incorrectly specifying the current package name to `import`. bool is_same_package = is_import_implicit_current_package || import.package_id == file_package_id; // True if the import points at the same library as the file's library. bool is_same_library = is_same_package && (packaging ? import.library_id == packaging->names.library_id : is_import_default_library); // Diagnose explicit imports of the same library, whether from `api` or // `impl`. if (is_same_library) { CARBON_DIAGNOSTIC(ExplicitImportApi, Error, "explicit import of `api` from `impl` file is " "redundant with implicit import"); CARBON_DIAGNOSTIC(ImportSelf, Error, "file cannot import itself"); bool is_impl = !packaging || packaging->is_impl; unit_info.emitter.Emit(import.node_id, is_impl ? ExplicitImportApi : ImportSelf); return; } // Diagnose explicit imports of `Main//default`. There is no `api` for it. // This lets other diagnostics handle explicit `Main` package naming. if (is_file_implicit_main && is_import_implicit_current_package && is_import_default_library) { CARBON_DIAGNOSTIC(ImportMainDefaultLibrary, Error, "cannot import `Main//default`"); unit_info.emitter.Emit(import.node_id, ImportMainDefaultLibrary); return; } if (!is_import_implicit_current_package) { // Diagnose explicit imports of the same package that use the package // name. if (is_same_package || (is_file_implicit_main && is_explicit_main)) { CARBON_DIAGNOSTIC( ImportCurrentPackageByName, Error, "imports from the current package must omit the package name"); unit_info.emitter.Emit(import.node_id, ImportCurrentPackageByName); return; } // Diagnose explicit imports from `Main`. if (is_explicit_main) { CARBON_DIAGNOSTIC(ImportMainPackage, Error, "cannot import `Main` from other packages"); unit_info.emitter.Emit(import.node_id, ImportMainPackage); return; } } } else if (is_explicit_main) { // An implicit import with an explicit `Main` occurs when a `package` rule // has bad syntax, which will have been diagnosed when building the API map. // As a consequence, we return silently. return; } // Get the package imports, or create them if this is the first. auto create_imports = [&]() -> int32_t { int32_t index = unit_info.package_imports.size(); unit_info.package_imports.push_back( PackageImports(import.package_id, import.node_id)); return index; }; auto insert_result = unit_info.package_imports_map.Insert(import.package_id, create_imports); PackageImports& package_imports = unit_info.package_imports[insert_result.value()]; if (auto api_lookup = api_map.Lookup(import_key)) { // Add references between the file and imported api. UnitAndImports* api = api_lookup.value(); package_imports.imports.push_back({import, api}); ++unit_info.imports_remaining; api->incoming_imports.push_back(&unit_info); // If this is the implicit import, note we have it. if (!explicit_import_map) { CARBON_CHECK(!unit_info.api_for_impl); unit_info.api_for_impl = api; } } else { // The imported api is missing. package_imports.has_load_error = true; CARBON_DIAGNOSTIC(LibraryApiNotFound, Error, "corresponding API for '{0}' not found", std::string); CARBON_DIAGNOSTIC(ImportNotFound, Error, "imported API '{0}' not found", std::string); unit_info.emitter.Emit( import.node_id, explicit_import_map ? ImportNotFound : LibraryApiNotFound, RenderImportKey(import_key)); } } // Builds a map of `api` files which might be imported. Also diagnoses issues // related to the packaging because the strings are loaded as part of getting // the ImportKey (which we then do for `impl` files too). static auto BuildApiMapAndDiagnosePackaging( llvm::MutableArrayRef unit_infos) -> Map { Map api_map; for (auto& unit_info : unit_infos) { const auto& packaging = unit_info.parse_tree().packaging_decl(); // An import key formed from the `package` or `library` declaration. Or, for // Main//default, a placeholder key. auto import_key = packaging ? GetImportKey(unit_info, PackageNameId::None, packaging->names) // Construct a boring key for Main//default. : ImportKey{"", ""}; // Diagnose restricted package names before they become marked as possible // APIs. if (import_key.first == MainPackageName) { CARBON_DIAGNOSTIC(ExplicitMainPackage, Error, "`Main//default` must omit `package` declaration"); CARBON_DIAGNOSTIC( ExplicitMainLibrary, Error, "use `library` declaration in `Main` package libraries"); unit_info.emitter.Emit(packaging->names.node_id, import_key.second.empty() ? ExplicitMainPackage : ExplicitMainLibrary); continue; } if (packaging && packaging->names.package_id == PackageNameId::Cpp) { CARBON_DIAGNOSTIC(CppPackageDeclaration, Error, "`Cpp` cannot be used by a `package` declaration"); unit_info.emitter.Emit(packaging->names.node_id, CppPackageDeclaration); continue; } bool is_impl = packaging && packaging->is_impl; // Add to the `api` map and diagnose duplicates. This occurs before the // file extension check because we might emit both diagnostics in situations // where the user forgets (or has syntax errors with) a package line // multiple times. if (!is_impl) { auto insert_result = api_map.Insert(import_key, &unit_info); if (!insert_result.is_inserted()) { llvm::StringRef prev_filename = insert_result.value()->source().filename(); if (packaging) { CARBON_DIAGNOSTIC(DuplicateLibraryApi, Error, "library's API previously provided by `{0}`", std::string); unit_info.emitter.Emit(packaging->names.node_id, DuplicateLibraryApi, prev_filename.str()); } else { CARBON_DIAGNOSTIC(DuplicateMainApi, Error, "`Main//default` previously provided by `{0}`", std::string); // Use `NodeId::None` because there's no node to associate with. unit_info.emitter.Emit(Parse::NodeId::None, DuplicateMainApi, prev_filename.str()); } } } // Validate file extensions. Note imports rely the packaging declaration, // not the extension. If the input is not a regular file, for example // because it is stdin, no filename checking is performed. if (unit_info.source().is_regular_file()) { auto filename = unit_info.source().filename(); static constexpr llvm::StringLiteral ApiExt = ".carbon"; static constexpr llvm::StringLiteral ImplExt = ".impl.carbon"; bool is_api_with_impl_ext = !is_impl && filename.ends_with(ImplExt); auto want_ext = is_impl ? ImplExt : ApiExt; if (is_api_with_impl_ext || !filename.ends_with(want_ext)) { CARBON_DIAGNOSTIC( IncorrectExtension, Error, "file extension of `{0:.impl|}.carbon` required for {0:`impl`|api}", Diagnostics::BoolAsSelect); auto diag = unit_info.emitter.Build( packaging ? packaging->names.node_id : Parse::NodeId::None, IncorrectExtension, is_impl); if (is_api_with_impl_ext) { CARBON_DIAGNOSTIC( IncorrectExtensionImplNote, Note, "file extension of `.impl.carbon` only allowed for `impl`"); diag.Note(Parse::NodeId::None, IncorrectExtensionImplNote); } diag.Emit(); } } } return api_map; } // Handles printing of formatted SemIR. static auto MaybeDumpFormattedSemIR( const SemIR::File& sem_ir, int total_ir_count, Parse::GetTreeAndSubtreesFn tree_and_subtrees_getter, bool include_in_dumps, const CheckParseTreesOptions& options) -> void { bool dump = options.dump_stream && include_in_dumps; if (!options.vlog_stream && !dump) { return; } const auto& tokens = sem_ir.parse_tree().tokens(); if (options.dump_sem_ir_ranges == CheckParseTreesOptions::DumpSemIRRanges::Only && !tokens.has_dump_sem_ir_ranges() && !tokens.has_include_in_dumps()) { return; } bool use_dump_sem_ir_ranges = options.dump_sem_ir_ranges != CheckParseTreesOptions::DumpSemIRRanges::Ignore && tokens.has_dump_sem_ir_ranges(); SemIR::Formatter formatter(&sem_ir, total_ir_count, tree_and_subtrees_getter, options.include_in_dumps, use_dump_sem_ir_ranges); formatter.Format(); if (options.vlog_stream) { CARBON_VLOG_TO(options.vlog_stream, "*** SemIR::File ***\n"); formatter.Write(*options.vlog_stream); } if (dump) { formatter.Write(*options.dump_stream); } } // Handles options for dumping SemIR, including verbose output. static auto MaybeDumpSemIR( llvm::ArrayRef units, const Parse::GetTreeAndSubtreesStore& tree_and_subtrees_getters, const CheckParseTreesOptions& options) -> void { if (!options.vlog_stream && !options.dump_stream && !options.raw_dump_stream) { return; } // Flush diagnostics before printing. for (const auto& unit : units) { unit.consumer->Flush(); } for (const auto& unit : units) { bool include_in_dumps = options.include_in_dumps->Get(unit.sem_ir->check_ir_id()); if (include_in_dumps && options.raw_dump_stream) { unit.sem_ir->Print(*options.raw_dump_stream, options.dump_raw_sem_ir_builtins); } MaybeDumpFormattedSemIR( *unit.sem_ir, units.size(), tree_and_subtrees_getters.Get(unit.sem_ir->check_ir_id()), include_in_dumps, options); } } // Handles options for dumping C++ AST. static auto MaybeDumpCppAST(llvm::ArrayRef units, const CheckParseTreesOptions& options) -> void { if (!options.dump_cpp_ast_stream) { return; } for (const Unit& unit : units) { if (options.include_in_dumps->Get(unit.sem_ir->check_ir_id())) { if (auto* cpp_file = unit.sem_ir->cpp_file()) { cpp_file->ast_context().getTranslationUnitDecl()->dump( *options.dump_cpp_ast_stream); } } } } auto CheckParseTrees( llvm::MutableArrayRef units, const Parse::GetTreeAndSubtreesStore& tree_and_subtrees_getters, llvm::IntrusiveRefCntPtr fs, const CheckParseTreesOptions& options, std::shared_ptr clang_invocation) -> void { // UnitAndImports is big due to its SmallVectors, so we default to 0 on the // stack. llvm::SmallVector unit_infos( llvm::map_range(units, [&](Unit& unit) { return UnitAndImports( &unit, tree_and_subtrees_getters.Get(unit.sem_ir->check_ir_id())); })); // Dump the raw SemIR in the event of a crash. We dump it to a separate file // to keep the stack trace manageable. PrettyStackTraceFunction sem_ir_dumper([&](llvm::raw_ostream& output) { if (!options.sem_ir_crash_dump.empty()) { output << "Dumping raw SemIR to " << options.sem_ir_crash_dump << "\n"; std::error_code error_code; llvm::raw_fd_ostream sem_ir_dump(options.sem_ir_crash_dump, error_code); if (error_code) { output << "Raw SemIR dump failed: " << error_code.category().name() << ":" << error_code.value() << ": " << error_code.message() << "\n"; } else { for (const auto& unit_info : unit_infos) { if (unit_info.is_checked && unit_info.unit->sem_ir != nullptr) { unit_info.unit->sem_ir->Print(sem_ir_dump); } } } } }); Map api_map = BuildApiMapAndDiagnosePackaging(unit_infos); // Mark down imports for all files. llvm::SmallVector ready_to_check; ready_to_check.reserve(units.size()); for (auto& unit_info : unit_infos) { const auto& packaging = unit_info.parse_tree().packaging_decl(); if (packaging && packaging->is_impl) { // An `impl` has an implicit import of its `api`. auto implicit_names = packaging->names; implicit_names.package_id = PackageNameId::None; TrackImport(api_map, nullptr, unit_info, implicit_names, options.fuzzing); } Map explicit_import_map; // Add the prelude import. It's added to explicit_import_map so that it can // conflict with an explicit import of the prelude. if (options.prelude_import && !(packaging && packaging->names.package_id == PackageNameId::Core)) { auto prelude_id = unit_info.unit->value_stores->string_literal_values().Add("prelude"); TrackImport(api_map, &explicit_import_map, unit_info, {.node_id = Parse::NoneNodeId(), .package_id = PackageNameId::Core, .library_id = prelude_id}, options.fuzzing); } for (const auto& import : unit_info.parse_tree().imports()) { TrackImport(api_map, &explicit_import_map, unit_info, import, options.fuzzing); } // If there were no imports, mark the file as ready to check for below. if (unit_info.imports_remaining == 0) { ready_to_check.push_back(&unit_info); } } // Check everything with no dependencies. Earlier entries with dependencies // will be checked as soon as all their dependencies have been checked. for (int check_index = 0; check_index < static_cast(ready_to_check.size()); ++check_index) { auto* unit_info = ready_to_check[check_index]; CheckUnit(unit_info, &tree_and_subtrees_getters, fs, unit_info->unit->llvm_context, clang_invocation, options.vlog_stream) .Run(); for (auto* incoming_import : unit_info->incoming_imports) { --incoming_import->imports_remaining; if (incoming_import->imports_remaining == 0) { ready_to_check.push_back(incoming_import); } } } // If there are still units with remaining imports, it means there's a // dependency loop. if (ready_to_check.size() < unit_infos.size()) { // Go through units and mask out unevaluated imports. This breaks everything // associated with a loop equivalently, whether it's part of it or depending // on a part of it. // TODO: Better identify cycles, maybe try to untangle them. for (auto& unit_info : unit_infos) { if (unit_info.imports_remaining > 0) { for (auto& package_imports : unit_info.package_imports) { for (auto* import_it = package_imports.imports.begin(); import_it != package_imports.imports.end();) { if (import_it->unit_info->is_checked) { // The import is checked, so continue. ++import_it; } else { // The import hasn't been checked, indicating a cycle. CARBON_DIAGNOSTIC(ImportCycleDetected, Error, "import cannot be used due to a cycle; cycle " "must be fixed to import"); unit_info.emitter.Emit(import_it->names.node_id, ImportCycleDetected); // Make this look the same as an import which wasn't found. package_imports.has_load_error = true; if (unit_info.api_for_impl == import_it->unit_info) { unit_info.api_for_impl = nullptr; } import_it = package_imports.imports.erase(import_it); } } } } } // Check the remaining file contents, which are probably broken due to // incomplete imports. for (auto& unit_info : unit_infos) { if (unit_info.imports_remaining > 0) { CheckUnit(&unit_info, &tree_and_subtrees_getters, fs, unit_info.unit->llvm_context, clang_invocation, options.vlog_stream) .Run(); } } } MaybeDumpSemIR(units, tree_and_subtrees_getters, options); MaybeDumpCppAST(units, options); } } // namespace Carbon::Check ================================================ FILE: toolchain/check/check.h ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #ifndef CARBON_TOOLCHAIN_CHECK_CHECK_H_ #define CARBON_TOOLCHAIN_CHECK_CHECK_H_ #include "clang/Frontend/CompilerInvocation.h" #include "common/ostream.h" #include "toolchain/base/shared_value_stores.h" #include "toolchain/base/timings.h" #include "toolchain/check/diagnostic_emitter.h" #include "toolchain/diagnostics/emitter.h" #include "toolchain/parse/tree_and_subtrees.h" #include "toolchain/sem_ir/file.h" #include "toolchain/sem_ir/ids.h" namespace Carbon::Check { // Checking information that's tracked per file. All members are caller-owned. // Other than `timings`, members must be non-null. struct Unit { Diagnostics::Consumer* consumer; SharedValueStores* value_stores; // The `timings` may be null if nothing is to be recorded. Timings* timings; // The unit's SemIR, provided as empty and filled in by CheckParseTrees. SemIR::File* sem_ir; llvm::LLVMContext* llvm_context; // The total number of files. int total_ir_count; }; struct CheckParseTreesOptions { // Options must be set individually, not through initialization. explicit CheckParseTreesOptions() = default; // Whether to import the prelude. bool prelude_import = false; // If set, enables verbose output. llvm::raw_ostream* vlog_stream = nullptr; // Whether fuzzing is being run. Used to disable features we don't want to // fuzz. bool fuzzing = false; // Whether to include each unit in dumps. This is required when dumping // (either of `dump_stream` or `raw_dump_stream`), and must have entries based // on CheckIRId. const FixedSizeValueStore* include_in_dumps = nullptr; // If set, SemIR will be dumped to this. llvm::raw_ostream* dump_stream = nullptr; // If set, C++ AST will be dumped to this. llvm::raw_ostream* dump_cpp_ast_stream = nullptr; // When dumping textual SemIR (or printing it to for verbose output), whether // to use ranges. enum class DumpSemIRRanges : int8_t { IfPresent, Only, Ignore, }; DumpSemIRRanges dump_sem_ir_ranges = DumpSemIRRanges::IfPresent; // If set, raw SemIR will be dumped to this. llvm::raw_ostream* raw_dump_stream = nullptr; // When dumping raw SemIR, whether to include builtins. bool dump_raw_sem_ir_builtins = false; // If not empty, a raw SemIR dump should be written to this path in the event // of a crash. llvm::StringRef sem_ir_crash_dump; }; // Checks a group of parse trees. This will use imports to decide the order of // checking. // // `units` will only contain units which should be checked, and is not indexed // by `CheckIRId`. auto CheckParseTrees( llvm::MutableArrayRef units, const Parse::GetTreeAndSubtreesStore& tree_and_subtrees_getters, llvm::IntrusiveRefCntPtr fs, const CheckParseTreesOptions& options, std::shared_ptr clang_invocation) -> void; } // namespace Carbon::Check #endif // CARBON_TOOLCHAIN_CHECK_CHECK_H_ ================================================ FILE: toolchain/check/check_fuzzer.cpp ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #include "common/exe_path.h" #include "llvm/ADT/StringRef.h" #include "llvm/Support/MemoryBuffer.h" #include "llvm/Support/VirtualFileSystem.h" #include "testing/fuzzing/libfuzzer.h" #include "toolchain/driver/driver.h" namespace Carbon::Testing { static const InstallPaths* install_paths = nullptr; // NOLINTNEXTLINE(readability-non-const-parameter): External API required types. extern "C" auto LLVMFuzzerInitialize(int* argc, char*** argv) -> int { CARBON_CHECK(*argc >= 1, "Need the `argv[0]` value to initialize!"); install_paths = new InstallPaths( InstallPaths::MakeForBazelRunfiles(FindExecutablePath((*argv)[0]))); return 0; } // NOLINTNEXTLINE: Match the documented fuzzer entry point declaration style. extern "C" int LLVMFuzzerTestOneInput(const unsigned char* data, size_t size) { // Ignore large inputs. // TODO: See tokenized_buffer_fuzzer.cpp. if (size > 100000) { return 0; } static constexpr llvm::StringLiteral TestFileName = "test.carbon"; llvm::IntrusiveRefCntPtr fs = new llvm::vfs::InMemoryFileSystem; llvm::StringRef data_ref(reinterpret_cast(data), size); CARBON_CHECK(fs->addFile( TestFileName, /*ModificationTime=*/0, llvm::MemoryBuffer::getMemBuffer(data_ref, /*BufferName=*/TestFileName, /*RequiresNullTerminator=*/false))); llvm::raw_null_ostream null_ostream; Driver driver(fs, install_paths, /*input_stream=*/nullptr, &null_ostream, &null_ostream, /*fuzzing=*/true); // TODO: Get checking to a point where it can handle invalid parse trees // without crashing. if (!driver.RunCommand({"compile", "--phase=parse", TestFileName}).success) { return 0; } driver.RunCommand({"compile", "--phase=check", TestFileName}); return 0; } } // namespace Carbon::Testing ================================================ FILE: toolchain/check/check_unit.cpp ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #include "toolchain/check/check_unit.h" #include #include #include #include #include "clang/Sema/Sema.h" #include "common/growing_range.h" #include "common/pretty_stack_trace_function.h" #include "llvm/ADT/IntrusiveRefCntPtr.h" #include "llvm/ADT/StringRef.h" #include "llvm/Support/VirtualFileSystem.h" #include "toolchain/base/fixed_size_value_store.h" #include "toolchain/base/kind_switch.h" #include "toolchain/check/cpp/generate_ast.h" #include "toolchain/check/cpp/import.h" #include "toolchain/check/diagnostic_helpers.h" #include "toolchain/check/generic.h" #include "toolchain/check/handle.h" #include "toolchain/check/impl.h" #include "toolchain/check/impl_lookup.h" #include "toolchain/check/impl_validation.h" #include "toolchain/check/import.h" #include "toolchain/check/import_ref.h" #include "toolchain/check/inst.h" #include "toolchain/check/node_id_traversal.h" #include "toolchain/check/type.h" #include "toolchain/check/type_structure.h" #include "toolchain/check/unused.h" #include "toolchain/diagnostics/diagnostic.h" #include "toolchain/sem_ir/function.h" #include "toolchain/sem_ir/ids.h" #include "toolchain/sem_ir/import_ir.h" #include "toolchain/sem_ir/typed_insts.h" namespace Carbon::Check { // Returns the number of imported IRs, to assist in Context construction. static auto GetImportedIRCount(UnitAndImports* unit_and_imports) -> int { int count = 0; for (auto& package_imports : unit_and_imports->package_imports) { count += package_imports.imports.size(); } if (!unit_and_imports->api_for_impl) { // Leave an empty slot for `ImportIRId::ApiForImpl`. ++count; } if (!unit_and_imports->cpp_imports.empty()) { // Leave an empty slot for `ImportIRId::Cpp`. ++count; } return count; } CheckUnit::CheckUnit( UnitAndImports* unit_and_imports, const Parse::GetTreeAndSubtreesStore* tree_and_subtrees_getters, llvm::IntrusiveRefCntPtr fs, llvm::LLVMContext* llvm_context, std::shared_ptr clang_invocation, llvm::raw_ostream* vlog_stream) : unit_and_imports_(unit_and_imports), tree_and_subtrees_getter_(tree_and_subtrees_getters->Get( unit_and_imports->unit->sem_ir->check_ir_id())), fs_(std::move(fs)), llvm_context_(llvm_context), clang_invocation_(std::move(clang_invocation)), emitter_(&unit_and_imports_->err_tracker, tree_and_subtrees_getters, unit_and_imports_->unit->sem_ir), context_(&emitter_, tree_and_subtrees_getter_, unit_and_imports_->unit->sem_ir, GetImportedIRCount(unit_and_imports), unit_and_imports_->unit->total_ir_count, vlog_stream) {} auto CheckUnit::Run() -> void { Timings::ScopedTiming timing(unit_and_imports_->unit->timings, "check"); // We can safely mark this as checked at the start. unit_and_imports_->is_checked = true; PrettyStackTraceFunction context_dumper( [&](llvm::raw_ostream& output) { context_.PrintForStackDump(output); }); // Add a block for the file. context_.inst_block_stack().Push(); InitPackageScopeAndImports(); // Eagerly import the impls declared in the api file to prepare to redeclare // them. ImportImplsFromApiFile(context_); if (!ProcessNodeIds()) { context_.sem_ir().set_has_errors(true); return; } FinishRun(); } auto CheckUnit::InitPackageScopeAndImports() -> void { // Importing makes many namespaces, so only canonicalize the type once. auto namespace_type_id = GetSingletonType(context_, SemIR::NamespaceType::TypeInstId); // Use the name of the package for the package scope. SemIR::NameId package_name_id = SemIR::NameId::MainPackage; const auto& packaging = context_.parse_tree().packaging_decl(); if (packaging && packaging->names.package_id.has_value()) { package_name_id = SemIR::NameId::ForPackageName(packaging->names.package_id); } // Define the package scope, with an instruction for `package` expressions to // reference. auto package_scope_id = context_.name_scopes().Add(SemIR::Namespace::PackageInstId, package_name_id, SemIR::NameScopeId::None); CARBON_CHECK(package_scope_id == SemIR::NameScopeId::Package); auto package_inst_id = AddInst(context_, Parse::NodeId::None, {.type_id = namespace_type_id, .name_scope_id = SemIR::NameScopeId::Package, .import_id = SemIR::InstId::None}); CARBON_CHECK(package_inst_id == SemIR::Namespace::PackageInstId); // Call `SetSpecialImportIRs()` to set `ImportIRId::ApiForImpl` and // `ImportIRId::Cpp` first, as required. if (unit_and_imports_->api_for_impl) { const auto& names = packaging->names; auto import_decl_id = AddInst( context_, names.node_id, {.package_id = SemIR::NameId::ForPackageName(names.package_id)}); SetSpecialImportIRs( context_, {.decl_id = import_decl_id, .is_export = false, .sem_ir = unit_and_imports_->api_for_impl->unit->sem_ir}); } else { SetSpecialImportIRs(context_, {.decl_id = SemIR::InstId::None, .sem_ir = nullptr}); } // Add import instructions for everything directly imported. Implicit imports // are handled separately. for (auto& package_imports : unit_and_imports_->package_imports) { CARBON_CHECK(!package_imports.import_decl_id.has_value()); package_imports.import_decl_id = AddInst( context_, package_imports.node_id, {.package_id = SemIR::NameId::ForPackageName(package_imports.package_id)}); } // Process the imports. if (unit_and_imports_->api_for_impl) { ImportApiFile(context_, namespace_type_id, *unit_and_imports_->api_for_impl->unit->sem_ir); } ImportCurrentPackage(package_inst_id, namespace_type_id); CARBON_CHECK(context_.scope_stack().PeekIndex() == ScopeIndex::Package); ImportOtherPackages(namespace_type_id); const auto& cpp_imports = unit_and_imports_->cpp_imports; if (!cpp_imports.empty()) { ImportCpp(context_, cpp_imports, fs_, llvm_context_, clang_invocation_); } } auto CheckUnit::CollectDirectImports( llvm::SmallVector& results, CheckIRIdToIntStore& ir_to_result_index, SemIR::InstId import_decl_id, const PackageImports& imports, bool is_local) -> void { for (const auto& import : imports.imports) { const auto& direct_ir = *import.unit_info->unit->sem_ir; auto& index = ir_to_result_index.Get(direct_ir.check_ir_id()); if (index != -1) { // This should only happen when doing API imports for an implementation // file. Don't change the entry; is_export doesn't matter. continue; } index = results.size(); results.push_back({.decl_id = import_decl_id, // Only tag exports in API files, ignoring the value in // implementation files. .is_export = is_local && import.names.is_export, .sem_ir = &direct_ir}); } } auto CheckUnit::CollectTransitiveImports(SemIR::InstId import_decl_id, const PackageImports* local_imports, const PackageImports* api_imports) -> llvm::SmallVector { llvm::SmallVector results; // Track whether an IR was imported in full, including `export import`. This // distinguishes from IRs that are indirectly added without all names being // exported to this IR. auto ir_to_result_index = CheckIRIdToIntStore::MakeWithExplicitSize( unit_and_imports_->unit->total_ir_count, -1); // First add direct imports. This means that if an entity is imported both // directly and indirectly, the import path will reflect the direct import. if (local_imports) { CollectDirectImports(results, ir_to_result_index, import_decl_id, *local_imports, /*is_local=*/true); } if (api_imports) { CollectDirectImports(results, ir_to_result_index, import_decl_id, *api_imports, /*is_local=*/false); } // Loop through direct imports for any indirect exports. The underlying vector // is appended during iteration, so take the size first. const int direct_imports = results.size(); for (int direct_index : llvm::seq(direct_imports)) { bool is_export = results[direct_index].is_export; for (const auto& indirect_ir : results[direct_index].sem_ir->import_irs().values()) { if (!indirect_ir.is_export) { continue; } auto& indirect_index = ir_to_result_index.Get(indirect_ir.sem_ir->check_ir_id()); if (indirect_index == -1) { indirect_index = results.size(); // TODO: In the case of a recursive `export import`, this only points at // the outermost import. May want something that better reflects the // recursion. results.push_back({.decl_id = results[direct_index].decl_id, .is_export = is_export, .sem_ir = indirect_ir.sem_ir}); } else if (is_export) { results[indirect_index].is_export = true; } } } return results; } auto CheckUnit::ImportCurrentPackage(SemIR::InstId package_inst_id, SemIR::TypeId namespace_type_id) -> void { // Add imports from the current package. auto import_map_lookup = unit_and_imports_->package_imports_map.Lookup(PackageNameId::None); if (!import_map_lookup) { // Push the scope; there are no names to add. context_.scope_stack().PushForEntity( package_inst_id, SemIR::NameScopeId::Package, SemIR::SpecificId::None, /*lexical_lookup_has_load_error=*/false); return; } PackageImports& self_import = unit_and_imports_->package_imports[import_map_lookup.value()]; if (self_import.has_load_error) { context_.name_scopes().Get(SemIR::NameScopeId::Package).set_has_error(); } ImportLibrariesFromCurrentPackage( context_, namespace_type_id, CollectTransitiveImports(self_import.import_decl_id, &self_import, /*api_imports=*/nullptr)); context_.scope_stack().PushForEntity( package_inst_id, SemIR::NameScopeId::Package, SemIR::SpecificId::None, context_.name_scopes().Get(SemIR::NameScopeId::Package).has_error()); } auto CheckUnit::ImportOtherPackages(SemIR::TypeId namespace_type_id) -> void { // api_imports_list is initially the size of the current file's imports, // including for API files, for simplicity in iteration. It's only really used // when processing an implementation file, in order to combine the API file // imports. // // For packages imported by the API file, the PackageNameId is the package // name and the index is into the API's import list. Otherwise, the initial // {None, -1} state remains. llvm::SmallVector> api_imports_list; api_imports_list.resize(unit_and_imports_->package_imports.size(), {PackageNameId::None, -1}); // When there's an API file, add the mapping to api_imports_list. if (unit_and_imports_->api_for_impl) { const auto& api_identifiers = unit_and_imports_->api_for_impl->unit->value_stores->identifiers(); auto& impl_identifiers = unit_and_imports_->unit->value_stores->identifiers(); for (auto [api_imports_index, api_imports] : llvm::enumerate(unit_and_imports_->api_for_impl->package_imports)) { // Skip the current package. if (!api_imports.package_id.has_value()) { continue; } // Translate the package ID from the API file to the implementation file. auto impl_package_id = api_imports.package_id; if (auto package_identifier_id = impl_package_id.AsIdentifierId(); package_identifier_id.has_value()) { impl_package_id = PackageNameId::ForIdentifier( impl_identifiers.Add(api_identifiers.Get(package_identifier_id))); } if (auto lookup = unit_and_imports_->package_imports_map.Lookup(impl_package_id)) { // On a hit, replace the entry to unify the API and implementation // imports. api_imports_list[lookup.value()] = {impl_package_id, api_imports_index}; } else { // On a miss, add the package as API-only. api_imports_list.push_back({impl_package_id, api_imports_index}); } } } for (auto [i, api_imports_entry] : llvm::enumerate(api_imports_list)) { // These variables are updated after figuring out which imports are present. auto import_decl_id = SemIR::InstId::None; PackageNameId package_id = PackageNameId::None; bool has_load_error = false; // Identify the local package imports if present. PackageImports* local_imports = nullptr; if (i < unit_and_imports_->package_imports.size()) { local_imports = &unit_and_imports_->package_imports[i]; if (!local_imports->package_id.has_value()) { // Skip the current package. continue; } import_decl_id = local_imports->import_decl_id; package_id = local_imports->package_id; has_load_error |= local_imports->has_load_error; } // Identify the API package imports if present. PackageImports* api_imports = nullptr; if (api_imports_entry.second != -1) { api_imports = &unit_and_imports_->api_for_impl ->package_imports[api_imports_entry.second]; if (local_imports) { CARBON_CHECK(package_id == api_imports_entry.first); } else { auto import_ir_inst_id = context_.import_ir_insts().Add(SemIR::ImportIRInst( SemIR::ImportIRId::ApiForImpl, api_imports->import_decl_id)); import_decl_id = AddInst(context_, MakeImportedLocIdAndInst( context_, import_ir_inst_id, {.package_id = SemIR::NameId::ForPackageName( api_imports_entry.first)})); package_id = api_imports_entry.first; } has_load_error |= api_imports->has_load_error; } // Do the actual import. ImportLibrariesFromOtherPackage( context_, namespace_type_id, import_decl_id, package_id, CollectTransitiveImports(import_decl_id, local_imports, api_imports), has_load_error); } } // Loops over all nodes in the tree. On some errors, this may return early, // for example if an unrecoverable state is encountered. auto CheckUnit::ProcessNodeIds() -> bool { NodeIdTraversal traversal(&context_); Parse::NodeId node_id = Parse::NodeId::None; // On crash, report which token we were handling. PrettyStackTraceFunction node_dumper([&](llvm::raw_ostream& output) { const auto& tree = tree_and_subtrees_getter_(); auto converted = tree.NodeToDiagnosticLoc(node_id, /*token_only=*/false); converted.loc.FormatLocation(output); output << "Checking " << context_.parse_tree().node_kind(node_id) << "\n"; // Crash output has a tab indent; try to indent slightly past that. converted.loc.FormatSnippet(output, /*indent=*/10); }); while (auto maybe_node_id = traversal.Next()) { node_id = *maybe_node_id; emitter_.AdvanceToken(context_.parse_tree().node_token(node_id)); if (context_.parse_tree().node_has_error(node_id)) { context_.TODO(node_id, "handle invalid parse trees in `check`"); return false; } bool result; auto parse_kind = context_.parse_tree().node_kind(node_id); switch (parse_kind) { #define CARBON_PARSE_NODE_KIND(Name) \ case Parse::NodeKind::Name: { \ result = HandleParseNode( \ context_, context_.parse_tree().As(node_id)); \ break; \ } #include "toolchain/parse/node_kind.def" } if (!result) { CARBON_CHECK( unit_and_imports_->err_tracker.seen_error(), "HandleParseNode for `{0}` returned false without diagnosing.", parse_kind); return false; } traversal.Handle(parse_kind); } return true; } auto CheckUnit::CheckRequiredDeclarations() -> void { for (const auto& function : context_.functions().values()) { if (!function.first_owning_decl_id.has_value() && function.extern_library_id == context_.sem_ir().library_id()) { auto function_import_id = context_.insts().GetImportSource(function.non_owning_decl_id); CARBON_CHECK(function_import_id.has_value()); auto import_ir_id = context_.sem_ir().import_ir_insts().Get(function_import_id).ir_id(); auto& import_ir = context_.import_irs().Get(import_ir_id); if (import_ir.sem_ir->package_id().has_value() != context_.sem_ir().package_id().has_value()) { continue; } CARBON_DIAGNOSTIC( MissingOwningDeclarationInApi, Error, "owning declaration required for non-owning declaration"); if (!import_ir.sem_ir->package_id().has_value() && !context_.sem_ir().package_id().has_value()) { emitter_.Emit(function.non_owning_decl_id, MissingOwningDeclarationInApi); continue; } if (import_ir.sem_ir->identifiers().Get( import_ir.sem_ir->package_id().AsIdentifierId()) == context_.sem_ir().identifiers().Get( context_.sem_ir().package_id().AsIdentifierId())) { emitter_.Emit(function.non_owning_decl_id, MissingOwningDeclarationInApi); } } } } auto CheckUnit::CheckRequiredDefinitions() -> void { CARBON_DIAGNOSTIC(MissingDefinitionInImpl, Error, "no definition found for declaration in impl file"); for (SemIR::InstId decl_inst_id : context_.definitions_required_by_decl()) { SemIR::Inst decl_inst = context_.insts().Get(decl_inst_id); CARBON_KIND_SWITCH(context_.insts().Get(decl_inst_id)) { case CARBON_KIND(SemIR::ClassDecl class_decl): { if (!context_.classes().Get(class_decl.class_id).is_complete()) { emitter_.Emit(decl_inst_id, MissingDefinitionInImpl); } break; } case CARBON_KIND(SemIR::FunctionDecl function_decl): { if (context_.functions().Get(function_decl.function_id).definition_id == SemIR::InstId::None) { emitter_.Emit(decl_inst_id, MissingDefinitionInImpl); } break; } case CARBON_KIND(SemIR::ImplDecl impl_decl): { auto& impl = context_.impls().Get(impl_decl.impl_id); if (!impl.is_complete()) { FillImplWitnessWithErrors(context_, impl); CARBON_DIAGNOSTIC(ImplMissingDefinition, Error, "impl declared but not defined"); emitter_.Emit(decl_inst_id, ImplMissingDefinition); } break; } case SemIR::InterfaceDecl::Kind: { // TODO: Handle `interface` as well, once we can test it without // triggering // https://github.com/carbon-language/carbon-lang/issues/4071. CARBON_FATAL("TODO: Support interfaces in DiagnoseMissingDefinitions"); } default: { CARBON_FATAL("Unexpected inst in definitions_required_by_decl: {0}", decl_inst); } } } for (auto [loc, specific_id] : GrowingRange(context_.definitions_required_by_use())) { // This is using the location for the use. We could track the // list of enclosing locations if this was used from a generic. if (!ResolveSpecificDefinition(context_, loc, specific_id)) { CARBON_DIAGNOSTIC(MissingGenericFunctionDefinition, Error, "use of undefined generic function"); CARBON_DIAGNOSTIC(MissingGenericFunctionDefinitionHere, Note, "generic function declared here"); auto generic_decl_id = context_.generics() .Get(context_.specifics().Get(specific_id).generic_id) .decl_id; emitter_.Build(loc, MissingGenericFunctionDefinition) .Note(generic_decl_id, MissingGenericFunctionDefinitionHere) .Emit(); } } } auto CheckUnit::CheckPoisonedConcreteImplLookupQueries() -> void { // Impl lookup can generate instructions (via deduce) which we don't use, as // we're only generating diagnostics here, so we catch and discard them. context_.inst_block_stack().Push(); auto poisoned_queries = std::exchange(context_.poisoned_concrete_impl_lookup_queries(), {}); for (const auto& poison : poisoned_queries) { auto witness_result = EvalLookupSingleImplWitness( context_, poison.loc_id, poison.query, poison.query.query_self_inst_id, EvalImplLookupMode::RecheckPoisonedLookup); CARBON_CHECK(witness_result.has_final_value()); auto found_witness_id = witness_result.final_witness(); if (found_witness_id == SemIR::ErrorInst::InstId) { // Errors may have been diagnosed with the impl used in the poisoned query // in the meantime (such as a missing definition). continue; } if (found_witness_id != poison.impl_witness) { auto witness_to_impl_id = [&](SemIR::InstId witness_id) { auto table_id = context_.insts() .GetAs(witness_id) .witness_table_id; return context_.insts() .GetAs(table_id) .impl_id; }; // We can get the `Impl` from the resulting witness here, which is the // `Impl` that conflicts with the previous poison query. auto bad_impl_id = witness_to_impl_id(found_witness_id); const auto& bad_impl = context_.impls().Get(bad_impl_id); auto prev_impl_id = witness_to_impl_id(poison.impl_witness); const auto& prev_impl = context_.impls().Get(prev_impl_id); CARBON_DIAGNOSTIC( PoisonedImplLookupConcreteResult, Error, "found `impl` that would change the result of an earlier " "use of `{0} as {1}`", InstIdAsRawType, SpecificInterfaceIdAsRawType); auto builder = emitter_.Build(poison.loc_id, PoisonedImplLookupConcreteResult, poison.query.query_self_inst_id, poison.query.query_specific_interface_id); CARBON_DIAGNOSTIC( PoisonedImplLookupConcreteResultNoteBadImpl, Note, "the use would select the `impl` here but it was not found yet"); builder.Note(bad_impl.first_decl_id(), PoisonedImplLookupConcreteResultNoteBadImpl); CARBON_DIAGNOSTIC(PoisonedImplLookupConcreteResultNotePreviousImpl, Note, "the use had selected the `impl` here"); builder.Note(prev_impl.first_decl_id(), PoisonedImplLookupConcreteResultNotePreviousImpl); builder.Emit(); } } context_.inst_block_stack().PopAndDiscard(); } auto CheckUnit::CheckImpls() -> void { ValidateImplsInFile(context_); } auto CheckUnit::FinishRun() -> void { CheckRequiredDeclarations(); CheckRequiredDefinitions(); CheckPoisonedConcreteImplLookupQueries(); CheckImpls(); // Finalizes the C++ portion of the compilation. FinishAst(context_); // Pop information for the file-level scope. context_.sem_ir().set_top_inst_block_id(context_.inst_block_stack().Pop()); context_.scope_stack().Pop(/*check_unused=*/true); // Finalizes reserved blocks, using `ReservedIds` to avoid missing values. for (auto reserved_id : SemIR::InstBlockId::ReservedIds) { if (reserved_id == SemIR::InstBlockId::Empty) { continue; } if (reserved_id == SemIR::InstBlockId::GlobalInit) { context_.global_init().Finalize(); continue; } llvm::ArrayRef block; if (reserved_id == SemIR::InstBlockId::Exports) { block = context_.exports(); } else if (reserved_id == SemIR::InstBlockId::Generated) { block = context_.generated(); } else if (reserved_id == SemIR::InstBlockId::Imports) { block = context_.imports(); } else { CARBON_FATAL("Unexpected reserved InstBlockId: {0}", reserved_id); } context_.inst_blocks().ReplacePlaceholder(reserved_id, block); } emitter_.Flush(); context_.sem_ir().set_has_errors(unit_and_imports_->err_tracker.seen_error()); // Verify that Context cleanly finished. context_.VerifyOnFinish(); } } // namespace Carbon::Check ================================================ FILE: toolchain/check/check_unit.h ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #ifndef CARBON_TOOLCHAIN_CHECK_CHECK_UNIT_H_ #define CARBON_TOOLCHAIN_CHECK_CHECK_UNIT_H_ #include "clang/Frontend/CompilerInvocation.h" #include "common/map.h" #include "llvm/ADT/SmallVector.h" #include "toolchain/check/check.h" #include "toolchain/check/context.h" #include "toolchain/check/diagnostic_emitter.h" #include "toolchain/parse/node_ids.h" #include "toolchain/sem_ir/ids.h" namespace Carbon::Check { struct UnitAndImports; // A file's imports corresponding to a single package, for // `UnitAndImports::package_imports`. struct PackageImports { // A given import within the file, with its destination. struct Import { Parse::Tree::PackagingNames names; UnitAndImports* unit_info; }; // Use the constructor so that the SmallVector is only constructed // as-needed. explicit PackageImports(PackageNameId package_id, Parse::AnyPackagingDeclId node_id) : package_id(package_id), node_id(node_id) {} // The identifier of the imported package. PackageNameId package_id; // The first `package` or `import` declaration in the file, which declared the // package's identifier (even if the import failed). Used for associating // diagnostics not specific to a single import. Parse::AnyPackagingDeclId node_id; // The associated `import` instruction. Has a value after a file is checked. SemIR::InstId import_decl_id = SemIR::InstId::None; // Whether there's an import that failed to load. bool has_load_error = false; // The list of valid imports. llvm::SmallVector imports; }; // Contains information accumulated while checking a `Unit` (primarily import // information), in addition to the `Unit` itself. struct UnitAndImports { // Converts a `NodeId` to a diagnostic location for `UnitAndImports`. class NodeEmitter : public Diagnostics::Emitter { public: explicit NodeEmitter(Diagnostics::Consumer* consumer, Parse::GetTreeAndSubtreesFn tree_and_subtrees_getter) : Emitter(consumer), tree_and_subtrees_getter_(tree_and_subtrees_getter) {} protected: auto ConvertLoc(Parse::NodeId node_id, ContextFnT /*context_fn*/) const -> Diagnostics::ConvertedLoc override { return tree_and_subtrees_getter_().NodeToDiagnosticLoc( node_id, /*token_only=*/false); } private: Parse::GetTreeAndSubtreesFn tree_and_subtrees_getter_; }; explicit UnitAndImports(Unit* unit, Parse::GetTreeAndSubtreesFn tree_and_subtrees_getter) : unit(unit), err_tracker(*unit->consumer), emitter(&err_tracker, tree_and_subtrees_getter) {} auto parse_tree() -> const Parse::Tree& { return unit->sem_ir->parse_tree(); } auto source() -> const SourceBuffer& { return parse_tree().tokens().source(); } Unit* unit; // Emitter information. Diagnostics::ErrorTrackingConsumer err_tracker; NodeEmitter emitter; // List of the outgoing imports. If a package includes unavailable library // imports, it has an entry with has_load_error set. Invalid imports (for // example, `import Main;`) aren't added because they won't add identifiers to // name lookup. llvm::SmallVector package_imports; // A map of the package names to the outgoing imports above. Map package_imports_map; // List of the `import Cpp` imports. llvm::SmallVector cpp_imports; // The remaining number of imports which must be checked before this unit can // be processed. int32_t imports_remaining = 0; // A list of incoming imports. This will be empty for `impl` files, because // imports only touch `api` files. llvm::SmallVector incoming_imports; // The corresponding `api` unit if this is an `impl` file. The entry should // also be in the corresponding `PackageImports`. UnitAndImports* api_for_impl = nullptr; // Whether the unit has been checked. bool is_checked = false; }; // Handles checking of a single unit. Requires that all dependencies have been // checked. // // This mainly splits out the single-unit logic from the higher level cross-unit // logic in check.cpp. class CheckUnit { public: // `unit_and_imports` and `tree_and_subtrees_getters` must be non-null. // `vlog_stream` is optional. explicit CheckUnit( UnitAndImports* unit_and_imports, const Parse::GetTreeAndSubtreesStore* tree_and_subtrees_getters, llvm::IntrusiveRefCntPtr fs, llvm::LLVMContext* llvm_context, std::shared_ptr clang_invocation, llvm::raw_ostream* vlog_stream); // Produces and checks the IR for the provided unit. auto Run() -> void; private: using CheckIRIdToIntStore = FixedSizeValueStore; // Add imports to the root block. auto InitPackageScopeAndImports() -> void; // Collects direct imports, for CollectTransitiveImports. auto CollectDirectImports(llvm::SmallVector& results, CheckIRIdToIntStore& ir_to_result_index, SemIR::InstId import_decl_id, const PackageImports& imports, bool is_local) -> void; // Collects transitive imports, handling deduplication. These will be unified // between local_imports and api_imports. auto CollectTransitiveImports(SemIR::InstId import_decl_id, const PackageImports* local_imports, const PackageImports* api_imports) -> llvm::SmallVector; // Imports the current package. auto ImportCurrentPackage(SemIR::InstId package_inst_id, SemIR::TypeId namespace_type_id) -> void; // Imports all other Carbon packages (excluding the current package). auto ImportOtherPackages(SemIR::TypeId namespace_type_id) -> void; // Checks that each required declaration is available. This applies for // declarations that should exist in an owning library, for which an extern // declaration exists that assigns ownership to the current API. auto CheckRequiredDeclarations() -> void; // Checks that each required definition is available. If the definition can be // generated by resolving a specific, does so, otherwise emits a diagnostic // for each declaration in context.definitions_required_by_decl() and // context.definitions_required_by_use that doesn't have a definition. auto CheckRequiredDefinitions() -> void; // Re-run every impl lookup with a concrete result and make sure they find the // same witnesses. auto CheckPoisonedConcreteImplLookupQueries() -> void; // Look for `impl` declarations that are invalid. auto CheckImpls() -> void; // Does work after processing the parse tree, such as finishing the IR and // checking for missing contents. auto FinishRun() -> void; // Loops over all nodes in the tree. On some errors, this may return early, // for example if an unrecoverable state is encountered. auto ProcessNodeIds() -> bool; UnitAndImports* unit_and_imports_; Parse::GetTreeAndSubtreesFn tree_and_subtrees_getter_; llvm::IntrusiveRefCntPtr fs_; llvm::LLVMContext* llvm_context_; std::shared_ptr clang_invocation_; DiagnosticEmitter emitter_; Context context_; }; } // namespace Carbon::Check #endif // CARBON_TOOLCHAIN_CHECK_CHECK_UNIT_H_ ================================================ FILE: toolchain/check/class.cpp ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #include "toolchain/check/class.h" #include "toolchain/check/context.h" #include "toolchain/check/convert.h" #include "toolchain/check/eval.h" #include "toolchain/check/function.h" #include "toolchain/check/generic.h" #include "toolchain/check/impl.h" #include "toolchain/check/import_ref.h" #include "toolchain/check/inst.h" #include "toolchain/check/name_lookup.h" #include "toolchain/check/name_ref.h" #include "toolchain/check/pattern.h" #include "toolchain/check/pattern_match.h" #include "toolchain/check/type.h" #include "toolchain/parse/node_ids.h" #include "toolchain/sem_ir/builtin_function_kind.h" #include "toolchain/sem_ir/function.h" #include "toolchain/sem_ir/ids.h" #include "toolchain/sem_ir/typed_insts.h" namespace Carbon::Check { auto SetClassSelfType(Context& context, SemIR::ClassId class_id) -> void { auto& class_info = context.classes().Get(class_id); auto specific_id = context.generics().GetSelfSpecific(class_info.generic_id); class_info.self_type_id = GetClassType(context, class_id, specific_id); } auto StartClassDefinition(Context& context, SemIR::Class& class_info, SemIR::InstId definition_id) -> void { // Track that this declaration is the definition. CARBON_CHECK(!class_info.has_definition_started()); class_info.definition_id = definition_id; class_info.scope_id = context.name_scopes().Add( definition_id, SemIR::NameId::None, class_info.parent_scope_id); // Introduce `Self`. context.name_scopes().AddRequiredName( class_info.scope_id, SemIR::NameId::SelfType, context.types().GetTypeInstId(class_info.self_type_id)); } // Checks that the specified finished adapter definition is valid and builds and // returns a corresponding complete type witness instruction. static auto CheckCompleteAdapterClassType( Context& context, Parse::NodeId node_id, SemIR::ClassId class_id, llvm::ArrayRef field_decls, llvm::ArrayRef body) -> SemIR::InstId { const auto& class_info = context.classes().Get(class_id); if (class_info.base_id.has_value()) { CARBON_DIAGNOSTIC(AdaptWithBase, Error, "adapter with base class"); CARBON_DIAGNOSTIC(AdaptWithBaseHere, Note, "`base` declaration is here"); context.emitter() .Build(class_info.adapt_id, AdaptWithBase) .Note(class_info.base_id, AdaptWithBaseHere) .Emit(); return SemIR::ErrorInst::InstId; } if (!field_decls.empty()) { CARBON_DIAGNOSTIC(AdaptWithFields, Error, "adapter with fields"); CARBON_DIAGNOSTIC(AdaptWithFieldHere, Note, "first field declaration is here"); context.emitter() .Build(class_info.adapt_id, AdaptWithFields) .Note(field_decls.front(), AdaptWithFieldHere) .Emit(); return SemIR::ErrorInst::InstId; } for (auto inst_id : body) { if (auto function_decl = context.insts().TryGetAs(inst_id)) { auto& function = context.functions().Get(function_decl->function_id); if (function.virtual_modifier == SemIR::Function::VirtualModifier::Virtual) { CARBON_DIAGNOSTIC(AdaptWithVirtual, Error, "adapter with virtual function"); CARBON_DIAGNOSTIC(AdaptWithVirtualHere, Note, "first virtual function declaration is here"); context.emitter() .Build(class_info.adapt_id, AdaptWithVirtual) .Note(inst_id, AdaptWithVirtualHere) .Emit(); return SemIR::ErrorInst::InstId; } } } // The object representation of the adapter is the object representation // of the adapted type. auto adapted_type_id = class_info.GetAdaptedType(context.sem_ir(), SemIR::SpecificId::None); auto object_repr_id = context.types().GetObjectRepr(adapted_type_id); return AddInst( context, node_id, {.type_id = GetSingletonType(context, SemIR::WitnessType::TypeInstId), // TODO: Use InstId from the adapt declaration. .object_repr_type_inst_id = context.types().GetTypeInstId(object_repr_id)}); } static auto AddStructTypeFields( Context& context, llvm::SmallVector& struct_type_fields, llvm::ArrayRef field_decls) -> SemIR::StructTypeFieldsId { for (auto field_decl_id : field_decls) { auto field_decl = context.insts().GetAs(field_decl_id); field_decl.index = SemIR::ElementIndex{static_cast(struct_type_fields.size())}; ReplaceInstPreservingConstantValue(context, field_decl_id, field_decl); if (field_decl.type_id == SemIR::ErrorInst::TypeId) { struct_type_fields.push_back( {.name_id = field_decl.name_id, .type_inst_id = SemIR::ErrorInst::TypeInstId}); continue; } auto unbound_element_type = context.sem_ir().types().GetAs( field_decl.type_id); struct_type_fields.push_back( {.name_id = field_decl.name_id, .type_inst_id = unbound_element_type.element_type_inst_id}); } auto fields_id = context.struct_type_fields().AddCanonical(struct_type_fields); return fields_id; } // Builds and returns a vtable for the current class. Assumes that the virtual // functions for the class are listed as the top element of the `vtable_stack`. static auto BuildVtable(Context& context, Parse::ClassDefinitionId node_id, SemIR::ClassId class_id, std::optional base_class_type, llvm::ArrayRef vtable_contents) -> SemIR::VtableId { auto base_vtable_id = SemIR::VtableId::None; auto base_class_specific_id = SemIR::SpecificId::None; // Get some base class/type/specific info. if (base_class_type) { auto& base_class_info = context.classes().Get(base_class_type->class_id); auto base_vtable_decl_inst_id = base_class_info.vtable_decl_id; if (base_vtable_decl_inst_id.has_value()) { LoadImportRef(context, base_vtable_decl_inst_id); auto canonical_base_vtable_inst_id = context.constant_values().GetConstantInstId(base_vtable_decl_inst_id); const auto& base_vtable_decl_inst = context.insts().GetAs( canonical_base_vtable_inst_id); base_vtable_id = base_vtable_decl_inst.vtable_id; base_class_specific_id = base_class_type->specific_id; } } const auto& class_info = context.classes().Get(class_id); auto class_generic_id = class_info.generic_id; // Wrap vtable entries in SpecificFunctions as needed/in generic classes. auto build_specific_function = [&](SemIR::InstId fn_decl_id) -> SemIR::InstId { if (!class_generic_id.has_value()) { return fn_decl_id; } const auto& fn_decl = context.insts().GetAs(fn_decl_id); const auto& function = context.functions().Get(fn_decl.function_id); return GetOrAddInst( context, node_id, {.type_id = GetSingletonType(context, SemIR::SpecificFunctionType::TypeInstId), .callee_id = fn_decl_id, .specific_id = context.generics().GetSelfSpecific(function.generic_id)}); }; llvm::SmallVector vtable; Set implemented_impls; if (base_vtable_id.has_value()) { auto base_vtable_inst_block = context.inst_blocks().Get( context.vtables().Get(base_vtable_id).virtual_functions_id); // TODO: Avoid quadratic search. Perhaps build a map from `NameId` to the // elements of the top of `vtable_stack`. for (auto base_vtable_entry_id : base_vtable_inst_block) { auto [derived_vtable_entry_id, derived_vtable_entry_const_id, fn_id, specific_id] = DecomposeVirtualFunction(context.sem_ir(), base_vtable_entry_id, base_class_specific_id); const auto& fn = context.sem_ir().functions().Get(fn_id); const auto* i = llvm::find_if( vtable_contents, [&](SemIR::InstId override_fn_decl_id) -> bool { const auto& override_fn = context.functions().Get( context.insts() .GetAs(override_fn_decl_id) .function_id); return override_fn.virtual_modifier == SemIR::FunctionFields::VirtualModifier::Override && override_fn.name_id == fn.name_id; }); if (i != vtable_contents.end()) { auto override_fn_id = context.insts().GetAs(*i).function_id; implemented_impls.Insert(override_fn_id); auto& override_fn = context.functions().Get(override_fn_id); CheckFunctionTypeMatches(context, override_fn, fn, specific_id, /*check_syntax=*/false, /*check_self=*/false); derived_vtable_entry_id = build_specific_function(*i); override_fn.virtual_index = vtable.size(); CARBON_CHECK(override_fn.virtual_index == fn.virtual_index); } else if (auto base_vtable_specific_function = context.insts().TryGetAs( derived_vtable_entry_id)) { if (derived_vtable_entry_const_id.is_symbolic()) { // Create a new instruction here that is otherwise identical to // `derived_vtable_entry_id` but is dependent within the derived // class. This ensures we can `GetConstantValueInSpecific` for it // with the derived class's specific (when forming further derived // classes, lowering the vtable, etc). derived_vtable_entry_id = GetOrAddInst( context, node_id, {.type_id = GetSingletonType( context, SemIR::SpecificFunctionType::TypeInstId), .callee_id = base_vtable_specific_function->callee_id, .specific_id = base_vtable_specific_function->specific_id}); } } vtable.push_back(derived_vtable_entry_id); } } for (auto inst_id : vtable_contents) { auto fn_decl = context.insts().GetAs(inst_id); auto& fn = context.functions().Get(fn_decl.function_id); if (fn.virtual_modifier != SemIR::FunctionFields::VirtualModifier::Override) { fn.virtual_index = vtable.size(); vtable.push_back(build_specific_function(inst_id)); } else if (!implemented_impls.Lookup(fn_decl.function_id)) { CARBON_DIAGNOSTIC(OverrideWithoutVirtualInBase, Error, "override without compatible virtual in base class"); context.emitter().Emit(SemIR::LocId(inst_id), OverrideWithoutVirtualInBase); } } return context.vtables().Add( {{.class_id = class_id, .virtual_functions_id = context.inst_blocks().Add(vtable)}}); } // Checks that the specified finished class definition is valid and builds and // returns a corresponding complete type witness instruction. static auto CheckCompleteClassType( Context& context, Parse::ClassDefinitionId node_id, SemIR::ClassId class_id, llvm::ArrayRef field_decls, llvm::ArrayRef vtable_contents, llvm::ArrayRef body) -> SemIR::InstId { auto& class_info = context.classes().Get(class_id); if (class_info.adapt_id.has_value()) { return CheckCompleteAdapterClassType(context, node_id, class_id, field_decls, body); } bool defining_vptr = class_info.is_dynamic; auto base_type_id = class_info.GetBaseType(context.sem_ir(), SemIR::SpecificId::None); // TODO: Use InstId from base declaration. auto base_type_inst_id = context.types().GetTypeInstId(base_type_id); std::optional base_class_type; if (base_type_id.has_value()) { // TODO: If the base class is template dependent, we will need to decide // whether to add a vptr as part of instantiation. base_class_type = context.types().TryGetAs(base_type_id); if (base_class_type && context.classes().Get(base_class_type->class_id).is_dynamic) { defining_vptr = false; } } llvm::SmallVector struct_type_fields; struct_type_fields.reserve(defining_vptr + class_info.base_id.has_value() + field_decls.size()); if (defining_vptr) { struct_type_fields.push_back( {.name_id = SemIR::NameId::Vptr, .type_inst_id = context.types().GetTypeInstId( GetPointerType(context, SemIR::VtableType::TypeInstId))}); } if (base_type_id.has_value()) { auto base_decl = context.insts().GetAs(class_info.base_id); base_decl.index = SemIR::ElementIndex{static_cast(struct_type_fields.size())}; ReplaceInstPreservingConstantValue(context, class_info.base_id, base_decl); struct_type_fields.push_back( {.name_id = SemIR::NameId::Base, .type_inst_id = base_type_inst_id}); } if (class_info.is_dynamic) { auto vtable_id = BuildVtable(context, node_id, class_id, base_class_type, vtable_contents); auto vptr_type_id = GetPointerType(context, SemIR::VtableType::TypeInstId); class_info.vtable_decl_id = AddInst( context, node_id, {.type_id = vptr_type_id, .vtable_id = vtable_id}); } auto struct_type_id = GetStructType( context, AddStructTypeFields(context, struct_type_fields, field_decls)); return AddInst( context, node_id, {.type_id = GetSingletonType(context, SemIR::WitnessType::TypeInstId), .object_repr_type_inst_id = context.types().GetTypeInstId(struct_type_id)}); } auto ComputeClassObjectRepr(Context& context, Parse::ClassDefinitionId node_id, SemIR::ClassId class_id, llvm::ArrayRef field_decls, llvm::ArrayRef vtable_contents, llvm::ArrayRef body) -> void { auto complete_type_witness_id = CheckCompleteClassType( context, node_id, class_id, field_decls, vtable_contents, body); auto& class_info = context.classes().Get(class_id); class_info.complete_type_witness_id = complete_type_witness_id; } } // namespace Carbon::Check ================================================ FILE: toolchain/check/class.h ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #ifndef CARBON_TOOLCHAIN_CHECK_CLASS_H_ #define CARBON_TOOLCHAIN_CHECK_CLASS_H_ #include "toolchain/check/context.h" namespace Carbon::Check { // Sets the `Self` type for the class. auto SetClassSelfType(Context& context, SemIR::ClassId class_id) -> void; // Starts the class definition, adding `Self` to name lookup. auto StartClassDefinition(Context& context, SemIR::Class& class_info, SemIR::InstId definition_id) -> void; // Computes the object representation for a fully defined class. auto ComputeClassObjectRepr(Context& context, Parse::ClassDefinitionId node_id, SemIR::ClassId class_id, llvm::ArrayRef field_decls, llvm::ArrayRef vtable_contents, llvm::ArrayRef body) -> void; } // namespace Carbon::Check #endif // CARBON_TOOLCHAIN_CHECK_CLASS_H_ ================================================ FILE: toolchain/check/context.cpp ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #include "toolchain/check/context.h" #include #include #include "common/check.h" #include "toolchain/base/kind_switch.h" #include "toolchain/check/deferred_definition_worklist.h" #include "toolchain/sem_ir/ids.h" namespace Carbon::Check { Context::Context(DiagnosticEmitterBase* emitter, Parse::GetTreeAndSubtreesFn tree_and_subtrees_getter, SemIR::File* sem_ir, int imported_ir_count, int total_ir_count, llvm::raw_ostream* vlog_stream) : emitter_(emitter), tree_and_subtrees_getter_(tree_and_subtrees_getter), sem_ir_(sem_ir), total_ir_count_(total_ir_count), vlog_stream_(vlog_stream), node_stack_(sem_ir->parse_tree(), vlog_stream), inst_block_stack_("inst_block_stack_", *sem_ir, vlog_stream), pattern_block_stack_("pattern_block_stack_", *sem_ir, vlog_stream), param_and_arg_refs_stack_(*sem_ir, vlog_stream, node_stack_), args_type_info_stack_("args_type_info_stack_", *sem_ir, vlog_stream), decl_name_stack_(this), scope_stack_(*this), deferred_definition_worklist_(vlog_stream), generic_region_stack_(vlog_stream), vtable_stack_("vtable_stack_", *sem_ir, vlog_stream), check_ir_map_(CheckIRToImpportIRStore::MakeWithExplicitSize( total_ir_count_, SemIR::ImportIRId::None)), global_init_(this), region_stack_([this](SemIR::LocId loc_id, std::string label) { TODO(loc_id, label); }), core_identifiers_(&identifiers()) { // Prepare fields which relate to the number of IRs available for import. import_irs().Reserve(imported_ir_count); import_ir_constant_values_.reserve(imported_ir_count); } auto Context::TODO(SemIR::LocId loc_id, std::string label) -> bool { CARBON_DIAGNOSTIC(SemanticsTodo, Error, "semantics TODO: `{0}`", std::string); emitter_->Emit(loc_id, SemanticsTodo, std::move(label)); return false; } auto Context::TODO(SemIR::InstId loc_inst_id, std::string label) -> bool { return TODO(SemIR::LocId(loc_inst_id), label); } auto Context::VerifyOnFinish() const -> void { // Information in all the various context objects should be cleaned up as // various pieces of context go out of scope. At this point, nothing should // remain, so we verify stacks are empty. `node_stack_` is an exception // because it ends containing all top-level entities. inst_block_stack_.VerifyOnFinish(); pattern_block_stack_.VerifyOnFinish(); param_and_arg_refs_stack_.VerifyOnFinish(); args_type_info_stack_.VerifyOnFinish(); CARBON_CHECK(struct_type_fields_stack_.empty()); CARBON_CHECK(field_decls_stack_.empty()); decl_name_stack_.VerifyOnFinish(); decl_introducer_state_stack_.VerifyOnFinish(); scope_stack_.VerifyOnFinish(); generic_region_stack_.VerifyOnFinish(); vtable_stack_.VerifyOnFinish(); region_stack_.VerifyOnFinish(); CARBON_CHECK(impl_lookup_stack_.empty()); CARBON_CHECK(return_form_expr_ == std::nullopt); #ifndef NDEBUG if (auto verify = sem_ir_->Verify(); !verify.ok()) { CARBON_FATAL("{0}Built invalid semantics IR: {1}\n", sem_ir_, verify.error()); } #endif } auto Context::PrintForStackDump(llvm::raw_ostream& output) const -> void { output << "Check::Context\n"; // In a stack dump, this is probably indented by a tab. We treat that as 8 // spaces then add a couple to indent past the Context label. constexpr int Indent = 10; output.indent(Indent); output << "filename: " << tokens().source().filename() << "\n"; node_stack_.PrintForStackDump(Indent, output); inst_block_stack_.PrintForStackDump(Indent, output); pattern_block_stack_.PrintForStackDump(Indent, output); param_and_arg_refs_stack_.PrintForStackDump(Indent, output); args_type_info_stack_.PrintForStackDump(Indent, output); } } // namespace Carbon::Check ================================================ FILE: toolchain/check/context.h ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #ifndef CARBON_TOOLCHAIN_CHECK_CONTEXT_H_ #define CARBON_TOOLCHAIN_CHECK_CONTEXT_H_ #include #include "common/map.h" #include "common/ostream.h" #include "llvm/ADT/SmallVector.h" #include "toolchain/base/canonical_value_store.h" #include "toolchain/base/value_store.h" #include "toolchain/check/core_identifier.h" #include "toolchain/check/cpp/context.h" #include "toolchain/check/decl_introducer_state.h" #include "toolchain/check/decl_name_stack.h" #include "toolchain/check/deferred_definition_worklist.h" #include "toolchain/check/diagnostic_helpers.h" #include "toolchain/check/full_pattern_stack.h" #include "toolchain/check/generic_region_stack.h" #include "toolchain/check/global_init.h" #include "toolchain/check/inst_block_stack.h" #include "toolchain/check/node_stack.h" #include "toolchain/check/param_and_arg_refs_stack.h" #include "toolchain/check/region_stack.h" #include "toolchain/check/scope_stack.h" #include "toolchain/diagnostics/emitter.h" #include "toolchain/parse/node_ids.h" #include "toolchain/parse/tree.h" #include "toolchain/parse/tree_and_subtrees.h" #include "toolchain/sem_ir/facet_type_info.h" #include "toolchain/sem_ir/file.h" #include "toolchain/sem_ir/ids.h" #include "toolchain/sem_ir/import_ir.h" #include "toolchain/sem_ir/inst.h" #include "toolchain/sem_ir/name_scope.h" #include "toolchain/sem_ir/specific_interface.h" #include "toolchain/sem_ir/typed_insts.h" namespace Carbon::Check { // Context stored during check. // // This file stores state, and members objects may provide an API. Other files // may also have helpers that operate on Context. To keep this file manageable, // please put logic into other files. // // For example, consider the API for functions: // - `context.functions()`: Exposes storage of `SemIR::Function` objects. // - `toolchain/check/function.h`: Contains helper functions which use // `Check::Context`. // - `toolchain/sem_ir/function.h`: Contains helper functions which only need // `SemIR` objects, for which it's helpful not to depend on `Check::Context` // (for example, shared with lowering). class Context { public: // Stores references for work. explicit Context(DiagnosticEmitterBase* emitter, Parse::GetTreeAndSubtreesFn tree_and_subtrees_getter, SemIR::File* sem_ir, int imported_ir_count, int total_ir_count, llvm::raw_ostream* vlog_stream); // Marks an implementation TODO. Always returns false. auto TODO(SemIR::LocId loc_id, std::string label) -> bool; auto TODO(SemIR::InstId loc_inst_id, std::string label) -> bool; // Runs verification that the processing cleanly finished. auto VerifyOnFinish() const -> void; // Prints information for a stack dump. auto PrintForStackDump(llvm::raw_ostream& output) const -> void; // Get the Lex::TokenKind of a node for diagnostics. auto token_kind(Parse::NodeId node_id) -> Lex::TokenKind { return tokens().GetKind(parse_tree().node_token(node_id)); } auto emitter() -> DiagnosticEmitterBase& { return *emitter_; } auto parse_tree_and_subtrees() -> const Parse::TreeAndSubtrees& { return tree_and_subtrees_getter_(); } auto sem_ir() -> SemIR::File& { return *sem_ir_; } auto sem_ir() const -> const SemIR::File& { return *sem_ir_; } auto cpp_context() -> CppContext* { return cpp_context_.get(); } // TODO: Remove this and pass the C++ context to the constructor. auto set_cpp_context(std::unique_ptr cpp_context) { CARBON_CHECK(!cpp_context_ || !cpp_context, "Already have a C++ context"); cpp_context_ = std::move(cpp_context); } // Convenience functions for major phase data. auto parse_tree() const -> const Parse::Tree& { return sem_ir_->parse_tree(); } auto tokens() const -> const Lex::TokenizedBuffer& { return parse_tree().tokens(); } auto vlog_stream() -> llvm::raw_ostream* { return vlog_stream_; } auto node_stack() -> NodeStack& { return node_stack_; } auto inst_block_stack() -> InstBlockStack& { return inst_block_stack_; } auto pattern_block_stack() -> InstBlockStack& { return pattern_block_stack_; } auto param_and_arg_refs_stack() -> ParamAndArgRefsStack& { return param_and_arg_refs_stack_; } auto args_type_info_stack() -> InstBlockStack& { return args_type_info_stack_; } auto struct_type_fields_stack() -> ArrayStack& { return struct_type_fields_stack_; } auto field_decls_stack() -> ArrayStack& { return field_decls_stack_; } auto require_impls_stack() -> ArrayStack& { return require_impls_stack_; } auto decl_name_stack() -> DeclNameStack& { return decl_name_stack_; } auto decl_introducer_state_stack() -> DeclIntroducerStateStack& { return decl_introducer_state_stack_; } auto scope_stack() -> ScopeStack& { return scope_stack_; } // Convenience functions for frequently-used `scope_stack` members. auto break_continue_stack() -> llvm::SmallVector& { return scope_stack().break_continue_stack(); } auto full_pattern_stack() -> FullPatternStack& { return scope_stack_.full_pattern_stack(); } auto deferred_definition_worklist() -> DeferredDefinitionWorklist& { return deferred_definition_worklist_; } auto generic_region_stack() -> GenericRegionStack& { return generic_region_stack_; } auto vtable_stack() -> InstBlockStack& { return vtable_stack_; } auto exports() -> llvm::SmallVector& { return exports_; } using CheckIRToImpportIRStore = FixedSizeValueStore; auto check_ir_map() -> CheckIRToImpportIRStore& { return check_ir_map_; } auto import_ir_constant_values() -> llvm::SmallVector& { return import_ir_constant_values_; } auto definitions_required_by_decl() -> llvm::SmallVector& { return definitions_required_by_decl_; } auto definitions_required_by_use() -> llvm::SmallVector>& { return definitions_required_by_use_; } auto global_init() -> GlobalInit& { return global_init_; } auto imports() -> llvm::SmallVector& { return imports_; } auto generated() -> llvm::SmallVector& { return generated_; } // Pre-computed parts of a binding pattern. // TODO: Consider putting this behind a narrower API to guard against emitting // multiple times. struct BindingPatternInfo { // The corresponding AnyBinding inst. SemIR::InstId bind_name_id; // The region of insts that computes the type of the binding. SemIR::ExprRegionId type_expr_region_id; }; auto bind_name_map() -> Map& { return bind_name_map_; } auto var_storage_map() -> Map& { return var_storage_map_; } // During Choice typechecking, each alternative turns into a name binding on // the Choice type, but this can't be done until the full Choice type is // known. This represents each binding to be done at the end of checking the // Choice type. struct ChoiceDeferredBinding { Parse::NodeIdOneOf node_id; NameComponent name_component; }; auto choice_deferred_bindings() -> llvm::SmallVector& { return choice_deferred_bindings_; } auto region_stack() -> RegionStack& { return region_stack_; } // An ongoing impl lookup, used to ensure termination. struct ImplLookupStackEntry { SemIR::ConstantId query_self_const_id; SemIR::ConstantId query_facet_type_const_id; // The location of the impl being looked at for the stack entry. SemIR::InstId impl_loc = SemIR::InstId::None; }; auto impl_lookup_stack() -> llvm::SmallVector& { return impl_lookup_stack_; } // A map from a (self, interface) pair to a final witness. using ImplLookupCacheKey = std::pair; using ImplLookupCacheMap = Map; auto impl_lookup_cache() -> ImplLookupCacheMap& { return impl_lookup_cache_; } // An impl lookup query that resulted in a concrete witness from finding an // `impl` declaration (not though a facet value), and its result. Used to look // for conflicting `impl` declarations. struct PoisonedConcreteImplLookupQuery { // The location the LookupImplWitness originated from. SemIR::LocId loc_id; // The query for a witness of an impl for an interface. SemIR::LookupImplWitness query; // The resulting ImplWitness. SemIR::InstId impl_witness; }; auto poisoned_concrete_impl_lookup_queries() -> llvm::SmallVector& { return poisoned_concrete_impl_lookup_queries_; } // A stack that tracks the rewrite constraints from a `where` expression being // checked. The back of the stack is the currently checked `where` expression. auto rewrites_stack() -> llvm::SmallVector>& { return rewrites_stack_; } // Data about a form expression. // // TODO: consider moving this out of Context. struct FormExpr { static const FormExpr Error; // The inst ID of the form expression itself. This is always a form inst, // such as InitForm or RefForm. // TODO: Consider creating an AnyForm inst category to refer to those insts. SemIR::InstId form_inst_id; // The inst ID of the form expression's type component. SemIR::TypeInstId type_component_inst_id; // The type ID corresponding to type_component_id. SemIR::TypeId type_component_id; }; // Pushes form_expr onto the stack of return form declarations for in-progress // function declarations. // // Note: the "stack" currently can only have one element, but that restriction // can be relaxed if it becomes possible to have multiple pending return type // declarations. auto PushReturnForm(FormExpr form_expr) -> void { CARBON_CHECK(return_form_expr_ == std::nullopt, "TODO: make form_expr_ a stack if necessary"); return_form_expr_ = form_expr; } // Pops a FormExpr off the stack of return form declarations for in-progress // function declarations. auto PopReturnForm() -> FormExpr { CARBON_CHECK(return_form_expr_ != std::nullopt); return *std::exchange(return_form_expr_, std::nullopt); } auto core_identifiers() -> CoreIdentifierCache& { return core_identifiers_; } // -------------------------------------------------------------------------- // Directly expose SemIR::File data accessors for brevity in calls. // -------------------------------------------------------------------------- auto identifiers() -> SharedValueStores::IdentifierStore& { return sem_ir().identifiers(); } auto ints() -> SharedValueStores::IntStore& { return sem_ir().ints(); } auto reals() -> SharedValueStores::RealStore& { return sem_ir().reals(); } auto floats() -> SharedValueStores::FloatStore& { return sem_ir().floats(); } auto string_literal_values() -> SharedValueStores::StringLiteralStore& { return sem_ir().string_literal_values(); } auto entity_names() -> SemIR::EntityNameStore& { return sem_ir().entity_names(); } auto cpp_global_names() -> SemIR::CppGlobalVarStore& { return sem_ir().cpp_global_vars(); } auto cpp_overload_sets() -> SemIR::CppOverloadSetStore& { return sem_ir().cpp_overload_sets(); } auto functions() -> SemIR::FunctionStore& { return sem_ir().functions(); } auto classes() -> SemIR::ClassStore& { return sem_ir().classes(); } auto vtables() -> SemIR::VtableStore& { return sem_ir().vtables(); } auto interfaces() -> SemIR::InterfaceStore& { return sem_ir().interfaces(); } auto named_constraints() -> SemIR::NamedConstraintStore& { return sem_ir().named_constraints(); } auto require_impls() -> SemIR::RequireImplsStore& { return sem_ir().require_impls(); } auto require_impls_blocks() -> SemIR::RequireImplsBlockStore& { return sem_ir().require_impls_blocks(); } auto associated_constants() -> SemIR::AssociatedConstantStore& { return sem_ir().associated_constants(); } auto facet_types() -> SemIR::FacetTypeInfoStore& { return sem_ir().facet_types(); } auto identified_facet_types() -> SemIR::IdentifiedFacetTypeStore& { return sem_ir().identified_facet_types(); } auto impls() -> SemIR::ImplStore& { return sem_ir().impls(); } auto specific_interfaces() -> SemIR::SpecificInterfaceStore& { return sem_ir().specific_interfaces(); } auto generics() -> SemIR::GenericStore& { return sem_ir().generics(); } auto specifics() -> SemIR::SpecificStore& { return sem_ir().specifics(); } auto import_irs() -> SemIR::ImportIRStore& { return sem_ir().import_irs(); } auto import_ir_insts() -> SemIR::ImportIRInstStore& { return sem_ir().import_ir_insts(); } auto ast_context() -> clang::ASTContext& { return cpp_context()->ast_context(); } auto clang_sema() -> clang::Sema& { return cpp_context()->sema(); } auto clang_decls() -> SemIR::ClangDeclStore& { return sem_ir().clang_decls(); } auto names() -> SemIR::NameStoreWrapper { return sem_ir().names(); } auto name_scopes() -> SemIR::NameScopeStore& { return sem_ir().name_scopes(); } auto struct_type_fields() -> SemIR::StructTypeFieldsStore& { return sem_ir().struct_type_fields(); } auto custom_layouts() -> SemIR::CustomLayoutStore& { return sem_ir().custom_layouts(); } auto types() -> SemIR::TypeStore& { return sem_ir().types(); } // Instructions should be added with `AddInst` or `AddInstInNoBlock` from // `inst.h`. This is `const` to prevent accidental misuse. auto insts() const -> const SemIR::InstStore& { return sem_ir().insts(); } auto constant_values() -> SemIR::ConstantValueStore& { return sem_ir().constant_values(); } auto inst_blocks() -> SemIR::InstBlockStore& { return sem_ir().inst_blocks(); } auto constants() -> SemIR::ConstantStore& { return sem_ir().constants(); } // -------------------------------------------------------------------------- // End of SemIR::File members. // -------------------------------------------------------------------------- private: // Handles diagnostics. DiagnosticEmitterBase* emitter_; // Returns a lazily constructed TreeAndSubtrees. Parse::GetTreeAndSubtreesFn tree_and_subtrees_getter_; // The SemIR::File being added to. SemIR::File* sem_ir_; // The total number of files. int total_ir_count_; // The C++ checking context. std::unique_ptr cpp_context_; // Whether to print verbose output. llvm::raw_ostream* vlog_stream_; // The stack during Build. Will contain file-level parse nodes on return. NodeStack node_stack_; // The stack of instruction blocks being used for general IR generation. InstBlockStack inst_block_stack_; // The stack of instruction blocks that contain pattern instructions. InstBlockStack pattern_block_stack_; // The stack of instruction blocks being used for param and arg ref blocks. ParamAndArgRefsStack param_and_arg_refs_stack_; // The stack of instruction blocks being used for type information while // processing arguments. This is used in parallel with // param_and_arg_refs_stack_. It's used for: // - Struct literals, where we need to track names for a type separate from // the literal arguments. // - The associated entries witness table, while parsing an interface. InstBlockStack args_type_info_stack_; // The stack of StructTypeFields for in-progress StructTypeLiterals. ArrayStack struct_type_fields_stack_; // The stack of FieldDecls for in-progress Class definitions. ArrayStack field_decls_stack_; // The stack of RequireImpls for in-progress Interface and Constraint // definitions. ArrayStack require_impls_stack_; // The stack used for qualified declaration name construction. DeclNameStack decl_name_stack_; // The stack of declarations that could have modifiers. DeclIntroducerStateStack decl_introducer_state_stack_; // The stack of scopes we are currently within. ScopeStack scope_stack_; // The worklist of deferred definition tasks to perform at the end of the // enclosing deferred definition scope. DeferredDefinitionWorklist deferred_definition_worklist_; // The stack of generic regions we are currently within. GenericRegionStack generic_region_stack_; // Contains a vtable block for each `class` scope which is currently being // defined, regardless of whether the class can have virtual functions. InstBlockStack vtable_stack_; // Instructions which are operands to an `export` directive. This becomes // `InstBlockId::Exports`. llvm::SmallVector exports_; // Maps CheckIRId to ImportIRId. CheckIRToImpportIRStore check_ir_map_; // Per-import constant values. These refer to the main IR and mainly serve as // a lookup table for quick access. // // Inline 0 elements because it's expected to require heap allocation. llvm::SmallVector import_ir_constant_values_; // Declaration instructions of entities that should have definitions by the // end of the current source file. llvm::SmallVector definitions_required_by_decl_; // Entities that should have definitions by the end of the current source // file, because of a generic was used a concrete specific. This is currently // only tracking specific functions that should have a definition emitted. llvm::SmallVector> definitions_required_by_use_; // State for global initialization. GlobalInit global_init_; // Instructions which are generated as a result of imports; both `ImportRef`s // and instructions they generate. For example, when a name reference resolves // an imported function, the `ImportRefLoaded` results in a `FunctionDecl`, // and both end up here. The `FunctionDecl` shouldn't use the current block on // inst_block_stack_ because it's not tied to the referencing scope. // // This becomes `InstBlockId::Imports`. llvm::SmallVector imports_; // Entities which are generated internally to the toolchain, to represent // builtin concepts which should be dumped as part of `SemIR`. For example, // when doing destruction, the `Destroy.Op` function is generated, and will be // found here. // // This becomes `InstBlockId::Generated`. llvm::SmallVector generated_; // Map from an AnyBindingPattern inst to precomputed parts of the // pattern-match SemIR for it. Map bind_name_map_; // Map from VarPattern insts to the corresponding VarStorage insts. The // VarStorage insts are allocated, emitted, and stored in the map after // processing the enclosing full-pattern. Map var_storage_map_; // Each alternative in a Choice gets an entry here, they are stored in // declaration order. The vector is consumed and emptied at the end of the // Choice definition. // // TODO: This may need to be a stack of vectors if it becomes possible to // define a Choice type inside an alternative's parameter set. llvm::SmallVector choice_deferred_bindings_; // Stack of single-entry regions being built. RegionStack region_stack_; // Tracks all ongoing impl lookups in order to ensure that lookup terminates // via the acyclic rule and the termination rule. llvm::SmallVector impl_lookup_stack_; // Tracks a mapping from (self, interface) to witness, for queries that had // final results. ImplLookupCacheMap impl_lookup_cache_; // Tracks impl lookup queries that lead to concrete witness results, along // with those results. Used to verify that the same queries produce the same // results at the end of the file. Any difference is diagnosed. llvm::SmallVector poisoned_concrete_impl_lookup_queries_; // A map from an ImplWitnessAccess on the LHS of a rewrite constraint to its // value on the RHS. Used during checking of a `where` expression to allow // constraints to access values from earlier constraints. llvm::SmallVector> rewrites_stack_; // Declared return form for the in-progress function declaration, if any. std::optional return_form_expr_; // See `CoreIdentifierCache` for details. CoreIdentifierCache core_identifiers_; }; inline constexpr Context::FormExpr Context::FormExpr::Error = { .form_inst_id = SemIR::ErrorInst::InstId, .type_component_inst_id = SemIR::ErrorInst::TypeInstId, .type_component_id = SemIR::ErrorInst::TypeId}; } // namespace Carbon::Check #endif // CARBON_TOOLCHAIN_CHECK_CONTEXT_H_ ================================================ FILE: toolchain/check/control_flow.cpp ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #include "toolchain/check/control_flow.h" #include #include "toolchain/base/kind_switch.h" #include "toolchain/check/call.h" #include "toolchain/check/inst.h" #include "toolchain/check/name_lookup.h" #include "toolchain/check/operator.h" #include "toolchain/sem_ir/ids.h" #include "toolchain/sem_ir/typed_insts.h" namespace Carbon::Check { template static auto AddDominatedBlockAndBranchImpl(Context& context, Parse::NodeId node_id, Args... args) -> SemIR::InstBlockId { if (!context.inst_block_stack().is_current_block_reachable()) { return SemIR::InstBlockId::Unreachable; } auto block_id = context.inst_blocks().AddPlaceholder(); AddInst(context, node_id, {block_id, args...}); return block_id; } auto AddDominatedBlockAndBranch(Context& context, Parse::NodeId node_id) -> SemIR::InstBlockId { return AddDominatedBlockAndBranchImpl(context, node_id); } auto AddDominatedBlockAndBranchWithArg(Context& context, Parse::NodeId node_id, SemIR::InstId arg_id) -> SemIR::InstBlockId { return AddDominatedBlockAndBranchImpl(context, node_id, arg_id); } auto AddDominatedBlockAndBranchIf(Context& context, Parse::NodeId node_id, SemIR::InstId cond_id) -> SemIR::InstBlockId { return AddDominatedBlockAndBranchImpl(context, node_id, cond_id); } auto AddConvergenceBlockAndPush(Context& context, Parse::NodeId node_id, int num_blocks) -> void { CARBON_CHECK(num_blocks >= 2, "no convergence"); SemIR::InstBlockId new_block_id = SemIR::InstBlockId::Unreachable; for ([[maybe_unused]] auto _ : llvm::seq(num_blocks)) { if (context.inst_block_stack().is_current_block_reachable()) { if (new_block_id == SemIR::InstBlockId::Unreachable) { new_block_id = context.inst_blocks().AddPlaceholder(); } CARBON_CHECK(node_id.has_value()); AddInst(context, node_id, {.target_id = new_block_id}); } context.inst_block_stack().Pop(); } context.inst_block_stack().Push(new_block_id); context.region_stack().AddToRegion(new_block_id, node_id); } auto AddConvergenceBlockWithArgAndPush( Context& context, Parse::NodeId node_id, std::initializer_list block_args) -> SemIR::InstId { CARBON_CHECK(block_args.size() >= 2, "no convergence"); SemIR::InstBlockId new_block_id = SemIR::InstBlockId::Unreachable; for (auto arg_id : block_args) { if (context.inst_block_stack().is_current_block_reachable()) { if (new_block_id == SemIR::InstBlockId::Unreachable) { new_block_id = context.inst_blocks().AddPlaceholder(); } AddInst( context, node_id, {.target_id = new_block_id, .arg_id = arg_id}); } context.inst_block_stack().Pop(); } context.inst_block_stack().Push(new_block_id); context.region_stack().AddToRegion(new_block_id, node_id); // Acquire the result value. SemIR::TypeId result_type_id = context.insts().Get(*block_args.begin()).type_id(); return AddInst( context, node_id, {.type_id = result_type_id, .block_id = new_block_id}); } auto SetBlockArgResultBeforeConstantUse(Context& context, SemIR::InstId select_id, SemIR::InstId cond_id, SemIR::InstId if_true, SemIR::InstId if_false) -> void { CARBON_CHECK(context.insts().Is(select_id)); // Determine the constant result based on the condition value. SemIR::ConstantId const_id = SemIR::ConstantId::NotConstant; auto cond_const_id = context.constant_values().Get(cond_id); if (!cond_const_id.is_concrete()) { // Symbolic or non-constant condition means a non-constant result. } else if (auto literal = context.insts().TryGetAs( context.constant_values().GetInstId(cond_const_id))) { const_id = context.constant_values().Get( literal.value().value.ToBool() ? if_true : if_false); } else { CARBON_CHECK(cond_const_id == SemIR::ErrorInst::ConstantId, "Unexpected constant branch condition."); const_id = SemIR::ErrorInst::ConstantId; } if (const_id.is_constant()) { CARBON_VLOG_TO(context.vlog_stream(), "Constant: {0} -> {1}\n", context.insts().Get(select_id), context.constant_values().GetInstId(const_id)); context.constant_values().Set(select_id, const_id); } } auto IsCurrentPositionReachable(Context& context) -> bool { if (!context.inst_block_stack().is_current_block_reachable()) { return false; } // Our current position is at the end of a reachable block. That position is // reachable unless the previous instruction is a terminator instruction. auto block_contents = context.inst_block_stack().PeekCurrentBlockContents(); if (block_contents.empty()) { return true; } const auto& last_inst = context.insts().Get(block_contents.back()); return last_inst.kind().terminator_kind() != SemIR::TerminatorKind::Terminator; } auto MaybeAddCleanupForInst(Context& context, SemIR::InstId inst_id) -> void { if (!context.scope_stack().IsInFunctionScope()) { // Cleanup can only occur in function scopes. return; } context.scope_stack().destroy_id_stack().AppendToTop(inst_id); } // Common support for cleanup blocks. static auto AddCleanupBlock(Context& context) -> void { auto destroy_ids = context.scope_stack().destroy_id_stack().PeekArray(); // If there's nothing to destroy, add the final instruction to the current // block. if (destroy_ids.empty()) { return; } for (auto destroy_id : llvm::reverse(destroy_ids)) { // TODO: This does the `Destroy` lookup and call at every cleanup block. // Control flow can lead to the same variable being destroyed by multiple // cleanup blocks, so we'll want to avoid this in the future. BuildUnaryOperator(context, context.insts().GetLocIdForDesugaring(destroy_id), {.interface_name = CoreIdentifier::Destroy}, destroy_id); } } auto AddReturnCleanupBlock(Context& context, SemIR::LocIdAndInst loc_id_and_inst) -> void { AddCleanupBlock(context); AddInst(context, loc_id_and_inst); } } // namespace Carbon::Check ================================================ FILE: toolchain/check/control_flow.h ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #ifndef CARBON_TOOLCHAIN_CHECK_CONTROL_FLOW_H_ #define CARBON_TOOLCHAIN_CHECK_CONTROL_FLOW_H_ #include "toolchain/check/context.h" #include "toolchain/check/inst.h" #include "toolchain/parse/typed_nodes.h" #include "toolchain/sem_ir/ids.h" #include "toolchain/sem_ir/inst.h" namespace Carbon::Check { // Adds a `Branch` instruction branching to a new instruction block, and // returns the ID of the new block. All paths to the branch target must go // through the current block, though not necessarily through this branch. auto AddDominatedBlockAndBranch(Context& context, Parse::NodeId node_id) -> SemIR::InstBlockId; // Adds a `Branch` instruction branching to a new instruction block with a // value, and returns the ID of the new block. All paths to the branch target // must go through the current block. auto AddDominatedBlockAndBranchWithArg(Context& context, Parse::NodeId node_id, SemIR::InstId arg_id) -> SemIR::InstBlockId; // Adds a `BranchIf` instruction branching to a new instruction block, and // returns the ID of the new block. All paths to the branch target must go // through the current block. auto AddDominatedBlockAndBranchIf(Context& context, Parse::NodeId node_id, SemIR::InstId cond_id) -> SemIR::InstBlockId; // Handles recovergence of control flow. Adds branches from the top // `num_blocks` on the instruction block stack to a new block, pops the // existing blocks, pushes the new block onto the instruction block stack, // and adds it to the most recently pushed region. auto AddConvergenceBlockAndPush(Context& context, Parse::NodeId node_id, int num_blocks) -> void; // Handles recovergence of control flow with a result value. Adds branches // from the top few blocks on the instruction block stack to a new block, pops // the existing blocks, pushes the new block onto the instruction block // stack, and adds it to the most recently pushed region. The number of blocks // popped is the size of `block_args`, and the corresponding result values are // the elements of `block_args`. Returns an instruction referring to the // result value. auto AddConvergenceBlockWithArgAndPush( Context& context, Parse::NodeId node_id, std::initializer_list block_args) -> SemIR::InstId; // Sets the constant value of a block argument created as the result of a // branch. `select_id` should be a `BlockArg` that selects between two // values. `cond_id` is the condition, `if_false` is the value to use if the // condition is false, and `if_true` is the value to use if the condition is // true. We don't track enough information in the `BlockArg` inst for // `TryEvalInst` to do this itself. auto SetBlockArgResultBeforeConstantUse(Context& context, SemIR::InstId select_id, SemIR::InstId cond_id, SemIR::InstId if_true, SemIR::InstId if_false) -> void; // Returns whether the current position in the current block is reachable. auto IsCurrentPositionReachable(Context& context) -> bool; // Determines whether the instruction requires cleanup and, if so, adds it for // cleanup blocks. Note for example that a class may not need destruction when // neither it nor its members have `destroy` functions. auto MaybeAddCleanupForInst(Context& context, SemIR::InstId inst_id) -> void; // Adds an instruction that has cleanup associated. template requires(InstT::Kind.has_cleanup() && std::convertible_to) auto AddInstWithCleanup(Context& context, LocT loc, InstT inst) -> SemIR::InstId { auto inst_id = AddInst(context, SemIR::LocIdAndInst(loc, inst)); MaybeAddCleanupForInst(context, inst_id); return inst_id; } // Adds an instruction that has cleanup associated. template requires(InstT::Kind.has_cleanup() && std::convertible_to) auto AddInstWithCleanupInNoBlock(Context& context, LocT loc, InstT inst) -> SemIR::InstId { auto inst_id = AddInstInNoBlock(context, SemIR::LocIdAndInst(loc, inst)); MaybeAddCleanupForInst(context, inst_id); return inst_id; } // Adds a return cleanup block, including the returning instruction. // // Cleanup blocks are an effort to share cleanup instructions across equivalent // scope-ending instructions (for example, all `return;` instructions are // equivalent). Structurally, they should first run non-shared cleanup, then // either dispatch to a cleanup block that includes shared cleanup, or invoke // the control flow instruction. // // For example: // // fn F() { // var a: C; // if (...) { // // Cleanup block 1: destroy a, return // return; // } // // var b: C; // if (...) { // // Cleanup block 2: destroy b, reuse cleanup block 1. // return; // } // // DoSomethingMore(); // // Cleanup block 3: reuse cleanup block 2. // } // // TODO: Add support for `break;` and `continue;`. // TODO: Add reuse (described above but not done). auto AddReturnCleanupBlock(Context& context, SemIR::LocIdAndInst loc_id_and_inst) -> void; template auto AddReturnCleanupBlock(Context& context, LocT loc) -> void { AddReturnCleanupBlock(context, SemIR::LocIdAndInst(loc, SemIR::Return{})); } template auto AddReturnCleanupBlockWithExpr(Context& context, LocT loc, SemIR::ReturnExpr inst) -> void { AddReturnCleanupBlock(context, SemIR::LocIdAndInst(loc, inst)); } } // namespace Carbon::Check #endif // CARBON_TOOLCHAIN_CHECK_CONTROL_FLOW_H_ ================================================ FILE: toolchain/check/convert.cpp ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #include "toolchain/check/convert.h" #include #include #include #include "common/check.h" #include "common/map.h" #include "llvm/ADT/STLExtras.h" #include "toolchain/base/kind_switch.h" #include "toolchain/check/action.h" #include "toolchain/check/context.h" #include "toolchain/check/control_flow.h" #include "toolchain/check/core_identifier.h" #include "toolchain/check/diagnostic_helpers.h" #include "toolchain/check/eval.h" #include "toolchain/check/impl_lookup.h" #include "toolchain/check/import_ref.h" #include "toolchain/check/inst.h" #include "toolchain/check/member_access.h" #include "toolchain/check/operator.h" #include "toolchain/check/pattern_match.h" #include "toolchain/check/type.h" #include "toolchain/check/type_completion.h" #include "toolchain/diagnostics/emitter.h" #include "toolchain/diagnostics/format_providers.h" #include "toolchain/sem_ir/copy_on_write_block.h" #include "toolchain/sem_ir/expr_info.h" #include "toolchain/sem_ir/file.h" #include "toolchain/sem_ir/generic.h" #include "toolchain/sem_ir/ids.h" #include "toolchain/sem_ir/inst.h" #include "toolchain/sem_ir/inst_kind.h" #include "toolchain/sem_ir/type.h" #include "toolchain/sem_ir/type_info.h" #include "toolchain/sem_ir/typed_insts.h" // TODO: This contains a lot of recursion. Consider removing it in order to // prevent accidents. // NOLINTBEGIN(misc-no-recursion) namespace Carbon::Check { // If the initializing expression `init_id` has a storage argument that refers // to a temporary, overwrites it with the inst at `target.storage_id`, and // returns the ID that should now be used to refer to `init_id`'s storage. Has // no effect and returns `target.storage_id` unchanged if `target.storage_id` is // None, if `init_id` doesn't have a storage arg, or if the storage argument // doesn't point to a temporary. In the latter case, we assume it was set // correctly when the instruction was created. static auto OverwriteTemporaryStorageArg(SemIR::File& sem_ir, SemIR::InstId init_id, const ConversionTarget& target) -> SemIR::InstId { CARBON_CHECK(target.is_initializer()); if (!target.storage_id.has_value()) { return SemIR::InstId::None; } auto storage_arg_id = FindStorageArgForInitializer(sem_ir, init_id); if (!storage_arg_id.has_value() || storage_arg_id == target.storage_id || !sem_ir.insts().Is(storage_arg_id)) { return target.storage_id; } // Replace the temporary in the storage argument with a reference to our // target. return target.storage_access_block->MergeReplacing(storage_arg_id, target.storage_id); } // Materializes and returns a temporary initialized from the initializer // `init_id`. If `init_id` has a storage arg, it must be a `TemporaryStorage`; // if not, this function allocates one for it. static auto MaterializeTemporary(Context& context, SemIR::InstId init_id) -> SemIR::InstId { auto& sem_ir = context.sem_ir(); auto category = SemIR::GetExprCategory(sem_ir, init_id); CARBON_CHECK(SemIR::IsInitializerCategory(category)); auto init = sem_ir.insts().Get(init_id); auto storage_id = FindStorageArgForInitializer(sem_ir, init_id); if (!storage_id.has_value()) { CARBON_CHECK(category == SemIR::ExprCategory::ReprInitializing); // The initializer has no storage arg, but we want to produce an ephemeral // reference, so we need to allocate temporary storage. storage_id = AddInst( context, SemIR::LocId(init_id), {.type_id = init.type_id()}); } CARBON_CHECK( sem_ir.insts().Get(storage_id).kind() == SemIR::TemporaryStorage::Kind, "Storage arg for initializer does not contain a temporary; " "initialized multiple times? Have {0}", sem_ir.insts().Get(storage_id)); return AddInstWithCleanup(context, SemIR::LocId(init_id), {.type_id = init.type_id(), .storage_id = storage_id, .init_id = init_id}); } // Discards the initializer `init_id`. If `init_id` intrinsically writes to // memory, this materializes a temporary for it and starts its lifetime. // // TODO: We should probably start its lifetime unconditionally, because // types with by-copy representations can still have nontrivial destructors. static auto DiscardInitializer(Context& context, SemIR::InstId init_id) -> void { auto& sem_ir = context.sem_ir(); auto storage_id = FindStorageArgForInitializer(sem_ir, init_id); if (!storage_id.has_value()) { CARBON_CHECK(SemIR::GetExprCategory(sem_ir, init_id) == SemIR::ExprCategory::ReprInitializing); return; } // init_id writes to temporary storage, so we need to materialize a temporary // for it. MaterializeTemporary(context, init_id); } // If `expr_id` is an initializer, materializes it and returns the resulting // ephemeral reference expression. Otherwise, returns `expr_id`. static auto MaterializeIfInitializer(Context& context, SemIR::InstId expr_id) -> SemIR::InstId { if (SemIR::IsInitializerCategory( SemIR::GetExprCategory(context.sem_ir(), expr_id))) { return MaterializeTemporary(context, expr_id); } else { return expr_id; } } // Helper to allow `MakeElementAccessInst` to call `AddInst` with either a // `PendingBlock` or `Context` (defined in `inst.h`). template static auto AddInst(PendingBlock& block, SemIR::LocId loc_id, AccessInstT inst) -> SemIR::InstId { return block.AddInst(loc_id, inst); } // Creates and adds an instruction to perform element access into an aggregate. template static auto MakeElementAccessInst(Context& context, SemIR::LocId loc_id, SemIR::InstId aggregate_id, SemIR::TypeId elem_type_id, InstBlockT& block, size_t i) -> SemIR::InstId { if (!aggregate_id.has_value()) { return SemIR::InstId::None; } if constexpr (std::is_same_v) { // TODO: Add a new instruction kind for indexing an array at a constant // index so that we don't need an integer literal instruction here, and // remove this special case. auto index_id = block.template AddInst( loc_id, {.type_id = GetSingletonType(context, SemIR::IntLiteralType::TypeInstId), .int_id = context.ints().Add(static_cast(i))}); return AddInst(block, loc_id, {elem_type_id, aggregate_id, index_id}); } else { return AddInst( block, loc_id, {elem_type_id, aggregate_id, SemIR::ElementIndex(i)}); } } // Get the conversion target kind to use when initializing an element of an // aggregate. static auto GetAggregateElementConversionTargetKind(SemIR::File& sem_ir, ConversionTarget target) -> ConversionTarget::Kind { // If we're forming an initializer, then we want an initializer for each // element. if (target.is_initializer()) { // Perform a final destination store if we're performing an in-place // initialization. auto init_repr = SemIR::InitRepr::ForType(sem_ir, target.type_id); CARBON_CHECK(init_repr.kind != SemIR::InitRepr::Dependent, "Aggregate should not have dependent init kind"); if (init_repr.kind == SemIR::InitRepr::InPlace) { return ConversionTarget::InPlaceInitializing; } return ConversionTarget::Initializing; } // Otherwise, we want a value representation for each element. return ConversionTarget::Value; } // Converts an element of one aggregate so that it can be used as an element of // another aggregate. // // For the source: `src_id` is the source aggregate, `src_elem_type` is the // element type, `src_field_index` is the index, and `SourceAccessInstT` is the // kind of instruction used to access the source element. // // For the target: `kind` is the kind of conversion or initialization, // `target_elem_type` is the element type. For initialization, `target_id` is // the destination, `target_block` is a pending block for target location // calculations that will be spliced as the return slot of the initializer if // necessary, `target_field_index` is the index, and `TargetAccessInstT` is the // kind of instruction used to access the destination element. template static auto ConvertAggregateElement( Context& context, SemIR::LocId loc_id, SemIR::InstId src_id, SemIR::TypeInstId src_elem_type_inst, llvm::ArrayRef src_literal_elems, ConversionTarget::Kind kind, SemIR::InstId target_id, SemIR::TypeInstId target_elem_type_inst, PendingBlock* target_block, size_t src_field_index, size_t target_field_index) -> SemIR::InstId { auto src_elem_type = context.types().GetTypeIdForTypeInstId(src_elem_type_inst); auto target_elem_type = context.types().GetTypeIdForTypeInstId(target_elem_type_inst); // Compute the location of the source element. This goes into the current code // block, not into the target block. // TODO: Ideally we would discard this instruction if it's unused. auto src_elem_id = !src_literal_elems.empty() ? src_literal_elems[src_field_index] : MakeElementAccessInst( context, loc_id, src_id, src_elem_type, context, src_field_index); // If we're performing a conversion rather than an initialization, we won't // have or need a target. ConversionTarget target = {.kind = kind, .type_id = target_elem_type}; if (!target.is_initializer()) { return Convert(context, loc_id, src_elem_id, target); } // Compute the location of the target element and initialize it. PendingBlock::DiscardUnusedInstsScope scope(target_block); target.storage_access_block = target_block; target.storage_id = MakeElementAccessInst( context, loc_id, target_id, target_elem_type, *target_block, target_field_index); return Convert(context, loc_id, src_elem_id, target); } // Performs a conversion from a tuple to an array type. This function only // converts the type, and does not perform a final conversion to the requested // expression category. static auto ConvertTupleToArray(Context& context, SemIR::TupleType tuple_type, SemIR::ArrayType array_type, SemIR::InstId value_id, ConversionTarget target) -> SemIR::InstId { auto& sem_ir = context.sem_ir(); auto tuple_elem_types = sem_ir.inst_blocks().Get(tuple_type.type_elements_id); auto value = sem_ir.insts().Get(value_id); SemIR::LocId value_loc_id(value_id); // If we're initializing from a tuple literal, we will use its elements // directly. Otherwise, materialize a temporary if needed and index into the // result. llvm::ArrayRef literal_elems; if (auto tuple_literal = value.TryAs()) { literal_elems = sem_ir.inst_blocks().Get(tuple_literal->elements_id); } else { value_id = MaterializeIfInitializer(context, value_id); } // Check that the tuple is the right size. std::optional array_bound = sem_ir.GetArrayBoundValue(array_type.bound_id); if (!array_bound) { // TODO: Should this fall back to using `ImplicitAs`? if (target.diagnose) { CARBON_DIAGNOSTIC(ArrayInitDependentBound, Error, "cannot initialize array with dependent bound from a " "list of initializers"); context.emitter().Emit(value_loc_id, ArrayInitDependentBound); } return SemIR::ErrorInst::InstId; } if (tuple_elem_types.size() != array_bound) { if (target.diagnose) { CARBON_DIAGNOSTIC(ArrayInitFromLiteralArgCountMismatch, Error, "cannot initialize array of {0} element{0:s} from {1} " "initializer{1:s}", Diagnostics::IntAsSelect, Diagnostics::IntAsSelect); CARBON_DIAGNOSTIC( ArrayInitFromExprArgCountMismatch, Error, "cannot initialize array of {0} element{0:s} from tuple " "with {1} element{1:s}", Diagnostics::IntAsSelect, Diagnostics::IntAsSelect); context.emitter().Emit(value_loc_id, literal_elems.empty() ? ArrayInitFromExprArgCountMismatch : ArrayInitFromLiteralArgCountMismatch, *array_bound, tuple_elem_types.size()); } return SemIR::ErrorInst::InstId; } PendingBlock target_block_storage(&context); PendingBlock* target_block = target.storage_access_block ? target.storage_access_block : &target_block_storage; // Arrays are always initialized in-place. Allocate a temporary as the // destination for the array initialization if we weren't given one. SemIR::InstId return_slot_arg_id = target.storage_id; if (!target.storage_id.has_value()) { return_slot_arg_id = target_block->AddInst( value_loc_id, {.type_id = target.type_id}); } // Initialize each element of the array from the corresponding element of the // tuple. // TODO: Annotate diagnostics coming from here with the array element index, // if initializing from a tuple literal. llvm::SmallVector inits; inits.reserve(*array_bound + 1); for (auto [i, src_type_inst_id] : llvm::enumerate( context.types().GetBlockAsTypeInstIds(tuple_elem_types))) { // TODO: This call recurses back into conversion. Switch to an iterative // approach. auto init_id = ConvertAggregateElement( context, value_loc_id, value_id, src_type_inst_id, literal_elems, ConversionTarget::InPlaceInitializing, return_slot_arg_id, array_type.element_type_inst_id, target_block, i, i); if (init_id == SemIR::ErrorInst::InstId) { return SemIR::ErrorInst::InstId; } inits.push_back(init_id); } // Flush the temporary here if we didn't insert it earlier, so we can add a // reference to the return slot. target_block->InsertHere(); return AddInst(context, value_loc_id, {.type_id = target.type_id, .inits_id = sem_ir.inst_blocks().Add(inits), .dest_id = return_slot_arg_id}); } // Performs a conversion from a tuple to a tuple type. This function only // converts the type, and does not perform a final conversion to the requested // expression category. static auto ConvertTupleToTuple(Context& context, SemIR::TupleType src_type, SemIR::TupleType dest_type, SemIR::InstId value_id, ConversionTarget target) -> SemIR::InstId { auto& sem_ir = context.sem_ir(); auto src_elem_types = sem_ir.inst_blocks().Get(src_type.type_elements_id); auto dest_elem_types = sem_ir.inst_blocks().Get(dest_type.type_elements_id); auto value = sem_ir.insts().Get(value_id); SemIR::LocId value_loc_id(value_id); // If we're initializing from a tuple literal, we will use its elements // directly. Otherwise, materialize a temporary if needed and index into the // result. llvm::ArrayRef literal_elems; auto literal_elems_id = SemIR::InstBlockId::None; if (auto tuple_literal = value.TryAs()) { literal_elems_id = tuple_literal->elements_id; literal_elems = sem_ir.inst_blocks().Get(literal_elems_id); } else { value_id = MaterializeIfInitializer(context, value_id); } // Check that the tuples are the same size. if (src_elem_types.size() != dest_elem_types.size()) { if (target.diagnose) { CARBON_DIAGNOSTIC( TupleInitElementCountMismatch, Error, "cannot initialize tuple of {0} element{0:s} from tuple " "with {1} element{1:s}", Diagnostics::IntAsSelect, Diagnostics::IntAsSelect); context.emitter().Emit(value_loc_id, TupleInitElementCountMismatch, dest_elem_types.size(), src_elem_types.size()); } return SemIR::ErrorInst::InstId; } ConversionTarget::Kind inner_kind = GetAggregateElementConversionTargetKind(sem_ir, target); // Initialize each element of the destination from the corresponding element // of the source. // TODO: Annotate diagnostics coming from here with the element index. auto new_block = literal_elems_id.has_value() ? SemIR::CopyOnWriteInstBlock(&sem_ir, literal_elems_id) : SemIR::CopyOnWriteInstBlock( &sem_ir, SemIR::CopyOnWriteInstBlock::UninitializedBlock{ src_elem_types.size()}); for (auto [i, src_type_inst_id, dest_type_inst_id] : llvm::enumerate( context.types().GetBlockAsTypeInstIds(src_elem_types), context.types().GetBlockAsTypeInstIds(dest_elem_types))) { // TODO: This call recurses back into conversion. Switch to an iterative // approach. auto init_id = ConvertAggregateElement( context, value_loc_id, value_id, src_type_inst_id, literal_elems, inner_kind, target.storage_id, dest_type_inst_id, target.storage_access_block, i, i); if (init_id == SemIR::ErrorInst::InstId) { return SemIR::ErrorInst::InstId; } new_block.Set(i, init_id); } if (target.is_initializer()) { target.storage_access_block->InsertHere(); return AddInst(context, value_loc_id, {.type_id = target.type_id, .elements_id = new_block.id(), .dest_id = target.storage_id}); } else { return AddInst( context, value_loc_id, {.type_id = target.type_id, .elements_id = new_block.id()}); } } // Converts a tuple of elements that are convertible to `type` into a `type` // that is a tuple of types. static auto ConvertTupleToType(Context& context, SemIR::LocId loc_id, SemIR::InstId value_id, SemIR::TypeId value_type_id, ConversionTarget target) -> SemIR::TypeInstId { auto value_const_id = context.constant_values().Get(value_id); if (!value_const_id.is_constant()) { // Types are constants. The input value must have a constant value to // convert. return SemIR::TypeInstId::None; } llvm::SmallVector type_inst_ids; auto value_const_inst_id = context.constant_values().GetInstId(value_const_id); if (auto tuple_value = context.insts().TryGetAs(value_const_inst_id)) { for (auto tuple_inst_id : context.inst_blocks().Get(tuple_value->elements_id)) { // TODO: This call recurses back into conversion. Switch to an // iterative approach. type_inst_ids.push_back( ExprAsType(context, loc_id, tuple_inst_id, target.diagnose).inst_id); } } else { // A value of type TupleType that isn't a TupleValue must be a symbolic // binding. CARBON_CHECK( context.insts().Is(value_const_inst_id)); // Form a TupleAccess for each element in the symbolic value, which is then // converted to a `type` or diagnosed as an error. auto tuple_type = context.types().GetAs(value_type_id); auto type_elements = context.types().GetBlockAsTypeIds( context.inst_blocks().Get(tuple_type.type_elements_id)); for (auto [i, type_id] : llvm::enumerate(type_elements)) { auto access_inst_id = GetOrAddInst(context, loc_id, {.type_id = type_id, .tuple_id = value_id, .index = SemIR::ElementIndex(i)}); // TODO: This call recurses back into conversion. Switch to an // iterative approach. type_inst_ids.push_back( ExprAsType(context, loc_id, access_inst_id, target.diagnose).inst_id); } } // TODO: Should we add this as an instruction? It will contain // references to local InstIds. auto tuple_type_id = GetTupleType(context, type_inst_ids); return context.types().GetTypeInstId(tuple_type_id); } // Create a reference to the vtable pointer for a class. Returns None if the // class has no vptr. static auto CreateVtablePtrRef(Context& context, SemIR::LocId loc_id, SemIR::ClassType vtable_class_type) -> SemIR::InstId { auto vtable_decl_id = context.classes().Get(vtable_class_type.class_id).vtable_decl_id; if (!vtable_decl_id.has_value()) { return SemIR::InstId::None; } LoadImportRef(context, vtable_decl_id); auto canonical_vtable_decl_id = context.constant_values().GetConstantInstId(vtable_decl_id); return AddInst( context, loc_id, {.type_id = GetPointerType(context, SemIR::VtableType::TypeInstId), .vtable_id = context.insts() .GetAs(canonical_vtable_decl_id) .vtable_id, .specific_id = vtable_class_type.specific_id}); } // Returns whether the given expression performs in-place initialization (or is // invalid). static auto IsInPlaceInitializing(Context& context, SemIR::InstId result_id) { auto category = SemIR::GetExprCategory(context.sem_ir(), result_id); return category == SemIR::ExprCategory::InPlaceInitializing || (category == SemIR::ExprCategory::ReprInitializing && SemIR::InitRepr::ForType(context.sem_ir(), context.insts().Get(result_id).type_id()) .kind == SemIR::InitRepr::InPlace) || category == SemIR::ExprCategory::Error; } // Returns the index of the vptr field in the given struct type fields, or // None if there is no vptr field. static auto GetVptrFieldIndex(llvm::ArrayRef fields) -> SemIR::ElementIndex { // If the type introduces a vptr, it will always be the first field. bool has_vptr = !fields.empty() && fields.front().name_id == SemIR::NameId::Vptr; return has_vptr ? SemIR::ElementIndex(0) : SemIR::ElementIndex::None; } // Builds a member access expression naming the vptr field of the given class // object. This is analogous to what `PerformMemberAccess` for `NameId::Vptr` // would return if the vptr could be found by name lookup. static auto PerformVptrAccess(Context& context, SemIR::LocId loc_id, SemIR::InstId class_ref_id) -> SemIR::InstId { auto class_type_id = context.insts().Get(class_ref_id).type_id(); while (class_ref_id.has_value()) { // The type of `ref_id` must be a class type. if (class_type_id == SemIR::ErrorInst::TypeId) { return SemIR::ErrorInst::InstId; } auto class_type = context.types().GetAs(class_type_id); auto& class_info = context.classes().Get(class_type.class_id); // Get the object representation. auto object_repr_id = class_info.GetObjectRepr(context.sem_ir(), class_type.specific_id); if (object_repr_id == SemIR::ErrorInst::TypeId) { return SemIR::ErrorInst::InstId; } if (context.types().Is(object_repr_id)) { context.TODO(loc_id, "accessing vptr of custom layout class"); return SemIR::ErrorInst::InstId; } // Check to see if this class introduces the vptr. auto repr_struct_type = context.types().GetAs(object_repr_id); auto repr_fields = context.struct_type_fields().Get(repr_struct_type.fields_id); if (auto vptr_field_index = GetVptrFieldIndex(repr_fields); vptr_field_index.has_value()) { return AddInst( context, loc_id, {.type_id = context.types().GetTypeIdForTypeInstId( repr_fields[vptr_field_index.index].type_inst_id), .base_id = class_ref_id, .index = vptr_field_index}); } // Otherwise, step through to the base class and try again. CARBON_CHECK(class_info.base_id.has_value(), "Could not find vptr for dynamic class"); auto base_decl = context.insts().GetAs(class_info.base_id); class_type_id = context.types().GetTypeIdForTypeInstId( repr_fields[base_decl.index.index].type_inst_id); class_ref_id = AddInst(context, loc_id, {.type_id = class_type_id, .base_id = class_ref_id, .index = base_decl.index}); } return class_ref_id; } // Converts an initializer for a type `partial T` to an initializer for `T` by // initializing the vptr if necessary. static auto ConvertPartialInitializerToNonPartial( Context& context, ConversionTarget target, SemIR::ClassType vtable_class_type, SemIR::InstId result_id) -> SemIR::InstId { auto loc_id = SemIR::LocId(result_id); auto vptr_id = CreateVtablePtrRef(context, loc_id, vtable_class_type); if (!vptr_id.has_value()) { // No vtable pointer in this class, nothing to do. return result_id; } CARBON_CHECK( IsInPlaceInitializing(context, result_id), "Type with vptr should have in-place initializing representation"); target.storage_access_block->InsertHere(); auto dest_id = PerformVptrAccess(context, loc_id, target.storage_id); auto vptr_init_id = AddInst( context, loc_id, {.type_id = context.insts().Get(dest_id).type_id(), .src_id = vptr_id, .dest_id = dest_id}); return AddInst(context, loc_id, {.type_id = target.type_id, .base_init_id = result_id, .update_init_id = vptr_init_id}); } // Common implementation for ConvertStructToStruct and ConvertStructToClass. template static auto ConvertStructToStructOrClass( Context& context, SemIR::StructType src_type, SemIR::StructType dest_type, SemIR::InstId value_id, ConversionTarget target, SemIR::ClassType* vtable_class_type = nullptr) -> SemIR::InstId { static_assert(std::is_same_v || std::is_same_v); constexpr bool ToClass = std::is_same_v; auto& sem_ir = context.sem_ir(); auto src_elem_fields = sem_ir.struct_type_fields().Get(src_type.fields_id); auto dest_elem_fields = sem_ir.struct_type_fields().Get(dest_type.fields_id); auto dest_vptr_index = GetVptrFieldIndex(dest_elem_fields); auto dest_elem_fields_size = dest_elem_fields.size() - (dest_vptr_index.has_value() ? 1 : 0); auto value = sem_ir.insts().Get(value_id); SemIR::LocId value_loc_id(value_id); // If we're initializing from a struct literal, we will use its elements // directly. Otherwise, materialize a temporary if needed and index into the // result. llvm::ArrayRef literal_elems; auto literal_elems_id = SemIR::InstBlockId::None; if (auto struct_literal = value.TryAs()) { literal_elems_id = struct_literal->elements_id; literal_elems = sem_ir.inst_blocks().Get(literal_elems_id); } else { value_id = MaterializeIfInitializer(context, value_id); } // Check that the structs are the same size. // TODO: If not, include the name of the first source field that doesn't // exist in the destination or vice versa in the diagnostic. if (src_elem_fields.size() != dest_elem_fields_size) { if (target.diagnose) { CARBON_DIAGNOSTIC( StructInitElementCountMismatch, Error, "cannot initialize {0:class|struct} with {1} field{1:s} from struct " "with {2} field{2:s}", Diagnostics::BoolAsSelect, Diagnostics::IntAsSelect, Diagnostics::IntAsSelect); context.emitter().Emit(value_loc_id, StructInitElementCountMismatch, ToClass, dest_elem_fields_size, src_elem_fields.size()); } return SemIR::ErrorInst::InstId; } // Prepare to look up fields in the source by index. Map src_field_indexes; if (src_type.fields_id != dest_type.fields_id) { for (auto [i, field] : llvm::enumerate(src_elem_fields)) { auto result = src_field_indexes.Insert(field.name_id, i); CARBON_CHECK(result.is_inserted(), "Duplicate field in source structure"); } } ConversionTarget::Kind inner_kind = GetAggregateElementConversionTargetKind(sem_ir, target); // Initialize each element of the destination from the corresponding element // of the source. // TODO: Annotate diagnostics coming from here with the element index. auto new_block = literal_elems_id.has_value() && !dest_vptr_index.has_value() ? SemIR::CopyOnWriteInstBlock(&sem_ir, literal_elems_id) : SemIR::CopyOnWriteInstBlock( &sem_ir, SemIR::CopyOnWriteInstBlock::UninitializedBlock{ dest_elem_fields.size()}); for (auto [i, dest_field] : llvm::enumerate(dest_elem_fields)) { if (dest_field.name_id == SemIR::NameId::Vptr) { if constexpr (!ToClass) { CARBON_FATAL("Only classes should have vptrs."); } target.storage_access_block->InsertHere(); auto vptr_type_id = context.types().GetTypeIdForTypeInstId(dest_field.type_inst_id); auto dest_id = AddInst(context, value_loc_id, {.type_id = vptr_type_id, .base_id = target.storage_id, .index = SemIR::ElementIndex(i)}); auto vtable_ptr_id = SemIR::InstId::None; if (vtable_class_type) { vtable_ptr_id = CreateVtablePtrRef(context, value_loc_id, *vtable_class_type); // Track that we initialized the vptr so we don't do it again. vtable_class_type = nullptr; } else { // For a partial class type, we leave the vtable pointer uninitialized. // TODO: Consider storing a specified value such as null for hardening. vtable_ptr_id = AddInst( context, value_loc_id, {.type_id = GetPointerType(context, SemIR::VtableType::TypeInstId)}); } auto init_id = AddInst(context, value_loc_id, {.type_id = vptr_type_id, .src_id = vtable_ptr_id, .dest_id = dest_id}); new_block.Set(i, init_id); continue; } // Find the matching source field. auto src_field_index = i; if (src_type.fields_id != dest_type.fields_id) { if (auto lookup = src_field_indexes.Lookup(dest_field.name_id)) { src_field_index = lookup.value(); } else { if (target.diagnose) { if (literal_elems_id.has_value()) { CARBON_DIAGNOSTIC( StructInitMissingFieldInLiteral, Error, "missing value for field `{0}` in struct initialization", SemIR::NameId); context.emitter().Emit(value_loc_id, StructInitMissingFieldInLiteral, dest_field.name_id); } else { CARBON_DIAGNOSTIC(StructInitMissingFieldInConversion, Error, "cannot convert from struct type {0} to {1}: " "missing field `{2}` in source type", TypeOfInstId, SemIR::TypeId, SemIR::NameId); context.emitter().Emit(value_loc_id, StructInitMissingFieldInConversion, value_id, target.type_id, dest_field.name_id); } } return SemIR::ErrorInst::InstId; } } auto src_field = src_elem_fields[src_field_index]; // When initializing the `.base` field of a class, the destination type is // `partial Base`, not `Base`. // TODO: Skip this if the source field is an initializing expression of the // non-partial type in order to produce smaller IR. auto dest_field_type_inst_id = dest_field.type_inst_id; if (dest_field.name_id == SemIR::NameId::Base) { auto partial_type_id = GetQualifiedType( context, context.types().GetTypeIdForTypeInstId(dest_field.type_inst_id), SemIR::TypeQualifiers::Partial); dest_field_type_inst_id = context.types().GetTypeInstId(partial_type_id); } // TODO: This call recurses back into conversion. Switch to an iterative // approach. auto dest_field_index = src_field_index; if (dest_vptr_index.has_value() && static_cast(src_field_index) >= dest_vptr_index.index) { dest_field_index += 1; } auto init_id = ConvertAggregateElement( context, value_loc_id, value_id, src_field.type_inst_id, literal_elems, inner_kind, target.storage_id, dest_field_type_inst_id, target.storage_access_block, src_field_index, dest_field_index); if (init_id == SemIR::ErrorInst::InstId) { return SemIR::ErrorInst::InstId; } // When initializing the base, adjust the type of the initializer from // `partial Base` to `Base`. This isn't strictly correct, since we haven't // finished initializing a `Base` until we store to the vptr, but is better // than having an inconsistent type for the struct field initializer. if (dest_field_type_inst_id != dest_field.type_inst_id) { init_id = AddInst( context, value_loc_id, {.type_id = context.types().GetTypeIdForTypeInstId(dest_field.type_inst_id), .source_id = init_id}); } new_block.Set(i, init_id); } bool is_init = target.is_initializer(); if (ToClass) { target.storage_access_block->InsertHere(); CARBON_CHECK(is_init, "Converting directly to a class value is not supported"); auto result_id = AddInst(context, value_loc_id, {.type_id = target.type_id, .elements_id = new_block.id(), .dest_id = target.storage_id}); if (vtable_class_type) { result_id = ConvertPartialInitializerToNonPartial( context, target, *vtable_class_type, result_id); } return result_id; } else if (is_init) { target.storage_access_block->InsertHere(); return AddInst(context, value_loc_id, {.type_id = target.type_id, .elements_id = new_block.id(), .dest_id = target.storage_id}); } else { return AddInst( context, value_loc_id, {.type_id = target.type_id, .elements_id = new_block.id()}); } } // Performs a conversion from a struct to a struct type. This function only // converts the type, and does not perform a final conversion to the requested // expression category. static auto ConvertStructToStruct(Context& context, SemIR::StructType src_type, SemIR::StructType dest_type, SemIR::InstId value_id, ConversionTarget target) -> SemIR::InstId { return ConvertStructToStructOrClass( context, src_type, dest_type, value_id, target); } // Performs a conversion from a struct to a class type. This function only // converts the type, and does not perform a final conversion to the requested // expression category. static auto ConvertStructToClass(Context& context, SemIR::StructType src_type, SemIR::ClassType dest_type, SemIR::InstId value_id, ConversionTarget target, bool is_partial = false) -> SemIR::InstId { PendingBlock target_block(&context); auto& dest_class_info = context.classes().Get(dest_type.class_id); CARBON_CHECK(is_partial || dest_class_info.inheritance_kind != SemIR::Class::Abstract); auto object_repr_id = dest_class_info.GetObjectRepr(context.sem_ir(), dest_type.specific_id); if (object_repr_id == SemIR::ErrorInst::TypeId) { return SemIR::ErrorInst::InstId; } if (context.types().Is(object_repr_id)) { // Builtin conversion does not apply. return value_id; } auto dest_struct_type = context.types().GetAs(object_repr_id); // If we're trying to create a class value, form temporary storage to hold the // initializer. if (!target.is_initializer()) { target.kind = ConversionTarget::Initializing; target.storage_access_block = &target_block; target.storage_id = target_block.AddInst( SemIR::LocId(value_id), {.type_id = target.type_id}); } return ConvertStructToStructOrClass( context, src_type, dest_struct_type, value_id, target, is_partial ? nullptr : &dest_type); } // An inheritance path is a sequence of `BaseDecl`s and corresponding base types // in order from derived to base. using InheritancePath = llvm::SmallVector>; // Computes the inheritance path from class `derived_id` to class `base_id`. // Returns nullopt if `derived_id` is not a class derived from `base_id`. static auto ComputeInheritancePath(Context& context, SemIR::LocId loc_id, SemIR::TypeId derived_id, SemIR::TypeId base_id) -> std::optional { // We intend for NRVO to be applied to `result`. All `return` statements in // this function should `return result;`. std::optional result(std::in_place); if (!TryToCompleteType(context, derived_id, loc_id)) { // TODO: Should we give an error here? If we don't, and there is an // inheritance path when the class is defined, we may have a coherence // problem. result = std::nullopt; return result; } while (derived_id != base_id) { auto derived_class_type = context.types().TryGetAs(derived_id); if (!derived_class_type) { result = std::nullopt; break; } auto& derived_class = context.classes().Get(derived_class_type->class_id); auto base_type_id = derived_class.GetBaseType( context.sem_ir(), derived_class_type->specific_id); if (!base_type_id.has_value()) { result = std::nullopt; break; } result->push_back({derived_class.base_id, base_type_id}); derived_id = base_type_id; } return result; } // Performs a conversion from a derived class value or reference to a base class // value or reference. static auto ConvertDerivedToBase(Context& context, SemIR::LocId loc_id, SemIR::InstId value_id, const InheritancePath& path) -> SemIR::InstId { // Materialize a temporary if necessary. value_id = ConvertToValueOrRefExpr(context, value_id); // Preserve type qualifiers. auto quals = context.types() .GetUnqualifiedTypeAndQualifiers( context.insts().Get(value_id).type_id()) .second; // Add a series of `.base` accesses. for (auto [base_id, base_type_id] : path) { auto base_decl = context.insts().GetAs(base_id); value_id = AddInst( context, loc_id, {.type_id = GetQualifiedType(context, base_type_id, quals), .base_id = value_id, .index = base_decl.index}); } return value_id; } // Performs a conversion from a derived class pointer to a base class pointer. static auto ConvertDerivedPointerToBasePointer( Context& context, SemIR::LocId loc_id, SemIR::PointerType src_ptr_type, SemIR::TypeId dest_ptr_type_id, SemIR::InstId ptr_id, const InheritancePath& path) -> SemIR::InstId { auto pointee_type_id = context.types().GetTypeIdForTypeInstId(src_ptr_type.pointee_id); // Form `*p`. ptr_id = ConvertToValueExpr(context, ptr_id); auto ref_id = AddInst( context, loc_id, {.type_id = pointee_type_id, .pointer_id = ptr_id}); // Convert as a reference expression. ref_id = ConvertDerivedToBase(context, loc_id, ref_id, path); // Take the address. return AddInst( context, loc_id, {.type_id = dest_ptr_type_id, .lvalue_id = ref_id}); } // Returns whether `category` is a valid expression category to produce as a // result of a conversion with kind `target_kind`. static auto IsValidExprCategoryForConversionTarget( SemIR::ExprCategory category, ConversionTarget::Kind target_kind) -> bool { switch (target_kind) { case ConversionTarget::Value: return category == SemIR::ExprCategory::Value; case ConversionTarget::ValueOrRef: return category == SemIR::ExprCategory::Value || category == SemIR::ExprCategory::DurableRef || category == SemIR::ExprCategory::EphemeralRef; case ConversionTarget::Discarded: return category == SemIR::ExprCategory::Value || category == SemIR::ExprCategory::DurableRef || category == SemIR::ExprCategory::EphemeralRef || category == SemIR::ExprCategory::ReprInitializing || category == SemIR::ExprCategory::InPlaceInitializing; case ConversionTarget::RefParam: case ConversionTarget::UnmarkedRefParam: return category == SemIR::ExprCategory::DurableRef || category == SemIR::ExprCategory::EphemeralRef; case ConversionTarget::DurableRef: return category == SemIR::ExprCategory::DurableRef; case ConversionTarget::CppThunkRef: return category == SemIR::ExprCategory::EphemeralRef; case ConversionTarget::NoOp: case ConversionTarget::ExplicitAs: case ConversionTarget::ExplicitUnsafeAs: return true; case ConversionTarget::InPlaceInitializing: return category == SemIR::ExprCategory::InPlaceInitializing; case ConversionTarget::Initializing: return category == SemIR::ExprCategory::ReprInitializing; } } // Determines whether the initialization representation of the type is a copy of // the value representation. static auto InitReprIsCopyOfValueRepr(const SemIR::File& sem_ir, SemIR::TypeId type_id) -> bool { // The initializing representation is a copy of the value representation if // they're both copies of the object representation. return SemIR::InitRepr::ForType(sem_ir, type_id).IsCopyOfObjectRepr() && SemIR::ValueRepr::ForType(sem_ir, type_id) .IsCopyOfObjectRepr(sem_ir, type_id); } // Determines whether we can pull a value directly out of an initializing // expression of type `type_id` to initialize a target of type `type_id` and // kind `target_kind`. static auto CanUseValueOfInitializer(const SemIR::File& sem_ir, SemIR::TypeId type_id, ConversionTarget::Kind target_kind) -> bool { if (!IsValidExprCategoryForConversionTarget(SemIR::ExprCategory::Value, target_kind)) { // We don't want a value expression. return false; } // We can pull a value out of an initializing expression if it holds one. return InitReprIsCopyOfValueRepr(sem_ir, type_id); } // Determine whether the given set of qualifiers can be added by a conversion // of an expression of the given category. static auto CanAddQualifiers(SemIR::TypeQualifiers quals, SemIR::ExprCategory cat) -> bool { if (quals.HasAnyOf(SemIR::TypeQualifiers::MaybeUnformed) && !SemIR::IsRefCategory(cat)) { // `MaybeUnformed(T)` may have a different value representation or // initializing representation from `T`, so only allow it to be added for a // reference expression. // TODO: We should allow converting an initializing expression of type `T` // to `MaybeUnformed(T)`. `PerformBuiltinConversion` will need to generate // an `InPlaceInit` instruction when needed. // NOLINTNEXTLINE(readability-simplify-boolean-expr) return false; } // `const` and `partial` can always be added. return true; } // Determine whether the given set of qualifiers can be removed by a conversion // of an expression of the given category. static auto CanRemoveQualifiers(SemIR::TypeQualifiers quals, SemIR::ExprCategory cat, ConversionTarget::Kind kind) -> bool { bool allow_unsafe = kind == ConversionTarget::ExplicitUnsafeAs; if (quals.HasAnyOf(SemIR::TypeQualifiers::Const) && !allow_unsafe && SemIR::IsRefCategory(cat) && IsValidExprCategoryForConversionTarget(cat, kind)) { // Removing `const` is an unsafe conversion for a reference expression. But // it's OK if we will be converting to a different category as part of this // overall conversion anyway. return false; } if (quals.HasAnyOf(SemIR::TypeQualifiers::Partial) && !allow_unsafe && !SemIR::IsInitializerCategory(cat)) { // Removing `partial` is an unsafe conversion for a non-initializing // expression. But it's OK for an initializing expression because we will // initialize the vptr as part of the conversion. return false; } if (quals.HasAnyOf(SemIR::TypeQualifiers::MaybeUnformed) && (!allow_unsafe || SemIR::IsInitializerCategory(cat))) { // As an unsafe conversion, `MaybeUnformed` can be removed from a value or // reference expression. return false; } return true; } static auto DiagnoseConversionFailureToConstraintValue( Context& context, SemIR::LocId loc_id, SemIR::InstId expr_id, SemIR::TypeId target_type_id) -> void { CARBON_CHECK(context.types().IsFacetType(target_type_id)); // If the source type is/has a facet value (converted with `as type` or // otherwise), then we can include its `FacetType` in the diagnostic to help // explain what interfaces the source type implements. auto const_expr_id = GetCanonicalFacetOrTypeValue(context, expr_id); auto const_expr_type_id = context.insts().Get(const_expr_id).type_id(); if (context.types().Is(const_expr_type_id)) { CARBON_DIAGNOSTIC(ConversionFailureFacetToFacet, Error, "cannot convert type {0} that implements {1} into type " "implementing {2}", InstIdAsType, SemIR::TypeId, SemIR::TypeId); context.emitter().Emit(loc_id, ConversionFailureFacetToFacet, expr_id, const_expr_type_id, target_type_id); } else { CARBON_DIAGNOSTIC(ConversionFailureTypeToFacet, Error, "cannot convert type {0} into type implementing {1}", InstIdAsType, SemIR::TypeId); context.emitter().Emit(loc_id, ConversionFailureTypeToFacet, expr_id, target_type_id); } } static auto PerformBuiltinConversion(Context& context, SemIR::LocId loc_id, SemIR::InstId value_id, ConversionTarget target) -> SemIR::InstId { auto& sem_ir = context.sem_ir(); auto value = sem_ir.insts().Get(value_id); auto value_type_id = value.type_id(); auto target_type_inst = sem_ir.types().GetAsInst(target.type_id); // Various forms of implicit conversion are supported as builtin conversions, // either in addition to or instead of `impl`s of `ImplicitAs` in the Carbon // prelude. There are a few reasons we need to perform some of these // conversions as builtins: // // 1) Conversions from struct and tuple *literals* have special rules that // cannot be implemented by invoking `ImplicitAs`. Specifically, we must // recurse into the elements of the literal before performing // initialization in order to avoid unnecessary conversions between // expression categories that would be performed by `ImplicitAs.Convert`. // 2) (Not implemented yet) Conversion of a facet to a facet type depends on // the value of the facet, not only its type, and therefore cannot be // modeled by `ImplicitAs`. // 3) Some of these conversions are used while checking the library // definition of `ImplicitAs` itself or implementations of it. // // We also expect to see better performance by avoiding an `impl` lookup for // common conversions. // // TODO: We should provide a debugging flag to turn off as many of these // builtin conversions as we can so that we can test that they do the same // thing as the library implementations. // // The builtin conversions that correspond to `impl`s in the library all // correspond to `final impl`s, so we don't need to worry about `ImplicitAs` // being specialized in any of these cases. // If the value is already of the right kind and expression category, there's // nothing to do. Performing a conversion would decompose and rebuild tuples // and structs, so it's important that we bail out early in this case. if (value_type_id == target.type_id) { auto value_cat = SemIR::GetExprCategory(sem_ir, value_id); if (IsValidExprCategoryForConversionTarget(value_cat, target.kind)) { return value_id; } // If the source is an initializing expression, we may be able to pull a // value right out of it. if (value_cat == SemIR::ExprCategory::ReprInitializing && CanUseValueOfInitializer(sem_ir, value_type_id, target.kind)) { return AddInst( context, loc_id, {.type_id = value_type_id, .init_id = value_id}); } // Materialization is handled as part of the enclosing conversion. if (SemIR::IsInitializerCategory(value_cat) && target.kind == ConversionTarget::ValueOrRef) { return value_id; } // Final destination store is handled as part of the enclosing conversion. if (value_cat == SemIR::ExprCategory::ReprInitializing && target.kind == ConversionTarget::InPlaceInitializing) { return value_id; } // PerformBuiltinConversion converts each part of a tuple or struct, even // when the types are the same. This is not done for classes since they have // to define their conversions as part of their api. // // If a class adapts a tuple or struct, we convert each of its parts when // there's no other conversion going on (the source and target types are the // same). To do so, we have to insert a conversion of the value up to the // foundation and back down, and a conversion of the initializing object if // there is one. // // Implementation note: We do the conversion through a call to // PerformBuiltinConversion() call rather than a Convert() call to avoid // extraneous `converted` semir instructions on the adapted types, and as a // shortcut to doing the explicit calls to walk the parts of the // tuple/struct which happens inside PerformBuiltinConversion(). if (auto foundation_type_id = context.types().GetTransitiveAdaptedType(value_type_id); foundation_type_id != value_type_id && context.types().IsOneOf( foundation_type_id)) { auto foundation_value_id = AddInst( context, loc_id, {.type_id = foundation_type_id, .source_id = value_id}); auto foundation_init_id = target.storage_id; if (foundation_init_id != SemIR::InstId::None) { foundation_init_id = target.storage_access_block->AddInst( loc_id, {.type_id = foundation_type_id, .source_id = target.storage_id}); } { // While the types are the same, the conversion can still fail if it // performs a copy while converting the value to another category, and // the type (or some part of it) is not copyable. Diagnostics::AnnotationScope annotate_diagnostics( &context.emitter(), [&](auto& builder) { CARBON_DIAGNOSTIC(InCopy, Note, "in copy of {0}", TypeOfInstId); builder.Note(value_id, InCopy, value_id); }); foundation_value_id = PerformBuiltinConversion( context, loc_id, foundation_value_id, {.kind = target.kind, .type_id = foundation_type_id, .storage_id = foundation_init_id, .storage_access_block = target.storage_access_block, .diagnose = target.diagnose}); if (foundation_value_id == SemIR::ErrorInst::InstId) { return SemIR::ErrorInst::InstId; } } return AddInst( context, loc_id, {.type_id = target.type_id, .source_id = foundation_value_id}); } } // T implicitly converts to U if T and U are the same ignoring qualifiers, and // we're allowed to remove / add any qualifiers that differ. Similarly, T // explicitly converts to U if T is compatible with U, and we're allowed to // remove / add any qualifiers that differ. if (target.type_id != value_type_id) { auto [target_foundation_id, target_quals] = target.is_explicit_as() ? context.types().GetTransitiveUnqualifiedAdaptedType( target.type_id) : context.types().GetUnqualifiedTypeAndQualifiers(target.type_id); auto [value_foundation_id, value_quals] = target.is_explicit_as() ? context.types().GetTransitiveUnqualifiedAdaptedType(value_type_id) : context.types().GetUnqualifiedTypeAndQualifiers(value_type_id); if (target_foundation_id == value_foundation_id) { auto category = SemIR::GetExprCategory(context.sem_ir(), value_id); auto added_quals = target_quals & ~value_quals; auto removed_quals = value_quals & ~target_quals; if (CanAddQualifiers(added_quals, category) && CanRemoveQualifiers(removed_quals, category, target.kind)) { // For a struct or tuple literal, perform a category conversion if // necessary. if (category == SemIR::ExprCategory::Mixed) { value_id = PerformBuiltinConversion(context, loc_id, value_id, {.kind = ConversionTarget::Value, .type_id = value_type_id, .diagnose = target.diagnose}); } // `MaybeUnformed(T)` might have a pointer value representation when `T` // does not, so convert as needed when removing `MaybeUnformed`. bool need_value_binding = false; if ((removed_quals & SemIR::TypeQualifiers::MaybeUnformed) != SemIR::TypeQualifiers::None && category == SemIR::ExprCategory::Value) { auto value_rep = SemIR::ValueRepr::ForType(context.sem_ir(), value_type_id); auto unformed_value_rep = SemIR::ValueRepr::ForType(context.sem_ir(), target.type_id); if (value_rep.kind != unformed_value_rep.kind) { CARBON_CHECK(unformed_value_rep.kind == SemIR::ValueRepr::Pointer); value_id = AddInst( context, loc_id, {.type_id = value_type_id, .value_id = value_id}); need_value_binding = true; } } if ((removed_quals & SemIR::TypeQualifiers::Partial) != SemIR::TypeQualifiers::None && SemIR::IsInitializerCategory(category)) { auto unqual_target_type_id = context.types().GetUnqualifiedType(target.type_id); if (auto target_class_type = context.types().TryGetAs( unqual_target_type_id)) { value_id = ConvertPartialInitializerToNonPartial( context, target, *target_class_type, value_id); } } value_id = AddInst( context, loc_id, {.type_id = target.type_id, .source_id = value_id}); if (need_value_binding) { value_id = AddInst( context, loc_id, {.type_id = target.type_id, .value_id = value_id}); } return value_id; } else { // TODO: Produce a custom diagnostic explaining that we can't perform // this conversion due to the change in qualifiers and/or the expression // category. } } } // A tuple (T1, T2, ..., Tn) converts to (U1, U2, ..., Un) if each Ti // converts to Ui. if (auto target_tuple_type = target_type_inst.TryAs()) { if (auto src_tuple_type = sem_ir.types().TryGetAs(value_type_id)) { return ConvertTupleToTuple(context, *src_tuple_type, *target_tuple_type, value_id, target); } } // A struct {.f_1: T_1, .f_2: T_2, ..., .f_n: T_n} converts to // {.f_p(1): U_p(1), .f_p(2): U_p(2), ..., .f_p(n): U_p(n)} if // (p(1), ..., p(n)) is a permutation of (1, ..., n) and each Ti converts // to Ui. if (auto target_struct_type = target_type_inst.TryAs()) { if (auto src_struct_type = sem_ir.types().TryGetAs(value_type_id)) { return ConvertStructToStruct(context, *src_struct_type, *target_struct_type, value_id, target); } } // No other conversions apply when the source and destination types are the // same. if (value_type_id == target.type_id) { return value_id; } // A tuple (T1, T2, ..., Tn) converts to array(T, n) if each Ti converts to T. if (auto target_array_type = target_type_inst.TryAs()) { if (auto src_tuple_type = sem_ir.types().TryGetAs(value_type_id)) { return ConvertTupleToArray(context, *src_tuple_type, *target_array_type, value_id, target); } } // Split the qualifiers off the target type. // TODO: Most conversions should probably be looking at the unqualified target // type. auto [target_unqual_type_id, target_quals] = context.types().GetUnqualifiedTypeAndQualifiers(target.type_id); auto target_unqual_type_inst = sem_ir.types().GetAsInst(target_unqual_type_id); // A struct {.f_1: T_1, .f_2: T_2, ..., .f_n: T_n} converts to a class type // if it converts to the struct type that is the class's representation type // (a struct with the same fields as the class, plus a base field where // relevant). if (auto target_class_type = target_unqual_type_inst.TryAs()) { if (auto src_struct_type = sem_ir.types().TryGetAs(value_type_id)) { if (!context.classes() .Get(target_class_type->class_id) .adapt_id.has_value()) { return ConvertStructToClass( context, *src_struct_type, *target_class_type, value_id, target, target_quals.HasAnyOf(SemIR::TypeQualifiers::Partial)); } } // An expression of type T converts to U if T is a class derived from U. // // TODO: Combine this with the qualifiers and adapter conversion logic above // to allow qualifiers and inheritance conversions to be performed together. if (auto path = ComputeInheritancePath(context, loc_id, value_type_id, target.type_id); path && !path->empty()) { return ConvertDerivedToBase(context, loc_id, value_id, *path); } } // A pointer T* converts to [qualified] U* if T is the same as U, or is a // class derived from U. if (auto target_pointer_type = target_type_inst.TryAs()) { if (auto src_pointer_type = sem_ir.types().TryGetAs(value_type_id)) { auto target_pointee_id = context.types().GetTypeIdForTypeInstId( target_pointer_type->pointee_id); auto src_pointee_id = context.types().GetTypeIdForTypeInstId(src_pointer_type->pointee_id); // Try to complete the pointee types so that we can walk through adapters // to their adapted types. TryToCompleteType(context, target_pointee_id, loc_id); TryToCompleteType(context, src_pointee_id, loc_id); auto [unqual_target_pointee_type_id, target_quals] = sem_ir.types().GetTransitiveUnqualifiedAdaptedType(target_pointee_id); auto [unqual_src_pointee_type_id, src_quals] = sem_ir.types().GetTransitiveUnqualifiedAdaptedType(src_pointee_id); // If the qualifiers are incompatible, we can't perform a conversion, // except with `unsafe as`. if ((src_quals & ~target_quals) != SemIR::TypeQualifiers::None && target.kind != ConversionTarget::ExplicitUnsafeAs) { // TODO: Consider producing a custom diagnostic here for a cast that // discards constness. return value_id; } if (unqual_target_pointee_type_id != unqual_src_pointee_type_id) { // If there's an inheritance path from target to source, this is a // derived to base conversion. if (auto path = ComputeInheritancePath(context, loc_id, unqual_src_pointee_type_id, unqual_target_pointee_type_id); path && !path->empty()) { value_id = ConvertDerivedPointerToBasePointer( context, loc_id, *src_pointer_type, target.type_id, value_id, *path); } else { // No conversion was possible. return value_id; } } // Perform a compatible conversion to add any new qualifiers. if (src_quals != target_quals) { return AddInst( context, loc_id, {.type_id = target.type_id, .source_id = value_id}); } return value_id; } } if (sem_ir.types().IsFacetType(target.type_id)) { auto type_value_id = SemIR::TypeInstId::None; // A tuple of types converts to type `type`. if (sem_ir.types().Is(value_type_id)) { type_value_id = ConvertTupleToType(context, loc_id, value_id, value_type_id, target); } // `{}` converts to `{} as type`. if (auto struct_type = sem_ir.types().TryGetAs(value_type_id)) { if (struct_type->fields_id == SemIR::StructTypeFieldsId::Empty) { type_value_id = sem_ir.types().GetTypeInstId(value_type_id); } } if (type_value_id != SemIR::InstId::None) { if (sem_ir.types().Is(target.type_id)) { // Use the converted `TypeType` value for converting to a facet. value_id = type_value_id; value_type_id = SemIR::TypeType::TypeId; } else { // We wanted a `TypeType`, and we've done that. return type_value_id; } } } // FacetType converts to Type by wrapping the facet value in // FacetAccessType. if (target.type_id == SemIR::TypeType::TypeId && sem_ir.types().Is(value_type_id)) { return AddInst( context, loc_id, {.type_id = target.type_id, .facet_value_inst_id = value_id}); } // Type values can convert to facet values, and facet values can convert to // other facet values, as long as they satisfy the required interfaces of the // target `FacetType`. if (sem_ir.types().Is(target.type_id) && sem_ir.types().IsOneOf( value_type_id)) { // TODO: Runtime facet values should be allowed to convert based on their // FacetTypes, but we assume constant values for impl lookup at the moment. if (!context.constant_values().Get(value_id).is_constant()) { context.TODO(loc_id, "conversion of runtime facet value"); return SemIR::ErrorInst::InstId; } // Get the canonical type for which we want to attach a new set of witnesses // to match the requirements of the target FacetType. auto type_inst_id = SemIR::TypeInstId::None; if (sem_ir.types().Is(value_type_id)) { type_inst_id = AddTypeInst( context, loc_id, {.type_id = SemIR::TypeType::TypeId, .facet_value_inst_id = value_id}); } else { type_inst_id = context.types().GetAsTypeInstId(value_id); // Shortcut for lossless round trips through a FacetAccessType (which // evaluates to SymbolicBindingType when wrapping a symbolic binding) when // converting back to the type of the original symbolic binding facet // value. // // In the case where the FacetAccessType wraps a SymbolicBinding with the // exact facet type that we are converting to, the resulting FacetValue // would evaluate back to the original SymbolicBinding as its canonical // form. We can skip past the whole impl lookup step then and do that // here. // // TODO: This instruction is going to become a `SymbolicBindingType`, so // we'll need to handle that instead. auto facet_value_inst_id = GetCanonicalFacetOrTypeValue(context, type_inst_id); if (sem_ir.insts().Get(facet_value_inst_id).type_id() == target.type_id) { return facet_value_inst_id; } } // Conversion from a facet value (which has type `FacetType`) or a type // value (which has type `TypeType`) to a facet value. We can do this if the // type satisfies the requirements of the target `FacetType`, as determined // by finding impl witnesses for the target FacetType. auto lookup_result = LookupImplWitness( context, loc_id, sem_ir.constant_values().Get(type_inst_id), sem_ir.types().GetConstantId(target.type_id)); if (lookup_result.has_value()) { if (lookup_result.has_error_value()) { return SemIR::ErrorInst::InstId; } else { // Note that `FacetValue`'s type is the same `FacetType` that was used // to construct the set of witnesses, ie. the query to // `LookupImplWitness()`. This ensures that the witnesses are in the // same order as the `required_impls()` in the `IdentifiedFacetType` of // the `FacetValue`'s type. return AddInst( context, loc_id, {.type_id = target.type_id, .type_inst_id = type_inst_id, .witnesses_block_id = lookup_result.inst_block_id()}); } } else { // If impl lookup fails, don't keep looking for another way to convert. // See https://github.com/carbon-language/carbon-lang/issues/5122. // TODO: Pass this function into `LookupImplWitness` so it can construct // the error add notes explaining failure. if (target.diagnose) { DiagnoseConversionFailureToConstraintValue(context, loc_id, value_id, target.type_id); } return SemIR::ErrorInst::InstId; } } // No builtin conversion applies. return value_id; } // Determine whether this is a C++ enum type. // TODO: This should be removed once we can properly add a `Copy` impl for C++ // enum types. static auto IsCppEnum(Context& context, SemIR::TypeId type_id) -> bool { auto class_type = context.types().TryGetAs(type_id); if (!class_type) { return false; } // A C++-imported class type that is an adapter is an enum. auto& class_info = context.classes().Get(class_type->class_id); return class_info.adapt_id.has_value() && context.name_scopes().Get(class_info.scope_id).is_cpp_scope(); } // Given a value expression, form a corresponding initializer that copies from // that value to the specified target, if it is possible to do so. static auto PerformCopy(Context& context, SemIR::InstId expr_id, const ConversionTarget& target) -> SemIR::InstId { // TODO: We don't have a mechanism yet to generate `Copy` impls for each enum // type imported from C++. For now we fake it by providing a direct copy. auto type_id = context.insts().Get(expr_id).type_id(); if (IsCppEnum(context, type_id)) { return expr_id; } auto copy_id = BuildUnaryOperator( context, SemIR::LocId(expr_id), {.interface_name = CoreIdentifier::Copy}, expr_id, target.diagnose, [&](auto& builder) { CARBON_DIAGNOSTIC(CopyOfUncopyableType, Context, "cannot copy value of type {0}", TypeOfInstId); builder.Context(expr_id, CopyOfUncopyableType, expr_id); }); return copy_id; } // Convert a value expression so that it can be used to initialize a C++ thunk // parameter. static auto ConvertValueForCppThunkRef(Context& context, SemIR::InstId expr_id) -> SemIR::InstId { auto expr = context.insts().Get(expr_id); // If the expression has a pointer value representation, extract that and use // it directly. if (SemIR::ValueRepr::ForType(context.sem_ir(), expr.type_id()).kind == SemIR::ValueRepr::Pointer) { return AddInst( context, SemIR::LocId(expr_id), {.type_id = expr.type_id(), .value_id = expr_id}); } // Otherwise, we need a temporary to pass as the thunk argument. Create a copy // and initialize a temporary from it. auto temporary_id = AddInst( context, SemIR::LocId(expr_id), {.type_id = expr.type_id()}); expr_id = Initialize(context, SemIR::LocId(expr_id), temporary_id, expr_id); return AddInstWithCleanup(context, SemIR::LocId(expr_id), {.type_id = expr.type_id(), .storage_id = temporary_id, .init_id = expr_id}); } // Returns the Core interface name to use for a given kind of conversion. static auto GetConversionInterfaceName(ConversionTarget::Kind kind) -> CoreIdentifier { switch (kind) { case ConversionTarget::ExplicitAs: return CoreIdentifier::As; case ConversionTarget::ExplicitUnsafeAs: return CoreIdentifier::UnsafeAs; default: return CoreIdentifier::ImplicitAs; } } auto PerformAction(Context& context, SemIR::LocId loc_id, SemIR::ConvertToValueAction action) -> SemIR::InstId { return Convert(context, loc_id, action.inst_id, {.kind = ConversionTarget::Value, .type_id = context.types().GetTypeIdForTypeInstId( action.target_type_inst_id)}); } // State machine for performing category conversions. class CategoryConverter { public: // Constructs a converter which converts an expression at the given location // to the given conversion target. CategoryConverter(Context& context, SemIR::LocId loc_id, ConversionTarget& target) : context_(context), sem_ir_(context.sem_ir()), loc_id_(loc_id), target_(target) {} // Converts expr_id to the target specified in the constructor, and returns // the converted inst. auto Convert(SemIR::InstId expr_id) && -> SemIR::InstId { auto category = SemIR::GetExprCategory(sem_ir_, expr_id); while (true) { if (expr_id == SemIR::ErrorInst::InstId) { return expr_id; } CARBON_KIND_SWITCH(DoStep(expr_id, category)) { case CARBON_KIND(NextStep next_step): { CARBON_CHECK(next_step.expr_id != SemIR::InstId::None); expr_id = next_step.expr_id; category = next_step.category; break; } case CARBON_KIND(Done done): { return done.expr_id; } } } } private: // State that indicates there's more work to be done. As a convenience, // if expr_id is SemIR::ErrorInst::InstId, this is equivalent to // Done{SemIR::ErrorInst::InstId}. struct NextStep { // The inst to convert. SemIR::InstId expr_id; // The category of expr_id. SemIR::ExprCategory category; }; // State that indicates we've finished category conversion. struct Done { // The result of the conversion. SemIR::InstId expr_id; }; using State = std::variant; // Performs the first step of converting `expr_id` with category `category` // to the target specified in the constructor, and returns the state after // that step. auto DoStep(SemIR::InstId expr_id, SemIR::ExprCategory category) const -> State; Context& context_; SemIR::File& sem_ir_; SemIR::LocId loc_id_; const ConversionTarget& target_; }; auto CategoryConverter::DoStep(const SemIR::InstId expr_id, const SemIR::ExprCategory category) const -> State { CARBON_DCHECK(SemIR::GetExprCategory(sem_ir_, expr_id) == category || // TODO: Drop this special case once PerformCopy on C++ enums // produces an initializing expression. IsCppEnum(context_, target_.type_id)); switch (category) { case SemIR::ExprCategory::NotExpr: case SemIR::ExprCategory::Mixed: case SemIR::ExprCategory::Pattern: CARBON_FATAL("Unexpected expression {0} after builtin conversions", sem_ir_.insts().Get(expr_id)); case SemIR::ExprCategory::Error: return Done{SemIR::ErrorInst::InstId}; case SemIR::ExprCategory::Dependent: context_.TODO(expr_id, "Support symbolic expression forms"); return Done{SemIR::ErrorInst::InstId}; case SemIR::ExprCategory::InPlaceInitializing: case SemIR::ExprCategory::ReprInitializing: if (target_.is_initializer()) { // Overwrite the initializer's storage argument with the inst currently // at target_.storage_id, if both are present and the storage argument // hasn't already been set. However, we skip this if the type is a C++ // enum: in that case, we don't actually have an initializing // expression, we're just pretending we do. auto new_storage_id = target_.storage_id; if (!IsCppEnum(context_, target_.type_id)) { new_storage_id = OverwriteTemporaryStorageArg(sem_ir_, expr_id, target_); } // If in-place initialization was requested, and it hasn't already // happened, ensure it happens now. if (target_.kind == ConversionTarget::InPlaceInitializing && category != SemIR::ExprCategory::InPlaceInitializing && SemIR::InitRepr::ForType(sem_ir_, target_.type_id) .MightBeByCopy()) { target_.storage_access_block->InsertHere(); return Done{AddInst(context_, loc_id_, {.type_id = target_.type_id, .src_id = expr_id, .dest_id = new_storage_id})}; } return Done{expr_id}; } if (target_.kind == ConversionTarget::Discarded) { DiscardInitializer(context_, expr_id); return Done{SemIR::InstId::None}; } else if (IsValidExprCategoryForConversionTarget(category, target_.kind)) { return Done{expr_id}; } else { // Commit to using a temporary for this initializing expression. // TODO: Don't create a temporary if the initializing representation is // already a value representation. // TODO: If the target is DurableRef, materialize a VarStorage instead // of a TemporaryStorage to lifetime-extend. return NextStep{.expr_id = MaterializeTemporary(context_, expr_id), .category = SemIR::ExprCategory::EphemeralRef}; } case SemIR::ExprCategory::RefTagged: { auto tagged_expr_id = sem_ir_.insts().GetAs(expr_id).expr_id; auto tagged_expr_category = SemIR::GetExprCategory(sem_ir_, tagged_expr_id); if (target_.diagnose && tagged_expr_category != SemIR::ExprCategory::DurableRef) { CARBON_DIAGNOSTIC( RefTagNotDurableRef, Error, "expression tagged with `ref` is not a durable reference"); context_.emitter().Emit(tagged_expr_id, RefTagNotDurableRef); } if (target_.kind == ConversionTarget::RefParam) { return Done{expr_id}; } // If the target isn't a reference parameter, ignore the `ref` tag. // Unnecessary `ref` tags are diagnosed earlier. return NextStep{.expr_id = tagged_expr_id, .category = tagged_expr_category}; } case SemIR::ExprCategory::DurableRef: if (target_.kind == ConversionTarget::DurableRef || target_.kind == ConversionTarget::UnmarkedRefParam) { return Done{expr_id}; } if (target_.kind == ConversionTarget::RefParam) { if (target_.diagnose) { CARBON_DIAGNOSTIC( RefParamNoRefTag, Error, "argument to `ref` parameter not marked with `ref`"); context_.emitter().Emit(expr_id, RefParamNoRefTag); } return Done{expr_id}; } [[fallthrough]]; case SemIR::ExprCategory::EphemeralRef: // If a reference expression is an acceptable result, we're done. if (target_.kind == ConversionTarget::ValueOrRef || target_.kind == ConversionTarget::Discarded || target_.kind == ConversionTarget::CppThunkRef || target_.kind == ConversionTarget::RefParam || target_.kind == ConversionTarget::UnmarkedRefParam) { return Done{expr_id}; } // If we have a reference and don't want one, form a value binding. // TODO: Support types with custom value representations. return NextStep{.expr_id = AddInst( context_, SemIR::LocId(expr_id), {.type_id = target_.type_id, .value_id = expr_id}), .category = SemIR::ExprCategory::Value}; case SemIR::ExprCategory::Value: if (target_.kind == ConversionTarget::DurableRef) { if (target_.diagnose) { CARBON_DIAGNOSTIC(ConversionFailureNonRefToRef, Error, "cannot bind durable reference to non-reference " "value of type {0}", SemIR::TypeId); context_.emitter().Emit(loc_id_, ConversionFailureNonRefToRef, target_.type_id); } return Done{SemIR::ErrorInst::InstId}; } if (target_.kind == ConversionTarget::RefParam || target_.kind == ConversionTarget::UnmarkedRefParam) { if (target_.diagnose) { CARBON_DIAGNOSTIC(ValueForRefParam, Error, "value expression passed to reference parameter"); context_.emitter().Emit(loc_id_, ValueForRefParam); } return Done{SemIR::ErrorInst::InstId}; } // When initializing from a value, perform a copy. if (target_.is_initializer()) { auto copy_id = PerformCopy(context_, expr_id, target_); if (copy_id == SemIR::ErrorInst::InstId) { return Done{SemIR::ErrorInst::InstId}; } // Deal with special-case category behavior of PerformCopy. switch (SemIR::GetExprCategory(sem_ir_, copy_id)) { case SemIR::ExprCategory::Value: // As a temporary workaround, PerformCopy on a C++ enum currently // returns the unchanged value, but we treat it as an initializing // expression. // TODO: Drop this case once it's no longer applicable. CARBON_CHECK(IsCppEnum(context_, target_.type_id)); [[fallthrough]]; case SemIR::ExprCategory::ReprInitializing: // The common case: PerformCopy produces an initializing expression. return NextStep{.expr_id = copy_id, .category = SemIR::ExprCategory::ReprInitializing}; case SemIR::ExprCategory::InPlaceInitializing: // A C++ copy operation produces an ephemeral entire reference. return NextStep{ .expr_id = copy_id, .category = SemIR::ExprCategory::InPlaceInitializing}; default: CARBON_FATAL("Unexpected category of copy operation {0}", category); } } // When initializing a C++ thunk parameter, form a reference, creating a // temporary if needed. if (target_.kind == ConversionTarget::CppThunkRef) { return Done{ConvertValueForCppThunkRef(context_, expr_id)}; } return Done{expr_id}; } } // Returns true if converting `expr_id` to `target` requires `target.type_id` // to be complete. static auto ConversionNeedsCompleteTarget(Context& context, SemIR::InstId expr_id, ConversionTarget target) -> bool { auto source_type_id = context.insts().Get(expr_id).type_id(); // We allow conversion to incomplete facet types, since their representation // is fixed. This allows us to support using the `Self` of an interface inside // its definition. if (context.types().IsFacetType(target.type_id)) { return false; } // If the types are the same, we only have to worry about form conversions. if (source_type_id == target.type_id) { auto source_category = SemIR::GetExprCategory(context.sem_ir(), expr_id); // If there's no form conversion and no type conversion, the conversion is // a no-op, so we don't need a complete type. if (IsValidExprCategoryForConversionTarget(source_category, target.kind)) { return false; } } return true; } auto Convert(Context& context, SemIR::LocId loc_id, SemIR::InstId expr_id, ConversionTarget target) -> SemIR::InstId { auto& sem_ir = context.sem_ir(); auto orig_expr_id = expr_id; // Start by making sure both sides are non-errors. If any part is an error, // the result is an error and we shouldn't diagnose. if (sem_ir.insts().Get(expr_id).type_id() == SemIR::ErrorInst::TypeId || target.type_id == SemIR::ErrorInst::TypeId) { return SemIR::ErrorInst::InstId; } auto starting_category = SemIR::GetExprCategory(sem_ir, expr_id); if (starting_category == SemIR::ExprCategory::NotExpr) { // TODO: We currently encounter this for use of namespaces and functions. // We should provide a better diagnostic for inappropriate use of // namespace names, and allow use of functions as values. if (target.diagnose) { CARBON_DIAGNOSTIC(UseOfNonExprAsValue, Error, "expression cannot be used as a value"); context.emitter().Emit(expr_id, UseOfNonExprAsValue); } return SemIR::ErrorInst::InstId; } if (target.kind == ConversionTarget::NoOp) { CARBON_CHECK(target.type_id == sem_ir.insts().Get(expr_id).type_id()); return expr_id; } // Diagnose unnecessary `ref` tags early, so that they're not obscured by // conversions. if (starting_category == SemIR::ExprCategory::RefTagged && target.kind != ConversionTarget::RefParam && target.diagnose) { CARBON_DIAGNOSTIC(RefTagNoRefParam, Error, "`ref` tag is not an argument to a `ref` parameter"); context.emitter().Emit(expr_id, RefTagNoRefParam); } // TODO: Allow abstract but complete types if the conversion is just a // same-type value acqisition. // TODO: Push this check down to the points where we perform operations that // need the type to be complete. if (ConversionNeedsCompleteTarget(context, expr_id, target)) { if (target.diagnose) { if (!RequireConcreteType( context, target.type_id, loc_id, [&](auto& builder) { CARBON_CHECK( !target.is_initializer(), "Initialization of incomplete types is expected to be " "caught elsewhere."); CARBON_DIAGNOSTIC(IncompleteTypeInValueConversion, Context, "forming value of incomplete type {0}", SemIR::TypeId); CARBON_DIAGNOSTIC(IncompleteTypeInConversion, Context, "invalid use of incomplete type {0}", SemIR::TypeId); builder.Context(loc_id, target.kind == ConversionTarget::Value ? IncompleteTypeInValueConversion : IncompleteTypeInConversion, target.type_id); }, [&](auto& builder) { CARBON_DIAGNOSTIC(AbstractTypeInInit, Context, "initialization of abstract type {0}", SemIR::TypeId); builder.Context(loc_id, AbstractTypeInInit, target.type_id); })) { return SemIR::ErrorInst::InstId; } } else { if (!TryIsConcreteType(context, target.type_id, loc_id)) { return SemIR::ErrorInst::InstId; } } } // Clear storage_id in cases where it's clearly meaningless, to avoid misuse // and simplify the resulting SemIR. if (!target.is_initializer() || SemIR::InitRepr::ForType(context.sem_ir(), target.type_id).kind == SemIR::InitRepr::None) { target.storage_id = SemIR::InstId::None; } // The source type doesn't need to be complete, but its completeness can // affect the result. For example, we don't know what type it adapts or // derives from unless it's complete. // TODO: Is there a risk of coherence problems if the source type is // incomplete, but a conversion would have been possible or would have behaved // differently if it were complete? TryToCompleteType(context, context.insts().Get(expr_id).type_id(), loc_id); // Check whether any builtin conversion applies. expr_id = PerformBuiltinConversion(context, loc_id, expr_id, target); if (expr_id == SemIR::ErrorInst::InstId) { return expr_id; } // Defer the action if it's dependent. We do this now rather than before // attempting any conversion so that we can still perform builtin conversions // on dependent arguments. This matters for things like converting a // `template T:! SomeInterface` to `type`, where it's important to form a // `FacetAccessType` when checking the template. But when running the action // later, we need to try builtin conversions again, because one may apply that // didn't apply in the template definition. // TODO: Support this for targets other than `Value`. if (sem_ir.insts().Get(expr_id).type_id() != target.type_id && target.kind == ConversionTarget::Value && (OperandIsDependent(context, expr_id) || OperandIsDependent(context, target.type_id))) { auto target_type_inst_id = context.types().GetTypeInstId(target.type_id); return AddDependentActionSplice( context, loc_id, SemIR::ConvertToValueAction{ .type_id = GetSingletonType(context, SemIR::InstType::TypeInstId), .inst_id = expr_id, .target_type_inst_id = target_type_inst_id}, target_type_inst_id); } // If this is not a builtin conversion, try an `ImplicitAs` conversion. if (sem_ir.insts().Get(expr_id).type_id() != target.type_id) { SemIR::InstId interface_args[] = { context.types().GetTypeInstId(target.type_id)}; Operator op = { .interface_name = GetConversionInterfaceName(target.kind), .interface_args_ref = interface_args, .op_name = CoreIdentifier::Convert, }; expr_id = BuildUnaryOperator( context, loc_id, op, expr_id, target.diagnose, [&](auto& builder) { int target_kind_for_diag = target.kind == ConversionTarget::ExplicitAs ? 1 : target.kind == ConversionTarget::ExplicitUnsafeAs ? 2 : 0; if (target.type_id == SemIR::TypeType::TypeId || sem_ir.types().Is(target.type_id)) { CARBON_DIAGNOSTIC( ConversionFailureNonTypeToFacet, Context, "cannot{0:=0: implicitly|:} convert non-type value of type {1} " "{2:to|into type implementing} {3}" "{0:=1: with `as`|=2: with `unsafe as`|:}", Diagnostics::IntAsSelect, TypeOfInstId, Diagnostics::BoolAsSelect, SemIR::TypeId); builder.Context(loc_id, ConversionFailureNonTypeToFacet, target_kind_for_diag, expr_id, target.type_id == SemIR::TypeType::TypeId, target.type_id); } else { CARBON_DIAGNOSTIC( ConversionFailure, Context, "cannot{0:=0: implicitly|:} convert expression of type " "{1} to {2}{0:=1: with `as`|=2: with `unsafe as`|:}", Diagnostics::IntAsSelect, TypeOfInstId, SemIR::TypeId); builder.Context(loc_id, ConversionFailure, target_kind_for_diag, expr_id, target.type_id); } }); // Pull a value directly out of the initializer if possible and wanted. if (expr_id != SemIR::ErrorInst::InstId && CanUseValueOfInitializer(sem_ir, target.type_id, target.kind)) { expr_id = AddInst( context, loc_id, {.type_id = target.type_id, .init_id = expr_id}); } } // Track that we performed a type conversion, if we did so. if (orig_expr_id != expr_id) { expr_id = AddInst(context, loc_id, {.type_id = target.type_id, .original_id = orig_expr_id, .result_id = expr_id}); } // For `as`, don't perform any value category conversions. In particular, an // identity conversion shouldn't change the expression category. if (target.is_explicit_as()) { return expr_id; } // Now perform any necessary value category conversions. expr_id = CategoryConverter(context, loc_id, target).Convert(expr_id); return expr_id; } auto Initialize(Context& context, SemIR::LocId loc_id, SemIR::InstId storage_id, SemIR::InstId value_id, bool for_return) -> SemIR::InstId { auto type_id = context.insts().Get(storage_id).type_id(); if (for_return && !SemIR::InitRepr::ForType(context.sem_ir(), type_id).MightBeInPlace()) { // TODO: is it safe to use storage_id when the init repr is dependent? storage_id = SemIR::InstId::None; } // TODO: add CHECK that storage_id.index < value_id.index to enforce the // precondition, once existing violations have been cleaned up. PendingBlock target_block(&context); return Convert(context, loc_id, value_id, {.kind = ConversionTarget::Initializing, .type_id = type_id, .storage_id = storage_id, .storage_access_block = &target_block}); } auto ConvertToValueExpr(Context& context, SemIR::InstId expr_id) -> SemIR::InstId { return Convert(context, SemIR::LocId(expr_id), expr_id, {.kind = ConversionTarget::Value, .type_id = context.insts().Get(expr_id).type_id()}); } auto ConvertToValueOrRefExpr(Context& context, SemIR::InstId expr_id) -> SemIR::InstId { return Convert(context, SemIR::LocId(expr_id), expr_id, {.kind = ConversionTarget::ValueOrRef, .type_id = context.insts().Get(expr_id).type_id()}); } auto ConvertToValueOfType(Context& context, SemIR::LocId loc_id, SemIR::InstId expr_id, SemIR::TypeId type_id, bool diagnose) -> SemIR::InstId { return Convert(context, loc_id, expr_id, {.kind = ConversionTarget::Value, .type_id = type_id, .diagnose = diagnose}); } auto ConvertToValueOrRefOfType(Context& context, SemIR::LocId loc_id, SemIR::InstId expr_id, SemIR::TypeId type_id) -> SemIR::InstId { return Convert(context, loc_id, expr_id, {.kind = ConversionTarget::ValueOrRef, .type_id = type_id}); } // Like ConvertToValueOfType but failure to convert does not result in // diagnostics. An ErrorInst instruction is still returned on failure. auto TryConvertToValueOfType(Context& context, SemIR::LocId loc_id, SemIR::InstId expr_id, SemIR::TypeId type_id) -> SemIR::InstId { return Convert( context, loc_id, expr_id, {.kind = ConversionTarget::Value, .type_id = type_id, .diagnose = false}); } auto ConvertToBoolValue(Context& context, SemIR::LocId loc_id, SemIR::InstId value_id) -> SemIR::InstId { return ConvertToValueOfType( context, loc_id, value_id, GetSingletonType(context, SemIR::BoolType::TypeInstId)); } auto ConvertForExplicitAs(Context& context, Parse::NodeId as_node, SemIR::InstId value_id, SemIR::TypeId type_id, bool unsafe) -> SemIR::InstId { return Convert(context, as_node, value_id, {.kind = unsafe ? ConversionTarget::ExplicitUnsafeAs : ConversionTarget::ExplicitAs, .type_id = type_id}); } // TODO: Consider moving this to pattern_match.h. auto ConvertCallArgs(Context& context, SemIR::LocId call_loc_id, SemIR::InstId self_id, llvm::ArrayRef arg_refs, llvm::ArrayRef return_arg_ids, const SemIR::Function& callee, SemIR::SpecificId callee_specific_id, bool is_operator_syntax) -> SemIR::InstBlockId { auto param_patterns = context.inst_blocks().GetOrEmpty(callee.param_patterns_id); auto return_patterns_id = callee.return_patterns_id; // The caller should have ensured this callee has the right arity. CARBON_CHECK(arg_refs.size() == param_patterns.size()); if (callee.self_param_id.has_value() && !self_id.has_value()) { CARBON_DIAGNOSTIC(MissingObjectInMethodCall, Error, "missing object argument in method call"); CARBON_DIAGNOSTIC(InCallToFunction, Note, "calling function declared here"); context.emitter() .Build(call_loc_id, MissingObjectInMethodCall) .Note(callee.latest_decl_id(), InCallToFunction) .Emit(); self_id = SemIR::ErrorInst::InstId; } return CallerPatternMatch(context, callee_specific_id, callee.self_param_id, callee.param_patterns_id, return_patterns_id, self_id, arg_refs, return_arg_ids, is_operator_syntax); } auto TypeExpr::ForUnsugared(Context& context, SemIR::TypeId type_id) -> TypeExpr { return {.inst_id = context.types().GetTypeInstId(type_id), .type_id = type_id}; } static auto DiagnoseTypeExprEvaluationFailure(Context& context, SemIR::LocId loc_id) -> void { CARBON_DIAGNOSTIC(TypeExprEvaluationFailure, Error, "cannot evaluate type expression"); context.emitter().Emit(loc_id, TypeExprEvaluationFailure); } auto ExprAsType(Context& context, SemIR::LocId loc_id, SemIR::InstId value_id, bool diagnose) -> TypeExpr { auto type_as_inst_id = ConvertToValueOfType( context, loc_id, value_id, SemIR::TypeType::TypeId, diagnose); if (type_as_inst_id == SemIR::ErrorInst::InstId) { return {.inst_id = SemIR::ErrorInst::TypeInstId, .type_id = SemIR::ErrorInst::TypeId}; } auto type_as_const_id = context.constant_values().Get(type_as_inst_id); if (!type_as_const_id.is_constant()) { if (diagnose) { DiagnoseTypeExprEvaluationFailure(context, loc_id); } return {.inst_id = SemIR::ErrorInst::TypeInstId, .type_id = SemIR::ErrorInst::TypeId}; } return { .inst_id = context.types().GetAsTypeInstId(type_as_inst_id), .type_id = context.types().GetTypeIdForTypeConstantId(type_as_const_id)}; } auto FormExprAsForm(Context& context, SemIR::LocId loc_id, SemIR::InstId value_id) -> Context::FormExpr { auto form_inst_id = ConvertToValueOfType(context, loc_id, value_id, SemIR::FormType::TypeId); if (form_inst_id == SemIR::ErrorInst::InstId) { return Context::FormExpr::Error; } auto form_const_id = context.constant_values().Get(form_inst_id); if (!form_const_id.is_constant()) { CARBON_DIAGNOSTIC(FormExprEvaluationFailure, Error, "cannot evaluate form expression"); context.emitter().Emit(loc_id, FormExprEvaluationFailure); return Context::FormExpr::Error; } auto type_id = GetTypeComponent(context, form_inst_id); auto type_inst_id = context.types().GetTypeInstId(type_id); return {.form_inst_id = form_inst_id, .type_component_inst_id = type_inst_id, .type_component_id = type_id}; } auto ReturnExprAsForm(Context& context, SemIR::LocId loc_id, SemIR::InstId value_id) -> Context::FormExpr { auto form_inst_id = SemIR::InstId::None; auto type_inst_id = SemIR::InstId::None; if (auto ref_tag = context.insts().TryGetAs(value_id)) { type_inst_id = ConvertToValueOfType(context, loc_id, ref_tag->expr_id, SemIR::TypeType::TypeId); if (type_inst_id == SemIR::ErrorInst::InstId) { return Context::FormExpr::Error; } if (!context.constant_values().Get(type_inst_id).is_constant()) { DiagnoseTypeExprEvaluationFailure(context, SemIR::LocId(ref_tag->expr_id)); return Context::FormExpr::Error; } form_inst_id = AddInst( context, SemIR::LocIdAndInst::UncheckedLoc( loc_id, SemIR::RefForm{.type_id = SemIR::FormType::TypeId, .type_component_inst_id = context.types().GetAsTypeInstId(type_inst_id)})); } else { type_inst_id = ConvertToValueOfType(context, loc_id, value_id, SemIR::TypeType::TypeId); if (type_inst_id == SemIR::ErrorInst::InstId) { return Context::FormExpr::Error; } if (!context.constant_values().Get(type_inst_id).is_constant()) { DiagnoseTypeExprEvaluationFailure(context, loc_id); return Context::FormExpr::Error; } form_inst_id = AddInst( context, SemIR::LocIdAndInst::UncheckedLoc( loc_id, SemIR::InitForm{ .type_id = SemIR::FormType::TypeId, .type_component_inst_id = context.types().GetAsTypeInstId(type_inst_id)})); } auto type_const_id = context.constant_values().Get(type_inst_id); CARBON_CHECK(type_const_id.is_constant()); return { .form_inst_id = form_inst_id, .type_component_inst_id = context.types().GetAsTypeInstId(type_inst_id), .type_component_id = context.types().GetTypeIdForTypeConstantId(type_const_id)}; } auto DiscardExpr(Context& context, SemIR::InstId expr_id) -> void { // If we discard an initializing expression, convert it to a value or // reference so that it has something to initialize. auto expr = context.insts().Get(expr_id); Convert(context, SemIR::LocId(expr_id), expr_id, {.kind = ConversionTarget::Discarded, .type_id = expr.type_id()}); // TODO: This will eventually need to do some "do not discard" analysis. } } // namespace Carbon::Check // NOLINTEND(misc-no-recursion) ================================================ FILE: toolchain/check/convert.h ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #ifndef CARBON_TOOLCHAIN_CHECK_CONVERT_H_ #define CARBON_TOOLCHAIN_CHECK_CONVERT_H_ #include "toolchain/check/context.h" #include "toolchain/check/pending_block.h" #include "toolchain/sem_ir/entity_with_params_base.h" #include "toolchain/sem_ir/ids.h" namespace Carbon::Check { // Description of the target of a conversion. struct ConversionTarget { enum Kind : int8_t { // Perform no conversion. The source expression must already have type // `type_id`. NoOp, // Convert to a value of type `type_id`. Value, // Convert to either a value or a reference of type `type_id`. ValueOrRef, // Convert to a durable reference of type `type_id`. DurableRef, // Convert to a reference, suitable for binding to a reference parameter. // This allows both durable and ephemeral references. The restriction that // only a `ref self` parameter can bind to an ephemeral reference is // enforced separately when handling `ref` tags on call arguments. RefParam, // Equivalent to RefParam, except that the source expression is not required // to be marked with a `ref` tag, such as an argument to a `ref self` // parameter or an operator operand. UnmarkedRefParam, // Convert to a reference of type `type_id`, for use as the argument to a // C++ thunk. CppThunkRef, // Convert for an explicit `as` cast. This allows any expression category // as the result, and uses the `As` interface instead of the `ImplicitAs` // interface. ExplicitAs, // Convert for an explicit `unsafe as` cast. This allows any expression // category as the result, and uses the `UnsafeAs` interface instead of the // `As` or `ImplicitAs` interface. ExplicitUnsafeAs, // The result of the conversion is discarded. It can't be an initializing // expression, but can be anything else. Discarded, // Convert to an initializing expression that uses `type_id`'s initializing // representation. The resulting expression will usually be a // repr-initializing expression, but may be an in-place initializing // expression if the source expression was. If `storage_id` is present, it // is used as the storage argument for the converted expression, and it must // be present if the initializing representation might be in-place. Initializing, // Convert to an in-place initializing expression whose storage is // designated by `storage_id` (which must not be `None`) InPlaceInitializing, Last = InPlaceInitializing }; // The kind of the target for this conversion. Kind kind; // The target type for the conversion. SemIR::TypeId type_id; // The storage being initialized, if any. SemIR::InstId storage_id = SemIR::InstId::None; // For an initializer, a block of pending instructions that `storage_id` // depends on, and that can be discarded if `storage_id` is not accessed. // If this is not null or empty, its last element must be storage_id. PendingBlock* storage_access_block = nullptr; // Whether failure of conversion is an error and is diagnosed to the user. // When looking for a possible conversion but with graceful fallback, // `diagnose` should be false. If `diagnose` is false, an `ErrorInst` may be // returned, but it must be discarded. bool diagnose = true; // Are we converting this value into an initializer for an object? auto is_initializer() const -> bool { return kind == Initializing || kind == InPlaceInitializing; } // Is this some kind of explicit `as` conversion? auto is_explicit_as() const -> bool { return kind == ExplicitAs || kind == ExplicitUnsafeAs; } }; // Convert a value to another type and expression category. auto Convert(Context& context, SemIR::LocId loc_id, SemIR::InstId expr_id, ConversionTarget target) -> SemIR::InstId; // Converts `value_id` to an initializing expression of the type of // `storage_id`, and returns the possibly-converted initializing expression. // `storage_id` is used as the storage argument of the resulting expression // except as noted below, and when it is used as the storage argument it must // precede `value_id`. The caller is responsible for passing the result to an // inst that is documented as consuming it, such as `Assign`. // // `for_return` indicates that this conversion is initializing the operand of a // `return` statement. This means that `storage_id` will be the return slot // parameter, which isn't valid to access if the type's initializing // representation is not in-place, so in that case `storage_id` will be used // solely for its type. // // TODO: Consider making the target type a separate parameter, and making // storage_id optional. auto Initialize(Context& context, SemIR::LocId loc_id, SemIR::InstId storage_id, SemIR::InstId value_id, bool for_return = false) -> SemIR::InstId; // Convert the given expression to a value expression of the same type. auto ConvertToValueExpr(Context& context, SemIR::InstId expr_id) -> SemIR::InstId; // Convert the given expression to a value or reference expression of the same // type. auto ConvertToValueOrRefExpr(Context& context, SemIR::InstId expr_id) -> SemIR::InstId; // Converts `expr_id` to a value expression of type `type_id`. // // If `diagnose` is true, errors are diagnosed to the user. Set it to false when // looking to see if a conversion is possible but with graceful fallback. If // `diagnose` is false, an `ErrorInst` may be returned, but it must be // discarded. auto ConvertToValueOfType(Context& context, SemIR::LocId loc_id, SemIR::InstId expr_id, SemIR::TypeId type_id, bool diagnose = true) -> SemIR::InstId; // Convert the given expression to a value or reference expression of the given // type. auto ConvertToValueOrRefOfType(Context& context, SemIR::LocId loc_id, SemIR::InstId expr_id, SemIR::TypeId type_id) -> SemIR::InstId; // Attempted to convert `expr_id` to a value expression of type `type_id`, with // graceful failure, which does not result in diagnostics. An ErrorInst // instruction is still returned on failure. auto TryConvertToValueOfType(Context& context, SemIR::LocId loc_id, SemIR::InstId expr_id, SemIR::TypeId type_id) -> SemIR::InstId; // Converts `value_id` to a value expression of type `bool`. auto ConvertToBoolValue(Context& context, SemIR::LocId loc_id, SemIR::InstId value_id) -> SemIR::InstId; // Converts `value_id` to type `type_id` for an `as` expression. auto ConvertForExplicitAs(Context& context, Parse::NodeId as_node, SemIR::InstId value_id, SemIR::TypeId type_id, bool unsafe) -> SemIR::InstId; // Implicitly converts a set of arguments to match the parameter types in a // function call. Returns a block containing the converted implicit and explicit // argument values for runtime parameters. `is_operator_syntax` indicates that // this call was generated from an operator rather than from function call // syntax, so arguments to `ref` parameters aren't required to have `ref` tags. auto ConvertCallArgs(Context& context, SemIR::LocId call_loc_id, SemIR::InstId self_id, llvm::ArrayRef arg_refs, llvm::ArrayRef return_arg_ids, const SemIR::Function& callee, SemIR::SpecificId callee_specific_id, bool is_operator_syntax) -> SemIR::InstBlockId; // A type that has been converted for use as a type expression. struct TypeExpr { static const TypeExpr None; // Returns a TypeExpr describing a type with no associated spelling or type // sugar. static auto ForUnsugared(Context& context, SemIR::TypeId type_id) -> TypeExpr; // The converted expression of type `type`, or `ErrorInst::InstId`. SemIR::TypeInstId inst_id; // The corresponding type, or `ErrorInst::TypeId`. SemIR::TypeId type_id; }; inline constexpr TypeExpr TypeExpr::None = {.inst_id = SemIR::TypeInstId::None, .type_id = SemIR::TypeId::None}; // Converts an expression for use as a type. // // If `diagnose` is true, errors are diagnosed to the user. Set it to false when // looking to see if a conversion is possible but with graceful fallback. If // `diagnose` is false, an `ErrorInst` may be returned, but it must be // discarded. // // TODO: Most of the callers of this function discard the `inst_id` and lose // track of the conversion. In most cases we should be retaining that as the // operand of some downstream instruction. auto ExprAsType(Context& context, SemIR::LocId loc_id, SemIR::InstId value_id, bool diagnose = true) -> TypeExpr; // Converts an expression in a form position for use as a form. // // Note that the right-hand side of a `->` return type declaration is not // a form position for this purpose, because it uses a special syntax to specify // forms. `ReturnExprAsForm` should be used instead in that case. // // `diagnose` has the same effect as in `ExprAsType`. auto FormExprAsForm(Context& context, SemIR::LocId loc_id, SemIR::InstId value_id) -> Context::FormExpr; // Evaluates an expression in the return-type position (following `->`, not // `->?`) for use as a form, following the special-case language rules for // evaluating an expression in that position. auto ReturnExprAsForm(Context& context, SemIR::LocId loc_id, SemIR::InstId value_id) -> Context::FormExpr; // Handles an expression whose result value is unused. auto DiscardExpr(Context& context, SemIR::InstId expr_id) -> void; } // namespace Carbon::Check #endif // CARBON_TOOLCHAIN_CHECK_CONVERT_H_ ================================================ FILE: toolchain/check/core_identifier.cpp ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #include "toolchain/check/core_identifier.h" namespace Carbon::Check { CARBON_DEFINE_ENUM_CLASS_NAMES(CoreIdentifier) { #define CARBON_CORE_IDENTIFIER(Name) CARBON_ENUM_CLASS_NAME_STRING(Name) #include "toolchain/check/core_identifier.def" }; } // namespace Carbon::Check ================================================ FILE: toolchain/check/core_identifier.def ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // This is an X-macro header. It does not use `#include` guards, and instead is // designed to be `#include`ed after the x-macro is defined in order for its // inclusion to expand to the desired output. Macro definitions are cleaned up // at the end of this file. // // This macro should be defined before including this header: // - CARBON_CORE_IDENTIFIER(Name) // Invoked for each `Core` identifier. #ifndef CARBON_CORE_IDENTIFIER #error "Must define the x-macro to use this file." #define CARBON_CORE_IDENTIFIER(Name) #endif CARBON_CORE_IDENTIFIER(AddAssignWith) CARBON_CORE_IDENTIFIER(AddWith) CARBON_CORE_IDENTIFIER(As) CARBON_CORE_IDENTIFIER(AssignWith) CARBON_CORE_IDENTIFIER(At) CARBON_CORE_IDENTIFIER(BitAndAssignWith) CARBON_CORE_IDENTIFIER(BitAndWith) CARBON_CORE_IDENTIFIER(BitComplement) CARBON_CORE_IDENTIFIER(BitOrAssignWith) CARBON_CORE_IDENTIFIER(BitOrWith) CARBON_CORE_IDENTIFIER(BitXorAssignWith) CARBON_CORE_IDENTIFIER(BitXorWith) CARBON_CORE_IDENTIFIER(Bool) CARBON_CORE_IDENTIFIER(Char) CARBON_CORE_IDENTIFIER(Convert) CARBON_CORE_IDENTIFIER(Copy) CARBON_CORE_IDENTIFIER(CppCompat) CARBON_CORE_IDENTIFIER(CppUnsafeDeref) CARBON_CORE_IDENTIFIER(Dec) CARBON_CORE_IDENTIFIER(DefaultOrUnformed) CARBON_CORE_IDENTIFIER(Destroy) CARBON_CORE_IDENTIFIER(DivAssignWith) CARBON_CORE_IDENTIFIER(DivWith) CARBON_CORE_IDENTIFIER(EqWith) CARBON_CORE_IDENTIFIER(Equal) CARBON_CORE_IDENTIFIER(Float) CARBON_CORE_IDENTIFIER(Get) CARBON_CORE_IDENTIFIER(Greater) CARBON_CORE_IDENTIFIER(GreaterOrEquivalent) CARBON_CORE_IDENTIFIER(HasValue) CARBON_CORE_IDENTIFIER(ImplicitAs) CARBON_CORE_IDENTIFIER(Inc) CARBON_CORE_IDENTIFIER(IndexWith) CARBON_CORE_IDENTIFIER(Int) CARBON_CORE_IDENTIFIER(IntFitsIn) CARBON_CORE_IDENTIFIER(Iterate) CARBON_CORE_IDENTIFIER(LeftShiftAssignWith) CARBON_CORE_IDENTIFIER(LeftShiftWith) CARBON_CORE_IDENTIFIER(Less) CARBON_CORE_IDENTIFIER(LessOrEquivalent) CARBON_CORE_IDENTIFIER(Long32) CARBON_CORE_IDENTIFIER(LongLong64) CARBON_CORE_IDENTIFIER(ModAssignWith) CARBON_CORE_IDENTIFIER(ModWith) CARBON_CORE_IDENTIFIER(MulAssignWith) CARBON_CORE_IDENTIFIER(MulWith) CARBON_CORE_IDENTIFIER(Negate) CARBON_CORE_IDENTIFIER(NewCursor) CARBON_CORE_IDENTIFIER(Next) CARBON_CORE_IDENTIFIER(NotEqual) CARBON_CORE_IDENTIFIER(NullptrT) CARBON_CORE_IDENTIFIER(Op) CARBON_CORE_IDENTIFIER(Optional) CARBON_CORE_IDENTIFIER(OrderedWith) CARBON_CORE_IDENTIFIER(RightShiftAssignWith) CARBON_CORE_IDENTIFIER(RightShiftWith) CARBON_CORE_IDENTIFIER(String) CARBON_CORE_IDENTIFIER(SubAssignWith) CARBON_CORE_IDENTIFIER(SubWith) CARBON_CORE_IDENTIFIER(UInt) CARBON_CORE_IDENTIFIER(ULong32) CARBON_CORE_IDENTIFIER(ULongLong64) CARBON_CORE_IDENTIFIER(UnsafeAs) CARBON_CORE_IDENTIFIER(VoidBase) #undef CARBON_CORE_IDENTIFIER ================================================ FILE: toolchain/check/core_identifier.h ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #ifndef CARBON_TOOLCHAIN_CHECK_CORE_IDENTIFIER_H_ #define CARBON_TOOLCHAIN_CHECK_CORE_IDENTIFIER_H_ #include "common/enum_base.h" #include "toolchain/base/shared_value_stores.h" #include "toolchain/base/value_ids.h" #include "toolchain/sem_ir/ids.h" namespace Carbon::Check { CARBON_DEFINE_RAW_ENUM_CLASS(CoreIdentifier, uint8_t) { #define CARBON_CORE_IDENTIFIER(Name) CARBON_RAW_ENUM_ENUMERATOR(Name) #include "toolchain/check/core_identifier.def" }; // An identifier in `Core` that's significant for the language (typically used // by desugaring) but not a builtin or keyword. Note this includes non-top-level // identifiers, such as both `AddWith` and `Op` in `Core.AddWith.Op`. class CoreIdentifier : public CARBON_ENUM_BASE(CoreIdentifier) { public: #define CARBON_CORE_IDENTIFIER(Name) CARBON_ENUM_CONSTANT_DECL(Name) #include "toolchain/check/core_identifier.def" private: // Exposes `AsInt`. friend class CoreIdentifierCache; }; #define CARBON_CORE_IDENTIFIER(Name) \ CARBON_ENUM_CONSTANT_DEFINITION(CoreIdentifier, Name) #include "toolchain/check/core_identifier.def" // A cache of added `Core` identifiers. These are added to the identifier // store on first use. class CoreIdentifierCache { public: explicit CoreIdentifierCache(SharedValueStores::IdentifierStore* identifiers) : identifiers_(identifiers) {} // Returns the `NameId` for a `CoreIdentifier`. auto AddNameId(CoreIdentifier identifier) -> SemIR::NameId { auto& value = cache_[identifier.AsInt()]; if (!value.has_value()) { value = SemIR::NameId::ForIdentifier(identifiers_->Add(identifier.name())); } return value; } private: // The number of cache entries. static constexpr int CacheSize = 0 #define CARBON_CORE_IDENTIFIER(Name) +1 #include "toolchain/check/core_identifier.def" ; // A pointer for adding identifiers. SharedValueStores::IdentifierStore* identifiers_; // The cache of added identifiers. These are stored as a `NameId` because the // `IdentifierId` isn't directly used. SemIR::NameId cache_[CacheSize] = { #define CARBON_CORE_IDENTIFIER(Name) SemIR::NameId::None, #include "toolchain/check/core_identifier.def" }; }; } // namespace Carbon::Check #endif // CARBON_TOOLCHAIN_CHECK_CORE_IDENTIFIER_H_ ================================================ FILE: toolchain/check/cpp/access.cpp ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #include "toolchain/check/cpp/access.h" namespace Carbon::Check { static auto CalculateEffectiveAccess(clang::DeclAccessPair access_pair) -> clang::AccessSpecifier { // Note that we use `.getAccess()` here, not `->getAccess()`, which is // equivalent to `.getDecl()->getAccess()`, because we want to consider the // lookup access and not the lexical access. switch (access_pair.getAccess()) { // Lookup access takes precedence. case clang::AS_public: case clang::AS_protected: case clang::AS_private: return access_pair.getAccess(); case clang::AS_none: // No access specified meaning depends on the declaration. For non class // members, it means there's no access associated with this function so we // treat it as public. For class members it means we lost access along the // inheritance path, and the difference between `none` and `private` only // matters when the access check is performed within a friend or member of // the naming class. Because the naming class is a C++ class, and we don't // yet have a mechanism for a C++ class to befriend a Carbon class, we can // safely map `none` to `private` for now. return access_pair->isCXXClassMember() ? clang::AS_private : clang::AS_public; } } auto MapCppAccess(clang::DeclAccessPair access_pair) -> SemIR::AccessKind { switch (CalculateEffectiveAccess(access_pair)) { case clang::AS_public: return SemIR::AccessKind::Public; case clang::AS_protected: return SemIR::AccessKind::Protected; case clang::AS_private: return SemIR::AccessKind::Private; case clang::AS_none: CARBON_FATAL("Couldn't convert access"); } } } // namespace Carbon::Check ================================================ FILE: toolchain/check/cpp/access.h ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #ifndef CARBON_TOOLCHAIN_CHECK_CPP_ACCESS_H_ #define CARBON_TOOLCHAIN_CHECK_CPP_ACCESS_H_ #include "toolchain/sem_ir/name_scope.h" namespace Carbon::Check { // Calculates the effective access kind from the given (declaration, lookup // access) pair. auto MapCppAccess(clang::DeclAccessPair access_pair) -> SemIR::AccessKind; } // namespace Carbon::Check #endif // CARBON_TOOLCHAIN_CHECK_CPP_ACCESS_H_ ================================================ FILE: toolchain/check/cpp/call.cpp ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #include "toolchain/check/cpp/call.h" #include "clang/Sema/Sema.h" #include "clang/Sema/Template.h" #include "toolchain/base/kind_switch.h" #include "toolchain/check/call.h" #include "toolchain/check/cpp/constant.h" #include "toolchain/check/cpp/import.h" #include "toolchain/check/cpp/location.h" #include "toolchain/check/cpp/operators.h" #include "toolchain/check/cpp/overload_resolution.h" #include "toolchain/check/cpp/type_mapping.h" #include "toolchain/check/literal.h" #include "toolchain/sem_ir/function.h" #include "toolchain/sem_ir/ids.h" #include "toolchain/sem_ir/typed_insts.h" namespace Carbon::Check { // Returns true if the given instruction can only be a template argument, and // not a function argument. We classify arguments as definitely being template // arguments if they are types or the name of a template or generic. // TODO: We should also have a way to specify that an argument is a non-type // template argument. static auto IsTemplateArg(Context& context, SemIR::InstId arg_id) -> bool { auto arg_type_id = context.insts().Get(arg_id).type_id(); auto arg_type = context.types().GetAsInst(arg_type_id); return arg_type .IsOneOf(); } // Splits a call argument list into a list of template arguments followed by a // list of function arguments. We split the argument list as early as possible, // subject to the constraint that if an argument is a template argument, it goes // in the template argument list. static auto SplitCallArgumentList(Context& context, llvm::ArrayRef arg_ids) -> std::pair, llvm::ArrayRef> { for (auto [n, arg_id] : llvm::enumerate(llvm::reverse(arg_ids))) { if (IsTemplateArg(context, arg_id)) { return {arg_ids.drop_back(n), arg_ids.take_back(n)}; } } // No template arguments found. return {{}, arg_ids}; } auto PerformCallToCppFunction(Context& context, SemIR::LocId loc_id, SemIR::CppOverloadSetId overload_set_id, SemIR::InstId self_id, llvm::ArrayRef arg_ids, bool is_operator_syntax) -> SemIR::InstId { auto [template_arg_ids, function_arg_ids] = SplitCallArgumentList(context, arg_ids); auto callee_id = PerformCppOverloadResolution( context, loc_id, context.cpp_overload_sets().Get(overload_set_id), template_arg_ids, self_id, function_arg_ids); SemIR::Callee callee = GetCallee(context.sem_ir(), callee_id); CARBON_KIND_SWITCH(callee) { case CARBON_KIND(SemIR::CalleeError _): { return SemIR::ErrorInst::InstId; } case CARBON_KIND(SemIR::CalleeFunction fn): { CARBON_CHECK(!fn.self_id.has_value()); if (self_id.has_value()) { // Preserve the `self` argument from the original callee. fn.self_id = self_id; } return PerformCallToFunction(context, loc_id, callee_id, fn, function_arg_ids, is_operator_syntax); } case CARBON_KIND(SemIR::CalleeCppOverloadSet _): { CARBON_FATAL("overloads can't be recursive"); } case CARBON_KIND(SemIR::CalleeNonFunction _): { CARBON_FATAL("overloads should produce functions"); } } } // Synthesize a placeholder `void{}` template argument, that will never be a // valid argument for any template parameter. This is used in order to get Clang // to diagnose invalid template argument errors for us. The location of the // Carbon expression is used as the location of the C++ expression, so // Clang's diagnostics will point into the Carbon code. // // TODO: If Clang ever tries to print the type of the expression or to // pretty-print the expression itself, it would print the wrong thing. Currently // this doesn't appear to happen, but in principle it could. Ideally we'd add an // extension point to Clang to represent a "foreign expression" and use it here // instead of creating a bogus placeholder expression. static auto MakePlaceholderTemplateArg(Context& context, SemIR::InstId arg_id) -> clang::TemplateArgumentLoc { auto arg_loc = GetCppLocation(context, SemIR::LocId(arg_id)); auto void_type = context.ast_context().VoidTy; auto* arg = new (context.ast_context()) clang::CXXScalarValueInitExpr( void_type, context.ast_context().getTrivialTypeSourceInfo(void_type, arg_loc), arg_loc); return clang::TemplateArgumentLoc( clang::TemplateArgument(arg, /*IsCanonical=*/false), arg); } // Converts an argument in a call to a C++ template name into a corresponding // clang template argument, given the template parameter it will be matched // against. static auto ConvertArgToTemplateArg( Context& context, clang::TemplateDecl* template_decl, clang::NamedDecl* param_decl, SemIR::InstId arg_id, clang::SmallVector* template_args, unsigned argument_pack_index, bool diagnose) -> std::optional { if (isa(param_decl)) { auto type = ExprAsType(context, SemIR::LocId(arg_id), arg_id, diagnose); if (type.type_id == SemIR::ErrorInst::TypeId) { return std::nullopt; } auto clang_type = MapToCppType(context, type.type_id); if (clang_type.isNull()) { if (diagnose) { context.TODO(arg_id, "unsupported type used as template argument"); } return std::nullopt; } return clang::TemplateArgumentLoc( clang_type, context.ast_context().getTrivialTypeSourceInfo( clang_type, GetCppLocation(context, SemIR::LocId(arg_id)))); } if (isa(param_decl)) { auto inst = context.sem_ir().insts().Get(arg_id); if (auto template_name_type = context.types().TryGetAs( inst.type_id())) { clang::TemplateName name(cast( context.clang_decls().Get(template_name_type->decl_id).key.decl)); return clang::TemplateArgumentLoc( context.ast_context(), clang::TemplateArgument(name), /*TemplateKWLoc=*/clang::SourceLocation(), clang::NestedNameSpecifierLoc(), GetCppLocation(context, SemIR::LocId(arg_id))); } // TODO: Eventually we should also support passing Carbon generics as // template template arguments. return MakePlaceholderTemplateArg(context, arg_id); } if (auto* non_type = dyn_cast(param_decl)) { auto param_type = non_type->getType(); if (non_type->isParameterPack() && non_type->isExpandedParameterPack()) { param_type = non_type->getExpansionType(argument_pack_index); } // Handle non-type parameters with a dependent type. For example: // // C++: template struct S{}; // Carbon: Cpp.S(i32, 42) // // When evaluating the second template argument, the generic type of // `T` should be substituted with `i32`. if (param_type->isInstantiationDependentType()) { // If we don't want to diagnose errors, create a SFINAE context so that // Clang knows to suppress error messages. std::optional sfinae; if (!diagnose) { sfinae.emplace(context.clang_sema()); } clang::Sema::InstantiatingTemplate inst( context.clang_sema(), clang::SourceLocation(), param_decl, non_type, *template_args, clang::SourceRange()); if (inst.isInvalid()) { return std::nullopt; } clang::MultiLevelTemplateArgumentList mltal(template_decl, *template_args, /*Final=*/true); mltal.addOuterRetainedLevels(non_type->getDepth()); if (const auto* pet = param_type->getAs()) { clang::Sema::ArgPackSubstIndexRAII subst_index(context.clang_sema(), argument_pack_index); param_type = context.clang_sema().SubstType(pet->getPattern(), mltal, non_type->getLocation(), non_type->getDeclName()); } else { param_type = context.clang_sema().SubstType(param_type, mltal, non_type->getLocation(), non_type->getDeclName()); } if (!param_type.isNull()) { param_type = context.clang_sema().CheckNonTypeTemplateParameterType( param_type, non_type->getLocation()); } if (param_type.isNull() || (sfinae && sfinae->hasErrorOccurred())) { return std::nullopt; } } // Get the Carbon type corresponding to the parameter's Clang type. const auto type_expr = ImportCppType(context, SemIR::LocId(arg_id), param_type); // Try to convert the argument to the parameter type. const auto converted_inst_id = Convert(context, SemIR::LocId(arg_id), arg_id, { .kind = ConversionTarget::Value, .type_id = type_expr.type_id, .diagnose = diagnose, }); if (converted_inst_id == SemIR::ErrorInst::InstId) { return std::nullopt; } // TODO: provide a better location. auto template_loc = clang::TemplateArgumentLocInfo(); auto const_inst_id = context.constant_values().GetConstantInstId(converted_inst_id); if (const_inst_id.has_value()) { if (param_type->isPointerType()) { if (auto addr_of = context.insts().TryGetAs(const_inst_id)) { if (auto* var_decl = GetAsClangVarDecl(context, addr_of->lvalue_id)) { clang::TemplateArgument template_arg(var_decl, param_type); return clang::TemplateArgumentLoc(template_arg, template_loc); } // TODO: support pointers to variables declared in Carbon. } } else if (auto ap_value = MapConstantToAPValue(context, const_inst_id, param_type)) { clang::TemplateArgument template_arg(context.ast_context(), param_type, *ap_value); return clang::TemplateArgumentLoc(template_arg, template_loc); } } // TODO: Support other types. if (diagnose) { context.TODO(arg_id, "unsupported argument type for non-type template parameter"); } return std::nullopt; } CARBON_FATAL("Unknown declaration kind for template parameter"); } auto ConvertArgsToTemplateArgs(Context& context, clang::TemplateDecl* template_decl, llvm::ArrayRef arg_ids, clang::TemplateArgumentListInfo& arg_list, bool diagnose) -> bool { clang::SmallVector template_args; for (auto* param_decl : template_decl->getTemplateParameters()->asArray()) { if (arg_ids.empty()) { return true; } // A parameter pack consumes all remaining arguments; otherwise, it consumes // a single argument. // TODO: Handle expanded template parameter packs, which have a known, fixed // arity. llvm::ArrayRef args_for_param = param_decl->isTemplateParameterPack() ? std::exchange(arg_ids, {}) : arg_ids.consume_front(); for (auto [argument_pack_index, arg_id] : llvm::enumerate(args_for_param)) { if (auto arg = ConvertArgToTemplateArg(context, template_decl, param_decl, arg_id, &template_args, argument_pack_index, diagnose)) { arg_list.addArgument(*arg); template_args.push_back(arg->getArgument()); argument_pack_index++; } else { return false; } } } // If there are any remaining arguments, that's an error; convert them to // placeholder template arguments so that Clang will diagnose it for us. for (auto arg_id : arg_ids) { // Synthesize a placeholder `void{}` template argument. arg_list.addArgument(MakePlaceholderTemplateArg(context, arg_id)); } return true; } // Given a template and an template argument list, builds a Carbon value // describing the corresponding C++ template-id. static auto BuildTemplateId(Context& context, SemIR::LocId loc_id, clang::SourceLocation loc, clang::TemplateDecl* template_decl, clang::TemplateArgumentListInfo& arg_list) -> SemIR::InstId { if (auto* var_template_decl = dyn_cast(template_decl)) { auto decl_result = context.clang_sema().CheckVarTemplateId( var_template_decl, /*TemplateLoc=*/clang::SourceLocation(), loc, arg_list, /*SetWrittenArgs=*/false); return decl_result.isInvalid() ? SemIR::ErrorInst::InstId : ImportCppDecl(context, loc_id, SemIR::ClangDeclKey::ForNonFunctionDecl( decl_result.get())); } if (auto* concept_decl = dyn_cast(template_decl)) { auto expr_result = context.clang_sema().CheckConceptTemplateId( clang::CXXScopeSpec(), /*TemplateKWLoc=*/clang::SourceLocation(), clang::DeclarationNameInfo(concept_decl->getDeclName(), loc), concept_decl, concept_decl, &arg_list); if (expr_result.isInvalid()) { return SemIR::ErrorInst::InstId; } auto* expr = expr_result.getAs(); return MakeBoolLiteral(context, loc_id, SemIR::BoolValue::From(expr->isSatisfied())); } clang::TemplateName template_name(template_decl); auto clang_type = context.clang_sema().CheckTemplateIdType( clang::ElaboratedTypeKeyword::None, template_name, loc, arg_list, /*Scope=*/nullptr, /*ForNestedNameSpecifier=*/false); if (clang_type.isNull()) { return SemIR::ErrorInst::InstId; } return ImportCppType(context, loc_id, clang_type).inst_id; } auto PerformCallToCppTemplateName(Context& context, SemIR::LocId loc_id, SemIR::ClangDeclId template_decl_id, llvm::ArrayRef arg_ids) -> SemIR::InstId { auto* template_decl = dyn_cast( context.clang_decls().Get(template_decl_id).key.decl); auto loc = GetCppLocation(context, loc_id); // Form a template argument list for this template. clang::TemplateArgumentListInfo arg_list(loc, loc); if (!ConvertArgsToTemplateArgs(context, template_decl, arg_ids, arg_list)) { return SemIR::ErrorInst::InstId; } return BuildTemplateId(context, loc_id, loc, template_decl, arg_list); } } // namespace Carbon::Check ================================================ FILE: toolchain/check/cpp/call.h ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #ifndef CARBON_TOOLCHAIN_CHECK_CPP_CALL_H_ #define CARBON_TOOLCHAIN_CHECK_CPP_CALL_H_ #include "toolchain/check/context.h" #include "toolchain/sem_ir/ids.h" namespace Carbon::Check { // Converts a call argument list into a Clang template argument list for a given // template. Returns true on success, or false if an error was diagnosed. // // If `diagnose` is false, errors will be suppressed. auto ConvertArgsToTemplateArgs(Context& context, clang::TemplateDecl* template_decl, llvm::ArrayRef arg_ids, clang::TemplateArgumentListInfo& arg_list, bool diagnose = true) -> bool; // Checks and builds SemIR for a call to a C++ function in the given overload // set with self `self_id` and arguments `arg_ids`. `is_operator_syntax` // indicates that this call was generated from an operator rather than from // function call syntax, so arguments to `ref` parameters aren't required to // have `ref` tags. // // Chooses the best viable C++ function by performing Clang overloading // resolution over the overload set. // // Preserves the given self, if set. If not set, and the function is a C++ // member operator, self will be set to the first argument, which in turn will // be removed from the given args. // // A set with a single non-templated function goes through the same rules for // overloading resolution. This is to make sure that calls that have no viable // implicit conversion sequence are rejected even when an implicit conversion is // possible. Keeping the same behavior here for consistency and supporting // migrations so that the migrated callers from C++ remain valid. auto PerformCallToCppFunction(Context& context, SemIR::LocId loc_id, SemIR::CppOverloadSetId overload_set_id, SemIR::InstId self_id, llvm::ArrayRef arg_ids, bool is_operator_syntax) -> SemIR::InstId; // Checks and builds SemIR for a call to a C++ template name with arguments // `arg_ids`. // // Converts the arguments to a C++ template argument list and attempts to // instantiate a template specialization and import a declaration of it. auto PerformCallToCppTemplateName(Context& context, SemIR::LocId loc_id, SemIR::ClangDeclId template_decl_id, llvm::ArrayRef arg_ids) -> SemIR::InstId; } // namespace Carbon::Check #endif // CARBON_TOOLCHAIN_CHECK_CPP_CALL_H_ ================================================ FILE: toolchain/check/cpp/constant.cpp ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #include "toolchain/check/cpp/constant.h" #include "toolchain/check/cpp/import.h" #include "toolchain/check/cpp/type_mapping.h" #include "toolchain/check/eval.h" #include "toolchain/check/member_access.h" #include "toolchain/check/type.h" #include "toolchain/check/type_completion.h" #include "toolchain/diagnostics/format_providers.h" namespace Carbon::Check { static auto MapLValueToConstant(Context& context, SemIR::LocId loc_id, const clang::APValue& ap_value, clang::QualType type) -> SemIR::ConstantId { CARBON_CHECK(ap_value.isLValue(), "not an LValue"); const auto* value_decl = ap_value.getLValueBase().get(); if (!ap_value.hasLValuePath()) { context.TODO(loc_id, "lvalue has no path"); return SemIR::ErrorInst::ConstantId; } if (ap_value.isLValueOnePastTheEnd()) { context.TODO(loc_id, "one-past-the-end lvalue"); return SemIR::ErrorInst::ConstantId; } auto key = SemIR::ClangDeclKey::ForNonFunctionDecl( // TODO: can this const_cast be avoided? const_cast(value_decl)); auto inst_id = ImportCppDecl(context, loc_id, key); if (ap_value.getLValuePath().empty()) { return context.constant_values().Get(inst_id); } // Import the base type so that its fields can be accessed. auto var_storage = context.insts().GetAs(inst_id); // TODO: currently an error isn't reachable here because incomplete // array types can't be imported. Once that changes, switch to // `RequireCompleteType` and handle the error. CompleteTypeOrCheckFail(context, var_storage.type_id); clang::QualType qual_type = ap_value.getLValueBase().getType(); for (const auto& entry : ap_value.getLValuePath()) { if (qual_type->isArrayType()) { context.TODO(loc_id, "lvalue path contains an array type"); } else { const auto* decl = cast(entry.getAsBaseOrMember().getPointer()); const auto* field_decl = dyn_cast(decl); if (!field_decl) { context.TODO(loc_id, "lvalue path contains a base class subobject"); return SemIR::ErrorInst::ConstantId; } auto field_inst_id = ImportCppDecl(context, loc_id, SemIR::ClangDeclKey::ForNonFunctionDecl( const_cast(field_decl))); if (field_inst_id == SemIR::ErrorInst::InstId) { context.TODO(loc_id, "unsupported field in lvalue path: " + ap_value.getAsString(context.ast_context(), type)); return SemIR::ErrorInst::ConstantId; } const SemIR::FieldDecl& field_decl_inst = context.insts().GetAs(field_inst_id); qual_type = field_decl->getType(); inst_id = PerformMemberAccess(context, loc_id, inst_id, field_decl_inst.name_id); } } return context.constant_values().Get(inst_id); } auto MapAPValueToConstant(Context& context, SemIR::LocId loc_id, const clang::APValue& ap_value, clang::QualType type, bool is_lvalue) -> SemIR::ConstantId { SemIR::TypeId type_id = ImportCppType(context, loc_id, type).type_id; if (!type_id.has_value()) { return SemIR::ConstantId::NotConstant; } if (is_lvalue) { return MapLValueToConstant(context, loc_id, ap_value, type); } else if (type->isPointerType()) { auto const_id = MapLValueToConstant(context, loc_id, ap_value, type); auto inst_id = AddInst( context, loc_id, {.type_id = type_id, .lvalue_id = context.constant_values().GetInstId(const_id)}); return context.constant_values().Get(inst_id); } else if (ap_value.isInt()) { if (type->isBooleanType()) { auto value = SemIR::BoolValue::From(!ap_value.getInt().isZero()); return TryEvalInst( context, SemIR::BoolLiteral{.type_id = type_id, .value = value}); } else { CARBON_CHECK(type->isIntegralOrEnumerationType()); IntId int_id = context.ints().Add(ap_value.getInt()); return TryEvalInst(context, SemIR::IntValue{.type_id = type_id, .int_id = int_id}); } } else if (ap_value.isFloat()) { FloatId float_id = context.floats().Add(ap_value.getFloat()); return TryEvalInst( context, SemIR::FloatValue{.type_id = type_id, .float_id = float_id}); } else { // TODO: support other types. context.TODO(loc_id, "unsupported conversion to constant from APValue " + ap_value.getAsString(context.ast_context(), type)); return SemIR::ErrorInst::ConstantId; } } static auto MapAPValueToConstantForConstexpr(Context& context, SemIR::LocId loc_id, const clang::APValue& ap_value, clang::QualType type) -> SemIR::ConstantId { bool is_lvalue = false; if (type->isReferenceType()) { is_lvalue = true; type = type.getNonReferenceType(); } return MapAPValueToConstant(context, loc_id, ap_value, type, is_lvalue); } auto EvalCppVarDecl(Context& context, SemIR::LocId loc_id, const clang::VarDecl* var_decl, SemIR::TypeId type_id) -> SemIR::ConstantId { // If the C++ global is constant, map it to a Carbon constant. if (var_decl->isUsableInConstantExpressions(context.ast_context())) { if (const auto* ap_value = var_decl->getEvaluatedValue()) { auto clang_type = MapToCppType(context, type_id); if (clang_type.isNull()) { context.TODO(loc_id, "failed to map C++ type to Carbon"); return SemIR::ErrorInst::ConstantId; } return MapAPValueToConstantForConstexpr(context, loc_id, *ap_value, clang_type); } } return SemIR::ConstantId::NotConstant; } auto MapConstantToAPValue(Context& context, SemIR::InstId const_inst_id, clang::QualType param_type) -> std::optional { if (param_type->isIntegerType()) { const bool is_signed = param_type->isSignedIntegerOrEnumerationType(); if (auto int_value = context.insts().TryGetAs(const_inst_id)) { const auto& ap_int = context.ints().Get(int_value->int_id); auto aps_int = llvm::APSInt(ap_int, !is_signed) .extOrTrunc(context.ast_context().getIntWidth(param_type)); return clang::APValue(aps_int); } else if (auto bool_value = context.insts().TryGetAs( const_inst_id)) { llvm::APInt ap_int(context.ast_context().getIntWidth(param_type), bool_value->value.ToBool(), is_signed); auto aps_int = llvm::APSInt(ap_int, !is_signed) .extOrTrunc(context.ast_context().getIntWidth(param_type)); return clang::APValue(aps_int); } } else if (param_type->isFloatingType()) { if (auto float_value = context.insts().TryGetAs(const_inst_id)) { const auto& ap_float = context.floats().Get(float_value->float_id); return clang::APValue(ap_float); } } // TODO: support additional parameter types. return std::nullopt; } static auto ConvertArgToExpr(Context& context, SemIR::InstId arg_inst_id, clang::QualType param_type) -> clang::Expr* { auto const_inst_id = context.constant_values().GetConstantInstId(arg_inst_id); if (!const_inst_id.has_value()) { return nullptr; } auto ap_value = MapConstantToAPValue(context, const_inst_id, param_type); if (!ap_value.has_value()) { return nullptr; } auto* opaque_value_expr = new (context.ast_context()) clang::OpaqueValueExpr( clang::SourceLocation(), param_type, clang::VK_PRValue); return clang::ConstantExpr::Create(context.ast_context(), opaque_value_expr, *ap_value); } auto EvalCppCall(Context& context, SemIR::LocId loc_id, SemIR::ClangDeclId clang_decl_id, SemIR::InstBlockId args_id) -> SemIR::ConstantId { const auto& args = context.inst_blocks().Get(args_id); auto* decl = context.clang_decls().Get(clang_decl_id).GetAsKey().decl; auto* function_decl = cast(decl); // Create expr for the function declaration. auto* decl_ref_expr = clang::DeclRefExpr::Create( context.ast_context(), /*QualifierLoc=*/clang::NestedNameSpecifierLoc(), /*TemplateKWLoc=*/clang::SourceLocation(), function_decl, /*RefersToEnclosingVariableOrCapture=*/false, /*NameLoc=*/clang::SourceLocation(), function_decl->getType(), clang::VK_LValue); // Cast to a function pointer type. auto function_ptr_type = context.ast_context().getPointerType(function_decl->getType()); auto* implicit_cast_expr = clang::ImplicitCastExpr::Create( context.ast_context(), function_ptr_type, clang::CK_FunctionToPointerDecay, decl_ref_expr, nullptr, clang::VK_PRValue, clang::FPOptionsOverride()); // Convert the arguments to exprs. clang::SmallVector arg_exprs; for (auto [arg_inst_id, parm_var_decl] : llvm::zip(args, function_decl->parameters())) { if (auto* arg_expr = ConvertArgToExpr(context, arg_inst_id, parm_var_decl->getType())) { arg_exprs.push_back(arg_expr); } else { return SemIR::ConstantId::NotConstant; } } // Create an expr to call the function. auto* call_expr = clang::CallExpr::Create( context.ast_context(), implicit_cast_expr, arg_exprs, function_decl->getCallResultType(), clang::VK_PRValue, /*RParenLoc=*/clang::SourceLocation(), clang::FPOptionsOverride()); // Evaluate the expr as a constant and map that to Carbon constant. clang::Expr::EvalResult eval_result; if (!call_expr->EvaluateAsConstantExpr(eval_result, context.ast_context())) { // TODO: improve this diagnostic with information from `eval_result`. CARBON_DIAGNOSTIC(CppConstexprEval, Error, "failed to evaluate {0:consteval|constexpr} function " "call as a constant", Diagnostics::BoolAsSelect); context.emitter().Emit(loc_id, CppConstexprEval, function_decl->isConsteval()); return SemIR::ErrorInst::ConstantId; } return MapAPValueToConstantForConstexpr(context, loc_id, eval_result.Val, function_decl->getCallResultType()); } } // namespace Carbon::Check ================================================ FILE: toolchain/check/cpp/constant.h ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #ifndef CARBON_TOOLCHAIN_CHECK_CPP_CONSTANT_H_ #define CARBON_TOOLCHAIN_CHECK_CPP_CONSTANT_H_ #include #include "clang/AST/APValue.h" #include "toolchain/check/context.h" #include "toolchain/sem_ir/ids.h" namespace Carbon::Check { // Converts an `APValue` to a Carbon `ConstantId`. auto MapAPValueToConstant(Context& context, SemIR::LocId loc_id, const clang::APValue& ap_value, clang::QualType type, bool is_lvalue) -> SemIR::ConstantId; // Converts a Carbon constant instruction to an `APValue`. auto MapConstantToAPValue(Context& context, SemIR::InstId const_inst_id, clang::QualType param_type) -> std::optional; // Attempt to evaluate a C++ constexpr variable as a Carbon constant. auto EvalCppVarDecl(Context& context, SemIR::LocId loc_id, const clang::VarDecl* var_decl, SemIR::TypeId type_id) -> SemIR::ConstantId; // Attempt to evaluate a call to a C++ constexpr/consteval function as a // Carbon constant. auto EvalCppCall(Context& context, SemIR::LocId loc_id, SemIR::ClangDeclId clang_decl_id, SemIR::InstBlockId args_id) -> SemIR::ConstantId; } // namespace Carbon::Check #endif // CARBON_TOOLCHAIN_CHECK_CPP_CONSTANT_H_ ================================================ FILE: toolchain/check/cpp/context.cpp ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #include "toolchain/check/cpp/context.h" #include "clang/AST/Mangle.h" namespace Carbon::Check { CppContext::CppContext(clang::CompilerInstance& instance, std::unique_ptr parser) : ast_context_(&instance.getASTContext()), sema_(&instance.getSema()), parser_(std::move(parser)) {} CppContext::~CppContext() = default; auto CppContext::clang_mangle_context() -> clang::MangleContext& { if (!clang_mangle_context_) { clang_mangle_context_.reset(ast_context().createMangleContext()); } return *clang_mangle_context_; } } // namespace Carbon::Check ================================================ FILE: toolchain/check/cpp/context.h ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #ifndef CARBON_TOOLCHAIN_CHECK_CPP_CONTEXT_H_ #define CARBON_TOOLCHAIN_CHECK_CPP_CONTEXT_H_ #include #include "clang/Basic/SourceLocation.h" #include "clang/Frontend/CompilerInstance.h" #include "clang/Frontend/FrontendAction.h" #include "clang/Parse/Parser.h" #include "common/check.h" #include "llvm/ADT/SmallVector.h" namespace Carbon::Check { // Context for C++ code during check. // // This stores state for a Clang AST and Sema, as well as any additional // information needed to perform mapping between Carbon and C++ types, // declarations, and similar values. class CppContext { public: explicit CppContext(clang::CompilerInstance& instance, std::unique_ptr parser); ~CppContext(); auto ast_context() -> clang::ASTContext& { return *ast_context_; } auto sema() -> clang::Sema& { return *sema_; } auto parser() -> clang::Parser& { return *parser_; } auto clang_mangle_context() -> clang::MangleContext&; auto carbon_file_locations() -> llvm::SmallVector& { return carbon_file_locations_; } private: // The Clang AST context. clang::ASTContext* ast_context_; // The Clang semantic analysis engine. clang::Sema* sema_; // The Clang parser. std::unique_ptr parser_; // Per-Carbon-file start locations for corresponding Clang source buffers. // Owned and managed by code in location.cpp. llvm::SmallVector carbon_file_locations_; // The Clang mangle context for the target in the ASTContext. std::unique_ptr clang_mangle_context_; }; } // namespace Carbon::Check #endif // CARBON_TOOLCHAIN_CHECK_CPP_CONTEXT_H_ ================================================ FILE: toolchain/check/cpp/custom_type_mapping.cpp ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #include "toolchain/check/cpp/custom_type_mapping.h" #include "clang/AST/DeclCXX.h" #include "clang/AST/DeclTemplate.h" #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/StringRef.h" namespace Carbon::Check { // A small, lightweight library of AST matchers. Unlike clang's ASTMatchers, // this avoids heap allocations and is suitable for one-off matching rather than // matching against a whole AST. namespace Matchers { // A matcher for a type T is just a function that takes a T and returns whether // it matched. Matchers should be invoked immediately, and are not expected to // outlive the arguments of the call that created them. // TODO: We could avoid the indirect calls by making the below functions be // templated on the inner matcher. template using Matcher = llvm::function_refbool>; // Returns a matcher for class declarations that determines whether the given // class is a class template specialization in namespace std with the specified // name and template arguments matching the given predicate. static auto StdClassTemplate( llvm::StringLiteral name, Matcher args_matcher [[clang::lifetimebound]]) -> auto { return [=](const clang::CXXRecordDecl* class_decl) -> bool { const auto* specialization = dyn_cast(class_decl); const auto* identifier = class_decl->getIdentifier(); return specialization && identifier && identifier->isStr(name) && specialization->isInStdNamespace() && args_matcher(specialization->getTemplateArgs()); }; } // Returns a matcher that matches types if they are class types whose class // matches the given matcher. static auto Class(Matcher class_matcher [[clang::lifetimebound]]) -> auto { return [=](clang::QualType type) -> bool { const auto* class_decl = type->getAsCXXRecordDecl(); return !type.hasQualifiers() && class_decl && class_matcher(class_decl); }; } // Returns a matcher that determines whether the given template argument is a // type matching the given predicate. static auto TypeTemplateArgument(Matcher type_matcher [[clang::lifetimebound]]) -> auto { return [=](clang::TemplateArgument arg) -> bool { return arg.getKind() == clang::TemplateArgument::Type && type_matcher(arg.getAsType()); }; } // A matcher that determines whether the given type is `char`. static auto Char(clang::QualType type) -> bool { return !type.hasQualifiers() && type->isCharType(); } // Returns a matcher that determines whether the given template argument list // matches the given sequence of template argument matchers. static auto TemplateArgumentsAre( std::initializer_list> arg_matchers [[clang::lifetimebound]]) -> auto { return [=](const clang::TemplateArgumentList& args) -> bool { if (args.size() != arg_matchers.size()) { return false; } for (auto [arg, matcher] : llvm::zip_equal(args.asArray(), arg_matchers)) { if (!matcher(arg)) { return false; } } return true; }; } // A matcher for `std::char_traits`. static auto StdCharTraitsChar(clang::QualType type) -> bool { return Class(StdClassTemplate( "char_traits", TemplateArgumentsAre({TypeTemplateArgument(Char)})))(type); } // A matcher for `std::string_view`. static auto StdStringView(const clang::CXXRecordDecl* record_decl) -> bool { return StdClassTemplate( "basic_string_view", TemplateArgumentsAre({TypeTemplateArgument(Char), TypeTemplateArgument(StdCharTraitsChar)}))( record_decl); } } // end namespace Matchers auto GetCustomCppTypeMapping(const clang::CXXRecordDecl* record_decl) -> CustomCppTypeMapping { if (Matchers::StdStringView(record_decl)) { return CustomCppTypeMapping::Str; } return CustomCppTypeMapping::None; } } // namespace Carbon::Check ================================================ FILE: toolchain/check/cpp/custom_type_mapping.h ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #ifndef CARBON_TOOLCHAIN_CHECK_CPP_CUSTOM_TYPE_MAPPING_H_ #define CARBON_TOOLCHAIN_CHECK_CPP_CUSTOM_TYPE_MAPPING_H_ #include "clang/AST/DeclCXX.h" namespace Carbon::Check { // Carbon types that have a custom mapping from C++. enum class CustomCppTypeMapping : uint8_t { // None. None, // The Carbon `Str` type, which maps to `std::string_view`. Str, }; // Determines whether record_decl is a C++ class that has a custom mapping into // Carbon, and if so, returns the corresponding Carbon type. Otherwise returns // None. auto GetCustomCppTypeMapping(const clang::CXXRecordDecl* record_decl) -> CustomCppTypeMapping; } // namespace Carbon::Check #endif // CARBON_TOOLCHAIN_CHECK_CPP_CUSTOM_TYPE_MAPPING_H_ ================================================ FILE: toolchain/check/cpp/generate_ast.cpp ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #include "toolchain/check/cpp/generate_ast.h" #include #include #include "clang/AST/ASTContext.h" #include "clang/Basic/FileManager.h" #include "clang/CodeGen/ModuleBuilder.h" #include "clang/Frontend/CompilerInstance.h" #include "clang/Frontend/CompilerInvocation.h" #include "clang/Frontend/FrontendAction.h" #include "clang/Frontend/TextDiagnostic.h" #include "clang/Lex/PreprocessorOptions.h" #include "clang/Parse/Parser.h" #include "clang/Sema/ExternalSemaSource.h" #include "clang/Sema/MultiplexExternalSemaSource.h" #include "clang/Sema/Sema.h" #include "common/check.h" #include "common/raw_string_ostream.h" #include "llvm/ADT/IntrusiveRefCntPtr.h" #include "llvm/ADT/StringRef.h" #include "llvm/Support/raw_ostream.h" #include "toolchain/check/context.h" #include "toolchain/check/cpp/import.h" #include "toolchain/check/name_lookup.h" #include "toolchain/diagnostics/diagnostic.h" #include "toolchain/diagnostics/emitter.h" #include "toolchain/diagnostics/format_providers.h" #include "toolchain/parse/node_ids.h" #include "toolchain/sem_ir/cpp_file.h" namespace Carbon::Check { // Add a line marker directive pointing at the location of the `import Cpp` // declaration in the Carbon source file. This will cause Clang's diagnostics // machinery to track and report the location in Carbon code where the import // was written. static auto GenerateLineMarker(Context& context, llvm::raw_ostream& out, int line) { out << "# " << line << " \"" << FormatEscaped(context.tokens().source().filename()) << "\"\n"; } // Generates C++ file contents to #include all requested imports. static auto GenerateCppIncludesHeaderCode( Context& context, llvm::ArrayRef imports) -> std::string { std::string code; llvm::raw_string_ostream code_stream(code); for (const Parse::Tree::PackagingNames& import : imports) { if (import.inline_body_id.has_value()) { // Expand `import Cpp inline "code";` directly into the specified code. auto code_token = context.parse_tree().node_token(import.inline_body_id); // Compute the line number on which the C++ code starts. Usually the code // is specified as a block string literal and starts on the line after the // start of the string token. // TODO: Determine if this is a block string literal without calling // `GetTokenText`, which re-lexes the string. int line = context.tokens().GetLineNumber(code_token); if (context.tokens().GetTokenText(code_token).contains('\n')) { ++line; } GenerateLineMarker(context, code_stream, line); code_stream << context.string_literal_values().Get( context.tokens().GetStringLiteralValue(code_token)) << "\n"; // TODO: Inject a clang pragma here to produce an error if there are // unclosed scopes at the end of this inline C++ fragment. } else if (import.library_id.has_value()) { // Translate `import Cpp library "foo.h";` into `#include "foo.h"`. GenerateLineMarker(context, code_stream, context.tokens().GetLineNumber( context.parse_tree().node_token(import.node_id))); auto name = context.string_literal_values().Get(import.library_id); if (name.starts_with('<') && name.ends_with('>')) { code_stream << "#include <" << FormatEscaped(name.drop_front().drop_back()) << ">\n"; } else { code_stream << "#include \"" << FormatEscaped(name) << "\"\n"; } } } // Inject a declaration of placement operator new, because the code we // generate in thunks depends on it for placement new expressions. Clang has // special-case logic for lowering a new-expression using this, so a // definition is not required. // TODO: This is a hack. We should be able to directly generate Clang AST to // construct objects in-place without this. // TODO: Once we can rely on libc++ being available, consider including // `<__new/placement_new_delete.h>` instead. code_stream << R"(# 1 "" #undef constexpr #if __cplusplus > 202302L constexpr #endif #undef void #undef operator #undef new void* operator new(__SIZE_TYPE__, void*) #if __cplusplus < 201103L #undef throw throw() #else #undef noexcept noexcept #endif ; )"; return code; } // Adds the given source location and an `ImportIRInst` referring to it in // `ImportIRId::Cpp`. static auto AddImportIRInst(SemIR::File& file, clang::SourceLocation clang_source_loc) -> SemIR::ImportIRInstId { SemIR::ClangSourceLocId clang_source_loc_id = file.clang_source_locs().Add(clang_source_loc); return file.import_ir_insts().Add(SemIR::ImportIRInst(clang_source_loc_id)); } namespace { // Used to convert Clang diagnostics to Carbon diagnostics. // // Handling of Clang notes is a little subtle: as far as Clang is concerned, // notes are separate diagnostics, not connected to the error or warning that // precedes them. But in Carbon's diagnostics system, notes are part of the // enclosing diagnostic. To handle this, we buffer Clang diagnostics until we // reach a point where we know we're not in the middle of a diagnostic, and then // emit a diagnostic along with all of its notes. This is triggered when adding // or removing a Carbon context note, which could otherwise get attached to the // wrong C++ diagnostics, and at the end of the Carbon program. class CarbonClangDiagnosticConsumer : public clang::DiagnosticConsumer { public: // Creates an instance with the location that triggers calling Clang. The // `context` is not stored here, and the diagnostics consumer is expected to // outlive it. explicit CarbonClangDiagnosticConsumer( Context& context, std::shared_ptr invocation) : sem_ir_(&context.sem_ir()), emitter_(&context.emitter()), invocation_(std::move(invocation)) { emitter_->AddFlushFn([this] { EmitDiagnostics(); }); } ~CarbonClangDiagnosticConsumer() override { // Do not inspect `emitter_` here; it's typically destroyed before the // consumer is. // TODO: If Clang produces diagnostics after check finishes, they'll get // added to the list of pending diagnostics and never emitted. CARBON_CHECK(diagnostic_infos_.empty(), "Missing flush before destroying diagnostic consumer"); } // Generates a Carbon warning for each Clang warning and a Carbon error for // each Clang error or fatal. auto HandleDiagnostic(clang::DiagnosticsEngine::Level diag_level, const clang::Diagnostic& info) -> void override { DiagnosticConsumer::HandleDiagnostic(diag_level, info); SemIR::ImportIRInstId clang_import_ir_inst_id = AddImportIRInst(*sem_ir_, info.getLocation()); llvm::SmallString<256> message; info.FormatDiagnostic(message); // Render a code snippet including any highlighted ranges and fixit hints. // TODO: Also include the #include stack and macro expansion stack in the // diagnostic output in some way. RawStringOstream snippet_stream; if (!info.hasSourceManager()) { // If we don't have a source manager, this is an error from early in the // frontend. Don't produce a snippet. CARBON_CHECK(info.getLocation().isInvalid()); } else { CodeContextRenderer(snippet_stream, invocation_->getLangOpts(), invocation_->getDiagnosticOpts()) .emitDiagnostic( clang::FullSourceLoc(info.getLocation(), info.getSourceManager()), diag_level, message, info.getRanges(), info.getFixItHints()); } diagnostic_infos_.push_back({.level = diag_level, .import_ir_inst_id = clang_import_ir_inst_id, .message = message.str().str(), .snippet = snippet_stream.TakeStr()}); } // Returns the diagnostic to use for a given Clang diagnostic level. static auto GetDiagnostic(clang::DiagnosticsEngine::Level level) -> const Diagnostics::DiagnosticBase& { switch (level) { case clang::DiagnosticsEngine::Ignored: { CARBON_FATAL("Emitting an ignored diagnostic"); break; } case clang::DiagnosticsEngine::Note: { CARBON_DIAGNOSTIC(CppInteropParseNote, Note, "{0}", std::string); return CppInteropParseNote; } case clang::DiagnosticsEngine::Remark: case clang::DiagnosticsEngine::Warning: { // TODO: Add a distinct Remark level to Carbon diagnostics, and stop // mapping remarks to warnings. CARBON_DIAGNOSTIC(CppInteropParseWarning, Warning, "{0}", std::string); return CppInteropParseWarning; } case clang::DiagnosticsEngine::Error: case clang::DiagnosticsEngine::Fatal: { CARBON_DIAGNOSTIC(CppInteropParseError, Error, "{0}", std::string); return CppInteropParseError; } } } // Outputs Carbon diagnostics based on the collected Clang diagnostics. Must // be called after the AST is set in the context. auto EmitDiagnostics() -> void { CARBON_CHECK( sem_ir_->cpp_file(), "Attempted to emit C++ diagnostics before the C++ file is set"); for (size_t i = 0; i != diagnostic_infos_.size(); ++i) { const ClangDiagnosticInfo& info = diagnostic_infos_[i]; auto builder = emitter_->Build(SemIR::LocId(info.import_ir_inst_id), GetDiagnostic(info.level), info.message); builder.OverrideSnippet(info.snippet); for (; i + 1 < diagnostic_infos_.size() && diagnostic_infos_[i + 1].level == clang::DiagnosticsEngine::Note; ++i) { const ClangDiagnosticInfo& note_info = diagnostic_infos_[i + 1]; builder .Note(SemIR::LocId(note_info.import_ir_inst_id), GetDiagnostic(note_info.level), note_info.message) .OverrideSnippet(note_info.snippet); } // TODO: This will apply all current Carbon annotation functions. We // should instead track how Clang's context notes and Carbon's annotation // functions are interleaved, and interleave the notes in the same order. builder.Emit(); } diagnostic_infos_.clear(); } private: // A diagnostics renderer based on clang's TextDiagnostic that captures just // the code context (the snippet). class CodeContextRenderer : public clang::TextDiagnostic { protected: using TextDiagnostic::TextDiagnostic; void emitDiagnosticMessage( clang::FullSourceLoc /*loc*/, clang::PresumedLoc /*ploc*/, clang::DiagnosticsEngine::Level /*level*/, llvm::StringRef /*message*/, llvm::ArrayRef /*ranges*/, clang::DiagOrStoredDiag /*info*/) override {} void emitDiagnosticLoc( clang::FullSourceLoc /*loc*/, clang::PresumedLoc /*ploc*/, clang::DiagnosticsEngine::Level /*level*/, llvm::ArrayRef /*ranges*/) override {} // emitCodeContext is inherited from clang::TextDiagnostic. void emitIncludeLocation(clang::FullSourceLoc /*loc*/, clang::PresumedLoc /*ploc*/) override {} void emitImportLocation(clang::FullSourceLoc /*loc*/, clang::PresumedLoc /*ploc*/, llvm::StringRef /*module_name*/) override {} void emitBuildingModuleLocation(clang::FullSourceLoc /*loc*/, clang::PresumedLoc /*ploc*/, llvm::StringRef /*module_name*/) override {} // beginDiagnostic and endDiagnostic are inherited from // clang::TextDiagnostic in case it wants to do any setup / teardown work. }; // Information on a Clang diagnostic that can be converted to a Carbon // diagnostic. struct ClangDiagnosticInfo { // The Clang diagnostic level. clang::DiagnosticsEngine::Level level; // The ID of the ImportIR instruction referring to the Clang source // location. SemIR::ImportIRInstId import_ir_inst_id; // The Clang diagnostic textual message. std::string message; // The code snippet produced by clang. std::string snippet; }; // The Carbon file that this C++ compilation is attached to. SemIR::File* sem_ir_; // The diagnostic emitter that we're emitting diagnostics into. DiagnosticEmitterBase* emitter_; // The compiler invocation that is producing the diagnostics. std::shared_ptr invocation_; // Collects the information for all Clang diagnostics to be converted to // Carbon diagnostics after the context has been initialized with the Clang // AST. llvm::SmallVector diagnostic_infos_; }; // A wrapper around a clang::CompilerInvocation that allows us to make a shallow // copy of most of the invocation and only make a deep copy of the parts that we // want to change. // // clang::CowCompilerInvocation almost allows this, but doesn't derive from // CompilerInvocation or support shallow copies from a CompilerInvocation, so is // not useful to us as we can't build an ASTUnit from it. class ShallowCopyCompilerInvocation : public clang::CompilerInvocation { public: explicit ShallowCopyCompilerInvocation( const clang::CompilerInvocation& invocation) { shallow_copy_assign(invocation); // Make a deep copy of options that we modify. FrontendOpts = std::make_shared(*FrontendOpts); PPOpts = std::make_shared(*PPOpts); } }; class CarbonExternalASTSource : public clang::ExternalASTSource { public: explicit CarbonExternalASTSource(Context* context, clang::ASTContext* ast_context) : context_(context), ast_context_(ast_context) {} auto FindExternalVisibleDeclsByName( const clang::DeclContext* decl_context, clang::DeclarationName decl_name, const clang::DeclContext* original_decl_context) -> bool override; auto StartTranslationUnit(clang::ASTConsumer* consumer) -> void override; private: Check::Context* context_; clang::ASTContext* ast_context_; clang::NamespaceDecl* carbon_cpp_namespace_ = nullptr; }; void CarbonExternalASTSource::StartTranslationUnit( clang::ASTConsumer* /*Consumer*/) { auto& translation_unit = *ast_context_->getTranslationUnitDecl(); // Mark the translation unit as having external storage so we get a query for // the `Carbon` namespace in the top level/translation unit scope. translation_unit.setHasExternalVisibleStorage(); } // Map a Carbon entity to a Clang NamedDecl. static auto MapInstIdToClangDecl(Context& context, clang::ASTContext& ast_context, clang::DeclContext& decl_context, LookupResult lookup) -> clang::NamedDecl* { auto target_inst_id = lookup.scope_result.target_inst_id(); if (auto target_inst = context.insts().TryGetAs(target_inst_id)) { auto& name_scope = context.name_scopes().Get(target_inst->name_scope_id); auto* identifier_info = GetClangIdentifierInfo(context, name_scope.name_id()); // TODO: Don't immediately use the decl_context - build any intermediate // namespaces iteratively. // Eventually add a mapping and use that/populate it/keep it up to date. // decl_context could be prepopulated in that mapping and not passed // explicitly to MapInstIdToClangDecl. return clang::NamespaceDecl::Create( ast_context, &decl_context, false, clang::SourceLocation(), clang::SourceLocation(), identifier_info, nullptr, false); } return nullptr; } auto CarbonExternalASTSource::FindExternalVisibleDeclsByName( const clang::DeclContext* decl_context, clang::DeclarationName decl_name, const clang::DeclContext* /*OriginalDC*/) -> bool { if (decl_context != carbon_cpp_namespace_) { if (decl_context->getDeclKind() != clang::Decl::Kind::TranslationUnit) { return false; } static const llvm::StringLiteral carbon_namespace_name = "Carbon"; if (auto* identifier = decl_name.getAsIdentifierInfo(); !identifier || !identifier->isStr(carbon_namespace_name)) { return false; } // Build the top level 'Carbon' namespace auto& ast_context = decl_context->getParentASTContext(); auto& mutable_tu_decl_context = *ast_context.getTranslationUnitDecl(); carbon_cpp_namespace_ = clang::NamespaceDecl::Create( ast_context, &mutable_tu_decl_context, false, clang::SourceLocation(), clang::SourceLocation(), &ast_context.Idents.get(carbon_namespace_name), nullptr, false); carbon_cpp_namespace_->setHasExternalVisibleStorage(); SetExternalVisibleDeclsForName(decl_context, decl_name, {carbon_cpp_namespace_}); return true; } // Lookup the name in Carbon package scope llvm::SmallVector lookup_scopes; // LocId::None seems fine here because we shouldn't produce any diagnostics // here - completeness should've been checked by clang before this point. if (!AppendLookupScopesForConstant( *context_, SemIR::LocId::None, context_->constant_values().Get(SemIR::Namespace::PackageInstId), SemIR::ConstantId::None, &lookup_scopes)) { return false; } auto* identifier = decl_name.getAsIdentifierInfo(); if (!identifier) { // Only supporting identifiers for now. return false; } auto name_id = AddIdentifierName(*context_, identifier->getName()); // `required=false` so Carbon doesn't diagnose a failure, let Clang diagnose // it or even SFINAE. LookupResult result = LookupQualifiedName(*context_, SemIR::LocId::None, name_id, lookup_scopes, /*required=*/false); if (!result.scope_result.is_found()) { return false; } // Map the found Carbon entity to a Clang NamedDecl. auto* clang_decl = MapInstIdToClangDecl(*context_, *ast_context_, *carbon_cpp_namespace_, result); if (!clang_decl) { return false; } SetExternalVisibleDeclsForName(decl_context, decl_name, {clang_decl}); return true; } // An action and a set of registered Clang callbacks used to generate an AST // from a set of Cpp imports. class GenerateASTAction : public clang::ASTFrontendAction { public: explicit GenerateASTAction(Context& context) : context_(&context) {} protected: auto CreateASTConsumer(clang::CompilerInstance& clang_instance, llvm::StringRef /*file*/) -> std::unique_ptr override { auto& cpp_file = *context_->sem_ir().cpp_file(); if (!cpp_file.llvm_context()) { return std::make_unique(); } auto code_generator = std::unique_ptr(clang::CreateLLVMCodeGen( cpp_file.diagnostics(), context_->sem_ir().filename(), clang_instance.getVirtualFileSystemPtr(), clang_instance.getHeaderSearchOpts(), clang_instance.getPreprocessorOpts(), clang_instance.getCodeGenOpts(), *cpp_file.llvm_context())); cpp_file.SetCodeGenerator(code_generator.get()); return code_generator; } auto BeginSourceFileAction(clang::CompilerInstance& /*clang_instance*/) -> bool override { // TODO: `clang.getPreprocessor().enableIncrementalProcessing();` to avoid // the TU scope getting torn down before we're done parsing macros. return true; } // Parse the imports and inline C++ fragments. This is notionally very similar // to `clang::ParseAST`, which `ASTFrontendAction::ExecuteAction` calls, but // this version doesn't parse C++20 modules and stops just before reaching the // end of the translation unit. auto ExecuteAction() -> void override { clang::CompilerInstance& clang_instance = getCompilerInstance(); clang_instance.createSema(getTranslationUnitKind(), /*CompletionConsumer=*/nullptr); auto parser_ptr = std::make_unique( clang_instance.getPreprocessor(), clang_instance.getSema(), /*SkipFunctionBodies=*/false); auto& parser = *parser_ptr; clang_instance.getPreprocessor().EnterMainSourceFile(); if (auto* source = clang_instance.getASTContext().getExternalSource()) { source->StartTranslationUnit(&clang_instance.getASTConsumer()); } parser.Initialize(); clang_instance.getSema().ActOnStartOfTranslationUnit(); context_->set_cpp_context( std::make_unique(clang_instance, std::move(parser_ptr))); // Don't allow C++20 module declarations in inline Cpp code fragments. auto module_import_state = clang::Sema::ModuleImportState::NotACXX20Module; // Parse top-level declarations until we see EOF. Do not parse EOF, as that // will cause the parser to end the translation unit prematurely. while (parser.getCurToken().isNot(clang::tok::eof)) { clang::Parser::DeclGroupPtrTy decl_group; bool eof = parser.ParseTopLevelDecl(decl_group, module_import_state); CARBON_CHECK(!eof); if (decl_group && !clang_instance.getASTConsumer().HandleTopLevelDecl( decl_group.get())) { break; } } } private: Context* context_; }; } // namespace auto GenerateAst(Context& context, llvm::ArrayRef imports, llvm::IntrusiveRefCntPtr fs, llvm::LLVMContext* llvm_context, std::shared_ptr base_invocation) -> bool { CARBON_CHECK(!context.cpp_context()); CARBON_CHECK(!context.sem_ir().cpp_file()); auto invocation = std::make_shared(*base_invocation); // Ask Clang to not leak memory. invocation->getFrontendOpts().DisableFree = false; // Build a diagnostics engine. llvm::IntrusiveRefCntPtr diags( clang::CompilerInstance::createDiagnostics( *fs, invocation->getDiagnosticOpts(), new CarbonClangDiagnosticConsumer(context, invocation), /*ShouldOwnClient=*/true)); // Extract the input from the frontend invocation and make sure it makes // sense. const auto& inputs = invocation->getFrontendOpts().Inputs; CARBON_CHECK(inputs.size() == 1 && inputs[0].getKind().getLanguage() == clang::Language::CXX && inputs[0].getKind().getFormat() == clang::InputKind::Source); llvm::StringRef file_name = inputs[0].getFile(); // Remap the imports file name to the corresponding `#include`s. // TODO: Modify the frontend options to specify this memory buffer as input // instead of remapping the file. std::string includes = GenerateCppIncludesHeaderCode(context, imports); auto includes_buffer = llvm::MemoryBuffer::getMemBufferCopy(includes, file_name); invocation->getPreprocessorOpts().addRemappedFile(file_name, includes_buffer.release()); auto clang_instance_ptr = std::make_unique(invocation); auto& clang_instance = *clang_instance_ptr; context.sem_ir().set_cpp_file(std::make_unique( std::move(clang_instance_ptr), llvm_context)); clang_instance.setDiagnostics(diags); clang_instance.setVirtualFileSystem(fs); clang_instance.createFileManager(); clang_instance.createSourceManager(); if (!clang_instance.createTarget()) { return false; } GenerateASTAction action(context); if (!action.BeginSourceFile(clang_instance, inputs[0])) { return false; } auto& ast = clang_instance.getASTContext(); // TODO: Clang's modules support is implemented as an ExternalASTSource // (ASTReader) and there's no multiplexing support for ExternalASTSources at // the moment - so registering CarbonExternalASTSource breaks Clang modules // support. Implement multiplexing support (possibly in Clang) to restore // modules functionality. ast.setExternalSource( llvm::makeIntrusiveRefCnt(&context, &ast)); if (llvm::Error error = action.Execute()) { // `Execute` currently never fails, but its contract allows it to. context.TODO(SemIR::LocId::None, "failed to execute clang action: " + llvm::toString(std::move(error))); return false; } // Flush any diagnostics. We know we're not part-way through emitting a // diagnostic now. context.emitter().Flush(); return true; } auto FinishAst(Context& context) -> void { if (!context.cpp_context()) { return; } context.cpp_context()->sema().ActOnEndOfTranslationUnit(); // We don't call FrontendAction::EndSourceFile, because that destroys the AST. context.set_cpp_context(nullptr); context.emitter().Flush(); } } // namespace Carbon::Check ================================================ FILE: toolchain/check/cpp/generate_ast.h ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #ifndef CARBON_TOOLCHAIN_CHECK_CPP_GENERATE_AST_H_ #define CARBON_TOOLCHAIN_CHECK_CPP_GENERATE_AST_H_ #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/IntrusiveRefCntPtr.h" #include "llvm/Support/VirtualFileSystem.h" #include "toolchain/check/context.h" namespace Carbon::Check { // Generates a Clang AST for the given C++ imports and sets it as the context's // `cpp_context` and the SemIR's `cpp_file`. Returns a bool that represents // whether compilation was successful. auto GenerateAst(Context& context, llvm::ArrayRef imports, llvm::IntrusiveRefCntPtr fs, llvm::LLVMContext* llvm_context, std::shared_ptr base_invocation) -> bool; // Finishes AST generation for the given checking context. Performs end of file // steps such as template instantiation and warning on unused declarations. auto FinishAst(Context& context) -> void; } // namespace Carbon::Check #endif // CARBON_TOOLCHAIN_CHECK_CPP_GENERATE_AST_H_ ================================================ FILE: toolchain/check/cpp/impl_lookup.cpp ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #include "toolchain/check/cpp/impl_lookup.h" #include "clang/Sema/Sema.h" #include "toolchain/base/kind_switch.h" #include "toolchain/check/cpp/import.h" #include "toolchain/check/cpp/location.h" #include "toolchain/check/cpp/overload_resolution.h" #include "toolchain/check/custom_witness.h" #include "toolchain/check/impl.h" #include "toolchain/check/impl_lookup.h" #include "toolchain/check/import_ref.h" #include "toolchain/check/inst.h" #include "toolchain/check/type.h" #include "toolchain/sem_ir/ids.h" #include "toolchain/sem_ir/typed_insts.h" namespace Carbon::Check { // If the given type is a C++ class type, returns the corresponding class // declaration. Otherwise returns nullptr. // TODO: Handle qualified types. static auto TypeAsClassDecl(Context& context, SemIR::ConstantId query_self_const_id) -> clang::CXXRecordDecl* { auto self_inst_id = context.constant_values().GetInstId(query_self_const_id); auto class_type = context.insts().TryGetAs(self_inst_id); if (!class_type) { // Not a class. return nullptr; } SemIR::NameScopeId class_scope_id = context.classes().Get(class_type->class_id).scope_id; if (!class_scope_id.has_value()) { return nullptr; } const auto& scope = context.name_scopes().Get(class_scope_id); auto decl_id = scope.clang_decl_context_id(); if (!decl_id.has_value()) { return nullptr; } return dyn_cast( context.clang_decls().Get(decl_id).key.decl); } namespace { struct DeclInfo { // If null, no C++ decl was found and no witness can be created. clang::NamedDecl* decl = nullptr; SemIR::ClangDeclKey::Signature signature; }; } // namespace // Finds the InstId for the C++ function that is called by a specific interface. // Returns SemIR::InstId::None if a C++ function is not found, and // SemIR::ErrorInst::InstId if an error occurs. static auto GetFunctionId(Context& context, SemIR::LocId loc_id, DeclInfo decl_info, const TypeStructure* best_impl_type_structure, SemIR::LocId best_impl_loc_id) -> SemIR::InstId { if (!decl_info.decl) { // The C++ type is not able to implement the interface. return SemIR::InstId::None; } auto* cpp_fn = cast(decl_info.decl); if (context.clang_sema().DiagnoseUseOfOverloadedDecl( cpp_fn, GetCppLocation(context, loc_id))) { return SemIR::ErrorInst::InstId; } auto fn_id = ImportCppFunctionDecl(context, loc_id, cpp_fn, decl_info.signature); if (fn_id == SemIR::ErrorInst::InstId) { return SemIR::ErrorInst::InstId; } CheckCppOverloadAccess( context, loc_id, clang::DeclAccessPair::make(cpp_fn, cpp_fn->getAccess()), context.insts().GetAsKnownInstId(fn_id)); // TODO: Infer a C++ type structure and check whether it's less strict than // the best Carbon type structure. static_cast(best_impl_type_structure); static_cast(best_impl_loc_id); return fn_id; } static auto BuildCopyWitness( Context& context, SemIR::LocId loc_id, SemIR::ConstantId query_self_const_id, SemIR::SpecificInterfaceId query_specific_interface_id, const TypeStructure* best_impl_type_structure, SemIR::LocId best_impl_loc_id) -> SemIR::InstId { auto& clang_sema = context.clang_sema(); // TODO: This should provide `Copy` for enums and other trivially copyable // types. auto* class_decl = TypeAsClassDecl(context, query_self_const_id); if (!class_decl) { return SemIR::InstId::None; } auto decl_info = DeclInfo{.decl = clang_sema.LookupCopyingConstructor( class_decl, clang::Qualifiers::Const), .signature = {.num_params = 1}}; auto fn_id = GetFunctionId(context, loc_id, decl_info, best_impl_type_structure, best_impl_loc_id); if (fn_id == SemIR::ErrorInst::InstId || fn_id == SemIR::InstId::None) { return fn_id; } return BuildCustomWitness(context, loc_id, query_self_const_id, query_specific_interface_id, {fn_id}); } static auto BuildDestroyWitness( Context& context, SemIR::LocId loc_id, SemIR::ConstantId query_self_const_id, SemIR::SpecificInterfaceId query_specific_interface_id, const TypeStructure* best_impl_type_structure, SemIR::LocId best_impl_loc_id) -> SemIR::InstId { auto& clang_sema = context.clang_sema(); // TODO: This should provide `Destroy` for enums and other trivially // destructible types. auto* class_decl = TypeAsClassDecl(context, query_self_const_id); if (!class_decl) { return SemIR::InstId::None; } auto decl_info = DeclInfo{.decl = clang_sema.LookupDestructor(class_decl), .signature = {.num_params = 0}}; auto fn_id = GetFunctionId(context, loc_id, decl_info, best_impl_type_structure, best_impl_loc_id); if (fn_id == SemIR::ErrorInst::InstId || fn_id == SemIR::InstId::None) { return fn_id; } return BuildCustomWitness(context, loc_id, query_self_const_id, query_specific_interface_id, {fn_id}); } static auto BuildCppUnsafeDerefWitness( Context& context, SemIR::LocId loc_id, SemIR::ConstantId query_self_const_id, SemIR::SpecificInterfaceId query_specific_interface_id, const TypeStructure* best_impl_type_structure, SemIR::LocId best_impl_loc_id) -> SemIR::InstId { auto& clang_sema = context.clang_sema(); auto* class_decl = TypeAsClassDecl(context, query_self_const_id); if (!class_decl) { return SemIR::InstId::None; } auto candidates = class_decl->lookup( clang_sema.getASTContext().DeclarationNames.getCXXOperatorName( clang::OO_Star)); if (candidates.empty()) { return SemIR::InstId::None; } if (!candidates.isSingleResult()) { context.TODO(loc_id, "operator* overload sets not implemented yet"); return SemIR::ErrorInst::InstId; } auto decl_info = DeclInfo{.decl = *candidates.begin(), .signature = {.num_params = 0}}; auto fn_id = GetFunctionId(context, loc_id, decl_info, best_impl_type_structure, best_impl_loc_id); if (fn_id == SemIR::ErrorInst::InstId || fn_id == SemIR::InstId::None) { return fn_id; } auto result_type_id = context.functions() .Get(context.insts().GetAs(fn_id).function_id) .return_type_inst_id; if (result_type_id == SemIR::ErrorInst::InstId) { return SemIR::ErrorInst::InstId; } return BuildCustomWitness(context, loc_id, query_self_const_id, query_specific_interface_id, {result_type_id, fn_id}); } auto LookupCppImpl(Context& context, SemIR::LocId loc_id, CoreInterface core_interface, SemIR::ConstantId query_self_const_id, SemIR::SpecificInterfaceId query_specific_interface_id, const TypeStructure* best_impl_type_structure, SemIR::LocId best_impl_loc_id) -> SemIR::InstId { switch (core_interface) { case CoreInterface::Copy: return BuildCopyWitness(context, loc_id, query_self_const_id, query_specific_interface_id, best_impl_type_structure, best_impl_loc_id); case CoreInterface::Destroy: return BuildDestroyWitness(context, loc_id, query_self_const_id, query_specific_interface_id, best_impl_type_structure, best_impl_loc_id); case CoreInterface::CppUnsafeDeref: return BuildCppUnsafeDerefWitness( context, loc_id, query_self_const_id, query_specific_interface_id, best_impl_type_structure, best_impl_loc_id); // IntFitsIn is for Carbon integer types only. case CoreInterface::IntFitsIn: return SemIR::InstId::None; case CoreInterface::Unknown: CARBON_FATAL("unexpected CoreInterface `{0}`", core_interface); } } } // namespace Carbon::Check ================================================ FILE: toolchain/check/cpp/impl_lookup.h ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #ifndef CARBON_TOOLCHAIN_CHECK_CPP_IMPL_LOOKUP_H_ #define CARBON_TOOLCHAIN_CHECK_CPP_IMPL_LOOKUP_H_ #include "toolchain/check/context.h" #include "toolchain/check/custom_witness.h" #include "toolchain/check/impl_lookup.h" #include "toolchain/check/interface.h" #include "toolchain/check/type_structure.h" #include "toolchain/sem_ir/ids.h" #include "toolchain/sem_ir/specific_interface.h" namespace Carbon::Check { // Performs lookup for an impl witness for a query involving C++ types. // Shouldn't be called with `CoreInterface::Unknown`, because only core // interfaces can have lookup results. Returns a witness value, or `None` if a // synthesized C++ witness should not be used. // // Given a known `core_interface`, we can synthesize a witness based on C++ // operator overloads or special member functions. Performs the suitable C++ // lookup to determine if this interface should be considered implemented for // the specified type, and if so, synthesizes and returns a suitable witness. // // `best_impl_type_structure` provides the type structure of the best-matching // impl declaration. If this is better than every viable C++ candidate, a "none" // result will be returned. If this is worse than the best viable C++ candidate // according to C++ rules, a witness for the C++ candidate will be returned. // Otherwise, it is at least as good as the best viable C++ candidate, but there // is some C++ candidate that has a better type structure, in which case the // result is ambiguous and we diagnose an error. This parameter can be null if // there is no usable impl for this query. // // `best_impl_loc_id` gives the location of the impl corresponding to the best // type structure, and can be `None` if `best_impl_type_structure` is null. This // parameter is used only for ambiguity diagnostics. auto LookupCppImpl(Context& context, SemIR::LocId loc_id, CoreInterface core_interface, SemIR::ConstantId query_self_const_id, SemIR::SpecificInterfaceId query_specific_interface_id, const TypeStructure* best_impl_type_structure, SemIR::LocId best_impl_loc_id) -> SemIR::InstId; } // namespace Carbon::Check #endif // CARBON_TOOLCHAIN_CHECK_CPP_IMPL_LOOKUP_H_ ================================================ FILE: toolchain/check/cpp/import.cpp ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #include "toolchain/check/cpp/import.h" #include #include #include #include #include #include #include "clang/AST/ASTContext.h" #include "clang/AST/RecordLayout.h" #include "clang/AST/UnresolvedSet.h" #include "clang/AST/VTableBuilder.h" #include "clang/Frontend/CompilerInvocation.h" #include "clang/Sema/Lookup.h" #include "clang/Sema/Overload.h" #include "common/check.h" #include "common/ostream.h" #include "common/raw_string_ostream.h" #include "llvm/ADT/IntrusiveRefCntPtr.h" #include "llvm/ADT/ScopeExit.h" #include "llvm/ADT/StringRef.h" #include "llvm/Support/raw_ostream.h" #include "toolchain/base/int.h" #include "toolchain/base/kind_switch.h" #include "toolchain/base/value_ids.h" #include "toolchain/check/call.h" #include "toolchain/check/class.h" #include "toolchain/check/context.h" #include "toolchain/check/control_flow.h" #include "toolchain/check/convert.h" #include "toolchain/check/core_identifier.h" #include "toolchain/check/cpp/access.h" #include "toolchain/check/cpp/custom_type_mapping.h" #include "toolchain/check/cpp/generate_ast.h" #include "toolchain/check/cpp/location.h" #include "toolchain/check/cpp/macros.h" #include "toolchain/check/cpp/thunk.h" #include "toolchain/check/diagnostic_helpers.h" #include "toolchain/check/eval.h" #include "toolchain/check/function.h" #include "toolchain/check/import.h" #include "toolchain/check/inst.h" #include "toolchain/check/literal.h" #include "toolchain/check/member_access.h" #include "toolchain/check/name_lookup.h" #include "toolchain/check/operator.h" #include "toolchain/check/pattern.h" #include "toolchain/check/pattern_match.h" #include "toolchain/check/type.h" #include "toolchain/check/type_completion.h" #include "toolchain/check/unused.h" #include "toolchain/parse/node_ids.h" #include "toolchain/sem_ir/clang_decl.h" #include "toolchain/sem_ir/class.h" #include "toolchain/sem_ir/cpp_file.h" #include "toolchain/sem_ir/cpp_overload_set.h" #include "toolchain/sem_ir/function.h" #include "toolchain/sem_ir/ids.h" #include "toolchain/sem_ir/inst.h" #include "toolchain/sem_ir/name_scope.h" #include "toolchain/sem_ir/pattern.h" #include "toolchain/sem_ir/typed_insts.h" namespace Carbon::Check { // Adds the name to the scope with the given `access_kind` and `inst_id`. // `inst_id` must have a value. static auto AddNameToScope(Context& context, SemIR::NameScopeId scope_id, SemIR::NameId name_id, SemIR::AccessKind access_kind, SemIR::InstId inst_id) -> void { CARBON_CHECK(inst_id.has_value()); context.name_scopes().Get(scope_id).AddRequired( {.name_id = name_id, .result = SemIR::ScopeLookupResult::MakeFound(inst_id, access_kind)}); } // Maps a Clang name to a Carbon `NameId`. auto AddIdentifierName(Context& context, llvm::StringRef name) -> SemIR::NameId { return SemIR::NameId::ForIdentifier(context.identifiers().Add(name)); } // Adds the given source location and an `ImportIRInst` referring to it in // `ImportIRId::Cpp`. static auto AddImportIRInst(SemIR::File& file, clang::SourceLocation clang_source_loc) -> SemIR::ImportIRInstId { SemIR::ClangSourceLocId clang_source_loc_id = file.clang_source_locs().Add(clang_source_loc); return file.import_ir_insts().Add(SemIR::ImportIRInst(clang_source_loc_id)); } // Adds a namespace for the `Cpp` import and returns its `NameScopeId`. static auto AddNamespace(Context& context, PackageNameId cpp_package_id, llvm::ArrayRef imports) -> SemIR::NameScopeId { return AddImportNamespaceToScope( context, GetSingletonType(context, SemIR::NamespaceType::TypeInstId), SemIR::NameId::ForPackageName(cpp_package_id), SemIR::NameScopeId::Package, /*diagnose_duplicate_namespace=*/false, [&]() { return AddInst( context, context.parse_tree().As( imports.front().node_id), {}); }) .add_result.name_scope_id; } auto ImportCpp(Context& context, llvm::ArrayRef imports, llvm::IntrusiveRefCntPtr fs, llvm::LLVMContext* llvm_context, std::shared_ptr invocation) -> void { if (imports.empty()) { // TODO: Consider always having a (non-null) AST even if there are no Cpp // imports. return; } PackageNameId package_id = imports.front().package_id; CARBON_CHECK( llvm::all_of(imports, [&](const Parse::Tree::PackagingNames& import) { return import.package_id == package_id; })); auto name_scope_id = AddNamespace(context, package_id, imports); SemIR::NameScope& name_scope = context.name_scopes().Get(name_scope_id); name_scope.set_is_closed_import(true); if (GenerateAst(context, imports, fs, llvm_context, std::move(invocation))) { name_scope.set_clang_decl_context_id(context.clang_decls().Add( {.key = SemIR::ClangDeclKey( context.ast_context().getTranslationUnitDecl()), .inst_id = name_scope.inst_id()})); } else { name_scope.set_has_error(); } } // Returns the Clang `DeclContext` for the given name scope. Return the // translation unit decl if no scope is provided. static auto GetDeclContext(Context& context, SemIR::NameScopeId scope_id) -> clang::DeclContext* { if (!scope_id.has_value()) { return context.ast_context().getTranslationUnitDecl(); } auto scope_clang_decl_context_id = context.name_scopes().Get(scope_id).clang_decl_context_id(); return dyn_cast( context.clang_decls().Get(scope_clang_decl_context_id).key.decl); } // Returns true if the given Clang declaration is the implicit injected class // name within the class. static auto IsDeclInjectedClassName(Context& context, SemIR::NameScopeId scope_id, SemIR::NameId name_id, const clang::NamedDecl* named_decl) -> bool { if (!named_decl->isImplicit()) { return false; } const auto* record_decl = dyn_cast(named_decl); if (!record_decl) { return false; } const SemIR::ClangDecl& clang_decl = context.clang_decls().Get( context.name_scopes().Get(scope_id).clang_decl_context_id()); const auto* scope_record_decl = cast(clang_decl.key.decl); const clang::ASTContext& ast_context = context.ast_context(); CARBON_CHECK(ast_context.getCanonicalTagType(scope_record_decl) == ast_context.getCanonicalTagType(record_decl)); auto class_decl = context.insts().GetAs(clang_decl.inst_id); CARBON_CHECK(name_id == context.classes().Get(class_decl.class_id).name_id); return true; } // Performs a qualified name lookup of the identifier in the given scope. // Returns the lookup result if lookup was successful. static auto ClangLookupName(Context& context, SemIR::NameScopeId scope_id, clang::IdentifierInfo* identifier_name) -> std::optional { CARBON_CHECK(identifier_name, "Identifier name is empty"); clang::Sema& sema = context.clang_sema(); // TODO: Map the LocId of the lookup to a clang SourceLocation and provide it // here so that clang's diagnostics can point into the carbon code that uses // the name. clang::LookupResult lookup( sema, clang::DeclarationNameInfo(clang::DeclarationName(identifier_name), clang::SourceLocation()), clang::Sema::LookupNameKind::LookupOrdinaryName); bool found = sema.LookupQualifiedName(lookup, GetDeclContext(context, scope_id)); if (!found) { return std::nullopt; } return lookup; } // Returns whether `decl` already mapped to an instruction. static auto IsClangDeclImported(Context& context, SemIR::ClangDeclKey key) -> bool { return context.clang_decls().Lookup(key).has_value(); } // If `decl` already mapped to an instruction, returns that instruction. // Otherwise returns `None`. static auto LookupClangDeclInstId(Context& context, SemIR::ClangDeclKey key) -> SemIR::InstId { const auto& clang_decls = context.clang_decls(); if (auto context_clang_decl_id = clang_decls.Lookup(key); context_clang_decl_id.has_value()) { return clang_decls.Get(context_clang_decl_id).inst_id; } return SemIR::InstId::None; } // Returns the parent of the given declaration. Skips declaration types we // ignore. static auto GetParentDecl(clang::Decl* clang_decl) -> clang::Decl* { auto* parent_dc = clang_decl->getDeclContext(); while (!parent_dc->isLookupContext()) { parent_dc = parent_dc->getParent(); } return cast(parent_dc); } // Returns the given declaration's parent scope. Assumes the parent declaration // was already imported. static auto GetParentNameScopeId(Context& context, clang::Decl* clang_decl) -> SemIR::NameScopeId { auto* parent_decl = GetParentDecl(clang_decl); if (auto* tag_decl = dyn_cast(parent_decl)) { auto class_inst_id = LookupClangDeclInstId(context, SemIR::ClangDeclKey(tag_decl)); CARBON_CHECK(class_inst_id.has_value()); auto class_inst = context.insts().Get(class_inst_id); auto class_id = SemIR::ClassId::None; if (auto class_decl = class_inst.TryAs()) { // Common case: the tag was imported as a new Carbon class. class_id = class_decl->class_id; } else { // Rare case: the tag was imported as an existing Carbon class. This // happens for C++ classes that get mapped to Carbon prelude types, such // as `std::string_view`. // TODO: In this case, should we import the C++ class declaration and use // it as the parent, rather than using the existing Carbon class? class_id = class_inst.As().class_id; } return context.classes().Get(class_id).scope_id; } if (isa(parent_decl)) { auto namespace_inst_id = LookupClangDeclInstId( context, SemIR::ClangDeclKey::ForNonFunctionDecl(parent_decl)); CARBON_CHECK(namespace_inst_id.has_value()); return context.insts() .GetAs(namespace_inst_id) .name_scope_id; } CARBON_FATAL("Unexpected kind of parent {0}", parent_decl->getDeclKindName()); } // Imports a namespace declaration from Clang to Carbon. If successful, returns // the new Carbon namespace declaration `InstId`. If the declaration was already // imported, returns the mapped instruction. static auto ImportNamespaceDecl(Context& context, clang::NamespaceDecl* clang_decl) -> SemIR::InstId { auto key = SemIR::ClangDeclKey(clang_decl); // Check if the declaration is already mapped. if (SemIR::InstId existing_inst_id = LookupClangDeclInstId(context, key); existing_inst_id.has_value()) { return existing_inst_id; } auto result = AddImportNamespace( context, GetSingletonType(context, SemIR::NamespaceType::TypeInstId), AddIdentifierName(context, clang_decl->getName()), GetParentNameScopeId(context, clang_decl), /*import_id=*/SemIR::InstId::None); context.name_scopes() .Get(result.name_scope_id) .set_clang_decl_context_id( context.clang_decls().Add({.key = key, .inst_id = result.inst_id})); return result.inst_id; } // Creates a class declaration for the given class name in the given scope. // Returns the `InstId` for the declaration. static auto BuildClassDecl(Context& context, SemIR::ImportIRInstId import_ir_inst_id, SemIR::NameScopeId parent_scope_id, SemIR::NameId name_id) -> std::tuple { // Add the class declaration. auto class_decl = SemIR::ClassDecl{.type_id = SemIR::TypeType::TypeId, .class_id = SemIR::ClassId::None, .decl_block_id = SemIR::InstBlockId::None}; auto class_decl_id = AddPlaceholderImportedInstInNoBlock( context, MakeImportedLocIdAndInst(context, import_ir_inst_id, class_decl)); SemIR::Class class_info = { {.name_id = name_id, .parent_scope_id = parent_scope_id, .generic_id = SemIR::GenericId::None, .first_param_node_id = Parse::NodeId::None, .last_param_node_id = Parse::NodeId::None, .pattern_block_id = SemIR::InstBlockId::None, .implicit_param_patterns_id = SemIR::InstBlockId::None, .param_patterns_id = SemIR::InstBlockId::None, .is_extern = false, .extern_library_id = SemIR::LibraryNameId::None, .non_owning_decl_id = SemIR::InstId::None, .first_owning_decl_id = class_decl_id}, {// `.self_type_id` depends on the ClassType, so is set below. .self_type_id = SemIR::TypeId::None, // TODO: Support Dynamic classes. // TODO: Support Final classes. .inheritance_kind = SemIR::Class::Base}}; class_decl.class_id = context.classes().Add(class_info); // Write the class ID into the ClassDecl. ReplaceInstBeforeConstantUse(context, class_decl_id, class_decl); SetClassSelfType(context, class_decl.class_id); return {class_decl.class_id, context.types().GetAsTypeInstId(class_decl_id)}; } // Imports a tag declaration from Clang to Carbon. This covers classes (which // includes structs and unions) as well as enums. If successful, returns the new // Carbon class declaration `InstId`. static auto ImportTagDecl(Context& context, clang::TagDecl* clang_decl) -> SemIR::InstId { auto import_ir_inst_id = AddImportIRInst(context.sem_ir(), clang_decl->getLocation()); auto [class_id, class_inst_id] = BuildClassDecl( context, import_ir_inst_id, GetParentNameScopeId(context, clang_decl), AddIdentifierName(context, clang_decl->getName())); // TODO: The caller does the same lookup. Avoid doing it twice. auto key = SemIR::ClangDeclKey(clang_decl); auto clang_decl_id = context.clang_decls().Add({.key = key, .inst_id = class_inst_id}); // Name lookup into the Carbon class looks in the C++ class definition. auto& class_info = context.classes().Get(class_id); class_info.scope_id = context.name_scopes().Add( class_inst_id, SemIR::NameId::None, class_info.parent_scope_id); context.name_scopes() .Get(class_info.scope_id) .set_clang_decl_context_id(clang_decl_id); return class_inst_id; } // Determines the Carbon inheritance kind to use for a C++ class definition. static auto GetInheritanceKind(clang::CXXRecordDecl* class_def) -> SemIR::Class::InheritanceKind { if (class_def->isUnion()) { // Treat all unions as final classes to match their C++ semantics. While we // could support this, the author of a C++ union has no way to mark their // type as `final` to prevent it, and so we assume the intent was to // disallow inheritance. return SemIR::Class::Final; } if (class_def->hasAttr()) { // The class is final in C++; don't allow Carbon types to derive from it. // Note that such a type might also be abstract in C++; we treat final as // taking precedence. // // We could also treat classes with a final destructor as being final, as // Clang does when determining whether a class is "effectively final", but // to keep our rules simpler we do not. return SemIR::Class::Final; } if (class_def->getNumVBases()) { // TODO: We treat classes with virtual bases as final for now. We use the // layout of the class including its virtual bases as its Carbon type // layout, so we wouldn't behave correctly if we derived from it. return SemIR::Class::Final; } if (class_def->isAbstract()) { // If the class has any abstract members, it's abstract. return SemIR::Class::Abstract; } // Allow inheritance from any other C++ class type. return SemIR::Class::Base; } // Checks that the specified finished class definition is valid and builds and // returns a corresponding complete type witness instruction. static auto ImportClassObjectRepr(Context& context, SemIR::ClassId class_id, SemIR::ImportIRInstId import_ir_inst_id, SemIR::TypeInstId class_type_inst_id, const clang::CXXRecordDecl* clang_def) -> SemIR::TypeInstId { if (clang_def->isInvalidDecl()) { // Clang already diagnosed this error. return SemIR::ErrorInst::TypeInstId; } // For now, if the class is empty and an aggregate, produce an empty struct as // the object representation. This allows our tests to continue to pass while // we don't properly support initializing imported C++ classes. We only do // this for aggregates so that non-aggregate classes are not incorrectly // initializable from `{}`. // TODO: Remove this. if (clang_def->isEmpty() && !clang_def->getNumBases() && clang_def->isAggregate()) { return context.types().GetAsTypeInstId(AddInst( context, MakeImportedLocIdAndInst( context, import_ir_inst_id, SemIR::StructType{.type_id = SemIR::TypeType::TypeId, .fields_id = SemIR::StructTypeFieldsId::Empty}))); } const auto& clang_layout = context.ast_context().getASTRecordLayout(clang_def); llvm::SmallVector layout; llvm::SmallVector fields; static_assert(SemIR::CustomLayoutId::SizeIndex == 0); layout.push_back(clang_layout.getSize().getQuantity()); static_assert(SemIR::CustomLayoutId::AlignIndex == 1); layout.push_back(clang_layout.getAlignment().getQuantity()); static_assert(SemIR::CustomLayoutId::FirstFieldIndex == 2); // TODO: Import vptr(s). // The kind of base class we've picked so far. These are ordered in increasing // preference order. enum class BaseKind { None, Empty, NonEmpty, Polymorphic, }; BaseKind base_kind = BaseKind::None; // Import bases. for (const auto& base : clang_def->bases()) { if (base.isVirtual()) { // If the base is virtual, skip it from the layout. We don't know where it // will actually appear within the complete object layout, as a pointer to // this class might point to a derived type that puts the vbase in a // different place. // TODO: Track that the virtual base existed. Support derived-to-vbase // conversions by generating a clang AST fragment. continue; } auto [base_type_inst_id, base_type_id] = ImportCppType(context, import_ir_inst_id, base.getType()); if (!base_type_id.has_value()) { // TODO: If the base class's type can't be mapped, skip it. continue; } auto base_decl_id = AddInst( context, MakeImportedLocIdAndInst( context, import_ir_inst_id, SemIR::BaseDecl{.type_id = GetUnboundElementType( context, class_type_inst_id, base_type_inst_id), .base_type_inst_id = base_type_inst_id, .index = SemIR::ElementIndex(fields.size())})); auto* base_class = base.getType()->getAsCXXRecordDecl(); CARBON_CHECK(base_class, "Base class {0} is not a class", base.getType().getAsString()); // If there's a unique "best" base class, treat it as a Carbon base class // too. // TODO: Improve handling for the case where the class has multiple base // classes. BaseKind kind = base_class->isPolymorphic() ? BaseKind::Polymorphic : base_class->isEmpty() ? BaseKind::Empty : BaseKind::NonEmpty; auto& class_info = context.classes().Get(class_id); if (kind > base_kind) { // This base is better than the previous best. class_info.base_id = base_decl_id; base_kind = kind; } else if (kind == base_kind) { // Multiple base classes of this kind: no unique best. class_info.base_id = SemIR::InstId::None; } // TODO: If the base class has virtual bases, the size of the type that we // add to the layout here will be the full size of the class (including // virtual bases), whereas the size actually occupied by this base class is // only the nvsize (excluding virtual bases). auto base_offset = base.isVirtual() ? clang_layout.getVBaseClassOffset(base_class) : clang_layout.getBaseClassOffset(base_class); layout.push_back(base_offset.getQuantity()); fields.push_back( {.name_id = SemIR::NameId::Base, .type_inst_id = base_type_inst_id}); } // Import fields. for (auto* decl : clang_def->decls()) { auto* field = dyn_cast(decl); // Track the chain of fields from the class to this field. This chain is // only one element long unless the field is a member of an anonymous struct // or union. clang::NamedDecl* single_field_chain[1] = {field}; llvm::ArrayRef chain = single_field_chain; // If this isn't a field, it might be an indirect field in an anonymous // struct or union. if (!field) { auto* indirect_field = dyn_cast(decl); if (!indirect_field) { continue; } chain = indirect_field->chain(); field = indirect_field->getAnonField(); } if (field->isBitField()) { // TODO: Add a representation for named bitfield members. continue; } if (field->isAnonymousStructOrUnion()) { // Fields within an anonymous structure or union will be added via their // IndirectFieldDecls. continue; } auto field_name_id = AddIdentifierName(context, field->getName()); auto [field_type_inst_id, field_type_id] = ImportCppType(context, import_ir_inst_id, field->getType()); if (!field_type_inst_id.has_value()) { // TODO: For now, just skip over fields whose types we can't map. continue; } // Create a field now, as we know the index to use. // TODO: Consider doing this lazily instead. auto field_decl_id = AddInst( context, MakeImportedLocIdAndInst( context, import_ir_inst_id, SemIR::FieldDecl{ .type_id = GetUnboundElementType( context, class_type_inst_id, field_type_inst_id), .name_id = field_name_id, .index = SemIR::ElementIndex(fields.size())})); // The imported SemIR::FieldDecl represents the original declaration `decl`, // which is either the field or the indirect field declaration. auto key = SemIR::ClangDeclKey::ForNonFunctionDecl(decl); context.clang_decls().Add({.key = key, .inst_id = field_decl_id}); // Compute the offset to the field that appears directly in the class. uint64_t offset = clang_layout.getFieldOffset( cast(chain.front())->getFieldIndex()); // If this is an indirect field, walk the path and accumulate the offset to // the named field. for (auto* inner_decl : chain.drop_front()) { auto* inner_field = cast(inner_decl); const auto& inner_layout = context.ast_context().getASTRecordLayout(inner_field->getParent()); offset += inner_layout.getFieldOffset(inner_field->getFieldIndex()); } layout.push_back( context.ast_context().toCharUnitsFromBits(offset).getQuantity()); fields.push_back( {.name_id = field_name_id, .type_inst_id = field_type_inst_id}); } // TODO: Add a field to prevent tail padding reuse if necessary. return AddTypeInst(context, MakeImportedLocIdAndInst( context, import_ir_inst_id, {.type_id = SemIR::TypeType::TypeId, .fields_id = context.struct_type_fields().Add(fields), .layout_id = context.custom_layouts().Add(layout)})); } // Creates a Carbon class definition based on the information in the given Clang // class declaration, which is assumed to be for a class definition. static auto BuildClassDefinition(Context& context, SemIR::ImportIRInstId import_ir_inst_id, SemIR::ClassId class_id, SemIR::TypeInstId class_inst_id, clang::CXXRecordDecl* clang_def) -> void { auto& class_info = context.classes().Get(class_id); CARBON_CHECK(!class_info.has_definition_started()); class_info.definition_id = class_inst_id; context.inst_block_stack().Push(); class_info.inheritance_kind = GetInheritanceKind(clang_def); // Compute the class's object representation. auto object_repr_id = ImportClassObjectRepr( context, class_id, import_ir_inst_id, class_inst_id, clang_def); class_info.complete_type_witness_id = AddInst( context, MakeImportedLocIdAndInst( context, import_ir_inst_id, {.type_id = GetSingletonType(context, SemIR::WitnessType::TypeInstId), .object_repr_type_inst_id = object_repr_id})); class_info.body_block_id = context.inst_block_stack().Pop(); } // Computes and returns the Carbon type to use as the object representation of // the given C++ enum type. This is a builtin int type matching the enum's // representation. static auto ImportEnumObjectRepresentation( Context& context, SemIR::ImportIRInstId import_ir_inst_id, clang::EnumDecl* enum_decl) -> SemIR::TypeInstId { auto int_type = enum_decl->getIntegerType(); CARBON_CHECK(!int_type.isNull(), "incomplete enum type {0}", enum_decl->getNameAsString()); auto int_kind = int_type->isSignedIntegerType() ? SemIR::IntKind::Signed : SemIR::IntKind::Unsigned; auto bit_width_id = GetOrAddInst( context, MakeImportedLocIdAndInst( context, import_ir_inst_id, {.type_id = GetSingletonType( context, SemIR::IntLiteralType::TypeInstId), .int_id = context.ints().AddUnsigned(llvm::APInt( 64, context.ast_context().getIntWidth(int_type)))})); return context.types().GetAsTypeInstId( GetOrAddInst(context, SemIR::LocIdAndInst::NoLoc(SemIR::IntType{ .type_id = SemIR::TypeType::TypeId, .int_kind = int_kind, .bit_width_id = bit_width_id}))); } // Creates a Carbon class definition based on the information in the given Clang // enum declaration. static auto BuildEnumDefinition(Context& context, SemIR::ImportIRInstId import_ir_inst_id, SemIR::ClassId class_id, SemIR::TypeInstId class_inst_id, clang::EnumDecl* enum_decl) -> void { auto& class_info = context.classes().Get(class_id); CARBON_CHECK(!class_info.has_definition_started()); class_info.definition_id = class_inst_id; context.inst_block_stack().Push(); // Don't allow inheritance from C++ enums, to match the behavior in C++. class_info.inheritance_kind = SemIR::Class::Final; // Compute the enum type's object representation. An enum is an adapter for // the corresponding builtin integer type. auto object_repr_id = ImportEnumObjectRepresentation(context, import_ir_inst_id, enum_decl); class_info.adapt_id = AddInst( context, MakeImportedLocIdAndInst( context, import_ir_inst_id, SemIR::AdaptDecl{.adapted_type_inst_id = object_repr_id})); class_info.complete_type_witness_id = AddInst( context, MakeImportedLocIdAndInst( context, import_ir_inst_id, {.type_id = GetSingletonType(context, SemIR::WitnessType::TypeInstId), .object_repr_type_inst_id = object_repr_id})); class_info.body_block_id = context.inst_block_stack().Pop(); } // Imports an enumerator declaration from Clang to Carbon. static auto ImportEnumConstantDecl(Context& context, clang::EnumConstantDecl* enumerator_decl) -> SemIR::InstId { auto key = SemIR::ClangDeclKey(enumerator_decl); CARBON_CHECK(!IsClangDeclImported(context, key)); // Find the enclosing enum type. auto enum_key = SemIR::ClangDeclKey( cast(enumerator_decl->getDeclContext())); auto type_inst_id = LookupClangDeclInstId(context, enum_key); auto type_id = context.types().GetTypeIdForTypeInstId(type_inst_id); // Build a corresponding IntValue. auto int_id = context.ints().Add(enumerator_decl->getInitVal()); auto import_ir_inst_id = AddImportIRInst(context.sem_ir(), enumerator_decl->getLocation()); auto inst_id = AddInstInNoBlock( context, MakeImportedLocIdAndInst( context, import_ir_inst_id, {.type_id = type_id, .int_id = int_id})); context.imports().push_back(inst_id); context.clang_decls().Add({.key = key, .inst_id = inst_id}); return inst_id; } // Mark the given `key` as failed in `clang_decls`. static auto MarkFailedDecl(Context& context, SemIR::ClangDeclKey key) { context.clang_decls().Add({.key = key, .inst_id = SemIR::ErrorInst::InstId}); } // Creates an integer type of the given size. static auto MakeIntType(Context& context, IntId size_id, bool is_signed) -> TypeExpr { auto type_inst_id = MakeIntTypeLiteral( context, Parse::NodeId::None, is_signed ? SemIR::IntKind::Signed : SemIR::IntKind::Unsigned, size_id); return ExprAsType(context, Parse::NodeId::None, type_inst_id); } static auto MakeCppCompatType(Context& context, SemIR::LocId loc_id, CoreIdentifier name) -> TypeExpr { return ExprAsType( context, loc_id, LookupNameInCore(context, loc_id, {CoreIdentifier::CppCompat, name})); } // Maps a C++ builtin integer type to a Carbon `Core.CppCompat` type. static auto MapBuiltinCppCompatIntegerType(Context& context, unsigned int cpp_width, unsigned int carbon_width, CoreIdentifier cpp_compat_name) -> TypeExpr { if (cpp_width != carbon_width) { return TypeExpr::None; } return MakeCppCompatType(context, Parse::NodeId::None, cpp_compat_name); } // Maps a C++ builtin integer type to a Carbon type. // TODO: Handle integer types that map to named aliases. static auto MapBuiltinIntegerType(Context& context, SemIR::LocId loc_id, clang::QualType qual_type, const clang::BuiltinType& type) -> TypeExpr { clang::ASTContext& ast_context = context.ast_context(); unsigned width = ast_context.getIntWidth(qual_type); bool is_signed = type.isSignedInteger(); auto int_n_type = ast_context.getIntTypeForBitwidth(width, is_signed); if (clang::ASTContext::hasSameType(qual_type, int_n_type)) { TypeExpr type_expr = MakeIntType(context, context.ints().Add(width), is_signed); // Try to make sure integer types of 32 or 64 bits are complete so we can // check against them when deciding whether we need to generate a thunk. if (width == 32 || width == 64) { SemIR::TypeId type_id = type_expr.type_id; if (!context.types().IsComplete(type_id)) { TryToCompleteType(context, type_id, loc_id); } } return type_expr; } if (clang::ASTContext::hasSameType(qual_type, ast_context.CharTy)) { return ExprAsType(context, Parse::NodeId::None, MakeCharTypeLiteral(context, Parse::NodeId::None)); } if (clang::ASTContext::hasSameType(qual_type, ast_context.LongTy)) { return MapBuiltinCppCompatIntegerType(context, width, 32, CoreIdentifier::Long32); } if (clang::ASTContext::hasSameType(qual_type, ast_context.UnsignedLongTy)) { return MapBuiltinCppCompatIntegerType(context, width, 32, CoreIdentifier::ULong32); } if (clang::ASTContext::hasSameType(qual_type, ast_context.LongLongTy)) { return MapBuiltinCppCompatIntegerType(context, width, 64, CoreIdentifier::LongLong64); } if (clang::ASTContext::hasSameType(qual_type, ast_context.UnsignedLongLongTy)) { return MapBuiltinCppCompatIntegerType(context, width, 64, CoreIdentifier::ULongLong64); } return TypeExpr::None; } static auto MapNullptrType(Context& context, SemIR::LocId loc_id) -> TypeExpr { return MakeCppCompatType(context, loc_id, CoreIdentifier::NullptrT); } // Maps a C++ builtin type to a Carbon type. // TODO: Support more builtin types. static auto MapBuiltinType(Context& context, SemIR::LocId loc_id, clang::QualType qual_type, const clang::BuiltinType& type) -> TypeExpr { clang::ASTContext& ast_context = context.ast_context(); if (type.isBooleanType()) { CARBON_CHECK(ast_context.hasSameType(qual_type, ast_context.BoolTy)); return ExprAsType(context, Parse::NodeId::None, context.types().GetTypeInstId(GetSingletonType( context, SemIR::BoolType::TypeInstId))); } if (type.isInteger()) { return MapBuiltinIntegerType(context, loc_id, qual_type, type); } if (type.isFloatingPoint()) { if (type.isFloat16Type() || type.isFloat32Type() || type.isDoubleType() || type.isFloat128Type()) { return ExprAsType( context, Parse::NodeId::None, MakeFloatTypeLiteral( context, Parse::NodeId::None, context.ints().Add(ast_context.getTypeSize(qual_type)))); } // TODO: Handle floating-point types that map to named aliases. } else if (type.isVoidType()) { return MakeCppCompatType(context, loc_id, CoreIdentifier::VoidBase); } else if (type.isNullPtrType()) { return MapNullptrType(context, loc_id); } return TypeExpr::None; } // Determines whether record_decl is a C++ class that has a custom mapping into // Carbon, and if so, returns the corresponding Carbon type. Otherwise returns // None. static auto LookupCustomRecordType(Context& context, const clang::CXXRecordDecl* record_decl) -> TypeExpr { switch (GetCustomCppTypeMapping(record_decl)) { case CustomCppTypeMapping::None: return TypeExpr::None; case CustomCppTypeMapping::Str: return MakeStringType( context, AddImportIRInst(context.sem_ir(), record_decl->getLocation())); } } // Maps a C++ tag type (class, struct, union, enum) to a Carbon type. static auto MapTagType(Context& context, const clang::TagType& type) -> TypeExpr { auto* tag_decl = type.getDecl(); CARBON_CHECK(tag_decl); // Check if the declaration is already mapped. auto key = SemIR::ClangDeclKey(tag_decl); SemIR::InstId tag_inst_id = LookupClangDeclInstId(context, key); if (!tag_inst_id.has_value()) { if (auto* record_decl = dyn_cast(tag_decl)) { auto custom_type = LookupCustomRecordType(context, record_decl); if (custom_type.inst_id.has_value()) { context.clang_decls().Add({.key = key, .inst_id = custom_type.inst_id}); return custom_type; } } tag_inst_id = ImportTagDecl(context, tag_decl); } SemIR::TypeInstId record_type_inst_id = context.types().GetAsTypeInstId(tag_inst_id); return { // TODO: inst_id's location should be the location of the usage, not // the location of the type definition. Possibly we should synthesize a // NameRef inst, to match how this would work in Carbon code. .inst_id = record_type_inst_id, .type_id = context.types().GetTypeIdForTypeInstId(record_type_inst_id)}; } // Maps a C++ type that is not a wrapper type such as a pointer to a Carbon // type. // TODO: Support more types. static auto MapNonWrapperType(Context& context, SemIR::LocId loc_id, clang::QualType type) -> TypeExpr { if (const auto* builtin_type = type->getAs()) { return MapBuiltinType(context, loc_id, type, *builtin_type); } if (const auto* tag_type = type->getAs()) { return MapTagType(context, *tag_type); } CARBON_CHECK(!type.hasQualifiers() && !type->isPointerType(), "Should not see wrapper types here"); return TypeExpr::None; } // Maps a qualified C++ type to a Carbon type. static auto MapQualifiedType(Context& context, clang::QualType type, TypeExpr type_expr) -> TypeExpr { auto quals = type.getQualifiers(); if (quals.hasConst()) { auto type_id = GetConstType(context, type_expr.inst_id); type_expr = TypeExpr::ForUnsugared(context, type_id); quals.removeConst(); } // TODO: Support other qualifiers. if (!quals.empty()) { return TypeExpr::None; } return type_expr; } // Returns true if the type has the `_Nonnull` attribute. static auto IsClangTypeNonNull(clang::QualType type) -> bool { auto nullability = type->getNullability(); return nullability.has_value() && *nullability == clang::NullabilityKind::NonNull; } // Like `clang::QualType::getUnqualifiedType()`, retrieves the unqualified // variant of the given type, but preserves `_Nonnull`. static auto ClangGetUnqualifiedTypePreserveNonNull( Context& context, clang::QualType original_type) -> clang::QualType { clang::QualType type = original_type.getUnqualifiedType(); // Preserve non-nullability. if (IsClangTypeNonNull(original_type) && !IsClangTypeNonNull(type)) { type = context.ast_context().getAttributedType( clang::NullabilityKind::NonNull, type, type); } return type; } // Returns the type `Core.Optional(T)`, where `T` is described by // `inner_type_inst_id`. static auto MakeOptionalType(Context& context, SemIR::LocId loc_id, SemIR::InstId inner_type_inst_id) -> TypeExpr { auto fn_inst_id = LookupNameInCore(context, loc_id, CoreIdentifier::Optional); auto call_id = PerformCall(context, loc_id, fn_inst_id, {inner_type_inst_id}); return ExprAsType(context, loc_id, call_id); } // Maps a C++ pointer type to a Carbon pointer type. static auto MapPointerType(Context& context, SemIR::LocId loc_id, clang::QualType type, TypeExpr pointee_type_expr) -> TypeExpr { CARBON_CHECK(type->isPointerType()); bool optional = !IsClangTypeNonNull(type) && // If the type was produced by C++ template substitution, then we assume // it was deduced from a Carbon pointer type, so it's non-null. !type->getAs(); TypeExpr pointer_type_expr = TypeExpr::ForUnsugared( context, GetPointerType(context, pointee_type_expr.inst_id)); if (optional) { pointer_type_expr = MakeOptionalType(context, loc_id, pointer_type_expr.inst_id); } return pointer_type_expr; } // Maps a C++ reference type to a Carbon type. We map all references to // pointers for now. Note that when mapping function parameters and return // types, a different rule is used; see MapParameterType for details. // TODO: Revisit this and decide what we really want to do here. static auto MapReferenceType(Context& context, clang::QualType type, TypeExpr referenced_type_expr) -> TypeExpr { CARBON_CHECK(type->isReferenceType()); SemIR::TypeId pointer_type_id = GetPointerType(context, referenced_type_expr.inst_id); pointer_type_id = GetConstType(context, context.types().GetTypeInstId(pointer_type_id)); return TypeExpr::ForUnsugared(context, pointer_type_id); } // Maps a C++ type to a Carbon type. `type` should not be canonicalized because // we check for pointer nullability and nullability will be lost by // canonicalization. static auto MapType(Context& context, SemIR::LocId loc_id, clang::QualType type) -> TypeExpr { // Unwrap any type modifiers and wrappers. llvm::SmallVector wrapper_types; while (true) { clang::QualType orig_type = type; if (type.hasQualifiers()) { type = ClangGetUnqualifiedTypePreserveNonNull(context, type); } else if (type->isPointerType()) { type = type->getPointeeType(); } else if (type->isReferenceType()) { type = type.getNonReferenceType(); } else { break; } wrapper_types.push_back(orig_type); } auto mapped = MapNonWrapperType(context, loc_id, type); for (auto wrapper : llvm::reverse(wrapper_types)) { if (!mapped.inst_id.has_value() || mapped.type_id == SemIR::ErrorInst::TypeId) { break; } if (wrapper.hasQualifiers()) { mapped = MapQualifiedType(context, wrapper, mapped); } else if (wrapper->isPointerType()) { mapped = MapPointerType(context, loc_id, wrapper, mapped); } else if (wrapper->isReferenceType()) { mapped = MapReferenceType(context, wrapper, mapped); } else { CARBON_FATAL("Unexpected wrapper type {0}", wrapper.getAsString()); } } return mapped; } namespace { // Information about how to map a C++ parameter type into Carbon. struct ParameterTypeInfo { // The type to use for the Carbon parameter. TypeExpr type; // Whether to build a `ref` pattern. bool want_ref_pattern; }; } // namespace // Given the type of a C++ function parameter, returns information about the // type to use for the corresponding Carbon parameter. // // Note that if the parameter has a type for which `IsSimpleAbiType` returns // true, we must produce a parameter type that has the same calling convention // as the C++ type. static auto MapParameterType(Context& context, SemIR::LocId loc_id, clang::QualType param_type) -> ParameterTypeInfo { ParameterTypeInfo info = {.type = TypeExpr::None, .want_ref_pattern = false}; // Perform some custom mapping for parameters of reference type: // // * `T& x` -> `ref x: T`. // * `const T& x` -> `x: T`. // * `T&& x` -> `x: T`. // // TODO: For the `&&` mapping, we allow an rvalue reference to bind to a // durable reference expression. This should not be allowed. if (param_type->isReferenceType()) { clang::QualType pointee_type = param_type->getPointeeType(); if (param_type->isLValueReferenceType()) { if (pointee_type.isConstQualified()) { // TODO: Consider only doing this if `const` is the only qualifier. For // now, any other qualifier will fail when mapping the type. auto split_type = pointee_type.getSplitUnqualifiedType(); split_type.Quals.removeConst(); pointee_type = context.ast_context().getQualifiedType(split_type); } else { // The reference will map to a `ref` pattern. info.want_ref_pattern = true; } } param_type = pointee_type; } info.type = MapType(context, loc_id, param_type); return info; } // Returns a block for the implicit parameters of the given function // declaration. Because function templates are not yet supported, this currently // only contains the `self` parameter. On error, produces a diagnostic and // returns None. static auto MakeImplicitParamPatternsBlockId( Context& context, SemIR::LocId loc_id, const clang::FunctionDecl& clang_decl) -> SemIR::InstBlockId { const auto* method_decl = dyn_cast(&clang_decl); if (!method_decl || method_decl->isStatic() || isa(clang_decl)) { return SemIR::InstBlockId::Empty; } // Build a `self` parameter from the object parameter. BeginSubpattern(context); clang::QualType param_type = method_decl->getFunctionObjectParameterReferenceType(); auto param_info = MapParameterType(context, loc_id, param_type); auto [type_inst_id, type_id] = param_info.type; SemIR::ExprRegionId type_expr_region_id = EndSubpatternAsExpr(context, type_inst_id); if (!type_id.has_value()) { context.TODO(loc_id, llvm::formatv("Unsupported: object parameter type: {0}", param_type.getAsString())); return SemIR::InstBlockId::None; } // TODO: Fill in a location once available. auto pattern_id = AddParamPattern(context, loc_id, SemIR::NameId::SelfValue, type_expr_region_id, type_id, param_info.want_ref_pattern); return context.inst_blocks().Add({pattern_id}); } // Returns a block id for the explicit parameters of the given function // declaration. If the function declaration has no parameters, it returns // `SemIR::InstBlockId::Empty`. In the case of an unsupported parameter type, it // produces an error and returns `SemIR::InstBlockId::None`. `signature` // specifies how to convert the C++ signature to the Carbon signature. // TODO: Consider refactoring to extract and reuse more logic from // `HandleAnyBindingPattern()`. static auto MakeParamPatternsBlockId(Context& context, SemIR::LocId loc_id, const clang::FunctionDecl& clang_decl, SemIR::ClangDeclKey::Signature signature) -> SemIR::InstBlockId { llvm::SmallVector param_ids; llvm::SmallVector param_type_ids; param_ids.reserve(signature.num_params); param_type_ids.reserve(signature.num_params); CARBON_CHECK(static_cast(clang_decl.getNumNonObjectParams()) >= signature.num_params, "Function has fewer parameters than requested: {0} < {1}", clang_decl.getNumNonObjectParams(), signature.num_params); const auto* function_type = clang_decl.getType()->castAs(); for (int i : llvm::seq(signature.num_params)) { const auto* param = clang_decl.getNonObjectParameter(i); clang::QualType orig_param_type = function_type->getParamType( clang_decl.hasCXXExplicitFunctionObjectParameter() + i); // The parameter type is decayed but hasn't necessarily had its qualifiers // removed. // TODO: The presence of qualifiers here is probably a Clang bug. clang::QualType param_type = ClangGetUnqualifiedTypePreserveNonNull(context, orig_param_type); // Mark the start of a region of insts, needed for the type expression // created later with the call of `EndSubpatternAsExpr()`. BeginSubpattern(context); auto param_info = MapParameterType(context, loc_id, param_type); auto [type_inst_id, type_id] = param_info.type; // Type expression of the binding pattern - a single-entry/single-exit // region that allows control flow in the type expression e.g. fn F(x: if C // then i32 else i64). SemIR::ExprRegionId type_expr_region_id = EndSubpatternAsExpr(context, type_inst_id); if (!type_id.has_value()) { context.TODO(loc_id, llvm::formatv("Unsupported: parameter type: {0}", orig_param_type.getAsString())); return SemIR::InstBlockId::None; } llvm::StringRef param_name = param->getName(); SemIR::NameId name_id = param_name.empty() // Translate an unnamed parameter to an underscore to // match Carbon's naming of unnamed/unused function params. ? SemIR::NameId::Underscore : AddIdentifierName(context, param_name); SemIR::LocId param_loc_id = AddImportIRInst(context.sem_ir(), param->getLocation()); // TODO: Add template support. SemIR::InstId pattern_id = AddParamPattern(context, param_loc_id, name_id, type_expr_region_id, type_id, param_info.want_ref_pattern); param_ids.push_back(pattern_id); param_type_ids.push_back(type_inst_id); } switch (signature.kind) { case SemIR::ClangDeclKey::Signature::Normal: { // Use the converted parameter list as-is. break; } case SemIR::ClangDeclKey::Signature::TuplePattern: { // Replace the parameters with a single tuple pattern containing the // converted parameter list. auto param_block_id = context.inst_blocks().Add(param_ids); auto tuple_pattern_type_id = GetPatternType(context, GetTupleType(context, param_type_ids)); SemIR::InstId pattern_id = AddPatternInst( context, SemIR::LocIdAndInst::UncheckedLoc( loc_id, SemIR::TuplePattern{.type_id = tuple_pattern_type_id, .elements_id = param_block_id})); param_ids = {pattern_id}; break; } } return context.inst_blocks().Add(param_ids); } // Returns the return `TypeExpr` of the given function declaration. In case of // an unsupported return type, returns `SemIR::ErrorInst::InstId`. Constructors // are treated as returning a class instance. // TODO: Support more return types. static auto GetReturnTypeExpr(Context& context, SemIR::LocId loc_id, clang::FunctionDecl* clang_decl) -> Context::FormExpr { auto make_init_form = [&](SemIR::TypeInstId type_component_inst_id) { SemIR::InitForm inst = {.type_id = SemIR::FormType::TypeId, .type_component_inst_id = type_component_inst_id}; return context.constant_values().GetInstId(TryEvalInst(context, inst)); }; auto make_ref_form = [&](SemIR::TypeInstId type_component_inst_id) { SemIR::RefForm inst = {.type_id = SemIR::FormType::TypeId, .type_component_inst_id = type_component_inst_id}; return context.constant_values().GetInstId(TryEvalInst(context, inst)); }; clang::QualType orig_ret_type = clang_decl->getReturnType(); if (!orig_ret_type->isVoidType()) { bool is_reference = orig_ret_type->isReferenceType(); if (is_reference) { orig_ret_type = orig_ret_type->getPointeeType(); } // TODO: We should eventually map reference returns to non-pointer types // here. We should return by `ref` for `T&` return types once `ref` return // is implemented. auto [orig_type_inst_id, type_id] = MapType(context, loc_id, orig_ret_type); if (!orig_type_inst_id.has_value()) { context.TODO(loc_id, llvm::formatv("Unsupported: return type: {0}", orig_ret_type.getAsString())); return Context::FormExpr::Error; } Context::FormExpr result = { .form_inst_id = is_reference ? make_ref_form(orig_type_inst_id) : make_init_form(orig_type_inst_id), .type_component_inst_id = orig_type_inst_id, .type_component_id = type_id}; return result; } auto* ctor = dyn_cast(clang_decl); if (!ctor) { // void. return {.form_inst_id = SemIR::InstId::None, .type_component_inst_id = SemIR::TypeInstId::None, .type_component_id = SemIR::TypeId::None}; } // TODO: Make this a `PartialType`. SemIR::TypeInstId record_type_inst_id = context.types().GetAsTypeInstId( LookupClangDeclInstId(context, SemIR::ClangDeclKey(ctor->getParent()))); return {.form_inst_id = make_init_form(record_type_inst_id), .type_component_inst_id = record_type_inst_id, .type_component_id = context.types().GetTypeIdForTypeInstId(record_type_inst_id)}; } // Information about a function's declared return type, corresponding to the // fields of SemIR::Function with the same names. struct ReturnInfo { SemIR::TypeInstId return_type_inst_id; SemIR::InstId return_form_inst_id; SemIR::InstBlockId return_patterns_id; }; // Returns information about the declared return type of the given function // declaration. In case of an unsupported return type, it produces a diagnostic, // and the returned return_type_inst_id will be `SemIR::ErrorInst::InstId`. // Constructors are treated as returning a class instance. static auto GetReturnInfo(Context& context, SemIR::LocId loc_id, clang::FunctionDecl* clang_decl) -> ReturnInfo { auto [form_inst_id, type_inst_id, type_id] = GetReturnTypeExpr(context, loc_id, clang_decl); if (!form_inst_id.has_value()) { // void. return {.return_type_inst_id = SemIR::TypeInstId::None, .return_form_inst_id = SemIR::InstId::None, .return_patterns_id = SemIR::InstBlockId::None}; } if (form_inst_id == SemIR::ErrorInst::InstId) { return {.return_type_inst_id = SemIR::ErrorInst::TypeInstId, .return_form_inst_id = SemIR::ErrorInst::InstId, .return_patterns_id = SemIR::InstBlockId::None}; } auto pattern_type_id = GetPatternType(context, type_id); clang::SourceLocation return_type_loc = clang_decl->getReturnTypeSourceRange().getBegin(); if (return_type_loc.isInvalid()) { // TODO: While `getReturnTypeSourceRange()` should work, it seems broken for // trailing return type. See // https://github.com/llvm/llvm-project/issues/162649. Until this is fixed, // we fallback to `getTypeSpecStartLoc()`. return_type_loc = clang_decl->getTypeSpecStartLoc(); } SemIR::ImportIRInstId return_type_import_ir_inst_id = AddImportIRInst(context.sem_ir(), return_type_loc); auto return_patterns_id = SemIR::InstBlockId::Empty; if (auto init_form = context.insts().TryGetAs(form_inst_id)) { SemIR::InstId return_slot_pattern_id = AddPatternInst( context, MakeImportedLocIdAndInst( context, return_type_import_ir_inst_id, SemIR::ReturnSlotPattern({.type_id = pattern_type_id, .type_inst_id = type_inst_id}))); auto param_pattern_id = AddPatternInst( context, MakeImportedLocIdAndInst( context, return_type_import_ir_inst_id, SemIR::OutParamPattern({.type_id = pattern_type_id, .subpattern_id = return_slot_pattern_id}))); return_patterns_id = context.inst_blocks().Add({param_pattern_id}); } return {.return_type_inst_id = type_inst_id, .return_form_inst_id = form_inst_id, .return_patterns_id = return_patterns_id}; } namespace { // Represents the insts and inst blocks associated with the parameters and // returns of a function declaration, corresponding to the fields of // SemIR::Function with the same names. struct FunctionSignatureInsts { SemIR::InstBlockId implicit_param_patterns_id; SemIR::InstBlockId param_patterns_id; SemIR::TypeInstId return_type_inst_id; SemIR::InstId return_form_inst_id; SemIR::InstBlockId return_patterns_id; SemIR::InstBlockId call_param_patterns_id; SemIR::InstBlockId call_params_id; SemIR::Function::CallParamIndexRanges param_ranges; }; } // namespace // Creates the insts and inst blocks that represent the parameters and returns // of the given C++ function's Carbon counterpart, including emitting a callee // pattern match to create the `Call` parameters, and returns a // FunctionSignatureInsts containing the results. Produces a diagnostic and // returns `std::nullopt` if the function declaration has an unsupported // parameter type. `signature` specifies how to convert the C++ function // signature to the Carbon function signature. static auto CreateFunctionSignatureInsts( Context& context, SemIR::LocId loc_id, clang::FunctionDecl* clang_decl, SemIR::ClangDeclKey::Signature signature) -> std::optional { context.full_pattern_stack().StartImplicitParamList(); auto implicit_param_patterns_id = MakeImplicitParamPatternsBlockId(context, loc_id, *clang_decl); if (!implicit_param_patterns_id.has_value()) { return std::nullopt; } context.full_pattern_stack().EndImplicitParamList(); context.full_pattern_stack().StartExplicitParamList(); auto param_patterns_id = MakeParamPatternsBlockId(context, loc_id, *clang_decl, signature); if (!param_patterns_id.has_value()) { return std::nullopt; } context.full_pattern_stack().EndExplicitParamList(); auto [return_type_inst_id, return_form_inst_id, return_patterns_id] = GetReturnInfo(context, loc_id, clang_decl); if (return_type_inst_id == SemIR::ErrorInst::TypeInstId) { return std::nullopt; } auto match_results = CalleePatternMatch(context, implicit_param_patterns_id, param_patterns_id, return_patterns_id); return {{.implicit_param_patterns_id = implicit_param_patterns_id, .param_patterns_id = param_patterns_id, .return_type_inst_id = return_type_inst_id, .return_form_inst_id = return_form_inst_id, .return_patterns_id = return_patterns_id, .call_param_patterns_id = match_results.call_param_patterns_id, .call_params_id = match_results.call_params_id, .param_ranges = match_results.param_ranges}}; } // Returns the Carbon function name for the given function. static auto GetFunctionName(Context& context, clang::FunctionDecl* clang_decl) -> SemIR::NameId { switch (clang_decl->getDeclName().getNameKind()) { case clang::DeclarationName::CXXConstructorName: { auto key = SemIR::ClangDeclKey( cast(clang_decl)->getParent()); return context.classes() .Get(context.insts() .GetAs(LookupClangDeclInstId(context, key)) .class_id) .name_id; } case clang::DeclarationName::CXXDestructorName: { return SemIR::NameId::CppDestructor; } case clang::DeclarationName::CXXOperatorName: case clang::DeclarationName::CXXConversionFunctionName: { return SemIR::NameId::CppOperator; } default: { return AddIdentifierName(context, clang_decl->getName()); } } } // Creates a `FunctionDecl` and a `Function` without C++ thunk information. // Returns std::nullopt on failure. // // The given Clang declaration is assumed to: // * Have not been imported before. // * Be of supported type (ignoring parameters). // // `signature` specifies how to convert the C++ function signature to the Carbon // function signature. static auto ImportFunction(Context& context, SemIR::LocId loc_id, SemIR::ImportIRInstId import_ir_inst_id, clang::FunctionDecl* clang_decl, SemIR::ClangDeclKey::Signature signature) -> std::optional { StartFunctionSignature(context); auto function_params_insts = CreateFunctionSignatureInsts(context, loc_id, clang_decl, signature); auto [pattern_block_id, decl_block_id] = FinishFunctionSignature(context, /*check_unused=*/false); if (!function_params_insts.has_value()) { return std::nullopt; } auto virtual_modifier = SemIR::Function::VirtualModifier::None; int32_t virtual_index = -1; if (auto* method_decl = dyn_cast(clang_decl)) { if (method_decl->size_overridden_methods()) { virtual_modifier = SemIR::Function::VirtualModifier::Override; } else if (method_decl->isVirtual()) { virtual_modifier = SemIR::Function::VirtualModifier::Virtual; } if (virtual_modifier != SemIR::Function::VirtualModifier::None) { // TODO: Add support for Microsoft/non-Itanium vtables. virtual_index = dyn_cast( context.ast_context().getVTableContext()) ->getMethodVTableIndex(method_decl); } } SemIR::FunctionFields::EvaluationMode evaluation_mode = SemIR::FunctionFields::EvaluationMode::None; if (clang_decl->isConsteval()) { evaluation_mode = SemIR::FunctionFields::EvaluationMode::MustEval; } else if (clang_decl->isConstexpr()) { evaluation_mode = SemIR::FunctionFields::EvaluationMode::Eval; } auto [decl_id, function_id] = MakeFunctionDecl( context, import_ir_inst_id, decl_block_id, /*build_generic=*/false, /*is_definition=*/false, SemIR::Function{ { .name_id = GetFunctionName(context, clang_decl), .parent_scope_id = GetParentNameScopeId(context, clang_decl), .generic_id = SemIR::GenericId::None, .first_param_node_id = Parse::NodeId::None, .last_param_node_id = Parse::NodeId::None, .pattern_block_id = pattern_block_id, .implicit_param_patterns_id = function_params_insts->implicit_param_patterns_id, .param_patterns_id = function_params_insts->param_patterns_id, .is_extern = false, .extern_library_id = SemIR::LibraryNameId::None, .non_owning_decl_id = SemIR::InstId::None, // Set by `MakeFunctionDecl`. .first_owning_decl_id = SemIR::InstId::None, }, { .call_param_patterns_id = function_params_insts->call_param_patterns_id, .call_params_id = function_params_insts->call_params_id, .call_param_ranges = function_params_insts->param_ranges, .return_type_inst_id = function_params_insts->return_type_inst_id, .return_form_inst_id = function_params_insts->return_form_inst_id, .return_patterns_id = function_params_insts->return_patterns_id, .virtual_modifier = virtual_modifier, .virtual_index = virtual_index, .evaluation_mode = evaluation_mode, .self_param_id = FindSelfPattern( context, function_params_insts->implicit_param_patterns_id), }}); context.imports().push_back(decl_id); context.functions().Get(function_id).clang_decl_id = context.clang_decls().Add( {.key = SemIR::ClangDeclKey::ForFunctionDecl(clang_decl, signature), .inst_id = decl_id}); return function_id; } // Imports a C++ function, returning a corresponding Carbon function. // `signature` specifies how to convert the C++ function signature to the Carbon // function signature. `signature.num_params` may be less than the number of // parameters that the C++ function has if default arguments are available for // the trailing parameters. static auto ImportFunctionDecl(Context& context, SemIR::LocId loc_id, clang::FunctionDecl* clang_decl, SemIR::ClangDeclKey::Signature signature) -> SemIR::InstId { auto key = SemIR::ClangDeclKey::ForFunctionDecl(clang_decl, signature); // Check if the declaration is already mapped. if (SemIR::InstId existing_inst_id = LookupClangDeclInstId(context, key); existing_inst_id.has_value()) { return existing_inst_id; } if (clang_decl->isVariadic()) { context.TODO(loc_id, "Unsupported: Variadic function"); MarkFailedDecl(context, key); return SemIR::ErrorInst::InstId; } if (clang_decl->getTemplatedKind() == clang::FunctionDecl::TK_FunctionTemplate) { context.TODO(loc_id, "Unsupported: Template function"); MarkFailedDecl(context, key); return SemIR::ErrorInst::InstId; } auto import_ir_inst_id = AddImportIRInst(context.sem_ir(), clang_decl->getLocation()); CARBON_CHECK(clang_decl->getFunctionType()->isFunctionProtoType(), "Not Prototype function (non-C++ code)"); auto function_id = ImportFunction(context, loc_id, import_ir_inst_id, clang_decl, signature); if (!function_id) { MarkFailedDecl(context, key); return SemIR::ErrorInst::InstId; } SemIR::Function& function_info = context.functions().Get(*function_id); if (IsCppThunkRequired(context, function_info)) { Diagnostics::AnnotationScope annotate_diagnostics( &context.emitter(), [&](auto& builder) { CARBON_DIAGNOSTIC(InCppThunk, Note, "in thunk for C++ function used here"); builder.Note(loc_id, InCppThunk); }); if (clang::FunctionDecl* thunk_clang_decl = BuildCppThunk(context, function_info)) { if (auto thunk_function_id = ImportFunction( context, loc_id, import_ir_inst_id, thunk_clang_decl, {.num_params = static_cast(thunk_clang_decl->getNumParams())})) { SemIR::InstId thunk_function_decl_id = context.functions().Get(*thunk_function_id).first_owning_decl_id; function_info.SetHasCppThunk(thunk_function_decl_id); } } } else { // Inform Clang that the function has been referenced. This will trigger // instantiation if needed. context.clang_sema().MarkFunctionReferenced(GetCppLocation(context, loc_id), clang_decl); // If the function is trivial, mark it as being a builtin if possible. if (clang_decl->isTrivial()) { // Trivial destructors map to a "no_op" builtin. if (isa(clang_decl)) { function_info.SetBuiltinFunction(SemIR::BuiltinFunctionKind::NoOp); } // TODO: Should we model a trivial default constructor as performing // value-initialization (zero-initializing all fields) or // default-initialization (leaving fields uniniitalized)? Either way we // could model that effect as a builtin. // TODO: Add a builtin to model trivial copies. } } return function_info.first_owning_decl_id; } namespace { // An item to be imported in an import worklist. // TODO: If worklists ever become particularly large, consider changing this // to use a `PointerIntPair`. struct ImportItem { // A declaration that we want to import. SemIR::ClangDeclKey decl_key; // Whether we have added `decl`'s dependencies to the worklist. bool added_dependencies; }; // A worklist of declarations to import. using ImportWorklist = llvm::SmallVector; } // namespace // Adds the given declaration to our list of declarations to import. static auto AddDependentDecl(Context& context, SemIR::ClangDeclKey decl, ImportWorklist& worklist) -> void { if (!IsClangDeclImported(context, decl)) { worklist.push_back({.decl_key = decl, .added_dependencies = false}); } } // Finds all decls that need to be imported before importing the given type and // adds them to the given set. static auto AddDependentUnimportedTypeDecls(Context& context, clang::QualType type, ImportWorklist& worklist) -> void { while (true) { if (type->isPointerType() || type->isReferenceType()) { type = type->getPointeeType(); } else if (const clang::ArrayType* array_type = type->getAsArrayTypeUnsafe()) { type = array_type->getElementType(); } else { break; } } if (const auto* tag_type = type->getAs()) { AddDependentDecl(context, SemIR::ClangDeclKey(tag_type->getDecl()), worklist); } } // Finds all decls that need to be imported before importing the given function // and adds them to the given set. static auto AddDependentUnimportedFunctionDecls( Context& context, const clang::FunctionDecl& clang_decl, SemIR::ClangDeclKey::Signature signature, ImportWorklist& worklist) -> void { const auto* function_type = clang_decl.getType()->castAs(); for (int i : llvm::seq(clang_decl.hasCXXExplicitFunctionObjectParameter() + signature.num_params)) { AddDependentUnimportedTypeDecls(context, function_type->getParamType(i), worklist); } AddDependentUnimportedTypeDecls(context, clang_decl.getReturnType(), worklist); } // Finds all decls that need to be imported before importing the given // declaration and adds them to the given set. static auto AddDependentUnimportedDecls(Context& context, SemIR::ClangDeclKey key, ImportWorklist& worklist) -> void { clang::Decl* clang_decl = key.decl; if (auto* clang_function_decl = clang_decl->getAsFunction()) { AddDependentUnimportedFunctionDecls(context, *clang_function_decl, key.signature, worklist); } else if (auto* type_decl = dyn_cast(clang_decl)) { if (!isa(clang_decl)) { AddDependentUnimportedTypeDecls( context, type_decl->getASTContext().getTypeDeclType(type_decl), worklist); } } else if (auto* var_decl = dyn_cast(clang_decl)) { AddDependentUnimportedTypeDecls(context, var_decl->getType(), worklist); } auto* parent = GetParentDecl(clang_decl); if (llvm::isa_and_nonnull(parent)) { AddDependentDecl(context, SemIR::ClangDeclKey::ForNonFunctionDecl(parent), worklist); } } static auto ImportVarDecl(Context& context, SemIR::LocId loc_id, clang::VarDecl* var_decl) -> SemIR::InstId { if (SemIR::InstId existing_inst_id = LookupClangDeclInstId(context, SemIR::ClangDeclKey(var_decl)); existing_inst_id.has_value()) { return existing_inst_id; } // Extract type and name. clang::QualType var_type = var_decl->getType(); SemIR::TypeId var_type_id = MapType(context, loc_id, var_type).type_id; if (!var_type_id.has_value()) { context.TODO(loc_id, llvm::formatv("Unsupported: var type: {0}", var_type.getAsString())); return SemIR::ErrorInst::InstId; } SemIR::NameId var_name_id = AddIdentifierName(context, var_decl->getName()); // Create an entity name to identify this variable. SemIR::EntityNameId entity_name_id = context.entity_names().Add( {.name_id = var_name_id, .parent_scope_id = GetParentNameScopeId(context, var_decl), .is_unused = false}); // Create `RefBindingPattern` and `VarPattern`. Mirror the behavior of // import_ref and don't create a `NameBindingDecl` here; we'd never use it for // anything. SemIR::TypeId pattern_type_id = GetPatternType(context, var_type_id); SemIR::InstId binding_pattern_inst_id = AddInstInNoBlock( context, loc_id, {.type_id = pattern_type_id, .entity_name_id = entity_name_id}); context.imports().push_back(binding_pattern_inst_id); auto pattern_id = AddInstInNoBlock( context, Parse::VariablePatternId::None, {.type_id = pattern_type_id, .subpattern_id = binding_pattern_inst_id}); context.imports().push_back(pattern_id); // Create the imported storage for the global. We intentionally use the // untyped form of `AddInstInNoBlock` to bypass the check on adding an // instruction that requires a cleanup, because we don't want a cleanup here! SemIR::InstId var_storage_inst_id = AddInstInNoBlock( context, {loc_id, SemIR::VarStorage{.type_id = var_type_id, .pattern_id = pattern_id}}); context.imports().push_back(var_storage_inst_id); // Register the variable so we don't create it again, and track the // corresponding declaration to use for mangling. auto clang_decl_id = context.clang_decls().Add( {.key = SemIR::ClangDeclKey(var_decl), .inst_id = var_storage_inst_id}); context.cpp_global_names().Add({.key = {.entity_name_id = entity_name_id}, .clang_decl_id = clang_decl_id}); // Inform Clang that the variable has been referenced. context.clang_sema().MarkVariableReferenced(GetCppLocation(context, loc_id), var_decl); return var_storage_inst_id; } static auto ImportTemplateDecl(Context& context, clang::TemplateDecl* template_decl) -> SemIR::InstId { auto key = SemIR::ClangDeclKey(template_decl); // TODO: Avoid doing this lookup both here and in the insertion below. if (SemIR::InstId existing_inst_id = LookupClangDeclInstId(context, key); existing_inst_id.has_value()) { return existing_inst_id; } // Add a placeholder instruction to resolve cycle between the clang // declaration and the type. auto import_loc_id = AddImportIRInst(context.sem_ir(), template_decl->getLocation()); SemIR::StructValue value = {.type_id = SemIR::TypeId::None, .elements_id = SemIR::InstBlockId::Empty}; auto inst_id = AddPlaceholderImportedInstInNoBlock( context, MakeImportedLocIdAndInst(context, import_loc_id, value)); // Create a type for the constant value. auto name_id = context.entity_names().Add( {.name_id = AddIdentifierName(context, template_decl->getName()), .parent_scope_id = GetParentNameScopeId(context, template_decl)}); auto decl_id = context.clang_decls().Add({.key = key, .inst_id = inst_id}); value.type_id = GetCppTemplateNameType(context, name_id, decl_id); // Update the value with its type. ReplaceInstBeforeConstantUse(context, inst_id, value); return inst_id; } // Imports a declaration from Clang to Carbon. Returns the instruction for the // new Carbon declaration, which will be an ErrorInst on failure. Assumes all // dependencies have already been imported. static auto ImportDeclAfterDependencies(Context& context, SemIR::LocId loc_id, SemIR::ClangDeclKey key) -> SemIR::InstId { clang::Decl* clang_decl = key.decl; if (auto* clang_function_decl = clang_decl->getAsFunction()) { return ImportFunctionDecl(context, loc_id, clang_function_decl, key.signature); } if (auto* clang_namespace_decl = dyn_cast(clang_decl)) { return ImportNamespaceDecl(context, clang_namespace_decl); } if (auto* type_decl = dyn_cast(clang_decl)) { auto type = clang_decl->getASTContext().getTypeDeclType(type_decl); auto type_inst_id = MapType(context, loc_id, type).inst_id; if (!type_inst_id.has_value()) { context.TODO(AddImportIRInst(context.sem_ir(), type_decl->getLocation()), llvm::formatv("Unsupported: Type declaration: {0}", type.getAsString())); return SemIR::ErrorInst::InstId; } context.clang_decls().Add({.key = key, .inst_id = type_inst_id}); return type_inst_id; } if (isa(clang_decl)) { // Usable fields get imported as a side effect of importing the class. if (SemIR::InstId existing_inst_id = LookupClangDeclInstId(context, key); existing_inst_id.has_value()) { return existing_inst_id; } context.TODO(AddImportIRInst(context.sem_ir(), clang_decl->getLocation()), "Unsupported: field declaration has unhandled type or kind"); return SemIR::ErrorInst::InstId; } if (auto* enum_const_decl = dyn_cast(clang_decl)) { return ImportEnumConstantDecl(context, enum_const_decl); } if (auto* var_decl = dyn_cast(clang_decl)) { return ImportVarDecl(context, loc_id, var_decl); } if (auto* template_decl = dyn_cast(clang_decl)) { return ImportTemplateDecl(context, template_decl); } context.TODO(AddImportIRInst(context.sem_ir(), clang_decl->getLocation()), llvm::formatv("Unsupported: Declaration type {0}", clang_decl->getDeclKindName())); return SemIR::ErrorInst::InstId; } // Attempts to import a set of declarations. Returns `false` if an error was // produced, `true` otherwise. static auto ImportDeclSet(Context& context, SemIR::LocId loc_id, ImportWorklist& worklist) -> bool { // Walk the dependency graph in depth-first order, and import declarations // once we've imported all of their dependencies. while (!worklist.empty()) { auto& item = worklist.back(); if (!item.added_dependencies) { // Skip items we've already imported. We checked this when initially // adding the item to the worklist, but it might have been added to the // worklist twice before the first time we visited it. For example, this // happens for `fn F(a: Cpp.T, b: Cpp.T)`. if (IsClangDeclImported(context, item.decl_key)) { worklist.pop_back(); continue; } // First time visiting this declaration (preorder): add its dependencies // to the work list. item.added_dependencies = true; AddDependentUnimportedDecls(context, item.decl_key, worklist); } else { // Second time visiting this declaration (postorder): its dependencies are // already imported, so we can import it now. auto decl_key = worklist.pop_back_val().decl_key; auto inst_id = ImportDeclAfterDependencies(context, loc_id, decl_key); CARBON_CHECK(inst_id.has_value()); if (inst_id == SemIR::ErrorInst::InstId) { return false; } CARBON_CHECK(IsClangDeclImported(context, decl_key)); } } return true; } auto ImportCppDecl(Context& context, SemIR::LocId loc_id, SemIR::ClangDeclKey key) -> SemIR::InstId { // Collect dependencies by walking the dependency graph in depth-first order. ImportWorklist worklist; AddDependentDecl(context, key, worklist); if (!ImportDeclSet(context, loc_id, worklist)) { return SemIR::ErrorInst::InstId; } return LookupClangDeclInstId(context, key); } auto ImportCppType(Context& context, SemIR::LocId loc_id, clang::QualType type) -> TypeExpr { // Collect dependencies by walking the dependency graph in depth-first order. ImportWorklist worklist; AddDependentUnimportedTypeDecls(context, type, worklist); if (!ImportDeclSet(context, loc_id, worklist)) { return {.inst_id = SemIR::ErrorInst::TypeInstId, .type_id = SemIR::ErrorInst::TypeId}; } return MapType(context, loc_id, type); } // Imports a Clang declaration into Carbon and adds that name into the // `NameScope`. static auto ImportNameDeclIntoScope(Context& context, SemIR::LocId loc_id, SemIR::NameScopeId scope_id, SemIR::NameId name_id, SemIR::ClangDeclKey key, SemIR::AccessKind access_kind) -> SemIR::ScopeLookupResult { SemIR::InstId inst_id = ImportCppDecl(context, loc_id, key); if (!inst_id.has_value()) { return SemIR::ScopeLookupResult::MakeNotFound(); } AddNameToScope(context, scope_id, name_id, access_kind, inst_id); return SemIR::ScopeLookupResult::MakeWrappedLookupResult(inst_id, access_kind); } // Returns true if the scope is the top `Cpp` scope. static auto IsTopCppScope(Context& context, SemIR::NameScopeId scope_id) -> bool { const SemIR::NameScope& name_scope = context.name_scopes().Get(scope_id); CARBON_CHECK(name_scope.is_cpp_scope()); return name_scope.parent_scope_id() == SemIR::NameScopeId::Package; } // For a builtin name like `Cpp.long`, returns the associated type. static auto LookupBuiltinName(Context& context, SemIR::LocId loc_id, SemIR::NameScopeId scope_id, SemIR::NameId name_id) -> SemIR::InstId { if (!IsTopCppScope(context, scope_id)) { return SemIR::InstId::None; } auto name = context.names().GetAsStringIfIdentifier(name_id); if (!name) { return SemIR::InstId::None; } const clang::ASTContext& ast_context = context.ast_context(); // List of types based on // https://github.com/carbon-language/carbon-lang/blob/trunk/proposals/p5448.md#details auto builtin_type = llvm::StringSwitch(*name) .Case("signed_char", ast_context.SignedCharTy) .Case("short", ast_context.ShortTy) .Case("int", ast_context.IntTy) .Case("long", ast_context.LongTy) .Case("long_long", ast_context.LongLongTy) .Case("unsigned_char", ast_context.UnsignedCharTy) .Case("unsigned_short", ast_context.UnsignedShortTy) .Case("unsigned_int", ast_context.UnsignedIntTy) .Case("unsigned_long", ast_context.UnsignedLongTy) .Case("unsigned_long_long", ast_context.UnsignedLongLongTy) .Case("float", ast_context.FloatTy) .Case("double", ast_context.DoubleTy) .Case("long_double", ast_context.LongDoubleTy) .Case("void", ast_context.VoidTy) .Default(clang::QualType()); if (builtin_type.isNull()) { if (*name == "nullptr") { // Map `Cpp.nullptr` to an uninitialized value of type `Core.CppNullptrT`. auto type_id = MapNullptrType(context, loc_id).type_id; return GetOrAddInst( context, SemIR::LocId::None, {.type_id = type_id}); } return SemIR::InstId::None; } SemIR::InstId inst_id = MapNonWrapperType(context, loc_id, builtin_type).inst_id; if (!inst_id.has_value()) { context.TODO(loc_id, llvm::formatv("Unsupported: builtin type: {0}", builtin_type.getAsString())); return SemIR::ErrorInst::InstId; } return inst_id; } auto ImportCppOverloadSet( Context& context, SemIR::LocId loc_id, SemIR::NameScopeId scope_id, SemIR::NameId name_id, clang::CXXRecordDecl* naming_class, clang::UnresolvedSet<4>&& overload_set, clang::OverloadCandidateSet::OperatorRewriteInfo operator_rewrite_info) -> SemIR::InstId { SemIR::CppOverloadSetId overload_set_id = context.cpp_overload_sets().Add( SemIR::CppOverloadSet{.name_id = name_id, .parent_scope_id = scope_id, .naming_class = naming_class, .candidate_functions = std::move(overload_set), .operator_rewrite_info = operator_rewrite_info}); auto overload_set_inst_id = AddInstInNoBlock( context, loc_id, {.type_id = GetCppOverloadSetType(context, overload_set_id, SemIR::SpecificId::None), .overload_set_id = overload_set_id}); context.imports().push_back(overload_set_inst_id); return overload_set_inst_id; } // Gets the best access for an overloaded function set. This is the access that // we use for the overload set as a whole. More fine-grained checking is done // after overload resolution. static auto GetOverloadSetAccess(const clang::UnresolvedSet<4>& overload_set) -> SemIR::AccessKind { SemIR::AccessKind access_kind = SemIR::AccessKind::Private; for (clang::DeclAccessPair overload : overload_set.pairs()) { access_kind = std::min(access_kind, MapCppAccess(overload)); if (access_kind == SemIR::AccessKind::Public) { break; } } return access_kind; } // Imports an overload set from Clang to Carbon and adds the name into the // `NameScope`. static auto ImportOverloadSetIntoScope(Context& context, SemIR::LocId loc_id, SemIR::NameScopeId scope_id, SemIR::NameId name_id, clang::CXXRecordDecl* naming_class, clang::UnresolvedSet<4>&& overload_set) -> SemIR::ScopeLookupResult { SemIR::AccessKind access_kind = GetOverloadSetAccess(overload_set); SemIR::InstId inst_id = ImportCppOverloadSet( context, loc_id, scope_id, name_id, naming_class, std::move(overload_set), /*operator_rewrite_info=*/{}); AddNameToScope(context, scope_id, name_id, access_kind, inst_id); return SemIR::ScopeLookupResult::MakeWrappedLookupResult(inst_id, access_kind); } // Imports the constructors for a given class name. The found constructors are // imported as part of an overload set into the scope. Currently copy/move // constructors are not imported. static auto ImportConstructorsIntoScope(Context& context, SemIR::LocId loc_id, SemIR::NameScopeId scope_id, SemIR::NameId name_id) -> SemIR::ScopeLookupResult { auto* naming_class = cast(GetDeclContext(context, scope_id)); clang::DeclContextLookupResult constructors_lookup = context.clang_sema().LookupConstructors(naming_class); clang::UnresolvedSet<4> overload_set; for (auto* decl : constructors_lookup) { auto info = clang::getConstructorInfo(decl); if (!info.Constructor || info.Constructor->isCopyOrMoveConstructor()) { continue; } overload_set.addDecl(info.FoundDecl, info.FoundDecl.getAccess()); } if (overload_set.empty()) { return SemIR::ScopeLookupResult::MakeNotFound(); } return ImportOverloadSetIntoScope(context, loc_id, scope_id, name_id, naming_class, std::move(overload_set)); } // Attempts to import a builtin name from Clang to Carbon and adds the name into // the scope. static auto ImportBuiltinNameIntoScope(Context& context, SemIR::LocId loc_id, SemIR::NameScopeId scope_id, SemIR::NameId name_id) -> SemIR::ScopeLookupResult { SemIR::InstId builtin_inst_id = LookupBuiltinName(context, loc_id, scope_id, name_id); if (builtin_inst_id.has_value()) { AddNameToScope(context, scope_id, name_id, SemIR::AccessKind::Public, builtin_inst_id); return SemIR::ScopeLookupResult::MakeWrappedLookupResult( builtin_inst_id, SemIR::AccessKind::Public); } return SemIR::ScopeLookupResult::MakeNotFound(); } // Checks if the name scope is a class that is not complete. static auto IsIncompleteClass(Context& context, SemIR::NameScopeId scope_id) -> bool { auto class_decl = context.insts().TryGetAs( context.name_scopes().Get(scope_id).inst_id()); return class_decl.has_value() && !context.types().IsComplete( context.classes().Get(class_decl->class_id).self_type_id); } // Imports a macro definition into the scope. Currently supports only simple // object-like macros that expand to a constant integer value. // TODO: Add support for other macro types and non-integer literal values. static auto ImportMacro(Context& context, SemIR::LocId loc_id, SemIR::NameScopeId scope_id, SemIR::NameId name_id, clang::MacroInfo* macro_info) -> SemIR::ScopeLookupResult { auto inst_id = TryEvaluateMacro(context, loc_id, name_id, macro_info); if (inst_id == SemIR::ErrorInst::InstId) { return SemIR::ScopeLookupResult::MakeNotFound(); } AddNameToScope(context, scope_id, name_id, SemIR::AccessKind::Public, inst_id); return SemIR::ScopeLookupResult::MakeWrappedLookupResult( inst_id, SemIR::AccessKind::Public); } // Looks up a macro definition in the top-level `Cpp` scope. Returns nullptr if // the macro is not found or if it is a builtin macro, function-like macro or a // macro used for header guards. // TODO: Function-like and builtin macros are currently not supported and their // support still needs to be clarified. static auto LookupMacro(Context& context, SemIR::NameScopeId scope_id, clang::IdentifierInfo* identifier_info) -> clang::MacroInfo* { if (!IsTopCppScope(context, scope_id)) { return nullptr; } CARBON_CHECK(identifier_info, "Identifier info is empty"); clang::MacroInfo* macro_info = context.clang_sema().getPreprocessor().getMacroInfo(identifier_info); if (macro_info && !macro_info->isUsedForHeaderGuard() && !macro_info->isFunctionLike() && !macro_info->isBuiltinMacro()) { return macro_info; } return nullptr; } auto GetClangIdentifierInfo(Context& context, SemIR::NameId name_id) -> clang::IdentifierInfo* { std::optional string_name = context.names().GetAsStringIfIdentifier(name_id); if (!string_name) { return nullptr; } clang::IdentifierInfo* identifier_info = context.clang_sema().getPreprocessor().getIdentifierInfo(*string_name); return identifier_info; } auto ImportNameFromCpp(Context& context, SemIR::LocId loc_id, SemIR::NameScopeId scope_id, SemIR::NameId name_id) -> SemIR::ScopeLookupResult { Diagnostics::AnnotationScope annotate_diagnostics( &context.emitter(), [&](auto& builder) { CARBON_DIAGNOSTIC(InCppNameLookup, Note, "in `Cpp` name lookup for `{0}`", SemIR::NameId); builder.Note(loc_id, InCppNameLookup, name_id); }); if (IsIncompleteClass(context, scope_id)) { return SemIR::ScopeLookupResult::MakeError(); } clang::IdentifierInfo* identifier_info = GetClangIdentifierInfo(context, name_id); if (!identifier_info) { return SemIR::ScopeLookupResult::MakeNotFound(); } if (clang::MacroInfo* macro_info = LookupMacro(context, scope_id, identifier_info)) { return ImportMacro(context, loc_id, scope_id, name_id, macro_info); } auto lookup = ClangLookupName(context, scope_id, identifier_info); if (!lookup) { return ImportBuiltinNameIntoScope(context, loc_id, scope_id, name_id); } // Access checks are performed separately by the Carbon name lookup logic. lookup->suppressAccessDiagnostics(); if (lookup->isOverloadedResult() || (lookup->isSingleResult() && lookup->getFoundDecl()->isFunctionOrFunctionTemplate())) { clang::UnresolvedSet<4> overload_set; overload_set.append(lookup->begin(), lookup->end()); return ImportOverloadSetIntoScope(context, loc_id, scope_id, name_id, lookup->getNamingClass(), std::move(overload_set)); } if (!lookup->isSingleResult()) { // Clang will diagnose ambiguous lookup results for us. if (!lookup->isAmbiguous()) { context.TODO(loc_id, llvm::formatv("Unsupported: Lookup succeeded but couldn't " "find a single result; LookupResultKind: {0}", static_cast(lookup->getResultKind()))); } context.name_scopes().AddRequiredName(scope_id, name_id, SemIR::ErrorInst::InstId); return SemIR::ScopeLookupResult::MakeError(); } if (IsDeclInjectedClassName(context, scope_id, name_id, lookup->getFoundDecl())) { return ImportConstructorsIntoScope(context, loc_id, scope_id, name_id); } auto key = SemIR::ClangDeclKey::ForNonFunctionDecl(lookup->getFoundDecl()); return ImportNameDeclIntoScope(context, loc_id, scope_id, name_id, key, MapCppAccess(lookup->begin().getPair())); } auto ImportClassDefinitionForClangDecl(Context& context, SemIR::ClassId class_id, SemIR::ClangDeclId clang_decl_id) -> bool { SemIR::CppFile* cpp_file = context.sem_ir().cpp_file(); CARBON_CHECK(cpp_file); auto* clang_decl = cast(context.clang_decls().Get(clang_decl_id).key.decl); auto class_inst_id = context.types().GetAsTypeInstId( context.classes().Get(class_id).first_owning_decl_id); clang::SourceLocation loc = clang_decl->getLocation(); // Ask Clang whether the type is complete. This triggers template // instantiation if necessary. clang::DiagnosticErrorTrap trap(cpp_file->diagnostics()); if (!context.cpp_context()->sema().isCompleteType( loc, context.ast_context().getCanonicalTagType(clang_decl))) { // Type is incomplete. Nothing more to do, but tell the caller if we // produced an error. return !trap.hasErrorOccurred(); } auto import_ir_inst_id = context.insts().GetCanonicalLocId(class_inst_id).import_ir_inst_id(); if (auto* class_decl = dyn_cast(clang_decl)) { auto* class_def = class_decl->getDefinition(); CARBON_CHECK(class_def, "Complete type has no definition"); BuildClassDefinition(context, import_ir_inst_id, class_id, class_inst_id, class_def); } else if (auto* enum_decl = dyn_cast(clang_decl)) { BuildEnumDefinition(context, import_ir_inst_id, class_id, class_inst_id, enum_decl); } return true; } auto GetAsClangVarDecl(Context& context, SemIR::InstId inst_id) -> clang::VarDecl* { if (const auto& var_storage = context.insts().TryGetAs(inst_id)) { auto var_name_id = SemIR::GetFirstBindingNameFromPatternId( context.sem_ir(), var_storage->pattern_id); if (auto cpp_global_var_id = context.sem_ir().cpp_global_vars().Lookup( {.entity_name_id = var_name_id}); cpp_global_var_id.has_value()) { SemIR::ClangDeclId clang_decl_id = context.sem_ir() .cpp_global_vars() .Get(cpp_global_var_id) .clang_decl_id; return cast( context.clang_decls().Get(clang_decl_id).key.decl); } } return nullptr; } } // namespace Carbon::Check ================================================ FILE: toolchain/check/cpp/import.h ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #ifndef CARBON_TOOLCHAIN_CHECK_CPP_IMPORT_H_ #define CARBON_TOOLCHAIN_CHECK_CPP_IMPORT_H_ #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/IntrusiveRefCntPtr.h" #include "llvm/ADT/StringRef.h" #include "llvm/Support/VirtualFileSystem.h" #include "toolchain/check/context.h" #include "toolchain/check/convert.h" #include "toolchain/check/diagnostic_helpers.h" #include "toolchain/diagnostics/emitter.h" #include "toolchain/sem_ir/clang_decl.h" #include "toolchain/sem_ir/ids.h" namespace Carbon::Check { // Generates a C++ header that includes the imported cpp files, parses it, // generates the AST from it and links `SemIR::File` to it. Reports C++ errors // and warnings. If successful, adds a `Cpp` namespace. auto ImportCpp(Context& context, llvm::ArrayRef imports, llvm::IntrusiveRefCntPtr fs, llvm::LLVMContext* llvm_context, std::shared_ptr invocation) -> void; // Imports a declaration from Clang to Carbon. If successful, returns the new // Carbon declaration `InstId`. If the declaration was already imported, returns // the mapped instruction. All unimported dependencies are imported first. auto ImportCppDecl(Context& context, SemIR::LocId loc_id, SemIR::ClangDeclKey key) -> SemIR::InstId; // Imports a function declaration from Clang to Carbon. If successful, returns // the new Carbon function declaration `InstId`. If the declaration was already // imported, returns the mapped instruction. inline auto ImportCppFunctionDecl(Context& context, SemIR::LocId loc_id, clang::FunctionDecl* clang_decl, SemIR::ClangDeclKey::Signature signature) -> SemIR::InstId { return ImportCppDecl( context, loc_id, SemIR::ClangDeclKey::ForFunctionDecl(clang_decl, signature)); } // Imports a function declaration from Clang to Carbon. If successful, returns // the new Carbon function declaration `InstId`. If the declaration was already // imported, returns the mapped instruction. All unimported dependencies are // imported first. auto ImportCppType(Context& context, SemIR::LocId loc_id, clang::QualType type) -> TypeExpr; // Imports an overloaded function set from Clang to Carbon. auto ImportCppOverloadSet( Context& context, SemIR::LocId loc_id, SemIR::NameScopeId scope_id, SemIR::NameId name_id, clang::CXXRecordDecl* naming_class, clang::UnresolvedSet<4>&& overload_set, clang::OverloadCandidateSet::OperatorRewriteInfo operator_rewrite_info) -> SemIR::InstId; // Looks up the given name in the Clang AST generated when importing C++ code // and returns a lookup result. If using the injected class name (`X.X()`), // imports the class constructor as a function named as the class. auto ImportNameFromCpp(Context& context, SemIR::LocId loc_id, SemIR::NameScopeId scope_id, SemIR::NameId name_id) -> SemIR::ScopeLookupResult; // Given a Carbon class declaration that was imported from some kind of C++ // declaration, such as a class or enum, attempt to import a corresponding class // definition. Returns true if nothing went wrong (whether or not a definition // could be imported), false if a diagnostic was produced. auto ImportClassDefinitionForClangDecl(Context& context, SemIR::ClassId class_id, SemIR::ClangDeclId clang_decl_id) -> bool; // Gets the identifier info for a name. Returns `nullptr` if the name is not an // identifier name. auto GetClangIdentifierInfo(Context& context, SemIR::NameId name_id) -> clang::IdentifierInfo*; // Maps from a `VarStorage` instruction to a `clang::VarDecl`. Returns // null if the instruction is not a `VarStorage`, or if its contents // cannot be mapped to a `clang::VarDecl`. auto GetAsClangVarDecl(Context& context, SemIR::InstId inst_id) -> clang::VarDecl*; // Maps a Clang name to a Carbon `NameId`. auto AddIdentifierName(Context& context, llvm::StringRef name) -> SemIR::NameId; } // namespace Carbon::Check #endif // CARBON_TOOLCHAIN_CHECK_CPP_IMPORT_H_ ================================================ FILE: toolchain/check/cpp/location.cpp ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #include "toolchain/check/cpp/location.h" #include "toolchain/sem_ir/absolute_node_id.h" #include "toolchain/sem_ir/ids.h" namespace Carbon::Check { namespace { struct FileInfo { const SemIR::File* sem_ir; clang::SourceLocation start_loc; }; } // namespace // Map a CheckIRId into information about the corresponding file in both SemIR // and Clang's source manager. static auto GetFileInfo(Context& context, SemIR::CheckIRId ir_id) -> FileInfo { const SemIR::File* sem_ir = &context.sem_ir(); int file_index = 0; // If the file is imported, locate it in our imports map. if (ir_id != context.sem_ir().check_ir_id()) { auto import_id = context.check_ir_map().Get(ir_id); CARBON_CHECK(import_id.has_value()); file_index = import_id.index + 1; sem_ir = context.import_irs().Get(import_id).sem_ir; CARBON_CHECK(sem_ir, "Node location in nonexistent IR"); } // If we've seen this file before, reuse the same FileID. auto& file_start_locs = context.cpp_context()->carbon_file_locations(); if (static_cast(file_start_locs.size()) <= file_index) { // Never valid; prepare a slot for the caching below. file_start_locs.resize(file_index + 1); } else if (file_start_locs[file_index].isValid()) { return {.sem_ir = sem_ir, .start_loc = file_start_locs[file_index]}; } // We've not seen this file before. Create a corresponding virtual file in // Clang's source manager. // TODO: Consider recreating the complete import path instead of only the // final entry. const auto& source = sem_ir->parse_tree().tokens().source(); auto& src_mgr = context.ast_context().getSourceManager(); auto file_id = src_mgr.createFileID( llvm::MemoryBufferRef(source.text(), source.filename())); auto file_start_loc = src_mgr.getLocForStartOfFile(file_id); file_start_locs[file_index] = file_start_loc; return {.sem_ir = sem_ir, .start_loc = file_start_loc}; } auto GetCppLocation(Context& context, SemIR::LocId loc_id) -> clang::SourceLocation { if (!context.sem_ir().cpp_file()) { return clang::SourceLocation(); } // Break down the `LocId` into an import path. If that ends in a C++ location, // we can just return that directly. llvm::SmallVector absolute_node_ids = SemIR::GetAbsoluteNodeId(&context.sem_ir(), loc_id); if (absolute_node_ids.back().check_ir_id() == SemIR::CheckIRId::Cpp) { return context.sem_ir().clang_source_locs().Get( absolute_node_ids.back().clang_source_loc_id()); } // This is a location in Carbon code; get or create a corresponding file in // Clang and build a corresponding location. auto absolute_node_id = absolute_node_ids.back(); auto [ir, start_loc] = GetFileInfo(context, absolute_node_id.check_ir_id()); const auto& tree = ir->parse_tree(); auto offset = tree.tokens().GetByteOffset(tree.node_token(absolute_node_id.node_id())); return start_loc.getLocWithOffset(offset); } } // namespace Carbon::Check ================================================ FILE: toolchain/check/cpp/location.h ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #ifndef CARBON_TOOLCHAIN_CHECK_CPP_LOCATION_H_ #define CARBON_TOOLCHAIN_CHECK_CPP_LOCATION_H_ #include "toolchain/check/context.h" #include "toolchain/sem_ir/ids.h" namespace Carbon::Check { // Maps a Carbon source location into an equivalent Clang source location. auto GetCppLocation(Context& context, SemIR::LocId loc_id) -> clang::SourceLocation; } // namespace Carbon::Check #endif // CARBON_TOOLCHAIN_CHECK_CPP_LOCATION_H_ ================================================ FILE: toolchain/check/cpp/macros.cpp ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #include "toolchain/check/cpp/macros.h" #include "clang/AST/ASTContext.h" #include "clang/AST/Expr.h" #include "clang/Parse/Parser.h" #include "clang/Sema/Sema.h" #include "common/check.h" #include "toolchain/check/cpp/constant.h" #include "toolchain/check/cpp/import.h" #include "toolchain/check/literal.h" namespace Carbon::Check { // Maps a Clang literal expression to a Carbon constant. static auto MapConstant(Context& context, SemIR::LocId loc_id, clang::Expr* expr) -> SemIR::InstId { CARBON_CHECK(expr, "empty expression"); if (auto* string_literal = dyn_cast(expr)) { if (!string_literal->isOrdinary() && !string_literal->isUTF8()) { context.TODO(loc_id, llvm::formatv("Unsupported: string literal type: {0}", expr->getType())); return SemIR::ErrorInst::InstId; } StringLiteralValueId string_id = context.string_literal_values().Add(string_literal->getString()); auto inst_id = MakeStringLiteral(context, Parse::StringLiteralId::None, string_id); return inst_id; } else if (isa(expr)) { auto type_id = ImportCppType(context, loc_id, expr->getType()).type_id; return GetOrAddInst(context, SemIR::LocId::None, {.type_id = type_id}); } context.TODO(loc_id, llvm::formatv("Unsupported: C++ constant expression type: '{0}'", expr->getType().getAsString())); return SemIR::ErrorInst::InstId; } auto TryEvaluateMacro(Context& context, SemIR::LocId loc_id, SemIR::NameId name_id, clang::MacroInfo* macro_info) -> SemIR::InstId { auto name_str_opt = context.names().GetAsStringIfIdentifier(name_id); CARBON_CHECK(macro_info, "macro info missing"); if (macro_info->getNumTokens() == 0) { context.TODO(loc_id, "Unsupported: macro with 0 replacement tokens"); return SemIR::ErrorInst::InstId; } clang::Sema& sema = context.clang_sema(); clang::Preprocessor& preprocessor = sema.getPreprocessor(); auto& parser = context.cpp_context()->parser(); llvm::SmallVector tokens(macro_info->tokens().begin(), macro_info->tokens().end()); clang::Token current_token = parser.getCurToken(); // Add eof token clang::Token eof; eof.startToken(); eof.setKind(clang::tok::eof); eof.setLocation(current_token.getEndLoc()); tokens.push_back(eof); tokens.push_back(current_token); preprocessor.EnterTokenStream(tokens, /*DisableMacroExpansion=*/false, /*IsReinject=*/false); parser.ConsumeAnyToken(true); clang::ExprResult result = parser.ParseConstantExpression(); clang::Expr* result_expr = result.get(); bool success = !result.isInvalid() && parser.getCurToken().is(clang::tok::eof); if (!success) { parser.SkipUntil(clang::tok::eof); CARBON_DIAGNOSTIC( InCppMacroEvaluation, Error, "failed to parse macro Cpp.{0} to a valid constant expression", std::string); context.emitter().Emit(loc_id, InCppMacroEvaluation, (*name_str_opt).str()); return SemIR::ErrorInst::InstId; } result_expr = result_expr->IgnoreParenImpCasts(); if (isa(result_expr) || isa(result_expr)) { return MapConstant(context, loc_id, result_expr); } clang::Expr::EvalResult evaluated_result; if (!result_expr->EvaluateAsConstantExpr(evaluated_result, sema.getASTContext())) { CARBON_FATAL("failed to evaluate macro as constant expression"); } auto const_id = MapAPValueToConstant(context, loc_id, evaluated_result.Val, result_expr->getType(), /*is_lvalue=*/result_expr->isGLValue()); if (const_id == SemIR::ConstantId::NotConstant) { context.TODO(loc_id, "Unsupported: macro evaluated to a constant of type: " + result_expr->getType().getAsString()); return SemIR::ErrorInst::InstId; } return context.constant_values().GetInstId(const_id); } } // namespace Carbon::Check ================================================ FILE: toolchain/check/cpp/macros.h ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #ifndef CARBON_TOOLCHAIN_CHECK_CPP_MACROS_H_ #define CARBON_TOOLCHAIN_CHECK_CPP_MACROS_H_ #include "toolchain/check/context.h" namespace Carbon::Check { // Tries to evaluate the given macro. The macro will be evaluated as a // constant if possible. Returns an `InstId` on success or // `SemIR::ErrorInst::InstId` otherwise. auto TryEvaluateMacro(Context& context, SemIR::LocId loc_id, SemIR::NameId name_id, clang::MacroInfo* macro_info) -> SemIR::InstId; } // namespace Carbon::Check #endif // CARBON_TOOLCHAIN_CHECK_CPP_MACROS_H_ ================================================ FILE: toolchain/check/cpp/operators.cpp ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #include "toolchain/check/cpp/operators.h" #include "clang/Sema/Initialization.h" #include "clang/Sema/Overload.h" #include "clang/Sema/Sema.h" #include "toolchain/check/convert.h" #include "toolchain/check/core_identifier.h" #include "toolchain/check/cpp/import.h" #include "toolchain/check/cpp/location.h" #include "toolchain/check/cpp/overload_resolution.h" #include "toolchain/check/cpp/type_mapping.h" #include "toolchain/check/function.h" #include "toolchain/check/inst.h" #include "toolchain/check/type.h" #include "toolchain/check/type_completion.h" #include "toolchain/sem_ir/builtin_function_kind.h" #include "toolchain/sem_ir/cpp_initializer_list.h" #include "toolchain/sem_ir/ids.h" #include "toolchain/sem_ir/inst.h" #include "toolchain/sem_ir/typed_insts.h" namespace Carbon::Check { // Maps Carbon operator interface and operator names to Clang operator kinds. static auto GetClangOperatorKind(Context& context, SemIR::LocId loc_id, CoreIdentifier interface_name, CoreIdentifier op_name) -> std::optional { switch (interface_name) { // Unary operators. case CoreIdentifier::Destroy: case CoreIdentifier::As: case CoreIdentifier::ImplicitAs: case CoreIdentifier::UnsafeAs: case CoreIdentifier::Copy: { // TODO: Support destructors and conversions. return std::nullopt; } // Increment and decrement. case CoreIdentifier::Inc: { CARBON_CHECK(op_name == CoreIdentifier::Op); return clang::OO_PlusPlus; } case CoreIdentifier::Dec: { CARBON_CHECK(op_name == CoreIdentifier::Op); return clang::OO_MinusMinus; } // Arithmetic. case CoreIdentifier::Negate: { CARBON_CHECK(op_name == CoreIdentifier::Op); return clang::OO_Minus; } // Bitwise. case CoreIdentifier::BitComplement: { CARBON_CHECK(op_name == CoreIdentifier::Op); return clang::OO_Tilde; } // Binary operators. // Arithmetic operators. case CoreIdentifier::AddWith: { CARBON_CHECK(op_name == CoreIdentifier::Op); return clang::OO_Plus; } case CoreIdentifier::SubWith: { CARBON_CHECK(op_name == CoreIdentifier::Op); return clang::OO_Minus; } case CoreIdentifier::MulWith: { CARBON_CHECK(op_name == CoreIdentifier::Op); return clang::OO_Star; } case CoreIdentifier::DivWith: { CARBON_CHECK(op_name == CoreIdentifier::Op); return clang::OO_Slash; } case CoreIdentifier::ModWith: { CARBON_CHECK(op_name == CoreIdentifier::Op); return clang::OO_Percent; } // Bitwise operators. case CoreIdentifier::BitAndWith: { CARBON_CHECK(op_name == CoreIdentifier::Op); return clang::OO_Amp; } case CoreIdentifier::BitOrWith: { CARBON_CHECK(op_name == CoreIdentifier::Op); return clang::OO_Pipe; } case CoreIdentifier::BitXorWith: { CARBON_CHECK(op_name == CoreIdentifier::Op); return clang::OO_Caret; } case CoreIdentifier::LeftShiftWith: { CARBON_CHECK(op_name == CoreIdentifier::Op); return clang::OO_LessLess; } case CoreIdentifier::RightShiftWith: { CARBON_CHECK(op_name == CoreIdentifier::Op); return clang::OO_GreaterGreater; } // Assignment. case CoreIdentifier::AssignWith: { // TODO: This is not yet reached because we don't use the `AssignWith` // interface for assignment yet. CARBON_CHECK(op_name == CoreIdentifier::Op); return clang::OO_Equal; } // Compound assignment arithmetic operators. case CoreIdentifier::AddAssignWith: { CARBON_CHECK(op_name == CoreIdentifier::Op); return clang::OO_PlusEqual; } case CoreIdentifier::SubAssignWith: { CARBON_CHECK(op_name == CoreIdentifier::Op); return clang::OO_MinusEqual; } case CoreIdentifier::MulAssignWith: { CARBON_CHECK(op_name == CoreIdentifier::Op); return clang::OO_StarEqual; } case CoreIdentifier::DivAssignWith: { CARBON_CHECK(op_name == CoreIdentifier::Op); return clang::OO_SlashEqual; } case CoreIdentifier::ModAssignWith: { CARBON_CHECK(op_name == CoreIdentifier::Op); return clang::OO_PercentEqual; } // Compound assignment bitwise operators. case CoreIdentifier::BitAndAssignWith: { CARBON_CHECK(op_name == CoreIdentifier::Op); return clang::OO_AmpEqual; } case CoreIdentifier::BitOrAssignWith: { CARBON_CHECK(op_name == CoreIdentifier::Op); return clang::OO_PipeEqual; } case CoreIdentifier::BitXorAssignWith: { CARBON_CHECK(op_name == CoreIdentifier::Op); return clang::OO_CaretEqual; } case CoreIdentifier::LeftShiftAssignWith: { CARBON_CHECK(op_name == CoreIdentifier::Op); return clang::OO_LessLessEqual; } case CoreIdentifier::RightShiftAssignWith: { CARBON_CHECK(op_name == CoreIdentifier::Op); return clang::OO_GreaterGreaterEqual; } // Relational operators. case CoreIdentifier::EqWith: { if (op_name == CoreIdentifier::Equal) { return clang::OO_EqualEqual; } CARBON_CHECK(op_name == CoreIdentifier::NotEqual); return clang::OO_ExclaimEqual; } case CoreIdentifier::OrderedWith: { switch (op_name) { case CoreIdentifier::Less: return clang::OO_Less; case CoreIdentifier::Greater: return clang::OO_Greater; case CoreIdentifier::LessOrEquivalent: return clang::OO_LessEqual; case CoreIdentifier::GreaterOrEquivalent: return clang::OO_GreaterEqual; default: CARBON_FATAL("Unexpected OrderedWith op `{0}`", op_name); } } // Array indexing. case CoreIdentifier::IndexWith: { CARBON_CHECK(op_name == CoreIdentifier::At); return clang::OO_Subscript; } default: { context.TODO(loc_id, llvm::formatv("Unsupported operator interface `{0}`", interface_name)); return std::nullopt; } } } // Creates and returns a function that can be used to construct a // std::initializer_list from an array. // // TODO: This should ideally be implemented in Carbon code rather than by // synthesizing a function. // TODO: We should cache and reuse the generated function. static auto MakeCppStdInitializerListMake(Context& context, SemIR::LocId loc_id, clang::QualType init_list_type, int32_t size) -> SemIR::InstId { // Extract the element type `T` from the `std::initializer_list` type. clang::QualType element_type; bool is_std_initializer_list = context.clang_sema().isStdInitializerList(init_list_type, &element_type); CARBON_CHECK(is_std_initializer_list); auto element_type_inst_id = ImportCppType(context, loc_id, element_type).inst_id; if (element_type_inst_id == SemIR::ErrorInst::InstId) { return SemIR::ErrorInst::InstId; } // Import the `std::initializer_list` type and check we recognize its // layout. auto [init_list_type_inst_id, init_list_type_id] = ImportCppType(context, loc_id, init_list_type); if (init_list_type_id == SemIR::ErrorInst::TypeId) { return SemIR::ErrorInst::InstId; } auto layout = SemIR::GetStdInitializerListLayout(context.sem_ir(), init_list_type_id); if (layout.kind == SemIR::StdInitializerListLayout::None) { context.TODO(loc_id, "Unsupported layout for std::initializer_list"); return SemIR::ErrorInst::InstId; } auto init_list_class_id = context.sem_ir() .types() .GetAs(init_list_type_id) .class_id; auto& init_list_class = context.classes().Get(init_list_class_id); // Build the array type `T[size]` that we use as the parameter type. // TODO: This will eventually be called from impl lookup, possibly while // forming a specific, so we should not be adding instructions here. auto bound_id = AddInst( context, SemIR::LocIdAndInst( loc_id, SemIR::IntValue{ .type_id = GetSingletonType( context, SemIR::IntLiteralType::TypeInstId), .int_id = context.ints().Add(size)})); auto array_type_inst_id = AddTypeInst( context, SemIR::LocIdAndInst::UncheckedLoc( loc_id, SemIR::ArrayType{ .type_id = SemIR::TypeType::TypeId, .bound_id = bound_id, .element_type_inst_id = element_type_inst_id})); auto array_type_id = context.types().GetTypeIdForTypeInstId(array_type_inst_id); // Create a builtin function to perform the conversion from array type to // initializer list type. We name the synthesized function as if it were a // constructor of std::initializer_list. // TODO: Find a better way to handle this. Ideally we should stop using this // function entirely and declare the necessary builtin in the prelude. auto [decl_id, function_id] = MakeGeneratedFunctionDecl(context, loc_id, {.parent_scope_id = init_list_class.scope_id, .name_id = init_list_class.name_id, .param_type_ids = {array_type_id}, .return_type_id = init_list_type_id}); auto& function = context.functions().Get(function_id); CARBON_CHECK(IsValidBuiltinDeclaration( context, function, SemIR::BuiltinFunctionKind::CppStdInitializerListMake)); function.SetBuiltinFunction( SemIR::BuiltinFunctionKind::CppStdInitializerListMake); return decl_id; } // Returns information about the Carbon signature to import when importing a C++ // constructor or conversion operator. static auto GetConversionSignatureToImport( Context& context, SemIR::InstId source_id, clang::InitializationSequence::StepKind step_kind, clang::FunctionDecl* function_decl) -> SemIR::ClangDeclKey::Signature { // If we're performing a constructor initialization from a list, form a // function signature that takes a single tuple or struct pattern // instead of a function signature with one parameter per C++ parameter. if (step_kind == clang::InitializationSequence::SK_ConstructorInitializationFromList) { // The source type should always be a tuple type, because we don't support // C++ initialization from struct types. auto tuple_type = context.types().TryGetAs( context.insts().Get(source_id).type_id()); CARBON_CHECK(tuple_type, "List initialization from non-tuple type"); // Initialization from a tuple `(a, b, c)` results in a constructor // function that takes a tuple pattern: // // fn Class.Class((a: A, b: B, c: C)) -> Class; return { .kind = SemIR::ClangDeclKey::Signature::Kind::TuplePattern, .num_params = static_cast( context.inst_blocks().Get(tuple_type->type_elements_id).size())}; } // Any other initialization using a constructor is calling a converting // constructor: // // fn Class.Class(a: A) -> Class; if (isa(function_decl)) { return {.kind = SemIR::ClangDeclKey::Signature::Kind::Normal, .num_params = 1}; } // Otherwise, the initialization is calling a conversion function // `Source::operator Dest`: // // fn Source.[self: Source]() -> Dest; CARBON_CHECK(isa(function_decl)); return {.kind = SemIR::ClangDeclKey::Signature::Kind::Normal, .num_params = 0}; } static auto LookupCppConversion(Context& context, SemIR::LocId loc_id, SemIR::InstId source_id, SemIR::TypeId dest_type_id, bool allow_explicit) -> SemIR::InstId { if (context.types().Is( context.insts().Get(source_id).type_id())) { // Structs can only be used to initialize C++ aggregates. That case is // handled by Convert, not here. return SemIR::InstId::None; } auto dest_type = MapToCppType(context, dest_type_id); if (dest_type.isNull()) { return SemIR::InstId::None; } auto* arg_expr = InventClangArg(context, source_id); // If we can't map the argument, we can't perform the conversion. if (!arg_expr) { return SemIR::InstId::None; } auto loc = GetCppLocation(context, loc_id); // Form a Clang initialization sequence. auto& sema = context.clang_sema(); clang::InitializedEntity entity = clang::InitializedEntity::InitializeTemporary(dest_type); clang::InitializationKind kind = allow_explicit ? clang::InitializationKind::CreateDirect( loc, /*LParenLoc=*/clang::SourceLocation(), /*RParenLoc=*/clang::SourceLocation()) : clang::InitializationKind::CreateCopy( loc, /*EqualLoc=*/clang::SourceLocation()); clang::MultiExprArg args(arg_expr); // `(a, b) as T` uses `T{a, b}`, not `T({a, b})`. The latter would introduce // a redundant extra copy. // TODO: We need to communicate this back to the caller so they know to call // the constructor with an exploded argument list somehow. if (allow_explicit && isa(arg_expr)) { kind = clang::InitializationKind::CreateDirectList(loc); } clang::InitializationSequence init(sema, entity, kind, args); if (init.Failed()) { // TODO: Are there initialization failures that we should translate into // errors rather than a missing conversion? return SemIR::InstId::None; } // Scan the steps looking for user-defined conversions. For now we just find // and return the first such conversion function. We skip over standard // conversions; we'll perform those using the Carbon rules as part of calling // the C++ conversion function. for (const auto& step : init.steps()) { switch (step.Kind) { case clang::InitializationSequence::SK_UserConversion: case clang::InitializationSequence::SK_ConstructorInitialization: case clang::InitializationSequence::SK_StdInitializerListConstructorCall: case clang::InitializationSequence:: SK_ConstructorInitializationFromList: { if (auto* ctor = dyn_cast(step.Function.Function); ctor && ctor->isCopyOrMoveConstructor()) { // Skip copy / move constructor calls. They shouldn't be performed // this way because they're not considered conversions in Carbon, and // will frequently lead to infinite recursion because we'll end up // back here when attempting to convert the argument. continue; } if (sema.DiagnoseUseOfOverloadedDecl(step.Function.Function, loc)) { return SemIR::ErrorInst::InstId; } sema.MarkFunctionReferenced(loc, step.Function.Function); auto signature = GetConversionSignatureToImport( context, source_id, step.Kind, step.Function.Function); auto result_id = ImportCppFunctionDecl( context, loc_id, step.Function.Function, signature); if (auto fn_decl = context.insts().TryGetAsWithId( result_id)) { CheckCppOverloadAccess(context, loc_id, step.Function.FoundDecl, fn_decl->inst_id); } else { CARBON_CHECK(result_id == SemIR::ErrorInst::InstId); } // TODO: There may be other conversions later in the sequence that we // need to model; we've only applied the first one here. return result_id; } case clang::InitializationSequence::SK_StdInitializerList: { return MakeCppStdInitializerListMake( context, loc_id, step.Type, cast(arg_expr)->getNumInits()); } case clang::InitializationSequence::SK_ListInitialization: { // Aggregate initialization is handled by the normal Carbon conversion // logic, so we ignore it here. // TODO: So far we only support aggregate initialization for arrays and // empty classes. continue; } case clang::InitializationSequence::SK_ConversionSequence: case clang::InitializationSequence::SK_ConversionSequenceNoNarrowing: { // Implicit conversions are handled by the normal Carbon conversion // logic, so we ignore them here. continue; } default: { // TODO: Handle other kinds of initialization steps. For now we assume // they will be handled by our function call logic and we can skip them. RawStringOstream os; os << "Unsupported initialization sequence:\n"; init.dump(os); context.TODO(loc_id, os.TakeStr()); return SemIR::ErrorInst::InstId; } } } return SemIR::InstId::None; } auto LookupCppOperator(Context& context, SemIR::LocId loc_id, Operator op, llvm::ArrayRef arg_ids) -> SemIR::InstId { // Register an annotation scope to flush any Clang diagnostics when we return. // This is important to ensure that Clang diagnostics are properly interleaved // with Carbon diagnostics. Diagnostics::AnnotationScope annotate_diagnostics(&context.emitter(), [](auto& /*builder*/) {}); // Handle `ImplicitAs` and `As`. if (op.interface_name == CoreIdentifier::ImplicitAs || op.interface_name == CoreIdentifier::As) { if (op.interface_args_ref.size() != 1 || arg_ids.size() != 1) { return SemIR::InstId::None; } // The argument is the destination type for both interfaces. auto dest_const_id = context.constant_values().Get(op.interface_args_ref[0]); auto dest_type_id = context.types().TryGetTypeIdForTypeConstantId(dest_const_id); if (!dest_type_id.has_value()) { return SemIR::InstId::None; } return LookupCppConversion( context, loc_id, arg_ids[0], dest_type_id, /*allow_explicit=*/op.interface_name == CoreIdentifier::As); } auto op_kind = GetClangOperatorKind(context, loc_id, op.interface_name, op.op_name); if (!op_kind) { return SemIR::InstId::None; } // Make sure all operands are complete before lookup. for (SemIR::InstId arg_id : arg_ids) { SemIR::TypeId arg_type_id = context.insts().Get(arg_id).type_id(); if (!RequireCompleteType(context, arg_type_id, loc_id, [&](auto& builder) { CARBON_DIAGNOSTIC( IncompleteOperandTypeInCppOperatorLookup, Context, "looking up a C++ operator with incomplete operand type {0}", SemIR::TypeId); builder.Context(loc_id, IncompleteOperandTypeInCppOperatorLookup, arg_type_id); })) { return SemIR::ErrorInst::InstId; } } auto maybe_arg_exprs = InventClangArgs(context, arg_ids); if (!maybe_arg_exprs.has_value()) { return SemIR::ErrorInst::InstId; } auto& arg_exprs = *maybe_arg_exprs; clang::SourceLocation loc = GetCppLocation(context, loc_id); clang::OverloadCandidateSet::OperatorRewriteInfo operator_rewrite_info( *op_kind, loc, /*AllowRewritten=*/true); clang::OverloadCandidateSet candidate_set( loc, clang::OverloadCandidateSet::CSK_Operator, operator_rewrite_info); clang::Sema& sema = context.clang_sema(); // This works for both unary and binary operators. sema.LookupOverloadedBinOp(candidate_set, *op_kind, clang::UnresolvedSet<0>{}, arg_exprs); clang::OverloadCandidateSet::iterator best_viable_fn; switch (candidate_set.BestViableFunction(sema, loc, best_viable_fn)) { case clang::OverloadingResult::OR_Success: { if (!best_viable_fn->Function) { // The best viable candidate was a builtin. Let the Carbon operator // machinery handle that. return SemIR::InstId::None; } if (best_viable_fn->RewriteKind) { context.TODO( loc_id, llvm::formatv("Rewriting operator{0} using {1} is not supported", clang::getOperatorSpelling( candidate_set.getRewriteInfo().OriginalOperator), best_viable_fn->Function->getNameAsString())); return SemIR::ErrorInst::InstId; } sema.MarkFunctionReferenced(loc, best_viable_fn->Function); // If this is an operator method, the first arg will be used as self. int32_t num_params = arg_ids.size(); if (isa(best_viable_fn->Function)) { --num_params; } auto result_id = ImportCppFunctionDecl(context, loc_id, best_viable_fn->Function, {.num_params = num_params}); if (result_id != SemIR::ErrorInst::InstId) { CheckCppOverloadAccess( context, loc_id, best_viable_fn->FoundDecl, context.insts().GetAsKnownInstId(result_id)); } return result_id; } case clang::OverloadingResult::OR_No_Viable_Function: { // OK, didn't find a viable C++ candidate, but this is not an error, as // there might be a Carbon candidate. return SemIR::InstId::None; } case clang::OverloadingResult::OR_Ambiguous: { const char* spelling = clang::getOperatorSpelling(*op_kind); candidate_set.NoteCandidates( clang::PartialDiagnosticAt( loc, sema.PDiag(clang::diag::err_ovl_ambiguous_oper_binary) << spelling << arg_exprs[0]->getType() << arg_exprs[1]->getType()), sema, clang::OCD_AmbiguousCandidates, arg_exprs, spelling, loc); return SemIR::ErrorInst::InstId; } case clang::OverloadingResult::OR_Deleted: const char* spelling = clang::getOperatorSpelling(*op_kind); auto* message = best_viable_fn->Function->getDeletedMessage(); // The best viable function might be a different operator if the best // candidate is a rewritten candidate, so use the operator kind of the // candidate itself in the diagnostic. candidate_set.NoteCandidates( clang::PartialDiagnosticAt( loc, sema.PDiag(clang::diag::err_ovl_deleted_oper) << clang::getOperatorSpelling( best_viable_fn->Function->getOverloadedOperator()) << (message != nullptr) << (message ? message->getString() : llvm::StringRef())), sema, clang::OCD_AllCandidates, arg_exprs, spelling, loc); return SemIR::ErrorInst::InstId; } } auto IsCppOperatorMethodDecl(clang::Decl* decl) -> bool { auto* clang_method_decl = dyn_cast(decl); return clang_method_decl && (clang_method_decl->isOverloadedOperator() || isa(clang_method_decl)); } static auto GetAsCppFunctionDecl(Context& context, SemIR::InstId inst_id) -> clang::FunctionDecl* { if (inst_id == SemIR::InstId::None) { return nullptr; } auto function_type = context.types().TryGetAs( context.insts().Get(inst_id).type_id()); if (!function_type) { return nullptr; } SemIR::ClangDeclId clang_decl_id = context.functions().Get(function_type->function_id).clang_decl_id; return clang_decl_id.has_value() ? dyn_cast( context.clang_decls().Get(clang_decl_id).key.decl) : nullptr; } auto IsCppOperatorMethod(Context& context, SemIR::InstId inst_id) -> bool { auto* function_decl = GetAsCppFunctionDecl(context, inst_id); return function_decl && IsCppOperatorMethodDecl(function_decl); } auto IsCppConstructorOrNonMethodOperator(Context& context, SemIR::InstId inst_id) -> bool { auto* function_decl = GetAsCppFunctionDecl(context, inst_id); if (!function_decl) { return false; } if (isa(function_decl)) { return true; } return !isa(function_decl) && function_decl->isOverloadedOperator(); } } // namespace Carbon::Check ================================================ FILE: toolchain/check/cpp/operators.h ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #ifndef CARBON_TOOLCHAIN_CHECK_CPP_OPERATORS_H_ #define CARBON_TOOLCHAIN_CHECK_CPP_OPERATORS_H_ #include "toolchain/check/context.h" #include "toolchain/check/operator.h" #include "toolchain/sem_ir/ids.h" namespace Carbon::Check { // Looks up the given operator in the Clang AST generated when importing C++ // code using argument dependent lookup (ADL) and return overload set // instruction. auto LookupCppOperator(Context& context, SemIR::LocId loc_id, Operator op, llvm::ArrayRef arg_ids) -> SemIR::InstId; // Returns whether the decl is an operator member function. auto IsCppOperatorMethodDecl(clang::Decl* decl) -> bool; // Returns whether the specified instruction refers to a C++ overloaded operator // that is a method. If so, the first operand will be passed as `self` rather // than as the first argument. auto IsCppOperatorMethod(Context& context, SemIR::InstId inst_id) -> bool; // Returns whether the specified instruction refers to a C++ constructor or // non-operator method. If so, when mapping from a Carbon interface to a C++ // call, we pass a `self` parameter as the first argument instead. auto IsCppConstructorOrNonMethodOperator(Context& context, SemIR::InstId inst_id) -> bool; } // namespace Carbon::Check #endif // CARBON_TOOLCHAIN_CHECK_CPP_OPERATORS_H_ ================================================ FILE: toolchain/check/cpp/overload_resolution.cpp ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #include "toolchain/check/cpp/overload_resolution.h" #include "clang/Basic/DiagnosticSema.h" #include "clang/Sema/Overload.h" #include "clang/Sema/Sema.h" #include "toolchain/base/kind_switch.h" #include "toolchain/check/cpp/access.h" #include "toolchain/check/cpp/call.h" #include "toolchain/check/cpp/import.h" #include "toolchain/check/cpp/location.h" #include "toolchain/check/cpp/operators.h" #include "toolchain/check/cpp/type_mapping.h" #include "toolchain/check/member_access.h" #include "toolchain/check/name_lookup.h" #include "toolchain/diagnostics/emitter.h" #include "toolchain/sem_ir/function.h" #include "toolchain/sem_ir/ids.h" #include "toolchain/sem_ir/name_scope.h" #include "toolchain/sem_ir/typed_insts.h" namespace Carbon::Check { // Map a Carbon name into a C++ name. static auto GetCppName(Context& context, SemIR::NameId name_id) -> clang::DeclarationName { // TODO: Some special names should probably use different formatting. In // particular, NameId::CppOperator should probably map back to a // CXXOperatorName. auto name_str = context.names().GetFormatted(name_id); return clang::DeclarationName(&context.ast_context().Idents.get(name_str)); } // Adds the given overload candidates to the candidate set. static auto AddOverloadCandidates( Context& context, clang::OverloadCandidateSet& candidate_set, const clang::UnresolvedSet<4>& functions, llvm::ArrayRef template_arg_ids, clang::Expr* self_arg, llvm::ArrayRef args) -> void { clang::Sema& sema = context.clang_sema(); constexpr bool SuppressUserConversions = false; constexpr bool PartialOverloading = false; for (auto found_decl : functions.pairs()) { auto* decl = found_decl->getUnderlyingDecl(); // Form an explicit template argument list if needed. Note that this is done // per-candidate, as the conversions performed on the template arguments // differ based on the corresponding template parameters. auto* template_decl = dyn_cast(decl); clang::TemplateArgumentListInfo explicit_template_arg_storage; clang::TemplateArgumentListInfo* explicit_template_args = nullptr; if (!template_arg_ids.empty()) { if (!template_decl) { continue; } if (!ConvertArgsToTemplateArgs(context, template_decl, template_arg_ids, explicit_template_arg_storage, /*diagnose=*/false)) { continue; } explicit_template_args = &explicit_template_arg_storage; } auto* fn_decl = template_decl ? template_decl->getTemplatedDecl() : cast(decl); auto* method_decl = dyn_cast(fn_decl); if (method_decl && !method_decl->isStatic() && !isa(fn_decl)) { clang::QualType self_type; clang::Expr::Classification self_classification; if (self_arg) { self_type = self_arg->getType(); self_classification = self_arg->Classify(sema.Context); } if (template_decl) { sema.AddMethodTemplateCandidate( template_decl, found_decl, cast(template_decl->getDeclContext()), explicit_template_args, self_type, self_classification, args, candidate_set, SuppressUserConversions, PartialOverloading); } else if (method_decl->isOverloadedOperator()) { sema.AddMemberOperatorCandidates(method_decl->getOverloadedOperator(), candidate_set.getLocation(), args, candidate_set); } else { sema.AddMethodCandidate(method_decl, found_decl, method_decl->getParent(), self_type, self_classification, args, candidate_set, SuppressUserConversions, PartialOverloading); } } else if (template_decl) { sema.AddTemplateOverloadCandidate( template_decl, found_decl, explicit_template_args, args, candidate_set, SuppressUserConversions, PartialOverloading); } else { sema.AddOverloadCandidate(fn_decl, found_decl, args, candidate_set, SuppressUserConversions, PartialOverloading); } } } auto CheckCppOverloadAccess( Context& context, SemIR::LocId loc_id, clang::DeclAccessPair overload, SemIR::KnownInstId overload_inst_id, SemIR::NameScopeId parent_scope_id) -> void { SemIR::AccessKind member_access_kind = MapCppAccess(overload); if (member_access_kind == SemIR::AccessKind::Public) { return; } auto function_id = context.insts().Get(overload_inst_id).function_id; auto& function = context.functions().Get(function_id); if (!parent_scope_id.has_value()) { parent_scope_id = function.parent_scope_id; } auto name_scope_const_id = context.constant_values().Get( context.name_scopes().Get(parent_scope_id).inst_id()); SemIR::AccessKind allowed_access_kind = GetHighestAllowedAccess(context, loc_id, name_scope_const_id); CheckAccess(context, loc_id, SemIR::LocId(overload_inst_id), function.name_id, member_access_kind, /*is_parent_access=*/false, {.constant_id = name_scope_const_id, .highest_allowed_access = allowed_access_kind}); } auto PerformCppOverloadResolution( Context& context, SemIR::LocId loc_id, const SemIR::CppOverloadSet& overload_set, llvm::ArrayRef template_arg_ids, SemIR::InstId self_id, llvm::ArrayRef arg_ids) -> SemIR::InstId { // Register an annotation scope to flush any Clang diagnostics when we return. // This is important to ensure that Clang diagnostics are properly interleaved // with Carbon diagnostics. Diagnostics::AnnotationScope annotate_diagnostics(&context.emitter(), [](auto& /*builder*/) {}); // Map Carbon call argument types to C++ types. clang::Expr* self_expr = nullptr; if (self_id.has_value()) { self_expr = InventClangArg(context, self_id); if (!self_expr) { return SemIR::ErrorInst::InstId; } } auto maybe_arg_exprs = InventClangArgs(context, arg_ids); if (!maybe_arg_exprs.has_value()) { return SemIR::ErrorInst::InstId; } auto& arg_exprs = *maybe_arg_exprs; clang::SourceLocation loc = GetCppLocation(context, loc_id); // Add candidate functions from the name lookup. clang::OverloadCandidateSet candidate_set( loc, overload_set.operator_rewrite_info.OriginalOperator ? clang::OverloadCandidateSet::CandidateSetKind::CSK_Operator : clang::OverloadCandidateSet::CandidateSetKind::CSK_Normal, overload_set.operator_rewrite_info); AddOverloadCandidates(context, candidate_set, overload_set.candidate_functions, template_arg_ids, self_expr, arg_exprs); // Find best viable function among the candidates. clang::Sema& sema = context.clang_sema(); clang::OverloadCandidateSet::iterator best_viable_fn; clang::OverloadingResult overloading_result = candidate_set.BestViableFunction(sema, loc, best_viable_fn); switch (overloading_result) { case clang::OverloadingResult::OR_Success: { CARBON_CHECK(best_viable_fn->Function); CARBON_CHECK(!best_viable_fn->RewriteKind); SemIR::InstId result_id = ImportCppFunctionDecl( context, loc_id, best_viable_fn->Function, {.num_params = static_cast(arg_exprs.size())}); if (result_id != SemIR::ErrorInst::InstId) { CheckCppOverloadAccess( context, loc_id, best_viable_fn->FoundDecl, context.insts().GetAsKnownInstId(result_id), overload_set.parent_scope_id); } return result_id; } case clang::OverloadingResult::OR_No_Viable_Function: { candidate_set.NoteCandidates( clang::PartialDiagnosticAt( loc, sema.PDiag(clang::diag::err_ovl_no_viable_function_in_call) << GetCppName(context, overload_set.name_id)), sema, clang::OCD_AllCandidates, arg_exprs); return SemIR::ErrorInst::InstId; } case clang::OverloadingResult::OR_Ambiguous: { candidate_set.NoteCandidates( clang::PartialDiagnosticAt( loc, sema.PDiag(clang::diag::err_ovl_ambiguous_call) << GetCppName(context, overload_set.name_id)), sema, clang::OCD_AmbiguousCandidates, arg_exprs); return SemIR::ErrorInst::InstId; } case clang::OverloadingResult::OR_Deleted: { sema.DiagnoseUseOfDeletedFunction( loc, clang::SourceRange(loc, loc), GetCppName(context, overload_set.name_id), candidate_set, best_viable_fn->Function, arg_exprs); return SemIR::ErrorInst::InstId; } } } } // namespace Carbon::Check ================================================ FILE: toolchain/check/cpp/overload_resolution.h ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #ifndef CARBON_TOOLCHAIN_CHECK_CPP_OVERLOAD_RESOLUTION_H_ #define CARBON_TOOLCHAIN_CHECK_CPP_OVERLOAD_RESOLUTION_H_ #include "toolchain/check/context.h" #include "toolchain/sem_ir/function.h" #include "toolchain/sem_ir/ids.h" namespace Carbon::Check { // Checks whether a selected overload is accessible and diagnoses if not. // `parent_scope_id`, if specified, describes the scope that was named to find // the overload. If unspecified, we assume the overload was found in the class // that it is a direct member of, rather than a derived class. auto CheckCppOverloadAccess( Context& context, SemIR::LocId loc_id, clang::DeclAccessPair overload, SemIR::KnownInstId overload_inst_id, SemIR::NameScopeId parent_scope_id = SemIR::NameScopeId::None) -> void; // Resolves which function to call using Clang overload resolution. Returns an // instruction referring to that function, or an error instruction if overload // resolution failed. // // A set with a single non-templated function goes through the same rules for // overload resolution. This is to make sure that calls that have no viable // implicit conversion sequence are rejected even when an implicit conversion is // possible. Keeping the same behavior here for consistency and supporting // migrations so that the migrated callers from C++ remain valid. auto PerformCppOverloadResolution( Context& context, SemIR::LocId loc_id, const SemIR::CppOverloadSet& overload_set, llvm::ArrayRef template_arg_ids, SemIR::InstId self_id, llvm::ArrayRef arg_ids) -> SemIR::InstId; } // namespace Carbon::Check #endif // CARBON_TOOLCHAIN_CHECK_CPP_OVERLOAD_RESOLUTION_H_ ================================================ FILE: toolchain/check/cpp/thunk.cpp ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #include "toolchain/check/cpp/thunk.h" #include "clang/AST/ASTConsumer.h" #include "clang/AST/GlobalDecl.h" #include "clang/AST/Mangle.h" #include "clang/Sema/Lookup.h" #include "clang/Sema/Overload.h" #include "clang/Sema/Sema.h" #include "toolchain/check/call.h" #include "toolchain/check/context.h" #include "toolchain/check/control_flow.h" #include "toolchain/check/convert.h" #include "toolchain/check/literal.h" #include "toolchain/check/type.h" #include "toolchain/check/type_completion.h" #include "toolchain/sem_ir/function.h" #include "toolchain/sem_ir/ids.h" #include "toolchain/sem_ir/typed_insts.h" namespace Carbon::Check { // Returns the GlobalDecl to use to represent the given function declaration. // TODO: Refactor with `Lower::CreateGlobalDecl`. static auto GetGlobalDecl(const clang::FunctionDecl* decl) -> clang::GlobalDecl { if (const auto* ctor = dyn_cast(decl)) { return clang::GlobalDecl(ctor, clang::CXXCtorType::Ctor_Complete); } if (const auto* dtor = dyn_cast(decl)) { return clang::GlobalDecl(dtor, clang::CXXDtorType::Dtor_Complete); } return clang::GlobalDecl(decl); } // Returns the C++ thunk mangled name given the callee function. static auto GenerateThunkMangledName( clang::MangleContext& mangle_context, const clang::FunctionDecl& callee_function_decl, SemIR::ClangDeclKey::Signature::Kind signature_kind, int num_params) -> std::string { RawStringOstream mangled_name_stream; mangle_context.mangleName(GetGlobalDecl(&callee_function_decl), mangled_name_stream); switch (signature_kind) { case SemIR::ClangDeclKey::Signature::Normal: mangled_name_stream << ".carbon_thunk"; break; case SemIR::ClangDeclKey::Signature::TuplePattern: mangled_name_stream << ".carbon_thunk_tuple"; break; } if (num_params != static_cast(callee_function_decl.getNumNonObjectParams())) { mangled_name_stream << num_params; } return mangled_name_stream.TakeStr(); } // Returns whether the Carbon lowering for a parameter or return of this type is // known to match the C++ lowering. static auto IsSimpleAbiType(clang::ASTContext& ast_context, clang::QualType type, bool for_parameter) -> bool { if (type->isVoidType() || type->isPointerType()) { return true; } if (type->isReferenceType()) { if (for_parameter) { // A reference parameter has a simple ABI if it's a non-const lvalue // reference. Otherwise, we map it to pass-by-value, and it's only simple // if the type uses a pointer value representation. // // TODO: Check whether the pointee type maps to a Carbon type that uses a // pointer value representation, and treat it as simple if so. return type->isLValueReferenceType() && !type->getPointeeType().isConstQualified(); } // A reference return type is always mapped to a Carbon pointer, which uses // the same ABI rule as a C++ reference. return true; } if (const auto* enum_decl = type->getAsEnumDecl()) { // An enum type has a simple ABI if its underlying type does. type = enum_decl->getIntegerType(); if (type.isNull()) { return false; } } if (const auto* builtin_type = type->getAs()) { if (builtin_type->isIntegerType()) { uint64_t type_size = ast_context.getIntWidth(type); return type_size == 32 || type_size == 64; } } return false; } namespace { // Information about the callee of a thunk. struct CalleeFunctionInfo { explicit CalleeFunctionInfo(clang::FunctionDecl* decl, SemIR::ClangDeclKey::Signature signature) : decl(decl), signature_kind(signature.kind), num_params(signature.num_params + decl->hasCXXExplicitFunctionObjectParameter()) { auto& ast_context = decl->getASTContext(); const auto* method_decl = dyn_cast(decl); bool is_ctor = isa(decl); has_object_parameter = method_decl && !method_decl->isStatic() && !is_ctor; if (has_object_parameter && method_decl->isImplicitObjectMemberFunction()) { implicit_object_parameter_type = method_decl->getFunctionObjectParameterReferenceType(); } effective_return_type = is_ctor ? ast_context.getCanonicalTagType(method_decl->getParent()) : decl->getReturnType(); has_simple_return_type = IsSimpleAbiType(ast_context, effective_return_type, /*for_parameter=*/false); } // Returns whether this callee has an implicit `this` parameter. auto has_implicit_object_parameter() const -> bool { return !implicit_object_parameter_type.isNull(); } // Returns whether this callee has an explicit `this` parameter. auto has_explicit_object_parameter() const -> bool { return has_object_parameter && !has_implicit_object_parameter(); } // Returns the number of parameters the thunk should have. auto num_thunk_params() const -> unsigned { return has_implicit_object_parameter() + num_params + !has_simple_return_type; } // Returns the thunk parameter index corresponding to a given callee parameter // index. auto GetThunkParamIndex(unsigned callee_param_index) const -> unsigned { return has_implicit_object_parameter() + callee_param_index; } // Returns the thunk parameter index corresponding to the parameter that holds // the address of the return value. auto GetThunkReturnParamIndex() const -> unsigned { CARBON_CHECK(!has_simple_return_type); return has_implicit_object_parameter() + num_params; } // The callee function. clang::FunctionDecl* decl; // The kind of function signature being imported. SemIR::ClangDeclKey::Signature::Kind signature_kind; // The number of explicit parameters to import. This may be less than the // number of parameters that the function has if default arguments are being // used. int num_params; // Whether the callee has an object parameter, which might be explicit or // implicit. bool has_object_parameter; // If the callee has an implicit object parameter, the type of that parameter, // which will always be a reference type. Otherwise a null type. clang::QualType implicit_object_parameter_type; // The return type that the callee has when viewed from Carbon. This is the // C++ return type, except that constructors return the class type in Carbon // and return void in Clang's AST. clang::QualType effective_return_type; // Whether the callee has a simple return type, that we can return directly. // If not, we'll return through an out parameter instead. bool has_simple_return_type; }; } // namespace auto IsCppThunkRequired(Context& context, const SemIR::Function& function) -> bool { if (!function.clang_decl_id.has_value()) { return false; } const auto& decl_info = context.clang_decls().Get(function.clang_decl_id); auto* decl = cast(decl_info.key.decl); if (decl_info.key.signature.kind != SemIR::ClangDeclKey::Signature::Normal || decl_info.key.signature.num_params != static_cast(decl->getNumNonObjectParams())) { // We require a thunk if the number of parameters we want isn't all of them. // This happens if default arguments are in use, or (eventually) when // calling a varargs function. return true; } CalleeFunctionInfo callee_info(decl, decl_info.key.signature); if (!callee_info.has_simple_return_type) { return true; } auto& ast_context = context.ast_context(); if (callee_info.has_implicit_object_parameter() && !IsSimpleAbiType(ast_context, callee_info.implicit_object_parameter_type, /*for_parameter=*/true)) { return true; } const auto* function_type = decl->getType()->castAs(); for (int i : llvm::seq(decl->getNumParams())) { if (!IsSimpleAbiType(ast_context, function_type->getParamType(i), /*for_parameter=*/true)) { return true; } } return false; } // Given a pointer type, returns the corresponding _Nonnull-qualified pointer // type. static auto GetNonnullType(clang::ASTContext& ast_context, clang::QualType pointer_type) -> clang::QualType { return ast_context.getAttributedType(clang::NullabilityKind::NonNull, pointer_type, pointer_type); } // Given a type, returns the corresponding _Nonnull-qualified pointer type, // ignoring references. static auto GetNonNullablePointerType(clang::ASTContext& ast_context, clang::QualType type) { return GetNonnullType(ast_context, ast_context.getPointerType(type.getNonReferenceType())); } // Given the type of a callee parameter, returns the type to use for the // corresponding thunk parameter. static auto GetThunkParameterType(clang::ASTContext& ast_context, clang::QualType callee_type) -> clang::QualType { if (IsSimpleAbiType(ast_context, callee_type, /*for_parameter=*/true)) { return callee_type; } return GetNonNullablePointerType(ast_context, callee_type); } // Creates the thunk parameter types given the callee function. static auto BuildThunkParameterTypes(clang::ASTContext& ast_context, CalleeFunctionInfo callee_info) -> llvm::SmallVector { llvm::SmallVector thunk_param_types; thunk_param_types.reserve(callee_info.num_thunk_params()); if (callee_info.has_implicit_object_parameter()) { thunk_param_types.push_back(callee_info.implicit_object_parameter_type); } const auto* function_type = callee_info.decl->getType()->castAs(); for (int i : llvm::seq(callee_info.num_params)) { thunk_param_types.push_back( GetThunkParameterType(ast_context, function_type->getParamType(i))); } if (!callee_info.has_simple_return_type) { thunk_param_types.push_back(GetNonNullablePointerType( ast_context, callee_info.effective_return_type)); } CARBON_CHECK(thunk_param_types.size() == callee_info.num_thunk_params()); return thunk_param_types; } // Returns the thunk parameters using the callee function parameter identifiers. static auto BuildThunkParameters(clang::ASTContext& ast_context, CalleeFunctionInfo callee_info, clang::FunctionDecl* thunk_function_decl) -> llvm::SmallVector { clang::SourceLocation clang_loc = callee_info.decl->getLocation(); const auto* thunk_function_proto_type = thunk_function_decl->getType()->castAs(); llvm::SmallVector thunk_params; unsigned num_thunk_params = thunk_function_decl->getNumParams(); thunk_params.reserve(num_thunk_params); if (callee_info.has_implicit_object_parameter()) { clang::ParmVarDecl* thunk_param = clang::ParmVarDecl::Create(ast_context, thunk_function_decl, clang_loc, clang_loc, &ast_context.Idents.get("this"), thunk_function_proto_type->getParamType(0), nullptr, clang::SC_None, nullptr); thunk_params.push_back(thunk_param); } for (int i : llvm::seq(callee_info.num_params)) { clang::ParmVarDecl* thunk_param = clang::ParmVarDecl::Create( ast_context, thunk_function_decl, clang_loc, clang_loc, callee_info.decl->getParamDecl(i)->getIdentifier(), thunk_function_proto_type->getParamType( callee_info.GetThunkParamIndex(i)), nullptr, clang::SC_None, nullptr); thunk_params.push_back(thunk_param); } if (!callee_info.has_simple_return_type) { clang::ParmVarDecl* thunk_param = clang::ParmVarDecl::Create(ast_context, thunk_function_decl, clang_loc, clang_loc, &ast_context.Idents.get("return"), thunk_function_proto_type->getParamType( callee_info.GetThunkReturnParamIndex()), nullptr, clang::SC_None, nullptr); thunk_params.push_back(thunk_param); } CARBON_CHECK(thunk_params.size() == num_thunk_params); return thunk_params; } // Computes a name to use for a thunk, based on the name of the thunk's target. // The actual name used isn't critical, since it doesn't show up much except in // AST dumps and SemIR output, but we try to produce a valid C++ identifier. static auto GetDeclNameForThunk(clang::ASTContext& ast_context, clang::DeclarationName name) -> clang::DeclarationName { llvm::SmallString<64> thunk_name; switch (name.getNameKind()) { case clang::DeclarationName::NameKind::Identifier: { thunk_name = name.getAsIdentifierInfo()->getName(); break; } case clang::DeclarationName::NameKind::CXXOperatorName: { thunk_name = "operator_"; switch (name.getCXXOverloadedOperator()) { case clang::OO_None: case clang::NUM_OVERLOADED_OPERATORS: break; #define OVERLOADED_OPERATOR(Name, Spelling, Token, Unary, Binary, MemberOnly) \ case clang::OO_##Name: \ thunk_name += #Name; \ break; #include "clang/Basic/OperatorKinds.def" } break; } default: { break; } } if (auto type = name.getCXXNameType(); !type.isNull()) { if (auto* class_decl = type->getAsCXXRecordDecl()) { thunk_name += class_decl->getName(); } } thunk_name += "__carbon_thunk"; return &ast_context.Idents.get(thunk_name); } // Returns the thunk function declaration given the callee function and the // thunk parameter types. static auto CreateThunkFunctionDecl( Context& context, CalleeFunctionInfo callee_info, llvm::ArrayRef thunk_param_types) -> clang::FunctionDecl* { clang::ASTContext& ast_context = context.ast_context(); clang::SourceLocation clang_loc = callee_info.decl->getLocation(); clang::DeclarationName name = GetDeclNameForThunk(ast_context, callee_info.decl->getDeclName()); auto ext_proto_info = clang::FunctionProtoType::ExtProtoInfo(); clang::QualType thunk_function_type = ast_context.getFunctionType( callee_info.has_simple_return_type ? callee_info.effective_return_type : ast_context.VoidTy, thunk_param_types, ext_proto_info); clang::DeclContext* decl_context = ast_context.getTranslationUnitDecl(); // TODO: Thunks should not have external linkage, consider using `SC_Static`. clang::FunctionDecl* thunk_function_decl = clang::FunctionDecl::Create(ast_context, decl_context, clang_loc, clang_loc, name, thunk_function_type, /*TInfo=*/nullptr, clang::SC_Extern); decl_context->addDecl(thunk_function_decl); thunk_function_decl->setParams( BuildThunkParameters(ast_context, callee_info, thunk_function_decl)); // Set always_inline. thunk_function_decl->addAttr( clang::AlwaysInlineAttr::CreateImplicit(ast_context)); // Set asm(".carbon_thunk"). thunk_function_decl->addAttr(clang::AsmLabelAttr::CreateImplicit( ast_context, GenerateThunkMangledName( context.cpp_context()->clang_mangle_context(), *callee_info.decl, callee_info.signature_kind, callee_info.num_params - callee_info.has_explicit_object_parameter()), clang_loc)); // Set function declaration type source info. thunk_function_decl->setTypeSourceInfo(ast_context.getTrivialTypeSourceInfo( thunk_function_decl->getType(), clang_loc)); return thunk_function_decl; } // Builds a reference to the given parameter thunk. If `type` is specified, that // is the callee parameter type that's being held by the parameter, and // conversions will be performed as necessary to recover a value of that type. static auto BuildThunkParamRef(clang::Sema& sema, clang::FunctionDecl* thunk_function_decl, unsigned thunk_index, clang::QualType type = clang::QualType()) -> clang::Expr* { clang::ParmVarDecl* thunk_param = thunk_function_decl->getParamDecl(thunk_index); clang::SourceLocation clang_loc = thunk_param->getLocation(); clang::Expr* call_arg = sema.BuildDeclRefExpr( thunk_param, thunk_param->getType().getNonReferenceType(), clang::VK_LValue, clang_loc); if (!type.isNull() && thunk_param->getType() != type) { clang::ExprResult deref_result = sema.BuildUnaryOp(nullptr, clang_loc, clang::UO_Deref, call_arg); CARBON_CHECK(deref_result.isUsable()); call_arg = deref_result.get(); } // Cast to an rvalue when initializing an rvalue reference. The validity of // the initialization of the reference should be validated by the caller of // the thunk. // // TODO: Consider inserting a cast to an rvalue in more cases. Note that we // currently pass pointers to non-temporary objects as the argument when // calling a thunk, so we'll need to either change that or generate // different thunks depending on whether we're moving from each parameter. if (!type.isNull() && type->isRValueReferenceType()) { call_arg = clang::ImplicitCastExpr::Create( sema.getASTContext(), call_arg->getType(), clang::CK_NoOp, call_arg, nullptr, clang::ExprValueKind::VK_XValue, clang::FPOptionsOverride()); } return call_arg; } // Builds a reference to the parameter thunk parameter corresponding to the // given callee parameter index. static auto BuildParamRefForCalleeArg(clang::Sema& sema, clang::FunctionDecl* thunk_function_decl, CalleeFunctionInfo callee_info, unsigned callee_index) -> clang::Expr* { unsigned thunk_index = callee_info.GetThunkParamIndex(callee_index); return BuildThunkParamRef( sema, thunk_function_decl, thunk_index, callee_info.decl->getParamDecl(callee_index)->getType()); } // Builds an argument list for the callee function by creating suitable uses of // the corresponding thunk parameters. static auto BuildCalleeArgs(clang::Sema& sema, clang::FunctionDecl* thunk_function_decl, CalleeFunctionInfo callee_info) -> llvm::SmallVector { llvm::SmallVector call_args; // The object parameter is always passed as `self`, not in the callee argument // list, so the first argument corresponds to the second parameter if there is // an explicit object parameter and the first parameter otherwise. int first_param = callee_info.has_explicit_object_parameter(); call_args.reserve(callee_info.num_params - first_param); for (unsigned callee_index : llvm::seq(first_param, callee_info.num_params)) { call_args.push_back(BuildParamRefForCalleeArg(sema, thunk_function_decl, callee_info, callee_index)); } return call_args; } // Builds the thunk function body which calls the callee function using the call // args and returns the callee function return value. Returns nullptr on // failure. static auto BuildThunkBody(clang::Sema& sema, clang::FunctionDecl* thunk_function_decl, CalleeFunctionInfo callee_info) -> clang::StmtResult { // TODO: Consider building a CompoundStmt holding our created statement to // make our result more closely resemble a real C++ function. clang::SourceLocation clang_loc = callee_info.decl->getLocation(); // If the callee has an object parameter, build a member access expression as // the callee. Otherwise, build a regular reference to the function. clang::ExprResult callee; if (callee_info.has_object_parameter) { clang::QualType object_param_type = cast(callee_info.decl) ->getFunctionObjectParameterReferenceType(); auto* object_param_ref = BuildThunkParamRef(sema, thunk_function_decl, 0, object_param_type); constexpr bool IsArrow = false; auto object = sema.PerformMemberExprBaseConversion(object_param_ref, IsArrow); if (object.isInvalid()) { return clang::StmtError(); } callee = sema.BuildMemberExpr( object.get(), IsArrow, clang_loc, clang::NestedNameSpecifierLoc(), clang::SourceLocation(), callee_info.decl, clang::DeclAccessPair::make(callee_info.decl, clang::AS_public), /*HadMultipleCandidates=*/false, clang::DeclarationNameInfo(), sema.getASTContext().BoundMemberTy, clang::VK_PRValue, clang::OK_Ordinary); } else if (!isa(callee_info.decl)) { callee = sema.BuildDeclRefExpr(callee_info.decl, callee_info.decl->getType(), clang::VK_PRValue, clang_loc); } if (callee.isInvalid()) { return clang::StmtError(); } // Build the argument list. llvm::SmallVector call_args = BuildCalleeArgs(sema, thunk_function_decl, callee_info); clang::ExprResult call; if (auto info = clang::getConstructorInfo(callee_info.decl); info.Constructor) { // In C++, there are no direct calls to constructors, only initialization, // so we need to type-check and build the call ourselves. auto type = sema.Context.getCanonicalTagType( cast(callee_info.decl->getParent())); llvm::SmallVector converted_args; converted_args.reserve(call_args.size()); if (sema.CompleteConstructorCall(info.Constructor, type, call_args, clang_loc, converted_args)) { return clang::StmtError(); } call = sema.BuildCXXConstructExpr( clang_loc, type, callee_info.decl, info.Constructor, converted_args, false, false, false, false, clang::CXXConstructionKind::Complete, clang_loc); } else { call = sema.BuildCallExpr(nullptr, callee.get(), clang_loc, call_args, clang_loc); } if (!call.isUsable()) { return clang::StmtError(); } if (callee_info.has_simple_return_type) { return sema.BuildReturnStmt(clang_loc, call.get()); } auto* return_object_addr = BuildThunkParamRef( sema, thunk_function_decl, callee_info.GetThunkReturnParamIndex()); auto return_type = callee_info.effective_return_type.getNonReferenceType(); auto* return_type_info = sema.Context.getTrivialTypeSourceInfo(return_type, clang_loc); auto placement_new = sema.BuildCXXNew( clang_loc, /*UseGlobal=*/true, clang_loc, {return_object_addr}, clang_loc, /*TypeIdParens=*/clang::SourceRange(), return_type, return_type_info, /*ArraySize=*/std::nullopt, clang_loc, call.get()); return sema.ActOnExprStmt(placement_new, /*DiscardedValue=*/true); } auto BuildCppThunk(Context& context, const SemIR::Function& callee_function) -> clang::FunctionDecl* { auto clang_decl_key = context.clang_decls().Get(callee_function.clang_decl_id).key; clang::FunctionDecl* callee_function_decl = clang_decl_key.decl->getAsFunction(); CARBON_CHECK(callee_function_decl); // TODO: The signature kind doesn't affect the thunk that we build, so we // shouldn't consider it here. However, to do that, we would need to cache the // thunks we build so that we don't build the same thunk multiple times if // it's used with multiple different signature kinds. CalleeFunctionInfo callee_info(callee_function_decl, clang_decl_key.signature); // Build the thunk function declaration. auto thunk_param_types = BuildThunkParameterTypes(context.ast_context(), callee_info); clang::FunctionDecl* thunk_function_decl = CreateThunkFunctionDecl(context, callee_info, thunk_param_types); // Build the thunk function body. clang::Sema& sema = context.clang_sema(); clang::Sema::ContextRAII context_raii(sema, thunk_function_decl); sema.ActOnStartOfFunctionDef(nullptr, thunk_function_decl); clang::StmtResult body = BuildThunkBody(sema, thunk_function_decl, callee_info); sema.ActOnFinishFunctionBody(thunk_function_decl, body.get()); if (body.isInvalid()) { return nullptr; } context.clang_sema().getASTConsumer().HandleTopLevelDecl( clang::DeclGroupRef(thunk_function_decl)); return thunk_function_decl; } auto PerformCppThunkCall(Context& context, SemIR::LocId loc_id, SemIR::FunctionId callee_function_id, llvm::ArrayRef callee_arg_ids, SemIR::InstId thunk_callee_id) -> SemIR::InstId { auto& callee_function = context.functions().Get(callee_function_id); auto callee_function_params = context.inst_blocks().Get(callee_function.call_params_id); auto callee_return_patterns = context.inst_blocks().GetOrEmpty(callee_function.return_patterns_id); auto thunk_callee = GetCalleeAsFunction(context.sem_ir(), thunk_callee_id); auto& thunk_function = context.functions().Get(thunk_callee.function_id); auto thunk_function_params = context.inst_blocks().Get(thunk_function.call_params_id); auto thunk_return_patterns = context.inst_blocks().GetOrEmpty(thunk_function.return_patterns_id); CARBON_CHECK( callee_return_patterns.size() <= 1 && thunk_return_patterns.size() <= 1, "TODO: generalize this logic to support multiple return patterns."); // Whether we need to pass a return address to the thunk as a final argument. bool thunk_takes_return_address = !callee_return_patterns.empty() && thunk_return_patterns.empty(); // The number of arguments we should be acquiring in order to call the thunk. // This includes the return address parameters, if any. unsigned num_thunk_args = context.inst_blocks().Get(thunk_function.param_patterns_id).size(); // The corresponding number of arguments that would be provided in a syntactic // call to the callee. This excludes the return slot. unsigned num_callee_args = num_thunk_args - thunk_takes_return_address; // Grab the return slot argument, if we were given one. auto return_slot_id = SemIR::InstId::None; if (callee_arg_ids.size() == num_callee_args + 1) { return_slot_id = callee_arg_ids.consume_back(); } // If there are return slot patterns, drop the corresponding parameters. // TODO: The parameter should probably only be created if the return pattern // actually needs a return address to be passed in. thunk_function_params = thunk_function_params.drop_back(thunk_return_patterns.size()); callee_function_params = callee_function_params.drop_back(callee_return_patterns.size()); // We assume that the call parameters exactly match the parameter patterns for // both the thunk and the callee. This is guaranteed even when we generate a // tuple pattern wrapping the function parameters. CARBON_CHECK(num_callee_args == callee_function_params.size(), "{0} != {1}", num_callee_args, callee_function_params.size()); CARBON_CHECK(num_callee_args == callee_arg_ids.size()); CARBON_CHECK(num_thunk_args == thunk_function_params.size()); // Build the thunk arguments by converting the callee arguments as needed. llvm::SmallVector thunk_arg_ids; thunk_arg_ids.reserve(num_thunk_args); for (auto [callee_param_inst_id, thunk_param_inst_id, callee_arg_id] : llvm::zip(callee_function_params, thunk_function_params, callee_arg_ids)) { SemIR::TypeId callee_param_type_id = context.insts().GetAs(callee_param_inst_id).type_id; SemIR::TypeId thunk_param_type_id = context.insts().GetAs(thunk_param_inst_id).type_id; SemIR::InstId arg_id = callee_arg_id; if (callee_param_type_id != thunk_param_type_id) { arg_id = Convert(context, loc_id, arg_id, {.kind = ConversionTarget::CppThunkRef, .type_id = callee_param_type_id}); arg_id = AddInst( context, loc_id, {.type_id = GetPointerType( context, context.types().GetTypeInstId(callee_param_type_id)), .lvalue_id = arg_id}); arg_id = ConvertToValueOfType(context, loc_id, arg_id, thunk_param_type_id); } thunk_arg_ids.push_back(arg_id); } // Add an argument to hold the result of the call, if necessary. auto return_type_id = callee_function.GetDeclaredReturnType(context.sem_ir()); if (thunk_takes_return_address) { // Create a temporary if the caller didn't provide a return slot. if (!return_slot_id.has_value()) { return_slot_id = AddInst( context, loc_id, {.type_id = return_type_id}); } auto arg_id = AddInst( context, loc_id, {.type_id = GetPointerType( context, context.types().GetTypeInstId( context.insts().Get(return_slot_id).type_id())), .lvalue_id = return_slot_id}); thunk_arg_ids.push_back(arg_id); } else if (return_slot_id.has_value()) { thunk_arg_ids.push_back(return_slot_id); } // Compute the return type of the call to the thunk. auto thunk_return_type_id = thunk_function.GetDeclaredReturnType(context.sem_ir()); if (!thunk_return_type_id.has_value()) { CARBON_CHECK(thunk_takes_return_address || !return_type_id.has_value()); thunk_return_type_id = GetTupleType(context, {}); } else { CARBON_CHECK(thunk_return_type_id == return_type_id); } auto result_id = GetOrAddInst( context, loc_id, {.type_id = thunk_return_type_id, .callee_id = thunk_callee_id, .args_id = context.inst_blocks().Add(thunk_arg_ids)}); // Produce the result of the call, taking the value from the return storage. if (thunk_takes_return_address) { result_id = AddInst(context, loc_id, {.type_id = return_type_id, .src_id = result_id, .dest_id = return_slot_id}); } return result_id; } } // namespace Carbon::Check ================================================ FILE: toolchain/check/cpp/thunk.h ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #ifndef CARBON_TOOLCHAIN_CHECK_CPP_THUNK_H_ #define CARBON_TOOLCHAIN_CHECK_CPP_THUNK_H_ #include "toolchain/check/context.h" #include "toolchain/sem_ir/ids.h" namespace Carbon::Check { // Returns whether the given C++ imported function requires a C++ thunk to be // used to call it. A C++ thunk is required for functions whose ABI uses any // type except void, pointer and reference types, and signed 32-bit and 64-bit // integers. auto IsCppThunkRequired(Context& context, const SemIR::Function& function) -> bool; // Given a function signature and a callee function, builds a C++ thunk with // simple ABI (pointers, i32 and i64 types) that calls the specified callee. // Assumes `IsCppThunkRequired()` return true for `callee_function`. Returns // `nullptr` on failure. auto BuildCppThunk(Context& context, const SemIR::Function& callee_function) -> clang::FunctionDecl*; // Builds a call to a thunk function that forwards a call argument list built // for `callee_function_id` to a call to `thunk_callee_id`, for use when // building a call from a C++ thunk to its target. This is like `PerformCall`, // except that it takes a list of call arguments for `callee_function_id`, not a // syntactic argument list. auto PerformCppThunkCall(Context& context, SemIR::LocId loc_id, SemIR::FunctionId callee_function_id, llvm::ArrayRef callee_arg_ids, SemIR::InstId thunk_callee_id) -> SemIR::InstId; } // namespace Carbon::Check #endif // CARBON_TOOLCHAIN_CHECK_CPP_THUNK_H_ ================================================ FILE: toolchain/check/cpp/type_mapping.cpp ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #include "toolchain/check/cpp/type_mapping.h" #include #include #include #include "clang/AST/Type.h" #include "clang/Basic/TargetInfo.h" #include "clang/Sema/Lookup.h" #include "llvm/ADT/SmallVector.h" #include "toolchain/base/int.h" #include "toolchain/base/kind_switch.h" #include "toolchain/base/value_ids.h" #include "toolchain/check/context.h" #include "toolchain/check/convert.h" #include "toolchain/check/cpp/import.h" #include "toolchain/check/cpp/location.h" #include "toolchain/check/literal.h" #include "toolchain/sem_ir/class.h" #include "toolchain/sem_ir/expr_info.h" #include "toolchain/sem_ir/ids.h" #include "toolchain/sem_ir/inst.h" #include "toolchain/sem_ir/inst_kind.h" #include "toolchain/sem_ir/type.h" #include "toolchain/sem_ir/type_info.h" #include "toolchain/sem_ir/typed_insts.h" namespace Carbon::Check { // A function that wraps a C++ type to form another C++ type. Note that this is // a raw function pointer; we don't currently use any lambda captures here. This // can be replaced by a `std::function` if captures are found to be needed. using WrapFn = auto (*)(Context& context, clang::QualType inner_type) -> clang::QualType; // Represents a type that requires a subtype to be mapped into a Clang type // before it can be mapped. struct WrappedType { // The type contained in this wrapped type. SemIR::TypeId inner_type_id; // A function to construct the wrapped type from the mapped unwrapped type. WrapFn wrap_fn; }; // Possible results from attempting to map a type. A null QualType indicates // that the type couldn't be mapped. using TryMapTypeResult = std::variant; // Find the bit width of an integer literal. Following the C++ standard rules // for assigning a type to a decimal integer literal, the first signed integer // in which the value could fit among bit widths of 32, 64 and 128 is selected. // If the value can't fit into a signed integer with width of 128-bits, then a // diagnostic is emitted and the function returns IntId::None. Returns // IntId::None also if the argument is not a constant integer, if it is an // error constant, or if it is a symbolic constant. static auto FindIntLiteralBitWidth(Context& context, SemIR::LocId loc_id, SemIR::ConstantId arg_const_id) -> IntId { if (!arg_const_id.is_constant() || arg_const_id == SemIR::ErrorInst::ConstantId || arg_const_id.is_symbolic()) { // TODO: Add tests for these cases. return IntId::None; } auto arg = context.insts().TryGetAs( context.constant_values().GetInstId(arg_const_id)); if (!arg) { return IntId::None; } llvm::APInt arg_val = context.ints().Get(arg->int_id); int arg_non_sign_bits = arg_val.getSignificantBits() - 1; if (arg_non_sign_bits >= 128) { CARBON_DIAGNOSTIC(IntTooLargeForCppType, Error, "integer value {0} too large to fit in a signed C++ " "integer type; requires {1} bits, but max is 128", TypedInt, int); context.emitter().Emit(loc_id, IntTooLargeForCppType, {.type = arg->type_id, .value = arg_val}, arg_non_sign_bits + 1); return IntId::None; } return (arg_non_sign_bits < 32) ? IntId::MakeRaw(32) : ((arg_non_sign_bits < 64) ? IntId::MakeRaw(64) : IntId::MakeRaw(128)); } // Attempts to look up a type by name, and returns the corresponding `QualType`, // or a null type if lookup fails. `name_components` is the full path of the // type, including any namespaces or nested types, separated into separate // strings. static auto LookupCppType( Context& context, std::initializer_list name_components) -> clang::QualType { clang::Sema& sema = context.clang_sema(); clang::Decl* decl = sema.getASTContext().getTranslationUnitDecl(); for (auto name_component : name_components) { auto* scope = dyn_cast(decl); if (!scope) { return clang::QualType(); } // TODO: Map the LocId of the lookup to a clang SourceLocation and provide // it here so that clang's diagnostics can point into the carbon code that // uses the name. auto* identifier = sema.getPreprocessor().getIdentifierInfo(name_component); clang::LookupResult lookup( sema, clang::DeclarationNameInfo(identifier, clang::SourceLocation()), clang::Sema::LookupNameKind::LookupOrdinaryName); if (!sema.LookupQualifiedName(lookup, scope) || !lookup.isSingleResult()) { return clang::QualType(); } decl = lookup.getFoundDecl(); } auto* type_decl = dyn_cast(decl); return type_decl ? sema.getASTContext().getTypeDeclType(type_decl) : clang::QualType(); } // Returns the given integer type if its width is as expected. Otherwise returns // the null type. static auto VerifyIntegerTypeWidth(Context& context, clang::QualType type, unsigned int expected_width) -> clang::QualType { if (context.ast_context().getIntWidth(type) == expected_width) { return type; } return clang::QualType(); } // Maps a Carbon class type to a C++ type. Returns a null `QualType` if the // type is not supported. static auto TryMapClassType(Context& context, SemIR::ClassType class_type) -> TryMapTypeResult { clang::ASTContext& ast_context = context.ast_context(); // If the class was imported from C++, return the original C++ type. auto clang_decl_id = context.name_scopes() .Get(context.classes().Get(class_type.class_id).scope_id) .clang_decl_context_id(); if (clang_decl_id.has_value()) { clang::Decl* clang_decl = context.clang_decls().Get(clang_decl_id).key.decl; auto* tag_type_decl = clang::cast(clang_decl); return ast_context.getCanonicalTagType(tag_type_decl); } // If the class represents a Carbon type literal, map it to the corresponding // C++ builtin type. auto type_info = SemIR::RecognizedTypeInfo::ForType(context.sem_ir(), class_type); switch (type_info.kind) { case SemIR::RecognizedTypeInfo::None: { break; } case SemIR::RecognizedTypeInfo::Numeric: { // Carbon supports large bit width beyond C++ builtins; we don't translate // those into integer types. if (!type_info.numeric.bit_width_id.is_embedded_value()) { break; } int bit_width = type_info.numeric.bit_width_id.AsValue(); switch (type_info.numeric.kind) { case SemIR::NumericTypeLiteralInfo::None: { CARBON_FATAL("Unexpected invalid numeric type literal"); } case SemIR::NumericTypeLiteralInfo::Float: { return ast_context.getRealTypeForBitwidth( bit_width, clang::FloatModeKind::NoFloat); } case SemIR::NumericTypeLiteralInfo::Int: { return ast_context.getIntTypeForBitwidth(bit_width, true); } case SemIR::NumericTypeLiteralInfo::UInt: { return ast_context.getIntTypeForBitwidth(bit_width, false); } } } case SemIR::RecognizedTypeInfo::Char: { return ast_context.CharTy; } case SemIR::RecognizedTypeInfo::CppLong32: { return VerifyIntegerTypeWidth(context, ast_context.LongTy, 32); } case SemIR::RecognizedTypeInfo::CppULong32: { return VerifyIntegerTypeWidth(context, ast_context.UnsignedLongTy, 32); } case SemIR::RecognizedTypeInfo::CppLongLong64: { return VerifyIntegerTypeWidth(context, ast_context.LongLongTy, 64); } case SemIR::RecognizedTypeInfo::CppULongLong64: { return VerifyIntegerTypeWidth(context, ast_context.UnsignedLongLongTy, 64); } case SemIR::RecognizedTypeInfo::CppNullptrT: { return ast_context.NullPtrTy; } case SemIR::RecognizedTypeInfo::CppVoidBase: { return ast_context.VoidTy; } case SemIR::RecognizedTypeInfo::Optional: { auto args = context.inst_blocks().GetOrEmpty(type_info.args_id); if (args.size() == 1) { auto arg_id = args[0]; if (auto facet = context.insts().TryGetAs(arg_id)) { arg_id = facet->type_inst_id; } if (auto pointer_type = context.insts().TryGetAs(arg_id)) { return WrappedType{ .inner_type_id = context.types().GetTypeIdForTypeInstId( pointer_type->pointee_id), .wrap_fn = [](Context& context, clang::QualType inner_type) { return context.ast_context().getPointerType(inner_type); }}; } } break; } case SemIR::RecognizedTypeInfo::Str: { return LookupCppType(context, {"std", "string_view"}); } } // Otherwise we don't have a mapping for this Carbon class type. // TODO: If the class type wasn't imported from C++, create a corresponding // C++ class type. return clang::QualType(); } // Maps a Carbon type to a C++ type. Either returns the mapped type, a null type // as a placeholder indicating the type can't be mapped, or a `WrappedType` // representing a type that needs more work before it can be mapped. // TODO: Have both Carbon -> C++ and C++ -> Carbon mappings in a single place // to keep them in sync. static auto TryMapType(Context& context, SemIR::TypeId type_id) -> TryMapTypeResult { auto type_inst = context.types().GetAsInst(type_id); CARBON_KIND_SWITCH(type_inst) { case SemIR::BoolType::Kind: { return context.ast_context().BoolTy; } case Carbon::SemIR::CharLiteralType::Kind: { return context.ast_context().CharTy; } case CARBON_KIND(SemIR::ClassType class_type): { return TryMapClassType(context, class_type); } case CARBON_KIND(SemIR::ConstType const_type): { return WrappedType{ .inner_type_id = context.types().GetTypeIdForTypeInstId(const_type.inner_id), .wrap_fn = [](Context& /*context*/, clang::QualType inner_type) { return inner_type.withConst(); }}; } case SemIR::FloatLiteralType::Kind: { return context.ast_context().DoubleTy; } case CARBON_KIND(SemIR::PointerType pointer_type): { return WrappedType{ .inner_type_id = context.types().GetTypeIdForTypeInstId(pointer_type.pointee_id), .wrap_fn = [](Context& context, clang::QualType inner_type) { auto pointer_type = context.ast_context().getPointerType(inner_type); return context.ast_context().getAttributedType( clang::attr::TypeNonNull, pointer_type, pointer_type); }}; } default: { return clang::QualType(); } } return clang::QualType(); } auto MapToCppType(Context& context, SemIR::TypeId type_id) -> clang::QualType { // TODO: unify this with the C++ to Carbon type mapping function. llvm::SmallVector wrap_fns; while (true) { CARBON_KIND_SWITCH(TryMapType(context, type_id)) { case CARBON_KIND(clang::QualType type): { for (auto wrap_fn : llvm::reverse(wrap_fns)) { if (type.isNull()) { break; } type = wrap_fn(context, type); } return type; } case CARBON_KIND(WrappedType wrapped): { wrap_fns.push_back(wrapped.wrap_fn); type_id = wrapped.inner_type_id; break; } } } } namespace { // Information about the form of an expression. struct FormInfo { enum Kind : int8_t { Primitive, Tuple, Struct, }; // The kind of the form. Kind kind; // The category component of the form. For a composite form, if this is not // `Mixed` it represents the category component of all primitive sub-forms // of this form. SemIR::ExprCategory category; // The type component of the form. SemIR::TypeId type_id; // The constant value component of the form. SemIR::ConstantId constant_id; // The location of the expression whose form this is. SemIR::LocId loc_id; // The underlying instruction, if there is one. This is only present in order // to support lazy form decomposition, and should not be used for other // purposes. May be `None` if this is not the form of a tuple or struct // literal that can be decomposed further. SemIR::InstId inst_id; // Returns whether this is a compound form. auto is_compound() const -> bool { return kind == Tuple || kind == Struct; } }; } // namespace // Given a type, determines the category of the decomposed form of an expression // of that type. This is Primitive if the type does not support form // decomposition. static auto GetDecomposedFormKindForType(Context& context, SemIR::TypeId type_id) -> FormInfo::Kind { if (context.types().Is(type_id)) { return FormInfo::Tuple; } if (context.types().Is(type_id)) { return FormInfo::Struct; } return FormInfo::Primitive; } // Gets information about the form of an instruction. static auto GetFormInfo(Context& context, SemIR::InstId inst_id) -> FormInfo { auto inst = context.insts().Get(inst_id); SemIR::ExprCategory category = SemIR::GetExprCategory(context.sem_ir(), inst_id); if (inst.type_id() == SemIR::ErrorInst::TypeId) { // TODO: Should `GetExprCategory` do this? category = SemIR::ExprCategory::Error; } FormInfo::Kind kind = FormInfo::Primitive; if (category == SemIR::ExprCategory::Mixed) { kind = GetDecomposedFormKindForType(context, inst.type_id()); CARBON_CHECK(kind != FormInfo::Primitive, "Unexpected type {0} for mixed category", context.types().GetAsInst(inst.type_id())); } return {.kind = kind, .category = category, .type_id = inst.type_id(), .constant_id = context.constant_values().Get(inst_id), .loc_id = SemIR::LocId(inst_id), .inst_id = inst_id}; } // Given a form, attempts to perform form decomposition, converting it from a // primitive form into a compound form if possible. Otherwise, returns the form // unchanged. static auto DecomposeForm(Context& context, FormInfo form) -> FormInfo { if (form.kind == FormInfo::Primitive) { form.kind = GetDecomposedFormKindForType(context, form.type_id); // TODO: Should we replace a category of Initializing with // EphemeralReference here to model temporary materialization if we // performed decomposition? } return form; } using FormVisitor = llvm::function_refvoid>; // Gets information about the forms of the instructions in a block. static auto VisitFormInfos(Context& context, SemIR::InstBlockId inst_block_id, FormVisitor visitor) -> void { auto inst_ids = context.inst_blocks().Get(inst_block_id); for (auto inst_id : inst_ids) { visitor(GetFormInfo(context, inst_id)); } } // Given a tuple form, visits the forms of the elements. static auto VisitTupleElementForms(Context& context, FormInfo form, FormVisitor visitor) -> void { // If we have a tuple literal, directly grab the forms of its elements. if (auto tuple_lit_inst = context.insts().TryGetAsIfValid(form.inst_id)) { VisitFormInfos(context, tuple_lit_inst->elements_id, visitor); return; } // Otherwise, decompose the type and, if available, the constant value. auto tuple_type = context.types().GetAs(form.type_id); auto element_type_inst_ids = context.inst_blocks().Get(tuple_type.type_elements_id); llvm::SmallVector result; result.reserve(element_type_inst_ids.size()); auto tuple_const_inst = context.insts().TryGetAsIfValid( context.constant_values().GetInstIdIfValid(form.constant_id)); auto tuple_const_inst_ids = tuple_const_inst ? context.inst_blocks().Get(tuple_const_inst->elements_id) : llvm::ArrayRef(); for (auto [type_inst_id, const_inst_id] : llvm::zip_longest(element_type_inst_ids, tuple_const_inst_ids)) { visitor({.kind = FormInfo::Primitive, .category = form.category, .type_id = context.types().GetTypeIdForTypeInstId(*type_inst_id), .constant_id = const_inst_id ? context.constant_values().Get(*const_inst_id) : SemIR::ConstantId::NotConstant, .loc_id = form.loc_id, .inst_id = SemIR::InstId::None}); } } // Given a struct form, returns the forms of the elements. static auto VisitStructElementForms(Context& context, FormInfo form, FormVisitor visitor) -> void { // If we have a struct literal, directly grab the forms of its elements. if (auto struct_lit_inst = context.insts().TryGetAsIfValid(form.inst_id)) { VisitFormInfos(context, struct_lit_inst->elements_id, visitor); return; } // Otherwise, decompose the type and, if available, the constant value. auto struct_type = context.types().GetAs(form.type_id); auto fields = context.struct_type_fields().Get(struct_type.fields_id); llvm::SmallVector result; result.reserve(fields.size()); auto struct_const_inst = context.insts().TryGetAsIfValid( context.constant_values().GetInstIdIfValid(form.constant_id)); auto struct_const_inst_ids = struct_const_inst ? context.inst_blocks().Get(struct_const_inst->elements_id) : llvm::ArrayRef(); for (auto [field, const_inst_id] : llvm::zip_longest(fields, struct_const_inst_ids)) { visitor( {.kind = FormInfo::Primitive, .category = form.category, .type_id = context.types().GetTypeIdForTypeInstId(field->type_inst_id), .constant_id = const_inst_id ? context.constant_values().Get(*const_inst_id) : SemIR::ConstantId::NotConstant, .loc_id = form.loc_id, .inst_id = SemIR::InstId::None}); } } // Invent a primitive Clang argument given the form of the corresponding Carbon // expression. static auto InventPrimitiveClangArg(Context& context, FormInfo form) -> clang::Expr* { clang::ExprValueKind value_kind; switch (form.category) { case SemIR::ExprCategory::Error: // The argument error has already been diagnosed. return nullptr; case SemIR::ExprCategory::Pattern: CARBON_FATAL("Passing a pattern as a function argument"); case SemIR::ExprCategory::DurableRef: case SemIR::ExprCategory::RefTagged: value_kind = clang::ExprValueKind::VK_LValue; break; case SemIR::ExprCategory::EphemeralRef: value_kind = clang::ExprValueKind::VK_XValue; break; case SemIR::ExprCategory::NotExpr: // A call using this expression as an argument won't be valid, but we // don't diagnose that until we convert the expression to the parameter // type. value_kind = clang::ExprValueKind::VK_PRValue; break; case SemIR::ExprCategory::Value: value_kind = clang::ExprValueKind::VK_PRValue; break; case SemIR::ExprCategory::ReprInitializing: case SemIR::ExprCategory::InPlaceInitializing: value_kind = clang::ExprValueKind::VK_PRValue; break; case SemIR::ExprCategory::Mixed: case SemIR::ExprCategory::Dependent: CARBON_FATAL("Argument does not have primitive form"); } clang::QualType arg_cpp_type; // Special case: if the argument is an integer literal, look at its value. // TODO: Consider producing a `clang::IntegerLiteral` in this case instead, so // that C++ overloads that behave differently for zero-valued int literals can // recognize it. if (context.types().Is(form.type_id)) { IntId bit_width_id = FindIntLiteralBitWidth(context, form.loc_id, form.constant_id); if (bit_width_id != IntId::None) { arg_cpp_type = context.ast_context().getIntTypeForBitwidth( bit_width_id.AsValue(), true); } } if (arg_cpp_type.isNull()) { arg_cpp_type = MapToCppType(context, form.type_id); } if (arg_cpp_type.isNull()) { CARBON_DIAGNOSTIC(CppCallArgTypeNotSupported, Error, "call argument of type {0} is not supported", SemIR::TypeId); context.emitter().Emit(form.loc_id, CppCallArgTypeNotSupported, form.type_id); return nullptr; } // Map a value expression to a const-qualified prvalue so that overload // resolution doesn't think it's a suitable argument for a non-const-qualified // object parameter or a `T&&` parameter. We can only do this for class // prvalues, because non-class non-array prvalues can't be qualified in C++. if (form.category == SemIR::ExprCategory::Value && arg_cpp_type->isRecordType()) { arg_cpp_type = context.ast_context().getConstType(arg_cpp_type); } // TODO: Avoid heap allocating more of these on every call. Either cache them // somewhere or put them on the stack. return new (context.ast_context()) clang::OpaqueValueExpr(GetCppLocation(context, form.loc_id), arg_cpp_type.getNonReferenceType(), value_kind); } // Invent an initializer list Clang argument given the form of the corresponding // Carbon expression, which is a compound form. The initializers for the // elements are taken from the end of `results`. static auto InventCompoundClangArg(Context& context, FormInfo form, llvm::SmallVectorImpl& results) -> clang::Expr* { auto make_init_list = [&](llvm::ArrayRef inits) { // TODO: Compute the `(` and `)` locations for a tuple literal or the `{` // and `}` locations for a struct literal. auto compound_loc = GetCppLocation(context, form.loc_id); auto lbrace_loc = compound_loc; auto rbrace_loc = compound_loc; auto* init_list = new (context.ast_context()) clang::InitListExpr( context.ast_context(), lbrace_loc, inits, rbrace_loc); init_list->setType(context.ast_context().VoidTy); return init_list; }; switch (form.kind) { case FormInfo::Primitive: CARBON_FATAL("Not a compound form"); case FormInfo::Tuple: { // For a tuple, form a non-designated init list containing the // corresponding initializers. auto num_elements = context.inst_blocks() .Get(context.types() .GetAs(form.type_id) .type_elements_id) .size(); CARBON_CHECK(results.size() >= num_elements); auto* init_list = make_init_list(llvm::ArrayRef(results).take_back(num_elements)); results.truncate(results.size() - num_elements); return init_list; } case FormInfo::Struct: { // For a struct, form a designated initializer list, converting the struct // field names into designator names. auto fields = context.struct_type_fields().Get( context.types().GetAs(form.type_id).fields_id); llvm::SmallVector field_inits; field_inits.reserve(fields.size()); for (auto [field, init] : llvm::zip( fields, llvm::ArrayRef(results).take_back(fields.size()))) { auto loc = init->getExprLoc(); auto* field_name = GetClangIdentifierInfo(context, field.name_id); if (!field_name) { CARBON_DIAGNOSTIC(CppCallFieldNameNotSupported, Error, "field name `{0}` cannot be mapped into C++", SemIR::NameId); context.emitter().Emit(form.loc_id, CppCallFieldNameNotSupported, field.name_id); return nullptr; } auto designator = clang::DesignatedInitExpr::Designator::CreateFieldDesignator( field_name, /*DotLoc=*/loc, /*FieldLoc=*/loc); field_inits.push_back(clang::DesignatedInitExpr::Create( context.ast_context(), designator, /*IndexExprs*/ {}, /*EqualOrColonLoc=*/loc, /*GNUSyntax=*/false, init)); } results.truncate(results.size() - fields.size()); return make_init_list(field_inits); } } } auto InventClangArg(Context& context, SemIR::InstId arg_id) -> clang::Expr* { enum Phase { Initial, AfterSubexpressions }; llvm::SmallVector> worklist = { {GetFormInfo(context, arg_id), Initial}}; llvm::SmallVector pending_results = {}; while (!worklist.empty()) { auto [form, phase] = worklist.pop_back_val(); switch (phase) { case Initial: { form = DecomposeForm(context, form); switch (form.kind) { case FormInfo::Primitive: { auto* expr = InventPrimitiveClangArg(context, form); if (!expr) { return nullptr; } pending_results.push_back(expr); break; } case FormInfo::Tuple: case FormInfo::Struct: { worklist.push_back({form, AfterSubexpressions}); auto initial_size = worklist.size(); auto visitor = [&](FormInfo element) { worklist.push_back({element, Initial}); }; if (form.kind == FormInfo::Tuple) { VisitTupleElementForms(context, form, visitor); } else { VisitStructElementForms(context, form, visitor); } // Reverse the added elements so that we pop them in element order. std::reverse(worklist.begin() + initial_size, worklist.end()); break; } } break; } case AfterSubexpressions: { auto* expr = InventCompoundClangArg(context, form, pending_results); if (!expr) { return nullptr; } pending_results.push_back(expr); break; } } } CARBON_CHECK(pending_results.size() == 1); return pending_results.back(); } auto InventClangArgs(Context& context, llvm::ArrayRef arg_ids) -> std::optional> { std::optional> arg_exprs; arg_exprs.emplace(); arg_exprs->reserve(arg_ids.size()); for (SemIR::InstId arg_id : arg_ids) { auto* arg_expr = InventClangArg(context, arg_id); if (!arg_expr) { arg_exprs = std::nullopt; return arg_exprs; } arg_exprs->push_back(arg_expr); } return arg_exprs; } } // namespace Carbon::Check ================================================ FILE: toolchain/check/cpp/type_mapping.h ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #ifndef CARBON_TOOLCHAIN_CHECK_CPP_TYPE_MAPPING_H_ #define CARBON_TOOLCHAIN_CHECK_CPP_TYPE_MAPPING_H_ #include "clang/AST/Type.h" #include "toolchain/check/context.h" #include "toolchain/check/convert.h" #include "toolchain/sem_ir/ids.h" namespace Carbon::Check { // Converts a Carbon type to a corresponding C++ type. This uses the default // type mapping, which is suitable for template arguments, typedefs, etc. but // may not be the right mapping to use in a function signature. Returns a null // type if there is no mapping. auto MapToCppType(Context& context, SemIR::TypeId type_id) -> clang::QualType; // Invents a Clang argument expression to use in overload resolution to // represent the given Carbon argument instruction. auto InventClangArg(Context& context, SemIR::InstId arg_id) -> clang::Expr*; // For each arg, invents a Clang argument expression to use in overload // resolution or argument dependent lookup (ADL) to represent the given Carbon // argument instructions. Returns std::nullopt if any arg failed. auto InventClangArgs(Context& context, llvm::ArrayRef arg_ids) -> std::optional>; } // namespace Carbon::Check #endif // CARBON_TOOLCHAIN_CHECK_CPP_TYPE_MAPPING_H_ ================================================ FILE: toolchain/check/custom_witness.cpp ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #include "toolchain/check/custom_witness.h" #include "toolchain/base/kind_switch.h" #include "toolchain/check/facet_type.h" #include "toolchain/check/function.h" #include "toolchain/check/generic.h" #include "toolchain/check/impl.h" #include "toolchain/check/impl_lookup.h" #include "toolchain/check/import_ref.h" #include "toolchain/check/inst.h" #include "toolchain/check/name_lookup.h" #include "toolchain/check/type.h" #include "toolchain/check/type_completion.h" #include "toolchain/sem_ir/builtin_function_kind.h" #include "toolchain/sem_ir/ids.h" #include "toolchain/sem_ir/typed_insts.h" namespace Carbon::Check { // Given a value whose type `IsFacetTypeOrError`, returns the corresponding // type. static auto GetFacetAsType(Context& context, SemIR::ConstantId facet_or_type_const_id) -> SemIR::TypeId { auto facet_or_type_id = context.constant_values().GetInstId(facet_or_type_const_id); auto type_type_id = context.insts().Get(facet_or_type_id).type_id(); CARBON_CHECK(context.types().IsFacetTypeOrError(type_type_id)); if (context.types().Is(type_type_id)) { // It's a facet; access its type. facet_or_type_id = context.types().GetTypeInstId( GetFacetAccessType(context, facet_or_type_id)); } return context.types().GetTypeIdForTypeInstId(facet_or_type_id); } // Returns the body for `Destroy.Op`. This will return `None` if using the // builtin `NoOp` is appropriate. // // TODO: This is a placeholder still not actually destroying things, intended to // maintain mostly-consistent behavior with current logic while working. That // also means using `self`. // TODO: This mirrors `TypeCanDestroy` below, think about ways to share what's // handled. static auto MakeDestroyOpBody(Context& context, SemIR::LocId loc_id, SemIR::TypeId self_type_id) -> SemIR::InstBlockId { context.inst_block_stack().Push(); auto inst = context.types().GetAsInst(self_type_id); while (auto class_type = inst.TryAs()) { // Switch to looking at the object representation. auto class_info = context.classes().Get(class_type->class_id); CARBON_CHECK(class_info.is_complete()); inst = context.types().GetAsInst( class_info.GetObjectRepr(context.sem_ir(), class_type->specific_id)); } CARBON_KIND_SWITCH(inst) { case SemIR::ArrayType::Kind: case SemIR::ConstType::Kind: case SemIR::MaybeUnformedType::Kind: case SemIR::PartialType::Kind: case SemIR::StructType::Kind: case SemIR::TupleType::Kind: // TODO: Implement iterative destruction of types. break; case SemIR::BoolType::Kind: case SemIR::FloatType::Kind: case SemIR::IntType::Kind: case SemIR::PointerType::Kind: // For trivially destructible types, we don't generate anything, so that // this can collapse to a noop implementation when possible. break; case SemIR::ErrorInst::Kind: // Errors can't be destroyed, but we'll still try to generate calls for // other members. break; default: CARBON_FATAL("Unexpected type for destroy: {0}", inst); } if (context.inst_block_stack().PeekCurrentBlockContents().empty()) { context.inst_block_stack().PopAndDiscard(); return SemIR::InstBlockId::None; } AddInst(context, loc_id, SemIR::Return{}); return context.inst_block_stack().Pop(); } // Returns a manufactured `Destroy.Op` function with the `self` parameter typed // to `self_type_id`. static auto MakeDestroyOpFunction(Context& context, SemIR::LocId loc_id, SemIR::TypeId self_type_id, SemIR::NameScopeId parent_scope_id) -> SemIR::InstId { auto name_id = context.core_identifiers().AddNameId(CoreIdentifier::Op); auto [decl_id, function_id] = MakeGeneratedFunctionDecl(context, loc_id, {.parent_scope_id = parent_scope_id, .name_id = name_id, .self_type_id = self_type_id}); auto& function = context.functions().Get(function_id); auto body_id = MakeDestroyOpBody(context, loc_id, self_type_id); if (body_id.has_value()) { function.SetCoreWitness(); function.body_block_ids.push_back(body_id); } else { function.SetCoreWitness(SemIR::BuiltinFunctionKind::NoOp); } return decl_id; } static auto MakeCustomWitnessConstantInst( Context& context, SemIR::LocId loc_id, SemIR::SpecificInterfaceId query_specific_interface_id, SemIR::InstBlockId associated_entities_block_id) -> SemIR::InstId { // The witness is a CustomWitness of the query interface with a table that // contains each associated entity. auto const_id = EvalOrAddInst( context, loc_id, {.type_id = GetSingletonType(context, SemIR::WitnessType::TypeInstId), .elements_id = associated_entities_block_id, .query_specific_interface_id = query_specific_interface_id}); return context.constant_values().GetInstId(const_id); } struct TypesForSelfFacet { // A FacetType that contains only the query interface. SemIR::TypeId facet_type_for_query_specific_interface; // The query self as a type, which involves a conversion if it was a facet. SemIR::TypeId query_self_as_type_id; }; static auto GetTypesForSelfFacet( Context& context, SemIR::LocId loc_id, SemIR::ConstantId query_self_const_id, SemIR::SpecificInterfaceId query_specific_interface_id) -> TypesForSelfFacet { const auto query_specific_interface = context.specific_interfaces().Get(query_specific_interface_id); // The Self facet will have type FacetType, for the query interface. auto facet_type_for_query_specific_interface = context.types().GetTypeIdForTypeConstantId( EvalOrAddInst( context, loc_id, FacetTypeFromInterface(context, query_specific_interface.interface_id, query_specific_interface.specific_id))); // The Self facet needs to point to a type value. If it's not one already, // convert to type. auto query_self_as_type_id = GetFacetAsType(context, query_self_const_id); return {facet_type_for_query_specific_interface, query_self_as_type_id}; } // Build a new facet from the query self, using a CustomWitness for the query // interface with an entry for each associated entity so far. static auto MakeSelfFacetWithCustomWitness( Context& context, SemIR::LocId loc_id, TypesForSelfFacet query_types, SemIR::SpecificInterfaceId query_specific_interface_id, SemIR::InstBlockId associated_entities_block_id) -> SemIR::ConstantId { // We are building a facet value for a single interface, so the witness block // is a single witness for that interface. auto witnesses_block_id = context.inst_blocks().Add({MakeCustomWitnessConstantInst( context, loc_id, query_specific_interface_id, associated_entities_block_id)}); return EvalOrAddInst( context, loc_id, {.type_id = query_types.facet_type_for_query_specific_interface, .type_inst_id = context.types().GetTypeInstId(query_types.query_self_as_type_id), .witnesses_block_id = witnesses_block_id}); } auto BuildCustomWitness(Context& context, SemIR::LocId loc_id, SemIR::ConstantId query_self_const_id, SemIR::SpecificInterfaceId query_specific_interface_id, llvm::ArrayRef values) -> SemIR::InstId { const auto query_specific_interface = context.specific_interfaces().Get(query_specific_interface_id); const auto& interface = context.interfaces().Get(query_specific_interface.interface_id); auto assoc_entities = context.inst_blocks().GetOrEmpty(interface.associated_entities_id); if (assoc_entities.size() != values.size()) { context.TODO(loc_id, ("Unsupported definition of interface " + context.names().GetFormatted(interface.name_id)) .str()); return SemIR::ErrorInst::InstId; } auto query_types_for_self_facet = GetTypesForSelfFacet( context, loc_id, query_self_const_id, query_specific_interface_id); // The values that will go in the witness table. llvm::SmallVector entries; // Fill in the witness table. for (const auto& [assoc_entity_id, value_id] : llvm::zip_equal(assoc_entities, values)) { LoadImportRef(context, assoc_entity_id); // Build a witness with the current contents of the witness table. This will // grow as we progress through the impl. In theory this will build O(n^2) // table entries, but in practice n <= 2, so that's OK. // // This is necessary because later associated entities may refer to earlier // associated entities in their signatures. In particular, an associated // result type may be used as the return type of an associated function. auto self_facet = MakeSelfFacetWithCustomWitness( context, loc_id, query_types_for_self_facet, query_specific_interface_id, context.inst_blocks().Add(entries)); auto interface_with_self_specific_id = MakeSpecificWithInnerSelf( context, loc_id, interface.generic_id, interface.generic_with_self_id, query_specific_interface.specific_id, self_facet); auto decl_id = context.constant_values().GetInstId(SemIR::GetConstantValueInSpecific( context.sem_ir(), interface_with_self_specific_id, assoc_entity_id)); CARBON_CHECK(decl_id.has_value(), "Non-constant associated entity"); auto decl = context.insts().Get(decl_id); CARBON_KIND_SWITCH(decl) { case CARBON_KIND(SemIR::StructValue struct_value): { if (struct_value.type_id == SemIR::ErrorInst::TypeId) { return SemIR::ErrorInst::InstId; } // TODO: If a thunk is needed, this will build a different value each // time it's called, so we won't properly deduplicate repeated // witnesses. entries.push_back(CheckAssociatedFunctionImplementation( context, context.types().GetAs(struct_value.type_id), query_specific_interface.specific_id, value_id, /*defer_thunk_definition=*/false)); break; } case SemIR::AssociatedConstantDecl::Kind: { context.TODO(loc_id, "Associated constant in interface with synthesized impl"); return SemIR::ErrorInst::InstId; } default: CARBON_CHECK(decl_id == SemIR::ErrorInst::InstId, "Unexpected kind of associated entity {0}", decl); return SemIR::ErrorInst::InstId; } } // TODO: Consider building one witness after all associated constants, and // then a second after all associated functions, rather than building one in // each `StructValue`. Right now the code is written assuming at most one // function, though this CHECK can be removed as a temporary workaround. CARBON_CHECK(entries.size() <= 1, "TODO: Support multiple associated functions"); return MakeCustomWitnessConstantInst(context, loc_id, query_specific_interface_id, context.inst_blocks().Add(entries)); } auto GetCoreInterface(Context& context, SemIR::InterfaceId interface_id) -> CoreInterface { const auto& interface = context.interfaces().Get(interface_id); if (!context.name_scopes().IsCorePackage(interface.parent_scope_id) || !interface.name_id.AsIdentifierId().has_value()) { return CoreInterface::Unknown; } constexpr auto CoreIdentifiersToInterfaces = std::array{ std::pair{CoreIdentifier::Copy, CoreInterface::Copy}, std::pair{CoreIdentifier::Destroy, CoreInterface::Destroy}, std::pair{CoreIdentifier::IntFitsIn, CoreInterface::IntFitsIn}, std::pair{CoreIdentifier::CppUnsafeDeref, CoreInterface::CppUnsafeDeref}}; for (auto [core_identifier, core_interface] : CoreIdentifiersToInterfaces) { if (interface.name_id == context.core_identifiers().AddNameId(core_identifier)) { return core_interface; } } return CoreInterface::Unknown; } // Returns true if the `Self` should impl `Destroy`. static auto TypeCanDestroy(Context& context, SemIR::ConstantId query_self_const_id, SemIR::InterfaceId destroy_interface_id) -> bool { auto inst = context.insts().Get(context.constant_values().GetInstId( GetCanonicalFacetOrTypeValue(context, query_self_const_id))); // For facet values, look if the FacetType provides the same. if (auto facet_type = context.types().TryGetAs(inst.type_id())) { const auto& info = context.facet_types().Get(facet_type->facet_type_id); for (auto interface : info.extend_constraints) { if (interface.interface_id == destroy_interface_id) { return true; } } } CARBON_KIND_SWITCH(inst) { case CARBON_KIND(SemIR::ClassType class_type): { auto class_info = context.classes().Get(class_type.class_id); // Incomplete and abstract classes can't be destroyed. if (!class_info.is_complete() || class_info.inheritance_kind == SemIR::Class::InheritanceKind::Abstract) { return false; } // `LookupCppImpl` handles C++ types. if (context.name_scopes().Get(class_info.scope_id).is_cpp_scope()) { return false; } // TODO: Return false if the object repr doesn't impl `Destroy`. return true; } case SemIR::ArrayType::Kind: case SemIR::ConstType::Kind: case SemIR::MaybeUnformedType::Kind: case SemIR::PartialType::Kind: case SemIR::StructType::Kind: case SemIR::TupleType::Kind: // TODO: Return false for types that indirectly reference a type that // doesn't impl `Destroy`. return true; case SemIR::BoolType::Kind: case SemIR::FloatType::Kind: case SemIR::IntType::Kind: case SemIR::PointerType::Kind: // Trivially destructible. return true; default: return false; } } static auto MakeDestroyWitness( Context& context, SemIR::LocId loc_id, SemIR::ConstantId query_self_const_id, SemIR::SpecificInterfaceId query_specific_interface_id) -> std::optional { auto query_specific_interface = context.specific_interfaces().Get(query_specific_interface_id); if (!TypeCanDestroy(context, query_self_const_id, query_specific_interface.interface_id)) { return std::nullopt; } if (query_self_const_id.is_symbolic()) { return SemIR::InstId::None; } // Mark functions with the interface's scope as a hint to mangling. This does // not add them to the scope. auto parent_scope_id = context.interfaces() .Get(query_specific_interface.interface_id) .scope_without_self_id; auto self_type_id = GetFacetAsType(context, query_self_const_id); auto op_id = MakeDestroyOpFunction(context, loc_id, self_type_id, parent_scope_id); return BuildCustomWitness(context, loc_id, query_self_const_id, query_specific_interface_id, {op_id}); } static auto MakeIntFitsInWitness( Context& context, SemIR::LocId loc_id, SemIR::ConstantId query_self_const_id, SemIR::SpecificInterfaceId query_specific_interface_id) -> std::optional { auto query_specific_interface = context.specific_interfaces().Get(query_specific_interface_id); auto args_id = query_specific_interface.specific_id; if (!args_id.has_value()) { return std::nullopt; } auto args_block_id = context.specifics().Get(args_id).args_id; auto args_block = context.inst_blocks().Get(args_block_id); if (args_block.size() != 1) { return std::nullopt; } auto dest_const_id = context.constant_values().Get(args_block[0]); if (!dest_const_id.is_constant()) { return std::nullopt; } auto src_type_id = GetFacetAsType(context, query_self_const_id); auto dest_type_id = GetFacetAsType(context, dest_const_id); auto context_fn = [](DiagnosticContextBuilder& /*builder*/) -> void {}; if (!RequireCompleteType(context, src_type_id, loc_id, context_fn) || !RequireCompleteType(context, dest_type_id, loc_id, context_fn)) { return std::nullopt; } auto src_info = context.types().TryGetIntTypeInfo(src_type_id); auto dest_info = context.types().TryGetIntTypeInfo(dest_type_id); if (!src_info || !dest_info) { return std::nullopt; } // If the bit width is unknown (e.g., due to symbolic evaluation), we cannot // determine whether it fits yet. if (src_info->bit_width == IntId::None || dest_info->bit_width == IntId::None) { return std::nullopt; } const auto& src_width = context.ints().Get(src_info->bit_width); const auto& dest_width = context.ints().Get(dest_info->bit_width); bool fits = false; if (src_info->is_signed && !dest_info->is_signed) { // Signed -> unsigned: would truncate the sign bit. fits = false; } else if (src_info->is_signed == dest_info->is_signed) { // Signed -> signed or unsigned -> unsigned: allow widening or preserving // width. fits = src_width.sle(dest_width); } else { // Unsigned -> signed: strict widening required. fits = src_width.slt(dest_width); } if (!fits) { return std::nullopt; } return BuildCustomWitness(context, loc_id, query_self_const_id, query_specific_interface_id, {}); } auto LookupCustomWitness(Context& context, SemIR::LocId loc_id, CoreInterface core_interface, SemIR::ConstantId query_self_const_id, SemIR::SpecificInterfaceId query_specific_interface_id) -> std::optional { switch (core_interface) { case CoreInterface::Destroy: return MakeDestroyWitness(context, loc_id, query_self_const_id, query_specific_interface_id); case CoreInterface::IntFitsIn: return MakeIntFitsInWitness(context, loc_id, query_self_const_id, query_specific_interface_id); case CoreInterface::CppUnsafeDeref: case CoreInterface::Copy: case CoreInterface::Unknown: // TODO: Handle more interfaces, particularly copy, move, and conversion. return std::nullopt; } } } // namespace Carbon::Check ================================================ FILE: toolchain/check/custom_witness.h ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #ifndef CARBON_TOOLCHAIN_CHECK_CUSTOM_WITNESS_H_ #define CARBON_TOOLCHAIN_CHECK_CUSTOM_WITNESS_H_ #include "toolchain/check/context.h" #include "toolchain/sem_ir/ids.h" namespace Carbon::Check { // Builds a witness that the given type implements the given interface, // populating it with the specified set of values. Returns a corresponding // lookup result. Produces a diagnostic and returns `None` if the specified // values aren't suitable for the interface. auto BuildCustomWitness(Context& context, SemIR::LocId loc_id, SemIR::ConstantId query_self_const_id, SemIR::SpecificInterfaceId query_specific_interface_id, llvm::ArrayRef values) -> SemIR::InstId; // Significant interfaces in `Core` which correspond to language features and // can have custom witnesses. enum class CoreInterface { Copy, Destroy, IntFitsIn, CppUnsafeDeref, Unknown, }; // Given an interface, returns the corresponding enum if it's covered by // `CoreInterface`, or `Unknown` if it's some other interface. auto GetCoreInterface(Context& context, SemIR::InterfaceId interface_id) -> CoreInterface; // Returns a witness for a `CoreInterface` `CustomWitness`. A return value of // `None` indicates a non-final witness should be produced, while `std::nullopt` // indicates the query is final and no witness can be produced. auto LookupCustomWitness(Context& context, SemIR::LocId loc_id, CoreInterface core_interface, SemIR::ConstantId query_self_const_id, SemIR::SpecificInterfaceId query_specific_interface_id) -> std::optional; } // namespace Carbon::Check #endif // CARBON_TOOLCHAIN_CHECK_CUSTOM_WITNESS_H_ ================================================ FILE: toolchain/check/decl_introducer_state.h ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #ifndef CARBON_TOOLCHAIN_CHECK_DECL_INTRODUCER_STATE_H_ #define CARBON_TOOLCHAIN_CHECK_DECL_INTRODUCER_STATE_H_ #include "toolchain/check/keyword_modifier_set.h" #include "toolchain/lex/token_kind.h" #include "toolchain/parse/node_ids.h" namespace Carbon::Check { // State stored for each declaration we are introducing: the kind of // declaration and the keyword modifiers that apply to that declaration // introducer. struct DeclIntroducerState { auto modifier_node_id(ModifierOrder order) const -> Parse::NodeId { return ordered_modifier_node_ids[static_cast(order)]; } auto set_modifier_node_id(ModifierOrder order, Parse::NodeId node_id) -> void { ordered_modifier_node_ids[static_cast(order)] = node_id; } // The token kind of the introducer. Lex::TokenKind kind; // Nodes of modifiers on this declaration, in expected order. `None` if no // modifier of that kind is present. Parse::NodeId ordered_modifier_node_ids[static_cast(ModifierOrder::Last) + 1] = {Parse::NodeId::None, Parse::NodeId::None, Parse::NodeId::None, Parse::NodeId::None, Parse::NodeId::None}; // Invariant: contains just the modifiers represented by `saw_*_modifier`. KeywordModifierSet modifier_set = KeywordModifierSet(); // If there's an `extern library` in use, the library name. SemIR::LibraryNameId extern_library = SemIR::LibraryNameId::None; }; // Stack of `DeclIntroducerState` values, representing all the declaration // introducers we are currently nested within. Commonly size 0 or 1, as nested // introducers are rare. class DeclIntroducerStateStack { private: // Returns true for introducer tokens. Supports Push/Pop `requires`. template static constexpr auto IsDeclIntroducer() -> bool { switch (Kind) { #define CARBON_TOKEN(...) #define CARBON_DECL_INTRODUCER_TOKEN(Name, ...) case Lex::TokenKind::Name: #include "toolchain/lex/token_kind.def" return true; default: return false; } } public: // Begins a declaration introducer `Kind`. template requires(IsDeclIntroducer()) auto Push() -> void { stack_.push_back({.kind = Lex::TokenKind::Make(Kind)}); } // Gets the state of declaration at the top of the stack -- the innermost // declaration currently being processed. auto innermost() -> DeclIntroducerState& { return stack_.back(); } // Finishes a declaration introducer `Kind` and returns the produced state. template requires(IsDeclIntroducer()) auto Pop() -> DeclIntroducerState { CARBON_CHECK(stack_.back().kind == Kind, "Found: {0} expected: {1}", stack_.back().kind, Lex::TokenKind::Make(Kind)); return stack_.pop_back_val(); } // Runs verification that the processing cleanly finished. auto VerifyOnFinish() const -> void { CARBON_CHECK(stack_.empty(), "decl_introduce_state_stack still has {0} entries", stack_.size()); } private: llvm::SmallVector stack_; }; } // namespace Carbon::Check #endif // CARBON_TOOLCHAIN_CHECK_DECL_INTRODUCER_STATE_H_ ================================================ FILE: toolchain/check/decl_name_stack.cpp ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #include "toolchain/check/decl_name_stack.h" #include #include "toolchain/base/kind_switch.h" #include "toolchain/check/context.h" #include "toolchain/check/diagnostic_helpers.h" #include "toolchain/check/generic.h" #include "toolchain/check/merge.h" #include "toolchain/check/name_component.h" #include "toolchain/check/name_lookup.h" #include "toolchain/check/type_completion.h" #include "toolchain/check/unused.h" #include "toolchain/diagnostics/diagnostic.h" #include "toolchain/sem_ir/ids.h" #include "toolchain/sem_ir/name_scope.h" #include "toolchain/sem_ir/typed_insts.h" namespace Carbon::Check { auto DeclNameStack::NameContext::prev_inst_id() const -> SemIR::InstId { switch (state) { case NameContext::State::Error: // The name is malformed and a diagnostic has already been emitted. return SemIR::InstId::None; case NameContext::State::Empty: CARBON_FATAL( "Name is missing, not expected to call existing_inst_id (but that " "may change based on error handling)."); case NameContext::State::Resolved: return resolved_inst_id; case NameContext::State::Unresolved: return SemIR::InstId::None; case NameContext::State::Poisoned: CARBON_FATAL("Poisoned state should not call prev_inst_id()"); case NameContext::State::Finished: CARBON_FATAL("Finished state should only be used internally"); } } auto DeclNameStack::MakeEmptyNameContext() -> NameContext { return NameContext{ .initial_scope_index = context_->scope_stack().PeekIndex(), .parent_scope_id = context_->scope_stack().PeekNameScopeId()}; } auto DeclNameStack::MakeUnqualifiedName(SemIR::LocId loc_id, SemIR::NameId name_id) -> NameContext { NameContext context = MakeEmptyNameContext(); ApplyAndLookupName(context, loc_id, name_id); return context; } auto DeclNameStack::PushScopeAndStartName() -> void { decl_name_stack_.push_back(MakeEmptyNameContext()); // Create a scope for any parameters introduced in this name. context_->scope_stack().PushForDeclName(); } auto DeclNameStack::FinishName(const NameComponent& name) -> NameContext { CARBON_CHECK(decl_name_stack_.back().state != NameContext::State::Finished, "Finished name twice"); ApplyAndLookupName(decl_name_stack_.back(), name.name_loc_id, name.name_id); NameContext result = decl_name_stack_.back(); decl_name_stack_.back().state = NameContext::State::Finished; return result; } auto DeclNameStack::FinishImplName() -> NameContext { CARBON_CHECK(decl_name_stack_.back().state == NameContext::State::Empty, "Impl has a name"); NameContext result = decl_name_stack_.back(); decl_name_stack_.back().state = NameContext::State::Finished; return result; } auto DeclNameStack::PopScope(bool check_unused) -> void { CARBON_CHECK(decl_name_stack_.back().state == NameContext::State::Finished, "Missing call to FinishName before PopScope"); context_->scope_stack().PopTo(decl_name_stack_.back().initial_scope_index, check_unused); decl_name_stack_.pop_back(); } auto DeclNameStack::Suspend() -> SuspendedName { CARBON_CHECK(decl_name_stack_.back().state == NameContext::State::Finished, "Missing call to FinishName before Suspend"); SuspendedName result = {.name_context = decl_name_stack_.pop_back_val(), .scopes = {}}; auto scope_index = result.name_context.initial_scope_index; auto& scope_stack = context_->scope_stack(); while (scope_stack.PeekIndex() > scope_index) { result.scopes.push_back(scope_stack.Suspend()); } CARBON_CHECK(scope_stack.PeekIndex() == scope_index, "Scope index {0} does not enclose the current scope {1}", scope_index, scope_stack.PeekIndex()); return result; } auto DeclNameStack::Restore(SuspendedName&& sus) -> void { // The parent state must be the same when a name is restored. CARBON_CHECK(context_->scope_stack().PeekIndex() == sus.name_context.initial_scope_index, "Name restored at the wrong position in the name stack."); // clang-tidy warns that the `std::move` below has no effect. While that's // true, this `move` defends against `NameContext` growing more state later. // NOLINTNEXTLINE(performance-move-const-arg) decl_name_stack_.push_back(std::move(sus.name_context)); for (auto& suspended_scope : llvm::reverse(sus.scopes)) { // Reattempt to resolve the definition of the specific. The generic might // have been defined after we suspended this scope. if (suspended_scope.entry.specific_id.has_value()) { ResolveSpecificDefinition(*context_, sus.name_context.loc_id, suspended_scope.entry.specific_id); } context_->scope_stack().Restore(std::move(suspended_scope)); } } auto DeclNameStack::AddName(NameContext name_context, SemIR::InstId target_id, SemIR::AccessKind access_kind) -> void { switch (name_context.state) { case NameContext::State::Error: return; case NameContext::State::Unresolved: if (!name_context.parent_scope_id.has_value()) { AddNameToLookup(*context_, name_context.name_id, target_id, name_context.initial_scope_index); } else { auto& name_scope = context_->name_scopes().Get(name_context.parent_scope_id); if (name_context.has_qualifiers) { auto inst = context_->insts().Get(name_scope.inst_id()); if (!inst.Is()) { // TODO: Point at the declaration for the scoped entity. CARBON_DIAGNOSTIC( QualifiedDeclOutsideScopeEntity, Error, "out-of-line declaration requires a declaration in " "scoped entity"); context_->emitter().Emit(name_context.loc_id, QualifiedDeclOutsideScopeEntity); } } // Exports are only tracked when the declaration is at the file-level // scope. Otherwise, it's in some other entity, such as a class. if (access_kind == SemIR::AccessKind::Public && name_context.initial_scope_index == ScopeIndex::Package) { context_->exports().push_back(target_id); } name_scope.AddRequired({.name_id = name_context.name_id, .result = SemIR::ScopeLookupResult::MakeFound( target_id, access_kind)}); } break; default: CARBON_FATAL("Should not be calling AddName"); break; } } auto DeclNameStack::AddNameOrDiagnose(NameContext name_context, SemIR::InstId target_id, SemIR::AccessKind access_kind) -> void { if (name_context.state == DeclNameStack::NameContext::State::Poisoned) { DiagnosePoisonedName(*context_, name_context.name_id_for_new_inst(), name_context.poisoning_loc_id, name_context.loc_id); } else if (auto id = name_context.prev_inst_id(); id.has_value()) { DiagnoseDuplicateName(*context_, name_context.name_id, name_context.loc_id, SemIR::LocId(id)); } else { AddName(name_context, target_id, access_kind); } } auto DeclNameStack::LookupOrAddName(NameContext name_context, SemIR::InstId target_id, SemIR::AccessKind access_kind) -> SemIR::ScopeLookupResult { if (name_context.state == NameContext::State::Poisoned) { return SemIR::ScopeLookupResult::MakePoisoned( name_context.poisoning_loc_id); } if (auto id = name_context.prev_inst_id(); id.has_value()) { return SemIR::ScopeLookupResult::MakeFound(id, access_kind); } AddName(name_context, target_id, access_kind); return SemIR::ScopeLookupResult::MakeNotFound(); } // Get the name scope and generic to use for associated entities in `scope`. // Typically this is None, in which case the input scope should be used, but // some entities have a separate generic and inner scope used for associated // entities. static auto GetAssociatedEntityScope(Context& context, const SemIR::NameScope& scope) -> std::pair { auto scope_inst = context.insts().Get(scope.inst_id()); CARBON_KIND_SWITCH(scope_inst) { case CARBON_KIND(SemIR::InterfaceDecl interface_decl): { const auto& interface = context.interfaces().Get(interface_decl.interface_id); return {interface.scope_with_self_id, interface.generic_with_self_id}; } case CARBON_KIND(SemIR::InterfaceWithSelfDecl _): { CARBON_FATAL("Expected InterfaceDecl as qualifier scope"); } case CARBON_KIND(SemIR::NamedConstraintDecl _): { // ResolveAsScope() does not allow named constraints as a scope qualifier. CARBON_FATAL( "Did not expect to find named constraint as scope qualifier"); } case CARBON_KIND(SemIR::NamedConstraintWithSelfDecl _): { CARBON_FATAL("Expected NamedConstraintDecl as qualifier scope"); } default: return {SemIR::NameScopeId::None, SemIR::GenericId::None}; } } // Push a scope corresponding to a name qualifier. For example, for // `fn Class(T:! type).F(n: i32)` we will push the scope for `Class(T:! type)` // between the scope containing the declaration of `T` and the scope // containing the declaration of `n`. // // Returns the NameScopeId to use as the parent scope of the next name. static auto PushNameQualifierScope(Context& context, SemIR::LocId loc_id, SemIR::NameScopeId scope_id, SemIR::GenericId generic_id, bool has_error = false) -> SemIR::NameScopeId { // If the qualifier has no parameters, we don't need to keep around a // parameter scope. context.scope_stack().PopIfEmpty(/*check_unused=*/true); auto self_specific_id = SemIR::SpecificId::None; if (generic_id.has_value()) { self_specific_id = context.generics().GetSelfSpecific(generic_id); // When declaring a member of a generic, resolve the self specific. ResolveSpecificDefinition(context, loc_id, self_specific_id); } // Close the generic stack scope and open a new one for whatever comes after // the qualifier. As this is a qualifier it must not be the initial // declaration of the entity, so we treat it as a redeclaration. FinishGenericRedecl(context, generic_id); // What follows the qualifier will be a declaration. The signature of an // entity is also a declaration even if it is followed by curly braces // providing the definition. StartGenericDecl(context); const auto& scope = context.name_scopes().Get(scope_id); context.scope_stack().PushForEntity(scope.inst_id(), scope_id, self_specific_id, has_error); auto [assoc_entity_scope_id, assoc_entity_generic_id] = GetAssociatedEntityScope(context, scope); if (assoc_entity_scope_id.has_value()) { const auto& assoc_entity_scope = context.name_scopes().Get(assoc_entity_scope_id); // InterfaceDecl is the only inst that can be a scope qualifier and that has // an associated entity scope, the InterfaceWithSelfDecl. auto interface_decl = context.insts().GetAs( assoc_entity_scope.inst_id()); auto& interface = context.interfaces().Get(interface_decl.interface_id); // An interface also introduces its 'Self' parameter into the associated // entity scope, despite it not being redeclared as part of the qualifier. context.scope_stack().AddCompileTimeBinding(); context.scope_stack().PushCompileTimeBinding(interface.self_param_id); // Move into the interface-with-self scope. context.scope_stack().PushForEntity( assoc_entity_scope.inst_id(), assoc_entity_scope_id, context.generics().GetSelfSpecific(assoc_entity_generic_id), has_error); } // Enter a parameter scope in case the qualified name itself has parameters. context.scope_stack().PushForSameRegion(); return assoc_entity_scope_id.has_value() ? assoc_entity_scope_id : scope_id; } auto DeclNameStack::ApplyNameQualifier(const NameComponent& name) -> void { auto& name_context = decl_name_stack_.back(); ApplyAndLookupName(name_context, name.name_loc_id, name.name_id); name_context.has_qualifiers = true; // Resolve the qualifier as a scope and enter the new scope. auto [scope_id, generic_id] = ResolveAsScope(name_context, name); if (scope_id.has_value()) { name_context.parent_scope_id = PushNameQualifierScope( *context_, name_context.loc_id, scope_id, generic_id, context_->name_scopes().Get(scope_id).has_error()); } else { name_context.state = NameContext::State::Error; } } auto DeclNameStack::ApplyAndLookupName(NameContext& name_context, SemIR::LocId loc_id, SemIR::NameId name_id) -> void { // Update the final name component. name_context.loc_id = loc_id; name_context.name_id = name_id; // Don't perform any more lookups after we hit an error. We still track the // final name, though. if (name_context.state == NameContext::State::Error) { return; } // For identifier nodes, we need to perform a lookup on the identifier. auto lookup_result = LookupNameInDecl(*context_, name_context.loc_id, name_id, name_context.parent_scope_id, name_context.initial_scope_index); if (lookup_result.is_poisoned()) { name_context.poisoning_loc_id = lookup_result.poisoning_loc_id(); name_context.state = NameContext::State::Poisoned; } else if (!lookup_result.is_found()) { // Invalid indicates an unresolved name. Store it and return. name_context.state = NameContext::State::Unresolved; } else { // Store the resolved instruction and continue for the target scope // update. name_context.resolved_inst_id = lookup_result.target_inst_id(); name_context.state = NameContext::State::Resolved; } } // Checks and returns whether name_context, which is used as a name qualifier, // was successfully resolved. Issues a suitable diagnostic if not. static auto CheckQualifierIsResolved( Context& context, const DeclNameStack::NameContext& name_context) -> bool { switch (name_context.state) { case DeclNameStack::NameContext::State::Empty: CARBON_FATAL("No qualifier to resolve"); case DeclNameStack::NameContext::State::Resolved: return true; case DeclNameStack::NameContext::State::Poisoned: case DeclNameStack::NameContext::State::Unresolved: // Because more qualifiers were found, we diagnose that the earlier // qualifier failed to resolve. DiagnoseNameNotFound(context, name_context.loc_id, name_context.name_id); return false; case DeclNameStack::NameContext::State::Finished: CARBON_FATAL("Added a qualifier after calling FinishName"); case DeclNameStack::NameContext::State::Error: // Already in an error state, so return without examining. return false; } } // Diagnose that a qualified declaration name specifies an incomplete class as // its scope. static auto DiagnoseQualifiedDeclInIncompleteClassScope(Context& context, SemIR::LocId loc_id, SemIR::ClassId class_id) -> void { Diagnostics::ContextScope diagnostic_context( &context.emitter(), [&](auto& builder) { CARBON_DIAGNOSTIC(QualifiedDeclInIncompleteClassScope, Context, "cannot declare a member of incomplete class {0}", SemIR::TypeId); builder.Context(loc_id, QualifiedDeclInIncompleteClassScope, context.classes().Get(class_id).self_type_id); }); DiagnoseIncompleteClass(context, class_id); } // Diagnose that a qualified declaration name specifies an undefined interface // as its scope. static auto DiagnoseQualifiedDeclInUndefinedInterfaceScope( Context& context, SemIR::LocId loc_id, SemIR::InterfaceId interface_id, SemIR::InstId interface_inst_id) -> void { Diagnostics::ContextScope diagnostic_context( &context.emitter(), [&](auto& builder) { CARBON_DIAGNOSTIC(QualifiedDeclInUndefinedInterfaceScope, Context, "cannot declare a member of undefined interface {0}", InstIdAsType); builder.Context(loc_id, QualifiedDeclInUndefinedInterfaceScope, interface_inst_id); }); DiagnoseIncompleteInterface(context, interface_id); } // Diagnose that a qualified declaration name specifies a different package as // its scope. static auto DiagnoseQualifiedDeclInImportedPackage(Context& context, SemIR::LocId use_loc_id, SemIR::LocId import_loc_id) -> void { CARBON_DIAGNOSTIC(QualifiedDeclOutsidePackage, Error, "imported packages cannot be used for declarations"); CARBON_DIAGNOSTIC(QualifiedDeclOutsidePackageSource, Note, "package imported here"); context.emitter() .Build(use_loc_id, QualifiedDeclOutsidePackage) .Note(import_loc_id, QualifiedDeclOutsidePackageSource) .Emit(); } // Diagnose that a qualified declaration name specifies a non-scope entity as // its scope. static auto DiagnoseQualifiedDeclInNonScope( Context& context, SemIR::LocId use_loc_id, SemIR::LocId non_scope_entity_loc_id) -> void { CARBON_DIAGNOSTIC(QualifiedNameInNonScope, Error, "name qualifiers are only allowed for entities that " "provide a scope"); CARBON_DIAGNOSTIC(QualifiedNameNonScopeEntity, Note, "referenced non-scope entity declared here"); context.emitter() .Build(use_loc_id, QualifiedNameInNonScope) .Note(non_scope_entity_loc_id, QualifiedNameNonScopeEntity) .Emit(); } auto DeclNameStack::ResolveAsScope(const NameContext& name_context, const NameComponent& name) const -> std::pair { constexpr std::pair InvalidResult = { SemIR::NameScopeId::None, SemIR::GenericId::None}; if (!CheckQualifierIsResolved(*context_, name_context)) { return InvalidResult; } if (name_context.state == NameContext::State::Poisoned) { return InvalidResult; } auto new_params = DeclParams( name.name_loc_id, name.first_param_node_id, name.last_param_node_id, name.implicit_param_patterns_id, name.param_patterns_id); // Find the scope corresponding to the resolved instruction. // TODO: When diagnosing qualifiers on names, print a diagnostic that talks // about qualifiers instead of redeclarations. Maybe also rename // CheckRedeclParamsMatch. CARBON_KIND_SWITCH(context_->insts().Get(name_context.resolved_inst_id)) { case CARBON_KIND(SemIR::ClassDecl class_decl): { const auto& class_info = context_->classes().Get(class_decl.class_id); if (!CheckRedeclParamsMatch(*context_, new_params, DeclParams(class_info))) { return InvalidResult; } if (!class_info.is_complete()) { DiagnoseQualifiedDeclInIncompleteClassScope( *context_, name_context.loc_id, class_decl.class_id); return InvalidResult; } return {class_info.scope_id, class_info.generic_id}; } case CARBON_KIND(SemIR::InterfaceDecl interface_decl): { const auto& interface_info = context_->interfaces().Get(interface_decl.interface_id); if (!CheckRedeclParamsMatch(*context_, new_params, DeclParams(interface_info))) { return InvalidResult; } if (!interface_info.is_complete()) { DiagnoseQualifiedDeclInUndefinedInterfaceScope( *context_, name_context.loc_id, interface_decl.interface_id, name_context.resolved_inst_id); return InvalidResult; } // The scope and generic of an `I(T:! type)` is the outer // interface-without-self. That is the generic where parameters appear. // However when moving to the next qualifier, we need to move to the // interface-with-self for the associated entity name. return {interface_info.scope_without_self_id, interface_info.generic_id}; } case CARBON_KIND(SemIR::Namespace resolved_inst): { auto scope_id = resolved_inst.name_scope_id; auto& scope = context_->name_scopes().Get(scope_id); // This is specifically for qualified name handling. if (!CheckRedeclParamsMatch( *context_, new_params, DeclParams(SemIR::LocId(name_context.resolved_inst_id), Parse::NodeId::None, Parse::NodeId::None, SemIR::InstBlockId::None, SemIR::InstBlockId::None))) { return InvalidResult; } if (scope.is_closed_import()) { DiagnoseQualifiedDeclInImportedPackage(*context_, name_context.loc_id, SemIR::LocId(scope.inst_id())); // Only error once per package. Recover by allowing this package name to // be used as a name qualifier. scope.set_is_closed_import(false); } return {scope_id, SemIR::GenericId::None}; } default: { DiagnoseQualifiedDeclInNonScope( *context_, name_context.loc_id, SemIR::LocId(name_context.resolved_inst_id)); return InvalidResult; } } } } // namespace Carbon::Check ================================================ FILE: toolchain/check/decl_name_stack.h ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #ifndef CARBON_TOOLCHAIN_CHECK_DECL_NAME_STACK_H_ #define CARBON_TOOLCHAIN_CHECK_DECL_NAME_STACK_H_ #include "llvm/ADT/SmallVector.h" #include "toolchain/check/name_component.h" #include "toolchain/check/scope_index.h" #include "toolchain/check/scope_stack.h" #include "toolchain/sem_ir/ids.h" namespace Carbon::Check { class Context; // Provides support and stacking for qualified declaration name handling. // // A qualified declaration name will consist of entries, which are `Name`s // optionally followed by generic parameter lists, such as `Vector(T:! type)` // in `fn Vector(T:! type).Clear();`, but parameter lists aren't supported yet. // Identifiers such as `Clear` will be resolved to a name if possible, for // example when declaring things that are in a non-generic type or namespace, // and are otherwise marked as an unresolved identifier. // // Unresolved identifiers are valid if and only if they are the last step of a // qualified name; all resolved qualifiers must resolve to an entity with // members, such as a namespace. Resolved identifiers in the last step will // occur for both out-of-line definitions and new declarations, depending on // context. // // For each name component that is processed and denotes a scope, the // corresponding scope is also entered. This is important for unqualified name // lookup both in the definition of the entity being declared, and for names // appearing later in the declaration name itself. For example, in: // // ``` // fn ClassA.ClassB(T:! U).Fn() { var x: V; } // ``` // // the lookup for `U` looks in `ClassA`; the lookup for `V` looks first in // `ClassA.ClassB`, then its parent scope `ClassA`. Scopes entered as part of // processing the name are exited when the name is popped from the stack. // // Example state transitions: // // ``` // // Empty -> Unresolved, because `MyNamespace` is newly declared. // namespace MyNamespace; // // // Empty -> Resolved -> Unresolved, because `MyType` is newly declared. // class MyNamespace.MyType; // // // Empty -> Resolved -> Resolved, because `MyType` was forward declared. // class MyNamespace.MyType { // // Empty -> Unresolved, because `DoSomething` is newly declared. // fn DoSomething(); // } // // // Empty -> Resolved -> Resolved -> ResolvedNonScope, because `DoSomething` // // is forward declared in `MyType`, but is not a scope itself. // fn MyNamespace.MyType.DoSomething() { ... } // ``` class DeclNameStack { public: // Context for declaration name construction. // TODO: Add a helper for class, function, and interface to turn a NameContext // into an EntityWithParamsBase. struct NameContext { enum class State : int8_t { // A context that has not processed any parts of the qualifier. Empty, // The name has been resolved to an instruction ID. Resolved, // An identifier didn't resolve. Unresolved, // An identifier was poisoned in this scope. Poisoned, // The name has already been finished. This is not set in the name // returned by `FinishName`, but is used internally to track that // `FinishName` has already been called. Finished, // An error has occurred, such as an additional qualifier past an // unresolved name. No new diagnostics should be emitted. Error, }; // Combines name information to produce a base struct for entity // construction. auto MakeEntityWithParamsBase(const NameComponent& name, SemIR::InstId decl_id, bool is_extern, SemIR::LibraryNameId extern_library) const -> SemIR::EntityWithParamsBase { return { .name_id = name.name_id, .parent_scope_id = parent_scope_id, .generic_id = SemIR::GenericId::None, .first_param_node_id = name.first_param_node_id, .last_param_node_id = name.last_param_node_id, .pattern_block_id = name.pattern_block_id, .implicit_param_patterns_id = name.implicit_param_patterns_id, .param_patterns_id = name.param_patterns_id, .is_extern = is_extern, .extern_library_id = extern_library, .non_owning_decl_id = extern_library.has_value() ? decl_id : SemIR::InstId::None, .first_owning_decl_id = extern_library.has_value() ? SemIR::InstId::None : decl_id, }; } // Returns any name collision found, or `None`. Requires a non-poisoned // value. auto prev_inst_id() const -> SemIR::InstId; // Returns the name_id for a new instruction. This is `None` when the name // resolved. auto name_id_for_new_inst() const -> SemIR::NameId { switch (state) { case State::Unresolved: case State::Poisoned: return name_id; default: return SemIR::NameId::None; } } // The current scope when this name began. This is the scope that we will // return to at the end of the declaration. ScopeIndex initial_scope_index; State state = State::Empty; // Whether there have been qualifiers in the name. bool has_qualifiers = false; // The scope which qualified names are added to. For unqualified names in // an unnamed scope, this will be `None` to indicate the current scope // should be used. SemIR::NameScopeId parent_scope_id; // The location of the final name component. SemIR::LocId loc_id = SemIR::LocId::None; // The name of the final name component. SemIR::NameId name_id = SemIR::NameId::None; union { // The ID of a resolved qualifier, including both identifiers and // expressions. `None` indicates resolution failed. SemIR::InstId resolved_inst_id; // When `state` is `Poisoned` (name is unresolved due to name poisoning), // the poisoning location. SemIR::LocId poisoning_loc_id = SemIR::LocId::None; }; }; // Information about a declaration name that has been temporarily removed from // the stack and will later be restored. Names can only be suspended once they // are finished. // // This type is large, so moves of this type should be avoided. struct SuspendedName : public MoveOnly { // The declaration name information. NameContext name_context; // Suspended scopes. We only preallocate space for two of these, because // suspended names are usually used for classes and functions with // unqualified names, which only need at most two scopes -- one scope for // the parameter and one scope for the entity itself, and we can store quite // a few of these when processing a large class definition. llvm::SmallVector scopes; }; explicit DeclNameStack(Context* context) : context_(context) {} // Pushes processing of a new declaration name, which will be used // contextually, and prepares to enter scopes for that name. To pop this // state, `FinishName` and `PopScope` must be called, in that order. auto PushScopeAndStartName() -> void; // Creates and returns a name context corresponding to declaring an // unqualified name in the current context. This is suitable for adding to // name lookup in situations where a qualified name is not permitted, such as // a pattern binding. auto MakeUnqualifiedName(SemIR::LocId loc_id, SemIR::NameId name_id) -> NameContext; // Applies a name component as a qualifier for the current name. This will // enter the scope corresponding to the name if the name describes an existing // scope, such as a namespace or a defined class. auto ApplyNameQualifier(const NameComponent& name) -> void; // Finishes the current declaration name processing, returning the final // context for adding the name to lookup. The final name component should be // popped and passed to this function, and will be added to the declaration // name. auto FinishName(const NameComponent& name) -> NameContext; // Finishes the current declaration name processing for an `impl`, returning // the final context for adding the name to lookup. // // `impl`s don't actually have names, but want the rest of the name processing // logic such as building parameter scopes, so are a special case. auto FinishImplName() -> NameContext; // Pops the declaration name from the declaration name stack, and pops all // scopes that were entered as part of handling the declaration name. These // are the scopes corresponding to name qualifiers in the name, for example // the `A.B` in `fn A.B.F()`. // // This should be called at the end of the declaration. // If check_unused is true, then performs unused bindings checks and emits // associated diagnostics. auto PopScope(bool check_unused = false) -> void; // Peeks the current parent scope of the name on top of the stack. Note // that if we're still processing the name qualifiers, this can change before // the name is completed. Also, if the name up to this point was already // declared and is a scope, this will be that scope, rather than the scope // containing it. auto PeekParentScopeId() const -> SemIR::NameScopeId { return decl_name_stack_.back().parent_scope_id; } // Peeks the resolution scope index of the name on top of the stack. auto PeekInitialScopeIndex() const -> ScopeIndex { return decl_name_stack_.back().initial_scope_index; } // Temporarily remove the current declaration name and its associated scopes // from the stack. Can only be called once the name is finished. auto Suspend() -> SuspendedName; // Restore a previously suspended name. auto Restore(SuspendedName&& sus) -> void; // Adds a name to name lookup. Assumes duplicates are already handled. auto AddName(NameContext name_context, SemIR::InstId target_id, SemIR::AccessKind access_kind) -> void; // Adds a name to name lookup. Prints a diagnostic for name conflicts. auto AddNameOrDiagnose(NameContext name_context, SemIR::InstId target_id, SemIR::AccessKind access_kind) -> void; // Adds a name to name lookup if neither already declared nor poisoned in this // scope. auto LookupOrAddName(NameContext name_context, SemIR::InstId target_id, SemIR::AccessKind access_kind) -> SemIR::ScopeLookupResult; // Runs verification that the processing cleanly finished. auto VerifyOnFinish() const -> void { CARBON_CHECK(decl_name_stack_.empty(), "decl_name_stack still has {0} entries", decl_name_stack_.size()); } private: // Returns a name context corresponding to an empty name. auto MakeEmptyNameContext() -> NameContext; // Appends a name to the given name context, and performs a lookup to find // what, if anything, the name refers to. auto ApplyAndLookupName(NameContext& name_context, SemIR::LocId loc_id, SemIR::NameId name_id) -> void; // Attempts to resolve the given name context as a scope, and returns the // corresponding scope. Issues a suitable diagnostic and returns `None` if // the name doesn't resolve to a scope. auto ResolveAsScope(const NameContext& name_context, const NameComponent& name) const -> std::pair; // The linked context. Context* context_; // Provides nesting for construction. llvm::SmallVector decl_name_stack_; }; } // namespace Carbon::Check #endif // CARBON_TOOLCHAIN_CHECK_DECL_NAME_STACK_H_ ================================================ FILE: toolchain/check/deduce.cpp ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #include "toolchain/check/deduce.h" #include "llvm/ADT/SmallBitVector.h" #include "toolchain/base/kind_switch.h" #include "toolchain/check/context.h" #include "toolchain/check/convert.h" #include "toolchain/check/generic.h" #include "toolchain/check/subst.h" #include "toolchain/check/type.h" #include "toolchain/diagnostics/diagnostic.h" #include "toolchain/sem_ir/ids.h" #include "toolchain/sem_ir/impl.h" #include "toolchain/sem_ir/type.h" #include "toolchain/sem_ir/typed_insts.h" namespace Carbon::Check { namespace { // A list of pairs of (instruction from generic, corresponding instruction from // call to of generic) for which we still need to perform deduction, along with // methods to add and pop pending deductions from the list. Deductions are // popped in order from most- to least-recently pushed, with the intent that // they are visited in depth-first order, although the order is not expected to // matter except when it influences which error is diagnosed. class DeductionWorklist { public: // `context` must not be null. explicit DeductionWorklist(Context* context) : context_(context) {} struct PendingDeduction { SemIR::InstId param; SemIR::InstId arg; }; // Adds a single (param, arg) deduction. auto Add(SemIR::InstId param, SemIR::InstId arg) -> void { deductions_.push_back({.param = param, .arg = arg}); } // Adds a single (param, arg) type deduction. auto Add(SemIR::TypeId param, SemIR::TypeId arg) -> void { Add(context_->types().GetTypeInstId(param), context_->types().GetTypeInstId(arg)); } // Adds a single (param, arg) deduction of a specific. auto Add(SemIR::SpecificId param, SemIR::SpecificId arg) -> void { if (!param.has_value() || !arg.has_value()) { return; } auto& param_specific = context_->specifics().Get(param); auto& arg_specific = context_->specifics().Get(arg); if (param_specific.generic_id != arg_specific.generic_id) { // TODO: Decide whether to error on this or just treat the specific as // non-deduced. For now we treat it as non-deduced. return; } AddAll(param_specific.args_id, arg_specific.args_id); } // Adds a list of (param, arg) deductions. These are added in reverse order so // they are popped in forward order. template auto AddAll(llvm::ArrayRef params, llvm::ArrayRef args) -> void { if (params.size() != args.size()) { // TODO: Decide whether to error on this or just treat the parameter list // as non-deduced. For now we treat it as non-deduced. return; } for (auto [param, arg] : llvm::reverse(llvm::zip_equal(params, args))) { Add(param, arg); } } auto AddAll(SemIR::InstBlockId params, llvm::ArrayRef args) -> void { AddAll(context_->inst_blocks().Get(params), args); } auto AddAll(SemIR::StructTypeFieldsId params, SemIR::StructTypeFieldsId args) -> void { const auto& param_fields = context_->struct_type_fields().Get(params); const auto& arg_fields = context_->struct_type_fields().Get(args); if (param_fields.size() != arg_fields.size()) { // TODO: Decide whether to error on this or just treat the parameter list // as non-deduced. For now we treat it as non-deduced. return; } // Don't do deduction unless the names match in order. // TODO: Support reordering of names. for (auto [param, arg] : llvm::zip_equal(param_fields, arg_fields)) { if (param.name_id != arg.name_id) { return; } } for (auto [param, arg] : llvm::reverse(llvm::zip_equal(param_fields, arg_fields))) { Add(param.type_inst_id, arg.type_inst_id); } } auto AddAll(SemIR::InstBlockId params, SemIR::InstBlockId args) -> void { AddAll(context_->inst_blocks().Get(params), context_->inst_blocks().Get(args)); } // Adds a (param, arg) pair for an instruction argument, given its kind. auto AddInstArg(SemIR::Inst::ArgAndKind param, int32_t arg) -> void { CARBON_KIND_SWITCH(param) { case SemIR::IdKind::None: case SemIR::IdKind::For: case SemIR::IdKind::For: break; case CARBON_KIND(SemIR::InstId inst_id): { Add(inst_id, SemIR::InstId(arg)); break; } case CARBON_KIND(SemIR::TypeInstId inst_id): { Add(inst_id, SemIR::InstId(arg)); break; } case CARBON_KIND(SemIR::StructTypeFieldsId fields_id): { AddAll(fields_id, SemIR::StructTypeFieldsId(arg)); break; } case CARBON_KIND(SemIR::InstBlockId inst_block_id): { AddAll(inst_block_id, SemIR::InstBlockId(arg)); break; } case CARBON_KIND(SemIR::SpecificId specific_id): { Add(specific_id, SemIR::SpecificId(arg)); break; } default: CARBON_FATAL("unexpected argument kind"); } } // Returns whether we have completed all deductions. auto Done() -> bool { return deductions_.empty(); } // Pops the next deduction. Requires `!Done()`. auto PopNext() -> PendingDeduction { return deductions_.pop_back_val(); } private: Context* context_; llvm::SmallVector deductions_; }; // State that is tracked throughout the deduction process. class DeductionContext { public: // Preparse to perform deduction. If an enclosing specific or self type // are provided, adds the corresponding arguments as known arguments that will // not be deduced. `context` must not be null. DeductionContext(Context* context, SemIR::LocId loc_id, SemIR::GenericId generic_id, SemIR::SpecificId enclosing_specific_id, bool diagnose); auto context() const -> Context& { return *context_; } // Adds a pending deduction of `param` from `arg`. `needs_substitution` // indicates whether we need to substitute known generic parameters into // `param`. template auto Add(ParamT param, ArgT arg) -> void { worklist_.Add(param, arg); } // Same as `Add` but for an array or block of operands. template auto AddAll(ParamT param, ArgT arg) -> void { worklist_.AddAll(param, arg); } // Performs all deductions in the deduction worklist. Returns whether // deduction succeeded. auto Deduce() -> bool; // Returns whether every generic parameter has a corresponding deduced generic // argument. If not, issues a suitable diagnostic. auto CheckDeductionIsComplete() -> bool; // Forms a specific corresponding to the deduced generic with the deduced // argument list. Must not be called before deduction is complete. auto MakeSpecific() -> SemIR::SpecificId; private: auto NoteInitializingParam(SemIR::InstId param_id, auto& builder) -> void { if (auto param = context().insts().TryGetAs( param_id)) { CARBON_DIAGNOSTIC(InitializingGenericParam, Note, "initializing generic parameter `{0}` declared here", SemIR::NameId); builder.Note(param_id, InitializingGenericParam, context().entity_names().Get(param->entity_name_id).name_id); } else { NoteGenericHere(context(), generic_id_, builder); } } Context* context_; SemIR::LocId loc_id_; SemIR::GenericId generic_id_; bool diagnose_; DeductionWorklist worklist_; llvm::SmallVector result_arg_ids_; llvm::SmallVector substitutions_; SemIR::CompileTimeBindIndex first_deduced_index_; // Non-deduced indexes, indexed by parameter index - first_deduced_index_. llvm::SmallBitVector non_deduced_indexes_; }; } // namespace static auto NoteGenericHere(Context& context, SemIR::GenericId generic_id, DiagnosticBuilder& diag) -> void { CARBON_DIAGNOSTIC(DeductionGenericHere, Note, "while deducing parameters of generic declared here"); diag.Note(context.generics().Get(generic_id).decl_id, DeductionGenericHere); } DeductionContext::DeductionContext(Context* context, SemIR::LocId loc_id, SemIR::GenericId generic_id, SemIR::SpecificId enclosing_specific_id, bool diagnose) : context_(context), loc_id_(loc_id), generic_id_(generic_id), diagnose_(diagnose), worklist_(context), first_deduced_index_(0) { CARBON_CHECK(generic_id.has_value(), "Performing deduction for non-generic entity"); // Initialize the deduced arguments to `None`. result_arg_ids_.resize( context->inst_blocks() .Get(context->generics().Get(generic_id_).bindings_id) .size(), SemIR::InstId::None); if (enclosing_specific_id.has_value()) { // Copy any outer generic arguments from the specified instance and prepare // to substitute them into the function declaration. auto args = context->inst_blocks().Get( context->specifics().Get(enclosing_specific_id).args_id); llvm::copy(args, result_arg_ids_.begin()); // TODO: Subst is linear in the length of the substitutions list. Change // it so we can pass in an array mapping indexes to substitutions instead. substitutions_.reserve(args.size() + result_arg_ids_.size()); for (auto [i, subst_inst_id] : llvm::enumerate(args)) { substitutions_.push_back( {.bind_id = SemIR::CompileTimeBindIndex(i), .replacement_id = context->constant_values().Get(subst_inst_id)}); } first_deduced_index_ = SemIR::CompileTimeBindIndex(args.size()); } non_deduced_indexes_.resize(result_arg_ids_.size() - first_deduced_index_.index); } auto DeductionContext::Deduce() -> bool { while (!worklist_.Done()) { auto [param_id, arg_id] = worklist_.PopNext(); // TODO: Bail out if there's nothing to deduce: if we're not in a pattern // and the parameter doesn't have a symbolic constant value. auto param_type_id = context().insts().Get(param_id).type_id(); if (context().types().Is(param_type_id)) { param_type_id = SemIR::ExtractScrutineeType(context().sem_ir(), param_type_id); } else if (context().types().IsFacetType(param_type_id)) { // Given `fn F[G:! Interface](g: G)`, the type of `g` is `G as type`. For // deduction, we want to ignore the `as type`, and check that the argument // can convert to the FacetType of the canonical facet value. param_id = GetCanonicalFacetOrTypeValue(context(), param_id); param_type_id = context().insts().Get(param_id).type_id(); } // If the parameter has a symbolic type, deduce against that. if (param_type_id.is_symbolic()) { // TODO: This looks liable to add redundant work (possibly even // exponential amounts of it) in some of the cases handled below. Add(context().types().GetTypeInstId(param_type_id), context().types().GetTypeInstId( context().insts().Get(arg_id).type_id())); } else { // The argument (e.g. a TupleLiteral of types) may be convertible to a // compile-time value (e.g. TupleType) that we can decompose further. // So we do this conversion here, even though we will later try convert // again when we have deduced all of the bindings. Diagnostics::AnnotationScope annotate_diagnostics( &context().emitter(), [&](auto& builder) { if (diagnose_) { NoteInitializingParam(param_id, builder); } }); // TODO: The call logic should reuse the conversion here (if any) instead // of doing the same conversion again. At the moment we throw away the // converted arg_id. arg_id = diagnose_ ? ConvertToValueOfType(context(), loc_id_, arg_id, param_type_id) : TryConvertToValueOfType(context(), loc_id_, arg_id, param_type_id); if (arg_id == SemIR::ErrorInst::InstId) { return false; } } // Attempt to match `param_inst` against `arg_id`. If the match succeeds, // this should `continue` the outer loop. On `break`, we will try to desugar // the parameter to continue looking for a match. auto param_inst = context().insts().Get(param_id); CARBON_KIND_SWITCH(param_inst) { // Deducing a symbolic binding pattern from an argument deduces the // binding as having that constant value. For example, deducing // `(T:! type)` against `(i32)` deduces `T` to be `i32`. This only arises // when initializing a generic parameter from an explicitly specified // argument, and in this case, the argument is required to be a // compile-time constant. case CARBON_KIND(SemIR::SymbolicBindingPattern bind): { auto& entity_name = context().entity_names().Get(bind.entity_name_id); auto index = entity_name.bind_index(); if (!index.has_value()) { break; } CARBON_CHECK( index >= first_deduced_index_ && static_cast(index.index) < result_arg_ids_.size(), "Unexpected index {0} for symbolic binding pattern; " "expected to be in range [{1}, {2})", index.index, first_deduced_index_.index, result_arg_ids_.size()); CARBON_CHECK(!result_arg_ids_[index.index].has_value(), "Deduced a value for parameter prior to its declaration"); auto arg_const_inst_id = context().constant_values().GetConstantInstId(arg_id); if (!arg_const_inst_id.has_value()) { if (diagnose_) { CARBON_DIAGNOSTIC(CompTimeArgumentNotConstant, Error, "argument for generic parameter is not a " "compile-time constant"); auto diag = context().emitter().Build(loc_id_, CompTimeArgumentNotConstant); NoteInitializingParam(param_id, diag); diag.Emit(); } return false; } result_arg_ids_[index.index] = arg_const_inst_id; // This parameter index should not be deduced if it appears later. non_deduced_indexes_[index.index - first_deduced_index_.index] = true; continue; } // Deducing a symbolic binding appearing within an expression against a // constant value deduces the binding as having that value. For example, // deducing `[T:! type](x: T)` against `("foo")` deduces `T` as `String`. case CARBON_KIND(SemIR::SymbolicBinding bind): { auto& entity_name = context().entity_names().Get(bind.entity_name_id); auto index = entity_name.bind_index(); if (!index.has_value() || index < first_deduced_index_ || non_deduced_indexes_[index.index - first_deduced_index_.index]) { break; } CARBON_CHECK(static_cast(index.index) < result_arg_ids_.size(), "Deduced value for unexpected index {0}; expected to " "deduce {1} arguments.", index, result_arg_ids_.size()); auto arg_const_inst_id = context().constant_values().GetConstantInstId(arg_id); if (arg_const_inst_id.has_value()) { if (result_arg_ids_[index.index].has_value() && result_arg_ids_[index.index] != arg_const_inst_id) { if (diagnose_) { // TODO: Include the two different deduced values. CARBON_DIAGNOSTIC(DeductionInconsistent, Error, "inconsistent deductions for value of generic " "parameter `{0}`", SemIR::NameId); auto diag = context().emitter().Build( loc_id_, DeductionInconsistent, entity_name.name_id); NoteGenericHere(context(), generic_id_, diag); diag.Emit(); } return false; } result_arg_ids_[index.index] = arg_const_inst_id; } continue; } case SemIR::StructValue::Kind: // TODO: Match field name order between param and arg. break; // TODO: Handle more cases. default: if (param_inst.kind().deduce_through()) { // Various kinds of parameter should match an argument of the same // form, if the operands all match. auto arg_inst = context().insts().Get(arg_id); if (arg_inst.kind() != param_inst.kind()) { break; } worklist_.AddInstArg(param_inst.arg0_and_kind(), arg_inst.arg0()); worklist_.AddInstArg(param_inst.arg1_and_kind(), arg_inst.arg1()); continue; } break; } // We didn't manage to deduce against the syntactic form of the parameter. // Convert it to a canonical constant value and try deducing against that. auto param_const_id = context().constant_values().Get(param_id); if (!param_const_id.has_value() || !param_const_id.is_symbolic()) { // It's not a symbolic constant. There's nothing here to deduce. continue; } auto param_const_inst_id = context().constant_values().GetInstId(param_const_id); if (param_const_inst_id != param_id) { Add(param_const_inst_id, arg_id); continue; } } return true; } // Gets the entity name of a generic binding. The generic binding may be an // imported instruction. static auto GetEntityNameForGenericBinding(Context& context, SemIR::InstId binding_id) -> SemIR::NameId { // If `binding_id` is imported (or referenced indirectly perhaps in the // future), it may not have an entity name. Get a canonical local instruction // from its constant value which does. binding_id = context.constant_values().GetConstantInstId(binding_id); if (auto bind_name = context.insts().TryGetAs(binding_id)) { return context.entity_names().Get(bind_name->entity_name_id).name_id; } else { CARBON_FATAL("Instruction without entity name in generic binding position"); } } auto DeductionContext::CheckDeductionIsComplete() -> bool { // Check we deduced an argument value for every parameter, and convert each // argument to match the final parameter type after substituting any deduced // types it depends on. for (auto&& [i, deduced_arg_id] : llvm::enumerate(llvm::MutableArrayRef(result_arg_ids_) .drop_front(first_deduced_index_.index))) { auto binding_index = first_deduced_index_.index + i; auto binding_id = context().inst_blocks().Get( context().generics().Get(generic_id_).bindings_id)[binding_index]; if (!deduced_arg_id.has_value()) { if (diagnose_) { CARBON_DIAGNOSTIC(DeductionIncomplete, Error, "cannot deduce value for generic parameter `{0}`", SemIR::NameId); auto diag = context().emitter().Build( loc_id_, DeductionIncomplete, GetEntityNameForGenericBinding(context(), binding_id)); NoteGenericHere(context(), generic_id_, diag); diag.Emit(); } return false; } // If the binding is symbolic it can refer to other earlier bindings in the // same generic, or from an enclosing specific. Substitute to replace those // and get a non-symbolic type in order for us to know the final type that // the argument needs to be converted to. // // Note that when typechecking a checked generic, the arguments can // still be symbolic, so the substitution would also be symbolic. We are // unable to get the final type for symbolic bindings until deducing with // non-symbolic arguments. // // TODO: If arguments of different values, but that _convert to_ the same // value, are deduced for the same symbolic binding, then we will fail // typechecking in Deduce() with conflicting types via the // `DeductionInconsistent` diagnostic. If we defer that check until after // all conversions are done (after the code below) then we won't diagnose // that incorrectly. auto binding_type_id = context().insts().Get(binding_id).type_id(); if (binding_type_id.is_symbolic()) { auto param_type_const_id = SubstConstant(context(), SemIR::LocId(binding_id), binding_type_id.AsConstantId(), substitutions_); CARBON_CHECK(param_type_const_id.has_value()); binding_type_id = context().types().GetTypeIdForTypeConstantId(param_type_const_id); Diagnostics::AnnotationScope annotate_diagnostics( &context().emitter(), [&](auto& builder) { if (diagnose_) { NoteInitializingParam(binding_id, builder); } }); auto converted_arg_id = diagnose_ ? ConvertToValueOfType(context(), loc_id_, deduced_arg_id, binding_type_id) : TryConvertToValueOfType(context(), loc_id_, deduced_arg_id, binding_type_id); // The conversion of the argument to the parameter type can fail after // applying the enclosing specific, in which case deduction fails. if (converted_arg_id == SemIR::ErrorInst::InstId) { return false; } // Replace the deduced arg with its value converted to the parameter // type. The conversion of the argument type must produce a constant value // to be used in deduction. if (auto const_inst_id = context().constant_values().GetConstantInstId(converted_arg_id); const_inst_id.has_value()) { deduced_arg_id = const_inst_id; } else { if (diagnose_) { CARBON_DIAGNOSTIC(RuntimeConversionDuringCompTimeDeduction, Error, "compile-time value requires runtime conversion, " "constructing value of type {0}", SemIR::TypeId); auto diag = context().emitter().Build( loc_id_, RuntimeConversionDuringCompTimeDeduction, binding_type_id); NoteGenericHere(context(), generic_id_, diag); diag.Emit(); } deduced_arg_id = SemIR::ErrorInst::InstId; } } substitutions_.push_back( {.bind_id = SemIR::CompileTimeBindIndex(binding_index), .replacement_id = context().constant_values().Get(deduced_arg_id)}); } return true; } auto DeductionContext::MakeSpecific() -> SemIR::SpecificId { // TODO: Convert the deduced values to the types of the bindings. return Check::MakeSpecific(context(), loc_id_, generic_id_, result_arg_ids_); } auto DeduceGenericCallArguments( Context& context, SemIR::LocId loc_id, SemIR::GenericId generic_id, SemIR::SpecificId enclosing_specific_id, [[maybe_unused]] SemIR::InstBlockId implicit_param_patterns_id, SemIR::InstBlockId param_patterns_id, [[maybe_unused]] SemIR::InstId self_id, llvm::ArrayRef arg_ids) -> SemIR::SpecificId { DeductionContext deduction(&context, loc_id, generic_id, enclosing_specific_id, /*diagnose=*/true); // Prepare to perform deduction of the explicit parameters against their // arguments. // TODO: Also perform deduction for type of self. deduction.AddAll(param_patterns_id, arg_ids); if (!deduction.Deduce() || !deduction.CheckDeductionIsComplete()) { return SemIR::SpecificId::None; } return deduction.MakeSpecific(); } auto DeduceImplArguments(Context& context, SemIR::LocId loc_id, const SemIR::Impl& impl, SemIR::ConstantId self_id, SemIR::SpecificId constraint_specific_id) -> SemIR::SpecificId { DeductionContext deduction(&context, loc_id, impl.generic_id, /*enclosing_specific_id=*/SemIR::SpecificId::None, /*diagnose=*/false); // Prepare to perform deduction of the type and interface. Use the canonical // `self_id` to save a trip through the deduce loop, which will then need to // get the canonical instruction. deduction.Add(context.constant_values().GetConstantInstId(impl.self_id), context.constant_values().GetInstId(self_id)); deduction.Add(impl.interface.specific_id, constraint_specific_id); // TODO: Deduce has side effects in the semir by generating `Converted` // instructions, and may also introduce intermediate states like // `FacetAccessType`. We should stop generating those when deducing for impl // lookup, but for now we discard them by pushing an InstBlock on the stack // and dropping it right after. We also need to avoid adding those dropped // instructions to any enclosing generic, so we push a fresh generic region. context.inst_block_stack().Push(); context.generic_region_stack().Push({.generic_id = SemIR::GenericId::None}); bool success = deduction.Deduce() && deduction.CheckDeductionIsComplete(); context.generic_region_stack().Pop(); context.inst_block_stack().PopAndDiscard(); if (!success) { return SemIR::SpecificId::None; } return deduction.MakeSpecific(); } } // namespace Carbon::Check ================================================ FILE: toolchain/check/deduce.h ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #ifndef CARBON_TOOLCHAIN_CHECK_DEDUCE_H_ #define CARBON_TOOLCHAIN_CHECK_DEDUCE_H_ #include "toolchain/check/context.h" #include "toolchain/sem_ir/ids.h" namespace Carbon::Check { // Deduces the generic arguments to use in a call to a generic. auto DeduceGenericCallArguments(Context& context, SemIR::LocId loc_id, SemIR::GenericId generic_id, SemIR::SpecificId enclosing_specific_id, SemIR::InstBlockId implicit_param_patterns_id, SemIR::InstBlockId param_patterns_id, SemIR::InstId self_id, llvm::ArrayRef arg_ids) -> SemIR::SpecificId; // Deduces the impl arguments to use in a use of a parameterized impl. Returns // `None` if deduction fails. auto DeduceImplArguments(Context& context, SemIR::LocId loc_id, const SemIR::Impl& impl, SemIR::ConstantId self_id, SemIR::SpecificId constraint_specific_id) -> SemIR::SpecificId; } // namespace Carbon::Check #endif // CARBON_TOOLCHAIN_CHECK_DEDUCE_H_ ================================================ FILE: toolchain/check/deferred_definition_worklist.cpp ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #include "toolchain/check/deferred_definition_worklist.h" #include #include #include #include "common/emplace_by_calling.h" #include "common/vlog.h" #include "toolchain/base/kind_switch.h" #include "toolchain/check/context.h" namespace Carbon::Check { static constexpr llvm::StringLiteral VlogPrefix = "DeferredDefinitionWorklist "; DeferredDefinitionWorklist::DeferredDefinitionWorklist( llvm::raw_ostream* vlog_stream) : vlog_stream_(vlog_stream) { // See declaration of `worklist_`. worklist_.reserve(64); } auto DeferredDefinitionWorklist::SuspendFunctionAndPush( Parse::DeferredDefinitionIndex index, llvm::function_refSuspendedFunction> suspend) -> void { worklist_.emplace_back(EmplaceByCalling([&] { return CheckSkippedDefinition{.definition_index = index, .suspended_fn = suspend()}; })); CARBON_VLOG("{0}Push CheckSkippedDefinition {1}\n", VlogPrefix, index.index); } auto DeferredDefinitionWorklist::SuspendThunkAndPush(Context& context, ThunkInfo info) -> void { worklist_.emplace_back(EmplaceByCalling([&] { return DefineThunk{.info = info, .scope = context.scope_stack().Suspend()}; })); CARBON_VLOG("{0}Push DefineThunk {1}\n", VlogPrefix, info.function_id); } auto DeferredDefinitionWorklist::PushEnterDeferredDefinitionScope( Context& context) -> bool { bool nested = !entered_scopes_.empty() && entered_scopes_.back().scope_index == context.decl_name_stack().PeekInitialScopeIndex(); entered_scopes_.push_back({.nested = nested, .worklist_start_index = worklist_.size(), .scope_index = context.scope_stack().PeekIndex()}); if (nested) { worklist_.emplace_back(EmplaceByCalling([&] { return EnterNestedDeferredDefinitionScope{.suspended_name = std::nullopt}; })); CARBON_VLOG("{0}Push EnterDeferredDefinitionScope (nested)\n", VlogPrefix); } else { // Don't push a task to re-enter a non-nested scope. Instead, // SuspendFinishedScopeAndPush will remain in the scope when executing the // worklist tasks. CARBON_VLOG("{0}Entered non-nested deferred definition scope\n", VlogPrefix); } return !nested; } auto DeferredDefinitionWorklist::SuspendFinishedScopeAndPush(Context& context) -> FinishedScopeKind { auto [nested, start_index, _] = entered_scopes_.pop_back_val(); // If we've not found any tasks to perform in this scope, clean up the stack. // For non-nested scope, there will be no tasks on the worklist for this scope // in this case; for a nested scope, there will just be a task to re-enter the // nested scope. if (!nested && start_index == worklist_.size()) { context.decl_name_stack().PopScope(); CARBON_VLOG("{0}Left non-nested empty deferred definition scope\n", VlogPrefix); return FinishedScopeKind::NonNestedEmpty; } if (nested && start_index == worklist_.size() - 1) { CARBON_CHECK(std::holds_alternative( worklist_.back())); worklist_.pop_back(); context.decl_name_stack().PopScope(); CARBON_VLOG("{0}Pop EnterNestedDeferredDefinitionScope (empty)\n", VlogPrefix); return FinishedScopeKind::Nested; } // If we're finishing a nested deferred definition scope, keep track of that // but don't type-check deferred definitions now. if (nested) { auto& enter_scope = get(worklist_[start_index]); // This is a nested deferred definition scope. Suspend the inner scope so we // can restore it when we come to type-check the deferred definitions. enter_scope.suspended_name.emplace( EmplaceByCalling([&] { return context.decl_name_stack().Suspend(); })); // Enqueue a task to leave the nested scope. worklist_.emplace_back(LeaveNestedDeferredDefinitionScope{}); CARBON_VLOG("{0}Push LeaveNestedDeferredDefinitionScope\n", VlogPrefix); return FinishedScopeKind::Nested; } // We're at the end of a non-nested deferred definition scope. Start checking // deferred definitions. CARBON_VLOG("{0}Starting deferred definition processing\n", VlogPrefix); return FinishedScopeKind::NonNestedWithWork; } } // namespace Carbon::Check ================================================ FILE: toolchain/check/deferred_definition_worklist.h ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #ifndef CARBON_TOOLCHAIN_CHECK_DEFERRED_DEFINITION_WORKLIST_H_ #define CARBON_TOOLCHAIN_CHECK_DEFERRED_DEFINITION_WORKLIST_H_ #include #include #include "common/ostream.h" #include "llvm/ADT/SmallVector.h" #include "toolchain/check/decl_name_stack.h" #include "toolchain/parse/tree.h" namespace Carbon::Check { // A worklist of pending tasks to perform to check deferred function definitions // in the right order. class DeferredDefinitionWorklist { public: // State saved for a function definition that has been suspended after // processing its declaration and before processing its body. This is used for // inline method handling. // // This type is large, so moves of this type should be avoided. struct SuspendedFunction : public MoveOnly { // The function that was declared. SemIR::FunctionId function_id; // The instruction ID of the FunctionDecl instruction. SemIR::InstId decl_id; // The declaration name information of the function. This includes the scope // information, such as parameter names. DeclNameStack::SuspendedName saved_name_state; }; // A worklist task that indicates we should check a deferred function // definition that we previously skipped. // // This type is large, so moves of this type should be avoided. struct CheckSkippedDefinition : public MoveOnly { // The definition that we skipped. Parse::DeferredDefinitionIndex definition_index; // The suspended function. SuspendedFunction suspended_fn; }; // A description of a thunk. struct ThunkInfo { SemIR::FunctionId signature_id; SemIR::FunctionId function_id; SemIR::InstId decl_id; SemIR::InstId callee_id; }; // A worklist task that indicates we should define a thunk that was previously // declared. // // This type is large, so moves of this type should be avoided. struct DefineThunk : public MoveOnly { ThunkInfo info; ScopeStack::SuspendedScope scope; }; // A worklist task that indicates we should enter a nested deferred definition // scope. We delay processing the contents of nested deferred definition // scopes until we reach the end of the parent scope. For example: // // ``` // class A { // class B { // fn F() -> A { return {}; } // } // } // A.B.F is type-checked here, with A complete. // // fn F() { // class C { // fn G() {} // } // C.G is type-checked here. // } // ``` // // This type is large, so moves of this type should be avoided. struct EnterNestedDeferredDefinitionScope : public MoveOnly { // The suspended scope. This is only set once we reach the end of the scope. std::optional suspended_name; }; // A worklist task that indicates we should leave a nested deferred definition // scope. struct LeaveNestedDeferredDefinitionScope {}; // A pending type-checking task. using Task = std::variant; explicit DeferredDefinitionWorklist(llvm::raw_ostream* vlog_stream); // Suspends the current function definition and pushes a task onto the // worklist to finish it later. auto SuspendFunctionAndPush( Parse::DeferredDefinitionIndex index, llvm::function_refSuspendedFunction> suspend) -> void; // Suspends the current thunk scope and pushes a task onto the worklist to // define it later. auto SuspendThunkAndPush(Context& context, ThunkInfo info) -> void; // Pushes a task to re-enter a function scope, so that functions defined // within it are type-checked in the right context. Returns whether a // non-nested scope was entered. auto PushEnterDeferredDefinitionScope(Context& context) -> bool; // The kind of scope that we just finished. enum class FinishedScopeKind { // We finished a nested scope. No further action is taken at this point. Nested, // We finished a non-nested scope that has no further actions to perform. NonNestedEmpty, // We finished a non-nested scope that has further actions to perform. NonNestedWithWork, }; // Suspends the current deferred definition scope, which is finished but still // on the decl_name_stack, and pushes a task to leave the scope when we're // type-checking deferred definitions. Returns `true` if the current list of // deferred definitions should be type-checked immediately. auto SuspendFinishedScopeAndPush(Context& context) -> FinishedScopeKind; // Returns the current size of the worklist. auto size() const -> size_t { return worklist_.size(); } // Truncates the worklist to the given size. auto truncate(int new_size) -> void { worklist_.truncate(new_size); } // Gets the given item on the worklist. auto operator[](int index) -> Task& { return worklist_[index]; } // CHECK that the work list has no further work. auto VerifyEmpty() { CARBON_CHECK(worklist_.empty() && entered_scopes_.empty(), "Tasks left behind on worklist."); } private: // A deferred definition scope that is currently still open. struct EnteredScope { // Whether this scope is nested immediately within the enclosing scope. If // so, deferred definitions are not processed at the end of this scope. bool nested; // The index in worklist_ of the first task in this scope. For a nested // scope, this is a EnterNestedDeferredDefinitionScope task. size_t worklist_start_index; // The corresponding lexical scope index. ScopeIndex scope_index; }; llvm::raw_ostream* vlog_stream_; // A worklist of type-checking tasks we'll need to do later. // // Don't allocate any inline storage here. A Task is fairly large, so we never // want this to live on the stack. Instead, we reserve space in the // constructor for a fairly large number of deferred definitions. llvm::SmallVector worklist_; // The deferred definition scopes for the current checking actions. llvm::SmallVector entered_scopes_; }; } // namespace Carbon::Check #endif // CARBON_TOOLCHAIN_CHECK_DEFERRED_DEFINITION_WORKLIST_H_ ================================================ FILE: toolchain/check/diagnostic_emitter.cpp ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #include "toolchain/check/diagnostic_emitter.h" #include #include #include #include "common/raw_string_ostream.h" #include "toolchain/check/diagnostic_helpers.h" #include "toolchain/sem_ir/absolute_node_id.h" #include "toolchain/sem_ir/diagnostic_loc_converter.h" #include "toolchain/sem_ir/ids.h" #include "toolchain/sem_ir/stringify.h" namespace Carbon::Check { auto DiagnosticEmitter::ConvertLoc(LocIdForDiagnostics loc_id, ContextFnT context_fn) const -> Diagnostics::ConvertedLoc { auto [imports, converted] = loc_converter_.ConvertWithImports( loc_id.loc_id(), loc_id.is_token_only()); for (const auto& import : imports) { CARBON_DIAGNOSTIC(InImport, LocationInfo, "in import"); CARBON_DIAGNOSTIC(InCppInclude, LocationInfo, "in file included here"); CARBON_DIAGNOSTIC(InCppModule, LocationInfo, "in module imported here"); CARBON_DIAGNOSTIC(InCppMacroExpansion, LocationInfo, "in expansion of macro defined here"); switch (import.kind) { case Carbon::SemIR::DiagnosticLocConverter::ImportLoc::Import: // TODO: Include the library name in the note. context_fn(import.loc, InImport); break; case Carbon::SemIR::DiagnosticLocConverter::ImportLoc::CppInclude: // TODO: Include the file name in the note. context_fn(import.loc, InCppInclude); break; case Carbon::SemIR::DiagnosticLocConverter::ImportLoc::CppModuleImport: // TODO: Include the module name in the note. context_fn(import.loc, InCppModule); break; case Carbon::SemIR::DiagnosticLocConverter::ImportLoc::CppMacroExpansion: // TODO: Include the macro name in the note. context_fn(import.loc, InCppMacroExpansion); break; } } // Use the token when possible, but -1 is the default value. auto last_offset = -1; if (last_token_.has_value()) { last_offset = sem_ir_->parse_tree().tokens().GetByteOffset(last_token_); } // When the diagnostic is in the same file, we use the last possible offset; // otherwise, we ignore the offset because it's probably in that file. if (converted.loc.filename == sem_ir_->filename()) { converted.last_byte_offset = std::max(converted.last_byte_offset, last_offset); } else { converted.last_byte_offset = last_offset; } return converted; } auto DiagnosticEmitter::ConvertArg(llvm::Any arg) const -> llvm::Any { if (auto* library_name_id = llvm::any_cast(&arg)) { std::string library_name; if (*library_name_id == SemIR::LibraryNameId::Default) { library_name = "default library"; } else if (!library_name_id->has_value()) { library_name = "library "; } else { RawStringOstream stream; stream << "library \"" << sem_ir_->string_literal_values().Get( library_name_id->AsStringLiteralValueId()) << "\""; library_name = stream.TakeStr(); } return library_name; } if (auto* name_id = llvm::any_cast(&arg)) { return sem_ir_->names().GetFormatted(*name_id).str(); } if (auto* type_of_expr = llvm::any_cast(&arg)) { if (!type_of_expr->inst_id.has_value()) { return ""; } // TODO: Where possible, produce a better description of the type based on // the expression. return "`" + StringifyConstantInst( *sem_ir_, sem_ir_->types().GetTypeInstId( sem_ir_->insts().Get(type_of_expr->inst_id).type_id())) + "`"; } if (auto* expr = llvm::any_cast(&arg)) { return "`" + StringifyConstantInst(*sem_ir_, expr->inst_id) + "`"; } if (auto* type_expr = llvm::any_cast(&arg)) { return StringifyConstantInst(*sem_ir_, type_expr->inst_id); } if (auto* type = llvm::any_cast(&arg)) { return StringifyConstantInst(*sem_ir_, sem_ir_->types().GetTypeInstId(type->type_id)); } if (auto* type_id = llvm::any_cast(&arg)) { return "`" + StringifyConstantInst(*sem_ir_, sem_ir_->types().GetTypeInstId(*type_id)) + "`"; } if (auto* facet_type_id = llvm::any_cast(&arg)) { return "`" + StringifyFacetType(*sem_ir_, *facet_type_id) + "`"; } if (auto* specific_id = llvm::any_cast(&arg)) { return "`" + StringifySpecific(*sem_ir_, *specific_id) + "`"; } if (auto* typed_int = llvm::any_cast(&arg)) { return llvm::APSInt(typed_int->value, !sem_ir_->types().IsSignedInt(typed_int->type)); } if (auto* real_id = llvm::any_cast(&arg)) { RawStringOstream out; sem_ir_->reals().Get(*real_id).Print(out); return out.TakeStr(); } if (auto* specific_interface = llvm::any_cast(&arg)) { return StringifySpecificInterface(*sem_ir_, *specific_interface); } if (auto* specific_interface_id = llvm::any_cast(&arg)) { auto specific_interface = sem_ir_->specific_interfaces().Get(*specific_interface_id); return "`" + StringifySpecificInterface(*sem_ir_, specific_interface) + "`"; } if (auto* specific_interface_raw = llvm::any_cast(&arg)) { auto specific_interface = sem_ir_->specific_interfaces().Get( specific_interface_raw->specific_interface_id); return StringifySpecificInterface(*sem_ir_, specific_interface); } return DiagnosticEmitterBase::ConvertArg(arg); } } // namespace Carbon::Check ================================================ FILE: toolchain/check/diagnostic_emitter.h ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #ifndef CARBON_TOOLCHAIN_CHECK_DIAGNOSTIC_EMITTER_H_ #define CARBON_TOOLCHAIN_CHECK_DIAGNOSTIC_EMITTER_H_ #include "llvm/ADT/ArrayRef.h" #include "toolchain/check/diagnostic_helpers.h" #include "toolchain/diagnostics/emitter.h" #include "toolchain/lex/token_index.h" #include "toolchain/sem_ir/diagnostic_loc_converter.h" namespace Carbon::Check { // Handles the transformation of a SemIR::LocId to a DiagnosticLoc. class DiagnosticEmitter : public DiagnosticEmitterBase { public: // `consumer`, `tree_and_subtrees_getters`, and `sem_ir` must be non-null. explicit DiagnosticEmitter( Diagnostics::Consumer* consumer, const Parse::GetTreeAndSubtreesStore* tree_and_subtrees_getters, const SemIR::File* sem_ir) : DiagnosticEmitterBase(consumer), sem_ir_(sem_ir), loc_converter_(tree_and_subtrees_getters, sem_ir) {} // If a byte offset is past the current last byte offset, advances forward. // Earlier offsets are ignored. auto AdvanceToken(Lex::TokenIndex token) -> void { last_token_ = std::max(last_token_, token); } protected: // Implements argument conversions for supported check-phase arguments. auto ConvertArg(llvm::Any arg) const -> llvm::Any override; // Implements `DiagnosticConverter::ConvertLoc`. Adds context for any imports // used in the current SemIR to get to the underlying code. // // For the last byte offset, this uses `last_token_` exclusively for imported // locations, or `loc` if it's in the same file and (for whatever reason) // later. auto ConvertLoc(LocIdForDiagnostics loc_id, ContextFnT context_fn) const -> Diagnostics::ConvertedLoc override; private: // The current SemIR being processed. const SemIR::File* sem_ir_; // Converter for locations. SemIR::DiagnosticLocConverter loc_converter_; // The last token encountered during processing. Lex::TokenIndex last_token_ = Lex::TokenIndex::None; }; } // namespace Carbon::Check #endif // CARBON_TOOLCHAIN_CHECK_DIAGNOSTIC_EMITTER_H_ ================================================ FILE: toolchain/check/diagnostic_helpers.h ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #ifndef CARBON_TOOLCHAIN_CHECK_DIAGNOSTIC_HELPERS_H_ #define CARBON_TOOLCHAIN_CHECK_DIAGNOSTIC_HELPERS_H_ #include #include "llvm/ADT/APSInt.h" #include "toolchain/parse/node_ids.h" #include "toolchain/sem_ir/ids.h" namespace Carbon::Check { // The `DiagnosticEmitterBase` is templated on this type so that // diagnostics can be passed an `InstId` as a location, without having to // explicitly construct a `LocId` from it first. class LocIdForDiagnostics { public: // Constructs a token-only location for a diagnostic. // // This means the displayed location will include only the location's specific // parse node, instead of also including its descendants. static auto TokenOnly(Parse::NodeId node_id) -> LocIdForDiagnostics { return LocIdForDiagnostics(SemIR::LocId(node_id), true); } template requires std::constructible_from explicit(false) LocIdForDiagnostics(LocT loc_id) : LocIdForDiagnostics(SemIR::LocId(loc_id), false) {} auto loc_id() const -> SemIR::LocId { return loc_id_; } auto is_token_only() const -> bool { return is_token_only_; } private: explicit LocIdForDiagnostics(SemIR::LocId loc_id, bool is_token_only) : loc_id_(loc_id), is_token_only_(is_token_only) {} SemIR::LocId loc_id_; bool is_token_only_; }; // We define the emitter separately for dependencies, so only provide a base // here. using DiagnosticEmitterBase = Diagnostics::Emitter; using DiagnosticBuilder = DiagnosticEmitterBase::Builder; using DiagnosticContextBuilder = DiagnosticEmitterBase::ContextBuilder; // A function that adds a Context message for a diagnostic. using DiagnosticContextFn = llvm::function_refvoid>; // An expression with a constant value, for rendering in a diagnostic. The // diagnostic rendering will include enclosing "`"s. struct InstIdAsConstant { using DiagnosticType = Diagnostics::TypeInfo; explicit(false) InstIdAsConstant(SemIR::InstId inst_id) : inst_id(inst_id) {} SemIR::InstId inst_id; }; // An expression whose type should be rendered in a diagnostic. The diagnostic // rendering will include enclosing "`"s, and may also include extra information // about the type if it might otherwise be ambiguous or context-dependent, such // as the targets of aliases used in the type. // // TODO: Include such additional information where relevant. For example: // "`StdString` (aka `Cpp.std.basic_string(Char)`)". // // This should be used instead of `TypeId` as a diagnostic argument wherever // possible, because we should eventually be able to produce a sugared type name // in this case, whereas a `TypeId` will render as a canonical type. struct TypeOfInstId { using DiagnosticType = Diagnostics::TypeInfo; explicit(false) TypeOfInstId(SemIR::InstId inst_id) : inst_id(inst_id) {} SemIR::InstId inst_id; }; // A type expression, for rendering in a diagnostic. The diagnostic rendering // will include enclosing "`"s, and may also include extra information about the // type if it would otherwise be ambiguous. // // TODO: Include such additional information where relevant. // // This should be used when the source expression used to construct a type is // available. // // Note that this is currently an alias for InstIdAsConstant. However, using // InstIdAsType is clearer when defining CARBON_DIAGNOSTICs, and we may wish to // distinguish type arguments in diagnostics from more general constants in some // way in the future. using InstIdAsType = InstIdAsConstant; // A type expression, for rendering in a diagnostic as a raw type. When // formatting as a raw type in a diagnostic, the type will be formatted as a // simple Carbon expression, without enclosing "`"s. Once we start including // extra information about types, such annotations will also not be included for // raw types. // // This is intended for cases where the type is part of a larger syntactic // construct in a diagnostic, such as "redefinition of `impl {0} as {1}`". struct InstIdAsRawType { using DiagnosticType = Diagnostics::TypeInfo; explicit(false) InstIdAsRawType(SemIR::InstId inst_id) : inst_id(inst_id) {} SemIR::InstId inst_id; }; // A type value for rendering in a diagnostic without enclosing "`"s. See // `InstIdAsRawType` for details on raw type formatting. // // As with `TypeId`, this should be avoided as a diagnostic argument where // possible, because it can't be formatted with syntactic sugar such as aliases // that describe how the type was written. struct TypeIdAsRawType { using DiagnosticType = Diagnostics::TypeInfo; explicit(false) TypeIdAsRawType(SemIR::TypeId type_id) : type_id(type_id) {} SemIR::TypeId type_id; }; // An integer value together with its type. The type is used to determine how to // format the value in diagnostics. struct TypedInt { using DiagnosticType = Diagnostics::TypeInfo; SemIR::TypeId type; llvm::APInt value; }; struct SpecificInterfaceIdAsRawType { using DiagnosticType = Diagnostics::TypeInfo; explicit(false) SpecificInterfaceIdAsRawType( SemIR::SpecificInterfaceId specific_interface_id) : specific_interface_id(specific_interface_id) {} SemIR::SpecificInterfaceId specific_interface_id; }; } // namespace Carbon::Check #endif // CARBON_TOOLCHAIN_CHECK_DIAGNOSTIC_HELPERS_H_ ================================================ FILE: toolchain/check/dump.cpp ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // This library contains functions to assist dumping objects to stderr during // interactive debugging. Functions named `Dump` are intended for direct use by // developers, and should use overload resolution to determine which will be // invoked. The debugger should do namespace resolution automatically. For // example: // // - lldb: `expr Dump(context, id)` // - gdb: `call Dump(context, id)` #ifndef NDEBUG #include "toolchain/lex/dump.h" #include #include "common/check.h" #include "common/raw_string_ostream.h" #include "toolchain/check/context.h" #include "toolchain/lex/tokenized_buffer.h" #include "toolchain/parse/dump.h" #include "toolchain/parse/tree.h" #include "toolchain/sem_ir/dump.h" #include "toolchain/sem_ir/file.h" namespace Carbon::Check { LLVM_DUMP_METHOD static auto Dump(const Context& context, Lex::TokenIndex token) -> std::string { return Parse::Dump(context.parse_tree(), token); } LLVM_DUMP_METHOD static auto Dump(const Context& context, Parse::NodeId node_id) -> std::string { return Parse::Dump(context.parse_tree(), node_id); } LLVM_DUMP_METHOD static auto Dump(const Context& context, SemIR::ClassId class_id) -> std::string { return SemIR::Dump(context.sem_ir(), class_id); } LLVM_DUMP_METHOD static auto Dump(const Context& context, SemIR::ConstantId const_id) -> std::string { return SemIR::Dump(context.sem_ir(), const_id); } LLVM_DUMP_METHOD static auto Dump(const Context& context, SemIR::EntityNameId entity_name_id) -> std::string { return SemIR::Dump(context.sem_ir(), entity_name_id); } LLVM_DUMP_METHOD static auto Dump(const Context& context, SemIR::FacetTypeId facet_type_id) -> std::string { return SemIR::Dump(context.sem_ir(), facet_type_id); } LLVM_DUMP_METHOD static auto Dump(const Context& context, SemIR::FunctionId function_id) -> std::string { return SemIR::Dump(context.sem_ir(), function_id); } LLVM_DUMP_METHOD static auto Dump(const Context& context, SemIR::GenericId generic_id) -> std::string { return SemIR::Dump(context.sem_ir(), generic_id); } LLVM_DUMP_METHOD static auto Dump( const Context& context, SemIR::IdentifiedFacetTypeId identified_facet_type_id) -> std::string { return SemIR::Dump(context.sem_ir(), identified_facet_type_id); } LLVM_DUMP_METHOD static auto Dump(const Context& context, SemIR::ImplId impl_id) -> std::string { return SemIR::Dump(context.sem_ir(), impl_id); } LLVM_DUMP_METHOD static auto Dump(const Context& context, SemIR::InstBlockId inst_block_id) -> std::string { return SemIR::Dump(context.sem_ir(), inst_block_id); } LLVM_DUMP_METHOD static auto Dump(const Context& context, SemIR::InstId inst_id) -> std::string { return SemIR::Dump(context.sem_ir(), inst_id); } LLVM_DUMP_METHOD static auto Dump(const Context& context, SemIR::InterfaceId interface_id) -> std::string { return SemIR::Dump(context.sem_ir(), interface_id); } LLVM_DUMP_METHOD static auto Dump(const Context& context, SemIR::LocId loc_id) -> std::string { return SemIR::Dump(context.sem_ir(), loc_id); } LLVM_DUMP_METHOD static auto Dump(const Context& context, SemIR::NameId name_id) -> std::string { return SemIR::Dump(context.sem_ir(), name_id); } LLVM_DUMP_METHOD static auto Dump(const Context& context, SemIR::NameScopeId name_scope_id) -> std::string { return SemIR::Dump(context.sem_ir(), name_scope_id); } LLVM_DUMP_METHOD static auto Dump(const Context& context, SemIR::NamedConstraintId named_constraint_id) -> std::string { return SemIR::Dump(context.sem_ir(), named_constraint_id); } LLVM_DUMP_METHOD static auto Dump( const Context& context, SemIR::RequireImplsBlockId require_impls_block_id) -> std::string { return SemIR::Dump(context.sem_ir(), require_impls_block_id); } LLVM_DUMP_METHOD static auto Dump(const Context& context, SemIR::RequireImplsId require_impls_id) -> std::string { return SemIR::Dump(context.sem_ir(), require_impls_id); } LLVM_DUMP_METHOD static auto Dump(const Context& context, SemIR::SpecificId specific_id) -> std::string { return SemIR::Dump(context.sem_ir(), specific_id); } LLVM_DUMP_METHOD static auto Dump( const Context& context, SemIR::SpecificInterfaceId specific_interface_id) -> std::string { return SemIR::Dump(context.sem_ir(), specific_interface_id); } LLVM_DUMP_METHOD static auto Dump( const Context& context, SemIR::StructTypeFieldsId struct_type_fields_id) -> std::string { return SemIR::Dump(context.sem_ir(), struct_type_fields_id); } LLVM_DUMP_METHOD static auto Dump(const Context& context, SemIR::TypeId type_id) -> std::string { return SemIR::Dump(context.sem_ir(), type_id); } } // namespace Carbon::Check #endif // NDEBUG ================================================ FILE: toolchain/check/eval.cpp ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #include "toolchain/check/eval.h" #include #include #include #include #include "common/raw_string_ostream.h" #include "llvm/Support/ConvertUTF.h" #include "toolchain/base/canonical_value_store.h" #include "toolchain/base/kind_switch.h" #include "toolchain/check/action.h" #include "toolchain/check/cpp/constant.h" #include "toolchain/check/diagnostic_helpers.h" #include "toolchain/check/eval_inst.h" #include "toolchain/check/facet_type.h" #include "toolchain/check/generic.h" #include "toolchain/check/import_ref.h" #include "toolchain/check/name_lookup.h" #include "toolchain/check/type.h" #include "toolchain/check/type_completion.h" #include "toolchain/diagnostics/diagnostic.h" #include "toolchain/diagnostics/emitter.h" #include "toolchain/diagnostics/format_providers.h" #include "toolchain/sem_ir/builtin_function_kind.h" #include "toolchain/sem_ir/constant.h" #include "toolchain/sem_ir/facet_type_info.h" #include "toolchain/sem_ir/function.h" #include "toolchain/sem_ir/generic.h" #include "toolchain/sem_ir/id_kind.h" #include "toolchain/sem_ir/ids.h" #include "toolchain/sem_ir/impl.h" #include "toolchain/sem_ir/inst_categories.h" #include "toolchain/sem_ir/inst_kind.h" #include "toolchain/sem_ir/typed_insts.h" namespace Carbon::Check { namespace { // Information about an eval block of a specific that we are currently building. struct SpecificEvalInfo { // The region within the specific whose eval block we are building. SemIR::GenericInstIndex::Region region; // The work-in-progress contents of the eval block. llvm::ArrayRef values; }; // Information about a local scope that we're currently evaluating, such as a // call to an `eval fn`. In this scope, instructions with runtime phase may // locally have constant values, for example values that are computed from the // arguments to the call. These values are specific to the current evaluation // and not global properties of the instruction. struct LocalEvalInfo { // A mapping from instructions with runtime phase within the local scope to // the values that they have in the current evaluation. This is populated as // the local scope is evaluated, and due to control flow, the same instruction // may have its value set multiple times. This map tracks the most recent // value that the instruction had, which is the one that a reference to it in // well-formed SemIR should refer to. Map* locals; }; // Information about the context within which we are performing evaluation. // `context` must not be null. class EvalContext { public: explicit EvalContext( Context* context, SemIR::LocId fallback_loc_id, SemIR::SpecificId specific_id = SemIR::SpecificId::None, std::optional specific_eval_info = std::nullopt) : context_(context), fallback_loc_id_(fallback_loc_id), specific_id_(specific_id), specific_eval_info_(specific_eval_info) {} EvalContext(const EvalContext&) = delete; auto operator=(const EvalContext&) -> EvalContext& = delete; // Gets the location to use for diagnostics if a better location is // unavailable. // TODO: This is also sometimes unavailable. auto fallback_loc_id() const -> SemIR::LocId { return fallback_loc_id_; } // Returns a location to use to point at an instruction in a diagnostic, given // a list of instructions that might have an attached location. This is the // location of the first instruction in the list that has a location if there // is one, and otherwise the fallback location. auto GetDiagnosticLoc(llvm::ArrayRef inst_ids) -> SemIR::LocId { for (auto inst_id : inst_ids) { if (inst_id.has_value()) { auto loc_id = context_->insts().GetCanonicalLocId(inst_id); if (loc_id.has_value()) { return loc_id; } } } return fallback_loc_id_; } // Gets the value of the specified compile-time binding in this context. // Returns `None` if the value is not fixed in this context. auto GetCompileTimeBindValue(SemIR::CompileTimeBindIndex bind_index) -> SemIR::ConstantId { if (!bind_index.has_value() || !specific_id_.has_value()) { return SemIR::ConstantId::None; } const auto& specific = specifics().Get(specific_id_); auto args = inst_blocks().Get(specific.args_id); // Bindings past the ones with known arguments can appear as local // bindings of entities declared within this generic. if (static_cast(bind_index.index) >= args.size()) { return SemIR::ConstantId::None; } return constant_values().Get(args[bind_index.index]); } // Given information about a symbolic constant, determine its value in the // currently-being-evaluated eval block, if it refers to that eval block. If // we can't find a value in this way, returns `None`. auto GetInEvaluatedSpecific(const SemIR::SymbolicConstant& symbolic_info) -> SemIR::ConstantId { if (!specific_eval_info_ || !symbolic_info.index.has_value()) { return SemIR::ConstantId::None; } CARBON_CHECK( symbolic_info.generic_id == specifics().Get(specific_id_).generic_id, "Instruction has constant operand in wrong generic"); if (symbolic_info.index.region() != specific_eval_info_->region) { return SemIR::ConstantId::None; } auto inst_id = specific_eval_info_->values[symbolic_info.index.index()]; CARBON_CHECK(inst_id.has_value(), "Forward reference in eval block: index {0} referenced " "before evaluation", symbolic_info.index.index()); return constant_values().Get(inst_id); } // Gets the constant value of the specified instruction in this context. auto GetConstantValue(SemIR::InstId inst_id) -> SemIR::ConstantId { auto const_id = constant_values().GetAttached(inst_id); // While evaluating a function, map from local non-constant instructions to // their earlier-evaluated values. if (!const_id.is_constant()) { if (local_eval_info_) { if (auto local = local_eval_info_->locals->Lookup(inst_id)) { return local.value(); } } return const_id; } if (!const_id.is_symbolic()) { return const_id; } // While resolving a specific, map from previous instructions in the eval // block into their evaluated values. These values won't be present on the // specific itself yet, so `GetConstantValueInSpecific` won't be able to // find them. const auto& symbolic_info = constant_values().GetSymbolicConstant(const_id); if (auto eval_block_const_id = GetInEvaluatedSpecific(symbolic_info); eval_block_const_id.has_value()) { return eval_block_const_id; } return GetConstantValueInSpecific(sem_ir(), specific_id_, inst_id); } // Gets the type of the specified instruction in this context. auto GetTypeOfInst(SemIR::InstId inst_id) -> SemIR::TypeId { auto type_id = insts().GetAttachedType(inst_id); if (!type_id.is_symbolic()) { return type_id; } // While resolving a specific, map from previous instructions in the eval // block into their evaluated values. These values won't be present on the // specific itself yet, so `GetTypeOfInstInSpecific` won't be able to // find them. const auto& symbolic_info = constant_values().GetSymbolicConstant(types().GetConstantId(type_id)); if (auto eval_block_const_id = GetInEvaluatedSpecific(symbolic_info); eval_block_const_id.has_value()) { return types().GetTypeIdForTypeConstantId(eval_block_const_id); } return GetTypeOfInstInSpecific(sem_ir(), specific_id_, inst_id); } auto ints() -> SharedValueStores::IntStore& { return sem_ir().ints(); } auto floats() -> SharedValueStores::FloatStore& { return sem_ir().floats(); } auto entity_names() -> SemIR::EntityNameStore& { return sem_ir().entity_names(); } auto functions() -> const SemIR::FunctionStore& { return sem_ir().functions(); } auto classes() -> const SemIR::ClassStore& { return sem_ir().classes(); } auto interfaces() -> const SemIR::InterfaceStore& { return sem_ir().interfaces(); } auto specific_interfaces() -> SemIR::SpecificInterfaceStore& { return sem_ir().specific_interfaces(); } auto facet_types() -> SemIR::FacetTypeInfoStore& { return sem_ir().facet_types(); } auto generics() -> const SemIR::GenericStore& { return sem_ir().generics(); } auto specifics() -> const SemIR::SpecificStore& { return sem_ir().specifics(); } auto insts() -> const SemIR::InstStore& { return sem_ir().insts(); } auto inst_blocks() -> SemIR::InstBlockStore& { return sem_ir().inst_blocks(); } // Gets the constant value store. Note that this does not provide the constant // values that should be used from this evaluation context, and so should be // used with caution. auto constant_values() -> const SemIR::ConstantValueStore& { return sem_ir().constant_values(); } // Gets the types store. Note that this does not provide the type values that // should be used from this evaluation context, and so should be used with // caution. auto types() -> const SemIR::TypeStore& { return sem_ir().types(); } auto context() -> Context& { return *context_; } auto sem_ir() -> SemIR::File& { return context().sem_ir(); } auto emitter() -> DiagnosticEmitterBase& { return context().emitter(); } protected: explicit EvalContext(Context* context, SemIR::LocId fallback_loc_id, SemIR::SpecificId specific_id, std::optional local_eval_info) : context_(context), fallback_loc_id_(fallback_loc_id), specific_id_(specific_id), local_eval_info_(local_eval_info) {} // Returns the current locals map, which is assumed to exist. auto locals() -> Map& { return *local_eval_info_->locals; } private: // The type-checking context in which we're performing evaluation. Context* context_; // The location to use for diagnostics when a better location isn't available. SemIR::LocId fallback_loc_id_; // The specific that we are evaluating within. SemIR::SpecificId specific_id_; // If we are currently evaluating an eval block for `specific_id_`, // information about that evaluation. std::optional specific_eval_info_; // If we are currently evaluating within a local scope, values of local // instructions that have already been evaluated. This is here rather than in // `FunctionEvalContext` so we can reference it from `GetConstantValue`. std::optional local_eval_info_; }; } // namespace namespace { // The evaluation phase for an expression, computed by evaluation. These are // ordered so that the phase of an expression is the numerically highest phase // of its constituent evaluations. Note that an expression with any runtime // component is known to have Runtime phase even if it involves an evaluation // with UnknownDueToError phase. enum class Phase : uint8_t { // Value could be entirely and concretely computed. Concrete, // Evaluation phase is symbolic because the expression involves specifically a // reference to `.Self`. PeriodSelfSymbolic, // Evaluation phase is symbolic because the expression involves a reference to // a non-template symbolic binding other than `.Self`. CheckedSymbolic, // Evaluation phase is symbolic because the expression involves a reference to // a template parameter, or otherwise depends on something template dependent. // The expression might also reference non-template symbolic bindings. TemplateSymbolic, // The evaluation phase is unknown because evaluation encountered an // already-diagnosed semantic or syntax error. This is treated as being // potentially constant, but with an unknown phase. UnknownDueToError, // The expression has runtime phase because of a non-constant subexpression. Runtime, }; } // namespace static auto IsConstantOrError(Phase phase) -> bool { return phase != Phase::Runtime; } // Gets the phase in which the value of a constant will become available. static auto GetPhase(const SemIR::ConstantValueStore& constant_values, SemIR::ConstantId constant_id) -> Phase { if (!constant_id.is_constant()) { return Phase::Runtime; } else if (constant_id == SemIR::ErrorInst::ConstantId) { return Phase::UnknownDueToError; } switch (constant_values.GetDependence(constant_id)) { case SemIR::ConstantDependence::None: return Phase::Concrete; case SemIR::ConstantDependence::PeriodSelf: return Phase::PeriodSelfSymbolic; case SemIR::ConstantDependence::Checked: return Phase::CheckedSymbolic; case SemIR::ConstantDependence::Template: return Phase::TemplateSymbolic; } } // Returns the later of two phases. static auto LatestPhase(Phase a, Phase b) -> Phase { return static_cast( std::max(static_cast(a), static_cast(b))); } // Forms a `constant_id` describing a given evaluation result. static auto MakeConstantResult(Context& context, SemIR::Inst inst, Phase phase) -> SemIR::ConstantId { switch (phase) { case Phase::Concrete: return context.constants().GetOrAdd(inst, SemIR::ConstantDependence::None); case Phase::PeriodSelfSymbolic: return context.constants().GetOrAdd( inst, SemIR::ConstantDependence::PeriodSelf); case Phase::CheckedSymbolic: return context.constants().GetOrAdd(inst, SemIR::ConstantDependence::Checked); case Phase::TemplateSymbolic: return context.constants().GetOrAdd(inst, SemIR::ConstantDependence::Template); case Phase::UnknownDueToError: return SemIR::ErrorInst::ConstantId; case Phase::Runtime: return SemIR::ConstantId::NotConstant; } } // Forms a `constant_id` describing why an evaluation was not constant. static auto MakeNonConstantResult(Phase phase) -> SemIR::ConstantId { return phase == Phase::UnknownDueToError ? SemIR::ErrorInst::ConstantId : SemIR::ConstantId::NotConstant; } // Forms a constant for an empty tuple value. static auto MakeEmptyTupleResult(EvalContext& eval_context) -> SemIR::ConstantId { auto type_id = GetTupleType(eval_context.context(), {}); return MakeConstantResult( eval_context.context(), SemIR::TupleValue{.type_id = type_id, .elements_id = SemIR::InstBlockId::Empty}, Phase::Concrete); } // Converts a bool value into a ConstantId. static auto MakeBoolResult(Context& context, SemIR::TypeId bool_type_id, bool result) -> SemIR::ConstantId { return MakeConstantResult( context, SemIR::BoolLiteral{.type_id = bool_type_id, .value = SemIR::BoolValue::From(result)}, Phase::Concrete); } // Converts an APInt value into a ConstantId. static auto MakeIntResult(Context& context, SemIR::TypeId type_id, bool is_signed, llvm::APInt value) -> SemIR::ConstantId { CARBON_CHECK(is_signed == context.types().IsSignedInt(type_id)); auto result = is_signed ? context.ints().AddSigned(std::move(value)) : context.ints().AddUnsigned(std::move(value)); return MakeConstantResult( context, SemIR::IntValue{.type_id = type_id, .int_id = result}, Phase::Concrete); } // Converts an APFloat value into a ConstantId. static auto MakeFloatResult(Context& context, SemIR::TypeId type_id, llvm::APFloat value) -> SemIR::ConstantId { auto result = context.floats().Add(std::move(value)); return MakeConstantResult( context, SemIR::FloatValue{.type_id = type_id, .float_id = result}, Phase::Concrete); } // Creates a FacetType constant. static auto MakeFacetTypeResult(Context& context, const SemIR::FacetTypeInfo& info, Phase phase) -> SemIR::ConstantId { SemIR::FacetTypeId facet_type_id = context.facet_types().Add(info); return MakeConstantResult(context, SemIR::FacetType{.type_id = SemIR::TypeType::TypeId, .facet_type_id = facet_type_id}, phase); } // `GetConstantValue` checks to see whether the provided ID describes a value // with constant phase, and if so, returns the corresponding constant value. // Overloads are provided for different kinds of ID. `RequireConstantValue` does // the same, but produces an error diagnostic if the input is not constant. // AbsoluteInstId can not have its values substituted, so this overload is // deleted. This prevents conversion to InstId. static auto GetConstantValue(EvalContext& eval_context, SemIR::AbsoluteInstId inst_id, Phase* phase) -> SemIR::InstId = delete; // If the given instruction is constant, returns its constant value. static auto GetConstantValue(EvalContext& eval_context, SemIR::InstId inst_id, Phase* phase) -> SemIR::InstId { if (!inst_id.has_value()) { return SemIR::InstId::None; } auto const_id = eval_context.GetConstantValue(inst_id); *phase = LatestPhase(*phase, GetPhase(eval_context.constant_values(), const_id)); return eval_context.constant_values().GetInstId(const_id); } // Issue a suitable diagnostic for an instruction that evaluated to a // non-constant value but was required to evaluate to a constant. static auto DiagnoseNonConstantValue(EvalContext& eval_context, SemIR::InstId inst_id) -> void { if (inst_id != SemIR::ErrorInst::InstId) { CARBON_DIAGNOSTIC(EvalRequiresConstantValue, Error, "expression is runtime; expected constant"); eval_context.emitter().Emit(eval_context.GetDiagnosticLoc({inst_id}), EvalRequiresConstantValue); } } // Gets a constant value for an `inst_id`, diagnosing when the input is not a // constant value. static auto RequireConstantValue(EvalContext& eval_context, SemIR::InstId inst_id, Phase* phase) -> SemIR::InstId { if (!inst_id.has_value()) { return SemIR::InstId::None; } auto const_id = eval_context.GetConstantValue(inst_id); *phase = LatestPhase(*phase, GetPhase(eval_context.constant_values(), const_id)); if (const_id.is_constant()) { return eval_context.constant_values().GetInstId(const_id); } DiagnoseNonConstantValue(eval_context, inst_id); *phase = Phase::UnknownDueToError; return SemIR::ErrorInst::InstId; } // If the given instruction is constant, returns its constant value. Otherwise, // produces an error diagnostic. When determining the phase of the result, // ignore any dependence on `.Self`. // // This is used when evaluating facet types, for which `where` expressions using // `.Self` should not be considered symbolic // - `Interface where .Self impls I and .A = bool` -> concrete // - `T:! type` ... `Interface where .A = T` -> symbolic, since uses `T` which // is symbolic and not due to `.Self`. static auto RequireConstantValueIgnoringPeriodSelf(EvalContext& eval_context, SemIR::InstId inst_id, Phase* phase) -> SemIR::InstId { if (!inst_id.has_value()) { return SemIR::InstId::None; } Phase constant_phase = *phase; auto const_inst_id = RequireConstantValue(eval_context, inst_id, &constant_phase); // Since LatestPhase(x, Phase::Concrete) == x, this is equivalent to replacing // Phase::PeriodSelfSymbolic with Phase::Concrete. if (constant_phase != Phase::PeriodSelfSymbolic) { *phase = LatestPhase(*phase, constant_phase); } return const_inst_id; } // Gets a constant value for an `inst_id`, diagnosing when the input is not // constant, and CHECKing that it is concrete. Should only be used in contexts // where non-concrete constants cannot appear. static auto CheckConcreteValue(EvalContext& eval_context, SemIR::InstId inst_id) -> SemIR::InstId { auto phase = Phase::Concrete; auto value_inst_id = RequireConstantValue(eval_context, inst_id, &phase); if (phase == Phase::UnknownDueToError) { return SemIR::ErrorInst::InstId; } CARBON_CHECK(phase == Phase::Concrete, "expression evaluates to symbolic value {0}", eval_context.insts().Get(value_inst_id)); return value_inst_id; } // Find the instruction that the given instruction instantiates to, and return // that. static auto GetConstantValue(EvalContext& eval_context, SemIR::MetaInstId inst_id, Phase* phase) -> SemIR::MetaInstId { Phase inner_phase = Phase::Concrete; if (auto const_inst_id = GetConstantValue(eval_context, SemIR::InstId(inst_id), &inner_phase); const_inst_id.has_value()) { // The instruction has a constant value. Use that as the operand of the // action. *phase = LatestPhase(*phase, inner_phase); return const_inst_id; } // If this instruction is splicing in an action result, that action result is // our operand. if (auto splice = eval_context.insts().TryGetAs(inst_id)) { if (auto spliced_inst_id = GetConstantValue(eval_context, splice->inst_id, phase); spliced_inst_id.has_value()) { if (auto inst_value_id = eval_context.insts().TryGetAs( spliced_inst_id)) { return inst_value_id->inst_id; } } } // Otherwise, this is a normal instruction. if (OperandIsDependent(eval_context.context(), inst_id)) { *phase = LatestPhase(*phase, Phase::TemplateSymbolic); } return inst_id; } static auto GetConstantValue(EvalContext& eval_context, SemIR::TypeInstId inst_id, Phase* phase) -> SemIR::TypeInstId { // The input instruction is a TypeInstId, and eval does not change concrete // types (like TypeType which TypeInstId implies), so the result is also a // valid TypeInstId. return SemIR::TypeInstId::UnsafeMake(GetConstantValue( eval_context, static_cast(inst_id), phase)); } // Explicitly discard a `DestInstId`, because we should not be using the // destination as part of evaluation. static auto GetConstantValue(EvalContext& /*eval_context*/, SemIR::DestInstId /*inst_id*/, Phase* /*phase*/) -> SemIR::DestInstId { return SemIR::InstId::None; } // Given an instruction whose type may refer to a generic parameter, returns the // corresponding type in the evaluation context. // // If the `InstId` is not provided, the instruction is assumed to be new and // therefore unattached, and the type of the given instruction is returned // unchanged, but the phase is still updated. static auto GetTypeOfInst(EvalContext& eval_context, SemIR::InstId inst_id, SemIR::Inst inst, Phase* phase) -> SemIR::TypeId { auto type_id = inst_id.has_value() ? eval_context.GetTypeOfInst(inst_id) : inst.type_id(); *phase = LatestPhase(*phase, GetPhase(eval_context.constant_values(), eval_context.types().GetConstantId(type_id))); return type_id; } // AbsoluteInstBlockId can not have its values substituted, so this overload is // deleted. This prevents conversion to InstBlockId. static auto GetConstantValue(EvalContext& eval_context, SemIR::AbsoluteInstBlockId inst_block_id, Phase* phase) -> SemIR::InstBlockId = delete; // If the given instruction block contains only constants, returns a // corresponding block of those values. static auto GetConstantValue(EvalContext& eval_context, SemIR::InstBlockId inst_block_id, Phase* phase) -> SemIR::InstBlockId { if (!inst_block_id.has_value()) { return SemIR::InstBlockId::None; } auto insts = eval_context.inst_blocks().Get(inst_block_id); llvm::SmallVector const_insts; for (auto inst_id : insts) { auto const_inst_id = GetConstantValue(eval_context, inst_id, phase); if (!const_inst_id.has_value()) { return SemIR::InstBlockId::None; } // Once we leave the small buffer, we know the first few elements are all // constant, so it's likely that the entire block is constant. Resize to the // target size given that we're going to allocate memory now anyway. if (const_insts.size() == const_insts.capacity()) { const_insts.reserve(insts.size()); } const_insts.push_back(const_inst_id); } // TODO: If the new block is identical to the original block, and we know the // old ID was canonical, return the original ID. return eval_context.inst_blocks().AddCanonical(const_insts); } // Compute the constant value of a type block. This may be different from the // input type block if we have known generic arguments. static auto GetConstantValue(EvalContext& eval_context, SemIR::StructTypeFieldsId fields_id, Phase* phase) -> SemIR::StructTypeFieldsId { if (!fields_id.has_value()) { return SemIR::StructTypeFieldsId::None; } auto fields = eval_context.context().struct_type_fields().Get(fields_id); llvm::SmallVector new_fields; for (auto field : fields) { auto new_type_inst_id = GetConstantValue(eval_context, field.type_inst_id, phase); if (!new_type_inst_id.has_value()) { return SemIR::StructTypeFieldsId::None; } // Once we leave the small buffer, we know the first few elements are all // constant, so it's likely that the entire block is constant. Resize to the // target size given that we're going to allocate memory now anyway. if (new_fields.size() == new_fields.capacity()) { new_fields.reserve(fields.size()); } new_fields.push_back( {.name_id = field.name_id, .type_inst_id = new_type_inst_id}); } // TODO: If the new block is identical to the original block, and we know the // old ID was canonical, return the original ID. return eval_context.context().struct_type_fields().AddCanonical(new_fields); } // The constant value of a specific is the specific with the corresponding // constant values for its arguments. static auto GetConstantValue(EvalContext& eval_context, SemIR::SpecificId specific_id, Phase* phase) -> SemIR::SpecificId { if (!specific_id.has_value()) { return SemIR::SpecificId::None; } const auto& specific = eval_context.specifics().Get(specific_id); auto args_id = GetConstantValue(eval_context, specific.args_id, phase); if (!args_id.has_value()) { return SemIR::SpecificId::None; } // Generally, when making a new specific, it's done through MakeSpecific(), // which will ensure the declaration is resolved. // // However, the SpecificId returned here is intentionally left without its // declaration resolved. Imported instructions with SpecificIds should not // have the specific's declaration resolved, but other instructions which // include a new SpecificId should. // // The resolving of the specific's declaration will be ensured later when // evaluating the instruction containing the SpecificId. if (args_id == specific.args_id) { return specific_id; } return eval_context.context().specifics().GetOrAdd(specific.generic_id, args_id); } static auto GetConstantValue(EvalContext& eval_context, SemIR::SpecificInterfaceId specific_interface_id, Phase* phase) -> SemIR::SpecificInterfaceId { const auto& interface = eval_context.specific_interfaces().Get(specific_interface_id); if (!interface.specific_id.has_value()) { return specific_interface_id; } return eval_context.specific_interfaces().Add( {.interface_id = interface.interface_id, .specific_id = GetConstantValue(eval_context, interface.specific_id, phase)}); } // Like `GetConstantValue` but for a `FacetTypeInfo`. static auto GetConstantFacetTypeInfo(EvalContext& eval_context, SemIR::LocId loc_id, const SemIR::FacetTypeInfo& orig, Phase* phase) -> SemIR::FacetTypeInfo { SemIR::FacetTypeInfo info = {}; info.extend_constraints.reserve(orig.extend_constraints.size()); for (const auto& extend : orig.extend_constraints) { info.extend_constraints.push_back( {.interface_id = extend.interface_id, .specific_id = GetConstantValue(eval_context, extend.specific_id, phase)}); } info.self_impls_constraints.reserve(orig.self_impls_constraints.size()); for (const auto& self_impls : orig.self_impls_constraints) { info.self_impls_constraints.push_back( {.interface_id = self_impls.interface_id, .specific_id = GetConstantValue(eval_context, self_impls.specific_id, phase)}); } info.extend_named_constraints.reserve(orig.extend_named_constraints.size()); for (const auto& extend : orig.extend_named_constraints) { info.extend_named_constraints.push_back( {.named_constraint_id = extend.named_constraint_id, .specific_id = GetConstantValue(eval_context, extend.specific_id, phase)}); } info.self_impls_named_constraints.reserve( orig.self_impls_named_constraints.size()); for (const auto& self_impls : orig.self_impls_named_constraints) { info.self_impls_named_constraints.push_back( {.named_constraint_id = self_impls.named_constraint_id, .specific_id = GetConstantValue(eval_context, self_impls.specific_id, phase)}); } // Rewrite constraints are resolved first before replacing them with their // canonical instruction, so that in a `WhereExpr` we can work with the // `ImplWitnessAccess` references to `.Self` on the LHS of the constraints // rather than the value of the associated constant they reference. // // This also implies that we may find `ImplWitnessAccessSubstituted` // instructions in the LHS and RHS of these constraints, which are preserved // to maintain them as an unresolved reference to an associated constant, but // which must be handled gracefully during resolution. They will be replaced // with the constant value of the `ImplWitnessAccess` below when they are // substituted with a constant value. info.rewrite_constraints = orig.rewrite_constraints; if (!ResolveFacetTypeRewriteConstraints(eval_context.context(), loc_id, info.rewrite_constraints)) { *phase = Phase::UnknownDueToError; } for (auto& rewrite : info.rewrite_constraints) { // `where` requirements using `.Self` should not be considered symbolic. auto lhs_id = RequireConstantValueIgnoringPeriodSelf(eval_context, rewrite.lhs_id, phase); auto rhs_id = RequireConstantValueIgnoringPeriodSelf(eval_context, rewrite.rhs_id, phase); rewrite = {.lhs_id = lhs_id, .rhs_id = rhs_id}; } // TODO: Process other requirements. info.other_requirements = orig.other_requirements; info.Canonicalize(); return info; } static auto GetConstantValue(EvalContext& eval_context, SemIR::FacetTypeId facet_type_id, Phase* phase) -> SemIR::FacetTypeId { SemIR::FacetTypeInfo info = GetConstantFacetTypeInfo( eval_context, SemIR::LocId::None, eval_context.facet_types().Get(facet_type_id), phase); return eval_context.facet_types().Add(info); } static auto GetConstantValue(EvalContext& eval_context, SemIR::EntityNameId entity_name_id, Phase* phase) -> SemIR::EntityNameId { const auto& bind_name = eval_context.entity_names().Get(entity_name_id); Phase name_phase; if (bind_name.name_id == SemIR::NameId::PeriodSelf) { name_phase = Phase::PeriodSelfSymbolic; } else if (!bind_name.bind_index().has_value()) { name_phase = Phase::Concrete; } else if (bind_name.is_template) { name_phase = Phase::TemplateSymbolic; } else { name_phase = Phase::CheckedSymbolic; } *phase = LatestPhase(*phase, name_phase); return eval_context.entity_names().MakeCanonical(entity_name_id); } // Replaces the specified field of the given typed instruction with its constant // value, if it has constant phase. Returns true on success, false if the value // has runtime phase. template static auto ReplaceFieldWithConstantValue(EvalContext& eval_context, InstT* inst, FieldIdT InstT::* field, Phase* phase) -> bool { auto unwrapped = GetConstantValue(eval_context, inst->*field, phase); if (!unwrapped.has_value() && (inst->*field).has_value()) { return false; } inst->*field = unwrapped; return IsConstantOrError(*phase); } // Function template that can be called with an argument of type `T`. Used below // to detect which overloads of `GetConstantValue` exist. template static void Accept(T /*arg*/) {} // Determines whether a `GetConstantValue` overload exists for a given ID type. // Note that we do not check whether `GetConstantValue` is *callable* with a // given ID type, because that would use the `InstId` overload for // `AbsoluteInstId` and similar wrapper types, which should be left alone. template static constexpr bool HasGetConstantValueOverload = requires { AcceptIdT>(GetConstantValue); }; using ArgHandlerFnT = auto(EvalContext& context, int32_t arg, Phase* phase) -> int32_t; // Returns the arg handler for an `IdKind`. template static auto GetArgHandlerFn(TypeEnum id_kind) -> ArgHandlerFnT* { static constexpr std::array Table = { [](EvalContext& eval_context, int32_t arg, Phase* phase) -> int32_t { auto id = SemIR::Inst::FromRaw(arg); if constexpr (HasGetConstantValueOverload) { // If we have a custom `GetConstantValue` overload, call it. return SemIR::Inst::ToRaw( GetConstantValue(eval_context, id, phase)); } else { // Otherwise, we assume the value is already constant. return arg; } }..., // Invalid and None handling (ordering-sensitive). [](auto...) -> int32_t { CARBON_FATAL("Unexpected invalid IdKind"); }, [](EvalContext& /*context*/, int32_t arg, Phase* /*phase*/) -> int32_t { return arg; }, }; return Table[id_kind.ToIndex()]; } // Given the stored value `arg` of an instruction field and its corresponding // kind `kind`, returns the constant value to use for that field, if it has a // constant phase. `*phase` is updated to include the new constant value. If // the resulting phase is not constant, the returned value is not useful and // will typically be `NoneIndex`. static auto GetConstantValueForArg(EvalContext& eval_context, SemIR::Inst::ArgAndKind arg_and_kind, Phase* phase) -> int32_t { return GetArgHandlerFn(arg_and_kind.kind())(eval_context, arg_and_kind.value(), phase); } // Given an instruction, replaces its operands with their constant values from // the specified evaluation context. `*phase` is updated to describe the // constant phase of the result. Returns whether `*phase` is a constant phase; // if not, `inst` may not be fully updated and should not be used. static auto ReplaceAllFieldsWithConstantValues(EvalContext& eval_context, SemIR::Inst* inst, Phase* phase) -> bool { auto arg0 = GetConstantValueForArg(eval_context, inst->arg0_and_kind(), phase); if (!IsConstantOrError(*phase)) { return false; } auto arg1 = GetConstantValueForArg(eval_context, inst->arg1_and_kind(), phase); if (!IsConstantOrError(*phase)) { return false; } inst->SetArgs(arg0, arg1); return true; } // Given an instruction and its ID, replaces its type with the corresponding // value in this evaluation context. Updates `*phase` to describe the phase of // the result, and returns whether `*phase` is a constant phase. static auto ReplaceTypeWithConstantValue(EvalContext& eval_context, SemIR::InstId inst_id, SemIR::Inst* inst, Phase* phase) -> bool { inst->SetType(GetTypeOfInst(eval_context, inst_id, *inst, phase)); return IsConstantOrError(*phase); } template static auto ReplaceTypeWithConstantValue(EvalContext& eval_context, SemIR::InstId inst_id, InstT* inst, Phase* phase) -> bool { inst->type_id = GetTypeOfInst(eval_context, inst_id, *inst, phase); return IsConstantOrError(*phase); } template static auto KindHasGetConstantValueOverload(TypeEnum e) -> bool { static constexpr std::array Values = { (HasGetConstantValueOverload)...}; return Values[e.ToIndex()]; } static auto ResolveSpecificDeclForSpecificId(EvalContext& eval_context, SemIR::SpecificId specific_id) -> void { if (!specific_id.has_value()) { return; } const auto& specific = eval_context.specifics().Get(specific_id); const auto& generic = eval_context.generics().Get(specific.generic_id); if (specific_id == generic.self_specific_id) { // Impl witness table construction happens before its generic decl is // finish, in order to make the table's instructions dependent // instructions of the Impl's generic. But those instructions can refer to // the generic's self specific. We can not resolve the specific // declaration for the self specific until the generic is finished, but it // is explicitly resolved at that time in `FinishGenericDecl()`. return; } ResolveSpecificDecl(eval_context.context(), eval_context.fallback_loc_id(), specific_id); } // Resolves the specific declarations for a specific id in any field of the // `inst` instruction. static auto ResolveSpecificDeclForInst(EvalContext& eval_context, const SemIR::Inst& inst) -> void { for (auto arg_and_kind : {inst.arg0_and_kind(), inst.arg1_and_kind()}) { // This switch must handle any field type that has a GetConstantValue() // overload which canonicalizes a specific (and thus potentially forms a new // specific) as part of forming its constant value. CARBON_KIND_SWITCH(arg_and_kind) { case CARBON_KIND(SemIR::FacetTypeId facet_type_id): { const auto& info = eval_context.context().facet_types().Get(facet_type_id); for (const auto& interface : info.extend_constraints) { ResolveSpecificDeclForSpecificId(eval_context, interface.specific_id); } for (const auto& interface : info.self_impls_constraints) { ResolveSpecificDeclForSpecificId(eval_context, interface.specific_id); } for (const auto& constraint : info.extend_named_constraints) { ResolveSpecificDeclForSpecificId(eval_context, constraint.specific_id); } for (const auto& constraint : info.self_impls_named_constraints) { ResolveSpecificDeclForSpecificId(eval_context, constraint.specific_id); } break; } case CARBON_KIND(SemIR::SpecificId specific_id): { ResolveSpecificDeclForSpecificId(eval_context, specific_id); break; } case CARBON_KIND(SemIR::SpecificInterfaceId specific_interface_id): { ResolveSpecificDeclForSpecificId(eval_context, eval_context.specific_interfaces() .Get(specific_interface_id) .specific_id); break; } // These id types have a GetConstantValue() overload but that overload // does not canonicalize any SpecificId in the value type. case SemIR::IdKind::For: case SemIR::IdKind::For: case SemIR::IdKind::For: case SemIR::IdKind::For: case SemIR::IdKind::For: case SemIR::IdKind::For: case SemIR::IdKind::For: break; case SemIR::IdKind::None: // No arg. break; default: CARBON_CHECK( !KindHasGetConstantValueOverload(arg_and_kind.kind()), "Missing case for {0} which has a GetConstantValue() overload", arg_and_kind.kind()); break; } } } auto AddImportedConstant(Context& context, SemIR::Inst inst) -> SemIR::ConstantId { EvalContext eval_context(&context, SemIR::LocId::None); CARBON_CHECK(inst.kind().has_type(), "Can't import untyped instructions: {0}", inst.kind()); Phase phase = GetPhase(context.constant_values(), context.types().GetConstantId(inst.type_id())); // We ignore the return value of ReplaceAllFieldsWithConstantValues and just // propagate runtime and error constant values into the resulting ConstantId. ReplaceAllFieldsWithConstantValues(eval_context, &inst, &phase); return MakeConstantResult(context, inst, phase); } // Performs an index into a homogeneous aggregate, retrieving the specified // element. static auto PerformArrayIndex(EvalContext& eval_context, SemIR::ArrayIndex inst) -> SemIR::ConstantId { Phase phase = Phase::Concrete; auto index_id = GetConstantValue(eval_context, inst.index_id, &phase); if (!index_id.has_value()) { return MakeNonConstantResult(phase); } auto index = eval_context.insts().TryGetAs(index_id); if (!index) { CARBON_CHECK(phase != Phase::Concrete, "Concrete constant integer should be a literal"); return MakeNonConstantResult(phase); } // Array indexing is invalid if the index is constant and out of range, // regardless of whether the array itself is constant. const auto& index_val = eval_context.ints().Get(index->int_id); auto aggregate_type_id = eval_context.GetTypeOfInst(inst.array_id); if (auto array_type = eval_context.types().TryGetAs(aggregate_type_id)) { if (auto bound = eval_context.insts().TryGetAs( array_type->bound_id)) { // This awkward call to `getZExtValue` is a workaround for APInt not // supporting comparisons between integers of different bit widths. if (index_val.getActiveBits() > 64 || eval_context.ints() .Get(bound->int_id) .ule(index_val.getZExtValue())) { CARBON_DIAGNOSTIC(ArrayIndexOutOfBounds, Error, "array index `{0}` is past the end of type {1}", TypedInt, SemIR::TypeId); eval_context.emitter().Emit( eval_context.GetDiagnosticLoc(inst.index_id), ArrayIndexOutOfBounds, {.type = index->type_id, .value = index_val}, aggregate_type_id); return SemIR::ErrorInst::ConstantId; } } } auto aggregate_id = GetConstantValue(eval_context, inst.array_id, &phase); if (!aggregate_id.has_value()) { return MakeNonConstantResult(phase); } auto aggregate = eval_context.insts().TryGetAs(aggregate_id); if (!aggregate) { // TODO: Consider forming a symbolic constant or reference constant array // index in this case. return MakeNonConstantResult(phase); } auto elements = eval_context.inst_blocks().Get(aggregate->elements_id); return eval_context.GetConstantValue(elements[index_val.getZExtValue()]); } // Performs a conversion between character types, diagnosing if the value // doesn't fit in the destination type. static auto PerformCheckedCharConvert(Context& context, SemIR::LocId loc_id, SemIR::InstId arg_id, SemIR::TypeId dest_type_id) -> SemIR::ConstantId { auto arg = context.insts().GetAs(arg_id); // Values over 0x80 require multiple code units in UTF-8. if (arg.value.index >= 0x80) { CARBON_DIAGNOSTIC(CharTooLargeForType, Error, "character value {0} too large for type {1}", SemIR::CharId, SemIR::TypeId); context.emitter().Emit(loc_id, CharTooLargeForType, arg.value, dest_type_id); return SemIR::ErrorInst::ConstantId; } llvm::APInt int_val(8, arg.value.index, /*isSigned=*/false); return MakeIntResult(context, dest_type_id, /*is_signed=*/false, int_val); } // Forms a constant int type as an evaluation result. Requires that width_id is // constant. static auto MakeIntTypeResult(Context& context, SemIR::LocId loc_id, SemIR::IntKind int_kind, SemIR::InstId width_id, Phase phase) -> SemIR::ConstantId { auto result = SemIR::IntType{.type_id = SemIR::TypeType::TypeId, .int_kind = int_kind, .bit_width_id = width_id}; if (!ValidateIntType(context, loc_id, result)) { return SemIR::ErrorInst::ConstantId; } return MakeConstantResult(context, result, phase); } // Forms a constant float type as an evaluation result. Requires that width_id // is constant. static auto MakeFloatTypeResult(Context& context, SemIR::LocId loc_id, SemIR::InstId width_id, Phase phase) -> SemIR::ConstantId { auto result = SemIR::FloatType{.type_id = SemIR::TypeType::TypeId, .bit_width_id = width_id, .float_kind = SemIR::FloatKind::None}; if (!ValidateFloatTypeAndSetKind(context, loc_id, result)) { return SemIR::ErrorInst::ConstantId; } return MakeConstantResult(context, result, phase); } // Performs a conversion between integer types, truncating if the value doesn't // fit in the destination type. static auto PerformIntConvert(Context& context, SemIR::InstId arg_id, SemIR::TypeId dest_type_id) -> SemIR::ConstantId { auto arg_val = context.ints().Get(context.insts().GetAs(arg_id).int_id); auto [dest_is_signed, bit_width_id] = context.sem_ir().types().GetIntTypeInfo(dest_type_id); if (bit_width_id.has_value()) { // TODO: If the value fits in the destination type, reuse the existing // int_id rather than recomputing it. This is probably the most common case. bool src_is_signed = context.sem_ir().types().IsSignedInt( context.insts().Get(arg_id).type_id()); unsigned width = context.ints().Get(bit_width_id).getZExtValue(); arg_val = src_is_signed ? arg_val.sextOrTrunc(width) : arg_val.zextOrTrunc(width); } return MakeIntResult(context, dest_type_id, dest_is_signed, arg_val); } // Performs a conversion between integer types, diagnosing if the value doesn't // fit in the destination type. static auto PerformCheckedIntConvert(Context& context, SemIR::LocId loc_id, SemIR::InstId arg_id, SemIR::TypeId dest_type_id) -> SemIR::ConstantId { auto arg = context.insts().GetAs(arg_id); auto arg_val = context.ints().Get(arg.int_id); auto [is_signed, bit_width_id] = context.sem_ir().types().GetIntTypeInfo(dest_type_id); auto width = bit_width_id.has_value() ? context.ints().Get(bit_width_id).getZExtValue() : arg_val.getBitWidth(); if (!is_signed && arg_val.isNegative()) { CARBON_DIAGNOSTIC( NegativeIntInUnsignedType, Error, "negative integer value {0} converted to unsigned type {1}", TypedInt, SemIR::TypeId); context.emitter().Emit(loc_id, NegativeIntInUnsignedType, {.type = arg.type_id, .value = arg_val}, dest_type_id); } unsigned arg_non_sign_bits = arg_val.getSignificantBits() - 1; if (arg_non_sign_bits + is_signed > width) { CARBON_DIAGNOSTIC(IntTooLargeForType, Error, "integer value {0} too large for type {1}", TypedInt, SemIR::TypeId); context.emitter().Emit(loc_id, IntTooLargeForType, {.type = arg.type_id, .value = arg_val}, dest_type_id); } return MakeConstantResult( context, SemIR::IntValue{.type_id = dest_type_id, .int_id = arg.int_id}, Phase::Concrete); } // Performs a conversion between floating-point types, diagnosing if the value // doesn't fit in the destination type. static auto PerformCheckedFloatConvert(Context& context, SemIR::LocId loc_id, SemIR::InstId arg_id, SemIR::TypeId dest_type_id) -> SemIR::ConstantId { auto dest_type_object_rep_id = context.types().GetObjectRepr(dest_type_id); CARBON_CHECK(dest_type_object_rep_id.has_value(), "Conversion to incomplete type"); auto dest_float_type = context.types().TryGetAs(dest_type_object_rep_id); CARBON_CHECK(dest_float_type || context.types().Is( dest_type_object_rep_id)); if (auto literal = context.insts().TryGetAs(arg_id)) { if (!dest_float_type) { return MakeConstantResult( context, SemIR::FloatLiteralValue{.type_id = dest_type_id, .real_id = literal->real_id}, Phase::Concrete); } // Convert the real literal to an llvm::APFloat and add it to the floats // ValueStore. In the future this would use an arbitrary precision Rational // type. // // TODO: Implement Carbon's actual implicit conversion rules for // floating-point constants, as per the design // docs/design/expressions/implicit_conversions.md auto real_value = context.sem_ir().reals().Get(literal->real_id); // Convert the real value to a string. llvm::SmallString<64> str; real_value.mantissa.toString(str, real_value.is_decimal ? 10 : 16, /*signed=*/false, /*formatAsCLiteral=*/true); str += real_value.is_decimal ? "e" : "p"; real_value.exponent.toStringSigned(str); // Convert the string to an APFloat. llvm::APFloat result(dest_float_type->float_kind.Semantics()); // TODO: The implementation of this conversion effectively converts back to // APInts, but unfortunately the conversion from integer mantissa and // exponent in IEEEFloat::roundSignificandWithExponent is not part of the // public API. auto status = result.convertFromString(str, llvm::APFloat::rmNearestTiesToEven); if (auto error = status.takeError()) { // The literal we create should always successfully parse. CARBON_FATAL("Float literal parsing failed: {0}", toString(std::move(error))); } if (status.get() & llvm::APFloat::opOverflow) { CARBON_DIAGNOSTIC(FloatLiteralTooLargeForType, Error, "value {0} too large for floating-point type {1}", RealId, SemIR::TypeId); context.emitter().Emit(loc_id, FloatLiteralTooLargeForType, literal->real_id, dest_type_id); return SemIR::ErrorInst::ConstantId; } return MakeFloatResult(context, dest_type_id, std::move(result)); } if (!dest_float_type) { context.TODO(loc_id, "conversion from float to float literal"); return SemIR::ErrorInst::ConstantId; } // Convert to the destination float semantics. auto arg = context.insts().GetAs(arg_id); llvm::APFloat result = context.floats().Get(arg.float_id); bool loses_info; auto status = result.convert(dest_float_type->float_kind.Semantics(), llvm::APFloat::rmNearestTiesToEven, &loses_info); if (status & llvm::APFloat::opOverflow) { CARBON_DIAGNOSTIC(FloatTooLargeForType, Error, "value {0} too large for floating-point type {1}", llvm::APFloat, SemIR::TypeId); context.emitter().Emit(loc_id, FloatTooLargeForType, context.floats().Get(arg.float_id), dest_type_id); return SemIR::ErrorInst::ConstantId; } return MakeFloatResult(context, dest_type_id, std::move(result)); } // Issues a diagnostic for a compile-time division by zero. static auto DiagnoseDivisionByZero(Context& context, SemIR::LocId loc_id) -> void { CARBON_DIAGNOSTIC(CompileTimeDivisionByZero, Error, "division by zero"); context.emitter().Emit(loc_id, CompileTimeDivisionByZero); } // Get an integer at a suitable bit-width: either `bit_width_id` if it has a // value, or the canonical width from the value store if not. static auto GetIntAtSuitableWidth(Context& context, IntId int_id, IntId bit_width_id) -> llvm::APInt { return bit_width_id.has_value() ? context.ints().GetAtWidth(int_id, bit_width_id) : context.ints().Get(int_id); } // Performs a builtin unary integer -> integer operation. static auto PerformBuiltinUnaryIntOp(Context& context, SemIR::LocId loc_id, SemIR::BuiltinFunctionKind builtin_kind, SemIR::InstId arg_id) -> SemIR::ConstantId { auto op = context.insts().GetAs(arg_id); auto [is_signed, bit_width_id] = context.sem_ir().types().GetIntTypeInfo(op.type_id); llvm::APInt op_val = GetIntAtSuitableWidth(context, op.int_id, bit_width_id); switch (builtin_kind) { case SemIR::BuiltinFunctionKind::IntSNegate: if (op_val.isMinSignedValue()) { if (bit_width_id.has_value()) { CARBON_DIAGNOSTIC(CompileTimeIntegerNegateOverflow, Error, "integer overflow in negation of {0}", TypedInt); context.emitter().Emit(loc_id, CompileTimeIntegerNegateOverflow, {.type = op.type_id, .value = op_val}); } else { // Widen the integer so we don't overflow into the sign bit. op_val = op_val.sext(op_val.getBitWidth() + llvm::APInt::APINT_BITS_PER_WORD); } } op_val.negate(); break; case SemIR::BuiltinFunctionKind::IntUNegate: CARBON_CHECK(bit_width_id.has_value(), "Unsigned negate on unsized int"); op_val.negate(); break; case SemIR::BuiltinFunctionKind::IntComplement: // TODO: Should we have separate builtins for signed and unsigned // complement? Like with signed/unsigned negate, these operations do // different things to the integer value, even though they do the same // thing to the bits. We treat IntLiteral complement as signed complement, // given that the result of unsigned complement depends on the bit width. op_val.flipAllBits(); break; default: CARBON_FATAL("Unexpected builtin kind"); } return MakeIntResult(context, op.type_id, is_signed, std::move(op_val)); } namespace { // A pair of APInts that are the operands of a binary operator. We use an // aggregate rather than `std::pair` to allow RVO of the individual ints. struct APIntBinaryOperands { llvm::APInt lhs; llvm::APInt rhs; }; } // namespace // Get a pair of integers at the same suitable bit-width: either their actual // width if they have a fixed width, or the smallest canonical width in which // they both fit otherwise. static auto GetIntsAtSuitableWidth(Context& context, IntId lhs_id, IntId rhs_id, IntId bit_width_id) -> APIntBinaryOperands { // Unsized operands: take the wider of the bit widths. if (!bit_width_id.has_value()) { APIntBinaryOperands result = {.lhs = context.ints().Get(lhs_id), .rhs = context.ints().Get(rhs_id)}; if (result.lhs.getBitWidth() != result.rhs.getBitWidth()) { if (result.lhs.getBitWidth() > result.rhs.getBitWidth()) { result.rhs = result.rhs.sext(result.lhs.getBitWidth()); } else { result.lhs = result.lhs.sext(result.rhs.getBitWidth()); } } return result; } return {.lhs = context.ints().GetAtWidth(lhs_id, bit_width_id), .rhs = context.ints().GetAtWidth(rhs_id, bit_width_id)}; } namespace { // The result of performing a binary int operation. struct BinaryIntOpResult { llvm::APInt result_val; bool overflow; Lex::TokenKind op_token; }; } // namespace // Computes the result of a homogeneous binary (int, int) -> int operation. static auto ComputeBinaryIntOpResult(SemIR::BuiltinFunctionKind builtin_kind, const llvm::APInt& lhs_val, const llvm::APInt& rhs_val) -> BinaryIntOpResult { llvm::APInt result_val; bool overflow = false; Lex::TokenKind op_token = Lex::TokenKind::Not; switch (builtin_kind) { // Arithmetic. case SemIR::BuiltinFunctionKind::IntSAdd: result_val = lhs_val.sadd_ov(rhs_val, overflow); op_token = Lex::TokenKind::Plus; break; case SemIR::BuiltinFunctionKind::IntSSub: result_val = lhs_val.ssub_ov(rhs_val, overflow); op_token = Lex::TokenKind::Minus; break; case SemIR::BuiltinFunctionKind::IntSMul: result_val = lhs_val.smul_ov(rhs_val, overflow); op_token = Lex::TokenKind::Star; break; case SemIR::BuiltinFunctionKind::IntSDiv: result_val = lhs_val.sdiv_ov(rhs_val, overflow); op_token = Lex::TokenKind::Slash; break; case SemIR::BuiltinFunctionKind::IntSMod: result_val = lhs_val.srem(rhs_val); // LLVM weirdly lacks `srem_ov`, so we work it out for ourselves: // % -1 overflows because / -1 overflows. overflow = lhs_val.isMinSignedValue() && rhs_val.isAllOnes(); op_token = Lex::TokenKind::Percent; break; case SemIR::BuiltinFunctionKind::IntUAdd: result_val = lhs_val + rhs_val; op_token = Lex::TokenKind::Plus; break; case SemIR::BuiltinFunctionKind::IntUSub: result_val = lhs_val - rhs_val; op_token = Lex::TokenKind::Minus; break; case SemIR::BuiltinFunctionKind::IntUMul: result_val = lhs_val * rhs_val; op_token = Lex::TokenKind::Star; break; case SemIR::BuiltinFunctionKind::IntUDiv: result_val = lhs_val.udiv(rhs_val); op_token = Lex::TokenKind::Slash; break; case SemIR::BuiltinFunctionKind::IntUMod: result_val = lhs_val.urem(rhs_val); op_token = Lex::TokenKind::Percent; break; // Bitwise. case SemIR::BuiltinFunctionKind::IntAnd: result_val = lhs_val & rhs_val; op_token = Lex::TokenKind::And; break; case SemIR::BuiltinFunctionKind::IntOr: result_val = lhs_val | rhs_val; op_token = Lex::TokenKind::Pipe; break; case SemIR::BuiltinFunctionKind::IntXor: result_val = lhs_val ^ rhs_val; op_token = Lex::TokenKind::Caret; break; case SemIR::BuiltinFunctionKind::IntLeftShift: case SemIR::BuiltinFunctionKind::IntRightShift: CARBON_FATAL("Non-homogeneous operation handled separately."); default: CARBON_FATAL("Unexpected operation kind."); } return {.result_val = std::move(result_val), .overflow = overflow, .op_token = op_token}; } // Performs a builtin integer bit shift operation. static auto PerformBuiltinIntShiftOp(Context& context, SemIR::LocId loc_id, SemIR::BuiltinFunctionKind builtin_kind, SemIR::InstId lhs_id, SemIR::InstId rhs_id) -> SemIR::ConstantId { auto lhs = context.insts().GetAs(lhs_id); auto rhs = context.insts().GetAs(rhs_id); auto [lhs_is_signed, lhs_bit_width_id] = context.sem_ir().types().GetIntTypeInfo(lhs.type_id); llvm::APInt lhs_val = GetIntAtSuitableWidth(context, lhs.int_id, lhs_bit_width_id); const auto& rhs_orig_val = context.ints().Get(rhs.int_id); if (lhs_bit_width_id.has_value() && rhs_orig_val.uge(lhs_val.getBitWidth())) { CARBON_DIAGNOSTIC( CompileTimeShiftOutOfRange, Error, "shift distance >= type width of {0} in `{1} {2:<<|>>} {3}`", unsigned, TypedInt, Diagnostics::BoolAsSelect, TypedInt); context.emitter().Emit( loc_id, CompileTimeShiftOutOfRange, lhs_val.getBitWidth(), {.type = lhs.type_id, .value = lhs_val}, builtin_kind == SemIR::BuiltinFunctionKind::IntLeftShift, {.type = rhs.type_id, .value = rhs_orig_val}); // TODO: Is it useful to recover by returning 0 or -1? return SemIR::ErrorInst::ConstantId; } if (rhs_orig_val.isNegative() && context.sem_ir().types().IsSignedInt(rhs.type_id)) { CARBON_DIAGNOSTIC(CompileTimeShiftNegative, Error, "shift distance negative in `{0} {1:<<|>>} {2}`", TypedInt, Diagnostics::BoolAsSelect, TypedInt); context.emitter().Emit( loc_id, CompileTimeShiftNegative, {.type = lhs.type_id, .value = lhs_val}, builtin_kind == SemIR::BuiltinFunctionKind::IntLeftShift, {.type = rhs.type_id, .value = rhs_orig_val}); // TODO: Is it useful to recover by returning 0 or -1? return SemIR::ErrorInst::ConstantId; } llvm::APInt result_val; if (builtin_kind == SemIR::BuiltinFunctionKind::IntLeftShift) { if (!lhs_bit_width_id.has_value() && !lhs_val.isZero()) { // Ensure we don't generate a ridiculously large integer through a bit // shift. auto width = rhs_orig_val.trySExtValue(); if (!width || *width > IntStore::MaxIntWidth - lhs_val.getSignificantBits()) { CARBON_DIAGNOSTIC(CompileTimeUnsizedShiftOutOfRange, Error, "shift distance of {0} would result in an " "integer whose width is greater than the " "maximum supported width of {1}", TypedInt, int); context.emitter().Emit(loc_id, CompileTimeUnsizedShiftOutOfRange, {.type = rhs.type_id, .value = rhs_orig_val}, IntStore::MaxIntWidth); return SemIR::ErrorInst::ConstantId; } lhs_val = lhs_val.sext( IntStore::CanonicalBitWidth(lhs_val.getSignificantBits() + *width)); } result_val = lhs_val.shl(rhs_orig_val.getLimitedValue(lhs_val.getBitWidth())); } else if (lhs_is_signed) { result_val = lhs_val.ashr(rhs_orig_val.getLimitedValue(lhs_val.getBitWidth())); } else { CARBON_CHECK(lhs_bit_width_id.has_value(), "Logical shift on unsized int"); result_val = lhs_val.lshr(rhs_orig_val.getLimitedValue(lhs_val.getBitWidth())); } return MakeIntResult(context, lhs.type_id, lhs_is_signed, std::move(result_val)); } // Performs a homogeneous builtin binary integer -> integer operation. static auto PerformBuiltinBinaryIntOp(Context& context, SemIR::LocId loc_id, SemIR::BuiltinFunctionKind builtin_kind, SemIR::InstId lhs_id, SemIR::InstId rhs_id) -> SemIR::ConstantId { auto lhs = context.insts().GetAs(lhs_id); auto rhs = context.insts().GetAs(rhs_id); CARBON_CHECK(rhs.type_id == lhs.type_id, "Heterogeneous builtin integer op!"); auto type_id = lhs.type_id; auto [is_signed, bit_width_id] = context.sem_ir().types().GetIntTypeInfo(type_id); auto [lhs_val, rhs_val] = GetIntsAtSuitableWidth(context, lhs.int_id, rhs.int_id, bit_width_id); // Check for division by zero. switch (builtin_kind) { case SemIR::BuiltinFunctionKind::IntSDiv: case SemIR::BuiltinFunctionKind::IntSMod: case SemIR::BuiltinFunctionKind::IntUDiv: case SemIR::BuiltinFunctionKind::IntUMod: if (rhs_val.isZero()) { DiagnoseDivisionByZero(context, loc_id); return SemIR::ErrorInst::ConstantId; } break; default: break; } BinaryIntOpResult result = ComputeBinaryIntOpResult(builtin_kind, lhs_val, rhs_val); if (result.overflow && !bit_width_id.has_value()) { // Retry with a larger bit width. Most operations can only overflow by one // bit, but signed n-bit multiplication can overflow to 2n-1 bits. We don't // need to handle unsigned multiplication here because it's not permitted // for unsized integers. // // Note that we speculatively first perform the calculation in the width of // the wider operand: smaller operations are faster and overflow to a wider // integer is unlikely to be needed, especially given that the width will // have been rounded up to a multiple of 64 bits by the int store. CARBON_CHECK(builtin_kind != SemIR::BuiltinFunctionKind::IntUMul, "Unsigned arithmetic requires a fixed bitwidth"); int new_width = builtin_kind == SemIR::BuiltinFunctionKind::IntSMul ? lhs_val.getBitWidth() * 2 : IntStore::CanonicalBitWidth(lhs_val.getBitWidth() + 1); new_width = std::min(new_width, IntStore::MaxIntWidth); lhs_val = context.ints().GetAtWidth(lhs.int_id, new_width); rhs_val = context.ints().GetAtWidth(rhs.int_id, new_width); // Note that this can in theory still overflow if we limited `new_width` to // `MaxIntWidth`. In that case we fall through to the signed overflow // diagnostic below. result = ComputeBinaryIntOpResult(builtin_kind, lhs_val, rhs_val); CARBON_CHECK(!result.overflow || new_width == IntStore::MaxIntWidth); } if (result.overflow) { CARBON_DIAGNOSTIC(CompileTimeIntegerOverflow, Error, "integer overflow in calculation `{0} {1} {2}`", TypedInt, Lex::TokenKind, TypedInt); context.emitter().Emit(loc_id, CompileTimeIntegerOverflow, {.type = type_id, .value = lhs_val}, result.op_token, {.type = type_id, .value = rhs_val}); } return MakeIntResult(context, type_id, is_signed, std::move(result.result_val)); } // Performs a builtin integer comparison. static auto PerformBuiltinIntComparison(Context& context, SemIR::BuiltinFunctionKind builtin_kind, SemIR::InstId lhs_id, SemIR::InstId rhs_id, SemIR::TypeId bool_type_id) -> SemIR::ConstantId { auto lhs = context.insts().GetAs(lhs_id); auto rhs = context.insts().GetAs(rhs_id); llvm::APInt lhs_val = context.ints().Get(lhs.int_id); llvm::APInt rhs_val = context.ints().Get(rhs.int_id); bool result; switch (builtin_kind) { case SemIR::BuiltinFunctionKind::IntEq: result = (lhs_val == rhs_val); break; case SemIR::BuiltinFunctionKind::IntNeq: result = (lhs_val != rhs_val); break; case SemIR::BuiltinFunctionKind::IntLess: result = lhs_val.slt(rhs_val); break; case SemIR::BuiltinFunctionKind::IntLessEq: result = lhs_val.sle(rhs_val); break; case SemIR::BuiltinFunctionKind::IntGreater: result = lhs_val.sgt(rhs_val); break; case SemIR::BuiltinFunctionKind::IntGreaterEq: result = lhs_val.sge(rhs_val); break; default: CARBON_FATAL("Unexpected operation kind."); } return MakeBoolResult(context, bool_type_id, result); } // Performs a builtin unary float -> float operation. static auto PerformBuiltinUnaryFloatOp(Context& context, SemIR::BuiltinFunctionKind builtin_kind, SemIR::InstId arg_id) -> SemIR::ConstantId { auto op = context.insts().GetAs(arg_id); auto op_val = context.floats().Get(op.float_id); switch (builtin_kind) { case SemIR::BuiltinFunctionKind::FloatNegate: op_val.changeSign(); break; default: CARBON_FATAL("Unexpected builtin kind"); } return MakeFloatResult(context, op.type_id, std::move(op_val)); } // Performs a builtin binary float -> float operation. static auto PerformBuiltinBinaryFloatOp(Context& context, SemIR::BuiltinFunctionKind builtin_kind, SemIR::InstId lhs_id, SemIR::InstId rhs_id) -> SemIR::ConstantId { auto lhs = context.insts().GetAs(lhs_id); auto rhs = context.insts().GetAs(rhs_id); auto lhs_val = context.floats().Get(lhs.float_id); auto rhs_val = context.floats().Get(rhs.float_id); llvm::APFloat result_val(lhs_val.getSemantics()); switch (builtin_kind) { case SemIR::BuiltinFunctionKind::FloatAdd: result_val = lhs_val + rhs_val; break; case SemIR::BuiltinFunctionKind::FloatSub: result_val = lhs_val - rhs_val; break; case SemIR::BuiltinFunctionKind::FloatMul: result_val = lhs_val * rhs_val; break; case SemIR::BuiltinFunctionKind::FloatDiv: result_val = lhs_val / rhs_val; break; default: CARBON_FATAL("Unexpected operation kind."); } return MakeFloatResult(context, lhs.type_id, std::move(result_val)); } // Performs a builtin float comparison. static auto PerformBuiltinFloatComparison( Context& context, SemIR::BuiltinFunctionKind builtin_kind, SemIR::InstId lhs_id, SemIR::InstId rhs_id, SemIR::TypeId bool_type_id) -> SemIR::ConstantId { auto lhs = context.insts().GetAs(lhs_id); auto rhs = context.insts().GetAs(rhs_id); const auto& lhs_val = context.floats().Get(lhs.float_id); const auto& rhs_val = context.floats().Get(rhs.float_id); bool result; switch (builtin_kind) { case SemIR::BuiltinFunctionKind::FloatEq: result = (lhs_val == rhs_val); break; case SemIR::BuiltinFunctionKind::FloatNeq: result = (lhs_val != rhs_val); break; case SemIR::BuiltinFunctionKind::FloatLess: result = lhs_val < rhs_val; break; case SemIR::BuiltinFunctionKind::FloatLessEq: result = lhs_val <= rhs_val; break; case SemIR::BuiltinFunctionKind::FloatGreater: result = lhs_val > rhs_val; break; case SemIR::BuiltinFunctionKind::FloatGreaterEq: result = lhs_val >= rhs_val; break; default: CARBON_FATAL("Unexpected operation kind."); } return MakeBoolResult(context, bool_type_id, result); } // Performs a builtin boolean comparison. static auto PerformBuiltinBoolComparison( Context& context, SemIR::BuiltinFunctionKind builtin_kind, SemIR::InstId lhs_id, SemIR::InstId rhs_id, SemIR::TypeId bool_type_id) { bool lhs = context.insts().GetAs(lhs_id).value.ToBool(); bool rhs = context.insts().GetAs(rhs_id).value.ToBool(); return MakeBoolResult(context, bool_type_id, builtin_kind == SemIR::BuiltinFunctionKind::BoolEq ? lhs == rhs : lhs != rhs); } // Converts a call argument to a FacetTypeId. static auto ArgToFacetTypeId(Context& context, SemIR::LocId loc_id, SemIR::InstId arg_id) -> SemIR::FacetTypeId { auto type_arg_id = context.types().GetAsTypeInstId(arg_id); if (auto facet_type = context.insts().TryGetAs(type_arg_id)) { return facet_type->facet_type_id; } CARBON_DIAGNOSTIC(FacetTypeRequiredForTypeAndOperator, Error, "non-facet type {0} combined with `&` operator", SemIR::TypeId); // TODO: Find a location for the lhs or rhs specifically, instead of // the whole thing. If that's not possible we can change the text to // say if it's referring to the left or the right side for the error. // The `arg_id` instruction has no location in it for some reason. context.emitter().Emit(loc_id, FacetTypeRequiredForTypeAndOperator, context.types().GetTypeIdForTypeInstId(type_arg_id)); return SemIR::FacetTypeId::None; } // Returns a constant for a call to a builtin function. static auto MakeConstantForBuiltinCall(EvalContext& eval_context, SemIR::LocId loc_id, SemIR::Call call, SemIR::BuiltinFunctionKind builtin_kind, llvm::ArrayRef arg_ids, Phase phase) -> SemIR::ConstantId { auto& context = eval_context.context(); switch (builtin_kind) { case SemIR::BuiltinFunctionKind::None: CARBON_FATAL("Not a builtin function."); case SemIR::BuiltinFunctionKind::NoOp: { return MakeEmptyTupleResult(eval_context); } case SemIR::BuiltinFunctionKind::PrimitiveCopy: { return context.constant_values().Get(arg_ids[0]); } case SemIR::BuiltinFunctionKind::StringAt: { Phase phase = Phase::Concrete; auto str_id = GetConstantValue(eval_context, arg_ids[0], &phase); auto index_id = GetConstantValue(eval_context, arg_ids[1], &phase); if (phase != Phase::Concrete) { return MakeNonConstantResult(phase); } auto str_struct = eval_context.insts().GetAs(str_id); auto elements = eval_context.inst_blocks().Get(str_struct.elements_id); // String struct has two fields: a pointer to the string data and the // length. CARBON_CHECK(elements.size() == 2, "String struct should have 2 fields."); auto string_literal = eval_context.insts().GetAs( eval_context.constant_values().GetConstantInstId(elements[0])); const auto& string_value = eval_context.sem_ir().string_literal_values().Get( string_literal.string_literal_id); auto index_inst = eval_context.insts().GetAs(index_id); const auto& index_val = eval_context.ints().Get(index_inst.int_id); if (index_val.isNegative()) { CARBON_DIAGNOSTIC(StringAtIndexNegative, Error, "index `{0}` is negative.", TypedInt); context.emitter().Emit( loc_id, StringAtIndexNegative, {.type = eval_context.insts().Get(index_id).type_id(), .value = index_val}); return SemIR::ConstantId::NotConstant; } if (index_val.getZExtValue() >= string_value.size()) { CARBON_DIAGNOSTIC( StringAtIndexOutOfBounds, Error, "string index `{0}` is out of bounds; string has length {1}.", TypedInt, size_t); context.emitter().Emit( loc_id, StringAtIndexOutOfBounds, {.type = eval_context.insts().Get(index_id).type_id(), .value = index_val}, string_value.size()); return SemIR::ConstantId::NotConstant; } auto char_value = static_cast(string_value[index_val.getZExtValue()]); auto int_id = eval_context.ints().Add( llvm::APSInt(llvm::APInt(32, char_value), /*isUnsigned=*/false)); return MakeConstantResult( eval_context.context(), SemIR::IntValue{.type_id = call.type_id, .int_id = int_id}, phase); } case SemIR::BuiltinFunctionKind::MakeUninitialized: case SemIR::BuiltinFunctionKind::PrintChar: case SemIR::BuiltinFunctionKind::PrintInt: case SemIR::BuiltinFunctionKind::ReadChar: case SemIR::BuiltinFunctionKind::FloatAddAssign: case SemIR::BuiltinFunctionKind::FloatSubAssign: case SemIR::BuiltinFunctionKind::FloatMulAssign: case SemIR::BuiltinFunctionKind::FloatDivAssign: case SemIR::BuiltinFunctionKind::IntSAddAssign: case SemIR::BuiltinFunctionKind::IntSSubAssign: case SemIR::BuiltinFunctionKind::IntSMulAssign: case SemIR::BuiltinFunctionKind::IntSDivAssign: case SemIR::BuiltinFunctionKind::IntSModAssign: case SemIR::BuiltinFunctionKind::IntUAddAssign: case SemIR::BuiltinFunctionKind::IntUSubAssign: case SemIR::BuiltinFunctionKind::IntUMulAssign: case SemIR::BuiltinFunctionKind::IntUDivAssign: case SemIR::BuiltinFunctionKind::IntUModAssign: case SemIR::BuiltinFunctionKind::IntAndAssign: case SemIR::BuiltinFunctionKind::IntOrAssign: case SemIR::BuiltinFunctionKind::IntXorAssign: case SemIR::BuiltinFunctionKind::IntLeftShiftAssign: case SemIR::BuiltinFunctionKind::IntRightShiftAssign: case SemIR::BuiltinFunctionKind::PointerMakeNull: case SemIR::BuiltinFunctionKind::PointerIsNull: case SemIR::BuiltinFunctionKind::PointerUnsafeConvert: case SemIR::BuiltinFunctionKind::CppStdInitializerListMake: { // These are runtime-only builtins. // TODO: Consider tracking this on the `BuiltinFunctionKind`. return SemIR::ConstantId::NotConstant; } case SemIR::BuiltinFunctionKind::TypeAnd: { CARBON_CHECK(arg_ids.size() == 2); auto lhs_facet_type_id = ArgToFacetTypeId(context, loc_id, arg_ids[0]); auto rhs_facet_type_id = ArgToFacetTypeId(context, loc_id, arg_ids[1]); // Allow errors to be diagnosed for both sides of the operator before // returning here if any error occurred on either side. if (!lhs_facet_type_id.has_value() || !rhs_facet_type_id.has_value()) { return SemIR::ErrorInst::ConstantId; } // Reuse one of the argument instructions if nothing has changed. if (lhs_facet_type_id == rhs_facet_type_id) { return context.types().GetConstantId( context.types().GetTypeIdForTypeInstId(arg_ids[0])); } auto combined_info = SemIR::FacetTypeInfo::Combine( context.facet_types().Get(lhs_facet_type_id), context.facet_types().Get(rhs_facet_type_id)); if (!ResolveFacetTypeRewriteConstraints( eval_context.context(), loc_id, combined_info.rewrite_constraints)) { phase = Phase::UnknownDueToError; } combined_info.Canonicalize(); return MakeFacetTypeResult(eval_context.context(), combined_info, phase); } case SemIR::BuiltinFunctionKind::CharLiteralMakeType: { return context.constant_values().Get(SemIR::CharLiteralType::TypeInstId); } case SemIR::BuiltinFunctionKind::FloatLiteralMakeType: { return context.constant_values().Get(SemIR::FloatLiteralType::TypeInstId); } case SemIR::BuiltinFunctionKind::IntLiteralMakeType: { return context.constant_values().Get(SemIR::IntLiteralType::TypeInstId); } case SemIR::BuiltinFunctionKind::IntMakeTypeSigned: { return MakeIntTypeResult(context, loc_id, SemIR::IntKind::Signed, arg_ids[0], phase); } case SemIR::BuiltinFunctionKind::IntMakeTypeUnsigned: { return MakeIntTypeResult(context, loc_id, SemIR::IntKind::Unsigned, arg_ids[0], phase); } case SemIR::BuiltinFunctionKind::FloatMakeType: { return MakeFloatTypeResult(context, loc_id, arg_ids[0], phase); } case SemIR::BuiltinFunctionKind::BoolMakeType: { return context.constant_values().Get(SemIR::BoolType::TypeInstId); } case SemIR::BuiltinFunctionKind::MaybeUnformedMakeType: { return MakeConstantResult( context, SemIR::MaybeUnformedType{ .type_id = SemIR::TypeType::TypeId, .inner_id = context.types().GetAsTypeInstId(arg_ids[0])}, phase); } case SemIR::BuiltinFunctionKind::FormMakeType: { return context.constant_values().Get(SemIR::FormType::TypeInstId); } // Character conversions. case SemIR::BuiltinFunctionKind::CharConvertChecked: { if (phase != Phase::Concrete) { return MakeConstantResult(context, call, phase); } return PerformCheckedCharConvert(context, loc_id, arg_ids[0], call.type_id); } // Integer conversions. case SemIR::BuiltinFunctionKind::IntConvertChar: { if (phase != Phase::Concrete) { return MakeConstantResult(context, call, phase); } return PerformIntConvert(context, arg_ids[0], call.type_id); } case SemIR::BuiltinFunctionKind::IntConvert: { if (phase != Phase::Concrete) { return MakeConstantResult(context, call, phase); } return PerformIntConvert(context, arg_ids[0], call.type_id); } case SemIR::BuiltinFunctionKind::IntConvertChecked: { if (phase != Phase::Concrete) { return MakeConstantResult(context, call, phase); } return PerformCheckedIntConvert(context, loc_id, arg_ids[0], call.type_id); } // Unary integer -> integer operations. case SemIR::BuiltinFunctionKind::IntSNegate: case SemIR::BuiltinFunctionKind::IntUNegate: case SemIR::BuiltinFunctionKind::IntComplement: { if (phase != Phase::Concrete) { break; } return PerformBuiltinUnaryIntOp(context, loc_id, builtin_kind, arg_ids[0]); } // Homogeneous binary integer -> integer operations. case SemIR::BuiltinFunctionKind::IntSAdd: case SemIR::BuiltinFunctionKind::IntSSub: case SemIR::BuiltinFunctionKind::IntSMul: case SemIR::BuiltinFunctionKind::IntSDiv: case SemIR::BuiltinFunctionKind::IntSMod: case SemIR::BuiltinFunctionKind::IntUAdd: case SemIR::BuiltinFunctionKind::IntUSub: case SemIR::BuiltinFunctionKind::IntUMul: case SemIR::BuiltinFunctionKind::IntUDiv: case SemIR::BuiltinFunctionKind::IntUMod: case SemIR::BuiltinFunctionKind::IntAnd: case SemIR::BuiltinFunctionKind::IntOr: case SemIR::BuiltinFunctionKind::IntXor: { if (phase != Phase::Concrete) { break; } return PerformBuiltinBinaryIntOp(context, loc_id, builtin_kind, arg_ids[0], arg_ids[1]); } // Bit shift operations. case SemIR::BuiltinFunctionKind::IntLeftShift: case SemIR::BuiltinFunctionKind::IntRightShift: { if (phase != Phase::Concrete) { break; } return PerformBuiltinIntShiftOp(context, loc_id, builtin_kind, arg_ids[0], arg_ids[1]); } // Integer comparisons. case SemIR::BuiltinFunctionKind::IntEq: case SemIR::BuiltinFunctionKind::IntNeq: case SemIR::BuiltinFunctionKind::IntLess: case SemIR::BuiltinFunctionKind::IntLessEq: case SemIR::BuiltinFunctionKind::IntGreater: case SemIR::BuiltinFunctionKind::IntGreaterEq: { if (phase != Phase::Concrete) { break; } return PerformBuiltinIntComparison(context, builtin_kind, arg_ids[0], arg_ids[1], call.type_id); } // Floating-point conversions. case SemIR::BuiltinFunctionKind::FloatConvertChecked: { if (phase != Phase::Concrete) { return MakeConstantResult(context, call, phase); } return PerformCheckedFloatConvert(context, loc_id, arg_ids[0], call.type_id); } // Unary float -> float operations. case SemIR::BuiltinFunctionKind::FloatNegate: { if (phase != Phase::Concrete) { break; } return PerformBuiltinUnaryFloatOp(context, builtin_kind, arg_ids[0]); } // Binary float -> float operations. case SemIR::BuiltinFunctionKind::FloatAdd: case SemIR::BuiltinFunctionKind::FloatSub: case SemIR::BuiltinFunctionKind::FloatMul: case SemIR::BuiltinFunctionKind::FloatDiv: { if (phase != Phase::Concrete) { break; } return PerformBuiltinBinaryFloatOp(context, builtin_kind, arg_ids[0], arg_ids[1]); } // Float comparisons. case SemIR::BuiltinFunctionKind::FloatEq: case SemIR::BuiltinFunctionKind::FloatNeq: case SemIR::BuiltinFunctionKind::FloatLess: case SemIR::BuiltinFunctionKind::FloatLessEq: case SemIR::BuiltinFunctionKind::FloatGreater: case SemIR::BuiltinFunctionKind::FloatGreaterEq: { if (phase != Phase::Concrete) { break; } return PerformBuiltinFloatComparison(context, builtin_kind, arg_ids[0], arg_ids[1], call.type_id); } // Bool comparisons. case SemIR::BuiltinFunctionKind::BoolEq: case SemIR::BuiltinFunctionKind::BoolNeq: { if (phase != Phase::Concrete) { break; } return PerformBuiltinBoolComparison(context, builtin_kind, arg_ids[0], arg_ids[1], call.type_id); } } return SemIR::ConstantId::NotConstant; } static auto TryEvalCall(EvalContext& outer_eval_context, SemIR::LocId loc_id, const SemIR::Function& function, SemIR::SpecificId specific_id, SemIR::InstBlockId args_id) -> SemIR::ConstantId; // Makes a constant for a call instruction. static auto MakeConstantForCall(EvalContext& eval_context, SemIR::InstId inst_id, SemIR::Call call) -> SemIR::ConstantId { Phase phase = Phase::Concrete; // A call with an invalid argument list is used to represent an erroneous // call. // // TODO: Use a better representation for this. if (call.args_id == SemIR::InstBlockId::None) { return SemIR::ErrorInst::ConstantId; } // Find the constant value of the callee. bool has_constant_callee = ReplaceFieldWithConstantValue( eval_context, &call, &SemIR::Call::callee_id, &phase); auto callee = SemIR::GetCallee(eval_context.sem_ir(), call.callee_id); const SemIR::Function* function = nullptr; auto builtin_kind = SemIR::BuiltinFunctionKind::None; auto evaluation_mode = SemIR::Function::EvaluationMode::None; if (auto* callee_function = std::get_if(&callee)) { function = &eval_context.functions().Get(callee_function->function_id); builtin_kind = function->builtin_function_kind(); evaluation_mode = function->evaluation_mode; // Calls to builtins and to `eval` or `musteval` functions might be // constant. if (builtin_kind == SemIR::BuiltinFunctionKind::None && evaluation_mode == SemIR::Function::EvaluationMode::None) { return SemIR::ConstantId::NotConstant; } } else { // Calls to non-functions, such as calls to generic entity names, might be // constant. } // Find the argument values and the return type. bool has_constant_operands = has_constant_callee && ReplaceTypeWithConstantValue(eval_context, inst_id, &call, &phase) && ReplaceFieldWithConstantValue(eval_context, &call, &SemIR::Call::args_id, &phase); if (phase == Phase::UnknownDueToError) { return SemIR::ErrorInst::ConstantId; } // If any operand of the call is non-constant, the call is non-constant. // TODO: Some builtin calls might allow some operands to be non-constant. if (!has_constant_operands) { if (builtin_kind.IsCompTimeOnly( eval_context.sem_ir(), eval_context.inst_blocks().Get(call.args_id), call.type_id) || evaluation_mode == SemIR::Function::EvaluationMode::MustEval) { CARBON_DIAGNOSTIC(NonConstantCallToCompTimeOnlyFunction, Error, "non-constant call to compile-time-only function"); CARBON_DIAGNOSTIC(CompTimeOnlyFunctionHere, Note, "compile-time-only function declared here"); const auto& function = eval_context.functions().Get( std::get(callee).function_id); eval_context.emitter() .Build(inst_id, NonConstantCallToCompTimeOnlyFunction) .Note(function.latest_decl_id(), CompTimeOnlyFunctionHere) .Emit(); } return SemIR::ConstantId::NotConstant; } // Handle calls to builtins. if (builtin_kind != SemIR::BuiltinFunctionKind::None) { return MakeConstantForBuiltinCall( eval_context, SemIR::LocId(inst_id), call, builtin_kind, eval_context.inst_blocks().Get(call.args_id), phase); } // Handle calls to `eval` and `musteval` functions. if (evaluation_mode != SemIR::Function::EvaluationMode::None) { // A non-concrete call to `eval` or `musteval` is a template symbolic // constant, regardless of the phase of the arguments. if (phase != Phase::Concrete) { CARBON_CHECK(phase <= Phase::TemplateSymbolic); return MakeConstantResult(eval_context.context(), call, Phase::TemplateSymbolic); } // TODO: Instead of performing the call immediately, add it to a work queue // and do it non-recursively. return TryEvalCall( eval_context, SemIR::LocId(inst_id), *function, std::get(callee).resolved_specific_id, call.args_id); } return SemIR::ConstantId::NotConstant; } // Given an instruction, compute its phase based on its operands. static auto ComputeInstPhase(Context& context, SemIR::Inst inst) -> Phase { EvalContext eval_context(&context, SemIR::LocId::None); auto phase = GetPhase(context.constant_values(), context.types().GetConstantId(inst.type_id())); GetConstantValueForArg(eval_context, inst.arg0_and_kind(), &phase); GetConstantValueForArg(eval_context, inst.arg1_and_kind(), &phase); CARBON_CHECK(IsConstantOrError(phase)); return phase; } // Convert a ConstantEvalResult to a ConstantId. Factored out of // TryEvalTypedInst to avoid repeated instantiation of common code. static auto ConvertEvalResultToConstantId(Context& context, ConstantEvalResult result, Phase orig_phase) -> SemIR::ConstantId { if (result.is_new()) { return MakeConstantResult( context, result.new_inst(), result.same_phase_as_inst() ? orig_phase : ComputeInstPhase(context, result.new_inst())); } return result.existing(); } // Evaluates an instruction of a known type in an evaluation context. The // default behavior of this function depends on the constant kind of the // instruction: // // - InstConstantKind::Never: returns ConstantId::NotConstant. // - InstConstantKind::Indirect, SymbolicOnly, SymbolicOrReference, // Conditional: evaluates all the operands of the instruction, and calls // `EvalConstantInst` to evaluate the resulting constant instruction. // - InstConstantKind::WheneverPossible, Always: evaluates all the operands of // the instruction, and produces the resulting constant instruction as the // result. // - InstConstantKind::Unique: returns the `inst_id` as the resulting // constant. // // Returns an error constant ID if any of the nested evaluations fail, and // returns NotConstant if any of the nested evaluations is non-constant. // // This template is explicitly specialized for instructions that need special // handling. template static auto TryEvalTypedInst(EvalContext& eval_context, SemIR::InstId inst_id, SemIR::Inst inst) -> SemIR::ConstantId { constexpr auto ConstantKind = InstT::Kind.constant_kind(); if constexpr (ConstantKind == SemIR::InstConstantKind::Never) { return SemIR::ConstantId::NotConstant; } else if constexpr (ConstantKind == SemIR::InstConstantKind::AlwaysUnique) { CARBON_CHECK(inst_id.has_value()); return SemIR::ConstantId::ForConcreteConstant(inst_id); } else { // Build a constant instruction by replacing each non-constant operand with // its constant value. Phase phase = Phase::Concrete; if ((SemIR::Internal::HasTypeIdMember && !ReplaceTypeWithConstantValue(eval_context, inst_id, &inst, &phase)) || !ReplaceAllFieldsWithConstantValues(eval_context, &inst, &phase)) { if constexpr (ConstantKind == SemIR::InstConstantKind::Always) { CARBON_FATAL("{0} should always be constant", InstT::Kind); } return SemIR::ConstantId::NotConstant; } // If any operand of the instruction has an error in it, the instruction // itself evaluates to an error. if (phase == Phase::UnknownDueToError) { return SemIR::ErrorInst::ConstantId; } // When canonicalizing a SpecificId, we defer resolving the specific's // declaration until here, to avoid resolving declarations from imported // specifics. (Imported instructions are not evaluated.) ResolveSpecificDeclForInst(eval_context, inst); if constexpr (ConstantKind == SemIR::InstConstantKind::Always || ConstantKind == SemIR::InstConstantKind::WheneverPossible) { return MakeConstantResult(eval_context.context(), inst, phase); } else if constexpr (ConstantKind == SemIR::InstConstantKind::InstAction) { auto result_inst_id = PerformDelayedAction( eval_context.context(), SemIR::LocId(inst_id), inst.As()); if (result_inst_id.has_value()) { // The result is an instruction. return MakeConstantResult( eval_context.context(), SemIR::InstValue{ .type_id = GetSingletonType(eval_context.context(), SemIR::InstType::TypeInstId), .inst_id = result_inst_id}, Phase::Concrete); } // Couldn't perform the action because it's still dependent. return MakeConstantResult(eval_context.context(), inst, Phase::TemplateSymbolic); } else if constexpr (InstT::Kind.constant_needs_inst_id() != SemIR::InstConstantNeedsInstIdKind::No) { CARBON_CHECK(inst_id.has_value()); return ConvertEvalResultToConstantId( eval_context.context(), EvalConstantInst(eval_context.context(), inst_id, inst.As()), phase); } else { return ConvertEvalResultToConstantId( eval_context.context(), EvalConstantInst(eval_context.context(), inst.As()), phase); } } } // Specialize evaluation for array indexing because we want to check the index // expression even if the array expression is non-constant. template <> auto TryEvalTypedInst(EvalContext& eval_context, SemIR::InstId /*inst_id*/, SemIR::Inst inst) -> SemIR::ConstantId { return PerformArrayIndex(eval_context, inst.As()); } // Specialize evaluation for function calls because we want to check the callee // expression even if an argument expression is non-constant, and because we // will eventually want to perform control flow handling here. template <> auto TryEvalTypedInst(EvalContext& eval_context, SemIR::InstId inst_id, SemIR::Inst inst) -> SemIR::ConstantId { return MakeConstantForCall(eval_context, inst_id, inst.As()); } // ImportRefLoaded can have a constant value, but it's owned and maintained by // `import_ref.cpp`, not by us. // TODO: Rearrange how `ImportRefLoaded` instructions are created so we never // call this. template <> auto TryEvalTypedInst(EvalContext& /*eval_context*/, SemIR::InstId /*inst_id*/, SemIR::Inst /*inst*/) -> SemIR::ConstantId { return SemIR::ConstantId::NotConstant; } // Symbolic bindings are a special case because they can reach into the eval // context and produce a context-specific value. template <> auto TryEvalTypedInst(EvalContext& eval_context, SemIR::InstId inst_id, SemIR::Inst inst) -> SemIR::ConstantId { auto bind = inst.As(); // If we know which specific we're evaluating within and this is an argument // of that specific, its constant value is the corresponding argument value. const auto& bind_name = eval_context.entity_names().Get(bind.entity_name_id); if (bind_name.bind_index().has_value()) { if (auto value = eval_context.GetCompileTimeBindValue(bind_name.bind_index()); value.has_value()) { return value; } } // The constant form of a symbolic binding is an idealized form of the // original, with no equivalent value. Phase phase = Phase::Concrete; bind.value_id = SemIR::InstId::None; if (!ReplaceTypeWithConstantValue(eval_context, inst_id, &bind, &phase) || !ReplaceFieldWithConstantValue(eval_context, &bind, &SemIR::SymbolicBinding::entity_name_id, &phase)) { return SemIR::ConstantId::NotConstant; } // This correctly handles `Phase::UnknownDueToError`. return MakeConstantResult(eval_context.context(), bind, phase); } template <> auto TryEvalTypedInst(EvalContext& eval_context, SemIR::InstId inst_id, SemIR::Inst inst) -> SemIR::ConstantId { // If a specific provides a new value for the binding with `entity_name_id`, // the SymbolicBindingType is evaluated for that new value. const auto& bind_name = eval_context.entity_names().Get( inst.As().entity_name_id); if (bind_name.bind_index().has_value()) { if (auto value = eval_context.GetCompileTimeBindValue(bind_name.bind_index()); value.has_value()) { auto value_inst_id = eval_context.constant_values().GetInstId(value); // A SymbolicBindingType can evaluate to a FacetAccessType if the new // value of the entity is a facet value that that does not have a concrete // type (a FacetType) and does not have a new EntityName to point to (a // SymbolicBinding). auto access = SemIR::FacetAccessType{ .type_id = SemIR::TypeType::TypeId, .facet_value_inst_id = value_inst_id, }; return ConvertEvalResultToConstantId( eval_context.context(), EvalConstantInst(eval_context.context(), access), ComputeInstPhase(eval_context.context(), access)); } } Phase phase = Phase::Concrete; if (!ReplaceTypeWithConstantValue(eval_context, inst_id, &inst, &phase) || !ReplaceAllFieldsWithConstantValues(eval_context, &inst, &phase)) { return SemIR::ConstantId::NotConstant; } // Propagate error phase after getting the constant value for all fields. if (phase == Phase::UnknownDueToError) { return SemIR::ErrorInst::ConstantId; } // Evaluation of SymbolicBindingType. // // Like FacetAccessType, a SymbolicBindingType of a FacetValue just evaluates // to the type inside. // // TODO: Look in ScopeStack with the entity_name_id to find the facet value // and get its constant value in the current specific context. The // facet_value_inst_id will go away. if (auto facet_value = eval_context.insts().TryGetAs( inst.As().facet_value_inst_id)) { return eval_context.constant_values().Get(facet_value->type_inst_id); } return MakeConstantResult(eval_context.context(), inst, phase); } // Returns whether `const_id` is the same constant facet value as // `facet_value_inst_id`. // // Compares with the canonical facet value of `const_id`, dropping any `as type` // conversions. static auto IsSameFacetValue(Context& context, SemIR::ConstantId const_id, SemIR::InstId facet_value_inst_id) -> bool { auto canon_const_id = GetCanonicalFacetOrTypeValue(context, const_id); return canon_const_id == context.constant_values().Get(facet_value_inst_id); } // TODO: Convert this to an EvalConstantInst function. This will require // providing a `GetConstantValue` overload for a requirement block. template <> auto TryEvalTypedInst(EvalContext& eval_context, SemIR::InstId inst_id, SemIR::Inst inst) -> SemIR::ConstantId { auto typed_inst = inst.As(); Phase phase = Phase::Concrete; SemIR::FacetTypeInfo info; // Add the constraints from the `WhereExpr` instruction into `info`. if (typed_inst.requirements_id.has_value()) { auto insts = eval_context.inst_blocks().Get(typed_inst.requirements_id); for (auto inst_id : insts) { if (auto base = eval_context.insts().TryGetAs( inst_id)) { if (base->base_type_inst_id == SemIR::ErrorInst::TypeInstId) { return SemIR::ErrorInst::ConstantId; } if (auto base_facet_type = eval_context.insts().TryGetAs( base->base_type_inst_id)) { const auto& base_info = eval_context.facet_types().Get(base_facet_type->facet_type_id); info.extend_constraints.append(base_info.extend_constraints); info.self_impls_constraints.append(base_info.self_impls_constraints); info.rewrite_constraints.append(base_info.rewrite_constraints); info.other_requirements |= base_info.other_requirements; } } else if (auto rewrite = eval_context.insts().TryGetAs( inst_id)) { info.rewrite_constraints.push_back( {.lhs_id = rewrite->lhs_id, .rhs_id = rewrite->rhs_id}); } else if (auto impls = eval_context.insts().TryGetAs( inst_id)) { SemIR::ConstantId lhs_const_id = eval_context.GetConstantValue(impls->lhs_id); SemIR::ConstantId rhs_const_id = eval_context.GetConstantValue(impls->rhs_id); if (IsSameFacetValue(eval_context.context(), lhs_const_id, typed_inst.period_self_id)) { auto rhs_inst_id = eval_context.constant_values().GetInstId(rhs_const_id); if (rhs_inst_id == SemIR::ErrorInst::InstId) { // `.Self impls `. return SemIR::ErrorInst::ConstantId; } else if (rhs_inst_id == SemIR::TypeType::TypeInstId) { // `.Self impls type` -> nothing to do. } else { auto facet_type = eval_context.insts().GetAs( RequireConstantValue(eval_context, impls->rhs_id, &phase)); const auto& more_info = eval_context.facet_types().Get(facet_type.facet_type_id); // The way to prevent lookup into the interface requirements of a // facet type is to put it to the right of a `.Self impls`, which we // accomplish by putting them into `self_impls_constraints`. llvm::append_range(info.self_impls_constraints, more_info.extend_constraints); llvm::append_range(info.self_impls_constraints, more_info.self_impls_constraints); llvm::append_range(info.self_impls_named_constraints, more_info.extend_named_constraints); llvm::append_range(info.self_impls_named_constraints, more_info.self_impls_named_constraints); // Other requirements are copied in. llvm::append_range(info.rewrite_constraints, more_info.rewrite_constraints); info.other_requirements |= more_info.other_requirements; } } else { // TODO: Handle `impls` constraints beyond `.Self impls`. info.other_requirements = true; } } else { // TODO: Handle other requirements. info.other_requirements = true; } } } auto const_info = GetConstantFacetTypeInfo( eval_context, SemIR::LocId(inst_id), info, &phase); return MakeFacetTypeResult(eval_context.context(), const_info, phase); } // Implementation for `TryEvalInst`, wrapping `Context` with `EvalContext`. static auto TryEvalInstInContext(EvalContext& eval_context, SemIR::InstId inst_id, SemIR::Inst inst) -> SemIR::ConstantId { using EvalInstFn = auto(EvalContext & eval_context, SemIR::InstId inst_id, SemIR::Inst inst) ->SemIR::ConstantId; static constexpr EvalInstFn* EvalInstFns[] = { #define CARBON_SEM_IR_INST_KIND(Kind) &TryEvalTypedInst, #include "toolchain/sem_ir/inst_kind.def" }; [[clang::musttail]] return EvalInstFns[inst.kind().AsInt()](eval_context, inst_id, inst); } auto TryEvalInstUnsafe(Context& context, SemIR::InstId inst_id, SemIR::Inst inst) -> SemIR::ConstantId { EvalContext eval_context(&context, SemIR::LocId(inst_id)); return TryEvalInstInContext(eval_context, inst_id, inst); } auto TryEvalBlockForSpecific(Context& context, SemIR::LocId loc_id, SemIR::SpecificId specific_id, SemIR::GenericInstIndex::Region region) -> SemIR::InstBlockId { auto generic_id = context.specifics().Get(specific_id).generic_id; auto eval_block_id = context.generics().Get(generic_id).GetEvalBlock(region); auto eval_block = context.inst_blocks().Get(eval_block_id); llvm::SmallVector result; result.resize(eval_block.size(), SemIR::InstId::None); EvalContext eval_context(&context, loc_id, specific_id, SpecificEvalInfo{ .region = region, .values = result, }); Diagnostics::ContextScope diagnostic_context( &context.emitter(), [&](auto& builder) { CARBON_DIAGNOSTIC(ResolvingSpecificHere, SoftContext, "unable to monomorphize specific {0}", SemIR::SpecificId); builder.Context(loc_id, ResolvingSpecificHere, specific_id); }); for (auto [i, inst_id] : llvm::enumerate(eval_block)) { auto const_id = TryEvalInstInContext(eval_context, inst_id, context.insts().Get(inst_id)); result[i] = context.constant_values().GetInstId(const_id); CARBON_CHECK(result[i].has_value(), "Failed to evaluate {0} in eval block", context.insts().Get(inst_id)); } return context.inst_blocks().Add(result); } // Information about the function call we are currently executing. Unlike // evaluation, execution sequentially interprets instructions, and can handle // control flow and (eventually) side effects and mutable state. class FunctionExecContext : public EvalContext { public: // A block argument passed to `BranchWithArg`. struct BlockArgValue { SemIR::InstBlockId block_id = SemIR::InstBlockId::None; SemIR::ConstantId arg_id = SemIR::ConstantId::None; }; FunctionExecContext(Context* context, SemIR::LocId loc_id, SemIR::SpecificId specific_id, Map* locals, SemIR::InstBlockId args_id) : EvalContext(context, loc_id, specific_id, LocalEvalInfo{.locals = locals}), args_(context->inst_blocks().Get(args_id)) {} // Returns the argument values supplied in the call to the function. auto args() const -> llvm::ArrayRef { return args_; } using EvalContext::locals; // Branch control flow to the given block. This replaces the innermost block // in the block stack, but doesn't affect any enclosing blocks. auto BranchTo(SemIR::InstBlockId block_id) -> void { blocks_.back() = inst_blocks().Get(block_id); } // Push a new block to be executed immediately. After the block finishes, // control will resume after the current instruction. auto PushBlock(SemIR::InstBlockId block_id) -> void { blocks_.push_back(inst_blocks().Get(block_id)); } // Pops and returns the next instruction to be executed. auto PopNextInstId() -> SemIR::InstId { while (blocks_.back().empty()) { blocks_.pop_back(); CARBON_CHECK(!blocks_.empty(), "Fell off end of function"); } return blocks_.back().consume_front(); } // Sets the most recent block argument value provided by a `BranchWithArg`. // This can later be retrieved by a `BlockArg`. auto SetCurrentBlockArgValue(BlockArgValue arg) -> void { current_block_arg_value_ = arg; } // Returns the most recent block argument value provided by a `BranchWithArg`. auto current_block_arg_value() const -> BlockArgValue { return current_block_arg_value_; } private: // The stack of code blocks that we are currently evaluating. This is kept as // a stack so that we can schedule the function body to execute after the decl // block and so that we can handle `SpliceBlock`s. When the innermost block is // complete, it will be popped and the next outer block will execute. llvm::SmallVector, 4> blocks_; // The arguments in the function call. llvm::ArrayRef args_; // The block argument provided by the most recently executed `BranchWithArg`. // We assume that we only need to track one of these, as the branch target // will invoke `BlockArg` before the next `BranchWithArg` happens. We will // need to track more than one of these if that ever changes. BlockArgValue current_block_arg_value_; }; // Handles the result of executing an instruction in a function. Returns an // error the result is not a constant, and otherwise updates the locals map to // track the result as an input to later evaluations in this function and // returns None. static auto HandleExecResult(FunctionExecContext& eval_context, SemIR::InstId inst_id, SemIR::ConstantId const_id) -> SemIR::ConstantId { if (!const_id.has_value() || !const_id.is_constant()) { DiagnoseNonConstantValue(eval_context, inst_id); return SemIR::ErrorInst::ConstantId; } if (const_id == SemIR::ErrorInst::ConstantId) { return const_id; } eval_context.locals().Update(inst_id, const_id); return SemIR::ConstantId::None; } // Executes an instruction for TryEvalCall. By default, performs normal // evaluation of the instruction within a context that supplies the values // produced by executing prior instructions in this function execution. This is // specialized for instructions that have special handling in function // execution, such as those that access parameters or perform flow control. If // execution should continue, returns `SemIR::ConstantId::None`, otherwise // returns the result to produce for the enclosing function call, which should // be either the returned value or an error. template static auto TryExecTypedInst(FunctionExecContext& eval_context, SemIR::InstId inst_id, SemIR::Inst inst) -> SemIR::ConstantId { if constexpr (InstT::Kind.expr_category().TryAsFixedCategory() == SemIR::ExprCategory::NotExpr) { // Instructions in this category are assumed to not have a runtime effect. // This includes some kinds of declaration. return SemIR::ConstantId::None; } if constexpr (InstT::Kind.constant_kind() != SemIR::InstConstantKind::Never) { if (eval_context.constant_values().Get(inst_id).is_concrete()) { // Instruction has a concrete constant value that doesn't depend on the // context. We don't need to evaluate it again. return SemIR::ConstantId::None; } } // Evaluate the instruction in the current context. auto const_id = TryEvalTypedInst(eval_context, inst_id, inst); return HandleExecResult(eval_context, inst_id, const_id); } template <> auto TryExecTypedInst(FunctionExecContext& eval_context, SemIR::InstId inst_id, SemIR::Inst inst) -> SemIR::ConstantId { auto block_arg = inst.As(); CARBON_CHECK( block_arg.block_id == eval_context.current_block_arg_value().block_id, "BlockArg does not refer to most recent BranchWithArg"); eval_context.locals().Update(inst_id, eval_context.current_block_arg_value().arg_id); return SemIR::ConstantId::None; } template <> auto TryExecTypedInst(FunctionExecContext& eval_context, SemIR::InstId /*inst_id*/, SemIR::Inst inst) -> SemIR::ConstantId { auto branch = inst.As(); eval_context.BranchTo(branch.target_id); return SemIR::ConstantId::None; } template <> auto TryExecTypedInst(FunctionExecContext& eval_context, SemIR::InstId /*inst_id*/, SemIR::Inst inst) -> SemIR::ConstantId { auto branch_if = inst.As(); auto cond_id = CheckConcreteValue(eval_context, branch_if.cond_id); if (cond_id == SemIR::ErrorInst::InstId) { return SemIR::ErrorInst::ConstantId; } auto cond = eval_context.insts().GetAs(cond_id); if (cond.value == SemIR::BoolValue::True) { eval_context.BranchTo(branch_if.target_id); } return SemIR::ConstantId::None; } template <> auto TryExecTypedInst(FunctionExecContext& eval_context, SemIR::InstId /*inst_id*/, SemIR::Inst inst) -> SemIR::ConstantId { auto branch = inst.As(); eval_context.SetCurrentBlockArgValue( {.block_id = branch.target_id, .arg_id = eval_context.GetConstantValue(branch.arg_id)}); eval_context.BranchTo(branch.target_id); return SemIR::ConstantId::None; } template <> auto TryExecTypedInst(FunctionExecContext& eval_context, SemIR::InstId /*inst_id*/, SemIR::Inst /*inst*/) -> SemIR::ConstantId { return MakeEmptyTupleResult(eval_context); } template <> auto TryExecTypedInst(FunctionExecContext& eval_context, SemIR::InstId /*inst_id*/, SemIR::Inst inst) -> SemIR::ConstantId { auto return_expr = inst.As(); return eval_context.GetConstantValue(return_expr.expr_id); } template <> auto TryExecTypedInst(FunctionExecContext& eval_context, SemIR::InstId inst_id, SemIR::Inst inst) -> SemIR::ConstantId { auto return_slot = inst.As(); // In the case where the function's return type is not in-place, the return // slot will refer to an out parameter that doesn't have an argument. In that // case, we don't have a constant value for storage_id. To handle this, copy // the value directly from the locals map rather than using GetConstantValue. // // TODO: Remove this and use a normal call to `GetConstantValue` if we stop // adding out parameters with no corresponding argument. eval_context.locals().Insert( inst_id, eval_context.locals().Lookup(return_slot.storage_id).value()); return SemIR::ConstantId::None; } template <> auto TryExecTypedInst(FunctionExecContext& eval_context, SemIR::InstId /*inst_id*/, SemIR::Inst inst) -> SemIR::ConstantId { auto splice_block = inst.As(); eval_context.PushBlock(splice_block.block_id); // TODO: Copy the values from the result_id instruction to the result of // the splice_block instruction once the spliced block finishes. return SemIR::ConstantId::None; } // Executes the introduction of a parameter into the local scope. Copies the // argument supplied by the caller for the parameter into the locals map. static auto TryExecTypedParam(FunctionExecContext& eval_context, SemIR::InstId inst_id, SemIR::Inst inst) -> SemIR::ConstantId { auto param = inst.As(); CARBON_CHECK(static_cast(param.index.index) < eval_context.args().size()); eval_context.locals().Insert(inst_id, eval_context.constant_values().Get( eval_context.args()[param.index.index])); return SemIR::ConstantId::None; } template <> auto TryExecTypedInst(FunctionExecContext& eval_context, SemIR::InstId inst_id, SemIR::Inst inst) -> SemIR::ConstantId { auto param = inst.As(); if (static_cast(param.index.index) >= eval_context.args().size()) { // For return values that have a copy initializing representation, the SemIR // has an OutParam with an index that has no corresponding argument. In that // case, we do not have a constant value for the parameter, but this doesn't // prevent the call from being constant. // // TODO: Remove this once we stop adding out parameters with no // corresponding argument. eval_context.locals().Insert(inst_id, SemIR::ConstantId::None); return SemIR::ConstantId::None; } return TryExecTypedParam(eval_context, inst_id, inst); } template <> auto TryExecTypedInst(FunctionExecContext& eval_context, SemIR::InstId inst_id, SemIR::Inst inst) -> SemIR::ConstantId { return TryExecTypedParam(eval_context, inst_id, inst); } template <> auto TryExecTypedInst(FunctionExecContext& eval_context, SemIR::InstId inst_id, SemIR::Inst inst) -> SemIR::ConstantId { return TryExecTypedParam(eval_context, inst_id, inst); } template <> auto TryExecTypedInst(FunctionExecContext& eval_context, SemIR::InstId inst_id, SemIR::Inst inst) -> SemIR::ConstantId { auto value_binding = inst.As(); auto local_value_id = eval_context.GetConstantValue(value_binding.value_id); eval_context.locals().Insert(inst_id, local_value_id); return SemIR::ConstantId::None; } static auto TryExecInst(FunctionExecContext& eval_context, SemIR::InstId inst_id, SemIR::Inst inst) -> SemIR::ConstantId { using ExecInstFn = auto(FunctionExecContext & eval_context, SemIR::InstId inst_id, SemIR::Inst inst) ->SemIR::ConstantId; static constexpr ExecInstFn* ExecInstFns[] = { #define CARBON_SEM_IR_INST_KIND(Kind) &TryExecTypedInst, #include "toolchain/sem_ir/inst_kind.def" }; [[clang::musttail]] return ExecInstFns[inst.kind().AsInt()](eval_context, inst_id, inst); } // Evaluates a call to an `eval` or `musteval` function by executing the // function body. static auto TryEvalCall(EvalContext& outer_eval_context, SemIR::LocId loc_id, const SemIR::Function& function, SemIR::SpecificId specific_id, SemIR::InstBlockId args_id) -> SemIR::ConstantId { if (function.clang_decl_id != SemIR::ClangDeclId::None) { return EvalCppCall(outer_eval_context.context(), loc_id, function.clang_decl_id, args_id); } else if (function.body_block_ids.empty()) { // TODO: Diagnose this. return SemIR::ConstantId::NotConstant; } // TODO: Consider tracking the lowest and highest inst_id in the function and // using an array instead of a map. We would still need a map for instantiated // portions of a function template. Map locals; FunctionExecContext eval_context(&outer_eval_context.context(), loc_id, specific_id, &locals, args_id); Diagnostics::AnnotationScope annotate_diagnostics( &eval_context.emitter(), [&](auto& builder) { CARBON_DIAGNOSTIC(InCallToEvalFn, Note, "in call to {0} here", SemIR::NameId); builder.Note(loc_id, InCallToEvalFn, function.name_id); }); // Execute the function decl block followed by the body. eval_context.PushBlock(function.body_block_ids.front()); eval_context.PushBlock(eval_context.insts() .GetAs(function.definition_id) .decl_block_id); // Execute the blocks. This is mostly expression evaluation, with special // handling for control flow and parameters. while (true) { auto inst_id = eval_context.PopNextInstId(); auto inst = eval_context.context().insts().Get(inst_id); if (auto result = TryExecInst(eval_context, inst_id, inst); result.has_value()) { return result; } } } } // namespace Carbon::Check ================================================ FILE: toolchain/check/eval.h ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #ifndef CARBON_TOOLCHAIN_CHECK_EVAL_H_ #define CARBON_TOOLCHAIN_CHECK_EVAL_H_ #include "toolchain/check/context.h" #include "toolchain/sem_ir/ids.h" #include "toolchain/sem_ir/inst.h" #include "toolchain/sem_ir/inst_kind.h" namespace Carbon::Check { // Adds a `ConstantId` for a constant that has been imported from another IR. // Does not evaluate the instruction, instead trusting that it is already in a // suitable form, but does canonicalize the operands if necessary. // TODO: Rely on import to canonicalize the operands to avoid this work. auto AddImportedConstant(Context& context, SemIR::Inst inst) -> SemIR::ConstantId; // Evaluates the instruction `inst`. If `inst_id` is specified, it is the ID of // the instruction; otherwise, evaluation of the instruction must not require an // `InstId` to be provided. auto TryEvalInstUnsafe(Context& context, SemIR::InstId inst_id, SemIR::Inst inst) -> SemIR::ConstantId; // Determines the phase of the instruction `inst_id`, and returns its constant // value if it has constant phase. If it has runtime phase, returns // `SemIR::ConstantId::NotConstant`. inline auto TryEvalInst(Context& context, SemIR::InstId inst_id) -> SemIR::ConstantId { return TryEvalInstUnsafe(context, inst_id, context.insts().Get(inst_id)); } // Same, but for a typed instruction that doesn't have an InstId assigned yet, // in the case where evaluation doesn't need an InstId. This can be used to // avoid allocating an instruction in the case where you just want a constant // value and the instruction is known to not matter. However, even then care // should be taken: if the produced constant is symbolic, you may still need an // instruction to associate the constant with the enclosing generic. // // To evaluate an instruction and add it to SemIR only if necessary, use // EvalOrAddInst instead. template requires(InstT::Kind.constant_needs_inst_id() == SemIR::InstConstantNeedsInstIdKind::No) auto TryEvalInst(Context& context, InstT inst) -> SemIR::ConstantId { return TryEvalInstUnsafe(context, SemIR::InstId::None, inst); } // Evaluates the eval block for a region of a specific. Produces a block // containing the evaluated constant values of the instructions in the eval // block. // // TODO: Return whether any of the instructions produced contain an ErrorInst. auto TryEvalBlockForSpecific(Context& context, SemIR::LocId loc_id, SemIR::SpecificId specific_id, SemIR::GenericInstIndex::Region region) -> SemIR::InstBlockId; } // namespace Carbon::Check #endif // CARBON_TOOLCHAIN_CHECK_EVAL_H_ ================================================ FILE: toolchain/check/eval_inst.cpp ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #include "toolchain/check/eval_inst.h" #include #include "toolchain/base/kind_switch.h" #include "toolchain/check/action.h" #include "toolchain/check/cpp/constant.h" #include "toolchain/check/cpp/import.h" #include "toolchain/check/cpp/type_mapping.h" #include "toolchain/check/diagnostic_helpers.h" #include "toolchain/check/facet_type.h" #include "toolchain/check/generic.h" #include "toolchain/check/impl_lookup.h" #include "toolchain/check/import_ref.h" #include "toolchain/check/inst.h" #include "toolchain/check/type.h" #include "toolchain/check/type_completion.h" #include "toolchain/diagnostics/diagnostic.h" #include "toolchain/parse/typed_nodes.h" #include "toolchain/sem_ir/builtin_function_kind.h" #include "toolchain/sem_ir/expr_info.h" #include "toolchain/sem_ir/ids.h" #include "toolchain/sem_ir/pattern.h" #include "toolchain/sem_ir/typed_insts.h" namespace Carbon::Check { // Performs an access into an aggregate, retrieving the specified element. static auto PerformAggregateAccess(Context& context, SemIR::Inst inst) -> ConstantEvalResult { auto access_inst = inst.As(); if (auto aggregate = context.insts().TryGetAs( access_inst.aggregate_id)) { auto elements = context.inst_blocks().Get(aggregate->elements_id); auto index = static_cast(access_inst.index.index); CARBON_CHECK(index < elements.size(), "Access out of bounds."); // `Phase` is not used here. If this element is a concrete constant, then // so is the result of indexing, even if the aggregate also contains a // symbolic context. return ConstantEvalResult::Existing( context.constant_values().Get(elements[index])); } return ConstantEvalResult::NewSamePhase(inst); } auto EvalConstantInst(Context& /*context*/, SemIR::ArrayInit inst) -> ConstantEvalResult { // TODO: Add an `ArrayValue` to represent a constant array object // representation instead of using a `TupleValue`. return ConstantEvalResult::NewSamePhase( SemIR::TupleValue{.type_id = inst.type_id, .elements_id = inst.inits_id}); } auto EvalConstantInst(Context& context, SemIR::InstId inst_id, SemIR::ArrayType inst) -> ConstantEvalResult { auto bound_inst = context.insts().Get(inst.bound_id); auto int_bound = bound_inst.TryAs(); if (!int_bound) { CARBON_CHECK(context.constant_values().Get(inst.bound_id).is_symbolic(), "Unexpected inst {0} for template constant int", bound_inst); return ConstantEvalResult::NewSamePhase(inst); } // TODO: We should check that the size of the resulting array type // fits in 64 bits, not just that the bound does. Should we use a // 32-bit limit for 32-bit targets? const auto& bound_val = context.ints().Get(int_bound->int_id); if (context.types().IsSignedInt(int_bound->type_id) && bound_val.isNegative()) { CARBON_DIAGNOSTIC(ArrayBoundNegative, Error, "array bound of {0} is negative", TypedInt); context.emitter().Emit( context.insts().GetAs(inst_id).bound_id, ArrayBoundNegative, {.type = int_bound->type_id, .value = bound_val}); return ConstantEvalResult::Error; } if (bound_val.getActiveBits() > 64) { CARBON_DIAGNOSTIC(ArrayBoundTooLarge, Error, "array bound of {0} is too large", TypedInt); context.emitter().Emit( context.insts().GetAs(inst_id).bound_id, ArrayBoundTooLarge, {.type = int_bound->type_id, .value = bound_val}); return ConstantEvalResult::Error; } return ConstantEvalResult::NewSamePhase(inst); } auto EvalConstantInst(Context& context, SemIR::AsCompatible inst) -> ConstantEvalResult { // AsCompatible changes the type of the source instruction; its constant // value, if there is one, needs to be modified to be of the same type. auto value_id = context.constant_values().Get(inst.source_id); CARBON_CHECK(value_id.is_constant()); auto value_inst = context.insts().Get(context.constant_values().GetInstId(value_id)); value_inst.SetType(inst.type_id); return ConstantEvalResult::NewAnyPhase(value_inst); } auto EvalConstantInst(Context& context, SemIR::AliasBinding inst) -> ConstantEvalResult { // An alias evaluates to the value it's bound to. return ConstantEvalResult::Existing( context.constant_values().Get(inst.value_id)); } auto EvalConstantInst(Context& context, SemIR::RefBinding inst) -> ConstantEvalResult { // A reference binding evaluates to the value it's bound to. if (inst.value_id.has_value()) { return ConstantEvalResult::Existing( context.constant_values().Get(inst.value_id)); } return ConstantEvalResult::NotConstant; } auto EvalConstantInst(Context& /*context*/, SemIR::ValueBinding /*inst*/) -> ConstantEvalResult { // Non-`:!` value bindings are not constant. return ConstantEvalResult::NotConstant; } auto EvalConstantInst(Context& context, SemIR::InstId inst_id, SemIR::AcquireValue inst) -> ConstantEvalResult { if (const auto* var_decl = GetAsClangVarDecl(context, inst.value_id)) { auto const_id = EvalCppVarDecl(context, SemIR::LocId(inst_id), var_decl, inst.type_id); if (const_id.has_value() && const_id.is_constant()) { return ConstantEvalResult::Existing(const_id); } return ConstantEvalResult::NotConstant; } return ConstantEvalResult::TODO; } auto EvalConstantInst(Context& context, SemIR::ClassElementAccess inst) -> ConstantEvalResult { return PerformAggregateAccess(context, inst); } auto EvalConstantInst(Context& context, SemIR::ClassDecl inst) -> ConstantEvalResult { const auto& class_info = context.classes().Get(inst.class_id); // If the class has generic parameters, we don't produce a class type, but a // callable whose return value is a class type. if (class_info.has_parameters()) { return ConstantEvalResult::NewSamePhase(SemIR::StructValue{ .type_id = inst.type_id, .elements_id = SemIR::InstBlockId::Empty}); } // A non-generic class declaration evaluates to the class type. return ConstantEvalResult::NewAnyPhase(SemIR::ClassType{ .type_id = SemIR::TypeType::TypeId, .class_id = inst.class_id, .specific_id = context.generics().GetSelfSpecific(class_info.generic_id)}); } auto EvalConstantInst(Context& /*context*/, SemIR::ClassInit inst) -> ConstantEvalResult { // TODO: Add a `ClassValue` to represent a constant class object // representation instead of using a `StructValue`. return ConstantEvalResult::NewSamePhase(SemIR::StructValue{ .type_id = inst.type_id, .elements_id = inst.elements_id}); } auto EvalConstantInst(Context& context, SemIR::ConstType inst) -> ConstantEvalResult { // `const (const T)` evaluates to `const T`. if (context.insts().Is(inst.inner_id)) { return ConstantEvalResult::Existing( context.constant_values().Get(inst.inner_id)); } // Otherwise, `const T` evaluates to itself. return ConstantEvalResult::NewSamePhase(inst); } auto EvalConstantInst(Context& /*context*/, SemIR::PartialType inst) -> ConstantEvalResult { return ConstantEvalResult::NewSamePhase(inst); } auto EvalConstantInst(Context& context, SemIR::Converted inst) -> ConstantEvalResult { // A conversion evaluates to the result of the conversion. return ConstantEvalResult::Existing( context.constant_values().Get(inst.result_id)); } auto EvalConstantInst(Context& /*context*/, SemIR::Deref /*inst*/) -> ConstantEvalResult { // TODO: Handle this. return ConstantEvalResult::TODO; } auto EvalConstantInst(Context& context, SemIR::ExportDecl inst) -> ConstantEvalResult { // An export instruction evaluates to the exported declaration. return ConstantEvalResult::Existing( context.constant_values().Get(inst.value_id)); } auto EvalConstantInst(Context& context, SemIR::FacetAccessType inst) -> ConstantEvalResult { if (auto facet_value = context.insts().TryGetAs( inst.facet_value_inst_id)) { return ConstantEvalResult::Existing( context.constant_values().Get(facet_value->type_inst_id)); } if (auto bind_name = context.insts().TryGetAs( inst.facet_value_inst_id)) { return ConstantEvalResult::NewSamePhase(SemIR::SymbolicBindingType{ .type_id = SemIR::TypeType::TypeId, .entity_name_id = bind_name->entity_name_id, // TODO: This is to be removed, at which point explore if we should // replace NewSamePhase with NewAnyPhase (to make the constant value // concrete). This is still a symbolic type though even if the inst // doesn't contain a symbolic constant. Previously we crashed in CHECKs // when we had a symbolic instruction with only an EntityNameId, due to // it not changing in a generic eval block. Maybe that has improved in // the latest version of this instruction. If it's not symbolic, then // SubstConstantCallbacks and other Subst callers may need to handle // looking through concrete instructions which would be unfortunate. .facet_value_inst_id = inst.facet_value_inst_id}); } // The `facet_value_inst_id` is always a facet value (has type facet type). CARBON_CHECK(context.types().Is( context.insts().Get(inst.facet_value_inst_id).type_id())); // Other instructions (e.g. ImplWitnessAccess) of type FacetType can appear // here, in which case the constant inst is a FacetAccessType until those // instructions resolve to one of the above. return ConstantEvalResult::NewSamePhase(inst); } auto EvalConstantInst(Context& context, SemIR::FacetValue inst) -> ConstantEvalResult { // A FacetValue that just wraps a SymbolicBinding without adding/removing any // witnesses is evaluated back to the SymbolicBinding itself. if (auto bind_as_type = context.insts().TryGetAs( inst.type_inst_id)) { // TODO: Look in ScopeStack with the entity_name_id to find the facet value. auto bind_id = bind_as_type->facet_value_inst_id; auto bind = context.insts().GetAs(bind_id); // If the FacetTypes are the same, then the FacetValue didn't add/remove // any witnesses. if (bind.type_id == inst.type_id) { return ConstantEvalResult::Existing( context.constant_values().Get(bind_id)); } } return ConstantEvalResult::NewSamePhase(inst); } auto EvalConstantInst(Context& context, SemIR::InstId inst_id, SemIR::FloatType inst) -> ConstantEvalResult { return ValidateFloatTypeAndSetKind(context, SemIR::LocId(inst_id), inst) ? ConstantEvalResult::NewSamePhase(inst) : ConstantEvalResult::Error; } auto EvalConstantInst(Context& /*context*/, SemIR::FunctionDecl inst) -> ConstantEvalResult { // A function declaration evaluates to a function object, which is an empty // object of function type. // TODO: Eventually we may need to handle captures here. return ConstantEvalResult::NewSamePhase(SemIR::StructValue{ .type_id = inst.type_id, .elements_id = SemIR::InstBlockId::Empty}); } auto EvalConstantInst(Context& context, SemIR::InstId inst_id, SemIR::LookupImplWitness inst) -> ConstantEvalResult { // The self value is canonicalized in order to produce a canonical // LookupImplWitness instruction, avoiding multiple constant values for // `` and `` as type, which always have the same // lookup result. auto self_facet_value_inst_id = GetCanonicalFacetOrTypeValue(context, inst.query_self_inst_id); // When we look for a witness in the (facet) type of self, we may get a // concrete witness from a `FacetValue` (which is `self_facet_value_inst_id`) // in which case this instruction evaluates to that witness. // // If we only get a symbolic witness result though, then this instruction // evaluates to a `LookupImplWitness`. Since there was no concrete result in // the `FacetValue`, we don't need to preserve it. By looking through the // `FacetValue` at the type value it wraps to generate a more canonical value // for a symbolic `LookupImplWitness`. This makes us produce the same constant // value for symbolic lookups in `FacetValue(T)` and `T`, since they will // always have the same lookup result later, when `T` is replaced in a // specific by something that can provide a concrete witness. if (auto facet_value = context.insts().TryGetAs( self_facet_value_inst_id)) { inst.query_self_inst_id = GetCanonicalFacetOrTypeValue(context, facet_value->type_inst_id); } else { inst.query_self_inst_id = self_facet_value_inst_id; } auto result = EvalLookupSingleImplWitness(context, SemIR::LocId(inst_id), inst, self_facet_value_inst_id, EvalImplLookupMode::Normal); if (!result.has_value()) { // We use NotConstant to communicate back to impl lookup that the lookup // failed. This can not happen for a deferred symbolic lookup in a generic // eval block, since we only add the deferred lookup instruction (being // evaluated here) to the SemIR if the lookup succeeds. return ConstantEvalResult::NotConstant; } if (result.has_final_value()) { return ConstantEvalResult::Existing( context.constant_values().Get(result.final_witness())); } return ConstantEvalResult::NewSamePhase(inst); } auto EvalConstantInst(Context& context, SemIR::InstId inst_id, SemIR::ImplWitnessAccess inst) -> ConstantEvalResult { CARBON_DIAGNOSTIC(ImplAccessMemberBeforeSet, Error, "accessing member from impl before it has a defined value"); CARBON_KIND_SWITCH(context.insts().Get(inst.witness_id)) { case CARBON_KIND(SemIR::ImplWitness witness): { // This is PerformAggregateAccess followed by GetConstantValueInSpecific. auto witness_table = context.insts().GetAs( witness.witness_table_id); auto elements = context.inst_blocks().Get(witness_table.elements_id); // `elements` can be empty if there is only a forward declaration of the // impl. if (!elements.empty()) { auto index = static_cast(inst.index.index); CARBON_CHECK(index < elements.size(), "Access out of bounds."); auto element = elements[index]; if (element.has_value()) { LoadImportRef(context, element); return ConstantEvalResult::Existing(GetConstantValueInSpecific( context.sem_ir(), witness.specific_id, element)); } } // If we get here, this impl witness table entry has not been populated // yet, because the impl was referenced within its own definition. // TODO: Add note pointing to the impl declaration. context.emitter().Emit(inst_id, ImplAccessMemberBeforeSet); return ConstantEvalResult::Error; } case CARBON_KIND(SemIR::CustomWitness custom_witness): { auto elements = context.inst_blocks().Get(custom_witness.elements_id); auto index = static_cast(inst.index.index); // `elements` can be shorter than the number of associated entities while // we're building the synthetic witness. if (index < elements.size()) { return ConstantEvalResult::Existing( context.constant_values().Get(elements[index])); } // If we get here, this synthesized witness table entry has not been // populated yet. // TODO: Is this reachable? We have no test coverage for this diagnostic. context.emitter().Emit(inst_id, ImplAccessMemberBeforeSet); return ConstantEvalResult::Error; } case CARBON_KIND(SemIR::LookupImplWitness witness): { // If the witness is symbolic but has a self type that is a FacetType, it // can pull rewrite values from the self type. If the access is for one of // those rewrites, evaluate to the RHS of the rewrite. auto witness_self_type_id = context.insts().Get(witness.query_self_inst_id).type_id(); if (!context.types().Is(witness_self_type_id)) { return ConstantEvalResult::NewSamePhase(inst); } // The `ImplWitnessAccess` is accessing a value, by index, for this // interface. auto access_interface_id = witness.query_specific_interface_id; auto witness_self_facet_type_id = context.types() .GetAs(witness_self_type_id) .facet_type_id; // TODO: We could consider something better than linear search here, such // as a map. However that would probably require heap allocations which // may be worse overall since the number of rewrite constraints is // generally low. If the `rewrite_constraints` were sorted so that // associated constants are grouped together, as in // ResolveFacetTypeRewriteConstraints(), and limited to just the // `ImplWitnessAccess` entries, then a binary search may work here. for (auto witness_rewrite : context.facet_types() .Get(witness_self_facet_type_id) .rewrite_constraints) { // Look at each rewrite constraint in the self facet value's type. If // the LHS is an `ImplWitnessAccess` into the same interface that `inst` // is indexing into, then we can use its RHS as the value. auto witness_rewrite_lhs_access = context.insts().TryGetAs( witness_rewrite.lhs_id); if (!witness_rewrite_lhs_access) { continue; } if (witness_rewrite_lhs_access->index != inst.index) { continue; } auto witness_rewrite_lhs_interface_id = context.insts() .GetAs( witness_rewrite_lhs_access->witness_id) .query_specific_interface_id; if (witness_rewrite_lhs_interface_id != access_interface_id) { continue; } // The `ImplWitnessAccess` evaluates to the RHS from the witness self // facet value's type. return ConstantEvalResult::Existing( context.constant_values().Get(witness_rewrite.rhs_id)); } break; } default: break; } return ConstantEvalResult::NewSamePhase(inst); } auto EvalConstantInst(Context& context, SemIR::ImplWitnessAccessSubstituted inst) -> ConstantEvalResult { return ConstantEvalResult::Existing( context.constant_values().Get(inst.value_id)); } auto EvalConstantInst(Context& context, SemIR::ImplWitnessAssociatedConstant inst) -> ConstantEvalResult { return ConstantEvalResult::Existing( context.constant_values().Get(inst.inst_id)); } auto EvalConstantInst(Context& /*context*/, SemIR::ImportRefUnloaded inst) -> ConstantEvalResult { CARBON_FATAL("ImportRefUnloaded should be loaded before TryEvalInst: {0}", inst); } auto EvalConstantInst(Context& context, SemIR::InPlaceInit inst) -> ConstantEvalResult { // Initialization is not performed in-place during constant evaluation, so // just return the value of the initializer. return ConstantEvalResult::Existing( context.constant_values().Get(inst.src_id)); } auto EvalConstantInst(Context& context, SemIR::InstId inst_id, SemIR::IntType inst) -> ConstantEvalResult { return ValidateIntType(context, SemIR::LocId(inst_id), inst) ? ConstantEvalResult::NewSamePhase(inst) : ConstantEvalResult::Error; } auto EvalConstantInst(Context& context, SemIR::InterfaceDecl inst) -> ConstantEvalResult { const auto& interface_info = context.interfaces().Get(inst.interface_id); // If the interface has generic parameters, we don't produce an interface // type, but a callable whose return value is an interface type. if (interface_info.has_parameters()) { return ConstantEvalResult::NewSamePhase(SemIR::StructValue{ .type_id = inst.type_id, .elements_id = SemIR::InstBlockId::Empty}); } // A non-parameterized interface declaration evaluates to a declared facet // type containing just the interface. return ConstantEvalResult::NewAnyPhase(FacetTypeFromInterface( context, inst.interface_id, context.generics().GetSelfSpecific(interface_info.generic_id))); } auto EvalConstantInst(Context& context, SemIR::NamedConstraintDecl inst) -> ConstantEvalResult { const auto& named_constraint_info = context.named_constraints().Get(inst.named_constraint_id); // If the named constraint has generic parameters, we don't produce a named // constraint type, but a callable whose return value is a named constraint // type. if (named_constraint_info.has_parameters()) { return ConstantEvalResult::NewSamePhase(SemIR::StructValue{ .type_id = inst.type_id, .elements_id = SemIR::InstBlockId::Empty}); } // A non-parameterized named constraint declaration evaluates to a declared // facet type containing just the named constraint. return ConstantEvalResult::NewAnyPhase(FacetTypeFromNamedConstraint( context, inst.named_constraint_id, context.generics().GetSelfSpecific(named_constraint_info.generic_id))); } auto EvalConstantInst(Context& context, SemIR::NameRef inst) -> ConstantEvalResult { // A name reference evaluates to the value the name resolves to. return ConstantEvalResult::Existing( context.constant_values().Get(inst.value_id)); } auto EvalConstantInst(Context& context, SemIR::InstId inst_id, SemIR::RequireCompleteType inst) -> ConstantEvalResult { auto witness_type_id = GetSingletonType(context, SemIR::WitnessType::TypeInstId); // If the type is a concrete constant, require it to be complete now. auto complete_type_id = context.types().GetTypeIdForTypeInstId(inst.complete_type_inst_id); if (complete_type_id.is_concrete()) { Diagnostics::ContextScope diagnostic_context( &context.emitter(), [&](auto& builder) { CARBON_DIAGNOSTIC(IncompleteTypeInMonomorphization, Context, "{0} evaluates to incomplete type {1}", InstIdAsType, InstIdAsType); builder.Context(inst_id, IncompleteTypeInMonomorphization, context.insts() .GetAs(inst_id) .complete_type_inst_id, inst.complete_type_inst_id); }); // We use TryToCompleteType() instead of RequireCompleteType() because we // are currently evaluating a RequireCompleteType instruction, and calling // RequireCompleteType() would insert another copy of the same instruction. if (!TryToCompleteType(context, complete_type_id, SemIR::LocId(inst_id), true)) { return ConstantEvalResult::Error; } return ConstantEvalResult::NewSamePhase(SemIR::CompleteTypeWitness{ .type_id = witness_type_id, .object_repr_type_inst_id = context.types().GetTypeInstId( context.types().GetObjectRepr(complete_type_id))}); } // If it's not a concrete constant, require it to be complete once it // becomes one. return ConstantEvalResult::NewSamePhase(inst); } auto EvalConstantInst(Context& context, SemIR::RequireSpecificDefinition inst) -> ConstantEvalResult { // This can return false, we just need to try it. ResolveSpecificDefinition(context, SemIR::LocId::None, inst.specific_id); return ConstantEvalResult::NewSamePhase(inst); } auto EvalConstantInst(Context& context, SemIR::SpecificConstant inst) -> ConstantEvalResult { // Pull the constant value out of the specific. return ConstantEvalResult::Existing(SemIR::GetConstantValueInSpecific( context.sem_ir(), inst.specific_id, inst.inst_id)); } auto EvalConstantInst(Context& context, SemIR::InstId inst_id, SemIR::SpecificImplFunction inst) -> ConstantEvalResult { auto callee_inst = context.insts().Get(inst.callee_id); // If the callee is not a function value, we're not ready to evaluate this // yet. Build a symbolic `SpecificImplFunction` constant. if (!callee_inst.Is()) { return ConstantEvalResult::NewSamePhase(inst); } auto callee_type_id = callee_inst.type_id(); auto callee_fn_type = context.types().TryGetAs(callee_type_id); if (!callee_fn_type) { return ConstantEvalResult::NewSamePhase(inst); } // If the callee function found in the impl witness is not generic, the result // is simply that function. // TODO: We could do this even before the callee is concrete. auto generic_id = context.functions().Get(callee_fn_type->function_id).generic_id; if (!generic_id.has_value()) { return ConstantEvalResult::Existing( context.constant_values().Get(inst.callee_id)); } // Find the arguments to use. auto enclosing_specific_id = callee_fn_type->specific_id; auto enclosing_args = context.inst_blocks().Get( context.specifics().GetArgsOrEmpty(enclosing_specific_id)); auto interface_fn_args = context.inst_blocks().Get( context.specifics().GetArgsOrEmpty(inst.specific_id)); // Form new specific for the generic callee function. The arguments for this // specific are the enclosing arguments of the callee followed by the // remaining arguments from the interface function. Impl checking has ensured // that these arguments can also be used for the function in the impl witness. auto num_params = context.inst_blocks() .Get(context.generics().Get(generic_id).bindings_id) .size(); llvm::SmallVector args; args.reserve(num_params); args.append(enclosing_args.begin(), enclosing_args.end()); int remaining_params = num_params - args.size(); CARBON_CHECK(static_cast(interface_fn_args.size()) >= remaining_params); args.append(interface_fn_args.end() - remaining_params, interface_fn_args.end()); auto specific_id = MakeSpecific(context, SemIR::LocId(inst_id), generic_id, args); context.definitions_required_by_use().push_back( {SemIR::LocId(inst_id), specific_id}); return ConstantEvalResult::NewSamePhase( SemIR::SpecificFunction{.type_id = inst.type_id, .callee_id = inst.callee_id, .specific_id = specific_id}); } auto EvalConstantInst(Context& context, SemIR::InstId inst_id, SemIR::SpecificFunction inst) -> ConstantEvalResult { auto callee_function = SemIR::GetCalleeAsFunction(context.sem_ir(), inst.callee_id); const auto& fn = context.functions().Get(callee_function.function_id); if (!callee_function.self_type_id.has_value() && fn.builtin_function_kind() != SemIR::BuiltinFunctionKind::NoOp && fn.virtual_modifier != SemIR::Function::VirtualModifier::Abstract) { // This is not an associated function. Those will be required to be defined // as part of checking that the impl is complete. context.definitions_required_by_use().push_back( {SemIR::LocId(inst_id), inst.specific_id}); } // Create new constant for a specific function. return ConstantEvalResult::NewSamePhase(inst); } auto EvalConstantInst(Context& context, SemIR::SpliceBlock inst) -> ConstantEvalResult { // SpliceBlock evaluates to the result value that is (typically) within the // block. This can be constant even if the block contains other non-constant // instructions. return ConstantEvalResult::Existing( context.constant_values().Get(inst.result_id)); } auto EvalConstantInst(Context& context, SemIR::SpliceInst inst) -> ConstantEvalResult { // The constant value of a SpliceInst is the constant value of the instruction // being spliced. Note that `inst.inst_id` is the instruction being spliced, // so we need to go through another round of obtaining the constant value in // addition to the one performed by the eval infrastructure. if (auto inst_value = context.insts().TryGetAs(inst.inst_id)) { return ConstantEvalResult::Existing( context.constant_values().Get(inst_value->inst_id)); } // TODO: Consider creating a new `ValueOfInst` instruction analogous to // `TypeOfInst` to defer determining the constant value until we know the // instruction. Alternatively, produce a symbolic `SpliceInst` constant. return ConstantEvalResult::NotConstant; } auto EvalConstantInst(Context& context, SemIR::StructAccess inst) -> ConstantEvalResult { return PerformAggregateAccess(context, inst); } auto EvalConstantInst(Context& /*context*/, SemIR::StructInit inst) -> ConstantEvalResult { return ConstantEvalResult::NewSamePhase(SemIR::StructValue{ .type_id = inst.type_id, .elements_id = inst.elements_id}); } auto EvalConstantInst(Context& /*context*/, SemIR::StructLiteral inst) -> ConstantEvalResult { return ConstantEvalResult::NewSamePhase(SemIR::StructValue{ .type_id = inst.type_id, .elements_id = inst.elements_id}); } auto EvalConstantInst(Context& /*context*/, SemIR::Temporary /*inst*/) -> ConstantEvalResult { // TODO: Handle this. Can we just return the value of `init_id`? return ConstantEvalResult::TODO; } auto EvalConstantInst(Context& context, SemIR::TupleAccess inst) -> ConstantEvalResult { return PerformAggregateAccess(context, inst); } auto EvalConstantInst(Context& /*context*/, SemIR::TupleInit inst) -> ConstantEvalResult { return ConstantEvalResult::NewSamePhase(SemIR::TupleValue{ .type_id = inst.type_id, .elements_id = inst.elements_id}); } auto EvalConstantInst(Context& /*context*/, SemIR::TupleLiteral inst) -> ConstantEvalResult { return ConstantEvalResult::NewSamePhase(SemIR::TupleValue{ .type_id = inst.type_id, .elements_id = inst.elements_id}); } auto EvalConstantInst(Context& context, SemIR::TypeComponentOf inst) -> ConstantEvalResult { auto form_constant_inst_id = context.constant_values().GetConstantInstId(inst.form_inst_id); if (auto primitive_form = context.insts().TryGetAs( form_constant_inst_id)) { return ConstantEvalResult::Existing( context.constant_values().Get(primitive_form->type_component_id)); } return ConstantEvalResult::NewSamePhase(inst); } auto EvalConstantInst(Context& context, SemIR::TypeLiteral inst) -> ConstantEvalResult { return ConstantEvalResult::Existing( context.constant_values().Get(inst.value_id)); } auto EvalConstantInst(Context& context, SemIR::TypeOfInst inst) -> ConstantEvalResult { // Grab the type from the instruction produced as our operand. if (auto inst_value = context.insts().TryGetAs(inst.inst_id)) { return ConstantEvalResult::Existing(context.types().GetConstantId( context.insts().Get(inst_value->inst_id).type_id())); } return ConstantEvalResult::NewSamePhase(inst); } auto EvalConstantInst(Context& context, SemIR::UnaryOperatorNot inst) -> ConstantEvalResult { // `not true` -> `false`, `not false` -> `true`. // All other uses of unary `not` are non-constant. auto const_id = context.constant_values().Get(inst.operand_id); if (const_id.is_concrete()) { auto value = context.insts().GetAs( context.constant_values().GetInstId(const_id)); value.value = SemIR::BoolValue::From(!value.value.ToBool()); return ConstantEvalResult::NewSamePhase(value); } return ConstantEvalResult::NotConstant; } auto EvalConstantInst(Context& /*context*/, SemIR::UpdateInit /*inst*/) -> ConstantEvalResult { // TODO: Support folding together a ClassInit with an update that sets the // vptr. return ConstantEvalResult::TODO; } auto EvalConstantInst(Context& context, SemIR::ValueOfInitializer inst) -> ConstantEvalResult { // Values of value expressions and initializing expressions are represented in // the same way during constant evaluation, so just return the value of the // operand. return ConstantEvalResult::Existing( context.constant_values().Get(inst.init_id)); } auto EvalConstantInst(Context& context, SemIR::InstId inst_id, SemIR::VarStorage inst) -> ConstantEvalResult { if (!inst.pattern_id.has_value()) { // This variable was not created from a `var` pattern, so isn't a global // variable. return ConstantEvalResult::NotConstant; } // A variable is constant if it's global. auto entity_name_id = SemIR::GetFirstBindingNameFromPatternId( context.sem_ir(), inst.pattern_id); if (!entity_name_id.has_value()) { // Variable doesn't introduce any bindings, so can only be referenced by its // own initializer. We treat such a reference as not being constant. return ConstantEvalResult::NotConstant; } auto scope_id = context.entity_names().Get(entity_name_id).parent_scope_id; if (!scope_id.has_value()) { return ConstantEvalResult::NotConstant; } auto scope_inst = context.insts().Get(context.name_scopes().Get(scope_id).inst_id()); if (!scope_inst.Is() && !scope_inst.Is()) { // Only namespace-scope and class-scope variables are reference constants. // Class-scope variables cannot currently be declared directly, but can // occur when static data members are imported from C++. return ConstantEvalResult::NotConstant; } // This is a constant reference expression denoting this global variable. return ConstantEvalResult::Existing( SemIR::ConstantId::ForConcreteConstant(inst_id)); } } // namespace Carbon::Check ================================================ FILE: toolchain/check/eval_inst.h ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #ifndef CARBON_TOOLCHAIN_CHECK_EVAL_INST_H_ #define CARBON_TOOLCHAIN_CHECK_EVAL_INST_H_ #include "toolchain/check/eval.h" #include "toolchain/sem_ir/inst_kind.h" namespace Carbon::Check { // The result of constant evaluation of an instruction. class ConstantEvalResult { public: // Produce a new constant as the result of an evaluation. The phase of the // produced constant must be the same as the greatest phase of the operands in // the evaluation. This will typically be the case if the evaluation uses all // of its operands. static auto NewSamePhase(SemIR::Inst inst) -> ConstantEvalResult { return ConstantEvalResult(inst, /*same_phase_as_inst=*/true); } // Produce a new constant as the result of an evaluation. The constant may // have any phase. Use `NewSamePhase` instead where possible, as it avoids a // phase recomputation. static auto NewAnyPhase(SemIR::Inst inst) -> ConstantEvalResult { return ConstantEvalResult(inst, /*same_phase_as_inst=*/false); } // Produce an existing constant as the result of an evaluation. static constexpr auto Existing(SemIR::ConstantId existing_id) -> ConstantEvalResult { CARBON_CHECK(existing_id.has_value()); return ConstantEvalResult(existing_id); } // Indicates that an error was produced by evaluation. static const ConstantEvalResult Error; // Indicates that we encountered an instruction whose evaluation is // non-constant despite having constant operands. This should be rare; // usually we want to produce an error in this case. static const ConstantEvalResult NotConstant; // Indicates that we encountered an instruction for which we've not // implemented constant evaluation yet. Instruction is treated as not // constant. static const ConstantEvalResult TODO; // Returns whether the result of evaluation is that we should produce a new // constant described by `new_inst()` rather than an existing `ConstantId` // described by `existing()`. auto is_new() const -> bool { return !result_id_.has_value(); } // Returns the existing constant that this the instruction evaluates to, or // `None` if this is evaluation produces a new constant. auto existing() const -> SemIR::ConstantId { return result_id_; } // Returns the new constant instruction that is the result of evaluation. auto new_inst() const -> SemIR::Inst { CARBON_CHECK(is_new()); return new_inst_; } // Whether the new constant instruction is known to have the same phase as the // evaluated instruction. Requires `is_new()`. auto same_phase_as_inst() const -> bool { CARBON_CHECK(is_new()); return same_phase_as_inst_; } private: constexpr explicit ConstantEvalResult(SemIR::ConstantId raw_id) : result_id_(raw_id), same_phase_as_inst_(false) {} explicit ConstantEvalResult(SemIR::Inst inst, bool same_phase_as_inst) : result_id_(SemIR::ConstantId::None), new_inst_(inst), same_phase_as_inst_(same_phase_as_inst) {} SemIR::ConstantId result_id_; union { SemIR::Inst new_inst_; }; bool same_phase_as_inst_; }; inline constexpr ConstantEvalResult ConstantEvalResult::Error = Existing(SemIR::ErrorInst::ConstantId); inline constexpr ConstantEvalResult ConstantEvalResult::NotConstant = ConstantEvalResult(SemIR::ConstantId::NotConstant); inline constexpr ConstantEvalResult ConstantEvalResult::TODO = NotConstant; // Implementation details to compute the type of the `EvalConstantInst` // functions. namespace Internal { // Returns whether an `EvalConstantInst` overload is expected to exist for this // instruction constant kind. constexpr auto ConstantKindHasEvalConstantInst(SemIR::InstConstantKind kind) -> bool { switch (kind) { case SemIR::InstConstantKind::Never: case SemIR::InstConstantKind::InstAction: case SemIR::InstConstantKind::WheneverPossible: case SemIR::InstConstantKind::Always: case SemIR::InstConstantKind::AlwaysUnique: return false; case SemIR::InstConstantKind::Indirect: case SemIR::InstConstantKind::SymbolicOnly: case SemIR::InstConstantKind::SymbolicOrReference: case SemIR::InstConstantKind::Conditional: case SemIR::InstConstantKind::ConditionalUnique: return true; } } // Given an instruction kind, determines the type that should be used to declare // `EvalConstantInst` for that instruction. template struct FunctionTypeForEvalConstantInstImpl { // By default, we want no `EvalConstantInst` function at all. But we can't // express that, so use the type `auto () -> void` as a placeholder. using Type = auto() -> void; }; template struct FunctionTypeForEvalConstantInstImpl { // Can be evaluated, evaluation doesn't need InstId. using Type = auto(Context& context, InstT inst) -> ConstantEvalResult; }; template struct FunctionTypeForEvalConstantInstImpl { // Can be evaluated, evaluation needs InstId. using Type = auto(Context& context, SemIR::InstId inst_id, InstT inst) -> ConstantEvalResult; }; template using FunctionTypeForEvalConstantInst = typename FunctionTypeForEvalConstantInstImpl< InstT, ConstantKindHasEvalConstantInst(InstT::Kind.constant_kind()), InstT::Kind.constant_needs_inst_id() != SemIR::InstConstantNeedsInstIdKind::No>::Type; } // namespace Internal // Explicitly delete the overload generated for non-evaluatable instructions. // These all produce the same signature, so we only need to delete it once. auto EvalConstantInst() -> void = delete; // `EvalConstantInst` evaluates an instruction whose operands are all constant, // in a context unrelated to the enclosing evaluation. The function is given the // instruction after its operands, including its type, are replaced by their // evaluated value, and returns a `ConstantEvalResult` describing the result of // evaluating the instruction. // // An overload is defined for each type whose constant kind is one of the // following: // // - InstConstantKind::Indirect // - InstConstantKind::SymbolicOnly // - InstConstantKind::SymbolicOrReference // - InstConstantKind::Conditional // - InstConstantKind::ConditionalUnique // // ... except for cases where the result of evaluation depends on the evaluation // context itself. Those cases are handled by explicit specialization of // `TryEvalTypedInst` in `eval.cpp` instead. // // The signature of an overload is // // auto EvalConstantInst(Context& context, SemIR::InstId inst_id, InstT inst) // -> ConstantEvalResult; // // if `InstT::Kind.constant_needs_inst_id()` is true, and // // auto EvalConstantInst(Context& context, InstT inst) -> ConstantEvalResult; // // otherwise. // // Overloads are *declared* for all types, because there isn't a good way to // declare only the overloads we want here without duplicating the list of // types. Missing overloads will be diagnosed when linking. Excess overloads // map to a deleted signature to prevent accidental calls. #define CARBON_SEM_IR_INST_KIND(Kind) \ Internal::FunctionTypeForEvalConstantInst EvalConstantInst; #include "toolchain/sem_ir/inst_kind.def" } // namespace Carbon::Check #endif // CARBON_TOOLCHAIN_CHECK_EVAL_INST_H_ ================================================ FILE: toolchain/check/facet_type.cpp ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #include "toolchain/check/facet_type.h" #include "toolchain/base/kind_switch.h" #include "toolchain/check/context.h" #include "toolchain/check/control_flow.h" #include "toolchain/check/generic.h" #include "toolchain/check/import_ref.h" #include "toolchain/check/inst.h" #include "toolchain/check/interface.h" #include "toolchain/check/subst.h" #include "toolchain/check/type.h" #include "toolchain/check/type_completion.h" #include "toolchain/sem_ir/generic.h" #include "toolchain/sem_ir/ids.h" #include "toolchain/sem_ir/typed_insts.h" namespace Carbon::Check { auto FacetTypeFromInterface(Context& context, SemIR::InterfaceId interface_id, SemIR::SpecificId specific_id) -> SemIR::FacetType { auto info = SemIR::FacetTypeInfo{}; info.extend_constraints.push_back({interface_id, specific_id}); info.Canonicalize(); SemIR::FacetTypeId facet_type_id = context.facet_types().Add(info); return {.type_id = SemIR::TypeType::TypeId, .facet_type_id = facet_type_id}; } auto FacetTypeFromNamedConstraint(Context& context, SemIR::NamedConstraintId named_constraint_id, SemIR::SpecificId specific_id) -> SemIR::FacetType { auto info = SemIR::FacetTypeInfo{}; info.extend_named_constraints.push_back({named_constraint_id, specific_id}); info.Canonicalize(); SemIR::FacetTypeId facet_type_id = context.facet_types().Add(info); return {.type_id = SemIR::TypeType::TypeId, .facet_type_id = facet_type_id}; } auto GetImplWitnessAccessWithoutSubstitution(Context& context, SemIR::InstId inst_id) -> SemIR::InstId { if (auto inst = context.insts().TryGetAs( inst_id)) { return inst->impl_witness_access_id; } return inst_id; } // A mapping of each associated constant (represented as `ImplWitnessAccess`) to // its value (represented as an `InstId`). Used to track rewrite constraints, // with the LHS mapping to the resolved value of the RHS. class AccessRewriteValues { public: enum State { NotRewritten, BeingRewritten, FullyRewritten, }; struct Value { State state; SemIR::InstId inst_id; }; auto InsertNotRewritten( Context& context, SemIR::KnownInstId access_id, SemIR::InstId inst_id) -> void { map_.Insert(context.constant_values().Get(access_id), {NotRewritten, inst_id}); } // Finds and returns a pointer into the cache for a given ImplWitnessAccess. // The pointer will be invalidated by mutating the cache. Returns `nullptr` // if `access` is not found. auto FindRef(Context& context, SemIR::KnownInstId access_id) -> Value* { auto result = map_.Lookup(context.constant_values().Get(access_id)); if (!result) { return nullptr; } return &result.value(); } auto SetBeingRewritten(Value& value) -> void { if (value.state == NotRewritten) { value.state = BeingRewritten; } } auto SetFullyRewritten(Context& context, Value& value, SemIR::InstId inst_id) -> void { if (value.state == FullyRewritten) { CARBON_CHECK(context.constant_values().Get(value.inst_id) == context.constant_values().Get(inst_id)); } value = {FullyRewritten, inst_id}; } private: // Try avoid heap allocations in the common case where there are a small // number of rewrite rules referring to each other by keeping up to 16 on // the stack. // // TODO: Revisit if 16 is an appropriate number when we can measure how deep // rewrite constraint chains go in practice. Map map_; }; // To be used for substituting into the RHS of a rewrite constraint. // // It will substitute any `ImplWitnessAccess` into `.Self` (a reference to an // associated constant) with the RHS of another rewrite constraint that writes // to the same associated constant. For example: // ``` // Z where .X = () and .Y = .X // ``` // Here the second `.X` is an `ImplWitnessAccess` which would be substituted by // finding the first rewrite constraint, where the LHS is for the same // associated constant and using its RHS. So the substitution would produce: // ``` // Z where .X = () and .Y = () // ``` // // This additionally diagnoses cycles when the `ImplWitnessAccess` is reading // from the same rewrite constraint, and is thus assigning to the associated // constant a value that refers to the same associated constant, such as with `Z // where .X = C(.X)`. In the event of a cycle, the `ImplWitnessAccess` is // replaced with `ErrorInst` so that further evaluation of the // `ImplWitnessAccess` will not loop infinitely. // // The `rewrite_values` given to the constructor must be set up initially with // each rewrite rule of an associated constant inserted with its unresolved // value via `InsertNotRewritten`. Then for each rewrite rule of an associated // constant, the LHS access should be set as being rewritten with its state // changed to `BeingRewritten` in order to detect cycles before performing // SubstInst. The result of SubstInst should be preserved afterward by changing // the state and value for the LHS to `FullyRewritten` and the subst output // instruction, respectively, to avoid duplicating work. class SubstImplWitnessAccessCallbacks : public SubstInstCallbacks { public: explicit SubstImplWitnessAccessCallbacks(Context* context, SemIR::LocId loc_id, AccessRewriteValues* rewrite_values) : SubstInstCallbacks(context), loc_id_(loc_id), rewrite_values_(rewrite_values) {} auto Subst(SemIR::InstId& rhs_inst_id) -> SubstResult override { auto rhs_access = context().insts().TryGetAsWithId(rhs_inst_id); if (!rhs_access) { // We only want to substitute ImplWitnessAccesses written directly on the // RHS of the rewrite constraint, not when they are nested inside facet // types that are part of the RHS, like `.X = C as (I where .Y = {})`. if (context().insts().Is(rhs_inst_id)) { return SubstResult::FullySubstituted; } if (context().constant_values().Get(rhs_inst_id).is_concrete()) { // There's no ImplWitnessAccess that we care about inside this // instruction. return SubstResult::FullySubstituted; } if (auto subst = context().insts().TryGetAs( rhs_inst_id)) { // The reference to an associated constant was eagerly replaced with the // value of an earlier rewrite constraint, but may need further // substitution if it contains an `ImplWitnessAccess`. rhs_inst_id = subst->value_id; substs_in_progress_.push_back(rhs_inst_id); return SubstResult::SubstAgain; } // SubstOperands will result in a Rebuild or ReuseUnchanged callback, so // push the non-ImplWitnessAccess to get proper bracketing, allowing us // to pop it in the paired callback. substs_in_progress_.push_back(rhs_inst_id); return SubstResult::SubstOperands; } // If the access is going through a nested `ImplWitnessAccess`, that // access needs to be resolved to a facet value first. If it can't be // resolved then the outer one can not be either. if (auto lookup = context().insts().TryGetAs( rhs_access->inst.witness_id)) { if (context().insts().Is( lookup->query_self_inst_id)) { substs_in_progress_.push_back(rhs_inst_id); return SubstResult::SubstOperandsAndRetry; } } auto* rewrite_value = rewrite_values_->FindRef(context(), rhs_access->inst_id); if (!rewrite_value) { // The RHS refers to an associated constant for which there is no rewrite // rule. return SubstResult::FullySubstituted; } // Diagnose a cycle if the RHS refers to something that depends on the value // of the RHS. if (rewrite_value->state == AccessRewriteValues::BeingRewritten) { CARBON_DIAGNOSTIC(FacetTypeConstraintCycle, Error, "found cycle in facet type constraint for {0}", InstIdAsConstant); // TODO: It would be nice to note the places where the values are // assigned but rewrite constraint instructions are from canonical // constant values, and have no locations. We'd need to store a location // along with them in the rewrite constraints, and track propagation of // locations here, which may imply heap allocations. context().emitter().Emit(loc_id_, FacetTypeConstraintCycle, rhs_inst_id); rhs_inst_id = SemIR::ErrorInst::InstId; return SubstResult::FullySubstituted; } else if (rewrite_value->state == AccessRewriteValues::FullyRewritten) { rhs_inst_id = rewrite_value->inst_id; return SubstResult::FullySubstituted; } // We have a non-rewritten RHS. We need to recurse on rewriting it. Reuse // the previous lookup by mutating it in place. rewrite_values_->SetBeingRewritten(*rewrite_value); // The ImplWitnessAccess was replaced with some other instruction, which may // contain or be another ImplWitnessAccess. Keep track of the associated // constant we are now computing the value of. substs_in_progress_.push_back(rhs_inst_id); rhs_inst_id = rewrite_value->inst_id; return SubstResult::SubstAgain; } auto Rebuild(SemIR::InstId /*orig_inst_id*/, SemIR::Inst new_inst) -> SemIR::InstId override { auto inst_id = RebuildNewInst(loc_id_, new_inst); auto subst_inst_id = substs_in_progress_.pop_back_val(); if (auto access = context().insts().TryGetAsWithId( subst_inst_id)) { if (auto* rewrite_value = rewrite_values_->FindRef(context(), access->inst_id)) { rewrite_values_->SetFullyRewritten(context(), *rewrite_value, inst_id); } } return inst_id; } auto ReuseUnchanged(SemIR::InstId orig_inst_id) -> SemIR::InstId override { auto subst_inst_id = substs_in_progress_.pop_back_val(); if (auto access = context().insts().TryGetAsWithId( subst_inst_id)) { if (auto* rewrite_value = rewrite_values_->FindRef(context(), access->inst_id)) { rewrite_values_->SetFullyRewritten(context(), *rewrite_value, orig_inst_id); } } return orig_inst_id; } private: struct SubstInProgress { // The associated constant whose value is being determined, represented as // an ImplWitnessAccess. Or another instruction that we are recursing // through. SemIR::InstId inst_id; }; // The location of the rewrite constraints as a whole. SemIR::LocId loc_id_; // Tracks the resolved value of each rewrite constraint, keyed by the // `ImplWitnessAccess` of the associated constant on the LHS of the // constraint. The value of each associated constant may be changed during // substitution, replaced with a fully resolved value for the RHS. This allows // us to cache work; when a value for an associated constant is found once it // can be reused cheaply, avoiding exponential runtime when rewrite rules // refer to each other in ways that create exponential references. AccessRewriteValues* rewrite_values_; // A stack of instructions being replaced in Subst(). When it's an associated // constant, then it represents the constant value is being determined, // represented as an ImplWitnessAccess. // // Avoid heap allocations in common cases, if there are chains of instructions // in associated constants with a depth at most 16. llvm::SmallVector substs_in_progress_; }; auto ResolveFacetTypeRewriteConstraints( Context& context, SemIR::LocId loc_id, llvm::SmallVector& rewrites) -> bool { if (rewrites.empty()) { return true; } AccessRewriteValues rewrite_values; for (auto& constraint : rewrites) { auto lhs_access = context.insts().TryGetAsWithId( GetImplWitnessAccessWithoutSubstitution(context, constraint.lhs_id)); if (!lhs_access) { continue; } rewrite_values.InsertNotRewritten(context, lhs_access->inst_id, constraint.rhs_id); } for (auto& constraint : rewrites) { auto lhs_access = context.insts().TryGetAsWithId( GetImplWitnessAccessWithoutSubstitution(context, constraint.lhs_id)); if (!lhs_access) { continue; } auto* lhs_rewrite_value = rewrite_values.FindRef(context, lhs_access->inst_id); // Every LHS was added with InsertNotRewritten above. CARBON_CHECK(lhs_rewrite_value); rewrite_values.SetBeingRewritten(*lhs_rewrite_value); auto replace_witness_callbacks = SubstImplWitnessAccessCallbacks(&context, loc_id, &rewrite_values); auto rhs_subst_inst_id = SubstInst(context, constraint.rhs_id, replace_witness_callbacks); if (rhs_subst_inst_id == SemIR::ErrorInst::InstId) { return false; } if (lhs_rewrite_value->state == AccessRewriteValues::FullyRewritten) { auto rhs_existing_const_id = context.constant_values().Get(lhs_rewrite_value->inst_id); auto rhs_subst_const_id = context.constant_values().Get(rhs_subst_inst_id); if (rhs_subst_const_id != rhs_existing_const_id) { if (rhs_existing_const_id != SemIR::ErrorInst::ConstantId) { CARBON_DIAGNOSTIC(AssociatedConstantWithDifferentValues, Error, "associated constant {0} given two different " "values {1} and {2}", InstIdAsConstant, InstIdAsConstant, InstIdAsConstant); // Use inst id ordering as a simple proxy for source ordering, to // try to name the values in the same order they appear in the facet // type. auto source_order1 = lhs_rewrite_value->inst_id.index < rhs_subst_inst_id.index ? lhs_rewrite_value->inst_id : rhs_subst_inst_id; auto source_order2 = lhs_rewrite_value->inst_id.index >= rhs_subst_inst_id.index ? lhs_rewrite_value->inst_id : rhs_subst_inst_id; // TODO: It would be nice to note the places where the values are // assigned but rewrite constraint instructions are from canonical // constant values, and have no locations. We'd need to store a // location along with them in the rewrite constraints. context.emitter().Emit(loc_id, AssociatedConstantWithDifferentValues, GetImplWitnessAccessWithoutSubstitution( context, constraint.lhs_id), source_order1, source_order2); } return false; } } rewrite_values.SetFullyRewritten(context, *lhs_rewrite_value, rhs_subst_inst_id); } // Rebuild the `rewrites` vector with resolved values for the RHS. Drop any // duplicate rewrites in the `rewrites` vector by walking through the // `rewrite_values` map and dropping the computed RHS value for each LHS the // first time we see it, and erasing the constraint from the vector if we see // the same LHS again. size_t keep_size = rewrites.size(); for (size_t i = 0; i < keep_size;) { auto& constraint = rewrites[i]; auto lhs_access = context.insts().TryGetAsWithId( GetImplWitnessAccessWithoutSubstitution(context, constraint.lhs_id)); if (!lhs_access) { ++i; continue; } auto& rewrite_value = *rewrite_values.FindRef(context, lhs_access->inst_id); auto rhs_id = std::exchange(rewrite_value.inst_id, SemIR::InstId::None); if (rhs_id == SemIR::InstId::None) { std::swap(rewrites[i], rewrites[keep_size - 1]); --keep_size; } else { rewrites[i].rhs_id = rhs_id; ++i; } } rewrites.erase(rewrites.begin() + keep_size, rewrites.end()); return true; } auto MakePeriodSelfFacetValue(Context& context, SemIR::TypeId self_type_id) -> SemIR::InstId { auto entity_name_id = context.entity_names().AddCanonical({ .name_id = SemIR::NameId::PeriodSelf, .parent_scope_id = context.scope_stack().PeekNameScopeId(), }); auto inst_id = AddInst( context, SemIR::LocIdAndInst::NoLoc({ .type_id = self_type_id, .entity_name_id = entity_name_id, // `None` because there is no equivalent non-symbolic value. .value_id = SemIR::InstId::None, })); auto existing = context.scope_stack().LookupOrAddName( SemIR::NameId::PeriodSelf, inst_id, ScopeIndex::None, IsCurrentPositionReachable(context)); // Shouldn't have any names in newly created scope. CARBON_CHECK(!existing.has_value()); return inst_id; } auto GetEmptyFacetType(Context& context) -> SemIR::TypeId { SemIR::FacetTypeId facet_type_id = context.facet_types().Add(SemIR::FacetTypeInfo{}); auto const_id = EvalOrAddInst( context, SemIR::LocId::None, {.type_id = SemIR::TypeType::TypeId, .facet_type_id = facet_type_id}); return context.types().GetTypeIdForTypeConstantId(const_id); } auto GetConstantFacetValueForType(Context& context, SemIR::TypeInstId type_inst_id) -> SemIR::ConstantId { // We use an empty facet type because values of type `type` do not provide any // witnesses of their own. auto type_facet_type = GetEmptyFacetType(context); return EvalOrAddInst( context, SemIR::LocId::None, {.type_id = type_facet_type, .type_inst_id = type_inst_id, .witnesses_block_id = SemIR::InstBlockId::Empty}); } auto GetConstantFacetValueForTypeAndInterface( Context& context, SemIR::TypeInstId type_inst_id, SemIR::SpecificInterface specific_interface, SemIR::InstId witness_id) -> SemIR::ConstantId { // Get the type of the inner `Self`, which is the facet type of the interface. auto interface_facet_type = EvalOrAddInst( context, SemIR::LocId::None, FacetTypeFromInterface(context, specific_interface.interface_id, specific_interface.specific_id)); auto self_facet_type_in_generic_without_self = context.types().GetTypeIdForTypeConstantId(interface_facet_type); auto witnesses_block_id = context.inst_blocks().AddCanonical({witness_id}); auto self_value_const_id = EvalOrAddInst( context, SemIR::LocId::None, {.type_id = self_facet_type_in_generic_without_self, .type_inst_id = type_inst_id, .witnesses_block_id = witnesses_block_id}); return self_value_const_id; } } // namespace Carbon::Check ================================================ FILE: toolchain/check/facet_type.h ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #ifndef CARBON_TOOLCHAIN_CHECK_FACET_TYPE_H_ #define CARBON_TOOLCHAIN_CHECK_FACET_TYPE_H_ #include #include "toolchain/check/context.h" #include "toolchain/sem_ir/entity_with_params_base.h" #include "toolchain/sem_ir/ids.h" namespace Carbon::Check { // Create a FacetType typed instruction object consisting of a interface. The // `specific_id` specifies arguments in the case the interface is generic. auto FacetTypeFromInterface(Context& context, SemIR::InterfaceId interface_id, SemIR::SpecificId specific_id) -> SemIR::FacetType; // Create a FacetType typed instruction object consisting of a named constraint. // The `specific_id` specifies arguments in the case the named constraint is // generic. auto FacetTypeFromNamedConstraint(Context& context, SemIR::NamedConstraintId named_constraint_id, SemIR::SpecificId specific_id) -> SemIR::FacetType; // Given an ImplWitnessAccessSubstituted, returns the InstId of the // ImplWitnessAccess. Otherwise, returns the input `inst_id` unchanged. // // This must be used when accessing the LHS of a rewrite constraint which has // not yet been resolved in order to preserve which associated constant is being // rewritten. auto GetImplWitnessAccessWithoutSubstitution(Context& context, SemIR::InstId inst_id) -> SemIR::InstId; // Perform rewrite constraint resolution for a facet type. The rewrite // constraints resolution is described here: // https://docs.carbon-lang.dev/docs/design/generics/appendix-rewrite-constraints.html#rewrite-constraint-resolution // // This function: // * Replaces the RHS of rewrite rules referring to `.Self` with the value // coming from other rewrite rules. For example in `.X = () and .Y = .X` the // result is `.X = () and .Y = ()`. // * Discards duplicate assignments to the same associated constant, such as in // `.X = () and .X = ()` which becomes just `.X = ()`. // * Diagnoses multiple assignments of different values to the same associated // constant such as `.X = () and .X = .Y`. // * Diagnoses cycles between rewrite rules such as `.X = .Y and .Y = .X` or // even `.X = .X`. // // The rewrite constraints in `rewrites` are modified in place and may be // reordered, with `ErrorInst` inserted when diagnosing errors. // // Returns false if resolve failed due to diagnosing an error. The resulting // value of the facet type should be an error constant. auto ResolveFacetTypeRewriteConstraints( Context& context, SemIR::LocId loc_id, llvm::SmallVector& rewrites) -> bool; // Introduce `.Self` as a symbolic binding into the current scope, and return // the `SymbolicBinding` instruction. // // The `self_type_id` is either a facet type (as `FacetType`) or `type` (as // `TypeType`). auto MakePeriodSelfFacetValue(Context& context, SemIR::TypeId self_type_id) -> SemIR::InstId; // Get a FacetType instruction for an empty FacetType. This is the facet // equivalent to TypeType. // // TODO: We vaguely plan to replace TypeType with this FacetType in the future, // though that's a big change. auto GetEmptyFacetType(Context& context) -> SemIR::TypeId; // Make a facet value for a type value, which has an empty FacetType as its // type. Returns a constant value, whose instruction payload is a FacetValue. auto GetConstantFacetValueForType(Context& context, SemIR::TypeInstId type_inst_id) -> SemIR::ConstantId; auto GetConstantFacetValueForTypeAndInterface( Context& context, SemIR::TypeInstId type_inst_id, SemIR::SpecificInterface specific_interface, SemIR::InstId witness_id) -> SemIR::ConstantId; } // namespace Carbon::Check #endif // CARBON_TOOLCHAIN_CHECK_FACET_TYPE_H_ ================================================ FILE: toolchain/check/full_pattern_stack.h ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #ifndef CARBON_TOOLCHAIN_CHECK_FULL_PATTERN_STACK_H_ #define CARBON_TOOLCHAIN_CHECK_FULL_PATTERN_STACK_H_ #include "common/array_stack.h" #include "common/check.h" #include "toolchain/check/lexical_lookup.h" #include "toolchain/sem_ir/ids.h" namespace Carbon::Check { // Stack of full-patterns currently being checked (a full-pattern is a pattern // that is not part of an enclosing pattern). It is structured as a stack to // handle situations like a pattern that contains an initializer, or a pattern // in a lambda in an expression pattern. // // When a pattern is followed by an explicit initializer, name bindings should // not be used within that initializer, although they are usable before it // (within the pattern) and after it. This class keeps track of those state // transitions, as well as the kind of full-pattern (e.g. parameter list or name // binding pattern). // // TODO: Unify this with Context::pattern_block_stack, or differentiate them // more clearly (and consider unifying this with ScopeStack instead). class FullPatternStack { public: explicit FullPatternStack(LexicalLookup* lookup) : lookup_(lookup) {} // The kind of a full-pattern. There are two primary kinds: name binding // declarations and parameterized entity declarations. However, for efficiency // we also use this enum to track state transitions within a parameterized // entity declaration. A parameterized entity declaration always starts and // finishes in the `NotInEitherParamList` state, and can transition to either // the `ImplicitParamList` or `ExplicitParamList` state, and then back to the // `NotInEitherParamList` state. enum class Kind { // A name-binding declaration, such as a `let` or `var` statement. NameBindingDecl, // The implicit parameter list of a function or impl declaration. ImplicitParamList, // The explicit parameter list of a function declaration. ExplicitParamList, // This kind indicates that we're within the declaration of a parameterized // entity (such as a function or impl), but not within an explicit or // implicit parameter list. This is primarily useful for the return part of // a function declaration, which doesn't contain pattern syntax, but can // implicitly introduce output parameter patterns. However, the parse tree // doesn't let us reliably distinguish the return part from the part before // the parameter lists (or between them), particularly in the case where // there is no explicit parameter list. NotInEitherParamList }; auto empty() const -> bool { return kind_stack_.empty(); } // The kind of the current full-pattern. auto CurrentKind() const -> Kind { return kind_stack_.back(); } // Marks the start of a new full-pattern for a parameterized entity // declaration, such as a function or impl. The kind is initially // NotInEitherParamList. auto PushParameterizedDecl() -> void { kind_stack_.push_back(Kind::NotInEitherParamList); bind_name_stack_.PushArray(); } // Marks the start of a new full-pattern for a name binding declaration. auto PushNameBindingDecl() -> void { kind_stack_.push_back(Kind::NameBindingDecl); bind_name_stack_.PushArray(); } // Marks the start of the current parameterized entity's implicit parameter // list. auto StartImplicitParamList() -> void { CARBON_CHECK(kind_stack_.back() == Kind::NotInEitherParamList, "{0}", kind_stack_.back()); kind_stack_.back() = Kind::ImplicitParamList; } // Marks the end of the current parameterized entity's implicit parameter // list. auto EndImplicitParamList() -> void { CARBON_CHECK(kind_stack_.back() == Kind::ImplicitParamList, "{0}", kind_stack_.back()); kind_stack_.back() = Kind::NotInEitherParamList; } // Marks the start of the current parameterized entity's explicit parameter // list. auto StartExplicitParamList() -> void { CARBON_CHECK(kind_stack_.back() == Kind::NotInEitherParamList, "{0}", kind_stack_.back()); kind_stack_.back() = Kind::ExplicitParamList; } // Marks the end of the current parameterized entity's explicit parameter // list. auto EndExplicitParamList() -> void { CARBON_CHECK(kind_stack_.back() == Kind::ExplicitParamList, "{0}", kind_stack_.back()); kind_stack_.back() = Kind::NotInEitherParamList; } // Marks the start of the initializer for the current name binding decl. auto StartPatternInitializer() -> void { CARBON_CHECK(kind_stack_.back() == Kind::NameBindingDecl); for (auto& [name_id, inst_id] : bind_name_stack_.PeekArray()) { CARBON_CHECK(inst_id == SemIR::InstId::InitTombstone); auto& lookup_result = lookup_->Get(name_id); if (!lookup_result.empty()) { // TODO: find a way to preserve location information, so that we can // provide good diagnostics for a redeclaration of `name_id` in // the initializer, if that becomes possible. std::swap(lookup_result.back().inst_id, inst_id); } } } // Marks the end of the initializer for the current name-binding decl. auto EndPatternInitializer() -> void { for (auto& [name_id, inst_id] : bind_name_stack_.PeekArray()) { auto& lookup_result = lookup_->Get(name_id); if (!lookup_result.empty()) { std::swap(lookup_result.back().inst_id, inst_id); } CARBON_CHECK(inst_id == SemIR::InstId::InitTombstone); } } // Marks the end of checking for the current full-pattern. This cannot be // called while processing an initializer for the top pattern. auto PopFullPattern() -> void { kind_stack_.pop_back(); bind_name_stack_.PopArray(); } // Records that `name_id` was introduced by the current full-pattern. auto AddBindName(SemIR::NameId name_id) -> void { bind_name_stack_.AppendToTop( {.name_id = name_id, .inst_id = SemIR::InstId::InitTombstone}); } // Runs verification that the processing cleanly finished. auto VerifyOnFinish() const -> void { CARBON_CHECK(kind_stack_.empty(), "full_pattern_stack still has {0} entries", kind_stack_.size()); } private: LexicalLookup* lookup_; llvm::SmallVector kind_stack_; struct LookupEntry { SemIR::NameId name_id; SemIR::InstId inst_id; }; ArrayStack bind_name_stack_; }; } // namespace Carbon::Check #endif // CARBON_TOOLCHAIN_CHECK_FULL_PATTERN_STACK_H_ ================================================ FILE: toolchain/check/function.cpp ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #include "toolchain/check/function.h" #include "common/find.h" #include "toolchain/base/kind_switch.h" #include "toolchain/check/convert.h" #include "toolchain/check/generic.h" #include "toolchain/check/inst.h" #include "toolchain/check/merge.h" #include "toolchain/check/pattern.h" #include "toolchain/check/pattern_match.h" #include "toolchain/check/scope_stack.h" #include "toolchain/check/type.h" #include "toolchain/check/type_completion.h" #include "toolchain/diagnostics/format_providers.h" #include "toolchain/sem_ir/builtin_function_kind.h" #include "toolchain/sem_ir/ids.h" #include "toolchain/sem_ir/pattern.h" namespace Carbon::Check { auto FindSelfPattern(Context& context, SemIR::InstBlockId implicit_param_patterns_id) -> SemIR::InstId { auto implicit_param_patterns = context.inst_blocks().GetOrEmpty(implicit_param_patterns_id); return FindIfOrNone(implicit_param_patterns, [&](auto implicit_param_id) { return SemIR::IsSelfPattern(context.sem_ir(), implicit_param_id); }); } auto AddReturnPatterns(Context& context, SemIR::LocId loc_id, Context::FormExpr form_expr) -> SemIR::InstBlockId { llvm::SmallVector return_patterns; auto form_inst = context.insts().Get( context.constant_values().GetConstantInstId(form_expr.form_inst_id)); CARBON_KIND_SWITCH(form_inst) { case SemIR::RefForm::Kind: case SemIR::ValueForm::Kind: { break; } case CARBON_KIND(SemIR::InitForm _): { auto pattern_type_id = GetPatternType(context, form_expr.type_component_id); auto return_slot_pattern_id = AddPatternInst( context, loc_id, {.type_id = pattern_type_id, .type_inst_id = form_expr.type_component_inst_id}); return_patterns.push_back(AddPatternInst( context, SemIR::LocId(form_expr.form_inst_id), {.type_id = pattern_type_id, .subpattern_id = return_slot_pattern_id})); break; } case SemIR::ErrorInst::Kind: { break; } case SemIR::SymbolicBinding::Kind: CARBON_CHECK( context.constant_values().Get(form_expr.form_inst_id).is_symbolic()); context.TODO(loc_id, "Support symbolic return forms"); break; default: CARBON_FATAL("unexpected inst kind: {0}", form_inst); } return context.inst_blocks().AddCanonical(return_patterns); } auto IsValidBuiltinDeclaration(Context& context, const SemIR::Function& function, SemIR::BuiltinFunctionKind builtin_kind) -> bool { if (!function.call_params_id.has_value()) { // For now, we have no builtins that support positional parameters. return false; } // Find the list of call parameters other than the implicit return slots. auto call_params = context.inst_blocks() .Get(function.call_params_id) .take_front(function.call_param_ranges.explicit_end().index); // Get the return type. This is `()` if none was specified. auto return_type_id = function.GetDeclaredReturnType(context.sem_ir()); if (!return_type_id.has_value()) { return_type_id = GetTupleType(context, {}); } return builtin_kind.IsValidType(context.sem_ir(), call_params, return_type_id); } namespace { // Function signature fields for `MakeFunctionSignature`. struct FunctionSignatureInsts { SemIR::InstBlockId decl_block_id = SemIR::InstBlockId::None; SemIR::InstBlockId pattern_block_id = SemIR::InstBlockId::None; SemIR::InstBlockId implicit_param_patterns_id = SemIR::InstBlockId::None; SemIR::InstBlockId param_patterns_id = SemIR::InstBlockId::None; SemIR::InstBlockId call_param_patterns_id = SemIR::InstBlockId::None; SemIR::InstBlockId call_params_id = SemIR::InstBlockId::None; SemIR::Function::CallParamIndexRanges call_param_ranges = SemIR::Function::CallParamIndexRanges::Empty; SemIR::TypeInstId return_type_inst_id = SemIR::TypeInstId::None; SemIR::InstId return_form_inst_id = SemIR::InstId::None; SemIR::InstBlockId return_patterns_id = SemIR::InstBlockId::None; SemIR::InstId self_param_id = SemIR::InstId::None; }; } // namespace // Handles construction of the signature's parameter and return types. static auto MakeFunctionSignature(Context& context, SemIR::LocId loc_id, const FunctionDeclArgs& args) -> FunctionSignatureInsts { FunctionSignatureInsts insts; StartFunctionSignature(context); // Build and add a `[ref self: Self]` parameter if needed. if (args.self_type_id.has_value()) { context.full_pattern_stack().StartImplicitParamList(); BeginSubpattern(context); auto self_type_region_id = EndSubpatternAsExpr( context, context.types().GetTypeInstId(args.self_type_id)); insts.self_param_id = AddParamPattern( context, loc_id, SemIR::NameId::SelfValue, self_type_region_id, args.self_type_id, args.self_is_ref); insts.implicit_param_patterns_id = context.inst_blocks().Add({insts.self_param_id}); context.full_pattern_stack().EndImplicitParamList(); } // Build and add any explicit parameters. We always use value parameters for // now. context.full_pattern_stack().StartExplicitParamList(); if (args.param_type_ids.empty()) { insts.param_patterns_id = SemIR::InstBlockId::Empty; } else { context.inst_block_stack().Push(); for (auto param_type_id : args.param_type_ids) { BeginSubpattern(context); auto param_type_region_id = EndSubpatternAsExpr( context, context.types().GetTypeInstId(param_type_id)); context.inst_block_stack().AddInstId(AddParamPattern( context, loc_id, SemIR::NameId::Underscore, param_type_region_id, param_type_id, /*is_ref=*/false)); } insts.param_patterns_id = context.inst_block_stack().Pop(); } context.full_pattern_stack().EndExplicitParamList(); // Build and add the return type. We always use an initializing form for now. if (args.return_type_id.has_value()) { auto return_form = ReturnExprAsForm( context, loc_id, context.types().GetTypeInstId(args.return_type_id)); insts.return_type_inst_id = return_form.type_component_inst_id; insts.return_form_inst_id = return_form.form_inst_id; insts.return_patterns_id = AddReturnPatterns(context, loc_id, return_form); } auto match_results = CalleePatternMatch(context, insts.implicit_param_patterns_id, insts.param_patterns_id, insts.return_patterns_id); insts.call_param_patterns_id = match_results.call_param_patterns_id; insts.call_params_id = match_results.call_params_id; insts.call_param_ranges = match_results.param_ranges; auto [pattern_block_id, decl_block_id] = FinishFunctionSignature(context, /*check_unused=*/false); insts.pattern_block_id = pattern_block_id; insts.decl_block_id = decl_block_id; return insts; } auto MakeGeneratedFunctionDecl(Context& context, SemIR::LocId loc_id, const FunctionDeclArgs& args) -> std::pair { auto insts = MakeFunctionSignature(context, loc_id, args); // Add the function declaration. auto [decl_id, function_id] = MakeFunctionDecl( context, loc_id, insts.decl_block_id, /*build_generic=*/false, /*is_definition=*/true, SemIR::Function{ { .name_id = args.name_id, .parent_scope_id = args.parent_scope_id, .generic_id = SemIR::GenericId::None, .first_param_node_id = Parse::NodeId::None, .last_param_node_id = Parse::NodeId::None, .pattern_block_id = insts.pattern_block_id, .implicit_param_patterns_id = insts.implicit_param_patterns_id, .param_patterns_id = insts.param_patterns_id, .is_extern = false, .extern_library_id = SemIR::LibraryNameId::None, .non_owning_decl_id = SemIR::InstId::None, // Set by `MakeFunctionDecl`. .first_owning_decl_id = SemIR::InstId::None, }, { .call_param_patterns_id = insts.call_param_patterns_id, .call_params_id = insts.call_params_id, .call_param_ranges = insts.call_param_ranges, .return_type_inst_id = insts.return_type_inst_id, .return_form_inst_id = insts.return_form_inst_id, .return_patterns_id = insts.return_patterns_id, .self_param_id = insts.self_param_id, }}); context.generated().push_back(decl_id); return {decl_id, function_id}; } auto CheckFunctionReturnTypeMatches(Context& context, const SemIR::Function& new_function, const SemIR::Function& prev_function, SemIR::SpecificId prev_specific_id, bool diagnose) -> bool { // TODO: Pass a specific ID for `prev_function` instead of substitutions and // use it here. auto new_return_type_id = new_function.GetDeclaredReturnType(context.sem_ir()); auto prev_return_type_id = prev_function.GetDeclaredReturnType(context.sem_ir(), prev_specific_id); if (new_return_type_id == SemIR::ErrorInst::TypeId || prev_return_type_id == SemIR::ErrorInst::TypeId) { return false; } if (!context.types().AreEqualAcrossDeclarations(new_return_type_id, prev_return_type_id)) { if (!diagnose) { return false; } CARBON_DIAGNOSTIC( FunctionRedeclReturnTypeDiffers, Error, "function redeclaration differs because return type is {0}", SemIR::TypeId); CARBON_DIAGNOSTIC( FunctionRedeclReturnTypeDiffersNoReturn, Error, "function redeclaration differs because no return type is provided"); auto diag = new_return_type_id.has_value() ? context.emitter().Build(new_function.latest_decl_id(), FunctionRedeclReturnTypeDiffers, new_return_type_id) : context.emitter().Build(new_function.latest_decl_id(), FunctionRedeclReturnTypeDiffersNoReturn); if (prev_return_type_id.has_value()) { CARBON_DIAGNOSTIC(FunctionRedeclReturnTypePrevious, Note, "previously declared with return type {0}", SemIR::TypeId); diag.Note(prev_function.latest_decl_id(), FunctionRedeclReturnTypePrevious, prev_return_type_id); } else { CARBON_DIAGNOSTIC(FunctionRedeclReturnTypePreviousNoReturn, Note, "previously declared with no return type"); diag.Note(prev_function.latest_decl_id(), FunctionRedeclReturnTypePreviousNoReturn); } diag.Emit(); return false; } return true; } // Checks that a function declaration's evaluation mode matches the previous // declaration's evaluation mode. Returns `false` and optionally produces a // diagnostic on mismatch. static auto CheckFunctionEvaluationModeMatches( Context& context, const SemIR::Function& new_function, const SemIR::Function& prev_function, bool diagnose) -> bool { if (prev_function.evaluation_mode == new_function.evaluation_mode) { return true; } if (!diagnose) { return false; } auto eval_mode_index = [](SemIR::Function::EvaluationMode mode) { switch (mode) { case SemIR::Function::EvaluationMode::None: return 0; case SemIR::Function::EvaluationMode::Eval: return 1; case SemIR::Function::EvaluationMode::MustEval: return 2; } }; auto prev_eval_mode_index = eval_mode_index(prev_function.evaluation_mode); auto new_eval_mode_index = eval_mode_index(new_function.evaluation_mode); CARBON_DIAGNOSTIC( FunctionRedeclEvaluationModeDiffers, Error, "function redeclaration differs because new function is " "{0:=-1:not `eval`|=-2:not `musteval`|=1:`eval`|=2:`musteval`}", Diagnostics::IntAsSelect); CARBON_DIAGNOSTIC(FunctionRedeclEvaluationModePrevious, Note, "previously {0:<0:not |:}declared as " "{0:=-1:`eval`|=-2:`musteval`|=1:`eval`|=2:`musteval`}", Diagnostics::IntAsSelect); context.emitter() .Build(new_function.latest_decl_id(), FunctionRedeclEvaluationModeDiffers, new_eval_mode_index ? new_eval_mode_index : -prev_eval_mode_index) .Note(prev_function.latest_decl_id(), FunctionRedeclEvaluationModePrevious, prev_eval_mode_index ? prev_eval_mode_index : -new_eval_mode_index) .Emit(); return false; } auto CheckFunctionTypeMatches(Context& context, const SemIR::Function& new_function, const SemIR::Function& prev_function, SemIR::SpecificId prev_specific_id, bool check_syntax, bool check_self, bool diagnose) -> bool { if (!CheckRedeclParamsMatch(context, DeclParams(new_function), DeclParams(prev_function), prev_specific_id, diagnose, check_syntax, check_self)) { return false; } if (!CheckFunctionReturnTypeMatches(context, new_function, prev_function, prev_specific_id, diagnose)) { return false; } if (!CheckFunctionEvaluationModeMatches(context, new_function, prev_function, diagnose)) { return false; } return true; } auto CheckFunctionReturnPatternType(Context& context, SemIR::LocId loc_id, SemIR::InstId return_pattern_id, SemIR::SpecificId specific_id) -> SemIR::TypeId { auto arg_type_id = SemIR::ExtractScrutineeType( context.sem_ir(), SemIR::GetTypeOfInstInSpecific( context.sem_ir(), specific_id, return_pattern_id)); auto init_repr = SemIR::InitRepr::ForType(context.sem_ir(), arg_type_id); if (!init_repr.is_valid()) { // TODO: Consider suppressing the diagnostics if we've already diagnosed a // definition or call to this function. if (!RequireConcreteType( context, arg_type_id, SemIR::LocId(return_pattern_id), [&](auto& builder) { CARBON_DIAGNOSTIC(IncompleteTypeInFunctionReturnType, Context, "function returns incomplete type {0}", SemIR::TypeId); builder.Context(loc_id, IncompleteTypeInFunctionReturnType, arg_type_id); }, [&](auto& builder) { CARBON_DIAGNOSTIC(AbstractTypeInFunctionReturnType, Context, "function returns abstract type {0}", SemIR::TypeId); builder.Context(loc_id, AbstractTypeInFunctionReturnType, arg_type_id); })) { return SemIR::ErrorInst::TypeId; } } return arg_type_id; } auto CheckFunctionDefinitionSignature(Context& context, SemIR::FunctionId function_id) -> void { auto& function = context.functions().Get(function_id); auto params_to_complete = context.inst_blocks().GetOrEmpty(function.call_params_id); // The return parameter will be diagnosed after and differently from other // parameters. auto return_call_param = SemIR::InstId::None; if (!params_to_complete.empty() && function.return_patterns_id.has_value()) { return_call_param = params_to_complete.consume_back(); } // Check the parameter types are complete. for (auto param_ref_id : params_to_complete) { if (param_ref_id == SemIR::ErrorInst::InstId) { continue; } // The parameter types need to be complete. RequireCompleteType( context, context.insts().GetAs(param_ref_id).type_id, SemIR::LocId(param_ref_id), [&](auto& builder) { CARBON_DIAGNOSTIC( IncompleteTypeInFunctionParam, Context, "parameter has incomplete type {0} in function definition", TypeOfInstId); builder.Context(param_ref_id, IncompleteTypeInFunctionParam, param_ref_id); }); } // Check the return type is complete. if (function.return_patterns_id.has_value()) { for (auto return_pattern_id : context.inst_blocks().Get(function.return_patterns_id)) { CheckFunctionReturnPatternType(context, SemIR::LocId(return_pattern_id), return_pattern_id, SemIR::SpecificId::None); } // `CheckFunctionReturnPatternType` should have diagnosed incomplete types, // so don't `RequireCompleteType` on the return type. if (return_call_param.has_value()) { // TODO: If the types are already checked for completeness then this does // nothing? TryToCompleteType( context, context.insts().GetAs(return_call_param).type_id, SemIR::LocId(return_call_param)); } } } auto StartFunctionSignature(Context& context) -> void { context.scope_stack().PushForDeclName(); context.inst_block_stack().Push(); context.pattern_block_stack().Push(); context.full_pattern_stack().PushParameterizedDecl(); } auto FinishFunctionSignature(Context& context, bool check_unused) -> FinishFunctionSignatureResult { context.full_pattern_stack().PopFullPattern(); auto pattern_block_id = context.pattern_block_stack().Pop(); auto decl_block_id = context.inst_block_stack().Pop(); context.scope_stack().Pop(check_unused); return {.pattern_block_id = pattern_block_id, .decl_block_id = decl_block_id}; } auto MakeFunctionDecl(Context& context, SemIR::LocId loc_id, SemIR::InstBlockId decl_block_id, bool build_generic, bool is_definition, SemIR::Function function) -> std::pair { CARBON_CHECK(!function.first_owning_decl_id.has_value()); SemIR::FunctionDecl function_decl = {SemIR::TypeId::None, SemIR::FunctionId::None, decl_block_id}; auto decl_id = AddPlaceholderInstInNoBlock( context, SemIR::LocIdAndInst::UncheckedLoc(loc_id, function_decl)); function.first_owning_decl_id = decl_id; if (is_definition) { function.definition_id = decl_id; } if (build_generic) { function.generic_id = BuildGenericDecl(context, decl_id); } // Create the `Function` object. function_decl.function_id = context.functions().Add(std::move(function)); function_decl.type_id = GetFunctionType(context, function_decl.function_id, build_generic ? context.scope_stack().PeekSpecificId() : SemIR::SpecificId::None); ReplaceInstBeforeConstantUse(context, decl_id, function_decl); return {decl_id, function_decl.function_id}; } auto StartFunctionDefinition(Context& context, SemIR::InstId decl_id, SemIR::FunctionId function_id) -> void { // Create the function scope and the entry block. context.scope_stack().PushForFunctionBody(decl_id); context.inst_block_stack().Push(); context.region_stack().PushRegion(context.inst_block_stack().PeekOrAdd()); StartGenericDefinition(context, context.functions().Get(function_id).generic_id); CheckFunctionDefinitionSignature(context, function_id); } auto FinishFunctionDefinition(Context& context, SemIR::FunctionId function_id) -> void { context.inst_block_stack().Pop(); context.scope_stack().Pop(/*check_unused=*/true); auto& function = context.functions().Get(function_id); function.body_block_ids = context.region_stack().PopRegion(); // If this is a generic function, collect information about the definition. FinishGenericDefinition(context, function.generic_id); } } // namespace Carbon::Check ================================================ FILE: toolchain/check/function.h ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #ifndef CARBON_TOOLCHAIN_CHECK_FUNCTION_H_ #define CARBON_TOOLCHAIN_CHECK_FUNCTION_H_ #include "toolchain/check/context.h" #include "toolchain/check/custom_witness.h" #include "toolchain/check/decl_name_stack.h" #include "toolchain/check/subst.h" #include "toolchain/sem_ir/function.h" #include "toolchain/sem_ir/ids.h" namespace Carbon::Check { // Returns the ID of the self parameter pattern, or None. // TODO: Do this during initial traversal of implicit params. auto FindSelfPattern(Context& context, SemIR::InstBlockId implicit_param_patterns_id) -> SemIR::InstId; // Creates suitable return patterns for the given return form, and adds them to // the current pattern block. auto AddReturnPatterns(Context& context, SemIR::LocId loc_id, Context::FormExpr form_expr) -> SemIR::InstBlockId; // Returns whether `function` is a valid declaration of `builtin_kind`. auto IsValidBuiltinDeclaration(Context& context, const SemIR::Function& function, SemIR::BuiltinFunctionKind builtin_kind) -> bool; // Arguments for making a function declaration. struct FunctionDeclArgs { SemIR::NameScopeId parent_scope_id; SemIR::NameId name_id; // The type of the implicit `[self: Self]` parameter, or `None` if there is // none. SemIR::TypeId self_type_id = SemIR::TypeId::None; // Whether `self` is a ref parameter. bool self_is_ref = true; // The types of the explicit parameters. llvm::ArrayRef param_type_ids = {}; // The return type, or `None` if the function doesn't declare a return type. SemIR::TypeId return_type_id = SemIR::TypeId::None; }; // Generates and returns a function declaration. The caller should update the // function object to add a definition. The caller is responsible for ensuring // that the signature is non-generic. auto MakeGeneratedFunctionDecl(Context& context, SemIR::LocId loc_id, const FunctionDeclArgs& args) -> std::pair; // Checks that `new_function` has the same return type as `prev_function`, or if // `prev_function_id` is specified, a specific version of `prev_function`. // Prints a suitable diagnostic and returns false if not. Never checks for a // syntactic match. auto CheckFunctionReturnTypeMatches(Context& context, const SemIR::Function& new_function, const SemIR::Function& prev_function, SemIR::SpecificId prev_specific_id, bool diagnose = true) -> bool; // Checks that `new_function` has the same parameter types and return type as // `prev_function`, or if `prev_function_id` is specified, a specific version of // `prev_function`. Prints a suitable diagnostic and returns false if not. // // `check_syntax` is false if the redeclaration can be called via a thunk with // implicit conversions from the original declaration. // `check_self` is false if the self declaration does not have to match (for // instance in impls of virtual functions). auto CheckFunctionTypeMatches(Context& context, const SemIR::Function& new_function, const SemIR::Function& prev_function, SemIR::SpecificId prev_specific_id, bool check_syntax, bool check_self, bool diagnose = true) -> bool; inline auto CheckFunctionTypeMatches(Context& context, const SemIR::Function& new_function, const SemIR::Function& prev_function) -> bool { return CheckFunctionTypeMatches(context, new_function, prev_function, SemIR::SpecificId::None, /*check_syntax=*/true, /*check_self=*/true); } // Checks that the scrutinee type of `return_pattern_id` in `specific_id` is // concrete. If so, it returns that type; if not, it issues an error and returns // SemIR::ErrorInst::TypeId. `return_pattern_id` must be part of a function's // return form, or the error message will be nonsensical. auto CheckFunctionReturnPatternType(Context& context, SemIR::LocId loc_id, SemIR::InstId return_pattern_id, SemIR::SpecificId specific_id) -> SemIR::TypeId; // Checks that a function declaration's signature is suitable to support a // function definition. This requires the parameter types to be complete and the // return type to be concrete. auto CheckFunctionDefinitionSignature(Context& context, SemIR::FunctionId function_id) -> void; // Prepares for a function signature. Handles necessary stack setup. This is // used for generated functions/thunks, not user-declared functions. auto StartFunctionSignature(Context& context) -> void; // Results for `FinishFunctionSignature`. struct FinishFunctionSignatureResult { SemIR::InstBlockId pattern_block_id; SemIR::InstBlockId decl_block_id; }; // Finishes signatures started by `StartFunctionSignature`. auto FinishFunctionSignature(Context& context, bool check_unused = true) -> FinishFunctionSignatureResult; // Creates a function object for the given function declaration. The caller must // add the returned `decl_id` to a block (typically the current block or // imports). auto MakeFunctionDecl(Context& context, SemIR::LocId loc_id, SemIR::InstBlockId decl_block_id, bool build_generic, bool is_definition, SemIR::Function function) -> std::pair; // Starts a function definition. Handles necessary stack setup, creating the // function scope and entry block, and definition validation. This is used for // both generated functions/thunks and user-declared functions. auto StartFunctionDefinition(Context& context, SemIR::InstId decl_id, SemIR::FunctionId function_id) -> void; // Finishes definitions started by `StartFunctionDefinition`. auto FinishFunctionDefinition(Context& context, SemIR::FunctionId function_id) -> void; } // namespace Carbon::Check #endif // CARBON_TOOLCHAIN_CHECK_FUNCTION_H_ ================================================ FILE: toolchain/check/fuzzer_corpus/008cb7b685ac13c051abc8e778bb56c6be8c920d ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE // CHECK:STDOUT: result: 1 package ExplorerTest; var x: i32; fn Main() -> i32 { return 1; } ================================================ FILE: toolchain/check/fuzzer_corpus/016a8df42375f098eaf2bda8fa7ed1cdd322e51d ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE // CHECK:STDOUT: result: 1 package ExplorerTest; choice Ints { None, One(i32), Two(i32, i32) } // Test some alternate syntaxes choice MoreInts { None(), One(i32), Two(i32, i32), } fn Main() -> i32 { var x: Ints = Ints.None; var y: Ints = Ints.One(42); var z: MoreInts = MoreInts.None(); var n: i32 = 0; match (y) { case Ints.None => { n = n + 2; } case Ints.One(x: auto) => { n = x + 1 - 42; } case Ints.Two(a: auto, b: auto) => { n = 2; } } match (x) { case Ints.One(x: auto) => { n = x + 2; } case Ints.None => { n = n - 1; } case Ints.Two(x: auto, y: auto) => { n = 5; } } match (z) { case MoreInts.None() => { ++n; } } return n; } ================================================ FILE: toolchain/check/fuzzer_corpus/01b6283c2544cf135b8c219478e5b0f8da2325ed ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE // CHECK:STDOUT: 10 // CHECK:STDOUT: 30 // CHECK:STDOUT: 210 // CHECK:STDOUT: result: 0 package ExplorerTest; class A { var n: i32; } impl A as MulWith(i32) where .Result = A { fn Op[self: Self](rhs: i32) -> A { return {.n = self.n * rhs}; } } fn Main() -> i32 { var a: A = {.n = 5}; a = a * 2; Print("{0}", a.n); a *= 3; Print("{0}", a.n); Print("{0}", (a * 7).n); return 0; } ================================================ FILE: toolchain/check/fuzzer_corpus/02163cf92aea19e124c4a5838dae8f0ed340fbfa ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE // CHECK:STDOUT: result: 3 package ExplorerTest; class Cell(T:! type) { fn Create(x: T) -> Cell(T) { return { .data = x }; } fn Get[self: Self]() -> T { return self.data; } fn Put[addr self: Self*](x: T) { (*self).data = x; } fn Update[addr self: Self*, U:! ImplicitAs(T)](x: U) { (*self).data = x; } fn CreateOther[self: Self, U:! type](x: U) -> Cell(U) { return {.data = x}; } var data: T; } class Integer { var int: i32; } fn Main() -> i32 { var i: Integer = {.int = 1}; var c: Cell(Integer) = Cell(Integer).Create(i); // c contains 1 i = {.int = 2}; var j: Integer = c.Get(); // j == 1 c.Put(i); // c contains 2 c.Update(j); // c contains 1 var d: Cell(Integer) = c.CreateOther(i); // d contains 2 return c.data.int + d.data.int; } ================================================ FILE: toolchain/check/fuzzer_corpus/0234ac9a0b945c8a32fc87ee33f9a6d8f9933019 ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE package ExplorerTest; fn Bad[T:! type](x: {.a: i32, .b: T}) {} fn Main() -> i32 { // CHECK:STDERR: COMPILATION ERROR: fail_implicit_conversion_extra_field.carbon:[[@LINE+1]]: mismatch in field names, source field `c` not in destination type `{.a: i32, .b: T}` Bad({.b = 5, .a = 7, .c = 2}); return 0; } ================================================ FILE: toolchain/check/fuzzer_corpus/023877f085e3eedd9a0f13951f04859de07944e9 ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE // CHECK:STDOUT: result: 0 package ExplorerTest; fn Main() -> i32 { var (x: auto, y: [i32;]) = (42, (0, 1)); var index: i32 = 1; y[index] = 0; return y[0] + y[1]; } ================================================ FILE: toolchain/check/fuzzer_corpus/0272d8c7b9a7495efe6a19e9e814ecbe55823213 ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE // CHECK:STDOUT: result: 0 package ExplorerTest; interface X { fn F(); } impl i32 as X { fn F() {} } fn G[T:! X](v: T) -> type { v.F(); return i32; } fn Main() -> i32 { var v: G(0) = 0; return v; } ================================================ FILE: toolchain/check/fuzzer_corpus/03a61af13df6ab601a52a1784995ad7b84d04555 ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE // CHECK:STDOUT: result: 1 package ExplorerTest; fn Main() -> i32 { var x: i32; return 1; } ================================================ FILE: toolchain/check/fuzzer_corpus/03f031da904f8bb48412e82acaa77957fde2445b ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE // CHECK:STDOUT: ab.a: 2 // CHECK:STDOUT: ab.b: 1 // CHECK:STDOUT: ba.a: 2 // CHECK:STDOUT: ba.b: 1 // CHECK:STDOUT: result: 0 package ExplorerTest; alias AB = {.a: i32, .b: i32}; alias BA = {.b: i32, .a: i32}; fn Main() -> i32 { var ab: AB = {.b = 1, .a = 2}; var ba: BA = ab; Print("ab.a: {0}", ab.a); Print("ab.b: {0}", ab.b); Print("ba.a: {0}", ba.a); Print("ba.b: {0}", ba.b); return 0; } ================================================ FILE: toolchain/check/fuzzer_corpus/0444b366b646a800d860d362876dec36afb41e42 ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE // CHECK:STDOUT: DESTRUCTOR A 0 // CHECK:STDOUT: DESTRUCTOR A 1 // CHECK:STDOUT: DESTRUCTOR A 2 // CHECK:STDOUT: result: 1 package ExplorerTest; class A{ destructor[self: Self]{ Print("DESTRUCTOR A {0}",self.n); } var n: i32; } fn Main() -> i32 { var a1: A = {.n = 2}; var a: array(A, 2) = ({.n = 1},{.n = 0}); return 1; } ================================================ FILE: toolchain/check/fuzzer_corpus/048e2bfcc725778f118b704acf2679b712bc6ded ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE package ExplorerTest; choice AB { A(), B() } fn F() -> AB { return AB.A(); } fn Main() -> i32 { // This is T9 from http://moscova.inria.fr/~maranget/papers/warn/warn014.html // Note, this match is exhaustive, but it exceeds our depth limit. match ((F(), F(), F(), F(), F(), F(), F(), F(), F())) { case (AB.A(), AB.A(), AB.A(), AB.A(), AB.A(), AB.A(), AB.A(), AB.A(), AB.A()) => { return 0; } case (AB.B(), AB.B(), AB.B(), AB.B(), AB.B(), AB.B(), AB.B(), AB.B(), AB.B()) => { return 1; } case (_: AB, AB.A(), AB.A(), AB.A(), AB.A(), AB.A(), AB.A(), AB.A(), AB.A()) => { return 2; } case (_: AB, AB.B(), AB.B(), AB.B(), AB.B(), AB.B(), AB.B(), AB.B(), AB.B()) => { return 3; } case (_: AB, _: AB, AB.A(), AB.A(), AB.A(), AB.A(), AB.A(), AB.A(), AB.A()) => { return 4; } case (_: AB, _: AB, AB.B(), AB.B(), AB.B(), AB.B(), AB.B(), AB.B(), AB.B()) => { return 5; } case (_: AB, _: AB, _: AB, AB.A(), AB.A(), AB.A(), AB.A(), AB.A(), AB.A()) => { return 6; } case (_: AB, _: AB, _: AB, AB.B(), AB.B(), AB.B(), AB.B(), AB.B(), AB.B()) => { return 7; } case (_: AB, _: AB, _: AB, _: AB, AB.A(), AB.A(), AB.A(), AB.A(), AB.A()) => { return 8; } case (_: AB, _: AB, _: AB, _: AB, AB.B(), AB.B(), AB.B(), AB.B(), AB.B()) => { return 9; } case (_: AB, _: AB, _: AB, _: AB, _: AB, AB.A(), AB.A(), AB.A(), AB.A()) => { return 10; } case (_: AB, _: AB, _: AB, _: AB, _: AB, AB.B(), AB.B(), AB.B(), AB.B()) => { return 11; } case (_: AB, _: AB, _: AB, _: AB, _: AB, _: AB, AB.A(), AB.A(), AB.A()) => { return 12; } case (_: AB, _: AB, _: AB, _: AB, _: AB, _: AB, AB.B(), AB.B(), AB.B()) => { return 13; } case (_: AB, _: AB, _: AB, _: AB, _: AB, _: AB, _: AB, AB.A(), AB.A()) => { return 14; } case (_: AB, _: AB, _: AB, _: AB, _: AB, _: AB, _: AB, AB.B(), AB.B()) => { return 15; } case (_: AB, _: AB, _: AB, _: AB, _: AB, _: AB, _: AB, _: AB, AB.A()) => { return 16; } case (_: AB, _: AB, _: AB, _: AB, _: AB, _: AB, _: AB, _: AB, AB.B()) => { return 17; } } // CHECK:STDERR: COMPILATION ERROR: fail_exhaustive_exponential_series_t.carbon:[[@LINE+1]]: non-exhaustive match may allow control-flow to reach the end of a function that provides a `->` return type } ================================================ FILE: toolchain/check/fuzzer_corpus/068ac29021d30e21dc976177ae56f68096da6740 ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE // CHECK:STDOUT: result: 0 package ExplorerTest; fn ReturnIndirectly[T:! type](direct: bool, x: T) -> type { if (direct) { return T; } else { return ReturnIndirectly(true, x); } } fn Main() -> ReturnIndirectly(false, 0) { return 0; } ================================================ FILE: toolchain/check/fuzzer_corpus/06c71952ad52d77790a1a5eb631eaa0759800fec ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE // CHECK:STDOUT: 1 // CHECK:STDOUT: 1 // CHECK:STDOUT: 1 // CHECK:STDOUT: 1 // CHECK:STDOUT: result: 0 package Foo; class X { fn F[self: Self]() -> i32 { return self.n; } var n: i32; } fn Main() -> i32 { var v: X = {.n = 1}; let p: X* = &v; Print("{0}", p->n); Print("{0}", p->(X.n)); Print("{0}", p->F()); Print("{0}", p->(X.F)()); return 0; } ================================================ FILE: toolchain/check/fuzzer_corpus/078ec0d032fa51fabffef18fc82712e9536b013e ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE package ExplorerTest; interface ManyTypes { let T0:! type; let T1:! type; let T2:! type; let T3:! type; let T4:! type; let T5:! type; let T6:! type; let T7:! type; let T8:! type; let T9:! type; } fn F[ M:! ManyTypes where .T0 = .T1 and .T1 = .T2 and .T2 = .T3 and .T3 = .T4 and .T4 = .T5 and .T5 = .T6 and .T6 = .T7 and .T7 = .T8 and .T8 = .T9 and // CHECK:STDERR: COMPILATION ERROR: fail_rewrite_cycle.carbon:[[@LINE+1]]: rewrite of (M).(ManyTypes.T4) applies within its own resolved expansion of (M).(ManyTypes.T4) .T9 = .T0]() {} fn Main() -> i32 { return 0; } ================================================ FILE: toolchain/check/fuzzer_corpus/07b0e6716d0adf617012146642d9cba39b9b006b ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE // CHECK:STDOUT: result: 0 package ExplorerTest; fn ReturnSecond(_: i32, x: i32) -> i32 { return x; } fn Main() -> i32 { return ReturnSecond(1, 0); } ================================================ FILE: toolchain/check/fuzzer_corpus/081d7c4effd1e9cc6129090e3c831e1af2633e62 ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE // CHECK:STDOUT: result: -2 package ExplorerTest; fn apply[T:! type, U:! type](f: __Fn (T) -> U, x: T) -> U { return f(x); } fn positive(x: bool) -> i32 { if (x) { return 2; } else { return -2; } } fn Main() -> i32 { return apply(positive, false); } ================================================ FILE: toolchain/check/fuzzer_corpus/0831e49d5a00cd7ae6512b7cd8f782c1ed305fa5 ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE // CHECK:STDOUT: result: 0 package ExplorerTest; choice AB { A(), B() } fn F() -> AB { return AB.A(); } fn Main() -> i32 { match ((F(), F(), F(), F(), F(), F(), F(), F())) { case (AB.A(), AB.A(), AB.A(), AB.A(), AB.A(), AB.A(), AB.A(), AB.A()) => { return 0; } case (AB.B(), AB.A(), AB.A(), AB.A(), AB.A(), AB.A(), AB.A(), AB.A()) => { return 1; } case (_: AB, AB.B(), AB.A(), AB.A(), AB.A(), AB.A(), AB.A(), AB.A()) => { return 2; } case (_: AB, _: AB, AB.B(), AB.A(), AB.A(), AB.A(), AB.A(), AB.A()) => { return 3; } case (_: AB, _: AB, _: AB, AB.B(), AB.A(), AB.A(), AB.A(), AB.A()) => { return 4; } case (_: AB, _: AB, _: AB, _: AB, AB.B(), AB.A(), AB.A(), AB.A()) => { return 5; } case (_: AB, _: AB, _: AB, _: AB, _: AB, AB.B(), AB.A(), AB.A()) => { return 6; } case (_: AB, _: AB, _: AB, _: AB, _: AB, _: AB, AB.B(), AB.A()) => { return 7; } case (_: AB, _: AB, _: AB, _: AB, _: AB, _: AB, _: AB, AB.B()) => { return 8; } } } ================================================ FILE: toolchain/check/fuzzer_corpus/083cd63392060ddfbc062ce0ba14a7ac89441a1f ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE // CHECK:STDOUT: c.Foo(): 1 // CHECK:STDOUT: d.Foo(): 2 // CHECK:STDOUT: e.Foo(): 3 // CHECK:STDOUT: (*dp).Foo(): 3 // CHECK:STDOUT: (*dc).Foo(): 3 // CHECK:STDOUT: result: 0 package ExplorerTest; base class C { var value_c: i32; virtual fn Foo[self: Self]() -> i32 { return self.value_c; } } base class D { extend base: C; var value_d: i32; impl fn Foo[self: Self]() -> i32 { return self.value_d; } } class E { extend base: D; var value_e: i32; impl fn Foo[self: Self]() -> i32 { return self.value_e; } } fn Main() -> i32 { var c: C = {.value_c = 1}; Print("c.Foo(): {0}", c.Foo()); var d: D = {.value_d = 2, .base = {.value_c = 1}}; Print("d.Foo(): {0}", d.Foo()); var e: E = {.value_e = 3, .base={.value_d = 2, .base = {.value_c = 1}}}; Print("e.Foo(): {0}", e.Foo()); var dp: D* = &e; Print("(*dp).Foo(): {0}", (*dp).Foo()); var dc: C* = &e; Print("(*dc).Foo(): {0}", (*dc).Foo()); return 0; } ================================================ FILE: toolchain/check/fuzzer_corpus/0900a9afaef22b798df75b576e8096343a98903a ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE // CHECK:STDOUT: result: 0 package ExplorerTest; // Test mutation of a global variable. var zero: i32 = 1; fn Main() -> i32 { zero = 0; return zero; } ================================================ FILE: toolchain/check/fuzzer_corpus/09762ec95a5e7e5a232b735f95e808da510678fd ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE // CHECK:STDOUT: 1 // CHECK:STDOUT: 3 // CHECK:STDOUT: result: 0 package ExplorerTest; interface GetX { fn DoIt[self: Self]() -> i32; } impl forall [template T:! type] T as GetX { fn DoIt[self: Self]() -> i32 { return self.x; } } class C { var x: i32; } fn Main() -> i32 { var a: auto = {.x = 1, .y = 2}; var b: C = {.x = 3}; Print("{0}", a.(GetX.DoIt)()); Print("{0}", b.(GetX.DoIt)()); return 0; } ================================================ FILE: toolchain/check/fuzzer_corpus/0c1db2e8ec3df9d6adf52c88ba67333e2059da97 ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE // CHECK:STDOUT: result: 526 package ExplorerTest; interface A { fn F() -> i32; fn G() -> i32; } interface B { fn H() -> i32; } fn Get1[T:! A & B](n: T) -> i32 { return n.F() + n.H(); } fn Get2[T:! B & A](n: T) -> i32 { return n.G(); } fn Get3[T:! B & A & A & B & A](n: T) -> i32 { return n.G() + n.H(); } impl i32 as A { fn F() -> i32 { return 1; } fn G() -> i32 { return 2; } } impl i32 as B { fn H() -> i32 { return 4; } } fn Main() -> i32 { var z: i32 = 0; return Get1(z) * 100 + Get2(z) * 10 + Get3(z); } ================================================ FILE: toolchain/check/fuzzer_corpus/0ce1b0382ddf1eaf0a21e39a1932936edd0c7b63 ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE // CHECK:STDOUT: result: 3 package Foo; class X { fn F[self: Self](o: Self) -> Self { return {.n = self.n + o.n}; } var n: i32; } fn Main() -> i32 { var v: X = {.n = 1}; var w: X = {.n = 2}; return v.(X.F)(w).(X.n); } ================================================ FILE: toolchain/check/fuzzer_corpus/0d824e8e58e5a28b74bfde959ca83df292a33702 ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE // CHECK:STDOUT: result: 0 package ExplorerTest; interface Vector { fn Add[self: Self](b: Self) -> Self; fn Scale[self: Self](v: i32) -> Self; } class Point { var x: i32; var y: i32; extend impl as Vector { fn Add[self: Point](b: Point) -> Point { return {.x = self.x + b.x, .y = self.y + b.y}; } fn Scale[self: Point](v: i32) -> Point { return {.x = self.x * v, .y = self.y * v}; } } } fn AddAndScaleGeneric[T:! Vector](a: T, b: T, s: i32) -> T { var m: __Fn(T)->T = a.Add; var n: __Fn(i32)->T = m(b).Scale; return n(s); } fn Main() -> i32 { var a: Point = {.x = 1, .y = 1}; var b: Point = {.x = 2, .y = 3}; var p: Point = AddAndScaleGeneric(a, b, 5); return p.x - 15; } ================================================ FILE: toolchain/check/fuzzer_corpus/0d9b1f8863ca4b08680e246aeb58b12737afc902 ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE // CHECK:STDOUT: strings less: 1 // CHECK:STDOUT: ints less: 0 // CHECK:STDOUT: strings less eq: 1 // CHECK:STDOUT: ints less eq: 1 // CHECK:STDOUT: strings greater: 0 // CHECK:STDOUT: ints greater: 0 // CHECK:STDOUT: strings greater eq: 0 // CHECK:STDOUT: ints greater eq: 1 // CHECK:STDOUT: result: 0 package ExplorerTest; fn Main() -> i32 { Print("strings less: {0}", if "hello" < "world" then 1 else 0); Print("ints less: {0}", if 1 < 1 then 1 else 0); Print("strings less eq: {0}", if "hello" <= "world" then 1 else 0); Print("ints less eq: {0}", if 1 <= 1 then 1 else 0); Print("strings greater: {0}", if "hello" > "world" then 1 else 0); Print("ints greater: {0}", if 1 > 1 then 1 else 0); Print("strings greater eq: {0}", if "hello" >= "world" then 1 else 0); Print("ints greater eq: {0}", if 1 >= 1 then 1 else 0); return 0; } ================================================ FILE: toolchain/check/fuzzer_corpus/0dc65251b81300d504287a2f7be97054bd22d386 ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE // CHECK:STDOUT: result: 0 package ExplorerTest; namespace N; fn N.F() {} alias A = N; alias B = A; alias C = B; fn Main() -> i32 { C.F(); return 0; } ================================================ FILE: toolchain/check/fuzzer_corpus/0fc717e2905c26bce32a4591acdd0f46859765a2 ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE package ExplorerTest; __mixin M1 { fn F1[self: Self](x: Self) -> Self{ return x; } // CHECK:STDERR: COMPILATION ERROR: fail_circular_mixing.carbon:[[@LINE+1]]: 'M3' has not been declared yet __mix M3; } __mixin M2 { fn F2() { } __mix M1; } __mixin M3 { __mix M2; fn F3() { } } class C { __mix M2; } fn Main() -> i32 { return 0; } ================================================ FILE: toolchain/check/fuzzer_corpus/10204d82b32ad9dc256b595048de9efabd93067f ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE // CHECK:STDOUT: result: 0 package ExplorerTest; class C(T:! type) {} fn F(T:! type) -> type { return C(T); } fn Main() -> i32 { var v: F(i32) = {}; var w: C(i32) = v; return 0; } ================================================ FILE: toolchain/check/fuzzer_corpus/11253cbc5df4b6b891cc68ac943599555d3e30dc ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE // CHECK:STDOUT: result: 0 package ExplorerTest; fn DoNothing() { // Empty block } fn Main() -> i32 { var x: i32 = 0; DoNothing(); return x; } ================================================ FILE: toolchain/check/fuzzer_corpus/12b56f338f82115bc9733846f50bc5863ca3fd9c ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE // CHECK:STDOUT: result: 0 package ExplorerTest; fn Main() -> i32 { var s: String = '''filetype A "block" ""string"" literal with file type indicator. '''; if (s == "A \"block\" \"\"string\"\" literal\n with file type indicator.\n") { return 0; } else { return 1; } } ================================================ FILE: toolchain/check/fuzzer_corpus/12c9b3f16c56bbb271111f4bdc8a2401dab16154 ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE // CHECK:STDOUT: result: 0 package ExplorerTest; fn CompareStr(s: String) -> i32 { if (s == ##"str"##) { return 0; } return 1; } fn Main() -> i32 { return CompareStr(#####"str"#####); } ================================================ FILE: toolchain/check/fuzzer_corpus/138a0e4547e111ad5cc545e9b19a1e430348636b ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE impl package ExplorerTest; fn Main() -> i32 { // CHECK:STDERR: RUNTIME ERROR: fail_index_empty.carbon:[[@LINE+1]]: Invalid `{}` in `Print: {}` Print("Print: {}"); return 0; } ================================================ FILE: toolchain/check/fuzzer_corpus/13f9d954261112e0d9131eb97327515395550d00 ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE // CHECK:STDOUT: result: 0 package ExplorerTest; fn add1(x: i32) -> i32 { return x + 1; } fn Main() -> i32 { var f: __Fn(i32)->i32 = add1; return f(-1); } ================================================ FILE: toolchain/check/fuzzer_corpus/1551a409e456b8102c5b44313303aad008b2a06d ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE package ExplorerTest; interface IFace { // CHECK:STDERR: COMPILATION ERROR: fail_use_assoc_const_before_decl.carbon:[[@LINE+1]]: 'C' has not been declared yet let B:! C; let C:! IFace; } fn Main() -> i32 { // return T; } ================================================ FILE: toolchain/check/fuzzer_corpus/155ec88a933f5a3ae411ee810eb89f5631bff2d5 ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE // CHECK:STDOUT: result: 0 package ExplorerTest; namespace N; fn Main() -> i32 { return 0; } ================================================ FILE: toolchain/check/fuzzer_corpus/15a7fdcf1eda6e722c03d2cccbc93ddf75681128 ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE // CHECK:STDOUT: (*c).a: 1 // CHECK:STDOUT: (*c).Foo(): 1 // CHECK:STDOUT: (*c).Bar(): 1 // CHECK:STDOUT: result: 0 package ExplorerTest; base class C { var a: i32; fn Foo[self: Self]() -> i32 { return 1; } fn Bar() -> i32 { return 1; } } class D { extend base: C; var b: i32; fn Foo[self: Self]() -> i32 { return 2; } fn Bar() -> i32 { return 2; } } fn Main() -> i32 { var d: D = { .base = {.a = 1}, .b = 2 }; var c: C* = &d; Print("(*c).a: {0}", (*c).a); Print("(*c).Foo(): {0}", (*c).Foo()); Print("(*c).Bar(): {0}", (*c).Bar()); return 0; } ================================================ FILE: toolchain/check/fuzzer_corpus/162348262c5e2797cf53a49adec2c1df056268d2 ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE package ExplorerTest; fn Main() -> i32 { var x: i32; // CHECK:STDERR: COMPILATION ERROR: fail_rhs_def.carbon:[[@LINE+1]]: use of uninitialized variable x var y: i32 = x; return x; } ================================================ FILE: toolchain/check/fuzzer_corpus/16fb4fcee5f2fbd4b5b4e65358ab00e800f67db9 ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE // CHECK:STDOUT: result: 0 package ExplorerTest; interface Number { fn Zero() -> Self; fn Add[self: Self](other: Self) -> Self; } class Point(T:! Number) { var x: T; var y: T; } impl i32 as Number { fn Zero() -> i32 { return 0; } fn Add[self: i32](other: i32) -> i32 { return self + other; } } impl forall [U:! Number] Point(U) as Number { fn Zero() -> Point(U) { return {.x = U.Zero(), .y = U.Zero() }; } fn Add[self: Point(U)](other: Point(U)) -> Point(U) { return {.x = self.x.Add(other.x), .y = self.y.Add(other.y)}; } } fn Sum[E:! Number](x: E, y: E) -> E { var total: E = E.Zero(); total = total.Add(x); total = total.Add(y); return total; } fn SumPoints[E:! Number](p: Point(E), q: Point(E)) -> Point(E) { return Sum(p, q); } fn Main() -> i32 { var p: Point(i32) = {.x = 1, .y = 2}; var q: Point(i32) = {.x = 4, .y = 3}; var r: Point(i32) = SumPoints(p, q); return r.x - r.y; } ================================================ FILE: toolchain/check/fuzzer_corpus/17261c3182bd1de4382f0d3fef3917a949c36e32 ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // This doesn't check trace output because it's too slow with it. // AUTOUPDATE // CHECK:STDOUT: result: 1 package ExplorerTest; interface ManyTypes { let T0:! type; let T1:! type; let T2:! type; let T3:! type; let T4:! type; let T5:! type; let T6:! type; let T7:! type; } interface Splat { fn Op(n: i32) -> Self; } impl i32 as Splat { fn Op(n: i32) -> Self { return n; } } impl forall [T:! Splat] (T, T, T) as Splat { fn Op(n: i32) -> Self { let v: T = T.Op(n); return (v, v, v); } } fn CallSplat(T:! Splat, n: i32) -> T { return T.Op(n); } fn DoSplat( M:! ManyTypes where .T0 = (.T1, .T1, .T1) and .T1 = (.T2, .T2, .T2) and .T2 = (.T3, .T3, .T3) and .T3 = (.T4, .T4, .T4) and .T4 = (.T5, .T5, .T5) and .T5 = (.T6, .T6, .T6) and .T6 = (.T7, .T7, .T7) and .T7 = i32, n: M.T7) -> M.T0 { return CallSplat(M.T0, n); } interface First { fn Op[self: Self]() -> i32; } impl i32 as First { fn Op[self: Self]() -> i32 { return self; } } impl forall [T:! First] (T, T, T) as First { fn Op[self: Self]() -> i32 { let (a: T, b: T, c: T) = self; return a.Op(); } } fn DoFirst( M:! ManyTypes where .T7 = i32 and .T6 = (.T7, .T7, .T7) and .T5 = (.T6, .T6, .T6) and .T4 = (.T5, .T5, .T5) and .T3 = (.T4, .T4, .T4) and .T2 = (.T3, .T3, .T3) and .T1 = (.T2, .T2, .T2) and .T0 = (.T1, .T1, .T1), v: M.T0) -> M.T7 { return v.(First.Op)(); } class C { extend impl as ManyTypes where .T3 = (.T4, .T4, .T4) and .T1 = (.T2, .T2, .T2) and .T4 = (.T5, .T5, .T5) and .T6 = (.T7, .T7, .T7) and .T2 = (.T3, .T3, .T3) and .T7 = i32 and .T5 = (.T6, .T6, .T6) and .T0 = (.T1, .T1, .T1) {} } fn Main() -> i32 { return DoFirst(C, DoSplat(C, 1)); } ================================================ FILE: toolchain/check/fuzzer_corpus/17dc6eb1bbb8fd9671f46aa63924cf1a76837a2c ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE // CHECK:STDOUT: 1: 1 // CHECK:STDOUT: 2: 2 // CHECK:STDOUT: 3: 3 // CHECK:STDOUT: 4: 4 // CHECK:STDOUT: result: 0 package ExplorerTest; class A { var n: i32; impl as ImplicitAs(i32) { fn Convert[self: Self]() -> i32 { return self.n; } } } impl i32 as ImplicitAs(A) { fn Convert[self: Self]() -> A { return {.n = self}; } } fn Main() -> i32 { var arr1: array(i32, 2) = (1, 2 as A); Print("1: {0}", arr1[0]); Print("2: {0}", arr1[1]); var arr2: array(A, 2) = (3, 4 as A); Print("3: {0}", arr2[0].n); Print("4: {0}", arr2[1].n); return 0; } ================================================ FILE: toolchain/check/fuzzer_corpus/184c236354c35072383945d9e6bdf0c005b5d20e ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE // CHECK:STDOUT: result: 0 package ExplorerTest; class Point { fn Origin() -> Point { return {.x = 0, .y = 0}; } fn GetX[self: Point]() -> i32 { return self.x; } fn GetXY[self: Point]() -> (i32, i32) { return (self.GetX(), self.y); } var x: i32; var y: i32; } fn Main() -> i32 { var p: Point = Point.Origin(); return p.GetXY()[0]; } ================================================ FILE: toolchain/check/fuzzer_corpus/18909d083c5867fc03a952211bd61ca688bb908e ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE package ExplorerTest; interface Container { let Element:! type; fn Front[self: Self]() -> Element; } fn A[T:! Container where .Element = i32](x: T) -> T.Element { return x.Front(); } fn B[T:! Container](x: T) -> i32 { // CHECK:STDERR: COMPILATION ERROR: fail_missing_rewrite.carbon:[[@LINE+1]]: constraint requires that (T).(Container.Element) (with value (T).(Container.Element)) == i32, which is not known to be true return A(x); } fn Main() -> i32 { return 0; } ================================================ FILE: toolchain/check/fuzzer_corpus/1986b4fb3bc5c60ad5f4faa1b8003beb70a7ece8 ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE // CHECK:STDOUT: result: 1 package ExplorerTest; fn Main() -> i32 { var (x: i32, y: i32); x = 1; return x; } ================================================ FILE: toolchain/check/fuzzer_corpus/1a5c8022aeb946ee3551e569dad4ed36cdbcd44f ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE // CHECK:STDOUT: A // CHECK:STDOUT: c.a: 1 // CHECK:STDOUT: c.b: 2 // CHECK:STDOUT: c.c: 3 // CHECK:STDOUT: result: 0 package ExplorerTest; base class A { fn FunctionA() {} var a: i32; var aa: String; } base class B { extend base: A; fn FunctionB() {} var b: i32; } class C { extend base: B; fn FunctionC() {} var c: i32; } fn Main() -> i32 { var c: C = {.base={.base={.aa="A", .a=1}, .b=2, }, .c=3}; c.FunctionA(); c.FunctionB(); c.FunctionC(); Print(c.aa); Print("c.a: {0}", c.a); Print("c.b: {0}", c.b); Print("c.c: {0}", c.c); return 0; } ================================================ FILE: toolchain/check/fuzzer_corpus/1afe54e48b1e5bcdd7c8949513e0b2a3fbde1a65 ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE // CHECK:STDOUT: Print: {0} // CHECK:STDOUT: result: 0 package ExplorerTest; fn Main() -> i32 { Print("Print: {{0}"); return 0; } ================================================ FILE: toolchain/check/fuzzer_corpus/1b85cf86024d9fcfe011f6998861b289aac97e60 ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE // CHECK:STDOUT: Allocate D // CHECK:STDOUT: DESTRUCTOR B // CHECK:STDOUT: DESTRUCTOR A // CHECK:STDOUT: DESTRUCTOR D // CHECK:STDOUT: DESTRUCTOR C // CHECK:STDOUT: Delete B from A* // CHECK:STDOUT: DESTRUCTOR B // CHECK:STDOUT: DESTRUCTOR A // CHECK:STDOUT: Delete D from C* // CHECK:STDOUT: DESTRUCTOR D // CHECK:STDOUT: DESTRUCTOR C // CHECK:STDOUT: result: 0 package ExplorerTest; base class A { virtual destructor[self: Self] { Print("DESTRUCTOR A"); } } class B { extend base: A; fn Create() -> Self{ return {.base={}}; } impl destructor[self: Self] { Print("DESTRUCTOR B"); } } base class C { virtual destructor[self: Self] { Print("DESTRUCTOR C"); } } class D { extend base: C; fn Create() -> Self{ return {.base={}, .d_pa=heap.New(B.Create())}; } impl destructor[self: Self] { Print("DESTRUCTOR D"); } var d_pa: A*; } fn Main() -> i32 { Print("Allocate D"); var pd: D* = heap.New(D.Create()); var pc: C* = pd; Print("Delete B from A*"); heap.Delete(pd->d_pa); Print("Delete D from C*"); heap.Delete(pc); return 0; } ================================================ FILE: toolchain/check/fuzzer_corpus/1baac08b0dfe6bf0b1157757b14ad4a389ba4693 ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE package ExplorerTest; fn Main() -> i32 { // CHECK:STDERR: COMPILATION ERROR: fail_too_many_args.carbon:[[@LINE+1]]: Print takes 1 or 2 arguments, received 5 Print("too", "many", "args", "to", "print"); return 0; } ================================================ FILE: toolchain/check/fuzzer_corpus/1be679f324846aca4379ea4939981410425b287f ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE package ExplorerTest; fn Main() -> i32 { // CHECK:STDERR: COMPILATION ERROR: fail_assign_to_rval.carbon:[[@LINE+1]]: Only a reference expression can be assigned to, but got `1` 1 = 0; return 0; } ================================================ FILE: toolchain/check/fuzzer_corpus/1c56a65ba702fd27f5a72caf9c5e9170312db350 ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE package ExplorerTest; fn Main() -> i32 { // CHECK:STDERR: COMPILATION ERROR: fail_size_mismatch.carbon:[[@LINE+1]]: type error in initializer of variable: '(i32, i32, i32)' is not implicitly convertible to 'array(i32, 2)' var x: array(i32, 2) = (0, 1, 2); return x[0]; } ================================================ FILE: toolchain/check/fuzzer_corpus/1c7eed351b92a87b8fd0c60d58e1916f3ae242d6 ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE // CHECK:STDOUT: result: 25 package ExplorerTest; class List { choice Node { Nil, Cons(i32, Self*) } var node: Node; // Creates a list of `Cons(n, Cons(n - 1, ... Cons(1, Nil) ... ))`. fn Make(n: i32) -> Self { return { .node = if n == 0 then Node.Nil else Node.Cons(n, heap.New(Make(n - 1))) }; } // Returns the sum of values in the list plus the value of `a`. fn Sum[self: Self](a: i32) -> i32 { match (self.node) { case Node.Nil => { return a; } case Node.Cons(b: i32, rest: Self*) => { return rest->Sum(a + b); } } } } fn Main() -> i32 { var l: List = List.Make(5); return l.Sum(10); } ================================================ FILE: toolchain/check/fuzzer_corpus/1d35c2bfb3606a1875a7d9218edc05b242cc75aa ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE // CHECK:STDOUT: result: 3 package ExplorerTest; fn AddInt(a: i32, b: i32) -> i32 { if (a == b) { returned var ret: i32 = a + b; return var; } else { return a + b; } } fn Main() -> i32 { return AddInt(1, 2); } ================================================ FILE: toolchain/check/fuzzer_corpus/1e1c015742738ddadeecb98baeb2988e9debfb6f ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE // CHECK:STDOUT: i32 // CHECK:STDOUT: String // CHECK:STDOUT: result: 0 package Testcase; interface HasName { let Name:! String; } impl i32 as HasName where .Name = "i32" {} impl String as HasName where .Name = "String" {} fn Main() -> i32 { Print(i32.(HasName.Name)); Print(String.(HasName.Name)); return 0; } ================================================ FILE: toolchain/check/fuzzer_corpus/1e55c67e9a55008a70df08daf7964b04102f5dd7 ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE // CHECK:STDOUT: result: 0 package ExplorerTest; fn add(x: i32, y: i32) -> auto { return x + y; } fn Main() -> i32 { return add(1, 2) - 3; } ================================================ FILE: toolchain/check/fuzzer_corpus/1f6409865ff8ddad8bc5771a00d12932a67fb7dd ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE // CHECK:STDERR: RUNTIME ERROR: {{.*}}/explorer/data/prelude.carbon:{{.*}}: Integer overflow package ExplorerTest; fn Main() -> i32 { var a: auto = 5 << -1; return 0; } ================================================ FILE: toolchain/check/fuzzer_corpus/20e03ea11ed959b8d8c2815133dbf0b31e2164c2 ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE // CHECK:STDOUT: result: 1 package Foo; interface A { fn F() -> i32; } class X { extend impl as A { fn F() -> i32 { return 1; } } } fn Main() -> i32 { var a: X = {}; return a.(A.F)(); } ================================================ FILE: toolchain/check/fuzzer_corpus/2106211513dcb519a34bb65957c8fd0c85f75b16 ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE package ExplorerTest; fn Main() -> i32 { { // CHECK:STDERR: SYNTAX ERROR: fail_block.carbon:[[@LINE+1]]: syntax error, unexpected RETURN, expecting PERIOD or RIGHT_CURLY_BRACE return 0; } } ================================================ FILE: toolchain/check/fuzzer_corpus/219513dc72d769293ba33b019ebd2282b7833404 ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE // CHECK:STDOUT: result: 0 package ExplorerTest; choice AB { A(), B() } fn F() -> AB { return AB.A(); } fn Main() -> i32 { // This is T8 from http://moscova.inria.fr/~maranget/papers/warn/warn014.html match ((F(), F(), F(), F(), F(), F(), F(), F())) { case (AB.A(), AB.A(), AB.A(), AB.A(), AB.A(), AB.A(), AB.A(), AB.A()) => { return 0; } case (AB.B(), AB.B(), AB.B(), AB.B(), AB.B(), AB.B(), AB.B(), AB.B()) => { return 1; } case (_: AB, AB.A(), AB.A(), AB.A(), AB.A(), AB.A(), AB.A(), AB.A()) => { return 2; } case (_: AB, AB.B(), AB.B(), AB.B(), AB.B(), AB.B(), AB.B(), AB.B()) => { return 3; } case (_: AB, _: AB, AB.A(), AB.A(), AB.A(), AB.A(), AB.A(), AB.A()) => { return 4; } case (_: AB, _: AB, AB.B(), AB.B(), AB.B(), AB.B(), AB.B(), AB.B()) => { return 5; } case (_: AB, _: AB, _: AB, AB.A(), AB.A(), AB.A(), AB.A(), AB.A()) => { return 6; } case (_: AB, _: AB, _: AB, AB.B(), AB.B(), AB.B(), AB.B(), AB.B()) => { return 7; } case (_: AB, _: AB, _: AB, _: AB, AB.A(), AB.A(), AB.A(), AB.A()) => { return 8; } case (_: AB, _: AB, _: AB, _: AB, AB.B(), AB.B(), AB.B(), AB.B()) => { return 9; } case (_: AB, _: AB, _: AB, _: AB, _: AB, AB.A(), AB.A(), AB.A()) => { return 10; } case (_: AB, _: AB, _: AB, _: AB, _: AB, AB.B(), AB.B(), AB.B()) => { return 11; } case (_: AB, _: AB, _: AB, _: AB, _: AB, _: AB, AB.A(), AB.A()) => { return 12; } case (_: AB, _: AB, _: AB, _: AB, _: AB, _: AB, AB.B(), AB.B()) => { return 13; } case (_: AB, _: AB, _: AB, _: AB, _: AB, _: AB, _: AB, AB.A()) => { return 14; } case (_: AB, _: AB, _: AB, _: AB, _: AB, _: AB, _: AB, AB.B()) => { return 15; } } } ================================================ FILE: toolchain/check/fuzzer_corpus/2268ee6ebd54f257f7fe853a15c6ef7785108cea ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE // CHECK:STDOUT: result: 0 package ExplorerTest; class Point { var x: i32; var y: i32; } var p: Point = {.x = 1, .y = 2}; fn Main() -> i32 { return p.y - p.x - 1; } ================================================ FILE: toolchain/check/fuzzer_corpus/228ce64e3803e722ecf32dc43b499d6726756daf ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE // CHECK:STDOUT: 7 // CHECK:STDOUT: 3 // CHECK:STDOUT: 0 // CHECK:STDOUT: result: 0 package ExplorerTest; class A { var n: i32; } impl A as ModWith(i32) where .Result = A { fn Op[self: Self](rhs: i32) -> A { return {.n = self.n % rhs}; } } fn Main() -> i32 { var a: A = {.n = 15}; a = a % 8; Print("{0}", a.n); a %= 4; Print("{0}", a.n); Print("{0}", (a % 3).n); return 0; } ================================================ FILE: toolchain/check/fuzzer_corpus/2335a5e08cccb3045cf4b8bc8e68feb442d2324f ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // TODO: This SHOULD fail but doesn't presently. // // AUTOUPDATE // CHECK:STDOUT: result: 0 package ExplorerTest; import ExplorerTest library "Nonexistent"; fn Main() -> i32 { return 0; } ================================================ FILE: toolchain/check/fuzzer_corpus/236e8e31b5787b58b23bb57e00b101f89e8535b0 ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE package ExplorerTest; class A {} fn Main() -> i32 { var a: A = {}; // CHECK:STDERR: COMPILATION ERROR: fail_no_mul.carbon:[[@LINE+2]]: type error in `*`: // CHECK:STDERR: could not find implementation of interface MulWith(U = i32) for class A a * 1; return 0; } ================================================ FILE: toolchain/check/fuzzer_corpus/2379517a82b1d7db9c20bb4922230d42bbffa68a ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE // CHECK:STDOUT: result: 1 package ExplorerTest; fn Main() -> i32 { var x: i32; x = 1; return x; } ================================================ FILE: toolchain/check/fuzzer_corpus/2394164e5f5c1d546a3da770f5b6e05994f3a30f ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE // CHECK:STDOUT: 1 // CHECK:STDOUT: 2 // CHECK:STDOUT: 3 // CHECK:STDOUT: result: 0 package ExplorerTest; base class A { var a: i32; } base class B { extend base: A; var b: i32; } class C { extend base: B; var c: i32; } fn Main() -> i32 { var c: C = {.base = {.base = {.a = 1}, .b = 2}, .c = 3}; let (pa: A*, pb: B*, pc: C*) = (&c, &c, &c); Print("{0}", pa->a); Print("{0}", pb->b); Print("{0}", pc->c); return 0; } ================================================ FILE: toolchain/check/fuzzer_corpus/241b6bbd6226547b2428d4a5cb71fed63597659d ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE // CHECK:STDOUT: result: 0 package ExplorerTest; choice Ints { None, One(i32), Two(i32, i32) } fn Main() -> i32 { let (var Ints.Two(a1: auto, var a2: auto), ((b: auto, var c: auto), var (d: auto, e: auto))) = (Ints.Two(1, 10), ((2, 3), (4, 5))); a1 = 0; a2 = 0; c = 0; d = 0; e = 0; return 0; } ================================================ FILE: toolchain/check/fuzzer_corpus/24a8948753be6ae079a443e207af49e88785fc27 ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE package ExplorerTest; class ConvertTo(T:! type) { var v: T; extend impl as ImplicitAs(T) { fn Convert[self: Self]() -> T { return self.v; } } } fn Main() -> i32 { // CHECK:STDERR: COMPILATION ERROR: fail_intrinsic_no_convert.carbon:[[@LINE+3]]: type error in __intrinsic_assert argument 0 // CHECK:STDERR: expected: bool // CHECK:STDERR: actual: class ConvertTo(T = bool) __intrinsic_assert({.v = true} as ConvertTo(bool), {.v = "Pass"} as ConvertTo(String)); return 0; } ================================================ FILE: toolchain/check/fuzzer_corpus/24f96b44faf9d816c26ec36acd1f5c4562d0f1e9 ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE package ExplorerTest; fn Main() -> i32 { // CHECK:STDERR: COMPILATION ERROR: fail_destination_not_type.carbon:[[@LINE+1]]: type error in type expression: 'i32' is not implicitly convertible to 'type' return 4 as 7; } ================================================ FILE: toolchain/check/fuzzer_corpus/2689f156236c25d70a265b1261f8bea8cfef527f ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // This doesn't test trace ouptut because it's too slow. // AUTOUPDATE // CHECK:STDOUT: String list length is: 7 // CHECK:STDOUT: This is a String Entry 4 // CHECK:STDOUT: Value 1337 // CHECK:STDOUT: result: 0 package ExplorerTest; class Node(T:! type) { fn Create(value: T)-> Node(T) { return { .value = Optional(T).Create(value), .next = Optional(Node(T)*).CreateEmpty(), .prev = Optional(Node(T)*).CreateEmpty() }; } fn set_next[addr self: Self*](n: Optional(Node(T)*)) { (*self).next = n; } fn set_prev[addr self: Self*](n: Optional(Node(T)*)) { (*self).prev = n; } var value: Optional(T); var next: Optional(Node(T)*); var prev: Optional(Node(T)*); } class LinkedList(T:! type) { fn Create() -> LinkedList(T) { return { .head = Optional(Node(T)*).CreateEmpty(), .tail = Optional(Node(T)*).CreateEmpty(), .len = 0 }; } fn PushBack[addr self: Self*](value:T) { (*self).len = (*self).len + 1; if(not (*self).head.HasValue()) { (*self).head = Optional(Node(T)*).Create(heap.New(Node(T).Create(value))); (*self).tail = (*self).head; return; } var last: Optional(Node(T)*) = (*self).tail; var last_value:Node(T)* = last.Get(); var v_wrapped:Optional(Node(T)*) = Optional(Node(T)*).Create(heap.New(Node(T).Create(value))); (*last_value).set_next(v_wrapped); (*v_wrapped.Get()).set_prev(last); (*self).tail = v_wrapped; } fn PushFront[addr self: Self*](value:T) { (*self).len = (*self).len + 1; if(not (*self).head.HasValue()) { (*self).head = Optional(Node(T)*).Create(heap.New(Node(T).Create(value))); (*self).tail = (*self).head; return; } var v_wrapped:Optional(Node(T)*) = Optional(Node(T)*).Create(heap.New(Node(T).Create(value))); var current_head: Optional(Node(T)*) = (*self).head; var current_head_value: Node(T)* = current_head.Get(); (*v_wrapped.Get()).set_next(current_head); (*current_head_value).set_prev(v_wrapped); (*self).head = v_wrapped; } fn Clear[addr self: Self*]() { if((*self).len == 0) { return; } var iter: auto = (*self).head; while(iter.HasValue()) { var current: auto = iter; iter = (*iter.Get()).next; heap.Delete(current.Get()); } (*self).head = Optional(Node(T)*).CreateEmpty(); (*self).tail = Optional(Node(T)*).CreateEmpty(); (*self).len = 0; } fn Length[self: Self]() -> i32 { return self.len; } var head: Optional(Node(T)*); var tail: Optional(Node(T)*); var len: i32; } // This function exists as a helper the context of this test case specifically and does not represent // the common case for index based lookups in list like structures. fn GetListEntryByIndex[T:! type](list: LinkedList(T)*, target_index: i32) -> Optional(T) { let list_length: i32 = (*list).Length(); if(target_index > list_length - 1 or target_index < 0) { // Not in possible range return Optional(T).CreateEmpty(); } var search_backwards:bool = target_index > list_length / 2; var iter:Optional(Node(T)*) = if search_backwards then (*list).tail else (*list).head; var c:i32 = if search_backwards then list_length -1 else 0; while(c != target_index) { var node: Node(T) = *(iter.Get()); if(search_backwards) { c = c - 1; iter = node.prev; } else { iter = node.next; c = c + 1; } } return (*(iter.Get())).value; } fn Main() -> i32 { var test_list:auto = LinkedList(String).Create(); test_list.PushBack("This is a String Entry 0"); test_list.PushBack("This is a String Entry 1"); test_list.PushBack("This is a String Entry 2"); test_list.PushBack("This is a String Entry 3"); test_list.PushBack("This is a String Entry 4"); test_list.PushBack("This is a String Entry 5"); test_list.PushFront("This is a prepended String -1"); Print("String list length is: {0}", test_list.Length()); var search_index:i32 = 5; var retrieved_entry: auto = GetListEntryByIndex(&test_list,search_index); if(retrieved_entry.HasValue()) { var retrieved_value:String = retrieved_entry.Get(); Print(retrieved_value); } else { Print("No entry found in String list for index {0}!", search_index); } test_list.Clear(); var second_list: auto = LinkedList(i32*).Create(); var number: i32 = 1337; var number_ptr: i32* = &number; second_list.PushBack(number_ptr); var retrieved_ptr:i32* = GetListEntryByIndex(&second_list, 0).Get(); var value: i32 = *retrieved_ptr; Print("Value {0}", value); return 0; } ================================================ FILE: toolchain/check/fuzzer_corpus/283a045fd6c041aa47df273da411d601e7dada48 ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE // CHECK:STDOUT: result: 12 package ExplorerTest; fn Main() -> i32 { var cond: if true then bool else i32 = true; if (if cond then true else false) {} while (if cond then false else true) {} return if if cond then true or false else false and true then if not cond then 1 + 2 else 3 * 4 else if not cond then 5 + 6 else 7 * 8; } ================================================ FILE: toolchain/check/fuzzer_corpus/291711a108e2ed04586d978f1ac4a730277ffbb8 ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE // CHECK:STDOUT: result: 5 package ExplorerTest; class A { var n: i32; } impl A as ImplicitAs(i32) { fn Convert[self: Self]() -> i32 { return self.n; } } fn Main() -> i32 { var a: A = {.n = 5}; return a as i32; } ================================================ FILE: toolchain/check/fuzzer_corpus/2a9df05598f94d7fefa11dd1451237e8c7b99f60 ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE // CHECK:STDOUT: result: 2 package ExplorerTest; fn A(b: bool) -> i32 { match (b) { case false => { return 1; } case true => { return 2; } } } fn Main() -> i32 { return A(true); } ================================================ FILE: toolchain/check/fuzzer_corpus/2b504c78bc414daf63ba4618ebbd36bd92004d6e ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE // CHECK:STDOUT: Nice! // CHECK:STDOUT: result: 0 package ExplorerTest; fn Main() -> i32 { var i: i32 = Rand(0, 100); var j: i32 = Rand(0, 100); if (i == j) { Print("HALLO WELT"); } else { Print("Nice!"); } return 0; } ================================================ FILE: toolchain/check/fuzzer_corpus/2c05dbcf73a57af47793888b03384cdd43d5c594 ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE package ExplorerTest; fn Main() -> i32 { // CHECK:STDERR: RUNTIME ERROR: fail_div_by_zero.carbon:[[@LINE+1]]: division by zero var a: auto = 5 / 0; return 0; } ================================================ FILE: toolchain/check/fuzzer_corpus/2d316de15bbe6edbbbeb3bc65e9350a7b53e0abe ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE // CHECK:STDOUT: result: 2 package Foo; fn Main() -> i32 { return {.m = 1, .n = 2}.({.n: i32, .m: i32}.n); } ================================================ FILE: toolchain/check/fuzzer_corpus/2df8a8f5865c0e813ed26a874c2ea0daf7421086 ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE // CHECK:STDOUT: MemberF.F // CHECK:STDOUT: ImplF.(HasF.F) // CHECK:STDOUT: BothFs.(HasF.F) // CHECK:STDOUT: BothFs.F // CHECK:STDOUT: BothFs.F // CHECK:STDOUT: result: 0 package ExplorerTest; choice ImplKind { Checked, ConstrainedTemplate, UnconstrainedTemplate } interface CallF(K:! ImplKind) { fn DoIt[self: Self](); } interface HasF { fn F[self: Self](); } impl forall [T:! HasF] T as CallF(ImplKind.Checked) { fn DoIt[self: Self]() { self.F(); } } impl forall [template T:! HasF] T as CallF(ImplKind.ConstrainedTemplate) { fn DoIt[self: Self]() { self.F(); } } impl forall [template T:! type] T as CallF(ImplKind.UnconstrainedTemplate) { fn DoIt[self: Self]() { self.F(); } } class MemberF { fn F[self: Self]() { Print("MemberF.F"); } } class ImplF {} impl ImplF as HasF { fn F[self: Self]() { Print("ImplF.(HasF.F)"); } } class BothFs { fn F[self: Self]() { Print("BothFs.F"); } } impl BothFs as HasF { fn F[self: Self]() { Print("BothFs.(HasF.F)"); } } fn Main() -> i32 { var mem: MemberF = {}; var imp: ImplF = {}; var both: BothFs = {}; mem.(CallF(ImplKind.UnconstrainedTemplate).DoIt)(); imp.(CallF(ImplKind.Checked).DoIt)(); // TODO: Should be valid, but currently fails during instantiation. //imp.(CallF(ImplKind.ConstrainedTemplate).DoIt)(); both.(CallF(ImplKind.Checked).DoIt)(); // TODO: Should be rejected, but currently incorrectly accepted. // This line can be deleted once it starts failing; we test that this is // rejected in fail_name_lookup.carbon. both.(CallF(ImplKind.ConstrainedTemplate).DoIt)(); both.(CallF(ImplKind.UnconstrainedTemplate).DoIt)(); return 0; } ================================================ FILE: toolchain/check/fuzzer_corpus/2e02f9befd32eac2983de14b6ac8cf6cef960b4c ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE // CHECK:STDOUT: result: 0 package ExplorerTest; fn Id(t: type) -> auto { return t; } // Test non-trivial type expression in variable declaration statement. fn Main() -> i32 { var x: Id(i32) = 0; return x; } ================================================ FILE: toolchain/check/fuzzer_corpus/2e05abea6fd46fc2b5315dca60d3b56e5b407fd4 ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE package ExplorerTest; class Shape { var x: i32; } class Point { fn Origin() -> Point { return {.x = 0, .y = 0}; } fn GetSetX[addr self: Shape*](x: i32) -> i32 { var old: auto = (*self).x; (*self).x = x; return old; } var x: i32; var y: i32; } fn Main() -> i32 { var p: Point = Point.Origin(); // CHECK:STDERR: COMPILATION ERROR: fail_method_self_type.carbon:[[@LINE+3]]: type error in method access, receiver type // CHECK:STDERR: expected: class Shape // CHECK:STDERR: actual: class Point var x: auto = p.GetSetX(42); if (p.x == 42) { return x; } return 1; } ================================================ FILE: toolchain/check/fuzzer_corpus/2e1aea3071937c4307dd48ebb2b1df3bc9e8716f ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE // CHECK:STDOUT: result: 12 package Foo; interface A { fn F[self: Self]() -> i32; } interface B { fn G(o: Self) -> i32; } alias C = A & B; class X { extend impl as A { fn F[self: Self]() -> i32 { return 10 * self.n; } } extend impl as B { fn G(o: Self) -> i32 { return o.n; } } var n: i32; } fn Main() -> i32 { var v: X = {.n = 1}; var w: X = {.n = 2}; return v.(C.F)() + X.(C.G)(w); } ================================================ FILE: toolchain/check/fuzzer_corpus/300e45006b32eec72e90d33d3cd6685680448957 ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE impl package ExplorerTest; fn Main() -> i32 { // CHECK:STDERR: RUNTIME ERROR: fail_single_brace.carbon:[[@LINE+1]]: `{` must be followed by a second `{` or index in `{` Print("{"); return 0; } ================================================ FILE: toolchain/check/fuzzer_corpus/301dc6263f2a34b4630aa88e940f42acf020f160 ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE package ExplorerTest; fn Main() -> i32 { // CHECK:STDERR: SYNTAX ERROR: fail_raw_block_quotes_not_on_own_line.carbon:[[@LINE+1]]: Invalid block string: Should end with triple quotes: error: closing ''' var s: String = #''' error: closing '''# is not on its own line. '''#; return 0; } ================================================ FILE: toolchain/check/fuzzer_corpus/3037fd1afeda607e889fe3e73fb1088d13c9f8bf ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE // CHECK:STDOUT: result: 0 package ExplorerTest; // Check that even generic parameters are looked up in the namespace of an // entity. namespace N; interface N.Interface {} class N.A(T:! Interface) {} interface N.B(T:! Interface) {} constraint N.C(T:! Interface) {} __mixin N.D(T:! Interface) {} choice N.E(T:! Interface) {} fn N.F[T:! Interface](a: A(T)) {} fn Main() -> i32 { return 0; } ================================================ FILE: toolchain/check/fuzzer_corpus/306f80d197d3d627368e752f1ad6aa42d2e89aa6 ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE // CHECK:STDOUT: result: 0 package ExplorerTest; fn Main() -> i32 { var x: array(array(i32, 3), 2) = ((0, 1, 2), (3, 4, 5)); return x[1][2] - 5; } ================================================ FILE: toolchain/check/fuzzer_corpus/3082cf2a6045d00b6e81d3ae14a3a3d56314b988 ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE // CHECK:STDOUT: c.Foo() -> 1 // CHECK:STDOUT: c.Bar() -> 2 // CHECK:STDOUT: d.Foo() -> 3 // CHECK:STDOUT: d.Bar() -> 4 // CHECK:STDOUT: cc.Foo() -> 3 // CHECK:STDOUT: cc.Bar() -> 2 // CHECK:STDOUT: result: 0 package ExplorerTest; base class C { virtual fn Foo[self: Self]() -> i32 { return 1; } fn Bar[self: Self]() -> i32 { return 2; } } class D { extend base: C; impl fn Foo[self: Self]() -> i32 { return 3; } fn Bar[self: Self]() -> i32 { return 4; } } fn Main() -> i32 { var c: C = {}; Print("c.Foo() -> {0}", c.Foo()); Print("c.Bar() -> {0}", c.Bar()); var d: D = {.base = {}}; Print("d.Foo() -> {0}", d.Foo()); Print("d.Bar() -> {0}", d.Bar()); var cc: C* = &d; Print("cc.Foo() -> {0}", (*cc).Foo()); Print("cc.Bar() -> {0}", (*cc).Bar()); return 0; } ================================================ FILE: toolchain/check/fuzzer_corpus/310b6d61f64901b21169432ef6bbc13a2b602376 ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE // CHECK:STDOUT: result: 3 package ExplorerTest; fn Main() -> i32 { var t: auto = (1, 2); var a: array(i32, 2) = t; return a[0] + a[1]; } ================================================ FILE: toolchain/check/fuzzer_corpus/3116bbbb3bae537a09e62f5c8331a431d6c56b1e ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE // CHECK:STDOUT: Allocate B // CHECK:STDOUT: DESTRUCTOR B // CHECK:STDOUT: DESTRUCTOR A // CHECK:STDOUT: Delete B // CHECK:STDOUT: DESTRUCTOR B // CHECK:STDOUT: DESTRUCTOR A // CHECK:STDOUT: Return // CHECK:STDOUT: result: 0 package ExplorerTest; base class A{ destructor[self: Self] { Print("DESTRUCTOR A"); } } class B { extend base: A; fn Create() -> Self{ return {.base={}}; } destructor[self: Self] { Print("DESTRUCTOR B"); } } fn Main() -> i32 { Print("Allocate B"); var pb: B* = heap.New(B.Create()); Print("Delete B"); heap.Delete(pb); Print("Return"); return 0; } ================================================ FILE: toolchain/check/fuzzer_corpus/3119cacb5f47007660225c4a63a934eff83091a2 ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE // CHECK:STDOUT: result: 0 package ExplorerTest; interface Vector { fn Zero() -> Self; fn Add[self: Self](b: Self) -> Self; fn Scale[self: Self](v: i32) -> Self; } class Point(T:! type) { var x: T; var y: T; } impl Point(i32) as Vector { // Allowed: `Self` means `Point(i32)` here. fn Zero() -> Self { return {.x = 0, .y = 0}; } fn Add[self: Self](b: Self) -> Self { return {.x = self.x + b.x, .y = self.y + b.y}; } fn Scale[self: Self](v: i32) -> Self { return {.x = self.x * v, .y = self.y * v}; } } fn AddAndScaleGeneric[T:! Vector](a: T, s: i32) -> T { return a.Add(T.Zero()).Scale(s); } fn Main() -> i32 { var a: Point(i32) = {.x = 2, .y = 1}; var p: Point(i32) = AddAndScaleGeneric(a, 5); return p.x - 10; } ================================================ FILE: toolchain/check/fuzzer_corpus/317bdb0ba661c19d4be7dc1a9e0eb74529c80bba ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE // CHECK:STDOUT: result: 0 package ExplorerTest; fn Main() -> i32 { if (0 == 1) { return 1; } else if (0 == 0) { return 0; } return 1; } ================================================ FILE: toolchain/check/fuzzer_corpus/31e5d9b3712744f0659429e330ba44958eadce7c ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE impl package ExplorerTest; fn Main() -> i32 { // CHECK:STDERR: RUNTIME ERROR: fail_index_bounds1.carbon:[[@LINE+1]]: Index invalid with argument count of 1 at offset 12 in `Print: {0} {1}` Print("Print: {0} {1}", 1); return 0; } ================================================ FILE: toolchain/check/fuzzer_corpus/3292cacd15031b51dcfdd4d623e0ef30c66eb86b ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE package ExplorerTest; __mixin Operations { fn F[self: Self](x: Self) -> Self{ return x; } } class Point { fn Origin() -> Point { return {.x = 0, .y = 0}; } var x: i32; var y: i32; __mix Operations; } class Complex { fn Zero() -> Complex { return {.r = 0, .i = 0}; } var r: i32; var i: i32; } fn Main() -> i32 { var p: Point = Point.Origin(); var c: Complex = {.r = 42, .i = 1}; // CHECK:STDERR: COMPILATION ERROR: fail_self_substitution.carbon:[[@LINE+1]]: mismatch in non-deduced types, `class Complex` is not implicitly convertible to `class Point` var p1: Point = p.F(c); return p1.x - 42; } ================================================ FILE: toolchain/check/fuzzer_corpus/32ffc355b444c61ccd2d80ca466aa47b4f5b4d4b ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE // CHECK:STDOUT: result: 12 package ExplorerTest; interface Runnable { fn Run[self: Self]() -> i32; } interface Walkable { fn Walk[self: Self]() -> i32; } constraint Traversible { extend Runnable; extend Walkable; } impl i32 as Traversible { fn Run[self: i32]() -> i32 { return 10 * self; } fn Walk[self: i32]() -> i32 { return self + 1; } } fn Main() -> i32 { var n: i32 = 1; return n.(Runnable.Run)() + n.(Walkable.Walk)(); } ================================================ FILE: toolchain/check/fuzzer_corpus/33358007d39274e780a256e497695c3fe46f1029 ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE // CHECK:STDOUT: result: 1 package Foo; interface A { fn F[self: Self]() -> i32; } class X { extend impl as A { fn F[self: Self]() -> i32 { return 1; } } } fn Main() -> i32 { var a: X = {}; return a.(X.(A.F))(); } ================================================ FILE: toolchain/check/fuzzer_corpus/3476b8646b5b6b64fba9ab4b662850eff9312d41 ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE // CHECK:STDOUT: result: 0 package ExplorerTest; // Ensure that we can declare all different kinds of declarations as namespace // members. namespace N; __mixin N.Mixin {} base class N.BaseClass { __mix Mixin; } class N.DerivedClass { extend base: BaseClass; } namespace N.Inner; alias N.Inner.InnerClass = DerivedClass; choice N.Choice { A, B, C(DerivedClass) } interface N.Iface { fn F() -> DerivedClass*; } constraint N.Constraint { extend Iface; } var v: N.DerivedClass = {.base = {}}; fn N.F[T:! Constraint]() { var v: Inner.InnerClass = {.base = {}}; } impl i32 as N.Constraint { fn F() -> N.DerivedClass* { return &v; } } fn Main() -> i32 { return 0; } ================================================ FILE: toolchain/check/fuzzer_corpus/358f451f412939d0e0863704128cbc4756839499 ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE impl package EmptyIdentifier; fn A() { // CHECK:STDERR: RUNTIME ERROR: fail_function_recursion.carbon:[[@LINE+1]]: stack overflow: too many interpreter actions on stack A(); } fn Main() -> i32 { A(); return 0; } ================================================ FILE: toolchain/check/fuzzer_corpus/37934fb6e5e6d857d3227ea76872ce6a3bc4d630 ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE // CHECK:STDOUT: result: 0 package ExplorerTest; fn Main() -> i32 { if (0 == 1) { return 1; } else { return 0; } return 1; } ================================================ FILE: toolchain/check/fuzzer_corpus/37b6c00cc00d3ea562a9a67d365b1617be17cda4 ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE package ExplorerTest; fn AddInt(a: i32, b: i32) -> i32 { // CHECK:STDERR: COMPILATION ERROR: fail_returned_var_type_mismatch.carbon:[[@LINE+1]]: type of returned var `bool` does not match return type `i32` returned var ret: bool = true; return var; } fn Main() -> i32 { return AddInt(1, 2); } ================================================ FILE: toolchain/check/fuzzer_corpus/396c9121839a9d9866282f987fb283fa86556474 ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE // CHECK:STDOUT: XYZ: 1 // CHECK:STDOUT: XY: 1 // CHECK:STDOUT: XZ: 1 // CHECK:STDOUT: YZ: 2 // CHECK:STDOUT: JustX: 1 // CHECK:STDOUT: JustY: 2 // CHECK:STDOUT: JustZ: 3 // CHECK:STDOUT: None: 4 // CHECK:STDOUT: result: 0 package ExplorerTest; interface A { fn Which() -> i32; } interface X {} interface Y {} interface Z {} __match_first { impl forall [T:! X] T as A { fn Which() -> i32 { return 1; } } impl forall [T:! Y] T as A { fn Which() -> i32 { return 2; } } impl forall [T:! Z] T as A { fn Which() -> i32 { return 3; } } impl forall [T:! type] T as A { fn Which() -> i32 { return 4; } } } class XYZ {} class XY {} class XZ {} class YZ {} class JustX {} class JustY {} class JustZ {} class None {} impl XYZ as X & Y & Z {} impl XY as X & Y {} impl XZ as X & Z {} impl YZ as Y & Z {} impl JustX as X {} impl JustY as Y {} impl JustZ as Z {} fn Main() -> i32 { Print("XYZ: {0}", XYZ.(A.Which)()); Print("XY: {0}", XY.(A.Which)()); Print("XZ: {0}", XZ.(A.Which)()); Print("YZ: {0}", YZ.(A.Which)()); Print("JustX: {0}", JustX.(A.Which)()); Print("JustY: {0}", JustY.(A.Which)()); Print("JustZ: {0}", JustZ.(A.Which)()); Print("None: {0}", None.(A.Which)()); return 0; } ================================================ FILE: toolchain/check/fuzzer_corpus/3a414cdb79537ba6113eebe683a258098caebe9c ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE package ExplorerTest; class C { // CHECK:STDERR: COMPILATION ERROR: fail_member_of_self.carbon:[[@LINE+1]]: incomplete type `class C` used in type of variable var m: Self; } fn Main() -> i32 { return 0; } ================================================ FILE: toolchain/check/fuzzer_corpus/3abd6729fc8141ee2cf270432da6cfe0bc85c3b6 ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE // CHECK:STDOUT: result: 0 package ExplorerTest; interface Invalid {} fn Main() -> i32 { return 0; } ================================================ FILE: toolchain/check/fuzzer_corpus/3ade1694dc4a9b14c34f03ad62b68872d22dee82 ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE package ExplorerTest; fn f(x: i32, y: i32) -> i32 { return x + y; } fn Main() -> i32 { var xy: (i32, i32) = (1, 2); // should fail to type-check // CHECK:STDERR: COMPILATION ERROR: fail_call_with_tuple.carbon:[[@LINE+1]]: wrong number of arguments in function call, expected 2 but got 1 return f(xy); } ================================================ FILE: toolchain/check/fuzzer_corpus/3c57cd00b20c65032e36beeae83dbc34da90d96a ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE impl package EmptyIdentifier; fn Main() -> i32 { while (true) { // Ideally we would hit an OOM here, but it's too difficult to OOM from heap // allocations before hitting max steps. Maybe with string operations we // could trigger actual excessive memory allocations by just doubling the // size of the string each time. // CHECK:STDERR: RUNTIME ERROR: fail_allocate.carbon:[[@LINE+1]]: possible infinite loop: too many interpreter steps executed heap.New((0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0)); } return 0; } ================================================ FILE: toolchain/check/fuzzer_corpus/3cf84352b479800d6fc3cf96f8493945c2211b0f ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE package ExplorerTest; fn Main() -> i32 { // CHECK:STDERR: SYNTAX ERROR: fail_raw_block_more_hash_tags_on_left.carbon:[[@LINE+1]]: Unexpected end of file var s: String = ##''' error: there are more #s on the left than the right. '''#; return 0; } ================================================ FILE: toolchain/check/fuzzer_corpus/3d3543e880e8d01a5f3de62c8e5137fb6cddf741 ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE // CHECK:STDOUT: result: 4 package ExplorerTest; alias TypeAlias = i32; fn Main() -> TypeAlias { var n: TypeAlias = 1; var m: i32 = n; var p: i32* = &n; var q: TypeAlias* = &m; return *p + *q + m + n; } ================================================ FILE: toolchain/check/fuzzer_corpus/3e2b7a2777680a17f8ee4fdc72e6e2eeccb06bbe ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE // CHECK:STDOUT: result: 0 package ExplorerTest; __mixin Operations { fn Square[self: Self](x:i32) -> i32{ return x * x; } } class Point { var x: i32; var y: i32; fn DistanceSquare[self: Self](other:Self) -> i32 { return self.Square(self.x - other.x) + self.Square(self.y - other.y); } __mix Operations; } class Complex { var r: i32; var i: i32; __mix Operations; fn AbsSquare[self: Self]() -> i32 { return self.Square(self.r) + self.Square(self.i); } } fn Main() -> i32 { var p1: Point = {.x = 1, .y = 2 }; var p2: Point = {.x = 4, .y = 3 }; var c: Complex = {.r = 5, .i = 6 }; return c.AbsSquare() - p1.DistanceSquare(p2) - 51; } ================================================ FILE: toolchain/check/fuzzer_corpus/41de2a58a6891d68e779f2017ef063decc946cd4 ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE package ExplorerTest; class C { // CHECK:STDERR: SYNTAX ERROR: fail_method_self_misspelled.carbon:[[@LINE+1]]: illegal binding pattern in implicit parameter list fn F[addr slef: Self*]() {} } fn Main() -> i32 { return 0; } ================================================ FILE: toolchain/check/fuzzer_corpus/423c611625411a3e7e1a514f78b3f56d4482852f ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE // CHECK:STDOUT: result: 0 package ExplorerTest; class Point { fn Origin() -> Point { return {.x = 0, .y = 0}; } fn GetX[self: Point]() -> i32 { return self.x; } var x: i32; var y: i32; } fn Main() -> i32 { var p: Point = Point.Origin(); var f: __Fn() -> i32 = p.GetX; return f(); } ================================================ FILE: toolchain/check/fuzzer_corpus/4250d5daad936e112c495fba82c027d68b64d5af ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE impl package ExplorerTest; fn Main() -> i32 { // CHECK:STDERR: COMPILATION ERROR: fail_intrinsic_too_many_args.carbon:[[@LINE+1]]: Print takes 1 or 2 arguments, received 4 __intrinsic_print("", 1, 2, 3); } ================================================ FILE: toolchain/check/fuzzer_corpus/43403b1e7abd01b7c671e6defefd7531f5c6702e ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE // CHECK:STDERR: RUNTIME ERROR: {{.*}}/explorer/data/prelude.carbon:{{.*}}: "HALLO WELT" package ExplorerTest; fn Main() -> i32 { Assert(false, "HALLO WELT"); return 0; } ================================================ FILE: toolchain/check/fuzzer_corpus/4340b66cecbff7df2c6932cb03440a29e55bdfd3 ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE // CHECK:STDOUT: result: 0 package ExplorerTest; class Point { fn Origin() -> Point { return {.x = 0, .y = 0}; } var x: i32; var y: i32; } fn Main() -> i32 { var p: Point = Point.Origin(); return p.x; } ================================================ FILE: toolchain/check/fuzzer_corpus/4455db071205f7d5bdb9fc2fae623c64067b42d7 ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE package ExplorerTest; // Error: Can't use keyword `Self` as the name of a class. // CHECK:STDERR: SYNTAX ERROR: fail_class_named_self.carbon:[[@LINE+1]]: syntax error, unexpected SELF, expecting identifier or LEFT_PARENTHESIS class Self { var x: i32; var y: i32; } fn Main() -> i32 { var p: Self = {.x = 1, .y = 2}; return p.y - p.x - 1; } ================================================ FILE: toolchain/check/fuzzer_corpus/47afc3df9dd4fcdebc2c8a1ef7b0dc87435dc016 ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE package ExplorerTest; __mixin Mixin { // CHECK:STDERR: COMPILATION ERROR: fail_mix_invalid.carbon:[[@LINE+1]]: Not a valid mixin: `()` __mix (); } fn Main() -> i32 { return 0; } ================================================ FILE: toolchain/check/fuzzer_corpus/483ceff680765b8a2c582e18111fe71a754e1844 ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE // CHECK:STDOUT: result: 0 package ExplorerTest; interface Number { fn Zero() -> Self; fn Add[self: Self](other: Self) -> Self; } class Point(T:! Number) { fn Origin() -> Point(T) { return {.x = T.Zero(), .y = T.Zero()}; } var x: T; var y: T; } impl i32 as Number { fn Zero() -> i32 { return 0; } fn Add[self: i32](other: i32) -> i32 { return self + other; } } fn SumXY[U:! Number](p: Point(U)) -> U { return p.Origin().x.Add(p.y); } fn Main() -> i32 { var p: Point(i32) = {.x = 0, .y = 0}; return SumXY(p); } ================================================ FILE: toolchain/check/fuzzer_corpus/48b15c4b985c9b46b8e95e3c822ad1f65108ae39 ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE // CHECK:STDOUT: result: 5 package ExplorerTest; class A { var n: i32; fn Get[self: Self]() -> i32 { return self.n; } } fn Main() -> i32 { return ({.n = 5} as A).Get(); } ================================================ FILE: toolchain/check/fuzzer_corpus/48f161ebf1c515fbc92fc959814c40d6394b5360 ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE // CHECK:STDOUT: result: 0 package ExplorerTest; fn Main() -> i32 { var arr: array(i32, 2) = (0, 1); var x: [i32;] = arr; var index: i32 = 1; x[index] = 0; return x[0] + x[1]; } ================================================ FILE: toolchain/check/fuzzer_corpus/498eecf3c6bcc6332b955ae5c2260a5451e73add ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE // CHECK:STDOUT: T as Iface // CHECK:STDOUT: T as Iface // CHECK:STDOUT: A as Iface // CHECK:STDOUT: result: 0 package ExplorerTest; interface HasType { let AssocType:! type; } interface Iface { fn F(); } impl forall [T:! HasType where .Self.AssocType impls Iface] T as Iface { fn F() { Print("T as Iface"); T.AssocType.(Iface.F)(); } } class A { extend impl as Iface { fn F() { Print("A as Iface"); } } } class B { extend impl as HasType where .AssocType = A {} } class C { extend impl as HasType where .AssocType = B {} } fn Main() -> i32 { let c: C = {}; c.(Iface.F)(); return 0; } ================================================ FILE: toolchain/check/fuzzer_corpus/4a035921222f3bc29547b41d7ba8b6da0d6d1918 ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE // CHECK:STDOUT: result: 0 package ExplorerTest; fn Main() -> i32 { var s: String = #''' A "block" ""string"" literal with indent. '''#; if (s == #"A \#"block\#" \#"\#"string\#"\#" literal\#n with indent.\#n"#) { return 0; } else { return 1; } } ================================================ FILE: toolchain/check/fuzzer_corpus/4a2b48d7a92dd98fe609be64224be82772f55d0b ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE package ExplorerTest; fn Main() -> i32 { // CHECK:STDERR: COMPILATION ERROR: fail_unimplemented_example.carbon:[[@LINE+1]]: Unimplemented return 1 __unimplemented_example_infix 2; } ================================================ FILE: toolchain/check/fuzzer_corpus/4a53102a9f8003845552337ac998169e11d09d50 ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE // CHECK:STDOUT: result: 0 package ExplorerTest; fn Main() -> i32 { var x: i32 = 0; match (x) { case 1 => { return 1; } } return 0; } ================================================ FILE: toolchain/check/fuzzer_corpus/4ab72b4e37f3135ed58cbad67238c92072799757 ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE package ExplorerTest; // CHECK:STDERR: COMPILATION ERROR: fail_unknown_namespace.carbon:[[@LINE+1]]: name 'N' has not been declared in this scope fn N.F() {} fn Main() -> i32 { return 0; } ================================================ FILE: toolchain/check/fuzzer_corpus/4be506d3d29dbde616deda5b966e460b702d8078 ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE // CHECK:STDOUT: result: 0 package ExplorerTest; interface Interface {} impl {} as Interface {} impl {.x: i32} as Interface {} fn Main() -> i32 { return 0; } ================================================ FILE: toolchain/check/fuzzer_corpus/4c74c032df7c917af8e4bc833b777bb2bec80211 ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE package ExplorerTest; class Point { fn Origin() -> Point { return {.x = 0, .y = 0}; } var x: i32; var y: i32; } fn Main() -> i32 { // CHECK:STDERR: COMPILATION ERROR: fail_instantiate_non_generic.carbon:[[@LINE+1]]: in call `Point(i32)`, expected callee to be a function, found `type` var p: Point(i32) = Point.Origin(); return 0; } ================================================ FILE: toolchain/check/fuzzer_corpus/4ce8273ccba8f2b4cf0d8b721bf35b100e19e0b2 ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE // CHECK:STDOUT: 0: Heap{}, 1: C{} // CHECK:STDOUT: Initialize c from reference expression // CHECK:STDOUT: 0: Heap{}, 1: C{} // CHECK:STDOUT: c destroyed // CHECK:STDOUT: result: 0 package ExplorerTest; class C { destructor[self: Self] { Print("c destroyed"); } } fn FromReferenceExpression() { var c_var: C = {}; heap.PrintAllocs(); Print("Initialize c from reference expression"); let c: C = c_var; heap.PrintAllocs(); } fn Main() -> i32 { FromReferenceExpression(); return 0; } ================================================ FILE: toolchain/check/fuzzer_corpus/4d2449569a2536b234828155b7449ee755d60a1c ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE package ExplorerTest; fn Main() -> i32 { // CHECK:STDERR: SYNTAX ERROR: fail_hex_truncated.carbon:[[@LINE+1]]: Invalid escaping in string: "str\x" Print("str\x"); return 0; } ================================================ FILE: toolchain/check/fuzzer_corpus/4dd5200e6960e3538c180263762afa41dafd9e85 ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE package ExplorerTest; class A {} fn Main() -> i32 { var a: A = {}; var b: A* = &a; // CHECK:STDERR: COMPILATION ERROR: fail_invalid_ptr_conversion1.carbon:[[@LINE+1]]: type error in initializer of variable: 'class A*' is not implicitly convertible to 'i32' var c: i32 = b; return 1; } ================================================ FILE: toolchain/check/fuzzer_corpus/4f3bfc326a24c79bc61dd3f657879de3c4c92393 ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE package ExplorerTest; // CHECK:STDERR: COMPILATION ERROR: fail_invalid_fnty.carbon:[[@LINE+2]]: type error in `-`: // CHECK:STDERR: could not find implementation of interface Negate for bool fn f(g: __Fn(-true) -> true) { } fn Main() -> i32 { return 0; } ================================================ FILE: toolchain/check/fuzzer_corpus/50fa09b0616f12e3550c8d8db69371cc2909a299 ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE // CHECK:STDOUT: 6 // CHECK:STDOUT: 0 // CHECK:STDOUT: -1 // CHECK:STDOUT: result: 0 package ExplorerTest; class A { var n: i32; } impl A as BitXorWith(i32) where .Result = A { fn Op[self: Self](rhs: i32) -> A { return {.n = self.n ^ rhs}; } } fn Main() -> i32 { var a: A = {.n = 5}; a = a ^ 3; Print("{0}", a.n); a ^= 6; Print("{0}", a.n); Print("{0}", (a ^ -1).n); return 0; } ================================================ FILE: toolchain/check/fuzzer_corpus/51be544495cddd6b63fa15569d1f9c15da88d404 ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE // CHECK:STDOUT: result: 0 package ExplorerTest; fn Main() -> i32 { var s: String = #''' A block string literal \#'''# '''#; if (s == "A block string literal\n'''#\n") { return 0; } else { return 1; } } ================================================ FILE: toolchain/check/fuzzer_corpus/52aef5e56788c511815ca05612341c56cf96bc7c ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE // CHECK:STDOUT: result: 1 package ExplorerTest; interface ManyTypes { let T0:! type; let T1:! type; let T2:! type; let T3:! type; let T4:! type; let T5:! type; let T6:! type; let T7:! type; let T8:! type; let T9:! type; } fn F[ M:! ManyTypes where .T0 = .T1 and .T1 = .T2 and .T2 = .T3 and .T3 = .T4 and .T4 = .T5 and .T5 = .T6 and .T6 = .T7 and .T7 = .T8 and .T8 = .T9 and .T9 = i32](m: M) -> i32 { var v: M.T0 = 1; return v; } class C { extend impl as ManyTypes where .T0 = i32 and .T1 = .T0 and .T2 = .T1 and .T3 = .T2 and .T4 = .T3 and .T5 = .T4 and .T6 = .T5 and .T7 = .T6 and .T8 = .T7 and .T9 = .T8 {} } fn Main() -> i32 { var c: C = {}; return F(c); } ================================================ FILE: toolchain/check/fuzzer_corpus/547ffea2b538ce83c02ff5e112290b3c72f944dc ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE // CHECK:STDOUT: result: 0 package ExplorerTest; class Point(T:! type) { // Allowed: `Self` means `Point(T)` here. fn Origin(zero: T) -> Self { return {.x = zero, .y = zero}; } fn GetX[self: Self]() -> T { return self.x; } var x: T; var y: T; } fn Main() -> i32 { var p: Point(i32) = Point(i32).Origin(0); return p.GetX(); } ================================================ FILE: toolchain/check/fuzzer_corpus/54887e4d15dfaada3929936e0a970b82c1dfb62e ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE // CHECK:STDOUT: result: 0 package ExplorerTest; class S(T:! type) {} class Z {} class Tree(L:! type, R:! type) {} class Leaf {} interface Foo(T:! type) {} impl forall [U:! type, T:! Foo(Tree(U, U))] S(T) as Foo(U) {} alias Level1 = Tree(Leaf, Leaf); alias Level2 = Tree(Level1, Level1); alias Level3 = Tree(Level2, Level2); alias Level4 = Tree(Level3, Level3); alias Level5 = Tree(Level4, Level4); impl Z as Foo(Level5) {} fn F[T:! Foo(Leaf)](x: T) {} fn Main() -> i32 { var n: S(S(S(S(S(Z))))) = {}; F(n); return 0; } ================================================ FILE: toolchain/check/fuzzer_corpus/54dab9151ed3293f3aec67e58aa20e26aa632a98 ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE package ExplorerTest; fn Main() -> i32 { var v: array(i32, 2); // CHECK:STDERR: COMPILATION ERROR: fail_array.carbon:[[@LINE+1]]: use of uninitialized variable v return v[0]; } ================================================ FILE: toolchain/check/fuzzer_corpus/54e697b7fd97a4ca280483aac15d69a1fe995994 ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE package ExplorerTest; class C { fn F() {} fn G[self: Self]() {} } fn ReturnF() -> auto { return C.F; } // CHECK:STDERR: COMPILATION ERROR: fail_return_method.carbon:[[@LINE+1]]: member name G can only be used in a member access or alias fn ReturnG() -> auto { return C.G; } fn Main() -> i32 { return 0; } ================================================ FILE: toolchain/check/fuzzer_corpus/55bafb0b85e56d740867aa2eced4270cee6022ec ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE // CHECK:STDOUT: Initialize c from initializing expression (return ) // CHECK:STDOUT: Entering call // CHECK:STDOUT: 0: Heap{}, 1: !Uninit // CHECK:STDOUT: Object created, returning // CHECK:STDOUT: 0: Heap{}, 1: !Uninit, 2: C{} // CHECK:STDOUT: c destroyed // CHECK:STDOUT: 0: Heap{}, 1: C{}, 2: !!C{} // CHECK:STDOUT: c destroyed // CHECK:STDOUT: result: 0 package ExplorerTest; class C { destructor[self: Self] { Print("c destroyed"); } } fn CallWithReturnExpression() -> C { Print("Entering call"); heap.PrintAllocs(); var c: C = {}; Print("Object created, returning"); heap.PrintAllocs(); return c; } fn FromInitializingExpression_ReturnExpr() { Print("Initialize c from initializing expression (return )"); var c: C = CallWithReturnExpression(); heap.PrintAllocs(); } fn Main() -> i32 { FromInitializingExpression_ReturnExpr(); return 0; } ================================================ FILE: toolchain/check/fuzzer_corpus/55cffdb200c153f12a0b584d8f24754b6a5cdbca ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE // CHECK:STDOUT: result: 0 package ExplorerTest; fn Main() -> i32 { var x: auto = {.x = 10, .y = 1}; var p: i32* = &x.x; *p = 0; return x.x; } ================================================ FILE: toolchain/check/fuzzer_corpus/56aa3535abaf9c5df81b685d1eb0a71f5d80f43c ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE // CHECK:STDOUT: DESTRUCTOR A 1 // CHECK:STDOUT: DESTRUCTOR A 2 // CHECK:STDOUT: DESTRUCTOR A 3 // CHECK:STDOUT: DESTRUCTOR A 1 // CHECK:STDOUT: DESTRUCTOR A 2 // CHECK:STDOUT: DESTRUCTOR A 3 // CHECK:STDOUT: result: 1 package ExplorerTest; class A{ destructor[self: Self]{ Print("DESTRUCTOR A {0}",self.n); } var n: i32; } fn Main() -> i32 { var i: i32 = 0; while( i < 2){ var a: A = {.n = 3}; var b: A = {.n = 2}; var c: A = {.n = 1}; i = i + 1; } return 1; } ================================================ FILE: toolchain/check/fuzzer_corpus/5730670e62da8cb89476867288e21c654dcde6e5 ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE // CHECK:STDOUT: result: 0 package ExplorerTest; fn Main() -> i32 { var a: i32 = 1; let a_pinned: i32 = a; // OK: Value unused after being mutated. a = 2; return 0; } ================================================ FILE: toolchain/check/fuzzer_corpus/5aa8d527e2f0f75c4013ddde704d0f7f6afa4ad5 ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE // CHECK:STDOUT: result: 0 package ExplorerTest; fn CompareStr(s: String) -> i32 { if (s == ##"#"str"#"##) { return 0; } return 1; } fn Main() -> i32 { return CompareStr("#\"str\"#"); } ================================================ FILE: toolchain/check/fuzzer_corpus/5b0309128112c0a5ac3a2ffcd03a66d062224d48 ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE // CHECK:STDOUT: result: 0 package ExplorerTest; // Test a global variable depending on another global. var x: i32 = 0; var y: i32 = x; fn Main() -> i32 { return y; } ================================================ FILE: toolchain/check/fuzzer_corpus/5b19e864b0b73fde9f4ec0903239424fb6c036ca ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE package ExplorerTest; fn Main() -> i32 { var x: i32; // CHECK:STDERR: COMPILATION ERROR: fail_compound_assign.carbon:[[@LINE+1]]: use of uninitialized variable x x += 1; return x; } ================================================ FILE: toolchain/check/fuzzer_corpus/5b3a56d8084b2c87582aa9cb7f0ff56cfde0f0b5 ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE // CHECK:STDOUT: result: 3 package Foo; class X { fn F[self: Self](o: Self) -> Self { return {.n = self.n + o.n}; } var n: i32; } fn Main() -> i32 { return {.n = 1}.(X.F)({.n = 2}).n; } ================================================ FILE: toolchain/check/fuzzer_corpus/5bbdca0e72629685bfc1132d00e114960b2fcb82 ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE // CHECK:STDOUT: result: 0 package ExplorerTest; fn fst[T:! type](x: T, y: T) -> T { return x; } fn Main() -> i32 { return fst(0, 1); } ================================================ FILE: toolchain/check/fuzzer_corpus/5bd0c3c2901ec5b0052a7a9a76eb84cb611d1c42 ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE // CHECK:STDOUT: result: 7 package ExplorerTest; fn Main() -> i32 { return (1 + 2) + 4; } ================================================ FILE: toolchain/check/fuzzer_corpus/5d0c180874af1a76aaa137f32b3eecedb408ceef ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE // CHECK:STDOUT: result: 0 package ExplorerTest; fn Main() -> i32 { if (0 == 0) { if (0 == 1) { return 1; } else { return 0; } } return 1; } ================================================ FILE: toolchain/check/fuzzer_corpus/5d8390990020b1a44590ce1b13c484d1075dc50f ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE // CHECK:STDOUT: DESTRUCTOR A 1 // CHECK:STDOUT: DESTRUCTOR B 2 // CHECK:STDOUT: DESTRUCTOR A 3 // CHECK:STDOUT: DESTRUCTOR A 4 // CHECK:STDOUT: DESTRUCTOR A 5 // CHECK:STDOUT: result: 1 package ExplorerTest; class A{ destructor[self: Self]{ Print("DESTRUCTOR A {0}",self.n); } fn Create(x: i32) -> A{ return {.n = x}; } var n: i32; } class B{ destructor[self: Self]{ Print("DESTRUCTOR B {0}",self.n); } fn Create(x: i32) -> B{ return {.n = x, .a1 = A.Create(4),.a2 = A.Create(3) }; } var a1: A; var n: i32; var a2: A; } fn Main() -> i32 { var a: A = A.Create(5); var b: B = B.Create(2); var c: A = A.Create(1); return 1; } ================================================ FILE: toolchain/check/fuzzer_corpus/5da086526436d984a0e8b01ca482b178852084e5 ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE // CHECK:STDOUT: result: 3 package ExplorerTest; interface Frob { let Result:! type; fn F[self: Self]() -> Result; } fn Use[T:! Frob where .Result = .Self](x: T) -> T { var v: T = x.F(); return v; } impl i32 as Frob where .Result = i32 { fn F[self: Self]() -> i32 { return self + 1; } } fn Main() -> i32 { return Use(2); } ================================================ FILE: toolchain/check/fuzzer_corpus/5dce231f6950396fe0965788b6d8f24cf033b4b4 ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE // CHECK:STDOUT: result: 0 package ExplorerTest; fn Main() -> i32 { var x: auto = 2; while (x != 0) { x = x - 1; continue; x = x + 1; } return x; } ================================================ FILE: toolchain/check/fuzzer_corpus/5e45e4e1b479e4388e38277605992ce44cea1e57 ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE // CHECK:STDOUT: result: 0 package ExplorerTest; fn CompareStr(s: String) -> i32 { if (s == "str;\x07") { return 0; } return 1; } fn Main() -> i32 { return CompareStr("\x73\x74\x72\x3B\x07"); } ================================================ FILE: toolchain/check/fuzzer_corpus/5e968b8fb1b7fa51cea34638d04c2b0c75b3c92f ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE // CHECK:STDOUT: result: 0 package ExplorerTest; fn CompareStr(s: String) -> i32 { if (s == "HELLO WORLD!!") { return 0; } return 1; } fn Main() -> i32 { return CompareStr("\u{0048}\u{0045}\u{004C}\u{004C}\u{004F} \u{0057}\u{004F}\u{0052}\u{004C}\u{0044}\u{0021}\u{21}"); } ================================================ FILE: toolchain/check/fuzzer_corpus/5f89b222e699d47c8a41bb74abebc81f76578cf0 ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE // CHECK:STDOUT: result: 6 package ExplorerTest; namespace N; fn OuterI32() -> type { return i32; } fn One() -> i32 { return 1; } fn N.I32() -> type { return i32; } fn N.Five() -> I32() { return 5; } fn N.Six() -> OuterI32() { return Five() + One(); } fn Main() -> i32 { return N.Six(); } ================================================ FILE: toolchain/check/fuzzer_corpus/5f954e8ee1ace4e6a57d4defefc7d87c30deadbe ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE // CHECK:STDOUT: result: 4 package ExplorerTest; fn F[T:! ImplicitAs(type)](x: T) -> type { return x; } fn Main() -> i32 { var v: (i32, i32) as type = (1, 2); var w: F((i32, i32)) = (3, 4); v = w; return v[1]; } ================================================ FILE: toolchain/check/fuzzer_corpus/6003b3dcd82396edba816e537507a435839fe1f8 ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE // CHECK:STDOUT: result: 0 package ExplorerTest; fn F() { return; } fn Main() -> i32 { F(); return 0; } ================================================ FILE: toolchain/check/fuzzer_corpus/616631daa1b2f3fb44b606c331569f490f341778 ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE package ExplorerTest; fn Main() -> i32 { // CHECK:STDERR: SYNTAX ERROR: fail_octal.carbon:[[@LINE+1]]: Invalid escaping in string: "str\01" Print("str\01"); return 0; } ================================================ FILE: toolchain/check/fuzzer_corpus/631701015ee748a5c3d22fe34412f7b00c32a4af ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE // CHECK:STDOUT: result: 0 package ExplorerTest; fn Main() -> i32 { var a: [i32;] = (); return 0; } ================================================ FILE: toolchain/check/fuzzer_corpus/6485f068351f2f9eb9124bc4032d5fdad43fc611 ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE package ExplorerTest; fn Main() -> i32 { // CHECK:STDERR: SYNTAX ERROR: fail_raw_block_more_hash_tags_on_right.carbon:[[@LINE+1]]: invalid character '\x23' in source file. var s: String = #''' error: there are more #s on the right than the left. '''##; return 0; } ================================================ FILE: toolchain/check/fuzzer_corpus/64cabff382c905a7598bdb67fdc450e7d675a22e ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE // CHECK:STDOUT: result: 2 package ExplorerTest; interface Vector { let Dim:! i32; } class Point { var x: i32; var y: i32; extend impl as Vector where .Dim = 2 {} } fn Main() -> i32 { return Point.(Vector.Dim); } ================================================ FILE: toolchain/check/fuzzer_corpus/64d95aad6787b2388d89783f9f19de16ad3bf546 ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE // CHECK:STDOUT: result: 0 package ExplorerTest; fn Main() -> i32 { var x: i32 = 2; while (true) { if (x == 0) { break; } else { x = x - 1; } } return x; } ================================================ FILE: toolchain/check/fuzzer_corpus/66400bd8514c00f6cf25eff62d2e48445f57182c ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE package ExplorerTest; fn Main() -> i32 { // CHECK:STDERR: RUNTIME ERROR: fail_mod_by_zero.carbon:[[@LINE+1]]: division by zero var a: auto = 5 % 0; return 0; } ================================================ FILE: toolchain/check/fuzzer_corpus/6771c2bd2677e585863903ea55026479ccd17870 ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE package ExplorerTest; class Point { // CHECK:STDERR: COMPILATION ERROR: fail_qualified_method.carbon:[[@LINE+1]]: qualified declaration names are not permitted in this context fn Point.F[self: Self]() {} } fn Main() -> i32 { return 0; } ================================================ FILE: toolchain/check/fuzzer_corpus/67aaf0d5127285b5bc7f230c34abeb2f046cc18c ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // TODO: This SHOULD fail but doesn't presently. // // AUTOUPDATE // CHECK:STDOUT: result: 0 package ExplorerTest; import Nonexistent; fn Main() -> i32 { return 0; } ================================================ FILE: toolchain/check/fuzzer_corpus/68adfc912ab9c22031735ea064b9abfb759e860b ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE // CHECK:STDOUT: result: 0 package ExplorerTest; class Point { var x: i32; var y: i32; } fn Main() -> i32 { var p: Point = {.x = 1, .y = 2}; return p.y - p.x - 1; } ================================================ FILE: toolchain/check/fuzzer_corpus/68aebf293bd4ae573c514613325fcf490c8d98c4 ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE // CHECK:STDOUT: result: 3 package ExplorerTest; // The bodies of member functions are processed after all immediately enclosing // classes, impl declarations, and interfaces. class A { fn F[self: Self]() -> i32 { return G() + self.H(); } fn G() -> i32 { return 1; } fn H[self: Self]() -> i32 { return 2; } } fn Main() -> i32 { var a: A = {}; return a.F(); } ================================================ FILE: toolchain/check/fuzzer_corpus/69ae9f5b1bed179e5cccdfb4f7351a3fa7825d46 ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE // CHECK:STDOUT: result: 0 package ExplorerTest; // This tests the call-by-value aspect of parameter passing. // This makes sure that when the value in `x` dies, // it does not cause the value in `a` to also die. fn f(x: i32) -> i32 { return 0; } fn Main() -> i32 { var a: i32 = 0; var b: i32 = 1; f(a); b = a; return b; } ================================================ FILE: toolchain/check/fuzzer_corpus/69bab187ee3bed045d1af0a1325d5daabbfebcb7 ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE // CHECK:STDOUT: 1 // CHECK:STDOUT: i32.Hash // CHECK:STDOUT: 0 // CHECK:STDOUT: Potato.(Hashable.Hash) // CHECK:STDOUT: 1 // CHECK:STDOUT: Potato.Hash // CHECK:STDOUT: 2 // CHECK:STDOUT: Potato.Hash // CHECK:STDOUT: 2 // CHECK:STDOUT: result: 0 package ExplorerTest; interface Hashable { fn Hash[self: Self]() -> i32; } class Potato { impl as Hashable { fn Hash[self: Self]() -> i32 { Print("Potato.(Hashable.Hash)"); return 1; } } fn Hash[self: Self]() -> i32 { Print("Potato.Hash"); return 2; } } interface Maker { let Result:! Hashable; fn Make() -> Result; } impl i32 as Hashable { fn Hash[self: Self]() -> i32 { Print("i32.Hash"); return self; } } fn F[T:! Maker where .Result = i32](x: T) -> i32 { // OK, can treat T.Make() as an i32. return T.Make() + 1; } fn G[T:! Maker](x: T) -> i32 { // OK, Potato.(Hashable.Hash), not Potato.Hash. return T.Make().Hash(); } fn H[T:! Maker where .Result = Potato](x: T) -> i32 { // OK, Potato.Hash, not Potato.(Hashable.Hash). return T.Make().Hash(); } fn I[T:! Maker where .Result = Potato](x: T) -> i32 { var p: Potato = {}; // OK, Potato.Hash, not Potato.(Hashable.Hash), even though we know Potato is // Hashable here. return p.Hash(); } class IntFactory { extend impl as Maker where .Result = i32 { fn Make() -> i32 { return 0; } } } class PotatoFactory { extend impl as Maker where .Result = Potato { fn Make() -> Potato { return {}; } } } fn Main() -> i32 { var f: IntFactory = {}; var g: PotatoFactory = {}; Print("{0}", F(f)); Print("{0}", G(f)); Print("{0}", G(g)); Print("{0}", H(g)); Print("{0}", I(g)); return 0; } ================================================ FILE: toolchain/check/fuzzer_corpus/6b59783e9131166ae1396039351b8920c1532eda ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE // CHECK:STDOUT: result: 0 package ExplorerTest; fn Main() -> i32 { var (x: auto, y: auto) = (2, 3); return y - x - 1; } ================================================ FILE: toolchain/check/fuzzer_corpus/6bcc2a0c19cfffb661acb141bc57e351b31fe545 ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE // CHECK:STDOUT: result: 0 package ExplorerTest; class Point(T:! type) { fn Origin(zero: T) -> Point(T) { return {.x = zero, .y = zero}; } fn GetX[self: Point(T)]() -> T { return self.x; } var x: T; var y: T; } fn Main() -> i32 { var p: Point(i32) = Point(i32).Origin(0); return p.GetX(); } ================================================ FILE: toolchain/check/fuzzer_corpus/6c0ade4e7511d55f3594435cbab260c61c6eb433 ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE package ExplorerTest; __mixin M1 { fn F1[self: Self](x: Self) -> Self{ return x; } // CHECK:STDERR: COMPILATION ERROR: fail_recursive_mixing.carbon:[[@LINE+1]]: incomplete mixin `M1` used in mix declaration __mix M1; } class C { __mix M1; } fn Main() -> i32 { return 0; } ================================================ FILE: toolchain/check/fuzzer_corpus/6c4ef5a1d954eca2c4076a9ab38c85ed546d7d78 ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE // CHECK:STDOUT: result: 0 package ExplorerTest; fn Main() -> i32{ var x: Optional(String) = Optional(String).CreateEmpty(); if(x.HasValue()){ return -1; } return 0; } ================================================ FILE: toolchain/check/fuzzer_corpus/6c7a321e86789ab26b7bdb3e5709364a0d4cd686 ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE // CHECK:STDOUT: result: 5 package ExplorerTest; class Point(T:! type) { var x: T; var y: T; } fn GetX[T:! type](pt: Point(T)) -> T { return pt.x; } fn GetY(T:! type, pt: Point(T)) -> T { return pt.y; } fn Main() -> i32 { var p: Point(i32) = {.x = 1, .y = 2}; // TODO: Should `GetX({.x = 1, .y = 2})` work? See #1251. return GetX(p) + GetY(i32, {.x = 3, .y = 4}); } ================================================ FILE: toolchain/check/fuzzer_corpus/6d8712954da7301cf9eba612efb43217ff2c9c9d ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE package Foo; fn F[self: i32]() {} fn Main() -> i32 { // TODO: It's unclear whether this is valid per the current rules. See // https://github.com/carbon-language/carbon-lang/pull/1122 // CHECK:STDERR: COMPILATION ERROR: fail_qualified_non_member.carbon:[[@LINE+1]]: expected name of instance member or interface member in compound member access, found fn [self: i32]() -> () 42.(F)(); return 0; } ================================================ FILE: toolchain/check/fuzzer_corpus/6d9f91f6f40fb8ddf061ae76c5f2beaefe6c8bee ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE // CHECK:STDOUT: result: 0 package ExplorerTest; fn Foo() -> () { returned var x: () = (); return var; } fn Main() -> i32 { Foo(); return 0; } ================================================ FILE: toolchain/check/fuzzer_corpus/6e7b40cf5df2608cac7f3a034875623185812bdc ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE package ExplorerTest; interface Similar(T:! type) {} impl forall [T:! type] T as Similar(T) {} impl forall [T:! type] i32 as Similar(T) {} fn CheckSimilar[T:! type, U:! Similar(T)](a: T, b: U) {} fn Main() -> i32 { let n: i32 = 0; CheckSimilar(true, false); CheckSimilar(true, n); // CHECK:STDERR: COMPILATION ERROR: fail_param_interface_in_impl.carbon:[[@LINE+1]]: could not find implementation of interface Similar(T = i32) for bool CheckSimilar(n, false); return 0; } ================================================ FILE: toolchain/check/fuzzer_corpus/6ee7d990d6d84b254f8ff655a2a9061ff87df262 ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE // CHECK:STDOUT: result: 1 package ExplorerTest; var call_count: i32 = 0; fn ReturnTrue() -> bool { call_count = call_count + 1; return true; } fn Main() -> i32 { var result: bool = ReturnTrue() or ReturnTrue(); return if result then call_count else -1; } ================================================ FILE: toolchain/check/fuzzer_corpus/6f39466681bb52b19ab384450e0bbb67598fd2ce ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE // CHECK:STDOUT: result: 0 package ExplorerTest; interface I { fn F() -> i32; fn M[self: Self]() -> i32; } class A { var n: i32; extend impl as I { fn F() -> i32 { return 1; } fn M[self: Self]() -> i32 { return 2; } } fn G[self: Self]() -> i32 { return 3; } } impl i32 as I { fn F() -> i32 { return 4; } fn M[self: Self]() -> i32 { return 5; } } alias IF = I.F; alias IM = I.M; alias AIF = A.(IF); alias AIM = A.(IM); alias AG = A.G; alias i32IF = i32.(IF); alias i32IM = i32.(IM); fn Main() -> i32 { var a: A = {.n = 0}; if (A.(IF)() != 1) { return 1; } if (a.(IF)() != 1) { return 2; } if (a.(IM)() != 2) { return 3; } if (a.(A.(IM))() != 2) { return 4; } if (AIF() != 1) { return 5; } if (a.(AIM)() != 2) { return 6; } if (a.(AG)() != 3) { return 7; } if (i32.(IF)() != 4) { return 8; } if (0.(IF)() != 4) { return 9; } if (0.(IM)() != 5) { return 10; } if (0.(i32.(IM))() != 5) { return 11; } if (i32IF() != 4) { return 12; } if (0.(i32IM)() != 5) { return 13; } return 0; } ================================================ FILE: toolchain/check/fuzzer_corpus/6f97d913a4f39bd5e8501dec2cc7bcf87e23e9d0 ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE // CHECK:STDOUT: H 22 // CHECK:STDOUT: result: 0 package ExplorerTest; choice MyOptionalElement(T:! type) { None(), Element(T) } class MyOptional(T:! type){ fn CreateEmpty() -> MyOptional(T){ return { .element = MyOptionalElement(T).None() }; } fn Create ( value: T ) -> MyOptional(T){ return { .element = MyOptionalElement(T).Element(value) }; } fn has_value[self: Self] () -> bool{ var x: MyOptionalElement(T) = self.element; match(x){ case MyOptionalElement(T).None() => { return false; } } return false; } fn get[self: Self] () -> T { var x: MyOptionalElement(T) = self.element; match(x){ case MyOptionalElement(T).Element( var x: T ) =>{ return x; } } // TODO: Mark this as unreachable somehow. return get(); } var element: MyOptionalElement(T); } fn Main() -> i32 { var f: MyOptional(i32) = MyOptional(i32).Create(22); var x: i32 = f.get(); Print("H {0}",x); return 0; } ================================================ FILE: toolchain/check/fuzzer_corpus/70f5b6fb23691d532301ffb58ab24649f6454d8c ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE // CHECK:STDOUT: result: 0 package ExplorerTest; fn Main() -> i32 { var p: i32* = __intrinsic_new(0); var y: i32 = *p; __intrinsic_delete(p); return y; } ================================================ FILE: toolchain/check/fuzzer_corpus/710cfbe95b3fc5a9bb53fce89ef1a0abe2e09e85 ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE // CHECK:STDOUT: result: 0 package ExplorerTest; class Point { fn Origin() -> Point { return {.x = 0, .y = 0}; } fn GetSetX[addr self: Point*](x: i32) -> i32 { var old: auto = (*self).x; (*self).x = x; return old; } var x: i32; var y: i32; } fn Main() -> i32 { var p: Point = Point.Origin(); var x: auto = p.GetSetX(42); if (p.x == 42) { return x; } return 1; } ================================================ FILE: toolchain/check/fuzzer_corpus/71a53f6b5ecb6b4368eee4224d614609f4ff4d56 ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE package ExplorerTest; interface I { fn G[self: Self](); } class C { impl as I { // CHECK:STDERR: COMPILATION ERROR: fail_impl_missing_self.carbon:[[@LINE+3]]: type error in member of implementation // CHECK:STDERR: expected: fn [self: class C]() -> () // CHECK:STDERR: actual: fn () -> () fn G() { } } } fn Main() -> i32 { return 0; } ================================================ FILE: toolchain/check/fuzzer_corpus/723771f1826c803bbd50afe6e703268a89d6edab ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE // CHECK:STDERR: SYNTAX ERROR: fail_invalid_char.carbon:[[@LINE+1]]: invalid character '\xEF' in source file. � ================================================ FILE: toolchain/check/fuzzer_corpus/72a4e959879e3eaffd79dc30af15cb2ada073648 ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE // CHECK:STDOUT: result: 0 package ExplorerTest; interface Has(T:! type) { fn Get() -> T; } impl i32 as Has(type) { fn Get() -> type { return Self; } } class WithType(T:! Has(type)) { fn Get() -> type { return T.Get(); } } fn Main() -> i32 { var v: WithType(i32).Get() = 0; return v; } ================================================ FILE: toolchain/check/fuzzer_corpus/73235c4067e2da4fc1dbfe403e2354fd0598b639 ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE // CHECK:STDOUT: result: 0 package ExplorerTest; class Point { fn Origin() -> Point { return {.x = 0, .y = 0}; } var x: i32; var y: i32; } fn Main() -> i32 { var p: Point = Point.Origin(); return p.Origin().x; } ================================================ FILE: toolchain/check/fuzzer_corpus/7405fcca02b54d584c14af5493adc45467b2fdd9 ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE package ExplorerTest; class C { fn F() {} fn G[self: Self]() {} } fn H[T:! type](x: T) {} fn Main() -> i32 { H(C.F); // CHECK:STDERR: COMPILATION ERROR: fail_method_deduced.carbon:[[@LINE+1]]: member name G can only be used in a member access or alias H(C.G); return 0; } ================================================ FILE: toolchain/check/fuzzer_corpus/7471dc3d8630cf15ed61e384f95aeb80117b3e69 ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE // CHECK:STDOUT: result: 5 package ExplorerTest; interface A(T:! type) { let AResult:! type; } interface B(T:! type) { let BResult:! A(T); } interface I(T:! type) { let X:! B(T); // The constraints introduced by X should be in scope here. let Y:! X.BResult.AResult; } class CA { extend impl as A(i32) where .AResult = i32 {} } class CB { extend impl as B(i32) where .BResult = CA {} } class CI { extend impl as I(i32) where .X = CB and .Y = 5 {} } fn Main() -> i32 { var v: CI = {}; return v.(I(i32).Y); } ================================================ FILE: toolchain/check/fuzzer_corpus/760ef78e2194d159e3160c5f1ed9c2d14461da19 ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE // CHECK:STDOUT: result: 7 package ExplorerTest; alias TypeAlias = i32; fn Function(a: i32, b: TypeAlias) -> i32 { return a + b; } fn GenericFunction[T:! type](x: T) -> T { return x; } alias FunctionAlias = Function; alias GenericFunctionAlias = GenericFunction; fn Main() -> i32 { return FunctionAlias(1, 2) + GenericFunctionAlias(4); } ================================================ FILE: toolchain/check/fuzzer_corpus/78280fde00fc74d5293e1df394c1cf6012277f2c ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE // CHECK:STDOUT: result: 1 package ExplorerTest; var x: i32; fn Main() -> i32 { x = 1; return x; } ================================================ FILE: toolchain/check/fuzzer_corpus/79189aed1ece423113c07026aa1d7b2c9f79050c ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE package ExplorerTest; // TODO: Implement this with some kind of reflection? impl {.x: i32, .y: i32} as EqWith(Self) { fn Equal[self: Self](other: Self) -> bool { return self.x == other.x and self.y == other.y; } fn NotEqual[self: Self](other: Self) -> bool { return self.x != other.x or self.y != other.y; } } fn Main() -> i32 { var t1: {.x: i32, .y: i32} = {.x = 5, .y = 2}; var t2: {.x: i32,} = {.x = 5,}; // CHECK:STDERR: COMPILATION ERROR: fail_equality_type.carbon:[[@LINE+1]]: {.x: i32, .y: i32} is not equality comparable with {.x: i32} (could not find implementation of interface EqWith(U = {.x: i32}) for {.x: i32, .y: i32}) if (t1 == t2) { return 1; } else { return 0; } } ================================================ FILE: toolchain/check/fuzzer_corpus/79df4004383e0a52581c0b97ce9cf662e0032101 ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE package ExplorerTest; fn Main() -> i32 { // CHECK:STDERR: SYNTAX ERROR: fail_invalid_integer.carbon:[[@LINE+1]]: Invalid integer literal: 11111111111111111111111111 return 11111111111111111111111111; } ================================================ FILE: toolchain/check/fuzzer_corpus/7a700c8e06316b9d48f34dcbdd6c79c9ca739570 ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE // CHECK:STDOUT: result: 1 package ExplorerTest; fn map[T:! type](f: __Fn (T) -> T, tuple: (T, T)) -> (T, T) { return (f(tuple[0]), f(tuple[1])); } fn inc(x: i32) -> i32 { return x + 1; } fn Main() -> i32 { return map(inc, (0, 2))[0]; } ================================================ FILE: toolchain/check/fuzzer_corpus/7b1de3f5a9e1d40dbd7c4b342ec1da84cceb016f ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE package ExplorerTest; fn Main() -> i32 { // CHECK:STDERR: COMPILATION ERROR: fail_unexpected_tuple.carbon:[[@LINE+1]]: didn't expect a tuple var a: (auto,) = 1; return 0; } ================================================ FILE: toolchain/check/fuzzer_corpus/7c27cb6aeaf045bb89f59207280218ae5712357d ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE // CHECK:STDOUT: Initialize c1 from initializing expression (returned var) // CHECK:STDOUT: Before nested init // CHECK:STDOUT: 0: Heap{}, 1: !Uninit // CHECK:STDOUT: Nested call return // CHECK:STDOUT: 0: Heap{}, 1: C{} // CHECK:STDOUT: First call return // CHECK:STDOUT: 0: Heap{}, 1: C{} // CHECK:STDOUT: Declaration scope // CHECK:STDOUT: 0: Heap{}, 1: C{} // CHECK:STDOUT: c destroyed // CHECK:STDOUT: result: 0 package ExplorerTest; class C { destructor[self: Self] { Print("c destroyed"); } } fn CallWithReturnedVar2() -> C { Print("Before nested init"); heap.PrintAllocs(); returned var c: C = {}; Print("Nested call return"); heap.PrintAllocs(); return var; } fn CallWithReturnedVar() -> C { returned var c: C = CallWithReturnedVar2(); Print("First call return"); heap.PrintAllocs(); return var; } fn FromInitializingExpression_ReturnedVar() { Print("Initialize c1 from initializing expression (returned var)"); let c: C = CallWithReturnedVar(); Print("Declaration scope"); heap.PrintAllocs(); } fn Main() -> i32 { FromInitializingExpression_ReturnedVar(); return 0; } ================================================ FILE: toolchain/check/fuzzer_corpus/7c829002e321ac8fbc1b7bcc46f58f3032646bfb ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE package ExplorerTest; fn Main() -> i32 { // CHECK:STDERR: RUNTIME ERROR: fail_overflow_add.carbon:[[@LINE+1]]: integer overflow var a: auto = 2147483647 + 1; return 0; } ================================================ FILE: toolchain/check/fuzzer_corpus/7ca9fe97a14160b8620cee481aba4f12ef5e239f ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE // CHECK:STDOUT: result: 0 package ExplorerTest; fn F() { } fn Main() -> i32 { F(); return 0; } ================================================ FILE: toolchain/check/fuzzer_corpus/7cc77a9919de591289f6f01b7f3df2d2daa58297 ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE // CHECK:STDOUT: result: 0 package ExplorerTest; interface IFace {} impl {.IFace : (type where type == i32 and type == i32 and type == i32, )} as IFace { } fn Main() -> i32 { return 0; } ================================================ FILE: toolchain/check/fuzzer_corpus/7f5cf073e6e1bfe73c3256257aa83cffc3456ce5 ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE impl package ExplorerTest; fn Main() -> i32 { // CHECK:STDERR: COMPILATION ERROR: fail_intrinsic_int_format.carbon:[[@LINE+3]]: type error in Print argument 0 // CHECK:STDERR: expected: String // CHECK:STDERR: actual: i32 __intrinsic_print(0); } ================================================ FILE: toolchain/check/fuzzer_corpus/80e834464c299e6fa3fed5551d31712d4ae0adc2 ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE package ExplorerTest; fn F() -> () { // CHECK:STDERR: COMPILATION ERROR: fail_explicit_with_plain_return.carbon:[[@LINE+1]]: return; should provide a return value, to match the function's signature. return; } fn Main() -> i32 { F(); return 0; } ================================================ FILE: toolchain/check/fuzzer_corpus/8232c8a2839a7671df6947987108ae25e6027767 ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE // CHECK:STDOUT: result: 42 package ExplorerTest; class A { extend impl as ImplicitAs(i32) { fn Convert[self: Self]() -> i32 { return 42; } } } fn Main() -> i32 { var a: A = {}; return a; } ================================================ FILE: toolchain/check/fuzzer_corpus/82431aad891633c384f8e3dc8af548420cfc207a ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE // CHECK:STDOUT: result: 0 package ExplorerTest; // Never actually called, so this only tests typechecking and semantic analysis. fn F(n: i32, p: i32*, q: i32***) -> i32* { var a: i32 = n * *p; var b: i32 = a*n; *p = b*(*p); **q = p; return **q; } fn Main() -> i32 { return 0; } ================================================ FILE: toolchain/check/fuzzer_corpus/827994a1d2fbe88e785e28ac7fe3dc2d8043e8ad ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE // CHECK:STDOUT: result: 0 package ExplorerTest; class Point { // Allowed: `Self` here means `Point`. fn Origin() -> Self { return {.x = 0, .y = 0}; } var x: i32; var y: i32; } fn Main() -> i32 { var p: Point = Point.Origin(); return p.x; } ================================================ FILE: toolchain/check/fuzzer_corpus/8308e1750041583282a6c913cc4e2c4fb9f48353 ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE // CHECK:STDOUT: result: 0 package ExplorerTest; // Test empty parameters and return type fn f() { } fn Main() -> i32 { f(); return 0; } ================================================ FILE: toolchain/check/fuzzer_corpus/83941bf67b0cd2e4d3f7502b2bb40a83889bf78b ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE // CHECK:STDERR: SYNTAX ERROR: fail_missing.carbon:[[@LINE+1]]: syntax error, unexpected FN, expecting PACKAGE fn Main() -> i32 { return 0; } ================================================ FILE: toolchain/check/fuzzer_corpus/83e710c959e54010bceb9b0aad34ded746b6eb2a ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE // CHECK:STDOUT: result: 0 package ExplorerTest; // Test multiple arguments fn f(x: i32, y: i32) -> i32 { return x + y; } fn Main() -> i32 { return f(2, 3) - 5; } ================================================ FILE: toolchain/check/fuzzer_corpus/842268df9223d721e951b5865fb9d9c684129679 ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE // CHECK:STDOUT: result: 0 package ExplorerTest; fn Main() -> i32 { var x: i32 = 0; return x; } ================================================ FILE: toolchain/check/fuzzer_corpus/85017c3fdc47972d87932e4089f349f6c324ece9 ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE // CHECK:STDOUT: result: 3 package ExplorerTest; choice Opt { None(), Some(i32) } fn A(a: Opt, b: Opt) -> i32 { match ((a, b)) { case (Opt.Some(n: i32), _: auto) => { return n; } case (_: auto, Opt.Some(n: i32)) => { return n; } case (Opt.None(), Opt.None()) => { return 0; } } } fn B(a: Opt, b: Opt) -> i32 { match ((a, b)) { case (Opt.None(), _: auto) => { return 0; } case (Opt.Some(n: i32), Opt.None()) => { return n; } case (Opt.Some(n: i32), Opt.Some(m: i32)) => { return n + m; } } } fn Main() -> i32 { return A(Opt.None(), Opt.Some(1)) + B(Opt.Some(2), Opt.None()); } ================================================ FILE: toolchain/check/fuzzer_corpus/85cde7cf9e3e8e282a265f653b60cb0ef60b8b48 ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE package ExplorerTest; class Point { var x: i32; var y: i32; } fn Main() -> i32 { var p: Point; if (1 == 0) { p = {.x = 0, .y = 0}; } // CHECK:STDERR: RUNTIME ERROR: fail_store_to_uninitialized_class.carbon:[[@LINE+1]]: undefined behavior: store to subobject of uninitialized value Uninit> p.x = 1; p.y = 2; return p.x; } ================================================ FILE: toolchain/check/fuzzer_corpus/85f4667d9dee7230ec98fd7a1b1c7455a6a4ab5d ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE // CHECK:STDOUT: result: 0 package ExplorerTest; class B { var x: i32; fn GetSetX[addr self: Self*](x: i32) -> i32 { var oldX: auto = (*self).x; (*self).x = x; return oldX; } } class A { var b: B; } fn Main() -> i32 { var b: B = {.x = 0}; var a: A = {.b = b}; var x: auto = a.b.GetSetX(42); if (a.b.x == 42) { return x; } return 1; } ================================================ FILE: toolchain/check/fuzzer_corpus/874eddf3ad6e08849a2214088ee525df154883c6 ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE // CHECK:STDOUT: 3 // CHECK:STDOUT: 4 // CHECK:STDOUT: result: 0 package ExplorerTest; fn Main() -> i32 { var a: (i32, i32) = (1, 2); var p: (i32, i32)* = &a; a[0] = 3; Print("{0}", (*p)[0]); (*p)[1] = 4; Print("{0}", a[1]); return 0; } ================================================ FILE: toolchain/check/fuzzer_corpus/877257bc5b2b43345bd445518317b709596eac7c ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE // CHECK:STDOUT: 3 // CHECK:STDOUT: 7 // CHECK:STDOUT: 15 // CHECK:STDOUT: result: 0 package ExplorerTest; class A { var n: i32; } impl A as BitOrWith(i32) where .Result = A { fn Op[self: Self](rhs: i32) -> A { return {.n = self.n | rhs}; } } fn Main() -> i32 { var a: A = {.n = 1}; a = a | 2; Print("{0}", a.n); a |= 5; Print("{0}", a.n); Print("{0}", (a | 12).n); return 0; } ================================================ FILE: toolchain/check/fuzzer_corpus/88031d95585417463e2939d40ff53cc9c8a12221 ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE // CHECK:STDOUT: result: 1 package ExplorerTest; fn IntDiff(a: i32, b: i32) -> i32 { if (a == b) { returned var ret: i32 = a - b; return var; } else { returned var ret2: i32 = b - a; return var; } } fn Main() -> i32 { return IntDiff(1, 2); } ================================================ FILE: toolchain/check/fuzzer_corpus/89a6c64c03c3683482810e5362c2417be45ed07c ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE package ExplorerTest; fn Main() -> i32 { // CHECK:STDERR: RUNTIME ERROR: fail_overflow_multiply.carbon:[[@LINE+1]]: integer overflow var a: auto = 1000000000 * 1000000000; return 0; } ================================================ FILE: toolchain/check/fuzzer_corpus/89af2ff0da3a9c42a04a7b2b3bbc05755060273d ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE package ExplorerTest; interface Z { fn Zero() -> Self; } class Point { var x: i32; var y: i32; // `Self` not allowed after `extend impl`. // CHECK:STDERR: SYNTAX ERROR: fail_extend_with_self.carbon:[[@LINE+1]]: syntax error, unexpected SELF, expecting AS extend impl Self as Z { fn Zero() -> Self { return {.x = 0, .y = 0}; } } } fn Main() -> i32 { var a: Point = Point.Zero(); return p.x; } ================================================ FILE: toolchain/check/fuzzer_corpus/89fab5bae29d8a674f37cce24cbedeff198cfb5d ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE // CHECK:STDOUT: DESTRUCTOR A 1 // CHECK:STDOUT: DESTRUCTOR A 2 // CHECK:STDOUT: DESTRUCTOR A 3 // CHECK:STDOUT: DESTRUCTOR A 4 // CHECK:STDOUT: DESTRUCTOR A 5 // CHECK:STDOUT: DESTRUCTOR A 6 // CHECK:STDOUT: DESTRUCTOR A 7 // CHECK:STDOUT: DESTRUCTOR A 8 // CHECK:STDOUT: DESTRUCTOR A 6 // CHECK:STDOUT: DESTRUCTOR A 7 // CHECK:STDOUT: DESTRUCTOR A 8 // CHECK:STDOUT: result: 1 package ExplorerTest; class A{ destructor[self: Self]{ Print("DESTRUCTOR A {0}",self.n); } var n: i32; } fn Main() -> i32 { var i: i32 = 0; while(true){ var a: A = {.n = 4}; var b: A = {.n = 3}; var c: A = {.n = 2}; if(true){ var a: A = {.n = 1}; break; } } i = 0; while(i < 2 ){ var a: A = {.n = 8}; var b: A = {.n = 7}; var c: A = {.n = 6}; if(i == 0){ var a: A = {.n = 5}; i = i+1; continue; } i = i+1; } return 1; } ================================================ FILE: toolchain/check/fuzzer_corpus/8aed12f4aa27c3ac5d1b3703d202428777fe983b ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE impl package ExplorerTest; interface I {} // CHECK:STDERR: COMPILATION ERROR: fail_call_at_compile_time.carbon:[[@LINE+1]]: Print called before run time impl forall [T:! String] Print(T) as I {} fn Main() -> i32 { return 0; } ================================================ FILE: toolchain/check/fuzzer_corpus/8cbe098387a007f60eb4209259915d6d85532fa5 ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE // CHECK:STDOUT: result: 1 package ExplorerTest; interface A(T:! type) {} interface B { fn F() -> i32; } class C(T:! type) {} impl forall [T:! type] C(T) as A(T) & B { fn F() -> i32 { return 1; } } fn Main() -> i32 { var v: C(i32) = {}; return v.(B.F)(); } ================================================ FILE: toolchain/check/fuzzer_corpus/8e0b56d89ffc8caa0feaf38b36642aea4c1be601 ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE // CHECK:STDOUT: result: 0 package ExplorerTest; // TODO: Implement this with some kind of reflection? impl {.x: i32, .y: i32} as EqWith(Self) { fn Equal[self: Self](other: Self) -> bool { return self.x == other.x and self.y == other.y; } fn NotEqual[self: Self](other: Self) -> bool { return self.x != other.x or self.y != other.y; } } fn Main() -> i32 { var t1: {.x: i32, .y: i32} = {.x = 5, .y = 2}; var t2: {.x: i32, .y: i32} = {.x = 5, .y = 4}; if (t1 == t2) { return 1; } else { return 0; } } ================================================ FILE: toolchain/check/fuzzer_corpus/8f27934a9f00dec469d67e7336b7d8f52ec4abd6 ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE // CHECK:STDOUT: result: 6 package ExplorerTest; // The constraint here resolves to: // // MulWith(T) where .(MulWith(T).Result) == // // Note in particular that this involves a member of `MulWith(T)`, so we need // `T` to have a symbolic identity when checking its own type. fn DoMul[T:! MulWith(.Self) where .Result == .Self](x: T, y: T) -> T { return x * y; } fn Main() -> i32 { return DoMul(2, 3); } ================================================ FILE: toolchain/check/fuzzer_corpus/8fab0dc50d28280cdeab5def1ae75d70f860a1a7 ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE package ExplorerTest; abstract class B { } // CHECK:STDERR: COMPILATION ERROR: fail_instantiate_global_abstract.carbon:[[@LINE+1]]: Cannot instantiate abstract class B var b: B = {}; fn Main() -> i32 { return 0; } ================================================ FILE: toolchain/check/fuzzer_corpus/90ce2617c868946d7fa04649195ab1ab5fff4b20 ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE package ExplorerTest; fn Main() -> i32 { // CHECK:STDERR: SYNTAX ERROR: fail_block_quotes_not_on_own_line.carbon:[[@LINE+1]]: Invalid block string: Should end with triple quotes: error: closing ''' var s: String = ''' error: closing ''' is not on its own line. '''; return 0; } ================================================ FILE: toolchain/check/fuzzer_corpus/92904c2b09ee456d0d1a2cf3d69dfd26fd190641 ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE // CHECK:STDOUT: result: 0 package ExplorerTest; fn Main() -> i32 { var a: [i32;0] = (); var b: [i32;] = a; return 0; } ================================================ FILE: toolchain/check/fuzzer_corpus/92b290bf1d1d95e5a7e7813af2199781f92b350e ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE // CHECK:STDOUT: result: 1 package ExplorerTest; fn Main() -> i32 { returned var x: i32 = 1; if (true) { var x: i32 = 2; return var; } x = 3; return var; } ================================================ FILE: toolchain/check/fuzzer_corpus/92b34177d4245d4172436708ba988ac171eb9eb2 ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE // CHECK:STDOUT: result: 0 package ExplorerTest; interface Vector { fn Add[self: Self](b: Self) -> Self; fn Scale[self: Self](v: i32) -> Self; } class Point { var x: i32; var y: i32; extend impl as Vector { fn Add[self: Point](b: Point) -> Point { return {.x = self.x + b.x, .y = self.y + b.y}; } fn Scale[self: Point](v: i32) -> Point { return {.x = self.x * v, .y = self.y * v}; } } } fn ScaleGeneric[U:! Vector](c: U, s: i32) -> U { return c.Scale(s); } fn AddAndScaleGeneric[T:! Vector](a: T, b: T, s: i32) -> T { return ScaleGeneric(a.Add(b), s); } fn Main() -> i32 { var a: Point = {.x = 1, .y = 1}; var b: Point = {.x = 2, .y = 3}; var p: Point = AddAndScaleGeneric(a, b, 5); return p.x - 15; } ================================================ FILE: toolchain/check/fuzzer_corpus/932e6052bd882919514042cc953293cd63659b5e ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE package ExplorerTest; class Point(T:! type) { // CHECK:STDERR: COMPILATION ERROR: fail_two_arg_lists.carbon:[[@LINE+1]]: in call `Point(T)(T)`, expected callee to be a function, found `type` fn Origin(zero: T) -> Point(T)(T) { return {.x = zero, .y = zero}; } fn GetX[self: Point(T)(T)]() -> T { return self.x; } var x: T; var y: T; } fn Main() -> i32 { var p: Point(i32) = Point(i32).Origin(0); return p.GetX(); } ================================================ FILE: toolchain/check/fuzzer_corpus/94a3ce6126281beaf24a4595ac2f27a2c6e04b2d ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE // CHECK:STDOUT: result: 0 package ExplorerTest library "Foo"; fn Main() -> i32 { return 0; } ================================================ FILE: toolchain/check/fuzzer_corpus/964b65adb9c33d86539ec025f5cf745e1c4e61e1 ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE package ExplorerTest; // CHECK:STDERR: COMPILATION ERROR: fail_parameter_type_only.carbon:[[@LINE+1]]: An irrefutable pattern is required, but `i32` is refutable. fn f(x: i32, i32) {} fn Main() -> i32 { return 0; } ================================================ FILE: toolchain/check/fuzzer_corpus/96656d19f3e5b82f968f3b4282ba524a814595b3 ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE package ExplorerTest; // CHECK:STDERR: COMPILATION ERROR: fail_dot_self_not_in_scope.carbon:[[@LINE+1]]: could not resolve '.Self' fn Main() -> .Self { return 0; } ================================================ FILE: toolchain/check/fuzzer_corpus/968568fe7d320af2e8e6adfc30e3f81dd2f8818e ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE package ExplorerTest; fn Main() -> i32 { var x: i32; // CHECK:STDERR: COMPILATION ERROR: fail_increment.carbon:[[@LINE+1]]: use of uninitialized variable x ++x; return x; } ================================================ FILE: toolchain/check/fuzzer_corpus/9706cab087de61b00158e0ac6d755f1352589bdd ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE // CHECK:STDOUT: result: 0 package ExplorerTest; fn Main() -> i32 { // -2147483648 is unsupported as a literal, but we can do it with a decrement. var low: i32 = -2147483647; low -= 1; var high: i32 = 2147483647; Rand(low, high); return 0; } ================================================ FILE: toolchain/check/fuzzer_corpus/9745b84a597bf13955fb7d208e94f2795aeb7813 ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE // CHECK:STDOUT: 1 // CHECK:STDOUT: result: 0 package ExplorerTest; fn Foo(n: i32) { Print("{0}", n); } fn Main() -> i32 { var v: {.a: i32, .b: i32} = {.a = 1, .b = 2}; let a: i32 = v.a; v.b = 3; Foo(a); return 0; } ================================================ FILE: toolchain/check/fuzzer_corpus/9787ad2c566323a856a6225b91dbbfbf8e8d3ee8 ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE // CHECK:STDOUT: result: 5 package ExplorerTest; fn Main() -> i32 { let n: i32 = 5; return n as i32; } ================================================ FILE: toolchain/check/fuzzer_corpus/979f63c217bbde19dd385c49d577dc3090d54317 ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE // CHECK:STDOUT: result: 2 package ExplorerTest; interface HasAssoc { let Assoc:! type; } class X { impl as HasAssoc where .Assoc = i32 {} } alias WithoutRewrite = HasAssoc where .Assoc == i32; alias WithRewrite = HasAssoc where .Assoc = i32; fn G[T:! WithoutRewrite](x: T) -> i32 { // TODO: We should reject this once `==` isn't applied automatically. var a: T.(WithRewrite.Assoc) = 2; return a; } fn Main() -> i32 { var x: X = {}; return G(x); } ================================================ FILE: toolchain/check/fuzzer_corpus/98b45893e4f4c1b0a893d2562df5e8559555f223 ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE // CHECK:STDOUT: result: 2 package ExplorerTest; var call_count: i32 = 0; fn ReturnFalse() -> bool { call_count = call_count + 1; return false; } fn Main() -> i32 { var result: bool = ReturnFalse() or ReturnFalse(); return if not result then call_count else -1; } ================================================ FILE: toolchain/check/fuzzer_corpus/9909b29e2f33447fa30b3b261cc7c672740e1236 ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE // CHECK:STDOUT: result: 0 package ExplorerTest; fn Main() -> i32 { if (0 == 1) { return 1; } return 0; } ================================================ FILE: toolchain/check/fuzzer_corpus/9a3f71755e720ffdafd5d7112321c8796d46fc62 ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE // CHECK:STDOUT: result: 0 package ExplorerTest; fn identFn( x: i32 ) -> i32{ return x; } fn Main() -> i32 { var x : i32 = 7 % 2; if( x != 1){ return 1; } x = (3*3) % 3; if( x != 0){ return 2; } x = (4+4+4+4) % (2+1); if( x != 1){ return 3; } x = 15 % (identFn(2)+1); if( x != 0){ return 4; } x = -15 % 7; if( x != -1){ return 5; } x = 15 % -7; if( x != 1){ return 6; } x = -15 % -identFn(7); if( x != -1){ return 7; } return 0; } ================================================ FILE: toolchain/check/fuzzer_corpus/9af28460c915ac87c79b91ff233959dc031cf3cc ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE // CHECK:STDOUT: result: 0 package ExplorerTest; fn Id(t: type) -> auto { return t; } // Test non-trivial type expression in parameter type. fn f(x: Id(i32)) -> i32 { return x; } fn Main() -> i32 { return f(0); } ================================================ FILE: toolchain/check/fuzzer_corpus/9bfc6a4a473aa74403e38910c8843a87cbee0dd9 ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE // CHECK:STDOUT: 6 // CHECK:STDOUT: 5 // CHECK:STDOUT: 6 // CHECK:STDOUT: 5 // CHECK:STDOUT: 6 // CHECK:STDOUT: 5 // CHECK:STDOUT: 6 // CHECK:STDOUT: 5 // CHECK:STDOUT: result: 0 package ExplorerTest; class A { var n: i32; } impl A as Inc { fn Op[addr self: Self*]() { ++self->n; } } impl A as Dec { fn Op[addr self: Self*]() { --self->n; } } fn Main() -> i32 { var a: A = {.n = 5}; ++a.n; Print("{0}", a.n); --a.n; Print("{0}", a.n); ++a; Print("{0}", a.n); --a; Print("{0}", a.n); a.n.(Inc.Op)(); Print("{0}", a.n); a.n.(Dec.Op)(); Print("{0}", a.n); a.(Inc.Op)(); Print("{0}", a.n); a.(Dec.Op)(); Print("{0}", a.n); return 0; } ================================================ FILE: toolchain/check/fuzzer_corpus/9cadde2867b9fe7867d7d217139982632f640cc0 ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE package ExplorerTest; interface FrobWith(T:! type) {} fn F[T:! FrobWith(.Self), // CHECK:STDERR: COMPILATION ERROR: fail_dot_self_after_scope_2.carbon:[[@LINE+1]]: `.Self` used in type of non-type generic binding `U` U:! .Self]() { } fn Main() -> i32 { return 0; } ================================================ FILE: toolchain/check/fuzzer_corpus/9f27297beea402d07125cf1bb0c7081ceb401bb5 ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE // CHECK:STDOUT: bool: 1 // CHECK:STDOUT: bool: 1 // CHECK:STDOUT: bool: 1 // CHECK:STDOUT: bool: 1 // CHECK:STDOUT: bool: 1 // CHECK:STDOUT: bool: 1 // CHECK:STDOUT: bool: 1 // CHECK:STDOUT: bool: 1 // CHECK:STDOUT: string: 1 // CHECK:STDOUT: string: 1 // CHECK:STDOUT: string: 1 // CHECK:STDOUT: string: 1 // CHECK:STDOUT: string: 1 // CHECK:STDOUT: string: 1 // CHECK:STDOUT: string: 1 // CHECK:STDOUT: string: 1 // CHECK:STDOUT: int: 1 // CHECK:STDOUT: int: 1 // CHECK:STDOUT: int: 1 // CHECK:STDOUT: int: 1 // CHECK:STDOUT: int: 1 // CHECK:STDOUT: int: 1 // CHECK:STDOUT: int: 1 // CHECK:STDOUT: int: 1 // CHECK:STDOUT: result: 0 package ExplorerTest; fn CompareEqualValues[T:! EqWith(.Self)](format: String, a: T, b: T) { Print(format, if a == b then 1 else 0); Print(format, if a != b then 0 else 1); } fn CompareDifferentValues[U:! EqWith(.Self)](format: String, a: U, b: U) { Print(format, if a == b then 0 else 1); Print(format, if a != b then 1 else 0); } fn CompareAll[V:! EqWith(.Self)](format: String, a: V, b: V) { CompareEqualValues(format, a, a); CompareEqualValues(format, b, b); CompareDifferentValues(format, a, b); CompareDifferentValues(format, b, a); } fn Main() -> i32 { CompareAll("bool: {0}", false, true); CompareAll("string: {0}", "hello", "world"); CompareAll("int: {0}", 1, 2); return 0; } ================================================ FILE: toolchain/check/fuzzer_corpus/a01f53c98465a54636523de8f520a4a20bdd1403 ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE // CHECK:STDOUT: Hallo Welt // CHECK:STDOUT: result: 0 package ExplorerTest; fn Main() -> i32{ var x: Optional(String) = Optional(String).Create( "Hallo Welt" ); if(x.HasValue()){ Print(x.Get()); return 0; } return -1; } ================================================ FILE: toolchain/check/fuzzer_corpus/a091cfa270edff9d7192eeb8f594ea77b09bba3d ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE // CHECK:STDOUT: result: 0 package ExplorerTest; interface AddMul { fn Add[self: Self](o: Self) -> Self; fn Mul[self: Self](o: Self) -> Self; } impl i32 as AddMul { fn Add[self: i32](o: i32) -> i32 { return self + o; } fn Mul[self: i32](o: i32) -> i32 { return self * o; } } class Holder(T:! AddMul) { var v: T; } interface Vector(Scalar:! AddMul) { fn Zero() -> Self; fn Add[self: Self](b: Self) -> Self; fn Scale[self: Self](v: Scalar) -> Self; fn Hold[self: Self](v: Scalar) -> Holder(Scalar); } class Point { var x: i32; var y: i32; extend impl as Vector(i32) { fn Zero() -> Point { return {.x = 0, .y = 0}; } fn Add[self: Point](b: Point) -> Point { return {.x = self.x + b.x, .y = self.y + b.y}; } fn Scale[self: Point](v: i32) -> Point { return {.x = self.x * v, .y = self.y * v}; } fn Hold[self: Point](v: i32) -> Holder(i32) { return {.v = v}; } } } fn AddAndScaleGeneric[T:! AddMul, U:! Vector(T)](a: U, s: T) -> U { return a.Add(U.Zero()).Scale(a.Hold(s).v); } fn Main() -> i32 { var a: Point = {.x = 2, .y = 1}; var p: Point = AddAndScaleGeneric(a, 5); return p.x - 10; } ================================================ FILE: toolchain/check/fuzzer_corpus/a0c798f9f52432ff1f72f8aa1ba9f7830a105f09 ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE // CHECK:STDOUT: 0: Heap{} // CHECK:STDOUT: result: 0 package ExplorerTest; fn Main() -> i32 { heap.PrintAllocs(); return 0; } ================================================ FILE: toolchain/check/fuzzer_corpus/a13c6779bdbc457a609f9667267beed711896f6f ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE package ExplorerTest; fn CompareStr(s: String) -> i32 { // CHECK:STDERR: SYNTAX ERROR: fail_raw_more_hash_tags_on_left.carbon:[[@LINE+2]]: missing closing quote in single-line string: ##"str"#) { // CHECK:STDERR: if (s == ##"str"#) { return 0; } return 1; } fn Main() -> i32 { return CompareStr("str"); } ================================================ FILE: toolchain/check/fuzzer_corpus/a1786976927e1d3184a978427bc5bb1cfa7aefa4 ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE // CHECK:STDOUT: result: 0 package ExplorerTest; fn CompareStr(s: String) -> i32 { if (s == "str\n") { return 0; } return 1; } fn Main() -> i32 { return CompareStr("str\n"); } ================================================ FILE: toolchain/check/fuzzer_corpus/a1bba96a1c0e17301f64b08c70a3df48170bd730 ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE // CHECK:STDOUT: result: 1 package ExplorerTest; class A { var n: i32; } impl i32 as ImplicitAs(A) { fn Convert[self: Self]() -> A { return {.n = self }; } } fn Main() -> i32 { var a: A = {.n = 0}; a = 1; return a.n; } ================================================ FILE: toolchain/check/fuzzer_corpus/a1f193dcf4589332a9130b6551c1afa7fe79656a ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE package ExplorerTest; fn Main() -> i32 { var (x: i32, y: i32); // CHECK:STDERR: COMPILATION ERROR: fail_while_cond.carbon:[[@LINE+1]]: use of uninitialized variable x while (x == 0) { y = 1; } return 1; } ================================================ FILE: toolchain/check/fuzzer_corpus/a2cfb003a0e59b4e421af3880c551363b8ff78f0 ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE // CHECK:STDOUT: Struct OK // CHECK:STDOUT: Choice OK // CHECK:STDOUT: Class OK // CHECK:STDOUT: Interface OK // CHECK:STDOUT: Constraint OK // CHECK:STDOUT: result: 0 package Foo; choice Choice { Alternative() } class Class { fn F(n: i32) -> i32 { return n + 1; } } interface Interface { fn G[self: Self]() -> Self; } interface AnotherInterface {} impl i32 as Interface { fn G[self: i32]() -> i32 { return self + 1; } } impl i32 as AnotherInterface {} // TODO: These are intended to be called at compile time. Mark them as // constexpr once we have syntax for that. fn GetStruct() -> type { return {.n: i32}; } fn GetChoice() -> type { return Choice; } fn GetClass() -> type { return Class; } fn GetInterface() -> type { return Interface; } fn GetConstraint() -> type { return Interface & AnotherInterface; } fn TestStruct() { var s: GetStruct() = {.n = 1}; if (s.(GetStruct().n) == 1) { Print("Struct OK"); } } fn TestChoice() { var c: GetChoice() = GetChoice().Alternative(); match (c) { case GetChoice().Alternative() => { Print("Choice OK"); } } } fn TestClass() { if (GetClass().F(1) == 2) { Print("Class OK"); } } fn TestInterface() { var n: i32 = 1; if (n.(GetInterface().G)() == 2) { Print("Interface OK"); } } fn TestConstraint() { var n: i32 = 1; if (n.(GetConstraint().G)() == 2) { Print("Constraint OK"); } } fn Main() -> i32 { TestStruct(); TestChoice(); TestClass(); TestInterface(); TestConstraint(); return 0; } ================================================ FILE: toolchain/check/fuzzer_corpus/a2de12dcf9c2df7a3ac23b82f802b3d076e6ad86 ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE package ExplorerTest; // CHECK:STDERR: COMPILATION ERROR: fail_not_type.carbon:[[@LINE+1]]: type error in type expression: 'i32' is not implicitly convertible to 'type' fn F[a:! 42](); fn Main() -> i32 { return 0; } ================================================ FILE: toolchain/check/fuzzer_corpus/a3cfd48c1f4a33aad9d1552ddbe46d44a2436384 ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE package ExplorerTest; // The bodies of member functions are processed after all immediately enclosing // classes, impl declarations, and interfaces. class A { fn F() -> type { return i32; } // OK, resolves to `A.F`. fn G() -> F() { return 0; } // CHECK:STDERR: COMPILATION ERROR: fail_class_fn_use_before_declaration.carbon:[[@LINE+1]]: 'I' has not been declared yet fn H() -> I() { return 0; } fn I() -> type { return i32; } } fn Main() -> i32 { return 0; } ================================================ FILE: toolchain/check/fuzzer_corpus/a6bf2f116b1e98f7546e9bce3b9d2946d8074aa2 ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE package ExplorerTest; fn Main() -> i32 { // CHECK:STDERR: SYNTAX ERROR: fail_newline.carbon:[[@LINE+2]]: missing closing quote in single-line string: "new // CHECK:STDERR: Print("new line"); return 0; } ================================================ FILE: toolchain/check/fuzzer_corpus/a780174f6f7b59148dbb023be4e3659a92541020 ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE // CHECK:STDOUT: 0: Heap{}, 1: C{} // CHECK:STDOUT: Bind from c value expression // CHECK:STDOUT: Binding scope end // CHECK:STDOUT: c destroyed // CHECK:STDOUT: 0: Heap{}, 1: C{}, 2: !!C{} // CHECK:STDOUT: c destroyed // CHECK:STDOUT: result: 0 package ExplorerTest; class C { destructor[self: Self] { Print("c destroyed"); } } fn CallWithReferenceExpressionBinding(var c: C) { Print("Binding scope end"); } fn Main() -> i32 { let c_let: C = {}; heap.PrintAllocs(); Print("Bind from c value expression"); CallWithReferenceExpressionBinding(c_let); heap.PrintAllocs(); return 0; } ================================================ FILE: toolchain/check/fuzzer_corpus/a8a90bb993589759a112cf567143c47be5a89ac4 ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE // CHECK:STDOUT: result: 0 package ExplorerTest; fn Main() -> i32 { var x: [i32;] = (0, 1); var index: i32 = 1; x[index] = 0; return x[0] + x[1]; } ================================================ FILE: toolchain/check/fuzzer_corpus/a8a999063a872e7de8a55734e0f4a198efeeaafb ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE // CHECK:STDOUT: 1 // CHECK:STDOUT: 2 // CHECK:STDOUT: 3 // CHECK:STDOUT: 4 // CHECK:STDOUT: 1 // CHECK:STDOUT: 2 // CHECK:STDOUT: 3 // CHECK:STDOUT: 4 // CHECK:STDOUT: result: 0 package ExplorerTest; class ListElement{ fn Create(value: i32)->ListElement{ return { .element = Optional(i32).Create(value), .next = Optional(ListElement*).CreateEmpty() }; } fn set[addr self: Self*] (value: Optional(ListElement*)){ (*self).next = value; } var element: Optional(i32); var next: Optional(ListElement*); } class List{ fn Create() -> List{ return {.next = Optional(ListElement*).CreateEmpty() }; } fn insert[addr self: Self*] (value: i32){ if( not (*self).next.HasValue() ){ (*self).next = Optional(ListElement*).Create( heap.New(ListElement.Create(value)) ); return; } var iter: Optional(ListElement*) = (*self).next; var node: auto = (iter.Get()); while((*node).next.HasValue()){ iter = (*node).next; node = (iter.Get()); } var v : ListElement* = iter.Get(); var x: Optional(ListElement*) = Optional(ListElement*).Create( heap.New(ListElement.Create(value) )); (*v).set(x); } fn print[self: Self] (){ var iter: Optional(ListElement*) = self.next; while(iter.HasValue()){ var node: auto = *(iter.Get()); Print("{0}",node.element.Get()); var node_ptr: auto = iter.Get(); iter = node.next; } } fn clean[addr self: Self*] (){ var head: auto = (*self).next; while(head.HasValue()){ var tmp: auto = head; head = (*head.Get()).next; heap.Delete(tmp.Get()); } (*self).next= Optional(ListElement*).CreateEmpty(); } var next: Optional(ListElement*); } fn Main() -> i32{ var list: List = List.Create(); list.insert(1); list.insert(2); list.insert(3); list.insert(4); list.print(); list.clean(); list.print(); list.insert(1); list.insert(2); list.insert(3); list.insert(4); list.print(); return 0; } ================================================ FILE: toolchain/check/fuzzer_corpus/a93b46ffd68cc67330744e0ed1099a14bdb56b47 ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE // CHECK:STDOUT: 1 // CHECK:STDOUT: 2 // CHECK:STDOUT: 3 // CHECK:STDOUT: 4 // CHECK:STDOUT: 4 // CHECK:STDOUT: 6 // CHECK:STDOUT: 5 // CHECK:STDOUT: 6 // CHECK:STDOUT: result: 0 package ExplorerTest; class A(T:! type, U:! type) {} interface B(T:! type, U:! type) { fn F(); } impl forall [T:! type] A(T, i32) as B(i32, i32) { fn F() { Print("1"); } } impl forall [T:! type] A(i32, T) as B(i32, i32) { fn F() { Print("2"); } } // Intentionally out of order so that explorer can't get the right answer by // chance, by just picking the first or last matching impl. impl forall [T:! type] A(i32, i32) as B(i32, T) { fn F() { Print("4"); } } impl forall [T:! type] A(i32, i32) as B(T, i32) { fn F() { Print("3"); } } impl forall [T:! type, U:! type] A(T, i32*) as B(i32*, U) { fn F() { Print("5"); } } impl forall [T:! type, U:! type] A(i32*, T) as B(U, i32*) { fn F() { Print("6"); } } fn Main() -> i32 { A((), i32).(B(i32, i32).F)(); A(i32, ()).(B(i32, i32).F)(); A(i32, i32).(B((), i32).F)(); A(i32, i32).(B(i32, ()).F)(); A(i32, i32).(B(i32, i32).F)(); A(i32*, i32*).(B(i32, i32*).F)(); A(i32*, i32*).(B(i32*, i32).F)(); A(i32*, i32*).(B(i32*, i32*).F)(); return 0; } ================================================ FILE: toolchain/check/fuzzer_corpus/a9c4f7786b6b8ba23fa102e8ba2296752120ae84 ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE // CHECK:STDOUT: result: 3 package Foo; interface A { fn F[self: Self](o: Self) -> Self; } class X { extend impl as A { fn F[self: Self](o: Self) -> Self { return {.n = self.n + o.n}; } } var n: i32; } fn F[T:! A](v: T, w: T) -> T { return v.(T.(A.F))(w); } fn Main() -> i32 { var v: X = {.n = 1}; var w: X = {.n = 2}; return F(v, w).n; } ================================================ FILE: toolchain/check/fuzzer_corpus/ab1d10a814f9068e09642a678671d76511342dd4 ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE // CHECK:STDOUT: result: 0 package ExplorerTest; class A { class B {} alias C = B; } fn Main() -> i32 { return 0; } ================================================ FILE: toolchain/check/fuzzer_corpus/ab97f0b738451b9e490d7cadaacc8081ae676240 ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE // CHECK:STDOUT: A(T) as B(i32) // CHECK:STDOUT: A(T) as B(U) // CHECK:STDOUT: A(i32) as B(T) // CHECK:STDOUT: A(i32) as B(T) // CHECK:STDOUT: result: 0 package ExplorerTest; class A(T:! type) {} interface B(T:! type) { fn F(); } impl forall [T:! type] A(T) as B(i32) { fn F() { Print("A(T) as B(i32)"); } } impl forall [T:! type] A(i32) as B(T) { fn F() { Print("A(i32) as B(T)"); } } impl forall [T:! type, U:! type] A(T) as B(U) { fn F() { Print("A(T) as B(U)"); } } fn F[T:! B(i32)](x: T) { T.F(); } fn G[T:! B(bool)](x: T) { T.F(); } fn Main() -> i32 { let a: A(bool) = {}; let b: A(i32) = {}; F(a); G(a); F(b); G(b); return 0; } ================================================ FILE: toolchain/check/fuzzer_corpus/ac4a09688df096b7967d347059fe514b011a0c5e ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE // CHECK:STDOUT: result: 5 package ExplorerTest; interface HasTypeAndValue { let T:! type; let V:! T; } fn F(X:! HasTypeAndValue where .T = i32 and .V = 5) -> i32 { return X.V; } impl i32 as HasTypeAndValue where .T = i32 and .V = 5 {} fn Main() -> i32 { return F(i32); } ================================================ FILE: toolchain/check/fuzzer_corpus/ad153a95127f656da230d82cdba152a50f871556 ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE // CHECK:STDOUT: result: 0 package ExplorerTest; interface Vector { fn Add[self: Self](b: Self) -> Self; fn Scale[self: Self](v: i32) -> Self; } class Point { var x: i32; var y: i32; extend impl as Vector { fn Add[self: Point](b: Point) -> Point { return {.x = self.x + b.x, .y = self.y + b.y}; } fn Scale[self: Point](v: i32) -> Point { return {.x = self.x * v, .y = self.y * v}; } } } fn AddAndScaleGeneric[T:! Vector](t: (T, T), s: i32) -> T { var m: auto = t[0].Add; var n: auto = m(t[1]).Scale; return n(s); } fn Main() -> i32 { var a: Point = {.x = 1, .y = 1}; var b: Point = {.x = 2, .y = 3}; var p: Point = AddAndScaleGeneric((a, b), 5); return p.x - 15; } ================================================ FILE: toolchain/check/fuzzer_corpus/aedf34fefa255ebc431491de0b68156a724221ae ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE // CHECK:STDOUT: Carrot.G // CHECK:STDOUT: result: 5 package ExplorerTest; interface Apple(T:! type) { fn F[self: Self]() -> T; } interface Banana { require Self impls Apple(i32); fn G[self: Self](); } class Carrot { var n: i32; } impl Carrot as Apple(i32) { fn F[self: Self]() -> i32 { return self.n; } } impl Carrot as Banana { fn G[self: Self]() { Print("Carrot.G"); } } fn H[T:! Banana](x: T) -> i32 { x.G(); return x.(Apple(i32).F)(); } fn Main() -> i32 { var c: Carrot = {.n = 5}; return H(c); } ================================================ FILE: toolchain/check/fuzzer_corpus/afd20d50d83e001bbd7f6db02ca1e2e0a6d1f309 ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE // CHECK:STDOUT: result: 0 package ExplorerTest; // Demonstrate the aliasing behavior of pointers. fn Main() -> i32 { var p: i32* = heap.New(5); var q: i32* = p; *q = 0; return *p; } ================================================ FILE: toolchain/check/fuzzer_corpus/b08132223d41827e2b8cd8f849ffb7ac4af4e993 ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE // CHECK:STDOUT: result: 20 package ExplorerTest; fn Main() -> i32 { var ar: array(i32, 4) = (0, 1,2,3); var count : i32 = 0; for( x: i32 in ar){ count = count +1; for( x: i32 in ar){ count = count +1; } } return count; } ================================================ FILE: toolchain/check/fuzzer_corpus/b0a0c7b28dfac0b65bba3cf74ebd1100adf7c0b7 ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE // CHECK:STDOUT: result: 4213 package ExplorerTest; interface IntLike { fn Convert[self: Self]() -> i32; } impl i32 as IntLike { fn Convert[self: i32]() -> i32 { return self; } } fn add[T:! IntLike](x: {.a: T, .b: ({.m: i32, .n: T}, i32)}) -> i32 { return 1000 * x.a.Convert() + 100 * x.b[0].m + 10 * x.b[0].n.Convert() + x.b[1]; } fn Main() -> i32 { return add({.b = ({.n = 1, .m = 2}, 3), .a = 4}); } ================================================ FILE: toolchain/check/fuzzer_corpus/b0e276497e7036c6b3c90deeff71a055256ed5e3 ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE // CHECK:STDOUT: result: 0 package ExplorerTest; fn Main() -> i32 { var ar: array(i32, 0) = () ; var count : i32 = 0; for( x: i32 in ar ){ count = 2; } return count; } ================================================ FILE: toolchain/check/fuzzer_corpus/b1f68671cb99a64e7c35d8453fd1f13759823e49 ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE package ExplorerTest; fn Main() -> i32 { // CHECK:STDERR: RUNTIME ERROR: fail_overflow_div.carbon:[[@LINE+1]]: integer overflow var a: auto = (-2147483647 -1) /-1; return 0; } ================================================ FILE: toolchain/check/fuzzer_corpus/b3738109fea51776656084b4904eb6161501f39e ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE package ExplorerTest; fn Main() -> i32 { // CHECK:STDERR: SYNTAX ERROR: fail_hex_lower.carbon:[[@LINE+1]]: Invalid escaping in string: "str\xaa" Print("str\xaa"); return 0; } ================================================ FILE: toolchain/check/fuzzer_corpus/b39d93d15f5f9693f621391de119e616687fbfa3 ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE // CHECK:STDOUT: result: 0 package ExplorerTest; fn Main() -> i32 { Assert(true, "HALLO WELT"); return 0; } ================================================ FILE: toolchain/check/fuzzer_corpus/b3e6b065765987b63bb992656e202cd2753d579d ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE // CHECK:STDOUT: result: 0 package ExplorerTest; fn F[T:! type & type](x: T) {} fn Main() -> i32 { F(0); return 0; } ================================================ FILE: toolchain/check/fuzzer_corpus/b46338342e410d950a63cae90807b22ad2674468 ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE // CHECK:STDOUT: result: 0 package ExplorerTest; fn Main() -> i32 { var t2: {.x: i32, .y: i32} = {.x = 2, .y = 5}; t2.y = 3; // 3 - 2 - 1 return t2.y - t2.x - 1; } ================================================ FILE: toolchain/check/fuzzer_corpus/b4a3cec4308215fd5e3c772c7f3ce019c9c5b026 ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE // CHECK:STDERR: RUNTIME ERROR: {{.*}}/explorer/data/prelude.carbon:{{.*}}: Integer overflow package ExplorerTest; fn Main() -> i32 { var a: auto = 5 >> 75; return 0; } ================================================ FILE: toolchain/check/fuzzer_corpus/b4e1923164eed688e64d926007d332b4d9a7bafb ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE // CHECK:STDOUT: result: 0 package ExplorerTest; fn F() -> () { return (); } fn Main() -> i32 { F(); return 0; } ================================================ FILE: toolchain/check/fuzzer_corpus/b5025c7140bdccbefb9823d5906815f7aea5f555 ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE // CHECK:STDOUT: 75 // CHECK:STDOUT: 100 // CHECK:STDOUT: result: 0 package ExplorerTest; interface Vector { fn Zero() -> Self; fn Add[addr self: Self*](b: Self); fn Scale[addr self: Self*](v: i32); } class Point { var x: i32; var y: i32; extend impl as Vector { fn Zero() -> Self { return {.x = 1, .y = 1}; } fn Add[addr self: Self*](b: Self) { (*self).x = (*self).x + b.x; (*self).y = (*self).y + b.y; } fn Scale[addr self: Self*](v: i32) { (*self).x = (*self).x * v; (*self).y = (*self).y * v; } } } fn AddAndScaleGeneric[T:! Vector](p: T*, s: i32) { (*p).Add(T.Zero()); (*p).(Vector.Scale)(s); (*p).(T.(Vector.Scale))(s); } fn Main() -> i32 { var a: Point = {.x = 2, .y = 3}; AddAndScaleGeneric(&a, 5); Print("{0}", a.x); Print("{0}", a.y); return 0; } ================================================ FILE: toolchain/check/fuzzer_corpus/b585c712c89adab1146766f73010d48b0a9d9de5 ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE // CHECK:STDERR: RUNTIME ERROR: {{.*}}/explorer/data/prelude.carbon:{{.*}}: "Attempted to unwrap empty Optional" package ExplorerTest; fn Main() -> i32{ var x: Optional(i32) = Optional(i32).CreateEmpty(); return x.Get(); } ================================================ FILE: toolchain/check/fuzzer_corpus/b6b53ceb779903ab3b483786d48390423bbbe797 ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE package ExplorerTest; class C {} // CHECK:STDERR: COMPILATION ERROR: fail_combine_rhs.carbon:[[@LINE+1]]: expected a constraint in second operand of `&`, found class C fn F[T:! type & C](); fn Main() -> i32 { return 0; } ================================================ FILE: toolchain/check/fuzzer_corpus/b8695514d6502d5b1b9e09cc61711898dbe1e486 ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE // CHECK:STDOUT: result: 3 package ExplorerTest; interface HasAssoc { let Assoc:! type; } class X { impl as HasAssoc where .Assoc = i32 {} } fn H[T:! HasAssoc where .Assoc = i32, U:! type where .Self == i32](a: T, b: U) -> i32 { var a: T.((HasAssoc where .Assoc = U).Assoc) = 3; return a; } fn Main() -> i32 { var x: X = {}; var y: i32 = 0; return H(x, y); } ================================================ FILE: toolchain/check/fuzzer_corpus/b9d290495c4dce17305b5d9d29e2ae54c6242b54 ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE // CHECK:STDOUT: Class 1 // CHECK:STDOUT: Class 2 // CHECK:STDOUT: result: 0 package ExplorerTest; base class C { fn BasePrint(v: i32) { Print("Class {0}", v); } fn Method1[self: Self]() { self.BasePrint(self.value_c); } var value_c: i32; } class D { extend base: C; fn Method2[self: Self]() { self.BasePrint(self.value_d); } var value_d: i32; } fn Main() -> i32 { var d: D = {.base = {.value_c = 1}, .value_d = 2}; d.Method1(); d.Method2(); return 0; } ================================================ FILE: toolchain/check/fuzzer_corpus/ba4fa6565defd5cc4b8921ed0d478be330350658 ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE package ExplorerTest; class Point(T:! type) { var x: T; var y: T; } fn Main() -> i32 { var p: Point(i32) = {.x = 0, .y = 0}; // CHECK:STDERR: COMPILATION ERROR: fail_point_equal.carbon:[[@LINE+1]]: type error in initializer of variable: 'class Point(T = i32)' is not implicitly convertible to 'class Point(T = bool)' var q: Point(bool) = p; return 0; } ================================================ FILE: toolchain/check/fuzzer_corpus/ba5b59911968d39433b9003d02e3ff4fa7b4b177 ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE package ExplorerTest; fn Main() -> i32 { // CHECK:STDERR: SYNTAX ERROR: fail_tab.carbon:[[@LINE+1]]: Invalid escaping in string: "new line" Print("new line"); return 0; } ================================================ FILE: toolchain/check/fuzzer_corpus/bab137edbdcbcf1220dc018b55d49066378523e9 ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE package ExplorerTest; // This declaration is not allowed. // CHECK:STDERR: COMPILATION ERROR: fail_separate_decl.carbon:[[@LINE+1]]: Function declaration has deduced return type but no body fn ComputeSum(x: i32, y: i32) -> auto; fn Main() -> i32 { return ComputeSum(1, 2) - 3; } ================================================ FILE: toolchain/check/fuzzer_corpus/bb0ee9a7c7f604c41cfef744a974c6603df4b0db ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE package ExplorerTest; fn Main() -> i32 { var x: array(i32, 2) = (0, 1); // CHECK:STDERR: RUNTIME ERROR: fail_index.carbon:[[@LINE+1]]: index 2 out of range in (0, 1) return x[2]; } ================================================ FILE: toolchain/check/fuzzer_corpus/bc8de3dd156c3193c1a5209c7023499abe9bbdde ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE // CHECK:STDOUT: (*c1).val: 1 // CHECK:STDOUT: (*c2).val: 1 // CHECK:STDOUT: (*d).val: 2 // CHECK:STDOUT: e.val: 3 // CHECK:STDOUT: result: 0 package ExplorerTest; base class C { var val: i32; } base class D { extend base: C; var val: i32; } class E { extend base: D; var val: i32; } fn Foo(c: C*) -> i32 { return (*c).val; } fn Main() -> i32 { var e: E = { .val = 3, .base = {.val = 2,.base = {.val = 1}}}; var d: D* = &e; var c1: C* = &e; var c2: C* = d; Print("(*c1).val: {0}", (*c1).val); Print("(*c2).val: {0}", (*c2).val); Print("(*d).val: {0}", (*d).val); Print("e.val: {0}", e.val); return 0; } ================================================ FILE: toolchain/check/fuzzer_corpus/bd4d126e58ef4f15642eeba1d41a830e217a4ad1 ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE package ExplorerTest; fn Main() -> i32 { // CHECK:STDERR: COMPILATION ERROR: fail_length_mismatch_with_auto.carbon:[[@LINE+1]]: tuples of different length var a: (auto,) = (1, 2); return 0; } ================================================ FILE: toolchain/check/fuzzer_corpus/bd5f439085bdf3ab0cbe796b3637cf9139fb1914 ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE // CHECK:STDOUT: result: 12345 package ExplorerTest; class Class { var a: i32; } class GenericClass(T:! type) { var a: T; } interface Interface { fn Make() -> Self; } interface GenericInterface(T:! type) { fn Make() -> (Self, T); } alias ClassAlias = Class; alias GenericClassAlias = GenericClass; alias ClassSpecializationAlias = GenericClassAlias(i32); alias InterfaceAlias = Interface; alias GenericInterfaceAlias = GenericInterface; alias InterfaceSpecializationAlias = GenericInterfaceAlias(i32); impl ClassAlias as InterfaceAlias { fn Make() -> Self { return {.a = 1}; } } impl ClassSpecializationAlias as InterfaceSpecializationAlias { fn Make() -> (Self, i32) { return ({.a = 2}, 3); } } alias S = {.a: i32}; impl GenericClassAlias(S) as GenericInterfaceAlias(S) { fn Make() -> (Self, {.a: i32}) { return ({.a = {.a = 4}}, {.a = 5}); } } fn CheckImplementsInterface[T:! Interface](x: T) -> T { return T.Make(); } fn CheckImplementsGenericInterface_i32 [T:! GenericInterface(i32)](x: T) -> (T, i32) { return T.Make(); } fn CheckImplementsGenericInterface_struct [T:! GenericInterface(S)](x: T) -> (T, S) { return T.Make(); } fn Main() -> i32 { var a: Class = {.a = 0}; a = CheckImplementsInterface(a); var b: GenericClass(i32) = {.a = 0}; var (b2: GenericClass(i32), n: i32) = CheckImplementsGenericInterface_i32(b); var c: GenericClass({.a: i32}) = {.a = {.a = 0}}; var (c2: GenericClass({.a: i32}), s: S) = CheckImplementsGenericInterface_struct(c); return 10000 * a.a + 1000 * b2.a + 100 * n + 10 * c2.a.a + s.a; } ================================================ FILE: toolchain/check/fuzzer_corpus/bee65bde4d7da5558b60a5dacb8a5826486b1df2 ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE package ExplorerTest; interface FrobWith(T:! type) {} fn F[T:! FrobWith(.Self)] // CHECK:STDERR: COMPILATION ERROR: fail_dot_self_after_scope.carbon:[[@LINE+1]]: could not resolve '.Self' (U: .Self) { } fn Main() -> i32 { return 0; } ================================================ FILE: toolchain/check/fuzzer_corpus/bff4337d7117fd0883b58ac02469d740648a8072 ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE // CHECK:STDOUT: 8 // CHECK:STDOUT: 4 // CHECK:STDOUT: result: 0 package ExplorerTest; fn Main() -> i32 { if (1 << 0 != 1) { return 1; } if (1 << 3 != 8) { return 2; } if (0 << 3 != 0) { return 3; } if (3 << 1 != 6) { return 4; } if (-1 << 2 != -4) { return 5; } if (1 >> 0 != 1) { return 6; } if (1 >> 1 != 0) { return 7; } if (3 >> 1 != 1) { return 8; } if (-1 >> 1 != -1) { return 9; } if (-2 >> 1 != -1) { return 10; } var n: i32 = 1; n <<= 3; Print("{0}", n); n >>= 1; Print("{0}", n); return 0; } ================================================ FILE: toolchain/check/fuzzer_corpus/c07f23f6980fe961e62413e101aa21f1696e3e41 ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE // CHECK:STDOUT: Before: 1 // CHECK:STDOUT: Interface: 2 // CHECK:STDOUT: Op: 3 // CHECK:STDOUT: result: 0 package ExplorerTest; class C { var n: i32; } fn Main() -> i32 { var c: C = {.n = 1}; Print("Before: {0}", c.n); c.(AssignWith({.n: i32}).Op)({.n = 2}); Print("Interface: {0}", c.n); c = {.n = 3}; Print("Op: {0}", c.n); return 0; } ================================================ FILE: toolchain/check/fuzzer_corpus/c294ac41a73adf8be9ed88cbc20458d7fad20f68 ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE // CHECK:STDOUT: result: 0 package ExplorerTest; interface HasTypes { let A:! type; let B:! type; let C:! type; let D:! type; let E:! type; let F:! type; } interface HasParams(A:! type, B:! type, C:! type, D:! type) { let V:! HasTypes; } // Here we discover that there is a rewrite for `HasParams(...).V` only after // substitution converts each one to `HasParam(X, X, X, X).V`. fn F[X:! (HasTypes & HasParams(.Self, .Self, .Self, .Self)) where .Self impls HasParams(.A, .A, .A, .A) and .Self impls HasParams(.B, .B, .B, .B) and .Self impls HasParams(.C, .C, .C, .C) and .Self impls HasParams(.D, .D, .D, .D) and .Self impls HasParams(.E, .E, .E, .E) and .F = .Self.(HasParams(.E, .E, .E, .E).V).A and .E = .Self.(HasParams(.D, .D, .D, .D).V).A and .D = .Self.(HasParams(.C, .C, .C, .C).V).A and .C = .Self.(HasParams(.B, .B, .B, .B).V).A and .B = .Self.(HasParams(.A, .A, .A, .A).V).A and .A = .Self and .V = .Self](x: X) -> X.F { return x; } impl i32 as HasTypes where .A = .B and .B = .C and .C = .D and .D = .E and .E = .F and .F = i32 {} impl i32 as HasParams(i32, i32, i32, i32) where .V = i32 {} fn Main() -> i32 { return F(0); } ================================================ FILE: toolchain/check/fuzzer_corpus/c2efeeb24454e0da713af7391d28f6b1f1820d6c ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE // CHECK:STDOUT: result: 122 package ExplorerTest; interface A { fn F() -> i32; } interface B { fn F() -> i32; } impl i32 as A { fn F() -> i32 { return 1; } } impl i32 as B { fn F() -> i32 { return 2; } } fn GetAB(T:! B where .Self impls A) -> i32 { return 100 * T.(A.F)() + 10 * T.(B.F)() + T.F(); } fn Main() -> i32 { return GetAB(i32); } ================================================ FILE: toolchain/check/fuzzer_corpus/c3c888581796e961aed506d812407f76c99c1659 ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE package ExplorerTest; class C { // CHECK:STDERR: COMPILATION ERROR: fail_circular_inheritance.carbon:[[@LINE+4]]: could not find `class C { // CHECK:STDERR: extend base: C; // CHECK:STDERR: } // CHECK:STDERR: ` extend base: C; } fn Main() -> i32 { return 0; } ================================================ FILE: toolchain/check/fuzzer_corpus/c42ace6ddd8e547dced2e0ed23181b826788913d ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE package ExplorerTest; fn Main() -> i32 { var p: {.x: i32, .y: i32}; // CHECK:STDERR: COMPILATION ERROR: fail_struct_member_access.carbon:[[@LINE+1]]: use of uninitialized variable p p.x = p.y; return 0; } ================================================ FILE: toolchain/check/fuzzer_corpus/c4d643c7e8081d02c8467600200be7be29d58d8d ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE // CHECK:STDOUT: result: 3 package ExplorerTest; class TypeLike { extend impl as ImplicitAs(type) { fn Convert[self: Self]() -> type { return i32; } } fn Make() -> Self { return {}; } } fn Main() -> TypeLike.Make() { var x: {.a: TypeLike.Make(), .b: TypeLike.Make()} = {.a = 1, .b = 2}; return x.a + x.b; } ================================================ FILE: toolchain/check/fuzzer_corpus/c5cd5cb2197a889e003767ff5c0de2040094ac96 ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE package ExplorerTest; interface Iface { let N:! i32; } fn F(T:! Iface where .N == 5) {} class Good {} class Bad {} impl Good as Iface where .N = 5 {} impl Bad as Iface where .N = 4 {} fn Main() -> i32 { F(Good); // CHECK:STDERR: COMPILATION ERROR: fail_different_value.carbon:[[@LINE+1]]: constraint requires that (T).(Iface.N) (with value 4) == 5, which is not known to be true F(Bad); return 0; } ================================================ FILE: toolchain/check/fuzzer_corpus/c5ec8aaab0717d6b901c8bc42757700a342aaf32 ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE package ExplorerTest; interface Iface { let T:! type; // CHECK:STDERR: COMPILATION ERROR: fail_redefined.carbon:[[@LINE+1]]: Duplicate name `T` also found at fail_redefined.carbon:[[@LINE-1]] let T:! type; } fn Main() -> i32 { return 0; } ================================================ FILE: toolchain/check/fuzzer_corpus/c6812fb07715a0206afeac4ac2275784a158cf63 ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE package ExplorerTest; fn CompareBools(a: bool, b: bool) -> bool { // CHECK:STDERR: SYNTAX ERROR: fail_compare_precedence.carbon:[[@LINE+1]]: syntax error, unexpected EQUAL_EQUAL, expecting SEMICOLON return not a == b; } ================================================ FILE: toolchain/check/fuzzer_corpus/c855a3994277ba5408dbbadaa49d92b52f2467d2 ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE // CHECK:STDOUT: result: 0 package ExplorerTest; interface Vector { fn Add[self: Self](b: Self) -> Self; fn Scale[self: Self](v: i32) -> Self; } class Point1 { var x: i32; var y: i32; extend impl as Vector { fn Add[self: Point1](b: Point1) -> Point1 { return {.x = self.x + b.x, .y = self.y + b.y}; } fn Scale[self: Point1](v: i32) -> Point1 { return {.x = self.x * v, .y = self.y * v}; } } } class Point2 { var x: i32; var y: i32; extend impl as Vector { fn Add[self: Point2](b: Point2) -> Point2 { return {.x = self.x + b.x + 1, .y = self.y + b.y + 1}; } fn Scale[self: Point2](v: i32) -> Point2 { return {.x = self.x * v * 2, .y = self.y * v * 2}; } } } fn ScaleGeneric[U:! Vector](c: U, s: i32) -> U { return c.Scale(s); } fn AddAndScaleGeneric[T:! Vector, V:! Vector](a: T, b: V, s: i32) -> (T, V) { return (ScaleGeneric(a.Add(a), s), ScaleGeneric(b.Add(b), s)); } fn Main() -> i32 { var a: Point1 = {.x = 1, .y = 1}; var b: Point2 = {.x = 2, .y = 3}; var (p: Point1, q: Point2) = AddAndScaleGeneric(a, b, 5); return q.x - p.x - 40; } ================================================ FILE: toolchain/check/fuzzer_corpus/c86a4df5e4046b6cffb57a8563514e8ea459147a ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE // CHECK:STDOUT: result: 0 package ExplorerTest; fn Main() -> i32 { var a: i32 = 1; let a_pinned: i32 = a; let a_pinned2: i32 = a; // OK: Value unused after being mutated. a = 2; return 0; } ================================================ FILE: toolchain/check/fuzzer_corpus/c8c23b0c0d7655f81ac96fdb089bcb7a82c25950 ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE // CHECK:STDOUT: result: 0 package ExplorerTest; fn Main() -> i32 { var t1: {.x: i32,} = {.x = 5,}; var t2: {.x: i32, .y: i32} = {.x = 2, .y = 3,}; return t1.x - t2.x - t2.y; } ================================================ FILE: toolchain/check/fuzzer_corpus/ca5382377576c400f270660fcce6472e8664deb9 ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE package ExplorerTest; fn CompareStr(s: String) -> i32 { // CHECK:STDERR: SYNTAX ERROR: fail_raw_block_single_line.carbon:[[@LINE+1]]: Invalid block string: Too few lines if (s == #'''raw string literal starting with '''#) { return 0; } return 1; } fn Main() -> i32 { return CompareStr("\"\"raw string literal starting with \"\""); } ================================================ FILE: toolchain/check/fuzzer_corpus/ca69be54b8273f334ab8c7db0ad454c5ff0291c9 ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE // CHECK:STDOUT: result: 0 package ExplorerTest; interface Vector(Scalar:! type) { fn Zero() -> Self; fn Add[self: Self](b: Self) -> Self; fn Scale[self: Self](v: Scalar) -> Self; } class Point { var x: i32; var y: i32; extend impl as Vector(i32) { fn Zero() -> Point { return {.x = 0, .y = 0}; } fn Add[self: Point](b: Point) -> Point { return {.x = self.x + b.x, .y = self.y + b.y}; } fn Scale[self: Point](v: i32) -> Point { return {.x = self.x * v, .y = self.y * v}; } } } fn AddAndScaleGeneric[T:! type, U:! Vector(T)](a: U, s: T) -> U { return a.Add(U.Zero()).Scale(s); } fn Main() -> i32 { var a: Point = {.x = 2, .y = 1}; var p: Point = AddAndScaleGeneric(a, 5); return p.x - 10; } ================================================ FILE: toolchain/check/fuzzer_corpus/cb2c2e620fb2192d723bcecff7a8cadd41775362 ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE // CHECK:STDOUT: result: 0 package ExplorerTest; fn f(x: i32) -> i32 { return x - 1; } fn Main() -> i32 { return f(1); } ================================================ FILE: toolchain/check/fuzzer_corpus/cbd23efc1218cbf81ebdcb503cfeaaf6925c29d2 ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE // CHECK:STDOUT: result: 0 package ExplorerTest; class Point { fn Origin() -> Point { return {.x = 0, .y = 0}; } // Allowed: `Self` here means `Point`. fn GetX[self: Self]() -> i32 { return self.x; } var x: i32; var y: i32; } fn Main() -> i32 { var p: Point = Point.Origin(); return p.GetX(); } ================================================ FILE: toolchain/check/fuzzer_corpus/cbe88d560d36df81f2d7ae7910a44e6926d46f7e ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE // CHECK:STDOUT: 4 // CHECK:STDOUT: 3 // CHECK:STDOUT: 2 // CHECK:STDOUT: result: 0 package ExplorerTest; class A { var n: i32; } impl A as SubWith(i32) where .Result = A { fn Op[self: Self](rhs: i32) -> A { return {.n = self.n - rhs}; } } fn Main() -> i32 { var a: A = {.n = 5}; a = a - 1; Print("{0}", a.n); a -= 1; Print("{0}", a.n); Print("{0}", (a - 1).n); return 0; } ================================================ FILE: toolchain/check/fuzzer_corpus/cc055d77975b79cf9281d71e00182aaeef7ccf1b ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE // CHECK:STDOUT: result: 0 package ExplorerTest; class Point { var x: i32; var y: i32; } fn GetX(p: Point) -> i32 { return p.x; } fn Main() -> i32 { return GetX({.x = 1, .y = 2}) - 1; } ================================================ FILE: toolchain/check/fuzzer_corpus/cc2d5a48c731ae4058d884411e7235b2ae4bf829 ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE package ExplorerTest; fn F() {} alias A = F; // CHECK:STDERR: COMPILATION ERROR: fail_wrong_fn_alias_use.carbon:[[@LINE+1]]: alias A cannot be used as a name qualifier fn A.Foo() {} fn Main() -> i32 { return 0; } ================================================ FILE: toolchain/check/fuzzer_corpus/cc774069d5739d66235983360e14f8740de6d975 ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE package ExplorerTest; fn BadSimpleMemberAccess[T:! type](a: T) -> T { // CHECK:STDERR: COMPILATION ERROR: fail_field_access_on_generic.carbon:[[@LINE+1]]: member access in unconstrained type return a.x; } fn Main() -> i32 { return BadSimpleMemberAccess(0); } ================================================ FILE: toolchain/check/fuzzer_corpus/ccaf2f7ac19101566bddd7e0b4bcbb5434a38bf4 ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE package ExplorerTest; constraint X { // CHECK:STDERR: COMPILATION ERROR: fail_associated_constant.carbon:[[@LINE+1]]: associated constant not permitted in named constraint let N:! type; } fn Main() -> i32 { return 0; } ================================================ FILE: toolchain/check/fuzzer_corpus/cd940109a8870955e7eb8e03004304e9dc7fc808 ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE package ExplorerTest; fn Main() -> i32 { // CHECK:STDERR: SYNTAX ERROR: fail_invalid_integer_type.carbon:[[@LINE+1]]: Invalid type literal: i11111111111111111111111111 var x: i11111111111111111111111111 = 1; return 0; } ================================================ FILE: toolchain/check/fuzzer_corpus/cded9327e7355942a46c8e25c8328275dbef50bf ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE // CHECK:STDOUT: result: 1 package ExplorerTest; fn snd[T:! type](x: i32, y: T) -> T { return y; } fn Main() -> i32 { return snd(0, 1); } ================================================ FILE: toolchain/check/fuzzer_corpus/ce2080f786cedebec8a0147b450b3a9a85365258 ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE // CHECK:STDOUT: result: 0 package ExplorerTest; fn Main() -> i32 { var s: String = #''' A block string literal \ '''#; if (s == "A block string literal\n\\\n") { return 0; } else { return 1; } } ================================================ FILE: toolchain/check/fuzzer_corpus/cff3eed86965820544d6454ed5d9cf0735a7bf48 ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE package ExplorerTest; // CHECK:STDERR: COMPILATION ERROR: fail_fn_use_in_return_type.carbon:[[@LINE+1]]: 'F' is not usable until after it has been completely declared fn F() -> F() { return type; } fn Main() -> i32 { return x; } ================================================ FILE: toolchain/check/fuzzer_corpus/d0d95941336a7a4ac3202f363708dbc685900f64 ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE // CHECK:STDOUT: result: 0 package ExplorerTest; class Point { fn Origin() -> Point { return {.x = 0, .y = 0}; } var x: i32; var y: i32; } fn Main() -> i32 { var f: __Fn() -> Point = Point.Origin; return f().x; } ================================================ FILE: toolchain/check/fuzzer_corpus/d1ec799a11b9190d5a39b84e45558cba93c31fdd ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE package ExplorerTest; interface I {} impl i32 as I {} fn F(A:! i32, B:! i32, C:! i32, D:! i32, E:! i32, T:! I where A == B and C == D and C == E and B == D) { // CHECK:STDERR: COMPILATION ERROR: fail_combine_equality.carbon:[[@LINE+1]]: member access, F not in interface I where T impls interface I and A == B and C == D and C == E and B == D T.F(); } fn Main() -> i32 { F(1, 1, 1, 1, 1, i32); return 0; } ================================================ FILE: toolchain/check/fuzzer_corpus/d2a4af5c22209dd1503f38d0a36d75e9b21a8bd4 ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE package ExplorerTest; class Point(T:! type) { fn Create(x: T, y: T) -> Point(T) { return {.x = x, .y = y}; } var x: T; var y: T; } fn Main() -> i32 { // CHECK:STDERR: COMPILATION ERROR: fail_generic_class_arg.carbon:[[@LINE+1]]: wrong number of arguments in function call, expected 1 but got 2 var p: Point(i32) = Point(i32, i32).Create(0, 1); return p.x; } ================================================ FILE: toolchain/check/fuzzer_corpus/d3fc2d9df6573461a1ea20c7997f27dfd9f1ee8f ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE // CHECK:STDOUT: result: 0 package ExplorerTest; class Cell(T:! type) { fn Create(x: T) -> Cell(T) { return { .data = x }; } fn Get[self: Self]() -> T { return self.data; } fn Put[addr self: Self*](x: T) { (*self).data = x; } fn CreateOther[self: Self, U:! type](x: U) -> Cell(U) { return {.data = x}; } var data: T; } class Integer { var int: i32; } fn Main() -> i32 { var i: Integer = {.int = 1}; var c: Cell(Integer) = Cell(Integer).Create(i); i = {.int = 0}; c.Put(i); var j: Integer = c.Get(); var d: Cell(Integer) = c.CreateOther(j); return c.data.int + d.data.int; } ================================================ FILE: toolchain/check/fuzzer_corpus/d533854f44af409027dfc8c1c288f90ebf111192 ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE // CHECK:STDOUT: result: 1 package ExplorerTest; fn Main() -> i32 { var n: i32 = 0; match (n) { case 1 => { return 0; } } return 1; } ================================================ FILE: toolchain/check/fuzzer_corpus/d5a91fb15ac01afd500538b5679647289146045a ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE package ExplorerTest; interface GetX { fn DoIt[self: Self]() -> i32; } impl forall [template T:! type] T as GetX { // CHECK:STDERR: COMPILATION ERROR: fail_no_member.carbon:[[@LINE+1]]: member access, unexpected i32 in self.x fn DoIt[self: Self]() -> i32 { return self.x; } } fn Main() -> i32 { return 0.(GetX.DoIt)(); } ================================================ FILE: toolchain/check/fuzzer_corpus/d5af10d2b47f3859bcc15da85fa3d077b942f43a ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE // CHECK:STDOUT: DESTRUCTOR A 1 // CHECK:STDOUT: DESTRUCTOR B 2 // CHECK:STDOUT: result: 1 package ExplorerTest; class A{ class B{ destructor[self: Self]{ Print("DESTRUCTOR B {0}",self.n); } fn Create(x: i32) -> B{ return {.n = x }; } var n: i32; } destructor[self: Self]{ Print("DESTRUCTOR A {0}",self.n); } fn Create(x: i32) -> A{ return {.n = x, .b = B.Create(2)}; } var n: i32; var b : B; } fn Main() -> i32 { var a: A = A.Create(1); return 1; } ================================================ FILE: toolchain/check/fuzzer_corpus/d5f20d0057317362bc92258e8c06f6b03c2926a4 ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE // CHECK:STDOUT: Destructor A 3 // CHECK:STDOUT: Destructor A 1 // CHECK:STDOUT: result: 0 package ExplorerTest; class A { var i: i32; destructor[self: Self] { Print("Destructor A {0}", self.i); } } fn Main() -> i32 { var a0: A; var a1: A = {.i = 1}; var a2: A; var a3: A = {.i = 3}; return 0; } ================================================ FILE: toolchain/check/fuzzer_corpus/d60be9abac4bd2cb92b3432b7c10771def0c009e ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE // CHECK:STDOUT: Hello world! // CHECK:STDOUT: result: 0 package ExplorerTest; fn Main() -> i32 { var s: auto = "Hello world!"; Print(s); return 0; } ================================================ FILE: toolchain/check/fuzzer_corpus/d698884570f5265a3cd5ab0b822ed28fba467d13 ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE // CHECK:STDOUT: result: 0 package ExplorerTest; fn f(x: i32) -> i32 { if (x == 0) { return x; } else { return f(x - 1); } } fn Main() -> i32 { return f(2); } ================================================ FILE: toolchain/check/fuzzer_corpus/d77e0fdacf1d3d9c711e24c8b088e2da26d98746 ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE // CHECK:STDOUT: result: 2 package ExplorerTest; var call_count: i32 = 0; fn ReturnTrue() -> bool { call_count = call_count + 1; return true; } fn Main() -> i32 { var result: bool = ReturnTrue() and ReturnTrue(); return if result then call_count else -1; } ================================================ FILE: toolchain/check/fuzzer_corpus/d7de67b2375a361719d942f4cf49ea0a9cdf2c45 ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE // CHECK:STDOUT: result: 1 package ExplorerTest; class Point(T:! type, V:! T) { fn Get[self: Self]() -> T { return V; } } fn F(p: Point(i32, 1)) -> i32 { return p.Get(); } fn Main() -> i32 { var v: Point(i32, 1) = {}; return F(v); } ================================================ FILE: toolchain/check/fuzzer_corpus/d81e51e207bd2a4077a15890f337487a93060f7b ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE // CHECK:STDOUT: Foo(c1): 1 // CHECK:STDOUT: Foo(c2): 1 // CHECK:STDOUT: Foo(d): 1 // CHECK:STDOUT: Foo(&e): 1 // CHECK:STDOUT: result: 0 package ExplorerTest; base class C { var val: i32; } base class D { extend base: C; var val: i32; } class E { extend base: D; var val: i32; } fn Foo(c: C*) -> i32 { return (*c).val; } fn Main() -> i32 { var e: E = { .val = 3, .base = {.val = 2,.base = {.val = 1}}}; var d: D* = &e; var c1: C* = &e; var c2: C* = d; Print("Foo(c1): {0}", Foo(c1)); Print("Foo(c2): {0}", Foo(c2)); Print("Foo(d): {0}", Foo(d)); Print("Foo(&e): {0}", Foo(&e)); return 0; } ================================================ FILE: toolchain/check/fuzzer_corpus/d87e64cb8726f55d05773858cbe6979e550c0f5c ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE package ExplorerTest; fn CompareStr(s: String) -> i32 { // CHECK:STDERR: SYNTAX ERROR: fail_raw_more_hash_tags_on_right.carbon:[[@LINE+1]]: invalid character '\x23' in source file. if (s == "str"#) { return 0; } return 1; } fn Main() -> i32 { return CompareStr("str"); } ================================================ FILE: toolchain/check/fuzzer_corpus/d967a050c50d0b75e8f64611560b0f221bd9525f ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE // CHECK:STDOUT: d.a=1 // CHECK:STDOUT: d.b=2 // CHECK:STDOUT: result: 0 package ExplorerTest; abstract class C { var a: i32; } class D { extend base: C; var b: i32; } fn Main() -> i32 { var d: D = { .base = {.a = 1}, .b = 2 }; Print("d.a={0}", d.a); Print("d.b={0}", d.b); return 0; } ================================================ FILE: toolchain/check/fuzzer_corpus/da795d40354dd38e3d00b82c7790eb4d6c8ec2b5 ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE // CHECK:STDOUT: result: 1 package ExplorerTest; class A(T:! type) { var v: T; } fn F(T:! type, x: T) -> T { var v: A(T) = {.v = x}; return v.v; } fn Main() -> i32 { return F(i32, 1); } ================================================ FILE: toolchain/check/fuzzer_corpus/db5937dfd7d8e43ea817fbcac0b83ad38ccae116 ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE // CHECK:STDOUT: result: 0 package ExplorerTest; interface Vector { fn Add[self: Self](b: Self) -> Self; fn Scale[self: Self](v: i32) -> Self; } class Point { var x: i32; var y: i32; } impl Point as Vector { fn Add[self: Point](b: Point) -> Point { return {.x = self.x + b.x, .y = self.y + b.y}; } fn Scale[self: Point](v: i32) -> Point { return {.x = self.x * v, .y = self.y * v}; } } fn AddAndScaleGeneric[T:! Vector](a: T, b: T, s: i32) -> T { var m: auto = a.Add; var n: auto = m(b).Scale; return n(s); } fn Main() -> i32 { var a: Point = {.x = 1, .y = 4}; var b: Point = {.x = 2, .y = 3}; var p: Point = AddAndScaleGeneric(a, b, 5); return p.x - 15; } ================================================ FILE: toolchain/check/fuzzer_corpus/dbd0b78b1bfd4878676ac3d882aee2b797d7af15 ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE // CHECK:STDOUT: DESTRUCTOR A 1 // CHECK:STDOUT: DESTRUCTOR A 2 // CHECK:STDOUT: DESTRUCTOR A 3 // CHECK:STDOUT: DESTRUCTOR A 4 // CHECK:STDOUT: DESTRUCTOR A 5 // CHECK:STDOUT: DESTRUCTOR A 6 // CHECK:STDOUT: result: 1 package ExplorerTest; class A{ destructor[self: Self]{ Print("DESTRUCTOR A {0}",self.n); } var n: i32; } fn Main() -> i32 { var a: array(A, 2) = ({.n = 6},{.n = 5}); var b: array(array(A, 2), 2) = (({.n = 4},{.n = 3}), ({.n = 2},{.n = 1})); return 1; } ================================================ FILE: toolchain/check/fuzzer_corpus/dccc5c3818f328ee1fd500f899c37ac0af7b2182 ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE // CHECK:STDOUT: 5 // CHECK:STDOUT: 4 // CHECK:STDOUT: 0 // CHECK:STDOUT: result: 0 package ExplorerTest; class A { var n: i32; } impl A as BitAndWith(i32) where .Result = A { fn Op[self: Self](rhs: i32) -> A { return {.n = self.n & rhs}; } } fn Main() -> i32 { var a: A = {.n = 13}; a = a & 7; Print("{0}", a.n); a &= -2; Print("{0}", a.n); Print("{0}", (a & 3).n); return 0; } ================================================ FILE: toolchain/check/fuzzer_corpus/dcde55e2a4e60d6bf3dd574d15a425e30279d53c ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE // CHECK:STDOUT: result: 0 package ExplorerTest; fn Main() -> i32 { if (0 == 1) { return 1; } else if (0 == 2) { return 2; } else { return 0; } return 1; } ================================================ FILE: toolchain/check/fuzzer_corpus/dd662568f4fe92a74f53b026f11638725f966e0b ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE // CHECK:STDOUT: result: 1 package ExplorerTest; fn Main() -> i32 { returned var x: i32; x = 1; return var; } ================================================ FILE: toolchain/check/fuzzer_corpus/de46bc1199653f93b7db2c1c39e7ca61dfa6b810 ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE // CHECK:STDOUT: result: 0 package ExplorerTest; fn Main() -> i32 { var t: auto = (1, 2, 3, 4); match (t) { case (_: i32, _: auto, x: i32, y: auto) => { return y - x - 1; } } return 1; } ================================================ FILE: toolchain/check/fuzzer_corpus/e028180f3ba177567c45f5149e96c0313d1b175f ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE package ExplorerTest; namespace A; namespace A.B; namespace A.B.C; fn A.B.C. // CHECK:STDERR: COMPILATION ERROR: fail_missing_nested.carbon:[[@LINE+1]]: name 'D' has not been declared in namespace A.B.C D // Don't combine with the previous line. This test is verifying that the // diagnostic points at the right token. .E.F() {} fn Main() -> i32 { return 0; } ================================================ FILE: toolchain/check/fuzzer_corpus/e03df3e0be45ac48d5ad2d59b67b677678a76de6 ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE // CHECK:STDOUT: result: 3 package ExplorerTest; choice A { Value(i32) } impl i32 as ImplicitAs(A) { fn Convert[self: Self]() -> A { return A.Value(self + 1); } } fn Main() -> i32 { let A.Value(n: i32) = 2; return n; } ================================================ FILE: toolchain/check/fuzzer_corpus/e0afc6bd74d5718e01a5888df16600e8a4645f63 ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE // TODO: Should this work? package ExplorerTest; interface Vector { let Dim:! i32; } impl (i32, i32, i32) as Vector where .Dim = 3 {} class Point(Scalar:! type, Dim:! i32) {} fn F[Scalar:! type, V:! Vector where .Dim == 3](p: Point(Scalar, V.Dim), v: V) {} fn G[Scalar:! type](p: Point(Scalar, 3)) {} fn H[V:! Vector where .Dim == 3](v: V) { var p: Point(i32, V.Dim) = {}; // CHECK:STDERR: COMPILATION ERROR: fail_match_in_deduction.carbon:[[@LINE+1]]: mismatch in non-deduced values, `(V).(Vector.Dim)` != `3` G(p); } fn Main() -> i32 { var p: Point(i32, 3) = {}; // Deduce Point(Scalar, V.Dim) from Point(i32, 3). F(p, (0, 0, 0)); // Deduce Point(Scalar, 3) from Point(i32, V.Dim). H((0, 0, 0)); return 0; } ================================================ FILE: toolchain/check/fuzzer_corpus/e2c590fff5cd302d986f3fed578e45fa09836052 ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE // CHECK:STDOUT: i32.F // CHECK:STDOUT: result: 0 package ExplorerTest; interface Base { fn F(); } interface Extension { extend Base; } fn F[T:! type where .Self impls Extension](x: T) { x.(Extension.F)(); } impl i32 as Extension { fn F() { Print("i32.F"); } } fn Main() -> i32 { var n: i32 = 0; F(n); return 0; } ================================================ FILE: toolchain/check/fuzzer_corpus/e3895b1888cb48ad3ab9c9cd3b78debedb0ad5e9 ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE // CHECK:STDOUT: 6 // CHECK:STDOUT: 3 // CHECK:STDOUT: 1 // CHECK:STDOUT: result: 0 package ExplorerTest; class A { var n: i32; } impl A as DivWith(i32) where .Result = A { fn Op[self: Self](rhs: i32) -> A { return {.n = self.n / rhs}; } } fn Main() -> i32 { var a: A = {.n = 19}; a = a / 3; Print("{0}", a.n); a /= 2; Print("{0}", a.n); Print("{0}", (a / 2).n); return 0; } ================================================ FILE: toolchain/check/fuzzer_corpus/e45bda3cd3778f2372ce18cc0d9ff7bbf1f50ba5 ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE // CHECK:STDOUT: result: 0 package ExplorerTest; __mixin M1 { fn Scale10[self: Self](x:i32) -> i32{ return x * 10; } } __mixin M2 { __mix M1; fn Square[self: Self](x:i32) -> i32{ return x * x; } } class C { __mix M2; } fn Main() -> i32 { var c: C = {}; return c.Square(11) - c.Scale10(10) - 21; } ================================================ FILE: toolchain/check/fuzzer_corpus/e48c19846a9d9a8418fbf4e363eb69d720fdab9d ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE // CHECK:STDOUT: result: 1234 package ExplorerTest; interface A { fn F() -> i32; } interface B { fn F() -> i32; fn G() -> i32; } interface C(T:! type) { fn H() -> T; } impl i32 as A { fn F() -> i32 { return 1; } } impl i32 as B & C(i32) where .Self impls A { fn F() -> i32 { return 2; } fn G() -> i32 { return 3; } fn H() -> i32 { return 4; } } fn Main() -> i32 { let n: i32 = 0; return n.(A.F)() * 1000 + n.(B.F)() * 100 + n.(B.G)() * 10 + n.(C(i32).H)(); } ================================================ FILE: toolchain/check/fuzzer_corpus/e4b64d16a5d43dc7e5bee135714611d2929364a6 ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE package ExplorerTest; interface Iface { let T:! type; } fn F[T:! Iface where .T == i32](x: T) {} fn G[U:! Iface where .T == i32](x: U) { F(x); } fn H[V:! Iface](x: V) { // CHECK:STDERR: COMPILATION ERROR: fail_equal_to_dependent_type.carbon:[[@LINE+1]]: constraint requires that (T).(Iface.T) (with value (V).(Iface.T)) == i32, which is not known to be true F(x); } class Class { extend impl as Iface where .T = i32 {} } fn Main() -> i32 { var x: Class = {}; G(x); H(x); return 0; } ================================================ FILE: toolchain/check/fuzzer_corpus/e657c790f4c10f0678e90dc52facef51920a94d3 ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE package ExplorerTest; constraint X { // CHECK:STDERR: COMPILATION ERROR: fail_associated_function.carbon:[[@LINE+1]]: associated function not permitted in named constraint fn F(); } fn Main() -> i32 { return 0; } ================================================ FILE: toolchain/check/fuzzer_corpus/e673a37cb310aaa88cbc216044663eb2c49ae76f ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE // CHECK:STDOUT: result: 0 package ExplorerTest; fn Main() -> i32 { var t: (auto, (i32, i32)) = ((1, 2), (3, 4)); return t[0][0] + t[1][1] - 5; } ================================================ FILE: toolchain/check/fuzzer_corpus/e6da0876675b17c319ea55bf19ecbc12b779d3df ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE // CHECK:STDOUT: result: 1133 package ExplorerTest; interface I(T:! type) { fn F[self: Self](t: T, o: Self) -> Self; } class X(U:! type) { extend impl as I(U) { fn F[self: Self](u: U, o: Self) -> Self { return {.m = u, .n = self.n + o.n}; } } var m: U; var n: i32; } fn Run[V:! type](x: V) -> (V, V, i32, i32) { var v: X(V) = {.m = x, .n = 1}; var w: X(V) = {.m = x, .n = 2}; // OK, know that `X(V)` implements `I(V)` from the `impl` in the class. var call: X(V) = v.(X(V).(I(V).F))(x, w); return (call.(X(V).m), call.m, call.(X(V).n), call.n); } fn Main() -> i32 { var (a: i32, b: i32, c: i32, d: i32) = Run(1); return a * 1000 + b * 100 + c * 10 + d; } ================================================ FILE: toolchain/check/fuzzer_corpus/e77f7b118df2a85fa55e8798a40f343b59ceab88 ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE package ExplorerTest; // CHECK:STDERR: COMPILATION ERROR: fail_impl_as.carbon:[[@LINE+1]]: expected a constraint in impl declaration, found i32 impl i32 as i32 {} fn Main() -> i32 { } ================================================ FILE: toolchain/check/fuzzer_corpus/e78aa6328c0b365b87a34a2dd1cb8e5270e04a0b ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE package ExplorerTest; class Point { fn Origin() -> Point { return {.x = 0, .y = 0}; } fn SetX[self: Point](x: i32) { // CHECK:STDERR: COMPILATION ERROR: fail_method_args.carbon:[[@LINE+1]]: Only a reference expression can be assigned to, but got `x` x = 10; } var x: i32; var y: i32; } fn Main() -> i32 { var p: Point = Point.Origin(); p.SetX(42); return 0; } ================================================ FILE: toolchain/check/fuzzer_corpus/e9dff14915dda34282efb04147877d0fc83ba2a0 ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE // CHECK:STDOUT: result: 0 package ExplorerTest; fn swap[T:! type, U:! type](tuple: (T, U)) -> (U, T) { return (tuple[1], tuple[0]); } fn Main() -> i32 { return swap((0, true))[1]; } ================================================ FILE: toolchain/check/fuzzer_corpus/ea454bd2c8e7219c267ca0b01e27c649999c1451 ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE package ExplorerTest; class A { fn F[self: Self]() -> i32; } fn Main() -> i32 { var a: A = {}; // CHECK:STDERR: RUNTIME ERROR: fail_call_undefined_method.carbon:[[@LINE+1]]: attempt to call function `F` that has not been defined return a.F(); } ================================================ FILE: toolchain/check/fuzzer_corpus/ea82b6f22005103932231c0641c74b125fcc7fb0 ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE // CHECK:STDOUT: result: -6 package ExplorerTest; class A { var n: i32; } impl A as BitComplement where .Result = A { fn Op[self: Self]() -> A { return {.n = ^self.n}; } } fn Main() -> i32 { var a: A = {.n = 5}; a = ^a; return a.n; } ================================================ FILE: toolchain/check/fuzzer_corpus/eaa7bb9e4dbf3576f9587cd88b29976ca2024fe6 ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE // CHECK:STDOUT: DESTRUCTOR A 1 // CHECK:STDOUT: DESTRUCTOR A 2 // CHECK:STDOUT: DESTRUCTOR A 3 // CHECK:STDOUT: DESTRUCTOR A 4 // CHECK:STDOUT: result: 2 package ExplorerTest; class A{ destructor[self: Self]{ Print("DESTRUCTOR A {0}",self.n); } var n: i32; } fn ident(x: bool)-> bool{ return x; } fn ident_i32(x: i32)-> i32{ return x; } // It should be enforced that different runtime scopes are on the stack. //So that it can be tested that the wrong scopes are not removed from the stack. fn Main() -> i32 { var i: i32 = 0; var d: A = {.n = 4}; if(ident(true)){ var a: A = {.n = 3}; if(true){ var b: A = {.n = 2}; ident_i32(2); if(ident_i32(0) == 0){ var c: A = {.n = 1}; return 2; } } } return 1; } ================================================ FILE: toolchain/check/fuzzer_corpus/eaca8f02a5ca41d60a68be9186e5f79538ae2719 ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE // CHECK:STDOUT: result: 0 package ExplorerTest; fn Main() -> i32 { var x: i32 = (1); var t2: (i32, i32) = (5, 2); t2[0] = 3; return t2[0] - t2[1] - x; } ================================================ FILE: toolchain/check/fuzzer_corpus/ebb86c45027660ce57bbadede73ab1a5dbc43e35 ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE package ExplorerTest; // CHECK:STDERR: COMPILATION ERROR: fail_nontype_tuple_as_type.carbon:[[@LINE+1]]: type error in type of name binding: '(i32, i32)' is not implicitly convertible to 'type' fn F[T:! (i32, i32)](x: T); fn Main() -> i32; ================================================ FILE: toolchain/check/fuzzer_corpus/ecd12a6a72a7c5cdf93dd9bb7637cb35f65c1d00 ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE // CHECK:STDOUT: result: 0 package ExplorerTest; fn Main() -> i32 { var s: String = ''' A "block" ""string"" literal with indent. '''; if (s == "A \"block\" \"\"string\"\" literal\n with indent.\n") { return 0; } else { return 1; } } ================================================ FILE: toolchain/check/fuzzer_corpus/ecdb111d6c89a22189b961cb47ab1e73df8ed430 ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE package ExplorerTest; fn Main() -> i32 { var v: bool; // CHECK:STDERR: COMPILATION ERROR: fail_if_cond.carbon:[[@LINE+1]]: use of uninitialized variable v return if v then 1 else 2; } ================================================ FILE: toolchain/check/fuzzer_corpus/ed0769767eea7a7e0ee58d52278417c698605719 ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE package ExplorerTest; fn Main() -> i32 { var x: i32; // CHECK:STDERR: COMPILATION ERROR: fail_field_value.carbon:[[@LINE+1]]: use of uninitialized variable x var p: auto = {.x = x,}; return 0; } ================================================ FILE: toolchain/check/fuzzer_corpus/ed28ae6948896b9be69c556380b22f8c6fdee8d1 ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE // CHECK:STDERR: RUNTIME ERROR: :0: attempt to call function `Main` that has not been defined package ExplorerTest; fn Main() -> i32; ================================================ FILE: toolchain/check/fuzzer_corpus/ed306e9bbd8d275d000beb5cf551284765c97c23 ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE // CHECK:STDOUT: result: 0 package ExplorerTest; fn Main() -> i32 { var t: auto = 5; match (t) { case x: i32 => { return x - 5; } } } ================================================ FILE: toolchain/check/fuzzer_corpus/ee45e8d94544f8f79e7b6330365a9426715f47f9 ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE // CHECK:STDOUT: (*c).a: 1 // CHECK:STDOUT: Foo(&d): 1 // CHECK:STDOUT: result: 0 package ExplorerTest; base class C { var a: i32; } class D { extend base: C; } fn Foo(c: C*) -> i32 { return (*c).a; } fn Main() -> i32 { var d: D = { .base = {.a = 1} }; var c: C* = &d; Print("(*c).a: {0}", (*c).a); Print("Foo(&d): {0}", Foo(&d)); return 0; } ================================================ FILE: toolchain/check/fuzzer_corpus/ee8db01890a0f9c520e30f1d75c44efabbbbdb01 ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE // CHECK:STDOUT: result: 0 package ExplorerTest; fn CompareStr(s: String) -> i32 { if (s == #"str"#) { return 0; } return 1; } fn Main() -> i32 { return CompareStr("str"); } ================================================ FILE: toolchain/check/fuzzer_corpus/eed2aa2d6ca72b1972eaf2bb206a0591a2ce9a95 ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE // CHECK:STDOUT: result: 0 package ExplorerTest; fn Main() -> i32 { if (^0 != -1) { return 11; } if (^1 != -2) { return 12; } if (^(-3) != 2) { return 13; } if (3 & 6 != 2) { return 21; } if (-1 & 4 != 4) { return 22; } if (-1 & -2 != -2) { return 23; } if (1 | 4 != 5) { return 31; } if (5 | 3 != 7) { return 32; } if (-2 | 1 != -1) { return 33; } if (1 ^ 4 != 5) { return 41; } if (5 ^ 3 != 6) { return 42; } if (-2 ^ -3 != 3) { return 43; } return 0; } ================================================ FILE: toolchain/check/fuzzer_corpus/eeea07311e1c93999ce139f71cc5e6be83ae938d ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE // CHECK:STDOUT: result: 0 package ExplorerTest; // Test that assignment performs a copy and does not create an alias. fn Main() -> i32 { var x: i32 = -1; if (true) { var y: i32 = 0; x = y; // y dies here } return x; } ================================================ FILE: toolchain/check/fuzzer_corpus/ef87d1f2a1afcc05941ade6a5bd57c9fdea83b34 ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE // CHECK:STDOUT: result: 0 package ExplorerTest; interface Number { fn Zero() -> Self; fn Add[self: Self](other: Self) -> Self; } class Point(T:! Number) { fn Origin() -> Point(T) { return {.x = T.Zero(), .y = T.Zero()}; } fn Clone[self: Point(T)]() -> Point(T) { return {.x = self.x, .y = self.y}; } fn SumXY[self: Point(T)]() -> T { return self.x.Add(self.y); } var x: T; var y: T; } impl i32 as Number { fn Zero() -> i32 { return 0; } fn Add[self: i32](other: i32) -> i32 { return self + other; } } fn Main() -> i32 { var p: Point(i32) = Point(i32).Origin(); return p.Clone().SumXY(); } ================================================ FILE: toolchain/check/fuzzer_corpus/f0122e82b0498ec11a6a9d68e6de8440d891b272 ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE // CHECK:STDOUT: DESTRUCTOR C 1 // CHECK:STDOUT: DESTRUCTOR B 2 // CHECK:STDOUT: DESTRUCTOR A 3 // CHECK:STDOUT: result: 1 package ExplorerTest; class A{ destructor[self: Self]{ Print("DESTRUCTOR A {0}",self.n); } var n: i32; } class B{ destructor[self: Self]{ Print("DESTRUCTOR B {0}",self.n); } var n: i32; } class C{ destructor[self: Self]{ Print("DESTRUCTOR C {0}",self.n); } var n: i32; } fn Main() -> i32 { var a: A = {.n = 3}; var b: B = {.n = 2}; var c: C = {.n = 1}; return 1; } ================================================ FILE: toolchain/check/fuzzer_corpus/f0457f1e27c67e8425559e6b753cca540cf69f65 ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE // CHECK:STDOUT: Destructor // CHECK:STDOUT: result: 0 package ExplorerTest; class C { destructor[self: Self] { Print("Destructor"); } } fn Main() -> i32 { let c: C = {}; return 0; } ================================================ FILE: toolchain/check/fuzzer_corpus/f0ddb293345e5013757ebfacd7769ef92acd41d4 ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE package ExplorerTest; class C { // CHECK:STDERR: COMPILATION ERROR: fail_use_before_typecheck.carbon:[[@LINE+1]]: incomplete type `class C` used in member access var n: if not C.WrapInStruct() then i32 else {.n: i32}; fn WrapInStruct() -> bool { return true; } } fn Main() -> i32 { return 0; } ================================================ FILE: toolchain/check/fuzzer_corpus/f1986481baa957b8dd977f8ca3e8c4107d492799 ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE // CHECK:STDOUT: result: 0 package ExplorerTest; namespace N; namespace N.M; fn N.F() {} fn N.M.FM() {} alias A = N; alias AM = N.M; alias N.A = N.M; alias N.A2 = N; fn Main() -> i32 { A.F(); AM.FM(); N.A.FM(); A.A2.A2.A2.A2.A2.A2.A2.F(); return 0; } ================================================ FILE: toolchain/check/fuzzer_corpus/f19c5bb5147295d791fb5d6899d93ed7a23c072f ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE // CHECK:STDOUT: Interface: -3 // CHECK:STDOUT: Op: -3 // CHECK:STDOUT: result: 0 package ExplorerTest; fn Main() -> i32 { var val: i32 = 3; // Make sure that both the interface and operator work with i32. These rely on // builtin arithmetic more directly. Print("Interface: {0}", val.(Negate.Op)()); Print("Op: {0}", -val); return 0; } ================================================ FILE: toolchain/check/fuzzer_corpus/f1b24ed86c606cde53c498fc6411053d0ae22ced ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE package ExplorerTest; class X(T:! type) {} // CHECK:STDERR: COMPILATION ERROR: fail_non_type_self.carbon:[[@LINE+1]]: `.Self` used in type of non-type generic binding `T` fn F[T:! X(.Self)](x: T) {} fn Main() -> i32 { return 0; } ================================================ FILE: toolchain/check/fuzzer_corpus/f1d88daac0be722a2a2e6bedd09b39e240043653 ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE // CHECK:STDOUT: result: 0 package ExplorerTest; interface X(T:! type) {} interface Y(T:! type) { let M:! X(T); } interface Z { // The `i32 impls X(.Self)` constraint is indirectly required by // specifying that `.M = i32`. let N:! Y(.Self) where i32 impls X(.Self) and .M = i32; } impl i32 as X(i32) {} impl i32 as Y(i32) where .M = i32 {} impl i32 as Z where .N = i32 {} fn F[A:! Z](a: A) -> A { return a; } fn Main() -> i32 { return F(0); } ================================================ FILE: toolchain/check/fuzzer_corpus/f2b43d44db5dec0cca4bc6fc63f487fabae1b941 ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE package ExplorerTest; fn Main() -> i32 { // CHECK:STDERR: COMPILATION ERROR: fail_negative_size.carbon:[[@LINE+1]]: Array size cannot be negative var x: array(i32, -1) = (); return x[0]; } ================================================ FILE: toolchain/check/fuzzer_corpus/f39ac0d9de634c2a723f879231e77ef600e036d9 ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE package ExplorerTest; fn Main() -> i32 { // CHECK:STDERR: COMPILATION ERROR: fail_no_impl.carbon:[[@LINE+1]]: i32 is not equality comparable with String (could not find implementation of interface EqWith(U = String) for i32) Print("different types equal: {0}", if 1 == "1" then 1 else 0); return 0; } ================================================ FILE: toolchain/check/fuzzer_corpus/f4f4353df90e753e832eb974c782e27dbe6753d4 ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE package ExplorerTest; fn Main() -> i32 { // CHECK:STDERR: SYNTAX ERROR: fail_invalid_escape.carbon:[[@LINE+1]]: Invalid escaping in string: "str\e" Print("str\e"); return 0; } ================================================ FILE: toolchain/check/fuzzer_corpus/f669b57c4be29746f06e2ad46b428769a791dd07 ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE package ExplorerTest; class C {} // CHECK:STDERR: SYNTAX ERROR: fail_type_only.carbon:[[@LINE+1]]: syntax error, unexpected RIGHT_SQUARE_BRACKET, expecting COLON fn f[x:! i32, addr C]() {} fn Main() -> i32 { return 0; } ================================================ FILE: toolchain/check/fuzzer_corpus/f71cbd0475ddb41b78a0d439aa78ae1e2eec0df5 ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE // CHECK:STDOUT: result: 3 package ExplorerTest; fn Main() -> i32 { var ar: array(i32, 4) = (0, 1,2,3); var count : i32 = 0; for( x: i32 in ar){ count = count +x; if( x == 2){ break; } } return count; } ================================================ FILE: toolchain/check/fuzzer_corpus/f72b3c6b4ade452568f6834dd6e5896d1772ff2f ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE // CHECK:STDOUT: test // CHECK:STDOUT: result: 0 package ExplorerTest; fn F() { Print("test"); } fn Main() -> i32 { var p: auto = heap.New(F); var y: auto = *p; y(); heap.Delete(p); return 0; } ================================================ FILE: toolchain/check/fuzzer_corpus/f72fd742e291347d71ef000e002dc8e8baa13830 ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE package ExplorerTest; fn F() {} // CHECK:STDERR: COMPILATION ERROR: fail_qualifier_not_namespace.carbon:[[@LINE+1]]: fn F cannot be used as a name qualifier fn F.G() {} fn Main() -> i32 { return 0; } ================================================ FILE: toolchain/check/fuzzer_corpus/f7b8bc9f4b18f013697267cc9c966ae65f4cd7e3 ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE package ExplorerTest; interface Vector { fn Add[self: Self](b: Self) -> Self; fn Scale[self: Self](v: i32) -> Self; } class Point { var x: i32; var y: i32; } // Error: need to specify which type implementing `Vector` for. // CHECK:STDERR: COMPILATION ERROR: fail_external_impl_omit_self.carbon:[[@LINE+1]]: could not resolve 'Self' impl as Vector { fn Add[self: Point](b: Point) -> Point { return {.x = self.x + b.x, .y = self.y + b.y}; } fn Scale[self: Point](v: i32) -> Point { return {.x = self.x * v, .y = self.y * v}; } } fn AddAndScaleGeneric[T:! Vector](a: T, b: T, s: i32) -> T { var m: auto = a.Add; var n: auto = m(b).Scale; return n(s); } fn Main() -> i32 { var a: Point = {.x = 1, .y = 4}; var b: Point = {.x = 2, .y = 3}; var p: Point = AddAndScaleGeneric(a, b, 5); return p.x - 15; } ================================================ FILE: toolchain/check/fuzzer_corpus/f7c747dc5107c73b9c4bfe6f24267f04ba16d129 ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE // CHECK:STDOUT: Initialize c from initializing expression (return ) // CHECK:STDOUT: 0: Heap{}, 1: !Uninit, 2: C{} // CHECK:STDOUT: Object created, returning // CHECK:STDOUT: c destroyed // CHECK:STDOUT: 0: Heap{}, 1: C{}, 2: !!C{} // CHECK:STDOUT: c destroyed // CHECK:STDOUT: result: 0 package ExplorerTest; class C { destructor[self: Self] { Print("c destroyed"); } } fn CallWithReturnExpression() -> C { var c: C = {}; heap.PrintAllocs(); Print("Object created, returning"); return c; } fn FromInitializingExpression_ReturnExpr() { Print("Initialize c from initializing expression (return )"); let c: C = CallWithReturnExpression(); heap.PrintAllocs(); } fn Main() -> i32 { FromInitializingExpression_ReturnExpr(); return 0; } ================================================ FILE: toolchain/check/fuzzer_corpus/f91e230199efd6367f8622f5a6eaa54d1d4d8e90 ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE // CHECK:STDOUT: result: 0 package ExplorerTest; var x: i32 = 0; fn Main() -> i32 { var x: i32 = 0; return 0; } ================================================ FILE: toolchain/check/fuzzer_corpus/fa056963c20a8a8dc279cb2e2a9b769338cc92da ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE package ExplorerTest; interface HasThreeTypes { let A:! type; let B:! type; let C:! type; } // CHECK:STDERR: COMPILATION ERROR: fail_incomplete_impl_2.carbon:[[@LINE+1]]: implementation doesn't provide a concrete value for interface HasThreeTypes.C impl i32 as HasThreeTypes where .A = i32 and .B = .C {} fn Main() -> i32 { return 0; } ================================================ FILE: toolchain/check/fuzzer_corpus/fa11a132da6f10be8e8aa10cbd25ea5d8107a614 ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE package ExplorerTest; choice AB { A(), B() } fn F() -> AB { return AB.A(); } fn Main() -> i32 { // Note, this match is exhaustive, but it exceeds our depth limit. match ((F(), F(), F(), F(), F(), F(), F(), F(), F())) { case (AB.A(), AB.A(), AB.A(), AB.A(), AB.A(), AB.A(), AB.A(), AB.A(), AB.A()) => { return 0; } case (AB.B(), AB.A(), AB.A(), AB.A(), AB.A(), AB.A(), AB.A(), AB.A(), AB.A()) => { return 1; } case (_: AB, AB.B(), AB.A(), AB.A(), AB.A(), AB.A(), AB.A(), AB.A(), AB.A()) => { return 2; } case (_: AB, _: AB, AB.B(), AB.A(), AB.A(), AB.A(), AB.A(), AB.A(), AB.A()) => { return 3; } case (_: AB, _: AB, _: AB, AB.B(), AB.A(), AB.A(), AB.A(), AB.A(), AB.A()) => { return 4; } case (_: AB, _: AB, _: AB, _: AB, AB.B(), AB.A(), AB.A(), AB.A(), AB.A()) => { return 5; } case (_: AB, _: AB, _: AB, _: AB, _: AB, AB.B(), AB.A(), AB.A(), AB.A()) => { return 6; } case (_: AB, _: AB, _: AB, _: AB, _: AB, _: AB, AB.B(), AB.A(), AB.A()) => { return 7; } case (_: AB, _: AB, _: AB, _: AB, _: AB, _: AB, _: AB, AB.B(), AB.A()) => { return 8; } case (_: AB, _: AB, _: AB, _: AB, _: AB, _: AB, _: AB, _: AB, AB.B()) => { return 9; } } // CHECK:STDERR: COMPILATION ERROR: fail_exhaustive_exponential_9x.carbon:[[@LINE+1]]: non-exhaustive match may allow control-flow to reach the end of a function that provides a `->` return type } ================================================ FILE: toolchain/check/fuzzer_corpus/fb4504a84cc35d086689b9f93eb9f365331f4750 ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE // CHECK:STDOUT: result: 3 package ExplorerTest; fn AddInt(a: i32, b: i32) -> i32 { returned var ret: i32 = a + b; return var; } fn Main() -> i32 { return AddInt(1, 2); } ================================================ FILE: toolchain/check/fuzzer_corpus/fbd855380068533d0148c6011a302bf07cae0612 ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE // CHECK:STDOUT: result: 0 package ExplorerTest; fn id[T:! type](x: T) -> T { return x; } fn Main() -> i32 { return id(0); } ================================================ FILE: toolchain/check/fuzzer_corpus/fc2e6307261474cbadb9046919ff50710904d4d5 ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE // CHECK:STDOUT: result: 0 package ExplorerTest; fn Main() -> i32 { var s: String = ''' A block string literal \''' '''; if (s == "A block string literal\n'''\n") { return 0; } else { return 1; } } ================================================ FILE: toolchain/check/fuzzer_corpus/fc927b8e82af4dcdaa2495e28ff7269c43456ff2 ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE // CHECK:STDOUT: result: 5 package ExplorerTest; interface ConvertsFromInt { require i32 impls ImplicitAs(Self); } fn ConvertIntTo(T:! ConvertsFromInt, n: i32) -> T { return n; } class IntHolder { var n: i32; } impl i32 as ImplicitAs(IntHolder) { fn Convert[self: Self]() -> IntHolder { return {.n = self}; } } impl IntHolder as ConvertsFromInt {} fn Main() -> i32 { return ConvertIntTo(IntHolder, 5).n; } ================================================ FILE: toolchain/check/fuzzer_corpus/fdc040e7e474c3ec5da7044a80e2df6bfcb80917 ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE // CHECK:STDOUT: result: 0 package ExplorerTest; class Point { var x: i32; var y: i32; } fn Main() -> i32 { var p1: Point = {.x = 1, .y = 2}; var p2: auto = p1; p2.x = 3; return p1.x - 1; } ================================================ FILE: toolchain/check/generic.cpp ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #include "toolchain/check/generic.h" #include #include "toolchain/base/kind_switch.h" #include "toolchain/check/diagnostic_helpers.h" #include "toolchain/check/eval.h" #include "toolchain/check/generic_region_stack.h" #include "toolchain/check/inst.h" #include "toolchain/check/subst.h" #include "toolchain/check/type.h" #include "toolchain/check/type_completion.h" #include "toolchain/diagnostics/diagnostic.h" #include "toolchain/sem_ir/constant.h" #include "toolchain/sem_ir/generic.h" #include "toolchain/sem_ir/ids.h" #include "toolchain/sem_ir/inst.h" #include "toolchain/sem_ir/typed_insts.h" namespace Carbon::Check { CARBON_DEFINE_ENUM_MASK_NAMES(DependentInstKind) { CARBON_DEPENDENT_INST_KIND(CARBON_ENUM_MASK_NAME_STRING) }; static auto MakeSelfSpecificId(Context& context, SemIR::GenericId generic_id) -> SemIR::SpecificId; // Get the current pending generic. If we have not yet allocated a `GenericId` // for it, do so now. static auto GetOrCreatePendingGeneric(Context& context) -> GenericRegionStack::PendingGeneric { auto pending_generic = context.generic_region_stack().PeekPendingGeneric(); if (!pending_generic.generic_id.has_value()) { // Allocate a placeholder generic now to form a generic ID. This generic // will be populated once we reach the end of the generic declaration. pending_generic.generic_id = context.generics().Add( SemIR::Generic{.decl_id = SemIR::InstId::None, .bindings_id = SemIR::InstBlockId::None, .self_specific_id = SemIR::SpecificId::None}); context.generic_region_stack().SetPendingGenericId( pending_generic.generic_id); } return pending_generic; } // Adds an instruction `generic_inst_id` to the eval block for the current // generic region. The instruction `generic_inst_id` is expected to compute the // value of the constant described by `const_inst_id` in each specific. Forms // and returns a corresponding symbolic constant ID that refers to the // substituted value of that instruction in each specific. static auto AddGenericConstantInstToEvalBlock( Context& context, SemIR::InstId const_inst_id, SemIR::InstId generic_inst_id, SemIR::ConstantDependence dependence) -> SemIR::ConstantId { auto [generic_id, region] = GetOrCreatePendingGeneric(context); auto index = SemIR::GenericInstIndex( region, context.generic_region_stack().PeekEvalBlock().size()); context.generic_region_stack().AddInstToEvalBlock(generic_inst_id); return context.constant_values().AddSymbolicConstant( {.inst_id = const_inst_id, .generic_id = generic_id, .index = index, .dependence = dependence}); } namespace { // Substitution callbacks to rebuild a generic constant in the eval block for a // generic region. class RebuildGenericConstantInEvalBlockCallbacks : public SubstInstCallbacks { public: // `context` must not be null. RebuildGenericConstantInEvalBlockCallbacks(Context* context, SemIR::LocId loc_id) : SubstInstCallbacks(context), loc_id_(loc_id), constants_in_generic_( context->generic_region_stack().PeekConstantsInGenericMap()) {} auto RebuildType(SemIR::TypeInstId type_inst_id) const -> SemIR::TypeId override { // When building instructions in the eval block, form attached types. return context().types().GetTypeIdForTypeConstantId( context().constant_values().GetAttached(type_inst_id)); } // Check for instructions for which we already have a mapping into the eval // block, and substitute them with the instructions in the eval block. auto Subst(SemIR::InstId& inst_id) -> SubstResult override { auto const_id = context().constant_values().Get(inst_id); if (!const_id.has_value()) { // An unloaded import ref should never contain anything we need to // substitute into. Don't trigger loading it here. CARBON_CHECK( context().insts().Is(inst_id), "Substituting into instruction with invalid constant ID: {0}", context().insts().Get(inst_id)); return SubstResult::FullySubstituted; } if (!context().constant_values().DependsOnGenericParameter(const_id)) { // This instruction doesn't have a symbolic constant value, so can't // contain any bindings that need to be substituted. return SubstResult::FullySubstituted; } // If this constant value has a defining instruction in the eval block, // replace the instruction in the body of the generic with the one from the // eval block. if (auto result = constants_in_generic_.Lookup( context().constant_values().GetInstId(const_id))) { inst_id = result.value(); return SubstResult::FullySubstituted; } return SubstResult::SubstOperands; } // Build a new instruction in the eval block corresponding to the given // constant. auto Rebuild(SemIR::InstId orig_inst_id, SemIR::Inst new_inst) -> SemIR::InstId override { auto& orig_symbolic_const = context().constant_values().GetSymbolicConstant( context().constant_values().Get(orig_inst_id)); auto const_inst_id = orig_symbolic_const.inst_id; auto dependence = orig_symbolic_const.dependence; // We might already have an instruction in the eval block if a transitive // operand of this instruction has the same constant value. auto result = constants_in_generic_.Insert(const_inst_id, [&] { // TODO: Add a function on `Context` to add the instruction without // inserting it into the dependent instructions list or computing a // constant value for it. // TODO: Is the location we pick here always appropriate for the new // instruction? auto inst_id = context().sem_ir().insts().AddInNoBlock( SemIR::LocIdAndInst::UncheckedLoc(loc_id_, new_inst)); auto const_id = AddGenericConstantInstToEvalBlock( context(), const_inst_id, inst_id, dependence); context().constant_values().Set(inst_id, const_id); return inst_id; }); return result.value(); } auto ReuseUnchanged(SemIR::InstId orig_inst_id) -> SemIR::InstId override { auto inst = context().insts().Get(orig_inst_id); CARBON_CHECK( (inst.IsOneOf()), "Instruction {0} has symbolic constant value but no symbolic operands", inst); // Rebuild the instruction anyway so that it's included in the eval block. // TODO: Can we just reuse the instruction in this case? return Rebuild(orig_inst_id, inst); } private: SemIR::LocId loc_id_; ConstantsInGenericMap& constants_in_generic_; }; // Substitution callbacks to rebuild a template action. This rebuilds the action // instruction in-place if it needs to be modified. class RebuildTemplateActionInEvalBlockCallbacks final : public RebuildGenericConstantInEvalBlockCallbacks { public: // `context` must not be null. RebuildTemplateActionInEvalBlockCallbacks(Context* context, SemIR::LocId loc_id, SemIR::InstId action_inst_id) : RebuildGenericConstantInEvalBlockCallbacks(context, loc_id), action_inst_id_(action_inst_id) {} auto Rebuild(SemIR::InstId orig_inst_id, SemIR::Inst new_inst) -> SemIR::InstId override { if (orig_inst_id == action_inst_id_) { // TODO: We want to ReplaceInstPreservingConstantValue here, but don't // want to evaluate the action to check the value hasn't changed. context().sem_ir().insts().Set(orig_inst_id, new_inst); return orig_inst_id; } return RebuildGenericConstantInEvalBlockCallbacks::Rebuild(orig_inst_id, new_inst); } auto ReuseUnchanged(SemIR::InstId orig_inst_id) -> SemIR::InstId override { if (orig_inst_id == action_inst_id_) { return orig_inst_id; } return RebuildGenericConstantInEvalBlockCallbacks::ReuseUnchanged( orig_inst_id); } private: SemIR::InstId action_inst_id_; }; } // namespace // Adds instructions to compute the substituted version of `type_id` in each // specific into the eval block for the current generic region. Returns a // symbolic type ID that refers to the substituted type in each specific. static auto AddGenericTypeToEvalBlock(Context& context, SemIR::LocId loc_id, SemIR::TypeId type_id) -> SemIR::TypeId { // Substitute into the type's constant instruction and rebuild it in the eval // block. auto rebuild_generic_constant_callbacks = RebuildGenericConstantInEvalBlockCallbacks(&context, loc_id); auto type_inst_id = SubstInst(context, context.types().GetTypeInstId(type_id), rebuild_generic_constant_callbacks); return context.types().GetTypeIdForTypeConstantId( context.constant_values().GetAttached(type_inst_id)); } // Adds instructions to compute the substituted value of `inst_id` in each // specific into the eval block for the current generic region. Returns a // symbolic constant instruction ID that refers to the substituted constant // value in each specific. static auto AddGenericConstantToEvalBlock(Context& context, SemIR::InstId inst_id) -> SemIR::ConstantId { CARBON_CHECK(context.constant_values().Get(inst_id).is_symbolic(), "Adding generic constant {0} with non-symbolic value {1}", context.insts().Get(inst_id), context.constant_values().Get(inst_id)); // Substitute into the constant value and rebuild it in the eval block if // we've not encountered it before. auto const_inst_id = context.constant_values().GetConstantInstId(inst_id); auto callbacks = RebuildGenericConstantInEvalBlockCallbacks( &context, SemIR::LocId(inst_id)); auto new_inst_id = SubstInst(context, const_inst_id, callbacks); CARBON_CHECK(new_inst_id != const_inst_id, "No substitutions performed for generic constant {0}", context.insts().Get(inst_id)); return context.constant_values().GetAttached(new_inst_id); } // Adds an instruction that performs a template action to the eval block for the // generic. The instruction should not yet have been added to any block. The // instruction might refer to types and constants that need to be rewritten, so // substitute into it first. static auto AddTemplateActionToEvalBlock(Context& context, SemIR::InstId inst_id) -> void { // Substitute into the constant value and rebuild it in the eval block. auto rebuild_template_action_callbacks = RebuildTemplateActionInEvalBlockCallbacks(&context, SemIR::LocId(inst_id), inst_id); auto new_inst_id = SubstInst(context, inst_id, rebuild_template_action_callbacks); CARBON_CHECK(new_inst_id == inst_id, "Substitution changed InstId of template action"); context.generic_region_stack().PeekConstantsInGenericMap().Insert(inst_id, inst_id); // Add the action to the eval block and point its constant value back to its // index within the block. auto [generic_id, region] = GetOrCreatePendingGeneric(context); auto& symbolic_constant = context.constant_values().GetSymbolicConstant( context.constant_values().GetAttached(inst_id)); symbolic_constant.generic_id = generic_id; symbolic_constant.index = SemIR::GenericInstIndex( region, context.generic_region_stack().PeekEvalBlock().size()); context.generic_region_stack().AddInstToEvalBlock(inst_id); } // Populates a map of constants in a generic from the constants in the // declaration region, in preparation for building the definition region. static auto PopulateConstantsFromDeclaration( Context& context, SemIR::GenericId generic_id, ConstantsInGenericMap& constants_in_generic) { // For the definition region, populate constants from the declaration. auto decl_eval_block = context.inst_blocks().Get( context.generics().Get(generic_id).decl_block_id); constants_in_generic.GrowForInsertCount(decl_eval_block.size()); for (auto inst_id : decl_eval_block) { auto const_inst_id = context.constant_values().GetConstantInstId(inst_id); auto result = constants_in_generic.Insert(const_inst_id, inst_id); CARBON_CHECK(result.is_inserted(), "Duplicate constant in generic decl eval block: {0}", context.insts().Get(const_inst_id)); } } auto AttachDependentInstToCurrentGeneric(Context& context, DependentInst dependent_inst) -> void { auto [inst_id, dep_kind] = dependent_inst; // If we don't have a generic region here, leave the dependent instruction // unattached. This happens for out-of-line redeclarations of members of // dependent scopes: // // class A(T:! type) { // fn F(); // } // // Has generic type and constant value, but no generic region. // fn A(T:! type).F() {} // // TODO: Copy the attached type and constant value from the previous // declaration in this case instead of attempting to attach the new // declaration to a generic region that we're no longer within. if (context.generic_region_stack().Empty()) { // This should only happen for `*Decl` instructions, never for template // actions. CARBON_CHECK(!dep_kind.HasAnyOf(DependentInstKind::Template)); return; } context.generic_region_stack().AddDependentInst(dependent_inst.inst_id); // If the type is symbolic, replace it with a type specific to this generic. if (dep_kind.HasAnyOf(DependentInstKind::SymbolicType)) { auto inst = context.insts().Get(inst_id); auto type_id = AddGenericTypeToEvalBlock(context, SemIR::LocId(inst_id), inst.type_id()); // TODO: Eventually, completeness requirements should be modeled as // constraints on the generic rather than properties of the type. For now, // require the transformed type to be complete if the original was. if (context.types().IsComplete(inst.type_id())) { CompleteTypeOrCheckFail(context, type_id); } inst.SetType(type_id); context.sem_ir().insts().Set(inst_id, inst); } // If the instruction has a symbolic constant value, then make a note that // we'll need to evaluate this instruction when forming the specific. Update // the constant value of the instruction to refer to the result of that // eventual evaluation. if (dep_kind.HasAnyOf(DependentInstKind::SymbolicConstant)) { // Update the constant value to refer to this generic. context.constant_values().Set( inst_id, AddGenericConstantToEvalBlock(context, inst_id)); } // If the instruction is a template action, add it directly to this position // in the eval block. if (dep_kind.HasAnyOf(DependentInstKind::Template)) { AddTemplateActionToEvalBlock(context, inst_id); } } // Builds and returns a block of instructions whose constant values need to be // evaluated in order to resolve a generic to a specific. static auto MakeGenericEvalBlock(Context& context) -> SemIR::InstBlockId { return context.inst_blocks().Add( context.generic_region_stack().PeekEvalBlock()); } // Builds and returns an eval block, given the list of canonical symbolic // constants that the instructions in the eval block should produce. This is // used when importing a generic. auto RebuildGenericEvalBlock(Context& context, SemIR::GenericId generic_id, SemIR::GenericInstIndex::Region region, llvm::ArrayRef const_ids) -> SemIR::InstBlockId { context.generic_region_stack().Push( {.generic_id = generic_id, .region = region}); auto& constants_in_generic = context.generic_region_stack().PeekConstantsInGenericMap(); // For the definition region, populate constants from the declaration. if (region == SemIR::GenericInstIndex::Definition) { PopulateConstantsFromDeclaration(context, generic_id, constants_in_generic); } constants_in_generic.GrowForInsertCount(const_ids.size()); for (auto [i, inst_id] : llvm::enumerate(const_ids)) { // Build a constant in the inst block. AddGenericConstantToEvalBlock(context, inst_id); CARBON_CHECK(context.generic_region_stack().PeekEvalBlock().size() == i + 1, "Produced {0} instructions when importing {1}", (context.generic_region_stack().PeekEvalBlock().size() - i), context.insts().Get(inst_id)); } auto eval_block_id = MakeGenericEvalBlock(context); context.generic_region_stack().Pop(); return eval_block_id; } auto StartGenericDecl(Context& context) -> void { context.generic_region_stack().Push( {.generic_id = SemIR::GenericId::None, .region = SemIR::GenericInstIndex::Declaration}); } auto StartGenericDefinition(Context& context, SemIR::GenericId generic_id) -> void { // Push a generic region even if we don't have a generic_id. We might still // have locally-introduced generic parameters to track: // // fn F() { // let T:! type = i32; // var x: T; // } context.generic_region_stack().Push( {.generic_id = generic_id, .region = SemIR::GenericInstIndex::Definition}); if (generic_id.has_value()) { PopulateConstantsFromDeclaration( context, generic_id, context.generic_region_stack().PeekConstantsInGenericMap()); } } auto DiscardGenericDecl(Context& context) -> void { // Unattach any types and constant values we might have created in the // generic. for (auto inst_id : context.generic_region_stack().PeekDependentInsts()) { // Note that `Get` returns an instruction with an unattached type. context.sem_ir().insts().Set(inst_id, context.insts().Get(inst_id)); // Note that `Get` returns an unattached constant. context.constant_values().Set(inst_id, context.constant_values().Get(inst_id)); } // Note that we may leak a GenericId here, if one was allocated. context.generic_region_stack().Pop(); } auto BuildGeneric(Context& context, SemIR::InstId decl_id) -> SemIR::GenericId { auto all_bindings = context.scope_stack().compile_time_binding_stack().PeekAllValues(); if (all_bindings.empty()) { CARBON_CHECK(context.generic_region_stack().PeekEvalBlock().empty(), "Have non-empty eval block {0} in declaration {1} but no " "compile time bindings are in scope.", context.insts().Get( context.generic_region_stack().PeekEvalBlock().front()), context.insts().Get(decl_id)); DiscardGenericDecl(context); return SemIR::GenericId::None; } // Build the new Generic object. Note that we intentionally do not hold a // persistent reference to it throughout this function, because the `generics` // collection can have items added to it by import resolution while we are // building this generic. auto bindings_id = context.inst_blocks().Add(all_bindings); SemIR::Generic generic = {.decl_id = decl_id, .bindings_id = bindings_id, .self_specific_id = SemIR::SpecificId::None}; // Get the generic ID, or allocate one now if we don't have one yet. That // could happen if the eval block is empty. auto generic_id = context.generic_region_stack().PeekPendingGeneric().generic_id; if (!generic_id.has_value()) { CARBON_CHECK(context.generic_region_stack().PeekEvalBlock().empty(), "Non-empty eval block but didn't yet allocate a GenericId"); generic_id = context.generics().Add(generic); context.generic_region_stack().SetPendingGenericId(generic_id); } else { CARBON_CHECK(!context.generics().Get(generic_id).decl_id.has_value(), "Built generic {0} twice", generic_id); context.generics().Get(generic_id) = generic; } auto self_specific_id = MakeSelfSpecificId(context, generic_id); context.generics().Get(generic_id).self_specific_id = self_specific_id; return generic_id; } auto FinishGenericDecl(Context& context, SemIR::LocId loc_id, SemIR::GenericId generic_id) -> void { if (!generic_id.has_value()) { return; } auto decl_block_id = MakeGenericEvalBlock(context); context.generic_region_stack().Pop(); context.generics().Get(generic_id).decl_block_id = decl_block_id; ResolveSpecificDecl(context, loc_id, context.generics().GetSelfSpecific(generic_id)); } auto BuildGenericDecl(Context& context, SemIR::InstId decl_id) -> SemIR::GenericId { SemIR::GenericId generic_id = BuildGeneric(context, decl_id); if (generic_id.has_value()) { FinishGenericDecl(context, SemIR::LocId(decl_id), generic_id); } return generic_id; } // Returns the first difference between the two given eval blocks. static auto FirstDifferenceBetweenEvalBlocks( Context& context, llvm::ArrayRef old_eval_block, llvm::ArrayRef new_eval_block) -> std::pair { // Check each element of the eval block computes the same unattached constant. for (auto [old_inst_id, new_inst_id] : llvm::zip(old_eval_block, new_eval_block)) { auto old_const_id = context.constant_values().Get(old_inst_id); auto new_const_id = context.constant_values().Get(new_inst_id); if (old_const_id != new_const_id) { if (old_const_id.is_symbolic() && new_const_id.is_symbolic() && context.constant_values().GetDependence(old_const_id) == SemIR::ConstantDependence::Template && context.constant_values().GetDependence(new_const_id) == SemIR::ConstantDependence::Template && context.insts().Get(old_inst_id).kind() == context.insts().Get(new_inst_id).kind()) { // TODO: We don't have a good mechanism to compare template constants // because they canonicalize to themselves, so just assume this is OK. continue; } // These constant values differ unexpectedly. return {old_inst_id, new_inst_id}; } } if (old_eval_block.size() < new_eval_block.size()) { return {SemIR::InstId::None, new_eval_block[old_eval_block.size()]}; } if (old_eval_block.size() > new_eval_block.size()) { return {old_eval_block[new_eval_block.size()], SemIR::InstId::None}; } return {SemIR::InstId::None, SemIR::InstId::None}; } // If `constant_id` refers to a symbolic constant within the declaration region // of `generic_id`, remap it to refer to the constant value of the corresponding // element in the given eval block. Otherwise returns the ID unchanged. static auto ReattachConstant(Context& context, SemIR::GenericId generic_id, llvm::ArrayRef eval_block, SemIR::ConstantId constant_id) -> SemIR::ConstantId { if (!constant_id.has_value() || !constant_id.is_symbolic()) { return constant_id; } auto& symbolic_const = context.constant_values().GetSymbolicConstant(constant_id); if (symbolic_const.generic_id != generic_id) { // Constant doesn't refer into this generic. return constant_id; } CARBON_CHECK( symbolic_const.index.region() == SemIR::GenericInstIndex::Declaration, "Definition region of redeclaration should not be referenced"); return context.constant_values().GetAttached( eval_block[symbolic_const.index.index()]); } // Same as `ReattachConstant` but for a type. static auto ReattachType(Context& context, SemIR::GenericId generic_id, llvm::ArrayRef eval_block, SemIR::TypeId type_id) -> SemIR::TypeId { return context.types().GetTypeIdForTypeConstantId(ReattachConstant( context, generic_id, eval_block, context.types().GetConstantId(type_id))); } auto FinishGenericRedecl(Context& context, SemIR::GenericId generic_id) -> void { if (!generic_id.has_value()) { DiscardGenericDecl(context); return; } // Find the old and new eval blocks. auto old_eval_block_id = context.generics() .Get(generic_id) .GetEvalBlock(SemIR::GenericInstIndex::Declaration); CARBON_CHECK(old_eval_block_id.has_value(), "Old generic is not fully declared"); auto old_eval_block = context.inst_blocks().Get(old_eval_block_id); auto new_eval_block = context.generic_region_stack().PeekEvalBlock(); // Check the eval blocks are computing the same constants in the same order. // This should always be the case because we have already verified they have // the same parse tree, and the poisoning rules mean that all entities they // refer to are also the same. // // Note that it's OK if the first difference is that an old instruction has no // corresponding new instruction; we wouldn't have used that anyway. This // happens for `ImplDecl`, for which the witness is included in the eval block // of the first declaration. if (auto [old_inst_id, new_inst_id] = FirstDifferenceBetweenEvalBlocks( context, old_eval_block, new_eval_block); new_inst_id.has_value()) { // This shouldn't be possible: we should have already checked that the // syntax of the redeclaration matches the prior declaration, and none of // the name lookups or semantic checks should be allowed to differ between // the two declarations, so we should have built the same eval block as in // the prior declaration. // // However, that isn't a strong enough invariant that it seems appropriate // to CHECK-fail here, so we produce a diagnostic with context.TODO() // instead. // // TODO: Add something like context.UNEXPECTED() instead of using // context.TODO() here because there's not really anything to do. context.TODO(new_inst_id, "generic redeclaration differs from previous declaration"); if (old_inst_id.has_value()) { context.TODO(old_inst_id, "instruction in previous declaration"); } DiscardGenericDecl(context); return; } auto redecl_generic_id = context.generic_region_stack().PeekPendingGeneric().generic_id; // Reattach any instructions that depend on the redeclaration to instead refer // to the original. for (auto inst_id : context.generic_region_stack().PeekDependentInsts()) { // Reattach the type. auto inst = context.insts().GetWithAttachedType(inst_id); inst.SetType(ReattachType(context, redecl_generic_id, old_eval_block, inst.type_id())); context.sem_ir().insts().Set(inst_id, inst); // Reattach the constant value. context.constant_values().Set( inst_id, ReattachConstant(context, redecl_generic_id, old_eval_block, context.constant_values().GetAttached(inst_id))); } context.generic_region_stack().Pop(); } auto FinishGenericDefinition(Context& context, SemIR::GenericId generic_id) -> void { if (!generic_id.has_value()) { DiscardGenericDecl(context); return; } auto definition_block_id = MakeGenericEvalBlock(context); context.generic_region_stack().Pop(); context.generics().Get(generic_id).definition_block_id = definition_block_id; } auto ResolveSpecificDecl(Context& context, SemIR::LocId loc_id, SemIR::SpecificId specific_id) -> void { // If this is the first time we've formed this specific, evaluate its decl // block to form information about the specific. auto& specific = context.specifics().Get(specific_id); if (!specific.decl_block_id.has_value()) { // Set a placeholder value as the decl block ID so we won't attempt to // recursively resolve the same specific. specific.decl_block_id = SemIR::InstBlockId::Empty; // TODO: Store in the specific whether the declaration contains any // ErrorInst values. specific.decl_block_id = TryEvalBlockForSpecific(context, loc_id, specific_id, SemIR::GenericInstIndex::Region::Declaration); } } auto MakeSpecific(Context& context, SemIR::LocId loc_id, SemIR::GenericId generic_id, SemIR::InstBlockId args_id) -> SemIR::SpecificId { auto specific_id = context.specifics().GetOrAdd(generic_id, args_id); ResolveSpecificDecl(context, loc_id, specific_id); return specific_id; } auto MakeSpecific(Context& context, SemIR::LocId loc_id, SemIR::GenericId generic_id, llvm::ArrayRef args) -> SemIR::SpecificId { auto args_id = context.inst_blocks().AddCanonical(args); return MakeSpecific(context, loc_id, generic_id, args_id); } static auto MakeSelfSpecificId(Context& context, SemIR::GenericId generic_id) -> SemIR::SpecificId { if (!generic_id.has_value()) { return SemIR::SpecificId::None; } auto& generic = context.generics().Get(generic_id); auto args = context.inst_blocks().Get(generic.bindings_id); // Form a canonical argument list for the generic. llvm::SmallVector arg_ids; arg_ids.reserve(args.size()); for (auto arg_id : args) { arg_ids.push_back(context.constant_values().GetConstantInstId(arg_id)); } auto args_id = context.inst_blocks().AddCanonical(arg_ids); return context.specifics().GetOrAdd(generic_id, args_id); } auto MakeSelfSpecific(Context& context, SemIR::LocId loc_id, SemIR::GenericId generic_id) -> SemIR::SpecificId { // Build a corresponding specific. SemIR::SpecificId specific_id = MakeSelfSpecificId(context, generic_id); // TODO: This could be made more efficient. We don't need to perform // substitution here; we know we want identity mappings for all constants and // types. We could also consider not storing the mapping at all in this case. ResolveSpecificDecl(context, loc_id, specific_id); return specific_id; } auto ResolveSpecificDefinition(Context& context, SemIR::LocId loc_id, SemIR::SpecificId specific_id) -> bool { // TODO: Handle recursive resolution of the same generic definition. auto& specific = context.specifics().Get(specific_id); auto generic_id = specific.generic_id; CARBON_CHECK(generic_id.has_value(), "Specific with no generic ID"); if (!specific.definition_block_id.has_value()) { // Evaluate the eval block for the definition of the generic. auto& generic = context.generics().Get(generic_id); CARBON_CHECK(generic.decl_block_id.has_value(), "missing declaration"); if (!generic.definition_block_id.has_value()) { // The generic is not defined yet. return false; } // TODO: Store in the specific whether the definition contains any ErrorInst // values. specific.definition_block_id = TryEvalBlockForSpecific( context, loc_id, specific_id, SemIR::GenericInstIndex::Definition); } return true; } auto DiagnoseIfGenericMissingExplicitParameters( Context& context, const SemIR::EntityWithParamsBase& entity_base) -> void { if (!entity_base.implicit_param_patterns_id.has_value() || entity_base.param_patterns_id.has_value()) { return; } CARBON_DIAGNOSTIC(GenericMissingExplicitParameters, Error, "expected explicit parameters after implicit parameters"); context.emitter().Emit(entity_base.last_param_node_id, GenericMissingExplicitParameters); } static auto ValidateGenericWithoutAndWithSelfMatch( Context& context, SemIR::GenericId generic_without_self_id, SemIR::GenericId generic_with_self_id, SemIR::SpecificId specific_without_self_id) -> void { CARBON_CHECK( generic_without_self_id.has_value() == specific_without_self_id.has_value(), "Have a generic-without-self {0} but no specific-without-self {1} or " "vice-versa", generic_without_self_id, specific_without_self_id); CARBON_CHECK( generic_with_self_id.has_value(), "Missing a generic ID for generic-with-self that should always exist."); const auto& generic_with_self = context.generics().Get(generic_with_self_id); auto generic_with_self_decl = context.insts().Get(generic_with_self.decl_id); CARBON_CHECK( (generic_with_self_decl.IsOneOf()), "generic-with-self {0} should be a generic for an " "InterfaceWithSelfDecl or NamedConstraintWithSelfDecl, found {1}", generic_with_self, generic_with_self_decl); if (!generic_without_self_id.has_value()) { return; } const auto& generic_without_self = context.generics().Get(generic_without_self_id); const auto& specific_without_self = context.specifics().Get(specific_without_self_id); CARBON_CHECK(specific_without_self.generic_id == generic_without_self_id, "specific-without-self {0} is not a specific for the " "generic-without-self {1}", specific_without_self, generic_without_self); auto generic_without_self_decl = context.insts().Get(generic_without_self.decl_id); CARBON_KIND_SWITCH(generic_without_self_decl) { case CARBON_KIND(SemIR::InterfaceDecl without_self_decl): { auto with_self_decl = generic_with_self_decl.As(); CARBON_CHECK( without_self_decl.interface_id == with_self_decl.interface_id, "Found generic-without-self for interface {0}, and generic-with-self " "for interface {1}; expected the same interface for both", context.interfaces().Get(without_self_decl.interface_id), context.interfaces().Get(with_self_decl.interface_id)); break; } case CARBON_KIND(SemIR::NamedConstraintDecl without_self_decl): { auto with_self_decl = generic_with_self_decl.As(); CARBON_CHECK( without_self_decl.named_constraint_id == with_self_decl.named_constraint_id, "Found generic-without-self for constraint {0}, and " "generic-with-self for named constraint {1}; expected the same named " "constraint for both", context.named_constraints().Get( without_self_decl.named_constraint_id), context.named_constraints().Get(with_self_decl.named_constraint_id)); break; } default: CARBON_FATAL( "generic-without-self {0} should be a generic for an InterfaceDecl " "or NamedConstraintDecl, found {1}", generic_without_self, generic_without_self_decl); } } auto MakeSpecificWithInnerSelf(Context& context, SemIR::LocId loc_id, SemIR::GenericId generic_without_self_id, SemIR::GenericId generic_with_self_id, SemIR::SpecificId specific_without_self_id, SemIR::ConstantId self_facet) -> SemIR::SpecificId { ValidateGenericWithoutAndWithSelfMatch(context, generic_without_self_id, generic_with_self_id, specific_without_self_id); auto outer_args_id = context.specifics().GetArgsOrEmpty(specific_without_self_id); auto outer_args = context.inst_blocks().Get(outer_args_id); llvm::SmallVector args; args.reserve(outer_args.size() + 1); llvm::append_range(args, outer_args); if (self_facet == SemIR::ErrorInst::ConstantId) { args.push_back(SemIR::ErrorInst::InstId); } else { auto self_facet_inst_id = context.constant_values().GetInstId(self_facet); CARBON_CHECK(context.types().Is( context.insts().Get(self_facet_inst_id).type_id())); args.push_back(self_facet_inst_id); } auto specific_id = MakeSpecific(context, loc_id, generic_with_self_id, args); ResolveSpecificDefinition(context, loc_id, specific_id); return specific_id; } auto CopySpecificToGeneric(Context& context, SemIR::LocId loc_id, SemIR::SpecificId specific_id, SemIR::GenericId target_generic_id) -> SemIR::SpecificId { if (!specific_id.has_value()) { const auto& target_generic = context.generics().Get(target_generic_id); auto target_bindings = context.inst_blocks().Get(target_generic.bindings_id); CARBON_CHECK(target_bindings.empty()); return SemIR::SpecificId::None; } const auto& specific = context.specifics().Get(specific_id); auto source_generic_id = specific.generic_id; const auto& source_generic = context.generics().Get(source_generic_id); const auto& target_generic = context.generics().Get(target_generic_id); auto source_bindings = context.inst_blocks().Get(source_generic.bindings_id); auto target_bindings = context.inst_blocks().Get(target_generic.bindings_id); for (auto [source, target] : llvm::zip_equal(source_bindings, target_bindings)) { CARBON_CHECK(context.constant_values().Get(source) == context.constant_values().Get(target)); } auto args_id = context.specifics().GetArgsOrEmpty(specific_id); return MakeSpecific(context, loc_id, target_generic_id, args_id); } } // namespace Carbon::Check ================================================ FILE: toolchain/check/generic.h ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #ifndef CARBON_TOOLCHAIN_CHECK_GENERIC_H_ #define CARBON_TOOLCHAIN_CHECK_GENERIC_H_ #include "common/enum_mask_base.h" #include "toolchain/check/context.h" #include "toolchain/sem_ir/entity_with_params_base.h" #include "toolchain/sem_ir/ids.h" namespace Carbon::Check { // Start processing a declaration or definition that might be a generic entity. auto StartGenericDecl(Context& context) -> void; // Start processing a declaration or definition that might be a generic entity. auto StartGenericDefinition(Context& context, SemIR::GenericId generic_id) -> void; #define CARBON_DEPENDENT_INST_KIND(X) \ /* The type of the instruction depends on a checked generic parameter. */ \ X(SymbolicType) \ /* The constant value of the instruction depends on a checked generic \ * parameter. */ \ X(SymbolicConstant) \ X(Template) CARBON_DEFINE_RAW_ENUM_MASK(DependentInstKind, uint8_t) { CARBON_DEPENDENT_INST_KIND(CARBON_RAW_ENUM_MASK_ENUMERATOR) }; // Represents a set of keyword modifiers, using a separate bit per modifier. class DependentInstKind : public CARBON_ENUM_MASK_BASE(DependentInstKind) { public: CARBON_DEPENDENT_INST_KIND(CARBON_ENUM_MASK_CONSTANT_DECL) }; #define CARBON_DEPENDENT_INST_KIND_WITH_TYPE(X) \ CARBON_ENUM_MASK_CONSTANT_DEFINITION(DependentInstKind, X) CARBON_DEPENDENT_INST_KIND(CARBON_DEPENDENT_INST_KIND_WITH_TYPE) #undef CARBON_DEPENDENT_INST_KIND_WITH_TYPE // An instruction that depends on a generic parameter in some way. struct DependentInst { SemIR::InstId inst_id; DependentInstKind kind; }; // Attach a dependent instruction to the current generic, updating its type and // constant value as necessary. auto AttachDependentInstToCurrentGeneric(Context& context, DependentInst dependent_inst) -> void; // Discard the information about the current generic entity. This should be // called instead of `FinishGenericDecl` if the corresponding `Generic` object // would not actually be used, or when recovering from an error. auto DiscardGenericDecl(Context& context) -> void; // Finish processing a potentially generic declaration and produce a // corresponding generic object. Returns SemIR::GenericId::None if this // declaration is not actually generic. auto BuildGeneric(Context& context, SemIR::InstId decl_id) -> SemIR::GenericId; // Builds eval block for the declaration. auto FinishGenericDecl(Context& context, SemIR::LocId loc_id, SemIR::GenericId generic_id) -> void; // BuildGeneric() and FinishGenericDecl() combined. Normally you would call this // function unless the caller has work to do between the two steps. auto BuildGenericDecl(Context& context, SemIR::InstId decl_id) -> SemIR::GenericId; // Merge a redeclaration of an entity that might be a generic into the original // declaration. auto FinishGenericRedecl(Context& context, SemIR::GenericId generic_id) -> void; // Finish processing a potentially generic definition. auto FinishGenericDefinition(Context& context, SemIR::GenericId generic_id) -> void; // Builds and returns an eval block, given the list of canonical symbolic // constants that the instructions in the eval block should produce. This is // used when importing a generic. auto RebuildGenericEvalBlock(Context& context, SemIR::GenericId generic_id, SemIR::GenericInstIndex::Region region, llvm::ArrayRef const_ids) -> SemIR::InstBlockId; // Builds a new specific with a given argument list, or finds an existing one if // this generic has already been referenced with these arguments. Performs // substitution into the declaration, but not the definition, of the generic. auto MakeSpecific(Context& context, SemIR::LocId loc_id, SemIR::GenericId generic_id, llvm::ArrayRef args) -> SemIR::SpecificId; // Builds a new specific or finds an existing one in the case where the argument // list has already been converted into an instruction block. `args_id` should // be a canonical instruction block referring to constants. auto MakeSpecific(Context& context, SemIR::LocId loc_id, SemIR::GenericId generic_id, SemIR::InstBlockId args_id) -> SemIR::SpecificId; // Builds the specific that describes how the generic should refer to itself. // For example, for a generic `G(T:! type)`, this is the specific `G(T)`. If // `generic_id` is `None`, returns `None`. auto MakeSelfSpecific(Context& context, SemIR::LocId loc_id, SemIR::GenericId generic_id) -> SemIR::SpecificId; // Resolve the declaration of the given specific, by evaluating the eval block // of the corresponding generic and storing a corresponding value block in the // specific. auto ResolveSpecificDecl(Context& context, SemIR::LocId loc_id, SemIR::SpecificId specific_id) -> void; // Attempts to resolve the definition of the given specific, by evaluating the // eval block of the corresponding generic and storing a corresponding value // block in the specific. Returns false if a definition is not available. auto ResolveSpecificDefinition(Context& context, SemIR::LocId loc_id, SemIR::SpecificId specific_id) -> bool; // Diagnoses if an entity has implicit parameters, indicating it's generic, but // is missing explicit parameters. auto DiagnoseIfGenericMissingExplicitParameters( Context& context, const SemIR::EntityWithParamsBase& entity_base) -> void; // Given a generic and specific for an entity, construct the specific for the // inner generic-with-self. // // Interfaces and named constraints each have two generics. // * A regular outward facing generic which includes just the generic bindings // as written in the declaration. // * An inner generic-with-self which includes an additional generic binding of // the `Self` facet value. Associated entities are located inside this inner // generic-with-self. // // This function moves from a specific for the outer generic to a specific for // the inner generic-with-self. An entity which has no generic bindings will // have no outer generic-without-self and thus no specific-without-self, but // there is always an inner generic-with-self regardless, because of the // additional `Self` binding. // // If the generic-without-self has its definition completed, the resulting // specific will also. Note that during construction of an interface/constraint, // the definition cannot be complete yet. // // TODO: This should take a `diagnoser` parameter which is passed through to // MakeSpecific() and TryEvalBlockForSpecific(), so that monomorphization errors // get diagnosed to the correct semantic operation, instead of just to specific // instantiation. auto MakeSpecificWithInnerSelf(Context& context, SemIR::LocId loc_id, SemIR::GenericId generic_without_self_id, SemIR::GenericId generic_with_self_id, SemIR::SpecificId specific_without_self_id, SemIR::ConstantId self_facet) -> SemIR::SpecificId; // Copy the arguments of a specific into the context of another generic. The // target generic must have the exact same bindings as the specific's generic. // // TODO: This should take a `diagnoser` parameter which is passed through to // MakeSpecific() and TryEvalBlockForSpecific(), so that monomorphization errors // get diagnosed to the correct semantic operation, instead of just to specific // instantiation. auto CopySpecificToGeneric(Context& context, SemIR::LocId loc_id, SemIR::SpecificId specific_id, SemIR::GenericId target_generic_id) -> SemIR::SpecificId; } // namespace Carbon::Check #endif // CARBON_TOOLCHAIN_CHECK_GENERIC_H_ ================================================ FILE: toolchain/check/generic_region_stack.cpp ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #include "toolchain/check/generic_region_stack.h" #include "common/vlog.h" namespace Carbon::Check { auto GenericRegionStack::Push(PendingGeneric generic) -> void { CARBON_VLOG("GenericRegion Push: {0} {1}\n", generic.generic_id, generic.region); pending_generic_ids_.push_back(generic); pending_eval_block_stack_.PushArray(); dependent_inst_stack_.PushArray(); constants_in_generic_stack_.emplace_back(); } auto GenericRegionStack::Pop() -> void { auto pending = pending_generic_ids_.pop_back_val(); CARBON_VLOG("GenericRegion Pop: {0} {1}\n", pending.generic_id, pending.region); pending_eval_block_stack_.PopArray(); dependent_inst_stack_.PopArray(); constants_in_generic_stack_.pop_back(); } } // namespace Carbon::Check ================================================ FILE: toolchain/check/generic_region_stack.h ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #ifndef CARBON_TOOLCHAIN_CHECK_GENERIC_REGION_STACK_H_ #define CARBON_TOOLCHAIN_CHECK_GENERIC_REGION_STACK_H_ #include "common/array_stack.h" #include "common/map.h" #include "toolchain/sem_ir/ids.h" namespace Carbon::Check { // A map from an instruction ID representing a canonical symbolic constant to an // instruction within an eval block of the generic that computes the specific // value for that constant. // // We arbitrarily use a small size of 256 bytes for the map. // TODO: Determine a better number based on measurements. using ConstantsInGenericMap = Map; // A stack of enclosing regions that might be declaring or defining a generic // entity. In such a region, we track the generic constructs that are used, such // as symbolic constants and types, and instructions that depend on a template // parameter. // // We split a generic into two regions -- declaration and definition -- because // these are in general introduced separately, and substituted into separately. // For example, for `class C(T:! type, N:! T) { var x: T; }`, a use such as // `C(i32, 0)*` substitutes into just the declaration, whereas a use such as // `var x: C(i32, 0) = {.x = 0};` also substitutes into the definition. class GenericRegionStack { public: explicit GenericRegionStack(llvm::raw_ostream* vlog_stream) : vlog_stream_(vlog_stream) { // Reserve a large enough stack that we typically won't need to reallocate. constants_in_generic_stack_.reserve(4); } struct PendingGeneric { // The generic ID. May not have a value if no ID has been assigned yet. SemIR::GenericId generic_id; // The region of the generic that is being processed. SemIR::GenericInstIndex::Region region; }; // Pushes a region that might be declaring or defining a generic. auto Push(PendingGeneric generic) -> void; // Pops a generic region. auto Pop() -> void; // Returns whether the stack is empty. auto Empty() const -> bool { return pending_generic_ids_.empty(); } // Sets the GenericId for the currently pending generic, once one has been // allocated. auto SetPendingGenericId(SemIR::GenericId generic_id) -> void { CARBON_CHECK(!pending_generic_ids_.back().generic_id.has_value(), "Already have a GenericId for the pending generic"); pending_generic_ids_.back().generic_id = generic_id; } // Adds an instruction to the list of instructions whose type or value depends // on something in the current pending generic. auto AddDependentInst(SemIR::InstId inst_id) -> void { CARBON_CHECK(!Empty()); dependent_inst_stack_.AppendToTop(inst_id); } // Adds an instruction to the eval block for the current pending generic. auto AddInstToEvalBlock(SemIR::InstId inst_id) -> void { CARBON_CHECK(!Empty()); pending_eval_block_stack_.AppendToTop(inst_id); } // Returns the current pending generic. auto PeekPendingGeneric() const -> PendingGeneric { CARBON_CHECK(!Empty()); return pending_generic_ids_.back(); } // Returns the list of dependent instructions in the current generic region. auto PeekDependentInsts() -> llvm::ArrayRef { CARBON_CHECK(!Empty()); return dependent_inst_stack_.PeekArray(); } // Returns the contents of the eval block for the current generic region. auto PeekEvalBlock() -> llvm::ArrayRef { CARBON_CHECK(!Empty()); return pending_eval_block_stack_.PeekArray(); } // Returns the mapping from abstract constant instructions to eval block // instructions for the current generic. auto PeekConstantsInGenericMap() -> ConstantsInGenericMap& { CARBON_CHECK(!Empty()); return constants_in_generic_stack_.back(); } // Runs verification that the processing cleanly finished. auto VerifyOnFinish() const -> void { CARBON_CHECK(pending_generic_ids_.empty(), "pending_generic_ids_ still has {0} entries", pending_generic_ids_.size()); } private: // Whether to print verbose output. llvm::raw_ostream* vlog_stream_; // The IDs of pending generics. llvm::SmallVector pending_generic_ids_; // Contents of eval blocks for pending generics. ArrayStack pending_eval_block_stack_; // Instructions that depend on the current generic. ArrayStack dependent_inst_stack_; // Mapping from constant InstIds to the corresponding InstIds in the eval // blocks for each enclosing generic. We reserve this to a suitable size in // the constructor. llvm::SmallVector constants_in_generic_stack_; }; } // namespace Carbon::Check #endif // CARBON_TOOLCHAIN_CHECK_GENERIC_REGION_STACK_H_ ================================================ FILE: toolchain/check/global_init.cpp ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #include "toolchain/check/global_init.h" #include "toolchain/check/context.h" #include "toolchain/check/inst.h" namespace Carbon::Check { auto GlobalInit::Resume() -> void { context_->inst_block_stack().Push(block_id_, block_); } auto GlobalInit::Suspend() -> void { // TODO: Consider splicing together blocks in order to avoid sizable copies // here. auto contents = context_->inst_block_stack().PeekCurrentBlockContents(); block_.assign(contents.begin(), contents.end()); block_id_ = context_->inst_block_stack().PeekOrAdd(); context_->inst_block_stack().PopAndDiscard(); } auto GlobalInit::Finalize() -> void { // __global_init is only added if there are initialization instructions. if (block_.empty() && block_id_ == SemIR::InstBlockId::GlobalInit) { return; } Resume(); AddInst(*context_, Parse::NodeId::None, {}); // Pop the GlobalInit block here to finalize it. context_->inst_block_stack().Pop(); auto name_id = context_->sem_ir().identifiers().Add("__global_init"); context_->sem_ir().set_global_ctor_id(context_->sem_ir().functions().Add( {{.name_id = SemIR::NameId::ForIdentifier(name_id), .parent_scope_id = SemIR::NameScopeId::Package, .generic_id = SemIR::GenericId::None, .first_param_node_id = Parse::NodeId::None, .last_param_node_id = Parse::NodeId::None, .pattern_block_id = SemIR::InstBlockId::Empty, .implicit_param_patterns_id = SemIR::InstBlockId::None, .param_patterns_id = SemIR::InstBlockId::Empty, .is_extern = false, .extern_library_id = SemIR::LibraryNameId::None, .non_owning_decl_id = SemIR::InstId::None, .first_owning_decl_id = SemIR::InstId::None}, {.call_param_patterns_id = SemIR::InstBlockId::Empty, .call_params_id = SemIR::InstBlockId::Empty, .call_param_ranges = SemIR::Function::CallParamIndexRanges::Empty, .return_type_inst_id = SemIR::TypeInstId::None, .return_form_inst_id = SemIR::InstId::None, .return_patterns_id = SemIR::InstBlockId::None, .body_block_ids = {SemIR::InstBlockId::GlobalInit}}})); } } // namespace Carbon::Check ================================================ FILE: toolchain/check/global_init.h ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #ifndef CARBON_TOOLCHAIN_CHECK_GLOBAL_INIT_H_ #define CARBON_TOOLCHAIN_CHECK_GLOBAL_INIT_H_ #include "llvm/ADT/SmallVector.h" #include "toolchain/sem_ir/ids.h" namespace Carbon::Check { class Context; // Tracks state for global initialization. Handles should `Resume` when entering // an expression that's used for global init, and `Suspend` when the expression // is finished. Instructions in the middle will be tracked for the // `__global_init` function. class GlobalInit { public: explicit GlobalInit(Context* context) : context_(context) {} // Resumes adding instructions to global init. auto Resume() -> void; // Suspends adding instructions to global init. auto Suspend() -> void; // Finalizes the global initialization state, creating `__global_init` if // needed. Only called once at the end of checking. auto Finalize() -> void; private: // The associated context. Stored for convenience. Context* context_; // The currently suspended global init block. The value may change as a result // of control flow in initialization. SemIR::InstBlockId block_id_ = SemIR::InstBlockId::GlobalInit; // The contents for the currently suspended global init block. llvm::SmallVector block_; }; } // namespace Carbon::Check #endif // CARBON_TOOLCHAIN_CHECK_GLOBAL_INIT_H_ ================================================ FILE: toolchain/check/handle.h ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #ifndef CARBON_TOOLCHAIN_CHECK_HANDLE_H_ #define CARBON_TOOLCHAIN_CHECK_HANDLE_H_ #include "toolchain/check/context.h" #include "toolchain/check/deferred_definition_worklist.h" #include "toolchain/check/function.h" #include "toolchain/parse/node_ids.h" namespace Carbon::Check { // Parse node handlers. Returns false for unrecoverable errors. #define CARBON_PARSE_NODE_KIND(Name) \ auto HandleParseNode(Context& context, Parse::Name##Id node_id) -> bool; #include "toolchain/parse/node_kind.def" // Handle suspending the definition of a function. This is used for inline // methods, which are processed out of the normal lexical order. This plus // HandleFunctionDefinitionResume carry out the same actions as // HandleFunctionDefinitionStart, except that the various context stacks are // cleared out in between. auto HandleFunctionDefinitionSuspend(Context& context, Parse::FunctionDefinitionStartId node_id) -> DeferredDefinitionWorklist::SuspendedFunction; // Handle resuming the definition of a function, after a previous suspension. auto HandleFunctionDefinitionResume( Context& context, Parse::FunctionDefinitionStartId node_id, DeferredDefinitionWorklist::SuspendedFunction&& suspended_fn) -> void; } // namespace Carbon::Check #endif // CARBON_TOOLCHAIN_CHECK_HANDLE_H_ ================================================ FILE: toolchain/check/handle_alias.cpp ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #include "toolchain/check/context.h" #include "toolchain/check/generic.h" #include "toolchain/check/handle.h" #include "toolchain/check/inst.h" #include "toolchain/check/modifiers.h" #include "toolchain/check/name_component.h" #include "toolchain/parse/node_ids.h" #include "toolchain/sem_ir/ids.h" #include "toolchain/sem_ir/typed_insts.h" namespace Carbon::Check { auto HandleParseNode(Context& context, Parse::AliasIntroducerId /*node_id*/) -> bool { // Aliases can't be generic, but we might have parsed a generic parameter in // their name, so enter a generic scope just in case. StartGenericDecl(context); // Optional modifiers and the name follow. context.decl_introducer_state_stack().Push(); context.decl_name_stack().PushScopeAndStartName(); return true; } auto HandleParseNode(Context& /*context*/, Parse::AliasInitializerId /*node_id*/) -> bool { return true; } auto HandleParseNode(Context& context, Parse::AliasId /*node_id*/) -> bool { auto [expr_node, expr_id] = context.node_stack().PopExprWithNodeId(); auto name_context = context.decl_name_stack().FinishName( PopNameComponentWithoutParams(context, Lex::TokenKind::Alias)); DiscardGenericDecl(context); auto introducer = context.decl_introducer_state_stack().Pop(); LimitModifiersOnDecl(context, introducer, KeywordModifierSet::Access); auto entity_name_id = context.entity_names().Add( {.name_id = name_context.name_id_for_new_inst(), .parent_scope_id = name_context.parent_scope_id}); auto alias_type_id = SemIR::TypeId::None; auto alias_value_id = SemIR::InstId::None; if (auto inst = context.insts().TryGetAs(expr_id)) { // Pass through name references, albeit changing the name in use. alias_type_id = inst->type_id; alias_value_id = inst->value_id; } else if (auto inst = context.insts().TryGetAs(expr_id)) { // Treat type literals such as `type` or `bool` like name references. alias_type_id = inst->type_id; alias_value_id = inst->value_id; } else { CARBON_DIAGNOSTIC(AliasRequiresNameRef, Error, "alias initializer must be a name reference"); context.emitter().Emit(expr_node, AliasRequiresNameRef); alias_type_id = SemIR::ErrorInst::TypeId; alias_value_id = SemIR::ErrorInst::InstId; } auto alias_id = AddInst(context, name_context.loc_id, {.type_id = alias_type_id, .entity_name_id = entity_name_id, .value_id = alias_value_id}); // Add the name of the binding to the current scope. context.decl_name_stack().PopScope(); context.decl_name_stack().AddNameOrDiagnose( name_context, alias_id, introducer.modifier_set.GetAccessKind()); return true; } } // namespace Carbon::Check ================================================ FILE: toolchain/check/handle_array.cpp ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #include "toolchain/check/context.h" #include "toolchain/check/convert.h" #include "toolchain/check/handle.h" #include "toolchain/check/inst.h" #include "toolchain/check/type.h" #include "toolchain/parse/node_kind.h" namespace Carbon::Check { auto HandleParseNode(Context& /*context*/, Parse::ArrayExprOpenParenId /*node_id*/) -> bool { return true; } auto HandleParseNode(Context& /*context*/, Parse::ArrayExprKeywordId /*node_id*/) -> bool { return true; } auto HandleParseNode(Context& /*context*/, Parse::ArrayExprCommaId /*node_id*/) -> bool { return true; } auto HandleParseNode(Context& context, Parse::ArrayExprId node_id) -> bool { auto bound_inst_id = context.node_stack().PopExpr(); auto [element_type_node_id, element_type_inst_id] = context.node_stack().PopExprWithNodeId(); auto element_type = ExprAsType(context, element_type_node_id, element_type_inst_id); // The array bound must be a constant. Diagnose this prior to conversion // because conversion to `IntLiteral` will produce a generic "non-constant // call to compile-time-only function" error. // // TODO: Should we support runtime-phase bounds in cases such as: // comptime fn F(n: i32) -> type { return array(i32; n); } if (!context.constant_values().Get(bound_inst_id).is_constant()) { CARBON_DIAGNOSTIC(InvalidArrayExpr, Error, "array bound is not a constant"); context.emitter().Emit(bound_inst_id, InvalidArrayExpr); context.node_stack().Push(node_id, SemIR::ErrorInst::InstId); return true; } bound_inst_id = ConvertToValueOfType( context, SemIR::LocId(bound_inst_id), bound_inst_id, GetSingletonType(context, SemIR::IntLiteralType::TypeInstId)); AddInstAndPush( context, node_id, {.type_id = SemIR::TypeType::TypeId, .bound_id = bound_inst_id, .element_type_inst_id = element_type.inst_id}); return true; } } // namespace Carbon::Check ================================================ FILE: toolchain/check/handle_binding_pattern.cpp ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #include #include "toolchain/base/kind_switch.h" #include "toolchain/check/context.h" #include "toolchain/check/convert.h" #include "toolchain/check/facet_type.h" #include "toolchain/check/handle.h" #include "toolchain/check/inst.h" #include "toolchain/check/interface.h" #include "toolchain/check/name_lookup.h" #include "toolchain/check/pattern.h" #include "toolchain/check/return.h" #include "toolchain/check/type.h" #include "toolchain/check/type_completion.h" #include "toolchain/check/unused.h" #include "toolchain/diagnostics/format_providers.h" #include "toolchain/parse/node_ids.h" #include "toolchain/sem_ir/ids.h" #include "toolchain/sem_ir/inst.h" #include "toolchain/sem_ir/pattern.h" #include "toolchain/sem_ir/typed_insts.h" namespace Carbon::Check { auto HandleParseNode(Context& context, Parse::UnderscoreNameId node_id) -> bool { context.node_stack().Push(node_id, SemIR::NameId::Underscore); return true; } // Returns the `InstKind` corresponding to the pattern's `NodeKind`. static auto GetPatternInstKind(Parse::NodeKind node_kind, bool is_ref) -> SemIR::InstKind { switch (node_kind) { case Parse::NodeKind::CompileTimeBindingPattern: return SemIR::InstKind::SymbolicBindingPattern; case Parse::NodeKind::LetBindingPattern: return is_ref ? SemIR::InstKind::RefBindingPattern : SemIR::InstKind::ValueBindingPattern; case Parse::NodeKind::VarBindingPattern: return SemIR::InstKind::RefBindingPattern; case Parse::NodeKind::FormBindingPattern: return SemIR::InstKind::FormBindingPattern; default: CARBON_FATAL("Unexpected node kind: {0}", node_kind); } } // Returns true if a parameter is valid in the given `introducer_kind`. static auto IsValidParamForIntroducer(Context& context, Parse::NodeId node_id, SemIR::NameId name_id, Lex::TokenKind introducer_kind, bool is_generic) -> bool { switch (introducer_kind) { case Lex::TokenKind::Fn: { if (context.full_pattern_stack().CurrentKind() == FullPatternStack::Kind::ImplicitParamList && !(is_generic || name_id == SemIR::NameId::SelfValue)) { CARBON_DIAGNOSTIC( ImplictParamMustBeConstant, Error, "implicit parameters of functions must be constant or `self`"); context.emitter().Emit(node_id, ImplictParamMustBeConstant); return false; } // Parameters can have incomplete types in a function declaration, but not // in a function definition. We don't know which kind we have here, so // don't validate it. return true; } case Lex::TokenKind::Choice: if (context.scope_stack().PeekInstId().has_value()) { // We are building a pattern for a choice alternative, not the // choice type itself. // Implicit param lists are prevented during parse. CARBON_CHECK(context.full_pattern_stack().CurrentKind() != FullPatternStack::Kind::ImplicitParamList, "choice alternative with implicit parameters"); // Don't fall through to the `Class` logic for choice alternatives. return true; } [[fallthrough]]; case Lex::TokenKind::Class: case Lex::TokenKind::Impl: case Lex::TokenKind::Interface: { if (name_id == SemIR::NameId::SelfValue) { CARBON_DIAGNOSTIC(SelfParameterNotAllowed, Error, "`self` parameter only allowed on functions"); context.emitter().Emit(node_id, SelfParameterNotAllowed); return false; } if (!is_generic) { CARBON_DIAGNOSTIC(GenericParamMustBeConstant, Error, "parameters of generic types must be constant"); context.emitter().Emit(node_id, GenericParamMustBeConstant); return false; } return true; } default: return true; } } namespace { // Information about the expression in the type position of a binding pattern, // i.e. the position following the `:`/`:?`/`:!` separator. Note that this // expression may be interpreted as a type or a form, depending on the binding // kind. struct BindingPatternTypeInfo { // The parse node representing the expression. Parse::AnyExprId node_id; // The inst representing the converted value of that expression. For a `:?` // binding the expression is converted to type `Core.Form`; otherwise it is // converted to type `type`. SemIR::InstId inst_id; // For a `:?` binding this is the type component of the form denoted by // `inst_id`. Otherwise this is the type denoted by `inst_id`. SemIR::TypeId type_component_id; }; } // namespace // Handle the type position of a binding pattern. static auto HandleAnyBindingPatternType(Context& context, Parse::NodeKind node_kind) -> BindingPatternTypeInfo { auto [node_id, original_inst_id] = context.node_stack().PopExprWithNodeId(); if (node_kind == Parse::FormBindingPattern::Kind) { auto as_form = FormExprAsForm(context, node_id, original_inst_id); return {.node_id = node_id, .inst_id = as_form.form_inst_id, .type_component_id = as_form.type_component_id}; } else { auto as_type = ExprAsType(context, node_id, original_inst_id); return {.node_id = node_id, .inst_id = as_type.inst_id, .type_component_id = as_type.type_id}; } } // TODO: make this function shorter by factoring pieces out. static auto HandleAnyBindingPattern(Context& context, Parse::NodeId node_id, Parse::NodeKind node_kind, bool is_unused = false) -> bool { auto type_expr = HandleAnyBindingPatternType(context, node_kind); if (context.types() .GetAsInst(type_expr.type_component_id) .Is()) { return context.TODO(node_id, "Support symbolic form bindings"); } SemIR::ExprRegionId type_expr_region_id = EndSubpatternAsExpr(context, type_expr.inst_id); // The name in a generic binding may be wrapped in `template`. bool is_generic = node_kind == Parse::NodeKind::CompileTimeBindingPattern; bool is_template = context.node_stack() .PopAndDiscardSoloNodeIdIf(); // A non-generic template binding is diagnosed by the parser. is_template &= is_generic; // The name in a runtime binding may be wrapped in `ref`. bool is_ref = context.node_stack() .PopAndDiscardSoloNodeIdIf(); SemIR::InstKind pattern_inst_kind = GetPatternInstKind(node_kind, is_ref); auto [name_node, name_id] = context.node_stack().PopNameWithNodeId(); const DeclIntroducerState& introducer = context.decl_introducer_state_stack().innermost(); auto form_id = pattern_inst_kind == SemIR::FormBindingPattern::Kind ? context.constant_values().Get(type_expr.inst_id) : SemIR::ConstantId::None; auto make_binding_pattern = [&]() -> SemIR::InstId { // TODO: Eventually the name will need to support associations with other // scopes, but right now we don't support qualified names here. auto phase = BindingPhase::Runtime; if (pattern_inst_kind == SemIR::SymbolicBindingPattern::Kind) { phase = is_template ? BindingPhase::Template : BindingPhase::Symbolic; } auto binding = AddBindingPattern( context, name_node, type_expr_region_id, {.kind = pattern_inst_kind, .type_id = GetPatternType(context, type_expr.type_component_id), .entity_name_id = AddBindingEntityName(context, name_id, form_id, is_unused, phase)}); // TODO: If `is_generic`, then `binding.bind_id is a SymbolicBinding. Subst // the `.Self` of type `type` in the `cast_type_id` type (a `FacetType`) // with the `binding.bind_id` itself, and build a new pattern with that. // This is kind of cyclical. So we need to reuse the EntityNameId, which // will also reuse the CompileTimeBinding for the new SymbolicBinding. if (name_id != SemIR::NameId::Underscore) { // Add name to lookup immediately, so it can be used in the rest of the // enclosing pattern. auto name_context = context.decl_name_stack().MakeUnqualifiedName(name_node, name_id); context.decl_name_stack().AddNameOrDiagnose( name_context, binding.bind_id, introducer.modifier_set.GetAccessKind()); context.full_pattern_stack().AddBindName(name_id); } return binding.pattern_id; }; auto abstract_diagnostic_context = [&](auto& builder) { CARBON_DIAGNOSTIC(AbstractTypeInVarPattern, Context, "binding pattern has abstract type {0} in `var` " "pattern", SemIR::TypeId); builder.Context(type_expr.node_id, AbstractTypeInVarPattern, type_expr.type_component_id); }; // A `self` binding can only appear in an implicit parameter list. if (name_id == SemIR::NameId::SelfValue && !context.node_stack().PeekIs(Parse::NodeKind::ImplicitParamListStart)) { CARBON_DIAGNOSTIC( SelfOutsideImplicitParamList, Error, "`self` can only be declared in an implicit parameter list"); context.emitter().Emit(node_id, SelfOutsideImplicitParamList); } if (node_kind == Parse::NodeKind::CompileTimeBindingPattern && introducer.kind == Lex::TokenKind::Let) { // TODO: We should re-evaluate the contents of the eval block in a // synthesized specific to form these values, in order to propagate the // values. return context.TODO(node_id, "local `let :!` bindings are currently unsupported"); } // Allocate an instruction of the appropriate kind, linked to the name for // error locations. switch (context.full_pattern_stack().CurrentKind()) { case FullPatternStack::Kind::ImplicitParamList: case FullPatternStack::Kind::ExplicitParamList: { if (!IsValidParamForIntroducer(context, node_id, name_id, introducer.kind, is_generic)) { if (name_id != SemIR::NameId::Underscore) { AddNameToLookup(context, name_id, SemIR::ErrorInst::InstId); } // Replace the parameter with `ErrorInst` so that we don't try // constructing a generic based on it. context.node_stack().Push(node_id, SemIR::ErrorInst::InstId); break; } // Using `AsConcreteType` here causes `fn F[var self: Self]();` // to fail since `Self` is an incomplete type. if (node_kind == Parse::NodeKind::VarBindingPattern) { auto [unqualified_type_id, qualifiers] = context.types().GetUnqualifiedTypeAndQualifiers( type_expr.type_component_id); if ((qualifiers & SemIR::TypeQualifiers::Partial) != SemIR::TypeQualifiers::Partial && context.types().Is(unqualified_type_id)) { auto class_type = context.types().GetAs(unqualified_type_id); auto& class_info = context.classes().Get(class_type.class_id); if (class_info.inheritance_kind == SemIR::Class::InheritanceKind::Abstract) { Diagnostics::ContextScope scope(&context.emitter(), abstract_diagnostic_context); DiagnoseAbstractClass(context, class_type.class_id, /*direct_use=*/true); type_expr.type_component_id = SemIR::ErrorInst::TypeId; } } } auto result_inst_id = make_binding_pattern(); // A binding pattern in a function signature is a `Call` parameter // unless it's nested inside a `var` pattern (because then the // enclosing `var` pattern is), or it's a compile-time binding pattern // (because then it's not passed to the `Call` inst). if (node_kind == Parse::NodeKind::LetBindingPattern || node_kind == Parse::NodeKind::FormBindingPattern) { auto type_id = context.insts().GetAttachedType(result_inst_id); if (is_ref) { result_inst_id = AddPatternInst( context, node_id, {.type_id = type_id, .subpattern_id = result_inst_id}); } else if (node_kind == Parse::NodeKind::FormBindingPattern) { result_inst_id = AddPatternInst( context, node_id, {.type_id = type_id, .subpattern_id = result_inst_id, .form_id = form_id}); } else { result_inst_id = AddPatternInst( context, node_id, {.type_id = type_id, .subpattern_id = result_inst_id}); } } context.node_stack().Push(node_id, result_inst_id); break; } case FullPatternStack::Kind::NameBindingDecl: { auto incomplete_diagnostic_context = [&](auto& builder) { CARBON_DIAGNOSTIC(IncompleteTypeInBindingDecl, Context, "binding pattern has incomplete type {0} in name " "binding declaration", InstIdAsType); builder.Context(type_expr.node_id, IncompleteTypeInBindingDecl, type_expr.inst_id); }; if (node_kind == Parse::NodeKind::VarBindingPattern) { if (!RequireConcreteType( context, type_expr.type_component_id, type_expr.node_id, incomplete_diagnostic_context, abstract_diagnostic_context)) { type_expr.type_component_id = SemIR::ErrorInst::TypeId; } } else { if (!RequireCompleteType(context, type_expr.type_component_id, type_expr.node_id, incomplete_diagnostic_context)) { type_expr.type_component_id = SemIR::ErrorInst::TypeId; } } auto binding_pattern_id = make_binding_pattern(); if (node_kind == Parse::NodeKind::VarBindingPattern) { CARBON_CHECK(!is_generic); if (introducer.modifier_set.HasAnyOf(KeywordModifierSet::Returned)) { // TODO: Should we check this for the `var` as a whole, rather than // for the name binding? auto bind_id = context.bind_name_map() .Lookup(binding_pattern_id) .value() .bind_name_id; RegisterReturnedVar( context, introducer.modifier_node_id(ModifierOrder::Decl), type_expr.node_id, type_expr.type_component_id, bind_id, name_id); } } context.node_stack().Push(node_id, binding_pattern_id); break; } case FullPatternStack::Kind::NotInEitherParamList: CARBON_FATAL("Unreachable"); } return true; } auto HandleParseNode(Context& context, Parse::LetBindingPatternId node_id) -> bool { return HandleAnyBindingPattern(context, node_id, Parse::NodeKind::LetBindingPattern); } auto HandleParseNode(Context& context, Parse::VarBindingPatternId node_id) -> bool { return HandleAnyBindingPattern(context, node_id, Parse::NodeKind::VarBindingPattern); } auto HandleParseNode(Context& context, Parse::FormBindingPatternId node_id) -> bool { return HandleAnyBindingPattern(context, node_id, Parse::NodeKind::FormBindingPattern); } auto HandleParseNode(Context& context, Parse::CompileTimeBindingPatternStartId /*node_id*/) -> bool { // Make a scope to contain the `.Self` facet value for use in the type of the // compile time binding. This is popped when handling the // CompileTimeBindingPatternId. context.scope_stack().PushForSameRegion(); // The `.Self` must have a type of `FacetType`, so that it gets wrapped in // `FacetAccessType` when used in a type position, such as in `U:! I(.Self)`. // This allows substitution with other facet values without requiring an // additional `FacetAccessType` to be inserted. auto type_id = GetEmptyFacetType(context); MakePeriodSelfFacetValue(context, type_id); return true; } auto HandleParseNode(Context& context, Parse::CompileTimeBindingPatternId node_id) -> bool { // Pop the `.Self` facet value name introduced by the // CompileTimeBindingPatternStart. context.scope_stack().Pop(/*check_unused=*/true); auto node_kind = Parse::NodeKind::CompileTimeBindingPattern; const DeclIntroducerState& introducer = context.decl_introducer_state_stack().innermost(); if (introducer.kind == Lex::TokenKind::Let) { // Disallow `let` outside of function and interface definitions. // TODO: Find a less brittle way of doing this. A `scope_inst_id` of `None` // can represent a block scope, but is also used for other kinds of scopes // that aren't necessarily part of a function decl. // We don't need to check if the scope is an interface here as this is // already caught in the parse phase by the separated associated constant // logic. auto scope_inst_id = context.scope_stack().PeekInstId(); if (scope_inst_id.has_value()) { auto scope_inst = context.insts().Get(scope_inst_id); if (!scope_inst.Is()) { context.TODO( node_id, "`let` compile time binding outside function or interface"); node_kind = Parse::NodeKind::LetBindingPattern; } } } return HandleAnyBindingPattern(context, node_id, node_kind); } auto HandleParseNode(Context& context, Parse::AssociatedConstantNameAndTypeId node_id) -> bool { auto [type_node, parsed_type_id] = context.node_stack().PopExprWithNodeId(); auto [cast_type_inst_id, cast_type_id] = ExprAsType(context, type_node, parsed_type_id); EndSubpatternAsExpr(context, cast_type_inst_id); auto [name_node, name_id] = context.node_stack().PopNameWithNodeId(); if (name_id == SemIR::NameId::Underscore) { // The action item here may be to document this as not allowed, and // add a proper diagnostic. context.TODO(node_id, "_ used as associated constant name"); } SemIR::AssociatedConstantDecl assoc_const_decl = { .type_id = cast_type_id, .assoc_const_id = SemIR::AssociatedConstantId::None, .decl_block_id = SemIR::InstBlockId::None}; auto decl_id = AddPlaceholderInstInNoBlock(context, node_id, assoc_const_decl); assoc_const_decl.assoc_const_id = context.associated_constants().Add( {.name_id = name_id, .parent_scope_id = context.scope_stack().PeekNameScopeId(), .decl_id = decl_id, .default_value_id = SemIR::InstId::None}); ReplaceInstBeforeConstantUse(context, decl_id, assoc_const_decl); context.node_stack().Push(node_id, decl_id); return true; } auto HandleParseNode(Context& context, Parse::FieldNameAndTypeId node_id) -> bool { auto [type_node, parsed_type_id] = context.node_stack().PopExprWithNodeId(); auto [cast_type_inst_id, cast_type_id] = ExprAsType(context, type_node, parsed_type_id); auto [name_node, name_id] = context.node_stack().PopNameWithNodeId(); auto parent_class_decl = context.scope_stack().TryGetCurrentScopeAs(); CARBON_CHECK(parent_class_decl); if (!RequireConcreteType( context, cast_type_id, type_node, [&](auto& builder) { CARBON_DIAGNOSTIC(IncompleteTypeInFieldDecl, Context, "field has incomplete type {0}", SemIR::TypeId); builder.Context(type_node, IncompleteTypeInFieldDecl, cast_type_id); }, [&](auto& builder) { CARBON_DIAGNOSTIC(AbstractTypeInFieldDecl, Context, "field has abstract type {0}", SemIR::TypeId); builder.Context(type_node, AbstractTypeInFieldDecl, cast_type_id); })) { cast_type_id = SemIR::ErrorInst::TypeId; } if (cast_type_id == SemIR::ErrorInst::TypeId) { cast_type_inst_id = SemIR::ErrorInst::TypeInstId; } auto& class_info = context.classes().Get(parent_class_decl->class_id); auto field_type_id = GetUnboundElementType( context, context.types().GetTypeInstId(class_info.self_type_id), cast_type_inst_id); auto field_id = AddInst(context, node_id, {.type_id = field_type_id, .name_id = name_id, .index = SemIR::ElementIndex::None}); context.field_decls_stack().AppendToTop(field_id); auto name_context = context.decl_name_stack().MakeUnqualifiedName(node_id, name_id); context.decl_name_stack().AddNameOrDiagnose( name_context, field_id, context.decl_introducer_state_stack() .innermost() .modifier_set.GetAccessKind()); return true; } auto HandleParseNode(Context& context, Parse::RefBindingNameId node_id) -> bool { context.node_stack().Push(node_id); return true; } auto HandleParseNode(Context& context, Parse::TemplateBindingNameId node_id) -> bool { context.node_stack().Push(node_id); return true; } // Within a pattern with an unused modifier, sets the is_unused on all // entity names and also returns whether any names were found. The result // is needed to emit a diagnostic when the unused modifier is // unnecessary. static auto MarkPatternUnused(Context& context, SemIR::InstId inst_id) -> bool { bool found_name = false; llvm::SmallVector worklist; worklist.push_back(inst_id); while (!worklist.empty()) { auto current_inst_id = worklist.pop_back_val(); auto inst = context.insts().Get(current_inst_id); CARBON_KIND_SWITCH(inst) { case CARBON_KIND_ANY(SemIR::AnyParamPattern, param): { worklist.push_back(param.subpattern_id); break; } case CARBON_KIND_ANY(SemIR::AnyBindingPattern, bind): { auto& name = context.entity_names().Get(bind.entity_name_id); name.is_unused = true; // We treat `_` as not marking the pattern as unused for the purpose of // deciding whether to issue a warning for `unused` on a pattern that // doesn't contain any bindings. `_` is implicitly unused, so marking it // `unused` is redundant but harmless. if (name.name_id != SemIR::NameId::Underscore) { found_name = true; } break; } case CARBON_KIND(SemIR::TuplePattern tuple): { for (auto elem_id : context.inst_blocks().Get(tuple.elements_id)) { worklist.push_back(elem_id); } break; } case CARBON_KIND(SemIR::VarPattern var): { worklist.push_back(var.subpattern_id); break; } default: break; } } return found_name; } auto HandleParseNode(Context& context, Parse::UnusedPatternId node_id) -> bool { auto [child_node, child_inst_id] = context.node_stack().PopPatternWithNodeId(); if (!MarkPatternUnused(context, child_inst_id)) { CARBON_DIAGNOSTIC(UnusedPatternNoBindings, Warning, "`unused` modifier on pattern without bindings"); context.emitter().Emit(node_id, UnusedPatternNoBindings); } context.node_stack().Push(node_id, child_inst_id); return true; } } // namespace Carbon::Check ================================================ FILE: toolchain/check/handle_call_expr.cpp ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #include "toolchain/check/call.h" #include "toolchain/check/context.h" #include "toolchain/check/handle.h" #include "toolchain/check/inst.h" #include "toolchain/sem_ir/expr_info.h" #include "toolchain/sem_ir/inst.h" namespace Carbon::Check { auto HandleParseNode(Context& context, Parse::CallExprStartId node_id) -> bool { auto name_id = context.node_stack().PopExpr(); context.node_stack().Push(node_id, name_id); context.param_and_arg_refs_stack().Push(); return true; } auto HandleParseNode(Context& context, Parse::CallExprId node_id) -> bool { // Process the final explicit call argument now, but leave the arguments // block on the stack until the end of this function. context.param_and_arg_refs_stack().EndNoPop(Parse::NodeKind::CallExprStart); auto callee_id = context.node_stack().Pop(); auto call_id = PerformCall( context, node_id, callee_id, context.param_and_arg_refs_stack().PeekCurrentBlockContents()); context.param_and_arg_refs_stack().PopAndDiscard(); context.node_stack().Push(node_id, call_id); return true; } } // namespace Carbon::Check ================================================ FILE: toolchain/check/handle_choice.cpp ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #include "toolchain/check/context.h" #include "toolchain/check/convert.h" #include "toolchain/check/decl_name_stack.h" #include "toolchain/check/eval.h" #include "toolchain/check/generic.h" #include "toolchain/check/handle.h" #include "toolchain/check/inst.h" #include "toolchain/check/literal.h" #include "toolchain/check/name_component.h" #include "toolchain/check/type.h" #include "toolchain/check/unused.h" #include "toolchain/diagnostics/diagnostic.h" #include "toolchain/lex/token_kind.h" #include "toolchain/sem_ir/ids.h" #include "toolchain/sem_ir/inst.h" #include "toolchain/sem_ir/name_scope.h" #include "toolchain/sem_ir/typed_insts.h" namespace Carbon::Check { auto HandleParseNode(Context& context, Parse::ChoiceIntroducerId node_id) -> bool { // This choice is potentially generic. StartGenericDecl(context); // Create an instruction block to hold the instructions created as part of the // choice signature, such as generic parameters. context.inst_block_stack().Push(); // There's no modifiers on a choice, but this informs how to typecheck any // generic binding pattern. context.decl_introducer_state_stack().Push(); // Push the bracketing node. context.node_stack().Push(node_id); // The choice's name follows. context.decl_name_stack().PushScopeAndStartName(); return true; } auto HandleParseNode(Context& context, Parse::ChoiceDefinitionStartId node_id) -> bool { auto name = PopNameComponent(context); auto name_context = context.decl_name_stack().FinishName(name); context.node_stack() .PopAndDiscardSoloNodeId(); context.decl_introducer_state_stack().Pop(); auto decl_block_id = context.inst_block_stack().Pop(); // Choices create a ClassId, since they ultimately turn into a class with // methods and some builtin impls. auto class_decl = SemIR::ClassDecl{.type_id = SemIR::TypeType::TypeId, .class_id = SemIR::ClassId::None, .decl_block_id = decl_block_id}; auto class_decl_id = AddPlaceholderInst(context, node_id, class_decl); context.decl_name_stack().AddNameOrDiagnose(name_context, class_decl_id, SemIR::AccessKind::Public); // An inst block for the body of the choice. context.inst_block_stack().Push(); auto body_block_id = context.inst_block_stack().PeekOrAdd(); SemIR::Class class_info = { name_context.MakeEntityWithParamsBase(name, class_decl_id, /*is_extern=*/false, SemIR::LibraryNameId::None), {// `.self_type_id` depends on the ClassType, so is set below. .self_type_id = SemIR::TypeId::None, .inheritance_kind = SemIR::ClassFields::Final, // TODO: Handle the case where there's control flow in the alternatives. // For example: // // choice C { // Alt(x: if true then i32 else f64), // } // // We may need to track a list of instruction blocks here, as we do for a // function. .body_block_id = body_block_id}}; // This call finishes the GenericDecl, after which we can use the `Self` // specific. class_info.generic_id = BuildGenericDecl(context, class_decl_id); auto self_specific_id = context.generics().GetSelfSpecific(class_info.generic_id); class_info.definition_id = class_decl_id; class_info.scope_id = context.name_scopes().Add( class_decl_id, SemIR::NameId::None, class_info.parent_scope_id); class_decl.class_id = context.classes().Add(class_info); if (class_info.has_parameters()) { class_decl.type_id = GetGenericClassType( context, class_decl.class_id, context.scope_stack().PeekSpecificId()); } ReplaceInstBeforeConstantUse(context, class_decl_id, class_decl); // We had to construct the `ClassId` from `Class` in order to build the `Self` // type below. But it needs to be written back to the `Class` in the // ValueStore, not the local variable. This gives a mutable reference to the // `Class` in the ValueStore. SemIR::Class& mut_class = context.classes().Get(class_decl.class_id); // Build the `Self` type using the resulting type constant. auto self_type_id = GetClassType(context, class_decl.class_id, self_specific_id); mut_class.self_type_id = self_type_id; // Enter the choice scope. context.scope_stack().PushForEntity(class_decl_id, class_info.scope_id, self_specific_id); // Checking the binding pattern for an alternative requires a non-empty stack. // We reuse the Choice token even though we're now checking an alternative // inside the Choice, since there's no better token to use. // // TODO: The token here is _not_ `Choice` though, we shouldn't need to use // that here. Either remove the need for a token or find a token (a new // introducer?) for the alternative to name. context.decl_introducer_state_stack().Push(); StartGenericDefinition(context, class_info.generic_id); context.name_scopes().AddRequiredName( class_info.scope_id, SemIR::NameId::SelfType, context.types().GetTypeInstId(self_type_id)); // Mark the beginning of the choice body. context.node_stack().Push(node_id, class_decl.class_id); CARBON_CHECK(context.choice_deferred_bindings().empty(), "Alternatives left behind in choice_deferred_bindings: {0}", context.choice_deferred_bindings().size()); return true; } static auto AddChoiceAlternative( Context& context, Parse::NodeIdOneOf node_id) -> void { // Note, there is nothing like a ChoiceAlternativeIntroducer node, so no parse // node to pop here. auto name_component = PopNameComponent(context); if (name_component.param_patterns_id == SemIR::InstBlockId::Empty) { // Treat an empty parameter list the same as no parameter list. // // TODO: The current design suggests that we want Foo() to result in a // member function `ChoiceType.Foo()`, and `Foo` to result in a member // constant `ChoiceType.Foo`, but that only one of the two is allowed in a // single choice type. See // https://github.com/carbon-language/carbon-lang/blob/trunk/docs/design/sum_types.md#user-defined-sum-types. // For now they are not treated differently and both resolve to a member // constant. context.TODO(name_component.params_loc_id, "empty parameter list should make a member function"); name_component.param_patterns_id = SemIR::InstBlockId::None; } if (name_component.param_patterns_id.has_value()) { context.TODO(name_component.params_loc_id, "choice alternatives with parameters are not yet supported"); return; } context.choice_deferred_bindings().push_back({node_id, name_component}); } // Info about the Choice type, used to construct each alternative member of the // class representing the Choice. struct ChoiceInfo { // The `Self` type. SemIR::TypeId self_type_id; // The scope of the class for adding the alternatives to. SemIR::NameScopeId name_scope_id; // A struct type with the same fields as `Self`. Used to construct `Self`. SemIR::TypeId self_struct_type_id; // The type of the discriminant value. SemIR::TypeId discriminant_type_id; int num_alternative_bits; }; // Builds a `let` binding for an alternative without parameters as a member of // the resulting class for the Choice definition. If the alternative was `Alt` // then the binding will be like: // ``` // let Alt: ChoiceType = ; // ``` static auto MakeLetBinding(Context& context, const ChoiceInfo& choice_info, int alternative_index, const Context::ChoiceDeferredBinding& binding) -> void { SemIR::InstId discriminant_value_id = [&] { if (choice_info.num_alternative_bits == 0) { return AddInst(context, binding.node_id, SemIR::TupleLiteral{ .type_id = GetTupleType(context, {}), .elements_id = SemIR::InstBlockId::Empty, }); } else { return MakeIntLiteral(context, binding.node_id, context.ints().Add(alternative_index)); } }(); discriminant_value_id = ConvertToValueOfType(context, binding.node_id, discriminant_value_id, choice_info.discriminant_type_id); auto self_value_id = ConvertToValueOfType( context, binding.node_id, AddInst(context, binding.node_id, SemIR::StructLiteral{ .type_id = choice_info.self_struct_type_id, .elements_id = [&] { context.inst_block_stack().Push(); context.inst_block_stack().AddInstId( discriminant_value_id); return context.inst_block_stack().Pop(); }(), }), choice_info.self_type_id); auto entity_name_id = context.entity_names().Add( {.name_id = binding.name_component.name_id, .parent_scope_id = choice_info.name_scope_id}); auto bind_name_id = AddInst(context, binding.node_id, SemIR::ValueBinding{ .type_id = choice_info.self_type_id, .entity_name_id = entity_name_id, .value_id = self_value_id, }); context.name_scopes() .Get(choice_info.name_scope_id) .AddRequired({.name_id = binding.name_component.name_id, .result = SemIR::ScopeLookupResult::MakeFound( bind_name_id, SemIR::AccessKind::Public)}); } auto HandleParseNode(Context& context, Parse::ChoiceDefinitionId node_id) -> bool { // The last alternative may optionally not have a comma after it, in which // case we get here after the last alternative. if (!context.node_stack().PeekIs(Parse::NodeKind::ChoiceDefinitionStart)) { AddChoiceAlternative(context, node_id); } auto class_id = context.node_stack().Pop(); int num_alternatives = context.choice_deferred_bindings().size(); int num_alternative_bits = [&] { if (num_alternatives > 1) { return static_cast(ceil(log2(num_alternatives))); } else { return 0; } }(); SemIR::TypeId discriminant_type_id = [&] { if (num_alternative_bits == 0) { // Even though there's no bits needed, we add an empty field. We want to // prevent constructing the Choice from an empty struct literal instead of // going through an alternative. And in the case there is no alternative, // then there's no way to construct the Choice (which can be a useful // type). // // TODO: Find a way to produce a better diagnostic, and not require an // empty field. return GetTupleType(context, {}); } else { return MakeIntType(context, node_id, SemIR::IntKind::Unsigned, context.ints().Add(num_alternative_bits)); } }(); llvm::SmallVector struct_type_fields; struct_type_fields.push_back({ .name_id = SemIR::NameId::ChoiceDiscriminant, .type_inst_id = context.types().GetTypeInstId(discriminant_type_id), }); auto fields_id = context.struct_type_fields().AddCanonical(struct_type_fields); auto choice_witness_id = AddInst( context, node_id, SemIR::CompleteTypeWitness{ .type_id = GetSingletonType(context, SemIR::WitnessType::TypeInstId), .object_repr_type_inst_id = context.types().GetTypeInstId( GetStructType(context, fields_id))}); auto& class_info = context.classes().Get(class_id); class_info.complete_type_witness_id = choice_witness_id; auto self_struct_type_id = GetStructType( context, context.struct_type_fields().AddCanonical(struct_type_fields)); for (auto [i, deferred_binding] : llvm::enumerate(context.choice_deferred_bindings())) { // TODO: This requires the class to be complete, but we've not yet called // `FinishGenericDefinition`, so we can't use it as a complete type yet. But // this also potentially adds things to the generic definition, so we can't // call `FinsihGenericDefinition` before this call, either. MakeLetBinding(context, ChoiceInfo{.self_type_id = class_info.self_type_id, .name_scope_id = class_info.scope_id, .self_struct_type_id = self_struct_type_id, .discriminant_type_id = discriminant_type_id, .num_alternative_bits = num_alternative_bits}, i, deferred_binding); } // The scopes and blocks for the choice itself. context.inst_block_stack().Pop(); context.decl_introducer_state_stack().Pop(); context.scope_stack().Pop(/*check_unused=*/true); context.decl_name_stack().PopScope(); FinishGenericDefinition(context, class_info.generic_id); context.choice_deferred_bindings().clear(); return true; } auto HandleParseNode(Context& context, Parse::ChoiceAlternativeListCommaId node_id) -> bool { AddChoiceAlternative(context, node_id); return true; } } // namespace Carbon::Check ================================================ FILE: toolchain/check/handle_class.cpp ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #include #include #include "toolchain/base/kind_switch.h" #include "toolchain/check/class.h" #include "toolchain/check/context.h" #include "toolchain/check/convert.h" #include "toolchain/check/decl_name_stack.h" #include "toolchain/check/diagnostic_helpers.h" #include "toolchain/check/eval.h" #include "toolchain/check/generic.h" #include "toolchain/check/handle.h" #include "toolchain/check/impl.h" #include "toolchain/check/import.h" #include "toolchain/check/import_ref.h" #include "toolchain/check/inst.h" #include "toolchain/check/merge.h" #include "toolchain/check/modifiers.h" #include "toolchain/check/name_component.h" #include "toolchain/check/name_lookup.h" #include "toolchain/check/type.h" #include "toolchain/check/type_completion.h" #include "toolchain/diagnostics/emitter.h" #include "toolchain/parse/node_ids.h" #include "toolchain/sem_ir/function.h" #include "toolchain/sem_ir/ids.h" #include "toolchain/sem_ir/inst.h" #include "toolchain/sem_ir/typed_insts.h" namespace Carbon::Check { auto HandleParseNode(Context& context, Parse::ClassIntroducerId node_id) -> bool { // This class is potentially generic. StartGenericDecl(context); // Create an instruction block to hold the instructions created as part of the // class signature, such as generic parameters. context.inst_block_stack().Push(); // Push the bracketing node. context.node_stack().Push(node_id); // Optional modifiers and the name follow. context.decl_introducer_state_stack().Push(); context.decl_name_stack().PushScopeAndStartName(); return true; } // Tries to merge new_class into prev_class_id. Since new_class won't have a // definition even if one is upcoming, set is_definition to indicate the planned // result. // // If merging is successful, returns true and may update the previous class. // Otherwise, returns false. Prints a diagnostic when appropriate. static auto MergeClassRedecl(Context& context, Parse::AnyClassDeclId node_id, SemIR::Class& new_class, bool new_is_definition, SemIR::ClassId prev_class_id, SemIR::ImportIRId prev_import_ir_id) -> bool { auto& prev_class = context.classes().Get(prev_class_id); SemIR::LocId prev_loc_id(prev_class.latest_decl_id()); // Check the generic parameters match, if they were specified. if (!CheckRedeclParamsMatch(context, DeclParams(new_class), DeclParams(prev_class))) { return false; } DiagnoseIfInvalidRedecl( context, Lex::TokenKind::Class, prev_class.name_id, RedeclInfo(new_class, node_id, new_is_definition), RedeclInfo(prev_class, prev_loc_id, prev_class.has_definition_started()), prev_import_ir_id); if (new_is_definition && prev_class.has_definition_started()) { // Don't attempt to merge multiple definitions. return false; } if (new_is_definition) { prev_class.MergeDefinition(new_class); prev_class.scope_id = new_class.scope_id; prev_class.body_block_id = new_class.body_block_id; prev_class.adapt_id = new_class.adapt_id; prev_class.base_id = new_class.base_id; prev_class.complete_type_witness_id = new_class.complete_type_witness_id; } if (prev_import_ir_id.has_value() || (prev_class.is_extern && !new_class.is_extern)) { prev_class.first_owning_decl_id = new_class.first_owning_decl_id; ReplacePrevInstForMerge(context, new_class.parent_scope_id, prev_class.name_id, new_class.first_owning_decl_id); } return true; } // Adds the name to name lookup. If there's a conflict, tries to merge. May // update class_decl and class_info when merging. static auto MergeOrAddName(Context& context, Parse::AnyClassDeclId node_id, const DeclNameStack::NameContext& name_context, SemIR::InstId class_decl_id, SemIR::ClassDecl& class_decl, SemIR::Class& class_info, bool is_definition, SemIR::AccessKind access_kind) -> void { SemIR::ScopeLookupResult lookup_result = context.decl_name_stack().LookupOrAddName(name_context, class_decl_id, access_kind); if (lookup_result.is_poisoned()) { // This is a declaration of a poisoned name. DiagnosePoisonedName(context, name_context.name_id_for_new_inst(), lookup_result.poisoning_loc_id(), name_context.loc_id); return; } if (!lookup_result.is_found()) { return; } SemIR::InstId prev_id = lookup_result.target_inst_id(); auto prev_class_id = SemIR::ClassId::None; auto prev_import_ir_id = SemIR::ImportIRId::None; auto prev = context.insts().Get(prev_id); CARBON_KIND_SWITCH(prev) { case CARBON_KIND(SemIR::ClassDecl class_decl): { prev_class_id = class_decl.class_id; break; } case CARBON_KIND(SemIR::ImportRefLoaded import_ref): { auto import_ir_inst = context.import_ir_insts().Get(import_ref.import_ir_inst_id); // Verify the decl so that things like aliases are name conflicts. const auto* import_ir = context.import_irs().Get(import_ir_inst.ir_id()).sem_ir; if (!import_ir->insts().Is(import_ir_inst.inst_id())) { break; } // Use the constant value to get the ID. auto decl_value = context.insts().Get( context.constant_values().GetConstantInstId(prev_id)); if (auto class_type = decl_value.TryAs()) { prev_class_id = class_type->class_id; prev_import_ir_id = import_ir_inst.ir_id(); } else if (auto generic_class_type = context.types().TryGetAs( decl_value.type_id())) { prev_class_id = generic_class_type->class_id; prev_import_ir_id = import_ir_inst.ir_id(); } break; } default: break; } if (!prev_class_id.has_value()) { // This is a redeclaration of something other than a class. DiagnoseDuplicateName(context, name_context.name_id, name_context.loc_id, SemIR::LocId(prev_id)); return; } // TODO: Fix `extern` logic. It doesn't work correctly, but doesn't seem worth // ripping out because existing code may incrementally help. if (MergeClassRedecl(context, node_id, class_info, is_definition, prev_class_id, prev_import_ir_id)) { // When merging, use the existing entity rather than adding a new one. class_decl.class_id = prev_class_id; class_decl.type_id = prev.type_id(); // TODO: Validate that the redeclaration doesn't set an access modifier. } } static auto BuildClassDecl(Context& context, Parse::AnyClassDeclId node_id, bool is_definition) -> std::tuple { auto name = PopNameComponent(context); auto name_context = context.decl_name_stack().FinishName(name); context.node_stack() .PopAndDiscardSoloNodeId(); // Process modifiers. auto [_, parent_scope_inst] = context.name_scopes().GetInstIfValid(name_context.parent_scope_id); auto introducer = context.decl_introducer_state_stack().Pop(); CheckAccessModifiersOnDecl(context, introducer, parent_scope_inst); auto always_acceptable_modifiers = KeywordModifierSet::Access | KeywordModifierSet::Extern; LimitModifiersOnDecl(context, introducer, always_acceptable_modifiers | KeywordModifierSet::Class); if (!is_definition) { LimitModifiersOnNotDefinition(context, introducer, always_acceptable_modifiers); } RestrictExternModifierOnDecl(context, introducer, parent_scope_inst, is_definition); bool is_extern = introducer.modifier_set.HasAnyOf(KeywordModifierSet::Extern); if (introducer.extern_library.has_value()) { context.TODO(node_id, "extern library"); } auto inheritance_kind = introducer.modifier_set.ToEnum() .Case(KeywordModifierSet::Abstract, SemIR::Class::Abstract) .Case(KeywordModifierSet::Base, SemIR::Class::Base) .Default(SemIR::Class::Final); auto decl_block_id = context.inst_block_stack().Pop(); // Add the class declaration. auto class_decl = SemIR::ClassDecl{.type_id = SemIR::TypeType::TypeId, .class_id = SemIR::ClassId::None, .decl_block_id = decl_block_id}; auto class_decl_id = AddPlaceholderInst(context, node_id, class_decl); // TODO: Store state regarding is_extern. SemIR::Class class_info = { name_context.MakeEntityWithParamsBase(name, class_decl_id, is_extern, SemIR::LibraryNameId::None), {// `.self_type_id` depends on the ClassType, so is set below. .self_type_id = SemIR::TypeId::None, .inheritance_kind = inheritance_kind}}; DiagnoseIfGenericMissingExplicitParameters(context, class_info); MergeOrAddName(context, node_id, name_context, class_decl_id, class_decl, class_info, is_definition, introducer.modifier_set.GetAccessKind()); // Create a new class if this isn't a valid redeclaration. bool is_new_class = !class_decl.class_id.has_value(); if (is_new_class) { // TODO: If this is an invalid redeclaration of a non-class entity or there // was an error in the qualifier, we will have lost track of the class name // here. We should keep track of it even if the name is invalid. class_info.generic_id = BuildGenericDecl(context, class_decl_id); class_decl.class_id = context.classes().Add(class_info); if (class_info.has_parameters()) { class_decl.type_id = GetGenericClassType( context, class_decl.class_id, context.scope_stack().PeekSpecificId()); } } else { auto prev_decl_generic_id = context.classes().Get(class_decl.class_id).generic_id; FinishGenericRedecl(context, prev_decl_generic_id); } // Write the class ID into the ClassDecl. ReplaceInstBeforeConstantUse(context, class_decl_id, class_decl); if (is_new_class) { // TODO: Form this as part of building the definition, not as part of the // declaration. SetClassSelfType(context, class_decl.class_id); } if (!is_definition && context.sem_ir().is_impl() && !is_extern) { context.definitions_required_by_decl().push_back(class_decl_id); } return {class_decl.class_id, class_decl_id}; } auto HandleParseNode(Context& context, Parse::ClassDeclId node_id) -> bool { BuildClassDecl(context, node_id, /*is_definition=*/false); context.decl_name_stack().PopScope(); return true; } auto HandleParseNode(Context& context, Parse::ClassDefinitionStartId node_id) -> bool { auto [class_id, class_decl_id] = BuildClassDecl(context, node_id, /*is_definition=*/true); auto& class_info = context.classes().Get(class_id); StartClassDefinition(context, class_info, class_decl_id); // Enter the class scope. context.scope_stack().PushForEntity( class_decl_id, class_info.scope_id, context.generics().GetSelfSpecific(class_info.generic_id)); StartGenericDefinition(context, class_info.generic_id); context.inst_block_stack().Push(); context.node_stack().Push(node_id, class_id); context.field_decls_stack().PushArray(); context.vtable_stack().Push(); // TODO: Handle the case where there's control flow in the class body. For // example: // // class C { // var v: if true then i32 else f64; // } // // We may need to track a list of instruction blocks here, as we do for a // function. class_info.body_block_id = context.inst_block_stack().PeekOrAdd(); return true; } // Diagnoses a class-specific declaration appearing outside a class. static auto DiagnoseClassSpecificDeclOutsideClass(Context& context, SemIR::LocId loc_id, Lex::TokenKind tok) -> void { CARBON_DIAGNOSTIC(ClassSpecificDeclOutsideClass, Error, "`{0}` declaration outside class", Lex::TokenKind); context.emitter().Emit(loc_id, ClassSpecificDeclOutsideClass, tok); } // Returns the current scope's class declaration, or diagnoses if it isn't a // class. static auto GetCurrentScopeAsClassOrDiagnose(Context& context, SemIR::LocId loc_id, Lex::TokenKind tok) -> std::optional { auto class_scope = context.scope_stack().TryGetCurrentScopeAs(); if (!class_scope) { DiagnoseClassSpecificDeclOutsideClass(context, loc_id, tok); } return class_scope; } // Diagnoses a class-specific declaration that is repeated within a class, but // is not permitted to be repeated. static auto DiagnoseClassSpecificDeclRepeated(Context& context, SemIR::LocId new_loc_id, SemIR::LocId prev_loc_id, Lex::TokenKind tok) -> void { CARBON_DIAGNOSTIC(AdaptDeclRepeated, Error, "multiple `adapt` declarations in class"); CARBON_DIAGNOSTIC(BaseDeclRepeated, Error, "multiple `base` declarations in class; multiple " "inheritance is not permitted"); CARBON_DIAGNOSTIC(ClassSpecificDeclPrevious, Note, "previous `{0}` declaration is here", Lex::TokenKind); CARBON_CHECK(tok == Lex::TokenKind::Adapt || tok == Lex::TokenKind::Base); context.emitter() .Build(new_loc_id, tok == Lex::TokenKind::Adapt ? AdaptDeclRepeated : BaseDeclRepeated) .Note(prev_loc_id, ClassSpecificDeclPrevious, tok) .Emit(); } auto HandleParseNode(Context& context, Parse::AdaptIntroducerId /*node_id*/) -> bool { context.decl_introducer_state_stack().Push(); return true; } auto HandleParseNode(Context& context, Parse::AdaptDeclId node_id) -> bool { auto [adapted_type_node, adapted_type_expr_id] = context.node_stack().PopExprWithNodeId(); // Process modifiers. `extend` is permitted, no others are allowed. auto introducer = context.decl_introducer_state_stack().Pop(); LimitModifiersOnDecl(context, introducer, KeywordModifierSet::Extend); auto parent_class_decl = GetCurrentScopeAsClassOrDiagnose(context, node_id, Lex::TokenKind::Adapt); if (!parent_class_decl) { return true; } auto& class_info = context.classes().Get(parent_class_decl->class_id); if (class_info.adapt_id.has_value()) { DiagnoseClassSpecificDeclRepeated(context, node_id, SemIR::LocId(class_info.adapt_id), Lex::TokenKind::Adapt); return true; } auto [adapted_type_inst_id, adapted_type_id] = ExprAsType(context, node_id, adapted_type_expr_id); if (!RequireConcreteType( context, adapted_type_id, node_id, [&](auto& builder) { CARBON_DIAGNOSTIC(IncompleteTypeInAdaptDecl, Context, "adapted type {0} is an incomplete type", InstIdAsType); builder.Context(node_id, IncompleteTypeInAdaptDecl, adapted_type_inst_id); }, [&](auto& builder) { CARBON_DIAGNOSTIC(AbstractTypeInAdaptDecl, Context, "adapted type {0} is an abstract type", InstIdAsType); builder.Context(node_id, AbstractTypeInAdaptDecl, adapted_type_inst_id); })) { adapted_type_id = SemIR::ErrorInst::TypeId; } if (adapted_type_id == SemIR::ErrorInst::TypeId) { adapted_type_inst_id = SemIR::ErrorInst::TypeInstId; } // Build a SemIR representation for the declaration. class_info.adapt_id = AddInst( context, node_id, {.adapted_type_inst_id = adapted_type_inst_id}); // Extend the class scope with the adapted type's scope if requested. if (introducer.modifier_set.HasAnyOf(KeywordModifierSet::Extend)) { auto& class_scope = context.name_scopes().Get(class_info.scope_id); class_scope.AddExtendedScope({adapted_type_inst_id}); } return true; } auto HandleParseNode(Context& context, Parse::BaseIntroducerId /*node_id*/) -> bool { context.decl_introducer_state_stack().Push(); return true; } auto HandleParseNode(Context& /*context*/, Parse::BaseColonId /*node_id*/) -> bool { return true; } namespace { // Information gathered about a base type specified in a `base` declaration. struct BaseInfo { // A `BaseInfo` representing an erroneous base. static const BaseInfo Error; SemIR::TypeId type_id; SemIR::NameScopeId scope_id; SemIR::TypeInstId inst_id; }; constexpr BaseInfo BaseInfo::Error = {.type_id = SemIR::ErrorInst::TypeId, .scope_id = SemIR::NameScopeId::None, .inst_id = SemIR::ErrorInst::TypeInstId}; } // namespace // Diagnoses an attempt to derive from a final type. static auto DiagnoseBaseIsFinal(Context& context, Parse::NodeId node_id, SemIR::TypeInstId base_type_inst_id) -> void { CARBON_DIAGNOSTIC(BaseIsFinal, Error, "deriving from final type {0}; base type must be an " "`abstract` or `base` class", InstIdAsType); context.emitter().Emit(node_id, BaseIsFinal, base_type_inst_id); } // Checks that the specified base type is valid. static auto CheckBaseType(Context& context, Parse::NodeId node_id, SemIR::InstId base_expr_id) -> BaseInfo { auto [base_type_inst_id, base_type_id] = ExprAsType(context, node_id, base_expr_id); if (base_type_id == SemIR::ErrorInst::TypeId) { return BaseInfo::Error; } if (!RequireCompleteType(context, base_type_id, node_id, [&](auto& builder) { CARBON_DIAGNOSTIC(IncompleteTypeInBaseDecl, Context, "base {0} is an incomplete type", InstIdAsType); builder.Context(node_id, IncompleteTypeInBaseDecl, base_type_inst_id); })) { return BaseInfo::Error; } auto class_type = context.types().TryGetAs(base_type_id); // The base must not be a final class. if (!class_type) { // For now, we treat all types that aren't introduced by a `class` // declaration as being final classes. // TODO: Once we have a better idea of which types are considered to be // classes, produce a better diagnostic for deriving from a non-class type. DiagnoseBaseIsFinal(context, node_id, base_type_inst_id); return BaseInfo::Error; } const auto& base_class_info = context.classes().Get(class_type->class_id); if (base_class_info.inheritance_kind == SemIR::Class::Final) { DiagnoseBaseIsFinal(context, node_id, base_type_inst_id); } CARBON_CHECK(base_class_info.scope_id.has_value(), "Complete class should have a scope"); return {.type_id = base_type_id, .scope_id = base_class_info.scope_id, .inst_id = base_type_inst_id}; } auto HandleParseNode(Context& context, Parse::BaseDeclId node_id) -> bool { auto [base_type_node_id, base_type_expr_id] = context.node_stack().PopExprWithNodeId(); // Process modifiers. `extend` is required, no others are allowed. auto introducer = context.decl_introducer_state_stack().Pop(); LimitModifiersOnDecl(context, introducer, KeywordModifierSet::Extend); if (!introducer.modifier_set.HasAnyOf(KeywordModifierSet::Extend)) { CARBON_DIAGNOSTIC(BaseMissingExtend, Error, "missing `extend` before `base` declaration"); context.emitter().Emit(node_id, BaseMissingExtend); } auto parent_class_decl = GetCurrentScopeAsClassOrDiagnose(context, node_id, Lex::TokenKind::Base); if (!parent_class_decl) { return true; } auto& class_info = context.classes().Get(parent_class_decl->class_id); if (class_info.base_id.has_value()) { DiagnoseClassSpecificDeclRepeated(context, node_id, SemIR::LocId(class_info.base_id), Lex::TokenKind::Base); return true; } if (!context.field_decls_stack().PeekArray().empty()) { // TODO: Add note that includes the first field location as an example. CARBON_DIAGNOSTIC( BaseDeclAfterFieldDecl, Error, "`base` declaration must appear before field declarations"); context.emitter().Emit(node_id, BaseDeclAfterFieldDecl); return true; } auto base_info = CheckBaseType(context, base_type_node_id, base_type_expr_id); // TODO: Should we diagnose if there are already any fields? // The `base` value in the class scope has an unbound element type. Instance // binding will be performed when it's found by name lookup into an instance. auto field_type_id = GetUnboundElementType( context, context.types().GetTypeInstId(class_info.self_type_id), base_info.inst_id); class_info.base_id = AddInst(context, node_id, {.type_id = field_type_id, .base_type_inst_id = base_info.inst_id, .index = SemIR::ElementIndex::None}); if (base_info.type_id != SemIR::ErrorInst::TypeId) { auto base_class_info = context.classes().Get( context.types().GetAs(base_info.type_id).class_id); class_info.is_dynamic |= base_class_info.is_dynamic; } // Bind the name `base` in the class to the base field. context.decl_name_stack().AddNameOrDiagnose( context.decl_name_stack().MakeUnqualifiedName(node_id, SemIR::NameId::Base), class_info.base_id, introducer.modifier_set.GetAccessKind()); // Extend the class scope with the base class. if (introducer.modifier_set.HasAnyOf(KeywordModifierSet::Extend)) { auto& class_scope = context.name_scopes().Get(class_info.scope_id); if (base_info.scope_id.has_value()) { class_scope.AddExtendedScope({base_info.inst_id}); } else { class_scope.set_has_error(); } } return true; } auto HandleParseNode(Context& context, Parse::ClassDefinitionId node_id) -> bool { auto class_id = context.node_stack().Pop(); // The class type is now fully defined. Compute its object representation. ComputeClassObjectRepr(context, node_id, class_id, context.field_decls_stack().PeekArray(), context.vtable_stack().PeekCurrentBlockContents(), context.inst_block_stack().PeekCurrentBlockContents()); context.inst_block_stack().Pop(); context.field_decls_stack().PopArray(); context.vtable_stack().Pop(); FinishGenericDefinition(context, context.classes().Get(class_id).generic_id); // The decl_name_stack and scopes are popped by `ProcessNodeIds`. return true; } } // namespace Carbon::Check ================================================ FILE: toolchain/check/handle_codeblock.cpp ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #include "toolchain/check/context.h" #include "toolchain/check/handle.h" #include "toolchain/check/unused.h" namespace Carbon::Check { auto HandleParseNode(Context& context, Parse::CodeBlockStartId node_id) -> bool { context.node_stack().Push(node_id); context.scope_stack().PushForSameRegion(); return true; } auto HandleParseNode(Context& context, Parse::CodeBlockId /*node_id*/) -> bool { context.scope_stack().Pop(/*check_unused=*/true); context.node_stack() .PopAndDiscardSoloNodeId(); return true; } } // namespace Carbon::Check ================================================ FILE: toolchain/check/handle_export.cpp ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #include "toolchain/check/context.h" #include "toolchain/check/decl_name_stack.h" #include "toolchain/check/generic.h" #include "toolchain/check/handle.h" #include "toolchain/check/inst.h" #include "toolchain/check/modifiers.h" #include "toolchain/check/name_component.h" #include "toolchain/check/name_lookup.h" #include "toolchain/parse/typed_nodes.h" #include "toolchain/sem_ir/ids.h" #include "toolchain/sem_ir/typed_insts.h" namespace Carbon::Check { auto HandleParseNode(Context& context, Parse::ExportIntroducerId /*node_id*/) -> bool { // Export declarations can't be generic, but we might have parsed a generic // parameter in their name, so enter a generic scope just in case. StartGenericDecl(context); context.decl_introducer_state_stack().Push(); // TODO: Probably need to update DeclNameStack to restrict to only namespaces. context.decl_name_stack().PushScopeAndStartName(); return true; } auto HandleParseNode(Context& context, Parse::ExportDeclId node_id) -> bool { auto name_context = context.decl_name_stack().FinishName( PopNameComponentWithoutParams(context, Lex::TokenKind::Export)); DiscardGenericDecl(context); context.decl_name_stack().PopScope(); auto introducer = context.decl_introducer_state_stack().Pop(); LimitModifiersOnDecl(context, introducer, KeywordModifierSet::None); if (name_context.state == DeclNameStack::NameContext::State::Error) { // Should already be diagnosed. return true; } // Exporting uses the decl name primarily for lookup, so treat poisoning the // same as "not found". auto inst_id = name_context.state == DeclNameStack::NameContext::State::Poisoned ? SemIR::InstId::None : name_context.prev_inst_id(); if (!inst_id.has_value()) { DiagnoseNameNotFound(context, node_id, name_context.name_id_for_new_inst()); return true; } auto inst = context.insts().Get(inst_id); if (inst.Is()) { CARBON_DIAGNOSTIC(ExportRedundant, Warning, "`export` matches previous `export`"); CARBON_DIAGNOSTIC(ExportPrevious, Note, "previous `export` here"); context.emitter() .Build(node_id, ExportRedundant) // Use the location of the export itself, not the exported instruction. // // TODO: This construction of a LocId that does not just contain the // InstId prevents GetAbsoluteNodeIdImpl() from seeing the `ExportDecl` // instruction, which prevents it from chasing through it to the entity // being exported. It might be nice to make this more explicit. .Note(context.insts().GetCanonicalLocId(inst_id), ExportPrevious) .Emit(); return true; } auto import_ref = context.insts().TryGetAs(inst_id); if (!import_ref) { CARBON_DIAGNOSTIC(ExportNotImportedEntity, Error, "only imported entities are valid for `export`"); CARBON_DIAGNOSTIC(ExportNotImportedEntitySource, Note, "name is declared here"); context.emitter() .Build(node_id, ExportNotImportedEntity) .Note(inst_id, ExportNotImportedEntitySource) .Emit(); return true; } auto export_id = AddInst(context, node_id, {.type_id = import_ref->type_id, .entity_name_id = import_ref->entity_name_id, .value_id = inst_id}); context.exports().push_back(export_id); // Replace the ImportRef in name lookup, both for the above duplicate // diagnostic and so that cross-package imports can find it easily. auto entity_name = context.entity_names().Get(import_ref->entity_name_id); auto& parent_scope = context.name_scopes().Get(entity_name.parent_scope_id); auto& scope_result = parent_scope.GetEntry(*parent_scope.Lookup(entity_name.name_id)).result; CARBON_CHECK(scope_result.target_inst_id() == inst_id); scope_result = SemIR::ScopeLookupResult::MakeFound( export_id, scope_result.access_kind()); return true; } } // namespace Carbon::Check ================================================ FILE: toolchain/check/handle_expr_statement.cpp ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #include "toolchain/check/context.h" #include "toolchain/check/convert.h" #include "toolchain/check/handle.h" #include "toolchain/sem_ir/inst.h" namespace Carbon::Check { auto HandleParseNode(Context& context, Parse::ExprStatementId /*node_id*/) -> bool { DiscardExpr(context, context.node_stack().PopExpr()); return true; } } // namespace Carbon::Check ================================================ FILE: toolchain/check/handle_file.cpp ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #include "toolchain/check/context.h" #include "toolchain/check/handle.h" namespace Carbon::Check { auto HandleParseNode(Context& /*context*/, Parse::FileStartId /*node_id*/) -> bool { // No action to perform. // TODO: We may want to push `FileStart` as a sentinel so that `Peek`s can't // fail. return true; } auto HandleParseNode(Context& /*context*/, Parse::FileEndId /*node_id*/) -> bool { // No action to perform. return true; } } // namespace Carbon::Check ================================================ FILE: toolchain/check/handle_form_literal.cpp ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #include "toolchain/check/context.h" #include "toolchain/check/convert.h" #include "toolchain/check/handle.h" #include "toolchain/check/inst.h" #include "toolchain/parse/node_category.h" namespace Carbon::Check { auto HandleParseNode(Context& context, Parse::RefPrimitiveFormId node_id) -> bool { auto [type_node_id, type_inst_id] = context.node_stack().PopExprWithNodeId(); auto type_expr = ExprAsType(context, type_node_id, type_inst_id); auto inst_id = AddInst(context, node_id, {.type_id = SemIR::FormType::TypeId, .type_component_inst_id = type_expr.inst_id}); context.node_stack().Push(node_id, inst_id); return true; } auto HandleParseNode(Context& context, Parse::ValPrimitiveFormId node_id) -> bool { auto [type_node_id, type_inst_id] = context.node_stack().PopExprWithNodeId(); auto type_expr = ExprAsType(context, type_node_id, type_inst_id); auto inst_id = AddInst(context, node_id, {.type_id = SemIR::FormType::TypeId, .type_component_inst_id = type_expr.inst_id}); context.node_stack().Push(node_id, inst_id); return true; } auto HandleParseNode(Context& context, Parse::VarPrimitiveFormId node_id) -> bool { auto [type_node_id, type_inst_id] = context.node_stack().PopExprWithNodeId(); auto type_expr = ExprAsType(context, type_node_id, type_inst_id); auto inst_id = AddInst(context, node_id, {.type_id = SemIR::FormType::TypeId, .type_component_inst_id = type_expr.inst_id}); context.node_stack().Push(node_id, inst_id); return true; } auto HandleParseNode(Context& /*context*/, Parse::FormLiteralKeywordId /*node_id*/) -> bool { return true; } auto HandleParseNode(Context& /*context*/, Parse::FormLiteralOpenParenId /*node_id*/) -> bool { return true; } auto HandleParseNode(Context& /*context*/, Parse::FormLiteralId /*node_id*/) -> bool { return true; } } // namespace Carbon::Check ================================================ FILE: toolchain/check/handle_function.cpp ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #include #include #include "toolchain/base/kind_switch.h" #include "toolchain/check/context.h" #include "toolchain/check/control_flow.h" #include "toolchain/check/convert.h" #include "toolchain/check/decl_introducer_state.h" #include "toolchain/check/generic.h" #include "toolchain/check/handle.h" #include "toolchain/check/import_ref.h" #include "toolchain/check/interface.h" #include "toolchain/check/literal.h" #include "toolchain/check/merge.h" #include "toolchain/check/modifiers.h" #include "toolchain/check/name_component.h" #include "toolchain/check/name_lookup.h" #include "toolchain/check/return.h" #include "toolchain/check/type.h" #include "toolchain/check/type_completion.h" #include "toolchain/check/unused.h" #include "toolchain/lex/token_kind.h" #include "toolchain/parse/node_ids.h" #include "toolchain/sem_ir/builtin_function_kind.h" #include "toolchain/sem_ir/entry_point.h" #include "toolchain/sem_ir/function.h" #include "toolchain/sem_ir/ids.h" #include "toolchain/sem_ir/inst.h" #include "toolchain/sem_ir/typed_insts.h" namespace Carbon::Check { auto HandleParseNode(Context& context, Parse::FunctionIntroducerId node_id) -> bool { // The function is potentially generic. StartGenericDecl(context); // Create an instruction block to hold the instructions created as part of the // function signature, such as parameter and return types. context.inst_block_stack().Push(); // Push the bracketing node. context.node_stack().Push(node_id); // Optional modifiers and the name follow. context.decl_introducer_state_stack().Push(); context.decl_name_stack().PushScopeAndStartName(); return true; } // Handles a `->` or `->?` return declaration. static auto HandleReturnDecl(Context& context, Parse::AnyReturnDeclId node_id) -> bool { auto [expr_node_id, expr_inst_id] = context.node_stack().PopExprWithNodeId(); Context::FormExpr form_expr = [&]() { if (context.parse_tree().node_kind(node_id) == Parse::ReturnTypeId::Kind) { return ReturnExprAsForm(context, expr_node_id, expr_inst_id); } else { return FormExprAsForm(context, expr_node_id, expr_inst_id); } }(); context.PushReturnForm(form_expr); auto return_patterns_id = AddReturnPatterns(context, node_id, form_expr); context.node_stack().Push(node_id, return_patterns_id); return true; } auto HandleParseNode(Context& context, Parse::ReturnTypeId node_id) -> bool { return HandleReturnDecl(context, node_id); } auto HandleParseNode(Context& context, Parse::ReturnFormId node_id) -> bool { return HandleReturnDecl(context, node_id); } // Diagnoses issues with the modifiers, removing modifiers that shouldn't be // present. static auto DiagnoseModifiers(Context& context, Parse::AnyFunctionDeclId node_id, DeclIntroducerState& introducer, bool is_definition, SemIR::NameScopeId parent_scope_id, SemIR::InstId parent_scope_inst_id, std::optional parent_scope_inst, SemIR::InstId self_param_id) -> void { CheckAccessModifiersOnDecl(context, introducer, parent_scope_inst); LimitModifiersOnDecl( context, introducer, KeywordModifierSet::Access | KeywordModifierSet::Extern | KeywordModifierSet::Export | KeywordModifierSet::Method | KeywordModifierSet::Interface | KeywordModifierSet::Evaluation); RestrictExternModifierOnDecl(context, introducer, parent_scope_inst, is_definition); CheckMethodModifiersOnFunction(context, introducer, parent_scope_inst_id, parent_scope_inst); RequireDefaultFinalOnlyInInterfaces(context, introducer, parent_scope_id); if (introducer.modifier_set.HasAnyOf(KeywordModifierSet::Interface)) { // TODO: Once we are saving the modifiers for a function, add check that // the function may only be defined if it is marked `default` or `final`. context.TODO(introducer.modifier_node_id(ModifierOrder::Decl), "interface modifier"); } if (!self_param_id.has_value() && introducer.modifier_set.HasAnyOf(KeywordModifierSet::Method)) { CARBON_DIAGNOSTIC(VirtualWithoutSelf, Error, "virtual class function"); context.emitter().Emit(node_id, VirtualWithoutSelf); introducer.modifier_set.Remove(KeywordModifierSet::Method); } } // Returns the virtual-family modifier as an enum. static auto GetVirtualModifier(const KeywordModifierSet& modifier_set) -> SemIR::Function::VirtualModifier { return modifier_set.ToEnum() .Case(KeywordModifierSet::Virtual, SemIR::Function::VirtualModifier::Virtual) .Case(KeywordModifierSet::Abstract, SemIR::Function::VirtualModifier::Abstract) .Case(KeywordModifierSet::Override, SemIR::Function::VirtualModifier::Override) .Default(SemIR::Function::VirtualModifier::None); } // Returns the evaluation modifier as an enum. static auto GetEvaluationMode(const KeywordModifierSet& modifier_set) -> SemIR::Function::EvaluationMode { return modifier_set.ToEnum() .Case(KeywordModifierSet::Eval, SemIR::Function::EvaluationMode::Eval) .Case(KeywordModifierSet::MustEval, SemIR::Function::EvaluationMode::MustEval) .Default(SemIR::Function::EvaluationMode::None); } // Tries to merge new_function into prev_function_id. Since new_function won't // have a definition even if one is upcoming, set is_definition to indicate the // planned result. // // If merging is successful, returns true and may update the previous function. // Otherwise, returns false. Prints a diagnostic when appropriate. static auto MergeFunctionRedecl(Context& context, Parse::AnyFunctionDeclId node_id, SemIR::Function& new_function, bool new_is_definition, SemIR::FunctionId prev_function_id, SemIR::ImportIRId prev_import_ir_id) -> bool { auto& prev_function = context.functions().Get(prev_function_id); if (!CheckFunctionTypeMatches(context, new_function, prev_function)) { return false; } DiagnoseIfInvalidRedecl( context, Lex::TokenKind::Fn, prev_function.name_id, RedeclInfo(new_function, node_id, new_is_definition), RedeclInfo(prev_function, SemIR::LocId(prev_function.latest_decl_id()), prev_function.has_definition_started()), prev_import_ir_id); if (new_is_definition && prev_function.has_definition_started()) { return false; } if (!prev_function.first_owning_decl_id.has_value()) { prev_function.first_owning_decl_id = new_function.first_owning_decl_id; } if (new_is_definition) { // Track the signature from the definition, so that IDs in the body // match IDs in the signature. prev_function.MergeDefinition(new_function); prev_function.call_param_patterns_id = new_function.call_param_patterns_id; prev_function.call_params_id = new_function.call_params_id; prev_function.return_type_inst_id = new_function.return_type_inst_id; prev_function.return_form_inst_id = new_function.return_form_inst_id; prev_function.return_patterns_id = new_function.return_patterns_id; prev_function.self_param_id = new_function.self_param_id; } if (prev_import_ir_id.has_value()) { ReplacePrevInstForMerge(context, new_function.parent_scope_id, prev_function.name_id, new_function.first_owning_decl_id); } return true; } // Check whether this is a redeclaration, merging if needed. static auto TryMergeRedecl(Context& context, Parse::AnyFunctionDeclId node_id, const DeclNameStack::NameContext& name_context, SemIR::FunctionDecl& function_decl, SemIR::Function& function_info, bool is_definition) -> void { if (name_context.state == DeclNameStack::NameContext::State::Poisoned) { DiagnosePoisonedName(context, name_context.name_id_for_new_inst(), name_context.poisoning_loc_id, name_context.loc_id); return; } auto prev_id = name_context.prev_inst_id(); if (!prev_id.has_value()) { return; } auto prev_function_id = SemIR::FunctionId::None; auto prev_type_id = SemIR::TypeId::None; auto prev_import_ir_id = SemIR::ImportIRId::None; CARBON_KIND_SWITCH(context.insts().Get(prev_id)) { case CARBON_KIND(SemIR::AssociatedEntity assoc_entity): { // This is a function in an interface definition scope (see // NameScope::is_interface_definition()). auto function_decl = context.insts().GetAs(assoc_entity.decl_id); prev_function_id = function_decl.function_id; prev_type_id = function_decl.type_id; break; } case CARBON_KIND(SemIR::FunctionDecl function_decl): { prev_function_id = function_decl.function_id; prev_type_id = function_decl.type_id; break; } case SemIR::ImportRefLoaded::Kind: { auto import_ir_inst = GetCanonicalImportIRInst(context, prev_id); // Verify the decl so that things like aliases are name conflicts. const auto* import_ir = context.import_irs().Get(import_ir_inst.ir_id()).sem_ir; if (!import_ir->insts().Is( import_ir_inst.inst_id())) { break; } // Use the type to get the ID. if (auto struct_value = context.insts().TryGetAs( context.constant_values().GetConstantInstId(prev_id))) { if (auto function_type = context.types().TryGetAs( struct_value->type_id)) { prev_function_id = function_type->function_id; prev_type_id = struct_value->type_id; prev_import_ir_id = import_ir_inst.ir_id(); } } break; } default: break; } if (!prev_function_id.has_value()) { DiagnoseDuplicateName(context, name_context.name_id, name_context.loc_id, SemIR::LocId(prev_id)); return; } if (MergeFunctionRedecl(context, node_id, function_info, is_definition, prev_function_id, prev_import_ir_id)) { // When merging, use the existing function rather than adding a new one. function_decl.function_id = prev_function_id; function_decl.type_id = prev_type_id; } } // Adds the declaration to name lookup when appropriate. static auto MaybeAddToNameLookup(Context& context, const DeclNameStack::NameContext& name_context, const KeywordModifierSet& modifier_set, SemIR::NameScopeId parent_scope_id, SemIR::InstId decl_id) -> void { if (name_context.state == DeclNameStack::NameContext::State::Poisoned || name_context.prev_inst_id().has_value()) { return; } // At interface scope, a function declaration introduces an associated // function. auto lookup_result_id = decl_id; if (parent_scope_id.has_value() && !name_context.has_qualifiers) { const auto& parent_scope = context.name_scopes().Get(parent_scope_id); if (parent_scope.is_interface_definition()) { auto interface_decl = context.insts().GetAs( parent_scope.inst_id()); lookup_result_id = BuildAssociatedEntity(context, interface_decl.interface_id, decl_id); } } context.decl_name_stack().AddName(name_context, lookup_result_id, modifier_set.GetAccessKind()); } // Returns whether the given type is `i32`. static auto IsI32(Context& context, Parse::NodeId node_id, SemIR::TypeId type_id) -> bool { return type_id == MakeIntType(context, node_id, SemIR::IntKind::Signed, context.ints().Add(32)); } // Returns whether the given parameter list is valid for the entry point // function `Main.Run`. static auto IsValidEntryPointParamList(Context& context, Parse::NodeId node_id, SemIR::InstBlockId param_patterns_id) -> bool { if (!param_patterns_id.has_value()) { // Positional parameters for are not supported. return false; } for (auto [index, param_pattern_id] : llvm::enumerate(context.inst_blocks().Get(param_patterns_id))) { if (param_pattern_id == SemIR::ErrorInst::InstId) { // Ignore erroneous parameters. continue; } auto param = context.insts().TryGetAs(param_pattern_id); if (!param) { // Only value parameters are supported for now. return false; } if (param->type_id == SemIR::ErrorInst::TypeId) { // Ignore parameters with erroneous types. continue; } auto param_type_inst_id = context.types() .GetAs(param->type_id) .scrutinee_type_inst_id; switch (index) { case 0: { // `argc` should be a 32-bit integer. if (!IsI32( context, node_id, context.types().GetTypeIdForTypeInstId(param_type_inst_id))) { return false; } break; } case 1: { // `argv` should be a pointer. // TODO: Consider checking the pointee type also. if (!context.insts().Is(param_type_inst_id)) { return false; } break; } default: { // TODO: Decide whether to allow a third `envp` parameter. return false; } } } return true; } // Returns whether the given return type is valid for the entry point // function `Main.Run`. static auto IsValidEntryPointReturnType(Context& context, Parse::NodeId node_id, SemIR::TypeId return_type_id) -> bool { // An implicit or explicit return type of `()` is OK. // TODO: Translate this to returning an `i32` with value `0` in lowering. if (!return_type_id.has_value()) { return true; } if (return_type_id == GetTupleType(context, {})) { return true; } if (IsI32(context, node_id, return_type_id)) { // Explicit return type of `i32` or an adapter for it is OK. return true; } // For now, disallow anything else. // TODO: Decide on valid return types for `Main.Run`. Perhaps we should // have an interface for this. return false; } // If the function is the entry point, do corresponding validation. static auto ValidateForEntryPoint(Context& context, Parse::AnyFunctionDeclId node_id, SemIR::FunctionId function_id, const SemIR::Function& function_info) -> void { if (!SemIR::IsEntryPoint(context.sem_ir(), function_id)) { return; } // TODO: Update this once valid signatures for the entry point are decided. // See https://github.com/carbon-language/carbon-lang/issues/6735 if (function_info.implicit_param_patterns_id.has_value() || !IsValidEntryPointParamList(context, node_id, function_info.param_patterns_id)) { CARBON_DIAGNOSTIC(InvalidMainRunParameters, Error, "invalid parameters for `Main.Run` function; expected " "`()` or `(argc: i32, argv: Core.Optional(char*)*)`"); context.emitter().Emit(node_id, InvalidMainRunParameters); } else if (!IsValidEntryPointReturnType( context, node_id, function_info.GetDeclaredReturnType(context.sem_ir()))) { CARBON_DIAGNOSTIC(InvalidMainRunReturnType, Error, "invalid return type for `Main.Run` function; expected " "`fn (...)` or `fn (...) -> i32`"); context.emitter().Emit(node_id, InvalidMainRunReturnType); } } static auto IsGenericFunction(Context& context, SemIR::GenericId function_generic_id, SemIR::GenericId class_generic_id) -> bool { if (function_generic_id == SemIR::GenericId::None) { return false; } if (class_generic_id == SemIR::GenericId::None) { return true; } const auto& function_generic = context.generics().Get(function_generic_id); const auto& class_generic = context.generics().Get(class_generic_id); auto function_bindings = context.inst_blocks().Get(function_generic.bindings_id); auto class_bindings = context.inst_blocks().Get(class_generic.bindings_id); // If the function's bindings are the same size as the class's bindings, // then there are no extra bindings for the function, so it is effectively // non-generic within the scope of a specific of the class. return class_bindings.size() != function_bindings.size(); } // Requests a vtable be created when processing a virtual function. static auto RequestVtableIfVirtual( Context& context, Parse::AnyFunctionDeclId node_id, SemIR::Function::VirtualModifier& virtual_modifier, const std::optional& parent_scope_inst, SemIR::InstId decl_id, SemIR::GenericId generic_id) -> void { // In order to request a vtable, the function must be virtual, and in a class // scope. if (virtual_modifier == SemIR::Function::VirtualModifier::None || !parent_scope_inst) { return; } auto class_decl = parent_scope_inst->TryAs(); if (!class_decl) { return; } auto& class_info = context.classes().Get(class_decl->class_id); if (virtual_modifier == SemIR::Function::VirtualModifier::Override && !class_info.base_id.has_value()) { CARBON_DIAGNOSTIC(OverrideWithoutBase, Error, "override without base class"); context.emitter().Emit(node_id, OverrideWithoutBase); virtual_modifier = SemIR::Function::VirtualModifier::None; return; } if (IsGenericFunction(context, generic_id, class_info.generic_id)) { CARBON_DIAGNOSTIC(GenericVirtual, Error, "generic virtual function"); context.emitter().Emit(node_id, GenericVirtual); virtual_modifier = SemIR::Function::VirtualModifier::None; return; } // TODO: If this is an `impl` function, check there's a matching base // function that's impl or virtual. class_info.is_dynamic = true; context.vtable_stack().AddInstId(decl_id); } // Diagnoses when positional params aren't supported. Reassigns the pattern // block if needed. static auto DiagnosePositionalParams(Context& context, SemIR::Function& function_info) -> void { if (function_info.param_patterns_id.has_value()) { return; } context.TODO(function_info.latest_decl_id(), "function with positional parameters"); function_info.param_patterns_id = SemIR::InstBlockId::Empty; } // Build a FunctionDecl describing the signature of a function. This // handles the common logic shared by function declaration syntax and function // definition syntax. static auto BuildFunctionDecl(Context& context, Parse::AnyFunctionDeclId node_id, bool is_definition) -> std::pair { auto return_patterns_id = SemIR::InstBlockId::None; auto return_type_inst_id = SemIR::TypeInstId::None; auto return_form_inst_id = SemIR::InstId::None; if (auto [return_node, maybe_return_patterns_id] = context.node_stack() .PopWithNodeIdIf(); maybe_return_patterns_id) { return_patterns_id = *maybe_return_patterns_id; auto return_form = context.PopReturnForm(); return_type_inst_id = return_form.type_component_inst_id; return_form_inst_id = return_form.form_inst_id; } auto name = PopNameComponent(context, return_patterns_id); auto name_context = context.decl_name_stack().FinishName(name); context.node_stack() .PopAndDiscardSoloNodeId(); auto self_param_id = FindSelfPattern(context, name.implicit_param_patterns_id); // Process modifiers. auto [parent_scope_inst_id, parent_scope_inst] = context.name_scopes().GetInstIfValid(name_context.parent_scope_id); auto introducer = context.decl_introducer_state_stack().Pop(); DiagnoseModifiers(context, node_id, introducer, is_definition, name_context.parent_scope_id, parent_scope_inst_id, parent_scope_inst, self_param_id); bool is_extern = introducer.modifier_set.HasAnyOf(KeywordModifierSet::Extern); auto virtual_modifier = GetVirtualModifier(introducer.modifier_set); auto evaluation_mode = GetEvaluationMode(introducer.modifier_set); // Add the function declaration. SemIR::FunctionDecl function_decl = {SemIR::TypeId::None, SemIR::FunctionId::None, context.inst_block_stack().Pop()}; auto decl_id = AddPlaceholderInst(context, node_id, function_decl); // Build the function entity. This will be merged into an existing function if // there is one, or otherwise added to the function store. auto function_info = SemIR::Function{name_context.MakeEntityWithParamsBase( name, decl_id, is_extern, introducer.extern_library), {.call_param_patterns_id = name.call_param_patterns_id, .call_params_id = name.call_params_id, .call_param_ranges = name.param_ranges, .return_type_inst_id = return_type_inst_id, .return_form_inst_id = return_form_inst_id, .return_patterns_id = return_patterns_id, .virtual_modifier = virtual_modifier, .evaluation_mode = evaluation_mode, .self_param_id = self_param_id}}; if (is_definition) { function_info.definition_id = decl_id; } DiagnosePositionalParams(context, function_info); TryMergeRedecl(context, node_id, name_context, function_decl, function_info, is_definition); // Create a new function if this isn't a valid redeclaration. if (!function_decl.function_id.has_value()) { if (function_info.is_extern && context.sem_ir().is_impl()) { DiagnoseExternRequiresDeclInApiFile(context, node_id); } function_info.generic_id = BuildGenericDecl(context, decl_id); function_decl.function_id = context.functions().Add(function_info); function_decl.type_id = GetFunctionType(context, function_decl.function_id, context.scope_stack().PeekSpecificId()); } else { auto prev_decl_generic_id = context.functions().Get(function_decl.function_id).generic_id; FinishGenericRedecl(context, prev_decl_generic_id); // TODO: Validate that the redeclaration doesn't set an access modifier. } RequestVtableIfVirtual(context, node_id, function_info.virtual_modifier, parent_scope_inst, decl_id, function_info.generic_id); // Write the function ID into the FunctionDecl. ReplaceInstBeforeConstantUse(context, decl_id, function_decl); // Diagnose 'definition of `abstract` function' using the canonical Function's // modifiers. if (is_definition && context.functions().Get(function_decl.function_id).virtual_modifier == SemIR::Function::VirtualModifier::Abstract) { CARBON_DIAGNOSTIC(DefinedAbstractFunction, Error, "definition of `abstract` function"); context.emitter().Emit(LocIdForDiagnostics::TokenOnly(node_id), DefinedAbstractFunction); } // Add to name lookup if needed, now that the decl is built. MaybeAddToNameLookup(context, name_context, introducer.modifier_set, name_context.parent_scope_id, decl_id); ValidateForEntryPoint(context, node_id, function_decl.function_id, function_info); if (!is_definition && context.sem_ir().is_impl() && !is_extern) { context.definitions_required_by_decl().push_back(decl_id); } return {function_decl.function_id, decl_id}; } // Checks that "unused" marker is only used in definitions, and emits a // diagnostic for every binding that is marked unused. static auto CheckUnusedBindingsInPattern(Context& context, SemIR::InstId pattern_id) -> void { llvm::SmallVector work_list; work_list.push_back(pattern_id); while (!work_list.empty()) { auto current_id = work_list.pop_back_val(); auto inst = context.insts().Get(current_id); CARBON_KIND_SWITCH(inst) { case CARBON_KIND_ANY(SemIR::AnyParamPattern, param): { work_list.push_back(param.subpattern_id); break; } case CARBON_KIND_ANY(SemIR::AnyBindingPattern, bind): { auto& entity_name = context.entity_names().Get(bind.entity_name_id); // We need special treatment for the name "_" which is implicitly // unused but actually permitted in declarations. if (entity_name.is_unused && entity_name.name_id != SemIR::NameId::Underscore) { CARBON_DIAGNOSTIC(UnusedModifierOnDeclaration, Error, "`unused` modifier on declaration"); context.emitter().Emit(current_id, UnusedModifierOnDeclaration); } break; } case CARBON_KIND(SemIR::VarPattern var_pattern): { work_list.push_back(var_pattern.subpattern_id); break; } case CARBON_KIND(SemIR::TuplePattern tuple_pattern): { auto elements = context.inst_blocks().Get(tuple_pattern.elements_id); for (auto element_id : llvm::reverse(elements)) { work_list.push_back(element_id); } break; } default: break; } } } static auto DiagnoseUnusedMarkersInDeclaration(Context& context, SemIR::FunctionId function_id) -> void { const auto& function = context.functions().Get(function_id); if (function.param_patterns_id.has_value()) { for (auto pattern_id : context.inst_blocks().Get(function.param_patterns_id)) { CheckUnusedBindingsInPattern(context, pattern_id); } } } auto HandleParseNode(Context& context, Parse::FunctionDeclId node_id) -> bool { auto [function_id, decl_id] = BuildFunctionDecl(context, node_id, /*is_definition=*/false); DiagnoseUnusedMarkersInDeclaration(context, function_id); context.decl_name_stack().PopScope(); return true; } // Processes a function definition after a signature for which we have already // built a function ID. This logic is shared between processing regular function // definitions and delayed parsing of inline method definitions. static auto HandleFunctionDefinitionAfterSignature( Context& context, Parse::FunctionDefinitionStartId node_id, SemIR::FunctionId function_id, SemIR::InstId decl_id) -> void { StartFunctionDefinition(context, decl_id, function_id); context.node_stack().Push(node_id, function_id); } auto HandleFunctionDefinitionSuspend(Context& context, Parse::FunctionDefinitionStartId node_id) -> DeferredDefinitionWorklist::SuspendedFunction { // Process the declaration portion of the function. auto [function_id, decl_id] = BuildFunctionDecl(context, node_id, /*is_definition=*/true); return {.function_id = function_id, .decl_id = decl_id, .saved_name_state = context.decl_name_stack().Suspend()}; } auto HandleFunctionDefinitionResume( Context& context, Parse::FunctionDefinitionStartId node_id, DeferredDefinitionWorklist::SuspendedFunction&& suspended_fn) -> void { context.decl_name_stack().Restore(std::move(suspended_fn.saved_name_state)); HandleFunctionDefinitionAfterSignature( context, node_id, suspended_fn.function_id, suspended_fn.decl_id); } auto HandleParseNode(Context& context, Parse::FunctionDefinitionStartId node_id) -> bool { // Process the declaration portion of the function. auto [function_id, decl_id] = BuildFunctionDecl(context, node_id, /*is_definition=*/true); HandleFunctionDefinitionAfterSignature(context, node_id, function_id, decl_id); return true; } auto HandleParseNode(Context& context, Parse::FunctionDefinitionId node_id) -> bool { SemIR::FunctionId function_id = context.node_stack().Pop(); // If the `}` of the function is reachable, reject if we need a return value // and otherwise add an implicit `return;`. if (IsCurrentPositionReachable(context)) { if (context.functions().Get(function_id).return_form_inst_id.has_value()) { CARBON_DIAGNOSTIC( MissingReturnStatement, Error, "missing `return` at end of function with declared return type"); context.emitter().Emit(LocIdForDiagnostics::TokenOnly(node_id), MissingReturnStatement); } else { AddReturnCleanupBlock(context, node_id); } } FinishFunctionDefinition(context, function_id); context.decl_name_stack().PopScope(/*check_unused=*/true); return true; } auto HandleParseNode(Context& context, Parse::BuiltinFunctionDefinitionStartId node_id) -> bool { // Process the declaration portion of the function. auto [function_id, _] = BuildFunctionDecl(context, node_id, /*is_definition=*/true); context.node_stack().Push(node_id, function_id); return true; } auto HandleParseNode(Context& context, Parse::BuiltinNameId node_id) -> bool { context.node_stack().Push(node_id); return true; } // Looks up a builtin function kind given its name as a string. // TODO: Move this out to another file. static auto LookupBuiltinFunctionKind(Context& context, Parse::BuiltinNameId name_id) -> SemIR::BuiltinFunctionKind { auto builtin_name = context.string_literal_values().Get( context.tokens().GetStringLiteralValue( context.parse_tree().node_token(name_id))); auto kind = SemIR::BuiltinFunctionKind::ForBuiltinName(builtin_name); if (kind == SemIR::BuiltinFunctionKind::None) { CARBON_DIAGNOSTIC(UnknownBuiltinFunctionName, Error, "unknown builtin function name \"{0}\"", std::string); context.emitter().Emit(name_id, UnknownBuiltinFunctionName, builtin_name.str()); } return kind; } auto HandleParseNode(Context& context, Parse::BuiltinFunctionDefinitionId /*node_id*/) -> bool { auto name_id = context.node_stack().PopForSoloNodeId(); auto [fn_node_id, function_id] = context.node_stack() .PopWithNodeId(); auto builtin_kind = LookupBuiltinFunctionKind(context, name_id); if (builtin_kind != SemIR::BuiltinFunctionKind::None) { CheckFunctionDefinitionSignature(context, function_id); auto& function = context.functions().Get(function_id); if (IsValidBuiltinDeclaration(context, function, builtin_kind)) { function.SetBuiltinFunction(builtin_kind); // Build an empty generic definition if this is a generic builtin. StartGenericDefinition(context, function.generic_id); FinishGenericDefinition(context, function.generic_id); } else { CARBON_DIAGNOSTIC(InvalidBuiltinSignature, Error, "invalid signature for builtin function \"{0}\"", std::string); context.emitter().Emit(fn_node_id, InvalidBuiltinSignature, builtin_kind.name().str()); } } context.decl_name_stack().PopScope(); return true; } auto HandleParseNode(Context& context, Parse::FunctionTerseDefinitionId node_id) -> bool { return context.TODO(node_id, "HandleFunctionTerseDefinition"); } } // namespace Carbon::Check ================================================ FILE: toolchain/check/handle_if_expr.cpp ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #include "toolchain/check/context.h" #include "toolchain/check/control_flow.h" #include "toolchain/check/convert.h" #include "toolchain/check/handle.h" #include "toolchain/check/literal.h" namespace Carbon::Check { auto HandleParseNode(Context& context, Parse::IfExprIfId node_id) -> bool { // Alias node_id for if/then/else consistency. auto& if_node = node_id; auto [cond_node, cond_value_id] = context.node_stack().PopExprWithNodeId(); // Convert the condition to `bool`, and branch on it. cond_value_id = ConvertToBoolValue(context, if_node, cond_value_id); context.node_stack().Push(cond_node, cond_value_id); auto then_block_id = AddDominatedBlockAndBranchIf(context, if_node, cond_value_id); auto else_block_id = AddDominatedBlockAndBranch(context, if_node); // Start emitting the `then` block. context.inst_block_stack().Pop(); context.inst_block_stack().Push(then_block_id); context.region_stack().AddToRegion(then_block_id, node_id); context.node_stack().Push(if_node, else_block_id); return true; } // If the operand is an `IntLiteral`, convert it to a suitably-sized `Int` type. // TODO: For now we always pick `i32`. static auto DecayIntLiteralToSizedInt(Context& context, Parse::NodeId node_id, SemIR::InstId operand_id) -> SemIR::InstId { if (context.types().GetTypeInstId( context.insts().Get(operand_id).type_id()) == SemIR::IntLiteralType::TypeInstId) { operand_id = ConvertToValueOfType( context, node_id, operand_id, MakeIntType(context, node_id, SemIR::IntKind::Signed, context.ints().Add(32))); } return operand_id; } auto HandleParseNode(Context& context, Parse::IfExprThenId node_id) -> bool { auto then_value_id = context.node_stack().PopExpr(); auto else_block_id = context.node_stack().Peek(); // Convert the first operand to a value. then_value_id = ConvertToValueExpr(context, then_value_id); then_value_id = DecayIntLiteralToSizedInt(context, node_id, then_value_id); // Start emitting the `else` block. context.inst_block_stack().Push(else_block_id); context.region_stack().AddToRegion(else_block_id, node_id); context.node_stack().Push(node_id, then_value_id); return true; } auto HandleParseNode(Context& context, Parse::IfExprElseId node_id) -> bool { if (!context.scope_stack().IsInFunctionScope()) { return context.TODO(node_id, "Control flow expressions are currently only supported " "inside functions."); } // Alias node_id for if/then/else consistency. auto& else_node = node_id; auto else_value_id = context.node_stack().PopExpr(); auto then_value_id = context.node_stack().Pop(); auto [if_node, _] = context.node_stack().PopWithNodeId(); auto cond_value_id = context.node_stack().PopExpr(); // Convert the `else` value to the `then` value's type, and finish the `else` // block. // TODO: Find a common type, and convert both operands to it instead. auto result_type_id = context.insts().Get(then_value_id).type_id(); else_value_id = ConvertToValueOfType(context, else_node, else_value_id, result_type_id); // Create a resumption block and branches to it. auto chosen_value_id = AddConvergenceBlockWithArgAndPush( context, if_node, {else_value_id, then_value_id}); SetBlockArgResultBeforeConstantUse(context, chosen_value_id, cond_value_id, then_value_id, else_value_id); // Push the result value. context.node_stack().Push(else_node, chosen_value_id); return true; } } // namespace Carbon::Check ================================================ FILE: toolchain/check/handle_if_statement.cpp ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #include "toolchain/check/context.h" #include "toolchain/check/control_flow.h" #include "toolchain/check/convert.h" #include "toolchain/check/handle.h" #include "toolchain/check/inst.h" namespace Carbon::Check { auto HandleParseNode(Context& /*context*/, Parse::IfConditionStartId /*node_id*/) -> bool { return true; } auto HandleParseNode(Context& context, Parse::IfConditionId node_id) -> bool { // Convert the condition to `bool`. auto cond_value_id = context.node_stack().PopExpr(); cond_value_id = ConvertToBoolValue(context, node_id, cond_value_id); // Create the then block and the else block, and branch to the right one. If // there is no `else`, the then block will terminate with a branch to the // else block, which will be reused as the resumption block. auto then_block_id = AddDominatedBlockAndBranchIf(context, node_id, cond_value_id); auto else_block_id = AddDominatedBlockAndBranch(context, node_id); // Start emitting the `then` block. context.inst_block_stack().Pop(); context.inst_block_stack().Push(then_block_id); context.region_stack().AddToRegion(then_block_id, node_id); context.node_stack().Push(node_id, else_block_id); return true; } auto HandleParseNode(Context& context, Parse::IfStatementElseId node_id) -> bool { auto else_block_id = context.node_stack().Pop(); // Switch to emitting the `else` block. context.inst_block_stack().Push(else_block_id); context.region_stack().AddToRegion(else_block_id, node_id); context.node_stack().Push(node_id); return true; } auto HandleParseNode(Context& context, Parse::IfStatementId node_id) -> bool { switch (auto kind = context.node_stack().PeekNodeKind()) { case Parse::NodeKind::IfCondition: { // Branch from then block to else block, and start emitting the else // block. auto else_block_id = context.node_stack().Pop(); AddInst(context, node_id, {.target_id = else_block_id}); context.inst_block_stack().Pop(); context.inst_block_stack().Push(else_block_id); context.region_stack().AddToRegion(else_block_id, node_id); break; } case Parse::NodeKind::IfStatementElse: { // Branch from the then and else blocks to a new resumption block. context.node_stack() .PopAndDiscardSoloNodeId(); AddConvergenceBlockAndPush(context, node_id, /*num_blocks=*/2); break; } default: { CARBON_FATAL("Unexpected parse node at start of `if`: {0}", kind); } } return true; } } // namespace Carbon::Check ================================================ FILE: toolchain/check/handle_impl.cpp ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #include #include #include "toolchain/base/kind_switch.h" #include "toolchain/check/context.h" #include "toolchain/check/convert.h" #include "toolchain/check/decl_name_stack.h" #include "toolchain/check/generic.h" #include "toolchain/check/handle.h" #include "toolchain/check/impl.h" #include "toolchain/check/inst.h" #include "toolchain/check/modifiers.h" #include "toolchain/check/name_lookup.h" #include "toolchain/check/name_scope.h" #include "toolchain/check/pattern_match.h" #include "toolchain/check/type.h" #include "toolchain/check/type_completion.h" #include "toolchain/parse/node_ids.h" #include "toolchain/parse/typed_nodes.h" #include "toolchain/sem_ir/generic.h" #include "toolchain/sem_ir/ids.h" #include "toolchain/sem_ir/specific_interface.h" #include "toolchain/sem_ir/typed_insts.h" namespace Carbon::Check { // Returns the implicit `Self` type for an `impl` when it's in a `class` // declaration. // // TODO: Mixin scopes also have a default `Self` type. static auto GetImplDefaultSelfType(Context& context, const ClassScope& class_scope) -> SemIR::TypeId { return context.classes().Get(class_scope.class_decl.class_id).self_type_id; } auto HandleParseNode(Context& context, Parse::ImplIntroducerId node_id) -> bool { // This might be a generic impl. StartGenericDecl(context); // Create an instruction block to hold the instructions created for the type // and interface. context.inst_block_stack().Push(); // Push the bracketing node. context.node_stack().Push(node_id); // Optional modifiers follow. context.decl_introducer_state_stack().Push(); // An impl doesn't have a name per se, but it makes the processing more // consistent to imagine that it does. This also gives us a scope for implicit // parameters. context.decl_name_stack().PushScopeAndStartName(); return true; } auto HandleParseNode(Context& context, Parse::ForallId /*node_id*/) -> bool { // Push a pattern block for the signature of the `forall`. context.pattern_block_stack().Push(); context.full_pattern_stack().PushParameterizedDecl(); return true; } auto HandleParseNode(Context& context, Parse::ImplTypeAsId node_id) -> bool { auto [self_node, self_id] = context.node_stack().PopExprWithNodeId(); auto self_type = ExprAsType(context, self_node, self_id); const auto& introducer = context.decl_introducer_state_stack().innermost(); if (introducer.modifier_set.HasAnyOf(KeywordModifierSet::Extend)) { // TODO: Also handle the parent scope being a mixin. if (auto class_scope = TryAsClassScope( context, context.decl_name_stack().PeekParentScopeId())) { // If we're not inside a class at all, that will be diagnosed against the // `extend` elsewhere. auto extend_node = introducer.modifier_node_id(ModifierOrder::Extend); CARBON_DIAGNOSTIC(ExtendImplSelfAs, Error, "cannot `extend` an `impl` with an explicit self type"); auto diag = context.emitter().Build(extend_node, ExtendImplSelfAs); if (self_type.type_id == GetImplDefaultSelfType(context, *class_scope)) { // If the explicit self type is the default, suggest removing it with a // diagnostic, but continue as if no error occurred since the self-type // is semantically valid. CARBON_DIAGNOSTIC(ExtendImplSelfAsDefault, Note, "remove the explicit `Self` type here"); diag.Note(self_node, ExtendImplSelfAsDefault); if (self_type.type_id != SemIR::ErrorInst::TypeId) { diag.Emit(); } } else if (self_type.type_id != SemIR::ErrorInst::TypeId) { // Otherwise, the self-type is an error. diag.Emit(); self_type.inst_id = SemIR::ErrorInst::TypeInstId; } } } // Introduce `Self`. Note that we add this name lexically rather than adding // to the `NameScopeId` of the `impl`, because this happens before we enter // the `impl` scope or even identify which `impl` we're declaring. // TODO: Revisit this once #3714 is resolved. AddNameToLookup(context, SemIR::NameId::SelfType, self_type.inst_id); context.node_stack().Push(node_id, self_type.inst_id); return true; } auto HandleParseNode(Context& context, Parse::ImplDefaultSelfAsId node_id) -> bool { auto self_inst_id = SemIR::TypeInstId::None; if (auto class_scope = TryAsClassScope( context, context.decl_name_stack().PeekParentScopeId())) { auto self_type_id = GetImplDefaultSelfType(context, *class_scope); // Build the implicit access to the enclosing `Self`. // TODO: Consider calling `HandleNameAsExpr` to build this implicit `Self` // expression. We've already done the work to check that the enclosing // context is a class and found its `Self`, so additionally performing an // unqualified name lookup would be redundant work, but would avoid // duplicating the handling of the `Self` expression. self_inst_id = AddTypeInst( context, node_id, SemIR::NameRef{ .type_id = SemIR::TypeType::TypeId, .name_id = SemIR::NameId::SelfType, .value_id = context.types().GetTypeInstId(self_type_id)}); } else { CARBON_DIAGNOSTIC(ImplAsOutsideClass, Error, "`impl as` can only be used in a class"); context.emitter().Emit(node_id, ImplAsOutsideClass); self_inst_id = SemIR::ErrorInst::TypeInstId; } // There's no need to push `Self` into scope here, because we can find it in // the parent class scope. context.node_stack().Push(node_id, self_inst_id); return true; } // Pops the parameters of an `impl`, forming a `NameComponent` with no // associated name that describes them. static auto PopImplIntroducerAndParamsAsNameComponent( Context& context, Parse::AnyImplDeclId end_of_decl_node_id) -> NameComponent { auto [implicit_params_loc_id, implicit_param_patterns_id] = context.node_stack() .PopWithNodeIdIf(); if (implicit_param_patterns_id) { context.node_stack() .PopAndDiscardSoloNodeId(); // Emit the `forall` match. This shouldn't produce any valid `Call` params, // because `impl`s are never actually called at runtime. auto match_results = CalleePatternMatch(context, *implicit_param_patterns_id, SemIR::InstBlockId::None, SemIR::InstBlockId::None); CARBON_CHECK(match_results.call_params_id == SemIR::InstBlockId::Empty); CARBON_CHECK(match_results.call_param_patterns_id == SemIR::InstBlockId::Empty); } Parse::NodeId first_param_node_id = context.node_stack().PopForSoloNodeId(); // Subtracting 1 since we don't want to include the final `{` or `;` of the // declaration when performing syntactic match. Parse::Tree::PostorderIterator last_param_iter(end_of_decl_node_id); --last_param_iter; auto pattern_block_id = SemIR::InstBlockId::None; if (implicit_param_patterns_id) { pattern_block_id = context.pattern_block_stack().Pop(); context.full_pattern_stack().PopFullPattern(); } return {.name_loc_id = Parse::NodeId::None, .name_id = SemIR::NameId::None, .first_param_node_id = first_param_node_id, .last_param_node_id = *last_param_iter, .implicit_params_loc_id = implicit_params_loc_id, .implicit_param_patterns_id = implicit_param_patterns_id.value_or(SemIR::InstBlockId::None), .params_loc_id = Parse::NodeId::None, .param_patterns_id = SemIR::InstBlockId::None, .call_param_patterns_id = SemIR::InstBlockId::None, .call_params_id = SemIR::InstBlockId::None, .param_ranges = SemIR::Function::CallParamIndexRanges::Empty, .pattern_block_id = pattern_block_id}; } // Build an ImplDecl describing the signature of an impl. This handles the // common logic shared by impl forward declarations and impl definitions. static auto BuildImplDecl(Context& context, Parse::AnyImplDeclId node_id) -> std::pair { auto [constraint_node, constraint_id] = context.node_stack().PopExprWithNodeId(); auto [self_type_node, self_type_inst_id] = context.node_stack().PopWithNodeId(); // Pop the `impl` introducer and any `forall` parameters as a "name". auto name = PopImplIntroducerAndParamsAsNameComponent(context, node_id); auto decl_block_id = context.inst_block_stack().Pop(); // Convert the constraint expression to a type. auto [constraint_type_inst_id, constraint_type_id] = ExprAsType(context, constraint_node, constraint_id); // Process modifiers. // TODO: Should we somehow permit access specifiers on `impl`s? auto introducer = context.decl_introducer_state_stack().Pop(); LimitModifiersOnDecl(context, introducer, KeywordModifierSet::ImplDecl); bool is_final = introducer.modifier_set.HasAnyOf(KeywordModifierSet::Final); // Finish processing the name, which should be empty, but might have // parameters. auto name_context = context.decl_name_stack().FinishImplName(); CARBON_CHECK(name_context.state == DeclNameStack::NameContext::State::Empty); // TODO: Check for an orphan `impl`. // Add the impl declaration. auto impl_decl_id = AddPlaceholderInst(context, node_id, SemIR::ImplDecl{.impl_id = SemIR::ImplId::None, .decl_block_id = decl_block_id}); // This requires that the facet type is identified. It returns None if an // error was diagnosed. auto specific_interface = CheckConstraintIsInterface( context, impl_decl_id, self_type_inst_id, constraint_type_inst_id); auto impl_id = SemIR::ImplId::None; { SemIR::Impl impl = {name_context.MakeEntityWithParamsBase( name, impl_decl_id, /*is_extern=*/false, SemIR::LibraryNameId::None), {.self_id = self_type_inst_id, .constraint_id = constraint_type_inst_id, .interface = specific_interface, .is_final = is_final}}; // There's a bunch of places that may represent a diagnostic that occurred // in checking the impl up to this point, which we consolidate into this // bool. Due to lack of an instruction to set to `ErrorInst`, an // `InterfaceId::None` indicates that the interface could not be identified // and an error was diagnosed. bool impl_had_error = context.types().GetTypeIdForTypeInstId(impl.self_id) == SemIR::ErrorInst::TypeId || context.types().GetTypeIdForTypeInstId(impl.constraint_id) == SemIR::ErrorInst::TypeId || !impl.interface.interface_id.has_value(); CARBON_KIND_SWITCH(FindImplId(context, impl)) { case CARBON_KIND(RedeclaredImpl redeclared_impl): { // This is a redeclaration of another impl, now held in `impl_id`. impl_id = redeclared_impl.prev_impl_id; // Note that we don't reconstruct the witness for a redeclaration, which // was the instruction that came last in the first declaration's eval // block. And FinishGenericRedecl allows the redecl to have fewer // instructions to support this case. const auto& prev_impl = context.impls().Get(impl_id); FinishGenericRedecl(context, prev_impl.generic_id); break; } case CARBON_KIND(NewImpl new_impl): { // This is a new declaration (possibly with an attached definition). // Create a new `impl_id`, filling the missing generic and witness in // `Impl` structure. impl_had_error |= new_impl.find_had_error; impl.generic_id = BuildGeneric(context, impl_decl_id); if (impl_had_error) { // If there's any error in the construction of the impl, then the // witness can't be constructed. We set it to `ErrorInst` to make the // impl unusable for impl lookup. impl.witness_id = SemIR::ErrorInst::InstId; } else { context.inst_block_stack().Push(); // This makes either a placeholder witness table or a full witness // table. The full witness table is deferred to the impl definition // unless the declaration uses rewrite constraints to set values of // associated constants in the interface. // // The witness instruction contains the SelfSpecific that is // constructed by BuildGeneric(), but the witness and its rewrites // also must be part of the generic eval block by coming before // FinishGenericDecl(). impl.witness_id = AddImplWitnessForDeclaration( context, node_id, impl, context.generics().GetSelfSpecific(impl.generic_id)); impl.witness_block_id = context.inst_block_stack().Pop(); } FinishGenericDecl(context, node_id, impl.generic_id); auto extend_node = introducer.modifier_node_id(ModifierOrder::Extend); impl_id = AddImpl(context, impl, new_impl.lookup_bucket, extend_node, name.implicit_params_loc_id); } } } // `FindImplId` returned an existing ImplId, or we added a new id with // `AddImpl` above. Write that ImplId into the ImplDecl instruction and finish // it. auto impl_decl = context.insts().GetAs(impl_decl_id); impl_decl.impl_id = impl_id; ReplaceInstBeforeConstantUse(context, impl_decl_id, impl_decl); return {impl_id, impl_decl_id}; } auto HandleParseNode(Context& context, Parse::ImplDeclId node_id) -> bool { auto [impl_id, impl_decl_id] = BuildImplDecl(context, node_id); auto& impl = context.impls().Get(impl_id); context.decl_name_stack().PopScope(); // Impl definitions are required in the same file as the declaration. We skip // this requirement if we've already issued an invalid redeclaration error, or // there is an error that would prevent the impl from being legal to define. if (impl.witness_id != SemIR::ErrorInst::InstId) { context.definitions_required_by_decl().push_back(impl_decl_id); } return true; } auto HandleParseNode(Context& context, Parse::ImplDefinitionStartId node_id) -> bool { auto [impl_id, impl_decl_id] = BuildImplDecl(context, node_id); auto& impl = context.impls().Get(impl_id); CheckRequireDeclsSatisfied(context, node_id, impl); CARBON_CHECK(!impl.has_definition_started()); impl.definition_id = impl_decl_id; impl.scope_id = context.name_scopes().Add(impl_decl_id, SemIR::NameId::None, context.decl_name_stack().PeekParentScopeId()); context.scope_stack().PushForEntity( impl_decl_id, impl.scope_id, context.generics().GetSelfSpecific(impl.generic_id)); StartGenericDefinition(context, impl.generic_id); ImplWitnessStartDefinition(context, impl); context.inst_block_stack().Push(); context.node_stack().Push(node_id, impl_id); // TODO: Handle the case where there's control flow in the impl body. For // example: // // impl C as I { // fn F() -> if true then i32 else f64; // } // // We may need to track a list of instruction blocks here, as we do for a // function. impl.body_block_id = context.inst_block_stack().PeekOrAdd(); return true; } auto HandleParseNode(Context& context, Parse::ImplDefinitionId /*node_id*/) -> bool { auto impl_id = context.node_stack().Pop(); auto& impl = context.impls().Get(impl_id); FinishImplWitness(context, impl); impl.defined = true; FinishGenericDefinition(context, impl.generic_id); context.inst_block_stack().Pop(); // The decl_name_stack and scopes are popped by `ProcessNodeIds`. return true; } } // namespace Carbon::Check ================================================ FILE: toolchain/check/handle_import_and_package.cpp ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #include "toolchain/check/context.h" #include "toolchain/check/decl_introducer_state.h" #include "toolchain/check/handle.h" #include "toolchain/check/modifiers.h" namespace Carbon::Check { // `import` and `package` are structured by parsing. As a consequence, no // checking logic is needed here. auto HandleParseNode(Context& context, Parse::ImportIntroducerId /*node_id*/) -> bool { context.decl_introducer_state_stack().Push(); return true; } auto HandleParseNode(Context& context, Parse::ImportDeclId /*node_id*/) -> bool { context.node_stack().PopIf(); auto introducer = context.decl_introducer_state_stack().Pop(); LimitModifiersOnDecl(context, introducer, KeywordModifierSet::Export); return true; } auto HandleParseNode(Context& context, Parse::LibraryIntroducerId /*node_id*/) -> bool { context.decl_introducer_state_stack().Push(); return true; } auto HandleParseNode(Context& context, Parse::LibraryDeclId /*node_id*/) -> bool { context.node_stack().PopIf(); auto introducer = context.decl_introducer_state_stack().Pop(); LimitModifiersOnDecl(context, introducer, KeywordModifierSet::Impl); return true; } auto HandleParseNode(Context& context, Parse::PackageIntroducerId /*node_id*/) -> bool { context.decl_introducer_state_stack().Push(); return true; } auto HandleParseNode(Context& context, Parse::PackageDeclId /*node_id*/) -> bool { context.node_stack().PopIf(); auto introducer = context.decl_introducer_state_stack().Pop(); LimitModifiersOnDecl(context, introducer, KeywordModifierSet::Impl); return true; } auto HandleParseNode(Context& context, Parse::LibrarySpecifierId /*node_id*/) -> bool { CARBON_CHECK(context.node_stack().PeekIs()); return true; } auto HandleParseNode(Context& /*context*/, Parse::IdentifierPackageNameId /*node_id*/) -> bool { return true; } auto HandleParseNode(Context& /*context*/, Parse::CorePackageNameId /*node_id*/) -> bool { return true; } auto HandleParseNode(Context& /*context*/, Parse::CppPackageNameId /*node_id*/) -> bool { return true; } auto HandleParseNode(Context& context, Parse::LibraryNameId node_id) -> bool { // This is discarded in this file's uses, but is used by modifiers for `extern // library`. auto literal_id = context.tokens().GetStringLiteralValue( context.parse_tree().node_token(node_id)); context.node_stack().Push( node_id, SemIR::LibraryNameId::ForStringLiteralValueId(literal_id)); return true; } auto HandleParseNode(Context& context, Parse::DefaultLibraryId node_id) -> bool { context.node_stack().Push(node_id, SemIR::LibraryNameId::Default); return true; } auto HandleParseNode(Context& /*context*/, Parse::InlineImportSpecifierId /*node_id*/) -> bool { return true; } auto HandleParseNode(Context& /*context*/, Parse::InlineImportBodyId /*node_id*/) -> bool { return true; } } // namespace Carbon::Check ================================================ FILE: toolchain/check/handle_index.cpp ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #include #include "toolchain/base/kind_switch.h" #include "toolchain/check/context.h" #include "toolchain/check/convert.h" #include "toolchain/check/handle.h" #include "toolchain/check/inst.h" #include "toolchain/check/literal.h" #include "toolchain/check/name_lookup.h" #include "toolchain/check/operator.h" #include "toolchain/check/type.h" #include "toolchain/diagnostics/diagnostic.h" #include "toolchain/sem_ir/expr_info.h" #include "toolchain/sem_ir/inst.h" #include "toolchain/sem_ir/typed_insts.h" namespace Carbon::Check { auto HandleParseNode(Context& /*context*/, Parse::IndexExprStartId /*node_id*/) -> bool { // Leave the expression on the stack for IndexExpr. return true; } // Performs an index with base expression `operand_inst_id` and // `operand_type_id` for types that are not an array. This checks if // the base expression implements the `IndexWith` interface; if so, uses the // `At` associative method, otherwise prints a diagnostic. static auto PerformIndexWith(Context& context, Parse::NodeId node_id, SemIR::InstId operand_inst_id, SemIR::InstId index_inst_id) -> SemIR::InstId { SemIR::InstId args[] = {context.types().GetTypeInstId( context.insts().Get(index_inst_id).type_id())}; Operator op{.interface_name = CoreIdentifier::IndexWith, .interface_args_ref = args, .op_name = CoreIdentifier::At}; return BuildBinaryOperator(context, node_id, op, operand_inst_id, index_inst_id); } auto HandleParseNode(Context& context, Parse::IndexExprId node_id) -> bool { auto index_inst_id = context.node_stack().PopExpr(); auto operand_inst_id = context.node_stack().PopExpr(); operand_inst_id = ConvertToValueOrRefExpr(context, operand_inst_id); auto operand_inst = context.insts().Get(operand_inst_id); auto operand_type_id = operand_inst.type_id(); CARBON_KIND_SWITCH(context.types().GetAsInst(operand_type_id)) { case CARBON_KIND(SemIR::ArrayType array_type): { auto cast_index_id = ConvertToValueOfType( context, SemIR::LocId(index_inst_id), index_inst_id, // TODO: Replace this with impl lookup rather than hardcoding `i32`. MakeIntType(context, node_id, SemIR::IntKind::Signed, context.ints().Add(32))); auto array_cat = SemIR::GetExprCategory(context.sem_ir(), operand_inst_id); if (array_cat == SemIR::ExprCategory::Value) { // If the operand is an array value, convert it to an ephemeral // reference to an array so we can perform a primitive indexing into it. operand_inst_id = AddInst( context, node_id, {.type_id = operand_type_id, .value_id = operand_inst_id}); } // Constant evaluation will perform a bounds check on this array indexing // if the index is constant. auto elem_id = AddInst( context, node_id, {.type_id = context.types().GetTypeIdForTypeInstId( array_type.element_type_inst_id), .array_id = operand_inst_id, .index_id = cast_index_id}); if (array_cat != SemIR::ExprCategory::DurableRef) { // Indexing a durable reference gives a durable reference expression. // Indexing anything else gives a value expression. // TODO: This should be replaced by a choice between using `IndexWith` // and `IndirectIndexWith`. elem_id = ConvertToValueExpr(context, elem_id); } context.node_stack().Push(node_id, elem_id); return true; } default: { auto elem_id = PerformIndexWith(context, node_id, operand_inst_id, index_inst_id); context.node_stack().Push(node_id, elem_id); return true; } } } } // namespace Carbon::Check ================================================ FILE: toolchain/check/handle_interface.cpp ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #include #include "toolchain/check/context.h" #include "toolchain/check/eval.h" #include "toolchain/check/facet_type.h" #include "toolchain/check/generic.h" #include "toolchain/check/handle.h" #include "toolchain/check/inst.h" #include "toolchain/check/interface.h" #include "toolchain/check/merge.h" #include "toolchain/check/modifiers.h" #include "toolchain/check/name_component.h" #include "toolchain/check/name_lookup.h" #include "toolchain/check/type.h" #include "toolchain/sem_ir/entity_with_params_base.h" #include "toolchain/sem_ir/ids.h" #include "toolchain/sem_ir/interface.h" #include "toolchain/sem_ir/typed_insts.h" namespace Carbon::Check { auto HandleParseNode(Context& context, Parse::InterfaceIntroducerId node_id) -> bool { // This interface is potentially generic. StartGenericDecl(context); // Create an instruction block to hold the instructions created as part of the // interface signature, such as generic parameters. context.inst_block_stack().Push(); // Optional modifiers and the name follow. context.decl_introducer_state_stack().Push(); context.decl_name_stack().PushScopeAndStartName(); // Push the bracketing node. context.node_stack().Push(node_id); return true; } static auto BuildInterfaceDecl(Context& context, Parse::AnyInterfaceDeclId node_id, bool is_definition) -> std::tuple { auto name = PopNameComponent(context); auto name_context = context.decl_name_stack().FinishName(name); context.node_stack() .PopAndDiscardSoloNodeId(); // Process modifiers. auto [_, parent_scope_inst] = context.name_scopes().GetInstIfValid(name_context.parent_scope_id); auto introducer = context.decl_introducer_state_stack().Pop(); CheckAccessModifiersOnDecl(context, introducer, parent_scope_inst); LimitModifiersOnDecl(context, introducer, KeywordModifierSet::Access); auto decl_block_id = context.inst_block_stack().Pop(); // Add the interface declaration. auto interface_decl = SemIR::InterfaceDecl{ SemIR::TypeType::TypeId, SemIR::InterfaceId::None, decl_block_id}; auto decl_inst_id = AddPlaceholderInst(context, node_id, interface_decl); SemIR::Interface interface_info = {name_context.MakeEntityWithParamsBase( name, decl_inst_id, /*is_extern=*/false, SemIR::LibraryNameId::None)}; DiagnoseIfGenericMissingExplicitParameters(context, interface_info); // Check whether this is a redeclaration. SemIR::ScopeLookupResult lookup_result = context.decl_name_stack().LookupOrAddName( name_context, decl_inst_id, introducer.modifier_set.GetAccessKind()); if (auto existing_decl = TryGetExistingDecl(context, name, lookup_result, interface_info, is_definition)) { auto existing_interface_decl = existing_decl->As(); interface_decl.interface_id = existing_interface_decl.interface_id; interface_decl.type_id = existing_interface_decl.type_id; // TODO: If the new declaration is a definition, keep its parameter // and implicit parameter lists rather than the ones from the // previous declaration. auto prev_decl_generic_id = context.interfaces().Get(interface_decl.interface_id).generic_id; FinishGenericRedecl(context, prev_decl_generic_id); } else { // Create a new interface if this isn't a valid redeclaration. interface_info.generic_id = BuildGenericDecl(context, decl_inst_id); interface_decl.interface_id = context.interfaces().Add(interface_info); if (interface_info.has_parameters()) { interface_decl.type_id = GetGenericInterfaceType(context, interface_decl.interface_id, context.scope_stack().PeekSpecificId()); } } // Write the interface ID into the InterfaceDecl. ReplaceInstBeforeConstantUse(context, decl_inst_id, interface_decl); return {interface_decl.interface_id, decl_inst_id}; } auto HandleParseNode(Context& context, Parse::InterfaceDeclId node_id) -> bool { BuildInterfaceDecl(context, node_id, /*is_definition=*/false); context.decl_name_stack().PopScope(); return true; } auto HandleParseNode(Context& context, Parse::InterfaceDefinitionStartId node_id) -> bool { auto [interface_id, decl_inst_id] = BuildInterfaceDecl(context, node_id, /*is_definition=*/true); auto& interface_info = context.interfaces().Get(interface_id); // Track that this declaration is the definition. CARBON_CHECK(!interface_info.has_definition_started(), "Can't merge with defined interfaces."); interface_info.definition_id = decl_inst_id; interface_info.scope_without_self_id = context.name_scopes().Add( decl_inst_id, SemIR::NameId::None, interface_info.parent_scope_id); // Start the definition of interface-without-self. StartGenericDefinition(context, interface_info.generic_id); context.inst_block_stack().Push(); // Enter the interface-without-self scope, which is used for the Self // instruction, since it needs to reference the interface (without-self) // generic. Self can't reference the interface-with-self generic since it's a // parameter to the generic. context.scope_stack().PushForEntity( decl_inst_id, interface_info.scope_without_self_id, context.generics().GetSelfSpecific(interface_info.generic_id)); // Declare and introduce `Self`. We model `Self` as a symbolic binding whose // type is the interface, excluding any other interfaces mentioned by // `require` declarations. // // This is an instruction in the interface-without-self so that we can apply a // SpecificInterface to it, and get the inner `Self` as modified by any // enclosing specific. SemIR::TypeId self_type_id = GetInterfaceType( context, interface_id, context.generics().GetSelfSpecific(interface_info.generic_id)); interface_info.self_param_id = AddSelfSymbolicBindingToScope( context, node_id, self_type_id, interface_info.scope_without_self_id, /*is_template=*/false); // Start the declaration of interface-with-self. StartGenericDecl(context); // Push `Self` as a parameter of the interface-with-self. context.scope_stack().PushCompileTimeBinding(interface_info.self_param_id); // Add the interface-with-self declaration and build the generic for it. This // captures the `interface_info.self_param_id` as a parameter of the generic. auto interface_with_self_decl = SemIR::InterfaceWithSelfDecl{.interface_id = interface_id}; auto decl_with_self_inst_id = AddPlaceholderInst(context, node_id, interface_with_self_decl); auto generic_with_self_id = BuildGenericDecl(context, decl_with_self_inst_id); interface_info.generic_with_self_id = generic_with_self_id; ReplaceInstBeforeConstantUse(context, decl_with_self_inst_id, interface_with_self_decl); interface_info.scope_with_self_id = context.name_scopes().Add(decl_with_self_inst_id, SemIR::NameId::None, interface_info.scope_without_self_id); // Set on the name scope that `M` is replaced by `Self.M`. context.name_scopes() .Get(interface_info.scope_with_self_id) .set_is_interface_definition(); // Start the definition of interface-with-self. StartGenericDefinition(context, interface_info.generic_with_self_id); // Enter a scope for the interace-with-self. context.scope_stack().PushForEntity( decl_with_self_inst_id, interface_info.scope_with_self_id, context.generics().GetSelfSpecific(interface_info.generic_with_self_id)); interface_info.body_block_without_self_id = context.inst_block_stack().PeekOrAdd(); context.inst_block_stack().Push(); context.require_impls_stack().PushArray(); // We use the arg stack to build the witness table type. context.args_type_info_stack().Push(); // TODO: Handle the case where there's control flow in the interface body. For // example: // // interface C { // let v: if true then i32 else f64; // } // // We may need to track a list of instruction blocks here, as we do for a // function. interface_info.body_block_with_self_id = context.inst_block_stack().PeekOrAdd(); context.node_stack().Push(node_id, interface_id); return true; } auto HandleParseNode(Context& context, Parse::InterfaceDefinitionId /*node_id*/) -> bool { auto interface_id = context.node_stack().Pop(); // Pop the body_block_with_self. context.inst_block_stack().Pop(); auto associated_entities_id = context.args_type_info_stack().Pop(); auto require_impls_block_id = context.require_impls_blocks().Add( context.require_impls_stack().PeekArray()); context.require_impls_stack().PopArray(); auto& interface_info = context.interfaces().Get(interface_id); if (!interface_info.associated_entities_id.has_value()) { interface_info.require_impls_block_id = require_impls_block_id; // This marks the interface type as fully defined. interface_info.associated_entities_id = associated_entities_id; } // Finish the definition of interface-with-self. FinishGenericDefinition(context, interface_info.generic_with_self_id); // Pop the body_block_without_self. context.inst_block_stack().Pop(); // Finish the definition of interface-without-self. FinishGenericDefinition(context, interface_info.generic_id); // The decl_name_stack and scopes are popped by `ProcessNodeIds`. return true; } } // namespace Carbon::Check ================================================ FILE: toolchain/check/handle_lambda.cpp ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #include "toolchain/check/context.h" #include "toolchain/check/handle.h" namespace Carbon::Check { auto HandleParseNode(Context& context, Parse::LambdaIntroducerId node_id) -> bool { return context.TODO(node_id, "HandleLambdaIntroducer"); } auto HandleParseNode(Context& context, Parse::LambdaId node_id) -> bool { return context.TODO(node_id, "HandleLambda"); } auto HandleParseNode(Context& context, Parse::TerseBodyArrowId node_id) -> bool { return context.TODO(node_id, "HandleTerseBodyArrow"); } } // namespace Carbon::Check ================================================ FILE: toolchain/check/handle_let_and_var.cpp ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #include #include "toolchain/check/call.h" #include "toolchain/check/context.h" #include "toolchain/check/convert.h" #include "toolchain/check/core_identifier.h" #include "toolchain/check/decl_introducer_state.h" #include "toolchain/check/handle.h" #include "toolchain/check/inst.h" #include "toolchain/check/interface.h" #include "toolchain/check/keyword_modifier_set.h" #include "toolchain/check/member_access.h" #include "toolchain/check/modifiers.h" #include "toolchain/check/name_lookup.h" #include "toolchain/check/pattern.h" #include "toolchain/check/pattern_match.h" #include "toolchain/diagnostics/emitter.h" #include "toolchain/diagnostics/format_providers.h" #include "toolchain/lex/token_kind.h" #include "toolchain/parse/node_ids.h" #include "toolchain/parse/node_kind.h" #include "toolchain/parse/typed_nodes.h" #include "toolchain/sem_ir/ids.h" #include "toolchain/sem_ir/inst.h" #include "toolchain/sem_ir/name_scope.h" #include "toolchain/sem_ir/pattern.h" #include "toolchain/sem_ir/type.h" #include "toolchain/sem_ir/typed_insts.h" namespace Carbon::Check { // Handles the end of the declaration region of an associated constant. This is // called at the `=` or the `;` of the declaration, whichever comes first. static auto EndAssociatedConstantDeclRegion(Context& context, SemIR::InterfaceId interface_id) -> void { // Peek the pattern. For a valid associated constant, the corresponding // instruction will be an `AssociatedConstantDecl` instruction. auto decl_id = context.node_stack().PeekPattern(); auto assoc_const_decl = context.insts().GetAs(decl_id); auto& assoc_const = context.associated_constants().Get(assoc_const_decl.assoc_const_id); // Build a corresponding associated entity and add it into scope. // // TODO: The instruction is added to the associated constant's decl block. // It probably should be in the interface-with-self body instead. auto assoc_id = BuildAssociatedEntity(context, interface_id, decl_id); auto name_context = context.decl_name_stack().MakeUnqualifiedName( context.node_stack().PeekNodeId(), assoc_const.name_id); auto access_kind = context.decl_introducer_state_stack() .innermost() .modifier_set.GetAccessKind(); context.decl_name_stack().AddNameOrDiagnose(name_context, assoc_id, access_kind); } template static auto HandleIntroducer(Context& context, Parse::NodeId node_id) -> bool { context.decl_introducer_state_stack().Push(); // Push a bracketing node and pattern block to establish the pattern context. context.node_stack().Push(node_id); context.pattern_block_stack().Push(); context.full_pattern_stack().PushNameBindingDecl(); BeginSubpattern(context); return true; } auto HandleParseNode(Context& context, Parse::LetIntroducerId node_id) -> bool { return HandleIntroducer(context, node_id); } auto HandleParseNode(Context& context, Parse::AssociatedConstantIntroducerId node_id) -> bool { // Collect the declarations nested in the associated constant in a decl // block. This is popped by FinishAssociatedConstantDecl. context.inst_block_stack().Push(); return HandleIntroducer(context, node_id); } auto HandleParseNode(Context& context, Parse::VariableIntroducerId node_id) -> bool { return HandleIntroducer(context, node_id); } auto HandleParseNode(Context& context, Parse::FieldIntroducerId node_id) -> bool { context.decl_introducer_state_stack().Push(); context.node_stack().Push(node_id); return true; } auto HandleParseNode(Context& context, Parse::VariablePatternId node_id) -> bool { auto subpattern_id = context.node_stack().PopPattern(); auto type_id = context.insts().Get(subpattern_id).type_id(); if (subpattern_id == SemIR::ErrorInst::InstId) { context.node_stack().Push(node_id, SemIR::ErrorInst::InstId); return true; } // In a parameter list, a `var` pattern is always a single `Call` parameter, // even if it contains multiple binding patterns. switch (context.full_pattern_stack().CurrentKind()) { case FullPatternStack::Kind::ExplicitParamList: case FullPatternStack::Kind::ImplicitParamList: subpattern_id = AddPatternInst( context, node_id, {.type_id = type_id, .subpattern_id = subpattern_id}); break; case FullPatternStack::Kind::NameBindingDecl: break; case FullPatternStack::Kind::NotInEitherParamList: CARBON_FATAL("Unreachable"); } auto pattern_id = AddPatternInst( context, node_id, {.type_id = type_id, .subpattern_id = subpattern_id}); context.node_stack().Push(node_id, pattern_id); return true; } // Handle the end of the full-pattern of a let/var declaration (before the // start of the initializer, if any). static auto EndFullPattern(Context& context) -> void { auto scope_id = context.scope_stack().PeekNameScopeId(); if (scope_id.has_value() && context.name_scopes().Get(scope_id).is_interface_definition()) { // Don't emit NameBindingDecl for an associated constant, because it will // always be empty. context.pattern_block_stack().PopAndDiscard(); return; } auto pattern_block_id = context.pattern_block_stack().Pop(); AddInst(context, context.node_stack().PeekNodeId(), {.pattern_block_id = pattern_block_id}); // Emit storage for any `var`s in the pattern now. bool returned = context.decl_introducer_state_stack().innermost().modifier_set.HasAnyOf( KeywordModifierSet::Returned); AddPatternVarStorage(context, pattern_block_id, returned); } static auto StartPatternInitializer(Context& context) -> bool { if (context.scope_stack().PeekIndex() == ScopeIndex::Package) { context.global_init().Resume(); } context.full_pattern_stack().StartPatternInitializer(); return true; } static auto EndPatternInitializer(Context& context) -> void { if (context.scope_stack().PeekIndex() == ScopeIndex::Package) { context.global_init().Suspend(); } context.full_pattern_stack().EndPatternInitializer(); } static auto HandleInitializer(Context& context, Parse::NodeId node_id) -> bool { EndFullPattern(context); context.node_stack().Push(node_id); StartPatternInitializer(context); return true; } auto HandleParseNode(Context& context, Parse::LetInitializerId node_id) -> bool { return HandleInitializer(context, node_id); } auto HandleParseNode(Context& context, Parse::AssociatedConstantInitializerId node_id) -> bool { auto interface_decl = context.scope_stack().GetCurrentScopeAs(); EndAssociatedConstantDeclRegion(context, interface_decl.interface_id); return HandleInitializer(context, node_id); } auto HandleParseNode(Context& context, Parse::VariableInitializerId node_id) -> bool { return HandleInitializer(context, node_id); } auto HandleParseNode(Context& context, Parse::FieldInitializerId node_id) -> bool { context.node_stack().Push(node_id); return true; } // Make a default initialization expression for a `var` declaration. static auto MakeDefaultInit(Context& context, SemIR::LocId loc_id, SemIR::InstId pattern_id) -> SemIR::InstId { loc_id = context.insts().GetLocIdForDesugaring(loc_id); // Extract the matched type from the pattern. // // TODO: Diagnose if the pattern doesn't have a type, for example `var 123;` // or `var a: auto;`. auto pattern_type_id = context.insts().Get(pattern_id).type_id(); auto type_inst_id = context.types().GetTypeInstId( SemIR::ExtractScrutineeType(context.sem_ir(), pattern_type_id)); if (type_inst_id == SemIR::ErrorInst::InstId) { return SemIR::ErrorInst::InstId; } // Form `Type as Core.DefaultOrUnformed`. auto interface_id = LookupNameInCore(context, loc_id, CoreIdentifier::DefaultOrUnformed); auto interface_type = ExprAsType(context, loc_id, interface_id); auto facet_id = ConvertToValueOfType(context, loc_id, type_inst_id, interface_type.type_id); // Form a call to `facet.Op()`. auto op_name_id = context.core_identifiers().AddNameId(CoreIdentifier::Op); auto op_id = PerformMemberAccess(context, loc_id, facet_id, op_name_id); return PerformCall(context, loc_id, op_id, {}); } namespace { // State from HandleDecl, returned for type-specific handling. struct DeclInfo { // The optional initializer. SemIR::InstId init_id = SemIR::InstId::None; // The pattern. For an associated constant, this is the associated constant // declaration. SemIR::InstId pattern_id = SemIR::InstId::None; DeclIntroducerState introducer = DeclIntroducerState(); }; } // namespace // Handles common logic for `let` and `var` declarations. // TODO: There's still a lot of divergence here, including logic in // handle_binding_pattern. These should really be better unified. template static auto HandleDecl(Context& context, Parse::NodeId node_id) -> DeclInfo { DeclInfo decl_info = DeclInfo(); // Handle the optional initializer. if (context.node_stack().PeekNextIs(InitializerNodeKind)) { decl_info.init_id = context.node_stack().PopExpr(); context.node_stack().PopAndDiscardSoloNodeId(); EndPatternInitializer(context); } else { // For an associated constant declaration, handle the completed declaration // now. We will have done this at the `=` if there was an initializer. if constexpr (IntroducerNodeKind == Parse::NodeKind::AssociatedConstantIntroducer) { auto interface_decl = context.scope_stack() .GetCurrentScopeAs(); EndAssociatedConstantDeclRegion(context, interface_decl.interface_id); } EndFullPattern(context); // A variable declaration without an explicit initializer is initialized by // calling `(T as Core.DefaultOrUnformed).Op()`. if constexpr (IntroducerNodeKind == Parse::NodeKind::VariableIntroducer) { StartPatternInitializer(context); decl_info.init_id = MakeDefaultInit(context, node_id, context.node_stack().PeekPattern()); EndPatternInitializer(context); } } context.full_pattern_stack().PopFullPattern(); decl_info.pattern_id = context.node_stack().PopPattern(); context.node_stack().PopAndDiscardSoloNodeId(); // Process declaration modifiers. // TODO: For a qualified `let` or `var` declaration, this should use the // target scope of the name introduced in the declaration. See #2590. auto parent_scope_inst = context.name_scopes() .GetInstIfValid(context.scope_stack().PeekNameScopeId()) .second; decl_info.introducer = context.decl_introducer_state_stack().Pop(); CheckAccessModifiersOnDecl(context, decl_info.introducer, parent_scope_inst); return decl_info; } auto HandleParseNode(Context& context, Parse::LetDeclId node_id) -> bool { auto decl_info = HandleDecl(context, node_id); LimitModifiersOnDecl( context, decl_info.introducer, KeywordModifierSet::Access | KeywordModifierSet::Interface); // Diagnose interface modifiers given that we're not building an associated // constant. We use this rather than `LimitModifiersOnDecl` to get a more // specific error. RequireDefaultFinalOnlyInInterfaces(context, decl_info.introducer, SemIR::NameScopeId::None); if (decl_info.init_id.has_value()) { LocalPatternMatch(context, decl_info.pattern_id, decl_info.init_id); } else { CARBON_DIAGNOSTIC( ExpectedInitializerAfterLet, Error, "expected `=`; `let` declaration must have an initializer"); context.emitter().Emit(LocIdForDiagnostics::TokenOnly(node_id), ExpectedInitializerAfterLet); } return true; } auto HandleParseNode(Context& context, Parse::AssociatedConstantDeclId node_id) -> bool { auto decl_info = HandleDecl( context, node_id); LimitModifiersOnDecl( context, decl_info.introducer, KeywordModifierSet::Access | KeywordModifierSet::Interface); auto interface_scope = context.scope_stack().GetCurrentScopeAs(); // The `AssociatedConstantDecl` instruction and the corresponding // `AssociatedConstant` entity are built as part of handling the binding // pattern, but we still need to attach the default value, if any is // specified. if (decl_info.pattern_id == SemIR::ErrorInst::InstId) { const auto& interface = context.interfaces().Get(interface_scope.interface_id); context.name_scopes().Get(interface.scope_with_self_id).set_has_error(); context.inst_block_stack().Pop(); return true; } auto decl = context.insts().GetAs( decl_info.pattern_id); if (decl_info.introducer.modifier_set.HasAnyOf( KeywordModifierSet::Interface)) { context.TODO(decl_info.introducer.modifier_node_id(ModifierOrder::Decl), "interface modifier"); } // If there was an initializer, convert it and store it on the constant. if (decl_info.init_id.has_value()) { // TODO: Diagnose if the `default` modifier was not used. auto default_value_id = ConvertToValueOfType(context, node_id, decl_info.init_id, decl.type_id); auto& assoc_const = context.associated_constants().Get(decl.assoc_const_id); assoc_const.default_value_id = default_value_id; } else { // TODO: Either allow redeclarations of associated constants or diagnose if // the `default` modifier was used. } // Store the decl block on the declaration. decl.decl_block_id = context.inst_block_stack().Pop(); ReplaceInstPreservingConstantValue(context, decl_info.pattern_id, decl); context.inst_block_stack().AddInstId(decl_info.pattern_id); return true; } auto HandleParseNode(Context& context, Parse::VariableDeclId node_id) -> bool { auto decl_info = HandleDecl(context, node_id); LimitModifiersOnDecl( context, decl_info.introducer, KeywordModifierSet::Access | KeywordModifierSet::Returned); LocalPatternMatch(context, decl_info.pattern_id, decl_info.init_id); return true; } auto HandleParseNode(Context& context, Parse::FieldDeclId node_id) -> bool { if (context.node_stack().PeekNextIs(Parse::NodeKind::FieldInitializer)) { // TODO: In a class scope, we should instead save the initializer // somewhere so that we can use it as a default. context.TODO(node_id, "Field initializer"); context.node_stack().PopExpr(); context.node_stack() .PopAndDiscardSoloNodeId(); } context.node_stack() .PopAndDiscardSoloNodeId(); auto parent_scope_inst = context.name_scopes() .GetInstIfValid(context.scope_stack().PeekNameScopeId()) .second; auto introducer = context.decl_introducer_state_stack().Pop(); CheckAccessModifiersOnDecl(context, introducer, parent_scope_inst); LimitModifiersOnDecl(context, introducer, KeywordModifierSet::Access); return true; } } // namespace Carbon::Check ================================================ FILE: toolchain/check/handle_literal.cpp ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #include #include "toolchain/check/call.h" #include "toolchain/check/context.h" #include "toolchain/check/convert.h" #include "toolchain/check/handle.h" #include "toolchain/check/inst.h" #include "toolchain/check/literal.h" #include "toolchain/check/name_lookup.h" #include "toolchain/check/type.h" #include "toolchain/diagnostics/format_providers.h" #include "toolchain/sem_ir/ids.h" #include "toolchain/sem_ir/typed_insts.h" namespace Carbon::Check { auto HandleParseNode(Context& context, Parse::BoolLiteralFalseId node_id) -> bool { context.node_stack().Push( node_id, MakeBoolLiteral(context, node_id, SemIR::BoolValue::False)); return true; } auto HandleParseNode(Context& context, Parse::BoolLiteralTrueId node_id) -> bool { context.node_stack().Push( node_id, MakeBoolLiteral(context, node_id, SemIR::BoolValue::True)); return true; } auto HandleParseNode(Context& context, Parse::CharLiteralId node_id) -> bool { auto value = context.tokens().GetCharLiteralValue( context.parse_tree().node_token(node_id)); auto inst_id = AddInst( context, node_id, {.type_id = GetSingletonType(context, SemIR::CharLiteralType::TypeInstId), .value = SemIR::CharId(value.value)}); context.node_stack().Push(node_id, inst_id); return true; } auto HandleParseNode(Context& context, Parse::IntLiteralId node_id) -> bool { auto int_literal_id = MakeIntLiteral( context, node_id, context.tokens().GetIntLiteral(context.parse_tree().node_token(node_id))); context.node_stack().Push(node_id, int_literal_id); return true; } auto HandleParseNode(Context& context, Parse::RealLiteralId node_id) -> bool { auto real_id = context.tokens().GetRealLiteral(context.parse_tree().node_token(node_id)); AddInstAndPush( context, node_id, {.type_id = GetSingletonType(context, SemIR::FloatLiteralType::TypeInstId), .real_id = real_id}); return true; } auto HandleParseNode(Context& context, Parse::StringLiteralId node_id) -> bool { auto str_literal_id = MakeStringLiteral(context, node_id, context.tokens().GetStringLiteralValue( context.parse_tree().node_token(node_id))); context.node_stack().Push(node_id, str_literal_id); return true; } auto HandleParseNode(Context& context, Parse::BoolTypeLiteralId node_id) -> bool { auto type_inst_id = MakeBoolTypeLiteral(context, node_id); context.node_stack().Push(node_id, type_inst_id); return true; } auto HandleParseNode(Context& context, Parse::CharTypeLiteralId node_id) -> bool { auto type_inst_id = MakeCharTypeLiteral(context, node_id); context.node_stack().Push(node_id, type_inst_id); return true; } // Shared implementation for handling `iN` and `uN` literals. static auto HandleIntOrUnsignedIntTypeLiteral(Context& context, Parse::NodeId node_id, SemIR::IntKind int_kind, IntId size_id) -> bool { if (!(context.ints().Get(size_id) & 3).isZero()) { CARBON_DIAGNOSTIC(IntWidthNotMultipleOf8, Error, "bit width of integer type literal must be a multiple of " "8; use `Core.{0:Int|UInt}({1})` instead", Diagnostics::BoolAsSelect, llvm::APSInt); context.emitter().Emit( node_id, IntWidthNotMultipleOf8, int_kind.is_signed(), llvm::APSInt(context.ints().Get(size_id), /*isUnsigned=*/true)); } auto type_inst_id = MakeIntTypeLiteral(context, node_id, int_kind, size_id); context.node_stack().Push(node_id, type_inst_id); return true; } auto HandleParseNode(Context& context, Parse::IntTypeLiteralId node_id) -> bool { auto tok_id = context.parse_tree().node_token(node_id); auto size_id = context.tokens().GetTypeLiteralSize(tok_id); return HandleIntOrUnsignedIntTypeLiteral(context, node_id, SemIR::IntKind::Signed, size_id); } auto HandleParseNode(Context& context, Parse::UnsignedIntTypeLiteralId node_id) -> bool { auto tok_id = context.parse_tree().node_token(node_id); auto size_id = context.tokens().GetTypeLiteralSize(tok_id); return HandleIntOrUnsignedIntTypeLiteral(context, node_id, SemIR::IntKind::Unsigned, size_id); } auto HandleParseNode(Context& context, Parse::FloatTypeLiteralId node_id) -> bool { auto tok_id = context.parse_tree().node_token(node_id); auto size_id = context.tokens().GetTypeLiteralSize(tok_id); auto type_inst_id = MakeFloatTypeLiteral(context, node_id, size_id); context.node_stack().Push(node_id, type_inst_id); return true; } auto HandleParseNode(Context& context, Parse::StringTypeLiteralId node_id) -> bool { auto type_inst_id = MakeStringTypeLiteral(context, node_id); context.node_stack().Push(node_id, type_inst_id); return true; } auto HandleParseNode(Context& context, Parse::TypeTypeLiteralId node_id) -> bool { auto type_inst_id = MakeTypeTypeLiteral(context, node_id); context.node_stack().Push(node_id, type_inst_id); return true; } auto HandleParseNode(Context& context, Parse::AutoTypeLiteralId node_id) -> bool { return context.TODO(node_id, "HandleAutoTypeLiteral"); } } // namespace Carbon::Check ================================================ FILE: toolchain/check/handle_loop_statement.cpp ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #include "toolchain/check/call.h" #include "toolchain/check/context.h" #include "toolchain/check/control_flow.h" #include "toolchain/check/convert.h" #include "toolchain/check/core_identifier.h" #include "toolchain/check/full_pattern_stack.h" #include "toolchain/check/handle.h" #include "toolchain/check/inst.h" #include "toolchain/check/member_access.h" #include "toolchain/check/operator.h" #include "toolchain/check/pattern.h" #include "toolchain/check/pattern_match.h" #include "toolchain/check/type.h" #include "toolchain/sem_ir/absolute_node_id.h" #include "toolchain/sem_ir/ids.h" namespace Carbon::Check { // Starts emitting the loop header for a `while`-like looping construct. Returns // the loop header block ID. static auto StartLoopHeader(Context& context, Parse::NodeId node_id) -> SemIR::InstBlockId { // Branch to the loop header block. Note that we create a new block here even // if the current block is empty; this ensures that the loop always has a // preheader block. auto loop_header_id = AddDominatedBlockAndBranch(context, node_id); context.inst_block_stack().Pop(); // Start emitting the loop header block. context.inst_block_stack().Push(loop_header_id); context.region_stack().AddToRegion(loop_header_id, node_id); return loop_header_id; } // Starts emitting the loop body for a `while`-like looping construct. Converts // `cond_value_id` to bool and branches to the loop body if it is `true` and to // the loop exit if it is `false`. static auto BranchAndStartLoopBody(Context& context, Parse::NodeId node_id, SemIR::InstBlockId loop_header_id, SemIR::InstId cond_value_id) -> void { cond_value_id = ConvertToBoolValue(context, node_id, cond_value_id); // Branch to either the loop body or the loop exit block. auto loop_body_id = AddDominatedBlockAndBranchIf(context, node_id, cond_value_id); auto loop_exit_id = AddDominatedBlockAndBranch(context, node_id); context.inst_block_stack().Pop(); // Start emitting the loop body. context.inst_block_stack().Push(loop_body_id); context.region_stack().AddToRegion(loop_body_id, node_id); // Allow `break` and `continue` in this scope. context.break_continue_stack().push_back( {.break_target = loop_exit_id, .continue_target = loop_header_id}); } // Finishes emitting the body for a `while`-like loop. Adds a back-edge to the // loop header, and starts emitting in the loop exit block. static auto FinishLoopBody(Context& context, Parse::NodeId node_id) -> void { auto blocks = context.break_continue_stack().pop_back_val(); // Add the loop backedge. AddInst(context, node_id, {.target_id = blocks.continue_target}); context.inst_block_stack().Pop(); // Start emitting the loop exit block. context.inst_block_stack().Push(blocks.break_target); context.region_stack().AddToRegion(blocks.break_target, node_id); } // `while` // ------- auto HandleParseNode(Context& context, Parse::WhileConditionStartId node_id) -> bool { context.node_stack().Push(node_id, StartLoopHeader(context, node_id)); return true; } auto HandleParseNode(Context& context, Parse::WhileConditionId node_id) -> bool { auto cond_value_id = context.node_stack().PopExpr(); auto loop_header_id = context.node_stack().Pop(); // Branch to either the loop body or the loop exit block, and start emitting // the loop body. BranchAndStartLoopBody(context, node_id, loop_header_id, cond_value_id); return true; } auto HandleParseNode(Context& context, Parse::WhileStatementId node_id) -> bool { FinishLoopBody(context, node_id); return true; } // `for` // ----- auto HandleParseNode(Context& context, Parse::ForHeaderStartId node_id) -> bool { // Create a nested scope to hold the cursor variable. This is also the lexical // scope that names in the pattern are added to, although they get rebound on // each loop iteration. context.scope_stack().PushForSameRegion(); // Begin an implicit let declaration context for the pattern. context.decl_introducer_state_stack().Push(); context.pattern_block_stack().Push(); context.full_pattern_stack().PushNameBindingDecl(); BeginSubpattern(context); context.node_stack().Push(node_id); return true; } auto HandleParseNode(Context& context, Parse::ForInId node_id) -> bool { auto pattern_block_id = context.pattern_block_stack().Pop(); AddInst(context, node_id, {.pattern_block_id = pattern_block_id}); context.decl_introducer_state_stack().Pop(); context.full_pattern_stack().StartPatternInitializer(); context.node_stack().Push(node_id, pattern_block_id); return true; } // For a value or reference of type `Optional(T)`, call the given accessor. static auto CallOptionalAccessor(Context& context, Parse::NodeId node_id, SemIR::InstId optional_id, CoreIdentifier accessor_name) -> SemIR::InstId { auto accessor_name_id = context.core_identifiers().AddNameId(accessor_name); auto accessor_id = PerformMemberAccess(context, node_id, optional_id, accessor_name_id); return PerformCall(context, node_id, accessor_id, {}); } auto HandleParseNode(Context& context, Parse::ForHeaderId node_id) -> bool { auto range_id = context.node_stack().PopExpr(); auto pattern_block_id = context.node_stack().Pop(); auto pattern_id = context.node_stack().PopPattern(); auto start_node_id = context.node_stack().PopForSoloNodeId(); // Convert the range expression to a value or reference so that we can use it // multiple times. // TODO: If this produces a temporary, its lifetime should presumably be // extended to cover the loop body. range_id = ConvertToValueOrRefExpr(context, range_id); // Create the cursor variable. // TODO: Produce a custom diagnostic if the range operand can't be used as a // range. // TODO: We need to allocate the `VarStorage` before building the operator. // The current order risks violating the preconditions on `Initialize` and // risks violating the topological ordering of insts. auto cursor_id = BuildUnaryOperator(context, node_id, {.interface_name = CoreIdentifier::Iterate, .op_name = CoreIdentifier::NewCursor}, range_id); auto cursor_type_id = context.insts().Get(cursor_id).type_id(); auto cursor_var_id = AddInstWithCleanup( context, node_id, {.type_id = cursor_type_id, .pattern_id = SemIR::AbsoluteInstId::None}); auto init_id = Initialize(context, node_id, cursor_var_id, cursor_id); AddInst(context, node_id, {.lhs_id = cursor_var_id, .rhs_id = init_id}); // Start emitting the loop header block. auto loop_header_id = StartLoopHeader(context, start_node_id); // Call `.(Iterate.Next)(&cursor)`. auto cursor_type_inst_id = context.types().GetTypeInstId(cursor_type_id); auto cursor_addr_id = AddInst( context, node_id, {.type_id = GetPointerType(context, cursor_type_inst_id), .lvalue_id = cursor_var_id}); auto element_id = BuildBinaryOperator(context, node_id, {.interface_name = CoreIdentifier::Iterate, .op_name = CoreIdentifier::Next}, range_id, cursor_addr_id); // We need to convert away from an initializing expression in order to call // `HasValue` and then separately pattern-match against the element. // TODO: Instead, form a `.Some(pattern_id)` pattern and pattern-match against // that. element_id = ConvertToValueOrRefExpr(context, element_id); // Branch to the loop body if the optional element has a value. auto cond_value_id = CallOptionalAccessor(context, node_id, element_id, CoreIdentifier::HasValue); BranchAndStartLoopBody(context, node_id, loop_header_id, cond_value_id); // The loop pattern's initializer is now complete, and any bindings in it // should be in scope. context.full_pattern_stack().EndPatternInitializer(); context.full_pattern_stack().PopFullPattern(); // Create storage for var patterns now. AddPatternVarStorage(context, pattern_block_id, /*is_returned_var=*/false); // Initialize the pattern from `.Get()`. auto element_value_id = CallOptionalAccessor(context, node_id, element_id, CoreIdentifier::Get); LocalPatternMatch(context, pattern_id, element_value_id); return true; } auto HandleParseNode(Context& context, Parse::ForStatementId node_id) -> bool { FinishLoopBody(context, node_id); return true; } // `break` // ------- auto HandleParseNode(Context& context, Parse::BreakStatementStartId node_id) -> bool { auto& stack = context.break_continue_stack(); if (stack.empty()) { CARBON_DIAGNOSTIC(BreakOutsideLoop, Error, "`break` can only be used in a loop"); context.emitter().Emit(node_id, BreakOutsideLoop); } else { AddInst(context, node_id, {.target_id = stack.back().break_target}); } context.inst_block_stack().Pop(); context.inst_block_stack().PushUnreachable(); return true; } auto HandleParseNode(Context& /*context*/, Parse::BreakStatementId /*node_id*/) -> bool { return true; } // `continue` // ---------- auto HandleParseNode(Context& context, Parse::ContinueStatementStartId node_id) -> bool { auto& stack = context.break_continue_stack(); if (stack.empty()) { CARBON_DIAGNOSTIC(ContinueOutsideLoop, Error, "`continue` can only be used in a loop"); context.emitter().Emit(node_id, ContinueOutsideLoop); } else { AddInst(context, node_id, {.target_id = stack.back().continue_target}); } context.inst_block_stack().Pop(); context.inst_block_stack().PushUnreachable(); return true; } auto HandleParseNode(Context& /*context*/, Parse::ContinueStatementId /*node_id*/) -> bool { return true; } } // namespace Carbon::Check ================================================ FILE: toolchain/check/handle_match.cpp ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #include "toolchain/check/context.h" #include "toolchain/check/convert.h" #include "toolchain/check/handle.h" namespace Carbon::Check { auto HandleParseNode(Context& context, Parse::MatchConditionStartId node_id) -> bool { return context.TODO(node_id, "HandleMatchConditionStart"); } auto HandleParseNode(Context& context, Parse::MatchConditionId node_id) -> bool { return context.TODO(node_id, "HandleMatchCondition"); } auto HandleParseNode(Context& context, Parse::MatchIntroducerId node_id) -> bool { return context.TODO(node_id, "HandleMatchIntroducer"); } auto HandleParseNode(Context& context, Parse::MatchStatementStartId node_id) -> bool { return context.TODO(node_id, "HandleMatchStatementStart"); } auto HandleParseNode(Context& context, Parse::MatchCaseIntroducerId node_id) -> bool { return context.TODO(node_id, "HandleMatchCaseIntroducer"); } auto HandleParseNode(Context& context, Parse::MatchCaseGuardIntroducerId node_id) -> bool { return context.TODO(node_id, "HandleMatchCaseGuardIntroducer"); } auto HandleParseNode(Context& context, Parse::MatchCaseGuardStartId node_id) -> bool { return context.TODO(node_id, "HandleMatchCaseGuardStart"); } auto HandleParseNode(Context& context, Parse::MatchCaseGuardId node_id) -> bool { return context.TODO(node_id, "HandleMatchCaseGuard"); } auto HandleParseNode(Context& context, Parse::MatchCaseId node_id) -> bool { return context.TODO(node_id, "HandleMatchCase"); } auto HandleParseNode(Context& context, Parse::MatchDefaultIntroducerId node_id) -> bool { return context.TODO(node_id, "MatchDefaultIntroducer"); } auto HandleParseNode(Context& context, Parse::MatchDefaultId node_id) -> bool { return context.TODO(node_id, "HandleMatchDefault"); } auto HandleParseNode(Context& context, Parse::MatchHandlerStartId node_id) -> bool { return context.TODO(node_id, "HandleMatchHandlerStart"); } auto HandleParseNode(Context& context, Parse::MatchHandlerId node_id) -> bool { return context.TODO(node_id, "HandleMatchHandler"); } auto HandleParseNode(Context& context, Parse::MatchStatementId node_id) -> bool { return context.TODO(node_id, "HandleMatchStatement"); } } // namespace Carbon::Check ================================================ FILE: toolchain/check/handle_modifier.cpp ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #include "toolchain/check/context.h" #include "toolchain/check/decl_introducer_state.h" #include "toolchain/check/handle.h" #include "toolchain/lex/token_kind.h" namespace Carbon::Check { CARBON_DIAGNOSTIC(ModifierPrevious, Note, "`{0}` previously appeared here", Lex::TokenKind); static auto DiagnoseRepeated(Context& context, Parse::NodeId first_node, Parse::NodeId second_node) -> void { CARBON_DIAGNOSTIC(ModifierRepeated, Error, "`{0}` repeated on declaration", Lex::TokenKind); context.emitter() .Build(second_node, ModifierRepeated, context.token_kind(second_node)) .Note(first_node, ModifierPrevious, context.token_kind(first_node)) .Emit(); } static auto DiagnoseNotAllowedWith(Context& context, Parse::NodeId first_node, Parse::NodeId second_node) -> void { CARBON_DIAGNOSTIC(ModifierNotAllowedWith, Error, "`{0}` not allowed on declaration with `{1}`", Lex::TokenKind, Lex::TokenKind); context.emitter() .Build(second_node, ModifierNotAllowedWith, context.token_kind(second_node), context.token_kind(first_node)) .Note(first_node, ModifierPrevious, context.token_kind(first_node)) .Emit(); } // Handles the keyword that starts a modifier. This may a standalone keyword, // such as `private`, or the first in a complex modifier, such as `extern` in // `extern library ...`. If valid, adds it to the modifier set and returns true. // Otherwise, diagnoses and returns false. static auto HandleModifier(Context& context, Parse::NodeId node_id, KeywordModifierSet keyword) -> bool { auto& s = context.decl_introducer_state_stack().innermost(); ModifierOrder order; KeywordModifierSet later_modifiers; if (keyword.HasAnyOf(KeywordModifierSet::Access)) { order = ModifierOrder::Access; later_modifiers = KeywordModifierSet::Extern | KeywordModifierSet::Decl | KeywordModifierSet::Evaluation; } else if (keyword.HasAnyOf(KeywordModifierSet::Extern)) { order = ModifierOrder::Extern; later_modifiers = KeywordModifierSet::Decl | KeywordModifierSet::Evaluation; } else if (keyword.HasAnyOf(KeywordModifierSet::Extend)) { order = ModifierOrder::Extend; later_modifiers = KeywordModifierSet::Decl; } else if (keyword.HasAnyOf(KeywordModifierSet::Decl)) { order = ModifierOrder::Decl; later_modifiers = KeywordModifierSet::Evaluation; } else if (keyword.HasAnyOf(KeywordModifierSet::Evaluation)) { order = ModifierOrder::Evaluation; later_modifiers = KeywordModifierSet::None; } else { CARBON_FATAL("Unexpected modifier keyword."); } auto current_modifier_node_id = s.modifier_node_id(order); if (s.modifier_set.HasAnyOf(keyword)) { DiagnoseRepeated(context, current_modifier_node_id, node_id); return false; } if (current_modifier_node_id.has_value()) { DiagnoseNotAllowedWith(context, current_modifier_node_id, node_id); return false; } if (auto later_modifier_set = s.modifier_set & later_modifiers; !later_modifier_set.empty()) { // At least one later modifier is present. Diagnose using the closest. Parse::NodeId closest_later_modifier = Parse::NodeId::None; for (auto later_order = static_cast(order) + 1; later_order <= static_cast(ModifierOrder::Last); ++later_order) { if (s.ordered_modifier_node_ids[later_order].has_value()) { closest_later_modifier = s.ordered_modifier_node_ids[later_order]; break; } } CARBON_CHECK(closest_later_modifier.has_value()); CARBON_DIAGNOSTIC(ModifierMustAppearBefore, Error, "`{0}` must appear before `{1}`", Lex::TokenKind, Lex::TokenKind); context.emitter() .Build(node_id, ModifierMustAppearBefore, context.token_kind(node_id), context.token_kind(closest_later_modifier)) .Note(closest_later_modifier, ModifierPrevious, context.token_kind(closest_later_modifier)) .Emit(); return false; } s.modifier_set.Add(keyword); s.set_modifier_node_id(order, node_id); return true; } #define CARBON_PARSE_NODE_KIND(Name) #define CARBON_PARSE_NODE_KIND_TOKEN_MODIFIER(Name) \ auto HandleParseNode(Context& context, Parse::Name##ModifierId node_id) \ -> bool { \ HandleModifier(context, node_id, KeywordModifierSet::Name); \ return true; \ } #include "toolchain/parse/node_kind.def" auto HandleParseNode(Context& context, Parse::ExternModifierWithLibraryId node_id) -> bool { auto name_literal_id = context.node_stack().Pop(); if (HandleModifier(context, node_id, KeywordModifierSet::Extern)) { auto& s = context.decl_introducer_state_stack().innermost(); s.extern_library = name_literal_id; } return true; } auto HandleParseNode(Context& context, Parse::ExternModifierId node_id) -> bool { return HandleModifier(context, node_id, KeywordModifierSet::Extern); } auto HandleParseNode(Context& context, Parse::ReturnedModifierId node_id) -> bool { return HandleModifier(context, node_id, KeywordModifierSet::Returned); } } // namespace Carbon::Check ================================================ FILE: toolchain/check/handle_name.cpp ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #include "toolchain/check/context.h" #include "toolchain/check/generic.h" #include "toolchain/check/handle.h" #include "toolchain/check/inst.h" #include "toolchain/check/member_access.h" #include "toolchain/check/name_component.h" #include "toolchain/check/name_lookup.h" #include "toolchain/check/name_ref.h" #include "toolchain/check/pointer_dereference.h" #include "toolchain/check/type.h" #include "toolchain/lex/token_kind.h" #include "toolchain/sem_ir/inst.h" #include "toolchain/sem_ir/typed_insts.h" namespace Carbon::Check { auto HandleParseNode(Context& context, Parse::MemberAccessExprId node_id) -> bool { auto node_kind = context.node_stack().PeekNodeKind(); if (node_kind == Parse::NodeKind::ParenExpr) { auto member_expr_id = context.node_stack().PopExpr(); auto base_id = context.node_stack().PopExpr(); auto member_id = PerformCompoundMemberAccess(context, node_id, base_id, member_expr_id); context.node_stack().Push(node_id, member_id); } else if (node_kind == Parse::NodeKind::IntLiteral) { auto index_inst_id = context.node_stack().PopExpr(); auto tuple_inst_id = context.node_stack().PopExpr(); auto tuple_value_inst_id = PerformTupleAccess(context, node_id, tuple_inst_id, index_inst_id); context.node_stack().Push(node_id, tuple_value_inst_id); } else { SemIR::NameId name_id = context.node_stack().PopName(); auto base_id = context.node_stack().PopExpr(); auto member_id = PerformMemberAccess(context, node_id, base_id, name_id); context.node_stack().Push(node_id, member_id); } return true; } auto HandleParseNode(Context& context, Parse::PointerMemberAccessExprId node_id) -> bool { auto diagnose_not_pointer = [&context, &node_id](SemIR::TypeId not_pointer_type_id) { // TODO: Pass in the expression we're trying to dereference to produce a // better diagnostic. CARBON_DIAGNOSTIC(ArrowOperatorOfNonPointer, Error, "cannot apply `->` operator to non-pointer type {0}", SemIR::TypeId); auto builder = context.emitter().Build(LocIdForDiagnostics::TokenOnly(node_id), ArrowOperatorOfNonPointer, not_pointer_type_id); builder.Emit(); }; auto node_kind = context.node_stack().PeekNodeKind(); if (node_kind == Parse::NodeKind::ParenExpr) { auto member_expr_id = context.node_stack().PopExpr(); auto base_id = context.node_stack().PopExpr(); auto deref_base_id = PerformPointerDereference(context, node_id, base_id, diagnose_not_pointer); auto member_id = PerformCompoundMemberAccess(context, node_id, deref_base_id, member_expr_id); context.node_stack().Push(node_id, member_id); } else if (node_kind == Parse::NodeKind::IntLiteral) { auto index_inst_id = context.node_stack().PopExpr(); auto tuple_pointer_inst_id = context.node_stack().PopExpr(); auto tuple_inst_id = PerformPointerDereference( context, node_id, tuple_pointer_inst_id, diagnose_not_pointer); auto tuple_value_inst_id = PerformTupleAccess(context, node_id, tuple_inst_id, index_inst_id); context.node_stack().Push(node_id, tuple_value_inst_id); } else { SemIR::NameId name_id = context.node_stack().PopName(); auto base_id = context.node_stack().PopExpr(); auto deref_base_id = PerformPointerDereference(context, node_id, base_id, diagnose_not_pointer); auto member_id = PerformMemberAccess(context, node_id, deref_base_id, name_id); context.node_stack().Push(node_id, member_id); } return true; } // Returns the `NameId` for an identifier node. static auto GetIdentifierAsNameId( Context& context, Parse::NodeIdOneOf node_id) -> SemIR::NameId { CARBON_CHECK(!context.parse_tree().node_has_error(node_id), "TODO: Support checking error parse nodes"); auto token = context.parse_tree().node_token(node_id); return SemIR::NameId::ForIdentifier(context.tokens().GetIdentifier(token)); } // Handle a name that is used as an expression by performing unqualified name // lookup. static auto HandleNameAsExpr(Context& context, Parse::NodeId node_id, SemIR::NameId name_id) -> SemIR::InstId { auto result = LookupUnqualifiedName(context, node_id, name_id); return BuildNameRef(context, node_id, name_id, result.scope_result.target_inst_id(), result.specific_id); } auto HandleParseNode(Context& context, Parse::IdentifierNameNotBeforeSignatureId node_id) -> bool { // The parent is responsible for binding the name. context.node_stack().Push(node_id, GetIdentifierAsNameId(context, node_id)); return true; } auto HandleParseNode(Context& context, Parse::IdentifierNameMaybeBeforeSignatureId node_id) -> bool { // Push a pattern block stack entry to handle the parameter pattern. context.pattern_block_stack().Push(); context.full_pattern_stack().PushParameterizedDecl(); // The parent is responsible for binding the name. context.node_stack().Push(node_id, GetIdentifierAsNameId(context, node_id)); return true; } auto HandleParseNode(Context& context, Parse::IdentifierNameExprId node_id) -> bool { auto name_id = GetIdentifierAsNameId(context, node_id); context.node_stack().Push(node_id, HandleNameAsExpr(context, node_id, name_id)); return true; } auto HandleParseNode(Context& context, Parse::BaseNameId node_id) -> bool { context.node_stack().Push(node_id, SemIR::NameId::Base); return true; } auto HandleParseNode(Context& context, Parse::SelfTypeNameId node_id) -> bool { context.node_stack().Push(node_id, SemIR::NameId::SelfType); return true; } auto HandleParseNode(Context& context, Parse::SelfTypeNameExprId node_id) -> bool { context.node_stack().Push( node_id, HandleNameAsExpr(context, node_id, SemIR::NameId::SelfType)); return true; } auto HandleParseNode(Context& context, Parse::SelfValueNameId node_id) -> bool { context.node_stack().Push(node_id, SemIR::NameId::SelfValue); return true; } auto HandleParseNode(Context& context, Parse::SelfValueNameExprId node_id) -> bool { context.node_stack().Push( node_id, HandleNameAsExpr(context, node_id, SemIR::NameId::SelfValue)); return true; } // Common logic for name qualifiers. static auto ApplyNameQualifier(Context& context) -> bool { context.decl_name_stack().ApplyNameQualifier(PopNameComponent(context)); return true; } auto HandleParseNode(Context& context, Parse::IdentifierNameQualifierWithParamsId /*node_id*/) -> bool { return ApplyNameQualifier(context); } auto HandleParseNode(Context& context, Parse::IdentifierNameQualifierWithoutParamsId /*node_id*/) -> bool { return ApplyNameQualifier(context); } auto HandleParseNode(Context& context, Parse::DesignatorExprId node_id) -> bool { SemIR::NameId name_id = context.node_stack().PopName(); if (name_id == SemIR::NameId::SelfType) { // Look up `.Self`. SemIR::InstId period_self_id = HandleNameAsExpr(context, node_id, SemIR::NameId::PeriodSelf); context.node_stack().Push(node_id, period_self_id); } else { // Otherwise this is `.Member`, so look up `.Self` and then `Member` in // `.Self`. SemIR::InstId period_self_id = SemIR::InstId::None; { // TODO: Instead of annotating the diagnostic, should change // `HandleNameAsExpr` to optionally allow us to produce the diagnostic // instead so we can generate a "name `.Self` implicitly referenced by // designated expression, but not found" diagnostic instead of adding a // note to the current "name `.Self` not found" message. Diagnostics::AnnotationScope annotate_diagnostics( &context.emitter(), [&](auto& builder) { CARBON_DIAGNOSTIC( NoPeriodSelfForDesignator, Note, "designator may only be used when `.Self` is in scope"); builder.Note(SemIR::LocId::None, NoPeriodSelfForDesignator); }); period_self_id = HandleNameAsExpr(context, node_id, SemIR::NameId::PeriodSelf); } auto member_id = PerformMemberAccess(context, node_id, period_self_id, name_id); context.node_stack().Push(node_id, member_id); } return true; } auto HandleParseNode(Context& context, Parse::PackageExprId node_id) -> bool { AddInstAndPush( context, node_id, {.type_id = GetSingletonType(context, SemIR::NamespaceType::TypeInstId), .name_id = SemIR::NameId::PackageKeyword, .value_id = SemIR::Namespace::PackageInstId}); return true; } auto HandleParseNode(Context& context, Parse::CoreNameExprId node_id) -> bool { // TODO: Unqualified lookup will never find anything; perform lookup directly // into file scope. context.node_stack().Push( node_id, HandleNameAsExpr(context, node_id, SemIR::NameId::Core)); return true; } auto HandleParseNode(Context& context, Parse::CppNameExprId node_id) -> bool { // TODO: Unqualified lookup will never find anything; perform lookup directly // into file scope. context.node_stack().Push( node_id, HandleNameAsExpr(context, node_id, SemIR::NameId::Cpp)); return true; } } // namespace Carbon::Check ================================================ FILE: toolchain/check/handle_named_constraint.cpp ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #include "toolchain/check/context.h" #include "toolchain/check/generic.h" #include "toolchain/check/handle.h" #include "toolchain/check/inst.h" #include "toolchain/check/interface.h" #include "toolchain/check/modifiers.h" #include "toolchain/check/type.h" #include "toolchain/sem_ir/ids.h" #include "toolchain/sem_ir/named_constraint.h" #include "toolchain/sem_ir/typed_insts.h" namespace Carbon::Check { auto HandleParseNode(Context& context, Parse::NamedConstraintIntroducerId node_id) -> bool { // This named constraint is potentially generic. StartGenericDecl(context); // Create an instruction block to hold the instructions created as part of the // named constraint signature, such as generic parameters. context.inst_block_stack().Push(); // Optional modifiers and the name follow. context.decl_introducer_state_stack().Push(); context.decl_name_stack().PushScopeAndStartName(); // Push the bracketing node. context.node_stack().Push(node_id); return true; } static auto BuildNamedConstraintDecl(Context& context, Parse::AnyNamedConstraintDeclId node_id, bool is_definition) -> std::tuple { auto name = PopNameComponent(context); auto name_context = context.decl_name_stack().FinishName(name); context.node_stack() .PopAndDiscardSoloNodeId(); // TODO: PopSoloNodeId(`template`) if it's present, and track that in the // NamedConstraint. Or maybe it should be a modifier, like `abstract class`? // Process modifiers. auto [_, parent_scope_inst] = context.name_scopes().GetInstIfValid(name_context.parent_scope_id); auto introducer = context.decl_introducer_state_stack().Pop(); CheckAccessModifiersOnDecl(context, introducer, parent_scope_inst); LimitModifiersOnDecl(context, introducer, KeywordModifierSet::Access); auto decl_block_id = context.inst_block_stack().Pop(); // Add the constraint declaration. auto constraint_decl = SemIR::NamedConstraintDecl{ SemIR::TypeType::TypeId, SemIR::NamedConstraintId::None, decl_block_id}; auto decl_inst_id = AddPlaceholderInst(context, node_id, constraint_decl); SemIR::NamedConstraint constraint_info = { name_context.MakeEntityWithParamsBase(name, decl_inst_id, /*is_extern=*/false, SemIR::LibraryNameId::None)}; DiagnoseIfGenericMissingExplicitParameters(context, constraint_info); // Check whether this is a redeclaration. SemIR::ScopeLookupResult lookup_result = context.decl_name_stack().LookupOrAddName( name_context, decl_inst_id, introducer.modifier_set.GetAccessKind()); if (auto existing_decl = TryGetExistingDecl(context, name, lookup_result, constraint_info, is_definition)) { auto existing_constraint_decl = existing_decl->As(); constraint_decl.named_constraint_id = existing_constraint_decl.named_constraint_id; constraint_decl.type_id = existing_constraint_decl.type_id; // TODO: If the new declaration is a definition, keep its parameter // and implicit parameter lists rather than the ones from the // previous declaration. auto prev_decl_generic_id = context.named_constraints() .Get(constraint_decl.named_constraint_id) .generic_id; FinishGenericRedecl(context, prev_decl_generic_id); } else { // Create a new named constraint if this isn't a valid redeclaration. constraint_info.generic_id = BuildGenericDecl(context, decl_inst_id); constraint_decl.named_constraint_id = context.named_constraints().Add(constraint_info); if (constraint_info.has_parameters()) { constraint_decl.type_id = GetGenericNamedConstraintType( context, constraint_decl.named_constraint_id, context.scope_stack().PeekSpecificId()); } } // Write the completed NamedConstraintDecl instruction. ReplaceInstBeforeConstantUse(context, decl_inst_id, constraint_decl); return {constraint_decl.named_constraint_id, decl_inst_id}; } auto HandleParseNode(Context& context, Parse::NamedConstraintDeclId node_id) -> bool { BuildNamedConstraintDecl(context, node_id, /*is_definition=*/false); context.decl_name_stack().PopScope(); return true; } auto HandleParseNode(Context& context, Parse::NamedConstraintDefinitionStartId node_id) -> bool { auto [named_constraint_id, decl_inst_id] = BuildNamedConstraintDecl(context, node_id, /*is_definition=*/true); auto& constraint_info = context.named_constraints().Get(named_constraint_id); // TODO: Support for `template constraint`. bool is_template = false; // Track that this declaration is the definition. CARBON_CHECK(!constraint_info.has_definition_started(), "Can't merge with defined named constraints."); constraint_info.definition_id = decl_inst_id; constraint_info.scope_without_self_id = context.name_scopes().Add( decl_inst_id, SemIR::NameId::None, constraint_info.parent_scope_id); StartGenericDefinition(context, constraint_info.generic_id); context.inst_block_stack().Push(); // Enter the constraint-without-self scope, which is used for the Self // instruction, since it needs to reference the constraint (without-self) // generic. Self can't reference the constraint-with-self generic since it's a // parameter to the generic. context.scope_stack().PushForEntity( decl_inst_id, constraint_info.scope_without_self_id, context.generics().GetSelfSpecific(constraint_info.generic_id)); // Declare and introduce `Self`. We model `Self` as a symbolic binding whose // type is the named constraint, excluding any other interfaces mentioned by // `require` declarations. This makes it an empty facet type. SemIR::TypeId self_type_id = GetNamedConstraintType( context, named_constraint_id, context.generics().GetSelfSpecific(constraint_info.generic_id)); constraint_info.self_param_id = AddSelfSymbolicBindingToScope( context, node_id, self_type_id, constraint_info.scope_without_self_id, is_template); // Start the declaration of constraint-with-self. StartGenericDecl(context); // Push `Self` as a parameter of the constraint-with-self. context.scope_stack().PushCompileTimeBinding(constraint_info.self_param_id); // Add the interface-with-self declaration and build the generic for it. This // captures the `interface_info.self_param_id` as a parameter of the generic. auto constraint_with_self_decl = SemIR::NamedConstraintWithSelfDecl{ .named_constraint_id = named_constraint_id}; auto decl_with_self_inst_id = AddPlaceholderInst(context, node_id, constraint_with_self_decl); auto generic_with_self_id = BuildGenericDecl(context, decl_with_self_inst_id); constraint_info.generic_with_self_id = generic_with_self_id; ReplaceInstBeforeConstantUse(context, decl_with_self_inst_id, constraint_with_self_decl); constraint_info.scope_with_self_id = context.name_scopes().Add(decl_with_self_inst_id, SemIR::NameId::None, constraint_info.scope_without_self_id); // Start the definition of constraint-with-self. StartGenericDefinition(context, constraint_info.generic_with_self_id); // Enter a scope for the constraint-with-self. context.scope_stack().PushForEntity( decl_with_self_inst_id, constraint_info.scope_with_self_id, context.generics().GetSelfSpecific(constraint_info.generic_with_self_id)); constraint_info.body_block_without_self_id = context.inst_block_stack().PeekOrAdd(); context.inst_block_stack().Push(); constraint_info.body_block_with_self_id = context.inst_block_stack().PeekOrAdd(); context.require_impls_stack().PushArray(); context.node_stack().Push(node_id, named_constraint_id); return true; } auto HandleParseNode(Context& context, Parse::NamedConstraintDefinitionId /*node_id*/) -> bool { auto named_constraint_id = context.node_stack() .Pop(); // Pop the body_block_with_self. context.inst_block_stack().Pop(); auto require_impls_block_id = context.require_impls_blocks().Add( context.require_impls_stack().PeekArray()); context.require_impls_stack().PopArray(); auto& constraint_info = context.named_constraints().Get(named_constraint_id); if (!constraint_info.complete) { constraint_info.require_impls_block_id = require_impls_block_id; // TODO: Do something with `alias` statements in the body of the // constraint. constraint_info.complete = true; } // Finish the definition of constraint-with-self. FinishGenericDefinition(context, constraint_info.generic_with_self_id); // Pop the body_block_without_self. context.inst_block_stack().Pop(); // Finish the definition of interfconstraintace-without-self. FinishGenericDefinition(context, constraint_info.generic_id); // The decl_name_stack and scopes are popped by `ProcessNodeIds`. return true; } } // namespace Carbon::Check ================================================ FILE: toolchain/check/handle_namespace.cpp ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #include "toolchain/check/context.h" #include "toolchain/check/decl_introducer_state.h" #include "toolchain/check/generic.h" #include "toolchain/check/handle.h" #include "toolchain/check/inst.h" #include "toolchain/check/modifiers.h" #include "toolchain/check/name_component.h" #include "toolchain/check/name_lookup.h" #include "toolchain/check/type.h" #include "toolchain/sem_ir/ids.h" #include "toolchain/sem_ir/inst.h" #include "toolchain/sem_ir/name_scope.h" #include "toolchain/sem_ir/typed_insts.h" namespace Carbon::Check { auto HandleParseNode(Context& context, Parse::NamespaceStartId /*node_id*/) -> bool { // Namespaces can't be generic, but we might have parsed a generic parameter // in their name, so enter a generic scope just in case. StartGenericDecl(context); // Optional modifiers and the name follow. context.decl_introducer_state_stack().Push(); context.decl_name_stack().PushScopeAndStartName(); return true; } static auto IsNamespaceScope(Context& context, SemIR::NameScopeId name_scope_id) -> bool { auto [_, inst] = context.name_scopes().GetInstIfValid(name_scope_id); return inst && inst->Is(); } auto HandleParseNode(Context& context, Parse::NamespaceId node_id) -> bool { auto name_context = context.decl_name_stack().FinishName( PopNameComponentWithoutParams(context, Lex::TokenKind::Namespace)); DiscardGenericDecl(context); auto introducer = context.decl_introducer_state_stack().Pop(); LimitModifiersOnDecl(context, introducer, KeywordModifierSet::None); auto namespace_inst = SemIR::Namespace{ GetSingletonType(context, SemIR::NamespaceType::TypeInstId), SemIR::NameScopeId::None, SemIR::InstId::None}; auto namespace_id = AddPlaceholderInst(context, node_id, namespace_inst); SemIR::ScopeLookupResult lookup_result = context.decl_name_stack().LookupOrAddName(name_context, namespace_id, SemIR::AccessKind::Public); if (lookup_result.is_poisoned()) { DiagnosePoisonedName(context, name_context.name_id_for_new_inst(), lookup_result.poisoning_loc_id(), name_context.loc_id); } else if (lookup_result.is_found()) { SemIR::InstId existing_inst_id = lookup_result.target_inst_id(); if (auto existing = context.insts().TryGetAs(existing_inst_id)) { // If there's a name conflict with a namespace, "merge" by using the // previous declaration. Otherwise, diagnose the issue. // Point at the other namespace. namespace_inst.name_scope_id = existing->name_scope_id; if (context.name_scopes() .Get(existing->name_scope_id) .is_closed_import()) { // The existing name is a package name, so this is a name conflict. DiagnoseDuplicateName(context, name_context.name_id, name_context.loc_id, SemIR::LocId(existing_inst_id)); // Treat this as a local namespace name from now on to avoid further // diagnostics. context.name_scopes() .Get(existing->name_scope_id) .set_is_closed_import(false); } else if (existing->import_id.has_value() && !context.insts() .GetCanonicalLocId(existing_inst_id) .has_value()) { // When the name conflict is an imported namespace, fill the location ID // so that future diagnostics point at this declaration. SetNamespaceNodeId(context, existing_inst_id, node_id); } } else { DiagnoseDuplicateName(context, name_context.name_id, name_context.loc_id, SemIR::LocId(existing_inst_id)); } } // If we weren't able to merge namespaces, add a new name scope. Note this // occurs even for duplicates where we discard the namespace, because we want // to produce a valid constant. if (!namespace_inst.name_scope_id.has_value()) { namespace_inst.name_scope_id = context.name_scopes().Add( namespace_id, name_context.name_id_for_new_inst(), name_context.parent_scope_id); if (!IsNamespaceScope(context, name_context.parent_scope_id)) { CARBON_DIAGNOSTIC(NamespaceDeclNotAtTopLevel, Error, "`namespace` declaration not at top level"); context.emitter().Emit(node_id, NamespaceDeclNotAtTopLevel); } } ReplaceInstBeforeConstantUse(context, namespace_id, namespace_inst); context.decl_name_stack().PopScope(); return true; } } // namespace Carbon::Check ================================================ FILE: toolchain/check/handle_noop.cpp ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #include "toolchain/check/context.h" #include "toolchain/check/handle.h" namespace Carbon::Check { auto HandleParseNode(Context& /*context*/, Parse::EmptyDeclId /*node_id*/) -> bool { // Empty declarations have no actions associated. return true; } auto HandleParseNode(Context& /*context*/, Parse::InvalidParseId /*node_id*/) -> bool { CARBON_FATAL("Unreachable until we support checking error parse nodes"); } auto HandleParseNode(Context& /*context*/, Parse::InvalidParseStartId /*node_id*/) -> bool { CARBON_FATAL("Unreachable until we support checking error parse nodes"); } auto HandleParseNode(Context& /*context*/, Parse::InvalidParseSubtreeId /*node_id*/) -> bool { CARBON_FATAL("Unreachable until we support checking error parse nodes"); } auto HandleParseNode(Context& /*context*/, Parse::PlaceholderId /*node_id*/) -> bool { CARBON_FATAL( "Placeholder node should always be replaced before parse completes"); } } // namespace Carbon::Check ================================================ FILE: toolchain/check/handle_observe.cpp ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #include "toolchain/check/context.h" #include "toolchain/check/handle.h" #include "toolchain/parse/node_ids.h" namespace Carbon::Check { auto HandleParseNode(Context& context, Parse::ObserveIntroducerId node_id) -> bool { return context.TODO(node_id, "ObserveIntroducerId"); } auto HandleParseNode(Context& context, Parse::ObserveEqualEqualId node_id) -> bool { return context.TODO(node_id, "ObserveEqualEqualId"); } auto HandleParseNode(Context& context, Parse::ObserveImplsId node_id) -> bool { return context.TODO(node_id, "ObserveImplsId"); } auto HandleParseNode(Context& context, Parse::ObserveDeclId node_id) -> bool { return context.TODO(node_id, "ObserveDeclId"); } } // namespace Carbon::Check ================================================ FILE: toolchain/check/handle_operator.cpp ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #include "toolchain/check/context.h" #include "toolchain/check/control_flow.h" #include "toolchain/check/convert.h" #include "toolchain/check/core_identifier.h" #include "toolchain/check/handle.h" #include "toolchain/check/inst.h" #include "toolchain/check/operator.h" #include "toolchain/check/pointer_dereference.h" #include "toolchain/check/type.h" #include "toolchain/diagnostics/emitter.h" #include "toolchain/parse/typed_nodes.h" #include "toolchain/sem_ir/expr_info.h" namespace Carbon::Check { // Common logic for unary operator handlers. static auto HandleUnaryOperator(Context& context, Parse::AnyExprId expr_node_id, CoreIdentifier interface_name) -> bool { auto operand_id = context.node_stack().PopExpr(); auto result_id = BuildUnaryOperator( context, expr_node_id, {.interface_name = interface_name}, operand_id); context.node_stack().Push(expr_node_id, result_id); return true; } // Common logic for binary operator handlers. static auto HandleBinaryOperator(Context& context, Parse::AnyExprId expr_node_id, CoreIdentifier interface_name, CoreIdentifier op_name = CoreIdentifier::Op) -> bool { auto rhs_id = context.node_stack().PopExpr(); auto lhs_id = context.node_stack().PopExpr(); // All the `*With` binary operator interfaces take a single argument that is // the type of the RHS operand. `as` has different rules and we don't call // this function for it. SemIR::InstId args[] = { context.types().GetTypeInstId(context.insts().Get(rhs_id).type_id())}; auto result_id = BuildBinaryOperator(context, expr_node_id, {.interface_name = interface_name, .interface_args_ref = args, .op_name = op_name}, lhs_id, rhs_id); context.node_stack().Push(expr_node_id, result_id); return true; } auto HandleParseNode(Context& context, Parse::InfixOperatorAmpId node_id) -> bool { // TODO: Facet type intersection may need to be handled directly. return HandleBinaryOperator(context, node_id, CoreIdentifier::BitAndWith); } auto HandleParseNode(Context& context, Parse::InfixOperatorAmpEqualId node_id) -> bool { return HandleBinaryOperator(context, node_id, CoreIdentifier::BitAndAssignWith); } auto HandleParseNode(Context& context, Parse::UnsafeModifierId node_id) -> bool { auto [rhs_node, rhs_id] = context.node_stack().PopExprWithNodeId(); context.node_stack().Push(node_id, rhs_id); return true; } auto HandleParseNode(Context& context, Parse::InfixOperatorAsId node_id) -> bool { auto [rhs_node, rhs_id] = context.node_stack().PopExprWithNodeId(); auto [lhs_node, lhs_id] = context.node_stack().PopExprWithNodeId(); bool unsafe = context.parse_tree().node_kind(lhs_node) == Parse::NodeKind::UnsafeModifier; auto rhs_type_id = ExprAsType(context, rhs_node, rhs_id).type_id; context.node_stack().Push( node_id, ConvertForExplicitAs(context, node_id, lhs_id, rhs_type_id, unsafe)); return true; } auto HandleParseNode(Context& context, Parse::InfixOperatorCaretId node_id) -> bool { return HandleBinaryOperator(context, node_id, CoreIdentifier::BitXorWith); } auto HandleParseNode(Context& context, Parse::InfixOperatorCaretEqualId node_id) -> bool { return HandleBinaryOperator(context, node_id, CoreIdentifier::BitXorAssignWith); } auto HandleParseNode(Context& context, Parse::InfixOperatorEqualId node_id) -> bool { // TODO: Switch to using assignment interface for most assignment. Some cases // may need to be handled directly. // // return HandleBinaryOperator(context, node_id, // CoreIdentifier::AssignWith); auto [rhs_node, rhs_id] = context.node_stack().PopExprWithNodeId(); auto [lhs_node, lhs_id] = context.node_stack().PopExprWithNodeId(); if (auto lhs_cat = SemIR::GetExprCategory(context.sem_ir(), lhs_id); lhs_cat != SemIR::ExprCategory::DurableRef && lhs_cat != SemIR::ExprCategory::Error) { CARBON_DIAGNOSTIC(AssignmentToNonAssignable, Error, "expression is not assignable"); context.emitter().Emit(lhs_node, AssignmentToNonAssignable); } // TODO: Destroy the old value before reinitializing. This will require // building the destruction code before we build the RHS subexpression. rhs_id = Initialize(context, node_id, lhs_id, rhs_id); AddInst(context, node_id, {.lhs_id = lhs_id, .rhs_id = rhs_id}); // We model assignment as an expression, so we need to push a value for // it, even though it doesn't produce a value. // TODO: Consider changing our parse tree to model assignment as a // different kind of statement than an expression statement. context.node_stack().Push(node_id, lhs_id); return true; } auto HandleParseNode(Context& context, Parse::InfixOperatorEqualEqualId node_id) -> bool { return HandleBinaryOperator(context, node_id, CoreIdentifier::EqWith, CoreIdentifier::Equal); } auto HandleParseNode(Context& context, Parse::InfixOperatorExclaimEqualId node_id) -> bool { return HandleBinaryOperator(context, node_id, CoreIdentifier::EqWith, CoreIdentifier::NotEqual); } auto HandleParseNode(Context& context, Parse::InfixOperatorGreaterId node_id) -> bool { return HandleBinaryOperator(context, node_id, CoreIdentifier::OrderedWith, CoreIdentifier::Greater); } auto HandleParseNode(Context& context, Parse::InfixOperatorGreaterEqualId node_id) -> bool { return HandleBinaryOperator(context, node_id, CoreIdentifier::OrderedWith, CoreIdentifier::GreaterOrEquivalent); } auto HandleParseNode(Context& context, Parse::InfixOperatorGreaterGreaterId node_id) -> bool { return HandleBinaryOperator(context, node_id, CoreIdentifier::RightShiftWith); } auto HandleParseNode(Context& context, Parse::InfixOperatorGreaterGreaterEqualId node_id) -> bool { return HandleBinaryOperator(context, node_id, CoreIdentifier::RightShiftAssignWith); } auto HandleParseNode(Context& context, Parse::InfixOperatorLessId node_id) -> bool { return HandleBinaryOperator(context, node_id, CoreIdentifier::OrderedWith, CoreIdentifier::Less); } auto HandleParseNode(Context& context, Parse::InfixOperatorLessEqualId node_id) -> bool { return HandleBinaryOperator(context, node_id, CoreIdentifier::OrderedWith, CoreIdentifier::LessOrEquivalent); } auto HandleParseNode(Context& context, Parse::InfixOperatorLessEqualGreaterId node_id) -> bool { return context.TODO(node_id, "remove <=> operator that is not in the design"); } auto HandleParseNode(Context& context, Parse::InfixOperatorLessLessId node_id) -> bool { return HandleBinaryOperator(context, node_id, CoreIdentifier::LeftShiftWith); } auto HandleParseNode(Context& context, Parse::InfixOperatorLessLessEqualId node_id) -> bool { return HandleBinaryOperator(context, node_id, CoreIdentifier::LeftShiftAssignWith); } auto HandleParseNode(Context& context, Parse::InfixOperatorMinusId node_id) -> bool { return HandleBinaryOperator(context, node_id, CoreIdentifier::SubWith); } auto HandleParseNode(Context& context, Parse::InfixOperatorMinusEqualId node_id) -> bool { return HandleBinaryOperator(context, node_id, CoreIdentifier::SubAssignWith); } auto HandleParseNode(Context& context, Parse::InfixOperatorPercentId node_id) -> bool { return HandleBinaryOperator(context, node_id, CoreIdentifier::ModWith); } auto HandleParseNode(Context& context, Parse::InfixOperatorPercentEqualId node_id) -> bool { return HandleBinaryOperator(context, node_id, CoreIdentifier::ModAssignWith); } auto HandleParseNode(Context& context, Parse::InfixOperatorPipeId node_id) -> bool { return HandleBinaryOperator(context, node_id, CoreIdentifier::BitOrWith); } auto HandleParseNode(Context& context, Parse::InfixOperatorPipeEqualId node_id) -> bool { return HandleBinaryOperator(context, node_id, CoreIdentifier::BitOrAssignWith); } auto HandleParseNode(Context& context, Parse::InfixOperatorPlusId node_id) -> bool { return HandleBinaryOperator(context, node_id, CoreIdentifier::AddWith); } auto HandleParseNode(Context& context, Parse::InfixOperatorPlusEqualId node_id) -> bool { return HandleBinaryOperator(context, node_id, CoreIdentifier::AddAssignWith); } auto HandleParseNode(Context& context, Parse::InfixOperatorSlashId node_id) -> bool { return HandleBinaryOperator(context, node_id, CoreIdentifier::DivWith); } auto HandleParseNode(Context& context, Parse::InfixOperatorSlashEqualId node_id) -> bool { return HandleBinaryOperator(context, node_id, CoreIdentifier::DivAssignWith); } auto HandleParseNode(Context& context, Parse::InfixOperatorStarId node_id) -> bool { return HandleBinaryOperator(context, node_id, CoreIdentifier::MulWith); } auto HandleParseNode(Context& context, Parse::InfixOperatorStarEqualId node_id) -> bool { return HandleBinaryOperator(context, node_id, CoreIdentifier::MulAssignWith); } auto HandleParseNode(Context& context, Parse::PostfixOperatorStarId node_id) -> bool { auto value_id = context.node_stack().PopExpr(); auto inner_type = ExprAsType(context, node_id, value_id); AddInstAndPush( context, node_id, {.type_id = SemIR::TypeType::TypeId, .pointee_id = inner_type.inst_id}); return true; } auto HandleParseNode(Context& context, Parse::PrefixOperatorAmpId node_id) -> bool { auto value_id = context.node_stack().PopExpr(); auto type_id = context.insts().Get(value_id).type_id(); // Only durable reference expressions can have their address taken. switch (SemIR::GetExprCategory(context.sem_ir(), value_id)) { case SemIR::ExprCategory::DurableRef: case SemIR::ExprCategory::Error: break; default: CARBON_DIAGNOSTIC(AddrOfNonRef, Error, "cannot take the address of non-reference expression"); context.emitter().Emit(LocIdForDiagnostics::TokenOnly(node_id), AddrOfNonRef); value_id = SemIR::ErrorInst::InstId; break; } // TODO: Preserve spelling of type of operand where possible. auto type_inst_id = context.types().GetTypeInstId(type_id); AddInstAndPush( context, node_id, SemIR::AddrOf{.type_id = GetPointerType(context, type_inst_id), .lvalue_id = value_id}); return true; } auto HandleParseNode(Context& context, Parse::PrefixOperatorCaretId node_id) -> bool { return HandleUnaryOperator(context, node_id, CoreIdentifier::BitComplement); } auto HandleParseNode(Context& context, Parse::PrefixOperatorConstId node_id) -> bool { auto value_id = context.node_stack().PopExpr(); // `const (const T)` is probably not what the developer intended. // TODO: Detect `const (const T)*` and suggest moving the `*` inside the // parentheses. if (context.insts().Get(value_id).kind() == SemIR::ConstType::Kind) { CARBON_DIAGNOSTIC(RepeatedConst, Warning, "`const` applied repeatedly to the same type has no " "additional effect"); context.emitter().Emit(node_id, RepeatedConst); } auto inner_type = ExprAsType(context, node_id, value_id); AddInstAndPush( context, node_id, {.type_id = SemIR::TypeType::TypeId, .inner_id = inner_type.inst_id}); return true; } auto HandleParseNode(Context& context, Parse::PrefixOperatorMinusId node_id) -> bool { return HandleUnaryOperator(context, node_id, CoreIdentifier::Negate); } auto HandleParseNode(Context& context, Parse::PrefixOperatorMinusMinusId node_id) -> bool { return HandleUnaryOperator(context, node_id, CoreIdentifier::Dec); } auto HandleParseNode(Context& context, Parse::PrefixOperatorNotId node_id) -> bool { auto value_id = context.node_stack().PopExpr(); value_id = ConvertToBoolValue(context, node_id, value_id); AddInstAndPush( context, node_id, {.type_id = context.insts().Get(value_id).type_id(), .operand_id = value_id}); return true; } auto HandleParseNode(Context& context, Parse::PrefixOperatorPartialId node_id) -> bool { auto value_id = context.node_stack().PopExpr(); auto inner_type = ExprAsType(context, node_id, value_id); auto class_type = context.types().TryGetAs(inner_type.type_id); if (!class_type || context.classes().Get(class_type->class_id).inheritance_kind == SemIR::Class::InheritanceKind::Final) { CARBON_DIAGNOSTIC(PartialOnFinal, Error, "`partial` applied to final type {0}", SemIR::TypeId); context.emitter().Emit(node_id, PartialOnFinal, inner_type.type_id); } AddInstAndPush( context, node_id, {.type_id = SemIR::TypeType::TypeId, .inner_id = inner_type.inst_id}); return true; } auto HandleParseNode(Context& context, Parse::PrefixOperatorPlusPlusId node_id) -> bool { return HandleUnaryOperator(context, node_id, CoreIdentifier::Inc); } auto HandleParseNode(Context& context, Parse::PrefixOperatorRefId node_id) -> bool { auto expr_id = context.node_stack().PopExpr(); auto ref_id = AddInst( context, node_id, {.type_id = context.insts().Get(expr_id).type_id(), .expr_id = expr_id}); context.node_stack().Push(node_id, ref_id); return true; } auto HandleParseNode(Context& context, Parse::PrefixOperatorStarId node_id) -> bool { auto base_id = context.node_stack().PopExpr(); auto deref_base_id = PerformPointerDereference( context, node_id, base_id, [&context, &node_id](SemIR::TypeId not_pointer_type_id) { // TODO: Pass in the expression we're trying to dereference to produce a // better diagnostic. CARBON_DIAGNOSTIC(DerefOfNonPointer, Error, "cannot dereference operand of non-pointer type {0}", SemIR::TypeId); auto builder = context.emitter().Build(LocIdForDiagnostics::TokenOnly(node_id), DerefOfNonPointer, not_pointer_type_id); // TODO: Check for any facet here, rather than only a type. if (not_pointer_type_id == SemIR::TypeType::TypeId) { CARBON_DIAGNOSTIC( DerefOfType, Note, "to form a pointer type, write the `*` after the pointee type"); builder.Note(LocIdForDiagnostics::TokenOnly(node_id), DerefOfType); } builder.Emit(); }); context.node_stack().Push(node_id, deref_base_id); return true; } // Adds the branch for a short circuit operand. static auto HandleShortCircuitOperand(Context& context, Parse::NodeId node_id, bool is_or) -> bool { // Convert the condition to `bool`. auto [cond_node, cond_value_id] = context.node_stack().PopExprWithNodeId(); cond_value_id = ConvertToBoolValue(context, node_id, cond_value_id); auto bool_type_id = context.insts().Get(cond_value_id).type_id(); // Compute the branch value: the condition for `and`, inverted for `or`. SemIR::InstId branch_value_id = is_or ? AddInst( context, node_id, {.type_id = bool_type_id, .operand_id = cond_value_id}) : cond_value_id; auto short_circuit_result_id = AddInst( context, node_id, {.type_id = bool_type_id, .value = SemIR::BoolValue::From(is_or)}); // Create a block for the right-hand side and for the continuation. auto rhs_block_id = AddDominatedBlockAndBranchIf(context, node_id, branch_value_id); auto end_block_id = AddDominatedBlockAndBranchWithArg( context, node_id, short_circuit_result_id); // Push the branch condition and result for use when handling the complete // expression. context.node_stack().Push(cond_node, branch_value_id); context.node_stack().Push(cond_node, short_circuit_result_id); // Push the resumption and the right-hand side blocks, and start emitting the // right-hand operand. context.inst_block_stack().Pop(); context.inst_block_stack().Push(end_block_id); context.inst_block_stack().Push(rhs_block_id); context.region_stack().AddToRegion(rhs_block_id, node_id); // HandleShortCircuitOperator will follow, and doesn't need the operand on the // node stack. return true; } auto HandleParseNode(Context& context, Parse::ShortCircuitOperandAndId node_id) -> bool { return HandleShortCircuitOperand(context, node_id, /*is_or=*/false); } auto HandleParseNode(Context& context, Parse::ShortCircuitOperandOrId node_id) -> bool { return HandleShortCircuitOperand(context, node_id, /*is_or=*/true); } // Short circuit operator handling is uniform because the branching logic // occurs during operand handling. static auto HandleShortCircuitOperator(Context& context, Parse::NodeId node_id) -> bool { if (!context.scope_stack().IsInFunctionScope()) { return context.TODO(node_id, "Control flow expressions are currently only supported " "inside functions."); } auto [rhs_node, rhs_id] = context.node_stack().PopExprWithNodeId(); auto short_circuit_result_id = context.node_stack().PopExpr(); auto branch_value_id = context.node_stack().PopExpr(); // The first operand is wrapped in a ShortCircuitOperand, which we // already handled by creating a RHS block and a resumption block, which // are the current block and its enclosing block. rhs_id = ConvertToBoolValue(context, node_id, rhs_id); // When the second operand is evaluated, the result of `and` and `or` is // its value. auto resume_block_id = context.inst_block_stack().PeekOrAdd(/*depth=*/1); AddInst( context, node_id, {.target_id = resume_block_id, .arg_id = rhs_id}); context.inst_block_stack().Pop(); context.region_stack().AddToRegion(resume_block_id, node_id); // Collect the result from either the first or second operand. auto result_id = AddInst( context, node_id, {.type_id = context.insts().Get(rhs_id).type_id(), .block_id = resume_block_id}); SetBlockArgResultBeforeConstantUse(context, result_id, branch_value_id, rhs_id, short_circuit_result_id); context.node_stack().Push(node_id, result_id); return true; } auto HandleParseNode(Context& context, Parse::ShortCircuitOperatorAndId node_id) -> bool { return HandleShortCircuitOperator(context, node_id); } auto HandleParseNode(Context& context, Parse::ShortCircuitOperatorOrId node_id) -> bool { return HandleShortCircuitOperator(context, node_id); } } // namespace Carbon::Check ================================================ FILE: toolchain/check/handle_paren_expr.cpp ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #include "toolchain/check/context.h" #include "toolchain/check/handle.h" namespace Carbon::Check { auto HandleParseNode(Context& /*context*/, Parse::ParenExprStartId /*node_id*/) -> bool { // The open paren is unused. return true; } auto HandleParseNode(Context& context, Parse::ParenExprId node_id) -> bool { // We re-push because the ParenExpr is valid for member expressions, whereas // the child expression might not be. context.node_stack().Push(node_id, context.node_stack().PopExpr()); return true; } } // namespace Carbon::Check ================================================ FILE: toolchain/check/handle_pattern_list.cpp ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #include "toolchain/check/context.h" #include "toolchain/check/handle.h" #include "toolchain/check/inst.h" #include "toolchain/check/pattern.h" #include "toolchain/check/type.h" namespace Carbon::Check { // Handle the start of any kind of pattern list. static auto HandlePatternListStart(Context& context, Parse::NodeId node_id) -> bool { context.node_stack().Push(node_id); context.param_and_arg_refs_stack().Push(); BeginSubpattern(context); return true; } auto HandleParseNode(Context& context, Parse::ImplicitParamListStartId node_id) -> bool { context.full_pattern_stack().StartImplicitParamList(); return HandlePatternListStart(context, node_id); } auto HandleParseNode(Context& context, Parse::TuplePatternStartId node_id) -> bool { return HandlePatternListStart(context, node_id); } auto HandleParseNode(Context& context, Parse::ExplicitParamListStartId node_id) -> bool { context.full_pattern_stack().StartExplicitParamList(); return HandlePatternListStart(context, node_id); } // Handle the end of any kind of parameter list (tuple patterns have separate // logic). static auto HandleParamListEnd(Context& context, Parse::NodeId node_id, Parse::NodeKind start_kind) -> bool { if (context.node_stack().PeekIs(start_kind)) { // End the subpattern started by a trailing comma, or the opening delimiter // of an empty list. EndSubpatternAsNonExpr(context); } // Note the Start node remains on the stack, where the param list handler can // make use of it. auto refs_id = context.param_and_arg_refs_stack().EndAndPop(start_kind); context.node_stack().Push(node_id, refs_id); return true; } auto HandleParseNode(Context& context, Parse::ImplicitParamListId node_id) -> bool { context.full_pattern_stack().EndImplicitParamList(); return HandleParamListEnd(context, node_id, Parse::NodeKind::ImplicitParamListStart); } auto HandleParseNode(Context& context, Parse::ExplicitParamListId node_id) -> bool { context.full_pattern_stack().EndExplicitParamList(); return HandleParamListEnd(context, node_id, Parse::NodeKind::ExplicitParamListStart); } auto HandleParseNode(Context& context, Parse::TuplePatternId node_id) -> bool { if (context.node_stack().PeekIs(Parse::NodeKind::TuplePatternStart)) { // End the subpattern started by a trailing comma, or the opening delimiter // of an empty list. EndSubpatternAsNonExpr(context); } auto refs_id = context.param_and_arg_refs_stack().EndAndPop( Parse::NodeKind::TuplePatternStart); context.node_stack() .PopAndDiscardSoloNodeId(); const auto& inst_block = context.inst_blocks().Get(refs_id); llvm::SmallVector type_inst_ids; type_inst_ids.reserve(inst_block.size()); for (auto inst : inst_block) { auto type_id = ExtractScrutineeType(context.sem_ir(), context.insts().Get(inst).type_id()); type_inst_ids.push_back(context.types().GetTypeInstId(type_id)); } auto type_id = GetPatternType(context, GetTupleType(context, type_inst_ids)); context.node_stack().Push( node_id, AddPatternInst( context, node_id, {.type_id = type_id, .elements_id = refs_id})); EndSubpatternAsNonExpr(context); return true; } auto HandleParseNode(Context& context, Parse::PatternListCommaId /*node_id*/) -> bool { context.param_and_arg_refs_stack().ApplyComma(); BeginSubpattern(context); return true; } } // namespace Carbon::Check ================================================ FILE: toolchain/check/handle_require.cpp ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #include "toolchain/base/kind_switch.h" #include "toolchain/check/context.h" #include "toolchain/check/convert.h" #include "toolchain/check/generic.h" #include "toolchain/check/handle.h" #include "toolchain/check/inst.h" #include "toolchain/check/modifiers.h" #include "toolchain/check/name_lookup.h" #include "toolchain/check/subst.h" #include "toolchain/check/type.h" #include "toolchain/check/type_completion.h" #include "toolchain/diagnostics/diagnostic.h" #include "toolchain/parse/node_ids.h" #include "toolchain/sem_ir/ids.h" #include "toolchain/sem_ir/named_constraint.h" #include "toolchain/sem_ir/specific_named_constraint.h" #include "toolchain/sem_ir/type_iterator.h" #include "toolchain/sem_ir/typed_insts.h" namespace Carbon::Check { auto HandleParseNode(Context& context, Parse::RequireIntroducerId node_id) -> bool { // Require decls are always generic, since everything in an `interface` or // `constraint` is generic over `Self`. StartGenericDecl(context); // Create an instruction block to hold the instructions created for the type // and constraint. context.inst_block_stack().Push(); // Optional modifiers follow. context.decl_introducer_state_stack().Push(); auto scope_id = context.scope_stack().PeekNameScopeId(); auto scope_inst_id = context.name_scopes().Get(scope_id).inst_id(); auto scope_inst = context.insts().Get(scope_inst_id); if (!scope_inst.Is() && !scope_inst.Is()) { CARBON_DIAGNOSTIC( RequireInWrongScope, Error, "`require` can only be used in an `interface` or `constraint`"); context.emitter().Emit(node_id, RequireInWrongScope); scope_inst_id = SemIR::ErrorInst::InstId; } context.node_stack().Push(node_id, scope_inst_id); return true; } auto HandleParseNode(Context& context, Parse::RequireDefaultSelfImplsId node_id) -> bool { auto scope_inst_id = context.node_stack().Peek(); if (scope_inst_id == SemIR::ErrorInst::InstId) { context.node_stack().Push(node_id, SemIR::ErrorInst::TypeInstId); return true; } auto lookup_result = LookupUnqualifiedName(context, node_id, SemIR::NameId::SelfType, /*required=*/true); auto self_inst_id = lookup_result.scope_result.target_inst_id(); auto self_type_id = context.insts().Get(self_inst_id).type_id(); if (self_type_id == SemIR::ErrorInst::TypeId) { context.node_stack().Push(node_id, SemIR::ErrorInst::TypeInstId); return true; } CARBON_CHECK(context.types().Is(self_type_id)); // TODO: We could simplify with a call to ExprAsType, like below? auto self_facet_as_type = AddTypeInst( context, node_id, {.type_id = SemIR::TypeType::TypeId, .facet_value_inst_id = self_inst_id}); context.node_stack().Push(node_id, self_facet_as_type); return true; } auto HandleParseNode(Context& context, Parse::RequireTypeImplsId node_id) -> bool { auto [self_node_id, self_inst_id] = context.node_stack().PopExprWithNodeId(); auto self_type = ExprAsType(context, self_node_id, self_inst_id); const auto& introducer = context.decl_introducer_state_stack().innermost(); if (introducer.modifier_set.HasAnyOf(KeywordModifierSet::Extend)) { if (self_type.type_id != SemIR::ErrorInst::TypeId) { CARBON_DIAGNOSTIC(RequireImplsExtendWithExplicitSelf, Error, "`extend require impls` with explicit type"); // TODO: If the explicit self-type matches a lookup of NameId::SelfType, // add a note to the diagnostic: "remove the explicit `Self` type here", // and continue without an ErrorInst. See ExtendImplSelfAsDefault. context.emitter().Emit(self_node_id, RequireImplsExtendWithExplicitSelf); } self_type.inst_id = SemIR::ErrorInst::TypeInstId; } context.node_stack().Push(node_id, self_type.inst_id); return true; } static auto TypeStructureReferencesSelf( Context& context, SemIR::LocId loc_id, SemIR::TypeInstId inst_id, const SemIR::IdentifiedFacetType& identified_facet_type) -> bool { auto find_self = [&](SemIR::TypeIterator& type_iter) -> bool { while (true) { auto step = type_iter.Next(); if (step.Is()) { break; } CARBON_KIND_SWITCH(step.any) { case CARBON_KIND(SemIR::TypeIterator::Step::Error _): { // Don't generate more diagnostics. return true; } case CARBON_KIND(SemIR::TypeIterator::Step::SymbolicBinding bind): { if (context.entity_names().Get(bind.entity_name_id).name_id == SemIR::NameId::SelfType) { return true; } break; } default: break; } } return false; }; { SemIR::TypeIterator type_iter(&context.sem_ir()); type_iter.Add(context.constant_values().GetConstantTypeInstId(inst_id)); if (find_self(type_iter)) { return true; } } if (identified_facet_type.required_impls().empty()) { CARBON_DIAGNOSTIC( RequireImplsMissingSelfEmptyFacetType, Error, "no `Self` reference found in `require` declaration; `Self` must " "appear in the self-type or as a generic argument for each required " "interface, but no interfaces were found"); context.emitter().Emit(loc_id, RequireImplsMissingSelfEmptyFacetType); return false; } bool interfaces_all_reference_self = true; for (auto [_, specific_interface] : identified_facet_type.required_impls()) { SemIR::TypeIterator type_iter(&context.sem_ir()); type_iter.Add(specific_interface); if (!find_self(type_iter)) { // TODO: The IdentifiedFacetType loses the location (since it's // canonical), but it would be nice to somehow point this diagnostic at // the particular interface in the facet type that is missing `Self`. CARBON_DIAGNOSTIC( RequireImplsMissingSelf, Error, "no `Self` reference found in `require` declaration; `Self` must " "appear in the self-type or as a generic argument for each required " "interface, but found interface `{0}` without a `Self` argument", SemIR::SpecificInterface); context.emitter().Emit(loc_id, RequireImplsMissingSelf, specific_interface); interfaces_all_reference_self = false; } } return interfaces_all_reference_self; } struct ValidateRequireResult { // The TypeId of a FacetType. SemIR::TypeId constraint_type_id; const SemIR::IdentifiedFacetType* identified_facet_type; }; // Returns nullopt if a diagnostic has been emitted and the `require` decl is // not valid. static auto ValidateRequire(Context& context, SemIR::LocId loc_id, SemIR::TypeInstId self_inst_id, SemIR::InstId constraint_inst_id, SemIR::InstId scope_inst_id) -> std::optional { auto self_constant_value_id = context.constant_values().Get(self_inst_id); auto constraint_constant_value_id = context.constant_values().Get(constraint_inst_id); if (self_constant_value_id == SemIR::ErrorInst::ConstantId || constraint_constant_value_id == SemIR::ErrorInst::ConstantId || scope_inst_id == SemIR::ErrorInst::InstId) { // An error was already diagnosed, don't diagnose another. We can't build a // useful `require` with an error, it couldn't do anything. return std::nullopt; } auto constraint_type_id = SemIR::TypeId::ForTypeConstant(constraint_constant_value_id); auto constraint_facet_type = context.types().TryGetAs(constraint_type_id); if (!constraint_facet_type) { CARBON_DIAGNOSTIC( RequireImplsMissingFacetType, Error, "`require` declaration constrained by a non-facet type; " "expected an `interface` or `constraint` name after `impls`"); context.emitter().Emit(constraint_inst_id, RequireImplsMissingFacetType); // Can't continue without a constraint to use. return std::nullopt; } if (auto named_constraint = context.insts().TryGetAs( scope_inst_id)) { const auto& constraint_facet_type_info = context.facet_types().Get(constraint_facet_type->facet_type_id); // TODO: Handle other impls named constraints for the // RequireImplsReferenceCycle diagnostic. if (constraint_facet_type_info.other_requirements) { context.TODO(constraint_inst_id, "facet type has constraints that we don't handle yet"); return std::nullopt; } auto named_constraints = llvm::concat( constraint_facet_type_info.extend_named_constraints, constraint_facet_type_info.self_impls_named_constraints); for (auto c : named_constraints) { if (c.named_constraint_id == named_constraint->named_constraint_id) { const auto& named_constraint = context.named_constraints().Get(c.named_constraint_id); CARBON_DIAGNOSTIC(RequireImplsReferenceCycle, Error, "facet type in `require` declaration refers to the " "named constraint `{0}` from within its definition", SemIR::NameId); context.emitter().Emit(constraint_inst_id, RequireImplsReferenceCycle, named_constraint.name_id); return std::nullopt; } } } auto identified_facet_type_id = RequireIdentifiedFacetType( context, SemIR::LocId(constraint_inst_id), self_constant_value_id, *constraint_facet_type, [&](auto& builder) { CARBON_DIAGNOSTIC( RequireImplsUnidentifiedFacetType, Context, "facet type {0} cannot be identified in `require` declaration", InstIdAsType); builder.Context(constraint_inst_id, RequireImplsUnidentifiedFacetType, constraint_inst_id); }); if (!identified_facet_type_id.has_value()) { // The constraint can't be used. A diagnostic was emitted by // RequireIdentifiedFacetType(). return std::nullopt; } const auto& identified = context.identified_facet_types().Get(identified_facet_type_id); if (!TypeStructureReferencesSelf(context, loc_id, self_inst_id, identified)) { return std::nullopt; } return ValidateRequireResult{.constraint_type_id = constraint_type_id, .identified_facet_type = &identified}; } auto HandleParseNode(Context& context, Parse::RequireDeclId node_id) -> bool { auto [constraint_node_id, constraint_inst_id] = context.node_stack().PopExprWithNodeId(); auto [self_node_id, self_inst_id] = context.node_stack().PopWithNodeId(); auto decl_block_id = context.inst_block_stack().Pop(); // Process modifiers. auto introducer = context.decl_introducer_state_stack().Pop(); LimitModifiersOnDecl(context, introducer, KeywordModifierSet::Extend); bool extend = introducer.modifier_set.HasAnyOf(KeywordModifierSet::Extend); auto scope_inst_id = context.node_stack().Pop(); auto validated = ValidateRequire(context, node_id, self_inst_id, constraint_inst_id, scope_inst_id); if (!validated) { // In an `extend` decl, errors get propagated into the parent scope just as // names do. if (extend) { auto scope_id = context.scope_stack().PeekNameScopeId(); context.name_scopes().Get(scope_id).set_has_error(); } DiscardGenericDecl(context); return true; } auto [constraint_type_id, identified_facet_type] = *validated; if (identified_facet_type->required_impls().empty()) { // A `require T impls type` adds no actual constraints, so nothing to do. // This is not an error though. DiscardGenericDecl(context); return true; } auto require_impls_decl = SemIR::RequireImplsDecl{// To be filled in after. .require_impls_id = SemIR::RequireImplsId::None, .decl_block_id = decl_block_id}; auto decl_id = AddPlaceholderInst(context, node_id, require_impls_decl); auto require_impls_id = context.require_impls().Add( {.self_id = self_inst_id, .facet_type_inst_id = context.types().GetAsTypeInstId(constraint_inst_id), .extend_self = extend, .decl_id = decl_id, .parent_scope_id = context.scope_stack().PeekNameScopeId(), .generic_id = BuildGenericDecl(context, decl_id)}); require_impls_decl.require_impls_id = require_impls_id; ReplaceInstBeforeConstantUse(context, decl_id, require_impls_decl); // We look for a complete type after BuildGenericDecl, so that the resulting // RequireCompleteType instruction is part of the enclosing interface or named // constraint generic definition. Then requiring enclosing entity to be // complete will resolve that definition (via ResolveSpecificDefinition()) and // also construct a specific for the `constraint_inst_id`, finding any // monomorphization errors that result. if (extend) { if (!RequireCompleteType( context, constraint_type_id, SemIR::LocId(constraint_inst_id), [&](auto& builder) { CARBON_DIAGNOSTIC(RequireImplsIncompleteFacetType, Context, "`extend require` of incomplete facet type {0}", InstIdAsType); builder.Context(constraint_inst_id, RequireImplsIncompleteFacetType, constraint_inst_id); })) { return true; } // The extended scope instruction must be part of the enclosing scope (and // generic). A specific for the enclosing scope will be applied to it when // using the instruction later. To do so, we wrap the constraint facet type // it in a SpecificConstant, which preserves the require declaration's // specific along with the facet type. // // TODO: Remove the separate generic for each require decl, then we don't // need a SpecificConstant anymore, as the constraint_inst_id will already // be in the generic of the interface-with-self. auto self_specific_id = context.generics().GetSelfSpecific( context.require_impls().Get(require_impls_id).generic_id); auto constraint_id_in_self_specific = AddTypeInst( context, node_id, {.type_id = SemIR::TypeType::TypeId, .inst_id = constraint_inst_id, .specific_id = self_specific_id}); auto enclosing_scope_id = context.scope_stack().PeekNameScopeId(); auto& enclosing_scope = context.name_scopes().Get(enclosing_scope_id); enclosing_scope.AddExtendedScope(constraint_id_in_self_specific); } context.require_impls_stack().AppendToTop(require_impls_id); return true; } } // namespace Carbon::Check ================================================ FILE: toolchain/check/handle_return_statement.cpp ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #include "toolchain/check/context.h" #include "toolchain/check/handle.h" #include "toolchain/check/return.h" namespace Carbon::Check { auto HandleParseNode(Context& context, Parse::ReturnStatementStartId node_id) -> bool { // No action, just a bracketing node. context.node_stack().Push(node_id); return true; } auto HandleParseNode(Context& context, Parse::ReturnVarModifierId node_id) -> bool { // No action, just a bracketing node. context.node_stack().Push(node_id); return true; } auto HandleParseNode(Context& context, Parse::ReturnStatementId node_id) -> bool { switch (context.node_stack().PeekNodeKind()) { case Parse::NodeKind::ReturnStatementStart: // This is a `return;` statement. context.node_stack() .PopAndDiscardSoloNodeId(); BuildReturnWithNoExpr(context, node_id); break; case Parse::NodeKind::ReturnVarModifier: // This is a `return var;` statement. context.node_stack() .PopAndDiscardSoloNodeId(); context.node_stack() .PopAndDiscardSoloNodeId(); BuildReturnVar(context, node_id); break; default: // This is a `return ;` statement. auto expr_id = context.node_stack().PopExpr(); context.node_stack() .PopAndDiscardSoloNodeId(); BuildReturnWithExpr(context, node_id, expr_id); break; } // Switch to a new, unreachable, empty instruction block. This typically won't // contain any semantics IR, but it can do if there are statements following // the `return` statement. context.inst_block_stack().Pop(); context.inst_block_stack().PushUnreachable(); return true; } } // namespace Carbon::Check ================================================ FILE: toolchain/check/handle_struct.cpp ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #include "common/map.h" #include "toolchain/check/context.h" #include "toolchain/check/convert.h" #include "toolchain/check/handle.h" #include "toolchain/check/inst.h" #include "toolchain/check/type.h" #include "toolchain/check/unused.h" #include "toolchain/diagnostics/format_providers.h" namespace Carbon::Check { auto HandleParseNode(Context& context, Parse::StructLiteralStartId node_id) -> bool { context.scope_stack().PushForSameRegion(); context.node_stack().Push(node_id); context.struct_type_fields_stack().PushArray(); context.param_and_arg_refs_stack().Push(); return true; } auto HandleParseNode(Context& context, Parse::StructTypeLiteralStartId node_id) -> bool { context.scope_stack().PushForSameRegion(); context.node_stack().Push(node_id); context.struct_type_fields_stack().PushArray(); return true; } auto HandleParseNode(Context& context, Parse::StructFieldDesignatorId /*node_id*/) -> bool { // This leaves the designated name on top because the `.` isn't interesting. CARBON_CHECK(context.node_stack().PeekIs()); return true; } auto HandleParseNode(Context& context, Parse::StructLiteralCommaId /*node_id*/) -> bool { context.param_and_arg_refs_stack().ApplyComma(); return true; } auto HandleParseNode(Context& /*context*/, Parse::StructTypeLiteralCommaId /*node_id*/) -> bool { return true; } auto HandleParseNode(Context& context, Parse::StructLiteralFieldId node_id) -> bool { auto value_inst_id = context.node_stack().PopExpr(); // Get the name while leaving it on the stack. auto name_id = context.node_stack().Peek(); // Store the name for the type. auto value_type_inst_id = context.types().GetTypeInstId( context.insts().Get(value_inst_id).type_id()); context.struct_type_fields_stack().AppendToTop( {.name_id = name_id, .type_inst_id = value_type_inst_id}); // Push the value back on the stack as an argument. context.node_stack().Push(node_id, value_inst_id); return true; } auto HandleParseNode(Context& context, Parse::StructTypeLiteralFieldId /*node_id*/) -> bool { auto [type_node, type_id] = context.node_stack().PopExprWithNodeId(); auto cast_type_inst_id = ExprAsType(context, type_node, type_id).inst_id; // Get the name while leaving it on the stack. auto name_id = context.node_stack().Peek(); context.struct_type_fields_stack().AppendToTop( {.name_id = name_id, .type_inst_id = cast_type_inst_id}); return true; } // Diagnoses and returns true if there's a duplicate name. static auto DiagnoseDuplicateNames( Context& context, llvm::ArrayRef field_name_nodes, llvm::ArrayRef fields, bool is_struct_type_literal) -> bool { Map names; for (auto [field_name_node, field] : llvm::zip_equal(field_name_nodes, fields)) { auto result = names.Insert(field.name_id, field_name_node); if (!result.is_inserted()) { CARBON_DIAGNOSTIC(StructNameDuplicate, Error, "duplicated field name `{1}` in " "{0:struct type literal|struct literal}", Diagnostics::BoolAsSelect, SemIR::NameId); CARBON_DIAGNOSTIC(StructNamePrevious, Note, "field with the same name here"); context.emitter() .Build(result.value(), StructNameDuplicate, is_struct_type_literal, field.name_id) .Note(field_name_node, StructNamePrevious) .Emit(); return true; } } return false; } // Pops the names of each field from the stack. These will have been left while // handling struct fields. static auto PopFieldNameNodes(Context& context, size_t field_count) -> llvm::SmallVector { llvm::SmallVector nodes; nodes.reserve(field_count); while (true) { auto [name_node, _] = context.node_stack().PopWithNodeIdIf(); if (name_node.has_value()) { nodes.push_back(name_node); } else { break; } } CARBON_CHECK(nodes.size() == field_count, "Found {0} names, expected {1}", nodes.size(), field_count); return nodes; } auto HandleParseNode(Context& context, Parse::StructLiteralId node_id) -> bool { if (!context.node_stack().PeekIs(Parse::NodeCategory::MemberName)) { // Remove the last parameter from the node stack before collecting names. context.param_and_arg_refs_stack().EndNoPop( Parse::NodeKind::StructLiteralStart); } auto fields = context.struct_type_fields_stack().PeekArray(); llvm::SmallVector field_name_nodes = PopFieldNameNodes(context, fields.size()); auto elements_id = context.param_and_arg_refs_stack().EndAndPop( Parse::NodeKind::StructLiteralStart); context.scope_stack().Pop(/*check_unused=*/true); context.node_stack() .PopAndDiscardSoloNodeId(); if (DiagnoseDuplicateNames(context, field_name_nodes, fields, /*is_struct_type_literal=*/false)) { context.node_stack().Push(node_id, SemIR::ErrorInst::InstId); } else { auto type_id = GetStructType( context, context.struct_type_fields().AddCanonical(fields)); auto value_id = AddInst( context, node_id, {.type_id = type_id, .elements_id = elements_id}); context.node_stack().Push(node_id, value_id); } context.struct_type_fields_stack().PopArray(); return true; } auto HandleParseNode(Context& context, Parse::StructTypeLiteralId node_id) -> bool { auto fields = context.struct_type_fields_stack().PeekArray(); llvm::SmallVector field_name_nodes = PopFieldNameNodes(context, fields.size()); context.scope_stack().Pop(/*check_unused=*/true); context.node_stack() .PopAndDiscardSoloNodeId(); if (DiagnoseDuplicateNames(context, field_name_nodes, fields, /*is_struct_type_literal=*/true)) { context.node_stack().Push(node_id, SemIR::ErrorInst::InstId); } else { auto fields_id = context.struct_type_fields().AddCanonical(fields); AddInstAndPush( context, node_id, {.type_id = SemIR::TypeType::TypeId, .fields_id = fields_id}); } context.struct_type_fields_stack().PopArray(); return true; } } // namespace Carbon::Check ================================================ FILE: toolchain/check/handle_tuple_literal.cpp ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #include "toolchain/check/context.h" #include "toolchain/check/handle.h" #include "toolchain/check/inst.h" #include "toolchain/check/type.h" namespace Carbon::Check { auto HandleParseNode(Context& context, Parse::TupleLiteralStartId node_id) -> bool { context.node_stack().Push(node_id); context.param_and_arg_refs_stack().Push(); return true; } auto HandleParseNode(Context& context, Parse::TupleLiteralCommaId /*node_id*/) -> bool { context.param_and_arg_refs_stack().ApplyComma(); return true; } auto HandleParseNode(Context& context, Parse::TupleLiteralId node_id) -> bool { auto refs_id = context.param_and_arg_refs_stack().EndAndPop( Parse::NodeKind::TupleLiteralStart); context.node_stack() .PopAndDiscardSoloNodeId(); const auto& inst_block = context.inst_blocks().Get(refs_id); llvm::SmallVector type_inst_ids; type_inst_ids.reserve(inst_block.size()); for (auto inst : inst_block) { type_inst_ids.push_back( context.types().GetTypeInstId(context.insts().Get(inst).type_id())); } auto type_id = GetTupleType(context, type_inst_ids); auto value_id = AddInst( context, node_id, {.type_id = type_id, .elements_id = refs_id}); context.node_stack().Push(node_id, value_id); return true; } } // namespace Carbon::Check ================================================ FILE: toolchain/check/handle_where.cpp ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #include "toolchain/check/context.h" #include "toolchain/check/convert.h" #include "toolchain/check/facet_type.h" #include "toolchain/check/generic.h" #include "toolchain/check/handle.h" #include "toolchain/check/inst.h" #include "toolchain/check/type.h" #include "toolchain/check/unused.h" #include "toolchain/sem_ir/facet_type_info.h" #include "toolchain/sem_ir/ids.h" #include "toolchain/sem_ir/inst.h" #include "toolchain/sem_ir/typed_insts.h" namespace Carbon::Check { auto HandleParseNode(Context& context, Parse::WhereOperandId node_id) -> bool { // The expression at the top of the stack represents a constraint type that // is being modified by the `where` operator. It would be `MyInterface` in // `MyInterface where .Member = i32`. auto [self_node, self_id] = context.node_stack().PopExprWithNodeId(); auto self_with_constraints_type_id = ExprAsType(context, self_node, self_id).type_id; // Only facet types may have `where` restrictions. if (!context.types().IsFacetTypeOrError(self_with_constraints_type_id)) { CARBON_DIAGNOSTIC(WhereOnNonFacetType, Error, "left argument of `where` operator must be a facet type"); context.emitter().Emit(self_node, WhereOnNonFacetType); self_with_constraints_type_id = SemIR::ErrorInst::TypeId; } // Strip off any constraints provided by a `WhereExpr` from the `Self` facet // type. For a facet type like `I & J where .X = .Y`, this will reduce it down // to just `I & J`. // // Any references to `.Self` in constraints for the current `WhereExpr` will // not see constraints in the `Self` facet type, but they will resolve to // values through the constraints explicitly when they are combined together. auto self_without_constraints_type_id = self_with_constraints_type_id; if (auto facet_type = context.types().TryGetAs( self_without_constraints_type_id)) { const auto& info = context.facet_types().Get(facet_type->facet_type_id); auto stripped_info = SemIR::FacetTypeInfo{.extend_constraints = info.extend_constraints}; stripped_info.Canonicalize(); self_without_constraints_type_id = GetFacetType(context, stripped_info); } // Introduce a name scope so that we can remove the `.Self` entry we are // adding to name lookup at the end of the `where` expression. context.scope_stack().PushForSameRegion(); // Introduce `.Self` as a symbolic binding. Its type is the value of the // expression to the left of `where`, so `MyInterface` in the example above. auto period_self_inst_id = MakePeriodSelfFacetValue(context, self_without_constraints_type_id); // Save the `.Self` symbolic binding on the node stack. It will become the // first argument to the `WhereExpr` instruction. context.node_stack().Push(node_id, period_self_inst_id); // Going to put each requirement on `args_type_info_stack`, so we can have an // inst block with the varying number of requirements but keeping other // instructions on the current inst block from the `inst_block_stack()`. context.args_type_info_stack().Push(); // Pass along all the constraints from the base facet type to be added to the // resulting facet type. context.args_type_info_stack().AddInstId( AddInstInNoBlock( context, SemIR::LocId(node_id), {.base_type_inst_id = context.types().GetTypeInstId(self_with_constraints_type_id)})); // Add a context stack for tracking rewrite constraints, that will be used to // allow later constraints to read from them eagerly. context.rewrites_stack().emplace_back(); // Make rewrite constraints from the self facet type available immediately to // expressions in rewrite constraints for this `where` expression. if (auto self_facet_type = context.types().TryGetAs( self_with_constraints_type_id)) { const auto& base_facet_type_info = context.facet_types().Get(self_facet_type->facet_type_id); for (const auto& rewrite : base_facet_type_info.rewrite_constraints) { if (rewrite.lhs_id != SemIR::ErrorInst::InstId) { context.rewrites_stack().back().Insert( context.constant_values().Get( GetImplWitnessAccessWithoutSubstitution(context, rewrite.lhs_id)), rewrite.rhs_id); } } } return true; } auto HandleParseNode(Context& context, Parse::RequirementEqualId node_id) -> bool { auto [rhs_node, rhs_id] = context.node_stack().PopExprWithNodeId(); auto lhs_id = context.node_stack().PopExpr(); // Convert rhs to type of lhs. auto lhs_type_id = context.insts().Get(lhs_id).type_id(); if (lhs_type_id.is_symbolic()) { // If the type of the associated constant is symbolic, we defer conversion // until the constraint is resolved, in case it depends on `Self` (which // will now be a reference to `.Self`). // For now we convert to a value expression eagerly because otherwise we'll // often be unable to constant-evaluate the enclosing `where` expression. // TODO: Perform the conversion symbolically and add an implicit constraint // that this conversion is valid and produces a constant. rhs_id = ConvertToValueExpr(context, rhs_id); } else { rhs_id = ConvertToValueOfType(context, rhs_node, rhs_id, context.insts().Get(lhs_id).type_id()); } // Build up the list of arguments for the `WhereExpr` inst. context.args_type_info_stack().AddInstId( AddInstInNoBlock( context, node_id, {.lhs_id = lhs_id, .rhs_id = rhs_id})); if (lhs_id != SemIR::ErrorInst::InstId) { // Track the value of the rewrite so further constraints can use it // immediately, before they are evaluated. This happens directly where the // `ImplWitnessAccess` that refers to the rewrite constraint would have been // created, and the value of the constraint will be used instead. context.rewrites_stack().back().Insert( context.constant_values().Get( GetImplWitnessAccessWithoutSubstitution(context, lhs_id)), rhs_id); } return true; } auto HandleParseNode(Context& context, Parse::RequirementEqualEqualId node_id) -> bool { auto rhs = context.node_stack().PopExpr(); auto lhs = context.node_stack().PopExpr(); // TODO: Type check lhs and rhs are comparable. // TODO: Require that at least one side uses a designator. // Build up the list of arguments for the `WhereExpr` inst. context.args_type_info_stack().AddInstId( AddInstInNoBlock( context, node_id, {.lhs_id = lhs, .rhs_id = rhs})); return true; } auto HandleParseNode(Context& context, Parse::RequirementImplsId node_id) -> bool { auto [rhs_node, rhs_id] = context.node_stack().PopExprWithNodeId(); auto [lhs_node, lhs_id] = context.node_stack().PopExprWithNodeId(); // Check lhs is a facet and rhs is a facet type. auto lhs_as_type = ExprAsType(context, lhs_node, lhs_id); auto rhs_as_type = ExprAsType(context, rhs_node, rhs_id); if (rhs_as_type.type_id != SemIR::ErrorInst::TypeId && !context.types().IsFacetType(rhs_as_type.type_id)) { CARBON_DIAGNOSTIC( ImplsOnNonFacetType, Error, "right argument of `impls` requirement must be a facet type"); context.emitter().Emit(rhs_node, ImplsOnNonFacetType); rhs_as_type.inst_id = SemIR::ErrorInst::TypeInstId; } // TODO: Require that at least one side uses a designator. // TODO: For things like `HashSet(.T) as type`, add an implied constraint // that `.T impls Hash`. // Build up the list of arguments for the `WhereExpr` inst. context.args_type_info_stack().AddInstId( AddInstInNoBlock( context, node_id, {.lhs_id = lhs_as_type.inst_id, .rhs_id = rhs_as_type.inst_id})); return true; } auto HandleParseNode(Context& /*context*/, Parse::RequirementAndId /*node_id*/) -> bool { // Nothing to do. return true; } auto HandleParseNode(Context& context, Parse::WhereExprId node_id) -> bool { context.rewrites_stack().pop_back(); // Remove `PeriodSelf` from name lookup, undoing the `Push` done for the // `WhereOperand`. context.scope_stack().Pop(/*check_unused=*/true); SemIR::InstId period_self_id = context.node_stack().Pop(); SemIR::InstBlockId requirements_id = context.args_type_info_stack().Pop(); AddInstAndPush(context, node_id, {.type_id = SemIR::TypeType::TypeId, .period_self_id = period_self_id, .requirements_id = requirements_id}); return true; } } // namespace Carbon::Check ================================================ FILE: toolchain/check/impl.cpp ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #include "toolchain/check/impl.h" #include "toolchain/base/kind_switch.h" #include "toolchain/check/context.h" #include "toolchain/check/convert.h" #include "toolchain/check/deduce.h" #include "toolchain/check/eval.h" #include "toolchain/check/facet_type.h" #include "toolchain/check/function.h" #include "toolchain/check/generic.h" #include "toolchain/check/impl_lookup.h" #include "toolchain/check/import_ref.h" #include "toolchain/check/inst.h" #include "toolchain/check/interface.h" #include "toolchain/check/merge.h" #include "toolchain/check/name_lookup.h" #include "toolchain/check/name_scope.h" #include "toolchain/check/thunk.h" #include "toolchain/check/type.h" #include "toolchain/check/type_completion.h" #include "toolchain/check/type_structure.h" #include "toolchain/diagnostics/emitter.h" #include "toolchain/sem_ir/generic.h" #include "toolchain/sem_ir/ids.h" #include "toolchain/sem_ir/impl.h" #include "toolchain/sem_ir/inst.h" #include "toolchain/sem_ir/typed_insts.h" namespace Carbon::Check { // Adds the location of the associated function to a diagnostic. static auto NoteAssociatedFunction(Context& context, DiagnosticBuilder& builder, SemIR::FunctionId function_id) -> void { CARBON_DIAGNOSTIC(AssociatedFunctionHere, Note, "associated function {0} declared here", SemIR::NameId); const auto& function = context.functions().Get(function_id); builder.Note(function.latest_decl_id(), AssociatedFunctionHere, function.name_id); } auto CheckAssociatedFunctionImplementation( Context& context, SemIR::FunctionType interface_function_type, SemIR::SpecificId enclosing_specific_id, SemIR::InstId impl_decl_id, bool defer_thunk_definition) -> SemIR::InstId { auto impl_function_decl = context.insts().TryGetAs(impl_decl_id); if (!impl_function_decl) { if (impl_decl_id != SemIR::ErrorInst::InstId) { CARBON_DIAGNOSTIC(ImplFunctionWithNonFunction, Error, "associated function {0} implemented by non-function", SemIR::NameId); auto builder = context.emitter().Build( impl_decl_id, ImplFunctionWithNonFunction, context.functions().Get(interface_function_type.function_id).name_id); NoteAssociatedFunction(context, builder, interface_function_type.function_id); builder.Emit(); } return SemIR::ErrorInst::InstId; } // Map from the specific for the function type to the specific for the // function signature. The function signature may have additional generic // parameters. auto interface_function_specific_id = GetSelfSpecificForInterfaceMemberWithSelfType( context, SemIR::LocId(impl_decl_id), interface_function_type.specific_id, context.functions() .Get(interface_function_type.function_id) .generic_id, enclosing_specific_id); return BuildThunk(context, interface_function_type.function_id, interface_function_specific_id, impl_decl_id, defer_thunk_definition); } // Returns true if impl redeclaration parameters match. static auto CheckImplRedeclParamsMatch(Context& context, const SemIR::Impl& new_impl, SemIR::ImplId prev_impl_id) -> bool { auto& prev_impl = context.impls().Get(prev_impl_id); // If the parameters aren't the same, then this is not a redeclaration of this // `impl`. Keep looking for a prior declaration without issuing a diagnostic. if (!CheckRedeclParamsMatch(context, DeclParams(new_impl), DeclParams(prev_impl), SemIR::SpecificId::None, /*diagnose=*/false, /*check_syntax=*/true, /*check_self=*/true)) { // NOLINTNEXTLINE(readability-simplify-boolean-expr) return false; } return true; } // Returns whether an impl can be redeclared. For example, defined impls // cannot be redeclared. static auto IsValidImplRedecl(Context& context, const SemIR::Impl& new_impl, SemIR::ImplId prev_impl_id) -> bool { auto& prev_impl = context.impls().Get(prev_impl_id); // TODO: Following #3763, disallow redeclarations in different scopes. // Following #4672, disallowing defining non-extern declarations in another // file. if (auto import_ref = context.insts().TryGetAs(prev_impl.self_id)) { // TODO: Handle extern. CARBON_DIAGNOSTIC(RedeclImportedImpl, Error, "redeclaration of imported impl"); // TODO: Note imported declaration context.emitter().Emit(new_impl.latest_decl_id(), RedeclImportedImpl); return false; } if (prev_impl.has_definition_started()) { // Impls aren't merged in order to avoid generic region lookup into a // mismatching table. CARBON_DIAGNOSTIC(ImplRedefinition, Error, "redefinition of `impl {0} as {1}`", InstIdAsRawType, InstIdAsRawType); CARBON_DIAGNOSTIC(ImplPreviousDefinition, Note, "previous definition was here"); context.emitter() .Build(new_impl.latest_decl_id(), ImplRedefinition, new_impl.self_id, new_impl.constraint_id) .Note(prev_impl.definition_id, ImplPreviousDefinition) .Emit(); return false; } // TODO: Only allow redeclaration in a match_first/impl_priority block. return true; } // Looks for any unused generic bindings. If one is found, it is diagnosed and // false is returned. static auto VerifyAllGenericBindingsUsed(Context& context, SemIR::LocId loc_id, SemIR::LocId implicit_params_loc_id, SemIR::Impl& impl) -> bool { if (impl.witness_id == SemIR::ErrorInst::InstId) { return true; } if (!impl.generic_id.has_value()) { return true; } if (impl.implicit_param_patterns_id.has_value()) { for (auto inst_id : context.inst_blocks().Get(impl.implicit_param_patterns_id)) { if (inst_id == SemIR::ErrorInst::InstId) { // An error was already diagnosed for a generic binding. return true; } } } auto deduced_specific_id = DeduceImplArguments( context, loc_id, impl, context.constant_values().Get(impl.self_id), impl.interface.specific_id); if (deduced_specific_id.has_value()) { // Deduction succeeded, all bindings were used. return true; } CARBON_DIAGNOSTIC(ImplUnusedBinding, Error, "`impl` with unused generic binding"); // TODO: This location may be incorrect, the binding may be inherited // from an outer declaration. It would be nice to get the particular // binding that was undeducible back from DeduceImplArguments here and // use that. auto diag_loc_id = implicit_params_loc_id.has_value() ? implicit_params_loc_id : loc_id; context.emitter().Emit(diag_loc_id, ImplUnusedBinding); return false; } // Apply an `extend impl` declaration by extending the parent scope with the // `impl`. If there's an error it is diagnosed and false is returned. static auto ApplyExtendImplAs(Context& context, SemIR::LocId loc_id, const SemIR::Impl& impl, Parse::NodeId extend_node, SemIR::LocId implicit_params_loc_id) -> bool { auto parent_scope_id = context.decl_name_stack().PeekParentScopeId(); // TODO: Also handle the parent scope being a mixin. auto class_scope = TryAsClassScope(context, parent_scope_id); if (!class_scope) { if (impl.witness_id != SemIR::ErrorInst::InstId) { CARBON_DIAGNOSTIC( ExtendImplOutsideClass, Error, "`extend impl` can only be used in an interface or class"); context.emitter().Emit(loc_id, ExtendImplOutsideClass); } return false; } auto& parent_scope = *class_scope->name_scope; // An error was already diagnosed, but this is `extend impl as` inside a // class, so propagate the error into the enclosing class scope. if (impl.witness_id == SemIR::ErrorInst::InstId) { parent_scope.set_has_error(); return false; } if (implicit_params_loc_id.has_value()) { CARBON_DIAGNOSTIC(ExtendImplForall, Error, "cannot `extend` a parameterized `impl`"); context.emitter().Emit(extend_node, ExtendImplForall); parent_scope.set_has_error(); return false; } if (!RequireCompleteType( context, context.types().GetTypeIdForTypeInstId(impl.constraint_id), SemIR::LocId(impl.constraint_id), [&](auto& builder) { CARBON_DIAGNOSTIC(ExtendImplAsIncomplete, Context, "`extend impl as` incomplete facet type {0}", InstIdAsType); builder.Context(impl.latest_decl_id(), ExtendImplAsIncomplete, impl.constraint_id); })) { parent_scope.set_has_error(); return false; } if (!impl.generic_id.has_value()) { parent_scope.AddExtendedScope(impl.constraint_id); } else { // The extended scope instruction must be part of the enclosing scope (and // generic). A specific for the enclosing scope will be applied to it when // using the instruction later. To do so, we wrap the constraint facet type // it in a SpecificConstant, which preserves the impl declaration's // specific along with the facet type. auto constraint_id_in_self_specific = AddTypeInst( context, SemIR::LocId(impl.constraint_id), {.type_id = SemIR::TypeType::TypeId, .inst_id = impl.constraint_id, .specific_id = context.generics().GetSelfSpecific(impl.generic_id)}); parent_scope.AddExtendedScope(constraint_id_in_self_specific); } return true; } auto FindImplId(Context& context, const SemIR::Impl& query_impl) -> std::variant { // Look for an existing matching declaration. auto lookup_bucket_ref = context.impls().GetOrAddLookupBucket(query_impl); // TODO: Detect two impl declarations with the same self type and interface, // and issue an error if they don't match. for (auto prev_impl_id : lookup_bucket_ref) { if (CheckImplRedeclParamsMatch(context, query_impl, prev_impl_id)) { if (IsValidImplRedecl(context, query_impl, prev_impl_id)) { return RedeclaredImpl{.prev_impl_id = prev_impl_id}; } else { // IsValidImplRedecl() has issued a diagnostic, take care to avoid // generating more diagnostics for this declaration. return NewImpl{.lookup_bucket = lookup_bucket_ref, .find_had_error = true}; } break; } } return NewImpl{.lookup_bucket = lookup_bucket_ref, .find_had_error = false}; } // Sets the `ImplId` in the `ImplWitnessTable`. static auto AssignImplIdInWitness(Context& context, SemIR::ImplId impl_id, SemIR::InstId witness_id) -> void { if (witness_id == SemIR::ErrorInst::InstId) { return; } auto witness = context.insts().GetAs(witness_id); auto witness_table = context.insts().GetAs(witness.witness_table_id); witness_table.impl_id = impl_id; // Note: The `ImplWitnessTable` instruction is `Unique`, so while this marks // the instruction as being a dependent instruction of a generic impl, it will // not be substituted into the eval block. ReplaceInstBeforeConstantUse(context, witness.witness_table_id, witness_table); } auto AddImpl(Context& context, const SemIR::Impl& impl, SemIR::ImplStore::LookupBucketRef lookup_bucket, Parse::NodeId extend_node, SemIR::LocId implicit_params_loc_id) -> SemIR::ImplId { auto impl_decl_id = impl.latest_decl_id(); // From here on, use the `Impl` from the `ImplStore` instead of `impl` in // order to make and see any changes to the `Impl`. auto impl_id = context.impls().Add(impl); lookup_bucket.push_back(impl_id); AssignImplIdInWitness(context, impl_id, impl.witness_id); auto& stored_impl = context.impls().Get(impl_id); // Look to see if there are any generic bindings on the `impl` declaration // that are not deducible. If so, and the `impl` does not actually use all // its generic bindings, and will never be matched. This should be // diagnossed to the user. if (!VerifyAllGenericBindingsUsed(context, SemIR::LocId(impl_decl_id), implicit_params_loc_id, stored_impl)) { FillImplWitnessWithErrors(context, stored_impl); } if (extend_node.has_value()) { if (!ApplyExtendImplAs(context, SemIR::LocId(impl_decl_id), stored_impl, extend_node, implicit_params_loc_id)) { FillImplWitnessWithErrors(context, stored_impl); } } return impl_id; } // Returns whether the `LookupImplWitness` of `witness_id` matches `interface`. static auto WitnessQueryMatchesInterface( Context& context, SemIR::InstId witness_id, const SemIR::SpecificInterface& interface) -> bool { auto lookup = context.insts().GetAs(witness_id); return interface == context.specific_interfaces().Get(lookup.query_specific_interface_id); } auto AddImplWitnessForDeclaration(Context& context, SemIR::LocId loc_id, const SemIR::Impl& impl, SemIR::SpecificId self_specific_id) -> SemIR::InstId { auto facet_type_id = context.types().GetTypeIdForTypeInstId(impl.constraint_id); CARBON_CHECK(facet_type_id != SemIR::ErrorInst::TypeId); auto facet_type = context.types().GetAs(facet_type_id); const auto& facet_type_info = context.facet_types().Get(facet_type.facet_type_id); // An iterator over the rewrite_constraints where the LHS of the rewrite names // a member of the `impl.interface`. This filters out rewrites of names // from other interfaces, as they do not set values in the witness table. auto rewrites_into_interface_to_witness = llvm::make_filter_range( facet_type_info.rewrite_constraints, [&](const SemIR::FacetTypeInfo::RewriteConstraint& rewrite) { auto access = context.insts().GetAs( GetImplWitnessAccessWithoutSubstitution(context, rewrite.lhs_id)); return WitnessQueryMatchesInterface(context, access.witness_id, impl.interface); }); if (rewrites_into_interface_to_witness.empty()) { // The witness table is not needed until the definition. Make a placeholder // for the declaration. auto witness_table_inst_id = AddInst( context, loc_id, {.elements_id = context.inst_blocks().AddPlaceholder(), .impl_id = SemIR::ImplId::None}); return AddInst( context, loc_id, {.type_id = GetSingletonType(context, SemIR::WitnessType::TypeInstId), .witness_table_id = witness_table_inst_id, .specific_id = self_specific_id}); } const auto& interface = context.interfaces().Get(impl.interface.interface_id); if (!interface.is_complete()) { // This is a declaration with rewrite constraints into `.Self`, but the // interface is not complete. Those rewrites have already been diagnosed as // an error in their member access. return SemIR::ErrorInst::InstId; } auto assoc_entities = context.inst_blocks().Get(interface.associated_entities_id); // TODO: When this function is used for things other than just impls, may want // to only load the specific associated entities that are mentioned in rewrite // rules. for (auto decl_id : assoc_entities) { LoadImportRef(context, decl_id); } SemIR::InstId witness_inst_id = SemIR::InstId::None; llvm::MutableArrayRef table; { auto elements_id = context.inst_blocks().AddUninitialized(assoc_entities.size()); table = context.inst_blocks().GetMutable(elements_id); for (auto& uninit : table) { uninit = SemIR::InstId::ImplWitnessTablePlaceholder; } auto witness_table_inst_id = AddInst( context, loc_id, {.elements_id = elements_id, .impl_id = SemIR::ImplId::None}); witness_inst_id = AddInst( context, loc_id, {.type_id = GetSingletonType(context, SemIR::WitnessType::TypeInstId), .witness_table_id = witness_table_inst_id, .specific_id = self_specific_id}); } for (auto rewrite : rewrites_into_interface_to_witness) { auto access = context.insts().GetAs( GetImplWitnessAccessWithoutSubstitution(context, rewrite.lhs_id)); auto& table_entry = table[access.index.index]; if (table_entry == SemIR::ErrorInst::InstId) { // Don't overwrite an error value. This prioritizes not generating // multiple errors for one associated constant over picking a value // for it to use to attempt recovery. continue; } auto rewrite_inst_id = rewrite.rhs_id; if (rewrite_inst_id == SemIR::ErrorInst::InstId) { table_entry = SemIR::ErrorInst::InstId; continue; } auto decl_id = context.constant_values().GetConstantInstId( assoc_entities[access.index.index]); CARBON_CHECK(decl_id.has_value(), "Non-constant associated entity"); if (decl_id == SemIR::ErrorInst::InstId) { table_entry = SemIR::ErrorInst::InstId; continue; } auto assoc_constant_decl = context.insts().TryGetAs(decl_id); if (!assoc_constant_decl) { auto type_id = context.insts().Get(decl_id).type_id(); auto type_inst = context.types().GetAsInst(type_id); auto fn_type = type_inst.As(); const auto& fn = context.functions().Get(fn_type.function_id); CARBON_DIAGNOSTIC(RewriteForAssociatedFunction, Error, "rewrite specified for associated function {0}", SemIR::NameId); context.emitter().Emit(impl.constraint_id, RewriteForAssociatedFunction, fn.name_id); table_entry = SemIR::ErrorInst::InstId; continue; } // FacetTypes resolution disallows two rewrites to the same associated // constant, so we won't ever have a facet write twice to the same position // in the witness table. CARBON_CHECK(table_entry == SemIR::InstId::ImplWitnessTablePlaceholder); // If the associated constant has a symbolic type, convert the rewrite // value to that type now we know the value of `Self`. SemIR::TypeId assoc_const_type_id = assoc_constant_decl->type_id; if (assoc_const_type_id.is_symbolic()) { auto self_facet = GetConstantFacetValueForType(context, impl.self_id); auto interface_with_self_specific_id = MakeSpecificWithInnerSelf( context, loc_id, interface.generic_id, interface.generic_with_self_id, impl.interface.specific_id, self_facet); // Get the type of the associated constant in this interface with this // value for `Self`. assoc_const_type_id = GetTypeForSpecificAssociatedEntity( context, interface_with_self_specific_id, decl_id); // Perform the conversion of the value to the type. We skipped this when // forming the facet type because the type of the associated constant // was symbolic. auto converted_inst_id = ConvertToValueOfType(context, SemIR::LocId(impl.constraint_id), rewrite_inst_id, assoc_const_type_id); // Canonicalize the converted constant value. converted_inst_id = context.constant_values().GetConstantInstId(converted_inst_id); // The result of conversion can be non-constant even if the original // value was constant. if (converted_inst_id.has_value()) { rewrite_inst_id = converted_inst_id; } else { const auto& assoc_const = context.associated_constants().Get( assoc_constant_decl->assoc_const_id); CARBON_DIAGNOSTIC( AssociatedConstantNotConstantAfterConversion, Error, "associated constant {0} given value {1} that is not constant " "after conversion to {2}", SemIR::NameId, InstIdAsConstant, SemIR::TypeId); context.emitter().Emit( impl.constraint_id, AssociatedConstantNotConstantAfterConversion, assoc_const.name_id, rewrite_inst_id, assoc_const_type_id); rewrite_inst_id = SemIR::ErrorInst::InstId; } } CARBON_CHECK(rewrite_inst_id == context.constant_values().GetConstantInstId( rewrite_inst_id), "Rewritten value for associated constant is not canonical."); table_entry = AddInst( context, loc_id, {.type_id = context.insts().Get(rewrite_inst_id).type_id(), .inst_id = rewrite_inst_id}); } return witness_inst_id; } auto ImplWitnessStartDefinition(Context& context, SemIR::Impl& impl) -> void { CARBON_CHECK(impl.is_being_defined()); CARBON_CHECK(impl.witness_id.has_value()); if (impl.witness_id == SemIR::ErrorInst::InstId) { return; } { if (!RequireCompleteType( context, context.types().GetTypeIdForTypeInstId(impl.constraint_id), SemIR::LocId(impl.constraint_id), [&](auto& builder) { CARBON_DIAGNOSTIC( ImplAsIncompleteFacetTypeDefinition, Context, "definition of impl as incomplete facet type {0}", InstIdAsType); builder.Context(SemIR::LocId(impl.latest_decl_id()), ImplAsIncompleteFacetTypeDefinition, impl.constraint_id); })) { FillImplWitnessWithErrors(context, impl); return; } } const auto& interface = context.interfaces().Get(impl.interface.interface_id); auto assoc_entities = context.inst_blocks().Get(interface.associated_entities_id); for (auto decl_id : assoc_entities) { LoadImportRef(context, decl_id); } auto witness = context.insts().GetAs(impl.witness_id); auto witness_table = context.insts().GetAs(witness.witness_table_id); auto witness_block = context.inst_blocks().GetMutable(witness_table.elements_id); // The impl declaration may have created a placeholder witness table, or a // full witness table. We can detect that the witness table is a placeholder // table if it's not the `Empty` id, but it is empty still. If it was a // placeholder, we can replace the placeholder here with a table of the proper // size, since the interface must be complete for the impl definition. bool witness_table_is_placeholder = witness_table.elements_id != SemIR::InstBlockId::Empty && witness_block.empty(); if (witness_table_is_placeholder) { // TODO: Since our `empty_table` repeats the same value throughout, we could // skip an allocation here if there was a `ReplacePlaceholder` function that // took a size and value instead of an array of values. llvm::SmallVector empty_table( assoc_entities.size(), SemIR::InstId::ImplWitnessTablePlaceholder); context.inst_blocks().ReplacePlaceholder(witness_table.elements_id, empty_table); witness_block = context.inst_blocks().GetMutable(witness_table.elements_id); } // Check we have a value for all non-function associated constants in the // witness. for (auto [assoc_entity, witness_value] : llvm::zip_equal(assoc_entities, witness_block)) { auto decl_id = context.constant_values().GetConstantInstId(assoc_entity); CARBON_CHECK(decl_id.has_value(), "Non-constant associated entity"); if (auto decl = context.insts().TryGetAs(decl_id)) { if (witness_value == SemIR::InstId::ImplWitnessTablePlaceholder) { CARBON_DIAGNOSTIC(ImplAssociatedConstantNeedsValue, Error, "associated constant {0} not given a value in impl " "of interface {1}", SemIR::NameId, SemIR::NameId); CARBON_DIAGNOSTIC(AssociatedConstantHere, Note, "associated constant declared here"); context.emitter() .Build(impl.definition_id, ImplAssociatedConstantNeedsValue, context.associated_constants() .Get(decl->assoc_const_id) .name_id, interface.name_id) .Note(assoc_entity, AssociatedConstantHere) .Emit(); witness_value = SemIR::ErrorInst::InstId; } } } } // Adds functions to the witness that the specified impl implements the given // interface. auto FinishImplWitness(Context& context, const SemIR::Impl& impl) -> void { CARBON_CHECK(impl.is_being_defined()); CARBON_CHECK(impl.witness_id.has_value()); if (impl.witness_id == SemIR::ErrorInst::InstId) { return; } auto witness = context.insts().GetAs(impl.witness_id); auto witness_table = context.insts().GetAs(witness.witness_table_id); auto witness_block = context.inst_blocks().GetMutable(witness_table.elements_id); auto& impl_scope = context.name_scopes().Get(impl.scope_id); const auto& interface = context.interfaces().Get(impl.interface.interface_id); auto assoc_entities = context.inst_blocks().Get(interface.associated_entities_id); llvm::SmallVector used_decl_ids; auto self_facet = GetConstantFacetValueForTypeAndInterface( context, impl.self_id, impl.interface, impl.witness_id); auto interface_with_self_specific_id = MakeSpecificWithInnerSelf( context, SemIR::LocId(impl.definition_id), interface.generic_id, interface.generic_with_self_id, impl.interface.specific_id, self_facet); for (auto [assoc_entity, witness_value] : llvm::zip_equal(assoc_entities, witness_block)) { auto decl_id = context.constant_values().GetInstId(SemIR::GetConstantValueInSpecific( context.sem_ir(), interface_with_self_specific_id, assoc_entity)); CARBON_CHECK(decl_id.has_value(), "Non-constant associated entity"); auto decl = context.insts().Get(decl_id); CARBON_KIND_SWITCH(decl) { case CARBON_KIND(SemIR::StructValue struct_value): { if (struct_value.type_id == SemIR::ErrorInst::TypeId) { witness_value = SemIR::ErrorInst::InstId; break; } auto type_inst = context.types().GetAsInst(struct_value.type_id); auto fn_type = type_inst.TryAs(); if (!fn_type) { CARBON_FATAL("Unexpected type: {0}", type_inst); } auto& fn = context.functions().Get(fn_type->function_id); auto lookup_result = LookupNameInExactScope(context, SemIR::LocId(decl_id), fn.name_id, impl.scope_id, impl_scope); if (lookup_result.is_found()) { used_decl_ids.push_back(lookup_result.target_inst_id()); witness_value = CheckAssociatedFunctionImplementation( context, *fn_type, context.generics().GetSelfSpecific(impl.generic_id), lookup_result.target_inst_id(), /*defer_thunk_definition=*/true); } else { CARBON_DIAGNOSTIC( ImplMissingFunction, Error, "missing implementation of {0} in impl of interface {1}", SemIR::NameId, SemIR::NameId); auto builder = context.emitter().Build(impl.definition_id, ImplMissingFunction, fn.name_id, interface.name_id); NoteAssociatedFunction(context, builder, fn_type->function_id); builder.Emit(); witness_value = SemIR::ErrorInst::InstId; } break; } case SemIR::AssociatedConstantDecl::Kind: { // These are set to their final values already. break; } default: CARBON_CHECK(decl_id == SemIR::ErrorInst::InstId, "Unexpected kind of associated entity {0}", decl); witness_value = SemIR::ErrorInst::InstId; break; } } // TODO: Diagnose if any declarations in the impl are not in used_decl_ids. } auto CheckRequireDeclsSatisfied(Context& context, SemIR::LocId loc_id, SemIR::Impl& impl) -> void { if (impl.witness_id == SemIR::ErrorInst::InstId) { return; } const auto& interface = context.interfaces().Get(impl.interface.interface_id); if (!interface.is_complete()) { // This will be diagnosed later. We check for required decls before starting // the definition to avoid inserting these lookups into the definition, as // the lookups can end up looking for the impl being defined, which creates // a cycle. return; } auto require_ids = context.require_impls_blocks().Get(interface.require_impls_block_id); if (require_ids.empty()) { return; } // Make a facet value for the self type. auto self_facet = GetConstantFacetValueForType(context, impl.self_id); auto interface_with_self_specific_id = MakeSpecificWithInnerSelf( context, loc_id, interface.generic_id, interface.generic_with_self_id, impl.interface.specific_id, self_facet); for (auto require_id : require_ids) { const auto& require = context.require_impls().Get(require_id); // Each require is in its own generic, with no additional bindings and no // definition, so that they can have their specifics independently // instantiated. auto require_specific_id = CopySpecificToGeneric( context, SemIR::LocId(require.decl_id), interface_with_self_specific_id, require.generic_id); auto self_const_id = GetConstantValueInSpecific( context.sem_ir(), require_specific_id, require.self_id); auto facet_type_const_id = GetConstantValueInSpecific( context.sem_ir(), require_specific_id, require.facet_type_inst_id); if (self_const_id == SemIR::ErrorInst::ConstantId || facet_type_const_id == SemIR::ErrorInst::ConstantId) { FillImplWitnessWithErrors(context, impl); break; } auto result = LookupImplWitness(context, loc_id, self_const_id, facet_type_const_id); // TODO: If the facet type contains 2 interfaces, and one is not `impl`ed, // it would be nice to diagnose which one was not `impl`ed, but that // requires LookupImplWitness to return a partial result, or take a // diagnostic lambda or something. if (!result.has_value()) { auto facet_type_inst_id = context.constant_values().GetInstId(facet_type_const_id); if (!result.has_error_value() && facet_type_inst_id != SemIR::ErrorInst::InstId) { CARBON_DIAGNOSTIC(RequireImplsNotImplemented, Error, "interface `{0}` being implemented requires that {1} " "implements {2}", SemIR::SpecificInterface, SemIR::TypeId, SemIR::FacetTypeId); context.emitter().Emit( loc_id, RequireImplsNotImplemented, impl.interface, context.types().GetTypeIdForTypeConstantId(self_const_id), context.insts() .GetAs(facet_type_inst_id) .facet_type_id); } } if (!result.has_value() || result.has_error_value()) { FillImplWitnessWithErrors(context, impl); break; } } } auto FillImplWitnessWithErrors(Context& context, SemIR::Impl& impl) -> void { if (impl.witness_id == SemIR::ErrorInst::InstId) { return; } auto witness = context.insts().GetAs(impl.witness_id); auto witness_table = context.insts().GetAs(witness.witness_table_id); auto witness_block = context.inst_blocks().GetMutable(witness_table.elements_id); for (auto& elem : witness_block) { if (elem == SemIR::InstId::ImplWitnessTablePlaceholder) { elem = SemIR::ErrorInst::InstId; } } impl.witness_id = SemIR::ErrorInst::InstId; } auto IsImplEffectivelyFinal(Context& context, const SemIR::Impl& impl) -> bool { return impl.is_final || (context.constant_values().Get(impl.self_id).is_concrete() && context.constant_values().Get(impl.constraint_id).is_concrete()); } auto CheckConstraintIsInterface(Context& context, SemIR::InstId impl_decl_id, SemIR::InstId self_id, SemIR::TypeInstId constraint_id) -> SemIR::SpecificInterface { auto facet_type_as_type_id = context.types().GetTypeIdForTypeInstId(constraint_id); if (facet_type_as_type_id == SemIR::ErrorInst::TypeId) { return SemIR::SpecificInterface::None; } auto facet_type = context.types().TryGetAs(facet_type_as_type_id); if (!facet_type) { CARBON_DIAGNOSTIC(ImplAsNonFacetType, Error, "impl as non-facet type {0}", InstIdAsType); context.emitter().Emit(impl_decl_id, ImplAsNonFacetType, constraint_id); return SemIR::SpecificInterface::None; } auto identified_id = RequireIdentifiedFacetType( context, SemIR::LocId(constraint_id), context.constant_values().Get(self_id), *facet_type, [&](auto& builder) { CARBON_DIAGNOSTIC(ImplOfUnidentifiedFacetType, Context, "facet type {0} cannot be identified in `impl as`", InstIdAsType); builder.Context(impl_decl_id, ImplOfUnidentifiedFacetType, constraint_id); }); if (!identified_id.has_value()) { return SemIR::SpecificInterface::None; } const auto& identified = context.identified_facet_types().Get(identified_id); if (!identified.is_valid_impl_as_target()) { CARBON_DIAGNOSTIC(ImplOfNotOneInterface, Error, "impl as {0} interfaces, expected 1", int); context.emitter().Emit(impl_decl_id, ImplOfNotOneInterface, identified.num_interfaces_to_impl()); return SemIR::SpecificInterface::None; } return identified.impl_as_target_interface(); } } // namespace Carbon::Check ================================================ FILE: toolchain/check/impl.h ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #ifndef CARBON_TOOLCHAIN_CHECK_IMPL_H_ #define CARBON_TOOLCHAIN_CHECK_IMPL_H_ #include "toolchain/check/context.h" #include "toolchain/sem_ir/ids.h" namespace Carbon::Check { struct RedeclaredImpl { // The previous Impl which the query Impl is redeclaring. SemIR::ImplId prev_impl_id; }; struct NewImpl { // The lookup bucket for the query Impl where it should be added once an // ImplId is known. SemIR::ImplStore::LookupBucketRef lookup_bucket; // Indicates the query Impl is not a redeclaration but an error was diagnosed. // The caller should avoid diagnosing more errors in the query impl. bool find_had_error; }; // Finds an existing impl if the `query_impl` is a redeclaration, and returns // its `ImplId`. This ensures all (valid) redeclarations share the same // `ImplId`. Otherwise, returns the bucket where a new `ImplId` should be added. auto FindImplId(Context& context, const SemIR::Impl& query_impl) -> std::variant; // Adds an impl to the ImplStore, and returns a new `ImplId`. // // If the impl is modified with `extend` then the parent's scope is extended // with it. auto AddImpl(Context& context, const SemIR::Impl& impl, SemIR::ImplStore::LookupBucketRef lookup_bucket, Parse::NodeId extend_node, SemIR::LocId implicit_params_loc_id) -> SemIR::ImplId; // Creates and returns an impl witness instruction for an impl declaration. // // If there are no rewrites into a name of the interface being implemented, a // placeholder witness table is created, to be replaced in the impl definition. // // Adds and returns an `ImplWitness` instruction (created with location set to // `loc_id`) that shows the "`Self` type" (from a facet in `impl.self_id`) // implements an identified interface (from a facet type in // `impl.constraint_id`). This witness reflects the values assigned to // associated constant members of that interface by rewrite constraints in the // constraint facet type. `self_specific_id` will be the `specific_id` of the // resulting witness. auto AddImplWitnessForDeclaration(Context& context, SemIR::LocId loc_id, const SemIR::Impl& impl, SemIR::SpecificId self_specific_id) -> SemIR::InstId; // Update `impl`'s witness at the start of a definition. auto ImplWitnessStartDefinition(Context& context, SemIR::Impl& impl) -> void; // Adds the function members to the witness for `impl`. auto FinishImplWitness(Context& context, const SemIR::Impl& impl_id) -> void; // Checks that any `require` declarations in the interface being implemented by // `impl` are satisfied. Otherwise, a diagnostic is issued and the `impl` is // made invalid. auto CheckRequireDeclsSatisfied(Context& context, SemIR::LocId loc_id, SemIR::Impl& impl) -> void; // Sets all unset members of the witness for `impl` to the error instruction and // sets the witness id in the `Impl` to an error. auto FillImplWitnessWithErrors(Context& context, SemIR::Impl& impl) -> void; // Returns whether the impl is either `final` explicitly, or implicitly due to // being concrete. auto IsImplEffectivelyFinal(Context& context, const SemIR::Impl& impl) -> bool; // Checks that `impl_function_id` is a valid implementation of the function // described in the interface as `interface_function_id`. Returns the value to // put into the corresponding slot in the witness table, which can be // `ErrorInst::InstId` if the function is not usable. auto CheckAssociatedFunctionImplementation( Context& context, SemIR::FunctionType interface_function_type, SemIR::SpecificId enclosing_specific_id, SemIR::InstId impl_decl_id, bool defer_thunk_definition) -> SemIR::InstId; // Checks that the constraint specified for the impl is valid and identified. // Returns the interface that the impl implements. On error, issues a diagnostic // and returns `None`. auto CheckConstraintIsInterface(Context& context, SemIR::InstId impl_decl_id, SemIR::InstId self_id, SemIR::TypeInstId constraint_id) -> SemIR::SpecificInterface; } // namespace Carbon::Check #endif // CARBON_TOOLCHAIN_CHECK_IMPL_H_ ================================================ FILE: toolchain/check/impl_lookup.cpp ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #include "toolchain/check/impl_lookup.h" #include #include #include #include #include "toolchain/base/kind_switch.h" #include "toolchain/check/cpp/impl_lookup.h" #include "toolchain/check/custom_witness.h" #include "toolchain/check/deduce.h" #include "toolchain/check/diagnostic_helpers.h" #include "toolchain/check/eval.h" #include "toolchain/check/facet_type.h" #include "toolchain/check/generic.h" #include "toolchain/check/impl.h" #include "toolchain/check/import_ref.h" #include "toolchain/check/inst.h" #include "toolchain/check/subst.h" #include "toolchain/check/type.h" #include "toolchain/check/type_completion.h" #include "toolchain/check/type_structure.h" #include "toolchain/sem_ir/facet_type_info.h" #include "toolchain/sem_ir/ids.h" #include "toolchain/sem_ir/impl.h" #include "toolchain/sem_ir/inst.h" #include "toolchain/sem_ir/typed_insts.h" namespace Carbon::Check { // Returns IRs which are allowed to define an `impl` involving the arguments. // This is limited by the orphan rule. static auto FindAssociatedImportIRs( Context& context, SemIR::ConstantId query_self_const_id, SemIR::SpecificInterface query_specific_interface) -> llvm::SmallVector { llvm::SmallVector result; // Add an entity to our result. auto add_entity = [&](const SemIR::EntityWithParamsBase& entity) { // We will look for impls in the import IR associated with the first owning // declaration. auto decl_id = entity.first_owning_decl_id; if (!decl_id.has_value()) { return; } auto import_ir_inst = GetCanonicalImportIRInst(context, decl_id); const auto* sem_ir = &context.sem_ir(); if (import_ir_inst.ir_id().has_value()) { sem_ir = context.import_irs().Get(import_ir_inst.ir_id()).sem_ir; } // For an instruction imported from C++, `GetCanonicalImportIRInst` returns // the final Carbon import instruction, so go one extra step to check for a // C++ import. if (auto import_ir_inst_id = sem_ir->insts().GetImportSource(import_ir_inst.inst_id()); import_ir_inst_id.has_value()) { result.push_back( sem_ir->import_ir_insts().Get(import_ir_inst_id).ir_id()); } else if (import_ir_inst.ir_id().has_value()) { result.push_back(import_ir_inst.ir_id()); } }; llvm::SmallVector worklist; // Push the contents of an instruction block onto our worklist. auto push_block = [&](SemIR::InstBlockId block_id) { if (block_id.has_value()) { llvm::append_range(worklist, context.inst_blocks().Get(block_id)); } }; // Add the arguments of a specific to the worklist. auto push_args = [&](SemIR::SpecificId specific_id) { if (specific_id.has_value()) { push_block(context.specifics().Get(specific_id).args_id); } }; worklist.push_back(context.constant_values().GetInstId(query_self_const_id)); add_entity(context.interfaces().Get(query_specific_interface.interface_id)); push_args(query_specific_interface.specific_id); while (!worklist.empty()) { auto inst_id = worklist.pop_back_val(); // Visit the operands of the constant. auto inst = context.insts().Get(inst_id); for (auto arg : {inst.arg0_and_kind(), inst.arg1_and_kind()}) { CARBON_KIND_SWITCH(arg) { case CARBON_KIND(SemIR::InstId inst_id): { if (inst_id.has_value()) { worklist.push_back(inst_id); } break; } case CARBON_KIND(SemIR::TypeInstId inst_id): { if (inst_id.has_value()) { worklist.push_back(inst_id); } break; } case CARBON_KIND(SemIR::InstBlockId inst_block_id): { push_block(inst_block_id); break; } case CARBON_KIND(SemIR::ClassId class_id): { add_entity(context.classes().Get(class_id)); break; } case CARBON_KIND(SemIR::InterfaceId interface_id): { add_entity(context.interfaces().Get(interface_id)); break; } case CARBON_KIND(SemIR::FacetTypeId facet_type_id): { const auto& facet_type_info = context.facet_types().Get(facet_type_id); for (const auto& impl : facet_type_info.extend_constraints) { add_entity(context.interfaces().Get(impl.interface_id)); push_args(impl.specific_id); } for (const auto& impl : facet_type_info.self_impls_constraints) { add_entity(context.interfaces().Get(impl.interface_id)); push_args(impl.specific_id); } break; } case CARBON_KIND(SemIR::FunctionId function_id): { add_entity(context.functions().Get(function_id)); break; } case CARBON_KIND(SemIR::SpecificId specific_id): { push_args(specific_id); break; } default: { break; } } } } // Deduplicate. llvm::sort(result, [](SemIR::ImportIRId a, SemIR::ImportIRId b) { return a.index < b.index; }); result.erase(llvm::unique(result), result.end()); return result; } // Returns true if a cycle was found and diagnosed. static auto FindAndDiagnoseImplLookupCycle( Context& context, const llvm::SmallVector& stack, SemIR::LocId loc_id, SemIR::ConstantId query_self_const_id, SemIR::ConstantId query_facet_type_const_id) -> bool { // Deduction of the interface parameters can do further impl lookups, and we // need to ensure we terminate. // // https://docs.carbon-lang.dev/docs/design/generics/details.html#acyclic-rule // - We look for violations of the acyclic rule by seeing if a previous lookup // had all the same type inputs. // - The `query_facet_type_const_id` encodes the entire facet type being // looked up, including any specific parameters for a generic interface. // // TODO: Implement the termination rule, which requires looking at the // complexity of the types on the top of (or throughout?) the stack: // https://docs.carbon-lang.dev/docs/design/generics/details.html#termination-rule for (auto [i, entry] : llvm::enumerate(stack)) { if (entry.query_self_const_id == query_self_const_id && entry.query_facet_type_const_id == query_facet_type_const_id) { auto facet_type_type_id = context.types().GetTypeIdForTypeConstantId(query_facet_type_const_id); CARBON_DIAGNOSTIC(ImplLookupCycle, Error, "cycle found in search for impl of {0} for type {1}", SemIR::TypeId, SemIR::TypeId); auto builder = context.emitter().Build( loc_id, ImplLookupCycle, facet_type_type_id, context.types().GetTypeIdForTypeConstantId(query_self_const_id)); for (const auto& active_entry : llvm::drop_begin(stack, i)) { if (active_entry.impl_loc.has_value()) { CARBON_DIAGNOSTIC(ImplLookupCycleNote, Note, "determining if this impl clause matches", ); builder.Note(active_entry.impl_loc, ImplLookupCycleNote); } } builder.Emit(); return true; } } return false; } struct RequiredImplsFromConstraint { llvm::ArrayRef req_impls; bool other_requirements; }; // Gets the set of `SpecificInterface`s that are required by a facet type // (as a constant value), and any special requirements. static auto GetRequiredImplsFromConstraint( Context& context, SemIR::LocId loc_id, SemIR::ConstantId query_self_const_id, SemIR::ConstantId query_facet_type_const_id) -> std::optional { auto facet_type_inst_id = context.constant_values().GetInstId(query_facet_type_const_id); auto facet_type_inst = context.insts().GetAs(facet_type_inst_id); const auto& facet_type_info = context.facet_types().Get(facet_type_inst.facet_type_id); auto identified_id = RequireIdentifiedFacetType( context, loc_id, query_self_const_id, facet_type_inst, [&](auto& builder) { CARBON_DIAGNOSTIC(ImplLookupInUnidentifiedFacetType, Context, "facet type {0} can not be identified", InstIdAsType); builder.Context(loc_id, ImplLookupInUnidentifiedFacetType, facet_type_inst_id); }); if (!identified_id.has_value()) { return std::nullopt; } return { {.req_impls = context.identified_facet_types().Get(identified_id).required_impls(), .other_requirements = facet_type_info.other_requirements}}; } static auto GetWitnessIdForImpl(Context& context, SemIR::LocId loc_id, bool query_is_concrete, SemIR::ConstantId query_self_const_id, const SemIR::SpecificInterface& interface, const SemIR::Impl& impl) -> EvalImplLookupResult { // The impl may have generic arguments, in which case we need to deduce them // to find what they are given the specific type and interface query. We use // that specific to map values in the impl to the deduced values. auto specific_id = SemIR::SpecificId::None; if (impl.generic_id.has_value()) { specific_id = DeduceImplArguments( context, loc_id, impl, query_self_const_id, interface.specific_id); if (!specific_id.has_value()) { return EvalImplLookupResult::MakeNone(); } } // The self type of the impl must match the type in the query, or this is an // `impl T as ...` for some other type `T` and should not be considered. auto noncanonical_deduced_self_const_id = SemIR::GetConstantValueInSpecific( context.sem_ir(), specific_id, impl.self_id); // In a generic `impl forall` the self type can be a FacetAccessType, which // will not be the same constant value as a query facet value. We move through // to the facet value here, and if the query was a FacetAccessType we did the // same there so they still match. auto deduced_self_const_id = GetCanonicalFacetOrTypeValue(context, noncanonical_deduced_self_const_id); if (query_self_const_id != deduced_self_const_id) { return EvalImplLookupResult::MakeNone(); } // The impl's constraint is a facet type which it is implementing for the self // type: the `I` in `impl ... as I`. The deduction step may be unable to be // fully applied to the types in the constraint and result in an error here, // in which case it does not match the query. auto deduced_constraint_id = context.constant_values().GetInstId(SemIR::GetConstantValueInSpecific( context.sem_ir(), specific_id, impl.constraint_id)); if (deduced_constraint_id == SemIR::ErrorInst::InstId) { return EvalImplLookupResult::MakeNone(); } auto deduced_constraint_facet_type_id = context.insts() .GetAs(deduced_constraint_id) .facet_type_id; const auto& deduced_constraint_facet_type_info = context.facet_types().Get(deduced_constraint_facet_type_id); CARBON_CHECK(deduced_constraint_facet_type_info.extend_constraints.size() == 1); if (deduced_constraint_facet_type_info.other_requirements) { return EvalImplLookupResult::MakeNone(); } // The specifics in the queried interface must match the deduced specifics in // the impl's constraint facet type. auto impl_interface_specific_id = deduced_constraint_facet_type_info.extend_constraints[0].specific_id; auto query_interface_specific_id = interface.specific_id; if (impl_interface_specific_id != query_interface_specific_id) { return EvalImplLookupResult::MakeNone(); } LoadImportRef(context, impl.witness_id); if (specific_id.has_value()) { // If the impl definition can be resolved, eval will do it immediately; // otherwise, it can be resolved by further specialization. This is used to // resolve dependency chains when `MakeFinal` is returned without a concrete // definition; particularly final impls with symbolic constants. AddInstInNoBlock( context, loc_id, SemIR::RequireSpecificDefinition{ .type_id = GetSingletonType( context, SemIR::RequireSpecificDefinitionType::TypeInstId), .specific_id = specific_id}); } if (query_is_concrete || impl.is_final) { // TODO: These final results should be cached somehow. Positive (non-None) // results could be cached globally, as they can not change. But // negative results can change after a final impl is written, so // they can only be cached in a limited way, or the cache needs to // be invalidated by writing a final impl that would match. return EvalImplLookupResult::MakeFinal( context.constant_values().GetInstId(SemIR::GetConstantValueInSpecific( context.sem_ir(), specific_id, impl.witness_id))); } else { return EvalImplLookupResult::MakeNonFinal(); } } // Finds a lookup result from `query_self_inst_id` if it is a facet value that // names the query interface in its facet type. Note that `query_self_inst_id` // is allowed to be a non-canonical facet value in order to find a concrete // witness, so it's not referenced as a constant value. static auto LookupImplWitnessInSelfFacetValue( Context& context, SemIR::LocId loc_id, SemIR::InstId self_facet_value_inst_id, SemIR::SpecificInterface query_specific_interface) -> EvalImplLookupResult { auto facet_type = context.types().TryGetAs( context.insts().Get(self_facet_value_inst_id).type_id()); if (!facet_type) { return EvalImplLookupResult::MakeNone(); } auto self_facet_value_const_id = context.constant_values().Get(self_facet_value_inst_id); // The position of the interface in `required_impls()` is also the // position of the witness for that interface in `FacetValue`. The // `FacetValue` witnesses are the output of an impl lookup, which finds and // returns witnesses in the same order. auto identified_id = RequireIdentifiedFacetType( context, loc_id, self_facet_value_const_id, *facet_type, [&](auto& builder) { CARBON_DIAGNOSTIC(ImplLookupInUnidentifiedFacetTypeOfQuerySelf, Context, "facet type of value {0} can not be identified", InstIdAsType); builder.Context(loc_id, ImplLookupInUnidentifiedFacetTypeOfQuerySelf, self_facet_value_inst_id); }); if (!identified_id.has_value()) { return EvalImplLookupResult::MakeNone(); } auto facet_type_req_impls = llvm::enumerate( context.identified_facet_types().Get(identified_id).required_impls()); auto it = llvm::find_if(facet_type_req_impls, [&](auto e) { auto [req_self, req_specific_interface] = e.value(); // The `self_facet_value_inst_id` in eval is a canonicalized facet value, as // is the self in the identified facet type. return req_self == self_facet_value_const_id && req_specific_interface == query_specific_interface; }); if (it == facet_type_req_impls.end()) { return EvalImplLookupResult::MakeNone(); } auto index = (*it).index(); if (auto facet_value = context.insts().TryGetAs( self_facet_value_inst_id)) { auto witness_id = context.inst_blocks().Get(facet_value->witnesses_block_id)[index]; if (context.insts().Is(witness_id)) { return EvalImplLookupResult::MakeFinal(witness_id); } } return EvalImplLookupResult::MakeNonFinal(); } // Substitutes witnesess in place of `LookupImplWitness` queries into `.Self`, // when the witness is for the same interface as the one `.Self` is referring // to. // // This allows access to the `FacetType` and its constraints from the witness, // and allows `ImplWitnessAccess` instructions to be immediately resolved to a // more specific value when possible. class SubstWitnessesCallbacks : public SubstInstCallbacks { public: // `context` must not be null. explicit SubstWitnessesCallbacks( Context* context, SemIR::LocId loc_id, SemIR::ConstantId query_self_const_id, llvm::ArrayRef req_impls, llvm::ArrayRef witness_inst_ids) : SubstInstCallbacks(context), loc_id_(loc_id), canonical_query_self_const_id_( GetCanonicalFacetOrTypeValue(*context, query_self_const_id)), req_impls_(req_impls), witness_inst_ids_(witness_inst_ids) {} auto Subst(SemIR::InstId& inst_id) -> SubstResult override { // `FacetType` can be concrete even when it has rewrite constraints that // have a symbolic dependency on `.Self`. See use of // `GetConstantValueIgnoringPeriodSelf` in eval. So in order to recurse into // `FacetType` we must check for it before the `is_concrete` early return. if (context().insts().Is(inst_id)) { ++facet_type_depth_; return SubstOperands; } if (context().constant_values().Get(inst_id).is_concrete()) { return FullySubstituted; } auto access = context().insts().TryGetAs(inst_id); if (!access) { return SubstOperands; } auto lookup = context().insts().GetAs(access->witness_id); auto bind_name = context().insts().TryGetAs( lookup.query_self_inst_id); if (!bind_name) { return SubstOperands; } const auto& self_entity_name = context().entity_names().Get(bind_name->entity_name_id); if (self_entity_name.name_id != SemIR::NameId::PeriodSelf) { return SubstOperands; } // TODO: Once we are numbering `EntityName`, (see the third model in // https://docs.google.com/document/d/1Yt-i5AmF76LSvD4TrWRIAE_92kii6j5yFiW-S7ahzlg/edit?tab=t.0#heading=h.7urbxcq23olv) // then verify that the index here is equal to the `facet_type_depth_`, // which would mean that it is a reference to the top-level `Self`, which is // being replaced with the impl lookup query self facet value (and then we // use the witness derived from it). // // For now, we only substitute if depth == 0, which is incorrect inside // nested facet types, as it can miss references in specifics up to the top // level facet value. if (facet_type_depth_ > 0) { return SubstOperands; } auto witness_id = FindWitnessForInterface(lookup.query_specific_interface_id); if (!witness_id.has_value()) { return SubstOperands; } inst_id = RebuildNewInst( context().insts().GetLocIdForDesugaring(loc_id_), SemIR::ImplWitnessAccess{.type_id = GetSingletonType( context(), SemIR::WitnessType::TypeInstId), .witness_id = witness_id, .index = access->index}); // Once we replace a witness, we either have a concrete value or some // reference to an associated constant that came from the witness's facet // type. We don't want to substitute into the witness's facet type, so we // don't recurse on whatever came from the witness. return FullySubstituted; } auto Rebuild(SemIR::InstId orig_inst_id, SemIR::Inst new_inst) -> SemIR::InstId override { if (context().insts().Is(orig_inst_id)) { --facet_type_depth_; } return RebuildNewInst(loc_id_, new_inst); } auto ReuseUnchanged(SemIR::InstId orig_inst_id) -> SemIR::InstId override { if (context().insts().Is(orig_inst_id)) { --facet_type_depth_; } return orig_inst_id; } private: auto FindWitnessForInterface(SemIR::SpecificInterfaceId specific_interface_id) -> SemIR::InstId { auto lookup_query_interface = context().specific_interfaces().Get(specific_interface_id); for (auto [req_impl, witness_inst_id] : llvm::zip_equal(req_impls_, witness_inst_ids_)) { auto [req_self, req_interface] = req_impl; // The `LookupImplWitness` is for `.Self`, so if the witness is for some // type other than the query self, we can't use it for `.Self`. if (req_self != canonical_query_self_const_id_) { continue; } // If the `LookupImplWitness` for `.Self` is not looking for the same // interface as we have a witness for, this is not the right witness to // use to replace the lookup for `.Self`. if (req_interface.interface_id != lookup_query_interface.interface_id) { continue; } return witness_inst_id; } return SemIR::InstId::None; } SemIR::LocId loc_id_; SemIR::ConstantId canonical_query_self_const_id_; llvm::ArrayRef req_impls_; llvm::ArrayRef witness_inst_ids_; int facet_type_depth_ = 0; }; static auto VerifyQueryFacetTypeConstraints( Context& context, SemIR::LocId loc_id, SemIR::ConstantId query_self_const_id, SemIR::ConstantId query_facet_type_const_id, llvm::ArrayRef req_impls, llvm::ArrayRef witness_inst_ids) -> bool { SemIR::InstId query_facet_type_inst_id = context.constant_values().GetInstId(query_facet_type_const_id); CARBON_CHECK(context.insts().Is(query_facet_type_inst_id)); const auto& facet_type_info = context.facet_types().Get( context.insts() .GetAs(query_facet_type_inst_id) .facet_type_id); if (!facet_type_info.rewrite_constraints.empty()) { auto callbacks = SubstWitnessesCallbacks( &context, loc_id, query_self_const_id, req_impls, witness_inst_ids); for (const auto& rewrite : facet_type_info.rewrite_constraints) { auto lhs_id = SubstInst(context, rewrite.lhs_id, callbacks); auto rhs_id = SubstInst(context, rewrite.rhs_id, callbacks); if (lhs_id != rhs_id) { // TODO: Provide a diagnostic note and location for which rewrite // constraint was not satisfied, if a diagnostic is going to be // displayed for the LookupImplWitessFailure. This will require plumbing // through a callback that lets us add a Note to another diagnostic. return false; } } } // TODO: Validate that the witnesses satisfy the other requirements in the // `facet_type_info`. return true; } // Begin a search for an impl declaration matching the query. We do this by // creating an LookupImplWitness instruction and evaluating. If it's able to // find a final concrete impl, then it will evaluate to that `ImplWitness` but // if not, it will evaluate to itself as a symbolic witness to be further // evaluated with a more specific query when building a specific for the generic // context the query came from. static auto GetOrAddLookupImplWitness(Context& context, SemIR::LocId loc_id, SemIR::ConstantId query_self_const_id, SemIR::SpecificInterface interface) -> SemIR::InstId { auto witness_const_id = EvalOrAddInst( context, context.insts().GetLocIdForDesugaring(loc_id), SemIR::LookupImplWitness{ .type_id = GetSingletonType(context, SemIR::WitnessType::TypeInstId), .query_self_inst_id = context.constant_values().GetInstId(query_self_const_id), .query_specific_interface_id = context.specific_interfaces().Add(interface), }); // We use a NotConstant result from eval to communicate back an impl // lookup failure. See `EvalConstantInst()` for `LookupImplWitness`. if (!witness_const_id.is_constant()) { return SemIR::InstId::None; } return context.constant_values().GetInstId(witness_const_id); } auto LookupImplWitness(Context& context, SemIR::LocId loc_id, SemIR::ConstantId query_self_const_id, SemIR::ConstantId query_facet_type_const_id) -> SemIR::InstBlockIdOrError { if (query_self_const_id == SemIR::ErrorInst::ConstantId || query_facet_type_const_id == SemIR::ErrorInst::ConstantId) { return SemIR::InstBlockIdOrError::MakeError(); } { // The query self value is a type value or a facet value. auto query_self_type_id = context.insts() .Get(context.constant_values().GetInstId(query_self_const_id)) .type_id(); CARBON_CHECK((context.types().IsOneOf( query_self_type_id))); // The query facet type value is indeed a facet type. CARBON_CHECK(context.insts().Is( context.constant_values().GetInstId(query_facet_type_const_id))); } auto req_impls_from_constraint = GetRequiredImplsFromConstraint( context, loc_id, query_self_const_id, query_facet_type_const_id); if (!req_impls_from_constraint) { return SemIR::InstBlockIdOrError::MakeError(); } auto [req_impls, other_requirements] = *req_impls_from_constraint; if (other_requirements) { // TODO: Remove this when other requirements go away. return SemIR::InstBlockId::None; } if (req_impls.empty()) { return SemIR::InstBlockId::Empty; } if (FindAndDiagnoseImplLookupCycle(context, context.impl_lookup_stack(), loc_id, query_self_const_id, query_facet_type_const_id)) { return SemIR::InstBlockIdOrError::MakeError(); } auto& stack = context.impl_lookup_stack(); stack.push_back({ .query_self_const_id = query_self_const_id, .query_facet_type_const_id = query_facet_type_const_id, }); // We need to find a witness for each self+interface pair in `req_impls`. // // Every consumer of a facet type needs to agree on the order of interfaces // used for its witnesses, which is done by following the order in the // IdentifiedFacetType. llvm::SmallVector result_witness_ids; for (const auto& req_impl : req_impls) { // TODO: Since both `interfaces` and `query_self_const_id` are sorted lists, // do an O(N+M) merge instead of O(N*M) nested loops. auto result_witness_id = GetOrAddLookupImplWitness(context, loc_id, req_impl.self_facet_value, req_impl.specific_interface); if (result_witness_id.has_value()) { result_witness_ids.push_back(result_witness_id); } else { // At least one queried interface in the facet type has no witness for the // given type, we can stop looking for more. break; } } stack.pop_back(); // All interfaces in the query facet type must have been found to be available // through some impl, or directly on the value's facet type if // `query_self_const_id` is a facet value. if (result_witness_ids.size() != req_impls.size()) { return SemIR::InstBlockId::None; } // Verify rewrite constraints in the query constraint are satisfied after // applying the rewrites from the found witnesses. if (!VerifyQueryFacetTypeConstraints(context, loc_id, query_self_const_id, query_facet_type_const_id, req_impls, result_witness_ids)) { return SemIR::InstBlockId::None; } return context.inst_blocks().AddCanonical(result_witness_ids); } // Returns whether the query is concrete, it is false if the self type or // interface specifics have a symbolic dependency. static auto QueryIsConcrete(Context& context, SemIR::ConstantId self_const_id, const SemIR::SpecificInterface& specific_interface) -> bool { if (!self_const_id.is_concrete()) { return false; } if (!specific_interface.specific_id.has_value()) { return true; } auto args_id = context.specifics().Get(specific_interface.specific_id).args_id; for (auto inst_id : context.inst_blocks().Get(args_id)) { if (!context.constant_values().Get(inst_id).is_concrete()) { return false; } } return true; } namespace { // A class to filter imported impls based on whether they could possibly match a // query, prior to importing them. For now we only consider impls that are for // an interface that's being queried. // // TODO: There's a lot more we could do to filter out impls that can't possibly // match. class ImportImplFilter { public: explicit ImportImplFilter(Context& context, SemIR::ImportIRId import_ir_id, SemIR::SpecificInterface interface) : context_(&context), interface_id_(interface.interface_id), import_ir_id_(import_ir_id), import_ir_(context_->import_irs().Get(import_ir_id).sem_ir), cached_import_interface_id_(SemIR::InterfaceId::None) {} // Returns whether the given impl is potentially relevant for the current // query. auto IsRelevantImpl(SemIR::ImplId import_impl_id) -> bool { auto impl_interface_id = import_ir_->impls().Get(import_impl_id).interface.interface_id; if (!impl_interface_id.has_value()) { // This indicates that an error occurred when type-checking the impl. // TODO: Use an explicit error value for this rather than None. return false; } return IsRelevantInterface(impl_interface_id); } private: // Returns whether an impl for the given interface might be relevant to the // current query. auto IsRelevantInterface(SemIR::InterfaceId import_interface_id) -> bool { if (!cached_import_interface_id_.has_value()) { if (IsSameInterface(import_interface_id, interface_id_)) { cached_import_interface_id_ = import_interface_id; return true; } } else if (cached_import_interface_id_ == import_interface_id) { return true; } return false; } // Returns whether the given interfaces from two different IRs are the same. auto IsSameInterface(SemIR::InterfaceId import_interface_id, SemIR::InterfaceId local_interface_id) -> bool { // The names must be the same. if (import_ir_->names().GetAsStringIfIdentifier( import_ir_->interfaces().Get(import_interface_id).name_id) != context_->names().GetAsStringIfIdentifier( context_->interfaces().Get(local_interface_id).name_id)) { return false; } // Compare the interfaces themselves. // TODO: Should we check the scope of the interface before doing this? auto local_version_of_import_interface_id = ImportInterface(*context_, import_ir_id_, import_interface_id); return local_version_of_import_interface_id == local_interface_id; } Context* context_; // The interface being looked up. SemIR::InterfaceId interface_id_; // The IR that we are currently importing impls from. SemIR::ImportIRId import_ir_id_; const SemIR::File* import_ir_; // The interface ID of `interface_id_` in `import_ir_`, if known. SemIR::InterfaceId cached_import_interface_id_; }; } // namespace struct CandidateImpl { const SemIR::Impl* impl; // Used for sorting the candidates to find the most-specialized match. TypeStructure type_structure; }; struct CandidateImpls { llvm::SmallVector impls; bool consider_cpp_candidates = false; }; // Returns the list of candidates impls for lookup to select from. static auto CollectCandidateImplsForQuery( Context& context, bool final_only, SemIR::ConstantId query_self_const_id, const TypeStructure& query_type_structure, SemIR::SpecificInterface& query_specific_interface) -> CandidateImpls { CandidateImpls candidates; auto import_irs = FindAssociatedImportIRs(context, query_self_const_id, query_specific_interface); for (auto import_ir_id : import_irs) { // If `Cpp` is an associated package, then we'll instead look for C++ // operator overloads for certain well-known interfaces. if (import_ir_id == SemIR::ImportIRId::Cpp) { candidates.consider_cpp_candidates = true; continue; } // Instead of importing all impls, only import ones that are in some way // connected to this query. ImportImplFilter filter(context, import_ir_id, query_specific_interface); for (auto [import_impl_id, _] : context.import_irs().Get(import_ir_id).sem_ir->impls().enumerate()) { if (filter.IsRelevantImpl(import_impl_id)) { // TODO: Track the relevant impls and only consider those ones and any // local impls, rather than looping over all impls below. ImportImpl(context, import_ir_id, import_impl_id); } } } for (auto [id, impl] : context.impls().enumerate()) { CARBON_CHECK(impl.witness_id.has_value()); if (final_only && !IsImplEffectivelyFinal(context, impl)) { continue; } // If the impl's interface_id differs from the query, then this impl can // not possibly provide the queried interface. if (impl.interface.interface_id != query_specific_interface.interface_id) { continue; } // When the impl's interface_id matches, but the interface is generic, the // impl may or may not match based on restrictions in the generic // parameters of the impl. // // As a shortcut, if the impl's constraint is not symbolic (does not // depend on any generic parameters), then we can determine whether we match // by looking if the specific ids match exactly. auto impl_interface_const_id = context.constant_values().Get(impl.constraint_id); if (!impl_interface_const_id.is_symbolic() && impl.interface.specific_id != query_specific_interface.specific_id) { continue; } // Build the type structure used for choosing the best the candidate. auto type_structure = BuildTypeStructure(context, impl.self_id, impl.interface); if (!type_structure) { continue; } // TODO: We can skip the comparison here if the `impl_interface_const_id` is // not symbolic, since when the interface and specific ids match, and they // aren't symbolic, the structure will be identical. if (!query_type_structure.CompareStructure( TypeStructure::CompareTest::IsEqualToOrMoreSpecificThan, *type_structure)) { continue; } candidates.impls.push_back({&impl, std::move(*type_structure)}); } auto compare = [](auto& lhs, auto& rhs) -> bool { return lhs.type_structure < rhs.type_structure; }; // Stable sort is used so that impls that are seen first are preferred when // they have an equal priority ordering. // TODO: Allow Carbon code to provide a priority ordering explicitly. For // now they have all the same priority, so the priority is the order in // which they are found in code. llvm::stable_sort(candidates.impls, compare); return candidates; } // Record the query which found a final impl witness. It's illegal to // write a final impl afterward that would match the same query. static auto PoisonImplLookupQuery(Context& context, SemIR::LocId loc_id, EvalImplLookupMode mode, SemIR::LookupImplWitness eval_query, const EvalImplLookupResult& result, const SemIR::Impl& impl) -> void { if (mode == EvalImplLookupMode::RecheckPoisonedLookup) { return; } if (!result.has_final_value()) { return; } // If the impl was effectively final, then we don't need to poison here. A // change of query result will already be diagnosed at the point where the // new impl decl was written that changes the result. if (IsImplEffectivelyFinal(context, impl)) { return; } context.poisoned_concrete_impl_lookup_queries().push_back( {.loc_id = loc_id, .query = eval_query, .impl_witness = result.final_witness()}); } auto EvalLookupSingleImplWitness(Context& context, SemIR::LocId loc_id, SemIR::LookupImplWitness eval_query, SemIR::InstId self_facet_value_inst_id, EvalImplLookupMode mode) -> EvalImplLookupResult { auto query_specific_interface = context.specific_interfaces().Get(eval_query.query_specific_interface_id); // Ensure specifics don't substitute in weird things for the query self. CARBON_CHECK(context.types().IsFacetType( context.insts().Get(eval_query.query_self_inst_id).type_id())); SemIR::ConstantId query_self_const_id = context.constant_values().Get(eval_query.query_self_inst_id); auto facet_lookup_result = LookupImplWitnessInSelfFacetValue( context, loc_id, self_facet_value_inst_id, query_specific_interface); if (facet_lookup_result.has_final_value()) { return facet_lookup_result; } // If the self type is a facet that provides a witness, then we are in an // `interface` or an `impl`. In both cases, we don't want to do any impl // lookups. The query will eventually resolve to a concrete witness when it // can get it from the self facet value, when it has a specific applied in the // future. // // In particular, this avoids a LookupImplWitness instruction in the eval // block of an impl declaration from doing impl lookup. Specifically the // lookup of the implicit .Self in `impl ... where .X`. If it does impl lookup // when the eval block is run, it finds the same `impl`, tries to build a // specific from it, which runs the eval block, creating a recursive loop that // crashes. if (facet_lookup_result.has_value()) { if (auto bind = context.insts().TryGetAs( eval_query.query_self_inst_id)) { const auto& entity = context.entity_names().Get(bind->entity_name_id); if (entity.name_id == SemIR::NameId::PeriodSelf || entity.name_id == SemIR::NameId::SelfType) { return facet_lookup_result; } } } auto query_type_structure = BuildTypeStructure( context, context.constant_values().GetInstId(query_self_const_id), query_specific_interface); if (!query_type_structure) { return EvalImplLookupResult::MakeNone(); } // Check to see if this result is in the cache. But skip the cache if we're // re-checking a poisoned result and need to redo the lookup. auto impl_lookup_cache_key = Context::ImplLookupCacheKey{ query_self_const_id, eval_query.query_specific_interface_id}; if (mode != EvalImplLookupMode::RecheckPoisonedLookup) { if (auto result = context.impl_lookup_cache().Lookup(impl_lookup_cache_key)) { return EvalImplLookupResult::MakeFinal(result.value()); } } // If the self value is a (symbolic) facet value that has a symbolic witness, // then we don't need to do impl lookup, except that we want to find any final // impls to return a concrete witness if possible. So we limit the query to // final impls only in that case. Note as in the CHECK above, the query can // not be concrete in this case, so only final impls can produce a concrete // witness for this query. auto candidates = CollectCandidateImplsForQuery( context, facet_lookup_result.has_value(), query_self_const_id, *query_type_structure, query_specific_interface); bool query_is_concrete = QueryIsConcrete(context, query_self_const_id, query_specific_interface); CARBON_CHECK(!query_is_concrete || !facet_lookup_result.has_value(), "Non-concrete facet lookup value for concrete query"); // Perform a lookup for an `impl` that matches the query. If we don't find a // final impl, the self value may still have been a facet that provides a // symbolic witness in the `facet_lookup_result`, which we want to fall back // to. It records that an `impl` will exist for the query, but is yet unknown. struct LookupResult { EvalImplLookupResult result; const TypeStructure* impl_type_structure = nullptr; SemIR::LocId impl_loc_id = SemIR::LocId::None; }; LookupResult lookup_result = {.result = facet_lookup_result}; auto core_interface = GetCoreInterface(context, query_specific_interface.interface_id); // Consider a custom witness for core interfaces. // TODO: This needs to expand to more interfaces, and we might want to have // that dispatch in custom_witness.cpp instead of here. bool used_custom_witness = false; if (auto witness_id = LookupCustomWitness( context, loc_id, core_interface, query_self_const_id, eval_query.query_specific_interface_id)) { if (witness_id->has_value()) { lookup_result = {.result = EvalImplLookupResult::MakeFinal(*witness_id)}; } else { lookup_result = {.result = EvalImplLookupResult::MakeNonFinal()}; } used_custom_witness = true; } // Only consider candidates when a custom witness didn't apply. if (!used_custom_witness) { for (const auto& candidate : candidates.impls) { const auto& impl = *candidate.impl; // In deferred lookup for a symbolic impl witness, while building a // specific, there may be no stack yet as this may be the first lookup. If // further lookups are started as a result in deduce, they will build the // stack. if (!context.impl_lookup_stack().empty()) { context.impl_lookup_stack().back().impl_loc = impl.definition_id; } auto result = GetWitnessIdForImpl(context, loc_id, query_is_concrete, query_self_const_id, query_specific_interface, impl); if (result.has_value()) { PoisonImplLookupQuery(context, loc_id, mode, eval_query, result, impl); lookup_result = {.result = result, .impl_type_structure = &candidate.type_structure, .impl_loc_id = SemIR::LocId(impl.definition_id)}; break; } } } if (query_is_concrete && candidates.consider_cpp_candidates && core_interface != CoreInterface::Unknown) { // Also check for a C++ candidate that is a better match than whatever // `impl` we may have found in Carbon. auto cpp_witness_id = LookupCppImpl( context, loc_id, core_interface, query_self_const_id, eval_query.query_specific_interface_id, lookup_result.impl_type_structure, lookup_result.impl_loc_id); if (cpp_witness_id.has_value()) { lookup_result = {.result = EvalImplLookupResult::MakeFinal(cpp_witness_id)}; } } if (mode != EvalImplLookupMode::RecheckPoisonedLookup && lookup_result.result.has_final_value()) { context.impl_lookup_cache().Insert(impl_lookup_cache_key, lookup_result.result.final_witness()); } return lookup_result.result; } auto LookupMatchesImpl(Context& context, SemIR::LocId loc_id, SemIR::ConstantId query_self_const_id, SemIR::SpecificInterface query_specific_interface, SemIR::ImplId target_impl) -> bool { if (query_self_const_id == SemIR::ErrorInst::ConstantId) { return false; } auto result = GetWitnessIdForImpl( context, loc_id, /*query_is_concrete=*/false, query_self_const_id, query_specific_interface, context.impls().Get(target_impl)); return result.has_value(); } } // namespace Carbon::Check ================================================ FILE: toolchain/check/impl_lookup.h ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #ifndef CARBON_TOOLCHAIN_CHECK_IMPL_LOOKUP_H_ #define CARBON_TOOLCHAIN_CHECK_IMPL_LOOKUP_H_ #include #include "toolchain/check/context.h" #include "toolchain/sem_ir/ids.h" #include "toolchain/sem_ir/inst.h" #include "toolchain/sem_ir/typed_insts.h" namespace Carbon::Check { // Looks up the witnesses to use for a type value or facet value, and a facet // type naming a set of interfaces required to be implemented for that type, as // well as possible constraints on those interfaces. // // N.B. In the future, `TypeType` will become a facet type, at which point type // values will also be facet values. // // The return value is one of: // - An InstBlockId value, containing an `ImplWitness` instruction for each // required interface in the `query_facet_type_const_id`. This verifies the // facet type is satisfied for the type in `type_const_id`, and provides a // witness for accessing the impl of each interface. // // - `InstBlockId::None`, indicating lookup failed for at least one required // interface in the `query_facet_type_const_id`. The facet type is not // satisfied for the type in `type_const_id`. This represents lookup failure, // but is not an error, so no diagnostic is emitted. // // - An error value, indicating the program is invalid and a diagonstic has been // produced, either in this function or before. auto LookupImplWitness(Context& context, SemIR::LocId loc_id, SemIR::ConstantId query_self_const_id, SemIR::ConstantId query_facet_type_const_id) -> SemIR::InstBlockIdOrError; // Returns whether the query matches against the given impl. This is like a // `LookupImplWitness` operation but for a single interface, and against only // the single impl. auto LookupMatchesImpl(Context& context, SemIR::LocId loc_id, SemIR::ConstantId query_self_const_id, SemIR::SpecificInterface query_specific_interface, SemIR::ImplId target_impl) -> bool; // The result of EvalLookupSingleImplWitness(). It can be one of: // - No value. Lookup failed to find an impl declaration. // - An effectively final value. Lookup found either a concrete impl or a // `final` impl declaration; both can be used definitely. A witness is // available. // - A non-`final` symbolic value. Lookup found an impl, but it is not returned // since lookup will need to be done again with a more specific query to look // for specializations. class [[nodiscard]] EvalImplLookupResult { public: static auto MakeNone() -> EvalImplLookupResult { return EvalImplLookupResult(FoundNone()); } static auto MakeFinal(SemIR::InstId inst_id) -> EvalImplLookupResult { return EvalImplLookupResult(inst_id); } static auto MakeNonFinal() -> EvalImplLookupResult { return EvalImplLookupResult(FoundNonFinalImpl()); } // True if an impl declaration was found, either effectively final or // symbolic. auto has_value() const -> bool { return !std::holds_alternative(value_); } // True if there is an effectively final witness in the result. If false, and // `has_value()` is true, it means an impl was found that's not effectively // final, and a further more specific query will need to be done. auto has_final_value() const -> bool { return std::holds_alternative(value_); } // Returns the witness id for an effectively final value's impl declaration. // Only valid to call if `has_final_value` is true. auto final_witness() const -> SemIR::InstId { return std::get(value_); } private: struct FoundNone {}; struct FoundNonFinalImpl {}; using Value = std::variant; explicit EvalImplLookupResult(Value value) : value_(value) {} Value value_; }; // The kind of impl lookup being performed by a call to // `EvalLookupSingleImplWitness`. enum class EvalImplLookupMode { // This is a regular impl lookup performed during check. If we produce a final // witness value that uses a specializable impl, the query will be poisoned so // that we will recheck it at the end of the compilation. Normal, // This is a re-check of a poisoned lookup being performed at the end of a // file. This disables any caching of lookup results for this query and redoes // the impl lookup. RecheckPoisonedLookup, }; // Looks for a witness instruction of an impl declaration for a query consisting // of a type value or facet value, and a single interface. This is for eval to // execute lookup via the LookupImplWitness instruction. It does not consider // the self facet value for finding a witness, since LookupImplWitness() would // have found that and not caused us to defer lookup to here. auto EvalLookupSingleImplWitness(Context& context, SemIR::LocId loc_id, SemIR::LookupImplWitness eval_query, SemIR::InstId self_facet_value_inst_id, EvalImplLookupMode mode) -> EvalImplLookupResult; } // namespace Carbon::Check #endif // CARBON_TOOLCHAIN_CHECK_IMPL_LOOKUP_H_ ================================================ FILE: toolchain/check/impl_validation.cpp ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #include "toolchain/check/impl_validation.h" #include #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/SmallVector.h" #include "toolchain/base/kind_switch.h" #include "toolchain/check/diagnostic_helpers.h" #include "toolchain/check/impl_lookup.h" #include "toolchain/check/import_ref.h" #include "toolchain/check/type_structure.h" #include "toolchain/sem_ir/entity_with_params_base.h" #include "toolchain/sem_ir/ids.h" #include "toolchain/sem_ir/type_iterator.h" #include "toolchain/sem_ir/typed_insts.h" namespace Carbon::Check { namespace { // All information about a `SemIR::Impl` needed for validation. struct ImplInfo { SemIR::ImplId impl_id; bool is_final; SemIR::InstId witness_id; SemIR::TypeInstId self_id; SemIR::InstId latest_decl_id; SemIR::SpecificInterface interface; // Whether the `impl` decl was imported or from the local file. bool is_local; // If imported, the IR from which the `impl` decl was imported. SemIR::ImportIRId ir_id; std::optional type_structure; }; } // namespace static auto GetIRId(Context& context, SemIR::InstId owning_inst_id) -> SemIR::ImportIRId { if (!owning_inst_id.has_value()) { return SemIR::ImportIRId::None; } return GetCanonicalImportIRInst(context, owning_inst_id).ir_id(); } // Returns if `owning_inst_id` is from the current file. This does not count an // api and impl file as the same file. static auto IsSameFile(Context& context, SemIR::InstId owning_inst_id) -> bool { if (!owning_inst_id.has_value()) { return false; } auto ir_id = GetCanonicalImportIRInst(context, owning_inst_id).ir_id(); return !ir_id.has_value(); } // Returns if `owning_inst_id` is from the current library. This does count api // and impl files as the same library. static auto IsSameLibrary(Context& context, SemIR::InstId owning_inst_id) -> bool { if (!owning_inst_id.has_value()) { return false; } auto ir_id = GetCanonicalImportIRInst(context, owning_inst_id).ir_id(); if (!ir_id.has_value()) { return true; } if (const auto* api = context.import_irs().Get(SemIR::ImportIRId::ApiForImpl).sem_ir) { auto& ir = context.import_irs().Get(ir_id); return ir.sem_ir == api; } return false; } static auto GetImplInfo(Context& context, SemIR::ImplId impl_id) -> ImplInfo { const auto& impl = context.impls().Get(impl_id); auto ir_id = GetIRId(context, impl.first_owning_decl_id); return {.impl_id = impl_id, .is_final = impl.is_final, .witness_id = impl.witness_id, .self_id = impl.self_id, .latest_decl_id = impl.latest_decl_id(), .interface = impl.interface, .is_local = !ir_id.has_value(), .ir_id = ir_id, .type_structure = BuildTypeStructure(context, impl.self_id, impl.interface)}; } // A final impl must be in the same file as either its root self type or the // interface in its constraint. // // Returns true if an error was diagnosed. static auto DiagnoseFinalImplNotInSameFileAsRootSelfTypeOrInterface( Context& context, const ImplInfo& impl, SemIR::ImportIRId interface_ir_id) -> bool { bool self_type_same_file = false; auto type_iter = SemIR::TypeIterator(&context.sem_ir()); type_iter.Add(impl.self_id); auto step = type_iter.Next(); using Step = SemIR::TypeIterator::Step; CARBON_KIND_SWITCH(step.any) { case CARBON_KIND(Step::ClassStart start): { auto inst_id = context.classes().Get(start.class_id).definition_id; if (IsSameFile(context, inst_id)) { self_type_same_file = true; } break; } case CARBON_KIND(Step::ClassStartOnly start): { auto inst_id = context.classes().Get(start.class_id).definition_id; if (IsSameFile(context, inst_id)) { self_type_same_file = true; } break; } case CARBON_KIND(Step::Done _): { CARBON_FATAL("self type is empty?"); } default: break; } bool interface_same_file = !interface_ir_id.has_value(); if (!self_type_same_file && !interface_same_file) { CARBON_DIAGNOSTIC(FinalImplInvalidFile, Error, "`final impl` found in file that does not contain " "the root self type nor the interface definition"); context.emitter().Emit(impl.latest_decl_id, FinalImplInvalidFile); return true; } return false; } static auto DiagnoseOrphanImpl(Context& context, const ImplInfo& impl, SemIR::ImportIRId interface_ir_id) -> bool { // If the interface is defined in this file, then the impl is not an orphan. if (!interface_ir_id.has_value()) { return true; } // Look for a class in the self type, or the interface specific, that is from // this file to show the impl is not an orphan. auto type_iter = SemIR::TypeIterator(&context.sem_ir()); type_iter.Add(impl.self_id); type_iter.Add(impl.interface); for (auto done = false; !done;) { auto step = type_iter.Next(); using Step = SemIR::TypeIterator::Step; CARBON_KIND_SWITCH(step.any) { case CARBON_KIND(Step::ClassStart start): { auto inst_id = context.classes().Get(start.class_id).definition_id; if (IsSameLibrary(context, inst_id)) { return true; } break; } case CARBON_KIND(Step::ClassStartOnly start): { auto inst_id = context.classes().Get(start.class_id).definition_id; if (IsSameLibrary(context, inst_id)) { return true; } break; } case CARBON_KIND(Step::ConcreteType type): { // These are found in a specific when a `GenericClass`, a // `GenericInterface` or a `GenericNamedConstraint` appears. The generic // instruction itself is evaluated to a callable StructValue in the // type, but the specific also contains the callable's type which is one // of these. CARBON_KIND_SWITCH(context.types().GetAsInst(type.type_id)) { case CARBON_KIND(SemIR::GenericClassType class_type): { auto class_id = class_type.class_id; auto inst_id = context.classes().Get(class_id).definition_id; if (IsSameLibrary(context, inst_id)) { return true; } break; } case CARBON_KIND(SemIR::GenericInterfaceType interface_type): { auto interface_id = interface_type.interface_id; auto inst_id = context.interfaces().Get(interface_id).definition_id; if (IsSameLibrary(context, inst_id)) { return true; } break; } case CARBON_KIND(SemIR::GenericNamedConstraintType constraint_type): { auto constraint_id = constraint_type.named_constraint_id; auto inst_id = context.named_constraints().Get(constraint_id).definition_id; if (IsSameLibrary(context, inst_id)) { return true; } break; } default: break; } break; } case CARBON_KIND(Step::Done _): { done = true; break; } default: break; } } CARBON_DIAGNOSTIC(ImplIsOrphan, Error, "orphan `impl` found; something in the self-type or " "constraint must be defined in the same file"); context.emitter().Emit(impl.latest_decl_id, ImplIsOrphan); return false; } // The type structure each non-final `impl` must differ from all other non-final // `impl` for the same interface visible from the file. // // Returns true if an error was diagnosed. static auto DiagnoseNonFinalImplsWithSameTypeStructure(Context& context, const ImplInfo& impl_a, const ImplInfo& impl_b) -> bool { if (impl_a.type_structure == impl_b.type_structure) { CARBON_DIAGNOSTIC(ImplNonFinalSameTypeStructure, Error, "found non-final `impl` with the same type " "structure as another non-final `impl`"); auto builder = context.emitter().Build(impl_b.latest_decl_id, ImplNonFinalSameTypeStructure); CARBON_DIAGNOSTIC(ImplNonFinalSameTypeStructureNote, Note, "other `impl` here"); builder.Note(impl_a.latest_decl_id, ImplNonFinalSameTypeStructureNote); builder.Emit(); return true; } return false; } // An impl's self type and constraint can not match (as a lookup query) // against any final impl, or it would never be used instead of that // final impl. // // Returns true if an error was diagnosed. static auto DiagnoseUnmatchableNonFinalImplWithFinalImpl(Context& context, const ImplInfo& impl_a, const ImplInfo& impl_b) -> bool { auto diagnose_unmatchable_impl = [&](const ImplInfo& query_impl, const ImplInfo& final_impl) -> bool { if (LookupMatchesImpl(context, SemIR::LocId(query_impl.latest_decl_id), context.constant_values().Get(query_impl.self_id), query_impl.interface, final_impl.impl_id)) { CARBON_DIAGNOSTIC(ImplFinalOverlapsNonFinal, Error, "`impl` will never be used"); auto builder = context.emitter().Build(query_impl.latest_decl_id, ImplFinalOverlapsNonFinal); CARBON_DIAGNOSTIC( ImplFinalOverlapsNonFinalNote, Note, "`final impl` declared here would always be used instead"); builder.Note(final_impl.latest_decl_id, ImplFinalOverlapsNonFinalNote); builder.Emit(); return true; } return false; }; CARBON_CHECK(impl_a.is_final || impl_b.is_final); if (impl_b.is_final) { return diagnose_unmatchable_impl(impl_a, impl_b); } else { return diagnose_unmatchable_impl(impl_b, impl_a); } } // Final impls that overlap in their type structure must be in the // same file. // // Returns true if an error was diagnosed. static auto DiagnoseFinalImplsOverlapInDifferentFiles(Context& context, const ImplInfo& impl_a, const ImplInfo& impl_b) -> bool { if (impl_a.ir_id != impl_b.ir_id) { CARBON_DIAGNOSTIC( FinalImplOverlapsDifferentFile, Error, "`final impl` overlaps with `final impl` from another file"); CARBON_DIAGNOSTIC(FinalImplOverlapsDifferentFileNote, Note, "imported `final impl` here"); if (impl_a.is_local) { auto builder = context.emitter().Build(impl_a.latest_decl_id, FinalImplOverlapsDifferentFile); builder.Note(impl_b.latest_decl_id, FinalImplOverlapsDifferentFileNote); builder.Emit(); } else { auto builder = context.emitter().Build(impl_b.latest_decl_id, FinalImplOverlapsDifferentFile); builder.Note(impl_a.latest_decl_id, FinalImplOverlapsDifferentFileNote); builder.Emit(); } return true; } return false; } // Two final impls in the same file can not overlap in their type // structure if they are not in the same match_first block. // // TODO: Support for match_first needed here when they exist in the // toolchain. // // Returns true if an error was diagnosed. static auto DiagnoseFinalImplsOverlapOutsideMatchFirst(Context& context, const ImplInfo& impl_a, const ImplInfo& impl_b) -> bool { if (impl_a.is_local && impl_b.is_local) { CARBON_DIAGNOSTIC(FinalImplOverlapsSameFile, Error, "`final impl` overlaps with `final impl` from same file " "outside a `match_first` block"); auto builder = context.emitter().Build(impl_b.latest_decl_id, FinalImplOverlapsSameFile); CARBON_DIAGNOSTIC(FinalImplOverlapsSameFileNote, Note, "other `final impl` here"); builder.Note(impl_a.latest_decl_id, FinalImplOverlapsSameFileNote); builder.Emit(); return true; } return false; } static auto ValidateImplsForInterface(Context& context, llvm::ArrayRef impls) -> void { // All `impl`s we look at here have the same `InterfaceId` (though different // `SpecificId`s in their `SpecificInterface`s). So we can grab the // `ImportIRId` for the interface a single time up front. auto interface_decl_id = context.interfaces() .Get(impls[0].interface.interface_id) .first_owning_decl_id; auto interface_ir_id = GetIRId(context, interface_decl_id); for (const auto& impl : impls) { if (impl.is_final && impl.is_local) { // ======================================================================= /// Rules for an individual final impl. // ======================================================================= DiagnoseFinalImplNotInSameFileAsRootSelfTypeOrInterface(context, impl, interface_ir_id); } else if (impl.is_local) { DiagnoseOrphanImpl(context, impl, interface_ir_id); } } // TODO: We should revisit this and look for a way to do these checks in less // than quadratic time. From @zygoloid: Possibly by converting the set of // impls into a decision tree. // // For each impl, we compare it pair-wise which each impl found before it, so // that diagnostics are attached to the later impl, as the earlier impl on its // own does not generate a diagnostic. size_t num_impls = impls.size(); for (auto [split_point, impl_b] : llvm::drop_begin(llvm::enumerate(impls))) { // Prevent diagnosing the same error multiple times for the same `impl_b` // against different impls before it. But still ensure we do give one of // each diagnostic when they are different errors. bool did_diagnose_non_final_impls_with_same_type_structure = false; bool did_diagnose_unmatchable_non_final_impl_with_final_impl = false; bool did_diagnose_final_impls_overlap_in_different_files = false; bool did_diagnose_final_impls_overlap_outside_match_first = false; auto impls_before = llvm::drop_end(impls, num_impls - split_point); for (const auto& impl_a : impls_before) { // Don't diagnose structures that contain errors. if (!impl_a.type_structure || !impl_b.type_structure) { continue; } // Only enforce rules when at least one of the impls was written in this // file. if (!impl_a.is_local && !impl_b.is_local) { continue; } if (!impl_a.is_final && !impl_b.is_final) { // ===================================================================== // Rules between two non-final impls. // ===================================================================== if (!did_diagnose_non_final_impls_with_same_type_structure) { // Two impls in separate files will need to have some different // concrete element in their type structure, as enforced by the orphan // rule. So we don't need to check against non-local impls. if (impl_a.is_local && impl_b.is_local) { if (DiagnoseNonFinalImplsWithSameTypeStructure(context, impl_a, impl_b)) { // The same final `impl_a` may overlap with multiple `impl_b`s, // and we want to diagnose each `impl_b`. did_diagnose_non_final_impls_with_same_type_structure = true; } } } } else if (!impl_a.is_final || !impl_b.is_final) { // ===================================================================== // Rules between final impl and non-final impl. // ===================================================================== if (!did_diagnose_unmatchable_non_final_impl_with_final_impl) { if (DiagnoseUnmatchableNonFinalImplWithFinalImpl(context, impl_a, impl_b)) { did_diagnose_unmatchable_non_final_impl_with_final_impl = true; } } } else if (impl_a.type_structure->CompareStructure( TypeStructure::CompareTest::HasOverlap, *impl_b.type_structure)) { // ===================================================================== // Rules between two overlapping final impls. // ===================================================================== CARBON_CHECK(impl_a.is_final && impl_b.is_final); if (!did_diagnose_final_impls_overlap_in_different_files) { if (DiagnoseFinalImplsOverlapInDifferentFiles(context, impl_a, impl_b)) { did_diagnose_final_impls_overlap_in_different_files = true; } } if (!did_diagnose_final_impls_overlap_outside_match_first) { if (DiagnoseFinalImplsOverlapOutsideMatchFirst(context, impl_a, impl_b)) { did_diagnose_final_impls_overlap_outside_match_first = true; } } } } } } // For each `impl` seen in this file, ensure that we import every available // `final impl` for the same interface, so that we can to check for // diagnostics about the relationship between them and the `impl`s in this // file. static auto ImportFinalImplsWithImplInFile(Context& context) -> void { struct InterfaceToImport { SemIR::ImportIRId ir_id; SemIR::InterfaceId interface_id; constexpr auto operator==(const InterfaceToImport& rhs) const -> bool = default; constexpr auto operator<=>(const InterfaceToImport& rhs) const -> auto { if (ir_id != rhs.ir_id) { return ir_id.index <=> rhs.ir_id.index; } return interface_id.index <=> rhs.interface_id.index; } }; llvm::SmallVector interfaces_to_import; for (const auto& impl : context.impls().values()) { if (impl.witness_id == SemIR::ErrorInst::InstId) { continue; } auto impl_import_ir_id = GetIRId(context, impl.first_owning_decl_id); if (impl_import_ir_id.has_value()) { // Only import `impl`s of interfaces for which there is a local `impl` of // that that interface. continue; } auto interface_id = impl.interface.interface_id; const auto& interface = context.interfaces().Get(interface_id); if (!interface.first_owning_decl_id.has_value()) { continue; } const auto& import_ir_inst = GetCanonicalImportIRInst(context, interface.first_owning_decl_id); if (!import_ir_inst.ir_id().has_value()) { continue; } interfaces_to_import.push_back( {.ir_id = import_ir_inst.ir_id(), .interface_id = context.import_irs() .Get(import_ir_inst.ir_id()) .sem_ir->insts() .GetAs(import_ir_inst.inst_id()) .interface_id}); } llvm::sort(interfaces_to_import); llvm::unique(interfaces_to_import); struct ImplToImport { SemIR::ImportIRId ir_id; SemIR::ImplId import_impl_id; constexpr auto operator==(const ImplToImport& rhs) const -> bool = default; constexpr auto operator<=>(const ImplToImport& rhs) const -> auto { if (ir_id != rhs.ir_id) { return ir_id.index <=> rhs.ir_id.index; } return import_impl_id.index <=> rhs.import_impl_id.index; } }; llvm::SmallVector impls_to_import; for (auto [ir_id, interface_id] : interfaces_to_import) { const SemIR::File& sem_ir = *context.import_irs().Get(ir_id).sem_ir; for (auto [impl_id, impl] : sem_ir.impls().enumerate()) { if (impl.is_final && impl.interface.interface_id == interface_id) { impls_to_import.push_back({.ir_id = ir_id, .import_impl_id = impl_id}); } } } llvm::sort(impls_to_import); llvm::unique(impls_to_import); for (auto [ir_id, import_impl_id] : impls_to_import) { ImportImpl(context, ir_id, import_impl_id); } } auto ValidateImplsInFile(Context& context) -> void { ImportFinalImplsWithImplInFile(context); // Collect all of the impls sorted into contiguous segments by their // interface. We only need to compare impls within each such segment. We don't // keep impls with an Error in them, as they may be missing other values // needed to check the diagnostics and they already have a diagnostic printed // about them anyhow. We also verify the impl has an `InterfaceId` since it // can be missing, in which case a diagnostic would have been generated // already as well. llvm::SmallVector impl_ids_by_interface(llvm::map_range( llvm::make_filter_range( context.impls().enumerate(), [](std::pair pair) { return pair.second.witness_id != SemIR::ErrorInst::InstId && pair.second.interface.interface_id.has_value(); }), [&](std::pair pair) { return GetImplInfo(context, pair.first); })); llvm::stable_sort(impl_ids_by_interface, [](const ImplInfo& lhs, const ImplInfo& rhs) { return lhs.interface.interface_id.index < rhs.interface.interface_id.index; }); const auto* it = impl_ids_by_interface.begin(); while (it != impl_ids_by_interface.end()) { const auto* segment_begin = it; auto begin_interface_id = segment_begin->interface.interface_id; do { ++it; } while (it != impl_ids_by_interface.end() && it->interface.interface_id == begin_interface_id); const auto* segment_end = it; ValidateImplsForInterface(context, {segment_begin, segment_end}); } } } // namespace Carbon::Check ================================================ FILE: toolchain/check/impl_validation.h ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #ifndef CARBON_TOOLCHAIN_CHECK_IMPL_VALIDATION_H_ #define CARBON_TOOLCHAIN_CHECK_IMPL_VALIDATION_H_ #include "toolchain/check/context.h" namespace Carbon::Check { // Called after typechecking the full file to diagnose any `impl` declarations // that are invalid because they are in wrong file or overlap with other `impl` // declarations incorrectly. auto ValidateImplsInFile(Context& context) -> void; } // namespace Carbon::Check #endif // CARBON_TOOLCHAIN_CHECK_IMPL_VALIDATION_H_ ================================================ FILE: toolchain/check/import.cpp ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #include "toolchain/check/import.h" #include #include #include "common/check.h" #include "common/map.h" #include "toolchain/base/kind_switch.h" #include "toolchain/check/context.h" #include "toolchain/check/import_ref.h" #include "toolchain/check/inst.h" #include "toolchain/check/merge.h" #include "toolchain/check/name_lookup.h" #include "toolchain/check/type.h" #include "toolchain/parse/node_ids.h" #include "toolchain/sem_ir/file.h" #include "toolchain/sem_ir/ids.h" #include "toolchain/sem_ir/import_ir.h" #include "toolchain/sem_ir/inst.h" #include "toolchain/sem_ir/name_scope.h" #include "toolchain/sem_ir/typed_insts.h" namespace Carbon::Check { // Returns name information for an EntityWithParamsBase. template static auto GetImportNameForEntity(const T& entity) -> std::pair { return {entity.name_id, entity.parent_scope_id}; } template <> auto GetImportNameForEntity(const SemIR::NameScope& entity) -> std::pair { return {entity.name_id(), entity.parent_scope_id()}; } // Returns name information for the entity, corresponding to IDs in the import // IR rather than the current IR. static auto GetImportName(const SemIR::File& import_sem_ir, SemIR::Inst import_inst) -> std::pair { CARBON_KIND_SWITCH(import_inst) { case SemIR::AliasBinding::Kind: case SemIR::RefBinding::Kind: case SemIR::SymbolicBinding::Kind: case SemIR::ValueBinding::Kind: case SemIR::ExportDecl::Kind: { auto bind_inst = import_inst.As(); return GetImportNameForEntity( import_sem_ir.entity_names().Get(bind_inst.entity_name_id)); } case CARBON_KIND(SemIR::ClassDecl class_decl): { return GetImportNameForEntity( import_sem_ir.classes().Get(class_decl.class_id)); } case CARBON_KIND(SemIR::FunctionDecl function_decl): { return GetImportNameForEntity( import_sem_ir.functions().Get(function_decl.function_id)); } case CARBON_KIND(SemIR::InterfaceDecl interface_decl): { return GetImportNameForEntity( import_sem_ir.interfaces().Get(interface_decl.interface_id)); } case CARBON_KIND(SemIR::Namespace ns): { return GetImportNameForEntity( import_sem_ir.name_scopes().Get(ns.name_scope_id)); } case CARBON_KIND(SemIR::NamedConstraintDecl named_constraint_decl): { return GetImportNameForEntity(import_sem_ir.named_constraints().Get( named_constraint_decl.named_constraint_id)); } default: CARBON_FATAL("Unsupported export kind: {0}", import_inst); } } // Translate the name to the current IR. It will usually be an identifier, but // could also be a builtin name ID which is equivalent cross-IR. static auto CopyNameFromImportIR(Context& context, const SemIR::File& import_sem_ir, SemIR::NameId import_name_id) -> SemIR::NameId { if (auto import_identifier_id = import_name_id.AsIdentifierId(); import_identifier_id.has_value()) { auto name = import_sem_ir.identifiers().Get(import_identifier_id); return SemIR::NameId::ForIdentifier(context.identifiers().Add(name)); } return import_name_id; } // Returns the LocIdAndInst for the namespace. static auto MakeImportedNamespaceLocIdAndInst(Context& context, SemIR::InstId import_id, SemIR::Namespace namespace_inst) -> SemIR::LocIdAndInst { if (!import_id.has_value()) { // TODO: Associate the namespace with a proper location. This is related to: // https://github.com/carbon-language/carbon-lang/issues/4666. return SemIR::LocIdAndInst::NoLoc(namespace_inst); } // If the import was itself imported, use its location. if (auto import_ir_inst_id = context.insts().GetImportSource(import_id); import_ir_inst_id.has_value()) { return MakeImportedLocIdAndInst(context, import_ir_inst_id, namespace_inst); } // Otherwise we should have a node location for some kind of namespace // declaration in the current file. SemIR::LocId import_loc_id = context.insts().GetCanonicalLocId(import_id); switch (import_loc_id.kind()) { case SemIR::LocId::Kind::NodeId: return SemIR::LocIdAndInst(context.parse_tree().As( import_loc_id.node_id()), namespace_inst); case SemIR::LocId::Kind::None: // TODO: Either document the use-case for this, or require a location. return SemIR::LocIdAndInst::NoLoc(namespace_inst); case SemIR::LocId::Kind::ImportIRInstId: case SemIR::LocId::Kind::InstId: CARBON_FATAL("Unexpected LocId kind"); } } auto AddImportNamespace(Context& context, SemIR::TypeId namespace_type_id, SemIR::NameId name_id, SemIR::NameScopeId parent_scope_id, SemIR::InstId import_id) -> AddImportNamespaceResult { auto namespace_inst = SemIR::Namespace{.type_id = namespace_type_id, .name_scope_id = SemIR::NameScopeId::None, .import_id = import_id}; auto namespace_inst_and_loc = MakeImportedNamespaceLocIdAndInst(context, import_id, namespace_inst); AddImportNamespaceResult result = { .name_scope_id = SemIR::NameScopeId::None, .inst_id = AddPlaceholderImportedInstInNoBlock(context, namespace_inst_and_loc)}; namespace_inst.name_scope_id = context.name_scopes().Add(result.inst_id, name_id, parent_scope_id); result.name_scope_id = namespace_inst.name_scope_id; ReplaceInstBeforeConstantUse(context, result.inst_id, namespace_inst); return result; } auto AddImportNamespaceToScope( Context& context, SemIR::TypeId namespace_type_id, SemIR::NameId name_id, SemIR::NameScopeId parent_scope_id, bool diagnose_duplicate_namespace, llvm::function_ref make_import_id) -> AddImportNamespaceToScopeResult { auto& parent_scope = context.name_scopes().Get(parent_scope_id); auto [inserted, entry_id] = parent_scope.LookupOrAdd( name_id, // This InstId is temporary and would be overridden if used. SemIR::InstId::None, SemIR::AccessKind::Public); if (!inserted) { const auto& prev_entry = parent_scope.GetEntry(entry_id); if (!prev_entry.result.is_poisoned()) { auto prev_inst_id = prev_entry.result.target_inst_id(); if (auto namespace_inst = context.insts().TryGetAs(prev_inst_id)) { if (diagnose_duplicate_namespace) { auto import_id = make_import_id(); CARBON_CHECK(import_id.has_value()); // TODO: Pass the import package name location instead of the import // id to get more accurate location. DiagnoseDuplicateName(context, name_id, SemIR::LocId(import_id), SemIR::LocId(prev_inst_id)); } return {.add_result = {.name_scope_id = namespace_inst->name_scope_id, .inst_id = prev_inst_id}, .is_duplicate_of_namespace_in_current_package = true}; } } } auto import_id = make_import_id(); CARBON_CHECK(import_id.has_value()); AddImportNamespaceToScopeResult result = { .add_result = AddImportNamespace(context, namespace_type_id, name_id, parent_scope_id, import_id), .is_duplicate_of_namespace_in_current_package = false}; // Diagnose if there's a name conflict, but still produce the namespace to // supersede the name conflict in order to avoid repeat diagnostics. Names // are poisoned optimistically by name lookup before checking for imports, // so we may be overwriting a poisoned entry here. auto& lookup_result = parent_scope.GetEntry(entry_id).result; if (!lookup_result.is_poisoned() && !inserted) { // TODO: Pass the import namespace name location instead of the namespace // id to get more accurate location. DiagnoseDuplicateName(context, name_id, SemIR::LocId(result.add_result.inst_id), SemIR::LocId(lookup_result.target_inst_id())); } lookup_result = SemIR::ScopeLookupResult::MakeFound( result.add_result.inst_id, SemIR::AccessKind::Public); return result; } // Adds a copied namespace to the cache. static auto CacheCopiedNamespace( Map& copied_namespaces, SemIR::NameScopeId import_scope_id, SemIR::NameScopeId to_scope_id) -> void { auto result = copied_namespaces.Insert(import_scope_id, to_scope_id); CARBON_CHECK(result.is_inserted() || result.value() == to_scope_id, "Copy result for namespace changed from {0} to {1}", import_scope_id, to_scope_id); } // Copies a namespace from the import IR, returning its ID. This may diagnose // name conflicts, but that won't change the result because namespaces supersede // other names in conflicts. The bool on return is true if there was a name // conflict. copied_namespaces is optional. static auto CopySingleNameScopeFromImportIR( Context& context, SemIR::TypeId namespace_type_id, Map* copied_namespaces, SemIR::ImportIRId ir_id, SemIR::InstId import_inst_id, SemIR::NameScopeId import_scope_id, SemIR::NameScopeId parent_scope_id, SemIR::NameId name_id) -> AddImportNamespaceToScopeResult { // Produce the namespace for the entry. auto make_import_id = [&]() { auto entity_name_id = context.entity_names().Add( {.name_id = name_id, .parent_scope_id = parent_scope_id}); auto import_ir_inst_id = context.import_ir_insts().Add( SemIR::ImportIRInst(ir_id, import_inst_id)); auto inst_id = AddInstInNoBlock( context, MakeImportedLocIdAndInst( context, import_ir_inst_id, {.type_id = namespace_type_id, .import_ir_inst_id = import_ir_inst_id, .entity_name_id = entity_name_id})); context.imports().push_back(inst_id); return inst_id; }; AddImportNamespaceToScopeResult result = AddImportNamespaceToScope( context, namespace_type_id, name_id, parent_scope_id, /*diagnose_duplicate_namespace=*/false, make_import_id); auto namespace_const_id = context.constant_values().Get(result.add_result.inst_id); context .import_ir_constant_values()[context.sem_ir().import_irs().GetRawIndex( ir_id)] .Set(import_inst_id, namespace_const_id); if (copied_namespaces) { CacheCopiedNamespace(*copied_namespaces, import_scope_id, result.add_result.name_scope_id); } return result; } // Copies ancestor name scopes from the import IR. Handles the parent traversal. // Returns the NameScope corresponding to the copied import_parent_scope_id. static auto CopyAncestorNameScopesFromImportIR( Context& context, SemIR::TypeId namespace_type_id, const SemIR::File& import_sem_ir, SemIR::ImportIRId ir_id, SemIR::NameScopeId import_parent_scope_id, Map& copied_namespaces) -> SemIR::NameScopeId { // Package-level names don't need work. if (import_parent_scope_id == SemIR::NameScopeId::Package) { return import_parent_scope_id; } // The scope to add namespaces to. Note this may change while looking at // parent scopes, if we encounter a namespace that's already added. auto scope_cursor = SemIR::NameScopeId::Package; // Build a stack of ancestor namespace names, with the immediate parent first. llvm::SmallVector new_namespaces; while (import_parent_scope_id != SemIR::NameScopeId::Package) { // If the namespace was already copied, reuse the results. if (auto result = copied_namespaces.Lookup(import_parent_scope_id)) { // We inject names at the provided scope, and don't need to keep // traversing parents. scope_cursor = result.value(); break; } // The namespace hasn't been copied yet, so add it to our list. const auto& scope = import_sem_ir.name_scopes().Get(import_parent_scope_id); auto scope_inst = import_sem_ir.insts().GetAs(scope.inst_id()); new_namespaces.push_back(scope_inst.name_scope_id); import_parent_scope_id = scope.parent_scope_id(); } // Add ancestor namespace names, starting with the outermost. for (auto import_scope_id : llvm::reverse(new_namespaces)) { const auto& import_scope = import_sem_ir.name_scopes().Get(import_scope_id); auto name_id = CopyNameFromImportIR(context, import_sem_ir, import_scope.name_id()); scope_cursor = CopySingleNameScopeFromImportIR( context, namespace_type_id, &copied_namespaces, ir_id, import_scope.inst_id(), import_scope_id, scope_cursor, name_id) .add_result.name_scope_id; } return scope_cursor; } // Imports the function if it's a non-owning declaration with the current file // as owner. static auto LoadImportForOwningFunction(Context& context, const SemIR::File& import_sem_ir, const SemIR::Function& function, SemIR::InstId import_ref) { if (!function.extern_library_id.has_value()) { return; } CARBON_CHECK(function.is_extern && "Expected extern functions"); auto lib_id = function.extern_library_id; bool is_lib_default = lib_id == SemIR::LibraryNameId::Default; auto current_id = context.sem_ir().library_id(); bool is_current_default = current_id == SemIR::LibraryNameId::Default; if (is_lib_default == is_current_default) { if (is_lib_default) { // Both libraries are default, import ref. LoadImportRef(context, import_ref); } else { // Both libraries are non-default: check if they're the same named // library, import ref if yes. auto str_owner_library = context.string_literal_values().Get( current_id.AsStringLiteralValueId()); auto str_decl_library = import_sem_ir.string_literal_values().Get( lib_id.AsStringLiteralValueId()); if (str_owner_library == str_decl_library) { LoadImportRef(context, import_ref); } } } } // Adds an ImportRef for an entity, handling merging if needed. static auto AddImportRefOrMerge(Context& context, SemIR::ImportIRId ir_id, const SemIR::File& import_sem_ir, SemIR::InstId import_inst_id, SemIR::NameScopeId parent_scope_id, SemIR::NameId name_id) -> void { // Leave a placeholder that the inst comes from the other IR. auto& parent_scope = context.name_scopes().Get(parent_scope_id); auto [inserted, entry_id] = parent_scope.LookupOrAdd( name_id, // This InstId is temporary and would be overridden if used. SemIR::InstId::None, SemIR::AccessKind::Public); auto& entry = parent_scope.GetEntry(entry_id); if (inserted) { auto entity_name_id = context.entity_names().Add( {.name_id = name_id, .parent_scope_id = parent_scope_id}); auto import_ref = AddImportRef( context, SemIR::ImportIRInst(ir_id, import_inst_id), entity_name_id); entry.result = SemIR::ScopeLookupResult::MakeFound( import_ref, SemIR::AccessKind::Public); // Import references for non-owning declarations that match current library. if (auto function_decl = import_sem_ir.insts().TryGetAs( import_inst_id)) { LoadImportForOwningFunction( context, import_sem_ir, import_sem_ir.functions().Get(function_decl->function_id), import_ref); } return; } auto inst_id = entry.result.target_inst_id(); auto prev_ir_inst = GetCanonicalImportIRInst(context, inst_id); VerifySameCanonicalImportIRInst(context, name_id, inst_id, prev_ir_inst, ir_id, &import_sem_ir, import_inst_id); } namespace { // A scope in the API file that still needs to be copied to the implementation // file. Only used for API file imports. struct TodoScope { // The scope's instruction in the API file. SemIR::InstId api_inst_id; // The scope in the API file. SemIR::NameScopeId api_scope_id; // The already-translated scope name in the implementation file. SemIR::NameId impl_name_id; // The already-copied parent scope in the implementation file. SemIR::NameScopeId impl_parent_scope_id; }; } // namespace // Adds an ImportRef to a name scope. static auto AddScopedImportRef(Context& context, SemIR::NameScopeId parent_scope_id, SemIR::NameScope& parent_scope, SemIR::NameId name_id, SemIR::ImportIRInst import_inst, SemIR::AccessKind access_kind) -> SemIR::InstId { // Add an ImportRef for other instructions. auto impl_entity_name_id = context.entity_names().Add( {.name_id = name_id, .parent_scope_id = parent_scope_id}); auto import_ref_id = AddImportRef(context, import_inst, impl_entity_name_id); parent_scope.AddRequired({.name_id = name_id, .result = SemIR::ScopeLookupResult::MakeFound( import_ref_id, access_kind)}); return import_ref_id; } // Imports entries in a specific scope into the current file. static auto ImportScopeFromApiFile(Context& context, const SemIR::File& api_sem_ir, SemIR::NameScopeId api_scope_id, SemIR::NameScopeId impl_scope_id, llvm::SmallVector& todo_scopes) -> void { const auto& api_scope = api_sem_ir.name_scopes().Get(api_scope_id); auto& impl_scope = context.name_scopes().Get(impl_scope_id); for (const auto& api_entry : api_scope.entries()) { if (api_entry.result.is_poisoned()) { continue; } auto impl_name_id = CopyNameFromImportIR(context, api_sem_ir, api_entry.name_id); if (auto ns = api_sem_ir.insts().TryGetAs( api_entry.result.target_inst_id())) { // Ignore cross-package imports. These will be handled through // ImportLibrariesFromOtherPackage. if (api_scope_id == SemIR::NameScopeId::Package) { const auto& ns_scope = api_sem_ir.name_scopes().Get(ns->name_scope_id); if (!ns_scope.import_ir_scopes().empty()) { continue; } } // Namespaces will be recursed into. Name scope creation is delayed in // order to avoid invalidating api_scope/impl_scope. todo_scopes.push_back({.api_inst_id = api_entry.result.target_inst_id(), .api_scope_id = ns->name_scope_id, .impl_name_id = impl_name_id, .impl_parent_scope_id = impl_scope_id}); } else { // Add an ImportRef for other instructions. AddScopedImportRef(context, impl_scope_id, impl_scope, impl_name_id, SemIR::ImportIRInst(SemIR::ImportIRId::ApiForImpl, api_entry.result.target_inst_id()), api_entry.result.access_kind()); } } } auto ImportApiFile(Context& context, SemIR::TypeId namespace_type_id, const SemIR::File& api_sem_ir) -> void { context.import_ir_constant_values()[SemIR::ImportIRId::ApiForImpl.index].Set( SemIR::Namespace::PackageInstId, context.constant_values().Get(SemIR::Namespace::PackageInstId)); llvm::SmallVector todo_scopes = {}; ImportScopeFromApiFile(context, api_sem_ir, SemIR::NameScopeId::Package, SemIR::NameScopeId::Package, todo_scopes); while (!todo_scopes.empty()) { auto todo_scope = todo_scopes.pop_back_val(); auto impl_scope_id = CopySingleNameScopeFromImportIR( context, namespace_type_id, /*copied_namespaces=*/nullptr, SemIR::ImportIRId::ApiForImpl, todo_scope.api_inst_id, todo_scope.api_scope_id, todo_scope.impl_parent_scope_id, todo_scope.impl_name_id) .add_result.name_scope_id; ImportScopeFromApiFile(context, api_sem_ir, todo_scope.api_scope_id, impl_scope_id, todo_scopes); } } auto ImportLibrariesFromCurrentPackage( Context& context, SemIR::TypeId namespace_type_id, llvm::ArrayRef import_irs) -> void { for (auto import_ir : import_irs) { auto ir_id = AddImportIR(context, import_ir); context .import_ir_constant_values()[context.sem_ir().import_irs().GetRawIndex( ir_id)] .Set(SemIR::Namespace::PackageInstId, context.constant_values().Get(SemIR::Namespace::PackageInstId)); for (const auto import_inst_id : import_ir.sem_ir->inst_blocks().Get(SemIR::InstBlockId::Exports)) { auto import_inst = import_ir.sem_ir->insts().Get(import_inst_id); auto [import_name_id, import_parent_scope_id] = GetImportName(*import_ir.sem_ir, import_inst); Map copied_namespaces; auto name_id = CopyNameFromImportIR(context, *import_ir.sem_ir, import_name_id); SemIR::NameScopeId parent_scope_id = CopyAncestorNameScopesFromImportIR( context, namespace_type_id, *import_ir.sem_ir, ir_id, import_parent_scope_id, copied_namespaces); if (auto import_namespace_inst = import_inst.TryAs()) { // Namespaces are always imported because they're essential for // qualifiers, and the type is simple. CopySingleNameScopeFromImportIR( context, namespace_type_id, &copied_namespaces, ir_id, import_inst_id, import_namespace_inst->name_scope_id, parent_scope_id, name_id); } else { AddImportRefOrMerge(context, ir_id, *import_ir.sem_ir, import_inst_id, parent_scope_id, name_id); } } // If an import of the current package caused an error for the imported // file, it transitively affects the current file too. if (import_ir.sem_ir->name_scopes() .Get(SemIR::NameScopeId::Package) .has_error()) { context.name_scopes().Get(SemIR::NameScopeId::Package).set_has_error(); } } } auto ImportLibrariesFromOtherPackage(Context& context, SemIR::TypeId namespace_type_id, SemIR::InstId import_decl_id, PackageNameId package_id, llvm::ArrayRef import_irs, bool has_load_error) -> void { CARBON_CHECK(has_load_error || !import_irs.empty(), "There should be either a load error or at least one IR."); auto name_id = SemIR::NameId::ForPackageName(package_id); AddImportNamespaceToScopeResult result = AddImportNamespaceToScope( context, namespace_type_id, name_id, SemIR::NameScopeId::Package, /*diagnose_duplicate_namespace=*/true, [&] { return import_decl_id; }); auto namespace_const_id = context.constant_values().Get(result.add_result.inst_id); auto& scope = context.name_scopes().Get(result.add_result.name_scope_id); scope.set_is_closed_import( !result.is_duplicate_of_namespace_in_current_package); for (auto import_ir : import_irs) { auto ir_id = AddImportIR(context, import_ir); scope.AddImportIRScope({ir_id, SemIR::NameScopeId::Package}); context .import_ir_constant_values()[context.sem_ir().import_irs().GetRawIndex( ir_id)] .Set(SemIR::Namespace::PackageInstId, namespace_const_id); } if (has_load_error) { scope.set_has_error(); } } // Looks up a name in a scope imported from another package. An `identifier` is // provided if `name_id` corresponds to an identifier in the current file; // otherwise, `name_id` is file-agnostic and can be used directly. static auto LookupNameInImport(const SemIR::File& import_ir, SemIR::NameScopeId import_scope_id, SemIR::NameId name_id, llvm::StringRef identifier) -> const Carbon::SemIR::NameScope::Entry* { // Determine the NameId in the import IR. SemIR::NameId import_name_id = name_id; if (!identifier.empty()) { auto import_identifier_id = import_ir.identifiers().Lookup(identifier); if (!import_identifier_id.has_value()) { // Name doesn't exist in the import IR. return nullptr; } import_name_id = SemIR::NameId::ForIdentifier(import_identifier_id); } // Look up the name in the import scope. const auto& import_scope = import_ir.name_scopes().Get(import_scope_id); auto import_scope_entry_id = import_scope.Lookup(import_name_id); if (!import_scope_entry_id) { // Name doesn't exist in the import scope. return nullptr; } const auto& import_scope_entry = import_scope.GetEntry(*import_scope_entry_id); if (import_scope_entry.result.access_kind() != SemIR::AccessKind::Public) { // Ignore cross-package non-public names. return nullptr; } return &import_scope_entry; } // Adds a namespace that points to one in another package. static auto AddNamespaceFromOtherPackage(Context& context, SemIR::ImportIRId import_ir_id, SemIR::InstId import_inst_id, SemIR::Namespace import_ns, SemIR::NameScopeId parent_scope_id, SemIR::NameId name_id) -> SemIR::InstId { auto namespace_type_id = GetSingletonType(context, SemIR::NamespaceType::TypeInstId); AddImportNamespaceToScopeResult result = CopySingleNameScopeFromImportIR( context, namespace_type_id, /*copied_namespaces=*/nullptr, import_ir_id, import_inst_id, import_ns.name_scope_id, parent_scope_id, name_id); auto& scope = context.name_scopes().Get(result.add_result.name_scope_id); scope.set_is_closed_import( !result.is_duplicate_of_namespace_in_current_package); scope.AddImportIRScope({import_ir_id, import_ns.name_scope_id}); return result.add_result.inst_id; } auto ImportNameFromOtherPackage( Context& context, SemIR::LocId loc_id, SemIR::NameScopeId scope_id, llvm::ArrayRef> import_ir_scopes, SemIR::NameId name_id) -> SemIR::InstId { // If the name is an identifier, get the string first so that it can be shared // when there are multiple IRs. llvm::StringRef identifier; if (auto identifier_id = name_id.AsIdentifierId(); identifier_id.has_value()) { identifier = context.identifiers().Get(identifier_id); CARBON_CHECK(!identifier.empty()); } // Annotate diagnostics as occurring during this name lookup. Diagnostics::AnnotationScope annotate_diagnostics( &context.emitter(), [&](auto& builder) { CARBON_DIAGNOSTIC(InNameLookup, Note, "in name lookup for `{0}`", SemIR::NameId); builder.Note(loc_id, InNameLookup, name_id); }); // Although we track the result here and look in each IR, we pretty much use // the first result. auto result_id = SemIR::InstId::None; // The canonical IR and inst_id for where `result_id` came from, which may be // indirectly imported. This is only resolved on a conflict, when it can be // used to determine the conflict is actually the same instruction. std::optional canonical_result_inst; for (auto [import_ir_id, import_scope_id] : import_ir_scopes) { auto& import_ir = context.import_irs().Get(import_ir_id); const auto* import_scope_entry = LookupNameInImport( *import_ir.sem_ir, import_scope_id, name_id, identifier); if (!import_scope_entry || !import_scope_entry->result.is_found()) { continue; } SemIR::InstId import_scope_inst_id = import_scope_entry->result.target_inst_id(); auto import_inst = import_ir.sem_ir->insts().Get(import_scope_inst_id); if (import_inst.Is()) { // This entity was added to name lookup by using an import, and is not // exported. continue; } // Add the first result found. if (!result_id.has_value()) { // If the imported instruction is a namespace, we add it directly instead // of as an ImportRef. if (auto import_ns = import_inst.TryAs()) { result_id = AddNamespaceFromOtherPackage(context, import_ir_id, import_scope_inst_id, *import_ns, scope_id, name_id); } else { result_id = AddScopedImportRef( context, scope_id, context.name_scopes().Get(scope_id), name_id, SemIR::ImportIRInst(import_ir_id, import_scope_inst_id), SemIR::AccessKind::Public); LoadImportRef(context, result_id); } continue; } // When namespaces collide between files, merge lookup in the scopes. if (auto import_ns = import_inst.TryAs()) { if (auto ns = context.insts().TryGetAs(result_id)) { auto& name_scope = context.name_scopes().Get(ns->name_scope_id); name_scope.AddImportIRScope({import_ir_id, import_ns->name_scope_id}); continue; } } // When there's a name collision, they need to either be the same canonical // instruction, or we'll diagnose. if (!canonical_result_inst) { canonical_result_inst = GetCanonicalImportIRInst(context, result_id); } VerifySameCanonicalImportIRInst(context, name_id, result_id, *canonical_result_inst, import_ir_id, import_ir.sem_ir, import_scope_inst_id); } return result_id; } // Returns whether a parse node associated with an imported instruction of kind // `imported_kind` is usable as the location of a corresponding local // instruction of kind `local_kind`. static auto HasCompatibleImportedNodeKind(SemIR::InstKind imported_kind, SemIR::InstKind local_kind) -> bool { if (imported_kind == local_kind) { return true; } if (imported_kind == SemIR::ImportDecl::Kind && local_kind == SemIR::Namespace::Kind) { static_assert( std::is_convertible_v); return true; } return false; } namespace Internal { auto CheckCompatibleImportedNodeKind(Context& context, SemIR::ImportIRInstId imported_loc_id, SemIR::InstKind kind) -> void { auto& import_ir_inst = context.import_ir_insts().Get(imported_loc_id); if (import_ir_inst.ir_id() == SemIR::ImportIRId::Cpp) { // We don't require a matching node kind if the location is in C++, because // there isn't a node. return; } const auto* import_ir = context.import_irs().Get(import_ir_inst.ir_id()).sem_ir; auto imported_kind = import_ir->insts().Get(import_ir_inst.inst_id()).kind(); CARBON_CHECK( HasCompatibleImportedNodeKind(imported_kind, kind), "Node of kind {0} created with location of imported node of kind {1}", kind, imported_kind); } } // namespace Internal } // namespace Carbon::Check ================================================ FILE: toolchain/check/import.h ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #ifndef CARBON_TOOLCHAIN_CHECK_IMPORT_H_ #define CARBON_TOOLCHAIN_CHECK_IMPORT_H_ #include "toolchain/check/context.h" #include "toolchain/parse/node_ids.h" #include "toolchain/sem_ir/file.h" namespace Carbon::Check { struct AddImportNamespaceResult { // The namespace scope id. SemIR::NameScopeId name_scope_id; // The namespace instruction id. SemIR::InstId inst_id; }; // Adds a namespace to the IR. Associates the namespace with the import referred // in `import_id`, if any. Do not try to add the namespace `name_id` to the // scope and don't check if it already exists. Used when the name is added // afterwards. auto AddImportNamespace(Context& context, SemIR::TypeId namespace_type_id, SemIR::NameId name_id, SemIR::NameScopeId parent_scope_id, SemIR::InstId import_id) -> AddImportNamespaceResult; struct AddImportNamespaceToScopeResult { AddImportNamespaceResult add_result; // When trying to add the namespace name, whether it already exists and refers // to a namespace. bool is_duplicate_of_namespace_in_current_package = false; }; // Adds a namespace to the IR. Associates the namespace with the import returned // from `make_import_id`, which must return a value. Tries to add the namespace // `name_id` to the scope. If the name already exists, diagnose only if // `diagnose_duplicate_namespace` is true or if it doesn't refer to a namespace. // `diagnose_duplicate_namespace` is used when handling a cross-package import, // where an existing namespace is in the current package and the new namespace // is a different package. auto AddImportNamespaceToScope( Context& context, SemIR::TypeId namespace_type_id, SemIR::NameId name_id, SemIR::NameScopeId parent_scope_id, bool diagnose_duplicate_namespace, llvm::function_ref make_import_id) -> AddImportNamespaceToScopeResult; // Imports the API file's name lookup information into a corresponding // implementation file. Only information for the current package will be copied; // information for other packages should be handled through // ImportLibrariesFromOtherPackage. auto ImportApiFile(Context& context, SemIR::TypeId namespace_type_id, const SemIR::File& api_sem_ir) -> void; // Add the current package's imports to name lookup. This pulls in all names; // conflicts for things such as `package.a.b.c` will be flagged even though they // are several layers deep. auto ImportLibrariesFromCurrentPackage( Context& context, SemIR::TypeId namespace_type_id, llvm::ArrayRef import_irs) -> void; // Adds another package's imports to name lookup. This only adds the package // name to lookup, so that `package.ImportedPackage` will resolve, and will // provide a name scope that can be used for further qualified name lookups. // // import_irs may be empty. has_load_error is used to indicate if any library in // the package failed to import correctly. auto ImportLibrariesFromOtherPackage(Context& context, SemIR::TypeId namespace_type_id, SemIR::InstId import_decl_id, PackageNameId package_id, llvm::ArrayRef import_irs, bool has_load_error) -> void; // Given a name scope that corresponds to another package (having one or more // `import_irs`), looks for the name in imports. Name resolution results are // added to the scope, and the `InstId` (possibly `None`) is returned. // // In general, this will add an `ImportRef` and load it; it's never left // unloaded because the result is expected to be immediately used. Namespaces // will be directly produced, similar to how they function for imports from the // current package. Conflicts will be resolved and diagnosed. // // Arguments are all in the context of the current IR. Scope lookup is expected // to be resolved first. auto ImportNameFromOtherPackage( Context& context, SemIR::LocId loc_id, SemIR::NameScopeId scope_id, llvm::ArrayRef> import_ir_scopes, SemIR::NameId name_id) -> SemIR::InstId; namespace Internal { // Checks that the provided imported location has a node kind that is // compatible with that of the given instruction. auto CheckCompatibleImportedNodeKind(Context& context, SemIR::ImportIRInstId imported_loc_id, SemIR::InstKind kind) -> void; } // namespace Internal // Returns a LocIdAndInst for an instruction with an imported location. Checks // that the imported location is compatible with the kind of instruction being // created. template requires SemIR::Internal::HasNodeId auto MakeImportedLocIdAndInst(Context& context, SemIR::ImportIRInstId imported_loc_id, InstT inst) -> SemIR::LocIdAndInst { if constexpr (!SemIR::Internal::HasUntypedNodeId) { Internal::CheckCompatibleImportedNodeKind(context, imported_loc_id, InstT::Kind); } return SemIR::LocIdAndInst::UncheckedLoc(SemIR::LocId(imported_loc_id), inst); } } // namespace Carbon::Check #endif // CARBON_TOOLCHAIN_CHECK_IMPORT_H_ ================================================ FILE: toolchain/check/import_ref.cpp ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #include "toolchain/check/import_ref.h" #include #include #include #include #include #include "common/check.h" #include "common/growing_range.h" #include "toolchain/base/kind_switch.h" #include "toolchain/base/shared_value_stores.h" #include "toolchain/check/context.h" #include "toolchain/check/eval.h" #include "toolchain/check/facet_type.h" #include "toolchain/check/generic.h" #include "toolchain/check/import.h" #include "toolchain/check/inst.h" #include "toolchain/check/name_lookup.h" #include "toolchain/check/type.h" #include "toolchain/check/type_completion.h" #include "toolchain/parse/node_ids.h" #include "toolchain/sem_ir/constant.h" #include "toolchain/sem_ir/file.h" #include "toolchain/sem_ir/ids.h" #include "toolchain/sem_ir/impl.h" #include "toolchain/sem_ir/import_ir.h" #include "toolchain/sem_ir/inst.h" #include "toolchain/sem_ir/inst_categories.h" #include "toolchain/sem_ir/inst_kind.h" #include "toolchain/sem_ir/name_scope.h" #include "toolchain/sem_ir/specific_interface.h" #include "toolchain/sem_ir/specific_named_constraint.h" #include "toolchain/sem_ir/type_info.h" #include "toolchain/sem_ir/typed_insts.h" namespace Carbon::Check { // Adds the ImportIR, excluding the update to the check_ir_map. static auto InternalAddImportIR(Context& context, SemIR::ImportIR import_ir) -> SemIR::ImportIRId { context.import_ir_constant_values().push_back(SemIR::ConstantValueStore( SemIR::ConstantId::None, import_ir.sem_ir ? &import_ir.sem_ir->insts() : nullptr)); return context.import_irs().Add(import_ir); } // Adds a special-cased IR and verifies it received the correct ID. static auto SetSpecialImportIR(Context& context, SemIR::ImportIR import_ir, SemIR::ImportIRId expected_import_ir_id) -> void { auto ir_id = SemIR::ImportIRId::None; if (import_ir.sem_ir != nullptr) { ir_id = AddImportIR(context, import_ir); } else { // We don't have a check_ir_id, so add without touching check_ir_map. context.import_ir_constant_values().push_back( SemIR::ConstantValueStore(SemIR::ConstantValueStore::Unusable)); ir_id = context.import_irs().Add(import_ir); } CARBON_CHECK(ir_id == expected_import_ir_id, "Actual ImportIRId ({0}) != Expected ImportIRId ({1})", ir_id, expected_import_ir_id); } auto SetSpecialImportIRs(Context& context, SemIR::ImportIR import_ir) -> void { SetSpecialImportIR(context, import_ir, SemIR::ImportIRId::ApiForImpl); SetSpecialImportIR(context, {.decl_id = SemIR::InstId::None, .is_export = false}, SemIR::ImportIRId::Cpp); } auto AddImportIR(Context& context, SemIR::ImportIR import_ir) -> SemIR::ImportIRId { auto& ir_id = context.check_ir_map().Get(import_ir.sem_ir->check_ir_id()); if (!ir_id.has_value()) { // Note this updates check_ir_map. ir_id = InternalAddImportIR(context, import_ir); } else if (import_ir.is_export) { // We're processing an `export import`. In case the IR was indirectly added // as a non-export, mark it as an export. context.import_irs().Get(ir_id).is_export = true; } return ir_id; } auto AddImportRef(Context& context, SemIR::ImportIRInst import_ir_inst, SemIR::EntityNameId entity_name_id = SemIR::EntityNameId::None) -> SemIR::InstId { auto import_ir_inst_id = context.import_ir_insts().Add(import_ir_inst); auto import_ref_id = AddPlaceholderImportedInstInNoBlock( context, MakeImportedLocIdAndInst( context, import_ir_inst_id, SemIR::ImportRefUnloaded{.import_ir_inst_id = import_ir_inst_id, .entity_name_id = entity_name_id})); return import_ref_id; } static auto GetCanonicalImportIRInst(Context& context, const SemIR::File* target_ir, SemIR::InstId target_inst_id) -> SemIR::ImportIRInst { auto [canonical_ir, canonical_inst_id] = GetCanonicalFileAndInstId(target_ir, target_inst_id); auto ir_id = SemIR::ImportIRId::None; if (canonical_ir != &context.sem_ir()) { // This uses AddImportIR in case it was indirectly found, which can // happen with two or more steps of exports. ir_id = AddImportIR(context, {.decl_id = SemIR::InstId::None, .is_export = false, .sem_ir = canonical_ir}); } return SemIR::ImportIRInst(ir_id, canonical_inst_id); } auto GetCanonicalImportIRInst(Context& context, SemIR::InstId inst_id) -> SemIR::ImportIRInst { return GetCanonicalImportIRInst(context, &context.sem_ir(), inst_id); } auto VerifySameCanonicalImportIRInst(Context& context, SemIR::NameId name_id, SemIR::InstId prev_id, SemIR::ImportIRInst prev_import_ir_inst, SemIR::ImportIRId new_ir_id, const SemIR::File* new_import_ir, SemIR::InstId new_inst_id) -> void { auto new_import_ir_inst = GetCanonicalImportIRInst(context, new_import_ir, new_inst_id); if (new_import_ir_inst == prev_import_ir_inst) { return; } auto conflict_id = AddImportRef(context, SemIR::ImportIRInst(new_ir_id, new_inst_id)); // TODO: Pass the imported name location instead of the conflict id. DiagnoseDuplicateName(context, name_id, SemIR::LocId(conflict_id), SemIR::LocId(prev_id)); } namespace { // A context within which we are performing an import. Tracks information about // the source and destination. This provides a restricted interface compared to // ImportResolver: in particular, it does not have access to a work list. // Therefore code that accepts an ImportContext is unable to enqueue new work. class ImportContext { public: // `context` must not be null. explicit ImportContext(Context* context, SemIR::ImportIRId import_ir_id) : context_(context), import_ir_id_(import_ir_id), import_ir_(*context_->import_irs().Get(import_ir_id).sem_ir) {} // Returns the file we are importing from. auto import_ir() -> const SemIR::File& { return import_ir_; } // Accessors into value stores of the file we are importing from. auto import_associated_constants() -> const SemIR::AssociatedConstantStore& { return import_ir().associated_constants(); } auto import_classes() -> const SemIR::ClassStore& { return import_ir().classes(); } auto import_vtables() -> const SemIR::VtableStore& { return import_ir().vtables(); } auto import_constant_values() -> const SemIR::ConstantValueStore& { return import_ir().constant_values(); } auto import_entity_names() -> const SemIR::EntityNameStore& { return import_ir().entity_names(); } auto import_facet_types() -> const SemIR::FacetTypeInfoStore& { return import_ir().facet_types(); } auto import_functions() -> const SemIR::FunctionStore& { return import_ir().functions(); } auto import_generics() -> const SemIR::GenericStore& { return import_ir().generics(); } auto import_identifiers() -> const SharedValueStores::IdentifierStore& { return import_ir().identifiers(); } auto import_impls() -> const SemIR::ImplStore& { return import_ir().impls(); } auto import_inst_blocks() -> const SemIR::InstBlockStore& { return import_ir().inst_blocks(); } auto import_insts() -> const SemIR::InstStore& { return import_ir().insts(); } auto import_interfaces() -> const SemIR::InterfaceStore& { return import_ir().interfaces(); } auto import_named_constraints() -> const SemIR::NamedConstraintStore& { return import_ir().named_constraints(); } auto import_ints() -> const SharedValueStores::IntStore& { return import_ir().ints(); } auto import_name_scopes() -> const SemIR::NameScopeStore& { return import_ir().name_scopes(); } auto import_require_impls() -> const SemIR::RequireImplsStore& { return import_ir().require_impls(); } auto import_require_impls_blocks() -> const SemIR::RequireImplsBlockStore& { return import_ir().require_impls_blocks(); } auto import_specifics() -> const SemIR::SpecificStore& { return import_ir().specifics(); } auto import_specific_interfaces() -> const SemIR::SpecificInterfaceStore& { return import_ir().specific_interfaces(); } auto import_string_literal_values() -> const SharedValueStores::StringLiteralStore& { return import_ir().string_literal_values(); } auto import_struct_type_fields() -> const SemIR::StructTypeFieldsStore& { return import_ir().struct_type_fields(); } auto import_types() -> const SemIR::TypeStore& { return import_ir().types(); } // Returns the local file's import ID for the IR we are importing from. auto import_ir_id() -> SemIR::ImportIRId { return import_ir_id_; } // A value store for local constant values of imported instructions. This maps // from `InstId`s in the import IR to corresponding `ConstantId`s in the local // IR. auto local_constant_values_for_import_insts() -> SemIR::ConstantValueStore& { auto index = local_ir().import_irs().GetRawIndex(import_ir_id_); return local_context().import_ir_constant_values()[index]; } // Returns the file we are importing into. auto local_ir() -> SemIR::File& { return context_->sem_ir(); } // Returns the type-checking context we are importing into. auto local_context() -> Context& { return *context_; } // Accessors into value stores of the file we are importing into. auto local_associated_constants() -> SemIR::AssociatedConstantStore& { return local_ir().associated_constants(); } auto local_classes() -> SemIR::ClassStore& { return local_ir().classes(); } auto local_vtables() -> SemIR::VtableStore& { return local_ir().vtables(); } auto local_constant_values() -> SemIR::ConstantValueStore& { return local_ir().constant_values(); } auto local_entity_names() -> SemIR::EntityNameStore& { return local_ir().entity_names(); } auto local_facet_types() -> SemIR::FacetTypeInfoStore& { return local_ir().facet_types(); } auto local_functions() -> SemIR::FunctionStore& { return local_ir().functions(); } auto local_generics() -> SemIR::GenericStore& { return local_ir().generics(); } auto local_identifiers() -> SharedValueStores::IdentifierStore& { return local_ir().identifiers(); } auto local_impls() -> SemIR::ImplStore& { return local_ir().impls(); } auto local_import_ir_insts() -> SemIR::ImportIRInstStore& { return local_ir().import_ir_insts(); } auto local_inst_blocks() -> SemIR::InstBlockStore& { return local_ir().inst_blocks(); } auto local_insts() -> SemIR::InstStore& { return local_ir().insts(); } auto local_interfaces() -> SemIR::InterfaceStore& { return local_ir().interfaces(); } auto local_named_constraints() -> SemIR::NamedConstraintStore& { return local_ir().named_constraints(); } auto local_ints() -> SharedValueStores::IntStore& { return local_ir().ints(); } auto local_name_scopes() -> SemIR::NameScopeStore& { return local_ir().name_scopes(); } auto local_require_impls() -> SemIR::RequireImplsStore& { return local_ir().require_impls(); } auto local_require_impls_blocks() -> SemIR::RequireImplsBlockStore& { return local_ir().require_impls_blocks(); } auto local_specifics() -> SemIR::SpecificStore& { return local_ir().specifics(); } auto local_specific_interfaces() -> SemIR::SpecificInterfaceStore& { return local_ir().specific_interfaces(); } auto local_string_literal_values() -> SharedValueStores::StringLiteralStore& { return local_ir().string_literal_values(); } auto local_struct_type_fields() -> SemIR::StructTypeFieldsStore& { return local_ir().struct_type_fields(); } auto local_types() -> SemIR::TypeStore& { return local_ir().types(); } private: Context* context_; SemIR::ImportIRId import_ir_id_; const SemIR::File& import_ir_; }; // Resolves an instruction from an imported IR into a constant referring to the // current IR. // // Calling Resolve on an instruction operates in an iterative manner, tracking // Work items on work_stack_. At a high level, the loop is: // // 1. If a constant value is already known for the work item and was not set by // this work item, it's considered resolved. // - The constant check avoids performance costs of deduplication on add. // - If we've processed this work item before, then we now process it again. // It didn't complete last time, even though we have a constant value // already. // // 2. Resolve the instruction (TryResolveInst/TryResolveTypedInst). This is done // in three phases. The first and second phases can add work to the worklist // and end in a retry, in which case those phases will be rerun once the // added work is done. The rerun cannot also end in a retry, so this results // in at most three calls, but in practice one or two calls is almost always // sufficient. Due to the chance of a second or third call to TryResolveInst, // it's important to only perform expensive work once, even when the same // phase is rerun. // // - First phase: // - Gather all input constants necessary to form the constant value of the // instruction. Gathering constants directly adds unresolved values to // work_stack_. // - If HasNewWork() reports that any work was added, then return Retry(): // this instruction needs another call to complete. Gather the // now-resolved constants and continue to the next step once the retry // happens. // // - Second phase: // - Build the constant value of the instruction. // - Gather all input constants necessary to finish importing the // instruction. This is only necessary for instructions like classes that // can be forward-declared. For these instructions, we first import the // constant value and then later import the rest of the declaration in // order to break cycles. // - If HasNewWork() reports that any work was added, then return // Retry(constant_value): this instruction needs another call to // complete. Gather the now-resolved constants and continue to the next // step once the retry happens. // // - Third phase: // - After the second phase, the constant value for the instruction is // already set, and will be passed back into TryResolve*Inst on retry. It // should not be created again. // - Fill in any remaining information to complete the import of the // instruction. For example, when importing a class declaration, build // the class scope and information about the definition. // - Return Done() to finish the resolution process. This will cause the // Resolve loop to set a constant value if we didn't retry at the end of // the second phase. // // In the common case where the second phase cannot add new work (because the // inst doesn't represent a declaration of an entity that can be forward // declared), the second and third phases are usually expressed as a call to // ResolveResult::Deduplicated or ResolveResult::Unique. // // 3. If resolve didn't return Retry(), pop the work. Otherwise, it needs to // remain, and may no longer be at the top of the stack; update the state of // the work item to track what work still needs to be done. // // The same instruction can be enqueued for resolution multiple times. However, // we will only reach the second phase once: once a constant value is set, only // the resolution step that set it will retry. // // TODO: Fix class `extern` handling and merging, rewrite tests. // - check/testdata/class/cross_package_import.carbon // - check/testdata/class/extern.carbon // TODO: Fix function `extern` handling and merging, rewrite tests. // - check/testdata/function/declaration/import.carbon // - check/testdata/packages/cross_package_import.carbon class ImportRefResolver : public ImportContext { public: // `context` must not be null. explicit ImportRefResolver(Context* context, SemIR::ImportIRId import_ir_id) : ImportContext(context, import_ir_id) {} // Iteratively resolves an imported instruction's inner references until a // constant ID referencing the current IR is produced. See the class comment // for more details. auto Resolve(SemIR::InstId inst_id) -> SemIR::ConstantId; // Wraps constant evaluation with logic to handle constants. auto ResolveConstant(SemIR::ConstantId import_const_id) -> SemIR::ConstantId; // Wraps constant evaluation with logic to handle types. auto ResolveType(SemIR::TypeId import_type_id) -> SemIR::TypeId; // Returns true if new unresolved constants were found as part of this // `Resolve` step. auto HasNewWork() -> bool; // Pushes a specific onto the work stack. This will only process when the // current instruction is done, and does not count towards `HasNewWork`. We // add specifics this way because some instructions (e.g. `FacetTypeInfo`) can // add multiple specifics. // // The insert may do extra work moving already-added work on the work stack, // but that is expected to be okay because the common cases are 0 or 1 // specifics being added. If this ends up showing up in profiles, potentially // due to vector growth, it may be worth revisiting. auto PushSpecific(SemIR::SpecificId import_id, SemIR::SpecificId local_id) -> void; // Returns the ConstantId for an InstId. Adds unresolved constants to // work_stack_. auto GetLocalConstantValueOrPush(SemIR::InstId inst_id) -> SemIR::ConstantId; private: // An instruction to import. struct InstWork { // The instruction to work on. SemIR::InstId inst_id; // Whether this work item set the constant value for the instruction and // requested a retry. bool retry_with_constant_value = false; }; // A generic to import. struct GenericWork { SemIR::GenericId import_id; SemIR::GenericId local_id; }; // A specific to import. struct SpecificWork { SemIR::SpecificId import_id; SemIR::SpecificId local_id; }; // The constant found by FindResolvedConstId. struct ResolvedConstId { // The constant for the instruction. `None` if not yet resolved. SemIR::ConstantId const_id = SemIR::ConstantId::None; // Instructions which are indirect but equivalent to the current instruction // being resolved, and should have their constant set to the same. Empty // when `const_id` has a value. llvm::SmallVector indirect_insts = {}; }; // Looks to see if an instruction has been resolved. If a constant is only // found indirectly, sets the constant for any indirect steps that don't // already have the constant. If a constant isn't found, returns the indirect // instructions so that they can have the resolved constant assigned later. auto FindResolvedConstId(SemIR::InstId inst_id) -> ResolvedConstId; // Sets a resolved constant into the current and indirect instructions. auto SetResolvedConstId(SemIR::InstId inst_id, llvm::ArrayRef indirect_insts, SemIR::ConstantId const_id) -> void; llvm::SmallVector> work_stack_; // The size of work_stack_ at the start of resolving the current instruction. size_t initial_work_ = 0; }; } // namespace // Wrapper for `AddImportRef` that provides the `import_ir_id`. static auto AddImportRef(ImportContext& context, SemIR::InstId inst_id, SemIR::EntityNameId entity_name_id = SemIR::EntityNameId::None) -> SemIR::InstId { if (!inst_id.has_value()) { return SemIR::InstId::None; } return AddImportRef(context.local_context(), SemIR::ImportIRInst(context.import_ir_id(), inst_id), entity_name_id); } // Handles setting a constant on instructions related to an import. static auto SetIndirectConstantValues( Context& context, llvm::ArrayRef indirect_insts, SemIR::ConstantId constant_id) -> void { for (const auto& import_ir_inst : indirect_insts) { auto ir_index = context.sem_ir().import_irs().GetRawIndex(import_ir_inst.ir_id()); context.import_ir_constant_values()[ir_index].Set(import_ir_inst.inst_id(), constant_id); } } // Adds an import_ref instruction for an instruction that we have already loaded // from an imported IR, with a known constant value. This is useful when the // instruction has a symbolic constant value, in order to produce an instruction // that holds that symbolic constant. static auto AddLoadedImportRef(ImportContext& context, SemIR::TypeId local_type_id, SemIR::InstId import_inst_id, SemIR::ConstantId local_const_id) -> SemIR::InstId { auto import_ir_inst_id = context.local_import_ir_insts().Add( SemIR::ImportIRInst(context.import_ir_id(), import_inst_id)); SemIR::ImportRefLoaded inst = {.type_id = local_type_id, .import_ir_inst_id = import_ir_inst_id, .entity_name_id = SemIR::EntityNameId::None}; auto inst_id = AddPlaceholderImportedInstInNoBlock( context.local_context(), MakeImportedLocIdAndInst(context.local_context(), import_ir_inst_id, inst)); context.local_constant_values().Set(inst_id, local_const_id); context.local_constant_values_for_import_insts().Set(import_inst_id, local_const_id); return inst_id; } // Like `AddLoadedImportRef`, but only for types, and returns a `TypeInstId`. static auto AddLoadedImportRefForType(ImportContext& context, SemIR::TypeInstId import_inst_id, SemIR::ConstantId local_const_id) -> SemIR::TypeInstId { return context.local_types().GetAsTypeInstId(AddLoadedImportRef( context, SemIR::TypeType::TypeId, import_inst_id, local_const_id)); } static auto AddImportIRInst(ImportContext& context, SemIR::InstId inst_id) -> SemIR::ImportIRInstId { return context.local_import_ir_insts().Add( SemIR::ImportIRInst(context.import_ir_id(), inst_id)); } // Computes, sets, and returns the constant value for an instruction. static auto SetConstantValue(Context& context, SemIR::InstId inst_id, SemIR::Inst inst) -> SemIR::ConstantId { auto const_id = TryEvalInstUnsafe(context, inst_id, inst); if (const_id.is_constant()) { CARBON_VLOG_TO(context.vlog_stream(), "Constant: {0} -> {1}\n", inst, context.constant_values().GetInstId(const_id)); } context.constant_values().Set(inst_id, const_id); return const_id; } // Adds an imported instruction without setting its constant value. The // instruction should later be updated by either `SetConstantValue` or // `ReplacePlaceholderImportedInst`. template static auto AddPlaceholderImportedInst(ImportContext& context, SemIR::InstId import_inst_id, InstT inst) -> SemIR::InstId { auto import_ir_inst_id = AddImportIRInst(context, import_inst_id); return AddPlaceholderImportedInstInNoBlock( context.local_context(), MakeImportedLocIdAndInst(context.local_context(), import_ir_inst_id, inst)); } // Replace an imported instruction that was added by // `AddPlaceholderImportedInst` with a new instruction. Computes, sets, and // returns the new constant value. static auto ReplacePlaceholderImportedInst(ImportContext& context, SemIR::InstId inst_id, SemIR::Inst inst) -> SemIR::ConstantId { CARBON_VLOG_TO(context.local_context().vlog_stream(), "ReplaceImportedInst: {0} -> {1}\n", inst_id, inst); context.local_insts().Set(inst_id, inst); CARBON_CHECK(context.local_constant_values().Get(inst_id) == SemIR::ConstantId::None); return SetConstantValue(context.local_context(), inst_id, inst); } // Returns the ConstantId for an InstId. Adds unresolved constants to // the resolver's work stack. static auto GetLocalConstantId(ImportRefResolver& resolver, SemIR::InstId inst_id) -> SemIR::ConstantId { return resolver.GetLocalConstantValueOrPush(inst_id); } // Returns the ConstantId for an imported ConstantId. Adds unresolved // constants to the resolver's work stack. static auto GetLocalConstantId(ImportRefResolver& resolver, SemIR::ConstantId const_id) -> SemIR::ConstantId { return GetLocalConstantId( resolver, GetInstWithConstantValue(resolver.import_ir(), const_id)); } // Returns the local constant InstId for an imported InstId. static auto GetLocalConstantInstId(ImportRefResolver& resolver, SemIR::InstId inst_id) -> SemIR::InstId { auto const_id = GetLocalConstantId(resolver, inst_id); return resolver.local_constant_values().GetInstIdIfValid(const_id); } // Returns the local constant InstId for an imported InstId. static auto GetLocalTypeInstId(ImportRefResolver& resolver, SemIR::TypeInstId inst_id) -> SemIR::TypeInstId { // The input instruction is a TypeInstId, and import does not change the type // of instructions, so the result is also a valid TypeInstId. return SemIR::TypeInstId::UnsafeMake( GetLocalConstantInstId(resolver, inst_id)); } // Returns the ConstantId for a TypeId. Adds unresolved constants to // work_stack_. static auto GetLocalConstantId(ImportRefResolver& resolver, SemIR::TypeId type_id) -> SemIR::ConstantId { return GetLocalConstantId(resolver, resolver.import_types().GetConstantId(type_id)); } // Translates a NameId from the import IR to a local NameId. // // No new work is generated by calling this function. static auto GetLocalNameId(ImportContext& context, SemIR::NameId import_name_id) -> SemIR::NameId { if (auto ident_id = import_name_id.AsIdentifierId(); ident_id.has_value()) { return SemIR::NameId::ForIdentifier(context.local_identifiers().Add( context.import_identifiers().Get(ident_id))); } return import_name_id; } // Returns the id for a local symbolic EntityName from an imported one, // preserving only the `NameId`, the `CompileTimeBindIndex`, and whether it is a // template. Other parts of the EntityName are not kept and are not considered // part of the canonical EntityName (even if they are present there). // // No new work is generated by calling this function. static auto GetLocalSymbolicEntityNameId( ImportContext& context, SemIR::EntityNameId import_entity_name_id) -> SemIR::EntityNameId { const auto& import_entity_name = context.import_entity_names().Get(import_entity_name_id); auto name_id = GetLocalNameId(context, import_entity_name.name_id); return context.local_entity_names().AddSymbolicBindingName( name_id, SemIR::NameScopeId::None, import_entity_name.bind_index(), import_entity_name.is_template, import_entity_name.is_unused); } // Gets the local constant values corresponding to an imported inst block. static auto GetLocalInstBlockContents(ImportRefResolver& resolver, SemIR::InstBlockId import_block_id) -> llvm::SmallVector { llvm::SmallVector inst_ids; if (!import_block_id.has_value() || import_block_id == SemIR::InstBlockId::Empty) { return inst_ids; } // Import all the values in the block. auto import_block = resolver.import_inst_blocks().Get(import_block_id); inst_ids.reserve(import_block.size()); for (auto import_inst_id : import_block) { inst_ids.push_back(GetLocalConstantInstId(resolver, import_inst_id)); } return inst_ids; } // Gets a local canonical instruction block ID corresponding to an imported inst // block whose contents were already imported, for example by // GetLocalInstBlockContents. static auto GetLocalCanonicalInstBlockId(ImportContext& context, SemIR::InstBlockId import_block_id, llvm::ArrayRef contents) -> SemIR::InstBlockId { if (!import_block_id.has_value()) { return SemIR::InstBlockId::None; } return context.local_inst_blocks().AddCanonical(contents); } // Imports the RequireImplsDecl instructions for each RequireImplsId in the // block, and gets the local RequireImplsIds from them. The returned vector is // only complete if there is no more work to do in the resolver on return. static auto GetLocalRequireImplsBlockContents( ImportRefResolver& resolver, SemIR::RequireImplsBlockId import_block_id) -> llvm::SmallVector { llvm::SmallVector require_decl_ids; if (!import_block_id.has_value() || import_block_id == SemIR::RequireImplsBlockId::Empty) { return require_decl_ids; } // Import the RequireImplsDecl for each RequireImpls in the block. auto import_block = resolver.import_require_impls_blocks().Get(import_block_id); require_decl_ids.reserve(import_block.size()); for (auto import_require_impls_id : import_block) { const auto& import_require = resolver.import_require_impls().Get(import_require_impls_id); auto local_decl_id = GetLocalConstantInstId(resolver, import_require.decl_id); // If `local_decl_id` is None, the resolver will have more work to do, and // we will call this function to try get all the decl instructions again. if (local_decl_id.has_value()) { // Importing the RequireImplsDecl instruction in `local_decl_id` also // imported the RequireImpls structure that it points to through the // RequireImplsId. require_decl_ids.push_back( resolver.local_insts() .GetAs(local_decl_id) .require_impls_id); } } return require_decl_ids; } // Gets the local block of RequireImplsIds from the imported block. Only valid // to call once there is no more work to do after the call to // GetLocalRequireImplsBlockContents(). static auto GetLocalCanonicalRequireImplsBlockId( ImportContext& context, SemIR::RequireImplsBlockId import_block_id, llvm::ArrayRef contents) -> SemIR::RequireImplsBlockId { if (!import_block_id.has_value()) { return SemIR::RequireImplsBlockId::None; } return context.local_require_impls_blocks().Add(contents); } // Gets a local instruction block containing ImportRefs referring to the // instructions in the specified imported instruction block. static auto GetLocalImportRefInstBlock(ImportContext& context, SemIR::InstBlockId import_inst_block_id) -> SemIR::InstBlockId { llvm::SmallVector elements; auto import_elements = context.import_inst_blocks().Get(import_inst_block_id); elements.reserve(import_elements.size()); for (auto element : import_elements) { elements.push_back(AddImportRef(context, element)); } return context.local_inst_blocks().Add(elements); } // Gets an incomplete local version of an imported generic. Most fields are // set in the third phase. static auto ImportIncompleteGeneric(ImportContext& context, SemIR::InstId decl_id, SemIR::GenericId generic_id) -> SemIR::GenericId { if (!generic_id.has_value()) { return SemIR::GenericId::None; } return context.local_generics().Add( {.decl_id = decl_id, .bindings_id = SemIR::InstBlockId::None, .self_specific_id = SemIR::SpecificId::None}); } namespace { // Local information associated with an imported generic. struct GenericData { struct Binding { // The attached type's constant, which may differ from the type on the // constant. This needs to be preserved for the ImportRef. SemIR::ConstantId type_constant_id; SemIR::ConstantId inst_constant_id; }; llvm::SmallVector bindings; llvm::SmallVector decl_block; }; } // namespace // Gets a local version of the data associated with a generic. This is processed // through `ResolveResult::FinishGenericOrDone`. static auto GetLocalGenericData(ImportRefResolver& resolver, SemIR::GenericId import_generic_id) -> GenericData { GenericData generic_data; if (import_generic_id.has_value()) { const auto& import_generic = resolver.import_generics().Get(import_generic_id); if (import_generic.bindings_id.has_value()) { auto import_bindings = resolver.import_inst_blocks().Get(import_generic.bindings_id); generic_data.bindings.reserve(import_bindings.size()); for (auto import_inst_id : import_bindings) { generic_data.bindings.push_back( {.type_constant_id = GetLocalConstantId( resolver, resolver.import_insts().GetAttachedType(import_inst_id)), .inst_constant_id = GetLocalConstantId(resolver, import_inst_id)}); } } generic_data.decl_block = GetLocalInstBlockContents(resolver, import_generic.decl_block_id); } return generic_data; } // Rebuilds an eval block and sets locations. // TODO: Import the generic eval block rather than calling // RebuildGenericEvalBlock to rebuild it. static auto ResolveLocalEvalBlock(ImportContext& context, SemIR::InstBlockId import_block_id, llvm::ArrayRef local_block, SemIR::GenericId generic_id, SemIR::GenericInstIndex::Region region) -> SemIR::InstBlockId { auto eval_block_id = RebuildGenericEvalBlock(context.local_context(), generic_id, region, local_block); // Set the locations of the instructions in the inst block to match those of // the imported instructions. for (auto [import_inst_id, local_inst_id] : llvm::zip_equal(context.import_inst_blocks().Get(import_block_id), context.local_inst_blocks().Get(eval_block_id))) { auto import_ir_inst_id = AddImportIRInst(context, import_inst_id); context.local_insts().SetLocId(local_inst_id, import_ir_inst_id); } return eval_block_id; } // Adds the given local generic data to the given generic. This should only be // called by `ResolveResult`. static auto SetGenericDataForResolveResult(ImportContext& context, SemIR::GenericId import_generic_id, SemIR::GenericId new_generic_id, const GenericData& generic_data) -> void { if (!import_generic_id.has_value()) { return; } const auto& import_generic = context.import_generics().Get(import_generic_id); auto& new_generic = context.local_generics().Get(new_generic_id); auto import_bindings = context.import_inst_blocks().Get(import_generic.bindings_id); llvm::SmallVector new_bindings; new_bindings.reserve(import_bindings.size()); for (auto [import_binding_id, binding] : llvm::zip_equal(import_bindings, generic_data.bindings)) { auto local_type_id = context.local_types().GetTypeIdForTypeConstantId( binding.type_constant_id); new_bindings.push_back(AddLoadedImportRef( context, local_type_id, import_binding_id, binding.inst_constant_id)); } new_generic.bindings_id = context.local_inst_blocks().Add(new_bindings); new_generic.decl_block_id = ResolveLocalEvalBlock( context, import_generic.decl_block_id, generic_data.decl_block, new_generic_id, SemIR::GenericInstIndex::Region::Declaration); } // Gets a local constant value corresponding to an imported generic ID. May // add work to the work stack and return `None`. static auto GetLocalConstantId(ImportRefResolver& resolver, SemIR::GenericId generic_id) -> SemIR::ConstantId { if (!generic_id.has_value()) { return SemIR::ConstantId::None; } auto import_decl_inst_id = resolver.import_generics().Get(generic_id).decl_id; auto import_decl_inst = resolver.import_insts().GetWithAttachedType(import_decl_inst_id); if (import_decl_inst.IsOneOf()) { // For these decl types, the imported entity can be found via the // declaration's operands. return GetLocalConstantId(resolver, import_decl_inst_id); } // For all other kinds of declaration, the imported entity can be found via // the type of the declaration. CARBON_CHECK(import_decl_inst.type_id().has_value()); return GetLocalConstantId(resolver, import_decl_inst.type_id()); } // Gets a local generic ID given the corresponding local constant ID returned // by GetLocalConstantId for the imported generic. Does not add any new work. static auto GetLocalGenericId(ImportContext& context, SemIR::ConstantId local_const_id) -> SemIR::GenericId { if (!local_const_id.has_value()) { return SemIR::GenericId::None; } auto inst = context.local_insts().Get( context.local_constant_values().GetInstId(local_const_id)); CARBON_KIND_SWITCH(inst) { case CARBON_KIND(SemIR::FunctionType fn_type): { return context.local_functions().Get(fn_type.function_id).generic_id; } case CARBON_KIND(SemIR::GenericClassType class_type): { return context.local_classes().Get(class_type.class_id).generic_id; } case CARBON_KIND(SemIR::GenericInterfaceType interface_type): { return context.local_interfaces() .Get(interface_type.interface_id) .generic_id; } case CARBON_KIND(SemIR::GenericNamedConstraintType constraint_type): { return context.local_named_constraints() .Get(constraint_type.named_constraint_id) .generic_id; } case CARBON_KIND(SemIR::ImplDecl impl_decl): { return context.local_impls().Get(impl_decl.impl_id).generic_id; } case CARBON_KIND(SemIR::InterfaceWithSelfDecl interface_with_self_decl): { return context.local_interfaces() .Get(interface_with_self_decl.interface_id) .generic_with_self_id; } case CARBON_KIND( SemIR::NamedConstraintWithSelfDecl constraint_with_self_decl): { return context.local_named_constraints() .Get(constraint_with_self_decl.named_constraint_id) .generic_with_self_id; } case CARBON_KIND(SemIR::RequireImplsDecl require_decl): { return context.local_require_impls() .Get(require_decl.require_impls_id) .generic_id; } default: { CARBON_FATAL("Unexpected inst for generic declaration: {0}", inst); } } } namespace { // Local information associated with an imported specific. struct SpecificData { SemIR::ConstantId generic_const_id; llvm::SmallVector args; }; } // namespace // Gets local information about an imported specific. static auto GetLocalSpecificData(ImportRefResolver& resolver, SemIR::SpecificId specific_id) -> SpecificData { if (!specific_id.has_value()) { return {.generic_const_id = SemIR::ConstantId::None, .args = {}}; } const auto& specific = resolver.import_specifics().Get(specific_id); return { .generic_const_id = GetLocalConstantId(resolver, specific.generic_id), .args = GetLocalInstBlockContents(resolver, specific.args_id), }; } // True for an already-imported specific. static auto IsSpecificImported(const SemIR::Specific& import_specific, const SemIR::Specific& local_specific) -> bool { return local_specific.decl_block_id.has_value() && (local_specific.definition_block_id.has_value() || !import_specific.definition_block_id.has_value()); } // Gets a local specific whose data was already imported by // GetLocalSpecificData. This can add work through `PushSpecific`, but callers // shouldn't need to consider that because specifics are processed after the // current instruction. // // `local_generic_id` is provided when this is used for a generic's `self` // specific, where `GetLocalGenericId` won't work because `generic_const_id` can // be `TypeType`. static auto GetOrAddLocalSpecific( ImportRefResolver& resolver, SemIR::SpecificId import_specific_id, const SpecificData& data, SemIR::GenericId local_generic_id = SemIR::GenericId::None) -> SemIR::SpecificId { if (!import_specific_id.has_value()) { return SemIR::SpecificId::None; } // Form a corresponding local specific ID. const auto& import_specific = resolver.import_specifics().Get(import_specific_id); if (!local_generic_id.has_value()) { local_generic_id = GetLocalGenericId(resolver, data.generic_const_id); } auto args_id = GetLocalCanonicalInstBlockId(resolver, import_specific.args_id, data.args); // Get the specific. auto local_specific_id = resolver.local_specifics().GetOrAdd(local_generic_id, args_id); if (!IsSpecificImported(import_specific, resolver.local_specifics().Get(local_specific_id))) { // Enqueue the specific to fill in remaining fields. resolver.PushSpecific(import_specific_id, local_specific_id); } return local_specific_id; } // Given a generic that's gone through the initial setup with `GenericData`, // finish the import. static auto TryFinishGeneric(ImportRefResolver& resolver, SemIR::GenericId import_generic_id, SemIR::GenericId local_generic_id) -> bool { const auto& import_generic = resolver.import_generics().Get(import_generic_id); auto specific_data = GetLocalSpecificData(resolver, import_generic.self_specific_id); llvm::SmallVector definition_block; if (import_generic.definition_block_id.has_value()) { definition_block = GetLocalInstBlockContents(resolver, import_generic.definition_block_id); } if (resolver.HasNewWork()) { return false; } auto& local_generic = resolver.local_generics().Get(local_generic_id); CARBON_CHECK(!local_generic.self_specific_id.has_value(), "Currently assuming we can't find a GenericId multiple ways"); local_generic.self_specific_id = GetOrAddLocalSpecific(resolver, import_generic.self_specific_id, specific_data, local_generic_id); if (import_generic.definition_block_id.has_value()) { local_generic.definition_block_id = ResolveLocalEvalBlock( resolver, import_generic.definition_block_id, definition_block, local_generic_id, SemIR::GenericInstIndex::Region::Definition); } return true; } // Given a specific that's gone through the initial setup with `SpecificData`, // finish the import. static auto TryFinishSpecific(ImportRefResolver& resolver, SemIR::SpecificId import_specific_id, SemIR::SpecificId local_specific_id) -> bool { const auto& import_specific = resolver.import_specifics().Get(import_specific_id); auto& local_specific = resolver.local_specifics().Get(local_specific_id); if (IsSpecificImported(import_specific, local_specific)) { return true; } llvm::SmallVector decl_block; if (!local_specific.decl_block_id.has_value()) { decl_block = GetLocalInstBlockContents(resolver, import_specific.decl_block_id); } auto definition_block = GetLocalInstBlockContents(resolver, import_specific.definition_block_id); if (resolver.HasNewWork()) { return false; } if (!local_specific.decl_block_id.has_value()) { local_specific.decl_block_id = GetLocalCanonicalInstBlockId( resolver, import_specific.decl_block_id, decl_block); } local_specific.definition_block_id = GetLocalCanonicalInstBlockId( resolver, import_specific.definition_block_id, definition_block); return true; } namespace { struct SpecificInterfaceData { SemIR::ConstantId interface_const_id; SpecificData specific_data; }; } // namespace static auto GetLocalSpecificInterfaceData( ImportRefResolver& resolver, SemIR::SpecificInterface import_interface) -> SpecificInterfaceData { SemIR::ConstantId interface_const_id = SemIR::ConstantId::None; if (import_interface.interface_id.has_value()) { interface_const_id = GetLocalConstantId(resolver, resolver.import_interfaces() .Get(import_interface.interface_id) .first_owning_decl_id); } return {.interface_const_id = interface_const_id, .specific_data = GetLocalSpecificData(resolver, import_interface.specific_id)}; } static auto GetLocalSpecificInterface( ImportRefResolver& resolver, SemIR::SpecificInterface import_specific_interface, SpecificInterfaceData interface_data) -> SemIR::SpecificInterface { if (!interface_data.interface_const_id.has_value()) { return SemIR::SpecificInterface::None; } // Find the corresponding interface type. For a non-generic interface, // this is the type of the interface declaration. For a generic interface, // build a interface type referencing this specialization of the generic // interface. auto interface_const_inst = resolver.local_insts().Get(resolver.local_constant_values().GetInstId( interface_data.interface_const_id)); if (auto facet_type = interface_const_inst.TryAs()) { const SemIR::FacetTypeInfo& new_facet_type_info = resolver.local_facet_types().Get(facet_type->facet_type_id); return std::get( *new_facet_type_info.TryAsSingleExtend()); } else { auto generic_interface_type = resolver.local_types().GetAs( interface_const_inst.type_id()); auto specific_id = GetOrAddLocalSpecific(resolver, import_specific_interface.specific_id, interface_data.specific_data); return {generic_interface_type.interface_id, specific_id}; } } namespace { struct SpecificNamedConstraintData { SemIR::ConstantId constraint_const_id; SpecificData specific_data; }; } // namespace static auto GetLocalSpecificNamedConstraintData( ImportRefResolver& resolver, SemIR::SpecificNamedConstraint import_constraint) -> SpecificNamedConstraintData { SemIR::ConstantId constraint_const_id = SemIR::ConstantId::None; if (import_constraint.named_constraint_id.has_value()) { constraint_const_id = GetLocalConstantId( resolver, resolver.import_named_constraints() .Get(import_constraint.named_constraint_id) .first_owning_decl_id); } return {.constraint_const_id = constraint_const_id, .specific_data = GetLocalSpecificData(resolver, import_constraint.specific_id)}; } static auto GetLocalSpecificNamedConstraint( ImportRefResolver& resolver, SemIR::SpecificNamedConstraint import_specific_constraint, SpecificNamedConstraintData constraint_data) -> SemIR::SpecificNamedConstraint { if (!constraint_data.constraint_const_id.has_value()) { return SemIR::SpecificNamedConstraint::None; } // Find the corresponding named constraint type. For a non-generic constraint, // this is the type of the named constraint declaration. For a generic // constraint, build a named constraint type referencing this specialization // of the generic named constraint. auto constraint_const_inst = resolver.local_insts().Get(resolver.local_constant_values().GetInstId( constraint_data.constraint_const_id)); if (auto facet_type = constraint_const_inst.TryAs()) { const SemIR::FacetTypeInfo& new_facet_type_info = resolver.local_facet_types().Get(facet_type->facet_type_id); return std::get( *new_facet_type_info.TryAsSingleExtend()); } else { auto generic_constraint_type = resolver.local_types().GetAs( constraint_const_inst.type_id()); auto specific_id = GetOrAddLocalSpecific(resolver, import_specific_constraint.specific_id, constraint_data.specific_data); return {generic_constraint_type.named_constraint_id, specific_id}; } } static auto GetLocalNameScopeIdImpl(ImportRefResolver& resolver, SemIR::ConstantId const_id) -> SemIR::NameScopeId { if (!const_id.has_value()) { return SemIR::NameScopeId::None; } auto const_inst_id = resolver.local_constant_values().GetInstId(const_id); auto name_scope_inst = resolver.local_insts().Get(const_inst_id); CARBON_KIND_SWITCH(name_scope_inst) { case CARBON_KIND(SemIR::Namespace inst): { return inst.name_scope_id; } case CARBON_KIND(SemIR::ClassType inst): { return resolver.local_classes().Get(inst.class_id).scope_id; } case CARBON_KIND(SemIR::FacetType inst): { const SemIR::FacetTypeInfo& facet_type_info = resolver.local_facet_types().Get(inst.facet_type_id); if (auto single = facet_type_info.TryAsSingleExtend()) { // This is the facet type produced by an interface or named constraint // declaration. CARBON_KIND_SWITCH(*single) { case CARBON_KIND(SemIR::SpecificInterface interface): { return resolver.local_interfaces() .Get(interface.interface_id) .scope_without_self_id; } case CARBON_KIND(SemIR::SpecificNamedConstraint constraint): { return resolver.local_named_constraints() .Get(constraint.named_constraint_id) .scope_without_self_id; } } } break; } case CARBON_KIND(SemIR::ImplDecl inst): { return resolver.local_impls().Get(inst.impl_id).scope_id; } case CARBON_KIND(SemIR::InterfaceWithSelfDecl interface_with_self): { return resolver.local_interfaces() .Get(interface_with_self.interface_id) .scope_with_self_id; } case CARBON_KIND(SemIR::NamedConstraintWithSelfDecl constraint_with_self): { return resolver.local_named_constraints() .Get(constraint_with_self.named_constraint_id) .scope_with_self_id; } case SemIR::StructValue::Kind: { auto type_inst = resolver.local_types().GetAsInst(name_scope_inst.type_id()); CARBON_KIND_SWITCH(type_inst) { case CARBON_KIND(SemIR::GenericClassType inst): { return resolver.local_classes().Get(inst.class_id).scope_id; } case CARBON_KIND(SemIR::GenericInterfaceType inst): { return resolver.local_interfaces() .Get(inst.interface_id) .scope_without_self_id; } case CARBON_KIND(SemIR::GenericNamedConstraintType inst): { return resolver.local_named_constraints() .Get(inst.named_constraint_id) .scope_without_self_id; } default: { break; } } break; } default: { if (const_inst_id == SemIR::ErrorInst::InstId) { return SemIR::NameScopeId::None; } break; } } CARBON_FATAL("Unexpected instruction kind for name scope: {0}", name_scope_inst); } // Translates a NameScopeId from the import IR to a local NameScopeId. Adds // unresolved constants to the resolver's work stack. static auto GetLocalNameScopeId(ImportRefResolver& resolver, SemIR::NameScopeId name_scope_id) -> SemIR::NameScopeId { // Get the instruction that created the scope. auto [inst_id, inst] = resolver.import_name_scopes().GetInstIfValid(name_scope_id); if (!inst) { // Map scopes that aren't associated with an instruction to `None`. For now, // such scopes aren't used, and we don't have a good way to remap them. return SemIR::NameScopeId::None; } // Get the constant value for the scope. auto const_id = GetLocalConstantId(resolver, inst_id); if (!const_id.has_value()) { return SemIR::NameScopeId::None; } auto result = GetLocalNameScopeIdImpl(resolver, const_id); CARBON_CHECK(result.has_value()); return result; } // Given an imported entity base, returns an incomplete, local version of it. // // Most fields are set in the third phase once they're imported. Import enough // of the parameter lists that we know whether this interface is a generic // interface and can build the right constant value for it. // // TODO: Support extern. static auto GetIncompleteLocalEntityBase( ImportContext& context, SemIR::InstId decl_id, const SemIR::EntityWithParamsBase& import_base) -> SemIR::EntityWithParamsBase { // Translate the extern_library_id if present. auto extern_library_id = SemIR::LibraryNameId::None; if (import_base.extern_library_id.has_value()) { if (import_base.extern_library_id.index >= 0) { auto val = context.import_string_literal_values().Get( import_base.extern_library_id.AsStringLiteralValueId()); extern_library_id = SemIR::LibraryNameId::ForStringLiteralValueId( context.local_string_literal_values().Add(val)); } else { extern_library_id = import_base.extern_library_id; } } return { .name_id = GetLocalNameId(context, import_base.name_id), .parent_scope_id = SemIR::NameScopeId::None, .generic_id = ImportIncompleteGeneric(context, decl_id, import_base.generic_id), .first_param_node_id = Parse::NodeId::None, .last_param_node_id = Parse::NodeId::None, .pattern_block_id = SemIR::InstBlockId::None, .implicit_param_patterns_id = import_base.implicit_param_patterns_id.has_value() ? SemIR::InstBlockId::Empty : SemIR::InstBlockId::None, .param_patterns_id = import_base.param_patterns_id.has_value() ? SemIR::InstBlockId::Empty : SemIR::InstBlockId::None, .is_extern = import_base.is_extern, .extern_library_id = extern_library_id, .non_owning_decl_id = import_base.non_owning_decl_id.has_value() ? decl_id : SemIR::InstId::None, .first_owning_decl_id = import_base.first_owning_decl_id.has_value() ? decl_id : SemIR::InstId::None, }; } // Adds ImportRefUnloaded entries for members of the imported scope, for name // lookup. static auto AddNameScopeImportRefs(ImportContext& context, const SemIR::NameScope& import_scope, SemIR::NameScope& new_scope) -> void { for (auto entry : import_scope.entries()) { SemIR::ScopeLookupResult result = entry.result; if (result.is_poisoned()) { continue; } auto ref_id = AddImportRef(context, result.target_inst_id()); new_scope.AddRequired({.name_id = GetLocalNameId(context, entry.name_id), .result = SemIR::ScopeLookupResult::MakeFound( ref_id, result.access_kind())}); } for (auto scope_inst_id : import_scope.extended_scopes()) { new_scope.AddExtendedScope(AddImportRef(context, scope_inst_id)); } } // Given a block ID for a list of associated entities of a witness, returns a // version localized to the current IR. static auto AddAssociatedEntities(ImportContext& context, SemIR::NameScopeId local_name_scope_id, SemIR::InstBlockId associated_entities_id) -> SemIR::InstBlockId { if (associated_entities_id == SemIR::InstBlockId::Empty) { return SemIR::InstBlockId::Empty; } auto associated_entities = context.import_inst_blocks().Get(associated_entities_id); llvm::SmallVector new_associated_entities; new_associated_entities.reserve(associated_entities.size()); for (auto inst_id : associated_entities) { // Determine the name of the associated entity, by switching on its kind. SemIR::NameId import_name_id = SemIR::NameId::None; if (auto assoc_const_decl = context.import_insts().TryGetAs( inst_id)) { const auto& assoc_const = context.import_associated_constants().Get( assoc_const_decl->assoc_const_id); import_name_id = assoc_const.name_id; } else if (auto function_decl = context.import_insts().TryGetAs( inst_id)) { const auto& function = context.import_functions().Get(function_decl->function_id); import_name_id = function.name_id; } else if (auto import_ref = context.import_insts().TryGetAs( inst_id)) { import_name_id = context.import_entity_names().Get(import_ref->entity_name_id).name_id; } else { // We don't need `GetWithAttachedType` here because we don't access the // type. CARBON_FATAL("Unhandled associated entity kind: {0}", context.import_insts().Get(inst_id).kind()); } auto name_id = GetLocalNameId(context, import_name_id); auto entity_name_id = context.local_entity_names().Add( {.name_id = name_id, .parent_scope_id = local_name_scope_id}); new_associated_entities.push_back( AddImportRef(context, inst_id, entity_name_id)); } return context.local_inst_blocks().Add(new_associated_entities); } namespace { namespace Internal { // Internal concept for instruction kinds that produce unique constants. template concept HasUniqueConstantKind = InstT::Kind.constant_kind() == SemIR::InstConstantKind::AlwaysUnique || InstT::Kind.constant_kind() == SemIR::InstConstantKind::ConditionalUnique; } // namespace Internal // The result of attempting to resolve an imported instruction to a constant. struct ResolveResult { // The new constant value, if known. SemIR::ConstantId const_id; // Newly created declaration whose value is being resolved, if any. SemIR::InstId decl_id = SemIR::InstId::None; // Whether resolution has been attempted once and needs to be retried. bool retry = false; // If a generic needs to be resolved, the generic information. struct ResolveGeneric { SemIR::GenericId import_generic_id = SemIR::GenericId::None; SemIR::GenericId local_generic_id = SemIR::GenericId::None; }; std::array resolve_generic; // Produces a resolve result that tries resolving this instruction again. If // `const_id` is specified, then this is the end of the second phase, and the // constant value will be passed to the next resolution attempt. Otherwise, // this is the end of the first phase. static auto Retry(SemIR::ConstantId const_id = SemIR::ConstantId::None, SemIR::InstId decl_id = SemIR::InstId::None) -> ResolveResult { return {.const_id = const_id, .decl_id = decl_id, .retry = true}; } // Produces a resolve result that provides the given constant value. Requires // that there is no new work. static auto Done(SemIR::ConstantId const_id, SemIR::InstId decl_id = SemIR::InstId::None) -> ResolveResult { return {.const_id = const_id, .decl_id = decl_id}; } // Produces a resolve result that provides the given constant value. Retries // instead if work has been added. static auto RetryOrDone(ImportRefResolver& resolver, SemIR::ConstantId const_id) -> ResolveResult { if (resolver.HasNewWork()) { return Retry(); } return Done(const_id); } // If there's no generic, this is equivalent to `Done`. If there is a generic, // it's still done, but the fetched generic data is processed and the generic // is enqueued for further work. // // It's not valid to have a generic-with-self but no base generic. static auto FinishGenericOrDone(ImportRefResolver& resolver, SemIR::ConstantId const_id, SemIR::InstId decl_id, SemIR::GenericId import_generic_id, SemIR::GenericId local_generic_id, GenericData generic_data) -> ResolveResult { auto result = Done(const_id, decl_id); if (import_generic_id.has_value()) { SetGenericDataForResolveResult(resolver, import_generic_id, local_generic_id, generic_data); result.resolve_generic[0].import_generic_id = import_generic_id; result.resolve_generic[0].local_generic_id = local_generic_id; } return result; } // Adds `inst` to the local context as a deduplicated constant and returns a // successful `ResolveResult`. Requires that there is no new work. // // This implements phases 2 and 3 of resolving the inst (as described on // `ImportRefResolver`) for the common case where those phases are combined. // Cases where that isn't applicable should instead use // `AddPlaceholderImportedInst` and `ReplacePlaceholderImportedInst`. // // This should not be used for instructions that represent declarations, or // other instructions with `constant_kind == InstConstantKind::Unique`, // because they should not be deduplicated. template requires(!Internal::HasUniqueConstantKind) static auto Deduplicated(ImportRefResolver& resolver, InstT inst) -> ResolveResult { CARBON_CHECK(!resolver.HasNewWork()); // AddImportedConstant produces an unattached constant, so its type must // be unattached as well. inst.type_id = resolver.local_types().GetUnattachedType(inst.type_id); auto const_id = AddImportedConstant(resolver.local_context(), inst); CARBON_CHECK(const_id.is_constant(), "{0} is not constant", inst); return Done(const_id); } // Adds `inst` to the local context as a unique constant and returns a // successful `ResolveResult`. `import_inst_id` is the corresponding inst ID // in the local context. Requires that there is no new work. // // This implements phases 2 and 3 of resolving the inst (as described on // `ImportRefResolver`) for the common case where those phases are combined. // Cases where that isn't applicable should instead use // `AddPlaceholderImportedInst` and `ReplacePlaceholderImportedInst`. // // This should only be used for instructions that represent declarations, or // other instructions with `constant_kind == InstConstantKind::Unique`, // because it does not perform deduplication. template requires Internal::HasUniqueConstantKind static auto Unique(ImportRefResolver& resolver, SemIR::InstId import_inst_id, InstT inst) -> ResolveResult { CARBON_CHECK(!resolver.HasNewWork()); auto inst_id = AddPlaceholderImportedInst(resolver, import_inst_id, inst); auto const_id = SetConstantValue(resolver.local_context(), inst_id, inst); CARBON_CHECK(const_id.is_constant(), "{0} is not constant", inst); return Done(const_id, inst_id); } }; } // namespace static auto TryResolveTypedInst(ImportRefResolver& resolver, SemIR::AdaptDecl inst, SemIR::InstId import_inst_id) -> ResolveResult { auto adapted_type_const_id = GetLocalConstantId(resolver, inst.adapted_type_inst_id); if (resolver.HasNewWork()) { return ResolveResult::Retry(); } auto adapted_type_inst_id = AddLoadedImportRefForType( resolver, inst.adapted_type_inst_id, adapted_type_const_id); // Create a corresponding instruction to represent the declaration. return ResolveResult::Unique( resolver, import_inst_id, {.adapted_type_inst_id = adapted_type_inst_id}); } template requires SemIR::Internal::HasInstCategory static auto TryResolveTypedInst(ImportRefResolver& resolver, ParamPatternT inst, SemIR::InstId import_inst_id) -> ResolveResult { auto type_const_id = GetLocalConstantId(resolver, inst.type_id); auto subpattern_id = GetLocalConstantInstId(resolver, inst.subpattern_id); if (resolver.HasNewWork()) { return ResolveResult::Retry(); } return ResolveResult::Unique( resolver, import_inst_id, {.type_id = resolver.local_types().GetTypeIdForTypeConstantId(type_const_id), .subpattern_id = subpattern_id}); } static auto TryResolveTypedInst(ImportRefResolver& resolver, SemIR::ArrayType inst) -> ResolveResult { CARBON_CHECK(inst.type_id == SemIR::TypeType::TypeId); auto element_type_inst_id = GetLocalTypeInstId(resolver, inst.element_type_inst_id); auto bound_id = GetLocalConstantInstId(resolver, inst.bound_id); if (resolver.HasNewWork()) { return ResolveResult::Retry(); } return ResolveResult::Deduplicated( resolver, {.type_id = SemIR::TypeType::TypeId, .bound_id = bound_id, .element_type_inst_id = element_type_inst_id}); } static auto ImportAssociatedConstant( ImportContext& context, const SemIR::AssociatedConstant& import_assoc_const, SemIR::TypeId type_id) -> std::pair { SemIR::AssociatedConstantDecl assoc_const_decl = { .type_id = type_id, .assoc_const_id = SemIR::AssociatedConstantId::None, .decl_block_id = SemIR::InstBlockId::Empty}; auto assoc_const_decl_id = AddPlaceholderImportedInst( context, import_assoc_const.decl_id, assoc_const_decl); assoc_const_decl.assoc_const_id = context.local_associated_constants().Add({ .name_id = GetLocalNameId(context, import_assoc_const.name_id), .parent_scope_id = SemIR::NameScopeId::None, .decl_id = assoc_const_decl_id, .default_value_id = import_assoc_const.default_value_id.has_value() ? AddImportRef(context, import_assoc_const.default_value_id) : SemIR::InstId::None, }); // Write the associated constant ID into the AssociatedConstantDecl. auto const_id = ReplacePlaceholderImportedInst(context, assoc_const_decl_id, assoc_const_decl); return {assoc_const_decl.assoc_const_id, const_id}; } static auto TryResolveTypedInst(ImportRefResolver& resolver, SemIR::AssociatedConstantDecl inst, SemIR::ConstantId const_id) -> ResolveResult { const auto& import_assoc_const = resolver.import_associated_constants().Get(inst.assoc_const_id); SemIR::AssociatedConstantId assoc_const_id = SemIR::AssociatedConstantId::None; if (!const_id.has_value()) { // In the first phase, import the type of the associated constant. auto type_const_id = GetLocalConstantId(resolver, inst.type_id); if (resolver.HasNewWork()) { return ResolveResult::Retry(); } // In the second phase, create the associated constant and its declaration. auto type_id = resolver.local_types().GetTypeIdForTypeConstantId(type_const_id); std::tie(assoc_const_id, const_id) = ImportAssociatedConstant(resolver, import_assoc_const, type_id); } else { // In the third phase, compute the associated constant ID from the constant // value of the declaration. assoc_const_id = resolver.local_insts() .GetAs( resolver.local_constant_values().GetInstId(const_id)) .assoc_const_id; } // Load the values to populate the entity with. auto parent_scope_id = GetLocalNameScopeId(resolver, import_assoc_const.parent_scope_id); auto& new_assoc_const = resolver.local_associated_constants().Get(assoc_const_id); if (resolver.HasNewWork()) { return ResolveResult::Retry(const_id, new_assoc_const.decl_id); } // Populate the entity. new_assoc_const.parent_scope_id = parent_scope_id; return ResolveResult::Done(const_id, new_assoc_const.decl_id); } static auto TryResolveTypedInst(ImportRefResolver& resolver, SemIR::AssociatedEntity inst) -> ResolveResult { auto type_const_id = GetLocalConstantId(resolver, inst.type_id); if (resolver.HasNewWork()) { return ResolveResult::Retry(); } // Add a lazy reference to the target declaration. auto decl_id = AddImportRef(resolver, inst.decl_id); return ResolveResult::Deduplicated( resolver, {.type_id = resolver.local_types().GetTypeIdForTypeConstantId( type_const_id), .index = inst.index, .decl_id = decl_id}); } static auto TryResolveTypedInst(ImportRefResolver& resolver, SemIR::AssociatedEntityType inst) -> ResolveResult { CARBON_CHECK(inst.type_id == SemIR::TypeType::TypeId); auto data = GetLocalSpecificInterfaceData(resolver, inst.GetSpecificInterface()); if (resolver.HasNewWork()) { return ResolveResult::Retry(); } auto specific_interface = GetLocalSpecificInterface(resolver, inst.GetSpecificInterface(), data); return ResolveResult::Deduplicated( resolver, {.type_id = SemIR::TypeType::TypeId, .interface_id = specific_interface.interface_id, .interface_without_self_specific_id = specific_interface.specific_id}); } static auto TryResolveTypedInst(ImportRefResolver& resolver, SemIR::BaseDecl inst, SemIR::InstId import_inst_id) -> ResolveResult { auto type_const_id = GetLocalConstantId(resolver, inst.type_id); auto base_type_const_id = GetLocalConstantId(resolver, inst.base_type_inst_id); if (resolver.HasNewWork()) { return ResolveResult::Retry(); } auto base_type_inst_id = AddLoadedImportRefForType( resolver, inst.base_type_inst_id, base_type_const_id); // Create a corresponding instruction to represent the declaration. return ResolveResult::Unique( resolver, import_inst_id, {.type_id = resolver.local_types().GetTypeIdForTypeConstantId(type_const_id), .base_type_inst_id = base_type_inst_id, .index = inst.index}); } static auto TryResolveTypedInst(ImportRefResolver& resolver, SemIR::AliasBinding inst) -> ResolveResult { auto value_id = GetLocalConstantId(resolver, inst.value_id); return ResolveResult::RetryOrDone(resolver, value_id); } static auto TryResolveTypedInst(ImportRefResolver& resolver, SemIR::SymbolicBinding inst) -> ResolveResult { auto type_id = GetLocalConstantId(resolver, inst.type_id); if (resolver.HasNewWork()) { return ResolveResult::Retry(); } auto entity_name_id = GetLocalSymbolicEntityNameId(resolver, inst.entity_name_id); return ResolveResult::Deduplicated( resolver, {.type_id = resolver.local_types().GetTypeIdForTypeConstantId(type_id), .entity_name_id = entity_name_id, .value_id = SemIR::InstId::None}); } template requires SemIR::Internal::HasInstCategory static auto TryResolveTypedInst(ImportRefResolver& resolver, BindingPatternT inst, SemIR::InstId import_inst_id) -> ResolveResult { auto type_const_id = GetLocalConstantId(resolver, inst.type_id); const auto& import_entity_name = resolver.import_entity_names().Get(inst.entity_name_id); auto parent_scope_id = GetLocalNameScopeId(resolver, import_entity_name.parent_scope_id); if (resolver.HasNewWork()) { return ResolveResult::Retry(); } auto name_id = GetLocalNameId(resolver, import_entity_name.name_id); auto entity_name_id = resolver.local_entity_names().Add( {.name_id = name_id, .parent_scope_id = parent_scope_id, .bind_index_value = import_entity_name.bind_index().index, .is_template = import_entity_name.is_template}); return ResolveResult::Unique( resolver, import_inst_id, {.type_id = resolver.local_types().GetTypeIdForTypeConstantId(type_const_id), .entity_name_id = entity_name_id}); } static auto TryResolveTypedInst(ImportRefResolver& resolver, SemIR::BoolLiteral inst) -> ResolveResult { CARBON_CHECK(resolver.import_types().GetTypeInstId(inst.type_id) == SemIR::BoolType::TypeInstId); CARBON_CHECK(!resolver.HasNewWork()); return ResolveResult::Deduplicated( resolver, {.type_id = GetSingletonType(resolver.local_context(), SemIR::BoolType::TypeInstId), .value = inst.value}); } static auto TryResolveTypedInst(ImportRefResolver& resolver, SemIR::BoundMethod inst) -> ResolveResult { CARBON_CHECK(resolver.import_types().GetTypeInstId(inst.type_id) == SemIR::BoundMethodType::TypeInstId); auto object_id = GetLocalConstantInstId(resolver, inst.object_id); auto function_decl_id = GetLocalConstantInstId(resolver, inst.function_decl_id); if (resolver.HasNewWork()) { return ResolveResult::Retry(); } return ResolveResult::Deduplicated( resolver, {.type_id = GetSingletonType(resolver.local_context(), SemIR::BoundMethodType::TypeInstId), .object_id = object_id, .function_decl_id = function_decl_id}); } static auto TryResolveTypedInst(ImportRefResolver& resolver, SemIR::Call inst) -> ResolveResult { auto type_id = GetLocalConstantId(resolver, inst.type_id); auto callee_id = GetLocalConstantInstId(resolver, inst.callee_id); auto args = GetLocalInstBlockContents(resolver, inst.args_id); if (resolver.HasNewWork()) { return ResolveResult::Retry(); } return ResolveResult::Deduplicated( resolver, {.type_id = resolver.local_types().GetTypeIdForTypeConstantId(type_id), .callee_id = callee_id, .args_id = GetLocalCanonicalInstBlockId(resolver, inst.args_id, args)}); } static auto TryResolveTypedInst(ImportRefResolver& resolver, SemIR::CharLiteralValue inst) -> ResolveResult { CARBON_CHECK(resolver.import_types().GetTypeInstId(inst.type_id) == SemIR::CharLiteralType::TypeInstId); CARBON_CHECK(!resolver.HasNewWork()); return ResolveResult::Deduplicated( resolver, {.type_id = GetSingletonType(resolver.local_context(), SemIR::CharLiteralType::TypeInstId), .value = inst.value}); } static auto AddPlaceholderNameScope(ImportContext& context) -> SemIR::NameScopeId { return context.local_name_scopes().Add( SemIR::InstId::None, SemIR::NameId::None, SemIR::NameScopeId::None); } // Makes an incomplete class. This is necessary even with classes with a // complete declaration, because things such as `Self` may refer back to the // type. static auto ImportIncompleteClass(ImportContext& context, const SemIR::Class& import_class, SemIR::SpecificId enclosing_specific_id) -> std::pair { SemIR::ClassDecl class_decl = {.type_id = SemIR::TypeType::TypeId, .class_id = SemIR::ClassId::None, .decl_block_id = SemIR::InstBlockId::Empty}; auto class_decl_id = AddPlaceholderImportedInst( context, import_class.latest_decl_id(), class_decl); // Regardless of whether ClassDecl is a complete type, we first need an // incomplete type so that any references have something to point at. class_decl.class_id = context.local_classes().Add( {GetIncompleteLocalEntityBase(context, class_decl_id, import_class), {.self_type_id = SemIR::TypeId::None, .inheritance_kind = import_class.inheritance_kind, .is_dynamic = import_class.is_dynamic, .scope_id = import_class.is_complete() ? AddPlaceholderNameScope(context) : SemIR::NameScopeId::None}}); if (import_class.has_parameters()) { class_decl.type_id = GetGenericClassType( context.local_context(), class_decl.class_id, enclosing_specific_id); } // Write the class ID into the ClassDecl. auto self_const_id = ReplacePlaceholderImportedInst(context, class_decl_id, class_decl); return {class_decl.class_id, self_const_id}; } static auto InitializeNameScopeAndImportRefs( ImportContext& context, const SemIR::NameScope& import_scope, SemIR::NameScope& new_scope, SemIR::InstId decl_id, SemIR::NameId name_id, SemIR::NameScopeId parent_scope_id) { new_scope.Set(decl_id, name_id, parent_scope_id); AddNameScopeImportRefs(context, import_scope, new_scope); } // Fills out the class definition for an incomplete class. static auto ImportClassDefinition(ImportContext& context, const SemIR::Class& import_class, SemIR::Class& new_class, SemIR::InstId complete_type_witness_id, SemIR::InstId base_id, SemIR::InstId adapt_id, SemIR::InstId vtable_decl_id) -> void { new_class.definition_id = new_class.first_owning_decl_id; new_class.complete_type_witness_id = complete_type_witness_id; auto& new_scope = context.local_name_scopes().Get(new_class.scope_id); const auto& import_scope = context.import_name_scopes().Get(import_class.scope_id); // Push a block so that we can add scoped instructions to it. context.local_context().inst_block_stack().Push(); InitializeNameScopeAndImportRefs( context, import_scope, new_scope, new_class.first_owning_decl_id, SemIR::NameId::None, new_class.parent_scope_id); new_class.body_block_id = context.local_context().inst_block_stack().Pop(); if (import_class.base_id.has_value()) { new_class.base_id = base_id; } if (import_class.adapt_id.has_value()) { new_class.adapt_id = adapt_id; } if (import_class.vtable_decl_id.has_value()) { new_class.vtable_decl_id = vtable_decl_id; } } static auto TryResolveTypedInst(ImportRefResolver& resolver, SemIR::ClassDecl inst, SemIR::ConstantId class_const_id) -> ResolveResult { // TODO: The handling of interfaces repeats a lot with the handling of // classes, and will likely also be repeated for named constraints and // choice types. Factor out some of this functionality. const auto& import_class = resolver.import_classes().Get(inst.class_id); SemIR::ClassId class_id = SemIR::ClassId::None; if (!class_const_id.has_value()) { auto import_specific_id = SemIR::SpecificId::None; if (auto import_generic_class_type = resolver.import_types().TryGetAs( inst.type_id)) { import_specific_id = import_generic_class_type->enclosing_specific_id; } auto specific_data = GetLocalSpecificData(resolver, import_specific_id); if (resolver.HasNewWork()) { // This is the end of the first phase. Don't make a new class yet if // we already have new work. return ResolveResult::Retry(); } // On the second phase, create a forward declaration of the class for any // recursive references. auto enclosing_specific_id = GetOrAddLocalSpecific(resolver, import_specific_id, specific_data); std::tie(class_id, class_const_id) = ImportIncompleteClass(resolver, import_class, enclosing_specific_id); } else { // On the third phase, compute the class ID from the constant // value of the declaration. auto class_const_inst = resolver.local_insts().Get( resolver.local_constant_values().GetInstId(class_const_id)); if (auto class_type = class_const_inst.TryAs()) { class_id = class_type->class_id; } else { auto generic_class_type = resolver.local_types().GetAs( class_const_inst.type_id()); class_id = generic_class_type.class_id; } } // Load constants for the definition. auto parent_scope_id = GetLocalNameScopeId(resolver, import_class.parent_scope_id); auto implicit_param_patterns = GetLocalInstBlockContents( resolver, import_class.implicit_param_patterns_id); auto param_patterns = GetLocalInstBlockContents(resolver, import_class.param_patterns_id); auto generic_data = GetLocalGenericData(resolver, import_class.generic_id); auto self_const_id = GetLocalConstantId(resolver, import_class.self_type_id); auto complete_type_witness_const_id = import_class.complete_type_witness_id.has_value() ? GetLocalConstantId(resolver, import_class.complete_type_witness_id) : SemIR::ConstantId::None; auto base_id = import_class.base_id.has_value() ? GetLocalConstantInstId(resolver, import_class.base_id) : SemIR::InstId::None; auto adapt_id = import_class.adapt_id.has_value() ? GetLocalConstantInstId(resolver, import_class.adapt_id) : SemIR::InstId::None; auto vtable_decl_id = import_class.vtable_decl_id.has_value() ? AddImportRef(resolver, import_class.vtable_decl_id) : SemIR::InstId::None; auto& new_class = resolver.local_classes().Get(class_id); if (resolver.HasNewWork()) { return ResolveResult::Retry(class_const_id, new_class.first_decl_id()); } new_class.parent_scope_id = parent_scope_id; new_class.implicit_param_patterns_id = GetLocalCanonicalInstBlockId( resolver, import_class.implicit_param_patterns_id, implicit_param_patterns); new_class.param_patterns_id = GetLocalCanonicalInstBlockId( resolver, import_class.param_patterns_id, param_patterns); new_class.self_type_id = resolver.local_types().GetTypeIdForTypeConstantId(self_const_id); if (import_class.is_complete()) { auto complete_type_witness_id = AddLoadedImportRef( resolver, GetSingletonType(resolver.local_context(), SemIR::WitnessType::TypeInstId), import_class.complete_type_witness_id, complete_type_witness_const_id); ImportClassDefinition(resolver, import_class, new_class, complete_type_witness_id, base_id, adapt_id, vtable_decl_id); } return ResolveResult::FinishGenericOrDone( resolver, class_const_id, new_class.first_decl_id(), import_class.generic_id, new_class.generic_id, generic_data); } static auto TryResolveTypedInst(ImportRefResolver& resolver, SemIR::ClassType inst) -> ResolveResult { CARBON_CHECK(inst.type_id == SemIR::TypeType::TypeId); auto class_const_id = GetLocalConstantId( resolver, resolver.import_classes().Get(inst.class_id).first_owning_decl_id); if (class_const_id == SemIR::ErrorInst::ConstantId) { // TODO: It should be possible to remove this once C++ imports work. return ResolveResult::Done(class_const_id); } auto specific_data = GetLocalSpecificData(resolver, inst.specific_id); if (resolver.HasNewWork()) { return ResolveResult::Retry(); } // Find the corresponding class type. For a non-generic class, this is the // type of the class declaration. For a generic class, build a class type // referencing this specialization of the generic class. auto class_const_inst = resolver.local_insts().Get( resolver.local_constant_values().GetInstId(class_const_id)); if (class_const_inst.Is()) { return ResolveResult::Done(class_const_id); } else { auto generic_class_type = resolver.local_types().GetAs( class_const_inst.type_id()); auto specific_id = GetOrAddLocalSpecific(resolver, inst.specific_id, specific_data); return ResolveResult::Deduplicated( resolver, {.type_id = SemIR::TypeType::TypeId, .class_id = generic_class_type.class_id, .specific_id = specific_id}); } } static auto TryResolveTypedInst(ImportRefResolver& resolver, SemIR::CompleteTypeWitness inst) -> ResolveResult { CARBON_CHECK(resolver.import_types().GetTypeInstId(inst.type_id) == SemIR::WitnessType::TypeInstId); auto object_repr_type_inst_id = GetLocalTypeInstId(resolver, inst.object_repr_type_inst_id); if (resolver.HasNewWork()) { return ResolveResult::Retry(); } return ResolveResult::Deduplicated( resolver, {.type_id = GetSingletonType(resolver.local_context(), SemIR::WitnessType::TypeInstId), .object_repr_type_inst_id = object_repr_type_inst_id}); } template requires SemIR::Internal::HasInstCategory static auto TryResolveTypedInst(ImportRefResolver& resolver, InstT inst) -> ResolveResult { CARBON_CHECK(inst.type_id == SemIR::TypeType::TypeId); auto inner_id = GetLocalTypeInstId(resolver, inst.inner_id); if (resolver.HasNewWork()) { return ResolveResult::Retry(); } return ResolveResult::Deduplicated( resolver, {.type_id = SemIR::TypeType::TypeId, .inner_id = inner_id}); } static auto HandleUnsupportedCppOverloadSet(ImportRefResolver& resolver, SemIR::CppOverloadSetId id) { // Supporting C++ overload resolution of imported functions is a large task, // which might require serializing and deserializing AST for using decl ids, // using modules and/or linking ASTs. resolver.local_context().TODO( SemIR::LocId::None, llvm::formatv( "Unsupported: Importing C++ function `{0}` indirectly", resolver.import_ir().names().GetAsStringIfIdentifier( resolver.import_ir().cpp_overload_sets().Get(id).name_id))); return ResolveResult::Done(SemIR::ErrorInst::ConstantId, SemIR::ErrorInst::InstId); } static auto TryResolveTypedInst(ImportRefResolver& resolver, SemIR::CppOverloadSetType inst) -> ResolveResult { return HandleUnsupportedCppOverloadSet(resolver, inst.overload_set_id); } static auto TryResolveTypedInst(ImportRefResolver& resolver, SemIR::CppOverloadSetValue inst) -> ResolveResult { return HandleUnsupportedCppOverloadSet(resolver, inst.overload_set_id); } static auto TryResolveTypedInst(ImportRefResolver& resolver, SemIR::CustomWitness inst) -> ResolveResult { CARBON_CHECK(resolver.import_types().GetTypeInstId(inst.type_id) == SemIR::WitnessType::TypeInstId); auto elements = GetLocalInstBlockContents(resolver, inst.elements_id); const auto& import_specific_interface = resolver.import_specific_interfaces().Get( inst.query_specific_interface_id); auto data = GetLocalSpecificInterfaceData(resolver, import_specific_interface); if (resolver.HasNewWork()) { return ResolveResult::Retry(); } auto elements_id = GetLocalCanonicalInstBlockId(resolver, inst.elements_id, elements); auto specific_interface = GetLocalSpecificInterface(resolver, import_specific_interface, data); auto query_specific_interface_id = resolver.local_specific_interfaces().Add(specific_interface); return ResolveResult::Deduplicated( resolver, {.type_id = GetSingletonType(resolver.local_context(), SemIR::WitnessType::TypeInstId), .elements_id = elements_id, .query_specific_interface_id = query_specific_interface_id}); } static auto TryResolveTypedInst(ImportRefResolver& resolver, SemIR::ExportDecl inst) -> ResolveResult { auto value_id = GetLocalConstantId(resolver, inst.value_id); return ResolveResult::RetryOrDone(resolver, value_id); } static auto TryResolveTypedInst(ImportRefResolver& resolver, SemIR::FieldDecl inst, SemIR::InstId import_inst_id) -> ResolveResult { auto const_id = GetLocalConstantId(resolver, inst.type_id); if (resolver.HasNewWork()) { return ResolveResult::Retry(); } return ResolveResult::Unique( resolver, import_inst_id, {.type_id = resolver.local_types().GetTypeIdForTypeConstantId(const_id), .name_id = GetLocalNameId(resolver, inst.name_id), .index = inst.index}); } static auto TryResolveTypedInst(ImportRefResolver& resolver, SemIR::FloatLiteralValue inst) -> ResolveResult { CARBON_CHECK(resolver.import_types().GetTypeInstId(inst.type_id) == SemIR::FloatLiteralType::TypeInstId); CARBON_CHECK(!resolver.HasNewWork()); auto real_id = resolver.local_ir().reals().Add( resolver.import_ir().reals().Get(inst.real_id)); return ResolveResult::Deduplicated( resolver, {.type_id = GetSingletonType(resolver.local_context(), SemIR::FloatLiteralType::TypeInstId), .real_id = real_id}); } static auto TryResolveTypedInst(ImportRefResolver& resolver, SemIR::FloatType inst) -> ResolveResult { CARBON_CHECK(inst.type_id == SemIR::TypeType::TypeId); auto bit_width_id = GetLocalConstantInstId(resolver, inst.bit_width_id); if (resolver.HasNewWork()) { return ResolveResult::Retry(); } return ResolveResult::Deduplicated( resolver, {.type_id = SemIR::TypeType::TypeId, .bit_width_id = bit_width_id, .float_kind = inst.float_kind}); } static auto TryResolveTypedInst(ImportRefResolver& resolver, SemIR::FloatValue inst) -> ResolveResult { auto type_id = GetLocalConstantId(resolver, inst.type_id); if (resolver.HasNewWork()) { return ResolveResult::Retry(); } auto float_id = resolver.local_ir().floats().Add( resolver.import_ir().floats().Get(inst.float_id)); return ResolveResult::Deduplicated( resolver, {.type_id = resolver.local_types().GetTypeIdForTypeConstantId(type_id), .float_id = float_id}); } // Make a declaration of a function. This is done as a separate step from // importing the function declaration in order to resolve cycles. static auto ImportFunctionDecl(ImportContext& context, const SemIR::Function& import_function, SemIR::SpecificId specific_id) -> std::pair { SemIR::FunctionDecl function_decl = { .type_id = SemIR::TypeId::None, .function_id = SemIR::FunctionId::None, .decl_block_id = SemIR::InstBlockId::Empty}; auto function_decl_id = AddPlaceholderImportedInst( context, import_function.first_decl_id(), function_decl); // Start with an incomplete function. function_decl.function_id = context.local_functions().Add( {GetIncompleteLocalEntityBase(context, function_decl_id, import_function), {.call_param_patterns_id = SemIR::InstBlockId::None, .call_params_id = SemIR::InstBlockId::None, .call_param_ranges = import_function.call_param_ranges, .return_type_inst_id = SemIR::TypeInstId::None, .return_form_inst_id = SemIR::InstId::None, .return_patterns_id = SemIR::InstBlockId::None, .virtual_modifier = import_function.virtual_modifier, .virtual_index = import_function.virtual_index, .evaluation_mode = import_function.evaluation_mode}}); // Directly add the function type constant. Don't use `GetFunctionType` // because that will evaluate the function type, which we can't do if the // specific's value block is still pending. auto type_const_id = AddImportedConstant( context.local_context(), SemIR::FunctionType{.type_id = SemIR::TypeType::TypeId, .function_id = function_decl.function_id, .specific_id = specific_id}); function_decl.type_id = context.local_types().GetTypeIdForTypeConstantId(type_const_id); // Write the function ID and type into the FunctionDecl. auto function_const_id = ReplacePlaceholderImportedInst(context, function_decl_id, function_decl); return {function_decl.function_id, function_const_id}; } static auto TryResolveTypedInst(ImportRefResolver& resolver, SemIR::FunctionDecl inst, SemIR::ConstantId function_const_id) -> ResolveResult { const auto& import_function = resolver.import_functions().Get(inst.function_id); SemIR::FunctionId function_id = SemIR::FunctionId::None; if (!function_const_id.has_value()) { auto import_specific_id = resolver.import_types() .GetAs(inst.type_id) .specific_id; auto specific_data = GetLocalSpecificData(resolver, import_specific_id); if (resolver.HasNewWork()) { // This is the end of the first phase. Don't make a new function yet if // we already have new work. return ResolveResult::Retry(); } // On the second phase, create a forward declaration of the function. auto specific_id = GetOrAddLocalSpecific(resolver, import_specific_id, specific_data); std::tie(function_id, function_const_id) = ImportFunctionDecl(resolver, import_function, specific_id); } else { // On the third phase, compute the function ID from the constant value of // the declaration. auto function_const_inst = resolver.local_insts().Get( resolver.local_constant_values().GetInstId(function_const_id)); auto function_type = resolver.local_types().GetAs( function_const_inst.type_id()); function_id = function_type.function_id; } auto call_param_patterns = GetLocalInstBlockContents( resolver, import_function.call_param_patterns_id); auto return_type_const_id = SemIR::ConstantId::None; if (import_function.return_type_inst_id.has_value()) { return_type_const_id = GetLocalConstantId(resolver, import_function.return_type_inst_id); } auto return_form_const_id = SemIR::ConstantId::None; if (import_function.return_form_inst_id.has_value()) { return_form_const_id = GetLocalConstantId(resolver, import_function.return_form_inst_id); } auto parent_scope_id = GetLocalNameScopeId(resolver, import_function.parent_scope_id); auto implicit_param_patterns = GetLocalInstBlockContents( resolver, import_function.implicit_param_patterns_id); auto param_patterns = GetLocalInstBlockContents(resolver, import_function.param_patterns_id); auto generic_data = GetLocalGenericData(resolver, import_function.generic_id); auto self_param_id = GetLocalConstantInstId(resolver, import_function.self_param_id); auto return_patterns = GetLocalInstBlockContents(resolver, import_function.return_patterns_id); auto& new_function = resolver.local_functions().Get(function_id); if (resolver.HasNewWork()) { return ResolveResult::Retry(function_const_id, new_function.first_decl_id()); } // Add the function declaration. new_function.call_param_patterns_id = GetLocalCanonicalInstBlockId( resolver, import_function.call_param_patterns_id, call_param_patterns); new_function.parent_scope_id = parent_scope_id; new_function.implicit_param_patterns_id = GetLocalCanonicalInstBlockId( resolver, import_function.implicit_param_patterns_id, implicit_param_patterns); new_function.self_param_id = self_param_id; new_function.param_patterns_id = GetLocalCanonicalInstBlockId( resolver, import_function.param_patterns_id, param_patterns); new_function.return_type_inst_id = SemIR::TypeInstId::None; if (import_function.return_type_inst_id.has_value()) { new_function.return_type_inst_id = AddLoadedImportRefForType( resolver, import_function.return_type_inst_id, return_type_const_id); } new_function.return_form_inst_id = SemIR::InstId::None; if (import_function.return_form_inst_id.has_value()) { new_function.return_form_inst_id = AddLoadedImportRef( resolver, SemIR::FormType::TypeId, import_function.return_form_inst_id, return_form_const_id); } new_function.return_patterns_id = GetLocalCanonicalInstBlockId( resolver, import_function.return_patterns_id, return_patterns); if (import_function.definition_id.has_value()) { new_function.definition_id = new_function.first_owning_decl_id; } switch (import_function.special_function_kind) { case SemIR::Function::SpecialFunctionKind::None: { break; } case SemIR::Function::SpecialFunctionKind::Builtin: { new_function.SetBuiltinFunction(import_function.builtin_function_kind()); break; } case SemIR::Function::SpecialFunctionKind::CoreWitness: { new_function.SetCoreWitness(); break; } case SemIR::Function::SpecialFunctionKind::Thunk: { auto entity_name_id = resolver.local_entity_names().AddCanonical( {.name_id = new_function.name_id, .parent_scope_id = new_function.parent_scope_id}); new_function.SetThunk(AddImportRef( resolver, import_function.thunk_decl_id(), entity_name_id)); break; } case SemIR::Function::SpecialFunctionKind::HasCppThunk: { resolver.local_context().TODO(SemIR::LocId::None, "Unsupported: Importing C++ functions that " "require thunks indirectly"); } } return ResolveResult::FinishGenericOrDone( resolver, function_const_id, new_function.first_decl_id(), import_function.generic_id, new_function.generic_id, generic_data); } static auto TryResolveTypedInst(ImportRefResolver& resolver, SemIR::VtableDecl inst) -> ResolveResult { const auto& import_vtable = resolver.import_vtables().Get(inst.vtable_id); auto class_const_id = GetLocalConstantId(resolver, resolver.import_classes() .Get(import_vtable.class_id) .first_owning_decl_id); auto class_const_inst = resolver.local_insts().Get( resolver.local_constant_values().GetInstId(class_const_id)); // TODO: Ensure the vtable is only imported once, in eg: if there's distinct // vtable constants (imported from multiple libraries using the vtable) that // refer to the same vtable, the vtable should still be singular. auto virtual_functions = resolver.import_inst_blocks().Get(import_vtable.virtual_functions_id); llvm::SmallVector lazy_virtual_functions; lazy_virtual_functions.reserve(virtual_functions.size()); for (auto vtable_entry_id : virtual_functions) { auto local_attached_constant_id = GetLocalConstantId(resolver, vtable_entry_id); lazy_virtual_functions.push_back( resolver.local_constant_values().GetInstIdIfValid( local_attached_constant_id)); } if (resolver.HasNewWork()) { return ResolveResult::Retry(); } for (auto [import_vtable_entry_inst_id, local_vtable_entry_inst_id] : llvm::zip_equal(virtual_functions, lazy_virtual_functions)) { // Use LoadedImportRef for imported symbolic constant vtable entries so they // can carry attached constants necessary for applying specifics to these // constants when they are used. auto local_attached_constant_id = resolver.local_constant_values().GetAttached( local_vtable_entry_inst_id); if (local_attached_constant_id.is_symbolic()) { local_vtable_entry_inst_id = AddLoadedImportRef( resolver, GetSingletonType(resolver.local_context(), SemIR::SpecificFunctionType::TypeInstId), import_vtable_entry_inst_id, local_attached_constant_id); } } auto class_id = SemIR::ClassId::None; if (class_const_inst.Is()) { class_id = class_const_inst.As().class_id; } else { auto generic_class_type = resolver.local_types().GetAs( class_const_inst.type_id()); class_id = generic_class_type.class_id; } auto new_vtable_id = resolver.local_vtables().Add( {{.class_id = class_id, .virtual_functions_id = resolver.local_inst_blocks().Add(lazy_virtual_functions)}}); return ResolveResult::Deduplicated( resolver, {.type_id = GetPointerType(resolver.local_context(), SemIR::VtableType::TypeInstId), .vtable_id = new_vtable_id}); } static auto TryResolveTypedInst(ImportRefResolver& resolver, SemIR::VtablePtr inst) -> ResolveResult { auto specific_data = GetLocalSpecificData(resolver, inst.specific_id); auto vtable_const_id = GetLocalConstantId( resolver, resolver.import_classes() .Get(resolver.import_vtables().Get(inst.vtable_id).class_id) .vtable_decl_id); auto vtable_const_inst = resolver.local_insts().Get( resolver.local_constant_values().GetInstId(vtable_const_id)); if (resolver.HasNewWork()) { return ResolveResult::Retry(); } return ResolveResult::Deduplicated( resolver, {.type_id = GetPointerType(resolver.local_context(), SemIR::VtableType::TypeInstId), .vtable_id = vtable_const_inst.As().vtable_id, .specific_id = GetOrAddLocalSpecific(resolver, inst.specific_id, specific_data)}); } static auto TryResolveTypedInst(ImportRefResolver& resolver, SemIR::FunctionType inst) -> ResolveResult { CARBON_CHECK(inst.type_id == SemIR::TypeType::TypeId); auto fn_val_id = GetLocalConstantInstId( resolver, resolver.import_functions().Get(inst.function_id).first_decl_id()); auto specific_data = GetLocalSpecificData(resolver, inst.specific_id); if (resolver.HasNewWork()) { return ResolveResult::Retry(); } auto fn_type_id = resolver.local_insts().Get(fn_val_id).type_id(); return ResolveResult::Deduplicated( resolver, {.type_id = SemIR::TypeType::TypeId, .function_id = resolver.local_types() .GetAs(fn_type_id) .function_id, .specific_id = GetOrAddLocalSpecific( resolver, inst.specific_id, specific_data)}); } static auto TryResolveTypedInst(ImportRefResolver& resolver, SemIR::FunctionTypeWithSelfType inst) -> ResolveResult { CARBON_CHECK(inst.type_id == SemIR::TypeType::TypeId); auto interface_function_type_id = GetLocalTypeInstId(resolver, inst.interface_function_type_id); auto self_id = GetLocalConstantInstId(resolver, inst.self_id); if (resolver.HasNewWork()) { return ResolveResult::Retry(); } return ResolveResult::Deduplicated( resolver, {.type_id = SemIR::TypeType::TypeId, .interface_function_type_id = interface_function_type_id, .self_id = self_id}); } static auto TryResolveTypedInst(ImportRefResolver& resolver, SemIR::GenericClassType inst) -> ResolveResult { CARBON_CHECK(inst.type_id == SemIR::TypeType::TypeId); auto class_val_id = GetLocalConstantInstId( resolver, resolver.import_classes().Get(inst.class_id).first_owning_decl_id); if (resolver.HasNewWork()) { return ResolveResult::Retry(); } auto class_val = resolver.local_insts().Get(class_val_id); CARBON_CHECK( resolver.local_types().Is(class_val.type_id())); return ResolveResult::Done( resolver.local_types().GetConstantId(class_val.type_id())); } static auto TryResolveTypedInst(ImportRefResolver& resolver, SemIR::GenericInterfaceType inst) -> ResolveResult { CARBON_CHECK(inst.type_id == SemIR::TypeType::TypeId); auto interface_val_id = GetLocalConstantInstId( resolver, resolver.import_interfaces().Get(inst.interface_id).first_owning_decl_id); if (resolver.HasNewWork()) { return ResolveResult::Retry(); } auto interface_val = resolver.local_insts().Get(interface_val_id); CARBON_CHECK(resolver.local_types().Is( interface_val.type_id())); return ResolveResult::Done( resolver.local_types().GetConstantId(interface_val.type_id())); } static auto TryResolveTypedInst(ImportRefResolver& resolver, SemIR::GenericNamedConstraintType inst) -> ResolveResult { CARBON_CHECK(inst.type_id == SemIR::TypeType::TypeId); auto constraint_val_id = GetLocalConstantInstId(resolver, resolver.import_named_constraints() .Get(inst.named_constraint_id) .first_owning_decl_id); if (resolver.HasNewWork()) { return ResolveResult::Retry(); } auto constraint_val = resolver.local_insts().Get(constraint_val_id); CARBON_CHECK(resolver.local_types().Is( constraint_val.type_id())); return ResolveResult::Done( resolver.local_types().GetConstantId(constraint_val.type_id())); } // Make a declaration of an impl. This is done as a separate step from // importing the impl definition in order to resolve cycles. static auto ImportImplDecl(ImportContext& context, const SemIR::Impl& import_impl, SemIR::InstId witness_id) -> std::pair { SemIR::ImplDecl impl_decl = {.impl_id = SemIR::ImplId::None, .decl_block_id = SemIR::InstBlockId::Empty}; auto impl_decl_id = AddPlaceholderImportedInst( context, import_impl.latest_decl_id(), impl_decl); impl_decl.impl_id = context.local_impls().Add( {GetIncompleteLocalEntityBase(context, impl_decl_id, import_impl), {.self_id = SemIR::TypeInstId::None, .constraint_id = SemIR::TypeInstId::None, .interface = SemIR::SpecificInterface::None, .witness_id = witness_id, .scope_id = import_impl.is_complete() ? AddPlaceholderNameScope(context) : SemIR::NameScopeId::None, .is_final = import_impl.is_final}}); // Write the impl ID into the ImplDecl. auto impl_const_id = ReplacePlaceholderImportedInst(context, impl_decl_id, impl_decl); return {impl_decl.impl_id, impl_const_id}; } // Imports the definition of an impl. static auto ImportImplDefinition(ImportContext& context, const SemIR::Impl& import_impl, SemIR::Impl& new_impl) -> void { new_impl.definition_id = new_impl.first_owning_decl_id; new_impl.defined = true; if (import_impl.scope_id.has_value()) { auto& new_scope = context.local_name_scopes().Get(new_impl.scope_id); new_scope.Set(new_impl.first_owning_decl_id, SemIR::NameId::None, new_impl.parent_scope_id); // Import the contents of the definition scope, if we might need it. Name // lookup is never performed into this scope by a user of the impl, so // this is only necessary in the same library that defined the impl, in // order to support defining members of the impl out of line in the impl // file when the impl is defined in the API file. // TODO: Check to see if this impl is owned by the API file, rather than // merely being imported into it. if (context.import_ir_id() == SemIR::ImportIRId::ApiForImpl) { const auto& import_scope = context.import_name_scopes().Get(import_impl.scope_id); // Push a block so that we can add scoped instructions to it. context.local_context().inst_block_stack().Push(); AddNameScopeImportRefs(context, import_scope, new_scope); new_impl.body_block_id = context.local_context().inst_block_stack().Pop(); } } } static auto TryResolveTypedInst(ImportRefResolver& resolver, SemIR::ImplDecl inst, SemIR::ConstantId impl_const_id) -> ResolveResult { // TODO: This duplicates a lot of the handling of interfaces, classes, and // functions. Factor out the commonality. const auto& import_impl = resolver.import_impls().Get(inst.impl_id); auto specific_interface_data = GetLocalSpecificInterfaceData(resolver, import_impl.interface); SemIR::ImplId impl_id = SemIR::ImplId::None; if (!impl_const_id.has_value()) { if (resolver.HasNewWork()) { // This is the end of the first phase. Don't make a new impl yet if we // already have new work. return ResolveResult::Retry(); } // On the second phase, create a forward declaration of the impl for any // recursive references. auto witness_id = AddImportRef(resolver, import_impl.witness_id); std::tie(impl_id, impl_const_id) = ImportImplDecl(resolver, import_impl, witness_id); } else { // On the third phase, compute the impl ID from the "constant value" of // the declaration, which is a reference to the created ImplDecl. auto impl_const_inst = resolver.local_insts().GetAs( resolver.local_constant_values().GetInstId(impl_const_id)); impl_id = impl_const_inst.impl_id; } // Load constants for the definition. auto parent_scope_id = GetLocalNameScopeId(resolver, import_impl.parent_scope_id); auto implicit_param_patterns = GetLocalInstBlockContents( resolver, import_impl.implicit_param_patterns_id); auto generic_data = GetLocalGenericData(resolver, import_impl.generic_id); auto self_const_id = GetLocalConstantId(resolver, import_impl.self_id); auto constraint_const_id = GetLocalConstantId(resolver, import_impl.constraint_id); auto& new_impl = resolver.local_impls().Get(impl_id); if (resolver.HasNewWork()) { return ResolveResult::Retry(impl_const_id, new_impl.first_decl_id()); } new_impl.parent_scope_id = parent_scope_id; new_impl.implicit_param_patterns_id = GetLocalCanonicalInstBlockId( resolver, import_impl.implicit_param_patterns_id, implicit_param_patterns); // Create instructions for self and constraint to hold the symbolic constant // value for a generic impl. new_impl.self_id = AddLoadedImportRefForType(resolver, import_impl.self_id, self_const_id); new_impl.constraint_id = AddLoadedImportRefForType( resolver, import_impl.constraint_id, constraint_const_id); new_impl.interface = GetLocalSpecificInterface( resolver, import_impl.interface, specific_interface_data); // Create a local IdentifiedFacetType for the imported facet type, since impl // declarations always identify the facet type. if (auto facet_type = resolver.local_insts().TryGetAs( resolver.local_constant_values().GetInstId(constraint_const_id))) { // Lookups later will be with the unattached constant, whereas // GetLocalConstantId gave us an attached constant. auto unattached_self_const_id = resolver.local_constant_values().GetUnattachedConstant(self_const_id); RequireIdentifiedFacetType( resolver.local_context(), SemIR::LocId::None, unattached_self_const_id, *facet_type, []([[maybe_unused]] auto& builder) { CARBON_FATAL("Imported impl constraint can't be identified"); }); } if (import_impl.is_complete()) { ImportImplDefinition(resolver, import_impl, new_impl); } // If the `impl` is declared in the API file corresponding to the current // file, add this to impl lookup so that it can be found by redeclarations // in the current file. if (resolver.import_ir_id() == SemIR::ImportIRId::ApiForImpl) { resolver.local_impls().GetOrAddLookupBucket(new_impl).push_back(impl_id); } return ResolveResult::FinishGenericOrDone( resolver, impl_const_id, new_impl.first_decl_id(), import_impl.generic_id, new_impl.generic_id, generic_data); } static auto TryResolveTypedInst(ImportRefResolver& resolver, SemIR::RequireImplsDecl inst, SemIR::ConstantId require_decl_const_id) -> ResolveResult { const auto& import_require = resolver.import_require_impls().Get(inst.require_impls_id); auto require_decl_id = SemIR::InstId::None; auto require_impls_id = SemIR::RequireImplsId::None; if (!require_decl_const_id.has_value()) { // Phase one: Make the decl and structure with placeholder values to be // filled in. Begin the generic so instructions can be attached to it. SemIR::RequireImplsDecl require_decl = { .require_impls_id = SemIR::RequireImplsId::None, .decl_block_id = SemIR::InstBlockId::Empty}; auto require_decl_id = AddPlaceholderImportedInst( resolver, import_require.decl_id, require_decl); require_impls_id = resolver.local_require_impls().Add( {.self_id = SemIR::TypeInstId::None, .facet_type_inst_id = SemIR::TypeInstId::None, .decl_id = require_decl_id, .parent_scope_id = SemIR::NameScopeId::None, .generic_id = ImportIncompleteGeneric(resolver, require_decl_id, import_require.generic_id)}); // Write the RequireImplsId into the RequireImplsDecl. require_decl.require_impls_id = require_impls_id; require_decl_const_id = ReplacePlaceholderImportedInst(resolver, require_decl_id, require_decl); } else { // Phase two: Get the `require_decl_id` and `require_impls_id` from the // RequireImplsDecl constructed in phase one. require_decl_id = resolver.local_constant_values().GetInstId(require_decl_const_id); require_impls_id = resolver.local_insts() .GetAs(require_decl_id) .require_impls_id; } // Load dependent constants. auto parent_scope_id = GetLocalNameScopeId(resolver, import_require.parent_scope_id); auto generic_data = GetLocalGenericData(resolver, import_require.generic_id); auto self_const_id = GetLocalConstantId(resolver, import_require.self_id); auto facet_type_const_id = GetLocalConstantId(resolver, import_require.facet_type_inst_id); if (resolver.HasNewWork()) { return ResolveResult::Retry(require_decl_const_id, require_decl_id); } // Fill in the RequireImpls structure. auto& new_require = resolver.local_require_impls().Get(require_impls_id); new_require.self_id = AddLoadedImportRefForType( resolver, import_require.self_id, self_const_id); new_require.facet_type_inst_id = AddLoadedImportRefForType( resolver, import_require.facet_type_inst_id, facet_type_const_id); new_require.extend_self = import_require.extend_self; new_require.parent_scope_id = parent_scope_id; return ResolveResult::FinishGenericOrDone( resolver, require_decl_const_id, require_decl_id, import_require.generic_id, new_require.generic_id, generic_data); } static auto TryResolveTypedInst(ImportRefResolver& resolver, SemIR::ImportRefLoaded /*inst*/, SemIR::InstId inst_id) -> ResolveResult { // Return the constant for the instruction of the imported constant. auto constant_id = resolver.import_constant_values().Get(inst_id); CARBON_CHECK(constant_id.has_value(), "Loaded import ref has no constant value"); if (!constant_id.is_constant()) { resolver.local_context().TODO( inst_id, "Non-constant ImportRefLoaded (comes up with var)"); return ResolveResult::Done(constant_id); } auto new_constant_id = GetLocalConstantId(resolver, constant_id); return ResolveResult::RetryOrDone(resolver, new_constant_id); } // Make a declaration of an interface. This is done as a separate step from // importing the interface definition in order to resolve cycles. static auto ImportInterfaceDecl(ImportContext& context, const SemIR::Interface& import_interface, SemIR::SpecificId enclosing_specific_id) -> std::pair { SemIR::InterfaceDecl interface_decl = { .type_id = SemIR::TypeType::TypeId, .interface_id = SemIR::InterfaceId::None, .decl_block_id = SemIR::InstBlockId::Empty}; auto interface_decl_id = AddPlaceholderImportedInst( context, import_interface.first_owning_decl_id, interface_decl); // Start with an incomplete interface. // // The generic_with_self_id is constructed by the InterfaceWithSelfDecl // instruction inside the InterfaceDecl's body. interface_decl.interface_id = context.local_interfaces().Add( {GetIncompleteLocalEntityBase(context, interface_decl_id, import_interface), {.scope_without_self_id = import_interface.is_complete() ? AddPlaceholderNameScope(context) : SemIR::NameScopeId::None, .scope_with_self_id = import_interface.is_complete() ? AddPlaceholderNameScope(context) : SemIR::NameScopeId::None}}); if (import_interface.has_parameters()) { interface_decl.type_id = GetGenericInterfaceType( context.local_context(), interface_decl.interface_id, enclosing_specific_id); } // Write the interface ID into the InterfaceDecl. auto interface_const_id = ReplacePlaceholderImportedInst( context, interface_decl_id, interface_decl); return {interface_decl.interface_id, interface_const_id}; } // Imports the definition for an interface that has been imported as a forward // declaration. static auto ImportInterfaceDefinition(ImportContext& context, const SemIR::Interface& import_interface, SemIR::Interface& new_interface, SemIR::InstId self_param_id) -> void { auto& new_scope = context.local_name_scopes().Get(new_interface.scope_without_self_id); const auto& import_scope = context.import_name_scopes().Get(import_interface.scope_without_self_id); // Push a block so that we can add scoped instructions to it. context.local_context().inst_block_stack().Push(); InitializeNameScopeAndImportRefs( context, import_scope, new_scope, new_interface.first_owning_decl_id, SemIR::NameId::None, new_interface.parent_scope_id); new_interface.body_block_without_self_id = context.local_context().inst_block_stack().Pop(); new_interface.self_param_id = self_param_id; } static auto TryResolveTypedInst(ImportRefResolver& resolver, SemIR::InterfaceDecl inst, SemIR::ConstantId interface_const_id) -> ResolveResult { const auto& import_interface = resolver.import_interfaces().Get(inst.interface_id); auto interface_id = SemIR::InterfaceId::None; if (!interface_const_id.has_value()) { auto import_specific_id = SemIR::SpecificId::None; if (auto import_generic_interface_type = resolver.import_types().TryGetAs( inst.type_id)) { import_specific_id = import_generic_interface_type->enclosing_specific_id; } auto specific_data = GetLocalSpecificData(resolver, import_specific_id); if (resolver.HasNewWork()) { // This is the end of the first phase. Don't make a new interface yet if // we already have new work. return ResolveResult::Retry(); } // On the second phase, create a forward declaration of the interface. auto enclosing_specific_id = GetOrAddLocalSpecific(resolver, import_specific_id, specific_data); std::tie(interface_id, interface_const_id) = ImportInterfaceDecl(resolver, import_interface, enclosing_specific_id); } else { // On the third phase, compute the interface ID from the constant value of // the declaration. auto interface_const_inst = resolver.local_insts().Get( resolver.local_constant_values().GetInstId(interface_const_id)); if (auto facet_type = interface_const_inst.TryAs()) { const SemIR::FacetTypeInfo& facet_type_info = resolver.local_facet_types().Get(facet_type->facet_type_id); auto single = facet_type_info.TryAsSingleExtend(); CARBON_CHECK(single); interface_id = std::get(*single).interface_id; } else { auto generic_interface_type = resolver.local_types().GetAs( interface_const_inst.type_id()); interface_id = generic_interface_type.interface_id; } } auto parent_scope_id = GetLocalNameScopeId(resolver, import_interface.parent_scope_id); auto implicit_param_patterns = GetLocalInstBlockContents( resolver, import_interface.implicit_param_patterns_id); auto param_patterns = GetLocalInstBlockContents(resolver, import_interface.param_patterns_id); auto generic_data = GetLocalGenericData(resolver, import_interface.generic_id); auto require_impls = GetLocalRequireImplsBlockContents( resolver, import_interface.require_impls_block_id); std::optional self_param_id; if (import_interface.is_complete()) { // Note the TODO on ResolveLocalEvalBlock, the generic eval block is rebuilt // instead of being imported. When it's imported maybe this should be an // ImportRef? self_param_id = GetLocalConstantInstId(resolver, import_interface.self_param_id); // Importing the `generic_with_self_id` imports the InterfaceWithSelfDecl // which sets the associated constants in the interface (if it's complete) // which marks the local interface as complete. The InterfaceWithSelfDecl // also sets the `generic_with_self_id` field on the local interface. GetLocalConstantId(resolver, import_interface.generic_with_self_id); } auto& new_interface = resolver.local_interfaces().Get(interface_id); if (resolver.HasNewWork()) { return ResolveResult::Retry(interface_const_id, new_interface.first_decl_id()); } new_interface.parent_scope_id = parent_scope_id; new_interface.implicit_param_patterns_id = GetLocalCanonicalInstBlockId( resolver, import_interface.implicit_param_patterns_id, implicit_param_patterns); new_interface.param_patterns_id = GetLocalCanonicalInstBlockId( resolver, import_interface.param_patterns_id, param_patterns); new_interface.require_impls_block_id = GetLocalCanonicalRequireImplsBlockId( resolver, import_interface.require_impls_block_id, require_impls); if (import_interface.is_complete()) { CARBON_CHECK(self_param_id); ImportInterfaceDefinition(resolver, import_interface, new_interface, *self_param_id); } // The interface's `generic_with_self_id` is filled out and finished by // importing the InterfaceWithSelfDecl instruction which we find inside the // InterfaceDecl. return ResolveResult::FinishGenericOrDone( resolver, interface_const_id, new_interface.first_decl_id(), import_interface.generic_id, new_interface.generic_id, generic_data); } static auto TryResolveTypedInst(ImportRefResolver& resolver, SemIR::InterfaceWithSelfDecl inst, SemIR::ConstantId const_id) -> ResolveResult { auto interface_const_id = GetLocalConstantId( resolver, resolver.import_interfaces().Get(inst.interface_id).first_owning_decl_id); // These are set differently in each phase. auto decl_id = SemIR::InstId::None; auto local_interface_id = SemIR::InterfaceId::None; auto generic_with_self_id = SemIR::GenericId::None; // Note that InterfaceWithSelfDecl always occurs inside an InterfaceDecl, so // the import here can rely on the `Interface` already existing. auto import_generic_with_self_id = resolver.import_interfaces().Get(inst.interface_id).generic_with_self_id; if (!const_id.has_value()) { if (resolver.HasNewWork()) { // This is the end of the first phase. Don't make a new generic yet if we // already have new work. return ResolveResult::Retry(); } // Get the local interface ID from the constant value of the interface decl, // which is either a GenericInterfaceType (if generic) or a FacetType (if // not). auto interface_const_inst_id = resolver.local_constant_values().GetInstId(interface_const_id); if (auto struct_value = resolver.local_insts().TryGetAs( interface_const_inst_id)) { auto generic_interface_type = resolver.local_types().GetAs( struct_value->type_id); local_interface_id = generic_interface_type.interface_id; } else { auto local_facet_type = resolver.local_insts().GetAs( interface_const_inst_id); const auto& local_facet_type_info = resolver.local_facet_types().Get(local_facet_type.facet_type_id); auto single_interface = *local_facet_type_info.TryAsSingleExtend(); CARBON_KIND_SWITCH(single_interface) { case CARBON_KIND(SemIR::SpecificInterface specific_interface): { local_interface_id = specific_interface.interface_id; break; } case CARBON_KIND(SemIR::SpecificNamedConstraint _): { CARBON_FATAL( "Unexpected NamedConstraint in InterfaceDecl value's facet type"); } } } // On the second phase, create a local decl instruction with a local generic // ID. Store that generic ID in the local interface. const auto& import_generic = resolver.import_generics().Get(import_generic_with_self_id); SemIR::InterfaceWithSelfDecl interface_with_self_decl = { .interface_id = SemIR::InterfaceId::None}; decl_id = AddPlaceholderImportedInst(resolver, import_generic.decl_id, interface_with_self_decl); generic_with_self_id = ImportIncompleteGeneric(resolver, decl_id, import_generic_with_self_id); interface_with_self_decl.interface_id = local_interface_id; const_id = ReplacePlaceholderImportedInst(resolver, decl_id, interface_with_self_decl); resolver.local_interfaces().Get(local_interface_id).generic_with_self_id = generic_with_self_id; } else { // On the third phase, get the interface, decl and generic IDs from the // constant value of the decl (which is itself) from the second phase. auto decl = resolver.local_insts().GetAs( resolver.local_constant_values().GetInstId(const_id)); local_interface_id = decl.interface_id; generic_with_self_id = resolver.local_interfaces() .Get(local_interface_id) .generic_with_self_id; decl_id = resolver.local_generics().Get(generic_with_self_id).decl_id; } auto generic_with_self_data = GetLocalGenericData(resolver, import_generic_with_self_id); if (resolver.HasNewWork()) { return ResolveResult::Retry(const_id, decl_id); } auto& local_interface = resolver.local_interfaces().Get( resolver.local_insts() .GetAs(decl_id) .interface_id); const auto& import_interface = resolver.import_interfaces().Get(inst.interface_id); auto& new_scope = resolver.local_name_scopes().Get(local_interface.scope_with_self_id); const auto& import_scope = resolver.import_name_scopes().Get(import_interface.scope_with_self_id); // Push a block so that we can add scoped instructions to it. resolver.local_context().inst_block_stack().Push(); InitializeNameScopeAndImportRefs(resolver, import_scope, new_scope, decl_id, SemIR::NameId::None, local_interface.scope_without_self_id); new_scope.set_is_interface_definition(); local_interface.associated_entities_id = AddAssociatedEntities(resolver, local_interface.scope_with_self_id, import_interface.associated_entities_id); local_interface.body_block_with_self_id = resolver.local_context().inst_block_stack().Pop(); return ResolveResult::FinishGenericOrDone( resolver, const_id, decl_id, import_generic_with_self_id, generic_with_self_id, generic_with_self_data); } // Make a declaration of a named constraint. This is done as a separate step // from importing the constraint definition in order to resolve cycles. static auto ImportNamedConstraintDecl( ImportContext& context, const SemIR::NamedConstraint& import_named_constraint, SemIR::SpecificId enclosing_specific_id) -> std::pair { SemIR::NamedConstraintDecl named_constraint_decl = { .type_id = SemIR::TypeType::TypeId, .named_constraint_id = SemIR::NamedConstraintId::None, .decl_block_id = SemIR::InstBlockId::Empty}; auto named_constraint_decl_id = AddPlaceholderImportedInst( context, import_named_constraint.first_owning_decl_id, named_constraint_decl); // Start with an incomplete named constraint. // // The generic_with_self_id is constructed by the NamedConstraintWithSelfDecl // instruction inside the NamedConstraintDecl's body. named_constraint_decl.named_constraint_id = context.local_named_constraints().Add( {GetIncompleteLocalEntityBase(context, named_constraint_decl_id, import_named_constraint), {.scope_without_self_id = import_named_constraint.is_complete() ? AddPlaceholderNameScope(context) : SemIR::NameScopeId::None, .scope_with_self_id = import_named_constraint.is_complete() ? AddPlaceholderNameScope(context) : SemIR::NameScopeId::None}}); if (import_named_constraint.has_parameters()) { named_constraint_decl.type_id = GetGenericNamedConstraintType( context.local_context(), named_constraint_decl.named_constraint_id, enclosing_specific_id); } // Write the named constraint ID into the NameConstraintDecl. auto interface_const_id = ReplacePlaceholderImportedInst( context, named_constraint_decl_id, named_constraint_decl); return {named_constraint_decl.named_constraint_id, interface_const_id}; } // Imports the definition for a named constraint that has been imported as a // forward declaration. static auto ImportNamedConstraintDefinition( ImportContext& context, const SemIR::NamedConstraint& import_named_constraint, SemIR::NamedConstraint& new_named_constraint, SemIR::InstId self_param_id) -> void { auto& new_scope = context.local_name_scopes().Get( new_named_constraint.scope_without_self_id); const auto& import_scope = context.import_name_scopes().Get( import_named_constraint.scope_without_self_id); // Push a block so that we can add scoped instructions to it. context.local_context().inst_block_stack().Push(); InitializeNameScopeAndImportRefs(context, import_scope, new_scope, new_named_constraint.first_owning_decl_id, SemIR::NameId::None, new_named_constraint.parent_scope_id); new_named_constraint.body_block_without_self_id = context.local_context().inst_block_stack().Pop(); new_named_constraint.self_param_id = self_param_id; } static auto TryResolveTypedInst(ImportRefResolver& resolver, SemIR::NamedConstraintDecl inst, SemIR::ConstantId named_constraint_const_id) -> ResolveResult { const auto& import_named_constraint = resolver.import_named_constraints().Get(inst.named_constraint_id); auto named_constraint_id = SemIR::NamedConstraintId::None; if (!named_constraint_const_id.has_value()) { auto import_specific_id = SemIR::SpecificId::None; if (auto import_generic_named_constraint_type = resolver.import_types().TryGetAs( inst.type_id)) { import_specific_id = import_generic_named_constraint_type->enclosing_specific_id; } auto specific_data = GetLocalSpecificData(resolver, import_specific_id); if (resolver.HasNewWork()) { // This is the end of the first phase. Don't make a new interface yet if // we already have new work. return ResolveResult::Retry(); } // On the second phase, create a forward declaration of the interface. auto enclosing_specific_id = GetOrAddLocalSpecific(resolver, import_specific_id, specific_data); std::tie(named_constraint_id, named_constraint_const_id) = ImportNamedConstraintDecl(resolver, import_named_constraint, enclosing_specific_id); } else { // On the third phase, compute the interface ID from the constant value of // the declaration. auto named_constraint_const_inst = resolver.local_insts().Get( resolver.local_constant_values().GetInstId(named_constraint_const_id)); if (auto facet_type = named_constraint_const_inst.TryAs()) { const SemIR::FacetTypeInfo& facet_type_info = resolver.local_facet_types().Get(facet_type->facet_type_id); auto single = facet_type_info.TryAsSingleExtend(); CARBON_CHECK(single); named_constraint_id = std::get(*single).named_constraint_id; } else { auto generic_named_constraint_type = resolver.local_types().GetAs( named_constraint_const_inst.type_id()); named_constraint_id = generic_named_constraint_type.named_constraint_id; } } auto parent_scope_id = GetLocalNameScopeId(resolver, import_named_constraint.parent_scope_id); auto implicit_param_patterns = GetLocalInstBlockContents( resolver, import_named_constraint.implicit_param_patterns_id); auto param_patterns = GetLocalInstBlockContents( resolver, import_named_constraint.param_patterns_id); auto generic_data = GetLocalGenericData(resolver, import_named_constraint.generic_id); auto require_impls = GetLocalRequireImplsBlockContents( resolver, import_named_constraint.require_impls_block_id); std::optional self_param_id; if (import_named_constraint.is_complete()) { self_param_id = GetLocalConstantInstId(resolver, import_named_constraint.self_param_id); // Importing the `generic_with_self_id` imports the // NamedConstraintWithSelfDecl which (if it's complete) marks the local // named constraint as complete. The NamedConstraintWithSelfDecl also sets // the `generic_with_self_id` field on the local interface. GetLocalConstantId(resolver, import_named_constraint.generic_with_self_id); } auto& new_named_constraint = resolver.local_named_constraints().Get(named_constraint_id); if (resolver.HasNewWork()) { return ResolveResult::Retry(named_constraint_const_id, new_named_constraint.first_decl_id()); } new_named_constraint.parent_scope_id = parent_scope_id; new_named_constraint.implicit_param_patterns_id = GetLocalCanonicalInstBlockId( resolver, import_named_constraint.implicit_param_patterns_id, implicit_param_patterns); new_named_constraint.param_patterns_id = GetLocalCanonicalInstBlockId( resolver, import_named_constraint.param_patterns_id, param_patterns); new_named_constraint.require_impls_block_id = GetLocalCanonicalRequireImplsBlockId( resolver, import_named_constraint.require_impls_block_id, require_impls); if (import_named_constraint.is_complete()) { CARBON_CHECK(self_param_id); ImportNamedConstraintDefinition(resolver, import_named_constraint, new_named_constraint, *self_param_id); } // The named constraint's `generic_with_self_id` is filled out and finished by // importing the NamedConstraintWithSelfDecl instruction which we find inside // the NamedConstraintDecl. return ResolveResult::FinishGenericOrDone( resolver, named_constraint_const_id, new_named_constraint.first_decl_id(), import_named_constraint.generic_id, new_named_constraint.generic_id, generic_data); } static auto TryResolveTypedInst(ImportRefResolver& resolver, SemIR::NamedConstraintWithSelfDecl inst, SemIR::ConstantId const_id) -> ResolveResult { auto constraint_const_id = GetLocalConstantId(resolver, resolver.import_named_constraints() .Get(inst.named_constraint_id) .first_owning_decl_id); // These are set differently in each phase. auto decl_id = SemIR::InstId::None; auto local_constraint_id = SemIR::NamedConstraintId::None; auto generic_with_self_id = SemIR::GenericId::None; // Note that NamedConstraintWithSelfDecl always occurs inside an // NamedConstraintDecl, so the import here can rely on the `NamedConstraint` // already existing. auto import_generic_with_self_id = resolver.import_named_constraints() .Get(inst.named_constraint_id) .generic_with_self_id; if (!const_id.has_value()) { if (resolver.HasNewWork()) { // This is the end of the first phase. Don't make a new generic yet if we // already have new work. return ResolveResult::Retry(); } // Get the local named constraint ID from the constant value of the named // constraint decl, which is either a GenericNamedConstraintType (if // generic) or a FacetType (if not). auto constraint_const_inst_id = resolver.local_constant_values().GetInstId(constraint_const_id); if (auto struct_value = resolver.local_insts().TryGetAs( constraint_const_inst_id)) { auto generic_constraint_type = resolver.local_types().GetAs( struct_value->type_id); local_constraint_id = generic_constraint_type.named_constraint_id; } else { auto local_facet_type = resolver.local_insts().GetAs( constraint_const_inst_id); const auto& local_facet_type_info = resolver.local_facet_types().Get(local_facet_type.facet_type_id); auto single_interface = *local_facet_type_info.TryAsSingleExtend(); CARBON_KIND_SWITCH(single_interface) { case CARBON_KIND(SemIR::SpecificNamedConstraint specific_constraint): { local_constraint_id = specific_constraint.named_constraint_id; break; } case CARBON_KIND(SemIR::SpecificInterface _): { CARBON_FATAL( "Unexpected Interface in NamedConstraintDecl value's facet type"); } } } // On the second phase, create a local decl instruction with a local generic // ID. Store that generic ID in the local interface. const auto& import_generic = resolver.import_generics().Get(import_generic_with_self_id); SemIR::NamedConstraintWithSelfDecl constraint_with_self_decl = { .named_constraint_id = SemIR::NamedConstraintId::None}; decl_id = AddPlaceholderImportedInst(resolver, import_generic.decl_id, constraint_with_self_decl); generic_with_self_id = ImportIncompleteGeneric(resolver, decl_id, import_generic_with_self_id); constraint_with_self_decl.named_constraint_id = local_constraint_id; const_id = ReplacePlaceholderImportedInst(resolver, decl_id, constraint_with_self_decl); resolver.local_named_constraints() .Get(local_constraint_id) .generic_with_self_id = generic_with_self_id; } else { // On the third phase, get the interface, decl and generic IDs from the // constant value of the decl (which is itself) from the second phase. auto decl = resolver.local_insts().GetAs( resolver.local_constant_values().GetInstId(const_id)); local_constraint_id = decl.named_constraint_id; generic_with_self_id = resolver.local_named_constraints() .Get(local_constraint_id) .generic_with_self_id; decl_id = resolver.local_generics().Get(generic_with_self_id).decl_id; } auto generic_with_self_data = GetLocalGenericData(resolver, import_generic_with_self_id); if (resolver.HasNewWork()) { return ResolveResult::Retry(const_id, decl_id); } auto& local_constraint = resolver.local_named_constraints().Get( resolver.local_insts() .GetAs(decl_id) .named_constraint_id); const auto& import_constraint = resolver.import_named_constraints().Get(inst.named_constraint_id); auto& new_scope = resolver.local_name_scopes().Get(local_constraint.scope_with_self_id); const auto& import_scope = resolver.import_name_scopes().Get(import_constraint.scope_with_self_id); // Push a block so that we can add scoped instructions to it. resolver.local_context().inst_block_stack().Push(); InitializeNameScopeAndImportRefs(resolver, import_scope, new_scope, decl_id, SemIR::NameId::None, local_constraint.scope_without_self_id); local_constraint.complete = import_constraint.complete; local_constraint.body_block_with_self_id = resolver.local_context().inst_block_stack().Pop(); return ResolveResult::FinishGenericOrDone( resolver, const_id, decl_id, import_generic_with_self_id, generic_with_self_id, generic_with_self_data); } static auto TryResolveTypedInst(ImportRefResolver& resolver, SemIR::FacetAccessType inst) -> ResolveResult { auto facet_value_inst_id = GetLocalConstantInstId(resolver, inst.facet_value_inst_id); if (resolver.HasNewWork()) { return ResolveResult::Retry(); } return ResolveResult::Deduplicated( resolver, {.type_id = SemIR::TypeType::TypeId, .facet_value_inst_id = facet_value_inst_id}); } // Collects and assigns constants for a `FacetTypeInfo`. Discards constants when // `local_facet_type_info` is null. static auto ResolveFacetTypeInfo( ImportRefResolver& resolver, const SemIR::FacetTypeInfo& import_facet_type_info, SemIR::FacetTypeInfo* local_facet_type_info) -> void { if (local_facet_type_info) { local_facet_type_info->extend_constraints.reserve( import_facet_type_info.extend_constraints.size()); } for (auto interface : import_facet_type_info.extend_constraints) { auto data = GetLocalSpecificInterfaceData(resolver, interface); if (local_facet_type_info) { local_facet_type_info->extend_constraints.push_back( GetLocalSpecificInterface(resolver, interface, data)); } } if (local_facet_type_info) { local_facet_type_info->self_impls_constraints.reserve( import_facet_type_info.self_impls_constraints.size()); } for (auto interface : import_facet_type_info.self_impls_constraints) { auto data = GetLocalSpecificInterfaceData(resolver, interface); if (local_facet_type_info) { local_facet_type_info->self_impls_constraints.push_back( GetLocalSpecificInterface(resolver, interface, data)); } } if (local_facet_type_info) { local_facet_type_info->extend_named_constraints.reserve( import_facet_type_info.extend_named_constraints.size()); } for (auto constraint : import_facet_type_info.extend_named_constraints) { auto data = GetLocalSpecificNamedConstraintData(resolver, constraint); if (local_facet_type_info) { local_facet_type_info->extend_named_constraints.push_back( GetLocalSpecificNamedConstraint(resolver, constraint, data)); } } if (local_facet_type_info) { local_facet_type_info->self_impls_named_constraints.reserve( import_facet_type_info.self_impls_named_constraints.size()); } for (auto constraint : import_facet_type_info.self_impls_named_constraints) { auto data = GetLocalSpecificNamedConstraintData(resolver, constraint); if (local_facet_type_info) { local_facet_type_info->self_impls_named_constraints.push_back( GetLocalSpecificNamedConstraint(resolver, constraint, data)); } } if (local_facet_type_info) { local_facet_type_info->rewrite_constraints.reserve( import_facet_type_info.rewrite_constraints.size()); } for (auto rewrite : import_facet_type_info.rewrite_constraints) { auto lhs_id = GetLocalConstantInstId(resolver, rewrite.lhs_id); auto rhs_id = GetLocalConstantInstId(resolver, rewrite.rhs_id); if (local_facet_type_info) { local_facet_type_info->rewrite_constraints.push_back( {.lhs_id = lhs_id, .rhs_id = rhs_id}); } } } static auto TryResolveTypedInst(ImportRefResolver& resolver, SemIR::FacetType inst) -> ResolveResult { CARBON_CHECK(inst.type_id == SemIR::TypeType::TypeId); const SemIR::FacetTypeInfo& import_facet_type_info = resolver.import_facet_types().Get(inst.facet_type_id); // Ensure values are imported, but discard them to avoid allocations. ResolveFacetTypeInfo(resolver, import_facet_type_info, /*local_facet_type_info=*/nullptr); if (resolver.HasNewWork()) { return ResolveResult::Retry(); } SemIR::FacetTypeInfo local_facet_type_info = { // TODO: Also process the other requirements. .other_requirements = import_facet_type_info.other_requirements}; // Re-resolve and add values to the local `FacetTypeInfo`. ResolveFacetTypeInfo(resolver, import_facet_type_info, &local_facet_type_info); SemIR::FacetTypeId facet_type_id = resolver.local_facet_types().Add(std::move(local_facet_type_info)); return ResolveResult::Deduplicated( resolver, {.type_id = SemIR::TypeType::TypeId, .facet_type_id = facet_type_id}); } static auto TryResolveTypedInst(ImportRefResolver& resolver, SemIR::FacetValue inst) -> ResolveResult { auto type_id = GetLocalConstantId(resolver, inst.type_id); auto type_inst_id = GetLocalTypeInstId(resolver, inst.type_inst_id); auto witnesses = GetLocalInstBlockContents(resolver, inst.witnesses_block_id); if (resolver.HasNewWork()) { return ResolveResult::Retry(); } auto witnesses_block_id = SemIR::InstBlockId::None; if (inst.witnesses_block_id.has_value()) { witnesses_block_id = AddCanonicalWitnessesBlock(resolver.local_ir(), witnesses); } return ResolveResult::Deduplicated( resolver, {.type_id = resolver.local_types().GetTypeIdForTypeConstantId(type_id), .type_inst_id = type_inst_id, .witnesses_block_id = witnesses_block_id}); } static auto TryResolveTypedInst(ImportRefResolver& resolver, SemIR::LookupImplWitness inst) -> ResolveResult { CARBON_CHECK(resolver.import_types().GetTypeInstId(inst.type_id) == SemIR::WitnessType::TypeInstId); auto query_self_inst_id = GetLocalConstantInstId(resolver, inst.query_self_inst_id); const auto& import_specific_interface = resolver.import_specific_interfaces().Get( inst.query_specific_interface_id); auto data = GetLocalSpecificInterfaceData(resolver, import_specific_interface); if (resolver.HasNewWork()) { return ResolveResult::Retry(); } auto specific_interface = GetLocalSpecificInterface(resolver, import_specific_interface, data); auto query_specific_interface_id = resolver.local_specific_interfaces().Add(specific_interface); return ResolveResult::Deduplicated( resolver, {.type_id = GetSingletonType(resolver.local_context(), SemIR::WitnessType::TypeInstId), .query_self_inst_id = query_self_inst_id, .query_specific_interface_id = query_specific_interface_id}); } static auto TryResolveTypedInst(ImportRefResolver& resolver, SemIR::ImplWitness inst) -> ResolveResult { CARBON_CHECK(resolver.import_types().GetTypeInstId(inst.type_id) == SemIR::WitnessType::TypeInstId); auto specific_data = GetLocalSpecificData(resolver, inst.specific_id); auto witness_table_id = GetLocalConstantInstId(resolver, inst.witness_table_id); if (resolver.HasNewWork()) { return ResolveResult::Retry(); } auto specific_id = GetOrAddLocalSpecific(resolver, inst.specific_id, specific_data); return ResolveResult::Deduplicated( resolver, {.type_id = GetSingletonType(resolver.local_context(), SemIR::WitnessType::TypeInstId), .witness_table_id = witness_table_id, .specific_id = specific_id}); } static auto TryResolveTypedInst(ImportRefResolver& resolver, SemIR::ImplWitnessAccess inst) -> ResolveResult { auto type_id = GetLocalConstantId(resolver, inst.type_id); auto witness_id = GetLocalConstantInstId(resolver, inst.witness_id); if (resolver.HasNewWork()) { return ResolveResult::Retry(); } return ResolveResult::Deduplicated( resolver, {.type_id = resolver.local_types().GetTypeIdForTypeConstantId(type_id), .witness_id = witness_id, .index = inst.index}); } static auto TryResolveTypedInst(ImportRefResolver& resolver, SemIR::ImplWitnessTable inst, SemIR::InstId import_inst_id) -> ResolveResult { const auto& import_impl = resolver.import_impls().Get(inst.impl_id); auto import_decl_inst_id = import_impl.first_decl_id(); auto local_decl_inst_id = GetLocalConstantInstId(resolver, import_decl_inst_id); if (resolver.HasNewWork()) { return ResolveResult::Retry(); } auto impl_decl = resolver.local_insts().GetAs(local_decl_inst_id); auto impl_id = impl_decl.impl_id; auto elements_id = GetLocalImportRefInstBlock(resolver, inst.elements_id); // Create a corresponding instruction to represent the table. return ResolveResult::Unique( resolver, import_inst_id, {.elements_id = elements_id, .impl_id = impl_id}); } static auto TryResolveTypedInst(ImportRefResolver& resolver, SemIR::InitForm inst) -> ResolveResult { auto type_const_id = GetLocalConstantId(resolver, inst.type_id); auto type_component_const_id = GetLocalConstantId(resolver, inst.type_component_inst_id); if (resolver.HasNewWork()) { return ResolveResult::Retry(); } return ResolveResult::Deduplicated( resolver, SemIR::InitForm{ .type_id = resolver.local_types().GetTypeIdForTypeConstantId(type_const_id), .type_component_inst_id = resolver.local_types().GetTypeInstId( resolver.local_types().GetTypeIdForTypeConstantId( type_component_const_id))}); } static auto TryResolveTypedInst(ImportRefResolver& resolver, SemIR::IntValue inst) -> ResolveResult { auto type_id = GetLocalConstantId(resolver, inst.type_id); if (resolver.HasNewWork()) { return ResolveResult::Retry(); } // We can directly reuse the value IDs across file IRs. Otherwise, we need // to add a new canonical int in this IR. auto int_id = inst.int_id.is_embedded_value() ? inst.int_id : resolver.local_ints().AddSigned( resolver.import_ints().Get(inst.int_id)); return ResolveResult::Deduplicated( resolver, {.type_id = resolver.local_types().GetTypeIdForTypeConstantId(type_id), .int_id = int_id}); } static auto TryResolveTypedInst(ImportRefResolver& resolver, SemIR::IntType inst) -> ResolveResult { CARBON_CHECK(inst.type_id == SemIR::TypeType::TypeId); auto bit_width_id = GetLocalConstantInstId(resolver, inst.bit_width_id); if (resolver.HasNewWork()) { return ResolveResult::Retry(); } return ResolveResult::Deduplicated( resolver, {.type_id = SemIR::TypeType::TypeId, .int_kind = inst.int_kind, .bit_width_id = bit_width_id}); } static auto TryResolveTypedInst(ImportRefResolver& resolver, SemIR::Namespace inst, SemIR::InstId import_inst_id) -> ResolveResult { const auto& name_scope = resolver.import_name_scopes().Get(inst.name_scope_id); // A package from a different file becomes a child of the package here, as it // would be if it were imported. auto parent_scope_id = inst.name_scope_id == SemIR::NameScopeId::Package ? SemIR::NameScopeId::Package : GetLocalNameScopeId(resolver, name_scope.parent_scope_id()); if (resolver.HasNewWork()) { return ResolveResult::Retry(); } auto namespace_type_id = GetSingletonType(resolver.local_context(), SemIR::NamespaceType::TypeInstId); auto namespace_decl = SemIR::Namespace{.type_id = namespace_type_id, .name_scope_id = SemIR::NameScopeId::None, .import_id = SemIR::AbsoluteInstId::None}; auto inst_id = AddPlaceholderImportedInst(resolver, import_inst_id, namespace_decl); auto name_id = GetLocalNameId(resolver, name_scope.name_id()); namespace_decl.name_scope_id = resolver.local_name_scopes().Add(inst_id, name_id, parent_scope_id); // Namespaces from this package are eagerly imported, so anything we load here // must be a closed import. resolver.local_name_scopes() .Get(namespace_decl.name_scope_id) .set_is_closed_import(true); auto namespace_const_id = ReplacePlaceholderImportedInst(resolver, inst_id, namespace_decl); return {.const_id = namespace_const_id}; } static auto TryResolveTypedInst(ImportRefResolver& resolver, SemIR::PatternType inst) -> ResolveResult { CARBON_CHECK(inst.type_id == SemIR::TypeType::TypeId); auto scrutinee_type_inst_id = GetLocalTypeInstId(resolver, inst.scrutinee_type_inst_id); if (resolver.HasNewWork()) { return ResolveResult::Retry(); } return ResolveResult::Deduplicated( resolver, {.type_id = SemIR::TypeType::TypeId, .scrutinee_type_inst_id = scrutinee_type_inst_id}); } static auto TryResolveTypedInst(ImportRefResolver& resolver, SemIR::PointerType inst) -> ResolveResult { CARBON_CHECK(inst.type_id == SemIR::TypeType::TypeId); auto pointee_id = GetLocalTypeInstId(resolver, inst.pointee_id); if (resolver.HasNewWork()) { return ResolveResult::Retry(); } return ResolveResult::Deduplicated( resolver, {.type_id = SemIR::TypeType::TypeId, .pointee_id = pointee_id}); } static auto TryResolveTypedInst(ImportRefResolver& resolver, SemIR::RefForm inst) -> ResolveResult { auto type_component_inst_id = GetLocalConstantId(resolver, inst.type_component_inst_id); if (resolver.HasNewWork()) { return ResolveResult::Retry(); } return ResolveResult::Deduplicated( resolver, {.type_id = SemIR::TypeType::TypeId, .type_component_inst_id = resolver.local_types().GetTypeInstId( resolver.local_types().GetTypeIdForTypeConstantId( type_component_inst_id))}); } static auto TryResolveTypedInst(ImportRefResolver& resolver, SemIR::RequireCompleteType inst) -> ResolveResult { CARBON_CHECK(resolver.import_types().GetTypeInstId(inst.type_id) == SemIR::WitnessType::TypeInstId); auto complete_type_inst_id = GetLocalTypeInstId(resolver, inst.complete_type_inst_id); if (resolver.HasNewWork()) { return ResolveResult::Retry(); } return ResolveResult::Deduplicated( resolver, {.type_id = GetSingletonType(resolver.local_context(), SemIR::WitnessType::TypeInstId), .complete_type_inst_id = complete_type_inst_id}); } static auto TryResolveTypedInst(ImportRefResolver& resolver, SemIR::RequireSpecificDefinition inst) -> ResolveResult { CARBON_CHECK(resolver.import_types().GetTypeInstId(inst.type_id) == SemIR::RequireSpecificDefinitionType::TypeInstId); auto specific_data = GetLocalSpecificData(resolver, inst.specific_id); if (resolver.HasNewWork()) { return ResolveResult::Retry(); } auto specific_id = GetOrAddLocalSpecific(resolver, inst.specific_id, specific_data); return ResolveResult::Deduplicated( resolver, {.type_id = GetSingletonType( resolver.local_context(), SemIR::RequireSpecificDefinitionType::TypeInstId), .specific_id = specific_id}); } static auto TryResolveTypedInst(ImportRefResolver& resolver, SemIR::ReturnSlotPattern inst, SemIR::InstId import_inst_id) -> ResolveResult { auto type_const_id = GetLocalConstantId(resolver, inst.type_id); if (resolver.HasNewWork()) { return ResolveResult::Retry(); } return ResolveResult::Unique( resolver, import_inst_id, {.type_id = resolver.local_types().GetTypeIdForTypeConstantId(type_const_id), .type_inst_id = SemIR::TypeInstId::None}); } static auto TryResolveTypedInst(ImportRefResolver& resolver, SemIR::SpecificFunction inst) -> ResolveResult { auto type_const_id = GetLocalConstantId(resolver, inst.type_id); auto callee_id = GetLocalConstantInstId(resolver, inst.callee_id); auto specific_data = GetLocalSpecificData(resolver, inst.specific_id); if (resolver.HasNewWork()) { return ResolveResult::Retry(); } auto type_id = resolver.local_types().GetTypeIdForTypeConstantId(type_const_id); auto specific_id = GetOrAddLocalSpecific(resolver, inst.specific_id, specific_data); return ResolveResult::Deduplicated( resolver, {.type_id = type_id, .callee_id = callee_id, .specific_id = specific_id}); } static auto TryResolveTypedInst(ImportRefResolver& resolver, SemIR::SpecificImplFunction inst) -> ResolveResult { auto callee_id = GetLocalConstantInstId(resolver, inst.callee_id); auto specific_data = GetLocalSpecificData(resolver, inst.specific_id); if (resolver.HasNewWork()) { return ResolveResult::Retry(); } auto type_id = GetSingletonType(resolver.local_context(), SemIR::SpecificFunctionType::TypeInstId); auto specific_id = GetOrAddLocalSpecific(resolver, inst.specific_id, specific_data); return ResolveResult::Deduplicated( resolver, {.type_id = type_id, .callee_id = callee_id, .specific_id = specific_id}); } static auto TryResolveTypedInst(ImportRefResolver& resolver, SemIR::StructAccess inst) -> ResolveResult { auto type_id = GetLocalConstantId(resolver, inst.type_id); auto struct_id = GetLocalConstantInstId(resolver, inst.struct_id); if (resolver.HasNewWork()) { return ResolveResult::Retry(); } // A `struct_access` constant requires its struct operand to have a complete // type. CompleteTypeOrCheckFail(resolver.local_context(), resolver.local_insts().Get(struct_id).type_id()); return ResolveResult::Deduplicated( resolver, {.type_id = resolver.local_types().GetTypeIdForTypeConstantId(type_id), .struct_id = struct_id, .index = inst.index}); } static auto TryResolveTypedInst(ImportRefResolver& resolver, SemIR::StructType inst) -> ResolveResult { CARBON_CHECK(inst.type_id == SemIR::TypeType::TypeId); auto orig_fields = resolver.import_struct_type_fields().Get(inst.fields_id); llvm::SmallVector field_type_inst_ids; field_type_inst_ids.reserve(orig_fields.size()); for (auto field : orig_fields) { field_type_inst_ids.push_back( GetLocalTypeInstId(resolver, field.type_inst_id)); } if (resolver.HasNewWork()) { return ResolveResult::Retry(); } // Prepare a vector of fields for GetStructType. llvm::SmallVector new_fields; new_fields.reserve(orig_fields.size()); for (auto [orig_field, field_type_inst_id] : llvm::zip_equal(orig_fields, field_type_inst_ids)) { auto name_id = GetLocalNameId(resolver, orig_field.name_id); new_fields.push_back( {.name_id = name_id, .type_inst_id = field_type_inst_id}); } return ResolveResult::Deduplicated( resolver, {.type_id = SemIR::TypeType::TypeId, .fields_id = resolver.local_struct_type_fields().AddCanonical( new_fields)}); } static auto TryResolveTypedInst(ImportRefResolver& resolver, SemIR::StructValue inst) -> ResolveResult { auto type_id = GetLocalConstantId(resolver, inst.type_id); auto elems = GetLocalInstBlockContents(resolver, inst.elements_id); if (resolver.HasNewWork()) { return ResolveResult::Retry(); } return ResolveResult::Deduplicated( resolver, {.type_id = resolver.local_types().GetTypeIdForTypeConstantId(type_id), .elements_id = GetLocalCanonicalInstBlockId(resolver, inst.elements_id, elems)}); } static auto TryResolveTypedInst(ImportRefResolver& resolver, SemIR::SymbolicBindingType inst) -> ResolveResult { auto facet_value_inst_id = GetLocalConstantInstId(resolver, inst.facet_value_inst_id); if (resolver.HasNewWork()) { return ResolveResult::Retry(); } auto entity_name_id = GetLocalSymbolicEntityNameId(resolver, inst.entity_name_id); return ResolveResult::Deduplicated( resolver, {.type_id = SemIR::TypeType::TypeId, .entity_name_id = entity_name_id, .facet_value_inst_id = facet_value_inst_id}); } static auto TryResolveTypedInst(ImportRefResolver& resolver, SemIR::TupleAccess inst) -> ResolveResult { auto type_id = GetLocalConstantId(resolver, inst.type_id); auto tuple_id = GetLocalConstantInstId(resolver, inst.tuple_id); if (resolver.HasNewWork()) { return ResolveResult::Retry(); } // A `tuple_access` constant requires its struct operand to have a complete // type. CompleteTypeOrCheckFail(resolver.local_context(), resolver.local_insts().Get(tuple_id).type_id()); return ResolveResult::Deduplicated( resolver, {.type_id = resolver.local_types().GetTypeIdForTypeConstantId(type_id), .tuple_id = tuple_id, .index = inst.index}); } static auto TryResolveTypedInst(ImportRefResolver& resolver, SemIR::TuplePattern inst, SemIR::InstId import_inst_id) -> ResolveResult { auto type_const_id = GetLocalConstantId(resolver, inst.type_id); auto elements = GetLocalInstBlockContents(resolver, inst.elements_id); if (resolver.HasNewWork()) { return ResolveResult::Retry(); } return ResolveResult::Unique( resolver, import_inst_id, {.type_id = resolver.local_types().GetTypeIdForTypeConstantId(type_const_id), .elements_id = GetLocalCanonicalInstBlockId(resolver, inst.elements_id, elements)}); } static auto TryResolveTypedInst(ImportRefResolver& resolver, SemIR::TupleType inst) -> ResolveResult { CARBON_CHECK(inst.type_id == SemIR::TypeType::TypeId); auto orig_type_inst_ids = resolver.import_inst_blocks().Get(inst.type_elements_id); // TODO: It might be nice to make the `InstBlock` in `TupleType` record in the // type system that its holding `TypeInstId` elements. llvm::SmallVector type_inst_ids; type_inst_ids.reserve(orig_type_inst_ids.size()); for (auto elem_type_inst_id : resolver.import_ir().types().GetBlockAsTypeInstIds(orig_type_inst_ids)) { type_inst_ids.push_back(GetLocalTypeInstId(resolver, elem_type_inst_id)); } if (resolver.HasNewWork()) { return ResolveResult::Retry(); } return ResolveResult::Deduplicated( resolver, {.type_id = SemIR::TypeType::TypeId, .type_elements_id = GetLocalCanonicalInstBlockId( resolver, inst.type_elements_id, type_inst_ids)}); } static auto TryResolveTypedInst(ImportRefResolver& resolver, SemIR::TupleValue inst) -> ResolveResult { auto type_id = GetLocalConstantId(resolver, inst.type_id); auto elems = GetLocalInstBlockContents(resolver, inst.elements_id); if (resolver.HasNewWork()) { return ResolveResult::Retry(); } return ResolveResult::Deduplicated( resolver, {.type_id = resolver.local_types().GetTypeIdForTypeConstantId(type_id), .elements_id = GetLocalCanonicalInstBlockId(resolver, inst.elements_id, elems)}); } static auto TryResolveTypedInst(ImportRefResolver& resolver, SemIR::UnboundElementType inst) -> ResolveResult { CARBON_CHECK(inst.type_id == SemIR::TypeType::TypeId); auto class_const_inst_id = GetLocalTypeInstId(resolver, inst.class_type_inst_id); auto elem_const_inst_id = GetLocalTypeInstId(resolver, inst.element_type_inst_id); if (resolver.HasNewWork()) { return ResolveResult::Retry(); } return ResolveResult::Deduplicated( resolver, {.type_id = SemIR::TypeType::TypeId, .class_type_inst_id = class_const_inst_id, .element_type_inst_id = elem_const_inst_id}); } static auto TryResolveTypedInst(ImportRefResolver& resolver, SemIR::VarPattern inst, SemIR::InstId import_inst_id) -> ResolveResult { auto type_const_id = GetLocalConstantId(resolver, inst.type_id); auto subpattern_id = GetLocalConstantInstId(resolver, inst.subpattern_id); if (resolver.HasNewWork()) { return ResolveResult::Retry(); } return ResolveResult::Unique( resolver, import_inst_id, {.type_id = resolver.local_types().GetTypeIdForTypeConstantId(type_const_id), .subpattern_id = subpattern_id}); } static auto TryResolveTypedInst(ImportRefResolver& resolver, SemIR::VarStorage inst, SemIR::InstId import_inst_id) -> ResolveResult { auto type_const_id = GetLocalConstantId(resolver, inst.type_id); auto pattern_id = GetLocalConstantInstId(resolver, inst.pattern_id); if (resolver.HasNewWork()) { return ResolveResult::Retry(); } return ResolveResult::Unique( resolver, import_inst_id, {.type_id = resolver.local_types().GetTypeIdForTypeConstantId(type_const_id), .pattern_id = pattern_id}); } // Tries to resolve the InstId, returning a canonical constant when ready, or // `None` if more has been added to the stack. This is the same as // TryResolveInst, except that it may resolve symbolic constants as canonical // constants instead of as constants associated with a particular generic. static auto TryResolveInstCanonical(ImportRefResolver& resolver, SemIR::InstId inst_id, SemIR::ConstantId const_id) -> ResolveResult { // These instruction types are imported across multiple phases to arrive at // their constant value. We can't just import their constant value instruction // directly. auto untyped_inst = resolver.import_insts().GetWithAttachedType(inst_id); CARBON_KIND_SWITCH(untyped_inst) { case CARBON_KIND(SemIR::AssociatedConstantDecl inst): { return TryResolveTypedInst(resolver, inst, const_id); } case CARBON_KIND(SemIR::ClassDecl inst): { return TryResolveTypedInst(resolver, inst, const_id); } case CARBON_KIND(SemIR::FunctionDecl inst): { return TryResolveTypedInst(resolver, inst, const_id); } case CARBON_KIND(SemIR::ImplDecl inst): { return TryResolveTypedInst(resolver, inst, const_id); } case CARBON_KIND(SemIR::InterfaceDecl inst): { return TryResolveTypedInst(resolver, inst, const_id); } case CARBON_KIND(SemIR::InterfaceWithSelfDecl inst): { return TryResolveTypedInst(resolver, inst, const_id); } case CARBON_KIND(SemIR::NamedConstraintDecl inst): { return TryResolveTypedInst(resolver, inst, const_id); } case CARBON_KIND(SemIR::NamedConstraintWithSelfDecl inst): { return TryResolveTypedInst(resolver, inst, const_id); } case CARBON_KIND(SemIR::RequireImplsDecl inst): { return TryResolveTypedInst(resolver, inst, const_id); } default: break; } // Other instructions are imported in a single phase (once their dependencies // are all imported). CARBON_CHECK(!const_id.has_value()); auto inst_constant_id = resolver.import_constant_values().Get(inst_id); if (!inst_constant_id.is_constant()) { // TODO: Import of non-constant BindNames happens when importing `let` // declarations. CARBON_CHECK(resolver.import_insts().Is(inst_id), "TryResolveInst on non-constant instruction {0}", inst_id); return ResolveResult::Done(SemIR::ConstantId::NotConstant); } // Import the canonical constant value instruction for `inst_id` directly. We // don't try to import the non-canonical `inst_id`. auto constant_inst_id = resolver.import_constant_values().GetInstId(inst_constant_id); CARBON_DCHECK(resolver.import_constant_values().GetConstantInstId( constant_inst_id) == constant_inst_id, "Constant value of constant instruction should refer to " "the same instruction"); if (SemIR::IsSingletonInstId(constant_inst_id)) { // Constants for builtins can be directly copied. return ResolveResult::Done( resolver.local_constant_values().Get(constant_inst_id)); } auto untyped_constant_inst = resolver.import_insts().GetWithAttachedType(constant_inst_id); CARBON_KIND_SWITCH(untyped_constant_inst) { case CARBON_KIND(SemIR::AdaptDecl inst): { return TryResolveTypedInst(resolver, inst, constant_inst_id); } case CARBON_KIND(SemIR::ArrayType inst): { return TryResolveTypedInst(resolver, inst); } case CARBON_KIND(SemIR::AssociatedEntity inst): { return TryResolveTypedInst(resolver, inst); } case CARBON_KIND(SemIR::AssociatedEntityType inst): { return TryResolveTypedInst(resolver, inst); } case CARBON_KIND(SemIR::BaseDecl inst): { return TryResolveTypedInst(resolver, inst, constant_inst_id); } case CARBON_KIND(SemIR::AliasBinding inst): { return TryResolveTypedInst(resolver, inst); } case CARBON_KIND(SemIR::SymbolicBinding inst): { return TryResolveTypedInst(resolver, inst); } case CARBON_KIND(SemIR::BoolLiteral inst): { return TryResolveTypedInst(resolver, inst); } case CARBON_KIND(SemIR::BoundMethod inst): { return TryResolveTypedInst(resolver, inst); } case CARBON_KIND(SemIR::Call inst): { return TryResolveTypedInst(resolver, inst); } case CARBON_KIND(SemIR::CharLiteralValue inst): { return TryResolveTypedInst(resolver, inst); } case CARBON_KIND(SemIR::ClassType inst): { return TryResolveTypedInst(resolver, inst); } case CARBON_KIND(SemIR::CompleteTypeWitness inst): { return TryResolveTypedInst(resolver, inst); } case CARBON_KIND(SemIR::ConstType inst): { return TryResolveTypedInst(resolver, inst); } case CARBON_KIND(SemIR::CppOverloadSetType inst): { return TryResolveTypedInst(resolver, inst); } case CARBON_KIND(SemIR::CppOverloadSetValue inst): { return TryResolveTypedInst(resolver, inst); } case CARBON_KIND(SemIR::CustomWitness inst): { return TryResolveTypedInst(resolver, inst); } case CARBON_KIND(SemIR::ExportDecl inst): { return TryResolveTypedInst(resolver, inst); } case CARBON_KIND(SemIR::FacetAccessType inst): { return TryResolveTypedInst(resolver, inst); } case CARBON_KIND(SemIR::FacetType inst): { return TryResolveTypedInst(resolver, inst); } case CARBON_KIND(SemIR::FacetValue inst): { return TryResolveTypedInst(resolver, inst); } case CARBON_KIND(SemIR::FieldDecl inst): { return TryResolveTypedInst(resolver, inst, constant_inst_id); } case CARBON_KIND(SemIR::FloatLiteralValue inst): { return TryResolveTypedInst(resolver, inst); } case CARBON_KIND(SemIR::FloatType inst): { return TryResolveTypedInst(resolver, inst); } case CARBON_KIND(SemIR::FloatValue inst): { return TryResolveTypedInst(resolver, inst); } case CARBON_KIND(SemIR::FunctionType inst): { return TryResolveTypedInst(resolver, inst); } case CARBON_KIND(SemIR::FunctionTypeWithSelfType inst): { return TryResolveTypedInst(resolver, inst); } case CARBON_KIND(SemIR::GenericClassType inst): { return TryResolveTypedInst(resolver, inst); } case CARBON_KIND(SemIR::GenericInterfaceType inst): { return TryResolveTypedInst(resolver, inst); } case CARBON_KIND(SemIR::GenericNamedConstraintType inst): { return TryResolveTypedInst(resolver, inst); } case CARBON_KIND(SemIR::LookupImplWitness inst): { return TryResolveTypedInst(resolver, inst); } case CARBON_KIND(SemIR::ImplWitness inst): { return TryResolveTypedInst(resolver, inst); } case CARBON_KIND(SemIR::ImplWitnessAccess inst): { return TryResolveTypedInst(resolver, inst); } case CARBON_KIND(SemIR::ImplWitnessTable inst): { return TryResolveTypedInst(resolver, inst, constant_inst_id); } case CARBON_KIND(SemIR::ImportRefLoaded inst): { return TryResolveTypedInst(resolver, inst, constant_inst_id); } case CARBON_KIND(SemIR::InitForm inst): { return TryResolveTypedInst(resolver, inst); } case CARBON_KIND(SemIR::IntValue inst): { return TryResolveTypedInst(resolver, inst); } case CARBON_KIND(SemIR::IntType inst): { return TryResolveTypedInst(resolver, inst); } case CARBON_KIND(SemIR::MaybeUnformedType inst): { return TryResolveTypedInst(resolver, inst); } case CARBON_KIND(SemIR::Namespace inst): { return TryResolveTypedInst(resolver, inst, constant_inst_id); } case CARBON_KIND(SemIR::OutParamPattern inst): { return TryResolveTypedInst(resolver, inst, constant_inst_id); } case CARBON_KIND(SemIR::PartialType inst): { return TryResolveTypedInst(resolver, inst); } case CARBON_KIND(SemIR::PatternType inst): { return TryResolveTypedInst(resolver, inst); } case CARBON_KIND(SemIR::PointerType inst): { return TryResolveTypedInst(resolver, inst); } case CARBON_KIND(SemIR::RefBindingPattern inst): { return TryResolveTypedInst(resolver, inst, constant_inst_id); } case CARBON_KIND(SemIR::RefForm inst): { return TryResolveTypedInst(resolver, inst); } case CARBON_KIND(SemIR::RefParamPattern inst): { return TryResolveTypedInst(resolver, inst, constant_inst_id); } case CARBON_KIND(SemIR::RequireCompleteType inst): { return TryResolveTypedInst(resolver, inst); } case CARBON_KIND(SemIR::RequireSpecificDefinition inst): { return TryResolveTypedInst(resolver, inst); } case CARBON_KIND(SemIR::ReturnSlotPattern inst): { return TryResolveTypedInst(resolver, inst, constant_inst_id); } case CARBON_KIND(SemIR::SpecificFunction inst): { return TryResolveTypedInst(resolver, inst); } case CARBON_KIND(SemIR::SpecificImplFunction inst): { return TryResolveTypedInst(resolver, inst); } case CARBON_KIND(SemIR::StructAccess inst): { return TryResolveTypedInst(resolver, inst); } case CARBON_KIND(SemIR::StructType inst): { return TryResolveTypedInst(resolver, inst); } case CARBON_KIND(SemIR::StructValue inst): { return TryResolveTypedInst(resolver, inst); } case CARBON_KIND(SemIR::SymbolicBindingPattern inst): { return TryResolveTypedInst(resolver, inst, constant_inst_id); } case CARBON_KIND(SemIR::SymbolicBindingType inst): { return TryResolveTypedInst(resolver, inst); } case CARBON_KIND(SemIR::TupleAccess inst): { return TryResolveTypedInst(resolver, inst); } case CARBON_KIND(SemIR::TuplePattern inst): { return TryResolveTypedInst(resolver, inst, constant_inst_id); } case CARBON_KIND(SemIR::TupleType inst): { return TryResolveTypedInst(resolver, inst); } case CARBON_KIND(SemIR::TupleValue inst): { return TryResolveTypedInst(resolver, inst); } case CARBON_KIND(SemIR::UnboundElementType inst): { return TryResolveTypedInst(resolver, inst); } case CARBON_KIND(SemIR::ValueBindingPattern inst): { return TryResolveTypedInst(resolver, inst, constant_inst_id); } case CARBON_KIND(SemIR::ValueParamPattern inst): { return TryResolveTypedInst(resolver, inst, constant_inst_id); } case CARBON_KIND(SemIR::VarParamPattern inst): { return TryResolveTypedInst(resolver, inst, constant_inst_id); } case CARBON_KIND(SemIR::VarPattern inst): { return TryResolveTypedInst(resolver, inst, constant_inst_id); } case CARBON_KIND(SemIR::VarStorage inst): { return TryResolveTypedInst(resolver, inst, constant_inst_id); } case CARBON_KIND(SemIR::VtableDecl inst): { return TryResolveTypedInst(resolver, inst); } case CARBON_KIND(SemIR::VtablePtr inst): { return TryResolveTypedInst(resolver, inst); } default: // Found a canonical instruction which needs to be resolved, but which is // not yet handled. // // TODO: Could we turn this into a compile-time error? CARBON_FATAL( "Missing case in TryResolveInstCanonical for instruction kind {0}", untyped_constant_inst.kind()); } } // Tries to resolve the InstId, returning a constant when ready, or `None` if // more has been added to the stack. A similar API is followed for all // following TryResolveTypedInst helper functions. // // `const_id` is `None` unless we've tried to resolve this instruction // before, in which case it's the previous result. // // TODO: Error is returned when support is missing, but that should go away. static auto TryResolveInst(ImportRefResolver& resolver, SemIR::InstId inst_id, SemIR::ConstantId const_id) -> ResolveResult { auto inst_const_id = resolver.import_constant_values().GetAttached(inst_id); if (!inst_const_id.has_value() || !inst_const_id.is_symbolic()) { return TryResolveInstCanonical(resolver, inst_id, const_id); } // Try to import the generic. This might add new work. const auto& symbolic_const = resolver.import_constant_values().GetSymbolicConstant(inst_const_id); auto generic_const_id = GetLocalConstantId(resolver, symbolic_const.generic_id); auto inner_const_id = SemIR::ConstantId::None; if (const_id.has_value()) { // For the third phase, extract the constant value that // TryResolveInstCanonical produced previously. inner_const_id = resolver.local_constant_values().GetAttached( resolver.local_constant_values().GetSymbolicConstant(const_id).inst_id); } // Import the constant and rebuild the symbolic constant data. auto result = TryResolveInstCanonical(resolver, inst_id, inner_const_id); if (!result.const_id.has_value()) { // First phase: TryResolveInstCanoncial needs a retry. return result; } if (!const_id.has_value()) { // Second phase: we have created an unattached constant. Create a // corresponding attached constant. if (symbolic_const.generic_id.has_value()) { result.const_id = resolver.local_constant_values().AddSymbolicConstant( {.inst_id = resolver.local_constant_values().GetInstId(result.const_id), .generic_id = GetLocalGenericId(resolver, generic_const_id), .index = symbolic_const.index, .dependence = symbolic_const.dependence}); if (result.decl_id.has_value()) { // Overwrite the unattached symbolic constant given initially to the // declaration with its final attached symbolic value. resolver.local_constant_values().Set(result.decl_id, result.const_id); } } } else { // Third phase: perform a consistency check and produce the constant we // created in the second phase. CARBON_CHECK(result.const_id == inner_const_id, "Constant value changed in third phase."); result.const_id = const_id; } return result; } auto ImportRefResolver::Resolve(SemIR::InstId inst_id) -> SemIR::ConstantId { work_stack_.push_back(InstWork{.inst_id = inst_id}); while (!work_stack_.empty()) { auto work_variant = work_stack_.back(); CARBON_KIND_SWITCH(work_variant) { case CARBON_KIND(InstWork work): { CARBON_CHECK(work.inst_id.has_value()); // Step 1: check for a constant value. auto existing = FindResolvedConstId(work.inst_id); if (existing.const_id.has_value() && !work.retry_with_constant_value) { work_stack_.pop_back(); continue; } // Step 2: resolve the instruction. initial_work_ = work_stack_.size(); auto result = TryResolveInst(*this, work.inst_id, existing.const_id); CARBON_CHECK(!HasNewWork() || result.retry); CARBON_CHECK(!existing.const_id.has_value() || existing.const_id == result.const_id, "Constant value changed in third phase."); if (!existing.const_id.has_value()) { SetResolvedConstId(work.inst_id, existing.indirect_insts, result.const_id); } // Step 3: pop or retry. if (result.retry) { std::get(work_stack_[initial_work_ - 1]) .retry_with_constant_value = result.const_id.has_value(); } else { work_stack_.pop_back(); for (const auto& resolve : result.resolve_generic) { if (resolve.import_generic_id.has_value()) { work_stack_.push_back( GenericWork{.import_id = resolve.import_generic_id, .local_id = resolve.local_generic_id}); } } } break; } case CARBON_KIND(GenericWork generic_work): { // Generics may require 2 steps to finish, similar to step 2 and step 3 // of instructions. initial_work_ = work_stack_.size(); if (TryFinishGeneric(*this, generic_work.import_id, generic_work.local_id)) { work_stack_.pop_back(); } break; } case CARBON_KIND(SpecificWork specific_work): { // Specifics may require 2 steps to finish, similar to step 2 and step 3 // of instructions. initial_work_ = work_stack_.size(); if (TryFinishSpecific(*this, specific_work.import_id, specific_work.local_id)) { work_stack_.pop_back(); } break; } } } auto constant_id = local_constant_values_for_import_insts().GetAttached(inst_id); CARBON_CHECK(constant_id.has_value()); return constant_id; } auto ImportRefResolver::ResolveConstant(SemIR::ConstantId import_const_id) -> SemIR::ConstantId { return Resolve(GetInstWithConstantValue(import_ir(), import_const_id)); } auto ImportRefResolver::ResolveType(SemIR::TypeId import_type_id) -> SemIR::TypeId { if (!import_type_id.has_value()) { return import_type_id; } auto import_type_const_id = import_ir().types().GetConstantId(import_type_id); CARBON_CHECK(import_type_const_id.has_value()); if (auto import_type_inst_id = import_ir().types().GetAsTypeInstId( import_ir().constant_values().GetInstId(import_type_const_id)); SemIR::IsSingletonInstId(import_type_inst_id)) { // Builtins don't require constant resolution; we can use them directly. return GetSingletonType(local_context(), import_type_inst_id); } else { return local_types().GetTypeIdForTypeConstantId( ResolveConstant(import_type_id.AsConstantId())); } } auto ImportRefResolver::HasNewWork() -> bool { CARBON_CHECK(initial_work_ <= work_stack_.size(), "Work shouldn't decrease"); return initial_work_ < work_stack_.size(); } auto ImportRefResolver::PushSpecific(SemIR::SpecificId import_id, SemIR::SpecificId local_id) -> void { // Insert before the current instruction. work_stack_.insert( work_stack_.begin() + initial_work_ - 1, SpecificWork{.import_id = import_id, .local_id = local_id}); ++initial_work_; } auto ImportRefResolver::GetLocalConstantValueOrPush(SemIR::InstId inst_id) -> SemIR::ConstantId { if (!inst_id.has_value()) { return SemIR::ConstantId::None; } auto const_id = local_constant_values_for_import_insts().GetAttached(inst_id); if (!const_id.has_value()) { work_stack_.push_back(InstWork{.inst_id = inst_id}); } return const_id; } auto ImportRefResolver::FindResolvedConstId(SemIR::InstId inst_id) -> ResolvedConstId { ResolvedConstId result; if (auto existing_const_id = local_constant_values_for_import_insts().GetAttached(inst_id); existing_const_id.has_value()) { result.const_id = existing_const_id; return result; } const auto* cursor_ir = &import_ir(); auto cursor_inst_id = inst_id; while (true) { auto import_ir_inst_id = cursor_ir->insts().GetImportSource(cursor_inst_id); if (!import_ir_inst_id.has_value()) { return result; } auto ir_inst = cursor_ir->import_ir_insts().Get(import_ir_inst_id); if (ir_inst.ir_id() == SemIR::ImportIRId::Cpp) { local_context().TODO(SemIR::LocId::None, "Unsupported: Importing C++ indirectly"); SetResolvedConstId(inst_id, result.indirect_insts, SemIR::ErrorInst::ConstantId); result.const_id = SemIR::ErrorInst::ConstantId; result.indirect_insts.clear(); return result; } const auto* prev_ir = cursor_ir; auto prev_inst_id = cursor_inst_id; cursor_ir = cursor_ir->import_irs().Get(ir_inst.ir_id()).sem_ir; auto cursor_ir_id = AddImportIR(local_context(), {.decl_id = SemIR::InstId::None, .is_export = false, .sem_ir = cursor_ir}); cursor_inst_id = ir_inst.inst_id(); CARBON_CHECK(cursor_ir != prev_ir || cursor_inst_id != prev_inst_id, "{0}", cursor_ir->insts().Get(cursor_inst_id)); if (auto const_id = local_context() .import_ir_constant_values() [local_ir().import_irs().GetRawIndex(cursor_ir_id)] .GetAttached(cursor_inst_id); const_id.has_value()) { SetResolvedConstId(inst_id, result.indirect_insts, const_id); result.const_id = const_id; result.indirect_insts.clear(); return result; } else { result.indirect_insts.push_back( SemIR::ImportIRInst(cursor_ir_id, cursor_inst_id)); } } } auto ImportRefResolver::SetResolvedConstId( SemIR::InstId inst_id, llvm::ArrayRef indirect_insts, SemIR::ConstantId const_id) -> void { local_constant_values_for_import_insts().Set(inst_id, const_id); SetIndirectConstantValues(local_context(), indirect_insts, const_id); } // Returns a list of ImportIRInsts equivalent to the ImportRef currently being // loaded (including the one pointed at directly by the ImportRef), and the // final instruction's type ID. // // This addresses cases where an ImportRefUnloaded may point at another // ImportRefUnloaded. The ImportRefResolver requires a SemIR with a // constant-evaluated version of the instruction to work with. static auto GetInstForLoad(Context& context, SemIR::ImportIRInstId import_ir_inst_id) -> std::pair, SemIR::TypeId> { std::pair, SemIR::TypeId> result = { {}, SemIR::TypeId::None}; auto& [import_ir_insts, type_id] = result; auto import_ir_inst = context.import_ir_insts().Get(import_ir_inst_id); // The first ImportIRInst is added directly because the IR doesn't need to be // localized. import_ir_insts.push_back(import_ir_inst); const auto* cursor_ir = context.import_irs().Get(import_ir_inst.ir_id()).sem_ir; while (true) { auto cursor_inst = cursor_ir->insts().GetWithAttachedType(import_ir_inst.inst_id()); auto import_ref = cursor_inst.TryAs(); if (!import_ref) { type_id = cursor_inst.type_id(); return result; } import_ir_inst = cursor_ir->import_ir_insts().Get(import_ref->import_ir_inst_id); cursor_ir = cursor_ir->import_irs().Get(import_ir_inst.ir_id()).sem_ir; import_ir_insts.push_back(SemIR::ImportIRInst( AddImportIR(context, {.decl_id = SemIR::InstId::None, .is_export = false, .sem_ir = cursor_ir}), import_ir_inst.inst_id())); } } auto LoadImportRef(Context& context, SemIR::InstId inst_id) -> void { auto inst = context.insts().TryGetAs(inst_id); if (!inst) { return; } auto [indirect_insts, load_type_id] = GetInstForLoad(context, inst->import_ir_inst_id); // The last indirect instruction is the one to resolve. Pop it here because // Resolve will assign the constant. auto load_ir_inst = indirect_insts.pop_back_val(); ImportRefResolver resolver(&context, load_ir_inst.ir_id()); // Loading an import ref creates local constants from the import ones, but // shouldn't be generating novel instructions in the semir as a side effect of // that process. Doing so in a generic context would also cause them to end up // in the eval block, which would be doubly wrong. context.inst_block_stack().Push(); auto type_id = resolver.ResolveType(load_type_id); auto constant_id = resolver.Resolve(load_ir_inst.inst_id()); CARBON_CHECK( context.inst_block_stack().PeekCurrentBlockContents().empty(), "Importing an instruction shouldn't add new instructions to the " "local inst block. Found {0} new instructions, first is {1}: {2}.", context.inst_block_stack().PeekCurrentBlockContents().size(), context.inst_block_stack().PeekCurrentBlockContents().front(), context.insts().Get( context.inst_block_stack().PeekCurrentBlockContents().front())); context.inst_block_stack().PopAndDiscard(); // Replace the ImportRefUnloaded instruction with ImportRefLoaded. This // doesn't use ReplacePlaceholderImportedInst because it would trigger // TryEvalInst, which we want to avoid with ImportRefs. context.sem_ir().insts().Set( inst_id, SemIR::ImportRefLoaded{.type_id = type_id, .import_ir_inst_id = inst->import_ir_inst_id, .entity_name_id = inst->entity_name_id}); // Store the constant for both the ImportRefLoaded and indirect instructions. context.constant_values().Set(inst_id, constant_id); SetIndirectConstantValues(context, indirect_insts, constant_id); } auto ImportImplsFromApiFile(Context& context) -> void { SemIR::ImportIRId import_ir_id = SemIR::ImportIRId::ApiForImpl; auto& import_ir = context.import_irs().Get(import_ir_id); if (!import_ir.sem_ir) { return; } for (auto [impl_id, _] : import_ir.sem_ir->impls().enumerate()) { // Resolve the imported impl to a local impl ID. ImportImpl(context, import_ir_id, impl_id); } } auto ImportImpl(Context& context, SemIR::ImportIRId import_ir_id, SemIR::ImplId impl_id) -> void { ImportRefResolver resolver(&context, import_ir_id); resolver.Resolve(resolver.import_impls().Get(impl_id).first_decl_id()); } auto ImportInterface(Context& context, SemIR::ImportIRId import_ir_id, SemIR::InterfaceId interface_id) -> SemIR::InterfaceId { ImportRefResolver resolver(&context, import_ir_id); auto local_id = resolver.Resolve( resolver.import_interfaces().Get(interface_id).first_decl_id()); auto local_inst = context.insts().Get(context.constant_values().GetInstId(local_id)); // A non-generic interface will import as a facet type for that single // interface. if (auto facet_type = local_inst.TryAs()) { auto single = context.facet_types() .Get(facet_type->facet_type_id) .TryAsSingleExtend(); CARBON_CHECK(single, "Importing an interface didn't produce a single interface"); return std::get(*single).interface_id; } // A generic interface will import as a constant of generic interface type. auto generic_interface_type = context.types().GetAs(local_inst.type_id()); return generic_interface_type.interface_id; } } // namespace Carbon::Check ================================================ FILE: toolchain/check/import_ref.h ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #ifndef CARBON_TOOLCHAIN_CHECK_IMPORT_REF_H_ #define CARBON_TOOLCHAIN_CHECK_IMPORT_REF_H_ #include "toolchain/check/context.h" #include "toolchain/sem_ir/file.h" #include "toolchain/sem_ir/ids.h" namespace Carbon::Check { // Sets the IRs for `ImportIRId::ApiForImpl` from `import_ir`, and // `ImportIRId::Cpp` from defaults. Should be called before `AddImportIR` in // order to ensure the correct IDs are assigned. auto SetSpecialImportIRs(Context& context, SemIR::ImportIR import_ir) -> void; // Adds an ImportIR, returning the ID. May use an existing ID if already added. auto AddImportIR(Context& context, SemIR::ImportIR import_ir) -> SemIR::ImportIRId; // Adds an import_ref instruction for the specified instruction in the // specified IR. The import_ref is initially marked as unused. auto AddImportRef(Context& context, SemIR::ImportIRInst import_ir_inst, SemIR::EntityNameId entity_name_id) -> SemIR::InstId; // Returns the canonical IR inst for an entity. Returns an `ImportIRInst` with // a `None` ir_id for an entity that was not imported. This may add an // `ImportIR` if it finds an indirectly referenced `File` that hasn't previously // been found. auto GetCanonicalImportIRInst(Context& context, SemIR::InstId inst_id) -> SemIR::ImportIRInst; // Verifies a new instruction is the same as a previous instruction. // prev_import_ir_inst should come from GetCanonicalImportIRInst. auto VerifySameCanonicalImportIRInst(Context& context, SemIR::NameId name_id, SemIR::InstId prev_id, SemIR::ImportIRInst prev_import_ir_inst, SemIR::ImportIRId new_ir_id, const SemIR::File* new_import_ir, SemIR::InstId new_inst_id) -> void; // If the passed in instruction ID is an ImportRefUnloaded, turns it into an // ImportRefLoaded for use. auto LoadImportRef(Context& context, SemIR::InstId inst_id) -> void; // Load all impls declared in the api file corresponding to this impl file. auto ImportImplsFromApiFile(Context& context) -> void; // Load an impl that is declared in an imported IR. auto ImportImpl(Context& context, SemIR::ImportIRId import_ir_id, SemIR::ImplId impl_id) -> void; // Load an interface that is declared in an imported IR. auto ImportInterface(Context& context, SemIR::ImportIRId import_ir_id, SemIR::InterfaceId interface_id) -> SemIR::InterfaceId; } // namespace Carbon::Check #endif // CARBON_TOOLCHAIN_CHECK_IMPORT_REF_H_ ================================================ FILE: toolchain/check/inst.cpp ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #include "toolchain/check/inst.h" #include "common/vlog.h" #include "toolchain/check/context.h" #include "toolchain/check/eval.h" #include "toolchain/check/generic.h" #include "toolchain/sem_ir/constant.h" #include "toolchain/sem_ir/ids.h" #include "toolchain/sem_ir/inst_kind.h" namespace Carbon::Check { // Finish producing an instruction. Set its constant value, and register it in // any applicable instruction lists. static auto FinishInst(Context& context, SemIR::InstId inst_id, SemIR::Inst inst) -> void { DependentInstKind dep_kind = DependentInstKind::None; // If the instruction has a symbolic constant type, track that we need to // substitute into it. if (context.constant_values().DependsOnGenericParameter( context.types().GetConstantId(inst.type_id()))) { dep_kind.Add(DependentInstKind::SymbolicType); } // If the instruction has a constant value, compute it. auto const_id = TryEvalInstUnsafe(context, inst_id, inst); context.constant_values().Set(inst_id, const_id); if (const_id.is_constant()) { CARBON_VLOG_TO(context.vlog_stream(), "Constant: {0} -> {1}\n", inst, context.constant_values().GetInstId(const_id)); // If the constant value is symbolic, track that we need to substitute into // it. if (context.constant_values().DependsOnGenericParameter(const_id)) { dep_kind.Add(DependentInstKind::SymbolicConstant); } } // Template-dependent instructions are handled separately by // `AddDependentActionInst`. CARBON_CHECK( inst.kind().constant_kind() != SemIR::InstConstantKind::InstAction, "Use AddDependentActionInst to add an action instruction"); // Keep track of dependent instructions. if (!dep_kind.empty()) { AttachDependentInstToCurrentGeneric(context, {.inst_id = inst_id, .kind = dep_kind}); } } auto AddInst(Context& context, SemIR::LocIdAndInst loc_id_and_inst) -> SemIR::InstId { auto inst_id = AddInstInNoBlock(context, loc_id_and_inst); context.inst_block_stack().AddInstId(inst_id); return inst_id; } auto AddInstInNoBlock(Context& context, SemIR::LocIdAndInst loc_id_and_inst) -> SemIR::InstId { auto inst_id = context.sem_ir().insts().AddInNoBlock(loc_id_and_inst); CARBON_VLOG_TO(context.vlog_stream(), "AddInst: {0}\n", loc_id_and_inst.inst); FinishInst(context, inst_id, loc_id_and_inst.inst); return inst_id; } auto AddDependentActionInst(Context& context, SemIR::LocIdAndInst loc_id_and_inst) -> SemIR::InstId { auto inst_id = context.sem_ir().insts().AddInNoBlock(loc_id_and_inst); CARBON_VLOG_TO(context.vlog_stream(), "AddDependentActionInst: {0}\n", loc_id_and_inst.inst); // Set the constant value of this instruction to point back to itself. auto const_id = context.constant_values().AddSymbolicConstant( {.inst_id = inst_id, .generic_id = SemIR::GenericId::None, .index = SemIR::GenericInstIndex::None, .dependence = SemIR::ConstantDependence::Template}); context.constant_values().Set(inst_id, const_id); // Register the instruction to be added to the eval block. AttachDependentInstToCurrentGeneric( context, {.inst_id = inst_id, .kind = DependentInstKind::Template}); return inst_id; } auto AddPatternInst(Context& context, SemIR::LocIdAndInst loc_id_and_inst) -> SemIR::InstId { auto type_id = loc_id_and_inst.inst.type_id(); CARBON_CHECK(type_id == SemIR::ErrorInst::TypeId || context.types().Is(type_id)); auto inst_id = AddInstInNoBlock(context, loc_id_and_inst); context.pattern_block_stack().AddInstId(inst_id); return inst_id; } auto GetOrAddInst(Context& context, SemIR::LocIdAndInst loc_id_and_inst) -> SemIR::InstId { CARBON_CHECK(!loc_id_and_inst.inst.kind().has_cleanup()); auto handle_constant_id = [&](SemIR::ConstantId const_id) -> SemIR::InstId { CARBON_CHECK(const_id.has_value()); // If we didn't produce a constant value for the instruction, we have to add // the instruction. if (!const_id.is_constant()) { return SemIR::InstId::None; } if (const_id.is_symbolic()) { // TODO: Only add this instruction to the eval block, and don't // re-evaluate it. return AddInst(context, loc_id_and_inst); } CARBON_VLOG_TO(context.vlog_stream(), "GetOrAddInst: constant: {0}\n", loc_id_and_inst.inst); return context.constant_values().GetInstId(const_id); }; // If the instruction is from desugaring, produce its constant value instead // if possible. if (loc_id_and_inst.loc_id.is_desugared()) { switch (loc_id_and_inst.inst.kind().constant_needs_inst_id()) { case SemIR::InstConstantNeedsInstIdKind::No: { // Evaluation doesn't need an InstId. Just do it. auto const_id = TryEvalInstUnsafe(context, SemIR::InstId::None, loc_id_and_inst.inst); if (auto result_inst_id = handle_constant_id(const_id); result_inst_id.has_value()) { return result_inst_id; } break; } case SemIR::InstConstantNeedsInstIdKind::DuringEvaluation: { // Evaluation temporarily needs an InstId. Add one for now. auto inst_id = AddInstInNoBlock(context, loc_id_and_inst); auto const_id = context.constant_values().Get(inst_id); if (auto result_inst_id = handle_constant_id(const_id); result_inst_id.has_value()) { // TODO: We didn't end up needing the `inst_id` instruction. Consider // removing it from `insts` if it's still the most recently added // instruction. CARBON_CHECK(result_inst_id != inst_id); return result_inst_id; } context.inst_block_stack().AddInstId(inst_id); return inst_id; } case SemIR::InstConstantNeedsInstIdKind::Permanent: { // Evaluation needs a permanent InstId. Add the instruction. break; } } } // TODO: For an implicit instruction, this reattempts evaluation. return AddInst(context, loc_id_and_inst); } auto EvalOrAddInst(Context& context, SemIR::LocIdAndInst loc_id_and_inst) -> SemIR::ConstantId { CARBON_CHECK(!loc_id_and_inst.inst.kind().has_cleanup()); switch (loc_id_and_inst.inst.kind().constant_needs_inst_id()) { case SemIR::InstConstantNeedsInstIdKind::No: { // Evaluation doesn't need an InstId. Just do it. return TryEvalInstUnsafe(context, SemIR::InstId::None, loc_id_and_inst.inst); } case SemIR::InstConstantNeedsInstIdKind::DuringEvaluation: { // Evaluation temporarily needs an InstId. Add one for now. auto inst_id = AddInstInNoBlock(context, loc_id_and_inst); // TODO: Consider removing `inst_id` from `insts` if it's still the most // recently added instruction. return context.constant_values().Get(inst_id); } case SemIR::InstConstantNeedsInstIdKind::Permanent: { // Evaluation needs a permanent InstId. Add the instruction. auto inst_id = AddInst(context, loc_id_and_inst); return context.constant_values().Get(inst_id); } } } auto AddPlaceholderInstInNoBlock(Context& context, SemIR::LocIdAndInst loc_id_and_inst) -> SemIR::InstId { auto inst_id = context.sem_ir().insts().AddInNoBlock(loc_id_and_inst); CARBON_VLOG_TO(context.vlog_stream(), "AddPlaceholderInst: {0}\n", loc_id_and_inst.inst); context.constant_values().Set(inst_id, SemIR::ConstantId::None); return inst_id; } auto AddPlaceholderImportedInstInNoBlock(Context& context, SemIR::LocIdAndInst loc_id_and_inst) -> SemIR::InstId { auto inst_id = AddPlaceholderInstInNoBlock(context, loc_id_and_inst); context.imports().push_back(inst_id); return inst_id; } auto AddPlaceholderInst(Context& context, SemIR::LocIdAndInst loc_id_and_inst) -> SemIR::InstId { auto inst_id = AddPlaceholderInstInNoBlock(context, loc_id_and_inst); context.inst_block_stack().AddInstId(inst_id); return inst_id; } auto ReplaceLocIdAndInstBeforeConstantUse(Context& context, SemIR::InstId inst_id, SemIR::LocIdAndInst loc_id_and_inst) -> void { context.sem_ir().insts().SetLocIdAndInst(inst_id, loc_id_and_inst); CARBON_VLOG_TO(context.vlog_stream(), "ReplaceInst: {0} -> {1}\n", inst_id, loc_id_and_inst.inst); FinishInst(context, inst_id, loc_id_and_inst.inst); } auto ReplaceInstBeforeConstantUse(Context& context, SemIR::InstId inst_id, SemIR::Inst inst) -> void { context.sem_ir().insts().Set(inst_id, inst); CARBON_VLOG_TO(context.vlog_stream(), "ReplaceInst: {0} -> {1}\n", inst_id, inst); FinishInst(context, inst_id, inst); } auto ReplaceInstPreservingConstantValue(Context& context, SemIR::InstId inst_id, SemIR::Inst inst) -> void { // Check that the type didn't change: a change of type will change the // constant value. Replace the type with the attached type. auto old_type_id = context.insts().GetAttachedType(inst_id); CARBON_CHECK(context.types().GetUnattachedType(old_type_id) == inst.type_id(), "Given wrong type for replacement instruction"); inst.SetType(old_type_id); // Update the instruction. context.sem_ir().insts().Set(inst_id, inst); CARBON_VLOG_TO(context.vlog_stream(), "ReplaceInst: {0} -> {1}\n", inst_id, inst); // Check the constant value didn't change. auto old_const_id = context.constant_values().Get(inst_id); auto new_const_id = TryEvalInstUnsafe(context, inst_id, inst); CARBON_CHECK(old_const_id == new_const_id); } auto SetNamespaceNodeId(Context& context, SemIR::InstId inst_id, Parse::NodeId node_id) -> void { context.sem_ir().insts().SetLocId(inst_id, SemIR::LocId(node_id)); } } // namespace Carbon::Check ================================================ FILE: toolchain/check/inst.h ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #ifndef CARBON_TOOLCHAIN_CHECK_INST_H_ #define CARBON_TOOLCHAIN_CHECK_INST_H_ #include #include "toolchain/check/context.h" #include "toolchain/sem_ir/ids.h" #include "toolchain/sem_ir/inst.h" namespace Carbon::Check { // Adds an instruction to the current block, returning the produced ID. auto AddInst(Context& context, SemIR::LocIdAndInst loc_id_and_inst) -> SemIR::InstId; // Convenience for AddInst with typed nodes. // // As a safety check, prevent use with storage insts (see `AddInstWithCleanup`). template requires(!InstT::Kind.has_cleanup() && std::convertible_to) auto AddInst(Context& context, LocT loc, InstT inst) -> SemIR::InstId { return AddInst(context, SemIR::LocIdAndInst(loc, inst)); } // Like AddInst, but for instructions with a type_id of `TypeType`, which is // encoded in the return type of `TypeInstId`. inline auto AddTypeInst(Context& context, SemIR::LocIdAndInst loc_id_and_inst) -> SemIR::TypeInstId { return context.types().GetAsTypeInstId(AddInst(context, loc_id_and_inst)); } template requires(!InstT::Kind.has_cleanup() && std::convertible_to) auto AddTypeInst(Context& context, LocT loc, InstT inst) -> SemIR::TypeInstId { return AddTypeInst(context, SemIR::LocIdAndInst(loc, inst)); } // Pushes a parse tree node onto the stack, storing the SemIR::Inst as the // result. // // As a safety check, prevent use with storage insts (see `AddInstWithCleanup`). template requires(SemIR::Internal::HasNodeId && !InstT::Kind.has_cleanup()) auto AddInstAndPush(Context& context, typename decltype(InstT::Kind)::TypedNodeId node_id, InstT inst) -> void { context.node_stack().Push(node_id, AddInst(context, node_id, inst)); } // Adds an instruction in no block, returning the produced ID. Should be used // rarely. auto AddInstInNoBlock(Context& context, SemIR::LocIdAndInst loc_id_and_inst) -> SemIR::InstId; // Convenience for AddInstInNoBlock with typed nodes. // // As a safety check, prevent use with storage insts (see `AddInstWithCleanup`). template requires(!InstT::Kind.has_cleanup() && std::convertible_to) auto AddInstInNoBlock(Context& context, LocT loc, InstT inst) -> SemIR::InstId { return AddInstInNoBlock(context, SemIR::LocIdAndInst(loc, inst)); } // If the instruction has a desugared location and a constant value, returns // the constant value's instruction ID. Otherwise, same as AddInst. auto GetOrAddInst(Context& context, SemIR::LocIdAndInst loc_id_and_inst) -> SemIR::InstId; // Convenience for GetOrAddInst with typed nodes. // // As a safety check, prevent use with storage insts (see `AddInstWithCleanup`). template requires(!InstT::Kind.has_cleanup() && std::convertible_to) auto GetOrAddInst(Context& context, LocT loc, InstT inst) -> SemIR::InstId { return GetOrAddInst(context, SemIR::LocIdAndInst(loc, inst)); } // Evaluate the given instruction, and returns the corresponding constant value. // Adds the instruction to the current block if it might be referenced by its // constant value; otherwise, does not add the instruction to an instruction // block. auto EvalOrAddInst(Context& context, SemIR::LocIdAndInst loc_id_and_inst) -> SemIR::ConstantId; // Convenience for EvalOrAddInst with typed nodes. // // As a safety check, prevent use with storage insts (see `AddInstWithCleanup`). template requires(!InstT::Kind.has_cleanup() && std::convertible_to) auto EvalOrAddInst(Context& context, LocT loc, InstT inst) -> SemIR::ConstantId { return EvalOrAddInst(context, SemIR::LocIdAndInst(loc, inst)); } // Adds an instruction and enqueues it to be added to the eval block of the // enclosing generic, returning the produced ID. The instruction is expected to // be a dependent template instantiation action. auto AddDependentActionInst(Context& context, SemIR::LocIdAndInst loc_id_and_inst) -> SemIR::InstId; // Convenience wrapper for AddDependentActionInst. template requires std::convertible_to auto AddDependentActionInst(Context& context, LocT loc, InstT inst) -> SemIR::InstId { return AddDependentActionInst(context, SemIR::LocIdAndInst(loc, inst)); } // Like AddDependentActionInst, but for instructions with a type_id of // `TypeType`, which is encoded in the return type of `TypeInstId`. template requires std::convertible_to auto AddDependentActionTypeInst(Context& context, LocT loc, InstT inst) -> SemIR::TypeInstId { return context.types().GetAsTypeInstId( AddDependentActionInst(context, loc, inst)); } // Adds an instruction to the current pattern block, returning the produced // ID. // TODO: Is it possible to remove this and pattern_block_stack, now that // we have BeginSubpattern etc. instead? auto AddPatternInst(Context& context, SemIR::LocIdAndInst loc_id_and_inst) -> SemIR::InstId; // Convenience for AddPatternInst with typed nodes. // // As a safety check, prevent use with storage insts (see `AddInstWithCleanup`). template requires(!InstT::Kind.has_cleanup() && std::convertible_to) auto AddPatternInst(Context& context, LocT loc, InstT inst) -> SemIR::InstId { return AddPatternInst(context, SemIR::LocIdAndInst(loc, inst)); } // Adds an instruction to the current block, returning the produced ID. The // instruction is a placeholder that is expected to be replaced by // `ReplaceInstBeforeConstantUse`. auto AddPlaceholderInst(Context& context, SemIR::LocIdAndInst loc_id_and_inst) -> SemIR::InstId; // Convenience for AddPlaceholderInst with typed nodes. // // As a safety check, prevent use with storage insts (see `AddInstWithCleanup`). template requires(!InstT::Kind.has_cleanup()) auto AddPlaceholderInst(Context& context, LocT loc, InstT inst) -> SemIR::InstId { return AddPlaceholderInst(context, SemIR::LocIdAndInst(loc, inst)); } // Adds an instruction in no block, returning the produced ID. Should be used // rarely. The instruction is a placeholder that is expected to be replaced by // `ReplaceInstBeforeConstantUse`. auto AddPlaceholderInstInNoBlock(Context& context, SemIR::LocIdAndInst loc_id_and_inst) -> SemIR::InstId; // Convenience for AddPlaceholderInstInNoBlock with typed nodes. // // As a safety check, prevent use with storage insts (see `AddInstWithCleanup`). template requires(!InstT::Kind.has_cleanup() && std::convertible_to) auto AddPlaceholderInstInNoBlock(Context& context, LocT loc, InstT inst) -> SemIR::InstId { return AddPlaceholderInstInNoBlock(context, SemIR::LocIdAndInst(loc, inst)); } // Similar to `AddPlaceholderInstInNoBlock`, but also tracks the instruction as // an import. auto AddPlaceholderImportedInstInNoBlock(Context& context, SemIR::LocIdAndInst loc_id_and_inst) -> SemIR::InstId; // Replaces the instruction at `inst_id` with `loc_id_and_inst`. The // instruction is required to not have been used in any constant evaluation, // either because it's newly created and entirely unused, or because it's only // used in a position that constant evaluation ignores, such as a return slot. auto ReplaceLocIdAndInstBeforeConstantUse(Context& context, SemIR::InstId inst_id, SemIR::LocIdAndInst loc_id_and_inst) -> void; // Replaces the instruction at `inst_id` with `inst`, not affecting location. // The instruction is required to not have been used in any constant // evaluation, either because it's newly created and entirely unused, or // because it's only used in a position that constant evaluation ignores, such // as a return slot. auto ReplaceInstBeforeConstantUse(Context& context, SemIR::InstId inst_id, SemIR::Inst inst) -> void; // Replaces the instruction at `inst_id` with `inst`, not affecting location. // The instruction is required to not change its constant value. auto ReplaceInstPreservingConstantValue(Context& context, SemIR::InstId inst_id, SemIR::Inst inst) -> void; // Sets only the parse node of an instruction. This is only used when setting // the parse node of an imported namespace. Versus // ReplaceInstBeforeConstantUse, it is safe to use after the namespace is used // in constant evaluation. It's exposed this way mainly so that `insts()` can // remain const. auto SetNamespaceNodeId(Context& context, SemIR::InstId inst_id, Parse::NodeId node_id) -> void; } // namespace Carbon::Check #endif // CARBON_TOOLCHAIN_CHECK_INST_H_ ================================================ FILE: toolchain/check/inst_block_stack.cpp ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #include "toolchain/check/inst_block_stack.h" #include "common/vlog.h" #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/StringExtras.h" namespace Carbon::Check { auto InstBlockStack::Push(SemIR::InstBlockId id) -> void { CARBON_VLOG("{0} Push {1}\n", name_, id_stack_.size()); CARBON_CHECK(id_stack_.size() < (1 << 20), "Excessive stack size: likely infinite loop"); id_stack_.push_back(id); insts_stack_.PushArray(); } auto InstBlockStack::Push(SemIR::InstBlockId id, llvm::ArrayRef inst_ids) -> void { Push(id); insts_stack_.AppendToTop(inst_ids); } auto InstBlockStack::PeekOrAdd(int depth) -> SemIR::InstBlockId { CARBON_CHECK(static_cast(id_stack_.size()) > depth, "no such block"); int index = id_stack_.size() - depth - 1; auto& slot = id_stack_[index]; if (!slot.has_value()) { slot = sem_ir_->inst_blocks().AddPlaceholder(); } return slot; } auto InstBlockStack::Pop() -> SemIR::InstBlockId { CARBON_CHECK(!empty(), "no current block"); auto id = id_stack_.pop_back_val(); auto insts = insts_stack_.PeekArray(); // Finalize the block. if (!insts.empty() && id != SemIR::InstBlockId::Unreachable) { if (id.has_value()) { sem_ir_->inst_blocks().ReplacePlaceholder(id, insts); } else { id = sem_ir_->inst_blocks().Add(insts); } } insts_stack_.PopArray(); CARBON_VLOG("{0} Pop {1}: {2}\n", name_, id_stack_.size(), id); return id.has_value() ? id : SemIR::InstBlockId::Empty; } auto InstBlockStack::PopAndDiscard() -> void { CARBON_CHECK(!empty(), "no current block"); id_stack_.pop_back(); insts_stack_.PopArray(); CARBON_VLOG("{0} PopAndDiscard {1}\n", name_, id_stack_.size()); } auto InstBlockStack::PrintForStackDump(int indent, llvm::raw_ostream& output) const -> void { output.indent(indent); output << name_ << ":\n"; for (const auto& [i, id] : llvm::enumerate(id_stack_)) { output.indent(indent + 2); output << i << ".\t" << id << "\t{"; llvm::ListSeparator sep; for (auto id : insts_stack_.PeekArrayAt(i)) { output << sep << id; } output << "}\n"; } } } // namespace Carbon::Check ================================================ FILE: toolchain/check/inst_block_stack.h ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #ifndef CARBON_TOOLCHAIN_CHECK_INST_BLOCK_STACK_H_ #define CARBON_TOOLCHAIN_CHECK_INST_BLOCK_STACK_H_ #include "common/array_stack.h" #include "llvm/ADT/SmallVector.h" #include "toolchain/sem_ir/file.h" namespace Carbon::Check { // A stack of instruction blocks that are currently being constructed in a // Context. The contents of the instruction blocks are stored here until the // instruction block is popped from the stack, at which point they are // transferred into the SemIR::File for long-term storage. // // All pushes and pops will be vlogged. class InstBlockStack { public: explicit InstBlockStack(llvm::StringLiteral name, SemIR::File& sem_ir, llvm::raw_ostream* vlog_stream) : name_(name), sem_ir_(&sem_ir), vlog_stream_(vlog_stream) {} // Pushes an existing instruction block. auto Push(SemIR::InstBlockId id) -> void; // Pushes an existing instruction block with a set of instructions. auto Push(SemIR::InstBlockId id, llvm::ArrayRef inst_ids) -> void; // Pushes a new instruction block. It will be `None` unless PeekOrAdd is // called in order to support lazy allocation. auto Push() -> void { Push(SemIR::InstBlockId::None); } // Pushes a new unreachable code block. auto PushUnreachable() -> void { Push(SemIR::InstBlockId::Unreachable); } // Returns the ID of the top instruction block, allocating one if necessary. // If `depth` is specified, returns the instruction block at `depth` levels // from the top of the stack instead of the top block, where the top block is // at depth 0. auto PeekOrAdd(int depth = 0) -> SemIR::InstBlockId; // Pops the top instruction block. This will never return `None`; `Empty` is // returned if one wasn't allocated. auto Pop() -> SemIR::InstBlockId; // Pops the top instruction block, and discards it if it hasn't had an ID // allocated. auto PopAndDiscard() -> void; // Adds the given instruction ID to the block at the top of the stack. auto AddInstId(SemIR::InstId inst_id) -> void { CARBON_CHECK(!empty(), "{0} has no current block", name_); insts_stack_.AppendToTop(inst_id); } // Returns whether the current block is statically reachable. auto is_current_block_reachable() -> bool { return id_stack_.back() != SemIR::InstBlockId::Unreachable; } // Returns a view of the contents of the top instruction block on the stack. auto PeekCurrentBlockContents() const -> llvm::ArrayRef { CARBON_CHECK(!empty(), "no current block"); return insts_stack_.PeekArray(); } // Prints the stack for a stack dump. auto PrintForStackDump(int indent, llvm::raw_ostream& output) const -> void; // Runs verification that the processing cleanly finished. auto VerifyOnFinish() const -> void { CARBON_CHECK(empty(), "{0} still has {1} entries", name_, id_stack_.size()); } auto empty() const -> bool { return id_stack_.empty(); } private: // A name for debugging. llvm::StringLiteral name_; // The underlying SemIR::File instance. Always non-null. SemIR::File* sem_ir_; // Whether to print verbose output. llvm::raw_ostream* vlog_stream_; // The stack of block IDs. A value if allocated, `None` if no block has been // allocated, or `Unreachable` if this block is known to be unreachable. llvm::SmallVector id_stack_; // The stack of insts in each block. ArrayStack insts_stack_; }; } // namespace Carbon::Check #endif // CARBON_TOOLCHAIN_CHECK_INST_BLOCK_STACK_H_ ================================================ FILE: toolchain/check/interface.cpp ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #include "toolchain/check/interface.h" #include #include #include "common/concepts.h" #include "toolchain/check/context.h" #include "toolchain/check/core_identifier.h" #include "toolchain/check/eval.h" #include "toolchain/check/generic.h" #include "toolchain/check/inst.h" #include "toolchain/check/merge.h" #include "toolchain/check/name_lookup.h" #include "toolchain/check/type.h" #include "toolchain/sem_ir/entity_with_params_base.h" #include "toolchain/sem_ir/ids.h" #include "toolchain/sem_ir/inst.h" #include "toolchain/sem_ir/typed_insts.h" namespace Carbon::Check { auto BuildAssociatedEntity(Context& context, SemIR::InterfaceId interface_id, SemIR::InstId decl_id) -> SemIR::InstId { auto& interface_info = context.interfaces().Get(interface_id); if (!interface_info.is_being_defined()) { // This should only happen if the interface is erroneously defined more than // once. // TODO: Find a way to CHECK this. return SemIR::ErrorInst::InstId; } // This associated entity is being declared as a member of an interface. We // use the self-specific of the interface-without-self as the AssociatedEntity // names the externally facing SpecificInterface (without self). auto interface_without_self_specific_id = context.generics().GetSelfSpecific(interface_info.generic_id); // Register this declaration as declaring an associated entity. auto index = SemIR::ElementIndex( context.args_type_info_stack().PeekCurrentBlockContents().size()); context.args_type_info_stack().AddInstId(decl_id); // Name lookup for the declaration's name should name the associated entity, // not the declaration itself. auto type_id = GetAssociatedEntityType(context, interface_id, interface_without_self_specific_id); return AddInst( context, SemIR::LocId(decl_id), {.type_id = type_id, .index = index, .decl_id = decl_id}); } auto GetSelfSpecificForInterfaceMemberWithSelfType( Context& context, SemIR::LocId loc_id, SemIR::SpecificId interface_with_self_specific_id, SemIR::GenericId generic_id, SemIR::SpecificId enclosing_specific_id) -> SemIR::SpecificId { const auto& generic = context.generics().Get(generic_id); auto self_specific_args = context.inst_blocks().Get( context.specifics().Get(generic.self_specific_id).args_id); auto arg_ids = llvm::SmallVector(context.inst_blocks().Get( context.specifics().GetArgsOrEmpty(interface_with_self_specific_id))); // Determine the number of specific arguments that enclose the point where // this self specific will be used from. In an impl, this will be the number // of parameters that the impl has. int num_enclosing_specific_args = context.inst_blocks() .Get(context.specifics().GetArgsOrEmpty(enclosing_specific_id)) .size(); // The index of each remaining generic parameter is adjusted to match the // numbering at the point where the self specific is used. int index_delta = num_enclosing_specific_args - arg_ids.size(); // Take any trailing argument values from the self specific. // TODO: If these refer to outer arguments, for example in their types, we may // need to perform extra substitutions here. for (auto arg_id : self_specific_args.drop_front(arg_ids.size())) { auto new_arg_id = context.constant_values().GetConstantInstId(arg_id); if (index_delta) { // If this parameter would have a new index in the context described by // `enclosing_specific_id`, form a new binding with an adjusted index. auto bind_name = context.insts().GetAs( context.constant_values().GetConstantInstId(arg_id)); auto entity_name = context.entity_names().Get(bind_name.entity_name_id); entity_name.bind_index_value += index_delta; CARBON_CHECK(entity_name.bind_index_value >= 0); bind_name.entity_name_id = context.entity_names().AddCanonical(entity_name); new_arg_id = context.constant_values().GetInstId(TryEvalInst(context, bind_name)); } arg_ids.push_back(new_arg_id); } return MakeSpecific(context, loc_id, generic_id, arg_ids); } auto GetTypeForSpecificAssociatedEntity( Context& context, SemIR::SpecificId interface_with_self_specific_id, SemIR::InstId decl_id) -> SemIR::TypeId { auto decl_constant_inst_id = context.constant_values().GetConstantInstId(decl_id); if (decl_constant_inst_id == SemIR::ErrorInst::InstId) { return SemIR::ErrorInst::TypeId; } auto decl = context.insts().Get(decl_constant_inst_id); if (auto assoc_const = decl.TryAs()) { return SemIR::GetTypeOfInstInSpecific( context.sem_ir(), interface_with_self_specific_id, decl_id); } if (auto fn = context.types().TryGetAs(decl.type_id())) { // Form the type of the function within the interface, and attach the `Self` // type. auto interface_fn_type_id = SemIR::GetTypeOfInstInSpecific( context.sem_ir(), interface_with_self_specific_id, decl_id); auto self_facet_id = context.inst_blocks() .Get(context.specifics().GetArgsOrEmpty( interface_with_self_specific_id)) .back(); return GetFunctionTypeWithSelfType( context, context.types().GetTypeInstId(interface_fn_type_id), self_facet_id); } CARBON_FATAL("Unexpected kind for associated constant {0}", decl); } auto AddSelfSymbolicBindingToScope(Context& context, SemIR::LocId definition_loc_id, SemIR::TypeId type_id, SemIR::NameScopeId scope_id, bool is_template) -> SemIR::InstId { auto entity_name_id = context.entity_names().AddSymbolicBindingName( SemIR::NameId::SelfType, scope_id, context.scope_stack().AddCompileTimeBinding(), is_template, /*is_unused=*/false); // Because there is no equivalent non-symbolic value, we use `None` as // the `value_id` on the `SymbolicBinding`. auto self_param_inst_id = AddInst(context, definition_loc_id, {.type_id = type_id, .entity_name_id = entity_name_id, .value_id = SemIR::InstId::None}); context.name_scopes().AddRequiredName(scope_id, SemIR::NameId::SelfType, self_param_inst_id); return self_param_inst_id; } template requires std::same_as static auto TryGetEntity(Context& context, SemIR::Inst inst) -> const SemIR::EntityWithParamsBase* { if (auto decl = inst.TryAs()) { return &context.interfaces().Get(decl->interface_id); } else { return nullptr; } } template requires std::same_as static auto TryGetEntity(Context& context, SemIR::Inst inst) -> const SemIR::EntityWithParamsBase* { if (auto decl = inst.TryAs()) { return &context.named_constraints().Get(decl->named_constraint_id); } else { return nullptr; } } template requires std::same_as static constexpr auto DeclTokenKind() -> Lex::TokenKind { return Lex::TokenKind::Interface; } template requires std::same_as static constexpr auto DeclTokenKind() -> Lex::TokenKind { return Lex::TokenKind::Constraint; } template requires SameAsOneOf auto TryGetExistingDecl(Context& context, const NameComponent& name, SemIR::ScopeLookupResult lookup_result, const EntityT& entity, bool is_definition) -> std::optional { if (lookup_result.is_poisoned()) { // This is a declaration of a poisoned name. DiagnosePoisonedName(context, name.name_id, lookup_result.poisoning_loc_id(), name.name_loc_id); return std::nullopt; } if (!lookup_result.is_found()) { return std::nullopt; } SemIR::InstId existing_id = lookup_result.target_inst_id(); SemIR::Inst existing_decl_inst = context.insts().Get(existing_id); const auto* existing_decl_entity = TryGetEntity(context, existing_decl_inst); if (!existing_decl_entity) { // This is a redeclaration with a different entity kind. DiagnoseDuplicateName(context, name.name_id, name.name_loc_id, SemIR::LocId(existing_id)); return std::nullopt; } if (!CheckRedeclParamsMatch( context, DeclParams(SemIR::LocId(entity.latest_decl_id()), name.first_param_node_id, name.last_param_node_id, name.implicit_param_patterns_id, name.param_patterns_id), DeclParams(*existing_decl_entity))) { // Mismatch is diagnosed already if found. return std::nullopt; } // TODO: This should be refactored a little, particularly for // prev_import_ir_id. See similar logic for classes and functions, which // might also be refactored to merge. DiagnoseIfInvalidRedecl( context, DeclTokenKind(), existing_decl_entity->name_id, RedeclInfo(entity, SemIR::LocId(entity.latest_decl_id()), is_definition), RedeclInfo(*existing_decl_entity, SemIR::LocId(existing_decl_entity->latest_decl_id()), existing_decl_entity->has_definition_started()), /*prev_import_ir_id=*/SemIR::ImportIRId::None); if (is_definition && existing_decl_entity->has_definition_started()) { // DiagnoseIfInvalidRedecl would diagnose an error in this case, since we'd // have two definitions. Given the declaration parts of the definitions // match, we would be able to use the prior declaration for error recovery, // except that having two definitions causes larger problems for generics. // All interfaces (and named constraints) are generic with an implicit Self // compile time binding. return std::nullopt; } // This is a matching redeclaration of an existing entity of the same type. return existing_decl_inst; } template auto TryGetExistingDecl(Context& context, const NameComponent& name, SemIR::ScopeLookupResult lookup_result, const SemIR::Interface& entity, bool is_definition) -> std::optional; template auto TryGetExistingDecl(Context& context, const NameComponent& name, SemIR::ScopeLookupResult lookup_result, const SemIR::NamedConstraint& entity, bool is_definition) -> std::optional; } // namespace Carbon::Check ================================================ FILE: toolchain/check/interface.h ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #ifndef CARBON_TOOLCHAIN_CHECK_INTERFACE_H_ #define CARBON_TOOLCHAIN_CHECK_INTERFACE_H_ #include #include "toolchain/check/context.h" #include "toolchain/check/decl_name_stack.h" #include "toolchain/check/name_component.h" #include "toolchain/parse/node_ids.h" #include "toolchain/sem_ir/entity_with_params_base.h" #include "toolchain/sem_ir/ids.h" #include "toolchain/sem_ir/name_scope.h" #include "toolchain/sem_ir/typed_insts.h" namespace Carbon::Check { // Builds and returns an associated entity for `interface_id` corresponding to // the declaration `decl_id`, which can be an associated function or an // associated constant. Registers the associated entity in the list for the // interface. auto BuildAssociatedEntity(Context& context, SemIR::InterfaceId interface_id, SemIR::InstId decl_id) -> SemIR::InstId; // Gets the self specific of a generic declaration that is an interface member, // given a specific for the interface plus a type to use as `Self`. auto GetSelfSpecificForInterfaceMemberWithSelfType( Context& context, SemIR::LocId loc_id, SemIR::SpecificId interface_with_self_specific_id, SemIR::GenericId generic_id, SemIR::SpecificId enclosing_specific_id) -> SemIR::SpecificId; // Gets the type of the specified associated entity, given the specific for the // interface and the type of `Self`. auto GetTypeForSpecificAssociatedEntity( Context& context, SemIR::SpecificId interface_with_self_specific_id, SemIR::InstId decl_id) -> SemIR::TypeId; // Creates a symbolic binding for `Self` of type `type_id` in the scope of // `scope_id`. // // Returns the symbolic binding instruction. auto AddSelfSymbolicBindingToScope(Context& context, SemIR::LocId definition_loc_id, SemIR::TypeId type_id, SemIR::NameScopeId scope_id, bool is_template) -> SemIR::InstId; // Given a search result `lookup_result` for `name`, returns the previous valid // declaration of `name` if there is one. The `entity` is a new decl of the same // `name`, and the existing decl need to be of the same entity type. Otherwise, // produces diagnostics if needed and returns nullopt. template requires SameAsOneOf auto TryGetExistingDecl(Context& context, const NameComponent& name, SemIR::ScopeLookupResult lookup_result, const EntityT& entity, bool is_definition) -> std::optional; } // namespace Carbon::Check #endif // CARBON_TOOLCHAIN_CHECK_INTERFACE_H_ ================================================ FILE: toolchain/check/keyword_modifier_set.cpp ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #include "toolchain/check/keyword_modifier_set.h" namespace Carbon::Check { CARBON_DEFINE_ENUM_MASK_NAMES(KeywordModifierSet) { CARBON_KEYWORD_MODIFIER_SET(CARBON_ENUM_MASK_NAME_STRING) }; } // namespace Carbon::Check ================================================ FILE: toolchain/check/keyword_modifier_set.h ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #ifndef CARBON_TOOLCHAIN_CHECK_KEYWORD_MODIFIER_SET_H_ #define CARBON_TOOLCHAIN_CHECK_KEYWORD_MODIFIER_SET_H_ #include #include "common/enum_mask_base.h" #include "toolchain/sem_ir/name_scope.h" namespace Carbon::Check { // The order of modifiers. Each of these corresponds to a group on // KeywordModifierSet, and can be used as an array index. enum class ModifierOrder : int8_t { Access, Extern, Extend, Decl, Evaluation, Last = Evaluation }; // A single X-macro to cover modifier groups. These are split out to make groups // clearer. #define CARBON_KEYWORD_MODIFIER_SET(X) \ /* At most one of these access modifiers allowed for a given declaration, \ * and if present it must be first. */ \ X(Private) \ X(Protected) \ \ /* Extern is standalone. */ \ X(Extern) \ \ /* Extend can be combined with Final, but no others in the group below. */ \ X(Extend) \ \ /* At most one of these declaration modifiers allowed for a given \ * declaration. */ \ X(Abstract) \ X(Base) \ X(Default) \ X(Export) \ X(Final) \ X(Impl) \ X(Override) \ X(Returned) \ X(Virtual) \ \ /* Eval and MustEval are mutually exclusive. */ \ X(Eval) \ X(MustEval) // We expect this to grow, so are using a bigger size than needed. CARBON_DEFINE_RAW_ENUM_MASK(KeywordModifierSet, uint32_t) { CARBON_KEYWORD_MODIFIER_SET(CARBON_RAW_ENUM_MASK_ENUMERATOR) }; // Represents a set of keyword modifiers, using a separate bit per modifier. class KeywordModifierSet : public CARBON_ENUM_MASK_BASE(KeywordModifierSet) { public: CARBON_KEYWORD_MODIFIER_SET(CARBON_ENUM_MASK_CONSTANT_DECL) // Sets of modifiers. static const KeywordModifierSet Access; static const KeywordModifierSet Class; static const KeywordModifierSet Method; static const KeywordModifierSet ImplDecl; static const KeywordModifierSet Interface; static const KeywordModifierSet Evaluation; static const KeywordModifierSet Decl; // Return a builder that returns the new enumeration type once a series of // mapping `Case`s and a final `Default` are provided. For example: // ``` // auto e = set.ToEnum() // .Case(KeywordModifierSet::A, SomeEnum::A) // .Case(KeywordModifierSet::B, SomeEnum::B) // .Default(SomeEnum::DefaultValue); // ``` template auto ToEnum() const -> auto { class Converter { public: explicit Converter(const KeywordModifierSet& set) : set_(set) {} auto Case(KeywordModifierSet other, T result) -> Converter& { if (set_.HasAnyOf(other)) { result_ = result; } return *this; } auto Default(T default_value) -> T { if (result_) { return *result_; } return default_value; } private: const KeywordModifierSet& set_; std::optional result_; }; return Converter(*this); } // Returns the access kind from modifiers. auto GetAccessKind() const -> SemIR::AccessKind { if (HasAnyOf(KeywordModifierSet::Protected)) { return SemIR::AccessKind::Protected; } if (HasAnyOf(KeywordModifierSet::Private)) { return SemIR::AccessKind::Private; } return SemIR::AccessKind::Public; } }; #define CARBON_KEYWORD_MODIFIER_SET_WITH_TYPE(X) \ CARBON_ENUM_MASK_CONSTANT_DEFINITION(KeywordModifierSet, X) CARBON_KEYWORD_MODIFIER_SET(CARBON_KEYWORD_MODIFIER_SET_WITH_TYPE) #undef CARBON_KEYWORD_MODIFIER_SET_WITH_TYPE inline constexpr KeywordModifierSet KeywordModifierSet::Access(Private | Protected); inline constexpr KeywordModifierSet KeywordModifierSet::Class(Abstract | Base); inline constexpr KeywordModifierSet KeywordModifierSet::Method(Abstract | Override | Virtual); inline constexpr KeywordModifierSet KeywordModifierSet::ImplDecl(Extend | Final); inline constexpr KeywordModifierSet KeywordModifierSet::Interface(Default | Final); inline constexpr KeywordModifierSet KeywordModifierSet::Decl(Class | Method | Impl | Interface | Export | Returned); inline constexpr KeywordModifierSet KeywordModifierSet::Evaluation(Eval | MustEval); // TODO: This and the ordering checking logic in handle_modifiers.cpp are // becoming unwieldy. Find a better representation. static_assert( !KeywordModifierSet::Access.HasAnyOf(KeywordModifierSet::Extern) && !(KeywordModifierSet::Access | KeywordModifierSet::Extern | KeywordModifierSet::Extend) .HasAnyOf(KeywordModifierSet::Decl) && !(KeywordModifierSet::Access | KeywordModifierSet::Extern | KeywordModifierSet::Decl) .HasAnyOf(KeywordModifierSet::Evaluation), "Order-related sets must not overlap"); #define CARBON_KEYWORD_MODIFIER_SET_IN_GROUP(Modifier) \ static_assert((KeywordModifierSet::Access | KeywordModifierSet::Extern | \ KeywordModifierSet::Extend | KeywordModifierSet::Decl | \ KeywordModifierSet::Evaluation) \ .HasAnyOf(KeywordModifierSet::Modifier), \ "Modifier missing from all modifier sets: " #Modifier); CARBON_KEYWORD_MODIFIER_SET(CARBON_KEYWORD_MODIFIER_SET_IN_GROUP) #undef CARBON_KEYWORD_MODIFIER_SET_IN_GROUP } // namespace Carbon::Check #endif // CARBON_TOOLCHAIN_CHECK_KEYWORD_MODIFIER_SET_H_ ================================================ FILE: toolchain/check/lexical_lookup.h ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #ifndef CARBON_TOOLCHAIN_CHECK_LEXICAL_LOOKUP_H_ #define CARBON_TOOLCHAIN_CHECK_LEXICAL_LOOKUP_H_ #include "toolchain/base/canonical_value_store.h" #include "toolchain/base/shared_value_stores.h" #include "toolchain/base/value_ids.h" #include "toolchain/check/scope_index.h" #include "toolchain/sem_ir/ids.h" namespace Carbon::Check { // Manages lexical lookup information for NameIds. // // Values are a stack of name lookup results in the ancestor scopes. This offers // constant-time lookup of names, regardless of how many scopes exist between // the name declaration and reference. The corresponding scope for each lookup // result is tracked, so that lexical lookup results can be interleaved with // lookup results from non-lexical scopes such as classes. class LexicalLookup { public: // A lookup result. struct Result { // The instruction that was added to lookup. SemIR::InstId inst_id; // The scope in which the instruction was added. ScopeIndex scope_index; // Whether the name was declared in a reachable position. bool is_decl_reachable = true; // The location of the first use of the name, if any. SemIR::LocId use_loc_id = SemIR::LocId::None; }; // A lookup result that has been temporarily removed from scope. struct SuspendedResult { // The lookup index. This is notionally a size_t, but is stored in 32 bits // to keep this type small, which helps to keep SuspendedFunctions small. uint32_t index; // The lookup result. SemIR::InstId inst_id; // Whether the name was declared in a reachable position. bool is_decl_reachable; // The location of the first use of the name, if any. SemIR::LocId use_loc_id; }; explicit LexicalLookup(const SharedValueStores::IdentifierStore& identifiers) : lookup_(identifiers.size() + SemIR::NameId::NonIndexValueCount) {} // Returns the lexical lookup results for a name. auto Get(SemIR::NameId name_id) -> llvm::SmallVector& { auto index = GetLookupIndex(name_id); CARBON_CHECK( index < lookup_.size(), "An identifier was added after the Context was initialized. Currently, " "we expect that new identifiers will never be used with lexical lookup " "(they're added for things like detecting name collisions in imports). " "That might change with metaprogramming: if it does, we may need to " "start resizing `lookup_`, either on each identifier addition or in " "Get` where this CHECK currently fires."); return lookup_[index]; } // Temporarily remove the top lookup result for `name_id` from scope. auto Suspend(SemIR::NameId name_id) -> SuspendedResult { auto index = GetLookupIndex(name_id); auto& results = lookup_[index]; CARBON_CHECK(!results.empty(), "Suspending a nonexistent result for {0}.", name_id); CARBON_CHECK(index <= std::numeric_limits::max(), "Unexpectedly large index {0} for name ID", index); auto result = results.pop_back_val(); return {.index = static_cast(index), .inst_id = result.inst_id, .is_decl_reachable = result.is_decl_reachable, .use_loc_id = result.use_loc_id}; } // Restore a previously-suspended lookup result. auto Restore(SuspendedResult sus, ScopeIndex index) -> void { lookup_[sus.index].push_back({.inst_id = sus.inst_id, .scope_index = index, .is_decl_reachable = sus.is_decl_reachable, .use_loc_id = sus.use_loc_id}); } private: // Get the index at which the specified name is stored in `lookup_`. auto GetLookupIndex(SemIR::NameId name_id) -> size_t { return static_cast(name_id.index) + SemIR::NameId::NonIndexValueCount; } // Maps identifiers to name lookup results. // // The outer size of `0` is used because it's resized once on construction, // and will rarely fit on the stack. The inner size of `2` is used because // most entries will only have zero or one results. // // TODO: Consider TinyPtrVector or similar. For now, use a small size // of 2 to cover the common case. llvm::SmallVector, 0> lookup_; }; } // namespace Carbon::Check #endif // CARBON_TOOLCHAIN_CHECK_LEXICAL_LOOKUP_H_ ================================================ FILE: toolchain/check/literal.cpp ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #include "toolchain/check/literal.h" #include "toolchain/check/call.h" #include "toolchain/check/context.h" #include "toolchain/check/convert.h" #include "toolchain/check/diagnostic_helpers.h" #include "toolchain/check/name_lookup.h" #include "toolchain/check/type.h" #include "toolchain/check/type_completion.h" #include "toolchain/diagnostics/diagnostic.h" #include "toolchain/lex/token_info.h" #include "toolchain/parse/node_ids.h" #include "toolchain/sem_ir/ids.h" namespace Carbon::Check { // Adds a TypeLiteral instruction to represent a syntactic type literal. static auto MakeTypeLiteral(Context& context, SemIR::LocId loc_id, SemIR::InstId value_id) -> SemIR::TypeInstId { auto type_inst_id = ExprAsType(context, loc_id, value_id).inst_id; return AddTypeInst( context, loc_id, {.type_id = SemIR::TypeType::TypeId, .value_id = type_inst_id}); } auto MakeTypeTypeLiteral(Context& context, Parse::NodeId node_id) -> SemIR::TypeInstId { return MakeTypeLiteral(context, node_id, SemIR::TypeType::TypeInstId); } auto MakeBoolTypeLiteral(Context& context, Parse::NodeId node_id) -> SemIR::TypeInstId { auto desugared_loc_id = SemIR::LocId(node_id).AsDesugared(); auto inst_id = LookupNameInCore(context, desugared_loc_id, CoreIdentifier::Bool); inst_id = PerformCall(context, desugared_loc_id, inst_id, {}); return MakeTypeLiteral(context, node_id, inst_id); } auto MakeBoolLiteral(Context& context, SemIR::LocId loc_id, SemIR::BoolValue value) -> SemIR::InstId { return AddInst( context, loc_id, {.type_id = GetSingletonType(context, SemIR::BoolType::TypeInstId), .value = value}); } auto MakeIntLiteral(Context& context, Parse::NodeId node_id, IntId int_id) -> SemIR::InstId { return AddInst( context, node_id, {.type_id = GetSingletonType(context, SemIR::IntLiteralType::TypeInstId), .int_id = int_id}); } // Returns an instruction with the given constant integer value. static auto GetOrAddIntValue(Context& context, SemIR::LocId loc_id, IntId int_id) -> SemIR::InstId { return GetOrAddInst( context, loc_id, {.type_id = GetSingletonType(context, SemIR::IntLiteralType::TypeInstId), .int_id = int_id}); } auto MakeCharTypeLiteral(Context& context, Parse::NodeId node_id) -> SemIR::TypeInstId { auto inst_id = LookupNameInCore(context, node_id, CoreIdentifier::Char); return MakeTypeLiteral(context, node_id, inst_id); } // Returns an instruction representing the type `iN` or `uN`. static auto GetOrAddIntTypeInst(Context& context, SemIR::LocId loc_id, SemIR::IntKind int_kind, IntId size_id) -> SemIR::InstId { auto width_id = GetOrAddIntValue(context, loc_id, size_id); auto fn_inst_id = LookupNameInCore(context, loc_id, int_kind == SemIR::IntKind::Signed ? CoreIdentifier::Int : CoreIdentifier::UInt); return PerformCall(context, loc_id, fn_inst_id, {width_id}); } auto MakeIntTypeLiteral(Context& context, Parse::NodeId node_id, SemIR::IntKind int_kind, IntId size_id) -> SemIR::TypeInstId { auto desugared_loc_id = SemIR::LocId(node_id).AsDesugared(); auto type_inst_id = GetOrAddIntTypeInst(context, desugared_loc_id, int_kind, size_id); return MakeTypeLiteral(context, node_id, type_inst_id); } auto MakeIntType(Context& context, Parse::NodeId node_id, SemIR::IntKind int_kind, IntId size_id) -> SemIR::TypeId { auto desugared_loc_id = SemIR::LocId(node_id).AsDesugared(); auto type_inst_id = GetOrAddIntTypeInst(context, desugared_loc_id, int_kind, size_id); return ExprAsType(context, node_id, type_inst_id).type_id; } auto MakeFloatTypeLiteral(Context& context, Parse::NodeId node_id, IntId size_id) -> SemIR::TypeInstId { auto desugared_loc_id = SemIR::LocId(node_id).AsDesugared(); auto width_id = GetOrAddIntValue(context, desugared_loc_id, size_id); auto fn_inst_id = LookupNameInCore(context, desugared_loc_id, CoreIdentifier::Float); auto call_id = PerformCall(context, desugared_loc_id, fn_inst_id, {width_id}); return MakeTypeLiteral(context, node_id, call_id); } namespace { // The extracted representation of the type `Core.String`. struct StringRepr { SemIR::TypeId ptr_field_type_id; SemIR::TypeId size_field_type_id; SemIR::TypeStore::IntTypeInfo size_field_type_info; }; } // namespace // Extracts information about the representation of the `Core.String` type // necessary for building a string literal. static auto GetStringLiteralRepr(Context& context, SemIR::LocId loc_id, SemIR::TypeId type_id) -> std::optional { // The object representation should be a struct type. auto object_repr_id = context.types().GetObjectRepr(type_id); auto struct_repr = context.types().TryGetAs(object_repr_id); if (!struct_repr) { return std::nullopt; } // The struct should have two fields. auto fields = context.struct_type_fields().Get(struct_repr->fields_id); if (fields.size() != 2) { return std::nullopt; } // The first field should be a pointer to 8-bit integers. auto ptr_type = context.insts().TryGetAs(fields[0].type_inst_id); if (!ptr_type) { return std::nullopt; } auto pointee_type_id = context.types().GetTypeIdForTypeInstId(ptr_type->pointee_id); if (!TryToCompleteType(context, pointee_type_id, loc_id)) { return std::nullopt; } auto elem_type_info = context.types().TryGetIntTypeInfo(pointee_type_id); if (!elem_type_info || context.ints().Get(elem_type_info->bit_width) != 8) { return std::nullopt; } // The second field should be an integer type. auto size_field_type_id = context.types().GetTypeIdForTypeInstId(fields[1].type_inst_id); auto size_type_info = context.types().TryGetIntTypeInfo(size_field_type_id); if (!size_type_info) { return std::nullopt; } return StringRepr{.ptr_field_type_id = context.types().GetTypeIdForTypeInstId( fields[0].type_inst_id), .size_field_type_id = size_field_type_id, .size_field_type_info = *size_type_info}; } auto MakeStringLiteral(Context& context, Parse::StringLiteralId node_id, StringLiteralValueId value_id) -> SemIR::InstId { auto str_type = MakeStringType(context, SemIR::LocId(node_id).AsDesugared()); if (!RequireCompleteType( context, str_type.type_id, node_id, [&](auto& builder) { CARBON_DIAGNOSTIC(StringLiteralTypeIncomplete, Context, "type {0} is incomplete", InstIdAsType); builder.Context(node_id, StringLiteralTypeIncomplete, str_type.inst_id); })) { return SemIR::ErrorInst::InstId; } auto repr = GetStringLiteralRepr(context, node_id, str_type.type_id); if (!repr) { if (str_type.type_id != SemIR::ErrorInst::TypeId) { CARBON_DIAGNOSTIC(StringLiteralTypeUnexpected, Error, "unexpected representation for type {0}", InstIdAsType); context.emitter().Emit(node_id, StringLiteralTypeUnexpected, str_type.inst_id); } return SemIR::ErrorInst::InstId; } // The pointer field is a `StringLiteral` object. // TODO: Perhaps `StringLiteral` should instead produce a durable reference, // and we should take its address here? auto ptr_value_id = AddInst( context, node_id, {.type_id = repr->ptr_field_type_id, .string_literal_id = value_id}); // The size field is an integer literal. auto size = context.string_literal_values().Get(value_id).size(); if (repr->size_field_type_info.bit_width.has_value()) { // Check that the size value fits in the size field. auto width = context.ints() .Get(repr->size_field_type_info.bit_width) .getLimitedValue(); if (repr->size_field_type_info.is_signed ? !llvm::isIntN(width, size) : !llvm::isUIntN(width, size)) { CARBON_DIAGNOSTIC(StringLiteralTooLong, Error, "string literal is too long"); context.emitter().Emit(node_id, StringLiteralTooLong); return SemIR::ErrorInst::InstId; } } auto size_value_id = AddInst(context, node_id, {.type_id = repr->size_field_type_id, .int_id = context.ints().Add(size)}); // Build the representation struct. auto elements_id = context.inst_blocks().Add({ptr_value_id, size_value_id}); return AddInst( context, node_id, {.type_id = str_type.type_id, .elements_id = elements_id}); } // Returns an instruction with the value `str`. static auto GetOrAddStringTypeInst(Context& context, SemIR::LocId loc_id) -> SemIR::InstId { return LookupNameInCore(context, loc_id, CoreIdentifier::String); } auto MakeStringTypeLiteral(Context& context, Parse::StringTypeLiteralId node_id) -> SemIR::TypeInstId { auto inst_id = GetOrAddStringTypeInst(context, node_id); return MakeTypeLiteral(context, node_id, inst_id); } auto MakeStringType(Context& context, SemIR::LocId loc_id) -> TypeExpr { auto type_inst_id = GetOrAddStringTypeInst(context, loc_id); return ExprAsType(context, loc_id, type_inst_id); } } // namespace Carbon::Check ================================================ FILE: toolchain/check/literal.h ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #ifndef CARBON_TOOLCHAIN_CHECK_LITERAL_H_ #define CARBON_TOOLCHAIN_CHECK_LITERAL_H_ #include "toolchain/base/value_ids.h" #include "toolchain/check/context.h" #include "toolchain/check/convert.h" #include "toolchain/lex/token_info.h" #include "toolchain/sem_ir/ids.h" namespace Carbon::Check { // Forms a TypeType for a `type` literal. auto MakeTypeTypeLiteral(Context& context, Parse::NodeId node_id) -> SemIR::TypeInstId; // Forms a boolean type for a `bool` literal. auto MakeBoolTypeLiteral(Context& context, Parse::NodeId node_id) -> SemIR::TypeInstId; // Forms a BoolLiteral instruction with the given value and returns it. auto MakeBoolLiteral(Context& context, SemIR::LocId loc_id, SemIR::BoolValue value) -> SemIR::InstId; // Forms an IntValue instruction with type `IntLiteral` for a given literal // integer value, which is assumed to be unsigned. auto MakeIntLiteral(Context& context, Parse::NodeId node_id, IntId int_id) -> SemIR::InstId; // Forms a char type expression for `char` literal. auto MakeCharTypeLiteral(Context& context, Parse::NodeId node_id) -> SemIR::TypeInstId; // Forms an integer type expression for either an `iN` or `uN` literal. auto MakeIntTypeLiteral(Context& context, Parse::NodeId node_id, SemIR::IntKind int_kind, IntId size_id) -> SemIR::TypeInstId; // Forms an integer type of the specified kind and bit-width. auto MakeIntType(Context& context, Parse::NodeId node_id, SemIR::IntKind int_kind, IntId size_id) -> SemIR::TypeId; // Forms a floating point type expression for `fN` literal. auto MakeFloatTypeLiteral(Context& context, Parse::NodeId node_id, IntId size_id) -> SemIR::TypeInstId; // Forms a string literal value instruction for a given string literal. auto MakeStringLiteral(Context& context, Parse::StringLiteralId node_id, StringLiteralValueId value_id) -> SemIR::InstId; // Forms a string literal type expression for a `str` literal. auto MakeStringTypeLiteral(Context& context, Parse::StringTypeLiteralId node_id) -> SemIR::TypeInstId; // Forms a string type. auto MakeStringType(Context& context, SemIR::LocId loc_id) -> TypeExpr; } // namespace Carbon::Check #endif // CARBON_TOOLCHAIN_CHECK_LITERAL_H_ ================================================ FILE: toolchain/check/member_access.cpp ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #include "toolchain/check/member_access.h" #include #include "llvm/ADT/STLExtras.h" #include "toolchain/base/kind_switch.h" #include "toolchain/check/action.h" #include "toolchain/check/context.h" #include "toolchain/check/convert.h" #include "toolchain/check/eval.h" #include "toolchain/check/facet_type.h" #include "toolchain/check/generic.h" #include "toolchain/check/impl_lookup.h" #include "toolchain/check/import_ref.h" #include "toolchain/check/inst.h" #include "toolchain/check/interface.h" #include "toolchain/check/name_lookup.h" #include "toolchain/check/type.h" #include "toolchain/check/type_completion.h" #include "toolchain/diagnostics/emitter.h" #include "toolchain/sem_ir/expr_info.h" #include "toolchain/sem_ir/function.h" #include "toolchain/sem_ir/generic.h" #include "toolchain/sem_ir/ids.h" #include "toolchain/sem_ir/inst.h" #include "toolchain/sem_ir/name_scope.h" #include "toolchain/sem_ir/specific_interface.h" #include "toolchain/sem_ir/typed_insts.h" namespace Carbon::Check { // Returns the index of the specified class element within the class's // representation. static auto GetClassElementIndex(Context& context, SemIR::InstId element_id) -> SemIR::ElementIndex { auto element_inst = context.insts().Get(element_id); if (auto field = element_inst.TryAs()) { return field->index; } if (auto base = element_inst.TryAs()) { return base->index; } CARBON_FATAL("Unexpected value {0} in class element name", element_inst); } // Returns whether `function_id` is an instance method: in other words, whether // it has an implicit `self` parameter. static auto IsInstanceMethod(const SemIR::File& sem_ir, SemIR::FunctionId function_id) -> bool { const auto& function = sem_ir.functions().Get(function_id); return function.self_param_id.has_value(); } // For callee functions which are instance methods, returns the `self_id` (which // may be `None`). This may be an instance method either because it's a Carbon // instance method or because it's a C++ overload set that might contain an // instance method. static auto GetSelfIfInstanceMethod(const SemIR::File& sem_ir, const SemIR::Callee& callee) -> std::optional { CARBON_KIND_SWITCH(callee) { case CARBON_KIND(SemIR::CalleeFunction fn): { if (IsInstanceMethod(sem_ir, fn.function_id)) { return fn.self_id; } return std::nullopt; } case CARBON_KIND(SemIR::CalleeCppOverloadSet overload): { // For now, treat all C++ overload sets as potentially containing instance // methods. Overload resolution will handle the case where we actually // found a static method. // TODO: Consider returning `None` if there are no non-instance methods // in the overload set. This would cause us to reject // `instance.(Class.StaticMethod)()` like we do in pure Carbon code. return overload.self_id; } case CARBON_KIND(SemIR::CalleeError _): { return std::nullopt; } case CARBON_KIND(SemIR::CalleeNonFunction _): { return std::nullopt; } } } // Return whether `type_id`, the type of an associated entity, is for an // instance member (currently true only for instance methods). static auto IsInstanceType(Context& context, SemIR::TypeId type_id) -> bool { if (auto function_type = context.types().TryGetAs(type_id)) { return IsInstanceMethod(context.sem_ir(), function_type->function_id); } return false; } auto GetHighestAllowedAccess(Context& context, SemIR::LocId loc_id, SemIR::ConstantId name_scope_const_id) -> SemIR::AccessKind { SemIR::ScopeLookupResult lookup_result = LookupUnqualifiedName(context, loc_id, SemIR::NameId::SelfType, /*required=*/false) .scope_result; CARBON_CHECK(!lookup_result.is_poisoned()); if (!lookup_result.is_found()) { return SemIR::AccessKind::Public; } // TODO: Support other types for `Self`. auto self_class_type = context.insts().TryGetAs( lookup_result.target_inst_id()); if (!self_class_type) { return SemIR::AccessKind::Public; } auto self_class_info = context.classes().Get(self_class_type->class_id); // TODO: Support other types. if (auto class_type = context.insts().TryGetAs( context.constant_values().GetInstId(name_scope_const_id))) { auto class_info = context.classes().Get(class_type->class_id); if (self_class_info.self_type_id == class_info.self_type_id) { return SemIR::AccessKind::Private; } // If the `type_id` of `Self` does not match with the one we're currently // accessing, try checking if this class is of the parent type of `Self`. if (auto base_type_id = self_class_info.GetBaseType( context.sem_ir(), self_class_type->specific_id); base_type_id.has_value()) { if (context.types().GetConstantId(base_type_id) == name_scope_const_id) { return SemIR::AccessKind::Protected; } // TODO: Also check whether this base class has a base class of its own. } else if (auto adapt_type_id = self_class_info.GetAdaptedType( context.sem_ir(), self_class_type->specific_id); adapt_type_id.has_value()) { if (context.types().GetConstantId(adapt_type_id) == name_scope_const_id) { // TODO: Should we be allowed to access protected fields of a type we // are adapting? The design doesn't allow this. return SemIR::AccessKind::Protected; } } } return SemIR::AccessKind::Public; } // Returns whether `scope` is a scope for which impl lookup should be performed // if we find an associated entity. static auto ScopeNeedsImplLookup(Context& context, SemIR::ConstantId name_scope_const_id) -> bool { SemIR::InstId inst_id = context.constant_values().GetInstId(name_scope_const_id); CARBON_CHECK(inst_id.has_value()); SemIR::Inst inst = context.insts().Get(inst_id); if (inst.Is()) { // Don't perform impl lookup if an associated entity is named as a member of // a facet type. return false; } if (inst.Is()) { // Don't perform impl lookup if an associated entity is named as a namespace // member. // TODO: This case is not yet listed in the design. return false; } // Any other kind of scope is assumed to be a type that implements the // interface containing the associated entity, and impl lookup is performed. return true; } static auto AccessMemberOfImplWitness( Context& context, SemIR::LocId loc_id, SemIR::InstId witness_id, SemIR::SpecificId interface_with_self_specific_id, SemIR::InstId member_id) -> SemIR::InstId { auto member_value_id = context.constant_values().GetConstantInstId(member_id); if (!member_value_id.has_value()) { if (member_value_id != SemIR::ErrorInst::InstId) { context.TODO(member_id, "non-constant associated entity"); } return SemIR::ErrorInst::InstId; } auto assoc_entity = context.insts().TryGetAs(member_value_id); if (!assoc_entity) { context.TODO(member_id, "unexpected value for associated entity"); return SemIR::ErrorInst::InstId; } // Substitute the interface specific and `Self` type into the type of the // associated entity to find the type of the member access. LoadImportRef(context, assoc_entity->decl_id); auto assoc_type_id = GetTypeForSpecificAssociatedEntity( context, interface_with_self_specific_id, assoc_entity->decl_id); return GetOrAddInst(context, loc_id, {.type_id = assoc_type_id, .witness_id = witness_id, .index = assoc_entity->index}); } // For an impl lookup query with a single interface in it, we can convert the // result to a single witness InstId. // // This CHECKs that the result (and thus the query) was a single interface. This // generally only makes sense in member access, where the lookup query's // interface is found through name lookup, and we don't have an arbitrary // `FacetType`. static auto GetWitnessFromSingleImplLookupResult( Context& context, SemIR::InstBlockIdOrError lookup_result) -> SemIR::InstId { auto witness_id = SemIR::InstId::None; if (lookup_result.has_error_value()) { witness_id = SemIR::ErrorInst::InstId; } else { auto witnesses = context.inst_blocks().Get(lookup_result.inst_block_id()); CARBON_CHECK(witnesses.size() == 1); witness_id = witnesses[0]; } return witness_id; } // Performs impl lookup for a member name expression. This finds the relevant // impl witness and extracts the corresponding impl member. static auto PerformImplLookup( Context& context, SemIR::LocId loc_id, SemIR::ConstantId type_const_id, SemIR::AssociatedEntityType assoc_type, SemIR::InstId member_id, bool diagnose = true, DiagnosticContextFn missing_impl_diagnostic_context = nullptr) -> SemIR::InstId { auto self_type_id = context.types().GetTypeIdForTypeConstantId(type_const_id); // TODO: Avoid forming and then immediately decomposing a `FacetType` here. auto interface_type_id = GetInterfaceType(context, assoc_type.interface_id, assoc_type.interface_without_self_specific_id); auto lookup_result = LookupImplWitness(context, loc_id, type_const_id, interface_type_id.AsConstantId()); if (!lookup_result.has_value()) { if (diagnose) { if (missing_impl_diagnostic_context) { Diagnostics::ContextScope scope(&context.emitter(), missing_impl_diagnostic_context); // TODO: Pass in the expression whose type we are printing. CARBON_DIAGNOSTIC(MissingImplInMemberAccessInContext, Error, "type {1} does not implement interface {0}", SemIR::TypeId, SemIR::TypeId); context.emitter().Emit(loc_id, MissingImplInMemberAccessInContext, interface_type_id, self_type_id); } else { // TODO: Pass in the expression whose type we are printing. CARBON_DIAGNOSTIC(MissingImplInMemberAccess, Error, "cannot access member of interface {0} in type {1} " "that does not implement that interface", SemIR::TypeId, SemIR::TypeId); context.emitter().Emit(loc_id, MissingImplInMemberAccess, interface_type_id, self_type_id); } } return SemIR::ErrorInst::InstId; } auto witness_id = GetWitnessFromSingleImplLookupResult(context, lookup_result); auto self_facet = GetConstantFacetValueForTypeAndInterface( context, context.types().GetTypeInstId(self_type_id), assoc_type.GetSpecificInterface(), witness_id); const auto& interface = context.interfaces().Get(assoc_type.interface_id); auto interface_with_self_specific_id = MakeSpecificWithInnerSelf( context, loc_id, interface.generic_id, interface.generic_with_self_id, assoc_type.interface_without_self_specific_id, self_facet); return AccessMemberOfImplWitness(context, loc_id, witness_id, interface_with_self_specific_id, member_id); } // Performs a member name lookup into the specified scope, including performing // impl lookup if necessary. If the scope result is `None`, assume an error has // already been diagnosed, and return `ErrorInst`. static auto LookupMemberNameInScope(Context& context, SemIR::LocId loc_id, SemIR::InstId base_id, SemIR::NameId name_id, SemIR::ConstantId name_scope_const_id, llvm::ArrayRef lookup_scopes, bool lookup_in_type_of_base, bool required) -> SemIR::InstId { AccessInfo access_info = { .constant_id = name_scope_const_id, .highest_allowed_access = GetHighestAllowedAccess(context, loc_id, name_scope_const_id), }; LookupResult result = LookupQualifiedName( context, loc_id, name_id, lookup_scopes, required, access_info); if (!result.scope_result.is_found()) { return SemIR::ErrorInst::InstId; } // TODO: This duplicates the work that HandleNameAsExpr does. Factor this out. auto type_id = SemIR::GetTypeOfInstInSpecific(context.sem_ir(), result.specific_id, result.scope_result.target_inst_id()); CARBON_CHECK(type_id.has_value(), "Missing type for member {0}", context.insts().Get(result.scope_result.target_inst_id())); // If the named entity has a constant value that depends on its specific, // store the specific too. if (result.specific_id.has_value() && context.constant_values() .Get(result.scope_result.target_inst_id()) .is_symbolic()) { result.scope_result = SemIR::ScopeLookupResult::MakeFound( GetOrAddInst( context, loc_id, {.type_id = type_id, .inst_id = result.scope_result.target_inst_id(), .specific_id = result.specific_id}), SemIR::AccessKind::Public); } // TODO: Use a different kind of instruction that also references the // `base_id` so that `SemIR` consumers can find it. auto member_id = GetOrAddInst( context, loc_id, {.type_id = type_id, .name_id = name_id, .value_id = result.scope_result.target_inst_id()}); // If member name lookup finds an associated entity name, and the scope is not // a facet type, perform impl lookup. // // TODO: We need to do this as part of searching extended scopes, because a // lookup that finds an associated entity and also finds the corresponding // impl member is not supposed to be treated as ambiguous. if (auto assoc_type = context.types().TryGetAs(type_id)) { if (lookup_in_type_of_base) { auto base_type_id = context.insts().Get(base_id).type_id(); member_id = PerformImplLookup(context, loc_id, context.types().GetConstantId(base_type_id), *assoc_type, member_id); } else if (ScopeNeedsImplLookup(context, name_scope_const_id)) { // Handles `T.F` where `T` is a type extending an interface containing // `F`. member_id = PerformImplLookup(context, loc_id, name_scope_const_id, *assoc_type, member_id); } } if (!context.rewrites_stack().empty()) { if (auto access = context.insts().TryGetAs(member_id)) { if (auto result = context.rewrites_stack().back().Lookup( context.constant_values().Get(member_id))) { return GetOrAddInst( context, loc_id, {.type_id = access->type_id, .impl_witness_access_id = member_id, .value_id = result.value()}); } } } return member_id; } // Performs the instance binding step in member access. If the found member is a // field, forms a class member access. If the found member is an instance // method, forms a bound method. Otherwise, the member is returned unchanged. static auto PerformInstanceBinding(Context& context, SemIR::LocId loc_id, SemIR::InstId base_id, SemIR::InstId member_id) -> SemIR::InstId { // If the member is a function, check whether it's an instance method. if (auto self_id = GetSelfIfInstanceMethod( context.sem_ir(), SemIR::GetCallee(context.sem_ir(), member_id))) { if (self_id->has_value()) { // Found an already-bound method. return member_id; } return GetOrAddInst( context, loc_id, {.type_id = GetSingletonType(context, SemIR::BoundMethodType::TypeInstId), .object_id = base_id, .function_decl_id = member_id}); } // Otherwise, if it's a field, form a class element access. if (auto unbound_element_type = context.types().TryGetAs( context.insts().Get(member_id).type_id())) { // Convert the base to the type of the element if necessary. base_id = ConvertToValueOrRefOfType( context, loc_id, base_id, context.types().GetTypeIdForTypeInstId( unbound_element_type->class_type_inst_id)); // Find the specified element, which could be either a field or a base // class, and build an element access expression. auto element_id = context.constant_values().GetConstantInstId(member_id); CARBON_CHECK(element_id.has_value(), "Non-constant value {0} of unbound element type", context.insts().Get(member_id)); auto index = GetClassElementIndex(context, element_id); auto access_id = GetOrAddInst( context, loc_id, {.type_id = context.types().GetTypeIdForTypeInstId( unbound_element_type->element_type_inst_id), .base_id = base_id, .index = index}); if (SemIR::GetExprCategory(context.sem_ir(), base_id) == SemIR::ExprCategory::Value && SemIR::GetExprCategory(context.sem_ir(), access_id) != SemIR::ExprCategory::Value) { // Class element access on a value expression produces an ephemeral // reference if the class's value representation is a pointer to the // object representation. Add a value acquisition in that case so that the // expression category of the result matches the expression category // of the base. access_id = ConvertToValueExpr(context, access_id); } return access_id; } // Not an instance member: no instance binding. return member_id; } // Validates that the index (required to be an IntValue) is valid within the // tuple size. Returns the index on success, or nullptr on failure. static auto ValidateTupleIndex(Context& context, SemIR::LocId loc_id, SemIR::InstId operand_inst_id, SemIR::IntValue index_inst, int size) -> std::optional { llvm::APInt index_val = context.ints().Get(index_inst.int_id); if (index_val.uge(size)) { CARBON_DIAGNOSTIC(TupleIndexOutOfBounds, Error, "tuple element index `{0}` is past the end of type {1}", TypedInt, TypeOfInstId); context.emitter().Emit(loc_id, TupleIndexOutOfBounds, {.type = index_inst.type_id, .value = index_val}, operand_inst_id); return std::nullopt; } return index_val; } auto PerformMemberAccess(Context& context, SemIR::LocId loc_id, SemIR::InstId base_id, SemIR::NameId name_id, bool required) -> SemIR::InstId { // TODO: Member access for dependent member names is supposed to perform a // lookup in both the template definition context and the template // instantiation context, and reject if both succeed but find different // things. if (required) { return HandleAction( context, loc_id, {.type_id = GetSingletonType(context, SemIR::InstType::TypeInstId), .base_id = base_id, .name_id = name_id}); } else { return HandleAction( context, loc_id, {.type_id = GetSingletonType(context, SemIR::InstType::TypeInstId), .base_id = base_id, .name_id = name_id}); } } // Common logic for `AccessMemberAction` and `AccessOptionalMemberAction`. static auto PerformActionHelper(Context& context, SemIR::LocId loc_id, SemIR::InstId base_id, SemIR::NameId name_id, bool required) -> SemIR::InstId { // Unwrap the facet value in `base_id` if possible. if (auto facet_value = TryGetCanonicalFacetValue(context, base_id); facet_value.has_value()) { base_id = facet_value; } // If the base is a name scope, such as a class or namespace, perform lookup // into that scope. if (auto base_const_id = context.constant_values().Get(base_id); base_const_id.is_constant()) { llvm::SmallVector lookup_scopes; if (AppendLookupScopesForConstant(context, loc_id, base_const_id, base_const_id, &lookup_scopes)) { return LookupMemberNameInScope( context, loc_id, base_id, name_id, base_const_id, lookup_scopes, /*lookup_in_type_of_base=*/false, required); } // If the base is a facet (a symbolic name scope), perform lookup into its // facet type. // // TODO: According to the design, this should just lookup directly in the // `base_id` (as part the class case above), as the `base_id` facet should // have member names that directly name members of the `impl`. auto base_type_id = context.insts().Get(base_id).type_id(); if (context.types().Is(base_type_id)) { // Name lookup into a facet requires the facet type to be complete, so // that any names available through the facet type are known for the // facet. // // TODO: This should be part of AppendLookupScopesForConstant when we do // lookup on the facet directly instead of the facet type. For now it's // here to provide a better diagnostic than what we get when looking for // scopes directly on the facet type. if (!RequireCompleteType( context, base_type_id, SemIR::LocId(base_id), [&](auto& builder) { CARBON_DIAGNOSTIC( IncompleteTypeInMemberAccessOfFacet, Context, "member access into facet of incomplete type {0}", SemIR::TypeId); builder.Context(base_id, IncompleteTypeInMemberAccessOfFacet, base_type_id); })) { // If the scope is invalid in AppendLookupScopesForConstant we still // return true and proceed with lookup, just ignoring that scope. // Match behaviour here for when this moves into // AppendLookupScopesForConstant. base_type_id = SemIR::ErrorInst::TypeId; } auto base_type_const_id = context.types().GetConstantId(base_type_id); llvm::SmallVector lookup_scopes; if (AppendLookupScopesForConstant(context, loc_id, base_type_const_id, base_const_id, &lookup_scopes)) { // The name scope constant needs to be a type, but is currently a // FacetType, so perform `as type` to get a FacetAccessType. auto base_as_type = ExprAsType(context, loc_id, base_id); base_type_const_id = context.types().GetConstantId(base_as_type.type_id); return LookupMemberNameInScope(context, loc_id, base_id, name_id, base_type_const_id, lookup_scopes, /*lookup_in_type_of_base=*/false, required); } } } // Otherwise, handle `x.F` by performing lookup into the type of `x` (where // `x` is `base_id`). auto base_type_id = context.insts().Get(base_id).type_id(); // Require a complete type explicitly. Materializing a temporary will too, but // we can produce a better diagnostic here with context about what operation // is being done (member access) that requires the complete type. // // TODO: ConvertToValueOrRefExpr could take context about the operation being // done to give a better error than "invalid use of" an incomplete type? if (!RequireCompleteType( context, base_type_id, SemIR::LocId(base_id), [&](auto& builder) { CARBON_DIAGNOSTIC( IncompleteTypeInMemberAccess, Context, "member access into object of incomplete type {0}", TypeOfInstId); builder.Context(base_id, IncompleteTypeInMemberAccess, base_id); })) { return SemIR::ErrorInst::InstId; } // Materialize a temporary for the base expression if necessary. base_id = ConvertToValueOrRefExpr(context, base_id); base_type_id = context.insts().Get(base_id).type_id(); auto lookup_const_id = context.types().GetConstantId(base_type_id); // TODO: If the type is a facet, we look through it into the facet's type (a // FacetType) for names. According to the design, we shouldn't need to do // this, as the facet should have member names that directly name members of // the `impl`. auto base_type_as_facet = GetCanonicalFacetOrTypeValue( context, context.types().GetTypeInstId(base_type_id)); auto base_type_facet_type_id = context.insts().Get(base_type_as_facet).type_id(); if (context.types().Is(base_type_facet_type_id)) { lookup_const_id = context.types().GetConstantId(base_type_facet_type_id); } // Perform lookup into the base type. llvm::SmallVector lookup_scopes; if (AppendLookupScopesForConstant( context, loc_id, lookup_const_id, // The `self_type_const_id` should be the type of `base_id` even if // it's a facet. // // TODO: This can be replaced with `lookup_const_id` once we stop // having to look through the facet at its type for the scope. context.types().GetConstantId(base_type_id), &lookup_scopes)) { auto member_id = LookupMemberNameInScope( context, loc_id, base_id, name_id, lookup_const_id, lookup_scopes, /*lookup_in_type_of_base=*/true, required); // Perform instance binding if we found an instance member. member_id = PerformInstanceBinding(context, loc_id, base_id, member_id); return member_id; } // The base type is not a name scope. Try some fallback options. if (auto struct_type = context.insts().TryGetAs( context.types().GetTypeInstId(base_type_id))) { // TODO: Do we need to optimize this with a lookup table for O(1)? for (auto [i, field] : llvm::enumerate( context.struct_type_fields().Get(struct_type->fields_id))) { if (name_id == field.name_id) { // TODO: Model this as producing a lookup result, and do instance // binding separately. Perhaps a struct type should be a name scope. return GetOrAddInst( context, loc_id, {.type_id = context.types().GetTypeIdForTypeInstId(field.type_inst_id), .struct_id = base_id, .index = SemIR::ElementIndex(i)}); } } if (required) { CARBON_DIAGNOSTIC(QualifiedExprNameNotFound, Error, "type {0} does not have a member `{1}`", TypeOfInstId, SemIR::NameId); context.emitter().Emit(loc_id, QualifiedExprNameNotFound, base_id, name_id); return SemIR::ErrorInst::InstId; } else { return SemIR::InstId::None; } } if (base_type_id != SemIR::ErrorInst::TypeId) { CARBON_DIAGNOSTIC(QualifiedExprUnsupported, Error, "type {0} does not support qualified expressions", TypeOfInstId); context.emitter().Emit(loc_id, QualifiedExprUnsupported, base_id); } return SemIR::ErrorInst::InstId; } auto PerformAction(Context& context, SemIR::LocId loc_id, SemIR::AccessMemberAction action) -> SemIR::InstId { return PerformActionHelper(context, loc_id, action.base_id, action.name_id, /*required=*/true); } auto PerformAction(Context& context, SemIR::LocId loc_id, SemIR::AccessOptionalMemberAction action) -> SemIR::InstId { return PerformActionHelper(context, loc_id, action.base_id, action.name_id, /*required=*/false); } // Logic shared by GetAssociatedValue() and PerformCompoundMemberAccess(). static auto GetAssociatedValueImpl(Context& context, SemIR::LocId loc_id, SemIR::InstId base_id, const SemIR::AssociatedEntity& assoc_entity, SemIR::SpecificInterface specific_interface) -> SemIR::InstId { // Convert to the interface type of the associated member, to get a facet // value. auto interface_type_id = GetInterfaceType( context, specific_interface.interface_id, specific_interface.specific_id); auto self_facet_inst_id = ConvertToValueOfType(context, loc_id, base_id, interface_type_id); if (self_facet_inst_id == SemIR::ErrorInst::InstId) { return SemIR::ErrorInst::InstId; } auto self_facet_const_id = context.constant_values().Get(self_facet_inst_id); // TODO: We should be able to lookup constant associated values from runtime // facet values by using their FacetType only, but we assume constant values // for impl lookup at the moment. if (!self_facet_const_id.is_constant()) { context.TODO(loc_id, "associated value lookup on runtime facet value"); return SemIR::ErrorInst::InstId; } // TODO: If `ConvertToValueOfType` returned a `FacetValue`, we already got a // witness for this interface there. We don't need to do both a // ConvertToValueOfType and LookupImplWitness, that is redundant. Since we // want to do LookupImplWitness unconditionally (eg. if `base_id` has exactly // the right FacetType already), can we drop the ConvertToValueOfType step? auto lookup_result = LookupImplWitness( context, loc_id, self_facet_const_id, EvalOrAddInst( context, loc_id, FacetTypeFromInterface(context, specific_interface.interface_id, specific_interface.specific_id))); CARBON_CHECK(lookup_result.has_value()); auto witness_id = GetWitnessFromSingleImplLookupResult(context, lookup_result); const auto& interface = context.interfaces().Get(specific_interface.interface_id); auto interface_with_self_specific_id = MakeSpecificWithInnerSelf( context, loc_id, interface.generic_id, interface.generic_with_self_id, specific_interface.specific_id, self_facet_const_id); // Before we can access the element of the witness, we need to figure out // the type of that element. It depends on the self type and the specific // interface. auto assoc_type_id = GetTypeForSpecificAssociatedEntity( context, interface_with_self_specific_id, assoc_entity.decl_id); // Now that we have the witness, an index into it, and the type of the // result, return the element of the witness. return GetOrAddInst(context, loc_id, {.type_id = assoc_type_id, .witness_id = witness_id, .index = assoc_entity.index}); } auto GetAssociatedValue(Context& context, SemIR::LocId loc_id, SemIR::InstId base_id, SemIR::ConstantId assoc_entity_const_id, SemIR::SpecificInterface specific_interface) -> SemIR::InstId { // TODO: This function shares a code with PerformCompoundMemberAccess(), // it would be nice to reduce the duplication. auto value_inst_id = context.constant_values().GetInstId(assoc_entity_const_id); auto assoc_entity = context.insts().GetAs(value_inst_id); auto decl_id = assoc_entity.decl_id; LoadImportRef(context, decl_id); return GetAssociatedValueImpl(context, loc_id, base_id, assoc_entity, specific_interface); } auto PerformCompoundMemberAccess( Context& context, SemIR::LocId loc_id, SemIR::InstId base_id, SemIR::InstId member_expr_id, bool diagnose, DiagnosticContextFn missing_impl_diagnostic_context) -> SemIR::InstId { auto base_type_id = context.insts().Get(base_id).type_id(); auto base_type_const_id = context.types().GetConstantId(base_type_id); auto member_id = member_expr_id; auto member = context.insts().Get(member_id); // If the member expression names an associated entity, impl lookup is always // performed using the type of the base expression. if (auto assoc_type = context.types().TryGetAs( member.type_id())) { // Step 1: figure out the type of the associated entity from the interface. auto value_inst_id = context.constant_values().GetConstantInstId(member_id); // TODO: According to // https://docs.carbon-lang.dev/docs/design/expressions/member_access.html#member-resolution // > For a compound member access, the second operand is evaluated as a // > compile-time constant to determine the member being accessed. The // > evaluation is required to succeed [...] if (!value_inst_id.has_value()) { context.TODO(loc_id, "Non-constant associated entity value"); return SemIR::ErrorInst::InstId; } auto assoc_entity = context.insts().GetAs(value_inst_id); auto decl_id = assoc_entity.decl_id; LoadImportRef(context, decl_id); auto decl_value_id = context.constant_values().GetConstantInstId(decl_id); auto decl_type_id = context.insts().Get(decl_value_id).type_id(); if (IsInstanceType(context, decl_type_id)) { // Step 2a: For instance methods, lookup the impl of the interface for // this type and get the method. member_id = PerformImplLookup(context, loc_id, base_type_const_id, *assoc_type, member_id, diagnose, missing_impl_diagnostic_context); // Next we will perform instance binding. } else { // Step 2b: For non-instance methods and associated constants, we access // the value of the associated constant, and don't do any instance // binding. return GetAssociatedValueImpl(context, loc_id, base_id, assoc_entity, assoc_type->GetSpecificInterface()); } } // Perform instance binding if we found an instance member. member_id = PerformInstanceBinding(context, loc_id, base_id, member_id); // If we didn't perform impl lookup or instance binding, that's an error // because the base expression is not used for anything. if (member_id == member_expr_id && member.type_id() != SemIR::ErrorInst::TypeId) { // As a special case, an integer-valued expression can be used as a member // name when indexing a tuple. if (context.insts().Is( context.constant_values().GetInstId(base_type_const_id))) { return PerformTupleAccess(context, loc_id, base_id, member_expr_id); } CARBON_DIAGNOSTIC(CompoundMemberAccessDoesNotUseBase, Error, "member name of type {0} in compound member access is " "not an instance member or an interface member", TypeOfInstId); context.emitter().Emit(loc_id, CompoundMemberAccessDoesNotUseBase, member_id); } return member_id; } auto PerformTupleAccess(Context& context, SemIR::LocId loc_id, SemIR::InstId tuple_inst_id, SemIR::InstId index_inst_id) -> SemIR::InstId { tuple_inst_id = ConvertToValueOrRefExpr(context, tuple_inst_id); auto tuple_type_id = context.insts().Get(tuple_inst_id).type_id(); auto tuple_type = context.types().TryGetAs(tuple_type_id); if (!tuple_type) { CARBON_DIAGNOSTIC(TupleIndexOnANonTupleType, Error, "type {0} does not support tuple indexing; only " "tuples can be indexed that way", TypeOfInstId); context.emitter().Emit(loc_id, TupleIndexOnANonTupleType, tuple_inst_id); return SemIR::ErrorInst::InstId; } auto diag_non_constant_index = [&] { // TODO: Decide what to do if the index is a symbolic constant. CARBON_DIAGNOSTIC(TupleIndexNotConstant, Error, "tuple index must be a constant"); context.emitter().Emit(loc_id, TupleIndexNotConstant); return SemIR::ErrorInst::InstId; }; // Diagnose a non-constant index prior to conversion to IntLiteral, because // the conversion will fail if the index is not constant. if (!context.constant_values().Get(index_inst_id).is_concrete()) { return diag_non_constant_index(); } SemIR::TypeId element_type_id = SemIR::ErrorInst::TypeId; index_inst_id = ConvertToValueOfType( context, SemIR::LocId(index_inst_id), index_inst_id, GetSingletonType(context, SemIR::IntLiteralType::TypeInstId)); auto index_const_id = context.constant_values().Get(index_inst_id); if (index_const_id == SemIR::ErrorInst::ConstantId) { return SemIR::ErrorInst::InstId; } else if (!index_const_id.is_concrete()) { return diag_non_constant_index(); } auto index_literal = context.insts().GetAs( context.constant_values().GetInstId(index_const_id)); auto type_block = context.inst_blocks().Get(tuple_type->type_elements_id); std::optional index_val = ValidateTupleIndex( context, loc_id, tuple_inst_id, index_literal, type_block.size()); if (!index_val) { return SemIR::ErrorInst::InstId; } // TODO: Handle the case when `index_val->getZExtValue()` has too many bits. element_type_id = context.types().GetTypeIdForTypeInstId( type_block[index_val->getZExtValue()]); auto tuple_index = SemIR::ElementIndex(index_val->getZExtValue()); return GetOrAddInst(context, loc_id, {.type_id = element_type_id, .tuple_id = tuple_inst_id, .index = tuple_index}); } } // namespace Carbon::Check ================================================ FILE: toolchain/check/member_access.h ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #ifndef CARBON_TOOLCHAIN_CHECK_MEMBER_ACCESS_H_ #define CARBON_TOOLCHAIN_CHECK_MEMBER_ACCESS_H_ #include "toolchain/check/context.h" #include "toolchain/sem_ir/ids.h" namespace Carbon::Check { // Returns the highest allowed access for members of `name_scope_const_id`. For // example, if this returns `Protected` then only `Public` and `Protected` // accesses are allowed -- not `Private`. auto GetHighestAllowedAccess(Context& context, SemIR::LocId loc_id, SemIR::ConstantId name_scope_const_id) -> SemIR::AccessKind; // Creates SemIR to perform a member access with base expression `base_id` and // member name `name_id`. When `required`, failing to find the name is a // diagnosed error; otherwise, `None` is returned. Returns the result of the // access. auto PerformMemberAccess(Context& context, SemIR::LocId loc_id, SemIR::InstId base_id, SemIR::NameId name_id, bool required = true) -> SemIR::InstId; // Creates SemIR to perform a compound member access with base expression // `base_id` and member name expression `member_expr_id`. Returns the result of // the access. If specified, `missing_impl_diagnostic_context()` is used to // provide context for the error diagnostic when impl binding fails due to a // missing `impl`. // // On failure, an ErrorInst is returned and a diagnostic is produced unless // `diagnose` is false. It is incorrect to specify `diagnose` as false if the // resulting ErrorInst may appear in the produced SemIR. auto PerformCompoundMemberAccess( Context& context, SemIR::LocId loc_id, SemIR::InstId base_id, SemIR::InstId member_expr_id, bool diagnose = true, DiagnosticContextFn missing_impl_diagnostic_context = nullptr) -> SemIR::InstId; // Finds the value of an associated entity (given by assoc_entity_inst_id, a // member of the interface given by interface_type_id) associated with a type or // facet (given by base_id). Never does instance binding. auto GetAssociatedValue(Context& context, SemIR::LocId loc_id, SemIR::InstId base_id, SemIR::ConstantId assoc_entity_const_id, SemIR::SpecificInterface interface) -> SemIR::InstId; // Creates SemIR to perform a tuple index with base expression `tuple_inst_id` // and index expression `index_inst_id`. Returns the result of the access. auto PerformTupleAccess(Context& context, SemIR::LocId loc_id, SemIR::InstId tuple_inst_id, SemIR::InstId index_inst_id) -> SemIR::InstId; } // namespace Carbon::Check #endif // CARBON_TOOLCHAIN_CHECK_MEMBER_ACCESS_H_ ================================================ FILE: toolchain/check/merge.cpp ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #include "toolchain/check/merge.h" #include "toolchain/base/kind_switch.h" #include "toolchain/check/import.h" #include "toolchain/check/import_ref.h" #include "toolchain/diagnostics/format_providers.h" #include "toolchain/sem_ir/ids.h" #include "toolchain/sem_ir/typed_insts.h" namespace Carbon::Check { CARBON_DIAGNOSTIC(RedeclPrevDecl, Note, "previously declared here"); // Diagnoses a redeclaration which is redundant. static auto DiagnoseRedundant(Context& context, Lex::TokenKind decl_kind, SemIR::NameId name_id, SemIR::LocId new_loc_id, SemIR::LocId prev_loc_id) -> void { CARBON_DIAGNOSTIC(RedeclRedundant, Error, "redeclaration of `{0} {1}` is redundant", Lex::TokenKind, SemIR::NameId); context.emitter() .Build(new_loc_id, RedeclRedundant, decl_kind, name_id) .Note(prev_loc_id, RedeclPrevDecl) .Emit(); } // Diagnoses a redefinition. static auto DiagnoseRedef(Context& context, Lex::TokenKind decl_kind, SemIR::NameId name_id, SemIR::LocId new_loc_id, SemIR::LocId prev_loc_id) -> void { CARBON_DIAGNOSTIC(RedeclRedef, Error, "redefinition of `{0} {1}`", Lex::TokenKind, SemIR::NameId); CARBON_DIAGNOSTIC(RedeclPrevDef, Note, "previously defined here"); context.emitter() .Build(new_loc_id, RedeclRedef, decl_kind, name_id) .Note(prev_loc_id, RedeclPrevDef) .Emit(); } // Diagnoses an `extern` versus non-`extern` mismatch. static auto DiagnoseExternMismatch(Context& context, Lex::TokenKind decl_kind, SemIR::NameId name_id, SemIR::LocId new_loc_id, SemIR::LocId prev_loc_id) -> void { CARBON_DIAGNOSTIC(RedeclExternMismatch, Error, "redeclarations of `{0} {1}` must match use of `extern`", Lex::TokenKind, SemIR::NameId); context.emitter() .Build(new_loc_id, RedeclExternMismatch, decl_kind, name_id) .Note(prev_loc_id, RedeclPrevDecl) .Emit(); } // Diagnoses `extern library` declared in a library importing the owned entity. static auto DiagnoseExternLibraryInImporter(Context& context, Lex::TokenKind decl_kind, SemIR::NameId name_id, SemIR::LocId new_loc_id, SemIR::LocId prev_loc_id) -> void { CARBON_DIAGNOSTIC(ExternLibraryInImporter, Error, "cannot declare imported `{0} {1}` as `extern library`", Lex::TokenKind, SemIR::NameId); context.emitter() .Build(new_loc_id, ExternLibraryInImporter, decl_kind, name_id) .Note(prev_loc_id, RedeclPrevDecl) .Emit(); } // Diagnoses `extern library` pointing to the wrong library. static auto DiagnoseExternLibraryIncorrect(Context& context, SemIR::LocId new_loc_id, SemIR::LocId prev_loc_id) -> void { CARBON_DIAGNOSTIC( ExternLibraryIncorrect, Error, "declaration in {0} doesn't match `extern library` declaration", SemIR::LibraryNameId); CARBON_DIAGNOSTIC(ExternLibraryExpected, Note, "previously declared with `extern library` here"); context.emitter() .Build(new_loc_id, ExternLibraryIncorrect, context.sem_ir().library_id()) .Note(prev_loc_id, ExternLibraryExpected) .Emit(); } auto DiagnoseExternRequiresDeclInApiFile(Context& context, SemIR::LocId loc_id) -> void { CARBON_DIAGNOSTIC( ExternRequiresDeclInApiFile, Error, "`extern` entities must have a declaration in the API file"); context.emitter().Emit(loc_id, ExternRequiresDeclInApiFile); } auto DiagnoseIfInvalidRedecl(Context& context, Lex::TokenKind decl_kind, SemIR::NameId name_id, RedeclInfo new_decl, RedeclInfo prev_decl, SemIR::ImportIRId import_ir_id) -> void { if (!import_ir_id.has_value()) { // Check for disallowed redeclarations in the same file. if (!new_decl.is_definition) { DiagnoseRedundant(context, decl_kind, name_id, new_decl.loc_id, prev_decl.loc_id); return; } if (prev_decl.is_definition) { DiagnoseRedef(context, decl_kind, name_id, new_decl.loc_id, prev_decl.loc_id); return; } if (prev_decl.is_extern != new_decl.is_extern) { DiagnoseExternMismatch(context, decl_kind, name_id, new_decl.loc_id, prev_decl.loc_id); return; } return; } if (import_ir_id == SemIR::ImportIRId::ApiForImpl) { // Check for disallowed redeclarations in the same library. Note that a // forward declaration in the impl is allowed. if (prev_decl.is_definition) { if (new_decl.is_definition) { DiagnoseRedef(context, decl_kind, name_id, new_decl.loc_id, prev_decl.loc_id); } else { DiagnoseRedundant(context, decl_kind, name_id, new_decl.loc_id, prev_decl.loc_id); } return; } if (prev_decl.is_extern != new_decl.is_extern) { DiagnoseExternMismatch(context, decl_kind, name_id, new_decl.loc_id, prev_decl.loc_id); return; } return; } // Check for disallowed redeclarations cross-library. if (new_decl.is_extern && context.sem_ir().is_impl()) { // We continue after issuing the "missing API declaration" diagnostic, // because it may still be helpful to note other issues with the // declarations. DiagnoseExternRequiresDeclInApiFile(context, new_decl.loc_id); } if (prev_decl.is_extern != new_decl.is_extern) { DiagnoseExternMismatch(context, decl_kind, name_id, new_decl.loc_id, prev_decl.loc_id); return; } if (!prev_decl.extern_library_id.has_value()) { if (new_decl.extern_library_id.has_value()) { DiagnoseExternLibraryInImporter(context, decl_kind, name_id, new_decl.loc_id, prev_decl.loc_id); } else { DiagnoseRedundant(context, decl_kind, name_id, new_decl.loc_id, prev_decl.loc_id); } return; } if (prev_decl.extern_library_id != SemIR::LibraryNameId::Error && prev_decl.extern_library_id != context.sem_ir().library_id()) { DiagnoseExternLibraryIncorrect(context, new_decl.loc_id, prev_decl.loc_id); return; } } auto ReplacePrevInstForMerge(Context& context, SemIR::NameScopeId scope_id, SemIR::NameId name_id, SemIR::InstId new_inst_id) -> void { auto& scope = context.name_scopes().Get(scope_id); auto entry_id = scope.Lookup(name_id); if (entry_id) { auto& result = scope.GetEntry(*entry_id).result; result = SemIR::ScopeLookupResult::MakeWrappedLookupResult( new_inst_id, result.access_kind()); } } // Returns true if there was an error in declaring the entity, which will have // previously been diagnosed. static auto EntityHasParamError(Context& context, const DeclParams& info) -> bool { for (auto param_patterns_id : {info.implicit_param_patterns_id, info.param_patterns_id}) { if (param_patterns_id.has_value() && param_patterns_id != SemIR::InstBlockId::Empty) { for (auto param_id : context.inst_blocks().Get(param_patterns_id)) { if (context.insts().Get(param_id).type_id() == SemIR::ErrorInst::TypeId) { return true; } } } } return false; } // Returns false if a param differs for a redeclaration. The caller is expected // to provide a diagnostic. static auto CheckRedeclParam(Context& context, bool is_implicit_param, int32_t param_index, SemIR::InstId orig_new_param_pattern_id, SemIR::InstId orig_prev_param_pattern_id, SemIR::SpecificId prev_specific_id, bool diagnose, bool check_syntax, bool check_self) -> bool { // TODO: Consider differentiating between type and name mistakes. For now, // taking the simpler approach because I also think we may want to refactor // params. CARBON_DIAGNOSTIC( RedeclParamPrevious, Note, "previous declaration's corresponding {0:implicit |}parameter here", Diagnostics::BoolAsSelect); auto emit_diagnostic = [&]() { if (!diagnose) { return; } CARBON_DIAGNOSTIC(RedeclParamDiffers, Error, "redeclaration differs at {0:implicit |}parameter {1}", Diagnostics::BoolAsSelect, int32_t); context.emitter() .Build(orig_new_param_pattern_id, RedeclParamDiffers, is_implicit_param, param_index + 1) .Note(orig_prev_param_pattern_id, RedeclParamPrevious, is_implicit_param) .Emit(); }; struct PatternPair { SemIR::InstId prev_id; SemIR::InstId new_id; }; llvm::SmallVector pattern_stack; pattern_stack.push_back({.prev_id = orig_prev_param_pattern_id, .new_id = orig_new_param_pattern_id}); do { auto patterns = pattern_stack.pop_back_val(); auto new_param_pattern = context.insts().Get(patterns.new_id); auto prev_param_pattern = context.insts().Get(patterns.prev_id); if (new_param_pattern.kind() != prev_param_pattern.kind()) { emit_diagnostic(); return false; } CARBON_KIND_SWITCH(new_param_pattern) { case CARBON_KIND_ANY(SemIR::AnyParamPattern, new_any_param_pattern): { auto prev_any_param_pattern = prev_param_pattern.As(); pattern_stack.push_back( {.prev_id = prev_any_param_pattern.subpattern_id, .new_id = new_any_param_pattern.subpattern_id}); break; } case CARBON_KIND(SemIR::VarPattern new_var_pattern): { auto prev_var_pattern = prev_param_pattern.As(); pattern_stack.push_back({.prev_id = prev_var_pattern.subpattern_id, .new_id = new_var_pattern.subpattern_id}); break; } case CARBON_KIND_ANY(SemIR::AnyBindingPattern, new_any_binding_pattern): { auto prev_any_binding_pattern = prev_param_pattern.As(); auto new_name_id = context.entity_names() .Get(new_any_binding_pattern.entity_name_id) .name_id; auto prev_name_id = context.entity_names() .Get(prev_any_binding_pattern.entity_name_id) .name_id; if (!check_self && new_name_id == SemIR::NameId::SelfValue && prev_name_id == SemIR::NameId::SelfValue) { break; } auto prev_param_type_id = SemIR::GetTypeOfInstInSpecific( context.sem_ir(), prev_specific_id, patterns.prev_id); if (!context.types().AreEqualAcrossDeclarations( new_param_pattern.type_id(), prev_param_type_id)) { if (!diagnose) { return false; } CARBON_DIAGNOSTIC( RedeclParamDiffersType, Error, "type {3} of {0:implicit |}parameter {1} in " "redeclaration differs from previous parameter type {2}", Diagnostics::BoolAsSelect, int32_t, SemIR::TypeId, SemIR::TypeId); context.emitter() .Build(orig_new_param_pattern_id, RedeclParamDiffersType, is_implicit_param, param_index + 1, prev_param_type_id, new_param_pattern.type_id()) .Note(orig_prev_param_pattern_id, RedeclParamPrevious, is_implicit_param) .Emit(); return false; } if (check_syntax && new_name_id != prev_name_id) { emit_diagnostic(); return false; } break; } default: { CARBON_FATAL("Unexpected inst kind in parameter pattern: {0}", new_param_pattern.kind()); } } } while (!pattern_stack.empty()); return true; } // Returns false if the param refs differ for a redeclaration. static auto CheckRedeclParams(Context& context, SemIR::LocId new_decl_loc_id, SemIR::InstBlockId new_param_patterns_id, SemIR::LocId prev_decl_loc_id, SemIR::InstBlockId prev_param_patterns_id, bool is_implicit_param, SemIR::SpecificId prev_specific_id, bool diagnose, bool check_syntax, bool check_self) -> bool { // This will often occur for empty params. if (new_param_patterns_id == prev_param_patterns_id) { return true; } // If exactly one of the parameter lists was present, they differ. if (new_param_patterns_id.has_value() != prev_param_patterns_id.has_value()) { if (!diagnose) { return false; } CARBON_DIAGNOSTIC(RedeclParamListDiffers, Error, "redeclaration differs because of " "{1:|missing }{0:implicit |}parameter list", Diagnostics::BoolAsSelect, Diagnostics::BoolAsSelect); CARBON_DIAGNOSTIC(RedeclParamListPrevious, Note, "previously declared " "{1:with|without} {0:implicit |}parameter list", Diagnostics::BoolAsSelect, Diagnostics::BoolAsSelect); context.emitter() .Build(new_decl_loc_id, RedeclParamListDiffers, is_implicit_param, new_param_patterns_id.has_value()) .Note(prev_decl_loc_id, RedeclParamListPrevious, is_implicit_param, prev_param_patterns_id.has_value()) .Emit(); return false; } CARBON_CHECK(new_param_patterns_id.has_value() && prev_param_patterns_id.has_value()); const auto new_param_pattern_ids = context.inst_blocks().Get(new_param_patterns_id); const auto prev_param_pattern_ids = context.inst_blocks().Get(prev_param_patterns_id); if (new_param_pattern_ids.size() != prev_param_pattern_ids.size()) { if (!diagnose) { return false; } CARBON_DIAGNOSTIC( RedeclParamCountDiffers, Error, "redeclaration differs because of {0:implicit |}parameter count of {1}", Diagnostics::BoolAsSelect, int32_t); CARBON_DIAGNOSTIC( RedeclParamCountPrevious, Note, "previously declared with {0:implicit |}parameter count of {1}", Diagnostics::BoolAsSelect, int32_t); context.emitter() .Build(new_decl_loc_id, RedeclParamCountDiffers, is_implicit_param, new_param_pattern_ids.size()) .Note(prev_decl_loc_id, RedeclParamCountPrevious, is_implicit_param, prev_param_pattern_ids.size()) .Emit(); return false; } for (auto [index, new_param_pattern_id, prev_param_pattern_id] : llvm::enumerate(new_param_pattern_ids, prev_param_pattern_ids)) { if (!CheckRedeclParam(context, is_implicit_param, index, new_param_pattern_id, prev_param_pattern_id, prev_specific_id, diagnose, check_syntax, check_self)) { return false; } } return true; } // Returns true if the two nodes represent the same syntax. // TODO: Detect raw identifiers (will require token changes). static auto IsNodeSyntaxEqual(Context& context, Parse::NodeId new_node_id, Parse::NodeId prev_node_id) -> bool { if (context.parse_tree().node_kind(new_node_id) != context.parse_tree().node_kind(prev_node_id)) { return false; } // TODO: Should there be a trivial way to check if we need to check spellings? // Identifiers and literals need their text checked for cross-file matching, // but not intra-file. Keywords and operators shouldn't need the token text // examined at all. auto new_spelling = context.tokens().GetTokenText( context.parse_tree().node_token(new_node_id)); auto prev_spelling = context.tokens().GetTokenText( context.parse_tree().node_token(prev_node_id)); return new_spelling == prev_spelling; } // Returns false if redeclaration parameter syntax doesn't match. static auto CheckRedeclParamSyntax(Context& context, Parse::NodeId new_first_param_node_id, Parse::NodeId new_last_param_node_id, Parse::NodeId prev_first_param_node_id, Parse::NodeId prev_last_param_node_id, bool diagnose) -> bool { // Parse nodes may not always be available to compare. // TODO: Support cross-file syntax checks. Right now imports provide // `NodeId::None`, and we'll need to follow the declaration to its original // file to get the parse tree. if (!new_first_param_node_id.has_value() || !prev_first_param_node_id.has_value()) { return true; } CARBON_CHECK(new_last_param_node_id.has_value(), "new_last_param_node_id.has_value should match " "new_first_param_node_id.has_value"); CARBON_CHECK(prev_last_param_node_id.has_value(), "prev_last_param_node_id.has_value should match " "prev_first_param_node_id.has_value"); Parse::Tree::PostorderIterator new_iter(new_first_param_node_id); Parse::Tree::PostorderIterator new_end(new_last_param_node_id); Parse::Tree::PostorderIterator prev_iter(prev_first_param_node_id); Parse::Tree::PostorderIterator prev_end(prev_last_param_node_id); // Done when one past the last node to check. ++new_end; ++prev_end; // Compare up to the shortest length. for (; new_iter != new_end && prev_iter != prev_end; ++new_iter, ++prev_iter) { auto new_node_id = *new_iter; auto new_node_kind = context.parse_tree().node_kind(new_node_id); // Skip over "unused" markers. if (new_node_kind == Parse::NodeKind::UnusedPattern) { ++new_iter; new_node_id = *new_iter; new_node_kind = context.parse_tree().node_kind(new_node_id); } auto prev_node_id = *prev_iter; auto prev_node_kind = context.parse_tree().node_kind(prev_node_id); if (prev_node_kind == Parse::NodeKind::UnusedPattern) { ++prev_iter; prev_node_id = *prev_iter; prev_node_kind = context.parse_tree().node_kind(prev_node_id); } if (!IsNodeSyntaxEqual(context, new_node_id, prev_node_id)) { // Skip difference if it is `Self as` vs. `as` in an `impl` declaration. // https://github.com/carbon-language/carbon-lang/blob/trunk/proposals/p3763.md#redeclarations if (new_node_kind == Parse::NodeKind::ImplDefaultSelfAs && prev_node_kind == Parse::NodeKind::SelfTypeNameExpr && context.parse_tree().node_kind(prev_iter[1]) == Parse::NodeKind::ImplTypeAs) { ++prev_iter; continue; } if (prev_node_kind == Parse::NodeKind::ImplDefaultSelfAs && new_node_kind == Parse::NodeKind::SelfTypeNameExpr && context.parse_tree().node_kind(new_iter[1]) == Parse::NodeKind::ImplTypeAs) { ++new_iter; continue; } if (!diagnose) { return false; } CARBON_DIAGNOSTIC(RedeclParamSyntaxDiffers, Error, "redeclaration syntax differs here"); CARBON_DIAGNOSTIC(RedeclParamSyntaxPrevious, Note, "comparing with previous declaration here"); context.emitter() .Build(new_node_id, RedeclParamSyntaxDiffers) .Note(prev_node_id, RedeclParamSyntaxPrevious) .Emit(); return false; } } // The prefixes are the same, but the lengths may still be different. This is // only relevant for `impl` declarations where the final bracketing node is // not included in the range of nodes being compared, and in those cases // `diagnose` is false. if (new_iter != new_end) { CARBON_CHECK(!diagnose); return false; } else if (prev_iter != prev_end) { CARBON_CHECK(!diagnose); return false; } return true; } auto CheckRedeclParamsMatch(Context& context, const DeclParams& new_entity, const DeclParams& prev_entity, SemIR::SpecificId prev_specific_id, bool diagnose, bool check_syntax, bool check_self) -> bool { if (EntityHasParamError(context, new_entity) || EntityHasParamError(context, prev_entity)) { return false; } if (!CheckRedeclParams( context, new_entity.loc_id, new_entity.implicit_param_patterns_id, prev_entity.loc_id, prev_entity.implicit_param_patterns_id, /*is_implicit_param=*/true, prev_specific_id, diagnose, check_syntax, check_self)) { return false; } // Don't forward `check_self` here because it's extra cost, and `self` is only // allowed in implicit params. if (!CheckRedeclParams(context, new_entity.loc_id, new_entity.param_patterns_id, prev_entity.loc_id, prev_entity.param_patterns_id, /*is_implicit_param=*/false, prev_specific_id, diagnose, check_syntax, /*check_self=*/true)) { return false; } if (check_syntax && !CheckRedeclParamSyntax(context, new_entity.first_param_node_id, new_entity.last_param_node_id, prev_entity.first_param_node_id, prev_entity.last_param_node_id, diagnose)) { return false; } return true; } } // namespace Carbon::Check ================================================ FILE: toolchain/check/merge.h ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #ifndef CARBON_TOOLCHAIN_CHECK_MERGE_H_ #define CARBON_TOOLCHAIN_CHECK_MERGE_H_ #include "toolchain/check/context.h" #include "toolchain/check/subst.h" #include "toolchain/sem_ir/ids.h" namespace Carbon::Check { // Diagnoses an `extern` declaration that was not preceded by a declaration in // the API file. auto DiagnoseExternRequiresDeclInApiFile(Context& context, SemIR::LocId loc_id) -> void; // Information on new and previous declarations for DiagnoseIfInvalidRedecl. struct RedeclInfo { explicit RedeclInfo(const SemIR::EntityWithParamsBase& params, SemIR::LocId loc_id, bool is_definition) : loc_id(loc_id), is_definition(is_definition), is_extern(params.is_extern), extern_library_id(params.extern_library_id) {} // The associated diagnostic location. SemIR::LocId loc_id; // True if a definition. bool is_definition; // True if an `extern` declaration. bool is_extern; // The library name in `extern library`, or `None` if not present. SemIR::LibraryNameId extern_library_id; }; // Checks for various invalid redeclarations. This can emit diagnostics. // However, merging is still often appropriate for error recovery, so this // doesn't return whether a diagnostic occurred. // // The kinds of things this verifies are: // - A declaration is not redundant. // - A definition doesn't redefine a prior definition. // - The use of `extern` is consistent within a library. // - Multiple libraries do not declare non-`extern`. auto DiagnoseIfInvalidRedecl(Context& context, Lex::TokenKind decl_kind, SemIR::NameId name_id, RedeclInfo new_decl, RedeclInfo prev_decl, SemIR::ImportIRId prev_import_ir_id) -> void; // When the prior name lookup result is an import and we are successfully // merging, replace the name lookup result with the reference in the current // file. auto ReplacePrevInstForMerge(Context& context, SemIR::NameScopeId scope_id, SemIR::NameId name_id, SemIR::InstId new_inst_id) -> void; // Information about the parameters of a declaration, which is common across // different kinds of entity such as classes and functions. struct DeclParams { explicit DeclParams(const SemIR::EntityWithParamsBase& base) : loc_id(base.latest_decl_id()), first_param_node_id(base.first_param_node_id), last_param_node_id(base.last_param_node_id), implicit_param_patterns_id(base.implicit_param_patterns_id), param_patterns_id(base.param_patterns_id) {} DeclParams(SemIR::LocId loc_id, Parse::NodeId first_param_node_id, Parse::NodeId last_param_node_id, SemIR::InstBlockId implicit_param_patterns_id, SemIR::InstBlockId param_patterns_id) : loc_id(loc_id), first_param_node_id(first_param_node_id), last_param_node_id(last_param_node_id), implicit_param_patterns_id(implicit_param_patterns_id), param_patterns_id(param_patterns_id) {} // The location of the declaration of the entity. SemIR::LocId loc_id; // Parse tree bounds for the parameters, including both implicit and explicit // parameters. These will be compared to match between declaration and // definition. Parse::NodeId first_param_node_id; Parse::NodeId last_param_node_id; // The implicit parameters of the entity. Can be `None` if there is no // implicit parameter list. SemIR::InstBlockId implicit_param_patterns_id; // The explicit parameters of the entity. Can be `None` if there is no // explicit parameter list. SemIR::InstBlockId param_patterns_id; }; // Checks that the parameters in a redeclaration of an entity match the // parameters in the prior declaration. If not, produces a diagnostic if // `diagnose` is true, and returns false. auto CheckRedeclParamsMatch(Context& context, const DeclParams& new_entity, const DeclParams& prev_entity, SemIR::SpecificId prev_specific_id, bool diagnose, bool check_syntax, bool check_self) -> bool; inline auto CheckRedeclParamsMatch(Context& context, const DeclParams& new_entity, const DeclParams& prev_entity) -> bool { return CheckRedeclParamsMatch(context, new_entity, prev_entity, SemIR::SpecificId::None, /*diagnose=*/true, /*check_syntax=*/true, /*check_self=*/true); } } // namespace Carbon::Check #endif // CARBON_TOOLCHAIN_CHECK_MERGE_H_ ================================================ FILE: toolchain/check/modifiers.cpp ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #include "toolchain/check/modifiers.h" #include #include "toolchain/check/decl_introducer_state.h" namespace Carbon::Check { // Builds the diagnostic for DiagnoseNotAllowed. template static auto StartDiagnoseNotAllowed( Context& context, const Diagnostics::DiagnosticBase& diagnostic_base, Parse::NodeId modifier_node, Lex::TokenKind declaration_kind) -> DiagnosticBuilder { if constexpr (sizeof...(TokenKinds) == 0) { return context.emitter().Build(modifier_node, diagnostic_base); } else if constexpr (sizeof...(TokenKinds) == 1) { return context.emitter().Build(modifier_node, diagnostic_base, context.token_kind(modifier_node)); } else { static_assert(sizeof...(TokenKinds) == 2); return context.emitter().Build(modifier_node, diagnostic_base, context.token_kind(modifier_node), declaration_kind); } } // Diagnoses that a modifier wasn't allowed. Handles adding context when // possible. // // The diagnostic can take up to two TokenKinds: the modifier kind, and the // declaration kind. template static auto DiagnoseNotAllowed( Context& context, const Diagnostics::DiagnosticBase& diagnostic_base, Parse::NodeId modifier_node, Lex::TokenKind decl_kind, SemIR::LocId context_loc_id) -> void { auto diag = StartDiagnoseNotAllowed(context, diagnostic_base, modifier_node, decl_kind); if (context_loc_id.has_value()) { CARBON_DIAGNOSTIC(ModifierNotInContext, Note, "containing definition here"); diag.Note(context_loc_id, ModifierNotInContext); } diag.Emit(); } // Returns the KeywordModifierSet corresponding to the ModifierOrder entry. static auto ModifierOrderAsSet(ModifierOrder order) -> KeywordModifierSet { switch (order) { case ModifierOrder::Access: return KeywordModifierSet::Access; case ModifierOrder::Extern: return KeywordModifierSet::Extern; case ModifierOrder::Extend: return KeywordModifierSet::Extend; case ModifierOrder::Decl: return KeywordModifierSet::Decl; case ModifierOrder::Evaluation: return KeywordModifierSet::Evaluation; } } // Like `LimitModifiersOnDecl`, except says which modifiers are forbidden, and a // `context_string` (and optional `context_loc_id`) specifying the context in // which those modifiers are forbidden. // // See DiagnoseNotAllowed for details regarding diagnostic_base. template static auto ForbidModifiersOnDecl( Context& context, const DiagnosticBaseT& diagnostic_base, DeclIntroducerState& introducer, KeywordModifierSet forbidden, SemIR::LocId context_loc_id = SemIR::LocId::None) -> void { auto not_allowed = introducer.modifier_set & forbidden; if (not_allowed.empty()) { return; } for (auto order_index = 0; order_index <= static_cast(ModifierOrder::Last); ++order_index) { auto order = static_cast(order_index); if (not_allowed.HasAnyOf(ModifierOrderAsSet(order))) { DiagnoseNotAllowed(context, diagnostic_base, introducer.modifier_node_id(order), introducer.kind, context_loc_id); introducer.set_modifier_node_id(order, Parse::NodeId::None); } } introducer.modifier_set.Remove(forbidden); } auto LimitModifiersOnDecl(Context& context, DeclIntroducerState& introducer, KeywordModifierSet allowed) -> void { CARBON_DIAGNOSTIC(ModifierNotAllowedOnDeclaration, Error, "`{0}` not allowed on `{1}` declaration", Lex::TokenKind, Lex::TokenKind); ForbidModifiersOnDecl(context, ModifierNotAllowedOnDeclaration, introducer, ~allowed); } auto LimitModifiersOnNotDefinition(Context& context, DeclIntroducerState& introducer, KeywordModifierSet allowed) -> void { CARBON_DIAGNOSTIC( ModifierOnlyAllowedOnDefinition, Error, "`{0}` not allowed on `{1}` forward declaration, only definition", Lex::TokenKind, Lex::TokenKind); ForbidModifiersOnDecl(context, ModifierOnlyAllowedOnDefinition, introducer, ~allowed); } auto CheckAccessModifiersOnDecl(Context& context, DeclIntroducerState& introducer, std::optional parent_scope_inst) -> void { CARBON_DIAGNOSTIC(ModifierProtectedNotAllowed, Error, "`protected` not allowed; requires class scope"); if (parent_scope_inst) { if (parent_scope_inst->Is()) { // TODO: This assumes that namespaces can only be declared at file scope. // If we add support for non-file-scope namespaces, we will need to check // the parents of the target scope to determine whether we're at file // scope. ForbidModifiersOnDecl(context, ModifierProtectedNotAllowed, introducer, KeywordModifierSet::Protected); return; } if (parent_scope_inst->Is()) { // Both `private` and `protected` allowed in a class definition. return; } } // Otherwise neither `private` nor `protected` allowed. ForbidModifiersOnDecl(context, ModifierProtectedNotAllowed, introducer, KeywordModifierSet::Protected); CARBON_DIAGNOSTIC(ModifierPrivateNotAllowed, Error, "`private` not allowed; requires class or file scope"); ForbidModifiersOnDecl(context, ModifierPrivateNotAllowed, introducer, KeywordModifierSet::Private); } auto CheckMethodModifiersOnFunction( Context& context, DeclIntroducerState& introducer, SemIR::InstId parent_scope_inst_id, std::optional parent_scope_inst) -> void { if (parent_scope_inst) { if (auto class_decl = parent_scope_inst->TryAs()) { auto inheritance_kind = context.classes().Get(class_decl->class_id).inheritance_kind; if (inheritance_kind == SemIR::Class::Final) { CARBON_DIAGNOSTIC( ModifierVirtualNotAllowed, Error, "`virtual` not allowed; requires `abstract` or `base` class scope"); ForbidModifiersOnDecl(context, ModifierVirtualNotAllowed, introducer, KeywordModifierSet::Virtual, SemIR::LocId(parent_scope_inst_id)); } if (inheritance_kind != SemIR::Class::Abstract) { CARBON_DIAGNOSTIC( ModifierAbstractNotAllowed, Error, "`abstract` not allowed; requires `abstract` class scope"); ForbidModifiersOnDecl(context, ModifierAbstractNotAllowed, introducer, KeywordModifierSet::Abstract, SemIR::LocId(parent_scope_inst_id)); } return; } } CARBON_DIAGNOSTIC(ModifierRequiresClass, Error, "`{0}` not allowed; requires class scope", Lex::TokenKind); ForbidModifiersOnDecl(context, ModifierRequiresClass, introducer, KeywordModifierSet::Method); } auto RestrictExternModifierOnDecl(Context& context, DeclIntroducerState& introducer, std::optional parent_scope_inst, bool is_definition) -> void { if (!introducer.modifier_set.HasAnyOf(KeywordModifierSet::Extern)) { return; } if (parent_scope_inst && !parent_scope_inst->Is()) { CARBON_DIAGNOSTIC(ModifierExternNotAllowed, Error, "`{0}` not allowed; requires file or namespace scope", Lex::TokenKind); ForbidModifiersOnDecl(context, ModifierExternNotAllowed, introducer, KeywordModifierSet::Extern); // Treat as unset. introducer.extern_library = SemIR::LibraryNameId::None; return; } if (introducer.extern_library == context.sem_ir().library_id()) { // This prints an error for `extern library`, but doesn't drop it because we // assume there is some other, correct value that we just don't know here. CARBON_DIAGNOSTIC(ExternLibraryIsCurrentLibrary, Error, "`extern library` cannot specify the current library"); context.emitter().Emit(introducer.modifier_node_id(ModifierOrder::Extern), ExternLibraryIsCurrentLibrary); introducer.extern_library = SemIR::LibraryNameId::Error; // Right now this can produce both this and the below diagnostic. } if (is_definition && introducer.extern_library.has_value()) { CARBON_DIAGNOSTIC(ExternLibraryOnDefinition, Error, "a library cannot be provided for an `extern` modifier " "on a definition"); context.emitter().Emit(introducer.modifier_node_id(ModifierOrder::Extern), ExternLibraryOnDefinition); } } auto RequireDefaultFinalOnlyInInterfaces(Context& context, DeclIntroducerState& introducer, SemIR::NameScopeId parent_scope_id) -> void { if (parent_scope_id.has_value() && context.name_scopes().Get(parent_scope_id).is_interface_definition()) { // Both `default` and `final` allowed in an interface definition. return; } CARBON_DIAGNOSTIC(ModifierRequiresInterface, Error, "`{0}` not allowed; requires interface scope", Lex::TokenKind); ForbidModifiersOnDecl(context, ModifierRequiresInterface, introducer, KeywordModifierSet::Interface); } } // namespace Carbon::Check ================================================ FILE: toolchain/check/modifiers.h ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #ifndef CARBON_TOOLCHAIN_CHECK_MODIFIERS_H_ #define CARBON_TOOLCHAIN_CHECK_MODIFIERS_H_ #include "toolchain/check/context.h" namespace Carbon::Check { // Reports a diagnostic if access control modifiers on this are not allowed for // a declaration in `parent_scope_inst`, and updates `introducer`. // // `parent_scope_inst` may be nullopt for a declaration in a block scope. auto CheckAccessModifiersOnDecl(Context& context, DeclIntroducerState& introducer, std::optional parent_scope_inst) -> void; // Reports a diagnostic if the method function modifiers `abstract`, `virtual`, // or `impl` are present but not permitted on a function declaration in // `parent_scope_inst`. // // `parent_scope_inst` may be nullopt for a declaration in a block scope. auto CheckMethodModifiersOnFunction( Context& context, DeclIntroducerState& introducer, SemIR::InstId parent_scope_inst_id, std::optional parent_scope_inst) -> void; // Reports a diagnostic (using `decl_kind`) if modifiers on this declaration are // not in `allowed`. Updates `introducer`. auto LimitModifiersOnDecl(Context& context, DeclIntroducerState& introducer, KeywordModifierSet allowed) -> void; auto LimitModifiersOnNotDefinition(Context& context, DeclIntroducerState& introducer, KeywordModifierSet allowed) -> void; // Restricts the `extern` modifier to only be used on namespace-scoped // declarations. Diagnoses and cleans up: // - `extern library` on a definition. // - `extern` on a scoped entity. // // `parent_scope_inst` may be nullopt for a declaration in a block scope. auto RestrictExternModifierOnDecl(Context& context, DeclIntroducerState& introducer, std::optional parent_scope_inst, bool is_definition) -> void; // Report a diagonostic if `default` and `final` modifiers are used on // declarations where they are not allowed. Right now they are only allowed // inside interfaces. // // `parent_scope_id` may be None for a declaration in a block scope. auto RequireDefaultFinalOnlyInInterfaces(Context& context, DeclIntroducerState& introducer, SemIR::NameScopeId parent_scope_id) -> void; } // namespace Carbon::Check #endif // CARBON_TOOLCHAIN_CHECK_MODIFIERS_H_ ================================================ FILE: toolchain/check/name_component.cpp ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #include "toolchain/check/name_component.h" #include "toolchain/check/context.h" #include "toolchain/check/pattern_match.h" namespace Carbon::Check { auto PopNameComponent(Context& context, SemIR::InstBlockId return_patterns_id) -> NameComponent { Parse::NodeId first_param_node_id = Parse::NoneNodeId(); Parse::NodeId last_param_node_id = Parse::NoneNodeId(); // Explicit params. auto [params_node_id, param_patterns_id] = context.node_stack() .PopWithNodeIdIf(); if (param_patterns_id) { first_param_node_id = context.node_stack() .PopForSoloNodeId(); last_param_node_id = params_node_id; } else { param_patterns_id = SemIR::InstBlockId::None; } // Implicit params. auto [implicit_params_node_id, implicit_param_patterns_id] = context.node_stack() .PopWithNodeIdIf(); if (implicit_param_patterns_id) { // Implicit params always come before explicit params. first_param_node_id = context.node_stack() .PopForSoloNodeId(); // Only use the end of implicit params if there weren't explicit params. if (!last_param_node_id.has_value()) { last_param_node_id = implicit_params_node_id; } } else { implicit_param_patterns_id = SemIR::InstBlockId::None; } auto call_param_patterns_id = SemIR::InstBlockId::None; auto call_params_id = SemIR::InstBlockId::None; auto param_ranges = SemIR::Function::CallParamIndexRanges::Empty; auto pattern_block_id = SemIR::InstBlockId::None; if (param_patterns_id->has_value() || implicit_param_patterns_id->has_value() || (!context.inst_blocks().GetOrEmpty(return_patterns_id).empty())) { auto results = CalleePatternMatch(context, *implicit_param_patterns_id, *param_patterns_id, return_patterns_id); call_param_patterns_id = results.call_param_patterns_id; call_params_id = results.call_params_id; param_ranges = results.param_ranges; pattern_block_id = context.pattern_block_stack().Pop(); context.full_pattern_stack().PopFullPattern(); } auto [name_loc_id, name_id] = context.node_stack().PopWithNodeId(); return { .name_loc_id = name_loc_id, .name_id = name_id, .first_param_node_id = first_param_node_id, .last_param_node_id = last_param_node_id, .implicit_params_loc_id = implicit_params_node_id, .implicit_param_patterns_id = *implicit_param_patterns_id, .params_loc_id = params_node_id, .param_patterns_id = *param_patterns_id, .call_param_patterns_id = call_param_patterns_id, .call_params_id = call_params_id, .param_ranges = param_ranges, .pattern_block_id = pattern_block_id, }; } // Pop the name of a declaration from the node stack, and diagnose if it has // parameters. auto PopNameComponentWithoutParams(Context& context, Lex::TokenKind introducer) -> NameComponent { NameComponent name = PopNameComponent(context); if (name.call_params_id.has_value()) { CARBON_DIAGNOSTIC(UnexpectedDeclNameParams, Error, "`{0}` declaration cannot have parameters", Lex::TokenKind); // Point to the lexically first parameter list in the diagnostic. context.emitter().Emit(name.implicit_param_patterns_id.has_value() ? name.implicit_params_loc_id : name.params_loc_id, UnexpectedDeclNameParams, introducer); name.call_params_id = SemIR::InstBlockId::None; } return name; } } // namespace Carbon::Check ================================================ FILE: toolchain/check/name_component.h ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #ifndef CARBON_TOOLCHAIN_CHECK_NAME_COMPONENT_H_ #define CARBON_TOOLCHAIN_CHECK_NAME_COMPONENT_H_ #include "toolchain/check/node_stack.h" #include "toolchain/parse/node_ids.h" #include "toolchain/sem_ir/function.h" #include "toolchain/sem_ir/ids.h" namespace Carbon::Check { class Context; // A component in a declaration name, such as `C[T:! type](N:! T)` in // `fn C[T:! type](N:! T).F() {}`. struct NameComponent { // The name of the declaration. Parse::NodeId name_loc_id; SemIR::NameId name_id; // Parse tree bounds for the parameters, including both implicit and explicit // parameters. These will be compared to match between declaration and // definition. Parse::NodeId first_param_node_id; Parse::NodeId last_param_node_id; // The implicit parameter list. Parse::NodeId implicit_params_loc_id; SemIR::InstBlockId implicit_param_patterns_id; // The explicit parameter list. Parse::NodeId params_loc_id; SemIR::InstBlockId param_patterns_id; // The `Call` parameters of the entity, if it's a function (see the // corresponding members of SemIR::FunctionFields and // SemIR::EntityWithParamsBase). SemIR::InstBlockId call_param_patterns_id; SemIR::InstBlockId call_params_id; SemIR::Function::CallParamIndexRanges param_ranges; // The pattern block. SemIR::InstBlockId pattern_block_id; }; // Pops a name component from the node stack (and pattern block stack, if it has // parameters). auto PopNameComponent(Context& context, SemIR::InstBlockId return_patterns_id = SemIR::InstBlockId::None) -> NameComponent; // Equivalent to PopNameComponent, but also diagnoses if the name component has // parameters. auto PopNameComponentWithoutParams(Context& context, Lex::TokenKind introducer) -> NameComponent; } // namespace Carbon::Check #endif // CARBON_TOOLCHAIN_CHECK_NAME_COMPONENT_H_ ================================================ FILE: toolchain/check/name_lookup.cpp ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #include "toolchain/check/name_lookup.h" #include #include "common/raw_string_ostream.h" #include "toolchain/check/control_flow.h" #include "toolchain/check/cpp/import.h" #include "toolchain/check/facet_type.h" #include "toolchain/check/generic.h" #include "toolchain/check/import.h" #include "toolchain/check/import_ref.h" #include "toolchain/check/inst.h" #include "toolchain/check/member_access.h" #include "toolchain/check/subst.h" #include "toolchain/check/type.h" #include "toolchain/check/type_completion.h" #include "toolchain/diagnostics/format_providers.h" #include "toolchain/sem_ir/generic.h" #include "toolchain/sem_ir/ids.h" #include "toolchain/sem_ir/name_scope.h" namespace Carbon::Check { auto AddNameToLookup(Context& context, SemIR::NameId name_id, SemIR::InstId target_id, ScopeIndex scope_index) -> void { if (auto existing = context.scope_stack().LookupOrAddName( name_id, target_id, scope_index, IsCurrentPositionReachable(context)); existing.has_value()) { // TODO: Add coverage to this use case and use the location of the name // instead of the target. DiagnoseDuplicateName(context, name_id, SemIR::LocId(target_id), SemIR::LocId(existing)); } } auto LookupNameInDecl(Context& context, SemIR::LocId loc_id, SemIR::NameId name_id, SemIR::NameScopeId scope_id, ScopeIndex scope_index) -> SemIR::ScopeLookupResult { if (!scope_id.has_value()) { // Look for a name in the specified scope or a scope nested within it only. // There are two cases where the name would be in an outer scope: // // - The name is the sole component of the declared name: // // class A; // fn F() { // class A; // } // // In this case, the inner A is not the same class as the outer A, so // lookup should not find the outer A. // // - The name is a qualifier of some larger declared name: // // class A { class B; } // fn F() { // class A.B {} // } // // In this case, we're not in the correct scope to define a member of // class A, so we should reject, and we achieve this by not finding the // name A from the outer scope. // // There is also one case where the name would be in an inner scope: // // - The name is redeclared by a parameter of the same entity: // // fn F() { // class C(C:! type); // } // // In this case, the class C is not a redeclaration of its parameter, but // we find the parameter in order to diagnose a redeclaration error. return SemIR::ScopeLookupResult::MakeWrappedLookupResult( context.scope_stack().LookupInLexicalScopesWithin( name_id, scope_index, /*use_loc_id=*/SemIR::LocId::None, /*is_reachable=*/true), SemIR::AccessKind::Public); } else { // We do not look into `extend`ed scopes here. A qualified name in a // declaration must specify the exact scope in which the name was originally // introduced: // // base class A { fn F(); } // class B { extend base: A; } // // // Error, no `F` in `B`. // fn B.F() {} return LookupNameInExactScope(context, loc_id, name_id, scope_id, context.name_scopes().Get(scope_id), /*is_being_declared=*/true); } } auto LookupUnqualifiedName(Context& context, SemIR::LocId loc_id, SemIR::NameId name_id, bool required) -> LookupResult { // TODO: Check for shadowed lookup results. // Find the results from ancestor lexical scopes. These will be combined with // results from non-lexical scopes such as namespaces and classes. auto [lexical_result, non_lexical_scopes] = context.scope_stack().LookupInLexicalScopes( name_id, loc_id, IsCurrentPositionReachable(context)); // Walk the non-lexical scopes and perform lookups into each of them. for (auto [index, lookup_scope_id, specific_id] : llvm::reverse(non_lexical_scopes)) { if (auto non_lexical_result = LookupQualifiedName( context, loc_id, name_id, LookupScope{.name_scope_id = lookup_scope_id, .specific_id = specific_id, // A non-lexical lookup does not know what `Self` will // be; it remains symbolic if needed. .self_const_id = SemIR::ConstantId::None}, /*required=*/false); non_lexical_result.scope_result.is_found()) { // In an interface definition, replace associated entity `M` with // `Self.M` (where the `Self` is the `Self` of the interface). const auto& scope = context.name_scopes().Get(lookup_scope_id); if (scope.is_interface_definition()) { SemIR::InstId target_inst_id = non_lexical_result.scope_result.target_inst_id(); if (auto assoc_type = context.types().TryGetAs( SemIR::GetTypeOfInstInSpecific( context.sem_ir(), non_lexical_result.specific_id, target_inst_id))) { auto interface_decl = context.insts().GetAs( scope.inst_id()); const auto& interface = context.interfaces().Get(interface_decl.interface_id); SemIR::InstId result_inst_id = GetAssociatedValue( context, loc_id, interface.self_param_id, SemIR::GetConstantValueInSpecific(context.sem_ir(), non_lexical_result.specific_id, target_inst_id), assoc_type->GetSpecificInterface()); non_lexical_result = { .specific_id = SemIR::SpecificId::None, .scope_result = SemIR::ScopeLookupResult::MakeFound( result_inst_id, non_lexical_result.scope_result.access_kind())}; } } return non_lexical_result; } } if (lexical_result == SemIR::InstId::InitTombstone) { CARBON_DIAGNOSTIC(UsedBeforeInitialization, Error, "`{0}` used before initialization", SemIR::NameId); context.emitter().Emit(loc_id, UsedBeforeInitialization, name_id); return {.specific_id = SemIR::SpecificId::None, .scope_result = SemIR::ScopeLookupResult::MakeError()}; } if (lexical_result.has_value()) { // A lexical scope never needs an associated specific. If there's a // lexically enclosing generic, then it also encloses the point of use of // the name. return {.specific_id = SemIR::SpecificId::None, .scope_result = SemIR::ScopeLookupResult::MakeFound( lexical_result, SemIR::AccessKind::Public)}; } // We didn't find anything at all. if (required) { DiagnoseNameNotFound(context, loc_id, name_id); } // TODO: Should this return MakeNotFound if `required` is false, so that // `is_found()` would be false? return {.specific_id = SemIR::SpecificId::None, .scope_result = SemIR::ScopeLookupResult::MakeError()}; } auto LookupNameInExactScope(Context& context, SemIR::LocId loc_id, SemIR::NameId name_id, SemIR::NameScopeId scope_id, SemIR::NameScope& scope, bool is_being_declared) -> SemIR::ScopeLookupResult { if (auto entry_id = is_being_declared ? scope.Lookup(name_id) : scope.LookupOrPoison(loc_id, name_id)) { auto lookup_result = scope.GetEntry(*entry_id).result; if (!lookup_result.is_poisoned()) { LoadImportRef(context, lookup_result.target_inst_id()); } return lookup_result; } if (!scope.import_ir_scopes().empty()) { // TODO: Enforce other access modifiers for imports. return SemIR::ScopeLookupResult::MakeWrappedLookupResult( ImportNameFromOtherPackage(context, loc_id, scope_id, scope.import_ir_scopes(), name_id), SemIR::AccessKind::Public); } if (scope.is_cpp_scope()) { return ImportNameFromCpp(context, loc_id, scope_id, name_id); } return SemIR::ScopeLookupResult::MakeNotFound(); } // Prints diagnostics on invalid qualified name access. static auto DiagnoseInvalidQualifiedNameAccess( Context& context, SemIR::LocId loc_id, SemIR::LocId member_loc_id, SemIR::NameId name_id, SemIR::AccessKind access_kind, bool is_parent_access, AccessInfo access_info) -> void { auto class_type = context.insts().TryGetAs( context.constant_values().GetInstId(access_info.constant_id)); if (!class_type) { return; } // TODO: Support scoped entities other than just classes. const auto& class_info = context.classes().Get(class_type->class_id); auto parent_type_id = class_info.self_type_id; if (access_kind == SemIR::AccessKind::Private && is_parent_access) { if (auto base_type_id = class_info.GetBaseType(context.sem_ir(), class_type->specific_id); base_type_id.has_value()) { parent_type_id = base_type_id; } else if (auto adapted_type_id = class_info.GetAdaptedType( context.sem_ir(), class_type->specific_id); adapted_type_id.has_value()) { parent_type_id = adapted_type_id; } else { CARBON_FATAL("Expected parent for parent access"); } } CARBON_DIAGNOSTIC( ClassInvalidMemberAccess, Error, "cannot access {0:private|protected} member `{1}` of type {2}", Diagnostics::BoolAsSelect, SemIR::NameId, SemIR::TypeId); CARBON_DIAGNOSTIC(ClassMemberDeclaration, Note, "declared here"); context.emitter() .Build(loc_id, ClassInvalidMemberAccess, access_kind == SemIR::AccessKind::Private, name_id, parent_type_id) .Note(member_loc_id, ClassMemberDeclaration) .Emit(); } // Returns whether the access is prohibited by the access modifiers. static auto IsAccessProhibited(std::optional access_info, SemIR::AccessKind access_kind, bool is_parent_access) -> bool { if (!access_info) { return false; } switch (access_kind) { case SemIR::AccessKind::Public: return false; case SemIR::AccessKind::Protected: return access_info->highest_allowed_access == SemIR::AccessKind::Public; case SemIR::AccessKind::Private: return access_info->highest_allowed_access != SemIR::AccessKind::Private || is_parent_access; } } auto CheckAccess(Context& context, SemIR::LocId loc_id, SemIR::LocId member_loc_id, SemIR::NameId name_id, SemIR::AccessKind access_kind, bool is_parent_access, AccessInfo access_info) -> void { if (IsAccessProhibited(access_info, access_kind, is_parent_access)) { DiagnoseInvalidQualifiedNameAccess(context, loc_id, member_loc_id, name_id, access_kind, is_parent_access, access_info); } } // Information regarding a prohibited access. struct ProhibitedAccessInfo { // The resulting inst of the lookup. SemIR::InstId scope_result_id; // The access kind of the lookup. SemIR::AccessKind access_kind; // If the lookup is from an extended scope. For example, if this is a base // class member access from a class that extends it. bool is_parent_access; }; static auto GetSelfFacetForInterfaceFromLookupSelfType( Context& context, const SemIR::GenericId generic_with_self_id, SemIR::ConstantId self_type_const_id) -> SemIR::ConstantId { if (!self_type_const_id.has_value()) { // In a lookup into a non-lexical scope, there is no self-type from the // lookup for the interface-with-self specific. So the self-type we use is // the abstract symbolic Self from the self specific of the // interface-with-self. auto self_specific_args_id = context.specifics().GetArgsOrEmpty( context.generics().GetSelfSpecific(generic_with_self_id)); auto self_specific_args = context.inst_blocks().Get(self_specific_args_id); return context.constant_values().Get(self_specific_args.back()); } if (context.insts().Is( context.constant_values().GetInstId(self_type_const_id))) { // We are looking directly in a facet type, like `I.F` for an interface `I`, // which means there is no self-type from the lookup for the // interface-with-self specific. So the self-type we use is the abstract // symbolic Self from the self specific of the interface-with-self. auto self_specific_args_id = context.specifics().GetArgsOrEmpty( context.generics().GetSelfSpecific(generic_with_self_id)); auto self_specific_args = context.inst_blocks().Get(self_specific_args_id); return context.constant_values().Get(self_specific_args.back()); } // Extended name lookup into a type, like `x.F`, can find a facet // type extended scope from the type of `x`. The type of `x` maybe a // facet converted to a type, so drop the `as type` conversion if // so. auto canonical_facet_or_type = GetCanonicalFacetOrTypeValue(context, self_type_const_id); auto type_of_canonical_facet_or_type = context.insts() .Get(context.constant_values().GetInstId(canonical_facet_or_type)) .type_id(); if (type_of_canonical_facet_or_type == SemIR::TypeType::TypeId) { // If we still have a type, turn it into a facet for use in the // interface-with-self specific. return GetConstantFacetValueForType( context, context.types().GetAsTypeInstId( context.constant_values().GetInstId(self_type_const_id))); } // We have a facet for the self-type (or perhaps an ErrorInst), which we can // use directly in the interface-with-self specific. return canonical_facet_or_type; } auto AppendLookupScopesForConstant(Context& context, SemIR::LocId loc_id, SemIR::ConstantId lookup_const_id, SemIR::ConstantId self_type_const_id, llvm::SmallVector* scopes) -> bool { auto lookup_inst_id = context.constant_values().GetInstId(lookup_const_id); auto lookup = context.insts().Get(lookup_inst_id); if (auto ns = lookup.TryAs()) { scopes->push_back(LookupScope{.name_scope_id = ns->name_scope_id, .specific_id = SemIR::SpecificId::None, .self_const_id = SemIR::ConstantId::None}); return true; } if (auto class_ty = lookup.TryAs()) { // TODO: Allow name lookup into classes that are being defined even if they // are not complete. RequireCompleteType( context, context.types().GetTypeIdForTypeConstantId(lookup_const_id), loc_id, [&](auto& builder) { CARBON_DIAGNOSTIC(QualifiedExprInIncompleteClassScope, Context, "member access into incomplete class {0}", InstIdAsType); builder.Context(loc_id, QualifiedExprInIncompleteClassScope, lookup_inst_id); }); auto& class_info = context.classes().Get(class_ty->class_id); scopes->push_back(LookupScope{.name_scope_id = class_info.scope_id, .specific_id = class_ty->specific_id, .self_const_id = self_type_const_id}); return true; } // Extended scopes may point to a FacetType. if (auto facet_type = lookup.TryAs()) { // TODO: Allow name lookup into facet types that are being defined even if // they are not complete. if (RequireCompleteType( context, context.types().GetTypeIdForTypeConstantId(lookup_const_id), loc_id, [&](auto& builder) { CARBON_DIAGNOSTIC( QualifiedExprInIncompleteFacetTypeScope, Context, "member access into incomplete facet type {0}", InstIdAsType); builder.Context(loc_id, QualifiedExprInIncompleteFacetTypeScope, lookup_inst_id); })) { auto facet_type_info = context.facet_types().Get(facet_type->facet_type_id); // Name lookup into "extend" constraints but not "self impls" constraints. for (const auto& extend : facet_type_info.extend_constraints) { auto& interface = context.interfaces().Get(extend.interface_id); // We need to build the inner interface-with-self specific. To do that // we need to determine the self facet value to use. auto self_facet = GetSelfFacetForInterfaceFromLookupSelfType( context, interface.generic_with_self_id, self_type_const_id); auto interface_with_self_specific_id = MakeSpecificWithInnerSelf( context, loc_id, interface.generic_id, interface.generic_with_self_id, extend.specific_id, self_facet); scopes->push_back({.name_scope_id = interface.scope_with_self_id, .specific_id = interface_with_self_specific_id, .self_const_id = self_type_const_id}); } for (const auto& extend : facet_type_info.extend_named_constraints) { auto& constraint = context.named_constraints().Get(extend.named_constraint_id); // We need to build the inner constraint-with-self specific. To do that // we need to determine the self facet value to use. auto self_facet = GetSelfFacetForInterfaceFromLookupSelfType( context, constraint.generic_with_self_id, self_type_const_id); auto constraint_with_self_specific_id = MakeSpecificWithInnerSelf( context, loc_id, constraint.generic_id, constraint.generic_with_self_id, extend.specific_id, self_facet); scopes->push_back({.name_scope_id = constraint.scope_with_self_id, .specific_id = constraint_with_self_specific_id, .self_const_id = self_type_const_id}); } } else { // Lookup into this scope should fail without producing an error since // `RequireCompleteFacetType` has already issued a diagnostic. scopes->push_back(LookupScope{.name_scope_id = SemIR::NameScopeId::None, .specific_id = SemIR::SpecificId::None, .self_const_id = SemIR::ConstantId::None}); } return true; } if (lookup_const_id == SemIR::ErrorInst::ConstantId) { // Lookup into this scope should fail without producing an error. scopes->push_back(LookupScope{.name_scope_id = SemIR::NameScopeId::None, .specific_id = SemIR::SpecificId::None, .self_const_id = SemIR::ConstantId::None}); return true; } // TODO: Per the design, if `base_id` is any kind of type, then lookup should // treat it as a name scope, even if it doesn't have members. For example, // `(i32*).X` should fail because there's no name `X` in `i32*`, not because // there's no name `X` in `type`. return false; } // Prints a diagnostic for a missing qualified name. static auto DiagnoseMemberNameNotFound( Context& context, SemIR::LocId loc_id, SemIR::NameId name_id, llvm::ArrayRef lookup_scopes) -> void { if (lookup_scopes.size() == 1 && lookup_scopes.front().name_scope_id.has_value()) { if (auto specific_id = lookup_scopes.front().specific_id; specific_id.has_value()) { CARBON_DIAGNOSTIC(MemberNameNotFoundInSpecificScope, Error, "member name `{0}` not found in {1}", SemIR::NameId, SemIR::SpecificId); context.emitter().Emit(loc_id, MemberNameNotFoundInSpecificScope, name_id, specific_id); } else { auto scope_inst_id = context.name_scopes() .Get(lookup_scopes.front().name_scope_id) .inst_id(); CARBON_DIAGNOSTIC(MemberNameNotFoundInInstScope, Error, "member name `{0}` not found in {1}", SemIR::NameId, InstIdAsType); context.emitter().Emit(loc_id, MemberNameNotFoundInInstScope, name_id, scope_inst_id); } return; } CARBON_DIAGNOSTIC(MemberNameNotFound, Error, "member name `{0}` not found", SemIR::NameId); context.emitter().Emit(loc_id, MemberNameNotFound, name_id); } auto LookupQualifiedName(Context& context, SemIR::LocId loc_id, SemIR::NameId name_id, llvm::ArrayRef lookup_scopes, bool required, std::optional access_info) -> LookupResult { llvm::SmallVector scopes(lookup_scopes); // TODO: Support reporting of multiple prohibited access. llvm::SmallVector prohibited_accesses; LookupResult result = { .specific_id = SemIR::SpecificId::None, .scope_result = SemIR::ScopeLookupResult::MakeNotFound()}; auto parent_const_id = SemIR::ConstantId::None; bool has_error = false; bool is_parent_access = false; // Walk this scope and, if nothing is found here, the scopes it extends. while (!scopes.empty()) { auto [scope_id, specific_id, self_const_id] = scopes.pop_back_val(); if (!scope_id.has_value()) { has_error = true; continue; } auto& name_scope = context.name_scopes().Get(scope_id); has_error |= name_scope.has_error(); const SemIR::ScopeLookupResult scope_result = LookupNameInExactScope(context, loc_id, name_id, scope_id, name_scope); SemIR::AccessKind access_kind = scope_result.access_kind(); if (is_parent_access && scope_result.is_found() && !access_info.has_value()) { access_info = AccessInfo{.constant_id = parent_const_id, .highest_allowed_access = SemIR::AccessKind::Protected}; } auto is_access_prohibited = IsAccessProhibited(access_info, access_kind, is_parent_access); // Keep track of prohibited accesses, this will be useful for reporting // multiple prohibited accesses if we can't find a suitable lookup. if (is_access_prohibited) { prohibited_accesses.push_back({ .scope_result_id = scope_result.target_inst_id(), .access_kind = access_kind, .is_parent_access = is_parent_access, }); } if (!scope_result.is_found() || is_access_prohibited) { // If nothing is found in this scope or if we encountered an invalid // access, look in its extended scopes. const auto& extended = name_scope.extended_scopes(); scopes.reserve(scopes.size() + extended.size()); for (auto extended_id : llvm::reverse(extended)) { // Substitute into the constant describing the extended scope to // determine its corresponding specific. CARBON_CHECK(extended_id.has_value()); LoadImportRef(context, extended_id); SemIR::ConstantId const_id = GetConstantValueInSpecific( context.sem_ir(), specific_id, extended_id); if (!AppendLookupScopesForConstant(context, loc_id, const_id, self_const_id, &scopes)) { // TODO: Handle case where we have a symbolic type and instead should // look in its type. } } is_parent_access |= !extended.empty(); parent_const_id = context.constant_values().Get(name_scope.inst_id()); continue; } // If this is our second lookup result, diagnose an ambiguity. if (result.scope_result.is_found()) { CARBON_DIAGNOSTIC( NameAmbiguousDueToExtend, Error, "ambiguous use of name `{0}` found in multiple extended scopes", SemIR::NameId); context.emitter().Emit(loc_id, NameAmbiguousDueToExtend, name_id); // TODO: Add notes pointing to the scopes. return {.specific_id = SemIR::SpecificId::None, .scope_result = SemIR::ScopeLookupResult::MakeError()}; } result.scope_result = scope_result; result.specific_id = specific_id; } if ((!prohibited_accesses.empty() || required) && !result.scope_result.is_found()) { if (!has_error) { if (prohibited_accesses.empty()) { DiagnoseMemberNameNotFound(context, loc_id, name_id, lookup_scopes); } else { // TODO: We should report multiple prohibited accesses in case we don't // find a valid lookup. Reporting the last one should suffice for now. auto [scope_result_id, access_kind, is_parent_access] = prohibited_accesses.back(); // Note, `access_info` is guaranteed to have a value here, since // `prohibited_accesses` is non-empty. DiagnoseInvalidQualifiedNameAccess( context, loc_id, SemIR::LocId(scope_result_id), name_id, access_kind, is_parent_access, *access_info); } } CARBON_CHECK(!result.scope_result.is_poisoned()); return {.specific_id = SemIR::SpecificId::None, .scope_result = SemIR::ScopeLookupResult::MakeError()}; } return result; } // Returns a `Core.` name for diagnostics. static auto GetCoreQualifiedName(llvm::ArrayRef qualifiers) -> std::string { RawStringOstream str; str << "Core"; for (auto qualifier : qualifiers) { str << "." << qualifier; } return str.TakeStr(); } // Returns the scope of the Core package, or `None` if it's not found. // // TODO: Consider tracking the Core package in SemIR so we don't need to use // name lookup to find it. static auto GetCorePackage(Context& context, SemIR::LocId loc_id, llvm::ArrayRef qualifiers) -> SemIR::NameScopeId { if (context.name_scopes().IsCorePackage(SemIR::NameScopeId::Package)) { return SemIR::NameScopeId::Package; } // Look up `package.Core`. auto core_scope_result = LookupNameInExactScope( context, loc_id, SemIR::NameId::Core, SemIR::NameScopeId::Package, context.name_scopes().Get(SemIR::NameScopeId::Package)); if (core_scope_result.is_found()) { // We expect it to be a namespace. if (auto namespace_inst = context.insts().TryGetAs( core_scope_result.target_inst_id())) { // TODO: Decide whether to allow the case where `Core` is not a package. return namespace_inst->name_scope_id; } } CARBON_DIAGNOSTIC( CoreNotFound, Error, "`{0}` implicitly referenced here, but package `Core` not found", std::string); context.emitter().Emit(loc_id, CoreNotFound, GetCoreQualifiedName(qualifiers)); return SemIR::NameScopeId::None; } auto LookupNameInCore(Context& context, SemIR::LocId loc_id, llvm::ArrayRef qualifiers) -> SemIR::InstId { CARBON_CHECK(!qualifiers.empty()); auto core_package_id = GetCorePackage(context, loc_id, qualifiers); if (!core_package_id.has_value()) { return SemIR::ErrorInst::InstId; } auto inst_id = SemIR::InstId::None; for (auto qualifier : qualifiers) { auto name_id = context.core_identifiers().AddNameId(qualifier); auto scope_id = SemIR::NameScopeId::None; if (inst_id.has_value()) { auto namespace_inst = context.insts().TryGetAs(inst_id); if (namespace_inst) { scope_id = namespace_inst->name_scope_id; } } else { scope_id = core_package_id; } auto scope_result = scope_id.has_value() ? LookupNameInExactScope(context, loc_id, name_id, scope_id, context.name_scopes().Get(scope_id)) : SemIR::ScopeLookupResult::MakeNotFound(); if (!scope_result.is_found()) { CARBON_DIAGNOSTIC(CoreNameNotFound, Error, "name `{0}` implicitly referenced here, but not found", std::string); context.emitter().Emit(loc_id, CoreNameNotFound, GetCoreQualifiedName(qualifiers)); return SemIR::ErrorInst::InstId; } // Look through import_refs and aliases. inst_id = context.constant_values().GetConstantInstId( scope_result.target_inst_id()); } return inst_id; } auto DiagnoseDuplicateName(Context& context, SemIR::NameId name_id, SemIR::LocId dup_def, SemIR::LocId prev_def) -> void { CARBON_DIAGNOSTIC(NameDeclDuplicate, Error, "duplicate name `{0}` being declared in the same scope", SemIR::NameId); CARBON_DIAGNOSTIC(NameDeclPrevious, Note, "name is previously declared here"); context.emitter() .Build(dup_def, NameDeclDuplicate, name_id) .Note(prev_def, NameDeclPrevious) .Emit(); } auto DiagnosePoisonedName(Context& context, SemIR::NameId name_id, SemIR::LocId poisoning_loc_id, SemIR::LocId decl_name_loc_id) -> void { CARBON_CHECK(poisoning_loc_id.has_value(), "Trying to diagnose poisoned name with no poisoning location"); CARBON_DIAGNOSTIC(NameUseBeforeDecl, Error, "name `{0}` used before it was declared", SemIR::NameId); CARBON_DIAGNOSTIC(NameUseBeforeDeclNote, Note, "declared here"); context.emitter() .Build(poisoning_loc_id, NameUseBeforeDecl, name_id) .Note(decl_name_loc_id, NameUseBeforeDeclNote) .Emit(); } auto DiagnoseNameNotFound(Context& context, SemIR::LocId loc_id, SemIR::NameId name_id) -> void { CARBON_DIAGNOSTIC(NameNotFound, Error, "name `{0}` not found", SemIR::NameId); context.emitter().Emit(loc_id, NameNotFound, name_id); } } // namespace Carbon::Check ================================================ FILE: toolchain/check/name_lookup.h ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #ifndef CARBON_TOOLCHAIN_CHECK_NAME_LOOKUP_H_ #define CARBON_TOOLCHAIN_CHECK_NAME_LOOKUP_H_ #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/StringRef.h" #include "toolchain/check/context.h" #include "toolchain/check/core_identifier.h" #include "toolchain/sem_ir/ids.h" namespace Carbon::Check { // Information about a scope in which we can perform name lookup. struct LookupScope { // The name scope in which names are searched. SemIR::NameScopeId name_scope_id; // The specific for the name scope, or `None` if the name scope is not // defined by a generic or we should perform lookup into the generic itself. SemIR::SpecificId specific_id; // The self-type where lookup is happening when the lookup is for a member // access. SemIR::ConstantId self_const_id; }; // A result produced by name lookup. struct LookupResult { // The specific in which the lookup result was found. `None` if the result // was not found in a specific. SemIR::SpecificId specific_id; // The result from the lookup in the scope. SemIR::ScopeLookupResult scope_result; }; // Information about an access. struct AccessInfo { // The constant being accessed. SemIR::ConstantId constant_id; // The highest allowed access for a lookup. For example, `Protected` allows // access to `Public` and `Protected` names, but not `Private`. SemIR::AccessKind highest_allowed_access; }; // Adds a name to name lookup. Prints a diagnostic for name conflicts. If // specified, `scope_index` specifies which lexical scope the name is inserted // into, otherwise the name is inserted into the current scope. auto AddNameToLookup(Context& context, SemIR::NameId name_id, SemIR::InstId target_id, ScopeIndex scope_index = ScopeIndex::None) -> void; // Performs name lookup in a specified scope for a name appearing in a // declaration. If scope_id is `None`, performs lookup into the lexical scope // specified by scope_index instead. auto LookupNameInDecl(Context& context, SemIR::LocId loc_id, SemIR::NameId name_id, SemIR::NameScopeId scope_id, ScopeIndex scope_index) -> SemIR::ScopeLookupResult; // Performs an unqualified name lookup, returning the referenced `InstId`. auto LookupUnqualifiedName(Context& context, SemIR::LocId loc_id, SemIR::NameId name_id, bool required = true) -> LookupResult; // Performs a name lookup in a specified scope, returning the referenced // `InstId`. Does not look into extended scopes. Returns `InstId::None` if the // name is not found. // // If `is_being_declared` is false, then this is a regular name lookup, and // the name will be poisoned if not found so that later lookups will fail; a // poisoned name will be treated as if it is not declared. Otherwise, this is // a lookup for a name being declared, so the name will not be poisoned, but // poison will be returned if it's already been looked up. // // If `name_id` is not an identifier, the name will not be poisoned. auto LookupNameInExactScope(Context& context, SemIR::LocId loc_id, SemIR::NameId name_id, SemIR::NameScopeId scope_id, SemIR::NameScope& scope, bool is_being_declared = false) -> SemIR::ScopeLookupResult; // Appends the lookup scopes corresponding to `lookup_const_id` to `*scopes`. // // The `self_type_const_id` is the self-type that we are looking for a name in, // and which is passed through to extended scopes. It may be a facet or a value // of type `type`. Some extended scopes have a symbolic `Self` internally which // needs to know the self-type in order to produce a correct specific scope in // the result. // // Returns `false` if not a scope. On invalid scopes, prints a diagnostic, but // still updates `*scopes` and returns `true`. auto AppendLookupScopesForConstant(Context& context, SemIR::LocId loc_id, SemIR::ConstantId lookup_const_id, SemIR::ConstantId self_type_const_id, llvm::SmallVector* scopes) -> bool; // Performs a qualified name lookup in a specified scopes and in scopes that // they extend, returning the referenced `InstId`. auto LookupQualifiedName(Context& context, SemIR::LocId loc_id, SemIR::NameId name_id, llvm::ArrayRef lookup_scopes, bool required = true, std::optional access_info = std::nullopt) -> LookupResult; // Returns the `InstId` corresponding to a qualified name in the `Core` package, // or BuiltinErrorInst if not found. auto LookupNameInCore(Context& context, SemIR::LocId loc_id, llvm::ArrayRef qualifiers) -> SemIR::InstId; // Returns the `InstId` corresponding to a name in the `Core`, or // BuiltinErrorInst if not found. inline auto LookupNameInCore(Context& context, SemIR::LocId loc_id, CoreIdentifier identifier) -> SemIR::InstId { return LookupNameInCore(context, loc_id, llvm::ArrayRef{identifier}); } // Checks whether a name is accessible in the given access context. Produces a // diagnostic if not. auto CheckAccess(Context& context, SemIR::LocId loc_id, SemIR::LocId member_loc_id, SemIR::NameId name_id, SemIR::AccessKind access_kind, bool is_parent_access, AccessInfo access_info) -> void; // Prints a diagnostic for a duplicate name. auto DiagnoseDuplicateName(Context& context, SemIR::NameId name_id, SemIR::LocId dup_def, SemIR::LocId prev_def) -> void; // Prints a diagnostic for a poisoned name when it's later declared. auto DiagnosePoisonedName(Context& context, SemIR::NameId name_id, SemIR::LocId poisoning_loc_id, SemIR::LocId decl_name_loc_id) -> void; // Prints a diagnostic for a missing name. auto DiagnoseNameNotFound(Context& context, SemIR::LocId loc_id, SemIR::NameId name_id) -> void; } // namespace Carbon::Check #endif // CARBON_TOOLCHAIN_CHECK_NAME_LOOKUP_H_ ================================================ FILE: toolchain/check/name_ref.cpp ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #include "toolchain/check/name_ref.h" #include "toolchain/check/inst.h" namespace Carbon::Check { auto BuildNameRef(Context& context, SemIR::LocId loc_id, SemIR::NameId name_id, SemIR::InstId inst_id, SemIR::SpecificId specific_id) -> SemIR::InstId { auto type_id = SemIR::GetTypeOfInstInSpecific(context.sem_ir(), specific_id, inst_id); CARBON_CHECK(type_id.has_value(), "Missing type for {0}", context.insts().Get(inst_id)); // If the named entity has a constant value that depends on its specific, // store the specific too. if (specific_id.has_value() && context.constant_values().Get(inst_id).is_symbolic()) { inst_id = AddInst( context, loc_id, {.type_id = type_id, .inst_id = inst_id, .specific_id = specific_id}); } return AddInst( context, loc_id, {.type_id = type_id, .name_id = name_id, .value_id = inst_id}); } } // namespace Carbon::Check ================================================ FILE: toolchain/check/name_ref.h ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #ifndef CARBON_TOOLCHAIN_CHECK_NAME_REF_H_ #define CARBON_TOOLCHAIN_CHECK_NAME_REF_H_ #include "toolchain/check/context.h" #include "toolchain/sem_ir/ids.h" namespace Carbon::Check { // Builds a reference to the given name, which has already been resolved to // `inst_id` within `specific_id`. auto BuildNameRef(Context& context, SemIR::LocId loc_id, SemIR::NameId name_id, SemIR::InstId inst_id, SemIR::SpecificId specific_id) -> SemIR::InstId; } // namespace Carbon::Check #endif // CARBON_TOOLCHAIN_CHECK_NAME_REF_H_ ================================================ FILE: toolchain/check/name_scope.cpp ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #include "toolchain/check/name_scope.h" namespace Carbon::Check { auto TryAsClassScope(Context& context, SemIR::NameScopeId scope_id) -> std::optional { if (!scope_id.has_value()) { return std::nullopt; } auto& scope = context.name_scopes().Get(scope_id); if (!scope.inst_id().has_value()) { return std::nullopt; } auto class_decl = context.insts().TryGetAs(scope.inst_id()); if (!class_decl) { return std::nullopt; } return {{.class_decl = *class_decl, .name_scope = &scope}}; } } // namespace Carbon::Check ================================================ FILE: toolchain/check/name_scope.h ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #ifndef CARBON_TOOLCHAIN_CHECK_NAME_SCOPE_H_ #define CARBON_TOOLCHAIN_CHECK_NAME_SCOPE_H_ #include #include "toolchain/check/context.h" #include "toolchain/sem_ir/ids.h" #include "toolchain/sem_ir/typed_insts.h" namespace Carbon::Check { struct ClassScope { SemIR::ClassDecl class_decl; SemIR::NameScope* name_scope; }; // If the specified name scope corresponds to a class, returns the corresponding // class declaration. auto TryAsClassScope(Context& context, SemIR::NameScopeId scope_id) -> std::optional; } // namespace Carbon::Check #endif // CARBON_TOOLCHAIN_CHECK_NAME_SCOPE_H_ ================================================ FILE: toolchain/check/node_id_traversal.cpp ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #include "toolchain/check/node_id_traversal.h" #include #include #include #include "toolchain/check/deferred_definition_worklist.h" #include "toolchain/check/handle.h" #include "toolchain/check/thunk.h" namespace Carbon::Check { NodeIdTraversal::NodeIdTraversal(Context* context) : context_(context), next_deferred_definition_(&context->parse_tree()), worklist_(&context->deferred_definition_worklist()) { auto range = context->parse_tree().postorder(); chunks_.push_back({.it = range.begin(), .end = range.end(), .next_definition = Parse::DeferredDefinitionIndex::None}); } auto NodeIdTraversal::Next() -> std::optional { while (true) { // If we're checking deferred definitions, find the next definition we // should check, restore its suspended state, and add a corresponding // `Chunk` to the top of the chunk list. if (chunks_.back().checking_deferred_definitions) { if (chunks_.back().next_worklist_index < worklist().size()) { std::visit([&](auto& task) { PerformTask(std::move(task)); }, worklist()[chunks_.back().next_worklist_index++]); continue; } // Worklist is empty: discard the worklist items associated with this // chunk, and leave the scope. worklist().truncate(chunks_.back().first_worklist_index); // We reach here when // `DeferredDefinitionScope::SuspendFinishedScopeAndPush` returns // `NonNestedWithWork`. In this case it's our responsibility to pop the // scope left behind by the `Handle*Definition` function for the // non-nested definition. context_->decl_name_stack().PopScope(); chunks_.back().checking_deferred_definitions = false; } // If we're not checking deferred definitions, produce the next parse node // for this chunk. If we've run out of parse nodes, we're done with this // chunk of the parse tree. if (chunks_.back().it == chunks_.back().end) { auto old_chunk = chunks_.pop_back_val(); // If we're out of chunks, then we're done entirely. if (chunks_.empty()) { worklist().VerifyEmpty(); return std::nullopt; } next_deferred_definition_.SkipTo(old_chunk.next_definition); continue; } auto node_id = *chunks_.back().it; // If we've reached the start of a deferred definition, skip to the end of // it, and track that we need to check it later. if (node_id == next_deferred_definition_.start_id()) { const auto& definition_info = context_->parse_tree().deferred_definitions().Get( next_deferred_definition_.index()); worklist().SuspendFunctionAndPush(next_deferred_definition_.index(), [&] { return HandleFunctionDefinitionSuspend(*context_, definition_info.start_id); }); // Continue type-checking the parse tree after the end of the definition. chunks_.back().it = Parse::Tree::PostorderIterator(definition_info.definition_id) + 1; next_deferred_definition_.SkipTo(definition_info.next_definition_index); continue; } ++chunks_.back().it; return node_id; } } // Determines whether this node kind is the start of a deferred definition // scope. static auto IsStartOfDeferredDefinitionScope(Parse::NodeKind kind) -> bool { switch (kind) { case Parse::NodeKind::ClassDefinitionStart: case Parse::NodeKind::ImplDefinitionStart: case Parse::NodeKind::InterfaceDefinitionStart: case Parse::NodeKind::NamedConstraintDefinitionStart: // TODO: Mixins. return true; default: return false; } } // Determines whether this node kind is the end of a deferred definition scope. static auto IsEndOfDeferredDefinitionScope(Parse::NodeKind kind) -> bool { switch (kind) { case Parse::NodeKind::ClassDefinition: case Parse::NodeKind::ImplDefinition: case Parse::NodeKind::InterfaceDefinition: case Parse::NodeKind::NamedConstraintDefinition: // TODO: Mixins. return true; default: return false; } } // TODO: Investigate factoring out `IsStartOfDeferredDefinitionScope` and // `IsEndOfDeferredDefinitionScope` in order to make `NodeIdTraversal` // reusable. auto NodeIdTraversal::Handle(Parse::NodeKind parse_kind) -> void { // When we reach the start of a deferred definition scope, add a task to the // worklist to check future skipped definitions in the new context. if (IsStartOfDeferredDefinitionScope(parse_kind)) { worklist().PushEnterDeferredDefinitionScope(*context_); } // When we reach the end of a deferred definition scope, add a task to the // worklist to leave the scope. If this is not a nested scope, start // checking the deferred definitions now. if (IsEndOfDeferredDefinitionScope(parse_kind)) { auto scope_kind = worklist().SuspendFinishedScopeAndPush(*context_); // If we have deferred tasks in this scope, perform them next. if (scope_kind == DeferredDefinitionWorklist::FinishedScopeKind::NonNestedWithWork) { chunks_.back().checking_deferred_definitions = true; chunks_.back().next_worklist_index = chunks_.back().first_worklist_index; } } } auto NodeIdTraversal::PerformTask( DeferredDefinitionWorklist::EnterNestedDeferredDefinitionScope&& enter) -> void { CARBON_CHECK(enter.suspended_name, "Entering a scope with no suspension information."); context_->decl_name_stack().Restore(std::move(*enter.suspended_name)); } auto NodeIdTraversal::PerformTask( DeferredDefinitionWorklist::LeaveNestedDeferredDefinitionScope&& /*leave*/) -> void { context_->decl_name_stack().PopScope(); } auto NodeIdTraversal::PerformTask( DeferredDefinitionWorklist::CheckSkippedDefinition&& parse_definition) -> void { auto& [definition_index, suspended_fn] = parse_definition; const auto& definition_info = context_->parse_tree().deferred_definitions().Get(definition_index); HandleFunctionDefinitionResume(*context_, definition_info.start_id, std::move(suspended_fn)); auto range = Parse::Tree::PostorderIterator::MakeRange( definition_info.start_id, definition_info.definition_id); chunks_.push_back({.it = range.begin() + 1, .end = range.end(), .next_definition = next_deferred_definition_.index(), .checking_deferred_definitions = false, .first_worklist_index = worklist().size(), .next_worklist_index = worklist().size()}); ++definition_index.index; next_deferred_definition_.SkipTo(definition_index); } auto NodeIdTraversal::PerformTask( DeferredDefinitionWorklist::DefineThunk&& define_thunk) -> void { BuildThunkDefinition(*context_, std::move(define_thunk)); } NodeIdTraversal::NextDeferredDefinitionCache::NextDeferredDefinitionCache( const Parse::Tree* tree) : tree_(tree) { SkipTo(Parse::DeferredDefinitionIndex(0)); } // Set the specified deferred definition index as being the next one that // will be encountered. auto NodeIdTraversal::NextDeferredDefinitionCache::SkipTo( Parse::DeferredDefinitionIndex next_index) -> void { index_ = next_index; if (static_cast(index_.index) == tree_->deferred_definitions().size()) { start_id_ = Parse::NodeId::None; } else { start_id_ = tree_->deferred_definitions().Get(index_).start_id; } } } // namespace Carbon::Check ================================================ FILE: toolchain/check/node_id_traversal.h ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #ifndef CARBON_TOOLCHAIN_CHECK_NODE_ID_TRAVERSAL_H_ #define CARBON_TOOLCHAIN_CHECK_NODE_ID_TRAVERSAL_H_ #include "common/ostream.h" #include "llvm/ADT/SmallVector.h" #include "toolchain/check/context.h" #include "toolchain/check/deferred_definition_worklist.h" #include "toolchain/parse/tree.h" namespace Carbon::Check { // A traversal of the node IDs in the parse tree, in the order in which we need // to check them. class NodeIdTraversal { public: // `context` must not be null. explicit NodeIdTraversal(Context* context); // Finds the next `NodeId` to type-check. Returns nullopt if the traversal is // complete. auto Next() -> std::optional; // Performs any processing necessary after we type-check a node. auto Handle(Parse::NodeKind parse_kind) -> void; private: // State used to track the next deferred function definition that we will // encounter and need to reorder. class NextDeferredDefinitionCache { public: explicit NextDeferredDefinitionCache(const Parse::Tree* tree); // Set the specified deferred definition index as being the next one that // will be encountered. auto SkipTo(Parse::DeferredDefinitionIndex next_index) -> void; // Returns the index of the next deferred definition to be encountered. auto index() const -> Parse::DeferredDefinitionIndex { return index_; } // Returns the ID of the start node of the next deferred definition. auto start_id() const -> Parse::NodeId { return start_id_; } private: const Parse::Tree* tree_; Parse::DeferredDefinitionIndex index_ = Parse::DeferredDefinitionIndex::None; Parse::NodeId start_id_ = Parse::NodeId::None; }; // A chunk of the parse tree that we need to type-check. struct Chunk { Parse::Tree::PostorderIterator it; Parse::Tree::PostorderIterator end; // The next definition that will be encountered after this chunk completes. Parse::DeferredDefinitionIndex next_definition; // Whether we are currently checking deferred definitions, rather than the // tokens of this chunk. If so, we'll pull tasks off `worklist` and execute // them until we're done with this batch of deferred definitions. Otherwise, // we'll pull node IDs from `*it` until it reaches `end`. bool checking_deferred_definitions = false; // If we're checking deferred definitions, the index of the first task to // execute from `worklist`. size_t first_worklist_index; // If we're checking deferred definitions, the index of the next task to // execute from `worklist`. size_t next_worklist_index; }; auto worklist() -> DeferredDefinitionWorklist& { return *worklist_; } // Re-enter a nested deferred definition scope. auto PerformTask( DeferredDefinitionWorklist::EnterNestedDeferredDefinitionScope&& enter) -> void; // Leave a nested deferred definition scope. auto PerformTask( DeferredDefinitionWorklist::LeaveNestedDeferredDefinitionScope&& leave) -> void; // Resume checking a deferred definition. auto PerformTask( DeferredDefinitionWorklist::CheckSkippedDefinition&& parse_definition) -> void; // Define a thunk. auto PerformTask(DeferredDefinitionWorklist::DefineThunk&& define_thunk) -> void; Context* context_; NextDeferredDefinitionCache next_deferred_definition_; DeferredDefinitionWorklist* worklist_; llvm::SmallVector chunks_; }; } // namespace Carbon::Check #endif // CARBON_TOOLCHAIN_CHECK_NODE_ID_TRAVERSAL_H_ ================================================ FILE: toolchain/check/node_stack.cpp ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #include "toolchain/check/node_stack.h" #include "llvm/ADT/STLExtras.h" namespace Carbon::Check { auto NodeStack::PrintForStackDump(int indent, llvm::raw_ostream& output) const -> void { auto print_id = [&](Id id) { if constexpr (Kind == Id::Kind::None) { output << "no value"; } else if constexpr (Kind == Id::Kind::Invalid) { CARBON_FATAL("Should not be in node stack"); } else { output << id.As(); } }; output.indent(indent); output << "NodeStack:\n"; for (auto [i, entry] : llvm::enumerate(stack_)) { auto node_kind = parse_tree_->node_kind(entry.node_id); output.indent(indent + 2); output << i << ". " << node_kind << ": "; switch (node_kind) { #define CARBON_PARSE_NODE_KIND(Kind) \ case Parse::NodeKind::Kind: \ print_id.operator()(entry.id); \ break; #include "toolchain/parse/node_kind.def" } output << "\n"; } } auto NodeStack::CheckIdKindTable() -> void { #define CARBON_PARSE_NODE_KIND(Name) \ { \ constexpr auto from_category = \ NodeCategoryToIdKind(Parse::Name::Kind.category(), true); \ constexpr auto from_kind = \ NodeKindToIdKindSpecialCases(Parse::Name::Kind); \ static_assert(from_category || from_kind, \ "Id::Kind not specified for " #Name \ "; add to NodeStack::NodeKindToIdKindSpecialCases or " \ "specify a node category in typed_nodes.h"); \ static_assert(!from_category || !from_kind, \ "Special case Id::Kind specified for " #Name \ ", but node category has an Id::Kind; remove from " \ "NodeStack::NodeKindToIdKindSpecialCases"); \ } #include "toolchain/parse/node_kind.def" } } // namespace Carbon::Check ================================================ FILE: toolchain/check/node_stack.h ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #ifndef CARBON_TOOLCHAIN_CHECK_NODE_STACK_H_ #define CARBON_TOOLCHAIN_CHECK_NODE_STACK_H_ #include "common/vlog.h" #include "llvm/ADT/SmallVector.h" #include "toolchain/parse/node_ids.h" #include "toolchain/parse/node_kind.h" #include "toolchain/parse/tree.h" #include "toolchain/parse/typed_nodes.h" #include "toolchain/sem_ir/id_kind.h" #include "toolchain/sem_ir/ids.h" namespace Carbon::Check { // A non-discriminated union of ID types. class IdUnion { public: // The default constructor forms a `None` ID. explicit constexpr IdUnion() : index(AnyIdBase::NoneIndex) {} template requires SemIR::IdKind::Contains explicit constexpr IdUnion(IdT id) : index(id.index) {} using Kind = SemIR::IdKind::RawEnumType; // Returns the ID given its type. template requires SemIR::IdKind::Contains constexpr auto As() const -> IdT { return IdT(index); } // Returns the ID given its kind. template constexpr auto As() const -> SemIR::IdKind::TypeFor { return As>(); } // Translates an ID type to the enum ID kind. Returns `None` if `IdT` isn't // a type that can be stored in this union. template static constexpr auto KindFor() -> Kind { return SemIR::IdKind::For; } private: decltype(AnyIdBase::index) index; }; // The stack of parse nodes representing the current state of a Check::Context. // Each parse node can have an associated id of some kind (instruction, // instruction block, function, class, ...). // // All pushes and pops will be vlogged. // // Pop APIs will run basic verification: // // - If receiving a Parse::NodeKind, verify that the node_id being popped has // that kind. Similarly, if receiving a Parse::NodeCategory, make sure the // of the popped node_id overlaps that category. // - Validates the kind of id data in the node based on the kind or category of // the node_id. // // These should be assumed API constraints unless otherwise mentioned on a // method. The main exception is PopAndIgnore, which doesn't do verification. class NodeStack { public: explicit NodeStack(const Parse::Tree& parse_tree, llvm::raw_ostream* vlog_stream) : parse_tree_(&parse_tree), vlog_stream_(vlog_stream) {} // Pushes a solo parse tree node onto the stack. Used when there is no // IR generated by the node. auto Push(Parse::NodeId node_id) -> void { auto kind = parse_tree_->node_kind(node_id); CARBON_CHECK(NodeKindToIdKind(kind) == Id::Kind::None, "Parse kind expects an Id: {0}", kind); CARBON_VLOG("Node Push {0}: {1} -> \n", stack_.size(), kind); CARBON_CHECK(stack_.size() < (1 << 20), "Excessive stack size: likely infinite loop"); stack_.push_back({.node_id = node_id, .id = Id()}); } // Pushes a parse tree node onto the stack with an ID. template auto Push(Parse::NodeId node_id, IdT id) -> void { auto kind = parse_tree_->node_kind(node_id); CARBON_CHECK(NodeKindToIdKind(kind) == Id::KindFor(), "Parse kind expected a different IdT: {0} -> {1}\n", kind, id); CARBON_CHECK(id.has_value(), "Push called with `None` id: {0}", parse_tree_->node_kind(node_id)); CARBON_VLOG("Node Push {0}: {1} -> {2}\n", stack_.size(), kind, id); CARBON_CHECK(stack_.size() < (1 << 20), "Excessive stack size: likely infinite loop"); stack_.push_back({.node_id = node_id, .id = Id(id)}); } // TODO: Most parse nodes don't know about TypeInstId, so downgrade TypeInstId // to InstId to match expectations. We should teach parse nodes that will // receive a TypeInstId to expect it, and this function can go away. auto Push(Parse::NodeId node_id, SemIR::TypeInstId id) -> void { auto kind = parse_tree_->node_kind(node_id); if (NodeKindToIdKind(kind) == Id::KindFor()) { Push(node_id, id); } else { Push(node_id, id); } } // Returns whether there is a node of the specified kind on top of the stack. auto PeekIs(Parse::NodeKind kind) const -> bool { return !stack_.empty() && PeekNodeKind() == kind; } // Returns whether the node on the top of the stack has an overlapping // category. auto PeekIs(Parse::NodeCategory category) const -> bool { return !stack_.empty() && PeekNodeKind().category().HasAnyOf(category); } // Returns whether there is a node with the corresponding ID on top of the // stack. template auto PeekIs() const -> bool { return !stack_.empty() && NodeKindToIdKind(PeekNodeKind()) == Id::KindFor(); } // Returns whether the *next* node on the stack is a given kind. This doesn't // have the breadth of support versus other Peek functions because it's // expected to be used in narrow circumstances when determining how to treat // the *current* top of the stack. auto PeekNextIs(Parse::NodeKind kind) const -> bool { CARBON_CHECK(stack_.size() >= 2); return parse_tree_->node_kind(stack_[stack_.size() - 2].node_id) == kind; } // Pops the top of the stack without any verification. auto PopAndIgnore() -> void { Entry back = stack_.pop_back_val(); CARBON_VLOG("Node Pop {0}: {1} -> \n", stack_.size(), parse_tree_->node_kind(back.node_id)); } // Pops the top of the stack and returns the node_id. template auto PopForSoloNodeId() -> Parse::NodeIdForKind { Entry back = PopEntry(); RequireIdKind(RequiredParseKind, Id::Kind::None); return parse_tree_->As>( back.node_id); } // Pops the top of the stack if it is the given kind, and returns the // node_id. Otherwise, returns std::nullopt. template auto PopForSoloNodeIdIf() -> std::optional> { if (PeekIs(RequiredParseKind)) { return PopForSoloNodeId(); } return std::nullopt; } // Pops the top of the stack. template auto PopAndDiscardSoloNodeId() -> void { PopForSoloNodeId(); } // Pops the top of the stack if it is the given kind. Returns `true` if a node // was popped. template auto PopAndDiscardSoloNodeIdIf() -> bool { if (!PeekIs(RequiredParseKind)) { return false; } PopForSoloNodeId(); return true; } // Pops an expression from the top of the stack and returns the node_id and // the ID. auto PopExprWithNodeId() -> std::pair; // Pops a pattern from the top of the stack and returns the node_id and // the ID. auto PopPatternWithNodeId() -> std::pair { return PopWithNodeId(); } // Pops a name from the top of the stack and returns the node_id and // the ID. auto PopNameWithNodeId() -> std::pair { return PopWithNodeId(); } // Pops the top of the stack and returns the node_id and the ID. template auto PopWithNodeId() -> auto { auto id = Peek(); auto node_id = parse_tree_->As>( stack_.pop_back_val().node_id); return std::make_pair(node_id, id); } // Pops the top of the stack and returns the node_id and the ID. template auto PopWithNodeId() -> auto { auto id = Peek(); auto node_id = parse_tree_->As>( stack_.pop_back_val().node_id); return std::make_pair(node_id, id); } // Pops an expression from the top of the stack and returns the ID. // Expressions always map Parse::NodeCategory::Expr nodes to SemIR::InstId. auto PopExpr() -> SemIR::InstId { return PopExprWithNodeId().second; } // Pops a pattern from the top of the stack and returns the ID. // Patterns map multiple Parse::NodeKinds to SemIR::InstId always. // TODO: TuplePatterns store an InstBlockId instead and must be dealt with as // a special case before calling this function. auto PopPattern() -> SemIR::InstId { return PopPatternWithNodeId().second; } // Pops a name from the top of the stack and returns the ID. auto PopName() -> SemIR::NameId { return PopNameWithNodeId().second; } // Pops the top of the stack and returns the ID. template auto Pop() -> auto { return PopWithNodeId().second; } // Pops the top of the stack and returns the ID. template auto Pop() -> auto { return PopWithNodeId().second; } // Pops the top of the stack and returns the ID. template auto Pop() -> IdT { return PopWithNodeId().second; } // Pops the top of the stack if it has the given kind, and returns the ID. // Otherwise returns std::nullopt. template auto PopIf() -> std::optional())> { if (PeekIs(RequiredParseKind)) { return Pop(); } return std::nullopt; } // Pops the top of the stack if it has the given category, and returns the ID. // Otherwise returns std::nullopt. template auto PopIf() -> std::optional())> { if (PeekIs(RequiredParseCategory)) { return Pop(); } return std::nullopt; } // Pops the top of the stack if it has the given category, and returns the ID. // Otherwise returns std::nullopt. template auto PopIf() -> std::optional { if (PeekIs()) { return Pop(); } return std::nullopt; } // Pops the top of the stack and returns the node_id and the ID if it is // of the specified kind. template auto PopWithNodeIdIf() -> std::pair, decltype(PopIf())> { if (!PeekIs(RequiredParseKind)) { return {Parse::NodeId::None, std::nullopt}; } return PopWithNodeId(); } // Pops the top of the stack and returns the node_id and the ID if it is // of the specified category. template auto PopWithNodeIdIf() -> std::pair, decltype(PopIf())> { if (!PeekIs(RequiredParseCategory)) { return {Parse::NodeId::None, std::nullopt}; } return PopWithNodeId(); } // Peeks at the parse node of the top of the node stack. auto PeekNodeId() const -> Parse::NodeId { return stack_.back().node_id; } // Peeks at the kind of the parse node of the top of the node stack. auto PeekNodeKind() const -> Parse::NodeKind { return parse_tree_->node_kind(PeekNodeId()); } // Peeks at the ID associated with the top of the name stack. template auto Peek() const -> auto { Entry back = stack_.back(); CARBON_CHECK(RequiredParseKind == parse_tree_->node_kind(back.node_id), "Expected {0}, found {1}", RequiredParseKind, parse_tree_->node_kind(back.node_id)); constexpr Id::Kind RequiredIdKind = NodeKindToIdKind(RequiredParseKind); return Peek(); } // Peeks at the ID associated with the top of the name stack. template auto Peek() const -> auto { Entry back = stack_.back(); RequireParseCategory(back.node_id); constexpr std::optional RequiredIdKind = NodeCategoryToIdKind(RequiredParseCategory, false); static_assert(RequiredIdKind.has_value()); return Peek<*RequiredIdKind>(); } // Peeks at the ID associated with the pattern at the top of the stack. // Patterns map multiple Parse::NodeKinds to SemIR::InstId always. // TODO: TuplePatterns store an InstBlockId instead and must be dealt with as // a special case before calling this function. auto PeekPattern() const -> SemIR::InstId; // Prints the stack for a stack dump. auto PrintForStackDump(int indent, llvm::raw_ostream& output) const -> void; auto empty() const -> bool { return stack_.empty(); } auto size() const -> size_t { return stack_.size(); } private: // An ID that can be associated with a parse node. // // Each parse node kind has a corresponding Id::Kind indicating which kind of // ID is stored, computed by NodeKindToIdKind. Id::Kind::None indicates // that the parse node has no associated ID, in which case the *SoloNodeId // functions should be used to push and pop it. Id::Kind::Invalid indicates // that the parse node should not appear in the node stack at all. using Id = IdUnion; // An entry in stack_. struct Entry { // The parse node associated with the stack entry. Parse::NodeId node_id; // The ID associated with this parse node. The kind of ID is determined by // the kind of the parse node, so a separate discriminiator is not needed. Id id; }; static_assert(sizeof(Entry) == 8, "Unexpected Entry size"); // Translate a parse node category to the enum ID kind it should always // provide, if it is consistent. static constexpr auto NodeCategoryToIdKind(Parse::NodeCategory category, bool for_node_kind) -> std::optional { std::optional result; auto set_id_if_category_is = [&](Parse::NodeCategory cat, Id::Kind kind) { if (category.HasAnyOf(cat)) { // Check for no consistent Id::Kind due to category with multiple bits // set. When computing the Id::Kind for a node kind, a partial category // match is OK, so long as we don't match two inconsistent categories. // When computing the Id::Kind for a category query, the query can't // have any extra bits set or we could be popping a node that is not in // this category. if (for_node_kind ? result.has_value() : category.HasAnyOf(~cat)) { result = Id::Kind::Invalid; } else { result = kind; } } }; set_id_if_category_is(Parse::NodeCategory::Pattern, Id::KindFor()); set_id_if_category_is(Parse::NodeCategory::Expr, Id::KindFor()); set_id_if_category_is( Parse::NodeCategory::MemberName | Parse::NodeCategory::NonExprName, Id::KindFor()); set_id_if_category_is(Parse::NodeCategory::ImplAs, Id::KindFor()); set_id_if_category_is(Parse::NodeCategory::RequireImpls, Id::KindFor()); set_id_if_category_is(Parse::NodeCategory::Decl | Parse::NodeCategory::Statement | Parse::NodeCategory::Modifier, Id::Kind::None); set_id_if_category_is(Parse::NodeCategory::ReturnDecl, Id::KindFor()); return result; } // Translate a parse node kind to the enum ID kind it should always // provide, for the cases where this is not known from the category. static constexpr auto NodeKindToIdKindSpecialCases(Parse::NodeKind node_kind) -> std::optional { switch (node_kind) { case Parse::NodeKind::CallExprStart: case Parse::NodeKind::FieldNameAndType: case Parse::NodeKind::IfExprThen: case Parse::NodeKind::RequireIntroducer: case Parse::NodeKind::ShortCircuitOperandAnd: case Parse::NodeKind::ShortCircuitOperandOr: case Parse::NodeKind::StructLiteralField: case Parse::NodeKind::WhereOperand: return Id::KindFor(); case Parse::NodeKind::ExplicitParamList: case Parse::NodeKind::ForIn: case Parse::NodeKind::IfCondition: case Parse::NodeKind::IfExprIf: case Parse::NodeKind::ImplicitParamList: case Parse::NodeKind::WhileConditionStart: return Id::KindFor(); case Parse::NodeKind::FunctionDefinitionStart: case Parse::NodeKind::BuiltinFunctionDefinitionStart: return Id::KindFor(); case Parse::NodeKind::ChoiceDefinitionStart: // TODO: Should we have a separate SemIR::ChoiceId? case Parse::NodeKind::ClassDefinitionStart: return Id::KindFor(); case Parse::NodeKind::InterfaceDefinitionStart: return Id::KindFor(); case Parse::NodeKind::ImplDefinitionStart: return Id::KindFor(); case Parse::NodeKind::NamedConstraintDefinitionStart: return Id::KindFor(); case Parse::NodeKind::SelfTypeName: case Parse::NodeKind::SelfValueName: return Id::KindFor(); case Parse::NodeKind::DefaultLibrary: case Parse::NodeKind::LibraryName: return Id::KindFor(); case Parse::NodeKind::AssociatedConstantInitializer: case Parse::NodeKind::AssociatedConstantIntroducer: case Parse::NodeKind::BuiltinName: case Parse::NodeKind::ChoiceIntroducer: case Parse::NodeKind::ClassIntroducer: case Parse::NodeKind::CodeBlockStart: case Parse::NodeKind::ExplicitParamListStart: case Parse::NodeKind::FieldInitializer: case Parse::NodeKind::FieldIntroducer: case Parse::NodeKind::ForHeaderStart: case Parse::NodeKind::FunctionIntroducer: case Parse::NodeKind::IfStatementElse: case Parse::NodeKind::ImplicitParamListStart: case Parse::NodeKind::ImplIntroducer: case Parse::NodeKind::InterfaceIntroducer: case Parse::NodeKind::LambdaIntroducer: case Parse::NodeKind::LetInitializer: case Parse::NodeKind::LetIntroducer: case Parse::NodeKind::NamedConstraintIntroducer: case Parse::NodeKind::ObserveIntroducer: case Parse::NodeKind::RefBindingName: case Parse::NodeKind::ReturnStatementStart: case Parse::NodeKind::StructLiteralStart: case Parse::NodeKind::StructTypeLiteralField: case Parse::NodeKind::StructTypeLiteralStart: case Parse::NodeKind::TemplateBindingName: case Parse::NodeKind::TupleLiteralStart: case Parse::NodeKind::TuplePatternStart: case Parse::NodeKind::VariableInitializer: case Parse::NodeKind::VariableIntroducer: return Id::Kind::None; case Parse::NodeKind::AdaptIntroducer: case Parse::NodeKind::AliasInitializer: case Parse::NodeKind::AliasIntroducer: case Parse::NodeKind::ArrayExprComma: case Parse::NodeKind::ArrayExprKeyword: case Parse::NodeKind::ArrayExprOpenParen: case Parse::NodeKind::BaseColon: case Parse::NodeKind::BaseIntroducer: case Parse::NodeKind::BreakStatementStart: case Parse::NodeKind::ChoiceAlternativeListComma: case Parse::NodeKind::CodeBlock: case Parse::NodeKind::CompileTimeBindingPatternStart: case Parse::NodeKind::ContinueStatementStart: case Parse::NodeKind::CorePackageName: case Parse::NodeKind::CppPackageName: case Parse::NodeKind::ExportIntroducer: case Parse::NodeKind::FileEnd: case Parse::NodeKind::FileStart: case Parse::NodeKind::ForHeader: case Parse::NodeKind::Forall: case Parse::NodeKind::FormLiteralKeyword: case Parse::NodeKind::FormLiteralOpenParen: case Parse::NodeKind::IdentifierNameQualifierWithParams: case Parse::NodeKind::IdentifierNameQualifierWithoutParams: case Parse::NodeKind::IdentifierPackageName: case Parse::NodeKind::IfConditionStart: case Parse::NodeKind::ImportIntroducer: case Parse::NodeKind::IndexExprStart: case Parse::NodeKind::InvalidParseStart: case Parse::NodeKind::LibraryIntroducer: case Parse::NodeKind::LibrarySpecifier: case Parse::NodeKind::InlineImportSpecifier: case Parse::NodeKind::InlineImportBody: case Parse::NodeKind::MatchCase: case Parse::NodeKind::MatchCaseGuard: case Parse::NodeKind::MatchCaseGuardIntroducer: case Parse::NodeKind::MatchCaseGuardStart: case Parse::NodeKind::MatchCaseIntroducer: case Parse::NodeKind::MatchCondition: case Parse::NodeKind::MatchConditionStart: case Parse::NodeKind::MatchDefault: case Parse::NodeKind::MatchDefaultIntroducer: case Parse::NodeKind::MatchHandlerStart: case Parse::NodeKind::MatchHandler: case Parse::NodeKind::MatchIntroducer: case Parse::NodeKind::MatchStatementStart: case Parse::NodeKind::NamespaceStart: case Parse::NodeKind::ObserveEqualEqual: case Parse::NodeKind::ObserveImpls: case Parse::NodeKind::PackageIntroducer: case Parse::NodeKind::ParenExprStart: case Parse::NodeKind::PatternListComma: case Parse::NodeKind::Placeholder: case Parse::NodeKind::RequirementAnd: case Parse::NodeKind::RequirementEqual: case Parse::NodeKind::RequirementEqualEqual: case Parse::NodeKind::RequirementImpls: case Parse::NodeKind::StructLiteralComma: case Parse::NodeKind::StructFieldDesignator: case Parse::NodeKind::StructTypeLiteralComma: case Parse::NodeKind::TerseBodyArrow: case Parse::NodeKind::TupleLiteralComma: case Parse::NodeKind::WhileCondition: return Id::Kind::Invalid; default: // In this case, the kind must be determinable from the category, or we // will produce a build error. return std::nullopt; } } using IdKindTableType = std::array; // Lookup table to implement `NodeKindToIdKind`. Initialized to the // return value of `ComputeIdKindTable()`. static const IdKindTableType IdKindTable; static consteval auto ComputeIdKindTable() -> IdKindTableType { IdKindTableType table = {}; auto to_id_kind = [](const Parse::NodeKind::Definition& node_kind) -> Id::Kind { if (auto from_category = NodeCategoryToIdKind(node_kind.category(), true)) { return *from_category; } // Assume any node kind that doesn't have an ID kind from its category nor // a special case can't appear on the stack just so we can build a table // and avoid follow-on errors. We'll enforce at compile time that a value // is actually specified in CheckIdKindTable. return NodeKindToIdKindSpecialCases(node_kind).value_or( Id::Kind::Invalid); }; #define CARBON_PARSE_NODE_KIND(Name) \ table[Parse::Name::Kind.AsInt()] = to_id_kind(Parse::Name::Kind); #include "toolchain/parse/node_kind.def" return table; } // Check that an Id::Kind is specified for every node kind. static auto CheckIdKindTable() -> void; // Translate a parse node kind to the enum ID kind it should always provide. static constexpr auto NodeKindToIdKind(Parse::NodeKind kind) -> Id::Kind { return IdKindTable[kind.AsInt()]; } // Peeks at the ID associated with the top of the name stack. template auto Peek() const -> auto { Id id = stack_.back().id; return id.As(); } // Pops an entry. template auto PopEntry() -> Entry { Entry back = stack_.pop_back_val(); CARBON_VLOG("Node Pop {0}: {1} -> {2}\n", stack_.size(), parse_tree_->node_kind(back.node_id), back.id.template As()); return back; } // Pops the top of the stack and returns the node_id and the ID. template auto PopWithNodeId() -> std::pair { Entry back = PopEntry(); RequireIdKind(parse_tree_->node_kind(back.node_id), Id::KindFor()); return {back.node_id, back.id.template As()}; } // Require a Parse::NodeKind be mapped to a particular Id::Kind. auto RequireIdKind(Parse::NodeKind parse_kind, Id::Kind id_kind) const -> void { CARBON_CHECK(NodeKindToIdKind(parse_kind) == id_kind, "Unexpected Id::Kind mapping for {0}: expected {1}, found {2}", parse_kind, SemIR::IdKind(id_kind), SemIR::IdKind(NodeKindToIdKind(parse_kind))); } // Require an entry to have the given Parse::NodeCategory. template auto RequireParseCategory(Parse::NodeId node_id) const -> void { auto kind = parse_tree_->node_kind(node_id); CARBON_CHECK(kind.category().HasAnyOf(RequiredParseCategory), "Expected {0}, found {1} with category {2}", RequiredParseCategory, kind, kind.category()); } // The file's parse tree. const Parse::Tree* parse_tree_; // Whether to print verbose output. llvm::raw_ostream* vlog_stream_; // The actual stack. // PushEntry and PopEntry control modification in order to centralize // vlogging. llvm::SmallVector stack_; }; inline constexpr NodeStack::IdKindTableType NodeStack::IdKindTable = ComputeIdKindTable(); inline auto NodeStack::PopExprWithNodeId() -> std::pair { return PopWithNodeId(); } inline auto NodeStack::PeekPattern() const -> SemIR::InstId { return Peek()>(); } } // namespace Carbon::Check #endif // CARBON_TOOLCHAIN_CHECK_NODE_STACK_H_ ================================================ FILE: toolchain/check/operator.cpp ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #include "toolchain/check/operator.h" #include #include "toolchain/check/call.h" #include "toolchain/check/context.h" #include "toolchain/check/cpp/call.h" #include "toolchain/check/cpp/operators.h" #include "toolchain/check/generic.h" #include "toolchain/check/member_access.h" #include "toolchain/check/name_lookup.h" #include "toolchain/sem_ir/class.h" #include "toolchain/sem_ir/ids.h" #include "toolchain/sem_ir/name_scope.h" #include "toolchain/sem_ir/typed_insts.h" namespace Carbon::Check { // Returns the `Op` function for the specified operator. static auto GetOperatorOpFunction(Context& context, SemIR::LocId loc_id, Operator op) -> SemIR::InstId { auto implicit_loc_id = context.insts().GetLocIdForDesugaring(loc_id); // Look up the interface, and pass it any generic arguments. // TODO: Improve diagnostics when the found `interface_id` isn't callable. auto interface_id = LookupNameInCore(context, implicit_loc_id, op.interface_name); if (!op.interface_args_ref.empty()) { interface_id = PerformCall(context, implicit_loc_id, interface_id, op.interface_args_ref); } // Look up the interface member. auto op_name_id = context.core_identifiers().AddNameId(op.op_name); return PerformMemberAccess(context, implicit_loc_id, interface_id, op_name_id); } // Returns whether the instruction is a C++ class type. Assumes the argument is // in canonical form and does not look through the constant value. static auto IsCppClassType(Context& context, SemIR::InstId inst_id) -> bool { auto class_type = context.insts().TryGetAs(inst_id); if (!class_type) { // Not a class. return false; } SemIR::NameScopeId class_scope_id = context.classes().Get(class_type->class_id).scope_id; return class_scope_id.has_value() && context.name_scopes().Get(class_scope_id).is_cpp_scope(); } // Returns whether the instruction is a value of C++ class type. static auto HasCppClassType(Context& context, SemIR::InstId inst_id) -> bool { return IsCppClassType(context, context.types().GetTypeInstId( context.insts().Get(inst_id).type_id())); } auto BuildUnaryOperator(Context& context, SemIR::LocId loc_id, Operator op, SemIR::InstId operand_id, bool diagnose, DiagnosticContextFn missing_impl_diagnostic_context) -> SemIR::InstId { if (operand_id == SemIR::ErrorInst::InstId) { // Exit early for errors, which prevent forming an `Op` function. return SemIR::ErrorInst::InstId; } SemIR::InstId op_fn_id = SemIR::InstId::None; // For unary operators with a C++ class as the operand, try to import and call // the C++ operator. // TODO: Change impl lookup instead. See // https://github.com/carbon-language/carbon-lang/blob/db0a00d713015436844c55e7ac190a0f95556499/toolchain/check/operator.cpp#L76 if (HasCppClassType(context, operand_id) || llvm::any_of(op.interface_args_ref, [&](SemIR::InstId arg_id) { return IsCppClassType(context, arg_id); })) { op_fn_id = LookupCppOperator(context, loc_id, op, {operand_id}); // If C++ operator lookup found a non-method operator, call it with one call // argument. Otherwise fall through to call it with a self argument. if (op_fn_id.has_value() && !IsCppOperatorMethod(context, op_fn_id)) { return PerformCall(context, loc_id, op_fn_id, {operand_id}, /*is_operator_syntax=*/true); } } if (!op_fn_id.has_value()) { // Look up the operator function. op_fn_id = GetOperatorOpFunction(context, loc_id, op); } // Form `operand.(Op)`. auto bound_op_id = PerformCompoundMemberAccess(context, loc_id, operand_id, op_fn_id, diagnose, missing_impl_diagnostic_context); if (bound_op_id == SemIR::ErrorInst::InstId) { return SemIR::ErrorInst::InstId; } // Form `bound_op()`. return PerformCall(context, loc_id, bound_op_id, {}, /*is_operator_syntax=*/true); } auto BuildBinaryOperator(Context& context, SemIR::LocId loc_id, Operator op, SemIR::InstId lhs_id, SemIR::InstId rhs_id, bool diagnose, DiagnosticContextFn missing_impl_diagnostic_context) -> SemIR::InstId { if (lhs_id == SemIR::ErrorInst::InstId) { // Exit early for errors, which prevent forming an `Op` function. return SemIR::ErrorInst::InstId; } SemIR::InstId op_fn_id = SemIR::InstId::None; // For binary operators with a C++ class as at least one of the operands, try // to import and call the C++ operator. // TODO: Instead of hooking this here, change impl lookup, so that a generic // constraint such as `T:! Core.Add` is satisfied by C++ class types that are // addable. See // https://github.com/carbon-language/carbon-lang/pull/5996/files/5d01fa69511b76f87efbc0387f5e40abcf4c911a#r2308666348 // and // https://github.com/carbon-language/carbon-lang/pull/5996/files/5d01fa69511b76f87efbc0387f5e40abcf4c911a#r2308664536 if (HasCppClassType(context, lhs_id) || HasCppClassType(context, rhs_id) || llvm::any_of(op.interface_args_ref, [&](SemIR::InstId arg_id) { return IsCppClassType(context, arg_id); })) { op_fn_id = LookupCppOperator(context, loc_id, op, {lhs_id, rhs_id}); // If C++ operator lookup found a non-method operator, call it with two call // arguments. Otherwise fall through to call it with a self argument and one // call argument. if (op_fn_id.has_value() && !IsCppOperatorMethod(context, op_fn_id)) { return PerformCall(context, loc_id, op_fn_id, {lhs_id, rhs_id}, /*is_operator_syntax=*/true); } } if (!op_fn_id.has_value()) { // Look up the operator function. op_fn_id = GetOperatorOpFunction(context, loc_id, op); } // Form `lhs.(Op)`. auto bound_op_id = PerformCompoundMemberAccess(context, loc_id, lhs_id, op_fn_id, diagnose, missing_impl_diagnostic_context); if (bound_op_id == SemIR::ErrorInst::InstId) { return SemIR::ErrorInst::InstId; } // Form `bound_op(rhs)`. return PerformCall(context, loc_id, bound_op_id, {rhs_id}, /*is_operator_syntax=*/true); } } // namespace Carbon::Check ================================================ FILE: toolchain/check/operator.h ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #ifndef CARBON_TOOLCHAIN_CHECK_OPERATOR_H_ #define CARBON_TOOLCHAIN_CHECK_OPERATOR_H_ #include "toolchain/check/context.h" #include "toolchain/check/core_identifier.h" #include "toolchain/parse/node_ids.h" #include "toolchain/sem_ir/ids.h" namespace Carbon::Check { struct Operator { CoreIdentifier interface_name; llvm::ArrayRef interface_args_ref = {}; CoreIdentifier op_name = CoreIdentifier::Op; }; // Checks and builds SemIR for a unary operator expression. For example, // `*operand` or `operand*`. // // On failure, an ErrorInst is returned and a diagnostic is produced unless // `diagnose` is false. It is incorrect to specify `diagnose` as false if the // resulting ErrorInst may appear in the produced SemIR. // // If specified, `missing_impl_diagnostic_context` is used to provide context // for the diagnostic if the impl lookup for the operator fails. auto BuildUnaryOperator(Context& context, SemIR::LocId loc_id, Operator op, SemIR::InstId operand_id, bool diagnose = true, DiagnosticContextFn missing_impl_diagnostic_context = nullptr) -> SemIR::InstId; // Checks and builds SemIR for a binary operator expression. For example, // `lhs_id * rhs_id`. // // // On failure, an ErrorInst is returned and a diagnostic is produced unless // `diagnose` is false. It is incorrect to specify `diagnose` as false if the // resulting ErrorInst may appear in the produced SemIR. // // If specified, `missing_impl_diagnostic_context` is used to provide context // for the diagnostic if the impl lookup for the operator fails. auto BuildBinaryOperator( Context& context, SemIR::LocId loc_id, Operator op, SemIR::InstId lhs_id, SemIR::InstId rhs_id, bool diagnose = true, DiagnosticContextFn missing_impl_diagnostic_context = nullptr) -> SemIR::InstId; } // namespace Carbon::Check #endif // CARBON_TOOLCHAIN_CHECK_OPERATOR_H_ ================================================ FILE: toolchain/check/param_and_arg_refs_stack.h ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #ifndef CARBON_TOOLCHAIN_CHECK_PARAM_AND_ARG_REFS_STACK_H_ #define CARBON_TOOLCHAIN_CHECK_PARAM_AND_ARG_REFS_STACK_H_ #include "common/check.h" #include "toolchain/check/inst_block_stack.h" #include "toolchain/check/node_stack.h" namespace Carbon::Check { // The stack of instruction blocks being used for per-element tracking of // instructions in parameter and argument instruction blocks. Versus // InstBlockStack, an element will have 1 or more instructions in blocks in // InstBlockStack, but only ever 1 instruction in blocks here. The result is // typically referred to as "param_refs" or "arg_refs". class ParamAndArgRefsStack { public: explicit ParamAndArgRefsStack(SemIR::File& sem_ir, llvm::raw_ostream* vlog_stream, NodeStack& node_stack) : node_stack_(&node_stack), stack_("param_and_arg_refs_stack", sem_ir, vlog_stream) {} // Starts handling parameters or arguments. auto Push() -> void { stack_.Push(); } // On a comma, pushes the most recent instruction, becoming param or arg ref. // This also pops the NodeStack, meaning its top will remain start_kind. auto ApplyComma() -> void { // Support expressions, parameters, and other nodes like // `StructLiteralField` that produce InstIds. stack_.AddInstId(node_stack_->Pop()); } // Detects whether there's an entry to push from the end of a parameter or // argument list, and if so, moves it to the current parameter or argument // list. Does not pop the list. `start_kind` is the node kind at the start // of the parameter or argument list, and will be at the top of the parse node // stack when this function returns. auto EndNoPop(Parse::NodeKind start_kind) -> void { if (!node_stack_->PeekIs(start_kind)) { // Support expressions, parameters, and other nodes like // `StructLiteralField` that produce InstIds. stack_.AddInstId(node_stack_->Pop()); } } // Pops the current parameter or argument list. Should only be called after // `EndNoPop`. auto Pop() -> SemIR::InstBlockId { return stack_.Pop(); } // Detects whether there's an entry to push. Pops and returns the argument // list. This is the same as `EndNoPop` followed by `Pop`. auto EndAndPop(Parse::NodeKind start_kind) -> SemIR::InstBlockId { EndNoPop(start_kind); return Pop(); } // Pops the top instruction block, and discards it if it hasn't had an ID // allocated. auto PopAndDiscard() -> void { stack_.PopAndDiscard(); } // Returns a view of the contents of the top instruction block on the stack. auto PeekCurrentBlockContents() -> llvm::ArrayRef { return stack_.PeekCurrentBlockContents(); } // Runs verification that the processing cleanly finished. auto VerifyOnFinish() const -> void { stack_.VerifyOnFinish(); } // Prints the stack for a stack dump. auto PrintForStackDump(int indent, llvm::raw_ostream& output) const -> void { stack_.PrintForStackDump(indent, output); } private: // The node stack is manipulated when adding refs. NodeStack* node_stack_; // The refs stack. InstBlockStack stack_; }; } // namespace Carbon::Check #endif // CARBON_TOOLCHAIN_CHECK_PARAM_AND_ARG_REFS_STACK_H_ ================================================ FILE: toolchain/check/pattern.cpp ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #include "toolchain/check/pattern.h" #include "toolchain/check/control_flow.h" #include "toolchain/check/inst.h" #include "toolchain/check/return.h" #include "toolchain/check/type.h" namespace Carbon::Check { auto BeginSubpattern(Context& context) -> void { context.inst_block_stack().Push(); context.region_stack().PushRegion(context.inst_block_stack().PeekOrAdd()); } auto EndSubpatternAsExpr(Context& context, SemIR::InstId result_id) -> SemIR::ExprRegionId { if (context.region_stack().PeekRegion().size() > 1) { // End the exit block with a branch to a successor block, whose contents // will be determined later. AddInst(context, SemIR::LocIdAndInst::NoLoc( {.target_id = context.inst_blocks().AddPlaceholder()})); } else { // This single-block region will be inserted as a SpliceBlock, so we don't // need control flow out of it. } auto block_id = context.inst_block_stack().Pop(); CARBON_CHECK(block_id == context.region_stack().PeekRegion().back()); // TODO: Is it possible to validate that this region is genuinely // single-entry, single-exit? return context.sem_ir().expr_regions().Add( {.block_ids = context.region_stack().PopRegion(), .result_id = result_id}); } auto EndSubpatternAsNonExpr(Context& context) -> void { auto block_id = context.inst_block_stack().Pop(); CARBON_CHECK(block_id == context.region_stack().PeekRegion().back()); CARBON_CHECK(context.region_stack().PeekRegion().size() == 1); CARBON_CHECK(context.inst_blocks().Get(block_id).empty()); context.region_stack().PopAndDiscardRegion(); } auto AddBindingEntityName(Context& context, SemIR::NameId name_id, SemIR::ConstantId form_id, bool is_unused, BindingPhase phase) -> SemIR::EntityNameId { SemIR::EntityName entity_name = { .name_id = name_id, .parent_scope_id = context.scope_stack().PeekNameScopeId(), .is_unused = is_unused || name_id == SemIR::NameId::Underscore}; if (phase != BindingPhase::Runtime) { entity_name.bind_index_value = context.scope_stack().AddCompileTimeBinding().index; entity_name.is_template = phase == BindingPhase::Template; } entity_name.form_id = form_id; return context.entity_names().Add(entity_name); } auto AddBindingPattern(Context& context, SemIR::LocId name_loc, SemIR::ExprRegionId type_region_id, SemIR::AnyBindingPattern pattern) -> BindingPatternInfo { SemIR::InstKind bind_name_kind; switch (pattern.kind) { case SemIR::InstKind::FormBindingPattern: bind_name_kind = SemIR::InstKind::FormBinding; break; case SemIR::InstKind::RefBindingPattern: bind_name_kind = SemIR::InstKind::RefBinding; break; case SemIR::InstKind::SymbolicBindingPattern: bind_name_kind = SemIR::InstKind::SymbolicBinding; break; case SemIR::InstKind::ValueBindingPattern: bind_name_kind = SemIR::InstKind::ValueBinding; break; default: CARBON_FATAL("pattern_kind {0} is not a binding pattern kind", pattern.kind); } auto type_id = SemIR::ExtractScrutineeType(context.sem_ir(), pattern.type_id); auto bind_id = AddInstInNoBlock( context, SemIR::LocIdAndInst::UncheckedLoc( name_loc, SemIR::AnyBinding{.kind = bind_name_kind, .type_id = type_id, .entity_name_id = pattern.entity_name_id, .value_id = SemIR::InstId::None})); auto binding_pattern_id = AddPatternInst( context, SemIR::LocIdAndInst::UncheckedLoc(name_loc, pattern)); if (pattern.kind == SemIR::SymbolicBindingPattern::Kind) { context.scope_stack().PushCompileTimeBinding(bind_id); } bool inserted = context.bind_name_map() .Insert(binding_pattern_id, {.bind_name_id = bind_id, .type_expr_region_id = type_region_id}) .is_inserted(); CARBON_CHECK(inserted); return {.pattern_id = binding_pattern_id, .bind_id = bind_id}; } // Returns a VarStorage inst for the given `var` pattern. If the pattern // is the body of a returned var, this reuses the return parameter, and // otherwise it adds a new inst. static auto GetOrAddVarStorage(Context& context, SemIR::InstId var_pattern_id, bool is_returned_var) -> SemIR::InstId { if (is_returned_var) { if (auto return_param_id = GetReturnedVarParam(context, GetCurrentFunctionForReturn(context)); return_param_id.has_value()) { return return_param_id; } } auto pattern = context.insts().GetWithLocId(var_pattern_id); return AddInstWithCleanup( context, pattern.loc_id, SemIR::VarStorage{.type_id = ExtractScrutineeType(context.sem_ir(), pattern.inst.type_id()), .pattern_id = var_pattern_id}); } auto AddPatternVarStorage(Context& context, SemIR::InstBlockId pattern_block_id, bool is_returned_var) -> void { // We need to emit the VarStorage insts early, because they may be output // arguments for the initializer. However, we can't emit them when we emit // the corresponding `VarPattern`s because they're part of the pattern match, // not part of the pattern. // TODO: Find a way to do this without walking the whole pattern block. for (auto inst_id : context.inst_blocks().Get(pattern_block_id)) { if (context.insts().Is(inst_id)) { context.var_storage_map().Insert( inst_id, GetOrAddVarStorage(context, inst_id, is_returned_var)); } } } auto AddParamPattern(Context& context, SemIR::LocId loc_id, SemIR::NameId name_id, SemIR::ExprRegionId type_expr_region_id, SemIR::TypeId type_id, bool is_ref) -> SemIR::InstId { const auto& binding_pattern_kind = is_ref ? SemIR::RefBindingPattern::Kind : SemIR::ValueBindingPattern::Kind; auto entity_name_id = AddBindingEntityName(context, name_id, /*form_id=*/SemIR::ConstantId::None, /*is_unused=*/false, /*phase=*/BindingPhase::Runtime); SemIR::InstId pattern_id = AddBindingPattern(context, loc_id, type_expr_region_id, {.kind = binding_pattern_kind, .type_id = GetPatternType(context, type_id), .entity_name_id = entity_name_id}) .pattern_id; const auto& param_pattern_kind = is_ref ? SemIR::RefParamPattern::Kind : SemIR::ValueParamPattern::Kind; pattern_id = AddPatternInst( context, SemIR::LocIdAndInst::UncheckedLoc( loc_id, SemIR::AnyParamPattern{ .kind = param_pattern_kind, .type_id = context.insts().Get(pattern_id).type_id(), .subpattern_id = pattern_id, .form_id = SemIR::ConstantId::None})); return pattern_id; } } // namespace Carbon::Check ================================================ FILE: toolchain/check/pattern.h ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #ifndef CARBON_TOOLCHAIN_CHECK_PATTERN_H_ #define CARBON_TOOLCHAIN_CHECK_PATTERN_H_ #include "toolchain/check/context.h" #include "toolchain/sem_ir/ids.h" namespace Carbon::Check { // Marks the start of a region of insts in a pattern context that might // represent an expression or a pattern. Typically this is called when // handling a parse node that can immediately precede a subpattern (such // as `let` or a `,` in a pattern list), and the handler for the subpattern // node makes the matching `EndSubpatternAs*` call. auto BeginSubpattern(Context& context) -> void; // Ends a region started by BeginSubpattern (in stack order), treating it as // an expression with the given result, and returns the ID of the region. The // region will not yet have any control-flow edges into or out of it. auto EndSubpatternAsExpr(Context& context, SemIR::InstId result_id) -> SemIR::ExprRegionId; // Ends a region started by BeginSubpattern (in stack order), asserting that // it had no expression content. auto EndSubpatternAsNonExpr(Context& context) -> void; // Information about a created binding pattern. struct BindingPatternInfo { SemIR::InstId pattern_id; SemIR::InstId bind_id; }; // TODO: Add EndSubpatternAsPattern, when needed. // The phase of a binding pattern. enum class BindingPhase { Template, Symbolic, Runtime }; // Creates an entity name for a binding pattern with the given properties. auto AddBindingEntityName(Context& context, SemIR::NameId name_id, SemIR::ConstantId form_id, bool is_unused, BindingPhase phase) -> SemIR::EntityNameId; // Creates a binding pattern and the associated binding inst, and returns their // IDs. `type_region_id` is the region representing the binding's type // expression. auto AddBindingPattern(Context& context, SemIR::LocId name_loc, SemIR::ExprRegionId type_region_id, SemIR::AnyBindingPattern pattern) -> BindingPatternInfo; // Creates storage for `var` patterns nested within the given pattern at the // current location in the output SemIR. For a `returned var`, this // reuses the function's return slot when present. auto AddPatternVarStorage(Context& context, SemIR::InstBlockId pattern_block_id, bool is_returned_var) -> void; // Adds a parameter pattern with the specified name and type information. The // pattern emulates `x: T` or `ref x: T` depending on the value of // `is_ref` (`var x: T` is not supported). This only sets up the parameter // pattern, binding pattern and type; callers are expected to add the returned // parameter pattern instruction to appropriate blocks. This is used when // generating functions, rather than processing a user-authored declaration. auto AddParamPattern(Context& context, SemIR::LocId loc_id, SemIR::NameId name_id, SemIR::ExprRegionId type_expr_region_id, SemIR::TypeId type_id, bool is_ref) -> SemIR::InstId; } // namespace Carbon::Check #endif // CARBON_TOOLCHAIN_CHECK_PATTERN_H_ ================================================ FILE: toolchain/check/pattern_match.cpp ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #include "toolchain/check/pattern_match.h" #include #include #include #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/SmallVector.h" #include "toolchain/base/kind_switch.h" #include "toolchain/check/context.h" #include "toolchain/check/control_flow.h" #include "toolchain/check/convert.h" #include "toolchain/check/pattern.h" #include "toolchain/check/type.h" #include "toolchain/diagnostics/format_providers.h" #include "toolchain/sem_ir/expr_info.h" #include "toolchain/sem_ir/pattern.h" namespace Carbon::Check { namespace { // Selects between the different kinds of pattern matching. enum class MatchKind : uint8_t { // Caller pattern matching occurs on the caller side of a function call, and // is responsible for matching the argument expression against the portion // of the pattern above the ParamPattern insts. Caller, // Callee pattern matching occurs in the function decl block, and is // responsible for matching the function's calling-convention parameters // against the portion of the pattern below the ParamPattern insts. Callee, // Local pattern matching is pattern matching outside of a function call, // such as in a let/var declaration. Local, }; // The collected state of a pattern-matching operation. class MatchContext { public: struct WorkItem : Printable { SemIR::InstId pattern_id; // `None` when processing the callee side. SemIR::InstId scrutinee_id; // If true, disables diagnostics that would otherwise require scrutinee_id // to be tagged with `ref`. Only affects caller pattern matching. bool allow_unmarked_ref = false; auto Print(llvm::raw_ostream& out) const -> void { out << "{pattern_id: " << pattern_id << ", scrutinee_id: " << scrutinee_id << ", allow_unmarked_ref = " << allow_unmarked_ref << "}"; } }; // Constructs a MatchContext. If `callee_specific_id` is not `None`, this // pattern match operation is part of implementing the signature of the given // specific. explicit MatchContext(MatchKind kind, SemIR::SpecificId callee_specific_id = SemIR::SpecificId::None) : kind_(kind), callee_specific_id_(callee_specific_id) {} // Adds a work item to the stack. auto AddWork(WorkItem work_item) -> void { stack_.push_back(work_item); } // Processes all work items on the stack. auto DoWork(Context& context) -> void; // Returns an inst block of references to all the emitted `Call` arguments. // Can only be called once, at the end of Caller pattern matching. auto CallerResults(Context& context) && -> SemIR::InstBlockId; // Returns an inst block of references to all the emitted `Call` params, // and an inst block of references to the `Call` param patterns they were // emitted to match. Can only be called once, at the end of Callee pattern // matching. struct ParamsAndPatterns { SemIR::InstBlockId call_param_patterns_id; SemIR::InstBlockId call_params_id; }; auto CalleeResults(Context& context) && -> ParamsAndPatterns; // Returns the number of call parameters that have been emitted so far. auto param_count() -> int { return call_params_.size(); } ~MatchContext(); private: // Emits the pattern-match insts necessary to match the pattern inst // `entry.pattern_id` against the scrutinee value `entry.scrutinee_id`, and // adds to `stack_` any work necessary to traverse into its subpatterns. This // behavior is contingent on the kind of match being performed, as indicated // by kind_`. For example, when performing a callee pattern match, this does // not emit insts for patterns on the caller side. However, it still traverses // into subpatterns if any of their descendants might emit insts. // TODO: Require that `entry.scrutinee_id` is valid if and only if insts // should be emitted, once we start emitting `Param` insts in the // `ParamPattern` case. auto EmitPatternMatch(Context& context, MatchContext::WorkItem entry) -> void; // Implementations of `EmitPatternMatch` for particular pattern inst kinds. auto DoEmitPatternMatch(Context& context, SemIR::AnyBindingPattern binding_pattern, WorkItem entry) -> void; auto DoEmitPatternMatch(Context& context, SemIR::AnyParamPattern param_pattern, WorkItem entry) -> void; auto DoEmitPatternMatch(Context& context, SemIR::ReturnSlotPattern return_slot_pattern, WorkItem entry) -> void; auto DoEmitPatternMatch(Context& context, SemIR::VarPattern var_pattern, WorkItem entry) -> void; auto DoEmitPatternMatch(Context& context, SemIR::TuplePattern tuple_pattern, WorkItem entry) -> void; // Performs the core logic of matching a variable pattern whose type is // `pattern_type_id`, but returns the scrutinee that its subpattern should be // matched with, rather than pushing it onto the worklist. This is factored // out so it can be reused when handling a `FormBindingPattern` or // `FormParamPattern` with an initializing form. auto DoEmitVarPatternMatchImpl(Context& context, SemIR::TypeId pattern_type_id, WorkItem entry) const -> SemIR::InstId; // The stack of work to be processed. llvm::SmallVector stack_; // The in-progress contents of the `Call` arguments block. This is populated // only when kind_ is Caller. llvm::SmallVector call_args_; // The in-progress contents of the `Call` parameters block. This is populated // only when kind_ is Callee. llvm::SmallVector call_params_; // The in-progress contents of the `Call` parameter patterns block. This is // populated only when kind_ is Callee. llvm::SmallVector call_param_patterns_; // The kind of pattern match being performed. MatchKind kind_; // The SpecificId of the function being called (if any). SemIR::SpecificId callee_specific_id_; }; } // namespace auto MatchContext::DoWork(Context& context) -> void { while (!stack_.empty()) { EmitPatternMatch(context, stack_.pop_back_val()); } } auto MatchContext::CallerResults(Context& context) && -> SemIR::InstBlockId { CARBON_CHECK(kind_ == MatchKind::Caller); auto block_id = context.inst_blocks().Add(call_args_); call_args_.clear(); return block_id; } auto MatchContext::CalleeResults(Context& context) && -> ParamsAndPatterns { CARBON_CHECK(kind_ == MatchKind::Callee); CARBON_CHECK(call_params_.size() == call_param_patterns_.size()); auto call_param_patterns_id = context.inst_blocks().Add(call_param_patterns_); call_param_patterns_.clear(); auto call_params_id = context.inst_blocks().Add(call_params_); call_params_.clear(); return {.call_param_patterns_id = call_param_patterns_id, .call_params_id = call_params_id}; } MatchContext::~MatchContext() { CARBON_CHECK(call_args_.empty() && call_params_.empty() && call_param_patterns_.empty(), "Unhandled pattern matching outputs. call_args_.size(): {0}, " "call_params_.size(): {1}, call_param_patterns_.size(): {2}", call_args_.size(), call_params_.size(), call_param_patterns_.size()); } // Inserts the given region into the current code block. If the region // consists of a single block, this will be implemented as a `splice_block` // inst. Otherwise, this will end the current block with a branch to the entry // block of the region, and add future insts to a new block which is the // immediate successor of the region's exit block. As a result, this cannot be // called more than once for the same region. static auto InsertHere(Context& context, SemIR::ExprRegionId region_id) -> SemIR::InstId { auto region = context.sem_ir().expr_regions().Get(region_id); auto exit_block = context.inst_blocks().Get(region.block_ids.back()); if (region.block_ids.size() == 1) { // TODO: Is it possible to avoid leaving an "orphan" block in the IR in the // first two cases? if (exit_block.empty()) { return region.result_id; } if (exit_block.size() == 1) { context.inst_block_stack().AddInstId(exit_block.front()); return region.result_id; } return AddInst( context, SemIR::LocId(region.result_id), {.type_id = context.insts().Get(region.result_id).type_id(), .block_id = region.block_ids.front(), .result_id = region.result_id}); } if (context.region_stack().empty()) { context.TODO(region.result_id, "Control flow expressions are currently only supported inside " "functions."); return SemIR::ErrorInst::InstId; } AddInst(context, SemIR::LocIdAndInst::NoLoc( {.target_id = region.block_ids.front()})); context.inst_block_stack().Pop(); // TODO: this will cumulatively cost O(MN) running time for M blocks // at the Nth level of the stack. Figure out how to do better. context.region_stack().AddToRegion(region.block_ids); auto resume_with_block_id = context.insts().GetAs(exit_block.back()).target_id; CARBON_CHECK(context.inst_blocks().GetOrEmpty(resume_with_block_id).empty()); context.inst_block_stack().Push(resume_with_block_id); context.region_stack().AddToRegion(resume_with_block_id, SemIR::LocId(region.result_id)); return region.result_id; } // Returns the kind of conversion to perform on the scrutinee when matching the // given pattern. static auto ConversionKindFor(Context& context, SemIR::Inst pattern, MatchContext::WorkItem entry) -> ConversionTarget::Kind { CARBON_KIND_SWITCH(pattern) { case SemIR::OutParamPattern::Kind: case SemIR::VarParamPattern::Kind: return ConversionTarget::NoOp; case SemIR::RefBindingPattern::Kind: return ConversionTarget::DurableRef; case SemIR::RefParamPattern::Kind: return entry.allow_unmarked_ref ? ConversionTarget::UnmarkedRefParam : ConversionTarget::RefParam; case SemIR::SymbolicBindingPattern::Kind: case SemIR::ValueBindingPattern::Kind: case SemIR::ValueParamPattern::Kind: return ConversionTarget::Value; case CARBON_KIND(SemIR::FormBindingPattern form_binding_pattern): { auto form_id = context.entity_names() .Get(form_binding_pattern.entity_name_id) .form_id; auto form_inst_id = context.constant_values().GetInstId(form_id); auto form_inst = context.insts().Get(form_inst_id); switch (form_inst.kind()) { case SemIR::InitForm::Kind: context.TODO(entry.pattern_id, "Support local initializing forms"); [[fallthrough]]; case SemIR::RefForm::Kind: return ConversionTarget::DurableRef; case SemIR::SymbolicBinding::Kind: context.TODO(entry.pattern_id, "Support symbolic form bindings"); [[fallthrough]]; case SemIR::ValueForm::Kind: return ConversionTarget::Value; default: CARBON_FATAL("Unexpected form {0}", form_inst); } } case CARBON_KIND(SemIR::FormParamPattern form_param_pattern): { auto form_inst_id = context.constant_values().GetInstId(form_param_pattern.form_id); auto form_inst = context.insts().Get(form_inst_id); switch (form_inst.kind()) { case SemIR::InitForm::Kind: return ConversionTarget::NoOp; case SemIR::RefForm::Kind: // TODO: Figure out rules for when the argument must have a `ref` tag. return entry.allow_unmarked_ref ? ConversionTarget::UnmarkedRefParam : ConversionTarget::RefParam; case SemIR::SymbolicBinding::Kind: context.TODO(entry.pattern_id, "Support symbolic form params"); [[fallthrough]]; case SemIR::ErrorInst::Kind: case SemIR::ValueForm::Kind: return ConversionTarget::Value; default: CARBON_FATAL("Unexpected form {0}", form_inst); } } default: CARBON_FATAL("Unexpected pattern kind in {0}", pattern); } } auto MatchContext::DoEmitPatternMatch(Context& context, SemIR::AnyBindingPattern binding_pattern, MatchContext::WorkItem entry) -> void { if (kind_ == MatchKind::Caller) { CARBON_CHECK( binding_pattern.kind == SemIR::SymbolicBindingPattern::Kind, "Found named runtime binding pattern during caller pattern match"); return; } // We're logically consuming this map entry, so we invalidate it in order // to avoid accidentally consuming it twice. auto [bind_name_id, type_expr_region_id] = std::exchange(context.bind_name_map().Lookup(entry.pattern_id).value(), {.bind_name_id = SemIR::InstId::None, .type_expr_region_id = SemIR::ExprRegionId::None}); if (type_expr_region_id.has_value()) { InsertHere(context, type_expr_region_id); } auto value_id = SemIR::InstId::None; if (kind_ == MatchKind::Local) { auto conversion_kind = ConversionKindFor(context, binding_pattern, entry); if (!bind_name_id.has_value()) { // TODO: Is this appropriate, or should we perform a conversion based on // whether the `_` binding is a value or ref binding first, and then // separately discard the initializer for a `_` binding? conversion_kind = ConversionTarget::Discarded; } value_id = Convert(context, SemIR::LocId(entry.scrutinee_id), entry.scrutinee_id, {.kind = conversion_kind, .type_id = context.insts().Get(bind_name_id).type_id()}); } else { // In a function call, conversion is handled while matching the enclosing // `*ParamPattern`. value_id = entry.scrutinee_id; } if (bind_name_id.has_value()) { auto bind_name = context.insts().GetAs(bind_name_id); CARBON_CHECK(!bind_name.value_id.has_value()); bind_name.value_id = value_id; ReplaceInstBeforeConstantUse(context, bind_name_id, bind_name); context.inst_block_stack().AddInstId(bind_name_id); } } // Returns the inst kind to use for the parameter corresponding to the given // parameter pattern. static auto ParamKindFor(Context& context, SemIR::Inst param_pattern, MatchContext::WorkItem entry) -> SemIR::InstKind { CARBON_KIND_SWITCH(param_pattern) { case SemIR::OutParamPattern::Kind: return SemIR::OutParam::Kind; case SemIR::RefParamPattern::Kind: case SemIR::VarParamPattern::Kind: return SemIR::RefParam::Kind; case SemIR::ValueParamPattern::Kind: return SemIR::ValueParam::Kind; case CARBON_KIND(SemIR::FormParamPattern form_param_pattern): { auto form_inst_id = context.constant_values().GetInstId(form_param_pattern.form_id); auto form_inst = context.insts().Get(form_inst_id); switch (form_inst.kind()) { case SemIR::InitForm::Kind: case SemIR::RefForm::Kind: return SemIR::RefParam::Kind; case SemIR::SymbolicBinding::Kind: context.TODO(entry.pattern_id, "Support symbolic form params"); [[fallthrough]]; case SemIR::ErrorInst::Kind: case SemIR::ValueForm::Kind: return SemIR::ValueParam::Kind; default: CARBON_FATAL("Unexpected form {0}", form_inst); } } default: CARBON_FATAL("Unexpected param pattern kind: {0}", param_pattern); } } auto MatchContext::DoEmitPatternMatch(Context& context, SemIR::AnyParamPattern param_pattern, WorkItem entry) -> void { // If the form is initializing, match this as a `VarPattern` before matching // it as a parameter pattern. if (param_pattern.kind == SemIR::FormParamPattern::Kind) { auto form_inst_id = context.constant_values().GetInstId(param_pattern.form_id); if (context.insts().Get(form_inst_id).kind() == SemIR::InitForm::Kind) { auto new_scrutinee_id = DoEmitVarPatternMatchImpl(context, param_pattern.type_id, entry); entry.scrutinee_id = new_scrutinee_id; } } switch (kind_) { case MatchKind::Caller: { CARBON_CHECK(entry.scrutinee_id.has_value()); if (entry.scrutinee_id == SemIR::ErrorInst::InstId) { call_args_.push_back(SemIR::ErrorInst::InstId); } else { auto scrutinee_type_id = ExtractScrutineeType( context.sem_ir(), SemIR::GetTypeOfInstInSpecific( context.sem_ir(), callee_specific_id_, entry.pattern_id)); call_args_.push_back(Convert( context, SemIR::LocId(entry.scrutinee_id), entry.scrutinee_id, {.kind = ConversionKindFor(context, param_pattern, entry), .type_id = scrutinee_type_id})); } // Do not traverse farther, because the caller side of the pattern // ends here. break; } case MatchKind::Callee: { SemIR::AnyParam param = { .kind = ParamKindFor(context, param_pattern, entry), .type_id = ExtractScrutineeType(context.sem_ir(), param_pattern.type_id), .index = SemIR::CallParamIndex(call_params_.size()), .pretty_name_id = SemIR::GetPrettyNameFromPatternId( context.sem_ir(), entry.pattern_id)}; auto param_id = AddInst(context, SemIR::LocIdAndInst::UncheckedLoc( SemIR::LocId(entry.pattern_id), param)); AddWork({.pattern_id = param_pattern.subpattern_id, .scrutinee_id = param_id}); call_params_.push_back(param_id); call_param_patterns_.push_back(entry.pattern_id); break; } case MatchKind::Local: { CARBON_FATAL("Found ValueParamPattern during local pattern match"); } } } auto MatchContext::DoEmitPatternMatch( Context& context, SemIR::ReturnSlotPattern return_slot_pattern, WorkItem entry) -> void { CARBON_CHECK(kind_ == MatchKind::Callee); auto type_id = ExtractScrutineeType(context.sem_ir(), return_slot_pattern.type_id); auto return_slot_id = AddInst( context, SemIR::LocId(entry.pattern_id), {.type_id = type_id, .type_inst_id = context.types().GetTypeInstId(type_id), .storage_id = entry.scrutinee_id}); bool already_in_lookup = context.scope_stack() .LookupOrAddName(SemIR::NameId::ReturnSlot, return_slot_id) .has_value(); CARBON_CHECK(!already_in_lookup); } auto MatchContext::DoEmitPatternMatch(Context& context, SemIR::VarPattern var_pattern, WorkItem entry) -> void { auto new_scrutinee_id = DoEmitVarPatternMatchImpl(context, var_pattern.type_id, entry); AddWork({.pattern_id = var_pattern.subpattern_id, .scrutinee_id = new_scrutinee_id}); } auto MatchContext::DoEmitVarPatternMatchImpl(Context& context, SemIR::TypeId pattern_type_id, WorkItem entry) const -> SemIR::InstId { auto storage_id = SemIR::InstId::None; switch (kind_) { case MatchKind::Callee: { // We're emitting pattern-match IR for the callee, but we're still on // the caller side of the pattern, so we traverse without emitting any // insts. return SemIR::InstId::None; } case MatchKind::Local: { // In a `var`/`let` declaration, the `VarStorage` inst is created before // we start pattern matching. auto lookup_result = context.var_storage_map().Lookup(entry.pattern_id); CARBON_CHECK(lookup_result); storage_id = lookup_result.value(); break; } case MatchKind::Caller: { storage_id = AddInst( context, SemIR::LocId(entry.pattern_id), {.type_id = ExtractScrutineeType(context.sem_ir(), pattern_type_id)}); CARBON_CHECK(entry.scrutinee_id.has_value()); break; } } // TODO: Find a more efficient way to put these insts in the global_init // block (or drop the distinction between the global_init block and the // file scope?) if (context.scope_stack().PeekIndex() == ScopeIndex::Package) { context.global_init().Resume(); } if (entry.scrutinee_id.has_value()) { auto init_id = Initialize(context, SemIR::LocId(entry.pattern_id), storage_id, entry.scrutinee_id); // If we created a `TemporaryStorage` to hold the var, create a // corresponding `Temporary` to model that its initialization is complete. // TODO: If the subpattern is a binding, we may want to destroy the // parameter variable in the callee instead of the caller so that we can // support destructive move from it. if (kind_ == MatchKind::Caller) { storage_id = AddInstWithCleanup( context, SemIR::LocId(entry.pattern_id), {.type_id = context.insts().Get(storage_id).type_id(), .storage_id = storage_id, .init_id = init_id}); } else { // TODO: Consider using different instruction kinds for assignment // versus initialization. AddInst(context, SemIR::LocId(entry.pattern_id), {.lhs_id = storage_id, .rhs_id = init_id}); } } if (context.scope_stack().PeekIndex() == ScopeIndex::Package) { context.global_init().Suspend(); } return storage_id; } auto MatchContext::DoEmitPatternMatch(Context& context, SemIR::TuplePattern tuple_pattern, WorkItem entry) -> void { if (tuple_pattern.type_id == SemIR::ErrorInst::TypeId) { return; } auto subpattern_ids = context.inst_blocks().Get(tuple_pattern.elements_id); auto add_all_subscrutinees = [&](llvm::ArrayRef subscrutinee_ids) { for (auto [subpattern_id, subscrutinee_id] : llvm::reverse(llvm::zip_equal(subpattern_ids, subscrutinee_ids))) { AddWork( {.pattern_id = subpattern_id, .scrutinee_id = subscrutinee_id}); } }; if (!entry.scrutinee_id.has_value()) { CARBON_CHECK(kind_ == MatchKind::Callee); // If we don't have a scrutinee yet, we're still on the caller side of the // pattern, so the subpatterns don't have a scrutinee either. for (auto subpattern_id : llvm::reverse(subpattern_ids)) { AddWork( {.pattern_id = subpattern_id, .scrutinee_id = SemIR::InstId::None}); } return; } auto scrutinee = context.insts().GetWithLocId(entry.scrutinee_id); if (auto scrutinee_literal = scrutinee.inst.TryAs()) { auto subscrutinee_ids = context.inst_blocks().Get(scrutinee_literal->elements_id); if (subscrutinee_ids.size() != subpattern_ids.size()) { CARBON_DIAGNOSTIC(TuplePatternSizeDoesntMatchLiteral, Error, "tuple pattern expects {0} element{0:s}, but tuple " "literal has {1}", Diagnostics::IntAsSelect, Diagnostics::IntAsSelect); context.emitter().Emit(entry.pattern_id, TuplePatternSizeDoesntMatchLiteral, subpattern_ids.size(), subscrutinee_ids.size()); return; } add_all_subscrutinees(subscrutinee_ids); return; } auto tuple_type_id = ExtractScrutineeType(context.sem_ir(), tuple_pattern.type_id); auto converted_scrutinee_id = ConvertToValueOrRefOfType(context, SemIR::LocId(entry.pattern_id), entry.scrutinee_id, tuple_type_id); if (auto scrutinee_value = context.insts().TryGetAs(converted_scrutinee_id)) { add_all_subscrutinees( context.inst_blocks().Get(scrutinee_value->elements_id)); return; } auto tuple_type = context.types().GetAs(tuple_type_id); auto element_type_inst_ids = context.inst_blocks().Get(tuple_type.type_elements_id); llvm::SmallVector subscrutinee_ids; subscrutinee_ids.reserve(element_type_inst_ids.size()); for (auto [i, element_type_id] : llvm::enumerate( context.types().GetBlockAsTypeIds(element_type_inst_ids))) { subscrutinee_ids.push_back( AddInst(context, scrutinee.loc_id, {.type_id = element_type_id, .tuple_id = converted_scrutinee_id, .index = SemIR::ElementIndex(i)})); } add_all_subscrutinees(subscrutinee_ids); } auto MatchContext::EmitPatternMatch(Context& context, MatchContext::WorkItem entry) -> void { if (entry.pattern_id == SemIR::ErrorInst::InstId) { return; } Diagnostics::AnnotationScope annotate_diagnostics( &context.emitter(), [&](auto& builder) { if (kind_ == MatchKind::Caller) { CARBON_DIAGNOSTIC(InCallToFunctionParam, Note, "initializing function parameter"); builder.Note(entry.pattern_id, InCallToFunctionParam); } }); auto pattern = context.insts().Get(entry.pattern_id); CARBON_KIND_SWITCH(pattern) { case CARBON_KIND_ANY(SemIR::AnyBindingPattern, any_binding_pattern): { DoEmitPatternMatch(context, any_binding_pattern, entry); break; } case CARBON_KIND_ANY(SemIR::AnyParamPattern, any_param_pattern): { DoEmitPatternMatch(context, any_param_pattern, entry); break; } case CARBON_KIND(SemIR::ReturnSlotPattern return_slot_pattern): { DoEmitPatternMatch(context, return_slot_pattern, entry); break; } case CARBON_KIND(SemIR::VarPattern var_pattern): { DoEmitPatternMatch(context, var_pattern, entry); break; } case CARBON_KIND(SemIR::TuplePattern tuple_pattern): { DoEmitPatternMatch(context, tuple_pattern, entry); break; } default: { CARBON_FATAL("Inst kind not handled: {0}", pattern.kind()); } } } auto CalleePatternMatch(Context& context, SemIR::InstBlockId implicit_param_patterns_id, SemIR::InstBlockId param_patterns_id, SemIR::InstBlockId return_patterns_id) -> CalleePatternMatchResults { if (!return_patterns_id.has_value() && !param_patterns_id.has_value() && !implicit_param_patterns_id.has_value()) { return {.call_param_patterns_id = SemIR::InstBlockId::None, .call_params_id = SemIR::InstBlockId::None, .param_ranges = SemIR::Function::CallParamIndexRanges::Empty}; } MatchContext match(MatchKind::Callee); // We add work to the stack in reverse so that the results will be produced // in the original order. if (implicit_param_patterns_id.has_value()) { for (SemIR::InstId inst_id : llvm::reverse(context.inst_blocks().Get(implicit_param_patterns_id))) { match.AddWork( {.pattern_id = inst_id, .scrutinee_id = SemIR::InstId::None}); } } match.DoWork(context); auto implicit_end = SemIR::CallParamIndex(match.param_count()); if (param_patterns_id.has_value()) { for (SemIR::InstId inst_id : llvm::reverse(context.inst_blocks().Get(param_patterns_id))) { match.AddWork( {.pattern_id = inst_id, .scrutinee_id = SemIR::InstId::None}); } } match.DoWork(context); auto explicit_end = SemIR::CallParamIndex(match.param_count()); for (auto return_pattern_id : context.inst_blocks().GetOrEmpty(return_patterns_id)) { match.AddWork( {.pattern_id = return_pattern_id, .scrutinee_id = SemIR::InstId::None}); } match.DoWork(context); auto return_end = SemIR::CallParamIndex(match.param_count()); match.DoWork(context); auto blocks = std::move(match).CalleeResults(context); return {.call_param_patterns_id = blocks.call_param_patterns_id, .call_params_id = blocks.call_params_id, .param_ranges = {implicit_end, explicit_end, return_end}}; } auto CallerPatternMatch(Context& context, SemIR::SpecificId specific_id, SemIR::InstId self_pattern_id, SemIR::InstBlockId param_patterns_id, SemIR::InstBlockId return_patterns_id, SemIR::InstId self_arg_id, llvm::ArrayRef arg_refs, llvm::ArrayRef return_arg_ids, bool is_operator_syntax) -> SemIR::InstBlockId { MatchContext match(MatchKind::Caller, specific_id); auto return_patterns = context.inst_blocks().GetOrEmpty(return_patterns_id); // Track the return storage, if present. for (auto [return_pattern_id, return_arg_id] : llvm::zip_equal(return_patterns, return_arg_ids)) { if (return_arg_id.has_value()) { match.AddWork( {.pattern_id = return_pattern_id, .scrutinee_id = return_arg_id}); } else { CARBON_CHECK(return_arg_ids.size() == 1, "TODO: do the match even if return_arg_id is None, so that " "subsequent args are at the right index in the arg block"); } } // Check type conversions per-element. for (auto [arg_id, param_pattern_id] : llvm::reverse(llvm::zip_equal( arg_refs, context.inst_blocks().GetOrEmpty(param_patterns_id)))) { match.AddWork({.pattern_id = param_pattern_id, .scrutinee_id = arg_id, .allow_unmarked_ref = is_operator_syntax}); } if (self_pattern_id.has_value()) { match.AddWork({.pattern_id = self_pattern_id, .scrutinee_id = self_arg_id, .allow_unmarked_ref = true}); } match.DoWork(context); return std::move(match).CallerResults(context); } auto LocalPatternMatch(Context& context, SemIR::InstId pattern_id, SemIR::InstId scrutinee_id) -> void { MatchContext match(MatchKind::Local); match.AddWork({.pattern_id = pattern_id, .scrutinee_id = scrutinee_id}); match.DoWork(context); } } // namespace Carbon::Check ================================================ FILE: toolchain/check/pattern_match.h ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #ifndef CARBON_TOOLCHAIN_CHECK_PATTERN_MATCH_H_ #define CARBON_TOOLCHAIN_CHECK_PATTERN_MATCH_H_ #include "toolchain/check/context.h" #include "toolchain/sem_ir/function.h" #include "toolchain/sem_ir/ids.h" namespace Carbon::Check { // TODO: Find a better place for this overview, once it has stabilized. // // The signature pattern of a function call is matched partially by the caller // and partially by the callee. `ParamPattern` insts mark the boundary // between the two: pattern insts that are descendants of a `ParamPattern` // are matched by the callee, and pattern insts that have a `ParamPattern` // as a descendant are matched by the caller. // Emits the pattern-match IR for the declaration of a parameterized entity with // the given implicit and explicit parameter patterns, and the given return // patterns (any of which may be `None` if not applicable). This IR performs the // callee side of pattern matching, starting at the `ParamPattern` insts, and // matching them against the corresponding `Call` parameters (see // entity_with_params_base.h for the definition of that term). // Returns the IDs of inst blocks consisting of references to the `Call` // parameter patterns and `Call` parameters of the function, as well as // the implicit, explicit, and return index ranges of those blocks. struct CalleePatternMatchResults { SemIR::InstBlockId call_param_patterns_id; SemIR::InstBlockId call_params_id; SemIR::Function::CallParamIndexRanges param_ranges; }; auto CalleePatternMatch(Context& context, SemIR::InstBlockId implicit_param_patterns_id, SemIR::InstBlockId param_patterns_id, SemIR::InstBlockId return_patterns_id) -> CalleePatternMatchResults; // Emits the pattern-match IR for matching the given arguments with the given // parameter patterns, and returns an inst block of the arguments that should // be passed to the `Call` inst. `is_operator_syntax` indicates that this call // was generated from an operator rather than from function call syntax, so // arguments to `ref` parameters aren't required to have `ref` tags. auto CallerPatternMatch(Context& context, SemIR::SpecificId specific_id, SemIR::InstId self_pattern_id, SemIR::InstBlockId param_patterns_id, SemIR::InstBlockId return_patterns_id, SemIR::InstId self_arg_id, llvm::ArrayRef arg_refs, llvm::ArrayRef return_arg_ids, bool is_operator_syntax) -> SemIR::InstBlockId; // Emits the pattern-match IR for a local pattern matching operation with the // given pattern and scrutinee. auto LocalPatternMatch(Context& context, SemIR::InstId pattern_id, SemIR::InstId scrutinee_id) -> void; } // namespace Carbon::Check #endif // CARBON_TOOLCHAIN_CHECK_PATTERN_MATCH_H_ ================================================ FILE: toolchain/check/pending_block.h ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #ifndef CARBON_TOOLCHAIN_CHECK_PENDING_BLOCK_H_ #define CARBON_TOOLCHAIN_CHECK_PENDING_BLOCK_H_ #include "llvm/ADT/SmallVector.h" #include "toolchain/check/context.h" #include "toolchain/check/inst.h" #include "toolchain/sem_ir/ids.h" #include "toolchain/sem_ir/inst.h" namespace Carbon::Check { // A block of code that contains pending instructions that might be needed but // that haven't been inserted yet. class PendingBlock { public: // `context` must not be null. explicit PendingBlock(Context* context) : context_(context) {} PendingBlock(const PendingBlock&) = delete; auto operator=(const PendingBlock&) -> PendingBlock& = delete; // A scope in which we will tentatively add instructions to a pending block. // If we leave the scope without inserting or merging the block, instructions // added after this point will be removed again. class DiscardUnusedInstsScope { public: // If `block` is not null, enters the scope. If `block` is null, this object // has no effect. explicit DiscardUnusedInstsScope(PendingBlock* block) : block_(block), size_(block ? block->insts_.size() : 0) {} ~DiscardUnusedInstsScope() { if (block_ && block_->insts_.size() > size_) { block_->insts_.truncate(size_); } } private: PendingBlock* block_; size_t size_; }; template requires(std::convertible_to) auto AddInst(LocT loc_id, InstT inst) -> SemIR::InstId { auto inst_id = AddInstInNoBlock(*context_, loc_id, inst); insts_.push_back(inst_id); return inst_id; } template requires(std::convertible_to) auto AddInstWithCleanup(LocT loc_id, InstT inst) -> SemIR::InstId { auto inst_id = AddInstWithCleanupInNoBlock(*context_, loc_id, inst); insts_.push_back(inst_id); return inst_id; } // Insert the pending block of code at the current position. auto InsertHere() -> void { for (auto id : insts_) { context_->inst_block_stack().AddInstId(id); } insts_.clear(); } // Replace the instruction at target_id with the instructions in this block. // The new value for target_id should be value_id. Returns the InstId that // should be used to refer to the result from now on. value_id must precede // target_id, or be the last ID in this block, in order to preserve the // property that SemIR is topologically sorted. // // TODO: we could also allow value_id to be one of the other insts in this // block, but that would be costlier to enforce. auto MergeReplacing(SemIR::InstId target_id, SemIR::InstId value_id) -> SemIR::InstId { CARBON_CHECK(target_id != value_id); // TODO: consider adding an end-of-phase check that the SemIR::File is in // SSA form, and dropping this check and the ordering preconditions here and // on Initialize. CARBON_CHECK(value_id.index <= target_id.index || (!insts_.empty() && insts_.back() == value_id), "Splice would break topological sorting of insts"); SemIR::LocIdAndInst value = context_->insts().GetWithLocId(value_id); auto result_id = value_id; if (insts_.size() == 1 && insts_[0] == value_id) { // The block is {value_id}. Replace `target_id` with the instruction // referred to by `value_id`. This is intended to be the common case. result_id = target_id; } else { // Anything else: splice it into the IR, replacing `target_id`. This // includes empty blocks, which `Add` handles. value.inst = SemIR::SpliceBlock{.type_id = value.inst.type_id(), .block_id = context_->inst_blocks().Add(insts_), .result_id = value_id}; } ReplaceLocIdAndInstBeforeConstantUse(*context_, target_id, value); // Prepare to stash more pending instructions. insts_.clear(); return result_id; } private: Context* context_; llvm::SmallVector insts_; }; } // namespace Carbon::Check #endif // CARBON_TOOLCHAIN_CHECK_PENDING_BLOCK_H_ ================================================ FILE: toolchain/check/pointer_dereference.cpp ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #include "toolchain/check/pointer_dereference.h" #include "llvm/ADT/STLFunctionalExtras.h" #include "toolchain/check/context.h" #include "toolchain/check/convert.h" #include "toolchain/check/inst.h" #include "toolchain/parse/node_ids.h" #include "toolchain/sem_ir/ids.h" namespace Carbon::Check { auto PerformPointerDereference( Context& context, SemIR::LocId loc_id, SemIR::InstId base_id, llvm::function_refvoid> diagnose_not_pointer) -> SemIR::InstId { // TODO: Once we have a finalized design for a pointer interface, use // // HandleUnaryOperator(context, loc_id, {"Pointer", "Dereference"}); // // to convert to a pointer value. base_id = ConvertToValueExpr(context, base_id); auto type_id = context.types().GetUnqualifiedType( context.insts().Get(base_id).type_id()); auto result_type_id = SemIR::ErrorInst::TypeId; if (auto pointer_type = context.types().TryGetAs(type_id)) { result_type_id = context.types().GetTypeIdForTypeInstId(pointer_type->pointee_id); } else if (type_id != SemIR::ErrorInst::TypeId) { diagnose_not_pointer(type_id); } return AddInst( context, loc_id, {.type_id = result_type_id, .pointer_id = base_id}); } } // namespace Carbon::Check ================================================ FILE: toolchain/check/pointer_dereference.h ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #ifndef CARBON_TOOLCHAIN_CHECK_POINTER_DEREFERENCE_H_ #define CARBON_TOOLCHAIN_CHECK_POINTER_DEREFERENCE_H_ #include "llvm/ADT/STLFunctionalExtras.h" #include "toolchain/check/context.h" #include "toolchain/parse/node_ids.h" #include "toolchain/sem_ir/ids.h" namespace Carbon::Check { // Creates SemIR to perform a pointer dereference with base expression // `base_id`. Returns the result of the access. auto PerformPointerDereference( Context& context, SemIR::LocId loc_id, SemIR::InstId base_i, llvm::function_refvoid> diagnose_not_pointer) -> SemIR::InstId; } // namespace Carbon::Check #endif // CARBON_TOOLCHAIN_CHECK_POINTER_DEREFERENCE_H_ ================================================ FILE: toolchain/check/region_stack.h ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #ifndef CARBON_TOOLCHAIN_CHECK_REGION_STACK_H_ #define CARBON_TOOLCHAIN_CHECK_REGION_STACK_H_ #include #include "common/array_stack.h" #include "toolchain/check/diagnostic_helpers.h" #include "toolchain/sem_ir/ids.h" namespace Carbon::Check { // Provides a stack of single-entry regions being built. class RegionStack { public: // A callback for Context::TODO. using TodoFn = std::functionvoid>; explicit RegionStack(TodoFn todo_fn) : todo_fn_(std::move(todo_fn)) {} // Mark the start of a new single-entry region with the given entry block. auto PushRegion(SemIR::InstBlockId entry_block_id) -> void { stack_.PushArray(); stack_.AppendToTop(entry_block_id); } // Add `block_id` to the most recently pushed single-entry region. To preserve // the single-entry property, `block_id` must not be directly reachable from // any block outside the region. To ensure the region's blocks are in lexical // order, this should be called when the first parse node associated with this // block is handled, or as close as possible. auto AddToRegion(SemIR::InstBlockId block_id, SemIR::LocId loc_id) -> void { if (stack_.empty()) { todo_fn_(loc_id, "Control flow expressions are currently only supported inside " "functions."); return; } if (block_id == SemIR::InstBlockId::Unreachable) { return; } stack_.AppendToTop(block_id); } // Adds multiple blocks at once. The caller is responsible for validating that // each block is reachable. auto AddToRegion(llvm::ArrayRef block_ids) -> void { stack_.AppendToTop(block_ids); } // Complete creation of the most recently pushed single-entry region, and // return a list of its blocks. auto PopRegion() -> llvm::SmallVector { llvm::SmallVector result(stack_.PeekArray()); stack_.PopArray(); return result; } // Pops a region, and does not return its contents. auto PopAndDiscardRegion() -> void { stack_.PopArray(); } // Returns the top-most region. auto PeekRegion() -> llvm::ArrayRef { return stack_.PeekArray(); } // Runs verification that the processing cleanly finished. auto VerifyOnFinish() const -> void { CARBON_CHECK(stack_.empty(), "region_stack still has {0} entries", stack_.all_values_size()); } // Returns true if any regions have been added. auto empty() -> bool { return stack_.empty(); } private: TodoFn todo_fn_; ArrayStack stack_; }; } // namespace Carbon::Check #endif // CARBON_TOOLCHAIN_CHECK_REGION_STACK_H_ ================================================ FILE: toolchain/check/return.cpp ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #include "toolchain/check/return.h" #include "toolchain/base/kind_switch.h" #include "toolchain/check/context.h" #include "toolchain/check/control_flow.h" #include "toolchain/check/convert.h" #include "toolchain/check/inst.h" namespace Carbon::Check { // Gets the function that lexically encloses the current location. auto GetCurrentFunctionForReturn(Context& context) -> SemIR::Function& { CARBON_CHECK(context.scope_stack().IsInFunctionScope(), "Handling return but not in a function"); auto decl_id = context.scope_stack().GetReturnScopeDeclId(); auto function_id = context.insts().GetAs(decl_id).function_id; return context.functions().Get(function_id); } auto GetReturnedVarParam(Context& context, const SemIR::Function& function) -> SemIR::InstId { auto return_form_id = function.GetDeclaredReturnForm(context.sem_ir()); if (auto return_form = context.insts().TryGetAsIfValid(return_form_id)) { auto call_params = context.inst_blocks().Get(function.call_params_id); CARBON_CHECK(function.call_param_ranges.return_size() == 1); auto return_param_id = call_params[function.call_param_ranges.return_begin().index]; auto return_type_id = context.insts().Get(return_param_id).type_id(); if (SemIR::InitRepr::ForType(context.sem_ir(), return_type_id) .MightBeInPlace()) { return return_param_id; } } return SemIR::InstId::None; } // Gets the currently in scope `returned var` binding, if any, that would be // returned by a `return var;`. static auto GetCurrentReturnedVar(Context& context) -> SemIR::InstId { CARBON_CHECK(context.scope_stack().IsInFunctionScope(), "Handling return but not in a function"); return context.scope_stack().GetReturnedVar(); } // Produces a note that the given function has no explicit return type. static auto NoteNoReturnTypeProvided(DiagnosticBuilder& diag, const SemIR::Function& function) { CARBON_DIAGNOSTIC(ReturnTypeOmittedNote, Note, "there was no return type provided"); diag.Note(function.latest_decl_id(), ReturnTypeOmittedNote); } // Produces a note describing the return type of the given function, which // must be a function whose definition is currently being checked. static auto NoteReturnType(DiagnosticBuilder& diag, const SemIR::Function& function) { CARBON_DIAGNOSTIC(ReturnTypeHereNote, Note, "return type of function is {0}", InstIdAsType); diag.Note(function.return_type_inst_id, ReturnTypeHereNote, function.return_type_inst_id); } // Produces a note pointing at the currently in scope `returned var`. static auto NoteReturnedVar(DiagnosticBuilder& diag, SemIR::InstId returned_var_id) { CARBON_DIAGNOSTIC(ReturnedVarHere, Note, "`returned var` was declared here"); diag.Note(returned_var_id, ReturnedVarHere); } auto RegisterReturnedVar(Context& context, Parse::NodeId returned_node, Parse::NodeId type_node, SemIR::TypeId type_id, SemIR::InstId bind_id, SemIR::NameId name_id) -> void { auto& function = GetCurrentFunctionForReturn(context); auto return_type_id = function.GetDeclaredReturnType(context.sem_ir()); // A `returned var` requires an explicit return type. if (!return_type_id.has_value()) { CARBON_DIAGNOSTIC(ReturnedVarWithNoReturnType, Error, "cannot declare a `returned var` in this function"); auto diag = context.emitter().Build(returned_node, ReturnedVarWithNoReturnType); NoteNoReturnTypeProvided(diag, function); diag.Emit(); return; } // The declared type of the var must match the return type of the function. if (return_type_id != type_id) { CARBON_DIAGNOSTIC(ReturnedVarWrongType, Error, "type {0} of `returned var` does not match " "return type of enclosing function", SemIR::TypeId); auto diag = context.emitter().Build(type_node, ReturnedVarWrongType, type_id); NoteReturnType(diag, function); diag.Emit(); } auto form_inst_id = function.GetDeclaredReturnForm(context.sem_ir()); if (!context.insts().Is(form_inst_id)) { CARBON_DIAGNOSTIC(ReturnedVarNotInit, Error, "`returned var` declaration in function with " "non-initializing return form"); auto diag = context.emitter().Build(returned_node, ReturnedVarNotInit); CARBON_DIAGNOSTIC(ReturnFormHereNote, Note, "return form declared here"); diag.Note(function.return_form_inst_id, ReturnFormHereNote); diag.Emit(); } auto existing_id = context.scope_stack().SetReturnedVarOrGetExisting(bind_id, name_id); if (existing_id.has_value()) { CARBON_DIAGNOSTIC(ReturnedVarShadowed, Error, "cannot declare a `returned var` in the scope of " "another `returned var`"); auto diag = context.emitter().Build(bind_id, ReturnedVarShadowed); NoteReturnedVar(diag, existing_id); diag.Emit(); } } auto BuildReturnWithNoExpr(Context& context, SemIR::LocId loc_id) -> void { const auto& function = GetCurrentFunctionForReturn(context); auto return_type_id = function.GetDeclaredReturnType(context.sem_ir()); if (return_type_id.has_value()) { CARBON_DIAGNOSTIC(ReturnStatementMissingExpr, Error, "missing return value"); auto diag = context.emitter().Build(loc_id, ReturnStatementMissingExpr); NoteReturnType(diag, function); diag.Emit(); } AddReturnCleanupBlock(context, loc_id); } auto BuildReturnWithExpr(Context& context, SemIR::LocId loc_id, SemIR::InstId expr_id) -> void { const auto& function = GetCurrentFunctionForReturn(context); auto returned_var_id = GetCurrentReturnedVar(context); auto out_param_id = SemIR::InstId::None; auto return_type_id = SemIR::TypeId::None; if (function.return_type_inst_id.has_value()) { return_type_id = context.types().GetTypeIdForTypeInstId(function.return_type_inst_id); } if (!return_type_id.has_value()) { CARBON_DIAGNOSTIC( ReturnStatementDisallowExpr, Error, "no return expression should be provided in this context"); auto diag = context.emitter().Build(loc_id, ReturnStatementDisallowExpr); NoteNoReturnTypeProvided(diag, function); diag.Emit(); expr_id = SemIR::ErrorInst::InstId; } else if (returned_var_id.has_value()) { CARBON_DIAGNOSTIC( ReturnExprWithReturnedVar, Error, "can only `return var;` in the scope of a `returned var`"); auto diag = context.emitter().Build(loc_id, ReturnExprWithReturnedVar); NoteReturnedVar(diag, returned_var_id); diag.Emit(); expr_id = SemIR::ErrorInst::InstId; } else if (!SemIR::InitRepr::ForType(context.sem_ir(), return_type_id) .is_valid() || return_type_id == SemIR::ErrorInst::TypeId) { // We already diagnosed that the return type is invalid. Don't try to // convert to it. expr_id = SemIR::ErrorInst::InstId; } else { auto return_form = context.insts().Get(function.GetDeclaredReturnForm(context.sem_ir())); CARBON_KIND_SWITCH(return_form) { case CARBON_KIND(SemIR::InitForm _): { auto call_params = context.inst_blocks().Get( GetCurrentFunctionForReturn(context).call_params_id); CARBON_CHECK(function.call_param_ranges.return_size() == 1); out_param_id = call_params[function.call_param_ranges.return_begin().index]; CARBON_CHECK(out_param_id.has_value()); expr_id = Initialize(context, loc_id, out_param_id, expr_id, /*for_return=*/true); if (!SemIR::InitRepr::ForType(context.sem_ir(), return_type_id) .MightBeInPlace()) { out_param_id = SemIR::InstId::None; } break; } case CARBON_KIND(SemIR::RefForm ref_form): { expr_id = Convert( context, loc_id, expr_id, ConversionTarget{.kind = ConversionTarget::DurableRef, .type_id = context.types().GetTypeIdForTypeInstId( ref_form.type_component_inst_id)}); break; } default: CARBON_FATAL("Unexpected inst kind: {0}", return_form); } } AddReturnCleanupBlockWithExpr(context, loc_id, {.expr_id = expr_id, .dest_id = out_param_id}); } auto BuildReturnVar(Context& context, Parse::ReturnStatementId node_id) -> void { const auto& function = GetCurrentFunctionForReturn(context); auto returned_var_id = GetCurrentReturnedVar(context); if (!returned_var_id.has_value()) { CARBON_DIAGNOSTIC(ReturnVarWithNoReturnedVar, Error, "`return var;` with no `returned var` in scope"); context.emitter().Emit(node_id, ReturnVarWithNoReturnedVar); returned_var_id = SemIR::ErrorInst::InstId; } auto return_param_id = GetReturnedVarParam(context, function); if (!return_param_id.has_value()) { // If we don't have a return slot, we're returning by value. Convert to a // value expression. returned_var_id = ConvertToValueExpr(context, returned_var_id); } AddReturnCleanupBlockWithExpr( context, node_id, {.expr_id = returned_var_id, .dest_id = return_param_id}); } } // namespace Carbon::Check ================================================ FILE: toolchain/check/return.h ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #ifndef CARBON_TOOLCHAIN_CHECK_RETURN_H_ #define CARBON_TOOLCHAIN_CHECK_RETURN_H_ #include "toolchain/check/context.h" #include "toolchain/parse/node_ids.h" namespace Carbon::Check { // Gets the function that a `return` statement in the current context would // return from. auto GetCurrentFunctionForReturn(Context& context) -> SemIR::Function&; // Gets the return parameter corresponding to `function`'s `returned var`. // Returns None if the `returned var` doesn't correspond to a return parameter // (e.g. because it doesn't have an in-place init representation). auto GetReturnedVarParam(Context& context, const SemIR::Function& function) -> SemIR::InstId; // Checks a `returned var` binding and registers it as the current `returned // var` in this scope. auto RegisterReturnedVar(Context& context, Parse::NodeId returned_node, Parse::NodeId type_node, SemIR::TypeId type_id, SemIR::InstId bind_id, SemIR::NameId name_id) -> void; // Checks and builds SemIR for a `return;` statement. auto BuildReturnWithNoExpr(Context& context, SemIR::LocId loc_id) -> void; // Checks and builds SemIR for a `return ;` statement. auto BuildReturnWithExpr(Context& context, SemIR::LocId loc_id, SemIR::InstId expr_id) -> void; // Checks and builds SemIR for a `return var;` statement. auto BuildReturnVar(Context& context, Parse::ReturnStatementId node_id) -> void; } // namespace Carbon::Check #endif // CARBON_TOOLCHAIN_CHECK_RETURN_H_ ================================================ FILE: toolchain/check/scope_index.h ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #ifndef CARBON_TOOLCHAIN_CHECK_SCOPE_INDEX_H_ #define CARBON_TOOLCHAIN_CHECK_SCOPE_INDEX_H_ #include "toolchain/base/index_base.h" namespace Carbon::Check { // An index for a pushed scope. This may correspond to a permanent scope with a // corresponding `NameScope`, in which case a different index will be assigned // each time the scope is entered. Alternatively, it may be a temporary scope // such as is created for a block, and will only be entered once. // // `ScopeIndex` values are comparable. Lower `ScopeIndex` values correspond to // scopes entered earlier in the file. struct ScopeIndex : public IndexBase { static constexpr llvm::StringLiteral Label = "scope"; static const ScopeIndex Package; using IndexBase::IndexBase; }; inline constexpr ScopeIndex ScopeIndex::Package = ScopeIndex(0); } // namespace Carbon::Check #endif // CARBON_TOOLCHAIN_CHECK_SCOPE_INDEX_H_ ================================================ FILE: toolchain/check/scope_stack.cpp ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #include "toolchain/check/scope_stack.h" #include #include "common/check.h" #include "toolchain/check/context.h" #include "toolchain/check/unused.h" #include "toolchain/sem_ir/ids.h" namespace Carbon::Check { ScopeStack::ScopeStack(Context& context) : context_(&context), lexical_lookup_(context.sem_ir().identifiers()), full_pattern_stack_(&lexical_lookup_) {} auto ScopeStack::sem_ir() const -> const SemIR::File& { return context_->sem_ir(); } auto ScopeStack::VerifyOnFinish() const -> void { CARBON_CHECK(return_scope_stack_.empty(), "{0}", return_scope_stack_.size()); CARBON_CHECK(break_continue_stack_.empty(), "{0}", break_continue_stack_.size()); CARBON_CHECK(scope_stack_.empty(), "{0}", scope_stack_.size()); CARBON_CHECK(destroy_id_stack_.empty(), "{0}", destroy_id_stack_.all_values_size()); CARBON_CHECK(non_lexical_scope_stack_.empty(), "{0}", non_lexical_scope_stack_.size()); CARBON_CHECK(compile_time_binding_stack_.empty(), "{0}", compile_time_binding_stack_.all_values_size()); full_pattern_stack_.VerifyOnFinish(); } auto ScopeStack::VerifyNextCompileTimeBindIndex(llvm::StringLiteral label, const ScopeStackEntry& scope) -> void { CARBON_CHECK( static_cast(compile_time_binding_stack_.all_values_size()) == scope.next_compile_time_bind_index.index, "Wrong number of entries in compile-time binding stack after {0}: have " "{1}, expected {2}", label, compile_time_binding_stack_.all_values_size(), scope.next_compile_time_bind_index.index); } auto ScopeStack::Push(SemIR::InstId scope_inst_id, SemIR::NameScopeId scope_id, SemIR::SpecificId specific_id, bool lexical_lookup_has_load_error) -> void { // If this scope doesn't have a specific of its own, it lives in the enclosing // scope's specific, if any. auto enclosing_specific_id = specific_id; if (!specific_id.has_value() && !scope_stack_.empty()) { enclosing_specific_id = PeekSpecificId(); } compile_time_binding_stack_.PushArray(); scope_stack_.push_back( {.index = next_scope_index_, .scope_inst_id = scope_inst_id, .scope_id = scope_id, .specific_id = enclosing_specific_id, .next_compile_time_bind_index = SemIR::CompileTimeBindIndex( compile_time_binding_stack_.all_values_size()), .lexical_lookup_has_load_error = LexicalLookupHasLoadError() || lexical_lookup_has_load_error}); if (scope_stack_.back().is_lexical_scope()) { // For lexical lookups, unqualified lookup doesn't know how to find the // associated specific, so if we start adding lexical scopes associated with // specifics, we'll need to somehow track them in lookup. CARBON_CHECK(!specific_id.has_value(), "Lexical scope should not have an associated specific."); } else { non_lexical_scope_stack_.push_back({.scope_index = next_scope_index_, .name_scope_id = scope_id, .specific_id = enclosing_specific_id}); } // TODO: Handle this case more gracefully. CARBON_CHECK(next_scope_index_.index != std::numeric_limits::max(), "Ran out of scopes"); ++next_scope_index_.index; VerifyNextCompileTimeBindIndex("Push", scope_stack_.back()); } auto ScopeStack::PushForDeclName() -> void { Push(SemIR::InstId::None, SemIR::NameScopeId::None, SemIR::SpecificId::None, /*lexical_lookup_has_load_error=*/false); MarkNestingIfInReturnScope(); } auto ScopeStack::PushForEntity(SemIR::InstId scope_inst_id, SemIR::NameScopeId scope_id, SemIR::SpecificId specific_id, bool lexical_lookup_has_load_error) -> void { CARBON_CHECK(scope_inst_id.has_value()); CARBON_DCHECK(!sem_ir().insts().Is(scope_inst_id)); Push(scope_inst_id, scope_id, specific_id, lexical_lookup_has_load_error); MarkNestingIfInReturnScope(); } auto ScopeStack::PushForSameRegion() -> void { Push(SemIR::InstId::None, SemIR::NameScopeId::None, SemIR::SpecificId::None, /*lexical_lookup_has_load_error=*/false); } auto ScopeStack::PushForFunctionBody(SemIR::InstId scope_inst_id) -> void { CARBON_DCHECK(sem_ir().insts().Is(scope_inst_id)); Push(scope_inst_id, SemIR::NameScopeId::None, SemIR::SpecificId::None, /*lexical_lookup_has_load_error=*/false); return_scope_stack_.push_back({.decl_id = scope_inst_id}); destroy_id_stack_.PushArray(); } auto ScopeStack::Pop(bool check_unused) -> void { auto scope = scope_stack_.pop_back_val(); // TODO: Multiple diagnostics on same line has non-deterministic order. // Add second sort key in diagnostics sorting. scope.names.ForEach([&, check_unused](SemIR::NameId name_id) { auto& lexical_results = lexical_lookup_.Get(name_id); CARBON_CHECK(lexical_results.back().scope_index == scope.index, "Inconsistent scope index for name {0}", name_id); if (check_unused) { CheckUnusedBinding(*context_, name_id, lexical_results.back()); } lexical_results.pop_back(); }); if (!scope.is_lexical_scope()) { CARBON_CHECK(non_lexical_scope_stack_.back().scope_index == scope.index); non_lexical_scope_stack_.pop_back(); } if (!return_scope_stack_.empty()) { if (scope.has_returned_var) { CARBON_CHECK(return_scope_stack_.back().returned_var.has_value()); return_scope_stack_.back().returned_var = SemIR::InstId::None; } if (return_scope_stack_.back().decl_id == scope.scope_inst_id) { // Leaving the function scope. return_scope_stack_.pop_back(); destroy_id_stack_.PopArray(); } else if (return_scope_stack_.back().nested_scope_index == scope.index) { // Returned to a function scope from a non-function nested entity scope. return_scope_stack_.back().nested_scope_index = ScopeIndex::None; } } else { CARBON_CHECK(!scope.has_returned_var); } VerifyNextCompileTimeBindIndex("Pop", scope); compile_time_binding_stack_.PopArray(); } auto ScopeStack::PopTo(ScopeIndex index, bool check_unused) -> void { while (PeekIndex() > index) { Pop(check_unused); } CARBON_CHECK(PeekIndex() == index, "Scope index {0} does not enclose the current scope {1}", index, PeekIndex()); } auto ScopeStack::MarkUsed(SemIR::NameId name_id, SemIR::LocId loc_id, bool is_reachable) -> void { auto& lexical_results = lexical_lookup_.Get(name_id); if (lexical_results.empty()) { return; } auto& result = lexical_results.back(); if (result.use_loc_id.has_value()) { return; } // Determine if we should set use_loc_id. if (result.inst_id.has_value() && result.inst_id != SemIR::InstId::InitTombstone) { if (auto binding = context_->insts().TryGetAs(result.inst_id)) { const auto& entity_name = context_->entity_names().Get(binding->entity_name_id); if (entity_name.is_unused && !is_reachable) { return; } } } // For non-bindings (like namespaces), we just mark them as used. // If the instruction is not valid (e.g. InitTombstone), we mark it as used // to avoid spurious "unused" warnings, assuming the invalid state will be // diagnosed elsewhere (e.g. used before init). result.use_loc_id = loc_id; } auto ScopeStack::LookupInLexicalScopesWithin(SemIR::NameId name_id, ScopeIndex scope_index, SemIR::LocId use_loc_id, bool is_reachable) -> SemIR::InstId { llvm::ArrayRef lexical_results = lexical_lookup_.Get(name_id); if (lexical_results.empty()) { return SemIR::InstId::None; } auto result = lexical_results.back(); if (result.scope_index < scope_index) { return SemIR::InstId::None; } if (use_loc_id.has_value()) { MarkUsed(name_id, use_loc_id, is_reachable); } return result.inst_id; } auto ScopeStack::LookupInLexicalScopes(SemIR::NameId name_id, SemIR::LocId use_loc_id, bool is_reachable) -> std::pair> { // Find the results from lexical scopes. These will be combined with results // from non-lexical scopes such as namespaces and classes. llvm::ArrayRef lexical_results = lexical_lookup_.Get(name_id); // If we have no lexical results, check all non-lexical scopes. if (lexical_results.empty()) { return {LexicalLookupHasLoadError() ? SemIR::ErrorInst::InstId : SemIR::InstId::None, non_lexical_scope_stack_}; } if (use_loc_id.has_value()) { MarkUsed(name_id, use_loc_id, is_reachable); } // Find the first non-lexical scope that is within the scope of the lexical // lookup result. auto* first_non_lexical_scope = llvm::lower_bound( non_lexical_scope_stack_, lexical_results.back().scope_index, [](const NonLexicalScope& scope, ScopeIndex index) { return scope.scope_index < index; }); return { lexical_results.back().inst_id, llvm::ArrayRef(first_non_lexical_scope, non_lexical_scope_stack_.end())}; } auto ScopeStack::LookupOrAddName(SemIR::NameId name_id, SemIR::InstId target_id, ScopeIndex scope_index, bool is_decl_reachable) -> SemIR::InstId { // Find the corresponding scope depth. // // TODO: Consider passing in the depth rather than performing a scan for it. // We only do this scan when declaring an entity such as a class within a // function, so it should be relatively rare, but it's still not necesasry to // recompute this. int scope_depth = scope_stack_.size() - 1; if (scope_index.has_value()) { scope_depth = llvm::lower_bound(scope_stack_, scope_index, [](const ScopeStackEntry& entry, ScopeIndex index) { return entry.index < index; }) - scope_stack_.begin(); CARBON_CHECK(scope_stack_[scope_depth].index == scope_index, "Declaring name in scope that has already ended"); } else { scope_index = scope_stack_[scope_depth].index; } // If this name has already been declared in this scope or an inner scope, // return the existing result. auto& lexical_results = lexical_lookup_.Get(name_id); if (!lexical_results.empty() && lexical_results.back().scope_index >= scope_index) { return lexical_results.back().inst_id; } // Add the name into the scope. bool inserted = scope_stack_[scope_depth].names.Insert(name_id).is_inserted(); CARBON_CHECK(inserted, "Name in scope but not in lexical lookups"); ++scope_stack_[scope_depth].num_names; // Add a corresponding lexical lookup result. lexical_results.push_back({.inst_id = target_id, .scope_index = scope_index, .is_decl_reachable = is_decl_reachable, .use_loc_id = SemIR::LocId::None}); return SemIR::InstId::None; } auto ScopeStack::SetReturnedVarOrGetExisting(SemIR::InstId inst_id, SemIR::NameId name_id) -> SemIR::InstId { CARBON_CHECK(!return_scope_stack_.empty(), "`returned var` in no function"); auto& return_scope = return_scope_stack_.back(); if (return_scope.returned_var.has_value()) { return return_scope.returned_var; } return_scope.returned_var = inst_id; CARBON_CHECK(!scope_stack_.back().has_returned_var, "Scope has returned var but none is set"); if (inst_id.has_value()) { scope_stack_.back().has_returned_var = true; MarkUsed(name_id, SemIR::LocId(inst_id), context_->inst_block_stack().is_current_block_reachable()); } return SemIR::InstId::None; } auto ScopeStack::Suspend() -> SuspendedScope { CARBON_CHECK(!scope_stack_.empty(), "No scope to suspend"); SuspendedScope result = {.entry = scope_stack_.pop_back_val(), .suspended_items = {}}; if (!result.entry.is_lexical_scope()) { non_lexical_scope_stack_.pop_back(); } auto peek_compile_time_bindings = compile_time_binding_stack_.PeekArray(); result.suspended_items.reserve(result.entry.num_names + peek_compile_time_bindings.size()); result.entry.names.ForEach([&](SemIR::NameId name_id) { auto suspended = lexical_lookup_.Suspend(name_id); CARBON_CHECK(suspended.index != SuspendedScope::ScopeItem::IndexForCompileTimeBinding); result.suspended_items.push_back( {.index = suspended.index, .inst_id = suspended.inst_id, .is_decl_reachable = suspended.is_decl_reachable, .use_loc_id = suspended.use_loc_id}); }); CARBON_CHECK(static_cast(result.suspended_items.size()) == result.entry.num_names); // Move any compile-time bindings into the suspended scope. for (auto inst_id : peek_compile_time_bindings) { result.suspended_items.push_back( {.index = SuspendedScope::ScopeItem::IndexForCompileTimeBinding, .inst_id = inst_id, .is_decl_reachable = true, .use_loc_id = SemIR::LocId::None}); } compile_time_binding_stack_.PopArray(); // This would be easy to support if we had a need, but currently we do not. CARBON_CHECK(!result.entry.has_returned_var, "Should not suspend a scope with a returned var."); return result; } auto ScopeStack::Restore(SuspendedScope&& scope) -> void { compile_time_binding_stack_.PushArray(); for (auto item : scope.suspended_items) { if (item.index == SuspendedScope::ScopeItem::IndexForCompileTimeBinding) { compile_time_binding_stack_.AppendToTop(item.inst_id); } else { lexical_lookup_.Restore({.index = item.index, .inst_id = item.inst_id, .is_decl_reachable = item.is_decl_reachable, .use_loc_id = item.use_loc_id}, scope.entry.index); } } VerifyNextCompileTimeBindIndex("Restore", scope.entry); if (!scope.entry.is_lexical_scope()) { non_lexical_scope_stack_.push_back( {.scope_index = scope.entry.index, .name_scope_id = scope.entry.scope_id, .specific_id = scope.entry.specific_id}); } scope_stack_.push_back(std::move(scope.entry)); } } // namespace Carbon::Check ================================================ FILE: toolchain/check/scope_stack.h ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #ifndef CARBON_TOOLCHAIN_CHECK_SCOPE_STACK_H_ #define CARBON_TOOLCHAIN_CHECK_SCOPE_STACK_H_ #include "common/array_stack.h" #include "common/move_only.h" #include "common/set.h" #include "llvm/ADT/SmallVector.h" #include "toolchain/check/full_pattern_stack.h" #include "toolchain/check/lexical_lookup.h" #include "toolchain/check/scope_index.h" #include "toolchain/sem_ir/file.h" #include "toolchain/sem_ir/ids.h" namespace Carbon::Check { class Context; // A stack of lexical and semantic scopes that we are currently performing // checking within. class ScopeStack { public: explicit ScopeStack(Context& context); // A scope in which `break` and `continue` can be used. struct BreakContinueScope { SemIR::InstBlockId break_target; SemIR::InstBlockId continue_target; }; // A non-lexical scope in which unqualified lookup may be required. struct NonLexicalScope { // The index of the scope in the scope stack. ScopeIndex scope_index; // The corresponding name scope. SemIR::NameScopeId name_scope_id; // The corresponding specific. SemIR::SpecificId specific_id; }; // Information about a scope that has been temporarily removed from the stack. // This type is large, so moves of this type should be avoided. struct SuspendedScope; // Pushes a scope for a declaration name's parameters. auto PushForDeclName() -> void; // Pushes a non-function entity scope. Functions must use // `PushForFunctionBody` instead. auto PushForEntity(SemIR::InstId scope_inst_id, SemIR::NameScopeId scope_id, SemIR::SpecificId specific_id, bool lexical_lookup_has_load_error = false) -> void; // Pushes a scope which should be in the same region as the current scope. // These can be in a function without breaking `return` scoping. For example, // this is used by struct literals and code blocks. auto PushForSameRegion() -> void; // Pushes a function scope. auto PushForFunctionBody(SemIR::InstId scope_inst_id) -> void; // Pops the top scope from scope_stack_. Removes names from lexical_lookup_. // If `check_unused` is set, checks and emits diagnostics for unused names. auto Pop(bool check_unused = false) -> void; // Pops the top scope from scope_stack_ if it contains no names. auto PopIfEmpty(bool check_unused = false) -> void { if (scope_stack_.back().num_names == 0) { Pop(check_unused); } } // Pops scopes until we return to the specified scope index. auto PopTo(ScopeIndex index, bool check_unused = false) -> void; // Returns the scope index associated with the current scope. auto PeekIndex() const -> ScopeIndex { return Peek().index; } // Returns the name scope associated with the current lexical scope, if any. auto PeekNameScopeId() const -> SemIR::NameScopeId { return Peek().scope_id; } // Returns the instruction associated with the current scope, or `None` if // there is no such instruction, such as for a block scope. auto PeekInstId() const -> SemIR::InstId { return Peek().scope_inst_id; } // Returns the specific associated with the innermost enclosing scope that is // associated with a specific. This will generally be the self specific of the // innermost enclosing generic, as there is no way to enter any other specific // scope. auto PeekSpecificId() const -> SemIR::SpecificId { return Peek().specific_id; } // Returns true if the current scope is inside a function scope (either the // scope itself, or a lexical scope), without an intervening entity scope. auto IsInFunctionScope() const -> bool { return !return_scope_stack_.empty() && !return_scope_stack_.back().nested_scope_index.has_value(); } // Returns the current scope, if it is of the specified kind. Otherwise, // returns nullopt. template auto TryGetCurrentScopeAs() -> std::optional { auto inst_id = PeekInstId(); if (!inst_id.has_value()) { return std::nullopt; } return sem_ir().insts().TryGetAs(inst_id); } // Returns the current scope, assuming it is of the specified kind. // Check-fails if there is no instruction for a current scope, or the scope is // of a different kind. template auto GetCurrentScopeAs() -> InstT { auto inst_id = PeekInstId(); CARBON_CHECK(inst_id.has_value()); return sem_ir().insts().GetAs(inst_id); } // If there is no `returned var` in scope, sets the given instruction to be // the current `returned var` and returns an `None`. If there // is already a `returned var`, returns it instead. auto SetReturnedVarOrGetExisting(SemIR::InstId inst_id, SemIR::NameId name_id) -> SemIR::InstId; // Returns the `returned var` instruction that's currently in scope, or `None` // if there isn't one. auto GetReturnedVar() -> SemIR::InstId { CARBON_CHECK(IsInFunctionScope(), "Handling return but not in a function"); return return_scope_stack_.back().returned_var; } // Returns the decl ID for the current return scope. auto GetReturnScopeDeclId() -> SemIR::InstId { CARBON_CHECK(IsInFunctionScope(), "Handling return but not in a function"); return return_scope_stack_.back().decl_id; } // Looks up the name `name_id` in the current scope and enclosing scopes, but // do not look past `scope_index`. Returns the existing lookup result, if any. // If `use_loc_id` is specified, the name is marked as used at that location. auto LookupInLexicalScopesWithin(SemIR::NameId name_id, ScopeIndex scope_index, SemIR::LocId use_loc_id, bool is_reachable) -> SemIR::InstId; // Looks up the name `name_id` in the current scope and related lexical // scopes. Returns the innermost lexical lookup result, if any, along with a // list of non-lexical scopes in which lookup should also be performed, // ordered from outermost to innermost. If `use_loc_id` is specified, the // name is marked as used at that location. auto LookupInLexicalScopes(SemIR::NameId name_id, SemIR::LocId use_loc_id, bool is_reachable) -> std::pair>; // Looks up the name `name_id` in the current scope, or in `scope_index` if // specified. Returns the existing instruction if the name is already declared // in that scope or any unfinished scope within it, and otherwise adds the // name with the value `target_id` and returns `None`. `is_decl_reachable` // indicates whether the name was declared in a reachable position. auto LookupOrAddName(SemIR::NameId name_id, SemIR::InstId target_id, ScopeIndex scope_index = ScopeIndex::None, bool is_decl_reachable = true) -> SemIR::InstId; // Prepares to add a compile-time binding in the current scope, and returns // its index. The added binding must then be pushed using // `PushCompileTimeBinding`. auto AddCompileTimeBinding() -> SemIR::CompileTimeBindIndex { auto index = scope_stack_.back().next_compile_time_bind_index; ++scope_stack_.back().next_compile_time_bind_index.index; return index; } // Pushes a compile-time binding into the current scope. auto PushCompileTimeBinding(SemIR::InstId bind_id) -> void { compile_time_binding_stack_.AppendToTop(bind_id); } // Temporarily removes the top of the stack and its lexical lookup results. auto Suspend() -> SuspendedScope; // Restores a suspended scope stack entry. auto Restore(SuspendedScope&& scope) -> void; // Runs verification that the processing cleanly finished. auto VerifyOnFinish() const -> void; auto break_continue_stack() -> llvm::SmallVector& { return break_continue_stack_; } auto destroy_id_stack() -> ArrayStack& { return destroy_id_stack_; } auto compile_time_binding_stack() -> ArrayStack& { return compile_time_binding_stack_; } auto full_pattern_stack() -> FullPatternStack& { return full_pattern_stack_; } private: auto sem_ir() const -> const SemIR::File&; auto lexical_lookup() -> LexicalLookup& { return lexical_lookup_; } // An entry in scope_stack_. struct ScopeStackEntry : public MoveOnly { auto is_lexical_scope() const -> bool { return !scope_id.has_value(); } // The sequential index of this scope entry within the file. ScopeIndex index; // The instruction associated with this entry, if any. This can be one of: // // - A `ClassDecl`, for a class definition scope. // - A `FunctionDecl`, for the outermost scope in a function // definition. // - Invalid, for any other scope. SemIR::InstId scope_inst_id; // The name scope associated with this entry, if any. SemIR::NameScopeId scope_id; // The specific associated with this entry, if any. SemIR::SpecificId specific_id; // The next compile-time binding index to allocate in this scope. SemIR::CompileTimeBindIndex next_compile_time_bind_index; // Whether lexical_lookup_ has load errors from this scope or an ancestor // scope. bool lexical_lookup_has_load_error; // Whether a `returned var` was introduced in this scope, and needs to be // unregistered when the scope ends. bool has_returned_var = false; // Whether there are any ids in the `names` set. int num_names = 0; // Names which are registered with lexical_lookup_, and will need to be // unregistered when the scope ends. Set names = {}; }; // A scope in which `return` can be used. struct ReturnScope { // The `FunctionDecl`. SemIR::InstId decl_id; // The value corresponding to the current `returned var`, if any. Will be // set and unset as `returned var`s are declared and go out of scope. SemIR::InstId returned_var = SemIR::InstId::None; // When a nested scope interrupts a return scope, this is the index of the // outermost interrupting scope (the one closest to the function scope). // This can then be used to determine whether we're actually inside the most // recent `ReturnScope`, or inside a different entity scope. // // This won't be set for functions directly inside functions, because they // will have their own `ReturnScope`. // For example, when a `class` is inside a `fn`, it interrupts the function // body by setting this on `PushEntity`; `Pop` will set it back to `None`. ScopeIndex nested_scope_index = ScopeIndex::None; }; // Pushes a scope onto scope_stack_. NameScopeId::None is used for new scopes. // lexical_lookup_has_load_error is used to limit diagnostics when a given // namespace may contain a mix of both successful and failed name imports. auto Push(SemIR::InstId scope_inst_id, SemIR::NameScopeId scope_id, SemIR::SpecificId specific_id, bool lexical_lookup_has_load_error) -> void; auto Peek() const -> const ScopeStackEntry& { return scope_stack_.back(); } // Returns whether lexical lookup currently has any load errors. auto LexicalLookupHasLoadError() const -> bool { return !scope_stack_.empty() && scope_stack_.back().lexical_lookup_has_load_error; } // If inside a return scope, marks a nested scope (see `nested_scope_index`). // Called after pushing the new scope. auto MarkNestingIfInReturnScope() -> void { if (!return_scope_stack_.empty() && !return_scope_stack_.back().nested_scope_index.has_value()) { return_scope_stack_.back().nested_scope_index = scope_stack_.back().index; } } // Marks the name `name_id` as used at the given location. auto MarkUsed(SemIR::NameId name_id, SemIR::LocId loc_id, bool is_reachable) -> void; // Checks that the provided scope's `next_compile_time_bind_index` matches the // full size of the current `compile_time_binding_stack_`. The values should // always match, and this is used to validate the correspondence during // significant changes. auto VerifyNextCompileTimeBindIndex(llvm::StringLiteral label, const ScopeStackEntry& scope) -> void; // Context, used only for checks and emitting diagnostics. Context* context_; // A stack of scopes from which we can `return`. llvm::SmallVector return_scope_stack_; // A stack of `break` and `continue` targets. llvm::SmallVector break_continue_stack_; // A stack for scope context. llvm::SmallVector scope_stack_; // A stack of instances to destroy. This only has entries inside of function // bodies, where destruction on scope exit is required. ArrayStack destroy_id_stack_; // Information about non-lexical scopes. This is a subset of the entries and // the information in scope_stack_. llvm::SmallVector non_lexical_scope_stack_; // A stack of the current compile time bindings. ArrayStack compile_time_binding_stack_; // The index of the next scope that will be pushed onto scope_stack_. The // first is always the package scope. ScopeIndex next_scope_index_ = ScopeIndex::Package; // Tracks lexical lookup results. LexicalLookup lexical_lookup_; // Stack of full-patterns currently being checked. FullPatternStack full_pattern_stack_; }; struct ScopeStack::SuspendedScope : public MoveOnly { // An item that was suspended within this scope. This represents either a // lexical lookup entry in this scope, or a compile time binding entry in this // scope. // // TODO: For compile-time bindings, the common case is that they will both // have a suspended lexical lookup entry and a suspended compile time binding // entry. We should be able to store that as a single ScopeItem rather than // two. struct ScopeItem { static constexpr uint32_t IndexForCompileTimeBinding = -1; // The scope index for a LexicalLookup::SuspendedResult, or // CompileTimeBindingIndex for a suspended compile time binding. uint32_t index; // The instruction within the scope. SemIR::InstId inst_id; // Whether the name was declared in a reachable position. bool is_decl_reachable; // The location of the first use of the name, if any. SemIR::LocId use_loc_id; }; // The suspended scope stack entry. ScopeStackEntry entry; // The list of items that were within this scope when it was suspended. The // inline size is an attempt to keep the size of a `SuspendedFunction` // reasonable while avoiding heap allocations most of the time. llvm::SmallVector suspended_items; }; } // namespace Carbon::Check #endif // CARBON_TOOLCHAIN_CHECK_SCOPE_STACK_H_ ================================================ FILE: toolchain/check/subst.cpp ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #include "toolchain/check/subst.h" #include "toolchain/base/kind_switch.h" #include "toolchain/check/eval.h" #include "toolchain/check/generic.h" #include "toolchain/check/inst.h" #include "toolchain/sem_ir/copy_on_write_block.h" #include "toolchain/sem_ir/ids.h" #include "toolchain/sem_ir/inst.h" #include "toolchain/sem_ir/typed_insts.h" namespace Carbon::Check { auto SubstInstCallbacks::RebuildType(SemIR::TypeInstId type_inst_id) const -> SemIR::TypeId { return context().types().GetTypeIdForTypeInstId(type_inst_id); } auto SubstInstCallbacks::RebuildNewInst(SemIR::LocId loc_id, SemIR::Inst new_inst) const -> SemIR::InstId { auto const_id = EvalOrAddInst( context(), SemIR::LocIdAndInst::UncheckedLoc(loc_id, new_inst)); CARBON_CHECK(const_id.has_value(), "Substitution into constant produced non-constant"); CARBON_CHECK(const_id.is_constant(), "Substitution into constant produced runtime value"); return context().constant_values().GetInstId(const_id); } namespace { // Information about an instruction that we are substituting into. struct WorklistItem { // The instruction that we are substituting into. SemIR::InstId inst_id; // Whether the operands of this instruction have been added to the worklist. bool is_expanded : 1; // Whether the instruction was subst'd and re-added to the worklist. bool is_repeated : 1; // The index of the worklist item to process after we finish updating this // one. For the final child of an instruction, this is the parent. For any // other child, this is the index of the next child of the parent. For the // root, this is -1. int next_index : 31; }; // A list of instructions that we're currently in the process of substituting // into. For details of the algorithm used here, see `SubstConstant`. class Worklist { public: explicit Worklist(SemIR::InstId root_id) { worklist_.push_back({.inst_id = root_id, .is_expanded = false, .is_repeated = false, .next_index = -1}); } auto operator[](int index) -> WorklistItem& { return worklist_[index]; } auto size() -> int { return worklist_.size(); } auto back() -> WorklistItem& { return worklist_.back(); } auto Push(SemIR::InstId inst_id) -> void { CARBON_CHECK(inst_id.has_value()); worklist_.push_back({.inst_id = inst_id, .is_expanded = false, .is_repeated = false, .next_index = static_cast(worklist_.size() + 1)}); CARBON_CHECK(worklist_.back().next_index > 0, "Constant too large."); } auto Pop() -> SemIR::InstId { return worklist_.pop_back_val().inst_id; } private: // Constants can get pretty large, so use a large worklist. This should be // about 4KiB, which should be small enough to comfortably fit on the stack, // but large enough that it's unlikely that we'll need a heap allocation. llvm::SmallVector worklist_; }; } // namespace // Pushes the specified operand onto the worklist. static auto PushOperand(Context& context, Worklist& worklist, SemIR::Inst::ArgAndKind arg) -> void { auto push_block = [&](SemIR::InstBlockId block_id) { for (auto inst_id : context.inst_blocks().Get(SemIR::InstBlockId(block_id))) { worklist.Push(inst_id); } }; auto push_specific = [&](SemIR::SpecificId specific_id) { if (specific_id.has_value()) { push_block(context.specifics().Get(specific_id).args_id); } }; CARBON_KIND_SWITCH(arg) { case CARBON_KIND(SemIR::InstId inst_id): { if (inst_id.has_value()) { worklist.Push(inst_id); } break; } case CARBON_KIND(SemIR::TypeInstId inst_id): { if (inst_id.has_value()) { worklist.Push(inst_id); } break; } case CARBON_KIND(SemIR::InstBlockId inst_block_id): { push_block(inst_block_id); break; } case CARBON_KIND(SemIR::StructTypeFieldsId fields_id): { for (auto field : context.struct_type_fields().Get(fields_id)) { worklist.Push(field.type_inst_id); } break; } case CARBON_KIND(SemIR::SpecificId specific_id): { push_specific(specific_id); break; } case CARBON_KIND(SemIR::SpecificInterfaceId interface_id): { auto interface = context.specific_interfaces().Get(interface_id); push_specific(interface.specific_id); break; } case CARBON_KIND(SemIR::FacetTypeId facet_type_id): { const auto& facet_type_info = context.facet_types().Get(facet_type_id); for (auto interface : facet_type_info.extend_constraints) { push_specific(interface.specific_id); } for (auto interface : facet_type_info.self_impls_constraints) { push_specific(interface.specific_id); } for (auto interface : facet_type_info.extend_named_constraints) { push_specific(interface.specific_id); } for (auto interface : facet_type_info.self_impls_named_constraints) { push_specific(interface.specific_id); } for (auto rewrite : facet_type_info.rewrite_constraints) { worklist.Push(rewrite.lhs_id); worklist.Push(rewrite.rhs_id); } // TODO: Process other requirements as well. break; } default: break; } } // Converts the operands of this instruction into `InstId`s and pushes them onto // the worklist. static auto ExpandOperands(Context& context, Worklist& worklist, SemIR::InstId inst_id) -> void { auto inst = context.insts().Get(inst_id); if (inst.type_id().has_value()) { worklist.Push(context.types().GetTypeInstId(inst.type_id())); } PushOperand(context, worklist, inst.arg0_and_kind()); PushOperand(context, worklist, inst.arg1_and_kind()); } // Pops the specified operand from the worklist and returns it. static auto PopOperand(Context& context, Worklist& worklist, SemIR::Inst::ArgAndKind arg) -> int32_t { auto pop_block_id = [&](SemIR::InstBlockId old_inst_block_id) { auto size = context.inst_blocks().Get(old_inst_block_id).size(); SemIR::CopyOnWriteInstBlock new_inst_block(&context.sem_ir(), old_inst_block_id); for (auto i : llvm::reverse(llvm::seq(size))) { new_inst_block.Set(i, worklist.Pop()); } return new_inst_block.GetCanonical(); }; auto pop_specific = [&](SemIR::SpecificId specific_id) { if (!specific_id.has_value()) { return specific_id; } auto& specific = context.specifics().Get(specific_id); auto args_id = pop_block_id(specific.args_id); return context.specifics().GetOrAdd(specific.generic_id, args_id); }; CARBON_KIND_SWITCH(arg) { case CARBON_KIND(SemIR::InstId inst_id): { if (!inst_id.has_value()) { return arg.value(); } return worklist.Pop().index; } case CARBON_KIND(SemIR::TypeInstId inst_id): { if (!inst_id.has_value()) { return arg.value(); } return worklist.Pop().index; } case CARBON_KIND(SemIR::InstBlockId inst_block_id): { return pop_block_id(inst_block_id).index; } case CARBON_KIND(SemIR::StructTypeFieldsId old_fields_id): { auto old_fields = context.struct_type_fields().Get(old_fields_id); SemIR::CopyOnWriteStructTypeFieldsBlock new_fields(&context.sem_ir(), old_fields_id); for (auto i : llvm::reverse(llvm::seq(old_fields.size()))) { new_fields.Set( i, {.name_id = old_fields[i].name_id, .type_inst_id = context.types().GetAsTypeInstId(worklist.Pop())}); } return new_fields.GetCanonical().index; } case CARBON_KIND(SemIR::SpecificId specific_id): { return pop_specific(specific_id).index; } case CARBON_KIND(SemIR::SpecificInterfaceId interface_id): { auto interface = context.specific_interfaces().Get(interface_id); auto specific_id = pop_specific(interface.specific_id); return context.specific_interfaces() .Add({ .interface_id = interface.interface_id, .specific_id = specific_id, }) .index; } case CARBON_KIND(SemIR::FacetTypeId facet_type_id): { const auto& old_facet_type_info = context.facet_types().Get(facet_type_id); SemIR::FacetTypeInfo new_facet_type_info = { .other_requirements = old_facet_type_info.other_requirements}; // Since these were added to a stack, we get them back in reverse order. new_facet_type_info.rewrite_constraints.resize( old_facet_type_info.rewrite_constraints.size(), SemIR::FacetTypeInfo::RewriteConstraint::None); for (auto& new_constraint : llvm::reverse(new_facet_type_info.rewrite_constraints)) { auto rhs_id = worklist.Pop(); auto lhs_id = worklist.Pop(); new_constraint = {.lhs_id = lhs_id, .rhs_id = rhs_id}; } new_facet_type_info.self_impls_named_constraints.resize( old_facet_type_info.self_impls_named_constraints.size(), SemIR::SpecificNamedConstraint::None); for (auto [old_constraint, new_constraint] : llvm::reverse(llvm::zip_equal( old_facet_type_info.self_impls_named_constraints, new_facet_type_info.self_impls_named_constraints))) { new_constraint = { .named_constraint_id = old_constraint.named_constraint_id, .specific_id = pop_specific(old_constraint.specific_id)}; } new_facet_type_info.extend_named_constraints.resize( old_facet_type_info.extend_named_constraints.size(), SemIR::SpecificNamedConstraint::None); for (auto [old_constraint, new_constraint] : llvm::reverse( llvm::zip_equal(old_facet_type_info.extend_named_constraints, new_facet_type_info.extend_named_constraints))) { new_constraint = { .named_constraint_id = old_constraint.named_constraint_id, .specific_id = pop_specific(old_constraint.specific_id)}; } new_facet_type_info.self_impls_constraints.resize( old_facet_type_info.self_impls_constraints.size(), SemIR::SpecificInterface::None); for (auto [old_constraint, new_constraint] : llvm::reverse( llvm::zip_equal(old_facet_type_info.self_impls_constraints, new_facet_type_info.self_impls_constraints))) { new_constraint = { .interface_id = old_constraint.interface_id, .specific_id = pop_specific(old_constraint.specific_id)}; } new_facet_type_info.extend_constraints.resize( old_facet_type_info.extend_constraints.size(), SemIR::SpecificInterface::None); for (auto [old_constraint, new_constraint] : llvm::reverse( llvm::zip_equal(old_facet_type_info.extend_constraints, new_facet_type_info.extend_constraints))) { new_constraint = { .interface_id = old_constraint.interface_id, .specific_id = pop_specific(old_constraint.specific_id)}; } new_facet_type_info.Canonicalize(); return context.facet_types().Add(new_facet_type_info).index; } default: return arg.value(); } } // Pops the operands of the specified instruction off the worklist and rebuilds // the instruction with the updated operands if it has changed. static auto Rebuild(Context& context, Worklist& worklist, SemIR::InstId inst_id, SubstInstCallbacks& callbacks) -> SemIR::InstId { auto inst = context.insts().Get(inst_id); // Note that we pop in reverse order because we pushed them in forwards order. int32_t arg1 = PopOperand(context, worklist, inst.arg1_and_kind()); int32_t arg0 = PopOperand(context, worklist, inst.arg0_and_kind()); auto type_id = inst.type_id().has_value() ? callbacks.RebuildType( context.types().GetAsTypeInstId(worklist.Pop())) : SemIR::TypeId::None; if (type_id == inst.type_id() && arg0 == inst.arg0() && arg1 == inst.arg1()) { return callbacks.ReuseUnchanged(inst_id); } // TODO: Do we need to require this type to be complete? inst.SetType(type_id); inst.SetArgs(arg0, arg1); return callbacks.Rebuild(inst_id, inst); } auto SubstInst(Context& context, SemIR::InstId inst_id, SubstInstCallbacks& callbacks) -> SemIR::InstId { Worklist worklist(inst_id); // For each instruction that forms part of the constant, we will visit it // twice: // // - First, we visit it with `is_expanded == false`, we add all of its // operands onto the worklist, and process them by following this same // process. // - Then, once all operands are processed, we visit the instruction with // `is_expanded == true`, pop the operands back off the worklist, and if any // of them changed, rebuild this instruction. // // The second step is skipped if we can detect in the first step that the // instruction will not need to be rebuilt. int index = 0; while (index != -1) { auto& item = worklist[index]; if (item.is_expanded) { // Rebuild this item if necessary. Note that this might pop items from the // worklist but does not reallocate, so does not invalidate `item`. auto old_inst_id = std::exchange( item.inst_id, Rebuild(context, worklist, item.inst_id, callbacks)); if (item.is_repeated && old_inst_id != item.inst_id) { // SubstOperandsAndRetry was returned for the item, and the instruction // was rebuilt from new operands, so go through Subst() again. Note that // we've already called Rebuild so we don't want to leave this item as // repeated, and call back to ReuseUnchanged for it again later unless // the next call to Subst() asks for that. item.is_expanded = false; item.is_repeated = false; } else { index = item.next_index; continue; } } if (item.is_repeated) { // SubstAgain was returned for the item, and the result of that Subst() is // at the back of the worklist, which we pop. Note that popping from the // worklist does not reallocate, so does not invalidate `item`. // // When Subst returns SubstAgain, we must call back to Rebuild or // ReuseUnchanged for that work item. item.inst_id = callbacks.ReuseUnchanged(worklist.Pop()); index = item.next_index; continue; } switch (callbacks.Subst(item.inst_id)) { case SubstInstCallbacks::SubstResult::FullySubstituted: // If any instruction is an ErrorInst, combining it into another // instruction will also produce an ErrorInst, so shortcut out here to // save wasted work. if (item.inst_id == SemIR::ErrorInst::InstId) { return SemIR::ErrorInst::InstId; } index = item.next_index; continue; case SubstInstCallbacks::SubstResult::SubstAgain: { item.is_repeated = true; // This modifies `worklist` which invalidates `item`. worklist.Push(item.inst_id); worklist.back().next_index = index; index = worklist.size() - 1; continue; } case SubstInstCallbacks::SubstResult::SubstOperands: break; case SubstInstCallbacks::SubstResult::SubstOperandsAndRetry: item.is_repeated = true; break; } // Extract the operands of this item into the worklist. Note that this // modifies the worklist, so it's not safe to use `item` after // `ExpandOperands` returns. item.is_expanded = true; int first_operand = worklist.size(); int next_index = item.next_index; ExpandOperands(context, worklist, item.inst_id); // If there are any operands, go and update them before rebuilding this // item. if (worklist.size() > first_operand) { worklist.back().next_index = index; index = first_operand; } else { // No need to rebuild this instruction: its operands can't be changed by // substitution because it has none. item.inst_id = callbacks.ReuseUnchanged(item.inst_id); index = next_index; } } CARBON_CHECK(worklist.size() == 1, "Unexpected data left behind in work list"); return worklist.back().inst_id; } auto SubstInst(Context& context, SemIR::TypeInstId inst_id, SubstInstCallbacks& callbacks) -> SemIR::TypeInstId { return context.types().GetAsTypeInstId( SubstInst(context, static_cast(inst_id), callbacks)); } namespace { // Callbacks for performing substitution of a set of Substitutions into a // symbolic constant. class SubstConstantCallbacks final : public SubstInstCallbacks { public: // `context` must not be null. SubstConstantCallbacks(Context* context, SemIR::LocId loc_id, Substitutions substitutions) : SubstInstCallbacks(context), loc_id_(loc_id), substitutions_(substitutions) {} // Applies the given Substitutions to an instruction, in order to replace // SymbolicBinding instructions with the value of the binding. auto Subst(SemIR::InstId& inst_id) -> SubstResult override { if (context().constant_values().Get(inst_id).is_concrete()) { // This instruction is a concrete constant, so can't contain any // bindings that need to be substituted. return SubstResult::FullySubstituted; } // A symbolic binding `as type` contains the EntityNameId of that symbolic // binding. If it matches a substitution, then we want to point the // EntityNameId to the substitution facet value. if (auto bind = context().insts().TryGetAs(inst_id)) { auto& entity_name = context().entity_names().Get(bind->entity_name_id); for (auto [bind_index, replacement_id] : substitutions_) { if (entity_name.bind_index() == bind_index) { auto replacement_inst_id = context().constant_values().GetInstId(replacement_id); inst_id = RebuildNewInst( loc_id_, { .type_id = SemIR::TypeType::TypeId, .facet_value_inst_id = replacement_inst_id, }); return SubstResult::FullySubstituted; } } } auto entity_name_id = SemIR::EntityNameId::None; if (auto bind = context().insts().TryGetAs(inst_id)) { entity_name_id = bind->entity_name_id; } else if (auto bind = context().insts().TryGetAs( inst_id)) { entity_name_id = bind->entity_name_id; } else { return SubstResult::SubstOperands; } auto& entity_name = context().entity_names().Get(entity_name_id); // This is a symbolic binding. Check if we're substituting it. // TODO: Consider building a hash map for substitutions. We might have a // lot of them. for (auto [bind_index, replacement_id] : substitutions_) { if (entity_name.bind_index() == bind_index) { // This is the binding we're replacing. Perform substitution. inst_id = context().constant_values().GetInstId(replacement_id); return SubstResult::FullySubstituted; } } // If it's not being substituted, we still need to look through it, as we // may need to substitute into its type (a `FacetType`, with one or more // `SpecificInterfaces` within). return SubstResult::SubstOperands; } // Rebuilds an instruction by building a new constant. auto Rebuild(SemIR::InstId /*old_inst_id*/, SemIR::Inst new_inst) -> SemIR::InstId override { return RebuildNewInst(loc_id_, new_inst); } private: SemIR::LocId loc_id_; Substitutions substitutions_; }; } // namespace auto SubstConstant(Context& context, SemIR::LocId loc_id, SemIR::ConstantId const_id, Substitutions substitutions) -> SemIR::ConstantId { CARBON_CHECK(const_id.is_constant(), "Substituting into non-constant"); if (substitutions.empty()) { // Nothing to substitute. return const_id; } if (!const_id.is_symbolic()) { // A concrete constant can't contain a reference to a symbolic binding. return const_id; } auto callbacks = SubstConstantCallbacks(&context, loc_id, substitutions); auto subst_inst_id = SubstInst( context, context.constant_values().GetInstId(const_id), callbacks); return context.constant_values().Get(subst_inst_id); } } // namespace Carbon::Check ================================================ FILE: toolchain/check/subst.h ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #ifndef CARBON_TOOLCHAIN_CHECK_SUBST_H_ #define CARBON_TOOLCHAIN_CHECK_SUBST_H_ #include "toolchain/check/context.h" #include "toolchain/sem_ir/ids.h" namespace Carbon::Check { // Callbacks used by SubstInst to recursively substitute into and rebuild an // instruction. class SubstInstCallbacks { public: explicit SubstInstCallbacks(Context* context) : context_(context) {} auto context() const -> Context& { return *context_; } // How further substitution should or should not be applied to the instruction // after Subst is done. // // Rebuild or ReuseUnchaged will always be called when SubstAgain or // SubstOperands is returned, after processing anything inside the instruction // after Subst. enum SubstResult { // Don't substitute into the operands of the instruction. FullySubstituted, // Attempt to substitute into the operands of the instruction. SubstOperands, // Attempt to substitute again on the resulting instruction, acting like // recursion on the instruction itself. SubstAgain, // Attempt to substitute into the operands of the instruction. If the InstId // returned from Rebuild or ReuseUnchanged differs from the input (typically // because some operand in the instruction changed), then the new // instruction will be given to `Subst` again afterward. This allows for the // uncommon case of substituting from the inside out. SubstOperandsAndRetry, }; // Performs any needed substitution into an instruction. The instruction ID // should be updated as necessary to represent the new instruction. // // Return FullySubstituted if the resulting instruction ID is // fully-substituted. Return SubstOperands if substitution may be needed into // operands of the instruction, or SubstAgain if the replaced instruction // itself should have substitution applied to it again. Return // SubstOperandsAndRetry to recurse on the instructions operands and then // substitute the resulting instruction afterward, if the instruction is // replaced by a new one (typically due to Rebuild when the operands changed). // // When SubstOperands, SubstAgain, or SubstOperandsAndRetry is returned, it // results in a call back to Rebuild or ReuseUnchanged when that instruction's // substitution step is complete. virtual auto Subst(SemIR::InstId& inst_id) -> SubstResult = 0; // Rebuilds the type of an instruction from the substituted type instruction. // By default this builds the unattached type described by the given type ID. virtual auto RebuildType(SemIR::TypeInstId type_inst_id) const -> SemIR::TypeId; // Rebuilds an instruction whose operands were changed by substitution. // `orig_inst_id` is the instruction prior to substitution, and `new_inst` is // the substituted instruction. Returns the new instruction ID to use to refer // to `new_inst`. virtual auto Rebuild(SemIR::InstId orig_inst_id, SemIR::Inst new_inst) -> SemIR::InstId = 0; // Performs any work needed when no substitutions were performed into an // instruction for which `Subst` returned `false`. Provides an opportunity to // perform any necessary updates to the instruction beyond updating its // operands. Returns the new instruction ID to use to refer to `orig_inst_id`. virtual auto ReuseUnchanged(SemIR::InstId orig_inst_id) -> SemIR::InstId { return orig_inst_id; } // Builds a new constant by evaluating `new_inst`, and returns its `InstId`. // // This can be used to implement `Rebuild` in straightforward cases. auto RebuildNewInst(SemIR::LocId loc_id, SemIR::Inst new_inst) const -> SemIR::InstId; template auto RebuildNewInst(SemIR::LocId loc_id, InstT new_inst) const -> SemIR::InstId { return RebuildNewInst(loc_id, static_cast(new_inst)); } private: Context* context_; }; // Performs substitution into `inst_id` and its operands recursively, using // `callbacks` to process each instruction. For each instruction encountered, // calls `Subst` to perform substitution on that instruction. auto SubstInst(Context& context, SemIR::InstId inst_id, SubstInstCallbacks& callbacks) -> SemIR::InstId; auto SubstInst(Context& context, SemIR::TypeInstId inst_id, SubstInstCallbacks& callbacks) -> SemIR::TypeInstId; // A substitution that is being performed. struct Substitution { // The index of a `SymbolicBinding` instruction that is being replaced. SemIR::CompileTimeBindIndex bind_id; // The replacement constant value to substitute. SemIR::ConstantId replacement_id; }; using Substitutions = llvm::ArrayRef; // Replaces the `SymbolicBinding` instruction `bind_id` with `replacement_id` // throughout the constant `const_id`, and returns the substituted value. auto SubstConstant(Context& context, SemIR::LocId loc_id, SemIR::ConstantId const_id, Substitutions substitutions) -> SemIR::ConstantId; } // namespace Carbon::Check #endif // CARBON_TOOLCHAIN_CHECK_SUBST_H_ ================================================ FILE: toolchain/check/testdata/alias/basics.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/convert.carbon // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/alias/basics.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/alias/basics.carbon // --- alias.carbon library "[[@TEST_NAME]]"; class C {}; //@dump-sem-ir-begin alias c = C; //@dump-sem-ir-end let l: c = {}; // --- alias_of_alias.carbon library "[[@TEST_NAME]]"; class C {} //@dump-sem-ir-begin alias a = C; alias b = a; alias c = b; let d: c = {}; //@dump-sem-ir-end // --- fail_control_flow.carbon library "[[@TEST_NAME]]"; // CHECK:STDERR: fail_control_flow.carbon:[[@LINE+8]]:11: error: semantics TODO: `Control flow expressions are currently only supported inside functions.` [SemanticsTodo] // CHECK:STDERR: alias a = true or false; // CHECK:STDERR: ^~~~~~~ // CHECK:STDERR: // CHECK:STDERR: fail_control_flow.carbon:[[@LINE+4]]:11: error: semantics TODO: `Control flow expressions are currently only supported inside functions.` [SemanticsTodo] // CHECK:STDERR: alias a = true or false; // CHECK:STDERR: ^~~~~~~~~~~~~ // CHECK:STDERR: alias a = true or false; // --- fail_name_conflict.carbon library "[[@TEST_NAME]]"; class C {} alias a = C; // CHECK:STDERR: fail_name_conflict.carbon:[[@LINE+7]]:5: error: duplicate name `a` being declared in the same scope [NameDeclDuplicate] // CHECK:STDERR: var a: C = {}; // CHECK:STDERR: ^ // CHECK:STDERR: fail_name_conflict.carbon:[[@LINE-4]]:7: note: name is previously declared here [NameDeclPrevious] // CHECK:STDERR: alias a = C; // CHECK:STDERR: ^ // CHECK:STDERR: var a: C = {}; var b: C = {}; // CHECK:STDERR: fail_name_conflict.carbon:[[@LINE+7]]:7: error: duplicate name `b` being declared in the same scope [NameDeclDuplicate] // CHECK:STDERR: alias b = C; // CHECK:STDERR: ^ // CHECK:STDERR: fail_name_conflict.carbon:[[@LINE-4]]:5: note: name is previously declared here [NameDeclPrevious] // CHECK:STDERR: var b: C = {}; // CHECK:STDERR: ^ // CHECK:STDERR: alias b = C; // --- fail_not_constant.carbon library "[[@TEST_NAME]]"; var a: () = (); var b: ()* = &a; // CHECK:STDERR: fail_not_constant.carbon:[[@LINE+4]]:11: error: alias initializer must be a name reference [AliasRequiresNameRef] // CHECK:STDERR: alias c = *b; // CHECK:STDERR: ^~ // CHECK:STDERR: alias c = *b; // --- fail_params.carbon library "[[@TEST_NAME]]"; // CHECK:STDERR: fail_params.carbon:[[@LINE+8]]:8: error: `alias` declaration cannot have parameters [UnexpectedDeclNameParams] // CHECK:STDERR: alias A(T:! type) = T*; // CHECK:STDERR: ^~~~~~~~~~ // CHECK:STDERR: // CHECK:STDERR: fail_params.carbon:[[@LINE+4]]:21: error: alias initializer must be a name reference [AliasRequiresNameRef] // CHECK:STDERR: alias A(T:! type) = T*; // CHECK:STDERR: ^~ // CHECK:STDERR: alias A(T:! type) = T*; // --- fail_modifiers.carbon library "[[@TEST_NAME]]"; class Class {} // CHECK:STDERR: fail_modifiers.carbon:[[@LINE+25]]:10: error: `base` not allowed on declaration with `abstract` [ModifierNotAllowedWith] // CHECK:STDERR: abstract base default final alias A = Class; // CHECK:STDERR: ^~~~ // CHECK:STDERR: fail_modifiers.carbon:[[@LINE+22]]:1: note: `abstract` previously appeared here [ModifierPrevious] // CHECK:STDERR: abstract base default final alias A = Class; // CHECK:STDERR: ^~~~~~~~ // CHECK:STDERR: // CHECK:STDERR: fail_modifiers.carbon:[[@LINE+18]]:15: error: `default` not allowed on declaration with `abstract` [ModifierNotAllowedWith] // CHECK:STDERR: abstract base default final alias A = Class; // CHECK:STDERR: ^~~~~~~ // CHECK:STDERR: fail_modifiers.carbon:[[@LINE+15]]:1: note: `abstract` previously appeared here [ModifierPrevious] // CHECK:STDERR: abstract base default final alias A = Class; // CHECK:STDERR: ^~~~~~~~ // CHECK:STDERR: // CHECK:STDERR: fail_modifiers.carbon:[[@LINE+11]]:23: error: `final` not allowed on declaration with `abstract` [ModifierNotAllowedWith] // CHECK:STDERR: abstract base default final alias A = Class; // CHECK:STDERR: ^~~~~ // CHECK:STDERR: fail_modifiers.carbon:[[@LINE+8]]:1: note: `abstract` previously appeared here [ModifierPrevious] // CHECK:STDERR: abstract base default final alias A = Class; // CHECK:STDERR: ^~~~~~~~ // CHECK:STDERR: // CHECK:STDERR: fail_modifiers.carbon:[[@LINE+4]]:1: error: `abstract` not allowed on `alias` declaration [ModifierNotAllowedOnDeclaration] // CHECK:STDERR: abstract base default final alias A = Class; // CHECK:STDERR: ^~~~~~~~ // CHECK:STDERR: abstract base default final alias A = Class; // CHECK:STDERR: fail_modifiers.carbon:[[@LINE+4]]:1: error: `impl` not allowed on `alias` declaration [ModifierNotAllowedOnDeclaration] // CHECK:STDERR: impl alias B = Class; // CHECK:STDERR: ^~~~ // CHECK:STDERR: impl alias B = Class; // CHECK:STDERR: fail_modifiers.carbon:[[@LINE+4]]:1: error: `extern` not allowed on `alias` declaration [ModifierNotAllowedOnDeclaration] // CHECK:STDERR: extern alias C = Class; // CHECK:STDERR: ^~~~~~ // CHECK:STDERR: extern alias C = Class; // CHECK:STDOUT: --- alias.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %C: type = class_type @C [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: %C.ref: type = name_ref C, %C.decl [concrete = constants.%C] // CHECK:STDOUT: %c: type = alias_binding c, %C.decl [concrete = constants.%C] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @__global_init() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- alias_of_alias.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %C: type = class_type @C [concrete] // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %pattern_type: type = pattern_type %C [concrete] // CHECK:STDOUT: %empty_struct: %empty_struct_type = struct_value () [concrete] // CHECK:STDOUT: %C.val: %C = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: %C.ref: type = name_ref C, %C.decl [concrete = constants.%C] // CHECK:STDOUT: %a: type = alias_binding a, %C.decl [concrete = constants.%C] // CHECK:STDOUT: %a.ref: type = name_ref a, %a [concrete = constants.%C] // CHECK:STDOUT: %b: type = alias_binding b, %a [concrete = constants.%C] // CHECK:STDOUT: %b.ref: type = name_ref b, %b [concrete = constants.%C] // CHECK:STDOUT: %c: type = alias_binding c, %b [concrete = constants.%C] // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %d.patt: %pattern_type = value_binding_pattern d [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %c.ref: type = name_ref c, %c [concrete = constants.%C] // CHECK:STDOUT: %.loc10_13.1: ref %C = temporary_storage // CHECK:STDOUT: %.loc10_13.2: init %C to %.loc10_13.1 = class_init () [concrete = constants.%C.val] // CHECK:STDOUT: %.loc10_13.3: init %C = converted @__global_init.%.loc10, %.loc10_13.2 [concrete = constants.%C.val] // CHECK:STDOUT: %.loc10_13.4: ref %C = temporary %.loc10_13.1, %.loc10_13.3 // CHECK:STDOUT: %.loc10_13.5: %C = acquire_value %.loc10_13.4 // CHECK:STDOUT: %d: %C = value_binding d, %.loc10_13.5 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @__global_init() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %.loc10: %empty_struct_type = struct_literal () [concrete = constants.%empty_struct] // CHECK:STDOUT: // CHECK:STDOUT: } // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/alias/builtins.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/primitives.carbon // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/alias/builtins.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/alias/builtins.carbon // --- i32.carbon library "[[@TEST_NAME]]"; //@dump-sem-ir-begin alias a = i32; //@dump-sem-ir-end // --- bool.carbon library "[[@TEST_NAME]]"; //@dump-sem-ir-begin alias b = bool; //@dump-sem-ir-end // --- fail_bool_value.carbon library "[[@TEST_NAME]]"; //@dump-sem-ir-begin // CHECK:STDERR: fail_bool_value.carbon:[[@LINE+4]]:11: error: alias initializer must be a name reference [AliasRequiresNameRef] // CHECK:STDERR: alias a = false; // CHECK:STDERR: ^~~~~ // CHECK:STDERR: alias a = false; // This fails silently due to the above diagnostic. let a_test: bool = a; //@dump-sem-ir-end // CHECK:STDOUT: --- i32.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [concrete] // CHECK:STDOUT: %i32: type = class_type @Int, @Int(%int_32) [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: %i32: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %a: type = alias_binding a, constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- bool.carbon // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: %.loc5: type = type_literal bool [concrete = bool] // CHECK:STDOUT: %b: type = alias_binding b, bool [concrete = bool] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- fail_bool_value.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %false: bool = bool_literal false [concrete] // CHECK:STDOUT: %pattern_type.831: type = pattern_type bool [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: %false: bool = bool_literal false [concrete = constants.%false] // CHECK:STDOUT: %a: = alias_binding a, [concrete = ] // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %a_test.patt: %pattern_type.831 = value_binding_pattern a_test [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %.loc12: type = type_literal bool [concrete = bool] // CHECK:STDOUT: %a_test: bool = value_binding a_test, [concrete = ] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @__global_init() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %a.ref: = name_ref a, file.%a [concrete = ] // CHECK:STDOUT: // CHECK:STDOUT: } // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/alias/export_name.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/convert.carbon // EXTRA-ARGS: --dump-sem-ir-ranges=if-present // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/alias/export_name.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/alias/export_name.carbon // ============================================================================ // Setup files // ============================================================================ // --- base.carbon library "[[@TEST_NAME]]"; class C {} alias D = C; // --- export.carbon library "[[@TEST_NAME]]"; import library "base"; export D; // --- export_orig.carbon library "[[@TEST_NAME]]"; import library "base"; export C; // ============================================================================ // Test files // ============================================================================ // --- use_export.carbon library "[[@TEST_NAME]]"; import library "export"; var d: D = {}; // --- fail_orig_name_not_in_export.carbon library "[[@TEST_NAME]]"; import library "export"; // CHECK:STDERR: fail_orig_name_not_in_export.carbon:[[@LINE+4]]:8: error: name `C` not found [NameNotFound] // CHECK:STDERR: var c: C = {}; // CHECK:STDERR: ^ // CHECK:STDERR: var c: C = {}; // --- indirect_compat.carbon library "[[@TEST_NAME]]"; import library "export"; import library "export_orig"; var c: C = {}; var d: D* = &c; // CHECK:STDOUT: --- base.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %C: type = class_type @C [concrete] // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %complete_type: = complete_type_witness %empty_struct_type [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .C = %C.decl // CHECK:STDOUT: .D = %D // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %C.decl: type = class_decl @C [concrete = constants.%C] {} {} // CHECK:STDOUT: %C.ref: type = name_ref C, %C.decl [concrete = constants.%C] // CHECK:STDOUT: %D: type = alias_binding D, %C.decl [concrete = constants.%C] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @C { // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%C // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- export.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %C: type = class_type @C [concrete] // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %complete_type: = complete_type_witness %empty_struct_type [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Main.C = import_ref Main//base, C, unloaded // CHECK:STDOUT: %Main.D: type = import_ref Main//base, D, loaded [concrete = constants.%C] // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: %Main.import_ref.8f2: = import_ref Main//base, loc4_10, loaded [concrete = constants.%complete_type] // CHECK:STDOUT: %Main.import_ref.743 = import_ref Main//base, inst{{[0-9A-F]+}} [no loc], unloaded // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .C = imports.%Main.C // CHECK:STDOUT: .D = %D // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %default.import = import // CHECK:STDOUT: %D: type = export D, imports.%Main.D [concrete = constants.%C] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @C [from "base.carbon"] { // CHECK:STDOUT: complete_type_witness = imports.%Main.import_ref.8f2 // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = imports.%Main.import_ref.743 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- export_orig.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %C: type = class_type @C [concrete] // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %complete_type: = complete_type_witness %empty_struct_type [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Main.C: type = import_ref Main//base, C, loaded [concrete = constants.%C] // CHECK:STDOUT: %Main.D = import_ref Main//base, D, unloaded // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: %Main.import_ref.8f2: = import_ref Main//base, loc4_10, loaded [concrete = constants.%complete_type] // CHECK:STDOUT: %Main.import_ref.743 = import_ref Main//base, inst{{[0-9A-F]+}} [no loc], unloaded // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .C = %C // CHECK:STDOUT: .D = imports.%Main.D // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %default.import = import // CHECK:STDOUT: %C: type = export C, imports.%Main.C [concrete = constants.%C] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @C [from "base.carbon"] { // CHECK:STDOUT: complete_type_witness = imports.%Main.import_ref.8f2 // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = imports.%Main.import_ref.743 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- use_export.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %C: type = class_type @C [concrete] // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %complete_type: = complete_type_witness %empty_struct_type [concrete] // CHECK:STDOUT: %pattern_type: type = pattern_type %C [concrete] // CHECK:STDOUT: %empty_struct: %empty_struct_type = struct_value () [concrete] // CHECK:STDOUT: %C.val: %C = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Main.D: type = import_ref Main//export, D, loaded [concrete = constants.%C] // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: %Main.import_ref.8db: = import_ref Main//export, inst{{[0-9A-F]+}} [indirect], loaded [concrete = constants.%complete_type] // CHECK:STDOUT: %Main.import_ref.a60 = import_ref Main//export, inst{{[0-9A-F]+}} [indirect], unloaded // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .D = imports.%Main.D // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .d = %d // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %default.import = import // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %d.patt: %pattern_type = ref_binding_pattern d [concrete] // CHECK:STDOUT: %d.var_patt: %pattern_type = var_pattern %d.patt [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %d.var: ref %C = var %d.var_patt [concrete] // CHECK:STDOUT: %D.ref: type = name_ref D, imports.%Main.D [concrete = constants.%C] // CHECK:STDOUT: %d: ref %C = ref_binding d, %d.var [concrete = %d.var] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @C [from "export.carbon"] { // CHECK:STDOUT: complete_type_witness = imports.%Main.import_ref.8db // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = imports.%Main.import_ref.a60 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @__global_init() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %.loc6_13.1: %empty_struct_type = struct_literal () [concrete = constants.%empty_struct] // CHECK:STDOUT: %.loc6_13.2: init %C to file.%d.var = class_init () [concrete = constants.%C.val] // CHECK:STDOUT: %.loc6_1: init %C = converted %.loc6_13.1, %.loc6_13.2 [concrete = constants.%C.val] // CHECK:STDOUT: assign file.%d.var, %.loc6_1 // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- fail_orig_name_not_in_export.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %empty_struct: %empty_struct_type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Main.D = import_ref Main//export, D, unloaded // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .D = imports.%Main.D // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .C = // CHECK:STDOUT: .c = %c // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %default.import = import // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %c.patt: = ref_binding_pattern c [concrete] // CHECK:STDOUT: %c.var_patt: = var_pattern %c.patt [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %c.var: ref = var %c.var_patt [concrete = ] // CHECK:STDOUT: %C.ref: = name_ref C, [concrete = ] // CHECK:STDOUT: %c: ref = ref_binding c, [concrete = ] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @__global_init() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %.loc10: %empty_struct_type = struct_literal () [concrete = constants.%empty_struct] // CHECK:STDOUT: assign file.%c.var, // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- indirect_compat.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %C: type = class_type @C [concrete] // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %complete_type.357: = complete_type_witness %empty_struct_type [concrete] // CHECK:STDOUT: %pattern_type.7c7: type = pattern_type %C [concrete] // CHECK:STDOUT: %empty_struct: %empty_struct_type = struct_value () [concrete] // CHECK:STDOUT: %C.val: %C = struct_value () [concrete] // CHECK:STDOUT: %ptr.31e: type = ptr_type %C [concrete] // CHECK:STDOUT: %pattern_type.506: type = pattern_type %ptr.31e [concrete] // CHECK:STDOUT: %addr: %ptr.31e = addr_of file.%c.var [concrete] // CHECK:STDOUT: %Copy.type: type = facet_type <@Copy> [concrete] // CHECK:STDOUT: %T.67d: type = symbolic_binding T, 0 [symbolic] // CHECK:STDOUT: %ptr.as.Copy.impl.Op.type.2d4: type = fn_type @ptr.as.Copy.impl.Op, @ptr.as.Copy.impl(%T.67d) [symbolic] // CHECK:STDOUT: %ptr.as.Copy.impl.Op.74e: %ptr.as.Copy.impl.Op.type.2d4 = struct_value () [symbolic] // CHECK:STDOUT: %Copy.impl_witness.2c7: = impl_witness imports.%Copy.impl_witness_table.c3a, @ptr.as.Copy.impl(%C) [concrete] // CHECK:STDOUT: %ptr.as.Copy.impl.Op.type.411: type = fn_type @ptr.as.Copy.impl.Op, @ptr.as.Copy.impl(%C) [concrete] // CHECK:STDOUT: %ptr.as.Copy.impl.Op.ed9: %ptr.as.Copy.impl.Op.type.411 = struct_value () [concrete] // CHECK:STDOUT: %Copy.facet: %Copy.type = facet_value %ptr.31e, (%Copy.impl_witness.2c7) [concrete] // CHECK:STDOUT: %Copy.WithSelf.Op.type.259: type = fn_type @Copy.WithSelf.Op, @Copy.WithSelf(%Copy.facet) [concrete] // CHECK:STDOUT: %.64b: type = fn_type_with_self_type %Copy.WithSelf.Op.type.259, %Copy.facet [concrete] // CHECK:STDOUT: %ptr.as.Copy.impl.Op.bound: = bound_method %addr, %ptr.as.Copy.impl.Op.ed9 [concrete] // CHECK:STDOUT: %ptr.as.Copy.impl.Op.specific_fn: = specific_function %ptr.as.Copy.impl.Op.ed9, @ptr.as.Copy.impl.Op(%C) [concrete] // CHECK:STDOUT: %bound_method: = bound_method %addr, %ptr.as.Copy.impl.Op.specific_fn [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Main.D: type = import_ref Main//export, D, loaded [concrete = constants.%C] // CHECK:STDOUT: %Main.C: type = import_ref Main//export_orig, C, loaded [concrete = constants.%C] // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: .Copy = %Core.Copy // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: %Main.import_ref.8db: = import_ref Main//export_orig, inst{{[0-9A-F]+}} [indirect], loaded [concrete = constants.%complete_type.357] // CHECK:STDOUT: %Main.import_ref.a60 = import_ref Main//export_orig, inst{{[0-9A-F]+}} [indirect], unloaded // CHECK:STDOUT: %Core.Copy: type = import_ref Core//prelude/parts/copy, Copy, loaded [concrete = constants.%Copy.type] // CHECK:STDOUT: %Core.import_ref.203: @ptr.as.Copy.impl.%ptr.as.Copy.impl.Op.type (%ptr.as.Copy.impl.Op.type.2d4) = import_ref Core//prelude/parts/copy, loc{{\d+_\d+}}, loaded [symbolic = @ptr.as.Copy.impl.%ptr.as.Copy.impl.Op (constants.%ptr.as.Copy.impl.Op.74e)] // CHECK:STDOUT: %Copy.impl_witness_table.c3a = impl_witness_table (%Core.import_ref.203), @ptr.as.Copy.impl [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .D = imports.%Main.D // CHECK:STDOUT: .C = imports.%Main.C // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .c = %c // CHECK:STDOUT: .d = %d // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %default.import = import // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %c.patt: %pattern_type.7c7 = ref_binding_pattern c [concrete] // CHECK:STDOUT: %c.var_patt: %pattern_type.7c7 = var_pattern %c.patt [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %c.var: ref %C = var %c.var_patt [concrete] // CHECK:STDOUT: %C.ref: type = name_ref C, imports.%Main.C [concrete = constants.%C] // CHECK:STDOUT: %c: ref %C = ref_binding c, %c.var [concrete = %c.var] // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %d.patt: %pattern_type.506 = ref_binding_pattern d [concrete] // CHECK:STDOUT: %d.var_patt: %pattern_type.506 = var_pattern %d.patt [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %d.var: ref %ptr.31e = var %d.var_patt [concrete] // CHECK:STDOUT: %.loc8: type = splice_block %ptr [concrete = constants.%ptr.31e] { // CHECK:STDOUT: %D.ref: type = name_ref D, imports.%Main.D [concrete = constants.%C] // CHECK:STDOUT: %ptr: type = ptr_type %D.ref [concrete = constants.%ptr.31e] // CHECK:STDOUT: } // CHECK:STDOUT: %d: ref %ptr.31e = ref_binding d, %d.var [concrete = %d.var] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @C [from "export_orig.carbon"] { // CHECK:STDOUT: complete_type_witness = imports.%Main.import_ref.8db // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = imports.%Main.import_ref.a60 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @__global_init() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %.loc7_13.1: %empty_struct_type = struct_literal () [concrete = constants.%empty_struct] // CHECK:STDOUT: %.loc7_13.2: init %C to file.%c.var = class_init () [concrete = constants.%C.val] // CHECK:STDOUT: %.loc7_1: init %C = converted %.loc7_13.1, %.loc7_13.2 [concrete = constants.%C.val] // CHECK:STDOUT: assign file.%c.var, %.loc7_1 // CHECK:STDOUT: %c.ref: ref %C = name_ref c, file.%c [concrete = file.%c.var] // CHECK:STDOUT: %addr: %ptr.31e = addr_of %c.ref [concrete = constants.%addr] // CHECK:STDOUT: %impl.elem0: %.64b = impl_witness_access constants.%Copy.impl_witness.2c7, element0 [concrete = constants.%ptr.as.Copy.impl.Op.ed9] // CHECK:STDOUT: %bound_method.loc8_13.1: = bound_method %addr, %impl.elem0 [concrete = constants.%ptr.as.Copy.impl.Op.bound] // CHECK:STDOUT: %specific_fn: = specific_function %impl.elem0, @ptr.as.Copy.impl.Op(constants.%C) [concrete = constants.%ptr.as.Copy.impl.Op.specific_fn] // CHECK:STDOUT: %bound_method.loc8_13.2: = bound_method %addr, %specific_fn [concrete = constants.%bound_method] // CHECK:STDOUT: %ptr.as.Copy.impl.Op.call: init %ptr.31e = call %bound_method.loc8_13.2(%addr) [concrete = constants.%addr] // CHECK:STDOUT: assign file.%d.var, %ptr.as.Copy.impl.Op.call // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/alias/import.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/convert.carbon // EXTRA-ARGS: --dump-sem-ir-ranges=if-present // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/alias/import.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/alias/import.carbon // --- class1.carbon library "[[@TEST_NAME]]"; class C {} alias c_alias = C; var a: C*; // --- class2.carbon library "[[@TEST_NAME]]"; import library "class1"; alias c_alias_alias = c_alias; var b: c_alias*; // --- class3.carbon library "[[@TEST_NAME]]"; import library "class2"; var c: c_alias_alias*; // --- var1.carbon library "[[@TEST_NAME]]"; var a: () = (); alias a_alias = a; // --- var2.carbon library "[[@TEST_NAME]]"; import library "var1"; alias a_alias_alias = a_alias; var b: () = a_alias; // --- var3.carbon library "[[@TEST_NAME]]"; import library "var2"; var c: () = a_alias_alias; // CHECK:STDOUT: --- class1.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %C: type = class_type @C [concrete] // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %complete_type.357: = complete_type_witness %empty_struct_type [concrete] // CHECK:STDOUT: %ptr: type = ptr_type %C [concrete] // CHECK:STDOUT: %pattern_type.506: type = pattern_type %ptr [concrete] // CHECK:STDOUT: %DefaultOrUnformed.type: type = facet_type <@DefaultOrUnformed> [concrete] // CHECK:STDOUT: %T: type = symbolic_binding T, 0 [symbolic] // CHECK:STDOUT: %T.as.DefaultOrUnformed.impl.Op.type.6c6: type = fn_type @T.as.DefaultOrUnformed.impl.Op, @T.as.DefaultOrUnformed.impl(%T) [symbolic] // CHECK:STDOUT: %T.as.DefaultOrUnformed.impl.Op.ae1: %T.as.DefaultOrUnformed.impl.Op.type.6c6 = struct_value () [symbolic] // CHECK:STDOUT: %DefaultOrUnformed.impl_witness.910: = impl_witness imports.%DefaultOrUnformed.impl_witness_table, @T.as.DefaultOrUnformed.impl(%ptr) [concrete] // CHECK:STDOUT: %T.as.DefaultOrUnformed.impl.Op.type.7ef: type = fn_type @T.as.DefaultOrUnformed.impl.Op, @T.as.DefaultOrUnformed.impl(%ptr) [concrete] // CHECK:STDOUT: %T.as.DefaultOrUnformed.impl.Op.b07: %T.as.DefaultOrUnformed.impl.Op.type.7ef = struct_value () [concrete] // CHECK:STDOUT: %DefaultOrUnformed.facet: %DefaultOrUnformed.type = facet_value %ptr, (%DefaultOrUnformed.impl_witness.910) [concrete] // CHECK:STDOUT: %T.as.DefaultOrUnformed.impl.Op.specific_fn: = specific_function %T.as.DefaultOrUnformed.impl.Op.b07, @T.as.DefaultOrUnformed.impl.Op(%ptr) [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: .DefaultOrUnformed = %Core.DefaultOrUnformed // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: %Core.DefaultOrUnformed: type = import_ref Core//prelude/parts/default, DefaultOrUnformed, loaded [concrete = constants.%DefaultOrUnformed.type] // CHECK:STDOUT: %Core.import_ref.01d: @T.as.DefaultOrUnformed.impl.%T.as.DefaultOrUnformed.impl.Op.type (%T.as.DefaultOrUnformed.impl.Op.type.6c6) = import_ref Core//prelude/parts/default, loc{{\d+_\d+}}, loaded [symbolic = @T.as.DefaultOrUnformed.impl.%T.as.DefaultOrUnformed.impl.Op (constants.%T.as.DefaultOrUnformed.impl.Op.ae1)] // CHECK:STDOUT: %DefaultOrUnformed.impl_witness_table = impl_witness_table (%Core.import_ref.01d), @T.as.DefaultOrUnformed.impl [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .C = %C.decl // CHECK:STDOUT: .c_alias = %c_alias // CHECK:STDOUT: .a = %a // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %C.decl: type = class_decl @C [concrete = constants.%C] {} {} // CHECK:STDOUT: %C.ref.loc6: type = name_ref C, %C.decl [concrete = constants.%C] // CHECK:STDOUT: %c_alias: type = alias_binding c_alias, %C.decl [concrete = constants.%C] // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %a.patt: %pattern_type.506 = ref_binding_pattern a [concrete] // CHECK:STDOUT: %a.var_patt: %pattern_type.506 = var_pattern %a.patt [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %a.var: ref %ptr = var %a.var_patt [concrete] // CHECK:STDOUT: %.loc8: type = splice_block %ptr [concrete = constants.%ptr] { // CHECK:STDOUT: %C.ref.loc8: type = name_ref C, %C.decl [concrete = constants.%C] // CHECK:STDOUT: %ptr: type = ptr_type %C.ref.loc8 [concrete = constants.%ptr] // CHECK:STDOUT: } // CHECK:STDOUT: %a: ref %ptr = ref_binding a, %a.var [concrete = %a.var] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @C { // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type.357] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%C // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @__global_init() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %DefaultOrUnformed.facet: %DefaultOrUnformed.type = facet_value constants.%ptr, (constants.%DefaultOrUnformed.impl_witness.910) [concrete = constants.%DefaultOrUnformed.facet] // CHECK:STDOUT: %.loc8_10.1: %DefaultOrUnformed.type = converted constants.%ptr, %DefaultOrUnformed.facet [concrete = constants.%DefaultOrUnformed.facet] // CHECK:STDOUT: %as_type: type = facet_access_type %.loc8_10.1 [concrete = constants.%ptr] // CHECK:STDOUT: %.loc8_10.2: type = converted %.loc8_10.1, %as_type [concrete = constants.%ptr] // CHECK:STDOUT: %T.as.DefaultOrUnformed.impl.Op.specific_fn: = specific_function constants.%T.as.DefaultOrUnformed.impl.Op.b07, @T.as.DefaultOrUnformed.impl.Op(constants.%ptr) [concrete = constants.%T.as.DefaultOrUnformed.impl.Op.specific_fn] // CHECK:STDOUT: %T.as.DefaultOrUnformed.impl.Op.call: init %ptr = call %T.as.DefaultOrUnformed.impl.Op.specific_fn() // CHECK:STDOUT: assign file.%a.var, %T.as.DefaultOrUnformed.impl.Op.call // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- class2.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %C: type = class_type @C [concrete] // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %complete_type.357: = complete_type_witness %empty_struct_type [concrete] // CHECK:STDOUT: %ptr: type = ptr_type %C [concrete] // CHECK:STDOUT: %pattern_type.506: type = pattern_type %ptr [concrete] // CHECK:STDOUT: %DefaultOrUnformed.type: type = facet_type <@DefaultOrUnformed> [concrete] // CHECK:STDOUT: %T: type = symbolic_binding T, 0 [symbolic] // CHECK:STDOUT: %T.as.DefaultOrUnformed.impl.Op.type.6c6: type = fn_type @T.as.DefaultOrUnformed.impl.Op, @T.as.DefaultOrUnformed.impl(%T) [symbolic] // CHECK:STDOUT: %T.as.DefaultOrUnformed.impl.Op.ae1: %T.as.DefaultOrUnformed.impl.Op.type.6c6 = struct_value () [symbolic] // CHECK:STDOUT: %DefaultOrUnformed.impl_witness.20b: = impl_witness imports.%DefaultOrUnformed.impl_witness_table, @T.as.DefaultOrUnformed.impl(%ptr) [concrete] // CHECK:STDOUT: %T.as.DefaultOrUnformed.impl.Op.type.7ef: type = fn_type @T.as.DefaultOrUnformed.impl.Op, @T.as.DefaultOrUnformed.impl(%ptr) [concrete] // CHECK:STDOUT: %T.as.DefaultOrUnformed.impl.Op.b07: %T.as.DefaultOrUnformed.impl.Op.type.7ef = struct_value () [concrete] // CHECK:STDOUT: %DefaultOrUnformed.facet: %DefaultOrUnformed.type = facet_value %ptr, (%DefaultOrUnformed.impl_witness.20b) [concrete] // CHECK:STDOUT: %T.as.DefaultOrUnformed.impl.Op.specific_fn: = specific_function %T.as.DefaultOrUnformed.impl.Op.b07, @T.as.DefaultOrUnformed.impl.Op(%ptr) [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Main.C = import_ref Main//class1, C, unloaded // CHECK:STDOUT: %Main.c_alias: type = import_ref Main//class1, c_alias, loaded [concrete = constants.%C] // CHECK:STDOUT: %Main.a = import_ref Main//class1, a, unloaded // CHECK:STDOUT: %Core.ece: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: .DefaultOrUnformed = %Core.DefaultOrUnformed // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: %Main.import_ref.8f2: = import_ref Main//class1, loc4_10, loaded [concrete = constants.%complete_type.357] // CHECK:STDOUT: %Main.import_ref.743 = import_ref Main//class1, inst{{[0-9A-F]+}} [no loc], unloaded // CHECK:STDOUT: %Core.DefaultOrUnformed: type = import_ref Core//prelude/parts/default, DefaultOrUnformed, loaded [concrete = constants.%DefaultOrUnformed.type] // CHECK:STDOUT: %Main.import_ref.dee: @T.as.DefaultOrUnformed.impl.%T.as.DefaultOrUnformed.impl.Op.type (%T.as.DefaultOrUnformed.impl.Op.type.6c6) = import_ref Main//class1, inst{{[0-9A-F]+}} [indirect], loaded [symbolic = @T.as.DefaultOrUnformed.impl.%T.as.DefaultOrUnformed.impl.Op (constants.%T.as.DefaultOrUnformed.impl.Op.ae1)] // CHECK:STDOUT: %DefaultOrUnformed.impl_witness_table = impl_witness_table (%Main.import_ref.dee), @T.as.DefaultOrUnformed.impl [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .C = imports.%Main.C // CHECK:STDOUT: .c_alias = imports.%Main.c_alias // CHECK:STDOUT: .a = imports.%Main.a // CHECK:STDOUT: .Core = imports.%Core.ece // CHECK:STDOUT: .c_alias_alias = %c_alias_alias // CHECK:STDOUT: .b = %b // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %default.import = import // CHECK:STDOUT: %c_alias.ref.loc6: type = name_ref c_alias, imports.%Main.c_alias [concrete = constants.%C] // CHECK:STDOUT: %c_alias_alias: type = alias_binding c_alias_alias, imports.%Main.c_alias [concrete = constants.%C] // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %b.patt: %pattern_type.506 = ref_binding_pattern b [concrete] // CHECK:STDOUT: %b.var_patt: %pattern_type.506 = var_pattern %b.patt [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %b.var: ref %ptr = var %b.var_patt [concrete] // CHECK:STDOUT: %.loc8: type = splice_block %ptr [concrete = constants.%ptr] { // CHECK:STDOUT: %c_alias.ref.loc8: type = name_ref c_alias, imports.%Main.c_alias [concrete = constants.%C] // CHECK:STDOUT: %ptr: type = ptr_type %c_alias.ref.loc8 [concrete = constants.%ptr] // CHECK:STDOUT: } // CHECK:STDOUT: %b: ref %ptr = ref_binding b, %b.var [concrete = %b.var] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @C [from "class1.carbon"] { // CHECK:STDOUT: complete_type_witness = imports.%Main.import_ref.8f2 // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = imports.%Main.import_ref.743 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @__global_init() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %DefaultOrUnformed.facet: %DefaultOrUnformed.type = facet_value constants.%ptr, (constants.%DefaultOrUnformed.impl_witness.20b) [concrete = constants.%DefaultOrUnformed.facet] // CHECK:STDOUT: %.loc8_16.1: %DefaultOrUnformed.type = converted constants.%ptr, %DefaultOrUnformed.facet [concrete = constants.%DefaultOrUnformed.facet] // CHECK:STDOUT: %as_type: type = facet_access_type %.loc8_16.1 [concrete = constants.%ptr] // CHECK:STDOUT: %.loc8_16.2: type = converted %.loc8_16.1, %as_type [concrete = constants.%ptr] // CHECK:STDOUT: %T.as.DefaultOrUnformed.impl.Op.specific_fn: = specific_function constants.%T.as.DefaultOrUnformed.impl.Op.b07, @T.as.DefaultOrUnformed.impl.Op(constants.%ptr) [concrete = constants.%T.as.DefaultOrUnformed.impl.Op.specific_fn] // CHECK:STDOUT: %T.as.DefaultOrUnformed.impl.Op.call: init %ptr = call %T.as.DefaultOrUnformed.impl.Op.specific_fn() // CHECK:STDOUT: assign file.%b.var, %T.as.DefaultOrUnformed.impl.Op.call // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- class3.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %C: type = class_type @C [concrete] // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %complete_type.357: = complete_type_witness %empty_struct_type [concrete] // CHECK:STDOUT: %ptr: type = ptr_type %C [concrete] // CHECK:STDOUT: %pattern_type.506: type = pattern_type %ptr [concrete] // CHECK:STDOUT: %DefaultOrUnformed.type: type = facet_type <@DefaultOrUnformed> [concrete] // CHECK:STDOUT: %T: type = symbolic_binding T, 0 [symbolic] // CHECK:STDOUT: %T.as.DefaultOrUnformed.impl.Op.type.6c6: type = fn_type @T.as.DefaultOrUnformed.impl.Op, @T.as.DefaultOrUnformed.impl(%T) [symbolic] // CHECK:STDOUT: %T.as.DefaultOrUnformed.impl.Op.ae1: %T.as.DefaultOrUnformed.impl.Op.type.6c6 = struct_value () [symbolic] // CHECK:STDOUT: %DefaultOrUnformed.impl_witness.910: = impl_witness imports.%DefaultOrUnformed.impl_witness_table, @T.as.DefaultOrUnformed.impl(%ptr) [concrete] // CHECK:STDOUT: %T.as.DefaultOrUnformed.impl.Op.type.7ef: type = fn_type @T.as.DefaultOrUnformed.impl.Op, @T.as.DefaultOrUnformed.impl(%ptr) [concrete] // CHECK:STDOUT: %T.as.DefaultOrUnformed.impl.Op.b07: %T.as.DefaultOrUnformed.impl.Op.type.7ef = struct_value () [concrete] // CHECK:STDOUT: %DefaultOrUnformed.facet: %DefaultOrUnformed.type = facet_value %ptr, (%DefaultOrUnformed.impl_witness.910) [concrete] // CHECK:STDOUT: %T.as.DefaultOrUnformed.impl.Op.specific_fn: = specific_function %T.as.DefaultOrUnformed.impl.Op.b07, @T.as.DefaultOrUnformed.impl.Op(%ptr) [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Main.c_alias_alias: type = import_ref Main//class2, c_alias_alias, loaded [concrete = constants.%C] // CHECK:STDOUT: %Main.b = import_ref Main//class2, b, unloaded // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: .DefaultOrUnformed = %Core.DefaultOrUnformed // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: %Main.import_ref.8db: = import_ref Main//class2, inst{{[0-9A-F]+}} [indirect], loaded [concrete = constants.%complete_type.357] // CHECK:STDOUT: %Main.import_ref.a60 = import_ref Main//class2, inst{{[0-9A-F]+}} [indirect], unloaded // CHECK:STDOUT: %Core.DefaultOrUnformed: type = import_ref Core//prelude/parts/default, DefaultOrUnformed, loaded [concrete = constants.%DefaultOrUnformed.type] // CHECK:STDOUT: %Core.import_ref.01d: @T.as.DefaultOrUnformed.impl.%T.as.DefaultOrUnformed.impl.Op.type (%T.as.DefaultOrUnformed.impl.Op.type.6c6) = import_ref Core//prelude/parts/default, loc{{\d+_\d+}}, loaded [symbolic = @T.as.DefaultOrUnformed.impl.%T.as.DefaultOrUnformed.impl.Op (constants.%T.as.DefaultOrUnformed.impl.Op.ae1)] // CHECK:STDOUT: %DefaultOrUnformed.impl_witness_table = impl_witness_table (%Core.import_ref.01d), @T.as.DefaultOrUnformed.impl [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .c_alias_alias = imports.%Main.c_alias_alias // CHECK:STDOUT: .b = imports.%Main.b // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .c = %c // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %default.import = import // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %c.patt: %pattern_type.506 = ref_binding_pattern c [concrete] // CHECK:STDOUT: %c.var_patt: %pattern_type.506 = var_pattern %c.patt [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %c.var: ref %ptr = var %c.var_patt [concrete] // CHECK:STDOUT: %.loc6: type = splice_block %ptr [concrete = constants.%ptr] { // CHECK:STDOUT: %c_alias_alias.ref: type = name_ref c_alias_alias, imports.%Main.c_alias_alias [concrete = constants.%C] // CHECK:STDOUT: %ptr: type = ptr_type %c_alias_alias.ref [concrete = constants.%ptr] // CHECK:STDOUT: } // CHECK:STDOUT: %c: ref %ptr = ref_binding c, %c.var [concrete = %c.var] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @C [from "class2.carbon"] { // CHECK:STDOUT: complete_type_witness = imports.%Main.import_ref.8db // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = imports.%Main.import_ref.a60 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @__global_init() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %DefaultOrUnformed.facet: %DefaultOrUnformed.type = facet_value constants.%ptr, (constants.%DefaultOrUnformed.impl_witness.910) [concrete = constants.%DefaultOrUnformed.facet] // CHECK:STDOUT: %.loc6_22.1: %DefaultOrUnformed.type = converted constants.%ptr, %DefaultOrUnformed.facet [concrete = constants.%DefaultOrUnformed.facet] // CHECK:STDOUT: %as_type: type = facet_access_type %.loc6_22.1 [concrete = constants.%ptr] // CHECK:STDOUT: %.loc6_22.2: type = converted %.loc6_22.1, %as_type [concrete = constants.%ptr] // CHECK:STDOUT: %T.as.DefaultOrUnformed.impl.Op.specific_fn: = specific_function constants.%T.as.DefaultOrUnformed.impl.Op.b07, @T.as.DefaultOrUnformed.impl.Op(constants.%ptr) [concrete = constants.%T.as.DefaultOrUnformed.impl.Op.specific_fn] // CHECK:STDOUT: %T.as.DefaultOrUnformed.impl.Op.call: init %ptr = call %T.as.DefaultOrUnformed.impl.Op.specific_fn() // CHECK:STDOUT: assign file.%c.var, %T.as.DefaultOrUnformed.impl.Op.call // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- var1.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] // CHECK:STDOUT: %empty_tuple: %empty_tuple.type = tuple_value () [concrete] // CHECK:STDOUT: %pattern_type: type = pattern_type %empty_tuple.type [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .a = %a // CHECK:STDOUT: .a_alias = %a_alias // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %a.patt: %pattern_type = ref_binding_pattern a [concrete] // CHECK:STDOUT: %a.var_patt: %pattern_type = var_pattern %a.patt [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %a.var: ref %empty_tuple.type = var %a.var_patt [concrete] // CHECK:STDOUT: %.loc4_9.1: type = splice_block %.loc4_9.3 [concrete = constants.%empty_tuple.type] { // CHECK:STDOUT: %.loc4_9.2: %empty_tuple.type = tuple_literal () [concrete = constants.%empty_tuple] // CHECK:STDOUT: %.loc4_9.3: type = converted %.loc4_9.2, constants.%empty_tuple.type [concrete = constants.%empty_tuple.type] // CHECK:STDOUT: } // CHECK:STDOUT: %a: ref %empty_tuple.type = ref_binding a, %a.var [concrete = %a.var] // CHECK:STDOUT: %a.ref: ref %empty_tuple.type = name_ref a, %a [concrete = %a.var] // CHECK:STDOUT: %a_alias: ref %empty_tuple.type = alias_binding a_alias, %a [concrete = %a.var] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @__global_init() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %.loc4_14.1: %empty_tuple.type = tuple_literal () [concrete = constants.%empty_tuple] // CHECK:STDOUT: %.loc4_14.2: init %empty_tuple.type = tuple_init () [concrete = constants.%empty_tuple] // CHECK:STDOUT: %.loc4_1: init %empty_tuple.type = converted %.loc4_14.1, %.loc4_14.2 [concrete = constants.%empty_tuple] // CHECK:STDOUT: assign file.%a.var, %.loc4_1 // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- var2.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] // CHECK:STDOUT: %pattern_type: type = pattern_type %empty_tuple.type [concrete] // CHECK:STDOUT: %empty_tuple: %empty_tuple.type = tuple_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Main.a = import_ref Main//var1, a, unloaded // CHECK:STDOUT: %Main.a_alias: ref %empty_tuple.type = import_ref Main//var1, a_alias, loaded [concrete = %a.var] // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: %a.patt: %pattern_type = ref_binding_pattern a [concrete] // CHECK:STDOUT: %a.var_patt: %pattern_type = var_pattern %a.patt [concrete] // CHECK:STDOUT: %a.var: ref %empty_tuple.type = var %a.var_patt [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .a = imports.%Main.a // CHECK:STDOUT: .a_alias = imports.%Main.a_alias // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .a_alias_alias = %a_alias_alias // CHECK:STDOUT: .b = %b // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %default.import = import // CHECK:STDOUT: %a_alias.ref: ref %empty_tuple.type = name_ref a_alias, imports.%Main.a_alias [concrete = imports.%a.var] // CHECK:STDOUT: %a_alias_alias: ref %empty_tuple.type = alias_binding a_alias_alias, imports.%Main.a_alias [concrete = imports.%a.var] // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %b.patt: %pattern_type = ref_binding_pattern b [concrete] // CHECK:STDOUT: %b.var_patt: %pattern_type = var_pattern %b.patt [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %b.var: ref %empty_tuple.type = var %b.var_patt [concrete] // CHECK:STDOUT: %.loc8_9.1: type = splice_block %.loc8_9.3 [concrete = constants.%empty_tuple.type] { // CHECK:STDOUT: %.loc8_9.2: %empty_tuple.type = tuple_literal () [concrete = constants.%empty_tuple] // CHECK:STDOUT: %.loc8_9.3: type = converted %.loc8_9.2, constants.%empty_tuple.type [concrete = constants.%empty_tuple.type] // CHECK:STDOUT: } // CHECK:STDOUT: %b: ref %empty_tuple.type = ref_binding b, %b.var [concrete = %b.var] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @__global_init() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %a_alias.ref: ref %empty_tuple.type = name_ref a_alias, imports.%Main.a_alias [concrete = imports.%a.var] // CHECK:STDOUT: %.loc8_13: init %empty_tuple.type = tuple_init () [concrete = constants.%empty_tuple] // CHECK:STDOUT: %.loc8_1: init %empty_tuple.type = converted %a_alias.ref, %.loc8_13 [concrete = constants.%empty_tuple] // CHECK:STDOUT: assign file.%b.var, %.loc8_1 // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- var3.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] // CHECK:STDOUT: %empty_tuple: %empty_tuple.type = tuple_value () [concrete] // CHECK:STDOUT: %pattern_type: type = pattern_type %empty_tuple.type [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Main.a_alias_alias: ref %empty_tuple.type = import_ref Main//var2, a_alias_alias, loaded [concrete = %a.var] // CHECK:STDOUT: %Main.b = import_ref Main//var2, b, unloaded // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: %a.patt: %pattern_type = ref_binding_pattern a [concrete] // CHECK:STDOUT: %a.var_patt: %pattern_type = var_pattern %a.patt [concrete] // CHECK:STDOUT: %a.var: ref %empty_tuple.type = var %a.var_patt [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .a_alias_alias = imports.%Main.a_alias_alias // CHECK:STDOUT: .b = imports.%Main.b // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .c = %c // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %default.import = import // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %c.patt: %pattern_type = ref_binding_pattern c [concrete] // CHECK:STDOUT: %c.var_patt: %pattern_type = var_pattern %c.patt [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %c.var: ref %empty_tuple.type = var %c.var_patt [concrete] // CHECK:STDOUT: %.loc6_9.1: type = splice_block %.loc6_9.3 [concrete = constants.%empty_tuple.type] { // CHECK:STDOUT: %.loc6_9.2: %empty_tuple.type = tuple_literal () [concrete = constants.%empty_tuple] // CHECK:STDOUT: %.loc6_9.3: type = converted %.loc6_9.2, constants.%empty_tuple.type [concrete = constants.%empty_tuple.type] // CHECK:STDOUT: } // CHECK:STDOUT: %c: ref %empty_tuple.type = ref_binding c, %c.var [concrete = %c.var] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @__global_init() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %a_alias_alias.ref: ref %empty_tuple.type = name_ref a_alias_alias, imports.%Main.a_alias_alias [concrete = imports.%a.var] // CHECK:STDOUT: %.loc6_13: init %empty_tuple.type = tuple_init () [concrete = constants.%empty_tuple] // CHECK:STDOUT: %.loc6_1: init %empty_tuple.type = converted %a_alias_alias.ref, %.loc6_13 [concrete = constants.%empty_tuple] // CHECK:STDOUT: assign file.%c.var, %.loc6_1 // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/alias/import_access.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/none.carbon // EXTRA-ARGS: --dump-sem-ir-ranges=if-present // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/alias/import_access.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/alias/import_access.carbon // ============================================================================ // Setup files // ============================================================================ // --- def.carbon package Test library "[[@TEST_NAME]]"; class C {} private alias A = C; // ============================================================================ // Test files // ============================================================================ // --- def.impl.carbon impl package Test library "[[@TEST_NAME]]"; var inst: A = {}; // --- fail_local_def.carbon package Test library "[[@TEST_NAME]]"; import library "def"; // CHECK:STDERR: fail_local_def.carbon:[[@LINE+4]]:11: error: name `A` not found [NameNotFound] // CHECK:STDERR: var inst: A = {}; // CHECK:STDERR: ^ // CHECK:STDERR: var inst: A = {}; // --- fail_other_def.carbon package Other library "[[@TEST_NAME]]"; import Test library "def"; // CHECK:STDERR: fail_other_def.carbon:[[@LINE+4]]:11: error: member name `A` not found in `Test` [MemberNameNotFoundInInstScope] // CHECK:STDERR: var inst: Test.A = {}; // CHECK:STDERR: ^~~~~~ // CHECK:STDERR: var inst: Test.A = {}; // CHECK:STDOUT: --- def.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %C: type = class_type @C [concrete] // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %complete_type: = complete_type_witness %empty_struct_type [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .C = %C.decl // CHECK:STDOUT: .A [private] = %A // CHECK:STDOUT: } // CHECK:STDOUT: %C.decl: type = class_decl @C [concrete = constants.%C] {} {} // CHECK:STDOUT: %C.ref: type = name_ref C, %C.decl [concrete = constants.%C] // CHECK:STDOUT: %A: type = alias_binding A, %C.decl [concrete = constants.%C] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @C { // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%C // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- def.impl.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %C: type = class_type @C [concrete] // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %complete_type: = complete_type_witness %empty_struct_type [concrete] // CHECK:STDOUT: %pattern_type: type = pattern_type %C [concrete] // CHECK:STDOUT: %empty_struct: %empty_struct_type = struct_value () [concrete] // CHECK:STDOUT: %C.val: %C = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Test.C = import_ref Test//def, C, unloaded // CHECK:STDOUT: %Test.A: type = import_ref Test//def, A, loaded [concrete = constants.%C] // CHECK:STDOUT: %Test.import_ref.8f2: = import_ref Test//def, loc4_10, loaded [concrete = constants.%complete_type] // CHECK:STDOUT: %Test.import_ref.c8c = import_ref Test//def, inst{{[0-9A-F]+}} [no loc], unloaded // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .C = imports.%Test.C // CHECK:STDOUT: .A [private] = imports.%Test.A // CHECK:STDOUT: .inst = %inst // CHECK:STDOUT: } // CHECK:STDOUT: %Test.import = import Test // CHECK:STDOUT: %default.import = import // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %inst.patt: %pattern_type = ref_binding_pattern inst [concrete] // CHECK:STDOUT: %inst.var_patt: %pattern_type = var_pattern %inst.patt [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %inst.var: ref %C = var %inst.var_patt [concrete] // CHECK:STDOUT: %A.ref: type = name_ref A, imports.%Test.A [concrete = constants.%C] // CHECK:STDOUT: %inst: ref %C = ref_binding inst, %inst.var [concrete = %inst.var] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @C [from "def.carbon"] { // CHECK:STDOUT: complete_type_witness = imports.%Test.import_ref.8f2 // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = imports.%Test.import_ref.c8c // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @__global_init() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %.loc4_16.1: %empty_struct_type = struct_literal () [concrete = constants.%empty_struct] // CHECK:STDOUT: %.loc4_16.2: init %C to file.%inst.var = class_init () [concrete = constants.%C.val] // CHECK:STDOUT: %.loc4_1: init %C = converted %.loc4_16.1, %.loc4_16.2 [concrete = constants.%C.val] // CHECK:STDOUT: assign file.%inst.var, %.loc4_1 // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- fail_local_def.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %empty_struct: %empty_struct_type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Test.C = import_ref Test//def, C, unloaded // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .C = imports.%Test.C // CHECK:STDOUT: .A = // CHECK:STDOUT: .inst = %inst // CHECK:STDOUT: } // CHECK:STDOUT: %default.import = import // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %inst.patt: = ref_binding_pattern inst [concrete] // CHECK:STDOUT: %inst.var_patt: = var_pattern %inst.patt [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %inst.var: ref = var %inst.var_patt [concrete = ] // CHECK:STDOUT: %A.ref: = name_ref A, [concrete = ] // CHECK:STDOUT: %inst: ref = ref_binding inst, [concrete = ] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @__global_init() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %.loc10: %empty_struct_type = struct_literal () [concrete = constants.%empty_struct] // CHECK:STDOUT: assign file.%inst.var, // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- fail_other_def.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %empty_struct: %empty_struct_type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Test: = namespace file.%Test.import, [concrete] { // CHECK:STDOUT: .A = // CHECK:STDOUT: import Test//def // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Test = imports.%Test // CHECK:STDOUT: .inst = %inst // CHECK:STDOUT: } // CHECK:STDOUT: %Test.import = import Test // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %inst.patt: = ref_binding_pattern inst [concrete] // CHECK:STDOUT: %inst.var_patt: = var_pattern %inst.patt [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %inst.var: ref = var %inst.var_patt [concrete = ] // CHECK:STDOUT: %.1: = splice_block [concrete = ] { // CHECK:STDOUT: %Test.ref: = name_ref Test, imports.%Test [concrete = imports.%Test] // CHECK:STDOUT: %A.ref: = name_ref A, [concrete = ] // CHECK:STDOUT: } // CHECK:STDOUT: %inst: ref = ref_binding inst, [concrete = ] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @__global_init() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %.loc10: %empty_struct_type = struct_literal () [concrete = constants.%empty_struct] // CHECK:STDOUT: assign file.%inst.var, // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/alias/import_order.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/none.carbon // EXTRA-ARGS: --dump-sem-ir-ranges=if-present // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/alias/import_order.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/alias/import_order.carbon // --- a.carbon library "[[@TEST_NAME]]"; class C { var v: (); } alias a = C; alias b = a; alias c = b; alias d = c; // --- b.carbon library "[[@TEST_NAME]]"; import library "a"; // Access imports in reverse order of export. var d_val: d = {.v = ()}; var c_val: c = {.v = d_val.v}; var b_val: b = {.v = c_val.v}; var a_val: a = {.v = b_val.v}; // CHECK:STDOUT: --- a.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %C: type = class_type @C [concrete] // CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] // CHECK:STDOUT: %empty_tuple: %empty_tuple.type = tuple_value () [concrete] // CHECK:STDOUT: %C.elem: type = unbound_element_type %C, %empty_tuple.type [concrete] // CHECK:STDOUT: %struct_type.v: type = struct_type {.v: %empty_tuple.type} [concrete] // CHECK:STDOUT: %complete_type: = complete_type_witness %struct_type.v [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .C = %C.decl // CHECK:STDOUT: .a = %a // CHECK:STDOUT: .b = %b // CHECK:STDOUT: .c = %c // CHECK:STDOUT: .d = %d // CHECK:STDOUT: } // CHECK:STDOUT: %C.decl: type = class_decl @C [concrete = constants.%C] {} {} // CHECK:STDOUT: %C.ref: type = name_ref C, %C.decl [concrete = constants.%C] // CHECK:STDOUT: %a: type = alias_binding a, %C.decl [concrete = constants.%C] // CHECK:STDOUT: %a.ref: type = name_ref a, %a [concrete = constants.%C] // CHECK:STDOUT: %b: type = alias_binding b, %a [concrete = constants.%C] // CHECK:STDOUT: %b.ref: type = name_ref b, %b [concrete = constants.%C] // CHECK:STDOUT: %c: type = alias_binding c, %b [concrete = constants.%C] // CHECK:STDOUT: %c.ref: type = name_ref c, %c [concrete = constants.%C] // CHECK:STDOUT: %d: type = alias_binding d, %c [concrete = constants.%C] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @C { // CHECK:STDOUT: %.loc4_19.1: %empty_tuple.type = tuple_literal () [concrete = constants.%empty_tuple] // CHECK:STDOUT: %.loc4_19.2: type = converted %.loc4_19.1, constants.%empty_tuple.type [concrete = constants.%empty_tuple.type] // CHECK:STDOUT: %.loc4_16: %C.elem = field_decl v, element0 [concrete] // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%struct_type.v [concrete = constants.%complete_type] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%C // CHECK:STDOUT: .v = %.loc4_16 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- b.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %C: type = class_type @C [concrete] // CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] // CHECK:STDOUT: %struct_type.v: type = struct_type {.v: %empty_tuple.type} [concrete] // CHECK:STDOUT: %complete_type: = complete_type_witness %struct_type.v [concrete] // CHECK:STDOUT: %pattern_type: type = pattern_type %C [concrete] // CHECK:STDOUT: %empty_tuple: %empty_tuple.type = tuple_value () [concrete] // CHECK:STDOUT: %struct.5f2: %struct_type.v = struct_value (%empty_tuple) [concrete] // CHECK:STDOUT: %.cad: ref %empty_tuple.type = class_element_access file.%d_val.var, element0 [concrete] // CHECK:STDOUT: %C.val: %C = struct_value (%empty_tuple) [concrete] // CHECK:STDOUT: %C.elem: type = unbound_element_type %C, %empty_tuple.type [concrete] // CHECK:STDOUT: %struct.d07: %struct_type.v = struct_value (%.cad) [concrete] // CHECK:STDOUT: %.da4: ref %empty_tuple.type = class_element_access file.%c_val.var, element0 [concrete] // CHECK:STDOUT: %struct.f0b: %struct_type.v = struct_value (%.da4) [concrete] // CHECK:STDOUT: %.0f2: ref %empty_tuple.type = class_element_access file.%b_val.var, element0 [concrete] // CHECK:STDOUT: %struct.938: %struct_type.v = struct_value (%.0f2) [concrete] // CHECK:STDOUT: %.851: ref %empty_tuple.type = class_element_access file.%a_val.var, element0 [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Main.C = import_ref Main//a, C, unloaded // CHECK:STDOUT: %Main.a: type = import_ref Main//a, a, loaded [concrete = constants.%C] // CHECK:STDOUT: %Main.b: type = import_ref Main//a, b, loaded [concrete = constants.%C] // CHECK:STDOUT: %Main.c: type = import_ref Main//a, c, loaded [concrete = constants.%C] // CHECK:STDOUT: %Main.d: type = import_ref Main//a, d, loaded [concrete = constants.%C] // CHECK:STDOUT: %Main.import_ref.146: = import_ref Main//a, loc4_22, loaded [concrete = constants.%complete_type] // CHECK:STDOUT: %Main.import_ref.743 = import_ref Main//a, inst{{[0-9A-F]+}} [no loc], unloaded // CHECK:STDOUT: %Main.import_ref.d43: %C.elem = import_ref Main//a, loc4_16, loaded [concrete = %.c09] // CHECK:STDOUT: %.c09: %C.elem = field_decl v, element0 [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .C = imports.%Main.C // CHECK:STDOUT: .a = imports.%Main.a // CHECK:STDOUT: .b = imports.%Main.b // CHECK:STDOUT: .c = imports.%Main.c // CHECK:STDOUT: .d = imports.%Main.d // CHECK:STDOUT: .d_val = %d_val // CHECK:STDOUT: .c_val = %c_val // CHECK:STDOUT: .b_val = %b_val // CHECK:STDOUT: .a_val = %a_val // CHECK:STDOUT: } // CHECK:STDOUT: %default.import = import // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %d_val.patt: %pattern_type = ref_binding_pattern d_val [concrete] // CHECK:STDOUT: %d_val.var_patt: %pattern_type = var_pattern %d_val.patt [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %d_val.var: ref %C = var %d_val.var_patt [concrete] // CHECK:STDOUT: %d.ref: type = name_ref d, imports.%Main.d [concrete = constants.%C] // CHECK:STDOUT: %d_val: ref %C = ref_binding d_val, %d_val.var [concrete = %d_val.var] // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %c_val.patt: %pattern_type = ref_binding_pattern c_val [concrete] // CHECK:STDOUT: %c_val.var_patt: %pattern_type = var_pattern %c_val.patt [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %c_val.var: ref %C = var %c_val.var_patt [concrete] // CHECK:STDOUT: %c.ref: type = name_ref c, imports.%Main.c [concrete = constants.%C] // CHECK:STDOUT: %c_val: ref %C = ref_binding c_val, %c_val.var [concrete = %c_val.var] // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %b_val.patt: %pattern_type = ref_binding_pattern b_val [concrete] // CHECK:STDOUT: %b_val.var_patt: %pattern_type = var_pattern %b_val.patt [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %b_val.var: ref %C = var %b_val.var_patt [concrete] // CHECK:STDOUT: %b.ref: type = name_ref b, imports.%Main.b [concrete = constants.%C] // CHECK:STDOUT: %b_val: ref %C = ref_binding b_val, %b_val.var [concrete = %b_val.var] // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %a_val.patt: %pattern_type = ref_binding_pattern a_val [concrete] // CHECK:STDOUT: %a_val.var_patt: %pattern_type = var_pattern %a_val.patt [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %a_val.var: ref %C = var %a_val.var_patt [concrete] // CHECK:STDOUT: %a.ref: type = name_ref a, imports.%Main.a [concrete = constants.%C] // CHECK:STDOUT: %a_val: ref %C = ref_binding a_val, %a_val.var [concrete = %a_val.var] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @C [from "a.carbon"] { // CHECK:STDOUT: complete_type_witness = imports.%Main.import_ref.146 // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = imports.%Main.import_ref.743 // CHECK:STDOUT: .v = imports.%Main.import_ref.d43 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @__global_init() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %.loc7_23.1: %empty_tuple.type = tuple_literal () [concrete = constants.%empty_tuple] // CHECK:STDOUT: %.loc7_24.1: %struct_type.v = struct_literal (%.loc7_23.1) [concrete = constants.%struct.5f2] // CHECK:STDOUT: %.loc7_24.2: ref %empty_tuple.type = class_element_access file.%d_val.var, element0 [concrete = constants.%.cad] // CHECK:STDOUT: %.loc7_23.2: init %empty_tuple.type = tuple_init () [concrete = constants.%empty_tuple] // CHECK:STDOUT: %.loc7_24.3: init %empty_tuple.type = converted %.loc7_23.1, %.loc7_23.2 [concrete = constants.%empty_tuple] // CHECK:STDOUT: %.loc7_24.4: init %C to file.%d_val.var = class_init (%.loc7_24.3) [concrete = constants.%C.val] // CHECK:STDOUT: %.loc7_1: init %C = converted %.loc7_24.1, %.loc7_24.4 [concrete = constants.%C.val] // CHECK:STDOUT: assign file.%d_val.var, %.loc7_1 // CHECK:STDOUT: %d_val.ref: ref %C = name_ref d_val, file.%d_val [concrete = file.%d_val.var] // CHECK:STDOUT: %v.ref.loc8: %C.elem = name_ref v, imports.%Main.import_ref.d43 [concrete = imports.%.c09] // CHECK:STDOUT: %.loc8_27.1: ref %empty_tuple.type = class_element_access %d_val.ref, element0 [concrete = constants.%.cad] // CHECK:STDOUT: %.loc8_29.1: %struct_type.v = struct_literal (%.loc8_27.1) [concrete = constants.%struct.d07] // CHECK:STDOUT: %.loc8_29.2: ref %empty_tuple.type = class_element_access file.%c_val.var, element0 [concrete = constants.%.da4] // CHECK:STDOUT: %.loc8_27.2: init %empty_tuple.type = tuple_init () [concrete = constants.%empty_tuple] // CHECK:STDOUT: %.loc8_29.3: init %empty_tuple.type = converted %.loc8_27.1, %.loc8_27.2 [concrete = constants.%empty_tuple] // CHECK:STDOUT: %.loc8_29.4: init %C to file.%c_val.var = class_init (%.loc8_29.3) [concrete = constants.%C.val] // CHECK:STDOUT: %.loc8_1: init %C = converted %.loc8_29.1, %.loc8_29.4 [concrete = constants.%C.val] // CHECK:STDOUT: assign file.%c_val.var, %.loc8_1 // CHECK:STDOUT: %c_val.ref: ref %C = name_ref c_val, file.%c_val [concrete = file.%c_val.var] // CHECK:STDOUT: %v.ref.loc9: %C.elem = name_ref v, imports.%Main.import_ref.d43 [concrete = imports.%.c09] // CHECK:STDOUT: %.loc9_27.1: ref %empty_tuple.type = class_element_access %c_val.ref, element0 [concrete = constants.%.da4] // CHECK:STDOUT: %.loc9_29.1: %struct_type.v = struct_literal (%.loc9_27.1) [concrete = constants.%struct.f0b] // CHECK:STDOUT: %.loc9_29.2: ref %empty_tuple.type = class_element_access file.%b_val.var, element0 [concrete = constants.%.0f2] // CHECK:STDOUT: %.loc9_27.2: init %empty_tuple.type = tuple_init () [concrete = constants.%empty_tuple] // CHECK:STDOUT: %.loc9_29.3: init %empty_tuple.type = converted %.loc9_27.1, %.loc9_27.2 [concrete = constants.%empty_tuple] // CHECK:STDOUT: %.loc9_29.4: init %C to file.%b_val.var = class_init (%.loc9_29.3) [concrete = constants.%C.val] // CHECK:STDOUT: %.loc9_1: init %C = converted %.loc9_29.1, %.loc9_29.4 [concrete = constants.%C.val] // CHECK:STDOUT: assign file.%b_val.var, %.loc9_1 // CHECK:STDOUT: %b_val.ref: ref %C = name_ref b_val, file.%b_val [concrete = file.%b_val.var] // CHECK:STDOUT: %v.ref.loc10: %C.elem = name_ref v, imports.%Main.import_ref.d43 [concrete = imports.%.c09] // CHECK:STDOUT: %.loc10_27.1: ref %empty_tuple.type = class_element_access %b_val.ref, element0 [concrete = constants.%.0f2] // CHECK:STDOUT: %.loc10_29.1: %struct_type.v = struct_literal (%.loc10_27.1) [concrete = constants.%struct.938] // CHECK:STDOUT: %.loc10_29.2: ref %empty_tuple.type = class_element_access file.%a_val.var, element0 [concrete = constants.%.851] // CHECK:STDOUT: %.loc10_27.2: init %empty_tuple.type = tuple_init () [concrete = constants.%empty_tuple] // CHECK:STDOUT: %.loc10_29.3: init %empty_tuple.type = converted %.loc10_27.1, %.loc10_27.2 [concrete = constants.%empty_tuple] // CHECK:STDOUT: %.loc10_29.4: init %C to file.%a_val.var = class_init (%.loc10_29.3) [concrete = constants.%C.val] // CHECK:STDOUT: %.loc10_1: init %C = converted %.loc10_29.1, %.loc10_29.4 [concrete = constants.%C.val] // CHECK:STDOUT: assign file.%a_val.var, %.loc10_1 // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/alias/in_namespace.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/none.carbon // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/alias/in_namespace.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/alias/in_namespace.carbon // --- in_namespace.carbon library "[[@TEST_NAME]]"; class C { var v: (); } //@dump-sem-ir-begin namespace NS; alias NS.a = C; //@dump-sem-ir-end let b: NS.a = {.v = ()}; fn F() -> NS.a { return {.v = ()}; } // --- fail_local_in_namespace.carbon library "[[@TEST_NAME]]"; namespace NS; fn F() -> {} { // CHECK:STDERR: fail_local_in_namespace.carbon:[[@LINE+8]]:9: error: name `NS` not found [NameNotFound] // CHECK:STDERR: alias NS.a = {}; // CHECK:STDERR: ^~ // CHECK:STDERR: // CHECK:STDERR: fail_local_in_namespace.carbon:[[@LINE+4]]:16: error: alias initializer must be a name reference [AliasRequiresNameRef] // CHECK:STDERR: alias NS.a = {}; // CHECK:STDERR: ^~ // CHECK:STDERR: alias NS.a = {}; // CHECK:STDERR: fail_local_in_namespace.carbon:[[@LINE+4]]:10: error: member name `a` not found in `NS` [MemberNameNotFoundInInstScope] // CHECK:STDERR: return NS.a; // CHECK:STDERR: ^~~~ // CHECK:STDERR: return NS.a; } // CHECK:STDOUT: --- in_namespace.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %C: type = class_type @C [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: %NS: = namespace [concrete] { // CHECK:STDOUT: .C = // CHECK:STDOUT: .a = %a // CHECK:STDOUT: } // CHECK:STDOUT: %C.ref: type = name_ref C, %C.decl [concrete = constants.%C] // CHECK:STDOUT: %a: type = alias_binding a, %C.decl [concrete = constants.%C] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @__global_init() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: // CHECK:STDOUT: } // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/alias/local.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/destroy.carbon // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/alias/local.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/alias/local.carbon // --- local.carbon library "[[@TEST_NAME]]"; fn F() -> () { var a: () = (); //@dump-sem-ir-begin alias b = a; //@dump-sem-ir-end return b; } // CHECK:STDOUT: --- local.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @F() -> out %return.param: %empty_tuple.type { // CHECK:STDOUT: !entry: // CHECK:STDOUT: // CHECK:STDOUT: %a.ref: ref %empty_tuple.type = name_ref a, %a // CHECK:STDOUT: %b: ref %empty_tuple.type = alias_binding b, %a // CHECK:STDOUT: // CHECK:STDOUT: } // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/alias/preserve_in_type_printing.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/none.carbon // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/alias/preserve_in_type_printing.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/alias/preserve_in_type_printing.carbon // --- fail_alias_nested_in_type.carbon library "[[@TEST_NAME]]"; class C {} alias A = C; // The diagnostics below should refer to `A`, not `C`. // CHECK:STDERR: fail_alias_nested_in_type.carbon:[[@LINE+7]]:15: error: missing return value [ReturnStatementMissingExpr] // CHECK:STDERR: fn F() -> A { return; } // CHECK:STDERR: ^~~~~~~ // CHECK:STDERR: fail_alias_nested_in_type.carbon:[[@LINE+4]]:11: note: return type of function is `A` [ReturnTypeHereNote] // CHECK:STDERR: fn F() -> A { return; } // CHECK:STDERR: ^ // CHECK:STDERR: fn F() -> A { return; } // CHECK:STDERR: fail_alias_nested_in_type.carbon:[[@LINE+7]]:16: error: missing return value [ReturnStatementMissingExpr] // CHECK:STDERR: fn G() -> A* { return; } // CHECK:STDERR: ^~~~~~~ // CHECK:STDERR: fail_alias_nested_in_type.carbon:[[@LINE+4]]:11: note: return type of function is `A*` [ReturnTypeHereNote] // CHECK:STDERR: fn G() -> A* { return; } // CHECK:STDERR: ^~ // CHECK:STDERR: fn G() -> A* { return; } // CHECK:STDERR: fail_alias_nested_in_type.carbon:[[@LINE+7]]:22: error: missing return value [ReturnStatementMissingExpr] // CHECK:STDERR: fn H() -> const A* { return; } // CHECK:STDERR: ^~~~~~~ // CHECK:STDERR: fail_alias_nested_in_type.carbon:[[@LINE+4]]:11: note: return type of function is `const A*` [ReturnTypeHereNote] // CHECK:STDERR: fn H() -> const A* { return; } // CHECK:STDERR: ^~~~~~~~ // CHECK:STDERR: fn H() -> const A* { return; } // CHECK:STDERR: fail_alias_nested_in_type.carbon:[[@LINE+7]]:26: error: missing return value [ReturnStatementMissingExpr] // CHECK:STDERR: fn I() -> array(A, 42) { return; } // CHECK:STDERR: ^~~~~~~ // CHECK:STDERR: fail_alias_nested_in_type.carbon:[[@LINE+4]]:11: note: return type of function is `array(A, 42)` [ReturnTypeHereNote] // CHECK:STDERR: fn I() -> array(A, 42) { return; } // CHECK:STDERR: ^~~~~~~~~~~~ // CHECK:STDERR: fn I() -> array(A, 42) { return; } ================================================ FILE: toolchain/check/testdata/array/basics.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/convert.carbon // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/array/basics.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/array/basics.carbon // --- assign_var.carbon library "[[@TEST_NAME]]"; var a: ((), (), ()); //@dump-sem-ir-begin var b: array((), 3) = a; //@dump-sem-ir-end // --- assign_arity.carbon var a0: array((), 0) = (); var a1: array((), 1) = ((),); var a2: array((), 2) = ((), ()); // --- array_in_place.carbon library "[[@TEST_NAME]]"; class C {} fn F() -> (C, C, C); fn G() { //@dump-sem-ir-begin var unused v: array((C, C, C), 2) = (F(), F()); //@dump-sem-ir-end } // --- array_vs_tuple.carbon library "[[@TEST_NAME]]"; fn G() { // These should have two different constant values. //@dump-sem-ir-begin var unused a: array((), 3); var unused b: ((), (), ()); //@dump-sem-ir-end } // --- assign_return_value.carbon library "[[@TEST_NAME]]"; fn F() -> ((),) { return ((),); } fn Run() { //@dump-sem-ir-begin var unused t: array((), 1) = F(); //@dump-sem-ir-end } // --- nine_elements.carbon library "[[@TEST_NAME]]"; var a: ((), (), (), (), (), (), (), (), ()) = ((), (), (), (), (), (), (), (), ()); // Regression test for APInt handling. //@dump-sem-ir-begin var b: array((), 9) = a; //@dump-sem-ir-end // --- fail_incomplete_element.carbon library "[[@TEST_NAME]]"; class Incomplete; // CHECK:STDERR: fail_incomplete_element.carbon:[[@LINE+7]]:8: error: binding pattern has incomplete type `array(Incomplete, 1)` in name binding declaration [IncompleteTypeInBindingDecl] // CHECK:STDERR: var a: array(Incomplete, 1); // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: fail_incomplete_element.carbon:[[@LINE-5]]:1: note: class was forward declared here [ClassForwardDeclaredHere] // CHECK:STDERR: class Incomplete; // CHECK:STDERR: ^~~~~~~~~~~~~~~~~ // CHECK:STDERR: var a: array(Incomplete, 1); var p: Incomplete* = &a[0]; // --- fail_invalid_element.carbon library "[[@TEST_NAME]]"; // CHECK:STDERR: fail_invalid_element.carbon:[[@LINE+7]]:14: error: cannot implicitly convert non-type value of type `Core.IntLiteral` to `type` [ConversionFailureNonTypeToFacet] // CHECK:STDERR: var a: array(1, 1); // CHECK:STDERR: ^ // CHECK:STDERR: fail_invalid_element.carbon:[[@LINE+4]]:14: note: type `Core.IntLiteral` does not implement interface `Core.ImplicitAs(type)` [MissingImplInMemberAccessInContext] // CHECK:STDERR: var a: array(1, 1); // CHECK:STDERR: ^ // CHECK:STDERR: var a: array(1, 1); // CHECK:STDOUT: --- assign_var.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] // CHECK:STDOUT: %empty_tuple: %empty_tuple.type = tuple_value () [concrete] // CHECK:STDOUT: %tuple.type: type = tuple_type (%empty_tuple.type, %empty_tuple.type, %empty_tuple.type) [concrete] // CHECK:STDOUT: %int_3: Core.IntLiteral = int_value 3 [concrete] // CHECK:STDOUT: %array_type: type = array_type %int_3, %empty_tuple.type [concrete] // CHECK:STDOUT: %pattern_type.035: type = pattern_type %array_type [concrete] // CHECK:STDOUT: %tuple.elem0: ref %empty_tuple.type = tuple_access file.%a.var, element0 [concrete] // CHECK:STDOUT: %int_0: Core.IntLiteral = int_value 0 [concrete] // CHECK:STDOUT: %tuple.elem1: ref %empty_tuple.type = tuple_access file.%a.var, element1 [concrete] // CHECK:STDOUT: %int_1: Core.IntLiteral = int_value 1 [concrete] // CHECK:STDOUT: %tuple.elem2: ref %empty_tuple.type = tuple_access file.%a.var, element2 [concrete] // CHECK:STDOUT: %int_2: Core.IntLiteral = int_value 2 [concrete] // CHECK:STDOUT: %array: %array_type = tuple_value (%empty_tuple, %empty_tuple, %empty_tuple) [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %b.patt: %pattern_type.035 = ref_binding_pattern b [concrete] // CHECK:STDOUT: %b.var_patt: %pattern_type.035 = var_pattern %b.patt [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %b.var: ref %array_type = var %b.var_patt [concrete] // CHECK:STDOUT: %.loc6_19: type = splice_block %array_type [concrete = constants.%array_type] { // CHECK:STDOUT: %.loc6_15.1: %empty_tuple.type = tuple_literal () [concrete = constants.%empty_tuple] // CHECK:STDOUT: %int_3: Core.IntLiteral = int_value 3 [concrete = constants.%int_3] // CHECK:STDOUT: %.loc6_15.2: type = converted %.loc6_15.1, constants.%empty_tuple.type [concrete = constants.%empty_tuple.type] // CHECK:STDOUT: %array_type: type = array_type %int_3, %.loc6_15.2 [concrete = constants.%array_type] // CHECK:STDOUT: } // CHECK:STDOUT: %b: ref %array_type = ref_binding b, %b.var [concrete = %b.var] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @__global_init() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: // CHECK:STDOUT: %a.ref: ref %tuple.type = name_ref a, file.%a [concrete = file.%a.var] // CHECK:STDOUT: %tuple.elem0: ref %empty_tuple.type = tuple_access %a.ref, element0 [concrete = constants.%tuple.elem0] // CHECK:STDOUT: %int_0: Core.IntLiteral = int_value 0 [concrete = constants.%int_0] // CHECK:STDOUT: %.loc6_23.1: ref %empty_tuple.type = array_index file.%b.var, %int_0 // CHECK:STDOUT: %.loc6_23.2: init %empty_tuple.type = tuple_init () [concrete = constants.%empty_tuple] // CHECK:STDOUT: %.loc6_23.3: init %empty_tuple.type = converted %tuple.elem0, %.loc6_23.2 [concrete = constants.%empty_tuple] // CHECK:STDOUT: %tuple.elem1: ref %empty_tuple.type = tuple_access %a.ref, element1 [concrete = constants.%tuple.elem1] // CHECK:STDOUT: %int_1: Core.IntLiteral = int_value 1 [concrete = constants.%int_1] // CHECK:STDOUT: %.loc6_23.4: ref %empty_tuple.type = array_index file.%b.var, %int_1 // CHECK:STDOUT: %.loc6_23.5: init %empty_tuple.type = tuple_init () [concrete = constants.%empty_tuple] // CHECK:STDOUT: %.loc6_23.6: init %empty_tuple.type = converted %tuple.elem1, %.loc6_23.5 [concrete = constants.%empty_tuple] // CHECK:STDOUT: %tuple.elem2: ref %empty_tuple.type = tuple_access %a.ref, element2 [concrete = constants.%tuple.elem2] // CHECK:STDOUT: %int_2: Core.IntLiteral = int_value 2 [concrete = constants.%int_2] // CHECK:STDOUT: %.loc6_23.7: ref %empty_tuple.type = array_index file.%b.var, %int_2 // CHECK:STDOUT: %.loc6_23.8: init %empty_tuple.type = tuple_init () [concrete = constants.%empty_tuple] // CHECK:STDOUT: %.loc6_23.9: init %empty_tuple.type = converted %tuple.elem2, %.loc6_23.8 [concrete = constants.%empty_tuple] // CHECK:STDOUT: %.loc6_23.10: init %array_type to file.%b.var = array_init (%.loc6_23.3, %.loc6_23.6, %.loc6_23.9) [concrete = constants.%array] // CHECK:STDOUT: %.loc6_1: init %array_type = converted %a.ref, %.loc6_23.10 [concrete = constants.%array] // CHECK:STDOUT: assign file.%b.var, %.loc6_1 // CHECK:STDOUT: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- array_in_place.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %C: type = class_type @C [concrete] // CHECK:STDOUT: %tuple.type.ff9: type = tuple_type (type, type, type) [concrete] // CHECK:STDOUT: %tuple: %tuple.type.ff9 = tuple_value (%C, %C, %C) [concrete] // CHECK:STDOUT: %tuple.type.e56: type = tuple_type (%C, %C, %C) [concrete] // CHECK:STDOUT: %F.type: type = fn_type @F [concrete] // CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] // CHECK:STDOUT: %F: %F.type = struct_value () [concrete] // CHECK:STDOUT: %int_2: Core.IntLiteral = int_value 2 [concrete] // CHECK:STDOUT: %array_type: type = array_type %int_2, %tuple.type.e56 [concrete] // CHECK:STDOUT: %pattern_type.709: type = pattern_type %array_type [concrete] // CHECK:STDOUT: %tuple.type.708: type = tuple_type (%tuple.type.e56, %tuple.type.e56) [concrete] // CHECK:STDOUT: %int_0: Core.IntLiteral = int_value 0 [concrete] // CHECK:STDOUT: %int_1: Core.IntLiteral = int_value 1 [concrete] // CHECK:STDOUT: %Destroy.Op.type: type = fn_type @Destroy.Op [concrete] // CHECK:STDOUT: %Destroy.Op: %Destroy.Op.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @G() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %v.patt: %pattern_type.709 = ref_binding_pattern v [concrete] // CHECK:STDOUT: %v.var_patt: %pattern_type.709 = var_pattern %v.patt [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %v.var: ref %array_type = var %v.var_patt // CHECK:STDOUT: %F.ref.loc10_40: %F.type = name_ref F, file.%F.decl [concrete = constants.%F] // CHECK:STDOUT: %.loc10_48.1: ref %tuple.type.e56 = splice_block %.loc10_48.6 { // CHECK:STDOUT: %int_0: Core.IntLiteral = int_value 0 [concrete = constants.%int_0] // CHECK:STDOUT: %.loc10_48.6: ref %tuple.type.e56 = array_index %v.var, %int_0 // CHECK:STDOUT: } // CHECK:STDOUT: %F.call.loc10_42: init %tuple.type.e56 to %.loc10_48.1 = call %F.ref.loc10_40() // CHECK:STDOUT: %F.ref.loc10_45: %F.type = name_ref F, file.%F.decl [concrete = constants.%F] // CHECK:STDOUT: %.loc10_48.2: ref %tuple.type.e56 = splice_block %.loc10_48.5 { // CHECK:STDOUT: %int_1: Core.IntLiteral = int_value 1 [concrete = constants.%int_1] // CHECK:STDOUT: %.loc10_48.5: ref %tuple.type.e56 = array_index %v.var, %int_1 // CHECK:STDOUT: } // CHECK:STDOUT: %F.call.loc10_47: init %tuple.type.e56 to %.loc10_48.2 = call %F.ref.loc10_45() // CHECK:STDOUT: %.loc10_48.3: %tuple.type.708 = tuple_literal (%F.call.loc10_42, %F.call.loc10_47) // CHECK:STDOUT: %.loc10_48.4: init %array_type to %v.var = array_init (%F.call.loc10_42, %F.call.loc10_47) // CHECK:STDOUT: %.loc10_3: init %array_type = converted %.loc10_48.3, %.loc10_48.4 // CHECK:STDOUT: assign %v.var, %.loc10_3 // CHECK:STDOUT: %.loc10_35: type = splice_block %array_type [concrete = constants.%array_type] { // CHECK:STDOUT: %C.ref.loc10_24: type = name_ref C, file.%C.decl [concrete = constants.%C] // CHECK:STDOUT: %C.ref.loc10_27: type = name_ref C, file.%C.decl [concrete = constants.%C] // CHECK:STDOUT: %C.ref.loc10_30: type = name_ref C, file.%C.decl [concrete = constants.%C] // CHECK:STDOUT: %.loc10_31.1: %tuple.type.ff9 = tuple_literal (%C.ref.loc10_24, %C.ref.loc10_27, %C.ref.loc10_30) [concrete = constants.%tuple] // CHECK:STDOUT: %int_2: Core.IntLiteral = int_value 2 [concrete = constants.%int_2] // CHECK:STDOUT: %.loc10_31.2: type = converted %.loc10_31.1, constants.%tuple.type.e56 [concrete = constants.%tuple.type.e56] // CHECK:STDOUT: %array_type: type = array_type %int_2, %.loc10_31.2 [concrete = constants.%array_type] // CHECK:STDOUT: } // CHECK:STDOUT: %v: ref %array_type = ref_binding v, %v.var // CHECK:STDOUT: %Destroy.Op.bound: = bound_method %v.var, constants.%Destroy.Op // CHECK:STDOUT: %Destroy.Op.call: init %empty_tuple.type = call %Destroy.Op.bound(%v.var) // CHECK:STDOUT: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @Destroy.Op(%self.param: ref %array_type) = "no_op"; // CHECK:STDOUT: // CHECK:STDOUT: --- array_vs_tuple.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] // CHECK:STDOUT: %empty_tuple: %empty_tuple.type = tuple_value () [concrete] // CHECK:STDOUT: %int_3: Core.IntLiteral = int_value 3 [concrete] // CHECK:STDOUT: %array_type: type = array_type %int_3, %empty_tuple.type [concrete] // CHECK:STDOUT: %pattern_type.035: type = pattern_type %array_type [concrete] // CHECK:STDOUT: %DefaultOrUnformed.type: type = facet_type <@DefaultOrUnformed> [concrete] // CHECK:STDOUT: %T: type = symbolic_binding T, 0 [symbolic] // CHECK:STDOUT: %T.as.DefaultOrUnformed.impl.Op.type.6c6: type = fn_type @T.as.DefaultOrUnformed.impl.Op, @T.as.DefaultOrUnformed.impl(%T) [symbolic] // CHECK:STDOUT: %T.as.DefaultOrUnformed.impl.Op.ae1: %T.as.DefaultOrUnformed.impl.Op.type.6c6 = struct_value () [symbolic] // CHECK:STDOUT: %DefaultOrUnformed.impl_witness.924: = impl_witness imports.%DefaultOrUnformed.impl_witness_table, @T.as.DefaultOrUnformed.impl(%array_type) [concrete] // CHECK:STDOUT: %DefaultOrUnformed.facet.86d: %DefaultOrUnformed.type = facet_value %array_type, (%DefaultOrUnformed.impl_witness.924) [concrete] // CHECK:STDOUT: %tuple.type: type = tuple_type (%empty_tuple.type, %empty_tuple.type, %empty_tuple.type) [concrete] // CHECK:STDOUT: %tuple: %tuple.type = tuple_value (%empty_tuple, %empty_tuple, %empty_tuple) [concrete] // CHECK:STDOUT: %pattern_type.8c1: type = pattern_type %tuple.type [concrete] // CHECK:STDOUT: %DefaultOrUnformed.impl_witness.032: = impl_witness imports.%DefaultOrUnformed.impl_witness_table, @T.as.DefaultOrUnformed.impl(%tuple.type) [concrete] // CHECK:STDOUT: %DefaultOrUnformed.facet.1ea: %DefaultOrUnformed.type = facet_value %tuple.type, (%DefaultOrUnformed.impl_witness.032) [concrete] // CHECK:STDOUT: %Destroy.Op.type.bae255.1: type = fn_type @Destroy.Op.loc8 [concrete] // CHECK:STDOUT: %Destroy.Op.651ba6.1: %Destroy.Op.type.bae255.1 = struct_value () [concrete] // CHECK:STDOUT: %Destroy.Op.type.bae255.2: type = fn_type @Destroy.Op.loc7 [concrete] // CHECK:STDOUT: %Destroy.Op.651ba6.2: %Destroy.Op.type.bae255.2 = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core.import_ref.01d: @T.as.DefaultOrUnformed.impl.%T.as.DefaultOrUnformed.impl.Op.type (%T.as.DefaultOrUnformed.impl.Op.type.6c6) = import_ref Core//prelude/parts/default, loc{{\d+_\d+}}, loaded [symbolic = @T.as.DefaultOrUnformed.impl.%T.as.DefaultOrUnformed.impl.Op (constants.%T.as.DefaultOrUnformed.impl.Op.ae1)] // CHECK:STDOUT: %DefaultOrUnformed.impl_witness_table = impl_witness_table (%Core.import_ref.01d), @T.as.DefaultOrUnformed.impl [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @G() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %a.patt: %pattern_type.035 = ref_binding_pattern a [concrete] // CHECK:STDOUT: %a.var_patt: %pattern_type.035 = var_pattern %a.patt [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %a.var: ref %array_type = var %a.var_patt // CHECK:STDOUT: %DefaultOrUnformed.facet.loc7: %DefaultOrUnformed.type = facet_value constants.%array_type, (constants.%DefaultOrUnformed.impl_witness.924) [concrete = constants.%DefaultOrUnformed.facet.86d] // CHECK:STDOUT: %.loc7_29.1: %DefaultOrUnformed.type = converted constants.%array_type, %DefaultOrUnformed.facet.loc7 [concrete = constants.%DefaultOrUnformed.facet.86d] // CHECK:STDOUT: %as_type.loc7: type = facet_access_type %.loc7_29.1 [concrete = constants.%array_type] // CHECK:STDOUT: %.loc7_29.2: type = converted %.loc7_29.1, %as_type.loc7 [concrete = constants.%array_type] // CHECK:STDOUT: // CHECK:STDOUT: %.loc7_3: ref %array_type = splice_block %a.var {} // CHECK:STDOUT: %T.as.DefaultOrUnformed.impl.Op.call.loc7: init %array_type to %.loc7_3 = call %T.as.DefaultOrUnformed.impl.Op.specific_fn.1() // CHECK:STDOUT: assign %a.var, %T.as.DefaultOrUnformed.impl.Op.call.loc7 // CHECK:STDOUT: %.loc7_28: type = splice_block %array_type [concrete = constants.%array_type] { // CHECK:STDOUT: %.loc7_24.1: %empty_tuple.type = tuple_literal () [concrete = constants.%empty_tuple] // CHECK:STDOUT: %int_3: Core.IntLiteral = int_value 3 [concrete = constants.%int_3] // CHECK:STDOUT: %.loc7_24.2: type = converted %.loc7_24.1, constants.%empty_tuple.type [concrete = constants.%empty_tuple.type] // CHECK:STDOUT: %array_type: type = array_type %int_3, %.loc7_24.2 [concrete = constants.%array_type] // CHECK:STDOUT: } // CHECK:STDOUT: %a: ref %array_type = ref_binding a, %a.var // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %b.patt: %pattern_type.8c1 = ref_binding_pattern b [concrete] // CHECK:STDOUT: %b.var_patt: %pattern_type.8c1 = var_pattern %b.patt [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %b.var: ref %tuple.type = var %b.var_patt // CHECK:STDOUT: %DefaultOrUnformed.facet.loc8: %DefaultOrUnformed.type = facet_value constants.%tuple.type, (constants.%DefaultOrUnformed.impl_witness.032) [concrete = constants.%DefaultOrUnformed.facet.1ea] // CHECK:STDOUT: %.loc8_29.1: %DefaultOrUnformed.type = converted constants.%tuple.type, %DefaultOrUnformed.facet.loc8 [concrete = constants.%DefaultOrUnformed.facet.1ea] // CHECK:STDOUT: %as_type.loc8: type = facet_access_type %.loc8_29.1 [concrete = constants.%tuple.type] // CHECK:STDOUT: %.loc8_29.2: type = converted %.loc8_29.1, %as_type.loc8 [concrete = constants.%tuple.type] // CHECK:STDOUT: // CHECK:STDOUT: %.loc8_3: ref %tuple.type = splice_block %b.var {} // CHECK:STDOUT: %T.as.DefaultOrUnformed.impl.Op.call.loc8: init %tuple.type to %.loc8_3 = call %T.as.DefaultOrUnformed.impl.Op.specific_fn.2() // CHECK:STDOUT: assign %b.var, %T.as.DefaultOrUnformed.impl.Op.call.loc8 // CHECK:STDOUT: %.loc8_28.1: type = splice_block %.loc8_28.6 [concrete = constants.%tuple.type] { // CHECK:STDOUT: %.loc8_19: %empty_tuple.type = tuple_literal () [concrete = constants.%empty_tuple] // CHECK:STDOUT: %.loc8_23: %empty_tuple.type = tuple_literal () [concrete = constants.%empty_tuple] // CHECK:STDOUT: %.loc8_27: %empty_tuple.type = tuple_literal () [concrete = constants.%empty_tuple] // CHECK:STDOUT: %.loc8_28.2: %tuple.type = tuple_literal (%.loc8_19, %.loc8_23, %.loc8_27) [concrete = constants.%tuple] // CHECK:STDOUT: %.loc8_28.3: type = converted constants.%empty_tuple, constants.%empty_tuple.type [concrete = constants.%empty_tuple.type] // CHECK:STDOUT: %.loc8_28.4: type = converted constants.%empty_tuple, constants.%empty_tuple.type [concrete = constants.%empty_tuple.type] // CHECK:STDOUT: %.loc8_28.5: type = converted constants.%empty_tuple, constants.%empty_tuple.type [concrete = constants.%empty_tuple.type] // CHECK:STDOUT: %.loc8_28.6: type = converted %.loc8_28.2, constants.%tuple.type [concrete = constants.%tuple.type] // CHECK:STDOUT: } // CHECK:STDOUT: %b: ref %tuple.type = ref_binding b, %b.var // CHECK:STDOUT: %Destroy.Op.bound.loc8: = bound_method %b.var, constants.%Destroy.Op.651ba6.1 // CHECK:STDOUT: %Destroy.Op.call.loc8: init %empty_tuple.type = call %Destroy.Op.bound.loc8(%b.var) // CHECK:STDOUT: %Destroy.Op.bound.loc7: = bound_method %a.var, constants.%Destroy.Op.651ba6.2 // CHECK:STDOUT: %Destroy.Op.call.loc7: init %empty_tuple.type = call %Destroy.Op.bound.loc7(%a.var) // CHECK:STDOUT: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @Destroy.Op.loc8(%self.param: ref %tuple.type) = "no_op"; // CHECK:STDOUT: // CHECK:STDOUT: fn @Destroy.Op.loc7(%self.param: ref %array_type) = "no_op"; // CHECK:STDOUT: // CHECK:STDOUT: --- assign_return_value.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] // CHECK:STDOUT: %empty_tuple: %empty_tuple.type = tuple_value () [concrete] // CHECK:STDOUT: %tuple.type: type = tuple_type (%empty_tuple.type) [concrete] // CHECK:STDOUT: %F.type: type = fn_type @F [concrete] // CHECK:STDOUT: %F: %F.type = struct_value () [concrete] // CHECK:STDOUT: %int_1: Core.IntLiteral = int_value 1 [concrete] // CHECK:STDOUT: %array_type: type = array_type %int_1, %empty_tuple.type [concrete] // CHECK:STDOUT: %pattern_type.fe8: type = pattern_type %array_type [concrete] // CHECK:STDOUT: %int_0: Core.IntLiteral = int_value 0 [concrete] // CHECK:STDOUT: %array: %array_type = tuple_value (%empty_tuple) [concrete] // CHECK:STDOUT: %Destroy.Op.type.bae255.1: type = fn_type @Destroy.Op.loc8_34 [concrete] // CHECK:STDOUT: %Destroy.Op.651ba6.1: %Destroy.Op.type.bae255.1 = struct_value () [concrete] // CHECK:STDOUT: %Destroy.Op.type.bae255.2: type = fn_type @Destroy.Op.loc8_3 [concrete] // CHECK:STDOUT: %Destroy.Op.651ba6.2: %Destroy.Op.type.bae255.2 = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @Run() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %t.patt: %pattern_type.fe8 = ref_binding_pattern t [concrete] // CHECK:STDOUT: %t.var_patt: %pattern_type.fe8 = var_pattern %t.patt [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %t.var: ref %array_type = var %t.var_patt // CHECK:STDOUT: %F.ref: %F.type = name_ref F, file.%F.decl [concrete = constants.%F] // CHECK:STDOUT: %F.call: init %tuple.type = call %F.ref() // CHECK:STDOUT: %.loc8_34.1: ref %tuple.type = temporary_storage // CHECK:STDOUT: %.loc8_34.2: ref %tuple.type = temporary %.loc8_34.1, %F.call // CHECK:STDOUT: %tuple.elem0: ref %empty_tuple.type = tuple_access %.loc8_34.2, element0 // CHECK:STDOUT: %int_0: Core.IntLiteral = int_value 0 [concrete = constants.%int_0] // CHECK:STDOUT: %.loc8_34.3: ref %empty_tuple.type = array_index %t.var, %int_0 // CHECK:STDOUT: %.loc8_34.4: init %empty_tuple.type = tuple_init () [concrete = constants.%empty_tuple] // CHECK:STDOUT: %.loc8_34.5: init %empty_tuple.type = converted %tuple.elem0, %.loc8_34.4 [concrete = constants.%empty_tuple] // CHECK:STDOUT: %.loc8_34.6: init %array_type to %t.var = array_init (%.loc8_34.5) [concrete = constants.%array] // CHECK:STDOUT: %.loc8_3: init %array_type = converted %F.call, %.loc8_34.6 [concrete = constants.%array] // CHECK:STDOUT: assign %t.var, %.loc8_3 // CHECK:STDOUT: %.loc8_28: type = splice_block %array_type [concrete = constants.%array_type] { // CHECK:STDOUT: %.loc8_24.1: %empty_tuple.type = tuple_literal () [concrete = constants.%empty_tuple] // CHECK:STDOUT: %int_1: Core.IntLiteral = int_value 1 [concrete = constants.%int_1] // CHECK:STDOUT: %.loc8_24.2: type = converted %.loc8_24.1, constants.%empty_tuple.type [concrete = constants.%empty_tuple.type] // CHECK:STDOUT: %array_type: type = array_type %int_1, %.loc8_24.2 [concrete = constants.%array_type] // CHECK:STDOUT: } // CHECK:STDOUT: %t: ref %array_type = ref_binding t, %t.var // CHECK:STDOUT: %Destroy.Op.bound.loc8_34: = bound_method %.loc8_34.2, constants.%Destroy.Op.651ba6.1 // CHECK:STDOUT: %Destroy.Op.call.loc8_34: init %empty_tuple.type = call %Destroy.Op.bound.loc8_34(%.loc8_34.2) // CHECK:STDOUT: %Destroy.Op.bound.loc8_3: = bound_method %t.var, constants.%Destroy.Op.651ba6.2 // CHECK:STDOUT: %Destroy.Op.call.loc8_3: init %empty_tuple.type = call %Destroy.Op.bound.loc8_3(%t.var) // CHECK:STDOUT: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @Destroy.Op.loc8_34(%self.param: ref %tuple.type) = "no_op"; // CHECK:STDOUT: // CHECK:STDOUT: fn @Destroy.Op.loc8_3(%self.param: ref %array_type) = "no_op"; // CHECK:STDOUT: // CHECK:STDOUT: --- nine_elements.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] // CHECK:STDOUT: %empty_tuple: %empty_tuple.type = tuple_value () [concrete] // CHECK:STDOUT: %tuple.type: type = tuple_type (%empty_tuple.type, %empty_tuple.type, %empty_tuple.type, %empty_tuple.type, %empty_tuple.type, %empty_tuple.type, %empty_tuple.type, %empty_tuple.type, %empty_tuple.type) [concrete] // CHECK:STDOUT: %tuple.elem0: ref %empty_tuple.type = tuple_access file.%a.var, element0 [concrete] // CHECK:STDOUT: %tuple.elem1: ref %empty_tuple.type = tuple_access file.%a.var, element1 [concrete] // CHECK:STDOUT: %tuple.elem2: ref %empty_tuple.type = tuple_access file.%a.var, element2 [concrete] // CHECK:STDOUT: %tuple.elem3: ref %empty_tuple.type = tuple_access file.%a.var, element3 [concrete] // CHECK:STDOUT: %tuple.elem4: ref %empty_tuple.type = tuple_access file.%a.var, element4 [concrete] // CHECK:STDOUT: %tuple.elem5: ref %empty_tuple.type = tuple_access file.%a.var, element5 [concrete] // CHECK:STDOUT: %tuple.elem6: ref %empty_tuple.type = tuple_access file.%a.var, element6 [concrete] // CHECK:STDOUT: %tuple.elem7: ref %empty_tuple.type = tuple_access file.%a.var, element7 [concrete] // CHECK:STDOUT: %tuple.elem8: ref %empty_tuple.type = tuple_access file.%a.var, element8 [concrete] // CHECK:STDOUT: %int_9: Core.IntLiteral = int_value 9 [concrete] // CHECK:STDOUT: %array_type: type = array_type %int_9, %empty_tuple.type [concrete] // CHECK:STDOUT: %pattern_type.3db: type = pattern_type %array_type [concrete] // CHECK:STDOUT: %int_0: Core.IntLiteral = int_value 0 [concrete] // CHECK:STDOUT: %int_1: Core.IntLiteral = int_value 1 [concrete] // CHECK:STDOUT: %int_2: Core.IntLiteral = int_value 2 [concrete] // CHECK:STDOUT: %int_3: Core.IntLiteral = int_value 3 [concrete] // CHECK:STDOUT: %int_4: Core.IntLiteral = int_value 4 [concrete] // CHECK:STDOUT: %int_5: Core.IntLiteral = int_value 5 [concrete] // CHECK:STDOUT: %int_6: Core.IntLiteral = int_value 6 [concrete] // CHECK:STDOUT: %int_7: Core.IntLiteral = int_value 7 [concrete] // CHECK:STDOUT: %int_8: Core.IntLiteral = int_value 8 [concrete] // CHECK:STDOUT: %array: %array_type = tuple_value (%empty_tuple, %empty_tuple, %empty_tuple, %empty_tuple, %empty_tuple, %empty_tuple, %empty_tuple, %empty_tuple, %empty_tuple) [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %b.patt: %pattern_type.3db = ref_binding_pattern b [concrete] // CHECK:STDOUT: %b.var_patt: %pattern_type.3db = var_pattern %b.patt [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %b.var: ref %array_type = var %b.var_patt [concrete] // CHECK:STDOUT: %.loc9_19: type = splice_block %array_type [concrete = constants.%array_type] { // CHECK:STDOUT: %.loc9_15.1: %empty_tuple.type = tuple_literal () [concrete = constants.%empty_tuple] // CHECK:STDOUT: %int_9: Core.IntLiteral = int_value 9 [concrete = constants.%int_9] // CHECK:STDOUT: %.loc9_15.2: type = converted %.loc9_15.1, constants.%empty_tuple.type [concrete = constants.%empty_tuple.type] // CHECK:STDOUT: %array_type: type = array_type %int_9, %.loc9_15.2 [concrete = constants.%array_type] // CHECK:STDOUT: } // CHECK:STDOUT: %b: ref %array_type = ref_binding b, %b.var [concrete = %b.var] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @__global_init() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: // CHECK:STDOUT: %a.ref: ref %tuple.type = name_ref a, file.%a [concrete = file.%a.var] // CHECK:STDOUT: %tuple.elem0.loc9: ref %empty_tuple.type = tuple_access %a.ref, element0 [concrete = constants.%tuple.elem0] // CHECK:STDOUT: %int_0: Core.IntLiteral = int_value 0 [concrete = constants.%int_0] // CHECK:STDOUT: %.loc9_23.1: ref %empty_tuple.type = array_index file.%b.var, %int_0 // CHECK:STDOUT: %.loc9_23.2: init %empty_tuple.type = tuple_init () [concrete = constants.%empty_tuple] // CHECK:STDOUT: %.loc9_23.3: init %empty_tuple.type = converted %tuple.elem0.loc9, %.loc9_23.2 [concrete = constants.%empty_tuple] // CHECK:STDOUT: %tuple.elem1.loc9: ref %empty_tuple.type = tuple_access %a.ref, element1 [concrete = constants.%tuple.elem1] // CHECK:STDOUT: %int_1: Core.IntLiteral = int_value 1 [concrete = constants.%int_1] // CHECK:STDOUT: %.loc9_23.4: ref %empty_tuple.type = array_index file.%b.var, %int_1 // CHECK:STDOUT: %.loc9_23.5: init %empty_tuple.type = tuple_init () [concrete = constants.%empty_tuple] // CHECK:STDOUT: %.loc9_23.6: init %empty_tuple.type = converted %tuple.elem1.loc9, %.loc9_23.5 [concrete = constants.%empty_tuple] // CHECK:STDOUT: %tuple.elem2.loc9: ref %empty_tuple.type = tuple_access %a.ref, element2 [concrete = constants.%tuple.elem2] // CHECK:STDOUT: %int_2: Core.IntLiteral = int_value 2 [concrete = constants.%int_2] // CHECK:STDOUT: %.loc9_23.7: ref %empty_tuple.type = array_index file.%b.var, %int_2 // CHECK:STDOUT: %.loc9_23.8: init %empty_tuple.type = tuple_init () [concrete = constants.%empty_tuple] // CHECK:STDOUT: %.loc9_23.9: init %empty_tuple.type = converted %tuple.elem2.loc9, %.loc9_23.8 [concrete = constants.%empty_tuple] // CHECK:STDOUT: %tuple.elem3.loc9: ref %empty_tuple.type = tuple_access %a.ref, element3 [concrete = constants.%tuple.elem3] // CHECK:STDOUT: %int_3: Core.IntLiteral = int_value 3 [concrete = constants.%int_3] // CHECK:STDOUT: %.loc9_23.10: ref %empty_tuple.type = array_index file.%b.var, %int_3 // CHECK:STDOUT: %.loc9_23.11: init %empty_tuple.type = tuple_init () [concrete = constants.%empty_tuple] // CHECK:STDOUT: %.loc9_23.12: init %empty_tuple.type = converted %tuple.elem3.loc9, %.loc9_23.11 [concrete = constants.%empty_tuple] // CHECK:STDOUT: %tuple.elem4.loc9: ref %empty_tuple.type = tuple_access %a.ref, element4 [concrete = constants.%tuple.elem4] // CHECK:STDOUT: %int_4: Core.IntLiteral = int_value 4 [concrete = constants.%int_4] // CHECK:STDOUT: %.loc9_23.13: ref %empty_tuple.type = array_index file.%b.var, %int_4 // CHECK:STDOUT: %.loc9_23.14: init %empty_tuple.type = tuple_init () [concrete = constants.%empty_tuple] // CHECK:STDOUT: %.loc9_23.15: init %empty_tuple.type = converted %tuple.elem4.loc9, %.loc9_23.14 [concrete = constants.%empty_tuple] // CHECK:STDOUT: %tuple.elem5.loc9: ref %empty_tuple.type = tuple_access %a.ref, element5 [concrete = constants.%tuple.elem5] // CHECK:STDOUT: %int_5: Core.IntLiteral = int_value 5 [concrete = constants.%int_5] // CHECK:STDOUT: %.loc9_23.16: ref %empty_tuple.type = array_index file.%b.var, %int_5 // CHECK:STDOUT: %.loc9_23.17: init %empty_tuple.type = tuple_init () [concrete = constants.%empty_tuple] // CHECK:STDOUT: %.loc9_23.18: init %empty_tuple.type = converted %tuple.elem5.loc9, %.loc9_23.17 [concrete = constants.%empty_tuple] // CHECK:STDOUT: %tuple.elem6.loc9: ref %empty_tuple.type = tuple_access %a.ref, element6 [concrete = constants.%tuple.elem6] // CHECK:STDOUT: %int_6: Core.IntLiteral = int_value 6 [concrete = constants.%int_6] // CHECK:STDOUT: %.loc9_23.19: ref %empty_tuple.type = array_index file.%b.var, %int_6 // CHECK:STDOUT: %.loc9_23.20: init %empty_tuple.type = tuple_init () [concrete = constants.%empty_tuple] // CHECK:STDOUT: %.loc9_23.21: init %empty_tuple.type = converted %tuple.elem6.loc9, %.loc9_23.20 [concrete = constants.%empty_tuple] // CHECK:STDOUT: %tuple.elem7.loc9: ref %empty_tuple.type = tuple_access %a.ref, element7 [concrete = constants.%tuple.elem7] // CHECK:STDOUT: %int_7: Core.IntLiteral = int_value 7 [concrete = constants.%int_7] // CHECK:STDOUT: %.loc9_23.22: ref %empty_tuple.type = array_index file.%b.var, %int_7 // CHECK:STDOUT: %.loc9_23.23: init %empty_tuple.type = tuple_init () [concrete = constants.%empty_tuple] // CHECK:STDOUT: %.loc9_23.24: init %empty_tuple.type = converted %tuple.elem7.loc9, %.loc9_23.23 [concrete = constants.%empty_tuple] // CHECK:STDOUT: %tuple.elem8.loc9: ref %empty_tuple.type = tuple_access %a.ref, element8 [concrete = constants.%tuple.elem8] // CHECK:STDOUT: %int_8: Core.IntLiteral = int_value 8 [concrete = constants.%int_8] // CHECK:STDOUT: %.loc9_23.25: ref %empty_tuple.type = array_index file.%b.var, %int_8 // CHECK:STDOUT: %.loc9_23.26: init %empty_tuple.type = tuple_init () [concrete = constants.%empty_tuple] // CHECK:STDOUT: %.loc9_23.27: init %empty_tuple.type = converted %tuple.elem8.loc9, %.loc9_23.26 [concrete = constants.%empty_tuple] // CHECK:STDOUT: %.loc9_23.28: init %array_type to file.%b.var = array_init (%.loc9_23.3, %.loc9_23.6, %.loc9_23.9, %.loc9_23.12, %.loc9_23.15, %.loc9_23.18, %.loc9_23.21, %.loc9_23.24, %.loc9_23.27) [concrete = constants.%array] // CHECK:STDOUT: %.loc9_1: init %array_type = converted %a.ref, %.loc9_23.28 [concrete = constants.%array] // CHECK:STDOUT: assign file.%b.var, %.loc9_1 // CHECK:STDOUT: // CHECK:STDOUT: } // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/array/bound_values.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/full.carbon // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/array/bound_values.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/array/bound_values.carbon // --- addition.carbon library "[[@TEST_NAME]]"; var a: //@dump-sem-ir-begin array(i32, 1 + 2) //@dump-sem-ir-end = (1, 2, 3); let b: array(i32, 3)* = &a; // --- unsigned.carbon library "[[@TEST_NAME]]"; var a: //@dump-sem-ir-begin array(i32, 3 as u32) //@dump-sem-ir-end = (1, 2, 3); let b: array(i32, 3)* = &a; // --- fail_negative.carbon library "[[@TEST_NAME]]"; // CHECK:STDERR: fail_negative.carbon:[[@LINE+4]]:19: error: array bound of -1 is negative [ArrayBoundNegative] // CHECK:STDERR: var a: array(i32, -1); // CHECK:STDERR: ^~ // CHECK:STDERR: var a: array(i32, -1); // --- fail_overflow.carbon library "[[@TEST_NAME]]"; // CHECK:STDERR: fail_overflow.carbon:[[@LINE+4]]:19: error: array bound of 39999999999999999993 is too large [ArrayBoundTooLarge] // CHECK:STDERR: var a: array(i32, 39999999999999999993); // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: var a: array(i32, 39999999999999999993); // --- fail_invalid_type_with_overflow.carbon library "[[@TEST_NAME]]"; // CHECK:STDERR: fail_invalid_type_with_overflow.carbon:[[@LINE+7]]:14: error: cannot implicitly convert non-type value of type `Core.IntLiteral` to `type` [ConversionFailureNonTypeToFacet] // CHECK:STDERR: var b: array(1, 39999999999999999993); // CHECK:STDERR: ^ // CHECK:STDERR: fail_invalid_type_with_overflow.carbon:[[@LINE+4]]:14: note: type `Core.IntLiteral` does not implement interface `Core.ImplicitAs(type)` [MissingImplInMemberAccessInContext] // CHECK:STDERR: var b: array(1, 39999999999999999993); // CHECK:STDERR: ^ // CHECK:STDERR: var b: array(1, 39999999999999999993); // CHECK:STDOUT: --- addition.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [concrete] // CHECK:STDOUT: %i32: type = class_type @Int, @Int(%int_32) [concrete] // CHECK:STDOUT: %int_1.5b8: Core.IntLiteral = int_value 1 [concrete] // CHECK:STDOUT: %int_2.ecc: Core.IntLiteral = int_value 2 [concrete] // CHECK:STDOUT: %AddWith.type.4c0: type = facet_type <@AddWith, @AddWith(Core.IntLiteral)> [concrete] // CHECK:STDOUT: %AddWith.impl_witness: = impl_witness imports.%AddWith.impl_witness_table [concrete] // CHECK:STDOUT: %AddWith.facet: %AddWith.type.4c0 = facet_value Core.IntLiteral, (%AddWith.impl_witness) [concrete] // CHECK:STDOUT: %AddWith.WithSelf.Op.type.900: type = fn_type @AddWith.WithSelf.Op, @AddWith.WithSelf(Core.IntLiteral, %AddWith.facet) [concrete] // CHECK:STDOUT: %.302: type = fn_type_with_self_type %AddWith.WithSelf.Op.type.900, %AddWith.facet [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.AddWith.impl.Op.type: type = fn_type @Core.IntLiteral.as.AddWith.impl.Op [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.AddWith.impl.Op: %Core.IntLiteral.as.AddWith.impl.Op.type = struct_value () [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.AddWith.impl.Op.bound: = bound_method %int_1.5b8, %Core.IntLiteral.as.AddWith.impl.Op [concrete] // CHECK:STDOUT: %int_3.1ba: Core.IntLiteral = int_value 3 [concrete] // CHECK:STDOUT: %array_type: type = array_type %int_3.1ba, %i32 [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core.import_ref.abd = import_ref Core//prelude/operators/arithmetic, loc{{\d+_\d+}}, unloaded // CHECK:STDOUT: %Core.import_ref.8dd: %Core.IntLiteral.as.AddWith.impl.Op.type = import_ref Core//prelude/operators/arithmetic, loc{{\d+_\d+}}, loaded [concrete = constants.%Core.IntLiteral.as.AddWith.impl.Op] // CHECK:STDOUT: %AddWith.impl_witness_table = impl_witness_table (%Core.import_ref.abd, %Core.import_ref.8dd), @Core.IntLiteral.as.AddWith.impl [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: %.loc6_21: type = splice_block %array_type.loc6 [concrete = constants.%array_type] { // CHECK:STDOUT: %i32.loc6: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %int_1: Core.IntLiteral = int_value 1 [concrete = constants.%int_1.5b8] // CHECK:STDOUT: %int_2: Core.IntLiteral = int_value 2 [concrete = constants.%int_2.ecc] // CHECK:STDOUT: %impl.elem1: %.302 = impl_witness_access constants.%AddWith.impl_witness, element1 [concrete = constants.%Core.IntLiteral.as.AddWith.impl.Op] // CHECK:STDOUT: %bound_method: = bound_method %int_1, %impl.elem1 [concrete = constants.%Core.IntLiteral.as.AddWith.impl.Op.bound] // CHECK:STDOUT: %Core.IntLiteral.as.AddWith.impl.Op.call: init Core.IntLiteral = call %bound_method(%int_1, %int_2) [concrete = constants.%int_3.1ba] // CHECK:STDOUT: %.loc6_18.1: Core.IntLiteral = value_of_initializer %Core.IntLiteral.as.AddWith.impl.Op.call [concrete = constants.%int_3.1ba] // CHECK:STDOUT: %.loc6_18.2: Core.IntLiteral = converted %Core.IntLiteral.as.AddWith.impl.Op.call, %.loc6_18.1 [concrete = constants.%int_3.1ba] // CHECK:STDOUT: %array_type.loc6: type = array_type %.loc6_18.2, %i32.loc6 [concrete = constants.%array_type] // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @__global_init() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- unsigned.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [concrete] // CHECK:STDOUT: %i32: type = class_type @Int, @Int(%int_32) [concrete] // CHECK:STDOUT: %int_3.1ba: Core.IntLiteral = int_value 3 [concrete] // CHECK:STDOUT: %u32: type = class_type @UInt, @UInt(%int_32) [concrete] // CHECK:STDOUT: %As.type.346: type = facet_type <@As, @As(%u32)> [concrete] // CHECK:STDOUT: %To.fe9: Core.IntLiteral = symbolic_binding To, 0 [symbolic] // CHECK:STDOUT: %Core.IntLiteral.as.As.impl.Convert.type.2b1: type = fn_type @Core.IntLiteral.as.As.impl.Convert, @Core.IntLiteral.as.As.impl(%To.fe9) [symbolic] // CHECK:STDOUT: %Core.IntLiteral.as.As.impl.Convert.979: %Core.IntLiteral.as.As.impl.Convert.type.2b1 = struct_value () [symbolic] // CHECK:STDOUT: %From.fe9: Core.IntLiteral = symbolic_binding From, 0 [symbolic] // CHECK:STDOUT: %As.impl_witness.e1d: = impl_witness imports.%As.impl_witness_table.7eb, @Core.IntLiteral.as.As.impl(%int_32) [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.As.impl.Convert.type.3e7: type = fn_type @Core.IntLiteral.as.As.impl.Convert, @Core.IntLiteral.as.As.impl(%int_32) [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.As.impl.Convert.bbd: %Core.IntLiteral.as.As.impl.Convert.type.3e7 = struct_value () [concrete] // CHECK:STDOUT: %As.facet: %As.type.346 = facet_value Core.IntLiteral, (%As.impl_witness.e1d) [concrete] // CHECK:STDOUT: %As.WithSelf.Convert.type.89a: type = fn_type @As.WithSelf.Convert, @As.WithSelf(%u32, %As.facet) [concrete] // CHECK:STDOUT: %.1a4: type = fn_type_with_self_type %As.WithSelf.Convert.type.89a, %As.facet [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.As.impl.Convert.bound: = bound_method %int_3.1ba, %Core.IntLiteral.as.As.impl.Convert.bbd [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.As.impl.Convert.specific_fn: = specific_function %Core.IntLiteral.as.As.impl.Convert.bbd, @Core.IntLiteral.as.As.impl.Convert(%int_32) [concrete] // CHECK:STDOUT: %bound_method.d64: = bound_method %int_3.1ba, %Core.IntLiteral.as.As.impl.Convert.specific_fn [concrete] // CHECK:STDOUT: %int_3.d14: %u32 = int_value 3 [concrete] // CHECK:STDOUT: %ImplicitAs.type.139: type = facet_type <@ImplicitAs, @ImplicitAs(Core.IntLiteral)> [concrete] // CHECK:STDOUT: %UInt.as.ImplicitAs.impl.Convert.type.7f8: type = fn_type @UInt.as.ImplicitAs.impl.Convert.1, @UInt.as.ImplicitAs.impl.607(%From.fe9) [symbolic] // CHECK:STDOUT: %UInt.as.ImplicitAs.impl.Convert.0ea: %UInt.as.ImplicitAs.impl.Convert.type.7f8 = struct_value () [symbolic] // CHECK:STDOUT: %ImplicitAs.impl_witness.895: = impl_witness imports.%ImplicitAs.impl_witness_table.493, @UInt.as.ImplicitAs.impl.607(%int_32) [concrete] // CHECK:STDOUT: %UInt.as.ImplicitAs.impl.Convert.type.543: type = fn_type @UInt.as.ImplicitAs.impl.Convert.1, @UInt.as.ImplicitAs.impl.607(%int_32) [concrete] // CHECK:STDOUT: %UInt.as.ImplicitAs.impl.Convert.342: %UInt.as.ImplicitAs.impl.Convert.type.543 = struct_value () [concrete] // CHECK:STDOUT: %ImplicitAs.facet.2c6: %ImplicitAs.type.139 = facet_value %u32, (%ImplicitAs.impl_witness.895) [concrete] // CHECK:STDOUT: %ImplicitAs.WithSelf.Convert.type.70d: type = fn_type @ImplicitAs.WithSelf.Convert, @ImplicitAs.WithSelf(Core.IntLiteral, %ImplicitAs.facet.2c6) [concrete] // CHECK:STDOUT: %.044: type = fn_type_with_self_type %ImplicitAs.WithSelf.Convert.type.70d, %ImplicitAs.facet.2c6 [concrete] // CHECK:STDOUT: %UInt.as.ImplicitAs.impl.Convert.bound: = bound_method %int_3.d14, %UInt.as.ImplicitAs.impl.Convert.342 [concrete] // CHECK:STDOUT: %UInt.as.ImplicitAs.impl.Convert.specific_fn: = specific_function %UInt.as.ImplicitAs.impl.Convert.342, @UInt.as.ImplicitAs.impl.Convert.1(%int_32) [concrete] // CHECK:STDOUT: %bound_method.c86: = bound_method %int_3.d14, %UInt.as.ImplicitAs.impl.Convert.specific_fn [concrete] // CHECK:STDOUT: %array_type: type = array_type %int_3.1ba, %i32 [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core.import_ref.600: @Core.IntLiteral.as.As.impl.%Core.IntLiteral.as.As.impl.Convert.type (%Core.IntLiteral.as.As.impl.Convert.type.2b1) = import_ref Core//prelude/types/uint, loc{{\d+_\d+}}, loaded [symbolic = @Core.IntLiteral.as.As.impl.%Core.IntLiteral.as.As.impl.Convert (constants.%Core.IntLiteral.as.As.impl.Convert.979)] // CHECK:STDOUT: %As.impl_witness_table.7eb = impl_witness_table (%Core.import_ref.600), @Core.IntLiteral.as.As.impl [concrete] // CHECK:STDOUT: %Core.import_ref.483: @UInt.as.ImplicitAs.impl.607.%UInt.as.ImplicitAs.impl.Convert.type (%UInt.as.ImplicitAs.impl.Convert.type.7f8) = import_ref Core//prelude/types/uint, loc{{\d+_\d+}}, loaded [symbolic = @UInt.as.ImplicitAs.impl.607.%UInt.as.ImplicitAs.impl.Convert (constants.%UInt.as.ImplicitAs.impl.Convert.0ea)] // CHECK:STDOUT: %ImplicitAs.impl_witness_table.493 = impl_witness_table (%Core.import_ref.483), @UInt.as.ImplicitAs.impl.607 [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: %.loc6_24: type = splice_block %array_type.loc6 [concrete = constants.%array_type] { // CHECK:STDOUT: %i32.loc6: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %int_3.loc6: Core.IntLiteral = int_value 3 [concrete = constants.%int_3.1ba] // CHECK:STDOUT: %u32: type = type_literal constants.%u32 [concrete = constants.%u32] // CHECK:STDOUT: %impl.elem0.loc6_18.1: %.1a4 = impl_witness_access constants.%As.impl_witness.e1d, element0 [concrete = constants.%Core.IntLiteral.as.As.impl.Convert.bbd] // CHECK:STDOUT: %bound_method.loc6_18.1: = bound_method %int_3.loc6, %impl.elem0.loc6_18.1 [concrete = constants.%Core.IntLiteral.as.As.impl.Convert.bound] // CHECK:STDOUT: %specific_fn.loc6_18.1: = specific_function %impl.elem0.loc6_18.1, @Core.IntLiteral.as.As.impl.Convert(constants.%int_32) [concrete = constants.%Core.IntLiteral.as.As.impl.Convert.specific_fn] // CHECK:STDOUT: %bound_method.loc6_18.2: = bound_method %int_3.loc6, %specific_fn.loc6_18.1 [concrete = constants.%bound_method.d64] // CHECK:STDOUT: %Core.IntLiteral.as.As.impl.Convert.call: init %u32 = call %bound_method.loc6_18.2(%int_3.loc6) [concrete = constants.%int_3.d14] // CHECK:STDOUT: %.loc6_18.1: %u32 = value_of_initializer %Core.IntLiteral.as.As.impl.Convert.call [concrete = constants.%int_3.d14] // CHECK:STDOUT: %.loc6_18.2: %u32 = converted %int_3.loc6, %.loc6_18.1 [concrete = constants.%int_3.d14] // CHECK:STDOUT: %impl.elem0.loc6_18.2: %.044 = impl_witness_access constants.%ImplicitAs.impl_witness.895, element0 [concrete = constants.%UInt.as.ImplicitAs.impl.Convert.342] // CHECK:STDOUT: %bound_method.loc6_18.3: = bound_method %.loc6_18.2, %impl.elem0.loc6_18.2 [concrete = constants.%UInt.as.ImplicitAs.impl.Convert.bound] // CHECK:STDOUT: %specific_fn.loc6_18.2: = specific_function %impl.elem0.loc6_18.2, @UInt.as.ImplicitAs.impl.Convert.1(constants.%int_32) [concrete = constants.%UInt.as.ImplicitAs.impl.Convert.specific_fn] // CHECK:STDOUT: %bound_method.loc6_18.4: = bound_method %.loc6_18.2, %specific_fn.loc6_18.2 [concrete = constants.%bound_method.c86] // CHECK:STDOUT: %UInt.as.ImplicitAs.impl.Convert.call: init Core.IntLiteral = call %bound_method.loc6_18.4(%.loc6_18.2) [concrete = constants.%int_3.1ba] // CHECK:STDOUT: %.loc6_18.3: Core.IntLiteral = value_of_initializer %UInt.as.ImplicitAs.impl.Convert.call [concrete = constants.%int_3.1ba] // CHECK:STDOUT: %.loc6_18.4: Core.IntLiteral = converted %.loc6_18.2, %.loc6_18.3 [concrete = constants.%int_3.1ba] // CHECK:STDOUT: %array_type.loc6: type = array_type %.loc6_18.4, %i32.loc6 [concrete = constants.%array_type] // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @__global_init() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: // CHECK:STDOUT: } // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/array/element_mismatches.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/convert.carbon // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/array/element_mismatches.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/array/element_mismatches.carbon // --- fail_arg_wrong_type.carbon library "[[@TEST_NAME]]"; class C {} // CHECK:STDERR: fail_arg_wrong_type.carbon:[[@LINE+7]]:22: error: cannot implicitly convert expression of type `()` to `C` [ConversionFailure] // CHECK:STDERR: var a: array(C, 3) = ({}, (), true); // CHECK:STDERR: ^~~~~~~~~~~~~~ // CHECK:STDERR: fail_arg_wrong_type.carbon:[[@LINE+4]]:22: note: type `()` does not implement interface `Core.ImplicitAs(C)` [MissingImplInMemberAccessInContext] // CHECK:STDERR: var a: array(C, 3) = ({}, (), true); // CHECK:STDERR: ^~~~~~~~~~~~~~ // CHECK:STDERR: var a: array(C, 3) = ({}, (), true); // --- fail_var_wrong_type.carbon library "[[@TEST_NAME]]"; class C {} class D {} var a: (D, C, D); // CHECK:STDERR: fail_var_wrong_type.carbon:[[@LINE+7]]:22: error: cannot implicitly convert expression of type `D` to `C` [ConversionFailure] // CHECK:STDERR: var b: array(C, 3) = a; // CHECK:STDERR: ^ // CHECK:STDERR: fail_var_wrong_type.carbon:[[@LINE+4]]:22: note: type `D` does not implement interface `Core.ImplicitAs(C)` [MissingImplInMemberAccessInContext] // CHECK:STDERR: var b: array(C, 3) = a; // CHECK:STDERR: ^ // CHECK:STDERR: var b: array(C, 3) = a; var c: (C, D, D); // CHECK:STDERR: fail_var_wrong_type.carbon:[[@LINE+7]]:22: error: cannot copy value of type `C` [CopyOfUncopyableType] // CHECK:STDERR: var d: array(C, 3) = c; // CHECK:STDERR: ^ // CHECK:STDERR: fail_var_wrong_type.carbon:[[@LINE+4]]:22: note: type `C` does not implement interface `Core.Copy` [MissingImplInMemberAccessInContext] // CHECK:STDERR: var d: array(C, 3) = c; // CHECK:STDERR: ^ // CHECK:STDERR: var d: array(C, 3) = c; // --- fail_arg_too_short.carbon library "[[@TEST_NAME]]"; class C {} // CHECK:STDERR: fail_arg_too_short.carbon:[[@LINE+4]]:22: error: cannot initialize array of 3 elements from 2 initializers [ArrayInitFromLiteralArgCountMismatch] // CHECK:STDERR: var a: array(C, 3) = ({}, {}); // CHECK:STDERR: ^~~~~~~~ // CHECK:STDERR: var a: array(C, 3) = ({}, {}); // --- fail_var_too_short.carbon library "[[@TEST_NAME]]"; class C {} var a: (C, C); // CHECK:STDERR: fail_var_too_short.carbon:[[@LINE+4]]:22: error: cannot initialize array of 3 elements from tuple with 2 elements [ArrayInitFromExprArgCountMismatch] // CHECK:STDERR: var b: array(C, 3) = a; // CHECK:STDERR: ^ // CHECK:STDERR: var b: array(C, 3) = a; // --- fail_arg_too_long.carbon // CHECK:STDERR: fail_arg_too_long.carbon:[[@LINE+4]]:23: error: cannot initialize array of 1 element from 3 initializers [ArrayInitFromLiteralArgCountMismatch] // CHECK:STDERR: var a: array((), 1) = ((), (), ()); // CHECK:STDERR: ^~~~~~~~~~~~ // CHECK:STDERR: var a: array((), 1) = ((), (), ()); ================================================ FILE: toolchain/check/testdata/array/import.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/int.carbon // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/array/import.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/array/import.carbon // --- library.carbon library "[[@TEST_NAME]]"; fn F() -> array(i32, 42); // --- user.carbon import library "library"; fn G(n: i32) -> i32 { //@dump-sem-ir-begin return F()[n]; //@dump-sem-ir-end } // --- fail_todo_symbolic_decl.carbon library "[[@TEST_NAME]]"; interface I { let value:! array(i32, 1); } class C {} // CHECK:STDERR: fail_todo_symbolic_decl.carbon:[[@LINE+4]]:28: error: expression is runtime; expected constant [EvalRequiresConstantValue] // CHECK:STDERR: impl C as I where .value = (1,) {} // CHECK:STDERR: ^~~~ // CHECK:STDERR: impl C as I where .value = (1,) {} // --- fail_todo_import_symbolic_decl.carbon library "[[@TEST_NAME]]"; import library "symbolic_decl"; fn F() -> array(i32, 1) { //@dump-sem-ir-begin // CHECK:STDERR: fail_todo_import_symbolic_decl.carbon:[[@LINE+4]]:11: error: cannot convert type `C` into type implementing `I` [ConversionFailureTypeToFacet] // CHECK:STDERR: return (C as I).value; // CHECK:STDERR: ^~~~~~ // CHECK:STDERR: return (C as I).value; //@dump-sem-ir-end } // CHECK:STDOUT: --- user.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [concrete] // CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] // CHECK:STDOUT: %N: Core.IntLiteral = symbolic_binding N, 0 [symbolic] // CHECK:STDOUT: %i32: type = class_type @Int, @Int(%int_32) [concrete] // CHECK:STDOUT: %F.type: type = fn_type @F [concrete] // CHECK:STDOUT: %F: %F.type = struct_value () [concrete] // CHECK:STDOUT: %int_42: Core.IntLiteral = int_value 42 [concrete] // CHECK:STDOUT: %array_type: type = array_type %int_42, %i32 [concrete] // CHECK:STDOUT: %Copy.type: type = facet_type <@Copy> [concrete] // CHECK:STDOUT: %Int.as.Copy.impl.Op.type.824: type = fn_type @Int.as.Copy.impl.Op, @Int.as.Copy.impl(%N) [symbolic] // CHECK:STDOUT: %Int.as.Copy.impl.Op.9b9: %Int.as.Copy.impl.Op.type.824 = struct_value () [symbolic] // CHECK:STDOUT: %Copy.impl_witness.f17: = impl_witness imports.%Copy.impl_witness_table.e76, @Int.as.Copy.impl(%int_32) [concrete] // CHECK:STDOUT: %Int.as.Copy.impl.Op.type.546: type = fn_type @Int.as.Copy.impl.Op, @Int.as.Copy.impl(%int_32) [concrete] // CHECK:STDOUT: %Int.as.Copy.impl.Op.664: %Int.as.Copy.impl.Op.type.546 = struct_value () [concrete] // CHECK:STDOUT: %Copy.facet: %Copy.type = facet_value %i32, (%Copy.impl_witness.f17) [concrete] // CHECK:STDOUT: %Copy.WithSelf.Op.type.081: type = fn_type @Copy.WithSelf.Op, @Copy.WithSelf(%Copy.facet) [concrete] // CHECK:STDOUT: %.8e2: type = fn_type_with_self_type %Copy.WithSelf.Op.type.081, %Copy.facet [concrete] // CHECK:STDOUT: %Int.as.Copy.impl.Op.specific_fn: = specific_function %Int.as.Copy.impl.Op.664, @Int.as.Copy.impl.Op(%int_32) [concrete] // CHECK:STDOUT: %Destroy.Op.type: type = fn_type @Destroy.Op [concrete] // CHECK:STDOUT: %Destroy.Op: %Destroy.Op.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Main.F: %F.type = import_ref Main//library, F, loaded [concrete = constants.%F] // CHECK:STDOUT: %Core.import_ref.18d: @Int.as.Copy.impl.%Int.as.Copy.impl.Op.type (%Int.as.Copy.impl.Op.type.824) = import_ref Core//prelude/parts/int, loc{{\d+_\d+}}, loaded [symbolic = @Int.as.Copy.impl.%Int.as.Copy.impl.Op (constants.%Int.as.Copy.impl.Op.9b9)] // CHECK:STDOUT: %Copy.impl_witness_table.e76 = impl_witness_table (%Core.import_ref.18d), @Int.as.Copy.impl [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @G(%n.param: %i32) -> out %return.param: %i32 { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %F.ref: %F.type = name_ref F, imports.%Main.F [concrete = constants.%F] // CHECK:STDOUT: %.loc6_12.1: ref %array_type = temporary_storage // CHECK:STDOUT: %F.call: init %array_type to %.loc6_12.1 = call %F.ref() // CHECK:STDOUT: %n.ref: %i32 = name_ref n, %n // CHECK:STDOUT: %.loc6_12.2: ref %array_type = temporary %.loc6_12.1, %F.call // CHECK:STDOUT: %.loc6_15.1: ref %i32 = array_index %.loc6_12.2, %n.ref // CHECK:STDOUT: %.loc6_15.2: %i32 = acquire_value %.loc6_15.1 // CHECK:STDOUT: %impl.elem0: %.8e2 = impl_witness_access constants.%Copy.impl_witness.f17, element0 [concrete = constants.%Int.as.Copy.impl.Op.664] // CHECK:STDOUT: %bound_method.loc6_15.1: = bound_method %.loc6_15.2, %impl.elem0 // CHECK:STDOUT: %specific_fn: = specific_function %impl.elem0, @Int.as.Copy.impl.Op(constants.%int_32) [concrete = constants.%Int.as.Copy.impl.Op.specific_fn] // CHECK:STDOUT: %bound_method.loc6_15.2: = bound_method %.loc6_15.2, %specific_fn // CHECK:STDOUT: %Int.as.Copy.impl.Op.call: init %i32 = call %bound_method.loc6_15.2(%.loc6_15.2) // CHECK:STDOUT: %Destroy.Op.bound: = bound_method %.loc6_12.2, constants.%Destroy.Op // CHECK:STDOUT: %Destroy.Op.call: init %empty_tuple.type = call %Destroy.Op.bound(%.loc6_12.2) // CHECK:STDOUT: return %Int.as.Copy.impl.Op.call // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @Destroy.Op(%self.param: ref %array_type) = "no_op"; // CHECK:STDOUT: // CHECK:STDOUT: --- fail_todo_import_symbolic_decl.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [concrete] // CHECK:STDOUT: %i32: type = class_type @Int, @Int(%int_32) [concrete] // CHECK:STDOUT: %int_1: Core.IntLiteral = int_value 1 [concrete] // CHECK:STDOUT: %array_type: type = array_type %int_1, %i32 [concrete] // CHECK:STDOUT: %C: type = class_type @C [concrete] // CHECK:STDOUT: %I.type: type = facet_type <@I> [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Main.I: type = import_ref Main//symbolic_decl, I, loaded [concrete = constants.%I.type] // CHECK:STDOUT: %Main.C: type = import_ref Main//symbolic_decl, C, loaded [concrete = constants.%C] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @F() -> out %return.param: %array_type { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %C.ref: type = name_ref C, imports.%Main.C [concrete = constants.%C] // CHECK:STDOUT: %I.ref: type = name_ref I, imports.%Main.I [concrete = constants.%I.type] // CHECK:STDOUT: %value.ref: = name_ref value, [concrete = ] // CHECK:STDOUT: return to %return.param // CHECK:STDOUT: } // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/array/index_not_literal.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/int.carbon // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/array/index_not_literal.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/array/index_not_literal.carbon // --- function_param.carbon fn F(arr: array(i32, 3), i: i32) -> i32 { //@dump-sem-ir-begin return arr[i]; //@dump-sem-ir-end } fn G() -> i32 { //@dump-sem-ir-begin return F((1, 2, 3), 1); //@dump-sem-ir-end } // --- index_non_literal.carbon library "[[@TEST_NAME]]"; fn F(a: array({}, 3)) -> {} { //@dump-sem-ir-begin return a[{.index = 2}.index]; //@dump-sem-ir-end } // --- fail_out_of_bound_non_literal.carbon library "[[@TEST_NAME]]"; fn F(a: array({}, 3)) -> {} { // CHECK:STDERR: fail_out_of_bound_non_literal.carbon:[[@LINE+4]]:12: error: array index `3` is past the end of type `array({}, 3)` [ArrayIndexOutOfBounds] // CHECK:STDERR: return a[{.index = 3}.index]; // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~ // CHECK:STDERR: return a[{.index = 3}.index]; } // CHECK:STDOUT: --- function_param.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [concrete] // CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] // CHECK:STDOUT: %N: Core.IntLiteral = symbolic_binding N, 0 [symbolic] // CHECK:STDOUT: %i32: type = class_type @Int, @Int(%int_32) [concrete] // CHECK:STDOUT: %int_3.1ba: Core.IntLiteral = int_value 3 [concrete] // CHECK:STDOUT: %array_type: type = array_type %int_3.1ba, %i32 [concrete] // CHECK:STDOUT: %F.type: type = fn_type @F [concrete] // CHECK:STDOUT: %F: %F.type = struct_value () [concrete] // CHECK:STDOUT: %Copy.type: type = facet_type <@Copy> [concrete] // CHECK:STDOUT: %Int.as.Copy.impl.Op.type.824: type = fn_type @Int.as.Copy.impl.Op, @Int.as.Copy.impl(%N) [symbolic] // CHECK:STDOUT: %Int.as.Copy.impl.Op.9b9: %Int.as.Copy.impl.Op.type.824 = struct_value () [symbolic] // CHECK:STDOUT: %Copy.impl_witness.f17: = impl_witness imports.%Copy.impl_witness_table.e76, @Int.as.Copy.impl(%int_32) [concrete] // CHECK:STDOUT: %Int.as.Copy.impl.Op.type.546: type = fn_type @Int.as.Copy.impl.Op, @Int.as.Copy.impl(%int_32) [concrete] // CHECK:STDOUT: %Int.as.Copy.impl.Op.664: %Int.as.Copy.impl.Op.type.546 = struct_value () [concrete] // CHECK:STDOUT: %Copy.facet: %Copy.type = facet_value %i32, (%Copy.impl_witness.f17) [concrete] // CHECK:STDOUT: %Copy.WithSelf.Op.type.081: type = fn_type @Copy.WithSelf.Op, @Copy.WithSelf(%Copy.facet) [concrete] // CHECK:STDOUT: %.8e2: type = fn_type_with_self_type %Copy.WithSelf.Op.type.081, %Copy.facet [concrete] // CHECK:STDOUT: %Int.as.Copy.impl.Op.specific_fn: = specific_function %Int.as.Copy.impl.Op.664, @Int.as.Copy.impl.Op(%int_32) [concrete] // CHECK:STDOUT: %int_1.5b8: Core.IntLiteral = int_value 1 [concrete] // CHECK:STDOUT: %int_2.ecc: Core.IntLiteral = int_value 2 [concrete] // CHECK:STDOUT: %tuple.type.37f: type = tuple_type (Core.IntLiteral, Core.IntLiteral, Core.IntLiteral) [concrete] // CHECK:STDOUT: %tuple.2d5: %tuple.type.37f = tuple_value (%int_1.5b8, %int_2.ecc, %int_3.1ba) [concrete] // CHECK:STDOUT: %int_0: Core.IntLiteral = int_value 0 [concrete] // CHECK:STDOUT: %ImplicitAs.type.e8c: type = facet_type <@ImplicitAs, @ImplicitAs(%i32)> [concrete] // CHECK:STDOUT: %To: Core.IntLiteral = symbolic_binding To, 0 [symbolic] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.4e6: type = fn_type @Core.IntLiteral.as.ImplicitAs.impl.Convert, @Core.IntLiteral.as.ImplicitAs.impl(%To) [symbolic] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.3c2: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.4e6 = struct_value () [symbolic] // CHECK:STDOUT: %ImplicitAs.impl_witness.6bc: = impl_witness imports.%ImplicitAs.impl_witness_table.74f, @Core.IntLiteral.as.ImplicitAs.impl(%int_32) [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.e0d: type = fn_type @Core.IntLiteral.as.ImplicitAs.impl.Convert, @Core.IntLiteral.as.ImplicitAs.impl(%int_32) [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.e0d = struct_value () [concrete] // CHECK:STDOUT: %ImplicitAs.facet: %ImplicitAs.type.e8c = facet_value Core.IntLiteral, (%ImplicitAs.impl_witness.6bc) [concrete] // CHECK:STDOUT: %ImplicitAs.WithSelf.Convert.type.b37: type = fn_type @ImplicitAs.WithSelf.Convert, @ImplicitAs.WithSelf(%i32, %ImplicitAs.facet) [concrete] // CHECK:STDOUT: %.545: type = fn_type_with_self_type %ImplicitAs.WithSelf.Convert.type.b37, %ImplicitAs.facet [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.bound.215: = bound_method %int_1.5b8, %Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5 [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn: = specific_function %Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5, @Core.IntLiteral.as.ImplicitAs.impl.Convert(%int_32) [concrete] // CHECK:STDOUT: %bound_method.38b: = bound_method %int_1.5b8, %Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn [concrete] // CHECK:STDOUT: %int_1.5d2: %i32 = int_value 1 [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.bound.4e5: = bound_method %int_2.ecc, %Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5 [concrete] // CHECK:STDOUT: %bound_method.646: = bound_method %int_2.ecc, %Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn [concrete] // CHECK:STDOUT: %int_2.ef8: %i32 = int_value 2 [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.bound.061: = bound_method %int_3.1ba, %Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5 [concrete] // CHECK:STDOUT: %bound_method.fa7: = bound_method %int_3.1ba, %Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn [concrete] // CHECK:STDOUT: %int_3.822: %i32 = int_value 3 [concrete] // CHECK:STDOUT: %array: %array_type = tuple_value (%int_1.5d2, %int_2.ef8, %int_3.822) [concrete] // CHECK:STDOUT: %Destroy.Op.type: type = fn_type @Destroy.Op [concrete] // CHECK:STDOUT: %Destroy.Op: %Destroy.Op.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core.import_ref.18d: @Int.as.Copy.impl.%Int.as.Copy.impl.Op.type (%Int.as.Copy.impl.Op.type.824) = import_ref Core//prelude/parts/int, loc{{\d+_\d+}}, loaded [symbolic = @Int.as.Copy.impl.%Int.as.Copy.impl.Op (constants.%Int.as.Copy.impl.Op.9b9)] // CHECK:STDOUT: %Copy.impl_witness_table.e76 = impl_witness_table (%Core.import_ref.18d), @Int.as.Copy.impl [concrete] // CHECK:STDOUT: %Core.import_ref.42d: @Core.IntLiteral.as.ImplicitAs.impl.%Core.IntLiteral.as.ImplicitAs.impl.Convert.type (%Core.IntLiteral.as.ImplicitAs.impl.Convert.type.4e6) = import_ref Core//prelude/parts/int, loc{{\d+_\d+}}, loaded [symbolic = @Core.IntLiteral.as.ImplicitAs.impl.%Core.IntLiteral.as.ImplicitAs.impl.Convert (constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.3c2)] // CHECK:STDOUT: %ImplicitAs.impl_witness_table.74f = impl_witness_table (%Core.import_ref.42d), @Core.IntLiteral.as.ImplicitAs.impl [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @F(%arr.param: %array_type, %i.param: %i32) -> out %return.param: %i32 { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %arr.ref: %array_type = name_ref arr, %arr // CHECK:STDOUT: %i.ref: %i32 = name_ref i, %i // CHECK:STDOUT: %.loc4_15.1: ref %array_type = value_as_ref %arr.ref // CHECK:STDOUT: %.loc4_15.2: ref %i32 = array_index %.loc4_15.1, %i.ref // CHECK:STDOUT: %.loc4_15.3: %i32 = acquire_value %.loc4_15.2 // CHECK:STDOUT: %impl.elem0: %.8e2 = impl_witness_access constants.%Copy.impl_witness.f17, element0 [concrete = constants.%Int.as.Copy.impl.Op.664] // CHECK:STDOUT: %bound_method.loc4_15.1: = bound_method %.loc4_15.3, %impl.elem0 // CHECK:STDOUT: %specific_fn: = specific_function %impl.elem0, @Int.as.Copy.impl.Op(constants.%int_32) [concrete = constants.%Int.as.Copy.impl.Op.specific_fn] // CHECK:STDOUT: %bound_method.loc4_15.2: = bound_method %.loc4_15.3, %specific_fn // CHECK:STDOUT: %Int.as.Copy.impl.Op.call: init %i32 = call %bound_method.loc4_15.2(%.loc4_15.3) // CHECK:STDOUT: return %Int.as.Copy.impl.Op.call // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @G() -> out %return.param: %i32 { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %F.ref: %F.type = name_ref F, file.%F.decl [concrete = constants.%F] // CHECK:STDOUT: %int_1.loc10_13: Core.IntLiteral = int_value 1 [concrete = constants.%int_1.5b8] // CHECK:STDOUT: %int_2.loc10_16: Core.IntLiteral = int_value 2 [concrete = constants.%int_2.ecc] // CHECK:STDOUT: %int_3: Core.IntLiteral = int_value 3 [concrete = constants.%int_3.1ba] // CHECK:STDOUT: %.loc10_20.1: %tuple.type.37f = tuple_literal (%int_1.loc10_13, %int_2.loc10_16, %int_3) [concrete = constants.%tuple.2d5] // CHECK:STDOUT: %int_1.loc10_23: Core.IntLiteral = int_value 1 [concrete = constants.%int_1.5b8] // CHECK:STDOUT: %impl.elem0.loc10_20.1: %.545 = impl_witness_access constants.%ImplicitAs.impl_witness.6bc, element0 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5] // CHECK:STDOUT: %bound_method.loc10_20.1: = bound_method %int_1.loc10_13, %impl.elem0.loc10_20.1 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.bound.215] // CHECK:STDOUT: %specific_fn.loc10_20.1: = specific_function %impl.elem0.loc10_20.1, @Core.IntLiteral.as.ImplicitAs.impl.Convert(constants.%int_32) [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn] // CHECK:STDOUT: %bound_method.loc10_20.2: = bound_method %int_1.loc10_13, %specific_fn.loc10_20.1 [concrete = constants.%bound_method.38b] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc10_20.1: init %i32 = call %bound_method.loc10_20.2(%int_1.loc10_13) [concrete = constants.%int_1.5d2] // CHECK:STDOUT: %.loc10_20.2: init %i32 = converted %int_1.loc10_13, %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc10_20.1 [concrete = constants.%int_1.5d2] // CHECK:STDOUT: %.loc10_20.3: ref %array_type = temporary_storage // CHECK:STDOUT: %int_0: Core.IntLiteral = int_value 0 [concrete = constants.%int_0] // CHECK:STDOUT: %.loc10_20.4: ref %i32 = array_index %.loc10_20.3, %int_0 // CHECK:STDOUT: %.loc10_20.5: init %i32 to %.loc10_20.4 = in_place_init %.loc10_20.2 [concrete = constants.%int_1.5d2] // CHECK:STDOUT: %impl.elem0.loc10_20.2: %.545 = impl_witness_access constants.%ImplicitAs.impl_witness.6bc, element0 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5] // CHECK:STDOUT: %bound_method.loc10_20.3: = bound_method %int_2.loc10_16, %impl.elem0.loc10_20.2 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.bound.4e5] // CHECK:STDOUT: %specific_fn.loc10_20.2: = specific_function %impl.elem0.loc10_20.2, @Core.IntLiteral.as.ImplicitAs.impl.Convert(constants.%int_32) [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn] // CHECK:STDOUT: %bound_method.loc10_20.4: = bound_method %int_2.loc10_16, %specific_fn.loc10_20.2 [concrete = constants.%bound_method.646] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc10_20.2: init %i32 = call %bound_method.loc10_20.4(%int_2.loc10_16) [concrete = constants.%int_2.ef8] // CHECK:STDOUT: %.loc10_20.6: init %i32 = converted %int_2.loc10_16, %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc10_20.2 [concrete = constants.%int_2.ef8] // CHECK:STDOUT: %int_1.loc10_20: Core.IntLiteral = int_value 1 [concrete = constants.%int_1.5b8] // CHECK:STDOUT: %.loc10_20.7: ref %i32 = array_index %.loc10_20.3, %int_1.loc10_20 // CHECK:STDOUT: %.loc10_20.8: init %i32 to %.loc10_20.7 = in_place_init %.loc10_20.6 [concrete = constants.%int_2.ef8] // CHECK:STDOUT: %impl.elem0.loc10_20.3: %.545 = impl_witness_access constants.%ImplicitAs.impl_witness.6bc, element0 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5] // CHECK:STDOUT: %bound_method.loc10_20.5: = bound_method %int_3, %impl.elem0.loc10_20.3 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.bound.061] // CHECK:STDOUT: %specific_fn.loc10_20.3: = specific_function %impl.elem0.loc10_20.3, @Core.IntLiteral.as.ImplicitAs.impl.Convert(constants.%int_32) [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn] // CHECK:STDOUT: %bound_method.loc10_20.6: = bound_method %int_3, %specific_fn.loc10_20.3 [concrete = constants.%bound_method.fa7] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc10_20.3: init %i32 = call %bound_method.loc10_20.6(%int_3) [concrete = constants.%int_3.822] // CHECK:STDOUT: %.loc10_20.9: init %i32 = converted %int_3, %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc10_20.3 [concrete = constants.%int_3.822] // CHECK:STDOUT: %int_2.loc10_20: Core.IntLiteral = int_value 2 [concrete = constants.%int_2.ecc] // CHECK:STDOUT: %.loc10_20.10: ref %i32 = array_index %.loc10_20.3, %int_2.loc10_20 // CHECK:STDOUT: %.loc10_20.11: init %i32 to %.loc10_20.10 = in_place_init %.loc10_20.9 [concrete = constants.%int_3.822] // CHECK:STDOUT: %.loc10_20.12: init %array_type to %.loc10_20.3 = array_init (%.loc10_20.5, %.loc10_20.8, %.loc10_20.11) [concrete = constants.%array] // CHECK:STDOUT: %.loc10_20.13: init %array_type = converted %.loc10_20.1, %.loc10_20.12 [concrete = constants.%array] // CHECK:STDOUT: %.loc10_20.14: ref %array_type = temporary %.loc10_20.3, %.loc10_20.13 // CHECK:STDOUT: %.loc10_20.15: %array_type = acquire_value %.loc10_20.14 // CHECK:STDOUT: %impl.elem0.loc10_23: %.545 = impl_witness_access constants.%ImplicitAs.impl_witness.6bc, element0 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5] // CHECK:STDOUT: %bound_method.loc10_23.1: = bound_method %int_1.loc10_23, %impl.elem0.loc10_23 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.bound.215] // CHECK:STDOUT: %specific_fn.loc10_23: = specific_function %impl.elem0.loc10_23, @Core.IntLiteral.as.ImplicitAs.impl.Convert(constants.%int_32) [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn] // CHECK:STDOUT: %bound_method.loc10_23.2: = bound_method %int_1.loc10_23, %specific_fn.loc10_23 [concrete = constants.%bound_method.38b] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc10_23: init %i32 = call %bound_method.loc10_23.2(%int_1.loc10_23) [concrete = constants.%int_1.5d2] // CHECK:STDOUT: %.loc10_23.1: %i32 = value_of_initializer %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc10_23 [concrete = constants.%int_1.5d2] // CHECK:STDOUT: %.loc10_23.2: %i32 = converted %int_1.loc10_23, %.loc10_23.1 [concrete = constants.%int_1.5d2] // CHECK:STDOUT: %F.call: init %i32 = call %F.ref(%.loc10_20.15, %.loc10_23.2) // CHECK:STDOUT: %Destroy.Op.bound: = bound_method %.loc10_20.14, constants.%Destroy.Op // CHECK:STDOUT: %Destroy.Op.call: init %empty_tuple.type = call %Destroy.Op.bound(%.loc10_20.14) // CHECK:STDOUT: return %F.call // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @Destroy.Op(%self.param: ref %array_type) = "no_op"; // CHECK:STDOUT: // CHECK:STDOUT: --- index_non_literal.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %empty_struct: %empty_struct_type = struct_value () [concrete] // CHECK:STDOUT: %int_3: Core.IntLiteral = int_value 3 [concrete] // CHECK:STDOUT: %array_type: type = array_type %int_3, %empty_struct_type [concrete] // CHECK:STDOUT: %int_2.ecc: Core.IntLiteral = int_value 2 [concrete] // CHECK:STDOUT: %struct_type.index: type = struct_type {.index: Core.IntLiteral} [concrete] // CHECK:STDOUT: %struct: %struct_type.index = struct_value (%int_2.ecc) [concrete] // CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [concrete] // CHECK:STDOUT: %i32: type = class_type @Int, @Int(%int_32) [concrete] // CHECK:STDOUT: %ImplicitAs.type.e8c: type = facet_type <@ImplicitAs, @ImplicitAs(%i32)> [concrete] // CHECK:STDOUT: %To: Core.IntLiteral = symbolic_binding To, 0 [symbolic] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.4e6: type = fn_type @Core.IntLiteral.as.ImplicitAs.impl.Convert, @Core.IntLiteral.as.ImplicitAs.impl(%To) [symbolic] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.3c2: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.4e6 = struct_value () [symbolic] // CHECK:STDOUT: %ImplicitAs.impl_witness.6bc: = impl_witness imports.%ImplicitAs.impl_witness_table.74f, @Core.IntLiteral.as.ImplicitAs.impl(%int_32) [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.e0d: type = fn_type @Core.IntLiteral.as.ImplicitAs.impl.Convert, @Core.IntLiteral.as.ImplicitAs.impl(%int_32) [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.e0d = struct_value () [concrete] // CHECK:STDOUT: %ImplicitAs.facet: %ImplicitAs.type.e8c = facet_value Core.IntLiteral, (%ImplicitAs.impl_witness.6bc) [concrete] // CHECK:STDOUT: %ImplicitAs.WithSelf.Convert.type.b37: type = fn_type @ImplicitAs.WithSelf.Convert, @ImplicitAs.WithSelf(%i32, %ImplicitAs.facet) [concrete] // CHECK:STDOUT: %.545: type = fn_type_with_self_type %ImplicitAs.WithSelf.Convert.type.b37, %ImplicitAs.facet [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.bound: = bound_method %int_2.ecc, %Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5 [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn: = specific_function %Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5, @Core.IntLiteral.as.ImplicitAs.impl.Convert(%int_32) [concrete] // CHECK:STDOUT: %bound_method: = bound_method %int_2.ecc, %Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn [concrete] // CHECK:STDOUT: %int_2.ef8: %i32 = int_value 2 [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core.import_ref.42d: @Core.IntLiteral.as.ImplicitAs.impl.%Core.IntLiteral.as.ImplicitAs.impl.Convert.type (%Core.IntLiteral.as.ImplicitAs.impl.Convert.type.4e6) = import_ref Core//prelude/parts/int, loc{{\d+_\d+}}, loaded [symbolic = @Core.IntLiteral.as.ImplicitAs.impl.%Core.IntLiteral.as.ImplicitAs.impl.Convert (constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.3c2)] // CHECK:STDOUT: %ImplicitAs.impl_witness_table.74f = impl_witness_table (%Core.import_ref.42d), @Core.IntLiteral.as.ImplicitAs.impl [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @F(%a.param: %array_type) -> out %return.param: %empty_struct_type { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %a.ref: %array_type = name_ref a, %a // CHECK:STDOUT: %int_2: Core.IntLiteral = int_value 2 [concrete = constants.%int_2.ecc] // CHECK:STDOUT: %.loc6_23.1: %struct_type.index = struct_literal (%int_2) [concrete = constants.%struct] // CHECK:STDOUT: %struct: %struct_type.index = struct_value (%int_2) [concrete = constants.%struct] // CHECK:STDOUT: %.loc6_23.2: %struct_type.index = converted %.loc6_23.1, %struct [concrete = constants.%struct] // CHECK:STDOUT: %.loc6_24.1: Core.IntLiteral = struct_access %.loc6_23.2, element0 [concrete = constants.%int_2.ecc] // CHECK:STDOUT: %impl.elem0: %.545 = impl_witness_access constants.%ImplicitAs.impl_witness.6bc, element0 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5] // CHECK:STDOUT: %bound_method.loc6_24.1: = bound_method %.loc6_24.1, %impl.elem0 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.bound] // CHECK:STDOUT: %specific_fn: = specific_function %impl.elem0, @Core.IntLiteral.as.ImplicitAs.impl.Convert(constants.%int_32) [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn] // CHECK:STDOUT: %bound_method.loc6_24.2: = bound_method %.loc6_24.1, %specific_fn [concrete = constants.%bound_method] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.call: init %i32 = call %bound_method.loc6_24.2(%.loc6_24.1) [concrete = constants.%int_2.ef8] // CHECK:STDOUT: %.loc6_24.2: %i32 = value_of_initializer %Core.IntLiteral.as.ImplicitAs.impl.Convert.call [concrete = constants.%int_2.ef8] // CHECK:STDOUT: %.loc6_24.3: %i32 = converted %.loc6_24.1, %.loc6_24.2 [concrete = constants.%int_2.ef8] // CHECK:STDOUT: %.loc6_30.1: ref %array_type = value_as_ref %a.ref // CHECK:STDOUT: %.loc6_30.2: ref %empty_struct_type = array_index %.loc6_30.1, %.loc6_24.3 // CHECK:STDOUT: %empty_struct: %empty_struct_type = struct_value () [concrete = constants.%empty_struct] // CHECK:STDOUT: %.loc6_30.3: %empty_struct_type = converted %.loc6_30.2, %empty_struct [concrete = constants.%empty_struct] // CHECK:STDOUT: %.loc6_30.4: init %empty_struct_type = struct_init () [concrete = constants.%empty_struct] // CHECK:STDOUT: %.loc6_31: init %empty_struct_type = converted %.loc6_30.3, %.loc6_30.4 [concrete = constants.%empty_struct] // CHECK:STDOUT: return %.loc6_31 // CHECK:STDOUT: } // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/array/init_dependent_bound.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/int.carbon // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/array/init_dependent_bound.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/array/init_dependent_bound.carbon // --- generic_empty.carbon library "[[@TEST_NAME]]"; fn G(T:! type) { // We can initialize this without knowing T. //@dump-sem-ir-begin var unused arr: array(T, 0) = (); //@dump-sem-ir-end } class C {} fn H() { G(C); } // --- fail_init_dependent_bound.carbon library "[[@TEST_NAME]]"; fn F(N:! i32) { // CHECK:STDERR: fail_init_dependent_bound.carbon:[[@LINE+4]]:35: error: cannot initialize array with dependent bound from a list of initializers [ArrayInitDependentBound] // CHECK:STDERR: var unused arr: array(i32, N) = (1, 2, 3); // CHECK:STDERR: ^~~~~~~~~ // CHECK:STDERR: var unused arr: array(i32, N) = (1, 2, 3); } // --- fail_todo_init_template_dependent_bound.carbon library "[[@TEST_NAME]]"; // TODO: This should be valid. fn G(template N:! i32) { //@dump-sem-ir-begin // CHECK:STDERR: fail_todo_init_template_dependent_bound.carbon:[[@LINE+4]]:19: error: cannot evaluate type expression [TypeExprEvaluationFailure] // CHECK:STDERR: var unused arr: array(i32, N) = (1, 2, 3); // CHECK:STDERR: ^~~~~~~~~~~~~ // CHECK:STDERR: var unused arr: array(i32, N) = (1, 2, 3); //@dump-sem-ir-end } fn H() { G(3); } // CHECK:STDOUT: --- generic_empty.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %T: type = symbolic_binding T, 0 [symbolic] // CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] // CHECK:STDOUT: %int_0: Core.IntLiteral = int_value 0 [concrete] // CHECK:STDOUT: %array_type.1b3: type = array_type %int_0, %T [symbolic] // CHECK:STDOUT: %require_complete: = require_complete_type %array_type.1b3 [symbolic] // CHECK:STDOUT: %pattern_type.bd6: type = pattern_type %array_type.1b3 [symbolic] // CHECK:STDOUT: %empty_tuple: %empty_tuple.type = tuple_value () [concrete] // CHECK:STDOUT: %array.ca4: %array_type.1b3 = tuple_value () [symbolic] // CHECK:STDOUT: %Destroy.type: type = facet_type <@Destroy> [concrete] // CHECK:STDOUT: %Destroy.lookup_impl_witness: = lookup_impl_witness %array_type.1b3, @Destroy [symbolic] // CHECK:STDOUT: %Destroy.facet.15e: %Destroy.type = facet_value %array_type.1b3, (%Destroy.lookup_impl_witness) [symbolic] // CHECK:STDOUT: %Destroy.WithSelf.Op.type.427: type = fn_type @Destroy.WithSelf.Op, @Destroy.WithSelf(%Destroy.facet.15e) [symbolic] // CHECK:STDOUT: %.6d6: type = fn_type_with_self_type %Destroy.WithSelf.Op.type.427, %Destroy.facet.15e [symbolic] // CHECK:STDOUT: %impl.elem0: %.6d6 = impl_witness_access %Destroy.lookup_impl_witness, element0 [symbolic] // CHECK:STDOUT: %specific_impl_fn: = specific_impl_function %impl.elem0, @Destroy.WithSelf.Op(%Destroy.facet.15e) [symbolic] // CHECK:STDOUT: %C: type = class_type @C [concrete] // CHECK:STDOUT: %array_type.70a: type = array_type %int_0, %C [concrete] // CHECK:STDOUT: %complete_type.95b: = complete_type_witness %array_type.70a [concrete] // CHECK:STDOUT: %pattern_type.f2c: type = pattern_type %array_type.70a [concrete] // CHECK:STDOUT: %array.40f: %array_type.70a = tuple_value () [concrete] // CHECK:STDOUT: %Destroy.Op.type: type = fn_type @Destroy.Op [concrete] // CHECK:STDOUT: %Destroy.Op: %Destroy.Op.type = struct_value () [concrete] // CHECK:STDOUT: %custom_witness.8d7: = custom_witness (%Destroy.Op), @Destroy [concrete] // CHECK:STDOUT: %Destroy.facet.00d: %Destroy.type = facet_value %array_type.70a, (%custom_witness.8d7) [concrete] // CHECK:STDOUT: %Destroy.WithSelf.Op.type.ed3: type = fn_type @Destroy.WithSelf.Op, @Destroy.WithSelf(%Destroy.facet.00d) [concrete] // CHECK:STDOUT: %.23f: type = fn_type_with_self_type %Destroy.WithSelf.Op.type.ed3, %Destroy.facet.00d [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic fn @G(%T.loc4_6.2: type) { // CHECK:STDOUT: // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %array_type.loc7_29.2: type = array_type constants.%int_0, %T.loc4_6.1 [symbolic = %array_type.loc7_29.2 (constants.%array_type.1b3)] // CHECK:STDOUT: %require_complete: = require_complete_type %array_type.loc7_29.2 [symbolic = %require_complete (constants.%require_complete)] // CHECK:STDOUT: %pattern_type: type = pattern_type %array_type.loc7_29.2 [symbolic = %pattern_type (constants.%pattern_type.bd6)] // CHECK:STDOUT: %array: @G.%array_type.loc7_29.2 (%array_type.1b3) = tuple_value () [symbolic = %array (constants.%array.ca4)] // CHECK:STDOUT: %Destroy.lookup_impl_witness: = lookup_impl_witness %array_type.loc7_29.2, @Destroy [symbolic = %Destroy.lookup_impl_witness (constants.%Destroy.lookup_impl_witness)] // CHECK:STDOUT: %Destroy.facet: %Destroy.type = facet_value %array_type.loc7_29.2, (%Destroy.lookup_impl_witness) [symbolic = %Destroy.facet (constants.%Destroy.facet.15e)] // CHECK:STDOUT: %Destroy.WithSelf.Op.type: type = fn_type @Destroy.WithSelf.Op, @Destroy.WithSelf(%Destroy.facet) [symbolic = %Destroy.WithSelf.Op.type (constants.%Destroy.WithSelf.Op.type.427)] // CHECK:STDOUT: %.loc7_3.2: type = fn_type_with_self_type %Destroy.WithSelf.Op.type, %Destroy.facet [symbolic = %.loc7_3.2 (constants.%.6d6)] // CHECK:STDOUT: %impl.elem0.loc7_3.2: @G.%.loc7_3.2 (%.6d6) = impl_witness_access %Destroy.lookup_impl_witness, element0 [symbolic = %impl.elem0.loc7_3.2 (constants.%impl.elem0)] // CHECK:STDOUT: %specific_impl_fn.loc7_3.2: = specific_impl_function %impl.elem0.loc7_3.2, @Destroy.WithSelf.Op(%Destroy.facet) [symbolic = %specific_impl_fn.loc7_3.2 (constants.%specific_impl_fn)] // CHECK:STDOUT: // CHECK:STDOUT: fn() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %arr.patt: @G.%pattern_type (%pattern_type.bd6) = ref_binding_pattern arr [concrete] // CHECK:STDOUT: %arr.var_patt: @G.%pattern_type (%pattern_type.bd6) = var_pattern %arr.patt [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %arr.var: ref @G.%array_type.loc7_29.2 (%array_type.1b3) = var %arr.var_patt // CHECK:STDOUT: %.loc7_34.1: %empty_tuple.type = tuple_literal () [concrete = constants.%empty_tuple] // CHECK:STDOUT: %.loc7_34.2: init @G.%array_type.loc7_29.2 (%array_type.1b3) to %arr.var = array_init () [symbolic = %array (constants.%array.ca4)] // CHECK:STDOUT: %.loc7_3.1: init @G.%array_type.loc7_29.2 (%array_type.1b3) = converted %.loc7_34.1, %.loc7_34.2 [symbolic = %array (constants.%array.ca4)] // CHECK:STDOUT: assign %arr.var, %.loc7_3.1 // CHECK:STDOUT: %.loc7_29: type = splice_block %array_type.loc7_29.1 [symbolic = %array_type.loc7_29.2 (constants.%array_type.1b3)] { // CHECK:STDOUT: %T.ref: type = name_ref T, %T.loc4_6.2 [symbolic = %T.loc4_6.1 (constants.%T)] // CHECK:STDOUT: %int_0: Core.IntLiteral = int_value 0 [concrete = constants.%int_0] // CHECK:STDOUT: %array_type.loc7_29.1: type = array_type %int_0, %T.ref [symbolic = %array_type.loc7_29.2 (constants.%array_type.1b3)] // CHECK:STDOUT: } // CHECK:STDOUT: %arr: ref @G.%array_type.loc7_29.2 (%array_type.1b3) = ref_binding arr, %arr.var // CHECK:STDOUT: %impl.elem0.loc7_3.1: @G.%.loc7_3.2 (%.6d6) = impl_witness_access constants.%Destroy.lookup_impl_witness, element0 [symbolic = %impl.elem0.loc7_3.2 (constants.%impl.elem0)] // CHECK:STDOUT: %bound_method.loc7_3.1: = bound_method %arr.var, %impl.elem0.loc7_3.1 // CHECK:STDOUT: %specific_impl_fn.loc7_3.1: = specific_impl_function %impl.elem0.loc7_3.1, @Destroy.WithSelf.Op(constants.%Destroy.facet.15e) [symbolic = %specific_impl_fn.loc7_3.2 (constants.%specific_impl_fn)] // CHECK:STDOUT: %bound_method.loc7_3.2: = bound_method %arr.var, %specific_impl_fn.loc7_3.1 // CHECK:STDOUT: %Destroy.WithSelf.Op.call: init %empty_tuple.type = call %bound_method.loc7_3.2(%arr.var) // CHECK:STDOUT: // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @Destroy.Op(%self.param: ref %array_type.70a) = "no_op"; // CHECK:STDOUT: // CHECK:STDOUT: specific @G(constants.%T) { // CHECK:STDOUT: %T.loc4_6.1 => constants.%T // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @G(constants.%C) { // CHECK:STDOUT: %T.loc4_6.1 => constants.%C // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %array_type.loc7_29.2 => constants.%array_type.70a // CHECK:STDOUT: %require_complete => constants.%complete_type.95b // CHECK:STDOUT: %pattern_type => constants.%pattern_type.f2c // CHECK:STDOUT: %array => constants.%array.40f // CHECK:STDOUT: %Destroy.lookup_impl_witness => constants.%custom_witness.8d7 // CHECK:STDOUT: %Destroy.facet => constants.%Destroy.facet.00d // CHECK:STDOUT: %Destroy.WithSelf.Op.type => constants.%Destroy.WithSelf.Op.type.ed3 // CHECK:STDOUT: %.loc7_3.2 => constants.%.23f // CHECK:STDOUT: %impl.elem0.loc7_3.2 => constants.%Destroy.Op // CHECK:STDOUT: %specific_impl_fn.loc7_3.2 => constants.%Destroy.Op // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- fail_todo_init_template_dependent_bound.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [concrete] // CHECK:STDOUT: %i32: type = class_type @Int, @Int(%int_32) [concrete] // CHECK:STDOUT: %N.5de: %i32 = symbolic_binding N, 0, template [template] // CHECK:STDOUT: %int_1: Core.IntLiteral = int_value 1 [concrete] // CHECK:STDOUT: %int_2: Core.IntLiteral = int_value 2 [concrete] // CHECK:STDOUT: %int_3.1ba: Core.IntLiteral = int_value 3 [concrete] // CHECK:STDOUT: %tuple.type: type = tuple_type (Core.IntLiteral, Core.IntLiteral, Core.IntLiteral) [concrete] // CHECK:STDOUT: %tuple: %tuple.type = tuple_value (%int_1, %int_2, %int_3.1ba) [concrete] // CHECK:STDOUT: %ImplicitAs.type.139: type = facet_type <@ImplicitAs, @ImplicitAs(Core.IntLiteral)> [concrete] // CHECK:STDOUT: %From: Core.IntLiteral = symbolic_binding From, 0 [symbolic] // CHECK:STDOUT: %Int.as.ImplicitAs.impl.Convert.type.2ed: type = fn_type @Int.as.ImplicitAs.impl.Convert, @Int.as.ImplicitAs.impl(%From) [symbolic] // CHECK:STDOUT: %Int.as.ImplicitAs.impl.Convert.d29: %Int.as.ImplicitAs.impl.Convert.type.2ed = struct_value () [symbolic] // CHECK:STDOUT: %int_3.822: %i32 = int_value 3 [concrete] // CHECK:STDOUT: %ImplicitAs.impl_witness.640: = impl_witness imports.%ImplicitAs.impl_witness_table.ea2, @Int.as.ImplicitAs.impl(%int_32) [concrete] // CHECK:STDOUT: %Int.as.ImplicitAs.impl.Convert.type.240: type = fn_type @Int.as.ImplicitAs.impl.Convert, @Int.as.ImplicitAs.impl(%int_32) [concrete] // CHECK:STDOUT: %Int.as.ImplicitAs.impl.Convert.dd4: %Int.as.ImplicitAs.impl.Convert.type.240 = struct_value () [concrete] // CHECK:STDOUT: %ImplicitAs.facet.290: %ImplicitAs.type.139 = facet_value %i32, (%ImplicitAs.impl_witness.640) [concrete] // CHECK:STDOUT: %ImplicitAs.WithSelf.Convert.type.462: type = fn_type @ImplicitAs.WithSelf.Convert, @ImplicitAs.WithSelf(Core.IntLiteral, %ImplicitAs.facet.290) [concrete] // CHECK:STDOUT: %.0a7: type = fn_type_with_self_type %ImplicitAs.WithSelf.Convert.type.462, %ImplicitAs.facet.290 [concrete] // CHECK:STDOUT: %Int.as.ImplicitAs.impl.Convert.bound: = bound_method %int_3.822, %Int.as.ImplicitAs.impl.Convert.dd4 [concrete] // CHECK:STDOUT: %Int.as.ImplicitAs.impl.Convert.specific_fn: = specific_function %Int.as.ImplicitAs.impl.Convert.dd4, @Int.as.ImplicitAs.impl.Convert(%int_32) [concrete] // CHECK:STDOUT: %bound_method.17a: = bound_method %int_3.822, %Int.as.ImplicitAs.impl.Convert.specific_fn [concrete] // CHECK:STDOUT: %inst.splice_block: = inst_value [concrete] { // CHECK:STDOUT: %.3e5: Core.IntLiteral = splice_block %.502 [concrete = %int_3.1ba] { // CHECK:STDOUT: %impl.elem0: %.0a7 = impl_witness_access %ImplicitAs.impl_witness.640, element0 [concrete = %Int.as.ImplicitAs.impl.Convert.dd4] // CHECK:STDOUT: %bound_method.372: = bound_method %int_3.822, %impl.elem0 [concrete = %Int.as.ImplicitAs.impl.Convert.bound] // CHECK:STDOUT: %specific_fn: = specific_function %impl.elem0, @Int.as.ImplicitAs.impl.Convert(%int_32) [concrete = %Int.as.ImplicitAs.impl.Convert.specific_fn] // CHECK:STDOUT: %bound_method.28e: = bound_method %int_3.822, %specific_fn [concrete = %bound_method.17a] // CHECK:STDOUT: %Int.as.ImplicitAs.impl.Convert.call: init Core.IntLiteral = call %bound_method.28e(%int_3.822) [concrete = %int_3.1ba] // CHECK:STDOUT: %.14d: Core.IntLiteral = value_of_initializer %Int.as.ImplicitAs.impl.Convert.call [concrete = %int_3.1ba] // CHECK:STDOUT: %.502: Core.IntLiteral = converted %int_3.822, %.14d [concrete = %int_3.1ba] // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core.import_ref.0bc: @Int.as.ImplicitAs.impl.%Int.as.ImplicitAs.impl.Convert.type (%Int.as.ImplicitAs.impl.Convert.type.2ed) = import_ref Core//prelude/parts/int, loc{{\d+_\d+}}, loaded [symbolic = @Int.as.ImplicitAs.impl.%Int.as.ImplicitAs.impl.Convert (constants.%Int.as.ImplicitAs.impl.Convert.d29)] // CHECK:STDOUT: %ImplicitAs.impl_witness_table.ea2 = impl_witness_table (%Core.import_ref.0bc), @Int.as.ImplicitAs.impl [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic fn @G(%N.loc5_15.2: %i32) { // CHECK:STDOUT: // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %.loc11_30.2: = convert_to_value_action %N.ref, Core.IntLiteral [template] // CHECK:STDOUT: // CHECK:STDOUT: fn() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %arr.patt: = ref_binding_pattern arr [concrete] // CHECK:STDOUT: %arr.var_patt: = var_pattern %arr.patt [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %arr.var: ref = var %arr.var_patt [concrete = ] // CHECK:STDOUT: %int_1: Core.IntLiteral = int_value 1 [concrete = constants.%int_1] // CHECK:STDOUT: %int_2: Core.IntLiteral = int_value 2 [concrete = constants.%int_2] // CHECK:STDOUT: %int_3: Core.IntLiteral = int_value 3 [concrete = constants.%int_3.1ba] // CHECK:STDOUT: %.loc11_43: %tuple.type = tuple_literal (%int_1, %int_2, %int_3) [concrete = constants.%tuple] // CHECK:STDOUT: assign %arr.var, // CHECK:STDOUT: // CHECK:STDOUT: %arr: ref = ref_binding arr, [concrete = ] // CHECK:STDOUT: // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @G(constants.%N.5de) { // CHECK:STDOUT: %N.loc5_15.1 => constants.%N.5de // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @G(constants.%int_3.822) { // CHECK:STDOUT: %N.loc5_15.1 => constants.%int_3.822 // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %.loc11_30.2 => constants.%inst.splice_block // CHECK:STDOUT: } // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/as/adapter_conversion.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/int.carbon // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/as/adapter_conversion.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/as/adapter_conversion.carbon // --- adapt_class.carbon library "[[@TEST_NAME]]"; class A { var x: i32; var y: i32; fn Make() -> A { return {.x = 1, .y = 2}; } } class B { adapt A; } var a_ref: A = {.x = 1, .y = 2}; let a_val: A = a_ref; // An `as` conversion to an adapter type preserves the expression category. //@dump-sem-ir-begin let b_val: B = a_val as B; let b_ptr: B* = &(a_ref as B); var b_factory: B = A.Make() as B; //@dump-sem-ir-end // --- adapt_i32.carbon library "[[@TEST_NAME]]"; class A { adapt i32; } //@dump-sem-ir-begin let a: A = (1 as i32) as A; let n: i32 = a as i32; //@dump-sem-ir-end // --- multi_level_adapt.carbon library "[[@TEST_NAME]]"; class A { adapt {}; } class B { adapt A; } class C { adapt B; } class D { adapt C; } //@dump-sem-ir-begin let d: D = {} as D; //@dump-sem-ir-end // --- init_class_value.carbon library "[[@TEST_NAME]]"; class A { var x: i32; var y: i32; } class B { adapt A; } //@dump-sem-ir-begin let b_value: B = ({.x = 1, .y = 2} as A) as B; //@dump-sem-ir-end // --- init_class_variable.carbon library "[[@TEST_NAME]]"; class A { var x: i32; var y: i32; } class B { adapt A; } var b_init: B = ({.x = 1, .y = 2} as A) as B; // --- init_tuple_value.carbon library "[[@TEST_NAME]]"; class Noncopyable { // TODO: Ensure this remains non-copyable once we have rules for class copyability. } class A { adapt ({}, Noncopyable); } fn F(a: A) { //@dump-sem-ir-begin let unused a_value: A = (a as ({}, Noncopyable)) as A; //@dump-sem-ir-end } // --- fail_init_tuple_variable.carbon library "[[@TEST_NAME]]"; class Noncopyable { // TODO: Ensure this remains non-copyable once we have rules for class copyability. } class A { adapt ({}, Noncopyable); } fn F(a: A) { // TODO: Here, we treat `a as (i32, Noncopyable)` as a value expression, not an // initializing expression, so `(...) as A` is a value expression too, requiring // a copy to perform initialization. It's not clear whether that is the right // behavior. // CHECK:STDERR: fail_init_tuple_variable.carbon:[[@LINE+10]]:3: error: cannot copy value of type `Noncopyable` [CopyOfUncopyableType] // CHECK:STDERR: var unused a_init: A = (a as ({}, Noncopyable)) as A; // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: fail_init_tuple_variable.carbon:[[@LINE+7]]:3: note: type `Noncopyable` does not implement interface `Core.Copy` [MissingImplInMemberAccessInContext] // CHECK:STDERR: var unused a_init: A = (a as ({}, Noncopyable)) as A; // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: fail_init_tuple_variable.carbon:[[@LINE+4]]:26: note: in copy of `A` [InCopy] // CHECK:STDERR: var unused a_init: A = (a as ({}, Noncopyable)) as A; // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: var unused a_init: A = (a as ({}, Noncopyable)) as A; } // --- fail_adapt_init_from_struct.carbon library "[[@TEST_NAME]]"; class A { var x: (); } class B { adapt A; } // We do not try to implicitly convert from the first operand of `as` to the // adapted type of the second operand. // CHECK:STDERR: fail_adapt_init_from_struct.carbon:[[@LINE+7]]:12: error: cannot convert expression of type `{.x: ()}` to `B` with `as` [ConversionFailure] // CHECK:STDERR: var b: B = {.x = ()} as B; // CHECK:STDERR: ^~~~~~~~~~~~~~ // CHECK:STDERR: fail_adapt_init_from_struct.carbon:[[@LINE+4]]:12: note: type `{.x: ()}` does not implement interface `Core.As(B)` [MissingImplInMemberAccessInContext] // CHECK:STDERR: var b: B = {.x = ()} as B; // CHECK:STDERR: ^~~~~~~~~~~~~~ // CHECK:STDERR: var b: B = {.x = ()} as B; // CHECK:STDOUT: --- adapt_class.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %A: type = class_type @A [concrete] // CHECK:STDOUT: %A.Make.type: type = fn_type @A.Make [concrete] // CHECK:STDOUT: %A.Make: %A.Make.type = struct_value () [concrete] // CHECK:STDOUT: %B: type = class_type @B [concrete] // CHECK:STDOUT: %pattern_type.1f4: type = pattern_type %B [concrete] // CHECK:STDOUT: %ptr.27c: type = ptr_type %B [concrete] // CHECK:STDOUT: %pattern_type.191: type = pattern_type %ptr.27c [concrete] // CHECK:STDOUT: %a_ref.var: ref %B = var file.%a_ref.var_patt [concrete] // CHECK:STDOUT: %addr: %ptr.27c = addr_of %a_ref.var [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %b_val.patt: %pattern_type.1f4 = value_binding_pattern b_val [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %B.ref.loc22: type = name_ref B, %B.decl [concrete = constants.%B] // CHECK:STDOUT: %b_val: %B = value_binding b_val, @__global_init.%.loc22_22.2 // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %b_ptr.patt: %pattern_type.191 = value_binding_pattern b_ptr [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %.loc23: type = splice_block %ptr [concrete = constants.%ptr.27c] { // CHECK:STDOUT: %B.ref.loc23: type = name_ref B, %B.decl [concrete = constants.%B] // CHECK:STDOUT: %ptr: type = ptr_type %B.ref.loc23 [concrete = constants.%ptr.27c] // CHECK:STDOUT: } // CHECK:STDOUT: %b_ptr: %ptr.27c = value_binding b_ptr, @__global_init.%addr // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %b_factory.patt: %pattern_type.1f4 = ref_binding_pattern b_factory [concrete] // CHECK:STDOUT: %b_factory.var_patt: %pattern_type.1f4 = var_pattern %b_factory.patt [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %b_factory.var: ref %B = var %b_factory.var_patt [concrete] // CHECK:STDOUT: %B.ref.loc25: type = name_ref B, %B.decl [concrete = constants.%B] // CHECK:STDOUT: %b_factory: ref %B = ref_binding b_factory, %b_factory.var [concrete = %b_factory.var] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @__global_init() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: // CHECK:STDOUT: %a_val.ref: %A = name_ref a_val, file.%a_val // CHECK:STDOUT: %B.ref.loc22: type = name_ref B, file.%B.decl [concrete = constants.%B] // CHECK:STDOUT: %.loc22_22.1: %B = as_compatible %a_val.ref // CHECK:STDOUT: %.loc22_22.2: %B = converted %a_val.ref, %.loc22_22.1 // CHECK:STDOUT: %a_ref.ref.loc23: ref %A = name_ref a_ref, file.%a_ref [concrete = file.%a_ref.var] // CHECK:STDOUT: %B.ref.loc23: type = name_ref B, file.%B.decl [concrete = constants.%B] // CHECK:STDOUT: %.loc23_25.1: ref %B = as_compatible %a_ref.ref.loc23 [concrete = constants.%a_ref.var] // CHECK:STDOUT: %.loc23_25.2: ref %B = converted %a_ref.ref.loc23, %.loc23_25.1 [concrete = constants.%a_ref.var] // CHECK:STDOUT: %addr: %ptr.27c = addr_of %.loc23_25.2 [concrete = constants.%addr] // CHECK:STDOUT: %A.ref: type = name_ref A, file.%A.decl [concrete = constants.%A] // CHECK:STDOUT: %Make.ref: %A.Make.type = name_ref Make, @A.%A.Make.decl [concrete = constants.%A.Make] // CHECK:STDOUT: %.loc25_1: ref %B = splice_block file.%b_factory.var [concrete = file.%b_factory.var] {} // CHECK:STDOUT: %A.Make.call: init %A to %.loc25_1 = call %Make.ref() // CHECK:STDOUT: %B.ref.loc25: type = name_ref B, file.%B.decl [concrete = constants.%B] // CHECK:STDOUT: %.loc25_29.1: init %B = as_compatible %A.Make.call // CHECK:STDOUT: %.loc25_29.2: init %B = converted %A.Make.call, %.loc25_29.1 // CHECK:STDOUT: assign file.%b_factory.var, %.loc25_29.2 // CHECK:STDOUT: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- adapt_i32.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %A: type = class_type @A [concrete] // CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [concrete] // CHECK:STDOUT: %i32: type = class_type @Int, @Int(%int_32) [concrete] // CHECK:STDOUT: %pattern_type.1ab: type = pattern_type %A [concrete] // CHECK:STDOUT: %int_1.5b8: Core.IntLiteral = int_value 1 [concrete] // CHECK:STDOUT: %As.type.047: type = facet_type <@As, @As(%i32)> [concrete] // CHECK:STDOUT: %To: Core.IntLiteral = symbolic_binding To, 0 [symbolic] // CHECK:STDOUT: %Core.IntLiteral.as.As.impl.Convert.type.09e: type = fn_type @Core.IntLiteral.as.As.impl.Convert, @Core.IntLiteral.as.As.impl(%To) [symbolic] // CHECK:STDOUT: %Core.IntLiteral.as.As.impl.Convert.dbe: %Core.IntLiteral.as.As.impl.Convert.type.09e = struct_value () [symbolic] // CHECK:STDOUT: %As.impl_witness.ab6: = impl_witness imports.%As.impl_witness_table.9fc, @Core.IntLiteral.as.As.impl(%int_32) [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.As.impl.Convert.type.8ec: type = fn_type @Core.IntLiteral.as.As.impl.Convert, @Core.IntLiteral.as.As.impl(%int_32) [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.As.impl.Convert.29b: %Core.IntLiteral.as.As.impl.Convert.type.8ec = struct_value () [concrete] // CHECK:STDOUT: %As.facet: %As.type.047 = facet_value Core.IntLiteral, (%As.impl_witness.ab6) [concrete] // CHECK:STDOUT: %As.WithSelf.Convert.type.e5b: type = fn_type @As.WithSelf.Convert, @As.WithSelf(%i32, %As.facet) [concrete] // CHECK:STDOUT: %.9ed: type = fn_type_with_self_type %As.WithSelf.Convert.type.e5b, %As.facet [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.As.impl.Convert.bound: = bound_method %int_1.5b8, %Core.IntLiteral.as.As.impl.Convert.29b [concrete] // CHECK:STDOUT: %pattern_type.7ce: type = pattern_type %i32 [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.As.impl.Convert.specific_fn: = specific_function %Core.IntLiteral.as.As.impl.Convert.29b, @Core.IntLiteral.as.As.impl.Convert(%int_32) [concrete] // CHECK:STDOUT: %bound_method: = bound_method %int_1.5b8, %Core.IntLiteral.as.As.impl.Convert.specific_fn [concrete] // CHECK:STDOUT: %int_1.5d2: %i32 = int_value 1 [concrete] // CHECK:STDOUT: %int_1.360: %A = int_value 1 [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core.import_ref.ca0: @Core.IntLiteral.as.As.impl.%Core.IntLiteral.as.As.impl.Convert.type (%Core.IntLiteral.as.As.impl.Convert.type.09e) = import_ref Core//prelude/parts/int, loc{{\d+_\d+}}, loaded [symbolic = @Core.IntLiteral.as.As.impl.%Core.IntLiteral.as.As.impl.Convert (constants.%Core.IntLiteral.as.As.impl.Convert.dbe)] // CHECK:STDOUT: %As.impl_witness_table.9fc = impl_witness_table (%Core.import_ref.ca0), @Core.IntLiteral.as.As.impl [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %a.patt: %pattern_type.1ab = value_binding_pattern a [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %A.ref: type = name_ref A, %A.decl [concrete = constants.%A] // CHECK:STDOUT: %a: %A = value_binding a, @__global_init.%.loc9_23.2 // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %n.patt: %pattern_type.7ce = value_binding_pattern n [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %i32: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %n: %i32 = value_binding n, @__global_init.%.loc10_16.2 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @__global_init() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %int_1: Core.IntLiteral = int_value 1 [concrete = constants.%int_1.5b8] // CHECK:STDOUT: %i32.loc9: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %impl.elem0: %.9ed = impl_witness_access constants.%As.impl_witness.ab6, element0 [concrete = constants.%Core.IntLiteral.as.As.impl.Convert.29b] // CHECK:STDOUT: %bound_method.loc9_15.1: = bound_method %int_1, %impl.elem0 [concrete = constants.%Core.IntLiteral.as.As.impl.Convert.bound] // CHECK:STDOUT: %specific_fn: = specific_function %impl.elem0, @Core.IntLiteral.as.As.impl.Convert(constants.%int_32) [concrete = constants.%Core.IntLiteral.as.As.impl.Convert.specific_fn] // CHECK:STDOUT: %bound_method.loc9_15.2: = bound_method %int_1, %specific_fn [concrete = constants.%bound_method] // CHECK:STDOUT: %Core.IntLiteral.as.As.impl.Convert.call: init %i32 = call %bound_method.loc9_15.2(%int_1) [concrete = constants.%int_1.5d2] // CHECK:STDOUT: %.loc9_15.1: %i32 = value_of_initializer %Core.IntLiteral.as.As.impl.Convert.call [concrete = constants.%int_1.5d2] // CHECK:STDOUT: %.loc9_15.2: %i32 = converted %int_1, %.loc9_15.1 [concrete = constants.%int_1.5d2] // CHECK:STDOUT: %A.ref: type = name_ref A, file.%A.decl [concrete = constants.%A] // CHECK:STDOUT: %.loc9_23.1: %A = as_compatible %.loc9_15.2 [concrete = constants.%int_1.360] // CHECK:STDOUT: %.loc9_23.2: %A = converted %.loc9_15.2, %.loc9_23.1 [concrete = constants.%int_1.360] // CHECK:STDOUT: %a.ref: %A = name_ref a, file.%a // CHECK:STDOUT: %i32.loc10: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %.loc10_16.1: %i32 = as_compatible %a.ref // CHECK:STDOUT: %.loc10_16.2: %i32 = converted %a.ref, %.loc10_16.1 // CHECK:STDOUT: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- multi_level_adapt.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %empty_struct: %empty_struct_type = struct_value () [concrete] // CHECK:STDOUT: %D: type = class_type @D [concrete] // CHECK:STDOUT: %pattern_type: type = pattern_type %D [concrete] // CHECK:STDOUT: %D.val: %D = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %d.patt: %pattern_type = value_binding_pattern d [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %D.ref: type = name_ref D, %D.decl [concrete = constants.%D] // CHECK:STDOUT: %d: %D = value_binding d, @__global_init.%.loc10_15.2 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @__global_init() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %.loc10_13: %empty_struct_type = struct_literal () [concrete = constants.%empty_struct] // CHECK:STDOUT: %D.ref: type = name_ref D, file.%D.decl [concrete = constants.%D] // CHECK:STDOUT: %empty_struct: %empty_struct_type = struct_value () [concrete = constants.%empty_struct] // CHECK:STDOUT: %.loc10_15.1: %D = as_compatible %empty_struct [concrete = constants.%D.val] // CHECK:STDOUT: %.loc10_15.2: %D = converted %.loc10_13, %.loc10_15.1 [concrete = constants.%D.val] // CHECK:STDOUT: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- init_class_value.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %A: type = class_type @A [concrete] // CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [concrete] // CHECK:STDOUT: %i32: type = class_type @Int, @Int(%int_32) [concrete] // CHECK:STDOUT: %B: type = class_type @B [concrete] // CHECK:STDOUT: %pattern_type.1f4: type = pattern_type %B [concrete] // CHECK:STDOUT: %int_1.5b8: Core.IntLiteral = int_value 1 [concrete] // CHECK:STDOUT: %int_2.ecc: Core.IntLiteral = int_value 2 [concrete] // CHECK:STDOUT: %struct_type.x.y.4cf: type = struct_type {.x: Core.IntLiteral, .y: Core.IntLiteral} [concrete] // CHECK:STDOUT: %struct: %struct_type.x.y.4cf = struct_value (%int_1.5b8, %int_2.ecc) [concrete] // CHECK:STDOUT: %ImplicitAs.type.e8c: type = facet_type <@ImplicitAs, @ImplicitAs(%i32)> [concrete] // CHECK:STDOUT: %To: Core.IntLiteral = symbolic_binding To, 0 [symbolic] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.4e6: type = fn_type @Core.IntLiteral.as.ImplicitAs.impl.Convert, @Core.IntLiteral.as.ImplicitAs.impl(%To) [symbolic] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.3c2: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.4e6 = struct_value () [symbolic] // CHECK:STDOUT: %ImplicitAs.impl_witness.6bc: = impl_witness imports.%ImplicitAs.impl_witness_table.74f, @Core.IntLiteral.as.ImplicitAs.impl(%int_32) [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.e0d: type = fn_type @Core.IntLiteral.as.ImplicitAs.impl.Convert, @Core.IntLiteral.as.ImplicitAs.impl(%int_32) [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.e0d = struct_value () [concrete] // CHECK:STDOUT: %ImplicitAs.facet: %ImplicitAs.type.e8c = facet_value Core.IntLiteral, (%ImplicitAs.impl_witness.6bc) [concrete] // CHECK:STDOUT: %ImplicitAs.WithSelf.Convert.type.b37: type = fn_type @ImplicitAs.WithSelf.Convert, @ImplicitAs.WithSelf(%i32, %ImplicitAs.facet) [concrete] // CHECK:STDOUT: %.545: type = fn_type_with_self_type %ImplicitAs.WithSelf.Convert.type.b37, %ImplicitAs.facet [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.bound.215: = bound_method %int_1.5b8, %Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5 [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn: = specific_function %Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5, @Core.IntLiteral.as.ImplicitAs.impl.Convert(%int_32) [concrete] // CHECK:STDOUT: %bound_method.38b: = bound_method %int_1.5b8, %Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn [concrete] // CHECK:STDOUT: %int_1.5d2: %i32 = int_value 1 [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.bound.4e5: = bound_method %int_2.ecc, %Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5 [concrete] // CHECK:STDOUT: %bound_method.646: = bound_method %int_2.ecc, %Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn [concrete] // CHECK:STDOUT: %int_2.ef8: %i32 = int_value 2 [concrete] // CHECK:STDOUT: %A.val: %A = struct_value (%int_1.5d2, %int_2.ef8) [concrete] // CHECK:STDOUT: %B.val: %B = struct_value (%int_1.5d2, %int_2.ef8) [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core.import_ref.42d: @Core.IntLiteral.as.ImplicitAs.impl.%Core.IntLiteral.as.ImplicitAs.impl.Convert.type (%Core.IntLiteral.as.ImplicitAs.impl.Convert.type.4e6) = import_ref Core//prelude/parts/int, loc{{\d+_\d+}}, loaded [symbolic = @Core.IntLiteral.as.ImplicitAs.impl.%Core.IntLiteral.as.ImplicitAs.impl.Convert (constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.3c2)] // CHECK:STDOUT: %ImplicitAs.impl_witness_table.74f = impl_witness_table (%Core.import_ref.42d), @Core.IntLiteral.as.ImplicitAs.impl [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %b_value.patt: %pattern_type.1f4 = value_binding_pattern b_value [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %B.ref: type = name_ref B, %B.decl [concrete = constants.%B] // CHECK:STDOUT: %.loc14_42.1: ref %B = temporary @__global_init.%.loc14_34.3, @__global_init.%.loc14_42.2 // CHECK:STDOUT: %.loc14_42.2: %B = acquire_value %.loc14_42.1 // CHECK:STDOUT: %b_value: %B = value_binding b_value, %.loc14_42.2 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @__global_init() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %int_1: Core.IntLiteral = int_value 1 [concrete = constants.%int_1.5b8] // CHECK:STDOUT: %int_2: Core.IntLiteral = int_value 2 [concrete = constants.%int_2.ecc] // CHECK:STDOUT: %.loc14_34.1: %struct_type.x.y.4cf = struct_literal (%int_1, %int_2) [concrete = constants.%struct] // CHECK:STDOUT: %A.ref: type = name_ref A, file.%A.decl [concrete = constants.%A] // CHECK:STDOUT: %impl.elem0.loc14_34.1: %.545 = impl_witness_access constants.%ImplicitAs.impl_witness.6bc, element0 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5] // CHECK:STDOUT: %bound_method.loc14_34.1: = bound_method %int_1, %impl.elem0.loc14_34.1 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.bound.215] // CHECK:STDOUT: %specific_fn.loc14_34.1: = specific_function %impl.elem0.loc14_34.1, @Core.IntLiteral.as.ImplicitAs.impl.Convert(constants.%int_32) [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn] // CHECK:STDOUT: %bound_method.loc14_34.2: = bound_method %int_1, %specific_fn.loc14_34.1 [concrete = constants.%bound_method.38b] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc14_34.1: init %i32 = call %bound_method.loc14_34.2(%int_1) [concrete = constants.%int_1.5d2] // CHECK:STDOUT: %.loc14_34.2: init %i32 = converted %int_1, %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc14_34.1 [concrete = constants.%int_1.5d2] // CHECK:STDOUT: %.loc14_34.3: ref %A = temporary_storage // CHECK:STDOUT: %.loc14_34.4: ref %i32 = class_element_access %.loc14_34.3, element0 // CHECK:STDOUT: %.loc14_34.5: init %i32 to %.loc14_34.4 = in_place_init %.loc14_34.2 [concrete = constants.%int_1.5d2] // CHECK:STDOUT: %impl.elem0.loc14_34.2: %.545 = impl_witness_access constants.%ImplicitAs.impl_witness.6bc, element0 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5] // CHECK:STDOUT: %bound_method.loc14_34.3: = bound_method %int_2, %impl.elem0.loc14_34.2 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.bound.4e5] // CHECK:STDOUT: %specific_fn.loc14_34.2: = specific_function %impl.elem0.loc14_34.2, @Core.IntLiteral.as.ImplicitAs.impl.Convert(constants.%int_32) [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn] // CHECK:STDOUT: %bound_method.loc14_34.4: = bound_method %int_2, %specific_fn.loc14_34.2 [concrete = constants.%bound_method.646] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc14_34.2: init %i32 = call %bound_method.loc14_34.4(%int_2) [concrete = constants.%int_2.ef8] // CHECK:STDOUT: %.loc14_34.6: init %i32 = converted %int_2, %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc14_34.2 [concrete = constants.%int_2.ef8] // CHECK:STDOUT: %.loc14_34.7: ref %i32 = class_element_access %.loc14_34.3, element1 // CHECK:STDOUT: %.loc14_34.8: init %i32 to %.loc14_34.7 = in_place_init %.loc14_34.6 [concrete = constants.%int_2.ef8] // CHECK:STDOUT: %.loc14_34.9: init %A to %.loc14_34.3 = class_init (%.loc14_34.5, %.loc14_34.8) [concrete = constants.%A.val] // CHECK:STDOUT: %.loc14_36: init %A = converted %.loc14_34.1, %.loc14_34.9 [concrete = constants.%A.val] // CHECK:STDOUT: %B.ref: type = name_ref B, file.%B.decl [concrete = constants.%B] // CHECK:STDOUT: %.loc14_42.1: init %B = as_compatible %.loc14_36 [concrete = constants.%B.val] // CHECK:STDOUT: %.loc14_42.2: init %B = converted %.loc14_36, %.loc14_42.1 [concrete = constants.%B.val] // CHECK:STDOUT: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- init_tuple_value.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %Noncopyable: type = class_type @Noncopyable [concrete] // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %A: type = class_type @A [concrete] // CHECK:STDOUT: %empty_struct: %empty_struct_type = struct_value () [concrete] // CHECK:STDOUT: %tuple.type.c8c: type = tuple_type (%empty_struct_type, type) [concrete] // CHECK:STDOUT: %tuple: %tuple.type.c8c = tuple_value (%empty_struct, %Noncopyable) [concrete] // CHECK:STDOUT: %tuple.type.037: type = tuple_type (%empty_struct_type, %Noncopyable) [concrete] // CHECK:STDOUT: %pattern_type: type = pattern_type %A [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @F(%a.param: %A) { // CHECK:STDOUT: !entry: // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %a_value.patt: %pattern_type = value_binding_pattern a_value [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %a.ref: %A = name_ref a, %a // CHECK:STDOUT: %.loc14_35: %empty_struct_type = struct_literal () [concrete = constants.%empty_struct] // CHECK:STDOUT: %Noncopyable.ref: type = name_ref Noncopyable, file.%Noncopyable.decl [concrete = constants.%Noncopyable] // CHECK:STDOUT: %.loc14_49.1: %tuple.type.c8c = tuple_literal (%.loc14_35, %Noncopyable.ref) [concrete = constants.%tuple] // CHECK:STDOUT: %.loc14_49.2: type = converted constants.%empty_struct, constants.%empty_struct_type [concrete = constants.%empty_struct_type] // CHECK:STDOUT: %.loc14_49.3: type = converted %.loc14_49.1, constants.%tuple.type.037 [concrete = constants.%tuple.type.037] // CHECK:STDOUT: %.loc14_30.1: %tuple.type.037 = as_compatible %a.ref // CHECK:STDOUT: %.loc14_30.2: %tuple.type.037 = converted %a.ref, %.loc14_30.1 // CHECK:STDOUT: %A.ref.loc14_55: type = name_ref A, file.%A.decl [concrete = constants.%A] // CHECK:STDOUT: %.loc14_52.1: %A = as_compatible %.loc14_30.2 // CHECK:STDOUT: %.loc14_52.2: %A = converted %.loc14_30.2, %.loc14_52.1 // CHECK:STDOUT: %A.ref.loc14_23: type = name_ref A, file.%A.decl [concrete = constants.%A] // CHECK:STDOUT: %a_value: %A = value_binding a_value, %.loc14_52.2 // CHECK:STDOUT: // CHECK:STDOUT: } // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/as/basics.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/convert.carbon // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/as/basics.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/as/basics.carbon // --- simple_as.carbon library "[[@TEST_NAME]]"; fn Main() -> {.x: (), .y: ()} { //@dump-sem-ir-begin return {.y = (), .x = ()} as {.x: (), .y: ()}; //@dump-sem-ir-end } // --- as_type.carbon library "[[@TEST_NAME]]"; //@dump-sem-ir-begin let t: type = ({}, {}) as type; //@dump-sem-ir-end // --- as_tuple.carbon library "[[@TEST_NAME]]"; class X { // ... } fn Make() -> X; fn Let() { // This should create value bindings for both tuple elements. //@dump-sem-ir-begin let unused a: (X, X) = (Make(), Make()) as (X, X); //@dump-sem-ir-end } fn Var() { // This should initialize both tuple elements in place. //@dump-sem-ir-begin var unused b: (X, X) = (Make(), Make()) as (X, X); //@dump-sem-ir-end } // --- identity.carbon library "[[@TEST_NAME]]"; class X { // ... } fn Value(n: X) { //@dump-sem-ir-begin let unused m: X = n as X; //@dump-sem-ir-end } fn Reference(p: X*) { //@dump-sem-ir-begin let unused q: X* = &(*p as X); //@dump-sem-ir-end } fn Make() -> X; fn Initializing() { //@dump-sem-ir-begin var unused x: X = (Make() as X); //@dump-sem-ir-end } // --- overloaded.carbon class X { var x: (); } class Y { var y: (); } impl Y as Core.As(X) { fn Convert[self: Y]() -> X { return {.x = self.y}; } } impl X as Core.As(Y) { fn Convert[self: X]() -> Y { return {.y = self.x}; } } //@dump-sem-ir-begin let n: Y = ({.x = ()} as X) as Y; //@dump-sem-ir-end // --- fail_no_conversion.carbon library "[[@TEST_NAME]]"; // CHECK:STDERR: fail_no_conversion.carbon:[[@LINE+7]]:13: error: cannot convert expression of type `Core.IntLiteral` to `{}` with `as` [ConversionFailure] // CHECK:STDERR: let a: {} = 1 as {}; // CHECK:STDERR: ^~~~~~~ // CHECK:STDERR: fail_no_conversion.carbon:[[@LINE+4]]:13: note: type `Core.IntLiteral` does not implement interface `Core.As({})` [MissingImplInMemberAccessInContext] // CHECK:STDERR: let a: {} = 1 as {}; // CHECK:STDERR: ^~~~~~~ // CHECK:STDERR: let a: {} = 1 as {}; // --- fail_not_type.carbon library "[[@TEST_NAME]]"; // CHECK:STDERR: fail_not_type.carbon:[[@LINE+7]]:32: error: cannot implicitly convert non-type value of type `{.x: ()}` to `type` [ConversionFailureNonTypeToFacet] // CHECK:STDERR: let n: {.x: ()} = {.x = ()} as {.x = ()}; // CHECK:STDERR: ^~~~~~~~~ // CHECK:STDERR: fail_not_type.carbon:[[@LINE+4]]:32: note: type `{.x: ()}` does not implement interface `Core.ImplicitAs(type)` [MissingImplInMemberAccessInContext] // CHECK:STDERR: let n: {.x: ()} = {.x = ()} as {.x = ()}; // CHECK:STDERR: ^~~~~~~~~ // CHECK:STDERR: let n: {.x: ()} = {.x = ()} as {.x = ()}; // CHECK:STDOUT: --- simple_as.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] // CHECK:STDOUT: %empty_tuple: %empty_tuple.type = tuple_value () [concrete] // CHECK:STDOUT: %struct_type.x.y: type = struct_type {.x: %empty_tuple.type, .y: %empty_tuple.type} [concrete] // CHECK:STDOUT: %struct_type.y.x: type = struct_type {.y: %empty_tuple.type, .x: %empty_tuple.type} [concrete] // CHECK:STDOUT: %struct.f76: %struct_type.y.x = struct_value (%empty_tuple, %empty_tuple) [concrete] // CHECK:STDOUT: %struct.005: %struct_type.x.y = struct_value (%empty_tuple, %empty_tuple) [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @Main() -> out %return.param: %struct_type.x.y { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %.loc6_17: %empty_tuple.type = tuple_literal () [concrete = constants.%empty_tuple] // CHECK:STDOUT: %.loc6_26: %empty_tuple.type = tuple_literal () [concrete = constants.%empty_tuple] // CHECK:STDOUT: %.loc6_27.1: %struct_type.y.x = struct_literal (%.loc6_17, %.loc6_26) [concrete = constants.%struct.f76] // CHECK:STDOUT: %.loc6_38.1: %empty_tuple.type = tuple_literal () [concrete = constants.%empty_tuple] // CHECK:STDOUT: %.loc6_38.2: type = converted %.loc6_38.1, constants.%empty_tuple.type [concrete = constants.%empty_tuple.type] // CHECK:STDOUT: %.loc6_46.1: %empty_tuple.type = tuple_literal () [concrete = constants.%empty_tuple] // CHECK:STDOUT: %.loc6_46.2: type = converted %.loc6_46.1, constants.%empty_tuple.type [concrete = constants.%empty_tuple.type] // CHECK:STDOUT: %struct_type.x.y.loc6: type = struct_type {.x: %empty_tuple.type, .y: %empty_tuple.type} [concrete = constants.%struct_type.x.y] // CHECK:STDOUT: %empty_tuple.loc6_26: %empty_tuple.type = tuple_value () [concrete = constants.%empty_tuple] // CHECK:STDOUT: %.loc6_27.2: %empty_tuple.type = converted %.loc6_26, %empty_tuple.loc6_26 [concrete = constants.%empty_tuple] // CHECK:STDOUT: %empty_tuple.loc6_17: %empty_tuple.type = tuple_value () [concrete = constants.%empty_tuple] // CHECK:STDOUT: %.loc6_27.3: %empty_tuple.type = converted %.loc6_17, %empty_tuple.loc6_17 [concrete = constants.%empty_tuple] // CHECK:STDOUT: %struct: %struct_type.x.y = struct_value (%.loc6_27.2, %.loc6_27.3) [concrete = constants.%struct.005] // CHECK:STDOUT: %.loc6_29.1: %struct_type.x.y = converted %.loc6_27.1, %struct [concrete = constants.%struct.005] // CHECK:STDOUT: %.loc6_29.2: %empty_tuple.type = struct_access %.loc6_29.1, element0 [concrete = constants.%empty_tuple] // CHECK:STDOUT: %.loc6_29.3: ref %empty_tuple.type = struct_access %return.param, element0 // CHECK:STDOUT: %.loc6_29.4: init %empty_tuple.type = tuple_init () [concrete = constants.%empty_tuple] // CHECK:STDOUT: %.loc6_29.5: init %empty_tuple.type = converted %.loc6_29.2, %.loc6_29.4 [concrete = constants.%empty_tuple] // CHECK:STDOUT: %.loc6_29.6: %empty_tuple.type = struct_access %.loc6_29.1, element1 [concrete = constants.%empty_tuple] // CHECK:STDOUT: %.loc6_29.7: ref %empty_tuple.type = struct_access %return.param, element1 // CHECK:STDOUT: %.loc6_29.8: init %empty_tuple.type = tuple_init () [concrete = constants.%empty_tuple] // CHECK:STDOUT: %.loc6_29.9: init %empty_tuple.type = converted %.loc6_29.6, %.loc6_29.8 [concrete = constants.%empty_tuple] // CHECK:STDOUT: %.loc6_29.10: init %struct_type.x.y to %return.param = struct_init (%.loc6_29.5, %.loc6_29.9) [concrete = constants.%struct.005] // CHECK:STDOUT: %.loc6_48: init %struct_type.x.y = converted %.loc6_29.1, %.loc6_29.10 [concrete = constants.%struct.005] // CHECK:STDOUT: return %.loc6_48 to %return.param // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- as_type.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %pattern_type: type = pattern_type type [concrete] // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %empty_struct: %empty_struct_type = struct_value () [concrete] // CHECK:STDOUT: %tuple.type.b6b: type = tuple_type (%empty_struct_type, %empty_struct_type) [concrete] // CHECK:STDOUT: %tuple: %tuple.type.b6b = tuple_value (%empty_struct, %empty_struct) [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %t.patt: %pattern_type = value_binding_pattern t [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %.loc5: type = type_literal type [concrete = type] // CHECK:STDOUT: %t: type = value_binding t, @__global_init.%.loc5_24.3 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @__global_init() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %.loc5_17: %empty_struct_type = struct_literal () [concrete = constants.%empty_struct] // CHECK:STDOUT: %.loc5_21: %empty_struct_type = struct_literal () [concrete = constants.%empty_struct] // CHECK:STDOUT: %.loc5_22: %tuple.type.b6b = tuple_literal (%.loc5_17, %.loc5_21) [concrete = constants.%tuple] // CHECK:STDOUT: %.loc5_27: type = type_literal type [concrete = type] // CHECK:STDOUT: %.loc5_24.1: type = converted constants.%empty_struct, constants.%empty_struct_type [concrete = constants.%empty_struct_type] // CHECK:STDOUT: %.loc5_24.2: type = converted constants.%empty_struct, constants.%empty_struct_type [concrete = constants.%empty_struct_type] // CHECK:STDOUT: %.loc5_24.3: type = converted %.loc5_22, constants.%tuple.type.b6b [concrete = constants.%tuple.type.b6b] // CHECK:STDOUT: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- as_tuple.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %X: type = class_type @X [concrete] // CHECK:STDOUT: %Make.type: type = fn_type @Make [concrete] // CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] // CHECK:STDOUT: %Make: %Make.type = struct_value () [concrete] // CHECK:STDOUT: %tuple.type.24b: type = tuple_type (type, type) [concrete] // CHECK:STDOUT: %tuple: %tuple.type.24b = tuple_value (%X, %X) [concrete] // CHECK:STDOUT: %tuple.type.2de: type = tuple_type (%X, %X) [concrete] // CHECK:STDOUT: %pattern_type.e9d: type = pattern_type %tuple.type.2de [concrete] // CHECK:STDOUT: %Destroy.Op.type.bae255.1: type = fn_type @Destroy.Op.loc13 [concrete] // CHECK:STDOUT: %Destroy.Op.651ba6.1: %Destroy.Op.type.bae255.1 = struct_value () [concrete] // CHECK:STDOUT: %Destroy.Op.type.bae255.2: type = fn_type @Destroy.Op.loc20 [concrete] // CHECK:STDOUT: %Destroy.Op.651ba6.2: %Destroy.Op.type.bae255.2 = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @Let() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %a.patt: %pattern_type.e9d = value_binding_pattern a [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %Make.ref.loc13_27: %Make.type = name_ref Make, file.%Make.decl [concrete = constants.%Make] // CHECK:STDOUT: %.loc13_32.1: ref %X = temporary_storage // CHECK:STDOUT: %Make.call.loc13_32: init %X to %.loc13_32.1 = call %Make.ref.loc13_27() // CHECK:STDOUT: %Make.ref.loc13_35: %Make.type = name_ref Make, file.%Make.decl [concrete = constants.%Make] // CHECK:STDOUT: %.loc13_40.1: ref %X = temporary_storage // CHECK:STDOUT: %Make.call.loc13_40: init %X to %.loc13_40.1 = call %Make.ref.loc13_35() // CHECK:STDOUT: %.loc13_41.1: %tuple.type.2de = tuple_literal (%Make.call.loc13_32, %Make.call.loc13_40) // CHECK:STDOUT: %X.ref.loc13_47: type = name_ref X, file.%X.decl [concrete = constants.%X] // CHECK:STDOUT: %X.ref.loc13_50: type = name_ref X, file.%X.decl [concrete = constants.%X] // CHECK:STDOUT: %.loc13_51.1: %tuple.type.24b = tuple_literal (%X.ref.loc13_47, %X.ref.loc13_50) [concrete = constants.%tuple] // CHECK:STDOUT: %.loc13_51.2: type = converted %.loc13_51.1, constants.%tuple.type.2de [concrete = constants.%tuple.type.2de] // CHECK:STDOUT: %.loc13_22.1: type = splice_block %.loc13_22.3 [concrete = constants.%tuple.type.2de] { // CHECK:STDOUT: %X.ref.loc13_18: type = name_ref X, file.%X.decl [concrete = constants.%X] // CHECK:STDOUT: %X.ref.loc13_21: type = name_ref X, file.%X.decl [concrete = constants.%X] // CHECK:STDOUT: %.loc13_22.2: %tuple.type.24b = tuple_literal (%X.ref.loc13_18, %X.ref.loc13_21) [concrete = constants.%tuple] // CHECK:STDOUT: %.loc13_22.3: type = converted %.loc13_22.2, constants.%tuple.type.2de [concrete = constants.%tuple.type.2de] // CHECK:STDOUT: } // CHECK:STDOUT: %.loc13_32.2: ref %X = temporary %.loc13_32.1, %Make.call.loc13_32 // CHECK:STDOUT: %.loc13_32.3: %X = acquire_value %.loc13_32.2 // CHECK:STDOUT: %.loc13_40.2: ref %X = temporary %.loc13_40.1, %Make.call.loc13_40 // CHECK:STDOUT: %.loc13_40.3: %X = acquire_value %.loc13_40.2 // CHECK:STDOUT: %tuple: %tuple.type.2de = tuple_value (%.loc13_32.3, %.loc13_40.3) // CHECK:STDOUT: %.loc13_41.2: %tuple.type.2de = converted %.loc13_41.1, %tuple // CHECK:STDOUT: %a: %tuple.type.2de = value_binding a, %.loc13_41.2 // CHECK:STDOUT: %Destroy.Op.bound.loc13_40: = bound_method %.loc13_40.2, constants.%Destroy.Op.651ba6.1 // CHECK:STDOUT: %Destroy.Op.call.loc13_40: init %empty_tuple.type = call %Destroy.Op.bound.loc13_40(%.loc13_40.2) // CHECK:STDOUT: %Destroy.Op.bound.loc13_32: = bound_method %.loc13_32.2, constants.%Destroy.Op.651ba6.1 // CHECK:STDOUT: %Destroy.Op.call.loc13_32: init %empty_tuple.type = call %Destroy.Op.bound.loc13_32(%.loc13_32.2) // CHECK:STDOUT: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @Destroy.Op.loc13(%self.param: ref %X) = "no_op"; // CHECK:STDOUT: // CHECK:STDOUT: fn @Var() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %b.patt: %pattern_type.e9d = ref_binding_pattern b [concrete] // CHECK:STDOUT: %b.var_patt: %pattern_type.e9d = var_pattern %b.patt [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %b.var: ref %tuple.type.2de = var %b.var_patt // CHECK:STDOUT: %Make.ref.loc20_27: %Make.type = name_ref Make, file.%Make.decl [concrete = constants.%Make] // CHECK:STDOUT: %tuple.elem0: ref %X = tuple_access %b.var, element0 // CHECK:STDOUT: %Make.call.loc20_32: init %X to %tuple.elem0 = call %Make.ref.loc20_27() // CHECK:STDOUT: %Make.ref.loc20_35: %Make.type = name_ref Make, file.%Make.decl [concrete = constants.%Make] // CHECK:STDOUT: %tuple.elem1: ref %X = tuple_access %b.var, element1 // CHECK:STDOUT: %Make.call.loc20_40: init %X to %tuple.elem1 = call %Make.ref.loc20_35() // CHECK:STDOUT: %.loc20_41.1: %tuple.type.2de = tuple_literal (%Make.call.loc20_32, %Make.call.loc20_40) // CHECK:STDOUT: %X.ref.loc20_47: type = name_ref X, file.%X.decl [concrete = constants.%X] // CHECK:STDOUT: %X.ref.loc20_50: type = name_ref X, file.%X.decl [concrete = constants.%X] // CHECK:STDOUT: %.loc20_51.1: %tuple.type.24b = tuple_literal (%X.ref.loc20_47, %X.ref.loc20_50) [concrete = constants.%tuple] // CHECK:STDOUT: %.loc20_51.2: type = converted %.loc20_51.1, constants.%tuple.type.2de [concrete = constants.%tuple.type.2de] // CHECK:STDOUT: %.loc20_41.2: init %tuple.type.2de to %b.var = tuple_init (%Make.call.loc20_32, %Make.call.loc20_40) // CHECK:STDOUT: %.loc20_3: init %tuple.type.2de = converted %.loc20_41.1, %.loc20_41.2 // CHECK:STDOUT: assign %b.var, %.loc20_3 // CHECK:STDOUT: %.loc20_22.1: type = splice_block %.loc20_22.3 [concrete = constants.%tuple.type.2de] { // CHECK:STDOUT: %X.ref.loc20_18: type = name_ref X, file.%X.decl [concrete = constants.%X] // CHECK:STDOUT: %X.ref.loc20_21: type = name_ref X, file.%X.decl [concrete = constants.%X] // CHECK:STDOUT: %.loc20_22.2: %tuple.type.24b = tuple_literal (%X.ref.loc20_18, %X.ref.loc20_21) [concrete = constants.%tuple] // CHECK:STDOUT: %.loc20_22.3: type = converted %.loc20_22.2, constants.%tuple.type.2de [concrete = constants.%tuple.type.2de] // CHECK:STDOUT: } // CHECK:STDOUT: %b: ref %tuple.type.2de = ref_binding b, %b.var // CHECK:STDOUT: %Destroy.Op.bound: = bound_method %b.var, constants.%Destroy.Op.651ba6.2 // CHECK:STDOUT: %Destroy.Op.call: init %empty_tuple.type = call %Destroy.Op.bound(%b.var) // CHECK:STDOUT: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @Destroy.Op.loc20(%self.param: ref %tuple.type.2de) = "no_op"; // CHECK:STDOUT: // CHECK:STDOUT: --- identity.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %X: type = class_type @X [concrete] // CHECK:STDOUT: %pattern_type.05f: type = pattern_type %X [concrete] // CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] // CHECK:STDOUT: %ptr.2a9: type = ptr_type %X [concrete] // CHECK:STDOUT: %pattern_type.37f: type = pattern_type %ptr.2a9 [concrete] // CHECK:STDOUT: %Make.type: type = fn_type @Make [concrete] // CHECK:STDOUT: %Make: %Make.type = struct_value () [concrete] // CHECK:STDOUT: %Destroy.Op.type: type = fn_type @Destroy.Op [concrete] // CHECK:STDOUT: %Destroy.Op: %Destroy.Op.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @Value(%n.param: %X) { // CHECK:STDOUT: !entry: // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %m.patt: %pattern_type.05f = value_binding_pattern m [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %n.ref: %X = name_ref n, %n // CHECK:STDOUT: %X.ref.loc10_26: type = name_ref X, file.%X.decl [concrete = constants.%X] // CHECK:STDOUT: %X.ref.loc10_17: type = name_ref X, file.%X.decl [concrete = constants.%X] // CHECK:STDOUT: %m: %X = value_binding m, %n.ref // CHECK:STDOUT: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @Reference(%p.param: %ptr.2a9) { // CHECK:STDOUT: !entry: // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %q.patt: %pattern_type.37f = value_binding_pattern q [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %p.ref: %ptr.2a9 = name_ref p, %p // CHECK:STDOUT: %.loc16_24: ref %X = deref %p.ref // CHECK:STDOUT: %X.ref.loc16_30: type = name_ref X, file.%X.decl [concrete = constants.%X] // CHECK:STDOUT: %addr: %ptr.2a9 = addr_of %.loc16_24 // CHECK:STDOUT: %.loc16_18: type = splice_block %ptr.loc16 [concrete = constants.%ptr.2a9] { // CHECK:STDOUT: %X.ref.loc16_17: type = name_ref X, file.%X.decl [concrete = constants.%X] // CHECK:STDOUT: %ptr.loc16: type = ptr_type %X.ref.loc16_17 [concrete = constants.%ptr.2a9] // CHECK:STDOUT: } // CHECK:STDOUT: %q: %ptr.2a9 = value_binding q, %addr // CHECK:STDOUT: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @Initializing() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %x.patt: %pattern_type.05f = ref_binding_pattern x [concrete] // CHECK:STDOUT: %x.var_patt: %pattern_type.05f = var_pattern %x.patt [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %x.var: ref %X = var %x.var_patt // CHECK:STDOUT: %Make.ref: %Make.type = name_ref Make, file.%Make.decl [concrete = constants.%Make] // CHECK:STDOUT: %.loc24: ref %X = splice_block %x.var {} // CHECK:STDOUT: %Make.call: init %X to %.loc24 = call %Make.ref() // CHECK:STDOUT: %X.ref.loc24_32: type = name_ref X, file.%X.decl [concrete = constants.%X] // CHECK:STDOUT: assign %x.var, %Make.call // CHECK:STDOUT: %X.ref.loc24_17: type = name_ref X, file.%X.decl [concrete = constants.%X] // CHECK:STDOUT: %x: ref %X = ref_binding x, %x.var // CHECK:STDOUT: %Destroy.Op.bound: = bound_method %x.var, constants.%Destroy.Op // CHECK:STDOUT: %Destroy.Op.call: init %empty_tuple.type = call %Destroy.Op.bound(%x.var) // CHECK:STDOUT: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @Destroy.Op(%self.param: ref %X) = "no_op"; // CHECK:STDOUT: // CHECK:STDOUT: --- overloaded.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %X: type = class_type @X [concrete] // CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] // CHECK:STDOUT: %empty_tuple: %empty_tuple.type = tuple_value () [concrete] // CHECK:STDOUT: %struct_type.x: type = struct_type {.x: %empty_tuple.type} [concrete] // CHECK:STDOUT: %Y: type = class_type @Y [concrete] // CHECK:STDOUT: %pattern_type.77c: type = pattern_type %Y [concrete] // CHECK:STDOUT: %struct.948: %struct_type.x = struct_value (%empty_tuple) [concrete] // CHECK:STDOUT: %X.val: %X = struct_value (%empty_tuple) [concrete] // CHECK:STDOUT: %As.type.d1e: type = facet_type <@As, @As(%Y)> [concrete] // CHECK:STDOUT: %As.impl_witness.e92: = impl_witness @X.as.As.impl.%As.impl_witness_table [concrete] // CHECK:STDOUT: %X.as.As.impl.Convert.type: type = fn_type @X.as.As.impl.Convert [concrete] // CHECK:STDOUT: %X.as.As.impl.Convert: %X.as.As.impl.Convert.type = struct_value () [concrete] // CHECK:STDOUT: %As.facet.c03: %As.type.d1e = facet_value %X, (%As.impl_witness.e92) [concrete] // CHECK:STDOUT: %As.WithSelf.Convert.type.edc: type = fn_type @As.WithSelf.Convert, @As.WithSelf(%Y, %As.facet.c03) [concrete] // CHECK:STDOUT: %.84a: type = fn_type_with_self_type %As.WithSelf.Convert.type.edc, %As.facet.c03 [concrete] // CHECK:STDOUT: %X.as.As.impl.Convert.bound: = bound_method %X.val, %X.as.As.impl.Convert [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %n.patt: %pattern_type.77c = value_binding_pattern n [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %Y.ref: type = name_ref Y, %Y.decl [concrete = constants.%Y] // CHECK:STDOUT: %.loc19_29.1: ref %Y = temporary @__global_init.%.loc19_29.1, @__global_init.%.loc19_29.2 // CHECK:STDOUT: %.loc19_29.2: %Y = acquire_value %.loc19_29.1 // CHECK:STDOUT: %n: %Y = value_binding n, %.loc19_29.2 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @__global_init() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %.loc19_20.1: %empty_tuple.type = tuple_literal () [concrete = constants.%empty_tuple] // CHECK:STDOUT: %.loc19_21.1: %struct_type.x = struct_literal (%.loc19_20.1) [concrete = constants.%struct.948] // CHECK:STDOUT: %X.ref: type = name_ref X, file.%X.decl [concrete = constants.%X] // CHECK:STDOUT: %.loc19_21.2: ref %X = temporary_storage // CHECK:STDOUT: %.loc19_21.3: ref %empty_tuple.type = class_element_access %.loc19_21.2, element0 // CHECK:STDOUT: %.loc19_20.2: init %empty_tuple.type = tuple_init () [concrete = constants.%empty_tuple] // CHECK:STDOUT: %.loc19_21.4: init %empty_tuple.type = converted %.loc19_20.1, %.loc19_20.2 [concrete = constants.%empty_tuple] // CHECK:STDOUT: %.loc19_21.5: init %X to %.loc19_21.2 = class_init (%.loc19_21.4) [concrete = constants.%X.val] // CHECK:STDOUT: %.loc19_23.1: init %X = converted %.loc19_21.1, %.loc19_21.5 [concrete = constants.%X.val] // CHECK:STDOUT: %Y.ref: type = name_ref Y, file.%Y.decl [concrete = constants.%Y] // CHECK:STDOUT: %impl.elem0: %.84a = impl_witness_access constants.%As.impl_witness.e92, element0 [concrete = constants.%X.as.As.impl.Convert] // CHECK:STDOUT: %bound_method: = bound_method %.loc19_23.1, %impl.elem0 [concrete = constants.%X.as.As.impl.Convert.bound] // CHECK:STDOUT: %.loc19_29.1: ref %Y = temporary_storage // CHECK:STDOUT: %.loc19_23.2: ref %X = temporary %.loc19_21.2, %.loc19_23.1 // CHECK:STDOUT: %.loc19_23.3: %X = acquire_value %.loc19_23.2 // CHECK:STDOUT: %X.as.As.impl.Convert.call: init %Y to %.loc19_29.1 = call %bound_method(%.loc19_23.3) // CHECK:STDOUT: %.loc19_29.2: init %Y = converted %.loc19_23.1, %X.as.As.impl.Convert.call // CHECK:STDOUT: // CHECK:STDOUT: } // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/as/const.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/convert.carbon // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/as/const.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/as/const.carbon // --- add_const.carbon library "[[@TEST_NAME]]"; class X {} fn Init() -> X; let value: X = Init(); var reference: X; let ptr: X* = &reference; fn Use() { // TODO: Should some of these be valid without the `as`? //@dump-sem-ir-begin var unused i: const X = Init() as const X; let unused v: const X = value as const X; let unused a: const X* = &(reference as const X); let unused b: const X* = ptr as const X*; //@dump-sem-ir-end } // --- remove_const.carbon library "[[@TEST_NAME]]"; class X {} fn Init() -> const X; let value: const X = Init(); fn Use() { // TODO: Should some of these be valid without the `as`? //@dump-sem-ir-begin var unused i: X = Init() as X; let unused v: X = value as X; //@dump-sem-ir-end } // --- fail_cannot_remove_const.carbon library "[[@TEST_NAME]]"; class X {} var reference: const X; let ptr: const X* = &reference; fn Use() { // CHECK:STDERR: fail_cannot_remove_const.carbon:[[@LINE+7]]:24: error: cannot convert expression of type `const X` to `X` with `as` [ConversionFailure] // CHECK:STDERR: let unused a: X* = &(reference as X); // CHECK:STDERR: ^~~~~~~~~~~~~~ // CHECK:STDERR: fail_cannot_remove_const.carbon:[[@LINE+4]]:24: note: type `const X` does not implement interface `Core.As(X)` [MissingImplInMemberAccessInContext] // CHECK:STDERR: let unused a: X* = &(reference as X); // CHECK:STDERR: ^~~~~~~~~~~~~~ // CHECK:STDERR: let unused a: X* = &(reference as X); // CHECK:STDERR: fail_cannot_remove_const.carbon:[[@LINE+7]]:22: error: cannot convert expression of type `const X*` to `X*` with `as` [ConversionFailure] // CHECK:STDERR: let unused b: X* = ptr as X*; // CHECK:STDERR: ^~~~~~~~~ // CHECK:STDERR: fail_cannot_remove_const.carbon:[[@LINE+4]]:22: note: type `const X*` does not implement interface `Core.As(X*)` [MissingImplInMemberAccessInContext] // CHECK:STDERR: let unused b: X* = ptr as X*; // CHECK:STDERR: ^~~~~~~~~ // CHECK:STDERR: let unused b: X* = ptr as X*; } // --- unsafe_remove_const.carbon library "[[@TEST_NAME]]"; class X {} var reference: const X; let ptr: const X* = &reference; fn Use() { //@dump-sem-ir-begin let unused a: X* = &(reference unsafe as X); let unused b: X* = ptr unsafe as X*; //@dump-sem-ir-end } // CHECK:STDOUT: --- add_const.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %X: type = class_type @X [concrete] // CHECK:STDOUT: %Init.type: type = fn_type @Init [concrete] // CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] // CHECK:STDOUT: %Init: %Init.type = struct_value () [concrete] // CHECK:STDOUT: %ptr.2a9: type = ptr_type %X [concrete] // CHECK:STDOUT: %const: type = const_type %X [concrete] // CHECK:STDOUT: %pattern_type.c9e: type = pattern_type %const [concrete] // CHECK:STDOUT: %ptr.d4c: type = ptr_type %const [concrete] // CHECK:STDOUT: %pattern_type.bf1: type = pattern_type %ptr.d4c [concrete] // CHECK:STDOUT: %reference.var: ref %const = var file.%reference.var_patt [concrete] // CHECK:STDOUT: %addr.160: %ptr.d4c = addr_of %reference.var [concrete] // CHECK:STDOUT: %Destroy.Op.type: type = fn_type @Destroy.Op [concrete] // CHECK:STDOUT: %Destroy.Op: %Destroy.Op.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @Use() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %i.patt: %pattern_type.c9e = ref_binding_pattern i [concrete] // CHECK:STDOUT: %i.var_patt: %pattern_type.c9e = var_pattern %i.patt [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %i.var: ref %const = var %i.var_patt // CHECK:STDOUT: %Init.ref: %Init.type = name_ref Init, file.%Init.decl [concrete = constants.%Init] // CHECK:STDOUT: %.loc14_3: ref %const = splice_block %i.var {} // CHECK:STDOUT: %Init.call: init %X to %.loc14_3 = call %Init.ref() // CHECK:STDOUT: %X.ref.loc14_43: type = name_ref X, file.%X.decl [concrete = constants.%X] // CHECK:STDOUT: %const.loc14_37: type = const_type %X.ref.loc14_43 [concrete = constants.%const] // CHECK:STDOUT: %.loc14_34.1: init %const = as_compatible %Init.call // CHECK:STDOUT: %.loc14_34.2: init %const = converted %Init.call, %.loc14_34.1 // CHECK:STDOUT: assign %i.var, %.loc14_34.2 // CHECK:STDOUT: %.loc14_17: type = splice_block %const.loc14_17 [concrete = constants.%const] { // CHECK:STDOUT: %X.ref.loc14_23: type = name_ref X, file.%X.decl [concrete = constants.%X] // CHECK:STDOUT: %const.loc14_17: type = const_type %X.ref.loc14_23 [concrete = constants.%const] // CHECK:STDOUT: } // CHECK:STDOUT: %i: ref %const = ref_binding i, %i.var // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %v.patt: %pattern_type.c9e = value_binding_pattern v [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %value.ref: %X = name_ref value, file.%value // CHECK:STDOUT: %X.ref.loc15_42: type = name_ref X, file.%X.decl [concrete = constants.%X] // CHECK:STDOUT: %const.loc15_36: type = const_type %X.ref.loc15_42 [concrete = constants.%const] // CHECK:STDOUT: %.loc15_33.1: %const = as_compatible %value.ref // CHECK:STDOUT: %.loc15_33.2: %const = converted %value.ref, %.loc15_33.1 // CHECK:STDOUT: %.loc15_17: type = splice_block %const.loc15_17 [concrete = constants.%const] { // CHECK:STDOUT: %X.ref.loc15_23: type = name_ref X, file.%X.decl [concrete = constants.%X] // CHECK:STDOUT: %const.loc15_17: type = const_type %X.ref.loc15_23 [concrete = constants.%const] // CHECK:STDOUT: } // CHECK:STDOUT: %v: %const = value_binding v, %.loc15_33.2 // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %a.patt: %pattern_type.bf1 = value_binding_pattern a [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %reference.ref: ref %X = name_ref reference, file.%reference [concrete = file.%reference.var] // CHECK:STDOUT: %X.ref.loc16_49: type = name_ref X, file.%X.decl [concrete = constants.%X] // CHECK:STDOUT: %const.loc16_43: type = const_type %X.ref.loc16_49 [concrete = constants.%const] // CHECK:STDOUT: %.loc16_40.1: ref %const = as_compatible %reference.ref [concrete = constants.%reference.var] // CHECK:STDOUT: %.loc16_40.2: ref %const = converted %reference.ref, %.loc16_40.1 [concrete = constants.%reference.var] // CHECK:STDOUT: %addr: %ptr.d4c = addr_of %.loc16_40.2 [concrete = constants.%addr.160] // CHECK:STDOUT: %.loc16_24: type = splice_block %ptr.loc16 [concrete = constants.%ptr.d4c] { // CHECK:STDOUT: %X.ref.loc16_23: type = name_ref X, file.%X.decl [concrete = constants.%X] // CHECK:STDOUT: %const.loc16_17: type = const_type %X.ref.loc16_23 [concrete = constants.%const] // CHECK:STDOUT: %ptr.loc16: type = ptr_type %const.loc16_17 [concrete = constants.%ptr.d4c] // CHECK:STDOUT: } // CHECK:STDOUT: %a: %ptr.d4c = value_binding a, %addr // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %b.patt: %pattern_type.bf1 = value_binding_pattern b [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %ptr.ref: %ptr.2a9 = name_ref ptr, file.%ptr.loc9_5 // CHECK:STDOUT: %X.ref.loc17_41: type = name_ref X, file.%X.decl [concrete = constants.%X] // CHECK:STDOUT: %const.loc17_35: type = const_type %X.ref.loc17_41 [concrete = constants.%const] // CHECK:STDOUT: %ptr.loc17_42: type = ptr_type %const.loc17_35 [concrete = constants.%ptr.d4c] // CHECK:STDOUT: %.loc17_32.1: %ptr.d4c = as_compatible %ptr.ref // CHECK:STDOUT: %.loc17_32.2: %ptr.d4c = converted %ptr.ref, %.loc17_32.1 // CHECK:STDOUT: %.loc17_24: type = splice_block %ptr.loc17_24 [concrete = constants.%ptr.d4c] { // CHECK:STDOUT: %X.ref.loc17_23: type = name_ref X, file.%X.decl [concrete = constants.%X] // CHECK:STDOUT: %const.loc17_17: type = const_type %X.ref.loc17_23 [concrete = constants.%const] // CHECK:STDOUT: %ptr.loc17_24: type = ptr_type %const.loc17_17 [concrete = constants.%ptr.d4c] // CHECK:STDOUT: } // CHECK:STDOUT: %b: %ptr.d4c = value_binding b, %.loc17_32.2 // CHECK:STDOUT: %Destroy.Op.bound: = bound_method %i.var, constants.%Destroy.Op // CHECK:STDOUT: %Destroy.Op.call: init %empty_tuple.type = call %Destroy.Op.bound(%i.var) // CHECK:STDOUT: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @Destroy.Op(%self.param: ref %const) = "no_op"; // CHECK:STDOUT: // CHECK:STDOUT: fn @__global_init() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- remove_const.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %X: type = class_type @X [concrete] // CHECK:STDOUT: %const: type = const_type %X [concrete] // CHECK:STDOUT: %Init.type: type = fn_type @Init [concrete] // CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] // CHECK:STDOUT: %Init: %Init.type = struct_value () [concrete] // CHECK:STDOUT: %pattern_type.05f: type = pattern_type %X [concrete] // CHECK:STDOUT: %Destroy.Op.type: type = fn_type @Destroy.Op [concrete] // CHECK:STDOUT: %Destroy.Op: %Destroy.Op.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @Use() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %i.patt: %pattern_type.05f = ref_binding_pattern i [concrete] // CHECK:STDOUT: %i.var_patt: %pattern_type.05f = var_pattern %i.patt [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %i.var: ref %X = var %i.var_patt // CHECK:STDOUT: %Init.ref: %Init.type = name_ref Init, file.%Init.decl [concrete = constants.%Init] // CHECK:STDOUT: %.loc12_3: ref %X = splice_block %i.var {} // CHECK:STDOUT: %Init.call: init %const to %.loc12_3 = call %Init.ref() // CHECK:STDOUT: %X.ref.loc12_31: type = name_ref X, file.%X.decl [concrete = constants.%X] // CHECK:STDOUT: %.loc12_28.1: init %X = as_compatible %Init.call // CHECK:STDOUT: %.loc12_28.2: init %X = converted %Init.call, %.loc12_28.1 // CHECK:STDOUT: assign %i.var, %.loc12_28.2 // CHECK:STDOUT: %X.ref.loc12_17: type = name_ref X, file.%X.decl [concrete = constants.%X] // CHECK:STDOUT: %i: ref %X = ref_binding i, %i.var // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %v.patt: %pattern_type.05f = value_binding_pattern v [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %value.ref: %const = name_ref value, file.%value // CHECK:STDOUT: %X.ref.loc13_30: type = name_ref X, file.%X.decl [concrete = constants.%X] // CHECK:STDOUT: %.loc13_27.1: %X = as_compatible %value.ref // CHECK:STDOUT: %.loc13_27.2: %X = converted %value.ref, %.loc13_27.1 // CHECK:STDOUT: %X.ref.loc13_17: type = name_ref X, file.%X.decl [concrete = constants.%X] // CHECK:STDOUT: %v: %X = value_binding v, %.loc13_27.2 // CHECK:STDOUT: %Destroy.Op.bound: = bound_method %i.var, constants.%Destroy.Op // CHECK:STDOUT: %Destroy.Op.call: init %empty_tuple.type = call %Destroy.Op.bound(%i.var) // CHECK:STDOUT: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @Destroy.Op(%self.param: ref %X) = "no_op"; // CHECK:STDOUT: // CHECK:STDOUT: fn @__global_init() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- unsafe_remove_const.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %X: type = class_type @X [concrete] // CHECK:STDOUT: %const: type = const_type %X [concrete] // CHECK:STDOUT: %ptr.d4c: type = ptr_type %const [concrete] // CHECK:STDOUT: %ptr.2a9: type = ptr_type %X [concrete] // CHECK:STDOUT: %pattern_type.37f: type = pattern_type %ptr.2a9 [concrete] // CHECK:STDOUT: %reference.var: ref %X = var file.%reference.var_patt [concrete] // CHECK:STDOUT: %addr.806: %ptr.2a9 = addr_of %reference.var [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @Use() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %a.patt: %pattern_type.37f = value_binding_pattern a [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %reference.ref: ref %const = name_ref reference, file.%reference [concrete = file.%reference.var] // CHECK:STDOUT: %X.ref.loc11_44: type = name_ref X, file.%X.decl [concrete = constants.%X] // CHECK:STDOUT: %.loc11_41.1: ref %X = as_compatible %reference.ref [concrete = constants.%reference.var] // CHECK:STDOUT: %.loc11_41.2: ref %X = converted %reference.ref, %.loc11_41.1 [concrete = constants.%reference.var] // CHECK:STDOUT: %addr: %ptr.2a9 = addr_of %.loc11_41.2 [concrete = constants.%addr.806] // CHECK:STDOUT: %.loc11_18: type = splice_block %ptr.loc11 [concrete = constants.%ptr.2a9] { // CHECK:STDOUT: %X.ref.loc11_17: type = name_ref X, file.%X.decl [concrete = constants.%X] // CHECK:STDOUT: %ptr.loc11: type = ptr_type %X.ref.loc11_17 [concrete = constants.%ptr.2a9] // CHECK:STDOUT: } // CHECK:STDOUT: %a: %ptr.2a9 = value_binding a, %addr // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %b.patt: %pattern_type.37f = value_binding_pattern b [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %ptr.ref: %ptr.d4c = name_ref ptr, file.%ptr.loc7_5 // CHECK:STDOUT: %X.ref.loc12_36: type = name_ref X, file.%X.decl [concrete = constants.%X] // CHECK:STDOUT: %ptr.loc12_37: type = ptr_type %X.ref.loc12_36 [concrete = constants.%ptr.2a9] // CHECK:STDOUT: %.loc12_33.1: %ptr.2a9 = as_compatible %ptr.ref // CHECK:STDOUT: %.loc12_33.2: %ptr.2a9 = converted %ptr.ref, %.loc12_33.1 // CHECK:STDOUT: %.loc12_18: type = splice_block %ptr.loc12_18 [concrete = constants.%ptr.2a9] { // CHECK:STDOUT: %X.ref.loc12_17: type = name_ref X, file.%X.decl [concrete = constants.%X] // CHECK:STDOUT: %ptr.loc12_18: type = ptr_type %X.ref.loc12_17 [concrete = constants.%ptr.2a9] // CHECK:STDOUT: } // CHECK:STDOUT: %b: %ptr.2a9 = value_binding b, %.loc12_33.2 // CHECK:STDOUT: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @__global_init() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: // CHECK:STDOUT: } // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/as/maybe_unformed.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/unformed.carbon // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/as/maybe_unformed.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/as/maybe_unformed.carbon // --- add_unformed.carbon library "[[@TEST_NAME]]"; class X {} var reference: X; let ptr: X* = &reference; fn Use() { // TODO: Should some of these be valid without the `as`? //@dump-sem-ir-begin let unused a: Core.MaybeUnformed(X)* = &(reference as Core.MaybeUnformed(X)); let unused b: Core.MaybeUnformed(X)* = ptr as Core.MaybeUnformed(X)*; //@dump-sem-ir-end } // --- fail_todo_add_unformed_nonref.carbon library "[[@TEST_NAME]]"; class X {} fn Init() -> X; let value: X = Init(); fn Use() { // TODO: These should probably be valid. //@dump-sem-ir-begin // CHECK:STDERR: fail_todo_add_unformed_nonref.carbon:[[@LINE+7]]:41: error: cannot convert expression of type `X` to `Core.MaybeUnformed(X)` with `as` [ConversionFailure] // CHECK:STDERR: var unused i: Core.MaybeUnformed(X) = Init() as Core.MaybeUnformed(X); // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: fail_todo_add_unformed_nonref.carbon:[[@LINE+4]]:41: note: type `X` does not implement interface `Core.As(Core.MaybeUnformed(X))` [MissingImplInMemberAccessInContext] // CHECK:STDERR: var unused i: Core.MaybeUnformed(X) = Init() as Core.MaybeUnformed(X); // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: var unused i: Core.MaybeUnformed(X) = Init() as Core.MaybeUnformed(X); // CHECK:STDERR: fail_todo_add_unformed_nonref.carbon:[[@LINE+7]]:41: error: cannot convert expression of type `X` to `Core.MaybeUnformed(X)` with `as` [ConversionFailure] // CHECK:STDERR: let unused v: Core.MaybeUnformed(X) = value as Core.MaybeUnformed(X); // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: fail_todo_add_unformed_nonref.carbon:[[@LINE+4]]:41: note: type `X` does not implement interface `Core.As(Core.MaybeUnformed(X))` [MissingImplInMemberAccessInContext] // CHECK:STDERR: let unused v: Core.MaybeUnformed(X) = value as Core.MaybeUnformed(X); // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: let unused v: Core.MaybeUnformed(X) = value as Core.MaybeUnformed(X); //@dump-sem-ir-end } // --- fail_cannot_remove_unformed.carbon library "[[@TEST_NAME]]"; class X {} fn Init() -> Core.MaybeUnformed(X); let value: Core.MaybeUnformed(X) = Init(); var reference: Core.MaybeUnformed(X); let ptr: Core.MaybeUnformed(X)* = &reference; fn Use() { // CHECK:STDERR: fail_cannot_remove_unformed.carbon:[[@LINE+7]]:21: error: cannot convert expression of type `Core.MaybeUnformed(X)` to `X` with `as` [ConversionFailure] // CHECK:STDERR: var unused i: X = Init() as X; // CHECK:STDERR: ^~~~~~~~~~~ // CHECK:STDERR: fail_cannot_remove_unformed.carbon:[[@LINE+4]]:21: note: type `Core.MaybeUnformed(X)` does not implement interface `Core.As(X)` [MissingImplInMemberAccessInContext] // CHECK:STDERR: var unused i: X = Init() as X; // CHECK:STDERR: ^~~~~~~~~~~ // CHECK:STDERR: var unused i: X = Init() as X; // TODO: The diagnostic should explain that the reason we can't perform this // conversion is due to the expression category. // CHECK:STDERR: fail_cannot_remove_unformed.carbon:[[@LINE+7]]:21: error: cannot convert expression of type `Core.MaybeUnformed(X)` to `X` with `unsafe as` [ConversionFailure] // CHECK:STDERR: var unused j: X = Init() unsafe as X; // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~ // CHECK:STDERR: fail_cannot_remove_unformed.carbon:[[@LINE+4]]:21: note: type `Core.MaybeUnformed(X)` does not implement interface `Core.UnsafeAs(X)` [MissingImplInMemberAccessInContext] // CHECK:STDERR: var unused j: X = Init() unsafe as X; // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~ // CHECK:STDERR: var unused j: X = Init() unsafe as X; // CHECK:STDERR: fail_cannot_remove_unformed.carbon:[[@LINE+7]]:21: error: cannot convert expression of type `Core.MaybeUnformed(X)` to `X` with `as` [ConversionFailure] // CHECK:STDERR: let unused v: X = value as X; // CHECK:STDERR: ^~~~~~~~~~ // CHECK:STDERR: fail_cannot_remove_unformed.carbon:[[@LINE+4]]:21: note: type `Core.MaybeUnformed(X)` does not implement interface `Core.As(X)` [MissingImplInMemberAccessInContext] // CHECK:STDERR: let unused v: X = value as X; // CHECK:STDERR: ^~~~~~~~~~ // CHECK:STDERR: let unused v: X = value as X; // CHECK:STDERR: fail_cannot_remove_unformed.carbon:[[@LINE+7]]:24: error: cannot convert expression of type `Core.MaybeUnformed(X)` to `X` with `as` [ConversionFailure] // CHECK:STDERR: let unused a: X* = &(reference as X); // CHECK:STDERR: ^~~~~~~~~~~~~~ // CHECK:STDERR: fail_cannot_remove_unformed.carbon:[[@LINE+4]]:24: note: type `Core.MaybeUnformed(X)` does not implement interface `Core.As(X)` [MissingImplInMemberAccessInContext] // CHECK:STDERR: let unused a: X* = &(reference as X); // CHECK:STDERR: ^~~~~~~~~~~~~~ // CHECK:STDERR: let unused a: X* = &(reference as X); // CHECK:STDERR: fail_cannot_remove_unformed.carbon:[[@LINE+7]]:22: error: cannot convert expression of type `Core.MaybeUnformed(X)*` to `X*` with `as` [ConversionFailure] // CHECK:STDERR: let unused b: X* = ptr as X*; // CHECK:STDERR: ^~~~~~~~~ // CHECK:STDERR: fail_cannot_remove_unformed.carbon:[[@LINE+4]]:22: note: type `Core.MaybeUnformed(X)*` does not implement interface `Core.As(X*)` [MissingImplInMemberAccessInContext] // CHECK:STDERR: let unused b: X* = ptr as X*; // CHECK:STDERR: ^~~~~~~~~ // CHECK:STDERR: let unused b: X* = ptr as X*; } // --- unsafe_remove_unformed.carbon library "[[@TEST_NAME]]"; class X {} fn Init() -> Core.MaybeUnformed(X); let value: Core.MaybeUnformed(X) = Init(); var reference: Core.MaybeUnformed(X); let ptr: Core.MaybeUnformed(X)* = &reference; fn Use() { //@dump-sem-ir-begin let unused v: X = value unsafe as X; let unused a: X* = &(reference unsafe as X); let unused b: X* = ptr unsafe as X*; //@dump-sem-ir-end } // CHECK:STDOUT: --- add_unformed.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %X: type = class_type @X [concrete] // CHECK:STDOUT: %DefaultOrUnformed.type: type = facet_type <@DefaultOrUnformed> [concrete] // CHECK:STDOUT: %ptr.2a9: type = ptr_type %X [concrete] // CHECK:STDOUT: %MaybeUnformed.type: type = generic_class_type @MaybeUnformed [concrete] // CHECK:STDOUT: %MaybeUnformed.generic: %MaybeUnformed.type = struct_value () [concrete] // CHECK:STDOUT: %MaybeUnformed.b49: type = class_type @MaybeUnformed, @MaybeUnformed(%X) [concrete] // CHECK:STDOUT: %ptr.045: type = ptr_type %MaybeUnformed.b49 [concrete] // CHECK:STDOUT: %pattern_type.396: type = pattern_type %ptr.045 [concrete] // CHECK:STDOUT: %reference.var: ref %MaybeUnformed.b49 = var file.%reference.var_patt [concrete] // CHECK:STDOUT: %addr.25c: %ptr.045 = addr_of %reference.var [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: .DefaultOrUnformed = %Core.DefaultOrUnformed // CHECK:STDOUT: .MaybeUnformed = %Core.MaybeUnformed // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: %Core.DefaultOrUnformed: type = import_ref Core//prelude/parts/default, DefaultOrUnformed, loaded [concrete = constants.%DefaultOrUnformed.type] // CHECK:STDOUT: %Core.MaybeUnformed: %MaybeUnformed.type = import_ref Core//prelude/parts/maybe_unformed, MaybeUnformed, loaded [concrete = constants.%MaybeUnformed.generic] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @Use() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %a.patt: %pattern_type.396 = value_binding_pattern a [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %reference.ref: ref %X = name_ref reference, file.%reference [concrete = file.%reference.var] // CHECK:STDOUT: %Core.ref.loc12_57: = name_ref Core, imports.%Core [concrete = imports.%Core] // CHECK:STDOUT: %MaybeUnformed.ref.loc12_61: %MaybeUnformed.type = name_ref MaybeUnformed, imports.%Core.MaybeUnformed [concrete = constants.%MaybeUnformed.generic] // CHECK:STDOUT: %X.ref.loc12_76: type = name_ref X, file.%X.decl [concrete = constants.%X] // CHECK:STDOUT: %MaybeUnformed.loc12_77: type = class_type @MaybeUnformed, @MaybeUnformed(constants.%X) [concrete = constants.%MaybeUnformed.b49] // CHECK:STDOUT: %.loc12_54.1: ref %MaybeUnformed.b49 = as_compatible %reference.ref [concrete = constants.%reference.var] // CHECK:STDOUT: %.loc12_54.2: ref %MaybeUnformed.b49 = converted %reference.ref, %.loc12_54.1 [concrete = constants.%reference.var] // CHECK:STDOUT: %addr: %ptr.045 = addr_of %.loc12_54.2 [concrete = constants.%addr.25c] // CHECK:STDOUT: %.loc12_38: type = splice_block %ptr.loc12 [concrete = constants.%ptr.045] { // CHECK:STDOUT: %Core.ref.loc12_17: = name_ref Core, imports.%Core [concrete = imports.%Core] // CHECK:STDOUT: %MaybeUnformed.ref.loc12_21: %MaybeUnformed.type = name_ref MaybeUnformed, imports.%Core.MaybeUnformed [concrete = constants.%MaybeUnformed.generic] // CHECK:STDOUT: %X.ref.loc12_36: type = name_ref X, file.%X.decl [concrete = constants.%X] // CHECK:STDOUT: %MaybeUnformed.loc12_37: type = class_type @MaybeUnformed, @MaybeUnformed(constants.%X) [concrete = constants.%MaybeUnformed.b49] // CHECK:STDOUT: %ptr.loc12: type = ptr_type %MaybeUnformed.loc12_37 [concrete = constants.%ptr.045] // CHECK:STDOUT: } // CHECK:STDOUT: %a: %ptr.045 = value_binding a, %addr // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %b.patt: %pattern_type.396 = value_binding_pattern b [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %ptr.ref: %ptr.2a9 = name_ref ptr, file.%ptr.loc7_5 // CHECK:STDOUT: %Core.ref.loc13_49: = name_ref Core, imports.%Core [concrete = imports.%Core] // CHECK:STDOUT: %MaybeUnformed.ref.loc13_53: %MaybeUnformed.type = name_ref MaybeUnformed, imports.%Core.MaybeUnformed [concrete = constants.%MaybeUnformed.generic] // CHECK:STDOUT: %X.ref.loc13_68: type = name_ref X, file.%X.decl [concrete = constants.%X] // CHECK:STDOUT: %MaybeUnformed.loc13_69: type = class_type @MaybeUnformed, @MaybeUnformed(constants.%X) [concrete = constants.%MaybeUnformed.b49] // CHECK:STDOUT: %ptr.loc13_70: type = ptr_type %MaybeUnformed.loc13_69 [concrete = constants.%ptr.045] // CHECK:STDOUT: %.loc13_46.1: %ptr.045 = as_compatible %ptr.ref // CHECK:STDOUT: %.loc13_46.2: %ptr.045 = converted %ptr.ref, %.loc13_46.1 // CHECK:STDOUT: %.loc13_38: type = splice_block %ptr.loc13_38 [concrete = constants.%ptr.045] { // CHECK:STDOUT: %Core.ref.loc13_17: = name_ref Core, imports.%Core [concrete = imports.%Core] // CHECK:STDOUT: %MaybeUnformed.ref.loc13_21: %MaybeUnformed.type = name_ref MaybeUnformed, imports.%Core.MaybeUnformed [concrete = constants.%MaybeUnformed.generic] // CHECK:STDOUT: %X.ref.loc13_36: type = name_ref X, file.%X.decl [concrete = constants.%X] // CHECK:STDOUT: %MaybeUnformed.loc13_37: type = class_type @MaybeUnformed, @MaybeUnformed(constants.%X) [concrete = constants.%MaybeUnformed.b49] // CHECK:STDOUT: %ptr.loc13_38: type = ptr_type %MaybeUnformed.loc13_37 [concrete = constants.%ptr.045] // CHECK:STDOUT: } // CHECK:STDOUT: %b: %ptr.045 = value_binding b, %.loc13_46.2 // CHECK:STDOUT: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @__global_init() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- fail_todo_add_unformed_nonref.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %X: type = class_type @X [concrete] // CHECK:STDOUT: %Init.type: type = fn_type @Init [concrete] // CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] // CHECK:STDOUT: %Init: %Init.type = struct_value () [concrete] // CHECK:STDOUT: %MaybeUnformed.type: type = generic_class_type @MaybeUnformed [concrete] // CHECK:STDOUT: %MaybeUnformed.generic: %MaybeUnformed.type = struct_value () [concrete] // CHECK:STDOUT: %MaybeUnformed.b49: type = class_type @MaybeUnformed, @MaybeUnformed(%X) [concrete] // CHECK:STDOUT: %pattern_type.de9: type = pattern_type %MaybeUnformed.b49 [concrete] // CHECK:STDOUT: %As.type.90f: type = generic_interface_type @As [concrete] // CHECK:STDOUT: %As.generic: %As.type.90f = struct_value () [concrete] // CHECK:STDOUT: %Destroy.type: type = facet_type <@Destroy> [concrete] // CHECK:STDOUT: %Destroy.Op.type: type = fn_type @Destroy.Op [concrete] // CHECK:STDOUT: %Destroy.Op: %Destroy.Op.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: .MaybeUnformed = %Core.MaybeUnformed // CHECK:STDOUT: .As = %Core.As // CHECK:STDOUT: .Destroy = %Core.Destroy // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: %Core.MaybeUnformed: %MaybeUnformed.type = import_ref Core//prelude/parts/maybe_unformed, MaybeUnformed, loaded [concrete = constants.%MaybeUnformed.generic] // CHECK:STDOUT: %Core.As: %As.type.90f = import_ref Core//prelude/parts/as, As, loaded [concrete = constants.%As.generic] // CHECK:STDOUT: %Core.Destroy: type = import_ref Core//prelude/parts/destroy, Destroy, loaded [concrete = constants.%Destroy.type] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @Use() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %i.patt: %pattern_type.de9 = ref_binding_pattern i [concrete] // CHECK:STDOUT: %i.var_patt: %pattern_type.de9 = var_pattern %i.patt [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %i.var: ref %MaybeUnformed.b49 = var %i.var_patt // CHECK:STDOUT: %Init.ref: %Init.type = name_ref Init, file.%Init.decl [concrete = constants.%Init] // CHECK:STDOUT: %.loc19_46: ref %X = temporary_storage // CHECK:STDOUT: %Init.call: init %X to %.loc19_46 = call %Init.ref() // CHECK:STDOUT: %Core.ref.loc19_51: = name_ref Core, imports.%Core [concrete = imports.%Core] // CHECK:STDOUT: %MaybeUnformed.ref.loc19_55: %MaybeUnformed.type = name_ref MaybeUnformed, imports.%Core.MaybeUnformed [concrete = constants.%MaybeUnformed.generic] // CHECK:STDOUT: %X.ref.loc19_70: type = name_ref X, file.%X.decl [concrete = constants.%X] // CHECK:STDOUT: %MaybeUnformed.loc19_71: type = class_type @MaybeUnformed, @MaybeUnformed(constants.%X) [concrete = constants.%MaybeUnformed.b49] // CHECK:STDOUT: %.loc19_48: %MaybeUnformed.b49 = converted %Init.call, [concrete = ] // CHECK:STDOUT: assign %i.var, // CHECK:STDOUT: %.loc19_37: type = splice_block %MaybeUnformed.loc19_37 [concrete = constants.%MaybeUnformed.b49] { // CHECK:STDOUT: %Core.ref.loc19_17: = name_ref Core, imports.%Core [concrete = imports.%Core] // CHECK:STDOUT: %MaybeUnformed.ref.loc19_21: %MaybeUnformed.type = name_ref MaybeUnformed, imports.%Core.MaybeUnformed [concrete = constants.%MaybeUnformed.generic] // CHECK:STDOUT: %X.ref.loc19_36: type = name_ref X, file.%X.decl [concrete = constants.%X] // CHECK:STDOUT: %MaybeUnformed.loc19_37: type = class_type @MaybeUnformed, @MaybeUnformed(constants.%X) [concrete = constants.%MaybeUnformed.b49] // CHECK:STDOUT: } // CHECK:STDOUT: %i: ref %MaybeUnformed.b49 = ref_binding i, %i.var // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %v.patt: %pattern_type.de9 = value_binding_pattern v [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %value.ref: %X = name_ref value, file.%value // CHECK:STDOUT: %Core.ref.loc27_50: = name_ref Core, imports.%Core [concrete = imports.%Core] // CHECK:STDOUT: %MaybeUnformed.ref.loc27_54: %MaybeUnformed.type = name_ref MaybeUnformed, imports.%Core.MaybeUnformed [concrete = constants.%MaybeUnformed.generic] // CHECK:STDOUT: %X.ref.loc27_69: type = name_ref X, file.%X.decl [concrete = constants.%X] // CHECK:STDOUT: %MaybeUnformed.loc27_70: type = class_type @MaybeUnformed, @MaybeUnformed(constants.%X) [concrete = constants.%MaybeUnformed.b49] // CHECK:STDOUT: %.loc27_47: %MaybeUnformed.b49 = converted %value.ref, [concrete = ] // CHECK:STDOUT: %.loc27_37: type = splice_block %MaybeUnformed.loc27_37 [concrete = constants.%MaybeUnformed.b49] { // CHECK:STDOUT: %Core.ref.loc27_17: = name_ref Core, imports.%Core [concrete = imports.%Core] // CHECK:STDOUT: %MaybeUnformed.ref.loc27_21: %MaybeUnformed.type = name_ref MaybeUnformed, imports.%Core.MaybeUnformed [concrete = constants.%MaybeUnformed.generic] // CHECK:STDOUT: %X.ref.loc27_36: type = name_ref X, file.%X.decl [concrete = constants.%X] // CHECK:STDOUT: %MaybeUnformed.loc27_37: type = class_type @MaybeUnformed, @MaybeUnformed(constants.%X) [concrete = constants.%MaybeUnformed.b49] // CHECK:STDOUT: } // CHECK:STDOUT: %v: %MaybeUnformed.b49 = value_binding v, [concrete = ] // CHECK:STDOUT: %Destroy.Op.bound: = bound_method %i.var, constants.%Destroy.Op // CHECK:STDOUT: %Destroy.Op.call: init %empty_tuple.type = call %Destroy.Op.bound(%i.var) // CHECK:STDOUT: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @Destroy.Op(%self.param: ref %MaybeUnformed.b49) = "no_op"; // CHECK:STDOUT: // CHECK:STDOUT: fn @__global_init() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- unsafe_remove_unformed.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %X: type = class_type @X [concrete] // CHECK:STDOUT: %MaybeUnformed.b49: type = class_type @MaybeUnformed, @MaybeUnformed(%X) [concrete] // CHECK:STDOUT: %ptr.045: type = ptr_type %MaybeUnformed.b49 [concrete] // CHECK:STDOUT: %pattern_type.05f: type = pattern_type %X [concrete] // CHECK:STDOUT: %ptr.2a9: type = ptr_type %X [concrete] // CHECK:STDOUT: %pattern_type.37f: type = pattern_type %ptr.2a9 [concrete] // CHECK:STDOUT: %reference.var: ref %X = var file.%reference.var_patt [concrete] // CHECK:STDOUT: %addr.6f7: %ptr.2a9 = addr_of %reference.var [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @Use() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %v.patt: %pattern_type.05f = value_binding_pattern v [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %value.ref: %MaybeUnformed.b49 = name_ref value, file.%value // CHECK:STDOUT: %X.ref.loc13_37: type = name_ref X, file.%X.decl [concrete = constants.%X] // CHECK:STDOUT: %.loc13_34.1: %X = as_compatible %value.ref // CHECK:STDOUT: %.loc13_34.2: %X = converted %value.ref, %.loc13_34.1 // CHECK:STDOUT: %X.ref.loc13_17: type = name_ref X, file.%X.decl [concrete = constants.%X] // CHECK:STDOUT: %v: %X = value_binding v, %.loc13_34.2 // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %a.patt: %pattern_type.37f = value_binding_pattern a [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %reference.ref: ref %MaybeUnformed.b49 = name_ref reference, file.%reference [concrete = file.%reference.var] // CHECK:STDOUT: %X.ref.loc14_44: type = name_ref X, file.%X.decl [concrete = constants.%X] // CHECK:STDOUT: %.loc14_41.1: ref %X = as_compatible %reference.ref [concrete = constants.%reference.var] // CHECK:STDOUT: %.loc14_41.2: ref %X = converted %reference.ref, %.loc14_41.1 [concrete = constants.%reference.var] // CHECK:STDOUT: %addr: %ptr.2a9 = addr_of %.loc14_41.2 [concrete = constants.%addr.6f7] // CHECK:STDOUT: %.loc14_18: type = splice_block %ptr.loc14 [concrete = constants.%ptr.2a9] { // CHECK:STDOUT: %X.ref.loc14_17: type = name_ref X, file.%X.decl [concrete = constants.%X] // CHECK:STDOUT: %ptr.loc14: type = ptr_type %X.ref.loc14_17 [concrete = constants.%ptr.2a9] // CHECK:STDOUT: } // CHECK:STDOUT: %a: %ptr.2a9 = value_binding a, %addr // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %b.patt: %pattern_type.37f = value_binding_pattern b [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %ptr.ref: %ptr.045 = name_ref ptr, file.%ptr.loc9_5 // CHECK:STDOUT: %X.ref.loc15_36: type = name_ref X, file.%X.decl [concrete = constants.%X] // CHECK:STDOUT: %ptr.loc15_37: type = ptr_type %X.ref.loc15_36 [concrete = constants.%ptr.2a9] // CHECK:STDOUT: %.loc15_33.1: %ptr.2a9 = as_compatible %ptr.ref // CHECK:STDOUT: %.loc15_33.2: %ptr.2a9 = converted %ptr.ref, %.loc15_33.1 // CHECK:STDOUT: %.loc15_18: type = splice_block %ptr.loc15_18 [concrete = constants.%ptr.2a9] { // CHECK:STDOUT: %X.ref.loc15_17: type = name_ref X, file.%X.decl [concrete = constants.%X] // CHECK:STDOUT: %ptr.loc15_18: type = ptr_type %X.ref.loc15_17 [concrete = constants.%ptr.2a9] // CHECK:STDOUT: } // CHECK:STDOUT: %b: %ptr.2a9 = value_binding b, %.loc15_33.2 // CHECK:STDOUT: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @__global_init() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: // CHECK:STDOUT: } // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/as/partial.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/convert.carbon // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/as/partial.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/as/partial.carbon // --- add_partial.carbon library "[[@TEST_NAME]]"; base class X {} fn Init() -> X; let value: X = Init(); var reference: X; let ptr: X* = &reference; fn Use() { // TODO: Should some of these be valid without the `as`? //@dump-sem-ir-begin var unused i: partial X = Init() as partial X; let unused v: partial X = value as partial X; let unused a: partial X* = &(reference as partial X); let unused b: partial X* = ptr as partial X*; //@dump-sem-ir-end } // --- remove_partial_in_init.carbon library "[[@TEST_NAME]]"; base class X {} fn Init() -> partial X; fn Use() { //@dump-sem-ir-begin var unused i: X = Init(); var unused j: X = Init() as X; var unused k: X = Init() unsafe as X; //@dump-sem-ir-end } // --- fail_cannot_remove_partial.carbon library "[[@TEST_NAME]]"; base class X {} fn Init() -> partial X; let value: partial X = Init(); var reference: partial X; let ptr: partial X* = &reference; fn Use() { // CHECK:STDERR: fail_cannot_remove_partial.carbon:[[@LINE+7]]:21: error: cannot convert expression of type `partial X` to `X` with `as` [ConversionFailure] // CHECK:STDERR: let unused v: X = value as X; // CHECK:STDERR: ^~~~~~~~~~ // CHECK:STDERR: fail_cannot_remove_partial.carbon:[[@LINE+4]]:21: note: type `partial X` does not implement interface `Core.As(X)` [MissingImplInMemberAccessInContext] // CHECK:STDERR: let unused v: X = value as X; // CHECK:STDERR: ^~~~~~~~~~ // CHECK:STDERR: let unused v: X = value as X; // CHECK:STDERR: fail_cannot_remove_partial.carbon:[[@LINE+7]]:24: error: cannot convert expression of type `partial X` to `X` with `as` [ConversionFailure] // CHECK:STDERR: let unused a: X* = &(reference as X); // CHECK:STDERR: ^~~~~~~~~~~~~~ // CHECK:STDERR: fail_cannot_remove_partial.carbon:[[@LINE+4]]:24: note: type `partial X` does not implement interface `Core.As(X)` [MissingImplInMemberAccessInContext] // CHECK:STDERR: let unused a: X* = &(reference as X); // CHECK:STDERR: ^~~~~~~~~~~~~~ // CHECK:STDERR: let unused a: X* = &(reference as X); // CHECK:STDERR: fail_cannot_remove_partial.carbon:[[@LINE+7]]:22: error: cannot convert expression of type `partial X*` to `X*` with `as` [ConversionFailure] // CHECK:STDERR: let unused b: X* = ptr as X*; // CHECK:STDERR: ^~~~~~~~~ // CHECK:STDERR: fail_cannot_remove_partial.carbon:[[@LINE+4]]:22: note: type `partial X*` does not implement interface `Core.As(X*)` [MissingImplInMemberAccessInContext] // CHECK:STDERR: let unused b: X* = ptr as X*; // CHECK:STDERR: ^~~~~~~~~ // CHECK:STDERR: let unused b: X* = ptr as X*; } // --- unsafe_remove_partial.carbon library "[[@TEST_NAME]]"; base class X {} fn Init() -> partial X; let value: partial X = Init(); var reference: partial X; let ptr: partial X* = &reference; fn Use() { //@dump-sem-ir-begin let unused v: X = value unsafe as X; let unused a: X* = &(reference unsafe as X); let unused b: X* = ptr unsafe as X*; //@dump-sem-ir-end } // CHECK:STDOUT: --- add_partial.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %X: type = class_type @X [concrete] // CHECK:STDOUT: %Init.type: type = fn_type @Init [concrete] // CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] // CHECK:STDOUT: %Init: %Init.type = struct_value () [concrete] // CHECK:STDOUT: %ptr.2a9: type = ptr_type %X [concrete] // CHECK:STDOUT: %.4b5: type = partial_type %X [concrete] // CHECK:STDOUT: %pattern_type.bc5: type = pattern_type %.4b5 [concrete] // CHECK:STDOUT: %ptr.314: type = ptr_type %.4b5 [concrete] // CHECK:STDOUT: %pattern_type.408: type = pattern_type %ptr.314 [concrete] // CHECK:STDOUT: %reference.var: ref %.4b5 = var file.%reference.var_patt [concrete] // CHECK:STDOUT: %addr.315: %ptr.314 = addr_of %reference.var [concrete] // CHECK:STDOUT: %Destroy.Op.type: type = fn_type @Destroy.Op [concrete] // CHECK:STDOUT: %Destroy.Op: %Destroy.Op.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @Use() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %i.patt: %pattern_type.bc5 = ref_binding_pattern i [concrete] // CHECK:STDOUT: %i.var_patt: %pattern_type.bc5 = var_pattern %i.patt [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %i.var: ref %.4b5 = var %i.var_patt // CHECK:STDOUT: %Init.ref: %Init.type = name_ref Init, file.%Init.decl [concrete = constants.%Init] // CHECK:STDOUT: %.loc14_3: ref %.4b5 = splice_block %i.var {} // CHECK:STDOUT: %Init.call: init %X to %.loc14_3 = call %Init.ref() // CHECK:STDOUT: %X.ref.loc14_47: type = name_ref X, file.%X.decl [concrete = constants.%X] // CHECK:STDOUT: %.loc14_39: type = partial_type %X.ref.loc14_47 [concrete = constants.%.4b5] // CHECK:STDOUT: %.loc14_36.1: init %.4b5 = as_compatible %Init.call // CHECK:STDOUT: %.loc14_36.2: init %.4b5 = converted %Init.call, %.loc14_36.1 // CHECK:STDOUT: assign %i.var, %.loc14_36.2 // CHECK:STDOUT: %.loc14_17.1: type = splice_block %.loc14_17.2 [concrete = constants.%.4b5] { // CHECK:STDOUT: %X.ref.loc14_25: type = name_ref X, file.%X.decl [concrete = constants.%X] // CHECK:STDOUT: %.loc14_17.2: type = partial_type %X.ref.loc14_25 [concrete = constants.%.4b5] // CHECK:STDOUT: } // CHECK:STDOUT: %i: ref %.4b5 = ref_binding i, %i.var // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %v.patt: %pattern_type.bc5 = value_binding_pattern v [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %value.ref: %X = name_ref value, file.%value // CHECK:STDOUT: %X.ref.loc15_46: type = name_ref X, file.%X.decl [concrete = constants.%X] // CHECK:STDOUT: %.loc15_38: type = partial_type %X.ref.loc15_46 [concrete = constants.%.4b5] // CHECK:STDOUT: %.loc15_35.1: %.4b5 = as_compatible %value.ref // CHECK:STDOUT: %.loc15_35.2: %.4b5 = converted %value.ref, %.loc15_35.1 // CHECK:STDOUT: %.loc15_17.1: type = splice_block %.loc15_17.2 [concrete = constants.%.4b5] { // CHECK:STDOUT: %X.ref.loc15_25: type = name_ref X, file.%X.decl [concrete = constants.%X] // CHECK:STDOUT: %.loc15_17.2: type = partial_type %X.ref.loc15_25 [concrete = constants.%.4b5] // CHECK:STDOUT: } // CHECK:STDOUT: %v: %.4b5 = value_binding v, %.loc15_35.2 // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %a.patt: %pattern_type.408 = value_binding_pattern a [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %reference.ref: ref %X = name_ref reference, file.%reference [concrete = file.%reference.var] // CHECK:STDOUT: %X.ref.loc16_53: type = name_ref X, file.%X.decl [concrete = constants.%X] // CHECK:STDOUT: %.loc16_45: type = partial_type %X.ref.loc16_53 [concrete = constants.%.4b5] // CHECK:STDOUT: %.loc16_42.1: ref %.4b5 = as_compatible %reference.ref [concrete = constants.%reference.var] // CHECK:STDOUT: %.loc16_42.2: ref %.4b5 = converted %reference.ref, %.loc16_42.1 [concrete = constants.%reference.var] // CHECK:STDOUT: %addr: %ptr.314 = addr_of %.loc16_42.2 [concrete = constants.%addr.315] // CHECK:STDOUT: %.loc16_26: type = splice_block %ptr.loc16 [concrete = constants.%ptr.314] { // CHECK:STDOUT: %X.ref.loc16_25: type = name_ref X, file.%X.decl [concrete = constants.%X] // CHECK:STDOUT: %.loc16_17: type = partial_type %X.ref.loc16_25 [concrete = constants.%.4b5] // CHECK:STDOUT: %ptr.loc16: type = ptr_type %.loc16_17 [concrete = constants.%ptr.314] // CHECK:STDOUT: } // CHECK:STDOUT: %a: %ptr.314 = value_binding a, %addr // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %b.patt: %pattern_type.408 = value_binding_pattern b [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %ptr.ref: %ptr.2a9 = name_ref ptr, file.%ptr.loc9_5 // CHECK:STDOUT: %X.ref.loc17_45: type = name_ref X, file.%X.decl [concrete = constants.%X] // CHECK:STDOUT: %.loc17_37: type = partial_type %X.ref.loc17_45 [concrete = constants.%.4b5] // CHECK:STDOUT: %ptr.loc17_46: type = ptr_type %.loc17_37 [concrete = constants.%ptr.314] // CHECK:STDOUT: %.loc17_34.1: %ptr.314 = as_compatible %ptr.ref // CHECK:STDOUT: %.loc17_34.2: %ptr.314 = converted %ptr.ref, %.loc17_34.1 // CHECK:STDOUT: %.loc17_26: type = splice_block %ptr.loc17_26 [concrete = constants.%ptr.314] { // CHECK:STDOUT: %X.ref.loc17_25: type = name_ref X, file.%X.decl [concrete = constants.%X] // CHECK:STDOUT: %.loc17_17: type = partial_type %X.ref.loc17_25 [concrete = constants.%.4b5] // CHECK:STDOUT: %ptr.loc17_26: type = ptr_type %.loc17_17 [concrete = constants.%ptr.314] // CHECK:STDOUT: } // CHECK:STDOUT: %b: %ptr.314 = value_binding b, %.loc17_34.2 // CHECK:STDOUT: %Destroy.Op.bound: = bound_method %i.var, constants.%Destroy.Op // CHECK:STDOUT: %Destroy.Op.call: init %empty_tuple.type = call %Destroy.Op.bound(%i.var) // CHECK:STDOUT: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @Destroy.Op(%self.param: ref %.4b5) = "no_op"; // CHECK:STDOUT: // CHECK:STDOUT: fn @__global_init() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- remove_partial_in_init.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %X: type = class_type @X [concrete] // CHECK:STDOUT: %.4b5: type = partial_type %X [concrete] // CHECK:STDOUT: %Init.type: type = fn_type @Init [concrete] // CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] // CHECK:STDOUT: %Init: %Init.type = struct_value () [concrete] // CHECK:STDOUT: %pattern_type.05f: type = pattern_type %X [concrete] // CHECK:STDOUT: %Destroy.Op.type: type = fn_type @Destroy.Op [concrete] // CHECK:STDOUT: %Destroy.Op: %Destroy.Op.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @Use() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %i.patt: %pattern_type.05f = ref_binding_pattern i [concrete] // CHECK:STDOUT: %i.var_patt: %pattern_type.05f = var_pattern %i.patt [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %i.var: ref %X = var %i.var_patt // CHECK:STDOUT: %Init.ref.loc10: %Init.type = name_ref Init, file.%Init.decl [concrete = constants.%Init] // CHECK:STDOUT: %.loc10_3.1: ref %X = splice_block %i.var {} // CHECK:STDOUT: %Init.call.loc10: init %.4b5 to %.loc10_3.1 = call %Init.ref.loc10() // CHECK:STDOUT: %.loc10_3.2: init %X = as_compatible %Init.call.loc10 // CHECK:STDOUT: %.loc10_3.3: init %X = converted %Init.call.loc10, %.loc10_3.2 // CHECK:STDOUT: assign %i.var, %.loc10_3.3 // CHECK:STDOUT: %X.ref.loc10: type = name_ref X, file.%X.decl [concrete = constants.%X] // CHECK:STDOUT: %i: ref %X = ref_binding i, %i.var // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %j.patt: %pattern_type.05f = ref_binding_pattern j [concrete] // CHECK:STDOUT: %j.var_patt: %pattern_type.05f = var_pattern %j.patt [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %j.var: ref %X = var %j.var_patt // CHECK:STDOUT: %Init.ref.loc11: %Init.type = name_ref Init, file.%Init.decl [concrete = constants.%Init] // CHECK:STDOUT: %.loc11_3: ref %X = splice_block %j.var {} // CHECK:STDOUT: %Init.call.loc11: init %.4b5 to %.loc11_3 = call %Init.ref.loc11() // CHECK:STDOUT: %X.ref.loc11_31: type = name_ref X, file.%X.decl [concrete = constants.%X] // CHECK:STDOUT: %.loc11_28.1: init %X = as_compatible %Init.call.loc11 // CHECK:STDOUT: %.loc11_28.2: init %X = converted %Init.call.loc11, %.loc11_28.1 // CHECK:STDOUT: assign %j.var, %.loc11_28.2 // CHECK:STDOUT: %X.ref.loc11_17: type = name_ref X, file.%X.decl [concrete = constants.%X] // CHECK:STDOUT: %j: ref %X = ref_binding j, %j.var // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %k.patt: %pattern_type.05f = ref_binding_pattern k [concrete] // CHECK:STDOUT: %k.var_patt: %pattern_type.05f = var_pattern %k.patt [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %k.var: ref %X = var %k.var_patt // CHECK:STDOUT: %Init.ref.loc12: %Init.type = name_ref Init, file.%Init.decl [concrete = constants.%Init] // CHECK:STDOUT: %.loc12_3: ref %X = splice_block %k.var {} // CHECK:STDOUT: %Init.call.loc12: init %.4b5 to %.loc12_3 = call %Init.ref.loc12() // CHECK:STDOUT: %X.ref.loc12_38: type = name_ref X, file.%X.decl [concrete = constants.%X] // CHECK:STDOUT: %.loc12_35.1: init %X = as_compatible %Init.call.loc12 // CHECK:STDOUT: %.loc12_35.2: init %X = converted %Init.call.loc12, %.loc12_35.1 // CHECK:STDOUT: assign %k.var, %.loc12_35.2 // CHECK:STDOUT: %X.ref.loc12_17: type = name_ref X, file.%X.decl [concrete = constants.%X] // CHECK:STDOUT: %k: ref %X = ref_binding k, %k.var // CHECK:STDOUT: %Destroy.Op.bound.loc12: = bound_method %k.var, constants.%Destroy.Op // CHECK:STDOUT: %Destroy.Op.call.loc12: init %empty_tuple.type = call %Destroy.Op.bound.loc12(%k.var) // CHECK:STDOUT: %Destroy.Op.bound.loc11: = bound_method %j.var, constants.%Destroy.Op // CHECK:STDOUT: %Destroy.Op.call.loc11: init %empty_tuple.type = call %Destroy.Op.bound.loc11(%j.var) // CHECK:STDOUT: %Destroy.Op.bound.loc10: = bound_method %i.var, constants.%Destroy.Op // CHECK:STDOUT: %Destroy.Op.call.loc10: init %empty_tuple.type = call %Destroy.Op.bound.loc10(%i.var) // CHECK:STDOUT: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @Destroy.Op(%self.param: ref %X) = "no_op"; // CHECK:STDOUT: // CHECK:STDOUT: --- unsafe_remove_partial.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %X: type = class_type @X [concrete] // CHECK:STDOUT: %.4b5: type = partial_type %X [concrete] // CHECK:STDOUT: %ptr.314: type = ptr_type %.4b5 [concrete] // CHECK:STDOUT: %pattern_type.05f: type = pattern_type %X [concrete] // CHECK:STDOUT: %ptr.2a9: type = ptr_type %X [concrete] // CHECK:STDOUT: %pattern_type.37f: type = pattern_type %ptr.2a9 [concrete] // CHECK:STDOUT: %reference.var: ref %X = var file.%reference.var_patt [concrete] // CHECK:STDOUT: %addr.9cd: %ptr.2a9 = addr_of %reference.var [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @Use() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %v.patt: %pattern_type.05f = value_binding_pattern v [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %value.ref: %.4b5 = name_ref value, file.%value // CHECK:STDOUT: %X.ref.loc14_37: type = name_ref X, file.%X.decl [concrete = constants.%X] // CHECK:STDOUT: %.loc14_34.1: %X = as_compatible %value.ref // CHECK:STDOUT: %.loc14_34.2: %X = converted %value.ref, %.loc14_34.1 // CHECK:STDOUT: %X.ref.loc14_17: type = name_ref X, file.%X.decl [concrete = constants.%X] // CHECK:STDOUT: %v: %X = value_binding v, %.loc14_34.2 // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %a.patt: %pattern_type.37f = value_binding_pattern a [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %reference.ref: ref %.4b5 = name_ref reference, file.%reference [concrete = file.%reference.var] // CHECK:STDOUT: %X.ref.loc15_44: type = name_ref X, file.%X.decl [concrete = constants.%X] // CHECK:STDOUT: %.loc15_41.1: ref %X = as_compatible %reference.ref [concrete = constants.%reference.var] // CHECK:STDOUT: %.loc15_41.2: ref %X = converted %reference.ref, %.loc15_41.1 [concrete = constants.%reference.var] // CHECK:STDOUT: %addr: %ptr.2a9 = addr_of %.loc15_41.2 [concrete = constants.%addr.9cd] // CHECK:STDOUT: %.loc15_18: type = splice_block %ptr.loc15 [concrete = constants.%ptr.2a9] { // CHECK:STDOUT: %X.ref.loc15_17: type = name_ref X, file.%X.decl [concrete = constants.%X] // CHECK:STDOUT: %ptr.loc15: type = ptr_type %X.ref.loc15_17 [concrete = constants.%ptr.2a9] // CHECK:STDOUT: } // CHECK:STDOUT: %a: %ptr.2a9 = value_binding a, %addr // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %b.patt: %pattern_type.37f = value_binding_pattern b [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %ptr.ref: %ptr.314 = name_ref ptr, file.%ptr.loc10_5 // CHECK:STDOUT: %X.ref.loc16_36: type = name_ref X, file.%X.decl [concrete = constants.%X] // CHECK:STDOUT: %ptr.loc16_37: type = ptr_type %X.ref.loc16_36 [concrete = constants.%ptr.2a9] // CHECK:STDOUT: %.loc16_33.1: %ptr.2a9 = as_compatible %ptr.ref // CHECK:STDOUT: %.loc16_33.2: %ptr.2a9 = converted %ptr.ref, %.loc16_33.1 // CHECK:STDOUT: %.loc16_18: type = splice_block %ptr.loc16_18 [concrete = constants.%ptr.2a9] { // CHECK:STDOUT: %X.ref.loc16_17: type = name_ref X, file.%X.decl [concrete = constants.%X] // CHECK:STDOUT: %ptr.loc16_18: type = ptr_type %X.ref.loc16_17 [concrete = constants.%ptr.2a9] // CHECK:STDOUT: } // CHECK:STDOUT: %b: %ptr.2a9 = value_binding b, %.loc16_33.2 // CHECK:STDOUT: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @__global_init() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: // CHECK:STDOUT: } // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/as/unsafe_as.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/unformed.carbon // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/as/unsafe_as.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/as/unsafe_as.carbon // --- fail_no_conversion.carbon library "[[@TEST_NAME]]"; class A {}; class B {}; fn Convert(a: A) -> B { // CHECK:STDERR: fail_no_conversion.carbon:[[@LINE+7]]:10: error: cannot convert expression of type `A` to `B` with `unsafe as` [ConversionFailure] // CHECK:STDERR: return a unsafe as B; // CHECK:STDERR: ^~~~~~~~~~~~~ // CHECK:STDERR: fail_no_conversion.carbon:[[@LINE+4]]:10: note: type `A` does not implement interface `Core.UnsafeAs(B)` [MissingImplInMemberAccessInContext] // CHECK:STDERR: return a unsafe as B; // CHECK:STDERR: ^~~~~~~~~~~~~ // CHECK:STDERR: return a unsafe as B; } ================================================ FILE: toolchain/check/testdata/as/var_init.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/convert.carbon // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/as/var_init.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/as/var_init.carbon // --- var_init.carbon library "[[@TEST_NAME]]"; class X { impl () as Core.ImplicitAs(X) { fn Convert[unused self: ()]() -> X { return {}; } } } fn Convert(unused t: ()) { //@dump-sem-ir-begin var unused x: X = (); //@dump-sem-ir-end } // CHECK:STDOUT: --- var_init.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %X: type = class_type @X [concrete] // CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] // CHECK:STDOUT: %empty_tuple: %empty_tuple.type = tuple_value () [concrete] // CHECK:STDOUT: %ImplicitAs.type.062: type = facet_type <@ImplicitAs, @ImplicitAs(%X)> [concrete] // CHECK:STDOUT: %ImplicitAs.impl_witness: = impl_witness @empty_tuple.type.as.ImplicitAs.impl.%ImplicitAs.impl_witness_table [concrete] // CHECK:STDOUT: %pattern_type.05f: type = pattern_type %X [concrete] // CHECK:STDOUT: %empty_tuple.type.as.ImplicitAs.impl.Convert.type: type = fn_type @empty_tuple.type.as.ImplicitAs.impl.Convert [concrete] // CHECK:STDOUT: %empty_tuple.type.as.ImplicitAs.impl.Convert: %empty_tuple.type.as.ImplicitAs.impl.Convert.type = struct_value () [concrete] // CHECK:STDOUT: %ImplicitAs.facet: %ImplicitAs.type.062 = facet_value %empty_tuple.type, (%ImplicitAs.impl_witness) [concrete] // CHECK:STDOUT: %ImplicitAs.WithSelf.Convert.type.73b: type = fn_type @ImplicitAs.WithSelf.Convert, @ImplicitAs.WithSelf(%X, %ImplicitAs.facet) [concrete] // CHECK:STDOUT: %.1bd: type = fn_type_with_self_type %ImplicitAs.WithSelf.Convert.type.73b, %ImplicitAs.facet [concrete] // CHECK:STDOUT: %empty_tuple.type.as.ImplicitAs.impl.Convert.bound: = bound_method %empty_tuple, %empty_tuple.type.as.ImplicitAs.impl.Convert [concrete] // CHECK:STDOUT: %Destroy.Op.type: type = fn_type @Destroy.Op [concrete] // CHECK:STDOUT: %Destroy.Op: %Destroy.Op.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @Convert(%t.param: %empty_tuple.type) { // CHECK:STDOUT: !entry: // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %x.patt: %pattern_type.05f = ref_binding_pattern x [concrete] // CHECK:STDOUT: %x.var_patt: %pattern_type.05f = var_pattern %x.patt [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %x.var: ref %X = var %x.var_patt // CHECK:STDOUT: %.loc12_22.1: %empty_tuple.type = tuple_literal () [concrete = constants.%empty_tuple] // CHECK:STDOUT: %impl.elem0: %.1bd = impl_witness_access constants.%ImplicitAs.impl_witness, element0 [concrete = constants.%empty_tuple.type.as.ImplicitAs.impl.Convert] // CHECK:STDOUT: %bound_method: = bound_method %.loc12_22.1, %impl.elem0 [concrete = constants.%empty_tuple.type.as.ImplicitAs.impl.Convert.bound] // CHECK:STDOUT: %.loc12_3.1: ref %X = splice_block %x.var {} // CHECK:STDOUT: %empty_tuple: %empty_tuple.type = tuple_value () [concrete = constants.%empty_tuple] // CHECK:STDOUT: %.loc12_22.2: %empty_tuple.type = converted %.loc12_22.1, %empty_tuple [concrete = constants.%empty_tuple] // CHECK:STDOUT: %empty_tuple.type.as.ImplicitAs.impl.Convert.call: init %X to %.loc12_3.1 = call %bound_method(%.loc12_22.2) // CHECK:STDOUT: %.loc12_3.2: init %X = converted %.loc12_22.1, %empty_tuple.type.as.ImplicitAs.impl.Convert.call // CHECK:STDOUT: assign %x.var, %.loc12_3.2 // CHECK:STDOUT: %X.ref: type = name_ref X, file.%X.decl [concrete = constants.%X] // CHECK:STDOUT: %x: ref %X = ref_binding x, %x.var // CHECK:STDOUT: %Destroy.Op.bound: = bound_method %x.var, constants.%Destroy.Op // CHECK:STDOUT: %Destroy.Op.call: init %empty_tuple.type = call %Destroy.Op.bound(%x.var) // CHECK:STDOUT: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @Destroy.Op(%self.param: ref %X) = "no_op"; // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/basics/dump_prelude.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/full.carbon // // ARGS: compile --phase=check --dump-sem-ir %s // // NOAUTOUPDATE // SET-CHECK-SUBSET // // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/basics/dump_prelude.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/basics/dump_prelude.carbon // CHECK:STDOUT: interface @Copy { // CHECK:STDOUT: interface @Destroy { // CHECK:STDOUT: fn @MakeInt(%size.param: Core.IntLiteral) -> out %return.param: type = "int.make_type_signed"; ================================================ FILE: toolchain/check/testdata/basics/dump_sem_ir_ranges.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/destroy.carbon // // The default behavior should be `--dump-sem-ir-ranges=if-present`, although // tests set it to `only`. This explicitly tests the default of the toolchain, // not of file_test. // ARGS: compile --phase=check --dump-sem-ir %s // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/basics/dump_sem_ir_ranges.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/basics/dump_sem_ir_ranges.carbon // --- function.carbon library "[[@TEST_NAME]]"; fn A() { var unused a: () = (); } fn B() -> () { var b: () = (); //@dump-sem-ir-begin b = A(); //@dump-sem-ir-end return b; } //@dump-sem-ir-begin fn C() -> () { var c: () = (); c = B(); return c; } //@dump-sem-ir-end // --- class.carbon library "[[@TEST_NAME]]"; class A { fn F(); //@dump-sem-ir-begin fn G(); //@dump-sem-ir-end } class B { fn H(); } //@dump-sem-ir-begin class C { fn I(); //@dump-sem-ir-end fn J(); } // --- call_params.carbon library "[[@TEST_NAME]]"; fn F(a: (), b: (), c: ()); fn A(); fn B(); fn C(); fn G() { F( //@dump-sem-ir-begin A(), //@dump-sem-ir-end B(), //@dump-sem-ir-begin C() //@dump-sem-ir-end ); } // --- file_without_ranges.carbon library "[[@TEST_NAME]]"; fn F(); // --- explicit_dump_file.carbon library "[[@TEST_NAME]]"; //@include-in-dumps // CHECK:STDOUT: --- function.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %A.type: type = fn_type @A [concrete] // CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] // CHECK:STDOUT: %A: %A.type = struct_value () [concrete] // CHECK:STDOUT: %empty_tuple: %empty_tuple.type = tuple_value () [concrete] // CHECK:STDOUT: %pattern_type.cb1: type = pattern_type %empty_tuple.type [concrete] // CHECK:STDOUT: %Destroy.Op.type: type = fn_type @Destroy.Op [concrete] // CHECK:STDOUT: %Destroy.Op: %Destroy.Op.type = struct_value () [concrete] // CHECK:STDOUT: %.262: Core.Form = init_form %empty_tuple.type [concrete] // CHECK:STDOUT: %B.type: type = fn_type @B [concrete] // CHECK:STDOUT: %B: %B.type = struct_value () [concrete] // CHECK:STDOUT: %C.type: type = fn_type @C [concrete] // CHECK:STDOUT: %C: %C.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: %C.decl: %C.type = fn_decl @C [concrete = constants.%C] { // CHECK:STDOUT: %return.patt: %pattern_type.cb1 = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: %pattern_type.cb1 = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %.loc17_12.1: %empty_tuple.type = tuple_literal () [concrete = constants.%empty_tuple] // CHECK:STDOUT: %.loc17_12.2: type = converted %.loc17_12.1, constants.%empty_tuple.type [concrete = constants.%empty_tuple.type] // CHECK:STDOUT: %.loc17_12.3: Core.Form = init_form %.loc17_12.2 [concrete = constants.%.262] // CHECK:STDOUT: %return.param: ref %empty_tuple.type = out_param call_param0 // CHECK:STDOUT: %return: ref %empty_tuple.type = return_slot %return.param // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @B() -> out %return.param: %empty_tuple.type { // CHECK:STDOUT: !entry: // CHECK:STDOUT: // CHECK:STDOUT: %b.ref.loc11: ref %empty_tuple.type = name_ref b, %b // CHECK:STDOUT: %A.ref: %A.type = name_ref A, file.%A.decl [concrete = constants.%A] // CHECK:STDOUT: %A.call: init %empty_tuple.type = call %A.ref() // CHECK:STDOUT: assign %b.ref.loc11, %A.call // CHECK:STDOUT: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @C() -> out %return.param: %empty_tuple.type { // CHECK:STDOUT: !entry: // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %c.patt: %pattern_type.cb1 = ref_binding_pattern c [concrete] // CHECK:STDOUT: %c.var_patt: %pattern_type.cb1 = var_pattern %c.patt [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %c.var: ref %empty_tuple.type = var %c.var_patt // CHECK:STDOUT: %.loc18_16.1: %empty_tuple.type = tuple_literal () [concrete = constants.%empty_tuple] // CHECK:STDOUT: %.loc18_16.2: init %empty_tuple.type = tuple_init () [concrete = constants.%empty_tuple] // CHECK:STDOUT: %.loc18_3: init %empty_tuple.type = converted %.loc18_16.1, %.loc18_16.2 [concrete = constants.%empty_tuple] // CHECK:STDOUT: assign %c.var, %.loc18_3 // CHECK:STDOUT: %.loc18_11.1: type = splice_block %.loc18_11.3 [concrete = constants.%empty_tuple.type] { // CHECK:STDOUT: %.loc18_11.2: %empty_tuple.type = tuple_literal () [concrete = constants.%empty_tuple] // CHECK:STDOUT: %.loc18_11.3: type = converted %.loc18_11.2, constants.%empty_tuple.type [concrete = constants.%empty_tuple.type] // CHECK:STDOUT: } // CHECK:STDOUT: %c: ref %empty_tuple.type = ref_binding c, %c.var // CHECK:STDOUT: %c.ref.loc19: ref %empty_tuple.type = name_ref c, %c // CHECK:STDOUT: %B.ref: %B.type = name_ref B, file.%B.decl [concrete = constants.%B] // CHECK:STDOUT: %B.call: init %empty_tuple.type = call %B.ref() // CHECK:STDOUT: assign %c.ref.loc19, %B.call // CHECK:STDOUT: %c.ref.loc20: ref %empty_tuple.type = name_ref c, %c // CHECK:STDOUT: %.loc20_10: init %empty_tuple.type = tuple_init () [concrete = constants.%empty_tuple] // CHECK:STDOUT: %.loc20_11: init %empty_tuple.type = converted %c.ref.loc20, %.loc20_10 [concrete = constants.%empty_tuple] // CHECK:STDOUT: %Destroy.Op.bound: = bound_method %c.var, constants.%Destroy.Op // CHECK:STDOUT: %Destroy.Op.call: init %empty_tuple.type = call %Destroy.Op.bound(%c.var) // CHECK:STDOUT: return %.loc20_11 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- class.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %A: type = class_type @A [concrete] // CHECK:STDOUT: %A.G.type: type = fn_type @A.G [concrete] // CHECK:STDOUT: %A.G: %A.G.type = struct_value () [concrete] // CHECK:STDOUT: %C: type = class_type @C [concrete] // CHECK:STDOUT: %C.I.type: type = fn_type @C.I [concrete] // CHECK:STDOUT: %C.I: %C.I.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: %C.decl: type = class_decl @C [concrete = constants.%C] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @A { // CHECK:STDOUT: // CHECK:STDOUT: %A.G.decl: %A.G.type = fn_decl @A.G [concrete = constants.%A.G] {} {} // CHECK:STDOUT: // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%A // CHECK:STDOUT: .F = %A.F.decl // CHECK:STDOUT: .G = %A.G.decl // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @C { // CHECK:STDOUT: %C.I.decl: %C.I.type = fn_decl @C.I [concrete = constants.%C.I] {} {} // CHECK:STDOUT: // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%C // CHECK:STDOUT: .I = %C.I.decl // CHECK:STDOUT: .J = %C.J.decl // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @A.G(); // CHECK:STDOUT: // CHECK:STDOUT: fn @C.I(); // CHECK:STDOUT: // CHECK:STDOUT: --- call_params.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] // CHECK:STDOUT: %empty_tuple: %empty_tuple.type = tuple_value () [concrete] // CHECK:STDOUT: %A.type: type = fn_type @A [concrete] // CHECK:STDOUT: %A: %A.type = struct_value () [concrete] // CHECK:STDOUT: %C.type: type = fn_type @C [concrete] // CHECK:STDOUT: %C: %C.type = struct_value () [concrete] // CHECK:STDOUT: %Destroy.Op.type: type = fn_type @Destroy.Op [concrete] // CHECK:STDOUT: %Destroy.Op: %Destroy.Op.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @G() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: // CHECK:STDOUT: %A.ref: %A.type = name_ref A, file.%A.decl [concrete = constants.%A] // CHECK:STDOUT: %A.call: init %empty_tuple.type = call %A.ref() // CHECK:STDOUT: // CHECK:STDOUT: %C.ref: %C.type = name_ref C, file.%C.decl [concrete = constants.%C] // CHECK:STDOUT: %C.call: init %empty_tuple.type = call %C.ref() // CHECK:STDOUT: %.loc13_7.1: ref %empty_tuple.type = temporary_storage // CHECK:STDOUT: %.loc13_7.2: ref %empty_tuple.type = temporary %.loc13_7.1, %A.call // CHECK:STDOUT: %tuple.loc13: %empty_tuple.type = tuple_value () [concrete = constants.%empty_tuple] // CHECK:STDOUT: %.loc13_7.3: %empty_tuple.type = converted %A.call, %tuple.loc13 [concrete = constants.%empty_tuple] // CHECK:STDOUT: // CHECK:STDOUT: %.loc17_7.1: ref %empty_tuple.type = temporary_storage // CHECK:STDOUT: %.loc17_7.2: ref %empty_tuple.type = temporary %.loc17_7.1, %C.call // CHECK:STDOUT: %tuple.loc17: %empty_tuple.type = tuple_value () [concrete = constants.%empty_tuple] // CHECK:STDOUT: %.loc17_7.3: %empty_tuple.type = converted %C.call, %tuple.loc17 [concrete = constants.%empty_tuple] // CHECK:STDOUT: // CHECK:STDOUT: %Destroy.Op.bound.loc17: = bound_method %.loc17_7.2, constants.%Destroy.Op // CHECK:STDOUT: %Destroy.Op.call.loc17: init %empty_tuple.type = call %Destroy.Op.bound.loc17(%.loc17_7.2) // CHECK:STDOUT: // CHECK:STDOUT: %Destroy.Op.bound.loc13: = bound_method %.loc13_7.2, constants.%Destroy.Op // CHECK:STDOUT: %Destroy.Op.call.loc13: init %empty_tuple.type = call %Destroy.Op.bound.loc13(%.loc13_7.2) // CHECK:STDOUT: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @Destroy.Op(%self.param: ref %empty_tuple.type) = "no_op"; // CHECK:STDOUT: // CHECK:STDOUT: --- file_without_ranges.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %F.type: type = fn_type @F [concrete] // CHECK:STDOUT: %F: %F.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .F = %F.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %F.decl: %F.type = fn_decl @F [concrete = constants.%F] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @F(); // CHECK:STDOUT: // CHECK:STDOUT: --- explicit_dump_file.carbon // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: } // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/basics/dump_sem_ir_ranges_ignore.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/none.carbon // EXTRA-ARGS: --dump-sem-ir-ranges=ignore // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/basics/dump_sem_ir_ranges_ignore.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/basics/dump_sem_ir_ranges_ignore.carbon // --- with-range.carbon library "[[@TEST_NAME]]"; //@dump-sem-ir-begin fn F(); //@dump-sem-ir-end // --- without-range.carbon library "[[@TEST_NAME]]"; fn F(); // --- explicit_dump_file.carbon library "[[@TEST_NAME]]"; //@include-in-dumps // CHECK:STDOUT: --- with-range.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %F.type: type = fn_type @F [concrete] // CHECK:STDOUT: %F: %F.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .F = %F.decl // CHECK:STDOUT: } // CHECK:STDOUT: %F.decl: %F.type = fn_decl @F [concrete = constants.%F] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @F(); // CHECK:STDOUT: // CHECK:STDOUT: --- without-range.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %F.type: type = fn_type @F [concrete] // CHECK:STDOUT: %F: %F.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .F = %F.decl // CHECK:STDOUT: } // CHECK:STDOUT: %F.decl: %F.type = fn_decl @F [concrete = constants.%F] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @F(); // CHECK:STDOUT: // CHECK:STDOUT: --- explicit_dump_file.carbon // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] {} // CHECK:STDOUT: } // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/basics/dump_sem_ir_ranges_only.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // This tests `--dump-sem-ir-range=only` behavior, which is set implicitly by // file_test. // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/none.carbon // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/basics/dump_sem_ir_ranges_only.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/basics/dump_sem_ir_ranges_only.carbon // --- with-range.carbon library "[[@TEST_NAME]]"; //@dump-sem-ir-begin fn F(); //@dump-sem-ir-end // --- without-range.carbon library "[[@TEST_NAME]]"; fn F(); // --- explicit_dump_file.carbon library "[[@TEST_NAME]]"; //@include-in-dumps // CHECK:STDOUT: --- with-range.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %F.type: type = fn_type @F [concrete] // CHECK:STDOUT: %F: %F.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: %F.decl: %F.type = fn_decl @F [concrete = constants.%F] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @F(); // CHECK:STDOUT: // CHECK:STDOUT: --- explicit_dump_file.carbon // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] {} // CHECK:STDOUT: } // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/basics/duplicate_name_same_line.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/destroy.carbon // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/basics/duplicate_name_same_line.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/basics/duplicate_name_same_line.carbon fn A() { if (true) { // This declares `n` in two different scopes, it's just showing the IR // behavior of having both on the same line. //@dump-sem-ir-begin var unused n: () = (); } else { var unused n: () = (); //@dump-sem-ir-end } } // CHECK:STDOUT: --- duplicate_name_same_line.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] // CHECK:STDOUT: %empty_tuple: %empty_tuple.type = tuple_value () [concrete] // CHECK:STDOUT: %pattern_type.cb1: type = pattern_type %empty_tuple.type [concrete] // CHECK:STDOUT: %Destroy.Op.type: type = fn_type @Destroy.Op [concrete] // CHECK:STDOUT: %Destroy.Op: %Destroy.Op.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @A() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: // CHECK:STDOUT: // CHECK:STDOUT: !if.then: // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %n.patt.loc18_16: %pattern_type.cb1 = ref_binding_pattern n [concrete] // CHECK:STDOUT: %n.var_patt.loc18_5: %pattern_type.cb1 = var_pattern %n.patt.loc18_16 [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %n.var.loc18_5: ref %empty_tuple.type = var %n.var_patt.loc18_5 // CHECK:STDOUT: %.loc18_25.1: %empty_tuple.type = tuple_literal () [concrete = constants.%empty_tuple] // CHECK:STDOUT: %.loc18_25.2: init %empty_tuple.type = tuple_init () [concrete = constants.%empty_tuple] // CHECK:STDOUT: %.loc18_5: init %empty_tuple.type = converted %.loc18_25.1, %.loc18_25.2 [concrete = constants.%empty_tuple] // CHECK:STDOUT: assign %n.var.loc18_5, %.loc18_5 // CHECK:STDOUT: %.loc18_20.1: type = splice_block %.loc18_20.3 [concrete = constants.%empty_tuple.type] { // CHECK:STDOUT: %.loc18_20.2: %empty_tuple.type = tuple_literal () [concrete = constants.%empty_tuple] // CHECK:STDOUT: %.loc18_20.3: type = converted %.loc18_20.2, constants.%empty_tuple.type [concrete = constants.%empty_tuple.type] // CHECK:STDOUT: } // CHECK:STDOUT: %n.loc18_16: ref %empty_tuple.type = ref_binding n, %n.var.loc18_5 // CHECK:STDOUT: // CHECK:STDOUT: // CHECK:STDOUT: !if.else: // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %n.patt.loc18_48: %pattern_type.cb1 = ref_binding_pattern n [concrete] // CHECK:STDOUT: %n.var_patt.loc18_37: %pattern_type.cb1 = var_pattern %n.patt.loc18_48 [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %n.var.loc18_37: ref %empty_tuple.type = var %n.var_patt.loc18_37 // CHECK:STDOUT: %.loc18_57.1: %empty_tuple.type = tuple_literal () [concrete = constants.%empty_tuple] // CHECK:STDOUT: %.loc18_57.2: init %empty_tuple.type = tuple_init () [concrete = constants.%empty_tuple] // CHECK:STDOUT: %.loc18_37: init %empty_tuple.type = converted %.loc18_57.1, %.loc18_57.2 [concrete = constants.%empty_tuple] // CHECK:STDOUT: assign %n.var.loc18_37, %.loc18_37 // CHECK:STDOUT: %.loc18_52.1: type = splice_block %.loc18_52.3 [concrete = constants.%empty_tuple.type] { // CHECK:STDOUT: %.loc18_52.2: %empty_tuple.type = tuple_literal () [concrete = constants.%empty_tuple] // CHECK:STDOUT: %.loc18_52.3: type = converted %.loc18_52.2, constants.%empty_tuple.type [concrete = constants.%empty_tuple.type] // CHECK:STDOUT: } // CHECK:STDOUT: %n.loc18_48: ref %empty_tuple.type = ref_binding n, %n.var.loc18_37 // CHECK:STDOUT: // CHECK:STDOUT: // CHECK:STDOUT: !if.done: // CHECK:STDOUT: %Destroy.Op.bound.loc18_37: = bound_method %n.var.loc18_37, constants.%Destroy.Op // CHECK:STDOUT: %Destroy.Op.call.loc18_37: init %empty_tuple.type = call %Destroy.Op.bound.loc18_37(%n.var.loc18_37) // CHECK:STDOUT: %Destroy.Op.bound.loc18_5: = bound_method %n.var.loc18_5, constants.%Destroy.Op // CHECK:STDOUT: %Destroy.Op.call.loc18_5: init %empty_tuple.type = call %Destroy.Op.bound.loc18_5(%n.var.loc18_5) // CHECK:STDOUT: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @Destroy.Op(%self.param: ref %empty_tuple.type) = "no_op"; // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/basics/empty.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/none.carbon // EXTRA-ARGS: --dump-sem-ir-ranges=if-present // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/basics/empty.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/basics/empty.carbon // --- empty.carbon // --- empty_decl.carbon library "[[@TEST_NAME]]"; ; // CHECK:STDOUT: --- empty.carbon // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- empty_decl.carbon // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] {} // CHECK:STDOUT: } // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/basics/include_in_dumps.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/convert.carbon // // This explicitly excludes some files, then set ranges to `if-present` so that // we can see imports (particularly of `impl`s) from files that would be excluded without // `//@include-in-dumps`. // EXTRA-ARGS: --exclude-dump-file-prefix=exclude/ --dump-sem-ir-ranges=if-present // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/basics/include_in_dumps.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/basics/include_in_dumps.carbon // --- exclude/included.carbon library "[[@TEST_NAME]]"; //@include-in-dumps interface I {} // --- exclude/included_with_range.carbon library "[[@TEST_NAME]]"; //@include-in-dumps //@dump-sem-ir-begin interface I { fn Op[unused self: Self](); } //@dump-sem-ir-end class C { impl C as I { fn Op[unused self: Self]() {} } } // --- import_included.carbon library "[[@TEST_NAME]]"; import library "included_with_range"; fn F(c: C) { c.(I.Op)(); } // --- exclude/excluded.carbon library "[[@TEST_NAME]]"; interface I {} // --- exclude/excluded_with_range.carbon library "[[@TEST_NAME]]"; //@dump-sem-ir-begin interface I { fn Op[unused self: Self](); } //@dump-sem-ir-end class C { impl C as I { fn Op[unused self: Self]() {} } } // --- import_excluded.carbon library "[[@TEST_NAME]]"; import library "excluded_with_range"; fn F(c: C) { c.(I.Op)(); } // CHECK:STDOUT: --- exclude/included.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %I.type: type = facet_type <@I> [concrete] // CHECK:STDOUT: %Self: %I.type = symbolic_binding Self, 0 [symbolic] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .I = %I.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %I.decl: type = interface_decl @I [concrete = constants.%I.type] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: interface @I { // CHECK:STDOUT: %Self: %I.type = symbolic_binding Self, 0 [symbolic = constants.%Self] // CHECK:STDOUT: %I.WithSelf.decl = interface_with_self_decl @I [concrete] // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = %Self // CHECK:STDOUT: witness = () // CHECK:STDOUT: // CHECK:STDOUT: !requires: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @I.WithSelf(constants.%Self) {} // CHECK:STDOUT: // CHECK:STDOUT: --- exclude/included_with_range.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %I.type: type = facet_type <@I> [concrete] // CHECK:STDOUT: %Self: %I.type = symbolic_binding Self, 0 [symbolic] // CHECK:STDOUT: %Self.binding.as_type: type = symbolic_binding_type Self, 0, %Self [symbolic] // CHECK:STDOUT: %pattern_type.fa0: type = pattern_type %Self.binding.as_type [symbolic] // CHECK:STDOUT: %I.WithSelf.Op.type.71c: type = fn_type @I.WithSelf.Op, @I.WithSelf(%Self) [symbolic] // CHECK:STDOUT: %I.WithSelf.Op.ae1: %I.WithSelf.Op.type.71c = struct_value () [symbolic] // CHECK:STDOUT: %I.assoc_type: type = assoc_entity_type @I [concrete] // CHECK:STDOUT: %assoc0: %I.assoc_type = assoc_entity element0, @I.WithSelf.%I.WithSelf.Op.decl [concrete] // CHECK:STDOUT: %C: type = class_type @C [concrete] // CHECK:STDOUT: %I.impl_witness: = impl_witness @C.as.I.impl.%I.impl_witness_table [concrete] // CHECK:STDOUT: %pattern_type.7c7: type = pattern_type %C [concrete] // CHECK:STDOUT: %I.facet: %I.type = facet_value %C, (%I.impl_witness) [concrete] // CHECK:STDOUT: %I.WithSelf.Op.type.c9a: type = fn_type @I.WithSelf.Op, @I.WithSelf(%I.facet) [concrete] // CHECK:STDOUT: %I.WithSelf.Op.b3e: %I.WithSelf.Op.type.c9a = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: %I.decl: type = interface_decl @I [concrete = constants.%I.type] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: interface @I { // CHECK:STDOUT: %Self: %I.type = symbolic_binding Self, 0 [symbolic = constants.%Self] // CHECK:STDOUT: %I.WithSelf.decl = interface_with_self_decl @I [concrete] // CHECK:STDOUT: // CHECK:STDOUT: !with Self: // CHECK:STDOUT: %I.WithSelf.Op.decl: @I.WithSelf.%I.WithSelf.Op.type (%I.WithSelf.Op.type.71c) = fn_decl @I.WithSelf.Op [symbolic = @I.WithSelf.%I.WithSelf.Op (constants.%I.WithSelf.Op.ae1)] { // CHECK:STDOUT: %self.patt: @I.WithSelf.Op.%pattern_type (%pattern_type.fa0) = value_binding_pattern self [concrete] // CHECK:STDOUT: %self.param_patt: @I.WithSelf.Op.%pattern_type (%pattern_type.fa0) = value_param_pattern %self.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %self.param: @I.WithSelf.Op.%Self.binding.as_type (%Self.binding.as_type) = value_param call_param0 // CHECK:STDOUT: %.loc8_22.1: type = splice_block %.loc8_22.2 [symbolic = %Self.binding.as_type (constants.%Self.binding.as_type)] { // CHECK:STDOUT: %Self.ref: %I.type = name_ref Self, @I.%Self [symbolic = %Self (constants.%Self)] // CHECK:STDOUT: %Self.as_type: type = facet_access_type %Self.ref [symbolic = %Self.binding.as_type (constants.%Self.binding.as_type)] // CHECK:STDOUT: %.loc8_22.2: type = converted %Self.ref, %Self.as_type [symbolic = %Self.binding.as_type (constants.%Self.binding.as_type)] // CHECK:STDOUT: } // CHECK:STDOUT: %self: @I.WithSelf.Op.%Self.binding.as_type (%Self.binding.as_type) = value_binding self, %self.param // CHECK:STDOUT: } // CHECK:STDOUT: %assoc0: %I.assoc_type = assoc_entity element0, %I.WithSelf.Op.decl [concrete = constants.%assoc0] // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = %Self // CHECK:STDOUT: .Op = @I.WithSelf.%assoc0 // CHECK:STDOUT: witness = (@I.WithSelf.%I.WithSelf.Op.decl) // CHECK:STDOUT: // CHECK:STDOUT: !requires: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic fn @I.WithSelf.Op(@I.%Self: %I.type) { // CHECK:STDOUT: %Self: %I.type = symbolic_binding Self, 0 [symbolic = %Self (constants.%Self)] // CHECK:STDOUT: %Self.binding.as_type: type = symbolic_binding_type Self, 0, %Self [symbolic = %Self.binding.as_type (constants.%Self.binding.as_type)] // CHECK:STDOUT: %pattern_type: type = pattern_type %Self.binding.as_type [symbolic = %pattern_type (constants.%pattern_type.fa0)] // CHECK:STDOUT: // CHECK:STDOUT: fn(%self.param: @I.WithSelf.Op.%Self.binding.as_type (%Self.binding.as_type)); // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @I.WithSelf(constants.%Self) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: %Self => constants.%Self // CHECK:STDOUT: %I.WithSelf.Op.type => constants.%I.WithSelf.Op.type.71c // CHECK:STDOUT: %I.WithSelf.Op => constants.%I.WithSelf.Op.ae1 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @I.WithSelf.Op(constants.%Self) { // CHECK:STDOUT: %Self => constants.%Self // CHECK:STDOUT: %Self.binding.as_type => constants.%Self.binding.as_type // CHECK:STDOUT: %pattern_type => constants.%pattern_type.fa0 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @I.WithSelf(constants.%I.facet) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: %Self => constants.%I.facet // CHECK:STDOUT: %I.WithSelf.Op.type => constants.%I.WithSelf.Op.type.c9a // CHECK:STDOUT: %I.WithSelf.Op => constants.%I.WithSelf.Op.b3e // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @I.WithSelf.Op(constants.%I.facet) { // CHECK:STDOUT: %Self => constants.%I.facet // CHECK:STDOUT: %Self.binding.as_type => constants.%C // CHECK:STDOUT: %pattern_type => constants.%pattern_type.7c7 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- import_included.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %C: type = class_type @C [concrete] // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %complete_type: = complete_type_witness %empty_struct_type [concrete] // CHECK:STDOUT: %pattern_type.7c7: type = pattern_type %C [concrete] // CHECK:STDOUT: %F.type: type = fn_type @F [concrete] // CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] // CHECK:STDOUT: %F: %F.type = struct_value () [concrete] // CHECK:STDOUT: %I.type: type = facet_type <@I> [concrete] // CHECK:STDOUT: %Self: %I.type = symbolic_binding Self, 0 [symbolic] // CHECK:STDOUT: %I.WithSelf.Op.type.f73: type = fn_type @I.WithSelf.Op, @I.WithSelf(%Self) [symbolic] // CHECK:STDOUT: %I.WithSelf.Op.f53: %I.WithSelf.Op.type.f73 = struct_value () [symbolic] // CHECK:STDOUT: %Self.binding.as_type: type = symbolic_binding_type Self, 0, %Self [symbolic] // CHECK:STDOUT: %pattern_type.422: type = pattern_type %Self.binding.as_type [symbolic] // CHECK:STDOUT: %I.assoc_type: type = assoc_entity_type @I [concrete] // CHECK:STDOUT: %assoc0: %I.assoc_type = assoc_entity element0, imports.%Main.import_ref.e26 [concrete] // CHECK:STDOUT: %I.impl_witness: = impl_witness imports.%I.impl_witness_table [concrete] // CHECK:STDOUT: %I.facet: %I.type = facet_value %C, (%I.impl_witness) [concrete] // CHECK:STDOUT: %I.WithSelf.Op.type.537: type = fn_type @I.WithSelf.Op, @I.WithSelf(%I.facet) [concrete] // CHECK:STDOUT: %I.WithSelf.Op.a52: %I.WithSelf.Op.type.537 = struct_value () [concrete] // CHECK:STDOUT: %.c12: type = fn_type_with_self_type %I.WithSelf.Op.type.537, %I.facet [concrete] // CHECK:STDOUT: %C.as.I.impl.Op.type: type = fn_type @C.as.I.impl.Op [concrete] // CHECK:STDOUT: %C.as.I.impl.Op: %C.as.I.impl.Op.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Main.I: type = import_ref Main//included_with_range, I, loaded [concrete = constants.%I.type] // CHECK:STDOUT: %Main.C: type = import_ref Main//included_with_range, C, loaded [concrete = constants.%C] // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: %Main.import_ref.8f2: = import_ref Main//included_with_range, loc16_1, loaded [concrete = constants.%complete_type] // CHECK:STDOUT: %Main.import_ref.743 = import_ref Main//included_with_range, inst{{[0-9A-F]+}} [no loc], unloaded // CHECK:STDOUT: %Main.import_ref.d09: %I.assoc_type = import_ref Main//included_with_range, loc8_29, loaded [concrete = constants.%assoc0] // CHECK:STDOUT: %Main.Op = import_ref Main//included_with_range, Op, unloaded // CHECK:STDOUT: %Main.import_ref.2362f8.2: %I.type = import_ref Main//included_with_range, loc7_13, loaded [symbolic = constants.%Self] // CHECK:STDOUT: %Main.import_ref.c82 = import_ref Main//included_with_range, loc7_13, unloaded // CHECK:STDOUT: %Main.import_ref.e26: @I.WithSelf.%I.WithSelf.Op.type (%I.WithSelf.Op.type.f73) = import_ref Main//included_with_range, loc8_29, loaded [symbolic = @I.WithSelf.%I.WithSelf.Op (constants.%I.WithSelf.Op.f53)] // CHECK:STDOUT: %Main.import_ref.f72: = import_ref Main//included_with_range, loc13_15, loaded [concrete = constants.%I.impl_witness] // CHECK:STDOUT: %Main.import_ref.61c: type = import_ref Main//included_with_range, loc13_8, loaded [concrete = constants.%C] // CHECK:STDOUT: %Main.import_ref.72a: type = import_ref Main//included_with_range, loc13_13, loaded [concrete = constants.%I.type] // CHECK:STDOUT: %Main.import_ref.4bc: %C.as.I.impl.Op.type = import_ref Main//included_with_range, loc14_32, loaded [concrete = constants.%C.as.I.impl.Op] // CHECK:STDOUT: %I.impl_witness_table = impl_witness_table (%Main.import_ref.4bc), @C.as.I.impl [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .I = imports.%Main.I // CHECK:STDOUT: .C = imports.%Main.C // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .F = %F.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %default.import = import // CHECK:STDOUT: %F.decl: %F.type = fn_decl @F [concrete = constants.%F] { // CHECK:STDOUT: %c.patt: %pattern_type.7c7 = value_binding_pattern c [concrete] // CHECK:STDOUT: %c.param_patt: %pattern_type.7c7 = value_param_pattern %c.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %c.param: %C = value_param call_param0 // CHECK:STDOUT: %C.ref: type = name_ref C, imports.%Main.C [concrete = constants.%C] // CHECK:STDOUT: %c: %C = value_binding c, %c.param // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: interface @I [from "exclude/included_with_range.carbon"] { // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = imports.%Main.import_ref.c82 // CHECK:STDOUT: .Op = imports.%Main.import_ref.d09 // CHECK:STDOUT: witness = (imports.%Main.Op) // CHECK:STDOUT: // CHECK:STDOUT: !requires: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: impl @C.as.I.impl: imports.%Main.import_ref.61c as imports.%Main.import_ref.72a [from "exclude/included_with_range.carbon"] { // CHECK:STDOUT: !members: // CHECK:STDOUT: witness = imports.%Main.import_ref.f72 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @C [from "exclude/included_with_range.carbon"] { // CHECK:STDOUT: complete_type_witness = imports.%Main.import_ref.8f2 // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = imports.%Main.import_ref.743 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @F(%c.param: %C) { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %c.ref: %C = name_ref c, %c // CHECK:STDOUT: %I.ref: type = name_ref I, imports.%Main.I [concrete = constants.%I.type] // CHECK:STDOUT: %Op.ref: %I.assoc_type = name_ref Op, imports.%Main.import_ref.d09 [concrete = constants.%assoc0] // CHECK:STDOUT: %impl.elem0: %.c12 = impl_witness_access constants.%I.impl_witness, element0 [concrete = constants.%C.as.I.impl.Op] // CHECK:STDOUT: %bound_method: = bound_method %c.ref, %impl.elem0 // CHECK:STDOUT: %C.as.I.impl.Op.call: init %empty_tuple.type = call %bound_method(%c.ref) // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic fn @I.WithSelf.Op(imports.%Main.import_ref.2362f8.2: %I.type) [from "exclude/included_with_range.carbon"] { // CHECK:STDOUT: %Self: %I.type = symbolic_binding Self, 0 [symbolic = %Self (constants.%Self)] // CHECK:STDOUT: %Self.binding.as_type: type = symbolic_binding_type Self, 0, %Self [symbolic = %Self.binding.as_type (constants.%Self.binding.as_type)] // CHECK:STDOUT: %pattern_type: type = pattern_type %Self.binding.as_type [symbolic = %pattern_type (constants.%pattern_type.422)] // CHECK:STDOUT: // CHECK:STDOUT: fn; // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @C.as.I.impl.Op [from "exclude/included_with_range.carbon"]; // CHECK:STDOUT: // CHECK:STDOUT: specific @I.WithSelf(constants.%Self) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: %Self => constants.%Self // CHECK:STDOUT: %I.WithSelf.Op.type => constants.%I.WithSelf.Op.type.f73 // CHECK:STDOUT: %I.WithSelf.Op => constants.%I.WithSelf.Op.f53 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @I.WithSelf.Op(constants.%Self) { // CHECK:STDOUT: %Self => constants.%Self // CHECK:STDOUT: %Self.binding.as_type => constants.%Self.binding.as_type // CHECK:STDOUT: %pattern_type => constants.%pattern_type.422 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @I.WithSelf(constants.%I.facet) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: %Self => constants.%I.facet // CHECK:STDOUT: %I.WithSelf.Op.type => constants.%I.WithSelf.Op.type.537 // CHECK:STDOUT: %I.WithSelf.Op => constants.%I.WithSelf.Op.a52 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- import_excluded.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %C: type = class_type @C [concrete] // CHECK:STDOUT: %pattern_type.7c7: type = pattern_type %C [concrete] // CHECK:STDOUT: %F.type: type = fn_type @F [concrete] // CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] // CHECK:STDOUT: %F: %F.type = struct_value () [concrete] // CHECK:STDOUT: %I.type: type = facet_type <@I> [concrete] // CHECK:STDOUT: %Self: %I.type = symbolic_binding Self, 0 [symbolic] // CHECK:STDOUT: %I.WithSelf.Op.type.f73: type = fn_type @I.WithSelf.Op, @I.WithSelf(%Self) [symbolic] // CHECK:STDOUT: %I.WithSelf.Op.f53: %I.WithSelf.Op.type.f73 = struct_value () [symbolic] // CHECK:STDOUT: %I.assoc_type: type = assoc_entity_type @I [concrete] // CHECK:STDOUT: %assoc0: %I.assoc_type = assoc_entity element0, imports.%Main.import_ref.e26 [concrete] // CHECK:STDOUT: %I.impl_witness: = impl_witness imports.%I.impl_witness_table [concrete] // CHECK:STDOUT: %I.facet: %I.type = facet_value %C, (%I.impl_witness) [concrete] // CHECK:STDOUT: %I.WithSelf.Op.type.537: type = fn_type @I.WithSelf.Op, @I.WithSelf(%I.facet) [concrete] // CHECK:STDOUT: %.c12: type = fn_type_with_self_type %I.WithSelf.Op.type.537, %I.facet [concrete] // CHECK:STDOUT: %C.as.I.impl.Op.type: type = fn_type @C.as.I.impl.Op [concrete] // CHECK:STDOUT: %C.as.I.impl.Op: %C.as.I.impl.Op.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Main.I: type = import_ref Main//excluded_with_range, I, loaded [concrete = constants.%I.type] // CHECK:STDOUT: %Main.C: type = import_ref Main//excluded_with_range, C, loaded [concrete = constants.%C] // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: %Main.import_ref.d09: %I.assoc_type = import_ref Main//excluded_with_range, loc6_29, loaded [concrete = constants.%assoc0] // CHECK:STDOUT: %Main.import_ref.e26: @I.WithSelf.%I.WithSelf.Op.type (%I.WithSelf.Op.type.f73) = import_ref Main//excluded_with_range, loc6_29, loaded [symbolic = @I.WithSelf.%I.WithSelf.Op (constants.%I.WithSelf.Op.f53)] // CHECK:STDOUT: %Main.import_ref.4bc: %C.as.I.impl.Op.type = import_ref Main//excluded_with_range, loc12_32, loaded [concrete = constants.%C.as.I.impl.Op] // CHECK:STDOUT: %I.impl_witness_table = impl_witness_table (%Main.import_ref.4bc), @C.as.I.impl [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .I = imports.%Main.I // CHECK:STDOUT: .C = imports.%Main.C // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .F = %F.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %default.import = import // CHECK:STDOUT: %F.decl: %F.type = fn_decl @F [concrete = constants.%F] { // CHECK:STDOUT: %c.patt: %pattern_type.7c7 = value_binding_pattern c [concrete] // CHECK:STDOUT: %c.param_patt: %pattern_type.7c7 = value_param_pattern %c.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %c.param: %C = value_param call_param0 // CHECK:STDOUT: %C.ref: type = name_ref C, imports.%Main.C [concrete = constants.%C] // CHECK:STDOUT: %c: %C = value_binding c, %c.param // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @F(%c.param: %C) { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %c.ref: %C = name_ref c, %c // CHECK:STDOUT: %I.ref: type = name_ref I, imports.%Main.I [concrete = constants.%I.type] // CHECK:STDOUT: %Op.ref: %I.assoc_type = name_ref Op, imports.%Main.import_ref.d09 [concrete = constants.%assoc0] // CHECK:STDOUT: %impl.elem0: %.c12 = impl_witness_access constants.%I.impl_witness, element0 [concrete = constants.%C.as.I.impl.Op] // CHECK:STDOUT: %bound_method: = bound_method %c.ref, %impl.elem0 // CHECK:STDOUT: %C.as.I.impl.Op.call: init %empty_tuple.type = call %bound_method(%c.ref) // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/basics/multi_error.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/none.carbon // EXTRA-ARGS: --dump-sem-ir-ranges=if-present // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/basics/multi_error.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/basics/multi_error.carbon // --- fail_multi_error.carbon // When naming insts, the `error` instruction generated is added for naming. // This regression test ensures that reuse of `` in different scopes // works. // CHECK:STDERR: fail_multi_error.carbon:[[@LINE+4]]:8: error: implicit parameters of functions must be constant or `self` [ImplictParamMustBeConstant] // CHECK:STDERR: fn Foo[a: ()](); // CHECK:STDERR: ^~~~~ // CHECK:STDERR: fn Foo[a: ()](); // CHECK:STDERR: fail_multi_error.carbon:[[@LINE+4]]:8: error: implicit parameters of functions must be constant or `self` [ImplictParamMustBeConstant] // CHECK:STDERR: fn Boo[a: ()]() {} // CHECK:STDERR: ^~~~~ // CHECK:STDERR: fn Boo[a: ()]() {} // CHECK:STDOUT: --- fail_multi_error.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %Foo.type: type = fn_type @Foo [concrete] // CHECK:STDOUT: %Foo: %Foo.type = struct_value () [concrete] // CHECK:STDOUT: %Boo.type: type = fn_type @Boo [concrete] // CHECK:STDOUT: %Boo: %Boo.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Foo = %Foo.decl // CHECK:STDOUT: .Boo = %Boo.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Foo.decl: %Foo.type = fn_decl @Foo [concrete = constants.%Foo] {} {} // CHECK:STDOUT: %Boo.decl: %Boo.type = fn_decl @Boo [concrete = constants.%Boo] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @Foo(); // CHECK:STDOUT: // CHECK:STDOUT: fn @Boo() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/basics/name_lookup.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/none.carbon // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/basics/name_lookup.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/basics/name_lookup.carbon // --- fail_not_found.carbon library "[[@TEST_NAME]]"; fn Main() { //@dump-sem-ir-begin // CHECK:STDERR: fail_not_found.carbon:[[@LINE+4]]:3: error: name `x` not found [NameNotFound] // CHECK:STDERR: x; // CHECK:STDERR: ^ // CHECK:STDERR: x; //@dump-sem-ir-end } // --- fail_qualififier_unsupported.carbon library "[[@TEST_NAME]]"; fn F() { //@dump-sem-ir-begin // CHECK:STDERR: fail_qualififier_unsupported.carbon:[[@LINE+4]]:3: error: type `bool` does not support qualified expressions [QualifiedExprUnsupported] // CHECK:STDERR: true.b; // CHECK:STDERR: ^~~~~~ // CHECK:STDERR: true.b; //@dump-sem-ir-end } // CHECK:STDOUT: --- fail_not_found.carbon // CHECK:STDOUT: // CHECK:STDOUT: fn @Main() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %x.ref: = name_ref x, [concrete = ] // CHECK:STDOUT: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- fail_qualififier_unsupported.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %true: bool = bool_literal true [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @F() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %true: bool = bool_literal true [concrete = constants.%true] // CHECK:STDOUT: // CHECK:STDOUT: } // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/basics/parens.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/int.carbon // EXTRA-ARGS: --dump-sem-ir-ranges=if-present // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/basics/parens.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/basics/parens.carbon var a: i32 = (1); var b: i32 = ((2)); // CHECK:STDOUT: --- parens.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [concrete] // CHECK:STDOUT: %Int.type: type = generic_class_type @Int [concrete] // CHECK:STDOUT: %Int.generic: %Int.type = struct_value () [concrete] // CHECK:STDOUT: %i32: type = class_type @Int, @Int(%int_32) [concrete] // CHECK:STDOUT: %pattern_type.7ce: type = pattern_type %i32 [concrete] // CHECK:STDOUT: %int_1.5b8: Core.IntLiteral = int_value 1 [concrete] // CHECK:STDOUT: %ImplicitAs.type.cc7: type = generic_interface_type @ImplicitAs [concrete] // CHECK:STDOUT: %ImplicitAs.generic: %ImplicitAs.type.cc7 = struct_value () [concrete] // CHECK:STDOUT: %ImplicitAs.type.e8c: type = facet_type <@ImplicitAs, @ImplicitAs(%i32)> [concrete] // CHECK:STDOUT: %To: Core.IntLiteral = symbolic_binding To, 0 [symbolic] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.4e6: type = fn_type @Core.IntLiteral.as.ImplicitAs.impl.Convert, @Core.IntLiteral.as.ImplicitAs.impl(%To) [symbolic] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.3c2: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.4e6 = struct_value () [symbolic] // CHECK:STDOUT: %ImplicitAs.impl_witness.6bc: = impl_witness imports.%ImplicitAs.impl_witness_table.74f, @Core.IntLiteral.as.ImplicitAs.impl(%int_32) [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.e0d: type = fn_type @Core.IntLiteral.as.ImplicitAs.impl.Convert, @Core.IntLiteral.as.ImplicitAs.impl(%int_32) [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.e0d = struct_value () [concrete] // CHECK:STDOUT: %ImplicitAs.facet: %ImplicitAs.type.e8c = facet_value Core.IntLiteral, (%ImplicitAs.impl_witness.6bc) [concrete] // CHECK:STDOUT: %ImplicitAs.WithSelf.Convert.type.b37: type = fn_type @ImplicitAs.WithSelf.Convert, @ImplicitAs.WithSelf(%i32, %ImplicitAs.facet) [concrete] // CHECK:STDOUT: %.545: type = fn_type_with_self_type %ImplicitAs.WithSelf.Convert.type.b37, %ImplicitAs.facet [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.bound.215: = bound_method %int_1.5b8, %Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5 [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn: = specific_function %Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5, @Core.IntLiteral.as.ImplicitAs.impl.Convert(%int_32) [concrete] // CHECK:STDOUT: %bound_method.38b: = bound_method %int_1.5b8, %Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn [concrete] // CHECK:STDOUT: %int_1.5d2: %i32 = int_value 1 [concrete] // CHECK:STDOUT: %int_2.ecc: Core.IntLiteral = int_value 2 [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.bound.4e5: = bound_method %int_2.ecc, %Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5 [concrete] // CHECK:STDOUT: %bound_method.646: = bound_method %int_2.ecc, %Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn [concrete] // CHECK:STDOUT: %int_2.ef8: %i32 = int_value 2 [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: .Int = %Core.Int // CHECK:STDOUT: .ImplicitAs = %Core.ImplicitAs // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: %Core.Int: %Int.type = import_ref Core//prelude/parts/int, Int, loaded [concrete = constants.%Int.generic] // CHECK:STDOUT: %Core.ImplicitAs: %ImplicitAs.type.cc7 = import_ref Core//prelude/parts/as, ImplicitAs, loaded [concrete = constants.%ImplicitAs.generic] // CHECK:STDOUT: %Core.import_ref.42d: @Core.IntLiteral.as.ImplicitAs.impl.%Core.IntLiteral.as.ImplicitAs.impl.Convert.type (%Core.IntLiteral.as.ImplicitAs.impl.Convert.type.4e6) = import_ref Core//prelude/parts/int, loc{{\d+_\d+}}, loaded [symbolic = @Core.IntLiteral.as.ImplicitAs.impl.%Core.IntLiteral.as.ImplicitAs.impl.Convert (constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.3c2)] // CHECK:STDOUT: %ImplicitAs.impl_witness_table.74f = impl_witness_table (%Core.import_ref.42d), @Core.IntLiteral.as.ImplicitAs.impl [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .a = %a // CHECK:STDOUT: .b = %b // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %a.patt: %pattern_type.7ce = ref_binding_pattern a [concrete] // CHECK:STDOUT: %a.var_patt: %pattern_type.7ce = var_pattern %a.patt [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %a.var: ref %i32 = var %a.var_patt [concrete] // CHECK:STDOUT: %i32.loc14: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %a: ref %i32 = ref_binding a, %a.var [concrete = %a.var] // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %b.patt: %pattern_type.7ce = ref_binding_pattern b [concrete] // CHECK:STDOUT: %b.var_patt: %pattern_type.7ce = var_pattern %b.patt [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %b.var: ref %i32 = var %b.var_patt [concrete] // CHECK:STDOUT: %i32.loc15: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %b: ref %i32 = ref_binding b, %b.var [concrete = %b.var] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @__global_init() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %int_1: Core.IntLiteral = int_value 1 [concrete = constants.%int_1.5b8] // CHECK:STDOUT: %impl.elem0.loc14: %.545 = impl_witness_access constants.%ImplicitAs.impl_witness.6bc, element0 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5] // CHECK:STDOUT: %bound_method.loc14_1.1: = bound_method %int_1, %impl.elem0.loc14 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.bound.215] // CHECK:STDOUT: %specific_fn.loc14: = specific_function %impl.elem0.loc14, @Core.IntLiteral.as.ImplicitAs.impl.Convert(constants.%int_32) [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn] // CHECK:STDOUT: %bound_method.loc14_1.2: = bound_method %int_1, %specific_fn.loc14 [concrete = constants.%bound_method.38b] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc14: init %i32 = call %bound_method.loc14_1.2(%int_1) [concrete = constants.%int_1.5d2] // CHECK:STDOUT: %.loc14: init %i32 = converted %int_1, %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc14 [concrete = constants.%int_1.5d2] // CHECK:STDOUT: assign file.%a.var, %.loc14 // CHECK:STDOUT: %int_2: Core.IntLiteral = int_value 2 [concrete = constants.%int_2.ecc] // CHECK:STDOUT: %impl.elem0.loc15: %.545 = impl_witness_access constants.%ImplicitAs.impl_witness.6bc, element0 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5] // CHECK:STDOUT: %bound_method.loc15_1.1: = bound_method %int_2, %impl.elem0.loc15 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.bound.4e5] // CHECK:STDOUT: %specific_fn.loc15: = specific_function %impl.elem0.loc15, @Core.IntLiteral.as.ImplicitAs.impl.Convert(constants.%int_32) [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn] // CHECK:STDOUT: %bound_method.loc15_1.2: = bound_method %int_2, %specific_fn.loc15 [concrete = constants.%bound_method.646] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc15: init %i32 = call %bound_method.loc15_1.2(%int_2) [concrete = constants.%int_2.ef8] // CHECK:STDOUT: %.loc15: init %i32 = converted %int_2, %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc15 [concrete = constants.%int_2.ef8] // CHECK:STDOUT: assign file.%b.var, %.loc15 // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/basics/raw_identifier.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/none.carbon // EXTRA-ARGS: --dump-sem-ir-ranges=if-present // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/basics/raw_identifier.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/basics/raw_identifier.carbon fn A(n: ()) -> () { return r#n; } fn B(r#n: ()) -> () { return n; } fn C(r#if: ()) -> () { return r#if; } // CHECK:STDOUT: --- raw_identifier.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] // CHECK:STDOUT: %empty_tuple: %empty_tuple.type = tuple_value () [concrete] // CHECK:STDOUT: %pattern_type: type = pattern_type %empty_tuple.type [concrete] // CHECK:STDOUT: %.262: Core.Form = init_form %empty_tuple.type [concrete] // CHECK:STDOUT: %A.type: type = fn_type @A [concrete] // CHECK:STDOUT: %A: %A.type = struct_value () [concrete] // CHECK:STDOUT: %B.type: type = fn_type @B [concrete] // CHECK:STDOUT: %B: %B.type = struct_value () [concrete] // CHECK:STDOUT: %C.type: type = fn_type @C [concrete] // CHECK:STDOUT: %C: %C.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .A = %A.decl // CHECK:STDOUT: .B = %B.decl // CHECK:STDOUT: .C = %C.decl // CHECK:STDOUT: } // CHECK:STDOUT: %A.decl: %A.type = fn_decl @A [concrete = constants.%A] { // CHECK:STDOUT: %n.patt: %pattern_type = value_binding_pattern n [concrete] // CHECK:STDOUT: %n.param_patt: %pattern_type = value_param_pattern %n.patt [concrete] // CHECK:STDOUT: %return.patt: %pattern_type = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: %pattern_type = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %.loc14_17.1: %empty_tuple.type = tuple_literal () [concrete = constants.%empty_tuple] // CHECK:STDOUT: %.loc14_17.2: type = converted %.loc14_17.1, constants.%empty_tuple.type [concrete = constants.%empty_tuple.type] // CHECK:STDOUT: %.loc14_17.3: Core.Form = init_form %.loc14_17.2 [concrete = constants.%.262] // CHECK:STDOUT: %n.param: %empty_tuple.type = value_param call_param0 // CHECK:STDOUT: %.loc14_10.1: type = splice_block %.loc14_10.3 [concrete = constants.%empty_tuple.type] { // CHECK:STDOUT: %.loc14_10.2: %empty_tuple.type = tuple_literal () [concrete = constants.%empty_tuple] // CHECK:STDOUT: %.loc14_10.3: type = converted %.loc14_10.2, constants.%empty_tuple.type [concrete = constants.%empty_tuple.type] // CHECK:STDOUT: } // CHECK:STDOUT: %n: %empty_tuple.type = value_binding n, %n.param // CHECK:STDOUT: %return.param: ref %empty_tuple.type = out_param call_param1 // CHECK:STDOUT: %return: ref %empty_tuple.type = return_slot %return.param // CHECK:STDOUT: } // CHECK:STDOUT: %B.decl: %B.type = fn_decl @B [concrete = constants.%B] { // CHECK:STDOUT: %n.patt: %pattern_type = value_binding_pattern n [concrete] // CHECK:STDOUT: %n.param_patt: %pattern_type = value_param_pattern %n.patt [concrete] // CHECK:STDOUT: %return.patt: %pattern_type = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: %pattern_type = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %.loc18_19.1: %empty_tuple.type = tuple_literal () [concrete = constants.%empty_tuple] // CHECK:STDOUT: %.loc18_19.2: type = converted %.loc18_19.1, constants.%empty_tuple.type [concrete = constants.%empty_tuple.type] // CHECK:STDOUT: %.loc18_19.3: Core.Form = init_form %.loc18_19.2 [concrete = constants.%.262] // CHECK:STDOUT: %n.param: %empty_tuple.type = value_param call_param0 // CHECK:STDOUT: %.loc18_12.1: type = splice_block %.loc18_12.3 [concrete = constants.%empty_tuple.type] { // CHECK:STDOUT: %.loc18_12.2: %empty_tuple.type = tuple_literal () [concrete = constants.%empty_tuple] // CHECK:STDOUT: %.loc18_12.3: type = converted %.loc18_12.2, constants.%empty_tuple.type [concrete = constants.%empty_tuple.type] // CHECK:STDOUT: } // CHECK:STDOUT: %n: %empty_tuple.type = value_binding n, %n.param // CHECK:STDOUT: %return.param: ref %empty_tuple.type = out_param call_param1 // CHECK:STDOUT: %return: ref %empty_tuple.type = return_slot %return.param // CHECK:STDOUT: } // CHECK:STDOUT: %C.decl: %C.type = fn_decl @C [concrete = constants.%C] { // CHECK:STDOUT: %if.patt: %pattern_type = value_binding_pattern r#if [concrete] // CHECK:STDOUT: %if.param_patt: %pattern_type = value_param_pattern %if.patt [concrete] // CHECK:STDOUT: %return.patt: %pattern_type = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: %pattern_type = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %.loc22_20.1: %empty_tuple.type = tuple_literal () [concrete = constants.%empty_tuple] // CHECK:STDOUT: %.loc22_20.2: type = converted %.loc22_20.1, constants.%empty_tuple.type [concrete = constants.%empty_tuple.type] // CHECK:STDOUT: %.loc22_20.3: Core.Form = init_form %.loc22_20.2 [concrete = constants.%.262] // CHECK:STDOUT: %if.param: %empty_tuple.type = value_param call_param0 // CHECK:STDOUT: %.loc22_13.1: type = splice_block %.loc22_13.3 [concrete = constants.%empty_tuple.type] { // CHECK:STDOUT: %.loc22_13.2: %empty_tuple.type = tuple_literal () [concrete = constants.%empty_tuple] // CHECK:STDOUT: %.loc22_13.3: type = converted %.loc22_13.2, constants.%empty_tuple.type [concrete = constants.%empty_tuple.type] // CHECK:STDOUT: } // CHECK:STDOUT: %if: %empty_tuple.type = value_binding r#if, %if.param // CHECK:STDOUT: %return.param: ref %empty_tuple.type = out_param call_param1 // CHECK:STDOUT: %return: ref %empty_tuple.type = return_slot %return.param // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @A(%n.param: %empty_tuple.type) -> out %return.param: %empty_tuple.type { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %n.ref: %empty_tuple.type = name_ref n, %n // CHECK:STDOUT: %.loc15_10: init %empty_tuple.type = tuple_init () [concrete = constants.%empty_tuple] // CHECK:STDOUT: %.loc15_13: init %empty_tuple.type = converted %n.ref, %.loc15_10 [concrete = constants.%empty_tuple] // CHECK:STDOUT: return %.loc15_13 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @B(%n.param: %empty_tuple.type) -> out %return.param: %empty_tuple.type { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %n.ref: %empty_tuple.type = name_ref n, %n // CHECK:STDOUT: %.loc19_10: init %empty_tuple.type = tuple_init () [concrete = constants.%empty_tuple] // CHECK:STDOUT: %.loc19_11: init %empty_tuple.type = converted %n.ref, %.loc19_10 [concrete = constants.%empty_tuple] // CHECK:STDOUT: return %.loc19_11 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @C(%if.param: %empty_tuple.type) -> out %return.param: %empty_tuple.type { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %if.ref: %empty_tuple.type = name_ref r#if, %if // CHECK:STDOUT: %.loc23_10: init %empty_tuple.type = tuple_init () [concrete = constants.%empty_tuple] // CHECK:STDOUT: %.loc23_14: init %empty_tuple.type = converted %if.ref, %.loc23_10 [concrete = constants.%empty_tuple] // CHECK:STDOUT: return %.loc23_14 // CHECK:STDOUT: } // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/basics/raw_sem_ir/builtins.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/none.carbon // EXTRA-ARGS: --dump-raw-sem-ir --builtin-sem-ir // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/basics/raw_sem_ir/builtins.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/basics/raw_sem_ir/builtins.carbon // CHECK:STDOUT: --- // CHECK:STDOUT: filename: builtins.carbon // CHECK:STDOUT: sem_ir: // CHECK:STDOUT: names: {} // CHECK:STDOUT: import_irs: // CHECK:STDOUT: 'import_ir(ApiForImpl)': {decl_id: inst, is_export: false} // CHECK:STDOUT: 'import_ir(Cpp)': {decl_id: inst, is_export: false} // CHECK:STDOUT: import_ir_insts: {} // CHECK:STDOUT: clang_decls: {} // CHECK:STDOUT: name_scopes: // CHECK:STDOUT: name_scope0: {inst: instF, parent_scope: name_scope, has_error: false, extended_scopes: [], names: {}} // CHECK:STDOUT: entity_names: {} // CHECK:STDOUT: cpp_global_vars: {} // CHECK:STDOUT: functions: {} // CHECK:STDOUT: classes: {} // CHECK:STDOUT: interfaces: {} // CHECK:STDOUT: associated_constants: {} // CHECK:STDOUT: impls: {} // CHECK:STDOUT: generics: {} // CHECK:STDOUT: specifics: {} // CHECK:STDOUT: specific_interfaces: {} // CHECK:STDOUT: struct_type_fields: // CHECK:STDOUT: struct_type_fields_empty: {} // CHECK:STDOUT: types: // CHECK:STDOUT: 'type(TypeType)': // CHECK:STDOUT: value_repr: {kind: copy, type: type(TypeType)} // CHECK:STDOUT: 'type(inst(FormType))': // CHECK:STDOUT: value_repr: {kind: copy, type: type(inst(FormType))} // CHECK:STDOUT: 'type(Error)': // CHECK:STDOUT: value_repr: {kind: copy, type: type(Error)} // CHECK:STDOUT: 'type(inst(NamespaceType))': // CHECK:STDOUT: value_repr: {kind: copy, type: type(inst(NamespaceType))} // CHECK:STDOUT: facet_types: {} // CHECK:STDOUT: insts: // CHECK:STDOUT: 'inst(TypeType)': {kind: TypeType, type: type(TypeType)} // CHECK:STDOUT: 'inst(AutoType)': {kind: AutoType, type: type(TypeType)} // CHECK:STDOUT: 'inst(BoolType)': {kind: BoolType, type: type(TypeType)} // CHECK:STDOUT: 'inst(BoundMethodType)': {kind: BoundMethodType, type: type(TypeType)} // CHECK:STDOUT: 'inst(CharLiteralType)': {kind: CharLiteralType, type: type(TypeType)} // CHECK:STDOUT: 'inst(ErrorInst)': {kind: ErrorInst, type: type(Error)} // CHECK:STDOUT: 'inst(FloatLiteralType)': {kind: FloatLiteralType, type: type(TypeType)} // CHECK:STDOUT: 'inst(FormType)': {kind: FormType, type: type(TypeType)} // CHECK:STDOUT: 'inst(InstType)': {kind: InstType, type: type(TypeType)} // CHECK:STDOUT: 'inst(IntLiteralType)': {kind: IntLiteralType, type: type(TypeType)} // CHECK:STDOUT: 'inst(NamespaceType)': {kind: NamespaceType, type: type(TypeType)} // CHECK:STDOUT: 'inst(RequireSpecificDefinitionType)': {kind: RequireSpecificDefinitionType, type: type(TypeType)} // CHECK:STDOUT: 'inst(SpecificFunctionType)': {kind: SpecificFunctionType, type: type(TypeType)} // CHECK:STDOUT: 'inst(VtableType)': {kind: VtableType, type: type(TypeType)} // CHECK:STDOUT: 'inst(WitnessType)': {kind: WitnessType, type: type(TypeType)} // CHECK:STDOUT: instF: {kind: Namespace, arg0: name_scope0, arg1: inst, type: type(inst(NamespaceType))} // CHECK:STDOUT: constant_values: // CHECK:STDOUT: values: // CHECK:STDOUT: 'inst(TypeType)': concrete_constant(inst(TypeType)) // CHECK:STDOUT: 'inst(AutoType)': concrete_constant(inst(AutoType)) // CHECK:STDOUT: 'inst(BoolType)': concrete_constant(inst(BoolType)) // CHECK:STDOUT: 'inst(BoundMethodType)': concrete_constant(inst(BoundMethodType)) // CHECK:STDOUT: 'inst(CharLiteralType)': concrete_constant(inst(CharLiteralType)) // CHECK:STDOUT: 'inst(ErrorInst)': concrete_constant(inst(ErrorInst)) // CHECK:STDOUT: 'inst(FloatLiteralType)': concrete_constant(inst(FloatLiteralType)) // CHECK:STDOUT: 'inst(FormType)': concrete_constant(inst(FormType)) // CHECK:STDOUT: 'inst(InstType)': concrete_constant(inst(InstType)) // CHECK:STDOUT: 'inst(IntLiteralType)': concrete_constant(inst(IntLiteralType)) // CHECK:STDOUT: 'inst(NamespaceType)': concrete_constant(inst(NamespaceType)) // CHECK:STDOUT: 'inst(RequireSpecificDefinitionType)': concrete_constant(inst(RequireSpecificDefinitionType)) // CHECK:STDOUT: 'inst(SpecificFunctionType)': concrete_constant(inst(SpecificFunctionType)) // CHECK:STDOUT: 'inst(VtableType)': concrete_constant(inst(VtableType)) // CHECK:STDOUT: 'inst(WitnessType)': concrete_constant(inst(WitnessType)) // CHECK:STDOUT: instF: concrete_constant(instF) // CHECK:STDOUT: symbolic_constants: {} // CHECK:STDOUT: inst_blocks: // CHECK:STDOUT: inst_block_empty: {} // CHECK:STDOUT: exports: {} // CHECK:STDOUT: generated: {} // CHECK:STDOUT: imports: {} // CHECK:STDOUT: global_init: {} // CHECK:STDOUT: inst_block50000005: // CHECK:STDOUT: 0: instF // CHECK:STDOUT: value_stores: // CHECK:STDOUT: shared_values: // CHECK:STDOUT: ints: {} // CHECK:STDOUT: reals: {} // CHECK:STDOUT: floats: {} // CHECK:STDOUT: identifiers: {} // CHECK:STDOUT: strings: {} // CHECK:STDOUT: ... ================================================ FILE: toolchain/check/testdata/basics/raw_sem_ir/cpp_interop.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/none.carbon // EXTRA-ARGS: --dump-raw-sem-ir --builtin-sem-ir // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/basics/raw_sem_ir/cpp_interop.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/basics/raw_sem_ir/cpp_interop.carbon // --- header.h struct X { X* _Nonnull p; }; void f(X x = {}) { } X* _Nonnull global; // --- import.carbon import Cpp library "header.h"; fn G(x: Cpp.X) { Cpp.f(); Cpp.f(x); Cpp.f(*Cpp.global); } // CHECK:STDOUT: --- // CHECK:STDOUT: filename: import.carbon // CHECK:STDOUT: sem_ir: // CHECK:STDOUT: names: // CHECK:STDOUT: name0: G // CHECK:STDOUT: name1: x // CHECK:STDOUT: name2: X // CHECK:STDOUT: name3: f // CHECK:STDOUT: name4: global // CHECK:STDOUT: name5: p // CHECK:STDOUT: name6: f__carbon_thunk // CHECK:STDOUT: import_irs: // CHECK:STDOUT: 'import_ir(ApiForImpl)': {decl_id: inst, is_export: false} // CHECK:STDOUT: 'import_ir(Cpp)': {decl_id: inst, is_export: false} // CHECK:STDOUT: import_ir_insts: // CHECK:STDOUT: import_ir_inst0: {ir_id: import_ir(Cpp), clang_source_loc_id: clang_source_loc50000000} // CHECK:STDOUT: import_ir_inst1: {ir_id: import_ir(Cpp), clang_source_loc_id: clang_source_loc50000001} // CHECK:STDOUT: import_ir_inst2: {ir_id: import_ir(Cpp), clang_source_loc_id: clang_source_loc50000002} // CHECK:STDOUT: import_ir_inst3: {ir_id: import_ir(Cpp), clang_source_loc_id: clang_source_loc50000003} // CHECK:STDOUT: import_ir_inst4: {ir_id: import_ir(Cpp), clang_source_loc_id: clang_source_loc50000004} // CHECK:STDOUT: clang_decls: // CHECK:STDOUT: clang_decl_id50000000: {key: "", inst_id: inst50000011} // CHECK:STDOUT: clang_decl_id50000001: {key: "struct X {}", inst_id: inst50000014} // CHECK:STDOUT: clang_decl_id50000002: {key: "X * _Nonnull p", inst_id: inst50000022} // CHECK:STDOUT: clang_decl_id50000003: {key: {decl: "void f(X x = {})", kind: normal, num_params: 0}, inst_id: inst5000002D} // CHECK:STDOUT: clang_decl_id50000004: {key: {decl: "extern void f__carbon_thunk()", kind: normal, num_params: 0}, inst_id: inst50000030} // CHECK:STDOUT: clang_decl_id50000005: {key: {decl: "void f(X x = {})", kind: normal, num_params: 1}, inst_id: inst5000003B} // CHECK:STDOUT: clang_decl_id50000006: {key: {decl: "extern void f__carbon_thunk(X * _Nonnull x)", kind: normal, num_params: 1}, inst_id: inst50000043} // CHECK:STDOUT: clang_decl_id50000007: {key: "X * _Nonnull global", inst_id: inst5000004E} // CHECK:STDOUT: name_scopes: // CHECK:STDOUT: name_scope0: {inst: instF, parent_scope: name_scope, has_error: false, extended_scopes: [], names: {name(Cpp): inst50000011, name0: inst5000001D}} // CHECK:STDOUT: name_scope50000001: {inst: inst50000011, parent_scope: name_scope0, has_error: false, extended_scopes: [], names: {name2: inst50000014, name3: inst5000002A, name4: inst5000004E}} // CHECK:STDOUT: name_scope50000002: {inst: inst50000014, parent_scope: name_scope50000001, has_error: false, extended_scopes: [], names: {}} // CHECK:STDOUT: entity_names: // CHECK:STDOUT: entity_name50000000: {name: name1, parent_scope: name_scope, index: -1, is_template: 0, is_unused: 0, form: constant} // CHECK:STDOUT: entity_name50000001: {name: name1, parent_scope: name_scope, index: -1, is_template: 0, is_unused: 0, form: constant} // CHECK:STDOUT: entity_name50000002: {name: name1, parent_scope: name_scope, index: -1, is_template: 0, is_unused: 0, form: constant} // CHECK:STDOUT: entity_name50000003: {name: name4, parent_scope: name_scope50000001, index: -1, is_template: 0, is_unused: 0, form: constant} // CHECK:STDOUT: cpp_global_vars: // CHECK:STDOUT: cpp_global_var50000000: {key: {entity_name_id: entity_name50000003}, clang_decl_id: clang_decl_id50000007} // CHECK:STDOUT: functions: // CHECK:STDOUT: function50000000: {name: name0, parent_scope: name_scope0, call_param_patterns_id: inst_block50000007, call_params_id: inst_block50000008, body: [inst_block5000000B]} // CHECK:STDOUT: function50000001: {name: name3, parent_scope: name_scope50000001, call_param_patterns_id: inst_block_empty, call_params_id: inst_block_empty} // CHECK:STDOUT: function50000002: {name: name6, parent_scope: name_scope50000001, call_param_patterns_id: inst_block_empty, call_params_id: inst_block_empty} // CHECK:STDOUT: function50000003: {name: name3, parent_scope: name_scope50000001, call_param_patterns_id: inst_block5000000F, call_params_id: inst_block50000010} // CHECK:STDOUT: function50000004: {name: name6, parent_scope: name_scope50000001, call_param_patterns_id: inst_block50000015, call_params_id: inst_block50000016} // CHECK:STDOUT: classes: // CHECK:STDOUT: class50000000: {name: name2, parent_scope: name_scope50000001, self_type_id: type(inst50000015), inheritance_kind: Base, is_dynamic: 0, scope_id: name_scope50000002, body_block_id: inst_block5000000C, adapt_id: inst, base_id: inst, complete_type_witness_id: inst50000025, vtable_decl_id: inst}} // CHECK:STDOUT: interfaces: {} // CHECK:STDOUT: associated_constants: {} // CHECK:STDOUT: impls: {} // CHECK:STDOUT: generics: {} // CHECK:STDOUT: specifics: {} // CHECK:STDOUT: specific_interfaces: {} // CHECK:STDOUT: struct_type_fields: // CHECK:STDOUT: struct_type_fields_empty: {} // CHECK:STDOUT: struct_type_fields50000001: // CHECK:STDOUT: 0: {name_id: name5, type_inst_id: inst50000020} // CHECK:STDOUT: struct_type_fields50000002: // CHECK:STDOUT: 0: {name_id: name5, type_inst_id: inst50000020} // CHECK:STDOUT: types: // CHECK:STDOUT: 'type(TypeType)': // CHECK:STDOUT: value_repr: {kind: copy, type: type(TypeType)} // CHECK:STDOUT: 'type(inst(FormType))': // CHECK:STDOUT: value_repr: {kind: copy, type: type(inst(FormType))} // CHECK:STDOUT: 'type(Error)': // CHECK:STDOUT: value_repr: {kind: copy, type: type(Error)} // CHECK:STDOUT: 'type(inst(NamespaceType))': // CHECK:STDOUT: value_repr: {kind: copy, type: type(inst(NamespaceType))} // CHECK:STDOUT: 'type(inst(InstType))': // CHECK:STDOUT: value_repr: {kind: none, type: type(inst50000013)} // CHECK:STDOUT: 'type(inst50000013)': // CHECK:STDOUT: value_repr: {kind: none, type: type(inst50000013)} // CHECK:STDOUT: 'type(inst5000001E)': // CHECK:STDOUT: value_repr: {kind: none, type: type(inst50000013)} // CHECK:STDOUT: 'type(inst50000020)': // CHECK:STDOUT: value_repr: {kind: copy, type: type(inst50000020)} // CHECK:STDOUT: 'type(inst(WitnessType))': // CHECK:STDOUT: value_repr: {kind: copy, type: type(inst(WitnessType))} // CHECK:STDOUT: 'type(inst50000027)': // CHECK:STDOUT: value_repr: {kind: copy, type: type(inst50000027)} // CHECK:STDOUT: 'type(inst50000024)': // CHECK:STDOUT: value_repr: {kind: pointer, type: type(inst50000027)} // CHECK:STDOUT: 'type(inst50000015)': // CHECK:STDOUT: value_repr: {kind: pointer, type: type(inst50000027)} // CHECK:STDOUT: 'type(inst50000029)': // CHECK:STDOUT: value_repr: {kind: none, type: type(inst50000013)} // CHECK:STDOUT: 'type(inst5000002E)': // CHECK:STDOUT: value_repr: {kind: none, type: type(inst50000013)} // CHECK:STDOUT: 'type(inst50000031)': // CHECK:STDOUT: value_repr: {kind: none, type: type(inst50000013)} // CHECK:STDOUT: 'type(inst5000003C)': // CHECK:STDOUT: value_repr: {kind: none, type: type(inst50000013)} // CHECK:STDOUT: 'type(inst50000044)': // CHECK:STDOUT: value_repr: {kind: none, type: type(inst50000013)} // CHECK:STDOUT: facet_types: {} // CHECK:STDOUT: insts: // CHECK:STDOUT: 'inst(TypeType)': {kind: TypeType, type: type(TypeType)} // CHECK:STDOUT: 'inst(AutoType)': {kind: AutoType, type: type(TypeType)} // CHECK:STDOUT: 'inst(BoolType)': {kind: BoolType, type: type(TypeType)} // CHECK:STDOUT: 'inst(BoundMethodType)': {kind: BoundMethodType, type: type(TypeType)} // CHECK:STDOUT: 'inst(CharLiteralType)': {kind: CharLiteralType, type: type(TypeType)} // CHECK:STDOUT: 'inst(ErrorInst)': {kind: ErrorInst, type: type(Error)} // CHECK:STDOUT: 'inst(FloatLiteralType)': {kind: FloatLiteralType, type: type(TypeType)} // CHECK:STDOUT: 'inst(FormType)': {kind: FormType, type: type(TypeType)} // CHECK:STDOUT: 'inst(InstType)': {kind: InstType, type: type(TypeType)} // CHECK:STDOUT: 'inst(IntLiteralType)': {kind: IntLiteralType, type: type(TypeType)} // CHECK:STDOUT: 'inst(NamespaceType)': {kind: NamespaceType, type: type(TypeType)} // CHECK:STDOUT: 'inst(RequireSpecificDefinitionType)': {kind: RequireSpecificDefinitionType, type: type(TypeType)} // CHECK:STDOUT: 'inst(SpecificFunctionType)': {kind: SpecificFunctionType, type: type(TypeType)} // CHECK:STDOUT: 'inst(VtableType)': {kind: VtableType, type: type(TypeType)} // CHECK:STDOUT: 'inst(WitnessType)': {kind: WitnessType, type: type(TypeType)} // CHECK:STDOUT: instF: {kind: Namespace, arg0: name_scope0, arg1: inst, type: type(inst(NamespaceType))} // CHECK:STDOUT: inst50000010: {kind: ImportCppDecl} // CHECK:STDOUT: inst50000011: {kind: Namespace, arg0: name_scope50000001, arg1: inst50000010, type: type(inst(NamespaceType))} // CHECK:STDOUT: inst50000012: {kind: NameRef, arg0: name(Cpp), arg1: inst50000011, type: type(inst(NamespaceType))} // CHECK:STDOUT: inst50000013: {kind: TupleType, arg0: inst_block_empty, type: type(TypeType)} // CHECK:STDOUT: inst50000014: {kind: ClassDecl, arg0: class50000000, arg1: inst_block, type: type(TypeType)} // CHECK:STDOUT: inst50000015: {kind: ClassType, arg0: class50000000, arg1: specific, type: type(TypeType)} // CHECK:STDOUT: inst50000016: {kind: NameRef, arg0: name2, arg1: inst50000014, type: type(TypeType)} // CHECK:STDOUT: inst50000017: {kind: PatternType, arg0: inst50000015, type: type(TypeType)} // CHECK:STDOUT: inst50000018: {kind: ValueBinding, arg0: entity_name50000000, arg1: inst5000001B, type: type(inst50000015)} // CHECK:STDOUT: inst50000019: {kind: ValueBindingPattern, arg0: entity_name50000000, type: type(inst50000017)} // CHECK:STDOUT: inst5000001A: {kind: ValueParamPattern, arg0: inst50000019, type: type(inst50000017)} // CHECK:STDOUT: inst5000001B: {kind: ValueParam, arg0: call_param0, arg1: name1, type: type(inst50000015)} // CHECK:STDOUT: inst5000001C: {kind: SpliceBlock, arg0: inst_block50000005, arg1: inst50000016, type: type(TypeType)} // CHECK:STDOUT: inst5000001D: {kind: FunctionDecl, arg0: function50000000, arg1: inst_block5000000A, type: type(inst5000001E)} // CHECK:STDOUT: inst5000001E: {kind: FunctionType, arg0: function50000000, arg1: specific, type: type(TypeType)} // CHECK:STDOUT: inst5000001F: {kind: StructValue, arg0: inst_block_empty, type: type(inst5000001E)} // CHECK:STDOUT: inst50000020: {kind: PointerType, arg0: inst50000015, type: type(TypeType)} // CHECK:STDOUT: inst50000021: {kind: UnboundElementType, arg0: inst50000015, arg1: inst50000020, type: type(TypeType)} // CHECK:STDOUT: inst50000022: {kind: FieldDecl, arg0: name5, arg1: element0, type: type(inst50000021)} // CHECK:STDOUT: inst50000023: {kind: CustomLayoutType, arg0: struct_type_fields50000001, arg1: custom_layout50000001, type: type(TypeType)} // CHECK:STDOUT: inst50000024: {kind: CustomLayoutType, arg0: struct_type_fields50000002, arg1: custom_layout50000001, type: type(TypeType)} // CHECK:STDOUT: inst50000025: {kind: CompleteTypeWitness, arg0: inst50000023, type: type(inst(WitnessType))} // CHECK:STDOUT: inst50000026: {kind: CompleteTypeWitness, arg0: inst50000024, type: type(inst(WitnessType))} // CHECK:STDOUT: inst50000027: {kind: PointerType, arg0: inst50000024, type: type(TypeType)} // CHECK:STDOUT: inst50000028: {kind: NameRef, arg0: name(Cpp), arg1: inst50000011, type: type(inst(NamespaceType))} // CHECK:STDOUT: inst50000029: {kind: CppOverloadSetType, arg0: cpp_overload_set50000000, arg1: specific, type: type(TypeType)} // CHECK:STDOUT: inst5000002A: {kind: CppOverloadSetValue, arg0: cpp_overload_set50000000, type: type(inst50000029)} // CHECK:STDOUT: inst5000002B: {kind: CppOverloadSetValue, arg0: cpp_overload_set50000000, type: type(inst50000029)} // CHECK:STDOUT: inst5000002C: {kind: NameRef, arg0: name3, arg1: inst5000002A, type: type(inst50000029)} // CHECK:STDOUT: inst5000002D: {kind: FunctionDecl, arg0: function50000001, arg1: inst_block_empty, type: type(inst5000002E)} // CHECK:STDOUT: inst5000002E: {kind: FunctionType, arg0: function50000001, arg1: specific, type: type(TypeType)} // CHECK:STDOUT: inst5000002F: {kind: StructValue, arg0: inst_block_empty, type: type(inst5000002E)} // CHECK:STDOUT: inst50000030: {kind: FunctionDecl, arg0: function50000002, arg1: inst_block_empty, type: type(inst50000031)} // CHECK:STDOUT: inst50000031: {kind: FunctionType, arg0: function50000002, arg1: specific, type: type(TypeType)} // CHECK:STDOUT: inst50000032: {kind: StructValue, arg0: inst_block_empty, type: type(inst50000031)} // CHECK:STDOUT: inst50000033: {kind: Call, arg0: inst50000030, arg1: inst_block_empty, type: type(inst50000013)} // CHECK:STDOUT: inst50000034: {kind: NameRef, arg0: name(Cpp), arg1: inst50000011, type: type(inst(NamespaceType))} // CHECK:STDOUT: inst50000035: {kind: NameRef, arg0: name3, arg1: inst5000002A, type: type(inst50000029)} // CHECK:STDOUT: inst50000036: {kind: NameRef, arg0: name1, arg1: inst50000018, type: type(inst50000015)} // CHECK:STDOUT: inst50000037: {kind: ValueBinding, arg0: entity_name50000001, arg1: inst5000003A, type: type(inst50000015)} // CHECK:STDOUT: inst50000038: {kind: ValueBindingPattern, arg0: entity_name50000001, type: type(inst50000017)} // CHECK:STDOUT: inst50000039: {kind: ValueParamPattern, arg0: inst50000038, type: type(inst50000017)} // CHECK:STDOUT: inst5000003A: {kind: ValueParam, arg0: call_param0, arg1: name1, type: type(inst50000015)} // CHECK:STDOUT: inst5000003B: {kind: FunctionDecl, arg0: function50000003, arg1: inst_block50000012, type: type(inst5000003C)} // CHECK:STDOUT: inst5000003C: {kind: FunctionType, arg0: function50000003, arg1: specific, type: type(TypeType)} // CHECK:STDOUT: inst5000003D: {kind: StructValue, arg0: inst_block_empty, type: type(inst5000003C)} // CHECK:STDOUT: inst5000003E: {kind: PatternType, arg0: inst50000020, type: type(TypeType)} // CHECK:STDOUT: inst5000003F: {kind: ValueBinding, arg0: entity_name50000002, arg1: inst50000042, type: type(inst50000020)} // CHECK:STDOUT: inst50000040: {kind: ValueBindingPattern, arg0: entity_name50000002, type: type(inst5000003E)} // CHECK:STDOUT: inst50000041: {kind: ValueParamPattern, arg0: inst50000040, type: type(inst5000003E)} // CHECK:STDOUT: inst50000042: {kind: ValueParam, arg0: call_param0, arg1: name1, type: type(inst50000020)} // CHECK:STDOUT: inst50000043: {kind: FunctionDecl, arg0: function50000004, arg1: inst_block50000018, type: type(inst50000044)} // CHECK:STDOUT: inst50000044: {kind: FunctionType, arg0: function50000004, arg1: specific, type: type(TypeType)} // CHECK:STDOUT: inst50000045: {kind: StructValue, arg0: inst_block_empty, type: type(inst50000044)} // CHECK:STDOUT: inst50000046: {kind: ValueAsRef, arg0: inst50000036, type: type(inst50000015)} // CHECK:STDOUT: inst50000047: {kind: AddrOf, arg0: inst50000046, type: type(inst50000020)} // CHECK:STDOUT: inst50000048: {kind: Call, arg0: inst50000043, arg1: inst_block5000001A, type: type(inst50000013)} // CHECK:STDOUT: inst50000049: {kind: NameRef, arg0: name(Cpp), arg1: inst50000011, type: type(inst(NamespaceType))} // CHECK:STDOUT: inst5000004A: {kind: NameRef, arg0: name3, arg1: inst5000002A, type: type(inst50000029)} // CHECK:STDOUT: inst5000004B: {kind: NameRef, arg0: name(Cpp), arg1: inst50000011, type: type(inst(NamespaceType))} // CHECK:STDOUT: inst5000004C: {kind: RefBindingPattern, arg0: entity_name50000003, type: type(inst5000003E)} // CHECK:STDOUT: inst5000004D: {kind: VarPattern, arg0: inst5000004C, type: type(inst5000003E)} // CHECK:STDOUT: inst5000004E: {kind: VarStorage, arg0: inst5000004D, type: type(inst50000020)} // CHECK:STDOUT: inst5000004F: {kind: NameRef, arg0: name4, arg1: inst5000004E, type: type(inst50000020)} // CHECK:STDOUT: inst50000050: {kind: AcquireValue, arg0: inst5000004F, type: type(inst50000020)} // CHECK:STDOUT: inst50000051: {kind: Deref, arg0: inst50000050, type: type(inst50000015)} // CHECK:STDOUT: inst50000052: {kind: AcquireValue, arg0: inst50000051, type: type(inst50000015)} // CHECK:STDOUT: inst50000053: {kind: ValueAsRef, arg0: inst50000052, type: type(inst50000015)} // CHECK:STDOUT: inst50000054: {kind: AddrOf, arg0: inst50000053, type: type(inst50000020)} // CHECK:STDOUT: inst50000055: {kind: Call, arg0: inst50000043, arg1: inst_block5000001C, type: type(inst50000013)} // CHECK:STDOUT: inst50000056: {kind: Return} // CHECK:STDOUT: constant_values: // CHECK:STDOUT: values: // CHECK:STDOUT: 'inst(TypeType)': concrete_constant(inst(TypeType)) // CHECK:STDOUT: 'inst(AutoType)': concrete_constant(inst(AutoType)) // CHECK:STDOUT: 'inst(BoolType)': concrete_constant(inst(BoolType)) // CHECK:STDOUT: 'inst(BoundMethodType)': concrete_constant(inst(BoundMethodType)) // CHECK:STDOUT: 'inst(CharLiteralType)': concrete_constant(inst(CharLiteralType)) // CHECK:STDOUT: 'inst(ErrorInst)': concrete_constant(inst(ErrorInst)) // CHECK:STDOUT: 'inst(FloatLiteralType)': concrete_constant(inst(FloatLiteralType)) // CHECK:STDOUT: 'inst(FormType)': concrete_constant(inst(FormType)) // CHECK:STDOUT: 'inst(InstType)': concrete_constant(inst(InstType)) // CHECK:STDOUT: 'inst(IntLiteralType)': concrete_constant(inst(IntLiteralType)) // CHECK:STDOUT: 'inst(NamespaceType)': concrete_constant(inst(NamespaceType)) // CHECK:STDOUT: 'inst(RequireSpecificDefinitionType)': concrete_constant(inst(RequireSpecificDefinitionType)) // CHECK:STDOUT: 'inst(SpecificFunctionType)': concrete_constant(inst(SpecificFunctionType)) // CHECK:STDOUT: 'inst(VtableType)': concrete_constant(inst(VtableType)) // CHECK:STDOUT: 'inst(WitnessType)': concrete_constant(inst(WitnessType)) // CHECK:STDOUT: instF: concrete_constant(instF) // CHECK:STDOUT: inst50000011: concrete_constant(inst50000011) // CHECK:STDOUT: inst50000012: concrete_constant(inst50000011) // CHECK:STDOUT: inst50000013: concrete_constant(inst50000013) // CHECK:STDOUT: inst50000014: concrete_constant(inst50000015) // CHECK:STDOUT: inst50000015: concrete_constant(inst50000015) // CHECK:STDOUT: inst50000016: concrete_constant(inst50000015) // CHECK:STDOUT: inst50000017: concrete_constant(inst50000017) // CHECK:STDOUT: inst50000019: concrete_constant(inst50000019) // CHECK:STDOUT: inst5000001A: concrete_constant(inst5000001A) // CHECK:STDOUT: inst5000001C: concrete_constant(inst50000015) // CHECK:STDOUT: inst5000001D: concrete_constant(inst5000001F) // CHECK:STDOUT: inst5000001E: concrete_constant(inst5000001E) // CHECK:STDOUT: inst5000001F: concrete_constant(inst5000001F) // CHECK:STDOUT: inst50000020: concrete_constant(inst50000020) // CHECK:STDOUT: inst50000021: concrete_constant(inst50000021) // CHECK:STDOUT: inst50000022: concrete_constant(inst50000022) // CHECK:STDOUT: inst50000023: concrete_constant(inst50000024) // CHECK:STDOUT: inst50000024: concrete_constant(inst50000024) // CHECK:STDOUT: inst50000025: concrete_constant(inst50000026) // CHECK:STDOUT: inst50000026: concrete_constant(inst50000026) // CHECK:STDOUT: inst50000027: concrete_constant(inst50000027) // CHECK:STDOUT: inst50000028: concrete_constant(inst50000011) // CHECK:STDOUT: inst50000029: concrete_constant(inst50000029) // CHECK:STDOUT: inst5000002A: concrete_constant(inst5000002B) // CHECK:STDOUT: inst5000002B: concrete_constant(inst5000002B) // CHECK:STDOUT: inst5000002C: concrete_constant(inst5000002B) // CHECK:STDOUT: inst5000002D: concrete_constant(inst5000002F) // CHECK:STDOUT: inst5000002E: concrete_constant(inst5000002E) // CHECK:STDOUT: inst5000002F: concrete_constant(inst5000002F) // CHECK:STDOUT: inst50000030: concrete_constant(inst50000032) // CHECK:STDOUT: inst50000031: concrete_constant(inst50000031) // CHECK:STDOUT: inst50000032: concrete_constant(inst50000032) // CHECK:STDOUT: inst50000034: concrete_constant(inst50000011) // CHECK:STDOUT: inst50000035: concrete_constant(inst5000002B) // CHECK:STDOUT: inst50000038: concrete_constant(inst50000038) // CHECK:STDOUT: inst50000039: concrete_constant(inst50000039) // CHECK:STDOUT: inst5000003B: concrete_constant(inst5000003D) // CHECK:STDOUT: inst5000003C: concrete_constant(inst5000003C) // CHECK:STDOUT: inst5000003D: concrete_constant(inst5000003D) // CHECK:STDOUT: inst5000003E: concrete_constant(inst5000003E) // CHECK:STDOUT: inst50000040: concrete_constant(inst50000040) // CHECK:STDOUT: inst50000041: concrete_constant(inst50000041) // CHECK:STDOUT: inst50000043: concrete_constant(inst50000045) // CHECK:STDOUT: inst50000044: concrete_constant(inst50000044) // CHECK:STDOUT: inst50000045: concrete_constant(inst50000045) // CHECK:STDOUT: inst50000049: concrete_constant(inst50000011) // CHECK:STDOUT: inst5000004A: concrete_constant(inst5000002B) // CHECK:STDOUT: inst5000004B: concrete_constant(inst50000011) // CHECK:STDOUT: inst5000004C: concrete_constant(inst5000004C) // CHECK:STDOUT: inst5000004D: concrete_constant(inst5000004D) // CHECK:STDOUT: inst5000004E: concrete_constant(inst5000004E) // CHECK:STDOUT: inst5000004F: concrete_constant(inst5000004E) // CHECK:STDOUT: symbolic_constants: {} // CHECK:STDOUT: inst_blocks: // CHECK:STDOUT: inst_block_empty: {} // CHECK:STDOUT: exports: // CHECK:STDOUT: 0: inst5000001D // CHECK:STDOUT: generated: {} // CHECK:STDOUT: imports: // CHECK:STDOUT: 0: inst50000011 // CHECK:STDOUT: 1: inst50000014 // CHECK:STDOUT: 2: inst5000002A // CHECK:STDOUT: 3: inst5000002D // CHECK:STDOUT: 4: inst50000030 // CHECK:STDOUT: 5: inst5000003B // CHECK:STDOUT: 6: inst50000043 // CHECK:STDOUT: 7: inst5000004C // CHECK:STDOUT: 8: inst5000004D // CHECK:STDOUT: 9: inst5000004E // CHECK:STDOUT: global_init: {} // CHECK:STDOUT: inst_block50000005: // CHECK:STDOUT: 0: inst50000012 // CHECK:STDOUT: 1: inst50000016 // CHECK:STDOUT: inst_block50000006: // CHECK:STDOUT: 0: inst5000001A // CHECK:STDOUT: inst_block50000007: // CHECK:STDOUT: 0: inst5000001A // CHECK:STDOUT: inst_block50000008: // CHECK:STDOUT: 0: inst5000001B // CHECK:STDOUT: inst_block50000009: // CHECK:STDOUT: 0: inst50000019 // CHECK:STDOUT: 1: inst5000001A // CHECK:STDOUT: inst_block5000000A: // CHECK:STDOUT: 0: inst5000001B // CHECK:STDOUT: 1: inst5000001C // CHECK:STDOUT: 2: inst50000018 // CHECK:STDOUT: inst_block5000000B: // CHECK:STDOUT: 0: inst50000028 // CHECK:STDOUT: 1: inst5000002C // CHECK:STDOUT: 2: inst50000033 // CHECK:STDOUT: 3: inst50000034 // CHECK:STDOUT: 4: inst50000035 // CHECK:STDOUT: 5: inst50000036 // CHECK:STDOUT: 6: inst50000046 // CHECK:STDOUT: 7: inst50000047 // CHECK:STDOUT: 8: inst50000048 // CHECK:STDOUT: 9: inst50000049 // CHECK:STDOUT: 10: inst5000004A // CHECK:STDOUT: 11: inst5000004B // CHECK:STDOUT: 12: inst5000004F // CHECK:STDOUT: 13: inst50000050 // CHECK:STDOUT: 14: inst50000051 // CHECK:STDOUT: 15: inst50000052 // CHECK:STDOUT: 16: inst50000053 // CHECK:STDOUT: 17: inst50000054 // CHECK:STDOUT: 18: inst50000055 // CHECK:STDOUT: 19: inst50000056 // CHECK:STDOUT: inst_block5000000C: // CHECK:STDOUT: 0: inst50000022 // CHECK:STDOUT: 1: inst50000023 // CHECK:STDOUT: 2: inst50000025 // CHECK:STDOUT: inst_block5000000D: {} // CHECK:STDOUT: inst_block5000000E: // CHECK:STDOUT: 0: inst50000039 // CHECK:STDOUT: inst_block5000000F: // CHECK:STDOUT: 0: inst50000039 // CHECK:STDOUT: inst_block50000010: // CHECK:STDOUT: 0: inst5000003A // CHECK:STDOUT: inst_block50000011: // CHECK:STDOUT: 0: inst50000038 // CHECK:STDOUT: 1: inst50000039 // CHECK:STDOUT: inst_block50000012: // CHECK:STDOUT: 0: inst5000003A // CHECK:STDOUT: 1: inst50000037 // CHECK:STDOUT: inst_block50000013: {} // CHECK:STDOUT: inst_block50000014: // CHECK:STDOUT: 0: inst50000041 // CHECK:STDOUT: inst_block50000015: // CHECK:STDOUT: 0: inst50000041 // CHECK:STDOUT: inst_block50000016: // CHECK:STDOUT: 0: inst50000042 // CHECK:STDOUT: inst_block50000017: // CHECK:STDOUT: 0: inst50000040 // CHECK:STDOUT: 1: inst50000041 // CHECK:STDOUT: inst_block50000018: // CHECK:STDOUT: 0: inst50000042 // CHECK:STDOUT: 1: inst5000003F // CHECK:STDOUT: inst_block50000019: // CHECK:STDOUT: 0: inst50000036 // CHECK:STDOUT: inst_block5000001A: // CHECK:STDOUT: 0: inst50000047 // CHECK:STDOUT: inst_block5000001B: // CHECK:STDOUT: 0: inst50000052 // CHECK:STDOUT: inst_block5000001C: // CHECK:STDOUT: 0: inst50000054 // CHECK:STDOUT: inst_block5000001D: // CHECK:STDOUT: 0: instF // CHECK:STDOUT: 1: inst50000010 // CHECK:STDOUT: 2: inst5000001D // CHECK:STDOUT: value_stores: // CHECK:STDOUT: shared_values: // CHECK:STDOUT: ints: {} // CHECK:STDOUT: reals: {} // CHECK:STDOUT: floats: {} // CHECK:STDOUT: identifiers: // CHECK:STDOUT: identifier0: G // CHECK:STDOUT: identifier1: x // CHECK:STDOUT: identifier2: X // CHECK:STDOUT: identifier3: f // CHECK:STDOUT: identifier4: global // CHECK:STDOUT: identifier5: p // CHECK:STDOUT: identifier6: f__carbon_thunk // CHECK:STDOUT: strings: // CHECK:STDOUT: string0: header.h // CHECK:STDOUT: ... ================================================ FILE: toolchain/check/testdata/basics/raw_sem_ir/multifile.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/none.carbon // EXTRA-ARGS: --dump-raw-sem-ir --no-dump-sem-ir // // Check that raw IR dumping works as expected. // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/basics/raw_sem_ir/multifile.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/basics/raw_sem_ir/multifile.carbon // --- a.carbon package A; fn A() {} // --- b.carbon package B; import A; fn B() { A.A(); } // CHECK:STDOUT: --- // CHECK:STDOUT: filename: a.carbon // CHECK:STDOUT: sem_ir: // CHECK:STDOUT: names: // CHECK:STDOUT: name0: A // CHECK:STDOUT: import_irs: // CHECK:STDOUT: 'import_ir(ApiForImpl)': {decl_id: inst, is_export: false} // CHECK:STDOUT: 'import_ir(Cpp)': {decl_id: inst, is_export: false} // CHECK:STDOUT: import_ir_insts: {} // CHECK:STDOUT: clang_decls: {} // CHECK:STDOUT: name_scopes: // CHECK:STDOUT: name_scope0: {inst: instF, parent_scope: name_scope, has_error: false, extended_scopes: [], names: {name0: inst50000010}} // CHECK:STDOUT: entity_names: {} // CHECK:STDOUT: cpp_global_vars: {} // CHECK:STDOUT: functions: // CHECK:STDOUT: function50000000: {name: name0, parent_scope: name_scope0, call_param_patterns_id: inst_block_empty, call_params_id: inst_block_empty, body: [inst_block50000006]} // CHECK:STDOUT: classes: {} // CHECK:STDOUT: interfaces: {} // CHECK:STDOUT: associated_constants: {} // CHECK:STDOUT: impls: {} // CHECK:STDOUT: generics: {} // CHECK:STDOUT: specifics: {} // CHECK:STDOUT: specific_interfaces: {} // CHECK:STDOUT: struct_type_fields: // CHECK:STDOUT: struct_type_fields_empty: {} // CHECK:STDOUT: types: // CHECK:STDOUT: 'type(TypeType)': // CHECK:STDOUT: value_repr: {kind: copy, type: type(TypeType)} // CHECK:STDOUT: 'type(inst(FormType))': // CHECK:STDOUT: value_repr: {kind: copy, type: type(inst(FormType))} // CHECK:STDOUT: 'type(Error)': // CHECK:STDOUT: value_repr: {kind: copy, type: type(Error)} // CHECK:STDOUT: 'type(inst(NamespaceType))': // CHECK:STDOUT: value_repr: {kind: copy, type: type(inst(NamespaceType))} // CHECK:STDOUT: 'type(inst50000011)': // CHECK:STDOUT: value_repr: {kind: none, type: type(inst50000012)} // CHECK:STDOUT: 'type(inst50000012)': // CHECK:STDOUT: value_repr: {kind: none, type: type(inst50000012)} // CHECK:STDOUT: facet_types: {} // CHECK:STDOUT: insts: // CHECK:STDOUT: instF: {kind: Namespace, arg0: name_scope0, arg1: inst, type: type(inst(NamespaceType))} // CHECK:STDOUT: inst50000010: {kind: FunctionDecl, arg0: function50000000, arg1: inst_block_empty, type: type(inst50000011)} // CHECK:STDOUT: inst50000011: {kind: FunctionType, arg0: function50000000, arg1: specific, type: type(TypeType)} // CHECK:STDOUT: inst50000012: {kind: TupleType, arg0: inst_block_empty, type: type(TypeType)} // CHECK:STDOUT: inst50000013: {kind: StructValue, arg0: inst_block_empty, type: type(inst50000011)} // CHECK:STDOUT: inst50000014: {kind: Return} // CHECK:STDOUT: constant_values: // CHECK:STDOUT: values: // CHECK:STDOUT: instF: concrete_constant(instF) // CHECK:STDOUT: inst50000010: concrete_constant(inst50000013) // CHECK:STDOUT: inst50000011: concrete_constant(inst50000011) // CHECK:STDOUT: inst50000012: concrete_constant(inst50000012) // CHECK:STDOUT: inst50000013: concrete_constant(inst50000013) // CHECK:STDOUT: symbolic_constants: {} // CHECK:STDOUT: inst_blocks: // CHECK:STDOUT: inst_block_empty: {} // CHECK:STDOUT: exports: // CHECK:STDOUT: 0: inst50000010 // CHECK:STDOUT: generated: {} // CHECK:STDOUT: imports: {} // CHECK:STDOUT: global_init: {} // CHECK:STDOUT: inst_block50000005: {} // CHECK:STDOUT: inst_block50000006: // CHECK:STDOUT: 0: inst50000014 // CHECK:STDOUT: inst_block50000007: // CHECK:STDOUT: 0: instF // CHECK:STDOUT: 1: inst50000010 // CHECK:STDOUT: value_stores: // CHECK:STDOUT: shared_values: // CHECK:STDOUT: ints: {} // CHECK:STDOUT: reals: {} // CHECK:STDOUT: floats: {} // CHECK:STDOUT: identifiers: // CHECK:STDOUT: identifier0: A // CHECK:STDOUT: strings: {} // CHECK:STDOUT: ... // CHECK:STDOUT: --- // CHECK:STDOUT: filename: b.carbon // CHECK:STDOUT: sem_ir: // CHECK:STDOUT: names: // CHECK:STDOUT: name0: B // CHECK:STDOUT: name1: A // CHECK:STDOUT: import_irs: // CHECK:STDOUT: 'import_ir(ApiForImpl)': {decl_id: inst, is_export: false} // CHECK:STDOUT: 'import_ir(Cpp)': {decl_id: inst, is_export: false} // CHECK:STDOUT: import_ir70000002: {decl_id: inst70000010, is_export: false} // CHECK:STDOUT: import_ir_insts: // CHECK:STDOUT: import_ir_inst0: {ir_id: import_ir70000002, inst_id: inst50000010} // CHECK:STDOUT: import_ir_inst1: {ir_id: import_ir70000002, inst_id: inst50000010} // CHECK:STDOUT: clang_decls: {} // CHECK:STDOUT: name_scopes: // CHECK:STDOUT: name_scope0: {inst: instF, parent_scope: name_scope, has_error: false, extended_scopes: [], names: {name1: inst70000011, name0: inst70000012}} // CHECK:STDOUT: name_scope70000001: {inst: inst70000011, parent_scope: name_scope0, has_error: false, extended_scopes: [], names: {name1: inst70000017}} // CHECK:STDOUT: entity_names: // CHECK:STDOUT: entity_name70000000: {name: name1, parent_scope: name_scope70000001, index: -1, is_template: 0, is_unused: 0, form: constant} // CHECK:STDOUT: cpp_global_vars: {} // CHECK:STDOUT: functions: // CHECK:STDOUT: function70000000: {name: name0, parent_scope: name_scope0, call_param_patterns_id: inst_block_empty, call_params_id: inst_block_empty, body: [inst_block70000006]} // CHECK:STDOUT: function70000001: {name: name1, parent_scope: name_scope70000001, call_param_patterns_id: inst_block_empty} // CHECK:STDOUT: classes: {} // CHECK:STDOUT: interfaces: {} // CHECK:STDOUT: associated_constants: {} // CHECK:STDOUT: impls: {} // CHECK:STDOUT: generics: {} // CHECK:STDOUT: specifics: {} // CHECK:STDOUT: specific_interfaces: {} // CHECK:STDOUT: struct_type_fields: // CHECK:STDOUT: struct_type_fields_empty: {} // CHECK:STDOUT: types: // CHECK:STDOUT: 'type(TypeType)': // CHECK:STDOUT: value_repr: {kind: copy, type: type(TypeType)} // CHECK:STDOUT: 'type(inst(FormType))': // CHECK:STDOUT: value_repr: {kind: copy, type: type(inst(FormType))} // CHECK:STDOUT: 'type(Error)': // CHECK:STDOUT: value_repr: {kind: copy, type: type(Error)} // CHECK:STDOUT: 'type(inst(NamespaceType))': // CHECK:STDOUT: value_repr: {kind: copy, type: type(inst(NamespaceType))} // CHECK:STDOUT: 'type(inst70000013)': // CHECK:STDOUT: value_repr: {kind: none, type: type(inst70000014)} // CHECK:STDOUT: 'type(inst70000014)': // CHECK:STDOUT: value_repr: {kind: none, type: type(inst70000014)} // CHECK:STDOUT: 'type(inst(InstType))': // CHECK:STDOUT: value_repr: {kind: none, type: type(inst70000014)} // CHECK:STDOUT: facet_types: {} // CHECK:STDOUT: insts: // CHECK:STDOUT: instF: {kind: Namespace, arg0: name_scope0, arg1: inst, type: type(inst(NamespaceType))} // CHECK:STDOUT: inst70000010: {kind: ImportDecl, arg0: name1} // CHECK:STDOUT: inst70000011: {kind: Namespace, arg0: name_scope70000001, arg1: inst70000010, type: type(inst(NamespaceType))} // CHECK:STDOUT: inst70000012: {kind: FunctionDecl, arg0: function70000000, arg1: inst_block_empty, type: type(inst70000013)} // CHECK:STDOUT: inst70000013: {kind: FunctionType, arg0: function70000000, arg1: specific, type: type(TypeType)} // CHECK:STDOUT: inst70000014: {kind: TupleType, arg0: inst_block_empty, type: type(TypeType)} // CHECK:STDOUT: inst70000015: {kind: StructValue, arg0: inst_block_empty, type: type(inst70000013)} // CHECK:STDOUT: inst70000016: {kind: NameRef, arg0: name1, arg1: inst70000011, type: type(inst(NamespaceType))} // CHECK:STDOUT: inst70000017: {kind: ImportRefLoaded, arg0: import_ir_inst0, arg1: entity_name70000000, type: type(inst70000019)} // CHECK:STDOUT: inst70000018: {kind: FunctionDecl, arg0: function70000001, arg1: inst_block_empty, type: type(inst70000019)} // CHECK:STDOUT: inst70000019: {kind: FunctionType, arg0: function70000001, arg1: specific, type: type(TypeType)} // CHECK:STDOUT: inst7000001A: {kind: StructValue, arg0: inst_block_empty, type: type(inst70000019)} // CHECK:STDOUT: inst7000001B: {kind: NameRef, arg0: name1, arg1: inst70000017, type: type(inst70000019)} // CHECK:STDOUT: inst7000001C: {kind: Call, arg0: inst7000001B, arg1: inst_block_empty, type: type(inst70000014)} // CHECK:STDOUT: inst7000001D: {kind: Return} // CHECK:STDOUT: constant_values: // CHECK:STDOUT: values: // CHECK:STDOUT: instF: concrete_constant(instF) // CHECK:STDOUT: inst70000011: concrete_constant(inst70000011) // CHECK:STDOUT: inst70000012: concrete_constant(inst70000015) // CHECK:STDOUT: inst70000013: concrete_constant(inst70000013) // CHECK:STDOUT: inst70000014: concrete_constant(inst70000014) // CHECK:STDOUT: inst70000015: concrete_constant(inst70000015) // CHECK:STDOUT: inst70000016: concrete_constant(inst70000011) // CHECK:STDOUT: inst70000017: concrete_constant(inst7000001A) // CHECK:STDOUT: inst70000018: concrete_constant(inst7000001A) // CHECK:STDOUT: inst70000019: concrete_constant(inst70000019) // CHECK:STDOUT: inst7000001A: concrete_constant(inst7000001A) // CHECK:STDOUT: inst7000001B: concrete_constant(inst7000001A) // CHECK:STDOUT: symbolic_constants: {} // CHECK:STDOUT: inst_blocks: // CHECK:STDOUT: inst_block_empty: {} // CHECK:STDOUT: exports: // CHECK:STDOUT: 0: inst70000012 // CHECK:STDOUT: generated: {} // CHECK:STDOUT: imports: // CHECK:STDOUT: 0: inst70000011 // CHECK:STDOUT: 1: inst70000017 // CHECK:STDOUT: 2: inst70000018 // CHECK:STDOUT: global_init: {} // CHECK:STDOUT: inst_block70000005: {} // CHECK:STDOUT: inst_block70000006: // CHECK:STDOUT: 0: inst70000016 // CHECK:STDOUT: 1: inst7000001B // CHECK:STDOUT: 2: inst7000001C // CHECK:STDOUT: 3: inst7000001D // CHECK:STDOUT: inst_block70000007: // CHECK:STDOUT: 0: instF // CHECK:STDOUT: 1: inst70000010 // CHECK:STDOUT: 2: inst70000012 // CHECK:STDOUT: value_stores: // CHECK:STDOUT: shared_values: // CHECK:STDOUT: ints: {} // CHECK:STDOUT: reals: {} // CHECK:STDOUT: floats: {} // CHECK:STDOUT: identifiers: // CHECK:STDOUT: identifier0: B // CHECK:STDOUT: identifier1: A // CHECK:STDOUT: strings: {} // CHECK:STDOUT: ... ================================================ FILE: toolchain/check/testdata/basics/raw_sem_ir/multifile_with_textual_ir.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/none.carbon // EXTRA-ARGS: --dump-raw-sem-ir --dump-sem-ir-ranges=if-present // // Check that we can combine textual IR and raw IR dumping in one compile. // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/basics/raw_sem_ir/multifile_with_textual_ir.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/basics/raw_sem_ir/multifile_with_textual_ir.carbon // --- a.carbon package A; fn A() {} // --- b.carbon package B; import A; fn B() { A.A(); } // CHECK:STDOUT: --- // CHECK:STDOUT: filename: a.carbon // CHECK:STDOUT: sem_ir: // CHECK:STDOUT: names: // CHECK:STDOUT: name0: A // CHECK:STDOUT: import_irs: // CHECK:STDOUT: 'import_ir(ApiForImpl)': {decl_id: inst, is_export: false} // CHECK:STDOUT: 'import_ir(Cpp)': {decl_id: inst, is_export: false} // CHECK:STDOUT: import_ir_insts: {} // CHECK:STDOUT: clang_decls: {} // CHECK:STDOUT: name_scopes: // CHECK:STDOUT: name_scope0: {inst: instF, parent_scope: name_scope, has_error: false, extended_scopes: [], names: {name0: inst50000010}} // CHECK:STDOUT: entity_names: {} // CHECK:STDOUT: cpp_global_vars: {} // CHECK:STDOUT: functions: // CHECK:STDOUT: function50000000: {name: name0, parent_scope: name_scope0, call_param_patterns_id: inst_block_empty, call_params_id: inst_block_empty, body: [inst_block50000006]} // CHECK:STDOUT: classes: {} // CHECK:STDOUT: interfaces: {} // CHECK:STDOUT: associated_constants: {} // CHECK:STDOUT: impls: {} // CHECK:STDOUT: generics: {} // CHECK:STDOUT: specifics: {} // CHECK:STDOUT: specific_interfaces: {} // CHECK:STDOUT: struct_type_fields: // CHECK:STDOUT: struct_type_fields_empty: {} // CHECK:STDOUT: types: // CHECK:STDOUT: 'type(TypeType)': // CHECK:STDOUT: value_repr: {kind: copy, type: type(TypeType)} // CHECK:STDOUT: 'type(inst(FormType))': // CHECK:STDOUT: value_repr: {kind: copy, type: type(inst(FormType))} // CHECK:STDOUT: 'type(Error)': // CHECK:STDOUT: value_repr: {kind: copy, type: type(Error)} // CHECK:STDOUT: 'type(inst(NamespaceType))': // CHECK:STDOUT: value_repr: {kind: copy, type: type(inst(NamespaceType))} // CHECK:STDOUT: 'type(inst50000011)': // CHECK:STDOUT: value_repr: {kind: none, type: type(inst50000012)} // CHECK:STDOUT: 'type(inst50000012)': // CHECK:STDOUT: value_repr: {kind: none, type: type(inst50000012)} // CHECK:STDOUT: facet_types: {} // CHECK:STDOUT: insts: // CHECK:STDOUT: instF: {kind: Namespace, arg0: name_scope0, arg1: inst, type: type(inst(NamespaceType))} // CHECK:STDOUT: inst50000010: {kind: FunctionDecl, arg0: function50000000, arg1: inst_block_empty, type: type(inst50000011)} // CHECK:STDOUT: inst50000011: {kind: FunctionType, arg0: function50000000, arg1: specific, type: type(TypeType)} // CHECK:STDOUT: inst50000012: {kind: TupleType, arg0: inst_block_empty, type: type(TypeType)} // CHECK:STDOUT: inst50000013: {kind: StructValue, arg0: inst_block_empty, type: type(inst50000011)} // CHECK:STDOUT: inst50000014: {kind: Return} // CHECK:STDOUT: constant_values: // CHECK:STDOUT: values: // CHECK:STDOUT: instF: concrete_constant(instF) // CHECK:STDOUT: inst50000010: concrete_constant(inst50000013) // CHECK:STDOUT: inst50000011: concrete_constant(inst50000011) // CHECK:STDOUT: inst50000012: concrete_constant(inst50000012) // CHECK:STDOUT: inst50000013: concrete_constant(inst50000013) // CHECK:STDOUT: symbolic_constants: {} // CHECK:STDOUT: inst_blocks: // CHECK:STDOUT: inst_block_empty: {} // CHECK:STDOUT: exports: // CHECK:STDOUT: 0: inst50000010 // CHECK:STDOUT: generated: {} // CHECK:STDOUT: imports: {} // CHECK:STDOUT: global_init: {} // CHECK:STDOUT: inst_block50000005: {} // CHECK:STDOUT: inst_block50000006: // CHECK:STDOUT: 0: inst50000014 // CHECK:STDOUT: inst_block50000007: // CHECK:STDOUT: 0: instF // CHECK:STDOUT: 1: inst50000010 // CHECK:STDOUT: value_stores: // CHECK:STDOUT: shared_values: // CHECK:STDOUT: ints: {} // CHECK:STDOUT: reals: {} // CHECK:STDOUT: floats: {} // CHECK:STDOUT: identifiers: // CHECK:STDOUT: identifier0: A // CHECK:STDOUT: strings: {} // CHECK:STDOUT: ... // CHECK:STDOUT: --- a.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %A.type: type = fn_type @A [concrete] // CHECK:STDOUT: %A: %A.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .A = %A.decl // CHECK:STDOUT: } // CHECK:STDOUT: %A.decl: %A.type = fn_decl @A [concrete = constants.%A] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @A() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- // CHECK:STDOUT: filename: b.carbon // CHECK:STDOUT: sem_ir: // CHECK:STDOUT: names: // CHECK:STDOUT: name0: B // CHECK:STDOUT: name1: A // CHECK:STDOUT: import_irs: // CHECK:STDOUT: 'import_ir(ApiForImpl)': {decl_id: inst, is_export: false} // CHECK:STDOUT: 'import_ir(Cpp)': {decl_id: inst, is_export: false} // CHECK:STDOUT: import_ir70000002: {decl_id: inst70000010, is_export: false} // CHECK:STDOUT: import_ir_insts: // CHECK:STDOUT: import_ir_inst0: {ir_id: import_ir70000002, inst_id: inst50000010} // CHECK:STDOUT: import_ir_inst1: {ir_id: import_ir70000002, inst_id: inst50000010} // CHECK:STDOUT: clang_decls: {} // CHECK:STDOUT: name_scopes: // CHECK:STDOUT: name_scope0: {inst: instF, parent_scope: name_scope, has_error: false, extended_scopes: [], names: {name1: inst70000011, name0: inst70000012}} // CHECK:STDOUT: name_scope70000001: {inst: inst70000011, parent_scope: name_scope0, has_error: false, extended_scopes: [], names: {name1: inst70000017}} // CHECK:STDOUT: entity_names: // CHECK:STDOUT: entity_name70000000: {name: name1, parent_scope: name_scope70000001, index: -1, is_template: 0, is_unused: 0, form: constant} // CHECK:STDOUT: cpp_global_vars: {} // CHECK:STDOUT: functions: // CHECK:STDOUT: function70000000: {name: name0, parent_scope: name_scope0, call_param_patterns_id: inst_block_empty, call_params_id: inst_block_empty, body: [inst_block70000006]} // CHECK:STDOUT: function70000001: {name: name1, parent_scope: name_scope70000001, call_param_patterns_id: inst_block_empty} // CHECK:STDOUT: classes: {} // CHECK:STDOUT: interfaces: {} // CHECK:STDOUT: associated_constants: {} // CHECK:STDOUT: impls: {} // CHECK:STDOUT: generics: {} // CHECK:STDOUT: specifics: {} // CHECK:STDOUT: specific_interfaces: {} // CHECK:STDOUT: struct_type_fields: // CHECK:STDOUT: struct_type_fields_empty: {} // CHECK:STDOUT: types: // CHECK:STDOUT: 'type(TypeType)': // CHECK:STDOUT: value_repr: {kind: copy, type: type(TypeType)} // CHECK:STDOUT: 'type(inst(FormType))': // CHECK:STDOUT: value_repr: {kind: copy, type: type(inst(FormType))} // CHECK:STDOUT: 'type(Error)': // CHECK:STDOUT: value_repr: {kind: copy, type: type(Error)} // CHECK:STDOUT: 'type(inst(NamespaceType))': // CHECK:STDOUT: value_repr: {kind: copy, type: type(inst(NamespaceType))} // CHECK:STDOUT: 'type(inst70000013)': // CHECK:STDOUT: value_repr: {kind: none, type: type(inst70000014)} // CHECK:STDOUT: 'type(inst70000014)': // CHECK:STDOUT: value_repr: {kind: none, type: type(inst70000014)} // CHECK:STDOUT: 'type(inst(InstType))': // CHECK:STDOUT: value_repr: {kind: none, type: type(inst70000014)} // CHECK:STDOUT: facet_types: {} // CHECK:STDOUT: insts: // CHECK:STDOUT: instF: {kind: Namespace, arg0: name_scope0, arg1: inst, type: type(inst(NamespaceType))} // CHECK:STDOUT: inst70000010: {kind: ImportDecl, arg0: name1} // CHECK:STDOUT: inst70000011: {kind: Namespace, arg0: name_scope70000001, arg1: inst70000010, type: type(inst(NamespaceType))} // CHECK:STDOUT: inst70000012: {kind: FunctionDecl, arg0: function70000000, arg1: inst_block_empty, type: type(inst70000013)} // CHECK:STDOUT: inst70000013: {kind: FunctionType, arg0: function70000000, arg1: specific, type: type(TypeType)} // CHECK:STDOUT: inst70000014: {kind: TupleType, arg0: inst_block_empty, type: type(TypeType)} // CHECK:STDOUT: inst70000015: {kind: StructValue, arg0: inst_block_empty, type: type(inst70000013)} // CHECK:STDOUT: inst70000016: {kind: NameRef, arg0: name1, arg1: inst70000011, type: type(inst(NamespaceType))} // CHECK:STDOUT: inst70000017: {kind: ImportRefLoaded, arg0: import_ir_inst0, arg1: entity_name70000000, type: type(inst70000019)} // CHECK:STDOUT: inst70000018: {kind: FunctionDecl, arg0: function70000001, arg1: inst_block_empty, type: type(inst70000019)} // CHECK:STDOUT: inst70000019: {kind: FunctionType, arg0: function70000001, arg1: specific, type: type(TypeType)} // CHECK:STDOUT: inst7000001A: {kind: StructValue, arg0: inst_block_empty, type: type(inst70000019)} // CHECK:STDOUT: inst7000001B: {kind: NameRef, arg0: name1, arg1: inst70000017, type: type(inst70000019)} // CHECK:STDOUT: inst7000001C: {kind: Call, arg0: inst7000001B, arg1: inst_block_empty, type: type(inst70000014)} // CHECK:STDOUT: inst7000001D: {kind: Return} // CHECK:STDOUT: constant_values: // CHECK:STDOUT: values: // CHECK:STDOUT: instF: concrete_constant(instF) // CHECK:STDOUT: inst70000011: concrete_constant(inst70000011) // CHECK:STDOUT: inst70000012: concrete_constant(inst70000015) // CHECK:STDOUT: inst70000013: concrete_constant(inst70000013) // CHECK:STDOUT: inst70000014: concrete_constant(inst70000014) // CHECK:STDOUT: inst70000015: concrete_constant(inst70000015) // CHECK:STDOUT: inst70000016: concrete_constant(inst70000011) // CHECK:STDOUT: inst70000017: concrete_constant(inst7000001A) // CHECK:STDOUT: inst70000018: concrete_constant(inst7000001A) // CHECK:STDOUT: inst70000019: concrete_constant(inst70000019) // CHECK:STDOUT: inst7000001A: concrete_constant(inst7000001A) // CHECK:STDOUT: inst7000001B: concrete_constant(inst7000001A) // CHECK:STDOUT: symbolic_constants: {} // CHECK:STDOUT: inst_blocks: // CHECK:STDOUT: inst_block_empty: {} // CHECK:STDOUT: exports: // CHECK:STDOUT: 0: inst70000012 // CHECK:STDOUT: generated: {} // CHECK:STDOUT: imports: // CHECK:STDOUT: 0: inst70000011 // CHECK:STDOUT: 1: inst70000017 // CHECK:STDOUT: 2: inst70000018 // CHECK:STDOUT: global_init: {} // CHECK:STDOUT: inst_block70000005: {} // CHECK:STDOUT: inst_block70000006: // CHECK:STDOUT: 0: inst70000016 // CHECK:STDOUT: 1: inst7000001B // CHECK:STDOUT: 2: inst7000001C // CHECK:STDOUT: 3: inst7000001D // CHECK:STDOUT: inst_block70000007: // CHECK:STDOUT: 0: instF // CHECK:STDOUT: 1: inst70000010 // CHECK:STDOUT: 2: inst70000012 // CHECK:STDOUT: value_stores: // CHECK:STDOUT: shared_values: // CHECK:STDOUT: ints: {} // CHECK:STDOUT: reals: {} // CHECK:STDOUT: floats: {} // CHECK:STDOUT: identifiers: // CHECK:STDOUT: identifier0: B // CHECK:STDOUT: identifier1: A // CHECK:STDOUT: strings: {} // CHECK:STDOUT: ... // CHECK:STDOUT: --- b.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %B.type: type = fn_type @B [concrete] // CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] // CHECK:STDOUT: %B: %B.type = struct_value () [concrete] // CHECK:STDOUT: %A.type: type = fn_type @A [concrete] // CHECK:STDOUT: %A: %A.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %A: = namespace file.%A.import, [concrete] { // CHECK:STDOUT: .A = %A.A // CHECK:STDOUT: import A//default // CHECK:STDOUT: } // CHECK:STDOUT: %A.A: %A.type = import_ref A//default, A, loaded [concrete = constants.%A] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .A = imports.%A // CHECK:STDOUT: .B = %B.decl // CHECK:STDOUT: } // CHECK:STDOUT: %A.import = import A // CHECK:STDOUT: %B.decl: %B.type = fn_decl @B [concrete = constants.%B] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @B() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %A.ref.loc6_3: = name_ref A, imports.%A [concrete = imports.%A] // CHECK:STDOUT: %A.ref.loc6_4: %A.type = name_ref A, imports.%A.A [concrete = constants.%A] // CHECK:STDOUT: %A.call: init %empty_tuple.type = call %A.ref.loc6_4() // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @A [from "a.carbon"]; // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/basics/raw_sem_ir/one_file.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/convert.carbon // EXTRA-ARGS: --dump-raw-sem-ir --no-dump-sem-ir // // Check that raw IR dumping works as expected. // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/basics/raw_sem_ir/one_file.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/basics/raw_sem_ir/one_file.carbon fn Foo[T:! type](p: T*) -> (T*, ()) { return (p, ()); } // CHECK:STDOUT: --- // CHECK:STDOUT: filename: one_file.carbon // CHECK:STDOUT: sem_ir: // CHECK:STDOUT: names: // CHECK:STDOUT: name0: Foo // CHECK:STDOUT: name1: T // CHECK:STDOUT: name2: p // CHECK:STDOUT: name3: Copy // CHECK:STDOUT: name4: Op // CHECK:STDOUT: name5: U // CHECK:STDOUT: name6: V // CHECK:STDOUT: import_irs: // CHECK:STDOUT: 'import_ir(ApiForImpl)': {decl_id: inst, is_export: false} // CHECK:STDOUT: 'import_ir(Cpp)': {decl_id: inst, is_export: false} // CHECK:STDOUT: import_ir78000002: {decl_id: inst78000010, is_export: false} // CHECK:STDOUT: import_ir78000003: {decl_id: inst78000010, is_export: false} // CHECK:STDOUT: import_ir78000004: {decl_id: inst78000010, is_export: false} // CHECK:STDOUT: import_ir78000005: {decl_id: inst78000010, is_export: false} // CHECK:STDOUT: import_ir78000006: {decl_id: inst78000010, is_export: false} // CHECK:STDOUT: import_ir78000007: {decl_id: inst78000010, is_export: false} // CHECK:STDOUT: import_ir_insts: // CHECK:STDOUT: import_ir_inst0: {ir_id: import_ir78000004, inst_id: inst70000010} // CHECK:STDOUT: import_ir_inst1: {ir_id: import_ir78000004, inst_id: inst70000010} // CHECK:STDOUT: import_ir_inst2: {ir_id: import_ir78000004, inst_id: inst70000015} // CHECK:STDOUT: import_ir_inst3: {ir_id: import_ir78000004, inst_id: inst70000035} // CHECK:STDOUT: import_ir_inst4: {ir_id: import_ir78000004, inst_id: inst7000002D} // CHECK:STDOUT: import_ir_inst5: {ir_id: import_ir78000004, inst_id: inst70000012} // CHECK:STDOUT: import_ir_inst6: {ir_id: import_ir78000004, inst_id: inst7000002D} // CHECK:STDOUT: import_ir_inst7: {ir_id: import_ir78000004, inst_id: inst70000027} // CHECK:STDOUT: import_ir_inst8: {ir_id: import_ir78000004, inst_id: inst70000028} // CHECK:STDOUT: import_ir_inst9: {ir_id: import_ir78000004, inst_id: inst7000001E} // CHECK:STDOUT: import_ir_instA: {ir_id: import_ir78000004, inst_id: inst70000020} // CHECK:STDOUT: import_ir_instB: {ir_id: import_ir78000004, inst_id: inst70000023} // CHECK:STDOUT: import_ir_instC: {ir_id: import_ir78000004, inst_id: inst70000024} // CHECK:STDOUT: import_ir_instD: {ir_id: import_ir78000004, inst_id: inst70000012} // CHECK:STDOUT: import_ir_instE: {ir_id: import_ir78000004, inst_id: inst70000017} // CHECK:STDOUT: import_ir_instF: {ir_id: import_ir78000004, inst_id: inst7000001A} // CHECK:STDOUT: import_ir_inst10: {ir_id: import_ir78000004, inst_id: inst7000001F} // CHECK:STDOUT: import_ir_inst11: {ir_id: import_ir78000004, inst_id: inst70000026} // CHECK:STDOUT: import_ir_inst12: {ir_id: import_ir78000004, inst_id: inst70000031} // CHECK:STDOUT: import_ir_inst13: {ir_id: import_ir78000004, inst_id: inst70000032} // CHECK:STDOUT: import_ir_inst14: {ir_id: import_ir78000004, inst_id: inst70000033} // CHECK:STDOUT: import_ir_inst15: {ir_id: import_ir78000004, inst_id: inst70000012} // CHECK:STDOUT: import_ir_inst16: {ir_id: import_ir78000004, inst_id: inst7000002D} // CHECK:STDOUT: import_ir_inst17: {ir_id: import_ir78000004, inst_id: inst70000072} // CHECK:STDOUT: import_ir_inst18: {ir_id: import_ir78000004, inst_id: inst70000070} // CHECK:STDOUT: import_ir_inst19: {ir_id: import_ir78000004, inst_id: inst70000089} // CHECK:STDOUT: import_ir_inst1A: {ir_id: import_ir78000004, inst_id: inst70000071} // CHECK:STDOUT: import_ir_inst1B: {ir_id: import_ir78000004, inst_id: inst70000089} // CHECK:STDOUT: import_ir_inst1C: {ir_id: import_ir78000004, inst_id: inst70000084} // CHECK:STDOUT: import_ir_inst1D: {ir_id: import_ir78000004, inst_id: inst70000085} // CHECK:STDOUT: import_ir_inst1E: {ir_id: import_ir78000004, inst_id: inst7000007D} // CHECK:STDOUT: import_ir_inst1F: {ir_id: import_ir78000004, inst_id: inst7000007F} // CHECK:STDOUT: import_ir_inst20: {ir_id: import_ir78000004, inst_id: inst70000080} // CHECK:STDOUT: import_ir_inst21: {ir_id: import_ir78000004, inst_id: inst70000081} // CHECK:STDOUT: import_ir_inst22: {ir_id: import_ir78000004, inst_id: inst70000062} // CHECK:STDOUT: import_ir_inst23: {ir_id: import_ir78000004, inst_id: inst70000078} // CHECK:STDOUT: import_ir_inst24: {ir_id: import_ir78000004, inst_id: inst70000079} // CHECK:STDOUT: import_ir_inst25: {ir_id: import_ir78000004, inst_id: inst7000007A} // CHECK:STDOUT: import_ir_inst26: {ir_id: import_ir78000004, inst_id: inst7000007E} // CHECK:STDOUT: import_ir_inst27: {ir_id: import_ir78000004, inst_id: inst70000083} // CHECK:STDOUT: import_ir_inst28: {ir_id: import_ir78000004, inst_id: inst70000093} // CHECK:STDOUT: import_ir_inst29: {ir_id: import_ir78000004, inst_id: inst7000009A} // CHECK:STDOUT: import_ir_inst2A: {ir_id: import_ir78000004, inst_id: inst700000A1} // CHECK:STDOUT: import_ir_inst2B: {ir_id: import_ir78000004, inst_id: inst700000A7} // CHECK:STDOUT: import_ir_inst2C: {ir_id: import_ir78000004, inst_id: inst700000A8} // CHECK:STDOUT: import_ir_inst2D: {ir_id: import_ir78000004, inst_id: inst700000A9} // CHECK:STDOUT: import_ir_inst2E: {ir_id: import_ir78000004, inst_id: inst700000AF} // CHECK:STDOUT: import_ir_inst2F: {ir_id: import_ir78000004, inst_id: inst70000065} // CHECK:STDOUT: import_ir_inst30: {ir_id: import_ir78000004, inst_id: inst7000006B} // CHECK:STDOUT: import_ir_inst31: {ir_id: import_ir78000004, inst_id: inst7000006E} // CHECK:STDOUT: import_ir_inst32: {ir_id: import_ir78000004, inst_id: inst70000062} // CHECK:STDOUT: import_ir_inst33: {ir_id: import_ir78000004, inst_id: inst70000064} // CHECK:STDOUT: import_ir_inst34: {ir_id: import_ir78000004, inst_id: inst70000069} // CHECK:STDOUT: import_ir_inst35: {ir_id: import_ir78000004, inst_id: inst7000006D} // CHECK:STDOUT: import_ir_inst36: {ir_id: import_ir78000004, inst_id: inst70000074} // CHECK:STDOUT: import_ir_inst37: {ir_id: import_ir78000004, inst_id: inst7000008C} // CHECK:STDOUT: import_ir_inst38: {ir_id: import_ir78000004, inst_id: inst7000008D} // CHECK:STDOUT: import_ir_inst39: {ir_id: import_ir78000004, inst_id: inst700000C2} // CHECK:STDOUT: import_ir_inst3A: {ir_id: import_ir78000004, inst_id: inst700000C0} // CHECK:STDOUT: import_ir_inst3B: {ir_id: import_ir78000004, inst_id: inst700000BE} // CHECK:STDOUT: import_ir_inst3C: {ir_id: import_ir78000004, inst_id: inst700000BF} // CHECK:STDOUT: import_ir_inst3D: {ir_id: import_ir78000004, inst_id: inst700000DE} // CHECK:STDOUT: import_ir_inst3E: {ir_id: import_ir78000004, inst_id: inst700000DC} // CHECK:STDOUT: import_ir_inst3F: {ir_id: import_ir78000004, inst_id: inst700000DA} // CHECK:STDOUT: import_ir_inst40: {ir_id: import_ir78000004, inst_id: inst700000DB} // CHECK:STDOUT: import_ir_inst41: {ir_id: import_ir78000004, inst_id: inst700000FA} // CHECK:STDOUT: import_ir_inst42: {ir_id: import_ir78000004, inst_id: inst700000F8} // CHECK:STDOUT: import_ir_inst43: {ir_id: import_ir78000004, inst_id: inst700000F6} // CHECK:STDOUT: import_ir_inst44: {ir_id: import_ir78000004, inst_id: inst700000F7} // CHECK:STDOUT: import_ir_inst45: {ir_id: import_ir78000004, inst_id: inst70000116} // CHECK:STDOUT: import_ir_inst46: {ir_id: import_ir78000004, inst_id: inst70000114} // CHECK:STDOUT: import_ir_inst47: {ir_id: import_ir78000004, inst_id: inst70000112} // CHECK:STDOUT: import_ir_inst48: {ir_id: import_ir78000004, inst_id: inst70000113} // CHECK:STDOUT: import_ir_inst49: {ir_id: import_ir78000004, inst_id: inst70000139} // CHECK:STDOUT: import_ir_inst4A: {ir_id: import_ir78000004, inst_id: inst70000137} // CHECK:STDOUT: import_ir_inst4B: {ir_id: import_ir78000004, inst_id: inst7000014D} // CHECK:STDOUT: import_ir_inst4C: {ir_id: import_ir78000004, inst_id: inst70000138} // CHECK:STDOUT: import_ir_inst4D: {ir_id: import_ir78000004, inst_id: inst70000130} // CHECK:STDOUT: import_ir_inst4E: {ir_id: import_ir78000004, inst_id: inst70000132} // CHECK:STDOUT: import_ir_inst4F: {ir_id: import_ir78000004, inst_id: inst70000135} // CHECK:STDOUT: import_ir_inst50: {ir_id: import_ir78000004, inst_id: inst7000012D} // CHECK:STDOUT: import_ir_inst51: {ir_id: import_ir78000004, inst_id: inst7000012F} // CHECK:STDOUT: import_ir_inst52: {ir_id: import_ir78000004, inst_id: inst70000134} // CHECK:STDOUT: import_ir_inst53: {ir_id: import_ir78000004, inst_id: inst7000013B} // CHECK:STDOUT: import_ir_inst54: {ir_id: import_ir78000004, inst_id: inst7000014D} // CHECK:STDOUT: import_ir_inst55: {ir_id: import_ir78000004, inst_id: inst70000148} // CHECK:STDOUT: import_ir_inst56: {ir_id: import_ir78000004, inst_id: inst70000149} // CHECK:STDOUT: import_ir_inst57: {ir_id: import_ir78000004, inst_id: inst70000141} // CHECK:STDOUT: import_ir_inst58: {ir_id: import_ir78000004, inst_id: inst70000143} // CHECK:STDOUT: import_ir_inst59: {ir_id: import_ir78000004, inst_id: inst70000144} // CHECK:STDOUT: import_ir_inst5A: {ir_id: import_ir78000004, inst_id: inst70000145} // CHECK:STDOUT: import_ir_inst5B: {ir_id: import_ir78000004, inst_id: inst7000012D} // CHECK:STDOUT: import_ir_inst5C: {ir_id: import_ir78000004, inst_id: inst7000013D} // CHECK:STDOUT: import_ir_inst5D: {ir_id: import_ir78000004, inst_id: inst7000013E} // CHECK:STDOUT: import_ir_inst5E: {ir_id: import_ir78000004, inst_id: inst70000142} // CHECK:STDOUT: import_ir_inst5F: {ir_id: import_ir78000004, inst_id: inst70000147} // CHECK:STDOUT: import_ir_inst60: {ir_id: import_ir78000004, inst_id: inst70000150} // CHECK:STDOUT: import_ir_inst61: {ir_id: import_ir78000004, inst_id: inst70000151} // CHECK:STDOUT: import_ir_inst62: {ir_id: import_ir78000004, inst_id: inst70000154} // CHECK:STDOUT: import_ir_inst63: {ir_id: import_ir78000004, inst_id: inst7000015C} // CHECK:STDOUT: import_ir_inst64: {ir_id: import_ir78000004, inst_id: inst7000015A} // CHECK:STDOUT: import_ir_inst65: {ir_id: import_ir78000004, inst_id: inst70000158} // CHECK:STDOUT: import_ir_inst66: {ir_id: import_ir78000004, inst_id: inst70000159} // CHECK:STDOUT: import_ir_inst67: {ir_id: import_ir78000004, inst_id: inst70000175} // CHECK:STDOUT: import_ir_inst68: {ir_id: import_ir78000004, inst_id: inst70000173} // CHECK:STDOUT: import_ir_inst69: {ir_id: import_ir78000004, inst_id: inst70000171} // CHECK:STDOUT: import_ir_inst6A: {ir_id: import_ir78000004, inst_id: inst70000172} // CHECK:STDOUT: import_ir_inst6B: {ir_id: import_ir78000004, inst_id: inst700001AB} // CHECK:STDOUT: import_ir_inst6C: {ir_id: import_ir78000004, inst_id: inst700001A9} // CHECK:STDOUT: import_ir_inst6D: {ir_id: import_ir78000004, inst_id: inst700001C6} // CHECK:STDOUT: import_ir_inst6E: {ir_id: import_ir78000004, inst_id: inst700001AA} // CHECK:STDOUT: import_ir_inst6F: {ir_id: import_ir78000004, inst_id: inst700001C6} // CHECK:STDOUT: import_ir_inst70: {ir_id: import_ir78000004, inst_id: inst700001C1} // CHECK:STDOUT: import_ir_inst71: {ir_id: import_ir78000004, inst_id: inst700001C2} // CHECK:STDOUT: import_ir_inst72: {ir_id: import_ir78000004, inst_id: inst700001BA} // CHECK:STDOUT: import_ir_inst73: {ir_id: import_ir78000004, inst_id: inst700001BC} // CHECK:STDOUT: import_ir_inst74: {ir_id: import_ir78000004, inst_id: inst700001BD} // CHECK:STDOUT: import_ir_inst75: {ir_id: import_ir78000004, inst_id: inst700001BE} // CHECK:STDOUT: import_ir_inst76: {ir_id: import_ir78000004, inst_id: inst7000018C} // CHECK:STDOUT: import_ir_inst77: {ir_id: import_ir78000004, inst_id: inst70000191} // CHECK:STDOUT: import_ir_inst78: {ir_id: import_ir78000004, inst_id: inst700001B3} // CHECK:STDOUT: import_ir_inst79: {ir_id: import_ir78000004, inst_id: inst700001B4} // CHECK:STDOUT: import_ir_inst7A: {ir_id: import_ir78000004, inst_id: inst700001B5} // CHECK:STDOUT: import_ir_inst7B: {ir_id: import_ir78000004, inst_id: inst700001B6} // CHECK:STDOUT: import_ir_inst7C: {ir_id: import_ir78000004, inst_id: inst700001B7} // CHECK:STDOUT: import_ir_inst7D: {ir_id: import_ir78000004, inst_id: inst700001BB} // CHECK:STDOUT: import_ir_inst7E: {ir_id: import_ir78000004, inst_id: inst700001C0} // CHECK:STDOUT: import_ir_inst7F: {ir_id: import_ir78000004, inst_id: inst700001D1} // CHECK:STDOUT: import_ir_inst80: {ir_id: import_ir78000004, inst_id: inst700001D7} // CHECK:STDOUT: import_ir_inst81: {ir_id: import_ir78000004, inst_id: inst700001DA} // CHECK:STDOUT: import_ir_inst82: {ir_id: import_ir78000004, inst_id: inst700001DC} // CHECK:STDOUT: import_ir_inst83: {ir_id: import_ir78000004, inst_id: inst700001DD} // CHECK:STDOUT: import_ir_inst84: {ir_id: import_ir78000004, inst_id: inst700001DE} // CHECK:STDOUT: import_ir_inst85: {ir_id: import_ir78000004, inst_id: inst700001E1} // CHECK:STDOUT: import_ir_inst86: {ir_id: import_ir78000004, inst_id: inst700001EB} // CHECK:STDOUT: import_ir_inst87: {ir_id: import_ir78000004, inst_id: inst700001F1} // CHECK:STDOUT: import_ir_inst88: {ir_id: import_ir78000004, inst_id: inst700001F5} // CHECK:STDOUT: import_ir_inst89: {ir_id: import_ir78000004, inst_id: inst700001F6} // CHECK:STDOUT: import_ir_inst8A: {ir_id: import_ir78000004, inst_id: inst700001F7} // CHECK:STDOUT: import_ir_inst8B: {ir_id: import_ir78000004, inst_id: inst700001FD} // CHECK:STDOUT: import_ir_inst8C: {ir_id: import_ir78000004, inst_id: inst70000194} // CHECK:STDOUT: import_ir_inst8D: {ir_id: import_ir78000004, inst_id: inst7000018E} // CHECK:STDOUT: import_ir_inst8E: {ir_id: import_ir78000004, inst_id: inst700001A4} // CHECK:STDOUT: import_ir_inst8F: {ir_id: import_ir78000004, inst_id: inst700001A6} // CHECK:STDOUT: import_ir_inst90: {ir_id: import_ir78000004, inst_id: inst7000018C} // CHECK:STDOUT: import_ir_inst91: {ir_id: import_ir78000004, inst_id: inst70000191} // CHECK:STDOUT: import_ir_inst92: {ir_id: import_ir78000004, inst_id: inst7000018D} // CHECK:STDOUT: import_ir_inst93: {ir_id: import_ir78000004, inst_id: inst70000193} // CHECK:STDOUT: import_ir_inst94: {ir_id: import_ir78000004, inst_id: inst7000019A} // CHECK:STDOUT: import_ir_inst95: {ir_id: import_ir78000004, inst_id: inst7000019D} // CHECK:STDOUT: import_ir_inst96: {ir_id: import_ir78000004, inst_id: inst700001A1} // CHECK:STDOUT: import_ir_inst97: {ir_id: import_ir78000004, inst_id: inst700001A5} // CHECK:STDOUT: import_ir_inst98: {ir_id: import_ir78000004, inst_id: inst700001AD} // CHECK:STDOUT: import_ir_inst99: {ir_id: import_ir78000004, inst_id: inst700001C9} // CHECK:STDOUT: import_ir_inst9A: {ir_id: import_ir78000004, inst_id: inst700001CA} // CHECK:STDOUT: import_ir_inst9B: {ir_id: import_ir78000004, inst_id: inst70000237} // CHECK:STDOUT: import_ir_inst9C: {ir_id: import_ir78000004, inst_id: inst70000235} // CHECK:STDOUT: import_ir_inst9D: {ir_id: import_ir78000004, inst_id: inst70000256} // CHECK:STDOUT: import_ir_inst9E: {ir_id: import_ir78000004, inst_id: inst70000236} // CHECK:STDOUT: import_ir_inst9F: {ir_id: import_ir78000004, inst_id: inst70000256} // CHECK:STDOUT: import_ir_instA0: {ir_id: import_ir78000004, inst_id: inst70000251} // CHECK:STDOUT: import_ir_instA1: {ir_id: import_ir78000004, inst_id: inst70000252} // CHECK:STDOUT: import_ir_instA2: {ir_id: import_ir78000004, inst_id: inst7000024A} // CHECK:STDOUT: import_ir_instA3: {ir_id: import_ir78000004, inst_id: inst7000024C} // CHECK:STDOUT: import_ir_instA4: {ir_id: import_ir78000004, inst_id: inst7000024D} // CHECK:STDOUT: import_ir_instA5: {ir_id: import_ir78000004, inst_id: inst7000024E} // CHECK:STDOUT: import_ir_instA6: {ir_id: import_ir78000004, inst_id: inst7000020E} // CHECK:STDOUT: import_ir_instA7: {ir_id: import_ir78000004, inst_id: inst70000213} // CHECK:STDOUT: import_ir_instA8: {ir_id: import_ir78000004, inst_id: inst70000218} // CHECK:STDOUT: import_ir_instA9: {ir_id: import_ir78000004, inst_id: inst70000241} // CHECK:STDOUT: import_ir_instAA: {ir_id: import_ir78000004, inst_id: inst70000242} // CHECK:STDOUT: import_ir_instAB: {ir_id: import_ir78000004, inst_id: inst70000243} // CHECK:STDOUT: import_ir_instAC: {ir_id: import_ir78000004, inst_id: inst70000244} // CHECK:STDOUT: import_ir_instAD: {ir_id: import_ir78000004, inst_id: inst70000245} // CHECK:STDOUT: import_ir_instAE: {ir_id: import_ir78000004, inst_id: inst70000246} // CHECK:STDOUT: import_ir_instAF: {ir_id: import_ir78000004, inst_id: inst70000247} // CHECK:STDOUT: import_ir_instB0: {ir_id: import_ir78000004, inst_id: inst7000024B} // CHECK:STDOUT: import_ir_instB1: {ir_id: import_ir78000004, inst_id: inst70000250} // CHECK:STDOUT: import_ir_instB2: {ir_id: import_ir78000004, inst_id: inst70000261} // CHECK:STDOUT: import_ir_instB3: {ir_id: import_ir78000004, inst_id: inst70000266} // CHECK:STDOUT: import_ir_instB4: {ir_id: import_ir78000004, inst_id: inst70000269} // CHECK:STDOUT: import_ir_instB5: {ir_id: import_ir78000004, inst_id: inst7000026B} // CHECK:STDOUT: import_ir_instB6: {ir_id: import_ir78000004, inst_id: inst7000026C} // CHECK:STDOUT: import_ir_instB7: {ir_id: import_ir78000004, inst_id: inst7000026D} // CHECK:STDOUT: import_ir_instB8: {ir_id: import_ir78000004, inst_id: inst70000270} // CHECK:STDOUT: import_ir_instB9: {ir_id: import_ir78000004, inst_id: inst70000278} // CHECK:STDOUT: import_ir_instBA: {ir_id: import_ir78000004, inst_id: inst7000027B} // CHECK:STDOUT: import_ir_instBB: {ir_id: import_ir78000004, inst_id: inst7000027D} // CHECK:STDOUT: import_ir_instBC: {ir_id: import_ir78000004, inst_id: inst7000027E} // CHECK:STDOUT: import_ir_instBD: {ir_id: import_ir78000004, inst_id: inst7000027F} // CHECK:STDOUT: import_ir_instBE: {ir_id: import_ir78000004, inst_id: inst70000282} // CHECK:STDOUT: import_ir_instBF: {ir_id: import_ir78000004, inst_id: inst7000028C} // CHECK:STDOUT: import_ir_instC0: {ir_id: import_ir78000004, inst_id: inst70000292} // CHECK:STDOUT: import_ir_instC1: {ir_id: import_ir78000004, inst_id: inst70000296} // CHECK:STDOUT: import_ir_instC2: {ir_id: import_ir78000004, inst_id: inst70000297} // CHECK:STDOUT: import_ir_instC3: {ir_id: import_ir78000004, inst_id: inst70000298} // CHECK:STDOUT: import_ir_instC4: {ir_id: import_ir78000004, inst_id: inst7000029E} // CHECK:STDOUT: import_ir_instC5: {ir_id: import_ir78000004, inst_id: inst7000021B} // CHECK:STDOUT: import_ir_instC6: {ir_id: import_ir78000004, inst_id: inst70000215} // CHECK:STDOUT: import_ir_instC7: {ir_id: import_ir78000004, inst_id: inst70000210} // CHECK:STDOUT: import_ir_instC8: {ir_id: import_ir78000004, inst_id: inst7000022F} // CHECK:STDOUT: import_ir_instC9: {ir_id: import_ir78000004, inst_id: inst70000231} // CHECK:STDOUT: import_ir_instCA: {ir_id: import_ir78000004, inst_id: inst7000020E} // CHECK:STDOUT: import_ir_instCB: {ir_id: import_ir78000004, inst_id: inst70000213} // CHECK:STDOUT: import_ir_instCC: {ir_id: import_ir78000004, inst_id: inst70000218} // CHECK:STDOUT: import_ir_instCD: {ir_id: import_ir78000004, inst_id: inst7000020F} // CHECK:STDOUT: import_ir_instCE: {ir_id: import_ir78000004, inst_id: inst70000214} // CHECK:STDOUT: import_ir_instCF: {ir_id: import_ir78000004, inst_id: inst7000021A} // CHECK:STDOUT: import_ir_instD0: {ir_id: import_ir78000004, inst_id: inst70000222} // CHECK:STDOUT: import_ir_instD1: {ir_id: import_ir78000004, inst_id: inst70000225} // CHECK:STDOUT: import_ir_instD2: {ir_id: import_ir78000004, inst_id: inst70000228} // CHECK:STDOUT: import_ir_instD3: {ir_id: import_ir78000004, inst_id: inst7000022C} // CHECK:STDOUT: import_ir_instD4: {ir_id: import_ir78000004, inst_id: inst70000230} // CHECK:STDOUT: import_ir_instD5: {ir_id: import_ir78000004, inst_id: inst70000239} // CHECK:STDOUT: import_ir_instD6: {ir_id: import_ir78000004, inst_id: inst70000259} // CHECK:STDOUT: import_ir_instD7: {ir_id: import_ir78000004, inst_id: inst7000025A} // CHECK:STDOUT: clang_decls: {} // CHECK:STDOUT: name_scopes: // CHECK:STDOUT: name_scope0: {inst: instF, parent_scope: name_scope, has_error: false, extended_scopes: [], names: {name(Core): inst78000011, name0: inst7800003E}} // CHECK:STDOUT: name_scope78000001: {inst: inst78000011, parent_scope: name_scope0, has_error: false, extended_scopes: [], names: {name3: inst7800004E}} // CHECK:STDOUT: name_scope78000002: {inst: inst7800004F, parent_scope: name_scope78000001, has_error: false, extended_scopes: [], names: {name(SelfType): inst7800006A}} // CHECK:STDOUT: name_scope78000003: {inst: inst78000051, parent_scope: name_scope78000002, has_error: false, extended_scopes: [], names: {name4: inst78000053}} // CHECK:STDOUT: name_scope78000004: {inst: inst78000070, parent_scope: name_scope78000001, has_error: false, extended_scopes: [], names: {}} // CHECK:STDOUT: name_scope78000005: {inst: inst780000A5, parent_scope: name_scope78000001, has_error: false, extended_scopes: [], names: {}} // CHECK:STDOUT: name_scope78000006: {inst: inst780000A9, parent_scope: name_scope78000001, has_error: false, extended_scopes: [], names: {}} // CHECK:STDOUT: name_scope78000007: {inst: inst780000AD, parent_scope: name_scope78000001, has_error: false, extended_scopes: [], names: {}} // CHECK:STDOUT: name_scope78000008: {inst: inst780000B1, parent_scope: name_scope78000001, has_error: false, extended_scopes: [], names: {}} // CHECK:STDOUT: name_scope78000009: {inst: inst780000B5, parent_scope: name_scope78000001, has_error: false, extended_scopes: [], names: {}} // CHECK:STDOUT: name_scope7800000A: {inst: inst780000D3, parent_scope: name_scope78000001, has_error: false, extended_scopes: [], names: {}} // CHECK:STDOUT: name_scope7800000B: {inst: inst780000D7, parent_scope: name_scope78000001, has_error: false, extended_scopes: [], names: {}} // CHECK:STDOUT: name_scope7800000C: {inst: inst780000DB, parent_scope: name_scope78000001, has_error: false, extended_scopes: [], names: {}} // CHECK:STDOUT: name_scope7800000D: {inst: inst7800011F, parent_scope: name_scope78000001, has_error: false, extended_scopes: [], names: {}} // CHECK:STDOUT: entity_names: // CHECK:STDOUT: entity_name78000000: {name: name(PeriodSelf), parent_scope: name_scope, index: -1, is_template: 0, is_unused: 0, form: constant} // CHECK:STDOUT: entity_name78000001: {name: name1, parent_scope: name_scope, index: 0, is_template: 0, is_unused: 0, form: constant} // CHECK:STDOUT: entity_name78000002: {name: name2, parent_scope: name_scope, index: -1, is_template: 0, is_unused: 0, form: constant} // CHECK:STDOUT: entity_name78000003: {name: name3, parent_scope: name_scope78000001, index: -1, is_template: 0, is_unused: 0, form: constant} // CHECK:STDOUT: entity_name78000004: {name: name(SelfType), parent_scope: name_scope, index: 0, is_template: 0, is_unused: 0, form: constant} // CHECK:STDOUT: entity_name78000005: {name: name4, parent_scope: name_scope78000003, index: -1, is_template: 0, is_unused: 0, form: constant} // CHECK:STDOUT: entity_name78000006: {name: name(SelfType), parent_scope: name_scope, index: 0, is_template: 0, is_unused: 0, form: constant} // CHECK:STDOUT: entity_name78000007: {name: name(SelfType), parent_scope: name_scope, index: 0, is_template: 0, is_unused: 0, form: constant} // CHECK:STDOUT: entity_name78000008: {name: name(SelfValue), parent_scope: name_scope, index: -1, is_template: 0, is_unused: 0, form: constant} // CHECK:STDOUT: entity_name78000009: {name: name(SelfType), parent_scope: name_scope, index: 0, is_template: 0, is_unused: 0, form: constant} // CHECK:STDOUT: entity_name7800000A: {name: name(SelfType), parent_scope: name_scope, index: 0, is_template: 0, is_unused: 0, form: constant} // CHECK:STDOUT: entity_name7800000B: {name: name(SelfType), parent_scope: name_scope, index: 0, is_template: 0, is_unused: 0, form: constant} // CHECK:STDOUT: entity_name7800000C: {name: name(SelfType), parent_scope: name_scope, index: 0, is_template: 0, is_unused: 0, form: constant} // CHECK:STDOUT: entity_name7800000D: {name: name1, parent_scope: name_scope, index: 0, is_template: 0, is_unused: 0, form: constant} // CHECK:STDOUT: entity_name7800000E: {name: name1, parent_scope: name_scope, index: 0, is_template: 0, is_unused: 0, form: constant} // CHECK:STDOUT: entity_name7800000F: {name: name(SelfValue), parent_scope: name_scope, index: -1, is_template: 0, is_unused: 0, form: constant} // CHECK:STDOUT: entity_name78000010: {name: name1, parent_scope: name_scope, index: 0, is_template: 0, is_unused: 0, form: constant} // CHECK:STDOUT: entity_name78000011: {name: name1, parent_scope: name_scope, index: 0, is_template: 0, is_unused: 0, form: constant} // CHECK:STDOUT: entity_name78000012: {name: name1, parent_scope: name_scope, index: 0, is_template: 0, is_unused: 0, form: constant} // CHECK:STDOUT: entity_name78000013: {name: name1, parent_scope: name_scope, index: 0, is_template: 0, is_unused: 0, form: constant} // CHECK:STDOUT: entity_name78000014: {name: name1, parent_scope: name_scope, index: 0, is_template: 0, is_unused: 0, form: constant} // CHECK:STDOUT: entity_name78000015: {name: name1, parent_scope: name_scope, index: 0, is_template: 0, is_unused: 0, form: constant} // CHECK:STDOUT: entity_name78000016: {name: name1, parent_scope: name_scope, index: 0, is_template: 0, is_unused: 0, form: constant} // CHECK:STDOUT: entity_name78000017: {name: name1, parent_scope: name_scope, index: 0, is_template: 0, is_unused: 0, form: constant} // CHECK:STDOUT: entity_name78000018: {name: name1, parent_scope: name_scope, index: 0, is_template: 0, is_unused: 0, form: constant} // CHECK:STDOUT: entity_name78000019: {name: name1, parent_scope: name_scope, index: 0, is_template: 0, is_unused: 0, form: constant} // CHECK:STDOUT: entity_name7800001A: {name: name(SelfValue), parent_scope: name_scope, index: -1, is_template: 0, is_unused: 0, form: constant} // CHECK:STDOUT: entity_name7800001B: {name: name1, parent_scope: name_scope, index: 0, is_template: 0, is_unused: 0, form: constant} // CHECK:STDOUT: entity_name7800001C: {name: name5, parent_scope: name_scope, index: 1, is_template: 0, is_unused: 0, form: constant} // CHECK:STDOUT: entity_name7800001D: {name: name5, parent_scope: name_scope, index: 1, is_template: 0, is_unused: 0, form: constant} // CHECK:STDOUT: entity_name7800001E: {name: name(SelfValue), parent_scope: name_scope, index: -1, is_template: 0, is_unused: 0, form: constant} // CHECK:STDOUT: entity_name7800001F: {name: name5, parent_scope: name_scope, index: 1, is_template: 0, is_unused: 0, form: constant} // CHECK:STDOUT: entity_name78000020: {name: name5, parent_scope: name_scope, index: 1, is_template: 0, is_unused: 0, form: constant} // CHECK:STDOUT: entity_name78000021: {name: name1, parent_scope: name_scope, index: 0, is_template: 0, is_unused: 0, form: constant} // CHECK:STDOUT: entity_name78000022: {name: name1, parent_scope: name_scope, index: 0, is_template: 0, is_unused: 0, form: constant} // CHECK:STDOUT: entity_name78000023: {name: name5, parent_scope: name_scope, index: 1, is_template: 0, is_unused: 0, form: constant} // CHECK:STDOUT: entity_name78000024: {name: name1, parent_scope: name_scope, index: 0, is_template: 0, is_unused: 0, form: constant} // CHECK:STDOUT: entity_name78000025: {name: name5, parent_scope: name_scope, index: 1, is_template: 0, is_unused: 0, form: constant} // CHECK:STDOUT: entity_name78000026: {name: name1, parent_scope: name_scope, index: 0, is_template: 0, is_unused: 0, form: constant} // CHECK:STDOUT: entity_name78000027: {name: name5, parent_scope: name_scope, index: 1, is_template: 0, is_unused: 0, form: constant} // CHECK:STDOUT: entity_name78000028: {name: name1, parent_scope: name_scope, index: 0, is_template: 0, is_unused: 0, form: constant} // CHECK:STDOUT: entity_name78000029: {name: name5, parent_scope: name_scope, index: 1, is_template: 0, is_unused: 0, form: constant} // CHECK:STDOUT: entity_name7800002A: {name: name1, parent_scope: name_scope, index: 0, is_template: 0, is_unused: 0, form: constant} // CHECK:STDOUT: entity_name7800002B: {name: name6, parent_scope: name_scope, index: 2, is_template: 0, is_unused: 0, form: constant} // CHECK:STDOUT: entity_name7800002C: {name: name6, parent_scope: name_scope, index: 2, is_template: 0, is_unused: 0, form: constant} // CHECK:STDOUT: entity_name7800002D: {name: name(SelfValue), parent_scope: name_scope, index: -1, is_template: 0, is_unused: 0, form: constant} // CHECK:STDOUT: entity_name7800002E: {name: name6, parent_scope: name_scope, index: 2, is_template: 0, is_unused: 0, form: constant} // CHECK:STDOUT: entity_name7800002F: {name: name6, parent_scope: name_scope, index: 2, is_template: 0, is_unused: 0, form: constant} // CHECK:STDOUT: entity_name78000030: {name: name5, parent_scope: name_scope, index: 1, is_template: 0, is_unused: 0, form: constant} // CHECK:STDOUT: entity_name78000031: {name: name5, parent_scope: name_scope, index: 1, is_template: 0, is_unused: 0, form: constant} // CHECK:STDOUT: entity_name78000032: {name: name1, parent_scope: name_scope, index: 0, is_template: 0, is_unused: 0, form: constant} // CHECK:STDOUT: entity_name78000033: {name: name1, parent_scope: name_scope, index: 0, is_template: 0, is_unused: 0, form: constant} // CHECK:STDOUT: entity_name78000034: {name: name6, parent_scope: name_scope, index: 2, is_template: 0, is_unused: 0, form: constant} // CHECK:STDOUT: entity_name78000035: {name: name5, parent_scope: name_scope, index: 1, is_template: 0, is_unused: 0, form: constant} // CHECK:STDOUT: entity_name78000036: {name: name1, parent_scope: name_scope, index: 0, is_template: 0, is_unused: 0, form: constant} // CHECK:STDOUT: entity_name78000037: {name: name6, parent_scope: name_scope, index: 2, is_template: 0, is_unused: 0, form: constant} // CHECK:STDOUT: entity_name78000038: {name: name5, parent_scope: name_scope, index: 1, is_template: 0, is_unused: 0, form: constant} // CHECK:STDOUT: entity_name78000039: {name: name1, parent_scope: name_scope, index: 0, is_template: 0, is_unused: 0, form: constant} // CHECK:STDOUT: entity_name7800003A: {name: name6, parent_scope: name_scope, index: 2, is_template: 0, is_unused: 0, form: constant} // CHECK:STDOUT: entity_name7800003B: {name: name5, parent_scope: name_scope, index: 1, is_template: 0, is_unused: 0, form: constant} // CHECK:STDOUT: entity_name7800003C: {name: name1, parent_scope: name_scope, index: 0, is_template: 0, is_unused: 0, form: constant} // CHECK:STDOUT: entity_name7800003D: {name: name6, parent_scope: name_scope, index: 2, is_template: 0, is_unused: 0, form: constant} // CHECK:STDOUT: entity_name7800003E: {name: name5, parent_scope: name_scope, index: 1, is_template: 0, is_unused: 0, form: constant} // CHECK:STDOUT: entity_name7800003F: {name: name1, parent_scope: name_scope, index: 0, is_template: 0, is_unused: 0, form: constant} // CHECK:STDOUT: cpp_global_vars: {} // CHECK:STDOUT: functions: // CHECK:STDOUT: function78000000: {name: name0, parent_scope: name_scope0, call_param_patterns_id: inst_block78000011, call_params_id: inst_block78000012, return_type_inst_id: inst78000030, return_form_inst_id: inst78000032, return_patterns_id: inst_block78000010, body: [inst_block78000019]} // CHECK:STDOUT: function78000001: {name: name4, parent_scope: name_scope78000003, call_param_patterns_id: inst_block7800001F, return_type_inst_id: inst78000060, return_form_inst_id: inst78000061, return_patterns_id: inst_block78000021} // CHECK:STDOUT: function78000002: {name: name4, parent_scope: name_scope78000004, call_param_patterns_id: inst_block7800002B, return_type_inst_id: inst78000080, return_form_inst_id: inst78000081, return_patterns_id: inst_block7800002D} // CHECK:STDOUT: function78000003: {name: name4, parent_scope: name_scope78000009, call_param_patterns_id: inst_block78000042, return_type_inst_id: inst780000C8, return_form_inst_id: inst780000C9, return_patterns_id: inst_block78000044} // CHECK:STDOUT: function78000004: {name: name4, parent_scope: name_scope7800000C, call_param_patterns_id: inst_block78000050, return_type_inst_id: inst780000ED, return_form_inst_id: inst780000EE, return_patterns_id: inst_block78000052} // CHECK:STDOUT: function78000005: {name: name4, parent_scope: name_scope7800000D, call_param_patterns_id: inst_block7800006D, return_type_inst_id: inst78000131, return_form_inst_id: inst78000132, return_patterns_id: inst_block7800006F} // CHECK:STDOUT: classes: {} // CHECK:STDOUT: interfaces: // CHECK:STDOUT: interface78000000: {name: name3, parent_scope: name_scope78000001, require_impls_block_id: require_block_empty} // CHECK:STDOUT: associated_constants: {} // CHECK:STDOUT: impls: // CHECK:STDOUT: impl78000000: {self: inst7800009B, constraint: inst7800009C, witness: inst7800006F} // CHECK:STDOUT: impl78000001: {self: inst780000A6, constraint: inst780000A7, witness: inst780000A4} // CHECK:STDOUT: impl78000002: {self: inst780000AA, constraint: inst780000AB, witness: inst780000A8} // CHECK:STDOUT: impl78000003: {self: inst780000AE, constraint: inst780000AF, witness: inst780000AC} // CHECK:STDOUT: impl78000004: {self: inst780000B2, constraint: inst780000B3, witness: inst780000B0} // CHECK:STDOUT: impl78000005: {self: inst780000BA, constraint: inst780000BB, witness: inst780000B4} // CHECK:STDOUT: impl78000006: {self: inst780000D4, constraint: inst780000D5, witness: inst780000D2} // CHECK:STDOUT: impl78000007: {self: inst780000D8, constraint: inst780000D9, witness: inst780000D6} // CHECK:STDOUT: impl78000008: {self: inst78000111, constraint: inst78000112, witness: inst780000DA} // CHECK:STDOUT: impl78000009: {self: inst7800015F, constraint: inst78000160, witness: inst7800011E} // CHECK:STDOUT: generics: // CHECK:STDOUT: generic78000000: {decl: inst7800003E, bindings: inst_block78000015, self_specific_id: specific78000000, decl_block_id: inst_block78000017, definition_block_id: inst_block78000091} // CHECK:STDOUT: generic78000001: {decl: inst78000051, bindings: inst_block7800001C, self_specific_id: specific78000001, decl_block_id: inst_block_empty, definition_block_id: inst_block78000026} // CHECK:STDOUT: generic78000002: {decl: inst78000056, bindings: inst_block78000022, self_specific_id: specific78000002, decl_block_id: inst_block78000023, definition_block_id: inst_block} // CHECK:STDOUT: generic78000003: {decl: inst78000070, bindings: inst_block78000037, self_specific_id: specific78000004, decl_block_id: inst_block78000039, definition_block_id: inst_block7800003B} // CHECK:STDOUT: generic78000004: {decl: inst78000077, bindings: inst_block7800002E, self_specific_id: specific78000007, decl_block_id: inst_block7800002F, definition_block_id: inst_block78000034} // CHECK:STDOUT: generic78000005: {decl: inst780000B5, bindings: inst_block7800003F, self_specific_id: specific7800000B, decl_block_id: inst_block78000041, definition_block_id: inst_block78000049} // CHECK:STDOUT: generic78000006: {decl: inst780000C0, bindings: inst_block78000045, self_specific_id: specific7800000D, decl_block_id: inst_block78000046, definition_block_id: inst_block_empty} // CHECK:STDOUT: generic78000007: {decl: inst780000DB, bindings: inst_block78000060, self_specific_id: specific7800000E, decl_block_id: inst_block78000064, definition_block_id: inst_block78000066} // CHECK:STDOUT: generic78000008: {decl: inst780000E2, bindings: inst_block78000053, self_specific_id: specific78000011, decl_block_id: inst_block78000055, definition_block_id: inst_block7800005D} // CHECK:STDOUT: generic78000009: {decl: inst7800011F, bindings: inst_block7800007F, self_specific_id: specific78000017, decl_block_id: inst_block78000083, definition_block_id: inst_block78000085} // CHECK:STDOUT: generic7800000A: {decl: inst78000126, bindings: inst_block78000070, self_specific_id: specific7800001A, decl_block_id: inst_block78000072, definition_block_id: inst_block7800007C} // CHECK:STDOUT: specifics: // CHECK:STDOUT: specific78000000: {generic: generic78000000, args: inst_block78000016, decl_block_id: inst_block78000018, definition_block_id: inst_block} // CHECK:STDOUT: specific78000001: {generic: generic78000001, args: inst_block7800001D, decl_block_id: inst_block_empty, definition_block_id: inst_block7800001E} // CHECK:STDOUT: specific78000002: {generic: generic78000002, args: inst_block7800001D, decl_block_id: inst_block78000024, definition_block_id: inst_block} // CHECK:STDOUT: specific78000003: {generic: generic78000001, args: inst_block78000025, decl_block_id: inst_block, definition_block_id: inst_block} // CHECK:STDOUT: specific78000004: {generic: generic78000003, args: inst_block78000028, decl_block_id: inst_block78000029, definition_block_id: inst_block7800002A} // CHECK:STDOUT: specific78000005: {generic: generic78000001, args: inst_block78000028, decl_block_id: inst_block_empty, definition_block_id: inst_block78000030} // CHECK:STDOUT: specific78000006: {generic: generic78000002, args: inst_block78000028, decl_block_id: inst_block78000031, definition_block_id: inst_block} // CHECK:STDOUT: specific78000007: {generic: generic78000004, args: inst_block78000028, decl_block_id: inst_block78000035, definition_block_id: inst_block} // CHECK:STDOUT: specific78000008: {generic: generic78000001, args: inst_block78000032, decl_block_id: inst_block, definition_block_id: inst_block} // CHECK:STDOUT: specific78000009: {generic: generic78000002, args: inst_block78000032, decl_block_id: inst_block, definition_block_id: inst_block} // CHECK:STDOUT: specific7800000A: {generic: generic78000003, args: inst_block78000038, decl_block_id: inst_block, definition_block_id: inst_block} // CHECK:STDOUT: specific7800000B: {generic: generic78000005, args: inst_block78000016, decl_block_id: inst_block7800003D, definition_block_id: inst_block78000086} // CHECK:STDOUT: specific7800000C: {generic: generic78000005, args: inst_block78000040, decl_block_id: inst_block, definition_block_id: inst_block} // CHECK:STDOUT: specific7800000D: {generic: generic78000006, args: inst_block78000016, decl_block_id: inst_block78000047, definition_block_id: inst_block} // CHECK:STDOUT: specific7800000E: {generic: generic78000007, args: inst_block7800004C, decl_block_id: inst_block7800004E, definition_block_id: inst_block7800004F} // CHECK:STDOUT: specific7800000F: {generic: generic78000001, args: inst_block78000056, decl_block_id: inst_block_empty, definition_block_id: inst_block78000057} // CHECK:STDOUT: specific78000010: {generic: generic78000002, args: inst_block78000056, decl_block_id: inst_block78000058, definition_block_id: inst_block} // CHECK:STDOUT: specific78000011: {generic: generic78000008, args: inst_block7800004C, decl_block_id: inst_block7800005E, definition_block_id: inst_block} // CHECK:STDOUT: specific78000012: {generic: generic78000001, args: inst_block78000059, decl_block_id: inst_block, definition_block_id: inst_block} // CHECK:STDOUT: specific78000013: {generic: generic78000002, args: inst_block78000059, decl_block_id: inst_block, definition_block_id: inst_block} // CHECK:STDOUT: specific78000014: {generic: generic78000001, args: inst_block7800005B, decl_block_id: inst_block, definition_block_id: inst_block} // CHECK:STDOUT: specific78000015: {generic: generic78000002, args: inst_block7800005B, decl_block_id: inst_block, definition_block_id: inst_block} // CHECK:STDOUT: specific78000016: {generic: generic78000007, args: inst_block78000061, decl_block_id: inst_block, definition_block_id: inst_block} // CHECK:STDOUT: specific78000017: {generic: generic78000009, args: inst_block78000069, decl_block_id: inst_block7800006B, definition_block_id: inst_block7800006C} // CHECK:STDOUT: specific78000018: {generic: generic78000001, args: inst_block78000073, decl_block_id: inst_block_empty, definition_block_id: inst_block78000074} // CHECK:STDOUT: specific78000019: {generic: generic78000002, args: inst_block78000073, decl_block_id: inst_block78000075, definition_block_id: inst_block} // CHECK:STDOUT: specific7800001A: {generic: generic7800000A, args: inst_block78000069, decl_block_id: inst_block7800007D, definition_block_id: inst_block} // CHECK:STDOUT: specific7800001B: {generic: generic78000001, args: inst_block78000076, decl_block_id: inst_block, definition_block_id: inst_block} // CHECK:STDOUT: specific7800001C: {generic: generic78000002, args: inst_block78000076, decl_block_id: inst_block, definition_block_id: inst_block} // CHECK:STDOUT: specific7800001D: {generic: generic78000001, args: inst_block78000078, decl_block_id: inst_block, definition_block_id: inst_block} // CHECK:STDOUT: specific7800001E: {generic: generic78000002, args: inst_block78000078, decl_block_id: inst_block, definition_block_id: inst_block} // CHECK:STDOUT: specific7800001F: {generic: generic78000001, args: inst_block7800007A, decl_block_id: inst_block, definition_block_id: inst_block} // CHECK:STDOUT: specific78000020: {generic: generic78000002, args: inst_block7800007A, decl_block_id: inst_block, definition_block_id: inst_block} // CHECK:STDOUT: specific78000021: {generic: generic78000009, args: inst_block78000080, decl_block_id: inst_block, definition_block_id: inst_block} // CHECK:STDOUT: specific78000022: {generic: generic78000005, args: inst_block78000087, decl_block_id: inst_block, definition_block_id: inst_block} // CHECK:STDOUT: specific78000023: {generic: generic78000001, args: inst_block78000089, decl_block_id: inst_block_empty, definition_block_id: inst_block7800008A} // CHECK:STDOUT: specific78000024: {generic: generic78000001, args: inst_block7800008C, decl_block_id: inst_block, definition_block_id: inst_block} // CHECK:STDOUT: specific78000025: {generic: generic78000002, args: inst_block78000089, decl_block_id: inst_block7800008D, definition_block_id: inst_block} // CHECK:STDOUT: specific78000026: {generic: generic78000002, args: inst_block7800008C, decl_block_id: inst_block, definition_block_id: inst_block} // CHECK:STDOUT: specific_interfaces: // CHECK:STDOUT: specific_interface78000000: {interface_id: interface78000000, specific_id: specific} // CHECK:STDOUT: struct_type_fields: // CHECK:STDOUT: struct_type_fields_empty: {} // CHECK:STDOUT: types: // CHECK:STDOUT: 'type(TypeType)': // CHECK:STDOUT: value_repr: {kind: copy, type: type(TypeType)} // CHECK:STDOUT: 'type(inst(FormType))': // CHECK:STDOUT: value_repr: {kind: copy, type: type(inst(FormType))} // CHECK:STDOUT: 'type(Error)': // CHECK:STDOUT: value_repr: {kind: copy, type: type(Error)} // CHECK:STDOUT: 'type(inst(NamespaceType))': // CHECK:STDOUT: value_repr: {kind: copy, type: type(inst(NamespaceType))} // CHECK:STDOUT: 'type(inst78000026)': // CHECK:STDOUT: value_repr: {kind: none, type: type(inst78000026)} // CHECK:STDOUT: 'type(inst7800002D)': // CHECK:STDOUT: value_repr: {kind: copy, type: type(inst7800002D)} // CHECK:STDOUT: 'type(inst78000029)': // CHECK:STDOUT: value_repr: {kind: pointer, type: type(inst7800002D)} // CHECK:STDOUT: 'type(inst7800003F)': // CHECK:STDOUT: value_repr: {kind: none, type: type(inst78000026)} // CHECK:STDOUT: 'type(symbolic_constant78000003)': // CHECK:STDOUT: value_repr: {kind: copy, type: type(symbolic_constant78000003)} // CHECK:STDOUT: 'type(inst(WitnessType))': // CHECK:STDOUT: value_repr: {kind: copy, type: type(inst(WitnessType))} // CHECK:STDOUT: 'type(symbolic_constant78000011)': // CHECK:STDOUT: value_repr: {kind: copy, type: type(symbolic_constant78000011)} // CHECK:STDOUT: 'type(symbolic_constant78000009)': // CHECK:STDOUT: value_repr: {kind: pointer, type: type(symbolic_constant78000011)} // CHECK:STDOUT: 'type(symbolic_constant78000004)': // CHECK:STDOUT: value_repr: {kind: copy, type: type(symbolic_constant78000004)} // CHECK:STDOUT: 'type(symbolic_constant7800000A)': // CHECK:STDOUT: value_repr: {kind: pointer, type: type(symbolic_constant78000011)} // CHECK:STDOUT: 'type(inst(InstType))': // CHECK:STDOUT: value_repr: {kind: none, type: type(inst78000026)} // CHECK:STDOUT: 'type(inst78000050)': // CHECK:STDOUT: value_repr: {kind: copy, type: type(inst78000050)} // CHECK:STDOUT: 'type(inst(SpecificFunctionType))': // CHECK:STDOUT: value_repr: {kind: copy, type: type(inst(SpecificFunctionType))} // CHECK:STDOUT: 'type(inst(RequireSpecificDefinitionType))': // CHECK:STDOUT: value_repr: {kind: copy, type: type(inst(RequireSpecificDefinitionType))} // CHECK:STDOUT: 'type(symbolic_constant78000146)': // CHECK:STDOUT: value_repr: {kind: none, type: type(inst78000026)} // CHECK:STDOUT: 'type(symbolic_constant7800014A)': // CHECK:STDOUT: value_repr: {kind: none, type: type(inst78000026)} // CHECK:STDOUT: 'type(inst(BoundMethodType))': // CHECK:STDOUT: value_repr: {kind: copy, type: type(inst(BoundMethodType))} // CHECK:STDOUT: facet_types: // CHECK:STDOUT: facet_type78000000: {} // CHECK:STDOUT: facet_type78000001: {extends interface: interface78000000} // CHECK:STDOUT: insts: // CHECK:STDOUT: instF: {kind: Namespace, arg0: name_scope0, arg1: inst, type: type(inst(NamespaceType))} // CHECK:STDOUT: inst78000010: {kind: ImportDecl, arg0: name(Core)} // CHECK:STDOUT: inst78000011: {kind: Namespace, arg0: name_scope78000001, arg1: inst78000010, type: type(inst(NamespaceType))} // CHECK:STDOUT: inst78000012: {kind: FacetType, arg0: facet_type78000000, type: type(TypeType)} // CHECK:STDOUT: inst78000013: {kind: SymbolicBinding, arg0: entity_name78000000, arg1: inst, type: type(inst78000012)} // CHECK:STDOUT: inst78000014: {kind: SymbolicBinding, arg0: entity_name78000000, arg1: inst, type: type(inst78000012)} // CHECK:STDOUT: inst78000015: {kind: TypeLiteral, arg0: inst(TypeType), type: type(TypeType)} // CHECK:STDOUT: inst78000016: {kind: PatternType, arg0: inst(TypeType), type: type(TypeType)} // CHECK:STDOUT: inst78000017: {kind: SymbolicBinding, arg0: entity_name78000001, arg1: inst, type: type(TypeType)} // CHECK:STDOUT: inst78000018: {kind: SymbolicBinding, arg0: entity_name78000001, arg1: inst, type: type(TypeType)} // CHECK:STDOUT: inst78000019: {kind: SymbolicBinding, arg0: entity_name78000001, arg1: inst, type: type(TypeType)} // CHECK:STDOUT: inst7800001A: {kind: SymbolicBindingPattern, arg0: entity_name78000001, type: type(inst78000016)} // CHECK:STDOUT: inst7800001B: {kind: NameRef, arg0: name1, arg1: inst78000017, type: type(TypeType)} // CHECK:STDOUT: inst7800001C: {kind: PointerType, arg0: inst7800001B, type: type(TypeType)} // CHECK:STDOUT: inst7800001D: {kind: PointerType, arg0: inst78000018, type: type(TypeType)} // CHECK:STDOUT: inst7800001E: {kind: PointerType, arg0: inst78000019, type: type(TypeType)} // CHECK:STDOUT: inst7800001F: {kind: PatternType, arg0: inst7800001D, type: type(TypeType)} // CHECK:STDOUT: inst78000020: {kind: ValueBinding, arg0: entity_name78000002, arg1: inst7800003A, type: type(symbolic_constant78000004)} // CHECK:STDOUT: inst78000021: {kind: ValueBindingPattern, arg0: entity_name78000002, type: type(symbolic_constant78000006)} // CHECK:STDOUT: inst78000022: {kind: PatternType, arg0: inst7800001E, type: type(TypeType)} // CHECK:STDOUT: inst78000023: {kind: ValueParamPattern, arg0: inst78000021, type: type(symbolic_constant78000006)} // CHECK:STDOUT: inst78000024: {kind: NameRef, arg0: name1, arg1: inst78000017, type: type(TypeType)} // CHECK:STDOUT: inst78000025: {kind: PointerType, arg0: inst78000024, type: type(TypeType)} // CHECK:STDOUT: inst78000026: {kind: TupleType, arg0: inst_block_empty, type: type(TypeType)} // CHECK:STDOUT: inst78000027: {kind: TupleLiteral, arg0: inst_block_empty, type: type(inst78000026)} // CHECK:STDOUT: inst78000028: {kind: TupleValue, arg0: inst_block_empty, type: type(inst78000026)} // CHECK:STDOUT: inst78000029: {kind: TupleType, arg0: inst_block7800000A, type: type(TypeType)} // CHECK:STDOUT: inst7800002A: {kind: TupleLiteral, arg0: inst_block78000009, type: type(inst78000029)} // CHECK:STDOUT: inst7800002B: {kind: TupleValue, arg0: inst_block7800000B, type: type(inst78000029)} // CHECK:STDOUT: inst7800002C: {kind: TupleValue, arg0: inst_block7800000C, type: type(inst78000029)} // CHECK:STDOUT: inst7800002D: {kind: PointerType, arg0: inst78000029, type: type(TypeType)} // CHECK:STDOUT: inst7800002E: {kind: Converted, arg0: inst78000028, arg1: inst78000026, type: type(TypeType)} // CHECK:STDOUT: inst7800002F: {kind: TupleType, arg0: inst_block7800000E, type: type(TypeType)} // CHECK:STDOUT: inst78000030: {kind: Converted, arg0: inst7800002A, arg1: inst7800002F, type: type(TypeType)} // CHECK:STDOUT: inst78000031: {kind: TupleType, arg0: inst_block7800000F, type: type(TypeType)} // CHECK:STDOUT: inst78000032: {kind: InitForm, arg0: inst78000030, type: type(inst(FormType))} // CHECK:STDOUT: inst78000033: {kind: InitForm, arg0: inst7800002F, type: type(inst(FormType))} // CHECK:STDOUT: inst78000034: {kind: InitForm, arg0: inst78000031, type: type(inst(FormType))} // CHECK:STDOUT: inst78000035: {kind: PatternType, arg0: inst7800002F, type: type(TypeType)} // CHECK:STDOUT: inst78000036: {kind: ReturnSlotPattern, arg0: inst78000030, type: type(symbolic_constant7800000E)} // CHECK:STDOUT: inst78000037: {kind: PatternType, arg0: inst78000031, type: type(TypeType)} // CHECK:STDOUT: inst78000038: {kind: OutParamPattern, arg0: inst78000036, type: type(symbolic_constant7800000E)} // CHECK:STDOUT: inst78000039: {kind: SpliceBlock, arg0: inst_block78000005, arg1: inst78000015, type: type(TypeType)} // CHECK:STDOUT: inst7800003A: {kind: ValueParam, arg0: call_param0, arg1: name2, type: type(symbolic_constant78000004)} // CHECK:STDOUT: inst7800003B: {kind: SpliceBlock, arg0: inst_block78000007, arg1: inst7800001C, type: type(TypeType)} // CHECK:STDOUT: inst7800003C: {kind: OutParam, arg0: call_param1, arg1: name(ReturnSlot), type: type(symbolic_constant7800000A)} // CHECK:STDOUT: inst7800003D: {kind: ReturnSlot, arg0: inst7800002F, arg1: inst7800003C, type: type(symbolic_constant7800000A)} // CHECK:STDOUT: inst7800003E: {kind: FunctionDecl, arg0: function78000000, arg1: inst_block78000014, type: type(inst7800003F)} // CHECK:STDOUT: inst7800003F: {kind: FunctionType, arg0: function78000000, arg1: specific, type: type(TypeType)} // CHECK:STDOUT: inst78000040: {kind: StructValue, arg0: inst_block_empty, type: type(inst7800003F)} // CHECK:STDOUT: inst78000041: {kind: RequireCompleteType, arg0: inst7800001D, type: type(inst(WitnessType))} // CHECK:STDOUT: inst78000042: {kind: RequireCompleteType, arg0: inst7800001D, type: type(inst(WitnessType))} // CHECK:STDOUT: inst78000043: {kind: RequireCompleteType, arg0: inst7800001E, type: type(inst(WitnessType))} // CHECK:STDOUT: inst78000044: {kind: PointerType, arg0: inst7800002F, type: type(TypeType)} // CHECK:STDOUT: inst78000045: {kind: RequireCompleteType, arg0: inst7800002F, type: type(inst(WitnessType))} // CHECK:STDOUT: inst78000046: {kind: RequireCompleteType, arg0: inst7800002F, type: type(inst(WitnessType))} // CHECK:STDOUT: inst78000047: {kind: RequireCompleteType, arg0: inst78000031, type: type(inst(WitnessType))} // CHECK:STDOUT: inst78000048: {kind: NameRef, arg0: name2, arg1: inst78000020, type: type(symbolic_constant78000004)} // CHECK:STDOUT: inst78000049: {kind: TupleLiteral, arg0: inst_block_empty, type: type(inst78000026)} // CHECK:STDOUT: inst7800004A: {kind: TupleLiteral, arg0: inst_block7800001A, type: type(symbolic_constant7800000A)} // CHECK:STDOUT: inst7800004B: {kind: RequireCompleteType, arg0: inst7800002F, type: type(inst(WitnessType))} // CHECK:STDOUT: inst7800004C: {kind: TupleAccess, arg0: inst7800003C, arg1: element0, type: type(symbolic_constant78000004)} // CHECK:STDOUT: inst7800004D: {kind: RequireCompleteType, arg0: inst7800001D, type: type(inst(WitnessType))} // CHECK:STDOUT: inst7800004E: {kind: ImportRefLoaded, arg0: import_ir_inst0, arg1: entity_name78000003, type: type(TypeType)} // CHECK:STDOUT: inst7800004F: {kind: InterfaceDecl, arg0: interface78000000, arg1: inst_block_empty, type: type(TypeType)} // CHECK:STDOUT: inst78000050: {kind: FacetType, arg0: facet_type78000001, type: type(TypeType)} // CHECK:STDOUT: inst78000051: {kind: InterfaceWithSelfDecl, arg0: interface78000000} // CHECK:STDOUT: inst78000052: {kind: SymbolicBinding, arg0: entity_name78000004, arg1: inst, type: type(inst78000050)} // CHECK:STDOUT: inst78000053: {kind: ImportRefLoaded, arg0: import_ir_inst3, arg1: entity_name, type: type(inst7800006B)} // CHECK:STDOUT: inst78000054: {kind: ImportRefUnloaded, arg0: import_ir_inst4, arg1: entity_name78000005} // CHECK:STDOUT: inst78000055: {kind: ImportRefLoaded, arg0: import_ir_inst5, arg1: entity_name, type: type(inst78000050)} // CHECK:STDOUT: inst78000056: {kind: FunctionDecl, arg0: function78000001, arg1: inst_block_empty, type: type(symbolic_constant78000015)} // CHECK:STDOUT: inst78000057: {kind: FunctionType, arg0: function78000001, arg1: specific78000001, type: type(TypeType)} // CHECK:STDOUT: inst78000058: {kind: StructValue, arg0: inst_block_empty, type: type(symbolic_constant78000015)} // CHECK:STDOUT: inst78000059: {kind: SymbolicBindingType, arg0: entity_name78000004, arg1: inst78000052, type: type(TypeType)} // CHECK:STDOUT: inst7800005A: {kind: PatternType, arg0: inst78000059, type: type(TypeType)} // CHECK:STDOUT: inst7800005B: {kind: ReturnSlotPattern, arg0: inst, type: type(symbolic_constant7800001B)} // CHECK:STDOUT: inst7800005C: {kind: OutParamPattern, arg0: inst7800005B, type: type(symbolic_constant7800001B)} // CHECK:STDOUT: inst7800005D: {kind: ValueBindingPattern, arg0: entity_name78000008, type: type(symbolic_constant7800001B)} // CHECK:STDOUT: inst7800005E: {kind: ValueParamPattern, arg0: inst7800005D, type: type(symbolic_constant7800001B)} // CHECK:STDOUT: inst7800005F: {kind: InitForm, arg0: inst78000059, type: type(inst(FormType))} // CHECK:STDOUT: inst78000060: {kind: ImportRefLoaded, arg0: import_ir_instB, arg1: entity_name, type: type(TypeType)} // CHECK:STDOUT: inst78000061: {kind: ImportRefLoaded, arg0: import_ir_instC, arg1: entity_name, type: type(inst(FormType))} // CHECK:STDOUT: inst78000062: {kind: ImportRefLoaded, arg0: import_ir_instD, arg1: entity_name, type: type(inst78000050)} // CHECK:STDOUT: inst78000063: {kind: SymbolicBinding, arg0: entity_name78000004, arg1: inst, type: type(inst78000050)} // CHECK:STDOUT: inst78000064: {kind: SymbolicBindingType, arg0: entity_name78000004, arg1: inst78000063, type: type(TypeType)} // CHECK:STDOUT: inst78000065: {kind: PatternType, arg0: inst78000064, type: type(TypeType)} // CHECK:STDOUT: inst78000066: {kind: InitForm, arg0: inst78000064, type: type(inst(FormType))} // CHECK:STDOUT: inst78000067: {kind: SymbolicBinding, arg0: entity_name78000004, arg1: inst, type: type(inst78000050)} // CHECK:STDOUT: inst78000068: {kind: FunctionType, arg0: function78000001, arg1: specific78000003, type: type(TypeType)} // CHECK:STDOUT: inst78000069: {kind: StructValue, arg0: inst_block_empty, type: type(symbolic_constant78000029)} // CHECK:STDOUT: inst7800006A: {kind: ImportRefUnloaded, arg0: import_ir_inst15, arg1: entity_name} // CHECK:STDOUT: inst7800006B: {kind: AssociatedEntityType, arg0: interface78000000, arg1: specific, type: type(TypeType)} // CHECK:STDOUT: inst7800006C: {kind: ImportRefLoaded, arg0: import_ir_inst16, arg1: entity_name, type: type(symbolic_constant78000019)} // CHECK:STDOUT: inst7800006D: {kind: AssociatedEntity, arg0: element0, arg1: inst7800006C, type: type(inst7800006B)} // CHECK:STDOUT: inst7800006E: {kind: LookupImplWitness, arg0: inst7800001D, arg1: specific_interface78000000, type: type(inst(WitnessType))} // CHECK:STDOUT: inst7800006F: {kind: ImportRefUnloaded, arg0: import_ir_inst17, arg1: entity_name} // CHECK:STDOUT: inst78000070: {kind: ImplDecl, arg0: impl78000000, arg1: inst_block_empty} // CHECK:STDOUT: inst78000071: {kind: SymbolicBinding, arg0: entity_name78000001, arg1: inst, type: type(inst78000050)} // CHECK:STDOUT: inst78000072: {kind: SymbolicBindingType, arg0: entity_name78000001, arg1: inst78000071, type: type(TypeType)} // CHECK:STDOUT: inst78000073: {kind: ConstType, arg0: inst78000072, type: type(TypeType)} // CHECK:STDOUT: inst78000074: {kind: ImportRefUnloaded, arg0: import_ir_inst19, arg1: entity_name} // CHECK:STDOUT: inst78000075: {kind: ImplWitnessTable, arg0: inst_block78000027, arg1: impl78000000} // CHECK:STDOUT: inst78000076: {kind: ImplWitness, arg0: inst78000075, arg1: specific78000004, type: type(inst(WitnessType))} // CHECK:STDOUT: inst78000077: {kind: FunctionDecl, arg0: function78000002, arg1: inst_block_empty, type: type(symbolic_constant78000031)} // CHECK:STDOUT: inst78000078: {kind: FunctionType, arg0: function78000002, arg1: specific78000004, type: type(TypeType)} // CHECK:STDOUT: inst78000079: {kind: StructValue, arg0: inst_block_empty, type: type(symbolic_constant78000031)} // CHECK:STDOUT: inst7800007A: {kind: PatternType, arg0: inst78000073, type: type(TypeType)} // CHECK:STDOUT: inst7800007B: {kind: ReturnSlotPattern, arg0: inst, type: type(symbolic_constant78000036)} // CHECK:STDOUT: inst7800007C: {kind: OutParamPattern, arg0: inst7800007B, type: type(symbolic_constant78000036)} // CHECK:STDOUT: inst7800007D: {kind: ValueBindingPattern, arg0: entity_name7800000F, type: type(symbolic_constant78000036)} // CHECK:STDOUT: inst7800007E: {kind: ValueParamPattern, arg0: inst7800007D, type: type(symbolic_constant78000036)} // CHECK:STDOUT: inst7800007F: {kind: InitForm, arg0: inst78000073, type: type(inst(FormType))} // CHECK:STDOUT: inst78000080: {kind: ImportRefLoaded, arg0: import_ir_inst20, arg1: entity_name, type: type(TypeType)} // CHECK:STDOUT: inst78000081: {kind: ImportRefLoaded, arg0: import_ir_inst21, arg1: entity_name, type: type(inst(FormType))} // CHECK:STDOUT: inst78000082: {kind: ImportRefLoaded, arg0: import_ir_inst22, arg1: entity_name, type: type(inst78000050)} // CHECK:STDOUT: inst78000083: {kind: SymbolicBinding, arg0: entity_name78000001, arg1: inst, type: type(inst78000050)} // CHECK:STDOUT: inst78000084: {kind: SymbolicBindingType, arg0: entity_name78000001, arg1: inst78000083, type: type(TypeType)} // CHECK:STDOUT: inst78000085: {kind: ConstType, arg0: inst78000084, type: type(TypeType)} // CHECK:STDOUT: inst78000086: {kind: PatternType, arg0: inst78000085, type: type(TypeType)} // CHECK:STDOUT: inst78000087: {kind: InitForm, arg0: inst78000085, type: type(inst(FormType))} // CHECK:STDOUT: inst78000088: {kind: LookupImplWitness, arg0: inst78000071, arg1: specific_interface78000000, type: type(inst(WitnessType))} // CHECK:STDOUT: inst78000089: {kind: FunctionType, arg0: function78000001, arg1: specific78000005, type: type(TypeType)} // CHECK:STDOUT: inst7800008A: {kind: StructValue, arg0: inst_block_empty, type: type(symbolic_constant78000045)} // CHECK:STDOUT: inst7800008B: {kind: FunctionTypeWithSelfType, arg0: inst78000089, arg1: inst78000071, type: type(TypeType)} // CHECK:STDOUT: inst7800008C: {kind: ImplWitnessAccess, arg0: inst78000088, arg1: element0, type: type(symbolic_constant78000047)} // CHECK:STDOUT: inst7800008D: {kind: SpecificImplFunction, arg0: inst7800008C, arg1: specific78000006, type: type(inst(SpecificFunctionType))} // CHECK:STDOUT: inst7800008E: {kind: InitForm, arg0: inst78000072, type: type(inst(FormType))} // CHECK:STDOUT: inst7800008F: {kind: PatternType, arg0: inst78000072, type: type(TypeType)} // CHECK:STDOUT: inst78000090: {kind: RequireCompleteType, arg0: inst78000072, type: type(inst(WitnessType))} // CHECK:STDOUT: inst78000091: {kind: RequireCompleteType, arg0: inst78000073, type: type(inst(WitnessType))} // CHECK:STDOUT: inst78000092: {kind: RequireCompleteType, arg0: inst78000085, type: type(inst(WitnessType))} // CHECK:STDOUT: inst78000093: {kind: RequireCompleteType, arg0: inst78000084, type: type(inst(WitnessType))} // CHECK:STDOUT: inst78000094: {kind: LookupImplWitness, arg0: inst78000083, arg1: specific_interface78000000, type: type(inst(WitnessType))} // CHECK:STDOUT: inst78000095: {kind: FunctionType, arg0: function78000001, arg1: specific78000008, type: type(TypeType)} // CHECK:STDOUT: inst78000096: {kind: FunctionTypeWithSelfType, arg0: inst78000095, arg1: inst78000083, type: type(TypeType)} // CHECK:STDOUT: inst78000097: {kind: ImplWitnessAccess, arg0: inst78000094, arg1: element0, type: type(symbolic_constant78000059)} // CHECK:STDOUT: inst78000098: {kind: SpecificImplFunction, arg0: inst78000097, arg1: specific78000009, type: type(inst(SpecificFunctionType))} // CHECK:STDOUT: inst78000099: {kind: PatternType, arg0: inst78000050, type: type(TypeType)} // CHECK:STDOUT: inst7800009A: {kind: SymbolicBindingPattern, arg0: entity_name78000015, type: type(inst78000099)} // CHECK:STDOUT: inst7800009B: {kind: ImportRefLoaded, arg0: import_ir_inst30, arg1: entity_name, type: type(TypeType)} // CHECK:STDOUT: inst7800009C: {kind: ImportRefLoaded, arg0: import_ir_inst31, arg1: entity_name, type: type(TypeType)} // CHECK:STDOUT: inst7800009D: {kind: ImportRefLoaded, arg0: import_ir_inst32, arg1: entity_name, type: type(inst78000050)} // CHECK:STDOUT: inst7800009E: {kind: SymbolicBinding, arg0: entity_name78000001, arg1: inst, type: type(inst78000050)} // CHECK:STDOUT: inst7800009F: {kind: SymbolicBindingType, arg0: entity_name78000001, arg1: inst7800009E, type: type(TypeType)} // CHECK:STDOUT: inst780000A0: {kind: ConstType, arg0: inst7800009F, type: type(TypeType)} // CHECK:STDOUT: inst780000A1: {kind: ImplWitness, arg0: inst78000075, arg1: specific7800000A, type: type(inst(WitnessType))} // CHECK:STDOUT: inst780000A2: {kind: FunctionType, arg0: function78000002, arg1: specific7800000A, type: type(TypeType)} // CHECK:STDOUT: inst780000A3: {kind: StructValue, arg0: inst_block_empty, type: type(symbolic_constant78000064)} // CHECK:STDOUT: inst780000A4: {kind: ImportRefUnloaded, arg0: import_ir_inst39, arg1: entity_name} // CHECK:STDOUT: inst780000A5: {kind: ImplDecl, arg0: impl78000001, arg1: inst_block_empty} // CHECK:STDOUT: inst780000A6: {kind: ImportRefLoaded, arg0: import_ir_inst3B, arg1: entity_name, type: type(TypeType)} // CHECK:STDOUT: inst780000A7: {kind: ImportRefLoaded, arg0: import_ir_inst3C, arg1: entity_name, type: type(TypeType)} // CHECK:STDOUT: inst780000A8: {kind: ImportRefUnloaded, arg0: import_ir_inst3D, arg1: entity_name} // CHECK:STDOUT: inst780000A9: {kind: ImplDecl, arg0: impl78000002, arg1: inst_block_empty} // CHECK:STDOUT: inst780000AA: {kind: ImportRefLoaded, arg0: import_ir_inst3F, arg1: entity_name, type: type(TypeType)} // CHECK:STDOUT: inst780000AB: {kind: ImportRefLoaded, arg0: import_ir_inst40, arg1: entity_name, type: type(TypeType)} // CHECK:STDOUT: inst780000AC: {kind: ImportRefUnloaded, arg0: import_ir_inst41, arg1: entity_name} // CHECK:STDOUT: inst780000AD: {kind: ImplDecl, arg0: impl78000003, arg1: inst_block_empty} // CHECK:STDOUT: inst780000AE: {kind: ImportRefLoaded, arg0: import_ir_inst43, arg1: entity_name, type: type(TypeType)} // CHECK:STDOUT: inst780000AF: {kind: ImportRefLoaded, arg0: import_ir_inst44, arg1: entity_name, type: type(TypeType)} // CHECK:STDOUT: inst780000B0: {kind: ImportRefUnloaded, arg0: import_ir_inst45, arg1: entity_name} // CHECK:STDOUT: inst780000B1: {kind: ImplDecl, arg0: impl78000004, arg1: inst_block_empty} // CHECK:STDOUT: inst780000B2: {kind: ImportRefLoaded, arg0: import_ir_inst47, arg1: entity_name, type: type(TypeType)} // CHECK:STDOUT: inst780000B3: {kind: ImportRefLoaded, arg0: import_ir_inst48, arg1: entity_name, type: type(TypeType)} // CHECK:STDOUT: inst780000B4: {kind: ImportRefLoaded, arg0: import_ir_inst49, arg1: entity_name, type: type(inst(WitnessType))} // CHECK:STDOUT: inst780000B5: {kind: ImplDecl, arg0: impl78000005, arg1: inst_block_empty} // CHECK:STDOUT: inst780000B6: {kind: ImportRefUnloaded, arg0: import_ir_inst4B, arg1: entity_name} // CHECK:STDOUT: inst780000B7: {kind: ImplWitnessTable, arg0: inst_block7800003C, arg1: impl78000005} // CHECK:STDOUT: inst780000B8: {kind: ImplWitness, arg0: inst780000B7, arg1: specific7800000B, type: type(inst(WitnessType))} // CHECK:STDOUT: inst780000B9: {kind: SymbolicBindingPattern, arg0: entity_name78000019, type: type(inst78000016)} // CHECK:STDOUT: inst780000BA: {kind: ImportRefLoaded, arg0: import_ir_inst4E, arg1: entity_name, type: type(TypeType)} // CHECK:STDOUT: inst780000BB: {kind: ImportRefLoaded, arg0: import_ir_inst4F, arg1: entity_name, type: type(TypeType)} // CHECK:STDOUT: inst780000BC: {kind: ImportRefLoaded, arg0: import_ir_inst50, arg1: entity_name, type: type(TypeType)} // CHECK:STDOUT: inst780000BD: {kind: SymbolicBinding, arg0: entity_name78000001, arg1: inst, type: type(TypeType)} // CHECK:STDOUT: inst780000BE: {kind: PointerType, arg0: inst780000BD, type: type(TypeType)} // CHECK:STDOUT: inst780000BF: {kind: ImplWitness, arg0: inst780000B7, arg1: specific7800000C, type: type(inst(WitnessType))} // CHECK:STDOUT: inst780000C0: {kind: FunctionDecl, arg0: function78000003, arg1: inst_block_empty, type: type(symbolic_constant78000070)} // CHECK:STDOUT: inst780000C1: {kind: FunctionType, arg0: function78000003, arg1: specific7800000B, type: type(TypeType)} // CHECK:STDOUT: inst780000C2: {kind: StructValue, arg0: inst_block_empty, type: type(symbolic_constant78000070)} // CHECK:STDOUT: inst780000C3: {kind: ReturnSlotPattern, arg0: inst, type: type(symbolic_constant78000074)} // CHECK:STDOUT: inst780000C4: {kind: OutParamPattern, arg0: inst780000C3, type: type(symbolic_constant78000074)} // CHECK:STDOUT: inst780000C5: {kind: ValueBindingPattern, arg0: entity_name7800001A, type: type(symbolic_constant78000074)} // CHECK:STDOUT: inst780000C6: {kind: ValueParamPattern, arg0: inst780000C5, type: type(symbolic_constant78000074)} // CHECK:STDOUT: inst780000C7: {kind: InitForm, arg0: inst7800001D, type: type(inst(FormType))} // CHECK:STDOUT: inst780000C8: {kind: ImportRefLoaded, arg0: import_ir_inst59, arg1: entity_name, type: type(TypeType)} // CHECK:STDOUT: inst780000C9: {kind: ImportRefLoaded, arg0: import_ir_inst5A, arg1: entity_name, type: type(inst(FormType))} // CHECK:STDOUT: inst780000CA: {kind: ImportRefLoaded, arg0: import_ir_inst5B, arg1: entity_name, type: type(TypeType)} // CHECK:STDOUT: inst780000CB: {kind: SymbolicBinding, arg0: entity_name78000001, arg1: inst, type: type(TypeType)} // CHECK:STDOUT: inst780000CC: {kind: PointerType, arg0: inst780000CB, type: type(TypeType)} // CHECK:STDOUT: inst780000CD: {kind: PatternType, arg0: inst780000CC, type: type(TypeType)} // CHECK:STDOUT: inst780000CE: {kind: InitForm, arg0: inst780000CC, type: type(inst(FormType))} // CHECK:STDOUT: inst780000CF: {kind: FunctionType, arg0: function78000003, arg1: specific7800000C, type: type(TypeType)} // CHECK:STDOUT: inst780000D0: {kind: StructValue, arg0: inst_block_empty, type: type(symbolic_constant78000080)} // CHECK:STDOUT: inst780000D1: {kind: RequireCompleteType, arg0: inst780000BE, type: type(inst(WitnessType))} // CHECK:STDOUT: inst780000D2: {kind: ImportRefUnloaded, arg0: import_ir_inst63, arg1: entity_name} // CHECK:STDOUT: inst780000D3: {kind: ImplDecl, arg0: impl78000006, arg1: inst_block_empty} // CHECK:STDOUT: inst780000D4: {kind: ImportRefLoaded, arg0: import_ir_inst65, arg1: entity_name, type: type(TypeType)} // CHECK:STDOUT: inst780000D5: {kind: ImportRefLoaded, arg0: import_ir_inst66, arg1: entity_name, type: type(TypeType)} // CHECK:STDOUT: inst780000D6: {kind: ImportRefUnloaded, arg0: import_ir_inst67, arg1: entity_name} // CHECK:STDOUT: inst780000D7: {kind: ImplDecl, arg0: impl78000007, arg1: inst_block_empty} // CHECK:STDOUT: inst780000D8: {kind: ImportRefLoaded, arg0: import_ir_inst69, arg1: entity_name, type: type(TypeType)} // CHECK:STDOUT: inst780000D9: {kind: ImportRefLoaded, arg0: import_ir_inst6A, arg1: entity_name, type: type(TypeType)} // CHECK:STDOUT: inst780000DA: {kind: ImportRefUnloaded, arg0: import_ir_inst6B, arg1: entity_name} // CHECK:STDOUT: inst780000DB: {kind: ImplDecl, arg0: impl78000008, arg1: inst_block_empty} // CHECK:STDOUT: inst780000DC: {kind: SymbolicBinding, arg0: entity_name7800001C, arg1: inst, type: type(inst78000050)} // CHECK:STDOUT: inst780000DD: {kind: SymbolicBindingType, arg0: entity_name7800001C, arg1: inst780000DC, type: type(TypeType)} // CHECK:STDOUT: inst780000DE: {kind: TupleType, arg0: inst_block7800004A, type: type(TypeType)} // CHECK:STDOUT: inst780000DF: {kind: ImportRefUnloaded, arg0: import_ir_inst6D, arg1: entity_name} // CHECK:STDOUT: inst780000E0: {kind: ImplWitnessTable, arg0: inst_block7800004B, arg1: impl78000008} // CHECK:STDOUT: inst780000E1: {kind: ImplWitness, arg0: inst780000E0, arg1: specific7800000E, type: type(inst(WitnessType))} // CHECK:STDOUT: inst780000E2: {kind: FunctionDecl, arg0: function78000004, arg1: inst_block_empty, type: type(symbolic_constant78000089)} // CHECK:STDOUT: inst780000E3: {kind: FunctionType, arg0: function78000004, arg1: specific7800000E, type: type(TypeType)} // CHECK:STDOUT: inst780000E4: {kind: StructValue, arg0: inst_block_empty, type: type(symbolic_constant78000089)} // CHECK:STDOUT: inst780000E5: {kind: TupleType, arg0: inst_block7800004D, type: type(TypeType)} // CHECK:STDOUT: inst780000E6: {kind: TupleValue, arg0: inst_block7800004C, type: type(inst780000E5)} // CHECK:STDOUT: inst780000E7: {kind: PatternType, arg0: inst780000DE, type: type(TypeType)} // CHECK:STDOUT: inst780000E8: {kind: ReturnSlotPattern, arg0: inst, type: type(symbolic_constant7800008F)} // CHECK:STDOUT: inst780000E9: {kind: OutParamPattern, arg0: inst780000E8, type: type(symbolic_constant7800008F)} // CHECK:STDOUT: inst780000EA: {kind: ValueBindingPattern, arg0: entity_name7800001E, type: type(symbolic_constant7800008F)} // CHECK:STDOUT: inst780000EB: {kind: ValueParamPattern, arg0: inst780000EA, type: type(symbolic_constant7800008F)} // CHECK:STDOUT: inst780000EC: {kind: InitForm, arg0: inst780000DE, type: type(inst(FormType))} // CHECK:STDOUT: inst780000ED: {kind: ImportRefLoaded, arg0: import_ir_inst74, arg1: entity_name, type: type(TypeType)} // CHECK:STDOUT: inst780000EE: {kind: ImportRefLoaded, arg0: import_ir_inst75, arg1: entity_name, type: type(inst(FormType))} // CHECK:STDOUT: inst780000EF: {kind: ImportRefLoaded, arg0: import_ir_inst76, arg1: entity_name, type: type(inst78000050)} // CHECK:STDOUT: inst780000F0: {kind: ImportRefLoaded, arg0: import_ir_inst77, arg1: entity_name, type: type(inst78000050)} // CHECK:STDOUT: inst780000F1: {kind: SymbolicBinding, arg0: entity_name78000001, arg1: inst, type: type(inst78000050)} // CHECK:STDOUT: inst780000F2: {kind: SymbolicBindingType, arg0: entity_name78000001, arg1: inst780000F1, type: type(TypeType)} // CHECK:STDOUT: inst780000F3: {kind: SymbolicBinding, arg0: entity_name7800001C, arg1: inst, type: type(inst78000050)} // CHECK:STDOUT: inst780000F4: {kind: SymbolicBindingType, arg0: entity_name7800001C, arg1: inst780000F3, type: type(TypeType)} // CHECK:STDOUT: inst780000F5: {kind: TupleType, arg0: inst_block78000054, type: type(TypeType)} // CHECK:STDOUT: inst780000F6: {kind: PatternType, arg0: inst780000F5, type: type(TypeType)} // CHECK:STDOUT: inst780000F7: {kind: InitForm, arg0: inst780000F5, type: type(inst(FormType))} // CHECK:STDOUT: inst780000F8: {kind: LookupImplWitness, arg0: inst780000DC, arg1: specific_interface78000000, type: type(inst(WitnessType))} // CHECK:STDOUT: inst780000F9: {kind: FunctionType, arg0: function78000001, arg1: specific7800000F, type: type(TypeType)} // CHECK:STDOUT: inst780000FA: {kind: StructValue, arg0: inst_block_empty, type: type(symbolic_constant780000A3)} // CHECK:STDOUT: inst780000FB: {kind: FunctionTypeWithSelfType, arg0: inst780000F9, arg1: inst780000DC, type: type(TypeType)} // CHECK:STDOUT: inst780000FC: {kind: ImplWitnessAccess, arg0: inst780000F8, arg1: element0, type: type(symbolic_constant780000A5)} // CHECK:STDOUT: inst780000FD: {kind: SpecificImplFunction, arg0: inst780000FC, arg1: specific78000010, type: type(inst(SpecificFunctionType))} // CHECK:STDOUT: inst780000FE: {kind: InitForm, arg0: inst780000DD, type: type(inst(FormType))} // CHECK:STDOUT: inst780000FF: {kind: PatternType, arg0: inst780000DD, type: type(TypeType)} // CHECK:STDOUT: inst78000100: {kind: RequireCompleteType, arg0: inst780000DD, type: type(inst(WitnessType))} // CHECK:STDOUT: inst78000101: {kind: RequireCompleteType, arg0: inst780000DE, type: type(inst(WitnessType))} // CHECK:STDOUT: inst78000102: {kind: RequireCompleteType, arg0: inst780000F5, type: type(inst(WitnessType))} // CHECK:STDOUT: inst78000103: {kind: RequireCompleteType, arg0: inst780000F2, type: type(inst(WitnessType))} // CHECK:STDOUT: inst78000104: {kind: LookupImplWitness, arg0: inst780000F1, arg1: specific_interface78000000, type: type(inst(WitnessType))} // CHECK:STDOUT: inst78000105: {kind: FunctionType, arg0: function78000001, arg1: specific78000012, type: type(TypeType)} // CHECK:STDOUT: inst78000106: {kind: FunctionTypeWithSelfType, arg0: inst78000105, arg1: inst780000F1, type: type(TypeType)} // CHECK:STDOUT: inst78000107: {kind: ImplWitnessAccess, arg0: inst78000104, arg1: element0, type: type(symbolic_constant780000BD)} // CHECK:STDOUT: inst78000108: {kind: SpecificImplFunction, arg0: inst78000107, arg1: specific78000013, type: type(inst(SpecificFunctionType))} // CHECK:STDOUT: inst78000109: {kind: RequireCompleteType, arg0: inst780000F4, type: type(inst(WitnessType))} // CHECK:STDOUT: inst7800010A: {kind: LookupImplWitness, arg0: inst780000F3, arg1: specific_interface78000000, type: type(inst(WitnessType))} // CHECK:STDOUT: inst7800010B: {kind: FunctionType, arg0: function78000001, arg1: specific78000014, type: type(TypeType)} // CHECK:STDOUT: inst7800010C: {kind: FunctionTypeWithSelfType, arg0: inst7800010B, arg1: inst780000F3, type: type(TypeType)} // CHECK:STDOUT: inst7800010D: {kind: ImplWitnessAccess, arg0: inst7800010A, arg1: element0, type: type(symbolic_constant780000C3)} // CHECK:STDOUT: inst7800010E: {kind: SpecificImplFunction, arg0: inst7800010D, arg1: specific78000015, type: type(inst(SpecificFunctionType))} // CHECK:STDOUT: inst7800010F: {kind: SymbolicBindingPattern, arg0: entity_name78000029, type: type(inst78000099)} // CHECK:STDOUT: inst78000110: {kind: SymbolicBindingPattern, arg0: entity_name7800002A, type: type(inst78000099)} // CHECK:STDOUT: inst78000111: {kind: ImportRefLoaded, arg0: import_ir_inst8E, arg1: entity_name, type: type(TypeType)} // CHECK:STDOUT: inst78000112: {kind: ImportRefLoaded, arg0: import_ir_inst8F, arg1: entity_name, type: type(TypeType)} // CHECK:STDOUT: inst78000113: {kind: ImportRefLoaded, arg0: import_ir_inst90, arg1: entity_name, type: type(inst78000050)} // CHECK:STDOUT: inst78000114: {kind: ImportRefLoaded, arg0: import_ir_inst91, arg1: entity_name, type: type(inst78000050)} // CHECK:STDOUT: inst78000115: {kind: SymbolicBinding, arg0: entity_name78000001, arg1: inst, type: type(inst78000050)} // CHECK:STDOUT: inst78000116: {kind: SymbolicBinding, arg0: entity_name7800001C, arg1: inst, type: type(inst78000050)} // CHECK:STDOUT: inst78000117: {kind: TupleValue, arg0: inst_block78000061, type: type(inst780000E5)} // CHECK:STDOUT: inst78000118: {kind: SymbolicBindingType, arg0: entity_name78000001, arg1: inst78000115, type: type(TypeType)} // CHECK:STDOUT: inst78000119: {kind: SymbolicBindingType, arg0: entity_name7800001C, arg1: inst78000116, type: type(TypeType)} // CHECK:STDOUT: inst7800011A: {kind: TupleType, arg0: inst_block78000062, type: type(TypeType)} // CHECK:STDOUT: inst7800011B: {kind: ImplWitness, arg0: inst780000E0, arg1: specific78000016, type: type(inst(WitnessType))} // CHECK:STDOUT: inst7800011C: {kind: FunctionType, arg0: function78000004, arg1: specific78000016, type: type(TypeType)} // CHECK:STDOUT: inst7800011D: {kind: StructValue, arg0: inst_block_empty, type: type(symbolic_constant780000D4)} // CHECK:STDOUT: inst7800011E: {kind: ImportRefUnloaded, arg0: import_ir_inst9B, arg1: entity_name} // CHECK:STDOUT: inst7800011F: {kind: ImplDecl, arg0: impl78000009, arg1: inst_block_empty} // CHECK:STDOUT: inst78000120: {kind: SymbolicBinding, arg0: entity_name7800002B, arg1: inst, type: type(inst78000050)} // CHECK:STDOUT: inst78000121: {kind: SymbolicBindingType, arg0: entity_name7800002B, arg1: inst78000120, type: type(TypeType)} // CHECK:STDOUT: inst78000122: {kind: TupleType, arg0: inst_block78000067, type: type(TypeType)} // CHECK:STDOUT: inst78000123: {kind: ImportRefUnloaded, arg0: import_ir_inst9D, arg1: entity_name} // CHECK:STDOUT: inst78000124: {kind: ImplWitnessTable, arg0: inst_block78000068, arg1: impl78000009} // CHECK:STDOUT: inst78000125: {kind: ImplWitness, arg0: inst78000124, arg1: specific78000017, type: type(inst(WitnessType))} // CHECK:STDOUT: inst78000126: {kind: FunctionDecl, arg0: function78000005, arg1: inst_block_empty, type: type(symbolic_constant780000DC)} // CHECK:STDOUT: inst78000127: {kind: FunctionType, arg0: function78000005, arg1: specific78000017, type: type(TypeType)} // CHECK:STDOUT: inst78000128: {kind: StructValue, arg0: inst_block_empty, type: type(symbolic_constant780000DC)} // CHECK:STDOUT: inst78000129: {kind: TupleType, arg0: inst_block7800006A, type: type(TypeType)} // CHECK:STDOUT: inst7800012A: {kind: TupleValue, arg0: inst_block78000069, type: type(inst78000129)} // CHECK:STDOUT: inst7800012B: {kind: PatternType, arg0: inst78000122, type: type(TypeType)} // CHECK:STDOUT: inst7800012C: {kind: ReturnSlotPattern, arg0: inst, type: type(symbolic_constant780000E2)} // CHECK:STDOUT: inst7800012D: {kind: OutParamPattern, arg0: inst7800012C, type: type(symbolic_constant780000E2)} // CHECK:STDOUT: inst7800012E: {kind: ValueBindingPattern, arg0: entity_name7800002D, type: type(symbolic_constant780000E2)} // CHECK:STDOUT: inst7800012F: {kind: ValueParamPattern, arg0: inst7800012E, type: type(symbolic_constant780000E2)} // CHECK:STDOUT: inst78000130: {kind: InitForm, arg0: inst78000122, type: type(inst(FormType))} // CHECK:STDOUT: inst78000131: {kind: ImportRefLoaded, arg0: import_ir_instA4, arg1: entity_name, type: type(TypeType)} // CHECK:STDOUT: inst78000132: {kind: ImportRefLoaded, arg0: import_ir_instA5, arg1: entity_name, type: type(inst(FormType))} // CHECK:STDOUT: inst78000133: {kind: ImportRefLoaded, arg0: import_ir_instA6, arg1: entity_name, type: type(inst78000050)} // CHECK:STDOUT: inst78000134: {kind: ImportRefLoaded, arg0: import_ir_instA7, arg1: entity_name, type: type(inst78000050)} // CHECK:STDOUT: inst78000135: {kind: ImportRefLoaded, arg0: import_ir_instA8, arg1: entity_name, type: type(inst78000050)} // CHECK:STDOUT: inst78000136: {kind: SymbolicBinding, arg0: entity_name78000001, arg1: inst, type: type(inst78000050)} // CHECK:STDOUT: inst78000137: {kind: SymbolicBindingType, arg0: entity_name78000001, arg1: inst78000136, type: type(TypeType)} // CHECK:STDOUT: inst78000138: {kind: SymbolicBinding, arg0: entity_name7800001C, arg1: inst, type: type(inst78000050)} // CHECK:STDOUT: inst78000139: {kind: SymbolicBindingType, arg0: entity_name7800001C, arg1: inst78000138, type: type(TypeType)} // CHECK:STDOUT: inst7800013A: {kind: SymbolicBinding, arg0: entity_name7800002B, arg1: inst, type: type(inst78000050)} // CHECK:STDOUT: inst7800013B: {kind: SymbolicBindingType, arg0: entity_name7800002B, arg1: inst7800013A, type: type(TypeType)} // CHECK:STDOUT: inst7800013C: {kind: TupleType, arg0: inst_block78000071, type: type(TypeType)} // CHECK:STDOUT: inst7800013D: {kind: PatternType, arg0: inst7800013C, type: type(TypeType)} // CHECK:STDOUT: inst7800013E: {kind: InitForm, arg0: inst7800013C, type: type(inst(FormType))} // CHECK:STDOUT: inst7800013F: {kind: LookupImplWitness, arg0: inst78000120, arg1: specific_interface78000000, type: type(inst(WitnessType))} // CHECK:STDOUT: inst78000140: {kind: FunctionType, arg0: function78000001, arg1: specific78000018, type: type(TypeType)} // CHECK:STDOUT: inst78000141: {kind: StructValue, arg0: inst_block_empty, type: type(symbolic_constant780000FB)} // CHECK:STDOUT: inst78000142: {kind: FunctionTypeWithSelfType, arg0: inst78000140, arg1: inst78000120, type: type(TypeType)} // CHECK:STDOUT: inst78000143: {kind: ImplWitnessAccess, arg0: inst7800013F, arg1: element0, type: type(symbolic_constant780000FD)} // CHECK:STDOUT: inst78000144: {kind: SpecificImplFunction, arg0: inst78000143, arg1: specific78000019, type: type(inst(SpecificFunctionType))} // CHECK:STDOUT: inst78000145: {kind: InitForm, arg0: inst78000121, type: type(inst(FormType))} // CHECK:STDOUT: inst78000146: {kind: PatternType, arg0: inst78000121, type: type(TypeType)} // CHECK:STDOUT: inst78000147: {kind: RequireCompleteType, arg0: inst78000121, type: type(inst(WitnessType))} // CHECK:STDOUT: inst78000148: {kind: RequireCompleteType, arg0: inst78000122, type: type(inst(WitnessType))} // CHECK:STDOUT: inst78000149: {kind: RequireCompleteType, arg0: inst7800013C, type: type(inst(WitnessType))} // CHECK:STDOUT: inst7800014A: {kind: RequireCompleteType, arg0: inst78000137, type: type(inst(WitnessType))} // CHECK:STDOUT: inst7800014B: {kind: LookupImplWitness, arg0: inst78000136, arg1: specific_interface78000000, type: type(inst(WitnessType))} // CHECK:STDOUT: inst7800014C: {kind: FunctionType, arg0: function78000001, arg1: specific7800001B, type: type(TypeType)} // CHECK:STDOUT: inst7800014D: {kind: FunctionTypeWithSelfType, arg0: inst7800014C, arg1: inst78000136, type: type(TypeType)} // CHECK:STDOUT: inst7800014E: {kind: ImplWitnessAccess, arg0: inst7800014B, arg1: element0, type: type(symbolic_constant7800011B)} // CHECK:STDOUT: inst7800014F: {kind: SpecificImplFunction, arg0: inst7800014E, arg1: specific7800001C, type: type(inst(SpecificFunctionType))} // CHECK:STDOUT: inst78000150: {kind: RequireCompleteType, arg0: inst78000139, type: type(inst(WitnessType))} // CHECK:STDOUT: inst78000151: {kind: LookupImplWitness, arg0: inst78000138, arg1: specific_interface78000000, type: type(inst(WitnessType))} // CHECK:STDOUT: inst78000152: {kind: FunctionType, arg0: function78000001, arg1: specific7800001D, type: type(TypeType)} // CHECK:STDOUT: inst78000153: {kind: FunctionTypeWithSelfType, arg0: inst78000152, arg1: inst78000138, type: type(TypeType)} // CHECK:STDOUT: inst78000154: {kind: ImplWitnessAccess, arg0: inst78000151, arg1: element0, type: type(symbolic_constant78000121)} // CHECK:STDOUT: inst78000155: {kind: SpecificImplFunction, arg0: inst78000154, arg1: specific7800001E, type: type(inst(SpecificFunctionType))} // CHECK:STDOUT: inst78000156: {kind: RequireCompleteType, arg0: inst7800013B, type: type(inst(WitnessType))} // CHECK:STDOUT: inst78000157: {kind: LookupImplWitness, arg0: inst7800013A, arg1: specific_interface78000000, type: type(inst(WitnessType))} // CHECK:STDOUT: inst78000158: {kind: FunctionType, arg0: function78000001, arg1: specific7800001F, type: type(TypeType)} // CHECK:STDOUT: inst78000159: {kind: FunctionTypeWithSelfType, arg0: inst78000158, arg1: inst7800013A, type: type(TypeType)} // CHECK:STDOUT: inst7800015A: {kind: ImplWitnessAccess, arg0: inst78000157, arg1: element0, type: type(symbolic_constant78000127)} // CHECK:STDOUT: inst7800015B: {kind: SpecificImplFunction, arg0: inst7800015A, arg1: specific78000020, type: type(inst(SpecificFunctionType))} // CHECK:STDOUT: inst7800015C: {kind: SymbolicBindingPattern, arg0: entity_name7800003D, type: type(inst78000099)} // CHECK:STDOUT: inst7800015D: {kind: SymbolicBindingPattern, arg0: entity_name7800003E, type: type(inst78000099)} // CHECK:STDOUT: inst7800015E: {kind: SymbolicBindingPattern, arg0: entity_name7800003F, type: type(inst78000099)} // CHECK:STDOUT: inst7800015F: {kind: ImportRefLoaded, arg0: import_ir_instC8, arg1: entity_name, type: type(TypeType)} // CHECK:STDOUT: inst78000160: {kind: ImportRefLoaded, arg0: import_ir_instC9, arg1: entity_name, type: type(TypeType)} // CHECK:STDOUT: inst78000161: {kind: ImportRefLoaded, arg0: import_ir_instCA, arg1: entity_name, type: type(inst78000050)} // CHECK:STDOUT: inst78000162: {kind: ImportRefLoaded, arg0: import_ir_instCB, arg1: entity_name, type: type(inst78000050)} // CHECK:STDOUT: inst78000163: {kind: ImportRefLoaded, arg0: import_ir_instCC, arg1: entity_name, type: type(inst78000050)} // CHECK:STDOUT: inst78000164: {kind: SymbolicBinding, arg0: entity_name78000001, arg1: inst, type: type(inst78000050)} // CHECK:STDOUT: inst78000165: {kind: SymbolicBinding, arg0: entity_name7800001C, arg1: inst, type: type(inst78000050)} // CHECK:STDOUT: inst78000166: {kind: SymbolicBinding, arg0: entity_name7800002B, arg1: inst, type: type(inst78000050)} // CHECK:STDOUT: inst78000167: {kind: TupleValue, arg0: inst_block78000080, type: type(inst78000129)} // CHECK:STDOUT: inst78000168: {kind: SymbolicBindingType, arg0: entity_name78000001, arg1: inst78000164, type: type(TypeType)} // CHECK:STDOUT: inst78000169: {kind: SymbolicBindingType, arg0: entity_name7800001C, arg1: inst78000165, type: type(TypeType)} // CHECK:STDOUT: inst7800016A: {kind: SymbolicBindingType, arg0: entity_name7800002B, arg1: inst78000166, type: type(TypeType)} // CHECK:STDOUT: inst7800016B: {kind: TupleType, arg0: inst_block78000081, type: type(TypeType)} // CHECK:STDOUT: inst7800016C: {kind: ImplWitness, arg0: inst78000124, arg1: specific78000021, type: type(inst(WitnessType))} // CHECK:STDOUT: inst7800016D: {kind: FunctionType, arg0: function78000005, arg1: specific78000021, type: type(TypeType)} // CHECK:STDOUT: inst7800016E: {kind: StructValue, arg0: inst_block_empty, type: type(symbolic_constant7800013C)} // CHECK:STDOUT: inst7800016F: {kind: RequireSpecificDefinition, arg0: specific7800000B, type: type(inst(RequireSpecificDefinitionType))} // CHECK:STDOUT: inst78000170: {kind: RequireSpecificDefinition, arg0: specific7800000B, type: type(inst(RequireSpecificDefinitionType))} // CHECK:STDOUT: inst78000171: {kind: RequireSpecificDefinition, arg0: specific78000022, type: type(inst(RequireSpecificDefinitionType))} // CHECK:STDOUT: inst78000172: {kind: LookupImplWitness, arg0: inst7800001D, arg1: specific_interface78000000, type: type(inst(WitnessType))} // CHECK:STDOUT: inst78000173: {kind: LookupImplWitness, arg0: inst7800001E, arg1: specific_interface78000000, type: type(inst(WitnessType))} // CHECK:STDOUT: inst78000174: {kind: FacetValue, arg0: inst7800001D, arg1: inst_block78000088, type: type(inst78000050)} // CHECK:STDOUT: inst78000175: {kind: FunctionType, arg0: function78000001, arg1: specific78000023, type: type(TypeType)} // CHECK:STDOUT: inst78000176: {kind: StructValue, arg0: inst_block_empty, type: type(symbolic_constant78000144)} // CHECK:STDOUT: inst78000177: {kind: FunctionTypeWithSelfType, arg0: inst78000175, arg1: inst78000174, type: type(TypeType)} // CHECK:STDOUT: inst78000178: {kind: ImplWitnessAccess, arg0: inst78000172, arg1: element0, type: type(symbolic_constant7800014A)} // CHECK:STDOUT: inst78000179: {kind: ImplWitnessAccess, arg0: inst78000172, arg1: element0, type: type(symbolic_constant78000146)} // CHECK:STDOUT: inst7800017A: {kind: FacetValue, arg0: inst7800001E, arg1: inst_block7800008B, type: type(inst78000050)} // CHECK:STDOUT: inst7800017B: {kind: FunctionType, arg0: function78000001, arg1: specific78000024, type: type(TypeType)} // CHECK:STDOUT: inst7800017C: {kind: FunctionTypeWithSelfType, arg0: inst7800017B, arg1: inst7800017A, type: type(TypeType)} // CHECK:STDOUT: inst7800017D: {kind: ImplWitnessAccess, arg0: inst78000173, arg1: element0, type: type(symbolic_constant7800014A)} // CHECK:STDOUT: inst7800017E: {kind: BoundMethod, arg0: inst78000048, arg1: inst78000178, type: type(inst(BoundMethodType))} // CHECK:STDOUT: inst7800017F: {kind: SpecificImplFunction, arg0: inst78000178, arg1: specific78000025, type: type(inst(SpecificFunctionType))} // CHECK:STDOUT: inst78000180: {kind: SpecificImplFunction, arg0: inst78000179, arg1: specific78000025, type: type(inst(SpecificFunctionType))} // CHECK:STDOUT: inst78000181: {kind: SpecificImplFunction, arg0: inst7800017D, arg1: specific78000026, type: type(inst(SpecificFunctionType))} // CHECK:STDOUT: inst78000182: {kind: BoundMethod, arg0: inst78000048, arg1: inst7800017F, type: type(inst(BoundMethodType))} // CHECK:STDOUT: inst78000183: {kind: Call, arg0: inst78000182, arg1: inst_block7800008F, type: type(symbolic_constant78000004)} // CHECK:STDOUT: inst78000184: {kind: InPlaceInit, arg0: inst78000183, arg1: inst7800004C, type: type(symbolic_constant78000004)} // CHECK:STDOUT: inst78000185: {kind: TupleAccess, arg0: inst7800003C, arg1: element1, type: type(inst78000026)} // CHECK:STDOUT: inst78000186: {kind: TupleInit, arg0: inst_block_empty, arg1: inst, type: type(inst78000026)} // CHECK:STDOUT: inst78000187: {kind: Converted, arg0: inst78000049, arg1: inst78000186, type: type(inst78000026)} // CHECK:STDOUT: inst78000188: {kind: TupleInit, arg0: inst_block78000090, arg1: inst7800003C, type: type(symbolic_constant7800000A)} // CHECK:STDOUT: inst78000189: {kind: Converted, arg0: inst7800004A, arg1: inst78000188, type: type(symbolic_constant7800000A)} // CHECK:STDOUT: inst7800018A: {kind: ReturnExpr, arg0: inst78000189, arg1: inst7800003C} // CHECK:STDOUT: constant_values: // CHECK:STDOUT: values: // CHECK:STDOUT: instF: concrete_constant(instF) // CHECK:STDOUT: inst78000011: concrete_constant(inst78000011) // CHECK:STDOUT: inst78000012: concrete_constant(inst78000012) // CHECK:STDOUT: inst78000013: symbolic_constant78000000 // CHECK:STDOUT: inst78000014: symbolic_constant78000000 // CHECK:STDOUT: inst78000015: concrete_constant(inst(TypeType)) // CHECK:STDOUT: inst78000016: concrete_constant(inst78000016) // CHECK:STDOUT: inst78000017: symbolic_constant78000002 // CHECK:STDOUT: inst78000018: symbolic_constant78000001 // CHECK:STDOUT: inst78000019: symbolic_constant78000002 // CHECK:STDOUT: inst7800001A: concrete_constant(inst7800001A) // CHECK:STDOUT: inst7800001B: symbolic_constant78000002 // CHECK:STDOUT: inst7800001C: symbolic_constant78000004 // CHECK:STDOUT: inst7800001D: symbolic_constant78000003 // CHECK:STDOUT: inst7800001E: symbolic_constant78000004 // CHECK:STDOUT: inst7800001F: symbolic_constant78000005 // CHECK:STDOUT: inst78000021: concrete_constant(inst78000021) // CHECK:STDOUT: inst78000022: symbolic_constant78000006 // CHECK:STDOUT: inst78000023: concrete_constant(inst78000023) // CHECK:STDOUT: inst78000024: symbolic_constant78000002 // CHECK:STDOUT: inst78000025: symbolic_constant78000004 // CHECK:STDOUT: inst78000026: concrete_constant(inst78000026) // CHECK:STDOUT: inst78000027: concrete_constant(inst78000028) // CHECK:STDOUT: inst78000028: concrete_constant(inst78000028) // CHECK:STDOUT: inst78000029: concrete_constant(inst78000029) // CHECK:STDOUT: inst7800002A: symbolic_constant78000008 // CHECK:STDOUT: inst7800002B: symbolic_constant78000007 // CHECK:STDOUT: inst7800002C: symbolic_constant78000008 // CHECK:STDOUT: inst7800002D: concrete_constant(inst7800002D) // CHECK:STDOUT: inst7800002E: concrete_constant(inst78000026) // CHECK:STDOUT: inst7800002F: symbolic_constant78000009 // CHECK:STDOUT: inst78000030: symbolic_constant7800000A // CHECK:STDOUT: inst78000031: symbolic_constant7800000A // CHECK:STDOUT: inst78000032: symbolic_constant7800000C // CHECK:STDOUT: inst78000033: symbolic_constant7800000B // CHECK:STDOUT: inst78000034: symbolic_constant7800000C // CHECK:STDOUT: inst78000035: symbolic_constant7800000D // CHECK:STDOUT: inst78000036: concrete_constant(inst78000036) // CHECK:STDOUT: inst78000037: symbolic_constant7800000E // CHECK:STDOUT: inst78000038: concrete_constant(inst78000038) // CHECK:STDOUT: inst78000039: concrete_constant(inst(TypeType)) // CHECK:STDOUT: inst7800003B: symbolic_constant78000004 // CHECK:STDOUT: inst7800003E: concrete_constant(inst78000040) // CHECK:STDOUT: inst7800003F: concrete_constant(inst7800003F) // CHECK:STDOUT: inst78000040: concrete_constant(inst78000040) // CHECK:STDOUT: inst78000041: symbolic_constant78000010 // CHECK:STDOUT: inst78000042: symbolic_constant7800000F // CHECK:STDOUT: inst78000043: symbolic_constant78000010 // CHECK:STDOUT: inst78000044: symbolic_constant78000011 // CHECK:STDOUT: inst78000045: symbolic_constant78000013 // CHECK:STDOUT: inst78000046: symbolic_constant78000012 // CHECK:STDOUT: inst78000047: symbolic_constant78000013 // CHECK:STDOUT: inst78000049: concrete_constant(inst78000028) // CHECK:STDOUT: inst7800004B: symbolic_constant78000013 // CHECK:STDOUT: inst7800004D: symbolic_constant78000010 // CHECK:STDOUT: inst7800004E: concrete_constant(inst78000050) // CHECK:STDOUT: inst7800004F: concrete_constant(inst78000050) // CHECK:STDOUT: inst78000050: concrete_constant(inst78000050) // CHECK:STDOUT: inst78000051: concrete_constant(inst78000051) // CHECK:STDOUT: inst78000052: symbolic_constant78000014 // CHECK:STDOUT: inst78000053: concrete_constant(inst7800006D) // CHECK:STDOUT: inst78000054: constant // CHECK:STDOUT: inst78000055: symbolic_constant78000014 // CHECK:STDOUT: inst78000056: symbolic_constant78000017 // CHECK:STDOUT: inst78000057: symbolic_constant78000015 // CHECK:STDOUT: inst78000058: symbolic_constant78000016 // CHECK:STDOUT: inst78000059: symbolic_constant78000018 // CHECK:STDOUT: inst7800005A: symbolic_constant7800001A // CHECK:STDOUT: inst7800005B: concrete_constant(inst7800005B) // CHECK:STDOUT: inst7800005C: concrete_constant(inst7800005C) // CHECK:STDOUT: inst7800005D: concrete_constant(inst7800005D) // CHECK:STDOUT: inst7800005E: concrete_constant(inst7800005E) // CHECK:STDOUT: inst7800005F: symbolic_constant7800001C // CHECK:STDOUT: inst78000060: symbolic_constant78000021 // CHECK:STDOUT: inst78000061: symbolic_constant78000020 // CHECK:STDOUT: inst78000062: symbolic_constant78000014 // CHECK:STDOUT: inst78000063: symbolic_constant78000022 // CHECK:STDOUT: inst78000064: symbolic_constant78000023 // CHECK:STDOUT: inst78000065: symbolic_constant78000024 // CHECK:STDOUT: inst78000066: symbolic_constant78000025 // CHECK:STDOUT: inst78000067: symbolic_constant78000028 // CHECK:STDOUT: inst78000068: symbolic_constant78000029 // CHECK:STDOUT: inst78000069: symbolic_constant7800002A // CHECK:STDOUT: inst7800006A: constant // CHECK:STDOUT: inst7800006B: concrete_constant(inst7800006B) // CHECK:STDOUT: inst7800006C: symbolic_constant78000017 // CHECK:STDOUT: inst7800006D: concrete_constant(inst7800006D) // CHECK:STDOUT: inst7800006E: symbolic_constant78000142 // CHECK:STDOUT: inst7800006F: constant // CHECK:STDOUT: inst78000070: concrete_constant(inst78000070) // CHECK:STDOUT: inst78000071: symbolic_constant7800002B // CHECK:STDOUT: inst78000072: symbolic_constant7800002C // CHECK:STDOUT: inst78000073: symbolic_constant7800002D // CHECK:STDOUT: inst78000074: constant // CHECK:STDOUT: inst78000075: concrete_constant(inst78000075) // CHECK:STDOUT: inst78000076: symbolic_constant7800002F // CHECK:STDOUT: inst78000077: symbolic_constant78000033 // CHECK:STDOUT: inst78000078: symbolic_constant78000031 // CHECK:STDOUT: inst78000079: symbolic_constant78000032 // CHECK:STDOUT: inst7800007A: symbolic_constant78000035 // CHECK:STDOUT: inst7800007B: concrete_constant(inst7800007B) // CHECK:STDOUT: inst7800007C: concrete_constant(inst7800007C) // CHECK:STDOUT: inst7800007D: concrete_constant(inst7800007D) // CHECK:STDOUT: inst7800007E: concrete_constant(inst7800007E) // CHECK:STDOUT: inst7800007F: symbolic_constant78000037 // CHECK:STDOUT: inst78000080: symbolic_constant7800003E // CHECK:STDOUT: inst78000081: symbolic_constant7800003D // CHECK:STDOUT: inst78000082: symbolic_constant7800003C // CHECK:STDOUT: inst78000083: symbolic_constant7800003F // CHECK:STDOUT: inst78000084: symbolic_constant78000040 // CHECK:STDOUT: inst78000085: symbolic_constant78000041 // CHECK:STDOUT: inst78000086: symbolic_constant78000042 // CHECK:STDOUT: inst78000087: symbolic_constant78000043 // CHECK:STDOUT: inst78000088: symbolic_constant78000044 // CHECK:STDOUT: inst78000089: symbolic_constant78000045 // CHECK:STDOUT: inst7800008A: symbolic_constant78000046 // CHECK:STDOUT: inst7800008B: symbolic_constant78000047 // CHECK:STDOUT: inst7800008C: symbolic_constant78000048 // CHECK:STDOUT: inst7800008D: symbolic_constant78000049 // CHECK:STDOUT: inst7800008E: symbolic_constant7800004B // CHECK:STDOUT: inst7800008F: symbolic_constant7800004C // CHECK:STDOUT: inst78000090: symbolic_constant78000051 // CHECK:STDOUT: inst78000091: symbolic_constant78000053 // CHECK:STDOUT: inst78000092: symbolic_constant78000055 // CHECK:STDOUT: inst78000093: symbolic_constant78000056 // CHECK:STDOUT: inst78000094: symbolic_constant78000057 // CHECK:STDOUT: inst78000095: symbolic_constant78000058 // CHECK:STDOUT: inst78000096: symbolic_constant78000059 // CHECK:STDOUT: inst78000097: symbolic_constant7800005A // CHECK:STDOUT: inst78000098: symbolic_constant7800005B // CHECK:STDOUT: inst78000099: concrete_constant(inst78000099) // CHECK:STDOUT: inst7800009A: concrete_constant(inst7800009A) // CHECK:STDOUT: inst7800009B: symbolic_constant7800002E // CHECK:STDOUT: inst7800009C: concrete_constant(inst78000050) // CHECK:STDOUT: inst7800009D: symbolic_constant7800003C // CHECK:STDOUT: inst7800009E: symbolic_constant7800005F // CHECK:STDOUT: inst7800009F: symbolic_constant78000060 // CHECK:STDOUT: inst780000A0: symbolic_constant78000061 // CHECK:STDOUT: inst780000A1: symbolic_constant78000062 // CHECK:STDOUT: inst780000A2: symbolic_constant78000064 // CHECK:STDOUT: inst780000A3: symbolic_constant78000065 // CHECK:STDOUT: inst780000A4: constant // CHECK:STDOUT: inst780000A5: concrete_constant(inst780000A5) // CHECK:STDOUT: inst780000A6: concrete_constant(inst(BoolType)) // CHECK:STDOUT: inst780000A7: concrete_constant(inst78000050) // CHECK:STDOUT: inst780000A8: constant // CHECK:STDOUT: inst780000A9: concrete_constant(inst780000A9) // CHECK:STDOUT: inst780000AA: concrete_constant(inst(CharLiteralType)) // CHECK:STDOUT: inst780000AB: concrete_constant(inst78000050) // CHECK:STDOUT: inst780000AC: constant // CHECK:STDOUT: inst780000AD: concrete_constant(inst780000AD) // CHECK:STDOUT: inst780000AE: concrete_constant(inst(FloatLiteralType)) // CHECK:STDOUT: inst780000AF: concrete_constant(inst78000050) // CHECK:STDOUT: inst780000B0: constant // CHECK:STDOUT: inst780000B1: concrete_constant(inst780000B1) // CHECK:STDOUT: inst780000B2: concrete_constant(inst(IntLiteralType)) // CHECK:STDOUT: inst780000B3: concrete_constant(inst78000050) // CHECK:STDOUT: inst780000B4: symbolic_constant7800013E // CHECK:STDOUT: inst780000B5: concrete_constant(inst780000B5) // CHECK:STDOUT: inst780000B6: constant // CHECK:STDOUT: inst780000B7: concrete_constant(inst780000B7) // CHECK:STDOUT: inst780000B8: symbolic_constant78000067 // CHECK:STDOUT: inst780000B9: concrete_constant(inst780000B9) // CHECK:STDOUT: inst780000BA: symbolic_constant78000066 // CHECK:STDOUT: inst780000BB: concrete_constant(inst78000050) // CHECK:STDOUT: inst780000BC: symbolic_constant7800006B // CHECK:STDOUT: inst780000BD: symbolic_constant7800006C // CHECK:STDOUT: inst780000BE: symbolic_constant7800006D // CHECK:STDOUT: inst780000BF: symbolic_constant7800006E // CHECK:STDOUT: inst780000C0: symbolic_constant78000072 // CHECK:STDOUT: inst780000C1: symbolic_constant78000070 // CHECK:STDOUT: inst780000C2: symbolic_constant78000071 // CHECK:STDOUT: inst780000C3: concrete_constant(inst780000C3) // CHECK:STDOUT: inst780000C4: concrete_constant(inst780000C4) // CHECK:STDOUT: inst780000C5: concrete_constant(inst780000C5) // CHECK:STDOUT: inst780000C6: concrete_constant(inst780000C6) // CHECK:STDOUT: inst780000C7: symbolic_constant78000075 // CHECK:STDOUT: inst780000C8: symbolic_constant7800007A // CHECK:STDOUT: inst780000C9: symbolic_constant78000079 // CHECK:STDOUT: inst780000CA: symbolic_constant7800006B // CHECK:STDOUT: inst780000CB: symbolic_constant7800007B // CHECK:STDOUT: inst780000CC: symbolic_constant7800007C // CHECK:STDOUT: inst780000CD: symbolic_constant7800007D // CHECK:STDOUT: inst780000CE: symbolic_constant7800007E // CHECK:STDOUT: inst780000CF: symbolic_constant78000080 // CHECK:STDOUT: inst780000D0: symbolic_constant78000081 // CHECK:STDOUT: inst780000D1: symbolic_constant78000082 // CHECK:STDOUT: inst780000D2: constant // CHECK:STDOUT: inst780000D3: concrete_constant(inst780000D3) // CHECK:STDOUT: inst780000D4: concrete_constant(inst(TypeType)) // CHECK:STDOUT: inst780000D5: concrete_constant(inst78000050) // CHECK:STDOUT: inst780000D6: constant // CHECK:STDOUT: inst780000D7: concrete_constant(inst780000D7) // CHECK:STDOUT: inst780000D8: concrete_constant(inst78000026) // CHECK:STDOUT: inst780000D9: concrete_constant(inst78000050) // CHECK:STDOUT: inst780000DA: constant // CHECK:STDOUT: inst780000DB: concrete_constant(inst780000DB) // CHECK:STDOUT: inst780000DC: symbolic_constant78000083 // CHECK:STDOUT: inst780000DD: symbolic_constant78000084 // CHECK:STDOUT: inst780000DE: symbolic_constant78000085 // CHECK:STDOUT: inst780000DF: constant // CHECK:STDOUT: inst780000E0: concrete_constant(inst780000E0) // CHECK:STDOUT: inst780000E1: symbolic_constant78000087 // CHECK:STDOUT: inst780000E2: symbolic_constant7800008B // CHECK:STDOUT: inst780000E3: symbolic_constant78000089 // CHECK:STDOUT: inst780000E4: symbolic_constant7800008A // CHECK:STDOUT: inst780000E5: concrete_constant(inst780000E5) // CHECK:STDOUT: inst780000E6: symbolic_constant7800008D // CHECK:STDOUT: inst780000E7: symbolic_constant7800008E // CHECK:STDOUT: inst780000E8: concrete_constant(inst780000E8) // CHECK:STDOUT: inst780000E9: concrete_constant(inst780000E9) // CHECK:STDOUT: inst780000EA: concrete_constant(inst780000EA) // CHECK:STDOUT: inst780000EB: concrete_constant(inst780000EB) // CHECK:STDOUT: inst780000EC: symbolic_constant78000090 // CHECK:STDOUT: inst780000ED: symbolic_constant7800009A // CHECK:STDOUT: inst780000EE: symbolic_constant78000099 // CHECK:STDOUT: inst780000EF: symbolic_constant78000098 // CHECK:STDOUT: inst780000F0: symbolic_constant78000097 // CHECK:STDOUT: inst780000F1: symbolic_constant7800009B // CHECK:STDOUT: inst780000F2: symbolic_constant7800009C // CHECK:STDOUT: inst780000F3: symbolic_constant7800009D // CHECK:STDOUT: inst780000F4: symbolic_constant7800009E // CHECK:STDOUT: inst780000F5: symbolic_constant7800009F // CHECK:STDOUT: inst780000F6: symbolic_constant780000A0 // CHECK:STDOUT: inst780000F7: symbolic_constant780000A1 // CHECK:STDOUT: inst780000F8: symbolic_constant780000A2 // CHECK:STDOUT: inst780000F9: symbolic_constant780000A3 // CHECK:STDOUT: inst780000FA: symbolic_constant780000A4 // CHECK:STDOUT: inst780000FB: symbolic_constant780000A5 // CHECK:STDOUT: inst780000FC: symbolic_constant780000A6 // CHECK:STDOUT: inst780000FD: symbolic_constant780000A7 // CHECK:STDOUT: inst780000FE: symbolic_constant780000A9 // CHECK:STDOUT: inst780000FF: symbolic_constant780000AA // CHECK:STDOUT: inst78000100: symbolic_constant780000AF // CHECK:STDOUT: inst78000101: symbolic_constant780000B7 // CHECK:STDOUT: inst78000102: symbolic_constant780000B9 // CHECK:STDOUT: inst78000103: symbolic_constant780000BA // CHECK:STDOUT: inst78000104: symbolic_constant780000BB // CHECK:STDOUT: inst78000105: symbolic_constant780000BC // CHECK:STDOUT: inst78000106: symbolic_constant780000BD // CHECK:STDOUT: inst78000107: symbolic_constant780000BE // CHECK:STDOUT: inst78000108: symbolic_constant780000BF // CHECK:STDOUT: inst78000109: symbolic_constant780000C0 // CHECK:STDOUT: inst7800010A: symbolic_constant780000C1 // CHECK:STDOUT: inst7800010B: symbolic_constant780000C2 // CHECK:STDOUT: inst7800010C: symbolic_constant780000C3 // CHECK:STDOUT: inst7800010D: symbolic_constant780000C4 // CHECK:STDOUT: inst7800010E: symbolic_constant780000C5 // CHECK:STDOUT: inst7800010F: concrete_constant(inst7800010F) // CHECK:STDOUT: inst78000110: concrete_constant(inst78000110) // CHECK:STDOUT: inst78000111: symbolic_constant78000086 // CHECK:STDOUT: inst78000112: concrete_constant(inst78000050) // CHECK:STDOUT: inst78000113: symbolic_constant78000098 // CHECK:STDOUT: inst78000114: symbolic_constant78000097 // CHECK:STDOUT: inst78000115: symbolic_constant780000CC // CHECK:STDOUT: inst78000116: symbolic_constant780000CD // CHECK:STDOUT: inst78000117: symbolic_constant780000CE // CHECK:STDOUT: inst78000118: symbolic_constant780000CF // CHECK:STDOUT: inst78000119: symbolic_constant780000D0 // CHECK:STDOUT: inst7800011A: symbolic_constant780000D1 // CHECK:STDOUT: inst7800011B: symbolic_constant780000D2 // CHECK:STDOUT: inst7800011C: symbolic_constant780000D4 // CHECK:STDOUT: inst7800011D: symbolic_constant780000D5 // CHECK:STDOUT: inst7800011E: constant // CHECK:STDOUT: inst7800011F: concrete_constant(inst7800011F) // CHECK:STDOUT: inst78000120: symbolic_constant780000D6 // CHECK:STDOUT: inst78000121: symbolic_constant780000D7 // CHECK:STDOUT: inst78000122: symbolic_constant780000D8 // CHECK:STDOUT: inst78000123: constant // CHECK:STDOUT: inst78000124: concrete_constant(inst78000124) // CHECK:STDOUT: inst78000125: symbolic_constant780000DA // CHECK:STDOUT: inst78000126: symbolic_constant780000DE // CHECK:STDOUT: inst78000127: symbolic_constant780000DC // CHECK:STDOUT: inst78000128: symbolic_constant780000DD // CHECK:STDOUT: inst78000129: concrete_constant(inst78000129) // CHECK:STDOUT: inst7800012A: symbolic_constant780000E0 // CHECK:STDOUT: inst7800012B: symbolic_constant780000E1 // CHECK:STDOUT: inst7800012C: concrete_constant(inst7800012C) // CHECK:STDOUT: inst7800012D: concrete_constant(inst7800012D) // CHECK:STDOUT: inst7800012E: concrete_constant(inst7800012E) // CHECK:STDOUT: inst7800012F: concrete_constant(inst7800012F) // CHECK:STDOUT: inst78000130: symbolic_constant780000E3 // CHECK:STDOUT: inst78000131: symbolic_constant780000F0 // CHECK:STDOUT: inst78000132: symbolic_constant780000EF // CHECK:STDOUT: inst78000133: symbolic_constant780000EE // CHECK:STDOUT: inst78000134: symbolic_constant780000ED // CHECK:STDOUT: inst78000135: symbolic_constant780000EC // CHECK:STDOUT: inst78000136: symbolic_constant780000F1 // CHECK:STDOUT: inst78000137: symbolic_constant780000F2 // CHECK:STDOUT: inst78000138: symbolic_constant780000F3 // CHECK:STDOUT: inst78000139: symbolic_constant780000F4 // CHECK:STDOUT: inst7800013A: symbolic_constant780000F5 // CHECK:STDOUT: inst7800013B: symbolic_constant780000F6 // CHECK:STDOUT: inst7800013C: symbolic_constant780000F7 // CHECK:STDOUT: inst7800013D: symbolic_constant780000F8 // CHECK:STDOUT: inst7800013E: symbolic_constant780000F9 // CHECK:STDOUT: inst7800013F: symbolic_constant780000FA // CHECK:STDOUT: inst78000140: symbolic_constant780000FB // CHECK:STDOUT: inst78000141: symbolic_constant780000FC // CHECK:STDOUT: inst78000142: symbolic_constant780000FD // CHECK:STDOUT: inst78000143: symbolic_constant780000FE // CHECK:STDOUT: inst78000144: symbolic_constant780000FF // CHECK:STDOUT: inst78000145: symbolic_constant78000101 // CHECK:STDOUT: inst78000146: symbolic_constant78000102 // CHECK:STDOUT: inst78000147: symbolic_constant78000107 // CHECK:STDOUT: inst78000148: symbolic_constant78000115 // CHECK:STDOUT: inst78000149: symbolic_constant78000117 // CHECK:STDOUT: inst7800014A: symbolic_constant78000118 // CHECK:STDOUT: inst7800014B: symbolic_constant78000119 // CHECK:STDOUT: inst7800014C: symbolic_constant7800011A // CHECK:STDOUT: inst7800014D: symbolic_constant7800011B // CHECK:STDOUT: inst7800014E: symbolic_constant7800011C // CHECK:STDOUT: inst7800014F: symbolic_constant7800011D // CHECK:STDOUT: inst78000150: symbolic_constant7800011E // CHECK:STDOUT: inst78000151: symbolic_constant7800011F // CHECK:STDOUT: inst78000152: symbolic_constant78000120 // CHECK:STDOUT: inst78000153: symbolic_constant78000121 // CHECK:STDOUT: inst78000154: symbolic_constant78000122 // CHECK:STDOUT: inst78000155: symbolic_constant78000123 // CHECK:STDOUT: inst78000156: symbolic_constant78000124 // CHECK:STDOUT: inst78000157: symbolic_constant78000125 // CHECK:STDOUT: inst78000158: symbolic_constant78000126 // CHECK:STDOUT: inst78000159: symbolic_constant78000127 // CHECK:STDOUT: inst7800015A: symbolic_constant78000128 // CHECK:STDOUT: inst7800015B: symbolic_constant78000129 // CHECK:STDOUT: inst7800015C: concrete_constant(inst7800015C) // CHECK:STDOUT: inst7800015D: concrete_constant(inst7800015D) // CHECK:STDOUT: inst7800015E: concrete_constant(inst7800015E) // CHECK:STDOUT: inst7800015F: symbolic_constant780000D9 // CHECK:STDOUT: inst78000160: concrete_constant(inst78000050) // CHECK:STDOUT: inst78000161: symbolic_constant780000EE // CHECK:STDOUT: inst78000162: symbolic_constant780000ED // CHECK:STDOUT: inst78000163: symbolic_constant780000EC // CHECK:STDOUT: inst78000164: symbolic_constant78000132 // CHECK:STDOUT: inst78000165: symbolic_constant78000133 // CHECK:STDOUT: inst78000166: symbolic_constant78000134 // CHECK:STDOUT: inst78000167: symbolic_constant78000135 // CHECK:STDOUT: inst78000168: symbolic_constant78000136 // CHECK:STDOUT: inst78000169: symbolic_constant78000137 // CHECK:STDOUT: inst7800016A: symbolic_constant78000138 // CHECK:STDOUT: inst7800016B: symbolic_constant78000139 // CHECK:STDOUT: inst7800016C: symbolic_constant7800013A // CHECK:STDOUT: inst7800016D: symbolic_constant7800013C // CHECK:STDOUT: inst7800016E: symbolic_constant7800013D // CHECK:STDOUT: inst7800016F: symbolic_constant78000140 // CHECK:STDOUT: inst78000170: symbolic_constant7800013F // CHECK:STDOUT: inst78000171: symbolic_constant78000140 // CHECK:STDOUT: inst78000172: symbolic_constant78000141 // CHECK:STDOUT: inst78000173: symbolic_constant78000142 // CHECK:STDOUT: inst78000174: symbolic_constant78000143 // CHECK:STDOUT: inst78000175: symbolic_constant78000144 // CHECK:STDOUT: inst78000176: symbolic_constant78000145 // CHECK:STDOUT: inst78000177: symbolic_constant78000146 // CHECK:STDOUT: inst78000178: symbolic_constant7800014B // CHECK:STDOUT: inst78000179: symbolic_constant78000147 // CHECK:STDOUT: inst7800017A: symbolic_constant78000148 // CHECK:STDOUT: inst7800017B: symbolic_constant78000149 // CHECK:STDOUT: inst7800017C: symbolic_constant7800014A // CHECK:STDOUT: inst7800017D: symbolic_constant7800014B // CHECK:STDOUT: inst7800017F: symbolic_constant7800014D // CHECK:STDOUT: inst78000180: symbolic_constant7800014C // CHECK:STDOUT: inst78000181: symbolic_constant7800014D // CHECK:STDOUT: inst78000186: concrete_constant(inst78000028) // CHECK:STDOUT: inst78000187: concrete_constant(inst78000028) // CHECK:STDOUT: symbolic_constants: // CHECK:STDOUT: symbolic_constant78000000: {inst: inst78000014, kind: self, attached: null} // CHECK:STDOUT: symbolic_constant78000001: {inst: inst78000018, kind: checked, attached: null} // CHECK:STDOUT: symbolic_constant78000002: {inst: inst78000018, kind: checked, attached: {generic: generic78000000, index: generic_inst_in_decl0}} // CHECK:STDOUT: symbolic_constant78000003: {inst: inst7800001D, kind: checked, attached: null} // CHECK:STDOUT: symbolic_constant78000004: {inst: inst7800001D, kind: checked, attached: {generic: generic78000000, index: generic_inst_in_decl1}} // CHECK:STDOUT: symbolic_constant78000005: {inst: inst7800001F, kind: checked, attached: null} // CHECK:STDOUT: symbolic_constant78000006: {inst: inst7800001F, kind: checked, attached: {generic: generic78000000, index: generic_inst_in_decl2}} // CHECK:STDOUT: symbolic_constant78000007: {inst: inst7800002B, kind: checked, attached: null} // CHECK:STDOUT: symbolic_constant78000008: {inst: inst7800002B, kind: checked, attached: {generic: generic78000000, index: generic_inst_in_decl3}} // CHECK:STDOUT: symbolic_constant78000009: {inst: inst7800002F, kind: checked, attached: null} // CHECK:STDOUT: symbolic_constant7800000A: {inst: inst7800002F, kind: checked, attached: {generic: generic78000000, index: generic_inst_in_decl4}} // CHECK:STDOUT: symbolic_constant7800000B: {inst: inst78000033, kind: checked, attached: null} // CHECK:STDOUT: symbolic_constant7800000C: {inst: inst78000033, kind: checked, attached: {generic: generic78000000, index: generic_inst_in_decl5}} // CHECK:STDOUT: symbolic_constant7800000D: {inst: inst78000035, kind: checked, attached: null} // CHECK:STDOUT: symbolic_constant7800000E: {inst: inst78000035, kind: checked, attached: {generic: generic78000000, index: generic_inst_in_decl6}} // CHECK:STDOUT: symbolic_constant7800000F: {inst: inst78000042, kind: checked, attached: null} // CHECK:STDOUT: symbolic_constant78000010: {inst: inst78000042, kind: checked, attached: {generic: generic78000000, index: generic_inst_in_def0}} // CHECK:STDOUT: symbolic_constant78000011: {inst: inst78000044, kind: checked, attached: null} // CHECK:STDOUT: symbolic_constant78000012: {inst: inst78000046, kind: checked, attached: null} // CHECK:STDOUT: symbolic_constant78000013: {inst: inst78000046, kind: checked, attached: {generic: generic78000000, index: generic_inst_in_def1}} // CHECK:STDOUT: symbolic_constant78000014: {inst: inst78000052, kind: checked, attached: null} // CHECK:STDOUT: symbolic_constant78000015: {inst: inst78000057, kind: checked, attached: null} // CHECK:STDOUT: symbolic_constant78000016: {inst: inst78000058, kind: checked, attached: null} // CHECK:STDOUT: symbolic_constant78000017: {inst: inst78000058, kind: checked, attached: {generic: generic78000001, index: generic_inst_in_def2}} // CHECK:STDOUT: symbolic_constant78000018: {inst: inst78000059, kind: checked, attached: null} // CHECK:STDOUT: symbolic_constant78000019: {inst: inst78000057, kind: checked, attached: {generic: generic78000001, index: generic_inst_in_def1}} // CHECK:STDOUT: symbolic_constant7800001A: {inst: inst7800005A, kind: checked, attached: null} // CHECK:STDOUT: symbolic_constant7800001B: {inst: inst7800005A, kind: checked, attached: {generic: generic78000002, index: generic_inst_in_decl2}} // CHECK:STDOUT: symbolic_constant7800001C: {inst: inst7800005F, kind: checked, attached: null} // CHECK:STDOUT: symbolic_constant7800001D: {inst: inst7800005F, kind: checked, attached: {generic: generic78000002, index: generic_inst_in_decl3}} // CHECK:STDOUT: symbolic_constant7800001E: {inst: inst78000059, kind: checked, attached: {generic: generic78000002, index: generic_inst_in_decl1}} // CHECK:STDOUT: symbolic_constant7800001F: {inst: inst78000052, kind: checked, attached: {generic: generic78000002, index: generic_inst_in_decl0}} // CHECK:STDOUT: symbolic_constant78000020: {inst: inst7800005F, kind: checked, attached: {generic: generic78000002, index: generic_inst_in_decl3}} // CHECK:STDOUT: symbolic_constant78000021: {inst: inst78000059, kind: checked, attached: {generic: generic78000002, index: generic_inst_in_decl1}} // CHECK:STDOUT: symbolic_constant78000022: {inst: inst78000052, kind: checked, attached: {generic: generic78000002, index: generic_inst_in_decl0}} // CHECK:STDOUT: symbolic_constant78000023: {inst: inst78000059, kind: checked, attached: {generic: generic78000002, index: generic_inst_in_decl1}} // CHECK:STDOUT: symbolic_constant78000024: {inst: inst7800005A, kind: checked, attached: {generic: generic78000002, index: generic_inst_in_decl2}} // CHECK:STDOUT: symbolic_constant78000025: {inst: inst7800005F, kind: checked, attached: {generic: generic78000002, index: generic_inst_in_decl3}} // CHECK:STDOUT: symbolic_constant78000026: {inst: inst78000058, kind: checked, attached: {generic: generic78000001, index: generic_inst_in_def2}} // CHECK:STDOUT: symbolic_constant78000027: {inst: inst78000052, kind: checked, attached: {generic: generic78000001, index: generic_inst_in_def0}} // CHECK:STDOUT: symbolic_constant78000028: {inst: inst78000052, kind: checked, attached: {generic: generic78000001, index: generic_inst_in_def0}} // CHECK:STDOUT: symbolic_constant78000029: {inst: inst78000057, kind: checked, attached: {generic: generic78000001, index: generic_inst_in_def1}} // CHECK:STDOUT: symbolic_constant7800002A: {inst: inst78000058, kind: checked, attached: {generic: generic78000001, index: generic_inst_in_def2}} // CHECK:STDOUT: symbolic_constant7800002B: {inst: inst78000071, kind: checked, attached: null} // CHECK:STDOUT: symbolic_constant7800002C: {inst: inst78000072, kind: checked, attached: null} // CHECK:STDOUT: symbolic_constant7800002D: {inst: inst78000073, kind: checked, attached: null} // CHECK:STDOUT: symbolic_constant7800002E: {inst: inst78000073, kind: checked, attached: {generic: generic78000003, index: generic_inst_in_decl2}} // CHECK:STDOUT: symbolic_constant7800002F: {inst: inst78000076, kind: checked, attached: null} // CHECK:STDOUT: symbolic_constant78000030: {inst: inst78000076, kind: checked, attached: {generic: generic78000003, index: generic_inst_in_decl3}} // CHECK:STDOUT: symbolic_constant78000031: {inst: inst78000078, kind: checked, attached: null} // CHECK:STDOUT: symbolic_constant78000032: {inst: inst78000079, kind: checked, attached: null} // CHECK:STDOUT: symbolic_constant78000033: {inst: inst78000079, kind: checked, attached: {generic: generic78000003, index: generic_inst_in_def1}} // CHECK:STDOUT: symbolic_constant78000034: {inst: inst78000078, kind: checked, attached: {generic: generic78000003, index: generic_inst_in_def0}} // CHECK:STDOUT: symbolic_constant78000035: {inst: inst7800007A, kind: checked, attached: null} // CHECK:STDOUT: symbolic_constant78000036: {inst: inst7800007A, kind: checked, attached: {generic: generic78000004, index: generic_inst_in_decl3}} // CHECK:STDOUT: symbolic_constant78000037: {inst: inst7800007F, kind: checked, attached: null} // CHECK:STDOUT: symbolic_constant78000038: {inst: inst7800007F, kind: checked, attached: {generic: generic78000004, index: generic_inst_in_decl4}} // CHECK:STDOUT: symbolic_constant78000039: {inst: inst78000073, kind: checked, attached: {generic: generic78000004, index: generic_inst_in_decl2}} // CHECK:STDOUT: symbolic_constant7800003A: {inst: inst78000072, kind: checked, attached: {generic: generic78000004, index: generic_inst_in_decl1}} // CHECK:STDOUT: symbolic_constant7800003B: {inst: inst78000071, kind: checked, attached: {generic: generic78000004, index: generic_inst_in_decl0}} // CHECK:STDOUT: symbolic_constant7800003C: {inst: inst78000071, kind: checked, attached: {generic: generic78000003, index: generic_inst_in_decl0}} // CHECK:STDOUT: symbolic_constant7800003D: {inst: inst7800007F, kind: checked, attached: {generic: generic78000004, index: generic_inst_in_decl4}} // CHECK:STDOUT: symbolic_constant7800003E: {inst: inst78000073, kind: checked, attached: {generic: generic78000004, index: generic_inst_in_decl2}} // CHECK:STDOUT: symbolic_constant7800003F: {inst: inst78000071, kind: checked, attached: {generic: generic78000004, index: generic_inst_in_decl0}} // CHECK:STDOUT: symbolic_constant78000040: {inst: inst78000072, kind: checked, attached: {generic: generic78000004, index: generic_inst_in_decl1}} // CHECK:STDOUT: symbolic_constant78000041: {inst: inst78000073, kind: checked, attached: {generic: generic78000004, index: generic_inst_in_decl2}} // CHECK:STDOUT: symbolic_constant78000042: {inst: inst7800007A, kind: checked, attached: {generic: generic78000004, index: generic_inst_in_decl3}} // CHECK:STDOUT: symbolic_constant78000043: {inst: inst7800007F, kind: checked, attached: {generic: generic78000004, index: generic_inst_in_decl4}} // CHECK:STDOUT: symbolic_constant78000044: {inst: inst78000088, kind: checked, attached: null} // CHECK:STDOUT: symbolic_constant78000045: {inst: inst78000089, kind: checked, attached: null} // CHECK:STDOUT: symbolic_constant78000046: {inst: inst7800008A, kind: checked, attached: null} // CHECK:STDOUT: symbolic_constant78000047: {inst: inst7800008B, kind: checked, attached: null} // CHECK:STDOUT: symbolic_constant78000048: {inst: inst7800008C, kind: checked, attached: null} // CHECK:STDOUT: symbolic_constant78000049: {inst: inst7800008D, kind: checked, attached: null} // CHECK:STDOUT: symbolic_constant7800004A: {inst: inst7800008D, kind: checked, attached: {generic: generic78000004, index: generic_inst_in_def6}} // CHECK:STDOUT: symbolic_constant7800004B: {inst: inst7800008E, kind: checked, attached: null} // CHECK:STDOUT: symbolic_constant7800004C: {inst: inst7800008F, kind: checked, attached: null} // CHECK:STDOUT: symbolic_constant7800004D: {inst: inst7800008C, kind: checked, attached: {generic: generic78000004, index: generic_inst_in_def5}} // CHECK:STDOUT: symbolic_constant7800004E: {inst: inst7800008B, kind: checked, attached: {generic: generic78000004, index: generic_inst_in_def4}} // CHECK:STDOUT: symbolic_constant7800004F: {inst: inst78000089, kind: checked, attached: {generic: generic78000004, index: generic_inst_in_def3}} // CHECK:STDOUT: symbolic_constant78000050: {inst: inst78000088, kind: checked, attached: {generic: generic78000004, index: generic_inst_in_def2}} // CHECK:STDOUT: symbolic_constant78000051: {inst: inst78000090, kind: checked, attached: null} // CHECK:STDOUT: symbolic_constant78000052: {inst: inst78000090, kind: checked, attached: {generic: generic78000004, index: generic_inst_in_def1}} // CHECK:STDOUT: symbolic_constant78000053: {inst: inst78000091, kind: checked, attached: null} // CHECK:STDOUT: symbolic_constant78000054: {inst: inst78000091, kind: checked, attached: {generic: generic78000004, index: generic_inst_in_def0}} // CHECK:STDOUT: symbolic_constant78000055: {inst: inst78000091, kind: checked, attached: {generic: generic78000004, index: generic_inst_in_def0}} // CHECK:STDOUT: symbolic_constant78000056: {inst: inst78000090, kind: checked, attached: {generic: generic78000004, index: generic_inst_in_def1}} // CHECK:STDOUT: symbolic_constant78000057: {inst: inst78000088, kind: checked, attached: {generic: generic78000004, index: generic_inst_in_def2}} // CHECK:STDOUT: symbolic_constant78000058: {inst: inst78000089, kind: checked, attached: {generic: generic78000004, index: generic_inst_in_def3}} // CHECK:STDOUT: symbolic_constant78000059: {inst: inst7800008B, kind: checked, attached: {generic: generic78000004, index: generic_inst_in_def4}} // CHECK:STDOUT: symbolic_constant7800005A: {inst: inst7800008C, kind: checked, attached: {generic: generic78000004, index: generic_inst_in_def5}} // CHECK:STDOUT: symbolic_constant7800005B: {inst: inst7800008D, kind: checked, attached: {generic: generic78000004, index: generic_inst_in_def6}} // CHECK:STDOUT: symbolic_constant7800005C: {inst: inst78000073, kind: checked, attached: {generic: generic78000003, index: generic_inst_in_decl2}} // CHECK:STDOUT: symbolic_constant7800005D: {inst: inst78000072, kind: checked, attached: {generic: generic78000003, index: generic_inst_in_decl1}} // CHECK:STDOUT: symbolic_constant7800005E: {inst: inst78000071, kind: checked, attached: {generic: generic78000003, index: generic_inst_in_decl0}} // CHECK:STDOUT: symbolic_constant7800005F: {inst: inst78000071, kind: checked, attached: {generic: generic78000003, index: generic_inst_in_decl0}} // CHECK:STDOUT: symbolic_constant78000060: {inst: inst78000072, kind: checked, attached: {generic: generic78000003, index: generic_inst_in_decl1}} // CHECK:STDOUT: symbolic_constant78000061: {inst: inst78000073, kind: checked, attached: {generic: generic78000003, index: generic_inst_in_decl2}} // CHECK:STDOUT: symbolic_constant78000062: {inst: inst78000076, kind: checked, attached: {generic: generic78000003, index: generic_inst_in_decl3}} // CHECK:STDOUT: symbolic_constant78000063: {inst: inst78000079, kind: checked, attached: {generic: generic78000003, index: generic_inst_in_def1}} // CHECK:STDOUT: symbolic_constant78000064: {inst: inst78000078, kind: checked, attached: {generic: generic78000003, index: generic_inst_in_def0}} // CHECK:STDOUT: symbolic_constant78000065: {inst: inst78000079, kind: checked, attached: {generic: generic78000003, index: generic_inst_in_def1}} // CHECK:STDOUT: symbolic_constant78000066: {inst: inst7800001D, kind: checked, attached: {generic: generic78000005, index: generic_inst_in_decl1}} // CHECK:STDOUT: symbolic_constant78000067: {inst: inst780000B8, kind: checked, attached: null} // CHECK:STDOUT: symbolic_constant78000068: {inst: inst780000B8, kind: checked, attached: {generic: generic78000005, index: generic_inst_in_decl2}} // CHECK:STDOUT: symbolic_constant78000069: {inst: inst7800001D, kind: checked, attached: {generic: generic78000005, index: generic_inst_in_decl1}} // CHECK:STDOUT: symbolic_constant7800006A: {inst: inst78000018, kind: checked, attached: {generic: generic78000005, index: generic_inst_in_decl0}} // CHECK:STDOUT: symbolic_constant7800006B: {inst: inst78000018, kind: checked, attached: {generic: generic78000005, index: generic_inst_in_decl0}} // CHECK:STDOUT: symbolic_constant7800006C: {inst: inst78000018, kind: checked, attached: {generic: generic78000005, index: generic_inst_in_decl0}} // CHECK:STDOUT: symbolic_constant7800006D: {inst: inst7800001D, kind: checked, attached: {generic: generic78000005, index: generic_inst_in_decl1}} // CHECK:STDOUT: symbolic_constant7800006E: {inst: inst780000B8, kind: checked, attached: {generic: generic78000005, index: generic_inst_in_decl2}} // CHECK:STDOUT: symbolic_constant7800006F: {inst: inst78000042, kind: checked, attached: {generic: generic78000005, index: generic_inst_in_def2}} // CHECK:STDOUT: symbolic_constant78000070: {inst: inst780000C1, kind: checked, attached: null} // CHECK:STDOUT: symbolic_constant78000071: {inst: inst780000C2, kind: checked, attached: null} // CHECK:STDOUT: symbolic_constant78000072: {inst: inst780000C2, kind: checked, attached: {generic: generic78000005, index: generic_inst_in_def1}} // CHECK:STDOUT: symbolic_constant78000073: {inst: inst780000C1, kind: checked, attached: {generic: generic78000005, index: generic_inst_in_def0}} // CHECK:STDOUT: symbolic_constant78000074: {inst: inst7800001F, kind: checked, attached: {generic: generic78000006, index: generic_inst_in_decl2}} // CHECK:STDOUT: symbolic_constant78000075: {inst: inst780000C7, kind: checked, attached: null} // CHECK:STDOUT: symbolic_constant78000076: {inst: inst780000C7, kind: checked, attached: {generic: generic78000006, index: generic_inst_in_decl3}} // CHECK:STDOUT: symbolic_constant78000077: {inst: inst7800001D, kind: checked, attached: {generic: generic78000006, index: generic_inst_in_decl1}} // CHECK:STDOUT: symbolic_constant78000078: {inst: inst78000018, kind: checked, attached: {generic: generic78000006, index: generic_inst_in_decl0}} // CHECK:STDOUT: symbolic_constant78000079: {inst: inst780000C7, kind: checked, attached: {generic: generic78000006, index: generic_inst_in_decl3}} // CHECK:STDOUT: symbolic_constant7800007A: {inst: inst7800001D, kind: checked, attached: {generic: generic78000006, index: generic_inst_in_decl1}} // CHECK:STDOUT: symbolic_constant7800007B: {inst: inst78000018, kind: checked, attached: {generic: generic78000006, index: generic_inst_in_decl0}} // CHECK:STDOUT: symbolic_constant7800007C: {inst: inst7800001D, kind: checked, attached: {generic: generic78000006, index: generic_inst_in_decl1}} // CHECK:STDOUT: symbolic_constant7800007D: {inst: inst7800001F, kind: checked, attached: {generic: generic78000006, index: generic_inst_in_decl2}} // CHECK:STDOUT: symbolic_constant7800007E: {inst: inst780000C7, kind: checked, attached: {generic: generic78000006, index: generic_inst_in_decl3}} // CHECK:STDOUT: symbolic_constant7800007F: {inst: inst780000C2, kind: checked, attached: {generic: generic78000005, index: generic_inst_in_def1}} // CHECK:STDOUT: symbolic_constant78000080: {inst: inst780000C1, kind: checked, attached: {generic: generic78000005, index: generic_inst_in_def0}} // CHECK:STDOUT: symbolic_constant78000081: {inst: inst780000C2, kind: checked, attached: {generic: generic78000005, index: generic_inst_in_def1}} // CHECK:STDOUT: symbolic_constant78000082: {inst: inst78000042, kind: checked, attached: {generic: generic78000005, index: generic_inst_in_def2}} // CHECK:STDOUT: symbolic_constant78000083: {inst: inst780000DC, kind: checked, attached: null} // CHECK:STDOUT: symbolic_constant78000084: {inst: inst780000DD, kind: checked, attached: null} // CHECK:STDOUT: symbolic_constant78000085: {inst: inst780000DE, kind: checked, attached: null} // CHECK:STDOUT: symbolic_constant78000086: {inst: inst780000DE, kind: checked, attached: {generic: generic78000007, index: generic_inst_in_decl5}} // CHECK:STDOUT: symbolic_constant78000087: {inst: inst780000E1, kind: checked, attached: null} // CHECK:STDOUT: symbolic_constant78000088: {inst: inst780000E1, kind: checked, attached: {generic: generic78000007, index: generic_inst_in_decl6}} // CHECK:STDOUT: symbolic_constant78000089: {inst: inst780000E3, kind: checked, attached: null} // CHECK:STDOUT: symbolic_constant7800008A: {inst: inst780000E4, kind: checked, attached: null} // CHECK:STDOUT: symbolic_constant7800008B: {inst: inst780000E4, kind: checked, attached: {generic: generic78000007, index: generic_inst_in_def1}} // CHECK:STDOUT: symbolic_constant7800008C: {inst: inst780000E3, kind: checked, attached: {generic: generic78000007, index: generic_inst_in_def0}} // CHECK:STDOUT: symbolic_constant7800008D: {inst: inst780000E6, kind: checked, attached: null} // CHECK:STDOUT: symbolic_constant7800008E: {inst: inst780000E7, kind: checked, attached: null} // CHECK:STDOUT: symbolic_constant7800008F: {inst: inst780000E7, kind: checked, attached: {generic: generic78000008, index: generic_inst_in_decl5}} // CHECK:STDOUT: symbolic_constant78000090: {inst: inst780000EC, kind: checked, attached: null} // CHECK:STDOUT: symbolic_constant78000091: {inst: inst780000EC, kind: checked, attached: {generic: generic78000008, index: generic_inst_in_decl6}} // CHECK:STDOUT: symbolic_constant78000092: {inst: inst780000DE, kind: checked, attached: {generic: generic78000008, index: generic_inst_in_decl4}} // CHECK:STDOUT: symbolic_constant78000093: {inst: inst780000DD, kind: checked, attached: {generic: generic78000008, index: generic_inst_in_decl3}} // CHECK:STDOUT: symbolic_constant78000094: {inst: inst780000DC, kind: checked, attached: {generic: generic78000008, index: generic_inst_in_decl2}} // CHECK:STDOUT: symbolic_constant78000095: {inst: inst78000072, kind: checked, attached: {generic: generic78000008, index: generic_inst_in_decl1}} // CHECK:STDOUT: symbolic_constant78000096: {inst: inst78000071, kind: checked, attached: {generic: generic78000008, index: generic_inst_in_decl0}} // CHECK:STDOUT: symbolic_constant78000097: {inst: inst780000DC, kind: checked, attached: {generic: generic78000007, index: generic_inst_in_decl1}} // CHECK:STDOUT: symbolic_constant78000098: {inst: inst78000071, kind: checked, attached: {generic: generic78000007, index: generic_inst_in_decl0}} // CHECK:STDOUT: symbolic_constant78000099: {inst: inst780000EC, kind: checked, attached: {generic: generic78000008, index: generic_inst_in_decl6}} // CHECK:STDOUT: symbolic_constant7800009A: {inst: inst780000DE, kind: checked, attached: {generic: generic78000008, index: generic_inst_in_decl4}} // CHECK:STDOUT: symbolic_constant7800009B: {inst: inst78000071, kind: checked, attached: {generic: generic78000008, index: generic_inst_in_decl0}} // CHECK:STDOUT: symbolic_constant7800009C: {inst: inst78000072, kind: checked, attached: {generic: generic78000008, index: generic_inst_in_decl1}} // CHECK:STDOUT: symbolic_constant7800009D: {inst: inst780000DC, kind: checked, attached: {generic: generic78000008, index: generic_inst_in_decl2}} // CHECK:STDOUT: symbolic_constant7800009E: {inst: inst780000DD, kind: checked, attached: {generic: generic78000008, index: generic_inst_in_decl3}} // CHECK:STDOUT: symbolic_constant7800009F: {inst: inst780000DE, kind: checked, attached: {generic: generic78000008, index: generic_inst_in_decl4}} // CHECK:STDOUT: symbolic_constant780000A0: {inst: inst780000E7, kind: checked, attached: {generic: generic78000008, index: generic_inst_in_decl5}} // CHECK:STDOUT: symbolic_constant780000A1: {inst: inst780000EC, kind: checked, attached: {generic: generic78000008, index: generic_inst_in_decl6}} // CHECK:STDOUT: symbolic_constant780000A2: {inst: inst780000F8, kind: checked, attached: null} // CHECK:STDOUT: symbolic_constant780000A3: {inst: inst780000F9, kind: checked, attached: null} // CHECK:STDOUT: symbolic_constant780000A4: {inst: inst780000FA, kind: checked, attached: null} // CHECK:STDOUT: symbolic_constant780000A5: {inst: inst780000FB, kind: checked, attached: null} // CHECK:STDOUT: symbolic_constant780000A6: {inst: inst780000FC, kind: checked, attached: null} // CHECK:STDOUT: symbolic_constant780000A7: {inst: inst780000FD, kind: checked, attached: null} // CHECK:STDOUT: symbolic_constant780000A8: {inst: inst780000FD, kind: checked, attached: {generic: generic78000008, index: generic_inst_in_def12}} // CHECK:STDOUT: symbolic_constant780000A9: {inst: inst780000FE, kind: checked, attached: null} // CHECK:STDOUT: symbolic_constant780000AA: {inst: inst780000FF, kind: checked, attached: null} // CHECK:STDOUT: symbolic_constant780000AB: {inst: inst780000FC, kind: checked, attached: {generic: generic78000008, index: generic_inst_in_def11}} // CHECK:STDOUT: symbolic_constant780000AC: {inst: inst780000FB, kind: checked, attached: {generic: generic78000008, index: generic_inst_in_def10}} // CHECK:STDOUT: symbolic_constant780000AD: {inst: inst780000F9, kind: checked, attached: {generic: generic78000008, index: generic_inst_in_def9}} // CHECK:STDOUT: symbolic_constant780000AE: {inst: inst780000F8, kind: checked, attached: {generic: generic78000008, index: generic_inst_in_def8}} // CHECK:STDOUT: symbolic_constant780000AF: {inst: inst78000100, kind: checked, attached: null} // CHECK:STDOUT: symbolic_constant780000B0: {inst: inst78000100, kind: checked, attached: {generic: generic78000008, index: generic_inst_in_def7}} // CHECK:STDOUT: symbolic_constant780000B1: {inst: inst7800008D, kind: checked, attached: {generic: generic78000008, index: generic_inst_in_def6}} // CHECK:STDOUT: symbolic_constant780000B2: {inst: inst7800008C, kind: checked, attached: {generic: generic78000008, index: generic_inst_in_def5}} // CHECK:STDOUT: symbolic_constant780000B3: {inst: inst7800008B, kind: checked, attached: {generic: generic78000008, index: generic_inst_in_def4}} // CHECK:STDOUT: symbolic_constant780000B4: {inst: inst78000089, kind: checked, attached: {generic: generic78000008, index: generic_inst_in_def3}} // CHECK:STDOUT: symbolic_constant780000B5: {inst: inst78000088, kind: checked, attached: {generic: generic78000008, index: generic_inst_in_def2}} // CHECK:STDOUT: symbolic_constant780000B6: {inst: inst78000090, kind: checked, attached: {generic: generic78000008, index: generic_inst_in_def1}} // CHECK:STDOUT: symbolic_constant780000B7: {inst: inst78000101, kind: checked, attached: null} // CHECK:STDOUT: symbolic_constant780000B8: {inst: inst78000101, kind: checked, attached: {generic: generic78000008, index: generic_inst_in_def0}} // CHECK:STDOUT: symbolic_constant780000B9: {inst: inst78000101, kind: checked, attached: {generic: generic78000008, index: generic_inst_in_def0}} // CHECK:STDOUT: symbolic_constant780000BA: {inst: inst78000090, kind: checked, attached: {generic: generic78000008, index: generic_inst_in_def1}} // CHECK:STDOUT: symbolic_constant780000BB: {inst: inst78000088, kind: checked, attached: {generic: generic78000008, index: generic_inst_in_def2}} // CHECK:STDOUT: symbolic_constant780000BC: {inst: inst78000089, kind: checked, attached: {generic: generic78000008, index: generic_inst_in_def3}} // CHECK:STDOUT: symbolic_constant780000BD: {inst: inst7800008B, kind: checked, attached: {generic: generic78000008, index: generic_inst_in_def4}} // CHECK:STDOUT: symbolic_constant780000BE: {inst: inst7800008C, kind: checked, attached: {generic: generic78000008, index: generic_inst_in_def5}} // CHECK:STDOUT: symbolic_constant780000BF: {inst: inst7800008D, kind: checked, attached: {generic: generic78000008, index: generic_inst_in_def6}} // CHECK:STDOUT: symbolic_constant780000C0: {inst: inst78000100, kind: checked, attached: {generic: generic78000008, index: generic_inst_in_def7}} // CHECK:STDOUT: symbolic_constant780000C1: {inst: inst780000F8, kind: checked, attached: {generic: generic78000008, index: generic_inst_in_def8}} // CHECK:STDOUT: symbolic_constant780000C2: {inst: inst780000F9, kind: checked, attached: {generic: generic78000008, index: generic_inst_in_def9}} // CHECK:STDOUT: symbolic_constant780000C3: {inst: inst780000FB, kind: checked, attached: {generic: generic78000008, index: generic_inst_in_def10}} // CHECK:STDOUT: symbolic_constant780000C4: {inst: inst780000FC, kind: checked, attached: {generic: generic78000008, index: generic_inst_in_def11}} // CHECK:STDOUT: symbolic_constant780000C5: {inst: inst780000FD, kind: checked, attached: {generic: generic78000008, index: generic_inst_in_def12}} // CHECK:STDOUT: symbolic_constant780000C6: {inst: inst780000DE, kind: checked, attached: {generic: generic78000007, index: generic_inst_in_decl5}} // CHECK:STDOUT: symbolic_constant780000C7: {inst: inst780000DD, kind: checked, attached: {generic: generic78000007, index: generic_inst_in_decl4}} // CHECK:STDOUT: symbolic_constant780000C8: {inst: inst78000072, kind: checked, attached: {generic: generic78000007, index: generic_inst_in_decl3}} // CHECK:STDOUT: symbolic_constant780000C9: {inst: inst780000E6, kind: checked, attached: {generic: generic78000007, index: generic_inst_in_decl2}} // CHECK:STDOUT: symbolic_constant780000CA: {inst: inst780000DC, kind: checked, attached: {generic: generic78000007, index: generic_inst_in_decl1}} // CHECK:STDOUT: symbolic_constant780000CB: {inst: inst78000071, kind: checked, attached: {generic: generic78000007, index: generic_inst_in_decl0}} // CHECK:STDOUT: symbolic_constant780000CC: {inst: inst78000071, kind: checked, attached: {generic: generic78000007, index: generic_inst_in_decl0}} // CHECK:STDOUT: symbolic_constant780000CD: {inst: inst780000DC, kind: checked, attached: {generic: generic78000007, index: generic_inst_in_decl1}} // CHECK:STDOUT: symbolic_constant780000CE: {inst: inst780000E6, kind: checked, attached: {generic: generic78000007, index: generic_inst_in_decl2}} // CHECK:STDOUT: symbolic_constant780000CF: {inst: inst78000072, kind: checked, attached: {generic: generic78000007, index: generic_inst_in_decl3}} // CHECK:STDOUT: symbolic_constant780000D0: {inst: inst780000DD, kind: checked, attached: {generic: generic78000007, index: generic_inst_in_decl4}} // CHECK:STDOUT: symbolic_constant780000D1: {inst: inst780000DE, kind: checked, attached: {generic: generic78000007, index: generic_inst_in_decl5}} // CHECK:STDOUT: symbolic_constant780000D2: {inst: inst780000E1, kind: checked, attached: {generic: generic78000007, index: generic_inst_in_decl6}} // CHECK:STDOUT: symbolic_constant780000D3: {inst: inst780000E4, kind: checked, attached: {generic: generic78000007, index: generic_inst_in_def1}} // CHECK:STDOUT: symbolic_constant780000D4: {inst: inst780000E3, kind: checked, attached: {generic: generic78000007, index: generic_inst_in_def0}} // CHECK:STDOUT: symbolic_constant780000D5: {inst: inst780000E4, kind: checked, attached: {generic: generic78000007, index: generic_inst_in_def1}} // CHECK:STDOUT: symbolic_constant780000D6: {inst: inst78000120, kind: checked, attached: null} // CHECK:STDOUT: symbolic_constant780000D7: {inst: inst78000121, kind: checked, attached: null} // CHECK:STDOUT: symbolic_constant780000D8: {inst: inst78000122, kind: checked, attached: null} // CHECK:STDOUT: symbolic_constant780000D9: {inst: inst78000122, kind: checked, attached: {generic: generic78000009, index: generic_inst_in_decl7}} // CHECK:STDOUT: symbolic_constant780000DA: {inst: inst78000125, kind: checked, attached: null} // CHECK:STDOUT: symbolic_constant780000DB: {inst: inst78000125, kind: checked, attached: {generic: generic78000009, index: generic_inst_in_decl8}} // CHECK:STDOUT: symbolic_constant780000DC: {inst: inst78000127, kind: checked, attached: null} // CHECK:STDOUT: symbolic_constant780000DD: {inst: inst78000128, kind: checked, attached: null} // CHECK:STDOUT: symbolic_constant780000DE: {inst: inst78000128, kind: checked, attached: {generic: generic78000009, index: generic_inst_in_def1}} // CHECK:STDOUT: symbolic_constant780000DF: {inst: inst78000127, kind: checked, attached: {generic: generic78000009, index: generic_inst_in_def0}} // CHECK:STDOUT: symbolic_constant780000E0: {inst: inst7800012A, kind: checked, attached: null} // CHECK:STDOUT: symbolic_constant780000E1: {inst: inst7800012B, kind: checked, attached: null} // CHECK:STDOUT: symbolic_constant780000E2: {inst: inst7800012B, kind: checked, attached: {generic: generic7800000A, index: generic_inst_in_decl7}} // CHECK:STDOUT: symbolic_constant780000E3: {inst: inst78000130, kind: checked, attached: null} // CHECK:STDOUT: symbolic_constant780000E4: {inst: inst78000130, kind: checked, attached: {generic: generic7800000A, index: generic_inst_in_decl8}} // CHECK:STDOUT: symbolic_constant780000E5: {inst: inst78000122, kind: checked, attached: {generic: generic7800000A, index: generic_inst_in_decl6}} // CHECK:STDOUT: symbolic_constant780000E6: {inst: inst78000121, kind: checked, attached: {generic: generic7800000A, index: generic_inst_in_decl5}} // CHECK:STDOUT: symbolic_constant780000E7: {inst: inst78000120, kind: checked, attached: {generic: generic7800000A, index: generic_inst_in_decl4}} // CHECK:STDOUT: symbolic_constant780000E8: {inst: inst780000DD, kind: checked, attached: {generic: generic7800000A, index: generic_inst_in_decl3}} // CHECK:STDOUT: symbolic_constant780000E9: {inst: inst780000DC, kind: checked, attached: {generic: generic7800000A, index: generic_inst_in_decl2}} // CHECK:STDOUT: symbolic_constant780000EA: {inst: inst78000072, kind: checked, attached: {generic: generic7800000A, index: generic_inst_in_decl1}} // CHECK:STDOUT: symbolic_constant780000EB: {inst: inst78000071, kind: checked, attached: {generic: generic7800000A, index: generic_inst_in_decl0}} // CHECK:STDOUT: symbolic_constant780000EC: {inst: inst78000120, kind: checked, attached: {generic: generic78000009, index: generic_inst_in_decl2}} // CHECK:STDOUT: symbolic_constant780000ED: {inst: inst780000DC, kind: checked, attached: {generic: generic78000009, index: generic_inst_in_decl1}} // CHECK:STDOUT: symbolic_constant780000EE: {inst: inst78000071, kind: checked, attached: {generic: generic78000009, index: generic_inst_in_decl0}} // CHECK:STDOUT: symbolic_constant780000EF: {inst: inst78000130, kind: checked, attached: {generic: generic7800000A, index: generic_inst_in_decl8}} // CHECK:STDOUT: symbolic_constant780000F0: {inst: inst78000122, kind: checked, attached: {generic: generic7800000A, index: generic_inst_in_decl6}} // CHECK:STDOUT: symbolic_constant780000F1: {inst: inst78000071, kind: checked, attached: {generic: generic7800000A, index: generic_inst_in_decl0}} // CHECK:STDOUT: symbolic_constant780000F2: {inst: inst78000072, kind: checked, attached: {generic: generic7800000A, index: generic_inst_in_decl1}} // CHECK:STDOUT: symbolic_constant780000F3: {inst: inst780000DC, kind: checked, attached: {generic: generic7800000A, index: generic_inst_in_decl2}} // CHECK:STDOUT: symbolic_constant780000F4: {inst: inst780000DD, kind: checked, attached: {generic: generic7800000A, index: generic_inst_in_decl3}} // CHECK:STDOUT: symbolic_constant780000F5: {inst: inst78000120, kind: checked, attached: {generic: generic7800000A, index: generic_inst_in_decl4}} // CHECK:STDOUT: symbolic_constant780000F6: {inst: inst78000121, kind: checked, attached: {generic: generic7800000A, index: generic_inst_in_decl5}} // CHECK:STDOUT: symbolic_constant780000F7: {inst: inst78000122, kind: checked, attached: {generic: generic7800000A, index: generic_inst_in_decl6}} // CHECK:STDOUT: symbolic_constant780000F8: {inst: inst7800012B, kind: checked, attached: {generic: generic7800000A, index: generic_inst_in_decl7}} // CHECK:STDOUT: symbolic_constant780000F9: {inst: inst78000130, kind: checked, attached: {generic: generic7800000A, index: generic_inst_in_decl8}} // CHECK:STDOUT: symbolic_constant780000FA: {inst: inst7800013F, kind: checked, attached: null} // CHECK:STDOUT: symbolic_constant780000FB: {inst: inst78000140, kind: checked, attached: null} // CHECK:STDOUT: symbolic_constant780000FC: {inst: inst78000141, kind: checked, attached: null} // CHECK:STDOUT: symbolic_constant780000FD: {inst: inst78000142, kind: checked, attached: null} // CHECK:STDOUT: symbolic_constant780000FE: {inst: inst78000143, kind: checked, attached: null} // CHECK:STDOUT: symbolic_constant780000FF: {inst: inst78000144, kind: checked, attached: null} // CHECK:STDOUT: symbolic_constant78000100: {inst: inst78000144, kind: checked, attached: {generic: generic7800000A, index: generic_inst_in_def18}} // CHECK:STDOUT: symbolic_constant78000101: {inst: inst78000145, kind: checked, attached: null} // CHECK:STDOUT: symbolic_constant78000102: {inst: inst78000146, kind: checked, attached: null} // CHECK:STDOUT: symbolic_constant78000103: {inst: inst78000143, kind: checked, attached: {generic: generic7800000A, index: generic_inst_in_def17}} // CHECK:STDOUT: symbolic_constant78000104: {inst: inst78000142, kind: checked, attached: {generic: generic7800000A, index: generic_inst_in_def16}} // CHECK:STDOUT: symbolic_constant78000105: {inst: inst78000140, kind: checked, attached: {generic: generic7800000A, index: generic_inst_in_def15}} // CHECK:STDOUT: symbolic_constant78000106: {inst: inst7800013F, kind: checked, attached: {generic: generic7800000A, index: generic_inst_in_def14}} // CHECK:STDOUT: symbolic_constant78000107: {inst: inst78000147, kind: checked, attached: null} // CHECK:STDOUT: symbolic_constant78000108: {inst: inst78000147, kind: checked, attached: {generic: generic7800000A, index: generic_inst_in_def13}} // CHECK:STDOUT: symbolic_constant78000109: {inst: inst780000FD, kind: checked, attached: {generic: generic7800000A, index: generic_inst_in_def12}} // CHECK:STDOUT: symbolic_constant7800010A: {inst: inst780000FC, kind: checked, attached: {generic: generic7800000A, index: generic_inst_in_def11}} // CHECK:STDOUT: symbolic_constant7800010B: {inst: inst780000FB, kind: checked, attached: {generic: generic7800000A, index: generic_inst_in_def10}} // CHECK:STDOUT: symbolic_constant7800010C: {inst: inst780000F9, kind: checked, attached: {generic: generic7800000A, index: generic_inst_in_def9}} // CHECK:STDOUT: symbolic_constant7800010D: {inst: inst780000F8, kind: checked, attached: {generic: generic7800000A, index: generic_inst_in_def8}} // CHECK:STDOUT: symbolic_constant7800010E: {inst: inst78000100, kind: checked, attached: {generic: generic7800000A, index: generic_inst_in_def7}} // CHECK:STDOUT: symbolic_constant7800010F: {inst: inst7800008D, kind: checked, attached: {generic: generic7800000A, index: generic_inst_in_def6}} // CHECK:STDOUT: symbolic_constant78000110: {inst: inst7800008C, kind: checked, attached: {generic: generic7800000A, index: generic_inst_in_def5}} // CHECK:STDOUT: symbolic_constant78000111: {inst: inst7800008B, kind: checked, attached: {generic: generic7800000A, index: generic_inst_in_def4}} // CHECK:STDOUT: symbolic_constant78000112: {inst: inst78000089, kind: checked, attached: {generic: generic7800000A, index: generic_inst_in_def3}} // CHECK:STDOUT: symbolic_constant78000113: {inst: inst78000088, kind: checked, attached: {generic: generic7800000A, index: generic_inst_in_def2}} // CHECK:STDOUT: symbolic_constant78000114: {inst: inst78000090, kind: checked, attached: {generic: generic7800000A, index: generic_inst_in_def1}} // CHECK:STDOUT: symbolic_constant78000115: {inst: inst78000148, kind: checked, attached: null} // CHECK:STDOUT: symbolic_constant78000116: {inst: inst78000148, kind: checked, attached: {generic: generic7800000A, index: generic_inst_in_def0}} // CHECK:STDOUT: symbolic_constant78000117: {inst: inst78000148, kind: checked, attached: {generic: generic7800000A, index: generic_inst_in_def0}} // CHECK:STDOUT: symbolic_constant78000118: {inst: inst78000090, kind: checked, attached: {generic: generic7800000A, index: generic_inst_in_def1}} // CHECK:STDOUT: symbolic_constant78000119: {inst: inst78000088, kind: checked, attached: {generic: generic7800000A, index: generic_inst_in_def2}} // CHECK:STDOUT: symbolic_constant7800011A: {inst: inst78000089, kind: checked, attached: {generic: generic7800000A, index: generic_inst_in_def3}} // CHECK:STDOUT: symbolic_constant7800011B: {inst: inst7800008B, kind: checked, attached: {generic: generic7800000A, index: generic_inst_in_def4}} // CHECK:STDOUT: symbolic_constant7800011C: {inst: inst7800008C, kind: checked, attached: {generic: generic7800000A, index: generic_inst_in_def5}} // CHECK:STDOUT: symbolic_constant7800011D: {inst: inst7800008D, kind: checked, attached: {generic: generic7800000A, index: generic_inst_in_def6}} // CHECK:STDOUT: symbolic_constant7800011E: {inst: inst78000100, kind: checked, attached: {generic: generic7800000A, index: generic_inst_in_def7}} // CHECK:STDOUT: symbolic_constant7800011F: {inst: inst780000F8, kind: checked, attached: {generic: generic7800000A, index: generic_inst_in_def8}} // CHECK:STDOUT: symbolic_constant78000120: {inst: inst780000F9, kind: checked, attached: {generic: generic7800000A, index: generic_inst_in_def9}} // CHECK:STDOUT: symbolic_constant78000121: {inst: inst780000FB, kind: checked, attached: {generic: generic7800000A, index: generic_inst_in_def10}} // CHECK:STDOUT: symbolic_constant78000122: {inst: inst780000FC, kind: checked, attached: {generic: generic7800000A, index: generic_inst_in_def11}} // CHECK:STDOUT: symbolic_constant78000123: {inst: inst780000FD, kind: checked, attached: {generic: generic7800000A, index: generic_inst_in_def12}} // CHECK:STDOUT: symbolic_constant78000124: {inst: inst78000147, kind: checked, attached: {generic: generic7800000A, index: generic_inst_in_def13}} // CHECK:STDOUT: symbolic_constant78000125: {inst: inst7800013F, kind: checked, attached: {generic: generic7800000A, index: generic_inst_in_def14}} // CHECK:STDOUT: symbolic_constant78000126: {inst: inst78000140, kind: checked, attached: {generic: generic7800000A, index: generic_inst_in_def15}} // CHECK:STDOUT: symbolic_constant78000127: {inst: inst78000142, kind: checked, attached: {generic: generic7800000A, index: generic_inst_in_def16}} // CHECK:STDOUT: symbolic_constant78000128: {inst: inst78000143, kind: checked, attached: {generic: generic7800000A, index: generic_inst_in_def17}} // CHECK:STDOUT: symbolic_constant78000129: {inst: inst78000144, kind: checked, attached: {generic: generic7800000A, index: generic_inst_in_def18}} // CHECK:STDOUT: symbolic_constant7800012A: {inst: inst78000122, kind: checked, attached: {generic: generic78000009, index: generic_inst_in_decl7}} // CHECK:STDOUT: symbolic_constant7800012B: {inst: inst78000121, kind: checked, attached: {generic: generic78000009, index: generic_inst_in_decl6}} // CHECK:STDOUT: symbolic_constant7800012C: {inst: inst780000DD, kind: checked, attached: {generic: generic78000009, index: generic_inst_in_decl5}} // CHECK:STDOUT: symbolic_constant7800012D: {inst: inst78000072, kind: checked, attached: {generic: generic78000009, index: generic_inst_in_decl4}} // CHECK:STDOUT: symbolic_constant7800012E: {inst: inst7800012A, kind: checked, attached: {generic: generic78000009, index: generic_inst_in_decl3}} // CHECK:STDOUT: symbolic_constant7800012F: {inst: inst78000120, kind: checked, attached: {generic: generic78000009, index: generic_inst_in_decl2}} // CHECK:STDOUT: symbolic_constant78000130: {inst: inst780000DC, kind: checked, attached: {generic: generic78000009, index: generic_inst_in_decl1}} // CHECK:STDOUT: symbolic_constant78000131: {inst: inst78000071, kind: checked, attached: {generic: generic78000009, index: generic_inst_in_decl0}} // CHECK:STDOUT: symbolic_constant78000132: {inst: inst78000071, kind: checked, attached: {generic: generic78000009, index: generic_inst_in_decl0}} // CHECK:STDOUT: symbolic_constant78000133: {inst: inst780000DC, kind: checked, attached: {generic: generic78000009, index: generic_inst_in_decl1}} // CHECK:STDOUT: symbolic_constant78000134: {inst: inst78000120, kind: checked, attached: {generic: generic78000009, index: generic_inst_in_decl2}} // CHECK:STDOUT: symbolic_constant78000135: {inst: inst7800012A, kind: checked, attached: {generic: generic78000009, index: generic_inst_in_decl3}} // CHECK:STDOUT: symbolic_constant78000136: {inst: inst78000072, kind: checked, attached: {generic: generic78000009, index: generic_inst_in_decl4}} // CHECK:STDOUT: symbolic_constant78000137: {inst: inst780000DD, kind: checked, attached: {generic: generic78000009, index: generic_inst_in_decl5}} // CHECK:STDOUT: symbolic_constant78000138: {inst: inst78000121, kind: checked, attached: {generic: generic78000009, index: generic_inst_in_decl6}} // CHECK:STDOUT: symbolic_constant78000139: {inst: inst78000122, kind: checked, attached: {generic: generic78000009, index: generic_inst_in_decl7}} // CHECK:STDOUT: symbolic_constant7800013A: {inst: inst78000125, kind: checked, attached: {generic: generic78000009, index: generic_inst_in_decl8}} // CHECK:STDOUT: symbolic_constant7800013B: {inst: inst78000128, kind: checked, attached: {generic: generic78000009, index: generic_inst_in_def1}} // CHECK:STDOUT: symbolic_constant7800013C: {inst: inst78000127, kind: checked, attached: {generic: generic78000009, index: generic_inst_in_def0}} // CHECK:STDOUT: symbolic_constant7800013D: {inst: inst78000128, kind: checked, attached: {generic: generic78000009, index: generic_inst_in_def1}} // CHECK:STDOUT: symbolic_constant7800013E: {inst: inst780000B8, kind: checked, attached: {generic: generic78000005, index: generic_inst_in_decl2}} // CHECK:STDOUT: symbolic_constant7800013F: {inst: inst78000170, kind: checked, attached: null} // CHECK:STDOUT: symbolic_constant78000140: {inst: inst78000170, kind: checked, attached: {generic: generic78000000, index: generic_inst_in_def2}} // CHECK:STDOUT: symbolic_constant78000141: {inst: inst78000172, kind: checked, attached: null} // CHECK:STDOUT: symbolic_constant78000142: {inst: inst78000172, kind: checked, attached: {generic: generic78000000, index: generic_inst_in_def3}} // CHECK:STDOUT: symbolic_constant78000143: {inst: inst78000174, kind: checked, attached: null} // CHECK:STDOUT: symbolic_constant78000144: {inst: inst78000175, kind: checked, attached: null} // CHECK:STDOUT: symbolic_constant78000145: {inst: inst78000176, kind: checked, attached: null} // CHECK:STDOUT: symbolic_constant78000146: {inst: inst78000177, kind: checked, attached: null} // CHECK:STDOUT: symbolic_constant78000147: {inst: inst78000179, kind: checked, attached: null} // CHECK:STDOUT: symbolic_constant78000148: {inst: inst78000174, kind: checked, attached: {generic: generic78000000, index: generic_inst_in_def4}} // CHECK:STDOUT: symbolic_constant78000149: {inst: inst78000175, kind: checked, attached: {generic: generic78000000, index: generic_inst_in_def5}} // CHECK:STDOUT: symbolic_constant7800014A: {inst: inst78000177, kind: checked, attached: {generic: generic78000000, index: generic_inst_in_def6}} // CHECK:STDOUT: symbolic_constant7800014B: {inst: inst78000179, kind: checked, attached: {generic: generic78000000, index: generic_inst_in_def7}} // CHECK:STDOUT: symbolic_constant7800014C: {inst: inst78000180, kind: checked, attached: null} // CHECK:STDOUT: symbolic_constant7800014D: {inst: inst78000180, kind: checked, attached: {generic: generic78000000, index: generic_inst_in_def8}} // CHECK:STDOUT: inst_blocks: // CHECK:STDOUT: inst_block_empty: {} // CHECK:STDOUT: exports: // CHECK:STDOUT: 0: inst7800003E // CHECK:STDOUT: generated: {} // CHECK:STDOUT: imports: // CHECK:STDOUT: 0: inst78000011 // CHECK:STDOUT: 1: inst7800004E // CHECK:STDOUT: 2: inst7800004F // CHECK:STDOUT: 3: inst78000051 // CHECK:STDOUT: 4: inst78000053 // CHECK:STDOUT: 5: inst78000054 // CHECK:STDOUT: 6: inst78000055 // CHECK:STDOUT: 7: inst78000056 // CHECK:STDOUT: 8: inst7800005B // CHECK:STDOUT: 9: inst7800005C // CHECK:STDOUT: 10: inst7800005D // CHECK:STDOUT: 11: inst7800005E // CHECK:STDOUT: 12: inst78000060 // CHECK:STDOUT: 13: inst78000061 // CHECK:STDOUT: 14: inst78000062 // CHECK:STDOUT: 15: inst7800006A // CHECK:STDOUT: 16: inst7800006C // CHECK:STDOUT: 17: inst7800006F // CHECK:STDOUT: 18: inst78000070 // CHECK:STDOUT: 19: inst78000074 // CHECK:STDOUT: 20: inst78000075 // CHECK:STDOUT: 21: inst78000077 // CHECK:STDOUT: 22: inst7800007B // CHECK:STDOUT: 23: inst7800007C // CHECK:STDOUT: 24: inst7800007D // CHECK:STDOUT: 25: inst7800007E // CHECK:STDOUT: 26: inst78000080 // CHECK:STDOUT: 27: inst78000081 // CHECK:STDOUT: 28: inst78000082 // CHECK:STDOUT: 29: inst7800009A // CHECK:STDOUT: 30: inst7800009B // CHECK:STDOUT: 31: inst7800009C // CHECK:STDOUT: 32: inst7800009D // CHECK:STDOUT: 33: inst780000A4 // CHECK:STDOUT: 34: inst780000A5 // CHECK:STDOUT: 35: inst780000A6 // CHECK:STDOUT: 36: inst780000A7 // CHECK:STDOUT: 37: inst780000A8 // CHECK:STDOUT: 38: inst780000A9 // CHECK:STDOUT: 39: inst780000AA // CHECK:STDOUT: 40: inst780000AB // CHECK:STDOUT: 41: inst780000AC // CHECK:STDOUT: 42: inst780000AD // CHECK:STDOUT: 43: inst780000AE // CHECK:STDOUT: 44: inst780000AF // CHECK:STDOUT: 45: inst780000B0 // CHECK:STDOUT: 46: inst780000B1 // CHECK:STDOUT: 47: inst780000B2 // CHECK:STDOUT: 48: inst780000B3 // CHECK:STDOUT: 49: inst780000B4 // CHECK:STDOUT: 50: inst780000B5 // CHECK:STDOUT: 51: inst780000B6 // CHECK:STDOUT: 52: inst780000B7 // CHECK:STDOUT: 53: inst780000B9 // CHECK:STDOUT: 54: inst780000BA // CHECK:STDOUT: 55: inst780000BB // CHECK:STDOUT: 56: inst780000BC // CHECK:STDOUT: 57: inst780000C0 // CHECK:STDOUT: 58: inst780000C3 // CHECK:STDOUT: 59: inst780000C4 // CHECK:STDOUT: 60: inst780000C5 // CHECK:STDOUT: 61: inst780000C6 // CHECK:STDOUT: 62: inst780000C8 // CHECK:STDOUT: 63: inst780000C9 // CHECK:STDOUT: 64: inst780000CA // CHECK:STDOUT: 65: inst780000D2 // CHECK:STDOUT: 66: inst780000D3 // CHECK:STDOUT: 67: inst780000D4 // CHECK:STDOUT: 68: inst780000D5 // CHECK:STDOUT: 69: inst780000D6 // CHECK:STDOUT: 70: inst780000D7 // CHECK:STDOUT: 71: inst780000D8 // CHECK:STDOUT: 72: inst780000D9 // CHECK:STDOUT: 73: inst780000DA // CHECK:STDOUT: 74: inst780000DB // CHECK:STDOUT: 75: inst780000DF // CHECK:STDOUT: 76: inst780000E0 // CHECK:STDOUT: 77: inst780000E2 // CHECK:STDOUT: 78: inst780000E8 // CHECK:STDOUT: 79: inst780000E9 // CHECK:STDOUT: 80: inst780000EA // CHECK:STDOUT: 81: inst780000EB // CHECK:STDOUT: 82: inst780000ED // CHECK:STDOUT: 83: inst780000EE // CHECK:STDOUT: 84: inst780000EF // CHECK:STDOUT: 85: inst780000F0 // CHECK:STDOUT: 86: inst7800010F // CHECK:STDOUT: 87: inst78000110 // CHECK:STDOUT: 88: inst78000111 // CHECK:STDOUT: 89: inst78000112 // CHECK:STDOUT: 90: inst78000113 // CHECK:STDOUT: 91: inst78000114 // CHECK:STDOUT: 92: inst7800011E // CHECK:STDOUT: 93: inst7800011F // CHECK:STDOUT: 94: inst78000123 // CHECK:STDOUT: 95: inst78000124 // CHECK:STDOUT: 96: inst78000126 // CHECK:STDOUT: 97: inst7800012C // CHECK:STDOUT: 98: inst7800012D // CHECK:STDOUT: 99: inst7800012E // CHECK:STDOUT: 100: inst7800012F // CHECK:STDOUT: 101: inst78000131 // CHECK:STDOUT: 102: inst78000132 // CHECK:STDOUT: 103: inst78000133 // CHECK:STDOUT: 104: inst78000134 // CHECK:STDOUT: 105: inst78000135 // CHECK:STDOUT: 106: inst7800015C // CHECK:STDOUT: 107: inst7800015D // CHECK:STDOUT: 108: inst7800015E // CHECK:STDOUT: 109: inst7800015F // CHECK:STDOUT: 110: inst78000160 // CHECK:STDOUT: 111: inst78000161 // CHECK:STDOUT: 112: inst78000162 // CHECK:STDOUT: 113: inst78000163 // CHECK:STDOUT: global_init: {} // CHECK:STDOUT: inst_block78000005: // CHECK:STDOUT: 0: inst78000013 // CHECK:STDOUT: 1: inst78000015 // CHECK:STDOUT: inst_block78000006: // CHECK:STDOUT: 0: inst7800001A // CHECK:STDOUT: inst_block78000007: // CHECK:STDOUT: 0: inst7800001B // CHECK:STDOUT: 1: inst7800001C // CHECK:STDOUT: inst_block78000008: // CHECK:STDOUT: 0: inst78000023 // CHECK:STDOUT: inst_block78000009: // CHECK:STDOUT: 0: inst78000025 // CHECK:STDOUT: 1: inst78000027 // CHECK:STDOUT: inst_block7800000A: // CHECK:STDOUT: 0: inst(TypeType) // CHECK:STDOUT: 1: inst78000026 // CHECK:STDOUT: inst_block7800000B: // CHECK:STDOUT: 0: inst7800001D // CHECK:STDOUT: 1: inst78000028 // CHECK:STDOUT: inst_block7800000C: // CHECK:STDOUT: 0: inst7800001E // CHECK:STDOUT: 1: inst78000028 // CHECK:STDOUT: inst_block7800000D: // CHECK:STDOUT: 0: inst7800001D // CHECK:STDOUT: 1: inst7800002E // CHECK:STDOUT: inst_block7800000E: // CHECK:STDOUT: 0: inst7800001D // CHECK:STDOUT: 1: inst78000026 // CHECK:STDOUT: inst_block7800000F: // CHECK:STDOUT: 0: inst7800001E // CHECK:STDOUT: 1: inst78000026 // CHECK:STDOUT: inst_block78000010: // CHECK:STDOUT: 0: inst78000038 // CHECK:STDOUT: inst_block78000011: // CHECK:STDOUT: 0: inst78000023 // CHECK:STDOUT: 1: inst78000038 // CHECK:STDOUT: inst_block78000012: // CHECK:STDOUT: 0: inst7800003A // CHECK:STDOUT: 1: inst7800003C // CHECK:STDOUT: inst_block78000013: // CHECK:STDOUT: 0: inst7800001A // CHECK:STDOUT: 1: inst78000021 // CHECK:STDOUT: 2: inst78000023 // CHECK:STDOUT: 3: inst78000036 // CHECK:STDOUT: 4: inst78000038 // CHECK:STDOUT: inst_block78000014: // CHECK:STDOUT: 0: inst78000024 // CHECK:STDOUT: 1: inst78000025 // CHECK:STDOUT: 2: inst78000027 // CHECK:STDOUT: 3: inst7800002A // CHECK:STDOUT: 4: inst7800002E // CHECK:STDOUT: 5: inst78000030 // CHECK:STDOUT: 6: inst78000032 // CHECK:STDOUT: 7: inst78000039 // CHECK:STDOUT: 8: inst78000017 // CHECK:STDOUT: 9: inst7800003A // CHECK:STDOUT: 10: inst7800003B // CHECK:STDOUT: 11: inst78000020 // CHECK:STDOUT: 12: inst7800003C // CHECK:STDOUT: 13: inst7800003D // CHECK:STDOUT: inst_block78000015: // CHECK:STDOUT: 0: inst78000017 // CHECK:STDOUT: inst_block78000016: // CHECK:STDOUT: 0: inst78000018 // CHECK:STDOUT: inst_block78000017: // CHECK:STDOUT: 0: inst78000019 // CHECK:STDOUT: 1: inst7800001E // CHECK:STDOUT: 2: inst78000022 // CHECK:STDOUT: 3: inst7800002C // CHECK:STDOUT: 4: inst78000031 // CHECK:STDOUT: 5: inst78000034 // CHECK:STDOUT: 6: inst78000037 // CHECK:STDOUT: inst_block78000018: // CHECK:STDOUT: 0: inst78000018 // CHECK:STDOUT: 1: inst7800001D // CHECK:STDOUT: 2: inst7800001F // CHECK:STDOUT: 3: inst7800002B // CHECK:STDOUT: 4: inst7800002F // CHECK:STDOUT: 5: inst78000033 // CHECK:STDOUT: 6: inst78000035 // CHECK:STDOUT: inst_block78000019: // CHECK:STDOUT: 0: inst78000048 // CHECK:STDOUT: 1: inst78000049 // CHECK:STDOUT: 2: inst7800004A // CHECK:STDOUT: 3: inst78000178 // CHECK:STDOUT: 4: inst7800017E // CHECK:STDOUT: 5: inst7800017F // CHECK:STDOUT: 6: inst78000182 // CHECK:STDOUT: 7: inst78000183 // CHECK:STDOUT: 8: inst7800004C // CHECK:STDOUT: 9: inst78000184 // CHECK:STDOUT: 10: inst78000185 // CHECK:STDOUT: 11: inst78000186 // CHECK:STDOUT: 12: inst78000187 // CHECK:STDOUT: 13: inst78000188 // CHECK:STDOUT: 14: inst78000189 // CHECK:STDOUT: 15: inst7800018A // CHECK:STDOUT: inst_block7800001A: // CHECK:STDOUT: 0: inst78000048 // CHECK:STDOUT: 1: inst78000049 // CHECK:STDOUT: inst_block7800001B: // CHECK:STDOUT: 0: inst78000054 // CHECK:STDOUT: inst_block7800001C: // CHECK:STDOUT: 0: inst78000055 // CHECK:STDOUT: inst_block7800001D: // CHECK:STDOUT: 0: inst78000052 // CHECK:STDOUT: inst_block7800001E: // CHECK:STDOUT: 0: inst78000052 // CHECK:STDOUT: 1: inst78000057 // CHECK:STDOUT: 2: inst78000058 // CHECK:STDOUT: inst_block7800001F: // CHECK:STDOUT: 0: inst7800005E // CHECK:STDOUT: 1: inst7800005C // CHECK:STDOUT: inst_block78000020: // CHECK:STDOUT: 0: inst7800005E // CHECK:STDOUT: inst_block78000021: // CHECK:STDOUT: 0: inst7800005C // CHECK:STDOUT: inst_block78000022: // CHECK:STDOUT: 0: inst78000062 // CHECK:STDOUT: inst_block78000023: // CHECK:STDOUT: 0: inst78000063 // CHECK:STDOUT: 1: inst78000064 // CHECK:STDOUT: 2: inst78000065 // CHECK:STDOUT: 3: inst78000066 // CHECK:STDOUT: inst_block78000024: // CHECK:STDOUT: 0: inst78000052 // CHECK:STDOUT: 1: inst78000059 // CHECK:STDOUT: 2: inst7800005A // CHECK:STDOUT: 3: inst7800005F // CHECK:STDOUT: inst_block78000025: // CHECK:STDOUT: 0: inst78000067 // CHECK:STDOUT: inst_block78000026: // CHECK:STDOUT: 0: inst78000067 // CHECK:STDOUT: 1: inst78000068 // CHECK:STDOUT: 2: inst78000069 // CHECK:STDOUT: inst_block78000027: // CHECK:STDOUT: 0: inst78000074 // CHECK:STDOUT: inst_block78000028: // CHECK:STDOUT: 0: inst78000071 // CHECK:STDOUT: inst_block78000029: // CHECK:STDOUT: 0: inst78000071 // CHECK:STDOUT: 1: inst78000072 // CHECK:STDOUT: 2: inst78000073 // CHECK:STDOUT: 3: inst78000076 // CHECK:STDOUT: inst_block7800002A: // CHECK:STDOUT: 0: inst78000078 // CHECK:STDOUT: 1: inst78000079 // CHECK:STDOUT: inst_block7800002B: // CHECK:STDOUT: 0: inst7800007E // CHECK:STDOUT: 1: inst7800007C // CHECK:STDOUT: inst_block7800002C: // CHECK:STDOUT: 0: inst7800007E // CHECK:STDOUT: inst_block7800002D: // CHECK:STDOUT: 0: inst7800007C // CHECK:STDOUT: inst_block7800002E: // CHECK:STDOUT: 0: inst78000082 // CHECK:STDOUT: inst_block7800002F: // CHECK:STDOUT: 0: inst78000083 // CHECK:STDOUT: 1: inst78000084 // CHECK:STDOUT: 2: inst78000085 // CHECK:STDOUT: 3: inst78000086 // CHECK:STDOUT: 4: inst78000087 // CHECK:STDOUT: inst_block78000030: // CHECK:STDOUT: 0: inst78000071 // CHECK:STDOUT: 1: inst78000089 // CHECK:STDOUT: 2: inst7800008A // CHECK:STDOUT: inst_block78000031: // CHECK:STDOUT: 0: inst78000071 // CHECK:STDOUT: 1: inst78000072 // CHECK:STDOUT: 2: inst7800008F // CHECK:STDOUT: 3: inst7800008E // CHECK:STDOUT: inst_block78000032: // CHECK:STDOUT: 0: inst78000083 // CHECK:STDOUT: inst_block78000033: // CHECK:STDOUT: 0: inst78000083 // CHECK:STDOUT: inst_block78000034: // CHECK:STDOUT: 0: inst78000092 // CHECK:STDOUT: 1: inst78000093 // CHECK:STDOUT: 2: inst78000094 // CHECK:STDOUT: 3: inst78000095 // CHECK:STDOUT: 4: inst78000096 // CHECK:STDOUT: 5: inst78000097 // CHECK:STDOUT: 6: inst78000098 // CHECK:STDOUT: inst_block78000035: // CHECK:STDOUT: 0: inst78000071 // CHECK:STDOUT: 1: inst78000072 // CHECK:STDOUT: 2: inst78000073 // CHECK:STDOUT: 3: inst7800007A // CHECK:STDOUT: 4: inst7800007F // CHECK:STDOUT: inst_block78000036: // CHECK:STDOUT: 0: inst7800009A // CHECK:STDOUT: inst_block78000037: // CHECK:STDOUT: 0: inst7800009D // CHECK:STDOUT: inst_block78000038: // CHECK:STDOUT: 0: inst7800009E // CHECK:STDOUT: inst_block78000039: // CHECK:STDOUT: 0: inst7800009E // CHECK:STDOUT: 1: inst7800009F // CHECK:STDOUT: 2: inst780000A0 // CHECK:STDOUT: 3: inst780000A1 // CHECK:STDOUT: inst_block7800003A: // CHECK:STDOUT: 0: inst7800009E // CHECK:STDOUT: inst_block7800003B: // CHECK:STDOUT: 0: inst780000A2 // CHECK:STDOUT: 1: inst780000A3 // CHECK:STDOUT: inst_block7800003C: // CHECK:STDOUT: 0: inst780000B6 // CHECK:STDOUT: inst_block7800003D: // CHECK:STDOUT: 0: inst78000018 // CHECK:STDOUT: 1: inst7800001D // CHECK:STDOUT: 2: inst780000B8 // CHECK:STDOUT: inst_block7800003E: // CHECK:STDOUT: 0: inst780000B9 // CHECK:STDOUT: inst_block7800003F: // CHECK:STDOUT: 0: inst780000BC // CHECK:STDOUT: inst_block78000040: // CHECK:STDOUT: 0: inst780000BD // CHECK:STDOUT: inst_block78000041: // CHECK:STDOUT: 0: inst780000BD // CHECK:STDOUT: 1: inst780000BE // CHECK:STDOUT: 2: inst780000BF // CHECK:STDOUT: inst_block78000042: // CHECK:STDOUT: 0: inst780000C6 // CHECK:STDOUT: 1: inst780000C4 // CHECK:STDOUT: inst_block78000043: // CHECK:STDOUT: 0: inst780000C6 // CHECK:STDOUT: inst_block78000044: // CHECK:STDOUT: 0: inst780000C4 // CHECK:STDOUT: inst_block78000045: // CHECK:STDOUT: 0: inst780000CA // CHECK:STDOUT: inst_block78000046: // CHECK:STDOUT: 0: inst780000CB // CHECK:STDOUT: 1: inst780000CC // CHECK:STDOUT: 2: inst780000CD // CHECK:STDOUT: 3: inst780000CE // CHECK:STDOUT: inst_block78000047: // CHECK:STDOUT: 0: inst78000018 // CHECK:STDOUT: 1: inst7800001D // CHECK:STDOUT: 2: inst7800001F // CHECK:STDOUT: 3: inst780000C7 // CHECK:STDOUT: inst_block78000048: // CHECK:STDOUT: 0: inst780000BD // CHECK:STDOUT: inst_block78000049: // CHECK:STDOUT: 0: inst780000CF // CHECK:STDOUT: 1: inst780000D0 // CHECK:STDOUT: 2: inst780000D1 // CHECK:STDOUT: inst_block7800004A: // CHECK:STDOUT: 0: inst78000072 // CHECK:STDOUT: 1: inst780000DD // CHECK:STDOUT: inst_block7800004B: // CHECK:STDOUT: 0: inst780000DF // CHECK:STDOUT: inst_block7800004C: // CHECK:STDOUT: 0: inst78000071 // CHECK:STDOUT: 1: inst780000DC // CHECK:STDOUT: inst_block7800004D: // CHECK:STDOUT: 0: inst78000050 // CHECK:STDOUT: 1: inst78000050 // CHECK:STDOUT: inst_block7800004E: // CHECK:STDOUT: 0: inst78000071 // CHECK:STDOUT: 1: inst780000DC // CHECK:STDOUT: 2: inst780000E6 // CHECK:STDOUT: 3: inst78000072 // CHECK:STDOUT: 4: inst780000DD // CHECK:STDOUT: 5: inst780000DE // CHECK:STDOUT: 6: inst780000E1 // CHECK:STDOUT: inst_block7800004F: // CHECK:STDOUT: 0: inst780000E3 // CHECK:STDOUT: 1: inst780000E4 // CHECK:STDOUT: inst_block78000050: // CHECK:STDOUT: 0: inst780000EB // CHECK:STDOUT: 1: inst780000E9 // CHECK:STDOUT: inst_block78000051: // CHECK:STDOUT: 0: inst780000EB // CHECK:STDOUT: inst_block78000052: // CHECK:STDOUT: 0: inst780000E9 // CHECK:STDOUT: inst_block78000053: // CHECK:STDOUT: 0: inst780000EF // CHECK:STDOUT: 1: inst780000F0 // CHECK:STDOUT: inst_block78000054: // CHECK:STDOUT: 0: inst780000F2 // CHECK:STDOUT: 1: inst780000F4 // CHECK:STDOUT: inst_block78000055: // CHECK:STDOUT: 0: inst780000F1 // CHECK:STDOUT: 1: inst780000F2 // CHECK:STDOUT: 2: inst780000F3 // CHECK:STDOUT: 3: inst780000F4 // CHECK:STDOUT: 4: inst780000F5 // CHECK:STDOUT: 5: inst780000F6 // CHECK:STDOUT: 6: inst780000F7 // CHECK:STDOUT: inst_block78000056: // CHECK:STDOUT: 0: inst780000DC // CHECK:STDOUT: inst_block78000057: // CHECK:STDOUT: 0: inst780000DC // CHECK:STDOUT: 1: inst780000F9 // CHECK:STDOUT: 2: inst780000FA // CHECK:STDOUT: inst_block78000058: // CHECK:STDOUT: 0: inst780000DC // CHECK:STDOUT: 1: inst780000DD // CHECK:STDOUT: 2: inst780000FF // CHECK:STDOUT: 3: inst780000FE // CHECK:STDOUT: inst_block78000059: // CHECK:STDOUT: 0: inst780000F1 // CHECK:STDOUT: inst_block7800005A: // CHECK:STDOUT: 0: inst780000F1 // CHECK:STDOUT: inst_block7800005B: // CHECK:STDOUT: 0: inst780000F3 // CHECK:STDOUT: inst_block7800005C: // CHECK:STDOUT: 0: inst780000F3 // CHECK:STDOUT: inst_block7800005D: // CHECK:STDOUT: 0: inst78000102 // CHECK:STDOUT: 1: inst78000103 // CHECK:STDOUT: 2: inst78000104 // CHECK:STDOUT: 3: inst78000105 // CHECK:STDOUT: 4: inst78000106 // CHECK:STDOUT: 5: inst78000107 // CHECK:STDOUT: 6: inst78000108 // CHECK:STDOUT: 7: inst78000109 // CHECK:STDOUT: 8: inst7800010A // CHECK:STDOUT: 9: inst7800010B // CHECK:STDOUT: 10: inst7800010C // CHECK:STDOUT: 11: inst7800010D // CHECK:STDOUT: 12: inst7800010E // CHECK:STDOUT: inst_block7800005E: // CHECK:STDOUT: 0: inst78000071 // CHECK:STDOUT: 1: inst78000072 // CHECK:STDOUT: 2: inst780000DC // CHECK:STDOUT: 3: inst780000DD // CHECK:STDOUT: 4: inst780000DE // CHECK:STDOUT: 5: inst780000E7 // CHECK:STDOUT: 6: inst780000EC // CHECK:STDOUT: inst_block7800005F: // CHECK:STDOUT: 0: inst78000110 // CHECK:STDOUT: 1: inst7800010F // CHECK:STDOUT: inst_block78000060: // CHECK:STDOUT: 0: inst78000113 // CHECK:STDOUT: 1: inst78000114 // CHECK:STDOUT: inst_block78000061: // CHECK:STDOUT: 0: inst78000115 // CHECK:STDOUT: 1: inst78000116 // CHECK:STDOUT: inst_block78000062: // CHECK:STDOUT: 0: inst78000118 // CHECK:STDOUT: 1: inst78000119 // CHECK:STDOUT: inst_block78000063: // CHECK:STDOUT: 0: inst78000115 // CHECK:STDOUT: 1: inst78000116 // CHECK:STDOUT: inst_block78000064: // CHECK:STDOUT: 0: inst78000115 // CHECK:STDOUT: 1: inst78000116 // CHECK:STDOUT: 2: inst78000117 // CHECK:STDOUT: 3: inst78000118 // CHECK:STDOUT: 4: inst78000119 // CHECK:STDOUT: 5: inst7800011A // CHECK:STDOUT: 6: inst7800011B // CHECK:STDOUT: inst_block78000065: // CHECK:STDOUT: 0: inst78000115 // CHECK:STDOUT: 1: inst78000116 // CHECK:STDOUT: inst_block78000066: // CHECK:STDOUT: 0: inst7800011C // CHECK:STDOUT: 1: inst7800011D // CHECK:STDOUT: inst_block78000067: // CHECK:STDOUT: 0: inst78000072 // CHECK:STDOUT: 1: inst780000DD // CHECK:STDOUT: 2: inst78000121 // CHECK:STDOUT: inst_block78000068: // CHECK:STDOUT: 0: inst78000123 // CHECK:STDOUT: inst_block78000069: // CHECK:STDOUT: 0: inst78000071 // CHECK:STDOUT: 1: inst780000DC // CHECK:STDOUT: 2: inst78000120 // CHECK:STDOUT: inst_block7800006A: // CHECK:STDOUT: 0: inst78000050 // CHECK:STDOUT: 1: inst78000050 // CHECK:STDOUT: 2: inst78000050 // CHECK:STDOUT: inst_block7800006B: // CHECK:STDOUT: 0: inst78000071 // CHECK:STDOUT: 1: inst780000DC // CHECK:STDOUT: 2: inst78000120 // CHECK:STDOUT: 3: inst7800012A // CHECK:STDOUT: 4: inst78000072 // CHECK:STDOUT: 5: inst780000DD // CHECK:STDOUT: 6: inst78000121 // CHECK:STDOUT: 7: inst78000122 // CHECK:STDOUT: 8: inst78000125 // CHECK:STDOUT: inst_block7800006C: // CHECK:STDOUT: 0: inst78000127 // CHECK:STDOUT: 1: inst78000128 // CHECK:STDOUT: inst_block7800006D: // CHECK:STDOUT: 0: inst7800012F // CHECK:STDOUT: 1: inst7800012D // CHECK:STDOUT: inst_block7800006E: // CHECK:STDOUT: 0: inst7800012F // CHECK:STDOUT: inst_block7800006F: // CHECK:STDOUT: 0: inst7800012D // CHECK:STDOUT: inst_block78000070: // CHECK:STDOUT: 0: inst78000133 // CHECK:STDOUT: 1: inst78000134 // CHECK:STDOUT: 2: inst78000135 // CHECK:STDOUT: inst_block78000071: // CHECK:STDOUT: 0: inst78000137 // CHECK:STDOUT: 1: inst78000139 // CHECK:STDOUT: 2: inst7800013B // CHECK:STDOUT: inst_block78000072: // CHECK:STDOUT: 0: inst78000136 // CHECK:STDOUT: 1: inst78000137 // CHECK:STDOUT: 2: inst78000138 // CHECK:STDOUT: 3: inst78000139 // CHECK:STDOUT: 4: inst7800013A // CHECK:STDOUT: 5: inst7800013B // CHECK:STDOUT: 6: inst7800013C // CHECK:STDOUT: 7: inst7800013D // CHECK:STDOUT: 8: inst7800013E // CHECK:STDOUT: inst_block78000073: // CHECK:STDOUT: 0: inst78000120 // CHECK:STDOUT: inst_block78000074: // CHECK:STDOUT: 0: inst78000120 // CHECK:STDOUT: 1: inst78000140 // CHECK:STDOUT: 2: inst78000141 // CHECK:STDOUT: inst_block78000075: // CHECK:STDOUT: 0: inst78000120 // CHECK:STDOUT: 1: inst78000121 // CHECK:STDOUT: 2: inst78000146 // CHECK:STDOUT: 3: inst78000145 // CHECK:STDOUT: inst_block78000076: // CHECK:STDOUT: 0: inst78000136 // CHECK:STDOUT: inst_block78000077: // CHECK:STDOUT: 0: inst78000136 // CHECK:STDOUT: inst_block78000078: // CHECK:STDOUT: 0: inst78000138 // CHECK:STDOUT: inst_block78000079: // CHECK:STDOUT: 0: inst78000138 // CHECK:STDOUT: inst_block7800007A: // CHECK:STDOUT: 0: inst7800013A // CHECK:STDOUT: inst_block7800007B: // CHECK:STDOUT: 0: inst7800013A // CHECK:STDOUT: inst_block7800007C: // CHECK:STDOUT: 0: inst78000149 // CHECK:STDOUT: 1: inst7800014A // CHECK:STDOUT: 2: inst7800014B // CHECK:STDOUT: 3: inst7800014C // CHECK:STDOUT: 4: inst7800014D // CHECK:STDOUT: 5: inst7800014E // CHECK:STDOUT: 6: inst7800014F // CHECK:STDOUT: 7: inst78000150 // CHECK:STDOUT: 8: inst78000151 // CHECK:STDOUT: 9: inst78000152 // CHECK:STDOUT: 10: inst78000153 // CHECK:STDOUT: 11: inst78000154 // CHECK:STDOUT: 12: inst78000155 // CHECK:STDOUT: 13: inst78000156 // CHECK:STDOUT: 14: inst78000157 // CHECK:STDOUT: 15: inst78000158 // CHECK:STDOUT: 16: inst78000159 // CHECK:STDOUT: 17: inst7800015A // CHECK:STDOUT: 18: inst7800015B // CHECK:STDOUT: inst_block7800007D: // CHECK:STDOUT: 0: inst78000071 // CHECK:STDOUT: 1: inst78000072 // CHECK:STDOUT: 2: inst780000DC // CHECK:STDOUT: 3: inst780000DD // CHECK:STDOUT: 4: inst78000120 // CHECK:STDOUT: 5: inst78000121 // CHECK:STDOUT: 6: inst78000122 // CHECK:STDOUT: 7: inst7800012B // CHECK:STDOUT: 8: inst78000130 // CHECK:STDOUT: inst_block7800007E: // CHECK:STDOUT: 0: inst7800015E // CHECK:STDOUT: 1: inst7800015D // CHECK:STDOUT: 2: inst7800015C // CHECK:STDOUT: inst_block7800007F: // CHECK:STDOUT: 0: inst78000161 // CHECK:STDOUT: 1: inst78000162 // CHECK:STDOUT: 2: inst78000163 // CHECK:STDOUT: inst_block78000080: // CHECK:STDOUT: 0: inst78000164 // CHECK:STDOUT: 1: inst78000165 // CHECK:STDOUT: 2: inst78000166 // CHECK:STDOUT: inst_block78000081: // CHECK:STDOUT: 0: inst78000168 // CHECK:STDOUT: 1: inst78000169 // CHECK:STDOUT: 2: inst7800016A // CHECK:STDOUT: inst_block78000082: // CHECK:STDOUT: 0: inst78000164 // CHECK:STDOUT: 1: inst78000165 // CHECK:STDOUT: 2: inst78000166 // CHECK:STDOUT: inst_block78000083: // CHECK:STDOUT: 0: inst78000164 // CHECK:STDOUT: 1: inst78000165 // CHECK:STDOUT: 2: inst78000166 // CHECK:STDOUT: 3: inst78000167 // CHECK:STDOUT: 4: inst78000168 // CHECK:STDOUT: 5: inst78000169 // CHECK:STDOUT: 6: inst7800016A // CHECK:STDOUT: 7: inst7800016B // CHECK:STDOUT: 8: inst7800016C // CHECK:STDOUT: inst_block78000084: // CHECK:STDOUT: 0: inst78000164 // CHECK:STDOUT: 1: inst78000165 // CHECK:STDOUT: 2: inst78000166 // CHECK:STDOUT: inst_block78000085: // CHECK:STDOUT: 0: inst7800016D // CHECK:STDOUT: 1: inst7800016E // CHECK:STDOUT: inst_block78000086: // CHECK:STDOUT: 0: inst780000C1 // CHECK:STDOUT: 1: inst780000C2 // CHECK:STDOUT: 2: inst78000042 // CHECK:STDOUT: inst_block78000087: // CHECK:STDOUT: 0: inst78000019 // CHECK:STDOUT: inst_block78000088: // CHECK:STDOUT: 0: inst78000172 // CHECK:STDOUT: inst_block78000089: // CHECK:STDOUT: 0: inst78000174 // CHECK:STDOUT: inst_block7800008A: // CHECK:STDOUT: 0: inst78000174 // CHECK:STDOUT: 1: inst78000175 // CHECK:STDOUT: 2: inst78000176 // CHECK:STDOUT: inst_block7800008B: // CHECK:STDOUT: 0: inst78000173 // CHECK:STDOUT: inst_block7800008C: // CHECK:STDOUT: 0: inst7800017A // CHECK:STDOUT: inst_block7800008D: // CHECK:STDOUT: 0: inst78000174 // CHECK:STDOUT: 1: inst7800001D // CHECK:STDOUT: 2: inst7800001F // CHECK:STDOUT: 3: inst780000C7 // CHECK:STDOUT: inst_block7800008E: // CHECK:STDOUT: 0: inst7800017A // CHECK:STDOUT: inst_block7800008F: // CHECK:STDOUT: 0: inst78000048 // CHECK:STDOUT: inst_block78000090: // CHECK:STDOUT: 0: inst78000184 // CHECK:STDOUT: 1: inst78000187 // CHECK:STDOUT: inst_block78000091: // CHECK:STDOUT: 0: inst78000043 // CHECK:STDOUT: 1: inst78000047 // CHECK:STDOUT: 2: inst78000171 // CHECK:STDOUT: 3: inst78000173 // CHECK:STDOUT: 4: inst7800017A // CHECK:STDOUT: 5: inst7800017B // CHECK:STDOUT: 6: inst7800017C // CHECK:STDOUT: 7: inst7800017D // CHECK:STDOUT: 8: inst78000181 // CHECK:STDOUT: inst_block78000092: // CHECK:STDOUT: 0: instF // CHECK:STDOUT: 1: inst78000010 // CHECK:STDOUT: 2: inst7800003E // CHECK:STDOUT: value_stores: // CHECK:STDOUT: shared_values: // CHECK:STDOUT: ints: {} // CHECK:STDOUT: reals: {} // CHECK:STDOUT: floats: {} // CHECK:STDOUT: identifiers: // CHECK:STDOUT: identifier0: Foo // CHECK:STDOUT: identifier1: T // CHECK:STDOUT: identifier2: p // CHECK:STDOUT: identifier3: Copy // CHECK:STDOUT: identifier4: Op // CHECK:STDOUT: identifier5: U // CHECK:STDOUT: identifier6: V // CHECK:STDOUT: strings: // CHECK:STDOUT: string0: prelude // CHECK:STDOUT: ... ================================================ FILE: toolchain/check/testdata/basics/raw_sem_ir/one_file_with_textual_ir.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/none.carbon // EXTRA-ARGS: --dump-raw-sem-ir --dump-sem-ir-ranges=if-present // // Check that we can combine textual IR and raw IR dumping in one compile. // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/basics/raw_sem_ir/one_file_with_textual_ir.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/basics/raw_sem_ir/one_file_with_textual_ir.carbon fn Foo(n: ()) -> ((), ()) { return (n, ()); } // CHECK:STDOUT: --- // CHECK:STDOUT: filename: one_file_with_textual_ir.carbon // CHECK:STDOUT: sem_ir: // CHECK:STDOUT: names: // CHECK:STDOUT: name0: Foo // CHECK:STDOUT: name1: n // CHECK:STDOUT: import_irs: // CHECK:STDOUT: 'import_ir(ApiForImpl)': {decl_id: inst, is_export: false} // CHECK:STDOUT: 'import_ir(Cpp)': {decl_id: inst, is_export: false} // CHECK:STDOUT: import_ir_insts: {} // CHECK:STDOUT: clang_decls: {} // CHECK:STDOUT: name_scopes: // CHECK:STDOUT: name_scope0: {inst: instF, parent_scope: name_scope, has_error: false, extended_scopes: [], names: {name0: inst5000002A}} // CHECK:STDOUT: entity_names: // CHECK:STDOUT: entity_name50000000: {name: name1, parent_scope: name_scope, index: -1, is_template: 0, is_unused: 0, form: constant} // CHECK:STDOUT: cpp_global_vars: {} // CHECK:STDOUT: functions: // CHECK:STDOUT: function50000000: {name: name0, parent_scope: name_scope0, call_param_patterns_id: inst_block5000000C, call_params_id: inst_block5000000D, return_type_inst_id: inst50000020, return_form_inst_id: inst50000021, return_patterns_id: inst_block5000000B, body: [inst_block50000010]} // CHECK:STDOUT: classes: {} // CHECK:STDOUT: interfaces: {} // CHECK:STDOUT: associated_constants: {} // CHECK:STDOUT: impls: {} // CHECK:STDOUT: generics: {} // CHECK:STDOUT: specifics: {} // CHECK:STDOUT: specific_interfaces: {} // CHECK:STDOUT: struct_type_fields: // CHECK:STDOUT: struct_type_fields_empty: {} // CHECK:STDOUT: types: // CHECK:STDOUT: 'type(TypeType)': // CHECK:STDOUT: value_repr: {kind: copy, type: type(TypeType)} // CHECK:STDOUT: 'type(inst(FormType))': // CHECK:STDOUT: value_repr: {kind: copy, type: type(inst(FormType))} // CHECK:STDOUT: 'type(Error)': // CHECK:STDOUT: value_repr: {kind: copy, type: type(Error)} // CHECK:STDOUT: 'type(inst(NamespaceType))': // CHECK:STDOUT: value_repr: {kind: copy, type: type(inst(NamespaceType))} // CHECK:STDOUT: 'type(inst50000010)': // CHECK:STDOUT: value_repr: {kind: none, type: type(inst50000010)} // CHECK:STDOUT: 'type(inst5000001D)': // CHECK:STDOUT: value_repr: {kind: copy, type: type(inst5000001D)} // CHECK:STDOUT: 'type(inst5000001A)': // CHECK:STDOUT: value_repr: {kind: pointer, type: type(inst5000001D)} // CHECK:STDOUT: 'type(inst5000002B)': // CHECK:STDOUT: value_repr: {kind: none, type: type(inst50000010)} // CHECK:STDOUT: facet_types: {} // CHECK:STDOUT: insts: // CHECK:STDOUT: instF: {kind: Namespace, arg0: name_scope0, arg1: inst, type: type(inst(NamespaceType))} // CHECK:STDOUT: inst50000010: {kind: TupleType, arg0: inst_block_empty, type: type(TypeType)} // CHECK:STDOUT: inst50000011: {kind: TupleLiteral, arg0: inst_block_empty, type: type(inst50000010)} // CHECK:STDOUT: inst50000012: {kind: TupleValue, arg0: inst_block_empty, type: type(inst50000010)} // CHECK:STDOUT: inst50000013: {kind: Converted, arg0: inst50000011, arg1: inst50000010, type: type(TypeType)} // CHECK:STDOUT: inst50000014: {kind: PatternType, arg0: inst50000010, type: type(TypeType)} // CHECK:STDOUT: inst50000015: {kind: ValueBinding, arg0: entity_name50000000, arg1: inst50000026, type: type(inst50000010)} // CHECK:STDOUT: inst50000016: {kind: ValueBindingPattern, arg0: entity_name50000000, type: type(inst50000014)} // CHECK:STDOUT: inst50000017: {kind: ValueParamPattern, arg0: inst50000016, type: type(inst50000014)} // CHECK:STDOUT: inst50000018: {kind: TupleLiteral, arg0: inst_block_empty, type: type(inst50000010)} // CHECK:STDOUT: inst50000019: {kind: TupleLiteral, arg0: inst_block_empty, type: type(inst50000010)} // CHECK:STDOUT: inst5000001A: {kind: TupleType, arg0: inst_block50000008, type: type(TypeType)} // CHECK:STDOUT: inst5000001B: {kind: TupleLiteral, arg0: inst_block50000007, type: type(inst5000001A)} // CHECK:STDOUT: inst5000001C: {kind: TupleValue, arg0: inst_block50000009, type: type(inst5000001A)} // CHECK:STDOUT: inst5000001D: {kind: PointerType, arg0: inst5000001A, type: type(TypeType)} // CHECK:STDOUT: inst5000001E: {kind: Converted, arg0: inst50000012, arg1: inst50000010, type: type(TypeType)} // CHECK:STDOUT: inst5000001F: {kind: Converted, arg0: inst50000012, arg1: inst50000010, type: type(TypeType)} // CHECK:STDOUT: inst50000020: {kind: Converted, arg0: inst5000001B, arg1: inst5000001A, type: type(TypeType)} // CHECK:STDOUT: inst50000021: {kind: InitForm, arg0: inst50000020, type: type(inst(FormType))} // CHECK:STDOUT: inst50000022: {kind: InitForm, arg0: inst5000001A, type: type(inst(FormType))} // CHECK:STDOUT: inst50000023: {kind: PatternType, arg0: inst5000001A, type: type(TypeType)} // CHECK:STDOUT: inst50000024: {kind: ReturnSlotPattern, arg0: inst50000020, type: type(inst50000023)} // CHECK:STDOUT: inst50000025: {kind: OutParamPattern, arg0: inst50000024, type: type(inst50000023)} // CHECK:STDOUT: inst50000026: {kind: ValueParam, arg0: call_param0, arg1: name1, type: type(inst50000010)} // CHECK:STDOUT: inst50000027: {kind: SpliceBlock, arg0: inst_block50000005, arg1: inst50000013, type: type(TypeType)} // CHECK:STDOUT: inst50000028: {kind: OutParam, arg0: call_param1, arg1: name(ReturnSlot), type: type(inst5000001A)} // CHECK:STDOUT: inst50000029: {kind: ReturnSlot, arg0: inst5000001A, arg1: inst50000028, type: type(inst5000001A)} // CHECK:STDOUT: inst5000002A: {kind: FunctionDecl, arg0: function50000000, arg1: inst_block5000000F, type: type(inst5000002B)} // CHECK:STDOUT: inst5000002B: {kind: FunctionType, arg0: function50000000, arg1: specific, type: type(TypeType)} // CHECK:STDOUT: inst5000002C: {kind: StructValue, arg0: inst_block_empty, type: type(inst5000002B)} // CHECK:STDOUT: inst5000002D: {kind: NameRef, arg0: name1, arg1: inst50000015, type: type(inst50000010)} // CHECK:STDOUT: inst5000002E: {kind: TupleLiteral, arg0: inst_block_empty, type: type(inst50000010)} // CHECK:STDOUT: inst5000002F: {kind: TupleLiteral, arg0: inst_block50000011, type: type(inst5000001A)} // CHECK:STDOUT: inst50000030: {kind: TupleAccess, arg0: inst50000028, arg1: element0, type: type(inst50000010)} // CHECK:STDOUT: inst50000031: {kind: TupleInit, arg0: inst_block50000012, arg1: inst, type: type(inst50000010)} // CHECK:STDOUT: inst50000032: {kind: Converted, arg0: inst5000002D, arg1: inst50000031, type: type(inst50000010)} // CHECK:STDOUT: inst50000033: {kind: TupleAccess, arg0: inst50000028, arg1: element1, type: type(inst50000010)} // CHECK:STDOUT: inst50000034: {kind: TupleInit, arg0: inst_block_empty, arg1: inst, type: type(inst50000010)} // CHECK:STDOUT: inst50000035: {kind: Converted, arg0: inst5000002E, arg1: inst50000034, type: type(inst50000010)} // CHECK:STDOUT: inst50000036: {kind: TupleInit, arg0: inst_block50000013, arg1: inst50000028, type: type(inst5000001A)} // CHECK:STDOUT: inst50000037: {kind: Converted, arg0: inst5000002F, arg1: inst50000036, type: type(inst5000001A)} // CHECK:STDOUT: inst50000038: {kind: ReturnExpr, arg0: inst50000037, arg1: inst50000028} // CHECK:STDOUT: constant_values: // CHECK:STDOUT: values: // CHECK:STDOUT: instF: concrete_constant(instF) // CHECK:STDOUT: inst50000010: concrete_constant(inst50000010) // CHECK:STDOUT: inst50000011: concrete_constant(inst50000012) // CHECK:STDOUT: inst50000012: concrete_constant(inst50000012) // CHECK:STDOUT: inst50000013: concrete_constant(inst50000010) // CHECK:STDOUT: inst50000014: concrete_constant(inst50000014) // CHECK:STDOUT: inst50000016: concrete_constant(inst50000016) // CHECK:STDOUT: inst50000017: concrete_constant(inst50000017) // CHECK:STDOUT: inst50000018: concrete_constant(inst50000012) // CHECK:STDOUT: inst50000019: concrete_constant(inst50000012) // CHECK:STDOUT: inst5000001A: concrete_constant(inst5000001A) // CHECK:STDOUT: inst5000001B: concrete_constant(inst5000001C) // CHECK:STDOUT: inst5000001C: concrete_constant(inst5000001C) // CHECK:STDOUT: inst5000001D: concrete_constant(inst5000001D) // CHECK:STDOUT: inst5000001E: concrete_constant(inst50000010) // CHECK:STDOUT: inst5000001F: concrete_constant(inst50000010) // CHECK:STDOUT: inst50000020: concrete_constant(inst5000001A) // CHECK:STDOUT: inst50000021: concrete_constant(inst50000022) // CHECK:STDOUT: inst50000022: concrete_constant(inst50000022) // CHECK:STDOUT: inst50000023: concrete_constant(inst50000023) // CHECK:STDOUT: inst50000024: concrete_constant(inst50000024) // CHECK:STDOUT: inst50000025: concrete_constant(inst50000025) // CHECK:STDOUT: inst50000027: concrete_constant(inst50000010) // CHECK:STDOUT: inst5000002A: concrete_constant(inst5000002C) // CHECK:STDOUT: inst5000002B: concrete_constant(inst5000002B) // CHECK:STDOUT: inst5000002C: concrete_constant(inst5000002C) // CHECK:STDOUT: inst5000002E: concrete_constant(inst50000012) // CHECK:STDOUT: inst50000031: concrete_constant(inst50000012) // CHECK:STDOUT: inst50000032: concrete_constant(inst50000012) // CHECK:STDOUT: inst50000034: concrete_constant(inst50000012) // CHECK:STDOUT: inst50000035: concrete_constant(inst50000012) // CHECK:STDOUT: inst50000036: concrete_constant(inst5000001C) // CHECK:STDOUT: inst50000037: concrete_constant(inst5000001C) // CHECK:STDOUT: symbolic_constants: {} // CHECK:STDOUT: inst_blocks: // CHECK:STDOUT: inst_block_empty: {} // CHECK:STDOUT: exports: // CHECK:STDOUT: 0: inst5000002A // CHECK:STDOUT: generated: {} // CHECK:STDOUT: imports: {} // CHECK:STDOUT: global_init: {} // CHECK:STDOUT: inst_block50000005: // CHECK:STDOUT: 0: inst50000011 // CHECK:STDOUT: 1: inst50000013 // CHECK:STDOUT: inst_block50000006: // CHECK:STDOUT: 0: inst50000017 // CHECK:STDOUT: inst_block50000007: // CHECK:STDOUT: 0: inst50000018 // CHECK:STDOUT: 1: inst50000019 // CHECK:STDOUT: inst_block50000008: // CHECK:STDOUT: 0: inst50000010 // CHECK:STDOUT: 1: inst50000010 // CHECK:STDOUT: inst_block50000009: // CHECK:STDOUT: 0: inst50000012 // CHECK:STDOUT: 1: inst50000012 // CHECK:STDOUT: inst_block5000000A: // CHECK:STDOUT: 0: inst5000001E // CHECK:STDOUT: 1: inst5000001F // CHECK:STDOUT: inst_block5000000B: // CHECK:STDOUT: 0: inst50000025 // CHECK:STDOUT: inst_block5000000C: // CHECK:STDOUT: 0: inst50000017 // CHECK:STDOUT: 1: inst50000025 // CHECK:STDOUT: inst_block5000000D: // CHECK:STDOUT: 0: inst50000026 // CHECK:STDOUT: 1: inst50000028 // CHECK:STDOUT: inst_block5000000E: // CHECK:STDOUT: 0: inst50000016 // CHECK:STDOUT: 1: inst50000017 // CHECK:STDOUT: 2: inst50000024 // CHECK:STDOUT: 3: inst50000025 // CHECK:STDOUT: inst_block5000000F: // CHECK:STDOUT: 0: inst50000018 // CHECK:STDOUT: 1: inst50000019 // CHECK:STDOUT: 2: inst5000001B // CHECK:STDOUT: 3: inst5000001E // CHECK:STDOUT: 4: inst5000001F // CHECK:STDOUT: 5: inst50000020 // CHECK:STDOUT: 6: inst50000021 // CHECK:STDOUT: 7: inst50000026 // CHECK:STDOUT: 8: inst50000027 // CHECK:STDOUT: 9: inst50000015 // CHECK:STDOUT: 10: inst50000028 // CHECK:STDOUT: 11: inst50000029 // CHECK:STDOUT: inst_block50000010: // CHECK:STDOUT: 0: inst5000002D // CHECK:STDOUT: 1: inst5000002E // CHECK:STDOUT: 2: inst5000002F // CHECK:STDOUT: 3: inst50000030 // CHECK:STDOUT: 4: inst50000031 // CHECK:STDOUT: 5: inst50000032 // CHECK:STDOUT: 6: inst50000033 // CHECK:STDOUT: 7: inst50000034 // CHECK:STDOUT: 8: inst50000035 // CHECK:STDOUT: 9: inst50000036 // CHECK:STDOUT: 10: inst50000037 // CHECK:STDOUT: 11: inst50000038 // CHECK:STDOUT: inst_block50000011: // CHECK:STDOUT: 0: inst5000002D // CHECK:STDOUT: 1: inst5000002E // CHECK:STDOUT: inst_block50000012: {} // CHECK:STDOUT: inst_block50000013: // CHECK:STDOUT: 0: inst50000032 // CHECK:STDOUT: 1: inst50000035 // CHECK:STDOUT: inst_block50000014: // CHECK:STDOUT: 0: instF // CHECK:STDOUT: 1: inst5000002A // CHECK:STDOUT: value_stores: // CHECK:STDOUT: shared_values: // CHECK:STDOUT: ints: {} // CHECK:STDOUT: reals: {} // CHECK:STDOUT: floats: {} // CHECK:STDOUT: identifiers: // CHECK:STDOUT: identifier0: Foo // CHECK:STDOUT: identifier1: n // CHECK:STDOUT: strings: {} // CHECK:STDOUT: ... // CHECK:STDOUT: --- one_file_with_textual_ir.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] // CHECK:STDOUT: %empty_tuple: %empty_tuple.type = tuple_value () [concrete] // CHECK:STDOUT: %pattern_type.cb1: type = pattern_type %empty_tuple.type [concrete] // CHECK:STDOUT: %tuple.type: type = tuple_type (%empty_tuple.type, %empty_tuple.type) [concrete] // CHECK:STDOUT: %tuple: %tuple.type = tuple_value (%empty_tuple, %empty_tuple) [concrete] // CHECK:STDOUT: %.e3a: Core.Form = init_form %tuple.type [concrete] // CHECK:STDOUT: %pattern_type.5b8: type = pattern_type %tuple.type [concrete] // CHECK:STDOUT: %Foo.type: type = fn_type @Foo [concrete] // CHECK:STDOUT: %Foo: %Foo.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Foo = %Foo.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Foo.decl: %Foo.type = fn_decl @Foo [concrete = constants.%Foo] { // CHECK:STDOUT: %n.patt: %pattern_type.cb1 = value_binding_pattern n [concrete] // CHECK:STDOUT: %n.param_patt: %pattern_type.cb1 = value_param_pattern %n.patt [concrete] // CHECK:STDOUT: %return.patt: %pattern_type.5b8 = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: %pattern_type.5b8 = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %.loc16_20: %empty_tuple.type = tuple_literal () [concrete = constants.%empty_tuple] // CHECK:STDOUT: %.loc16_24: %empty_tuple.type = tuple_literal () [concrete = constants.%empty_tuple] // CHECK:STDOUT: %.loc16_25.1: %tuple.type = tuple_literal (%.loc16_20, %.loc16_24) [concrete = constants.%tuple] // CHECK:STDOUT: %.loc16_25.2: type = converted constants.%empty_tuple, constants.%empty_tuple.type [concrete = constants.%empty_tuple.type] // CHECK:STDOUT: %.loc16_25.3: type = converted constants.%empty_tuple, constants.%empty_tuple.type [concrete = constants.%empty_tuple.type] // CHECK:STDOUT: %.loc16_25.4: type = converted %.loc16_25.1, constants.%tuple.type [concrete = constants.%tuple.type] // CHECK:STDOUT: %.loc16_25.5: Core.Form = init_form %.loc16_25.4 [concrete = constants.%.e3a] // CHECK:STDOUT: %n.param: %empty_tuple.type = value_param call_param0 // CHECK:STDOUT: %.loc16_12.1: type = splice_block %.loc16_12.3 [concrete = constants.%empty_tuple.type] { // CHECK:STDOUT: %.loc16_12.2: %empty_tuple.type = tuple_literal () [concrete = constants.%empty_tuple] // CHECK:STDOUT: %.loc16_12.3: type = converted %.loc16_12.2, constants.%empty_tuple.type [concrete = constants.%empty_tuple.type] // CHECK:STDOUT: } // CHECK:STDOUT: %n: %empty_tuple.type = value_binding n, %n.param // CHECK:STDOUT: %return.param: ref %tuple.type = out_param call_param1 // CHECK:STDOUT: %return: ref %tuple.type = return_slot %return.param // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @Foo(%n.param: %empty_tuple.type) -> out %return.param: %tuple.type { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %n.ref: %empty_tuple.type = name_ref n, %n // CHECK:STDOUT: %.loc17_15.1: %empty_tuple.type = tuple_literal () [concrete = constants.%empty_tuple] // CHECK:STDOUT: %.loc17_16.1: %tuple.type = tuple_literal (%n.ref, %.loc17_15.1) // CHECK:STDOUT: %tuple.elem0: ref %empty_tuple.type = tuple_access %return.param, element0 // CHECK:STDOUT: %.loc17_11: init %empty_tuple.type = tuple_init () [concrete = constants.%empty_tuple] // CHECK:STDOUT: %.loc17_16.2: init %empty_tuple.type = converted %n.ref, %.loc17_11 [concrete = constants.%empty_tuple] // CHECK:STDOUT: %tuple.elem1: ref %empty_tuple.type = tuple_access %return.param, element1 // CHECK:STDOUT: %.loc17_15.2: init %empty_tuple.type = tuple_init () [concrete = constants.%empty_tuple] // CHECK:STDOUT: %.loc17_16.3: init %empty_tuple.type = converted %.loc17_15.1, %.loc17_15.2 [concrete = constants.%empty_tuple] // CHECK:STDOUT: %.loc17_16.4: init %tuple.type to %return.param = tuple_init (%.loc17_16.2, %.loc17_16.3) [concrete = constants.%tuple] // CHECK:STDOUT: %.loc17_17: init %tuple.type = converted %.loc17_16.1, %.loc17_16.4 [concrete = constants.%tuple] // CHECK:STDOUT: return %.loc17_17 to %return.param // CHECK:STDOUT: } // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/basics/verbose.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/none.carbon // ARGS: -v compile --phase=check %s // // Only checks a couple statements in order to minimize manual update churn. // To test this file alone, run: // bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/basics/verbose.carbon // To dump output, run: // bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/basics/verbose.carbon // NOAUTOUPDATE // SET-CHECK-SUBSET // CHECK:STDERR: Node Push 0: FunctionIntroducer -> // CHECK:STDERR: AddPlaceholderInst: {kind: FunctionDecl, arg0: function, arg1: inst_block_empty} // CHECK:STDERR: ReplaceInst: inst{{[0-9A-F]+}} -> {kind: FunctionDecl, arg0: function{{[0-9A-F]+}}, arg1: inst_block_empty, type: type(inst{{[0-9A-F]+}})} // CHECK:STDERR: inst_block_stack_ Push 1 // CHECK:STDERR: AddInst: {kind: Return} // CHECK:STDERR: inst_block_stack_ Pop 1: inst_block{{[0-9]+}} fn Foo() { return; } ================================================ FILE: toolchain/check/testdata/builtins/bool/eq.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/full.carbon // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/builtins/bool/eq.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/builtins/bool/eq.carbon // --- builtin_call.carbon library "[[@TEST_NAME]]"; fn Eq(a: bool, b: bool) -> bool = "bool.eq"; class C(B:! bool) {} fn True() -> C(true); fn False() -> C(false); //@dump-sem-ir-begin var a: C(Eq(true, true)) = True(); //@dump-sem-ir-end var b: C(Eq(true, false)) = False(); var c: C(Eq(false, true)) = False(); var d: C(Eq(false, false)) = True(); // --- prelude.carbon library "[[@TEST_NAME]]"; class C(B:! bool) {} fn True() -> C(true); fn False() -> C(false); //@dump-sem-ir-begin var a: C(true == true) = True(); //@dump-sem-ir-end var b: C(true == false) = False(); var c: C(false == true) = False(); var d: C(false == false) = True(); // CHECK:STDOUT: --- builtin_call.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %Eq.type: type = fn_type @Eq [concrete] // CHECK:STDOUT: %Eq: %Eq.type = struct_value () [concrete] // CHECK:STDOUT: %C.type: type = generic_class_type @C [concrete] // CHECK:STDOUT: %C.generic: %C.type = struct_value () [concrete] // CHECK:STDOUT: %true: bool = bool_literal true [concrete] // CHECK:STDOUT: %C.a23: type = class_type @C, @C(%true) [concrete] // CHECK:STDOUT: %pattern_type.a06: type = pattern_type %C.a23 [concrete] // CHECK:STDOUT: %True.type: type = fn_type @True [concrete] // CHECK:STDOUT: %True: %True.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %a.patt: %pattern_type.a06 = ref_binding_pattern a [concrete] // CHECK:STDOUT: %a.var_patt: %pattern_type.a06 = var_pattern %a.patt [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %a.var: ref %C.a23 = var %a.var_patt [concrete] // CHECK:STDOUT: %.loc12_24.1: type = splice_block %C.loc12 [concrete = constants.%C.a23] { // CHECK:STDOUT: %C.ref.loc12: %C.type = name_ref C, %C.decl [concrete = constants.%C.generic] // CHECK:STDOUT: %Eq.ref.loc12: %Eq.type = name_ref Eq, %Eq.decl [concrete = constants.%Eq] // CHECK:STDOUT: %true.loc12_13: bool = bool_literal true [concrete = constants.%true] // CHECK:STDOUT: %true.loc12_19: bool = bool_literal true [concrete = constants.%true] // CHECK:STDOUT: %Eq.call.loc12: init bool = call %Eq.ref.loc12(%true.loc12_13, %true.loc12_19) [concrete = constants.%true] // CHECK:STDOUT: %.loc12_24.2: bool = value_of_initializer %Eq.call.loc12 [concrete = constants.%true] // CHECK:STDOUT: %.loc12_24.3: bool = converted %Eq.call.loc12, %.loc12_24.2 [concrete = constants.%true] // CHECK:STDOUT: %C.loc12: type = class_type @C, @C(constants.%true) [concrete = constants.%C.a23] // CHECK:STDOUT: } // CHECK:STDOUT: %a: ref %C.a23 = ref_binding a, %a.var [concrete = %a.var] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @__global_init() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %True.ref.loc12: %True.type = name_ref True, file.%True.decl [concrete = constants.%True] // CHECK:STDOUT: %.loc12: ref %C.a23 = splice_block file.%a.var [concrete = file.%a.var] {} // CHECK:STDOUT: %True.call.loc12: init %C.a23 to %.loc12 = call %True.ref.loc12() // CHECK:STDOUT: assign file.%a.var, %True.call.loc12 // CHECK:STDOUT: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- prelude.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %C.type: type = generic_class_type @C [concrete] // CHECK:STDOUT: %C.generic: %C.type = struct_value () [concrete] // CHECK:STDOUT: %true: bool = bool_literal true [concrete] // CHECK:STDOUT: %C.a23: type = class_type @C, @C(%true) [concrete] // CHECK:STDOUT: %pattern_type.a06: type = pattern_type %C.a23 [concrete] // CHECK:STDOUT: %True.type: type = fn_type @True [concrete] // CHECK:STDOUT: %True: %True.type = struct_value () [concrete] // CHECK:STDOUT: %EqWith.type.863: type = facet_type <@EqWith, @EqWith(bool)> [concrete] // CHECK:STDOUT: %EqWith.impl_witness: = impl_witness imports.%EqWith.impl_witness_table [concrete] // CHECK:STDOUT: %EqWith.facet: %EqWith.type.863 = facet_value bool, (%EqWith.impl_witness) [concrete] // CHECK:STDOUT: %EqWith.WithSelf.Equal.type.8c0: type = fn_type @EqWith.WithSelf.Equal, @EqWith.WithSelf(bool, %EqWith.facet) [concrete] // CHECK:STDOUT: %.483: type = fn_type_with_self_type %EqWith.WithSelf.Equal.type.8c0, %EqWith.facet [concrete] // CHECK:STDOUT: %bool.as.EqWith.impl.Equal.type: type = fn_type @bool.as.EqWith.impl.Equal [concrete] // CHECK:STDOUT: %bool.as.EqWith.impl.Equal: %bool.as.EqWith.impl.Equal.type = struct_value () [concrete] // CHECK:STDOUT: %bool.as.EqWith.impl.Equal.bound.40b: = bound_method %true, %bool.as.EqWith.impl.Equal [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core.import_ref.f73: %bool.as.EqWith.impl.Equal.type = import_ref Core//prelude/operators/comparison, loc{{\d+_\d+}}, loaded [concrete = constants.%bool.as.EqWith.impl.Equal] // CHECK:STDOUT: %Core.import_ref.115 = import_ref Core//prelude/operators/comparison, loc{{\d+_\d+}}, unloaded // CHECK:STDOUT: %EqWith.impl_witness_table = impl_witness_table (%Core.import_ref.f73, %Core.import_ref.115), @bool.as.EqWith.impl [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %a.patt: %pattern_type.a06 = ref_binding_pattern a [concrete] // CHECK:STDOUT: %a.var_patt: %pattern_type.a06 = var_pattern %a.patt [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %a.var: ref %C.a23 = var %a.var_patt [concrete] // CHECK:STDOUT: %.loc10_22.1: type = splice_block %C.loc10 [concrete = constants.%C.a23] { // CHECK:STDOUT: %C.ref.loc10: %C.type = name_ref C, %C.decl [concrete = constants.%C.generic] // CHECK:STDOUT: %true.loc10_10: bool = bool_literal true [concrete = constants.%true] // CHECK:STDOUT: %true.loc10_18: bool = bool_literal true [concrete = constants.%true] // CHECK:STDOUT: %impl.elem0.loc10: %.483 = impl_witness_access constants.%EqWith.impl_witness, element0 [concrete = constants.%bool.as.EqWith.impl.Equal] // CHECK:STDOUT: %bound_method.loc10: = bound_method %true.loc10_10, %impl.elem0.loc10 [concrete = constants.%bool.as.EqWith.impl.Equal.bound.40b] // CHECK:STDOUT: %bool.as.EqWith.impl.Equal.call.loc10: init bool = call %bound_method.loc10(%true.loc10_10, %true.loc10_18) [concrete = constants.%true] // CHECK:STDOUT: %.loc10_22.2: bool = value_of_initializer %bool.as.EqWith.impl.Equal.call.loc10 [concrete = constants.%true] // CHECK:STDOUT: %.loc10_22.3: bool = converted %bool.as.EqWith.impl.Equal.call.loc10, %.loc10_22.2 [concrete = constants.%true] // CHECK:STDOUT: %C.loc10: type = class_type @C, @C(constants.%true) [concrete = constants.%C.a23] // CHECK:STDOUT: } // CHECK:STDOUT: %a: ref %C.a23 = ref_binding a, %a.var [concrete = %a.var] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @__global_init() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %True.ref.loc10: %True.type = name_ref True, file.%True.decl [concrete = constants.%True] // CHECK:STDOUT: %.loc10: ref %C.a23 = splice_block file.%a.var [concrete = file.%a.var] {} // CHECK:STDOUT: %True.call.loc10: init %C.a23 to %.loc10 = call %True.ref.loc10() // CHECK:STDOUT: assign file.%a.var, %True.call.loc10 // CHECK:STDOUT: // CHECK:STDOUT: } // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/builtins/bool/make_type.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/none.carbon // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/builtins/bool/make_type.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/builtins/bool/make_type.carbon // --- types.carbon library "[[@TEST_NAME]]"; fn Bool() -> type = "bool.make_type"; // --- use_types.carbon library "[[@TEST_NAME]]"; import library "types"; //@dump-sem-ir-begin let b: Bool() = false; //@dump-sem-ir-end // CHECK:STDOUT: --- use_types.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %Bool.type: type = fn_type @Bool [concrete] // CHECK:STDOUT: %Bool: %Bool.type = struct_value () [concrete] // CHECK:STDOUT: %pattern_type.831: type = pattern_type bool [concrete] // CHECK:STDOUT: %false: bool = bool_literal false [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Main.Bool: %Bool.type = import_ref Main//types, Bool, loaded [concrete = constants.%Bool] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %b.patt: %pattern_type.831 = value_binding_pattern b [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %.loc7_13.1: type = splice_block %.loc7_13.3 [concrete = bool] { // CHECK:STDOUT: %Bool.ref: %Bool.type = name_ref Bool, imports.%Main.Bool [concrete = constants.%Bool] // CHECK:STDOUT: %Bool.call: init type = call %Bool.ref() [concrete = bool] // CHECK:STDOUT: %.loc7_13.2: type = value_of_initializer %Bool.call [concrete = bool] // CHECK:STDOUT: %.loc7_13.3: type = converted %Bool.call, %.loc7_13.2 [concrete = bool] // CHECK:STDOUT: } // CHECK:STDOUT: %b: bool = value_binding b, @__global_init.%false // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @__global_init() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %false: bool = bool_literal false [concrete = constants.%false] // CHECK:STDOUT: // CHECK:STDOUT: } // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/builtins/bool/neq.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/full.carbon // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/builtins/bool/neq.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/builtins/bool/neq.carbon // --- builtin_call.carbon library "[[@TEST_NAME]]"; fn Neq(a: bool, b: bool) -> bool = "bool.neq"; class C(B:! bool) {} fn True() -> C(true); fn False() -> C(false); //@dump-sem-ir-begin var a: C(Neq(true, true)) = False(); //@dump-sem-ir-end var b: C(Neq(true, false)) = True(); var c: C(Neq(false, true)) = True(); var d: C(Neq(false, false)) = False(); // --- prelude.carbon library "[[@TEST_NAME]]"; class C(B:! bool) {} fn True() -> C(true); fn False() -> C(false); //@dump-sem-ir-begin var a: C(true != true) = False(); //@dump-sem-ir-end var b: C(true != false) = True(); var c: C(false != true) = True(); var d: C(false != false) = False(); // CHECK:STDOUT: --- builtin_call.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %Neq.type: type = fn_type @Neq [concrete] // CHECK:STDOUT: %Neq: %Neq.type = struct_value () [concrete] // CHECK:STDOUT: %C.type: type = generic_class_type @C [concrete] // CHECK:STDOUT: %C.generic: %C.type = struct_value () [concrete] // CHECK:STDOUT: %true: bool = bool_literal true [concrete] // CHECK:STDOUT: %false: bool = bool_literal false [concrete] // CHECK:STDOUT: %C.082: type = class_type @C, @C(%false) [concrete] // CHECK:STDOUT: %pattern_type.2a5: type = pattern_type %C.082 [concrete] // CHECK:STDOUT: %False.type: type = fn_type @False [concrete] // CHECK:STDOUT: %False: %False.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %a.patt: %pattern_type.2a5 = ref_binding_pattern a [concrete] // CHECK:STDOUT: %a.var_patt: %pattern_type.2a5 = var_pattern %a.patt [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %a.var: ref %C.082 = var %a.var_patt [concrete] // CHECK:STDOUT: %.loc12_25.1: type = splice_block %C.loc12 [concrete = constants.%C.082] { // CHECK:STDOUT: %C.ref.loc12: %C.type = name_ref C, %C.decl [concrete = constants.%C.generic] // CHECK:STDOUT: %Neq.ref.loc12: %Neq.type = name_ref Neq, %Neq.decl [concrete = constants.%Neq] // CHECK:STDOUT: %true.loc12_14: bool = bool_literal true [concrete = constants.%true] // CHECK:STDOUT: %true.loc12_20: bool = bool_literal true [concrete = constants.%true] // CHECK:STDOUT: %Neq.call.loc12: init bool = call %Neq.ref.loc12(%true.loc12_14, %true.loc12_20) [concrete = constants.%false] // CHECK:STDOUT: %.loc12_25.2: bool = value_of_initializer %Neq.call.loc12 [concrete = constants.%false] // CHECK:STDOUT: %.loc12_25.3: bool = converted %Neq.call.loc12, %.loc12_25.2 [concrete = constants.%false] // CHECK:STDOUT: %C.loc12: type = class_type @C, @C(constants.%false) [concrete = constants.%C.082] // CHECK:STDOUT: } // CHECK:STDOUT: %a: ref %C.082 = ref_binding a, %a.var [concrete = %a.var] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @__global_init() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %False.ref.loc12: %False.type = name_ref False, file.%False.decl [concrete = constants.%False] // CHECK:STDOUT: %.loc12: ref %C.082 = splice_block file.%a.var [concrete = file.%a.var] {} // CHECK:STDOUT: %False.call.loc12: init %C.082 to %.loc12 = call %False.ref.loc12() // CHECK:STDOUT: assign file.%a.var, %False.call.loc12 // CHECK:STDOUT: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- prelude.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %C.type: type = generic_class_type @C [concrete] // CHECK:STDOUT: %C.generic: %C.type = struct_value () [concrete] // CHECK:STDOUT: %true: bool = bool_literal true [concrete] // CHECK:STDOUT: %false: bool = bool_literal false [concrete] // CHECK:STDOUT: %C.082: type = class_type @C, @C(%false) [concrete] // CHECK:STDOUT: %pattern_type.2a5: type = pattern_type %C.082 [concrete] // CHECK:STDOUT: %False.type: type = fn_type @False [concrete] // CHECK:STDOUT: %False: %False.type = struct_value () [concrete] // CHECK:STDOUT: %EqWith.type.863: type = facet_type <@EqWith, @EqWith(bool)> [concrete] // CHECK:STDOUT: %EqWith.impl_witness: = impl_witness imports.%EqWith.impl_witness_table [concrete] // CHECK:STDOUT: %EqWith.facet: %EqWith.type.863 = facet_value bool, (%EqWith.impl_witness) [concrete] // CHECK:STDOUT: %EqWith.WithSelf.NotEqual.type.14c: type = fn_type @EqWith.WithSelf.NotEqual, @EqWith.WithSelf(bool, %EqWith.facet) [concrete] // CHECK:STDOUT: %.d13: type = fn_type_with_self_type %EqWith.WithSelf.NotEqual.type.14c, %EqWith.facet [concrete] // CHECK:STDOUT: %bool.as.EqWith.impl.NotEqual.type: type = fn_type @bool.as.EqWith.impl.NotEqual [concrete] // CHECK:STDOUT: %bool.as.EqWith.impl.NotEqual: %bool.as.EqWith.impl.NotEqual.type = struct_value () [concrete] // CHECK:STDOUT: %bool.as.EqWith.impl.NotEqual.bound.a7d: = bound_method %true, %bool.as.EqWith.impl.NotEqual [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core.import_ref.093 = import_ref Core//prelude/operators/comparison, loc{{\d+_\d+}}, unloaded // CHECK:STDOUT: %Core.import_ref.fb0: %bool.as.EqWith.impl.NotEqual.type = import_ref Core//prelude/operators/comparison, loc{{\d+_\d+}}, loaded [concrete = constants.%bool.as.EqWith.impl.NotEqual] // CHECK:STDOUT: %EqWith.impl_witness_table = impl_witness_table (%Core.import_ref.093, %Core.import_ref.fb0), @bool.as.EqWith.impl [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %a.patt: %pattern_type.2a5 = ref_binding_pattern a [concrete] // CHECK:STDOUT: %a.var_patt: %pattern_type.2a5 = var_pattern %a.patt [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %a.var: ref %C.082 = var %a.var_patt [concrete] // CHECK:STDOUT: %.loc10_22.1: type = splice_block %C.loc10 [concrete = constants.%C.082] { // CHECK:STDOUT: %C.ref.loc10: %C.type = name_ref C, %C.decl [concrete = constants.%C.generic] // CHECK:STDOUT: %true.loc10_10: bool = bool_literal true [concrete = constants.%true] // CHECK:STDOUT: %true.loc10_18: bool = bool_literal true [concrete = constants.%true] // CHECK:STDOUT: %impl.elem1.loc10: %.d13 = impl_witness_access constants.%EqWith.impl_witness, element1 [concrete = constants.%bool.as.EqWith.impl.NotEqual] // CHECK:STDOUT: %bound_method.loc10: = bound_method %true.loc10_10, %impl.elem1.loc10 [concrete = constants.%bool.as.EqWith.impl.NotEqual.bound.a7d] // CHECK:STDOUT: %bool.as.EqWith.impl.NotEqual.call.loc10: init bool = call %bound_method.loc10(%true.loc10_10, %true.loc10_18) [concrete = constants.%false] // CHECK:STDOUT: %.loc10_22.2: bool = value_of_initializer %bool.as.EqWith.impl.NotEqual.call.loc10 [concrete = constants.%false] // CHECK:STDOUT: %.loc10_22.3: bool = converted %bool.as.EqWith.impl.NotEqual.call.loc10, %.loc10_22.2 [concrete = constants.%false] // CHECK:STDOUT: %C.loc10: type = class_type @C, @C(constants.%false) [concrete = constants.%C.082] // CHECK:STDOUT: } // CHECK:STDOUT: %a: ref %C.082 = ref_binding a, %a.var [concrete = %a.var] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @__global_init() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %False.ref.loc10: %False.type = name_ref False, file.%False.decl [concrete = constants.%False] // CHECK:STDOUT: %.loc10: ref %C.082 = splice_block file.%a.var [concrete = file.%a.var] {} // CHECK:STDOUT: %False.call.loc10: init %C.082 to %.loc10 = call %False.ref.loc10() // CHECK:STDOUT: assign file.%a.var, %False.call.loc10 // CHECK:STDOUT: // CHECK:STDOUT: } // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/builtins/char/convert_checked.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/full.carbon // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/builtins/char/convert_checked.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/builtins/char/convert_checked.carbon // --- builtin.carbon library "[[@TEST_NAME]]"; fn CharLiteral() -> type = "char_literal.make_type"; fn IntLiteral() -> type = "int_literal.make_type"; fn UInt(n: IntLiteral()) -> type = "int.make_type_unsigned"; fn ToChar(c: CharLiteral()) -> UInt(8) = "char.convert_checked"; // --- fail_bad_decl.carbon library "[[@TEST_NAME]]"; import library "builtin"; // CHECK:STDERR: fail_bad_decl.carbon:[[@LINE+4]]:1: error: invalid signature for builtin function "char.convert_checked" [InvalidBuiltinSignature] // CHECK:STDERR: fn ToCharBarResultU16(c: CharLiteral()) -> UInt(16) = "char.convert_checked"; // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: fn ToCharBarResultU16(c: CharLiteral()) -> UInt(16) = "char.convert_checked"; // CHECK:STDERR: fail_bad_decl.carbon:[[@LINE+4]]:1: error: invalid signature for builtin function "char.convert_checked" [InvalidBuiltinSignature] // CHECK:STDERR: fn ToCharBadResultU9(c: CharLiteral()) -> UInt(9) = "char.convert_checked"; // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: fn ToCharBadResultU9(c: CharLiteral()) -> UInt(9) = "char.convert_checked"; // CHECK:STDERR: fail_bad_decl.carbon:[[@LINE+4]]:1: error: invalid signature for builtin function "char.convert_checked" [InvalidBuiltinSignature] // CHECK:STDERR: fn ToCharNoParam() -> UInt(8) = "char.convert_checked"; // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: fn ToCharNoParam() -> UInt(8) = "char.convert_checked"; // --- fail_runtime_call.carbon library "[[@TEST_NAME]]"; import library "builtin"; let c: CharLiteral() = 'a'; // CHECK:STDERR: fail_runtime_call.carbon:[[@LINE+8]]:18: error: non-constant call to compile-time-only function [NonConstantCallToCompTimeOnlyFunction] // CHECK:STDERR: let d: UInt(8) = ToChar(c); // CHECK:STDERR: ^~~~~~~~~ // CHECK:STDERR: fail_runtime_call.carbon:[[@LINE-6]]:1: in import [InImport] // CHECK:STDERR: builtin.carbon:8:1: note: compile-time-only function declared here [CompTimeOnlyFunctionHere] // CHECK:STDERR: fn ToChar(c: CharLiteral()) -> UInt(8) = "char.convert_checked"; // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: let d: UInt(8) = ToChar(c); // --- narrow.carbon library "[[@TEST_NAME]]"; import library "builtin"; //@dump-sem-ir-begin let a: UInt(8) = ToChar('\0'); let b: UInt(8) = ToChar('b'); let c: UInt(8) = ToChar('\u{7F}'); //@dump-sem-ir-end // --- fail_size_small.carbon library "[[@TEST_NAME]]"; import library "builtin"; // CHECK:STDERR: fail_size_small.carbon:[[@LINE+4]]:18: error: character value U+0080 too large for type `` [CharTooLargeForType] // CHECK:STDERR: let c: UInt(8) = ToChar('\u{80}'); // CHECK:STDERR: ^~~~~~~~~~~~~~~~ // CHECK:STDERR: let c: UInt(8) = ToChar('\u{80}'); // --- fail_size_multi_byte.carbon library "[[@TEST_NAME]]"; import library "builtin"; // CHECK:STDERR: fail_size_multi_byte.carbon:[[@LINE+4]]:18: error: character value U+1E15 too large for type `` [CharTooLargeForType] // CHECK:STDERR: let c: UInt(8) = ToChar('\u{1E15}'); // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~ // CHECK:STDERR: let c: UInt(8) = ToChar('\u{1E15}'); // CHECK:STDOUT: --- narrow.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %UInt.type: type = fn_type @UInt [concrete] // CHECK:STDOUT: %UInt: %UInt.type = struct_value () [concrete] // CHECK:STDOUT: %int_8: Core.IntLiteral = int_value 8 [concrete] // CHECK:STDOUT: %u8.builtin: type = int_type unsigned, %int_8 [concrete] // CHECK:STDOUT: %pattern_type.456: type = pattern_type %u8.builtin [concrete] // CHECK:STDOUT: %ToChar.type: type = fn_type @ToChar [concrete] // CHECK:STDOUT: %ToChar: %ToChar.type = struct_value () [concrete] // CHECK:STDOUT: %.dc9: Core.CharLiteral = char_value U+0000 [concrete] // CHECK:STDOUT: %int_0: %u8.builtin = int_value 0 [concrete] // CHECK:STDOUT: %.711: Core.CharLiteral = char_value U+0062 [concrete] // CHECK:STDOUT: %int_98: %u8.builtin = int_value 98 [concrete] // CHECK:STDOUT: %.e28: Core.CharLiteral = char_value U+007F [concrete] // CHECK:STDOUT: %int_127: %u8.builtin = int_value 127 [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Main.UInt: %UInt.type = import_ref Main//builtin, UInt, loaded [concrete = constants.%UInt] // CHECK:STDOUT: %Main.ToChar: %ToChar.type = import_ref Main//builtin, ToChar, loaded [concrete = constants.%ToChar] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %a.patt: %pattern_type.456 = value_binding_pattern a [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %.loc6_14.1: type = splice_block %.loc6_14.3 [concrete = constants.%u8.builtin] { // CHECK:STDOUT: %UInt.ref.loc6: %UInt.type = name_ref UInt, imports.%Main.UInt [concrete = constants.%UInt] // CHECK:STDOUT: %int_8.loc6: Core.IntLiteral = int_value 8 [concrete = constants.%int_8] // CHECK:STDOUT: %UInt.call.loc6: init type = call %UInt.ref.loc6(%int_8.loc6) [concrete = constants.%u8.builtin] // CHECK:STDOUT: %.loc6_14.2: type = value_of_initializer %UInt.call.loc6 [concrete = constants.%u8.builtin] // CHECK:STDOUT: %.loc6_14.3: type = converted %UInt.call.loc6, %.loc6_14.2 [concrete = constants.%u8.builtin] // CHECK:STDOUT: } // CHECK:STDOUT: %.loc6_29.1: %u8.builtin = value_of_initializer @__global_init.%ToChar.call.loc6 [concrete = constants.%int_0] // CHECK:STDOUT: %.loc6_29.2: %u8.builtin = converted @__global_init.%ToChar.call.loc6, %.loc6_29.1 [concrete = constants.%int_0] // CHECK:STDOUT: %a: %u8.builtin = value_binding a, %.loc6_29.2 // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %b.patt: %pattern_type.456 = value_binding_pattern b [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %.loc7_14.1: type = splice_block %.loc7_14.3 [concrete = constants.%u8.builtin] { // CHECK:STDOUT: %UInt.ref.loc7: %UInt.type = name_ref UInt, imports.%Main.UInt [concrete = constants.%UInt] // CHECK:STDOUT: %int_8.loc7: Core.IntLiteral = int_value 8 [concrete = constants.%int_8] // CHECK:STDOUT: %UInt.call.loc7: init type = call %UInt.ref.loc7(%int_8.loc7) [concrete = constants.%u8.builtin] // CHECK:STDOUT: %.loc7_14.2: type = value_of_initializer %UInt.call.loc7 [concrete = constants.%u8.builtin] // CHECK:STDOUT: %.loc7_14.3: type = converted %UInt.call.loc7, %.loc7_14.2 [concrete = constants.%u8.builtin] // CHECK:STDOUT: } // CHECK:STDOUT: %.loc7_28.1: %u8.builtin = value_of_initializer @__global_init.%ToChar.call.loc7 [concrete = constants.%int_98] // CHECK:STDOUT: %.loc7_28.2: %u8.builtin = converted @__global_init.%ToChar.call.loc7, %.loc7_28.1 [concrete = constants.%int_98] // CHECK:STDOUT: %b: %u8.builtin = value_binding b, %.loc7_28.2 // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %c.patt: %pattern_type.456 = value_binding_pattern c [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %.loc8_14.1: type = splice_block %.loc8_14.3 [concrete = constants.%u8.builtin] { // CHECK:STDOUT: %UInt.ref.loc8: %UInt.type = name_ref UInt, imports.%Main.UInt [concrete = constants.%UInt] // CHECK:STDOUT: %int_8.loc8: Core.IntLiteral = int_value 8 [concrete = constants.%int_8] // CHECK:STDOUT: %UInt.call.loc8: init type = call %UInt.ref.loc8(%int_8.loc8) [concrete = constants.%u8.builtin] // CHECK:STDOUT: %.loc8_14.2: type = value_of_initializer %UInt.call.loc8 [concrete = constants.%u8.builtin] // CHECK:STDOUT: %.loc8_14.3: type = converted %UInt.call.loc8, %.loc8_14.2 [concrete = constants.%u8.builtin] // CHECK:STDOUT: } // CHECK:STDOUT: %.loc8_33.1: %u8.builtin = value_of_initializer @__global_init.%ToChar.call.loc8 [concrete = constants.%int_127] // CHECK:STDOUT: %.loc8_33.2: %u8.builtin = converted @__global_init.%ToChar.call.loc8, %.loc8_33.1 [concrete = constants.%int_127] // CHECK:STDOUT: %c: %u8.builtin = value_binding c, %.loc8_33.2 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @__global_init() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %ToChar.ref.loc6: %ToChar.type = name_ref ToChar, imports.%Main.ToChar [concrete = constants.%ToChar] // CHECK:STDOUT: %.loc6: Core.CharLiteral = char_value U+0000 [concrete = constants.%.dc9] // CHECK:STDOUT: %ToChar.call.loc6: init %u8.builtin = call %ToChar.ref.loc6(%.loc6) [concrete = constants.%int_0] // CHECK:STDOUT: %ToChar.ref.loc7: %ToChar.type = name_ref ToChar, imports.%Main.ToChar [concrete = constants.%ToChar] // CHECK:STDOUT: %.loc7: Core.CharLiteral = char_value U+0062 [concrete = constants.%.711] // CHECK:STDOUT: %ToChar.call.loc7: init %u8.builtin = call %ToChar.ref.loc7(%.loc7) [concrete = constants.%int_98] // CHECK:STDOUT: %ToChar.ref.loc8: %ToChar.type = name_ref ToChar, imports.%Main.ToChar [concrete = constants.%ToChar] // CHECK:STDOUT: %.loc8: Core.CharLiteral = char_value U+007F [concrete = constants.%.e28] // CHECK:STDOUT: %ToChar.call.loc8: init %u8.builtin = call %ToChar.ref.loc8(%.loc8) [concrete = constants.%int_127] // CHECK:STDOUT: // CHECK:STDOUT: } // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/builtins/char_literal/make_type.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/none.carbon // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/builtins/char_literal/make_type.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/builtins/char_literal/make_type.carbon // --- types.carbon library "[[@TEST_NAME]]"; fn CharLiteral() -> type = "char_literal.make_type"; // --- use_types.carbon library "[[@TEST_NAME]]"; import library "types"; //@dump-sem-ir-begin let ascii_x: CharLiteral() = 'x'; let not_7_bit: CharLiteral() = '\u{80}'; let not_8_bit: CharLiteral() = '\u{1E15}'; //@dump-sem-ir-end // CHECK:STDOUT: --- use_types.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %CharLiteral.type: type = fn_type @CharLiteral [concrete] // CHECK:STDOUT: %CharLiteral: %CharLiteral.type = struct_value () [concrete] // CHECK:STDOUT: %pattern_type.8c6: type = pattern_type Core.CharLiteral [concrete] // CHECK:STDOUT: %.4ac: Core.CharLiteral = char_value U+0078 [concrete] // CHECK:STDOUT: %.ae7: Core.CharLiteral = char_value U+0080 [concrete] // CHECK:STDOUT: %.a81: Core.CharLiteral = char_value U+1E15 [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Main.CharLiteral: %CharLiteral.type = import_ref Main//types, CharLiteral, loaded [concrete = constants.%CharLiteral] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %ascii_x.patt: %pattern_type.8c6 = value_binding_pattern ascii_x [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %.loc7_26.1: type = splice_block %.loc7_26.3 [concrete = Core.CharLiteral] { // CHECK:STDOUT: %CharLiteral.ref.loc7: %CharLiteral.type = name_ref CharLiteral, imports.%Main.CharLiteral [concrete = constants.%CharLiteral] // CHECK:STDOUT: %CharLiteral.call.loc7: init type = call %CharLiteral.ref.loc7() [concrete = Core.CharLiteral] // CHECK:STDOUT: %.loc7_26.2: type = value_of_initializer %CharLiteral.call.loc7 [concrete = Core.CharLiteral] // CHECK:STDOUT: %.loc7_26.3: type = converted %CharLiteral.call.loc7, %.loc7_26.2 [concrete = Core.CharLiteral] // CHECK:STDOUT: } // CHECK:STDOUT: %ascii_x: Core.CharLiteral = value_binding ascii_x, @__global_init.%.loc7 // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %not_7_bit.patt: %pattern_type.8c6 = value_binding_pattern not_7_bit [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %.loc8_28.1: type = splice_block %.loc8_28.3 [concrete = Core.CharLiteral] { // CHECK:STDOUT: %CharLiteral.ref.loc8: %CharLiteral.type = name_ref CharLiteral, imports.%Main.CharLiteral [concrete = constants.%CharLiteral] // CHECK:STDOUT: %CharLiteral.call.loc8: init type = call %CharLiteral.ref.loc8() [concrete = Core.CharLiteral] // CHECK:STDOUT: %.loc8_28.2: type = value_of_initializer %CharLiteral.call.loc8 [concrete = Core.CharLiteral] // CHECK:STDOUT: %.loc8_28.3: type = converted %CharLiteral.call.loc8, %.loc8_28.2 [concrete = Core.CharLiteral] // CHECK:STDOUT: } // CHECK:STDOUT: %not_7_bit: Core.CharLiteral = value_binding not_7_bit, @__global_init.%.loc8 // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %not_8_bit.patt: %pattern_type.8c6 = value_binding_pattern not_8_bit [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %.loc9_28.1: type = splice_block %.loc9_28.3 [concrete = Core.CharLiteral] { // CHECK:STDOUT: %CharLiteral.ref.loc9: %CharLiteral.type = name_ref CharLiteral, imports.%Main.CharLiteral [concrete = constants.%CharLiteral] // CHECK:STDOUT: %CharLiteral.call.loc9: init type = call %CharLiteral.ref.loc9() [concrete = Core.CharLiteral] // CHECK:STDOUT: %.loc9_28.2: type = value_of_initializer %CharLiteral.call.loc9 [concrete = Core.CharLiteral] // CHECK:STDOUT: %.loc9_28.3: type = converted %CharLiteral.call.loc9, %.loc9_28.2 [concrete = Core.CharLiteral] // CHECK:STDOUT: } // CHECK:STDOUT: %not_8_bit: Core.CharLiteral = value_binding not_8_bit, @__global_init.%.loc9 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @__global_init() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %.loc7: Core.CharLiteral = char_value U+0078 [concrete = constants.%.4ac] // CHECK:STDOUT: %.loc8: Core.CharLiteral = char_value U+0080 [concrete = constants.%.ae7] // CHECK:STDOUT: %.loc9: Core.CharLiteral = char_value U+1E15 [concrete = constants.%.a81] // CHECK:STDOUT: // CHECK:STDOUT: } // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/builtins/cpp/std/initializer_list/make.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/full.carbon // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/builtins/cpp/std/initializer_list/make.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/builtins/cpp/std/initializer_list/make.carbon // --- valid_pointer_pointer.carbon library "[[@TEST_NAME]]"; class PointerPointer { var start: i32*; var end: i32*; } fn Make(arr: array(i32, 3)) -> PointerPointer = "cpp.std.initializer_list.make"; // --- valid_pointer_int.carbon library "[[@TEST_NAME]]"; class PointerInt { var start: i32*; var size: i32; } fn Make(arr: array(i32, 3)) -> PointerInt = "cpp.std.initializer_list.make"; // --- imported_from_cpp.carbon library "[[@TEST_NAME]]"; import Cpp inline ''' struct PointerPointer { int* start; int* end; }; struct PointerInt { int* start; int size; }; '''; fn MakePointerPointer(arr: array(i32, 3)) -> Cpp.PointerPointer = "cpp.std.initializer_list.make"; fn MakePointerInt(arr: array(i32, 3)) -> Cpp.PointerInt = "cpp.std.initializer_list.make"; // --- fail_incomplete.carbon library "[[@TEST_NAME]]"; class FailIncomplete; // CHECK:STDERR: fail_incomplete.carbon:[[@LINE+11]]:20: error: function returns incomplete type `FailIncomplete` [IncompleteTypeInFunctionReturnType] // CHECK:STDERR: fn Make(n: i32) -> FailIncomplete = "cpp.std.initializer_list.make"; // CHECK:STDERR: ^~~~~~~~~~~~~~ // CHECK:STDERR: fail_incomplete.carbon:[[@LINE-5]]:1: note: class was forward declared here [ClassForwardDeclaredHere] // CHECK:STDERR: class FailIncomplete; // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: // CHECK:STDERR: fail_incomplete.carbon:[[@LINE+4]]:1: error: invalid signature for builtin function "cpp.std.initializer_list.make" [InvalidBuiltinSignature] // CHECK:STDERR: fn Make(n: i32) -> FailIncomplete = "cpp.std.initializer_list.make"; // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: fn Make(n: i32) -> FailIncomplete = "cpp.std.initializer_list.make"; // --- fail_wrong_arg.carbon library "[[@TEST_NAME]]"; class FailWrongArg { var start: i32*; var end: i32*; } // CHECK:STDERR: fail_wrong_arg.carbon:[[@LINE+4]]:1: error: invalid signature for builtin function "cpp.std.initializer_list.make" [InvalidBuiltinSignature] // CHECK:STDERR: fn Make(n: i32) -> FailWrongArg = "cpp.std.initializer_list.make"; // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: fn Make(n: i32) -> FailWrongArg = "cpp.std.initializer_list.make"; // --- fail_wrong_arg_count.carbon library "[[@TEST_NAME]]"; class FailWrongArgCount { var start: i32*; var end: i32*; } // CHECK:STDERR: fail_wrong_arg_count.carbon:[[@LINE+4]]:1: error: invalid signature for builtin function "cpp.std.initializer_list.make" [InvalidBuiltinSignature] // CHECK:STDERR: fn Make(arr: array(i32, 3), n: i32) -> FailWrongArgCount = "cpp.std.initializer_list.make"; // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: fn Make(arr: array(i32, 3), n: i32) -> FailWrongArgCount = "cpp.std.initializer_list.make"; // --- fail_not_a_class.carbon library "[[@TEST_NAME]]"; // CHECK:STDERR: fail_not_a_class.carbon:[[@LINE+4]]:1: error: invalid signature for builtin function "cpp.std.initializer_list.make" [InvalidBuiltinSignature] // CHECK:STDERR: fn Make(arr: array(i32, 3)) -> i32 = "cpp.std.initializer_list.make"; // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: fn Make(arr: array(i32, 3)) -> i32 = "cpp.std.initializer_list.make"; // --- fail_wrong_field_count.carbon library "[[@TEST_NAME]]"; class FailWrongFieldCount { var start: i32*; } // CHECK:STDERR: fail_wrong_field_count.carbon:[[@LINE+4]]:1: error: invalid signature for builtin function "cpp.std.initializer_list.make" [InvalidBuiltinSignature] // CHECK:STDERR: fn Make(arr: array(i32, 3)) -> FailWrongFieldCount = "cpp.std.initializer_list.make"; // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: fn Make(arr: array(i32, 3)) -> FailWrongFieldCount = "cpp.std.initializer_list.make"; // --- fail_wrong_first_field.carbon library "[[@TEST_NAME]]"; class FailWrongFirstField { var start: i32; var end: i32; } // CHECK:STDERR: fail_wrong_first_field.carbon:[[@LINE+4]]:1: error: invalid signature for builtin function "cpp.std.initializer_list.make" [InvalidBuiltinSignature] // CHECK:STDERR: fn Make(arr: array(i32, 3)) -> FailWrongFirstField = "cpp.std.initializer_list.make"; // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: fn Make(arr: array(i32, 3)) -> FailWrongFirstField = "cpp.std.initializer_list.make"; // --- fail_wrong_second_field.carbon library "[[@TEST_NAME]]"; class FailWrongSecondField { var start: i32*; var end: {.a: i32}; } // CHECK:STDERR: fail_wrong_second_field.carbon:[[@LINE+4]]:1: error: invalid signature for builtin function "cpp.std.initializer_list.make" [InvalidBuiltinSignature] // CHECK:STDERR: fn Make(arr: array(i32, 3)) -> FailWrongSecondField = "cpp.std.initializer_list.make"; // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: fn Make(arr: array(i32, 3)) -> FailWrongSecondField = "cpp.std.initializer_list.make"; ================================================ FILE: toolchain/check/testdata/builtins/float/add.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/primitives.carbon // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/builtins/float/add.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/builtins/float/add.carbon // --- float_add.carbon library "[[@TEST_NAME]]"; fn Add(a: f64, b: f64) -> f64 = "float.add"; fn RuntimeCallIsValid(a: f64, b: f64) -> f64 { //@dump-sem-ir-begin return Add(a, b); //@dump-sem-ir-end } var x: f64 = Add(2.2, 2.3); // --- fail_bad_decl.carbon library "[[@TEST_NAME]]"; // CHECK:STDERR: fail_bad_decl.carbon:[[@LINE+4]]:1: error: invalid signature for builtin function "float.add" [InvalidBuiltinSignature] // CHECK:STDERR: fn TooFew(a: f64) -> f64 = "float.add"; // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: fn TooFew(a: f64) -> f64 = "float.add"; // CHECK:STDERR: fail_bad_decl.carbon:[[@LINE+4]]:1: error: invalid signature for builtin function "float.add" [InvalidBuiltinSignature] // CHECK:STDERR: fn TooMany(a: f64, b: f64, c: f64) -> f64 = "float.add"; // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: fn TooMany(a: f64, b: f64, c: f64) -> f64 = "float.add"; // CHECK:STDERR: fail_bad_decl.carbon:[[@LINE+4]]:1: error: invalid signature for builtin function "float.add" [InvalidBuiltinSignature] // CHECK:STDERR: fn BadReturnType(a: f64, b: f64) -> bool = "float.add"; // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: fn BadReturnType(a: f64, b: f64) -> bool = "float.add"; fn JustRight(a: f64, b: f64) -> f64 = "float.add"; fn RuntimeCallIsValidTooFew(a: f64) -> f64 { return TooFew(a); } fn RuntimeCallIsValidTooMany(a: f64, b: f64, c: f64) -> f64 { return TooMany(a, b, c); } fn RuntimeCallIsValidBadReturnType(a: f64, b: f64) -> bool { return BadReturnType(a, b); } // --- fail_literal.carbon library "[[@TEST_NAME]]"; fn Literal() -> type = "float_literal.make_type"; // CHECK:STDERR: fail_literal.carbon:[[@LINE+4]]:1: error: invalid signature for builtin function "float.add" [InvalidBuiltinSignature] // CHECK:STDERR: fn AddLiteral(a: Literal(), b: Literal()) -> Literal() = "float.add"; // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: fn AddLiteral(a: Literal(), b: Literal()) -> Literal() = "float.add"; // CHECK:STDOUT: --- float_add.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %int_64: Core.IntLiteral = int_value 64 [concrete] // CHECK:STDOUT: %f64.d77: type = class_type @Float, @Float(%int_64) [concrete] // CHECK:STDOUT: %Add.type: type = fn_type @Add [concrete] // CHECK:STDOUT: %Add: %Add.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @RuntimeCallIsValid(%a.param: %f64.d77, %b.param: %f64.d77) -> out %return.param: %f64.d77 { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %Add.ref: %Add.type = name_ref Add, file.%Add.decl [concrete = constants.%Add] // CHECK:STDOUT: %a.ref: %f64.d77 = name_ref a, %a // CHECK:STDOUT: %b.ref: %f64.d77 = name_ref b, %b // CHECK:STDOUT: %Add.call: init %f64.d77 = call %Add.ref(%a.ref, %b.ref) // CHECK:STDOUT: return %Add.call // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @__global_init() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: // CHECK:STDOUT: } // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/builtins/float/add_assign.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/primitives.carbon // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/builtins/float/add_assign.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/builtins/float/add_assign.carbon // --- call.carbon library "[[@TEST_NAME]]"; fn Builtin(ref a: f64, b: f64) = "float.add_assign"; fn Call(ref a: f64, b: f64) { Builtin(ref a, b); } // --- fail_bad_decl.carbon library "[[@TEST_NAME]]"; // CHECK:STDERR: fail_bad_decl.carbon:[[@LINE+4]]:1: error: invalid signature for builtin function "float.add_assign" [InvalidBuiltinSignature] // CHECK:STDERR: fn NotRef(a: f64, b: f64) = "float.add_assign"; // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: fn NotRef(a: f64, b: f64) = "float.add_assign"; // CHECK:STDERR: fail_bad_decl.carbon:[[@LINE+4]]:1: error: invalid signature for builtin function "float.add_assign" [InvalidBuiltinSignature] // CHECK:STDERR: fn MixedTypes(ref a: f64, b: f32) = "float.add_assign"; // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: fn MixedTypes(ref a: f64, b: f32) = "float.add_assign"; // --- fail_literal.carbon library "[[@TEST_NAME]]"; fn Literal() -> type = "float_literal.make_type"; // CHECK:STDERR: fail_literal.carbon:[[@LINE+4]]:1: error: invalid signature for builtin function "float.add_assign" [InvalidBuiltinSignature] // CHECK:STDERR: fn LiteralRuntime(ref a: Literal(), b: Literal()) = "float.add_assign"; // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: fn LiteralRuntime(ref a: Literal(), b: Literal()) = "float.add_assign"; ================================================ FILE: toolchain/check/testdata/builtins/float/convert_checked.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/primitives.carbon // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/builtins/float/convert_checked.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/builtins/float/convert_checked.carbon // --- literal.carbon library "[[@TEST_NAME]]"; fn FloatLiteral() -> type = "float_literal.make_type"; fn FloatLiteralToFloatLiteral(a: FloatLiteral()) -> FloatLiteral() = "float.convert_checked"; // --- identity_literal.carbon library "[[@TEST_NAME]]"; import library "literal"; //@dump-sem-ir-begin let f: FloatLiteral() = FloatLiteralToFloatLiteral(1.0); //@dump-sem-ir-end // --- f64.carbon library "[[@TEST_NAME]]"; export import library "literal"; fn Float64ToFloat64(a: f64) -> f64 = "float.convert_checked"; fn Float64ToFloatLiteral(a: f64) -> FloatLiteral() = "float.convert_checked"; fn FloatLiteralToFloat64(a: FloatLiteral()) -> f64 = "float.convert_checked"; // --- f32.carbon library "[[@TEST_NAME]]"; export import library "literal"; fn Float32ToFloat32(a: f32) -> f32 = "float.convert_checked"; fn Float32ToFloatLiteral(a: f32) -> FloatLiteral() = "float.convert_checked"; fn FloatLiteralToFloat32(a: FloatLiteral()) -> f32 = "float.convert_checked"; fn Float32ToFloat64(a: f32) -> f64 = "float.convert_checked"; fn Float64ToFloat32(a: f64) -> f32 = "float.convert_checked"; // --- literal_f64.carbon library "[[@TEST_NAME]]"; import library "f64"; //@dump-sem-ir-begin let a: f64 = FloatLiteralToFloat64(0.0); let b: f64 = FloatLiteralToFloat64(1.0); let c: f64 = FloatLiteralToFloat64(1.0e308); //@dump-sem-ir-end // --- literal_f32.carbon library "[[@TEST_NAME]]"; import library "f32"; //@dump-sem-ir-begin let a: f32 = FloatLiteralToFloat32(0.0); let b: f32 = FloatLiteralToFloat32(1.0); let c: f32 = FloatLiteralToFloat32(1.0e38); //@dump-sem-ir-end // --- identity_f64.carbon library "[[@TEST_NAME]]"; import library "f64"; //@dump-sem-ir-begin let a: f64 = Float64ToFloat64(0.0); let b: f64 = Float64ToFloat64(1.0); let c: f64 = Float64ToFloat64(1.0e308); //@dump-sem-ir-end // --- identity_f32.carbon library "[[@TEST_NAME]]"; import library "f32"; //@dump-sem-ir-begin let a: f32 = Float32ToFloat32(0.0); let b: f32 = Float32ToFloat32(1.0); let c: f32 = Float32ToFloat32(1.0e38); //@dump-sem-ir-end // --- truncate.carbon library "[[@TEST_NAME]]"; import library "f32"; //@dump-sem-ir-begin let a: f32 = Float64ToFloat32(1.0); //@dump-sem-ir-end // --- fail_truncate_overflow.carbon library "[[@TEST_NAME]]"; import library "f32"; import library "f64"; //@dump-sem-ir-begin // CHECK:STDERR: fail_truncate_overflow.carbon:[[@LINE+4]]:14: error: value 9.9999999999999994E+38 too large for floating-point type `f32` [FloatTooLargeForType] // CHECK:STDERR: let a: f32 = Float64ToFloat32(1.0e39); // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: let a: f32 = Float64ToFloat32(1.0e39); // CHECK:STDERR: fail_truncate_overflow.carbon:[[@LINE+4]]:14: error: value 10*10^38 too large for floating-point type `f32` [FloatLiteralTooLargeForType] // CHECK:STDERR: let b: f32 = FloatLiteralToFloat32(1.0e39); // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: let b: f32 = FloatLiteralToFloat32(1.0e39); // CHECK:STDERR: fail_truncate_overflow.carbon:[[@LINE+4]]:14: error: value 10*10^308 too large for floating-point type `f64` [FloatLiteralTooLargeForType] // CHECK:STDERR: let c: f64 = FloatLiteralToFloat64(1.0e309); // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: let c: f64 = FloatLiteralToFloat64(1.0e309); //@dump-sem-ir-end // --- extend.carbon library "[[@TEST_NAME]]"; import library "f32"; //@dump-sem-ir-begin let a: f64 = Float32ToFloat64(1.0); let b: f64 = Float32ToFloat64(1.0e30); //@dump-sem-ir-end // --- fail_not_constant.carbon library "[[@TEST_NAME]]"; import library "f64"; //@dump-sem-ir-begin let not_constant_64: f64 = 0.0; // CHECK:STDERR: fail_not_constant.carbon:[[@LINE+8]]:33: error: non-constant call to compile-time-only function [NonConstantCallToCompTimeOnlyFunction] // CHECK:STDERR: let convert_not_constant: f64 = Float64ToFloat64(not_constant_64); // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: fail_not_constant.carbon:[[@LINE-7]]:1: in import [InImport] // CHECK:STDERR: f64.carbon:5:1: note: compile-time-only function declared here [CompTimeOnlyFunctionHere] // CHECK:STDERR: fn Float64ToFloat64(a: f64) -> f64 = "float.convert_checked"; // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: let convert_not_constant: f64 = Float64ToFloat64(not_constant_64); //@dump-sem-ir-end // CHECK:STDOUT: --- identity_literal.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %FloatLiteral.type: type = fn_type @FloatLiteral [concrete] // CHECK:STDOUT: %FloatLiteral: %FloatLiteral.type = struct_value () [concrete] // CHECK:STDOUT: %pattern_type.dab: type = pattern_type Core.FloatLiteral [concrete] // CHECK:STDOUT: %FloatLiteralToFloatLiteral.type: type = fn_type @FloatLiteralToFloatLiteral [concrete] // CHECK:STDOUT: %FloatLiteralToFloatLiteral: %FloatLiteralToFloatLiteral.type = struct_value () [concrete] // CHECK:STDOUT: %float: Core.FloatLiteral = float_literal_value 10e-1 [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Main.FloatLiteral: %FloatLiteral.type = import_ref Main//literal, FloatLiteral, loaded [concrete = constants.%FloatLiteral] // CHECK:STDOUT: %Main.FloatLiteralToFloatLiteral: %FloatLiteralToFloatLiteral.type = import_ref Main//literal, FloatLiteralToFloatLiteral, loaded [concrete = constants.%FloatLiteralToFloatLiteral] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %f.patt: %pattern_type.dab = value_binding_pattern f [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %.loc6_21.1: type = splice_block %.loc6_21.3 [concrete = Core.FloatLiteral] { // CHECK:STDOUT: %FloatLiteral.ref: %FloatLiteral.type = name_ref FloatLiteral, imports.%Main.FloatLiteral [concrete = constants.%FloatLiteral] // CHECK:STDOUT: %FloatLiteral.call: init type = call %FloatLiteral.ref() [concrete = Core.FloatLiteral] // CHECK:STDOUT: %.loc6_21.2: type = value_of_initializer %FloatLiteral.call [concrete = Core.FloatLiteral] // CHECK:STDOUT: %.loc6_21.3: type = converted %FloatLiteral.call, %.loc6_21.2 [concrete = Core.FloatLiteral] // CHECK:STDOUT: } // CHECK:STDOUT: %.loc6_55.1: Core.FloatLiteral = value_of_initializer @__global_init.%FloatLiteralToFloatLiteral.call [concrete = constants.%float] // CHECK:STDOUT: %.loc6_55.2: Core.FloatLiteral = converted @__global_init.%FloatLiteralToFloatLiteral.call, %.loc6_55.1 [concrete = constants.%float] // CHECK:STDOUT: %f: Core.FloatLiteral = value_binding f, %.loc6_55.2 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @__global_init() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %FloatLiteralToFloatLiteral.ref: %FloatLiteralToFloatLiteral.type = name_ref FloatLiteralToFloatLiteral, imports.%Main.FloatLiteralToFloatLiteral [concrete = constants.%FloatLiteralToFloatLiteral] // CHECK:STDOUT: %float: Core.FloatLiteral = float_literal_value 10e-1 [concrete = constants.%float] // CHECK:STDOUT: %FloatLiteralToFloatLiteral.call: init Core.FloatLiteral = call %FloatLiteralToFloatLiteral.ref(%float) [concrete = constants.%float] // CHECK:STDOUT: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- literal_f64.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %int_64: Core.IntLiteral = int_value 64 [concrete] // CHECK:STDOUT: %f64.d77: type = class_type @Float, @Float(%int_64) [concrete] // CHECK:STDOUT: %pattern_type.0ae: type = pattern_type %f64.d77 [concrete] // CHECK:STDOUT: %FloatLiteralToFloat64.type: type = fn_type @FloatLiteralToFloat64 [concrete] // CHECK:STDOUT: %FloatLiteralToFloat64: %FloatLiteralToFloat64.type = struct_value () [concrete] // CHECK:STDOUT: %float.1f7: Core.FloatLiteral = float_literal_value 0e-1 [concrete] // CHECK:STDOUT: %float.0a8: %f64.d77 = float_value 0 [concrete] // CHECK:STDOUT: %float.6da: Core.FloatLiteral = float_literal_value 10e-1 [concrete] // CHECK:STDOUT: %float.d20: %f64.d77 = float_value 1 [concrete] // CHECK:STDOUT: %float.12a: Core.FloatLiteral = float_literal_value 10e307 [concrete] // CHECK:STDOUT: %float.bde: %f64.d77 = float_value 1.0E+308 [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Main.FloatLiteralToFloat64: %FloatLiteralToFloat64.type = import_ref Main//f64, FloatLiteralToFloat64, loaded [concrete = constants.%FloatLiteralToFloat64] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %a.patt: %pattern_type.0ae = value_binding_pattern a [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %f64.loc6: type = type_literal constants.%f64.d77 [concrete = constants.%f64.d77] // CHECK:STDOUT: %.loc6_39.1: %f64.d77 = value_of_initializer @__global_init.%FloatLiteralToFloat64.call.loc6 [concrete = constants.%float.0a8] // CHECK:STDOUT: %.loc6_39.2: %f64.d77 = converted @__global_init.%FloatLiteralToFloat64.call.loc6, %.loc6_39.1 [concrete = constants.%float.0a8] // CHECK:STDOUT: %a: %f64.d77 = value_binding a, %.loc6_39.2 // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %b.patt: %pattern_type.0ae = value_binding_pattern b [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %f64.loc7: type = type_literal constants.%f64.d77 [concrete = constants.%f64.d77] // CHECK:STDOUT: %.loc7_39.1: %f64.d77 = value_of_initializer @__global_init.%FloatLiteralToFloat64.call.loc7 [concrete = constants.%float.d20] // CHECK:STDOUT: %.loc7_39.2: %f64.d77 = converted @__global_init.%FloatLiteralToFloat64.call.loc7, %.loc7_39.1 [concrete = constants.%float.d20] // CHECK:STDOUT: %b: %f64.d77 = value_binding b, %.loc7_39.2 // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %c.patt: %pattern_type.0ae = value_binding_pattern c [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %f64.loc8: type = type_literal constants.%f64.d77 [concrete = constants.%f64.d77] // CHECK:STDOUT: %.loc8_43.1: %f64.d77 = value_of_initializer @__global_init.%FloatLiteralToFloat64.call.loc8 [concrete = constants.%float.bde] // CHECK:STDOUT: %.loc8_43.2: %f64.d77 = converted @__global_init.%FloatLiteralToFloat64.call.loc8, %.loc8_43.1 [concrete = constants.%float.bde] // CHECK:STDOUT: %c: %f64.d77 = value_binding c, %.loc8_43.2 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @__global_init() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %FloatLiteralToFloat64.ref.loc6: %FloatLiteralToFloat64.type = name_ref FloatLiteralToFloat64, imports.%Main.FloatLiteralToFloat64 [concrete = constants.%FloatLiteralToFloat64] // CHECK:STDOUT: %float.loc6: Core.FloatLiteral = float_literal_value 0e-1 [concrete = constants.%float.1f7] // CHECK:STDOUT: %FloatLiteralToFloat64.call.loc6: init %f64.d77 = call %FloatLiteralToFloat64.ref.loc6(%float.loc6) [concrete = constants.%float.0a8] // CHECK:STDOUT: %FloatLiteralToFloat64.ref.loc7: %FloatLiteralToFloat64.type = name_ref FloatLiteralToFloat64, imports.%Main.FloatLiteralToFloat64 [concrete = constants.%FloatLiteralToFloat64] // CHECK:STDOUT: %float.loc7: Core.FloatLiteral = float_literal_value 10e-1 [concrete = constants.%float.6da] // CHECK:STDOUT: %FloatLiteralToFloat64.call.loc7: init %f64.d77 = call %FloatLiteralToFloat64.ref.loc7(%float.loc7) [concrete = constants.%float.d20] // CHECK:STDOUT: %FloatLiteralToFloat64.ref.loc8: %FloatLiteralToFloat64.type = name_ref FloatLiteralToFloat64, imports.%Main.FloatLiteralToFloat64 [concrete = constants.%FloatLiteralToFloat64] // CHECK:STDOUT: %float.loc8: Core.FloatLiteral = float_literal_value 10e307 [concrete = constants.%float.12a] // CHECK:STDOUT: %FloatLiteralToFloat64.call.loc8: init %f64.d77 = call %FloatLiteralToFloat64.ref.loc8(%float.loc8) [concrete = constants.%float.bde] // CHECK:STDOUT: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- literal_f32.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [concrete] // CHECK:STDOUT: %f32.97e: type = class_type @Float, @Float(%int_32) [concrete] // CHECK:STDOUT: %pattern_type.201: type = pattern_type %f32.97e [concrete] // CHECK:STDOUT: %FloatLiteralToFloat32.type: type = fn_type @FloatLiteralToFloat32 [concrete] // CHECK:STDOUT: %FloatLiteralToFloat32: %FloatLiteralToFloat32.type = struct_value () [concrete] // CHECK:STDOUT: %float.1f7: Core.FloatLiteral = float_literal_value 0e-1 [concrete] // CHECK:STDOUT: %float.4db: %f32.97e = float_value 0 [concrete] // CHECK:STDOUT: %float.6da: Core.FloatLiteral = float_literal_value 10e-1 [concrete] // CHECK:STDOUT: %float.e3b: %f32.97e = float_value 1 [concrete] // CHECK:STDOUT: %float.8d2: Core.FloatLiteral = float_literal_value 10e37 [concrete] // CHECK:STDOUT: %float.520: %f32.97e = float_value 9.99999968E+37 [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Main.FloatLiteralToFloat32: %FloatLiteralToFloat32.type = import_ref Main//f32, FloatLiteralToFloat32, loaded [concrete = constants.%FloatLiteralToFloat32] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %a.patt: %pattern_type.201 = value_binding_pattern a [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %f32.loc6: type = type_literal constants.%f32.97e [concrete = constants.%f32.97e] // CHECK:STDOUT: %.loc6_39.1: %f32.97e = value_of_initializer @__global_init.%FloatLiteralToFloat32.call.loc6 [concrete = constants.%float.4db] // CHECK:STDOUT: %.loc6_39.2: %f32.97e = converted @__global_init.%FloatLiteralToFloat32.call.loc6, %.loc6_39.1 [concrete = constants.%float.4db] // CHECK:STDOUT: %a: %f32.97e = value_binding a, %.loc6_39.2 // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %b.patt: %pattern_type.201 = value_binding_pattern b [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %f32.loc7: type = type_literal constants.%f32.97e [concrete = constants.%f32.97e] // CHECK:STDOUT: %.loc7_39.1: %f32.97e = value_of_initializer @__global_init.%FloatLiteralToFloat32.call.loc7 [concrete = constants.%float.e3b] // CHECK:STDOUT: %.loc7_39.2: %f32.97e = converted @__global_init.%FloatLiteralToFloat32.call.loc7, %.loc7_39.1 [concrete = constants.%float.e3b] // CHECK:STDOUT: %b: %f32.97e = value_binding b, %.loc7_39.2 // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %c.patt: %pattern_type.201 = value_binding_pattern c [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %f32.loc8: type = type_literal constants.%f32.97e [concrete = constants.%f32.97e] // CHECK:STDOUT: %.loc8_42.1: %f32.97e = value_of_initializer @__global_init.%FloatLiteralToFloat32.call.loc8 [concrete = constants.%float.520] // CHECK:STDOUT: %.loc8_42.2: %f32.97e = converted @__global_init.%FloatLiteralToFloat32.call.loc8, %.loc8_42.1 [concrete = constants.%float.520] // CHECK:STDOUT: %c: %f32.97e = value_binding c, %.loc8_42.2 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @__global_init() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %FloatLiteralToFloat32.ref.loc6: %FloatLiteralToFloat32.type = name_ref FloatLiteralToFloat32, imports.%Main.FloatLiteralToFloat32 [concrete = constants.%FloatLiteralToFloat32] // CHECK:STDOUT: %float.loc6: Core.FloatLiteral = float_literal_value 0e-1 [concrete = constants.%float.1f7] // CHECK:STDOUT: %FloatLiteralToFloat32.call.loc6: init %f32.97e = call %FloatLiteralToFloat32.ref.loc6(%float.loc6) [concrete = constants.%float.4db] // CHECK:STDOUT: %FloatLiteralToFloat32.ref.loc7: %FloatLiteralToFloat32.type = name_ref FloatLiteralToFloat32, imports.%Main.FloatLiteralToFloat32 [concrete = constants.%FloatLiteralToFloat32] // CHECK:STDOUT: %float.loc7: Core.FloatLiteral = float_literal_value 10e-1 [concrete = constants.%float.6da] // CHECK:STDOUT: %FloatLiteralToFloat32.call.loc7: init %f32.97e = call %FloatLiteralToFloat32.ref.loc7(%float.loc7) [concrete = constants.%float.e3b] // CHECK:STDOUT: %FloatLiteralToFloat32.ref.loc8: %FloatLiteralToFloat32.type = name_ref FloatLiteralToFloat32, imports.%Main.FloatLiteralToFloat32 [concrete = constants.%FloatLiteralToFloat32] // CHECK:STDOUT: %float.loc8: Core.FloatLiteral = float_literal_value 10e37 [concrete = constants.%float.8d2] // CHECK:STDOUT: %FloatLiteralToFloat32.call.loc8: init %f32.97e = call %FloatLiteralToFloat32.ref.loc8(%float.loc8) [concrete = constants.%float.520] // CHECK:STDOUT: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- identity_f64.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %int_64: Core.IntLiteral = int_value 64 [concrete] // CHECK:STDOUT: %f64.d77: type = class_type @Float, @Float(%int_64) [concrete] // CHECK:STDOUT: %pattern_type.0ae: type = pattern_type %f64.d77 [concrete] // CHECK:STDOUT: %Float64ToFloat64.type: type = fn_type @Float64ToFloat64 [concrete] // CHECK:STDOUT: %Float64ToFloat64: %Float64ToFloat64.type = struct_value () [concrete] // CHECK:STDOUT: %float.1f7: Core.FloatLiteral = float_literal_value 0e-1 [concrete] // CHECK:STDOUT: %ImplicitAs.type.4a8: type = facet_type <@ImplicitAs, @ImplicitAs(%f64.d77)> [concrete] // CHECK:STDOUT: %To: Core.IntLiteral = symbolic_binding To, 0 [symbolic] // CHECK:STDOUT: %Core.FloatLiteral.as.ImplicitAs.impl.Convert.type.02f: type = fn_type @Core.FloatLiteral.as.ImplicitAs.impl.Convert, @Core.FloatLiteral.as.ImplicitAs.impl(%To) [symbolic] // CHECK:STDOUT: %Core.FloatLiteral.as.ImplicitAs.impl.Convert.1f0: %Core.FloatLiteral.as.ImplicitAs.impl.Convert.type.02f = struct_value () [symbolic] // CHECK:STDOUT: %ImplicitAs.impl_witness.cb2: = impl_witness imports.%ImplicitAs.impl_witness_table, @Core.FloatLiteral.as.ImplicitAs.impl(%int_64) [concrete] // CHECK:STDOUT: %Core.FloatLiteral.as.ImplicitAs.impl.Convert.type.2c7: type = fn_type @Core.FloatLiteral.as.ImplicitAs.impl.Convert, @Core.FloatLiteral.as.ImplicitAs.impl(%int_64) [concrete] // CHECK:STDOUT: %Core.FloatLiteral.as.ImplicitAs.impl.Convert.239: %Core.FloatLiteral.as.ImplicitAs.impl.Convert.type.2c7 = struct_value () [concrete] // CHECK:STDOUT: %ImplicitAs.facet: %ImplicitAs.type.4a8 = facet_value Core.FloatLiteral, (%ImplicitAs.impl_witness.cb2) [concrete] // CHECK:STDOUT: %ImplicitAs.WithSelf.Convert.type.a33: type = fn_type @ImplicitAs.WithSelf.Convert, @ImplicitAs.WithSelf(%f64.d77, %ImplicitAs.facet) [concrete] // CHECK:STDOUT: %.6c5: type = fn_type_with_self_type %ImplicitAs.WithSelf.Convert.type.a33, %ImplicitAs.facet [concrete] // CHECK:STDOUT: %Core.FloatLiteral.as.ImplicitAs.impl.Convert.bound.8c6: = bound_method %float.1f7, %Core.FloatLiteral.as.ImplicitAs.impl.Convert.239 [concrete] // CHECK:STDOUT: %Core.FloatLiteral.as.ImplicitAs.impl.Convert.specific_fn: = specific_function %Core.FloatLiteral.as.ImplicitAs.impl.Convert.239, @Core.FloatLiteral.as.ImplicitAs.impl.Convert(%int_64) [concrete] // CHECK:STDOUT: %bound_method.f2b: = bound_method %float.1f7, %Core.FloatLiteral.as.ImplicitAs.impl.Convert.specific_fn [concrete] // CHECK:STDOUT: %float.0a8: %f64.d77 = float_value 0 [concrete] // CHECK:STDOUT: %float.6da: Core.FloatLiteral = float_literal_value 10e-1 [concrete] // CHECK:STDOUT: %Core.FloatLiteral.as.ImplicitAs.impl.Convert.bound.1ed: = bound_method %float.6da, %Core.FloatLiteral.as.ImplicitAs.impl.Convert.239 [concrete] // CHECK:STDOUT: %bound_method.50c: = bound_method %float.6da, %Core.FloatLiteral.as.ImplicitAs.impl.Convert.specific_fn [concrete] // CHECK:STDOUT: %float.d20: %f64.d77 = float_value 1 [concrete] // CHECK:STDOUT: %float.12a: Core.FloatLiteral = float_literal_value 10e307 [concrete] // CHECK:STDOUT: %Core.FloatLiteral.as.ImplicitAs.impl.Convert.bound.dee: = bound_method %float.12a, %Core.FloatLiteral.as.ImplicitAs.impl.Convert.239 [concrete] // CHECK:STDOUT: %bound_method.d56: = bound_method %float.12a, %Core.FloatLiteral.as.ImplicitAs.impl.Convert.specific_fn [concrete] // CHECK:STDOUT: %float.bde: %f64.d77 = float_value 1.0E+308 [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Main.Float64ToFloat64: %Float64ToFloat64.type = import_ref Main//f64, Float64ToFloat64, loaded [concrete = constants.%Float64ToFloat64] // CHECK:STDOUT: %Core.import_ref.38a: @Core.FloatLiteral.as.ImplicitAs.impl.%Core.FloatLiteral.as.ImplicitAs.impl.Convert.type (%Core.FloatLiteral.as.ImplicitAs.impl.Convert.type.02f) = import_ref Core//prelude/parts/float, loc{{\d+_\d+}}, loaded [symbolic = @Core.FloatLiteral.as.ImplicitAs.impl.%Core.FloatLiteral.as.ImplicitAs.impl.Convert (constants.%Core.FloatLiteral.as.ImplicitAs.impl.Convert.1f0)] // CHECK:STDOUT: %ImplicitAs.impl_witness_table = impl_witness_table (%Core.import_ref.38a), @Core.FloatLiteral.as.ImplicitAs.impl [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %a.patt: %pattern_type.0ae = value_binding_pattern a [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %f64.loc6: type = type_literal constants.%f64.d77 [concrete = constants.%f64.d77] // CHECK:STDOUT: %.loc6_34.1: %f64.d77 = value_of_initializer @__global_init.%Float64ToFloat64.call.loc6 [concrete = constants.%float.0a8] // CHECK:STDOUT: %.loc6_34.2: %f64.d77 = converted @__global_init.%Float64ToFloat64.call.loc6, %.loc6_34.1 [concrete = constants.%float.0a8] // CHECK:STDOUT: %a: %f64.d77 = value_binding a, %.loc6_34.2 // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %b.patt: %pattern_type.0ae = value_binding_pattern b [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %f64.loc7: type = type_literal constants.%f64.d77 [concrete = constants.%f64.d77] // CHECK:STDOUT: %.loc7_34.1: %f64.d77 = value_of_initializer @__global_init.%Float64ToFloat64.call.loc7 [concrete = constants.%float.d20] // CHECK:STDOUT: %.loc7_34.2: %f64.d77 = converted @__global_init.%Float64ToFloat64.call.loc7, %.loc7_34.1 [concrete = constants.%float.d20] // CHECK:STDOUT: %b: %f64.d77 = value_binding b, %.loc7_34.2 // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %c.patt: %pattern_type.0ae = value_binding_pattern c [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %f64.loc8: type = type_literal constants.%f64.d77 [concrete = constants.%f64.d77] // CHECK:STDOUT: %.loc8_38.1: %f64.d77 = value_of_initializer @__global_init.%Float64ToFloat64.call.loc8 [concrete = constants.%float.bde] // CHECK:STDOUT: %.loc8_38.2: %f64.d77 = converted @__global_init.%Float64ToFloat64.call.loc8, %.loc8_38.1 [concrete = constants.%float.bde] // CHECK:STDOUT: %c: %f64.d77 = value_binding c, %.loc8_38.2 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @__global_init() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %Float64ToFloat64.ref.loc6: %Float64ToFloat64.type = name_ref Float64ToFloat64, imports.%Main.Float64ToFloat64 [concrete = constants.%Float64ToFloat64] // CHECK:STDOUT: %float.loc6: Core.FloatLiteral = float_literal_value 0e-1 [concrete = constants.%float.1f7] // CHECK:STDOUT: %impl.elem0.loc6: %.6c5 = impl_witness_access constants.%ImplicitAs.impl_witness.cb2, element0 [concrete = constants.%Core.FloatLiteral.as.ImplicitAs.impl.Convert.239] // CHECK:STDOUT: %bound_method.loc6_31.1: = bound_method %float.loc6, %impl.elem0.loc6 [concrete = constants.%Core.FloatLiteral.as.ImplicitAs.impl.Convert.bound.8c6] // CHECK:STDOUT: %specific_fn.loc6: = specific_function %impl.elem0.loc6, @Core.FloatLiteral.as.ImplicitAs.impl.Convert(constants.%int_64) [concrete = constants.%Core.FloatLiteral.as.ImplicitAs.impl.Convert.specific_fn] // CHECK:STDOUT: %bound_method.loc6_31.2: = bound_method %float.loc6, %specific_fn.loc6 [concrete = constants.%bound_method.f2b] // CHECK:STDOUT: %Core.FloatLiteral.as.ImplicitAs.impl.Convert.call.loc6: init %f64.d77 = call %bound_method.loc6_31.2(%float.loc6) [concrete = constants.%float.0a8] // CHECK:STDOUT: %.loc6_31.1: %f64.d77 = value_of_initializer %Core.FloatLiteral.as.ImplicitAs.impl.Convert.call.loc6 [concrete = constants.%float.0a8] // CHECK:STDOUT: %.loc6_31.2: %f64.d77 = converted %float.loc6, %.loc6_31.1 [concrete = constants.%float.0a8] // CHECK:STDOUT: %Float64ToFloat64.call.loc6: init %f64.d77 = call %Float64ToFloat64.ref.loc6(%.loc6_31.2) [concrete = constants.%float.0a8] // CHECK:STDOUT: %Float64ToFloat64.ref.loc7: %Float64ToFloat64.type = name_ref Float64ToFloat64, imports.%Main.Float64ToFloat64 [concrete = constants.%Float64ToFloat64] // CHECK:STDOUT: %float.loc7: Core.FloatLiteral = float_literal_value 10e-1 [concrete = constants.%float.6da] // CHECK:STDOUT: %impl.elem0.loc7: %.6c5 = impl_witness_access constants.%ImplicitAs.impl_witness.cb2, element0 [concrete = constants.%Core.FloatLiteral.as.ImplicitAs.impl.Convert.239] // CHECK:STDOUT: %bound_method.loc7_31.1: = bound_method %float.loc7, %impl.elem0.loc7 [concrete = constants.%Core.FloatLiteral.as.ImplicitAs.impl.Convert.bound.1ed] // CHECK:STDOUT: %specific_fn.loc7: = specific_function %impl.elem0.loc7, @Core.FloatLiteral.as.ImplicitAs.impl.Convert(constants.%int_64) [concrete = constants.%Core.FloatLiteral.as.ImplicitAs.impl.Convert.specific_fn] // CHECK:STDOUT: %bound_method.loc7_31.2: = bound_method %float.loc7, %specific_fn.loc7 [concrete = constants.%bound_method.50c] // CHECK:STDOUT: %Core.FloatLiteral.as.ImplicitAs.impl.Convert.call.loc7: init %f64.d77 = call %bound_method.loc7_31.2(%float.loc7) [concrete = constants.%float.d20] // CHECK:STDOUT: %.loc7_31.1: %f64.d77 = value_of_initializer %Core.FloatLiteral.as.ImplicitAs.impl.Convert.call.loc7 [concrete = constants.%float.d20] // CHECK:STDOUT: %.loc7_31.2: %f64.d77 = converted %float.loc7, %.loc7_31.1 [concrete = constants.%float.d20] // CHECK:STDOUT: %Float64ToFloat64.call.loc7: init %f64.d77 = call %Float64ToFloat64.ref.loc7(%.loc7_31.2) [concrete = constants.%float.d20] // CHECK:STDOUT: %Float64ToFloat64.ref.loc8: %Float64ToFloat64.type = name_ref Float64ToFloat64, imports.%Main.Float64ToFloat64 [concrete = constants.%Float64ToFloat64] // CHECK:STDOUT: %float.loc8: Core.FloatLiteral = float_literal_value 10e307 [concrete = constants.%float.12a] // CHECK:STDOUT: %impl.elem0.loc8: %.6c5 = impl_witness_access constants.%ImplicitAs.impl_witness.cb2, element0 [concrete = constants.%Core.FloatLiteral.as.ImplicitAs.impl.Convert.239] // CHECK:STDOUT: %bound_method.loc8_31.1: = bound_method %float.loc8, %impl.elem0.loc8 [concrete = constants.%Core.FloatLiteral.as.ImplicitAs.impl.Convert.bound.dee] // CHECK:STDOUT: %specific_fn.loc8: = specific_function %impl.elem0.loc8, @Core.FloatLiteral.as.ImplicitAs.impl.Convert(constants.%int_64) [concrete = constants.%Core.FloatLiteral.as.ImplicitAs.impl.Convert.specific_fn] // CHECK:STDOUT: %bound_method.loc8_31.2: = bound_method %float.loc8, %specific_fn.loc8 [concrete = constants.%bound_method.d56] // CHECK:STDOUT: %Core.FloatLiteral.as.ImplicitAs.impl.Convert.call.loc8: init %f64.d77 = call %bound_method.loc8_31.2(%float.loc8) [concrete = constants.%float.bde] // CHECK:STDOUT: %.loc8_31.1: %f64.d77 = value_of_initializer %Core.FloatLiteral.as.ImplicitAs.impl.Convert.call.loc8 [concrete = constants.%float.bde] // CHECK:STDOUT: %.loc8_31.2: %f64.d77 = converted %float.loc8, %.loc8_31.1 [concrete = constants.%float.bde] // CHECK:STDOUT: %Float64ToFloat64.call.loc8: init %f64.d77 = call %Float64ToFloat64.ref.loc8(%.loc8_31.2) [concrete = constants.%float.bde] // CHECK:STDOUT: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- identity_f32.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [concrete] // CHECK:STDOUT: %f32.97e: type = class_type @Float, @Float(%int_32) [concrete] // CHECK:STDOUT: %pattern_type.201: type = pattern_type %f32.97e [concrete] // CHECK:STDOUT: %Float32ToFloat32.type: type = fn_type @Float32ToFloat32 [concrete] // CHECK:STDOUT: %Float32ToFloat32: %Float32ToFloat32.type = struct_value () [concrete] // CHECK:STDOUT: %float.1f7: Core.FloatLiteral = float_literal_value 0e-1 [concrete] // CHECK:STDOUT: %ImplicitAs.type.223: type = facet_type <@ImplicitAs, @ImplicitAs(%f32.97e)> [concrete] // CHECK:STDOUT: %To: Core.IntLiteral = symbolic_binding To, 0 [symbolic] // CHECK:STDOUT: %Core.FloatLiteral.as.ImplicitAs.impl.Convert.type.02f: type = fn_type @Core.FloatLiteral.as.ImplicitAs.impl.Convert, @Core.FloatLiteral.as.ImplicitAs.impl(%To) [symbolic] // CHECK:STDOUT: %Core.FloatLiteral.as.ImplicitAs.impl.Convert.1f0: %Core.FloatLiteral.as.ImplicitAs.impl.Convert.type.02f = struct_value () [symbolic] // CHECK:STDOUT: %ImplicitAs.impl_witness.bc6: = impl_witness imports.%ImplicitAs.impl_witness_table, @Core.FloatLiteral.as.ImplicitAs.impl(%int_32) [concrete] // CHECK:STDOUT: %Core.FloatLiteral.as.ImplicitAs.impl.Convert.type.461: type = fn_type @Core.FloatLiteral.as.ImplicitAs.impl.Convert, @Core.FloatLiteral.as.ImplicitAs.impl(%int_32) [concrete] // CHECK:STDOUT: %Core.FloatLiteral.as.ImplicitAs.impl.Convert.e55: %Core.FloatLiteral.as.ImplicitAs.impl.Convert.type.461 = struct_value () [concrete] // CHECK:STDOUT: %ImplicitAs.facet: %ImplicitAs.type.223 = facet_value Core.FloatLiteral, (%ImplicitAs.impl_witness.bc6) [concrete] // CHECK:STDOUT: %ImplicitAs.WithSelf.Convert.type.e4d: type = fn_type @ImplicitAs.WithSelf.Convert, @ImplicitAs.WithSelf(%f32.97e, %ImplicitAs.facet) [concrete] // CHECK:STDOUT: %.98d: type = fn_type_with_self_type %ImplicitAs.WithSelf.Convert.type.e4d, %ImplicitAs.facet [concrete] // CHECK:STDOUT: %Core.FloatLiteral.as.ImplicitAs.impl.Convert.bound.c2d: = bound_method %float.1f7, %Core.FloatLiteral.as.ImplicitAs.impl.Convert.e55 [concrete] // CHECK:STDOUT: %Core.FloatLiteral.as.ImplicitAs.impl.Convert.specific_fn: = specific_function %Core.FloatLiteral.as.ImplicitAs.impl.Convert.e55, @Core.FloatLiteral.as.ImplicitAs.impl.Convert(%int_32) [concrete] // CHECK:STDOUT: %bound_method.577: = bound_method %float.1f7, %Core.FloatLiteral.as.ImplicitAs.impl.Convert.specific_fn [concrete] // CHECK:STDOUT: %float.4db: %f32.97e = float_value 0 [concrete] // CHECK:STDOUT: %float.6da: Core.FloatLiteral = float_literal_value 10e-1 [concrete] // CHECK:STDOUT: %Core.FloatLiteral.as.ImplicitAs.impl.Convert.bound.c4b: = bound_method %float.6da, %Core.FloatLiteral.as.ImplicitAs.impl.Convert.e55 [concrete] // CHECK:STDOUT: %bound_method.57d: = bound_method %float.6da, %Core.FloatLiteral.as.ImplicitAs.impl.Convert.specific_fn [concrete] // CHECK:STDOUT: %float.e3b: %f32.97e = float_value 1 [concrete] // CHECK:STDOUT: %float.8d2: Core.FloatLiteral = float_literal_value 10e37 [concrete] // CHECK:STDOUT: %Core.FloatLiteral.as.ImplicitAs.impl.Convert.bound.d1f: = bound_method %float.8d2, %Core.FloatLiteral.as.ImplicitAs.impl.Convert.e55 [concrete] // CHECK:STDOUT: %bound_method.bed: = bound_method %float.8d2, %Core.FloatLiteral.as.ImplicitAs.impl.Convert.specific_fn [concrete] // CHECK:STDOUT: %float.520: %f32.97e = float_value 9.99999968E+37 [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Main.Float32ToFloat32: %Float32ToFloat32.type = import_ref Main//f32, Float32ToFloat32, loaded [concrete = constants.%Float32ToFloat32] // CHECK:STDOUT: %Core.import_ref.38a: @Core.FloatLiteral.as.ImplicitAs.impl.%Core.FloatLiteral.as.ImplicitAs.impl.Convert.type (%Core.FloatLiteral.as.ImplicitAs.impl.Convert.type.02f) = import_ref Core//prelude/parts/float, loc{{\d+_\d+}}, loaded [symbolic = @Core.FloatLiteral.as.ImplicitAs.impl.%Core.FloatLiteral.as.ImplicitAs.impl.Convert (constants.%Core.FloatLiteral.as.ImplicitAs.impl.Convert.1f0)] // CHECK:STDOUT: %ImplicitAs.impl_witness_table = impl_witness_table (%Core.import_ref.38a), @Core.FloatLiteral.as.ImplicitAs.impl [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %a.patt: %pattern_type.201 = value_binding_pattern a [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %f32.loc6: type = type_literal constants.%f32.97e [concrete = constants.%f32.97e] // CHECK:STDOUT: %.loc6_34.1: %f32.97e = value_of_initializer @__global_init.%Float32ToFloat32.call.loc6 [concrete = constants.%float.4db] // CHECK:STDOUT: %.loc6_34.2: %f32.97e = converted @__global_init.%Float32ToFloat32.call.loc6, %.loc6_34.1 [concrete = constants.%float.4db] // CHECK:STDOUT: %a: %f32.97e = value_binding a, %.loc6_34.2 // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %b.patt: %pattern_type.201 = value_binding_pattern b [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %f32.loc7: type = type_literal constants.%f32.97e [concrete = constants.%f32.97e] // CHECK:STDOUT: %.loc7_34.1: %f32.97e = value_of_initializer @__global_init.%Float32ToFloat32.call.loc7 [concrete = constants.%float.e3b] // CHECK:STDOUT: %.loc7_34.2: %f32.97e = converted @__global_init.%Float32ToFloat32.call.loc7, %.loc7_34.1 [concrete = constants.%float.e3b] // CHECK:STDOUT: %b: %f32.97e = value_binding b, %.loc7_34.2 // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %c.patt: %pattern_type.201 = value_binding_pattern c [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %f32.loc8: type = type_literal constants.%f32.97e [concrete = constants.%f32.97e] // CHECK:STDOUT: %.loc8_37.1: %f32.97e = value_of_initializer @__global_init.%Float32ToFloat32.call.loc8 [concrete = constants.%float.520] // CHECK:STDOUT: %.loc8_37.2: %f32.97e = converted @__global_init.%Float32ToFloat32.call.loc8, %.loc8_37.1 [concrete = constants.%float.520] // CHECK:STDOUT: %c: %f32.97e = value_binding c, %.loc8_37.2 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @__global_init() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %Float32ToFloat32.ref.loc6: %Float32ToFloat32.type = name_ref Float32ToFloat32, imports.%Main.Float32ToFloat32 [concrete = constants.%Float32ToFloat32] // CHECK:STDOUT: %float.loc6: Core.FloatLiteral = float_literal_value 0e-1 [concrete = constants.%float.1f7] // CHECK:STDOUT: %impl.elem0.loc6: %.98d = impl_witness_access constants.%ImplicitAs.impl_witness.bc6, element0 [concrete = constants.%Core.FloatLiteral.as.ImplicitAs.impl.Convert.e55] // CHECK:STDOUT: %bound_method.loc6_31.1: = bound_method %float.loc6, %impl.elem0.loc6 [concrete = constants.%Core.FloatLiteral.as.ImplicitAs.impl.Convert.bound.c2d] // CHECK:STDOUT: %specific_fn.loc6: = specific_function %impl.elem0.loc6, @Core.FloatLiteral.as.ImplicitAs.impl.Convert(constants.%int_32) [concrete = constants.%Core.FloatLiteral.as.ImplicitAs.impl.Convert.specific_fn] // CHECK:STDOUT: %bound_method.loc6_31.2: = bound_method %float.loc6, %specific_fn.loc6 [concrete = constants.%bound_method.577] // CHECK:STDOUT: %Core.FloatLiteral.as.ImplicitAs.impl.Convert.call.loc6: init %f32.97e = call %bound_method.loc6_31.2(%float.loc6) [concrete = constants.%float.4db] // CHECK:STDOUT: %.loc6_31.1: %f32.97e = value_of_initializer %Core.FloatLiteral.as.ImplicitAs.impl.Convert.call.loc6 [concrete = constants.%float.4db] // CHECK:STDOUT: %.loc6_31.2: %f32.97e = converted %float.loc6, %.loc6_31.1 [concrete = constants.%float.4db] // CHECK:STDOUT: %Float32ToFloat32.call.loc6: init %f32.97e = call %Float32ToFloat32.ref.loc6(%.loc6_31.2) [concrete = constants.%float.4db] // CHECK:STDOUT: %Float32ToFloat32.ref.loc7: %Float32ToFloat32.type = name_ref Float32ToFloat32, imports.%Main.Float32ToFloat32 [concrete = constants.%Float32ToFloat32] // CHECK:STDOUT: %float.loc7: Core.FloatLiteral = float_literal_value 10e-1 [concrete = constants.%float.6da] // CHECK:STDOUT: %impl.elem0.loc7: %.98d = impl_witness_access constants.%ImplicitAs.impl_witness.bc6, element0 [concrete = constants.%Core.FloatLiteral.as.ImplicitAs.impl.Convert.e55] // CHECK:STDOUT: %bound_method.loc7_31.1: = bound_method %float.loc7, %impl.elem0.loc7 [concrete = constants.%Core.FloatLiteral.as.ImplicitAs.impl.Convert.bound.c4b] // CHECK:STDOUT: %specific_fn.loc7: = specific_function %impl.elem0.loc7, @Core.FloatLiteral.as.ImplicitAs.impl.Convert(constants.%int_32) [concrete = constants.%Core.FloatLiteral.as.ImplicitAs.impl.Convert.specific_fn] // CHECK:STDOUT: %bound_method.loc7_31.2: = bound_method %float.loc7, %specific_fn.loc7 [concrete = constants.%bound_method.57d] // CHECK:STDOUT: %Core.FloatLiteral.as.ImplicitAs.impl.Convert.call.loc7: init %f32.97e = call %bound_method.loc7_31.2(%float.loc7) [concrete = constants.%float.e3b] // CHECK:STDOUT: %.loc7_31.1: %f32.97e = value_of_initializer %Core.FloatLiteral.as.ImplicitAs.impl.Convert.call.loc7 [concrete = constants.%float.e3b] // CHECK:STDOUT: %.loc7_31.2: %f32.97e = converted %float.loc7, %.loc7_31.1 [concrete = constants.%float.e3b] // CHECK:STDOUT: %Float32ToFloat32.call.loc7: init %f32.97e = call %Float32ToFloat32.ref.loc7(%.loc7_31.2) [concrete = constants.%float.e3b] // CHECK:STDOUT: %Float32ToFloat32.ref.loc8: %Float32ToFloat32.type = name_ref Float32ToFloat32, imports.%Main.Float32ToFloat32 [concrete = constants.%Float32ToFloat32] // CHECK:STDOUT: %float.loc8: Core.FloatLiteral = float_literal_value 10e37 [concrete = constants.%float.8d2] // CHECK:STDOUT: %impl.elem0.loc8: %.98d = impl_witness_access constants.%ImplicitAs.impl_witness.bc6, element0 [concrete = constants.%Core.FloatLiteral.as.ImplicitAs.impl.Convert.e55] // CHECK:STDOUT: %bound_method.loc8_31.1: = bound_method %float.loc8, %impl.elem0.loc8 [concrete = constants.%Core.FloatLiteral.as.ImplicitAs.impl.Convert.bound.d1f] // CHECK:STDOUT: %specific_fn.loc8: = specific_function %impl.elem0.loc8, @Core.FloatLiteral.as.ImplicitAs.impl.Convert(constants.%int_32) [concrete = constants.%Core.FloatLiteral.as.ImplicitAs.impl.Convert.specific_fn] // CHECK:STDOUT: %bound_method.loc8_31.2: = bound_method %float.loc8, %specific_fn.loc8 [concrete = constants.%bound_method.bed] // CHECK:STDOUT: %Core.FloatLiteral.as.ImplicitAs.impl.Convert.call.loc8: init %f32.97e = call %bound_method.loc8_31.2(%float.loc8) [concrete = constants.%float.520] // CHECK:STDOUT: %.loc8_31.1: %f32.97e = value_of_initializer %Core.FloatLiteral.as.ImplicitAs.impl.Convert.call.loc8 [concrete = constants.%float.520] // CHECK:STDOUT: %.loc8_31.2: %f32.97e = converted %float.loc8, %.loc8_31.1 [concrete = constants.%float.520] // CHECK:STDOUT: %Float32ToFloat32.call.loc8: init %f32.97e = call %Float32ToFloat32.ref.loc8(%.loc8_31.2) [concrete = constants.%float.520] // CHECK:STDOUT: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- truncate.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [concrete] // CHECK:STDOUT: %f32.97e: type = class_type @Float, @Float(%int_32) [concrete] // CHECK:STDOUT: %pattern_type.201: type = pattern_type %f32.97e [concrete] // CHECK:STDOUT: %Float64ToFloat32.type: type = fn_type @Float64ToFloat32 [concrete] // CHECK:STDOUT: %Float64ToFloat32: %Float64ToFloat32.type = struct_value () [concrete] // CHECK:STDOUT: %int_64: Core.IntLiteral = int_value 64 [concrete] // CHECK:STDOUT: %f64.d77: type = class_type @Float, @Float(%int_64) [concrete] // CHECK:STDOUT: %float.6da: Core.FloatLiteral = float_literal_value 10e-1 [concrete] // CHECK:STDOUT: %ImplicitAs.type.4a8: type = facet_type <@ImplicitAs, @ImplicitAs(%f64.d77)> [concrete] // CHECK:STDOUT: %To: Core.IntLiteral = symbolic_binding To, 0 [symbolic] // CHECK:STDOUT: %Core.FloatLiteral.as.ImplicitAs.impl.Convert.type.02f: type = fn_type @Core.FloatLiteral.as.ImplicitAs.impl.Convert, @Core.FloatLiteral.as.ImplicitAs.impl(%To) [symbolic] // CHECK:STDOUT: %Core.FloatLiteral.as.ImplicitAs.impl.Convert.1f0: %Core.FloatLiteral.as.ImplicitAs.impl.Convert.type.02f = struct_value () [symbolic] // CHECK:STDOUT: %ImplicitAs.impl_witness.cb2: = impl_witness imports.%ImplicitAs.impl_witness_table, @Core.FloatLiteral.as.ImplicitAs.impl(%int_64) [concrete] // CHECK:STDOUT: %Core.FloatLiteral.as.ImplicitAs.impl.Convert.type.2c7: type = fn_type @Core.FloatLiteral.as.ImplicitAs.impl.Convert, @Core.FloatLiteral.as.ImplicitAs.impl(%int_64) [concrete] // CHECK:STDOUT: %Core.FloatLiteral.as.ImplicitAs.impl.Convert.239: %Core.FloatLiteral.as.ImplicitAs.impl.Convert.type.2c7 = struct_value () [concrete] // CHECK:STDOUT: %ImplicitAs.facet: %ImplicitAs.type.4a8 = facet_value Core.FloatLiteral, (%ImplicitAs.impl_witness.cb2) [concrete] // CHECK:STDOUT: %ImplicitAs.WithSelf.Convert.type.a33: type = fn_type @ImplicitAs.WithSelf.Convert, @ImplicitAs.WithSelf(%f64.d77, %ImplicitAs.facet) [concrete] // CHECK:STDOUT: %.6c5: type = fn_type_with_self_type %ImplicitAs.WithSelf.Convert.type.a33, %ImplicitAs.facet [concrete] // CHECK:STDOUT: %Core.FloatLiteral.as.ImplicitAs.impl.Convert.bound: = bound_method %float.6da, %Core.FloatLiteral.as.ImplicitAs.impl.Convert.239 [concrete] // CHECK:STDOUT: %Core.FloatLiteral.as.ImplicitAs.impl.Convert.specific_fn: = specific_function %Core.FloatLiteral.as.ImplicitAs.impl.Convert.239, @Core.FloatLiteral.as.ImplicitAs.impl.Convert(%int_64) [concrete] // CHECK:STDOUT: %bound_method: = bound_method %float.6da, %Core.FloatLiteral.as.ImplicitAs.impl.Convert.specific_fn [concrete] // CHECK:STDOUT: %float.d20: %f64.d77 = float_value 1 [concrete] // CHECK:STDOUT: %float.e3b: %f32.97e = float_value 1 [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Main.Float64ToFloat32: %Float64ToFloat32.type = import_ref Main//f32, Float64ToFloat32, loaded [concrete = constants.%Float64ToFloat32] // CHECK:STDOUT: %Core.import_ref.38a: @Core.FloatLiteral.as.ImplicitAs.impl.%Core.FloatLiteral.as.ImplicitAs.impl.Convert.type (%Core.FloatLiteral.as.ImplicitAs.impl.Convert.type.02f) = import_ref Core//prelude/parts/float, loc{{\d+_\d+}}, loaded [symbolic = @Core.FloatLiteral.as.ImplicitAs.impl.%Core.FloatLiteral.as.ImplicitAs.impl.Convert (constants.%Core.FloatLiteral.as.ImplicitAs.impl.Convert.1f0)] // CHECK:STDOUT: %ImplicitAs.impl_witness_table = impl_witness_table (%Core.import_ref.38a), @Core.FloatLiteral.as.ImplicitAs.impl [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %a.patt: %pattern_type.201 = value_binding_pattern a [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %f32: type = type_literal constants.%f32.97e [concrete = constants.%f32.97e] // CHECK:STDOUT: %.loc6_34.1: %f32.97e = value_of_initializer @__global_init.%Float64ToFloat32.call [concrete = constants.%float.e3b] // CHECK:STDOUT: %.loc6_34.2: %f32.97e = converted @__global_init.%Float64ToFloat32.call, %.loc6_34.1 [concrete = constants.%float.e3b] // CHECK:STDOUT: %a: %f32.97e = value_binding a, %.loc6_34.2 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @__global_init() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %Float64ToFloat32.ref: %Float64ToFloat32.type = name_ref Float64ToFloat32, imports.%Main.Float64ToFloat32 [concrete = constants.%Float64ToFloat32] // CHECK:STDOUT: %float: Core.FloatLiteral = float_literal_value 10e-1 [concrete = constants.%float.6da] // CHECK:STDOUT: %impl.elem0: %.6c5 = impl_witness_access constants.%ImplicitAs.impl_witness.cb2, element0 [concrete = constants.%Core.FloatLiteral.as.ImplicitAs.impl.Convert.239] // CHECK:STDOUT: %bound_method.loc6_31.1: = bound_method %float, %impl.elem0 [concrete = constants.%Core.FloatLiteral.as.ImplicitAs.impl.Convert.bound] // CHECK:STDOUT: %specific_fn: = specific_function %impl.elem0, @Core.FloatLiteral.as.ImplicitAs.impl.Convert(constants.%int_64) [concrete = constants.%Core.FloatLiteral.as.ImplicitAs.impl.Convert.specific_fn] // CHECK:STDOUT: %bound_method.loc6_31.2: = bound_method %float, %specific_fn [concrete = constants.%bound_method] // CHECK:STDOUT: %Core.FloatLiteral.as.ImplicitAs.impl.Convert.call: init %f64.d77 = call %bound_method.loc6_31.2(%float) [concrete = constants.%float.d20] // CHECK:STDOUT: %.loc6_31.1: %f64.d77 = value_of_initializer %Core.FloatLiteral.as.ImplicitAs.impl.Convert.call [concrete = constants.%float.d20] // CHECK:STDOUT: %.loc6_31.2: %f64.d77 = converted %float, %.loc6_31.1 [concrete = constants.%float.d20] // CHECK:STDOUT: %Float64ToFloat32.call: init %f32.97e = call %Float64ToFloat32.ref(%.loc6_31.2) [concrete = constants.%float.e3b] // CHECK:STDOUT: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- fail_truncate_overflow.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [concrete] // CHECK:STDOUT: %f32.97e: type = class_type @Float, @Float(%int_32) [concrete] // CHECK:STDOUT: %pattern_type.201: type = pattern_type %f32.97e [concrete] // CHECK:STDOUT: %Float64ToFloat32.type: type = fn_type @Float64ToFloat32 [concrete] // CHECK:STDOUT: %Float64ToFloat32: %Float64ToFloat32.type = struct_value () [concrete] // CHECK:STDOUT: %int_64: Core.IntLiteral = int_value 64 [concrete] // CHECK:STDOUT: %f64.d77: type = class_type @Float, @Float(%int_64) [concrete] // CHECK:STDOUT: %pattern_type.0ae: type = pattern_type %f64.d77 [concrete] // CHECK:STDOUT: %float.cfcfd2.1: Core.FloatLiteral = float_literal_value 10e38 [concrete] // CHECK:STDOUT: %ImplicitAs.type.4a8: type = facet_type <@ImplicitAs, @ImplicitAs(%f64.d77)> [concrete] // CHECK:STDOUT: %To: Core.IntLiteral = symbolic_binding To, 0 [symbolic] // CHECK:STDOUT: %Core.FloatLiteral.as.ImplicitAs.impl.Convert.type.02f: type = fn_type @Core.FloatLiteral.as.ImplicitAs.impl.Convert, @Core.FloatLiteral.as.ImplicitAs.impl(%To) [symbolic] // CHECK:STDOUT: %Core.FloatLiteral.as.ImplicitAs.impl.Convert.1f0: %Core.FloatLiteral.as.ImplicitAs.impl.Convert.type.02f = struct_value () [symbolic] // CHECK:STDOUT: %ImplicitAs.impl_witness.cb2: = impl_witness imports.%ImplicitAs.impl_witness_table, @Core.FloatLiteral.as.ImplicitAs.impl(%int_64) [concrete] // CHECK:STDOUT: %Core.FloatLiteral.as.ImplicitAs.impl.Convert.type.2c7: type = fn_type @Core.FloatLiteral.as.ImplicitAs.impl.Convert, @Core.FloatLiteral.as.ImplicitAs.impl(%int_64) [concrete] // CHECK:STDOUT: %Core.FloatLiteral.as.ImplicitAs.impl.Convert.239: %Core.FloatLiteral.as.ImplicitAs.impl.Convert.type.2c7 = struct_value () [concrete] // CHECK:STDOUT: %ImplicitAs.facet: %ImplicitAs.type.4a8 = facet_value Core.FloatLiteral, (%ImplicitAs.impl_witness.cb2) [concrete] // CHECK:STDOUT: %ImplicitAs.WithSelf.Convert.type.a33: type = fn_type @ImplicitAs.WithSelf.Convert, @ImplicitAs.WithSelf(%f64.d77, %ImplicitAs.facet) [concrete] // CHECK:STDOUT: %.6c5: type = fn_type_with_self_type %ImplicitAs.WithSelf.Convert.type.a33, %ImplicitAs.facet [concrete] // CHECK:STDOUT: %Core.FloatLiteral.as.ImplicitAs.impl.Convert.bound: = bound_method %float.cfcfd2.1, %Core.FloatLiteral.as.ImplicitAs.impl.Convert.239 [concrete] // CHECK:STDOUT: %Core.FloatLiteral.as.ImplicitAs.impl.Convert.specific_fn: = specific_function %Core.FloatLiteral.as.ImplicitAs.impl.Convert.239, @Core.FloatLiteral.as.ImplicitAs.impl.Convert(%int_64) [concrete] // CHECK:STDOUT: %bound_method: = bound_method %float.cfcfd2.1, %Core.FloatLiteral.as.ImplicitAs.impl.Convert.specific_fn [concrete] // CHECK:STDOUT: %float.c37: %f64.d77 = float_value 9.9999999999999994E+38 [concrete] // CHECK:STDOUT: %FloatLiteralToFloat32.type: type = fn_type @FloatLiteralToFloat32 [concrete] // CHECK:STDOUT: %FloatLiteralToFloat32: %FloatLiteralToFloat32.type = struct_value () [concrete] // CHECK:STDOUT: %float.cfcfd2.2: Core.FloatLiteral = float_literal_value 10e38 [concrete] // CHECK:STDOUT: %FloatLiteralToFloat64.type: type = fn_type @FloatLiteralToFloat64 [concrete] // CHECK:STDOUT: %FloatLiteralToFloat64: %FloatLiteralToFloat64.type = struct_value () [concrete] // CHECK:STDOUT: %float.82f: Core.FloatLiteral = float_literal_value 10e308 [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Main.FloatLiteralToFloat32: %FloatLiteralToFloat32.type = import_ref Main//f32, FloatLiteralToFloat32, loaded [concrete = constants.%FloatLiteralToFloat32] // CHECK:STDOUT: %Main.Float64ToFloat32: %Float64ToFloat32.type = import_ref Main//f32, Float64ToFloat32, loaded [concrete = constants.%Float64ToFloat32] // CHECK:STDOUT: %Main.FloatLiteralToFloat64: %FloatLiteralToFloat64.type = import_ref Main//f64, FloatLiteralToFloat64, loaded [concrete = constants.%FloatLiteralToFloat64] // CHECK:STDOUT: %Core.import_ref.38a: @Core.FloatLiteral.as.ImplicitAs.impl.%Core.FloatLiteral.as.ImplicitAs.impl.Convert.type (%Core.FloatLiteral.as.ImplicitAs.impl.Convert.type.02f) = import_ref Core//prelude/parts/float, loc{{\d+_\d+}}, loaded [symbolic = @Core.FloatLiteral.as.ImplicitAs.impl.%Core.FloatLiteral.as.ImplicitAs.impl.Convert (constants.%Core.FloatLiteral.as.ImplicitAs.impl.Convert.1f0)] // CHECK:STDOUT: %ImplicitAs.impl_witness_table = impl_witness_table (%Core.import_ref.38a), @Core.FloatLiteral.as.ImplicitAs.impl [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %a.patt: %pattern_type.201 = value_binding_pattern a [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %f32.loc11: type = type_literal constants.%f32.97e [concrete = constants.%f32.97e] // CHECK:STDOUT: %.loc11_37.1: %f32.97e = value_of_initializer @__global_init.%Float64ToFloat32.call [concrete = ] // CHECK:STDOUT: %.loc11_37.2: %f32.97e = converted @__global_init.%Float64ToFloat32.call, %.loc11_37.1 [concrete = ] // CHECK:STDOUT: %a: %f32.97e = value_binding a, %.loc11_37.2 [concrete = ] // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %b.patt: %pattern_type.201 = value_binding_pattern b [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %f32.loc16: type = type_literal constants.%f32.97e [concrete = constants.%f32.97e] // CHECK:STDOUT: %.loc16_42.1: %f32.97e = value_of_initializer @__global_init.%FloatLiteralToFloat32.call [concrete = ] // CHECK:STDOUT: %.loc16_42.2: %f32.97e = converted @__global_init.%FloatLiteralToFloat32.call, %.loc16_42.1 [concrete = ] // CHECK:STDOUT: %b: %f32.97e = value_binding b, %.loc16_42.2 [concrete = ] // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %c.patt: %pattern_type.0ae = value_binding_pattern c [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %f64: type = type_literal constants.%f64.d77 [concrete = constants.%f64.d77] // CHECK:STDOUT: %.loc21_43.1: %f64.d77 = value_of_initializer @__global_init.%FloatLiteralToFloat64.call [concrete = ] // CHECK:STDOUT: %.loc21_43.2: %f64.d77 = converted @__global_init.%FloatLiteralToFloat64.call, %.loc21_43.1 [concrete = ] // CHECK:STDOUT: %c: %f64.d77 = value_binding c, %.loc21_43.2 [concrete = ] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @__global_init() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %Float64ToFloat32.ref: %Float64ToFloat32.type = name_ref Float64ToFloat32, imports.%Main.Float64ToFloat32 [concrete = constants.%Float64ToFloat32] // CHECK:STDOUT: %float.loc11: Core.FloatLiteral = float_literal_value 10e38 [concrete = constants.%float.cfcfd2.1] // CHECK:STDOUT: %impl.elem0: %.6c5 = impl_witness_access constants.%ImplicitAs.impl_witness.cb2, element0 [concrete = constants.%Core.FloatLiteral.as.ImplicitAs.impl.Convert.239] // CHECK:STDOUT: %bound_method.loc11_31.1: = bound_method %float.loc11, %impl.elem0 [concrete = constants.%Core.FloatLiteral.as.ImplicitAs.impl.Convert.bound] // CHECK:STDOUT: %specific_fn: = specific_function %impl.elem0, @Core.FloatLiteral.as.ImplicitAs.impl.Convert(constants.%int_64) [concrete = constants.%Core.FloatLiteral.as.ImplicitAs.impl.Convert.specific_fn] // CHECK:STDOUT: %bound_method.loc11_31.2: = bound_method %float.loc11, %specific_fn [concrete = constants.%bound_method] // CHECK:STDOUT: %Core.FloatLiteral.as.ImplicitAs.impl.Convert.call: init %f64.d77 = call %bound_method.loc11_31.2(%float.loc11) [concrete = constants.%float.c37] // CHECK:STDOUT: %.loc11_31.1: %f64.d77 = value_of_initializer %Core.FloatLiteral.as.ImplicitAs.impl.Convert.call [concrete = constants.%float.c37] // CHECK:STDOUT: %.loc11_31.2: %f64.d77 = converted %float.loc11, %.loc11_31.1 [concrete = constants.%float.c37] // CHECK:STDOUT: %Float64ToFloat32.call: init %f32.97e = call %Float64ToFloat32.ref(%.loc11_31.2) [concrete = ] // CHECK:STDOUT: %FloatLiteralToFloat32.ref: %FloatLiteralToFloat32.type = name_ref FloatLiteralToFloat32, imports.%Main.FloatLiteralToFloat32 [concrete = constants.%FloatLiteralToFloat32] // CHECK:STDOUT: %float.loc16: Core.FloatLiteral = float_literal_value 10e38 [concrete = constants.%float.cfcfd2.2] // CHECK:STDOUT: %FloatLiteralToFloat32.call: init %f32.97e = call %FloatLiteralToFloat32.ref(%float.loc16) [concrete = ] // CHECK:STDOUT: %FloatLiteralToFloat64.ref: %FloatLiteralToFloat64.type = name_ref FloatLiteralToFloat64, imports.%Main.FloatLiteralToFloat64 [concrete = constants.%FloatLiteralToFloat64] // CHECK:STDOUT: %float.loc21: Core.FloatLiteral = float_literal_value 10e308 [concrete = constants.%float.82f] // CHECK:STDOUT: %FloatLiteralToFloat64.call: init %f64.d77 = call %FloatLiteralToFloat64.ref(%float.loc21) [concrete = ] // CHECK:STDOUT: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- extend.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %int_64: Core.IntLiteral = int_value 64 [concrete] // CHECK:STDOUT: %f64.d77: type = class_type @Float, @Float(%int_64) [concrete] // CHECK:STDOUT: %pattern_type.0ae: type = pattern_type %f64.d77 [concrete] // CHECK:STDOUT: %Float32ToFloat64.type: type = fn_type @Float32ToFloat64 [concrete] // CHECK:STDOUT: %Float32ToFloat64: %Float32ToFloat64.type = struct_value () [concrete] // CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [concrete] // CHECK:STDOUT: %f32.97e: type = class_type @Float, @Float(%int_32) [concrete] // CHECK:STDOUT: %float.6da: Core.FloatLiteral = float_literal_value 10e-1 [concrete] // CHECK:STDOUT: %ImplicitAs.type.223: type = facet_type <@ImplicitAs, @ImplicitAs(%f32.97e)> [concrete] // CHECK:STDOUT: %To: Core.IntLiteral = symbolic_binding To, 0 [symbolic] // CHECK:STDOUT: %Core.FloatLiteral.as.ImplicitAs.impl.Convert.type.02f: type = fn_type @Core.FloatLiteral.as.ImplicitAs.impl.Convert, @Core.FloatLiteral.as.ImplicitAs.impl(%To) [symbolic] // CHECK:STDOUT: %Core.FloatLiteral.as.ImplicitAs.impl.Convert.1f0: %Core.FloatLiteral.as.ImplicitAs.impl.Convert.type.02f = struct_value () [symbolic] // CHECK:STDOUT: %ImplicitAs.impl_witness.bc6: = impl_witness imports.%ImplicitAs.impl_witness_table, @Core.FloatLiteral.as.ImplicitAs.impl(%int_32) [concrete] // CHECK:STDOUT: %Core.FloatLiteral.as.ImplicitAs.impl.Convert.type.461: type = fn_type @Core.FloatLiteral.as.ImplicitAs.impl.Convert, @Core.FloatLiteral.as.ImplicitAs.impl(%int_32) [concrete] // CHECK:STDOUT: %Core.FloatLiteral.as.ImplicitAs.impl.Convert.e55: %Core.FloatLiteral.as.ImplicitAs.impl.Convert.type.461 = struct_value () [concrete] // CHECK:STDOUT: %ImplicitAs.facet: %ImplicitAs.type.223 = facet_value Core.FloatLiteral, (%ImplicitAs.impl_witness.bc6) [concrete] // CHECK:STDOUT: %ImplicitAs.WithSelf.Convert.type.e4d: type = fn_type @ImplicitAs.WithSelf.Convert, @ImplicitAs.WithSelf(%f32.97e, %ImplicitAs.facet) [concrete] // CHECK:STDOUT: %.98d: type = fn_type_with_self_type %ImplicitAs.WithSelf.Convert.type.e4d, %ImplicitAs.facet [concrete] // CHECK:STDOUT: %Core.FloatLiteral.as.ImplicitAs.impl.Convert.bound.c4b: = bound_method %float.6da, %Core.FloatLiteral.as.ImplicitAs.impl.Convert.e55 [concrete] // CHECK:STDOUT: %Core.FloatLiteral.as.ImplicitAs.impl.Convert.specific_fn: = specific_function %Core.FloatLiteral.as.ImplicitAs.impl.Convert.e55, @Core.FloatLiteral.as.ImplicitAs.impl.Convert(%int_32) [concrete] // CHECK:STDOUT: %bound_method.57d: = bound_method %float.6da, %Core.FloatLiteral.as.ImplicitAs.impl.Convert.specific_fn [concrete] // CHECK:STDOUT: %float.e3b: %f32.97e = float_value 1 [concrete] // CHECK:STDOUT: %float.d20: %f64.d77 = float_value 1 [concrete] // CHECK:STDOUT: %float.c02: Core.FloatLiteral = float_literal_value 10e29 [concrete] // CHECK:STDOUT: %Core.FloatLiteral.as.ImplicitAs.impl.Convert.bound.9ad: = bound_method %float.c02, %Core.FloatLiteral.as.ImplicitAs.impl.Convert.e55 [concrete] // CHECK:STDOUT: %bound_method.601: = bound_method %float.c02, %Core.FloatLiteral.as.ImplicitAs.impl.Convert.specific_fn [concrete] // CHECK:STDOUT: %float.7d4: %f32.97e = float_value 1.00000002E+30 [concrete] // CHECK:STDOUT: %float.6a7: %f64.d77 = float_value 1.0000000150474662E+30 [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Main.Float32ToFloat64: %Float32ToFloat64.type = import_ref Main//f32, Float32ToFloat64, loaded [concrete = constants.%Float32ToFloat64] // CHECK:STDOUT: %Core.import_ref.38a: @Core.FloatLiteral.as.ImplicitAs.impl.%Core.FloatLiteral.as.ImplicitAs.impl.Convert.type (%Core.FloatLiteral.as.ImplicitAs.impl.Convert.type.02f) = import_ref Core//prelude/parts/float, loc{{\d+_\d+}}, loaded [symbolic = @Core.FloatLiteral.as.ImplicitAs.impl.%Core.FloatLiteral.as.ImplicitAs.impl.Convert (constants.%Core.FloatLiteral.as.ImplicitAs.impl.Convert.1f0)] // CHECK:STDOUT: %ImplicitAs.impl_witness_table = impl_witness_table (%Core.import_ref.38a), @Core.FloatLiteral.as.ImplicitAs.impl [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %a.patt: %pattern_type.0ae = value_binding_pattern a [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %f64.loc6: type = type_literal constants.%f64.d77 [concrete = constants.%f64.d77] // CHECK:STDOUT: %.loc6_34.1: %f64.d77 = value_of_initializer @__global_init.%Float32ToFloat64.call.loc6 [concrete = constants.%float.d20] // CHECK:STDOUT: %.loc6_34.2: %f64.d77 = converted @__global_init.%Float32ToFloat64.call.loc6, %.loc6_34.1 [concrete = constants.%float.d20] // CHECK:STDOUT: %a: %f64.d77 = value_binding a, %.loc6_34.2 // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %b.patt: %pattern_type.0ae = value_binding_pattern b [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %f64.loc7: type = type_literal constants.%f64.d77 [concrete = constants.%f64.d77] // CHECK:STDOUT: %.loc7_37.1: %f64.d77 = value_of_initializer @__global_init.%Float32ToFloat64.call.loc7 [concrete = constants.%float.6a7] // CHECK:STDOUT: %.loc7_37.2: %f64.d77 = converted @__global_init.%Float32ToFloat64.call.loc7, %.loc7_37.1 [concrete = constants.%float.6a7] // CHECK:STDOUT: %b: %f64.d77 = value_binding b, %.loc7_37.2 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @__global_init() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %Float32ToFloat64.ref.loc6: %Float32ToFloat64.type = name_ref Float32ToFloat64, imports.%Main.Float32ToFloat64 [concrete = constants.%Float32ToFloat64] // CHECK:STDOUT: %float.loc6: Core.FloatLiteral = float_literal_value 10e-1 [concrete = constants.%float.6da] // CHECK:STDOUT: %impl.elem0.loc6: %.98d = impl_witness_access constants.%ImplicitAs.impl_witness.bc6, element0 [concrete = constants.%Core.FloatLiteral.as.ImplicitAs.impl.Convert.e55] // CHECK:STDOUT: %bound_method.loc6_31.1: = bound_method %float.loc6, %impl.elem0.loc6 [concrete = constants.%Core.FloatLiteral.as.ImplicitAs.impl.Convert.bound.c4b] // CHECK:STDOUT: %specific_fn.loc6: = specific_function %impl.elem0.loc6, @Core.FloatLiteral.as.ImplicitAs.impl.Convert(constants.%int_32) [concrete = constants.%Core.FloatLiteral.as.ImplicitAs.impl.Convert.specific_fn] // CHECK:STDOUT: %bound_method.loc6_31.2: = bound_method %float.loc6, %specific_fn.loc6 [concrete = constants.%bound_method.57d] // CHECK:STDOUT: %Core.FloatLiteral.as.ImplicitAs.impl.Convert.call.loc6: init %f32.97e = call %bound_method.loc6_31.2(%float.loc6) [concrete = constants.%float.e3b] // CHECK:STDOUT: %.loc6_31.1: %f32.97e = value_of_initializer %Core.FloatLiteral.as.ImplicitAs.impl.Convert.call.loc6 [concrete = constants.%float.e3b] // CHECK:STDOUT: %.loc6_31.2: %f32.97e = converted %float.loc6, %.loc6_31.1 [concrete = constants.%float.e3b] // CHECK:STDOUT: %Float32ToFloat64.call.loc6: init %f64.d77 = call %Float32ToFloat64.ref.loc6(%.loc6_31.2) [concrete = constants.%float.d20] // CHECK:STDOUT: %Float32ToFloat64.ref.loc7: %Float32ToFloat64.type = name_ref Float32ToFloat64, imports.%Main.Float32ToFloat64 [concrete = constants.%Float32ToFloat64] // CHECK:STDOUT: %float.loc7: Core.FloatLiteral = float_literal_value 10e29 [concrete = constants.%float.c02] // CHECK:STDOUT: %impl.elem0.loc7: %.98d = impl_witness_access constants.%ImplicitAs.impl_witness.bc6, element0 [concrete = constants.%Core.FloatLiteral.as.ImplicitAs.impl.Convert.e55] // CHECK:STDOUT: %bound_method.loc7_31.1: = bound_method %float.loc7, %impl.elem0.loc7 [concrete = constants.%Core.FloatLiteral.as.ImplicitAs.impl.Convert.bound.9ad] // CHECK:STDOUT: %specific_fn.loc7: = specific_function %impl.elem0.loc7, @Core.FloatLiteral.as.ImplicitAs.impl.Convert(constants.%int_32) [concrete = constants.%Core.FloatLiteral.as.ImplicitAs.impl.Convert.specific_fn] // CHECK:STDOUT: %bound_method.loc7_31.2: = bound_method %float.loc7, %specific_fn.loc7 [concrete = constants.%bound_method.601] // CHECK:STDOUT: %Core.FloatLiteral.as.ImplicitAs.impl.Convert.call.loc7: init %f32.97e = call %bound_method.loc7_31.2(%float.loc7) [concrete = constants.%float.7d4] // CHECK:STDOUT: %.loc7_31.1: %f32.97e = value_of_initializer %Core.FloatLiteral.as.ImplicitAs.impl.Convert.call.loc7 [concrete = constants.%float.7d4] // CHECK:STDOUT: %.loc7_31.2: %f32.97e = converted %float.loc7, %.loc7_31.1 [concrete = constants.%float.7d4] // CHECK:STDOUT: %Float32ToFloat64.call.loc7: init %f64.d77 = call %Float32ToFloat64.ref.loc7(%.loc7_31.2) [concrete = constants.%float.6a7] // CHECK:STDOUT: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- fail_not_constant.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %int_64: Core.IntLiteral = int_value 64 [concrete] // CHECK:STDOUT: %f64.d77: type = class_type @Float, @Float(%int_64) [concrete] // CHECK:STDOUT: %pattern_type.0ae: type = pattern_type %f64.d77 [concrete] // CHECK:STDOUT: %float.1f7: Core.FloatLiteral = float_literal_value 0e-1 [concrete] // CHECK:STDOUT: %ImplicitAs.type.4a8: type = facet_type <@ImplicitAs, @ImplicitAs(%f64.d77)> [concrete] // CHECK:STDOUT: %To: Core.IntLiteral = symbolic_binding To, 0 [symbolic] // CHECK:STDOUT: %Core.FloatLiteral.as.ImplicitAs.impl.Convert.type.02f: type = fn_type @Core.FloatLiteral.as.ImplicitAs.impl.Convert, @Core.FloatLiteral.as.ImplicitAs.impl(%To) [symbolic] // CHECK:STDOUT: %Core.FloatLiteral.as.ImplicitAs.impl.Convert.1f0: %Core.FloatLiteral.as.ImplicitAs.impl.Convert.type.02f = struct_value () [symbolic] // CHECK:STDOUT: %ImplicitAs.impl_witness.cb2: = impl_witness imports.%ImplicitAs.impl_witness_table, @Core.FloatLiteral.as.ImplicitAs.impl(%int_64) [concrete] // CHECK:STDOUT: %Core.FloatLiteral.as.ImplicitAs.impl.Convert.type.2c7: type = fn_type @Core.FloatLiteral.as.ImplicitAs.impl.Convert, @Core.FloatLiteral.as.ImplicitAs.impl(%int_64) [concrete] // CHECK:STDOUT: %Core.FloatLiteral.as.ImplicitAs.impl.Convert.239: %Core.FloatLiteral.as.ImplicitAs.impl.Convert.type.2c7 = struct_value () [concrete] // CHECK:STDOUT: %ImplicitAs.facet: %ImplicitAs.type.4a8 = facet_value Core.FloatLiteral, (%ImplicitAs.impl_witness.cb2) [concrete] // CHECK:STDOUT: %ImplicitAs.WithSelf.Convert.type.a33: type = fn_type @ImplicitAs.WithSelf.Convert, @ImplicitAs.WithSelf(%f64.d77, %ImplicitAs.facet) [concrete] // CHECK:STDOUT: %.6c5: type = fn_type_with_self_type %ImplicitAs.WithSelf.Convert.type.a33, %ImplicitAs.facet [concrete] // CHECK:STDOUT: %Core.FloatLiteral.as.ImplicitAs.impl.Convert.bound: = bound_method %float.1f7, %Core.FloatLiteral.as.ImplicitAs.impl.Convert.239 [concrete] // CHECK:STDOUT: %Core.FloatLiteral.as.ImplicitAs.impl.Convert.specific_fn: = specific_function %Core.FloatLiteral.as.ImplicitAs.impl.Convert.239, @Core.FloatLiteral.as.ImplicitAs.impl.Convert(%int_64) [concrete] // CHECK:STDOUT: %bound_method: = bound_method %float.1f7, %Core.FloatLiteral.as.ImplicitAs.impl.Convert.specific_fn [concrete] // CHECK:STDOUT: %float.0a8: %f64.d77 = float_value 0 [concrete] // CHECK:STDOUT: %Float64ToFloat64.type: type = fn_type @Float64ToFloat64 [concrete] // CHECK:STDOUT: %Float64ToFloat64: %Float64ToFloat64.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Main.Float64ToFloat64: %Float64ToFloat64.type = import_ref Main//f64, Float64ToFloat64, loaded [concrete = constants.%Float64ToFloat64] // CHECK:STDOUT: %Core.import_ref.38a: @Core.FloatLiteral.as.ImplicitAs.impl.%Core.FloatLiteral.as.ImplicitAs.impl.Convert.type (%Core.FloatLiteral.as.ImplicitAs.impl.Convert.type.02f) = import_ref Core//prelude/parts/float, loc{{\d+_\d+}}, loaded [symbolic = @Core.FloatLiteral.as.ImplicitAs.impl.%Core.FloatLiteral.as.ImplicitAs.impl.Convert (constants.%Core.FloatLiteral.as.ImplicitAs.impl.Convert.1f0)] // CHECK:STDOUT: %ImplicitAs.impl_witness_table = impl_witness_table (%Core.import_ref.38a), @Core.FloatLiteral.as.ImplicitAs.impl [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %not_constant_64.patt: %pattern_type.0ae = value_binding_pattern not_constant_64 [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %f64.loc6: type = type_literal constants.%f64.d77 [concrete = constants.%f64.d77] // CHECK:STDOUT: %impl.elem0: %.6c5 = impl_witness_access constants.%ImplicitAs.impl_witness.cb2, element0 [concrete = constants.%Core.FloatLiteral.as.ImplicitAs.impl.Convert.239] // CHECK:STDOUT: %bound_method.loc6_28.1: = bound_method @__global_init.%float, %impl.elem0 [concrete = constants.%Core.FloatLiteral.as.ImplicitAs.impl.Convert.bound] // CHECK:STDOUT: %specific_fn: = specific_function %impl.elem0, @Core.FloatLiteral.as.ImplicitAs.impl.Convert(constants.%int_64) [concrete = constants.%Core.FloatLiteral.as.ImplicitAs.impl.Convert.specific_fn] // CHECK:STDOUT: %bound_method.loc6_28.2: = bound_method @__global_init.%float, %specific_fn [concrete = constants.%bound_method] // CHECK:STDOUT: %Core.FloatLiteral.as.ImplicitAs.impl.Convert.call: init %f64.d77 = call %bound_method.loc6_28.2(@__global_init.%float) [concrete = constants.%float.0a8] // CHECK:STDOUT: %.loc6_28.1: %f64.d77 = value_of_initializer %Core.FloatLiteral.as.ImplicitAs.impl.Convert.call [concrete = constants.%float.0a8] // CHECK:STDOUT: %.loc6_28.2: %f64.d77 = converted @__global_init.%float, %.loc6_28.1 [concrete = constants.%float.0a8] // CHECK:STDOUT: %not_constant_64: %f64.d77 = value_binding not_constant_64, %.loc6_28.2 // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %convert_not_constant.patt: %pattern_type.0ae = value_binding_pattern convert_not_constant [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %f64.loc15: type = type_literal constants.%f64.d77 [concrete = constants.%f64.d77] // CHECK:STDOUT: %.loc15_65.1: %f64.d77 = value_of_initializer @__global_init.%Float64ToFloat64.call // CHECK:STDOUT: %.loc15_65.2: %f64.d77 = converted @__global_init.%Float64ToFloat64.call, %.loc15_65.1 // CHECK:STDOUT: %convert_not_constant: %f64.d77 = value_binding convert_not_constant, %.loc15_65.2 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @__global_init() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %float: Core.FloatLiteral = float_literal_value 0e-1 [concrete = constants.%float.1f7] // CHECK:STDOUT: %Float64ToFloat64.ref: %Float64ToFloat64.type = name_ref Float64ToFloat64, imports.%Main.Float64ToFloat64 [concrete = constants.%Float64ToFloat64] // CHECK:STDOUT: %not_constant_64.ref: %f64.d77 = name_ref not_constant_64, file.%not_constant_64 // CHECK:STDOUT: %Float64ToFloat64.call: init %f64.d77 = call %Float64ToFloat64.ref(%not_constant_64.ref) // CHECK:STDOUT: // CHECK:STDOUT: } // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/builtins/float/div.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/primitives.carbon // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/builtins/float/div.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/builtins/float/div.carbon // --- float_div.carbon library "[[@TEST_NAME]]"; fn Div(a: f64, b: f64) -> f64 = "float.div"; fn RuntimeCallIsValid(a: f64, b: f64) -> f64 { //@dump-sem-ir-begin return Div(a, b); //@dump-sem-ir-end } var a: f64 = Div(10.0, 2.5); let b: f64 = Div(1.0, 0.0); let c: f64 = Div(0.0, 0.0); // --- fail_too_few.carbon library "[[@TEST_NAME]]"; // CHECK:STDERR: fail_too_few.carbon:[[@LINE+4]]:1: error: invalid signature for builtin function "float.div" [InvalidBuiltinSignature] // CHECK:STDERR: fn TooFew(a: f64) -> f64 = "float.div"; // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: fn TooFew(a: f64) -> f64 = "float.div"; fn RuntimeCallIsValidTooFew(a: f64) -> f64 { return TooFew(a); } // --- fail_too_many.carbon library "[[@TEST_NAME]]"; // CHECK:STDERR: fail_too_many.carbon:[[@LINE+4]]:1: error: invalid signature for builtin function "float.div" [InvalidBuiltinSignature] // CHECK:STDERR: fn TooMany(a: f64, b: f64, c: f64) -> f64 = "float.div"; // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: fn TooMany(a: f64, b: f64, c: f64) -> f64 = "float.div"; fn RuntimeCallIsValidTooMany(a: f64, b: f64, c: f64) -> f64 { return TooMany(a, b, c); } // --- fail_bad_return_type.carbon library "[[@TEST_NAME]]"; // CHECK:STDERR: fail_bad_return_type.carbon:[[@LINE+4]]:1: error: invalid signature for builtin function "float.div" [InvalidBuiltinSignature] // CHECK:STDERR: fn BadReturnType(a: f64, b: f64) -> bool = "float.div"; // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: fn BadReturnType(a: f64, b: f64) -> bool = "float.div"; fn RuntimeCallIsValidBadReturnType(a: f64, b: f64) -> bool { return BadReturnType(a, b); } // --- fail_literal.carbon library "[[@TEST_NAME]]"; fn Literal() -> type = "float_literal.make_type"; // CHECK:STDERR: fail_literal.carbon:[[@LINE+4]]:1: error: invalid signature for builtin function "float.div" [InvalidBuiltinSignature] // CHECK:STDERR: fn DivLiteral(a: Literal(), b: Literal()) -> Literal() = "float.div"; // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: fn DivLiteral(a: Literal(), b: Literal()) -> Literal() = "float.div"; // CHECK:STDOUT: --- float_div.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %int_64: Core.IntLiteral = int_value 64 [concrete] // CHECK:STDOUT: %f64.d77: type = class_type @Float, @Float(%int_64) [concrete] // CHECK:STDOUT: %Div.type: type = fn_type @Div [concrete] // CHECK:STDOUT: %Div: %Div.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @RuntimeCallIsValid(%a.param: %f64.d77, %b.param: %f64.d77) -> out %return.param: %f64.d77 { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %Div.ref: %Div.type = name_ref Div, file.%Div.decl [concrete = constants.%Div] // CHECK:STDOUT: %a.ref: %f64.d77 = name_ref a, %a // CHECK:STDOUT: %b.ref: %f64.d77 = name_ref b, %b // CHECK:STDOUT: %Div.call: init %f64.d77 = call %Div.ref(%a.ref, %b.ref) // CHECK:STDOUT: return %Div.call // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @__global_init() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: // CHECK:STDOUT: } // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/builtins/float/div_assign.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/primitives.carbon // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/builtins/float/div_assign.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/builtins/float/div_assign.carbon // --- call.carbon library "[[@TEST_NAME]]"; fn Builtin(ref a: f64, b: f64) = "float.div_assign"; fn Call(ref a: f64, b: f64) { Builtin(ref a, b); } // --- fail_bad_decl.carbon library "[[@TEST_NAME]]"; // CHECK:STDERR: fail_bad_decl.carbon:[[@LINE+4]]:1: error: invalid signature for builtin function "float.div_assign" [InvalidBuiltinSignature] // CHECK:STDERR: fn NotRef(a: f64, b: f64) = "float.div_assign"; // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: fn NotRef(a: f64, b: f64) = "float.div_assign"; // CHECK:STDERR: fail_bad_decl.carbon:[[@LINE+4]]:1: error: invalid signature for builtin function "float.div_assign" [InvalidBuiltinSignature] // CHECK:STDERR: fn MixedTypes(ref a: f64, b: f32) = "float.div_assign"; // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: fn MixedTypes(ref a: f64, b: f32) = "float.div_assign"; // --- fail_literal.carbon library "[[@TEST_NAME]]"; fn Literal() -> type = "float_literal.make_type"; // CHECK:STDERR: fail_literal.carbon:[[@LINE+4]]:1: error: invalid signature for builtin function "float.div_assign" [InvalidBuiltinSignature] // CHECK:STDERR: fn LiteralRuntime(ref a: Literal(), b: Literal()) = "float.div_assign"; // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: fn LiteralRuntime(ref a: Literal(), b: Literal()) = "float.div_assign"; ================================================ FILE: toolchain/check/testdata/builtins/float/eq.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/primitives.carbon // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/builtins/float/eq.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/builtins/float/eq.carbon // --- float_eq.carbon fn Eq(a: f64, b: f64) -> bool = "float.eq"; class True {} class False {} fn F(true_: True, false_: False) { true_ as (if Eq(1.0, 1.0) then True else False); false_ as (if Eq(1.0, 2.0) then True else False); } fn RuntimeCallIsValid(a: f64, b: f64) -> bool { //@dump-sem-ir-begin return Eq(a, b); //@dump-sem-ir-end } // --- fail_bad_decl.carbon library "[[@TEST_NAME]]"; // CHECK:STDERR: fail_bad_decl.carbon:[[@LINE+4]]:1: error: invalid signature for builtin function "float.eq" [InvalidBuiltinSignature] // CHECK:STDERR: fn WrongResult(a: f64, b: f64) -> f64 = "float.eq"; // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: fn WrongResult(a: f64, b: f64) -> f64 = "float.eq"; // --- fail_literal.carbon library "[[@TEST_NAME]]"; // CHECK:STDERR: fail_literal.carbon:[[@LINE+4]]:1: error: invalid signature for builtin function "float.eq" [InvalidBuiltinSignature] // CHECK:STDERR: fn Eq(a: Core.FloatLiteral(), b: Core.FloatLiteral()) -> bool = "float.eq"; // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: fn Eq(a: Core.FloatLiteral(), b: Core.FloatLiteral()) -> bool = "float.eq"; // CHECK:STDOUT: --- float_eq.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %int_64: Core.IntLiteral = int_value 64 [concrete] // CHECK:STDOUT: %f64.d77: type = class_type @Float, @Float(%int_64) [concrete] // CHECK:STDOUT: %Eq.type: type = fn_type @Eq [concrete] // CHECK:STDOUT: %Eq: %Eq.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @RuntimeCallIsValid(%a.param: %f64.d77, %b.param: %f64.d77) -> out %return.param: bool { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %Eq.ref: %Eq.type = name_ref Eq, file.%Eq.decl [concrete = constants.%Eq] // CHECK:STDOUT: %a.ref: %f64.d77 = name_ref a, %a // CHECK:STDOUT: %b.ref: %f64.d77 = name_ref b, %b // CHECK:STDOUT: %Eq.call: init bool = call %Eq.ref(%a.ref, %b.ref) // CHECK:STDOUT: return %Eq.call // CHECK:STDOUT: } // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/builtins/float/greater.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/primitives.carbon // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/builtins/float/greater.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/builtins/float/greater.carbon // --- float_greater.carbon fn Greater(a: f64, b: f64) -> bool = "float.greater"; fn Negate(a: f64) -> f64 = "float.negate"; class True {} class False {} fn F(true_: True, false_: False) { false_ as (if Greater(1.0, 2.0) then True else False); false_ as (if Greater(1.0, 1.0) then True else False); true_ as (if Greater(1.0, 0.0) then True else False); false_ as (if Greater(Negate(1.0), 0.0) then True else False); true_ as (if Greater(0.0, Negate(1.0)) then True else False); } fn RuntimeCallIsValid(a: f64, b: f64) -> bool { //@dump-sem-ir-begin return Greater(a, b); //@dump-sem-ir-end } // CHECK:STDOUT: --- float_greater.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %int_64: Core.IntLiteral = int_value 64 [concrete] // CHECK:STDOUT: %f64.d77: type = class_type @Float, @Float(%int_64) [concrete] // CHECK:STDOUT: %Greater.type: type = fn_type @Greater [concrete] // CHECK:STDOUT: %Greater: %Greater.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @RuntimeCallIsValid(%a.param: %f64.d77, %b.param: %f64.d77) -> out %return.param: bool { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %Greater.ref: %Greater.type = name_ref Greater, file.%Greater.decl [concrete = constants.%Greater] // CHECK:STDOUT: %a.ref: %f64.d77 = name_ref a, %a // CHECK:STDOUT: %b.ref: %f64.d77 = name_ref b, %b // CHECK:STDOUT: %Greater.call: init bool = call %Greater.ref(%a.ref, %b.ref) // CHECK:STDOUT: return %Greater.call // CHECK:STDOUT: } // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/builtins/float/greater_eq.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/primitives.carbon // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/builtins/float/greater_eq.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/builtins/float/greater_eq.carbon // --- float_greater_eq.carbon fn GreaterEq(a: f64, b: f64) -> bool = "float.greater_eq"; fn Negate(a: f64) -> f64 = "float.negate"; class True {} class False {} fn F(true_: True, false_: False) { false_ as (if GreaterEq(1.0, 2.0) then True else False); true_ as (if GreaterEq(1.0, 1.0) then True else False); true_ as (if GreaterEq(1.0, 0.0) then True else False); false_ as (if GreaterEq(Negate(1.0), 0.0) then True else False); true_ as (if GreaterEq(0.0, Negate(1.0)) then True else False); } fn RuntimeCallIsValid(a: f64, b: f64) -> bool { //@dump-sem-ir-begin return GreaterEq(a, b); //@dump-sem-ir-end } // CHECK:STDOUT: --- float_greater_eq.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %int_64: Core.IntLiteral = int_value 64 [concrete] // CHECK:STDOUT: %f64.d77: type = class_type @Float, @Float(%int_64) [concrete] // CHECK:STDOUT: %GreaterEq.type: type = fn_type @GreaterEq [concrete] // CHECK:STDOUT: %GreaterEq: %GreaterEq.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @RuntimeCallIsValid(%a.param: %f64.d77, %b.param: %f64.d77) -> out %return.param: bool { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %GreaterEq.ref: %GreaterEq.type = name_ref GreaterEq, file.%GreaterEq.decl [concrete = constants.%GreaterEq] // CHECK:STDOUT: %a.ref: %f64.d77 = name_ref a, %a // CHECK:STDOUT: %b.ref: %f64.d77 = name_ref b, %b // CHECK:STDOUT: %GreaterEq.call: init bool = call %GreaterEq.ref(%a.ref, %b.ref) // CHECK:STDOUT: return %GreaterEq.call // CHECK:STDOUT: } // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/builtins/float/less.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/primitives.carbon // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/builtins/float/less.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/builtins/float/less.carbon // --- float_less.carbon fn Less(a: f64, b: f64) -> bool = "float.less"; fn Negate(a: f64) -> f64 = "float.negate"; class True {} class False {} fn F(true_: True, false_: False) { true_ as (if Less(1.0, 2.0) then True else False); false_ as (if Less(1.0, 1.0) then True else False); false_ as (if Less(1.0, 0.0) then True else False); true_ as (if Less(Negate(1.0), 0.0) then True else False); false_ as (if Less(0.0, Negate(1.0)) then True else False); } fn RuntimeCallIsValid(a: f64, b: f64) -> bool { //@dump-sem-ir-begin return Less(a, b); //@dump-sem-ir-end } // CHECK:STDOUT: --- float_less.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %int_64: Core.IntLiteral = int_value 64 [concrete] // CHECK:STDOUT: %f64.d77: type = class_type @Float, @Float(%int_64) [concrete] // CHECK:STDOUT: %Less.type: type = fn_type @Less [concrete] // CHECK:STDOUT: %Less: %Less.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @RuntimeCallIsValid(%a.param: %f64.d77, %b.param: %f64.d77) -> out %return.param: bool { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %Less.ref: %Less.type = name_ref Less, file.%Less.decl [concrete = constants.%Less] // CHECK:STDOUT: %a.ref: %f64.d77 = name_ref a, %a // CHECK:STDOUT: %b.ref: %f64.d77 = name_ref b, %b // CHECK:STDOUT: %Less.call: init bool = call %Less.ref(%a.ref, %b.ref) // CHECK:STDOUT: return %Less.call // CHECK:STDOUT: } // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/builtins/float/less_eq.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/primitives.carbon // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/builtins/float/less_eq.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/builtins/float/less_eq.carbon // --- float_less_eq.carbon fn LessEq(a: f64, b: f64) -> bool = "float.less_eq"; fn Negate(a: f64) -> f64 = "float.negate"; class True {} class False {} fn F(true_: True, false_: False) { true_ as (if LessEq(1.0, 2.0) then True else False); true_ as (if LessEq(1.0, 1.0) then True else False); false_ as (if LessEq(1.0, 0.0) then True else False); true_ as (if LessEq(Negate(1.0), 0.0) then True else False); false_ as (if LessEq(0.0, Negate(1.0)) then True else False); } fn RuntimeCallIsValid(a: f64, b: f64) -> bool { //@dump-sem-ir-begin return LessEq(a, b); //@dump-sem-ir-end } // --- fail_literal.carbon library "[[@TEST_NAME]]"; // CHECK:STDERR: fail_literal.carbon:[[@LINE+4]]:1: error: invalid signature for builtin function "float.less_eq" [InvalidBuiltinSignature] // CHECK:STDERR: fn LessEq(a: Core.FloatLiteral(), b: Core.FloatLiteral()) -> bool = "float.less_eq"; // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: fn LessEq(a: Core.FloatLiteral(), b: Core.FloatLiteral()) -> bool = "float.less_eq"; // CHECK:STDOUT: --- float_less_eq.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %int_64: Core.IntLiteral = int_value 64 [concrete] // CHECK:STDOUT: %f64.d77: type = class_type @Float, @Float(%int_64) [concrete] // CHECK:STDOUT: %LessEq.type: type = fn_type @LessEq [concrete] // CHECK:STDOUT: %LessEq: %LessEq.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @RuntimeCallIsValid(%a.param: %f64.d77, %b.param: %f64.d77) -> out %return.param: bool { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %LessEq.ref: %LessEq.type = name_ref LessEq, file.%LessEq.decl [concrete = constants.%LessEq] // CHECK:STDOUT: %a.ref: %f64.d77 = name_ref a, %a // CHECK:STDOUT: %b.ref: %f64.d77 = name_ref b, %b // CHECK:STDOUT: %LessEq.call: init bool = call %LessEq.ref(%a.ref, %b.ref) // CHECK:STDOUT: return %LessEq.call // CHECK:STDOUT: } // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/builtins/float/make_type.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/int.carbon // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/builtins/float/make_type.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/builtins/float/make_type.carbon // --- types.carbon library "[[@TEST_NAME]]"; fn Float(size: i32) -> type = "float.make_type"; // --- use_types.carbon library "[[@TEST_NAME]]"; import library "types"; var a: Float(16); var b: Float(32); var c: Float(64); var d: Float(128); fn GetFloat(dyn_size: i32) -> type { //@dump-sem-ir-begin return Float(dyn_size); //@dump-sem-ir-end } // --- fail_invalid_size.carbon library "[[@TEST_NAME]]"; import library "types"; // CHECK:STDERR: fail_invalid_size.carbon:[[@LINE+4]]:20: error: unsupported floating-point bit width 931 [CompileTimeFloatBitWidth] // CHECK:STDERR: var invalid_float: Float(931); // CHECK:STDERR: ^~~~~~~~~~ // CHECK:STDERR: var invalid_float: Float(931); // --- fail_dyn_size.carbon library "[[@TEST_NAME]]"; import library "types"; var dyn_size: i32 = 64; // CHECK:STDERR: fail_dyn_size.carbon:[[@LINE+4]]:10: error: cannot evaluate type expression [TypeExprEvaluationFailure] // CHECK:STDERR: var dyn: Float(dyn_size); // CHECK:STDERR: ^~~~~~~~~~~~~~~ // CHECK:STDERR: var dyn: Float(dyn_size); // CHECK:STDOUT: --- use_types.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %Float.type: type = fn_type @Float [concrete] // CHECK:STDOUT: %Float: %Float.type = struct_value () [concrete] // CHECK:STDOUT: %int_32.be0: Core.IntLiteral = int_value 32 [concrete] // CHECK:STDOUT: %i32: type = class_type @Int, @Int(%int_32.be0) [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Main.Float: %Float.type = import_ref Main//types, Float, loaded [concrete = constants.%Float] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @GetFloat(%dyn_size.param: %i32) -> out %return.param: type { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %Float.ref: %Float.type = name_ref Float, imports.%Main.Float [concrete = constants.%Float] // CHECK:STDOUT: %dyn_size.ref: %i32 = name_ref dyn_size, %dyn_size // CHECK:STDOUT: %Float.call: init type = call %Float.ref(%dyn_size.ref) // CHECK:STDOUT: return %Float.call // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @__global_init() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: // CHECK:STDOUT: } // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/builtins/float/mul.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/primitives.carbon // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/builtins/float/mul.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/builtins/float/mul.carbon // --- mul_sub.carbon library "[[@TEST_NAME]]"; fn Mul(a: f64, b: f64) -> f64 = "float.mul"; fn RuntimeCallIsValid(a: f64, b: f64) -> f64 { //@dump-sem-ir-begin return Mul(a, b); //@dump-sem-ir-end } var x: f64 = Mul(2.0, 0.5); // --- fail_too_few.carbon library "[[@TEST_NAME]]"; // CHECK:STDERR: fail_too_few.carbon:[[@LINE+4]]:1: error: invalid signature for builtin function "float.mul" [InvalidBuiltinSignature] // CHECK:STDERR: fn TooFew(a: f64) -> f64 = "float.mul"; // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: fn TooFew(a: f64) -> f64 = "float.mul"; fn RuntimeCallIsValidTooFew(a: f64) -> f64 { return TooFew(a); } // --- fail_too_many.carbon library "[[@TEST_NAME]]"; // CHECK:STDERR: fail_too_many.carbon:[[@LINE+4]]:1: error: invalid signature for builtin function "float.mul" [InvalidBuiltinSignature] // CHECK:STDERR: fn TooMany(a: f64, b: f64, c: f64) -> f64 = "float.mul"; // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: fn TooMany(a: f64, b: f64, c: f64) -> f64 = "float.mul"; fn RuntimeCallIsValidTooMany(a: f64, b: f64, c: f64) -> f64 { return TooMany(a, b, c); } // --- fail_bad_return_type.carbon library "[[@TEST_NAME]]"; // CHECK:STDERR: fail_bad_return_type.carbon:[[@LINE+4]]:1: error: invalid signature for builtin function "float.mul" [InvalidBuiltinSignature] // CHECK:STDERR: fn BadReturnType(a: f64, b: f64) -> bool = "float.mul"; // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: fn BadReturnType(a: f64, b: f64) -> bool = "float.mul"; fn RuntimeCallIsValidBadReturnType(a: f64, b: f64) -> bool { return BadReturnType(a, b); } // --- fail_literal.carbon library "[[@TEST_NAME]]"; fn Literal() -> type = "float_literal.make_type"; // CHECK:STDERR: fail_literal.carbon:[[@LINE+4]]:1: error: invalid signature for builtin function "float.mul" [InvalidBuiltinSignature] // CHECK:STDERR: fn MulLiteral(a: Literal(), b: Literal()) -> Literal() = "float.mul"; // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: fn MulLiteral(a: Literal(), b: Literal()) -> Literal() = "float.mul"; // CHECK:STDOUT: --- mul_sub.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %int_64: Core.IntLiteral = int_value 64 [concrete] // CHECK:STDOUT: %f64.d77: type = class_type @Float, @Float(%int_64) [concrete] // CHECK:STDOUT: %Mul.type: type = fn_type @Mul [concrete] // CHECK:STDOUT: %Mul: %Mul.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @RuntimeCallIsValid(%a.param: %f64.d77, %b.param: %f64.d77) -> out %return.param: %f64.d77 { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %Mul.ref: %Mul.type = name_ref Mul, file.%Mul.decl [concrete = constants.%Mul] // CHECK:STDOUT: %a.ref: %f64.d77 = name_ref a, %a // CHECK:STDOUT: %b.ref: %f64.d77 = name_ref b, %b // CHECK:STDOUT: %Mul.call: init %f64.d77 = call %Mul.ref(%a.ref, %b.ref) // CHECK:STDOUT: return %Mul.call // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @__global_init() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: // CHECK:STDOUT: } // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/builtins/float/mul_assign.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/primitives.carbon // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/builtins/float/mul_assign.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/builtins/float/mul_assign.carbon // --- call.carbon library "[[@TEST_NAME]]"; fn Builtin(ref a: f64, b: f64) = "float.mul_assign"; fn Call(ref a: f64, b: f64) { Builtin(ref a, b); } // --- fail_bad_decl.carbon library "[[@TEST_NAME]]"; // CHECK:STDERR: fail_bad_decl.carbon:[[@LINE+4]]:1: error: invalid signature for builtin function "float.mul_assign" [InvalidBuiltinSignature] // CHECK:STDERR: fn NotRef(a: f64, b: f64) = "float.mul_assign"; // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: fn NotRef(a: f64, b: f64) = "float.mul_assign"; // CHECK:STDERR: fail_bad_decl.carbon:[[@LINE+4]]:1: error: invalid signature for builtin function "float.mul_assign" [InvalidBuiltinSignature] // CHECK:STDERR: fn MixedTypes(ref a: f64, b: f32) = "float.mul_assign"; // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: fn MixedTypes(ref a: f64, b: f32) = "float.mul_assign"; // --- fail_literal.carbon library "[[@TEST_NAME]]"; fn Literal() -> type = "float_literal.make_type"; // CHECK:STDERR: fail_literal.carbon:[[@LINE+4]]:1: error: invalid signature for builtin function "float.mul_assign" [InvalidBuiltinSignature] // CHECK:STDERR: fn LiteralRuntime(ref a: Literal(), b: Literal()) = "float.mul_assign"; // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: fn LiteralRuntime(ref a: Literal(), b: Literal()) = "float.mul_assign"; ================================================ FILE: toolchain/check/testdata/builtins/float/negate.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/primitives.carbon // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/builtins/float/negate.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/builtins/float/negate.carbon // --- float_negate.carbon library "[[@TEST_NAME]]"; fn Negate(a: f64) -> f64 = "float.negate"; fn RuntimeCallIsValid(a: f64, unused b: f64) -> f64 { //@dump-sem-ir-begin return Negate(a); //@dump-sem-ir-end } let a: f64 = Negate(1.5); // --- fail_too_few.carbon library "[[@TEST_NAME]]"; // CHECK:STDERR: fail_too_few.carbon:[[@LINE+4]]:1: error: invalid signature for builtin function "float.negate" [InvalidBuiltinSignature] // CHECK:STDERR: fn TooFew() -> f64 = "float.negate"; // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: fn TooFew() -> f64 = "float.negate"; fn RuntimeCallIsValidTooFew() -> f64 { return TooFew(); } // --- fail_too_many.carbon library "[[@TEST_NAME]]"; // CHECK:STDERR: fail_too_many.carbon:[[@LINE+4]]:1: error: invalid signature for builtin function "float.negate" [InvalidBuiltinSignature] // CHECK:STDERR: fn TooMany(a: f64, b: f64) -> f64 = "float.negate"; // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: fn TooMany(a: f64, b: f64) -> f64 = "float.negate"; fn RuntimeCallIsValidTooMany(a: f64, b: f64) -> f64 { return TooMany(a, b); } // --- fail_bad_return_type.carbon library "[[@TEST_NAME]]"; // CHECK:STDERR: fail_bad_return_type.carbon:[[@LINE+4]]:1: error: invalid signature for builtin function "float.negate" [InvalidBuiltinSignature] // CHECK:STDERR: fn BadReturnType(a: f64) -> bool = "float.negate"; // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: fn BadReturnType(a: f64) -> bool = "float.negate"; fn RuntimeCallIsValidBadReturnType(a: f64) -> bool { return BadReturnType(a); } // CHECK:STDOUT: --- float_negate.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %int_64: Core.IntLiteral = int_value 64 [concrete] // CHECK:STDOUT: %f64.d77: type = class_type @Float, @Float(%int_64) [concrete] // CHECK:STDOUT: %Negate.type: type = fn_type @Negate [concrete] // CHECK:STDOUT: %Negate: %Negate.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @RuntimeCallIsValid(%a.param: %f64.d77, %b.param: %f64.d77) -> out %return.param: %f64.d77 { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %Negate.ref: %Negate.type = name_ref Negate, file.%Negate.decl [concrete = constants.%Negate] // CHECK:STDOUT: %a.ref: %f64.d77 = name_ref a, %a // CHECK:STDOUT: %Negate.call: init %f64.d77 = call %Negate.ref(%a.ref) // CHECK:STDOUT: return %Negate.call // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @__global_init() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: // CHECK:STDOUT: } // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/builtins/float/neq.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/primitives.carbon // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/builtins/float/neq.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/builtins/float/neq.carbon // --- float_neq.carbon library "[[@TEST_NAME]]"; fn Neq(a: f64, b: f64) -> bool = "float.neq"; class True {} class False {} fn F(true_: True, false_: False) { true_ as (if Neq(1.0, 2.0) then True else False); false_ as (if Neq(1.0, 1.0) then True else False); } fn RuntimeCallIsValid(a: f64, b: f64) -> bool { //@dump-sem-ir-begin return Neq(a, b); //@dump-sem-ir-end } // --- fail_bad_decl.carbon library "[[@TEST_NAME]]"; // CHECK:STDERR: fail_bad_decl.carbon:[[@LINE+4]]:1: error: invalid signature for builtin function "float.neq" [InvalidBuiltinSignature] // CHECK:STDERR: fn WrongResult(a: f64, b: f64) -> f64 = "float.neq"; // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: fn WrongResult(a: f64, b: f64) -> f64 = "float.neq"; // CHECK:STDOUT: --- float_neq.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %int_64: Core.IntLiteral = int_value 64 [concrete] // CHECK:STDOUT: %f64.d77: type = class_type @Float, @Float(%int_64) [concrete] // CHECK:STDOUT: %Neq.type: type = fn_type @Neq [concrete] // CHECK:STDOUT: %Neq: %Neq.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @RuntimeCallIsValid(%a.param: %f64.d77, %b.param: %f64.d77) -> out %return.param: bool { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %Neq.ref: %Neq.type = name_ref Neq, file.%Neq.decl [concrete = constants.%Neq] // CHECK:STDOUT: %a.ref: %f64.d77 = name_ref a, %a // CHECK:STDOUT: %b.ref: %f64.d77 = name_ref b, %b // CHECK:STDOUT: %Neq.call: init bool = call %Neq.ref(%a.ref, %b.ref) // CHECK:STDOUT: return %Neq.call // CHECK:STDOUT: } // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/builtins/float/sub.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/primitives.carbon // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/builtins/float/sub.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/builtins/float/sub.carbon // --- float_sub.carbon library "[[@TEST_NAME]]"; fn Sub(a: f64, b: f64) -> f64 = "float.sub"; fn RuntimeCallIsValid(a: f64, b: f64) -> f64 { //@dump-sem-ir-begin return Sub(a, b); //@dump-sem-ir-end } var x: f64 = Sub(2.0, 0.5); // --- fail_too_few.carbon library "[[@TEST_NAME]]"; // CHECK:STDERR: fail_too_few.carbon:[[@LINE+4]]:1: error: invalid signature for builtin function "float.sub" [InvalidBuiltinSignature] // CHECK:STDERR: fn TooFew(a: f64) -> f64 = "float.sub"; // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: fn TooFew(a: f64) -> f64 = "float.sub"; fn RuntimeCallIsValidTooFew(a: f64) -> f64 { return TooFew(a); } // --- fail_too_many.carbon library "[[@TEST_NAME]]"; // CHECK:STDERR: fail_too_many.carbon:[[@LINE+4]]:1: error: invalid signature for builtin function "float.sub" [InvalidBuiltinSignature] // CHECK:STDERR: fn TooMany(a: f64, b: f64, c: f64) -> f64 = "float.sub"; // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: fn TooMany(a: f64, b: f64, c: f64) -> f64 = "float.sub"; fn RuntimeCallIsValidTooMany(a: f64, b: f64, c: f64) -> f64 { return TooMany(a, b, c); } // --- fail_bad_return_type.carbon library "[[@TEST_NAME]]"; // CHECK:STDERR: fail_bad_return_type.carbon:[[@LINE+4]]:1: error: invalid signature for builtin function "float.sub" [InvalidBuiltinSignature] // CHECK:STDERR: fn BadReturnType(a: f64, b: f64) -> bool = "float.sub"; // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: fn BadReturnType(a: f64, b: f64) -> bool = "float.sub"; fn RuntimeCallIsValidBadReturnType(a: f64, b: f64) -> bool { return BadReturnType(a, b); } // --- fail_literal.carbon library "[[@TEST_NAME]]"; fn Literal() -> type = "float_literal.make_type"; // CHECK:STDERR: fail_literal.carbon:[[@LINE+4]]:1: error: invalid signature for builtin function "float.sub" [InvalidBuiltinSignature] // CHECK:STDERR: fn SubLiteral(a: Literal(), b: Literal()) -> Literal() = "float.sub"; // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: fn SubLiteral(a: Literal(), b: Literal()) -> Literal() = "float.sub"; // CHECK:STDOUT: --- float_sub.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %int_64: Core.IntLiteral = int_value 64 [concrete] // CHECK:STDOUT: %f64.d77: type = class_type @Float, @Float(%int_64) [concrete] // CHECK:STDOUT: %Sub.type: type = fn_type @Sub [concrete] // CHECK:STDOUT: %Sub: %Sub.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @RuntimeCallIsValid(%a.param: %f64.d77, %b.param: %f64.d77) -> out %return.param: %f64.d77 { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %Sub.ref: %Sub.type = name_ref Sub, file.%Sub.decl [concrete = constants.%Sub] // CHECK:STDOUT: %a.ref: %f64.d77 = name_ref a, %a // CHECK:STDOUT: %b.ref: %f64.d77 = name_ref b, %b // CHECK:STDOUT: %Sub.call: init %f64.d77 = call %Sub.ref(%a.ref, %b.ref) // CHECK:STDOUT: return %Sub.call // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @__global_init() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: // CHECK:STDOUT: } // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/builtins/float/sub_assign.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/primitives.carbon // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/builtins/float/sub_assign.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/builtins/float/sub_assign.carbon // --- call.carbon library "[[@TEST_NAME]]"; fn Builtin(ref a: f64, b: f64) = "float.sub_assign"; fn Call(ref a: f64, b: f64) { Builtin(ref a, b); } // --- fail_bad_decl.carbon library "[[@TEST_NAME]]"; // CHECK:STDERR: fail_bad_decl.carbon:[[@LINE+4]]:1: error: invalid signature for builtin function "float.sub_assign" [InvalidBuiltinSignature] // CHECK:STDERR: fn NotRef(a: f64, b: f64) = "float.sub_assign"; // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: fn NotRef(a: f64, b: f64) = "float.sub_assign"; // CHECK:STDERR: fail_bad_decl.carbon:[[@LINE+4]]:1: error: invalid signature for builtin function "float.sub_assign" [InvalidBuiltinSignature] // CHECK:STDERR: fn MixedTypes(ref a: f64, b: f32) = "float.sub_assign"; // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: fn MixedTypes(ref a: f64, b: f32) = "float.sub_assign"; // --- fail_literal.carbon library "[[@TEST_NAME]]"; fn Literal() -> type = "float_literal.make_type"; // CHECK:STDERR: fail_literal.carbon:[[@LINE+4]]:1: error: invalid signature for builtin function "float.sub_assign" [InvalidBuiltinSignature] // CHECK:STDERR: fn LiteralRuntime(ref a: Literal(), b: Literal()) = "float.sub_assign"; // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: fn LiteralRuntime(ref a: Literal(), b: Literal()) = "float.sub_assign"; ================================================ FILE: toolchain/check/testdata/builtins/float_literal/make_type.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/none.carbon // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/builtins/float_literal/make_type.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/builtins/float_literal/make_type.carbon // --- types.carbon library "[[@TEST_NAME]]"; fn FloatLiteral() -> type = "float_literal.make_type"; // --- use_types.carbon library "[[@TEST_NAME]]"; import library "types"; //@dump-sem-ir-begin let f: FloatLiteral() = 1.0; //@dump-sem-ir-end // CHECK:STDOUT: --- use_types.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %FloatLiteral.type: type = fn_type @FloatLiteral [concrete] // CHECK:STDOUT: %FloatLiteral: %FloatLiteral.type = struct_value () [concrete] // CHECK:STDOUT: %pattern_type.dab: type = pattern_type Core.FloatLiteral [concrete] // CHECK:STDOUT: %float: Core.FloatLiteral = float_literal_value 10e-1 [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Main.FloatLiteral: %FloatLiteral.type = import_ref Main//types, FloatLiteral, loaded [concrete = constants.%FloatLiteral] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %f.patt: %pattern_type.dab = value_binding_pattern f [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %.loc7_21.1: type = splice_block %.loc7_21.3 [concrete = Core.FloatLiteral] { // CHECK:STDOUT: %FloatLiteral.ref: %FloatLiteral.type = name_ref FloatLiteral, imports.%Main.FloatLiteral [concrete = constants.%FloatLiteral] // CHECK:STDOUT: %FloatLiteral.call: init type = call %FloatLiteral.ref() [concrete = Core.FloatLiteral] // CHECK:STDOUT: %.loc7_21.2: type = value_of_initializer %FloatLiteral.call [concrete = Core.FloatLiteral] // CHECK:STDOUT: %.loc7_21.3: type = converted %FloatLiteral.call, %.loc7_21.2 [concrete = Core.FloatLiteral] // CHECK:STDOUT: } // CHECK:STDOUT: %f: Core.FloatLiteral = value_binding f, @__global_init.%float // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @__global_init() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %float: Core.FloatLiteral = float_literal_value 10e-1 [concrete = constants.%float] // CHECK:STDOUT: // CHECK:STDOUT: } // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/builtins/form/make_type.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/none.carbon // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/builtins/form/make_type.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/builtins/form/make_type.carbon // --- types.carbon library "[[@TEST_NAME]]"; fn Form() -> type = "form.make_type"; // --- use_types.carbon library "[[@TEST_NAME]]"; import library "types"; //@dump-sem-ir-begin let f: Form() = form(var ()); //@dump-sem-ir-end // CHECK:STDOUT: --- use_types.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %Form.type: type = fn_type @Form [concrete] // CHECK:STDOUT: %Form: %Form.type = struct_value () [concrete] // CHECK:STDOUT: %pattern_type.13f: type = pattern_type Core.Form [concrete] // CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] // CHECK:STDOUT: %empty_tuple: %empty_tuple.type = tuple_value () [concrete] // CHECK:STDOUT: %.262: Core.Form = init_form %empty_tuple.type [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Main.Form: %Form.type = import_ref Main//types, Form, loaded [concrete = constants.%Form] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %f.patt: %pattern_type.13f = value_binding_pattern f [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %.loc7_13.1: type = splice_block %.loc7_13.3 [concrete = Core.Form] { // CHECK:STDOUT: %Form.ref: %Form.type = name_ref Form, imports.%Main.Form [concrete = constants.%Form] // CHECK:STDOUT: %Form.call: init type = call %Form.ref() [concrete = Core.Form] // CHECK:STDOUT: %.loc7_13.2: type = value_of_initializer %Form.call [concrete = Core.Form] // CHECK:STDOUT: %.loc7_13.3: type = converted %Form.call, %.loc7_13.2 [concrete = Core.Form] // CHECK:STDOUT: } // CHECK:STDOUT: %f: Core.Form = value_binding f, @__global_init.%.loc7_22 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @__global_init() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %.loc7_27.1: %empty_tuple.type = tuple_literal () [concrete = constants.%empty_tuple] // CHECK:STDOUT: %.loc7_27.2: type = converted %.loc7_27.1, constants.%empty_tuple.type [concrete = constants.%empty_tuple.type] // CHECK:STDOUT: %.loc7_22: Core.Form = init_form %.loc7_27.2 [concrete = constants.%.262] // CHECK:STDOUT: // CHECK:STDOUT: } // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/builtins/int/and.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/int.carbon // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/builtins/int/and.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/builtins/int/and.carbon // --- int_and.carbon library "[[@TEST_NAME]]"; fn And(a: i32, b: i32) -> i32 = "int.and"; var arr: array(i32, And(12, 10)); let arr_p: array(i32, 8)* = &arr; fn RuntimeCallIsValid(a: i32, b: i32) -> i32 { //@dump-sem-ir-begin return And(a, b); //@dump-sem-ir-end } // --- literal.carbon library "[[@TEST_NAME]]"; fn And(a: Core.IntLiteral(), b: Core.IntLiteral()) -> Core.IntLiteral() = "int.and"; class Expect(N:! Core.IntLiteral()) {} fn Test(N:! Core.IntLiteral()) -> Expect(N) { return {}; } fn F() { Test(And(1, 2)) as Expect(0); Test(And(12, 10)) as Expect(8); Test(And(1, -1)) as Expect(1); Test(And(-2, -3)) as Expect(-4); // Ensure the sign bit is treated properly even for 64-bit numbers. Test(And(0x7FFF_FFFF_FFFF_FFFF, -3)) as Expect(0x7FFF_FFFF_FFFF_FFFD); Test(And(0x8000_0000_0000_0000, -1)) as Expect(0x8000_0000_0000_0000); } // --- fail_literal_runtime.carbon library "[[@TEST_NAME]]"; fn AndLit(a: Core.IntLiteral(), b: Core.IntLiteral()) -> Core.IntLiteral() = "int.and"; fn F(a: Core.IntLiteral()) -> Core.IntLiteral() { // CHECK:STDERR: fail_literal_runtime.carbon:[[@LINE+7]]:10: error: non-constant call to compile-time-only function [NonConstantCallToCompTimeOnlyFunction] // CHECK:STDERR: return AndLit(5, a); // CHECK:STDERR: ^~~~~~~~~~~~ // CHECK:STDERR: fail_literal_runtime.carbon:[[@LINE-6]]:1: note: compile-time-only function declared here [CompTimeOnlyFunctionHere] // CHECK:STDERR: fn AndLit(a: Core.IntLiteral(), b: Core.IntLiteral()) -> Core.IntLiteral() = "int.and"; // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: return AndLit(5, a); } // --- fail_bad_decl.carbon library "[[@TEST_NAME]]"; // Heterogeneous "and" is not supported. // CHECK:STDERR: fail_bad_decl.carbon:[[@LINE+4]]:1: error: invalid signature for builtin function "int.and" [InvalidBuiltinSignature] // CHECK:STDERR: fn MixedAnd1(a: i32, b: Core.IntLiteral()) -> i32 = "int.and"; // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: fn MixedAnd1(a: i32, b: Core.IntLiteral()) -> i32 = "int.and"; // CHECK:STDERR: fail_bad_decl.carbon:[[@LINE+4]]:1: error: invalid signature for builtin function "int.and" [InvalidBuiltinSignature] // CHECK:STDERR: fn MixedAnd2(a: Core.IntLiteral(), b: i32) -> i32 = "int.and"; // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: fn MixedAnd2(a: Core.IntLiteral(), b: i32) -> i32 = "int.and"; // CHECK:STDERR: fail_bad_decl.carbon:[[@LINE+4]]:1: error: invalid signature for builtin function "int.and" [InvalidBuiltinSignature] // CHECK:STDERR: fn MixedAnd3(a: i32, b: Core.IntLiteral()) -> Core.IntLiteral() = "int.and"; // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: fn MixedAnd3(a: i32, b: Core.IntLiteral()) -> Core.IntLiteral() = "int.and"; // CHECK:STDERR: fail_bad_decl.carbon:[[@LINE+4]]:1: error: invalid signature for builtin function "int.and" [InvalidBuiltinSignature] // CHECK:STDERR: fn MixedAnd4(a: Core.IntLiteral(), b: i32) -> Core.IntLiteral() = "int.and"; // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: fn MixedAnd4(a: Core.IntLiteral(), b: i32) -> Core.IntLiteral() = "int.and"; // --- fail_runtime_literal.carbon library "[[@TEST_NAME]]"; fn And(a: Core.IntLiteral(), b: Core.IntLiteral()) -> Core.IntLiteral() = "int.and"; fn Test(n: Core.IntLiteral()) { // OK And(1, 1); // CHECK:STDERR: fail_runtime_literal.carbon:[[@LINE+7]]:3: error: non-constant call to compile-time-only function [NonConstantCallToCompTimeOnlyFunction] // CHECK:STDERR: And(n, 1); // CHECK:STDERR: ^~~~~~~~~ // CHECK:STDERR: fail_runtime_literal.carbon:[[@LINE-8]]:1: note: compile-time-only function declared here [CompTimeOnlyFunctionHere] // CHECK:STDERR: fn And(a: Core.IntLiteral(), b: Core.IntLiteral()) -> Core.IntLiteral() = "int.and"; // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: And(n, 1); // CHECK:STDERR: fail_runtime_literal.carbon:[[@LINE+7]]:3: error: non-constant call to compile-time-only function [NonConstantCallToCompTimeOnlyFunction] // CHECK:STDERR: And(1, n); // CHECK:STDERR: ^~~~~~~~~ // CHECK:STDERR: fail_runtime_literal.carbon:[[@LINE-16]]:1: note: compile-time-only function declared here [CompTimeOnlyFunctionHere] // CHECK:STDERR: fn And(a: Core.IntLiteral(), b: Core.IntLiteral()) -> Core.IntLiteral() = "int.and"; // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: And(1, n); // CHECK:STDERR: fail_runtime_literal.carbon:[[@LINE+7]]:3: error: non-constant call to compile-time-only function [NonConstantCallToCompTimeOnlyFunction] // CHECK:STDERR: And(n, n); // CHECK:STDERR: ^~~~~~~~~ // CHECK:STDERR: fail_runtime_literal.carbon:[[@LINE-24]]:1: note: compile-time-only function declared here [CompTimeOnlyFunctionHere] // CHECK:STDERR: fn And(a: Core.IntLiteral(), b: Core.IntLiteral()) -> Core.IntLiteral() = "int.and"; // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: And(n, n); } // CHECK:STDOUT: --- int_and.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [concrete] // CHECK:STDOUT: %i32: type = class_type @Int, @Int(%int_32) [concrete] // CHECK:STDOUT: %And.type: type = fn_type @And [concrete] // CHECK:STDOUT: %And: %And.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @RuntimeCallIsValid(%a.param: %i32, %b.param: %i32) -> out %return.param: %i32 { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %And.ref: %And.type = name_ref And, file.%And.decl [concrete = constants.%And] // CHECK:STDOUT: %a.ref: %i32 = name_ref a, %a // CHECK:STDOUT: %b.ref: %i32 = name_ref b, %b // CHECK:STDOUT: %And.call: init %i32 = call %And.ref(%a.ref, %b.ref) // CHECK:STDOUT: return %And.call // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @__global_init() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: // CHECK:STDOUT: } // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/builtins/int/and_assign.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/int.carbon // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/builtins/int/and_assign.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/builtins/int/and_assign.carbon // --- call.carbon library "[[@TEST_NAME]]"; fn Builtin(ref a: i32, b: i32) = "int.and_assign"; fn Call(ref a: i32, b: i32) { //@dump-sem-ir-begin Builtin(ref a, b); //@dump-sem-ir-end } // --- fail_unsized.carbon library "[[@TEST_NAME]]"; // CHECK:STDERR: fail_unsized.carbon:[[@LINE+4]]:1: error: invalid signature for builtin function "int.and_assign" [InvalidBuiltinSignature] // CHECK:STDERR: fn Builtin(ref a: Core.IntLiteral(), b: Core.IntLiteral()) = "int.and_assign"; // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: fn Builtin(ref a: Core.IntLiteral(), b: Core.IntLiteral()) = "int.and_assign"; // --- fail_not_ptr.carbon library "[[@TEST_NAME]]"; // CHECK:STDERR: fail_not_ptr.carbon:[[@LINE+4]]:1: error: invalid signature for builtin function "int.and_assign" [InvalidBuiltinSignature] // CHECK:STDERR: fn NotRef(a: i32, b: i32) = "int.and_assign"; // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: fn NotRef(a: i32, b: i32) = "int.and_assign"; // TODO: There's a crash bug preventing splitting tests, it'd probably be good // to split. (also, look at examples in other files) // // --- fail_not_ptr.carbon // // library "[[@TEST_NAME]]"; // CHECK:STDERR: fail_not_ptr.carbon:[[@LINE+4]]:1: error: invalid signature for builtin function "int.and_assign" [InvalidBuiltinSignature] // CHECK:STDERR: fn MixedTypes(ref a: i32, b: i64) = "int.and_assign"; // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: fn MixedTypes(ref a: i32, b: i64) = "int.and_assign"; // CHECK:STDOUT: --- call.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [concrete] // CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] // CHECK:STDOUT: %i32: type = class_type @Int, @Int(%int_32) [concrete] // CHECK:STDOUT: %Builtin.type: type = fn_type @Builtin [concrete] // CHECK:STDOUT: %Builtin: %Builtin.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @Call(%a.param: ref %i32, %b.param: %i32) { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %Builtin.ref: %Builtin.type = name_ref Builtin, file.%Builtin.decl [concrete = constants.%Builtin] // CHECK:STDOUT: %a.ref: ref %i32 = name_ref a, %a // CHECK:STDOUT: %.loc8: %i32 = ref_tag %a.ref // CHECK:STDOUT: %b.ref: %i32 = name_ref b, %b // CHECK:STDOUT: %Builtin.call: init %empty_tuple.type = call %Builtin.ref(%.loc8, %b.ref) // CHECK:STDOUT: // CHECK:STDOUT: } // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/builtins/int/complement.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/int.carbon // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/builtins/int/complement.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/builtins/int/complement.carbon // --- int_complement.carbon library "[[@TEST_NAME]]"; fn Complement(a: i32) -> i32 = "int.complement"; fn And(a: i32, b: i32) -> i32 = "int.and"; var arr: array(i32, And(Complement(0x123456), 0xFFFFFF)); let arr_p: array(i32, 0xEDCBA9)* = &arr; fn RuntimeCallIsValid(a: i32) -> i32 { //@dump-sem-ir-begin return Complement(a); //@dump-sem-ir-end } // --- literal.carbon library "[[@TEST_NAME]]"; fn Complement(a: Core.IntLiteral()) -> Core.IntLiteral() = "int.complement"; class Expect(N:! Core.IntLiteral()) {} fn Test(N:! Core.IntLiteral()) -> Expect(N) { return {}; } fn F() { Test(Complement(0)) as Expect(-1); Test(Complement(1)) as Expect(-2); Test(Complement(-1)) as Expect(0); Test(Complement(-0x7FFF_FFFF_FFFF_FFFF)) as Expect(0x7FFF_FFFF_FFFF_FFFE); Test(Complement(-0x8000_0000_0000_0000)) as Expect(0x7FFF_FFFF_FFFF_FFFF); Test(Complement(0x7FFF_FFFF_FFFF_FFFF)) as Expect(-0x8000_0000_0000_0000); Test(Complement(0x8000_0000_0000_0000)) as Expect(-0x8000_0000_0000_0001); } // --- fail_literal_runtime.carbon library "[[@TEST_NAME]]"; fn Complement(a: Core.IntLiteral()) -> Core.IntLiteral() = "int.complement"; fn F(a: Core.IntLiteral()) -> Core.IntLiteral() { // CHECK:STDERR: fail_literal_runtime.carbon:[[@LINE+7]]:10: error: non-constant call to compile-time-only function [NonConstantCallToCompTimeOnlyFunction] // CHECK:STDERR: return Complement(a); // CHECK:STDERR: ^~~~~~~~~~~~~ // CHECK:STDERR: fail_literal_runtime.carbon:[[@LINE-6]]:1: note: compile-time-only function declared here [CompTimeOnlyFunctionHere] // CHECK:STDERR: fn Complement(a: Core.IntLiteral()) -> Core.IntLiteral() = "int.complement"; // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: return Complement(a); } // CHECK:STDOUT: --- int_complement.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [concrete] // CHECK:STDOUT: %i32: type = class_type @Int, @Int(%int_32) [concrete] // CHECK:STDOUT: %Complement.type: type = fn_type @Complement [concrete] // CHECK:STDOUT: %Complement: %Complement.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @RuntimeCallIsValid(%a.param: %i32) -> out %return.param: %i32 { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %Complement.ref: %Complement.type = name_ref Complement, file.%Complement.decl [concrete = constants.%Complement] // CHECK:STDOUT: %a.ref: %i32 = name_ref a, %a // CHECK:STDOUT: %Complement.call: init %i32 = call %Complement.ref(%a.ref) // CHECK:STDOUT: return %Complement.call // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @__global_init() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: // CHECK:STDOUT: } // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/builtins/int/convert.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/uint.carbon // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/builtins/int/convert.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/builtins/int/convert.carbon // --- int_ops.carbon library "[[@TEST_NAME]]"; // Size preserving fn Int32ToInt32(a: i32) -> i32 = "int.convert"; fn Int32ToUint32(a: i32) -> u32 = "int.convert"; fn Uint32ToInt32(a: u32) -> i32 = "int.convert"; fn Uint32ToUint32(a: u32) -> u32 = "int.convert"; fn IntLiteralToIntLiteral(a: Core.IntLiteral()) -> Core.IntLiteral() = "int.convert"; // Narrowing fn Int32ToInt16(a: i32) -> i16 = "int.convert"; fn Int32ToUint16(a: i32) -> u16 = "int.convert"; fn Uint32ToInt16(a: u32) -> i16 = "int.convert"; fn Uint32ToUint16(a: u32) -> u16 = "int.convert"; fn IntLiteralToInt16(a: Core.IntLiteral()) -> i16 = "int.convert"; fn IntLiteralToUint16(a: Core.IntLiteral()) -> u16 = "int.convert"; // Widening fn Int32ToInt64(a: i32) -> i64 = "int.convert"; fn Int32ToUint64(a: i32) -> u64 = "int.convert"; fn Uint32ToInt64(a: u32) -> i64 = "int.convert"; fn Uint32ToUint64(a: u32) -> u64 = "int.convert"; fn Int32ToIntLiteral(a: i32) -> Core.IntLiteral() = "int.convert"; fn Uint32ToIntLiteral(a: u32) -> Core.IntLiteral() = "int.convert"; // char conversion fn UInt8ToChar(a: u8) -> u8 = "int.convert_char"; fn UInt16ToChar(a: u16) -> u8 = "int.convert_char"; fn UInt32ToChar(a: u32) -> u8 = "int.convert_char"; fn UInt64ToChar(a: u64) -> u8 = "int.convert_char"; class Expect[T:! type](N:! T) {} fn Test[T:! type](N:! T) -> Expect(N) { return {}; } // --- runtime_call.carbon library "[[@TEST_NAME]]"; import library "int_ops"; fn SizePreserving(a: i32) -> u32 { //@dump-sem-ir-begin return Int32ToUint32(a); //@dump-sem-ir-end } fn Narrowing(a: i32) -> i16 { //@dump-sem-ir-begin return Int32ToInt16(a); //@dump-sem-ir-end } fn Widening(a: i32) -> i64 { //@dump-sem-ir-begin return Int32ToInt64(a); //@dump-sem-ir-end } // --- fail_self_test.carbon library "[[@TEST_NAME]]"; import library "int_ops"; fn F() { // Ensure our testing machinery works. // CHECK:STDERR: fail_self_test.carbon:[[@LINE+7]]:3: error: cannot convert expression of type `Expect(0)` to `Expect(1)` with `as` [ConversionFailure] // CHECK:STDERR: Test(Int32ToInt32(0)) as Expect(1 as i32); // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: fail_self_test.carbon:[[@LINE+4]]:3: note: type `Expect(0)` does not implement interface `Core.As(Expect(1))` [MissingImplInMemberAccessInContext] // CHECK:STDERR: Test(Int32ToInt32(0)) as Expect(1 as i32); // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: Test(Int32ToInt32(0)) as Expect(1 as i32); } // --- identity.carbon library "[[@TEST_NAME]]"; import library "int_ops"; fn F() { Test(Int32ToInt32(-0x8000_0000)) as Expect(-0x8000_0000 as i32); Test(Int32ToInt32(-1)) as Expect(-1 as i32); Test(Int32ToInt32(0)) as Expect(0 as i32); Test(Int32ToInt32(0x7FFF_FFFF)) as Expect(0x7FFF_FFFF as i32); Test(Uint32ToUint32(0)) as Expect(0 as u32); Test(Uint32ToUint32(0x7FFF_FFFF)) as Expect(0x7FFF_FFFF as u32); Test(Uint32ToUint32(0x8000_0000)) as Expect(0x8000_0000 as u32); Test(Uint32ToUint32(0xFFFF_FFFF)) as Expect(0xFFFF_FFFF as u32); Test(IntLiteralToIntLiteral(0x1_0000_0000_0000_0000)) as Expect(0x1_0000_0000_0000_0000); Test(IntLiteralToIntLiteral(-1)) as Expect(-1); } // --- same_size.carbon library "[[@TEST_NAME]]"; import library "int_ops"; fn F() { Test(Int32ToUint32(-0x8000_0000)) as Expect(0x8000_0000 as u32); Test(Int32ToUint32(-1)) as Expect(0xFFFF_FFFF as u32); Test(Int32ToUint32(0)) as Expect(0 as u32); Test(Int32ToUint32(0x7FFF_FFFF)) as Expect(0x7FFF_FFFF as u32); Test(Uint32ToInt32(0)) as Expect(0 as i32); Test(Uint32ToInt32(0x7FFF_FFFF)) as Expect(0x7FFF_FFFF as i32); Test(Uint32ToInt32(0x8000_0000)) as Expect(-0x8000_0000 as i32); Test(Uint32ToInt32(0xFFFF_FFFF)) as Expect(-1 as i32); } // --- truncate.carbon library "[[@TEST_NAME]]"; import library "int_ops"; fn F() { Test(Int32ToInt16(-0x8000_0000)) as Expect(0 as i16); Test(Int32ToInt16(-0x7FFF_EDCC)) as Expect(0x1234 as i16); Test(Int32ToInt16(-0x7FFF_1234)) as Expect(-0x1234 as i16); Test(Int32ToInt16(-0x8000)) as Expect(-0x8000 as i16); Test(Int32ToInt16(-1)) as Expect(-1 as i16); Test(Int32ToInt16(0)) as Expect(0 as i16); Test(Int32ToInt16(0x7FFF)) as Expect(0x7FFF as i16); Test(Int32ToInt16(0xFFFF)) as Expect(-1 as i16); Test(Int32ToInt16(0x7FFF_1234)) as Expect(0x1234 as i16); Test(Int32ToInt16(0x7FFF_EDCC)) as Expect(-0x1234 as i16); Test(Int32ToInt16(0x7FFF_FFFF)) as Expect(-1 as i16); Test(Int32ToUint16(-0x8000_0000)) as Expect(0 as u16); Test(Int32ToUint16(-0x7FFF_EDCC)) as Expect(0x1234 as u16); Test(Int32ToUint16(-0x7FFF_1234)) as Expect(0xEDCC as u16); Test(Int32ToUint16(-0x8000)) as Expect(0x8000 as u16); Test(Int32ToUint16(-1)) as Expect(0xFFFF as u16); Test(Int32ToUint16(0)) as Expect(0 as u16); Test(Int32ToUint16(0x7FFF)) as Expect(0x7FFF as u16); Test(Int32ToUint16(0xFFFF)) as Expect(0xFFFF as u16); Test(Int32ToUint16(0x7FFF_1234)) as Expect(0x1234 as u16); Test(Int32ToUint16(0x7FFF_EDCC)) as Expect(0xEDCC as u16); Test(Int32ToUint16(0x7FFF_FFFF)) as Expect(0xFFFF as u16); Test(Uint32ToInt16(0x8000_0000)) as Expect(0 as i16); Test(Uint32ToInt16(0xFFFF_1234)) as Expect(0x1234 as i16); Test(Uint32ToInt16(0xFFFF_EDCC)) as Expect(-0x1234 as i16); Test(Uint32ToInt16(0xFFFF_8000)) as Expect(-0x8000 as i16); Test(Uint32ToInt16(0xFFFF_FFFF)) as Expect(-1 as i16); Test(Uint32ToInt16(0)) as Expect(0 as i16); Test(Uint32ToInt16(0x7FFF)) as Expect(0x7FFF as i16); Test(Uint32ToInt16(0xFFFF)) as Expect(-1 as i16); Test(Uint32ToInt16(0x7FFF_1234)) as Expect(0x1234 as i16); Test(Uint32ToInt16(0x7FFF_EDCC)) as Expect(-0x1234 as i16); Test(Uint32ToInt16(0x7FFF_FFFF)) as Expect(-1 as i16); Test(Uint32ToUint16(0x8000_0000)) as Expect(0 as u16); Test(Uint32ToUint16(0xFFFF_1234)) as Expect(0x1234 as u16); Test(Uint32ToUint16(0xFFFF_EDCC)) as Expect(0xEDCC as u16); Test(Uint32ToUint16(0xFFFF_8000)) as Expect(0x8000 as u16); Test(Uint32ToUint16(0xFFFF_FFFF)) as Expect(0xFFFF as u16); Test(Uint32ToUint16(0)) as Expect(0 as u16); Test(Uint32ToUint16(0x7FFF)) as Expect(0x7FFF as u16); Test(Uint32ToUint16(0xFFFF)) as Expect(0xFFFF as u16); Test(Uint32ToUint16(0x7FFF_1234)) as Expect(0x1234 as u16); Test(Uint32ToUint16(0x7FFF_EDCC)) as Expect(0xEDCC as u16); Test(Uint32ToUint16(0x7FFF_FFFF)) as Expect(0xFFFF as u16); Test(IntLiteralToInt16(0)) as Expect(0 as i16); Test(IntLiteralToInt16(0x7FFF)) as Expect(0x7FFF as i16); Test(IntLiteralToInt16(0x8000)) as Expect(-0x8000 as i16); Test(IntLiteralToInt16(0xFFFF)) as Expect(-1 as i16); Test(IntLiteralToInt16(0x1_2345)) as Expect(0x2345 as i16); Test(IntLiteralToInt16(-1)) as Expect(-1 as i16); Test(IntLiteralToUint16(0)) as Expect(0 as u16); Test(IntLiteralToUint16(0x7FFF)) as Expect(0x7FFF as u16); Test(IntLiteralToUint16(0x8000)) as Expect(0x8000 as u16); Test(IntLiteralToUint16(0xFFFF)) as Expect(0xFFFF as u16); Test(IntLiteralToUint16(0x1_2345)) as Expect(0x2345 as u16); Test(IntLiteralToUint16(-1)) as Expect(0xFFFF as u16); } // --- zero_extend.carbon library "[[@TEST_NAME]]"; import library "int_ops"; fn F() { Test(Uint32ToInt64(0)) as Expect(0 as i64); Test(Uint32ToInt64(0x1234_5678)) as Expect(0x1234_5678 as i64); Test(Uint32ToInt64(0x7FFF_FFFF)) as Expect(0x7FFF_FFFF as i64); Test(Uint32ToInt64(0x8000_0000)) as Expect(0x8000_0000 as i64); Test(Uint32ToInt64(0xFFFF_FFFF)) as Expect(0xFFFF_FFFF as i64); Test(Uint32ToUint64(0)) as Expect(0 as u64); Test(Uint32ToUint64(0x1234_5678)) as Expect(0x1234_5678 as u64); Test(Uint32ToUint64(0x7FFF_FFFF)) as Expect(0x7FFF_FFFF as u64); Test(Uint32ToUint64(0x8000_0000)) as Expect(0x8000_0000 as u64); Test(Uint32ToUint64(0xFFFF_FFFF)) as Expect(0xFFFF_FFFF as u64); Test(Uint32ToIntLiteral(0x1234_5678)) as Expect(0x1234_5678); Test(Uint32ToIntLiteral(0x8765_4321)) as Expect(0x8765_4321); Test(Uint32ToIntLiteral(0xFFFF_FFFF)) as Expect(0xFFFF_FFFF); } // --- sign_extend.carbon library "[[@TEST_NAME]]"; import library "int_ops"; fn F() { Test(Int32ToInt64(0)) as Expect(0 as i64); Test(Int32ToInt64(0x1234_5678)) as Expect(0x1234_5678 as i64); Test(Int32ToInt64(0x7FFF_FFFF)) as Expect(0x7FFF_FFFF as i64); Test(Int32ToInt64(-1)) as Expect(-1 as i64); Test(Int32ToUint64(0)) as Expect(0 as u64); Test(Int32ToUint64(0x1234_5678)) as Expect(0x1234_5678 as u64); Test(Int32ToUint64(0x7FFF_FFFF)) as Expect(0x7FFF_FFFF as u64); Test(Int32ToUint64(-1)) as Expect(0xFFFF_FFFF_FFFF_FFFF as u64); Test(Int32ToUint64(-0x8000_0000)) as Expect(0xFFFF_FFFF_8000_0000 as u64); Test(Int32ToIntLiteral(0x1234_5678)) as Expect(0x1234_5678); Test(Int32ToIntLiteral(-0x1234_5678)) as Expect(-0x1234_5678); Test(Int32ToIntLiteral(-1)) as Expect(-1); } // --- fail_not_constant.carbon library "[[@TEST_NAME]]"; import library "int_ops"; let not_constant: Core.IntLiteral() = 0; // CHECK:STDERR: fail_not_constant.carbon:[[@LINE+8]]:33: error: non-constant call to compile-time-only function [NonConstantCallToCompTimeOnlyFunction] // CHECK:STDERR: let convert_not_constant: i16 = IntLiteralToInt16(not_constant); // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: fail_not_constant.carbon:[[@LINE-7]]:1: in import [InImport] // CHECK:STDERR: int_ops.carbon:16:1: note: compile-time-only function declared here [CompTimeOnlyFunctionHere] // CHECK:STDERR: fn IntLiteralToInt16(a: Core.IntLiteral()) -> i16 = "int.convert"; // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: let convert_not_constant: i16 = IntLiteralToInt16(not_constant); // --- char_conversion.carbon library "[[@TEST_NAME]]"; import library "int_ops"; fn F() { // u8 \u2192 char Test(UInt8ToChar(0x00)) as Expect(0x00 as u8); Test(UInt8ToChar(0x80)) as Expect(0x80 as u8); Test(UInt8ToChar(0xFF)) as Expect(0xFF as u8); // u16 \u2192 i64 Test(UInt16ToChar(0x0000)) as Expect(0x00 as u8); Test(UInt16ToChar(0xFF12)) as Expect(0x12 as u8); Test(UInt16ToChar(0xFFFF)) as Expect(0xFF as u8); // u32 \u2192 char Test(UInt32ToChar(0x0000_0000)) as Expect(0x00 as u8); Test(UInt32ToChar(0x8000_0012)) as Expect(0x12 as u8); Test(UInt32ToChar(0xFFFF_FF12)) as Expect(0x12 as u8); Test(UInt32ToChar(0xFFFF_FFFF)) as Expect(0xFF as u8); // u64 \u2192 char Test(UInt64ToChar(0x0)) as Expect(0x00 as u8); Test(UInt64ToChar(0x8000_0000_0000_0012)) as Expect(0x12 as u8); Test(UInt64ToChar(0xFFFF_FFFF_FFFF_FF12)) as Expect(0x12 as u8); Test(UInt64ToChar(0xFFFF_FFFF_FFFF_FFFF)) as Expect(0xFF as u8); } // CHECK:STDOUT: --- runtime_call.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [concrete] // CHECK:STDOUT: %i32: type = class_type @Int, @Int(%int_32) [concrete] // CHECK:STDOUT: %u32: type = class_type @UInt, @UInt(%int_32) [concrete] // CHECK:STDOUT: %Int32ToUint32.type: type = fn_type @Int32ToUint32 [concrete] // CHECK:STDOUT: %Int32ToUint32: %Int32ToUint32.type = struct_value () [concrete] // CHECK:STDOUT: %int_16: Core.IntLiteral = int_value 16 [concrete] // CHECK:STDOUT: %i16: type = class_type @Int, @Int(%int_16) [concrete] // CHECK:STDOUT: %Int32ToInt16.type: type = fn_type @Int32ToInt16 [concrete] // CHECK:STDOUT: %Int32ToInt16: %Int32ToInt16.type = struct_value () [concrete] // CHECK:STDOUT: %int_64: Core.IntLiteral = int_value 64 [concrete] // CHECK:STDOUT: %i64: type = class_type @Int, @Int(%int_64) [concrete] // CHECK:STDOUT: %Int32ToInt64.type: type = fn_type @Int32ToInt64 [concrete] // CHECK:STDOUT: %Int32ToInt64: %Int32ToInt64.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Main.Int32ToUint32: %Int32ToUint32.type = import_ref Main//int_ops, Int32ToUint32, loaded [concrete = constants.%Int32ToUint32] // CHECK:STDOUT: %Main.Int32ToInt16: %Int32ToInt16.type = import_ref Main//int_ops, Int32ToInt16, loaded [concrete = constants.%Int32ToInt16] // CHECK:STDOUT: %Main.Int32ToInt64: %Int32ToInt64.type = import_ref Main//int_ops, Int32ToInt64, loaded [concrete = constants.%Int32ToInt64] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @SizePreserving(%a.param: %i32) -> out %return.param: %u32 { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %Int32ToUint32.ref: %Int32ToUint32.type = name_ref Int32ToUint32, imports.%Main.Int32ToUint32 [concrete = constants.%Int32ToUint32] // CHECK:STDOUT: %a.ref: %i32 = name_ref a, %a // CHECK:STDOUT: %Int32ToUint32.call: init %u32 = call %Int32ToUint32.ref(%a.ref) // CHECK:STDOUT: return %Int32ToUint32.call // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @Narrowing(%a.param: %i32) -> out %return.param: %i16 { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %Int32ToInt16.ref: %Int32ToInt16.type = name_ref Int32ToInt16, imports.%Main.Int32ToInt16 [concrete = constants.%Int32ToInt16] // CHECK:STDOUT: %a.ref: %i32 = name_ref a, %a // CHECK:STDOUT: %Int32ToInt16.call: init %i16 = call %Int32ToInt16.ref(%a.ref) // CHECK:STDOUT: return %Int32ToInt16.call // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @Widening(%a.param: %i32) -> out %return.param: %i64 { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %Int32ToInt64.ref: %Int32ToInt64.type = name_ref Int32ToInt64, imports.%Main.Int32ToInt64 [concrete = constants.%Int32ToInt64] // CHECK:STDOUT: %a.ref: %i32 = name_ref a, %a // CHECK:STDOUT: %Int32ToInt64.call: init %i64 = call %Int32ToInt64.ref(%a.ref) // CHECK:STDOUT: return %Int32ToInt64.call // CHECK:STDOUT: } // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/builtins/int/convert_checked.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/uint.carbon // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/builtins/int/convert_checked.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/builtins/int/convert_checked.carbon // --- int_ops.carbon library "[[@TEST_NAME]]"; fn NegateI32(a: i32) -> i32 = "int.snegate"; fn SubI32(a: i32, b: i32) -> i32 = "int.ssub"; fn AddU32(a: u32, b: u32) -> u32 = "int.uadd"; fn IntLiteral() -> type = "int_literal.make_type"; // Size preserving fn Int32ToInt32(a: i32) -> i32 = "int.convert_checked"; fn Int32ToUint32(a: i32) -> u32 = "int.convert_checked"; fn Uint32ToInt32(a: u32) -> i32 = "int.convert_checked"; fn Uint32ToUint32(a: u32) -> u32 = "int.convert_checked"; fn IntLiteralToIntLiteral(a: IntLiteral()) -> IntLiteral() = "int.convert_checked"; // Narrowing fn Int32ToInt16(a: i32) -> i16 = "int.convert_checked"; fn Int32ToUint16(a: i32) -> u16 = "int.convert_checked"; fn Uint32ToInt16(a: u32) -> i16 = "int.convert_checked"; fn Uint32ToUint16(a: u32) -> u16 = "int.convert_checked"; fn IntLiteralToInt16(a: IntLiteral()) -> i16 = "int.convert_checked"; fn IntLiteralToUint16(a: IntLiteral()) -> u16 = "int.convert_checked"; // Widening fn Int32ToInt64(a: i32) -> i64 = "int.convert_checked"; fn Int32ToUint64(a: i32) -> u64 = "int.convert_checked"; fn Uint32ToInt64(a: u32) -> i64 = "int.convert_checked"; fn Uint32ToUint64(a: u32) -> u64 = "int.convert_checked"; fn Int32ToIntLiteral(a: i32) -> IntLiteral() = "int.convert_checked"; fn Uint32ToUintLiteral(a: u32) -> IntLiteral() = "int.convert_checked"; // --- runtime_call.carbon library "[[@TEST_NAME]]"; import library "int_ops"; //@dump-sem-ir-begin let SizePreserving: u32 = Int32ToUint32(1); let Narrowing: i16 = Int32ToInt16(1); let Widening: i64 = Int32ToInt64(1); //@dump-sem-ir-end // --- identity.carbon library "[[@TEST_NAME]]"; import library "int_ops"; let a: i32 = Int32ToInt32(0); let b: i32 = Int32ToInt32(0x7FFF_FFFF); let c: i32 = Int32ToInt32(SubI32(NegateI32(0x7FFF_FFFF), 1)); let d: IntLiteral() = IntLiteralToIntLiteral(Int32ToIntLiteral(NegateI32(1))); // --- same_size.carbon library "[[@TEST_NAME]]"; import library "int_ops"; let max: u32 = Int32ToUint32(0x7FFF_FFFF); let max_roundtrip: i32 = Uint32ToInt32(Int32ToUint32(0x7FFF_FFFF)); // --- truncate.carbon library "[[@TEST_NAME]]"; import library "int_ops"; let a: u16 = Int32ToUint16(0); let b: u16 = Int32ToUint16(0xFFFF); let c: i16 = Int32ToInt16(0x7FFF); let d: i16 = Int32ToInt16(NegateI32(0x8000)); let e: u16 = Uint32ToUint16(Int32ToUint32(0)); let f: u16 = Uint32ToUint16(Int32ToUint32(0xFFFF)); let g: i16 = Uint32ToInt16(Int32ToUint32(0)); let h: i16 = Uint32ToInt16(Int32ToUint32(0x7FFF)); let lit_i16_min: i16 = IntLiteralToInt16(Int32ToIntLiteral(NegateI32(0x8000))); let lit_i16_max: i16 = IntLiteralToInt16(Int32ToIntLiteral(0x7FFF)); let lit_u16_min: u16 = IntLiteralToUint16(Int32ToIntLiteral(0)); let lit_u16_max: u16 = IntLiteralToUint16(Int32ToIntLiteral(0xFFFF)); // --- zero_extend.carbon library "[[@TEST_NAME]]"; import library "int_ops"; let a: u64 = Uint32ToUint64(Int32ToUint32(0)); let b: u64 = Uint32ToUint64( AddU32( AddU32(Int32ToUint32(0x7FFF_FFFF), Int32ToUint32(0x7FFF_FFFF)), Int32ToUint32(1))); let c: i64 = Uint32ToInt64(Int32ToUint32(0)); let d: i64 = Uint32ToInt64( AddU32( AddU32(Int32ToUint32(0x7FFF_FFFF), Int32ToUint32(0x7FFF_FFFF)), Int32ToUint32(1))); // --- sign_extend.carbon library "[[@TEST_NAME]]"; import library "int_ops"; let a: u64 = Int32ToUint64(0); let b: u64 = Int32ToUint64(0x7FFF_FFFF); let c: i64 = Int32ToInt64(SubI32(NegateI32(0x7FFF_FFFF), 1)); let d: i64 = Int32ToInt64(0x7FFF_FFFF); // --- fail_too_large_u32_for_i32.carbon library "[[@TEST_NAME]]"; import library "int_ops"; let max_plus_one: i32 = // CHECK:STDERR: fail_too_large_u32_for_i32.carbon:[[@LINE+4]]:3: error: integer value 2147483648 too large for type `i32` [IntTooLargeForType] // CHECK:STDERR: Uint32ToInt32( // CHECK:STDERR: ^~~~~~~~~~~~~~ // CHECK:STDERR: Uint32ToInt32( AddU32(Int32ToUint32(0x7FFF_FFFF), Int32ToUint32(1))); // --- fail_too_large_i32_for_i16.carbon library "[[@TEST_NAME]]"; import library "int_ops"; // CHECK:STDERR: fail_too_large_i32_for_i16.carbon:[[@LINE+4]]:25: error: integer value 32768 too large for type `i16` [IntTooLargeForType] // CHECK:STDERR: let max_plus_one: i16 = Int32ToInt16(0x8000); // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: let max_plus_one: i16 = Int32ToInt16(0x8000); // --- fail_too_large_i32_for_u16.carbon library "[[@TEST_NAME]]"; import library "int_ops"; // CHECK:STDERR: fail_too_large_i32_for_u16.carbon:[[@LINE+4]]:25: error: integer value 65536 too large for type `u16` [IntTooLargeForType] // CHECK:STDERR: let max_plus_one: u16 = Int32ToUint16(0x1_0000); // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: let max_plus_one: u16 = Int32ToUint16(0x1_0000); // --- fail_too_large_u32_for_i16.carbon library "[[@TEST_NAME]]"; import library "int_ops"; // CHECK:STDERR: fail_too_large_u32_for_i16.carbon:[[@LINE+4]]:25: error: integer value 32768 too large for type `i16` [IntTooLargeForType] // CHECK:STDERR: let max_plus_one: i16 = Uint32ToInt16(Int32ToUint32(0x8000)); // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: let max_plus_one: i16 = Uint32ToInt16(Int32ToUint32(0x8000)); // --- fail_too_large_u32_for_u16.carbon library "[[@TEST_NAME]]"; import library "int_ops"; // CHECK:STDERR: fail_too_large_u32_for_u16.carbon:[[@LINE+4]]:25: error: integer value 65536 too large for type `u16` [IntTooLargeForType] // CHECK:STDERR: let max_plus_one: u16 = Uint32ToUint16(Int32ToUint32(0x1_0000)); // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: let max_plus_one: u16 = Uint32ToUint16(Int32ToUint32(0x1_0000)); // --- fail_negative_i32_to_u16.carbon library "[[@TEST_NAME]]"; import library "int_ops"; // CHECK:STDERR: fail_negative_i32_to_u16.carbon:[[@LINE+4]]:29: error: negative integer value -1 converted to unsigned type `u16` [NegativeIntInUnsignedType] // CHECK:STDERR: let minus_one_to_u16: u16 = Int32ToUint16(SubI32(0, 1)); // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: let minus_one_to_u16: u16 = Int32ToUint16(SubI32(0, 1)); // --- fail_negative_i32_to_u32.carbon library "[[@TEST_NAME]]"; import library "int_ops"; // CHECK:STDERR: fail_negative_i32_to_u32.carbon:[[@LINE+4]]:29: error: negative integer value -1 converted to unsigned type `u32` [NegativeIntInUnsignedType] // CHECK:STDERR: let minus_one_to_u32: u32 = Int32ToUint32(SubI32(0, 1)); // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: let minus_one_to_u32: u32 = Int32ToUint32(SubI32(0, 1)); // --- fail_negative_i32_to_u64.carbon library "[[@TEST_NAME]]"; import library "int_ops"; // CHECK:STDERR: fail_negative_i32_to_u64.carbon:[[@LINE+4]]:29: error: negative integer value -1 converted to unsigned type `u64` [NegativeIntInUnsignedType] // CHECK:STDERR: let minus_one_to_u64: u64 = Int32ToUint64(SubI32(0, 1)); // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: let minus_one_to_u64: u64 = Int32ToUint64(SubI32(0, 1)); // --- fail_too_small_i32_for_i16.carbon library "[[@TEST_NAME]]"; import library "int_ops"; // CHECK:STDERR: fail_too_small_i32_for_i16.carbon:[[@LINE+4]]:26: error: integer value -32769 too large for type `i16` [IntTooLargeForType] // CHECK:STDERR: let min_minus_one: i16 = Int32ToInt16(NegateI32(0x8001)); // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: let min_minus_one: i16 = Int32ToInt16(NegateI32(0x8001)); // --- fail_not_constant.carbon library "[[@TEST_NAME]]"; import library "int_ops"; let not_constant: i32 = 0; // CHECK:STDERR: fail_not_constant.carbon:[[@LINE+8]]:40: error: non-constant call to compile-time-only function [NonConstantCallToCompTimeOnlyFunction] // CHECK:STDERR: let convert_not_constant_narrow: i16 = Int32ToInt16(not_constant); // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: fail_not_constant.carbon:[[@LINE-7]]:1: in import [InImport] // CHECK:STDERR: int_ops.carbon:18:1: note: compile-time-only function declared here [CompTimeOnlyFunctionHere] // CHECK:STDERR: fn Int32ToInt16(a: i32) -> i16 = "int.convert_checked"; // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: let convert_not_constant_narrow: i16 = Int32ToInt16(not_constant); // CHECK:STDERR: fail_not_constant.carbon:[[@LINE+8]]:38: error: non-constant call to compile-time-only function [NonConstantCallToCompTimeOnlyFunction] // CHECK:STDERR: let convert_not_constant_same: i32 = Int32ToInt32(not_constant); // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: fail_not_constant.carbon:[[@LINE-17]]:1: in import [InImport] // CHECK:STDERR: int_ops.carbon:10:1: note: compile-time-only function declared here [CompTimeOnlyFunctionHere] // CHECK:STDERR: fn Int32ToInt32(a: i32) -> i32 = "int.convert_checked"; // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: let convert_not_constant_same: i32 = Int32ToInt32(not_constant); // CHECK:STDERR: fail_not_constant.carbon:[[@LINE+8]]:39: error: non-constant call to compile-time-only function [NonConstantCallToCompTimeOnlyFunction] // CHECK:STDERR: let convert_not_constant_widen: i64 = Int32ToInt64(not_constant); // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: fail_not_constant.carbon:[[@LINE-27]]:1: in import [InImport] // CHECK:STDERR: int_ops.carbon:26:1: note: compile-time-only function declared here [CompTimeOnlyFunctionHere] // CHECK:STDERR: fn Int32ToInt64(a: i32) -> i64 = "int.convert_checked"; // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: let convert_not_constant_widen: i64 = Int32ToInt64(not_constant); // CHECK:STDOUT: --- runtime_call.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [concrete] // CHECK:STDOUT: %u32: type = class_type @UInt, @UInt(%int_32) [concrete] // CHECK:STDOUT: %pattern_type.4a9: type = pattern_type %u32 [concrete] // CHECK:STDOUT: %Int32ToUint32.type: type = fn_type @Int32ToUint32 [concrete] // CHECK:STDOUT: %Int32ToUint32: %Int32ToUint32.type = struct_value () [concrete] // CHECK:STDOUT: %i32: type = class_type @Int, @Int(%int_32) [concrete] // CHECK:STDOUT: %int_1.5b8: Core.IntLiteral = int_value 1 [concrete] // CHECK:STDOUT: %ImplicitAs.type.cf3: type = facet_type <@ImplicitAs, @ImplicitAs(%i32)> [concrete] // CHECK:STDOUT: %To: Core.IntLiteral = symbolic_binding To, 0 [symbolic] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.004: type = fn_type @Core.IntLiteral.as.ImplicitAs.impl.Convert, @Core.IntLiteral.as.ImplicitAs.impl(%To) [symbolic] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.255: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.004 = struct_value () [symbolic] // CHECK:STDOUT: %ImplicitAs.impl_witness.58d: = impl_witness imports.%ImplicitAs.impl_witness_table.e45, @Core.IntLiteral.as.ImplicitAs.impl(%int_32) [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.199: type = fn_type @Core.IntLiteral.as.ImplicitAs.impl.Convert, @Core.IntLiteral.as.ImplicitAs.impl(%int_32) [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.5f4: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.199 = struct_value () [concrete] // CHECK:STDOUT: %ImplicitAs.facet: %ImplicitAs.type.cf3 = facet_value Core.IntLiteral, (%ImplicitAs.impl_witness.58d) [concrete] // CHECK:STDOUT: %ImplicitAs.WithSelf.Convert.type.979: type = fn_type @ImplicitAs.WithSelf.Convert, @ImplicitAs.WithSelf(%i32, %ImplicitAs.facet) [concrete] // CHECK:STDOUT: %.7c3: type = fn_type_with_self_type %ImplicitAs.WithSelf.Convert.type.979, %ImplicitAs.facet [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.bound: = bound_method %int_1.5b8, %Core.IntLiteral.as.ImplicitAs.impl.Convert.5f4 [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn: = specific_function %Core.IntLiteral.as.ImplicitAs.impl.Convert.5f4, @Core.IntLiteral.as.ImplicitAs.impl.Convert(%int_32) [concrete] // CHECK:STDOUT: %bound_method: = bound_method %int_1.5b8, %Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn [concrete] // CHECK:STDOUT: %int_1.47b: %i32 = int_value 1 [concrete] // CHECK:STDOUT: %int_1.c1d: %u32 = int_value 1 [concrete] // CHECK:STDOUT: %int_16: Core.IntLiteral = int_value 16 [concrete] // CHECK:STDOUT: %i16: type = class_type @Int, @Int(%int_16) [concrete] // CHECK:STDOUT: %pattern_type.88f: type = pattern_type %i16 [concrete] // CHECK:STDOUT: %Int32ToInt16.type: type = fn_type @Int32ToInt16 [concrete] // CHECK:STDOUT: %Int32ToInt16: %Int32ToInt16.type = struct_value () [concrete] // CHECK:STDOUT: %int_1.c22: %i16 = int_value 1 [concrete] // CHECK:STDOUT: %int_64: Core.IntLiteral = int_value 64 [concrete] // CHECK:STDOUT: %i64: type = class_type @Int, @Int(%int_64) [concrete] // CHECK:STDOUT: %pattern_type.a10: type = pattern_type %i64 [concrete] // CHECK:STDOUT: %Int32ToInt64.type: type = fn_type @Int32ToInt64 [concrete] // CHECK:STDOUT: %Int32ToInt64: %Int32ToInt64.type = struct_value () [concrete] // CHECK:STDOUT: %int_1.a95: %i64 = int_value 1 [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Main.Int32ToUint32: %Int32ToUint32.type = import_ref Main//int_ops, Int32ToUint32, loaded [concrete = constants.%Int32ToUint32] // CHECK:STDOUT: %Main.Int32ToInt16: %Int32ToInt16.type = import_ref Main//int_ops, Int32ToInt16, loaded [concrete = constants.%Int32ToInt16] // CHECK:STDOUT: %Main.Int32ToInt64: %Int32ToInt64.type = import_ref Main//int_ops, Int32ToInt64, loaded [concrete = constants.%Int32ToInt64] // CHECK:STDOUT: %Core.import_ref.b25: @Core.IntLiteral.as.ImplicitAs.impl.%Core.IntLiteral.as.ImplicitAs.impl.Convert.type (%Core.IntLiteral.as.ImplicitAs.impl.Convert.type.004) = import_ref Core//prelude/parts/int, loc{{\d+_\d+}}, loaded [symbolic = @Core.IntLiteral.as.ImplicitAs.impl.%Core.IntLiteral.as.ImplicitAs.impl.Convert (constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.255)] // CHECK:STDOUT: %ImplicitAs.impl_witness_table.e45 = impl_witness_table (%Core.import_ref.b25), @Core.IntLiteral.as.ImplicitAs.impl [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %SizePreserving.patt: %pattern_type.4a9 = value_binding_pattern SizePreserving [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %u32: type = type_literal constants.%u32 [concrete = constants.%u32] // CHECK:STDOUT: %.loc6_42.1: %u32 = value_of_initializer @__global_init.%Int32ToUint32.call [concrete = constants.%int_1.c1d] // CHECK:STDOUT: %.loc6_42.2: %u32 = converted @__global_init.%Int32ToUint32.call, %.loc6_42.1 [concrete = constants.%int_1.c1d] // CHECK:STDOUT: %SizePreserving: %u32 = value_binding SizePreserving, %.loc6_42.2 // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %Narrowing.patt: %pattern_type.88f = value_binding_pattern Narrowing [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %i16: type = type_literal constants.%i16 [concrete = constants.%i16] // CHECK:STDOUT: %.loc7_36.1: %i16 = value_of_initializer @__global_init.%Int32ToInt16.call [concrete = constants.%int_1.c22] // CHECK:STDOUT: %.loc7_36.2: %i16 = converted @__global_init.%Int32ToInt16.call, %.loc7_36.1 [concrete = constants.%int_1.c22] // CHECK:STDOUT: %Narrowing: %i16 = value_binding Narrowing, %.loc7_36.2 // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %Widening.patt: %pattern_type.a10 = value_binding_pattern Widening [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %i64: type = type_literal constants.%i64 [concrete = constants.%i64] // CHECK:STDOUT: %.loc8_35.1: %i64 = value_of_initializer @__global_init.%Int32ToInt64.call [concrete = constants.%int_1.a95] // CHECK:STDOUT: %.loc8_35.2: %i64 = converted @__global_init.%Int32ToInt64.call, %.loc8_35.1 [concrete = constants.%int_1.a95] // CHECK:STDOUT: %Widening: %i64 = value_binding Widening, %.loc8_35.2 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @__global_init() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %Int32ToUint32.ref: %Int32ToUint32.type = name_ref Int32ToUint32, imports.%Main.Int32ToUint32 [concrete = constants.%Int32ToUint32] // CHECK:STDOUT: %int_1.loc6: Core.IntLiteral = int_value 1 [concrete = constants.%int_1.5b8] // CHECK:STDOUT: %impl.elem0.loc6: %.7c3 = impl_witness_access constants.%ImplicitAs.impl_witness.58d, element0 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.5f4] // CHECK:STDOUT: %bound_method.loc6_41.1: = bound_method %int_1.loc6, %impl.elem0.loc6 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.bound] // CHECK:STDOUT: %specific_fn.loc6: = specific_function %impl.elem0.loc6, @Core.IntLiteral.as.ImplicitAs.impl.Convert(constants.%int_32) [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn] // CHECK:STDOUT: %bound_method.loc6_41.2: = bound_method %int_1.loc6, %specific_fn.loc6 [concrete = constants.%bound_method] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc6: init %i32 = call %bound_method.loc6_41.2(%int_1.loc6) [concrete = constants.%int_1.47b] // CHECK:STDOUT: %.loc6_41.1: %i32 = value_of_initializer %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc6 [concrete = constants.%int_1.47b] // CHECK:STDOUT: %.loc6_41.2: %i32 = converted %int_1.loc6, %.loc6_41.1 [concrete = constants.%int_1.47b] // CHECK:STDOUT: %Int32ToUint32.call: init %u32 = call %Int32ToUint32.ref(%.loc6_41.2) [concrete = constants.%int_1.c1d] // CHECK:STDOUT: %Int32ToInt16.ref: %Int32ToInt16.type = name_ref Int32ToInt16, imports.%Main.Int32ToInt16 [concrete = constants.%Int32ToInt16] // CHECK:STDOUT: %int_1.loc7: Core.IntLiteral = int_value 1 [concrete = constants.%int_1.5b8] // CHECK:STDOUT: %impl.elem0.loc7: %.7c3 = impl_witness_access constants.%ImplicitAs.impl_witness.58d, element0 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.5f4] // CHECK:STDOUT: %bound_method.loc7_35.1: = bound_method %int_1.loc7, %impl.elem0.loc7 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.bound] // CHECK:STDOUT: %specific_fn.loc7: = specific_function %impl.elem0.loc7, @Core.IntLiteral.as.ImplicitAs.impl.Convert(constants.%int_32) [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn] // CHECK:STDOUT: %bound_method.loc7_35.2: = bound_method %int_1.loc7, %specific_fn.loc7 [concrete = constants.%bound_method] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc7: init %i32 = call %bound_method.loc7_35.2(%int_1.loc7) [concrete = constants.%int_1.47b] // CHECK:STDOUT: %.loc7_35.1: %i32 = value_of_initializer %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc7 [concrete = constants.%int_1.47b] // CHECK:STDOUT: %.loc7_35.2: %i32 = converted %int_1.loc7, %.loc7_35.1 [concrete = constants.%int_1.47b] // CHECK:STDOUT: %Int32ToInt16.call: init %i16 = call %Int32ToInt16.ref(%.loc7_35.2) [concrete = constants.%int_1.c22] // CHECK:STDOUT: %Int32ToInt64.ref: %Int32ToInt64.type = name_ref Int32ToInt64, imports.%Main.Int32ToInt64 [concrete = constants.%Int32ToInt64] // CHECK:STDOUT: %int_1.loc8: Core.IntLiteral = int_value 1 [concrete = constants.%int_1.5b8] // CHECK:STDOUT: %impl.elem0.loc8: %.7c3 = impl_witness_access constants.%ImplicitAs.impl_witness.58d, element0 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.5f4] // CHECK:STDOUT: %bound_method.loc8_34.1: = bound_method %int_1.loc8, %impl.elem0.loc8 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.bound] // CHECK:STDOUT: %specific_fn.loc8: = specific_function %impl.elem0.loc8, @Core.IntLiteral.as.ImplicitAs.impl.Convert(constants.%int_32) [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn] // CHECK:STDOUT: %bound_method.loc8_34.2: = bound_method %int_1.loc8, %specific_fn.loc8 [concrete = constants.%bound_method] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc8: init %i32 = call %bound_method.loc8_34.2(%int_1.loc8) [concrete = constants.%int_1.47b] // CHECK:STDOUT: %.loc8_34.1: %i32 = value_of_initializer %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc8 [concrete = constants.%int_1.47b] // CHECK:STDOUT: %.loc8_34.2: %i32 = converted %int_1.loc8, %.loc8_34.1 [concrete = constants.%int_1.47b] // CHECK:STDOUT: %Int32ToInt64.call: init %i64 = call %Int32ToInt64.ref(%.loc8_34.2) [concrete = constants.%int_1.a95] // CHECK:STDOUT: // CHECK:STDOUT: } // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/builtins/int/eq.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/primitives.carbon // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/builtins/int/eq.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/builtins/int/eq.carbon // --- int_eq.carbon library "[[@TEST_NAME]]"; fn Eq(a: i32, b: i32) -> bool = "int.eq"; class True {} class False {} fn F(true_: True, false_: False) { true_ as (if Eq(1, 1) then True else False); false_ as (if Eq(1, 2) then True else False); } fn RuntimeCallIsValid(a: i32, b: i32) -> bool { //@dump-sem-ir-begin return Eq(a, b); //@dump-sem-ir-end } // --- fail_bad_decl.carbon library "[[@TEST_NAME]]"; // CHECK:STDERR: fail_bad_decl.carbon:[[@LINE+4]]:1: error: invalid signature for builtin function "int.eq" [InvalidBuiltinSignature] // CHECK:STDERR: fn WrongResult(a: i32, b: i32) -> i32 = "int.eq"; // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: fn WrongResult(a: i32, b: i32) -> i32 = "int.eq"; // --- literal.carbon library "[[@TEST_NAME]]"; fn Eq(a: Core.IntLiteral(), b: Core.IntLiteral()) -> bool = "int.eq"; class Expect(B:! bool) {} fn Test(B:! bool) -> Expect(B) { return {}; } fn F() { Test(Eq(5, 5)) as Expect(true); Test(Eq(5, 6)) as Expect(false); Test(Eq(-1, -1)) as Expect(true); Test(Eq(-1, 1)) as Expect(false); } // --- mixed.carbon library "[[@TEST_NAME]]"; fn Eq(a: Core.IntLiteral(), b: i32) -> bool = "int.eq"; class Expect(B:! bool) {} fn Test(B:! bool) -> Expect(B) { return {}; } fn F() { Test(Eq(5, 5)) as Expect(true); Test(Eq(5, 6)) as Expect(false); Test(Eq(-1, -1)) as Expect(true); Test(Eq(-1, 1)) as Expect(false); } // --- fail_runtime_literal.carbon library "[[@TEST_NAME]]"; fn Eq(a: Core.IntLiteral(), b: Core.IntLiteral()) -> bool = "int.eq"; fn Test(n: Core.IntLiteral()) { // OK Eq(1, 1); // CHECK:STDERR: fail_runtime_literal.carbon:[[@LINE+7]]:3: error: non-constant call to compile-time-only function [NonConstantCallToCompTimeOnlyFunction] // CHECK:STDERR: Eq(n, 1); // CHECK:STDERR: ^~~~~~~~ // CHECK:STDERR: fail_runtime_literal.carbon:[[@LINE-8]]:1: note: compile-time-only function declared here [CompTimeOnlyFunctionHere] // CHECK:STDERR: fn Eq(a: Core.IntLiteral(), b: Core.IntLiteral()) -> bool = "int.eq"; // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: Eq(n, 1); // CHECK:STDERR: fail_runtime_literal.carbon:[[@LINE+7]]:3: error: non-constant call to compile-time-only function [NonConstantCallToCompTimeOnlyFunction] // CHECK:STDERR: Eq(1, n); // CHECK:STDERR: ^~~~~~~~ // CHECK:STDERR: fail_runtime_literal.carbon:[[@LINE-16]]:1: note: compile-time-only function declared here [CompTimeOnlyFunctionHere] // CHECK:STDERR: fn Eq(a: Core.IntLiteral(), b: Core.IntLiteral()) -> bool = "int.eq"; // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: Eq(1, n); // CHECK:STDERR: fail_runtime_literal.carbon:[[@LINE+7]]:3: error: non-constant call to compile-time-only function [NonConstantCallToCompTimeOnlyFunction] // CHECK:STDERR: Eq(n, n); // CHECK:STDERR: ^~~~~~~~ // CHECK:STDERR: fail_runtime_literal.carbon:[[@LINE-24]]:1: note: compile-time-only function declared here [CompTimeOnlyFunctionHere] // CHECK:STDERR: fn Eq(a: Core.IntLiteral(), b: Core.IntLiteral()) -> bool = "int.eq"; // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: Eq(n, n); } // CHECK:STDOUT: --- int_eq.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [concrete] // CHECK:STDOUT: %i32: type = class_type @Int, @Int(%int_32) [concrete] // CHECK:STDOUT: %Eq.type: type = fn_type @Eq [concrete] // CHECK:STDOUT: %Eq: %Eq.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @RuntimeCallIsValid(%a.param: %i32, %b.param: %i32) -> out %return.param: bool { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %Eq.ref: %Eq.type = name_ref Eq, file.%Eq.decl [concrete = constants.%Eq] // CHECK:STDOUT: %a.ref: %i32 = name_ref a, %a // CHECK:STDOUT: %b.ref: %i32 = name_ref b, %b // CHECK:STDOUT: %Eq.call: init bool = call %Eq.ref(%a.ref, %b.ref) // CHECK:STDOUT: return %Eq.call // CHECK:STDOUT: } // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/builtins/int/greater.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/primitives.carbon // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/builtins/int/greater.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/builtins/int/greater.carbon // --- int_greater.carbon fn Greater(a: i32, b: i32) -> bool = "int.greater"; fn Negate(a: i32) -> i32 = "int.snegate"; class True {} class False {} fn F(true_: True, false_: False) { false_ as (if Greater(1, 2) then True else False); false_ as (if Greater(1, 1) then True else False); true_ as (if Greater(1, 0) then True else False); false_ as (if Greater(Negate(1), 0) then True else False); true_ as (if Greater(0, Negate(1)) then True else False); } fn RuntimeCallIsValid(a: i32, b: i32) -> bool { //@dump-sem-ir-begin return Greater(a, b); //@dump-sem-ir-end } // CHECK:STDOUT: --- int_greater.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [concrete] // CHECK:STDOUT: %i32: type = class_type @Int, @Int(%int_32) [concrete] // CHECK:STDOUT: %Greater.type: type = fn_type @Greater [concrete] // CHECK:STDOUT: %Greater: %Greater.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @RuntimeCallIsValid(%a.param: %i32, %b.param: %i32) -> out %return.param: bool { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %Greater.ref: %Greater.type = name_ref Greater, file.%Greater.decl [concrete = constants.%Greater] // CHECK:STDOUT: %a.ref: %i32 = name_ref a, %a // CHECK:STDOUT: %b.ref: %i32 = name_ref b, %b // CHECK:STDOUT: %Greater.call: init bool = call %Greater.ref(%a.ref, %b.ref) // CHECK:STDOUT: return %Greater.call // CHECK:STDOUT: } // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/builtins/int/greater_eq.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/primitives.carbon // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/builtins/int/greater_eq.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/builtins/int/greater_eq.carbon // --- int_greater_eq.carbon library "[[@TEST_NAME]]"; fn GreaterEq(a: i32, b: i32) -> bool = "int.greater_eq"; fn Negate(a: i32) -> i32 = "int.snegate"; class True {} class False {} fn F(true_: True, false_: False) { false_ as (if GreaterEq(1, 2) then True else False); true_ as (if GreaterEq(1, 1) then True else False); true_ as (if GreaterEq(1, 0) then True else False); false_ as (if GreaterEq(Negate(1), 0) then True else False); true_ as (if GreaterEq(0, Negate(1)) then True else False); } fn RuntimeCallIsValid(a: i32, b: i32) -> bool { //@dump-sem-ir-begin return GreaterEq(a, b); //@dump-sem-ir-end } // --- literal.carbon library "[[@TEST_NAME]]"; fn GreaterEq(a: Core.IntLiteral(), b: Core.IntLiteral()) -> bool = "int.greater_eq"; class Expect(B:! bool) {} fn Test(B:! bool) -> Expect(B) { return {}; } fn F() { Test(GreaterEq(5, 5)) as Expect(true); Test(GreaterEq(5, 6)) as Expect(false); Test(GreaterEq(6, 5)) as Expect(true); Test(GreaterEq(-1, -1)) as Expect(true); Test(GreaterEq(-1, 1)) as Expect(false); Test(GreaterEq(1, -1)) as Expect(true); } // --- mixed.carbon library "[[@TEST_NAME]]"; fn GreaterEq(a: Core.IntLiteral(), b: i32) -> bool = "int.greater_eq"; class Expect(B:! bool) {} fn Test(B:! bool) -> Expect(B) { return {}; } fn F() { Test(GreaterEq(5, 5)) as Expect(true); Test(GreaterEq(5, 6)) as Expect(false); Test(GreaterEq(6, 5)) as Expect(true); Test(GreaterEq(-1, -1)) as Expect(true); Test(GreaterEq(-1, 1)) as Expect(false); Test(GreaterEq(1, -1)) as Expect(true); } // CHECK:STDOUT: --- int_greater_eq.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [concrete] // CHECK:STDOUT: %i32: type = class_type @Int, @Int(%int_32) [concrete] // CHECK:STDOUT: %GreaterEq.type: type = fn_type @GreaterEq [concrete] // CHECK:STDOUT: %GreaterEq: %GreaterEq.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @RuntimeCallIsValid(%a.param: %i32, %b.param: %i32) -> out %return.param: bool { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %GreaterEq.ref: %GreaterEq.type = name_ref GreaterEq, file.%GreaterEq.decl [concrete = constants.%GreaterEq] // CHECK:STDOUT: %a.ref: %i32 = name_ref a, %a // CHECK:STDOUT: %b.ref: %i32 = name_ref b, %b // CHECK:STDOUT: %GreaterEq.call: init bool = call %GreaterEq.ref(%a.ref, %b.ref) // CHECK:STDOUT: return %GreaterEq.call // CHECK:STDOUT: } // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/builtins/int/left_shift.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/uint.carbon // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/builtins/int/left_shift.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/builtins/int/left_shift.carbon // --- i32.carbon library "[[@TEST_NAME]]"; class Expect(N:! i32) {} fn Test(N:! i32) -> Expect(N) { return {}; } fn LeftShift(a: i32, b: i32) -> i32 = "int.left_shift"; fn F() { Test(LeftShift(0, 0)) as Expect(0); Test(LeftShift(0, 1)) as Expect(0); Test(LeftShift(0, 30)) as Expect(0); Test(LeftShift(1, 30)) as Expect(0x4000_0000); Test(LeftShift(5, 2)) as Expect(20); Test(LeftShift(-1, 1)) as Expect(-2); Test(LeftShift(-2, 1)) as Expect(-4); Test(LeftShift(-3, 1)) as Expect(-6); } fn RuntimeCallIsValid(a: i32, b: i32) -> i32 { //@dump-sem-ir-begin return LeftShift(a, b); //@dump-sem-ir-end } // --- u32.carbon library "[[@TEST_NAME]]"; class Expect(N:! u32) {} fn Test(N:! u32) -> Expect(N) { return {}; } fn LeftShift(a: u32, b: i32) -> u32 = "int.left_shift"; fn F() { Test(LeftShift(0, 0)) as Expect(0); Test(LeftShift(0, 1)) as Expect(0); Test(LeftShift(0, 30)) as Expect(0); Test(LeftShift(1, 30)) as Expect(0x4000_0000); Test(LeftShift(5, 2)) as Expect(20); Test(LeftShift(0xFFFF_FFFF, 1)) as Expect(0xFFFF_FFFE); Test(LeftShift(0xFFFF_FFFE, 1)) as Expect(0xFFFF_FFFC); Test(LeftShift(0xABCD_EF01, 8)) as Expect(0xCDEF_0100); } fn RuntimeCall(a: u32, b: i32) -> u32 { //@dump-sem-ir-begin return LeftShift(a, b); //@dump-sem-ir-end } // --- literal.carbon library "[[@TEST_NAME]]"; fn LeftShift(a: Core.IntLiteral(), b: Core.IntLiteral()) -> Core.IntLiteral() = "int.left_shift"; class Expect(N:! Core.IntLiteral()) {} fn Test(N:! Core.IntLiteral()) -> Expect(N) { return {}; } fn F() { // Zero can be shifted by any amount. Test(LeftShift(0, 0)) as Expect(0); Test(LeftShift(0, 0)) as Expect(0); Test(LeftShift(0, 1)) as Expect(0); Test(LeftShift(0, 30)) as Expect(0); Test(LeftShift(0, 1_000_000_000)) as Expect(0); // Positive numbers can be shifted. Test(LeftShift(1, 0)) as Expect(1); Test(LeftShift(1, 1)) as Expect(2); Test(LeftShift(2, 1)) as Expect(4); Test(LeftShift(1, 2)) as Expect(4); Test(LeftShift(3, 2)) as Expect(12); Test(LeftShift(1, 30)) as Expect(0x4000_0000); Test(LeftShift(5, 2)) as Expect(20); // Negative numbers can be shifted too. Test(LeftShift(-1, 0)) as Expect(-1); Test(LeftShift(-1, 1)) as Expect(-2); Test(LeftShift(-2, 1)) as Expect(-4); Test(LeftShift(-3, 1)) as Expect(-6); // Large numbers can be shifted losslessly. Test(LeftShift(0xFFFF_FFFF, 1)) as Expect(0x1_FFFF_FFFE); Test(LeftShift(0xFFFF_FFFE, 1)) as Expect(0x1_FFFF_FFFC); Test(LeftShift(0xABCD_EF01, 8)) as Expect(0xAB_CDEF_0100); Test(LeftShift(0x7FFF_FFFF_FFFF_FFFF, 1)) as Expect(0xFFFF_FFFF_FFFF_FFFE); Test(LeftShift(0xFFFF_FFFF_FFFF_FFFF, 1)) as Expect(0x1_FFFF_FFFF_FFFF_FFFE); } // --- fail_bad_shift.carbon library "[[@TEST_NAME]]"; fn LeftShift(a: i32, b: i32) -> i32 = "int.left_shift"; fn LeftShiftLit(a: Core.IntLiteral(), b: i32) -> Core.IntLiteral() = "int.left_shift"; // Shift greater than size is disallowed for sized types. let size_1: i32 = LeftShift(1, 31); // CHECK:STDERR: fail_bad_shift.carbon:[[@LINE+4]]:19: error: shift distance >= type width of 32 in `1 << 32` [CompileTimeShiftOutOfRange] // CHECK:STDERR: let size_2: i32 = LeftShift(1, 32); // CHECK:STDERR: ^~~~~~~~~~~~~~~~ // CHECK:STDERR: let size_2: i32 = LeftShift(1, 32); // CHECK:STDERR: fail_bad_shift.carbon:[[@LINE+4]]:19: error: shift distance >= type width of 32 in `1 << 33` [CompileTimeShiftOutOfRange] // CHECK:STDERR: let size_3: i32 = LeftShift(1, 33); // CHECK:STDERR: ^~~~~~~~~~~~~~~~ // CHECK:STDERR: let size_3: i32 = LeftShift(1, 33); // Overflow is allowed if the shift distance is in bounds. let overflow_1: i32 = LeftShift(1000, 31); // CHECK:STDERR: fail_bad_shift.carbon:[[@LINE+4]]:23: error: shift distance >= type width of 32 in `1000 << 32` [CompileTimeShiftOutOfRange] // CHECK:STDERR: let overflow_2: i32 = LeftShift(1000, 32); // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: let overflow_2: i32 = LeftShift(1000, 32); // Oversize shifts aren't allowed even if there's no overflow. let no_overflow_1: i32 = LeftShift(0, 31); // CHECK:STDERR: fail_bad_shift.carbon:[[@LINE+4]]:26: error: shift distance >= type width of 32 in `0 << 32` [CompileTimeShiftOutOfRange] // CHECK:STDERR: let no_overflow_2: i32 = LeftShift(0, 32); // CHECK:STDERR: ^~~~~~~~~~~~~~~~ // CHECK:STDERR: let no_overflow_2: i32 = LeftShift(0, 32); // Negative shifts aren't allowed either, even for literals, even if the lhs is zero. // CHECK:STDERR: fail_bad_shift.carbon:[[@LINE+4]]:21: error: shift distance >= type width of 32 in `1 << -1` [CompileTimeShiftOutOfRange] // CHECK:STDERR: let negative: i32 = LeftShift(1, -1); // CHECK:STDERR: ^~~~~~~~~~~~~~~~ // CHECK:STDERR: let negative: i32 = LeftShift(1, -1); // CHECK:STDERR: fail_bad_shift.carbon:[[@LINE+4]]:26: error: shift distance >= type width of 32 in `0 << -1` [CompileTimeShiftOutOfRange] // CHECK:STDERR: let negative_zero: i32 = LeftShift(0, -1); // CHECK:STDERR: ^~~~~~~~~~~~~~~~ // CHECK:STDERR: let negative_zero: i32 = LeftShift(0, -1); // CHECK:STDERR: fail_bad_shift.carbon:[[@LINE+4]]:39: error: shift distance negative in `1 << -1` [CompileTimeShiftNegative] // CHECK:STDERR: let negative_lit: Core.IntLiteral() = LeftShiftLit(1, -1); // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: let negative_lit: Core.IntLiteral() = LeftShiftLit(1, -1); // CHECK:STDERR: fail_bad_shift.carbon:[[@LINE+4]]:44: error: shift distance negative in `0 << -1` [CompileTimeShiftNegative] // CHECK:STDERR: let negative_lit_zero: Core.IntLiteral() = LeftShiftLit(0, -1); // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: let negative_lit_zero: Core.IntLiteral() = LeftShiftLit(0, -1); // --- fail_literal_overflow.carbon library "[[@TEST_NAME]]"; fn LeftShift(a: Core.IntLiteral(), b: Core.IntLiteral()) -> Core.IntLiteral() = "int.left_shift"; // CHECK:STDERR: fail_literal_overflow.carbon:[[@LINE+4]]:16: error: shift distance of 1000000000 would result in an integer whose width is greater than the maximum supported width of 8388608 [CompileTimeUnsizedShiftOutOfRange] // CHECK:STDERR: let bad: i32 = LeftShift(1, 1_000_000_000); // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: let bad: i32 = LeftShift(1, 1_000_000_000); // CHECK:STDERR: fail_literal_overflow.carbon:[[@LINE+4]]:25: error: shift distance of 1000000000 would result in an integer whose width is greater than the maximum supported width of 8388608 [CompileTimeUnsizedShiftOutOfRange] // CHECK:STDERR: let bad_negative: i32 = LeftShift(-1, 1_000_000_000); // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: let bad_negative: i32 = LeftShift(-1, 1_000_000_000); // --- fail_comp_time_only_shift.carbon library "[[@TEST_NAME]]"; fn LeftShiftByLit(a: i32, b: Core.IntLiteral()) -> i32 = "int.left_shift"; fn LeftShiftOfLit(a: Core.IntLiteral(), b: i32) -> Core.IntLiteral() = "int.left_shift"; var a_lit: Core.IntLiteral() = 12; var an_i32: i32 = 34; // This can't be valid: we don't have a compile-time or runtime integer value for `a_lit`. // CHECK:STDERR: fail_comp_time_only_shift.carbon:[[@LINE+7]]:17: error: non-constant call to compile-time-only function [NonConstantCallToCompTimeOnlyFunction] // CHECK:STDERR: let bad1: i32 = LeftShiftByLit(an_i32, a_lit); // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: fail_comp_time_only_shift.carbon:[[@LINE-10]]:1: note: compile-time-only function declared here [CompTimeOnlyFunctionHere] // CHECK:STDERR: fn LeftShiftByLit(a: i32, b: Core.IntLiteral()) -> i32 = "int.left_shift"; // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: let bad1: i32 = LeftShiftByLit(an_i32, a_lit); // TODO: This could be valid because we don't actually need the return value at runtime. // CHECK:STDERR: fail_comp_time_only_shift.carbon:[[@LINE+7]]:31: error: non-constant call to compile-time-only function [NonConstantCallToCompTimeOnlyFunction] // CHECK:STDERR: let bad2: Core.IntLiteral() = LeftShiftOfLit(a_lit, an_i32); // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: fail_comp_time_only_shift.carbon:[[@LINE-19]]:1: note: compile-time-only function declared here [CompTimeOnlyFunctionHere] // CHECK:STDERR: fn LeftShiftOfLit(a: Core.IntLiteral(), b: i32) -> Core.IntLiteral() = "int.left_shift"; // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: let bad2: Core.IntLiteral() = LeftShiftOfLit(a_lit, an_i32); // TODO: This could be valid because the literal argument has a constant value. // CHECK:STDERR: fail_comp_time_only_shift.carbon:[[@LINE+7]]:17: error: non-constant call to compile-time-only function [NonConstantCallToCompTimeOnlyFunction] // CHECK:STDERR: let bad3: i32 = LeftShiftByLit(an_i32, 12); // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: fail_comp_time_only_shift.carbon:[[@LINE-30]]:1: note: compile-time-only function declared here [CompTimeOnlyFunctionHere] // CHECK:STDERR: fn LeftShiftByLit(a: i32, b: Core.IntLiteral()) -> i32 = "int.left_shift"; // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: let bad3: i32 = LeftShiftByLit(an_i32, 12); // TODO: This could be valid because we don't actually need the return value at runtime. // CHECK:STDERR: fail_comp_time_only_shift.carbon:[[@LINE+7]]:31: error: non-constant call to compile-time-only function [NonConstantCallToCompTimeOnlyFunction] // CHECK:STDERR: let bad4: Core.IntLiteral() = LeftShiftOfLit(12, an_i32); // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: fail_comp_time_only_shift.carbon:[[@LINE-39]]:1: note: compile-time-only function declared here [CompTimeOnlyFunctionHere] // CHECK:STDERR: fn LeftShiftOfLit(a: Core.IntLiteral(), b: i32) -> Core.IntLiteral() = "int.left_shift"; // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: let bad4: Core.IntLiteral() = LeftShiftOfLit(12, an_i32); // CHECK:STDOUT: --- i32.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [concrete] // CHECK:STDOUT: %i32: type = class_type @Int, @Int(%int_32) [concrete] // CHECK:STDOUT: %LeftShift.type: type = fn_type @LeftShift [concrete] // CHECK:STDOUT: %LeftShift: %LeftShift.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @RuntimeCallIsValid(%a.param: %i32, %b.param: %i32) -> out %return.param: %i32 { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %LeftShift.ref: %LeftShift.type = name_ref LeftShift, file.%LeftShift.decl [concrete = constants.%LeftShift] // CHECK:STDOUT: %a.ref: %i32 = name_ref a, %a // CHECK:STDOUT: %b.ref: %i32 = name_ref b, %b // CHECK:STDOUT: %LeftShift.call: init %i32 = call %LeftShift.ref(%a.ref, %b.ref) // CHECK:STDOUT: return %LeftShift.call // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- u32.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [concrete] // CHECK:STDOUT: %u32: type = class_type @UInt, @UInt(%int_32) [concrete] // CHECK:STDOUT: %i32: type = class_type @Int, @Int(%int_32) [concrete] // CHECK:STDOUT: %LeftShift.type: type = fn_type @LeftShift [concrete] // CHECK:STDOUT: %LeftShift: %LeftShift.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @RuntimeCall(%a.param: %u32, %b.param: %i32) -> out %return.param: %u32 { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %LeftShift.ref: %LeftShift.type = name_ref LeftShift, file.%LeftShift.decl [concrete = constants.%LeftShift] // CHECK:STDOUT: %a.ref: %u32 = name_ref a, %a // CHECK:STDOUT: %b.ref: %i32 = name_ref b, %b // CHECK:STDOUT: %LeftShift.call: init %u32 = call %LeftShift.ref(%a.ref, %b.ref) // CHECK:STDOUT: return %LeftShift.call // CHECK:STDOUT: } // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/builtins/int/left_shift_assign.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/int.carbon // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/builtins/int/left_shift_assign.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/builtins/int/left_shift_assign.carbon // --- call.carbon library "[[@TEST_NAME]]"; fn Builtin(ref a: i32, b: i32) = "int.left_shift_assign"; fn Call(ref a: i32, b: i32) { //@dump-sem-ir-begin Builtin(ref a, b); //@dump-sem-ir-end } fn MixedTypes(ref a: i32, b: i64) = "int.left_shift_assign"; fn CallMixed(ref a: i32, b: i64) { //@dump-sem-ir-begin MixedTypes(ref a, b); //@dump-sem-ir-end } // --- fail_unsized.carbon library "[[@TEST_NAME]]"; // CHECK:STDERR: fail_unsized.carbon:[[@LINE+4]]:1: error: invalid signature for builtin function "int.left_shift_assign" [InvalidBuiltinSignature] // CHECK:STDERR: fn Builtin(ref a: Core.IntLiteral(), b: Core.IntLiteral()) = "int.left_shift_assign"; // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: fn Builtin(ref a: Core.IntLiteral(), b: Core.IntLiteral()) = "int.left_shift_assign"; // --- fail_bad_decl.carbon library "[[@TEST_NAME]]"; // CHECK:STDERR: fail_bad_decl.carbon:[[@LINE+4]]:1: error: invalid signature for builtin function "int.left_shift_assign" [InvalidBuiltinSignature] // CHECK:STDERR: fn NotRef(a: i32, b: i32) = "int.left_shift_assign"; // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: fn NotRef(a: i32, b: i32) = "int.left_shift_assign"; // CHECK:STDOUT: --- call.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [concrete] // CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] // CHECK:STDOUT: %i32: type = class_type @Int, @Int(%int_32) [concrete] // CHECK:STDOUT: %Builtin.type: type = fn_type @Builtin [concrete] // CHECK:STDOUT: %Builtin: %Builtin.type = struct_value () [concrete] // CHECK:STDOUT: %int_64: Core.IntLiteral = int_value 64 [concrete] // CHECK:STDOUT: %i64: type = class_type @Int, @Int(%int_64) [concrete] // CHECK:STDOUT: %MixedTypes.type: type = fn_type @MixedTypes [concrete] // CHECK:STDOUT: %MixedTypes: %MixedTypes.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @Call(%a.param: ref %i32, %b.param: %i32) { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %Builtin.ref: %Builtin.type = name_ref Builtin, file.%Builtin.decl [concrete = constants.%Builtin] // CHECK:STDOUT: %a.ref: ref %i32 = name_ref a, %a // CHECK:STDOUT: %.loc8: %i32 = ref_tag %a.ref // CHECK:STDOUT: %b.ref: %i32 = name_ref b, %b // CHECK:STDOUT: %Builtin.call: init %empty_tuple.type = call %Builtin.ref(%.loc8, %b.ref) // CHECK:STDOUT: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @CallMixed(%a.param: ref %i32, %b.param: %i64) { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %MixedTypes.ref: %MixedTypes.type = name_ref MixedTypes, file.%MixedTypes.decl [concrete = constants.%MixedTypes] // CHECK:STDOUT: %a.ref: ref %i32 = name_ref a, %a // CHECK:STDOUT: %.loc16: %i32 = ref_tag %a.ref // CHECK:STDOUT: %b.ref: %i64 = name_ref b, %b // CHECK:STDOUT: %MixedTypes.call: init %empty_tuple.type = call %MixedTypes.ref(%.loc16, %b.ref) // CHECK:STDOUT: // CHECK:STDOUT: } // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/builtins/int/less.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/primitives.carbon // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/builtins/int/less.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/builtins/int/less.carbon // --- int_less.carbon fn Less(a: i32, b: i32) -> bool = "int.less"; fn Negate(a: i32) -> i32 = "int.snegate"; class True {} class False {} fn F(true_: True, false_: False) { true_ as (if Less(1, 2) then True else False); false_ as (if Less(1, 1) then True else False); false_ as (if Less(1, 0) then True else False); true_ as (if Less(Negate(1), 0) then True else False); false_ as (if Less(0, Negate(1)) then True else False); } fn RuntimeCallIsValid(a: i32, b: i32) -> bool { //@dump-sem-ir-begin return Less(a, b); //@dump-sem-ir-end } // CHECK:STDOUT: --- int_less.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [concrete] // CHECK:STDOUT: %i32: type = class_type @Int, @Int(%int_32) [concrete] // CHECK:STDOUT: %Less.type: type = fn_type @Less [concrete] // CHECK:STDOUT: %Less: %Less.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @RuntimeCallIsValid(%a.param: %i32, %b.param: %i32) -> out %return.param: bool { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %Less.ref: %Less.type = name_ref Less, file.%Less.decl [concrete = constants.%Less] // CHECK:STDOUT: %a.ref: %i32 = name_ref a, %a // CHECK:STDOUT: %b.ref: %i32 = name_ref b, %b // CHECK:STDOUT: %Less.call: init bool = call %Less.ref(%a.ref, %b.ref) // CHECK:STDOUT: return %Less.call // CHECK:STDOUT: } // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/builtins/int/less_eq.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/primitives.carbon // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/builtins/int/less_eq.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/builtins/int/less_eq.carbon // --- int_less_eq.carbon library "[[@TEST_NAME]]"; fn LessEq(a: i32, b: i32) -> bool = "int.less_eq"; fn Negate(a: i32) -> i32 = "int.snegate"; class True {} class False {} fn F(true_: True, false_: False) { true_ as (if LessEq(1, 2) then True else False); true_ as (if LessEq(1, 1) then True else False); false_ as (if LessEq(1, 0) then True else False); true_ as (if LessEq(Negate(1), 0) then True else False); false_ as (if LessEq(0, Negate(1)) then True else False); } fn RuntimeCallIsValid(a: i32, b: i32) -> bool { //@dump-sem-ir-begin return LessEq(a, b); //@dump-sem-ir-end } // --- literal.carbon library "[[@TEST_NAME]]"; fn LessEq(a: Core.IntLiteral(), b: Core.IntLiteral()) -> bool = "int.less_eq"; class Expect(B:! bool) {} fn Test(B:! bool) -> Expect(B) { return {}; } fn F() { Test(LessEq(5, 5)) as Expect(true); Test(LessEq(5, 6)) as Expect(true); Test(LessEq(6, 5)) as Expect(false); Test(LessEq(-1, -1)) as Expect(true); Test(LessEq(-1, 1)) as Expect(true); Test(LessEq(1, -1)) as Expect(false); } // --- mixed.carbon library "[[@TEST_NAME]]"; fn LessEq(a: Core.IntLiteral(), b: i32) -> bool = "int.less_eq"; class Expect(B:! bool) {} fn Test(B:! bool) -> Expect(B) { return {}; } fn F() { Test(LessEq(5, 5)) as Expect(true); Test(LessEq(5, 6)) as Expect(true); Test(LessEq(6, 5)) as Expect(false); Test(LessEq(-1, -1)) as Expect(true); Test(LessEq(-1, 1)) as Expect(true); Test(LessEq(1, -1)) as Expect(false); } // --- fail_runtime_literal.carbon library "[[@TEST_NAME]]"; fn LessEq(a: Core.IntLiteral(), b: Core.IntLiteral()) -> bool = "int.less_eq"; fn Test(n: Core.IntLiteral()) { // OK LessEq(1, 1); // CHECK:STDERR: fail_runtime_literal.carbon:[[@LINE+7]]:3: error: non-constant call to compile-time-only function [NonConstantCallToCompTimeOnlyFunction] // CHECK:STDERR: LessEq(n, 1); // CHECK:STDERR: ^~~~~~~~~~~~ // CHECK:STDERR: fail_runtime_literal.carbon:[[@LINE-8]]:1: note: compile-time-only function declared here [CompTimeOnlyFunctionHere] // CHECK:STDERR: fn LessEq(a: Core.IntLiteral(), b: Core.IntLiteral()) -> bool = "int.less_eq"; // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: LessEq(n, 1); // CHECK:STDERR: fail_runtime_literal.carbon:[[@LINE+7]]:3: error: non-constant call to compile-time-only function [NonConstantCallToCompTimeOnlyFunction] // CHECK:STDERR: LessEq(1, n); // CHECK:STDERR: ^~~~~~~~~~~~ // CHECK:STDERR: fail_runtime_literal.carbon:[[@LINE-16]]:1: note: compile-time-only function declared here [CompTimeOnlyFunctionHere] // CHECK:STDERR: fn LessEq(a: Core.IntLiteral(), b: Core.IntLiteral()) -> bool = "int.less_eq"; // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: LessEq(1, n); // CHECK:STDERR: fail_runtime_literal.carbon:[[@LINE+7]]:3: error: non-constant call to compile-time-only function [NonConstantCallToCompTimeOnlyFunction] // CHECK:STDERR: LessEq(n, n); // CHECK:STDERR: ^~~~~~~~~~~~ // CHECK:STDERR: fail_runtime_literal.carbon:[[@LINE-24]]:1: note: compile-time-only function declared here [CompTimeOnlyFunctionHere] // CHECK:STDERR: fn LessEq(a: Core.IntLiteral(), b: Core.IntLiteral()) -> bool = "int.less_eq"; // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: LessEq(n, n); } // CHECK:STDOUT: --- int_less_eq.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [concrete] // CHECK:STDOUT: %i32: type = class_type @Int, @Int(%int_32) [concrete] // CHECK:STDOUT: %LessEq.type: type = fn_type @LessEq [concrete] // CHECK:STDOUT: %LessEq: %LessEq.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @RuntimeCallIsValid(%a.param: %i32, %b.param: %i32) -> out %return.param: bool { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %LessEq.ref: %LessEq.type = name_ref LessEq, file.%LessEq.decl [concrete = constants.%LessEq] // CHECK:STDOUT: %a.ref: %i32 = name_ref a, %a // CHECK:STDOUT: %b.ref: %i32 = name_ref b, %b // CHECK:STDOUT: %LessEq.call: init bool = call %LessEq.ref(%a.ref, %b.ref) // CHECK:STDOUT: return %LessEq.call // CHECK:STDOUT: } // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/builtins/int/make_type_signed.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/convert.carbon // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/builtins/int/make_type_signed.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/builtins/int/make_type_signed.carbon // --- types.carbon library "[[@TEST_NAME]]"; fn IntLiteral() -> type = "int_literal.make_type"; fn Int(n: IntLiteral()) -> type = "int.make_type_signed"; // --- use_types.carbon library "[[@TEST_NAME]]"; import library "types"; fn Copy[N:! IntLiteral()](x: Int(N)) -> Int(N) = "primitive_copy"; fn F(n: Int(64)) -> //@dump-sem-ir-begin Int(64) //@dump-sem-ir-end { return Copy(n); } fn G(n: Int(13)) -> //@dump-sem-ir-begin Int(13) //@dump-sem-ir-end { return Copy(n); } fn Symbolic(N:! IntLiteral(), x: Int(N)) -> Int(N) { return Copy(x); } // --- import_types.carbon library "[[@TEST_NAME]]"; import library "types"; import library "use_types"; fn UseF(n: Int(64)) -> Int(64) { return F(n); } fn UseG(n: Int(13)) -> Int(13) { return G(n); } fn UseSymbolic(n: Int(24)) -> Int(24) { return Symbolic(24, n); } // --- fail_zero_size.carbon library "[[@TEST_NAME]]"; import library "types"; // CHECK:STDERR: fail_zero_size.carbon:[[@LINE+4]]:8: error: integer type width of 0 is not positive [IntWidthNotPositive] // CHECK:STDERR: var n: Int(0); // CHECK:STDERR: ^~~~~~ // CHECK:STDERR: var n: Int(0); // --- fail_negative_size.carbon library "[[@TEST_NAME]]"; import library "types"; fn Negate(a: IntLiteral()) -> IntLiteral() = "int.snegate"; // CHECK:STDERR: fail_negative_size.carbon:[[@LINE+4]]:8: error: integer type width of -1 is not positive [IntWidthNotPositive] // CHECK:STDERR: var n: Int(Negate(1)); // CHECK:STDERR: ^~~~~~~~~~~~~~ // CHECK:STDERR: var n: Int(Negate(1)); // --- fail_oversized.carbon library "[[@TEST_NAME]]"; import library "types"; // CHECK:STDERR: fail_oversized.carbon:[[@LINE+4]]:8: error: integer type width of 1000000000 is greater than the maximum supported width of 8388608 [IntWidthTooLarge] // CHECK:STDERR: var m: Int(1000000000); // CHECK:STDERR: ^~~~~~~~~~~~~~~ // CHECK:STDERR: var m: Int(1000000000); // CHECK:STDOUT: --- use_types.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %int_64: Core.IntLiteral = int_value 64 [concrete] // CHECK:STDOUT: %i64.builtin: type = int_type signed, %int_64 [concrete] // CHECK:STDOUT: %int_13: Core.IntLiteral = int_value 13 [concrete] // CHECK:STDOUT: %i13.builtin: type = int_type signed, %int_13 [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @F(%n.param: %i64.builtin) -> out %return.param: %i64.builtin { // CHECK:STDOUT: !entry: // CHECK:STDOUT: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @G(%n.param: %i13.builtin) -> out %return.param: %i13.builtin { // CHECK:STDOUT: !entry: // CHECK:STDOUT: // CHECK:STDOUT: } // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/builtins/int/make_type_unsigned.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/int.carbon // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/builtins/int/make_type_unsigned.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/builtins/int/make_type_unsigned.carbon // --- types.carbon library "[[@TEST_NAME]]"; fn IntLiteral() -> type = "int_literal.make_type"; fn UInt(n: IntLiteral()) -> type = "int.make_type_unsigned"; // --- use_types.carbon library "[[@TEST_NAME]]"; import library "types"; fn Copy[N:! IntLiteral()](x: UInt(N)) -> UInt(N) = "primitive_copy"; fn F(n: UInt(64)) -> //@dump-sem-ir-begin UInt(64) //@dump-sem-ir-end { return Copy(n); } fn G(n: UInt(13)) -> //@dump-sem-ir-begin UInt(13) //@dump-sem-ir-end { return Copy(n); } fn Symbolic(N:! IntLiteral(), x: UInt(N)) -> UInt(N) { return Copy(x); } // --- fail_zero_size.carbon library "[[@TEST_NAME]]"; import library "types"; // CHECK:STDERR: fail_zero_size.carbon:[[@LINE+4]]:8: error: integer type width of 0 is not positive [IntWidthNotPositive] // CHECK:STDERR: var n: UInt(0); // CHECK:STDERR: ^~~~~~~ // CHECK:STDERR: var n: UInt(0); // --- fail_negative_size.carbon library "[[@TEST_NAME]]"; import library "types"; fn Negate(n: i32) -> i32 = "int.snegate"; // CHECK:STDERR: fail_negative_size.carbon:[[@LINE+4]]:8: error: integer type width of -1 is not positive [IntWidthNotPositive] // CHECK:STDERR: var n: UInt(Negate(1)); // CHECK:STDERR: ^~~~~~~~~~~~~~~ // CHECK:STDERR: var n: UInt(Negate(1)); // --- fail_oversized.carbon library "[[@TEST_NAME]]"; import library "types"; // CHECK:STDERR: fail_oversized.carbon:[[@LINE+4]]:8: error: integer type width of 1000000000 is greater than the maximum supported width of 8388608 [IntWidthTooLarge] // CHECK:STDERR: var m: UInt(1000000000); // CHECK:STDERR: ^~~~~~~~~~~~~~~~ // CHECK:STDERR: var m: UInt(1000000000); // CHECK:STDOUT: --- use_types.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %int_64: Core.IntLiteral = int_value 64 [concrete] // CHECK:STDOUT: %u64.builtin: type = int_type unsigned, %int_64 [concrete] // CHECK:STDOUT: %int_13: Core.IntLiteral = int_value 13 [concrete] // CHECK:STDOUT: %u13.builtin: type = int_type unsigned, %int_13 [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @F(%n.param: %u64.builtin) -> out %return.param: %u64.builtin { // CHECK:STDOUT: !entry: // CHECK:STDOUT: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @G(%n.param: %u13.builtin) -> out %return.param: %u13.builtin { // CHECK:STDOUT: !entry: // CHECK:STDOUT: // CHECK:STDOUT: } // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/builtins/int/neq.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/primitives.carbon // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/builtins/int/neq.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/builtins/int/neq.carbon // --- int_neq.carbon fn Neq(a: i32, b: i32) -> bool = "int.neq"; class True {} class False {} fn F(true_: True, false_: False) { false_ as (if Neq(1, 1) then True else False); true_ as (if Neq(1, 2) then True else False); } fn RuntimeCallIsValid(a: i32, b: i32) -> bool { //@dump-sem-ir-begin return Neq(a, b); //@dump-sem-ir-end } // CHECK:STDOUT: --- int_neq.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [concrete] // CHECK:STDOUT: %i32: type = class_type @Int, @Int(%int_32) [concrete] // CHECK:STDOUT: %Neq.type: type = fn_type @Neq [concrete] // CHECK:STDOUT: %Neq: %Neq.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @RuntimeCallIsValid(%a.param: %i32, %b.param: %i32) -> out %return.param: bool { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %Neq.ref: %Neq.type = name_ref Neq, file.%Neq.decl [concrete = constants.%Neq] // CHECK:STDOUT: %a.ref: %i32 = name_ref a, %a // CHECK:STDOUT: %b.ref: %i32 = name_ref b, %b // CHECK:STDOUT: %Neq.call: init bool = call %Neq.ref(%a.ref, %b.ref) // CHECK:STDOUT: return %Neq.call // CHECK:STDOUT: } // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/builtins/int/or.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/int.carbon // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/builtins/int/or.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/builtins/int/or.carbon // --- int_or.carbon fn Or(a: i32, b: i32) -> i32 = "int.or"; var arr: array(i32, Or(12, 10)); let arr_p: array(i32, 14)* = &arr; fn RuntimeCallIsValid(a: i32, b: i32) -> i32 { //@dump-sem-ir-begin return Or(a, b); //@dump-sem-ir-end } // CHECK:STDOUT: --- int_or.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [concrete] // CHECK:STDOUT: %i32: type = class_type @Int, @Int(%int_32) [concrete] // CHECK:STDOUT: %Or.type: type = fn_type @Or [concrete] // CHECK:STDOUT: %Or: %Or.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @RuntimeCallIsValid(%a.param: %i32, %b.param: %i32) -> out %return.param: %i32 { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %Or.ref: %Or.type = name_ref Or, file.%Or.decl [concrete = constants.%Or] // CHECK:STDOUT: %a.ref: %i32 = name_ref a, %a // CHECK:STDOUT: %b.ref: %i32 = name_ref b, %b // CHECK:STDOUT: %Or.call: init %i32 = call %Or.ref(%a.ref, %b.ref) // CHECK:STDOUT: return %Or.call // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @__global_init() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: // CHECK:STDOUT: } // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/builtins/int/or_assign.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/int.carbon // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/builtins/int/or_assign.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/builtins/int/or_assign.carbon // --- call.carbon library "[[@TEST_NAME]]"; fn Builtin(ref a: i32, b: i32) = "int.or_assign"; fn Call(ref a: i32, b: i32) { //@dump-sem-ir-begin Builtin(ref a, b); //@dump-sem-ir-end } // --- fail_unsized.carbon library "[[@TEST_NAME]]"; // CHECK:STDERR: fail_unsized.carbon:[[@LINE+4]]:1: error: invalid signature for builtin function "int.or_assign" [InvalidBuiltinSignature] // CHECK:STDERR: fn Builtin(ref a: Core.IntLiteral(), b: Core.IntLiteral()) = "int.or_assign"; // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: fn Builtin(ref a: Core.IntLiteral(), b: Core.IntLiteral()) = "int.or_assign"; // --- fail_bad_decl.carbon library "[[@TEST_NAME]]"; // CHECK:STDERR: fail_bad_decl.carbon:[[@LINE+4]]:1: error: invalid signature for builtin function "int.or_assign" [InvalidBuiltinSignature] // CHECK:STDERR: fn NotRef(a: i32, b: i32) = "int.or_assign"; // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: fn NotRef(a: i32, b: i32) = "int.or_assign"; // CHECK:STDERR: fail_bad_decl.carbon:[[@LINE+4]]:1: error: invalid signature for builtin function "int.or_assign" [InvalidBuiltinSignature] // CHECK:STDERR: fn MixedTypes(ref a: i32, b: i64) = "int.or_assign"; // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: fn MixedTypes(ref a: i32, b: i64) = "int.or_assign"; // CHECK:STDOUT: --- call.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [concrete] // CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] // CHECK:STDOUT: %i32: type = class_type @Int, @Int(%int_32) [concrete] // CHECK:STDOUT: %Builtin.type: type = fn_type @Builtin [concrete] // CHECK:STDOUT: %Builtin: %Builtin.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @Call(%a.param: ref %i32, %b.param: %i32) { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %Builtin.ref: %Builtin.type = name_ref Builtin, file.%Builtin.decl [concrete = constants.%Builtin] // CHECK:STDOUT: %a.ref: ref %i32 = name_ref a, %a // CHECK:STDOUT: %.loc8: %i32 = ref_tag %a.ref // CHECK:STDOUT: %b.ref: %i32 = name_ref b, %b // CHECK:STDOUT: %Builtin.call: init %empty_tuple.type = call %Builtin.ref(%.loc8, %b.ref) // CHECK:STDOUT: // CHECK:STDOUT: } // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/builtins/int/right_shift.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/uint.carbon // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/builtins/int/right_shift.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/builtins/int/right_shift.carbon // --- i32.carbon library "[[@TEST_NAME]]"; class Expect(N:! i32) {} fn Test(N:! i32) -> Expect(N) { return {}; } fn RightShift(a: i32, b: i32) -> i32 = "int.right_shift"; fn F() { Test(RightShift(0, 31)) as Expect(0); Test(RightShift(1, 31)) as Expect(0); Test(RightShift(1, 0)) as Expect(1); Test(RightShift(1, 2)) as Expect(0); Test(RightShift(22, 2)) as Expect(5); Test(RightShift(-1, 1)) as Expect(-1); Test(RightShift(-2, 1)) as Expect(-1); Test(RightShift(-10, 2)) as Expect(-3); } fn RuntimeCallIsValid(a: i32, b: i32) -> i32 { //@dump-sem-ir-begin return RightShift(a, b); //@dump-sem-ir-end } // --- u32.carbon library "[[@TEST_NAME]]"; class Expect(N:! u32) {} fn Test(N:! u32) -> Expect(N) { return {}; } fn RightShift(a: u32, b: i32) -> u32 = "int.right_shift"; fn F() { Test(RightShift(0, 31)) as Expect(0); Test(RightShift(1, 31)) as Expect(0); Test(RightShift(1, 0)) as Expect(1); Test(RightShift(1, 2)) as Expect(0); Test(RightShift(22, 2)) as Expect(5); Test(RightShift(0xFFFF_FFFF, 1)) as Expect(0x7FFF_FFFF); Test(RightShift(0xABCD_EF01, 8)) as Expect(0xAB_CDEF); } fn RuntimeCall(a: u32, b: i32) -> u32 { //@dump-sem-ir-begin return RightShift(a, b); //@dump-sem-ir-end } // --- literal.carbon library "[[@TEST_NAME]]"; fn RightShift(a: Core.IntLiteral(), b: Core.IntLiteral()) -> Core.IntLiteral() = "int.right_shift"; class Expect(N:! Core.IntLiteral()) {} fn Test(N:! Core.IntLiteral()) -> Expect(N) { return {}; } fn F() { Test(RightShift(0, 31)) as Expect(0); Test(RightShift(1, 31)) as Expect(0); Test(RightShift(1, 0)) as Expect(1); Test(RightShift(1, 2)) as Expect(0); Test(RightShift(22, 2)) as Expect(5); Test(RightShift(-1, 1)) as Expect(-1); Test(RightShift(-2, 1)) as Expect(-1); Test(RightShift(-10, 2)) as Expect(-3); Test(RightShift(0xFFFF_FFFF, 1)) as Expect(0x7FFF_FFFF); Test(RightShift(0xABCD_EF01, 8)) as Expect(0xAB_CDEF); Test(RightShift(0x1234_5678, 1_000_000_000)) as Expect(0); Test(RightShift(-0x1234_5678, 1_000_000_000)) as Expect(-1); Test(RightShift(0xFFFF_FFFF_FFFF_FFFF, 1_000_000_000)) as Expect(0); Test(RightShift(0x7FFF_FFFF_FFFF_FFFF, 1_000_000_000)) as Expect(0); Test(RightShift(-0x7FFF_FFFF_FFFF_FFFF, 1_000_000_000)) as Expect(-1); Test(RightShift(-0x8000_0000_0000_0000, 1_000_000_000)) as Expect(-1); } // --- fail_bad_shift.carbon library "[[@TEST_NAME]]"; fn RightShift(a: i32, b: i32) -> i32 = "int.right_shift"; fn RightShiftLit(a: Core.IntLiteral(), b: i32) -> Core.IntLiteral() = "int.right_shift"; // Shift greater than size is disallowed for sized types. let size_1: i32 = RightShift(1, 31); // CHECK:STDERR: fail_bad_shift.carbon:[[@LINE+4]]:19: error: shift distance >= type width of 32 in `1 >> 32` [CompileTimeShiftOutOfRange] // CHECK:STDERR: let size_2: i32 = RightShift(1, 32); // CHECK:STDERR: ^~~~~~~~~~~~~~~~~ // CHECK:STDERR: let size_2: i32 = RightShift(1, 32); // CHECK:STDERR: fail_bad_shift.carbon:[[@LINE+4]]:19: error: shift distance >= type width of 32 in `1 >> 33` [CompileTimeShiftOutOfRange] // CHECK:STDERR: let size_3: i32 = RightShift(1, 33); // CHECK:STDERR: ^~~~~~~~~~~~~~~~~ // CHECK:STDERR: let size_3: i32 = RightShift(1, 33); // Negative shifts aren't allowed either, even for literals, even if the lhs is zero. // CHECK:STDERR: fail_bad_shift.carbon:[[@LINE+4]]:21: error: shift distance >= type width of 32 in `1 >> -1` [CompileTimeShiftOutOfRange] // CHECK:STDERR: let negative: i32 = RightShift(1, -1); // CHECK:STDERR: ^~~~~~~~~~~~~~~~~ // CHECK:STDERR: let negative: i32 = RightShift(1, -1); // CHECK:STDERR: fail_bad_shift.carbon:[[@LINE+4]]:26: error: shift distance >= type width of 32 in `0 >> -1` [CompileTimeShiftOutOfRange] // CHECK:STDERR: let negative_zero: i32 = RightShift(0, -1); // CHECK:STDERR: ^~~~~~~~~~~~~~~~~ // CHECK:STDERR: let negative_zero: i32 = RightShift(0, -1); // CHECK:STDERR: fail_bad_shift.carbon:[[@LINE+4]]:39: error: shift distance negative in `1 >> -1` [CompileTimeShiftNegative] // CHECK:STDERR: let negative_lit: Core.IntLiteral() = RightShiftLit(1, -1); // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: let negative_lit: Core.IntLiteral() = RightShiftLit(1, -1); // CHECK:STDERR: fail_bad_shift.carbon:[[@LINE+4]]:44: error: shift distance negative in `0 >> -1` [CompileTimeShiftNegative] // CHECK:STDERR: let negative_lit_zero: Core.IntLiteral() = RightShiftLit(0, -1); // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: let negative_lit_zero: Core.IntLiteral() = RightShiftLit(0, -1); // CHECK:STDOUT: --- i32.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [concrete] // CHECK:STDOUT: %i32: type = class_type @Int, @Int(%int_32) [concrete] // CHECK:STDOUT: %RightShift.type: type = fn_type @RightShift [concrete] // CHECK:STDOUT: %RightShift: %RightShift.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @RuntimeCallIsValid(%a.param: %i32, %b.param: %i32) -> out %return.param: %i32 { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %RightShift.ref: %RightShift.type = name_ref RightShift, file.%RightShift.decl [concrete = constants.%RightShift] // CHECK:STDOUT: %a.ref: %i32 = name_ref a, %a // CHECK:STDOUT: %b.ref: %i32 = name_ref b, %b // CHECK:STDOUT: %RightShift.call: init %i32 = call %RightShift.ref(%a.ref, %b.ref) // CHECK:STDOUT: return %RightShift.call // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- u32.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [concrete] // CHECK:STDOUT: %u32: type = class_type @UInt, @UInt(%int_32) [concrete] // CHECK:STDOUT: %i32: type = class_type @Int, @Int(%int_32) [concrete] // CHECK:STDOUT: %RightShift.type: type = fn_type @RightShift [concrete] // CHECK:STDOUT: %RightShift: %RightShift.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @RuntimeCall(%a.param: %u32, %b.param: %i32) -> out %return.param: %u32 { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %RightShift.ref: %RightShift.type = name_ref RightShift, file.%RightShift.decl [concrete = constants.%RightShift] // CHECK:STDOUT: %a.ref: %u32 = name_ref a, %a // CHECK:STDOUT: %b.ref: %i32 = name_ref b, %b // CHECK:STDOUT: %RightShift.call: init %u32 = call %RightShift.ref(%a.ref, %b.ref) // CHECK:STDOUT: return %RightShift.call // CHECK:STDOUT: } // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/builtins/int/right_shift_assign.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/int.carbon // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/builtins/int/right_shift_assign.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/builtins/int/right_shift_assign.carbon // --- call.carbon library "[[@TEST_NAME]]"; fn Builtin(ref a: i32, b: i32) = "int.right_shift_assign"; fn Call(ref a: i32, b: i32) { //@dump-sem-ir-begin Builtin(ref a, b); //@dump-sem-ir-end } fn MixedTypes(ref a: i32, b: i64) = "int.right_shift_assign"; fn CallMixed(ref a: i32, b: i64) { //@dump-sem-ir-begin MixedTypes(ref a, b); //@dump-sem-ir-end } // --- fail_unsized.carbon library "[[@TEST_NAME]]"; // CHECK:STDERR: fail_unsized.carbon:[[@LINE+4]]:1: error: invalid signature for builtin function "int.right_shift_assign" [InvalidBuiltinSignature] // CHECK:STDERR: fn Builtin(ref a: Core.IntLiteral(), b: Core.IntLiteral()) = "int.right_shift_assign"; // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: fn Builtin(ref a: Core.IntLiteral(), b: Core.IntLiteral()) = "int.right_shift_assign"; // --- fail_bad_decl.carbon library "[[@TEST_NAME]]"; // CHECK:STDERR: fail_bad_decl.carbon:[[@LINE+4]]:1: error: invalid signature for builtin function "int.right_shift_assign" [InvalidBuiltinSignature] // CHECK:STDERR: fn NotRef(a: i32, b: i32) = "int.right_shift_assign"; // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: fn NotRef(a: i32, b: i32) = "int.right_shift_assign"; // CHECK:STDOUT: --- call.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [concrete] // CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] // CHECK:STDOUT: %i32: type = class_type @Int, @Int(%int_32) [concrete] // CHECK:STDOUT: %Builtin.type: type = fn_type @Builtin [concrete] // CHECK:STDOUT: %Builtin: %Builtin.type = struct_value () [concrete] // CHECK:STDOUT: %int_64: Core.IntLiteral = int_value 64 [concrete] // CHECK:STDOUT: %i64: type = class_type @Int, @Int(%int_64) [concrete] // CHECK:STDOUT: %MixedTypes.type: type = fn_type @MixedTypes [concrete] // CHECK:STDOUT: %MixedTypes: %MixedTypes.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @Call(%a.param: ref %i32, %b.param: %i32) { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %Builtin.ref: %Builtin.type = name_ref Builtin, file.%Builtin.decl [concrete = constants.%Builtin] // CHECK:STDOUT: %a.ref: ref %i32 = name_ref a, %a // CHECK:STDOUT: %.loc8: %i32 = ref_tag %a.ref // CHECK:STDOUT: %b.ref: %i32 = name_ref b, %b // CHECK:STDOUT: %Builtin.call: init %empty_tuple.type = call %Builtin.ref(%.loc8, %b.ref) // CHECK:STDOUT: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @CallMixed(%a.param: ref %i32, %b.param: %i64) { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %MixedTypes.ref: %MixedTypes.type = name_ref MixedTypes, file.%MixedTypes.decl [concrete = constants.%MixedTypes] // CHECK:STDOUT: %a.ref: ref %i32 = name_ref a, %a // CHECK:STDOUT: %.loc16: %i32 = ref_tag %a.ref // CHECK:STDOUT: %b.ref: %i64 = name_ref b, %b // CHECK:STDOUT: %MixedTypes.call: init %empty_tuple.type = call %MixedTypes.ref(%.loc16, %b.ref) // CHECK:STDOUT: // CHECK:STDOUT: } // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/builtins/int/sadd.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/primitives.carbon // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/builtins/int/sadd.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/builtins/int/sadd.carbon // --- i32.carbon library "[[@TEST_NAME]]"; fn Add(a: i32, b: i32) -> i32 = "int.sadd"; class Expect(N:! i32) {} fn Test(N:! i32) -> Expect(N) { return {}; } fn F() { Test(Add(0, 0)) as Expect(0); Test(Add(1, 2)) as Expect(3); Test(Add(0x7FFF_FFFE, 1)) as Expect(0x7FFF_FFFF); } fn RuntimeCallIsValid(a: i32, b: i32) -> i32 { //@dump-sem-ir-begin return Add(a, b); //@dump-sem-ir-end } // --- literal.carbon library "[[@TEST_NAME]]"; fn Add(a: Core.IntLiteral(), b: Core.IntLiteral()) -> Core.IntLiteral() = "int.sadd"; class Expect(N:! Core.IntLiteral()) {} fn Test(N:! Core.IntLiteral()) -> Expect(N) { return {}; } fn F() { Test(Add(0, 0)) as Expect(0); Test(Add(1, 2)) as Expect(3); // Test some cases that might -- but shouldn't -- overflow. Test(Add(0x7FFF_FFFE, 1)) as Expect(0x7FFF_FFFF); Test(Add(0x7FFF_FFFF, 1)) as Expect(0x8000_0000); Test(Add(0x7FFF_FFFF_FFFF_FFFF, 1)) as Expect(0x8000_0000_0000_0000); Test(Add(0xFFFF_FFFF_FFFF_FFFF, 1)) as Expect(0x1_0000_0000_0000_0000); Test(Add(-0x8000_0000_0000_0000, -1)) as Expect(-0x8000_0000_0000_0001); Test(Add(-0x8000_0000_0000_0000, -0x8000_0000_0000_0000)) as Expect(-0x1_0000_0000_0000_0000); } // --- fail_too_few.carbon library "[[@TEST_NAME]]"; // CHECK:STDERR: fail_too_few.carbon:[[@LINE+4]]:1: error: invalid signature for builtin function "int.sadd" [InvalidBuiltinSignature] // CHECK:STDERR: fn TooFew(a: i32) -> i32 = "int.sadd"; // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: fn TooFew(a: i32) -> i32 = "int.sadd"; // CHECK:STDERR: fail_too_few.carbon:[[@LINE+4]]:26: error: name `TooMany` not found [NameNotFound] // CHECK:STDERR: var too_many: array(i32, TooMany(1, 2, 3)); // CHECK:STDERR: ^~~~~~~ // CHECK:STDERR: var too_many: array(i32, TooMany(1, 2, 3)); fn RuntimeCallIsValidTooFew(a: i32) -> i32 { return TooFew(a); } // --- fail_too_many.carbon library "[[@TEST_NAME]]"; // CHECK:STDERR: fail_too_many.carbon:[[@LINE+4]]:1: error: invalid signature for builtin function "int.sadd" [InvalidBuiltinSignature] // CHECK:STDERR: fn TooMany(a: i32, b: i32, c: i32) -> i32 = "int.sadd"; // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: fn TooMany(a: i32, b: i32, c: i32) -> i32 = "int.sadd"; // CHECK:STDERR: fail_too_many.carbon:[[@LINE+4]]:33: error: name `BadReturnType` not found [NameNotFound] // CHECK:STDERR: var bad_return_type: array(i32, BadReturnType(1, 2)); // CHECK:STDERR: ^~~~~~~~~~~~~ // CHECK:STDERR: var bad_return_type: array(i32, BadReturnType(1, 2)); fn RuntimeCallIsValidTooMany(a: i32, b: i32, c: i32) -> i32 { return TooMany(a, b, c); } // --- fail_bad_return_type.carbon library "[[@TEST_NAME]]"; // CHECK:STDERR: fail_bad_return_type.carbon:[[@LINE+4]]:1: error: invalid signature for builtin function "int.sadd" [InvalidBuiltinSignature] // CHECK:STDERR: fn BadReturnType(a: i32, b: i32) -> bool = "int.sadd"; // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: fn BadReturnType(a: i32, b: i32) -> bool = "int.sadd"; // CHECK:STDERR: fail_bad_return_type.carbon:[[@LINE+4]]:25: error: name `TooFew` not found [NameNotFound] // CHECK:STDERR: var too_few: array(i32, TooFew(1)); // CHECK:STDERR: ^~~~~~ // CHECK:STDERR: var too_few: array(i32, TooFew(1)); fn RuntimeCallIsValidBadReturnType(a: i32, b: i32) -> bool { return BadReturnType(a, b); } // --- fail_bad_call.carbon library "[[@TEST_NAME]]"; // CHECK:STDERR: fail_bad_call.carbon:[[@LINE+4]]:26: error: name `JustRight` not found [NameNotFound] // CHECK:STDERR: var bad_call: array(i32, JustRight(1, 2, 3)); // CHECK:STDERR: ^~~~~~~~~ // CHECK:STDERR: var bad_call: array(i32, JustRight(1, 2, 3)); // --- fail_mixed_add.carbon library "[[@TEST_NAME]]"; // CHECK:STDERR: fail_mixed_add.carbon:[[@LINE+4]]:1: error: invalid signature for builtin function "int.sadd" [InvalidBuiltinSignature] // CHECK:STDERR: fn MixedAdd1(a: i32, b: Core.IntLiteral()) -> i32 = "int.sadd"; // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: fn MixedAdd1(a: i32, b: Core.IntLiteral()) -> i32 = "int.sadd"; // CHECK:STDERR: fail_mixed_add.carbon:[[@LINE+4]]:1: error: invalid signature for builtin function "int.sadd" [InvalidBuiltinSignature] // CHECK:STDERR: fn MixedAdd2(a: Core.IntLiteral(), b: i32) -> i32 = "int.sadd"; // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: fn MixedAdd2(a: Core.IntLiteral(), b: i32) -> i32 = "int.sadd"; // CHECK:STDERR: fail_mixed_add.carbon:[[@LINE+4]]:1: error: invalid signature for builtin function "int.sadd" [InvalidBuiltinSignature] // CHECK:STDERR: fn MixedAdd3(a: i32, b: Core.IntLiteral()) -> Core.IntLiteral() = "int.sadd"; // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: fn MixedAdd3(a: i32, b: Core.IntLiteral()) -> Core.IntLiteral() = "int.sadd"; // CHECK:STDERR: fail_mixed_add.carbon:[[@LINE+4]]:1: error: invalid signature for builtin function "int.sadd" [InvalidBuiltinSignature] // CHECK:STDERR: fn MixedAdd4(a: Core.IntLiteral(), b: i32) -> Core.IntLiteral() = "int.sadd"; // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: fn MixedAdd4(a: Core.IntLiteral(), b: i32) -> Core.IntLiteral() = "int.sadd"; // --- fail_overflow.carbon library "[[@TEST_NAME]]"; fn Add(a: i32, b: i32) -> i32 = "int.sadd"; let a: i32 = Add(0x7FFFFFFF, 0); // CHECK:STDERR: fail_overflow.carbon:[[@LINE+4]]:14: error: integer overflow in calculation `2147483647 + 1` [CompileTimeIntegerOverflow] // CHECK:STDERR: let b: i32 = Add(0x7FFFFFFF, 1); // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~ // CHECK:STDERR: let b: i32 = Add(0x7FFFFFFF, 1); // CHECK:STDOUT: --- i32.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [concrete] // CHECK:STDOUT: %i32: type = class_type @Int, @Int(%int_32) [concrete] // CHECK:STDOUT: %Add.type: type = fn_type @Add [concrete] // CHECK:STDOUT: %Add: %Add.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @RuntimeCallIsValid(%a.param: %i32, %b.param: %i32) -> out %return.param: %i32 { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %Add.ref: %Add.type = name_ref Add, file.%Add.decl [concrete = constants.%Add] // CHECK:STDOUT: %a.ref: %i32 = name_ref a, %a // CHECK:STDOUT: %b.ref: %i32 = name_ref b, %b // CHECK:STDOUT: %Add.call: init %i32 = call %Add.ref(%a.ref, %b.ref) // CHECK:STDOUT: return %Add.call // CHECK:STDOUT: } // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/builtins/int/sadd_assign.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/int.carbon // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/builtins/int/sadd_assign.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/builtins/int/sadd_assign.carbon // --- call.carbon library "[[@TEST_NAME]]"; fn Builtin(ref a: i32, b: i32) = "int.sadd_assign"; fn Call(ref a: i32, b: i32) { Builtin(ref a, b); } // --- fail_unsized.carbon library "[[@TEST_NAME]]"; // CHECK:STDERR: fail_unsized.carbon:[[@LINE+4]]:1: error: invalid signature for builtin function "int.sadd_assign" [InvalidBuiltinSignature] // CHECK:STDERR: fn Builtin(ref a: Core.IntLiteral(), b: Core.IntLiteral()) = "int.sadd_assign"; // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: fn Builtin(ref a: Core.IntLiteral(), b: Core.IntLiteral()) = "int.sadd_assign"; // --- fail_bad_decl.carbon library "[[@TEST_NAME]]"; // CHECK:STDERR: fail_bad_decl.carbon:[[@LINE+4]]:1: error: invalid signature for builtin function "int.sadd_assign" [InvalidBuiltinSignature] // CHECK:STDERR: fn NotRef(a: i32, b: i32) = "int.sadd_assign"; // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: fn NotRef(a: i32, b: i32) = "int.sadd_assign"; // CHECK:STDERR: fail_bad_decl.carbon:[[@LINE+4]]:1: error: invalid signature for builtin function "int.sadd_assign" [InvalidBuiltinSignature] // CHECK:STDERR: fn MixedTypes(ref a: i32, b: i64) = "int.sadd_assign"; // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: fn MixedTypes(ref a: i32, b: i64) = "int.sadd_assign"; ================================================ FILE: toolchain/check/testdata/builtins/int/sdiv.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/int.carbon // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/builtins/int/sdiv.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/builtins/int/sdiv.carbon // --- int_div.carbon library "[[@TEST_NAME]]"; fn Div(a: i32, b: i32) -> i32 = "int.sdiv"; var arr: array(i32, Div(3, 2)); let arr_p: array(i32, 1)* = &arr; fn RuntimeCallIsValid(a: i32, b: i32) -> i32 { //@dump-sem-ir-begin return Div(a, b); //@dump-sem-ir-end } // --- fail_overflow.carbon library "[[@TEST_NAME]]"; fn Div(a: i32, b: i32) -> i32 = "int.sdiv"; fn Sub(a: i32, b: i32) -> i32 = "int.ssub"; fn Negate(a: i32) -> i32 = "int.snegate"; // -0x7FFF_FFFF / -1 is OK. let a: i32 = Div(-0x7FFF_FFFF, -1); // -0x8000_0000 / 1 is OK. let b: i32 = Div(-0x8000_0000, 1); // -0x8000_0000 / -1 overflows. // CHECK:STDERR: fail_overflow.carbon:[[@LINE+4]]:14: error: integer overflow in calculation `-2147483648 / -1` [CompileTimeIntegerOverflow] // CHECK:STDERR: let c: i32 = Div(-0x8000_0000, -1); // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: let c: i32 = Div(-0x8000_0000, -1); // --- literal_no_overflow.carbon library "[[@TEST_NAME]]"; fn Div(a: Core.IntLiteral(), b: Core.IntLiteral()) -> Core.IntLiteral() = "int.sdiv"; class Expect(N:! Core.IntLiteral()) {} fn Test(N:! Core.IntLiteral()) -> Expect(N) { return {}; } fn F() { Test(Div(-0x8000_0000, -1)) as Expect(0x8000_0000); Test(Div(-0x8000_0000_0000_0000, -1)) as Expect(0x8000_0000_0000_0000); } // --- fail_div_by_zero.carbon library "[[@TEST_NAME]]"; fn Div(a: i32, b: i32) -> i32 = "int.sdiv"; fn DivLit(a: Core.IntLiteral(), b: Core.IntLiteral()) -> Core.IntLiteral() = "int.sdiv"; // CHECK:STDERR: fail_div_by_zero.carbon:[[@LINE+4]]:14: error: division by zero [CompileTimeDivisionByZero] // CHECK:STDERR: let a: i32 = Div(1, 0); // CHECK:STDERR: ^~~~~~~~~ // CHECK:STDERR: let a: i32 = Div(1, 0); // CHECK:STDERR: fail_div_by_zero.carbon:[[@LINE+4]]:14: error: division by zero [CompileTimeDivisionByZero] // CHECK:STDERR: let b: i32 = Div(0, 0); // CHECK:STDERR: ^~~~~~~~~ // CHECK:STDERR: let b: i32 = Div(0, 0); // IntLiteral allows "overflow" by widening its representation, but not overflow to infinity. // CHECK:STDERR: fail_div_by_zero.carbon:[[@LINE+4]]:28: error: division by zero [CompileTimeDivisionByZero] // CHECK:STDERR: let c: Core.IntLiteral() = DivLit(1, 0); // CHECK:STDERR: ^~~~~~~~~~~~ // CHECK:STDERR: let c: Core.IntLiteral() = DivLit(1, 0); // CHECK:STDERR: fail_div_by_zero.carbon:[[@LINE+4]]:28: error: division by zero [CompileTimeDivisionByZero] // CHECK:STDERR: let d: Core.IntLiteral() = DivLit(0, 0); // CHECK:STDERR: ^~~~~~~~~~~~ // CHECK:STDERR: let d: Core.IntLiteral() = DivLit(0, 0); // CHECK:STDOUT: --- int_div.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [concrete] // CHECK:STDOUT: %i32: type = class_type @Int, @Int(%int_32) [concrete] // CHECK:STDOUT: %Div.type: type = fn_type @Div [concrete] // CHECK:STDOUT: %Div: %Div.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @RuntimeCallIsValid(%a.param: %i32, %b.param: %i32) -> out %return.param: %i32 { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %Div.ref: %Div.type = name_ref Div, file.%Div.decl [concrete = constants.%Div] // CHECK:STDOUT: %a.ref: %i32 = name_ref a, %a // CHECK:STDOUT: %b.ref: %i32 = name_ref b, %b // CHECK:STDOUT: %Div.call: init %i32 = call %Div.ref(%a.ref, %b.ref) // CHECK:STDOUT: return %Div.call // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @__global_init() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: // CHECK:STDOUT: } // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/builtins/int/sdiv_assign.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/int.carbon // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/builtins/int/sdiv_assign.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/builtins/int/sdiv_assign.carbon // --- call.carbon library "[[@TEST_NAME]]"; fn Builtin(ref a: i32, b: i32) = "int.sdiv_assign"; fn Call(ref a: i32, b: i32) { //@dump-sem-ir-begin Builtin(ref a, b); //@dump-sem-ir-end } // --- fail_unsized.carbon library "[[@TEST_NAME]]"; // CHECK:STDERR: fail_unsized.carbon:[[@LINE+4]]:1: error: invalid signature for builtin function "int.sdiv_assign" [InvalidBuiltinSignature] // CHECK:STDERR: fn Builtin(ref a: Core.IntLiteral(), b: Core.IntLiteral()) = "int.sdiv_assign"; // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: fn Builtin(ref a: Core.IntLiteral(), b: Core.IntLiteral()) = "int.sdiv_assign"; // --- fail_bad_decl.carbon library "[[@TEST_NAME]]"; // CHECK:STDERR: fail_bad_decl.carbon:[[@LINE+4]]:1: error: invalid signature for builtin function "int.sdiv_assign" [InvalidBuiltinSignature] // CHECK:STDERR: fn NotRef(a: i32, b: i32) = "int.sdiv_assign"; // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: fn NotRef(a: i32, b: i32) = "int.sdiv_assign"; // CHECK:STDERR: fail_bad_decl.carbon:[[@LINE+4]]:1: error: invalid signature for builtin function "int.sdiv_assign" [InvalidBuiltinSignature] // CHECK:STDERR: fn MixedTypes(ref a: i32, b: i64) = "int.sdiv_assign"; // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: fn MixedTypes(ref a: i32, b: i64) = "int.sdiv_assign"; // CHECK:STDOUT: --- call.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [concrete] // CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] // CHECK:STDOUT: %i32: type = class_type @Int, @Int(%int_32) [concrete] // CHECK:STDOUT: %Builtin.type: type = fn_type @Builtin [concrete] // CHECK:STDOUT: %Builtin: %Builtin.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @Call(%a.param: ref %i32, %b.param: %i32) { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %Builtin.ref: %Builtin.type = name_ref Builtin, file.%Builtin.decl [concrete = constants.%Builtin] // CHECK:STDOUT: %a.ref: ref %i32 = name_ref a, %a // CHECK:STDOUT: %.loc8: %i32 = ref_tag %a.ref // CHECK:STDOUT: %b.ref: %i32 = name_ref b, %b // CHECK:STDOUT: %Builtin.call: init %empty_tuple.type = call %Builtin.ref(%.loc8, %b.ref) // CHECK:STDOUT: // CHECK:STDOUT: } // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/builtins/int/smod.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/int.carbon // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/builtins/int/smod.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/builtins/int/smod.carbon // --- int_div.carbon library "[[@TEST_NAME]]"; fn Mod(a: i32, b: i32) -> i32 = "int.smod"; var arr: array(i32, Mod(5, 3)); let arr_p: array(i32, 2)* = &arr; fn RuntimeCallIsValid(a: i32, b: i32) -> i32 { //@dump-sem-ir-begin return Mod(a, b); //@dump-sem-ir-end } // --- fail_overflow.carbon library "[[@TEST_NAME]]"; fn Mod(a: i32, b: i32) -> i32 = "int.smod"; fn Sub(a: i32, b: i32) -> i32 = "int.ssub"; fn Negate(a: i32) -> i32 = "int.snegate"; // -0x7FFF_FFFF % -1 is OK. let a: i32 = Mod(Negate(0x7FFF_FFFF), Negate(1)); // -0x8000_0000 % 1 is OK. let b: i32 = Mod(Sub(Negate(0x7FFF_FFFF), 1), 1); // -0x8000_0000 / -1 overflows, so -0x8000_0000 % -1 is disallowed, even though // its result is representable. // CHECK:STDERR: fail_overflow.carbon:[[@LINE+4]]:14: error: integer overflow in calculation `-2147483648 % -1` [CompileTimeIntegerOverflow] // CHECK:STDERR: let c: i32 = Mod(Sub(Negate(0x7FFF_FFFF), 1), Negate(1)); // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: let c: i32 = Mod(Sub(Negate(0x7FFF_FFFF), 1), Negate(1)); // --- fail_div_by_zero.carbon library "[[@TEST_NAME]]"; fn Mod(a: i32, b: i32) -> i32 = "int.smod"; // Remainder of division by zero is not defined. // CHECK:STDERR: fail_div_by_zero.carbon:[[@LINE+4]]:14: error: division by zero [CompileTimeDivisionByZero] // CHECK:STDERR: let a: i32 = Mod(1, 0); // CHECK:STDERR: ^~~~~~~~~ // CHECK:STDERR: let a: i32 = Mod(1, 0); // CHECK:STDERR: fail_div_by_zero.carbon:[[@LINE+4]]:14: error: division by zero [CompileTimeDivisionByZero] // CHECK:STDERR: let b: i32 = Mod(0, 0); // CHECK:STDERR: ^~~~~~~~~ // CHECK:STDERR: let b: i32 = Mod(0, 0); // CHECK:STDOUT: --- int_div.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [concrete] // CHECK:STDOUT: %i32: type = class_type @Int, @Int(%int_32) [concrete] // CHECK:STDOUT: %Mod.type: type = fn_type @Mod [concrete] // CHECK:STDOUT: %Mod: %Mod.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @RuntimeCallIsValid(%a.param: %i32, %b.param: %i32) -> out %return.param: %i32 { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %Mod.ref: %Mod.type = name_ref Mod, file.%Mod.decl [concrete = constants.%Mod] // CHECK:STDOUT: %a.ref: %i32 = name_ref a, %a // CHECK:STDOUT: %b.ref: %i32 = name_ref b, %b // CHECK:STDOUT: %Mod.call: init %i32 = call %Mod.ref(%a.ref, %b.ref) // CHECK:STDOUT: return %Mod.call // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @__global_init() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: // CHECK:STDOUT: } // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/builtins/int/smod_assign.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/int.carbon // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/builtins/int/smod_assign.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/builtins/int/smod_assign.carbon // --- call.carbon library "[[@TEST_NAME]]"; fn Builtin(ref a: i32, b: i32) = "int.smod_assign"; fn Call(ref a: i32, b: i32) { //@dump-sem-ir-begin Builtin(ref a, b); //@dump-sem-ir-end } // --- fail_unsized.carbon library "[[@TEST_NAME]]"; // CHECK:STDERR: fail_unsized.carbon:[[@LINE+4]]:1: error: invalid signature for builtin function "int.smod_assign" [InvalidBuiltinSignature] // CHECK:STDERR: fn Builtin(ref a: Core.IntLiteral(), b: Core.IntLiteral()) = "int.smod_assign"; // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: fn Builtin(ref a: Core.IntLiteral(), b: Core.IntLiteral()) = "int.smod_assign"; // --- fail_bad_decl.carbon library "[[@TEST_NAME]]"; // CHECK:STDERR: fail_bad_decl.carbon:[[@LINE+4]]:1: error: invalid signature for builtin function "int.smod_assign" [InvalidBuiltinSignature] // CHECK:STDERR: fn NotRef(a: i32, b: i32) = "int.smod_assign"; // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: fn NotRef(a: i32, b: i32) = "int.smod_assign"; // CHECK:STDERR: fail_bad_decl.carbon:[[@LINE+4]]:1: error: invalid signature for builtin function "int.smod_assign" [InvalidBuiltinSignature] // CHECK:STDERR: fn MixedTypes(ref a: i32, b: i64) = "int.smod_assign"; // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: fn MixedTypes(ref a: i32, b: i64) = "int.smod_assign"; // CHECK:STDOUT: --- call.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [concrete] // CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] // CHECK:STDOUT: %i32: type = class_type @Int, @Int(%int_32) [concrete] // CHECK:STDOUT: %Builtin.type: type = fn_type @Builtin [concrete] // CHECK:STDOUT: %Builtin: %Builtin.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @Call(%a.param: ref %i32, %b.param: %i32) { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %Builtin.ref: %Builtin.type = name_ref Builtin, file.%Builtin.decl [concrete = constants.%Builtin] // CHECK:STDOUT: %a.ref: ref %i32 = name_ref a, %a // CHECK:STDOUT: %.loc8: %i32 = ref_tag %a.ref // CHECK:STDOUT: %b.ref: %i32 = name_ref b, %b // CHECK:STDOUT: %Builtin.call: init %empty_tuple.type = call %Builtin.ref(%.loc8, %b.ref) // CHECK:STDOUT: // CHECK:STDOUT: } // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/builtins/int/smul.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/int.carbon // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/builtins/int/smul.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/builtins/int/smul.carbon // --- i32.carbon library "[[@TEST_NAME]]"; fn Mul(a: i32, b: i32) -> i32 = "int.smul"; class Expect(N:! i32) {} fn Test(N:! i32) -> Expect(N) { return {}; } fn F() { Test(Mul(0, 0)) as Expect(0); Test(Mul(0, 3)) as Expect(0); Test(Mul(1, 2)) as Expect(2); Test(Mul(3, 2)) as Expect(6); Test(Mul(0x7FFF_FFFF, 1)) as Expect(0x7FFF_FFFF); Test(Mul(0x7FFF_FFFF, -1)) as Expect(-0x7FFF_FFFF); } fn RuntimeCallIsValid(a: i32, b: i32) -> i32 { //@dump-sem-ir-begin return Mul(a, b); //@dump-sem-ir-end } // --- literal.carbon library "[[@TEST_NAME]]"; fn Mul(a: Core.IntLiteral(), b: Core.IntLiteral()) -> Core.IntLiteral() = "int.smul"; class Expect(N:! Core.IntLiteral()) {} fn Test(N:! Core.IntLiteral()) -> Expect(N) { return {}; } fn F() { Test(Mul(0, 0)) as Expect(0); Test(Mul(0, 3)) as Expect(0); Test(Mul(1, 2)) as Expect(2); Test(Mul(3, 2)) as Expect(6); Test(Mul(0x7FFF_FFFF, 1)) as Expect(0x7FFF_FFFF); Test(Mul(0x7FFF_FFFF, -1)) as Expect(-0x7FFF_FFFF); // Test some cases that might -- but shouldn't -- overflow. Test(Mul(0x8000_0000_0000_0000, 1)) as Expect(0x8000_0000_0000_0000); Test(Mul(0x8000_0000_0000_0000, -1)) as Expect(-0x8000_0000_0000_0000); Test(Mul(-0x8000_0000_0000_0000, 1)) as Expect(-0x8000_0000_0000_0000); Test(Mul(-0x8000_0000_0000_0000, -1)) as Expect(0x8000_0000_0000_0000); Test(Mul(-0x8000_0000_0000_0000, -1)) as Expect(0x8000_0000_0000_0000); Test(Mul(-0x8000_0000_0000_0000, -0x8000_0000_0000_0000)) as Expect(0x4000_0000_0000_0000_0000_0000_0000_0000); } // --- fail_overflow.carbon library "[[@TEST_NAME]]"; fn Mul(a: i32, b: i32) -> i32 = "int.smul"; let a: i32 = Mul(0x7FFF, 0x10000); // CHECK:STDERR: fail_overflow.carbon:[[@LINE+4]]:14: error: integer overflow in calculation `32768 * 65536` [CompileTimeIntegerOverflow] // CHECK:STDERR: let b: i32 = Mul(0x8000, 0x10000); // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: let b: i32 = Mul(0x8000, 0x10000); // CHECK:STDOUT: --- i32.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [concrete] // CHECK:STDOUT: %i32: type = class_type @Int, @Int(%int_32) [concrete] // CHECK:STDOUT: %Mul.type: type = fn_type @Mul [concrete] // CHECK:STDOUT: %Mul: %Mul.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @RuntimeCallIsValid(%a.param: %i32, %b.param: %i32) -> out %return.param: %i32 { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %Mul.ref: %Mul.type = name_ref Mul, file.%Mul.decl [concrete = constants.%Mul] // CHECK:STDOUT: %a.ref: %i32 = name_ref a, %a // CHECK:STDOUT: %b.ref: %i32 = name_ref b, %b // CHECK:STDOUT: %Mul.call: init %i32 = call %Mul.ref(%a.ref, %b.ref) // CHECK:STDOUT: return %Mul.call // CHECK:STDOUT: } // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/builtins/int/smul_assign.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/int.carbon // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/builtins/int/smul_assign.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/builtins/int/smul_assign.carbon // --- call.carbon library "[[@TEST_NAME]]"; fn Builtin(ref a: i32, b: i32) = "int.smul_assign"; fn Call(ref a: i32, b: i32) { //@dump-sem-ir-begin Builtin(ref a, b); //@dump-sem-ir-end } // --- fail_unsized.carbon library "[[@TEST_NAME]]"; // CHECK:STDERR: fail_unsized.carbon:[[@LINE+4]]:1: error: invalid signature for builtin function "int.smul_assign" [InvalidBuiltinSignature] // CHECK:STDERR: fn Builtin(ref a: Core.IntLiteral(), b: Core.IntLiteral()) = "int.smul_assign"; // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: fn Builtin(ref a: Core.IntLiteral(), b: Core.IntLiteral()) = "int.smul_assign"; // --- fail_bad_decl.carbon library "[[@TEST_NAME]]"; // CHECK:STDERR: fail_bad_decl.carbon:[[@LINE+4]]:1: error: invalid signature for builtin function "int.smul_assign" [InvalidBuiltinSignature] // CHECK:STDERR: fn NotRef(a: i32, b: i32) = "int.smul_assign"; // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: fn NotRef(a: i32, b: i32) = "int.smul_assign"; // CHECK:STDERR: fail_bad_decl.carbon:[[@LINE+4]]:1: error: invalid signature for builtin function "int.smul_assign" [InvalidBuiltinSignature] // CHECK:STDERR: fn MixedTypes(ref a: i32, b: i64) = "int.smul_assign"; // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: fn MixedTypes(ref a: i32, b: i64) = "int.smul_assign"; // CHECK:STDOUT: --- call.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [concrete] // CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] // CHECK:STDOUT: %i32: type = class_type @Int, @Int(%int_32) [concrete] // CHECK:STDOUT: %Builtin.type: type = fn_type @Builtin [concrete] // CHECK:STDOUT: %Builtin: %Builtin.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @Call(%a.param: ref %i32, %b.param: %i32) { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %Builtin.ref: %Builtin.type = name_ref Builtin, file.%Builtin.decl [concrete = constants.%Builtin] // CHECK:STDOUT: %a.ref: ref %i32 = name_ref a, %a // CHECK:STDOUT: %.loc8: %i32 = ref_tag %a.ref // CHECK:STDOUT: %b.ref: %i32 = name_ref b, %b // CHECK:STDOUT: %Builtin.call: init %empty_tuple.type = call %Builtin.ref(%.loc8, %b.ref) // CHECK:STDOUT: // CHECK:STDOUT: } // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/builtins/int/snegate.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/primitives.carbon // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/builtins/int/snegate.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/builtins/int/snegate.carbon // --- int_negate.carbon library "[[@TEST_NAME]]"; fn Negate(a: i32) -> i32 = "int.snegate"; var arr: array(i32, Negate(Negate(123))); let arr_p: array(i32, 123)* = &arr; let n: i32 = Negate(1); fn RuntimeCallIsValid(a: i32, unused b: i32) -> i32 { //@dump-sem-ir-begin return Negate(a); //@dump-sem-ir-end } // --- literal.carbon library "[[@TEST_NAME]]"; fn Negate(a: Core.IntLiteral()) -> Core.IntLiteral() = "int.snegate"; fn Sub(a: Core.IntLiteral(), b: Core.IntLiteral()) -> Core.IntLiteral() = "int.ssub"; class Expect(N:! Core.IntLiteral()) {} fn Test(N:! Core.IntLiteral()) -> Expect(N) { return {}; } fn F() { Test(Negate(0)) as Expect(0); Test(Negate(1)) as Expect(Sub(0, 1)); Test(Negate(Sub(0, 0x8000_0000_0000_0000))) as Expect(0x8000_0000_0000_0000); } // --- fail_too_few.carbon library "[[@TEST_NAME]]"; // CHECK:STDERR: fail_too_few.carbon:[[@LINE+4]]:1: error: invalid signature for builtin function "int.snegate" [InvalidBuiltinSignature] // CHECK:STDERR: fn TooFew() -> i32 = "int.snegate"; // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: fn TooFew() -> i32 = "int.snegate"; // CHECK:STDERR: fail_too_few.carbon:[[@LINE+4]]:25: error: array bound is not a constant [InvalidArrayExpr] // CHECK:STDERR: var too_few: array(i32, TooFew()); // CHECK:STDERR: ^~~~~~~~ // CHECK:STDERR: var too_few: array(i32, TooFew()); fn RuntimeCallIsValidTooFew() -> i32 { return TooFew(); } // --- fail_too_many.carbon library "[[@TEST_NAME]]"; // CHECK:STDERR: fail_too_many.carbon:[[@LINE+4]]:1: error: invalid signature for builtin function "int.snegate" [InvalidBuiltinSignature] // CHECK:STDERR: fn TooMany(a: i32, b: i32) -> i32 = "int.snegate"; // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: fn TooMany(a: i32, b: i32) -> i32 = "int.snegate"; // CHECK:STDERR: fail_too_many.carbon:[[@LINE+4]]:26: error: array bound is not a constant [InvalidArrayExpr] // CHECK:STDERR: var too_many: array(i32, TooMany(1, 2)); // CHECK:STDERR: ^~~~~~~~~~~~~ // CHECK:STDERR: var too_many: array(i32, TooMany(1, 2)); fn RuntimeCallIsValidTooMany(a: i32, b: i32) -> i32 { return TooMany(a, b); } // --- fail_bad_return_type.carbon library "[[@TEST_NAME]]"; // CHECK:STDERR: fail_bad_return_type.carbon:[[@LINE+4]]:1: error: invalid signature for builtin function "int.snegate" [InvalidBuiltinSignature] // CHECK:STDERR: fn BadReturnType(a: i32) -> bool = "int.snegate"; // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: fn BadReturnType(a: i32) -> bool = "int.snegate"; // CHECK:STDERR: fail_bad_return_type.carbon:[[@LINE+4]]:33: error: array bound is not a constant [InvalidArrayExpr] // CHECK:STDERR: var bad_return_type: array(i32, BadReturnType(1)); // CHECK:STDERR: ^~~~~~~~~~~~~~~~ // CHECK:STDERR: var bad_return_type: array(i32, BadReturnType(1)); fn RuntimeCallIsValidBadReturnType(a: i32) -> bool { return BadReturnType(a); } // --- fail_bad_call.carbon library "[[@TEST_NAME]]"; fn Negate(a: i32) -> i32 = "int.snegate"; // CHECK:STDERR: fail_bad_call.carbon:[[@LINE+7]]:26: error: 2 arguments passed to function expecting 1 argument [CallArgCountMismatch] // CHECK:STDERR: var bad_call: array(i32, Negate(1, 2)); // CHECK:STDERR: ^~~~~~~~~~~~ // CHECK:STDERR: fail_bad_call.carbon:[[@LINE-5]]:1: note: calling function declared here [InCallToEntity] // CHECK:STDERR: fn Negate(a: i32) -> i32 = "int.snegate"; // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: var bad_call: array(i32, Negate(1, 2)); // --- fail_overflow.carbon library "[[@TEST_NAME]]"; fn Negate(a: i32) -> i32 = "int.snegate"; fn Sub(a: i32, b: i32) -> i32 = "int.ssub"; // -(-INT_MAX) is INT_MAX. let a: i32 = Negate(Negate(0x7FFF_FFFF)); // -INT_MIN is too large for i32. // CHECK:STDERR: fail_overflow.carbon:[[@LINE+4]]:14: error: integer overflow in negation of -2147483648 [CompileTimeIntegerNegateOverflow] // CHECK:STDERR: let b: i32 = Negate(-0x8000_0000); // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: let b: i32 = Negate(-0x8000_0000); // CHECK:STDOUT: --- int_negate.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [concrete] // CHECK:STDOUT: %i32: type = class_type @Int, @Int(%int_32) [concrete] // CHECK:STDOUT: %Negate.type: type = fn_type @Negate [concrete] // CHECK:STDOUT: %Negate: %Negate.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @RuntimeCallIsValid(%a.param: %i32, %b.param: %i32) -> out %return.param: %i32 { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %Negate.ref: %Negate.type = name_ref Negate, file.%Negate.decl [concrete = constants.%Negate] // CHECK:STDOUT: %a.ref: %i32 = name_ref a, %a // CHECK:STDOUT: %Negate.call: init %i32 = call %Negate.ref(%a.ref) // CHECK:STDOUT: return %Negate.call // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @__global_init() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: // CHECK:STDOUT: } // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/builtins/int/ssub.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/int.carbon // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/builtins/int/ssub.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/builtins/int/ssub.carbon // --- int_sub.carbon library "[[@TEST_NAME]]"; fn Sub(a: i32, b: i32) -> i32 = "int.ssub"; var arr: array(i32, Sub(3, 2)); let arr_p: array(i32, 1)* = &arr; fn RuntimeCallIsValid(a: i32, b: i32) -> i32 { //@dump-sem-ir-begin return Sub(a, b); //@dump-sem-ir-end } // --- fail_overflow.carbon library "[[@TEST_NAME]]"; fn Sub(a: i32, b: i32) -> i32 = "int.ssub"; let a: i32 = Sub(0, 0x7FFFFFFF); let b: i32 = Sub(Sub(0, 0x7FFFFFFF), 1); // CHECK:STDERR: fail_overflow.carbon:[[@LINE+4]]:14: error: integer overflow in calculation `-2147483647 - 2` [CompileTimeIntegerOverflow] // CHECK:STDERR: let c: i32 = Sub(Sub(0, 0x7FFFFFFF), 2); // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: let c: i32 = Sub(Sub(0, 0x7FFFFFFF), 2); // CHECK:STDOUT: --- int_sub.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [concrete] // CHECK:STDOUT: %i32: type = class_type @Int, @Int(%int_32) [concrete] // CHECK:STDOUT: %Sub.type: type = fn_type @Sub [concrete] // CHECK:STDOUT: %Sub: %Sub.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @RuntimeCallIsValid(%a.param: %i32, %b.param: %i32) -> out %return.param: %i32 { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %Sub.ref: %Sub.type = name_ref Sub, file.%Sub.decl [concrete = constants.%Sub] // CHECK:STDOUT: %a.ref: %i32 = name_ref a, %a // CHECK:STDOUT: %b.ref: %i32 = name_ref b, %b // CHECK:STDOUT: %Sub.call: init %i32 = call %Sub.ref(%a.ref, %b.ref) // CHECK:STDOUT: return %Sub.call // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @__global_init() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: // CHECK:STDOUT: } // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/builtins/int/ssub_assign.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/int.carbon // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/builtins/int/ssub_assign.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/builtins/int/ssub_assign.carbon // --- call.carbon library "[[@TEST_NAME]]"; fn Builtin(ref a: i32, b: i32) = "int.ssub_assign"; fn Call(ref a: i32, b: i32) { //@dump-sem-ir-begin Builtin(ref a, b); //@dump-sem-ir-end } // --- fail_unsized.carbon library "[[@TEST_NAME]]"; // CHECK:STDERR: fail_unsized.carbon:[[@LINE+4]]:1: error: invalid signature for builtin function "int.ssub_assign" [InvalidBuiltinSignature] // CHECK:STDERR: fn Builtin(ref a: Core.IntLiteral(), b: Core.IntLiteral()) = "int.ssub_assign"; // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: fn Builtin(ref a: Core.IntLiteral(), b: Core.IntLiteral()) = "int.ssub_assign"; // --- fail_bad_decl.carbon library "[[@TEST_NAME]]"; // CHECK:STDERR: fail_bad_decl.carbon:[[@LINE+4]]:1: error: invalid signature for builtin function "int.ssub_assign" [InvalidBuiltinSignature] // CHECK:STDERR: fn NotRef(a: i32, b: i32) = "int.ssub_assign"; // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: fn NotRef(a: i32, b: i32) = "int.ssub_assign"; // CHECK:STDERR: fail_bad_decl.carbon:[[@LINE+4]]:1: error: invalid signature for builtin function "int.ssub_assign" [InvalidBuiltinSignature] // CHECK:STDERR: fn MixedTypes(ref a: i32, b: i64) = "int.ssub_assign"; // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: fn MixedTypes(ref a: i32, b: i64) = "int.ssub_assign"; // CHECK:STDOUT: --- call.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [concrete] // CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] // CHECK:STDOUT: %i32: type = class_type @Int, @Int(%int_32) [concrete] // CHECK:STDOUT: %Builtin.type: type = fn_type @Builtin [concrete] // CHECK:STDOUT: %Builtin: %Builtin.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @Call(%a.param: ref %i32, %b.param: %i32) { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %Builtin.ref: %Builtin.type = name_ref Builtin, file.%Builtin.decl [concrete = constants.%Builtin] // CHECK:STDOUT: %a.ref: ref %i32 = name_ref a, %a // CHECK:STDOUT: %.loc8: %i32 = ref_tag %a.ref // CHECK:STDOUT: %b.ref: %i32 = name_ref b, %b // CHECK:STDOUT: %Builtin.call: init %empty_tuple.type = call %Builtin.ref(%.loc8, %b.ref) // CHECK:STDOUT: // CHECK:STDOUT: } // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/builtins/int/uadd.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/primitives.carbon // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/builtins/int/uadd.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/builtins/int/uadd.carbon // --- int_add.carbon library "[[@TEST_NAME]]"; fn Add(a: i32, b: i32) -> i32 = "int.uadd"; var arr: array(i32, Add(1, 2)); let arr_p: array(i32, 3)* = &arr; fn RuntimeCallIsValid(a: i32, b: i32) -> i32 { //@dump-sem-ir-begin return Add(a, b); //@dump-sem-ir-end } // --- fail_too_few.carbon library "[[@TEST_NAME]]"; // CHECK:STDERR: fail_too_few.carbon:[[@LINE+4]]:1: error: invalid signature for builtin function "int.uadd" [InvalidBuiltinSignature] // CHECK:STDERR: fn TooFew(a: i32) -> i32 = "int.uadd"; // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: fn TooFew(a: i32) -> i32 = "int.uadd"; // CHECK:STDERR: fail_too_few.carbon:[[@LINE+4]]:25: error: array bound is not a constant [InvalidArrayExpr] // CHECK:STDERR: var too_few: array(i32, TooFew(1)); // CHECK:STDERR: ^~~~~~~~~ // CHECK:STDERR: var too_few: array(i32, TooFew(1)); fn RuntimeCallIsValidTooFew(a: i32) -> i32 { return TooFew(a); } // --- fail_too_many.carbon library "[[@TEST_NAME]]"; // CHECK:STDERR: fail_too_many.carbon:[[@LINE+4]]:1: error: invalid signature for builtin function "int.uadd" [InvalidBuiltinSignature] // CHECK:STDERR: fn TooMany(a: i32, b: i32, c: i32) -> i32 = "int.uadd"; // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: fn TooMany(a: i32, b: i32, c: i32) -> i32 = "int.uadd"; // CHECK:STDERR: fail_too_many.carbon:[[@LINE+4]]:26: error: array bound is not a constant [InvalidArrayExpr] // CHECK:STDERR: var too_many: array(i32, TooMany(1, 2, 3)); // CHECK:STDERR: ^~~~~~~~~~~~~~~~ // CHECK:STDERR: var too_many: array(i32, TooMany(1, 2, 3)); fn RuntimeCallIsValidTooMany(a: i32, b: i32, c: i32) -> i32 { return TooMany(a, b, c); } // --- fail_bad_return_type.carbon library "[[@TEST_NAME]]"; // CHECK:STDERR: fail_bad_return_type.carbon:[[@LINE+4]]:1: error: invalid signature for builtin function "int.uadd" [InvalidBuiltinSignature] // CHECK:STDERR: fn BadReturnType(a: i32, b: i32) -> bool = "int.uadd"; // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: fn BadReturnType(a: i32, b: i32) -> bool = "int.uadd"; // CHECK:STDERR: fail_bad_return_type.carbon:[[@LINE+4]]:33: error: array bound is not a constant [InvalidArrayExpr] // CHECK:STDERR: var bad_return_type: array(i32, BadReturnType(1, 2)); // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: var bad_return_type: array(i32, BadReturnType(1, 2)); fn RuntimeCallIsValidBadReturnType(a: i32, b: i32) -> bool { return BadReturnType(a, b); } // --- fail_bad_call.carbon library "[[@TEST_NAME]]"; fn Add(a: i32, b: i32) -> i32 = "int.uadd"; // CHECK:STDERR: fail_bad_call.carbon:[[@LINE+7]]:26: error: 3 arguments passed to function expecting 2 arguments [CallArgCountMismatch] // CHECK:STDERR: var bad_call: array(i32, Add(1, 2, 3)); // CHECK:STDERR: ^~~~~~~~~~~~ // CHECK:STDERR: fail_bad_call.carbon:[[@LINE-5]]:1: note: calling function declared here [InCallToEntity] // CHECK:STDERR: fn Add(a: i32, b: i32) -> i32 = "int.uadd"; // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: var bad_call: array(i32, Add(1, 2, 3)); // --- overflow.carbon library "[[@TEST_NAME]]"; fn Add(a: i32, b: i32) -> i32 = "int.uadd"; // Overflow is OK. let a: i32 = Add(0x7FFFFFFF, 0); let b: i32 = Add(0x7FFFFFFF, 1); // CHECK:STDOUT: --- int_add.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [concrete] // CHECK:STDOUT: %i32: type = class_type @Int, @Int(%int_32) [concrete] // CHECK:STDOUT: %Add.type: type = fn_type @Add [concrete] // CHECK:STDOUT: %Add: %Add.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @RuntimeCallIsValid(%a.param: %i32, %b.param: %i32) -> out %return.param: %i32 { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %Add.ref: %Add.type = name_ref Add, file.%Add.decl [concrete = constants.%Add] // CHECK:STDOUT: %a.ref: %i32 = name_ref a, %a // CHECK:STDOUT: %b.ref: %i32 = name_ref b, %b // CHECK:STDOUT: %Add.call: init %i32 = call %Add.ref(%a.ref, %b.ref) // CHECK:STDOUT: return %Add.call // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @__global_init() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: // CHECK:STDOUT: } // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/builtins/int/uadd_assign.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/int.carbon // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/builtins/int/uadd_assign.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/builtins/int/uadd_assign.carbon // --- call.carbon library "[[@TEST_NAME]]"; fn Builtin(ref a: i32, b: i32) = "int.uadd_assign"; fn Call(ref a: i32, b: i32) { //@dump-sem-ir-begin Builtin(ref a, b); //@dump-sem-ir-end } // --- fail_unsized.carbon library "[[@TEST_NAME]]"; // CHECK:STDERR: fail_unsized.carbon:[[@LINE+4]]:1: error: invalid signature for builtin function "int.uadd_assign" [InvalidBuiltinSignature] // CHECK:STDERR: fn Builtin(ref a: Core.IntLiteral(), b: Core.IntLiteral()) = "int.uadd_assign"; // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: fn Builtin(ref a: Core.IntLiteral(), b: Core.IntLiteral()) = "int.uadd_assign"; // --- fail_bad_decl.carbon library "[[@TEST_NAME]]"; // CHECK:STDERR: fail_bad_decl.carbon:[[@LINE+4]]:1: error: invalid signature for builtin function "int.uadd_assign" [InvalidBuiltinSignature] // CHECK:STDERR: fn NotRef(a: i32, b: i32) = "int.uadd_assign"; // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: fn NotRef(a: i32, b: i32) = "int.uadd_assign"; // CHECK:STDERR: fail_bad_decl.carbon:[[@LINE+4]]:1: error: invalid signature for builtin function "int.uadd_assign" [InvalidBuiltinSignature] // CHECK:STDERR: fn MixedTypes(ref a: i32, b: i64) = "int.uadd_assign"; // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: fn MixedTypes(ref a: i32, b: i64) = "int.uadd_assign"; // CHECK:STDOUT: --- call.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [concrete] // CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] // CHECK:STDOUT: %i32: type = class_type @Int, @Int(%int_32) [concrete] // CHECK:STDOUT: %Builtin.type: type = fn_type @Builtin [concrete] // CHECK:STDOUT: %Builtin: %Builtin.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @Call(%a.param: ref %i32, %b.param: %i32) { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %Builtin.ref: %Builtin.type = name_ref Builtin, file.%Builtin.decl [concrete = constants.%Builtin] // CHECK:STDOUT: %a.ref: ref %i32 = name_ref a, %a // CHECK:STDOUT: %.loc8: %i32 = ref_tag %a.ref // CHECK:STDOUT: %b.ref: %i32 = name_ref b, %b // CHECK:STDOUT: %Builtin.call: init %empty_tuple.type = call %Builtin.ref(%.loc8, %b.ref) // CHECK:STDOUT: // CHECK:STDOUT: } // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/builtins/int/udiv.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/int.carbon // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/builtins/int/udiv.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/builtins/int/udiv.carbon // --- int_div.carbon library "[[@TEST_NAME]]"; fn Div(a: i32, b: i32) -> i32 = "int.udiv"; var arr: array(i32, Div(3, 2)); let arr_p: array(i32, 1)* = &arr; fn RuntimeCallIsValid(a: i32, b: i32) -> i32 { //@dump-sem-ir-begin return Div(a, b); //@dump-sem-ir-end } // --- overflow.carbon library "[[@TEST_NAME]]"; fn Div(a: i32, b: i32) -> i32 = "int.udiv"; fn Sub(a: i32, b: i32) -> i32 = "int.usub"; fn Negate(a: i32) -> i32 = "int.unegate"; // -0x7FFF_FFFF / -1 is OK. let a: i32 = Div(Negate(0x7FFF_FFFF), Negate(1)); // -0x8000_0000 / 1 is OK. let b: i32 = Div(Sub(Negate(0x7FFF_FFFF), 1), 1); // -0x8000_0000 / -1 is OK. let c: i32 = Div(Sub(Negate(0x7FFF_FFFF), 1), Negate(1)); // --- fail_div_by_zero.carbon library "[[@TEST_NAME]]"; fn Div(a: i32, b: i32) -> i32 = "int.udiv"; // CHECK:STDERR: fail_div_by_zero.carbon:[[@LINE+4]]:14: error: division by zero [CompileTimeDivisionByZero] // CHECK:STDERR: let a: i32 = Div(1, 0); // CHECK:STDERR: ^~~~~~~~~ // CHECK:STDERR: let a: i32 = Div(1, 0); // CHECK:STDERR: fail_div_by_zero.carbon:[[@LINE+4]]:14: error: division by zero [CompileTimeDivisionByZero] // CHECK:STDERR: let b: i32 = Div(0, 0); // CHECK:STDERR: ^~~~~~~~~ // CHECK:STDERR: let b: i32 = Div(0, 0); // CHECK:STDOUT: --- int_div.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [concrete] // CHECK:STDOUT: %i32: type = class_type @Int, @Int(%int_32) [concrete] // CHECK:STDOUT: %Div.type: type = fn_type @Div [concrete] // CHECK:STDOUT: %Div: %Div.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @RuntimeCallIsValid(%a.param: %i32, %b.param: %i32) -> out %return.param: %i32 { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %Div.ref: %Div.type = name_ref Div, file.%Div.decl [concrete = constants.%Div] // CHECK:STDOUT: %a.ref: %i32 = name_ref a, %a // CHECK:STDOUT: %b.ref: %i32 = name_ref b, %b // CHECK:STDOUT: %Div.call: init %i32 = call %Div.ref(%a.ref, %b.ref) // CHECK:STDOUT: return %Div.call // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @__global_init() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: // CHECK:STDOUT: } // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/builtins/int/udiv_assign.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/int.carbon // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/builtins/int/udiv_assign.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/builtins/int/udiv_assign.carbon // --- call.carbon library "[[@TEST_NAME]]"; fn Builtin(ref a: i32, b: i32) = "int.udiv_assign"; fn Call(ref a: i32, b: i32) { //@dump-sem-ir-begin Builtin(ref a, b); //@dump-sem-ir-end } // --- fail_unsized.carbon library "[[@TEST_NAME]]"; // CHECK:STDERR: fail_unsized.carbon:[[@LINE+4]]:1: error: invalid signature for builtin function "int.udiv_assign" [InvalidBuiltinSignature] // CHECK:STDERR: fn Builtin(ref a: Core.IntLiteral(), b: Core.IntLiteral()) = "int.udiv_assign"; // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: fn Builtin(ref a: Core.IntLiteral(), b: Core.IntLiteral()) = "int.udiv_assign"; // --- fail_bad_decl.carbon library "[[@TEST_NAME]]"; // CHECK:STDERR: fail_bad_decl.carbon:[[@LINE+4]]:1: error: invalid signature for builtin function "int.udiv_assign" [InvalidBuiltinSignature] // CHECK:STDERR: fn NotRef(a: i32, b: i32) = "int.udiv_assign"; // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: fn NotRef(a: i32, b: i32) = "int.udiv_assign"; // CHECK:STDERR: fail_bad_decl.carbon:[[@LINE+4]]:1: error: invalid signature for builtin function "int.udiv_assign" [InvalidBuiltinSignature] // CHECK:STDERR: fn MixedTypes(ref a: i32, b: i64) = "int.udiv_assign"; // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: fn MixedTypes(ref a: i32, b: i64) = "int.udiv_assign"; // CHECK:STDOUT: --- call.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [concrete] // CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] // CHECK:STDOUT: %i32: type = class_type @Int, @Int(%int_32) [concrete] // CHECK:STDOUT: %Builtin.type: type = fn_type @Builtin [concrete] // CHECK:STDOUT: %Builtin: %Builtin.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @Call(%a.param: ref %i32, %b.param: %i32) { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %Builtin.ref: %Builtin.type = name_ref Builtin, file.%Builtin.decl [concrete = constants.%Builtin] // CHECK:STDOUT: %a.ref: ref %i32 = name_ref a, %a // CHECK:STDOUT: %.loc8: %i32 = ref_tag %a.ref // CHECK:STDOUT: %b.ref: %i32 = name_ref b, %b // CHECK:STDOUT: %Builtin.call: init %empty_tuple.type = call %Builtin.ref(%.loc8, %b.ref) // CHECK:STDOUT: // CHECK:STDOUT: } // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/builtins/int/umod.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/int.carbon // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/builtins/int/umod.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/builtins/int/umod.carbon // --- int_div.carbon library "[[@TEST_NAME]]"; fn Mod(a: i32, b: i32) -> i32 = "int.umod"; var arr: array(i32, Mod(5, 3)); let arr_p: array(i32, 2)* = &arr; fn RuntimeCallIsValid(a: i32, b: i32) -> i32 { //@dump-sem-ir-begin return Mod(a, b); //@dump-sem-ir-end } // --- overflow.carbon library "[[@TEST_NAME]]"; fn Mod(a: i32, b: i32) -> i32 = "int.umod"; fn Sub(a: i32, b: i32) -> i32 = "int.usub"; fn Negate(a: i32) -> i32 = "int.unegate"; // -0x7FFF_FFFF % -1 is OK. let a: i32 = Mod(Negate(0x7FFF_FFFF), Negate(1)); // -0x8000_0000 % 1 is OK. let b: i32 = Mod(Sub(Negate(0x7FFF_FFFF), 1), 1); // -0x8000_0000 / -1 is OK. let c: i32 = Mod(Sub(Negate(0x7FFF_FFFF), 1), Negate(1)); // --- fail_div_by_zero.carbon library "[[@TEST_NAME]]"; fn Mod(a: i32, b: i32) -> i32 = "int.umod"; // Remainder of division by zero is not defined. // CHECK:STDERR: fail_div_by_zero.carbon:[[@LINE+4]]:14: error: division by zero [CompileTimeDivisionByZero] // CHECK:STDERR: let a: i32 = Mod(1, 0); // CHECK:STDERR: ^~~~~~~~~ // CHECK:STDERR: let a: i32 = Mod(1, 0); // CHECK:STDERR: fail_div_by_zero.carbon:[[@LINE+4]]:14: error: division by zero [CompileTimeDivisionByZero] // CHECK:STDERR: let b: i32 = Mod(0, 0); // CHECK:STDERR: ^~~~~~~~~ // CHECK:STDERR: let b: i32 = Mod(0, 0); // CHECK:STDOUT: --- int_div.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [concrete] // CHECK:STDOUT: %i32: type = class_type @Int, @Int(%int_32) [concrete] // CHECK:STDOUT: %Mod.type: type = fn_type @Mod [concrete] // CHECK:STDOUT: %Mod: %Mod.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @RuntimeCallIsValid(%a.param: %i32, %b.param: %i32) -> out %return.param: %i32 { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %Mod.ref: %Mod.type = name_ref Mod, file.%Mod.decl [concrete = constants.%Mod] // CHECK:STDOUT: %a.ref: %i32 = name_ref a, %a // CHECK:STDOUT: %b.ref: %i32 = name_ref b, %b // CHECK:STDOUT: %Mod.call: init %i32 = call %Mod.ref(%a.ref, %b.ref) // CHECK:STDOUT: return %Mod.call // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @__global_init() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: // CHECK:STDOUT: } // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/builtins/int/umod_assign.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/int.carbon // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/builtins/int/umod_assign.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/builtins/int/umod_assign.carbon // --- call.carbon library "[[@TEST_NAME]]"; fn Builtin(ref a: i32, b: i32) = "int.umod_assign"; fn Call(ref a: i32, b: i32) { //@dump-sem-ir-begin Builtin(ref a, b); //@dump-sem-ir-end } // --- fail_unsized.carbon library "[[@TEST_NAME]]"; // CHECK:STDERR: fail_unsized.carbon:[[@LINE+4]]:1: error: invalid signature for builtin function "int.umod_assign" [InvalidBuiltinSignature] // CHECK:STDERR: fn Builtin(ref a: Core.IntLiteral(), b: Core.IntLiteral()) = "int.umod_assign"; // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: fn Builtin(ref a: Core.IntLiteral(), b: Core.IntLiteral()) = "int.umod_assign"; // --- fail_bad_decl.carbon library "[[@TEST_NAME]]"; // CHECK:STDERR: fail_bad_decl.carbon:[[@LINE+4]]:1: error: invalid signature for builtin function "int.umod_assign" [InvalidBuiltinSignature] // CHECK:STDERR: fn NotRef(a: i32, b: i32) = "int.umod_assign"; // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: fn NotRef(a: i32, b: i32) = "int.umod_assign"; // CHECK:STDERR: fail_bad_decl.carbon:[[@LINE+4]]:1: error: invalid signature for builtin function "int.umod_assign" [InvalidBuiltinSignature] // CHECK:STDERR: fn MixedTypes(ref a: i32, b: i64) = "int.umod_assign"; // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: fn MixedTypes(ref a: i32, b: i64) = "int.umod_assign"; // CHECK:STDOUT: --- call.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [concrete] // CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] // CHECK:STDOUT: %i32: type = class_type @Int, @Int(%int_32) [concrete] // CHECK:STDOUT: %Builtin.type: type = fn_type @Builtin [concrete] // CHECK:STDOUT: %Builtin: %Builtin.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @Call(%a.param: ref %i32, %b.param: %i32) { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %Builtin.ref: %Builtin.type = name_ref Builtin, file.%Builtin.decl [concrete = constants.%Builtin] // CHECK:STDOUT: %a.ref: ref %i32 = name_ref a, %a // CHECK:STDOUT: %.loc8: %i32 = ref_tag %a.ref // CHECK:STDOUT: %b.ref: %i32 = name_ref b, %b // CHECK:STDOUT: %Builtin.call: init %empty_tuple.type = call %Builtin.ref(%.loc8, %b.ref) // CHECK:STDOUT: // CHECK:STDOUT: } // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/builtins/int/umul.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/int.carbon // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/builtins/int/umul.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/builtins/int/umul.carbon // --- int_mul.carbon library "[[@TEST_NAME]]"; fn Mul(a: i32, b: i32) -> i32 = "int.umul"; var arr: array(i32, Mul(3, 2)); let arr_p: array(i32, 6)* = &arr; fn RuntimeCallIsValid(a: i32, b: i32) -> i32 { //@dump-sem-ir-begin return Mul(a, b); //@dump-sem-ir-end } // --- overflow.carbon library "[[@TEST_NAME]]"; fn Mul(a: i32, b: i32) -> i32 = "int.umul"; let a: i32 = Mul(0x7FFF, 0x10000); let b: i32 = Mul(0x8000, 0x10000); // CHECK:STDOUT: --- int_mul.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [concrete] // CHECK:STDOUT: %i32: type = class_type @Int, @Int(%int_32) [concrete] // CHECK:STDOUT: %Mul.type: type = fn_type @Mul [concrete] // CHECK:STDOUT: %Mul: %Mul.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @RuntimeCallIsValid(%a.param: %i32, %b.param: %i32) -> out %return.param: %i32 { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %Mul.ref: %Mul.type = name_ref Mul, file.%Mul.decl [concrete = constants.%Mul] // CHECK:STDOUT: %a.ref: %i32 = name_ref a, %a // CHECK:STDOUT: %b.ref: %i32 = name_ref b, %b // CHECK:STDOUT: %Mul.call: init %i32 = call %Mul.ref(%a.ref, %b.ref) // CHECK:STDOUT: return %Mul.call // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @__global_init() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: // CHECK:STDOUT: } // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/builtins/int/umul_assign.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/int.carbon // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/builtins/int/umul_assign.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/builtins/int/umul_assign.carbon // --- call.carbon library "[[@TEST_NAME]]"; fn Builtin(ref a: i32, b: i32) = "int.umul_assign"; fn Call(ref a: i32, b: i32) { //@dump-sem-ir-begin Builtin(ref a, b); //@dump-sem-ir-end } // --- fail_unsized.carbon library "[[@TEST_NAME]]"; // CHECK:STDERR: fail_unsized.carbon:[[@LINE+4]]:1: error: invalid signature for builtin function "int.umul_assign" [InvalidBuiltinSignature] // CHECK:STDERR: fn Builtin(ref a: Core.IntLiteral(), b: Core.IntLiteral()) = "int.umul_assign"; // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: fn Builtin(ref a: Core.IntLiteral(), b: Core.IntLiteral()) = "int.umul_assign"; // --- fail_bad_decl.carbon library "[[@TEST_NAME]]"; // CHECK:STDERR: fail_bad_decl.carbon:[[@LINE+4]]:1: error: invalid signature for builtin function "int.umul_assign" [InvalidBuiltinSignature] // CHECK:STDERR: fn NotRef(a: i32, b: i32) = "int.umul_assign"; // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: fn NotRef(a: i32, b: i32) = "int.umul_assign"; // CHECK:STDERR: fail_bad_decl.carbon:[[@LINE+4]]:1: error: invalid signature for builtin function "int.umul_assign" [InvalidBuiltinSignature] // CHECK:STDERR: fn MixedTypes(ref a: i32, b: i64) = "int.umul_assign"; // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: fn MixedTypes(ref a: i32, b: i64) = "int.umul_assign"; // CHECK:STDOUT: --- call.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [concrete] // CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] // CHECK:STDOUT: %i32: type = class_type @Int, @Int(%int_32) [concrete] // CHECK:STDOUT: %Builtin.type: type = fn_type @Builtin [concrete] // CHECK:STDOUT: %Builtin: %Builtin.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @Call(%a.param: ref %i32, %b.param: %i32) { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %Builtin.ref: %Builtin.type = name_ref Builtin, file.%Builtin.decl [concrete = constants.%Builtin] // CHECK:STDOUT: %a.ref: ref %i32 = name_ref a, %a // CHECK:STDOUT: %.loc8: %i32 = ref_tag %a.ref // CHECK:STDOUT: %b.ref: %i32 = name_ref b, %b // CHECK:STDOUT: %Builtin.call: init %empty_tuple.type = call %Builtin.ref(%.loc8, %b.ref) // CHECK:STDOUT: // CHECK:STDOUT: } // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/builtins/int/unegate.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/primitives.carbon // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/builtins/int/unegate.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/builtins/int/unegate.carbon // --- int_negate.carbon library "[[@TEST_NAME]]"; fn Negate(a: u32) -> u32 = "int.unegate"; var arr: array(u32, Negate(Negate(123))); let arr_p: array(u32, 123)* = &arr; let n: u32 = Negate(1); fn RuntimeCallIsValid(a: u32, unused b: u32) -> u32 { //@dump-sem-ir-begin return Negate(a); //@dump-sem-ir-end } // --- fail_too_few.carbon library "[[@TEST_NAME]]"; // CHECK:STDERR: fail_too_few.carbon:[[@LINE+4]]:1: error: invalid signature for builtin function "int.unegate" [InvalidBuiltinSignature] // CHECK:STDERR: fn TooFew() -> u32 = "int.unegate"; // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: fn TooFew() -> u32 = "int.unegate"; // CHECK:STDERR: fail_too_few.carbon:[[@LINE+4]]:25: error: array bound is not a constant [InvalidArrayExpr] // CHECK:STDERR: var too_few: array(u32, TooFew()); // CHECK:STDERR: ^~~~~~~~ // CHECK:STDERR: var too_few: array(u32, TooFew()); fn RuntimeCallIsValidTooFew() -> u32 { return TooFew(); } // --- fail_too_many.carbon library "[[@TEST_NAME]]"; // CHECK:STDERR: fail_too_many.carbon:[[@LINE+4]]:1: error: invalid signature for builtin function "int.unegate" [InvalidBuiltinSignature] // CHECK:STDERR: fn TooMany(a: u32, b: u32) -> u32 = "int.unegate"; // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: fn TooMany(a: u32, b: u32) -> u32 = "int.unegate"; // CHECK:STDERR: fail_too_many.carbon:[[@LINE+4]]:26: error: array bound is not a constant [InvalidArrayExpr] // CHECK:STDERR: var too_many: array(u32, TooMany(1, 2)); // CHECK:STDERR: ^~~~~~~~~~~~~ // CHECK:STDERR: var too_many: array(u32, TooMany(1, 2)); fn RuntimeCallIsValidTooMany(a: u32, b: u32) -> u32 { return TooMany(a, b); } // --- fail_bad_return_type.carbon library "[[@TEST_NAME]]"; // CHECK:STDERR: fail_bad_return_type.carbon:[[@LINE+4]]:1: error: invalid signature for builtin function "int.unegate" [InvalidBuiltinSignature] // CHECK:STDERR: fn BadReturnType(a: u32) -> bool = "int.unegate"; // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: fn BadReturnType(a: u32) -> bool = "int.unegate"; // CHECK:STDERR: fail_bad_return_type.carbon:[[@LINE+4]]:33: error: array bound is not a constant [InvalidArrayExpr] // CHECK:STDERR: var bad_return_type: array(u32, BadReturnType(1)); // CHECK:STDERR: ^~~~~~~~~~~~~~~~ // CHECK:STDERR: var bad_return_type: array(u32, BadReturnType(1)); fn RuntimeCallIsValidBadReturnType(a: u32) -> bool { return BadReturnType(a); } // --- fail_bad_call.carbon library "[[@TEST_NAME]]"; fn Negate(a: u32) -> u32 = "int.unegate"; // CHECK:STDERR: fail_bad_call.carbon:[[@LINE+7]]:26: error: 2 arguments passed to function expecting 1 argument [CallArgCountMismatch] // CHECK:STDERR: var bad_call: array(u32, Negate(1, 2)); // CHECK:STDERR: ^~~~~~~~~~~~ // CHECK:STDERR: fail_bad_call.carbon:[[@LINE-5]]:1: note: calling function declared here [InCallToEntity] // CHECK:STDERR: fn Negate(a: u32) -> u32 = "int.unegate"; // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: var bad_call: array(u32, Negate(1, 2)); // --- overflow.carbon library "[[@TEST_NAME]]"; fn Negate(a: u32) -> u32 = "int.unegate"; class Expect(N:! u32) {} fn Test(N:! u32) -> Expect(N) { return {}; } fn F() { // -(-INT_MAX) is INT_MAX. Test(Negate(Negate(0x7FFF_FFFF))) as Expect(0x7FFF_FFFF); Test(-(Negate(0x7FFF_FFFF))) as Expect(0x7FFF_FFFF); // -(-(INT_MAX + 1)) is `INT_MAX + 1`. Test(Negate(Negate(0x8000_0000))) as Expect(0x8000_0000); Test(-(Negate(0x8000_0000))) as Expect(0x8000_0000); } // CHECK:STDOUT: --- int_negate.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [concrete] // CHECK:STDOUT: %u32: type = class_type @UInt, @UInt(%int_32) [concrete] // CHECK:STDOUT: %Negate.type: type = fn_type @Negate [concrete] // CHECK:STDOUT: %Negate: %Negate.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @RuntimeCallIsValid(%a.param: %u32, %b.param: %u32) -> out %return.param: %u32 { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %Negate.ref: %Negate.type = name_ref Negate, file.%Negate.decl [concrete = constants.%Negate] // CHECK:STDOUT: %a.ref: %u32 = name_ref a, %a // CHECK:STDOUT: %Negate.call: init %u32 = call %Negate.ref(%a.ref) // CHECK:STDOUT: return %Negate.call // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @__global_init() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: // CHECK:STDOUT: } // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/builtins/int/usub.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/int.carbon // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/builtins/int/usub.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/builtins/int/usub.carbon // --- int_sub.carbon library "[[@TEST_NAME]]"; fn Sub(a: i32, b: i32) -> i32 = "int.usub"; var arr: array(i32, Sub(3, 2)); let arr_p: array(i32, 1)* = &arr; fn RuntimeCallIsValid(a: i32, b: i32) -> i32 { //@dump-sem-ir-begin return Sub(a, b); //@dump-sem-ir-end } // --- overflow.carbon library "[[@TEST_NAME]]"; fn Sub(a: i32, b: i32) -> i32 = "int.usub"; let a: i32 = Sub(0, 0x7FFFFFFF); let b: i32 = Sub(Sub(0, 0x7FFFFFFF), 1); let c: i32 = Sub(Sub(0, 0x7FFFFFFF), 2); // CHECK:STDOUT: --- int_sub.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [concrete] // CHECK:STDOUT: %i32: type = class_type @Int, @Int(%int_32) [concrete] // CHECK:STDOUT: %Sub.type: type = fn_type @Sub [concrete] // CHECK:STDOUT: %Sub: %Sub.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @RuntimeCallIsValid(%a.param: %i32, %b.param: %i32) -> out %return.param: %i32 { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %Sub.ref: %Sub.type = name_ref Sub, file.%Sub.decl [concrete = constants.%Sub] // CHECK:STDOUT: %a.ref: %i32 = name_ref a, %a // CHECK:STDOUT: %b.ref: %i32 = name_ref b, %b // CHECK:STDOUT: %Sub.call: init %i32 = call %Sub.ref(%a.ref, %b.ref) // CHECK:STDOUT: return %Sub.call // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @__global_init() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: // CHECK:STDOUT: } // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/builtins/int/usub_assign.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/int.carbon // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/builtins/int/usub_assign.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/builtins/int/usub_assign.carbon // --- call.carbon library "[[@TEST_NAME]]"; fn Builtin(ref a: i32, b: i32) = "int.usub_assign"; fn Call(ref a: i32, b: i32) { //@dump-sem-ir-begin Builtin(ref a, b); //@dump-sem-ir-end } // --- fail_unsized.carbon library "[[@TEST_NAME]]"; // CHECK:STDERR: fail_unsized.carbon:[[@LINE+4]]:1: error: invalid signature for builtin function "int.usub_assign" [InvalidBuiltinSignature] // CHECK:STDERR: fn Builtin(ref a: Core.IntLiteral(), b: Core.IntLiteral()) = "int.usub_assign"; // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: fn Builtin(ref a: Core.IntLiteral(), b: Core.IntLiteral()) = "int.usub_assign"; // --- fail_bad_decl.carbon library "[[@TEST_NAME]]"; // CHECK:STDERR: fail_bad_decl.carbon:[[@LINE+4]]:1: error: invalid signature for builtin function "int.usub_assign" [InvalidBuiltinSignature] // CHECK:STDERR: fn NotRef(a: i32, b: i32) = "int.usub_assign"; // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: fn NotRef(a: i32, b: i32) = "int.usub_assign"; // CHECK:STDERR: fail_bad_decl.carbon:[[@LINE+4]]:1: error: invalid signature for builtin function "int.usub_assign" [InvalidBuiltinSignature] // CHECK:STDERR: fn MixedTypes(ref a: i32, b: i64) = "int.usub_assign"; // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: fn MixedTypes(ref a: i32, b: i64) = "int.usub_assign"; // CHECK:STDOUT: --- call.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [concrete] // CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] // CHECK:STDOUT: %i32: type = class_type @Int, @Int(%int_32) [concrete] // CHECK:STDOUT: %Builtin.type: type = fn_type @Builtin [concrete] // CHECK:STDOUT: %Builtin: %Builtin.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @Call(%a.param: ref %i32, %b.param: %i32) { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %Builtin.ref: %Builtin.type = name_ref Builtin, file.%Builtin.decl [concrete = constants.%Builtin] // CHECK:STDOUT: %a.ref: ref %i32 = name_ref a, %a // CHECK:STDOUT: %.loc8: %i32 = ref_tag %a.ref // CHECK:STDOUT: %b.ref: %i32 = name_ref b, %b // CHECK:STDOUT: %Builtin.call: init %empty_tuple.type = call %Builtin.ref(%.loc8, %b.ref) // CHECK:STDOUT: // CHECK:STDOUT: } // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/builtins/int/xor.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/int.carbon // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/builtins/int/xor.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/builtins/int/xor.carbon // --- int_xor.carbon fn Xor(a: i32, b: i32) -> i32 = "int.xor"; var arr: array(i32, Xor(12, 10)); let arr_p: array(i32, 6)* = &arr; fn RuntimeCallIsValid(a: i32, b: i32) -> i32 { //@dump-sem-ir-begin return Xor(a, b); //@dump-sem-ir-end } // CHECK:STDOUT: --- int_xor.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [concrete] // CHECK:STDOUT: %i32: type = class_type @Int, @Int(%int_32) [concrete] // CHECK:STDOUT: %Xor.type: type = fn_type @Xor [concrete] // CHECK:STDOUT: %Xor: %Xor.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @RuntimeCallIsValid(%a.param: %i32, %b.param: %i32) -> out %return.param: %i32 { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %Xor.ref: %Xor.type = name_ref Xor, file.%Xor.decl [concrete = constants.%Xor] // CHECK:STDOUT: %a.ref: %i32 = name_ref a, %a // CHECK:STDOUT: %b.ref: %i32 = name_ref b, %b // CHECK:STDOUT: %Xor.call: init %i32 = call %Xor.ref(%a.ref, %b.ref) // CHECK:STDOUT: return %Xor.call // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @__global_init() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: // CHECK:STDOUT: } // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/builtins/int/xor_assign.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/int.carbon // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/builtins/int/xor_assign.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/builtins/int/xor_assign.carbon // --- call.carbon library "[[@TEST_NAME]]"; fn Builtin(ref a: i32, b: i32) = "int.xor_assign"; fn Call(ref a: i32, b: i32) { //@dump-sem-ir-begin Builtin(ref a, b); //@dump-sem-ir-end } // --- fail_unsized.carbon library "[[@TEST_NAME]]"; // CHECK:STDERR: fail_unsized.carbon:[[@LINE+4]]:1: error: invalid signature for builtin function "int.xor_assign" [InvalidBuiltinSignature] // CHECK:STDERR: fn Builtin(ref a: Core.IntLiteral(), b: Core.IntLiteral()) = "int.xor_assign"; // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: fn Builtin(ref a: Core.IntLiteral(), b: Core.IntLiteral()) = "int.xor_assign"; // --- fail_bad_decl.carbon library "[[@TEST_NAME]]"; // CHECK:STDERR: fail_bad_decl.carbon:[[@LINE+4]]:1: error: invalid signature for builtin function "int.xor_assign" [InvalidBuiltinSignature] // CHECK:STDERR: fn NotRef(a: i32, b: i32) = "int.xor_assign"; // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: fn NotRef(a: i32, b: i32) = "int.xor_assign"; // CHECK:STDERR: fail_bad_decl.carbon:[[@LINE+4]]:1: error: invalid signature for builtin function "int.xor_assign" [InvalidBuiltinSignature] // CHECK:STDERR: fn MixedTypes(ref a: i32, b: i64) = "int.xor_assign"; // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: fn MixedTypes(ref a: i32, b: i64) = "int.xor_assign"; // CHECK:STDOUT: --- call.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [concrete] // CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] // CHECK:STDOUT: %i32: type = class_type @Int, @Int(%int_32) [concrete] // CHECK:STDOUT: %Builtin.type: type = fn_type @Builtin [concrete] // CHECK:STDOUT: %Builtin: %Builtin.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @Call(%a.param: ref %i32, %b.param: %i32) { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %Builtin.ref: %Builtin.type = name_ref Builtin, file.%Builtin.decl [concrete = constants.%Builtin] // CHECK:STDOUT: %a.ref: ref %i32 = name_ref a, %a // CHECK:STDOUT: %.loc8: %i32 = ref_tag %a.ref // CHECK:STDOUT: %b.ref: %i32 = name_ref b, %b // CHECK:STDOUT: %Builtin.call: init %empty_tuple.type = call %Builtin.ref(%.loc8, %b.ref) // CHECK:STDOUT: // CHECK:STDOUT: } // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/builtins/int_literal/make_type.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/convert.carbon // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/builtins/int_literal/make_type.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/builtins/int_literal/make_type.carbon // --- types.carbon library "[[@TEST_NAME]]"; fn IntLiteral() -> type = "int_literal.make_type"; // --- use_types.carbon library "[[@TEST_NAME]]"; import library "types"; var i: IntLiteral(); ================================================ FILE: toolchain/check/testdata/builtins/maybe_unformed/make_type.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/none.carbon // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/builtins/maybe_unformed/make_type.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/builtins/maybe_unformed/make_type.carbon // --- types.carbon library "[[@TEST_NAME]]"; fn Make(t: type) -> type = "maybe_unformed.make_type"; // --- use_types.carbon library "[[@TEST_NAME]]"; import library "types"; //@dump-sem-ir-begin fn F(unused b: Make(Make({}))) {} //@dump-sem-ir-end // --- runtime_call.carbon library "[[@TEST_NAME]]"; import library "types"; //@dump-sem-ir-begin let t: type = {}; let u: type = Make(t); //@dump-sem-ir-end // --- fail_bad_decl.carbon library "[[@TEST_NAME]]"; // CHECK:STDERR: fail_bad_decl.carbon:[[@LINE+4]]:1: error: invalid signature for builtin function "maybe_unformed.make_type" [InvalidBuiltinSignature] // CHECK:STDERR: fn NoParam() -> type = "maybe_unformed.make_type"; // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: fn NoParam() -> type = "maybe_unformed.make_type"; // CHECK:STDERR: fail_bad_decl.carbon:[[@LINE+4]]:1: error: invalid signature for builtin function "maybe_unformed.make_type" [InvalidBuiltinSignature] // CHECK:STDERR: fn ColonBangParam(T:! type) -> type = "maybe_unformed.make_type"; // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: fn ColonBangParam(T:! type) -> type = "maybe_unformed.make_type"; // CHECK:STDOUT: --- use_types.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %Make.type: type = fn_type @Make [concrete] // CHECK:STDOUT: %Make: %Make.type = struct_value () [concrete] // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %empty_struct: %empty_struct_type = struct_value () [concrete] // CHECK:STDOUT: %.a8d: type = maybe_unformed_type %empty_struct_type [concrete] // CHECK:STDOUT: %.2ba: type = maybe_unformed_type %.a8d [concrete] // CHECK:STDOUT: %pattern_type.ce6: type = pattern_type %.2ba [concrete] // CHECK:STDOUT: %F.type: type = fn_type @F [concrete] // CHECK:STDOUT: %F: %F.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Main.Make: %Make.type = import_ref Main//types, Make, loaded [concrete = constants.%Make] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: %F.decl: %F.type = fn_decl @F [concrete = constants.%F] { // CHECK:STDOUT: %b.patt: %pattern_type.ce6 = value_binding_pattern b [concrete] // CHECK:STDOUT: %b.param_patt: %pattern_type.ce6 = value_param_pattern %b.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %b.param: %.2ba = value_param call_param0 // CHECK:STDOUT: %.loc7_29.1: type = splice_block %.loc7_29.3 [concrete = constants.%.2ba] { // CHECK:STDOUT: %Make.ref.loc7_16: %Make.type = name_ref Make, imports.%Main.Make [concrete = constants.%Make] // CHECK:STDOUT: %Make.ref.loc7_21: %Make.type = name_ref Make, imports.%Main.Make [concrete = constants.%Make] // CHECK:STDOUT: %.loc7_27.1: %empty_struct_type = struct_literal () [concrete = constants.%empty_struct] // CHECK:STDOUT: %.loc7_27.2: type = converted %.loc7_27.1, constants.%empty_struct_type [concrete = constants.%empty_struct_type] // CHECK:STDOUT: %Make.call.loc7_28: init type = call %Make.ref.loc7_21(%.loc7_27.2) [concrete = constants.%.a8d] // CHECK:STDOUT: %.loc7_28.1: type = value_of_initializer %Make.call.loc7_28 [concrete = constants.%.a8d] // CHECK:STDOUT: %.loc7_28.2: type = converted %Make.call.loc7_28, %.loc7_28.1 [concrete = constants.%.a8d] // CHECK:STDOUT: %Make.call.loc7_29: init type = call %Make.ref.loc7_16(%.loc7_28.2) [concrete = constants.%.2ba] // CHECK:STDOUT: %.loc7_29.2: type = value_of_initializer %Make.call.loc7_29 [concrete = constants.%.2ba] // CHECK:STDOUT: %.loc7_29.3: type = converted %Make.call.loc7_29, %.loc7_29.2 [concrete = constants.%.2ba] // CHECK:STDOUT: } // CHECK:STDOUT: %b: %.2ba = value_binding b, %b.param // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @F(%b.param: %.2ba) { // CHECK:STDOUT: !entry: // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- runtime_call.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %pattern_type: type = pattern_type type [concrete] // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %empty_struct: %empty_struct_type = struct_value () [concrete] // CHECK:STDOUT: %Make.type: type = fn_type @Make [concrete] // CHECK:STDOUT: %Make: %Make.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Main.Make: %Make.type = import_ref Main//types, Make, loaded [concrete = constants.%Make] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %t.patt: %pattern_type = value_binding_pattern t [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %.loc7_8: type = type_literal type [concrete = type] // CHECK:STDOUT: %.loc7_16: type = converted @__global_init.%.loc7, constants.%empty_struct_type [concrete = constants.%empty_struct_type] // CHECK:STDOUT: %t: type = value_binding t, %.loc7_16 // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %u.patt: %pattern_type = value_binding_pattern u [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %.loc8_8: type = type_literal type [concrete = type] // CHECK:STDOUT: %.loc8_21.1: type = value_of_initializer @__global_init.%Make.call // CHECK:STDOUT: %.loc8_21.2: type = converted @__global_init.%Make.call, %.loc8_21.1 // CHECK:STDOUT: %u: type = value_binding u, %.loc8_21.2 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @__global_init() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %.loc7: %empty_struct_type = struct_literal () [concrete = constants.%empty_struct] // CHECK:STDOUT: %Make.ref: %Make.type = name_ref Make, imports.%Main.Make [concrete = constants.%Make] // CHECK:STDOUT: %t.ref: type = name_ref t, file.%t // CHECK:STDOUT: %Make.call: init type = call %Make.ref(%t.ref) // CHECK:STDOUT: // CHECK:STDOUT: } // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/builtins/no_op.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/none.carbon // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/builtins/no_op.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/builtins/no_op.carbon // --- no_op.carbon library "[[@TEST_NAME]]"; fn NoOp() = "no_op"; fn F() { //@dump-sem-ir-begin NoOp(); //@dump-sem-ir-end } // --- explicit_return.carbon library "[[@TEST_NAME]]"; fn NoOp() -> () = "no_op"; fn F() { //@dump-sem-ir-begin NoOp(); //@dump-sem-ir-end } // --- ignore_args.carbon library "[[@TEST_NAME]]"; fn NoOp(x: ()) -> () = "no_op"; fn F() { //@dump-sem-ir-begin NoOp(()); //@dump-sem-ir-end } // --- assign.carbon library "[[@TEST_NAME]]"; fn NoOp() = "no_op"; //@dump-sem-ir-begin var x: () = NoOp(); //@dump-sem-ir-end // --- fail_signature.carbon library "[[@TEST_NAME]]"; // CHECK:STDERR: fail_signature.carbon:[[@LINE+4]]:1: error: invalid signature for builtin function "no_op" [InvalidBuiltinSignature] // CHECK:STDERR: fn NoOp() -> {} = "no_op"; // CHECK:STDERR: ^~~~~~~~~~~~~~~~~ // CHECK:STDERR: fn NoOp() -> {} = "no_op"; // CHECK:STDOUT: --- no_op.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %NoOp.type: type = fn_type @NoOp [concrete] // CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] // CHECK:STDOUT: %NoOp: %NoOp.type = struct_value () [concrete] // CHECK:STDOUT: %empty_tuple: %empty_tuple.type = tuple_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @F() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %NoOp.ref: %NoOp.type = name_ref NoOp, file.%NoOp.decl [concrete = constants.%NoOp] // CHECK:STDOUT: %NoOp.call: init %empty_tuple.type = call %NoOp.ref() [concrete = constants.%empty_tuple] // CHECK:STDOUT: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- explicit_return.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] // CHECK:STDOUT: %empty_tuple: %empty_tuple.type = tuple_value () [concrete] // CHECK:STDOUT: %NoOp.type: type = fn_type @NoOp [concrete] // CHECK:STDOUT: %NoOp: %NoOp.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @F() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %NoOp.ref: %NoOp.type = name_ref NoOp, file.%NoOp.decl [concrete = constants.%NoOp] // CHECK:STDOUT: %NoOp.call: init %empty_tuple.type = call %NoOp.ref() [concrete = constants.%empty_tuple] // CHECK:STDOUT: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- ignore_args.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] // CHECK:STDOUT: %empty_tuple: %empty_tuple.type = tuple_value () [concrete] // CHECK:STDOUT: %NoOp.type: type = fn_type @NoOp [concrete] // CHECK:STDOUT: %NoOp: %NoOp.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @F() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %NoOp.ref: %NoOp.type = name_ref NoOp, file.%NoOp.decl [concrete = constants.%NoOp] // CHECK:STDOUT: %.loc8_9.1: %empty_tuple.type = tuple_literal () [concrete = constants.%empty_tuple] // CHECK:STDOUT: %empty_tuple: %empty_tuple.type = tuple_value () [concrete = constants.%empty_tuple] // CHECK:STDOUT: %.loc8_9.2: %empty_tuple.type = converted %.loc8_9.1, %empty_tuple [concrete = constants.%empty_tuple] // CHECK:STDOUT: %NoOp.call: init %empty_tuple.type = call %NoOp.ref(%.loc8_9.2) [concrete = constants.%empty_tuple] // CHECK:STDOUT: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- assign.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %NoOp.type: type = fn_type @NoOp [concrete] // CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] // CHECK:STDOUT: %NoOp: %NoOp.type = struct_value () [concrete] // CHECK:STDOUT: %empty_tuple: %empty_tuple.type = tuple_value () [concrete] // CHECK:STDOUT: %pattern_type: type = pattern_type %empty_tuple.type [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %x.patt: %pattern_type = ref_binding_pattern x [concrete] // CHECK:STDOUT: %x.var_patt: %pattern_type = var_pattern %x.patt [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %x.var: ref %empty_tuple.type = var %x.var_patt [concrete] // CHECK:STDOUT: %.loc7_9.1: type = splice_block %.loc7_9.3 [concrete = constants.%empty_tuple.type] { // CHECK:STDOUT: %.loc7_9.2: %empty_tuple.type = tuple_literal () [concrete = constants.%empty_tuple] // CHECK:STDOUT: %.loc7_9.3: type = converted %.loc7_9.2, constants.%empty_tuple.type [concrete = constants.%empty_tuple.type] // CHECK:STDOUT: } // CHECK:STDOUT: %x: ref %empty_tuple.type = ref_binding x, %x.var [concrete = %x.var] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @__global_init() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %NoOp.ref: %NoOp.type = name_ref NoOp, file.%NoOp.decl [concrete = constants.%NoOp] // CHECK:STDOUT: %NoOp.call: init %empty_tuple.type = call %NoOp.ref() [concrete = constants.%empty_tuple] // CHECK:STDOUT: assign file.%x.var, %NoOp.call // CHECK:STDOUT: // CHECK:STDOUT: } // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/builtins/pointer/is_null.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/bool.carbon // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/builtins/pointer/is_null.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/builtins/pointer/is_null.carbon // --- call_exact.carbon library "[[@TEST_NAME]]"; class C {} fn MakeUnformed(t: type) -> type = "maybe_unformed.make_type"; fn IsNullEmptyStruct(p: MakeUnformed({}*)) -> bool = "pointer.is_null"; fn IsNullC(p: MakeUnformed(C*)) -> bool = "pointer.is_null"; //@dump-sem-ir-begin fn TestEmptyStruct(s: MakeUnformed({}*)) -> bool { return IsNullEmptyStruct(s); } fn TestC(c: MakeUnformed(C*)) -> bool { return IsNullC(c); } //@dump-sem-ir-end // --- call_generic.carbon library "[[@TEST_NAME]]"; fn MakeUnformed(t: type) -> type = "maybe_unformed.make_type"; fn IsNull[T:! type](p: MakeUnformed(T*)) -> bool = "pointer.is_null"; class C {} //@dump-sem-ir-begin fn TestEmptyStruct(s: MakeUnformed({}*)) -> bool { return IsNull(s); } fn TestC(c: MakeUnformed(C*)) -> bool { return IsNull(c); } //@dump-sem-ir-end // --- fail_bad_decl.carbon library "[[@TEST_NAME]]"; fn MakeUnformed(t: type) -> type = "maybe_unformed.make_type"; // CHECK:STDERR: fail_bad_decl.carbon:[[@LINE+4]]:1: error: invalid signature for builtin function "pointer.is_null" [InvalidBuiltinSignature] // CHECK:STDERR: fn NoParam() -> bool = "pointer.is_null"; // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: fn NoParam() -> bool = "pointer.is_null"; // CHECK:STDERR: fail_bad_decl.carbon:[[@LINE+4]]:1: error: invalid signature for builtin function "pointer.is_null" [InvalidBuiltinSignature] // CHECK:STDERR: fn NoRetType(p: MakeUnformed({}*)) = "pointer.is_null"; // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: fn NoRetType(p: MakeUnformed({}*)) = "pointer.is_null"; // CHECK:STDERR: fail_bad_decl.carbon:[[@LINE+4]]:1: error: invalid signature for builtin function "pointer.is_null" [InvalidBuiltinSignature] // CHECK:STDERR: fn WrongRetType(p: MakeUnformed({}*)) -> {} = "pointer.is_null"; // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: fn WrongRetType(p: MakeUnformed({}*)) -> {} = "pointer.is_null"; // CHECK:STDERR: fail_bad_decl.carbon:[[@LINE+4]]:1: error: invalid signature for builtin function "pointer.is_null" [InvalidBuiltinSignature] // CHECK:STDERR: fn NoUnformed(p: {}*) -> bool = "pointer.is_null"; // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: fn NoUnformed(p: {}*) -> bool = "pointer.is_null"; // CHECK:STDERR: fail_bad_decl.carbon:[[@LINE+4]]:1: error: invalid signature for builtin function "pointer.is_null" [InvalidBuiltinSignature] // CHECK:STDERR: fn NotPointer(p: MakeUnformed({})) -> bool = "pointer.is_null"; // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: fn NotPointer(p: MakeUnformed({})) -> bool = "pointer.is_null"; // CHECK:STDOUT: --- call_exact.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %C: type = class_type @C [concrete] // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %MakeUnformed.type: type = fn_type @MakeUnformed [concrete] // CHECK:STDOUT: %MakeUnformed: %MakeUnformed.type = struct_value () [concrete] // CHECK:STDOUT: %empty_struct: %empty_struct_type = struct_value () [concrete] // CHECK:STDOUT: %ptr.c28: type = ptr_type %empty_struct_type [concrete] // CHECK:STDOUT: %.b2d: type = maybe_unformed_type %ptr.c28 [concrete] // CHECK:STDOUT: %pattern_type.b42: type = pattern_type %.b2d [concrete] // CHECK:STDOUT: %.f34: Core.Form = init_form bool [concrete] // CHECK:STDOUT: %pattern_type.831: type = pattern_type bool [concrete] // CHECK:STDOUT: %IsNullEmptyStruct.type: type = fn_type @IsNullEmptyStruct [concrete] // CHECK:STDOUT: %IsNullEmptyStruct: %IsNullEmptyStruct.type = struct_value () [concrete] // CHECK:STDOUT: %ptr.31e: type = ptr_type %C [concrete] // CHECK:STDOUT: %.edf: type = maybe_unformed_type %ptr.31e [concrete] // CHECK:STDOUT: %pattern_type.b78: type = pattern_type %.edf [concrete] // CHECK:STDOUT: %IsNullC.type: type = fn_type @IsNullC [concrete] // CHECK:STDOUT: %IsNullC: %IsNullC.type = struct_value () [concrete] // CHECK:STDOUT: %TestEmptyStruct.type: type = fn_type @TestEmptyStruct [concrete] // CHECK:STDOUT: %TestEmptyStruct: %TestEmptyStruct.type = struct_value () [concrete] // CHECK:STDOUT: %TestC.type: type = fn_type @TestC [concrete] // CHECK:STDOUT: %TestC: %TestC.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: %TestEmptyStruct.decl: %TestEmptyStruct.type = fn_decl @TestEmptyStruct [concrete = constants.%TestEmptyStruct] { // CHECK:STDOUT: %s.patt: %pattern_type.b42 = value_binding_pattern s [concrete] // CHECK:STDOUT: %s.param_patt: %pattern_type.b42 = value_param_pattern %s.patt [concrete] // CHECK:STDOUT: %return.patt: %pattern_type.831 = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: %pattern_type.831 = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %.loc11_45.1: type = type_literal bool [concrete = bool] // CHECK:STDOUT: %.loc11_45.2: Core.Form = init_form %.loc11_45.1 [concrete = constants.%.f34] // CHECK:STDOUT: %s.param: %.b2d = value_param call_param0 // CHECK:STDOUT: %.loc11_39.1: type = splice_block %.loc11_39.3 [concrete = constants.%.b2d] { // CHECK:STDOUT: %MakeUnformed.ref: %MakeUnformed.type = name_ref MakeUnformed, file.%MakeUnformed.decl [concrete = constants.%MakeUnformed] // CHECK:STDOUT: %.loc11_37: %empty_struct_type = struct_literal () [concrete = constants.%empty_struct] // CHECK:STDOUT: %.loc11_38: type = converted %.loc11_37, constants.%empty_struct_type [concrete = constants.%empty_struct_type] // CHECK:STDOUT: %ptr: type = ptr_type %.loc11_38 [concrete = constants.%ptr.c28] // CHECK:STDOUT: %MakeUnformed.call: init type = call %MakeUnformed.ref(%ptr) [concrete = constants.%.b2d] // CHECK:STDOUT: %.loc11_39.2: type = value_of_initializer %MakeUnformed.call [concrete = constants.%.b2d] // CHECK:STDOUT: %.loc11_39.3: type = converted %MakeUnformed.call, %.loc11_39.2 [concrete = constants.%.b2d] // CHECK:STDOUT: } // CHECK:STDOUT: %s: %.b2d = value_binding s, %s.param // CHECK:STDOUT: %return.param: ref bool = out_param call_param1 // CHECK:STDOUT: %return: ref bool = return_slot %return.param // CHECK:STDOUT: } // CHECK:STDOUT: %TestC.decl: %TestC.type = fn_decl @TestC [concrete = constants.%TestC] { // CHECK:STDOUT: %c.patt: %pattern_type.b78 = value_binding_pattern c [concrete] // CHECK:STDOUT: %c.param_patt: %pattern_type.b78 = value_param_pattern %c.patt [concrete] // CHECK:STDOUT: %return.patt: %pattern_type.831 = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: %pattern_type.831 = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %.loc15_34.1: type = type_literal bool [concrete = bool] // CHECK:STDOUT: %.loc15_34.2: Core.Form = init_form %.loc15_34.1 [concrete = constants.%.f34] // CHECK:STDOUT: %c.param: %.edf = value_param call_param0 // CHECK:STDOUT: %.loc15_28.1: type = splice_block %.loc15_28.3 [concrete = constants.%.edf] { // CHECK:STDOUT: %MakeUnformed.ref: %MakeUnformed.type = name_ref MakeUnformed, file.%MakeUnformed.decl [concrete = constants.%MakeUnformed] // CHECK:STDOUT: %C.ref: type = name_ref C, file.%C.decl [concrete = constants.%C] // CHECK:STDOUT: %ptr: type = ptr_type %C.ref [concrete = constants.%ptr.31e] // CHECK:STDOUT: %MakeUnformed.call: init type = call %MakeUnformed.ref(%ptr) [concrete = constants.%.edf] // CHECK:STDOUT: %.loc15_28.2: type = value_of_initializer %MakeUnformed.call [concrete = constants.%.edf] // CHECK:STDOUT: %.loc15_28.3: type = converted %MakeUnformed.call, %.loc15_28.2 [concrete = constants.%.edf] // CHECK:STDOUT: } // CHECK:STDOUT: %c: %.edf = value_binding c, %c.param // CHECK:STDOUT: %return.param: ref bool = out_param call_param1 // CHECK:STDOUT: %return: ref bool = return_slot %return.param // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @TestEmptyStruct(%s.param: %.b2d) -> out %return.param: bool { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %IsNullEmptyStruct.ref: %IsNullEmptyStruct.type = name_ref IsNullEmptyStruct, file.%IsNullEmptyStruct.decl [concrete = constants.%IsNullEmptyStruct] // CHECK:STDOUT: %s.ref: %.b2d = name_ref s, %s // CHECK:STDOUT: %IsNullEmptyStruct.call: init bool = call %IsNullEmptyStruct.ref(%s.ref) // CHECK:STDOUT: return %IsNullEmptyStruct.call // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @TestC(%c.param: %.edf) -> out %return.param: bool { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %IsNullC.ref: %IsNullC.type = name_ref IsNullC, file.%IsNullC.decl [concrete = constants.%IsNullC] // CHECK:STDOUT: %c.ref: %.edf = name_ref c, %c // CHECK:STDOUT: %IsNullC.call: init bool = call %IsNullC.ref(%c.ref) // CHECK:STDOUT: return %IsNullC.call // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- call_generic.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %MakeUnformed.type: type = fn_type @MakeUnformed [concrete] // CHECK:STDOUT: %MakeUnformed: %MakeUnformed.type = struct_value () [concrete] // CHECK:STDOUT: %.f34: Core.Form = init_form bool [concrete] // CHECK:STDOUT: %pattern_type.831: type = pattern_type bool [concrete] // CHECK:STDOUT: %IsNull.type: type = fn_type @IsNull [concrete] // CHECK:STDOUT: %IsNull: %IsNull.type = struct_value () [concrete] // CHECK:STDOUT: %C: type = class_type @C [concrete] // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %empty_struct: %empty_struct_type = struct_value () [concrete] // CHECK:STDOUT: %ptr.c28: type = ptr_type %empty_struct_type [concrete] // CHECK:STDOUT: %.b2d: type = maybe_unformed_type %ptr.c28 [concrete] // CHECK:STDOUT: %pattern_type.b42: type = pattern_type %.b2d [concrete] // CHECK:STDOUT: %TestEmptyStruct.type: type = fn_type @TestEmptyStruct [concrete] // CHECK:STDOUT: %TestEmptyStruct: %TestEmptyStruct.type = struct_value () [concrete] // CHECK:STDOUT: %IsNull.specific_fn.34e: = specific_function %IsNull, @IsNull(%empty_struct_type) [concrete] // CHECK:STDOUT: %ptr.31e: type = ptr_type %C [concrete] // CHECK:STDOUT: %.edf: type = maybe_unformed_type %ptr.31e [concrete] // CHECK:STDOUT: %pattern_type.b78: type = pattern_type %.edf [concrete] // CHECK:STDOUT: %TestC.type: type = fn_type @TestC [concrete] // CHECK:STDOUT: %TestC: %TestC.type = struct_value () [concrete] // CHECK:STDOUT: %IsNull.specific_fn.b1f: = specific_function %IsNull, @IsNull(%C) [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: %TestEmptyStruct.decl: %TestEmptyStruct.type = fn_decl @TestEmptyStruct [concrete = constants.%TestEmptyStruct] { // CHECK:STDOUT: %s.patt: %pattern_type.b42 = value_binding_pattern s [concrete] // CHECK:STDOUT: %s.param_patt: %pattern_type.b42 = value_param_pattern %s.patt [concrete] // CHECK:STDOUT: %return.patt: %pattern_type.831 = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: %pattern_type.831 = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %.loc10_45.1: type = type_literal bool [concrete = bool] // CHECK:STDOUT: %.loc10_45.2: Core.Form = init_form %.loc10_45.1 [concrete = constants.%.f34] // CHECK:STDOUT: %s.param: %.b2d = value_param call_param0 // CHECK:STDOUT: %.loc10_39.1: type = splice_block %.loc10_39.3 [concrete = constants.%.b2d] { // CHECK:STDOUT: %MakeUnformed.ref: %MakeUnformed.type = name_ref MakeUnformed, file.%MakeUnformed.decl [concrete = constants.%MakeUnformed] // CHECK:STDOUT: %.loc10_37: %empty_struct_type = struct_literal () [concrete = constants.%empty_struct] // CHECK:STDOUT: %.loc10_38: type = converted %.loc10_37, constants.%empty_struct_type [concrete = constants.%empty_struct_type] // CHECK:STDOUT: %ptr: type = ptr_type %.loc10_38 [concrete = constants.%ptr.c28] // CHECK:STDOUT: %MakeUnformed.call: init type = call %MakeUnformed.ref(%ptr) [concrete = constants.%.b2d] // CHECK:STDOUT: %.loc10_39.2: type = value_of_initializer %MakeUnformed.call [concrete = constants.%.b2d] // CHECK:STDOUT: %.loc10_39.3: type = converted %MakeUnformed.call, %.loc10_39.2 [concrete = constants.%.b2d] // CHECK:STDOUT: } // CHECK:STDOUT: %s: %.b2d = value_binding s, %s.param // CHECK:STDOUT: %return.param: ref bool = out_param call_param1 // CHECK:STDOUT: %return: ref bool = return_slot %return.param // CHECK:STDOUT: } // CHECK:STDOUT: %TestC.decl: %TestC.type = fn_decl @TestC [concrete = constants.%TestC] { // CHECK:STDOUT: %c.patt: %pattern_type.b78 = value_binding_pattern c [concrete] // CHECK:STDOUT: %c.param_patt: %pattern_type.b78 = value_param_pattern %c.patt [concrete] // CHECK:STDOUT: %return.patt: %pattern_type.831 = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: %pattern_type.831 = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %.loc14_34.1: type = type_literal bool [concrete = bool] // CHECK:STDOUT: %.loc14_34.2: Core.Form = init_form %.loc14_34.1 [concrete = constants.%.f34] // CHECK:STDOUT: %c.param: %.edf = value_param call_param0 // CHECK:STDOUT: %.loc14_28.1: type = splice_block %.loc14_28.3 [concrete = constants.%.edf] { // CHECK:STDOUT: %MakeUnformed.ref: %MakeUnformed.type = name_ref MakeUnformed, file.%MakeUnformed.decl [concrete = constants.%MakeUnformed] // CHECK:STDOUT: %C.ref: type = name_ref C, file.%C.decl [concrete = constants.%C] // CHECK:STDOUT: %ptr: type = ptr_type %C.ref [concrete = constants.%ptr.31e] // CHECK:STDOUT: %MakeUnformed.call: init type = call %MakeUnformed.ref(%ptr) [concrete = constants.%.edf] // CHECK:STDOUT: %.loc14_28.2: type = value_of_initializer %MakeUnformed.call [concrete = constants.%.edf] // CHECK:STDOUT: %.loc14_28.3: type = converted %MakeUnformed.call, %.loc14_28.2 [concrete = constants.%.edf] // CHECK:STDOUT: } // CHECK:STDOUT: %c: %.edf = value_binding c, %c.param // CHECK:STDOUT: %return.param: ref bool = out_param call_param1 // CHECK:STDOUT: %return: ref bool = return_slot %return.param // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @TestEmptyStruct(%s.param: %.b2d) -> out %return.param: bool { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %IsNull.ref: %IsNull.type = name_ref IsNull, file.%IsNull.decl [concrete = constants.%IsNull] // CHECK:STDOUT: %s.ref: %.b2d = name_ref s, %s // CHECK:STDOUT: %IsNull.specific_fn: = specific_function %IsNull.ref, @IsNull(constants.%empty_struct_type) [concrete = constants.%IsNull.specific_fn.34e] // CHECK:STDOUT: %IsNull.call: init bool = call %IsNull.specific_fn(%s.ref) // CHECK:STDOUT: return %IsNull.call // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @TestC(%c.param: %.edf) -> out %return.param: bool { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %IsNull.ref: %IsNull.type = name_ref IsNull, file.%IsNull.decl [concrete = constants.%IsNull] // CHECK:STDOUT: %c.ref: %.edf = name_ref c, %c // CHECK:STDOUT: %IsNull.specific_fn: = specific_function %IsNull.ref, @IsNull(constants.%C) [concrete = constants.%IsNull.specific_fn.b1f] // CHECK:STDOUT: %IsNull.call: init bool = call %IsNull.specific_fn(%c.ref) // CHECK:STDOUT: return %IsNull.call // CHECK:STDOUT: } // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/builtins/pointer/make_null.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/none.carbon // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/builtins/pointer/make_null.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/builtins/pointer/make_null.carbon // --- call_exact.carbon library "[[@TEST_NAME]]"; class C {} fn MakeUnformed(t: type) -> type = "maybe_unformed.make_type"; fn MakeNullEmptyStruct() -> MakeUnformed({}*) = "pointer.make_null"; fn MakeNullC() -> MakeUnformed(C*) = "pointer.make_null"; //@dump-sem-ir-begin let s: MakeUnformed({}*) = MakeNullEmptyStruct(); let c: MakeUnformed(C*) = MakeNullC(); //@dump-sem-ir-end // --- call_generic.carbon library "[[@TEST_NAME]]"; fn MakeUnformed(t: type) -> type = "maybe_unformed.make_type"; fn MakeNull(T:! type) -> MakeUnformed(T*) = "pointer.make_null"; class C {} //@dump-sem-ir-begin let s: MakeUnformed({}*) = MakeNull({}); let c: MakeUnformed(C*) = MakeNull(C); //@dump-sem-ir-end // --- fail_bad_decl.carbon library "[[@TEST_NAME]]"; fn MakeUnformed(t: type) -> type = "maybe_unformed.make_type"; // CHECK:STDERR: fail_bad_decl.carbon:[[@LINE+4]]:1: error: invalid signature for builtin function "pointer.make_null" [InvalidBuiltinSignature] // CHECK:STDERR: fn NoRetType() = "pointer.make_null"; // CHECK:STDERR: ^~~~~~~~~~~~~~~~ // CHECK:STDERR: fn NoRetType() = "pointer.make_null"; // CHECK:STDERR: fail_bad_decl.carbon:[[@LINE+4]]:1: error: invalid signature for builtin function "pointer.make_null" [InvalidBuiltinSignature] // CHECK:STDERR: fn NoUnformed() -> {}* = "pointer.make_null"; // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: fn NoUnformed() -> {}* = "pointer.make_null"; // CHECK:STDERR: fail_bad_decl.carbon:[[@LINE+4]]:1: error: invalid signature for builtin function "pointer.make_null" [InvalidBuiltinSignature] // CHECK:STDERR: fn NotPointer() -> MakeUnformed({}) = "pointer.make_null"; // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: fn NotPointer() -> MakeUnformed({}) = "pointer.make_null"; // CHECK:STDOUT: --- call_exact.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %C: type = class_type @C [concrete] // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %MakeUnformed.type: type = fn_type @MakeUnformed [concrete] // CHECK:STDOUT: %MakeUnformed: %MakeUnformed.type = struct_value () [concrete] // CHECK:STDOUT: %empty_struct: %empty_struct_type = struct_value () [concrete] // CHECK:STDOUT: %ptr.c28: type = ptr_type %empty_struct_type [concrete] // CHECK:STDOUT: %.b2d: type = maybe_unformed_type %ptr.c28 [concrete] // CHECK:STDOUT: %pattern_type.b42: type = pattern_type %.b2d [concrete] // CHECK:STDOUT: %MakeNullEmptyStruct.type: type = fn_type @MakeNullEmptyStruct [concrete] // CHECK:STDOUT: %MakeNullEmptyStruct: %MakeNullEmptyStruct.type = struct_value () [concrete] // CHECK:STDOUT: %ptr.31e: type = ptr_type %C [concrete] // CHECK:STDOUT: %.edf: type = maybe_unformed_type %ptr.31e [concrete] // CHECK:STDOUT: %pattern_type.b78: type = pattern_type %.edf [concrete] // CHECK:STDOUT: %MakeNullC.type: type = fn_type @MakeNullC [concrete] // CHECK:STDOUT: %MakeNullC: %MakeNullC.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %s.patt: %pattern_type.b42 = value_binding_pattern s [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %.loc11_24.1: type = splice_block %.loc11_24.3 [concrete = constants.%.b2d] { // CHECK:STDOUT: %MakeUnformed.ref.loc11: %MakeUnformed.type = name_ref MakeUnformed, %MakeUnformed.decl [concrete = constants.%MakeUnformed] // CHECK:STDOUT: %.loc11_22: %empty_struct_type = struct_literal () [concrete = constants.%empty_struct] // CHECK:STDOUT: %.loc11_23: type = converted %.loc11_22, constants.%empty_struct_type [concrete = constants.%empty_struct_type] // CHECK:STDOUT: %ptr.loc11: type = ptr_type %.loc11_23 [concrete = constants.%ptr.c28] // CHECK:STDOUT: %MakeUnformed.call.loc11: init type = call %MakeUnformed.ref.loc11(%ptr.loc11) [concrete = constants.%.b2d] // CHECK:STDOUT: %.loc11_24.2: type = value_of_initializer %MakeUnformed.call.loc11 [concrete = constants.%.b2d] // CHECK:STDOUT: %.loc11_24.3: type = converted %MakeUnformed.call.loc11, %.loc11_24.2 [concrete = constants.%.b2d] // CHECK:STDOUT: } // CHECK:STDOUT: %.loc11_48.1: %.b2d = value_of_initializer @__global_init.%MakeNullEmptyStruct.call // CHECK:STDOUT: %.loc11_48.2: %.b2d = converted @__global_init.%MakeNullEmptyStruct.call, %.loc11_48.1 // CHECK:STDOUT: %s: %.b2d = value_binding s, %.loc11_48.2 // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %c.patt: %pattern_type.b78 = value_binding_pattern c [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %.loc12_23.1: type = splice_block %.loc12_23.3 [concrete = constants.%.edf] { // CHECK:STDOUT: %MakeUnformed.ref.loc12: %MakeUnformed.type = name_ref MakeUnformed, %MakeUnformed.decl [concrete = constants.%MakeUnformed] // CHECK:STDOUT: %C.ref: type = name_ref C, %C.decl [concrete = constants.%C] // CHECK:STDOUT: %ptr.loc12: type = ptr_type %C.ref [concrete = constants.%ptr.31e] // CHECK:STDOUT: %MakeUnformed.call.loc12: init type = call %MakeUnformed.ref.loc12(%ptr.loc12) [concrete = constants.%.edf] // CHECK:STDOUT: %.loc12_23.2: type = value_of_initializer %MakeUnformed.call.loc12 [concrete = constants.%.edf] // CHECK:STDOUT: %.loc12_23.3: type = converted %MakeUnformed.call.loc12, %.loc12_23.2 [concrete = constants.%.edf] // CHECK:STDOUT: } // CHECK:STDOUT: %.loc12_37.1: %.edf = value_of_initializer @__global_init.%MakeNullC.call // CHECK:STDOUT: %.loc12_37.2: %.edf = converted @__global_init.%MakeNullC.call, %.loc12_37.1 // CHECK:STDOUT: %c: %.edf = value_binding c, %.loc12_37.2 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @__global_init() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %MakeNullEmptyStruct.ref: %MakeNullEmptyStruct.type = name_ref MakeNullEmptyStruct, file.%MakeNullEmptyStruct.decl [concrete = constants.%MakeNullEmptyStruct] // CHECK:STDOUT: %MakeNullEmptyStruct.call: init %.b2d = call %MakeNullEmptyStruct.ref() // CHECK:STDOUT: %MakeNullC.ref: %MakeNullC.type = name_ref MakeNullC, file.%MakeNullC.decl [concrete = constants.%MakeNullC] // CHECK:STDOUT: %MakeNullC.call: init %.edf = call %MakeNullC.ref() // CHECK:STDOUT: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- call_generic.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %MakeUnformed.type: type = fn_type @MakeUnformed [concrete] // CHECK:STDOUT: %MakeUnformed: %MakeUnformed.type = struct_value () [concrete] // CHECK:STDOUT: %MakeNull.type: type = fn_type @MakeNull [concrete] // CHECK:STDOUT: %MakeNull: %MakeNull.type = struct_value () [concrete] // CHECK:STDOUT: %C: type = class_type @C [concrete] // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %empty_struct: %empty_struct_type = struct_value () [concrete] // CHECK:STDOUT: %ptr.c28: type = ptr_type %empty_struct_type [concrete] // CHECK:STDOUT: %.b2d: type = maybe_unformed_type %ptr.c28 [concrete] // CHECK:STDOUT: %pattern_type.b42: type = pattern_type %.b2d [concrete] // CHECK:STDOUT: %MakeNull.specific_fn.4e3: = specific_function %MakeNull, @MakeNull(%empty_struct_type) [concrete] // CHECK:STDOUT: %ptr.31e: type = ptr_type %C [concrete] // CHECK:STDOUT: %.edf: type = maybe_unformed_type %ptr.31e [concrete] // CHECK:STDOUT: %pattern_type.b78: type = pattern_type %.edf [concrete] // CHECK:STDOUT: %MakeNull.specific_fn.152: = specific_function %MakeNull, @MakeNull(%C) [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %s.patt: %pattern_type.b42 = value_binding_pattern s [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %.loc10_24.1: type = splice_block %.loc10_24.3 [concrete = constants.%.b2d] { // CHECK:STDOUT: %MakeUnformed.ref.loc10: %MakeUnformed.type = name_ref MakeUnformed, %MakeUnformed.decl [concrete = constants.%MakeUnformed] // CHECK:STDOUT: %.loc10_22: %empty_struct_type = struct_literal () [concrete = constants.%empty_struct] // CHECK:STDOUT: %.loc10_23: type = converted %.loc10_22, constants.%empty_struct_type [concrete = constants.%empty_struct_type] // CHECK:STDOUT: %ptr.loc10: type = ptr_type %.loc10_23 [concrete = constants.%ptr.c28] // CHECK:STDOUT: %MakeUnformed.call.loc10: init type = call %MakeUnformed.ref.loc10(%ptr.loc10) [concrete = constants.%.b2d] // CHECK:STDOUT: %.loc10_24.2: type = value_of_initializer %MakeUnformed.call.loc10 [concrete = constants.%.b2d] // CHECK:STDOUT: %.loc10_24.3: type = converted %MakeUnformed.call.loc10, %.loc10_24.2 [concrete = constants.%.b2d] // CHECK:STDOUT: } // CHECK:STDOUT: %.loc10_39.1: %.b2d = value_of_initializer @__global_init.%MakeNull.call.loc10 // CHECK:STDOUT: %.loc10_39.2: %.b2d = converted @__global_init.%MakeNull.call.loc10, %.loc10_39.1 // CHECK:STDOUT: %s: %.b2d = value_binding s, %.loc10_39.2 // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %c.patt: %pattern_type.b78 = value_binding_pattern c [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %.loc11_23.1: type = splice_block %.loc11_23.3 [concrete = constants.%.edf] { // CHECK:STDOUT: %MakeUnformed.ref.loc11: %MakeUnformed.type = name_ref MakeUnformed, %MakeUnformed.decl [concrete = constants.%MakeUnformed] // CHECK:STDOUT: %C.ref: type = name_ref C, %C.decl [concrete = constants.%C] // CHECK:STDOUT: %ptr.loc11: type = ptr_type %C.ref [concrete = constants.%ptr.31e] // CHECK:STDOUT: %MakeUnformed.call.loc11: init type = call %MakeUnformed.ref.loc11(%ptr.loc11) [concrete = constants.%.edf] // CHECK:STDOUT: %.loc11_23.2: type = value_of_initializer %MakeUnformed.call.loc11 [concrete = constants.%.edf] // CHECK:STDOUT: %.loc11_23.3: type = converted %MakeUnformed.call.loc11, %.loc11_23.2 [concrete = constants.%.edf] // CHECK:STDOUT: } // CHECK:STDOUT: %.loc11_37.1: %.edf = value_of_initializer @__global_init.%MakeNull.call.loc11 // CHECK:STDOUT: %.loc11_37.2: %.edf = converted @__global_init.%MakeNull.call.loc11, %.loc11_37.1 // CHECK:STDOUT: %c: %.edf = value_binding c, %.loc11_37.2 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @__global_init() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %MakeNull.ref.loc10: %MakeNull.type = name_ref MakeNull, file.%MakeNull.decl [concrete = constants.%MakeNull] // CHECK:STDOUT: %.loc10_38: %empty_struct_type = struct_literal () [concrete = constants.%empty_struct] // CHECK:STDOUT: %.loc10_39: type = converted %.loc10_38, constants.%empty_struct_type [concrete = constants.%empty_struct_type] // CHECK:STDOUT: %MakeNull.specific_fn.loc10: = specific_function %MakeNull.ref.loc10, @MakeNull(constants.%empty_struct_type) [concrete = constants.%MakeNull.specific_fn.4e3] // CHECK:STDOUT: %MakeNull.call.loc10: init %.b2d = call %MakeNull.specific_fn.loc10() // CHECK:STDOUT: %MakeNull.ref.loc11: %MakeNull.type = name_ref MakeNull, file.%MakeNull.decl [concrete = constants.%MakeNull] // CHECK:STDOUT: %C.ref: type = name_ref C, file.%C.decl [concrete = constants.%C] // CHECK:STDOUT: %MakeNull.specific_fn.loc11: = specific_function %MakeNull.ref.loc11, @MakeNull(constants.%C) [concrete = constants.%MakeNull.specific_fn.152] // CHECK:STDOUT: %MakeNull.call.loc11: init %.edf = call %MakeNull.specific_fn.loc11() // CHECK:STDOUT: // CHECK:STDOUT: } // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/builtins/print/char.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/full.carbon // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/builtins/print/char.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/builtins/print/char.carbon import Core library "io"; fn PrintChar(a: char) -> i32 = "print.char"; fn Main() { //@dump-sem-ir-begin PrintChar('1'); Core.PrintChar('2'); //@dump-sem-ir-end } // CHECK:STDOUT: --- char.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %char: type = class_type @Char [concrete] // CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [concrete] // CHECK:STDOUT: %Int.type: type = generic_class_type @Int [concrete] // CHECK:STDOUT: %Int.generic: %Int.type = struct_value () [concrete] // CHECK:STDOUT: %i32: type = class_type @Int, @Int(%int_32) [concrete] // CHECK:STDOUT: %PrintChar.type.7fc: type = fn_type @PrintChar.loc15 [concrete] // CHECK:STDOUT: %PrintChar.f2e: %PrintChar.type.7fc = struct_value () [concrete] // CHECK:STDOUT: %.75c: Core.CharLiteral = char_value U+0031 [concrete] // CHECK:STDOUT: %ImplicitAs.type.cc7: type = generic_interface_type @ImplicitAs [concrete] // CHECK:STDOUT: %ImplicitAs.generic: %ImplicitAs.type.cc7 = struct_value () [concrete] // CHECK:STDOUT: %ImplicitAs.type.d99: type = facet_type <@ImplicitAs, @ImplicitAs(%char)> [concrete] // CHECK:STDOUT: %ImplicitAs.impl_witness.158: = impl_witness imports.%ImplicitAs.impl_witness_table.4bc [concrete] // CHECK:STDOUT: %ImplicitAs.facet: %ImplicitAs.type.d99 = facet_value Core.CharLiteral, (%ImplicitAs.impl_witness.158) [concrete] // CHECK:STDOUT: %ImplicitAs.WithSelf.Convert.type.95b: type = fn_type @ImplicitAs.WithSelf.Convert, @ImplicitAs.WithSelf(%char, %ImplicitAs.facet) [concrete] // CHECK:STDOUT: %.734: type = fn_type_with_self_type %ImplicitAs.WithSelf.Convert.type.95b, %ImplicitAs.facet [concrete] // CHECK:STDOUT: %Core.CharLiteral.as.ImplicitAs.impl.Convert.type: type = fn_type @Core.CharLiteral.as.ImplicitAs.impl.Convert [concrete] // CHECK:STDOUT: %Core.CharLiteral.as.ImplicitAs.impl.Convert: %Core.CharLiteral.as.ImplicitAs.impl.Convert.type = struct_value () [concrete] // CHECK:STDOUT: %Core.CharLiteral.as.ImplicitAs.impl.Convert.bound.75d: = bound_method %.75c, %Core.CharLiteral.as.ImplicitAs.impl.Convert [concrete] // CHECK:STDOUT: %int_49: %char = int_value 49 [concrete] // CHECK:STDOUT: %PrintChar.type.089: type = fn_type @PrintChar.1 [concrete] // CHECK:STDOUT: %PrintChar.d75: %PrintChar.type.089 = struct_value () [concrete] // CHECK:STDOUT: %.f8d: Core.CharLiteral = char_value U+0032 [concrete] // CHECK:STDOUT: %Core.CharLiteral.as.ImplicitAs.impl.Convert.bound.957: = bound_method %.f8d, %Core.CharLiteral.as.ImplicitAs.impl.Convert [concrete] // CHECK:STDOUT: %int_50: %char = int_value 50 [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: .Char = %Core.Char // CHECK:STDOUT: .Int = %Core.Int // CHECK:STDOUT: .ImplicitAs = %Core.ImplicitAs // CHECK:STDOUT: .PrintChar = %Core.PrintChar // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//io // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: %Core.Char: type = import_ref Core//prelude/types/char, Char, loaded [concrete = constants.%char] // CHECK:STDOUT: %Core.Int: %Int.type = import_ref Core//prelude/types/int, Int, loaded [concrete = constants.%Int.generic] // CHECK:STDOUT: %Core.ImplicitAs: %ImplicitAs.type.cc7 = import_ref Core//prelude/operators/as, ImplicitAs, loaded [concrete = constants.%ImplicitAs.generic] // CHECK:STDOUT: %Core.import_ref.b73: %Core.CharLiteral.as.ImplicitAs.impl.Convert.type = import_ref Core//prelude/types/char, loc{{\d+_\d+}}, loaded [concrete = constants.%Core.CharLiteral.as.ImplicitAs.impl.Convert] // CHECK:STDOUT: %ImplicitAs.impl_witness_table.4bc = impl_witness_table (%Core.import_ref.b73), @Core.CharLiteral.as.ImplicitAs.impl [concrete] // CHECK:STDOUT: %Core.PrintChar: %PrintChar.type.089 = import_ref Core//io, PrintChar, loaded [concrete = constants.%PrintChar.d75] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @Main() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %PrintChar.ref.loc19: %PrintChar.type.7fc = name_ref PrintChar, file.%PrintChar.decl [concrete = constants.%PrintChar.f2e] // CHECK:STDOUT: %.loc19_13.1: Core.CharLiteral = char_value U+0031 [concrete = constants.%.75c] // CHECK:STDOUT: %impl.elem0.loc19: %.734 = impl_witness_access constants.%ImplicitAs.impl_witness.158, element0 [concrete = constants.%Core.CharLiteral.as.ImplicitAs.impl.Convert] // CHECK:STDOUT: %bound_method.loc19: = bound_method %.loc19_13.1, %impl.elem0.loc19 [concrete = constants.%Core.CharLiteral.as.ImplicitAs.impl.Convert.bound.75d] // CHECK:STDOUT: %Core.CharLiteral.as.ImplicitAs.impl.Convert.call.loc19: init %char = call %bound_method.loc19(%.loc19_13.1) [concrete = constants.%int_49] // CHECK:STDOUT: %.loc19_13.2: %char = value_of_initializer %Core.CharLiteral.as.ImplicitAs.impl.Convert.call.loc19 [concrete = constants.%int_49] // CHECK:STDOUT: %.loc19_13.3: %char = converted %.loc19_13.1, %.loc19_13.2 [concrete = constants.%int_49] // CHECK:STDOUT: %PrintChar.call.loc19: init %i32 = call %PrintChar.ref.loc19(%.loc19_13.3) // CHECK:STDOUT: %Core.ref: = name_ref Core, imports.%Core [concrete = imports.%Core] // CHECK:STDOUT: %PrintChar.ref.loc20: %PrintChar.type.089 = name_ref PrintChar, imports.%Core.PrintChar [concrete = constants.%PrintChar.d75] // CHECK:STDOUT: %.loc20_18.1: Core.CharLiteral = char_value U+0032 [concrete = constants.%.f8d] // CHECK:STDOUT: %impl.elem0.loc20: %.734 = impl_witness_access constants.%ImplicitAs.impl_witness.158, element0 [concrete = constants.%Core.CharLiteral.as.ImplicitAs.impl.Convert] // CHECK:STDOUT: %bound_method.loc20: = bound_method %.loc20_18.1, %impl.elem0.loc20 [concrete = constants.%Core.CharLiteral.as.ImplicitAs.impl.Convert.bound.957] // CHECK:STDOUT: %Core.CharLiteral.as.ImplicitAs.impl.Convert.call.loc20: init %char = call %bound_method.loc20(%.loc20_18.1) [concrete = constants.%int_50] // CHECK:STDOUT: %.loc20_18.2: %char = value_of_initializer %Core.CharLiteral.as.ImplicitAs.impl.Convert.call.loc20 [concrete = constants.%int_50] // CHECK:STDOUT: %.loc20_18.3: %char = converted %.loc20_18.1, %.loc20_18.2 [concrete = constants.%int_50] // CHECK:STDOUT: %PrintChar.call.loc20: init %i32 = call %PrintChar.ref.loc20(%.loc20_18.3) // CHECK:STDOUT: // CHECK:STDOUT: } // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/builtins/print/int.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/full.carbon // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/builtins/print/int.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/builtins/print/int.carbon import Core library "io"; fn Print(a: i32) = "print.int"; fn Main() { //@dump-sem-ir-begin Print(1); Core.Print(2); //@dump-sem-ir-end } // CHECK:STDOUT: --- int.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [concrete] // CHECK:STDOUT: %Int.type: type = generic_class_type @Int [concrete] // CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] // CHECK:STDOUT: %Int.generic: %Int.type = struct_value () [concrete] // CHECK:STDOUT: %i32: type = class_type @Int, @Int(%int_32) [concrete] // CHECK:STDOUT: %Print.type.543: type = fn_type @Print.loc15 [concrete] // CHECK:STDOUT: %Print.029: %Print.type.543 = struct_value () [concrete] // CHECK:STDOUT: %int_1.5b8: Core.IntLiteral = int_value 1 [concrete] // CHECK:STDOUT: %ImplicitAs.type.cc7: type = generic_interface_type @ImplicitAs [concrete] // CHECK:STDOUT: %ImplicitAs.generic: %ImplicitAs.type.cc7 = struct_value () [concrete] // CHECK:STDOUT: %ImplicitAs.type.e8c: type = facet_type <@ImplicitAs, @ImplicitAs(%i32)> [concrete] // CHECK:STDOUT: %To: Core.IntLiteral = symbolic_binding To, 0 [symbolic] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.4e6: type = fn_type @Core.IntLiteral.as.ImplicitAs.impl.Convert, @Core.IntLiteral.as.ImplicitAs.impl(%To) [symbolic] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.3c2: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.4e6 = struct_value () [symbolic] // CHECK:STDOUT: %ImplicitAs.impl_witness.6bc: = impl_witness imports.%ImplicitAs.impl_witness_table.74f, @Core.IntLiteral.as.ImplicitAs.impl(%int_32) [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.e0d: type = fn_type @Core.IntLiteral.as.ImplicitAs.impl.Convert, @Core.IntLiteral.as.ImplicitAs.impl(%int_32) [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.e0d = struct_value () [concrete] // CHECK:STDOUT: %ImplicitAs.facet: %ImplicitAs.type.e8c = facet_value Core.IntLiteral, (%ImplicitAs.impl_witness.6bc) [concrete] // CHECK:STDOUT: %ImplicitAs.WithSelf.Convert.type.b37: type = fn_type @ImplicitAs.WithSelf.Convert, @ImplicitAs.WithSelf(%i32, %ImplicitAs.facet) [concrete] // CHECK:STDOUT: %.545: type = fn_type_with_self_type %ImplicitAs.WithSelf.Convert.type.b37, %ImplicitAs.facet [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.bound.215: = bound_method %int_1.5b8, %Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5 [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn: = specific_function %Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5, @Core.IntLiteral.as.ImplicitAs.impl.Convert(%int_32) [concrete] // CHECK:STDOUT: %bound_method.38b: = bound_method %int_1.5b8, %Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn [concrete] // CHECK:STDOUT: %int_1.5d2: %i32 = int_value 1 [concrete] // CHECK:STDOUT: %Print.type.6ed: type = fn_type @Print.1 [concrete] // CHECK:STDOUT: %Print.723: %Print.type.6ed = struct_value () [concrete] // CHECK:STDOUT: %int_2.ecc: Core.IntLiteral = int_value 2 [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.bound.4e5: = bound_method %int_2.ecc, %Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5 [concrete] // CHECK:STDOUT: %bound_method.646: = bound_method %int_2.ecc, %Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn [concrete] // CHECK:STDOUT: %int_2.ef8: %i32 = int_value 2 [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: .Int = %Core.Int // CHECK:STDOUT: .ImplicitAs = %Core.ImplicitAs // CHECK:STDOUT: .Print = %Core.Print // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//io // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: %Core.Int: %Int.type = import_ref Core//prelude/types/int, Int, loaded [concrete = constants.%Int.generic] // CHECK:STDOUT: %Core.ImplicitAs: %ImplicitAs.type.cc7 = import_ref Core//prelude/operators/as, ImplicitAs, loaded [concrete = constants.%ImplicitAs.generic] // CHECK:STDOUT: %Core.import_ref.42d: @Core.IntLiteral.as.ImplicitAs.impl.%Core.IntLiteral.as.ImplicitAs.impl.Convert.type (%Core.IntLiteral.as.ImplicitAs.impl.Convert.type.4e6) = import_ref Core//prelude/types/int, loc{{\d+_\d+}}, loaded [symbolic = @Core.IntLiteral.as.ImplicitAs.impl.%Core.IntLiteral.as.ImplicitAs.impl.Convert (constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.3c2)] // CHECK:STDOUT: %ImplicitAs.impl_witness_table.74f = impl_witness_table (%Core.import_ref.42d), @Core.IntLiteral.as.ImplicitAs.impl [concrete] // CHECK:STDOUT: %Core.Print: %Print.type.6ed = import_ref Core//io, Print, loaded [concrete = constants.%Print.723] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @Main() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %Print.ref.loc19: %Print.type.543 = name_ref Print, file.%Print.decl [concrete = constants.%Print.029] // CHECK:STDOUT: %int_1: Core.IntLiteral = int_value 1 [concrete = constants.%int_1.5b8] // CHECK:STDOUT: %impl.elem0.loc19: %.545 = impl_witness_access constants.%ImplicitAs.impl_witness.6bc, element0 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5] // CHECK:STDOUT: %bound_method.loc19_9.1: = bound_method %int_1, %impl.elem0.loc19 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.bound.215] // CHECK:STDOUT: %specific_fn.loc19: = specific_function %impl.elem0.loc19, @Core.IntLiteral.as.ImplicitAs.impl.Convert(constants.%int_32) [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn] // CHECK:STDOUT: %bound_method.loc19_9.2: = bound_method %int_1, %specific_fn.loc19 [concrete = constants.%bound_method.38b] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc19: init %i32 = call %bound_method.loc19_9.2(%int_1) [concrete = constants.%int_1.5d2] // CHECK:STDOUT: %.loc19_9.1: %i32 = value_of_initializer %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc19 [concrete = constants.%int_1.5d2] // CHECK:STDOUT: %.loc19_9.2: %i32 = converted %int_1, %.loc19_9.1 [concrete = constants.%int_1.5d2] // CHECK:STDOUT: %Print.call.loc19: init %empty_tuple.type = call %Print.ref.loc19(%.loc19_9.2) // CHECK:STDOUT: %Core.ref: = name_ref Core, imports.%Core [concrete = imports.%Core] // CHECK:STDOUT: %Print.ref.loc20: %Print.type.6ed = name_ref Print, imports.%Core.Print [concrete = constants.%Print.723] // CHECK:STDOUT: %int_2: Core.IntLiteral = int_value 2 [concrete = constants.%int_2.ecc] // CHECK:STDOUT: %impl.elem0.loc20: %.545 = impl_witness_access constants.%ImplicitAs.impl_witness.6bc, element0 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5] // CHECK:STDOUT: %bound_method.loc20_14.1: = bound_method %int_2, %impl.elem0.loc20 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.bound.4e5] // CHECK:STDOUT: %specific_fn.loc20: = specific_function %impl.elem0.loc20, @Core.IntLiteral.as.ImplicitAs.impl.Convert(constants.%int_32) [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn] // CHECK:STDOUT: %bound_method.loc20_14.2: = bound_method %int_2, %specific_fn.loc20 [concrete = constants.%bound_method.646] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc20: init %i32 = call %bound_method.loc20_14.2(%int_2) [concrete = constants.%int_2.ef8] // CHECK:STDOUT: %.loc20_14.1: %i32 = value_of_initializer %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc20 [concrete = constants.%int_2.ef8] // CHECK:STDOUT: %.loc20_14.2: %i32 = converted %int_2, %.loc20_14.1 [concrete = constants.%int_2.ef8] // CHECK:STDOUT: %Print.call.loc20: init %empty_tuple.type = call %Print.ref.loc20(%.loc20_14.2) // CHECK:STDOUT: // CHECK:STDOUT: } // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/builtins/read/char.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/full.carbon // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/builtins/read/char.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/builtins/read/char.carbon // --- char.carbon import Core library "io"; fn ReadChar() -> i32 = "read.char"; fn Main() { //@dump-sem-ir-begin let unused n: i32 = ReadChar(); let unused m: i32 = Core.ReadChar(); //@dump-sem-ir-end } // CHECK:STDOUT: --- char.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [concrete] // CHECK:STDOUT: %Int.type: type = generic_class_type @Int [concrete] // CHECK:STDOUT: %Int.generic: %Int.type = struct_value () [concrete] // CHECK:STDOUT: %i32: type = class_type @Int, @Int(%int_32) [concrete] // CHECK:STDOUT: %pattern_type.7ce: type = pattern_type %i32 [concrete] // CHECK:STDOUT: %ReadChar.type.c36: type = fn_type @ReadChar.loc4 [concrete] // CHECK:STDOUT: %ReadChar.6ad: %ReadChar.type.c36 = struct_value () [concrete] // CHECK:STDOUT: %ReadChar.type.9f3: type = fn_type @ReadChar.1 [concrete] // CHECK:STDOUT: %ReadChar.01f: %ReadChar.type.9f3 = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: .Int = %Core.Int // CHECK:STDOUT: .ReadChar = %Core.ReadChar // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//io // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: %Core.Int: %Int.type = import_ref Core//prelude/types/int, Int, loaded [concrete = constants.%Int.generic] // CHECK:STDOUT: %Core.ReadChar: %ReadChar.type.9f3 = import_ref Core//io, ReadChar, loaded [concrete = constants.%ReadChar.01f] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @Main() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %n.patt: %pattern_type.7ce = value_binding_pattern n [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %ReadChar.ref.loc8: %ReadChar.type.c36 = name_ref ReadChar, file.%ReadChar.decl [concrete = constants.%ReadChar.6ad] // CHECK:STDOUT: %ReadChar.call.loc8: init %i32 = call %ReadChar.ref.loc8() // CHECK:STDOUT: %i32.loc8: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %.loc8_32.1: %i32 = value_of_initializer %ReadChar.call.loc8 // CHECK:STDOUT: %.loc8_32.2: %i32 = converted %ReadChar.call.loc8, %.loc8_32.1 // CHECK:STDOUT: %n: %i32 = value_binding n, %.loc8_32.2 // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %m.patt: %pattern_type.7ce = value_binding_pattern m [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %Core.ref: = name_ref Core, imports.%Core [concrete = imports.%Core] // CHECK:STDOUT: %ReadChar.ref.loc9: %ReadChar.type.9f3 = name_ref ReadChar, imports.%Core.ReadChar [concrete = constants.%ReadChar.01f] // CHECK:STDOUT: %ReadChar.call.loc9: init %i32 = call %ReadChar.ref.loc9() // CHECK:STDOUT: %i32.loc9: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %.loc9_37.1: %i32 = value_of_initializer %ReadChar.call.loc9 // CHECK:STDOUT: %.loc9_37.2: %i32 = converted %ReadChar.call.loc9, %.loc9_37.1 // CHECK:STDOUT: %m: %i32 = value_binding m, %.loc9_37.2 // CHECK:STDOUT: // CHECK:STDOUT: } // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/builtins/type/and.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/convert.carbon // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/builtins/type/and.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/builtins/type/and.carbon // --- combine_facets.carbon library "[[@TEST_NAME]]"; fn TypeAnd(a: type, b: type) -> type = "type.and"; interface I {} interface J {} fn TakeIJ(unused T:! TypeAnd(I, J)) {} class IJ { impl as I {} impl as J {} } fn Call() { TakeIJ(IJ); } // --- fail_combine_facets_bad_call.carbon library "[[@TEST_NAME]]"; fn TypeAnd(a: type, b: type) -> type = "type.and"; interface I {} interface J {} fn TakeIJ(unused T:! TypeAnd(I, J)) {} class JustI { impl as I {} } class JustJ { impl as J {} } fn Call() { // CHECK:STDERR: fail_combine_facets_bad_call.carbon:[[@LINE+7]]:3: error: cannot convert type `JustI` into type implementing `I & J` [ConversionFailureTypeToFacet] // CHECK:STDERR: TakeIJ(JustI); // CHECK:STDERR: ^~~~~~~~~~~~~ // CHECK:STDERR: fail_combine_facets_bad_call.carbon:[[@LINE-14]]:18: note: initializing generic parameter `T` declared here [InitializingGenericParam] // CHECK:STDERR: fn TakeIJ(unused T:! TypeAnd(I, J)) {} // CHECK:STDERR: ^ // CHECK:STDERR: TakeIJ(JustI); // CHECK:STDERR: fail_combine_facets_bad_call.carbon:[[@LINE+7]]:3: error: cannot convert type `JustJ` into type implementing `I & J` [ConversionFailureTypeToFacet] // CHECK:STDERR: TakeIJ(JustJ); // CHECK:STDERR: ^~~~~~~~~~~~~ // CHECK:STDERR: fail_combine_facets_bad_call.carbon:[[@LINE-22]]:18: note: initializing generic parameter `T` declared here [InitializingGenericParam] // CHECK:STDERR: fn TakeIJ(unused T:! TypeAnd(I, J)) {} // CHECK:STDERR: ^ // CHECK:STDERR: TakeIJ(JustJ); } // --- fail_bad_decl.carbon library "[[@TEST_NAME]]"; // CHECK:STDERR: fail_bad_decl.carbon:[[@LINE+4]]:1: error: invalid signature for builtin function "type.and" [InvalidBuiltinSignature] // CHECK:STDERR: fn TooFew(a: type) -> type = "type.and"; // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: fn TooFew(a: type) -> type = "type.and"; // CHECK:STDERR: fail_bad_decl.carbon:[[@LINE+4]]:1: error: invalid signature for builtin function "type.and" [InvalidBuiltinSignature] // CHECK:STDERR: fn TooMany(a: type, b: type, c: type) -> type = "type.and"; // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: fn TooMany(a: type, b: type, c: type) -> type = "type.and"; // CHECK:STDERR: fail_bad_decl.carbon:[[@LINE+4]]:1: error: invalid signature for builtin function "type.and" [InvalidBuiltinSignature] // CHECK:STDERR: fn ParamNotType(a: {}, b: {}) -> type = "type.and"; // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: fn ParamNotType(a: {}, b: {}) -> type = "type.and"; // CHECK:STDERR: fail_bad_decl.carbon:[[@LINE+4]]:1: error: invalid signature for builtin function "type.and" [InvalidBuiltinSignature] // CHECK:STDERR: fn ResultNotType(a: type, b: type) -> {} = "type.and"; // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: fn ResultNotType(a: type, b: type) -> {} = "type.and"; // CHECK:STDERR: fail_bad_decl.carbon:[[@LINE+4]]:1: error: invalid signature for builtin function "type.and" [InvalidBuiltinSignature] // CHECK:STDERR: fn ResultNotSpecified(a: type, b: type) = "type.and"; // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: fn ResultNotSpecified(a: type, b: type) = "type.and"; ================================================ FILE: toolchain/check/testdata/choice/basic.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/uint.carbon // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/choice/basic.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/choice/basic.carbon // --- no_alternative.carbon library "[[@TEST_NAME]]"; //@dump-sem-ir-begin choice Never {} //@dump-sem-ir-end // --- one_alternative.carbon library "[[@TEST_NAME]]"; //@dump-sem-ir-begin choice Always { Sunny } let mood: Always = Always.Sunny; //@dump-sem-ir-end // --- multiple_alternatives.carbon library "[[@TEST_NAME]]"; //@dump-sem-ir-begin choice Ordering { Less, Equivalent, Greater, Incomparable } let less: Ordering = Ordering.Less; let equiv: Ordering = Ordering.Equivalent; let greater: Ordering = Ordering.Greater; let inc: Ordering = Ordering.Incomparable; //@dump-sem-ir-end // --- fail_no_alternative_construct.carbon library "[[@TEST_NAME]]"; choice Never {} // TODO: Can we produce a better diagnostic? // CHECK:STDERR: fail_no_alternative_construct.carbon:[[@LINE+4]]:20: error: cannot initialize class with 1 field from struct with 0 fields [StructInitElementCountMismatch] // CHECK:STDERR: let never: Never = {}; // CHECK:STDERR: ^~ // CHECK:STDERR: let never: Never = {}; // CHECK:STDOUT: --- no_alternative.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %Never: type = class_type @Never [concrete] // CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] // CHECK:STDOUT: %struct_type.discriminant: type = struct_type {.discriminant: %empty_tuple.type} [concrete] // CHECK:STDOUT: %complete_type: = complete_type_witness %struct_type.discriminant [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: %Never.decl: type = class_decl @Never [concrete = constants.%Never] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @Never { // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%struct_type.discriminant [concrete = constants.%complete_type] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%Never // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- one_alternative.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %Always: type = class_type @Always [concrete] // CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] // CHECK:STDOUT: %struct_type.discriminant: type = struct_type {.discriminant: %empty_tuple.type} [concrete] // CHECK:STDOUT: %complete_type: = complete_type_witness %struct_type.discriminant [concrete] // CHECK:STDOUT: %empty_tuple: %empty_tuple.type = tuple_value () [concrete] // CHECK:STDOUT: %struct: %struct_type.discriminant = struct_value (%empty_tuple) [concrete] // CHECK:STDOUT: %Always.val: %Always = struct_value (%empty_tuple) [concrete] // CHECK:STDOUT: %pattern_type: type = pattern_type %Always [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: %Always.decl: type = class_decl @Always [concrete = constants.%Always] {} {} // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %mood.patt: %pattern_type = value_binding_pattern mood [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %Always.ref: type = name_ref Always, %Always.decl [concrete = constants.%Always] // CHECK:STDOUT: %mood: %Always = value_binding mood, @__global_init.%Sunny.ref // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @Always { // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%struct_type.discriminant [concrete = constants.%complete_type] // CHECK:STDOUT: %.loc6_1.1: %empty_tuple.type = tuple_literal () [concrete = constants.%empty_tuple] // CHECK:STDOUT: %empty_tuple: %empty_tuple.type = tuple_value () [concrete = constants.%empty_tuple] // CHECK:STDOUT: %.loc6_1.2: %empty_tuple.type = converted %.loc6_1.1, %empty_tuple [concrete = constants.%empty_tuple] // CHECK:STDOUT: %.loc6_1.3: %struct_type.discriminant = struct_literal (%.loc6_1.2) [concrete = constants.%struct] // CHECK:STDOUT: %.loc6_1.4: ref %Always = temporary_storage // CHECK:STDOUT: %.loc6_1.5: ref %empty_tuple.type = class_element_access %.loc6_1.4, element0 // CHECK:STDOUT: %.loc6_1.6: init %empty_tuple.type = tuple_init () [concrete = constants.%empty_tuple] // CHECK:STDOUT: %.loc6_1.7: init %empty_tuple.type = converted %.loc6_1.2, %.loc6_1.6 [concrete = constants.%empty_tuple] // CHECK:STDOUT: %.loc6_1.8: init %Always to %.loc6_1.4 = class_init (%.loc6_1.7) [concrete = constants.%Always.val] // CHECK:STDOUT: %.loc6_1.9: init %Always = converted %.loc6_1.3, %.loc6_1.8 [concrete = constants.%Always.val] // CHECK:STDOUT: %.loc6_1.10: ref %Always = temporary %.loc6_1.4, %.loc6_1.9 // CHECK:STDOUT: %.loc6_1.11: %Always = acquire_value %.loc6_1.10 // CHECK:STDOUT: %Sunny: %Always = value_binding Sunny, %.loc6_1.11 // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%Always // CHECK:STDOUT: .Sunny = %Sunny // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @__global_init() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %Always.ref: type = name_ref Always, file.%Always.decl [concrete = constants.%Always] // CHECK:STDOUT: %Sunny.ref: %Always = name_ref Sunny, @Always.%Sunny // CHECK:STDOUT: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- multiple_alternatives.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %Ordering: type = class_type @Ordering [concrete] // CHECK:STDOUT: %int_2.ecc: Core.IntLiteral = int_value 2 [concrete] // CHECK:STDOUT: %N: Core.IntLiteral = symbolic_binding N, 0 [symbolic] // CHECK:STDOUT: %u2: type = class_type @UInt, @UInt(%int_2.ecc) [concrete] // CHECK:STDOUT: %struct_type.discriminant: type = struct_type {.discriminant: %u2} [concrete] // CHECK:STDOUT: %complete_type.de2: = complete_type_witness %struct_type.discriminant [concrete] // CHECK:STDOUT: %int_0.5c6: Core.IntLiteral = int_value 0 [concrete] // CHECK:STDOUT: %ImplicitAs.type.a92: type = facet_type <@ImplicitAs, @ImplicitAs(%u2)> [concrete] // CHECK:STDOUT: %To: Core.IntLiteral = symbolic_binding To, 0 [symbolic] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.6a6: type = fn_type @Core.IntLiteral.as.ImplicitAs.impl.Convert, @Core.IntLiteral.as.ImplicitAs.impl(%To) [symbolic] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.46e: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.6a6 = struct_value () [symbolic] // CHECK:STDOUT: %ImplicitAs.impl_witness.762: = impl_witness imports.%ImplicitAs.impl_witness_table.899, @Core.IntLiteral.as.ImplicitAs.impl(%int_2.ecc) [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.c8c: type = fn_type @Core.IntLiteral.as.ImplicitAs.impl.Convert, @Core.IntLiteral.as.ImplicitAs.impl(%int_2.ecc) [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.0ed: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.c8c = struct_value () [concrete] // CHECK:STDOUT: %ImplicitAs.facet: %ImplicitAs.type.a92 = facet_value Core.IntLiteral, (%ImplicitAs.impl_witness.762) [concrete] // CHECK:STDOUT: %ImplicitAs.WithSelf.Convert.type.539: type = fn_type @ImplicitAs.WithSelf.Convert, @ImplicitAs.WithSelf(%u2, %ImplicitAs.facet) [concrete] // CHECK:STDOUT: %.055: type = fn_type_with_self_type %ImplicitAs.WithSelf.Convert.type.539, %ImplicitAs.facet [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.bound.dd9: = bound_method %int_0.5c6, %Core.IntLiteral.as.ImplicitAs.impl.Convert.0ed [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn: = specific_function %Core.IntLiteral.as.ImplicitAs.impl.Convert.0ed, @Core.IntLiteral.as.ImplicitAs.impl.Convert(%int_2.ecc) [concrete] // CHECK:STDOUT: %bound_method.f58: = bound_method %int_0.5c6, %Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn [concrete] // CHECK:STDOUT: %int_0.9fd: %u2 = int_value 0 [concrete] // CHECK:STDOUT: %struct.559: %struct_type.discriminant = struct_value (%int_0.9fd) [concrete] // CHECK:STDOUT: %Copy.type: type = facet_type <@Copy> [concrete] // CHECK:STDOUT: %UInt.as.Copy.impl.Op.type.68f: type = fn_type @UInt.as.Copy.impl.Op, @UInt.as.Copy.impl(%N) [symbolic] // CHECK:STDOUT: %UInt.as.Copy.impl.Op.576: %UInt.as.Copy.impl.Op.type.68f = struct_value () [symbolic] // CHECK:STDOUT: %Copy.impl_witness.a32: = impl_witness imports.%Copy.impl_witness_table.bd0, @UInt.as.Copy.impl(%int_2.ecc) [concrete] // CHECK:STDOUT: %UInt.as.Copy.impl.Op.type.f98: type = fn_type @UInt.as.Copy.impl.Op, @UInt.as.Copy.impl(%int_2.ecc) [concrete] // CHECK:STDOUT: %UInt.as.Copy.impl.Op.b2d: %UInt.as.Copy.impl.Op.type.f98 = struct_value () [concrete] // CHECK:STDOUT: %Copy.facet: %Copy.type = facet_value %u2, (%Copy.impl_witness.a32) [concrete] // CHECK:STDOUT: %Copy.WithSelf.Op.type.bd7: type = fn_type @Copy.WithSelf.Op, @Copy.WithSelf(%Copy.facet) [concrete] // CHECK:STDOUT: %.a82: type = fn_type_with_self_type %Copy.WithSelf.Op.type.bd7, %Copy.facet [concrete] // CHECK:STDOUT: %UInt.as.Copy.impl.Op.bound.fba: = bound_method %int_0.9fd, %UInt.as.Copy.impl.Op.b2d [concrete] // CHECK:STDOUT: %UInt.as.Copy.impl.Op.specific_fn: = specific_function %UInt.as.Copy.impl.Op.b2d, @UInt.as.Copy.impl.Op(%int_2.ecc) [concrete] // CHECK:STDOUT: %bound_method.428: = bound_method %int_0.9fd, %UInt.as.Copy.impl.Op.specific_fn [concrete] // CHECK:STDOUT: %Ordering.val.9ea: %Ordering = struct_value (%int_0.9fd) [concrete] // CHECK:STDOUT: %int_1.5b8: Core.IntLiteral = int_value 1 [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.bound.6b7: = bound_method %int_1.5b8, %Core.IntLiteral.as.ImplicitAs.impl.Convert.0ed [concrete] // CHECK:STDOUT: %bound_method.a4b: = bound_method %int_1.5b8, %Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn [concrete] // CHECK:STDOUT: %int_1.b2c: %u2 = int_value 1 [concrete] // CHECK:STDOUT: %struct.0ff: %struct_type.discriminant = struct_value (%int_1.b2c) [concrete] // CHECK:STDOUT: %UInt.as.Copy.impl.Op.bound.67d: = bound_method %int_1.b2c, %UInt.as.Copy.impl.Op.b2d [concrete] // CHECK:STDOUT: %bound_method.8f6: = bound_method %int_1.b2c, %UInt.as.Copy.impl.Op.specific_fn [concrete] // CHECK:STDOUT: %Ordering.val.d41: %Ordering = struct_value (%int_1.b2c) [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.bound.c76: = bound_method %int_2.ecc, %Core.IntLiteral.as.ImplicitAs.impl.Convert.0ed [concrete] // CHECK:STDOUT: %bound_method.c5b: = bound_method %int_2.ecc, %Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn [concrete] // CHECK:STDOUT: %int_2.788: %u2 = int_value 2 [concrete] // CHECK:STDOUT: %struct.6e6: %struct_type.discriminant = struct_value (%int_2.788) [concrete] // CHECK:STDOUT: %UInt.as.Copy.impl.Op.bound.f0c: = bound_method %int_2.788, %UInt.as.Copy.impl.Op.b2d [concrete] // CHECK:STDOUT: %bound_method.f1a: = bound_method %int_2.788, %UInt.as.Copy.impl.Op.specific_fn [concrete] // CHECK:STDOUT: %Ordering.val.e86: %Ordering = struct_value (%int_2.788) [concrete] // CHECK:STDOUT: %int_3.1ba: Core.IntLiteral = int_value 3 [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.bound.cdf: = bound_method %int_3.1ba, %Core.IntLiteral.as.ImplicitAs.impl.Convert.0ed [concrete] // CHECK:STDOUT: %bound_method.898: = bound_method %int_3.1ba, %Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn [concrete] // CHECK:STDOUT: %int_3.975: %u2 = int_value 3 [concrete] // CHECK:STDOUT: %struct.7bd: %struct_type.discriminant = struct_value (%int_3.975) [concrete] // CHECK:STDOUT: %UInt.as.Copy.impl.Op.bound.7b5: = bound_method %int_3.975, %UInt.as.Copy.impl.Op.b2d [concrete] // CHECK:STDOUT: %bound_method.824: = bound_method %int_3.975, %UInt.as.Copy.impl.Op.specific_fn [concrete] // CHECK:STDOUT: %Ordering.val.a17: %Ordering = struct_value (%int_3.975) [concrete] // CHECK:STDOUT: %pattern_type.a36: type = pattern_type %Ordering [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core.import_ref.741: @Core.IntLiteral.as.ImplicitAs.impl.%Core.IntLiteral.as.ImplicitAs.impl.Convert.type (%Core.IntLiteral.as.ImplicitAs.impl.Convert.type.6a6) = import_ref Core//prelude/parts/uint, loc{{\d+_\d+}}, loaded [symbolic = @Core.IntLiteral.as.ImplicitAs.impl.%Core.IntLiteral.as.ImplicitAs.impl.Convert (constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.46e)] // CHECK:STDOUT: %ImplicitAs.impl_witness_table.899 = impl_witness_table (%Core.import_ref.741), @Core.IntLiteral.as.ImplicitAs.impl [concrete] // CHECK:STDOUT: %Core.import_ref.c3c: @UInt.as.Copy.impl.%UInt.as.Copy.impl.Op.type (%UInt.as.Copy.impl.Op.type.68f) = import_ref Core//prelude/parts/uint, loc{{\d+_\d+}}, loaded [symbolic = @UInt.as.Copy.impl.%UInt.as.Copy.impl.Op (constants.%UInt.as.Copy.impl.Op.576)] // CHECK:STDOUT: %Copy.impl_witness_table.bd0 = impl_witness_table (%Core.import_ref.c3c), @UInt.as.Copy.impl [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: %Ordering.decl: type = class_decl @Ordering [concrete = constants.%Ordering] {} {} // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %less.patt: %pattern_type.a36 = value_binding_pattern less [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %Ordering.ref.loc11: type = name_ref Ordering, %Ordering.decl [concrete = constants.%Ordering] // CHECK:STDOUT: %less: %Ordering = value_binding less, @__global_init.%Less.ref // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %equiv.patt: %pattern_type.a36 = value_binding_pattern equiv [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %Ordering.ref.loc12: type = name_ref Ordering, %Ordering.decl [concrete = constants.%Ordering] // CHECK:STDOUT: %equiv: %Ordering = value_binding equiv, @__global_init.%Equivalent.ref // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %greater.patt: %pattern_type.a36 = value_binding_pattern greater [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %Ordering.ref.loc13: type = name_ref Ordering, %Ordering.decl [concrete = constants.%Ordering] // CHECK:STDOUT: %greater: %Ordering = value_binding greater, @__global_init.%Greater.ref // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %inc.patt: %pattern_type.a36 = value_binding_pattern inc [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %Ordering.ref.loc14: type = name_ref Ordering, %Ordering.decl [concrete = constants.%Ordering] // CHECK:STDOUT: %inc: %Ordering = value_binding inc, @__global_init.%Incomparable.ref // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @Ordering { // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%struct_type.discriminant [concrete = constants.%complete_type.de2] // CHECK:STDOUT: %int_0: Core.IntLiteral = int_value 0 [concrete = constants.%int_0.5c6] // CHECK:STDOUT: %impl.elem0.loc5_7.1: %.055 = impl_witness_access constants.%ImplicitAs.impl_witness.762, element0 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.0ed] // CHECK:STDOUT: %bound_method.loc5_7.1: = bound_method %int_0, %impl.elem0.loc5_7.1 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.bound.dd9] // CHECK:STDOUT: %specific_fn.loc5_7.1: = specific_function %impl.elem0.loc5_7.1, @Core.IntLiteral.as.ImplicitAs.impl.Convert(constants.%int_2.ecc) [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn] // CHECK:STDOUT: %bound_method.loc5_7.2: = bound_method %int_0, %specific_fn.loc5_7.1 [concrete = constants.%bound_method.f58] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc5: init %u2 = call %bound_method.loc5_7.2(%int_0) [concrete = constants.%int_0.9fd] // CHECK:STDOUT: %.loc5_7.1: %u2 = value_of_initializer %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc5 [concrete = constants.%int_0.9fd] // CHECK:STDOUT: %.loc5_7.2: %u2 = converted %int_0, %.loc5_7.1 [concrete = constants.%int_0.9fd] // CHECK:STDOUT: %.loc5_7.3: %struct_type.discriminant = struct_literal (%.loc5_7.2) [concrete = constants.%struct.559] // CHECK:STDOUT: %impl.elem0.loc5_7.2: %.a82 = impl_witness_access constants.%Copy.impl_witness.a32, element0 [concrete = constants.%UInt.as.Copy.impl.Op.b2d] // CHECK:STDOUT: %bound_method.loc5_7.3: = bound_method %.loc5_7.2, %impl.elem0.loc5_7.2 [concrete = constants.%UInt.as.Copy.impl.Op.bound.fba] // CHECK:STDOUT: %specific_fn.loc5_7.2: = specific_function %impl.elem0.loc5_7.2, @UInt.as.Copy.impl.Op(constants.%int_2.ecc) [concrete = constants.%UInt.as.Copy.impl.Op.specific_fn] // CHECK:STDOUT: %bound_method.loc5_7.4: = bound_method %.loc5_7.2, %specific_fn.loc5_7.2 [concrete = constants.%bound_method.428] // CHECK:STDOUT: %UInt.as.Copy.impl.Op.call.loc5: init %u2 = call %bound_method.loc5_7.4(%.loc5_7.2) [concrete = constants.%int_0.9fd] // CHECK:STDOUT: %.loc5_7.4: ref %Ordering = temporary_storage // CHECK:STDOUT: %.loc5_7.5: ref %u2 = class_element_access %.loc5_7.4, element0 // CHECK:STDOUT: %.loc5_7.6: init %u2 to %.loc5_7.5 = in_place_init %UInt.as.Copy.impl.Op.call.loc5 [concrete = constants.%int_0.9fd] // CHECK:STDOUT: %.loc5_7.7: init %Ordering to %.loc5_7.4 = class_init (%.loc5_7.6) [concrete = constants.%Ordering.val.9ea] // CHECK:STDOUT: %.loc5_7.8: init %Ordering = converted %.loc5_7.3, %.loc5_7.7 [concrete = constants.%Ordering.val.9ea] // CHECK:STDOUT: %.loc5_7.9: ref %Ordering = temporary %.loc5_7.4, %.loc5_7.8 // CHECK:STDOUT: %.loc5_7.10: %Ordering = acquire_value %.loc5_7.9 // CHECK:STDOUT: %Less: %Ordering = value_binding Less, %.loc5_7.10 // CHECK:STDOUT: %int_1: Core.IntLiteral = int_value 1 [concrete = constants.%int_1.5b8] // CHECK:STDOUT: %impl.elem0.loc6_13.1: %.055 = impl_witness_access constants.%ImplicitAs.impl_witness.762, element0 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.0ed] // CHECK:STDOUT: %bound_method.loc6_13.1: = bound_method %int_1, %impl.elem0.loc6_13.1 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.bound.6b7] // CHECK:STDOUT: %specific_fn.loc6_13.1: = specific_function %impl.elem0.loc6_13.1, @Core.IntLiteral.as.ImplicitAs.impl.Convert(constants.%int_2.ecc) [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn] // CHECK:STDOUT: %bound_method.loc6_13.2: = bound_method %int_1, %specific_fn.loc6_13.1 [concrete = constants.%bound_method.a4b] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc6: init %u2 = call %bound_method.loc6_13.2(%int_1) [concrete = constants.%int_1.b2c] // CHECK:STDOUT: %.loc6_13.1: %u2 = value_of_initializer %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc6 [concrete = constants.%int_1.b2c] // CHECK:STDOUT: %.loc6_13.2: %u2 = converted %int_1, %.loc6_13.1 [concrete = constants.%int_1.b2c] // CHECK:STDOUT: %.loc6_13.3: %struct_type.discriminant = struct_literal (%.loc6_13.2) [concrete = constants.%struct.0ff] // CHECK:STDOUT: %impl.elem0.loc6_13.2: %.a82 = impl_witness_access constants.%Copy.impl_witness.a32, element0 [concrete = constants.%UInt.as.Copy.impl.Op.b2d] // CHECK:STDOUT: %bound_method.loc6_13.3: = bound_method %.loc6_13.2, %impl.elem0.loc6_13.2 [concrete = constants.%UInt.as.Copy.impl.Op.bound.67d] // CHECK:STDOUT: %specific_fn.loc6_13.2: = specific_function %impl.elem0.loc6_13.2, @UInt.as.Copy.impl.Op(constants.%int_2.ecc) [concrete = constants.%UInt.as.Copy.impl.Op.specific_fn] // CHECK:STDOUT: %bound_method.loc6_13.4: = bound_method %.loc6_13.2, %specific_fn.loc6_13.2 [concrete = constants.%bound_method.8f6] // CHECK:STDOUT: %UInt.as.Copy.impl.Op.call.loc6: init %u2 = call %bound_method.loc6_13.4(%.loc6_13.2) [concrete = constants.%int_1.b2c] // CHECK:STDOUT: %.loc6_13.4: ref %Ordering = temporary_storage // CHECK:STDOUT: %.loc6_13.5: ref %u2 = class_element_access %.loc6_13.4, element0 // CHECK:STDOUT: %.loc6_13.6: init %u2 to %.loc6_13.5 = in_place_init %UInt.as.Copy.impl.Op.call.loc6 [concrete = constants.%int_1.b2c] // CHECK:STDOUT: %.loc6_13.7: init %Ordering to %.loc6_13.4 = class_init (%.loc6_13.6) [concrete = constants.%Ordering.val.d41] // CHECK:STDOUT: %.loc6_13.8: init %Ordering = converted %.loc6_13.3, %.loc6_13.7 [concrete = constants.%Ordering.val.d41] // CHECK:STDOUT: %.loc6_13.9: ref %Ordering = temporary %.loc6_13.4, %.loc6_13.8 // CHECK:STDOUT: %.loc6_13.10: %Ordering = acquire_value %.loc6_13.9 // CHECK:STDOUT: %Equivalent: %Ordering = value_binding Equivalent, %.loc6_13.10 // CHECK:STDOUT: %int_2: Core.IntLiteral = int_value 2 [concrete = constants.%int_2.ecc] // CHECK:STDOUT: %impl.elem0.loc7_10.1: %.055 = impl_witness_access constants.%ImplicitAs.impl_witness.762, element0 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.0ed] // CHECK:STDOUT: %bound_method.loc7_10.1: = bound_method %int_2, %impl.elem0.loc7_10.1 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.bound.c76] // CHECK:STDOUT: %specific_fn.loc7_10.1: = specific_function %impl.elem0.loc7_10.1, @Core.IntLiteral.as.ImplicitAs.impl.Convert(constants.%int_2.ecc) [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn] // CHECK:STDOUT: %bound_method.loc7_10.2: = bound_method %int_2, %specific_fn.loc7_10.1 [concrete = constants.%bound_method.c5b] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc7: init %u2 = call %bound_method.loc7_10.2(%int_2) [concrete = constants.%int_2.788] // CHECK:STDOUT: %.loc7_10.1: %u2 = value_of_initializer %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc7 [concrete = constants.%int_2.788] // CHECK:STDOUT: %.loc7_10.2: %u2 = converted %int_2, %.loc7_10.1 [concrete = constants.%int_2.788] // CHECK:STDOUT: %.loc7_10.3: %struct_type.discriminant = struct_literal (%.loc7_10.2) [concrete = constants.%struct.6e6] // CHECK:STDOUT: %impl.elem0.loc7_10.2: %.a82 = impl_witness_access constants.%Copy.impl_witness.a32, element0 [concrete = constants.%UInt.as.Copy.impl.Op.b2d] // CHECK:STDOUT: %bound_method.loc7_10.3: = bound_method %.loc7_10.2, %impl.elem0.loc7_10.2 [concrete = constants.%UInt.as.Copy.impl.Op.bound.f0c] // CHECK:STDOUT: %specific_fn.loc7_10.2: = specific_function %impl.elem0.loc7_10.2, @UInt.as.Copy.impl.Op(constants.%int_2.ecc) [concrete = constants.%UInt.as.Copy.impl.Op.specific_fn] // CHECK:STDOUT: %bound_method.loc7_10.4: = bound_method %.loc7_10.2, %specific_fn.loc7_10.2 [concrete = constants.%bound_method.f1a] // CHECK:STDOUT: %UInt.as.Copy.impl.Op.call.loc7: init %u2 = call %bound_method.loc7_10.4(%.loc7_10.2) [concrete = constants.%int_2.788] // CHECK:STDOUT: %.loc7_10.4: ref %Ordering = temporary_storage // CHECK:STDOUT: %.loc7_10.5: ref %u2 = class_element_access %.loc7_10.4, element0 // CHECK:STDOUT: %.loc7_10.6: init %u2 to %.loc7_10.5 = in_place_init %UInt.as.Copy.impl.Op.call.loc7 [concrete = constants.%int_2.788] // CHECK:STDOUT: %.loc7_10.7: init %Ordering to %.loc7_10.4 = class_init (%.loc7_10.6) [concrete = constants.%Ordering.val.e86] // CHECK:STDOUT: %.loc7_10.8: init %Ordering = converted %.loc7_10.3, %.loc7_10.7 [concrete = constants.%Ordering.val.e86] // CHECK:STDOUT: %.loc7_10.9: ref %Ordering = temporary %.loc7_10.4, %.loc7_10.8 // CHECK:STDOUT: %.loc7_10.10: %Ordering = acquire_value %.loc7_10.9 // CHECK:STDOUT: %Greater: %Ordering = value_binding Greater, %.loc7_10.10 // CHECK:STDOUT: %int_3: Core.IntLiteral = int_value 3 [concrete = constants.%int_3.1ba] // CHECK:STDOUT: %impl.elem0.loc9_1.1: %.055 = impl_witness_access constants.%ImplicitAs.impl_witness.762, element0 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.0ed] // CHECK:STDOUT: %bound_method.loc9_1.1: = bound_method %int_3, %impl.elem0.loc9_1.1 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.bound.cdf] // CHECK:STDOUT: %specific_fn.loc9_1.1: = specific_function %impl.elem0.loc9_1.1, @Core.IntLiteral.as.ImplicitAs.impl.Convert(constants.%int_2.ecc) [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn] // CHECK:STDOUT: %bound_method.loc9_1.2: = bound_method %int_3, %specific_fn.loc9_1.1 [concrete = constants.%bound_method.898] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc9: init %u2 = call %bound_method.loc9_1.2(%int_3) [concrete = constants.%int_3.975] // CHECK:STDOUT: %.loc9_1.1: %u2 = value_of_initializer %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc9 [concrete = constants.%int_3.975] // CHECK:STDOUT: %.loc9_1.2: %u2 = converted %int_3, %.loc9_1.1 [concrete = constants.%int_3.975] // CHECK:STDOUT: %.loc9_1.3: %struct_type.discriminant = struct_literal (%.loc9_1.2) [concrete = constants.%struct.7bd] // CHECK:STDOUT: %impl.elem0.loc9_1.2: %.a82 = impl_witness_access constants.%Copy.impl_witness.a32, element0 [concrete = constants.%UInt.as.Copy.impl.Op.b2d] // CHECK:STDOUT: %bound_method.loc9_1.3: = bound_method %.loc9_1.2, %impl.elem0.loc9_1.2 [concrete = constants.%UInt.as.Copy.impl.Op.bound.7b5] // CHECK:STDOUT: %specific_fn.loc9_1.2: = specific_function %impl.elem0.loc9_1.2, @UInt.as.Copy.impl.Op(constants.%int_2.ecc) [concrete = constants.%UInt.as.Copy.impl.Op.specific_fn] // CHECK:STDOUT: %bound_method.loc9_1.4: = bound_method %.loc9_1.2, %specific_fn.loc9_1.2 [concrete = constants.%bound_method.824] // CHECK:STDOUT: %UInt.as.Copy.impl.Op.call.loc9: init %u2 = call %bound_method.loc9_1.4(%.loc9_1.2) [concrete = constants.%int_3.975] // CHECK:STDOUT: %.loc9_1.4: ref %Ordering = temporary_storage // CHECK:STDOUT: %.loc9_1.5: ref %u2 = class_element_access %.loc9_1.4, element0 // CHECK:STDOUT: %.loc9_1.6: init %u2 to %.loc9_1.5 = in_place_init %UInt.as.Copy.impl.Op.call.loc9 [concrete = constants.%int_3.975] // CHECK:STDOUT: %.loc9_1.7: init %Ordering to %.loc9_1.4 = class_init (%.loc9_1.6) [concrete = constants.%Ordering.val.a17] // CHECK:STDOUT: %.loc9_1.8: init %Ordering = converted %.loc9_1.3, %.loc9_1.7 [concrete = constants.%Ordering.val.a17] // CHECK:STDOUT: %.loc9_1.9: ref %Ordering = temporary %.loc9_1.4, %.loc9_1.8 // CHECK:STDOUT: %.loc9_1.10: %Ordering = acquire_value %.loc9_1.9 // CHECK:STDOUT: %Incomparable: %Ordering = value_binding Incomparable, %.loc9_1.10 // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%Ordering // CHECK:STDOUT: .Less = %Less // CHECK:STDOUT: .Equivalent = %Equivalent // CHECK:STDOUT: .Greater = %Greater // CHECK:STDOUT: .Incomparable = %Incomparable // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @__global_init() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %Ordering.ref.loc11: type = name_ref Ordering, file.%Ordering.decl [concrete = constants.%Ordering] // CHECK:STDOUT: %Less.ref: %Ordering = name_ref Less, @Ordering.%Less // CHECK:STDOUT: %Ordering.ref.loc12: type = name_ref Ordering, file.%Ordering.decl [concrete = constants.%Ordering] // CHECK:STDOUT: %Equivalent.ref: %Ordering = name_ref Equivalent, @Ordering.%Equivalent // CHECK:STDOUT: %Ordering.ref.loc13: type = name_ref Ordering, file.%Ordering.decl [concrete = constants.%Ordering] // CHECK:STDOUT: %Greater.ref: %Ordering = name_ref Greater, @Ordering.%Greater // CHECK:STDOUT: %Ordering.ref.loc14: type = name_ref Ordering, file.%Ordering.decl [concrete = constants.%Ordering] // CHECK:STDOUT: %Incomparable.ref: %Ordering = name_ref Incomparable, @Ordering.%Incomparable // CHECK:STDOUT: // CHECK:STDOUT: } // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/choice/generic.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/uint.carbon // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/choice/generic.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/choice/generic.carbon //@dump-sem-ir-begin choice Always(T:! type) { Sunny } //@dump-sem-ir-end // CHECK:STDOUT: --- generic.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %pattern_type: type = pattern_type type [concrete] // CHECK:STDOUT: %T: type = symbolic_binding T, 0 [symbolic] // CHECK:STDOUT: %Always.type: type = generic_class_type @Always [concrete] // CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] // CHECK:STDOUT: %Always.generic: %Always.type = struct_value () [concrete] // CHECK:STDOUT: %Always: type = class_type @Always, @Always(%T) [symbolic] // CHECK:STDOUT: %struct_type.discriminant: type = struct_type {.discriminant: %empty_tuple.type} [concrete] // CHECK:STDOUT: %complete_type: = complete_type_witness %struct_type.discriminant [concrete] // CHECK:STDOUT: %empty_tuple: %empty_tuple.type = tuple_value () [concrete] // CHECK:STDOUT: %struct: %struct_type.discriminant = struct_value (%empty_tuple) [concrete] // CHECK:STDOUT: %require_complete: = require_complete_type %Always [symbolic] // CHECK:STDOUT: %Always.val: %Always = struct_value (%empty_tuple) [symbolic] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: %Always.decl: %Always.type = class_decl @Always [concrete = constants.%Always.generic] { // CHECK:STDOUT: %T.patt: %pattern_type = symbolic_binding_pattern T, 0 [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %.loc14_19.1: type = splice_block %.loc14_19.2 [concrete = type] { // CHECK:STDOUT: // CHECK:STDOUT: %.loc14_19.2: type = type_literal type [concrete = type] // CHECK:STDOUT: } // CHECK:STDOUT: %T.loc14_15.2: type = symbolic_binding T, 0 [symbolic = %T.loc14_15.1 (constants.%T)] // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic class @Always(%T.loc14_15.2: type) { // CHECK:STDOUT: %T.loc14_15.1: type = symbolic_binding T, 0 [symbolic = %T.loc14_15.1 (constants.%T)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %Always: type = class_type @Always, @Always(%T.loc14_15.1) [symbolic = %Always (constants.%Always)] // CHECK:STDOUT: %require_complete: = require_complete_type %Always [symbolic = %require_complete (constants.%require_complete)] // CHECK:STDOUT: %Always.val: @Always.%Always (%Always) = struct_value (constants.%empty_tuple) [symbolic = %Always.val (constants.%Always.val)] // CHECK:STDOUT: // CHECK:STDOUT: class { // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%struct_type.discriminant [concrete = constants.%complete_type] // CHECK:STDOUT: %.loc16_1.1: %empty_tuple.type = tuple_literal () [concrete = constants.%empty_tuple] // CHECK:STDOUT: %empty_tuple: %empty_tuple.type = tuple_value () [concrete = constants.%empty_tuple] // CHECK:STDOUT: %.loc16_1.2: %empty_tuple.type = converted %.loc16_1.1, %empty_tuple [concrete = constants.%empty_tuple] // CHECK:STDOUT: %.loc16_1.3: %struct_type.discriminant = struct_literal (%.loc16_1.2) [concrete = constants.%struct] // CHECK:STDOUT: %.loc16_1.4: ref @Always.%Always (%Always) = temporary_storage // CHECK:STDOUT: %.loc16_1.5: ref %empty_tuple.type = class_element_access %.loc16_1.4, element0 // CHECK:STDOUT: %.loc16_1.6: init %empty_tuple.type = tuple_init () [concrete = constants.%empty_tuple] // CHECK:STDOUT: %.loc16_1.7: init %empty_tuple.type = converted %.loc16_1.2, %.loc16_1.6 [concrete = constants.%empty_tuple] // CHECK:STDOUT: %.loc16_1.8: init @Always.%Always (%Always) to %.loc16_1.4 = class_init (%.loc16_1.7) [symbolic = %Always.val (constants.%Always.val)] // CHECK:STDOUT: %.loc16_1.9: init @Always.%Always (%Always) = converted %.loc16_1.3, %.loc16_1.8 [symbolic = %Always.val (constants.%Always.val)] // CHECK:STDOUT: %.loc16_1.10: ref @Always.%Always (%Always) = temporary %.loc16_1.4, %.loc16_1.9 // CHECK:STDOUT: %.loc16_1.11: @Always.%Always (%Always) = acquire_value %.loc16_1.10 // CHECK:STDOUT: %Sunny: @Always.%Always (%Always) = value_binding Sunny, %.loc16_1.11 // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%Always // CHECK:STDOUT: .Sunny = %Sunny // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @Always(constants.%T) { // CHECK:STDOUT: %T.loc14_15.1 => constants.%T // CHECK:STDOUT: } // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/choice/params.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/uint.carbon // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/choice/params.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/choice/params.carbon // --- fail_todo_empty_params.carbon library "[[@TEST_NAME]]"; choice Always { // CHECK:STDERR: fail_todo_empty_params.carbon:[[@LINE+4]]:8: error: semantics TODO: `empty parameter list should make a member function` [SemanticsTodo] // CHECK:STDERR: Sunny() // CHECK:STDERR: ^~ // CHECK:STDERR: Sunny() } let mood: Always = Always.Sunny; // --- fail_todo_params.carbon library "[[@TEST_NAME]]"; choice C { Alt1, // CHECK:STDERR: fail_todo_params.carbon:[[@LINE+4]]:7: error: semantics TODO: `choice alternatives with parameters are not yet supported` [SemanticsTodo] // CHECK:STDERR: Alt2(a: i32, b: i64), // CHECK:STDERR: ^~~~~~~~~~~~~~~~ // CHECK:STDERR: Alt2(a: i32, b: i64), Alt3, } // --- fail_todo_generic_params.carbon library "[[@TEST_NAME]]"; choice C(T:! type) { // CHECK:STDERR: fail_todo_generic_params.carbon:[[@LINE+4]]:6: error: semantics TODO: `choice alternatives with parameters are not yet supported` [SemanticsTodo] // CHECK:STDERR: Alt(a: T) // CHECK:STDERR: ^~~~~~ // CHECK:STDERR: Alt(a: T) } // --- fail_todo_self_param.carbon library "[[@TEST_NAME]]"; choice C { // CHECK:STDERR: fail_todo_self_param.carbon:[[@LINE+4]]:6: error: semantics TODO: `choice alternatives with parameters are not yet supported` [SemanticsTodo] // CHECK:STDERR: Alt(a: Self*) // CHECK:STDERR: ^~~~~~~~~~ // CHECK:STDERR: Alt(a: Self*) } ================================================ FILE: toolchain/check/testdata/class/abstract/abstract.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/destroy.carbon // TODO: Add ranges and switch to "--dump-sem-ir-ranges=only". // EXTRA-ARGS: --dump-sem-ir-ranges=if-present // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/class/abstract/abstract.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/class/abstract/abstract.carbon // --- fail_abstract_field.carbon library "[[@TEST_NAME]]"; abstract class Abstract { } class Contains { // CHECK:STDERR: fail_abstract_field.carbon:[[@LINE+7]]:10: error: field has abstract type `Abstract` [AbstractTypeInFieldDecl] // CHECK:STDERR: var a: Abstract; // CHECK:STDERR: ^~~~~~~~ // CHECK:STDERR: fail_abstract_field.carbon:[[@LINE-7]]:1: note: class was declared abstract here [ClassAbstractHere] // CHECK:STDERR: abstract class Abstract { // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: var a: Abstract; } // --- fail_abstract_var.carbon library "[[@TEST_NAME]]"; abstract class Abstract { } fn Var() { // CHECK:STDERR: fail_abstract_var.carbon:[[@LINE+7]]:17: error: binding pattern has abstract type `Abstract` in `var` pattern [AbstractTypeInVarPattern] // CHECK:STDERR: var unused v: Abstract; // CHECK:STDERR: ^~~~~~~~ // CHECK:STDERR: fail_abstract_var.carbon:[[@LINE-7]]:1: note: class was declared abstract here [ClassAbstractHere] // CHECK:STDERR: abstract class Abstract { // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: var unused v: Abstract; } // --- fail_abstract_var_function_param.carbon library "[[@TEST_NAME]]"; abstract class Abstract { } // CHECK:STDERR: fail_abstract_var_function_param.carbon:[[@LINE+7]]:13: error: binding pattern has abstract type `Abstract` in `var` pattern [AbstractTypeInVarPattern] // CHECK:STDERR: fn F(var _: Abstract) { // CHECK:STDERR: ^~~~~~~~ // CHECK:STDERR: fail_abstract_var_function_param.carbon:[[@LINE-6]]:1: note: class was declared abstract here [ClassAbstractHere] // CHECK:STDERR: abstract class Abstract { // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: fn F(var _: Abstract) { } // --- abstract_let.carbon library "[[@TEST_NAME]]"; abstract class Abstract { } fn F(a: Abstract) { let unused l: Abstract = a; } // --- fail_abstract_adapter.carbon library "[[@TEST_NAME]]"; abstract class Abstract { } class Adapter { // TODO(#4387): This should probably be valid // CHECK:STDERR: fail_abstract_adapter.carbon:[[@LINE+7]]:3: error: adapted type `Abstract` is an abstract type [AbstractTypeInAdaptDecl] // CHECK:STDERR: adapt Abstract; // CHECK:STDERR: ^~~~~~~~~~~~~~~ // CHECK:STDERR: fail_abstract_adapter.carbon:[[@LINE-8]]:1: note: class was declared abstract here [ClassAbstractHere] // CHECK:STDERR: abstract class Abstract { // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: adapt Abstract; } // --- define_and_call_abstract_param.carbon library "[[@TEST_NAME]]"; abstract class Abstract { } fn Param(a: Abstract); fn Call(p: Abstract) { Param(p); } // --- return_nonabstract_derived.carbon library "[[@TEST_NAME]]"; abstract class Abstract { } class Derived { extend base: Abstract; var d: {}; } fn Make() -> Derived { return {.base = {}, .d = {}}; } // --- fail_return_abstract.carbon library "[[@TEST_NAME]]"; abstract class Abstract { } class Derived { extend base: Abstract; var d: {}; } // CHECK:STDERR: fail_return_abstract.carbon:[[@LINE+7]]:27: error: function returns abstract type `Abstract` [AbstractTypeInFunctionReturnType] // CHECK:STDERR: fn Return(a: Abstract) -> Abstract { // CHECK:STDERR: ^~~~~~~~ // CHECK:STDERR: fail_return_abstract.carbon:[[@LINE-12]]:1: note: class was declared abstract here [ClassAbstractHere] // CHECK:STDERR: abstract class Abstract { // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: fn Return(a: Abstract) -> Abstract { return a; } // --- fail_todo_access_abstract_subobject.carbon library "[[@TEST_NAME]]"; abstract class Abstract { var a: {}; } class Derived { extend base: Abstract; var d: {}; } fn Access(d: Derived) -> {} { // CHECK:STDERR: fail_todo_access_abstract_subobject.carbon:[[@LINE+7]]:10: error: initialization of abstract type `Abstract` [AbstractTypeInInit] // CHECK:STDERR: return d.base.a; // CHECK:STDERR: ^~~~~~ // CHECK:STDERR: fail_todo_access_abstract_subobject.carbon:[[@LINE-14]]:1: note: class was declared abstract here [ClassAbstractHere] // CHECK:STDERR: abstract class Abstract { // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: return d.base.a; } // --- fail_abstract_let_temporary_struct_literal.carbon library "[[@TEST_NAME]]"; abstract class Abstract { } fn F() { // CHECK:STDERR: fail_abstract_let_temporary_struct_literal.carbon:[[@LINE+7]]:28: error: initialization of abstract type `Abstract` [AbstractTypeInInit] // CHECK:STDERR: let unused l: Abstract = {}; // CHECK:STDERR: ^~ // CHECK:STDERR: fail_abstract_let_temporary_struct_literal.carbon:[[@LINE-7]]:1: note: class was declared abstract here [ClassAbstractHere] // CHECK:STDERR: abstract class Abstract { // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: let unused l: Abstract = {}; } // --- fail_todo_abstract_let_temporary.carbon library "[[@TEST_NAME]]"; abstract class Abstract { } class Derived { extend base: Abstract; } fn F() { // TODO: We should be able to construct a temporary `Derived`, and assign it // to the `Abstract` value since `Abstract` and `Derived` have pointer value // representations. // // CHECK:STDERR: fail_todo_abstract_let_temporary.carbon:[[@LINE+7]]:28: error: initialization of abstract type `Abstract` [AbstractTypeInInit] // CHECK:STDERR: let unused l: Abstract = {.base = {}} as Derived; // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: fail_todo_abstract_let_temporary.carbon:[[@LINE-15]]:1: note: class was declared abstract here [ClassAbstractHere] // CHECK:STDERR: abstract class Abstract { // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: let unused l: Abstract = {.base = {}} as Derived; } // --- fail_call_abstract_return.carbon library "[[@TEST_NAME]]"; abstract class Abstract { } fn ReturnAbstract() -> Abstract; fn CallReturnAbstract() { // CHECK:STDERR: fail_call_abstract_return.carbon:[[@LINE+10]]:3: error: function returns abstract type `Abstract` [AbstractTypeInFunctionReturnType] // CHECK:STDERR: ReturnAbstract(); // CHECK:STDERR: ^~~~~~~~~~~~~~~~ // CHECK:STDERR: fail_call_abstract_return.carbon:[[@LINE-9]]:1: note: class was declared abstract here [ClassAbstractHere] // CHECK:STDERR: abstract class Abstract { // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: fail_call_abstract_return.carbon:[[@LINE-9]]:24: note: return type declared here [IncompleteReturnTypeHere] // CHECK:STDERR: fn ReturnAbstract() -> Abstract; // CHECK:STDERR: ^~~~~~~~ // CHECK:STDERR: ReturnAbstract(); } // CHECK:STDOUT: --- fail_abstract_field.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %Abstract: type = class_type @Abstract [concrete] // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %complete_type: = complete_type_witness %empty_struct_type [concrete] // CHECK:STDOUT: %Contains: type = class_type @Contains [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .Abstract = %Abstract.decl // CHECK:STDOUT: .Contains = %Contains.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %Abstract.decl: type = class_decl @Abstract [concrete = constants.%Abstract] {} {} // CHECK:STDOUT: %Contains.decl: type = class_decl @Contains [concrete = constants.%Contains] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @Abstract { // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%Abstract // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @Contains { // CHECK:STDOUT: %Abstract.ref: type = name_ref Abstract, file.%Abstract.decl [concrete = constants.%Abstract] // CHECK:STDOUT: %.loc14: = field_decl a, element0 [concrete] // CHECK:STDOUT: %complete_type: = complete_type_witness [concrete = ] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%Contains // CHECK:STDOUT: .Abstract = // CHECK:STDOUT: .a = %.loc14 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- fail_abstract_var.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %Abstract: type = class_type @Abstract [concrete] // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %complete_type: = complete_type_witness %empty_struct_type [concrete] // CHECK:STDOUT: %Var.type: type = fn_type @Var [concrete] // CHECK:STDOUT: %Var: %Var.type = struct_value () [concrete] // CHECK:STDOUT: %Destroy.type: type = facet_type <@Destroy> [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: .Destroy = %Core.Destroy // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: %Core.Destroy: type = import_ref Core//prelude/parts/destroy, Destroy, loaded [concrete = constants.%Destroy.type] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .Abstract = %Abstract.decl // CHECK:STDOUT: .Var = %Var.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %Abstract.decl: type = class_decl @Abstract [concrete = constants.%Abstract] {} {} // CHECK:STDOUT: %Var.decl: %Var.type = fn_decl @Var [concrete = constants.%Var] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @Abstract { // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%Abstract // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @Var() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %v.patt: = ref_binding_pattern v [concrete] // CHECK:STDOUT: %v.var_patt: = var_pattern %v.patt [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %v.var: ref = var %v.var_patt [concrete = ] // CHECK:STDOUT: assign %v.var, // CHECK:STDOUT: %Abstract.ref: type = name_ref Abstract, file.%Abstract.decl [concrete = constants.%Abstract] // CHECK:STDOUT: %v: ref = ref_binding v, [concrete = ] // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- fail_abstract_var_function_param.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %Abstract: type = class_type @Abstract [concrete] // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %complete_type: = complete_type_witness %empty_struct_type [concrete] // CHECK:STDOUT: %F.type: type = fn_type @F [concrete] // CHECK:STDOUT: %F: %F.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .Abstract = %Abstract.decl // CHECK:STDOUT: .F = %F.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %Abstract.decl: type = class_decl @Abstract [concrete = constants.%Abstract] {} {} // CHECK:STDOUT: %F.decl: %F.type = fn_decl @F [concrete = constants.%F] { // CHECK:STDOUT: %_.patt: = ref_binding_pattern _ [concrete] // CHECK:STDOUT: %_.param_patt: = var_param_pattern %_.patt [concrete] // CHECK:STDOUT: %_.var_patt: = var_pattern %_.param_patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %_.param: ref = ref_param call_param0 // CHECK:STDOUT: %Abstract.ref: type = name_ref Abstract, file.%Abstract.decl [concrete = constants.%Abstract] // CHECK:STDOUT: %_: ref = ref_binding _, %_.param // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @Abstract { // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%Abstract // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @F(%_.param: ref ) { // CHECK:STDOUT: !entry: // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- abstract_let.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %Abstract: type = class_type @Abstract [concrete] // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %complete_type: = complete_type_witness %empty_struct_type [concrete] // CHECK:STDOUT: %pattern_type: type = pattern_type %Abstract [concrete] // CHECK:STDOUT: %F.type: type = fn_type @F [concrete] // CHECK:STDOUT: %F: %F.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .Abstract = %Abstract.decl // CHECK:STDOUT: .F = %F.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %Abstract.decl: type = class_decl @Abstract [concrete = constants.%Abstract] {} {} // CHECK:STDOUT: %F.decl: %F.type = fn_decl @F [concrete = constants.%F] { // CHECK:STDOUT: %a.patt: %pattern_type = value_binding_pattern a [concrete] // CHECK:STDOUT: %a.param_patt: %pattern_type = value_param_pattern %a.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %a.param: %Abstract = value_param call_param0 // CHECK:STDOUT: %Abstract.ref.loc6: type = name_ref Abstract, file.%Abstract.decl [concrete = constants.%Abstract] // CHECK:STDOUT: %a: %Abstract = value_binding a, %a.param // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @Abstract { // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%Abstract // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @F(%a.param: %Abstract) { // CHECK:STDOUT: !entry: // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %l.patt: %pattern_type = value_binding_pattern l [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %a.ref: %Abstract = name_ref a, %a // CHECK:STDOUT: %Abstract.ref.loc7: type = name_ref Abstract, file.%Abstract.decl [concrete = constants.%Abstract] // CHECK:STDOUT: %l: %Abstract = value_binding l, %a.ref // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- fail_abstract_adapter.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %Abstract: type = class_type @Abstract [concrete] // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %complete_type: = complete_type_witness %empty_struct_type [concrete] // CHECK:STDOUT: %Adapter: type = class_type @Adapter [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .Abstract = %Abstract.decl // CHECK:STDOUT: .Adapter = %Adapter.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %Abstract.decl: type = class_decl @Abstract [concrete = constants.%Abstract] {} {} // CHECK:STDOUT: %Adapter.decl: type = class_decl @Adapter [concrete = constants.%Adapter] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @Abstract { // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%Abstract // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @Adapter { // CHECK:STDOUT: %Abstract.ref: type = name_ref Abstract, file.%Abstract.decl [concrete = constants.%Abstract] // CHECK:STDOUT: adapt_decl [concrete] // CHECK:STDOUT: %complete_type: = complete_type_witness [concrete = ] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%Adapter // CHECK:STDOUT: .Abstract = // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- define_and_call_abstract_param.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %Abstract: type = class_type @Abstract [concrete] // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %complete_type: = complete_type_witness %empty_struct_type [concrete] // CHECK:STDOUT: %pattern_type: type = pattern_type %Abstract [concrete] // CHECK:STDOUT: %Param.type: type = fn_type @Param [concrete] // CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] // CHECK:STDOUT: %Param: %Param.type = struct_value () [concrete] // CHECK:STDOUT: %Call.type: type = fn_type @Call [concrete] // CHECK:STDOUT: %Call: %Call.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .Abstract = %Abstract.decl // CHECK:STDOUT: .Param = %Param.decl // CHECK:STDOUT: .Call = %Call.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %Abstract.decl: type = class_decl @Abstract [concrete = constants.%Abstract] {} {} // CHECK:STDOUT: %Param.decl: %Param.type = fn_decl @Param [concrete = constants.%Param] { // CHECK:STDOUT: %a.patt: %pattern_type = value_binding_pattern a [concrete] // CHECK:STDOUT: %a.param_patt: %pattern_type = value_param_pattern %a.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %a.param: %Abstract = value_param call_param0 // CHECK:STDOUT: %Abstract.ref: type = name_ref Abstract, file.%Abstract.decl [concrete = constants.%Abstract] // CHECK:STDOUT: %a: %Abstract = value_binding a, %a.param // CHECK:STDOUT: } // CHECK:STDOUT: %Call.decl: %Call.type = fn_decl @Call [concrete = constants.%Call] { // CHECK:STDOUT: %p.patt: %pattern_type = value_binding_pattern p [concrete] // CHECK:STDOUT: %p.param_patt: %pattern_type = value_param_pattern %p.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %p.param: %Abstract = value_param call_param0 // CHECK:STDOUT: %Abstract.ref: type = name_ref Abstract, file.%Abstract.decl [concrete = constants.%Abstract] // CHECK:STDOUT: %p: %Abstract = value_binding p, %p.param // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @Abstract { // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%Abstract // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @Param(%a.param: %Abstract); // CHECK:STDOUT: // CHECK:STDOUT: fn @Call(%p.param: %Abstract) { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %Param.ref: %Param.type = name_ref Param, file.%Param.decl [concrete = constants.%Param] // CHECK:STDOUT: %p.ref: %Abstract = name_ref p, %p // CHECK:STDOUT: %Param.call: init %empty_tuple.type = call %Param.ref(%p.ref) // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- return_nonabstract_derived.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %Abstract: type = class_type @Abstract [concrete] // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %complete_type.357: = complete_type_witness %empty_struct_type [concrete] // CHECK:STDOUT: %Derived: type = class_type @Derived [concrete] // CHECK:STDOUT: %Derived.elem.032: type = unbound_element_type %Derived, %Abstract [concrete] // CHECK:STDOUT: %empty_struct.a40: %empty_struct_type = struct_value () [concrete] // CHECK:STDOUT: %Derived.elem.87e: type = unbound_element_type %Derived, %empty_struct_type [concrete] // CHECK:STDOUT: %struct_type.base.d.be5: type = struct_type {.base: %Abstract, .d: %empty_struct_type} [concrete] // CHECK:STDOUT: %complete_type.840: = complete_type_witness %struct_type.base.d.be5 [concrete] // CHECK:STDOUT: %.d9b: Core.Form = init_form %Derived [concrete] // CHECK:STDOUT: %pattern_type: type = pattern_type %Derived [concrete] // CHECK:STDOUT: %Make.type: type = fn_type @Make [concrete] // CHECK:STDOUT: %Make: %Make.type = struct_value () [concrete] // CHECK:STDOUT: %struct_type.base.d.e0f: type = struct_type {.base: %empty_struct_type, .d: %empty_struct_type} [concrete] // CHECK:STDOUT: %struct: %struct_type.base.d.e0f = struct_value (%empty_struct.a40, %empty_struct.a40) [concrete] // CHECK:STDOUT: %.ec6: type = partial_type %Abstract [concrete] // CHECK:STDOUT: %empty_struct.8eb: %.ec6 = struct_value () [concrete] // CHECK:STDOUT: %Abstract.val: %Abstract = struct_value () [concrete] // CHECK:STDOUT: %Derived.val: %Derived = struct_value (%Abstract.val, %empty_struct.a40) [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .Abstract = %Abstract.decl // CHECK:STDOUT: .Derived = %Derived.decl // CHECK:STDOUT: .Make = %Make.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %Abstract.decl: type = class_decl @Abstract [concrete = constants.%Abstract] {} {} // CHECK:STDOUT: %Derived.decl: type = class_decl @Derived [concrete = constants.%Derived] {} {} // CHECK:STDOUT: %Make.decl: %Make.type = fn_decl @Make [concrete = constants.%Make] { // CHECK:STDOUT: %return.patt: %pattern_type = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: %pattern_type = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %Derived.ref: type = name_ref Derived, file.%Derived.decl [concrete = constants.%Derived] // CHECK:STDOUT: %.loc12: Core.Form = init_form %Derived.ref [concrete = constants.%.d9b] // CHECK:STDOUT: %return.param: ref %Derived = out_param call_param0 // CHECK:STDOUT: %return: ref %Derived = return_slot %return.param // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @Abstract { // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type.357] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%Abstract // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @Derived { // CHECK:STDOUT: %Abstract.ref: type = name_ref Abstract, file.%Abstract.decl [concrete = constants.%Abstract] // CHECK:STDOUT: %.loc7: %Derived.elem.032 = base_decl %Abstract.ref, element0 [concrete] // CHECK:STDOUT: %.loc9_11.1: %empty_struct_type = struct_literal () [concrete = constants.%empty_struct.a40] // CHECK:STDOUT: %.loc9_11.2: type = converted %.loc9_11.1, constants.%empty_struct_type [concrete = constants.%empty_struct_type] // CHECK:STDOUT: %.loc9_8: %Derived.elem.87e = field_decl d, element1 [concrete] // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%struct_type.base.d.be5 [concrete = constants.%complete_type.840] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%Derived // CHECK:STDOUT: .Abstract = // CHECK:STDOUT: .base = %.loc7 // CHECK:STDOUT: .d = %.loc9_8 // CHECK:STDOUT: extend %Abstract.ref // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @Make() -> out %return.param: %Derived { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %.loc13_20.1: %empty_struct_type = struct_literal () [concrete = constants.%empty_struct.a40] // CHECK:STDOUT: %.loc13_29.1: %empty_struct_type = struct_literal () [concrete = constants.%empty_struct.a40] // CHECK:STDOUT: %.loc13_30.1: %struct_type.base.d.e0f = struct_literal (%.loc13_20.1, %.loc13_29.1) [concrete = constants.%struct] // CHECK:STDOUT: %.loc13_30.2: ref %.ec6 = class_element_access %return.param, element0 // CHECK:STDOUT: %.loc13_20.2: init %.ec6 to %.loc13_30.2 = class_init () [concrete = constants.%empty_struct.8eb] // CHECK:STDOUT: %.loc13_30.3: init %.ec6 = converted %.loc13_20.1, %.loc13_20.2 [concrete = constants.%empty_struct.8eb] // CHECK:STDOUT: %.loc13_30.4: init %Abstract = as_compatible %.loc13_30.3 [concrete = constants.%Abstract.val] // CHECK:STDOUT: %.loc13_30.5: ref %empty_struct_type = class_element_access %return.param, element1 // CHECK:STDOUT: %.loc13_29.2: init %empty_struct_type = struct_init () [concrete = constants.%empty_struct.a40] // CHECK:STDOUT: %.loc13_30.6: init %empty_struct_type = converted %.loc13_29.1, %.loc13_29.2 [concrete = constants.%empty_struct.a40] // CHECK:STDOUT: %.loc13_30.7: init %Derived to %return.param = class_init (%.loc13_30.4, %.loc13_30.6) [concrete = constants.%Derived.val] // CHECK:STDOUT: %.loc13_31: init %Derived = converted %.loc13_30.1, %.loc13_30.7 [concrete = constants.%Derived.val] // CHECK:STDOUT: return %.loc13_31 to %return.param // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- fail_return_abstract.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %Abstract: type = class_type @Abstract [concrete] // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %complete_type.357: = complete_type_witness %empty_struct_type [concrete] // CHECK:STDOUT: %Derived: type = class_type @Derived [concrete] // CHECK:STDOUT: %Derived.elem.032: type = unbound_element_type %Derived, %Abstract [concrete] // CHECK:STDOUT: %empty_struct: %empty_struct_type = struct_value () [concrete] // CHECK:STDOUT: %Derived.elem.87e: type = unbound_element_type %Derived, %empty_struct_type [concrete] // CHECK:STDOUT: %struct_type.base.d: type = struct_type {.base: %Abstract, .d: %empty_struct_type} [concrete] // CHECK:STDOUT: %complete_type.840: = complete_type_witness %struct_type.base.d [concrete] // CHECK:STDOUT: %pattern_type: type = pattern_type %Abstract [concrete] // CHECK:STDOUT: %.7d7: Core.Form = init_form %Abstract [concrete] // CHECK:STDOUT: %Return.type: type = fn_type @Return [concrete] // CHECK:STDOUT: %Return: %Return.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .Abstract = %Abstract.decl // CHECK:STDOUT: .Derived = %Derived.decl // CHECK:STDOUT: .Return = %Return.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %Abstract.decl: type = class_decl @Abstract [concrete = constants.%Abstract] {} {} // CHECK:STDOUT: %Derived.decl: type = class_decl @Derived [concrete = constants.%Derived] {} {} // CHECK:STDOUT: %Return.decl: %Return.type = fn_decl @Return [concrete = constants.%Return] { // CHECK:STDOUT: %a.patt: %pattern_type = value_binding_pattern a [concrete] // CHECK:STDOUT: %a.param_patt: %pattern_type = value_param_pattern %a.patt [concrete] // CHECK:STDOUT: %return.patt: %pattern_type = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: %pattern_type = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %Abstract.ref.loc19_27: type = name_ref Abstract, file.%Abstract.decl [concrete = constants.%Abstract] // CHECK:STDOUT: %.loc19: Core.Form = init_form %Abstract.ref.loc19_27 [concrete = constants.%.7d7] // CHECK:STDOUT: %a.param: %Abstract = value_param call_param0 // CHECK:STDOUT: %Abstract.ref.loc19_14: type = name_ref Abstract, file.%Abstract.decl [concrete = constants.%Abstract] // CHECK:STDOUT: %a: %Abstract = value_binding a, %a.param // CHECK:STDOUT: %return.param: ref %Abstract = out_param call_param1 // CHECK:STDOUT: %return: ref %Abstract = return_slot %return.param // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @Abstract { // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type.357] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%Abstract // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @Derived { // CHECK:STDOUT: %Abstract.ref: type = name_ref Abstract, file.%Abstract.decl [concrete = constants.%Abstract] // CHECK:STDOUT: %.loc7: %Derived.elem.032 = base_decl %Abstract.ref, element0 [concrete] // CHECK:STDOUT: %.loc9_11.1: %empty_struct_type = struct_literal () [concrete = constants.%empty_struct] // CHECK:STDOUT: %.loc9_11.2: type = converted %.loc9_11.1, constants.%empty_struct_type [concrete = constants.%empty_struct_type] // CHECK:STDOUT: %.loc9_8: %Derived.elem.87e = field_decl d, element1 [concrete] // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%struct_type.base.d [concrete = constants.%complete_type.840] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%Derived // CHECK:STDOUT: .Abstract = // CHECK:STDOUT: .base = %.loc7 // CHECK:STDOUT: .d = %.loc9_8 // CHECK:STDOUT: extend %Abstract.ref // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @Return(%a.param: %Abstract) -> out %return.param: %Abstract { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %a.ref: %Abstract = name_ref a, %a // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- fail_todo_access_abstract_subobject.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %Abstract: type = class_type @Abstract [concrete] // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %empty_struct: %empty_struct_type = struct_value () [concrete] // CHECK:STDOUT: %Abstract.elem: type = unbound_element_type %Abstract, %empty_struct_type [concrete] // CHECK:STDOUT: %struct_type.a.225: type = struct_type {.a: %empty_struct_type} [concrete] // CHECK:STDOUT: %complete_type.8c6: = complete_type_witness %struct_type.a.225 [concrete] // CHECK:STDOUT: %Derived: type = class_type @Derived [concrete] // CHECK:STDOUT: %Derived.elem.032: type = unbound_element_type %Derived, %Abstract [concrete] // CHECK:STDOUT: %Derived.elem.87e: type = unbound_element_type %Derived, %empty_struct_type [concrete] // CHECK:STDOUT: %struct_type.base.d.be5: type = struct_type {.base: %Abstract, .d: %empty_struct_type} [concrete] // CHECK:STDOUT: %complete_type.840: = complete_type_witness %struct_type.base.d.be5 [concrete] // CHECK:STDOUT: %pattern_type.9f6: type = pattern_type %Derived [concrete] // CHECK:STDOUT: %.469: Core.Form = init_form %empty_struct_type [concrete] // CHECK:STDOUT: %pattern_type.a96: type = pattern_type %empty_struct_type [concrete] // CHECK:STDOUT: %Access.type: type = fn_type @Access [concrete] // CHECK:STDOUT: %Access: %Access.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .Abstract = %Abstract.decl // CHECK:STDOUT: .Derived = %Derived.decl // CHECK:STDOUT: .Access = %Access.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %Abstract.decl: type = class_decl @Abstract [concrete = constants.%Abstract] {} {} // CHECK:STDOUT: %Derived.decl: type = class_decl @Derived [concrete = constants.%Derived] {} {} // CHECK:STDOUT: %Access.decl: %Access.type = fn_decl @Access [concrete = constants.%Access] { // CHECK:STDOUT: %d.patt: %pattern_type.9f6 = value_binding_pattern d [concrete] // CHECK:STDOUT: %d.param_patt: %pattern_type.9f6 = value_param_pattern %d.patt [concrete] // CHECK:STDOUT: %return.patt: %pattern_type.a96 = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: %pattern_type.a96 = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %.loc13_27.1: %empty_struct_type = struct_literal () [concrete = constants.%empty_struct] // CHECK:STDOUT: %.loc13_27.2: type = converted %.loc13_27.1, constants.%empty_struct_type [concrete = constants.%empty_struct_type] // CHECK:STDOUT: %.loc13_27.3: Core.Form = init_form %.loc13_27.2 [concrete = constants.%.469] // CHECK:STDOUT: %d.param: %Derived = value_param call_param0 // CHECK:STDOUT: %Derived.ref: type = name_ref Derived, file.%Derived.decl [concrete = constants.%Derived] // CHECK:STDOUT: %d: %Derived = value_binding d, %d.param // CHECK:STDOUT: %return.param: ref %empty_struct_type = out_param call_param1 // CHECK:STDOUT: %return: ref %empty_struct_type = return_slot %return.param // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @Abstract { // CHECK:STDOUT: %.loc4_11.1: %empty_struct_type = struct_literal () [concrete = constants.%empty_struct] // CHECK:STDOUT: %.loc4_11.2: type = converted %.loc4_11.1, constants.%empty_struct_type [concrete = constants.%empty_struct_type] // CHECK:STDOUT: %.loc4_8: %Abstract.elem = field_decl a, element0 [concrete] // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%struct_type.a.225 [concrete = constants.%complete_type.8c6] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%Abstract // CHECK:STDOUT: .a = %.loc4_8 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @Derived { // CHECK:STDOUT: %Abstract.ref: type = name_ref Abstract, file.%Abstract.decl [concrete = constants.%Abstract] // CHECK:STDOUT: %.loc8: %Derived.elem.032 = base_decl %Abstract.ref, element0 [concrete] // CHECK:STDOUT: %.loc10_11.1: %empty_struct_type = struct_literal () [concrete = constants.%empty_struct] // CHECK:STDOUT: %.loc10_11.2: type = converted %.loc10_11.1, constants.%empty_struct_type [concrete = constants.%empty_struct_type] // CHECK:STDOUT: %.loc10_8: %Derived.elem.87e = field_decl d, element1 [concrete] // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%struct_type.base.d.be5 [concrete = constants.%complete_type.840] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%Derived // CHECK:STDOUT: .Abstract = // CHECK:STDOUT: .base = %.loc8 // CHECK:STDOUT: .d = %.loc10_8 // CHECK:STDOUT: extend %Abstract.ref // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @Access(%d.param: %Derived) -> out %return.param: %empty_struct_type { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %d.ref: %Derived = name_ref d, %d // CHECK:STDOUT: %base.ref: %Derived.elem.032 = name_ref base, @Derived.%.loc8 [concrete = @Derived.%.loc8] // CHECK:STDOUT: %.loc21: ref %Abstract = class_element_access %d.ref, element0 // CHECK:STDOUT: %a.ref: = name_ref a, [concrete = ] // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- fail_abstract_let_temporary_struct_literal.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %Abstract: type = class_type @Abstract [concrete] // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %complete_type: = complete_type_witness %empty_struct_type [concrete] // CHECK:STDOUT: %F.type: type = fn_type @F [concrete] // CHECK:STDOUT: %F: %F.type = struct_value () [concrete] // CHECK:STDOUT: %pattern_type: type = pattern_type %Abstract [concrete] // CHECK:STDOUT: %empty_struct: %empty_struct_type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .Abstract = %Abstract.decl // CHECK:STDOUT: .F = %F.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %Abstract.decl: type = class_decl @Abstract [concrete = constants.%Abstract] {} {} // CHECK:STDOUT: %F.decl: %F.type = fn_decl @F [concrete = constants.%F] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @Abstract { // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%Abstract // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @F() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %l.patt: %pattern_type = value_binding_pattern l [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %.loc14: %empty_struct_type = struct_literal () [concrete = constants.%empty_struct] // CHECK:STDOUT: %Abstract.ref: type = name_ref Abstract, file.%Abstract.decl [concrete = constants.%Abstract] // CHECK:STDOUT: %l: %Abstract = value_binding l, [concrete = ] // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- fail_todo_abstract_let_temporary.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %Abstract: type = class_type @Abstract [concrete] // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %complete_type.357: = complete_type_witness %empty_struct_type [concrete] // CHECK:STDOUT: %Derived: type = class_type @Derived [concrete] // CHECK:STDOUT: %Derived.elem: type = unbound_element_type %Derived, %Abstract [concrete] // CHECK:STDOUT: %struct_type.base.709: type = struct_type {.base: %Abstract} [concrete] // CHECK:STDOUT: %complete_type.907: = complete_type_witness %struct_type.base.709 [concrete] // CHECK:STDOUT: %F.type: type = fn_type @F [concrete] // CHECK:STDOUT: %F: %F.type = struct_value () [concrete] // CHECK:STDOUT: %pattern_type: type = pattern_type %Abstract [concrete] // CHECK:STDOUT: %empty_struct.a40: %empty_struct_type = struct_value () [concrete] // CHECK:STDOUT: %struct_type.base.f5e: type = struct_type {.base: %empty_struct_type} [concrete] // CHECK:STDOUT: %struct: %struct_type.base.f5e = struct_value (%empty_struct.a40) [concrete] // CHECK:STDOUT: %.ec6: type = partial_type %Abstract [concrete] // CHECK:STDOUT: %empty_struct.8eb: %.ec6 = struct_value () [concrete] // CHECK:STDOUT: %Abstract.val: %Abstract = struct_value () [concrete] // CHECK:STDOUT: %Derived.val: %Derived = struct_value (%Abstract.val) [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .Abstract = %Abstract.decl // CHECK:STDOUT: .Derived = %Derived.decl // CHECK:STDOUT: .F = %F.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %Abstract.decl: type = class_decl @Abstract [concrete = constants.%Abstract] {} {} // CHECK:STDOUT: %Derived.decl: type = class_decl @Derived [concrete = constants.%Derived] {} {} // CHECK:STDOUT: %F.decl: %F.type = fn_decl @F [concrete = constants.%F] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @Abstract { // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type.357] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%Abstract // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @Derived { // CHECK:STDOUT: %Abstract.ref: type = name_ref Abstract, file.%Abstract.decl [concrete = constants.%Abstract] // CHECK:STDOUT: %.loc7: %Derived.elem = base_decl %Abstract.ref, element0 [concrete] // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%struct_type.base.709 [concrete = constants.%complete_type.907] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%Derived // CHECK:STDOUT: .Abstract = // CHECK:STDOUT: .base = %.loc7 // CHECK:STDOUT: extend %Abstract.ref // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @F() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %l.patt: %pattern_type = value_binding_pattern l [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %.loc22_38.1: %empty_struct_type = struct_literal () [concrete = constants.%empty_struct.a40] // CHECK:STDOUT: %.loc22_39.1: %struct_type.base.f5e = struct_literal (%.loc22_38.1) [concrete = constants.%struct] // CHECK:STDOUT: %Derived.ref: type = name_ref Derived, file.%Derived.decl [concrete = constants.%Derived] // CHECK:STDOUT: %.loc22_39.2: ref %Derived = temporary_storage // CHECK:STDOUT: %.loc22_39.3: ref %.ec6 = class_element_access %.loc22_39.2, element0 // CHECK:STDOUT: %.loc22_38.2: init %.ec6 to %.loc22_39.3 = class_init () [concrete = constants.%empty_struct.8eb] // CHECK:STDOUT: %.loc22_39.4: init %.ec6 = converted %.loc22_38.1, %.loc22_38.2 [concrete = constants.%empty_struct.8eb] // CHECK:STDOUT: %.loc22_39.5: init %Abstract = as_compatible %.loc22_39.4 [concrete = constants.%Abstract.val] // CHECK:STDOUT: %.loc22_39.6: init %Derived to %.loc22_39.2 = class_init (%.loc22_39.5) [concrete = constants.%Derived.val] // CHECK:STDOUT: %.loc22_41: init %Derived = converted %.loc22_39.1, %.loc22_39.6 [concrete = constants.%Derived.val] // CHECK:STDOUT: %Abstract.ref: type = name_ref Abstract, file.%Abstract.decl [concrete = constants.%Abstract] // CHECK:STDOUT: %l: %Abstract = value_binding l, [concrete = ] // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- fail_call_abstract_return.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %Abstract: type = class_type @Abstract [concrete] // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %complete_type: = complete_type_witness %empty_struct_type [concrete] // CHECK:STDOUT: %.7d7: Core.Form = init_form %Abstract [concrete] // CHECK:STDOUT: %pattern_type: type = pattern_type %Abstract [concrete] // CHECK:STDOUT: %ReturnAbstract.type: type = fn_type @ReturnAbstract [concrete] // CHECK:STDOUT: %ReturnAbstract: %ReturnAbstract.type = struct_value () [concrete] // CHECK:STDOUT: %CallReturnAbstract.type: type = fn_type @CallReturnAbstract [concrete] // CHECK:STDOUT: %CallReturnAbstract: %CallReturnAbstract.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .Abstract = %Abstract.decl // CHECK:STDOUT: .ReturnAbstract = %ReturnAbstract.decl // CHECK:STDOUT: .CallReturnAbstract = %CallReturnAbstract.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %Abstract.decl: type = class_decl @Abstract [concrete = constants.%Abstract] {} {} // CHECK:STDOUT: %ReturnAbstract.decl: %ReturnAbstract.type = fn_decl @ReturnAbstract [concrete = constants.%ReturnAbstract] { // CHECK:STDOUT: %return.patt: %pattern_type = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: %pattern_type = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %Abstract.ref: type = name_ref Abstract, file.%Abstract.decl [concrete = constants.%Abstract] // CHECK:STDOUT: %.loc6: Core.Form = init_form %Abstract.ref [concrete = constants.%.7d7] // CHECK:STDOUT: %return.param: ref %Abstract = out_param call_param0 // CHECK:STDOUT: %return: ref %Abstract = return_slot %return.param // CHECK:STDOUT: } // CHECK:STDOUT: %CallReturnAbstract.decl: %CallReturnAbstract.type = fn_decl @CallReturnAbstract [concrete = constants.%CallReturnAbstract] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @Abstract { // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%Abstract // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @ReturnAbstract() -> out %return.param: %Abstract; // CHECK:STDOUT: // CHECK:STDOUT: fn @CallReturnAbstract() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %ReturnAbstract.ref: %ReturnAbstract.type = name_ref ReturnAbstract, file.%ReturnAbstract.decl [concrete = constants.%ReturnAbstract] // CHECK:STDOUT: %ReturnAbstract.call: init = call %ReturnAbstract.ref() // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/class/abstract/fail_abstract_in_struct.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/none.carbon // TODO: Add ranges and switch to "--dump-sem-ir-ranges=only". // EXTRA-ARGS: --dump-sem-ir-ranges=if-present // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/class/abstract/fail_abstract_in_struct.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/class/abstract/fail_abstract_in_struct.carbon // --- fail_abstract_field.carbon library "[[@TEST_NAME]]"; abstract class Abstract1 {} class Contains { // CHECK:STDERR: fail_abstract_field.carbon:[[@LINE+7]]:10: error: field has abstract type `{.m1: Abstract1}` [AbstractTypeInFieldDecl] // CHECK:STDERR: var a: {.m1: Abstract1}; // CHECK:STDERR: ^~~~~~~~~~~~~~~~ // CHECK:STDERR: fail_abstract_field.carbon:[[@LINE-6]]:1: note: uses class that was declared abstract here [ClassAbstractHere] // CHECK:STDERR: abstract class Abstract1 {} // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: var a: {.m1: Abstract1}; } // --- fail_abstract_var.carbon library "[[@TEST_NAME]]"; abstract class Abstract2 {} // CHECK:STDERR: fail_abstract_var.carbon:[[@LINE+7]]:8: error: binding pattern has abstract type `{.m2: Abstract2}` in `var` pattern [AbstractTypeInVarPattern] // CHECK:STDERR: var v: {.m2: Abstract2}; // CHECK:STDERR: ^~~~~~~~~~~~~~~~ // CHECK:STDERR: fail_abstract_var.carbon:[[@LINE-5]]:1: note: uses class that was declared abstract here [ClassAbstractHere] // CHECK:STDERR: abstract class Abstract2 {} // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: var v: {.m2: Abstract2}; // --- fail_todo_abstract_let.carbon library "[[@TEST_NAME]]"; abstract class Abstract3 { } fn F(a: Abstract3) { // CHECK:STDERR: fail_todo_abstract_let.carbon:[[@LINE+7]]:36: error: initialization of abstract type `{.m3: Abstract3}` [AbstractTypeInInit] // CHECK:STDERR: let unused l: {.m3: Abstract3} = {.m3 = a}; // CHECK:STDERR: ^~~~~~~~~ // CHECK:STDERR: fail_todo_abstract_let.carbon:[[@LINE-7]]:1: note: uses class that was declared abstract here [ClassAbstractHere] // CHECK:STDERR: abstract class Abstract3 { // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: let unused l: {.m3: Abstract3} = {.m3 = a}; } // --- fail_abstract_twice.carbon library "[[@TEST_NAME]]"; abstract class Abstract4 {} abstract class Abstract5 {} // CHECK:STDERR: fail_abstract_twice.carbon:[[@LINE+7]]:9: error: binding pattern has abstract type `{.m4: Abstract4, .m5: Abstract5}` in `var` pattern [AbstractTypeInVarPattern] // CHECK:STDERR: var v2: {.m4: Abstract4, .m5: Abstract5}; // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: fail_abstract_twice.carbon:[[@LINE-6]]:1: note: uses class that was declared abstract here [ClassAbstractHere] // CHECK:STDERR: abstract class Abstract4 {} // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: var v2: {.m4: Abstract4, .m5: Abstract5}; // --- fail_abstract_first.carbon library "[[@TEST_NAME]]"; abstract class Abstract6 {} // CHECK:STDERR: fail_abstract_first.carbon:[[@LINE+7]]:9: error: binding pattern has abstract type `{.m6: Abstract6, .c1: ()}` in `var` pattern [AbstractTypeInVarPattern] // CHECK:STDERR: var v3: {.m6: Abstract6, .c1: ()}; // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: fail_abstract_first.carbon:[[@LINE-5]]:1: note: uses class that was declared abstract here [ClassAbstractHere] // CHECK:STDERR: abstract class Abstract6 {} // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: var v3: {.m6: Abstract6, .c1: ()}; // --- fail_abstract_second.carbon library "[[@TEST_NAME]]"; abstract class Abstract7 {} // CHECK:STDERR: fail_abstract_second.carbon:[[@LINE+7]]:9: error: binding pattern has abstract type `{.c2: (), .m7: Abstract7}` in `var` pattern [AbstractTypeInVarPattern] // CHECK:STDERR: var v4: {.c2: (), .m7: Abstract7}; // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: fail_abstract_second.carbon:[[@LINE-5]]:1: note: uses class that was declared abstract here [ClassAbstractHere] // CHECK:STDERR: abstract class Abstract7 {} // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: var v4: {.c2: (), .m7: Abstract7}; // --- lib.carbon library "[[@TEST_NAME]]"; abstract class Abstract {} // --- fail_import.carbon library "[[@TEST_NAME]]"; import library "lib"; // CHECK:STDERR: fail_import.carbon:[[@LINE+8]]:9: error: binding pattern has abstract type `{.m: Abstract}` in `var` pattern [AbstractTypeInVarPattern] // CHECK:STDERR: var v5: {.m: Abstract}; // CHECK:STDERR: ^~~~~~~~~~~~~~ // CHECK:STDERR: fail_import.carbon:[[@LINE-5]]:1: in import [InImport] // CHECK:STDERR: lib.carbon:3:1: note: uses class that was declared abstract here [ClassAbstractHere] // CHECK:STDERR: abstract class Abstract {} // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: var v5: {.m: Abstract}; // CHECK:STDOUT: --- fail_abstract_field.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %Abstract1: type = class_type @Abstract1 [concrete] // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %complete_type: = complete_type_witness %empty_struct_type [concrete] // CHECK:STDOUT: %Contains: type = class_type @Contains [concrete] // CHECK:STDOUT: %struct_type.m1.ea7: type = struct_type {.m1: %Abstract1} [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Abstract1 = %Abstract1.decl // CHECK:STDOUT: .Contains = %Contains.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Abstract1.decl: type = class_decl @Abstract1 [concrete = constants.%Abstract1] {} {} // CHECK:STDOUT: %Contains.decl: type = class_decl @Contains [concrete = constants.%Contains] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @Abstract1 { // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%Abstract1 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @Contains { // CHECK:STDOUT: %Abstract1.ref: type = name_ref Abstract1, file.%Abstract1.decl [concrete = constants.%Abstract1] // CHECK:STDOUT: %struct_type.m1: type = struct_type {.m1: %Abstract1} [concrete = constants.%struct_type.m1.ea7] // CHECK:STDOUT: %.loc13: = field_decl a, element0 [concrete] // CHECK:STDOUT: %complete_type: = complete_type_witness [concrete = ] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%Contains // CHECK:STDOUT: .Abstract1 = // CHECK:STDOUT: .a = %.loc13 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- fail_abstract_var.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %Abstract2: type = class_type @Abstract2 [concrete] // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %complete_type: = complete_type_witness %empty_struct_type [concrete] // CHECK:STDOUT: %struct_type.m2.779: type = struct_type {.m2: %Abstract2} [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Abstract2 = %Abstract2.decl // CHECK:STDOUT: .v = %v // CHECK:STDOUT: } // CHECK:STDOUT: %Abstract2.decl: type = class_decl @Abstract2 [concrete = constants.%Abstract2] {} {} // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %v.patt: = ref_binding_pattern v [concrete] // CHECK:STDOUT: %v.var_patt: = var_pattern %v.patt [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %v.var: ref = var %v.var_patt [concrete = ] // CHECK:STDOUT: %.loc12: type = splice_block %struct_type.m2 [concrete = constants.%struct_type.m2.779] { // CHECK:STDOUT: %Abstract2.ref: type = name_ref Abstract2, %Abstract2.decl [concrete = constants.%Abstract2] // CHECK:STDOUT: %struct_type.m2: type = struct_type {.m2: %Abstract2} [concrete = constants.%struct_type.m2.779] // CHECK:STDOUT: } // CHECK:STDOUT: %v: ref = ref_binding v, [concrete = ] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @Abstract2 { // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%Abstract2 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @__global_init() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: assign file.%v.var, // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- fail_todo_abstract_let.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %Abstract3: type = class_type @Abstract3 [concrete] // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %complete_type: = complete_type_witness %empty_struct_type [concrete] // CHECK:STDOUT: %pattern_type.7de: type = pattern_type %Abstract3 [concrete] // CHECK:STDOUT: %F.type: type = fn_type @F [concrete] // CHECK:STDOUT: %F: %F.type = struct_value () [concrete] // CHECK:STDOUT: %struct_type.m3.b1b: type = struct_type {.m3: %Abstract3} [concrete] // CHECK:STDOUT: %pattern_type.816: type = pattern_type %struct_type.m3.b1b [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Abstract3 = %Abstract3.decl // CHECK:STDOUT: .F = %F.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Abstract3.decl: type = class_decl @Abstract3 [concrete = constants.%Abstract3] {} {} // CHECK:STDOUT: %F.decl: %F.type = fn_decl @F [concrete = constants.%F] { // CHECK:STDOUT: %a.patt: %pattern_type.7de = value_binding_pattern a [concrete] // CHECK:STDOUT: %a.param_patt: %pattern_type.7de = value_param_pattern %a.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %a.param: %Abstract3 = value_param call_param0 // CHECK:STDOUT: %Abstract3.ref.loc6: type = name_ref Abstract3, file.%Abstract3.decl [concrete = constants.%Abstract3] // CHECK:STDOUT: %a: %Abstract3 = value_binding a, %a.param // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @Abstract3 { // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%Abstract3 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @F(%a.param: %Abstract3) { // CHECK:STDOUT: !entry: // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %l.patt: %pattern_type.816 = value_binding_pattern l [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %a.ref: %Abstract3 = name_ref a, %a // CHECK:STDOUT: %.loc14_44: %struct_type.m3.b1b = struct_literal (%a.ref) // CHECK:STDOUT: %.loc14_32: type = splice_block %struct_type.m3 [concrete = constants.%struct_type.m3.b1b] { // CHECK:STDOUT: %Abstract3.ref.loc14: type = name_ref Abstract3, file.%Abstract3.decl [concrete = constants.%Abstract3] // CHECK:STDOUT: %struct_type.m3: type = struct_type {.m3: %Abstract3} [concrete = constants.%struct_type.m3.b1b] // CHECK:STDOUT: } // CHECK:STDOUT: %l: %struct_type.m3.b1b = value_binding l, [concrete = ] // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- fail_abstract_twice.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %Abstract4: type = class_type @Abstract4 [concrete] // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %complete_type: = complete_type_witness %empty_struct_type [concrete] // CHECK:STDOUT: %Abstract5: type = class_type @Abstract5 [concrete] // CHECK:STDOUT: %struct_type.m4.m5.2d0: type = struct_type {.m4: %Abstract4, .m5: %Abstract5} [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Abstract4 = %Abstract4.decl // CHECK:STDOUT: .Abstract5 = %Abstract5.decl // CHECK:STDOUT: .v2 = %v2 // CHECK:STDOUT: } // CHECK:STDOUT: %Abstract4.decl: type = class_decl @Abstract4 [concrete = constants.%Abstract4] {} {} // CHECK:STDOUT: %Abstract5.decl: type = class_decl @Abstract5 [concrete = constants.%Abstract5] {} {} // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %v2.patt: = ref_binding_pattern v2 [concrete] // CHECK:STDOUT: %v2.var_patt: = var_pattern %v2.patt [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %v2.var: ref = var %v2.var_patt [concrete = ] // CHECK:STDOUT: %.loc13: type = splice_block %struct_type.m4.m5 [concrete = constants.%struct_type.m4.m5.2d0] { // CHECK:STDOUT: %Abstract4.ref: type = name_ref Abstract4, %Abstract4.decl [concrete = constants.%Abstract4] // CHECK:STDOUT: %Abstract5.ref: type = name_ref Abstract5, %Abstract5.decl [concrete = constants.%Abstract5] // CHECK:STDOUT: %struct_type.m4.m5: type = struct_type {.m4: %Abstract4, .m5: %Abstract5} [concrete = constants.%struct_type.m4.m5.2d0] // CHECK:STDOUT: } // CHECK:STDOUT: %v2: ref = ref_binding v2, [concrete = ] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @Abstract4 { // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%Abstract4 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @Abstract5 { // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%Abstract5 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @__global_init() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: assign file.%v2.var, // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- fail_abstract_first.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %Abstract6: type = class_type @Abstract6 [concrete] // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %complete_type: = complete_type_witness %empty_struct_type [concrete] // CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] // CHECK:STDOUT: %empty_tuple: %empty_tuple.type = tuple_value () [concrete] // CHECK:STDOUT: %struct_type.m6.c1.69b: type = struct_type {.m6: %Abstract6, .c1: %empty_tuple.type} [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Abstract6 = %Abstract6.decl // CHECK:STDOUT: .v3 = %v3 // CHECK:STDOUT: } // CHECK:STDOUT: %Abstract6.decl: type = class_decl @Abstract6 [concrete = constants.%Abstract6] {} {} // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %v3.patt: = ref_binding_pattern v3 [concrete] // CHECK:STDOUT: %v3.var_patt: = var_pattern %v3.patt [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %v3.var: ref = var %v3.var_patt [concrete = ] // CHECK:STDOUT: %.loc12_33: type = splice_block %struct_type.m6.c1 [concrete = constants.%struct_type.m6.c1.69b] { // CHECK:STDOUT: %Abstract6.ref: type = name_ref Abstract6, %Abstract6.decl [concrete = constants.%Abstract6] // CHECK:STDOUT: %.loc12_32.1: %empty_tuple.type = tuple_literal () [concrete = constants.%empty_tuple] // CHECK:STDOUT: %.loc12_32.2: type = converted %.loc12_32.1, constants.%empty_tuple.type [concrete = constants.%empty_tuple.type] // CHECK:STDOUT: %struct_type.m6.c1: type = struct_type {.m6: %Abstract6, .c1: %empty_tuple.type} [concrete = constants.%struct_type.m6.c1.69b] // CHECK:STDOUT: } // CHECK:STDOUT: %v3: ref = ref_binding v3, [concrete = ] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @Abstract6 { // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%Abstract6 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @__global_init() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: assign file.%v3.var, // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- fail_abstract_second.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %Abstract7: type = class_type @Abstract7 [concrete] // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %complete_type: = complete_type_witness %empty_struct_type [concrete] // CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] // CHECK:STDOUT: %empty_tuple: %empty_tuple.type = tuple_value () [concrete] // CHECK:STDOUT: %struct_type.c2.m7.e94: type = struct_type {.c2: %empty_tuple.type, .m7: %Abstract7} [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Abstract7 = %Abstract7.decl // CHECK:STDOUT: .v4 = %v4 // CHECK:STDOUT: } // CHECK:STDOUT: %Abstract7.decl: type = class_decl @Abstract7 [concrete = constants.%Abstract7] {} {} // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %v4.patt: = ref_binding_pattern v4 [concrete] // CHECK:STDOUT: %v4.var_patt: = var_pattern %v4.patt [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %v4.var: ref = var %v4.var_patt [concrete = ] // CHECK:STDOUT: %.loc12_33: type = splice_block %struct_type.c2.m7 [concrete = constants.%struct_type.c2.m7.e94] { // CHECK:STDOUT: %.loc12_16.1: %empty_tuple.type = tuple_literal () [concrete = constants.%empty_tuple] // CHECK:STDOUT: %.loc12_16.2: type = converted %.loc12_16.1, constants.%empty_tuple.type [concrete = constants.%empty_tuple.type] // CHECK:STDOUT: %Abstract7.ref: type = name_ref Abstract7, %Abstract7.decl [concrete = constants.%Abstract7] // CHECK:STDOUT: %struct_type.c2.m7: type = struct_type {.c2: %empty_tuple.type, .m7: %Abstract7} [concrete = constants.%struct_type.c2.m7.e94] // CHECK:STDOUT: } // CHECK:STDOUT: %v4: ref = ref_binding v4, [concrete = ] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @Abstract7 { // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%Abstract7 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @__global_init() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: assign file.%v4.var, // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- lib.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %Abstract: type = class_type @Abstract [concrete] // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %complete_type: = complete_type_witness %empty_struct_type [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Abstract = %Abstract.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Abstract.decl: type = class_decl @Abstract [concrete = constants.%Abstract] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @Abstract { // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%Abstract // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- fail_import.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %Abstract: type = class_type @Abstract [concrete] // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %complete_type: = complete_type_witness %empty_struct_type [concrete] // CHECK:STDOUT: %struct_type.m.9e8: type = struct_type {.m: %Abstract} [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Main.Abstract: type = import_ref Main//lib, Abstract, loaded [concrete = constants.%Abstract] // CHECK:STDOUT: %Main.import_ref.8f2: = import_ref Main//lib, loc3_26, loaded [concrete = constants.%complete_type] // CHECK:STDOUT: %Main.import_ref.141 = import_ref Main//lib, inst{{[0-9A-F]+}} [no loc], unloaded // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Abstract = imports.%Main.Abstract // CHECK:STDOUT: .v5 = %v5 // CHECK:STDOUT: } // CHECK:STDOUT: %default.import = import // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %v5.patt: = ref_binding_pattern v5 [concrete] // CHECK:STDOUT: %v5.var_patt: = var_pattern %v5.patt [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %v5.var: ref = var %v5.var_patt [concrete = ] // CHECK:STDOUT: %.loc13: type = splice_block %struct_type.m [concrete = constants.%struct_type.m.9e8] { // CHECK:STDOUT: %Abstract.ref: type = name_ref Abstract, imports.%Main.Abstract [concrete = constants.%Abstract] // CHECK:STDOUT: %struct_type.m: type = struct_type {.m: %Abstract} [concrete = constants.%struct_type.m.9e8] // CHECK:STDOUT: } // CHECK:STDOUT: %v5: ref = ref_binding v5, [concrete = ] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @Abstract [from "lib.carbon"] { // CHECK:STDOUT: complete_type_witness = imports.%Main.import_ref.8f2 // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = imports.%Main.import_ref.141 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @__global_init() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: assign file.%v5.var, // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/class/abstract/fail_abstract_in_tuple.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/destroy.carbon // TODO: Add ranges and switch to "--dump-sem-ir-ranges=only". // EXTRA-ARGS: --dump-sem-ir-ranges=if-present // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/class/abstract/fail_abstract_in_tuple.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/class/abstract/fail_abstract_in_tuple.carbon // --- fail_abstract_field.carbon library "[[@TEST_NAME]]"; abstract class Abstract1 {} class Contains { // CHECK:STDERR: fail_abstract_field.carbon:[[@LINE+7]]:10: error: field has abstract type `(Abstract1,)` [AbstractTypeInFieldDecl] // CHECK:STDERR: var a: (Abstract1,); // CHECK:STDERR: ^~~~~~~~~~~~ // CHECK:STDERR: fail_abstract_field.carbon:[[@LINE-6]]:1: note: uses class that was declared abstract here [ClassAbstractHere] // CHECK:STDERR: abstract class Abstract1 {} // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: var a: (Abstract1,); } // --- fail_abstract_var.carbon library "[[@TEST_NAME]]"; abstract class Abstract2 {} fn Var() { // CHECK:STDERR: fail_abstract_var.carbon:[[@LINE+7]]:17: error: binding pattern has abstract type `(Abstract2,)` in `var` pattern [AbstractTypeInVarPattern] // CHECK:STDERR: var unused v: (Abstract2,); // CHECK:STDERR: ^~~~~~~~~~~~ // CHECK:STDERR: fail_abstract_var.carbon:[[@LINE-6]]:1: note: uses class that was declared abstract here [ClassAbstractHere] // CHECK:STDERR: abstract class Abstract2 {} // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: var unused v: (Abstract2,); } // --- fail_todo_abstract_let.carbon library "[[@TEST_NAME]]"; abstract class Abstract3 { } fn F(a: Abstract3) { // CHECK:STDERR: fail_todo_abstract_let.carbon:[[@LINE+7]]:32: error: initialization of abstract type `(Abstract3,)` [AbstractTypeInInit] // CHECK:STDERR: let unused l: (Abstract3,) = (a,); // CHECK:STDERR: ^~~~ // CHECK:STDERR: fail_todo_abstract_let.carbon:[[@LINE-7]]:1: note: uses class that was declared abstract here [ClassAbstractHere] // CHECK:STDERR: abstract class Abstract3 { // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: let unused l: (Abstract3,) = (a,); } // --- fail_abstract_twice.carbon library "[[@TEST_NAME]]"; abstract class Abstract4 {} abstract class Abstract5 {} fn Var2() { // CHECK:STDERR: fail_abstract_twice.carbon:[[@LINE+7]]:18: error: binding pattern has abstract type `(Abstract4, Abstract5)` in `var` pattern [AbstractTypeInVarPattern] // CHECK:STDERR: var unused v2: (Abstract4, Abstract5); // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: fail_abstract_twice.carbon:[[@LINE-7]]:1: note: uses class that was declared abstract here [ClassAbstractHere] // CHECK:STDERR: abstract class Abstract4 {} // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: var unused v2: (Abstract4, Abstract5); } // --- fail_abstract_first.carbon library "[[@TEST_NAME]]"; abstract class Abstract6 {} fn Var3() { // CHECK:STDERR: fail_abstract_first.carbon:[[@LINE+7]]:18: error: binding pattern has abstract type `(Abstract6, {})` in `var` pattern [AbstractTypeInVarPattern] // CHECK:STDERR: var unused v3: (Abstract6, {}); // CHECK:STDERR: ^~~~~~~~~~~~~~~ // CHECK:STDERR: fail_abstract_first.carbon:[[@LINE-6]]:1: note: uses class that was declared abstract here [ClassAbstractHere] // CHECK:STDERR: abstract class Abstract6 {} // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: var unused v3: (Abstract6, {}); } // --- fail_abstract_second.carbon library "[[@TEST_NAME]]"; abstract class Abstract7 {} fn Var4() { // CHECK:STDERR: fail_abstract_second.carbon:[[@LINE+7]]:18: error: binding pattern has abstract type `({}, Abstract7)` in `var` pattern [AbstractTypeInVarPattern] // CHECK:STDERR: var unused v4: ({}, Abstract7); // CHECK:STDERR: ^~~~~~~~~~~~~~~ // CHECK:STDERR: fail_abstract_second.carbon:[[@LINE-6]]:1: note: uses class that was declared abstract here [ClassAbstractHere] // CHECK:STDERR: abstract class Abstract7 {} // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: var unused v4: ({}, Abstract7); } // --- lib.carbon library "[[@TEST_NAME]]"; abstract class Abstract {} // --- fail_import.carbon library "[[@TEST_NAME]]"; import library "lib"; fn Var5() { // CHECK:STDERR: fail_import.carbon:[[@LINE+8]]:18: error: binding pattern has abstract type `(Abstract,)` in `var` pattern [AbstractTypeInVarPattern] // CHECK:STDERR: var unused v5: (Abstract,); // CHECK:STDERR: ^~~~~~~~~~~ // CHECK:STDERR: fail_import.carbon:[[@LINE-6]]:1: in import [InImport] // CHECK:STDERR: lib.carbon:3:1: note: uses class that was declared abstract here [ClassAbstractHere] // CHECK:STDERR: abstract class Abstract {} // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: var unused v5: (Abstract,); } // CHECK:STDOUT: --- fail_abstract_field.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %Abstract1: type = class_type @Abstract1 [concrete] // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %complete_type: = complete_type_witness %empty_struct_type [concrete] // CHECK:STDOUT: %Contains: type = class_type @Contains [concrete] // CHECK:STDOUT: %tuple.type.85c: type = tuple_type (type) [concrete] // CHECK:STDOUT: %tuple: %tuple.type.85c = tuple_value (%Abstract1) [concrete] // CHECK:STDOUT: %tuple.type.453: type = tuple_type (%Abstract1) [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .Abstract1 = %Abstract1.decl // CHECK:STDOUT: .Contains = %Contains.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %Abstract1.decl: type = class_decl @Abstract1 [concrete = constants.%Abstract1] {} {} // CHECK:STDOUT: %Contains.decl: type = class_decl @Contains [concrete = constants.%Contains] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @Abstract1 { // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%Abstract1 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @Contains { // CHECK:STDOUT: %Abstract1.ref: type = name_ref Abstract1, file.%Abstract1.decl [concrete = constants.%Abstract1] // CHECK:STDOUT: %.loc13_21.1: %tuple.type.85c = tuple_literal (%Abstract1.ref) [concrete = constants.%tuple] // CHECK:STDOUT: %.loc13_21.2: type = converted %.loc13_21.1, constants.%tuple.type.453 [concrete = constants.%tuple.type.453] // CHECK:STDOUT: %.loc13_8: = field_decl a, element0 [concrete] // CHECK:STDOUT: %complete_type: = complete_type_witness [concrete = ] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%Contains // CHECK:STDOUT: .Abstract1 = // CHECK:STDOUT: .a = %.loc13_8 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- fail_abstract_var.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %Abstract2: type = class_type @Abstract2 [concrete] // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %complete_type: = complete_type_witness %empty_struct_type [concrete] // CHECK:STDOUT: %Var.type: type = fn_type @Var [concrete] // CHECK:STDOUT: %Var: %Var.type = struct_value () [concrete] // CHECK:STDOUT: %tuple.type.85c: type = tuple_type (type) [concrete] // CHECK:STDOUT: %tuple: %tuple.type.85c = tuple_value (%Abstract2) [concrete] // CHECK:STDOUT: %tuple.type.d46: type = tuple_type (%Abstract2) [concrete] // CHECK:STDOUT: %Destroy.type: type = facet_type <@Destroy> [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: .Destroy = %Core.Destroy // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: %Core.Destroy: type = import_ref Core//prelude/parts/destroy, Destroy, loaded [concrete = constants.%Destroy.type] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .Abstract2 = %Abstract2.decl // CHECK:STDOUT: .Var = %Var.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %Abstract2.decl: type = class_decl @Abstract2 [concrete = constants.%Abstract2] {} {} // CHECK:STDOUT: %Var.decl: %Var.type = fn_decl @Var [concrete = constants.%Var] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @Abstract2 { // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%Abstract2 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @Var() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %v.patt: = ref_binding_pattern v [concrete] // CHECK:STDOUT: %v.var_patt: = var_pattern %v.patt [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %v.var: ref = var %v.var_patt [concrete = ] // CHECK:STDOUT: assign %v.var, // CHECK:STDOUT: %.loc13_28.1: type = splice_block %.loc13_28.3 [concrete = constants.%tuple.type.d46] { // CHECK:STDOUT: %Abstract2.ref: type = name_ref Abstract2, file.%Abstract2.decl [concrete = constants.%Abstract2] // CHECK:STDOUT: %.loc13_28.2: %tuple.type.85c = tuple_literal (%Abstract2.ref) [concrete = constants.%tuple] // CHECK:STDOUT: %.loc13_28.3: type = converted %.loc13_28.2, constants.%tuple.type.d46 [concrete = constants.%tuple.type.d46] // CHECK:STDOUT: } // CHECK:STDOUT: %v: ref = ref_binding v, [concrete = ] // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- fail_todo_abstract_let.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %Abstract3: type = class_type @Abstract3 [concrete] // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %complete_type: = complete_type_witness %empty_struct_type [concrete] // CHECK:STDOUT: %pattern_type.7de: type = pattern_type %Abstract3 [concrete] // CHECK:STDOUT: %F.type: type = fn_type @F [concrete] // CHECK:STDOUT: %F: %F.type = struct_value () [concrete] // CHECK:STDOUT: %tuple.type.85c: type = tuple_type (type) [concrete] // CHECK:STDOUT: %tuple: %tuple.type.85c = tuple_value (%Abstract3) [concrete] // CHECK:STDOUT: %tuple.type.c99: type = tuple_type (%Abstract3) [concrete] // CHECK:STDOUT: %pattern_type.016: type = pattern_type %tuple.type.c99 [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .Abstract3 = %Abstract3.decl // CHECK:STDOUT: .F = %F.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %Abstract3.decl: type = class_decl @Abstract3 [concrete = constants.%Abstract3] {} {} // CHECK:STDOUT: %F.decl: %F.type = fn_decl @F [concrete = constants.%F] { // CHECK:STDOUT: %a.patt: %pattern_type.7de = value_binding_pattern a [concrete] // CHECK:STDOUT: %a.param_patt: %pattern_type.7de = value_param_pattern %a.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %a.param: %Abstract3 = value_param call_param0 // CHECK:STDOUT: %Abstract3.ref.loc6: type = name_ref Abstract3, file.%Abstract3.decl [concrete = constants.%Abstract3] // CHECK:STDOUT: %a: %Abstract3 = value_binding a, %a.param // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @Abstract3 { // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%Abstract3 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @F(%a.param: %Abstract3) { // CHECK:STDOUT: !entry: // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %l.patt: %pattern_type.016 = value_binding_pattern l [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %a.ref: %Abstract3 = name_ref a, %a // CHECK:STDOUT: %.loc14_35: %tuple.type.c99 = tuple_literal (%a.ref) // CHECK:STDOUT: %.loc14_28.1: type = splice_block %.loc14_28.3 [concrete = constants.%tuple.type.c99] { // CHECK:STDOUT: %Abstract3.ref.loc14: type = name_ref Abstract3, file.%Abstract3.decl [concrete = constants.%Abstract3] // CHECK:STDOUT: %.loc14_28.2: %tuple.type.85c = tuple_literal (%Abstract3.ref.loc14) [concrete = constants.%tuple] // CHECK:STDOUT: %.loc14_28.3: type = converted %.loc14_28.2, constants.%tuple.type.c99 [concrete = constants.%tuple.type.c99] // CHECK:STDOUT: } // CHECK:STDOUT: %l: %tuple.type.c99 = value_binding l, [concrete = ] // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- fail_abstract_twice.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %Abstract4: type = class_type @Abstract4 [concrete] // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %complete_type: = complete_type_witness %empty_struct_type [concrete] // CHECK:STDOUT: %Abstract5: type = class_type @Abstract5 [concrete] // CHECK:STDOUT: %Var2.type: type = fn_type @Var2 [concrete] // CHECK:STDOUT: %Var2: %Var2.type = struct_value () [concrete] // CHECK:STDOUT: %tuple.type.24b: type = tuple_type (type, type) [concrete] // CHECK:STDOUT: %tuple: %tuple.type.24b = tuple_value (%Abstract4, %Abstract5) [concrete] // CHECK:STDOUT: %tuple.type.fa1: type = tuple_type (%Abstract4, %Abstract5) [concrete] // CHECK:STDOUT: %Destroy.type: type = facet_type <@Destroy> [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: .Destroy = %Core.Destroy // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: %Core.Destroy: type = import_ref Core//prelude/parts/destroy, Destroy, loaded [concrete = constants.%Destroy.type] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .Abstract4 = %Abstract4.decl // CHECK:STDOUT: .Abstract5 = %Abstract5.decl // CHECK:STDOUT: .Var2 = %Var2.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %Abstract4.decl: type = class_decl @Abstract4 [concrete = constants.%Abstract4] {} {} // CHECK:STDOUT: %Abstract5.decl: type = class_decl @Abstract5 [concrete = constants.%Abstract5] {} {} // CHECK:STDOUT: %Var2.decl: %Var2.type = fn_decl @Var2 [concrete = constants.%Var2] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @Abstract4 { // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%Abstract4 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @Abstract5 { // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%Abstract5 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @Var2() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %v2.patt: = ref_binding_pattern v2 [concrete] // CHECK:STDOUT: %v2.var_patt: = var_pattern %v2.patt [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %v2.var: ref = var %v2.var_patt [concrete = ] // CHECK:STDOUT: assign %v2.var, // CHECK:STDOUT: %.loc14_39.1: type = splice_block %.loc14_39.3 [concrete = constants.%tuple.type.fa1] { // CHECK:STDOUT: %Abstract4.ref: type = name_ref Abstract4, file.%Abstract4.decl [concrete = constants.%Abstract4] // CHECK:STDOUT: %Abstract5.ref: type = name_ref Abstract5, file.%Abstract5.decl [concrete = constants.%Abstract5] // CHECK:STDOUT: %.loc14_39.2: %tuple.type.24b = tuple_literal (%Abstract4.ref, %Abstract5.ref) [concrete = constants.%tuple] // CHECK:STDOUT: %.loc14_39.3: type = converted %.loc14_39.2, constants.%tuple.type.fa1 [concrete = constants.%tuple.type.fa1] // CHECK:STDOUT: } // CHECK:STDOUT: %v2: ref = ref_binding v2, [concrete = ] // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- fail_abstract_first.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %Abstract6: type = class_type @Abstract6 [concrete] // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %complete_type: = complete_type_witness %empty_struct_type [concrete] // CHECK:STDOUT: %Var3.type: type = fn_type @Var3 [concrete] // CHECK:STDOUT: %Var3: %Var3.type = struct_value () [concrete] // CHECK:STDOUT: %empty_struct: %empty_struct_type = struct_value () [concrete] // CHECK:STDOUT: %tuple.type.159: type = tuple_type (type, %empty_struct_type) [concrete] // CHECK:STDOUT: %tuple: %tuple.type.159 = tuple_value (%Abstract6, %empty_struct) [concrete] // CHECK:STDOUT: %tuple.type.a75: type = tuple_type (%Abstract6, %empty_struct_type) [concrete] // CHECK:STDOUT: %Destroy.type: type = facet_type <@Destroy> [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: .Destroy = %Core.Destroy // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: %Core.Destroy: type = import_ref Core//prelude/parts/destroy, Destroy, loaded [concrete = constants.%Destroy.type] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .Abstract6 = %Abstract6.decl // CHECK:STDOUT: .Var3 = %Var3.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %Abstract6.decl: type = class_decl @Abstract6 [concrete = constants.%Abstract6] {} {} // CHECK:STDOUT: %Var3.decl: %Var3.type = fn_decl @Var3 [concrete = constants.%Var3] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @Abstract6 { // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%Abstract6 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @Var3() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %v3.patt: = ref_binding_pattern v3 [concrete] // CHECK:STDOUT: %v3.var_patt: = var_pattern %v3.patt [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %v3.var: ref = var %v3.var_patt [concrete = ] // CHECK:STDOUT: assign %v3.var, // CHECK:STDOUT: %.loc13_32.1: type = splice_block %.loc13_32.4 [concrete = constants.%tuple.type.a75] { // CHECK:STDOUT: %Abstract6.ref: type = name_ref Abstract6, file.%Abstract6.decl [concrete = constants.%Abstract6] // CHECK:STDOUT: %.loc13_31: %empty_struct_type = struct_literal () [concrete = constants.%empty_struct] // CHECK:STDOUT: %.loc13_32.2: %tuple.type.159 = tuple_literal (%Abstract6.ref, %.loc13_31) [concrete = constants.%tuple] // CHECK:STDOUT: %.loc13_32.3: type = converted constants.%empty_struct, constants.%empty_struct_type [concrete = constants.%empty_struct_type] // CHECK:STDOUT: %.loc13_32.4: type = converted %.loc13_32.2, constants.%tuple.type.a75 [concrete = constants.%tuple.type.a75] // CHECK:STDOUT: } // CHECK:STDOUT: %v3: ref = ref_binding v3, [concrete = ] // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- fail_abstract_second.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %Abstract7: type = class_type @Abstract7 [concrete] // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %complete_type: = complete_type_witness %empty_struct_type [concrete] // CHECK:STDOUT: %Var4.type: type = fn_type @Var4 [concrete] // CHECK:STDOUT: %Var4: %Var4.type = struct_value () [concrete] // CHECK:STDOUT: %empty_struct: %empty_struct_type = struct_value () [concrete] // CHECK:STDOUT: %tuple.type.c8c: type = tuple_type (%empty_struct_type, type) [concrete] // CHECK:STDOUT: %tuple: %tuple.type.c8c = tuple_value (%empty_struct, %Abstract7) [concrete] // CHECK:STDOUT: %tuple.type.ff9: type = tuple_type (%empty_struct_type, %Abstract7) [concrete] // CHECK:STDOUT: %Destroy.type: type = facet_type <@Destroy> [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: .Destroy = %Core.Destroy // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: %Core.Destroy: type = import_ref Core//prelude/parts/destroy, Destroy, loaded [concrete = constants.%Destroy.type] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .Abstract7 = %Abstract7.decl // CHECK:STDOUT: .Var4 = %Var4.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %Abstract7.decl: type = class_decl @Abstract7 [concrete = constants.%Abstract7] {} {} // CHECK:STDOUT: %Var4.decl: %Var4.type = fn_decl @Var4 [concrete = constants.%Var4] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @Abstract7 { // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%Abstract7 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @Var4() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %v4.patt: = ref_binding_pattern v4 [concrete] // CHECK:STDOUT: %v4.var_patt: = var_pattern %v4.patt [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %v4.var: ref = var %v4.var_patt [concrete = ] // CHECK:STDOUT: assign %v4.var, // CHECK:STDOUT: %.loc13_32.1: type = splice_block %.loc13_32.4 [concrete = constants.%tuple.type.ff9] { // CHECK:STDOUT: %.loc13_20: %empty_struct_type = struct_literal () [concrete = constants.%empty_struct] // CHECK:STDOUT: %Abstract7.ref: type = name_ref Abstract7, file.%Abstract7.decl [concrete = constants.%Abstract7] // CHECK:STDOUT: %.loc13_32.2: %tuple.type.c8c = tuple_literal (%.loc13_20, %Abstract7.ref) [concrete = constants.%tuple] // CHECK:STDOUT: %.loc13_32.3: type = converted constants.%empty_struct, constants.%empty_struct_type [concrete = constants.%empty_struct_type] // CHECK:STDOUT: %.loc13_32.4: type = converted %.loc13_32.2, constants.%tuple.type.ff9 [concrete = constants.%tuple.type.ff9] // CHECK:STDOUT: } // CHECK:STDOUT: %v4: ref = ref_binding v4, [concrete = ] // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- lib.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %Abstract: type = class_type @Abstract [concrete] // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %complete_type: = complete_type_witness %empty_struct_type [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .Abstract = %Abstract.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %Abstract.decl: type = class_decl @Abstract [concrete = constants.%Abstract] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @Abstract { // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%Abstract // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- fail_import.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %Var5.type: type = fn_type @Var5 [concrete] // CHECK:STDOUT: %Var5: %Var5.type = struct_value () [concrete] // CHECK:STDOUT: %Abstract: type = class_type @Abstract [concrete] // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %complete_type: = complete_type_witness %empty_struct_type [concrete] // CHECK:STDOUT: %tuple.type.85c: type = tuple_type (type) [concrete] // CHECK:STDOUT: %tuple: %tuple.type.85c = tuple_value (%Abstract) [concrete] // CHECK:STDOUT: %tuple.type.e3a: type = tuple_type (%Abstract) [concrete] // CHECK:STDOUT: %Destroy.type: type = facet_type <@Destroy> [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Main.Abstract: type = import_ref Main//lib, Abstract, loaded [concrete = constants.%Abstract] // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: .Destroy = %Core.Destroy // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: %Main.import_ref.8f2: = import_ref Main//lib, loc3_26, loaded [concrete = constants.%complete_type] // CHECK:STDOUT: %Main.import_ref.141 = import_ref Main//lib, inst{{[0-9A-F]+}} [no loc], unloaded // CHECK:STDOUT: %Core.Destroy: type = import_ref Core//prelude/parts/destroy, Destroy, loaded [concrete = constants.%Destroy.type] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Abstract = imports.%Main.Abstract // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .Var5 = %Var5.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %default.import = import // CHECK:STDOUT: %Var5.decl: %Var5.type = fn_decl @Var5 [concrete = constants.%Var5] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @Abstract [from "lib.carbon"] { // CHECK:STDOUT: complete_type_witness = imports.%Main.import_ref.8f2 // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = imports.%Main.import_ref.141 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @Var5() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %v5.patt: = ref_binding_pattern v5 [concrete] // CHECK:STDOUT: %v5.var_patt: = var_pattern %v5.patt [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %v5.var: ref = var %v5.var_patt [concrete = ] // CHECK:STDOUT: assign %v5.var, // CHECK:STDOUT: %.loc14_28.1: type = splice_block %.loc14_28.3 [concrete = constants.%tuple.type.e3a] { // CHECK:STDOUT: %Abstract.ref: type = name_ref Abstract, imports.%Main.Abstract [concrete = constants.%Abstract] // CHECK:STDOUT: %.loc14_28.2: %tuple.type.85c = tuple_literal (%Abstract.ref) [concrete = constants.%tuple] // CHECK:STDOUT: %.loc14_28.3: type = converted %.loc14_28.2, constants.%tuple.type.e3a [concrete = constants.%tuple.type.e3a] // CHECK:STDOUT: } // CHECK:STDOUT: %v5: ref = ref_binding v5, [concrete = ] // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/class/access/access_modifers.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/int.carbon // TODO: Add ranges and switch to "--dump-sem-ir-ranges=only". // EXTRA-ARGS: --dump-sem-ir-ranges=if-present // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/class/access/access_modifers.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/class/access/access_modifers.carbon // --- fail_private_field_access.carbon library "[[@TEST_NAME]]"; class Circle { private var radius: i32; private let SOME_INTERNAL_CONSTANT: i32 = 5; private fn SomeInternalFunction() -> i32 { return 0; } fn Make() -> Self { return {.radius = 5}; } } fn Run() { let circle: Circle = Circle.Make(); // CHECK:STDERR: fail_private_field_access.carbon:[[@LINE+7]]:28: error: cannot access private member `radius` of type `Circle` [ClassInvalidMemberAccess] // CHECK:STDERR: let unused radius: i32 = circle.radius; // CHECK:STDERR: ^~~~~~~~~~~~~ // CHECK:STDERR: fail_private_field_access.carbon:[[@LINE-17]]:15: note: declared here [ClassMemberDeclaration] // CHECK:STDERR: private var radius: i32; // CHECK:STDERR: ^~~~~~~~~~~ // CHECK:STDERR: let unused radius: i32 = circle.radius; // CHECK:STDERR: fail_private_field_access.carbon:[[@LINE+7]]:3: error: cannot access private member `radius` of type `Circle` [ClassInvalidMemberAccess] // CHECK:STDERR: circle.radius = 5; // CHECK:STDERR: ^~~~~~~~~~~~~ // CHECK:STDERR: fail_private_field_access.carbon:[[@LINE-25]]:15: note: declared here [ClassMemberDeclaration] // CHECK:STDERR: private var radius: i32; // CHECK:STDERR: ^~~~~~~~~~~ // CHECK:STDERR: circle.radius = 5; // CHECK:STDERR: fail_private_field_access.carbon:[[@LINE+7]]:3: error: cannot access private member `SOME_INTERNAL_CONSTANT` of type `Circle` [ClassInvalidMemberAccess] // CHECK:STDERR: circle.SOME_INTERNAL_CONSTANT; // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: fail_private_field_access.carbon:[[@LINE-32]]:15: note: declared here [ClassMemberDeclaration] // CHECK:STDERR: private let SOME_INTERNAL_CONSTANT: i32 = 5; // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: circle.SOME_INTERNAL_CONSTANT; // CHECK:STDERR: fail_private_field_access.carbon:[[@LINE+7]]:3: error: cannot access private member `SomeInternalFunction` of type `Circle` [ClassInvalidMemberAccess] // CHECK:STDERR: circle.SomeInternalFunction(); // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: fail_private_field_access.carbon:[[@LINE-39]]:3: note: declared here [ClassMemberDeclaration] // CHECK:STDERR: private fn SomeInternalFunction() -> i32 { // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: circle.SomeInternalFunction(); } // --- fail_protected_field_access.carbon library "[[@TEST_NAME]]"; class A { protected var x: i32; } fn Run() { // CHECK:STDERR: fail_protected_field_access.carbon:[[@LINE+7]]:23: error: cannot access protected member `x` of type `A` [ClassInvalidMemberAccess] // CHECK:STDERR: let unused x: i32 = A.x; // CHECK:STDERR: ^~~ // CHECK:STDERR: fail_protected_field_access.carbon:[[@LINE-7]]:17: note: declared here [ClassMemberDeclaration] // CHECK:STDERR: protected var x: i32; // CHECK:STDERR: ^~~~~~ // CHECK:STDERR: let unused x: i32 = A.x; } // --- instance_private_field_access_on_self.carbon library "[[@TEST_NAME]]"; class Circle { private var radius: i32; fn GetRadius[self: Self]() -> i32 { return self.radius; } private fn SomeInternalFunction() -> i32 { return 0; } fn Compute[self: Self]() -> i32 { return self.SomeInternalFunction(); } } // --- public_global_access.carbon library "[[@TEST_NAME]]"; class A { let x: i32 = 5; } let x: i32 = A.x; // --- fail_global_access.carbon library "[[@TEST_NAME]]"; class A { protected let x: i32 = 5; private let y: i32 = 5; } // CHECK:STDERR: fail_global_access.carbon:[[@LINE+7]]:14: error: cannot access protected member `x` of type `A` [ClassInvalidMemberAccess] // CHECK:STDERR: let x: i32 = A.x; // CHECK:STDERR: ^~~ // CHECK:STDERR: fail_global_access.carbon:[[@LINE-7]]:17: note: declared here [ClassMemberDeclaration] // CHECK:STDERR: protected let x: i32 = 5; // CHECK:STDERR: ^ // CHECK:STDERR: let x: i32 = A.x; // CHECK:STDERR: fail_global_access.carbon:[[@LINE+7]]:14: error: cannot access private member `y` of type `A` [ClassInvalidMemberAccess] // CHECK:STDERR: let y: i32 = A.y; // CHECK:STDERR: ^~~ // CHECK:STDERR: fail_global_access.carbon:[[@LINE-14]]:15: note: declared here [ClassMemberDeclaration] // CHECK:STDERR: private let y: i32 = 5; // CHECK:STDERR: ^ // CHECK:STDERR: let y: i32 = A.y; // --- self_access.carbon library "[[@TEST_NAME]]"; class A { private fn F() {} private fn G() { Self.F(); } } // CHECK:STDOUT: --- fail_private_field_access.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %Circle: type = class_type @Circle [concrete] // CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [concrete] // CHECK:STDOUT: %Int.type: type = generic_class_type @Int [concrete] // CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] // CHECK:STDOUT: %Int.generic: %Int.type = struct_value () [concrete] // CHECK:STDOUT: %i32: type = class_type @Int, @Int(%int_32) [concrete] // CHECK:STDOUT: %Circle.elem: type = unbound_element_type %Circle, %i32 [concrete] // CHECK:STDOUT: %pattern_type.7ce: type = pattern_type %i32 [concrete] // CHECK:STDOUT: %int_5.64b: Core.IntLiteral = int_value 5 [concrete] // CHECK:STDOUT: %ImplicitAs.type.cc7: type = generic_interface_type @ImplicitAs [concrete] // CHECK:STDOUT: %ImplicitAs.generic: %ImplicitAs.type.cc7 = struct_value () [concrete] // CHECK:STDOUT: %ImplicitAs.type.e8c: type = facet_type <@ImplicitAs, @ImplicitAs(%i32)> [concrete] // CHECK:STDOUT: %To: Core.IntLiteral = symbolic_binding To, 0 [symbolic] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.4e6: type = fn_type @Core.IntLiteral.as.ImplicitAs.impl.Convert, @Core.IntLiteral.as.ImplicitAs.impl(%To) [symbolic] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.3c2: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.4e6 = struct_value () [symbolic] // CHECK:STDOUT: %ImplicitAs.impl_witness.6bc: = impl_witness imports.%ImplicitAs.impl_witness_table.74f, @Core.IntLiteral.as.ImplicitAs.impl(%int_32) [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.e0d: type = fn_type @Core.IntLiteral.as.ImplicitAs.impl.Convert, @Core.IntLiteral.as.ImplicitAs.impl(%int_32) [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.e0d = struct_value () [concrete] // CHECK:STDOUT: %ImplicitAs.facet: %ImplicitAs.type.e8c = facet_value Core.IntLiteral, (%ImplicitAs.impl_witness.6bc) [concrete] // CHECK:STDOUT: %ImplicitAs.WithSelf.Convert.type.b37: type = fn_type @ImplicitAs.WithSelf.Convert, @ImplicitAs.WithSelf(%i32, %ImplicitAs.facet) [concrete] // CHECK:STDOUT: %.545: type = fn_type_with_self_type %ImplicitAs.WithSelf.Convert.type.b37, %ImplicitAs.facet [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.bound.005: = bound_method %int_5.64b, %Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5 [concrete] // CHECK:STDOUT: %.ff5: Core.Form = init_form %i32 [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn: = specific_function %Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5, @Core.IntLiteral.as.ImplicitAs.impl.Convert(%int_32) [concrete] // CHECK:STDOUT: %bound_method.e9d: = bound_method %int_5.64b, %Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn [concrete] // CHECK:STDOUT: %int_5.0f6: %i32 = int_value 5 [concrete] // CHECK:STDOUT: %Circle.SomeInternalFunction.type: type = fn_type @Circle.SomeInternalFunction [concrete] // CHECK:STDOUT: %Circle.SomeInternalFunction: %Circle.SomeInternalFunction.type = struct_value () [concrete] // CHECK:STDOUT: %.f65: Core.Form = init_form %Circle [concrete] // CHECK:STDOUT: %pattern_type.fcb: type = pattern_type %Circle [concrete] // CHECK:STDOUT: %Circle.Make.type: type = fn_type @Circle.Make [concrete] // CHECK:STDOUT: %Circle.Make: %Circle.Make.type = struct_value () [concrete] // CHECK:STDOUT: %struct_type.radius.251: type = struct_type {.radius: %i32} [concrete] // CHECK:STDOUT: %complete_type.5a5: = complete_type_witness %struct_type.radius.251 [concrete] // CHECK:STDOUT: %int_0.5c6: Core.IntLiteral = int_value 0 [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.bound.897: = bound_method %int_0.5c6, %Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5 [concrete] // CHECK:STDOUT: %bound_method.d2e: = bound_method %int_0.5c6, %Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn [concrete] // CHECK:STDOUT: %int_0.6a9: %i32 = int_value 0 [concrete] // CHECK:STDOUT: %struct_type.radius.f47: type = struct_type {.radius: Core.IntLiteral} [concrete] // CHECK:STDOUT: %struct: %struct_type.radius.f47 = struct_value (%int_5.64b) [concrete] // CHECK:STDOUT: %Circle.val: %Circle = struct_value (%int_5.0f6) [concrete] // CHECK:STDOUT: %Run.type: type = fn_type @Run [concrete] // CHECK:STDOUT: %Run: %Run.type = struct_value () [concrete] // CHECK:STDOUT: %Destroy.type: type = facet_type <@Destroy> [concrete] // CHECK:STDOUT: %Destroy.Op.type: type = fn_type @Destroy.Op [concrete] // CHECK:STDOUT: %Destroy.Op: %Destroy.Op.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: .Int = %Core.Int // CHECK:STDOUT: .ImplicitAs = %Core.ImplicitAs // CHECK:STDOUT: .Destroy = %Core.Destroy // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: %Core.Int: %Int.type = import_ref Core//prelude/parts/int, Int, loaded [concrete = constants.%Int.generic] // CHECK:STDOUT: %Core.ImplicitAs: %ImplicitAs.type.cc7 = import_ref Core//prelude/parts/as, ImplicitAs, loaded [concrete = constants.%ImplicitAs.generic] // CHECK:STDOUT: %Core.import_ref.42d: @Core.IntLiteral.as.ImplicitAs.impl.%Core.IntLiteral.as.ImplicitAs.impl.Convert.type (%Core.IntLiteral.as.ImplicitAs.impl.Convert.type.4e6) = import_ref Core//prelude/parts/int, loc{{\d+_\d+}}, loaded [symbolic = @Core.IntLiteral.as.ImplicitAs.impl.%Core.IntLiteral.as.ImplicitAs.impl.Convert (constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.3c2)] // CHECK:STDOUT: %ImplicitAs.impl_witness_table.74f = impl_witness_table (%Core.import_ref.42d), @Core.IntLiteral.as.ImplicitAs.impl [concrete] // CHECK:STDOUT: %Core.Destroy: type = import_ref Core//prelude/parts/destroy, Destroy, loaded [concrete = constants.%Destroy.type] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .Circle = %Circle.decl // CHECK:STDOUT: .Run = %Run.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %Circle.decl: type = class_decl @Circle [concrete = constants.%Circle] {} {} // CHECK:STDOUT: %Run.decl: %Run.type = fn_decl @Run [concrete = constants.%Run] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @Circle { // CHECK:STDOUT: %i32.loc5: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %.loc5: %Circle.elem = field_decl radius, element0 [concrete] // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %SOME_INTERNAL_CONSTANT.patt: %pattern_type.7ce = value_binding_pattern SOME_INTERNAL_CONSTANT [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %int_5: Core.IntLiteral = int_value 5 [concrete = constants.%int_5.64b] // CHECK:STDOUT: %i32.loc6: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %impl.elem0: %.545 = impl_witness_access constants.%ImplicitAs.impl_witness.6bc, element0 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5] // CHECK:STDOUT: %bound_method.loc6_45.1: = bound_method %int_5, %impl.elem0 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.bound.005] // CHECK:STDOUT: %specific_fn: = specific_function %impl.elem0, @Core.IntLiteral.as.ImplicitAs.impl.Convert(constants.%int_32) [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn] // CHECK:STDOUT: %bound_method.loc6_45.2: = bound_method %int_5, %specific_fn [concrete = constants.%bound_method.e9d] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.call: init %i32 = call %bound_method.loc6_45.2(%int_5) [concrete = constants.%int_5.0f6] // CHECK:STDOUT: %.loc6_45.1: %i32 = value_of_initializer %Core.IntLiteral.as.ImplicitAs.impl.Convert.call [concrete = constants.%int_5.0f6] // CHECK:STDOUT: %.loc6_45.2: %i32 = converted %int_5, %.loc6_45.1 [concrete = constants.%int_5.0f6] // CHECK:STDOUT: %SOME_INTERNAL_CONSTANT: %i32 = value_binding SOME_INTERNAL_CONSTANT, %.loc6_45.2 // CHECK:STDOUT: %Circle.SomeInternalFunction.decl: %Circle.SomeInternalFunction.type = fn_decl @Circle.SomeInternalFunction [concrete = constants.%Circle.SomeInternalFunction] { // CHECK:STDOUT: %return.patt: %pattern_type.7ce = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: %pattern_type.7ce = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %i32: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %.loc8: Core.Form = init_form %i32 [concrete = constants.%.ff5] // CHECK:STDOUT: %return.param: ref %i32 = out_param call_param0 // CHECK:STDOUT: %return: ref %i32 = return_slot %return.param // CHECK:STDOUT: } // CHECK:STDOUT: %Circle.Make.decl: %Circle.Make.type = fn_decl @Circle.Make [concrete = constants.%Circle.Make] { // CHECK:STDOUT: %return.patt: %pattern_type.fcb = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: %pattern_type.fcb = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %Self.ref: type = name_ref Self, constants.%Circle [concrete = constants.%Circle] // CHECK:STDOUT: %.loc12: Core.Form = init_form %Self.ref [concrete = constants.%.f65] // CHECK:STDOUT: %return.param: ref %Circle = out_param call_param0 // CHECK:STDOUT: %return: ref %Circle = return_slot %return.param // CHECK:STDOUT: } // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%struct_type.radius.251 [concrete = constants.%complete_type.5a5] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%Circle // CHECK:STDOUT: .radius [private] = %.loc5 // CHECK:STDOUT: .SOME_INTERNAL_CONSTANT [private] = %SOME_INTERNAL_CONSTANT // CHECK:STDOUT: .SomeInternalFunction [private] = %Circle.SomeInternalFunction.decl // CHECK:STDOUT: .Make = %Circle.Make.decl // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @Circle.SomeInternalFunction() -> out %return.param: %i32 { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %int_0: Core.IntLiteral = int_value 0 [concrete = constants.%int_0.5c6] // CHECK:STDOUT: %impl.elem0: %.545 = impl_witness_access constants.%ImplicitAs.impl_witness.6bc, element0 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5] // CHECK:STDOUT: %bound_method.loc9_13.1: = bound_method %int_0, %impl.elem0 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.bound.897] // CHECK:STDOUT: %specific_fn: = specific_function %impl.elem0, @Core.IntLiteral.as.ImplicitAs.impl.Convert(constants.%int_32) [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn] // CHECK:STDOUT: %bound_method.loc9_13.2: = bound_method %int_0, %specific_fn [concrete = constants.%bound_method.d2e] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.call: init %i32 = call %bound_method.loc9_13.2(%int_0) [concrete = constants.%int_0.6a9] // CHECK:STDOUT: %.loc9: init %i32 = converted %int_0, %Core.IntLiteral.as.ImplicitAs.impl.Convert.call [concrete = constants.%int_0.6a9] // CHECK:STDOUT: return %.loc9 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @Circle.Make() -> out %return.param: %Circle { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %int_5: Core.IntLiteral = int_value 5 [concrete = constants.%int_5.64b] // CHECK:STDOUT: %.loc13_24.1: %struct_type.radius.f47 = struct_literal (%int_5) [concrete = constants.%struct] // CHECK:STDOUT: %impl.elem0: %.545 = impl_witness_access constants.%ImplicitAs.impl_witness.6bc, element0 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5] // CHECK:STDOUT: %bound_method.loc13_24.1: = bound_method %int_5, %impl.elem0 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.bound.005] // CHECK:STDOUT: %specific_fn: = specific_function %impl.elem0, @Core.IntLiteral.as.ImplicitAs.impl.Convert(constants.%int_32) [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn] // CHECK:STDOUT: %bound_method.loc13_24.2: = bound_method %int_5, %specific_fn [concrete = constants.%bound_method.e9d] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.call: init %i32 = call %bound_method.loc13_24.2(%int_5) [concrete = constants.%int_5.0f6] // CHECK:STDOUT: %.loc13_24.2: init %i32 = converted %int_5, %Core.IntLiteral.as.ImplicitAs.impl.Convert.call [concrete = constants.%int_5.0f6] // CHECK:STDOUT: %.loc13_24.3: ref %i32 = class_element_access %return.param, element0 // CHECK:STDOUT: %.loc13_24.4: init %i32 to %.loc13_24.3 = in_place_init %.loc13_24.2 [concrete = constants.%int_5.0f6] // CHECK:STDOUT: %.loc13_24.5: init %Circle to %return.param = class_init (%.loc13_24.4) [concrete = constants.%Circle.val] // CHECK:STDOUT: %.loc13_25: init %Circle = converted %.loc13_24.1, %.loc13_24.5 [concrete = constants.%Circle.val] // CHECK:STDOUT: return %.loc13_25 to %return.param // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @Run() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %circle.patt: %pattern_type.fcb = value_binding_pattern circle [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %Circle.ref.loc18_24: type = name_ref Circle, file.%Circle.decl [concrete = constants.%Circle] // CHECK:STDOUT: %Make.ref: %Circle.Make.type = name_ref Make, @Circle.%Circle.Make.decl [concrete = constants.%Circle.Make] // CHECK:STDOUT: %.loc18_36.1: ref %Circle = temporary_storage // CHECK:STDOUT: %Circle.Make.call: init %Circle to %.loc18_36.1 = call %Make.ref() // CHECK:STDOUT: %Circle.ref.loc18_15: type = name_ref Circle, file.%Circle.decl [concrete = constants.%Circle] // CHECK:STDOUT: %.loc18_36.2: ref %Circle = temporary %.loc18_36.1, %Circle.Make.call // CHECK:STDOUT: %.loc18_36.3: %Circle = acquire_value %.loc18_36.2 // CHECK:STDOUT: %circle: %Circle = value_binding circle, %.loc18_36.3 // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %radius.patt: %pattern_type.7ce = value_binding_pattern radius [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %circle.ref.loc26: %Circle = name_ref circle, %circle // CHECK:STDOUT: %radius.ref.loc26: = name_ref radius, [concrete = ] // CHECK:STDOUT: %i32: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %radius: %i32 = value_binding radius, [concrete = ] // CHECK:STDOUT: %circle.ref.loc34: %Circle = name_ref circle, %circle // CHECK:STDOUT: %radius.ref.loc34: = name_ref radius, [concrete = ] // CHECK:STDOUT: %int_5: Core.IntLiteral = int_value 5 [concrete = constants.%int_5.64b] // CHECK:STDOUT: assign %radius.ref.loc34, // CHECK:STDOUT: %circle.ref.loc42: %Circle = name_ref circle, %circle // CHECK:STDOUT: %SOME_INTERNAL_CONSTANT.ref: = name_ref SOME_INTERNAL_CONSTANT, [concrete = ] // CHECK:STDOUT: %circle.ref.loc51: %Circle = name_ref circle, %circle // CHECK:STDOUT: %SomeInternalFunction.ref: = name_ref SomeInternalFunction, [concrete = ] // CHECK:STDOUT: %Destroy.Op.bound: = bound_method %.loc18_36.2, constants.%Destroy.Op // CHECK:STDOUT: %Destroy.Op.call: init %empty_tuple.type = call %Destroy.Op.bound(%.loc18_36.2) // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @Destroy.Op(%self.param: ref %Circle) = "no_op"; // CHECK:STDOUT: // CHECK:STDOUT: --- fail_protected_field_access.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %A: type = class_type @A [concrete] // CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [concrete] // CHECK:STDOUT: %Int.type: type = generic_class_type @Int [concrete] // CHECK:STDOUT: %Int.generic: %Int.type = struct_value () [concrete] // CHECK:STDOUT: %i32: type = class_type @Int, @Int(%int_32) [concrete] // CHECK:STDOUT: %A.elem: type = unbound_element_type %A, %i32 [concrete] // CHECK:STDOUT: %struct_type.x: type = struct_type {.x: %i32} [concrete] // CHECK:STDOUT: %complete_type.1ec: = complete_type_witness %struct_type.x [concrete] // CHECK:STDOUT: %Run.type: type = fn_type @Run [concrete] // CHECK:STDOUT: %Run: %Run.type = struct_value () [concrete] // CHECK:STDOUT: %pattern_type.7ce: type = pattern_type %i32 [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: .Int = %Core.Int // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: %Core.Int: %Int.type = import_ref Core//prelude/parts/int, Int, loaded [concrete = constants.%Int.generic] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .A = %A.decl // CHECK:STDOUT: .Run = %Run.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %A.decl: type = class_decl @A [concrete = constants.%A] {} {} // CHECK:STDOUT: %Run.decl: %Run.type = fn_decl @Run [concrete = constants.%Run] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @A { // CHECK:STDOUT: %i32: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %.loc5: %A.elem = field_decl x, element0 [concrete] // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%struct_type.x [concrete = constants.%complete_type.1ec] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%A // CHECK:STDOUT: .x [protected] = %.loc5 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @Run() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %x.patt: %pattern_type.7ce = value_binding_pattern x [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %A.ref: type = name_ref A, file.%A.decl [concrete = constants.%A] // CHECK:STDOUT: %x.ref: = name_ref x, [concrete = ] // CHECK:STDOUT: %i32: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %x: %i32 = value_binding x, [concrete = ] // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- instance_private_field_access_on_self.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %Circle: type = class_type @Circle [concrete] // CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [concrete] // CHECK:STDOUT: %Int.type: type = generic_class_type @Int [concrete] // CHECK:STDOUT: %Int.generic: %Int.type = struct_value () [concrete] // CHECK:STDOUT: %N: Core.IntLiteral = symbolic_binding N, 0 [symbolic] // CHECK:STDOUT: %i32: type = class_type @Int, @Int(%int_32) [concrete] // CHECK:STDOUT: %Circle.elem: type = unbound_element_type %Circle, %i32 [concrete] // CHECK:STDOUT: %pattern_type.fcb: type = pattern_type %Circle [concrete] // CHECK:STDOUT: %.ff5: Core.Form = init_form %i32 [concrete] // CHECK:STDOUT: %pattern_type.7ce: type = pattern_type %i32 [concrete] // CHECK:STDOUT: %Circle.GetRadius.type: type = fn_type @Circle.GetRadius [concrete] // CHECK:STDOUT: %Circle.GetRadius: %Circle.GetRadius.type = struct_value () [concrete] // CHECK:STDOUT: %Circle.SomeInternalFunction.type: type = fn_type @Circle.SomeInternalFunction [concrete] // CHECK:STDOUT: %Circle.SomeInternalFunction: %Circle.SomeInternalFunction.type = struct_value () [concrete] // CHECK:STDOUT: %Circle.Compute.type: type = fn_type @Circle.Compute [concrete] // CHECK:STDOUT: %Circle.Compute: %Circle.Compute.type = struct_value () [concrete] // CHECK:STDOUT: %struct_type.radius: type = struct_type {.radius: %i32} [concrete] // CHECK:STDOUT: %complete_type.5a5: = complete_type_witness %struct_type.radius [concrete] // CHECK:STDOUT: %Copy.type: type = facet_type <@Copy> [concrete] // CHECK:STDOUT: %Int.as.Copy.impl.Op.type.824: type = fn_type @Int.as.Copy.impl.Op, @Int.as.Copy.impl(%N) [symbolic] // CHECK:STDOUT: %Int.as.Copy.impl.Op.9b9: %Int.as.Copy.impl.Op.type.824 = struct_value () [symbolic] // CHECK:STDOUT: %Copy.impl_witness.f17: = impl_witness imports.%Copy.impl_witness_table.e76, @Int.as.Copy.impl(%int_32) [concrete] // CHECK:STDOUT: %Int.as.Copy.impl.Op.type.546: type = fn_type @Int.as.Copy.impl.Op, @Int.as.Copy.impl(%int_32) [concrete] // CHECK:STDOUT: %Int.as.Copy.impl.Op.664: %Int.as.Copy.impl.Op.type.546 = struct_value () [concrete] // CHECK:STDOUT: %Copy.facet: %Copy.type = facet_value %i32, (%Copy.impl_witness.f17) [concrete] // CHECK:STDOUT: %Copy.WithSelf.Op.type.081: type = fn_type @Copy.WithSelf.Op, @Copy.WithSelf(%Copy.facet) [concrete] // CHECK:STDOUT: %.8e2: type = fn_type_with_self_type %Copy.WithSelf.Op.type.081, %Copy.facet [concrete] // CHECK:STDOUT: %Int.as.Copy.impl.Op.specific_fn: = specific_function %Int.as.Copy.impl.Op.664, @Int.as.Copy.impl.Op(%int_32) [concrete] // CHECK:STDOUT: %int_0.5c6: Core.IntLiteral = int_value 0 [concrete] // CHECK:STDOUT: %ImplicitAs.type.cc7: type = generic_interface_type @ImplicitAs [concrete] // CHECK:STDOUT: %ImplicitAs.generic: %ImplicitAs.type.cc7 = struct_value () [concrete] // CHECK:STDOUT: %ImplicitAs.type.e8c: type = facet_type <@ImplicitAs, @ImplicitAs(%i32)> [concrete] // CHECK:STDOUT: %To: Core.IntLiteral = symbolic_binding To, 0 [symbolic] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.4e6: type = fn_type @Core.IntLiteral.as.ImplicitAs.impl.Convert, @Core.IntLiteral.as.ImplicitAs.impl(%To) [symbolic] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.3c2: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.4e6 = struct_value () [symbolic] // CHECK:STDOUT: %ImplicitAs.impl_witness.6bc: = impl_witness imports.%ImplicitAs.impl_witness_table.74f, @Core.IntLiteral.as.ImplicitAs.impl(%int_32) [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.e0d: type = fn_type @Core.IntLiteral.as.ImplicitAs.impl.Convert, @Core.IntLiteral.as.ImplicitAs.impl(%int_32) [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.e0d = struct_value () [concrete] // CHECK:STDOUT: %ImplicitAs.facet: %ImplicitAs.type.e8c = facet_value Core.IntLiteral, (%ImplicitAs.impl_witness.6bc) [concrete] // CHECK:STDOUT: %ImplicitAs.WithSelf.Convert.type.b37: type = fn_type @ImplicitAs.WithSelf.Convert, @ImplicitAs.WithSelf(%i32, %ImplicitAs.facet) [concrete] // CHECK:STDOUT: %.545: type = fn_type_with_self_type %ImplicitAs.WithSelf.Convert.type.b37, %ImplicitAs.facet [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.bound: = bound_method %int_0.5c6, %Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5 [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn: = specific_function %Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5, @Core.IntLiteral.as.ImplicitAs.impl.Convert(%int_32) [concrete] // CHECK:STDOUT: %bound_method: = bound_method %int_0.5c6, %Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn [concrete] // CHECK:STDOUT: %int_0.6a9: %i32 = int_value 0 [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: .Int = %Core.Int // CHECK:STDOUT: .Copy = %Core.Copy // CHECK:STDOUT: .ImplicitAs = %Core.ImplicitAs // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: %Core.Int: %Int.type = import_ref Core//prelude/parts/int, Int, loaded [concrete = constants.%Int.generic] // CHECK:STDOUT: %Core.Copy: type = import_ref Core//prelude/parts/copy, Copy, loaded [concrete = constants.%Copy.type] // CHECK:STDOUT: %Core.import_ref.18d: @Int.as.Copy.impl.%Int.as.Copy.impl.Op.type (%Int.as.Copy.impl.Op.type.824) = import_ref Core//prelude/parts/int, loc{{\d+_\d+}}, loaded [symbolic = @Int.as.Copy.impl.%Int.as.Copy.impl.Op (constants.%Int.as.Copy.impl.Op.9b9)] // CHECK:STDOUT: %Copy.impl_witness_table.e76 = impl_witness_table (%Core.import_ref.18d), @Int.as.Copy.impl [concrete] // CHECK:STDOUT: %Core.ImplicitAs: %ImplicitAs.type.cc7 = import_ref Core//prelude/parts/as, ImplicitAs, loaded [concrete = constants.%ImplicitAs.generic] // CHECK:STDOUT: %Core.import_ref.42d: @Core.IntLiteral.as.ImplicitAs.impl.%Core.IntLiteral.as.ImplicitAs.impl.Convert.type (%Core.IntLiteral.as.ImplicitAs.impl.Convert.type.4e6) = import_ref Core//prelude/parts/int, loc{{\d+_\d+}}, loaded [symbolic = @Core.IntLiteral.as.ImplicitAs.impl.%Core.IntLiteral.as.ImplicitAs.impl.Convert (constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.3c2)] // CHECK:STDOUT: %ImplicitAs.impl_witness_table.74f = impl_witness_table (%Core.import_ref.42d), @Core.IntLiteral.as.ImplicitAs.impl [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .Circle = %Circle.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %Circle.decl: type = class_decl @Circle [concrete = constants.%Circle] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @Circle { // CHECK:STDOUT: %i32: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %.loc5: %Circle.elem = field_decl radius, element0 [concrete] // CHECK:STDOUT: %Circle.GetRadius.decl: %Circle.GetRadius.type = fn_decl @Circle.GetRadius [concrete = constants.%Circle.GetRadius] { // CHECK:STDOUT: %self.patt: %pattern_type.fcb = value_binding_pattern self [concrete] // CHECK:STDOUT: %self.param_patt: %pattern_type.fcb = value_param_pattern %self.patt [concrete] // CHECK:STDOUT: %return.patt: %pattern_type.7ce = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: %pattern_type.7ce = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %i32: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %.loc7: Core.Form = init_form %i32 [concrete = constants.%.ff5] // CHECK:STDOUT: %self.param: %Circle = value_param call_param0 // CHECK:STDOUT: %Self.ref: type = name_ref Self, constants.%Circle [concrete = constants.%Circle] // CHECK:STDOUT: %self: %Circle = value_binding self, %self.param // CHECK:STDOUT: %return.param: ref %i32 = out_param call_param1 // CHECK:STDOUT: %return: ref %i32 = return_slot %return.param // CHECK:STDOUT: } // CHECK:STDOUT: %Circle.SomeInternalFunction.decl: %Circle.SomeInternalFunction.type = fn_decl @Circle.SomeInternalFunction [concrete = constants.%Circle.SomeInternalFunction] { // CHECK:STDOUT: %return.patt: %pattern_type.7ce = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: %pattern_type.7ce = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %i32: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %.loc11: Core.Form = init_form %i32 [concrete = constants.%.ff5] // CHECK:STDOUT: %return.param: ref %i32 = out_param call_param0 // CHECK:STDOUT: %return: ref %i32 = return_slot %return.param // CHECK:STDOUT: } // CHECK:STDOUT: %Circle.Compute.decl: %Circle.Compute.type = fn_decl @Circle.Compute [concrete = constants.%Circle.Compute] { // CHECK:STDOUT: %self.patt: %pattern_type.fcb = value_binding_pattern self [concrete] // CHECK:STDOUT: %self.param_patt: %pattern_type.fcb = value_param_pattern %self.patt [concrete] // CHECK:STDOUT: %return.patt: %pattern_type.7ce = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: %pattern_type.7ce = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %i32: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %.loc15: Core.Form = init_form %i32 [concrete = constants.%.ff5] // CHECK:STDOUT: %self.param: %Circle = value_param call_param0 // CHECK:STDOUT: %Self.ref: type = name_ref Self, constants.%Circle [concrete = constants.%Circle] // CHECK:STDOUT: %self: %Circle = value_binding self, %self.param // CHECK:STDOUT: %return.param: ref %i32 = out_param call_param1 // CHECK:STDOUT: %return: ref %i32 = return_slot %return.param // CHECK:STDOUT: } // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%struct_type.radius [concrete = constants.%complete_type.5a5] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%Circle // CHECK:STDOUT: .radius [private] = %.loc5 // CHECK:STDOUT: .GetRadius = %Circle.GetRadius.decl // CHECK:STDOUT: .SomeInternalFunction [private] = %Circle.SomeInternalFunction.decl // CHECK:STDOUT: .Compute = %Circle.Compute.decl // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @Circle.GetRadius(%self.param: %Circle) -> out %return.param: %i32 { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %self.ref: %Circle = name_ref self, %self // CHECK:STDOUT: %radius.ref: %Circle.elem = name_ref radius, @Circle.%.loc5 [concrete = @Circle.%.loc5] // CHECK:STDOUT: %.loc8_16.1: ref %i32 = class_element_access %self.ref, element0 // CHECK:STDOUT: %.loc8_16.2: %i32 = acquire_value %.loc8_16.1 // CHECK:STDOUT: %impl.elem0: %.8e2 = impl_witness_access constants.%Copy.impl_witness.f17, element0 [concrete = constants.%Int.as.Copy.impl.Op.664] // CHECK:STDOUT: %bound_method.loc8_16.1: = bound_method %.loc8_16.2, %impl.elem0 // CHECK:STDOUT: %specific_fn: = specific_function %impl.elem0, @Int.as.Copy.impl.Op(constants.%int_32) [concrete = constants.%Int.as.Copy.impl.Op.specific_fn] // CHECK:STDOUT: %bound_method.loc8_16.2: = bound_method %.loc8_16.2, %specific_fn // CHECK:STDOUT: %Int.as.Copy.impl.Op.call: init %i32 = call %bound_method.loc8_16.2(%.loc8_16.2) // CHECK:STDOUT: return %Int.as.Copy.impl.Op.call // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @Circle.SomeInternalFunction() -> out %return.param: %i32 { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %int_0: Core.IntLiteral = int_value 0 [concrete = constants.%int_0.5c6] // CHECK:STDOUT: %impl.elem0: %.545 = impl_witness_access constants.%ImplicitAs.impl_witness.6bc, element0 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5] // CHECK:STDOUT: %bound_method.loc12_13.1: = bound_method %int_0, %impl.elem0 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.bound] // CHECK:STDOUT: %specific_fn: = specific_function %impl.elem0, @Core.IntLiteral.as.ImplicitAs.impl.Convert(constants.%int_32) [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn] // CHECK:STDOUT: %bound_method.loc12_13.2: = bound_method %int_0, %specific_fn [concrete = constants.%bound_method] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.call: init %i32 = call %bound_method.loc12_13.2(%int_0) [concrete = constants.%int_0.6a9] // CHECK:STDOUT: %.loc12: init %i32 = converted %int_0, %Core.IntLiteral.as.ImplicitAs.impl.Convert.call [concrete = constants.%int_0.6a9] // CHECK:STDOUT: return %.loc12 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @Circle.Compute(%self.param: %Circle) -> out %return.param: %i32 { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %self.ref: %Circle = name_ref self, %self // CHECK:STDOUT: %SomeInternalFunction.ref: %Circle.SomeInternalFunction.type = name_ref SomeInternalFunction, @Circle.%Circle.SomeInternalFunction.decl [concrete = constants.%Circle.SomeInternalFunction] // CHECK:STDOUT: %Circle.SomeInternalFunction.call: init %i32 = call %SomeInternalFunction.ref() // CHECK:STDOUT: return %Circle.SomeInternalFunction.call // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- public_global_access.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %A: type = class_type @A [concrete] // CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [concrete] // CHECK:STDOUT: %Int.type: type = generic_class_type @Int [concrete] // CHECK:STDOUT: %Int.generic: %Int.type = struct_value () [concrete] // CHECK:STDOUT: %i32: type = class_type @Int, @Int(%int_32) [concrete] // CHECK:STDOUT: %pattern_type.7ce: type = pattern_type %i32 [concrete] // CHECK:STDOUT: %int_5.64b: Core.IntLiteral = int_value 5 [concrete] // CHECK:STDOUT: %ImplicitAs.type.cc7: type = generic_interface_type @ImplicitAs [concrete] // CHECK:STDOUT: %ImplicitAs.generic: %ImplicitAs.type.cc7 = struct_value () [concrete] // CHECK:STDOUT: %ImplicitAs.type.e8c: type = facet_type <@ImplicitAs, @ImplicitAs(%i32)> [concrete] // CHECK:STDOUT: %To: Core.IntLiteral = symbolic_binding To, 0 [symbolic] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.4e6: type = fn_type @Core.IntLiteral.as.ImplicitAs.impl.Convert, @Core.IntLiteral.as.ImplicitAs.impl(%To) [symbolic] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.3c2: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.4e6 = struct_value () [symbolic] // CHECK:STDOUT: %ImplicitAs.impl_witness.6bc: = impl_witness imports.%ImplicitAs.impl_witness_table.74f, @Core.IntLiteral.as.ImplicitAs.impl(%int_32) [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.e0d: type = fn_type @Core.IntLiteral.as.ImplicitAs.impl.Convert, @Core.IntLiteral.as.ImplicitAs.impl(%int_32) [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.e0d = struct_value () [concrete] // CHECK:STDOUT: %ImplicitAs.facet: %ImplicitAs.type.e8c = facet_value Core.IntLiteral, (%ImplicitAs.impl_witness.6bc) [concrete] // CHECK:STDOUT: %ImplicitAs.WithSelf.Convert.type.b37: type = fn_type @ImplicitAs.WithSelf.Convert, @ImplicitAs.WithSelf(%i32, %ImplicitAs.facet) [concrete] // CHECK:STDOUT: %.545: type = fn_type_with_self_type %ImplicitAs.WithSelf.Convert.type.b37, %ImplicitAs.facet [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.bound: = bound_method %int_5.64b, %Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5 [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn: = specific_function %Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5, @Core.IntLiteral.as.ImplicitAs.impl.Convert(%int_32) [concrete] // CHECK:STDOUT: %bound_method: = bound_method %int_5.64b, %Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn [concrete] // CHECK:STDOUT: %int_5.0f6: %i32 = int_value 5 [concrete] // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %complete_type.357: = complete_type_witness %empty_struct_type [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: .Int = %Core.Int // CHECK:STDOUT: .ImplicitAs = %Core.ImplicitAs // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: %Core.Int: %Int.type = import_ref Core//prelude/parts/int, Int, loaded [concrete = constants.%Int.generic] // CHECK:STDOUT: %Core.ImplicitAs: %ImplicitAs.type.cc7 = import_ref Core//prelude/parts/as, ImplicitAs, loaded [concrete = constants.%ImplicitAs.generic] // CHECK:STDOUT: %Core.import_ref.42d: @Core.IntLiteral.as.ImplicitAs.impl.%Core.IntLiteral.as.ImplicitAs.impl.Convert.type (%Core.IntLiteral.as.ImplicitAs.impl.Convert.type.4e6) = import_ref Core//prelude/parts/int, loc{{\d+_\d+}}, loaded [symbolic = @Core.IntLiteral.as.ImplicitAs.impl.%Core.IntLiteral.as.ImplicitAs.impl.Convert (constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.3c2)] // CHECK:STDOUT: %ImplicitAs.impl_witness_table.74f = impl_witness_table (%Core.import_ref.42d), @Core.IntLiteral.as.ImplicitAs.impl [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .A = %A.decl // CHECK:STDOUT: .x = %x // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %A.decl: type = class_decl @A [concrete = constants.%A] {} {} // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %x.patt: %pattern_type.7ce = value_binding_pattern x [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %i32: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %x: %i32 = value_binding x, @__global_init.%x.ref // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @A { // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %x.patt: %pattern_type.7ce = value_binding_pattern x [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %int_5: Core.IntLiteral = int_value 5 [concrete = constants.%int_5.64b] // CHECK:STDOUT: %i32: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %impl.elem0: %.545 = impl_witness_access constants.%ImplicitAs.impl_witness.6bc, element0 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5] // CHECK:STDOUT: %bound_method.loc5_16.1: = bound_method %int_5, %impl.elem0 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.bound] // CHECK:STDOUT: %specific_fn: = specific_function %impl.elem0, @Core.IntLiteral.as.ImplicitAs.impl.Convert(constants.%int_32) [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn] // CHECK:STDOUT: %bound_method.loc5_16.2: = bound_method %int_5, %specific_fn [concrete = constants.%bound_method] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.call: init %i32 = call %bound_method.loc5_16.2(%int_5) [concrete = constants.%int_5.0f6] // CHECK:STDOUT: %.loc5_16.1: %i32 = value_of_initializer %Core.IntLiteral.as.ImplicitAs.impl.Convert.call [concrete = constants.%int_5.0f6] // CHECK:STDOUT: %.loc5_16.2: %i32 = converted %int_5, %.loc5_16.1 [concrete = constants.%int_5.0f6] // CHECK:STDOUT: %x: %i32 = value_binding x, %.loc5_16.2 // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type.357] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%A // CHECK:STDOUT: .x = %x // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @__global_init() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %A.ref: type = name_ref A, file.%A.decl [concrete = constants.%A] // CHECK:STDOUT: %x.ref: %i32 = name_ref x, @A.%x // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- fail_global_access.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %A: type = class_type @A [concrete] // CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [concrete] // CHECK:STDOUT: %Int.type: type = generic_class_type @Int [concrete] // CHECK:STDOUT: %Int.generic: %Int.type = struct_value () [concrete] // CHECK:STDOUT: %i32: type = class_type @Int, @Int(%int_32) [concrete] // CHECK:STDOUT: %pattern_type.7ce: type = pattern_type %i32 [concrete] // CHECK:STDOUT: %int_5.64b: Core.IntLiteral = int_value 5 [concrete] // CHECK:STDOUT: %ImplicitAs.type.cc7: type = generic_interface_type @ImplicitAs [concrete] // CHECK:STDOUT: %ImplicitAs.generic: %ImplicitAs.type.cc7 = struct_value () [concrete] // CHECK:STDOUT: %ImplicitAs.type.e8c: type = facet_type <@ImplicitAs, @ImplicitAs(%i32)> [concrete] // CHECK:STDOUT: %To: Core.IntLiteral = symbolic_binding To, 0 [symbolic] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.4e6: type = fn_type @Core.IntLiteral.as.ImplicitAs.impl.Convert, @Core.IntLiteral.as.ImplicitAs.impl(%To) [symbolic] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.3c2: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.4e6 = struct_value () [symbolic] // CHECK:STDOUT: %ImplicitAs.impl_witness.6bc: = impl_witness imports.%ImplicitAs.impl_witness_table.74f, @Core.IntLiteral.as.ImplicitAs.impl(%int_32) [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.e0d: type = fn_type @Core.IntLiteral.as.ImplicitAs.impl.Convert, @Core.IntLiteral.as.ImplicitAs.impl(%int_32) [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.e0d = struct_value () [concrete] // CHECK:STDOUT: %ImplicitAs.facet: %ImplicitAs.type.e8c = facet_value Core.IntLiteral, (%ImplicitAs.impl_witness.6bc) [concrete] // CHECK:STDOUT: %ImplicitAs.WithSelf.Convert.type.b37: type = fn_type @ImplicitAs.WithSelf.Convert, @ImplicitAs.WithSelf(%i32, %ImplicitAs.facet) [concrete] // CHECK:STDOUT: %.545: type = fn_type_with_self_type %ImplicitAs.WithSelf.Convert.type.b37, %ImplicitAs.facet [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.bound: = bound_method %int_5.64b, %Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5 [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn: = specific_function %Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5, @Core.IntLiteral.as.ImplicitAs.impl.Convert(%int_32) [concrete] // CHECK:STDOUT: %bound_method: = bound_method %int_5.64b, %Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn [concrete] // CHECK:STDOUT: %int_5.0f6: %i32 = int_value 5 [concrete] // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %complete_type.357: = complete_type_witness %empty_struct_type [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: .Int = %Core.Int // CHECK:STDOUT: .ImplicitAs = %Core.ImplicitAs // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: %Core.Int: %Int.type = import_ref Core//prelude/parts/int, Int, loaded [concrete = constants.%Int.generic] // CHECK:STDOUT: %Core.ImplicitAs: %ImplicitAs.type.cc7 = import_ref Core//prelude/parts/as, ImplicitAs, loaded [concrete = constants.%ImplicitAs.generic] // CHECK:STDOUT: %Core.import_ref.42d: @Core.IntLiteral.as.ImplicitAs.impl.%Core.IntLiteral.as.ImplicitAs.impl.Convert.type (%Core.IntLiteral.as.ImplicitAs.impl.Convert.type.4e6) = import_ref Core//prelude/parts/int, loc{{\d+_\d+}}, loaded [symbolic = @Core.IntLiteral.as.ImplicitAs.impl.%Core.IntLiteral.as.ImplicitAs.impl.Convert (constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.3c2)] // CHECK:STDOUT: %ImplicitAs.impl_witness_table.74f = impl_witness_table (%Core.import_ref.42d), @Core.IntLiteral.as.ImplicitAs.impl [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .A = %A.decl // CHECK:STDOUT: .x = %x // CHECK:STDOUT: .y = %y // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %A.decl: type = class_decl @A [concrete = constants.%A] {} {} // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %x.patt: %pattern_type.7ce = value_binding_pattern x [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %i32.loc16: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %x: %i32 = value_binding x, [concrete = ] // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %y.patt: %pattern_type.7ce = value_binding_pattern y [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %i32.loc24: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %y: %i32 = value_binding y, [concrete = ] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @A { // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %x.patt: %pattern_type.7ce = value_binding_pattern x [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %int_5.loc5: Core.IntLiteral = int_value 5 [concrete = constants.%int_5.64b] // CHECK:STDOUT: %i32.loc5: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %impl.elem0.loc5: %.545 = impl_witness_access constants.%ImplicitAs.impl_witness.6bc, element0 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5] // CHECK:STDOUT: %bound_method.loc5_26.1: = bound_method %int_5.loc5, %impl.elem0.loc5 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.bound] // CHECK:STDOUT: %specific_fn.loc5: = specific_function %impl.elem0.loc5, @Core.IntLiteral.as.ImplicitAs.impl.Convert(constants.%int_32) [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn] // CHECK:STDOUT: %bound_method.loc5_26.2: = bound_method %int_5.loc5, %specific_fn.loc5 [concrete = constants.%bound_method] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc5: init %i32 = call %bound_method.loc5_26.2(%int_5.loc5) [concrete = constants.%int_5.0f6] // CHECK:STDOUT: %.loc5_26.1: %i32 = value_of_initializer %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc5 [concrete = constants.%int_5.0f6] // CHECK:STDOUT: %.loc5_26.2: %i32 = converted %int_5.loc5, %.loc5_26.1 [concrete = constants.%int_5.0f6] // CHECK:STDOUT: %x: %i32 = value_binding x, %.loc5_26.2 // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %y.patt: %pattern_type.7ce = value_binding_pattern y [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %int_5.loc6: Core.IntLiteral = int_value 5 [concrete = constants.%int_5.64b] // CHECK:STDOUT: %i32.loc6: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %impl.elem0.loc6: %.545 = impl_witness_access constants.%ImplicitAs.impl_witness.6bc, element0 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5] // CHECK:STDOUT: %bound_method.loc6_24.1: = bound_method %int_5.loc6, %impl.elem0.loc6 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.bound] // CHECK:STDOUT: %specific_fn.loc6: = specific_function %impl.elem0.loc6, @Core.IntLiteral.as.ImplicitAs.impl.Convert(constants.%int_32) [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn] // CHECK:STDOUT: %bound_method.loc6_24.2: = bound_method %int_5.loc6, %specific_fn.loc6 [concrete = constants.%bound_method] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc6: init %i32 = call %bound_method.loc6_24.2(%int_5.loc6) [concrete = constants.%int_5.0f6] // CHECK:STDOUT: %.loc6_24.1: %i32 = value_of_initializer %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc6 [concrete = constants.%int_5.0f6] // CHECK:STDOUT: %.loc6_24.2: %i32 = converted %int_5.loc6, %.loc6_24.1 [concrete = constants.%int_5.0f6] // CHECK:STDOUT: %y: %i32 = value_binding y, %.loc6_24.2 // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type.357] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%A // CHECK:STDOUT: .x [protected] = %x // CHECK:STDOUT: .y [private] = %y // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @__global_init() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %A.ref.loc16: type = name_ref A, file.%A.decl [concrete = constants.%A] // CHECK:STDOUT: %x.ref: = name_ref x, [concrete = ] // CHECK:STDOUT: %A.ref.loc24: type = name_ref A, file.%A.decl [concrete = constants.%A] // CHECK:STDOUT: %y.ref: = name_ref y, [concrete = ] // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- self_access.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %A: type = class_type @A [concrete] // CHECK:STDOUT: %A.F.type: type = fn_type @A.F [concrete] // CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] // CHECK:STDOUT: %A.F: %A.F.type = struct_value () [concrete] // CHECK:STDOUT: %A.G.type: type = fn_type @A.G [concrete] // CHECK:STDOUT: %A.G: %A.G.type = struct_value () [concrete] // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %complete_type: = complete_type_witness %empty_struct_type [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .A = %A.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %A.decl: type = class_decl @A [concrete = constants.%A] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @A { // CHECK:STDOUT: %A.F.decl: %A.F.type = fn_decl @A.F [concrete = constants.%A.F] {} {} // CHECK:STDOUT: %A.G.decl: %A.G.type = fn_decl @A.G [concrete = constants.%A.G] {} {} // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%A // CHECK:STDOUT: .F [private] = %A.F.decl // CHECK:STDOUT: .G [private] = %A.G.decl // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @A.F() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @A.G() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %Self.ref: type = name_ref Self, constants.%A [concrete = constants.%A] // CHECK:STDOUT: %F.ref: %A.F.type = name_ref F, @A.%A.F.decl [concrete = constants.%A.F] // CHECK:STDOUT: %A.F.call: init %empty_tuple.type = call %F.ref() // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/class/access/import_access.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/none.carbon // TODO: Add ranges and switch to "--dump-sem-ir-ranges=only". // EXTRA-ARGS: --dump-sem-ir-ranges=if-present // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/class/access/import_access.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/class/access/import_access.carbon // ============================================================================ // Setup files // ============================================================================ // --- def.carbon package Test library "[[@TEST_NAME]]"; private class Def {} // --- forward_with_def.carbon package Test library "[[@TEST_NAME]]"; private class ForwardWithDef; class ForwardWithDef {} // --- forward.carbon package Test library "[[@TEST_NAME]]"; private class Forward; // ============================================================================ // Test files // ============================================================================ // --- def.impl.carbon impl package Test library "[[@TEST_NAME]]"; var c: Def = {}; // --- fail_local_def.carbon package Test library "[[@TEST_NAME]]"; import library "def"; // CHECK:STDERR: fail_local_def.carbon:[[@LINE+4]]:8: error: name `Def` not found [NameNotFound] // CHECK:STDERR: var c: Def = {}; // CHECK:STDERR: ^~~ // CHECK:STDERR: var c: Def = {}; // --- fail_other_def.carbon package Other library "[[@TEST_NAME]]"; import Test library "def"; // CHECK:STDERR: fail_other_def.carbon:[[@LINE+4]]:8: error: member name `Def` not found in `Test` [MemberNameNotFoundInInstScope] // CHECK:STDERR: var c: Test.Def = {}; // CHECK:STDERR: ^~~~~~~~ // CHECK:STDERR: var c: Test.Def = {}; // --- forward_with_def.impl.carbon impl package Test library "[[@TEST_NAME]]"; var c: ForwardWithDef = {}; // --- fail_local_forward_with_def.carbon package Test library "[[@TEST_NAME]]"; import library "forward_with_def"; // CHECK:STDERR: fail_local_forward_with_def.carbon:[[@LINE+4]]:8: error: name `ForwardWithDef` not found [NameNotFound] // CHECK:STDERR: var c: ForwardWithDef = {}; // CHECK:STDERR: ^~~~~~~~~~~~~~ // CHECK:STDERR: var c: ForwardWithDef = {}; // --- fail_other_forward_with_def.carbon package Other library "[[@TEST_NAME]]"; import Test library "forward_with_def"; // CHECK:STDERR: fail_other_forward_with_def.carbon:[[@LINE+4]]:8: error: member name `ForwardWithDef` not found in `Test` [MemberNameNotFoundInInstScope] // CHECK:STDERR: var c: Test.ForwardWithDef = {}; // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: var c: Test.ForwardWithDef = {}; // --- forward.impl.carbon impl package Test library "[[@TEST_NAME]]"; fn F(unused c: Forward*) {} class Forward {} // --- fail_local_forward.carbon package Test library "[[@TEST_NAME]]"; import library "forward"; // CHECK:STDERR: fail_local_forward.carbon:[[@LINE+4]]:16: error: name `Forward` not found [NameNotFound] // CHECK:STDERR: fn F(unused c: Forward*) {} // CHECK:STDERR: ^~~~~~~ // CHECK:STDERR: fn F(unused c: Forward*) {} // --- fail_other_forward.carbon package Other library "[[@TEST_NAME]]"; import Test library "forward"; // CHECK:STDERR: fail_other_forward.carbon:[[@LINE+4]]:16: error: member name `Forward` not found in `Test` [MemberNameNotFoundInInstScope] // CHECK:STDERR: fn F(unused c: Test.Forward*) {} // CHECK:STDERR: ^~~~~~~~~~~~ // CHECK:STDERR: fn F(unused c: Test.Forward*) {} // --- todo_fail_private_on_redecl.carbon library "[[@TEST_NAME]]"; private class Redecl; private class Redecl {} // CHECK:STDOUT: --- def.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %Def: type = class_type @Def [concrete] // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %complete_type: = complete_type_witness %empty_struct_type [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Def [private] = %Def.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Def.decl: type = class_decl @Def [concrete = constants.%Def] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @Def { // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%Def // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- forward_with_def.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %ForwardWithDef: type = class_type @ForwardWithDef [concrete] // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %complete_type: = complete_type_witness %empty_struct_type [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .ForwardWithDef [private] = %ForwardWithDef.decl.loc4 // CHECK:STDOUT: } // CHECK:STDOUT: %ForwardWithDef.decl.loc4: type = class_decl @ForwardWithDef [concrete = constants.%ForwardWithDef] {} {} // CHECK:STDOUT: %ForwardWithDef.decl.loc6: type = class_decl @ForwardWithDef [concrete = constants.%ForwardWithDef] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @ForwardWithDef { // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%ForwardWithDef // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- forward.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %Forward: type = class_type @Forward [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Forward [private] = %Forward.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Forward.decl: type = class_decl @Forward [concrete = constants.%Forward] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @Forward; // CHECK:STDOUT: // CHECK:STDOUT: --- def.impl.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %Def: type = class_type @Def [concrete] // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %complete_type: = complete_type_witness %empty_struct_type [concrete] // CHECK:STDOUT: %pattern_type: type = pattern_type %Def [concrete] // CHECK:STDOUT: %empty_struct: %empty_struct_type = struct_value () [concrete] // CHECK:STDOUT: %Def.val: %Def = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Test.Def: type = import_ref Test//def, Def, loaded [concrete = constants.%Def] // CHECK:STDOUT: %Test.import_ref.8f2: = import_ref Test//def, loc4_20, loaded [concrete = constants.%complete_type] // CHECK:STDOUT: %Test.import_ref.390 = import_ref Test//def, inst{{[0-9A-F]+}} [no loc], unloaded // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Def [private] = imports.%Test.Def // CHECK:STDOUT: .c = %c // CHECK:STDOUT: } // CHECK:STDOUT: %Test.import = import Test // CHECK:STDOUT: %default.import = import // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %c.patt: %pattern_type = ref_binding_pattern c [concrete] // CHECK:STDOUT: %c.var_patt: %pattern_type = var_pattern %c.patt [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %c.var: ref %Def = var %c.var_patt [concrete] // CHECK:STDOUT: %Def.ref: type = name_ref Def, imports.%Test.Def [concrete = constants.%Def] // CHECK:STDOUT: %c: ref %Def = ref_binding c, %c.var [concrete = %c.var] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @Def [from "def.carbon"] { // CHECK:STDOUT: complete_type_witness = imports.%Test.import_ref.8f2 // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = imports.%Test.import_ref.390 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @__global_init() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %.loc4_15.1: %empty_struct_type = struct_literal () [concrete = constants.%empty_struct] // CHECK:STDOUT: %.loc4_15.2: init %Def to file.%c.var = class_init () [concrete = constants.%Def.val] // CHECK:STDOUT: %.loc4_1: init %Def = converted %.loc4_15.1, %.loc4_15.2 [concrete = constants.%Def.val] // CHECK:STDOUT: assign file.%c.var, %.loc4_1 // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- fail_local_def.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %empty_struct: %empty_struct_type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Def = // CHECK:STDOUT: .c = %c // CHECK:STDOUT: } // CHECK:STDOUT: %default.import = import // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %c.patt: = ref_binding_pattern c [concrete] // CHECK:STDOUT: %c.var_patt: = var_pattern %c.patt [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %c.var: ref = var %c.var_patt [concrete = ] // CHECK:STDOUT: %Def.ref: = name_ref Def, [concrete = ] // CHECK:STDOUT: %c: ref = ref_binding c, [concrete = ] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @__global_init() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %.loc10: %empty_struct_type = struct_literal () [concrete = constants.%empty_struct] // CHECK:STDOUT: assign file.%c.var, // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- fail_other_def.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %empty_struct: %empty_struct_type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Test: = namespace file.%Test.import, [concrete] { // CHECK:STDOUT: .Def = // CHECK:STDOUT: import Test//def // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Test = imports.%Test // CHECK:STDOUT: .c = %c // CHECK:STDOUT: } // CHECK:STDOUT: %Test.import = import Test // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %c.patt: = ref_binding_pattern c [concrete] // CHECK:STDOUT: %c.var_patt: = var_pattern %c.patt [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %c.var: ref = var %c.var_patt [concrete = ] // CHECK:STDOUT: %.1: = splice_block [concrete = ] { // CHECK:STDOUT: %Test.ref: = name_ref Test, imports.%Test [concrete = imports.%Test] // CHECK:STDOUT: %Def.ref: = name_ref Def, [concrete = ] // CHECK:STDOUT: } // CHECK:STDOUT: %c: ref = ref_binding c, [concrete = ] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @__global_init() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %.loc10: %empty_struct_type = struct_literal () [concrete = constants.%empty_struct] // CHECK:STDOUT: assign file.%c.var, // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- forward_with_def.impl.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %ForwardWithDef: type = class_type @ForwardWithDef [concrete] // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %complete_type: = complete_type_witness %empty_struct_type [concrete] // CHECK:STDOUT: %pattern_type: type = pattern_type %ForwardWithDef [concrete] // CHECK:STDOUT: %empty_struct: %empty_struct_type = struct_value () [concrete] // CHECK:STDOUT: %ForwardWithDef.val: %ForwardWithDef = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Test.ForwardWithDef: type = import_ref Test//forward_with_def, ForwardWithDef, loaded [concrete = constants.%ForwardWithDef] // CHECK:STDOUT: %Test.import_ref.8f2: = import_ref Test//forward_with_def, loc6_23, loaded [concrete = constants.%complete_type] // CHECK:STDOUT: %Test.import_ref.623 = import_ref Test//forward_with_def, inst{{[0-9A-F]+}} [no loc], unloaded // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .ForwardWithDef [private] = imports.%Test.ForwardWithDef // CHECK:STDOUT: .c = %c // CHECK:STDOUT: } // CHECK:STDOUT: %Test.import = import Test // CHECK:STDOUT: %default.import = import // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %c.patt: %pattern_type = ref_binding_pattern c [concrete] // CHECK:STDOUT: %c.var_patt: %pattern_type = var_pattern %c.patt [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %c.var: ref %ForwardWithDef = var %c.var_patt [concrete] // CHECK:STDOUT: %ForwardWithDef.ref: type = name_ref ForwardWithDef, imports.%Test.ForwardWithDef [concrete = constants.%ForwardWithDef] // CHECK:STDOUT: %c: ref %ForwardWithDef = ref_binding c, %c.var [concrete = %c.var] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @ForwardWithDef [from "forward_with_def.carbon"] { // CHECK:STDOUT: complete_type_witness = imports.%Test.import_ref.8f2 // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = imports.%Test.import_ref.623 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @__global_init() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %.loc4_26.1: %empty_struct_type = struct_literal () [concrete = constants.%empty_struct] // CHECK:STDOUT: %.loc4_26.2: init %ForwardWithDef to file.%c.var = class_init () [concrete = constants.%ForwardWithDef.val] // CHECK:STDOUT: %.loc4_1: init %ForwardWithDef = converted %.loc4_26.1, %.loc4_26.2 [concrete = constants.%ForwardWithDef.val] // CHECK:STDOUT: assign file.%c.var, %.loc4_1 // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- fail_local_forward_with_def.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %empty_struct: %empty_struct_type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .ForwardWithDef = // CHECK:STDOUT: .c = %c // CHECK:STDOUT: } // CHECK:STDOUT: %default.import = import // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %c.patt: = ref_binding_pattern c [concrete] // CHECK:STDOUT: %c.var_patt: = var_pattern %c.patt [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %c.var: ref = var %c.var_patt [concrete = ] // CHECK:STDOUT: %ForwardWithDef.ref: = name_ref ForwardWithDef, [concrete = ] // CHECK:STDOUT: %c: ref = ref_binding c, [concrete = ] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @__global_init() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %.loc10: %empty_struct_type = struct_literal () [concrete = constants.%empty_struct] // CHECK:STDOUT: assign file.%c.var, // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- fail_other_forward_with_def.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %empty_struct: %empty_struct_type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Test: = namespace file.%Test.import, [concrete] { // CHECK:STDOUT: .ForwardWithDef = // CHECK:STDOUT: import Test//forward_with_def // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Test = imports.%Test // CHECK:STDOUT: .c = %c // CHECK:STDOUT: } // CHECK:STDOUT: %Test.import = import Test // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %c.patt: = ref_binding_pattern c [concrete] // CHECK:STDOUT: %c.var_patt: = var_pattern %c.patt [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %c.var: ref = var %c.var_patt [concrete = ] // CHECK:STDOUT: %.1: = splice_block [concrete = ] { // CHECK:STDOUT: %Test.ref: = name_ref Test, imports.%Test [concrete = imports.%Test] // CHECK:STDOUT: %ForwardWithDef.ref: = name_ref ForwardWithDef, [concrete = ] // CHECK:STDOUT: } // CHECK:STDOUT: %c: ref = ref_binding c, [concrete = ] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @__global_init() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %.loc10: %empty_struct_type = struct_literal () [concrete = constants.%empty_struct] // CHECK:STDOUT: assign file.%c.var, // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- forward.impl.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %Forward: type = class_type @Forward [concrete] // CHECK:STDOUT: %ptr: type = ptr_type %Forward [concrete] // CHECK:STDOUT: %pattern_type: type = pattern_type %ptr [concrete] // CHECK:STDOUT: %F.type: type = fn_type @F [concrete] // CHECK:STDOUT: %F: %F.type = struct_value () [concrete] // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %complete_type: = complete_type_witness %empty_struct_type [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Test.Forward: type = import_ref Test//forward, Forward, loaded [concrete = constants.%Forward] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Forward [private] = %Forward.decl // CHECK:STDOUT: .F = %F.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Test.import = import Test // CHECK:STDOUT: %default.import = import // CHECK:STDOUT: %F.decl: %F.type = fn_decl @F [concrete = constants.%F] { // CHECK:STDOUT: %c.patt: %pattern_type = value_binding_pattern c [concrete] // CHECK:STDOUT: %c.param_patt: %pattern_type = value_param_pattern %c.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %c.param: %ptr = value_param call_param0 // CHECK:STDOUT: %.loc4: type = splice_block %ptr [concrete = constants.%ptr] { // CHECK:STDOUT: %Forward.ref: type = name_ref Forward, imports.%Test.Forward [concrete = constants.%Forward] // CHECK:STDOUT: %ptr: type = ptr_type %Forward.ref [concrete = constants.%ptr] // CHECK:STDOUT: } // CHECK:STDOUT: %c: %ptr = value_binding c, %c.param // CHECK:STDOUT: } // CHECK:STDOUT: %Forward.decl: type = class_decl @Forward [concrete = constants.%Forward] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @Forward { // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%Forward // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @F(%c.param: %ptr) { // CHECK:STDOUT: !entry: // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- fail_local_forward.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %F.type: type = fn_type @F [concrete] // CHECK:STDOUT: %F: %F.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Forward = // CHECK:STDOUT: .F = %F.decl // CHECK:STDOUT: } // CHECK:STDOUT: %default.import = import // CHECK:STDOUT: %F.decl: %F.type = fn_decl @F [concrete = constants.%F] { // CHECK:STDOUT: %c.patt: = value_binding_pattern c [concrete] // CHECK:STDOUT: %c.param_patt: = value_param_pattern %c.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %c.param: = value_param call_param0 // CHECK:STDOUT: %.loc10: type = splice_block %ptr [concrete = ] { // CHECK:STDOUT: %Forward.ref: = name_ref Forward, [concrete = ] // CHECK:STDOUT: %ptr: type = ptr_type [concrete = ] // CHECK:STDOUT: } // CHECK:STDOUT: %c: = value_binding c, %c.param // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @F(%c.param: ) { // CHECK:STDOUT: !entry: // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- fail_other_forward.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %F.type: type = fn_type @F [concrete] // CHECK:STDOUT: %F: %F.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Test: = namespace file.%Test.import, [concrete] { // CHECK:STDOUT: .Forward = // CHECK:STDOUT: import Test//forward // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Test = imports.%Test // CHECK:STDOUT: .F = %F.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Test.import = import Test // CHECK:STDOUT: %F.decl: %F.type = fn_decl @F [concrete = constants.%F] { // CHECK:STDOUT: %c.patt: = value_binding_pattern c [concrete] // CHECK:STDOUT: %c.param_patt: = value_param_pattern %c.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %c.param: = value_param call_param0 // CHECK:STDOUT: %.loc10: type = splice_block %ptr [concrete = ] { // CHECK:STDOUT: %Test.ref: = name_ref Test, imports.%Test [concrete = imports.%Test] // CHECK:STDOUT: %Forward.ref: = name_ref Forward, [concrete = ] // CHECK:STDOUT: %ptr: type = ptr_type [concrete = ] // CHECK:STDOUT: } // CHECK:STDOUT: %c: = value_binding c, %c.param // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @F(%c.param: ) { // CHECK:STDOUT: !entry: // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- todo_fail_private_on_redecl.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %Redecl: type = class_type @Redecl [concrete] // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %complete_type: = complete_type_witness %empty_struct_type [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Redecl [private] = %Redecl.decl.loc4 // CHECK:STDOUT: } // CHECK:STDOUT: %Redecl.decl.loc4: type = class_decl @Redecl [concrete = constants.%Redecl] {} {} // CHECK:STDOUT: %Redecl.decl.loc6: type = class_decl @Redecl [concrete = constants.%Redecl] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @Redecl { // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%Redecl // CHECK:STDOUT: } // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/class/access/inheritance_access.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/int.carbon // TODO: Add ranges and switch to "--dump-sem-ir-ranges=only". // EXTRA-ARGS: --dump-sem-ir-ranges=if-present // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/class/access/inheritance_access.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/class/access/inheritance_access.carbon // --- instance_protected_field_access.carbon library "[[@TEST_NAME]]"; base class Shape { protected var x: i32; protected var y: i32; } class Circle { extend base: Shape; fn GetPosition[self: Self]() -> (i32, i32) { return (self.x, self.y); } } // --- shadowing_access.carbon library "[[@TEST_NAME]]"; base class A { fn F(); } base class B { extend base: A; private fn F() -> i32; } base class C { extend base: B; fn G[self: Self]() -> () { return self.F(); } } // --- inherited_member_access.carbon library "[[@TEST_NAME]]"; base class A { protected let SOME_CONSTANT: i32 = 5; protected fn SomeProtectedFunction() -> i32 { return 5; } } class B { extend base: A; fn G() -> i32 { return A.SOME_CONSTANT; } fn H() -> i32 { return A.SomeProtectedFunction(); } } // --- fail_inherited_private_field_access.carbon library "[[@TEST_NAME]]"; base class Shape { private var y: i32; } class Square { extend base: Shape; fn GetPosition[self: Self]() -> i32 { // CHECK:STDERR: fail_inherited_private_field_access.carbon:[[@LINE+7]]:12: error: cannot access private member `y` of type `Shape` [ClassInvalidMemberAccess] // CHECK:STDERR: return self.y; // CHECK:STDERR: ^~~~~~ // CHECK:STDERR: fail_inherited_private_field_access.carbon:[[@LINE-10]]:15: note: declared here [ClassMemberDeclaration] // CHECK:STDERR: private var y: i32; // CHECK:STDERR: ^~~~~~ // CHECK:STDERR: return self.y; } } // --- noninstance_private_on_self.carbon library "[[@TEST_NAME]]"; class C { private fn F() {} fn G() { Self.F(); F(); } } // --- noninstance_protected_on_self.carbon library "[[@TEST_NAME]]"; class C { protected fn F() {} fn G() { Self.F(); F(); } } // --- fail_noninstance_private_on_parent_qualified.carbon library "[[@TEST_NAME]]"; base class B { private fn F() {} } class C { extend base: B; // CHECK:STDERR: fail_noninstance_private_on_parent_qualified.carbon:[[@LINE+7]]:12: error: cannot access private member `F` of type `B` [ClassInvalidMemberAccess] // CHECK:STDERR: fn G() { Self.F(); } // CHECK:STDERR: ^~~~~~ // CHECK:STDERR: fail_noninstance_private_on_parent_qualified.carbon:[[@LINE-8]]:3: note: declared here [ClassMemberDeclaration] // CHECK:STDERR: private fn F() {} // CHECK:STDERR: ^~~~~~~~~~~~~~~~ // CHECK:STDERR: fn G() { Self.F(); } } // --- fail_noninstance_private_on_parent_unqualified.carbon library "[[@TEST_NAME]]"; base class A { private fn Fa() {} } base class B { extend base: A; private let SOME_PRIVATE_CONSTANT: i32 = 86; private fn Fb() {} } class C { extend base: B; // CHECK:STDERR: fail_noninstance_private_on_parent_unqualified.carbon:[[@LINE+7]]:27: error: cannot access private member `SOME_PRIVATE_CONSTANT` of type `B` [ClassInvalidMemberAccess] // CHECK:STDERR: fn G1() -> i32 { return SOME_PRIVATE_CONSTANT; } // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: fail_noninstance_private_on_parent_unqualified.carbon:[[@LINE-10]]:15: note: declared here [ClassMemberDeclaration] // CHECK:STDERR: private let SOME_PRIVATE_CONSTANT: i32 = 86; // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: fn G1() -> i32 { return SOME_PRIVATE_CONSTANT; } // CHECK:STDERR: fail_noninstance_private_on_parent_unqualified.carbon:[[@LINE+7]]:13: error: cannot access private member `Fa` of type `A` [ClassInvalidMemberAccess] // CHECK:STDERR: fn G2() { Fa(); } // CHECK:STDERR: ^~ // CHECK:STDERR: fail_noninstance_private_on_parent_unqualified.carbon:[[@LINE-25]]:3: note: declared here [ClassMemberDeclaration] // CHECK:STDERR: private fn Fa() {} // CHECK:STDERR: ^~~~~~~~~~~~~~~~~ // CHECK:STDERR: fn G2() { Fa(); } // CHECK:STDERR: fail_noninstance_private_on_parent_unqualified.carbon:[[@LINE+7]]:13: error: cannot access private member `Fb` of type `B` [ClassInvalidMemberAccess] // CHECK:STDERR: fn G3() { Fb(); } // CHECK:STDERR: ^~ // CHECK:STDERR: fail_noninstance_private_on_parent_unqualified.carbon:[[@LINE-27]]:3: note: declared here [ClassMemberDeclaration] // CHECK:STDERR: private fn Fb() {} // CHECK:STDERR: ^~~~~~~~~~~~~~~~~ // CHECK:STDERR: fn G3() { Fb(); } } // --- noninstance_protected_on_parent.carbon library "[[@TEST_NAME]]"; base class B { protected fn F() {} } class C { extend base: B; fn G() { Self.F(); } } // --- fail_non_inherited_access.carbon library "[[@TEST_NAME]]"; base class A { protected let SOME_PROTECTED_CONSTANT: i32 = 5; private let SOME_PRIVATE_CONSTANT: i32 = 5; } base class Internal { protected let INTERNAL_CONSTANT: i32 = 5; } class B { private var internal: Internal; fn G() -> i32 { // CHECK:STDERR: fail_non_inherited_access.carbon:[[@LINE+7]]:5: error: cannot access private member `SOME_PRIVATE_CONSTANT` of type `A` [ClassInvalidMemberAccess] // CHECK:STDERR: A.SOME_PRIVATE_CONSTANT; // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: fail_non_inherited_access.carbon:[[@LINE-14]]:15: note: declared here [ClassMemberDeclaration] // CHECK:STDERR: private let SOME_PRIVATE_CONSTANT: i32 = 5; // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: A.SOME_PRIVATE_CONSTANT; // CHECK:STDERR: fail_non_inherited_access.carbon:[[@LINE+7]]:12: error: cannot access protected member `SOME_PROTECTED_CONSTANT` of type `A` [ClassInvalidMemberAccess] // CHECK:STDERR: return A.SOME_PROTECTED_CONSTANT; // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: fail_non_inherited_access.carbon:[[@LINE-24]]:17: note: declared here [ClassMemberDeclaration] // CHECK:STDERR: protected let SOME_PROTECTED_CONSTANT: i32 = 5; // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: return A.SOME_PROTECTED_CONSTANT; } fn SomeFunc[self: Self]() -> i32{ // CHECK:STDERR: fail_non_inherited_access.carbon:[[@LINE+7]]:12: error: cannot access protected member `INTERNAL_CONSTANT` of type `Internal` [ClassInvalidMemberAccess] // CHECK:STDERR: return self.internal.INTERNAL_CONSTANT; // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: fail_non_inherited_access.carbon:[[@LINE-30]]:17: note: declared here [ClassMemberDeclaration] // CHECK:STDERR: protected let INTERNAL_CONSTANT: i32 = 5; // CHECK:STDERR: ^~~~~~~~~~~~~~~~~ // CHECK:STDERR: return self.internal.INTERNAL_CONSTANT; } } // --- fail_compound_member_access.carbon library "[[@TEST_NAME]]"; base class A { private var x: i32; } class B { extend base: A; fn F[self: Self]() { // CHECK:STDERR: fail_compound_member_access.carbon:[[@LINE+7]]:11: error: cannot access private member `x` of type `A` [ClassInvalidMemberAccess] // CHECK:STDERR: self.(A.x); // CHECK:STDERR: ^~~ // CHECK:STDERR: fail_compound_member_access.carbon:[[@LINE-9]]:15: note: declared here [ClassMemberDeclaration] // CHECK:STDERR: private var x: i32; // CHECK:STDERR: ^~~~~~ // CHECK:STDERR: self.(A.x); } } // --- inherited_compound_member_access.carbon library "[[@TEST_NAME]]"; base class A { protected var x: i32; } class B { extend base: A; fn F[self: Self]() { self.(A.x); } } // CHECK:STDOUT: --- instance_protected_field_access.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %Shape: type = class_type @Shape [concrete] // CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [concrete] // CHECK:STDOUT: %Int.type: type = generic_class_type @Int [concrete] // CHECK:STDOUT: %Int.generic: %Int.type = struct_value () [concrete] // CHECK:STDOUT: %N: Core.IntLiteral = symbolic_binding N, 0 [symbolic] // CHECK:STDOUT: %i32: type = class_type @Int, @Int(%int_32) [concrete] // CHECK:STDOUT: %Shape.elem: type = unbound_element_type %Shape, %i32 [concrete] // CHECK:STDOUT: %struct_type.x.y: type = struct_type {.x: %i32, .y: %i32} [concrete] // CHECK:STDOUT: %complete_type.70a: = complete_type_witness %struct_type.x.y [concrete] // CHECK:STDOUT: %Circle: type = class_type @Circle [concrete] // CHECK:STDOUT: %Circle.elem: type = unbound_element_type %Circle, %Shape [concrete] // CHECK:STDOUT: %pattern_type.fcb: type = pattern_type %Circle [concrete] // CHECK:STDOUT: %tuple.type.24b: type = tuple_type (type, type) [concrete] // CHECK:STDOUT: %tuple.95a: %tuple.type.24b = tuple_value (%i32, %i32) [concrete] // CHECK:STDOUT: %tuple.type.d07: type = tuple_type (%i32, %i32) [concrete] // CHECK:STDOUT: %.f32: Core.Form = init_form %tuple.type.d07 [concrete] // CHECK:STDOUT: %pattern_type.511: type = pattern_type %tuple.type.d07 [concrete] // CHECK:STDOUT: %Circle.GetPosition.type: type = fn_type @Circle.GetPosition [concrete] // CHECK:STDOUT: %Circle.GetPosition: %Circle.GetPosition.type = struct_value () [concrete] // CHECK:STDOUT: %struct_type.base.490: type = struct_type {.base: %Shape} [concrete] // CHECK:STDOUT: %complete_type.560: = complete_type_witness %struct_type.base.490 [concrete] // CHECK:STDOUT: %Copy.type: type = facet_type <@Copy> [concrete] // CHECK:STDOUT: %Int.as.Copy.impl.Op.type.824: type = fn_type @Int.as.Copy.impl.Op, @Int.as.Copy.impl(%N) [symbolic] // CHECK:STDOUT: %Int.as.Copy.impl.Op.9b9: %Int.as.Copy.impl.Op.type.824 = struct_value () [symbolic] // CHECK:STDOUT: %Copy.impl_witness.f17: = impl_witness imports.%Copy.impl_witness_table.e76, @Int.as.Copy.impl(%int_32) [concrete] // CHECK:STDOUT: %Int.as.Copy.impl.Op.type.546: type = fn_type @Int.as.Copy.impl.Op, @Int.as.Copy.impl(%int_32) [concrete] // CHECK:STDOUT: %Int.as.Copy.impl.Op.664: %Int.as.Copy.impl.Op.type.546 = struct_value () [concrete] // CHECK:STDOUT: %Copy.facet: %Copy.type = facet_value %i32, (%Copy.impl_witness.f17) [concrete] // CHECK:STDOUT: %Copy.WithSelf.Op.type.081: type = fn_type @Copy.WithSelf.Op, @Copy.WithSelf(%Copy.facet) [concrete] // CHECK:STDOUT: %.8e2: type = fn_type_with_self_type %Copy.WithSelf.Op.type.081, %Copy.facet [concrete] // CHECK:STDOUT: %Int.as.Copy.impl.Op.specific_fn: = specific_function %Int.as.Copy.impl.Op.664, @Int.as.Copy.impl.Op(%int_32) [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: .Int = %Core.Int // CHECK:STDOUT: .Copy = %Core.Copy // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: %Core.Int: %Int.type = import_ref Core//prelude/parts/int, Int, loaded [concrete = constants.%Int.generic] // CHECK:STDOUT: %Core.Copy: type = import_ref Core//prelude/parts/copy, Copy, loaded [concrete = constants.%Copy.type] // CHECK:STDOUT: %Core.import_ref.18d: @Int.as.Copy.impl.%Int.as.Copy.impl.Op.type (%Int.as.Copy.impl.Op.type.824) = import_ref Core//prelude/parts/int, loc{{\d+_\d+}}, loaded [symbolic = @Int.as.Copy.impl.%Int.as.Copy.impl.Op (constants.%Int.as.Copy.impl.Op.9b9)] // CHECK:STDOUT: %Copy.impl_witness_table.e76 = impl_witness_table (%Core.import_ref.18d), @Int.as.Copy.impl [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .Shape = %Shape.decl // CHECK:STDOUT: .Circle = %Circle.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %Shape.decl: type = class_decl @Shape [concrete = constants.%Shape] {} {} // CHECK:STDOUT: %Circle.decl: type = class_decl @Circle [concrete = constants.%Circle] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @Shape { // CHECK:STDOUT: %i32.loc5: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %.loc5: %Shape.elem = field_decl x, element0 [concrete] // CHECK:STDOUT: %i32.loc6: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %.loc6: %Shape.elem = field_decl y, element1 [concrete] // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%struct_type.x.y [concrete = constants.%complete_type.70a] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%Shape // CHECK:STDOUT: .x [protected] = %.loc5 // CHECK:STDOUT: .y [protected] = %.loc6 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @Circle { // CHECK:STDOUT: %Shape.ref: type = name_ref Shape, file.%Shape.decl [concrete = constants.%Shape] // CHECK:STDOUT: %.loc10: %Circle.elem = base_decl %Shape.ref, element0 [concrete] // CHECK:STDOUT: %Circle.GetPosition.decl: %Circle.GetPosition.type = fn_decl @Circle.GetPosition [concrete = constants.%Circle.GetPosition] { // CHECK:STDOUT: %self.patt: %pattern_type.fcb = value_binding_pattern self [concrete] // CHECK:STDOUT: %self.param_patt: %pattern_type.fcb = value_param_pattern %self.patt [concrete] // CHECK:STDOUT: %return.patt: %pattern_type.511 = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: %pattern_type.511 = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %i32.loc12_36: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %i32.loc12_41: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %.loc12_44.1: %tuple.type.24b = tuple_literal (%i32.loc12_36, %i32.loc12_41) [concrete = constants.%tuple.95a] // CHECK:STDOUT: %.loc12_44.2: type = converted %.loc12_44.1, constants.%tuple.type.d07 [concrete = constants.%tuple.type.d07] // CHECK:STDOUT: %.loc12_44.3: Core.Form = init_form %.loc12_44.2 [concrete = constants.%.f32] // CHECK:STDOUT: %self.param: %Circle = value_param call_param0 // CHECK:STDOUT: %Self.ref: type = name_ref Self, constants.%Circle [concrete = constants.%Circle] // CHECK:STDOUT: %self: %Circle = value_binding self, %self.param // CHECK:STDOUT: %return.param: ref %tuple.type.d07 = out_param call_param1 // CHECK:STDOUT: %return: ref %tuple.type.d07 = return_slot %return.param // CHECK:STDOUT: } // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%struct_type.base.490 [concrete = constants.%complete_type.560] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%Circle // CHECK:STDOUT: .Shape = // CHECK:STDOUT: .base = %.loc10 // CHECK:STDOUT: .GetPosition = %Circle.GetPosition.decl // CHECK:STDOUT: .x = // CHECK:STDOUT: .y = // CHECK:STDOUT: extend %Shape.ref // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @Circle.GetPosition(%self.param: %Circle) -> out %return.param: %tuple.type.d07 { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %self.ref.loc13_13: %Circle = name_ref self, %self // CHECK:STDOUT: %x.ref: %Shape.elem = name_ref x, @Shape.%.loc5 [concrete = @Shape.%.loc5] // CHECK:STDOUT: %.loc13_17.1: ref %Shape = class_element_access %self.ref.loc13_13, element0 // CHECK:STDOUT: %.loc13_17.2: ref %Shape = converted %self.ref.loc13_13, %.loc13_17.1 // CHECK:STDOUT: %.loc13_17.3: ref %i32 = class_element_access %.loc13_17.2, element0 // CHECK:STDOUT: %self.ref.loc13_21: %Circle = name_ref self, %self // CHECK:STDOUT: %y.ref: %Shape.elem = name_ref y, @Shape.%.loc6 [concrete = @Shape.%.loc6] // CHECK:STDOUT: %.loc13_25.1: ref %Shape = class_element_access %self.ref.loc13_21, element0 // CHECK:STDOUT: %.loc13_25.2: ref %Shape = converted %self.ref.loc13_21, %.loc13_25.1 // CHECK:STDOUT: %.loc13_25.3: ref %i32 = class_element_access %.loc13_25.2, element1 // CHECK:STDOUT: %.loc13_27.1: %tuple.type.d07 = tuple_literal (%.loc13_17.3, %.loc13_25.3) // CHECK:STDOUT: %.loc13_17.4: %i32 = acquire_value %.loc13_17.3 // CHECK:STDOUT: %impl.elem0.loc13_17: %.8e2 = impl_witness_access constants.%Copy.impl_witness.f17, element0 [concrete = constants.%Int.as.Copy.impl.Op.664] // CHECK:STDOUT: %bound_method.loc13_17.1: = bound_method %.loc13_17.4, %impl.elem0.loc13_17 // CHECK:STDOUT: %specific_fn.loc13_17: = specific_function %impl.elem0.loc13_17, @Int.as.Copy.impl.Op(constants.%int_32) [concrete = constants.%Int.as.Copy.impl.Op.specific_fn] // CHECK:STDOUT: %bound_method.loc13_17.2: = bound_method %.loc13_17.4, %specific_fn.loc13_17 // CHECK:STDOUT: %Int.as.Copy.impl.Op.call.loc13_17: init %i32 = call %bound_method.loc13_17.2(%.loc13_17.4) // CHECK:STDOUT: %tuple.elem0: ref %i32 = tuple_access %return.param, element0 // CHECK:STDOUT: %.loc13_27.2: init %i32 to %tuple.elem0 = in_place_init %Int.as.Copy.impl.Op.call.loc13_17 // CHECK:STDOUT: %.loc13_25.4: %i32 = acquire_value %.loc13_25.3 // CHECK:STDOUT: %impl.elem0.loc13_25: %.8e2 = impl_witness_access constants.%Copy.impl_witness.f17, element0 [concrete = constants.%Int.as.Copy.impl.Op.664] // CHECK:STDOUT: %bound_method.loc13_25.1: = bound_method %.loc13_25.4, %impl.elem0.loc13_25 // CHECK:STDOUT: %specific_fn.loc13_25: = specific_function %impl.elem0.loc13_25, @Int.as.Copy.impl.Op(constants.%int_32) [concrete = constants.%Int.as.Copy.impl.Op.specific_fn] // CHECK:STDOUT: %bound_method.loc13_25.2: = bound_method %.loc13_25.4, %specific_fn.loc13_25 // CHECK:STDOUT: %Int.as.Copy.impl.Op.call.loc13_25: init %i32 = call %bound_method.loc13_25.2(%.loc13_25.4) // CHECK:STDOUT: %tuple.elem1: ref %i32 = tuple_access %return.param, element1 // CHECK:STDOUT: %.loc13_27.3: init %i32 to %tuple.elem1 = in_place_init %Int.as.Copy.impl.Op.call.loc13_25 // CHECK:STDOUT: %.loc13_27.4: init %tuple.type.d07 to %return.param = tuple_init (%.loc13_27.2, %.loc13_27.3) // CHECK:STDOUT: %.loc13_28: init %tuple.type.d07 = converted %.loc13_27.1, %.loc13_27.4 // CHECK:STDOUT: return %.loc13_28 to %return.param // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- shadowing_access.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %A: type = class_type @A [concrete] // CHECK:STDOUT: %A.F.type: type = fn_type @A.F [concrete] // CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] // CHECK:STDOUT: %A.F: %A.F.type = struct_value () [concrete] // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %complete_type.357: = complete_type_witness %empty_struct_type [concrete] // CHECK:STDOUT: %B: type = class_type @B [concrete] // CHECK:STDOUT: %B.elem: type = unbound_element_type %B, %A [concrete] // CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [concrete] // CHECK:STDOUT: %Int.type: type = generic_class_type @Int [concrete] // CHECK:STDOUT: %Int.generic: %Int.type = struct_value () [concrete] // CHECK:STDOUT: %i32: type = class_type @Int, @Int(%int_32) [concrete] // CHECK:STDOUT: %.ff5: Core.Form = init_form %i32 [concrete] // CHECK:STDOUT: %pattern_type.7ce: type = pattern_type %i32 [concrete] // CHECK:STDOUT: %B.F.type: type = fn_type @B.F [concrete] // CHECK:STDOUT: %B.F: %B.F.type = struct_value () [concrete] // CHECK:STDOUT: %struct_type.base.5af: type = struct_type {.base: %A} [concrete] // CHECK:STDOUT: %complete_type.0d1: = complete_type_witness %struct_type.base.5af [concrete] // CHECK:STDOUT: %C: type = class_type @C [concrete] // CHECK:STDOUT: %C.elem: type = unbound_element_type %C, %B [concrete] // CHECK:STDOUT: %pattern_type.7c7: type = pattern_type %C [concrete] // CHECK:STDOUT: %empty_tuple: %empty_tuple.type = tuple_value () [concrete] // CHECK:STDOUT: %.262: Core.Form = init_form %empty_tuple.type [concrete] // CHECK:STDOUT: %pattern_type.cb1: type = pattern_type %empty_tuple.type [concrete] // CHECK:STDOUT: %C.G.type: type = fn_type @C.G [concrete] // CHECK:STDOUT: %C.G: %C.G.type = struct_value () [concrete] // CHECK:STDOUT: %struct_type.base.64a: type = struct_type {.base: %B} [concrete] // CHECK:STDOUT: %complete_type.021: = complete_type_witness %struct_type.base.64a [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: .Int = %Core.Int // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: %Core.Int: %Int.type = import_ref Core//prelude/parts/int, Int, loaded [concrete = constants.%Int.generic] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .A = %A.decl // CHECK:STDOUT: .B = %B.decl // CHECK:STDOUT: .C = %C.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %A.decl: type = class_decl @A [concrete = constants.%A] {} {} // CHECK:STDOUT: %B.decl: type = class_decl @B [concrete = constants.%B] {} {} // CHECK:STDOUT: %C.decl: type = class_decl @C [concrete = constants.%C] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @A { // CHECK:STDOUT: %A.F.decl: %A.F.type = fn_decl @A.F [concrete = constants.%A.F] {} {} // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type.357] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%A // CHECK:STDOUT: .F = %A.F.decl // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @B { // CHECK:STDOUT: %A.ref: type = name_ref A, file.%A.decl [concrete = constants.%A] // CHECK:STDOUT: %.loc9: %B.elem = base_decl %A.ref, element0 [concrete] // CHECK:STDOUT: %B.F.decl: %B.F.type = fn_decl @B.F [concrete = constants.%B.F] { // CHECK:STDOUT: %return.patt: %pattern_type.7ce = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: %pattern_type.7ce = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %i32: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %.loc10: Core.Form = init_form %i32 [concrete = constants.%.ff5] // CHECK:STDOUT: %return.param: ref %i32 = out_param call_param0 // CHECK:STDOUT: %return: ref %i32 = return_slot %return.param // CHECK:STDOUT: } // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%struct_type.base.5af [concrete = constants.%complete_type.0d1] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%B // CHECK:STDOUT: .A = // CHECK:STDOUT: .base = %.loc9 // CHECK:STDOUT: .F [private] = %B.F.decl // CHECK:STDOUT: extend %A.ref // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @C { // CHECK:STDOUT: %B.ref: type = name_ref B, file.%B.decl [concrete = constants.%B] // CHECK:STDOUT: %.loc14: %C.elem = base_decl %B.ref, element0 [concrete] // CHECK:STDOUT: %C.G.decl: %C.G.type = fn_decl @C.G [concrete = constants.%C.G] { // CHECK:STDOUT: %self.patt: %pattern_type.7c7 = value_binding_pattern self [concrete] // CHECK:STDOUT: %self.param_patt: %pattern_type.7c7 = value_param_pattern %self.patt [concrete] // CHECK:STDOUT: %return.patt: %pattern_type.cb1 = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: %pattern_type.cb1 = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %.loc15_26.1: %empty_tuple.type = tuple_literal () [concrete = constants.%empty_tuple] // CHECK:STDOUT: %.loc15_26.2: type = converted %.loc15_26.1, constants.%empty_tuple.type [concrete = constants.%empty_tuple.type] // CHECK:STDOUT: %.loc15_26.3: Core.Form = init_form %.loc15_26.2 [concrete = constants.%.262] // CHECK:STDOUT: %self.param: %C = value_param call_param0 // CHECK:STDOUT: %Self.ref: type = name_ref Self, constants.%C [concrete = constants.%C] // CHECK:STDOUT: %self: %C = value_binding self, %self.param // CHECK:STDOUT: %return.param: ref %empty_tuple.type = out_param call_param1 // CHECK:STDOUT: %return: ref %empty_tuple.type = return_slot %return.param // CHECK:STDOUT: } // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%struct_type.base.64a [concrete = constants.%complete_type.021] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%C // CHECK:STDOUT: .B = // CHECK:STDOUT: .base = %.loc14 // CHECK:STDOUT: .G = %C.G.decl // CHECK:STDOUT: .F = // CHECK:STDOUT: extend %B.ref // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @A.F(); // CHECK:STDOUT: // CHECK:STDOUT: fn @B.F() -> out %return.param: %i32; // CHECK:STDOUT: // CHECK:STDOUT: fn @C.G(%self.param: %C) -> out %return.param: %empty_tuple.type { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %self.ref: %C = name_ref self, %self // CHECK:STDOUT: %F.ref: %A.F.type = name_ref F, @A.%A.F.decl [concrete = constants.%A.F] // CHECK:STDOUT: %A.F.call: init %empty_tuple.type = call %F.ref() // CHECK:STDOUT: return %A.F.call // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- inherited_member_access.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %A: type = class_type @A [concrete] // CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [concrete] // CHECK:STDOUT: %Int.type: type = generic_class_type @Int [concrete] // CHECK:STDOUT: %Int.generic: %Int.type = struct_value () [concrete] // CHECK:STDOUT: %N: Core.IntLiteral = symbolic_binding N, 0 [symbolic] // CHECK:STDOUT: %i32: type = class_type @Int, @Int(%int_32) [concrete] // CHECK:STDOUT: %pattern_type.7ce: type = pattern_type %i32 [concrete] // CHECK:STDOUT: %int_5.64b: Core.IntLiteral = int_value 5 [concrete] // CHECK:STDOUT: %ImplicitAs.type.cc7: type = generic_interface_type @ImplicitAs [concrete] // CHECK:STDOUT: %ImplicitAs.generic: %ImplicitAs.type.cc7 = struct_value () [concrete] // CHECK:STDOUT: %ImplicitAs.type.e8c: type = facet_type <@ImplicitAs, @ImplicitAs(%i32)> [concrete] // CHECK:STDOUT: %To: Core.IntLiteral = symbolic_binding To, 0 [symbolic] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.4e6: type = fn_type @Core.IntLiteral.as.ImplicitAs.impl.Convert, @Core.IntLiteral.as.ImplicitAs.impl(%To) [symbolic] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.3c2: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.4e6 = struct_value () [symbolic] // CHECK:STDOUT: %ImplicitAs.impl_witness.6bc: = impl_witness imports.%ImplicitAs.impl_witness_table.74f, @Core.IntLiteral.as.ImplicitAs.impl(%int_32) [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.e0d: type = fn_type @Core.IntLiteral.as.ImplicitAs.impl.Convert, @Core.IntLiteral.as.ImplicitAs.impl(%int_32) [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.e0d = struct_value () [concrete] // CHECK:STDOUT: %ImplicitAs.facet: %ImplicitAs.type.e8c = facet_value Core.IntLiteral, (%ImplicitAs.impl_witness.6bc) [concrete] // CHECK:STDOUT: %ImplicitAs.WithSelf.Convert.type.b37: type = fn_type @ImplicitAs.WithSelf.Convert, @ImplicitAs.WithSelf(%i32, %ImplicitAs.facet) [concrete] // CHECK:STDOUT: %.545: type = fn_type_with_self_type %ImplicitAs.WithSelf.Convert.type.b37, %ImplicitAs.facet [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.bound: = bound_method %int_5.64b, %Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5 [concrete] // CHECK:STDOUT: %.ff5: Core.Form = init_form %i32 [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn: = specific_function %Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5, @Core.IntLiteral.as.ImplicitAs.impl.Convert(%int_32) [concrete] // CHECK:STDOUT: %bound_method: = bound_method %int_5.64b, %Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn [concrete] // CHECK:STDOUT: %int_5.0f6: %i32 = int_value 5 [concrete] // CHECK:STDOUT: %A.SomeProtectedFunction.type: type = fn_type @A.SomeProtectedFunction [concrete] // CHECK:STDOUT: %A.SomeProtectedFunction: %A.SomeProtectedFunction.type = struct_value () [concrete] // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %complete_type.357: = complete_type_witness %empty_struct_type [concrete] // CHECK:STDOUT: %B: type = class_type @B [concrete] // CHECK:STDOUT: %B.elem: type = unbound_element_type %B, %A [concrete] // CHECK:STDOUT: %B.G.type: type = fn_type @B.G [concrete] // CHECK:STDOUT: %B.G: %B.G.type = struct_value () [concrete] // CHECK:STDOUT: %B.H.type: type = fn_type @B.H [concrete] // CHECK:STDOUT: %B.H: %B.H.type = struct_value () [concrete] // CHECK:STDOUT: %struct_type.base: type = struct_type {.base: %A} [concrete] // CHECK:STDOUT: %complete_type.0d1: = complete_type_witness %struct_type.base [concrete] // CHECK:STDOUT: %Copy.type: type = facet_type <@Copy> [concrete] // CHECK:STDOUT: %Int.as.Copy.impl.Op.type.824: type = fn_type @Int.as.Copy.impl.Op, @Int.as.Copy.impl(%N) [symbolic] // CHECK:STDOUT: %Int.as.Copy.impl.Op.9b9: %Int.as.Copy.impl.Op.type.824 = struct_value () [symbolic] // CHECK:STDOUT: %Copy.impl_witness.f17: = impl_witness imports.%Copy.impl_witness_table.e76, @Int.as.Copy.impl(%int_32) [concrete] // CHECK:STDOUT: %Int.as.Copy.impl.Op.type.546: type = fn_type @Int.as.Copy.impl.Op, @Int.as.Copy.impl(%int_32) [concrete] // CHECK:STDOUT: %Int.as.Copy.impl.Op.664: %Int.as.Copy.impl.Op.type.546 = struct_value () [concrete] // CHECK:STDOUT: %Copy.facet: %Copy.type = facet_value %i32, (%Copy.impl_witness.f17) [concrete] // CHECK:STDOUT: %Copy.WithSelf.Op.type.081: type = fn_type @Copy.WithSelf.Op, @Copy.WithSelf(%Copy.facet) [concrete] // CHECK:STDOUT: %.8e2: type = fn_type_with_self_type %Copy.WithSelf.Op.type.081, %Copy.facet [concrete] // CHECK:STDOUT: %Int.as.Copy.impl.Op.specific_fn: = specific_function %Int.as.Copy.impl.Op.664, @Int.as.Copy.impl.Op(%int_32) [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: .Int = %Core.Int // CHECK:STDOUT: .ImplicitAs = %Core.ImplicitAs // CHECK:STDOUT: .Copy = %Core.Copy // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: %Core.Int: %Int.type = import_ref Core//prelude/parts/int, Int, loaded [concrete = constants.%Int.generic] // CHECK:STDOUT: %Core.ImplicitAs: %ImplicitAs.type.cc7 = import_ref Core//prelude/parts/as, ImplicitAs, loaded [concrete = constants.%ImplicitAs.generic] // CHECK:STDOUT: %Core.import_ref.42d: @Core.IntLiteral.as.ImplicitAs.impl.%Core.IntLiteral.as.ImplicitAs.impl.Convert.type (%Core.IntLiteral.as.ImplicitAs.impl.Convert.type.4e6) = import_ref Core//prelude/parts/int, loc{{\d+_\d+}}, loaded [symbolic = @Core.IntLiteral.as.ImplicitAs.impl.%Core.IntLiteral.as.ImplicitAs.impl.Convert (constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.3c2)] // CHECK:STDOUT: %ImplicitAs.impl_witness_table.74f = impl_witness_table (%Core.import_ref.42d), @Core.IntLiteral.as.ImplicitAs.impl [concrete] // CHECK:STDOUT: %Core.Copy: type = import_ref Core//prelude/parts/copy, Copy, loaded [concrete = constants.%Copy.type] // CHECK:STDOUT: %Core.import_ref.18d: @Int.as.Copy.impl.%Int.as.Copy.impl.Op.type (%Int.as.Copy.impl.Op.type.824) = import_ref Core//prelude/parts/int, loc{{\d+_\d+}}, loaded [symbolic = @Int.as.Copy.impl.%Int.as.Copy.impl.Op (constants.%Int.as.Copy.impl.Op.9b9)] // CHECK:STDOUT: %Copy.impl_witness_table.e76 = impl_witness_table (%Core.import_ref.18d), @Int.as.Copy.impl [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .A = %A.decl // CHECK:STDOUT: .B = %B.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %A.decl: type = class_decl @A [concrete = constants.%A] {} {} // CHECK:STDOUT: %B.decl: type = class_decl @B [concrete = constants.%B] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @A { // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %SOME_CONSTANT.patt: %pattern_type.7ce = value_binding_pattern SOME_CONSTANT [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %int_5: Core.IntLiteral = int_value 5 [concrete = constants.%int_5.64b] // CHECK:STDOUT: %i32: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %impl.elem0: %.545 = impl_witness_access constants.%ImplicitAs.impl_witness.6bc, element0 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5] // CHECK:STDOUT: %bound_method.loc5_38.1: = bound_method %int_5, %impl.elem0 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.bound] // CHECK:STDOUT: %specific_fn: = specific_function %impl.elem0, @Core.IntLiteral.as.ImplicitAs.impl.Convert(constants.%int_32) [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn] // CHECK:STDOUT: %bound_method.loc5_38.2: = bound_method %int_5, %specific_fn [concrete = constants.%bound_method] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.call: init %i32 = call %bound_method.loc5_38.2(%int_5) [concrete = constants.%int_5.0f6] // CHECK:STDOUT: %.loc5_38.1: %i32 = value_of_initializer %Core.IntLiteral.as.ImplicitAs.impl.Convert.call [concrete = constants.%int_5.0f6] // CHECK:STDOUT: %.loc5_38.2: %i32 = converted %int_5, %.loc5_38.1 [concrete = constants.%int_5.0f6] // CHECK:STDOUT: %SOME_CONSTANT: %i32 = value_binding SOME_CONSTANT, %.loc5_38.2 // CHECK:STDOUT: %A.SomeProtectedFunction.decl: %A.SomeProtectedFunction.type = fn_decl @A.SomeProtectedFunction [concrete = constants.%A.SomeProtectedFunction] { // CHECK:STDOUT: %return.patt: %pattern_type.7ce = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: %pattern_type.7ce = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %i32: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %.loc6: Core.Form = init_form %i32 [concrete = constants.%.ff5] // CHECK:STDOUT: %return.param: ref %i32 = out_param call_param0 // CHECK:STDOUT: %return: ref %i32 = return_slot %return.param // CHECK:STDOUT: } // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type.357] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%A // CHECK:STDOUT: .SOME_CONSTANT [protected] = %SOME_CONSTANT // CHECK:STDOUT: .SomeProtectedFunction [protected] = %A.SomeProtectedFunction.decl // CHECK:STDOUT: .A = // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @B { // CHECK:STDOUT: %A.ref: type = name_ref A, file.%A.decl [concrete = constants.%A] // CHECK:STDOUT: %.loc12: %B.elem = base_decl %A.ref, element0 [concrete] // CHECK:STDOUT: %B.G.decl: %B.G.type = fn_decl @B.G [concrete = constants.%B.G] { // CHECK:STDOUT: %return.patt: %pattern_type.7ce = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: %pattern_type.7ce = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %i32: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %.loc14: Core.Form = init_form %i32 [concrete = constants.%.ff5] // CHECK:STDOUT: %return.param: ref %i32 = out_param call_param0 // CHECK:STDOUT: %return: ref %i32 = return_slot %return.param // CHECK:STDOUT: } // CHECK:STDOUT: %B.H.decl: %B.H.type = fn_decl @B.H [concrete = constants.%B.H] { // CHECK:STDOUT: %return.patt: %pattern_type.7ce = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: %pattern_type.7ce = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %i32: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %.loc18: Core.Form = init_form %i32 [concrete = constants.%.ff5] // CHECK:STDOUT: %return.param: ref %i32 = out_param call_param0 // CHECK:STDOUT: %return: ref %i32 = return_slot %return.param // CHECK:STDOUT: } // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%struct_type.base [concrete = constants.%complete_type.0d1] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%B // CHECK:STDOUT: .A = // CHECK:STDOUT: .base = %.loc12 // CHECK:STDOUT: .G = %B.G.decl // CHECK:STDOUT: .H = %B.H.decl // CHECK:STDOUT: extend %A.ref // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @A.SomeProtectedFunction() -> out %return.param: %i32 { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %int_5: Core.IntLiteral = int_value 5 [concrete = constants.%int_5.64b] // CHECK:STDOUT: %impl.elem0: %.545 = impl_witness_access constants.%ImplicitAs.impl_witness.6bc, element0 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5] // CHECK:STDOUT: %bound_method.loc7_13.1: = bound_method %int_5, %impl.elem0 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.bound] // CHECK:STDOUT: %specific_fn: = specific_function %impl.elem0, @Core.IntLiteral.as.ImplicitAs.impl.Convert(constants.%int_32) [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn] // CHECK:STDOUT: %bound_method.loc7_13.2: = bound_method %int_5, %specific_fn [concrete = constants.%bound_method] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.call: init %i32 = call %bound_method.loc7_13.2(%int_5) [concrete = constants.%int_5.0f6] // CHECK:STDOUT: %.loc7: init %i32 = converted %int_5, %Core.IntLiteral.as.ImplicitAs.impl.Convert.call [concrete = constants.%int_5.0f6] // CHECK:STDOUT: return %.loc7 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @B.G() -> out %return.param: %i32 { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %A.ref: type = name_ref A, file.%A.decl [concrete = constants.%A] // CHECK:STDOUT: %SOME_CONSTANT.ref: %i32 = name_ref SOME_CONSTANT, @A.%SOME_CONSTANT // CHECK:STDOUT: %impl.elem0: %.8e2 = impl_witness_access constants.%Copy.impl_witness.f17, element0 [concrete = constants.%Int.as.Copy.impl.Op.664] // CHECK:STDOUT: %bound_method.loc15_13.1: = bound_method %SOME_CONSTANT.ref, %impl.elem0 // CHECK:STDOUT: %specific_fn: = specific_function %impl.elem0, @Int.as.Copy.impl.Op(constants.%int_32) [concrete = constants.%Int.as.Copy.impl.Op.specific_fn] // CHECK:STDOUT: %bound_method.loc15_13.2: = bound_method %SOME_CONSTANT.ref, %specific_fn // CHECK:STDOUT: %Int.as.Copy.impl.Op.call: init %i32 = call %bound_method.loc15_13.2(%SOME_CONSTANT.ref) // CHECK:STDOUT: return %Int.as.Copy.impl.Op.call // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @B.H() -> out %return.param: %i32 { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %A.ref: type = name_ref A, file.%A.decl [concrete = constants.%A] // CHECK:STDOUT: %SomeProtectedFunction.ref: %A.SomeProtectedFunction.type = name_ref SomeProtectedFunction, @A.%A.SomeProtectedFunction.decl [concrete = constants.%A.SomeProtectedFunction] // CHECK:STDOUT: %A.SomeProtectedFunction.call: init %i32 = call %SomeProtectedFunction.ref() // CHECK:STDOUT: return %A.SomeProtectedFunction.call // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- fail_inherited_private_field_access.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %Shape: type = class_type @Shape [concrete] // CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [concrete] // CHECK:STDOUT: %Int.type: type = generic_class_type @Int [concrete] // CHECK:STDOUT: %Int.generic: %Int.type = struct_value () [concrete] // CHECK:STDOUT: %i32: type = class_type @Int, @Int(%int_32) [concrete] // CHECK:STDOUT: %Shape.elem: type = unbound_element_type %Shape, %i32 [concrete] // CHECK:STDOUT: %struct_type.y: type = struct_type {.y: %i32} [concrete] // CHECK:STDOUT: %complete_type.0f9: = complete_type_witness %struct_type.y [concrete] // CHECK:STDOUT: %Square: type = class_type @Square [concrete] // CHECK:STDOUT: %Square.elem: type = unbound_element_type %Square, %Shape [concrete] // CHECK:STDOUT: %pattern_type.1d2: type = pattern_type %Square [concrete] // CHECK:STDOUT: %.ff5: Core.Form = init_form %i32 [concrete] // CHECK:STDOUT: %pattern_type.7ce: type = pattern_type %i32 [concrete] // CHECK:STDOUT: %Square.GetPosition.type: type = fn_type @Square.GetPosition [concrete] // CHECK:STDOUT: %Square.GetPosition: %Square.GetPosition.type = struct_value () [concrete] // CHECK:STDOUT: %struct_type.base.490: type = struct_type {.base: %Shape} [concrete] // CHECK:STDOUT: %complete_type.560: = complete_type_witness %struct_type.base.490 [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: .Int = %Core.Int // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: %Core.Int: %Int.type = import_ref Core//prelude/parts/int, Int, loaded [concrete = constants.%Int.generic] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .Shape = %Shape.decl // CHECK:STDOUT: .Square = %Square.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %Shape.decl: type = class_decl @Shape [concrete = constants.%Shape] {} {} // CHECK:STDOUT: %Square.decl: type = class_decl @Square [concrete = constants.%Square] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @Shape { // CHECK:STDOUT: %i32: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %.loc5: %Shape.elem = field_decl y, element0 [concrete] // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%struct_type.y [concrete = constants.%complete_type.0f9] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%Shape // CHECK:STDOUT: .y [private] = %.loc5 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @Square { // CHECK:STDOUT: %Shape.ref: type = name_ref Shape, file.%Shape.decl [concrete = constants.%Shape] // CHECK:STDOUT: %.loc9: %Square.elem = base_decl %Shape.ref, element0 [concrete] // CHECK:STDOUT: %Square.GetPosition.decl: %Square.GetPosition.type = fn_decl @Square.GetPosition [concrete = constants.%Square.GetPosition] { // CHECK:STDOUT: %self.patt: %pattern_type.1d2 = value_binding_pattern self [concrete] // CHECK:STDOUT: %self.param_patt: %pattern_type.1d2 = value_param_pattern %self.patt [concrete] // CHECK:STDOUT: %return.patt: %pattern_type.7ce = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: %pattern_type.7ce = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %i32: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %.loc11: Core.Form = init_form %i32 [concrete = constants.%.ff5] // CHECK:STDOUT: %self.param: %Square = value_param call_param0 // CHECK:STDOUT: %Self.ref: type = name_ref Self, constants.%Square [concrete = constants.%Square] // CHECK:STDOUT: %self: %Square = value_binding self, %self.param // CHECK:STDOUT: %return.param: ref %i32 = out_param call_param1 // CHECK:STDOUT: %return: ref %i32 = return_slot %return.param // CHECK:STDOUT: } // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%struct_type.base.490 [concrete = constants.%complete_type.560] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%Square // CHECK:STDOUT: .Shape = // CHECK:STDOUT: .base = %.loc9 // CHECK:STDOUT: .GetPosition = %Square.GetPosition.decl // CHECK:STDOUT: .y = // CHECK:STDOUT: extend %Shape.ref // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @Square.GetPosition(%self.param: %Square) -> out %return.param: %i32 { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %self.ref: %Square = name_ref self, %self // CHECK:STDOUT: %y.ref: = name_ref y, [concrete = ] // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- noninstance_private_on_self.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %C: type = class_type @C [concrete] // CHECK:STDOUT: %C.F.type: type = fn_type @C.F [concrete] // CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] // CHECK:STDOUT: %C.F: %C.F.type = struct_value () [concrete] // CHECK:STDOUT: %C.G.type: type = fn_type @C.G [concrete] // CHECK:STDOUT: %C.G: %C.G.type = struct_value () [concrete] // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %complete_type: = complete_type_witness %empty_struct_type [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .C = %C.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %C.decl: type = class_decl @C [concrete = constants.%C] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @C { // CHECK:STDOUT: %C.F.decl: %C.F.type = fn_decl @C.F [concrete = constants.%C.F] {} {} // CHECK:STDOUT: %C.G.decl: %C.G.type = fn_decl @C.G [concrete = constants.%C.G] {} {} // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%C // CHECK:STDOUT: .F [private] = %C.F.decl // CHECK:STDOUT: .G = %C.G.decl // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @C.F() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @C.G() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %Self.ref: type = name_ref Self, constants.%C [concrete = constants.%C] // CHECK:STDOUT: %F.ref.loc7: %C.F.type = name_ref F, @C.%C.F.decl [concrete = constants.%C.F] // CHECK:STDOUT: %C.F.call.loc7: init %empty_tuple.type = call %F.ref.loc7() // CHECK:STDOUT: %F.ref.loc8: %C.F.type = name_ref F, @C.%C.F.decl [concrete = constants.%C.F] // CHECK:STDOUT: %C.F.call.loc8: init %empty_tuple.type = call %F.ref.loc8() // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- noninstance_protected_on_self.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %C: type = class_type @C [concrete] // CHECK:STDOUT: %C.F.type: type = fn_type @C.F [concrete] // CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] // CHECK:STDOUT: %C.F: %C.F.type = struct_value () [concrete] // CHECK:STDOUT: %C.G.type: type = fn_type @C.G [concrete] // CHECK:STDOUT: %C.G: %C.G.type = struct_value () [concrete] // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %complete_type: = complete_type_witness %empty_struct_type [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .C = %C.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %C.decl: type = class_decl @C [concrete = constants.%C] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @C { // CHECK:STDOUT: %C.F.decl: %C.F.type = fn_decl @C.F [concrete = constants.%C.F] {} {} // CHECK:STDOUT: %C.G.decl: %C.G.type = fn_decl @C.G [concrete = constants.%C.G] {} {} // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%C // CHECK:STDOUT: .F [protected] = %C.F.decl // CHECK:STDOUT: .G = %C.G.decl // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @C.F() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @C.G() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %Self.ref: type = name_ref Self, constants.%C [concrete = constants.%C] // CHECK:STDOUT: %F.ref.loc7: %C.F.type = name_ref F, @C.%C.F.decl [concrete = constants.%C.F] // CHECK:STDOUT: %C.F.call.loc7: init %empty_tuple.type = call %F.ref.loc7() // CHECK:STDOUT: %F.ref.loc8: %C.F.type = name_ref F, @C.%C.F.decl [concrete = constants.%C.F] // CHECK:STDOUT: %C.F.call.loc8: init %empty_tuple.type = call %F.ref.loc8() // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- fail_noninstance_private_on_parent_qualified.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %B: type = class_type @B [concrete] // CHECK:STDOUT: %B.F.type: type = fn_type @B.F [concrete] // CHECK:STDOUT: %B.F: %B.F.type = struct_value () [concrete] // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %complete_type.357: = complete_type_witness %empty_struct_type [concrete] // CHECK:STDOUT: %C: type = class_type @C [concrete] // CHECK:STDOUT: %C.elem: type = unbound_element_type %C, %B [concrete] // CHECK:STDOUT: %C.G.type: type = fn_type @C.G [concrete] // CHECK:STDOUT: %C.G: %C.G.type = struct_value () [concrete] // CHECK:STDOUT: %struct_type.base.64a: type = struct_type {.base: %B} [concrete] // CHECK:STDOUT: %complete_type.021: = complete_type_witness %struct_type.base.64a [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .B = %B.decl // CHECK:STDOUT: .C = %C.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %B.decl: type = class_decl @B [concrete = constants.%B] {} {} // CHECK:STDOUT: %C.decl: type = class_decl @C [concrete = constants.%C] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @B { // CHECK:STDOUT: %B.F.decl: %B.F.type = fn_decl @B.F [concrete = constants.%B.F] {} {} // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type.357] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%B // CHECK:STDOUT: .F [private] = %B.F.decl // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @C { // CHECK:STDOUT: %B.ref: type = name_ref B, file.%B.decl [concrete = constants.%B] // CHECK:STDOUT: %.loc9: %C.elem = base_decl %B.ref, element0 [concrete] // CHECK:STDOUT: %C.G.decl: %C.G.type = fn_decl @C.G [concrete = constants.%C.G] {} {} // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%struct_type.base.64a [concrete = constants.%complete_type.021] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%C // CHECK:STDOUT: .B = // CHECK:STDOUT: .base = %.loc9 // CHECK:STDOUT: .G = %C.G.decl // CHECK:STDOUT: .F = // CHECK:STDOUT: extend %B.ref // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @B.F() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @C.G() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %Self.ref: type = name_ref Self, constants.%C [concrete = constants.%C] // CHECK:STDOUT: %F.ref: = name_ref F, [concrete = ] // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- fail_noninstance_private_on_parent_unqualified.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %A: type = class_type @A [concrete] // CHECK:STDOUT: %A.Fa.type: type = fn_type @A.Fa [concrete] // CHECK:STDOUT: %A.Fa: %A.Fa.type = struct_value () [concrete] // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %complete_type.357: = complete_type_witness %empty_struct_type [concrete] // CHECK:STDOUT: %B: type = class_type @B [concrete] // CHECK:STDOUT: %B.elem: type = unbound_element_type %B, %A [concrete] // CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [concrete] // CHECK:STDOUT: %Int.type: type = generic_class_type @Int [concrete] // CHECK:STDOUT: %Int.generic: %Int.type = struct_value () [concrete] // CHECK:STDOUT: %i32: type = class_type @Int, @Int(%int_32) [concrete] // CHECK:STDOUT: %pattern_type.7ce: type = pattern_type %i32 [concrete] // CHECK:STDOUT: %int_86.bd3: Core.IntLiteral = int_value 86 [concrete] // CHECK:STDOUT: %ImplicitAs.type.cc7: type = generic_interface_type @ImplicitAs [concrete] // CHECK:STDOUT: %ImplicitAs.generic: %ImplicitAs.type.cc7 = struct_value () [concrete] // CHECK:STDOUT: %ImplicitAs.type.e8c: type = facet_type <@ImplicitAs, @ImplicitAs(%i32)> [concrete] // CHECK:STDOUT: %To: Core.IntLiteral = symbolic_binding To, 0 [symbolic] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.4e6: type = fn_type @Core.IntLiteral.as.ImplicitAs.impl.Convert, @Core.IntLiteral.as.ImplicitAs.impl(%To) [symbolic] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.3c2: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.4e6 = struct_value () [symbolic] // CHECK:STDOUT: %ImplicitAs.impl_witness.6bc: = impl_witness imports.%ImplicitAs.impl_witness_table.74f, @Core.IntLiteral.as.ImplicitAs.impl(%int_32) [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.e0d: type = fn_type @Core.IntLiteral.as.ImplicitAs.impl.Convert, @Core.IntLiteral.as.ImplicitAs.impl(%int_32) [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.e0d = struct_value () [concrete] // CHECK:STDOUT: %ImplicitAs.facet: %ImplicitAs.type.e8c = facet_value Core.IntLiteral, (%ImplicitAs.impl_witness.6bc) [concrete] // CHECK:STDOUT: %ImplicitAs.WithSelf.Convert.type.b37: type = fn_type @ImplicitAs.WithSelf.Convert, @ImplicitAs.WithSelf(%i32, %ImplicitAs.facet) [concrete] // CHECK:STDOUT: %.545: type = fn_type_with_self_type %ImplicitAs.WithSelf.Convert.type.b37, %ImplicitAs.facet [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.bound: = bound_method %int_86.bd3, %Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5 [concrete] // CHECK:STDOUT: %.ff5: Core.Form = init_form %i32 [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn: = specific_function %Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5, @Core.IntLiteral.as.ImplicitAs.impl.Convert(%int_32) [concrete] // CHECK:STDOUT: %bound_method: = bound_method %int_86.bd3, %Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn [concrete] // CHECK:STDOUT: %int_86.261: %i32 = int_value 86 [concrete] // CHECK:STDOUT: %B.Fb.type: type = fn_type @B.Fb [concrete] // CHECK:STDOUT: %B.Fb: %B.Fb.type = struct_value () [concrete] // CHECK:STDOUT: %struct_type.base.5af: type = struct_type {.base: %A} [concrete] // CHECK:STDOUT: %complete_type.0d1: = complete_type_witness %struct_type.base.5af [concrete] // CHECK:STDOUT: %C: type = class_type @C [concrete] // CHECK:STDOUT: %C.elem: type = unbound_element_type %C, %B [concrete] // CHECK:STDOUT: %C.G1.type: type = fn_type @C.G1 [concrete] // CHECK:STDOUT: %C.G1: %C.G1.type = struct_value () [concrete] // CHECK:STDOUT: %C.G2.type: type = fn_type @C.G2 [concrete] // CHECK:STDOUT: %C.G2: %C.G2.type = struct_value () [concrete] // CHECK:STDOUT: %C.G3.type: type = fn_type @C.G3 [concrete] // CHECK:STDOUT: %C.G3: %C.G3.type = struct_value () [concrete] // CHECK:STDOUT: %struct_type.base.64a: type = struct_type {.base: %B} [concrete] // CHECK:STDOUT: %complete_type.021: = complete_type_witness %struct_type.base.64a [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: .Int = %Core.Int // CHECK:STDOUT: .ImplicitAs = %Core.ImplicitAs // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: %Core.Int: %Int.type = import_ref Core//prelude/parts/int, Int, loaded [concrete = constants.%Int.generic] // CHECK:STDOUT: %Core.ImplicitAs: %ImplicitAs.type.cc7 = import_ref Core//prelude/parts/as, ImplicitAs, loaded [concrete = constants.%ImplicitAs.generic] // CHECK:STDOUT: %Core.import_ref.42d: @Core.IntLiteral.as.ImplicitAs.impl.%Core.IntLiteral.as.ImplicitAs.impl.Convert.type (%Core.IntLiteral.as.ImplicitAs.impl.Convert.type.4e6) = import_ref Core//prelude/parts/int, loc{{\d+_\d+}}, loaded [symbolic = @Core.IntLiteral.as.ImplicitAs.impl.%Core.IntLiteral.as.ImplicitAs.impl.Convert (constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.3c2)] // CHECK:STDOUT: %ImplicitAs.impl_witness_table.74f = impl_witness_table (%Core.import_ref.42d), @Core.IntLiteral.as.ImplicitAs.impl [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .A = %A.decl // CHECK:STDOUT: .B = %B.decl // CHECK:STDOUT: .C = %C.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %A.decl: type = class_decl @A [concrete = constants.%A] {} {} // CHECK:STDOUT: %B.decl: type = class_decl @B [concrete = constants.%B] {} {} // CHECK:STDOUT: %C.decl: type = class_decl @C [concrete = constants.%C] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @A { // CHECK:STDOUT: %A.Fa.decl: %A.Fa.type = fn_decl @A.Fa [concrete = constants.%A.Fa] {} {} // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type.357] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%A // CHECK:STDOUT: .Fa [private] = %A.Fa.decl // CHECK:STDOUT: .SOME_PRIVATE_CONSTANT = // CHECK:STDOUT: .Fb = // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @B { // CHECK:STDOUT: %A.ref: type = name_ref A, file.%A.decl [concrete = constants.%A] // CHECK:STDOUT: %.loc9: %B.elem = base_decl %A.ref, element0 [concrete] // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %SOME_PRIVATE_CONSTANT.patt: %pattern_type.7ce = value_binding_pattern SOME_PRIVATE_CONSTANT [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %int_86: Core.IntLiteral = int_value 86 [concrete = constants.%int_86.bd3] // CHECK:STDOUT: %i32: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %impl.elem0: %.545 = impl_witness_access constants.%ImplicitAs.impl_witness.6bc, element0 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5] // CHECK:STDOUT: %bound_method.loc11_44.1: = bound_method %int_86, %impl.elem0 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.bound] // CHECK:STDOUT: %specific_fn: = specific_function %impl.elem0, @Core.IntLiteral.as.ImplicitAs.impl.Convert(constants.%int_32) [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn] // CHECK:STDOUT: %bound_method.loc11_44.2: = bound_method %int_86, %specific_fn [concrete = constants.%bound_method] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.call: init %i32 = call %bound_method.loc11_44.2(%int_86) [concrete = constants.%int_86.261] // CHECK:STDOUT: %.loc11_44.1: %i32 = value_of_initializer %Core.IntLiteral.as.ImplicitAs.impl.Convert.call [concrete = constants.%int_86.261] // CHECK:STDOUT: %.loc11_44.2: %i32 = converted %int_86, %.loc11_44.1 [concrete = constants.%int_86.261] // CHECK:STDOUT: %SOME_PRIVATE_CONSTANT: %i32 = value_binding SOME_PRIVATE_CONSTANT, %.loc11_44.2 // CHECK:STDOUT: %B.Fb.decl: %B.Fb.type = fn_decl @B.Fb [concrete = constants.%B.Fb] {} {} // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%struct_type.base.5af [concrete = constants.%complete_type.0d1] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%B // CHECK:STDOUT: .A = // CHECK:STDOUT: .base = %.loc9 // CHECK:STDOUT: .SOME_PRIVATE_CONSTANT [private] = %SOME_PRIVATE_CONSTANT // CHECK:STDOUT: .Fb [private] = %B.Fb.decl // CHECK:STDOUT: .Fa = // CHECK:STDOUT: extend %A.ref // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @C { // CHECK:STDOUT: %B.ref: type = name_ref B, file.%B.decl [concrete = constants.%B] // CHECK:STDOUT: %.loc16: %C.elem = base_decl %B.ref, element0 [concrete] // CHECK:STDOUT: %C.G1.decl: %C.G1.type = fn_decl @C.G1 [concrete = constants.%C.G1] { // CHECK:STDOUT: %return.patt: %pattern_type.7ce = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: %pattern_type.7ce = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %i32: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %.loc25: Core.Form = init_form %i32 [concrete = constants.%.ff5] // CHECK:STDOUT: %return.param: ref %i32 = out_param call_param0 // CHECK:STDOUT: %return: ref %i32 = return_slot %return.param // CHECK:STDOUT: } // CHECK:STDOUT: %C.G2.decl: %C.G2.type = fn_decl @C.G2 [concrete = constants.%C.G2] {} {} // CHECK:STDOUT: %C.G3.decl: %C.G3.type = fn_decl @C.G3 [concrete = constants.%C.G3] {} {} // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%struct_type.base.64a [concrete = constants.%complete_type.021] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%C // CHECK:STDOUT: .B = // CHECK:STDOUT: .base = %.loc16 // CHECK:STDOUT: .G1 = %C.G1.decl // CHECK:STDOUT: .G2 = %C.G2.decl // CHECK:STDOUT: .G3 = %C.G3.decl // CHECK:STDOUT: .SOME_PRIVATE_CONSTANT = // CHECK:STDOUT: .Fa = // CHECK:STDOUT: .Fb = // CHECK:STDOUT: extend %B.ref // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @A.Fa() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @B.Fb() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @C.G1() -> out %return.param: %i32 { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %SOME_PRIVATE_CONSTANT.ref: = name_ref SOME_PRIVATE_CONSTANT, [concrete = ] // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @C.G2() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %Fa.ref: = name_ref Fa, [concrete = ] // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @C.G3() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %Fb.ref: = name_ref Fb, [concrete = ] // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- noninstance_protected_on_parent.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %B: type = class_type @B [concrete] // CHECK:STDOUT: %B.F.type: type = fn_type @B.F [concrete] // CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] // CHECK:STDOUT: %B.F: %B.F.type = struct_value () [concrete] // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %complete_type.357: = complete_type_witness %empty_struct_type [concrete] // CHECK:STDOUT: %C: type = class_type @C [concrete] // CHECK:STDOUT: %C.elem: type = unbound_element_type %C, %B [concrete] // CHECK:STDOUT: %C.G.type: type = fn_type @C.G [concrete] // CHECK:STDOUT: %C.G: %C.G.type = struct_value () [concrete] // CHECK:STDOUT: %struct_type.base.64a: type = struct_type {.base: %B} [concrete] // CHECK:STDOUT: %complete_type.021: = complete_type_witness %struct_type.base.64a [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .B = %B.decl // CHECK:STDOUT: .C = %C.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %B.decl: type = class_decl @B [concrete = constants.%B] {} {} // CHECK:STDOUT: %C.decl: type = class_decl @C [concrete = constants.%C] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @B { // CHECK:STDOUT: %B.F.decl: %B.F.type = fn_decl @B.F [concrete = constants.%B.F] {} {} // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type.357] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%B // CHECK:STDOUT: .F [protected] = %B.F.decl // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @C { // CHECK:STDOUT: %B.ref: type = name_ref B, file.%B.decl [concrete = constants.%B] // CHECK:STDOUT: %.loc9: %C.elem = base_decl %B.ref, element0 [concrete] // CHECK:STDOUT: %C.G.decl: %C.G.type = fn_decl @C.G [concrete = constants.%C.G] {} {} // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%struct_type.base.64a [concrete = constants.%complete_type.021] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%C // CHECK:STDOUT: .B = // CHECK:STDOUT: .base = %.loc9 // CHECK:STDOUT: .G = %C.G.decl // CHECK:STDOUT: .F = // CHECK:STDOUT: extend %B.ref // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @B.F() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @C.G() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %Self.ref: type = name_ref Self, constants.%C [concrete = constants.%C] // CHECK:STDOUT: %F.ref: %B.F.type = name_ref F, @B.%B.F.decl [concrete = constants.%B.F] // CHECK:STDOUT: %B.F.call: init %empty_tuple.type = call %F.ref() // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- fail_non_inherited_access.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %A: type = class_type @A [concrete] // CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [concrete] // CHECK:STDOUT: %Int.type: type = generic_class_type @Int [concrete] // CHECK:STDOUT: %Int.generic: %Int.type = struct_value () [concrete] // CHECK:STDOUT: %i32: type = class_type @Int, @Int(%int_32) [concrete] // CHECK:STDOUT: %pattern_type.7ce: type = pattern_type %i32 [concrete] // CHECK:STDOUT: %int_5.64b: Core.IntLiteral = int_value 5 [concrete] // CHECK:STDOUT: %ImplicitAs.type.cc7: type = generic_interface_type @ImplicitAs [concrete] // CHECK:STDOUT: %ImplicitAs.generic: %ImplicitAs.type.cc7 = struct_value () [concrete] // CHECK:STDOUT: %ImplicitAs.type.e8c: type = facet_type <@ImplicitAs, @ImplicitAs(%i32)> [concrete] // CHECK:STDOUT: %To: Core.IntLiteral = symbolic_binding To, 0 [symbolic] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.4e6: type = fn_type @Core.IntLiteral.as.ImplicitAs.impl.Convert, @Core.IntLiteral.as.ImplicitAs.impl(%To) [symbolic] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.3c2: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.4e6 = struct_value () [symbolic] // CHECK:STDOUT: %ImplicitAs.impl_witness.6bc: = impl_witness imports.%ImplicitAs.impl_witness_table.74f, @Core.IntLiteral.as.ImplicitAs.impl(%int_32) [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.e0d: type = fn_type @Core.IntLiteral.as.ImplicitAs.impl.Convert, @Core.IntLiteral.as.ImplicitAs.impl(%int_32) [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.e0d = struct_value () [concrete] // CHECK:STDOUT: %ImplicitAs.facet: %ImplicitAs.type.e8c = facet_value Core.IntLiteral, (%ImplicitAs.impl_witness.6bc) [concrete] // CHECK:STDOUT: %ImplicitAs.WithSelf.Convert.type.b37: type = fn_type @ImplicitAs.WithSelf.Convert, @ImplicitAs.WithSelf(%i32, %ImplicitAs.facet) [concrete] // CHECK:STDOUT: %.545: type = fn_type_with_self_type %ImplicitAs.WithSelf.Convert.type.b37, %ImplicitAs.facet [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.bound: = bound_method %int_5.64b, %Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5 [concrete] // CHECK:STDOUT: %.ff5: Core.Form = init_form %i32 [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn: = specific_function %Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5, @Core.IntLiteral.as.ImplicitAs.impl.Convert(%int_32) [concrete] // CHECK:STDOUT: %bound_method: = bound_method %int_5.64b, %Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn [concrete] // CHECK:STDOUT: %int_5.0f6: %i32 = int_value 5 [concrete] // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %complete_type.357: = complete_type_witness %empty_struct_type [concrete] // CHECK:STDOUT: %Internal: type = class_type @Internal [concrete] // CHECK:STDOUT: %B: type = class_type @B [concrete] // CHECK:STDOUT: %B.elem: type = unbound_element_type %B, %Internal [concrete] // CHECK:STDOUT: %B.G.type: type = fn_type @B.G [concrete] // CHECK:STDOUT: %B.G: %B.G.type = struct_value () [concrete] // CHECK:STDOUT: %pattern_type.1f4: type = pattern_type %B [concrete] // CHECK:STDOUT: %B.SomeFunc.type: type = fn_type @B.SomeFunc [concrete] // CHECK:STDOUT: %B.SomeFunc: %B.SomeFunc.type = struct_value () [concrete] // CHECK:STDOUT: %struct_type.internal.f18: type = struct_type {.internal: %Internal} [concrete] // CHECK:STDOUT: %complete_type.ade: = complete_type_witness %struct_type.internal.f18 [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: .Int = %Core.Int // CHECK:STDOUT: .ImplicitAs = %Core.ImplicitAs // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: %Core.Int: %Int.type = import_ref Core//prelude/parts/int, Int, loaded [concrete = constants.%Int.generic] // CHECK:STDOUT: %Core.ImplicitAs: %ImplicitAs.type.cc7 = import_ref Core//prelude/parts/as, ImplicitAs, loaded [concrete = constants.%ImplicitAs.generic] // CHECK:STDOUT: %Core.import_ref.42d: @Core.IntLiteral.as.ImplicitAs.impl.%Core.IntLiteral.as.ImplicitAs.impl.Convert.type (%Core.IntLiteral.as.ImplicitAs.impl.Convert.type.4e6) = import_ref Core//prelude/parts/int, loc{{\d+_\d+}}, loaded [symbolic = @Core.IntLiteral.as.ImplicitAs.impl.%Core.IntLiteral.as.ImplicitAs.impl.Convert (constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.3c2)] // CHECK:STDOUT: %ImplicitAs.impl_witness_table.74f = impl_witness_table (%Core.import_ref.42d), @Core.IntLiteral.as.ImplicitAs.impl [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .A = %A.decl // CHECK:STDOUT: .Internal = %Internal.decl // CHECK:STDOUT: .B = %B.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %A.decl: type = class_decl @A [concrete = constants.%A] {} {} // CHECK:STDOUT: %Internal.decl: type = class_decl @Internal [concrete = constants.%Internal] {} {} // CHECK:STDOUT: %B.decl: type = class_decl @B [concrete = constants.%B] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @A { // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %SOME_PROTECTED_CONSTANT.patt: %pattern_type.7ce = value_binding_pattern SOME_PROTECTED_CONSTANT [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %int_5.loc5: Core.IntLiteral = int_value 5 [concrete = constants.%int_5.64b] // CHECK:STDOUT: %i32.loc5: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %impl.elem0.loc5: %.545 = impl_witness_access constants.%ImplicitAs.impl_witness.6bc, element0 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5] // CHECK:STDOUT: %bound_method.loc5_48.1: = bound_method %int_5.loc5, %impl.elem0.loc5 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.bound] // CHECK:STDOUT: %specific_fn.loc5: = specific_function %impl.elem0.loc5, @Core.IntLiteral.as.ImplicitAs.impl.Convert(constants.%int_32) [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn] // CHECK:STDOUT: %bound_method.loc5_48.2: = bound_method %int_5.loc5, %specific_fn.loc5 [concrete = constants.%bound_method] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc5: init %i32 = call %bound_method.loc5_48.2(%int_5.loc5) [concrete = constants.%int_5.0f6] // CHECK:STDOUT: %.loc5_48.1: %i32 = value_of_initializer %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc5 [concrete = constants.%int_5.0f6] // CHECK:STDOUT: %.loc5_48.2: %i32 = converted %int_5.loc5, %.loc5_48.1 [concrete = constants.%int_5.0f6] // CHECK:STDOUT: %SOME_PROTECTED_CONSTANT: %i32 = value_binding SOME_PROTECTED_CONSTANT, %.loc5_48.2 // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %SOME_PRIVATE_CONSTANT.patt: %pattern_type.7ce = value_binding_pattern SOME_PRIVATE_CONSTANT [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %int_5.loc6: Core.IntLiteral = int_value 5 [concrete = constants.%int_5.64b] // CHECK:STDOUT: %i32.loc6: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %impl.elem0.loc6: %.545 = impl_witness_access constants.%ImplicitAs.impl_witness.6bc, element0 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5] // CHECK:STDOUT: %bound_method.loc6_44.1: = bound_method %int_5.loc6, %impl.elem0.loc6 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.bound] // CHECK:STDOUT: %specific_fn.loc6: = specific_function %impl.elem0.loc6, @Core.IntLiteral.as.ImplicitAs.impl.Convert(constants.%int_32) [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn] // CHECK:STDOUT: %bound_method.loc6_44.2: = bound_method %int_5.loc6, %specific_fn.loc6 [concrete = constants.%bound_method] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc6: init %i32 = call %bound_method.loc6_44.2(%int_5.loc6) [concrete = constants.%int_5.0f6] // CHECK:STDOUT: %.loc6_44.1: %i32 = value_of_initializer %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc6 [concrete = constants.%int_5.0f6] // CHECK:STDOUT: %.loc6_44.2: %i32 = converted %int_5.loc6, %.loc6_44.1 [concrete = constants.%int_5.0f6] // CHECK:STDOUT: %SOME_PRIVATE_CONSTANT: %i32 = value_binding SOME_PRIVATE_CONSTANT, %.loc6_44.2 // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type.357] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%A // CHECK:STDOUT: .SOME_PROTECTED_CONSTANT [protected] = %SOME_PROTECTED_CONSTANT // CHECK:STDOUT: .SOME_PRIVATE_CONSTANT [private] = %SOME_PRIVATE_CONSTANT // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @Internal { // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %INTERNAL_CONSTANT.patt: %pattern_type.7ce = value_binding_pattern INTERNAL_CONSTANT [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %int_5: Core.IntLiteral = int_value 5 [concrete = constants.%int_5.64b] // CHECK:STDOUT: %i32: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %impl.elem0: %.545 = impl_witness_access constants.%ImplicitAs.impl_witness.6bc, element0 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5] // CHECK:STDOUT: %bound_method.loc10_42.1: = bound_method %int_5, %impl.elem0 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.bound] // CHECK:STDOUT: %specific_fn: = specific_function %impl.elem0, @Core.IntLiteral.as.ImplicitAs.impl.Convert(constants.%int_32) [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn] // CHECK:STDOUT: %bound_method.loc10_42.2: = bound_method %int_5, %specific_fn [concrete = constants.%bound_method] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.call: init %i32 = call %bound_method.loc10_42.2(%int_5) [concrete = constants.%int_5.0f6] // CHECK:STDOUT: %.loc10_42.1: %i32 = value_of_initializer %Core.IntLiteral.as.ImplicitAs.impl.Convert.call [concrete = constants.%int_5.0f6] // CHECK:STDOUT: %.loc10_42.2: %i32 = converted %int_5, %.loc10_42.1 [concrete = constants.%int_5.0f6] // CHECK:STDOUT: %INTERNAL_CONSTANT: %i32 = value_binding INTERNAL_CONSTANT, %.loc10_42.2 // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type.357] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%Internal // CHECK:STDOUT: .INTERNAL_CONSTANT [protected] = %INTERNAL_CONSTANT // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @B { // CHECK:STDOUT: %Internal.ref: type = name_ref Internal, file.%Internal.decl [concrete = constants.%Internal] // CHECK:STDOUT: %.loc14: %B.elem = field_decl internal, element0 [concrete] // CHECK:STDOUT: %B.G.decl: %B.G.type = fn_decl @B.G [concrete = constants.%B.G] { // CHECK:STDOUT: %return.patt: %pattern_type.7ce = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: %pattern_type.7ce = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %i32: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %.loc16: Core.Form = init_form %i32 [concrete = constants.%.ff5] // CHECK:STDOUT: %return.param: ref %i32 = out_param call_param0 // CHECK:STDOUT: %return: ref %i32 = return_slot %return.param // CHECK:STDOUT: } // CHECK:STDOUT: %B.SomeFunc.decl: %B.SomeFunc.type = fn_decl @B.SomeFunc [concrete = constants.%B.SomeFunc] { // CHECK:STDOUT: %self.patt: %pattern_type.1f4 = value_binding_pattern self [concrete] // CHECK:STDOUT: %self.param_patt: %pattern_type.1f4 = value_param_pattern %self.patt [concrete] // CHECK:STDOUT: %return.patt: %pattern_type.7ce = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: %pattern_type.7ce = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %i32: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %.loc36: Core.Form = init_form %i32 [concrete = constants.%.ff5] // CHECK:STDOUT: %self.param: %B = value_param call_param0 // CHECK:STDOUT: %Self.ref: type = name_ref Self, constants.%B [concrete = constants.%B] // CHECK:STDOUT: %self: %B = value_binding self, %self.param // CHECK:STDOUT: %return.param: ref %i32 = out_param call_param1 // CHECK:STDOUT: %return: ref %i32 = return_slot %return.param // CHECK:STDOUT: } // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%struct_type.internal.f18 [concrete = constants.%complete_type.ade] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%B // CHECK:STDOUT: .Internal = // CHECK:STDOUT: .internal [private] = %.loc14 // CHECK:STDOUT: .G = %B.G.decl // CHECK:STDOUT: .SomeFunc = %B.SomeFunc.decl // CHECK:STDOUT: .A = // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @B.G() -> out %return.param: %i32 { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %A.ref.loc24: type = name_ref A, file.%A.decl [concrete = constants.%A] // CHECK:STDOUT: %SOME_PRIVATE_CONSTANT.ref: = name_ref SOME_PRIVATE_CONSTANT, [concrete = ] // CHECK:STDOUT: %A.ref.loc33: type = name_ref A, file.%A.decl [concrete = constants.%A] // CHECK:STDOUT: %SOME_PROTECTED_CONSTANT.ref: = name_ref SOME_PROTECTED_CONSTANT, [concrete = ] // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @B.SomeFunc(%self.param: %B) -> out %return.param: %i32 { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %self.ref: %B = name_ref self, %self // CHECK:STDOUT: %internal.ref: %B.elem = name_ref internal, @B.%.loc14 [concrete = @B.%.loc14] // CHECK:STDOUT: %.loc44_16.1: ref %Internal = class_element_access %self.ref, element0 // CHECK:STDOUT: %.loc44_16.2: %Internal = acquire_value %.loc44_16.1 // CHECK:STDOUT: %INTERNAL_CONSTANT.ref: = name_ref INTERNAL_CONSTANT, [concrete = ] // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- fail_compound_member_access.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %A: type = class_type @A [concrete] // CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [concrete] // CHECK:STDOUT: %Int.type: type = generic_class_type @Int [concrete] // CHECK:STDOUT: %Int.generic: %Int.type = struct_value () [concrete] // CHECK:STDOUT: %i32: type = class_type @Int, @Int(%int_32) [concrete] // CHECK:STDOUT: %A.elem: type = unbound_element_type %A, %i32 [concrete] // CHECK:STDOUT: %struct_type.x: type = struct_type {.x: %i32} [concrete] // CHECK:STDOUT: %complete_type.1ec: = complete_type_witness %struct_type.x [concrete] // CHECK:STDOUT: %B: type = class_type @B [concrete] // CHECK:STDOUT: %B.elem: type = unbound_element_type %B, %A [concrete] // CHECK:STDOUT: %pattern_type.1f4: type = pattern_type %B [concrete] // CHECK:STDOUT: %B.F.type: type = fn_type @B.F [concrete] // CHECK:STDOUT: %B.F: %B.F.type = struct_value () [concrete] // CHECK:STDOUT: %struct_type.base.5af: type = struct_type {.base: %A} [concrete] // CHECK:STDOUT: %complete_type.0d1: = complete_type_witness %struct_type.base.5af [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: .Int = %Core.Int // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: %Core.Int: %Int.type = import_ref Core//prelude/parts/int, Int, loaded [concrete = constants.%Int.generic] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .A = %A.decl // CHECK:STDOUT: .B = %B.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %A.decl: type = class_decl @A [concrete = constants.%A] {} {} // CHECK:STDOUT: %B.decl: type = class_decl @B [concrete = constants.%B] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @A { // CHECK:STDOUT: %i32: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %.loc5: %A.elem = field_decl x, element0 [concrete] // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%struct_type.x [concrete = constants.%complete_type.1ec] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%A // CHECK:STDOUT: .x [private] = %.loc5 // CHECK:STDOUT: .A = // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @B { // CHECK:STDOUT: %A.ref: type = name_ref A, file.%A.decl [concrete = constants.%A] // CHECK:STDOUT: %.loc9: %B.elem = base_decl %A.ref, element0 [concrete] // CHECK:STDOUT: %B.F.decl: %B.F.type = fn_decl @B.F [concrete = constants.%B.F] { // CHECK:STDOUT: %self.patt: %pattern_type.1f4 = value_binding_pattern self [concrete] // CHECK:STDOUT: %self.param_patt: %pattern_type.1f4 = value_param_pattern %self.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %self.param: %B = value_param call_param0 // CHECK:STDOUT: %Self.ref: type = name_ref Self, constants.%B [concrete = constants.%B] // CHECK:STDOUT: %self: %B = value_binding self, %self.param // CHECK:STDOUT: } // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%struct_type.base.5af [concrete = constants.%complete_type.0d1] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%B // CHECK:STDOUT: .A = // CHECK:STDOUT: .base = %.loc9 // CHECK:STDOUT: .F = %B.F.decl // CHECK:STDOUT: extend %A.ref // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @B.F(%self.param: %B) { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %self.ref: %B = name_ref self, %self // CHECK:STDOUT: %A.ref: type = name_ref A, file.%A.decl [concrete = constants.%A] // CHECK:STDOUT: %x.ref: = name_ref x, [concrete = ] // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- inherited_compound_member_access.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %A: type = class_type @A [concrete] // CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [concrete] // CHECK:STDOUT: %Int.type: type = generic_class_type @Int [concrete] // CHECK:STDOUT: %Int.generic: %Int.type = struct_value () [concrete] // CHECK:STDOUT: %i32: type = class_type @Int, @Int(%int_32) [concrete] // CHECK:STDOUT: %A.elem: type = unbound_element_type %A, %i32 [concrete] // CHECK:STDOUT: %struct_type.x: type = struct_type {.x: %i32} [concrete] // CHECK:STDOUT: %complete_type.1ec: = complete_type_witness %struct_type.x [concrete] // CHECK:STDOUT: %B: type = class_type @B [concrete] // CHECK:STDOUT: %B.elem: type = unbound_element_type %B, %A [concrete] // CHECK:STDOUT: %pattern_type.1f4: type = pattern_type %B [concrete] // CHECK:STDOUT: %B.F.type: type = fn_type @B.F [concrete] // CHECK:STDOUT: %B.F: %B.F.type = struct_value () [concrete] // CHECK:STDOUT: %struct_type.base.5af: type = struct_type {.base: %A} [concrete] // CHECK:STDOUT: %complete_type.0d1: = complete_type_witness %struct_type.base.5af [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: .Int = %Core.Int // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: %Core.Int: %Int.type = import_ref Core//prelude/parts/int, Int, loaded [concrete = constants.%Int.generic] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .A = %A.decl // CHECK:STDOUT: .B = %B.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %A.decl: type = class_decl @A [concrete = constants.%A] {} {} // CHECK:STDOUT: %B.decl: type = class_decl @B [concrete = constants.%B] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @A { // CHECK:STDOUT: %i32: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %.loc5: %A.elem = field_decl x, element0 [concrete] // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%struct_type.x [concrete = constants.%complete_type.1ec] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%A // CHECK:STDOUT: .x [protected] = %.loc5 // CHECK:STDOUT: .A = // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @B { // CHECK:STDOUT: %A.ref: type = name_ref A, file.%A.decl [concrete = constants.%A] // CHECK:STDOUT: %.loc9: %B.elem = base_decl %A.ref, element0 [concrete] // CHECK:STDOUT: %B.F.decl: %B.F.type = fn_decl @B.F [concrete = constants.%B.F] { // CHECK:STDOUT: %self.patt: %pattern_type.1f4 = value_binding_pattern self [concrete] // CHECK:STDOUT: %self.param_patt: %pattern_type.1f4 = value_param_pattern %self.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %self.param: %B = value_param call_param0 // CHECK:STDOUT: %Self.ref: type = name_ref Self, constants.%B [concrete = constants.%B] // CHECK:STDOUT: %self: %B = value_binding self, %self.param // CHECK:STDOUT: } // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%struct_type.base.5af [concrete = constants.%complete_type.0d1] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%B // CHECK:STDOUT: .A = // CHECK:STDOUT: .base = %.loc9 // CHECK:STDOUT: .F = %B.F.decl // CHECK:STDOUT: extend %A.ref // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @B.F(%self.param: %B) { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %self.ref: %B = name_ref self, %self // CHECK:STDOUT: %A.ref: type = name_ref A, file.%A.decl [concrete = constants.%A] // CHECK:STDOUT: %x.ref: %A.elem = name_ref x, @A.%.loc5 [concrete = @A.%.loc5] // CHECK:STDOUT: %.loc12_9.1: ref %A = class_element_access %self.ref, element0 // CHECK:STDOUT: %.loc12_9.2: ref %A = converted %self.ref, %.loc12_9.1 // CHECK:STDOUT: %.loc12_9.3: ref %i32 = class_element_access %.loc12_9.2, element0 // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/class/access/method_access.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/none.carbon // TODO: Add ranges and switch to "--dump-sem-ir-ranges=only". // EXTRA-ARGS: --dump-sem-ir-ranges=if-present // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/class/access/method_access.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/class/access/method_access.carbon // --- fail_multiple_bindings.carbon class X { fn F[self: Self](); } fn G(x: X) { // TODO: Produce a better diagnostic for this case. // CHECK:STDERR: fail_multiple_bindings.carbon:[[@LINE+4]]:3: error: member name of type `` in compound member access is not an instance member or an interface member [CompoundMemberAccessDoesNotUseBase] // CHECK:STDERR: x.(x.F)(); // CHECK:STDERR: ^~~~~~~ // CHECK:STDERR: x.(x.F)(); } // CHECK:STDOUT: --- fail_multiple_bindings.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %X: type = class_type @X [concrete] // CHECK:STDOUT: %pattern_type: type = pattern_type %X [concrete] // CHECK:STDOUT: %X.F.type: type = fn_type @X.F [concrete] // CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] // CHECK:STDOUT: %X.F: %X.F.type = struct_value () [concrete] // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %complete_type: = complete_type_witness %empty_struct_type [concrete] // CHECK:STDOUT: %G.type: type = fn_type @G [concrete] // CHECK:STDOUT: %G: %G.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .X = %X.decl // CHECK:STDOUT: .G = %G.decl // CHECK:STDOUT: } // CHECK:STDOUT: %X.decl: type = class_decl @X [concrete = constants.%X] {} {} // CHECK:STDOUT: %G.decl: %G.type = fn_decl @G [concrete = constants.%G] { // CHECK:STDOUT: %x.patt: %pattern_type = value_binding_pattern x [concrete] // CHECK:STDOUT: %x.param_patt: %pattern_type = value_param_pattern %x.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %x.param: %X = value_param call_param0 // CHECK:STDOUT: %X.ref: type = name_ref X, file.%X.decl [concrete = constants.%X] // CHECK:STDOUT: %x: %X = value_binding x, %x.param // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @X { // CHECK:STDOUT: %X.F.decl: %X.F.type = fn_decl @X.F [concrete = constants.%X.F] { // CHECK:STDOUT: %self.patt: %pattern_type = value_binding_pattern self [concrete] // CHECK:STDOUT: %self.param_patt: %pattern_type = value_param_pattern %self.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %self.param: %X = value_param call_param0 // CHECK:STDOUT: %Self.ref: type = name_ref Self, constants.%X [concrete = constants.%X] // CHECK:STDOUT: %self: %X = value_binding self, %self.param // CHECK:STDOUT: } // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%X // CHECK:STDOUT: .F = %X.F.decl // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @X.F(%self.param: %X); // CHECK:STDOUT: // CHECK:STDOUT: fn @G(%x.param: %X) { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %x.ref.loc12_3: %X = name_ref x, %x // CHECK:STDOUT: %x.ref.loc12_6: %X = name_ref x, %x // CHECK:STDOUT: %F.ref: %X.F.type = name_ref F, @X.%X.F.decl [concrete = constants.%X.F] // CHECK:STDOUT: %X.F.bound: = bound_method %x.ref.loc12_6, %F.ref // CHECK:STDOUT: %X.F.call: init %empty_tuple.type = call %X.F.bound(%x.ref.loc12_6) // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/class/access/todo_access_modifiers.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/int.carbon // TODO: Add ranges and switch to "--dump-sem-ir-ranges=only". // EXTRA-ARGS: --dump-sem-ir-ranges=if-present // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/class/access/todo_access_modifiers.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/class/access/todo_access_modifiers.carbon // TODO: Test calls to these (member access control is not yet implemented). class Access { private fn F(); protected fn G(); private var k: i32; protected var l: i32; } // CHECK:STDOUT: --- todo_access_modifiers.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %Access: type = class_type @Access [concrete] // CHECK:STDOUT: %Access.F.type: type = fn_type @Access.F [concrete] // CHECK:STDOUT: %Access.F: %Access.F.type = struct_value () [concrete] // CHECK:STDOUT: %Access.G.type: type = fn_type @Access.G [concrete] // CHECK:STDOUT: %Access.G: %Access.G.type = struct_value () [concrete] // CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [concrete] // CHECK:STDOUT: %Int.type: type = generic_class_type @Int [concrete] // CHECK:STDOUT: %Int.generic: %Int.type = struct_value () [concrete] // CHECK:STDOUT: %i32: type = class_type @Int, @Int(%int_32) [concrete] // CHECK:STDOUT: %Access.elem: type = unbound_element_type %Access, %i32 [concrete] // CHECK:STDOUT: %struct_type.k.l: type = struct_type {.k: %i32, .l: %i32} [concrete] // CHECK:STDOUT: %complete_type.48a: = complete_type_witness %struct_type.k.l [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: .Int = %Core.Int // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: %Core.Int: %Int.type = import_ref Core//prelude/parts/int, Int, loaded [concrete = constants.%Int.generic] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .Access = %Access.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %Access.decl: type = class_decl @Access [concrete = constants.%Access] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @Access { // CHECK:STDOUT: %Access.F.decl: %Access.F.type = fn_decl @Access.F [concrete = constants.%Access.F] {} {} // CHECK:STDOUT: %Access.G.decl: %Access.G.type = fn_decl @Access.G [concrete = constants.%Access.G] {} {} // CHECK:STDOUT: %i32.loc21: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %.loc21: %Access.elem = field_decl k, element0 [concrete] // CHECK:STDOUT: %i32.loc23: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %.loc23: %Access.elem = field_decl l, element1 [concrete] // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%struct_type.k.l [concrete = constants.%complete_type.48a] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%Access // CHECK:STDOUT: .F [private] = %Access.F.decl // CHECK:STDOUT: .G [protected] = %Access.G.decl // CHECK:STDOUT: .k [private] = %.loc21 // CHECK:STDOUT: .l [protected] = %.loc23 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @Access.F(); // CHECK:STDOUT: // CHECK:STDOUT: fn @Access.G(); // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/class/adapter/adapt.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/int.carbon // TODO: Add ranges and switch to "--dump-sem-ir-ranges=only". // EXTRA-ARGS: --dump-sem-ir-ranges=if-present // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/class/adapter/adapt.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/class/adapter/adapt.carbon // --- basic.carbon library "[[@TEST_NAME]]"; class SomeClass { var a: i32; var b: i32; } class SomeClassAdapter { adapt SomeClass; } class StructAdapter { adapt {.a: i32, .b: i32}; } // --- fail_not_extend.carbon library "[[@TEST_NAME]]"; class Adapted { fn F(); } class AdaptNotExtend { adapt Adapted; } fn F(a: AdaptNotExtend) { // `Adapted` is not extended, so lookup for `F` finds nothing. // CHECK:STDERR: fail_not_extend.carbon:[[@LINE+4]]:3: error: member name `F` not found in `AdaptNotExtend` [MemberNameNotFoundInInstScope] // CHECK:STDERR: a.F(); // CHECK:STDERR: ^~~ // CHECK:STDERR: a.F(); } // --- fail_misplaced.carbon fn F() { // CHECK:STDERR: fail_misplaced.carbon:[[@LINE+4]]:3: error: `adapt` declaration outside class [ClassSpecificDeclOutsideClass] // CHECK:STDERR: adapt i32; // CHECK:STDERR: ^~~~~~~~~~ // CHECK:STDERR: adapt i32; } interface I { // CHECK:STDERR: fail_misplaced.carbon:[[@LINE+4]]:3: error: `adapt` declaration outside class [ClassSpecificDeclOutsideClass] // CHECK:STDERR: adapt i32; // CHECK:STDERR: ^~~~~~~~~~ // CHECK:STDERR: adapt i32; } // CHECK:STDOUT: --- basic.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %SomeClass: type = class_type @SomeClass [concrete] // CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [concrete] // CHECK:STDOUT: %Int.type: type = generic_class_type @Int [concrete] // CHECK:STDOUT: %Int.generic: %Int.type = struct_value () [concrete] // CHECK:STDOUT: %i32: type = class_type @Int, @Int(%int_32) [concrete] // CHECK:STDOUT: %SomeClass.elem: type = unbound_element_type %SomeClass, %i32 [concrete] // CHECK:STDOUT: %struct_type.a.b: type = struct_type {.a: %i32, .b: %i32} [concrete] // CHECK:STDOUT: %complete_type.705: = complete_type_witness %struct_type.a.b [concrete] // CHECK:STDOUT: %SomeClassAdapter: type = class_type @SomeClassAdapter [concrete] // CHECK:STDOUT: %StructAdapter: type = class_type @StructAdapter [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: .Int = %Core.Int // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: %Core.Int: %Int.type = import_ref Core//prelude/parts/int, Int, loaded [concrete = constants.%Int.generic] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .SomeClass = %SomeClass.decl // CHECK:STDOUT: .SomeClassAdapter = %SomeClassAdapter.decl // CHECK:STDOUT: .StructAdapter = %StructAdapter.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %SomeClass.decl: type = class_decl @SomeClass [concrete = constants.%SomeClass] {} {} // CHECK:STDOUT: %SomeClassAdapter.decl: type = class_decl @SomeClassAdapter [concrete = constants.%SomeClassAdapter] {} {} // CHECK:STDOUT: %StructAdapter.decl: type = class_decl @StructAdapter [concrete = constants.%StructAdapter] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @SomeClass { // CHECK:STDOUT: %i32.loc5: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %.loc5: %SomeClass.elem = field_decl a, element0 [concrete] // CHECK:STDOUT: %i32.loc6: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %.loc6: %SomeClass.elem = field_decl b, element1 [concrete] // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%struct_type.a.b [concrete = constants.%complete_type.705] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%SomeClass // CHECK:STDOUT: .a = %.loc5 // CHECK:STDOUT: .b = %.loc6 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @SomeClassAdapter { // CHECK:STDOUT: %SomeClass.ref: type = name_ref SomeClass, file.%SomeClass.decl [concrete = constants.%SomeClass] // CHECK:STDOUT: adapt_decl %SomeClass.ref [concrete] // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%struct_type.a.b [concrete = constants.%complete_type.705] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%SomeClassAdapter // CHECK:STDOUT: .SomeClass = // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @StructAdapter { // CHECK:STDOUT: %i32.loc14_14: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %i32.loc14_23: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %struct_type.a.b: type = struct_type {.a: %i32, .b: %i32} [concrete = constants.%struct_type.a.b] // CHECK:STDOUT: adapt_decl %struct_type.a.b [concrete] // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%struct_type.a.b [concrete = constants.%complete_type.705] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%StructAdapter // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- fail_not_extend.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %Adapted: type = class_type @Adapted [concrete] // CHECK:STDOUT: %Adapted.F.type: type = fn_type @Adapted.F [concrete] // CHECK:STDOUT: %Adapted.F: %Adapted.F.type = struct_value () [concrete] // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %complete_type: = complete_type_witness %empty_struct_type [concrete] // CHECK:STDOUT: %AdaptNotExtend: type = class_type @AdaptNotExtend [concrete] // CHECK:STDOUT: %pattern_type: type = pattern_type %AdaptNotExtend [concrete] // CHECK:STDOUT: %F.type: type = fn_type @F [concrete] // CHECK:STDOUT: %F: %F.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .Adapted = %Adapted.decl // CHECK:STDOUT: .AdaptNotExtend = %AdaptNotExtend.decl // CHECK:STDOUT: .F = %F.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %Adapted.decl: type = class_decl @Adapted [concrete = constants.%Adapted] {} {} // CHECK:STDOUT: %AdaptNotExtend.decl: type = class_decl @AdaptNotExtend [concrete = constants.%AdaptNotExtend] {} {} // CHECK:STDOUT: %F.decl: %F.type = fn_decl @F [concrete = constants.%F] { // CHECK:STDOUT: %a.patt: %pattern_type = value_binding_pattern a [concrete] // CHECK:STDOUT: %a.param_patt: %pattern_type = value_param_pattern %a.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %a.param: %AdaptNotExtend = value_param call_param0 // CHECK:STDOUT: %AdaptNotExtend.ref: type = name_ref AdaptNotExtend, file.%AdaptNotExtend.decl [concrete = constants.%AdaptNotExtend] // CHECK:STDOUT: %a: %AdaptNotExtend = value_binding a, %a.param // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @Adapted { // CHECK:STDOUT: %Adapted.F.decl: %Adapted.F.type = fn_decl @Adapted.F [concrete = constants.%Adapted.F] {} {} // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%Adapted // CHECK:STDOUT: .F = %Adapted.F.decl // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @AdaptNotExtend { // CHECK:STDOUT: %Adapted.ref: type = name_ref Adapted, file.%Adapted.decl [concrete = constants.%Adapted] // CHECK:STDOUT: adapt_decl %Adapted.ref [concrete] // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%AdaptNotExtend // CHECK:STDOUT: .Adapted = // CHECK:STDOUT: .F = // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @Adapted.F(); // CHECK:STDOUT: // CHECK:STDOUT: fn @F(%a.param: %AdaptNotExtend) { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %a.ref: %AdaptNotExtend = name_ref a, %a // CHECK:STDOUT: %F.ref: = name_ref F, [concrete = ] // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- fail_misplaced.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %F.type: type = fn_type @F [concrete] // CHECK:STDOUT: %F: %F.type = struct_value () [concrete] // CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [concrete] // CHECK:STDOUT: %Int.type: type = generic_class_type @Int [concrete] // CHECK:STDOUT: %Int.generic: %Int.type = struct_value () [concrete] // CHECK:STDOUT: %i32: type = class_type @Int, @Int(%int_32) [concrete] // CHECK:STDOUT: %I.type: type = facet_type <@I> [concrete] // CHECK:STDOUT: %Self: %I.type = symbolic_binding Self, 0 [symbolic] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: .Int = %Core.Int // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: %Core.Int: %Int.type = import_ref Core//prelude/parts/int, Int, loaded [concrete = constants.%Int.generic] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .F = %F.decl // CHECK:STDOUT: .I = %I.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %F.decl: %F.type = fn_decl @F [concrete = constants.%F] {} {} // CHECK:STDOUT: %I.decl: type = interface_decl @I [concrete = constants.%I.type] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: interface @I { // CHECK:STDOUT: %Self: %I.type = symbolic_binding Self, 0 [symbolic = constants.%Self] // CHECK:STDOUT: %I.WithSelf.decl = interface_with_self_decl @I [concrete] // CHECK:STDOUT: // CHECK:STDOUT: !with Self: // CHECK:STDOUT: %i32: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = %Self // CHECK:STDOUT: witness = () // CHECK:STDOUT: // CHECK:STDOUT: !requires: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @F() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %i32: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @I.WithSelf(constants.%Self) {} // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/class/adapter/adapt_copy.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/uint.carbon // TODO: Add ranges and switch to "--dump-sem-ir-ranges=only". // EXTRA-ARGS: --dump-sem-ir-ranges=if-present // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/class/adapter/adapt_copy.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/class/adapter/adapt_copy.carbon // TODO: Decide whether an adapter for a copyable type is copyable. As // demonstrated in this test, our behavior is currently inconsistent. // --- fail_adapt_copyable.carbon library "[[@TEST_NAME]]"; class AdaptCopyable { adapt i32; } fn F(c: AdaptCopyable) -> AdaptCopyable { // CHECK:STDERR: fail_adapt_copyable.carbon:[[@LINE+7]]:26: error: cannot copy value of type `AdaptCopyable` [CopyOfUncopyableType] // CHECK:STDERR: var d: AdaptCopyable = c; // CHECK:STDERR: ^ // CHECK:STDERR: fail_adapt_copyable.carbon:[[@LINE+4]]:26: note: type `AdaptCopyable` does not implement interface `Core.Copy` [MissingImplInMemberAccessInContext] // CHECK:STDERR: var d: AdaptCopyable = c; // CHECK:STDERR: ^ // CHECK:STDERR: var d: AdaptCopyable = c; // CHECK:STDERR: fail_adapt_copyable.carbon:[[@LINE+7]]:10: error: cannot copy value of type `AdaptCopyable` [CopyOfUncopyableType] // CHECK:STDERR: return d; // CHECK:STDERR: ^ // CHECK:STDERR: fail_adapt_copyable.carbon:[[@LINE+4]]:10: note: type `AdaptCopyable` does not implement interface `Core.Copy` [MissingImplInMemberAccessInContext] // CHECK:STDERR: return d; // CHECK:STDERR: ^ // CHECK:STDERR: return d; } fn InTuple(c: (AdaptCopyable, u32)) -> (AdaptCopyable, u32) { // CHECK:STDERR: fail_adapt_copyable.carbon:[[@LINE+7]]:33: error: cannot copy value of type `AdaptCopyable` [CopyOfUncopyableType] // CHECK:STDERR: var d: (AdaptCopyable, u32) = c; // CHECK:STDERR: ^ // CHECK:STDERR: fail_adapt_copyable.carbon:[[@LINE+4]]:33: note: type `AdaptCopyable` does not implement interface `Core.Copy` [MissingImplInMemberAccessInContext] // CHECK:STDERR: var d: (AdaptCopyable, u32) = c; // CHECK:STDERR: ^ // CHECK:STDERR: var d: (AdaptCopyable, u32) = c; // CHECK:STDERR: fail_adapt_copyable.carbon:[[@LINE+7]]:10: error: cannot copy value of type `AdaptCopyable` [CopyOfUncopyableType] // CHECK:STDERR: return d; // CHECK:STDERR: ^ // CHECK:STDERR: fail_adapt_copyable.carbon:[[@LINE+4]]:10: note: type `AdaptCopyable` does not implement interface `Core.Copy` [MissingImplInMemberAccessInContext] // CHECK:STDERR: return d; // CHECK:STDERR: ^ // CHECK:STDERR: return d; } // --- adapt_copyable_tuple.carbon library "[[@TEST_NAME]]"; class AdaptTuple { adapt (i32, i32); } fn F(c: AdaptTuple) -> AdaptTuple { var d: AdaptTuple = c; return d; } fn InTuple(c: (AdaptTuple, u32)) -> (AdaptTuple, u32) { var d: (AdaptTuple, u32) = c; return d; } // --- fail_adapt_not_copyable.carbon library "[[@TEST_NAME]]"; class Noncopyable { // TODO: Ensure this remains non-copyable once we have rules for class copyability. } class AdaptNoncopyable { adapt Noncopyable; } fn G(a: AdaptNoncopyable) -> AdaptNoncopyable { // CHECK:STDERR: fail_adapt_not_copyable.carbon:[[@LINE+7]]:29: error: cannot copy value of type `AdaptNoncopyable` [CopyOfUncopyableType] // CHECK:STDERR: var b: AdaptNoncopyable = a; // CHECK:STDERR: ^ // CHECK:STDERR: fail_adapt_not_copyable.carbon:[[@LINE+4]]:29: note: type `AdaptNoncopyable` does not implement interface `Core.Copy` [MissingImplInMemberAccessInContext] // CHECK:STDERR: var b: AdaptNoncopyable = a; // CHECK:STDERR: ^ // CHECK:STDERR: var b: AdaptNoncopyable = a; // CHECK:STDERR: fail_adapt_not_copyable.carbon:[[@LINE+7]]:10: error: cannot copy value of type `AdaptNoncopyable` [CopyOfUncopyableType] // CHECK:STDERR: return b; // CHECK:STDERR: ^ // CHECK:STDERR: fail_adapt_not_copyable.carbon:[[@LINE+4]]:10: note: type `AdaptNoncopyable` does not implement interface `Core.Copy` [MissingImplInMemberAccessInContext] // CHECK:STDERR: return b; // CHECK:STDERR: ^ // CHECK:STDERR: return b; } // --- fail_adapt_not_copyable_indirect.carbon library "[[@TEST_NAME]]"; class Noncopyable { // TODO: Ensure this remains non-copyable once we have rules for class copyability. } class AdaptNoncopyableIndirect { adapt (i32, Noncopyable, i32); } fn H(a: AdaptNoncopyableIndirect) -> AdaptNoncopyableIndirect { // CHECK:STDERR: fail_adapt_not_copyable_indirect.carbon:[[@LINE+10]]:3: error: cannot copy value of type `Noncopyable` [CopyOfUncopyableType] // CHECK:STDERR: var b: AdaptNoncopyableIndirect = a; // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: fail_adapt_not_copyable_indirect.carbon:[[@LINE+7]]:3: note: type `Noncopyable` does not implement interface `Core.Copy` [MissingImplInMemberAccessInContext] // CHECK:STDERR: var b: AdaptNoncopyableIndirect = a; // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: fail_adapt_not_copyable_indirect.carbon:[[@LINE+4]]:37: note: in copy of `AdaptNoncopyableIndirect` [InCopy] // CHECK:STDERR: var b: AdaptNoncopyableIndirect = a; // CHECK:STDERR: ^ // CHECK:STDERR: var b: AdaptNoncopyableIndirect = a; // CHECK:STDERR: fail_adapt_not_copyable_indirect.carbon:[[@LINE+10]]:3: error: cannot copy value of type `Noncopyable` [CopyOfUncopyableType] // CHECK:STDERR: return b; // CHECK:STDERR: ^~~~~~~~~ // CHECK:STDERR: fail_adapt_not_copyable_indirect.carbon:[[@LINE+7]]:3: note: type `Noncopyable` does not implement interface `Core.Copy` [MissingImplInMemberAccessInContext] // CHECK:STDERR: return b; // CHECK:STDERR: ^~~~~~~~~ // CHECK:STDERR: fail_adapt_not_copyable_indirect.carbon:[[@LINE+4]]:10: note: in copy of `AdaptNoncopyableIndirect` [InCopy] // CHECK:STDERR: return b; // CHECK:STDERR: ^ // CHECK:STDERR: return b; } // --- adapt_copyable_struct.carbon library "[[@TEST_NAME]]"; class AdaptStruct { adapt {.e: i32, .f: i32}; } fn I(g: AdaptStruct) -> AdaptStruct { var h: AdaptStruct = g; return h; } fn InTuple(c: (AdaptStruct, u32)) -> (AdaptStruct, u32) { var d: (AdaptStruct, u32) = c; return d; } // CHECK:STDOUT: --- fail_adapt_copyable.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %AdaptCopyable: type = class_type @AdaptCopyable [concrete] // CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [concrete] // CHECK:STDOUT: %Int.type: type = generic_class_type @Int [concrete] // CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] // CHECK:STDOUT: %Int.generic: %Int.type = struct_value () [concrete] // CHECK:STDOUT: %i32: type = class_type @Int, @Int(%int_32) [concrete] // CHECK:STDOUT: %i32.builtin: type = int_type signed, %int_32 [concrete] // CHECK:STDOUT: %complete_type.f8a: = complete_type_witness %i32.builtin [concrete] // CHECK:STDOUT: %pattern_type.e78: type = pattern_type %AdaptCopyable [concrete] // CHECK:STDOUT: %.23e: Core.Form = init_form %AdaptCopyable [concrete] // CHECK:STDOUT: %F.type: type = fn_type @F [concrete] // CHECK:STDOUT: %F: %F.type = struct_value () [concrete] // CHECK:STDOUT: %Copy.type: type = facet_type <@Copy> [concrete] // CHECK:STDOUT: %Destroy.type: type = facet_type <@Destroy> [concrete] // CHECK:STDOUT: %Destroy.Op.type.bae255.1: type = fn_type @Destroy.Op.loc16 [concrete] // CHECK:STDOUT: %Destroy.Op.651ba6.1: %Destroy.Op.type.bae255.1 = struct_value () [concrete] // CHECK:STDOUT: %UInt.type: type = generic_class_type @UInt [concrete] // CHECK:STDOUT: %UInt.generic: %UInt.type = struct_value () [concrete] // CHECK:STDOUT: %u32: type = class_type @UInt, @UInt(%int_32) [concrete] // CHECK:STDOUT: %tuple.type.24b: type = tuple_type (type, type) [concrete] // CHECK:STDOUT: %tuple.fd0: %tuple.type.24b = tuple_value (%AdaptCopyable, %u32) [concrete] // CHECK:STDOUT: %tuple.type.d78: type = tuple_type (%AdaptCopyable, %u32) [concrete] // CHECK:STDOUT: %pattern_type.87f: type = pattern_type %tuple.type.d78 [concrete] // CHECK:STDOUT: %.5d1: Core.Form = init_form %tuple.type.d78 [concrete] // CHECK:STDOUT: %InTuple.type: type = fn_type @InTuple [concrete] // CHECK:STDOUT: %InTuple: %InTuple.type = struct_value () [concrete] // CHECK:STDOUT: %Destroy.Op.type.bae255.2: type = fn_type @Destroy.Op.loc35 [concrete] // CHECK:STDOUT: %Destroy.Op.651ba6.2: %Destroy.Op.type.bae255.2 = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: .Int = %Core.Int // CHECK:STDOUT: .Copy = %Core.Copy // CHECK:STDOUT: .Destroy = %Core.Destroy // CHECK:STDOUT: .UInt = %Core.UInt // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: %Core.Int: %Int.type = import_ref Core//prelude/parts/int, Int, loaded [concrete = constants.%Int.generic] // CHECK:STDOUT: %Core.Copy: type = import_ref Core//prelude/parts/copy, Copy, loaded [concrete = constants.%Copy.type] // CHECK:STDOUT: %Core.Destroy: type = import_ref Core//prelude/parts/destroy, Destroy, loaded [concrete = constants.%Destroy.type] // CHECK:STDOUT: %Core.UInt: %UInt.type = import_ref Core//prelude/parts/uint, UInt, loaded [concrete = constants.%UInt.generic] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .AdaptCopyable = %AdaptCopyable.decl // CHECK:STDOUT: .F = %F.decl // CHECK:STDOUT: .InTuple = %InTuple.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %AdaptCopyable.decl: type = class_decl @AdaptCopyable [concrete = constants.%AdaptCopyable] {} {} // CHECK:STDOUT: %F.decl: %F.type = fn_decl @F [concrete = constants.%F] { // CHECK:STDOUT: %c.patt: %pattern_type.e78 = value_binding_pattern c [concrete] // CHECK:STDOUT: %c.param_patt: %pattern_type.e78 = value_param_pattern %c.patt [concrete] // CHECK:STDOUT: %return.patt: %pattern_type.e78 = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: %pattern_type.e78 = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %AdaptCopyable.ref.loc8_27: type = name_ref AdaptCopyable, file.%AdaptCopyable.decl [concrete = constants.%AdaptCopyable] // CHECK:STDOUT: %.loc8: Core.Form = init_form %AdaptCopyable.ref.loc8_27 [concrete = constants.%.23e] // CHECK:STDOUT: %c.param: %AdaptCopyable = value_param call_param0 // CHECK:STDOUT: %AdaptCopyable.ref.loc8_9: type = name_ref AdaptCopyable, file.%AdaptCopyable.decl [concrete = constants.%AdaptCopyable] // CHECK:STDOUT: %c: %AdaptCopyable = value_binding c, %c.param // CHECK:STDOUT: %return.param: ref %AdaptCopyable = out_param call_param1 // CHECK:STDOUT: %return: ref %AdaptCopyable = return_slot %return.param // CHECK:STDOUT: } // CHECK:STDOUT: %InTuple.decl: %InTuple.type = fn_decl @InTuple [concrete = constants.%InTuple] { // CHECK:STDOUT: %c.patt: %pattern_type.87f = value_binding_pattern c [concrete] // CHECK:STDOUT: %c.param_patt: %pattern_type.87f = value_param_pattern %c.patt [concrete] // CHECK:STDOUT: %return.patt: %pattern_type.87f = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: %pattern_type.87f = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %AdaptCopyable.ref.loc27_41: type = name_ref AdaptCopyable, file.%AdaptCopyable.decl [concrete = constants.%AdaptCopyable] // CHECK:STDOUT: %u32.loc27_56: type = type_literal constants.%u32 [concrete = constants.%u32] // CHECK:STDOUT: %.loc27_59.1: %tuple.type.24b = tuple_literal (%AdaptCopyable.ref.loc27_41, %u32.loc27_56) [concrete = constants.%tuple.fd0] // CHECK:STDOUT: %.loc27_59.2: type = converted %.loc27_59.1, constants.%tuple.type.d78 [concrete = constants.%tuple.type.d78] // CHECK:STDOUT: %.loc27_59.3: Core.Form = init_form %.loc27_59.2 [concrete = constants.%.5d1] // CHECK:STDOUT: %c.param: %tuple.type.d78 = value_param call_param0 // CHECK:STDOUT: %.loc27_34.1: type = splice_block %.loc27_34.3 [concrete = constants.%tuple.type.d78] { // CHECK:STDOUT: %AdaptCopyable.ref.loc27_16: type = name_ref AdaptCopyable, file.%AdaptCopyable.decl [concrete = constants.%AdaptCopyable] // CHECK:STDOUT: %u32.loc27_31: type = type_literal constants.%u32 [concrete = constants.%u32] // CHECK:STDOUT: %.loc27_34.2: %tuple.type.24b = tuple_literal (%AdaptCopyable.ref.loc27_16, %u32.loc27_31) [concrete = constants.%tuple.fd0] // CHECK:STDOUT: %.loc27_34.3: type = converted %.loc27_34.2, constants.%tuple.type.d78 [concrete = constants.%tuple.type.d78] // CHECK:STDOUT: } // CHECK:STDOUT: %c: %tuple.type.d78 = value_binding c, %c.param // CHECK:STDOUT: %return.param: ref %tuple.type.d78 = out_param call_param1 // CHECK:STDOUT: %return: ref %tuple.type.d78 = return_slot %return.param // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @AdaptCopyable { // CHECK:STDOUT: %i32: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: adapt_decl %i32 [concrete] // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%i32.builtin [concrete = constants.%complete_type.f8a] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%AdaptCopyable // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @F(%c.param: %AdaptCopyable) -> out %return.param: %AdaptCopyable { // CHECK:STDOUT: !entry: // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %d.patt: %pattern_type.e78 = ref_binding_pattern d [concrete] // CHECK:STDOUT: %d.var_patt: %pattern_type.e78 = var_pattern %d.patt [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %d.var: ref %AdaptCopyable = var %d.var_patt // CHECK:STDOUT: %c.ref: %AdaptCopyable = name_ref c, %c // CHECK:STDOUT: assign %d.var, // CHECK:STDOUT: %AdaptCopyable.ref.loc16: type = name_ref AdaptCopyable, file.%AdaptCopyable.decl [concrete = constants.%AdaptCopyable] // CHECK:STDOUT: %d: ref %AdaptCopyable = ref_binding d, %d.var // CHECK:STDOUT: %d.ref: ref %AdaptCopyable = name_ref d, %d // CHECK:STDOUT: %.loc24: %AdaptCopyable = acquire_value %d.ref // CHECK:STDOUT: %Destroy.Op.bound: = bound_method %d.var, constants.%Destroy.Op.651ba6.1 // CHECK:STDOUT: %Destroy.Op.call: init %empty_tuple.type = call %Destroy.Op.bound(%d.var) // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @Destroy.Op.loc16(%self.param: ref %AdaptCopyable) = "no_op"; // CHECK:STDOUT: // CHECK:STDOUT: fn @InTuple(%c.param: %tuple.type.d78) -> out %return.param: %tuple.type.d78 { // CHECK:STDOUT: !entry: // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %d.patt: %pattern_type.87f = ref_binding_pattern d [concrete] // CHECK:STDOUT: %d.var_patt: %pattern_type.87f = var_pattern %d.patt [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %d.var: ref %tuple.type.d78 = var %d.var_patt // CHECK:STDOUT: %c.ref: %tuple.type.d78 = name_ref c, %c // CHECK:STDOUT: %tuple.elem0.loc35: %AdaptCopyable = tuple_access %c.ref, element0 // CHECK:STDOUT: assign %d.var, // CHECK:STDOUT: %.loc35_29.1: type = splice_block %.loc35_29.3 [concrete = constants.%tuple.type.d78] { // CHECK:STDOUT: %AdaptCopyable.ref.loc35: type = name_ref AdaptCopyable, file.%AdaptCopyable.decl [concrete = constants.%AdaptCopyable] // CHECK:STDOUT: %u32.loc35: type = type_literal constants.%u32 [concrete = constants.%u32] // CHECK:STDOUT: %.loc35_29.2: %tuple.type.24b = tuple_literal (%AdaptCopyable.ref.loc35, %u32.loc35) [concrete = constants.%tuple.fd0] // CHECK:STDOUT: %.loc35_29.3: type = converted %.loc35_29.2, constants.%tuple.type.d78 [concrete = constants.%tuple.type.d78] // CHECK:STDOUT: } // CHECK:STDOUT: %d: ref %tuple.type.d78 = ref_binding d, %d.var // CHECK:STDOUT: %d.ref: ref %tuple.type.d78 = name_ref d, %d // CHECK:STDOUT: %tuple.elem0.loc43: ref %AdaptCopyable = tuple_access %d.ref, element0 // CHECK:STDOUT: %.loc43: %AdaptCopyable = acquire_value %tuple.elem0.loc43 // CHECK:STDOUT: %Destroy.Op.bound: = bound_method %d.var, constants.%Destroy.Op.651ba6.2 // CHECK:STDOUT: %Destroy.Op.call: init %empty_tuple.type = call %Destroy.Op.bound(%d.var) // CHECK:STDOUT: return to %return.param // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @Destroy.Op.loc35(%self.param: ref %tuple.type.d78) = "no_op"; // CHECK:STDOUT: // CHECK:STDOUT: --- adapt_copyable_tuple.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %AdaptTuple: type = class_type @AdaptTuple [concrete] // CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [concrete] // CHECK:STDOUT: %Int.type: type = generic_class_type @Int [concrete] // CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] // CHECK:STDOUT: %Int.generic: %Int.type = struct_value () [concrete] // CHECK:STDOUT: %N: Core.IntLiteral = symbolic_binding N, 0 [symbolic] // CHECK:STDOUT: %i32: type = class_type @Int, @Int(%int_32) [concrete] // CHECK:STDOUT: %tuple.type.24b: type = tuple_type (type, type) [concrete] // CHECK:STDOUT: %tuple.95a: %tuple.type.24b = tuple_value (%i32, %i32) [concrete] // CHECK:STDOUT: %tuple.type.d07: type = tuple_type (%i32, %i32) [concrete] // CHECK:STDOUT: %complete_type.65d: = complete_type_witness %tuple.type.d07 [concrete] // CHECK:STDOUT: %pattern_type.6cd: type = pattern_type %AdaptTuple [concrete] // CHECK:STDOUT: %.52a: Core.Form = init_form %AdaptTuple [concrete] // CHECK:STDOUT: %F.type: type = fn_type @F [concrete] // CHECK:STDOUT: %F: %F.type = struct_value () [concrete] // CHECK:STDOUT: %Copy.type: type = facet_type <@Copy> [concrete] // CHECK:STDOUT: %Int.as.Copy.impl.Op.type.824: type = fn_type @Int.as.Copy.impl.Op, @Int.as.Copy.impl(%N) [symbolic] // CHECK:STDOUT: %Int.as.Copy.impl.Op.9b9: %Int.as.Copy.impl.Op.type.824 = struct_value () [symbolic] // CHECK:STDOUT: %Copy.impl_witness.f17: = impl_witness imports.%Copy.impl_witness_table.e76, @Int.as.Copy.impl(%int_32) [concrete] // CHECK:STDOUT: %Int.as.Copy.impl.Op.type.546: type = fn_type @Int.as.Copy.impl.Op, @Int.as.Copy.impl(%int_32) [concrete] // CHECK:STDOUT: %Int.as.Copy.impl.Op.664: %Int.as.Copy.impl.Op.type.546 = struct_value () [concrete] // CHECK:STDOUT: %Copy.facet.de4: %Copy.type = facet_value %i32, (%Copy.impl_witness.f17) [concrete] // CHECK:STDOUT: %Copy.WithSelf.Op.type.081: type = fn_type @Copy.WithSelf.Op, @Copy.WithSelf(%Copy.facet.de4) [concrete] // CHECK:STDOUT: %.8e2: type = fn_type_with_self_type %Copy.WithSelf.Op.type.081, %Copy.facet.de4 [concrete] // CHECK:STDOUT: %Int.as.Copy.impl.Op.specific_fn: = specific_function %Int.as.Copy.impl.Op.664, @Int.as.Copy.impl.Op(%int_32) [concrete] // CHECK:STDOUT: %Destroy.type: type = facet_type <@Destroy> [concrete] // CHECK:STDOUT: %Destroy.Op.type.bae255.1: type = fn_type @Destroy.Op.loc9 [concrete] // CHECK:STDOUT: %Destroy.Op.651ba6.1: %Destroy.Op.type.bae255.1 = struct_value () [concrete] // CHECK:STDOUT: %UInt.type: type = generic_class_type @UInt [concrete] // CHECK:STDOUT: %UInt.generic: %UInt.type = struct_value () [concrete] // CHECK:STDOUT: %u32: type = class_type @UInt, @UInt(%int_32) [concrete] // CHECK:STDOUT: %tuple.b75: %tuple.type.24b = tuple_value (%AdaptTuple, %u32) [concrete] // CHECK:STDOUT: %tuple.type.3c7: type = tuple_type (%AdaptTuple, %u32) [concrete] // CHECK:STDOUT: %pattern_type.6f4: type = pattern_type %tuple.type.3c7 [concrete] // CHECK:STDOUT: %.709: Core.Form = init_form %tuple.type.3c7 [concrete] // CHECK:STDOUT: %InTuple.type: type = fn_type @InTuple [concrete] // CHECK:STDOUT: %InTuple: %InTuple.type = struct_value () [concrete] // CHECK:STDOUT: %UInt.as.Copy.impl.Op.type.68f: type = fn_type @UInt.as.Copy.impl.Op, @UInt.as.Copy.impl(%N) [symbolic] // CHECK:STDOUT: %UInt.as.Copy.impl.Op.576: %UInt.as.Copy.impl.Op.type.68f = struct_value () [symbolic] // CHECK:STDOUT: %Copy.impl_witness.514: = impl_witness imports.%Copy.impl_witness_table.bd0, @UInt.as.Copy.impl(%int_32) [concrete] // CHECK:STDOUT: %UInt.as.Copy.impl.Op.type.2fc: type = fn_type @UInt.as.Copy.impl.Op, @UInt.as.Copy.impl(%int_32) [concrete] // CHECK:STDOUT: %UInt.as.Copy.impl.Op.c10: %UInt.as.Copy.impl.Op.type.2fc = struct_value () [concrete] // CHECK:STDOUT: %Copy.facet.10b: %Copy.type = facet_value %u32, (%Copy.impl_witness.514) [concrete] // CHECK:STDOUT: %Copy.WithSelf.Op.type.ad7: type = fn_type @Copy.WithSelf.Op, @Copy.WithSelf(%Copy.facet.10b) [concrete] // CHECK:STDOUT: %.38c: type = fn_type_with_self_type %Copy.WithSelf.Op.type.ad7, %Copy.facet.10b [concrete] // CHECK:STDOUT: %UInt.as.Copy.impl.Op.specific_fn: = specific_function %UInt.as.Copy.impl.Op.c10, @UInt.as.Copy.impl.Op(%int_32) [concrete] // CHECK:STDOUT: %Destroy.Op.type.bae255.2: type = fn_type @Destroy.Op.loc14 [concrete] // CHECK:STDOUT: %Destroy.Op.651ba6.2: %Destroy.Op.type.bae255.2 = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: .Int = %Core.Int // CHECK:STDOUT: .Copy = %Core.Copy // CHECK:STDOUT: .Destroy = %Core.Destroy // CHECK:STDOUT: .UInt = %Core.UInt // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: %Core.Int: %Int.type = import_ref Core//prelude/parts/int, Int, loaded [concrete = constants.%Int.generic] // CHECK:STDOUT: %Core.Copy: type = import_ref Core//prelude/parts/copy, Copy, loaded [concrete = constants.%Copy.type] // CHECK:STDOUT: %Core.import_ref.18d: @Int.as.Copy.impl.%Int.as.Copy.impl.Op.type (%Int.as.Copy.impl.Op.type.824) = import_ref Core//prelude/parts/int, loc{{\d+_\d+}}, loaded [symbolic = @Int.as.Copy.impl.%Int.as.Copy.impl.Op (constants.%Int.as.Copy.impl.Op.9b9)] // CHECK:STDOUT: %Copy.impl_witness_table.e76 = impl_witness_table (%Core.import_ref.18d), @Int.as.Copy.impl [concrete] // CHECK:STDOUT: %Core.Destroy: type = import_ref Core//prelude/parts/destroy, Destroy, loaded [concrete = constants.%Destroy.type] // CHECK:STDOUT: %Core.UInt: %UInt.type = import_ref Core//prelude/parts/uint, UInt, loaded [concrete = constants.%UInt.generic] // CHECK:STDOUT: %Core.import_ref.c3c: @UInt.as.Copy.impl.%UInt.as.Copy.impl.Op.type (%UInt.as.Copy.impl.Op.type.68f) = import_ref Core//prelude/parts/uint, loc{{\d+_\d+}}, loaded [symbolic = @UInt.as.Copy.impl.%UInt.as.Copy.impl.Op (constants.%UInt.as.Copy.impl.Op.576)] // CHECK:STDOUT: %Copy.impl_witness_table.bd0 = impl_witness_table (%Core.import_ref.c3c), @UInt.as.Copy.impl [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .AdaptTuple = %AdaptTuple.decl // CHECK:STDOUT: .F = %F.decl // CHECK:STDOUT: .InTuple = %InTuple.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %AdaptTuple.decl: type = class_decl @AdaptTuple [concrete = constants.%AdaptTuple] {} {} // CHECK:STDOUT: %F.decl: %F.type = fn_decl @F [concrete = constants.%F] { // CHECK:STDOUT: %c.patt: %pattern_type.6cd = value_binding_pattern c [concrete] // CHECK:STDOUT: %c.param_patt: %pattern_type.6cd = value_param_pattern %c.patt [concrete] // CHECK:STDOUT: %return.patt: %pattern_type.6cd = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: %pattern_type.6cd = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %AdaptTuple.ref.loc8_24: type = name_ref AdaptTuple, file.%AdaptTuple.decl [concrete = constants.%AdaptTuple] // CHECK:STDOUT: %.loc8: Core.Form = init_form %AdaptTuple.ref.loc8_24 [concrete = constants.%.52a] // CHECK:STDOUT: %c.param: %AdaptTuple = value_param call_param0 // CHECK:STDOUT: %AdaptTuple.ref.loc8_9: type = name_ref AdaptTuple, file.%AdaptTuple.decl [concrete = constants.%AdaptTuple] // CHECK:STDOUT: %c: %AdaptTuple = value_binding c, %c.param // CHECK:STDOUT: %return.param: ref %AdaptTuple = out_param call_param1 // CHECK:STDOUT: %return: ref %AdaptTuple = return_slot %return.param // CHECK:STDOUT: } // CHECK:STDOUT: %InTuple.decl: %InTuple.type = fn_decl @InTuple [concrete = constants.%InTuple] { // CHECK:STDOUT: %c.patt: %pattern_type.6f4 = value_binding_pattern c [concrete] // CHECK:STDOUT: %c.param_patt: %pattern_type.6f4 = value_param_pattern %c.patt [concrete] // CHECK:STDOUT: %return.patt: %pattern_type.6f4 = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: %pattern_type.6f4 = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %AdaptTuple.ref.loc13_38: type = name_ref AdaptTuple, file.%AdaptTuple.decl [concrete = constants.%AdaptTuple] // CHECK:STDOUT: %u32.loc13_50: type = type_literal constants.%u32 [concrete = constants.%u32] // CHECK:STDOUT: %.loc13_53.1: %tuple.type.24b = tuple_literal (%AdaptTuple.ref.loc13_38, %u32.loc13_50) [concrete = constants.%tuple.b75] // CHECK:STDOUT: %.loc13_53.2: type = converted %.loc13_53.1, constants.%tuple.type.3c7 [concrete = constants.%tuple.type.3c7] // CHECK:STDOUT: %.loc13_53.3: Core.Form = init_form %.loc13_53.2 [concrete = constants.%.709] // CHECK:STDOUT: %c.param: %tuple.type.3c7 = value_param call_param0 // CHECK:STDOUT: %.loc13_31.1: type = splice_block %.loc13_31.3 [concrete = constants.%tuple.type.3c7] { // CHECK:STDOUT: %AdaptTuple.ref.loc13_16: type = name_ref AdaptTuple, file.%AdaptTuple.decl [concrete = constants.%AdaptTuple] // CHECK:STDOUT: %u32.loc13_28: type = type_literal constants.%u32 [concrete = constants.%u32] // CHECK:STDOUT: %.loc13_31.2: %tuple.type.24b = tuple_literal (%AdaptTuple.ref.loc13_16, %u32.loc13_28) [concrete = constants.%tuple.b75] // CHECK:STDOUT: %.loc13_31.3: type = converted %.loc13_31.2, constants.%tuple.type.3c7 [concrete = constants.%tuple.type.3c7] // CHECK:STDOUT: } // CHECK:STDOUT: %c: %tuple.type.3c7 = value_binding c, %c.param // CHECK:STDOUT: %return.param: ref %tuple.type.3c7 = out_param call_param1 // CHECK:STDOUT: %return: ref %tuple.type.3c7 = return_slot %return.param // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @AdaptTuple { // CHECK:STDOUT: %i32.loc5_10: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %i32.loc5_15: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %.loc5_18: %tuple.type.24b = tuple_literal (%i32.loc5_10, %i32.loc5_15) [concrete = constants.%tuple.95a] // CHECK:STDOUT: %.loc5_19: type = converted %.loc5_18, constants.%tuple.type.d07 [concrete = constants.%tuple.type.d07] // CHECK:STDOUT: adapt_decl %.loc5_19 [concrete] // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%tuple.type.d07 [concrete = constants.%complete_type.65d] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%AdaptTuple // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @F(%c.param: %AdaptTuple) -> out %return.param: %AdaptTuple { // CHECK:STDOUT: !entry: // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %d.patt: %pattern_type.6cd = ref_binding_pattern d [concrete] // CHECK:STDOUT: %d.var_patt: %pattern_type.6cd = var_pattern %d.patt [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %d.var: ref %AdaptTuple = var %d.var_patt // CHECK:STDOUT: %c.ref: %AdaptTuple = name_ref c, %c // CHECK:STDOUT: %.loc9_3.1: %tuple.type.d07 = as_compatible %c.ref // CHECK:STDOUT: %tuple.elem0.loc9_3.1: %i32 = tuple_access %.loc9_3.1, element0 // CHECK:STDOUT: %impl.elem0.loc9_3.1: %.8e2 = impl_witness_access constants.%Copy.impl_witness.f17, element0 [concrete = constants.%Int.as.Copy.impl.Op.664] // CHECK:STDOUT: %bound_method.loc9_3.1: = bound_method %tuple.elem0.loc9_3.1, %impl.elem0.loc9_3.1 // CHECK:STDOUT: %specific_fn.loc9_3.1: = specific_function %impl.elem0.loc9_3.1, @Int.as.Copy.impl.Op(constants.%int_32) [concrete = constants.%Int.as.Copy.impl.Op.specific_fn] // CHECK:STDOUT: %bound_method.loc9_3.2: = bound_method %tuple.elem0.loc9_3.1, %specific_fn.loc9_3.1 // CHECK:STDOUT: %Int.as.Copy.impl.Op.call.loc9_3.1: init %i32 = call %bound_method.loc9_3.2(%tuple.elem0.loc9_3.1) // CHECK:STDOUT: %.loc9_3.2: ref %tuple.type.d07 = as_compatible %d.var // CHECK:STDOUT: %tuple.elem0.loc9_3.2: ref %i32 = tuple_access %.loc9_3.2, element0 // CHECK:STDOUT: %.loc9_3.3: init %i32 to %tuple.elem0.loc9_3.2 = in_place_init %Int.as.Copy.impl.Op.call.loc9_3.1 // CHECK:STDOUT: %tuple.elem1.loc9_3.1: %i32 = tuple_access %.loc9_3.1, element1 // CHECK:STDOUT: %impl.elem0.loc9_3.2: %.8e2 = impl_witness_access constants.%Copy.impl_witness.f17, element0 [concrete = constants.%Int.as.Copy.impl.Op.664] // CHECK:STDOUT: %bound_method.loc9_3.3: = bound_method %tuple.elem1.loc9_3.1, %impl.elem0.loc9_3.2 // CHECK:STDOUT: %specific_fn.loc9_3.2: = specific_function %impl.elem0.loc9_3.2, @Int.as.Copy.impl.Op(constants.%int_32) [concrete = constants.%Int.as.Copy.impl.Op.specific_fn] // CHECK:STDOUT: %bound_method.loc9_3.4: = bound_method %tuple.elem1.loc9_3.1, %specific_fn.loc9_3.2 // CHECK:STDOUT: %Int.as.Copy.impl.Op.call.loc9_3.2: init %i32 = call %bound_method.loc9_3.4(%tuple.elem1.loc9_3.1) // CHECK:STDOUT: %tuple.elem1.loc9_3.2: ref %i32 = tuple_access %.loc9_3.2, element1 // CHECK:STDOUT: %.loc9_3.4: init %i32 to %tuple.elem1.loc9_3.2 = in_place_init %Int.as.Copy.impl.Op.call.loc9_3.2 // CHECK:STDOUT: %.loc9_3.5: init %tuple.type.d07 to %.loc9_3.2 = tuple_init (%.loc9_3.3, %.loc9_3.4) // CHECK:STDOUT: %.loc9_3.6: init %AdaptTuple = as_compatible %.loc9_3.5 // CHECK:STDOUT: %.loc9_3.7: init %AdaptTuple = converted %c.ref, %.loc9_3.6 // CHECK:STDOUT: assign %d.var, %.loc9_3.7 // CHECK:STDOUT: %AdaptTuple.ref.loc9: type = name_ref AdaptTuple, file.%AdaptTuple.decl [concrete = constants.%AdaptTuple] // CHECK:STDOUT: %d: ref %AdaptTuple = ref_binding d, %d.var // CHECK:STDOUT: %d.ref: ref %AdaptTuple = name_ref d, %d // CHECK:STDOUT: %.loc10_11.1: ref %tuple.type.d07 = as_compatible %d.ref // CHECK:STDOUT: %tuple.elem0.loc10_11.1: ref %i32 = tuple_access %.loc10_11.1, element0 // CHECK:STDOUT: %.loc10_11.2: %i32 = acquire_value %tuple.elem0.loc10_11.1 // CHECK:STDOUT: %impl.elem0.loc10_11.1: %.8e2 = impl_witness_access constants.%Copy.impl_witness.f17, element0 [concrete = constants.%Int.as.Copy.impl.Op.664] // CHECK:STDOUT: %bound_method.loc10_11.1: = bound_method %.loc10_11.2, %impl.elem0.loc10_11.1 // CHECK:STDOUT: %specific_fn.loc10_11.1: = specific_function %impl.elem0.loc10_11.1, @Int.as.Copy.impl.Op(constants.%int_32) [concrete = constants.%Int.as.Copy.impl.Op.specific_fn] // CHECK:STDOUT: %bound_method.loc10_11.2: = bound_method %.loc10_11.2, %specific_fn.loc10_11.1 // CHECK:STDOUT: %Int.as.Copy.impl.Op.call.loc10_11.1: init %i32 = call %bound_method.loc10_11.2(%.loc10_11.2) // CHECK:STDOUT: %.loc10_11.3: ref %tuple.type.d07 = as_compatible %return.param // CHECK:STDOUT: %tuple.elem0.loc10_11.2: ref %i32 = tuple_access %.loc10_11.3, element0 // CHECK:STDOUT: %.loc10_11.4: init %i32 to %tuple.elem0.loc10_11.2 = in_place_init %Int.as.Copy.impl.Op.call.loc10_11.1 // CHECK:STDOUT: %tuple.elem1.loc10_11.1: ref %i32 = tuple_access %.loc10_11.1, element1 // CHECK:STDOUT: %.loc10_11.5: %i32 = acquire_value %tuple.elem1.loc10_11.1 // CHECK:STDOUT: %impl.elem0.loc10_11.2: %.8e2 = impl_witness_access constants.%Copy.impl_witness.f17, element0 [concrete = constants.%Int.as.Copy.impl.Op.664] // CHECK:STDOUT: %bound_method.loc10_11.3: = bound_method %.loc10_11.5, %impl.elem0.loc10_11.2 // CHECK:STDOUT: %specific_fn.loc10_11.2: = specific_function %impl.elem0.loc10_11.2, @Int.as.Copy.impl.Op(constants.%int_32) [concrete = constants.%Int.as.Copy.impl.Op.specific_fn] // CHECK:STDOUT: %bound_method.loc10_11.4: = bound_method %.loc10_11.5, %specific_fn.loc10_11.2 // CHECK:STDOUT: %Int.as.Copy.impl.Op.call.loc10_11.2: init %i32 = call %bound_method.loc10_11.4(%.loc10_11.5) // CHECK:STDOUT: %tuple.elem1.loc10_11.2: ref %i32 = tuple_access %.loc10_11.3, element1 // CHECK:STDOUT: %.loc10_11.6: init %i32 to %tuple.elem1.loc10_11.2 = in_place_init %Int.as.Copy.impl.Op.call.loc10_11.2 // CHECK:STDOUT: %.loc10_11.7: init %tuple.type.d07 to %.loc10_11.3 = tuple_init (%.loc10_11.4, %.loc10_11.6) // CHECK:STDOUT: %.loc10_11.8: init %AdaptTuple = as_compatible %.loc10_11.7 // CHECK:STDOUT: %.loc10_11.9: init %AdaptTuple = converted %d.ref, %.loc10_11.8 // CHECK:STDOUT: %Destroy.Op.bound: = bound_method %d.var, constants.%Destroy.Op.651ba6.1 // CHECK:STDOUT: %Destroy.Op.call: init %empty_tuple.type = call %Destroy.Op.bound(%d.var) // CHECK:STDOUT: return %.loc10_11.9 to %return.param // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @Destroy.Op.loc9(%self.param: ref %AdaptTuple) = "no_op"; // CHECK:STDOUT: // CHECK:STDOUT: fn @InTuple(%c.param: %tuple.type.3c7) -> out %return.param: %tuple.type.3c7 { // CHECK:STDOUT: !entry: // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %d.patt: %pattern_type.6f4 = ref_binding_pattern d [concrete] // CHECK:STDOUT: %d.var_patt: %pattern_type.6f4 = var_pattern %d.patt [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %d.var: ref %tuple.type.3c7 = var %d.var_patt // CHECK:STDOUT: %c.ref: %tuple.type.3c7 = name_ref c, %c // CHECK:STDOUT: %tuple.elem0.loc14_30.1: %AdaptTuple = tuple_access %c.ref, element0 // CHECK:STDOUT: %.loc14_30.1: %tuple.type.d07 = as_compatible %tuple.elem0.loc14_30.1 // CHECK:STDOUT: %tuple.elem0.loc14_30.2: %i32 = tuple_access %.loc14_30.1, element0 // CHECK:STDOUT: %impl.elem0.loc14_30.1: %.8e2 = impl_witness_access constants.%Copy.impl_witness.f17, element0 [concrete = constants.%Int.as.Copy.impl.Op.664] // CHECK:STDOUT: %bound_method.loc14_30.1: = bound_method %tuple.elem0.loc14_30.2, %impl.elem0.loc14_30.1 // CHECK:STDOUT: %specific_fn.loc14_30.1: = specific_function %impl.elem0.loc14_30.1, @Int.as.Copy.impl.Op(constants.%int_32) [concrete = constants.%Int.as.Copy.impl.Op.specific_fn] // CHECK:STDOUT: %bound_method.loc14_30.2: = bound_method %tuple.elem0.loc14_30.2, %specific_fn.loc14_30.1 // CHECK:STDOUT: %Int.as.Copy.impl.Op.call.loc14_30.1: init %i32 = call %bound_method.loc14_30.2(%tuple.elem0.loc14_30.2) // CHECK:STDOUT: %tuple.elem0.loc14_30.3: ref %AdaptTuple = tuple_access %d.var, element0 // CHECK:STDOUT: %.loc14_30.2: ref %tuple.type.d07 = as_compatible %tuple.elem0.loc14_30.3 // CHECK:STDOUT: %tuple.elem0.loc14_30.4: ref %i32 = tuple_access %.loc14_30.2, element0 // CHECK:STDOUT: %.loc14_30.3: init %i32 to %tuple.elem0.loc14_30.4 = in_place_init %Int.as.Copy.impl.Op.call.loc14_30.1 // CHECK:STDOUT: %tuple.elem1.loc14_30.1: %i32 = tuple_access %.loc14_30.1, element1 // CHECK:STDOUT: %impl.elem0.loc14_30.2: %.8e2 = impl_witness_access constants.%Copy.impl_witness.f17, element0 [concrete = constants.%Int.as.Copy.impl.Op.664] // CHECK:STDOUT: %bound_method.loc14_30.3: = bound_method %tuple.elem1.loc14_30.1, %impl.elem0.loc14_30.2 // CHECK:STDOUT: %specific_fn.loc14_30.2: = specific_function %impl.elem0.loc14_30.2, @Int.as.Copy.impl.Op(constants.%int_32) [concrete = constants.%Int.as.Copy.impl.Op.specific_fn] // CHECK:STDOUT: %bound_method.loc14_30.4: = bound_method %tuple.elem1.loc14_30.1, %specific_fn.loc14_30.2 // CHECK:STDOUT: %Int.as.Copy.impl.Op.call.loc14_30.2: init %i32 = call %bound_method.loc14_30.4(%tuple.elem1.loc14_30.1) // CHECK:STDOUT: %tuple.elem1.loc14_30.2: ref %i32 = tuple_access %.loc14_30.2, element1 // CHECK:STDOUT: %.loc14_30.4: init %i32 to %tuple.elem1.loc14_30.2 = in_place_init %Int.as.Copy.impl.Op.call.loc14_30.2 // CHECK:STDOUT: %.loc14_30.5: init %tuple.type.d07 to %.loc14_30.2 = tuple_init (%.loc14_30.3, %.loc14_30.4) // CHECK:STDOUT: %.loc14_30.6: init %AdaptTuple = as_compatible %.loc14_30.5 // CHECK:STDOUT: %.loc14_30.7: init %AdaptTuple = converted %tuple.elem0.loc14_30.1, %.loc14_30.6 // CHECK:STDOUT: %tuple.elem1.loc14_30.3: %u32 = tuple_access %c.ref, element1 // CHECK:STDOUT: %impl.elem0.loc14_30.3: %.38c = impl_witness_access constants.%Copy.impl_witness.514, element0 [concrete = constants.%UInt.as.Copy.impl.Op.c10] // CHECK:STDOUT: %bound_method.loc14_30.5: = bound_method %tuple.elem1.loc14_30.3, %impl.elem0.loc14_30.3 // CHECK:STDOUT: %specific_fn.loc14_30.3: = specific_function %impl.elem0.loc14_30.3, @UInt.as.Copy.impl.Op(constants.%int_32) [concrete = constants.%UInt.as.Copy.impl.Op.specific_fn] // CHECK:STDOUT: %bound_method.loc14_30.6: = bound_method %tuple.elem1.loc14_30.3, %specific_fn.loc14_30.3 // CHECK:STDOUT: %UInt.as.Copy.impl.Op.call.loc14: init %u32 = call %bound_method.loc14_30.6(%tuple.elem1.loc14_30.3) // CHECK:STDOUT: %tuple.elem1.loc14_30.4: ref %u32 = tuple_access %d.var, element1 // CHECK:STDOUT: %.loc14_30.8: init %u32 to %tuple.elem1.loc14_30.4 = in_place_init %UInt.as.Copy.impl.Op.call.loc14 // CHECK:STDOUT: %.loc14_30.9: init %tuple.type.3c7 to %d.var = tuple_init (%.loc14_30.7, %.loc14_30.8) // CHECK:STDOUT: %.loc14_3: init %tuple.type.3c7 = converted %c.ref, %.loc14_30.9 // CHECK:STDOUT: assign %d.var, %.loc14_3 // CHECK:STDOUT: %.loc14_26.1: type = splice_block %.loc14_26.3 [concrete = constants.%tuple.type.3c7] { // CHECK:STDOUT: %AdaptTuple.ref.loc14: type = name_ref AdaptTuple, file.%AdaptTuple.decl [concrete = constants.%AdaptTuple] // CHECK:STDOUT: %u32.loc14: type = type_literal constants.%u32 [concrete = constants.%u32] // CHECK:STDOUT: %.loc14_26.2: %tuple.type.24b = tuple_literal (%AdaptTuple.ref.loc14, %u32.loc14) [concrete = constants.%tuple.b75] // CHECK:STDOUT: %.loc14_26.3: type = converted %.loc14_26.2, constants.%tuple.type.3c7 [concrete = constants.%tuple.type.3c7] // CHECK:STDOUT: } // CHECK:STDOUT: %d: ref %tuple.type.3c7 = ref_binding d, %d.var // CHECK:STDOUT: %d.ref: ref %tuple.type.3c7 = name_ref d, %d // CHECK:STDOUT: %tuple.elem0.loc15_10.1: ref %AdaptTuple = tuple_access %d.ref, element0 // CHECK:STDOUT: %.loc15_10.1: ref %tuple.type.d07 = as_compatible %tuple.elem0.loc15_10.1 // CHECK:STDOUT: %tuple.elem0.loc15_10.2: ref %i32 = tuple_access %.loc15_10.1, element0 // CHECK:STDOUT: %.loc15_10.2: %i32 = acquire_value %tuple.elem0.loc15_10.2 // CHECK:STDOUT: %impl.elem0.loc15_10.1: %.8e2 = impl_witness_access constants.%Copy.impl_witness.f17, element0 [concrete = constants.%Int.as.Copy.impl.Op.664] // CHECK:STDOUT: %bound_method.loc15_10.1: = bound_method %.loc15_10.2, %impl.elem0.loc15_10.1 // CHECK:STDOUT: %specific_fn.loc15_10.1: = specific_function %impl.elem0.loc15_10.1, @Int.as.Copy.impl.Op(constants.%int_32) [concrete = constants.%Int.as.Copy.impl.Op.specific_fn] // CHECK:STDOUT: %bound_method.loc15_10.2: = bound_method %.loc15_10.2, %specific_fn.loc15_10.1 // CHECK:STDOUT: %Int.as.Copy.impl.Op.call.loc15_10.1: init %i32 = call %bound_method.loc15_10.2(%.loc15_10.2) // CHECK:STDOUT: %tuple.elem0.loc15_10.3: ref %AdaptTuple = tuple_access %return.param, element0 // CHECK:STDOUT: %.loc15_10.3: ref %tuple.type.d07 = as_compatible %tuple.elem0.loc15_10.3 // CHECK:STDOUT: %tuple.elem0.loc15_10.4: ref %i32 = tuple_access %.loc15_10.3, element0 // CHECK:STDOUT: %.loc15_10.4: init %i32 to %tuple.elem0.loc15_10.4 = in_place_init %Int.as.Copy.impl.Op.call.loc15_10.1 // CHECK:STDOUT: %tuple.elem1.loc15_10.1: ref %i32 = tuple_access %.loc15_10.1, element1 // CHECK:STDOUT: %.loc15_10.5: %i32 = acquire_value %tuple.elem1.loc15_10.1 // CHECK:STDOUT: %impl.elem0.loc15_10.2: %.8e2 = impl_witness_access constants.%Copy.impl_witness.f17, element0 [concrete = constants.%Int.as.Copy.impl.Op.664] // CHECK:STDOUT: %bound_method.loc15_10.3: = bound_method %.loc15_10.5, %impl.elem0.loc15_10.2 // CHECK:STDOUT: %specific_fn.loc15_10.2: = specific_function %impl.elem0.loc15_10.2, @Int.as.Copy.impl.Op(constants.%int_32) [concrete = constants.%Int.as.Copy.impl.Op.specific_fn] // CHECK:STDOUT: %bound_method.loc15_10.4: = bound_method %.loc15_10.5, %specific_fn.loc15_10.2 // CHECK:STDOUT: %Int.as.Copy.impl.Op.call.loc15_10.2: init %i32 = call %bound_method.loc15_10.4(%.loc15_10.5) // CHECK:STDOUT: %tuple.elem1.loc15_10.2: ref %i32 = tuple_access %.loc15_10.3, element1 // CHECK:STDOUT: %.loc15_10.6: init %i32 to %tuple.elem1.loc15_10.2 = in_place_init %Int.as.Copy.impl.Op.call.loc15_10.2 // CHECK:STDOUT: %.loc15_10.7: init %tuple.type.d07 to %.loc15_10.3 = tuple_init (%.loc15_10.4, %.loc15_10.6) // CHECK:STDOUT: %.loc15_10.8: init %AdaptTuple = as_compatible %.loc15_10.7 // CHECK:STDOUT: %.loc15_10.9: init %AdaptTuple = converted %tuple.elem0.loc15_10.1, %.loc15_10.8 // CHECK:STDOUT: %tuple.elem1.loc15_10.3: ref %u32 = tuple_access %d.ref, element1 // CHECK:STDOUT: %.loc15_10.10: %u32 = acquire_value %tuple.elem1.loc15_10.3 // CHECK:STDOUT: %impl.elem0.loc15_10.3: %.38c = impl_witness_access constants.%Copy.impl_witness.514, element0 [concrete = constants.%UInt.as.Copy.impl.Op.c10] // CHECK:STDOUT: %bound_method.loc15_10.5: = bound_method %.loc15_10.10, %impl.elem0.loc15_10.3 // CHECK:STDOUT: %specific_fn.loc15_10.3: = specific_function %impl.elem0.loc15_10.3, @UInt.as.Copy.impl.Op(constants.%int_32) [concrete = constants.%UInt.as.Copy.impl.Op.specific_fn] // CHECK:STDOUT: %bound_method.loc15_10.6: = bound_method %.loc15_10.10, %specific_fn.loc15_10.3 // CHECK:STDOUT: %UInt.as.Copy.impl.Op.call.loc15: init %u32 = call %bound_method.loc15_10.6(%.loc15_10.10) // CHECK:STDOUT: %tuple.elem1.loc15_10.4: ref %u32 = tuple_access %return.param, element1 // CHECK:STDOUT: %.loc15_10.11: init %u32 to %tuple.elem1.loc15_10.4 = in_place_init %UInt.as.Copy.impl.Op.call.loc15 // CHECK:STDOUT: %.loc15_10.12: init %tuple.type.3c7 to %return.param = tuple_init (%.loc15_10.9, %.loc15_10.11) // CHECK:STDOUT: %.loc15_11: init %tuple.type.3c7 = converted %d.ref, %.loc15_10.12 // CHECK:STDOUT: %Destroy.Op.bound: = bound_method %d.var, constants.%Destroy.Op.651ba6.2 // CHECK:STDOUT: %Destroy.Op.call: init %empty_tuple.type = call %Destroy.Op.bound(%d.var) // CHECK:STDOUT: return %.loc15_11 to %return.param // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @Destroy.Op.loc14(%self.param: ref %tuple.type.3c7) = "no_op"; // CHECK:STDOUT: // CHECK:STDOUT: --- fail_adapt_not_copyable.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %Noncopyable: type = class_type @Noncopyable [concrete] // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %complete_type: = complete_type_witness %empty_struct_type [concrete] // CHECK:STDOUT: %AdaptNoncopyable: type = class_type @AdaptNoncopyable [concrete] // CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] // CHECK:STDOUT: %pattern_type.92b: type = pattern_type %AdaptNoncopyable [concrete] // CHECK:STDOUT: %.e94: Core.Form = init_form %AdaptNoncopyable [concrete] // CHECK:STDOUT: %G.type: type = fn_type @G [concrete] // CHECK:STDOUT: %G: %G.type = struct_value () [concrete] // CHECK:STDOUT: %Copy.type: type = facet_type <@Copy> [concrete] // CHECK:STDOUT: %Destroy.type: type = facet_type <@Destroy> [concrete] // CHECK:STDOUT: %Destroy.Op.type: type = fn_type @Destroy.Op [concrete] // CHECK:STDOUT: %Destroy.Op: %Destroy.Op.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: .Copy = %Core.Copy // CHECK:STDOUT: .Destroy = %Core.Destroy // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: %Core.Copy: type = import_ref Core//prelude/parts/copy, Copy, loaded [concrete = constants.%Copy.type] // CHECK:STDOUT: %Core.Destroy: type = import_ref Core//prelude/parts/destroy, Destroy, loaded [concrete = constants.%Destroy.type] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .Noncopyable = %Noncopyable.decl // CHECK:STDOUT: .AdaptNoncopyable = %AdaptNoncopyable.decl // CHECK:STDOUT: .G = %G.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %Noncopyable.decl: type = class_decl @Noncopyable [concrete = constants.%Noncopyable] {} {} // CHECK:STDOUT: %AdaptNoncopyable.decl: type = class_decl @AdaptNoncopyable [concrete = constants.%AdaptNoncopyable] {} {} // CHECK:STDOUT: %G.decl: %G.type = fn_decl @G [concrete = constants.%G] { // CHECK:STDOUT: %a.patt: %pattern_type.92b = value_binding_pattern a [concrete] // CHECK:STDOUT: %a.param_patt: %pattern_type.92b = value_param_pattern %a.patt [concrete] // CHECK:STDOUT: %return.patt: %pattern_type.92b = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: %pattern_type.92b = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %AdaptNoncopyable.ref.loc12_30: type = name_ref AdaptNoncopyable, file.%AdaptNoncopyable.decl [concrete = constants.%AdaptNoncopyable] // CHECK:STDOUT: %.loc12: Core.Form = init_form %AdaptNoncopyable.ref.loc12_30 [concrete = constants.%.e94] // CHECK:STDOUT: %a.param: %AdaptNoncopyable = value_param call_param0 // CHECK:STDOUT: %AdaptNoncopyable.ref.loc12_9: type = name_ref AdaptNoncopyable, file.%AdaptNoncopyable.decl [concrete = constants.%AdaptNoncopyable] // CHECK:STDOUT: %a: %AdaptNoncopyable = value_binding a, %a.param // CHECK:STDOUT: %return.param: ref %AdaptNoncopyable = out_param call_param1 // CHECK:STDOUT: %return: ref %AdaptNoncopyable = return_slot %return.param // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @Noncopyable { // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%Noncopyable // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @AdaptNoncopyable { // CHECK:STDOUT: %Noncopyable.ref: type = name_ref Noncopyable, file.%Noncopyable.decl [concrete = constants.%Noncopyable] // CHECK:STDOUT: adapt_decl %Noncopyable.ref [concrete] // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%AdaptNoncopyable // CHECK:STDOUT: .Noncopyable = // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @G(%a.param: %AdaptNoncopyable) -> out %return.param: %AdaptNoncopyable { // CHECK:STDOUT: !entry: // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %b.patt: %pattern_type.92b = ref_binding_pattern b [concrete] // CHECK:STDOUT: %b.var_patt: %pattern_type.92b = var_pattern %b.patt [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %b.var: ref %AdaptNoncopyable = var %b.var_patt // CHECK:STDOUT: %a.ref: %AdaptNoncopyable = name_ref a, %a // CHECK:STDOUT: assign %b.var, // CHECK:STDOUT: %AdaptNoncopyable.ref.loc20: type = name_ref AdaptNoncopyable, file.%AdaptNoncopyable.decl [concrete = constants.%AdaptNoncopyable] // CHECK:STDOUT: %b: ref %AdaptNoncopyable = ref_binding b, %b.var // CHECK:STDOUT: %b.ref: ref %AdaptNoncopyable = name_ref b, %b // CHECK:STDOUT: %.loc28: %AdaptNoncopyable = acquire_value %b.ref // CHECK:STDOUT: %Destroy.Op.bound: = bound_method %b.var, constants.%Destroy.Op // CHECK:STDOUT: %Destroy.Op.call: init %empty_tuple.type = call %Destroy.Op.bound(%b.var) // CHECK:STDOUT: return to %return.param // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @Destroy.Op(%self.param: ref %AdaptNoncopyable) = "no_op"; // CHECK:STDOUT: // CHECK:STDOUT: --- fail_adapt_not_copyable_indirect.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %Noncopyable: type = class_type @Noncopyable [concrete] // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %complete_type.357: = complete_type_witness %empty_struct_type [concrete] // CHECK:STDOUT: %AdaptNoncopyableIndirect: type = class_type @AdaptNoncopyableIndirect [concrete] // CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [concrete] // CHECK:STDOUT: %Int.type: type = generic_class_type @Int [concrete] // CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] // CHECK:STDOUT: %Int.generic: %Int.type = struct_value () [concrete] // CHECK:STDOUT: %N: Core.IntLiteral = symbolic_binding N, 0 [symbolic] // CHECK:STDOUT: %i32: type = class_type @Int, @Int(%int_32) [concrete] // CHECK:STDOUT: %tuple.type.ff9: type = tuple_type (type, type, type) [concrete] // CHECK:STDOUT: %tuple.19a: %tuple.type.ff9 = tuple_value (%i32, %Noncopyable, %i32) [concrete] // CHECK:STDOUT: %tuple.type.7f9: type = tuple_type (%i32, %Noncopyable, %i32) [concrete] // CHECK:STDOUT: %complete_type.381: = complete_type_witness %tuple.type.7f9 [concrete] // CHECK:STDOUT: %pattern_type.ca6: type = pattern_type %AdaptNoncopyableIndirect [concrete] // CHECK:STDOUT: %.ae4: Core.Form = init_form %AdaptNoncopyableIndirect [concrete] // CHECK:STDOUT: %H.type: type = fn_type @H [concrete] // CHECK:STDOUT: %H: %H.type = struct_value () [concrete] // CHECK:STDOUT: %Copy.type: type = facet_type <@Copy> [concrete] // CHECK:STDOUT: %Int.as.Copy.impl.Op.type.824: type = fn_type @Int.as.Copy.impl.Op, @Int.as.Copy.impl(%N) [symbolic] // CHECK:STDOUT: %Int.as.Copy.impl.Op.9b9: %Int.as.Copy.impl.Op.type.824 = struct_value () [symbolic] // CHECK:STDOUT: %Copy.impl_witness.f17: = impl_witness imports.%Copy.impl_witness_table.e76, @Int.as.Copy.impl(%int_32) [concrete] // CHECK:STDOUT: %Int.as.Copy.impl.Op.type.546: type = fn_type @Int.as.Copy.impl.Op, @Int.as.Copy.impl(%int_32) [concrete] // CHECK:STDOUT: %Int.as.Copy.impl.Op.664: %Int.as.Copy.impl.Op.type.546 = struct_value () [concrete] // CHECK:STDOUT: %Copy.facet: %Copy.type = facet_value %i32, (%Copy.impl_witness.f17) [concrete] // CHECK:STDOUT: %Copy.WithSelf.Op.type.081: type = fn_type @Copy.WithSelf.Op, @Copy.WithSelf(%Copy.facet) [concrete] // CHECK:STDOUT: %.8e2: type = fn_type_with_self_type %Copy.WithSelf.Op.type.081, %Copy.facet [concrete] // CHECK:STDOUT: %Int.as.Copy.impl.Op.specific_fn: = specific_function %Int.as.Copy.impl.Op.664, @Int.as.Copy.impl.Op(%int_32) [concrete] // CHECK:STDOUT: %Destroy.type: type = facet_type <@Destroy> [concrete] // CHECK:STDOUT: %Destroy.Op.type: type = fn_type @Destroy.Op [concrete] // CHECK:STDOUT: %Destroy.Op: %Destroy.Op.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: .Int = %Core.Int // CHECK:STDOUT: .Copy = %Core.Copy // CHECK:STDOUT: .Destroy = %Core.Destroy // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: %Core.Int: %Int.type = import_ref Core//prelude/parts/int, Int, loaded [concrete = constants.%Int.generic] // CHECK:STDOUT: %Core.Copy: type = import_ref Core//prelude/parts/copy, Copy, loaded [concrete = constants.%Copy.type] // CHECK:STDOUT: %Core.import_ref.18d: @Int.as.Copy.impl.%Int.as.Copy.impl.Op.type (%Int.as.Copy.impl.Op.type.824) = import_ref Core//prelude/parts/int, loc{{\d+_\d+}}, loaded [symbolic = @Int.as.Copy.impl.%Int.as.Copy.impl.Op (constants.%Int.as.Copy.impl.Op.9b9)] // CHECK:STDOUT: %Copy.impl_witness_table.e76 = impl_witness_table (%Core.import_ref.18d), @Int.as.Copy.impl [concrete] // CHECK:STDOUT: %Core.Destroy: type = import_ref Core//prelude/parts/destroy, Destroy, loaded [concrete = constants.%Destroy.type] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .Noncopyable = %Noncopyable.decl // CHECK:STDOUT: .AdaptNoncopyableIndirect = %AdaptNoncopyableIndirect.decl // CHECK:STDOUT: .H = %H.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %Noncopyable.decl: type = class_decl @Noncopyable [concrete = constants.%Noncopyable] {} {} // CHECK:STDOUT: %AdaptNoncopyableIndirect.decl: type = class_decl @AdaptNoncopyableIndirect [concrete = constants.%AdaptNoncopyableIndirect] {} {} // CHECK:STDOUT: %H.decl: %H.type = fn_decl @H [concrete = constants.%H] { // CHECK:STDOUT: %a.patt: %pattern_type.ca6 = value_binding_pattern a [concrete] // CHECK:STDOUT: %a.param_patt: %pattern_type.ca6 = value_param_pattern %a.patt [concrete] // CHECK:STDOUT: %return.patt: %pattern_type.ca6 = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: %pattern_type.ca6 = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %AdaptNoncopyableIndirect.ref.loc12_38: type = name_ref AdaptNoncopyableIndirect, file.%AdaptNoncopyableIndirect.decl [concrete = constants.%AdaptNoncopyableIndirect] // CHECK:STDOUT: %.loc12: Core.Form = init_form %AdaptNoncopyableIndirect.ref.loc12_38 [concrete = constants.%.ae4] // CHECK:STDOUT: %a.param: %AdaptNoncopyableIndirect = value_param call_param0 // CHECK:STDOUT: %AdaptNoncopyableIndirect.ref.loc12_9: type = name_ref AdaptNoncopyableIndirect, file.%AdaptNoncopyableIndirect.decl [concrete = constants.%AdaptNoncopyableIndirect] // CHECK:STDOUT: %a: %AdaptNoncopyableIndirect = value_binding a, %a.param // CHECK:STDOUT: %return.param: ref %AdaptNoncopyableIndirect = out_param call_param1 // CHECK:STDOUT: %return: ref %AdaptNoncopyableIndirect = return_slot %return.param // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @Noncopyable { // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type.357] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%Noncopyable // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @AdaptNoncopyableIndirect { // CHECK:STDOUT: %i32.loc9_10: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %Noncopyable.ref: type = name_ref Noncopyable, file.%Noncopyable.decl [concrete = constants.%Noncopyable] // CHECK:STDOUT: %i32.loc9_28: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %.loc9_31: %tuple.type.ff9 = tuple_literal (%i32.loc9_10, %Noncopyable.ref, %i32.loc9_28) [concrete = constants.%tuple.19a] // CHECK:STDOUT: %.loc9_32: type = converted %.loc9_31, constants.%tuple.type.7f9 [concrete = constants.%tuple.type.7f9] // CHECK:STDOUT: adapt_decl %.loc9_32 [concrete] // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%tuple.type.7f9 [concrete = constants.%complete_type.381] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%AdaptNoncopyableIndirect // CHECK:STDOUT: .Noncopyable = // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @H(%a.param: %AdaptNoncopyableIndirect) -> out %return.param: %AdaptNoncopyableIndirect { // CHECK:STDOUT: !entry: // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %b.patt: %pattern_type.ca6 = ref_binding_pattern b [concrete] // CHECK:STDOUT: %b.var_patt: %pattern_type.ca6 = var_pattern %b.patt [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %b.var: ref %AdaptNoncopyableIndirect = var %b.var_patt // CHECK:STDOUT: %a.ref: %AdaptNoncopyableIndirect = name_ref a, %a // CHECK:STDOUT: %.loc23_3.1: %tuple.type.7f9 = as_compatible %a.ref // CHECK:STDOUT: %tuple.elem0.loc23_3.1: %i32 = tuple_access %.loc23_3.1, element0 // CHECK:STDOUT: %impl.elem0.loc23: %.8e2 = impl_witness_access constants.%Copy.impl_witness.f17, element0 [concrete = constants.%Int.as.Copy.impl.Op.664] // CHECK:STDOUT: %bound_method.loc23_3.1: = bound_method %tuple.elem0.loc23_3.1, %impl.elem0.loc23 // CHECK:STDOUT: %specific_fn.loc23: = specific_function %impl.elem0.loc23, @Int.as.Copy.impl.Op(constants.%int_32) [concrete = constants.%Int.as.Copy.impl.Op.specific_fn] // CHECK:STDOUT: %bound_method.loc23_3.2: = bound_method %tuple.elem0.loc23_3.1, %specific_fn.loc23 // CHECK:STDOUT: %Int.as.Copy.impl.Op.call.loc23: init %i32 = call %bound_method.loc23_3.2(%tuple.elem0.loc23_3.1) // CHECK:STDOUT: %.loc23_3.2: ref %tuple.type.7f9 = as_compatible %b.var // CHECK:STDOUT: %tuple.elem0.loc23_3.2: ref %i32 = tuple_access %.loc23_3.2, element0 // CHECK:STDOUT: %.loc23_3.3: init %i32 to %tuple.elem0.loc23_3.2 = in_place_init %Int.as.Copy.impl.Op.call.loc23 // CHECK:STDOUT: %tuple.elem1.loc23: %Noncopyable = tuple_access %.loc23_3.1, element1 // CHECK:STDOUT: assign %b.var, // CHECK:STDOUT: %AdaptNoncopyableIndirect.ref.loc23: type = name_ref AdaptNoncopyableIndirect, file.%AdaptNoncopyableIndirect.decl [concrete = constants.%AdaptNoncopyableIndirect] // CHECK:STDOUT: %b: ref %AdaptNoncopyableIndirect = ref_binding b, %b.var // CHECK:STDOUT: %b.ref: ref %AdaptNoncopyableIndirect = name_ref b, %b // CHECK:STDOUT: %.loc34_11.1: ref %tuple.type.7f9 = as_compatible %b.ref // CHECK:STDOUT: %tuple.elem0.loc34_11.1: ref %i32 = tuple_access %.loc34_11.1, element0 // CHECK:STDOUT: %.loc34_11.2: %i32 = acquire_value %tuple.elem0.loc34_11.1 // CHECK:STDOUT: %impl.elem0.loc34: %.8e2 = impl_witness_access constants.%Copy.impl_witness.f17, element0 [concrete = constants.%Int.as.Copy.impl.Op.664] // CHECK:STDOUT: %bound_method.loc34_11.1: = bound_method %.loc34_11.2, %impl.elem0.loc34 // CHECK:STDOUT: %specific_fn.loc34: = specific_function %impl.elem0.loc34, @Int.as.Copy.impl.Op(constants.%int_32) [concrete = constants.%Int.as.Copy.impl.Op.specific_fn] // CHECK:STDOUT: %bound_method.loc34_11.2: = bound_method %.loc34_11.2, %specific_fn.loc34 // CHECK:STDOUT: %Int.as.Copy.impl.Op.call.loc34: init %i32 = call %bound_method.loc34_11.2(%.loc34_11.2) // CHECK:STDOUT: %.loc34_11.3: ref %tuple.type.7f9 = as_compatible %return.param // CHECK:STDOUT: %tuple.elem0.loc34_11.2: ref %i32 = tuple_access %.loc34_11.3, element0 // CHECK:STDOUT: %.loc34_11.4: init %i32 to %tuple.elem0.loc34_11.2 = in_place_init %Int.as.Copy.impl.Op.call.loc34 // CHECK:STDOUT: %tuple.elem1.loc34: ref %Noncopyable = tuple_access %.loc34_11.1, element1 // CHECK:STDOUT: %.loc34_11.5: %Noncopyable = acquire_value %tuple.elem1.loc34 // CHECK:STDOUT: %Destroy.Op.bound: = bound_method %b.var, constants.%Destroy.Op // CHECK:STDOUT: %Destroy.Op.call: init %empty_tuple.type = call %Destroy.Op.bound(%b.var) // CHECK:STDOUT: return to %return.param // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @Destroy.Op(%self.param: ref %AdaptNoncopyableIndirect) = "no_op"; // CHECK:STDOUT: // CHECK:STDOUT: --- adapt_copyable_struct.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %AdaptStruct: type = class_type @AdaptStruct [concrete] // CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [concrete] // CHECK:STDOUT: %Int.type: type = generic_class_type @Int [concrete] // CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] // CHECK:STDOUT: %Int.generic: %Int.type = struct_value () [concrete] // CHECK:STDOUT: %N: Core.IntLiteral = symbolic_binding N, 0 [symbolic] // CHECK:STDOUT: %i32: type = class_type @Int, @Int(%int_32) [concrete] // CHECK:STDOUT: %struct_type.e.f: type = struct_type {.e: %i32, .f: %i32} [concrete] // CHECK:STDOUT: %complete_type.511: = complete_type_witness %struct_type.e.f [concrete] // CHECK:STDOUT: %pattern_type.341: type = pattern_type %AdaptStruct [concrete] // CHECK:STDOUT: %.8e3: Core.Form = init_form %AdaptStruct [concrete] // CHECK:STDOUT: %I.type: type = fn_type @I [concrete] // CHECK:STDOUT: %I: %I.type = struct_value () [concrete] // CHECK:STDOUT: %Copy.type: type = facet_type <@Copy> [concrete] // CHECK:STDOUT: %Int.as.Copy.impl.Op.type.824: type = fn_type @Int.as.Copy.impl.Op, @Int.as.Copy.impl(%N) [symbolic] // CHECK:STDOUT: %Int.as.Copy.impl.Op.9b9: %Int.as.Copy.impl.Op.type.824 = struct_value () [symbolic] // CHECK:STDOUT: %Copy.impl_witness.f17: = impl_witness imports.%Copy.impl_witness_table.e76, @Int.as.Copy.impl(%int_32) [concrete] // CHECK:STDOUT: %Int.as.Copy.impl.Op.type.546: type = fn_type @Int.as.Copy.impl.Op, @Int.as.Copy.impl(%int_32) [concrete] // CHECK:STDOUT: %Int.as.Copy.impl.Op.664: %Int.as.Copy.impl.Op.type.546 = struct_value () [concrete] // CHECK:STDOUT: %Copy.facet.de4: %Copy.type = facet_value %i32, (%Copy.impl_witness.f17) [concrete] // CHECK:STDOUT: %Copy.WithSelf.Op.type.081: type = fn_type @Copy.WithSelf.Op, @Copy.WithSelf(%Copy.facet.de4) [concrete] // CHECK:STDOUT: %.8e2: type = fn_type_with_self_type %Copy.WithSelf.Op.type.081, %Copy.facet.de4 [concrete] // CHECK:STDOUT: %Int.as.Copy.impl.Op.specific_fn: = specific_function %Int.as.Copy.impl.Op.664, @Int.as.Copy.impl.Op(%int_32) [concrete] // CHECK:STDOUT: %Destroy.type: type = facet_type <@Destroy> [concrete] // CHECK:STDOUT: %Destroy.Op.type.bae255.1: type = fn_type @Destroy.Op.loc9 [concrete] // CHECK:STDOUT: %Destroy.Op.651ba6.1: %Destroy.Op.type.bae255.1 = struct_value () [concrete] // CHECK:STDOUT: %UInt.type: type = generic_class_type @UInt [concrete] // CHECK:STDOUT: %UInt.generic: %UInt.type = struct_value () [concrete] // CHECK:STDOUT: %u32: type = class_type @UInt, @UInt(%int_32) [concrete] // CHECK:STDOUT: %tuple.type.24b: type = tuple_type (type, type) [concrete] // CHECK:STDOUT: %tuple.556: %tuple.type.24b = tuple_value (%AdaptStruct, %u32) [concrete] // CHECK:STDOUT: %tuple.type.691: type = tuple_type (%AdaptStruct, %u32) [concrete] // CHECK:STDOUT: %pattern_type.b13: type = pattern_type %tuple.type.691 [concrete] // CHECK:STDOUT: %.174: Core.Form = init_form %tuple.type.691 [concrete] // CHECK:STDOUT: %InTuple.type: type = fn_type @InTuple [concrete] // CHECK:STDOUT: %InTuple: %InTuple.type = struct_value () [concrete] // CHECK:STDOUT: %UInt.as.Copy.impl.Op.type.68f: type = fn_type @UInt.as.Copy.impl.Op, @UInt.as.Copy.impl(%N) [symbolic] // CHECK:STDOUT: %UInt.as.Copy.impl.Op.576: %UInt.as.Copy.impl.Op.type.68f = struct_value () [symbolic] // CHECK:STDOUT: %Copy.impl_witness.514: = impl_witness imports.%Copy.impl_witness_table.bd0, @UInt.as.Copy.impl(%int_32) [concrete] // CHECK:STDOUT: %UInt.as.Copy.impl.Op.type.2fc: type = fn_type @UInt.as.Copy.impl.Op, @UInt.as.Copy.impl(%int_32) [concrete] // CHECK:STDOUT: %UInt.as.Copy.impl.Op.c10: %UInt.as.Copy.impl.Op.type.2fc = struct_value () [concrete] // CHECK:STDOUT: %Copy.facet.10b: %Copy.type = facet_value %u32, (%Copy.impl_witness.514) [concrete] // CHECK:STDOUT: %Copy.WithSelf.Op.type.ad7: type = fn_type @Copy.WithSelf.Op, @Copy.WithSelf(%Copy.facet.10b) [concrete] // CHECK:STDOUT: %.38c: type = fn_type_with_self_type %Copy.WithSelf.Op.type.ad7, %Copy.facet.10b [concrete] // CHECK:STDOUT: %UInt.as.Copy.impl.Op.specific_fn: = specific_function %UInt.as.Copy.impl.Op.c10, @UInt.as.Copy.impl.Op(%int_32) [concrete] // CHECK:STDOUT: %Destroy.Op.type.bae255.2: type = fn_type @Destroy.Op.loc14 [concrete] // CHECK:STDOUT: %Destroy.Op.651ba6.2: %Destroy.Op.type.bae255.2 = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: .Int = %Core.Int // CHECK:STDOUT: .Copy = %Core.Copy // CHECK:STDOUT: .Destroy = %Core.Destroy // CHECK:STDOUT: .UInt = %Core.UInt // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: %Core.Int: %Int.type = import_ref Core//prelude/parts/int, Int, loaded [concrete = constants.%Int.generic] // CHECK:STDOUT: %Core.Copy: type = import_ref Core//prelude/parts/copy, Copy, loaded [concrete = constants.%Copy.type] // CHECK:STDOUT: %Core.import_ref.18d: @Int.as.Copy.impl.%Int.as.Copy.impl.Op.type (%Int.as.Copy.impl.Op.type.824) = import_ref Core//prelude/parts/int, loc{{\d+_\d+}}, loaded [symbolic = @Int.as.Copy.impl.%Int.as.Copy.impl.Op (constants.%Int.as.Copy.impl.Op.9b9)] // CHECK:STDOUT: %Copy.impl_witness_table.e76 = impl_witness_table (%Core.import_ref.18d), @Int.as.Copy.impl [concrete] // CHECK:STDOUT: %Core.Destroy: type = import_ref Core//prelude/parts/destroy, Destroy, loaded [concrete = constants.%Destroy.type] // CHECK:STDOUT: %Core.UInt: %UInt.type = import_ref Core//prelude/parts/uint, UInt, loaded [concrete = constants.%UInt.generic] // CHECK:STDOUT: %Core.import_ref.c3c: @UInt.as.Copy.impl.%UInt.as.Copy.impl.Op.type (%UInt.as.Copy.impl.Op.type.68f) = import_ref Core//prelude/parts/uint, loc{{\d+_\d+}}, loaded [symbolic = @UInt.as.Copy.impl.%UInt.as.Copy.impl.Op (constants.%UInt.as.Copy.impl.Op.576)] // CHECK:STDOUT: %Copy.impl_witness_table.bd0 = impl_witness_table (%Core.import_ref.c3c), @UInt.as.Copy.impl [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .AdaptStruct = %AdaptStruct.decl // CHECK:STDOUT: .I = %I.decl // CHECK:STDOUT: .InTuple = %InTuple.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %AdaptStruct.decl: type = class_decl @AdaptStruct [concrete = constants.%AdaptStruct] {} {} // CHECK:STDOUT: %I.decl: %I.type = fn_decl @I [concrete = constants.%I] { // CHECK:STDOUT: %g.patt: %pattern_type.341 = value_binding_pattern g [concrete] // CHECK:STDOUT: %g.param_patt: %pattern_type.341 = value_param_pattern %g.patt [concrete] // CHECK:STDOUT: %return.patt: %pattern_type.341 = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: %pattern_type.341 = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %AdaptStruct.ref.loc8_25: type = name_ref AdaptStruct, file.%AdaptStruct.decl [concrete = constants.%AdaptStruct] // CHECK:STDOUT: %.loc8: Core.Form = init_form %AdaptStruct.ref.loc8_25 [concrete = constants.%.8e3] // CHECK:STDOUT: %g.param: %AdaptStruct = value_param call_param0 // CHECK:STDOUT: %AdaptStruct.ref.loc8_9: type = name_ref AdaptStruct, file.%AdaptStruct.decl [concrete = constants.%AdaptStruct] // CHECK:STDOUT: %g: %AdaptStruct = value_binding g, %g.param // CHECK:STDOUT: %return.param: ref %AdaptStruct = out_param call_param1 // CHECK:STDOUT: %return: ref %AdaptStruct = return_slot %return.param // CHECK:STDOUT: } // CHECK:STDOUT: %InTuple.decl: %InTuple.type = fn_decl @InTuple [concrete = constants.%InTuple] { // CHECK:STDOUT: %c.patt: %pattern_type.b13 = value_binding_pattern c [concrete] // CHECK:STDOUT: %c.param_patt: %pattern_type.b13 = value_param_pattern %c.patt [concrete] // CHECK:STDOUT: %return.patt: %pattern_type.b13 = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: %pattern_type.b13 = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %AdaptStruct.ref.loc13_39: type = name_ref AdaptStruct, file.%AdaptStruct.decl [concrete = constants.%AdaptStruct] // CHECK:STDOUT: %u32.loc13_52: type = type_literal constants.%u32 [concrete = constants.%u32] // CHECK:STDOUT: %.loc13_55.1: %tuple.type.24b = tuple_literal (%AdaptStruct.ref.loc13_39, %u32.loc13_52) [concrete = constants.%tuple.556] // CHECK:STDOUT: %.loc13_55.2: type = converted %.loc13_55.1, constants.%tuple.type.691 [concrete = constants.%tuple.type.691] // CHECK:STDOUT: %.loc13_55.3: Core.Form = init_form %.loc13_55.2 [concrete = constants.%.174] // CHECK:STDOUT: %c.param: %tuple.type.691 = value_param call_param0 // CHECK:STDOUT: %.loc13_32.1: type = splice_block %.loc13_32.3 [concrete = constants.%tuple.type.691] { // CHECK:STDOUT: %AdaptStruct.ref.loc13_16: type = name_ref AdaptStruct, file.%AdaptStruct.decl [concrete = constants.%AdaptStruct] // CHECK:STDOUT: %u32.loc13_29: type = type_literal constants.%u32 [concrete = constants.%u32] // CHECK:STDOUT: %.loc13_32.2: %tuple.type.24b = tuple_literal (%AdaptStruct.ref.loc13_16, %u32.loc13_29) [concrete = constants.%tuple.556] // CHECK:STDOUT: %.loc13_32.3: type = converted %.loc13_32.2, constants.%tuple.type.691 [concrete = constants.%tuple.type.691] // CHECK:STDOUT: } // CHECK:STDOUT: %c: %tuple.type.691 = value_binding c, %c.param // CHECK:STDOUT: %return.param: ref %tuple.type.691 = out_param call_param1 // CHECK:STDOUT: %return: ref %tuple.type.691 = return_slot %return.param // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @AdaptStruct { // CHECK:STDOUT: %i32.loc5_14: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %i32.loc5_23: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %struct_type.e.f: type = struct_type {.e: %i32, .f: %i32} [concrete = constants.%struct_type.e.f] // CHECK:STDOUT: adapt_decl %struct_type.e.f [concrete] // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%struct_type.e.f [concrete = constants.%complete_type.511] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%AdaptStruct // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @I(%g.param: %AdaptStruct) -> out %return.param: %AdaptStruct { // CHECK:STDOUT: !entry: // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %h.patt: %pattern_type.341 = ref_binding_pattern h [concrete] // CHECK:STDOUT: %h.var_patt: %pattern_type.341 = var_pattern %h.patt [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %h.var: ref %AdaptStruct = var %h.var_patt // CHECK:STDOUT: %g.ref: %AdaptStruct = name_ref g, %g // CHECK:STDOUT: %.loc9_3.1: %struct_type.e.f = as_compatible %g.ref // CHECK:STDOUT: %.loc9_3.2: %i32 = struct_access %.loc9_3.1, element0 // CHECK:STDOUT: %impl.elem0.loc9_3.1: %.8e2 = impl_witness_access constants.%Copy.impl_witness.f17, element0 [concrete = constants.%Int.as.Copy.impl.Op.664] // CHECK:STDOUT: %bound_method.loc9_3.1: = bound_method %.loc9_3.2, %impl.elem0.loc9_3.1 // CHECK:STDOUT: %specific_fn.loc9_3.1: = specific_function %impl.elem0.loc9_3.1, @Int.as.Copy.impl.Op(constants.%int_32) [concrete = constants.%Int.as.Copy.impl.Op.specific_fn] // CHECK:STDOUT: %bound_method.loc9_3.2: = bound_method %.loc9_3.2, %specific_fn.loc9_3.1 // CHECK:STDOUT: %Int.as.Copy.impl.Op.call.loc9_3.1: init %i32 = call %bound_method.loc9_3.2(%.loc9_3.2) // CHECK:STDOUT: %.loc9_3.3: ref %struct_type.e.f = as_compatible %h.var // CHECK:STDOUT: %.loc9_3.4: ref %i32 = struct_access %.loc9_3.3, element0 // CHECK:STDOUT: %.loc9_3.5: init %i32 to %.loc9_3.4 = in_place_init %Int.as.Copy.impl.Op.call.loc9_3.1 // CHECK:STDOUT: %.loc9_3.6: %i32 = struct_access %.loc9_3.1, element1 // CHECK:STDOUT: %impl.elem0.loc9_3.2: %.8e2 = impl_witness_access constants.%Copy.impl_witness.f17, element0 [concrete = constants.%Int.as.Copy.impl.Op.664] // CHECK:STDOUT: %bound_method.loc9_3.3: = bound_method %.loc9_3.6, %impl.elem0.loc9_3.2 // CHECK:STDOUT: %specific_fn.loc9_3.2: = specific_function %impl.elem0.loc9_3.2, @Int.as.Copy.impl.Op(constants.%int_32) [concrete = constants.%Int.as.Copy.impl.Op.specific_fn] // CHECK:STDOUT: %bound_method.loc9_3.4: = bound_method %.loc9_3.6, %specific_fn.loc9_3.2 // CHECK:STDOUT: %Int.as.Copy.impl.Op.call.loc9_3.2: init %i32 = call %bound_method.loc9_3.4(%.loc9_3.6) // CHECK:STDOUT: %.loc9_3.7: ref %i32 = struct_access %.loc9_3.3, element1 // CHECK:STDOUT: %.loc9_3.8: init %i32 to %.loc9_3.7 = in_place_init %Int.as.Copy.impl.Op.call.loc9_3.2 // CHECK:STDOUT: %.loc9_3.9: init %struct_type.e.f to %.loc9_3.3 = struct_init (%.loc9_3.5, %.loc9_3.8) // CHECK:STDOUT: %.loc9_3.10: init %AdaptStruct = as_compatible %.loc9_3.9 // CHECK:STDOUT: %.loc9_3.11: init %AdaptStruct = converted %g.ref, %.loc9_3.10 // CHECK:STDOUT: assign %h.var, %.loc9_3.11 // CHECK:STDOUT: %AdaptStruct.ref.loc9: type = name_ref AdaptStruct, file.%AdaptStruct.decl [concrete = constants.%AdaptStruct] // CHECK:STDOUT: %h: ref %AdaptStruct = ref_binding h, %h.var // CHECK:STDOUT: %h.ref: ref %AdaptStruct = name_ref h, %h // CHECK:STDOUT: %.loc10_11.1: ref %struct_type.e.f = as_compatible %h.ref // CHECK:STDOUT: %.loc10_11.2: ref %i32 = struct_access %.loc10_11.1, element0 // CHECK:STDOUT: %.loc10_11.3: %i32 = acquire_value %.loc10_11.2 // CHECK:STDOUT: %impl.elem0.loc10_11.1: %.8e2 = impl_witness_access constants.%Copy.impl_witness.f17, element0 [concrete = constants.%Int.as.Copy.impl.Op.664] // CHECK:STDOUT: %bound_method.loc10_11.1: = bound_method %.loc10_11.3, %impl.elem0.loc10_11.1 // CHECK:STDOUT: %specific_fn.loc10_11.1: = specific_function %impl.elem0.loc10_11.1, @Int.as.Copy.impl.Op(constants.%int_32) [concrete = constants.%Int.as.Copy.impl.Op.specific_fn] // CHECK:STDOUT: %bound_method.loc10_11.2: = bound_method %.loc10_11.3, %specific_fn.loc10_11.1 // CHECK:STDOUT: %Int.as.Copy.impl.Op.call.loc10_11.1: init %i32 = call %bound_method.loc10_11.2(%.loc10_11.3) // CHECK:STDOUT: %.loc10_11.4: ref %struct_type.e.f = as_compatible %return.param // CHECK:STDOUT: %.loc10_11.5: ref %i32 = struct_access %.loc10_11.4, element0 // CHECK:STDOUT: %.loc10_11.6: init %i32 to %.loc10_11.5 = in_place_init %Int.as.Copy.impl.Op.call.loc10_11.1 // CHECK:STDOUT: %.loc10_11.7: ref %i32 = struct_access %.loc10_11.1, element1 // CHECK:STDOUT: %.loc10_11.8: %i32 = acquire_value %.loc10_11.7 // CHECK:STDOUT: %impl.elem0.loc10_11.2: %.8e2 = impl_witness_access constants.%Copy.impl_witness.f17, element0 [concrete = constants.%Int.as.Copy.impl.Op.664] // CHECK:STDOUT: %bound_method.loc10_11.3: = bound_method %.loc10_11.8, %impl.elem0.loc10_11.2 // CHECK:STDOUT: %specific_fn.loc10_11.2: = specific_function %impl.elem0.loc10_11.2, @Int.as.Copy.impl.Op(constants.%int_32) [concrete = constants.%Int.as.Copy.impl.Op.specific_fn] // CHECK:STDOUT: %bound_method.loc10_11.4: = bound_method %.loc10_11.8, %specific_fn.loc10_11.2 // CHECK:STDOUT: %Int.as.Copy.impl.Op.call.loc10_11.2: init %i32 = call %bound_method.loc10_11.4(%.loc10_11.8) // CHECK:STDOUT: %.loc10_11.9: ref %i32 = struct_access %.loc10_11.4, element1 // CHECK:STDOUT: %.loc10_11.10: init %i32 to %.loc10_11.9 = in_place_init %Int.as.Copy.impl.Op.call.loc10_11.2 // CHECK:STDOUT: %.loc10_11.11: init %struct_type.e.f to %.loc10_11.4 = struct_init (%.loc10_11.6, %.loc10_11.10) // CHECK:STDOUT: %.loc10_11.12: init %AdaptStruct = as_compatible %.loc10_11.11 // CHECK:STDOUT: %.loc10_11.13: init %AdaptStruct = converted %h.ref, %.loc10_11.12 // CHECK:STDOUT: %Destroy.Op.bound: = bound_method %h.var, constants.%Destroy.Op.651ba6.1 // CHECK:STDOUT: %Destroy.Op.call: init %empty_tuple.type = call %Destroy.Op.bound(%h.var) // CHECK:STDOUT: return %.loc10_11.13 to %return.param // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @Destroy.Op.loc9(%self.param: ref %AdaptStruct) = "no_op"; // CHECK:STDOUT: // CHECK:STDOUT: fn @InTuple(%c.param: %tuple.type.691) -> out %return.param: %tuple.type.691 { // CHECK:STDOUT: !entry: // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %d.patt: %pattern_type.b13 = ref_binding_pattern d [concrete] // CHECK:STDOUT: %d.var_patt: %pattern_type.b13 = var_pattern %d.patt [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %d.var: ref %tuple.type.691 = var %d.var_patt // CHECK:STDOUT: %c.ref: %tuple.type.691 = name_ref c, %c // CHECK:STDOUT: %tuple.elem0.loc14_31.1: %AdaptStruct = tuple_access %c.ref, element0 // CHECK:STDOUT: %.loc14_31.1: %struct_type.e.f = as_compatible %tuple.elem0.loc14_31.1 // CHECK:STDOUT: %.loc14_31.2: %i32 = struct_access %.loc14_31.1, element0 // CHECK:STDOUT: %impl.elem0.loc14_31.1: %.8e2 = impl_witness_access constants.%Copy.impl_witness.f17, element0 [concrete = constants.%Int.as.Copy.impl.Op.664] // CHECK:STDOUT: %bound_method.loc14_31.1: = bound_method %.loc14_31.2, %impl.elem0.loc14_31.1 // CHECK:STDOUT: %specific_fn.loc14_31.1: = specific_function %impl.elem0.loc14_31.1, @Int.as.Copy.impl.Op(constants.%int_32) [concrete = constants.%Int.as.Copy.impl.Op.specific_fn] // CHECK:STDOUT: %bound_method.loc14_31.2: = bound_method %.loc14_31.2, %specific_fn.loc14_31.1 // CHECK:STDOUT: %Int.as.Copy.impl.Op.call.loc14_31.1: init %i32 = call %bound_method.loc14_31.2(%.loc14_31.2) // CHECK:STDOUT: %tuple.elem0.loc14_31.2: ref %AdaptStruct = tuple_access %d.var, element0 // CHECK:STDOUT: %.loc14_31.3: ref %struct_type.e.f = as_compatible %tuple.elem0.loc14_31.2 // CHECK:STDOUT: %.loc14_31.4: ref %i32 = struct_access %.loc14_31.3, element0 // CHECK:STDOUT: %.loc14_31.5: init %i32 to %.loc14_31.4 = in_place_init %Int.as.Copy.impl.Op.call.loc14_31.1 // CHECK:STDOUT: %.loc14_31.6: %i32 = struct_access %.loc14_31.1, element1 // CHECK:STDOUT: %impl.elem0.loc14_31.2: %.8e2 = impl_witness_access constants.%Copy.impl_witness.f17, element0 [concrete = constants.%Int.as.Copy.impl.Op.664] // CHECK:STDOUT: %bound_method.loc14_31.3: = bound_method %.loc14_31.6, %impl.elem0.loc14_31.2 // CHECK:STDOUT: %specific_fn.loc14_31.2: = specific_function %impl.elem0.loc14_31.2, @Int.as.Copy.impl.Op(constants.%int_32) [concrete = constants.%Int.as.Copy.impl.Op.specific_fn] // CHECK:STDOUT: %bound_method.loc14_31.4: = bound_method %.loc14_31.6, %specific_fn.loc14_31.2 // CHECK:STDOUT: %Int.as.Copy.impl.Op.call.loc14_31.2: init %i32 = call %bound_method.loc14_31.4(%.loc14_31.6) // CHECK:STDOUT: %.loc14_31.7: ref %i32 = struct_access %.loc14_31.3, element1 // CHECK:STDOUT: %.loc14_31.8: init %i32 to %.loc14_31.7 = in_place_init %Int.as.Copy.impl.Op.call.loc14_31.2 // CHECK:STDOUT: %.loc14_31.9: init %struct_type.e.f to %.loc14_31.3 = struct_init (%.loc14_31.5, %.loc14_31.8) // CHECK:STDOUT: %.loc14_31.10: init %AdaptStruct = as_compatible %.loc14_31.9 // CHECK:STDOUT: %.loc14_31.11: init %AdaptStruct = converted %tuple.elem0.loc14_31.1, %.loc14_31.10 // CHECK:STDOUT: %tuple.elem1.loc14_31.1: %u32 = tuple_access %c.ref, element1 // CHECK:STDOUT: %impl.elem0.loc14_31.3: %.38c = impl_witness_access constants.%Copy.impl_witness.514, element0 [concrete = constants.%UInt.as.Copy.impl.Op.c10] // CHECK:STDOUT: %bound_method.loc14_31.5: = bound_method %tuple.elem1.loc14_31.1, %impl.elem0.loc14_31.3 // CHECK:STDOUT: %specific_fn.loc14_31.3: = specific_function %impl.elem0.loc14_31.3, @UInt.as.Copy.impl.Op(constants.%int_32) [concrete = constants.%UInt.as.Copy.impl.Op.specific_fn] // CHECK:STDOUT: %bound_method.loc14_31.6: = bound_method %tuple.elem1.loc14_31.1, %specific_fn.loc14_31.3 // CHECK:STDOUT: %UInt.as.Copy.impl.Op.call.loc14: init %u32 = call %bound_method.loc14_31.6(%tuple.elem1.loc14_31.1) // CHECK:STDOUT: %tuple.elem1.loc14_31.2: ref %u32 = tuple_access %d.var, element1 // CHECK:STDOUT: %.loc14_31.12: init %u32 to %tuple.elem1.loc14_31.2 = in_place_init %UInt.as.Copy.impl.Op.call.loc14 // CHECK:STDOUT: %.loc14_31.13: init %tuple.type.691 to %d.var = tuple_init (%.loc14_31.11, %.loc14_31.12) // CHECK:STDOUT: %.loc14_3: init %tuple.type.691 = converted %c.ref, %.loc14_31.13 // CHECK:STDOUT: assign %d.var, %.loc14_3 // CHECK:STDOUT: %.loc14_27.1: type = splice_block %.loc14_27.3 [concrete = constants.%tuple.type.691] { // CHECK:STDOUT: %AdaptStruct.ref.loc14: type = name_ref AdaptStruct, file.%AdaptStruct.decl [concrete = constants.%AdaptStruct] // CHECK:STDOUT: %u32.loc14: type = type_literal constants.%u32 [concrete = constants.%u32] // CHECK:STDOUT: %.loc14_27.2: %tuple.type.24b = tuple_literal (%AdaptStruct.ref.loc14, %u32.loc14) [concrete = constants.%tuple.556] // CHECK:STDOUT: %.loc14_27.3: type = converted %.loc14_27.2, constants.%tuple.type.691 [concrete = constants.%tuple.type.691] // CHECK:STDOUT: } // CHECK:STDOUT: %d: ref %tuple.type.691 = ref_binding d, %d.var // CHECK:STDOUT: %d.ref: ref %tuple.type.691 = name_ref d, %d // CHECK:STDOUT: %tuple.elem0.loc15_10.1: ref %AdaptStruct = tuple_access %d.ref, element0 // CHECK:STDOUT: %.loc15_10.1: ref %struct_type.e.f = as_compatible %tuple.elem0.loc15_10.1 // CHECK:STDOUT: %.loc15_10.2: ref %i32 = struct_access %.loc15_10.1, element0 // CHECK:STDOUT: %.loc15_10.3: %i32 = acquire_value %.loc15_10.2 // CHECK:STDOUT: %impl.elem0.loc15_10.1: %.8e2 = impl_witness_access constants.%Copy.impl_witness.f17, element0 [concrete = constants.%Int.as.Copy.impl.Op.664] // CHECK:STDOUT: %bound_method.loc15_10.1: = bound_method %.loc15_10.3, %impl.elem0.loc15_10.1 // CHECK:STDOUT: %specific_fn.loc15_10.1: = specific_function %impl.elem0.loc15_10.1, @Int.as.Copy.impl.Op(constants.%int_32) [concrete = constants.%Int.as.Copy.impl.Op.specific_fn] // CHECK:STDOUT: %bound_method.loc15_10.2: = bound_method %.loc15_10.3, %specific_fn.loc15_10.1 // CHECK:STDOUT: %Int.as.Copy.impl.Op.call.loc15_10.1: init %i32 = call %bound_method.loc15_10.2(%.loc15_10.3) // CHECK:STDOUT: %tuple.elem0.loc15_10.2: ref %AdaptStruct = tuple_access %return.param, element0 // CHECK:STDOUT: %.loc15_10.4: ref %struct_type.e.f = as_compatible %tuple.elem0.loc15_10.2 // CHECK:STDOUT: %.loc15_10.5: ref %i32 = struct_access %.loc15_10.4, element0 // CHECK:STDOUT: %.loc15_10.6: init %i32 to %.loc15_10.5 = in_place_init %Int.as.Copy.impl.Op.call.loc15_10.1 // CHECK:STDOUT: %.loc15_10.7: ref %i32 = struct_access %.loc15_10.1, element1 // CHECK:STDOUT: %.loc15_10.8: %i32 = acquire_value %.loc15_10.7 // CHECK:STDOUT: %impl.elem0.loc15_10.2: %.8e2 = impl_witness_access constants.%Copy.impl_witness.f17, element0 [concrete = constants.%Int.as.Copy.impl.Op.664] // CHECK:STDOUT: %bound_method.loc15_10.3: = bound_method %.loc15_10.8, %impl.elem0.loc15_10.2 // CHECK:STDOUT: %specific_fn.loc15_10.2: = specific_function %impl.elem0.loc15_10.2, @Int.as.Copy.impl.Op(constants.%int_32) [concrete = constants.%Int.as.Copy.impl.Op.specific_fn] // CHECK:STDOUT: %bound_method.loc15_10.4: = bound_method %.loc15_10.8, %specific_fn.loc15_10.2 // CHECK:STDOUT: %Int.as.Copy.impl.Op.call.loc15_10.2: init %i32 = call %bound_method.loc15_10.4(%.loc15_10.8) // CHECK:STDOUT: %.loc15_10.9: ref %i32 = struct_access %.loc15_10.4, element1 // CHECK:STDOUT: %.loc15_10.10: init %i32 to %.loc15_10.9 = in_place_init %Int.as.Copy.impl.Op.call.loc15_10.2 // CHECK:STDOUT: %.loc15_10.11: init %struct_type.e.f to %.loc15_10.4 = struct_init (%.loc15_10.6, %.loc15_10.10) // CHECK:STDOUT: %.loc15_10.12: init %AdaptStruct = as_compatible %.loc15_10.11 // CHECK:STDOUT: %.loc15_10.13: init %AdaptStruct = converted %tuple.elem0.loc15_10.1, %.loc15_10.12 // CHECK:STDOUT: %tuple.elem1.loc15_10.1: ref %u32 = tuple_access %d.ref, element1 // CHECK:STDOUT: %.loc15_10.14: %u32 = acquire_value %tuple.elem1.loc15_10.1 // CHECK:STDOUT: %impl.elem0.loc15_10.3: %.38c = impl_witness_access constants.%Copy.impl_witness.514, element0 [concrete = constants.%UInt.as.Copy.impl.Op.c10] // CHECK:STDOUT: %bound_method.loc15_10.5: = bound_method %.loc15_10.14, %impl.elem0.loc15_10.3 // CHECK:STDOUT: %specific_fn.loc15_10.3: = specific_function %impl.elem0.loc15_10.3, @UInt.as.Copy.impl.Op(constants.%int_32) [concrete = constants.%UInt.as.Copy.impl.Op.specific_fn] // CHECK:STDOUT: %bound_method.loc15_10.6: = bound_method %.loc15_10.14, %specific_fn.loc15_10.3 // CHECK:STDOUT: %UInt.as.Copy.impl.Op.call.loc15: init %u32 = call %bound_method.loc15_10.6(%.loc15_10.14) // CHECK:STDOUT: %tuple.elem1.loc15_10.2: ref %u32 = tuple_access %return.param, element1 // CHECK:STDOUT: %.loc15_10.15: init %u32 to %tuple.elem1.loc15_10.2 = in_place_init %UInt.as.Copy.impl.Op.call.loc15 // CHECK:STDOUT: %.loc15_10.16: init %tuple.type.691 to %return.param = tuple_init (%.loc15_10.13, %.loc15_10.15) // CHECK:STDOUT: %.loc15_11: init %tuple.type.691 = converted %d.ref, %.loc15_10.16 // CHECK:STDOUT: %Destroy.Op.bound: = bound_method %d.var, constants.%Destroy.Op.651ba6.2 // CHECK:STDOUT: %Destroy.Op.call: init %empty_tuple.type = call %Destroy.Op.bound(%d.var) // CHECK:STDOUT: return %.loc15_11 to %return.param // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @Destroy.Op.loc14(%self.param: ref %tuple.type.691) = "no_op"; // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/class/adapter/convert_incomplete.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/int.carbon // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/class/adapter/convert_incomplete.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/class/adapter/convert_incomplete.carbon // --- already_complete.carbon library "[[@TEST_NAME]]"; class Adapter(T:! type) { extend adapt T*; } class X {} // Trigger completion of Adapter(X) here. var x: Adapter(X); fn F(p: Adapter(X)*) { let unused x: X* = *p as X*; } // --- can_be_completed.carbon library "[[@TEST_NAME]]"; class Adapter(T:! type) { extend adapt T*; } class X {} fn F(p: Adapter(X)*) { // The conversion here triggers completion of Adapter(X) // so we can determine what it adapts. let unused x: X* = *p as X*; } // --- fail_incomplete.carbon library "[[@TEST_NAME]]"; class Adapter(T:! type); class X {} fn F(p: Adapter(X)*) { // CHECK:STDERR: fail_incomplete.carbon:[[@LINE+7]]:22: error: cannot convert expression of type `Adapter(X)` to `X*` with `as` [ConversionFailure] // CHECK:STDERR: let unused x: X* = *p as X*; // CHECK:STDERR: ^~~~~~~~ // CHECK:STDERR: fail_incomplete.carbon:[[@LINE+4]]:22: note: type `Adapter(X)` does not implement interface `Core.As(X*)` [MissingImplInMemberAccessInContext] // CHECK:STDERR: let unused x: X* = *p as X*; // CHECK:STDERR: ^~~~~~~~ // CHECK:STDERR: let unused x: X* = *p as X*; } ================================================ FILE: toolchain/check/testdata/class/adapter/extend_adapt.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/int.carbon // TODO: Add ranges and switch to "--dump-sem-ir-ranges=only". // EXTRA-ARGS: --dump-sem-ir-ranges=if-present // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/class/adapter/extend_adapt.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/class/adapter/extend_adapt.carbon // --- basic.carbon library "[[@TEST_NAME]]"; class SomeClassAdapter; class SomeClass { var a: i32; var b: i32; fn StaticMemberFunction(); fn AdapterMethod[self: SomeClassAdapter](); } class SomeClassAdapter { extend adapt SomeClass; } fn TestStaticMemberFunction(a: SomeClassAdapter) { a.StaticMemberFunction(); } fn TestAdapterMethod(a: SomeClassAdapter) { a.AdapterMethod(); } // --- fail_todo_method_access.carbon library "[[@TEST_NAME]]"; class SomeClass { fn F[self: Self](); } class SomeClassAdapter { extend adapt SomeClass; } fn F(a: SomeClassAdapter) { // CHECK:STDERR: fail_todo_method_access.carbon:[[@LINE+10]]:3: error: cannot implicitly convert expression of type `SomeClassAdapter` to `SomeClass` [ConversionFailure] // CHECK:STDERR: a.F(); // CHECK:STDERR: ^ // CHECK:STDERR: fail_todo_method_access.carbon:[[@LINE+7]]:3: note: type `SomeClassAdapter` does not implement interface `Core.ImplicitAs(SomeClass)` [MissingImplInMemberAccessInContext] // CHECK:STDERR: a.F(); // CHECK:STDERR: ^ // CHECK:STDERR: fail_todo_method_access.carbon:[[@LINE-14]]:8: note: initializing function parameter [InCallToFunctionParam] // CHECK:STDERR: fn F[self: Self](); // CHECK:STDERR: ^~~~~~~~~~ // CHECK:STDERR: a.F(); } // --- fail_todo_field_access.carbon library "[[@TEST_NAME]]"; class SomeClass { var a: i32; var b: i32; } class SomeClassAdapter { extend adapt SomeClass; } fn F(a: SomeClassAdapter) -> i32 { // CHECK:STDERR: fail_todo_field_access.carbon:[[@LINE+7]]:10: error: cannot implicitly convert expression of type `SomeClassAdapter` to `SomeClass` [ConversionFailure] // CHECK:STDERR: return a.b; // CHECK:STDERR: ^~~ // CHECK:STDERR: fail_todo_field_access.carbon:[[@LINE+4]]:10: note: type `SomeClassAdapter` does not implement interface `Core.ImplicitAs(SomeClass)` [MissingImplInMemberAccessInContext] // CHECK:STDERR: return a.b; // CHECK:STDERR: ^~~ // CHECK:STDERR: return a.b; } // --- fail_todo_adapt_struct.carbon library "[[@TEST_NAME]]"; class StructAdapter { extend adapt {.a: i32, .b: i32}; } fn F(a: StructAdapter) -> i32 { // TODO: This should be allowed. // CHECK:STDERR: fail_todo_adapt_struct.carbon:[[@LINE+4]]:10: error: member name `b` not found in `StructAdapter` [MemberNameNotFoundInInstScope] // CHECK:STDERR: return a.b; // CHECK:STDERR: ^~~ // CHECK:STDERR: return a.b; } // --- fail_todo_adapt_tuple.carbon library "[[@TEST_NAME]]"; class TupleAdapter { extend adapt (i32, i32); } fn F(a: TupleAdapter) -> i32 { // TODO: This should be allowed. // CHECK:STDERR: fail_todo_adapt_tuple.carbon:[[@LINE+4]]:10: error: type `TupleAdapter` does not support tuple indexing; only tuples can be indexed that way [TupleIndexOnANonTupleType] // CHECK:STDERR: return a.1; // CHECK:STDERR: ^~~ // CHECK:STDERR: return a.1; } // --- fail_adapt_builtin.carbon library "[[@TEST_NAME]]"; fn MakeInt(N: Core.IntLiteral()) -> type = "int.make_type_signed"; class IntAdapter { extend adapt MakeInt(32); } fn F(a: IntAdapter) -> i32 { // Builtin types have no member names. // CHECK:STDERR: fail_adapt_builtin.carbon:[[@LINE+4]]:10: error: member name `foo` not found in `IntAdapter` [MemberNameNotFoundInInstScope] // CHECK:STDERR: return a.foo; // CHECK:STDERR: ^~~~~ // CHECK:STDERR: return a.foo; } // CHECK:STDOUT: --- basic.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %SomeClassAdapter: type = class_type @SomeClassAdapter [concrete] // CHECK:STDOUT: %SomeClass: type = class_type @SomeClass [concrete] // CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [concrete] // CHECK:STDOUT: %Int.type: type = generic_class_type @Int [concrete] // CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] // CHECK:STDOUT: %Int.generic: %Int.type = struct_value () [concrete] // CHECK:STDOUT: %i32: type = class_type @Int, @Int(%int_32) [concrete] // CHECK:STDOUT: %SomeClass.elem: type = unbound_element_type %SomeClass, %i32 [concrete] // CHECK:STDOUT: %SomeClass.StaticMemberFunction.type: type = fn_type @SomeClass.StaticMemberFunction [concrete] // CHECK:STDOUT: %SomeClass.StaticMemberFunction: %SomeClass.StaticMemberFunction.type = struct_value () [concrete] // CHECK:STDOUT: %pattern_type.31a: type = pattern_type %SomeClassAdapter [concrete] // CHECK:STDOUT: %SomeClass.AdapterMethod.type: type = fn_type @SomeClass.AdapterMethod [concrete] // CHECK:STDOUT: %SomeClass.AdapterMethod: %SomeClass.AdapterMethod.type = struct_value () [concrete] // CHECK:STDOUT: %struct_type.a.b: type = struct_type {.a: %i32, .b: %i32} [concrete] // CHECK:STDOUT: %complete_type.705: = complete_type_witness %struct_type.a.b [concrete] // CHECK:STDOUT: %TestStaticMemberFunction.type: type = fn_type @TestStaticMemberFunction [concrete] // CHECK:STDOUT: %TestStaticMemberFunction: %TestStaticMemberFunction.type = struct_value () [concrete] // CHECK:STDOUT: %TestAdapterMethod.type: type = fn_type @TestAdapterMethod [concrete] // CHECK:STDOUT: %TestAdapterMethod: %TestAdapterMethod.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: .Int = %Core.Int // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: %Core.Int: %Int.type = import_ref Core//prelude/parts/int, Int, loaded [concrete = constants.%Int.generic] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .SomeClassAdapter = %SomeClassAdapter.decl.loc4 // CHECK:STDOUT: .SomeClass = %SomeClass.decl // CHECK:STDOUT: .TestStaticMemberFunction = %TestStaticMemberFunction.decl // CHECK:STDOUT: .TestAdapterMethod = %TestAdapterMethod.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %SomeClassAdapter.decl.loc4: type = class_decl @SomeClassAdapter [concrete = constants.%SomeClassAdapter] {} {} // CHECK:STDOUT: %SomeClass.decl: type = class_decl @SomeClass [concrete = constants.%SomeClass] {} {} // CHECK:STDOUT: %SomeClassAdapter.decl.loc15: type = class_decl @SomeClassAdapter [concrete = constants.%SomeClassAdapter] {} {} // CHECK:STDOUT: %TestStaticMemberFunction.decl: %TestStaticMemberFunction.type = fn_decl @TestStaticMemberFunction [concrete = constants.%TestStaticMemberFunction] { // CHECK:STDOUT: %a.patt: %pattern_type.31a = value_binding_pattern a [concrete] // CHECK:STDOUT: %a.param_patt: %pattern_type.31a = value_param_pattern %a.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %a.param: %SomeClassAdapter = value_param call_param0 // CHECK:STDOUT: %SomeClassAdapter.ref: type = name_ref SomeClassAdapter, file.%SomeClassAdapter.decl.loc4 [concrete = constants.%SomeClassAdapter] // CHECK:STDOUT: %a: %SomeClassAdapter = value_binding a, %a.param // CHECK:STDOUT: } // CHECK:STDOUT: %TestAdapterMethod.decl: %TestAdapterMethod.type = fn_decl @TestAdapterMethod [concrete = constants.%TestAdapterMethod] { // CHECK:STDOUT: %a.patt: %pattern_type.31a = value_binding_pattern a [concrete] // CHECK:STDOUT: %a.param_patt: %pattern_type.31a = value_param_pattern %a.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %a.param: %SomeClassAdapter = value_param call_param0 // CHECK:STDOUT: %SomeClassAdapter.ref: type = name_ref SomeClassAdapter, file.%SomeClassAdapter.decl.loc4 [concrete = constants.%SomeClassAdapter] // CHECK:STDOUT: %a: %SomeClassAdapter = value_binding a, %a.param // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @SomeClassAdapter { // CHECK:STDOUT: %SomeClass.ref: type = name_ref SomeClass, file.%SomeClass.decl [concrete = constants.%SomeClass] // CHECK:STDOUT: adapt_decl %SomeClass.ref [concrete] // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%struct_type.a.b [concrete = constants.%complete_type.705] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%SomeClassAdapter // CHECK:STDOUT: .SomeClass = // CHECK:STDOUT: .StaticMemberFunction = // CHECK:STDOUT: .AdapterMethod = // CHECK:STDOUT: extend %SomeClass.ref // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @SomeClass { // CHECK:STDOUT: %i32.loc7: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %.loc7: %SomeClass.elem = field_decl a, element0 [concrete] // CHECK:STDOUT: %i32.loc8: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %.loc8: %SomeClass.elem = field_decl b, element1 [concrete] // CHECK:STDOUT: %SomeClass.StaticMemberFunction.decl: %SomeClass.StaticMemberFunction.type = fn_decl @SomeClass.StaticMemberFunction [concrete = constants.%SomeClass.StaticMemberFunction] {} {} // CHECK:STDOUT: %SomeClass.AdapterMethod.decl: %SomeClass.AdapterMethod.type = fn_decl @SomeClass.AdapterMethod [concrete = constants.%SomeClass.AdapterMethod] { // CHECK:STDOUT: %self.patt: %pattern_type.31a = value_binding_pattern self [concrete] // CHECK:STDOUT: %self.param_patt: %pattern_type.31a = value_param_pattern %self.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %self.param: %SomeClassAdapter = value_param call_param0 // CHECK:STDOUT: %SomeClassAdapter.ref: type = name_ref SomeClassAdapter, file.%SomeClassAdapter.decl.loc4 [concrete = constants.%SomeClassAdapter] // CHECK:STDOUT: %self: %SomeClassAdapter = value_binding self, %self.param // CHECK:STDOUT: } // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%struct_type.a.b [concrete = constants.%complete_type.705] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%SomeClass // CHECK:STDOUT: .a = %.loc7 // CHECK:STDOUT: .b = %.loc8 // CHECK:STDOUT: .StaticMemberFunction = %SomeClass.StaticMemberFunction.decl // CHECK:STDOUT: .SomeClassAdapter = // CHECK:STDOUT: .AdapterMethod = %SomeClass.AdapterMethod.decl // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @SomeClass.StaticMemberFunction(); // CHECK:STDOUT: // CHECK:STDOUT: fn @SomeClass.AdapterMethod(%self.param: %SomeClassAdapter); // CHECK:STDOUT: // CHECK:STDOUT: fn @TestStaticMemberFunction(%a.param: %SomeClassAdapter) { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %a.ref: %SomeClassAdapter = name_ref a, %a // CHECK:STDOUT: %StaticMemberFunction.ref: %SomeClass.StaticMemberFunction.type = name_ref StaticMemberFunction, @SomeClass.%SomeClass.StaticMemberFunction.decl [concrete = constants.%SomeClass.StaticMemberFunction] // CHECK:STDOUT: %SomeClass.StaticMemberFunction.call: init %empty_tuple.type = call %StaticMemberFunction.ref() // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @TestAdapterMethod(%a.param: %SomeClassAdapter) { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %a.ref: %SomeClassAdapter = name_ref a, %a // CHECK:STDOUT: %AdapterMethod.ref: %SomeClass.AdapterMethod.type = name_ref AdapterMethod, @SomeClass.%SomeClass.AdapterMethod.decl [concrete = constants.%SomeClass.AdapterMethod] // CHECK:STDOUT: %SomeClass.AdapterMethod.bound: = bound_method %a.ref, %AdapterMethod.ref // CHECK:STDOUT: %SomeClass.AdapterMethod.call: init %empty_tuple.type = call %SomeClass.AdapterMethod.bound(%a.ref) // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- fail_todo_method_access.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %SomeClass: type = class_type @SomeClass [concrete] // CHECK:STDOUT: %pattern_type.ea0: type = pattern_type %SomeClass [concrete] // CHECK:STDOUT: %SomeClass.F.type: type = fn_type @SomeClass.F [concrete] // CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] // CHECK:STDOUT: %SomeClass.F: %SomeClass.F.type = struct_value () [concrete] // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %complete_type: = complete_type_witness %empty_struct_type [concrete] // CHECK:STDOUT: %SomeClassAdapter: type = class_type @SomeClassAdapter [concrete] // CHECK:STDOUT: %pattern_type.31a: type = pattern_type %SomeClassAdapter [concrete] // CHECK:STDOUT: %F.type: type = fn_type @F [concrete] // CHECK:STDOUT: %F: %F.type = struct_value () [concrete] // CHECK:STDOUT: %ImplicitAs.type.cc7: type = generic_interface_type @ImplicitAs [concrete] // CHECK:STDOUT: %ImplicitAs.generic: %ImplicitAs.type.cc7 = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: .ImplicitAs = %Core.ImplicitAs // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: %Core.ImplicitAs: %ImplicitAs.type.cc7 = import_ref Core//prelude/parts/as, ImplicitAs, loaded [concrete = constants.%ImplicitAs.generic] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .SomeClass = %SomeClass.decl // CHECK:STDOUT: .SomeClassAdapter = %SomeClassAdapter.decl // CHECK:STDOUT: .F = %F.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %SomeClass.decl: type = class_decl @SomeClass [concrete = constants.%SomeClass] {} {} // CHECK:STDOUT: %SomeClassAdapter.decl: type = class_decl @SomeClassAdapter [concrete = constants.%SomeClassAdapter] {} {} // CHECK:STDOUT: %F.decl: %F.type = fn_decl @F [concrete = constants.%F] { // CHECK:STDOUT: %a.patt: %pattern_type.31a = value_binding_pattern a [concrete] // CHECK:STDOUT: %a.param_patt: %pattern_type.31a = value_param_pattern %a.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %a.param: %SomeClassAdapter = value_param call_param0 // CHECK:STDOUT: %SomeClassAdapter.ref: type = name_ref SomeClassAdapter, file.%SomeClassAdapter.decl [concrete = constants.%SomeClassAdapter] // CHECK:STDOUT: %a: %SomeClassAdapter = value_binding a, %a.param // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @SomeClass { // CHECK:STDOUT: %SomeClass.F.decl: %SomeClass.F.type = fn_decl @SomeClass.F [concrete = constants.%SomeClass.F] { // CHECK:STDOUT: %self.patt: %pattern_type.ea0 = value_binding_pattern self [concrete] // CHECK:STDOUT: %self.param_patt: %pattern_type.ea0 = value_param_pattern %self.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %self.param: %SomeClass = value_param call_param0 // CHECK:STDOUT: %Self.ref: type = name_ref Self, constants.%SomeClass [concrete = constants.%SomeClass] // CHECK:STDOUT: %self: %SomeClass = value_binding self, %self.param // CHECK:STDOUT: } // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%SomeClass // CHECK:STDOUT: .F = %SomeClass.F.decl // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @SomeClassAdapter { // CHECK:STDOUT: %SomeClass.ref: type = name_ref SomeClass, file.%SomeClass.decl [concrete = constants.%SomeClass] // CHECK:STDOUT: adapt_decl %SomeClass.ref [concrete] // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%SomeClassAdapter // CHECK:STDOUT: .SomeClass = // CHECK:STDOUT: .F = // CHECK:STDOUT: extend %SomeClass.ref // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @SomeClass.F(%self.param: %SomeClass); // CHECK:STDOUT: // CHECK:STDOUT: fn @F(%a.param: %SomeClassAdapter) { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %a.ref: %SomeClassAdapter = name_ref a, %a // CHECK:STDOUT: %F.ref: %SomeClass.F.type = name_ref F, @SomeClass.%SomeClass.F.decl [concrete = constants.%SomeClass.F] // CHECK:STDOUT: %SomeClass.F.bound: = bound_method %a.ref, %F.ref // CHECK:STDOUT: %.loc23: %SomeClass = converted %a.ref, [concrete = ] // CHECK:STDOUT: %SomeClass.F.call: init %empty_tuple.type = call %SomeClass.F.bound() // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- fail_todo_field_access.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %SomeClass: type = class_type @SomeClass [concrete] // CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [concrete] // CHECK:STDOUT: %Int.type: type = generic_class_type @Int [concrete] // CHECK:STDOUT: %Int.generic: %Int.type = struct_value () [concrete] // CHECK:STDOUT: %i32: type = class_type @Int, @Int(%int_32) [concrete] // CHECK:STDOUT: %SomeClass.elem: type = unbound_element_type %SomeClass, %i32 [concrete] // CHECK:STDOUT: %struct_type.a.b: type = struct_type {.a: %i32, .b: %i32} [concrete] // CHECK:STDOUT: %complete_type.705: = complete_type_witness %struct_type.a.b [concrete] // CHECK:STDOUT: %SomeClassAdapter: type = class_type @SomeClassAdapter [concrete] // CHECK:STDOUT: %pattern_type.31a: type = pattern_type %SomeClassAdapter [concrete] // CHECK:STDOUT: %.ff5: Core.Form = init_form %i32 [concrete] // CHECK:STDOUT: %pattern_type.7ce: type = pattern_type %i32 [concrete] // CHECK:STDOUT: %F.type: type = fn_type @F [concrete] // CHECK:STDOUT: %F: %F.type = struct_value () [concrete] // CHECK:STDOUT: %ImplicitAs.type.cc7: type = generic_interface_type @ImplicitAs [concrete] // CHECK:STDOUT: %ImplicitAs.generic: %ImplicitAs.type.cc7 = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: .Int = %Core.Int // CHECK:STDOUT: .ImplicitAs = %Core.ImplicitAs // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: %Core.Int: %Int.type = import_ref Core//prelude/parts/int, Int, loaded [concrete = constants.%Int.generic] // CHECK:STDOUT: %Core.ImplicitAs: %ImplicitAs.type.cc7 = import_ref Core//prelude/parts/as, ImplicitAs, loaded [concrete = constants.%ImplicitAs.generic] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .SomeClass = %SomeClass.decl // CHECK:STDOUT: .SomeClassAdapter = %SomeClassAdapter.decl // CHECK:STDOUT: .F = %F.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %SomeClass.decl: type = class_decl @SomeClass [concrete = constants.%SomeClass] {} {} // CHECK:STDOUT: %SomeClassAdapter.decl: type = class_decl @SomeClassAdapter [concrete = constants.%SomeClassAdapter] {} {} // CHECK:STDOUT: %F.decl: %F.type = fn_decl @F [concrete = constants.%F] { // CHECK:STDOUT: %a.patt: %pattern_type.31a = value_binding_pattern a [concrete] // CHECK:STDOUT: %a.param_patt: %pattern_type.31a = value_param_pattern %a.patt [concrete] // CHECK:STDOUT: %return.patt: %pattern_type.7ce = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: %pattern_type.7ce = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %i32: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %.loc13: Core.Form = init_form %i32 [concrete = constants.%.ff5] // CHECK:STDOUT: %a.param: %SomeClassAdapter = value_param call_param0 // CHECK:STDOUT: %SomeClassAdapter.ref: type = name_ref SomeClassAdapter, file.%SomeClassAdapter.decl [concrete = constants.%SomeClassAdapter] // CHECK:STDOUT: %a: %SomeClassAdapter = value_binding a, %a.param // CHECK:STDOUT: %return.param: ref %i32 = out_param call_param1 // CHECK:STDOUT: %return: ref %i32 = return_slot %return.param // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @SomeClass { // CHECK:STDOUT: %i32.loc5: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %.loc5: %SomeClass.elem = field_decl a, element0 [concrete] // CHECK:STDOUT: %i32.loc6: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %.loc6: %SomeClass.elem = field_decl b, element1 [concrete] // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%struct_type.a.b [concrete = constants.%complete_type.705] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%SomeClass // CHECK:STDOUT: .a = %.loc5 // CHECK:STDOUT: .b = %.loc6 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @SomeClassAdapter { // CHECK:STDOUT: %SomeClass.ref: type = name_ref SomeClass, file.%SomeClass.decl [concrete = constants.%SomeClass] // CHECK:STDOUT: adapt_decl %SomeClass.ref [concrete] // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%struct_type.a.b [concrete = constants.%complete_type.705] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%SomeClassAdapter // CHECK:STDOUT: .SomeClass = // CHECK:STDOUT: .b = // CHECK:STDOUT: extend %SomeClass.ref // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @F(%a.param: %SomeClassAdapter) -> out %return.param: %i32 { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %a.ref: %SomeClassAdapter = name_ref a, %a // CHECK:STDOUT: %b.ref: %SomeClass.elem = name_ref b, @SomeClass.%.loc6 [concrete = @SomeClass.%.loc6] // CHECK:STDOUT: %.loc21_11.1: %SomeClass = converted %a.ref, [concrete = ] // CHECK:STDOUT: %.loc21_11.2: %i32 = class_element_access , element1 [concrete = ] // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- fail_todo_adapt_struct.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %StructAdapter: type = class_type @StructAdapter [concrete] // CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [concrete] // CHECK:STDOUT: %Int.type: type = generic_class_type @Int [concrete] // CHECK:STDOUT: %Int.generic: %Int.type = struct_value () [concrete] // CHECK:STDOUT: %i32: type = class_type @Int, @Int(%int_32) [concrete] // CHECK:STDOUT: %struct_type.a.b: type = struct_type {.a: %i32, .b: %i32} [concrete] // CHECK:STDOUT: %complete_type.705: = complete_type_witness %struct_type.a.b [concrete] // CHECK:STDOUT: %pattern_type.7d8: type = pattern_type %StructAdapter [concrete] // CHECK:STDOUT: %.ff5: Core.Form = init_form %i32 [concrete] // CHECK:STDOUT: %pattern_type.7ce: type = pattern_type %i32 [concrete] // CHECK:STDOUT: %F.type: type = fn_type @F [concrete] // CHECK:STDOUT: %F: %F.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: .Int = %Core.Int // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: %Core.Int: %Int.type = import_ref Core//prelude/parts/int, Int, loaded [concrete = constants.%Int.generic] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .StructAdapter = %StructAdapter.decl // CHECK:STDOUT: .F = %F.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %StructAdapter.decl: type = class_decl @StructAdapter [concrete = constants.%StructAdapter] {} {} // CHECK:STDOUT: %F.decl: %F.type = fn_decl @F [concrete = constants.%F] { // CHECK:STDOUT: %a.patt: %pattern_type.7d8 = value_binding_pattern a [concrete] // CHECK:STDOUT: %a.param_patt: %pattern_type.7d8 = value_param_pattern %a.patt [concrete] // CHECK:STDOUT: %return.patt: %pattern_type.7ce = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: %pattern_type.7ce = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %i32: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %.loc8: Core.Form = init_form %i32 [concrete = constants.%.ff5] // CHECK:STDOUT: %a.param: %StructAdapter = value_param call_param0 // CHECK:STDOUT: %StructAdapter.ref: type = name_ref StructAdapter, file.%StructAdapter.decl [concrete = constants.%StructAdapter] // CHECK:STDOUT: %a: %StructAdapter = value_binding a, %a.param // CHECK:STDOUT: %return.param: ref %i32 = out_param call_param1 // CHECK:STDOUT: %return: ref %i32 = return_slot %return.param // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @StructAdapter { // CHECK:STDOUT: %i32.loc5_21: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %i32.loc5_30: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %struct_type.a.b: type = struct_type {.a: %i32, .b: %i32} [concrete = constants.%struct_type.a.b] // CHECK:STDOUT: adapt_decl %struct_type.a.b [concrete] // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%struct_type.a.b [concrete = constants.%complete_type.705] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%StructAdapter // CHECK:STDOUT: .b = // CHECK:STDOUT: extend %struct_type.a.b // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @F(%a.param: %StructAdapter) -> out %return.param: %i32 { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %a.ref: %StructAdapter = name_ref a, %a // CHECK:STDOUT: %b.ref: = name_ref b, [concrete = ] // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- fail_todo_adapt_tuple.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %TupleAdapter: type = class_type @TupleAdapter [concrete] // CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [concrete] // CHECK:STDOUT: %Int.type: type = generic_class_type @Int [concrete] // CHECK:STDOUT: %Int.generic: %Int.type = struct_value () [concrete] // CHECK:STDOUT: %i32: type = class_type @Int, @Int(%int_32) [concrete] // CHECK:STDOUT: %tuple.type.24b: type = tuple_type (type, type) [concrete] // CHECK:STDOUT: %tuple: %tuple.type.24b = tuple_value (%i32, %i32) [concrete] // CHECK:STDOUT: %tuple.type.d07: type = tuple_type (%i32, %i32) [concrete] // CHECK:STDOUT: %complete_type.65d: = complete_type_witness %tuple.type.d07 [concrete] // CHECK:STDOUT: %pattern_type.3a8: type = pattern_type %TupleAdapter [concrete] // CHECK:STDOUT: %.ff5: Core.Form = init_form %i32 [concrete] // CHECK:STDOUT: %pattern_type.7ce: type = pattern_type %i32 [concrete] // CHECK:STDOUT: %F.type: type = fn_type @F [concrete] // CHECK:STDOUT: %F: %F.type = struct_value () [concrete] // CHECK:STDOUT: %int_1: Core.IntLiteral = int_value 1 [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: .Int = %Core.Int // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: %Core.Int: %Int.type = import_ref Core//prelude/parts/int, Int, loaded [concrete = constants.%Int.generic] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .TupleAdapter = %TupleAdapter.decl // CHECK:STDOUT: .F = %F.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %TupleAdapter.decl: type = class_decl @TupleAdapter [concrete = constants.%TupleAdapter] {} {} // CHECK:STDOUT: %F.decl: %F.type = fn_decl @F [concrete = constants.%F] { // CHECK:STDOUT: %a.patt: %pattern_type.3a8 = value_binding_pattern a [concrete] // CHECK:STDOUT: %a.param_patt: %pattern_type.3a8 = value_param_pattern %a.patt [concrete] // CHECK:STDOUT: %return.patt: %pattern_type.7ce = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: %pattern_type.7ce = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %i32: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %.loc8: Core.Form = init_form %i32 [concrete = constants.%.ff5] // CHECK:STDOUT: %a.param: %TupleAdapter = value_param call_param0 // CHECK:STDOUT: %TupleAdapter.ref: type = name_ref TupleAdapter, file.%TupleAdapter.decl [concrete = constants.%TupleAdapter] // CHECK:STDOUT: %a: %TupleAdapter = value_binding a, %a.param // CHECK:STDOUT: %return.param: ref %i32 = out_param call_param1 // CHECK:STDOUT: %return: ref %i32 = return_slot %return.param // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @TupleAdapter { // CHECK:STDOUT: %i32.loc5_17: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %i32.loc5_22: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %.loc5_25: %tuple.type.24b = tuple_literal (%i32.loc5_17, %i32.loc5_22) [concrete = constants.%tuple] // CHECK:STDOUT: %.loc5_26: type = converted %.loc5_25, constants.%tuple.type.d07 [concrete = constants.%tuple.type.d07] // CHECK:STDOUT: adapt_decl %.loc5_26 [concrete] // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%tuple.type.d07 [concrete = constants.%complete_type.65d] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%TupleAdapter // CHECK:STDOUT: extend %.loc5_26 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @F(%a.param: %TupleAdapter) -> out %return.param: %i32 { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %a.ref: %TupleAdapter = name_ref a, %a // CHECK:STDOUT: %int_1: Core.IntLiteral = int_value 1 [concrete = constants.%int_1] // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- fail_adapt_builtin.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %IntLiteral.type: type = fn_type @IntLiteral [concrete] // CHECK:STDOUT: %IntLiteral: %IntLiteral.type = struct_value () [concrete] // CHECK:STDOUT: %pattern_type.98f: type = pattern_type type [concrete] // CHECK:STDOUT: %.805: Core.Form = init_form type [concrete] // CHECK:STDOUT: %pattern_type.dc0: type = pattern_type Core.IntLiteral [concrete] // CHECK:STDOUT: %MakeInt.type: type = fn_type @MakeInt [concrete] // CHECK:STDOUT: %MakeInt: %MakeInt.type = struct_value () [concrete] // CHECK:STDOUT: %IntAdapter: type = class_type @IntAdapter [concrete] // CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [concrete] // CHECK:STDOUT: %i32.builtin: type = int_type signed, %int_32 [concrete] // CHECK:STDOUT: %complete_type.f8a: = complete_type_witness %i32.builtin [concrete] // CHECK:STDOUT: %pattern_type.f40: type = pattern_type %IntAdapter [concrete] // CHECK:STDOUT: %Int.type: type = generic_class_type @Int [concrete] // CHECK:STDOUT: %Int.generic: %Int.type = struct_value () [concrete] // CHECK:STDOUT: %i32: type = class_type @Int, @Int(%int_32) [concrete] // CHECK:STDOUT: %.ff5: Core.Form = init_form %i32 [concrete] // CHECK:STDOUT: %pattern_type.7ce: type = pattern_type %i32 [concrete] // CHECK:STDOUT: %F.type: type = fn_type @F [concrete] // CHECK:STDOUT: %F: %F.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: .IntLiteral = %Core.IntLiteral // CHECK:STDOUT: .Int = %Core.Int // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: %Core.IntLiteral: %IntLiteral.type = import_ref Core//prelude/parts/int_literal, IntLiteral, loaded [concrete = constants.%IntLiteral] // CHECK:STDOUT: %Core.Int: %Int.type = import_ref Core//prelude/parts/int, Int, loaded [concrete = constants.%Int.generic] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .MakeInt = %MakeInt.decl // CHECK:STDOUT: .IntAdapter = %IntAdapter.decl // CHECK:STDOUT: .F = %F.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %MakeInt.decl: %MakeInt.type = fn_decl @MakeInt [concrete = constants.%MakeInt] { // CHECK:STDOUT: %N.patt: %pattern_type.dc0 = value_binding_pattern N [concrete] // CHECK:STDOUT: %N.param_patt: %pattern_type.dc0 = value_param_pattern %N.patt [concrete] // CHECK:STDOUT: %return.patt: %pattern_type.98f = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: %pattern_type.98f = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %.loc4_37.1: type = type_literal type [concrete = type] // CHECK:STDOUT: %.loc4_37.2: Core.Form = init_form %.loc4_37.1 [concrete = constants.%.805] // CHECK:STDOUT: %N.param: Core.IntLiteral = value_param call_param0 // CHECK:STDOUT: %.loc4_31.1: type = splice_block %.loc4_31.3 [concrete = Core.IntLiteral] { // CHECK:STDOUT: %Core.ref: = name_ref Core, imports.%Core [concrete = imports.%Core] // CHECK:STDOUT: %IntLiteral.ref: %IntLiteral.type = name_ref IntLiteral, imports.%Core.IntLiteral [concrete = constants.%IntLiteral] // CHECK:STDOUT: %IntLiteral.call: init type = call %IntLiteral.ref() [concrete = Core.IntLiteral] // CHECK:STDOUT: %.loc4_31.2: type = value_of_initializer %IntLiteral.call [concrete = Core.IntLiteral] // CHECK:STDOUT: %.loc4_31.3: type = converted %IntLiteral.call, %.loc4_31.2 [concrete = Core.IntLiteral] // CHECK:STDOUT: } // CHECK:STDOUT: %N: Core.IntLiteral = value_binding N, %N.param // CHECK:STDOUT: %return.param: ref type = out_param call_param1 // CHECK:STDOUT: %return: ref type = return_slot %return.param // CHECK:STDOUT: } // CHECK:STDOUT: %IntAdapter.decl: type = class_decl @IntAdapter [concrete = constants.%IntAdapter] {} {} // CHECK:STDOUT: %F.decl: %F.type = fn_decl @F [concrete = constants.%F] { // CHECK:STDOUT: %a.patt: %pattern_type.f40 = value_binding_pattern a [concrete] // CHECK:STDOUT: %a.param_patt: %pattern_type.f40 = value_param_pattern %a.patt [concrete] // CHECK:STDOUT: %return.patt: %pattern_type.7ce = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: %pattern_type.7ce = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %i32: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %.loc10: Core.Form = init_form %i32 [concrete = constants.%.ff5] // CHECK:STDOUT: %a.param: %IntAdapter = value_param call_param0 // CHECK:STDOUT: %IntAdapter.ref: type = name_ref IntAdapter, file.%IntAdapter.decl [concrete = constants.%IntAdapter] // CHECK:STDOUT: %a: %IntAdapter = value_binding a, %a.param // CHECK:STDOUT: %return.param: ref %i32 = out_param call_param1 // CHECK:STDOUT: %return: ref %i32 = return_slot %return.param // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @IntAdapter { // CHECK:STDOUT: %MakeInt.ref: %MakeInt.type = name_ref MakeInt, file.%MakeInt.decl [concrete = constants.%MakeInt] // CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [concrete = constants.%int_32] // CHECK:STDOUT: %MakeInt.call: init type = call %MakeInt.ref(%int_32) [concrete = constants.%i32.builtin] // CHECK:STDOUT: %.loc7_27.1: type = value_of_initializer %MakeInt.call [concrete = constants.%i32.builtin] // CHECK:STDOUT: %.loc7_27.2: type = converted %MakeInt.call, %.loc7_27.1 [concrete = constants.%i32.builtin] // CHECK:STDOUT: adapt_decl %.loc7_27.2 [concrete] // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%i32.builtin [concrete = constants.%complete_type.f8a] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%IntAdapter // CHECK:STDOUT: .MakeInt = // CHECK:STDOUT: .foo = // CHECK:STDOUT: extend %.loc7_27.2 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @MakeInt(%N.param: Core.IntLiteral) -> out %return.param: type = "int.make_type_signed"; // CHECK:STDOUT: // CHECK:STDOUT: fn @F(%a.param: %IntAdapter) -> out %return.param: %i32 { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %a.ref: %IntAdapter = name_ref a, %a // CHECK:STDOUT: %foo.ref: = name_ref foo, [concrete = ] // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/class/adapter/fail_adapt_bad_decl.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/convert.carbon // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/class/adapter/fail_adapt_bad_decl.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/class/adapter/fail_adapt_bad_decl.carbon // --- fail_not_type.carbon library "[[@TEST_NAME]]"; class Bad { // CHECK:STDERR: fail_not_type.carbon:[[@LINE+7]]:3: error: cannot implicitly convert non-type value of type `Core.IntLiteral` to `type` [ConversionFailureNonTypeToFacet] // CHECK:STDERR: adapt 100; // CHECK:STDERR: ^~~~~~~~~~ // CHECK:STDERR: fail_not_type.carbon:[[@LINE+4]]:3: note: type `Core.IntLiteral` does not implement interface `Core.ImplicitAs(type)` [MissingImplInMemberAccessInContext] // CHECK:STDERR: adapt 100; // CHECK:STDERR: ^~~~~~~~~~ // CHECK:STDERR: adapt 100; } // CHECK:STDERR: fail_not_type.carbon:[[@LINE+4]]:18: error: member name `F` not found in `Bad` [MemberNameNotFoundInInstScope] // CHECK:STDERR: fn Use(b: Bad) { b.F(); } // CHECK:STDERR: ^~~ // CHECK:STDERR: fn Use(b: Bad) { b.F(); } // --- fail_extend_not_type.carbon library "[[@TEST_NAME]]"; class Bad { // CHECK:STDERR: fail_extend_not_type.carbon:[[@LINE+7]]:3: error: cannot implicitly convert non-type value of type `Core.IntLiteral` to `type` [ConversionFailureNonTypeToFacet] // CHECK:STDERR: extend adapt 100; // CHECK:STDERR: ^~~~~~~~~~~~~~~~~ // CHECK:STDERR: fail_extend_not_type.carbon:[[@LINE+4]]:3: note: type `Core.IntLiteral` does not implement interface `Core.ImplicitAs(type)` [MissingImplInMemberAccessInContext] // CHECK:STDERR: extend adapt 100; // CHECK:STDERR: ^~~~~~~~~~~~~~~~~ // CHECK:STDERR: extend adapt 100; } // No diagnostic here, we don't know what names Bad has. fn Use(b: Bad) { b.F(); } // --- fail_repeated.carbon library "[[@TEST_NAME]]"; class MultipleAdapts { adapt (); // CHECK:STDERR: fail_repeated.carbon:[[@LINE+7]]:3: error: multiple `adapt` declarations in class [AdaptDeclRepeated] // CHECK:STDERR: adapt {}; // CHECK:STDERR: ^~~~~~~~~ // CHECK:STDERR: fail_repeated.carbon:[[@LINE-4]]:3: note: previous `adapt` declaration is here [ClassSpecificDeclPrevious] // CHECK:STDERR: adapt (); // CHECK:STDERR: ^~~~~~~~~ // CHECK:STDERR: adapt {}; } class MultipleAdaptsSameType { adapt (); // CHECK:STDERR: fail_repeated.carbon:[[@LINE+7]]:3: error: multiple `adapt` declarations in class [AdaptDeclRepeated] // CHECK:STDERR: adapt (); // CHECK:STDERR: ^~~~~~~~~ // CHECK:STDERR: fail_repeated.carbon:[[@LINE-4]]:3: note: previous `adapt` declaration is here [ClassSpecificDeclPrevious] // CHECK:STDERR: adapt (); // CHECK:STDERR: ^~~~~~~~~ // CHECK:STDERR: adapt (); } // --- fail_bad_scope.carbon library "[[@TEST_NAME]]"; // CHECK:STDERR: fail_bad_scope.carbon:[[@LINE+4]]:1: error: `adapt` declaration outside class [ClassSpecificDeclOutsideClass] // CHECK:STDERR: adapt {}; // CHECK:STDERR: ^~~~~~~~~ // CHECK:STDERR: adapt {}; interface I { // CHECK:STDERR: fail_bad_scope.carbon:[[@LINE+4]]:3: error: `adapt` declaration outside class [ClassSpecificDeclOutsideClass] // CHECK:STDERR: adapt {}; // CHECK:STDERR: ^~~~~~~~~ // CHECK:STDERR: adapt {}; } class C { interface I { // CHECK:STDERR: fail_bad_scope.carbon:[[@LINE+4]]:5: error: `adapt` declaration outside class [ClassSpecificDeclOutsideClass] // CHECK:STDERR: adapt {}; // CHECK:STDERR: ^~~~~~~~~ // CHECK:STDERR: adapt {}; } } ================================================ FILE: toolchain/check/testdata/class/adapter/fail_adapt_bad_type.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/convert.carbon // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/class/adapter/fail_adapt_bad_type.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/class/adapter/fail_adapt_bad_type.carbon // --- fail_incomplete_type.carbon library "[[@TEST_NAME]]"; class Incomplete; class AdaptIncomplete { // CHECK:STDERR: fail_incomplete_type.carbon:[[@LINE+7]]:3: error: adapted type `Incomplete` is an incomplete type [IncompleteTypeInAdaptDecl] // CHECK:STDERR: adapt Incomplete; // CHECK:STDERR: ^~~~~~~~~~~~~~~~~ // CHECK:STDERR: fail_incomplete_type.carbon:[[@LINE-6]]:1: note: class was forward declared here [ClassForwardDeclaredHere] // CHECK:STDERR: class Incomplete; // CHECK:STDERR: ^~~~~~~~~~~~~~~~~ // CHECK:STDERR: adapt Incomplete; } ================================================ FILE: toolchain/check/testdata/class/adapter/fail_adapt_modifiers.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/convert.carbon // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/class/adapter/fail_adapt_modifiers.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/class/adapter/fail_adapt_modifiers.carbon class B {} class C1 { // CHECK:STDERR: fail_adapt_modifiers.carbon:[[@LINE+4]]:3: error: `private` not allowed on `adapt` declaration [ModifierNotAllowedOnDeclaration] // CHECK:STDERR: private adapt B; // CHECK:STDERR: ^~~~~~~ // CHECK:STDERR: private adapt B; } class C2 { // CHECK:STDERR: fail_adapt_modifiers.carbon:[[@LINE+4]]:3: error: `abstract` not allowed on `adapt` declaration [ModifierNotAllowedOnDeclaration] // CHECK:STDERR: abstract adapt B; // CHECK:STDERR: ^~~~~~~~ // CHECK:STDERR: abstract adapt B; } class C3 { // CHECK:STDERR: fail_adapt_modifiers.carbon:[[@LINE+4]]:3: error: `default` not allowed on `adapt` declaration [ModifierNotAllowedOnDeclaration] // CHECK:STDERR: default adapt B; // CHECK:STDERR: ^~~~~~~ // CHECK:STDERR: default adapt B; } class C4 { // CHECK:STDERR: fail_adapt_modifiers.carbon:[[@LINE+7]]:10: error: `extend` repeated on declaration [ModifierRepeated] // CHECK:STDERR: extend extend adapt B; // CHECK:STDERR: ^~~~~~ // CHECK:STDERR: fail_adapt_modifiers.carbon:[[@LINE+4]]:3: note: `extend` previously appeared here [ModifierPrevious] // CHECK:STDERR: extend extend adapt B; // CHECK:STDERR: ^~~~~~ // CHECK:STDERR: extend extend adapt B; } class C5 { // CHECK:STDERR: fail_adapt_modifiers.carbon:[[@LINE+4]]:3: error: `base` not allowed on `adapt` declaration [ModifierNotAllowedOnDeclaration] // CHECK:STDERR: base adapt B; // CHECK:STDERR: ^~~~ // CHECK:STDERR: base adapt B; } class C6 { // CHECK:STDERR: fail_adapt_modifiers.carbon:[[@LINE+4]]:10: error: `base` not allowed on `adapt` declaration [ModifierNotAllowedOnDeclaration] // CHECK:STDERR: extend base adapt B; // CHECK:STDERR: ^~~~ // CHECK:STDERR: extend base adapt B; } ================================================ FILE: toolchain/check/testdata/class/adapter/fail_adapt_with_base.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/convert.carbon // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/class/adapter/fail_adapt_with_base.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/class/adapter/fail_adapt_with_base.carbon class Simple {}; base class AdaptWithVirtual { virtual fn F[self: Self](); // CHECK:STDERR: fail_adapt_with_base.carbon:[[@LINE+7]]:3: error: adapter with virtual function [AdaptWithVirtual] // CHECK:STDERR: adapt Simple; // CHECK:STDERR: ^~~~~~~~~~~~~ // CHECK:STDERR: fail_adapt_with_base.carbon:[[@LINE-4]]:3: note: first virtual function declaration is here [AdaptWithVirtualHere] // CHECK:STDERR: virtual fn F[self: Self](); // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: adapt Simple; } ================================================ FILE: toolchain/check/testdata/class/adapter/fail_adapt_with_subobjects.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/int.carbon // TODO: Add ranges and switch to "--dump-sem-ir-ranges=only". // EXTRA-ARGS: --dump-sem-ir-ranges=if-present // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/class/adapter/fail_adapt_with_subobjects.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/class/adapter/fail_adapt_with_subobjects.carbon // --- fail_adapt_with_base.carbon library "[[@TEST_NAME]]"; base class Base {} class AdaptWithBase { // CHECK:STDERR: fail_adapt_with_base.carbon:[[@LINE+3]]:3: error: adapter with base class [AdaptWithBase] // CHECK:STDERR: adapt i32; // CHECK:STDERR: ^~~~~~~~~~ adapt i32; // CHECK:STDERR: fail_adapt_with_base.carbon:[[@LINE+4]]:3: note: `base` declaration is here [AdaptWithBaseHere] // CHECK:STDERR: extend base: Base; // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~ // CHECK:STDERR: extend base: Base; } // --- fail_adapt_with_fields.carbon library "[[@TEST_NAME]]"; class AdaptWithField { // CHECK:STDERR: fail_adapt_with_fields.carbon:[[@LINE+3]]:3: error: adapter with fields [AdaptWithFields] // CHECK:STDERR: adapt i32; // CHECK:STDERR: ^~~~~~~~~~ adapt i32; // CHECK:STDERR: fail_adapt_with_fields.carbon:[[@LINE+4]]:7: note: first field declaration is here [AdaptWithFieldHere] // CHECK:STDERR: var n: i32; // CHECK:STDERR: ^~~~~~ // CHECK:STDERR: var n: i32; } class AdaptWithFields { // CHECK:STDERR: fail_adapt_with_fields.carbon:[[@LINE+3]]:3: error: adapter with fields [AdaptWithFields] // CHECK:STDERR: adapt i32; // CHECK:STDERR: ^~~~~~~~~~ adapt i32; // CHECK:STDERR: fail_adapt_with_fields.carbon:[[@LINE+4]]:7: note: first field declaration is here [AdaptWithFieldHere] // CHECK:STDERR: var a: i32; // CHECK:STDERR: ^~~~~~ // CHECK:STDERR: var a: i32; var b: i32; var c: i32; } // --- fail_adapt_with_base_and_fields.carbon library "[[@TEST_NAME]]"; base class Base {} class AdaptWithBaseAndFields { extend base: Base; var n: i32; // CHECK:STDERR: fail_adapt_with_base_and_fields.carbon:[[@LINE+7]]:3: error: adapter with base class [AdaptWithBase] // CHECK:STDERR: adapt {}; // CHECK:STDERR: ^~~~~~~~~ // CHECK:STDERR: fail_adapt_with_base_and_fields.carbon:[[@LINE-5]]:3: note: `base` declaration is here [AdaptWithBaseHere] // CHECK:STDERR: extend base: Base; // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~ // CHECK:STDERR: adapt {}; } // CHECK:STDOUT: --- fail_adapt_with_base.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %Base: type = class_type @Base [concrete] // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %complete_type.357: = complete_type_witness %empty_struct_type [concrete] // CHECK:STDOUT: %AdaptWithBase: type = class_type @AdaptWithBase [concrete] // CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [concrete] // CHECK:STDOUT: %Int.type: type = generic_class_type @Int [concrete] // CHECK:STDOUT: %Int.generic: %Int.type = struct_value () [concrete] // CHECK:STDOUT: %i32: type = class_type @Int, @Int(%int_32) [concrete] // CHECK:STDOUT: %AdaptWithBase.elem: type = unbound_element_type %AdaptWithBase, %Base [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: .Int = %Core.Int // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: %Core.Int: %Int.type = import_ref Core//prelude/parts/int, Int, loaded [concrete = constants.%Int.generic] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .Base = %Base.decl // CHECK:STDOUT: .AdaptWithBase = %AdaptWithBase.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %Base.decl: type = class_decl @Base [concrete = constants.%Base] {} {} // CHECK:STDOUT: %AdaptWithBase.decl: type = class_decl @AdaptWithBase [concrete = constants.%AdaptWithBase] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @Base { // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type.357] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%Base // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @AdaptWithBase { // CHECK:STDOUT: %i32: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: adapt_decl %i32 [concrete] // CHECK:STDOUT: %Base.ref: type = name_ref Base, file.%Base.decl [concrete = constants.%Base] // CHECK:STDOUT: %.loc15: %AdaptWithBase.elem = base_decl %Base.ref, element [concrete] // CHECK:STDOUT: complete_type_witness = // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%AdaptWithBase // CHECK:STDOUT: .Base = // CHECK:STDOUT: .base = %.loc15 // CHECK:STDOUT: extend %Base.ref // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- fail_adapt_with_fields.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %AdaptWithField: type = class_type @AdaptWithField [concrete] // CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [concrete] // CHECK:STDOUT: %Int.type: type = generic_class_type @Int [concrete] // CHECK:STDOUT: %Int.generic: %Int.type = struct_value () [concrete] // CHECK:STDOUT: %i32: type = class_type @Int, @Int(%int_32) [concrete] // CHECK:STDOUT: %AdaptWithField.elem: type = unbound_element_type %AdaptWithField, %i32 [concrete] // CHECK:STDOUT: %AdaptWithFields: type = class_type @AdaptWithFields [concrete] // CHECK:STDOUT: %AdaptWithFields.elem: type = unbound_element_type %AdaptWithFields, %i32 [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: .Int = %Core.Int // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: %Core.Int: %Int.type = import_ref Core//prelude/parts/int, Int, loaded [concrete = constants.%Int.generic] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .AdaptWithField = %AdaptWithField.decl // CHECK:STDOUT: .AdaptWithFields = %AdaptWithFields.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %AdaptWithField.decl: type = class_decl @AdaptWithField [concrete = constants.%AdaptWithField] {} {} // CHECK:STDOUT: %AdaptWithFields.decl: type = class_decl @AdaptWithFields [concrete = constants.%AdaptWithFields] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @AdaptWithField { // CHECK:STDOUT: %i32.loc8: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: adapt_decl %i32.loc8 [concrete] // CHECK:STDOUT: %i32.loc13: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %.loc13: %AdaptWithField.elem = field_decl n, element [concrete] // CHECK:STDOUT: complete_type_witness = // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%AdaptWithField // CHECK:STDOUT: .n = %.loc13 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @AdaptWithFields { // CHECK:STDOUT: %i32.loc20: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: adapt_decl %i32.loc20 [concrete] // CHECK:STDOUT: %i32.loc25: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %.loc25: %AdaptWithFields.elem = field_decl a, element [concrete] // CHECK:STDOUT: %i32.loc26: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %.loc26: %AdaptWithFields.elem = field_decl b, element [concrete] // CHECK:STDOUT: %i32.loc27: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %.loc27: %AdaptWithFields.elem = field_decl c, element [concrete] // CHECK:STDOUT: complete_type_witness = // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%AdaptWithFields // CHECK:STDOUT: .a = %.loc25 // CHECK:STDOUT: .b = %.loc26 // CHECK:STDOUT: .c = %.loc27 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- fail_adapt_with_base_and_fields.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %Base: type = class_type @Base [concrete] // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %complete_type.357: = complete_type_witness %empty_struct_type [concrete] // CHECK:STDOUT: %AdaptWithBaseAndFields: type = class_type @AdaptWithBaseAndFields [concrete] // CHECK:STDOUT: %AdaptWithBaseAndFields.elem.43f: type = unbound_element_type %AdaptWithBaseAndFields, %Base [concrete] // CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [concrete] // CHECK:STDOUT: %Int.type: type = generic_class_type @Int [concrete] // CHECK:STDOUT: %Int.generic: %Int.type = struct_value () [concrete] // CHECK:STDOUT: %i32: type = class_type @Int, @Int(%int_32) [concrete] // CHECK:STDOUT: %AdaptWithBaseAndFields.elem.37a: type = unbound_element_type %AdaptWithBaseAndFields, %i32 [concrete] // CHECK:STDOUT: %empty_struct: %empty_struct_type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: .Int = %Core.Int // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: %Core.Int: %Int.type = import_ref Core//prelude/parts/int, Int, loaded [concrete = constants.%Int.generic] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .Base = %Base.decl // CHECK:STDOUT: .AdaptWithBaseAndFields = %AdaptWithBaseAndFields.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %Base.decl: type = class_decl @Base [concrete = constants.%Base] {} {} // CHECK:STDOUT: %AdaptWithBaseAndFields.decl: type = class_decl @AdaptWithBaseAndFields [concrete = constants.%AdaptWithBaseAndFields] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @Base { // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type.357] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%Base // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @AdaptWithBaseAndFields { // CHECK:STDOUT: %Base.ref: type = name_ref Base, file.%Base.decl [concrete = constants.%Base] // CHECK:STDOUT: %.loc7: %AdaptWithBaseAndFields.elem.43f = base_decl %Base.ref, element [concrete] // CHECK:STDOUT: %i32: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %.loc8: %AdaptWithBaseAndFields.elem.37a = field_decl n, element [concrete] // CHECK:STDOUT: %.loc16_10: %empty_struct_type = struct_literal () [concrete = constants.%empty_struct] // CHECK:STDOUT: %.loc16_11: type = converted %.loc16_10, constants.%empty_struct_type [concrete = constants.%empty_struct_type] // CHECK:STDOUT: adapt_decl %.loc16_11 [concrete] // CHECK:STDOUT: complete_type_witness = // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%AdaptWithBaseAndFields // CHECK:STDOUT: .Base = // CHECK:STDOUT: .base = %.loc7 // CHECK:STDOUT: .n = %.loc8 // CHECK:STDOUT: extend %Base.ref // CHECK:STDOUT: } // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/class/adapter/init_adapt.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/int.carbon // TODO: Add ranges and switch to "--dump-sem-ir-ranges=only". // EXTRA-ARGS: --dump-sem-ir-ranges=if-present // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/class/adapter/init_adapt.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/class/adapter/init_adapt.carbon // --- init_adapt.carbon library "[[@TEST_NAME]]"; class C { var a: i32; var b: i32; } class AdaptC { adapt C; } let a: C = {.a = 1, .b = 2}; let b: AdaptC = a as AdaptC; let c: C = b as C; fn MakeC() -> C; fn MakeAdaptC() -> AdaptC; var d: AdaptC = MakeC() as AdaptC; var e: C = MakeAdaptC() as C; // --- fail_not_implicit.carbon library "[[@TEST_NAME]]"; class C { var a: i32; var b: i32; } class AdaptC { adapt C; } let a: C = {.a = 1, .b = 2}; // Cannot implicitly convert between a type and an adapter for the type. // CHECK:STDERR: fail_not_implicit.carbon:[[@LINE+7]]:17: error: cannot implicitly convert expression of type `C` to `AdaptC` [ConversionFailure] // CHECK:STDERR: let b: AdaptC = a; // CHECK:STDERR: ^ // CHECK:STDERR: fail_not_implicit.carbon:[[@LINE+4]]:17: note: type `C` does not implement interface `Core.ImplicitAs(AdaptC)` [MissingImplInMemberAccessInContext] // CHECK:STDERR: let b: AdaptC = a; // CHECK:STDERR: ^ // CHECK:STDERR: let b: AdaptC = a; // CHECK:STDERR: fail_not_implicit.carbon:[[@LINE+7]]:12: error: cannot implicitly convert expression of type `AdaptC` to `C` [ConversionFailure] // CHECK:STDERR: let c: C = b; // CHECK:STDERR: ^ // CHECK:STDERR: fail_not_implicit.carbon:[[@LINE+4]]:12: note: type `AdaptC` does not implement interface `Core.ImplicitAs(C)` [MissingImplInMemberAccessInContext] // CHECK:STDERR: let c: C = b; // CHECK:STDERR: ^ // CHECK:STDERR: let c: C = b; fn MakeC() -> C; fn MakeAdaptC() -> AdaptC; // CHECK:STDERR: fail_not_implicit.carbon:[[@LINE+7]]:1: error: cannot implicitly convert expression of type `C` to `AdaptC` [ConversionFailure] // CHECK:STDERR: var d: AdaptC = MakeC(); // CHECK:STDERR: ^~~~~~~~~~~~~ // CHECK:STDERR: fail_not_implicit.carbon:[[@LINE+4]]:1: note: type `C` does not implement interface `Core.ImplicitAs(AdaptC)` [MissingImplInMemberAccessInContext] // CHECK:STDERR: var d: AdaptC = MakeC(); // CHECK:STDERR: ^~~~~~~~~~~~~ // CHECK:STDERR: var d: AdaptC = MakeC(); // CHECK:STDERR: fail_not_implicit.carbon:[[@LINE+7]]:1: error: cannot implicitly convert expression of type `AdaptC` to `C` [ConversionFailure] // CHECK:STDERR: var e: C = MakeAdaptC(); // CHECK:STDERR: ^~~~~~~~ // CHECK:STDERR: fail_not_implicit.carbon:[[@LINE+4]]:1: note: type `AdaptC` does not implement interface `Core.ImplicitAs(C)` [MissingImplInMemberAccessInContext] // CHECK:STDERR: var e: C = MakeAdaptC(); // CHECK:STDERR: ^~~~~~~~ // CHECK:STDERR: var e: C = MakeAdaptC(); // CHECK:STDOUT: --- init_adapt.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %C: type = class_type @C [concrete] // CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [concrete] // CHECK:STDOUT: %Int.type: type = generic_class_type @Int [concrete] // CHECK:STDOUT: %Int.generic: %Int.type = struct_value () [concrete] // CHECK:STDOUT: %i32: type = class_type @Int, @Int(%int_32) [concrete] // CHECK:STDOUT: %C.elem: type = unbound_element_type %C, %i32 [concrete] // CHECK:STDOUT: %struct_type.a.b.501: type = struct_type {.a: %i32, .b: %i32} [concrete] // CHECK:STDOUT: %complete_type.705: = complete_type_witness %struct_type.a.b.501 [concrete] // CHECK:STDOUT: %AdaptC: type = class_type @AdaptC [concrete] // CHECK:STDOUT: %pattern_type.7c7: type = pattern_type %C [concrete] // CHECK:STDOUT: %int_1.5b8: Core.IntLiteral = int_value 1 [concrete] // CHECK:STDOUT: %int_2.ecc: Core.IntLiteral = int_value 2 [concrete] // CHECK:STDOUT: %struct_type.a.b.cfd: type = struct_type {.a: Core.IntLiteral, .b: Core.IntLiteral} [concrete] // CHECK:STDOUT: %struct: %struct_type.a.b.cfd = struct_value (%int_1.5b8, %int_2.ecc) [concrete] // CHECK:STDOUT: %ImplicitAs.type.cc7: type = generic_interface_type @ImplicitAs [concrete] // CHECK:STDOUT: %ImplicitAs.generic: %ImplicitAs.type.cc7 = struct_value () [concrete] // CHECK:STDOUT: %ImplicitAs.type.e8c: type = facet_type <@ImplicitAs, @ImplicitAs(%i32)> [concrete] // CHECK:STDOUT: %To: Core.IntLiteral = symbolic_binding To, 0 [symbolic] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.4e6: type = fn_type @Core.IntLiteral.as.ImplicitAs.impl.Convert, @Core.IntLiteral.as.ImplicitAs.impl(%To) [symbolic] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.3c2: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.4e6 = struct_value () [symbolic] // CHECK:STDOUT: %ImplicitAs.impl_witness.6bc: = impl_witness imports.%ImplicitAs.impl_witness_table.74f, @Core.IntLiteral.as.ImplicitAs.impl(%int_32) [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.e0d: type = fn_type @Core.IntLiteral.as.ImplicitAs.impl.Convert, @Core.IntLiteral.as.ImplicitAs.impl(%int_32) [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.e0d = struct_value () [concrete] // CHECK:STDOUT: %ImplicitAs.facet: %ImplicitAs.type.e8c = facet_value Core.IntLiteral, (%ImplicitAs.impl_witness.6bc) [concrete] // CHECK:STDOUT: %ImplicitAs.WithSelf.Convert.type.b37: type = fn_type @ImplicitAs.WithSelf.Convert, @ImplicitAs.WithSelf(%i32, %ImplicitAs.facet) [concrete] // CHECK:STDOUT: %.545: type = fn_type_with_self_type %ImplicitAs.WithSelf.Convert.type.b37, %ImplicitAs.facet [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.bound.215: = bound_method %int_1.5b8, %Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5 [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn: = specific_function %Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5, @Core.IntLiteral.as.ImplicitAs.impl.Convert(%int_32) [concrete] // CHECK:STDOUT: %bound_method.38b: = bound_method %int_1.5b8, %Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn [concrete] // CHECK:STDOUT: %int_1.5d2: %i32 = int_value 1 [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.bound.4e5: = bound_method %int_2.ecc, %Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5 [concrete] // CHECK:STDOUT: %bound_method.646: = bound_method %int_2.ecc, %Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn [concrete] // CHECK:STDOUT: %int_2.ef8: %i32 = int_value 2 [concrete] // CHECK:STDOUT: %C.val: %C = struct_value (%int_1.5d2, %int_2.ef8) [concrete] // CHECK:STDOUT: %pattern_type.507: type = pattern_type %AdaptC [concrete] // CHECK:STDOUT: %.a69: Core.Form = init_form %C [concrete] // CHECK:STDOUT: %MakeC.type: type = fn_type @MakeC [concrete] // CHECK:STDOUT: %MakeC: %MakeC.type = struct_value () [concrete] // CHECK:STDOUT: %.e6b: Core.Form = init_form %AdaptC [concrete] // CHECK:STDOUT: %MakeAdaptC.type: type = fn_type @MakeAdaptC [concrete] // CHECK:STDOUT: %MakeAdaptC: %MakeAdaptC.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: .Int = %Core.Int // CHECK:STDOUT: .ImplicitAs = %Core.ImplicitAs // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: %Core.Int: %Int.type = import_ref Core//prelude/parts/int, Int, loaded [concrete = constants.%Int.generic] // CHECK:STDOUT: %Core.ImplicitAs: %ImplicitAs.type.cc7 = import_ref Core//prelude/parts/as, ImplicitAs, loaded [concrete = constants.%ImplicitAs.generic] // CHECK:STDOUT: %Core.import_ref.42d: @Core.IntLiteral.as.ImplicitAs.impl.%Core.IntLiteral.as.ImplicitAs.impl.Convert.type (%Core.IntLiteral.as.ImplicitAs.impl.Convert.type.4e6) = import_ref Core//prelude/parts/int, loc{{\d+_\d+}}, loaded [symbolic = @Core.IntLiteral.as.ImplicitAs.impl.%Core.IntLiteral.as.ImplicitAs.impl.Convert (constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.3c2)] // CHECK:STDOUT: %ImplicitAs.impl_witness_table.74f = impl_witness_table (%Core.import_ref.42d), @Core.IntLiteral.as.ImplicitAs.impl [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .C = %C.decl // CHECK:STDOUT: .AdaptC = %AdaptC.decl // CHECK:STDOUT: .a = %a // CHECK:STDOUT: .b = %b // CHECK:STDOUT: .c = %c // CHECK:STDOUT: .MakeC = %MakeC.decl // CHECK:STDOUT: .MakeAdaptC = %MakeAdaptC.decl // CHECK:STDOUT: .d = %d // CHECK:STDOUT: .e = %e // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %C.decl: type = class_decl @C [concrete = constants.%C] {} {} // CHECK:STDOUT: %AdaptC.decl: type = class_decl @AdaptC [concrete = constants.%AdaptC] {} {} // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %a.patt: %pattern_type.7c7 = value_binding_pattern a [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %C.ref.loc13: type = name_ref C, %C.decl [concrete = constants.%C] // CHECK:STDOUT: %impl.elem0.loc13_27.1: %.545 = impl_witness_access constants.%ImplicitAs.impl_witness.6bc, element0 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5] // CHECK:STDOUT: %bound_method.loc13_27.1: = bound_method @__global_init.%int_1, %impl.elem0.loc13_27.1 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.bound.215] // CHECK:STDOUT: %specific_fn.loc13_27.1: = specific_function %impl.elem0.loc13_27.1, @Core.IntLiteral.as.ImplicitAs.impl.Convert(constants.%int_32) [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn] // CHECK:STDOUT: %bound_method.loc13_27.2: = bound_method @__global_init.%int_1, %specific_fn.loc13_27.1 [concrete = constants.%bound_method.38b] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc13_27.1: init %i32 = call %bound_method.loc13_27.2(@__global_init.%int_1) [concrete = constants.%int_1.5d2] // CHECK:STDOUT: %.loc13_27.1: init %i32 = converted @__global_init.%int_1, %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc13_27.1 [concrete = constants.%int_1.5d2] // CHECK:STDOUT: %.loc13_27.2: ref %C = temporary_storage // CHECK:STDOUT: %.loc13_27.3: ref %i32 = class_element_access %.loc13_27.2, element0 // CHECK:STDOUT: %.loc13_27.4: init %i32 to %.loc13_27.3 = in_place_init %.loc13_27.1 [concrete = constants.%int_1.5d2] // CHECK:STDOUT: %impl.elem0.loc13_27.2: %.545 = impl_witness_access constants.%ImplicitAs.impl_witness.6bc, element0 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5] // CHECK:STDOUT: %bound_method.loc13_27.3: = bound_method @__global_init.%int_2, %impl.elem0.loc13_27.2 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.bound.4e5] // CHECK:STDOUT: %specific_fn.loc13_27.2: = specific_function %impl.elem0.loc13_27.2, @Core.IntLiteral.as.ImplicitAs.impl.Convert(constants.%int_32) [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn] // CHECK:STDOUT: %bound_method.loc13_27.4: = bound_method @__global_init.%int_2, %specific_fn.loc13_27.2 [concrete = constants.%bound_method.646] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc13_27.2: init %i32 = call %bound_method.loc13_27.4(@__global_init.%int_2) [concrete = constants.%int_2.ef8] // CHECK:STDOUT: %.loc13_27.5: init %i32 = converted @__global_init.%int_2, %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc13_27.2 [concrete = constants.%int_2.ef8] // CHECK:STDOUT: %.loc13_27.6: ref %i32 = class_element_access %.loc13_27.2, element1 // CHECK:STDOUT: %.loc13_27.7: init %i32 to %.loc13_27.6 = in_place_init %.loc13_27.5 [concrete = constants.%int_2.ef8] // CHECK:STDOUT: %.loc13_27.8: init %C to %.loc13_27.2 = class_init (%.loc13_27.4, %.loc13_27.7) [concrete = constants.%C.val] // CHECK:STDOUT: %.loc13_27.9: init %C = converted @__global_init.%.loc13, %.loc13_27.8 [concrete = constants.%C.val] // CHECK:STDOUT: %.loc13_27.10: ref %C = temporary %.loc13_27.2, %.loc13_27.9 // CHECK:STDOUT: %.loc13_27.11: %C = acquire_value %.loc13_27.10 // CHECK:STDOUT: %a: %C = value_binding a, %.loc13_27.11 // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %b.patt: %pattern_type.507 = value_binding_pattern b [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %AdaptC.ref.loc15: type = name_ref AdaptC, %AdaptC.decl [concrete = constants.%AdaptC] // CHECK:STDOUT: %b: %AdaptC = value_binding b, @__global_init.%.loc15_19.2 // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %c.patt: %pattern_type.7c7 = value_binding_pattern c [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %C.ref.loc17: type = name_ref C, %C.decl [concrete = constants.%C] // CHECK:STDOUT: %c: %C = value_binding c, @__global_init.%.loc17_14.2 // CHECK:STDOUT: %MakeC.decl: %MakeC.type = fn_decl @MakeC [concrete = constants.%MakeC] { // CHECK:STDOUT: %return.patt: %pattern_type.7c7 = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: %pattern_type.7c7 = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %C.ref: type = name_ref C, file.%C.decl [concrete = constants.%C] // CHECK:STDOUT: %.loc19: Core.Form = init_form %C.ref [concrete = constants.%.a69] // CHECK:STDOUT: %return.param: ref %C = out_param call_param0 // CHECK:STDOUT: %return: ref %C = return_slot %return.param // CHECK:STDOUT: } // CHECK:STDOUT: %MakeAdaptC.decl: %MakeAdaptC.type = fn_decl @MakeAdaptC [concrete = constants.%MakeAdaptC] { // CHECK:STDOUT: %return.patt: %pattern_type.507 = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: %pattern_type.507 = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %AdaptC.ref: type = name_ref AdaptC, file.%AdaptC.decl [concrete = constants.%AdaptC] // CHECK:STDOUT: %.loc21: Core.Form = init_form %AdaptC.ref [concrete = constants.%.e6b] // CHECK:STDOUT: %return.param: ref %AdaptC = out_param call_param0 // CHECK:STDOUT: %return: ref %AdaptC = return_slot %return.param // CHECK:STDOUT: } // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %d.patt: %pattern_type.507 = ref_binding_pattern d [concrete] // CHECK:STDOUT: %d.var_patt: %pattern_type.507 = var_pattern %d.patt [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %d.var: ref %AdaptC = var %d.var_patt [concrete] // CHECK:STDOUT: %AdaptC.ref.loc23: type = name_ref AdaptC, %AdaptC.decl [concrete = constants.%AdaptC] // CHECK:STDOUT: %d: ref %AdaptC = ref_binding d, %d.var [concrete = %d.var] // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %e.patt: %pattern_type.7c7 = ref_binding_pattern e [concrete] // CHECK:STDOUT: %e.var_patt: %pattern_type.7c7 = var_pattern %e.patt [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %e.var: ref %C = var %e.var_patt [concrete] // CHECK:STDOUT: %C.ref.loc25: type = name_ref C, %C.decl [concrete = constants.%C] // CHECK:STDOUT: %e: ref %C = ref_binding e, %e.var [concrete = %e.var] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @C { // CHECK:STDOUT: %i32.loc5: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %.loc5: %C.elem = field_decl a, element0 [concrete] // CHECK:STDOUT: %i32.loc6: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %.loc6: %C.elem = field_decl b, element1 [concrete] // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%struct_type.a.b.501 [concrete = constants.%complete_type.705] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%C // CHECK:STDOUT: .a = %.loc5 // CHECK:STDOUT: .b = %.loc6 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @AdaptC { // CHECK:STDOUT: %C.ref: type = name_ref C, file.%C.decl [concrete = constants.%C] // CHECK:STDOUT: adapt_decl %C.ref [concrete] // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%struct_type.a.b.501 [concrete = constants.%complete_type.705] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%AdaptC // CHECK:STDOUT: .C = // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @MakeC() -> out %return.param: %C; // CHECK:STDOUT: // CHECK:STDOUT: fn @MakeAdaptC() -> out %return.param: %AdaptC; // CHECK:STDOUT: // CHECK:STDOUT: fn @__global_init() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %int_1: Core.IntLiteral = int_value 1 [concrete = constants.%int_1.5b8] // CHECK:STDOUT: %int_2: Core.IntLiteral = int_value 2 [concrete = constants.%int_2.ecc] // CHECK:STDOUT: %.loc13: %struct_type.a.b.cfd = struct_literal (%int_1, %int_2) [concrete = constants.%struct] // CHECK:STDOUT: %a.ref: %C = name_ref a, file.%a // CHECK:STDOUT: %AdaptC.ref.loc15: type = name_ref AdaptC, file.%AdaptC.decl [concrete = constants.%AdaptC] // CHECK:STDOUT: %.loc15_19.1: %AdaptC = as_compatible %a.ref // CHECK:STDOUT: %.loc15_19.2: %AdaptC = converted %a.ref, %.loc15_19.1 // CHECK:STDOUT: %b.ref: %AdaptC = name_ref b, file.%b // CHECK:STDOUT: %C.ref.loc17: type = name_ref C, file.%C.decl [concrete = constants.%C] // CHECK:STDOUT: %.loc17_14.1: %C = as_compatible %b.ref // CHECK:STDOUT: %.loc17_14.2: %C = converted %b.ref, %.loc17_14.1 // CHECK:STDOUT: %MakeC.ref: %MakeC.type = name_ref MakeC, file.%MakeC.decl [concrete = constants.%MakeC] // CHECK:STDOUT: %.loc23_1: ref %AdaptC = splice_block file.%d.var [concrete = file.%d.var] {} // CHECK:STDOUT: %MakeC.call: init %C to %.loc23_1 = call %MakeC.ref() // CHECK:STDOUT: %AdaptC.ref.loc23: type = name_ref AdaptC, file.%AdaptC.decl [concrete = constants.%AdaptC] // CHECK:STDOUT: %.loc23_25.1: init %AdaptC = as_compatible %MakeC.call // CHECK:STDOUT: %.loc23_25.2: init %AdaptC = converted %MakeC.call, %.loc23_25.1 // CHECK:STDOUT: assign file.%d.var, %.loc23_25.2 // CHECK:STDOUT: %MakeAdaptC.ref: %MakeAdaptC.type = name_ref MakeAdaptC, file.%MakeAdaptC.decl [concrete = constants.%MakeAdaptC] // CHECK:STDOUT: %.loc25_1: ref %C = splice_block file.%e.var [concrete = file.%e.var] {} // CHECK:STDOUT: %MakeAdaptC.call: init %AdaptC to %.loc25_1 = call %MakeAdaptC.ref() // CHECK:STDOUT: %C.ref.loc25: type = name_ref C, file.%C.decl [concrete = constants.%C] // CHECK:STDOUT: %.loc25_25.1: init %C = as_compatible %MakeAdaptC.call // CHECK:STDOUT: %.loc25_25.2: init %C = converted %MakeAdaptC.call, %.loc25_25.1 // CHECK:STDOUT: assign file.%e.var, %.loc25_25.2 // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- fail_not_implicit.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %C: type = class_type @C [concrete] // CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [concrete] // CHECK:STDOUT: %Int.type: type = generic_class_type @Int [concrete] // CHECK:STDOUT: %Int.generic: %Int.type = struct_value () [concrete] // CHECK:STDOUT: %i32: type = class_type @Int, @Int(%int_32) [concrete] // CHECK:STDOUT: %C.elem: type = unbound_element_type %C, %i32 [concrete] // CHECK:STDOUT: %struct_type.a.b.501: type = struct_type {.a: %i32, .b: %i32} [concrete] // CHECK:STDOUT: %complete_type.705: = complete_type_witness %struct_type.a.b.501 [concrete] // CHECK:STDOUT: %AdaptC: type = class_type @AdaptC [concrete] // CHECK:STDOUT: %pattern_type.7c7: type = pattern_type %C [concrete] // CHECK:STDOUT: %int_1.5b8: Core.IntLiteral = int_value 1 [concrete] // CHECK:STDOUT: %int_2.ecc: Core.IntLiteral = int_value 2 [concrete] // CHECK:STDOUT: %struct_type.a.b.cfd: type = struct_type {.a: Core.IntLiteral, .b: Core.IntLiteral} [concrete] // CHECK:STDOUT: %struct: %struct_type.a.b.cfd = struct_value (%int_1.5b8, %int_2.ecc) [concrete] // CHECK:STDOUT: %ImplicitAs.type.cc7: type = generic_interface_type @ImplicitAs [concrete] // CHECK:STDOUT: %ImplicitAs.generic: %ImplicitAs.type.cc7 = struct_value () [concrete] // CHECK:STDOUT: %ImplicitAs.type.e8c: type = facet_type <@ImplicitAs, @ImplicitAs(%i32)> [concrete] // CHECK:STDOUT: %To: Core.IntLiteral = symbolic_binding To, 0 [symbolic] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.4e6: type = fn_type @Core.IntLiteral.as.ImplicitAs.impl.Convert, @Core.IntLiteral.as.ImplicitAs.impl(%To) [symbolic] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.3c2: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.4e6 = struct_value () [symbolic] // CHECK:STDOUT: %ImplicitAs.impl_witness.6bc: = impl_witness imports.%ImplicitAs.impl_witness_table.74f, @Core.IntLiteral.as.ImplicitAs.impl(%int_32) [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.e0d: type = fn_type @Core.IntLiteral.as.ImplicitAs.impl.Convert, @Core.IntLiteral.as.ImplicitAs.impl(%int_32) [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.e0d = struct_value () [concrete] // CHECK:STDOUT: %ImplicitAs.facet: %ImplicitAs.type.e8c = facet_value Core.IntLiteral, (%ImplicitAs.impl_witness.6bc) [concrete] // CHECK:STDOUT: %ImplicitAs.WithSelf.Convert.type.b37: type = fn_type @ImplicitAs.WithSelf.Convert, @ImplicitAs.WithSelf(%i32, %ImplicitAs.facet) [concrete] // CHECK:STDOUT: %.545: type = fn_type_with_self_type %ImplicitAs.WithSelf.Convert.type.b37, %ImplicitAs.facet [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.bound.215: = bound_method %int_1.5b8, %Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5 [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn: = specific_function %Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5, @Core.IntLiteral.as.ImplicitAs.impl.Convert(%int_32) [concrete] // CHECK:STDOUT: %bound_method.38b: = bound_method %int_1.5b8, %Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn [concrete] // CHECK:STDOUT: %int_1.5d2: %i32 = int_value 1 [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.bound.4e5: = bound_method %int_2.ecc, %Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5 [concrete] // CHECK:STDOUT: %bound_method.646: = bound_method %int_2.ecc, %Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn [concrete] // CHECK:STDOUT: %int_2.ef8: %i32 = int_value 2 [concrete] // CHECK:STDOUT: %C.val: %C = struct_value (%int_1.5d2, %int_2.ef8) [concrete] // CHECK:STDOUT: %pattern_type.507: type = pattern_type %AdaptC [concrete] // CHECK:STDOUT: %.a69: Core.Form = init_form %C [concrete] // CHECK:STDOUT: %MakeC.type: type = fn_type @MakeC [concrete] // CHECK:STDOUT: %MakeC: %MakeC.type = struct_value () [concrete] // CHECK:STDOUT: %.e6b: Core.Form = init_form %AdaptC [concrete] // CHECK:STDOUT: %MakeAdaptC.type: type = fn_type @MakeAdaptC [concrete] // CHECK:STDOUT: %MakeAdaptC: %MakeAdaptC.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: .Int = %Core.Int // CHECK:STDOUT: .ImplicitAs = %Core.ImplicitAs // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: %Core.Int: %Int.type = import_ref Core//prelude/parts/int, Int, loaded [concrete = constants.%Int.generic] // CHECK:STDOUT: %Core.ImplicitAs: %ImplicitAs.type.cc7 = import_ref Core//prelude/parts/as, ImplicitAs, loaded [concrete = constants.%ImplicitAs.generic] // CHECK:STDOUT: %Core.import_ref.42d: @Core.IntLiteral.as.ImplicitAs.impl.%Core.IntLiteral.as.ImplicitAs.impl.Convert.type (%Core.IntLiteral.as.ImplicitAs.impl.Convert.type.4e6) = import_ref Core//prelude/parts/int, loc{{\d+_\d+}}, loaded [symbolic = @Core.IntLiteral.as.ImplicitAs.impl.%Core.IntLiteral.as.ImplicitAs.impl.Convert (constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.3c2)] // CHECK:STDOUT: %ImplicitAs.impl_witness_table.74f = impl_witness_table (%Core.import_ref.42d), @Core.IntLiteral.as.ImplicitAs.impl [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .C = %C.decl // CHECK:STDOUT: .AdaptC = %AdaptC.decl // CHECK:STDOUT: .a = %a // CHECK:STDOUT: .b = %b // CHECK:STDOUT: .c = %c // CHECK:STDOUT: .MakeC = %MakeC.decl // CHECK:STDOUT: .MakeAdaptC = %MakeAdaptC.decl // CHECK:STDOUT: .d = %d // CHECK:STDOUT: .e = %e // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %C.decl: type = class_decl @C [concrete = constants.%C] {} {} // CHECK:STDOUT: %AdaptC.decl: type = class_decl @AdaptC [concrete = constants.%AdaptC] {} {} // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %a.patt: %pattern_type.7c7 = value_binding_pattern a [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %C.ref.loc13: type = name_ref C, %C.decl [concrete = constants.%C] // CHECK:STDOUT: %impl.elem0.loc13_27.1: %.545 = impl_witness_access constants.%ImplicitAs.impl_witness.6bc, element0 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5] // CHECK:STDOUT: %bound_method.loc13_27.1: = bound_method @__global_init.%int_1, %impl.elem0.loc13_27.1 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.bound.215] // CHECK:STDOUT: %specific_fn.loc13_27.1: = specific_function %impl.elem0.loc13_27.1, @Core.IntLiteral.as.ImplicitAs.impl.Convert(constants.%int_32) [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn] // CHECK:STDOUT: %bound_method.loc13_27.2: = bound_method @__global_init.%int_1, %specific_fn.loc13_27.1 [concrete = constants.%bound_method.38b] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc13_27.1: init %i32 = call %bound_method.loc13_27.2(@__global_init.%int_1) [concrete = constants.%int_1.5d2] // CHECK:STDOUT: %.loc13_27.1: init %i32 = converted @__global_init.%int_1, %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc13_27.1 [concrete = constants.%int_1.5d2] // CHECK:STDOUT: %.loc13_27.2: ref %C = temporary_storage // CHECK:STDOUT: %.loc13_27.3: ref %i32 = class_element_access %.loc13_27.2, element0 // CHECK:STDOUT: %.loc13_27.4: init %i32 to %.loc13_27.3 = in_place_init %.loc13_27.1 [concrete = constants.%int_1.5d2] // CHECK:STDOUT: %impl.elem0.loc13_27.2: %.545 = impl_witness_access constants.%ImplicitAs.impl_witness.6bc, element0 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5] // CHECK:STDOUT: %bound_method.loc13_27.3: = bound_method @__global_init.%int_2, %impl.elem0.loc13_27.2 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.bound.4e5] // CHECK:STDOUT: %specific_fn.loc13_27.2: = specific_function %impl.elem0.loc13_27.2, @Core.IntLiteral.as.ImplicitAs.impl.Convert(constants.%int_32) [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn] // CHECK:STDOUT: %bound_method.loc13_27.4: = bound_method @__global_init.%int_2, %specific_fn.loc13_27.2 [concrete = constants.%bound_method.646] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc13_27.2: init %i32 = call %bound_method.loc13_27.4(@__global_init.%int_2) [concrete = constants.%int_2.ef8] // CHECK:STDOUT: %.loc13_27.5: init %i32 = converted @__global_init.%int_2, %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc13_27.2 [concrete = constants.%int_2.ef8] // CHECK:STDOUT: %.loc13_27.6: ref %i32 = class_element_access %.loc13_27.2, element1 // CHECK:STDOUT: %.loc13_27.7: init %i32 to %.loc13_27.6 = in_place_init %.loc13_27.5 [concrete = constants.%int_2.ef8] // CHECK:STDOUT: %.loc13_27.8: init %C to %.loc13_27.2 = class_init (%.loc13_27.4, %.loc13_27.7) [concrete = constants.%C.val] // CHECK:STDOUT: %.loc13_27.9: init %C = converted @__global_init.%.loc13, %.loc13_27.8 [concrete = constants.%C.val] // CHECK:STDOUT: %.loc13_27.10: ref %C = temporary %.loc13_27.2, %.loc13_27.9 // CHECK:STDOUT: %.loc13_27.11: %C = acquire_value %.loc13_27.10 // CHECK:STDOUT: %a: %C = value_binding a, %.loc13_27.11 // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %b.patt: %pattern_type.507 = value_binding_pattern b [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %AdaptC.ref.loc24: type = name_ref AdaptC, %AdaptC.decl [concrete = constants.%AdaptC] // CHECK:STDOUT: %.loc24: %AdaptC = converted @__global_init.%a.ref, [concrete = ] // CHECK:STDOUT: %b: %AdaptC = value_binding b, [concrete = ] // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %c.patt: %pattern_type.7c7 = value_binding_pattern c [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %C.ref.loc33: type = name_ref C, %C.decl [concrete = constants.%C] // CHECK:STDOUT: %.loc33: %C = converted @__global_init.%b.ref, [concrete = ] // CHECK:STDOUT: %c: %C = value_binding c, [concrete = ] // CHECK:STDOUT: %MakeC.decl: %MakeC.type = fn_decl @MakeC [concrete = constants.%MakeC] { // CHECK:STDOUT: %return.patt: %pattern_type.7c7 = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: %pattern_type.7c7 = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %C.ref: type = name_ref C, file.%C.decl [concrete = constants.%C] // CHECK:STDOUT: %.loc35: Core.Form = init_form %C.ref [concrete = constants.%.a69] // CHECK:STDOUT: %return.param: ref %C = out_param call_param0 // CHECK:STDOUT: %return: ref %C = return_slot %return.param // CHECK:STDOUT: } // CHECK:STDOUT: %MakeAdaptC.decl: %MakeAdaptC.type = fn_decl @MakeAdaptC [concrete = constants.%MakeAdaptC] { // CHECK:STDOUT: %return.patt: %pattern_type.507 = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: %pattern_type.507 = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %AdaptC.ref: type = name_ref AdaptC, file.%AdaptC.decl [concrete = constants.%AdaptC] // CHECK:STDOUT: %.loc37: Core.Form = init_form %AdaptC.ref [concrete = constants.%.e6b] // CHECK:STDOUT: %return.param: ref %AdaptC = out_param call_param0 // CHECK:STDOUT: %return: ref %AdaptC = return_slot %return.param // CHECK:STDOUT: } // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %d.patt: %pattern_type.507 = ref_binding_pattern d [concrete] // CHECK:STDOUT: %d.var_patt: %pattern_type.507 = var_pattern %d.patt [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %d.var: ref %AdaptC = var %d.var_patt [concrete] // CHECK:STDOUT: %AdaptC.ref.loc46: type = name_ref AdaptC, %AdaptC.decl [concrete = constants.%AdaptC] // CHECK:STDOUT: %d: ref %AdaptC = ref_binding d, %d.var [concrete = %d.var] // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %e.patt: %pattern_type.7c7 = ref_binding_pattern e [concrete] // CHECK:STDOUT: %e.var_patt: %pattern_type.7c7 = var_pattern %e.patt [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %e.var: ref %C = var %e.var_patt [concrete] // CHECK:STDOUT: %C.ref.loc55: type = name_ref C, %C.decl [concrete = constants.%C] // CHECK:STDOUT: %e: ref %C = ref_binding e, %e.var [concrete = %e.var] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @C { // CHECK:STDOUT: %i32.loc5: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %.loc5: %C.elem = field_decl a, element0 [concrete] // CHECK:STDOUT: %i32.loc6: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %.loc6: %C.elem = field_decl b, element1 [concrete] // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%struct_type.a.b.501 [concrete = constants.%complete_type.705] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%C // CHECK:STDOUT: .a = %.loc5 // CHECK:STDOUT: .b = %.loc6 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @AdaptC { // CHECK:STDOUT: %C.ref: type = name_ref C, file.%C.decl [concrete = constants.%C] // CHECK:STDOUT: adapt_decl %C.ref [concrete] // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%struct_type.a.b.501 [concrete = constants.%complete_type.705] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%AdaptC // CHECK:STDOUT: .C = // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @MakeC() -> out %return.param: %C; // CHECK:STDOUT: // CHECK:STDOUT: fn @MakeAdaptC() -> out %return.param: %AdaptC; // CHECK:STDOUT: // CHECK:STDOUT: fn @__global_init() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %int_1: Core.IntLiteral = int_value 1 [concrete = constants.%int_1.5b8] // CHECK:STDOUT: %int_2: Core.IntLiteral = int_value 2 [concrete = constants.%int_2.ecc] // CHECK:STDOUT: %.loc13: %struct_type.a.b.cfd = struct_literal (%int_1, %int_2) [concrete = constants.%struct] // CHECK:STDOUT: %a.ref: %C = name_ref a, file.%a // CHECK:STDOUT: %b.ref: %AdaptC = name_ref b, file.%b [concrete = ] // CHECK:STDOUT: %MakeC.ref: %MakeC.type = name_ref MakeC, file.%MakeC.decl [concrete = constants.%MakeC] // CHECK:STDOUT: %.loc46_23: ref %C = temporary_storage // CHECK:STDOUT: %MakeC.call: init %C to %.loc46_23 = call %MakeC.ref() // CHECK:STDOUT: %.loc46_1: %AdaptC = converted %MakeC.call, [concrete = ] // CHECK:STDOUT: assign file.%d.var, // CHECK:STDOUT: %MakeAdaptC.ref: %MakeAdaptC.type = name_ref MakeAdaptC, file.%MakeAdaptC.decl [concrete = constants.%MakeAdaptC] // CHECK:STDOUT: %.loc55_23: ref %AdaptC = temporary_storage // CHECK:STDOUT: %MakeAdaptC.call: init %AdaptC to %.loc55_23 = call %MakeAdaptC.ref() // CHECK:STDOUT: %.loc55_1: %C = converted %MakeAdaptC.call, [concrete = ] // CHECK:STDOUT: assign file.%e.var, // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/class/basic.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/int.carbon // TODO: Add ranges and switch to "--dump-sem-ir-ranges=only". // EXTRA-ARGS: --dump-sem-ir-ranges=if-present // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/class/basic.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/class/basic.carbon class Class { fn F(n: i32) -> i32 { return n; } fn G(n: i32) -> i32; var k: i32; } fn Class.G(n: i32) -> i32 { return n; } fn Run() -> i32 { return Class.F(4); } // CHECK:STDOUT: --- basic.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %Class: type = class_type @Class [concrete] // CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [concrete] // CHECK:STDOUT: %Int.type: type = generic_class_type @Int [concrete] // CHECK:STDOUT: %Int.generic: %Int.type = struct_value () [concrete] // CHECK:STDOUT: %N: Core.IntLiteral = symbolic_binding N, 0 [symbolic] // CHECK:STDOUT: %i32: type = class_type @Int, @Int(%int_32) [concrete] // CHECK:STDOUT: %pattern_type.7ce: type = pattern_type %i32 [concrete] // CHECK:STDOUT: %.ff5: Core.Form = init_form %i32 [concrete] // CHECK:STDOUT: %Class.F.type: type = fn_type @Class.F [concrete] // CHECK:STDOUT: %Class.F: %Class.F.type = struct_value () [concrete] // CHECK:STDOUT: %Class.G.type: type = fn_type @Class.G [concrete] // CHECK:STDOUT: %Class.G: %Class.G.type = struct_value () [concrete] // CHECK:STDOUT: %Class.elem: type = unbound_element_type %Class, %i32 [concrete] // CHECK:STDOUT: %struct_type.k: type = struct_type {.k: %i32} [concrete] // CHECK:STDOUT: %complete_type.954: = complete_type_witness %struct_type.k [concrete] // CHECK:STDOUT: %Copy.type: type = facet_type <@Copy> [concrete] // CHECK:STDOUT: %Int.as.Copy.impl.Op.type.824: type = fn_type @Int.as.Copy.impl.Op, @Int.as.Copy.impl(%N) [symbolic] // CHECK:STDOUT: %Int.as.Copy.impl.Op.9b9: %Int.as.Copy.impl.Op.type.824 = struct_value () [symbolic] // CHECK:STDOUT: %Copy.impl_witness.f17: = impl_witness imports.%Copy.impl_witness_table.e76, @Int.as.Copy.impl(%int_32) [concrete] // CHECK:STDOUT: %Int.as.Copy.impl.Op.type.546: type = fn_type @Int.as.Copy.impl.Op, @Int.as.Copy.impl(%int_32) [concrete] // CHECK:STDOUT: %Int.as.Copy.impl.Op.664: %Int.as.Copy.impl.Op.type.546 = struct_value () [concrete] // CHECK:STDOUT: %Copy.facet: %Copy.type = facet_value %i32, (%Copy.impl_witness.f17) [concrete] // CHECK:STDOUT: %Copy.WithSelf.Op.type.081: type = fn_type @Copy.WithSelf.Op, @Copy.WithSelf(%Copy.facet) [concrete] // CHECK:STDOUT: %.8e2: type = fn_type_with_self_type %Copy.WithSelf.Op.type.081, %Copy.facet [concrete] // CHECK:STDOUT: %Int.as.Copy.impl.Op.specific_fn: = specific_function %Int.as.Copy.impl.Op.664, @Int.as.Copy.impl.Op(%int_32) [concrete] // CHECK:STDOUT: %Run.type: type = fn_type @Run [concrete] // CHECK:STDOUT: %Run: %Run.type = struct_value () [concrete] // CHECK:STDOUT: %int_4.0c1: Core.IntLiteral = int_value 4 [concrete] // CHECK:STDOUT: %ImplicitAs.type.cc7: type = generic_interface_type @ImplicitAs [concrete] // CHECK:STDOUT: %ImplicitAs.generic: %ImplicitAs.type.cc7 = struct_value () [concrete] // CHECK:STDOUT: %ImplicitAs.type.e8c: type = facet_type <@ImplicitAs, @ImplicitAs(%i32)> [concrete] // CHECK:STDOUT: %To: Core.IntLiteral = symbolic_binding To, 0 [symbolic] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.4e6: type = fn_type @Core.IntLiteral.as.ImplicitAs.impl.Convert, @Core.IntLiteral.as.ImplicitAs.impl(%To) [symbolic] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.3c2: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.4e6 = struct_value () [symbolic] // CHECK:STDOUT: %ImplicitAs.impl_witness.6bc: = impl_witness imports.%ImplicitAs.impl_witness_table.74f, @Core.IntLiteral.as.ImplicitAs.impl(%int_32) [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.e0d: type = fn_type @Core.IntLiteral.as.ImplicitAs.impl.Convert, @Core.IntLiteral.as.ImplicitAs.impl(%int_32) [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.e0d = struct_value () [concrete] // CHECK:STDOUT: %ImplicitAs.facet: %ImplicitAs.type.e8c = facet_value Core.IntLiteral, (%ImplicitAs.impl_witness.6bc) [concrete] // CHECK:STDOUT: %ImplicitAs.WithSelf.Convert.type.b37: type = fn_type @ImplicitAs.WithSelf.Convert, @ImplicitAs.WithSelf(%i32, %ImplicitAs.facet) [concrete] // CHECK:STDOUT: %.545: type = fn_type_with_self_type %ImplicitAs.WithSelf.Convert.type.b37, %ImplicitAs.facet [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.bound: = bound_method %int_4.0c1, %Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5 [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn: = specific_function %Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5, @Core.IntLiteral.as.ImplicitAs.impl.Convert(%int_32) [concrete] // CHECK:STDOUT: %bound_method: = bound_method %int_4.0c1, %Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn [concrete] // CHECK:STDOUT: %int_4.940: %i32 = int_value 4 [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: .Int = %Core.Int // CHECK:STDOUT: .Copy = %Core.Copy // CHECK:STDOUT: .ImplicitAs = %Core.ImplicitAs // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: %Core.Int: %Int.type = import_ref Core//prelude/parts/int, Int, loaded [concrete = constants.%Int.generic] // CHECK:STDOUT: %Core.Copy: type = import_ref Core//prelude/parts/copy, Copy, loaded [concrete = constants.%Copy.type] // CHECK:STDOUT: %Core.import_ref.18d: @Int.as.Copy.impl.%Int.as.Copy.impl.Op.type (%Int.as.Copy.impl.Op.type.824) = import_ref Core//prelude/parts/int, loc{{\d+_\d+}}, loaded [symbolic = @Int.as.Copy.impl.%Int.as.Copy.impl.Op (constants.%Int.as.Copy.impl.Op.9b9)] // CHECK:STDOUT: %Copy.impl_witness_table.e76 = impl_witness_table (%Core.import_ref.18d), @Int.as.Copy.impl [concrete] // CHECK:STDOUT: %Core.ImplicitAs: %ImplicitAs.type.cc7 = import_ref Core//prelude/parts/as, ImplicitAs, loaded [concrete = constants.%ImplicitAs.generic] // CHECK:STDOUT: %Core.import_ref.42d: @Core.IntLiteral.as.ImplicitAs.impl.%Core.IntLiteral.as.ImplicitAs.impl.Convert.type (%Core.IntLiteral.as.ImplicitAs.impl.Convert.type.4e6) = import_ref Core//prelude/parts/int, loc{{\d+_\d+}}, loaded [symbolic = @Core.IntLiteral.as.ImplicitAs.impl.%Core.IntLiteral.as.ImplicitAs.impl.Convert (constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.3c2)] // CHECK:STDOUT: %ImplicitAs.impl_witness_table.74f = impl_witness_table (%Core.import_ref.42d), @Core.IntLiteral.as.ImplicitAs.impl [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .Class = %Class.decl // CHECK:STDOUT: .Run = %Run.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %Class.decl: type = class_decl @Class [concrete = constants.%Class] {} {} // CHECK:STDOUT: %Class.G.decl: %Class.G.type = fn_decl @Class.G [concrete = constants.%Class.G] { // CHECK:STDOUT: %n.patt: %pattern_type.7ce = value_binding_pattern n [concrete] // CHECK:STDOUT: %n.param_patt: %pattern_type.7ce = value_param_pattern %n.patt [concrete] // CHECK:STDOUT: %return.patt: %pattern_type.7ce = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: %pattern_type.7ce = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %i32.loc25_23: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %.loc25: Core.Form = init_form %i32.loc25_23 [concrete = constants.%.ff5] // CHECK:STDOUT: %n.param.loc25: %i32 = value_param call_param0 // CHECK:STDOUT: %i32.loc25_15: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %n.loc25: %i32 = value_binding n, %n.param.loc25 // CHECK:STDOUT: %return.param.loc25: ref %i32 = out_param call_param1 // CHECK:STDOUT: %return.loc25: ref %i32 = return_slot %return.param.loc25 // CHECK:STDOUT: } // CHECK:STDOUT: %Run.decl: %Run.type = fn_decl @Run [concrete = constants.%Run] { // CHECK:STDOUT: %return.patt: %pattern_type.7ce = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: %pattern_type.7ce = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %i32: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %.loc29: Core.Form = init_form %i32 [concrete = constants.%.ff5] // CHECK:STDOUT: %return.param: ref %i32 = out_param call_param0 // CHECK:STDOUT: %return: ref %i32 = return_slot %return.param // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @Class { // CHECK:STDOUT: %Class.F.decl: %Class.F.type = fn_decl @Class.F [concrete = constants.%Class.F] { // CHECK:STDOUT: %n.patt: %pattern_type.7ce = value_binding_pattern n [concrete] // CHECK:STDOUT: %n.param_patt: %pattern_type.7ce = value_param_pattern %n.patt [concrete] // CHECK:STDOUT: %return.patt: %pattern_type.7ce = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: %pattern_type.7ce = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %i32.loc16_19: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %.loc16: Core.Form = init_form %i32.loc16_19 [concrete = constants.%.ff5] // CHECK:STDOUT: %n.param: %i32 = value_param call_param0 // CHECK:STDOUT: %i32.loc16_11: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %n: %i32 = value_binding n, %n.param // CHECK:STDOUT: %return.param: ref %i32 = out_param call_param1 // CHECK:STDOUT: %return: ref %i32 = return_slot %return.param // CHECK:STDOUT: } // CHECK:STDOUT: %Class.G.decl: %Class.G.type = fn_decl @Class.G [concrete = constants.%Class.G] { // CHECK:STDOUT: %n.patt: %pattern_type.7ce = value_binding_pattern n [concrete] // CHECK:STDOUT: %n.param_patt: %pattern_type.7ce = value_param_pattern %n.patt [concrete] // CHECK:STDOUT: %return.patt: %pattern_type.7ce = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: %pattern_type.7ce = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %i32.loc20_19: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %.loc20: Core.Form = init_form %i32.loc20_19 [concrete = constants.%.ff5] // CHECK:STDOUT: %n.param.loc20: %i32 = value_param call_param0 // CHECK:STDOUT: %i32.loc20_11: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %n.loc20: %i32 = value_binding n, %n.param.loc20 // CHECK:STDOUT: %return.param.loc20: ref %i32 = out_param call_param1 // CHECK:STDOUT: %return.loc20: ref %i32 = return_slot %return.param.loc20 // CHECK:STDOUT: } // CHECK:STDOUT: %i32: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %.loc22: %Class.elem = field_decl k, element0 [concrete] // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%struct_type.k [concrete = constants.%complete_type.954] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%Class // CHECK:STDOUT: .F = %Class.F.decl // CHECK:STDOUT: .G = %Class.G.decl // CHECK:STDOUT: .k = %.loc22 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @Class.F(%n.param: %i32) -> out %return.param: %i32 { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %n.ref: %i32 = name_ref n, %n // CHECK:STDOUT: %impl.elem0: %.8e2 = impl_witness_access constants.%Copy.impl_witness.f17, element0 [concrete = constants.%Int.as.Copy.impl.Op.664] // CHECK:STDOUT: %bound_method.loc17_12.1: = bound_method %n.ref, %impl.elem0 // CHECK:STDOUT: %specific_fn: = specific_function %impl.elem0, @Int.as.Copy.impl.Op(constants.%int_32) [concrete = constants.%Int.as.Copy.impl.Op.specific_fn] // CHECK:STDOUT: %bound_method.loc17_12.2: = bound_method %n.ref, %specific_fn // CHECK:STDOUT: %Int.as.Copy.impl.Op.call: init %i32 = call %bound_method.loc17_12.2(%n.ref) // CHECK:STDOUT: return %Int.as.Copy.impl.Op.call // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @Class.G(%n.param.loc25: %i32) -> out %return.param.loc25: %i32 { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %n.ref: %i32 = name_ref n, %n.loc25 // CHECK:STDOUT: %impl.elem0: %.8e2 = impl_witness_access constants.%Copy.impl_witness.f17, element0 [concrete = constants.%Int.as.Copy.impl.Op.664] // CHECK:STDOUT: %bound_method.loc26_10.1: = bound_method %n.ref, %impl.elem0 // CHECK:STDOUT: %specific_fn: = specific_function %impl.elem0, @Int.as.Copy.impl.Op(constants.%int_32) [concrete = constants.%Int.as.Copy.impl.Op.specific_fn] // CHECK:STDOUT: %bound_method.loc26_10.2: = bound_method %n.ref, %specific_fn // CHECK:STDOUT: %Int.as.Copy.impl.Op.call: init %i32 = call %bound_method.loc26_10.2(%n.ref) // CHECK:STDOUT: return %Int.as.Copy.impl.Op.call // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @Run() -> out %return.param: %i32 { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %Class.ref: type = name_ref Class, file.%Class.decl [concrete = constants.%Class] // CHECK:STDOUT: %F.ref: %Class.F.type = name_ref F, @Class.%Class.F.decl [concrete = constants.%Class.F] // CHECK:STDOUT: %int_4: Core.IntLiteral = int_value 4 [concrete = constants.%int_4.0c1] // CHECK:STDOUT: %impl.elem0: %.545 = impl_witness_access constants.%ImplicitAs.impl_witness.6bc, element0 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5] // CHECK:STDOUT: %bound_method.loc30_18.1: = bound_method %int_4, %impl.elem0 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.bound] // CHECK:STDOUT: %specific_fn: = specific_function %impl.elem0, @Core.IntLiteral.as.ImplicitAs.impl.Convert(constants.%int_32) [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn] // CHECK:STDOUT: %bound_method.loc30_18.2: = bound_method %int_4, %specific_fn [concrete = constants.%bound_method] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.call: init %i32 = call %bound_method.loc30_18.2(%int_4) [concrete = constants.%int_4.940] // CHECK:STDOUT: %.loc30_18.1: %i32 = value_of_initializer %Core.IntLiteral.as.ImplicitAs.impl.Convert.call [concrete = constants.%int_4.940] // CHECK:STDOUT: %.loc30_18.2: %i32 = converted %int_4, %.loc30_18.1 [concrete = constants.%int_4.940] // CHECK:STDOUT: %Class.F.call: init %i32 = call %F.ref(%.loc30_18.2) // CHECK:STDOUT: return %Class.F.call // CHECK:STDOUT: } // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/class/complete_in_member_fn.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/int.carbon // TODO: Add ranges and switch to "--dump-sem-ir-ranges=only". // EXTRA-ARGS: --dump-sem-ir-ranges=if-present // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/class/complete_in_member_fn.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/class/complete_in_member_fn.carbon class C { fn F(c: C) -> i32 { return c.a; } var a: i32; } // CHECK:STDOUT: --- complete_in_member_fn.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %C: type = class_type @C [concrete] // CHECK:STDOUT: %pattern_type.7c7: type = pattern_type %C [concrete] // CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [concrete] // CHECK:STDOUT: %Int.type: type = generic_class_type @Int [concrete] // CHECK:STDOUT: %Int.generic: %Int.type = struct_value () [concrete] // CHECK:STDOUT: %N: Core.IntLiteral = symbolic_binding N, 0 [symbolic] // CHECK:STDOUT: %i32: type = class_type @Int, @Int(%int_32) [concrete] // CHECK:STDOUT: %.ff5: Core.Form = init_form %i32 [concrete] // CHECK:STDOUT: %pattern_type.7ce: type = pattern_type %i32 [concrete] // CHECK:STDOUT: %C.F.type: type = fn_type @C.F [concrete] // CHECK:STDOUT: %C.F: %C.F.type = struct_value () [concrete] // CHECK:STDOUT: %C.elem: type = unbound_element_type %C, %i32 [concrete] // CHECK:STDOUT: %struct_type.a: type = struct_type {.a: %i32} [concrete] // CHECK:STDOUT: %complete_type.fd7: = complete_type_witness %struct_type.a [concrete] // CHECK:STDOUT: %Copy.type: type = facet_type <@Copy> [concrete] // CHECK:STDOUT: %Int.as.Copy.impl.Op.type.824: type = fn_type @Int.as.Copy.impl.Op, @Int.as.Copy.impl(%N) [symbolic] // CHECK:STDOUT: %Int.as.Copy.impl.Op.9b9: %Int.as.Copy.impl.Op.type.824 = struct_value () [symbolic] // CHECK:STDOUT: %Copy.impl_witness.f17: = impl_witness imports.%Copy.impl_witness_table.e76, @Int.as.Copy.impl(%int_32) [concrete] // CHECK:STDOUT: %Int.as.Copy.impl.Op.type.546: type = fn_type @Int.as.Copy.impl.Op, @Int.as.Copy.impl(%int_32) [concrete] // CHECK:STDOUT: %Int.as.Copy.impl.Op.664: %Int.as.Copy.impl.Op.type.546 = struct_value () [concrete] // CHECK:STDOUT: %Copy.facet: %Copy.type = facet_value %i32, (%Copy.impl_witness.f17) [concrete] // CHECK:STDOUT: %Copy.WithSelf.Op.type.081: type = fn_type @Copy.WithSelf.Op, @Copy.WithSelf(%Copy.facet) [concrete] // CHECK:STDOUT: %.8e2: type = fn_type_with_self_type %Copy.WithSelf.Op.type.081, %Copy.facet [concrete] // CHECK:STDOUT: %Int.as.Copy.impl.Op.specific_fn: = specific_function %Int.as.Copy.impl.Op.664, @Int.as.Copy.impl.Op(%int_32) [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: .Int = %Core.Int // CHECK:STDOUT: .Copy = %Core.Copy // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: %Core.Int: %Int.type = import_ref Core//prelude/parts/int, Int, loaded [concrete = constants.%Int.generic] // CHECK:STDOUT: %Core.Copy: type = import_ref Core//prelude/parts/copy, Copy, loaded [concrete = constants.%Copy.type] // CHECK:STDOUT: %Core.import_ref.18d: @Int.as.Copy.impl.%Int.as.Copy.impl.Op.type (%Int.as.Copy.impl.Op.type.824) = import_ref Core//prelude/parts/int, loc{{\d+_\d+}}, loaded [symbolic = @Int.as.Copy.impl.%Int.as.Copy.impl.Op (constants.%Int.as.Copy.impl.Op.9b9)] // CHECK:STDOUT: %Copy.impl_witness_table.e76 = impl_witness_table (%Core.import_ref.18d), @Int.as.Copy.impl [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .C = %C.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %C.decl: type = class_decl @C [concrete = constants.%C] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @C { // CHECK:STDOUT: %C.F.decl: %C.F.type = fn_decl @C.F [concrete = constants.%C.F] { // CHECK:STDOUT: %c.patt: %pattern_type.7c7 = value_binding_pattern c [concrete] // CHECK:STDOUT: %c.param_patt: %pattern_type.7c7 = value_param_pattern %c.patt [concrete] // CHECK:STDOUT: %return.patt: %pattern_type.7ce = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: %pattern_type.7ce = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %i32: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %.loc16_17: Core.Form = init_form %i32 [concrete = constants.%.ff5] // CHECK:STDOUT: %c.param: %C = value_param call_param0 // CHECK:STDOUT: %C.ref: type = name_ref C, file.%C.decl [concrete = constants.%C] // CHECK:STDOUT: %c: %C = value_binding c, %c.param // CHECK:STDOUT: %return.param: ref %i32 = out_param call_param1 // CHECK:STDOUT: %return: ref %i32 = return_slot %return.param // CHECK:STDOUT: } // CHECK:STDOUT: %i32: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %.loc18: %C.elem = field_decl a, element0 [concrete] // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%struct_type.a [concrete = constants.%complete_type.fd7] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%C // CHECK:STDOUT: .C = // CHECK:STDOUT: .F = %C.F.decl // CHECK:STDOUT: .a = %.loc18 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @C.F(%c.param: %C) -> out %return.param: %i32 { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %c.ref: %C = name_ref c, %c // CHECK:STDOUT: %a.ref: %C.elem = name_ref a, @C.%.loc18 [concrete = @C.%.loc18] // CHECK:STDOUT: %.loc16_31.1: ref %i32 = class_element_access %c.ref, element0 // CHECK:STDOUT: %.loc16_31.2: %i32 = acquire_value %.loc16_31.1 // CHECK:STDOUT: %impl.elem0: %.8e2 = impl_witness_access constants.%Copy.impl_witness.f17, element0 [concrete = constants.%Int.as.Copy.impl.Op.664] // CHECK:STDOUT: %bound_method.loc16_31.1: = bound_method %.loc16_31.2, %impl.elem0 // CHECK:STDOUT: %specific_fn: = specific_function %impl.elem0, @Int.as.Copy.impl.Op(constants.%int_32) [concrete = constants.%Int.as.Copy.impl.Op.specific_fn] // CHECK:STDOUT: %bound_method.loc16_31.2: = bound_method %.loc16_31.2, %specific_fn // CHECK:STDOUT: %Int.as.Copy.impl.Op.call: init %i32 = call %bound_method.loc16_31.2(%.loc16_31.2) // CHECK:STDOUT: return %Int.as.Copy.impl.Op.call // CHECK:STDOUT: } // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/class/cross_package_import.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/convert.carbon // TODO: Add ranges and switch to "--dump-sem-ir-ranges=only". // EXTRA-ARGS: --dump-sem-ir-ranges=if-present // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/class/cross_package_import.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/class/cross_package_import.carbon // ============================================================================ // Setup files // ============================================================================ // --- other_define.carbon package Other library "[[@TEST_NAME]]"; class C {} // --- other_extern.carbon package Other library "[[@TEST_NAME]]"; extern class C; // --- other_conflict.carbon package Other library "[[@TEST_NAME]]"; fn C() {} // ============================================================================ // Test files // ============================================================================ // --- define.carbon library "[[@TEST_NAME]]"; import Other library "other_define"; var c: Other.C = {}; // --- fail_extern.carbon library "[[@TEST_NAME]]"; import Other library "other_extern"; // CHECK:STDERR: fail_extern.carbon:[[@LINE+8]]:8: error: binding pattern has incomplete type `C` in name binding declaration [IncompleteTypeInBindingDecl] // CHECK:STDERR: var c: Other.C = {}; // CHECK:STDERR: ^~~~~~~ // CHECK:STDERR: fail_extern.carbon:[[@LINE-5]]:1: in import [InImport] // CHECK:STDERR: other_extern.carbon:4:1: note: class was forward declared here [ClassForwardDeclaredHere] // CHECK:STDERR: extern class C; // CHECK:STDERR: ^~~~~~~~~~~~~~~ // CHECK:STDERR: var c: Other.C = {}; // --- fail_todo_merge_define_extern.carbon library "[[@TEST_NAME]]"; // CHECK:STDERR: fail_todo_merge_define_extern.carbon:[[@LINE+8]]:1: in import [InImport] // CHECK:STDERR: other_extern.carbon:4:1: error: duplicate name `C` being declared in the same scope [NameDeclDuplicate] // CHECK:STDERR: extern class C; // CHECK:STDERR: ^~~~~~~~~~~~~~~ // CHECK:STDERR: fail_todo_merge_define_extern.carbon:[[@LINE+4]]:1: in import [InImport] // CHECK:STDERR: other_define.carbon:4:1: note: name is previously declared here [NameDeclPrevious] // CHECK:STDERR: class C {} // CHECK:STDERR: ^~~~~~~~~ import Other library "other_define"; import Other library "other_extern"; // CHECK:STDERR: fail_todo_merge_define_extern.carbon:[[@LINE+4]]:8: note: in name lookup for `C` [InNameLookup] // CHECK:STDERR: var c: Other.C = {}; // CHECK:STDERR: ^~~~~~~ // CHECK:STDERR: var c: Other.C = {}; // --- fail_conflict.carbon library "[[@TEST_NAME]]"; // CHECK:STDERR: fail_conflict.carbon:[[@LINE+8]]:1: in import [InImport] // CHECK:STDERR: other_conflict.carbon:4:1: error: duplicate name `C` being declared in the same scope [NameDeclDuplicate] // CHECK:STDERR: fn C() {} // CHECK:STDERR: ^~~~~~~~ // CHECK:STDERR: fail_conflict.carbon:[[@LINE+4]]:1: in import [InImport] // CHECK:STDERR: other_define.carbon:4:1: note: name is previously declared here [NameDeclPrevious] // CHECK:STDERR: class C {} // CHECK:STDERR: ^~~~~~~~~ import Other library "other_define"; import Other library "other_conflict"; // CHECK:STDERR: fail_conflict.carbon:[[@LINE+4]]:8: note: in name lookup for `C` [InNameLookup] // CHECK:STDERR: var c: Other.C = {}; // CHECK:STDERR: ^~~~~~~ // CHECK:STDERR: var c: Other.C = {}; // CHECK:STDOUT: --- other_define.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %C: type = class_type @C [concrete] // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %complete_type: = complete_type_witness %empty_struct_type [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .C = %C.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %C.decl: type = class_decl @C [concrete = constants.%C] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @C { // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%C // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- other_extern.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %C: type = class_type @C [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .C = %C.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %C.decl: type = class_decl @C [concrete = constants.%C] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @C; // CHECK:STDOUT: // CHECK:STDOUT: --- other_conflict.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %C.type: type = fn_type @C [concrete] // CHECK:STDOUT: %C: %C.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .C = %C.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %C.decl: %C.type = fn_decl @C [concrete = constants.%C] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @C() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- define.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %C: type = class_type @C [concrete] // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %complete_type: = complete_type_witness %empty_struct_type [concrete] // CHECK:STDOUT: %pattern_type: type = pattern_type %C [concrete] // CHECK:STDOUT: %empty_struct: %empty_struct_type = struct_value () [concrete] // CHECK:STDOUT: %C.val: %C = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: %Other: = namespace file.%Other.import, [concrete] { // CHECK:STDOUT: .C = %Other.C // CHECK:STDOUT: import Other//other_define // CHECK:STDOUT: } // CHECK:STDOUT: %Other.C: type = import_ref Other//other_define, C, loaded [concrete = constants.%C] // CHECK:STDOUT: %Other.import_ref.8f2: = import_ref Other//other_define, loc4_10, loaded [concrete = constants.%complete_type] // CHECK:STDOUT: %Other.import_ref.42d = import_ref Other//other_define, inst{{[0-9A-F]+}} [no loc], unloaded // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .Other = imports.%Other // CHECK:STDOUT: .c = %c // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %Other.import = import Other // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %c.patt: %pattern_type = ref_binding_pattern c [concrete] // CHECK:STDOUT: %c.var_patt: %pattern_type = var_pattern %c.patt [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %c.var: ref %C = var %c.var_patt [concrete] // CHECK:STDOUT: %.loc6: type = splice_block %C.ref [concrete = constants.%C] { // CHECK:STDOUT: %Other.ref: = name_ref Other, imports.%Other [concrete = imports.%Other] // CHECK:STDOUT: %C.ref: type = name_ref C, imports.%Other.C [concrete = constants.%C] // CHECK:STDOUT: } // CHECK:STDOUT: %c: ref %C = ref_binding c, %c.var [concrete = %c.var] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @C [from "other_define.carbon"] { // CHECK:STDOUT: complete_type_witness = imports.%Other.import_ref.8f2 // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = imports.%Other.import_ref.42d // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @__global_init() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %.loc6_19.1: %empty_struct_type = struct_literal () [concrete = constants.%empty_struct] // CHECK:STDOUT: %.loc6_19.2: init %C to file.%c.var = class_init () [concrete = constants.%C.val] // CHECK:STDOUT: %.loc6_1: init %C = converted %.loc6_19.1, %.loc6_19.2 [concrete = constants.%C.val] // CHECK:STDOUT: assign file.%c.var, %.loc6_1 // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- fail_extern.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %C: type = class_type @C [concrete] // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %empty_struct: %empty_struct_type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: %Other: = namespace file.%Other.import, [concrete] { // CHECK:STDOUT: .C = %Other.C // CHECK:STDOUT: import Other//other_extern // CHECK:STDOUT: } // CHECK:STDOUT: %Other.C: type = import_ref Other//other_extern, C, loaded [concrete = constants.%C] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .Other = imports.%Other // CHECK:STDOUT: .c = %c // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %Other.import = import Other // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %c.patt: = ref_binding_pattern c [concrete] // CHECK:STDOUT: %c.var_patt: = var_pattern %c.patt [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %c.var: ref = var %c.var_patt [concrete = ] // CHECK:STDOUT: %.loc14: type = splice_block %C.ref [concrete = constants.%C] { // CHECK:STDOUT: %Other.ref: = name_ref Other, imports.%Other [concrete = imports.%Other] // CHECK:STDOUT: %C.ref: type = name_ref C, imports.%Other.C [concrete = constants.%C] // CHECK:STDOUT: } // CHECK:STDOUT: %c: ref = ref_binding c, [concrete = ] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @C [from "other_extern.carbon"]; // CHECK:STDOUT: // CHECK:STDOUT: fn @__global_init() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %.loc14: %empty_struct_type = struct_literal () [concrete = constants.%empty_struct] // CHECK:STDOUT: assign file.%c.var, // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- fail_todo_merge_define_extern.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %C: type = class_type @C [concrete] // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %complete_type: = complete_type_witness %empty_struct_type [concrete] // CHECK:STDOUT: %pattern_type: type = pattern_type %C [concrete] // CHECK:STDOUT: %empty_struct: %empty_struct_type = struct_value () [concrete] // CHECK:STDOUT: %C.val: %C = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: %Other: = namespace file.%Other.import, [concrete] { // CHECK:STDOUT: .C = %Other.C // CHECK:STDOUT: import Other//other_define // CHECK:STDOUT: import Other//other_extern // CHECK:STDOUT: } // CHECK:STDOUT: %Other.C: type = import_ref Other//other_define, C, loaded [concrete = constants.%C] // CHECK:STDOUT: %Other.import_ref.8f2: = import_ref Other//other_define, loc4_10, loaded [concrete = constants.%complete_type] // CHECK:STDOUT: %Other.import_ref.42d = import_ref Other//other_define, inst{{[0-9A-F]+}} [no loc], unloaded // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .Other = imports.%Other // CHECK:STDOUT: .c = %c // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %Other.import = import Other // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %c.patt: %pattern_type = ref_binding_pattern c [concrete] // CHECK:STDOUT: %c.var_patt: %pattern_type = var_pattern %c.patt [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %c.var: ref %C = var %c.var_patt [concrete] // CHECK:STDOUT: %.loc19: type = splice_block %C.ref [concrete = constants.%C] { // CHECK:STDOUT: %Other.ref: = name_ref Other, imports.%Other [concrete = imports.%Other] // CHECK:STDOUT: %C.ref: type = name_ref C, imports.%Other.C [concrete = constants.%C] // CHECK:STDOUT: } // CHECK:STDOUT: %c: ref %C = ref_binding c, %c.var [concrete = %c.var] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @C [from "other_define.carbon"] { // CHECK:STDOUT: complete_type_witness = imports.%Other.import_ref.8f2 // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = imports.%Other.import_ref.42d // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @__global_init() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %.loc19_19.1: %empty_struct_type = struct_literal () [concrete = constants.%empty_struct] // CHECK:STDOUT: %.loc19_19.2: init %C to file.%c.var = class_init () [concrete = constants.%C.val] // CHECK:STDOUT: %.loc19_1: init %C = converted %.loc19_19.1, %.loc19_19.2 [concrete = constants.%C.val] // CHECK:STDOUT: assign file.%c.var, %.loc19_1 // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- fail_conflict.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %C: type = class_type @C [concrete] // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %complete_type: = complete_type_witness %empty_struct_type [concrete] // CHECK:STDOUT: %pattern_type: type = pattern_type %C [concrete] // CHECK:STDOUT: %empty_struct: %empty_struct_type = struct_value () [concrete] // CHECK:STDOUT: %C.val: %C = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: %Other: = namespace file.%Other.import, [concrete] { // CHECK:STDOUT: .C = %Other.C // CHECK:STDOUT: import Other//other_define // CHECK:STDOUT: import Other//other_conflict // CHECK:STDOUT: } // CHECK:STDOUT: %Other.C: type = import_ref Other//other_define, C, loaded [concrete = constants.%C] // CHECK:STDOUT: %Other.import_ref.8f2: = import_ref Other//other_define, loc4_10, loaded [concrete = constants.%complete_type] // CHECK:STDOUT: %Other.import_ref.42d = import_ref Other//other_define, inst{{[0-9A-F]+}} [no loc], unloaded // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .Other = imports.%Other // CHECK:STDOUT: .c = %c // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %Other.import = import Other // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %c.patt: %pattern_type = ref_binding_pattern c [concrete] // CHECK:STDOUT: %c.var_patt: %pattern_type = var_pattern %c.patt [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %c.var: ref %C = var %c.var_patt [concrete] // CHECK:STDOUT: %.loc19: type = splice_block %C.ref [concrete = constants.%C] { // CHECK:STDOUT: %Other.ref: = name_ref Other, imports.%Other [concrete = imports.%Other] // CHECK:STDOUT: %C.ref: type = name_ref C, imports.%Other.C [concrete = constants.%C] // CHECK:STDOUT: } // CHECK:STDOUT: %c: ref %C = ref_binding c, %c.var [concrete = %c.var] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @C [from "other_define.carbon"] { // CHECK:STDOUT: complete_type_witness = imports.%Other.import_ref.8f2 // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = imports.%Other.import_ref.42d // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @__global_init() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %.loc19_19.1: %empty_struct_type = struct_literal () [concrete = constants.%empty_struct] // CHECK:STDOUT: %.loc19_19.2: init %C to file.%c.var = class_init () [concrete = constants.%C.val] // CHECK:STDOUT: %.loc19_1: init %C = converted %.loc19_19.1, %.loc19_19.2 [concrete = constants.%C.val] // CHECK:STDOUT: assign file.%c.var, %.loc19_1 // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/class/destroy_calls.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/destroy.carbon // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/class/destroy_calls.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/class/destroy_calls.carbon // --- implicit_return.carbon library "[[@TEST_NAME]]"; class A {} class B {} class C {} //@dump-sem-ir-begin fn F() { var unused a: A = {}; var unused b: B = {}; var unused c: C = {}; } //@dump-sem-ir-end // --- nested_scope.carbon library "[[@TEST_NAME]]"; class A {} class B {} class C {} //@dump-sem-ir-begin fn F() { var unused a: A = {}; var unused b: B = {}; if (true) { var unused c: C = {}; } } //@dump-sem-ir-end // --- temp.carbon library "[[@TEST_NAME]]"; class A { fn Make() -> A; } class B { fn Make() -> B; } class C { fn Make() -> C; } //@dump-sem-ir-begin fn F() { // TODO: The scoping of these destroy calls is incorrect. Maybe we need to // establish statement scopes? A.Make(); B.Make(); C.Make(); } //@dump-sem-ir-end // --- generic_class.carbon library "[[@TEST_NAME]]"; class C {} class D(template T:! type) {} //@dump-sem-ir-begin fn F() { var unused a: D(C) = {}; } //@dump-sem-ir-end // --- generic_use_inside_generic.carbon library "[[@TEST_NAME]]"; class C(template T:! type) {} //@dump-sem-ir-begin fn F(template T:! type) { var unused v: C(T) = {}; } //@dump-sem-ir-end fn G() { F({}); } // CHECK:STDOUT: --- implicit_return.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %A: type = class_type @A [concrete] // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %B: type = class_type @B [concrete] // CHECK:STDOUT: %C: type = class_type @C [concrete] // CHECK:STDOUT: %F.type: type = fn_type @F [concrete] // CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] // CHECK:STDOUT: %F: %F.type = struct_value () [concrete] // CHECK:STDOUT: %pattern_type.1ab: type = pattern_type %A [concrete] // CHECK:STDOUT: %empty_struct: %empty_struct_type = struct_value () [concrete] // CHECK:STDOUT: %A.val: %A = struct_value () [concrete] // CHECK:STDOUT: %pattern_type.1f4: type = pattern_type %B [concrete] // CHECK:STDOUT: %B.val: %B = struct_value () [concrete] // CHECK:STDOUT: %pattern_type.7c7: type = pattern_type %C [concrete] // CHECK:STDOUT: %C.val: %C = struct_value () [concrete] // CHECK:STDOUT: %Destroy.Op.type.bae255.1: type = fn_type @Destroy.Op.loc12 [concrete] // CHECK:STDOUT: %Destroy.Op.651ba6.1: %Destroy.Op.type.bae255.1 = struct_value () [concrete] // CHECK:STDOUT: %Destroy.Op.type.bae255.2: type = fn_type @Destroy.Op.loc11 [concrete] // CHECK:STDOUT: %Destroy.Op.651ba6.2: %Destroy.Op.type.bae255.2 = struct_value () [concrete] // CHECK:STDOUT: %Destroy.Op.type.bae255.3: type = fn_type @Destroy.Op.loc10 [concrete] // CHECK:STDOUT: %Destroy.Op.651ba6.3: %Destroy.Op.type.bae255.3 = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: %F.decl: %F.type = fn_decl @F [concrete = constants.%F] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @F() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %a.patt: %pattern_type.1ab = ref_binding_pattern a [concrete] // CHECK:STDOUT: %a.var_patt: %pattern_type.1ab = var_pattern %a.patt [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %a.var: ref %A = var %a.var_patt // CHECK:STDOUT: %.loc10_22.1: %empty_struct_type = struct_literal () [concrete = constants.%empty_struct] // CHECK:STDOUT: %.loc10_22.2: init %A to %a.var = class_init () [concrete = constants.%A.val] // CHECK:STDOUT: %.loc10_3: init %A = converted %.loc10_22.1, %.loc10_22.2 [concrete = constants.%A.val] // CHECK:STDOUT: assign %a.var, %.loc10_3 // CHECK:STDOUT: %A.ref: type = name_ref A, file.%A.decl [concrete = constants.%A] // CHECK:STDOUT: %a: ref %A = ref_binding a, %a.var // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %b.patt: %pattern_type.1f4 = ref_binding_pattern b [concrete] // CHECK:STDOUT: %b.var_patt: %pattern_type.1f4 = var_pattern %b.patt [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %b.var: ref %B = var %b.var_patt // CHECK:STDOUT: %.loc11_22.1: %empty_struct_type = struct_literal () [concrete = constants.%empty_struct] // CHECK:STDOUT: %.loc11_22.2: init %B to %b.var = class_init () [concrete = constants.%B.val] // CHECK:STDOUT: %.loc11_3: init %B = converted %.loc11_22.1, %.loc11_22.2 [concrete = constants.%B.val] // CHECK:STDOUT: assign %b.var, %.loc11_3 // CHECK:STDOUT: %B.ref: type = name_ref B, file.%B.decl [concrete = constants.%B] // CHECK:STDOUT: %b: ref %B = ref_binding b, %b.var // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %c.patt: %pattern_type.7c7 = ref_binding_pattern c [concrete] // CHECK:STDOUT: %c.var_patt: %pattern_type.7c7 = var_pattern %c.patt [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %c.var: ref %C = var %c.var_patt // CHECK:STDOUT: %.loc12_22.1: %empty_struct_type = struct_literal () [concrete = constants.%empty_struct] // CHECK:STDOUT: %.loc12_22.2: init %C to %c.var = class_init () [concrete = constants.%C.val] // CHECK:STDOUT: %.loc12_3: init %C = converted %.loc12_22.1, %.loc12_22.2 [concrete = constants.%C.val] // CHECK:STDOUT: assign %c.var, %.loc12_3 // CHECK:STDOUT: %C.ref: type = name_ref C, file.%C.decl [concrete = constants.%C] // CHECK:STDOUT: %c: ref %C = ref_binding c, %c.var // CHECK:STDOUT: %Destroy.Op.bound.loc12: = bound_method %c.var, constants.%Destroy.Op.651ba6.1 // CHECK:STDOUT: %Destroy.Op.call.loc12: init %empty_tuple.type = call %Destroy.Op.bound.loc12(%c.var) // CHECK:STDOUT: %Destroy.Op.bound.loc11: = bound_method %b.var, constants.%Destroy.Op.651ba6.2 // CHECK:STDOUT: %Destroy.Op.call.loc11: init %empty_tuple.type = call %Destroy.Op.bound.loc11(%b.var) // CHECK:STDOUT: %Destroy.Op.bound.loc10: = bound_method %a.var, constants.%Destroy.Op.651ba6.3 // CHECK:STDOUT: %Destroy.Op.call.loc10: init %empty_tuple.type = call %Destroy.Op.bound.loc10(%a.var) // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @Destroy.Op.loc12(%self.param: ref %C) = "no_op"; // CHECK:STDOUT: // CHECK:STDOUT: fn @Destroy.Op.loc11(%self.param: ref %B) = "no_op"; // CHECK:STDOUT: // CHECK:STDOUT: fn @Destroy.Op.loc10(%self.param: ref %A) = "no_op"; // CHECK:STDOUT: // CHECK:STDOUT: --- nested_scope.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %A: type = class_type @A [concrete] // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %B: type = class_type @B [concrete] // CHECK:STDOUT: %C: type = class_type @C [concrete] // CHECK:STDOUT: %F.type: type = fn_type @F [concrete] // CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] // CHECK:STDOUT: %F: %F.type = struct_value () [concrete] // CHECK:STDOUT: %pattern_type.1ab: type = pattern_type %A [concrete] // CHECK:STDOUT: %empty_struct: %empty_struct_type = struct_value () [concrete] // CHECK:STDOUT: %A.val: %A = struct_value () [concrete] // CHECK:STDOUT: %pattern_type.1f4: type = pattern_type %B [concrete] // CHECK:STDOUT: %B.val: %B = struct_value () [concrete] // CHECK:STDOUT: %true: bool = bool_literal true [concrete] // CHECK:STDOUT: %pattern_type.7c7: type = pattern_type %C [concrete] // CHECK:STDOUT: %C.val: %C = struct_value () [concrete] // CHECK:STDOUT: %Destroy.Op.type.bae255.1: type = fn_type @Destroy.Op.loc13 [concrete] // CHECK:STDOUT: %Destroy.Op.651ba6.1: %Destroy.Op.type.bae255.1 = struct_value () [concrete] // CHECK:STDOUT: %Destroy.Op.type.bae255.2: type = fn_type @Destroy.Op.loc11 [concrete] // CHECK:STDOUT: %Destroy.Op.651ba6.2: %Destroy.Op.type.bae255.2 = struct_value () [concrete] // CHECK:STDOUT: %Destroy.Op.type.bae255.3: type = fn_type @Destroy.Op.loc10 [concrete] // CHECK:STDOUT: %Destroy.Op.651ba6.3: %Destroy.Op.type.bae255.3 = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: %F.decl: %F.type = fn_decl @F [concrete = constants.%F] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @F() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %a.patt: %pattern_type.1ab = ref_binding_pattern a [concrete] // CHECK:STDOUT: %a.var_patt: %pattern_type.1ab = var_pattern %a.patt [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %a.var: ref %A = var %a.var_patt // CHECK:STDOUT: %.loc10_22.1: %empty_struct_type = struct_literal () [concrete = constants.%empty_struct] // CHECK:STDOUT: %.loc10_22.2: init %A to %a.var = class_init () [concrete = constants.%A.val] // CHECK:STDOUT: %.loc10_3: init %A = converted %.loc10_22.1, %.loc10_22.2 [concrete = constants.%A.val] // CHECK:STDOUT: assign %a.var, %.loc10_3 // CHECK:STDOUT: %A.ref: type = name_ref A, file.%A.decl [concrete = constants.%A] // CHECK:STDOUT: %a: ref %A = ref_binding a, %a.var // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %b.patt: %pattern_type.1f4 = ref_binding_pattern b [concrete] // CHECK:STDOUT: %b.var_patt: %pattern_type.1f4 = var_pattern %b.patt [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %b.var: ref %B = var %b.var_patt // CHECK:STDOUT: %.loc11_22.1: %empty_struct_type = struct_literal () [concrete = constants.%empty_struct] // CHECK:STDOUT: %.loc11_22.2: init %B to %b.var = class_init () [concrete = constants.%B.val] // CHECK:STDOUT: %.loc11_3: init %B = converted %.loc11_22.1, %.loc11_22.2 [concrete = constants.%B.val] // CHECK:STDOUT: assign %b.var, %.loc11_3 // CHECK:STDOUT: %B.ref: type = name_ref B, file.%B.decl [concrete = constants.%B] // CHECK:STDOUT: %b: ref %B = ref_binding b, %b.var // CHECK:STDOUT: %true: bool = bool_literal true [concrete = constants.%true] // CHECK:STDOUT: if %true br !if.then else br !if.else // CHECK:STDOUT: // CHECK:STDOUT: !if.then: // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %c.patt: %pattern_type.7c7 = ref_binding_pattern c [concrete] // CHECK:STDOUT: %c.var_patt: %pattern_type.7c7 = var_pattern %c.patt [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %c.var: ref %C = var %c.var_patt // CHECK:STDOUT: %.loc13_24.1: %empty_struct_type = struct_literal () [concrete = constants.%empty_struct] // CHECK:STDOUT: %.loc13_24.2: init %C to %c.var = class_init () [concrete = constants.%C.val] // CHECK:STDOUT: %.loc13_5: init %C = converted %.loc13_24.1, %.loc13_24.2 [concrete = constants.%C.val] // CHECK:STDOUT: assign %c.var, %.loc13_5 // CHECK:STDOUT: %C.ref: type = name_ref C, file.%C.decl [concrete = constants.%C] // CHECK:STDOUT: %c: ref %C = ref_binding c, %c.var // CHECK:STDOUT: br !if.else // CHECK:STDOUT: // CHECK:STDOUT: !if.else: // CHECK:STDOUT: %Destroy.Op.bound.loc13: = bound_method %c.var, constants.%Destroy.Op.651ba6.1 // CHECK:STDOUT: %Destroy.Op.call.loc13: init %empty_tuple.type = call %Destroy.Op.bound.loc13(%c.var) // CHECK:STDOUT: %Destroy.Op.bound.loc11: = bound_method %b.var, constants.%Destroy.Op.651ba6.2 // CHECK:STDOUT: %Destroy.Op.call.loc11: init %empty_tuple.type = call %Destroy.Op.bound.loc11(%b.var) // CHECK:STDOUT: %Destroy.Op.bound.loc10: = bound_method %a.var, constants.%Destroy.Op.651ba6.3 // CHECK:STDOUT: %Destroy.Op.call.loc10: init %empty_tuple.type = call %Destroy.Op.bound.loc10(%a.var) // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @Destroy.Op.loc13(%self.param: ref %C) = "no_op"; // CHECK:STDOUT: // CHECK:STDOUT: fn @Destroy.Op.loc11(%self.param: ref %B) = "no_op"; // CHECK:STDOUT: // CHECK:STDOUT: fn @Destroy.Op.loc10(%self.param: ref %A) = "no_op"; // CHECK:STDOUT: // CHECK:STDOUT: --- temp.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %A: type = class_type @A [concrete] // CHECK:STDOUT: %A.Make.type: type = fn_type @A.Make [concrete] // CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] // CHECK:STDOUT: %A.Make: %A.Make.type = struct_value () [concrete] // CHECK:STDOUT: %B: type = class_type @B [concrete] // CHECK:STDOUT: %B.Make.type: type = fn_type @B.Make [concrete] // CHECK:STDOUT: %B.Make: %B.Make.type = struct_value () [concrete] // CHECK:STDOUT: %C: type = class_type @C [concrete] // CHECK:STDOUT: %C.Make.type: type = fn_type @C.Make [concrete] // CHECK:STDOUT: %C.Make: %C.Make.type = struct_value () [concrete] // CHECK:STDOUT: %F.type: type = fn_type @F [concrete] // CHECK:STDOUT: %F: %F.type = struct_value () [concrete] // CHECK:STDOUT: %Destroy.Op.type.bae255.1: type = fn_type @Destroy.Op.loc14 [concrete] // CHECK:STDOUT: %Destroy.Op.651ba6.1: %Destroy.Op.type.bae255.1 = struct_value () [concrete] // CHECK:STDOUT: %Destroy.Op.type.bae255.2: type = fn_type @Destroy.Op.loc13 [concrete] // CHECK:STDOUT: %Destroy.Op.651ba6.2: %Destroy.Op.type.bae255.2 = struct_value () [concrete] // CHECK:STDOUT: %Destroy.Op.type.bae255.3: type = fn_type @Destroy.Op.loc12 [concrete] // CHECK:STDOUT: %Destroy.Op.651ba6.3: %Destroy.Op.type.bae255.3 = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: %F.decl: %F.type = fn_decl @F [concrete = constants.%F] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @F() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %A.ref: type = name_ref A, file.%A.decl [concrete = constants.%A] // CHECK:STDOUT: %Make.ref.loc12: %A.Make.type = name_ref Make, @A.%A.Make.decl [concrete = constants.%A.Make] // CHECK:STDOUT: %.loc12_10.1: ref %A = temporary_storage // CHECK:STDOUT: %A.Make.call: init %A to %.loc12_10.1 = call %Make.ref.loc12() // CHECK:STDOUT: %.loc12_10.2: ref %A = temporary %.loc12_10.1, %A.Make.call // CHECK:STDOUT: %B.ref: type = name_ref B, file.%B.decl [concrete = constants.%B] // CHECK:STDOUT: %Make.ref.loc13: %B.Make.type = name_ref Make, @B.%B.Make.decl [concrete = constants.%B.Make] // CHECK:STDOUT: %.loc13_10.1: ref %B = temporary_storage // CHECK:STDOUT: %B.Make.call: init %B to %.loc13_10.1 = call %Make.ref.loc13() // CHECK:STDOUT: %.loc13_10.2: ref %B = temporary %.loc13_10.1, %B.Make.call // CHECK:STDOUT: %C.ref: type = name_ref C, file.%C.decl [concrete = constants.%C] // CHECK:STDOUT: %Make.ref.loc14: %C.Make.type = name_ref Make, @C.%C.Make.decl [concrete = constants.%C.Make] // CHECK:STDOUT: %.loc14_10.1: ref %C = temporary_storage // CHECK:STDOUT: %C.Make.call: init %C to %.loc14_10.1 = call %Make.ref.loc14() // CHECK:STDOUT: %.loc14_10.2: ref %C = temporary %.loc14_10.1, %C.Make.call // CHECK:STDOUT: %Destroy.Op.bound.loc14: = bound_method %.loc14_10.2, constants.%Destroy.Op.651ba6.1 // CHECK:STDOUT: %Destroy.Op.call.loc14: init %empty_tuple.type = call %Destroy.Op.bound.loc14(%.loc14_10.2) // CHECK:STDOUT: %Destroy.Op.bound.loc13: = bound_method %.loc13_10.2, constants.%Destroy.Op.651ba6.2 // CHECK:STDOUT: %Destroy.Op.call.loc13: init %empty_tuple.type = call %Destroy.Op.bound.loc13(%.loc13_10.2) // CHECK:STDOUT: %Destroy.Op.bound.loc12: = bound_method %.loc12_10.2, constants.%Destroy.Op.651ba6.3 // CHECK:STDOUT: %Destroy.Op.call.loc12: init %empty_tuple.type = call %Destroy.Op.bound.loc12(%.loc12_10.2) // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @Destroy.Op.loc14(%self.param: ref %C) = "no_op"; // CHECK:STDOUT: // CHECK:STDOUT: fn @Destroy.Op.loc13(%self.param: ref %B) = "no_op"; // CHECK:STDOUT: // CHECK:STDOUT: fn @Destroy.Op.loc12(%self.param: ref %A) = "no_op"; // CHECK:STDOUT: // CHECK:STDOUT: --- generic_class.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %C: type = class_type @C [concrete] // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %D.type: type = generic_class_type @D [concrete] // CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] // CHECK:STDOUT: %D.generic: %D.type = struct_value () [concrete] // CHECK:STDOUT: %F.type: type = fn_type @F [concrete] // CHECK:STDOUT: %F: %F.type = struct_value () [concrete] // CHECK:STDOUT: %D.213: type = class_type @D, @D(%C) [concrete] // CHECK:STDOUT: %pattern_type.002: type = pattern_type %D.213 [concrete] // CHECK:STDOUT: %empty_struct: %empty_struct_type = struct_value () [concrete] // CHECK:STDOUT: %D.val: %D.213 = struct_value () [concrete] // CHECK:STDOUT: %Destroy.Op.type: type = fn_type @Destroy.Op [concrete] // CHECK:STDOUT: %Destroy.Op: %Destroy.Op.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: %F.decl: %F.type = fn_decl @F [concrete = constants.%F] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @F() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %a.patt: %pattern_type.002 = ref_binding_pattern a [concrete] // CHECK:STDOUT: %a.var_patt: %pattern_type.002 = var_pattern %a.patt [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %a.var: ref %D.213 = var %a.var_patt // CHECK:STDOUT: %.loc9_25.1: %empty_struct_type = struct_literal () [concrete = constants.%empty_struct] // CHECK:STDOUT: %.loc9_25.2: init %D.213 to %a.var = class_init () [concrete = constants.%D.val] // CHECK:STDOUT: %.loc9_3: init %D.213 = converted %.loc9_25.1, %.loc9_25.2 [concrete = constants.%D.val] // CHECK:STDOUT: assign %a.var, %.loc9_3 // CHECK:STDOUT: %.loc9_20: type = splice_block %D [concrete = constants.%D.213] { // CHECK:STDOUT: %D.ref: %D.type = name_ref D, file.%D.decl [concrete = constants.%D.generic] // CHECK:STDOUT: %C.ref: type = name_ref C, file.%C.decl [concrete = constants.%C] // CHECK:STDOUT: %D: type = class_type @D, @D(constants.%C) [concrete = constants.%D.213] // CHECK:STDOUT: } // CHECK:STDOUT: %a: ref %D.213 = ref_binding a, %a.var // CHECK:STDOUT: %Destroy.Op.bound: = bound_method %a.var, constants.%Destroy.Op // CHECK:STDOUT: %Destroy.Op.call: init %empty_tuple.type = call %Destroy.Op.bound(%a.var) // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @Destroy.Op(%self.param: ref %D.213) = "no_op"; // CHECK:STDOUT: // CHECK:STDOUT: --- generic_use_inside_generic.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %pattern_type.98f: type = pattern_type type [concrete] // CHECK:STDOUT: %T: type = symbolic_binding T, 0, template [template] // CHECK:STDOUT: %C.type: type = generic_class_type @C [concrete] // CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] // CHECK:STDOUT: %C.generic: %C.type = struct_value () [concrete] // CHECK:STDOUT: %C.5a3: type = class_type @C, @C(%T) [template] // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %complete_type: = complete_type_witness %empty_struct_type [concrete] // CHECK:STDOUT: %F.type: type = fn_type @F [concrete] // CHECK:STDOUT: %F: %F.type = struct_value () [concrete] // CHECK:STDOUT: %require_complete: = require_complete_type %C.5a3 [template] // CHECK:STDOUT: %pattern_type.3d5: type = pattern_type %C.5a3 [template] // CHECK:STDOUT: %empty_struct: %empty_struct_type = struct_value () [concrete] // CHECK:STDOUT: %C.val.8b3: %C.5a3 = struct_value () [template] // CHECK:STDOUT: %Destroy.type: type = facet_type <@Destroy> [concrete] // CHECK:STDOUT: %Destroy.lookup_impl_witness: = lookup_impl_witness %C.5a3, @Destroy [template] // CHECK:STDOUT: %Destroy.facet.472: %Destroy.type = facet_value %C.5a3, (%Destroy.lookup_impl_witness) [template] // CHECK:STDOUT: %Destroy.WithSelf.Op.type.7d1: type = fn_type @Destroy.WithSelf.Op, @Destroy.WithSelf(%Destroy.facet.472) [template] // CHECK:STDOUT: %.306: type = fn_type_with_self_type %Destroy.WithSelf.Op.type.7d1, %Destroy.facet.472 [template] // CHECK:STDOUT: %impl.elem0: %.306 = impl_witness_access %Destroy.lookup_impl_witness, element0 [template] // CHECK:STDOUT: %specific_impl_fn: = specific_impl_function %impl.elem0, @Destroy.WithSelf.Op(%Destroy.facet.472) [template] // CHECK:STDOUT: %C.850: type = class_type @C, @C(%empty_struct_type) [concrete] // CHECK:STDOUT: %pattern_type.526: type = pattern_type %C.850 [concrete] // CHECK:STDOUT: %C.val.09d: %C.850 = struct_value () [concrete] // CHECK:STDOUT: %Destroy.Op.type: type = fn_type @Destroy.Op [concrete] // CHECK:STDOUT: %Destroy.Op: %Destroy.Op.type = struct_value () [concrete] // CHECK:STDOUT: %custom_witness.8d7: = custom_witness (%Destroy.Op), @Destroy [concrete] // CHECK:STDOUT: %Destroy.facet.81b: %Destroy.type = facet_value %C.850, (%custom_witness.8d7) [concrete] // CHECK:STDOUT: %Destroy.WithSelf.Op.type.599: type = fn_type @Destroy.WithSelf.Op, @Destroy.WithSelf(%Destroy.facet.81b) [concrete] // CHECK:STDOUT: %.540: type = fn_type_with_self_type %Destroy.WithSelf.Op.type.599, %Destroy.facet.81b [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: %F.decl: %F.type = fn_decl @F [concrete = constants.%F] { // CHECK:STDOUT: %T.patt: %pattern_type.98f = symbolic_binding_pattern T, 0, template [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %.loc6_19.1: type = splice_block %.loc6_19.2 [concrete = type] { // CHECK:STDOUT: // CHECK:STDOUT: %.loc6_19.2: type = type_literal type [concrete = type] // CHECK:STDOUT: } // CHECK:STDOUT: %T.loc6_15.2: type = symbolic_binding T, 0, template [template = %T.loc6_15.1 (constants.%T)] // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic fn @F(%T.loc6_15.2: type) { // CHECK:STDOUT: %T.loc6_15.1: type = symbolic_binding T, 0, template [template = %T.loc6_15.1 (constants.%T)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %C.loc7_20.2: type = class_type @C, @C(%T.loc6_15.1) [template = %C.loc7_20.2 (constants.%C.5a3)] // CHECK:STDOUT: %require_complete: = require_complete_type %C.loc7_20.2 [template = %require_complete (constants.%require_complete)] // CHECK:STDOUT: %pattern_type: type = pattern_type %C.loc7_20.2 [template = %pattern_type (constants.%pattern_type.3d5)] // CHECK:STDOUT: %C.val: @F.%C.loc7_20.2 (%C.5a3) = struct_value () [template = %C.val (constants.%C.val.8b3)] // CHECK:STDOUT: %Destroy.lookup_impl_witness: = lookup_impl_witness %C.loc7_20.2, @Destroy [template = %Destroy.lookup_impl_witness (constants.%Destroy.lookup_impl_witness)] // CHECK:STDOUT: %Destroy.facet: %Destroy.type = facet_value %C.loc7_20.2, (%Destroy.lookup_impl_witness) [template = %Destroy.facet (constants.%Destroy.facet.472)] // CHECK:STDOUT: %Destroy.WithSelf.Op.type: type = fn_type @Destroy.WithSelf.Op, @Destroy.WithSelf(%Destroy.facet) [template = %Destroy.WithSelf.Op.type (constants.%Destroy.WithSelf.Op.type.7d1)] // CHECK:STDOUT: %.loc7_3.2: type = fn_type_with_self_type %Destroy.WithSelf.Op.type, %Destroy.facet [template = %.loc7_3.2 (constants.%.306)] // CHECK:STDOUT: %impl.elem0.loc7_3.2: @F.%.loc7_3.2 (%.306) = impl_witness_access %Destroy.lookup_impl_witness, element0 [template = %impl.elem0.loc7_3.2 (constants.%impl.elem0)] // CHECK:STDOUT: %specific_impl_fn.loc7_3.2: = specific_impl_function %impl.elem0.loc7_3.2, @Destroy.WithSelf.Op(%Destroy.facet) [template = %specific_impl_fn.loc7_3.2 (constants.%specific_impl_fn)] // CHECK:STDOUT: // CHECK:STDOUT: fn() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %v.patt: @F.%pattern_type (%pattern_type.3d5) = ref_binding_pattern v [concrete] // CHECK:STDOUT: %v.var_patt: @F.%pattern_type (%pattern_type.3d5) = var_pattern %v.patt [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %v.var: ref @F.%C.loc7_20.2 (%C.5a3) = var %v.var_patt // CHECK:STDOUT: %.loc7_25.1: %empty_struct_type = struct_literal () [concrete = constants.%empty_struct] // CHECK:STDOUT: %.loc7_25.2: init @F.%C.loc7_20.2 (%C.5a3) to %v.var = class_init () [template = %C.val (constants.%C.val.8b3)] // CHECK:STDOUT: %.loc7_3.1: init @F.%C.loc7_20.2 (%C.5a3) = converted %.loc7_25.1, %.loc7_25.2 [template = %C.val (constants.%C.val.8b3)] // CHECK:STDOUT: assign %v.var, %.loc7_3.1 // CHECK:STDOUT: %.loc7_20: type = splice_block %C.loc7_20.1 [template = %C.loc7_20.2 (constants.%C.5a3)] { // CHECK:STDOUT: %C.ref: %C.type = name_ref C, file.%C.decl [concrete = constants.%C.generic] // CHECK:STDOUT: %T.ref: type = name_ref T, %T.loc6_15.2 [template = %T.loc6_15.1 (constants.%T)] // CHECK:STDOUT: %C.loc7_20.1: type = class_type @C, @C(constants.%T) [template = %C.loc7_20.2 (constants.%C.5a3)] // CHECK:STDOUT: } // CHECK:STDOUT: %v: ref @F.%C.loc7_20.2 (%C.5a3) = ref_binding v, %v.var // CHECK:STDOUT: %impl.elem0.loc7_3.1: @F.%.loc7_3.2 (%.306) = impl_witness_access constants.%Destroy.lookup_impl_witness, element0 [template = %impl.elem0.loc7_3.2 (constants.%impl.elem0)] // CHECK:STDOUT: %bound_method.loc7_3.1: = bound_method %v.var, %impl.elem0.loc7_3.1 // CHECK:STDOUT: %specific_impl_fn.loc7_3.1: = specific_impl_function %impl.elem0.loc7_3.1, @Destroy.WithSelf.Op(constants.%Destroy.facet.472) [template = %specific_impl_fn.loc7_3.2 (constants.%specific_impl_fn)] // CHECK:STDOUT: %bound_method.loc7_3.2: = bound_method %v.var, %specific_impl_fn.loc7_3.1 // CHECK:STDOUT: %Destroy.WithSelf.Op.call: init %empty_tuple.type = call %bound_method.loc7_3.2(%v.var) // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @Destroy.Op(%self.param: ref %C.850) = "no_op"; // CHECK:STDOUT: // CHECK:STDOUT: specific @F(constants.%T) { // CHECK:STDOUT: %T.loc6_15.1 => constants.%T // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @F(constants.%empty_struct_type) { // CHECK:STDOUT: %T.loc6_15.1 => constants.%empty_struct_type // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %C.loc7_20.2 => constants.%C.850 // CHECK:STDOUT: %require_complete => constants.%complete_type // CHECK:STDOUT: %pattern_type => constants.%pattern_type.526 // CHECK:STDOUT: %C.val => constants.%C.val.09d // CHECK:STDOUT: %Destroy.lookup_impl_witness => constants.%custom_witness.8d7 // CHECK:STDOUT: %Destroy.facet => constants.%Destroy.facet.81b // CHECK:STDOUT: %Destroy.WithSelf.Op.type => constants.%Destroy.WithSelf.Op.type.599 // CHECK:STDOUT: %.loc7_3.2 => constants.%.540 // CHECK:STDOUT: %impl.elem0.loc7_3.2 => constants.%Destroy.Op // CHECK:STDOUT: %specific_impl_fn.loc7_3.2 => constants.%Destroy.Op // CHECK:STDOUT: } // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/class/export_name.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/none.carbon // TODO: Add ranges and switch to "--dump-sem-ir-ranges=only". // EXTRA-ARGS: --dump-sem-ir-ranges=if-present // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/class/export_name.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/class/export_name.carbon // ============================================================================ // Setup files // ============================================================================ // --- base.carbon library "[[@TEST_NAME]]"; class C {} // --- export.carbon library "[[@TEST_NAME]]"; import library "base"; export C; // ============================================================================ // Test files // ============================================================================ // --- use_export.carbon library "[[@TEST_NAME]]"; import library "export"; var c: C = {}; // CHECK:STDOUT: --- base.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %C: type = class_type @C [concrete] // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %complete_type: = complete_type_witness %empty_struct_type [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .C = %C.decl // CHECK:STDOUT: } // CHECK:STDOUT: %C.decl: type = class_decl @C [concrete = constants.%C] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @C { // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%C // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- export.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %C: type = class_type @C [concrete] // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %complete_type: = complete_type_witness %empty_struct_type [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Main.C: type = import_ref Main//base, C, loaded [concrete = constants.%C] // CHECK:STDOUT: %Main.import_ref.8f2: = import_ref Main//base, loc4_10, loaded [concrete = constants.%complete_type] // CHECK:STDOUT: %Main.import_ref.743 = import_ref Main//base, inst{{[0-9A-F]+}} [no loc], unloaded // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .C = %C // CHECK:STDOUT: } // CHECK:STDOUT: %default.import = import // CHECK:STDOUT: %C: type = export C, imports.%Main.C [concrete = constants.%C] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @C [from "base.carbon"] { // CHECK:STDOUT: complete_type_witness = imports.%Main.import_ref.8f2 // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = imports.%Main.import_ref.743 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- use_export.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %C: type = class_type @C [concrete] // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %complete_type: = complete_type_witness %empty_struct_type [concrete] // CHECK:STDOUT: %pattern_type: type = pattern_type %C [concrete] // CHECK:STDOUT: %empty_struct: %empty_struct_type = struct_value () [concrete] // CHECK:STDOUT: %C.val: %C = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Main.C: type = import_ref Main//export, C, loaded [concrete = constants.%C] // CHECK:STDOUT: %Main.import_ref.8db: = import_ref Main//export, inst{{[0-9A-F]+}} [indirect], loaded [concrete = constants.%complete_type] // CHECK:STDOUT: %Main.import_ref.a60 = import_ref Main//export, inst{{[0-9A-F]+}} [indirect], unloaded // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .C = imports.%Main.C // CHECK:STDOUT: .c = %c // CHECK:STDOUT: } // CHECK:STDOUT: %default.import = import // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %c.patt: %pattern_type = ref_binding_pattern c [concrete] // CHECK:STDOUT: %c.var_patt: %pattern_type = var_pattern %c.patt [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %c.var: ref %C = var %c.var_patt [concrete] // CHECK:STDOUT: %C.ref: type = name_ref C, imports.%Main.C [concrete = constants.%C] // CHECK:STDOUT: %c: ref %C = ref_binding c, %c.var [concrete = %c.var] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @C [from "export.carbon"] { // CHECK:STDOUT: complete_type_witness = imports.%Main.import_ref.8db // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = imports.%Main.import_ref.a60 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @__global_init() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %.loc6_13.1: %empty_struct_type = struct_literal () [concrete = constants.%empty_struct] // CHECK:STDOUT: %.loc6_13.2: init %C to file.%c.var = class_init () [concrete = constants.%C.val] // CHECK:STDOUT: %.loc6_1: init %C = converted %.loc6_13.1, %.loc6_13.2 [concrete = constants.%C.val] // CHECK:STDOUT: assign file.%c.var, %.loc6_1 // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/class/extern.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/none.carbon // TODO: Add ranges and switch to "--dump-sem-ir-ranges=only". // EXTRA-ARGS: --dump-sem-ir-ranges=if-present // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/class/extern.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/class/extern.carbon // ============================================================================ // Setup files // ============================================================================ // --- decl.carbon library "[[@TEST_NAME]]"; class C; // --- extern_decl.carbon library "[[@TEST_NAME]]"; extern class C; // --- extern_decl_copy.carbon library "[[@TEST_NAME]]"; extern class C; // --- def.carbon library "[[@TEST_NAME]]"; class C {} // ============================================================================ // Test files // ============================================================================ // --- fail_decl_fn_in_extern.carbon library "[[@TEST_NAME]]"; extern class C; // CHECK:STDERR: fail_decl_fn_in_extern.carbon:[[@LINE+7]]:4: error: cannot declare a member of incomplete class `C` [QualifiedDeclInIncompleteClassScope] // CHECK:STDERR: fn C.F(); // CHECK:STDERR: ^ // CHECK:STDERR: fail_decl_fn_in_extern.carbon:[[@LINE-4]]:1: note: class was forward declared here [ClassForwardDeclaredHere] // CHECK:STDERR: extern class C; // CHECK:STDERR: ^~~~~~~~~~~~~~~ // CHECK:STDERR: fn C.F(); // --- extern_def.carbon library "[[@TEST_NAME]]"; extern class C {} // --- fail_extern_decl_after_extern_decl.carbon library "[[@TEST_NAME]]"; extern class C; // CHECK:STDERR: fail_extern_decl_after_extern_decl.carbon:[[@LINE+7]]:1: error: redeclaration of `class C` is redundant [RedeclRedundant] // CHECK:STDERR: extern class C; // CHECK:STDERR: ^~~~~~~~~~~~~~~ // CHECK:STDERR: fail_extern_decl_after_extern_decl.carbon:[[@LINE-4]]:1: note: previously declared here [RedeclPrevDecl] // CHECK:STDERR: extern class C; // CHECK:STDERR: ^~~~~~~~~~~~~~~ // CHECK:STDERR: extern class C; // --- fail_decl_after_extern_decl.carbon library "[[@TEST_NAME]]"; extern class C; // CHECK:STDERR: fail_decl_after_extern_decl.carbon:[[@LINE+7]]:1: error: redeclaration of `class C` is redundant [RedeclRedundant] // CHECK:STDERR: class C; // CHECK:STDERR: ^~~~~~~~ // CHECK:STDERR: fail_decl_after_extern_decl.carbon:[[@LINE-4]]:1: note: previously declared here [RedeclPrevDecl] // CHECK:STDERR: extern class C; // CHECK:STDERR: ^~~~~~~~~~~~~~~ // CHECK:STDERR: class C; // --- fail_extern_member_class.carbon library "[[@TEST_NAME]]"; class C { // CHECK:STDERR: fail_extern_member_class.carbon:[[@LINE+4]]:3: error: `extern` not allowed; requires file or namespace scope [ModifierExternNotAllowed] // CHECK:STDERR: extern class D; // CHECK:STDERR: ^~~~~~ // CHECK:STDERR: extern class D; } // --- fail_def_after_extern_decl.carbon library "[[@TEST_NAME]]"; extern class C; // CHECK:STDERR: fail_def_after_extern_decl.carbon:[[@LINE+7]]:1: error: redeclarations of `class C` must match use of `extern` [RedeclExternMismatch] // CHECK:STDERR: class C {} // CHECK:STDERR: ^~~~~~~~~ // CHECK:STDERR: fail_def_after_extern_decl.carbon:[[@LINE-4]]:1: note: previously declared here [RedeclPrevDecl] // CHECK:STDERR: extern class C; // CHECK:STDERR: ^~~~~~~~~~~~~~~ // CHECK:STDERR: class C {} // --- fail_extern_decl_after_decl.carbon library "[[@TEST_NAME]]"; class C; // CHECK:STDERR: fail_extern_decl_after_decl.carbon:[[@LINE+7]]:1: error: redeclaration of `class C` is redundant [RedeclRedundant] // CHECK:STDERR: extern class C; // CHECK:STDERR: ^~~~~~~~~~~~~~~ // CHECK:STDERR: fail_extern_decl_after_decl.carbon:[[@LINE-4]]:1: note: previously declared here [RedeclPrevDecl] // CHECK:STDERR: class C; // CHECK:STDERR: ^~~~~~~~ // CHECK:STDERR: extern class C; // --- fail_import_extern_decl_then_decl.carbon library "[[@TEST_NAME]]"; // CHECK:STDERR: fail_import_extern_decl_then_decl.carbon:[[@LINE+9]]:1: in import [InImport] // CHECK:STDERR: decl.carbon:4:1: error: duplicate name `C` being declared in the same scope [NameDeclDuplicate] // CHECK:STDERR: class C; // CHECK:STDERR: ^~~~~~~~ // CHECK:STDERR: fail_import_extern_decl_then_decl.carbon:[[@LINE+5]]:1: in import [InImport] // CHECK:STDERR: extern_decl.carbon:4:1: note: name is previously declared here [NameDeclPrevious] // CHECK:STDERR: extern class C; // CHECK:STDERR: ^~~~~~~~~~~~~~~ // CHECK:STDERR: import library "extern_decl"; import library "decl"; // --- fail_import_decl_then_extern_decl.carbon library "[[@TEST_NAME]]"; // CHECK:STDERR: fail_import_decl_then_extern_decl.carbon:[[@LINE+9]]:1: in import [InImport] // CHECK:STDERR: extern_decl.carbon:4:1: error: duplicate name `C` being declared in the same scope [NameDeclDuplicate] // CHECK:STDERR: extern class C; // CHECK:STDERR: ^~~~~~~~~~~~~~~ // CHECK:STDERR: fail_import_decl_then_extern_decl.carbon:[[@LINE+5]]:1: in import [InImport] // CHECK:STDERR: decl.carbon:4:1: note: name is previously declared here [NameDeclPrevious] // CHECK:STDERR: class C; // CHECK:STDERR: ^~~~~~~~ // CHECK:STDERR: import library "decl"; import library "extern_decl"; // --- fail_import_extern_decl_then_def.carbon library "[[@TEST_NAME]]"; // CHECK:STDERR: fail_import_extern_decl_then_def.carbon:[[@LINE+9]]:1: in import [InImport] // CHECK:STDERR: def.carbon:4:1: error: duplicate name `C` being declared in the same scope [NameDeclDuplicate] // CHECK:STDERR: class C {} // CHECK:STDERR: ^~~~~~~~~ // CHECK:STDERR: fail_import_extern_decl_then_def.carbon:[[@LINE+5]]:1: in import [InImport] // CHECK:STDERR: extern_decl.carbon:4:1: note: name is previously declared here [NameDeclPrevious] // CHECK:STDERR: extern class C; // CHECK:STDERR: ^~~~~~~~~~~~~~~ // CHECK:STDERR: import library "extern_decl"; import library "def"; // --- fail_import_ownership_conflict.carbon library "[[@TEST_NAME]]"; // CHECK:STDERR: fail_import_ownership_conflict.carbon:[[@LINE+18]]:1: in import [InImport] // CHECK:STDERR: decl.carbon:4:1: error: duplicate name `C` being declared in the same scope [NameDeclDuplicate] // CHECK:STDERR: class C; // CHECK:STDERR: ^~~~~~~~ // CHECK:STDERR: fail_import_ownership_conflict.carbon:[[@LINE+14]]:1: in import [InImport] // CHECK:STDERR: extern_decl.carbon:4:1: note: name is previously declared here [NameDeclPrevious] // CHECK:STDERR: extern class C; // CHECK:STDERR: ^~~~~~~~~~~~~~~ // CHECK:STDERR: // CHECK:STDERR: fail_import_ownership_conflict.carbon:[[@LINE+9]]:1: in import [InImport] // CHECK:STDERR: def.carbon:4:1: error: duplicate name `C` being declared in the same scope [NameDeclDuplicate] // CHECK:STDERR: class C {} // CHECK:STDERR: ^~~~~~~~~ // CHECK:STDERR: fail_import_ownership_conflict.carbon:[[@LINE+5]]:1: in import [InImport] // CHECK:STDERR: extern_decl.carbon:4:1: note: name is previously declared here [NameDeclPrevious] // CHECK:STDERR: extern class C; // CHECK:STDERR: ^~~~~~~~~~~~~~~ // CHECK:STDERR: import library "extern_decl"; import library "decl"; import library "def"; // --- fail_todo_import_extern_decl_copy.carbon library "[[@TEST_NAME]]"; // CHECK:STDERR: fail_todo_import_extern_decl_copy.carbon:[[@LINE+9]]:1: in import [InImport] // CHECK:STDERR: extern_decl_copy.carbon:4:1: error: duplicate name `C` being declared in the same scope [NameDeclDuplicate] // CHECK:STDERR: extern class C; // CHECK:STDERR: ^~~~~~~~~~~~~~~ // CHECK:STDERR: fail_todo_import_extern_decl_copy.carbon:[[@LINE+5]]:1: in import [InImport] // CHECK:STDERR: extern_decl.carbon:4:1: note: name is previously declared here [NameDeclPrevious] // CHECK:STDERR: extern class C; // CHECK:STDERR: ^~~~~~~~~~~~~~~ // CHECK:STDERR: import library "extern_decl"; import library "extern_decl_copy"; // --- fail_extern_decl_after_import_extern_decl.carbon library "[[@TEST_NAME]]"; import library "extern_decl"; // CHECK:STDERR: fail_extern_decl_after_import_extern_decl.carbon:[[@LINE+8]]:1: error: redeclaration of `class C` is redundant [RedeclRedundant] // CHECK:STDERR: extern class C; // CHECK:STDERR: ^~~~~~~~~~~~~~~ // CHECK:STDERR: fail_extern_decl_after_import_extern_decl.carbon:[[@LINE-5]]:1: in import [InImport] // CHECK:STDERR: extern_decl.carbon:4:1: note: previously declared here [RedeclPrevDecl] // CHECK:STDERR: extern class C; // CHECK:STDERR: ^~~~~~~~~~~~~~~ // CHECK:STDERR: extern class C; // --- fail_decl_after_import_extern_decl.carbon library "[[@TEST_NAME]]"; import library "decl"; // CHECK:STDERR: fail_decl_after_import_extern_decl.carbon:[[@LINE+8]]:1: error: redeclarations of `class C` must match use of `extern` [RedeclExternMismatch] // CHECK:STDERR: extern class C; // CHECK:STDERR: ^~~~~~~~~~~~~~~ // CHECK:STDERR: fail_decl_after_import_extern_decl.carbon:[[@LINE-5]]:1: in import [InImport] // CHECK:STDERR: decl.carbon:4:1: note: previously declared here [RedeclPrevDecl] // CHECK:STDERR: class C; // CHECK:STDERR: ^~~~~~~~ // CHECK:STDERR: extern class C; // --- fail_def_after_import_extern_decl.carbon library "[[@TEST_NAME]]"; import library "def"; // CHECK:STDERR: fail_def_after_import_extern_decl.carbon:[[@LINE+8]]:1: error: redeclarations of `class C` must match use of `extern` [RedeclExternMismatch] // CHECK:STDERR: extern class C; // CHECK:STDERR: ^~~~~~~~~~~~~~~ // CHECK:STDERR: fail_def_after_import_extern_decl.carbon:[[@LINE-5]]:1: in import [InImport] // CHECK:STDERR: def.carbon:4:1: note: previously declared here [RedeclPrevDecl] // CHECK:STDERR: class C {} // CHECK:STDERR: ^~~~~~~~~ // CHECK:STDERR: extern class C; // --- fail_extern_decl_after_import_def.carbon library "[[@TEST_NAME]]"; import library "def"; // CHECK:STDERR: fail_extern_decl_after_import_def.carbon:[[@LINE+8]]:1: error: redeclarations of `class C` must match use of `extern` [RedeclExternMismatch] // CHECK:STDERR: extern class C; // CHECK:STDERR: ^~~~~~~~~~~~~~~ // CHECK:STDERR: fail_extern_decl_after_import_def.carbon:[[@LINE-5]]:1: in import [InImport] // CHECK:STDERR: def.carbon:4:1: note: previously declared here [RedeclPrevDecl] // CHECK:STDERR: class C {} // CHECK:STDERR: ^~~~~~~~~ // CHECK:STDERR: extern class C; // CHECK:STDOUT: --- decl.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %C: type = class_type @C [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .C = %C.decl // CHECK:STDOUT: } // CHECK:STDOUT: %C.decl: type = class_decl @C [concrete = constants.%C] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @C; // CHECK:STDOUT: // CHECK:STDOUT: --- extern_decl.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %C: type = class_type @C [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .C = %C.decl // CHECK:STDOUT: } // CHECK:STDOUT: %C.decl: type = class_decl @C [concrete = constants.%C] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @C; // CHECK:STDOUT: // CHECK:STDOUT: --- extern_decl_copy.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %C: type = class_type @C [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .C = %C.decl // CHECK:STDOUT: } // CHECK:STDOUT: %C.decl: type = class_decl @C [concrete = constants.%C] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @C; // CHECK:STDOUT: // CHECK:STDOUT: --- def.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %C: type = class_type @C [concrete] // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %complete_type: = complete_type_witness %empty_struct_type [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .C = %C.decl // CHECK:STDOUT: } // CHECK:STDOUT: %C.decl: type = class_decl @C [concrete = constants.%C] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @C { // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%C // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- fail_decl_fn_in_extern.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %C: type = class_type @C [concrete] // CHECK:STDOUT: %F.type: type = fn_type @F [concrete] // CHECK:STDOUT: %F: %F.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .C = %C.decl // CHECK:STDOUT: } // CHECK:STDOUT: %C.decl: type = class_decl @C [concrete = constants.%C] {} {} // CHECK:STDOUT: %F.decl: %F.type = fn_decl @F [concrete = constants.%F] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @C; // CHECK:STDOUT: // CHECK:STDOUT: fn @F(); // CHECK:STDOUT: // CHECK:STDOUT: --- extern_def.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %C: type = class_type @C [concrete] // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %complete_type: = complete_type_witness %empty_struct_type [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .C = %C.decl // CHECK:STDOUT: } // CHECK:STDOUT: %C.decl: type = class_decl @C [concrete = constants.%C] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @C { // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%C // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- fail_extern_decl_after_extern_decl.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %C: type = class_type @C [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .C = %C.decl.loc4 // CHECK:STDOUT: } // CHECK:STDOUT: %C.decl.loc4: type = class_decl @C [concrete = constants.%C] {} {} // CHECK:STDOUT: %C.decl.loc12: type = class_decl @C [concrete = constants.%C] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @C; // CHECK:STDOUT: // CHECK:STDOUT: --- fail_decl_after_extern_decl.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %C: type = class_type @C [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .C = %C.decl.loc12 // CHECK:STDOUT: } // CHECK:STDOUT: %C.decl.loc4: type = class_decl @C [concrete = constants.%C] {} {} // CHECK:STDOUT: %C.decl.loc12: type = class_decl @C [concrete = constants.%C] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @C; // CHECK:STDOUT: // CHECK:STDOUT: --- fail_extern_member_class.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %C: type = class_type @C [concrete] // CHECK:STDOUT: %D: type = class_type @D [concrete] // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %complete_type: = complete_type_witness %empty_struct_type [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .C = %C.decl // CHECK:STDOUT: } // CHECK:STDOUT: %C.decl: type = class_decl @C [concrete = constants.%C] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @C { // CHECK:STDOUT: %D.decl: type = class_decl @D [concrete = constants.%D] {} {} // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%C // CHECK:STDOUT: .D = %D.decl // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @D; // CHECK:STDOUT: // CHECK:STDOUT: --- fail_def_after_extern_decl.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %C: type = class_type @C [concrete] // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %complete_type: = complete_type_witness %empty_struct_type [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .C = %C.decl.loc12 // CHECK:STDOUT: } // CHECK:STDOUT: %C.decl.loc4: type = class_decl @C [concrete = constants.%C] {} {} // CHECK:STDOUT: %C.decl.loc12: type = class_decl @C [concrete = constants.%C] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @C { // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%C // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- fail_extern_decl_after_decl.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %C: type = class_type @C [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .C = %C.decl.loc4 // CHECK:STDOUT: } // CHECK:STDOUT: %C.decl.loc4: type = class_decl @C [concrete = constants.%C] {} {} // CHECK:STDOUT: %C.decl.loc12: type = class_decl @C [concrete = constants.%C] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @C; // CHECK:STDOUT: // CHECK:STDOUT: --- fail_import_extern_decl_then_decl.carbon // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Main.C = import_ref Main//extern_decl, C, unloaded // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .C = imports.%Main.C // CHECK:STDOUT: } // CHECK:STDOUT: %default.import = import // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- fail_import_decl_then_extern_decl.carbon // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Main.C = import_ref Main//decl, C, unloaded // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .C = imports.%Main.C // CHECK:STDOUT: } // CHECK:STDOUT: %default.import = import // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- fail_import_extern_decl_then_def.carbon // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Main.C = import_ref Main//extern_decl, C, unloaded // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .C = imports.%Main.C // CHECK:STDOUT: } // CHECK:STDOUT: %default.import = import // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- fail_import_ownership_conflict.carbon // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Main.C = import_ref Main//extern_decl, C, unloaded // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .C = imports.%Main.C // CHECK:STDOUT: } // CHECK:STDOUT: %default.import = import // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- fail_todo_import_extern_decl_copy.carbon // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Main.C = import_ref Main//extern_decl, C, unloaded // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .C = imports.%Main.C // CHECK:STDOUT: } // CHECK:STDOUT: %default.import = import // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- fail_extern_decl_after_import_extern_decl.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %C: type = class_type @C [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .C = %C.decl // CHECK:STDOUT: } // CHECK:STDOUT: %default.import = import // CHECK:STDOUT: %C.decl: type = class_decl @C [concrete = constants.%C] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @C; // CHECK:STDOUT: // CHECK:STDOUT: --- fail_decl_after_import_extern_decl.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %C: type = class_type @C [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .C = %C.decl // CHECK:STDOUT: } // CHECK:STDOUT: %default.import = import // CHECK:STDOUT: %C.decl: type = class_decl @C [concrete = constants.%C] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @C; // CHECK:STDOUT: // CHECK:STDOUT: --- fail_def_after_import_extern_decl.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %C: type = class_type @C [concrete] // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %complete_type: = complete_type_witness %empty_struct_type [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Main.import_ref.8f2: = import_ref Main//def, loc4_10, loaded [concrete = constants.%complete_type] // CHECK:STDOUT: %Main.import_ref.743 = import_ref Main//def, inst{{[0-9A-F]+}} [no loc], unloaded // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .C = %C.decl // CHECK:STDOUT: } // CHECK:STDOUT: %default.import = import // CHECK:STDOUT: %C.decl: type = class_decl @C [concrete = constants.%C] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @C { // CHECK:STDOUT: complete_type_witness = imports.%Main.import_ref.8f2 // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = imports.%Main.import_ref.743 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- fail_extern_decl_after_import_def.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %C: type = class_type @C [concrete] // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %complete_type: = complete_type_witness %empty_struct_type [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Main.import_ref.8f2: = import_ref Main//def, loc4_10, loaded [concrete = constants.%complete_type] // CHECK:STDOUT: %Main.import_ref.743 = import_ref Main//def, inst{{[0-9A-F]+}} [no loc], unloaded // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .C = %C.decl // CHECK:STDOUT: } // CHECK:STDOUT: %default.import = import // CHECK:STDOUT: %C.decl: type = class_decl @C [concrete = constants.%C] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @C { // CHECK:STDOUT: complete_type_witness = imports.%Main.import_ref.8f2 // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = imports.%Main.import_ref.743 // CHECK:STDOUT: } // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/class/extern_library.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/none.carbon // TODO: Add ranges and switch to "--dump-sem-ir-ranges=only". // EXTRA-ARGS: --dump-sem-ir-ranges=if-present // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/class/extern_library.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/class/extern_library.carbon // --- fail_todo.carbon // CHECK:STDERR: fail_todo.carbon:[[@LINE+4]]:1: error: semantics TODO: `extern library` [SemanticsTodo] // CHECK:STDERR: extern library "foo" class C; // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: extern library "foo" class C; // CHECK:STDOUT: --- fail_todo.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %C: type = class_type @C [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .C = %C.decl // CHECK:STDOUT: } // CHECK:STDOUT: %C.decl: type = class_decl @C [concrete = constants.%C] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @C; // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/class/fail_compound_type_mismatch.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/int.carbon // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/class/fail_compound_type_mismatch.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/class/fail_compound_type_mismatch.carbon class A { var a: i32; } class B { var b: i32; } fn AccessBInA(a: A) -> i32 { // CHECK:STDERR: fail_compound_type_mismatch.carbon:[[@LINE+7]]:10: error: cannot implicitly convert expression of type `A` to `B` [ConversionFailure] // CHECK:STDERR: return a.(B.b); // CHECK:STDERR: ^~~~~~~ // CHECK:STDERR: fail_compound_type_mismatch.carbon:[[@LINE+4]]:10: note: type `A` does not implement interface `Core.ImplicitAs(B)` [MissingImplInMemberAccessInContext] // CHECK:STDERR: return a.(B.b); // CHECK:STDERR: ^~~~~~~ // CHECK:STDERR: return a.(B.b); } ================================================ FILE: toolchain/check/testdata/class/fail_convert_to_invalid.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/convert.carbon // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/class/fail_convert_to_invalid.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/class/fail_convert_to_invalid.carbon class C { // CHECK:STDERR: fail_convert_to_invalid.carbon:[[@LINE+4]]:10: error: name `NoSuchType` not found [NameNotFound] // CHECK:STDERR: var a: NoSuchType; // CHECK:STDERR: ^~~~~~~~~~ // CHECK:STDERR: var a: NoSuchType; } fn Make() -> C { return {.a = ()}; } ================================================ FILE: toolchain/check/testdata/class/fail_error_recovery.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/convert.carbon // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/class/fail_error_recovery.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/class/fail_error_recovery.carbon // --- fail_virtual_fn_in_invalid_context.carbon // CHECK:STDERR: fail_virtual_fn_in_invalid_context.carbon:[[@LINE+4]]:17: error: name `error_not_found` not found [NameNotFound] // CHECK:STDERR: fn F(unused N:! error_not_found) { // CHECK:STDERR: ^~~~~~~~~~~~~~~ // CHECK:STDERR: fn F(unused N:! error_not_found) { base class C { virtual fn Foo[unused self: Self]() {} } base class D { extend base: C; } } ================================================ FILE: toolchain/check/testdata/class/fail_import_misuses.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/convert.carbon // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/class/fail_import_misuses.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/class/fail_import_misuses.carbon // --- a.carbon library "[[@TEST_NAME]]"; class Empty { } class Incomplete; // --- fail_b.carbon library "[[@TEST_NAME]]"; import library "a"; // CHECK:STDERR: fail_b.carbon:[[@LINE+8]]:1: error: redeclaration of `class Empty` is redundant [RedeclRedundant] // CHECK:STDERR: class Empty { // CHECK:STDERR: ^~~~~~~~~~~~~ // CHECK:STDERR: fail_b.carbon:[[@LINE-5]]:1: in import [InImport] // CHECK:STDERR: a.carbon:4:1: note: previously declared here [RedeclPrevDecl] // CHECK:STDERR: class Empty { // CHECK:STDERR: ^~~~~~~~~~~~~ // CHECK:STDERR: class Empty { } // CHECK:STDERR: fail_b.carbon:[[@LINE+8]]:8: error: binding pattern has incomplete type `Incomplete` in name binding declaration [IncompleteTypeInBindingDecl] // CHECK:STDERR: var a: Incomplete; // CHECK:STDERR: ^~~~~~~~~~ // CHECK:STDERR: fail_b.carbon:[[@LINE-16]]:1: in import [InImport] // CHECK:STDERR: a.carbon:7:1: note: class was forward declared here [ClassForwardDeclaredHere] // CHECK:STDERR: class Incomplete; // CHECK:STDERR: ^~~~~~~~~~~~~~~~~ // CHECK:STDERR: var a: Incomplete; ================================================ FILE: toolchain/check/testdata/class/fail_incomplete.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/convert.carbon // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/class/fail_incomplete.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/class/fail_incomplete.carbon // --- fail_forward_decl.carbon library "[[@TEST_NAME]]"; class Class; // CHECK:STDERR: fail_forward_decl.carbon:[[@LINE+7]]:4: error: cannot declare a member of incomplete class `Class` [QualifiedDeclInIncompleteClassScope] // CHECK:STDERR: fn Class.Function() {} // CHECK:STDERR: ^~~~~ // CHECK:STDERR: fail_forward_decl.carbon:[[@LINE-5]]:1: note: class was forward declared here [ClassForwardDeclaredHere] // CHECK:STDERR: class Class; // CHECK:STDERR: ^~~~~~~~~~~~ // CHECK:STDERR: fn Class.Function() {} fn CallClassFunction() { // CHECK:STDERR: fail_forward_decl.carbon:[[@LINE+7]]:3: error: member access into incomplete class `Class` [QualifiedExprInIncompleteClassScope] // CHECK:STDERR: Class.Function(); // CHECK:STDERR: ^~~~~~~~~~~~~~ // CHECK:STDERR: fail_forward_decl.carbon:[[@LINE-15]]:1: note: class was forward declared here [ClassForwardDeclaredHere] // CHECK:STDERR: class Class; // CHECK:STDERR: ^~~~~~~~~~~~ // CHECK:STDERR: Class.Function(); } // CHECK:STDERR: fail_forward_decl.carbon:[[@LINE+7]]:17: error: binding pattern has incomplete type `Class` in name binding declaration [IncompleteTypeInBindingDecl] // CHECK:STDERR: var global_var: Class; // CHECK:STDERR: ^~~~~ // CHECK:STDERR: fail_forward_decl.carbon:[[@LINE-25]]:1: note: class was forward declared here [ClassForwardDeclaredHere] // CHECK:STDERR: class Class; // CHECK:STDERR: ^~~~~~~~~~~~ // CHECK:STDERR: var global_var: Class; // CHECK:STDERR: fail_forward_decl.carbon:[[@LINE+7]]:27: error: function returns incomplete type `Class` [IncompleteTypeInFunctionReturnType] // CHECK:STDERR: fn ConvertFromStruct() -> Class { return {}; } // CHECK:STDERR: ^~~~~ // CHECK:STDERR: fail_forward_decl.carbon:[[@LINE-34]]:1: note: class was forward declared here [ClassForwardDeclaredHere] // CHECK:STDERR: class Class; // CHECK:STDERR: ^~~~~~~~~~~~ // CHECK:STDERR: fn ConvertFromStruct() -> Class { return {}; } fn G(p: Class*) -> () { // CHECK:STDERR: fail_forward_decl.carbon:[[@LINE+7]]:10: error: member access into object of incomplete type `Class` [IncompleteTypeInMemberAccess] // CHECK:STDERR: return p->n; // CHECK:STDERR: ^~~~ // CHECK:STDERR: fail_forward_decl.carbon:[[@LINE-44]]:1: note: class was forward declared here [ClassForwardDeclaredHere] // CHECK:STDERR: class Class; // CHECK:STDERR: ^~~~~~~~~~~~ // CHECK:STDERR: return p->n; } fn MemberAccess(p: Class*) -> () { // CHECK:STDERR: fail_forward_decl.carbon:[[@LINE+7]]:11: error: member access into object of incomplete type `Class` [IncompleteTypeInMemberAccess] // CHECK:STDERR: return (*p).n; // CHECK:STDERR: ^~ // CHECK:STDERR: fail_forward_decl.carbon:[[@LINE-55]]:1: note: class was forward declared here [ClassForwardDeclaredHere] // CHECK:STDERR: class Class; // CHECK:STDERR: ^~~~~~~~~~~~ // CHECK:STDERR: return (*p).n; } // CHECK:STDERR: fail_forward_decl.carbon:[[@LINE+7]]:23: error: function returns incomplete type `Class` [IncompleteTypeInFunctionReturnType] // CHECK:STDERR: fn Copy(p: Class*) -> Class { // CHECK:STDERR: ^~~~~ // CHECK:STDERR: fail_forward_decl.carbon:[[@LINE-65]]:1: note: class was forward declared here [ClassForwardDeclaredHere] // CHECK:STDERR: class Class; // CHECK:STDERR: ^~~~~~~~~~~~ // CHECK:STDERR: fn Copy(p: Class*) -> Class { return *p; } fn Let(p: Class*) { // CHECK:STDERR: fail_forward_decl.carbon:[[@LINE+7]]:17: error: binding pattern has incomplete type `Class` in name binding declaration [IncompleteTypeInBindingDecl] // CHECK:STDERR: let unused c: Class = *p; // CHECK:STDERR: ^~~~~ // CHECK:STDERR: fail_forward_decl.carbon:[[@LINE-77]]:1: note: class was forward declared here [ClassForwardDeclaredHere] // CHECK:STDERR: class Class; // CHECK:STDERR: ^~~~~~~~~~~~ // CHECK:STDERR: let unused c: Class = *p; } fn TakeIncomplete(c: Class); fn ReturnIncomplete() -> Class; fn CallTakeIncomplete(p: Class*) { // CHECK:STDERR: fail_forward_decl.carbon:[[@LINE+10]]:18: error: forming value of incomplete type `Class` [IncompleteTypeInValueConversion] // CHECK:STDERR: TakeIncomplete(*p); // CHECK:STDERR: ^~ // CHECK:STDERR: fail_forward_decl.carbon:[[@LINE-92]]:1: note: class was forward declared here [ClassForwardDeclaredHere] // CHECK:STDERR: class Class; // CHECK:STDERR: ^~~~~~~~~~~~ // CHECK:STDERR: fail_forward_decl.carbon:[[@LINE-11]]:19: note: initializing function parameter [InCallToFunctionParam] // CHECK:STDERR: fn TakeIncomplete(c: Class); // CHECK:STDERR: ^~~~~~~~ // CHECK:STDERR: TakeIncomplete(*p); // CHECK:STDERR: fail_forward_decl.carbon:[[@LINE+10]]:18: error: forming value of incomplete type `Class` [IncompleteTypeInValueConversion] // CHECK:STDERR: TakeIncomplete({}); // CHECK:STDERR: ^~ // CHECK:STDERR: fail_forward_decl.carbon:[[@LINE-104]]:1: note: class was forward declared here [ClassForwardDeclaredHere] // CHECK:STDERR: class Class; // CHECK:STDERR: ^~~~~~~~~~~~ // CHECK:STDERR: fail_forward_decl.carbon:[[@LINE-23]]:19: note: initializing function parameter [InCallToFunctionParam] // CHECK:STDERR: fn TakeIncomplete(c: Class); // CHECK:STDERR: ^~~~~~~~ // CHECK:STDERR: TakeIncomplete({}); } fn CallReturnIncomplete() { // CHECK:STDERR: fail_forward_decl.carbon:[[@LINE+10]]:3: error: function returns incomplete type `Class` [IncompleteTypeInFunctionReturnType] // CHECK:STDERR: ReturnIncomplete(); // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~ // CHECK:STDERR: fail_forward_decl.carbon:[[@LINE-118]]:1: note: class was forward declared here [ClassForwardDeclaredHere] // CHECK:STDERR: class Class; // CHECK:STDERR: ^~~~~~~~~~~~ // CHECK:STDERR: fail_forward_decl.carbon:[[@LINE-35]]:26: note: return type declared here [IncompleteReturnTypeHere] // CHECK:STDERR: fn ReturnIncomplete() -> Class; // CHECK:STDERR: ^~~~~ // CHECK:STDERR: ReturnIncomplete(); } // --- fail_in_definition.carbon library "[[@TEST_NAME]]"; class C { // CHECK:STDERR: fail_in_definition.carbon:[[@LINE+7]]:10: error: field has incomplete type `C` [IncompleteTypeInFieldDecl] // CHECK:STDERR: var c: C; // CHECK:STDERR: ^ // CHECK:STDERR: fail_in_definition.carbon:[[@LINE-4]]:1: note: class is incomplete within its definition [ClassIncompleteWithinDefinition] // CHECK:STDERR: class C { // CHECK:STDERR: ^~~~~~~~~ // CHECK:STDERR: var c: C; } ================================================ FILE: toolchain/check/testdata/class/fail_init.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/int.carbon // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/class/fail_init.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/class/fail_init.carbon class Class { var a: i32; var b: i32; } fn F() { // CHECK:STDERR: fail_init.carbon:[[@LINE+4]]:3: error: cannot initialize class with 2 fields from struct with 1 field [StructInitElementCountMismatch] // CHECK:STDERR: {.a = 1} as Class; // CHECK:STDERR: ^~~~~~~~ // CHECK:STDERR: {.a = 1} as Class; // CHECK:STDERR: fail_init.carbon:[[@LINE+4]]:3: error: missing value for field `b` in struct initialization [StructInitMissingFieldInLiteral] // CHECK:STDERR: {.a = 1, .c = 2} as Class; // CHECK:STDERR: ^~~~~~~~~~~~~~~~ // CHECK:STDERR: {.a = 1, .c = 2} as Class; // CHECK:STDERR: fail_init.carbon:[[@LINE+4]]:3: error: cannot initialize class with 2 fields from struct with 3 fields [StructInitElementCountMismatch] // CHECK:STDERR: {.a = 1, .b = 2, .c = 3} as Class; // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: {.a = 1, .b = 2, .c = 3} as Class; } ================================================ FILE: toolchain/check/testdata/class/fail_memaccess_category.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/convert.carbon // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/class/fail_memaccess_category.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/class/fail_memaccess_category.carbon class A { fn F[ref self: A](); } class B { var a: A; } fn F(s: {.a: A}, b: B) { // `s` has only a value representation, so this must be invalid. // CHECK:STDERR: fail_memaccess_category.carbon:[[@LINE+7]]:3: error: value expression passed to reference parameter [ValueForRefParam] // CHECK:STDERR: s.a.F(); // CHECK:STDERR: ^~~ // CHECK:STDERR: fail_memaccess_category.carbon:[[@LINE-12]]:8: note: initializing function parameter [InCallToFunctionParam] // CHECK:STDERR: fn F[ref self: A](); // CHECK:STDERR: ^~~~~~~~~~~ // CHECK:STDERR: s.a.F(); // `b` has an object representation for `A`, but this is still invalid for // consistency. // CHECK:STDERR: fail_memaccess_category.carbon:[[@LINE+7]]:3: error: value expression passed to reference parameter [ValueForRefParam] // CHECK:STDERR: b.a.F(); // CHECK:STDERR: ^~~ // CHECK:STDERR: fail_memaccess_category.carbon:[[@LINE-23]]:8: note: initializing function parameter [InCallToFunctionParam] // CHECK:STDERR: fn F[ref self: A](); // CHECK:STDERR: ^~~~~~~~~~~ // CHECK:STDERR: b.a.F(); } ================================================ FILE: toolchain/check/testdata/class/fail_member_of_let.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/convert.carbon // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/class/fail_member_of_let.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/class/fail_member_of_let.carbon class Class { fn F() -> (); } // TODO: Use `:!` here once it is available. let T: type = Class; // The class name is required to be written in the same way as in the class // declaration. An expression that evaluates to the class name is not accepted. // CHECK:STDERR: fail_member_of_let.carbon:[[@LINE+7]]:4: error: name qualifiers are only allowed for entities that provide a scope [QualifiedNameInNonScope] // CHECK:STDERR: fn T.F() {} // CHECK:STDERR: ^ // CHECK:STDERR: fail_member_of_let.carbon:[[@LINE-7]]:5: note: referenced non-scope entity declared here [QualifiedNameNonScopeEntity] // CHECK:STDERR: let T: type = Class; // CHECK:STDERR: ^ // CHECK:STDERR: fn T.F() {} ================================================ FILE: toolchain/check/testdata/class/fail_modifiers.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/convert.carbon // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/class/fail_modifiers.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/class/fail_modifiers.carbon // CHECK:STDERR: fail_modifiers.carbon:[[@LINE+7]]:9: error: `private` repeated on declaration [ModifierRepeated] // CHECK:STDERR: private private class DuplicatePrivate; // CHECK:STDERR: ^~~~~~~ // CHECK:STDERR: fail_modifiers.carbon:[[@LINE+4]]:1: note: `private` previously appeared here [ModifierPrevious] // CHECK:STDERR: private private class DuplicatePrivate; // CHECK:STDERR: ^~~~~~~ // CHECK:STDERR: private private class DuplicatePrivate; // CHECK:STDERR: fail_modifiers.carbon:[[@LINE+4]]:1: error: `abstract` not allowed on `class` forward declaration, only definition [ModifierOnlyAllowedOnDefinition] // CHECK:STDERR: abstract class AbstractDecl; // CHECK:STDERR: ^~~~~~~~ // CHECK:STDERR: abstract class AbstractDecl; // CHECK:STDERR: fail_modifiers.carbon:[[@LINE+7]]:9: error: `protected` not allowed on declaration with `private` [ModifierNotAllowedWith] // CHECK:STDERR: private protected class TwoAccess; // CHECK:STDERR: ^~~~~~~~~ // CHECK:STDERR: fail_modifiers.carbon:[[@LINE+4]]:1: note: `private` previously appeared here [ModifierPrevious] // CHECK:STDERR: private protected class TwoAccess; // CHECK:STDERR: ^~~~~~~ // CHECK:STDERR: private protected class TwoAccess; // CHECK:STDERR: fail_modifiers.carbon:[[@LINE+4]]:1: error: `base` not allowed on `class` forward declaration, only definition [ModifierOnlyAllowedOnDefinition] // CHECK:STDERR: base class BaseDecl; // CHECK:STDERR: ^~~~ // CHECK:STDERR: base class BaseDecl; // CHECK:STDERR: fail_modifiers.carbon:[[@LINE+7]]:10: error: `abstract` repeated on declaration [ModifierRepeated] // CHECK:STDERR: abstract abstract class TwoAbstract { } // CHECK:STDERR: ^~~~~~~~ // CHECK:STDERR: fail_modifiers.carbon:[[@LINE+4]]:1: note: `abstract` previously appeared here [ModifierPrevious] // CHECK:STDERR: abstract abstract class TwoAbstract { } // CHECK:STDERR: ^~~~~~~~ // CHECK:STDERR: abstract abstract class TwoAbstract { } // CHECK:STDERR: fail_modifiers.carbon:[[@LINE+15]]:19: error: `base` not allowed on declaration with `virtual` [ModifierNotAllowedWith] // CHECK:STDERR: protected virtual base class Virtual {} // CHECK:STDERR: ^~~~ // CHECK:STDERR: fail_modifiers.carbon:[[@LINE+12]]:11: note: `virtual` previously appeared here [ModifierPrevious] // CHECK:STDERR: protected virtual base class Virtual {} // CHECK:STDERR: ^~~~~~~ // CHECK:STDERR: // CHECK:STDERR: fail_modifiers.carbon:[[@LINE+8]]:1: error: `protected` not allowed; requires class scope [ModifierProtectedNotAllowed] // CHECK:STDERR: protected virtual base class Virtual {} // CHECK:STDERR: ^~~~~~~~~ // CHECK:STDERR: // CHECK:STDERR: fail_modifiers.carbon:[[@LINE+4]]:11: error: `virtual` not allowed on `class` declaration [ModifierNotAllowedOnDeclaration] // CHECK:STDERR: protected virtual base class Virtual {} // CHECK:STDERR: ^~~~~~~ // CHECK:STDERR: protected virtual base class Virtual {} // CHECK:STDERR: fail_modifiers.carbon:[[@LINE+7]]:10: error: `protected` must appear before `abstract` [ModifierMustAppearBefore] // CHECK:STDERR: abstract protected class WrongOrder { } // CHECK:STDERR: ^~~~~~~~~ // CHECK:STDERR: fail_modifiers.carbon:[[@LINE+4]]:1: note: `abstract` previously appeared here [ModifierPrevious] // CHECK:STDERR: abstract protected class WrongOrder { } // CHECK:STDERR: ^~~~~~~~ // CHECK:STDERR: abstract protected class WrongOrder { } // CHECK:STDERR: fail_modifiers.carbon:[[@LINE+7]]:10: error: `base` not allowed on declaration with `abstract` [ModifierNotAllowedWith] // CHECK:STDERR: abstract base class AbstractAndBase {} // CHECK:STDERR: ^~~~ // CHECK:STDERR: fail_modifiers.carbon:[[@LINE+4]]:1: note: `abstract` previously appeared here [ModifierPrevious] // CHECK:STDERR: abstract base class AbstractAndBase {} // CHECK:STDERR: ^~~~~~~~ // CHECK:STDERR: abstract base class AbstractAndBase {} abstract class AbstractWithDefinition { // CHECK:STDERR: fail_modifiers.carbon:[[@LINE+4]]:38: error: definition of `abstract` function [DefinedAbstractFunction] // CHECK:STDERR: abstract fn F[unused self: Self]() {} // CHECK:STDERR: ^ // CHECK:STDERR: abstract fn F[unused self: Self]() {} abstract fn G[unused self: Self](); } // CHECK:STDERR: fail_modifiers.carbon:[[@LINE+4]]:50: error: definition of `abstract` function [DefinedAbstractFunction] // CHECK:STDERR: fn AbstractWithDefinition.G[unused self: Self]() { // CHECK:STDERR: ^ // CHECK:STDERR: fn AbstractWithDefinition.G[unused self: Self]() { } ================================================ FILE: toolchain/check/testdata/class/fail_redeclaration_scope.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/convert.carbon // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/class/fail_redeclaration_scope.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/class/fail_redeclaration_scope.carbon class A; class X { // OK, a different A. class A { class B; } class A.B {} } class A { class B; } class Y { // CHECK:STDERR: fail_redeclaration_scope.carbon:[[@LINE+4]]:9: error: name `A` not found [NameNotFound] // CHECK:STDERR: class A.B {} // CHECK:STDERR: ^ // CHECK:STDERR: class A.B {} } ================================================ FILE: toolchain/check/testdata/class/fail_redefinition.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/convert.carbon // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/class/fail_redefinition.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/class/fail_redefinition.carbon class Class { fn F(); fn H(); fn I() {} } // CHECK:STDERR: fail_redefinition.carbon:[[@LINE+7]]:1: error: redefinition of `class Class` [RedeclRedef] // CHECK:STDERR: class Class { // CHECK:STDERR: ^~~~~~~~~~~~~ // CHECK:STDERR: fail_redefinition.carbon:[[@LINE-9]]:1: note: previously defined here [RedeclPrevDef] // CHECK:STDERR: class Class { // CHECK:STDERR: ^~~~~~~~~~~~~ // CHECK:STDERR: class Class { fn G(); fn H(); fn I() {} } fn Class.F() {} // CHECK:STDERR: fail_redefinition.carbon:[[@LINE+4]]:10: error: out-of-line declaration requires a declaration in scoped entity [QualifiedDeclOutsideScopeEntity] // CHECK:STDERR: fn Class.G() {} // CHECK:STDERR: ^ // CHECK:STDERR: fn Class.G() {} fn Class.H() {} // CHECK:STDERR: fail_redefinition.carbon:[[@LINE+7]]:1: error: redefinition of `fn I` [RedeclRedef] // CHECK:STDERR: fn Class.I() {} // CHECK:STDERR: ^~~~~~~~~~~~~~ // CHECK:STDERR: fail_redefinition.carbon:[[@LINE-26]]:3: note: previously defined here [RedeclPrevDef] // CHECK:STDERR: fn I() {} // CHECK:STDERR: ^~~~~~~~ // CHECK:STDERR: fn Class.I() {} ================================================ FILE: toolchain/check/testdata/class/fail_scope.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/convert.carbon // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/class/fail_scope.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/class/fail_scope.carbon class Class { fn F() -> () { return (); } } fn G() -> () { // CHECK:STDERR: fail_scope.carbon:[[@LINE+4]]:10: error: name `F` not found [NameNotFound] // CHECK:STDERR: return F(); // CHECK:STDERR: ^ // CHECK:STDERR: return F(); } ================================================ FILE: toolchain/check/testdata/class/fail_unknown_member.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/int.carbon // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/class/fail_unknown_member.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/class/fail_unknown_member.carbon class Class { var n: i32; } fn G(c: Class) -> i32 { // TODO: Mention the scope in which we looked for the name. // CHECK:STDERR: fail_unknown_member.carbon:[[@LINE+4]]:10: error: member name `something` not found in `Class` [MemberNameNotFoundInInstScope] // CHECK:STDERR: return c.something; // CHECK:STDERR: ^~~~~~~~~~~ // CHECK:STDERR: return c.something; } ================================================ FILE: toolchain/check/testdata/class/field/comp_time_field.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/none.carbon // TODO: Add ranges and switch to "--dump-sem-ir-ranges=only". // EXTRA-ARGS: --dump-sem-ir-ranges=if-present // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/class/field/comp_time_field.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/class/field/comp_time_field.carbon // --- fail_let.carbon library "[[@TEST_NAME]]"; class Class { // CHECK:STDERR: fail_let.carbon:[[@LINE+4]]:7: error: semantics TODO: ``let` compile time binding outside function or interface` [SemanticsTodo] // CHECK:STDERR: let A:! type = Class; // CHECK:STDERR: ^~~~~~~~ // CHECK:STDERR: let A:! type = Class; // CHECK:STDERR: fail_let.carbon:[[@LINE+4]]:7: error: semantics TODO: ``let` compile time binding outside function or interface` [SemanticsTodo] // CHECK:STDERR: let template B:! type = Class; // CHECK:STDERR: ^~~~~~~~~~~~~~~~~ // CHECK:STDERR: let template B:! type = Class; } // --- fail_var.carbon library "[[@TEST_NAME]]"; class Class { // CHECK:STDERR: fail_var.carbon:[[@LINE+8]]:8: error: expected `:` in field declaration [ExpectedFieldColon] // CHECK:STDERR: var C:! type = Class; // CHECK:STDERR: ^~ // CHECK:STDERR: // CHECK:STDERR: fail_var.carbon:[[@LINE+4]]:3: error: semantics TODO: `handle invalid parse trees in `check`` [SemanticsTodo] // CHECK:STDERR: var C:! type = Class; // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: var C:! type = Class; // CHECK:STDERR: fail_var.carbon:[[@LINE+4]]:7: error: expected identifier in field declaration [ExpectedFieldIdentifier] // CHECK:STDERR: var template D:! type = Class; // CHECK:STDERR: ^~~~~~~~ // CHECK:STDERR: var template D:! type = Class; } var x: Class = {}; // CHECK:STDOUT: --- fail_let.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %Class: type = class_type @Class [concrete] // CHECK:STDOUT: %type: type = facet_type [concrete] // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self] // CHECK:STDOUT: %pattern_type: type = pattern_type type [concrete] // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %complete_type: = complete_type_witness %empty_struct_type [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Class = %Class.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Class.decl: type = class_decl @Class [concrete = constants.%Class] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @Class { // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %A.patt: %pattern_type = value_binding_pattern A [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %Class.ref.loc9: type = name_ref Class, file.%Class.decl [concrete = constants.%Class] // CHECK:STDOUT: %.loc9_11.1: type = splice_block %.loc9_11.2 [concrete = type] { // CHECK:STDOUT: %.Self.2: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %.loc9_11.2: type = type_literal type [concrete = type] // CHECK:STDOUT: } // CHECK:STDOUT: %A: type = value_binding A, %Class.ref.loc9 // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %B.patt: %pattern_type = value_binding_pattern B [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %Class.ref.loc15: type = name_ref Class, file.%Class.decl [concrete = constants.%Class] // CHECK:STDOUT: %.loc15_20.1: type = splice_block %.loc15_20.2 [concrete = type] { // CHECK:STDOUT: %.Self.1: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %.loc15_20.2: type = type_literal type [concrete = type] // CHECK:STDOUT: } // CHECK:STDOUT: %B: type = value_binding B, %Class.ref.loc15 // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%Class // CHECK:STDOUT: .A = %A // CHECK:STDOUT: .Class = // CHECK:STDOUT: .B = %B // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- fail_var.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %Class: type = class_type @Class [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @Class { // CHECK:STDOUT: complete_type_witness = invalid // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%Class // CHECK:STDOUT: } // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/class/field/compound_field.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/int.carbon // TODO: Add ranges and switch to "--dump-sem-ir-ranges=only". // EXTRA-ARGS: --dump-sem-ir-ranges=if-present // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/class/field/compound_field.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/class/field/compound_field.carbon base class Base { var a: i32; var b: i32; var c: i32; } class Derived { extend base: Base; var d: i32; var e: i32; } fn AccessDerived(d: Derived) -> i32 { return d.(Derived.d); } fn AccessBase(d: Derived) -> i32 { return d.(Base.b); } fn AccessDerivedIndirect(p: Derived*) -> i32* { return &p->(Derived.d); } fn AccessBaseIndirect(p: Derived*) -> i32* { return &p->(Base.b); } // CHECK:STDOUT: --- compound_field.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %Base: type = class_type @Base [concrete] // CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [concrete] // CHECK:STDOUT: %Int.type: type = generic_class_type @Int [concrete] // CHECK:STDOUT: %Int.generic: %Int.type = struct_value () [concrete] // CHECK:STDOUT: %N: Core.IntLiteral = symbolic_binding N, 0 [symbolic] // CHECK:STDOUT: %i32: type = class_type @Int, @Int(%int_32) [concrete] // CHECK:STDOUT: %Base.elem: type = unbound_element_type %Base, %i32 [concrete] // CHECK:STDOUT: %struct_type.a.b.c: type = struct_type {.a: %i32, .b: %i32, .c: %i32} [concrete] // CHECK:STDOUT: %complete_type.ebc: = complete_type_witness %struct_type.a.b.c [concrete] // CHECK:STDOUT: %Derived: type = class_type @Derived [concrete] // CHECK:STDOUT: %Derived.elem.029: type = unbound_element_type %Derived, %Base [concrete] // CHECK:STDOUT: %Derived.elem.530: type = unbound_element_type %Derived, %i32 [concrete] // CHECK:STDOUT: %struct_type.base.d.e.b4b: type = struct_type {.base: %Base, .d: %i32, .e: %i32} [concrete] // CHECK:STDOUT: %complete_type.ea9: = complete_type_witness %struct_type.base.d.e.b4b [concrete] // CHECK:STDOUT: %pattern_type.9f6: type = pattern_type %Derived [concrete] // CHECK:STDOUT: %.ff5: Core.Form = init_form %i32 [concrete] // CHECK:STDOUT: %pattern_type.7ce: type = pattern_type %i32 [concrete] // CHECK:STDOUT: %AccessDerived.type: type = fn_type @AccessDerived [concrete] // CHECK:STDOUT: %AccessDerived: %AccessDerived.type = struct_value () [concrete] // CHECK:STDOUT: %Copy.type: type = facet_type <@Copy> [concrete] // CHECK:STDOUT: %Int.as.Copy.impl.Op.type.824: type = fn_type @Int.as.Copy.impl.Op, @Int.as.Copy.impl(%N) [symbolic] // CHECK:STDOUT: %Int.as.Copy.impl.Op.9b9: %Int.as.Copy.impl.Op.type.824 = struct_value () [symbolic] // CHECK:STDOUT: %T.67d: type = symbolic_binding T, 0 [symbolic] // CHECK:STDOUT: %ptr.as.Copy.impl.Op.type.2d4: type = fn_type @ptr.as.Copy.impl.Op, @ptr.as.Copy.impl(%T.67d) [symbolic] // CHECK:STDOUT: %ptr.as.Copy.impl.Op.74e: %ptr.as.Copy.impl.Op.type.2d4 = struct_value () [symbolic] // CHECK:STDOUT: %Copy.impl_witness.f17: = impl_witness imports.%Copy.impl_witness_table.e76, @Int.as.Copy.impl(%int_32) [concrete] // CHECK:STDOUT: %Int.as.Copy.impl.Op.type.546: type = fn_type @Int.as.Copy.impl.Op, @Int.as.Copy.impl(%int_32) [concrete] // CHECK:STDOUT: %Int.as.Copy.impl.Op.664: %Int.as.Copy.impl.Op.type.546 = struct_value () [concrete] // CHECK:STDOUT: %Copy.facet.de4: %Copy.type = facet_value %i32, (%Copy.impl_witness.f17) [concrete] // CHECK:STDOUT: %Copy.WithSelf.Op.type.081: type = fn_type @Copy.WithSelf.Op, @Copy.WithSelf(%Copy.facet.de4) [concrete] // CHECK:STDOUT: %.8e2: type = fn_type_with_self_type %Copy.WithSelf.Op.type.081, %Copy.facet.de4 [concrete] // CHECK:STDOUT: %Int.as.Copy.impl.Op.specific_fn: = specific_function %Int.as.Copy.impl.Op.664, @Int.as.Copy.impl.Op(%int_32) [concrete] // CHECK:STDOUT: %AccessBase.type: type = fn_type @AccessBase [concrete] // CHECK:STDOUT: %AccessBase: %AccessBase.type = struct_value () [concrete] // CHECK:STDOUT: %ptr.f74: type = ptr_type %Derived [concrete] // CHECK:STDOUT: %pattern_type.0dd: type = pattern_type %ptr.f74 [concrete] // CHECK:STDOUT: %ptr.235: type = ptr_type %i32 [concrete] // CHECK:STDOUT: %.605: Core.Form = init_form %ptr.235 [concrete] // CHECK:STDOUT: %pattern_type.fe8: type = pattern_type %ptr.235 [concrete] // CHECK:STDOUT: %AccessDerivedIndirect.type: type = fn_type @AccessDerivedIndirect [concrete] // CHECK:STDOUT: %AccessDerivedIndirect: %AccessDerivedIndirect.type = struct_value () [concrete] // CHECK:STDOUT: %Copy.impl_witness.843: = impl_witness imports.%Copy.impl_witness_table.c3a, @ptr.as.Copy.impl(%i32) [concrete] // CHECK:STDOUT: %ptr.as.Copy.impl.Op.type.c3c: type = fn_type @ptr.as.Copy.impl.Op, @ptr.as.Copy.impl(%i32) [concrete] // CHECK:STDOUT: %ptr.as.Copy.impl.Op.011: %ptr.as.Copy.impl.Op.type.c3c = struct_value () [concrete] // CHECK:STDOUT: %Copy.facet.a7b: %Copy.type = facet_value %ptr.235, (%Copy.impl_witness.843) [concrete] // CHECK:STDOUT: %Copy.WithSelf.Op.type.e01: type = fn_type @Copy.WithSelf.Op, @Copy.WithSelf(%Copy.facet.a7b) [concrete] // CHECK:STDOUT: %.a62: type = fn_type_with_self_type %Copy.WithSelf.Op.type.e01, %Copy.facet.a7b [concrete] // CHECK:STDOUT: %ptr.as.Copy.impl.Op.specific_fn: = specific_function %ptr.as.Copy.impl.Op.011, @ptr.as.Copy.impl.Op(%i32) [concrete] // CHECK:STDOUT: %AccessBaseIndirect.type: type = fn_type @AccessBaseIndirect [concrete] // CHECK:STDOUT: %AccessBaseIndirect: %AccessBaseIndirect.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: .Int = %Core.Int // CHECK:STDOUT: .Copy = %Core.Copy // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: %Core.Int: %Int.type = import_ref Core//prelude/parts/int, Int, loaded [concrete = constants.%Int.generic] // CHECK:STDOUT: %Core.Copy: type = import_ref Core//prelude/parts/copy, Copy, loaded [concrete = constants.%Copy.type] // CHECK:STDOUT: %Core.import_ref.18d: @Int.as.Copy.impl.%Int.as.Copy.impl.Op.type (%Int.as.Copy.impl.Op.type.824) = import_ref Core//prelude/parts/int, loc{{\d+_\d+}}, loaded [symbolic = @Int.as.Copy.impl.%Int.as.Copy.impl.Op (constants.%Int.as.Copy.impl.Op.9b9)] // CHECK:STDOUT: %Copy.impl_witness_table.e76 = impl_witness_table (%Core.import_ref.18d), @Int.as.Copy.impl [concrete] // CHECK:STDOUT: %Core.import_ref.203: @ptr.as.Copy.impl.%ptr.as.Copy.impl.Op.type (%ptr.as.Copy.impl.Op.type.2d4) = import_ref Core//prelude/parts/copy, loc{{\d+_\d+}}, loaded [symbolic = @ptr.as.Copy.impl.%ptr.as.Copy.impl.Op (constants.%ptr.as.Copy.impl.Op.74e)] // CHECK:STDOUT: %Copy.impl_witness_table.c3a = impl_witness_table (%Core.import_ref.203), @ptr.as.Copy.impl [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .Base = %Base.decl // CHECK:STDOUT: .Derived = %Derived.decl // CHECK:STDOUT: .AccessDerived = %AccessDerived.decl // CHECK:STDOUT: .AccessBase = %AccessBase.decl // CHECK:STDOUT: .AccessDerivedIndirect = %AccessDerivedIndirect.decl // CHECK:STDOUT: .AccessBaseIndirect = %AccessBaseIndirect.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %Base.decl: type = class_decl @Base [concrete = constants.%Base] {} {} // CHECK:STDOUT: %Derived.decl: type = class_decl @Derived [concrete = constants.%Derived] {} {} // CHECK:STDOUT: %AccessDerived.decl: %AccessDerived.type = fn_decl @AccessDerived [concrete = constants.%AccessDerived] { // CHECK:STDOUT: %d.patt: %pattern_type.9f6 = value_binding_pattern d [concrete] // CHECK:STDOUT: %d.param_patt: %pattern_type.9f6 = value_param_pattern %d.patt [concrete] // CHECK:STDOUT: %return.patt: %pattern_type.7ce = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: %pattern_type.7ce = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %i32: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %.loc28: Core.Form = init_form %i32 [concrete = constants.%.ff5] // CHECK:STDOUT: %d.param: %Derived = value_param call_param0 // CHECK:STDOUT: %Derived.ref.loc28: type = name_ref Derived, file.%Derived.decl [concrete = constants.%Derived] // CHECK:STDOUT: %d: %Derived = value_binding d, %d.param // CHECK:STDOUT: %return.param: ref %i32 = out_param call_param1 // CHECK:STDOUT: %return: ref %i32 = return_slot %return.param // CHECK:STDOUT: } // CHECK:STDOUT: %AccessBase.decl: %AccessBase.type = fn_decl @AccessBase [concrete = constants.%AccessBase] { // CHECK:STDOUT: %d.patt: %pattern_type.9f6 = value_binding_pattern d [concrete] // CHECK:STDOUT: %d.param_patt: %pattern_type.9f6 = value_param_pattern %d.patt [concrete] // CHECK:STDOUT: %return.patt: %pattern_type.7ce = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: %pattern_type.7ce = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %i32: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %.loc32: Core.Form = init_form %i32 [concrete = constants.%.ff5] // CHECK:STDOUT: %d.param: %Derived = value_param call_param0 // CHECK:STDOUT: %Derived.ref: type = name_ref Derived, file.%Derived.decl [concrete = constants.%Derived] // CHECK:STDOUT: %d: %Derived = value_binding d, %d.param // CHECK:STDOUT: %return.param: ref %i32 = out_param call_param1 // CHECK:STDOUT: %return: ref %i32 = return_slot %return.param // CHECK:STDOUT: } // CHECK:STDOUT: %AccessDerivedIndirect.decl: %AccessDerivedIndirect.type = fn_decl @AccessDerivedIndirect [concrete = constants.%AccessDerivedIndirect] { // CHECK:STDOUT: %p.patt: %pattern_type.0dd = value_binding_pattern p [concrete] // CHECK:STDOUT: %p.param_patt: %pattern_type.0dd = value_param_pattern %p.patt [concrete] // CHECK:STDOUT: %return.patt: %pattern_type.fe8 = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: %pattern_type.fe8 = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %i32: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %ptr.loc36_45: type = ptr_type %i32 [concrete = constants.%ptr.235] // CHECK:STDOUT: %.loc36_45: Core.Form = init_form %ptr.loc36_45 [concrete = constants.%.605] // CHECK:STDOUT: %p.param: %ptr.f74 = value_param call_param0 // CHECK:STDOUT: %.loc36_36: type = splice_block %ptr.loc36_36 [concrete = constants.%ptr.f74] { // CHECK:STDOUT: %Derived.ref.loc36: type = name_ref Derived, file.%Derived.decl [concrete = constants.%Derived] // CHECK:STDOUT: %ptr.loc36_36: type = ptr_type %Derived.ref.loc36 [concrete = constants.%ptr.f74] // CHECK:STDOUT: } // CHECK:STDOUT: %p: %ptr.f74 = value_binding p, %p.param // CHECK:STDOUT: %return.param: ref %ptr.235 = out_param call_param1 // CHECK:STDOUT: %return: ref %ptr.235 = return_slot %return.param // CHECK:STDOUT: } // CHECK:STDOUT: %AccessBaseIndirect.decl: %AccessBaseIndirect.type = fn_decl @AccessBaseIndirect [concrete = constants.%AccessBaseIndirect] { // CHECK:STDOUT: %p.patt: %pattern_type.0dd = value_binding_pattern p [concrete] // CHECK:STDOUT: %p.param_patt: %pattern_type.0dd = value_param_pattern %p.patt [concrete] // CHECK:STDOUT: %return.patt: %pattern_type.fe8 = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: %pattern_type.fe8 = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %i32: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %ptr.loc40_42: type = ptr_type %i32 [concrete = constants.%ptr.235] // CHECK:STDOUT: %.loc40_42: Core.Form = init_form %ptr.loc40_42 [concrete = constants.%.605] // CHECK:STDOUT: %p.param: %ptr.f74 = value_param call_param0 // CHECK:STDOUT: %.loc40_33: type = splice_block %ptr.loc40_33 [concrete = constants.%ptr.f74] { // CHECK:STDOUT: %Derived.ref: type = name_ref Derived, file.%Derived.decl [concrete = constants.%Derived] // CHECK:STDOUT: %ptr.loc40_33: type = ptr_type %Derived.ref [concrete = constants.%ptr.f74] // CHECK:STDOUT: } // CHECK:STDOUT: %p: %ptr.f74 = value_binding p, %p.param // CHECK:STDOUT: %return.param: ref %ptr.235 = out_param call_param1 // CHECK:STDOUT: %return: ref %ptr.235 = return_slot %return.param // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @Base { // CHECK:STDOUT: %i32.loc16: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %.loc16: %Base.elem = field_decl a, element0 [concrete] // CHECK:STDOUT: %i32.loc17: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %.loc17: %Base.elem = field_decl b, element1 [concrete] // CHECK:STDOUT: %i32.loc18: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %.loc18: %Base.elem = field_decl c, element2 [concrete] // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%struct_type.a.b.c [concrete = constants.%complete_type.ebc] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%Base // CHECK:STDOUT: .a = %.loc16 // CHECK:STDOUT: .b = %.loc17 // CHECK:STDOUT: .c = %.loc18 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @Derived { // CHECK:STDOUT: %Base.ref: type = name_ref Base, file.%Base.decl [concrete = constants.%Base] // CHECK:STDOUT: %.loc22: %Derived.elem.029 = base_decl %Base.ref, element0 [concrete] // CHECK:STDOUT: %i32.loc24: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %.loc24: %Derived.elem.530 = field_decl d, element1 [concrete] // CHECK:STDOUT: %i32.loc25: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %.loc25: %Derived.elem.530 = field_decl e, element2 [concrete] // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%struct_type.base.d.e.b4b [concrete = constants.%complete_type.ea9] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%Derived // CHECK:STDOUT: .Base = // CHECK:STDOUT: .base = %.loc22 // CHECK:STDOUT: .d = %.loc24 // CHECK:STDOUT: .e = %.loc25 // CHECK:STDOUT: extend %Base.ref // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @AccessDerived(%d.param: %Derived) -> out %return.param: %i32 { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %d.ref.loc29_10: %Derived = name_ref d, %d // CHECK:STDOUT: %Derived.ref.loc29: type = name_ref Derived, file.%Derived.decl [concrete = constants.%Derived] // CHECK:STDOUT: %d.ref.loc29_20: %Derived.elem.530 = name_ref d, @Derived.%.loc24 [concrete = @Derived.%.loc24] // CHECK:STDOUT: %.loc29_11.1: ref %i32 = class_element_access %d.ref.loc29_10, element1 // CHECK:STDOUT: %.loc29_11.2: %i32 = acquire_value %.loc29_11.1 // CHECK:STDOUT: %impl.elem0: %.8e2 = impl_witness_access constants.%Copy.impl_witness.f17, element0 [concrete = constants.%Int.as.Copy.impl.Op.664] // CHECK:STDOUT: %bound_method.loc29_11.1: = bound_method %.loc29_11.2, %impl.elem0 // CHECK:STDOUT: %specific_fn: = specific_function %impl.elem0, @Int.as.Copy.impl.Op(constants.%int_32) [concrete = constants.%Int.as.Copy.impl.Op.specific_fn] // CHECK:STDOUT: %bound_method.loc29_11.2: = bound_method %.loc29_11.2, %specific_fn // CHECK:STDOUT: %Int.as.Copy.impl.Op.call: init %i32 = call %bound_method.loc29_11.2(%.loc29_11.2) // CHECK:STDOUT: return %Int.as.Copy.impl.Op.call // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @AccessBase(%d.param: %Derived) -> out %return.param: %i32 { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %d.ref: %Derived = name_ref d, %d // CHECK:STDOUT: %Base.ref: type = name_ref Base, file.%Base.decl [concrete = constants.%Base] // CHECK:STDOUT: %b.ref: %Base.elem = name_ref b, @Base.%.loc17 [concrete = @Base.%.loc17] // CHECK:STDOUT: %.loc33_11.1: ref %Base = class_element_access %d.ref, element0 // CHECK:STDOUT: %.loc33_11.2: ref %Base = converted %d.ref, %.loc33_11.1 // CHECK:STDOUT: %.loc33_11.3: ref %i32 = class_element_access %.loc33_11.2, element1 // CHECK:STDOUT: %.loc33_11.4: %i32 = acquire_value %.loc33_11.3 // CHECK:STDOUT: %impl.elem0: %.8e2 = impl_witness_access constants.%Copy.impl_witness.f17, element0 [concrete = constants.%Int.as.Copy.impl.Op.664] // CHECK:STDOUT: %bound_method.loc33_11.1: = bound_method %.loc33_11.4, %impl.elem0 // CHECK:STDOUT: %specific_fn: = specific_function %impl.elem0, @Int.as.Copy.impl.Op(constants.%int_32) [concrete = constants.%Int.as.Copy.impl.Op.specific_fn] // CHECK:STDOUT: %bound_method.loc33_11.2: = bound_method %.loc33_11.4, %specific_fn // CHECK:STDOUT: %Int.as.Copy.impl.Op.call: init %i32 = call %bound_method.loc33_11.2(%.loc33_11.4) // CHECK:STDOUT: return %Int.as.Copy.impl.Op.call // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @AccessDerivedIndirect(%p.param: %ptr.f74) -> out %return.param: %ptr.235 { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %p.ref: %ptr.f74 = name_ref p, %p // CHECK:STDOUT: %Derived.ref.loc37: type = name_ref Derived, file.%Derived.decl [concrete = constants.%Derived] // CHECK:STDOUT: %d.ref: %Derived.elem.530 = name_ref d, @Derived.%.loc24 [concrete = @Derived.%.loc24] // CHECK:STDOUT: %.loc37_12.1: ref %Derived = deref %p.ref // CHECK:STDOUT: %.loc37_12.2: ref %i32 = class_element_access %.loc37_12.1, element1 // CHECK:STDOUT: %addr: %ptr.235 = addr_of %.loc37_12.2 // CHECK:STDOUT: %impl.elem0: %.a62 = impl_witness_access constants.%Copy.impl_witness.843, element0 [concrete = constants.%ptr.as.Copy.impl.Op.011] // CHECK:STDOUT: %bound_method.loc37_10.1: = bound_method %addr, %impl.elem0 // CHECK:STDOUT: %specific_fn: = specific_function %impl.elem0, @ptr.as.Copy.impl.Op(constants.%i32) [concrete = constants.%ptr.as.Copy.impl.Op.specific_fn] // CHECK:STDOUT: %bound_method.loc37_10.2: = bound_method %addr, %specific_fn // CHECK:STDOUT: %ptr.as.Copy.impl.Op.call: init %ptr.235 = call %bound_method.loc37_10.2(%addr) // CHECK:STDOUT: return %ptr.as.Copy.impl.Op.call // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @AccessBaseIndirect(%p.param: %ptr.f74) -> out %return.param: %ptr.235 { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %p.ref: %ptr.f74 = name_ref p, %p // CHECK:STDOUT: %Base.ref: type = name_ref Base, file.%Base.decl [concrete = constants.%Base] // CHECK:STDOUT: %b.ref: %Base.elem = name_ref b, @Base.%.loc17 [concrete = @Base.%.loc17] // CHECK:STDOUT: %.loc41_12.1: ref %Derived = deref %p.ref // CHECK:STDOUT: %.loc41_12.2: ref %Base = class_element_access %.loc41_12.1, element0 // CHECK:STDOUT: %.loc41_12.3: ref %Base = converted %.loc41_12.1, %.loc41_12.2 // CHECK:STDOUT: %.loc41_12.4: ref %i32 = class_element_access %.loc41_12.3, element1 // CHECK:STDOUT: %addr: %ptr.235 = addr_of %.loc41_12.4 // CHECK:STDOUT: %impl.elem0: %.a62 = impl_witness_access constants.%Copy.impl_witness.843, element0 [concrete = constants.%ptr.as.Copy.impl.Op.011] // CHECK:STDOUT: %bound_method.loc41_10.1: = bound_method %addr, %impl.elem0 // CHECK:STDOUT: %specific_fn: = specific_function %impl.elem0, @ptr.as.Copy.impl.Op(constants.%i32) [concrete = constants.%ptr.as.Copy.impl.Op.specific_fn] // CHECK:STDOUT: %bound_method.loc41_10.2: = bound_method %addr, %specific_fn // CHECK:STDOUT: %ptr.as.Copy.impl.Op.call: init %ptr.235 = call %bound_method.loc41_10.2(%addr) // CHECK:STDOUT: return %ptr.as.Copy.impl.Op.call // CHECK:STDOUT: } // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/class/field/fail_field_modifiers.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/int.carbon // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/class/field/fail_field_modifiers.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/class/field/fail_field_modifiers.carbon class Class { // CHECK:STDERR: fail_field_modifiers.carbon:[[@LINE+4]]:3: error: `default` not allowed on `var` declaration [ModifierNotAllowedOnDeclaration] // CHECK:STDERR: default var j: i32; // CHECK:STDERR: ^~~~~~~ // CHECK:STDERR: default var j: i32; // CHECK:STDERR: fail_field_modifiers.carbon:[[@LINE+4]]:3: error: `final` not allowed on `var` declaration [ModifierNotAllowedOnDeclaration] // CHECK:STDERR: final var k: i32; // CHECK:STDERR: ^~~~~ // CHECK:STDERR: final var k: i32; // CHECK:STDERR: fail_field_modifiers.carbon:[[@LINE+4]]:3: error: `default` not allowed; requires interface scope [ModifierRequiresInterface] // CHECK:STDERR: default let l: i32 = 0; // CHECK:STDERR: ^~~~~~~ // CHECK:STDERR: default let l: i32 = 0; // CHECK:STDERR: fail_field_modifiers.carbon:[[@LINE+4]]:3: error: `final` not allowed; requires interface scope [ModifierRequiresInterface] // CHECK:STDERR: final let m: i32 = 1; // CHECK:STDERR: ^~~~~ // CHECK:STDERR: final let m: i32 = 1; } ================================================ FILE: toolchain/check/testdata/class/field/fail_todo_field_initializer.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/int.carbon // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/class/field/fail_todo_field_initializer.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/class/field/fail_todo_field_initializer.carbon class Class { // CHECK:STDERR: fail_todo_field_initializer.carbon:[[@LINE+4]]:3: error: semantics TODO: `Field initializer` [SemanticsTodo] // CHECK:STDERR: var field: i32 = 0; // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: var field: i32 = 0; } ================================================ FILE: toolchain/check/testdata/class/field/fail_unbound_field.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/full.carbon // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/class/field/fail_unbound_field.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/class/field/fail_unbound_field.carbon class Class { var field: i32; fn F() -> i32 { // CHECK:STDERR: fail_unbound_field.carbon:[[@LINE+4]]:12: error: expression cannot be used as a value [UseOfNonExprAsValue] // CHECK:STDERR: return field; // CHECK:STDERR: ^~~~~ // CHECK:STDERR: return field; } fn IsZero() -> bool { // CHECK:STDERR: fail_unbound_field.carbon:[[@LINE+4]]:12: error: cannot access member of interface `Core.EqWith(Core.IntLiteral)` in type `` that does not implement that interface [MissingImplInMemberAccess] // CHECK:STDERR: return field == 0; // CHECK:STDERR: ^~~~~~~~~~ // CHECK:STDERR: return field == 0; } } fn G() -> i32 { // CHECK:STDERR: fail_unbound_field.carbon:[[@LINE+4]]:10: error: expression cannot be used as a value [UseOfNonExprAsValue] // CHECK:STDERR: return Class.field; // CHECK:STDERR: ^~~~~~~~~~~ // CHECK:STDERR: return Class.field; } ================================================ FILE: toolchain/check/testdata/class/field/field_access.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/int.carbon // TODO: Add ranges and switch to "--dump-sem-ir-ranges=only". // EXTRA-ARGS: --dump-sem-ir-ranges=if-present // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/class/field/field_access.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/class/field/field_access.carbon class Class { var j: i32; var k: i32; } fn Run() { var c: Class; c.j = 1; c.k = 2; var unused cj: i32 = c.j; var unused ck: i32 = c.k; } // CHECK:STDOUT: --- field_access.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %Class: type = class_type @Class [concrete] // CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [concrete] // CHECK:STDOUT: %Int.type: type = generic_class_type @Int [concrete] // CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] // CHECK:STDOUT: %Int.generic: %Int.type = struct_value () [concrete] // CHECK:STDOUT: %N: Core.IntLiteral = symbolic_binding N, 0 [symbolic] // CHECK:STDOUT: %i32: type = class_type @Int, @Int(%int_32) [concrete] // CHECK:STDOUT: %Class.elem: type = unbound_element_type %Class, %i32 [concrete] // CHECK:STDOUT: %struct_type.j.k: type = struct_type {.j: %i32, .k: %i32} [concrete] // CHECK:STDOUT: %complete_type.cf7: = complete_type_witness %struct_type.j.k [concrete] // CHECK:STDOUT: %Run.type: type = fn_type @Run [concrete] // CHECK:STDOUT: %Run: %Run.type = struct_value () [concrete] // CHECK:STDOUT: %pattern_type.904: type = pattern_type %Class [concrete] // CHECK:STDOUT: %DefaultOrUnformed.type: type = facet_type <@DefaultOrUnformed> [concrete] // CHECK:STDOUT: %T.67d: type = symbolic_binding T, 0 [symbolic] // CHECK:STDOUT: %T.as.DefaultOrUnformed.impl.Op.type.6c6: type = fn_type @T.as.DefaultOrUnformed.impl.Op, @T.as.DefaultOrUnformed.impl(%T.67d) [symbolic] // CHECK:STDOUT: %T.as.DefaultOrUnformed.impl.Op.ae1: %T.as.DefaultOrUnformed.impl.Op.type.6c6 = struct_value () [symbolic] // CHECK:STDOUT: %DefaultOrUnformed.impl_witness.6ca: = impl_witness imports.%DefaultOrUnformed.impl_witness_table, @T.as.DefaultOrUnformed.impl(%Class) [concrete] // CHECK:STDOUT: %T.as.DefaultOrUnformed.impl.Op.type.3d0: type = fn_type @T.as.DefaultOrUnformed.impl.Op, @T.as.DefaultOrUnformed.impl(%Class) [concrete] // CHECK:STDOUT: %T.as.DefaultOrUnformed.impl.Op.cda: %T.as.DefaultOrUnformed.impl.Op.type.3d0 = struct_value () [concrete] // CHECK:STDOUT: %DefaultOrUnformed.facet: %DefaultOrUnformed.type = facet_value %Class, (%DefaultOrUnformed.impl_witness.6ca) [concrete] // CHECK:STDOUT: %T.as.DefaultOrUnformed.impl.Op.specific_fn: = specific_function %T.as.DefaultOrUnformed.impl.Op.cda, @T.as.DefaultOrUnformed.impl.Op(%Class) [concrete] // CHECK:STDOUT: %int_1.5b8: Core.IntLiteral = int_value 1 [concrete] // CHECK:STDOUT: %ImplicitAs.type.cc7: type = generic_interface_type @ImplicitAs [concrete] // CHECK:STDOUT: %ImplicitAs.generic: %ImplicitAs.type.cc7 = struct_value () [concrete] // CHECK:STDOUT: %ImplicitAs.type.e8c: type = facet_type <@ImplicitAs, @ImplicitAs(%i32)> [concrete] // CHECK:STDOUT: %To: Core.IntLiteral = symbolic_binding To, 0 [symbolic] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.4e6: type = fn_type @Core.IntLiteral.as.ImplicitAs.impl.Convert, @Core.IntLiteral.as.ImplicitAs.impl(%To) [symbolic] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.3c2: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.4e6 = struct_value () [symbolic] // CHECK:STDOUT: %ImplicitAs.impl_witness.6bc: = impl_witness imports.%ImplicitAs.impl_witness_table.74f, @Core.IntLiteral.as.ImplicitAs.impl(%int_32) [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.e0d: type = fn_type @Core.IntLiteral.as.ImplicitAs.impl.Convert, @Core.IntLiteral.as.ImplicitAs.impl(%int_32) [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.e0d = struct_value () [concrete] // CHECK:STDOUT: %ImplicitAs.facet: %ImplicitAs.type.e8c = facet_value Core.IntLiteral, (%ImplicitAs.impl_witness.6bc) [concrete] // CHECK:STDOUT: %ImplicitAs.WithSelf.Convert.type.b37: type = fn_type @ImplicitAs.WithSelf.Convert, @ImplicitAs.WithSelf(%i32, %ImplicitAs.facet) [concrete] // CHECK:STDOUT: %.545: type = fn_type_with_self_type %ImplicitAs.WithSelf.Convert.type.b37, %ImplicitAs.facet [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.bound.215: = bound_method %int_1.5b8, %Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5 [concrete] // CHECK:STDOUT: %pattern_type.7ce: type = pattern_type %i32 [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn: = specific_function %Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5, @Core.IntLiteral.as.ImplicitAs.impl.Convert(%int_32) [concrete] // CHECK:STDOUT: %bound_method.38b: = bound_method %int_1.5b8, %Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn [concrete] // CHECK:STDOUT: %int_1.5d2: %i32 = int_value 1 [concrete] // CHECK:STDOUT: %int_2.ecc: Core.IntLiteral = int_value 2 [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.bound.4e5: = bound_method %int_2.ecc, %Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5 [concrete] // CHECK:STDOUT: %bound_method.646: = bound_method %int_2.ecc, %Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn [concrete] // CHECK:STDOUT: %int_2.ef8: %i32 = int_value 2 [concrete] // CHECK:STDOUT: %Copy.type: type = facet_type <@Copy> [concrete] // CHECK:STDOUT: %Int.as.Copy.impl.Op.type.824: type = fn_type @Int.as.Copy.impl.Op, @Int.as.Copy.impl(%N) [symbolic] // CHECK:STDOUT: %Int.as.Copy.impl.Op.9b9: %Int.as.Copy.impl.Op.type.824 = struct_value () [symbolic] // CHECK:STDOUT: %Copy.impl_witness.f17: = impl_witness imports.%Copy.impl_witness_table.e76, @Int.as.Copy.impl(%int_32) [concrete] // CHECK:STDOUT: %Int.as.Copy.impl.Op.type.546: type = fn_type @Int.as.Copy.impl.Op, @Int.as.Copy.impl(%int_32) [concrete] // CHECK:STDOUT: %Int.as.Copy.impl.Op.664: %Int.as.Copy.impl.Op.type.546 = struct_value () [concrete] // CHECK:STDOUT: %Copy.facet: %Copy.type = facet_value %i32, (%Copy.impl_witness.f17) [concrete] // CHECK:STDOUT: %Copy.WithSelf.Op.type.081: type = fn_type @Copy.WithSelf.Op, @Copy.WithSelf(%Copy.facet) [concrete] // CHECK:STDOUT: %.8e2: type = fn_type_with_self_type %Copy.WithSelf.Op.type.081, %Copy.facet [concrete] // CHECK:STDOUT: %Int.as.Copy.impl.Op.specific_fn: = specific_function %Int.as.Copy.impl.Op.664, @Int.as.Copy.impl.Op(%int_32) [concrete] // CHECK:STDOUT: %Destroy.type: type = facet_type <@Destroy> [concrete] // CHECK:STDOUT: %Destroy.Op.type.bae255.1: type = fn_type @Destroy.Op.loc25 [concrete] // CHECK:STDOUT: %Destroy.Op.651ba6.1: %Destroy.Op.type.bae255.1 = struct_value () [concrete] // CHECK:STDOUT: %Destroy.Op.type.bae255.2: type = fn_type @Destroy.Op.loc21 [concrete] // CHECK:STDOUT: %Destroy.Op.651ba6.2: %Destroy.Op.type.bae255.2 = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: .Int = %Core.Int // CHECK:STDOUT: .DefaultOrUnformed = %Core.DefaultOrUnformed // CHECK:STDOUT: .ImplicitAs = %Core.ImplicitAs // CHECK:STDOUT: .Copy = %Core.Copy // CHECK:STDOUT: .Destroy = %Core.Destroy // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: %Core.Int: %Int.type = import_ref Core//prelude/parts/int, Int, loaded [concrete = constants.%Int.generic] // CHECK:STDOUT: %Core.DefaultOrUnformed: type = import_ref Core//prelude/parts/default, DefaultOrUnformed, loaded [concrete = constants.%DefaultOrUnformed.type] // CHECK:STDOUT: %Core.import_ref.01d: @T.as.DefaultOrUnformed.impl.%T.as.DefaultOrUnformed.impl.Op.type (%T.as.DefaultOrUnformed.impl.Op.type.6c6) = import_ref Core//prelude/parts/default, loc{{\d+_\d+}}, loaded [symbolic = @T.as.DefaultOrUnformed.impl.%T.as.DefaultOrUnformed.impl.Op (constants.%T.as.DefaultOrUnformed.impl.Op.ae1)] // CHECK:STDOUT: %DefaultOrUnformed.impl_witness_table = impl_witness_table (%Core.import_ref.01d), @T.as.DefaultOrUnformed.impl [concrete] // CHECK:STDOUT: %Core.ImplicitAs: %ImplicitAs.type.cc7 = import_ref Core//prelude/parts/as, ImplicitAs, loaded [concrete = constants.%ImplicitAs.generic] // CHECK:STDOUT: %Core.import_ref.42d: @Core.IntLiteral.as.ImplicitAs.impl.%Core.IntLiteral.as.ImplicitAs.impl.Convert.type (%Core.IntLiteral.as.ImplicitAs.impl.Convert.type.4e6) = import_ref Core//prelude/parts/int, loc{{\d+_\d+}}, loaded [symbolic = @Core.IntLiteral.as.ImplicitAs.impl.%Core.IntLiteral.as.ImplicitAs.impl.Convert (constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.3c2)] // CHECK:STDOUT: %ImplicitAs.impl_witness_table.74f = impl_witness_table (%Core.import_ref.42d), @Core.IntLiteral.as.ImplicitAs.impl [concrete] // CHECK:STDOUT: %Core.Copy: type = import_ref Core//prelude/parts/copy, Copy, loaded [concrete = constants.%Copy.type] // CHECK:STDOUT: %Core.import_ref.18d: @Int.as.Copy.impl.%Int.as.Copy.impl.Op.type (%Int.as.Copy.impl.Op.type.824) = import_ref Core//prelude/parts/int, loc{{\d+_\d+}}, loaded [symbolic = @Int.as.Copy.impl.%Int.as.Copy.impl.Op (constants.%Int.as.Copy.impl.Op.9b9)] // CHECK:STDOUT: %Copy.impl_witness_table.e76 = impl_witness_table (%Core.import_ref.18d), @Int.as.Copy.impl [concrete] // CHECK:STDOUT: %Core.Destroy: type = import_ref Core//prelude/parts/destroy, Destroy, loaded [concrete = constants.%Destroy.type] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .Class = %Class.decl // CHECK:STDOUT: .Run = %Run.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %Class.decl: type = class_decl @Class [concrete = constants.%Class] {} {} // CHECK:STDOUT: %Run.decl: %Run.type = fn_decl @Run [concrete = constants.%Run] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @Class { // CHECK:STDOUT: %i32.loc16: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %.loc16: %Class.elem = field_decl j, element0 [concrete] // CHECK:STDOUT: %i32.loc17: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %.loc17: %Class.elem = field_decl k, element1 [concrete] // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%struct_type.j.k [concrete = constants.%complete_type.cf7] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%Class // CHECK:STDOUT: .j = %.loc16 // CHECK:STDOUT: .k = %.loc17 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @Run() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %c.patt: %pattern_type.904 = ref_binding_pattern c [concrete] // CHECK:STDOUT: %c.var_patt: %pattern_type.904 = var_pattern %c.patt [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %c.var: ref %Class = var %c.var_patt // CHECK:STDOUT: %DefaultOrUnformed.facet: %DefaultOrUnformed.type = facet_value constants.%Class, (constants.%DefaultOrUnformed.impl_witness.6ca) [concrete = constants.%DefaultOrUnformed.facet] // CHECK:STDOUT: %.loc21_15.1: %DefaultOrUnformed.type = converted constants.%Class, %DefaultOrUnformed.facet [concrete = constants.%DefaultOrUnformed.facet] // CHECK:STDOUT: %as_type: type = facet_access_type %.loc21_15.1 [concrete = constants.%Class] // CHECK:STDOUT: %.loc21_15.2: type = converted %.loc21_15.1, %as_type [concrete = constants.%Class] // CHECK:STDOUT: %T.as.DefaultOrUnformed.impl.Op.specific_fn: = specific_function constants.%T.as.DefaultOrUnformed.impl.Op.cda, @T.as.DefaultOrUnformed.impl.Op(constants.%Class) [concrete = constants.%T.as.DefaultOrUnformed.impl.Op.specific_fn] // CHECK:STDOUT: %.loc21_3: ref %Class = splice_block %c.var {} // CHECK:STDOUT: %T.as.DefaultOrUnformed.impl.Op.call: init %Class to %.loc21_3 = call %T.as.DefaultOrUnformed.impl.Op.specific_fn() // CHECK:STDOUT: assign %c.var, %T.as.DefaultOrUnformed.impl.Op.call // CHECK:STDOUT: %Class.ref: type = name_ref Class, file.%Class.decl [concrete = constants.%Class] // CHECK:STDOUT: %c: ref %Class = ref_binding c, %c.var // CHECK:STDOUT: %c.ref.loc22: ref %Class = name_ref c, %c // CHECK:STDOUT: %j.ref.loc22: %Class.elem = name_ref j, @Class.%.loc16 [concrete = @Class.%.loc16] // CHECK:STDOUT: %.loc22_4: ref %i32 = class_element_access %c.ref.loc22, element0 // CHECK:STDOUT: %int_1: Core.IntLiteral = int_value 1 [concrete = constants.%int_1.5b8] // CHECK:STDOUT: %impl.elem0.loc22: %.545 = impl_witness_access constants.%ImplicitAs.impl_witness.6bc, element0 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5] // CHECK:STDOUT: %bound_method.loc22_7.1: = bound_method %int_1, %impl.elem0.loc22 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.bound.215] // CHECK:STDOUT: %specific_fn.loc22: = specific_function %impl.elem0.loc22, @Core.IntLiteral.as.ImplicitAs.impl.Convert(constants.%int_32) [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn] // CHECK:STDOUT: %bound_method.loc22_7.2: = bound_method %int_1, %specific_fn.loc22 [concrete = constants.%bound_method.38b] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc22: init %i32 = call %bound_method.loc22_7.2(%int_1) [concrete = constants.%int_1.5d2] // CHECK:STDOUT: %.loc22_7: init %i32 = converted %int_1, %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc22 [concrete = constants.%int_1.5d2] // CHECK:STDOUT: assign %.loc22_4, %.loc22_7 // CHECK:STDOUT: %c.ref.loc23: ref %Class = name_ref c, %c // CHECK:STDOUT: %k.ref.loc23: %Class.elem = name_ref k, @Class.%.loc17 [concrete = @Class.%.loc17] // CHECK:STDOUT: %.loc23_4: ref %i32 = class_element_access %c.ref.loc23, element1 // CHECK:STDOUT: %int_2: Core.IntLiteral = int_value 2 [concrete = constants.%int_2.ecc] // CHECK:STDOUT: %impl.elem0.loc23: %.545 = impl_witness_access constants.%ImplicitAs.impl_witness.6bc, element0 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5] // CHECK:STDOUT: %bound_method.loc23_7.1: = bound_method %int_2, %impl.elem0.loc23 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.bound.4e5] // CHECK:STDOUT: %specific_fn.loc23: = specific_function %impl.elem0.loc23, @Core.IntLiteral.as.ImplicitAs.impl.Convert(constants.%int_32) [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn] // CHECK:STDOUT: %bound_method.loc23_7.2: = bound_method %int_2, %specific_fn.loc23 [concrete = constants.%bound_method.646] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc23: init %i32 = call %bound_method.loc23_7.2(%int_2) [concrete = constants.%int_2.ef8] // CHECK:STDOUT: %.loc23_7: init %i32 = converted %int_2, %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc23 [concrete = constants.%int_2.ef8] // CHECK:STDOUT: assign %.loc23_4, %.loc23_7 // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %cj.patt: %pattern_type.7ce = ref_binding_pattern cj [concrete] // CHECK:STDOUT: %cj.var_patt: %pattern_type.7ce = var_pattern %cj.patt [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %cj.var: ref %i32 = var %cj.var_patt // CHECK:STDOUT: %c.ref.loc24: ref %Class = name_ref c, %c // CHECK:STDOUT: %j.ref.loc24: %Class.elem = name_ref j, @Class.%.loc16 [concrete = @Class.%.loc16] // CHECK:STDOUT: %.loc24_25.1: ref %i32 = class_element_access %c.ref.loc24, element0 // CHECK:STDOUT: %.loc24_25.2: %i32 = acquire_value %.loc24_25.1 // CHECK:STDOUT: %impl.elem0.loc24: %.8e2 = impl_witness_access constants.%Copy.impl_witness.f17, element0 [concrete = constants.%Int.as.Copy.impl.Op.664] // CHECK:STDOUT: %bound_method.loc24_25.1: = bound_method %.loc24_25.2, %impl.elem0.loc24 // CHECK:STDOUT: %specific_fn.loc24: = specific_function %impl.elem0.loc24, @Int.as.Copy.impl.Op(constants.%int_32) [concrete = constants.%Int.as.Copy.impl.Op.specific_fn] // CHECK:STDOUT: %bound_method.loc24_25.2: = bound_method %.loc24_25.2, %specific_fn.loc24 // CHECK:STDOUT: %Int.as.Copy.impl.Op.call.loc24: init %i32 = call %bound_method.loc24_25.2(%.loc24_25.2) // CHECK:STDOUT: assign %cj.var, %Int.as.Copy.impl.Op.call.loc24 // CHECK:STDOUT: %i32.loc24: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %cj: ref %i32 = ref_binding cj, %cj.var // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %ck.patt: %pattern_type.7ce = ref_binding_pattern ck [concrete] // CHECK:STDOUT: %ck.var_patt: %pattern_type.7ce = var_pattern %ck.patt [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %ck.var: ref %i32 = var %ck.var_patt // CHECK:STDOUT: %c.ref.loc25: ref %Class = name_ref c, %c // CHECK:STDOUT: %k.ref.loc25: %Class.elem = name_ref k, @Class.%.loc17 [concrete = @Class.%.loc17] // CHECK:STDOUT: %.loc25_25.1: ref %i32 = class_element_access %c.ref.loc25, element1 // CHECK:STDOUT: %.loc25_25.2: %i32 = acquire_value %.loc25_25.1 // CHECK:STDOUT: %impl.elem0.loc25: %.8e2 = impl_witness_access constants.%Copy.impl_witness.f17, element0 [concrete = constants.%Int.as.Copy.impl.Op.664] // CHECK:STDOUT: %bound_method.loc25_25.1: = bound_method %.loc25_25.2, %impl.elem0.loc25 // CHECK:STDOUT: %specific_fn.loc25: = specific_function %impl.elem0.loc25, @Int.as.Copy.impl.Op(constants.%int_32) [concrete = constants.%Int.as.Copy.impl.Op.specific_fn] // CHECK:STDOUT: %bound_method.loc25_25.2: = bound_method %.loc25_25.2, %specific_fn.loc25 // CHECK:STDOUT: %Int.as.Copy.impl.Op.call.loc25: init %i32 = call %bound_method.loc25_25.2(%.loc25_25.2) // CHECK:STDOUT: assign %ck.var, %Int.as.Copy.impl.Op.call.loc25 // CHECK:STDOUT: %i32.loc25: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %ck: ref %i32 = ref_binding ck, %ck.var // CHECK:STDOUT: %Destroy.Op.bound.loc25: = bound_method %ck.var, constants.%Destroy.Op.651ba6.1 // CHECK:STDOUT: %Destroy.Op.call.loc25: init %empty_tuple.type = call %Destroy.Op.bound.loc25(%ck.var) // CHECK:STDOUT: %Destroy.Op.bound.loc24: = bound_method %cj.var, constants.%Destroy.Op.651ba6.1 // CHECK:STDOUT: %Destroy.Op.call.loc24: init %empty_tuple.type = call %Destroy.Op.bound.loc24(%cj.var) // CHECK:STDOUT: %Destroy.Op.bound.loc21: = bound_method %c.var, constants.%Destroy.Op.651ba6.2 // CHECK:STDOUT: %Destroy.Op.call.loc21: init %empty_tuple.type = call %Destroy.Op.bound.loc21(%c.var) // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @Destroy.Op.loc25(%self.param: ref %i32) = "no_op"; // CHECK:STDOUT: // CHECK:STDOUT: fn @Destroy.Op.loc21(%self.param: ref %Class) = "no_op"; // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/class/field/field_access_in_value.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/int.carbon // TODO: Add ranges and switch to "--dump-sem-ir-ranges=only". // EXTRA-ARGS: --dump-sem-ir-ranges=if-present // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/class/field/field_access_in_value.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/class/field/field_access_in_value.carbon class Class { var j: i32; var k: i32; } fn Test() { var cv: Class; cv.j = 1; cv.k = 2; let c: Class = cv; var unused cj: i32 = c.j; var unused ck: i32 = c.k; } // CHECK:STDOUT: --- field_access_in_value.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %Class: type = class_type @Class [concrete] // CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [concrete] // CHECK:STDOUT: %Int.type: type = generic_class_type @Int [concrete] // CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] // CHECK:STDOUT: %Int.generic: %Int.type = struct_value () [concrete] // CHECK:STDOUT: %N: Core.IntLiteral = symbolic_binding N, 0 [symbolic] // CHECK:STDOUT: %i32: type = class_type @Int, @Int(%int_32) [concrete] // CHECK:STDOUT: %Class.elem: type = unbound_element_type %Class, %i32 [concrete] // CHECK:STDOUT: %struct_type.j.k: type = struct_type {.j: %i32, .k: %i32} [concrete] // CHECK:STDOUT: %complete_type.cf7: = complete_type_witness %struct_type.j.k [concrete] // CHECK:STDOUT: %Test.type: type = fn_type @Test [concrete] // CHECK:STDOUT: %Test: %Test.type = struct_value () [concrete] // CHECK:STDOUT: %pattern_type.904: type = pattern_type %Class [concrete] // CHECK:STDOUT: %DefaultOrUnformed.type: type = facet_type <@DefaultOrUnformed> [concrete] // CHECK:STDOUT: %T.67d: type = symbolic_binding T, 0 [symbolic] // CHECK:STDOUT: %T.as.DefaultOrUnformed.impl.Op.type.6c6: type = fn_type @T.as.DefaultOrUnformed.impl.Op, @T.as.DefaultOrUnformed.impl(%T.67d) [symbolic] // CHECK:STDOUT: %T.as.DefaultOrUnformed.impl.Op.ae1: %T.as.DefaultOrUnformed.impl.Op.type.6c6 = struct_value () [symbolic] // CHECK:STDOUT: %DefaultOrUnformed.impl_witness.6ca: = impl_witness imports.%DefaultOrUnformed.impl_witness_table, @T.as.DefaultOrUnformed.impl(%Class) [concrete] // CHECK:STDOUT: %T.as.DefaultOrUnformed.impl.Op.type.3d0: type = fn_type @T.as.DefaultOrUnformed.impl.Op, @T.as.DefaultOrUnformed.impl(%Class) [concrete] // CHECK:STDOUT: %T.as.DefaultOrUnformed.impl.Op.cda: %T.as.DefaultOrUnformed.impl.Op.type.3d0 = struct_value () [concrete] // CHECK:STDOUT: %DefaultOrUnformed.facet: %DefaultOrUnformed.type = facet_value %Class, (%DefaultOrUnformed.impl_witness.6ca) [concrete] // CHECK:STDOUT: %T.as.DefaultOrUnformed.impl.Op.specific_fn: = specific_function %T.as.DefaultOrUnformed.impl.Op.cda, @T.as.DefaultOrUnformed.impl.Op(%Class) [concrete] // CHECK:STDOUT: %int_1.5b8: Core.IntLiteral = int_value 1 [concrete] // CHECK:STDOUT: %ImplicitAs.type.cc7: type = generic_interface_type @ImplicitAs [concrete] // CHECK:STDOUT: %ImplicitAs.generic: %ImplicitAs.type.cc7 = struct_value () [concrete] // CHECK:STDOUT: %ImplicitAs.type.e8c: type = facet_type <@ImplicitAs, @ImplicitAs(%i32)> [concrete] // CHECK:STDOUT: %To: Core.IntLiteral = symbolic_binding To, 0 [symbolic] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.4e6: type = fn_type @Core.IntLiteral.as.ImplicitAs.impl.Convert, @Core.IntLiteral.as.ImplicitAs.impl(%To) [symbolic] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.3c2: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.4e6 = struct_value () [symbolic] // CHECK:STDOUT: %ImplicitAs.impl_witness.6bc: = impl_witness imports.%ImplicitAs.impl_witness_table.74f, @Core.IntLiteral.as.ImplicitAs.impl(%int_32) [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.e0d: type = fn_type @Core.IntLiteral.as.ImplicitAs.impl.Convert, @Core.IntLiteral.as.ImplicitAs.impl(%int_32) [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.e0d = struct_value () [concrete] // CHECK:STDOUT: %ImplicitAs.facet: %ImplicitAs.type.e8c = facet_value Core.IntLiteral, (%ImplicitAs.impl_witness.6bc) [concrete] // CHECK:STDOUT: %ImplicitAs.WithSelf.Convert.type.b37: type = fn_type @ImplicitAs.WithSelf.Convert, @ImplicitAs.WithSelf(%i32, %ImplicitAs.facet) [concrete] // CHECK:STDOUT: %.545: type = fn_type_with_self_type %ImplicitAs.WithSelf.Convert.type.b37, %ImplicitAs.facet [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.bound.215: = bound_method %int_1.5b8, %Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5 [concrete] // CHECK:STDOUT: %pattern_type.7ce: type = pattern_type %i32 [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn: = specific_function %Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5, @Core.IntLiteral.as.ImplicitAs.impl.Convert(%int_32) [concrete] // CHECK:STDOUT: %bound_method.38b: = bound_method %int_1.5b8, %Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn [concrete] // CHECK:STDOUT: %int_1.5d2: %i32 = int_value 1 [concrete] // CHECK:STDOUT: %int_2.ecc: Core.IntLiteral = int_value 2 [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.bound.4e5: = bound_method %int_2.ecc, %Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5 [concrete] // CHECK:STDOUT: %bound_method.646: = bound_method %int_2.ecc, %Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn [concrete] // CHECK:STDOUT: %int_2.ef8: %i32 = int_value 2 [concrete] // CHECK:STDOUT: %Copy.type: type = facet_type <@Copy> [concrete] // CHECK:STDOUT: %Int.as.Copy.impl.Op.type.824: type = fn_type @Int.as.Copy.impl.Op, @Int.as.Copy.impl(%N) [symbolic] // CHECK:STDOUT: %Int.as.Copy.impl.Op.9b9: %Int.as.Copy.impl.Op.type.824 = struct_value () [symbolic] // CHECK:STDOUT: %Copy.impl_witness.f17: = impl_witness imports.%Copy.impl_witness_table.e76, @Int.as.Copy.impl(%int_32) [concrete] // CHECK:STDOUT: %Int.as.Copy.impl.Op.type.546: type = fn_type @Int.as.Copy.impl.Op, @Int.as.Copy.impl(%int_32) [concrete] // CHECK:STDOUT: %Int.as.Copy.impl.Op.664: %Int.as.Copy.impl.Op.type.546 = struct_value () [concrete] // CHECK:STDOUT: %Copy.facet: %Copy.type = facet_value %i32, (%Copy.impl_witness.f17) [concrete] // CHECK:STDOUT: %Copy.WithSelf.Op.type.081: type = fn_type @Copy.WithSelf.Op, @Copy.WithSelf(%Copy.facet) [concrete] // CHECK:STDOUT: %.8e2: type = fn_type_with_self_type %Copy.WithSelf.Op.type.081, %Copy.facet [concrete] // CHECK:STDOUT: %Int.as.Copy.impl.Op.specific_fn: = specific_function %Int.as.Copy.impl.Op.664, @Int.as.Copy.impl.Op(%int_32) [concrete] // CHECK:STDOUT: %Destroy.type: type = facet_type <@Destroy> [concrete] // CHECK:STDOUT: %Destroy.Op.type.bae255.1: type = fn_type @Destroy.Op.loc26 [concrete] // CHECK:STDOUT: %Destroy.Op.651ba6.1: %Destroy.Op.type.bae255.1 = struct_value () [concrete] // CHECK:STDOUT: %Destroy.Op.type.bae255.2: type = fn_type @Destroy.Op.loc21 [concrete] // CHECK:STDOUT: %Destroy.Op.651ba6.2: %Destroy.Op.type.bae255.2 = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: .Int = %Core.Int // CHECK:STDOUT: .DefaultOrUnformed = %Core.DefaultOrUnformed // CHECK:STDOUT: .ImplicitAs = %Core.ImplicitAs // CHECK:STDOUT: .Copy = %Core.Copy // CHECK:STDOUT: .Destroy = %Core.Destroy // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: %Core.Int: %Int.type = import_ref Core//prelude/parts/int, Int, loaded [concrete = constants.%Int.generic] // CHECK:STDOUT: %Core.DefaultOrUnformed: type = import_ref Core//prelude/parts/default, DefaultOrUnformed, loaded [concrete = constants.%DefaultOrUnformed.type] // CHECK:STDOUT: %Core.import_ref.01d: @T.as.DefaultOrUnformed.impl.%T.as.DefaultOrUnformed.impl.Op.type (%T.as.DefaultOrUnformed.impl.Op.type.6c6) = import_ref Core//prelude/parts/default, loc{{\d+_\d+}}, loaded [symbolic = @T.as.DefaultOrUnformed.impl.%T.as.DefaultOrUnformed.impl.Op (constants.%T.as.DefaultOrUnformed.impl.Op.ae1)] // CHECK:STDOUT: %DefaultOrUnformed.impl_witness_table = impl_witness_table (%Core.import_ref.01d), @T.as.DefaultOrUnformed.impl [concrete] // CHECK:STDOUT: %Core.ImplicitAs: %ImplicitAs.type.cc7 = import_ref Core//prelude/parts/as, ImplicitAs, loaded [concrete = constants.%ImplicitAs.generic] // CHECK:STDOUT: %Core.import_ref.42d: @Core.IntLiteral.as.ImplicitAs.impl.%Core.IntLiteral.as.ImplicitAs.impl.Convert.type (%Core.IntLiteral.as.ImplicitAs.impl.Convert.type.4e6) = import_ref Core//prelude/parts/int, loc{{\d+_\d+}}, loaded [symbolic = @Core.IntLiteral.as.ImplicitAs.impl.%Core.IntLiteral.as.ImplicitAs.impl.Convert (constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.3c2)] // CHECK:STDOUT: %ImplicitAs.impl_witness_table.74f = impl_witness_table (%Core.import_ref.42d), @Core.IntLiteral.as.ImplicitAs.impl [concrete] // CHECK:STDOUT: %Core.Copy: type = import_ref Core//prelude/parts/copy, Copy, loaded [concrete = constants.%Copy.type] // CHECK:STDOUT: %Core.import_ref.18d: @Int.as.Copy.impl.%Int.as.Copy.impl.Op.type (%Int.as.Copy.impl.Op.type.824) = import_ref Core//prelude/parts/int, loc{{\d+_\d+}}, loaded [symbolic = @Int.as.Copy.impl.%Int.as.Copy.impl.Op (constants.%Int.as.Copy.impl.Op.9b9)] // CHECK:STDOUT: %Copy.impl_witness_table.e76 = impl_witness_table (%Core.import_ref.18d), @Int.as.Copy.impl [concrete] // CHECK:STDOUT: %Core.Destroy: type = import_ref Core//prelude/parts/destroy, Destroy, loaded [concrete = constants.%Destroy.type] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .Class = %Class.decl // CHECK:STDOUT: .Test = %Test.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %Class.decl: type = class_decl @Class [concrete = constants.%Class] {} {} // CHECK:STDOUT: %Test.decl: %Test.type = fn_decl @Test [concrete = constants.%Test] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @Class { // CHECK:STDOUT: %i32.loc16: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %.loc16: %Class.elem = field_decl j, element0 [concrete] // CHECK:STDOUT: %i32.loc17: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %.loc17: %Class.elem = field_decl k, element1 [concrete] // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%struct_type.j.k [concrete = constants.%complete_type.cf7] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%Class // CHECK:STDOUT: .j = %.loc16 // CHECK:STDOUT: .k = %.loc17 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @Test() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %cv.patt: %pattern_type.904 = ref_binding_pattern cv [concrete] // CHECK:STDOUT: %cv.var_patt: %pattern_type.904 = var_pattern %cv.patt [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %cv.var: ref %Class = var %cv.var_patt // CHECK:STDOUT: %DefaultOrUnformed.facet: %DefaultOrUnformed.type = facet_value constants.%Class, (constants.%DefaultOrUnformed.impl_witness.6ca) [concrete = constants.%DefaultOrUnformed.facet] // CHECK:STDOUT: %.loc21_16.1: %DefaultOrUnformed.type = converted constants.%Class, %DefaultOrUnformed.facet [concrete = constants.%DefaultOrUnformed.facet] // CHECK:STDOUT: %as_type: type = facet_access_type %.loc21_16.1 [concrete = constants.%Class] // CHECK:STDOUT: %.loc21_16.2: type = converted %.loc21_16.1, %as_type [concrete = constants.%Class] // CHECK:STDOUT: %T.as.DefaultOrUnformed.impl.Op.specific_fn: = specific_function constants.%T.as.DefaultOrUnformed.impl.Op.cda, @T.as.DefaultOrUnformed.impl.Op(constants.%Class) [concrete = constants.%T.as.DefaultOrUnformed.impl.Op.specific_fn] // CHECK:STDOUT: %.loc21_3: ref %Class = splice_block %cv.var {} // CHECK:STDOUT: %T.as.DefaultOrUnformed.impl.Op.call: init %Class to %.loc21_3 = call %T.as.DefaultOrUnformed.impl.Op.specific_fn() // CHECK:STDOUT: assign %cv.var, %T.as.DefaultOrUnformed.impl.Op.call // CHECK:STDOUT: %Class.ref.loc21: type = name_ref Class, file.%Class.decl [concrete = constants.%Class] // CHECK:STDOUT: %cv: ref %Class = ref_binding cv, %cv.var // CHECK:STDOUT: %cv.ref.loc22: ref %Class = name_ref cv, %cv // CHECK:STDOUT: %j.ref.loc22: %Class.elem = name_ref j, @Class.%.loc16 [concrete = @Class.%.loc16] // CHECK:STDOUT: %.loc22_5: ref %i32 = class_element_access %cv.ref.loc22, element0 // CHECK:STDOUT: %int_1: Core.IntLiteral = int_value 1 [concrete = constants.%int_1.5b8] // CHECK:STDOUT: %impl.elem0.loc22: %.545 = impl_witness_access constants.%ImplicitAs.impl_witness.6bc, element0 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5] // CHECK:STDOUT: %bound_method.loc22_8.1: = bound_method %int_1, %impl.elem0.loc22 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.bound.215] // CHECK:STDOUT: %specific_fn.loc22: = specific_function %impl.elem0.loc22, @Core.IntLiteral.as.ImplicitAs.impl.Convert(constants.%int_32) [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn] // CHECK:STDOUT: %bound_method.loc22_8.2: = bound_method %int_1, %specific_fn.loc22 [concrete = constants.%bound_method.38b] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc22: init %i32 = call %bound_method.loc22_8.2(%int_1) [concrete = constants.%int_1.5d2] // CHECK:STDOUT: %.loc22_8: init %i32 = converted %int_1, %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc22 [concrete = constants.%int_1.5d2] // CHECK:STDOUT: assign %.loc22_5, %.loc22_8 // CHECK:STDOUT: %cv.ref.loc23: ref %Class = name_ref cv, %cv // CHECK:STDOUT: %k.ref.loc23: %Class.elem = name_ref k, @Class.%.loc17 [concrete = @Class.%.loc17] // CHECK:STDOUT: %.loc23_5: ref %i32 = class_element_access %cv.ref.loc23, element1 // CHECK:STDOUT: %int_2: Core.IntLiteral = int_value 2 [concrete = constants.%int_2.ecc] // CHECK:STDOUT: %impl.elem0.loc23: %.545 = impl_witness_access constants.%ImplicitAs.impl_witness.6bc, element0 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5] // CHECK:STDOUT: %bound_method.loc23_8.1: = bound_method %int_2, %impl.elem0.loc23 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.bound.4e5] // CHECK:STDOUT: %specific_fn.loc23: = specific_function %impl.elem0.loc23, @Core.IntLiteral.as.ImplicitAs.impl.Convert(constants.%int_32) [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn] // CHECK:STDOUT: %bound_method.loc23_8.2: = bound_method %int_2, %specific_fn.loc23 [concrete = constants.%bound_method.646] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc23: init %i32 = call %bound_method.loc23_8.2(%int_2) [concrete = constants.%int_2.ef8] // CHECK:STDOUT: %.loc23_8: init %i32 = converted %int_2, %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc23 [concrete = constants.%int_2.ef8] // CHECK:STDOUT: assign %.loc23_5, %.loc23_8 // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %c.patt: %pattern_type.904 = value_binding_pattern c [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %cv.ref.loc24: ref %Class = name_ref cv, %cv // CHECK:STDOUT: %Class.ref.loc24: type = name_ref Class, file.%Class.decl [concrete = constants.%Class] // CHECK:STDOUT: %.loc24: %Class = acquire_value %cv.ref.loc24 // CHECK:STDOUT: %c: %Class = value_binding c, %.loc24 // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %cj.patt: %pattern_type.7ce = ref_binding_pattern cj [concrete] // CHECK:STDOUT: %cj.var_patt: %pattern_type.7ce = var_pattern %cj.patt [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %cj.var: ref %i32 = var %cj.var_patt // CHECK:STDOUT: %c.ref.loc25: %Class = name_ref c, %c // CHECK:STDOUT: %j.ref.loc25: %Class.elem = name_ref j, @Class.%.loc16 [concrete = @Class.%.loc16] // CHECK:STDOUT: %.loc25_25.1: ref %i32 = class_element_access %c.ref.loc25, element0 // CHECK:STDOUT: %.loc25_25.2: %i32 = acquire_value %.loc25_25.1 // CHECK:STDOUT: %impl.elem0.loc25: %.8e2 = impl_witness_access constants.%Copy.impl_witness.f17, element0 [concrete = constants.%Int.as.Copy.impl.Op.664] // CHECK:STDOUT: %bound_method.loc25_25.1: = bound_method %.loc25_25.2, %impl.elem0.loc25 // CHECK:STDOUT: %specific_fn.loc25: = specific_function %impl.elem0.loc25, @Int.as.Copy.impl.Op(constants.%int_32) [concrete = constants.%Int.as.Copy.impl.Op.specific_fn] // CHECK:STDOUT: %bound_method.loc25_25.2: = bound_method %.loc25_25.2, %specific_fn.loc25 // CHECK:STDOUT: %Int.as.Copy.impl.Op.call.loc25: init %i32 = call %bound_method.loc25_25.2(%.loc25_25.2) // CHECK:STDOUT: assign %cj.var, %Int.as.Copy.impl.Op.call.loc25 // CHECK:STDOUT: %i32.loc25: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %cj: ref %i32 = ref_binding cj, %cj.var // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %ck.patt: %pattern_type.7ce = ref_binding_pattern ck [concrete] // CHECK:STDOUT: %ck.var_patt: %pattern_type.7ce = var_pattern %ck.patt [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %ck.var: ref %i32 = var %ck.var_patt // CHECK:STDOUT: %c.ref.loc26: %Class = name_ref c, %c // CHECK:STDOUT: %k.ref.loc26: %Class.elem = name_ref k, @Class.%.loc17 [concrete = @Class.%.loc17] // CHECK:STDOUT: %.loc26_25.1: ref %i32 = class_element_access %c.ref.loc26, element1 // CHECK:STDOUT: %.loc26_25.2: %i32 = acquire_value %.loc26_25.1 // CHECK:STDOUT: %impl.elem0.loc26: %.8e2 = impl_witness_access constants.%Copy.impl_witness.f17, element0 [concrete = constants.%Int.as.Copy.impl.Op.664] // CHECK:STDOUT: %bound_method.loc26_25.1: = bound_method %.loc26_25.2, %impl.elem0.loc26 // CHECK:STDOUT: %specific_fn.loc26: = specific_function %impl.elem0.loc26, @Int.as.Copy.impl.Op(constants.%int_32) [concrete = constants.%Int.as.Copy.impl.Op.specific_fn] // CHECK:STDOUT: %bound_method.loc26_25.2: = bound_method %.loc26_25.2, %specific_fn.loc26 // CHECK:STDOUT: %Int.as.Copy.impl.Op.call.loc26: init %i32 = call %bound_method.loc26_25.2(%.loc26_25.2) // CHECK:STDOUT: assign %ck.var, %Int.as.Copy.impl.Op.call.loc26 // CHECK:STDOUT: %i32.loc26: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %ck: ref %i32 = ref_binding ck, %ck.var // CHECK:STDOUT: %Destroy.Op.bound.loc26: = bound_method %ck.var, constants.%Destroy.Op.651ba6.1 // CHECK:STDOUT: %Destroy.Op.call.loc26: init %empty_tuple.type = call %Destroy.Op.bound.loc26(%ck.var) // CHECK:STDOUT: %Destroy.Op.bound.loc25: = bound_method %cj.var, constants.%Destroy.Op.651ba6.1 // CHECK:STDOUT: %Destroy.Op.call.loc25: init %empty_tuple.type = call %Destroy.Op.bound.loc25(%cj.var) // CHECK:STDOUT: %Destroy.Op.bound.loc21: = bound_method %cv.var, constants.%Destroy.Op.651ba6.2 // CHECK:STDOUT: %Destroy.Op.call.loc21: init %empty_tuple.type = call %Destroy.Op.bound.loc21(%cv.var) // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @Destroy.Op.loc26(%self.param: ref %i32) = "no_op"; // CHECK:STDOUT: // CHECK:STDOUT: fn @Destroy.Op.loc21(%self.param: ref %Class) = "no_op"; // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/class/forward_declared.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/convert.carbon // TODO: Add ranges and switch to "--dump-sem-ir-ranges=only". // EXTRA-ARGS: --dump-sem-ir-ranges=if-present // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/class/forward_declared.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/class/forward_declared.carbon class Class; fn F(p: Class*) -> Class* { return p; } // CHECK:STDOUT: --- forward_declared.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %Class: type = class_type @Class [concrete] // CHECK:STDOUT: %ptr.8e5: type = ptr_type %Class [concrete] // CHECK:STDOUT: %pattern_type.018: type = pattern_type %ptr.8e5 [concrete] // CHECK:STDOUT: %.c69: Core.Form = init_form %ptr.8e5 [concrete] // CHECK:STDOUT: %F.type: type = fn_type @F [concrete] // CHECK:STDOUT: %F: %F.type = struct_value () [concrete] // CHECK:STDOUT: %Copy.type: type = facet_type <@Copy> [concrete] // CHECK:STDOUT: %T.67d: type = symbolic_binding T, 0 [symbolic] // CHECK:STDOUT: %ptr.as.Copy.impl.Op.type.2d4: type = fn_type @ptr.as.Copy.impl.Op, @ptr.as.Copy.impl(%T.67d) [symbolic] // CHECK:STDOUT: %ptr.as.Copy.impl.Op.74e: %ptr.as.Copy.impl.Op.type.2d4 = struct_value () [symbolic] // CHECK:STDOUT: %Copy.impl_witness.9d3: = impl_witness imports.%Copy.impl_witness_table.c3a, @ptr.as.Copy.impl(%Class) [concrete] // CHECK:STDOUT: %ptr.as.Copy.impl.Op.type.02e: type = fn_type @ptr.as.Copy.impl.Op, @ptr.as.Copy.impl(%Class) [concrete] // CHECK:STDOUT: %ptr.as.Copy.impl.Op.120: %ptr.as.Copy.impl.Op.type.02e = struct_value () [concrete] // CHECK:STDOUT: %Copy.facet: %Copy.type = facet_value %ptr.8e5, (%Copy.impl_witness.9d3) [concrete] // CHECK:STDOUT: %Copy.WithSelf.Op.type.130: type = fn_type @Copy.WithSelf.Op, @Copy.WithSelf(%Copy.facet) [concrete] // CHECK:STDOUT: %.370: type = fn_type_with_self_type %Copy.WithSelf.Op.type.130, %Copy.facet [concrete] // CHECK:STDOUT: %ptr.as.Copy.impl.Op.specific_fn: = specific_function %ptr.as.Copy.impl.Op.120, @ptr.as.Copy.impl.Op(%Class) [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: .Copy = %Core.Copy // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: %Core.Copy: type = import_ref Core//prelude/parts/copy, Copy, loaded [concrete = constants.%Copy.type] // CHECK:STDOUT: %Core.import_ref.203: @ptr.as.Copy.impl.%ptr.as.Copy.impl.Op.type (%ptr.as.Copy.impl.Op.type.2d4) = import_ref Core//prelude/parts/copy, loc{{\d+_\d+}}, loaded [symbolic = @ptr.as.Copy.impl.%ptr.as.Copy.impl.Op (constants.%ptr.as.Copy.impl.Op.74e)] // CHECK:STDOUT: %Copy.impl_witness_table.c3a = impl_witness_table (%Core.import_ref.203), @ptr.as.Copy.impl [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .Class = %Class.decl // CHECK:STDOUT: .F = %F.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %Class.decl: type = class_decl @Class [concrete = constants.%Class] {} {} // CHECK:STDOUT: %F.decl: %F.type = fn_decl @F [concrete = constants.%F] { // CHECK:STDOUT: %p.patt: %pattern_type.018 = value_binding_pattern p [concrete] // CHECK:STDOUT: %p.param_patt: %pattern_type.018 = value_param_pattern %p.patt [concrete] // CHECK:STDOUT: %return.patt: %pattern_type.018 = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: %pattern_type.018 = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %Class.ref.loc17_20: type = name_ref Class, file.%Class.decl [concrete = constants.%Class] // CHECK:STDOUT: %ptr.loc17_25: type = ptr_type %Class.ref.loc17_20 [concrete = constants.%ptr.8e5] // CHECK:STDOUT: %.loc17_25: Core.Form = init_form %ptr.loc17_25 [concrete = constants.%.c69] // CHECK:STDOUT: %p.param: %ptr.8e5 = value_param call_param0 // CHECK:STDOUT: %.loc17_14: type = splice_block %ptr.loc17_14 [concrete = constants.%ptr.8e5] { // CHECK:STDOUT: %Class.ref.loc17_9: type = name_ref Class, file.%Class.decl [concrete = constants.%Class] // CHECK:STDOUT: %ptr.loc17_14: type = ptr_type %Class.ref.loc17_9 [concrete = constants.%ptr.8e5] // CHECK:STDOUT: } // CHECK:STDOUT: %p: %ptr.8e5 = value_binding p, %p.param // CHECK:STDOUT: %return.param: ref %ptr.8e5 = out_param call_param1 // CHECK:STDOUT: %return: ref %ptr.8e5 = return_slot %return.param // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @Class; // CHECK:STDOUT: // CHECK:STDOUT: fn @F(%p.param: %ptr.8e5) -> out %return.param: %ptr.8e5 { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %p.ref: %ptr.8e5 = name_ref p, %p // CHECK:STDOUT: %impl.elem0: %.370 = impl_witness_access constants.%Copy.impl_witness.9d3, element0 [concrete = constants.%ptr.as.Copy.impl.Op.120] // CHECK:STDOUT: %bound_method.loc17_36.1: = bound_method %p.ref, %impl.elem0 // CHECK:STDOUT: %specific_fn: = specific_function %impl.elem0, @ptr.as.Copy.impl.Op(constants.%Class) [concrete = constants.%ptr.as.Copy.impl.Op.specific_fn] // CHECK:STDOUT: %bound_method.loc17_36.2: = bound_method %p.ref, %specific_fn // CHECK:STDOUT: %ptr.as.Copy.impl.Op.call: init %ptr.8e5 = call %bound_method.loc17_36.2(%p.ref) // CHECK:STDOUT: return %ptr.as.Copy.impl.Op.call // CHECK:STDOUT: } // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/class/generic/adapt.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/int.carbon // TODO: Add ranges and switch to "--dump-sem-ir-ranges=only". // EXTRA-ARGS: --dump-sem-ir-ranges=if-present // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/class/generic/adapt.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/class/generic/adapt.carbon // --- adapt_specific_type.carbon library "[[@TEST_NAME]]"; class C(T:! type) { var x: T; } class Adapter { adapt C(i32); } fn Access(a: Adapter) -> i32 { return (a as C(i32)).x; } // --- import_adapt_specific_type.carbon library "[[@TEST_NAME]]"; import library "adapt_specific_type"; fn ImportedAccess(a: Adapter) -> i32 { return (a as C(i32)).x; } // --- fail_todo_extend_adapt_specific_type.carbon library "[[@TEST_NAME]]"; class C(T:! type) { var x: T; } class Adapter { extend adapt C(i32); } fn Access(a: Adapter) -> i32 { // TODO: This should presumably work, but the design doesn't say how yet. // CHECK:STDERR: fail_todo_extend_adapt_specific_type.carbon:[[@LINE+7]]:10: error: cannot implicitly convert expression of type `Adapter` to `C(i32)` [ConversionFailure] // CHECK:STDERR: return a.x; // CHECK:STDERR: ^~~ // CHECK:STDERR: fail_todo_extend_adapt_specific_type.carbon:[[@LINE+4]]:10: note: type `Adapter` does not implement interface `Core.ImplicitAs(C(i32))` [MissingImplInMemberAccessInContext] // CHECK:STDERR: return a.x; // CHECK:STDERR: ^~~ // CHECK:STDERR: return a.x; } // --- extend_adapt_specific_type_library.carbon // TODO: Delete this file and change the next file to instead import the // previous file once the previous file can be successfully type-checked. library "[[@TEST_NAME]]"; class C(T:! type) { var x: T; } class Adapter { extend adapt C(i32); } // --- fail_todo_import_extend_adapt_specific_type.carbon library "[[@TEST_NAME]]"; import library "extend_adapt_specific_type_library"; fn ImportedAccess(a: Adapter) -> i32 { // TODO: This should presumably work, but the design doesn't say how yet. // CHECK:STDERR: fail_todo_import_extend_adapt_specific_type.carbon:[[@LINE+7]]:10: error: cannot implicitly convert expression of type `Adapter` to `C(i32)` [ConversionFailure] // CHECK:STDERR: return a.x; // CHECK:STDERR: ^~~ // CHECK:STDERR: fail_todo_import_extend_adapt_specific_type.carbon:[[@LINE+4]]:10: note: type `Adapter` does not implement interface `Core.ImplicitAs(C(i32))` [MissingImplInMemberAccessInContext] // CHECK:STDERR: return a.x; // CHECK:STDERR: ^~~ // CHECK:STDERR: return a.x; } // --- adapt_generic_type.carbon library "[[@TEST_NAME]]"; class Adapter(T:! type) { adapt T; } fn Convert(a: Adapter(i32)) -> i32 { return a as i32; } // --- import_adapt_generic_type.carbon library "[[@TEST_NAME]]"; import library "adapt_generic_type"; fn ImportedConvert(a: Adapter(i32)) -> i32 { return a as i32; } class C { var n: i32; } fn ImportedConvertLocal(a: Adapter(C)) -> i32 { return (a as C).n; } // CHECK:STDOUT: --- adapt_specific_type.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %type: type = facet_type [concrete] // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self] // CHECK:STDOUT: %pattern_type.98f: type = pattern_type type [concrete] // CHECK:STDOUT: %T.67d: type = symbolic_binding T, 0 [symbolic] // CHECK:STDOUT: %C.type: type = generic_class_type @C [concrete] // CHECK:STDOUT: %C.generic: %C.type = struct_value () [concrete] // CHECK:STDOUT: %C.5a3: type = class_type @C, @C(%T.67d) [symbolic] // CHECK:STDOUT: %require_complete.944: = require_complete_type %T.67d [symbolic] // CHECK:STDOUT: %C.elem.bd3: type = unbound_element_type %C.5a3, %T.67d [symbolic] // CHECK:STDOUT: %struct_type.x.0c5: type = struct_type {.x: %T.67d} [symbolic] // CHECK:STDOUT: %complete_type.735: = complete_type_witness %struct_type.x.0c5 [symbolic] // CHECK:STDOUT: %Adapter: type = class_type @Adapter [concrete] // CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [concrete] // CHECK:STDOUT: %Int.type: type = generic_class_type @Int [concrete] // CHECK:STDOUT: %Int.generic: %Int.type = struct_value () [concrete] // CHECK:STDOUT: %N: Core.IntLiteral = symbolic_binding N, 0 [symbolic] // CHECK:STDOUT: %i32: type = class_type @Int, @Int(%int_32) [concrete] // CHECK:STDOUT: %C.b13: type = class_type @C, @C(%i32) [concrete] // CHECK:STDOUT: %i32.builtin: type = int_type signed, %int_32 [concrete] // CHECK:STDOUT: %complete_type.f8a: = complete_type_witness %i32.builtin [concrete] // CHECK:STDOUT: %C.elem.8f4: type = unbound_element_type %C.b13, %i32 [concrete] // CHECK:STDOUT: %struct_type.x.ed6: type = struct_type {.x: %i32} [concrete] // CHECK:STDOUT: %complete_type.1ec: = complete_type_witness %struct_type.x.ed6 [concrete] // CHECK:STDOUT: %pattern_type.bf2: type = pattern_type %Adapter [concrete] // CHECK:STDOUT: %.ff5: Core.Form = init_form %i32 [concrete] // CHECK:STDOUT: %pattern_type.7ce: type = pattern_type %i32 [concrete] // CHECK:STDOUT: %Access.type: type = fn_type @Access [concrete] // CHECK:STDOUT: %Access: %Access.type = struct_value () [concrete] // CHECK:STDOUT: %Copy.type: type = facet_type <@Copy> [concrete] // CHECK:STDOUT: %Int.as.Copy.impl.Op.type.824: type = fn_type @Int.as.Copy.impl.Op, @Int.as.Copy.impl(%N) [symbolic] // CHECK:STDOUT: %Int.as.Copy.impl.Op.9b9: %Int.as.Copy.impl.Op.type.824 = struct_value () [symbolic] // CHECK:STDOUT: %Copy.impl_witness.f17: = impl_witness imports.%Copy.impl_witness_table.e76, @Int.as.Copy.impl(%int_32) [concrete] // CHECK:STDOUT: %Int.as.Copy.impl.Op.type.546: type = fn_type @Int.as.Copy.impl.Op, @Int.as.Copy.impl(%int_32) [concrete] // CHECK:STDOUT: %Int.as.Copy.impl.Op.664: %Int.as.Copy.impl.Op.type.546 = struct_value () [concrete] // CHECK:STDOUT: %Copy.facet: %Copy.type = facet_value %i32, (%Copy.impl_witness.f17) [concrete] // CHECK:STDOUT: %Copy.WithSelf.Op.type.081: type = fn_type @Copy.WithSelf.Op, @Copy.WithSelf(%Copy.facet) [concrete] // CHECK:STDOUT: %.8e2: type = fn_type_with_self_type %Copy.WithSelf.Op.type.081, %Copy.facet [concrete] // CHECK:STDOUT: %Int.as.Copy.impl.Op.specific_fn: = specific_function %Int.as.Copy.impl.Op.664, @Int.as.Copy.impl.Op(%int_32) [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: .Int = %Core.Int // CHECK:STDOUT: .Copy = %Core.Copy // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: %Core.Int: %Int.type = import_ref Core//prelude/parts/int, Int, loaded [concrete = constants.%Int.generic] // CHECK:STDOUT: %Core.Copy: type = import_ref Core//prelude/parts/copy, Copy, loaded [concrete = constants.%Copy.type] // CHECK:STDOUT: %Core.import_ref.18d: @Int.as.Copy.impl.%Int.as.Copy.impl.Op.type (%Int.as.Copy.impl.Op.type.824) = import_ref Core//prelude/parts/int, loc{{\d+_\d+}}, loaded [symbolic = @Int.as.Copy.impl.%Int.as.Copy.impl.Op (constants.%Int.as.Copy.impl.Op.9b9)] // CHECK:STDOUT: %Copy.impl_witness_table.e76 = impl_witness_table (%Core.import_ref.18d), @Int.as.Copy.impl [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .C = %C.decl // CHECK:STDOUT: .Adapter = %Adapter.decl // CHECK:STDOUT: .Access = %Access.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %C.decl: %C.type = class_decl @C [concrete = constants.%C.generic] { // CHECK:STDOUT: %T.patt: %pattern_type.98f = symbolic_binding_pattern T, 0 [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %.loc4_13.1: type = splice_block %.loc4_13.2 [concrete = type] { // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %.loc4_13.2: type = type_literal type [concrete = type] // CHECK:STDOUT: } // CHECK:STDOUT: %T.loc4_9.2: type = symbolic_binding T, 0 [symbolic = %T.loc4_9.1 (constants.%T.67d)] // CHECK:STDOUT: } // CHECK:STDOUT: %Adapter.decl: type = class_decl @Adapter [concrete = constants.%Adapter] {} {} // CHECK:STDOUT: %Access.decl: %Access.type = fn_decl @Access [concrete = constants.%Access] { // CHECK:STDOUT: %a.patt: %pattern_type.bf2 = value_binding_pattern a [concrete] // CHECK:STDOUT: %a.param_patt: %pattern_type.bf2 = value_param_pattern %a.patt [concrete] // CHECK:STDOUT: %return.patt: %pattern_type.7ce = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: %pattern_type.7ce = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %i32.loc12: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %.loc12: Core.Form = init_form %i32.loc12 [concrete = constants.%.ff5] // CHECK:STDOUT: %a.param: %Adapter = value_param call_param0 // CHECK:STDOUT: %Adapter.ref: type = name_ref Adapter, file.%Adapter.decl [concrete = constants.%Adapter] // CHECK:STDOUT: %a: %Adapter = value_binding a, %a.param // CHECK:STDOUT: %return.param: ref %i32 = out_param call_param1 // CHECK:STDOUT: %return: ref %i32 = return_slot %return.param // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic class @C(%T.loc4_9.2: type) { // CHECK:STDOUT: %T.loc4_9.1: type = symbolic_binding T, 0 [symbolic = %T.loc4_9.1 (constants.%T.67d)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete: = require_complete_type %T.loc4_9.1 [symbolic = %require_complete (constants.%require_complete.944)] // CHECK:STDOUT: %C: type = class_type @C, @C(%T.loc4_9.1) [symbolic = %C (constants.%C.5a3)] // CHECK:STDOUT: %C.elem: type = unbound_element_type %C, %T.loc4_9.1 [symbolic = %C.elem (constants.%C.elem.bd3)] // CHECK:STDOUT: %struct_type.x: type = struct_type {.x: @C.%T.loc4_9.1 (%T.67d)} [symbolic = %struct_type.x (constants.%struct_type.x.0c5)] // CHECK:STDOUT: %complete_type.loc6_1.2: = complete_type_witness %struct_type.x [symbolic = %complete_type.loc6_1.2 (constants.%complete_type.735)] // CHECK:STDOUT: // CHECK:STDOUT: class { // CHECK:STDOUT: %T.ref: type = name_ref T, %T.loc4_9.2 [symbolic = %T.loc4_9.1 (constants.%T.67d)] // CHECK:STDOUT: %.loc5: @C.%C.elem (%C.elem.bd3) = field_decl x, element0 [concrete] // CHECK:STDOUT: %complete_type.loc6_1.1: = complete_type_witness constants.%struct_type.x.0c5 [symbolic = %complete_type.loc6_1.2 (constants.%complete_type.735)] // CHECK:STDOUT: complete_type_witness = %complete_type.loc6_1.1 // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%C.5a3 // CHECK:STDOUT: .T = // CHECK:STDOUT: .x = %.loc5 // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @Adapter { // CHECK:STDOUT: %C.ref: %C.type = name_ref C, file.%C.decl [concrete = constants.%C.generic] // CHECK:STDOUT: %i32: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %C: type = class_type @C, @C(constants.%i32) [concrete = constants.%C.b13] // CHECK:STDOUT: adapt_decl %C [concrete] // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%struct_type.x.ed6 [concrete = constants.%complete_type.1ec] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%Adapter // CHECK:STDOUT: .C = // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @Access(%a.param: %Adapter) -> out %return.param: %i32 { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %a.ref: %Adapter = name_ref a, %a // CHECK:STDOUT: %C.ref: %C.type = name_ref C, file.%C.decl [concrete = constants.%C.generic] // CHECK:STDOUT: %i32.loc13: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %C: type = class_type @C, @C(constants.%i32) [concrete = constants.%C.b13] // CHECK:STDOUT: %.loc13_13.1: %C.b13 = as_compatible %a.ref // CHECK:STDOUT: %.loc13_13.2: %C.b13 = converted %a.ref, %.loc13_13.1 // CHECK:STDOUT: %x.ref: %C.elem.8f4 = name_ref x, @C.%.loc5 [concrete = @C.%.loc5] // CHECK:STDOUT: %.loc13_23.1: ref %i32 = class_element_access %.loc13_13.2, element0 // CHECK:STDOUT: %.loc13_23.2: %i32 = acquire_value %.loc13_23.1 // CHECK:STDOUT: %impl.elem0: %.8e2 = impl_witness_access constants.%Copy.impl_witness.f17, element0 [concrete = constants.%Int.as.Copy.impl.Op.664] // CHECK:STDOUT: %bound_method.loc13_23.1: = bound_method %.loc13_23.2, %impl.elem0 // CHECK:STDOUT: %specific_fn: = specific_function %impl.elem0, @Int.as.Copy.impl.Op(constants.%int_32) [concrete = constants.%Int.as.Copy.impl.Op.specific_fn] // CHECK:STDOUT: %bound_method.loc13_23.2: = bound_method %.loc13_23.2, %specific_fn // CHECK:STDOUT: %Int.as.Copy.impl.Op.call: init %i32 = call %bound_method.loc13_23.2(%.loc13_23.2) // CHECK:STDOUT: return %Int.as.Copy.impl.Op.call // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @C(constants.%T.67d) { // CHECK:STDOUT: %T.loc4_9.1 => constants.%T.67d // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @C(constants.%i32) { // CHECK:STDOUT: %T.loc4_9.1 => constants.%i32 // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete => constants.%complete_type.f8a // CHECK:STDOUT: %C => constants.%C.b13 // CHECK:STDOUT: %C.elem => constants.%C.elem.8f4 // CHECK:STDOUT: %struct_type.x => constants.%struct_type.x.ed6 // CHECK:STDOUT: %complete_type.loc6_1.2 => constants.%complete_type.1ec // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- import_adapt_specific_type.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %Adapter: type = class_type @Adapter [concrete] // CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [concrete] // CHECK:STDOUT: %Int.type: type = generic_class_type @Int [concrete] // CHECK:STDOUT: %Int.generic: %Int.type = struct_value () [concrete] // CHECK:STDOUT: %N: Core.IntLiteral = symbolic_binding N, 0 [symbolic] // CHECK:STDOUT: %i32: type = class_type @Int, @Int(%int_32) [concrete] // CHECK:STDOUT: %i32.builtin: type = int_type signed, %int_32 [concrete] // CHECK:STDOUT: %complete_type.f8a: = complete_type_witness %i32.builtin [concrete] // CHECK:STDOUT: %C.type: type = generic_class_type @C [concrete] // CHECK:STDOUT: %C.generic: %C.type = struct_value () [concrete] // CHECK:STDOUT: %T.67d: type = symbolic_binding T, 0 [symbolic] // CHECK:STDOUT: %struct_type.x.0c5: type = struct_type {.x: %T.67d} [symbolic] // CHECK:STDOUT: %complete_type.735: = complete_type_witness %struct_type.x.0c5 [symbolic] // CHECK:STDOUT: %C.5a3: type = class_type @C, @C(%T.67d) [symbolic] // CHECK:STDOUT: %C.elem.bd3: type = unbound_element_type %C.5a3, %T.67d [symbolic] // CHECK:STDOUT: %require_complete.944: = require_complete_type %T.67d [symbolic] // CHECK:STDOUT: %C.829: type = class_type @C, @C(%i32) [concrete] // CHECK:STDOUT: %struct_type.x.767: type = struct_type {.x: %i32} [concrete] // CHECK:STDOUT: %complete_type.c07: = complete_type_witness %struct_type.x.767 [concrete] // CHECK:STDOUT: %C.elem.fd3: type = unbound_element_type %C.829, %i32 [concrete] // CHECK:STDOUT: %pattern_type.bf2: type = pattern_type %Adapter [concrete] // CHECK:STDOUT: %.4ca: Core.Form = init_form %i32 [concrete] // CHECK:STDOUT: %pattern_type.501: type = pattern_type %i32 [concrete] // CHECK:STDOUT: %ImportedAccess.type: type = fn_type @ImportedAccess [concrete] // CHECK:STDOUT: %ImportedAccess: %ImportedAccess.type = struct_value () [concrete] // CHECK:STDOUT: %Copy.type: type = facet_type <@Copy> [concrete] // CHECK:STDOUT: %Int.as.Copy.impl.Op.type.08e: type = fn_type @Int.as.Copy.impl.Op, @Int.as.Copy.impl(%N) [symbolic] // CHECK:STDOUT: %Int.as.Copy.impl.Op.014: %Int.as.Copy.impl.Op.type.08e = struct_value () [symbolic] // CHECK:STDOUT: %Copy.impl_witness.a2f: = impl_witness imports.%Copy.impl_witness_table.d6d, @Int.as.Copy.impl(%int_32) [concrete] // CHECK:STDOUT: %Int.as.Copy.impl.Op.type.837: type = fn_type @Int.as.Copy.impl.Op, @Int.as.Copy.impl(%int_32) [concrete] // CHECK:STDOUT: %Int.as.Copy.impl.Op.2b1: %Int.as.Copy.impl.Op.type.837 = struct_value () [concrete] // CHECK:STDOUT: %Copy.facet: %Copy.type = facet_value %i32, (%Copy.impl_witness.a2f) [concrete] // CHECK:STDOUT: %Copy.WithSelf.Op.type.67d: type = fn_type @Copy.WithSelf.Op, @Copy.WithSelf(%Copy.facet) [concrete] // CHECK:STDOUT: %.d31: type = fn_type_with_self_type %Copy.WithSelf.Op.type.67d, %Copy.facet [concrete] // CHECK:STDOUT: %Int.as.Copy.impl.Op.specific_fn: = specific_function %Int.as.Copy.impl.Op.2b1, @Int.as.Copy.impl.Op(%int_32) [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Main.C: %C.type = import_ref Main//adapt_specific_type, C, loaded [concrete = constants.%C.generic] // CHECK:STDOUT: %Main.Adapter: type = import_ref Main//adapt_specific_type, Adapter, loaded [concrete = constants.%Adapter] // CHECK:STDOUT: %Main.Access = import_ref Main//adapt_specific_type, Access, unloaded // CHECK:STDOUT: %Core.ece: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: .Int = %Core.Int // CHECK:STDOUT: .Copy = %Core.Copy // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: %Main.import_ref.7e3: = import_ref Main//adapt_specific_type, loc6_1, loaded [symbolic = @C.%complete_type (constants.%complete_type.735)] // CHECK:STDOUT: %Main.import_ref.820 = import_ref Main//adapt_specific_type, inst{{[0-9A-F]+}} [no loc], unloaded // CHECK:STDOUT: %Main.import_ref.b94: @C.%C.elem (%C.elem.bd3) = import_ref Main//adapt_specific_type, loc5_8, loaded [concrete = %.68d] // CHECK:STDOUT: %Main.import_ref.b3b: type = import_ref Main//adapt_specific_type, loc4_9, loaded [symbolic = @C.%T (constants.%T.67d)] // CHECK:STDOUT: %Main.import_ref.709: = import_ref Main//adapt_specific_type, loc10_1, loaded [concrete = constants.%complete_type.c07] // CHECK:STDOUT: %Main.import_ref.085 = import_ref Main//adapt_specific_type, inst{{[0-9A-F]+}} [no loc], unloaded // CHECK:STDOUT: %Core.Int: %Int.type = import_ref Core//prelude/parts/int, Int, loaded [concrete = constants.%Int.generic] // CHECK:STDOUT: %.68d: @C.%C.elem (%C.elem.bd3) = field_decl x, element0 [concrete] // CHECK:STDOUT: %Core.Copy: type = import_ref Core//prelude/parts/copy, Copy, loaded [concrete = constants.%Copy.type] // CHECK:STDOUT: %Core.import_ref.ea6: @Int.as.Copy.impl.%Int.as.Copy.impl.Op.type (%Int.as.Copy.impl.Op.type.08e) = import_ref Core//prelude/parts/int, loc{{\d+_\d+}}, loaded [symbolic = @Int.as.Copy.impl.%Int.as.Copy.impl.Op (constants.%Int.as.Copy.impl.Op.014)] // CHECK:STDOUT: %Copy.impl_witness_table.d6d = impl_witness_table (%Core.import_ref.ea6), @Int.as.Copy.impl [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .C = imports.%Main.C // CHECK:STDOUT: .Adapter = imports.%Main.Adapter // CHECK:STDOUT: .Access = imports.%Main.Access // CHECK:STDOUT: .Core = imports.%Core.ece // CHECK:STDOUT: .ImportedAccess = %ImportedAccess.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %default.import = import // CHECK:STDOUT: %ImportedAccess.decl: %ImportedAccess.type = fn_decl @ImportedAccess [concrete = constants.%ImportedAccess] { // CHECK:STDOUT: %a.patt: %pattern_type.bf2 = value_binding_pattern a [concrete] // CHECK:STDOUT: %a.param_patt: %pattern_type.bf2 = value_param_pattern %a.patt [concrete] // CHECK:STDOUT: %return.patt: %pattern_type.501 = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: %pattern_type.501 = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %i32.loc6: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %.loc6: Core.Form = init_form %i32.loc6 [concrete = constants.%.4ca] // CHECK:STDOUT: %a.param: %Adapter = value_param call_param0 // CHECK:STDOUT: %Adapter.ref: type = name_ref Adapter, imports.%Main.Adapter [concrete = constants.%Adapter] // CHECK:STDOUT: %a: %Adapter = value_binding a, %a.param // CHECK:STDOUT: %return.param: ref %i32 = out_param call_param1 // CHECK:STDOUT: %return: ref %i32 = return_slot %return.param // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @Adapter [from "adapt_specific_type.carbon"] { // CHECK:STDOUT: complete_type_witness = imports.%Main.import_ref.709 // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = imports.%Main.import_ref.085 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic class @C(imports.%Main.import_ref.b3b: type) [from "adapt_specific_type.carbon"] { // CHECK:STDOUT: %T: type = symbolic_binding T, 0 [symbolic = %T (constants.%T.67d)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete: = require_complete_type %T [symbolic = %require_complete (constants.%require_complete.944)] // CHECK:STDOUT: %C: type = class_type @C, @C(%T) [symbolic = %C (constants.%C.5a3)] // CHECK:STDOUT: %C.elem: type = unbound_element_type %C, %T [symbolic = %C.elem (constants.%C.elem.bd3)] // CHECK:STDOUT: %struct_type.x: type = struct_type {.x: @C.%T (%T.67d)} [symbolic = %struct_type.x (constants.%struct_type.x.0c5)] // CHECK:STDOUT: %complete_type: = complete_type_witness %struct_type.x [symbolic = %complete_type (constants.%complete_type.735)] // CHECK:STDOUT: // CHECK:STDOUT: class { // CHECK:STDOUT: complete_type_witness = imports.%Main.import_ref.7e3 // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = imports.%Main.import_ref.820 // CHECK:STDOUT: .x = imports.%Main.import_ref.b94 // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @ImportedAccess(%a.param: %Adapter) -> out %return.param: %i32 { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %a.ref: %Adapter = name_ref a, %a // CHECK:STDOUT: %C.ref: %C.type = name_ref C, imports.%Main.C [concrete = constants.%C.generic] // CHECK:STDOUT: %i32.loc7: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %C: type = class_type @C, @C(constants.%i32) [concrete = constants.%C.829] // CHECK:STDOUT: %.loc7_13.1: %C.829 = as_compatible %a.ref // CHECK:STDOUT: %.loc7_13.2: %C.829 = converted %a.ref, %.loc7_13.1 // CHECK:STDOUT: %x.ref: %C.elem.fd3 = name_ref x, imports.%Main.import_ref.b94 [concrete = imports.%.68d] // CHECK:STDOUT: %.loc7_23.1: ref %i32 = class_element_access %.loc7_13.2, element0 // CHECK:STDOUT: %.loc7_23.2: %i32 = acquire_value %.loc7_23.1 // CHECK:STDOUT: %impl.elem0: %.d31 = impl_witness_access constants.%Copy.impl_witness.a2f, element0 [concrete = constants.%Int.as.Copy.impl.Op.2b1] // CHECK:STDOUT: %bound_method.loc7_23.1: = bound_method %.loc7_23.2, %impl.elem0 // CHECK:STDOUT: %specific_fn: = specific_function %impl.elem0, @Int.as.Copy.impl.Op(constants.%int_32) [concrete = constants.%Int.as.Copy.impl.Op.specific_fn] // CHECK:STDOUT: %bound_method.loc7_23.2: = bound_method %.loc7_23.2, %specific_fn // CHECK:STDOUT: %Int.as.Copy.impl.Op.call: init %i32 = call %bound_method.loc7_23.2(%.loc7_23.2) // CHECK:STDOUT: return %Int.as.Copy.impl.Op.call // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @C(constants.%T.67d) { // CHECK:STDOUT: %T => constants.%T.67d // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @C(constants.%i32) { // CHECK:STDOUT: %T => constants.%i32 // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete => constants.%complete_type.f8a // CHECK:STDOUT: %C => constants.%C.829 // CHECK:STDOUT: %C.elem => constants.%C.elem.fd3 // CHECK:STDOUT: %struct_type.x => constants.%struct_type.x.767 // CHECK:STDOUT: %complete_type => constants.%complete_type.c07 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- fail_todo_extend_adapt_specific_type.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %type: type = facet_type [concrete] // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self] // CHECK:STDOUT: %pattern_type.98f: type = pattern_type type [concrete] // CHECK:STDOUT: %T: type = symbolic_binding T, 0 [symbolic] // CHECK:STDOUT: %C.type: type = generic_class_type @C [concrete] // CHECK:STDOUT: %C.generic: %C.type = struct_value () [concrete] // CHECK:STDOUT: %C.5a3: type = class_type @C, @C(%T) [symbolic] // CHECK:STDOUT: %require_complete.944: = require_complete_type %T [symbolic] // CHECK:STDOUT: %C.elem.bd3: type = unbound_element_type %C.5a3, %T [symbolic] // CHECK:STDOUT: %struct_type.x.0c5: type = struct_type {.x: %T} [symbolic] // CHECK:STDOUT: %complete_type.735: = complete_type_witness %struct_type.x.0c5 [symbolic] // CHECK:STDOUT: %Adapter: type = class_type @Adapter [concrete] // CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [concrete] // CHECK:STDOUT: %Int.type: type = generic_class_type @Int [concrete] // CHECK:STDOUT: %Int.generic: %Int.type = struct_value () [concrete] // CHECK:STDOUT: %i32: type = class_type @Int, @Int(%int_32) [concrete] // CHECK:STDOUT: %C.b13: type = class_type @C, @C(%i32) [concrete] // CHECK:STDOUT: %i32.builtin: type = int_type signed, %int_32 [concrete] // CHECK:STDOUT: %complete_type.f8a: = complete_type_witness %i32.builtin [concrete] // CHECK:STDOUT: %C.elem.8f4: type = unbound_element_type %C.b13, %i32 [concrete] // CHECK:STDOUT: %struct_type.x.ed6: type = struct_type {.x: %i32} [concrete] // CHECK:STDOUT: %complete_type.1ec: = complete_type_witness %struct_type.x.ed6 [concrete] // CHECK:STDOUT: %pattern_type.bf2: type = pattern_type %Adapter [concrete] // CHECK:STDOUT: %.ff5: Core.Form = init_form %i32 [concrete] // CHECK:STDOUT: %pattern_type.7ce: type = pattern_type %i32 [concrete] // CHECK:STDOUT: %Access.type: type = fn_type @Access [concrete] // CHECK:STDOUT: %Access: %Access.type = struct_value () [concrete] // CHECK:STDOUT: %ImplicitAs.type.cc7: type = generic_interface_type @ImplicitAs [concrete] // CHECK:STDOUT: %ImplicitAs.generic: %ImplicitAs.type.cc7 = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: .Int = %Core.Int // CHECK:STDOUT: .ImplicitAs = %Core.ImplicitAs // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: %Core.Int: %Int.type = import_ref Core//prelude/parts/int, Int, loaded [concrete = constants.%Int.generic] // CHECK:STDOUT: %Core.ImplicitAs: %ImplicitAs.type.cc7 = import_ref Core//prelude/parts/as, ImplicitAs, loaded [concrete = constants.%ImplicitAs.generic] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .C = %C.decl // CHECK:STDOUT: .Adapter = %Adapter.decl // CHECK:STDOUT: .Access = %Access.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %C.decl: %C.type = class_decl @C [concrete = constants.%C.generic] { // CHECK:STDOUT: %T.patt: %pattern_type.98f = symbolic_binding_pattern T, 0 [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %.loc4_13.1: type = splice_block %.loc4_13.2 [concrete = type] { // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %.loc4_13.2: type = type_literal type [concrete = type] // CHECK:STDOUT: } // CHECK:STDOUT: %T.loc4_9.2: type = symbolic_binding T, 0 [symbolic = %T.loc4_9.1 (constants.%T)] // CHECK:STDOUT: } // CHECK:STDOUT: %Adapter.decl: type = class_decl @Adapter [concrete = constants.%Adapter] {} {} // CHECK:STDOUT: %Access.decl: %Access.type = fn_decl @Access [concrete = constants.%Access] { // CHECK:STDOUT: %a.patt: %pattern_type.bf2 = value_binding_pattern a [concrete] // CHECK:STDOUT: %a.param_patt: %pattern_type.bf2 = value_param_pattern %a.patt [concrete] // CHECK:STDOUT: %return.patt: %pattern_type.7ce = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: %pattern_type.7ce = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %i32: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %.loc12: Core.Form = init_form %i32 [concrete = constants.%.ff5] // CHECK:STDOUT: %a.param: %Adapter = value_param call_param0 // CHECK:STDOUT: %Adapter.ref: type = name_ref Adapter, file.%Adapter.decl [concrete = constants.%Adapter] // CHECK:STDOUT: %a: %Adapter = value_binding a, %a.param // CHECK:STDOUT: %return.param: ref %i32 = out_param call_param1 // CHECK:STDOUT: %return: ref %i32 = return_slot %return.param // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic class @C(%T.loc4_9.2: type) { // CHECK:STDOUT: %T.loc4_9.1: type = symbolic_binding T, 0 [symbolic = %T.loc4_9.1 (constants.%T)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete: = require_complete_type %T.loc4_9.1 [symbolic = %require_complete (constants.%require_complete.944)] // CHECK:STDOUT: %C: type = class_type @C, @C(%T.loc4_9.1) [symbolic = %C (constants.%C.5a3)] // CHECK:STDOUT: %C.elem: type = unbound_element_type %C, %T.loc4_9.1 [symbolic = %C.elem (constants.%C.elem.bd3)] // CHECK:STDOUT: %struct_type.x: type = struct_type {.x: @C.%T.loc4_9.1 (%T)} [symbolic = %struct_type.x (constants.%struct_type.x.0c5)] // CHECK:STDOUT: %complete_type.loc6_1.2: = complete_type_witness %struct_type.x [symbolic = %complete_type.loc6_1.2 (constants.%complete_type.735)] // CHECK:STDOUT: // CHECK:STDOUT: class { // CHECK:STDOUT: %T.ref: type = name_ref T, %T.loc4_9.2 [symbolic = %T.loc4_9.1 (constants.%T)] // CHECK:STDOUT: %.loc5: @C.%C.elem (%C.elem.bd3) = field_decl x, element0 [concrete] // CHECK:STDOUT: %complete_type.loc6_1.1: = complete_type_witness constants.%struct_type.x.0c5 [symbolic = %complete_type.loc6_1.2 (constants.%complete_type.735)] // CHECK:STDOUT: complete_type_witness = %complete_type.loc6_1.1 // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%C.5a3 // CHECK:STDOUT: .T = // CHECK:STDOUT: .x = %.loc5 // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @Adapter { // CHECK:STDOUT: %C.ref: %C.type = name_ref C, file.%C.decl [concrete = constants.%C.generic] // CHECK:STDOUT: %i32: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %C: type = class_type @C, @C(constants.%i32) [concrete = constants.%C.b13] // CHECK:STDOUT: adapt_decl %C [concrete] // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%struct_type.x.ed6 [concrete = constants.%complete_type.1ec] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%Adapter // CHECK:STDOUT: .C = // CHECK:STDOUT: .x = // CHECK:STDOUT: extend %C // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @Access(%a.param: %Adapter) -> out %return.param: %i32 { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %a.ref: %Adapter = name_ref a, %a // CHECK:STDOUT: %x.ref: %C.elem.8f4 = name_ref x, @C.%.loc5 [concrete = @C.%.loc5] // CHECK:STDOUT: %.loc21_11.1: %C.b13 = converted %a.ref, [concrete = ] // CHECK:STDOUT: %.loc21_11.2: %i32 = class_element_access , element0 [concrete = ] // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @C(constants.%T) { // CHECK:STDOUT: %T.loc4_9.1 => constants.%T // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @C(constants.%i32) { // CHECK:STDOUT: %T.loc4_9.1 => constants.%i32 // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete => constants.%complete_type.f8a // CHECK:STDOUT: %C => constants.%C.b13 // CHECK:STDOUT: %C.elem => constants.%C.elem.8f4 // CHECK:STDOUT: %struct_type.x => constants.%struct_type.x.ed6 // CHECK:STDOUT: %complete_type.loc6_1.2 => constants.%complete_type.1ec // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- extend_adapt_specific_type_library.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %type: type = facet_type [concrete] // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self] // CHECK:STDOUT: %pattern_type.98f: type = pattern_type type [concrete] // CHECK:STDOUT: %T: type = symbolic_binding T, 0 [symbolic] // CHECK:STDOUT: %C.type: type = generic_class_type @C [concrete] // CHECK:STDOUT: %C.generic: %C.type = struct_value () [concrete] // CHECK:STDOUT: %C.5a3: type = class_type @C, @C(%T) [symbolic] // CHECK:STDOUT: %require_complete.944: = require_complete_type %T [symbolic] // CHECK:STDOUT: %C.elem.bd3: type = unbound_element_type %C.5a3, %T [symbolic] // CHECK:STDOUT: %struct_type.x.0c5: type = struct_type {.x: %T} [symbolic] // CHECK:STDOUT: %complete_type.735: = complete_type_witness %struct_type.x.0c5 [symbolic] // CHECK:STDOUT: %Adapter: type = class_type @Adapter [concrete] // CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [concrete] // CHECK:STDOUT: %Int.type: type = generic_class_type @Int [concrete] // CHECK:STDOUT: %Int.generic: %Int.type = struct_value () [concrete] // CHECK:STDOUT: %i32: type = class_type @Int, @Int(%int_32) [concrete] // CHECK:STDOUT: %C.b13: type = class_type @C, @C(%i32) [concrete] // CHECK:STDOUT: %i32.builtin: type = int_type signed, %int_32 [concrete] // CHECK:STDOUT: %complete_type.f8a: = complete_type_witness %i32.builtin [concrete] // CHECK:STDOUT: %C.elem.8f4: type = unbound_element_type %C.b13, %i32 [concrete] // CHECK:STDOUT: %struct_type.x.ed6: type = struct_type {.x: %i32} [concrete] // CHECK:STDOUT: %complete_type.1ec: = complete_type_witness %struct_type.x.ed6 [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: .Int = %Core.Int // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: %Core.Int: %Int.type = import_ref Core//prelude/parts/int, Int, loaded [concrete = constants.%Int.generic] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .C = %C.decl // CHECK:STDOUT: .Adapter = %Adapter.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %C.decl: %C.type = class_decl @C [concrete = constants.%C.generic] { // CHECK:STDOUT: %T.patt: %pattern_type.98f = symbolic_binding_pattern T, 0 [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %.loc7_13.1: type = splice_block %.loc7_13.2 [concrete = type] { // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %.loc7_13.2: type = type_literal type [concrete = type] // CHECK:STDOUT: } // CHECK:STDOUT: %T.loc7_9.2: type = symbolic_binding T, 0 [symbolic = %T.loc7_9.1 (constants.%T)] // CHECK:STDOUT: } // CHECK:STDOUT: %Adapter.decl: type = class_decl @Adapter [concrete = constants.%Adapter] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic class @C(%T.loc7_9.2: type) { // CHECK:STDOUT: %T.loc7_9.1: type = symbolic_binding T, 0 [symbolic = %T.loc7_9.1 (constants.%T)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete: = require_complete_type %T.loc7_9.1 [symbolic = %require_complete (constants.%require_complete.944)] // CHECK:STDOUT: %C: type = class_type @C, @C(%T.loc7_9.1) [symbolic = %C (constants.%C.5a3)] // CHECK:STDOUT: %C.elem: type = unbound_element_type %C, %T.loc7_9.1 [symbolic = %C.elem (constants.%C.elem.bd3)] // CHECK:STDOUT: %struct_type.x: type = struct_type {.x: @C.%T.loc7_9.1 (%T)} [symbolic = %struct_type.x (constants.%struct_type.x.0c5)] // CHECK:STDOUT: %complete_type.loc9_1.2: = complete_type_witness %struct_type.x [symbolic = %complete_type.loc9_1.2 (constants.%complete_type.735)] // CHECK:STDOUT: // CHECK:STDOUT: class { // CHECK:STDOUT: %T.ref: type = name_ref T, %T.loc7_9.2 [symbolic = %T.loc7_9.1 (constants.%T)] // CHECK:STDOUT: %.loc8: @C.%C.elem (%C.elem.bd3) = field_decl x, element0 [concrete] // CHECK:STDOUT: %complete_type.loc9_1.1: = complete_type_witness constants.%struct_type.x.0c5 [symbolic = %complete_type.loc9_1.2 (constants.%complete_type.735)] // CHECK:STDOUT: complete_type_witness = %complete_type.loc9_1.1 // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%C.5a3 // CHECK:STDOUT: .T = // CHECK:STDOUT: .x = %.loc8 // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @Adapter { // CHECK:STDOUT: %C.ref: %C.type = name_ref C, file.%C.decl [concrete = constants.%C.generic] // CHECK:STDOUT: %i32: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %C: type = class_type @C, @C(constants.%i32) [concrete = constants.%C.b13] // CHECK:STDOUT: adapt_decl %C [concrete] // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%struct_type.x.ed6 [concrete = constants.%complete_type.1ec] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%Adapter // CHECK:STDOUT: .C = // CHECK:STDOUT: extend %C // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @C(constants.%T) { // CHECK:STDOUT: %T.loc7_9.1 => constants.%T // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @C(constants.%i32) { // CHECK:STDOUT: %T.loc7_9.1 => constants.%i32 // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete => constants.%complete_type.f8a // CHECK:STDOUT: %C => constants.%C.b13 // CHECK:STDOUT: %C.elem => constants.%C.elem.8f4 // CHECK:STDOUT: %struct_type.x => constants.%struct_type.x.ed6 // CHECK:STDOUT: %complete_type.loc9_1.2 => constants.%complete_type.1ec // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- fail_todo_import_extend_adapt_specific_type.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %Adapter: type = class_type @Adapter [concrete] // CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [concrete] // CHECK:STDOUT: %Int.type: type = generic_class_type @Int [concrete] // CHECK:STDOUT: %Int.generic: %Int.type = struct_value () [concrete] // CHECK:STDOUT: %i32: type = class_type @Int, @Int(%int_32) [concrete] // CHECK:STDOUT: %i32.builtin: type = int_type signed, %int_32 [concrete] // CHECK:STDOUT: %complete_type.f8a: = complete_type_witness %i32.builtin [concrete] // CHECK:STDOUT: %T: type = symbolic_binding T, 0 [symbolic] // CHECK:STDOUT: %struct_type.x.0c5: type = struct_type {.x: %T} [symbolic] // CHECK:STDOUT: %complete_type.735: = complete_type_witness %struct_type.x.0c5 [symbolic] // CHECK:STDOUT: %C.5a3: type = class_type @C, @C(%T) [symbolic] // CHECK:STDOUT: %C.elem.bd3: type = unbound_element_type %C.5a3, %T [symbolic] // CHECK:STDOUT: %require_complete.944: = require_complete_type %T [symbolic] // CHECK:STDOUT: %C.829: type = class_type @C, @C(%i32) [concrete] // CHECK:STDOUT: %struct_type.x.767: type = struct_type {.x: %i32} [concrete] // CHECK:STDOUT: %complete_type.c07: = complete_type_witness %struct_type.x.767 [concrete] // CHECK:STDOUT: %C.elem.fd3: type = unbound_element_type %C.829, %i32 [concrete] // CHECK:STDOUT: %pattern_type.bf2: type = pattern_type %Adapter [concrete] // CHECK:STDOUT: %.4ca: Core.Form = init_form %i32 [concrete] // CHECK:STDOUT: %pattern_type.501: type = pattern_type %i32 [concrete] // CHECK:STDOUT: %ImportedAccess.type: type = fn_type @ImportedAccess [concrete] // CHECK:STDOUT: %ImportedAccess: %ImportedAccess.type = struct_value () [concrete] // CHECK:STDOUT: %ImplicitAs.type.cc7: type = generic_interface_type @ImplicitAs [concrete] // CHECK:STDOUT: %ImplicitAs.generic: %ImplicitAs.type.cc7 = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Main.C = import_ref Main//extend_adapt_specific_type_library, C, unloaded // CHECK:STDOUT: %Main.Adapter: type = import_ref Main//extend_adapt_specific_type_library, Adapter, loaded [concrete = constants.%Adapter] // CHECK:STDOUT: %Core.ece: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: .Int = %Core.Int // CHECK:STDOUT: .ImplicitAs = %Core.ImplicitAs // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: %Main.import_ref.7e3: = import_ref Main//extend_adapt_specific_type_library, loc9_1, loaded [symbolic = @C.%complete_type (constants.%complete_type.735)] // CHECK:STDOUT: %Main.import_ref.820 = import_ref Main//extend_adapt_specific_type_library, inst{{[0-9A-F]+}} [no loc], unloaded // CHECK:STDOUT: %Main.import_ref.b94: @C.%C.elem (%C.elem.bd3) = import_ref Main//extend_adapt_specific_type_library, loc8_8, loaded [concrete = %.68d] // CHECK:STDOUT: %Main.import_ref.b3b: type = import_ref Main//extend_adapt_specific_type_library, loc7_9, loaded [symbolic = @C.%T (constants.%T)] // CHECK:STDOUT: %Main.import_ref.709: = import_ref Main//extend_adapt_specific_type_library, loc13_1, loaded [concrete = constants.%complete_type.c07] // CHECK:STDOUT: %Main.import_ref.085 = import_ref Main//extend_adapt_specific_type_library, inst{{[0-9A-F]+}} [no loc], unloaded // CHECK:STDOUT: %Main.import_ref.9da228.2: type = import_ref Main//extend_adapt_specific_type_library, loc12_21, loaded [concrete = constants.%C.829] // CHECK:STDOUT: %Core.Int: %Int.type = import_ref Core//prelude/parts/int, Int, loaded [concrete = constants.%Int.generic] // CHECK:STDOUT: %.68d: @C.%C.elem (%C.elem.bd3) = field_decl x, element0 [concrete] // CHECK:STDOUT: %Core.ImplicitAs: %ImplicitAs.type.cc7 = import_ref Core//prelude/parts/as, ImplicitAs, loaded [concrete = constants.%ImplicitAs.generic] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .C = imports.%Main.C // CHECK:STDOUT: .Adapter = imports.%Main.Adapter // CHECK:STDOUT: .Core = imports.%Core.ece // CHECK:STDOUT: .ImportedAccess = %ImportedAccess.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %default.import = import // CHECK:STDOUT: %ImportedAccess.decl: %ImportedAccess.type = fn_decl @ImportedAccess [concrete = constants.%ImportedAccess] { // CHECK:STDOUT: %a.patt: %pattern_type.bf2 = value_binding_pattern a [concrete] // CHECK:STDOUT: %a.param_patt: %pattern_type.bf2 = value_param_pattern %a.patt [concrete] // CHECK:STDOUT: %return.patt: %pattern_type.501 = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: %pattern_type.501 = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %i32: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %.loc6: Core.Form = init_form %i32 [concrete = constants.%.4ca] // CHECK:STDOUT: %a.param: %Adapter = value_param call_param0 // CHECK:STDOUT: %Adapter.ref: type = name_ref Adapter, imports.%Main.Adapter [concrete = constants.%Adapter] // CHECK:STDOUT: %a: %Adapter = value_binding a, %a.param // CHECK:STDOUT: %return.param: ref %i32 = out_param call_param1 // CHECK:STDOUT: %return: ref %i32 = return_slot %return.param // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @Adapter [from "extend_adapt_specific_type_library.carbon"] { // CHECK:STDOUT: complete_type_witness = imports.%Main.import_ref.709 // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = imports.%Main.import_ref.085 // CHECK:STDOUT: .x = // CHECK:STDOUT: extend imports.%Main.import_ref.9da228.2 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic class @C(imports.%Main.import_ref.b3b: type) [from "extend_adapt_specific_type_library.carbon"] { // CHECK:STDOUT: %T: type = symbolic_binding T, 0 [symbolic = %T (constants.%T)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete: = require_complete_type %T [symbolic = %require_complete (constants.%require_complete.944)] // CHECK:STDOUT: %C: type = class_type @C, @C(%T) [symbolic = %C (constants.%C.5a3)] // CHECK:STDOUT: %C.elem: type = unbound_element_type %C, %T [symbolic = %C.elem (constants.%C.elem.bd3)] // CHECK:STDOUT: %struct_type.x: type = struct_type {.x: @C.%T (%T)} [symbolic = %struct_type.x (constants.%struct_type.x.0c5)] // CHECK:STDOUT: %complete_type: = complete_type_witness %struct_type.x [symbolic = %complete_type (constants.%complete_type.735)] // CHECK:STDOUT: // CHECK:STDOUT: class { // CHECK:STDOUT: complete_type_witness = imports.%Main.import_ref.7e3 // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = imports.%Main.import_ref.820 // CHECK:STDOUT: .x = imports.%Main.import_ref.b94 // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @ImportedAccess(%a.param: %Adapter) -> out %return.param: %i32 { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %a.ref: %Adapter = name_ref a, %a // CHECK:STDOUT: %x.ref: %C.elem.fd3 = name_ref x, imports.%Main.import_ref.b94 [concrete = imports.%.68d] // CHECK:STDOUT: %.loc15_11.1: %C.829 = converted %a.ref, [concrete = ] // CHECK:STDOUT: %.loc15_11.2: %i32 = class_element_access , element0 [concrete = ] // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @C(constants.%T) { // CHECK:STDOUT: %T => constants.%T // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @C(constants.%i32) { // CHECK:STDOUT: %T => constants.%i32 // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete => constants.%complete_type.f8a // CHECK:STDOUT: %C => constants.%C.829 // CHECK:STDOUT: %C.elem => constants.%C.elem.fd3 // CHECK:STDOUT: %struct_type.x => constants.%struct_type.x.767 // CHECK:STDOUT: %complete_type => constants.%complete_type.c07 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- adapt_generic_type.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %type: type = facet_type [concrete] // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self] // CHECK:STDOUT: %pattern_type.98f: type = pattern_type type [concrete] // CHECK:STDOUT: %T.67d: type = symbolic_binding T, 0 [symbolic] // CHECK:STDOUT: %Adapter.type: type = generic_class_type @Adapter [concrete] // CHECK:STDOUT: %Adapter.generic: %Adapter.type = struct_value () [concrete] // CHECK:STDOUT: %Adapter.562: type = class_type @Adapter, @Adapter(%T.67d) [symbolic] // CHECK:STDOUT: %require_complete.944: = require_complete_type %T.67d [symbolic] // CHECK:STDOUT: %complete_type.1aa: = complete_type_witness %T.67d [symbolic] // CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [concrete] // CHECK:STDOUT: %Int.type: type = generic_class_type @Int [concrete] // CHECK:STDOUT: %Int.generic: %Int.type = struct_value () [concrete] // CHECK:STDOUT: %N: Core.IntLiteral = symbolic_binding N, 0 [symbolic] // CHECK:STDOUT: %i32: type = class_type @Int, @Int(%int_32) [concrete] // CHECK:STDOUT: %Adapter.b1e: type = class_type @Adapter, @Adapter(%i32) [concrete] // CHECK:STDOUT: %pattern_type.1fb: type = pattern_type %Adapter.b1e [concrete] // CHECK:STDOUT: %.ff5: Core.Form = init_form %i32 [concrete] // CHECK:STDOUT: %pattern_type.7ce: type = pattern_type %i32 [concrete] // CHECK:STDOUT: %Convert.type: type = fn_type @Convert [concrete] // CHECK:STDOUT: %Convert: %Convert.type = struct_value () [concrete] // CHECK:STDOUT: %i32.builtin: type = int_type signed, %int_32 [concrete] // CHECK:STDOUT: %complete_type.f8a: = complete_type_witness %i32.builtin [concrete] // CHECK:STDOUT: %complete_type.1eb: = complete_type_witness %i32 [concrete] // CHECK:STDOUT: %Copy.type: type = facet_type <@Copy> [concrete] // CHECK:STDOUT: %Int.as.Copy.impl.Op.type.824: type = fn_type @Int.as.Copy.impl.Op, @Int.as.Copy.impl(%N) [symbolic] // CHECK:STDOUT: %Int.as.Copy.impl.Op.9b9: %Int.as.Copy.impl.Op.type.824 = struct_value () [symbolic] // CHECK:STDOUT: %Copy.impl_witness.f17: = impl_witness imports.%Copy.impl_witness_table.e76, @Int.as.Copy.impl(%int_32) [concrete] // CHECK:STDOUT: %Int.as.Copy.impl.Op.type.546: type = fn_type @Int.as.Copy.impl.Op, @Int.as.Copy.impl(%int_32) [concrete] // CHECK:STDOUT: %Int.as.Copy.impl.Op.664: %Int.as.Copy.impl.Op.type.546 = struct_value () [concrete] // CHECK:STDOUT: %Copy.facet: %Copy.type = facet_value %i32, (%Copy.impl_witness.f17) [concrete] // CHECK:STDOUT: %Copy.WithSelf.Op.type.081: type = fn_type @Copy.WithSelf.Op, @Copy.WithSelf(%Copy.facet) [concrete] // CHECK:STDOUT: %.8e2: type = fn_type_with_self_type %Copy.WithSelf.Op.type.081, %Copy.facet [concrete] // CHECK:STDOUT: %Int.as.Copy.impl.Op.specific_fn: = specific_function %Int.as.Copy.impl.Op.664, @Int.as.Copy.impl.Op(%int_32) [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: .Int = %Core.Int // CHECK:STDOUT: .Copy = %Core.Copy // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: %Core.Int: %Int.type = import_ref Core//prelude/parts/int, Int, loaded [concrete = constants.%Int.generic] // CHECK:STDOUT: %Core.Copy: type = import_ref Core//prelude/parts/copy, Copy, loaded [concrete = constants.%Copy.type] // CHECK:STDOUT: %Core.import_ref.18d: @Int.as.Copy.impl.%Int.as.Copy.impl.Op.type (%Int.as.Copy.impl.Op.type.824) = import_ref Core//prelude/parts/int, loc{{\d+_\d+}}, loaded [symbolic = @Int.as.Copy.impl.%Int.as.Copy.impl.Op (constants.%Int.as.Copy.impl.Op.9b9)] // CHECK:STDOUT: %Copy.impl_witness_table.e76 = impl_witness_table (%Core.import_ref.18d), @Int.as.Copy.impl [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .Adapter = %Adapter.decl // CHECK:STDOUT: .Convert = %Convert.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %Adapter.decl: %Adapter.type = class_decl @Adapter [concrete = constants.%Adapter.generic] { // CHECK:STDOUT: %T.patt: %pattern_type.98f = symbolic_binding_pattern T, 0 [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %.loc4_19.1: type = splice_block %.loc4_19.2 [concrete = type] { // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %.loc4_19.2: type = type_literal type [concrete = type] // CHECK:STDOUT: } // CHECK:STDOUT: %T.loc4_15.2: type = symbolic_binding T, 0 [symbolic = %T.loc4_15.1 (constants.%T.67d)] // CHECK:STDOUT: } // CHECK:STDOUT: %Convert.decl: %Convert.type = fn_decl @Convert [concrete = constants.%Convert] { // CHECK:STDOUT: %a.patt: %pattern_type.1fb = value_binding_pattern a [concrete] // CHECK:STDOUT: %a.param_patt: %pattern_type.1fb = value_param_pattern %a.patt [concrete] // CHECK:STDOUT: %return.patt: %pattern_type.7ce = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: %pattern_type.7ce = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %i32.loc8_32: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %.loc8_32: Core.Form = init_form %i32.loc8_32 [concrete = constants.%.ff5] // CHECK:STDOUT: %a.param: %Adapter.b1e = value_param call_param0 // CHECK:STDOUT: %.loc8_26: type = splice_block %Adapter [concrete = constants.%Adapter.b1e] { // CHECK:STDOUT: %Adapter.ref: %Adapter.type = name_ref Adapter, file.%Adapter.decl [concrete = constants.%Adapter.generic] // CHECK:STDOUT: %i32.loc8_23: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %Adapter: type = class_type @Adapter, @Adapter(constants.%i32) [concrete = constants.%Adapter.b1e] // CHECK:STDOUT: } // CHECK:STDOUT: %a: %Adapter.b1e = value_binding a, %a.param // CHECK:STDOUT: %return.param: ref %i32 = out_param call_param1 // CHECK:STDOUT: %return: ref %i32 = return_slot %return.param // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic class @Adapter(%T.loc4_15.2: type) { // CHECK:STDOUT: %T.loc4_15.1: type = symbolic_binding T, 0 [symbolic = %T.loc4_15.1 (constants.%T.67d)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete: = require_complete_type %T.loc4_15.1 [symbolic = %require_complete (constants.%require_complete.944)] // CHECK:STDOUT: %complete_type.loc6_1.2: = complete_type_witness %T.loc4_15.1 [symbolic = %complete_type.loc6_1.2 (constants.%complete_type.1aa)] // CHECK:STDOUT: // CHECK:STDOUT: class { // CHECK:STDOUT: %T.ref: type = name_ref T, %T.loc4_15.2 [symbolic = %T.loc4_15.1 (constants.%T.67d)] // CHECK:STDOUT: adapt_decl %T.ref [concrete] // CHECK:STDOUT: %complete_type.loc6_1.1: = complete_type_witness constants.%T.67d [symbolic = %complete_type.loc6_1.2 (constants.%complete_type.1aa)] // CHECK:STDOUT: complete_type_witness = %complete_type.loc6_1.1 // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%Adapter.562 // CHECK:STDOUT: .T = // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @Convert(%a.param: %Adapter.b1e) -> out %return.param: %i32 { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %a.ref: %Adapter.b1e = name_ref a, %a // CHECK:STDOUT: %i32.loc9: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %.loc9_12.1: %i32 = as_compatible %a.ref // CHECK:STDOUT: %.loc9_12.2: %i32 = converted %a.ref, %.loc9_12.1 // CHECK:STDOUT: %impl.elem0: %.8e2 = impl_witness_access constants.%Copy.impl_witness.f17, element0 [concrete = constants.%Int.as.Copy.impl.Op.664] // CHECK:STDOUT: %bound_method.loc9_12.1: = bound_method %.loc9_12.2, %impl.elem0 // CHECK:STDOUT: %specific_fn: = specific_function %impl.elem0, @Int.as.Copy.impl.Op(constants.%int_32) [concrete = constants.%Int.as.Copy.impl.Op.specific_fn] // CHECK:STDOUT: %bound_method.loc9_12.2: = bound_method %.loc9_12.2, %specific_fn // CHECK:STDOUT: %Int.as.Copy.impl.Op.call: init %i32 = call %bound_method.loc9_12.2(%.loc9_12.2) // CHECK:STDOUT: return %Int.as.Copy.impl.Op.call // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @Adapter(constants.%T.67d) { // CHECK:STDOUT: %T.loc4_15.1 => constants.%T.67d // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @Adapter(constants.%i32) { // CHECK:STDOUT: %T.loc4_15.1 => constants.%i32 // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete => constants.%complete_type.f8a // CHECK:STDOUT: %complete_type.loc6_1.2 => constants.%complete_type.1eb // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- import_adapt_generic_type.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %Adapter.type: type = generic_class_type @Adapter [concrete] // CHECK:STDOUT: %Adapter.generic: %Adapter.type = struct_value () [concrete] // CHECK:STDOUT: %T.67d: type = symbolic_binding T, 0 [symbolic] // CHECK:STDOUT: %complete_type.1aa: = complete_type_witness %T.67d [symbolic] // CHECK:STDOUT: %require_complete.944: = require_complete_type %T.67d [symbolic] // CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [concrete] // CHECK:STDOUT: %Int.type: type = generic_class_type @Int [concrete] // CHECK:STDOUT: %Int.generic: %Int.type = struct_value () [concrete] // CHECK:STDOUT: %N: Core.IntLiteral = symbolic_binding N, 0 [symbolic] // CHECK:STDOUT: %i32: type = class_type @Int, @Int(%int_32) [concrete] // CHECK:STDOUT: %Adapter.b1e: type = class_type @Adapter, @Adapter(%i32) [concrete] // CHECK:STDOUT: %pattern_type.1fb: type = pattern_type %Adapter.b1e [concrete] // CHECK:STDOUT: %.ff5: Core.Form = init_form %i32 [concrete] // CHECK:STDOUT: %pattern_type.7ce: type = pattern_type %i32 [concrete] // CHECK:STDOUT: %ImportedConvert.type: type = fn_type @ImportedConvert [concrete] // CHECK:STDOUT: %ImportedConvert: %ImportedConvert.type = struct_value () [concrete] // CHECK:STDOUT: %i32.builtin: type = int_type signed, %int_32 [concrete] // CHECK:STDOUT: %complete_type.f8a: = complete_type_witness %i32.builtin [concrete] // CHECK:STDOUT: %complete_type.1eb: = complete_type_witness %i32 [concrete] // CHECK:STDOUT: %Copy.type: type = facet_type <@Copy> [concrete] // CHECK:STDOUT: %Int.as.Copy.impl.Op.type.824: type = fn_type @Int.as.Copy.impl.Op, @Int.as.Copy.impl(%N) [symbolic] // CHECK:STDOUT: %Int.as.Copy.impl.Op.9b9: %Int.as.Copy.impl.Op.type.824 = struct_value () [symbolic] // CHECK:STDOUT: %Copy.impl_witness.f17: = impl_witness imports.%Copy.impl_witness_table.e76, @Int.as.Copy.impl(%int_32) [concrete] // CHECK:STDOUT: %Int.as.Copy.impl.Op.type.546: type = fn_type @Int.as.Copy.impl.Op, @Int.as.Copy.impl(%int_32) [concrete] // CHECK:STDOUT: %Int.as.Copy.impl.Op.664: %Int.as.Copy.impl.Op.type.546 = struct_value () [concrete] // CHECK:STDOUT: %Copy.facet: %Copy.type = facet_value %i32, (%Copy.impl_witness.f17) [concrete] // CHECK:STDOUT: %Copy.WithSelf.Op.type.081: type = fn_type @Copy.WithSelf.Op, @Copy.WithSelf(%Copy.facet) [concrete] // CHECK:STDOUT: %.8e2: type = fn_type_with_self_type %Copy.WithSelf.Op.type.081, %Copy.facet [concrete] // CHECK:STDOUT: %Int.as.Copy.impl.Op.specific_fn: = specific_function %Int.as.Copy.impl.Op.664, @Int.as.Copy.impl.Op(%int_32) [concrete] // CHECK:STDOUT: %C: type = class_type @C [concrete] // CHECK:STDOUT: %C.elem: type = unbound_element_type %C, %i32 [concrete] // CHECK:STDOUT: %struct_type.n: type = struct_type {.n: %i32} [concrete] // CHECK:STDOUT: %complete_type.54b: = complete_type_witness %struct_type.n [concrete] // CHECK:STDOUT: %Adapter.8a3: type = class_type @Adapter, @Adapter(%C) [concrete] // CHECK:STDOUT: %pattern_type.014: type = pattern_type %Adapter.8a3 [concrete] // CHECK:STDOUT: %ImportedConvertLocal.type: type = fn_type @ImportedConvertLocal [concrete] // CHECK:STDOUT: %ImportedConvertLocal: %ImportedConvertLocal.type = struct_value () [concrete] // CHECK:STDOUT: %complete_type.53d: = complete_type_witness %C [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Main.Adapter: %Adapter.type = import_ref Main//adapt_generic_type, Adapter, loaded [concrete = constants.%Adapter.generic] // CHECK:STDOUT: %Main.Convert = import_ref Main//adapt_generic_type, Convert, unloaded // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: .Int = %Core.Int // CHECK:STDOUT: .Copy = %Core.Copy // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: %Main.import_ref.80d: = import_ref Main//adapt_generic_type, loc6_1, loaded [symbolic = @Adapter.%complete_type (constants.%complete_type.1aa)] // CHECK:STDOUT: %Main.import_ref.5a1 = import_ref Main//adapt_generic_type, inst{{[0-9A-F]+}} [no loc], unloaded // CHECK:STDOUT: %Main.import_ref.b3b: type = import_ref Main//adapt_generic_type, loc4_15, loaded [symbolic = @Adapter.%T (constants.%T.67d)] // CHECK:STDOUT: %Core.Int: %Int.type = import_ref Core//prelude/parts/int, Int, loaded [concrete = constants.%Int.generic] // CHECK:STDOUT: %Core.Copy: type = import_ref Core//prelude/parts/copy, Copy, loaded [concrete = constants.%Copy.type] // CHECK:STDOUT: %Core.import_ref.18d: @Int.as.Copy.impl.%Int.as.Copy.impl.Op.type (%Int.as.Copy.impl.Op.type.824) = import_ref Core//prelude/parts/int, loc{{\d+_\d+}}, loaded [symbolic = @Int.as.Copy.impl.%Int.as.Copy.impl.Op (constants.%Int.as.Copy.impl.Op.9b9)] // CHECK:STDOUT: %Copy.impl_witness_table.e76 = impl_witness_table (%Core.import_ref.18d), @Int.as.Copy.impl [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Adapter = imports.%Main.Adapter // CHECK:STDOUT: .Convert = imports.%Main.Convert // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .ImportedConvert = %ImportedConvert.decl // CHECK:STDOUT: .C = %C.decl // CHECK:STDOUT: .ImportedConvertLocal = %ImportedConvertLocal.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %default.import = import // CHECK:STDOUT: %ImportedConvert.decl: %ImportedConvert.type = fn_decl @ImportedConvert [concrete = constants.%ImportedConvert] { // CHECK:STDOUT: %a.patt: %pattern_type.1fb = value_binding_pattern a [concrete] // CHECK:STDOUT: %a.param_patt: %pattern_type.1fb = value_param_pattern %a.patt [concrete] // CHECK:STDOUT: %return.patt: %pattern_type.7ce = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: %pattern_type.7ce = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %i32.loc6_40: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %.loc6_40: Core.Form = init_form %i32.loc6_40 [concrete = constants.%.ff5] // CHECK:STDOUT: %a.param: %Adapter.b1e = value_param call_param0 // CHECK:STDOUT: %.loc6_34: type = splice_block %Adapter [concrete = constants.%Adapter.b1e] { // CHECK:STDOUT: %Adapter.ref: %Adapter.type = name_ref Adapter, imports.%Main.Adapter [concrete = constants.%Adapter.generic] // CHECK:STDOUT: %i32.loc6_31: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %Adapter: type = class_type @Adapter, @Adapter(constants.%i32) [concrete = constants.%Adapter.b1e] // CHECK:STDOUT: } // CHECK:STDOUT: %a: %Adapter.b1e = value_binding a, %a.param // CHECK:STDOUT: %return.param: ref %i32 = out_param call_param1 // CHECK:STDOUT: %return: ref %i32 = return_slot %return.param // CHECK:STDOUT: } // CHECK:STDOUT: %C.decl: type = class_decl @C [concrete = constants.%C] {} {} // CHECK:STDOUT: %ImportedConvertLocal.decl: %ImportedConvertLocal.type = fn_decl @ImportedConvertLocal [concrete = constants.%ImportedConvertLocal] { // CHECK:STDOUT: %a.patt: %pattern_type.014 = value_binding_pattern a [concrete] // CHECK:STDOUT: %a.param_patt: %pattern_type.014 = value_param_pattern %a.patt [concrete] // CHECK:STDOUT: %return.patt: %pattern_type.7ce = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: %pattern_type.7ce = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %i32: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %.loc14_43: Core.Form = init_form %i32 [concrete = constants.%.ff5] // CHECK:STDOUT: %a.param: %Adapter.8a3 = value_param call_param0 // CHECK:STDOUT: %.loc14_37: type = splice_block %Adapter [concrete = constants.%Adapter.8a3] { // CHECK:STDOUT: %Adapter.ref: %Adapter.type = name_ref Adapter, imports.%Main.Adapter [concrete = constants.%Adapter.generic] // CHECK:STDOUT: %C.ref.loc14: type = name_ref C, file.%C.decl [concrete = constants.%C] // CHECK:STDOUT: %Adapter: type = class_type @Adapter, @Adapter(constants.%C) [concrete = constants.%Adapter.8a3] // CHECK:STDOUT: } // CHECK:STDOUT: %a: %Adapter.8a3 = value_binding a, %a.param // CHECK:STDOUT: %return.param: ref %i32 = out_param call_param1 // CHECK:STDOUT: %return: ref %i32 = return_slot %return.param // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic class @Adapter(imports.%Main.import_ref.b3b: type) [from "adapt_generic_type.carbon"] { // CHECK:STDOUT: %T: type = symbolic_binding T, 0 [symbolic = %T (constants.%T.67d)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete: = require_complete_type %T [symbolic = %require_complete (constants.%require_complete.944)] // CHECK:STDOUT: %complete_type: = complete_type_witness %T [symbolic = %complete_type (constants.%complete_type.1aa)] // CHECK:STDOUT: // CHECK:STDOUT: class { // CHECK:STDOUT: complete_type_witness = imports.%Main.import_ref.80d // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = imports.%Main.import_ref.5a1 // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @C { // CHECK:STDOUT: %i32: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %.loc11: %C.elem = field_decl n, element0 [concrete] // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%struct_type.n [concrete = constants.%complete_type.54b] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%C // CHECK:STDOUT: .n = %.loc11 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @ImportedConvert(%a.param: %Adapter.b1e) -> out %return.param: %i32 { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %a.ref: %Adapter.b1e = name_ref a, %a // CHECK:STDOUT: %i32.loc7: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %.loc7_12.1: %i32 = as_compatible %a.ref // CHECK:STDOUT: %.loc7_12.2: %i32 = converted %a.ref, %.loc7_12.1 // CHECK:STDOUT: %impl.elem0: %.8e2 = impl_witness_access constants.%Copy.impl_witness.f17, element0 [concrete = constants.%Int.as.Copy.impl.Op.664] // CHECK:STDOUT: %bound_method.loc7_12.1: = bound_method %.loc7_12.2, %impl.elem0 // CHECK:STDOUT: %specific_fn: = specific_function %impl.elem0, @Int.as.Copy.impl.Op(constants.%int_32) [concrete = constants.%Int.as.Copy.impl.Op.specific_fn] // CHECK:STDOUT: %bound_method.loc7_12.2: = bound_method %.loc7_12.2, %specific_fn // CHECK:STDOUT: %Int.as.Copy.impl.Op.call: init %i32 = call %bound_method.loc7_12.2(%.loc7_12.2) // CHECK:STDOUT: return %Int.as.Copy.impl.Op.call // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @ImportedConvertLocal(%a.param: %Adapter.8a3) -> out %return.param: %i32 { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %a.ref: %Adapter.8a3 = name_ref a, %a // CHECK:STDOUT: %C.ref.loc15: type = name_ref C, file.%C.decl [concrete = constants.%C] // CHECK:STDOUT: %.loc15_13.1: %C = as_compatible %a.ref // CHECK:STDOUT: %.loc15_13.2: %C = converted %a.ref, %.loc15_13.1 // CHECK:STDOUT: %n.ref: %C.elem = name_ref n, @C.%.loc11 [concrete = @C.%.loc11] // CHECK:STDOUT: %.loc15_18.1: ref %i32 = class_element_access %.loc15_13.2, element0 // CHECK:STDOUT: %.loc15_18.2: %i32 = acquire_value %.loc15_18.1 // CHECK:STDOUT: %impl.elem0: %.8e2 = impl_witness_access constants.%Copy.impl_witness.f17, element0 [concrete = constants.%Int.as.Copy.impl.Op.664] // CHECK:STDOUT: %bound_method.loc15_18.1: = bound_method %.loc15_18.2, %impl.elem0 // CHECK:STDOUT: %specific_fn: = specific_function %impl.elem0, @Int.as.Copy.impl.Op(constants.%int_32) [concrete = constants.%Int.as.Copy.impl.Op.specific_fn] // CHECK:STDOUT: %bound_method.loc15_18.2: = bound_method %.loc15_18.2, %specific_fn // CHECK:STDOUT: %Int.as.Copy.impl.Op.call: init %i32 = call %bound_method.loc15_18.2(%.loc15_18.2) // CHECK:STDOUT: return %Int.as.Copy.impl.Op.call // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @Adapter(constants.%T.67d) { // CHECK:STDOUT: %T => constants.%T.67d // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @Adapter(constants.%i32) { // CHECK:STDOUT: %T => constants.%i32 // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete => constants.%complete_type.f8a // CHECK:STDOUT: %complete_type => constants.%complete_type.1eb // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @Adapter(constants.%C) { // CHECK:STDOUT: %T => constants.%C // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete => constants.%complete_type.54b // CHECK:STDOUT: %complete_type => constants.%complete_type.53d // CHECK:STDOUT: } // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/class/generic/base_is_generic.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/int.carbon // TODO: Add ranges and switch to "--dump-sem-ir-ranges=only". // EXTRA-ARGS: --dump-sem-ir-ranges=if-present // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/class/generic/base_is_generic.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/class/generic/base_is_generic.carbon // --- extend_generic_base.carbon library "[[@TEST_NAME]]"; base class Base(T:! type) { var x: T; } class Param { var y: i32; } class Derived { extend base: Base(Param); } fn DoubleFieldAccess(d: Derived) -> i32 { return d.x.y; } // --- import.carbon library "[[@TEST_NAME]]"; import library "extend_generic_base"; fn ImportedDoubleFieldAccess(d: Derived) -> i32 { return d.x.y; } // --- fail_todo_extend_symbolic_base.carbon library "[[@TEST_NAME]]"; class C(T:! type) { // CHECK:STDERR: fail_todo_extend_symbolic_base.carbon:[[@LINE+4]]:16: error: deriving from final type `T`; base type must be an `abstract` or `base` class [BaseIsFinal] // CHECK:STDERR: extend base: T; // CHECK:STDERR: ^ // CHECK:STDERR: extend base: T; } base class X { fn G() {} } fn F() { C(X).G(); } // --- extend_generic_symbolic_base.carbon library "[[@TEST_NAME]]"; base class X(U:! type) { fn G() -> U { return G(); } } class C(T:! type) { extend base: X(T); } fn F() { let unused i: i32 = C(i32).G(); } // --- import_extend_generic_symbolic_base.carbon library "[[@TEST_NAME]]"; import library "extend_generic_symbolic_base"; fn H() { let unused j: i32 = C(i32).G(); } // CHECK:STDOUT: --- extend_generic_base.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %type: type = facet_type [concrete] // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self] // CHECK:STDOUT: %pattern_type.98f: type = pattern_type type [concrete] // CHECK:STDOUT: %T.67d: type = symbolic_binding T, 0 [symbolic] // CHECK:STDOUT: %Base.type: type = generic_class_type @Base [concrete] // CHECK:STDOUT: %Base.generic: %Base.type = struct_value () [concrete] // CHECK:STDOUT: %Base.d0c: type = class_type @Base, @Base(%T.67d) [symbolic] // CHECK:STDOUT: %require_complete.944: = require_complete_type %T.67d [symbolic] // CHECK:STDOUT: %Base.elem.8ab: type = unbound_element_type %Base.d0c, %T.67d [symbolic] // CHECK:STDOUT: %struct_type.x.0c5: type = struct_type {.x: %T.67d} [symbolic] // CHECK:STDOUT: %complete_type.735: = complete_type_witness %struct_type.x.0c5 [symbolic] // CHECK:STDOUT: %Param: type = class_type @Param [concrete] // CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [concrete] // CHECK:STDOUT: %Int.type: type = generic_class_type @Int [concrete] // CHECK:STDOUT: %Int.generic: %Int.type = struct_value () [concrete] // CHECK:STDOUT: %N: Core.IntLiteral = symbolic_binding N, 0 [symbolic] // CHECK:STDOUT: %i32: type = class_type @Int, @Int(%int_32) [concrete] // CHECK:STDOUT: %Param.elem: type = unbound_element_type %Param, %i32 [concrete] // CHECK:STDOUT: %struct_type.y: type = struct_type {.y: %i32} [concrete] // CHECK:STDOUT: %complete_type.0f9: = complete_type_witness %struct_type.y [concrete] // CHECK:STDOUT: %Derived: type = class_type @Derived [concrete] // CHECK:STDOUT: %Base.f8f: type = class_type @Base, @Base(%Param) [concrete] // CHECK:STDOUT: %Base.elem.6d8: type = unbound_element_type %Base.f8f, %Param [concrete] // CHECK:STDOUT: %struct_type.x.d96: type = struct_type {.x: %Param} [concrete] // CHECK:STDOUT: %complete_type.95c: = complete_type_witness %struct_type.x.d96 [concrete] // CHECK:STDOUT: %Derived.elem: type = unbound_element_type %Derived, %Base.f8f [concrete] // CHECK:STDOUT: %struct_type.base.9a9: type = struct_type {.base: %Base.f8f} [concrete] // CHECK:STDOUT: %complete_type.8de: = complete_type_witness %struct_type.base.9a9 [concrete] // CHECK:STDOUT: %pattern_type.9f6: type = pattern_type %Derived [concrete] // CHECK:STDOUT: %.ff5: Core.Form = init_form %i32 [concrete] // CHECK:STDOUT: %pattern_type.7ce: type = pattern_type %i32 [concrete] // CHECK:STDOUT: %DoubleFieldAccess.type: type = fn_type @DoubleFieldAccess [concrete] // CHECK:STDOUT: %DoubleFieldAccess: %DoubleFieldAccess.type = struct_value () [concrete] // CHECK:STDOUT: %Copy.type: type = facet_type <@Copy> [concrete] // CHECK:STDOUT: %Int.as.Copy.impl.Op.type.824: type = fn_type @Int.as.Copy.impl.Op, @Int.as.Copy.impl(%N) [symbolic] // CHECK:STDOUT: %Int.as.Copy.impl.Op.9b9: %Int.as.Copy.impl.Op.type.824 = struct_value () [symbolic] // CHECK:STDOUT: %Copy.impl_witness.f17: = impl_witness imports.%Copy.impl_witness_table.e76, @Int.as.Copy.impl(%int_32) [concrete] // CHECK:STDOUT: %Int.as.Copy.impl.Op.type.546: type = fn_type @Int.as.Copy.impl.Op, @Int.as.Copy.impl(%int_32) [concrete] // CHECK:STDOUT: %Int.as.Copy.impl.Op.664: %Int.as.Copy.impl.Op.type.546 = struct_value () [concrete] // CHECK:STDOUT: %Copy.facet: %Copy.type = facet_value %i32, (%Copy.impl_witness.f17) [concrete] // CHECK:STDOUT: %Copy.WithSelf.Op.type.081: type = fn_type @Copy.WithSelf.Op, @Copy.WithSelf(%Copy.facet) [concrete] // CHECK:STDOUT: %.8e2: type = fn_type_with_self_type %Copy.WithSelf.Op.type.081, %Copy.facet [concrete] // CHECK:STDOUT: %Int.as.Copy.impl.Op.specific_fn: = specific_function %Int.as.Copy.impl.Op.664, @Int.as.Copy.impl.Op(%int_32) [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: .Int = %Core.Int // CHECK:STDOUT: .Copy = %Core.Copy // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: %Core.Int: %Int.type = import_ref Core//prelude/parts/int, Int, loaded [concrete = constants.%Int.generic] // CHECK:STDOUT: %Core.Copy: type = import_ref Core//prelude/parts/copy, Copy, loaded [concrete = constants.%Copy.type] // CHECK:STDOUT: %Core.import_ref.18d: @Int.as.Copy.impl.%Int.as.Copy.impl.Op.type (%Int.as.Copy.impl.Op.type.824) = import_ref Core//prelude/parts/int, loc{{\d+_\d+}}, loaded [symbolic = @Int.as.Copy.impl.%Int.as.Copy.impl.Op (constants.%Int.as.Copy.impl.Op.9b9)] // CHECK:STDOUT: %Copy.impl_witness_table.e76 = impl_witness_table (%Core.import_ref.18d), @Int.as.Copy.impl [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .Base = %Base.decl // CHECK:STDOUT: .Param = %Param.decl // CHECK:STDOUT: .Derived = %Derived.decl // CHECK:STDOUT: .DoubleFieldAccess = %DoubleFieldAccess.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %Base.decl: %Base.type = class_decl @Base [concrete = constants.%Base.generic] { // CHECK:STDOUT: %T.patt: %pattern_type.98f = symbolic_binding_pattern T, 0 [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %.loc4_21.1: type = splice_block %.loc4_21.2 [concrete = type] { // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %.loc4_21.2: type = type_literal type [concrete = type] // CHECK:STDOUT: } // CHECK:STDOUT: %T.loc4_17.2: type = symbolic_binding T, 0 [symbolic = %T.loc4_17.1 (constants.%T.67d)] // CHECK:STDOUT: } // CHECK:STDOUT: %Param.decl: type = class_decl @Param [concrete = constants.%Param] {} {} // CHECK:STDOUT: %Derived.decl: type = class_decl @Derived [concrete = constants.%Derived] {} {} // CHECK:STDOUT: %DoubleFieldAccess.decl: %DoubleFieldAccess.type = fn_decl @DoubleFieldAccess [concrete = constants.%DoubleFieldAccess] { // CHECK:STDOUT: %d.patt: %pattern_type.9f6 = value_binding_pattern d [concrete] // CHECK:STDOUT: %d.param_patt: %pattern_type.9f6 = value_param_pattern %d.patt [concrete] // CHECK:STDOUT: %return.patt: %pattern_type.7ce = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: %pattern_type.7ce = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %i32: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %.loc16: Core.Form = init_form %i32 [concrete = constants.%.ff5] // CHECK:STDOUT: %d.param: %Derived = value_param call_param0 // CHECK:STDOUT: %Derived.ref: type = name_ref Derived, file.%Derived.decl [concrete = constants.%Derived] // CHECK:STDOUT: %d: %Derived = value_binding d, %d.param // CHECK:STDOUT: %return.param: ref %i32 = out_param call_param1 // CHECK:STDOUT: %return: ref %i32 = return_slot %return.param // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic class @Base(%T.loc4_17.2: type) { // CHECK:STDOUT: %T.loc4_17.1: type = symbolic_binding T, 0 [symbolic = %T.loc4_17.1 (constants.%T.67d)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete: = require_complete_type %T.loc4_17.1 [symbolic = %require_complete (constants.%require_complete.944)] // CHECK:STDOUT: %Base: type = class_type @Base, @Base(%T.loc4_17.1) [symbolic = %Base (constants.%Base.d0c)] // CHECK:STDOUT: %Base.elem: type = unbound_element_type %Base, %T.loc4_17.1 [symbolic = %Base.elem (constants.%Base.elem.8ab)] // CHECK:STDOUT: %struct_type.x: type = struct_type {.x: @Base.%T.loc4_17.1 (%T.67d)} [symbolic = %struct_type.x (constants.%struct_type.x.0c5)] // CHECK:STDOUT: %complete_type.loc6_1.2: = complete_type_witness %struct_type.x [symbolic = %complete_type.loc6_1.2 (constants.%complete_type.735)] // CHECK:STDOUT: // CHECK:STDOUT: class { // CHECK:STDOUT: %T.ref: type = name_ref T, %T.loc4_17.2 [symbolic = %T.loc4_17.1 (constants.%T.67d)] // CHECK:STDOUT: %.loc5: @Base.%Base.elem (%Base.elem.8ab) = field_decl x, element0 [concrete] // CHECK:STDOUT: %complete_type.loc6_1.1: = complete_type_witness constants.%struct_type.x.0c5 [symbolic = %complete_type.loc6_1.2 (constants.%complete_type.735)] // CHECK:STDOUT: complete_type_witness = %complete_type.loc6_1.1 // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%Base.d0c // CHECK:STDOUT: .T = // CHECK:STDOUT: .x = %.loc5 // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @Param { // CHECK:STDOUT: %i32: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %.loc9: %Param.elem = field_decl y, element0 [concrete] // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%struct_type.y [concrete = constants.%complete_type.0f9] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%Param // CHECK:STDOUT: .y = %.loc9 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @Derived { // CHECK:STDOUT: %Base.ref: %Base.type = name_ref Base, file.%Base.decl [concrete = constants.%Base.generic] // CHECK:STDOUT: %Param.ref: type = name_ref Param, file.%Param.decl [concrete = constants.%Param] // CHECK:STDOUT: %Base: type = class_type @Base, @Base(constants.%Param) [concrete = constants.%Base.f8f] // CHECK:STDOUT: %.loc13: %Derived.elem = base_decl %Base, element0 [concrete] // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%struct_type.base.9a9 [concrete = constants.%complete_type.8de] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%Derived // CHECK:STDOUT: .Base = // CHECK:STDOUT: .Param = // CHECK:STDOUT: .base = %.loc13 // CHECK:STDOUT: .x = // CHECK:STDOUT: extend %Base // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @DoubleFieldAccess(%d.param: %Derived) -> out %return.param: %i32 { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %d.ref: %Derived = name_ref d, %d // CHECK:STDOUT: %x.ref: %Base.elem.6d8 = name_ref x, @Base.%.loc5 [concrete = @Base.%.loc5] // CHECK:STDOUT: %.loc17_11.1: ref %Base.f8f = class_element_access %d.ref, element0 // CHECK:STDOUT: %.loc17_11.2: ref %Base.f8f = converted %d.ref, %.loc17_11.1 // CHECK:STDOUT: %.loc17_11.3: ref %Param = class_element_access %.loc17_11.2, element0 // CHECK:STDOUT: %y.ref: %Param.elem = name_ref y, @Param.%.loc9 [concrete = @Param.%.loc9] // CHECK:STDOUT: %.loc17_13.1: ref %i32 = class_element_access %.loc17_11.3, element0 // CHECK:STDOUT: %.loc17_13.2: %i32 = acquire_value %.loc17_13.1 // CHECK:STDOUT: %impl.elem0: %.8e2 = impl_witness_access constants.%Copy.impl_witness.f17, element0 [concrete = constants.%Int.as.Copy.impl.Op.664] // CHECK:STDOUT: %bound_method.loc17_13.1: = bound_method %.loc17_13.2, %impl.elem0 // CHECK:STDOUT: %specific_fn: = specific_function %impl.elem0, @Int.as.Copy.impl.Op(constants.%int_32) [concrete = constants.%Int.as.Copy.impl.Op.specific_fn] // CHECK:STDOUT: %bound_method.loc17_13.2: = bound_method %.loc17_13.2, %specific_fn // CHECK:STDOUT: %Int.as.Copy.impl.Op.call: init %i32 = call %bound_method.loc17_13.2(%.loc17_13.2) // CHECK:STDOUT: return %Int.as.Copy.impl.Op.call // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @Base(constants.%T.67d) { // CHECK:STDOUT: %T.loc4_17.1 => constants.%T.67d // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @Base(constants.%Param) { // CHECK:STDOUT: %T.loc4_17.1 => constants.%Param // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete => constants.%complete_type.0f9 // CHECK:STDOUT: %Base => constants.%Base.f8f // CHECK:STDOUT: %Base.elem => constants.%Base.elem.6d8 // CHECK:STDOUT: %struct_type.x => constants.%struct_type.x.d96 // CHECK:STDOUT: %complete_type.loc6_1.2 => constants.%complete_type.95c // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- import.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %Derived: type = class_type @Derived [concrete] // CHECK:STDOUT: %Param: type = class_type @Param [concrete] // CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [concrete] // CHECK:STDOUT: %Int.type: type = generic_class_type @Int [concrete] // CHECK:STDOUT: %Int.generic: %Int.type = struct_value () [concrete] // CHECK:STDOUT: %N: Core.IntLiteral = symbolic_binding N, 0 [symbolic] // CHECK:STDOUT: %i32: type = class_type @Int, @Int(%int_32) [concrete] // CHECK:STDOUT: %struct_type.y: type = struct_type {.y: %i32} [concrete] // CHECK:STDOUT: %complete_type.09d: = complete_type_witness %struct_type.y [concrete] // CHECK:STDOUT: %T.67d: type = symbolic_binding T, 0 [symbolic] // CHECK:STDOUT: %struct_type.x.0c5: type = struct_type {.x: %T.67d} [symbolic] // CHECK:STDOUT: %complete_type.735: = complete_type_witness %struct_type.x.0c5 [symbolic] // CHECK:STDOUT: %Base.d0c: type = class_type @Base, @Base(%T.67d) [symbolic] // CHECK:STDOUT: %Base.elem.8ab: type = unbound_element_type %Base.d0c, %T.67d [symbolic] // CHECK:STDOUT: %require_complete.944: = require_complete_type %T.67d [symbolic] // CHECK:STDOUT: %Base.f8f: type = class_type @Base, @Base(%Param) [concrete] // CHECK:STDOUT: %struct_type.x.d96: type = struct_type {.x: %Param} [concrete] // CHECK:STDOUT: %complete_type.95c: = complete_type_witness %struct_type.x.d96 [concrete] // CHECK:STDOUT: %Base.elem.6d8: type = unbound_element_type %Base.f8f, %Param [concrete] // CHECK:STDOUT: %struct_type.base.9a9: type = struct_type {.base: %Base.f8f} [concrete] // CHECK:STDOUT: %complete_type.8de: = complete_type_witness %struct_type.base.9a9 [concrete] // CHECK:STDOUT: %pattern_type.9f6: type = pattern_type %Derived [concrete] // CHECK:STDOUT: %.4ca: Core.Form = init_form %i32 [concrete] // CHECK:STDOUT: %pattern_type.501: type = pattern_type %i32 [concrete] // CHECK:STDOUT: %ImportedDoubleFieldAccess.type: type = fn_type @ImportedDoubleFieldAccess [concrete] // CHECK:STDOUT: %ImportedDoubleFieldAccess: %ImportedDoubleFieldAccess.type = struct_value () [concrete] // CHECK:STDOUT: %Param.elem: type = unbound_element_type %Param, %i32 [concrete] // CHECK:STDOUT: %Copy.type: type = facet_type <@Copy> [concrete] // CHECK:STDOUT: %Int.as.Copy.impl.Op.type.08e: type = fn_type @Int.as.Copy.impl.Op, @Int.as.Copy.impl(%N) [symbolic] // CHECK:STDOUT: %Int.as.Copy.impl.Op.014: %Int.as.Copy.impl.Op.type.08e = struct_value () [symbolic] // CHECK:STDOUT: %Copy.impl_witness.a2f: = impl_witness imports.%Copy.impl_witness_table.d6d, @Int.as.Copy.impl(%int_32) [concrete] // CHECK:STDOUT: %Int.as.Copy.impl.Op.type.837: type = fn_type @Int.as.Copy.impl.Op, @Int.as.Copy.impl(%int_32) [concrete] // CHECK:STDOUT: %Int.as.Copy.impl.Op.2b1: %Int.as.Copy.impl.Op.type.837 = struct_value () [concrete] // CHECK:STDOUT: %Copy.facet: %Copy.type = facet_value %i32, (%Copy.impl_witness.a2f) [concrete] // CHECK:STDOUT: %Copy.WithSelf.Op.type.67d: type = fn_type @Copy.WithSelf.Op, @Copy.WithSelf(%Copy.facet) [concrete] // CHECK:STDOUT: %.d31: type = fn_type_with_self_type %Copy.WithSelf.Op.type.67d, %Copy.facet [concrete] // CHECK:STDOUT: %Int.as.Copy.impl.Op.specific_fn: = specific_function %Int.as.Copy.impl.Op.2b1, @Int.as.Copy.impl.Op(%int_32) [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Main.Base = import_ref Main//extend_generic_base, Base, unloaded // CHECK:STDOUT: %Main.Param = import_ref Main//extend_generic_base, Param, unloaded // CHECK:STDOUT: %Main.Derived: type = import_ref Main//extend_generic_base, Derived, loaded [concrete = constants.%Derived] // CHECK:STDOUT: %Main.DoubleFieldAccess = import_ref Main//extend_generic_base, DoubleFieldAccess, unloaded // CHECK:STDOUT: %Core.ece: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: .Int = %Core.Int // CHECK:STDOUT: .Copy = %Core.Copy // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: %Main.import_ref.e8d: = import_ref Main//extend_generic_base, loc10_1, loaded [concrete = constants.%complete_type.09d] // CHECK:STDOUT: %Main.import_ref.304 = import_ref Main//extend_generic_base, inst{{[0-9A-F]+}} [no loc], unloaded // CHECK:STDOUT: %Main.import_ref.0c2: %Param.elem = import_ref Main//extend_generic_base, loc9_8, loaded [concrete = %.e89] // CHECK:STDOUT: %Main.import_ref.7e3: = import_ref Main//extend_generic_base, loc6_1, loaded [symbolic = @Base.%complete_type (constants.%complete_type.735)] // CHECK:STDOUT: %Main.import_ref.2af = import_ref Main//extend_generic_base, inst{{[0-9A-F]+}} [no loc], unloaded // CHECK:STDOUT: %Main.import_ref.568: @Base.%Base.elem (%Base.elem.8ab) = import_ref Main//extend_generic_base, loc5_8, loaded [concrete = %.bef] // CHECK:STDOUT: %Main.import_ref.b3b: type = import_ref Main//extend_generic_base, loc4_17, loaded [symbolic = @Base.%T (constants.%T.67d)] // CHECK:STDOUT: %Main.import_ref.56c: = import_ref Main//extend_generic_base, loc14_1, loaded [concrete = constants.%complete_type.8de] // CHECK:STDOUT: %Main.import_ref.33c = import_ref Main//extend_generic_base, inst{{[0-9A-F]+}} [no loc], unloaded // CHECK:STDOUT: %Main.import_ref.4b4 = import_ref Main//extend_generic_base, loc13_27, unloaded // CHECK:STDOUT: %Main.import_ref.8875f0.2: type = import_ref Main//extend_generic_base, loc13_26, loaded [concrete = constants.%Base.f8f] // CHECK:STDOUT: %Core.Int: %Int.type = import_ref Core//prelude/parts/int, Int, loaded [concrete = constants.%Int.generic] // CHECK:STDOUT: %.bef: @Base.%Base.elem (%Base.elem.8ab) = field_decl x, element0 [concrete] // CHECK:STDOUT: %.e89: %Param.elem = field_decl y, element0 [concrete] // CHECK:STDOUT: %Core.Copy: type = import_ref Core//prelude/parts/copy, Copy, loaded [concrete = constants.%Copy.type] // CHECK:STDOUT: %Core.import_ref.ea6: @Int.as.Copy.impl.%Int.as.Copy.impl.Op.type (%Int.as.Copy.impl.Op.type.08e) = import_ref Core//prelude/parts/int, loc{{\d+_\d+}}, loaded [symbolic = @Int.as.Copy.impl.%Int.as.Copy.impl.Op (constants.%Int.as.Copy.impl.Op.014)] // CHECK:STDOUT: %Copy.impl_witness_table.d6d = impl_witness_table (%Core.import_ref.ea6), @Int.as.Copy.impl [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Base = imports.%Main.Base // CHECK:STDOUT: .Param = imports.%Main.Param // CHECK:STDOUT: .Derived = imports.%Main.Derived // CHECK:STDOUT: .DoubleFieldAccess = imports.%Main.DoubleFieldAccess // CHECK:STDOUT: .Core = imports.%Core.ece // CHECK:STDOUT: .ImportedDoubleFieldAccess = %ImportedDoubleFieldAccess.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %default.import = import // CHECK:STDOUT: %ImportedDoubleFieldAccess.decl: %ImportedDoubleFieldAccess.type = fn_decl @ImportedDoubleFieldAccess [concrete = constants.%ImportedDoubleFieldAccess] { // CHECK:STDOUT: %d.patt: %pattern_type.9f6 = value_binding_pattern d [concrete] // CHECK:STDOUT: %d.param_patt: %pattern_type.9f6 = value_param_pattern %d.patt [concrete] // CHECK:STDOUT: %return.patt: %pattern_type.501 = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: %pattern_type.501 = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %i32: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %.loc6: Core.Form = init_form %i32 [concrete = constants.%.4ca] // CHECK:STDOUT: %d.param: %Derived = value_param call_param0 // CHECK:STDOUT: %Derived.ref: type = name_ref Derived, imports.%Main.Derived [concrete = constants.%Derived] // CHECK:STDOUT: %d: %Derived = value_binding d, %d.param // CHECK:STDOUT: %return.param: ref %i32 = out_param call_param1 // CHECK:STDOUT: %return: ref %i32 = return_slot %return.param // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @Derived [from "extend_generic_base.carbon"] { // CHECK:STDOUT: complete_type_witness = imports.%Main.import_ref.56c // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = imports.%Main.import_ref.33c // CHECK:STDOUT: .base = imports.%Main.import_ref.4b4 // CHECK:STDOUT: .x = // CHECK:STDOUT: extend imports.%Main.import_ref.8875f0.2 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @Param [from "extend_generic_base.carbon"] { // CHECK:STDOUT: complete_type_witness = imports.%Main.import_ref.e8d // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = imports.%Main.import_ref.304 // CHECK:STDOUT: .y = imports.%Main.import_ref.0c2 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic class @Base(imports.%Main.import_ref.b3b: type) [from "extend_generic_base.carbon"] { // CHECK:STDOUT: %T: type = symbolic_binding T, 0 [symbolic = %T (constants.%T.67d)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete: = require_complete_type %T [symbolic = %require_complete (constants.%require_complete.944)] // CHECK:STDOUT: %Base: type = class_type @Base, @Base(%T) [symbolic = %Base (constants.%Base.d0c)] // CHECK:STDOUT: %Base.elem: type = unbound_element_type %Base, %T [symbolic = %Base.elem (constants.%Base.elem.8ab)] // CHECK:STDOUT: %struct_type.x: type = struct_type {.x: @Base.%T (%T.67d)} [symbolic = %struct_type.x (constants.%struct_type.x.0c5)] // CHECK:STDOUT: %complete_type: = complete_type_witness %struct_type.x [symbolic = %complete_type (constants.%complete_type.735)] // CHECK:STDOUT: // CHECK:STDOUT: class { // CHECK:STDOUT: complete_type_witness = imports.%Main.import_ref.7e3 // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = imports.%Main.import_ref.2af // CHECK:STDOUT: .x = imports.%Main.import_ref.568 // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @ImportedDoubleFieldAccess(%d.param: %Derived) -> out %return.param: %i32 { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %d.ref: %Derived = name_ref d, %d // CHECK:STDOUT: %x.ref: %Base.elem.6d8 = name_ref x, imports.%Main.import_ref.568 [concrete = imports.%.bef] // CHECK:STDOUT: %.loc7_11.1: ref %Base.f8f = class_element_access %d.ref, element0 // CHECK:STDOUT: %.loc7_11.2: ref %Base.f8f = converted %d.ref, %.loc7_11.1 // CHECK:STDOUT: %.loc7_11.3: ref %Param = class_element_access %.loc7_11.2, element0 // CHECK:STDOUT: %y.ref: %Param.elem = name_ref y, imports.%Main.import_ref.0c2 [concrete = imports.%.e89] // CHECK:STDOUT: %.loc7_13.1: ref %i32 = class_element_access %.loc7_11.3, element0 // CHECK:STDOUT: %.loc7_13.2: %i32 = acquire_value %.loc7_13.1 // CHECK:STDOUT: %impl.elem0: %.d31 = impl_witness_access constants.%Copy.impl_witness.a2f, element0 [concrete = constants.%Int.as.Copy.impl.Op.2b1] // CHECK:STDOUT: %bound_method.loc7_13.1: = bound_method %.loc7_13.2, %impl.elem0 // CHECK:STDOUT: %specific_fn: = specific_function %impl.elem0, @Int.as.Copy.impl.Op(constants.%int_32) [concrete = constants.%Int.as.Copy.impl.Op.specific_fn] // CHECK:STDOUT: %bound_method.loc7_13.2: = bound_method %.loc7_13.2, %specific_fn // CHECK:STDOUT: %Int.as.Copy.impl.Op.call: init %i32 = call %bound_method.loc7_13.2(%.loc7_13.2) // CHECK:STDOUT: return %Int.as.Copy.impl.Op.call // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @Base(constants.%T.67d) { // CHECK:STDOUT: %T => constants.%T.67d // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @Base(constants.%Param) { // CHECK:STDOUT: %T => constants.%Param // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete => constants.%complete_type.09d // CHECK:STDOUT: %Base => constants.%Base.f8f // CHECK:STDOUT: %Base.elem => constants.%Base.elem.6d8 // CHECK:STDOUT: %struct_type.x => constants.%struct_type.x.d96 // CHECK:STDOUT: %complete_type => constants.%complete_type.95c // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- fail_todo_extend_symbolic_base.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %type: type = facet_type [concrete] // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self] // CHECK:STDOUT: %pattern_type: type = pattern_type type [concrete] // CHECK:STDOUT: %T: type = symbolic_binding T, 0 [symbolic] // CHECK:STDOUT: %C.type: type = generic_class_type @C [concrete] // CHECK:STDOUT: %C.generic: %C.type = struct_value () [concrete] // CHECK:STDOUT: %C.5a3: type = class_type @C, @C(%T) [symbolic] // CHECK:STDOUT: %require_complete: = require_complete_type %T [symbolic] // CHECK:STDOUT: %X: type = class_type @X [concrete] // CHECK:STDOUT: %X.G.type: type = fn_type @X.G [concrete] // CHECK:STDOUT: %X.G: %X.G.type = struct_value () [concrete] // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %complete_type: = complete_type_witness %empty_struct_type [concrete] // CHECK:STDOUT: %F.type: type = fn_type @F [concrete] // CHECK:STDOUT: %F: %F.type = struct_value () [concrete] // CHECK:STDOUT: %C.513: type = class_type @C, @C(%X) [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .C = %C.decl // CHECK:STDOUT: .X = %X.decl // CHECK:STDOUT: .F = %F.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %C.decl: %C.type = class_decl @C [concrete = constants.%C.generic] { // CHECK:STDOUT: %T.patt: %pattern_type = symbolic_binding_pattern T, 0 [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %.loc4_13.1: type = splice_block %.loc4_13.2 [concrete = type] { // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %.loc4_13.2: type = type_literal type [concrete = type] // CHECK:STDOUT: } // CHECK:STDOUT: %T.loc4_9.2: type = symbolic_binding T, 0 [symbolic = %T.loc4_9.1 (constants.%T)] // CHECK:STDOUT: } // CHECK:STDOUT: %X.decl: type = class_decl @X [concrete = constants.%X] {} {} // CHECK:STDOUT: %F.decl: %F.type = fn_decl @F [concrete = constants.%F] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic class @C(%T.loc4_9.2: type) { // CHECK:STDOUT: %T.loc4_9.1: type = symbolic_binding T, 0 [symbolic = %T.loc4_9.1 (constants.%T)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete: = require_complete_type %T.loc4_9.1 [symbolic = %require_complete (constants.%require_complete)] // CHECK:STDOUT: // CHECK:STDOUT: class { // CHECK:STDOUT: %T.ref: type = name_ref T, %T.loc4_9.2 [symbolic = %T.loc4_9.1 (constants.%T)] // CHECK:STDOUT: %.loc9: = base_decl , element0 [concrete] // CHECK:STDOUT: %complete_type: = complete_type_witness [concrete = ] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%C.5a3 // CHECK:STDOUT: .T = // CHECK:STDOUT: .base = %.loc9 // CHECK:STDOUT: .G = // CHECK:STDOUT: has_error // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @X { // CHECK:STDOUT: %X.G.decl: %X.G.type = fn_decl @X.G [concrete = constants.%X.G] {} {} // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%X // CHECK:STDOUT: .G = %X.G.decl // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @X.G() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @F() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %C.ref: %C.type = name_ref C, file.%C.decl [concrete = constants.%C.generic] // CHECK:STDOUT: %X.ref: type = name_ref X, file.%X.decl [concrete = constants.%X] // CHECK:STDOUT: %C: type = class_type @C, @C(constants.%X) [concrete = constants.%C.513] // CHECK:STDOUT: %G.ref: = name_ref G, [concrete = ] // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @C(constants.%T) { // CHECK:STDOUT: %T.loc4_9.1 => constants.%T // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @C(constants.%X) { // CHECK:STDOUT: %T.loc4_9.1 => constants.%X // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete => constants.%complete_type // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- extend_generic_symbolic_base.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %type: type = facet_type [concrete] // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self] // CHECK:STDOUT: %pattern_type.98f: type = pattern_type type [concrete] // CHECK:STDOUT: %U: type = symbolic_binding U, 0 [symbolic] // CHECK:STDOUT: %X.type: type = generic_class_type @X [concrete] // CHECK:STDOUT: %X.generic: %X.type = struct_value () [concrete] // CHECK:STDOUT: %X.03b463.1: type = class_type @X, @X(%U) [symbolic] // CHECK:STDOUT: %.184: Core.Form = init_form %U [symbolic] // CHECK:STDOUT: %pattern_type.51d: type = pattern_type %U [symbolic] // CHECK:STDOUT: %X.G.type.20eb90.1: type = fn_type @X.G, @X(%U) [symbolic] // CHECK:STDOUT: %X.G.f9f685.1: %X.G.type.20eb90.1 = struct_value () [symbolic] // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %complete_type.357: = complete_type_witness %empty_struct_type [concrete] // CHECK:STDOUT: %require_complete.944: = require_complete_type %U [symbolic] // CHECK:STDOUT: %X.G.specific_fn.974: = specific_function %X.G.f9f685.1, @X.G(%U) [symbolic] // CHECK:STDOUT: %T: type = symbolic_binding T, 0 [symbolic] // CHECK:STDOUT: %C.type: type = generic_class_type @C [concrete] // CHECK:STDOUT: %C.generic: %C.type = struct_value () [concrete] // CHECK:STDOUT: %C.5a3: type = class_type @C, @C(%T) [symbolic] // CHECK:STDOUT: %X.03b463.2: type = class_type @X, @X(%T) [symbolic] // CHECK:STDOUT: %X.G.type.20eb90.2: type = fn_type @X.G, @X(%T) [symbolic] // CHECK:STDOUT: %X.G.f9f685.2: %X.G.type.20eb90.2 = struct_value () [symbolic] // CHECK:STDOUT: %require_complete.5dd: = require_complete_type %X.03b463.2 [symbolic] // CHECK:STDOUT: %C.elem.161: type = unbound_element_type %C.5a3, %X.03b463.2 [symbolic] // CHECK:STDOUT: %struct_type.base.a6f: type = struct_type {.base: %X.03b463.2} [symbolic] // CHECK:STDOUT: %complete_type.bec: = complete_type_witness %struct_type.base.a6f [symbolic] // CHECK:STDOUT: %F.type: type = fn_type @F [concrete] // CHECK:STDOUT: %F: %F.type = struct_value () [concrete] // CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [concrete] // CHECK:STDOUT: %Int.type: type = generic_class_type @Int [concrete] // CHECK:STDOUT: %Int.generic: %Int.type = struct_value () [concrete] // CHECK:STDOUT: %i32: type = class_type @Int, @Int(%int_32) [concrete] // CHECK:STDOUT: %i32.builtin: type = int_type signed, %int_32 [concrete] // CHECK:STDOUT: %complete_type.f8a: = complete_type_witness %i32.builtin [concrete] // CHECK:STDOUT: %pattern_type.7ce: type = pattern_type %i32 [concrete] // CHECK:STDOUT: %C.b13: type = class_type @C, @C(%i32) [concrete] // CHECK:STDOUT: %X.1bc: type = class_type @X, @X(%i32) [concrete] // CHECK:STDOUT: %X.G.type.f75: type = fn_type @X.G, @X(%i32) [concrete] // CHECK:STDOUT: %X.G.e2a: %X.G.type.f75 = struct_value () [concrete] // CHECK:STDOUT: %C.elem.7f5: type = unbound_element_type %C.b13, %X.1bc [concrete] // CHECK:STDOUT: %struct_type.base.b52: type = struct_type {.base: %X.1bc} [concrete] // CHECK:STDOUT: %complete_type.ab4: = complete_type_witness %struct_type.base.b52 [concrete] // CHECK:STDOUT: %.ff5: Core.Form = init_form %i32 [concrete] // CHECK:STDOUT: %X.G.specific_fn.54d: = specific_function %X.G.e2a, @X.G(%i32) [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: .Int = %Core.Int // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: %Core.Int: %Int.type = import_ref Core//prelude/parts/int, Int, loaded [concrete = constants.%Int.generic] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .X = %X.decl // CHECK:STDOUT: .C = %C.decl // CHECK:STDOUT: .F = %F.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %X.decl: %X.type = class_decl @X [concrete = constants.%X.generic] { // CHECK:STDOUT: %U.patt: %pattern_type.98f = symbolic_binding_pattern U, 0 [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %.loc4_18.1: type = splice_block %.loc4_18.2 [concrete = type] { // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %.loc4_18.2: type = type_literal type [concrete = type] // CHECK:STDOUT: } // CHECK:STDOUT: %U.loc4_14.2: type = symbolic_binding U, 0 [symbolic = %U.loc4_14.1 (constants.%U)] // CHECK:STDOUT: } // CHECK:STDOUT: %C.decl: %C.type = class_decl @C [concrete = constants.%C.generic] { // CHECK:STDOUT: %T.patt: %pattern_type.98f = symbolic_binding_pattern T, 0 [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %.loc8_13.1: type = splice_block %.loc8_13.2 [concrete = type] { // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %.loc8_13.2: type = type_literal type [concrete = type] // CHECK:STDOUT: } // CHECK:STDOUT: %T.loc8_9.2: type = symbolic_binding T, 0 [symbolic = %T.loc8_9.1 (constants.%T)] // CHECK:STDOUT: } // CHECK:STDOUT: %F.decl: %F.type = fn_decl @F [concrete = constants.%F] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic class @X(%U.loc4_14.2: type) { // CHECK:STDOUT: %U.loc4_14.1: type = symbolic_binding U, 0 [symbolic = %U.loc4_14.1 (constants.%U)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %X.G.type: type = fn_type @X.G, @X(%U.loc4_14.1) [symbolic = %X.G.type (constants.%X.G.type.20eb90.1)] // CHECK:STDOUT: %X.G: @X.%X.G.type (%X.G.type.20eb90.1) = struct_value () [symbolic = %X.G (constants.%X.G.f9f685.1)] // CHECK:STDOUT: // CHECK:STDOUT: class { // CHECK:STDOUT: %X.G.decl: @X.%X.G.type (%X.G.type.20eb90.1) = fn_decl @X.G [symbolic = @X.%X.G (constants.%X.G.f9f685.1)] { // CHECK:STDOUT: %return.patt: @X.G.%pattern_type (%pattern_type.51d) = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: @X.G.%pattern_type (%pattern_type.51d) = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %U.ref: type = name_ref U, @X.%U.loc4_14.2 [symbolic = %U (constants.%U)] // CHECK:STDOUT: %.loc5_13.3: Core.Form = init_form %U.ref [symbolic = %.loc5_13.2 (constants.%.184)] // CHECK:STDOUT: %return.param: ref @X.G.%U (%U) = out_param call_param0 // CHECK:STDOUT: %return: ref @X.G.%U (%U) = return_slot %return.param // CHECK:STDOUT: } // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type.357] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%X.03b463.1 // CHECK:STDOUT: .U = // CHECK:STDOUT: .G = %X.G.decl // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic class @C(%T.loc8_9.2: type) { // CHECK:STDOUT: %T.loc8_9.1: type = symbolic_binding T, 0 [symbolic = %T.loc8_9.1 (constants.%T)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %X.loc9_19.2: type = class_type @X, @X(%T.loc8_9.1) [symbolic = %X.loc9_19.2 (constants.%X.03b463.2)] // CHECK:STDOUT: %require_complete: = require_complete_type %X.loc9_19.2 [symbolic = %require_complete (constants.%require_complete.5dd)] // CHECK:STDOUT: %C: type = class_type @C, @C(%T.loc8_9.1) [symbolic = %C (constants.%C.5a3)] // CHECK:STDOUT: %C.elem: type = unbound_element_type %C, %X.loc9_19.2 [symbolic = %C.elem (constants.%C.elem.161)] // CHECK:STDOUT: %struct_type.base: type = struct_type {.base: @C.%X.loc9_19.2 (%X.03b463.2)} [symbolic = %struct_type.base (constants.%struct_type.base.a6f)] // CHECK:STDOUT: %complete_type.loc10_1.2: = complete_type_witness %struct_type.base [symbolic = %complete_type.loc10_1.2 (constants.%complete_type.bec)] // CHECK:STDOUT: // CHECK:STDOUT: class { // CHECK:STDOUT: %X.ref: %X.type = name_ref X, file.%X.decl [concrete = constants.%X.generic] // CHECK:STDOUT: %T.ref: type = name_ref T, %T.loc8_9.2 [symbolic = %T.loc8_9.1 (constants.%T)] // CHECK:STDOUT: %X.loc9_19.1: type = class_type @X, @X(constants.%T) [symbolic = %X.loc9_19.2 (constants.%X.03b463.2)] // CHECK:STDOUT: %.loc9: @C.%C.elem (%C.elem.161) = base_decl %X.loc9_19.1, element0 [concrete] // CHECK:STDOUT: %complete_type.loc10_1.1: = complete_type_witness constants.%struct_type.base.a6f [symbolic = %complete_type.loc10_1.2 (constants.%complete_type.bec)] // CHECK:STDOUT: complete_type_witness = %complete_type.loc10_1.1 // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%C.5a3 // CHECK:STDOUT: .X = // CHECK:STDOUT: .T = // CHECK:STDOUT: .base = %.loc9 // CHECK:STDOUT: .G = // CHECK:STDOUT: extend %X.loc9_19.1 // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic fn @X.G(@X.%U.loc4_14.2: type) { // CHECK:STDOUT: %U: type = symbolic_binding U, 0 [symbolic = %U (constants.%U)] // CHECK:STDOUT: %.loc5_13.2: Core.Form = init_form %U [symbolic = %.loc5_13.2 (constants.%.184)] // CHECK:STDOUT: %pattern_type: type = pattern_type %U [symbolic = %pattern_type (constants.%pattern_type.51d)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete: = require_complete_type %U [symbolic = %require_complete (constants.%require_complete.944)] // CHECK:STDOUT: %X.G.type: type = fn_type @X.G, @X(%U) [symbolic = %X.G.type (constants.%X.G.type.20eb90.1)] // CHECK:STDOUT: %X.G: @X.G.%X.G.type (%X.G.type.20eb90.1) = struct_value () [symbolic = %X.G (constants.%X.G.f9f685.1)] // CHECK:STDOUT: %X.G.specific_fn.loc5_24.2: = specific_function %X.G, @X.G(%U) [symbolic = %X.G.specific_fn.loc5_24.2 (constants.%X.G.specific_fn.974)] // CHECK:STDOUT: // CHECK:STDOUT: fn() -> out %return.param: @X.G.%U (%U) { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %.loc5_24: @X.G.%X.G.type (%X.G.type.20eb90.1) = specific_constant @X.%X.G.decl, @X(constants.%U) [symbolic = %X.G (constants.%X.G.f9f685.1)] // CHECK:STDOUT: %G.ref: @X.G.%X.G.type (%X.G.type.20eb90.1) = name_ref G, %.loc5_24 [symbolic = %X.G (constants.%X.G.f9f685.1)] // CHECK:STDOUT: %X.G.specific_fn.loc5_24.1: = specific_function %G.ref, @X.G(constants.%U) [symbolic = %X.G.specific_fn.loc5_24.2 (constants.%X.G.specific_fn.974)] // CHECK:STDOUT: %.loc5_13.1: ref @X.G.%U (%U) = splice_block %return.param {} // CHECK:STDOUT: %X.G.call: init @X.G.%U (%U) to %.loc5_13.1 = call %X.G.specific_fn.loc5_24.1() // CHECK:STDOUT: return %X.G.call to %return.param // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @F() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %i.patt: %pattern_type.7ce = value_binding_pattern i [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %C.ref: %C.type = name_ref C, file.%C.decl [concrete = constants.%C.generic] // CHECK:STDOUT: %i32.loc13_25: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %C: type = class_type @C, @C(constants.%i32) [concrete = constants.%C.b13] // CHECK:STDOUT: %.loc13_29: %X.G.type.f75 = specific_constant @X.%X.G.decl, @X(constants.%i32) [concrete = constants.%X.G.e2a] // CHECK:STDOUT: %G.ref: %X.G.type.f75 = name_ref G, %.loc13_29 [concrete = constants.%X.G.e2a] // CHECK:STDOUT: %X.G.specific_fn: = specific_function %G.ref, @X.G(constants.%i32) [concrete = constants.%X.G.specific_fn.54d] // CHECK:STDOUT: %X.G.call: init %i32 = call %X.G.specific_fn() // CHECK:STDOUT: %i32.loc13_17: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %.loc13_32.1: %i32 = value_of_initializer %X.G.call // CHECK:STDOUT: %.loc13_32.2: %i32 = converted %X.G.call, %.loc13_32.1 // CHECK:STDOUT: %i: %i32 = value_binding i, %.loc13_32.2 // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @X(constants.%U) { // CHECK:STDOUT: %U.loc4_14.1 => constants.%U // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %X.G.type => constants.%X.G.type.20eb90.1 // CHECK:STDOUT: %X.G => constants.%X.G.f9f685.1 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @X.G(constants.%U) { // CHECK:STDOUT: %U => constants.%U // CHECK:STDOUT: %.loc5_13.2 => constants.%.184 // CHECK:STDOUT: %pattern_type => constants.%pattern_type.51d // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete => constants.%require_complete.944 // CHECK:STDOUT: %X.G.type => constants.%X.G.type.20eb90.1 // CHECK:STDOUT: %X.G => constants.%X.G.f9f685.1 // CHECK:STDOUT: %X.G.specific_fn.loc5_24.2 => constants.%X.G.specific_fn.974 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @C(constants.%T) { // CHECK:STDOUT: %T.loc8_9.1 => constants.%T // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @X(constants.%T) { // CHECK:STDOUT: %U.loc4_14.1 => constants.%T // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %X.G.type => constants.%X.G.type.20eb90.2 // CHECK:STDOUT: %X.G => constants.%X.G.f9f685.2 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @C(constants.%i32) { // CHECK:STDOUT: %T.loc8_9.1 => constants.%i32 // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %X.loc9_19.2 => constants.%X.1bc // CHECK:STDOUT: %require_complete => constants.%complete_type.357 // CHECK:STDOUT: %C => constants.%C.b13 // CHECK:STDOUT: %C.elem => constants.%C.elem.7f5 // CHECK:STDOUT: %struct_type.base => constants.%struct_type.base.b52 // CHECK:STDOUT: %complete_type.loc10_1.2 => constants.%complete_type.ab4 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @X(constants.%i32) { // CHECK:STDOUT: %U.loc4_14.1 => constants.%i32 // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %X.G.type => constants.%X.G.type.f75 // CHECK:STDOUT: %X.G => constants.%X.G.e2a // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @X.G(constants.%i32) { // CHECK:STDOUT: %U => constants.%i32 // CHECK:STDOUT: %.loc5_13.2 => constants.%.ff5 // CHECK:STDOUT: %pattern_type => constants.%pattern_type.7ce // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete => constants.%complete_type.f8a // CHECK:STDOUT: %X.G.type => constants.%X.G.type.f75 // CHECK:STDOUT: %X.G => constants.%X.G.e2a // CHECK:STDOUT: %X.G.specific_fn.loc5_24.2 => constants.%X.G.specific_fn.54d // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- import_extend_generic_symbolic_base.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %H.type: type = fn_type @H [concrete] // CHECK:STDOUT: %H: %H.type = struct_value () [concrete] // CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [concrete] // CHECK:STDOUT: %Int.type: type = generic_class_type @Int [concrete] // CHECK:STDOUT: %Int.generic: %Int.type = struct_value () [concrete] // CHECK:STDOUT: %i32: type = class_type @Int, @Int(%int_32) [concrete] // CHECK:STDOUT: %i32.builtin: type = int_type signed, %int_32 [concrete] // CHECK:STDOUT: %complete_type.f8a: = complete_type_witness %i32.builtin [concrete] // CHECK:STDOUT: %pattern_type.7ce: type = pattern_type %i32 [concrete] // CHECK:STDOUT: %C.type: type = generic_class_type @C [concrete] // CHECK:STDOUT: %C.generic: %C.type = struct_value () [concrete] // CHECK:STDOUT: %T: type = symbolic_binding T, 0 [symbolic] // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %complete_type.357: = complete_type_witness %empty_struct_type [concrete] // CHECK:STDOUT: %U: type = symbolic_binding U, 0 [symbolic] // CHECK:STDOUT: %X.G.type.20eb90.1: type = fn_type @X.G, @X(%U) [symbolic] // CHECK:STDOUT: %X.G.f9f685.1: %X.G.type.20eb90.1 = struct_value () [symbolic] // CHECK:STDOUT: %pattern_type.51d: type = pattern_type %U [symbolic] // CHECK:STDOUT: %.184: Core.Form = init_form %U [symbolic] // CHECK:STDOUT: %X.G.specific_fn.974: = specific_function %X.G.f9f685.1, @X.G(%U) [symbolic] // CHECK:STDOUT: %require_complete.944: = require_complete_type %U [symbolic] // CHECK:STDOUT: %X.03b463.2: type = class_type @X, @X(%T) [symbolic] // CHECK:STDOUT: %X.G.type.20eb90.2: type = fn_type @X.G, @X(%T) [symbolic] // CHECK:STDOUT: %X.G.f9f685.2: %X.G.type.20eb90.2 = struct_value () [symbolic] // CHECK:STDOUT: %C.5a3: type = class_type @C, @C(%T) [symbolic] // CHECK:STDOUT: %C.elem.161: type = unbound_element_type %C.5a3, %X.03b463.2 [symbolic] // CHECK:STDOUT: %struct_type.base.a6f: type = struct_type {.base: %X.03b463.2} [symbolic] // CHECK:STDOUT: %complete_type.bec: = complete_type_witness %struct_type.base.a6f [symbolic] // CHECK:STDOUT: %require_complete.5dd: = require_complete_type %X.03b463.2 [symbolic] // CHECK:STDOUT: %C.b13: type = class_type @C, @C(%i32) [concrete] // CHECK:STDOUT: %X.1bc: type = class_type @X, @X(%i32) [concrete] // CHECK:STDOUT: %X.G.type.f75: type = fn_type @X.G, @X(%i32) [concrete] // CHECK:STDOUT: %X.G.e2a: %X.G.type.f75 = struct_value () [concrete] // CHECK:STDOUT: %C.elem.7f5: type = unbound_element_type %C.b13, %X.1bc [concrete] // CHECK:STDOUT: %struct_type.base.b52: type = struct_type {.base: %X.1bc} [concrete] // CHECK:STDOUT: %complete_type.ab4: = complete_type_witness %struct_type.base.b52 [concrete] // CHECK:STDOUT: %.ff5: Core.Form = init_form %i32 [concrete] // CHECK:STDOUT: %X.G.specific_fn.54d: = specific_function %X.G.e2a, @X.G(%i32) [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Main.X = import_ref Main//extend_generic_symbolic_base, X, unloaded // CHECK:STDOUT: %Main.C: %C.type = import_ref Main//extend_generic_symbolic_base, C, loaded [concrete = constants.%C.generic] // CHECK:STDOUT: %Main.F = import_ref Main//extend_generic_symbolic_base, F, unloaded // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: .Int = %Core.Int // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: %Core.Int: %Int.type = import_ref Core//prelude/parts/int, Int, loaded [concrete = constants.%Int.generic] // CHECK:STDOUT: %Main.import_ref.b3bc94.1: type = import_ref Main//extend_generic_symbolic_base, loc4_14, loaded [symbolic = @X.%U (constants.%U)] // CHECK:STDOUT: %Main.import_ref.8f2: = import_ref Main//extend_generic_symbolic_base, loc6_1, loaded [concrete = constants.%complete_type.357] // CHECK:STDOUT: %Main.import_ref.5f5 = import_ref Main//extend_generic_symbolic_base, inst{{[0-9A-F]+}} [no loc], unloaded // CHECK:STDOUT: %Main.import_ref.940: @X.%X.G.type (%X.G.type.20eb90.1) = import_ref Main//extend_generic_symbolic_base, loc5_15, loaded [symbolic = @X.%X.G (constants.%X.G.f9f685.1)] // CHECK:STDOUT: %Main.import_ref.b3bc94.2: type = import_ref Main//extend_generic_symbolic_base, loc4_14, loaded [symbolic = @X.%U (constants.%U)] // CHECK:STDOUT: %Main.import_ref.d67: = import_ref Main//extend_generic_symbolic_base, loc10_1, loaded [symbolic = @C.%complete_type (constants.%complete_type.bec)] // CHECK:STDOUT: %Main.import_ref.820 = import_ref Main//extend_generic_symbolic_base, inst{{[0-9A-F]+}} [no loc], unloaded // CHECK:STDOUT: %Main.import_ref.5a3 = import_ref Main//extend_generic_symbolic_base, loc9_20, unloaded // CHECK:STDOUT: %Main.import_ref.7f62b4.2: type = import_ref Main//extend_generic_symbolic_base, loc9_19, loaded [symbolic = @C.%X (constants.%X.03b463.2)] // CHECK:STDOUT: %Main.import_ref.b3bc94.3: type = import_ref Main//extend_generic_symbolic_base, loc8_9, loaded [symbolic = @C.%T (constants.%T)] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .X = imports.%Main.X // CHECK:STDOUT: .C = imports.%Main.C // CHECK:STDOUT: .F = imports.%Main.F // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .H = %H.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %default.import = import // CHECK:STDOUT: %H.decl: %H.type = fn_decl @H [concrete = constants.%H] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic class @C(imports.%Main.import_ref.b3bc94.3: type) [from "extend_generic_symbolic_base.carbon"] { // CHECK:STDOUT: %T: type = symbolic_binding T, 0 [symbolic = %T (constants.%T)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %X: type = class_type @X, @X(%T) [symbolic = %X (constants.%X.03b463.2)] // CHECK:STDOUT: %require_complete: = require_complete_type %X [symbolic = %require_complete (constants.%require_complete.5dd)] // CHECK:STDOUT: %C: type = class_type @C, @C(%T) [symbolic = %C (constants.%C.5a3)] // CHECK:STDOUT: %C.elem: type = unbound_element_type %C, %X [symbolic = %C.elem (constants.%C.elem.161)] // CHECK:STDOUT: %struct_type.base: type = struct_type {.base: @C.%X (%X.03b463.2)} [symbolic = %struct_type.base (constants.%struct_type.base.a6f)] // CHECK:STDOUT: %complete_type: = complete_type_witness %struct_type.base [symbolic = %complete_type (constants.%complete_type.bec)] // CHECK:STDOUT: // CHECK:STDOUT: class { // CHECK:STDOUT: complete_type_witness = imports.%Main.import_ref.d67 // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = imports.%Main.import_ref.820 // CHECK:STDOUT: .base = imports.%Main.import_ref.5a3 // CHECK:STDOUT: .G = // CHECK:STDOUT: extend imports.%Main.import_ref.7f62b4.2 // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic class @X(imports.%Main.import_ref.b3bc94.2: type) [from "extend_generic_symbolic_base.carbon"] { // CHECK:STDOUT: %U: type = symbolic_binding U, 0 [symbolic = %U (constants.%U)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %X.G.type: type = fn_type @X.G, @X(%U) [symbolic = %X.G.type (constants.%X.G.type.20eb90.1)] // CHECK:STDOUT: %X.G: @X.%X.G.type (%X.G.type.20eb90.1) = struct_value () [symbolic = %X.G (constants.%X.G.f9f685.1)] // CHECK:STDOUT: // CHECK:STDOUT: class { // CHECK:STDOUT: complete_type_witness = imports.%Main.import_ref.8f2 // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = imports.%Main.import_ref.5f5 // CHECK:STDOUT: .G = imports.%Main.import_ref.940 // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @H() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %j.patt: %pattern_type.7ce = value_binding_pattern j [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %C.ref: %C.type = name_ref C, imports.%Main.C [concrete = constants.%C.generic] // CHECK:STDOUT: %i32.loc7_25: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %C: type = class_type @C, @C(constants.%i32) [concrete = constants.%C.b13] // CHECK:STDOUT: %.loc7_29: %X.G.type.f75 = specific_constant imports.%Main.import_ref.940, @X(constants.%i32) [concrete = constants.%X.G.e2a] // CHECK:STDOUT: %G.ref: %X.G.type.f75 = name_ref G, %.loc7_29 [concrete = constants.%X.G.e2a] // CHECK:STDOUT: %X.G.specific_fn: = specific_function %G.ref, @X.G(constants.%i32) [concrete = constants.%X.G.specific_fn.54d] // CHECK:STDOUT: %X.G.call: init %i32 = call %X.G.specific_fn() // CHECK:STDOUT: %i32.loc7_17: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %.loc7_32.1: %i32 = value_of_initializer %X.G.call // CHECK:STDOUT: %.loc7_32.2: %i32 = converted %X.G.call, %.loc7_32.1 // CHECK:STDOUT: %j: %i32 = value_binding j, %.loc7_32.2 // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic fn @X.G(imports.%Main.import_ref.b3bc94.1: type) [from "extend_generic_symbolic_base.carbon"] { // CHECK:STDOUT: %U: type = symbolic_binding U, 0 [symbolic = %U (constants.%U)] // CHECK:STDOUT: %.1: Core.Form = init_form %U [symbolic = %.1 (constants.%.184)] // CHECK:STDOUT: %pattern_type: type = pattern_type %U [symbolic = %pattern_type (constants.%pattern_type.51d)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete: = require_complete_type %U [symbolic = %require_complete (constants.%require_complete.944)] // CHECK:STDOUT: %X.G.type: type = fn_type @X.G, @X(%U) [symbolic = %X.G.type (constants.%X.G.type.20eb90.1)] // CHECK:STDOUT: %X.G: @X.G.%X.G.type (%X.G.type.20eb90.1) = struct_value () [symbolic = %X.G (constants.%X.G.f9f685.1)] // CHECK:STDOUT: %X.G.specific_fn: = specific_function %X.G, @X.G(%U) [symbolic = %X.G.specific_fn (constants.%X.G.specific_fn.974)] // CHECK:STDOUT: // CHECK:STDOUT: fn; // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @X(constants.%U) { // CHECK:STDOUT: %U => constants.%U // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %X.G.type => constants.%X.G.type.20eb90.1 // CHECK:STDOUT: %X.G => constants.%X.G.f9f685.1 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @X.G(constants.%U) { // CHECK:STDOUT: %U => constants.%U // CHECK:STDOUT: %.1 => constants.%.184 // CHECK:STDOUT: %pattern_type => constants.%pattern_type.51d // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete => constants.%require_complete.944 // CHECK:STDOUT: %X.G.type => constants.%X.G.type.20eb90.1 // CHECK:STDOUT: %X.G => constants.%X.G.f9f685.1 // CHECK:STDOUT: %X.G.specific_fn => constants.%X.G.specific_fn.974 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @X(constants.%T) { // CHECK:STDOUT: %U => constants.%T // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %X.G.type => constants.%X.G.type.20eb90.2 // CHECK:STDOUT: %X.G => constants.%X.G.f9f685.2 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @C(constants.%T) { // CHECK:STDOUT: %T => constants.%T // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @C(constants.%i32) { // CHECK:STDOUT: %T => constants.%i32 // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %X => constants.%X.1bc // CHECK:STDOUT: %require_complete => constants.%complete_type.357 // CHECK:STDOUT: %C => constants.%C.b13 // CHECK:STDOUT: %C.elem => constants.%C.elem.7f5 // CHECK:STDOUT: %struct_type.base => constants.%struct_type.base.b52 // CHECK:STDOUT: %complete_type => constants.%complete_type.ab4 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @X(constants.%i32) { // CHECK:STDOUT: %U => constants.%i32 // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %X.G.type => constants.%X.G.type.f75 // CHECK:STDOUT: %X.G => constants.%X.G.e2a // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @X.G(constants.%i32) { // CHECK:STDOUT: %U => constants.%i32 // CHECK:STDOUT: %.1 => constants.%.ff5 // CHECK:STDOUT: %pattern_type => constants.%pattern_type.7ce // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete => constants.%complete_type.f8a // CHECK:STDOUT: %X.G.type => constants.%X.G.type.f75 // CHECK:STDOUT: %X.G => constants.%X.G.e2a // CHECK:STDOUT: %X.G.specific_fn => constants.%X.G.specific_fn.54d // CHECK:STDOUT: } // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/class/generic/basic.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/convert.carbon // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/class/generic/basic.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/class/generic/basic.carbon // --- basic.carbon library "[[@TEST_NAME]]"; //@dump-sem-ir-begin class Class(T:! Core.Copy) { fn GetAddr[ref self: Self]() -> T* { return &self.k; } fn GetValue[self: Self]() -> T { return self.k; } var k: T; } class Declaration(T:! type); //@dump-sem-ir-end // CHECK:STDOUT: --- basic.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %Copy.type: type = facet_type <@Copy> [concrete] // CHECK:STDOUT: %pattern_type.ce2: type = pattern_type %Copy.type [concrete] // CHECK:STDOUT: %T.035: %Copy.type = symbolic_binding T, 0 [symbolic] // CHECK:STDOUT: %Class.type: type = generic_class_type @Class [concrete] // CHECK:STDOUT: %Class.generic: %Class.type = struct_value () [concrete] // CHECK:STDOUT: %Class: type = class_type @Class, @Class(%T.035) [symbolic] // CHECK:STDOUT: %pattern_type.893: type = pattern_type %Class [symbolic] // CHECK:STDOUT: %T.binding.as_type: type = symbolic_binding_type T, 0, %T.035 [symbolic] // CHECK:STDOUT: %ptr.e7d: type = ptr_type %T.binding.as_type [symbolic] // CHECK:STDOUT: %.66f: Core.Form = init_form %ptr.e7d [symbolic] // CHECK:STDOUT: %pattern_type.65a: type = pattern_type %ptr.e7d [symbolic] // CHECK:STDOUT: %Class.GetAddr.type: type = fn_type @Class.GetAddr, @Class(%T.035) [symbolic] // CHECK:STDOUT: %Class.GetAddr: %Class.GetAddr.type = struct_value () [symbolic] // CHECK:STDOUT: %.076a48.2: Core.Form = init_form %T.binding.as_type [symbolic] // CHECK:STDOUT: %pattern_type.9b9f0c.2: type = pattern_type %T.binding.as_type [symbolic] // CHECK:STDOUT: %Class.GetValue.type: type = fn_type @Class.GetValue, @Class(%T.035) [symbolic] // CHECK:STDOUT: %Class.GetValue: %Class.GetValue.type = struct_value () [symbolic] // CHECK:STDOUT: %require_complete.67c: = require_complete_type %T.binding.as_type [symbolic] // CHECK:STDOUT: %Class.elem: type = unbound_element_type %Class, %T.binding.as_type [symbolic] // CHECK:STDOUT: %struct_type.k: type = struct_type {.k: %T.binding.as_type} [symbolic] // CHECK:STDOUT: %complete_type: = complete_type_witness %struct_type.k [symbolic] // CHECK:STDOUT: %require_complete.904: = require_complete_type %Class [symbolic] // CHECK:STDOUT: %require_complete.9dc: = require_complete_type %ptr.e7d [symbolic] // CHECK:STDOUT: %Copy.lookup_impl_witness.58d: = lookup_impl_witness %T.035, @Copy [symbolic] // CHECK:STDOUT: %Copy.WithSelf.Op.type.735e75.2: type = fn_type @Copy.WithSelf.Op, @Copy.WithSelf(%T.035) [symbolic] // CHECK:STDOUT: %.023: type = fn_type_with_self_type %Copy.WithSelf.Op.type.735e75.2, %T.035 [symbolic] // CHECK:STDOUT: %impl.elem0.594: %.023 = impl_witness_access %Copy.lookup_impl_witness.58d, element0 [symbolic] // CHECK:STDOUT: %specific_impl_fn.bdc: = specific_impl_function %impl.elem0.594, @Copy.WithSelf.Op(%T.035) [symbolic] // CHECK:STDOUT: %T.67d: type = symbolic_binding T, 0 [symbolic] // CHECK:STDOUT: %pattern_type.98f: type = pattern_type type [concrete] // CHECK:STDOUT: %.3a3: require_specific_def_type = require_specific_def @ptr.as.Copy.impl(%T.binding.as_type) [symbolic] // CHECK:STDOUT: %Copy.lookup_impl_witness.2e7: = lookup_impl_witness %ptr.e7d, @Copy [symbolic] // CHECK:STDOUT: %Copy.facet: %Copy.type = facet_value %ptr.e7d, (%Copy.lookup_impl_witness.2e7) [symbolic] // CHECK:STDOUT: %Copy.WithSelf.Op.type.486: type = fn_type @Copy.WithSelf.Op, @Copy.WithSelf(%Copy.facet) [symbolic] // CHECK:STDOUT: %.b63: type = fn_type_with_self_type %Copy.WithSelf.Op.type.486, %Copy.facet [symbolic] // CHECK:STDOUT: %impl.elem0.387: %.b63 = impl_witness_access %Copy.lookup_impl_witness.2e7, element0 [symbolic] // CHECK:STDOUT: %specific_impl_fn.7f8: = specific_impl_function %impl.elem0.387, @Copy.WithSelf.Op(%Copy.facet) [symbolic] // CHECK:STDOUT: %Declaration.type: type = generic_class_type @Declaration [concrete] // CHECK:STDOUT: %Declaration.generic: %Declaration.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: .Copy = %Core.Copy // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: %Core.Copy: type = import_ref Core//prelude/parts/copy, Copy, loaded [concrete = constants.%Copy.type] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: %Class.decl: %Class.type = class_decl @Class [concrete = constants.%Class.generic] { // CHECK:STDOUT: %T.patt: %pattern_type.ce2 = symbolic_binding_pattern T, 0 [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %.loc5: type = splice_block %Copy.ref [concrete = constants.%Copy.type] { // CHECK:STDOUT: // CHECK:STDOUT: %Core.ref: = name_ref Core, imports.%Core [concrete = imports.%Core] // CHECK:STDOUT: %Copy.ref: type = name_ref Copy, imports.%Core.Copy [concrete = constants.%Copy.type] // CHECK:STDOUT: } // CHECK:STDOUT: %T.loc5_13.2: %Copy.type = symbolic_binding T, 0 [symbolic = %T.loc5_13.1 (constants.%T.035)] // CHECK:STDOUT: } // CHECK:STDOUT: %Declaration.decl: %Declaration.type = class_decl @Declaration [concrete = constants.%Declaration.generic] { // CHECK:STDOUT: %T.patt: %pattern_type.98f = symbolic_binding_pattern T, 0 [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %.loc17_23.1: type = splice_block %.loc17_23.2 [concrete = type] { // CHECK:STDOUT: // CHECK:STDOUT: %.loc17_23.2: type = type_literal type [concrete = type] // CHECK:STDOUT: } // CHECK:STDOUT: %T.loc17_19.2: type = symbolic_binding T, 0 [symbolic = %T.loc17_19.1 (constants.%T.67d)] // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic class @Class(%T.loc5_13.2: %Copy.type) { // CHECK:STDOUT: %T.loc5_13.1: %Copy.type = symbolic_binding T, 0 [symbolic = %T.loc5_13.1 (constants.%T.035)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %Class.GetAddr.type: type = fn_type @Class.GetAddr, @Class(%T.loc5_13.1) [symbolic = %Class.GetAddr.type (constants.%Class.GetAddr.type)] // CHECK:STDOUT: %Class.GetAddr: @Class.%Class.GetAddr.type (%Class.GetAddr.type) = struct_value () [symbolic = %Class.GetAddr (constants.%Class.GetAddr)] // CHECK:STDOUT: %Class.GetValue.type: type = fn_type @Class.GetValue, @Class(%T.loc5_13.1) [symbolic = %Class.GetValue.type (constants.%Class.GetValue.type)] // CHECK:STDOUT: %Class.GetValue: @Class.%Class.GetValue.type (%Class.GetValue.type) = struct_value () [symbolic = %Class.GetValue (constants.%Class.GetValue)] // CHECK:STDOUT: %T.binding.as_type: type = symbolic_binding_type T, 0, %T.loc5_13.1 [symbolic = %T.binding.as_type (constants.%T.binding.as_type)] // CHECK:STDOUT: %require_complete: = require_complete_type %T.binding.as_type [symbolic = %require_complete (constants.%require_complete.67c)] // CHECK:STDOUT: %Class: type = class_type @Class, @Class(%T.loc5_13.1) [symbolic = %Class (constants.%Class)] // CHECK:STDOUT: %Class.elem: type = unbound_element_type %Class, %T.binding.as_type [symbolic = %Class.elem (constants.%Class.elem)] // CHECK:STDOUT: %struct_type.k: type = struct_type {.k: @Class.%T.binding.as_type (%T.binding.as_type)} [symbolic = %struct_type.k (constants.%struct_type.k)] // CHECK:STDOUT: %complete_type.loc15_1.2: = complete_type_witness %struct_type.k [symbolic = %complete_type.loc15_1.2 (constants.%complete_type)] // CHECK:STDOUT: // CHECK:STDOUT: class { // CHECK:STDOUT: %Class.GetAddr.decl: @Class.%Class.GetAddr.type (%Class.GetAddr.type) = fn_decl @Class.GetAddr [symbolic = @Class.%Class.GetAddr (constants.%Class.GetAddr)] { // CHECK:STDOUT: %self.patt: @Class.GetAddr.%pattern_type.loc6_18 (%pattern_type.893) = ref_binding_pattern self [concrete] // CHECK:STDOUT: %self.param_patt: @Class.GetAddr.%pattern_type.loc6_18 (%pattern_type.893) = ref_param_pattern %self.patt [concrete] // CHECK:STDOUT: %return.patt: @Class.GetAddr.%pattern_type.loc6_32 (%pattern_type.65a) = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: @Class.GetAddr.%pattern_type.loc6_32 (%pattern_type.65a) = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %T.ref: %Copy.type = name_ref T, @Class.%T.loc5_13.2 [symbolic = %T (constants.%T.035)] // CHECK:STDOUT: %T.as_type: type = facet_access_type %T.ref [symbolic = %T.binding.as_type (constants.%T.binding.as_type)] // CHECK:STDOUT: %.loc6_36.2: type = converted %T.ref, %T.as_type [symbolic = %T.binding.as_type (constants.%T.binding.as_type)] // CHECK:STDOUT: %ptr.loc6_36.2: type = ptr_type %.loc6_36.2 [symbolic = %ptr.loc6_36.1 (constants.%ptr.e7d)] // CHECK:STDOUT: %.loc6_36.3: Core.Form = init_form %ptr.loc6_36.2 [symbolic = %.loc6_36.1 (constants.%.66f)] // CHECK:STDOUT: %self.param: ref @Class.GetAddr.%Class (%Class) = ref_param call_param0 // CHECK:STDOUT: %.loc6_24.1: type = splice_block %Self.ref [symbolic = %Class (constants.%Class)] { // CHECK:STDOUT: %.loc6_24.2: type = specific_constant constants.%Class, @Class(constants.%T.035) [symbolic = %Class (constants.%Class)] // CHECK:STDOUT: %Self.ref: type = name_ref Self, %.loc6_24.2 [symbolic = %Class (constants.%Class)] // CHECK:STDOUT: } // CHECK:STDOUT: %self: ref @Class.GetAddr.%Class (%Class) = ref_binding self, %self.param // CHECK:STDOUT: %return.param: ref @Class.GetAddr.%ptr.loc6_36.1 (%ptr.e7d) = out_param call_param1 // CHECK:STDOUT: %return: ref @Class.GetAddr.%ptr.loc6_36.1 (%ptr.e7d) = return_slot %return.param // CHECK:STDOUT: } // CHECK:STDOUT: %Class.GetValue.decl: @Class.%Class.GetValue.type (%Class.GetValue.type) = fn_decl @Class.GetValue [symbolic = @Class.%Class.GetValue (constants.%Class.GetValue)] { // CHECK:STDOUT: %self.patt: @Class.GetValue.%pattern_type.loc10_15 (%pattern_type.893) = value_binding_pattern self [concrete] // CHECK:STDOUT: %self.param_patt: @Class.GetValue.%pattern_type.loc10_15 (%pattern_type.893) = value_param_pattern %self.patt [concrete] // CHECK:STDOUT: %return.patt: @Class.GetValue.%pattern_type.loc10_29 (%pattern_type.9b9f0c.2) = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: @Class.GetValue.%pattern_type.loc10_29 (%pattern_type.9b9f0c.2) = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %T.ref: %Copy.type = name_ref T, @Class.%T.loc5_13.2 [symbolic = %T (constants.%T.035)] // CHECK:STDOUT: %T.as_type: type = facet_access_type %T.ref [symbolic = %T.binding.as_type (constants.%T.binding.as_type)] // CHECK:STDOUT: %.loc10_32.3: type = converted %T.ref, %T.as_type [symbolic = %T.binding.as_type (constants.%T.binding.as_type)] // CHECK:STDOUT: %.loc10_32.4: Core.Form = init_form %.loc10_32.3 [symbolic = %.loc10_32.2 (constants.%.076a48.2)] // CHECK:STDOUT: %self.param: @Class.GetValue.%Class (%Class) = value_param call_param0 // CHECK:STDOUT: %.loc10_21.1: type = splice_block %Self.ref [symbolic = %Class (constants.%Class)] { // CHECK:STDOUT: %.loc10_21.2: type = specific_constant constants.%Class, @Class(constants.%T.035) [symbolic = %Class (constants.%Class)] // CHECK:STDOUT: %Self.ref: type = name_ref Self, %.loc10_21.2 [symbolic = %Class (constants.%Class)] // CHECK:STDOUT: } // CHECK:STDOUT: %self: @Class.GetValue.%Class (%Class) = value_binding self, %self.param // CHECK:STDOUT: %return.param: ref @Class.GetValue.%T.binding.as_type (%T.binding.as_type) = out_param call_param1 // CHECK:STDOUT: %return: ref @Class.GetValue.%T.binding.as_type (%T.binding.as_type) = return_slot %return.param // CHECK:STDOUT: } // CHECK:STDOUT: %T.ref: %Copy.type = name_ref T, %T.loc5_13.2 [symbolic = %T.loc5_13.1 (constants.%T.035)] // CHECK:STDOUT: %T.as_type: type = facet_access_type %T.ref [symbolic = %T.binding.as_type (constants.%T.binding.as_type)] // CHECK:STDOUT: %.loc14_10: type = converted %T.ref, %T.as_type [symbolic = %T.binding.as_type (constants.%T.binding.as_type)] // CHECK:STDOUT: %.loc14_8: @Class.%Class.elem (%Class.elem) = field_decl k, element0 [concrete] // CHECK:STDOUT: %complete_type.loc15_1.1: = complete_type_witness constants.%struct_type.k [symbolic = %complete_type.loc15_1.2 (constants.%complete_type)] // CHECK:STDOUT: complete_type_witness = %complete_type.loc15_1.1 // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%Class // CHECK:STDOUT: .T = // CHECK:STDOUT: .GetAddr = %Class.GetAddr.decl // CHECK:STDOUT: .GetValue = %Class.GetValue.decl // CHECK:STDOUT: .k = %.loc14_8 // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic class @Declaration(%T.loc17_19.2: type) { // CHECK:STDOUT: %T.loc17_19.1: type = symbolic_binding T, 0 [symbolic = %T.loc17_19.1 (constants.%T.67d)] // CHECK:STDOUT: // CHECK:STDOUT: class; // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic fn @Class.GetAddr(@Class.%T.loc5_13.2: %Copy.type) { // CHECK:STDOUT: %T: %Copy.type = symbolic_binding T, 0 [symbolic = %T (constants.%T.035)] // CHECK:STDOUT: %Class: type = class_type @Class, @Class(%T) [symbolic = %Class (constants.%Class)] // CHECK:STDOUT: %pattern_type.loc6_18: type = pattern_type %Class [symbolic = %pattern_type.loc6_18 (constants.%pattern_type.893)] // CHECK:STDOUT: %T.binding.as_type: type = symbolic_binding_type T, 0, %T [symbolic = %T.binding.as_type (constants.%T.binding.as_type)] // CHECK:STDOUT: %ptr.loc6_36.1: type = ptr_type %T.binding.as_type [symbolic = %ptr.loc6_36.1 (constants.%ptr.e7d)] // CHECK:STDOUT: %.loc6_36.1: Core.Form = init_form %ptr.loc6_36.1 [symbolic = %.loc6_36.1 (constants.%.66f)] // CHECK:STDOUT: %pattern_type.loc6_32: type = pattern_type %ptr.loc6_36.1 [symbolic = %pattern_type.loc6_32 (constants.%pattern_type.65a)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete.loc6_22: = require_complete_type %Class [symbolic = %require_complete.loc6_22 (constants.%require_complete.904)] // CHECK:STDOUT: %require_complete.loc6_36: = require_complete_type %ptr.loc6_36.1 [symbolic = %require_complete.loc6_36 (constants.%require_complete.9dc)] // CHECK:STDOUT: %Class.elem: type = unbound_element_type %Class, %T.binding.as_type [symbolic = %Class.elem (constants.%Class.elem)] // CHECK:STDOUT: %.loc7_12.1: require_specific_def_type = require_specific_def @ptr.as.Copy.impl(%T.binding.as_type) [symbolic = %.loc7_12.1 (constants.%.3a3)] // CHECK:STDOUT: %Copy.lookup_impl_witness: = lookup_impl_witness %ptr.loc6_36.1, @Copy [symbolic = %Copy.lookup_impl_witness (constants.%Copy.lookup_impl_witness.2e7)] // CHECK:STDOUT: %Copy.facet: %Copy.type = facet_value %ptr.loc6_36.1, (%Copy.lookup_impl_witness) [symbolic = %Copy.facet (constants.%Copy.facet)] // CHECK:STDOUT: %Copy.WithSelf.Op.type: type = fn_type @Copy.WithSelf.Op, @Copy.WithSelf(%Copy.facet) [symbolic = %Copy.WithSelf.Op.type (constants.%Copy.WithSelf.Op.type.486)] // CHECK:STDOUT: %.loc7_12.2: type = fn_type_with_self_type %Copy.WithSelf.Op.type, %Copy.facet [symbolic = %.loc7_12.2 (constants.%.b63)] // CHECK:STDOUT: %impl.elem0.loc7_12.2: @Class.GetAddr.%.loc7_12.2 (%.b63) = impl_witness_access %Copy.lookup_impl_witness, element0 [symbolic = %impl.elem0.loc7_12.2 (constants.%impl.elem0.387)] // CHECK:STDOUT: %specific_impl_fn.loc7_12.2: = specific_impl_function %impl.elem0.loc7_12.2, @Copy.WithSelf.Op(%Copy.facet) [symbolic = %specific_impl_fn.loc7_12.2 (constants.%specific_impl_fn.7f8)] // CHECK:STDOUT: // CHECK:STDOUT: fn(%self.param: ref @Class.GetAddr.%Class (%Class)) -> out %return.param: @Class.GetAddr.%ptr.loc6_36.1 (%ptr.e7d) { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %self.ref: ref @Class.GetAddr.%Class (%Class) = name_ref self, %self // CHECK:STDOUT: %k.ref: @Class.GetAddr.%Class.elem (%Class.elem) = name_ref k, @Class.%.loc14_8 [concrete = @Class.%.loc14_8] // CHECK:STDOUT: %.loc7_17: ref @Class.GetAddr.%T.binding.as_type (%T.binding.as_type) = class_element_access %self.ref, element0 // CHECK:STDOUT: %addr: @Class.GetAddr.%ptr.loc6_36.1 (%ptr.e7d) = addr_of %.loc7_17 // CHECK:STDOUT: %impl.elem0.loc7_12.1: @Class.GetAddr.%.loc7_12.2 (%.b63) = impl_witness_access constants.%Copy.lookup_impl_witness.2e7, element0 [symbolic = %impl.elem0.loc7_12.2 (constants.%impl.elem0.387)] // CHECK:STDOUT: %bound_method.loc7_12.1: = bound_method %addr, %impl.elem0.loc7_12.1 // CHECK:STDOUT: %specific_impl_fn.loc7_12.1: = specific_impl_function %impl.elem0.loc7_12.1, @Copy.WithSelf.Op(constants.%Copy.facet) [symbolic = %specific_impl_fn.loc7_12.2 (constants.%specific_impl_fn.7f8)] // CHECK:STDOUT: %bound_method.loc7_12.2: = bound_method %addr, %specific_impl_fn.loc7_12.1 // CHECK:STDOUT: %Copy.WithSelf.Op.call: init @Class.GetAddr.%ptr.loc6_36.1 (%ptr.e7d) = call %bound_method.loc7_12.2(%addr) // CHECK:STDOUT: return %Copy.WithSelf.Op.call // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic fn @Class.GetValue(@Class.%T.loc5_13.2: %Copy.type) { // CHECK:STDOUT: %T: %Copy.type = symbolic_binding T, 0 [symbolic = %T (constants.%T.035)] // CHECK:STDOUT: %Class: type = class_type @Class, @Class(%T) [symbolic = %Class (constants.%Class)] // CHECK:STDOUT: %pattern_type.loc10_15: type = pattern_type %Class [symbolic = %pattern_type.loc10_15 (constants.%pattern_type.893)] // CHECK:STDOUT: %T.binding.as_type: type = symbolic_binding_type T, 0, %T [symbolic = %T.binding.as_type (constants.%T.binding.as_type)] // CHECK:STDOUT: %.loc10_32.2: Core.Form = init_form %T.binding.as_type [symbolic = %.loc10_32.2 (constants.%.076a48.2)] // CHECK:STDOUT: %pattern_type.loc10_29: type = pattern_type %T.binding.as_type [symbolic = %pattern_type.loc10_29 (constants.%pattern_type.9b9f0c.2)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete.loc10: = require_complete_type %Class [symbolic = %require_complete.loc10 (constants.%require_complete.904)] // CHECK:STDOUT: %Class.elem: type = unbound_element_type %Class, %T.binding.as_type [symbolic = %Class.elem (constants.%Class.elem)] // CHECK:STDOUT: %require_complete.loc11: = require_complete_type %T.binding.as_type [symbolic = %require_complete.loc11 (constants.%require_complete.67c)] // CHECK:STDOUT: %Copy.lookup_impl_witness: = lookup_impl_witness %T, @Copy [symbolic = %Copy.lookup_impl_witness (constants.%Copy.lookup_impl_witness.58d)] // CHECK:STDOUT: %Copy.WithSelf.Op.type: type = fn_type @Copy.WithSelf.Op, @Copy.WithSelf(%T) [symbolic = %Copy.WithSelf.Op.type (constants.%Copy.WithSelf.Op.type.735e75.2)] // CHECK:STDOUT: %.loc11_16.3: type = fn_type_with_self_type %Copy.WithSelf.Op.type, %T [symbolic = %.loc11_16.3 (constants.%.023)] // CHECK:STDOUT: %impl.elem0.loc11_16.2: @Class.GetValue.%.loc11_16.3 (%.023) = impl_witness_access %Copy.lookup_impl_witness, element0 [symbolic = %impl.elem0.loc11_16.2 (constants.%impl.elem0.594)] // CHECK:STDOUT: %specific_impl_fn.loc11_16.2: = specific_impl_function %impl.elem0.loc11_16.2, @Copy.WithSelf.Op(%T) [symbolic = %specific_impl_fn.loc11_16.2 (constants.%specific_impl_fn.bdc)] // CHECK:STDOUT: // CHECK:STDOUT: fn(%self.param: @Class.GetValue.%Class (%Class)) -> out %return.param: @Class.GetValue.%T.binding.as_type (%T.binding.as_type) { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %self.ref: @Class.GetValue.%Class (%Class) = name_ref self, %self // CHECK:STDOUT: %k.ref: @Class.GetValue.%Class.elem (%Class.elem) = name_ref k, @Class.%.loc14_8 [concrete = @Class.%.loc14_8] // CHECK:STDOUT: %.loc11_16.1: ref @Class.GetValue.%T.binding.as_type (%T.binding.as_type) = class_element_access %self.ref, element0 // CHECK:STDOUT: %.loc11_16.2: @Class.GetValue.%T.binding.as_type (%T.binding.as_type) = acquire_value %.loc11_16.1 // CHECK:STDOUT: %impl.elem0.loc11_16.1: @Class.GetValue.%.loc11_16.3 (%.023) = impl_witness_access constants.%Copy.lookup_impl_witness.58d, element0 [symbolic = %impl.elem0.loc11_16.2 (constants.%impl.elem0.594)] // CHECK:STDOUT: %bound_method.loc11_16.1: = bound_method %.loc11_16.2, %impl.elem0.loc11_16.1 // CHECK:STDOUT: %specific_impl_fn.loc11_16.1: = specific_impl_function %impl.elem0.loc11_16.1, @Copy.WithSelf.Op(constants.%T.035) [symbolic = %specific_impl_fn.loc11_16.2 (constants.%specific_impl_fn.bdc)] // CHECK:STDOUT: %bound_method.loc11_16.2: = bound_method %.loc11_16.2, %specific_impl_fn.loc11_16.1 // CHECK:STDOUT: %.loc10_32.1: ref @Class.GetValue.%T.binding.as_type (%T.binding.as_type) = splice_block %return.param {} // CHECK:STDOUT: %Copy.WithSelf.Op.call: init @Class.GetValue.%T.binding.as_type (%T.binding.as_type) to %.loc10_32.1 = call %bound_method.loc11_16.2(%.loc11_16.2) // CHECK:STDOUT: return %Copy.WithSelf.Op.call to %return.param // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @Class(constants.%T.035) { // CHECK:STDOUT: %T.loc5_13.1 => constants.%T.035 // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %Class.GetAddr.type => constants.%Class.GetAddr.type // CHECK:STDOUT: %Class.GetAddr => constants.%Class.GetAddr // CHECK:STDOUT: %Class.GetValue.type => constants.%Class.GetValue.type // CHECK:STDOUT: %Class.GetValue => constants.%Class.GetValue // CHECK:STDOUT: %T.binding.as_type => constants.%T.binding.as_type // CHECK:STDOUT: %require_complete => constants.%require_complete.67c // CHECK:STDOUT: %Class => constants.%Class // CHECK:STDOUT: %Class.elem => constants.%Class.elem // CHECK:STDOUT: %struct_type.k => constants.%struct_type.k // CHECK:STDOUT: %complete_type.loc15_1.2 => constants.%complete_type // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @Class.GetAddr(constants.%T.035) { // CHECK:STDOUT: %T => constants.%T.035 // CHECK:STDOUT: %Class => constants.%Class // CHECK:STDOUT: %pattern_type.loc6_18 => constants.%pattern_type.893 // CHECK:STDOUT: %T.binding.as_type => constants.%T.binding.as_type // CHECK:STDOUT: %ptr.loc6_36.1 => constants.%ptr.e7d // CHECK:STDOUT: %.loc6_36.1 => constants.%.66f // CHECK:STDOUT: %pattern_type.loc6_32 => constants.%pattern_type.65a // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @Class.GetValue(constants.%T.035) { // CHECK:STDOUT: %T => constants.%T.035 // CHECK:STDOUT: %Class => constants.%Class // CHECK:STDOUT: %pattern_type.loc10_15 => constants.%pattern_type.893 // CHECK:STDOUT: %T.binding.as_type => constants.%T.binding.as_type // CHECK:STDOUT: %.loc10_32.2 => constants.%.076a48.2 // CHECK:STDOUT: %pattern_type.loc10_29 => constants.%pattern_type.9b9f0c.2 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @Declaration(constants.%T.67d) { // CHECK:STDOUT: %T.loc17_19.1 => constants.%T.67d // CHECK:STDOUT: } // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/class/generic/call.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/int.carbon // TODO: Add ranges and switch to "--dump-sem-ir-ranges=only". // EXTRA-ARGS: --dump-sem-ir-ranges=if-present // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/class/generic/call.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/class/generic/call.carbon // --- call.carbon library "[[@TEST_NAME]]"; class Class(T:! type, N:! i32) {} var a: Class(i32*, 5); // Requires an implicit conversion to type `type`. var b: Class((), 0); // --- fail_too_few.carbon library "[[@TEST_NAME]]"; class Class(T:! type, N:! i32) {} // CHECK:STDERR: fail_too_few.carbon:[[@LINE+7]]:8: error: 1 argument passed to generic class expecting 2 arguments [CallArgCountMismatch] // CHECK:STDERR: var a: Class(i32*); // CHECK:STDERR: ^~~~~~~~~~~ // CHECK:STDERR: fail_too_few.carbon:[[@LINE-5]]:1: note: calling generic class declared here [InCallToEntity] // CHECK:STDERR: class Class(T:! type, N:! i32) {} // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: var a: Class(i32*); // --- fail_too_many.carbon library "[[@TEST_NAME]]"; class Class(T:! type, N:! i32) {} // CHECK:STDERR: fail_too_many.carbon:[[@LINE+7]]:8: error: 3 arguments passed to generic class expecting 2 arguments [CallArgCountMismatch] // CHECK:STDERR: var a: Class(i32*, 1, 2); // CHECK:STDERR: ^~~~~~~~~~~~~~~~~ // CHECK:STDERR: fail_too_many.carbon:[[@LINE-5]]:1: note: calling generic class declared here [InCallToEntity] // CHECK:STDERR: class Class(T:! type, N:! i32) {} // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: var a: Class(i32*, 1, 2); // --- fail_no_conversion.carbon library "[[@TEST_NAME]]"; class Class(T:! type, N:! i32) {} // CHECK:STDERR: fail_no_conversion.carbon:[[@LINE+10]]:8: error: cannot implicitly convert non-type value of type `Core.IntLiteral` to `type` [ConversionFailureNonTypeToFacet] // CHECK:STDERR: var a: Class(5, i32*); // CHECK:STDERR: ^~~~~~~~~~~~~~ // CHECK:STDERR: fail_no_conversion.carbon:[[@LINE+7]]:8: note: type `Core.IntLiteral` does not implement interface `Core.ImplicitAs(type)` [MissingImplInMemberAccessInContext] // CHECK:STDERR: var a: Class(5, i32*); // CHECK:STDERR: ^~~~~~~~~~~~~~ // CHECK:STDERR: fail_no_conversion.carbon:[[@LINE-8]]:13: note: initializing generic parameter `T` declared here [InitializingGenericParam] // CHECK:STDERR: class Class(T:! type, N:! i32) {} // CHECK:STDERR: ^ // CHECK:STDERR: var a: Class(5, i32*); // --- call_in_nested_return_type.carbon class Outer(T:! type) { class Inner(U:! type) { fn A() -> Outer(T) { return {}; } fn B() -> Outer(U) { return {}; } fn C() -> Inner(T) { return {}; } fn D() -> Inner(U) { return {}; } } } // CHECK:STDOUT: --- call.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %type: type = facet_type [concrete] // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self] // CHECK:STDOUT: %pattern_type.98f: type = pattern_type type [concrete] // CHECK:STDOUT: %T: type = symbolic_binding T, 0 [symbolic] // CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [concrete] // CHECK:STDOUT: %Int.type: type = generic_class_type @Int [concrete] // CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] // CHECK:STDOUT: %Int.generic: %Int.type = struct_value () [concrete] // CHECK:STDOUT: %i32: type = class_type @Int, @Int(%int_32) [concrete] // CHECK:STDOUT: %pattern_type.7ce: type = pattern_type %i32 [concrete] // CHECK:STDOUT: %N.6f3: %i32 = symbolic_binding N, 1 [symbolic] // CHECK:STDOUT: %Class.type: type = generic_class_type @Class [concrete] // CHECK:STDOUT: %Class.generic: %Class.type = struct_value () [concrete] // CHECK:STDOUT: %Class.e86: type = class_type @Class, @Class(%T, %N.6f3) [symbolic] // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %complete_type.357: = complete_type_witness %empty_struct_type [concrete] // CHECK:STDOUT: %ptr.235: type = ptr_type %i32 [concrete] // CHECK:STDOUT: %int_5.64b: Core.IntLiteral = int_value 5 [concrete] // CHECK:STDOUT: %ImplicitAs.type.cc7: type = generic_interface_type @ImplicitAs [concrete] // CHECK:STDOUT: %ImplicitAs.generic: %ImplicitAs.type.cc7 = struct_value () [concrete] // CHECK:STDOUT: %ImplicitAs.type.e8c: type = facet_type <@ImplicitAs, @ImplicitAs(%i32)> [concrete] // CHECK:STDOUT: %To: Core.IntLiteral = symbolic_binding To, 0 [symbolic] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.4e6: type = fn_type @Core.IntLiteral.as.ImplicitAs.impl.Convert, @Core.IntLiteral.as.ImplicitAs.impl(%To) [symbolic] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.3c2: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.4e6 = struct_value () [symbolic] // CHECK:STDOUT: %ImplicitAs.impl_witness.6bc: = impl_witness imports.%ImplicitAs.impl_witness_table.74f, @Core.IntLiteral.as.ImplicitAs.impl(%int_32) [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.e0d: type = fn_type @Core.IntLiteral.as.ImplicitAs.impl.Convert, @Core.IntLiteral.as.ImplicitAs.impl(%int_32) [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.e0d = struct_value () [concrete] // CHECK:STDOUT: %ImplicitAs.facet: %ImplicitAs.type.e8c = facet_value Core.IntLiteral, (%ImplicitAs.impl_witness.6bc) [concrete] // CHECK:STDOUT: %ImplicitAs.WithSelf.Convert.type.b37: type = fn_type @ImplicitAs.WithSelf.Convert, @ImplicitAs.WithSelf(%i32, %ImplicitAs.facet) [concrete] // CHECK:STDOUT: %.545: type = fn_type_with_self_type %ImplicitAs.WithSelf.Convert.type.b37, %ImplicitAs.facet [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.bound.005: = bound_method %int_5.64b, %Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5 [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn: = specific_function %Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5, @Core.IntLiteral.as.ImplicitAs.impl.Convert(%int_32) [concrete] // CHECK:STDOUT: %bound_method.e9d: = bound_method %int_5.64b, %Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn [concrete] // CHECK:STDOUT: %int_5.0f6: %i32 = int_value 5 [concrete] // CHECK:STDOUT: %Class.ff1: type = class_type @Class, @Class(%ptr.235, %int_5.0f6) [concrete] // CHECK:STDOUT: %pattern_type.d58: type = pattern_type %Class.ff1 [concrete] // CHECK:STDOUT: %DefaultOrUnformed.type: type = facet_type <@DefaultOrUnformed> [concrete] // CHECK:STDOUT: %T.as.DefaultOrUnformed.impl.Op.type.6c6: type = fn_type @T.as.DefaultOrUnformed.impl.Op, @T.as.DefaultOrUnformed.impl(%T) [symbolic] // CHECK:STDOUT: %T.as.DefaultOrUnformed.impl.Op.ae1: %T.as.DefaultOrUnformed.impl.Op.type.6c6 = struct_value () [symbolic] // CHECK:STDOUT: %DefaultOrUnformed.impl_witness.77b: = impl_witness imports.%DefaultOrUnformed.impl_witness_table, @T.as.DefaultOrUnformed.impl(%Class.ff1) [concrete] // CHECK:STDOUT: %T.as.DefaultOrUnformed.impl.Op.type.b8a: type = fn_type @T.as.DefaultOrUnformed.impl.Op, @T.as.DefaultOrUnformed.impl(%Class.ff1) [concrete] // CHECK:STDOUT: %T.as.DefaultOrUnformed.impl.Op.090: %T.as.DefaultOrUnformed.impl.Op.type.b8a = struct_value () [concrete] // CHECK:STDOUT: %DefaultOrUnformed.facet.31a: %DefaultOrUnformed.type = facet_value %Class.ff1, (%DefaultOrUnformed.impl_witness.77b) [concrete] // CHECK:STDOUT: %T.as.DefaultOrUnformed.impl.Op.specific_fn.16a: = specific_function %T.as.DefaultOrUnformed.impl.Op.090, @T.as.DefaultOrUnformed.impl.Op(%Class.ff1) [concrete] // CHECK:STDOUT: %empty_tuple: %empty_tuple.type = tuple_value () [concrete] // CHECK:STDOUT: %int_0.5c6: Core.IntLiteral = int_value 0 [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.bound.897: = bound_method %int_0.5c6, %Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5 [concrete] // CHECK:STDOUT: %bound_method.d2e: = bound_method %int_0.5c6, %Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn [concrete] // CHECK:STDOUT: %int_0.6a9: %i32 = int_value 0 [concrete] // CHECK:STDOUT: %Class.7c6: type = class_type @Class, @Class(%empty_tuple.type, %int_0.6a9) [concrete] // CHECK:STDOUT: %pattern_type.a3c: type = pattern_type %Class.7c6 [concrete] // CHECK:STDOUT: %DefaultOrUnformed.impl_witness.c95: = impl_witness imports.%DefaultOrUnformed.impl_witness_table, @T.as.DefaultOrUnformed.impl(%Class.7c6) [concrete] // CHECK:STDOUT: %T.as.DefaultOrUnformed.impl.Op.type.7ec: type = fn_type @T.as.DefaultOrUnformed.impl.Op, @T.as.DefaultOrUnformed.impl(%Class.7c6) [concrete] // CHECK:STDOUT: %T.as.DefaultOrUnformed.impl.Op.b11: %T.as.DefaultOrUnformed.impl.Op.type.7ec = struct_value () [concrete] // CHECK:STDOUT: %DefaultOrUnformed.facet.d1f: %DefaultOrUnformed.type = facet_value %Class.7c6, (%DefaultOrUnformed.impl_witness.c95) [concrete] // CHECK:STDOUT: %T.as.DefaultOrUnformed.impl.Op.specific_fn.87f: = specific_function %T.as.DefaultOrUnformed.impl.Op.b11, @T.as.DefaultOrUnformed.impl.Op(%Class.7c6) [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: .Int = %Core.Int // CHECK:STDOUT: .ImplicitAs = %Core.ImplicitAs // CHECK:STDOUT: .DefaultOrUnformed = %Core.DefaultOrUnformed // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: %Core.Int: %Int.type = import_ref Core//prelude/parts/int, Int, loaded [concrete = constants.%Int.generic] // CHECK:STDOUT: %Core.ImplicitAs: %ImplicitAs.type.cc7 = import_ref Core//prelude/parts/as, ImplicitAs, loaded [concrete = constants.%ImplicitAs.generic] // CHECK:STDOUT: %Core.import_ref.42d: @Core.IntLiteral.as.ImplicitAs.impl.%Core.IntLiteral.as.ImplicitAs.impl.Convert.type (%Core.IntLiteral.as.ImplicitAs.impl.Convert.type.4e6) = import_ref Core//prelude/parts/int, loc{{\d+_\d+}}, loaded [symbolic = @Core.IntLiteral.as.ImplicitAs.impl.%Core.IntLiteral.as.ImplicitAs.impl.Convert (constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.3c2)] // CHECK:STDOUT: %ImplicitAs.impl_witness_table.74f = impl_witness_table (%Core.import_ref.42d), @Core.IntLiteral.as.ImplicitAs.impl [concrete] // CHECK:STDOUT: %Core.DefaultOrUnformed: type = import_ref Core//prelude/parts/default, DefaultOrUnformed, loaded [concrete = constants.%DefaultOrUnformed.type] // CHECK:STDOUT: %Core.import_ref.01d: @T.as.DefaultOrUnformed.impl.%T.as.DefaultOrUnformed.impl.Op.type (%T.as.DefaultOrUnformed.impl.Op.type.6c6) = import_ref Core//prelude/parts/default, loc{{\d+_\d+}}, loaded [symbolic = @T.as.DefaultOrUnformed.impl.%T.as.DefaultOrUnformed.impl.Op (constants.%T.as.DefaultOrUnformed.impl.Op.ae1)] // CHECK:STDOUT: %DefaultOrUnformed.impl_witness_table = impl_witness_table (%Core.import_ref.01d), @T.as.DefaultOrUnformed.impl [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .Class = %Class.decl // CHECK:STDOUT: .a = %a // CHECK:STDOUT: .b = %b // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %Class.decl: %Class.type = class_decl @Class [concrete = constants.%Class.generic] { // CHECK:STDOUT: %T.patt: %pattern_type.98f = symbolic_binding_pattern T, 0 [concrete] // CHECK:STDOUT: %N.patt: %pattern_type.7ce = symbolic_binding_pattern N, 1 [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %.loc4_17.1: type = splice_block %.loc4_17.2 [concrete = type] { // CHECK:STDOUT: %.Self.2: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %.loc4_17.2: type = type_literal type [concrete = type] // CHECK:STDOUT: } // CHECK:STDOUT: %T.loc4_13.2: type = symbolic_binding T, 0 [symbolic = %T.loc4_13.1 (constants.%T)] // CHECK:STDOUT: %.loc4_27: type = splice_block %i32 [concrete = constants.%i32] { // CHECK:STDOUT: %.Self.1: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %i32: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: } // CHECK:STDOUT: %N.loc4_23.2: %i32 = symbolic_binding N, 1 [symbolic = %N.loc4_23.1 (constants.%N.6f3)] // CHECK:STDOUT: } // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %a.patt: %pattern_type.d58 = ref_binding_pattern a [concrete] // CHECK:STDOUT: %a.var_patt: %pattern_type.d58 = var_pattern %a.patt [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %a.var: ref %Class.ff1 = var %a.var_patt [concrete] // CHECK:STDOUT: %.loc6_21.1: type = splice_block %Class.loc6 [concrete = constants.%Class.ff1] { // CHECK:STDOUT: %Class.ref.loc6: %Class.type = name_ref Class, %Class.decl [concrete = constants.%Class.generic] // CHECK:STDOUT: %i32: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %ptr: type = ptr_type %i32 [concrete = constants.%ptr.235] // CHECK:STDOUT: %int_5: Core.IntLiteral = int_value 5 [concrete = constants.%int_5.64b] // CHECK:STDOUT: %impl.elem0.loc6: %.545 = impl_witness_access constants.%ImplicitAs.impl_witness.6bc, element0 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5] // CHECK:STDOUT: %bound_method.loc6_21.1: = bound_method %int_5, %impl.elem0.loc6 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.bound.005] // CHECK:STDOUT: %specific_fn.loc6: = specific_function %impl.elem0.loc6, @Core.IntLiteral.as.ImplicitAs.impl.Convert(constants.%int_32) [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn] // CHECK:STDOUT: %bound_method.loc6_21.2: = bound_method %int_5, %specific_fn.loc6 [concrete = constants.%bound_method.e9d] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc6: init %i32 = call %bound_method.loc6_21.2(%int_5) [concrete = constants.%int_5.0f6] // CHECK:STDOUT: %.loc6_21.2: %i32 = value_of_initializer %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc6 [concrete = constants.%int_5.0f6] // CHECK:STDOUT: %.loc6_21.3: %i32 = converted %int_5, %.loc6_21.2 [concrete = constants.%int_5.0f6] // CHECK:STDOUT: %Class.loc6: type = class_type @Class, @Class(constants.%ptr.235, constants.%int_5.0f6) [concrete = constants.%Class.ff1] // CHECK:STDOUT: } // CHECK:STDOUT: %a: ref %Class.ff1 = ref_binding a, %a.var [concrete = %a.var] // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %b.patt: %pattern_type.a3c = ref_binding_pattern b [concrete] // CHECK:STDOUT: %b.var_patt: %pattern_type.a3c = var_pattern %b.patt [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %b.var: ref %Class.7c6 = var %b.var_patt [concrete] // CHECK:STDOUT: %.loc9_19.1: type = splice_block %Class.loc9 [concrete = constants.%Class.7c6] { // CHECK:STDOUT: %Class.ref.loc9: %Class.type = name_ref Class, %Class.decl [concrete = constants.%Class.generic] // CHECK:STDOUT: %.loc9_15: %empty_tuple.type = tuple_literal () [concrete = constants.%empty_tuple] // CHECK:STDOUT: %int_0: Core.IntLiteral = int_value 0 [concrete = constants.%int_0.5c6] // CHECK:STDOUT: %.loc9_19.2: type = converted %.loc9_15, constants.%empty_tuple.type [concrete = constants.%empty_tuple.type] // CHECK:STDOUT: %impl.elem0.loc9: %.545 = impl_witness_access constants.%ImplicitAs.impl_witness.6bc, element0 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5] // CHECK:STDOUT: %bound_method.loc9_19.1: = bound_method %int_0, %impl.elem0.loc9 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.bound.897] // CHECK:STDOUT: %specific_fn.loc9: = specific_function %impl.elem0.loc9, @Core.IntLiteral.as.ImplicitAs.impl.Convert(constants.%int_32) [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn] // CHECK:STDOUT: %bound_method.loc9_19.2: = bound_method %int_0, %specific_fn.loc9 [concrete = constants.%bound_method.d2e] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc9: init %i32 = call %bound_method.loc9_19.2(%int_0) [concrete = constants.%int_0.6a9] // CHECK:STDOUT: %.loc9_19.3: %i32 = value_of_initializer %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc9 [concrete = constants.%int_0.6a9] // CHECK:STDOUT: %.loc9_19.4: %i32 = converted %int_0, %.loc9_19.3 [concrete = constants.%int_0.6a9] // CHECK:STDOUT: %Class.loc9: type = class_type @Class, @Class(constants.%empty_tuple.type, constants.%int_0.6a9) [concrete = constants.%Class.7c6] // CHECK:STDOUT: } // CHECK:STDOUT: %b: ref %Class.7c6 = ref_binding b, %b.var [concrete = %b.var] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic class @Class(%T.loc4_13.2: type, %N.loc4_23.2: %i32) { // CHECK:STDOUT: %T.loc4_13.1: type = symbolic_binding T, 0 [symbolic = %T.loc4_13.1 (constants.%T)] // CHECK:STDOUT: %N.loc4_23.1: %i32 = symbolic_binding N, 1 [symbolic = %N.loc4_23.1 (constants.%N.6f3)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: // CHECK:STDOUT: class { // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type.357] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%Class.e86 // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @__global_init() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %DefaultOrUnformed.facet.loc6: %DefaultOrUnformed.type = facet_value constants.%Class.ff1, (constants.%DefaultOrUnformed.impl_witness.77b) [concrete = constants.%DefaultOrUnformed.facet.31a] // CHECK:STDOUT: %.loc6_22.1: %DefaultOrUnformed.type = converted constants.%Class.ff1, %DefaultOrUnformed.facet.loc6 [concrete = constants.%DefaultOrUnformed.facet.31a] // CHECK:STDOUT: %as_type.loc6: type = facet_access_type %.loc6_22.1 [concrete = constants.%Class.ff1] // CHECK:STDOUT: %.loc6_22.2: type = converted %.loc6_22.1, %as_type.loc6 [concrete = constants.%Class.ff1] // CHECK:STDOUT: %T.as.DefaultOrUnformed.impl.Op.specific_fn.1: = specific_function constants.%T.as.DefaultOrUnformed.impl.Op.090, @T.as.DefaultOrUnformed.impl.Op(constants.%Class.ff1) [concrete = constants.%T.as.DefaultOrUnformed.impl.Op.specific_fn.16a] // CHECK:STDOUT: %.loc6_1: ref %Class.ff1 = splice_block file.%a.var [concrete = file.%a.var] {} // CHECK:STDOUT: %T.as.DefaultOrUnformed.impl.Op.call.loc6: init %Class.ff1 to %.loc6_1 = call %T.as.DefaultOrUnformed.impl.Op.specific_fn.1() // CHECK:STDOUT: assign file.%a.var, %T.as.DefaultOrUnformed.impl.Op.call.loc6 // CHECK:STDOUT: %DefaultOrUnformed.facet.loc9: %DefaultOrUnformed.type = facet_value constants.%Class.7c6, (constants.%DefaultOrUnformed.impl_witness.c95) [concrete = constants.%DefaultOrUnformed.facet.d1f] // CHECK:STDOUT: %.loc9_20.1: %DefaultOrUnformed.type = converted constants.%Class.7c6, %DefaultOrUnformed.facet.loc9 [concrete = constants.%DefaultOrUnformed.facet.d1f] // CHECK:STDOUT: %as_type.loc9: type = facet_access_type %.loc9_20.1 [concrete = constants.%Class.7c6] // CHECK:STDOUT: %.loc9_20.2: type = converted %.loc9_20.1, %as_type.loc9 [concrete = constants.%Class.7c6] // CHECK:STDOUT: %T.as.DefaultOrUnformed.impl.Op.specific_fn.2: = specific_function constants.%T.as.DefaultOrUnformed.impl.Op.b11, @T.as.DefaultOrUnformed.impl.Op(constants.%Class.7c6) [concrete = constants.%T.as.DefaultOrUnformed.impl.Op.specific_fn.87f] // CHECK:STDOUT: %.loc9_1: ref %Class.7c6 = splice_block file.%b.var [concrete = file.%b.var] {} // CHECK:STDOUT: %T.as.DefaultOrUnformed.impl.Op.call.loc9: init %Class.7c6 to %.loc9_1 = call %T.as.DefaultOrUnformed.impl.Op.specific_fn.2() // CHECK:STDOUT: assign file.%b.var, %T.as.DefaultOrUnformed.impl.Op.call.loc9 // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @Class(constants.%T, constants.%N.6f3) { // CHECK:STDOUT: %T.loc4_13.1 => constants.%T // CHECK:STDOUT: %N.loc4_23.1 => constants.%N.6f3 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @Class(constants.%ptr.235, constants.%int_5.0f6) { // CHECK:STDOUT: %T.loc4_13.1 => constants.%ptr.235 // CHECK:STDOUT: %N.loc4_23.1 => constants.%int_5.0f6 // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @Class(constants.%empty_tuple.type, constants.%int_0.6a9) { // CHECK:STDOUT: %T.loc4_13.1 => constants.%empty_tuple.type // CHECK:STDOUT: %N.loc4_23.1 => constants.%int_0.6a9 // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- fail_too_few.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %type: type = facet_type [concrete] // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self] // CHECK:STDOUT: %pattern_type.98f: type = pattern_type type [concrete] // CHECK:STDOUT: %T: type = symbolic_binding T, 0 [symbolic] // CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [concrete] // CHECK:STDOUT: %Int.type: type = generic_class_type @Int [concrete] // CHECK:STDOUT: %Int.generic: %Int.type = struct_value () [concrete] // CHECK:STDOUT: %i32: type = class_type @Int, @Int(%int_32) [concrete] // CHECK:STDOUT: %pattern_type.7ce: type = pattern_type %i32 [concrete] // CHECK:STDOUT: %N.6f3: %i32 = symbolic_binding N, 1 [symbolic] // CHECK:STDOUT: %Class.type: type = generic_class_type @Class [concrete] // CHECK:STDOUT: %Class.generic: %Class.type = struct_value () [concrete] // CHECK:STDOUT: %Class: type = class_type @Class, @Class(%T, %N.6f3) [symbolic] // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %complete_type.357: = complete_type_witness %empty_struct_type [concrete] // CHECK:STDOUT: %ptr: type = ptr_type %i32 [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: .Int = %Core.Int // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: %Core.Int: %Int.type = import_ref Core//prelude/parts/int, Int, loaded [concrete = constants.%Int.generic] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .Class = %Class.decl // CHECK:STDOUT: .a = %a // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %Class.decl: %Class.type = class_decl @Class [concrete = constants.%Class.generic] { // CHECK:STDOUT: %T.patt: %pattern_type.98f = symbolic_binding_pattern T, 0 [concrete] // CHECK:STDOUT: %N.patt: %pattern_type.7ce = symbolic_binding_pattern N, 1 [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %.loc4_17.1: type = splice_block %.loc4_17.2 [concrete = type] { // CHECK:STDOUT: %.Self.2: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %.loc4_17.2: type = type_literal type [concrete = type] // CHECK:STDOUT: } // CHECK:STDOUT: %T.loc4_13.2: type = symbolic_binding T, 0 [symbolic = %T.loc4_13.1 (constants.%T)] // CHECK:STDOUT: %.loc4_27: type = splice_block %i32 [concrete = constants.%i32] { // CHECK:STDOUT: %.Self.1: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %i32: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: } // CHECK:STDOUT: %N.loc4_23.2: %i32 = symbolic_binding N, 1 [symbolic = %N.loc4_23.1 (constants.%N.6f3)] // CHECK:STDOUT: } // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %a.patt: = ref_binding_pattern a [concrete] // CHECK:STDOUT: %a.var_patt: = var_pattern %a.patt [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %a.var: ref = var %a.var_patt [concrete = ] // CHECK:STDOUT: %.1: = splice_block [concrete = ] { // CHECK:STDOUT: %Class.ref: %Class.type = name_ref Class, %Class.decl [concrete = constants.%Class.generic] // CHECK:STDOUT: %i32: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %ptr: type = ptr_type %i32 [concrete = constants.%ptr] // CHECK:STDOUT: } // CHECK:STDOUT: %a: ref = ref_binding a, [concrete = ] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic class @Class(%T.loc4_13.2: type, %N.loc4_23.2: %i32) { // CHECK:STDOUT: %T.loc4_13.1: type = symbolic_binding T, 0 [symbolic = %T.loc4_13.1 (constants.%T)] // CHECK:STDOUT: %N.loc4_23.1: %i32 = symbolic_binding N, 1 [symbolic = %N.loc4_23.1 (constants.%N.6f3)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: // CHECK:STDOUT: class { // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type.357] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%Class // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @__global_init() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: assign file.%a.var, // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @Class(constants.%T, constants.%N.6f3) { // CHECK:STDOUT: %T.loc4_13.1 => constants.%T // CHECK:STDOUT: %N.loc4_23.1 => constants.%N.6f3 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- fail_too_many.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %type: type = facet_type [concrete] // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self] // CHECK:STDOUT: %pattern_type.98f: type = pattern_type type [concrete] // CHECK:STDOUT: %T: type = symbolic_binding T, 0 [symbolic] // CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [concrete] // CHECK:STDOUT: %Int.type: type = generic_class_type @Int [concrete] // CHECK:STDOUT: %Int.generic: %Int.type = struct_value () [concrete] // CHECK:STDOUT: %i32: type = class_type @Int, @Int(%int_32) [concrete] // CHECK:STDOUT: %pattern_type.7ce: type = pattern_type %i32 [concrete] // CHECK:STDOUT: %N.6f3: %i32 = symbolic_binding N, 1 [symbolic] // CHECK:STDOUT: %Class.type: type = generic_class_type @Class [concrete] // CHECK:STDOUT: %Class.generic: %Class.type = struct_value () [concrete] // CHECK:STDOUT: %Class: type = class_type @Class, @Class(%T, %N.6f3) [symbolic] // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %complete_type.357: = complete_type_witness %empty_struct_type [concrete] // CHECK:STDOUT: %ptr: type = ptr_type %i32 [concrete] // CHECK:STDOUT: %int_1: Core.IntLiteral = int_value 1 [concrete] // CHECK:STDOUT: %int_2: Core.IntLiteral = int_value 2 [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: .Int = %Core.Int // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: %Core.Int: %Int.type = import_ref Core//prelude/parts/int, Int, loaded [concrete = constants.%Int.generic] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .Class = %Class.decl // CHECK:STDOUT: .a = %a // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %Class.decl: %Class.type = class_decl @Class [concrete = constants.%Class.generic] { // CHECK:STDOUT: %T.patt: %pattern_type.98f = symbolic_binding_pattern T, 0 [concrete] // CHECK:STDOUT: %N.patt: %pattern_type.7ce = symbolic_binding_pattern N, 1 [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %.loc4_17.1: type = splice_block %.loc4_17.2 [concrete = type] { // CHECK:STDOUT: %.Self.2: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %.loc4_17.2: type = type_literal type [concrete = type] // CHECK:STDOUT: } // CHECK:STDOUT: %T.loc4_13.2: type = symbolic_binding T, 0 [symbolic = %T.loc4_13.1 (constants.%T)] // CHECK:STDOUT: %.loc4_27: type = splice_block %i32 [concrete = constants.%i32] { // CHECK:STDOUT: %.Self.1: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %i32: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: } // CHECK:STDOUT: %N.loc4_23.2: %i32 = symbolic_binding N, 1 [symbolic = %N.loc4_23.1 (constants.%N.6f3)] // CHECK:STDOUT: } // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %a.patt: = ref_binding_pattern a [concrete] // CHECK:STDOUT: %a.var_patt: = var_pattern %a.patt [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %a.var: ref = var %a.var_patt [concrete = ] // CHECK:STDOUT: %.1: = splice_block [concrete = ] { // CHECK:STDOUT: %Class.ref: %Class.type = name_ref Class, %Class.decl [concrete = constants.%Class.generic] // CHECK:STDOUT: %i32: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %ptr: type = ptr_type %i32 [concrete = constants.%ptr] // CHECK:STDOUT: %int_1: Core.IntLiteral = int_value 1 [concrete = constants.%int_1] // CHECK:STDOUT: %int_2: Core.IntLiteral = int_value 2 [concrete = constants.%int_2] // CHECK:STDOUT: } // CHECK:STDOUT: %a: ref = ref_binding a, [concrete = ] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic class @Class(%T.loc4_13.2: type, %N.loc4_23.2: %i32) { // CHECK:STDOUT: %T.loc4_13.1: type = symbolic_binding T, 0 [symbolic = %T.loc4_13.1 (constants.%T)] // CHECK:STDOUT: %N.loc4_23.1: %i32 = symbolic_binding N, 1 [symbolic = %N.loc4_23.1 (constants.%N.6f3)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: // CHECK:STDOUT: class { // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type.357] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%Class // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @__global_init() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: assign file.%a.var, // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @Class(constants.%T, constants.%N.6f3) { // CHECK:STDOUT: %T.loc4_13.1 => constants.%T // CHECK:STDOUT: %N.loc4_23.1 => constants.%N.6f3 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- fail_no_conversion.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %type: type = facet_type [concrete] // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self] // CHECK:STDOUT: %pattern_type.98f: type = pattern_type type [concrete] // CHECK:STDOUT: %T: type = symbolic_binding T, 0 [symbolic] // CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [concrete] // CHECK:STDOUT: %Int.type: type = generic_class_type @Int [concrete] // CHECK:STDOUT: %Int.generic: %Int.type = struct_value () [concrete] // CHECK:STDOUT: %i32: type = class_type @Int, @Int(%int_32) [concrete] // CHECK:STDOUT: %pattern_type.7ce: type = pattern_type %i32 [concrete] // CHECK:STDOUT: %N.6f3: %i32 = symbolic_binding N, 1 [symbolic] // CHECK:STDOUT: %Class.type: type = generic_class_type @Class [concrete] // CHECK:STDOUT: %Class.generic: %Class.type = struct_value () [concrete] // CHECK:STDOUT: %Class: type = class_type @Class, @Class(%T, %N.6f3) [symbolic] // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %complete_type.357: = complete_type_witness %empty_struct_type [concrete] // CHECK:STDOUT: %int_5: Core.IntLiteral = int_value 5 [concrete] // CHECK:STDOUT: %ptr: type = ptr_type %i32 [concrete] // CHECK:STDOUT: %ImplicitAs.type.cc7: type = generic_interface_type @ImplicitAs [concrete] // CHECK:STDOUT: %ImplicitAs.generic: %ImplicitAs.type.cc7 = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: .Int = %Core.Int // CHECK:STDOUT: .ImplicitAs = %Core.ImplicitAs // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: %Core.Int: %Int.type = import_ref Core//prelude/parts/int, Int, loaded [concrete = constants.%Int.generic] // CHECK:STDOUT: %Core.ImplicitAs: %ImplicitAs.type.cc7 = import_ref Core//prelude/parts/as, ImplicitAs, loaded [concrete = constants.%ImplicitAs.generic] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .Class = %Class.decl // CHECK:STDOUT: .a = %a // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %Class.decl: %Class.type = class_decl @Class [concrete = constants.%Class.generic] { // CHECK:STDOUT: %T.patt: %pattern_type.98f = symbolic_binding_pattern T, 0 [concrete] // CHECK:STDOUT: %N.patt: %pattern_type.7ce = symbolic_binding_pattern N, 1 [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %.loc4_17.1: type = splice_block %.loc4_17.2 [concrete = type] { // CHECK:STDOUT: %.Self.2: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %.loc4_17.2: type = type_literal type [concrete = type] // CHECK:STDOUT: } // CHECK:STDOUT: %T.loc4_13.2: type = symbolic_binding T, 0 [symbolic = %T.loc4_13.1 (constants.%T)] // CHECK:STDOUT: %.loc4_27: type = splice_block %i32 [concrete = constants.%i32] { // CHECK:STDOUT: %.Self.1: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %i32: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: } // CHECK:STDOUT: %N.loc4_23.2: %i32 = symbolic_binding N, 1 [symbolic = %N.loc4_23.1 (constants.%N.6f3)] // CHECK:STDOUT: } // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %a.patt: = ref_binding_pattern a [concrete] // CHECK:STDOUT: %a.var_patt: = var_pattern %a.patt [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %a.var: ref = var %a.var_patt [concrete = ] // CHECK:STDOUT: %.1: = splice_block [concrete = ] { // CHECK:STDOUT: %Class.ref: %Class.type = name_ref Class, %Class.decl [concrete = constants.%Class.generic] // CHECK:STDOUT: %int_5: Core.IntLiteral = int_value 5 [concrete = constants.%int_5] // CHECK:STDOUT: %i32: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %ptr: type = ptr_type %i32 [concrete = constants.%ptr] // CHECK:STDOUT: %.loc16: type = converted %int_5, [concrete = ] // CHECK:STDOUT: } // CHECK:STDOUT: %a: ref = ref_binding a, [concrete = ] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic class @Class(%T.loc4_13.2: type, %N.loc4_23.2: %i32) { // CHECK:STDOUT: %T.loc4_13.1: type = symbolic_binding T, 0 [symbolic = %T.loc4_13.1 (constants.%T)] // CHECK:STDOUT: %N.loc4_23.1: %i32 = symbolic_binding N, 1 [symbolic = %N.loc4_23.1 (constants.%N.6f3)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: // CHECK:STDOUT: class { // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type.357] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%Class // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @__global_init() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: assign file.%a.var, // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @Class(constants.%T, constants.%N.6f3) { // CHECK:STDOUT: %T.loc4_13.1 => constants.%T // CHECK:STDOUT: %N.loc4_23.1 => constants.%N.6f3 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- call_in_nested_return_type.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %type: type = facet_type [concrete] // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self] // CHECK:STDOUT: %pattern_type.98f: type = pattern_type type [concrete] // CHECK:STDOUT: %T: type = symbolic_binding T, 0 [symbolic] // CHECK:STDOUT: %Outer.type: type = generic_class_type @Outer [concrete] // CHECK:STDOUT: %Outer.generic: %Outer.type = struct_value () [concrete] // CHECK:STDOUT: %Outer.387: type = class_type @Outer, @Outer(%T) [symbolic] // CHECK:STDOUT: %U: type = symbolic_binding U, 1 [symbolic] // CHECK:STDOUT: %Inner.type.e0d: type = generic_class_type @Inner, @Outer(%T) [symbolic] // CHECK:STDOUT: %Inner.generic.ada: %Inner.type.e0d = struct_value () [symbolic] // CHECK:STDOUT: %Inner.e21: type = class_type @Inner, @Inner(%T, %U) [symbolic] // CHECK:STDOUT: %.b11: Core.Form = init_form %Outer.387 [symbolic] // CHECK:STDOUT: %pattern_type.130: type = pattern_type %Outer.387 [symbolic] // CHECK:STDOUT: %Inner.A.type.c2f: type = fn_type @Inner.A, @Inner(%T, %U) [symbolic] // CHECK:STDOUT: %Inner.A.07b: %Inner.A.type.c2f = struct_value () [symbolic] // CHECK:STDOUT: %Outer.d2f: type = class_type @Outer, @Outer(%U) [symbolic] // CHECK:STDOUT: %.a87: Core.Form = init_form %Outer.d2f [symbolic] // CHECK:STDOUT: %pattern_type.760: type = pattern_type %Outer.d2f [symbolic] // CHECK:STDOUT: %Inner.B.type.f42: type = fn_type @Inner.B, @Inner(%T, %U) [symbolic] // CHECK:STDOUT: %Inner.B.b14: %Inner.B.type.f42 = struct_value () [symbolic] // CHECK:STDOUT: %Inner.ddc: type = class_type @Inner, @Inner(%T, %T) [symbolic] // CHECK:STDOUT: %.fdd: Core.Form = init_form %Inner.ddc [symbolic] // CHECK:STDOUT: %pattern_type.81e: type = pattern_type %Inner.ddc [symbolic] // CHECK:STDOUT: %Inner.C.type.912: type = fn_type @Inner.C, @Inner(%T, %U) [symbolic] // CHECK:STDOUT: %Inner.C.125: %Inner.C.type.912 = struct_value () [symbolic] // CHECK:STDOUT: %.223: Core.Form = init_form %Inner.e21 [symbolic] // CHECK:STDOUT: %pattern_type.0d1: type = pattern_type %Inner.e21 [symbolic] // CHECK:STDOUT: %Inner.D.type.cd0: type = fn_type @Inner.D, @Inner(%T, %U) [symbolic] // CHECK:STDOUT: %Inner.D.147: %Inner.D.type.cd0 = struct_value () [symbolic] // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %complete_type: = complete_type_witness %empty_struct_type [concrete] // CHECK:STDOUT: %require_complete.448: = require_complete_type %Outer.387 [symbolic] // CHECK:STDOUT: %empty_struct: %empty_struct_type = struct_value () [concrete] // CHECK:STDOUT: %Outer.val.ca1: %Outer.387 = struct_value () [symbolic] // CHECK:STDOUT: %Inner.type.26a: type = generic_class_type @Inner, @Outer(%U) [symbolic] // CHECK:STDOUT: %Inner.generic.ddc: %Inner.type.26a = struct_value () [symbolic] // CHECK:STDOUT: %require_complete.ade: = require_complete_type %Outer.d2f [symbolic] // CHECK:STDOUT: %Outer.val.756: %Outer.d2f = struct_value () [symbolic] // CHECK:STDOUT: %Inner.A.type.e74: type = fn_type @Inner.A, @Inner(%T, %T) [symbolic] // CHECK:STDOUT: %Inner.A.d94: %Inner.A.type.e74 = struct_value () [symbolic] // CHECK:STDOUT: %Inner.B.type.539: type = fn_type @Inner.B, @Inner(%T, %T) [symbolic] // CHECK:STDOUT: %Inner.B.455: %Inner.B.type.539 = struct_value () [symbolic] // CHECK:STDOUT: %Inner.C.type.4b3: type = fn_type @Inner.C, @Inner(%T, %T) [symbolic] // CHECK:STDOUT: %Inner.C.f04: %Inner.C.type.4b3 = struct_value () [symbolic] // CHECK:STDOUT: %Inner.D.type.d17: type = fn_type @Inner.D, @Inner(%T, %T) [symbolic] // CHECK:STDOUT: %Inner.D.c1c: %Inner.D.type.d17 = struct_value () [symbolic] // CHECK:STDOUT: %require_complete.de9: = require_complete_type %Inner.ddc [symbolic] // CHECK:STDOUT: %Inner.val.240: %Inner.ddc = struct_value () [symbolic] // CHECK:STDOUT: %require_complete.41c: = require_complete_type %Inner.e21 [symbolic] // CHECK:STDOUT: %Inner.val.43a: %Inner.e21 = struct_value () [symbolic] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .Outer = %Outer.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %Outer.decl: %Outer.type = class_decl @Outer [concrete = constants.%Outer.generic] { // CHECK:STDOUT: %T.patt: %pattern_type.98f = symbolic_binding_pattern T, 0 [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %.loc2_17.1: type = splice_block %.loc2_17.2 [concrete = type] { // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %.loc2_17.2: type = type_literal type [concrete = type] // CHECK:STDOUT: } // CHECK:STDOUT: %T.loc2_13.2: type = symbolic_binding T, 0 [symbolic = %T.loc2_13.1 (constants.%T)] // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic class @Outer(%T.loc2_13.2: type) { // CHECK:STDOUT: %T.loc2_13.1: type = symbolic_binding T, 0 [symbolic = %T.loc2_13.1 (constants.%T)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %Inner.type: type = generic_class_type @Inner, @Outer(%T.loc2_13.1) [symbolic = %Inner.type (constants.%Inner.type.e0d)] // CHECK:STDOUT: %Inner.generic: @Outer.%Inner.type (%Inner.type.e0d) = struct_value () [symbolic = %Inner.generic (constants.%Inner.generic.ada)] // CHECK:STDOUT: // CHECK:STDOUT: class { // CHECK:STDOUT: %Inner.decl: @Outer.%Inner.type (%Inner.type.e0d) = class_decl @Inner [symbolic = @Outer.%Inner.generic (constants.%Inner.generic.ada)] { // CHECK:STDOUT: %U.patt: %pattern_type.98f = symbolic_binding_pattern U, 1 [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %.loc3_19.1: type = splice_block %.loc3_19.2 [concrete = type] { // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %.loc3_19.2: type = type_literal type [concrete = type] // CHECK:STDOUT: } // CHECK:STDOUT: %U.loc3_15.2: type = symbolic_binding U, 1 [symbolic = %U.loc3_15.1 (constants.%U)] // CHECK:STDOUT: } // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%Outer.387 // CHECK:STDOUT: .Inner = %Inner.decl // CHECK:STDOUT: .Outer = // CHECK:STDOUT: .T = // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic class @Inner(@Outer.%T.loc2_13.2: type, %U.loc3_15.2: type) { // CHECK:STDOUT: %U.loc3_15.1: type = symbolic_binding U, 1 [symbolic = %U.loc3_15.1 (constants.%U)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %T: type = symbolic_binding T, 0 [symbolic = %T (constants.%T)] // CHECK:STDOUT: %Inner.A.type: type = fn_type @Inner.A, @Inner(%T, %U.loc3_15.1) [symbolic = %Inner.A.type (constants.%Inner.A.type.c2f)] // CHECK:STDOUT: %Inner.A: @Inner.%Inner.A.type (%Inner.A.type.c2f) = struct_value () [symbolic = %Inner.A (constants.%Inner.A.07b)] // CHECK:STDOUT: %Inner.B.type: type = fn_type @Inner.B, @Inner(%T, %U.loc3_15.1) [symbolic = %Inner.B.type (constants.%Inner.B.type.f42)] // CHECK:STDOUT: %Inner.B: @Inner.%Inner.B.type (%Inner.B.type.f42) = struct_value () [symbolic = %Inner.B (constants.%Inner.B.b14)] // CHECK:STDOUT: %Inner.C.type: type = fn_type @Inner.C, @Inner(%T, %U.loc3_15.1) [symbolic = %Inner.C.type (constants.%Inner.C.type.912)] // CHECK:STDOUT: %Inner.C: @Inner.%Inner.C.type (%Inner.C.type.912) = struct_value () [symbolic = %Inner.C (constants.%Inner.C.125)] // CHECK:STDOUT: %Inner.D.type: type = fn_type @Inner.D, @Inner(%T, %U.loc3_15.1) [symbolic = %Inner.D.type (constants.%Inner.D.type.cd0)] // CHECK:STDOUT: %Inner.D: @Inner.%Inner.D.type (%Inner.D.type.cd0) = struct_value () [symbolic = %Inner.D (constants.%Inner.D.147)] // CHECK:STDOUT: // CHECK:STDOUT: class { // CHECK:STDOUT: %Inner.A.decl: @Inner.%Inner.A.type (%Inner.A.type.c2f) = fn_decl @Inner.A [symbolic = @Inner.%Inner.A (constants.%Inner.A.07b)] { // CHECK:STDOUT: %return.patt: @Inner.A.%pattern_type (%pattern_type.130) = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: @Inner.A.%pattern_type (%pattern_type.130) = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %Outer.ref: %Outer.type = name_ref Outer, file.%Outer.decl [concrete = constants.%Outer.generic] // CHECK:STDOUT: %T.ref: type = name_ref T, @Outer.%T.loc2_13.2 [symbolic = %T (constants.%T)] // CHECK:STDOUT: %Outer.loc4_22.2: type = class_type @Outer, @Outer(constants.%T) [symbolic = %Outer.loc4_22.1 (constants.%Outer.387)] // CHECK:STDOUT: %.loc4_22.2: Core.Form = init_form %Outer.loc4_22.2 [symbolic = %.loc4_22.1 (constants.%.b11)] // CHECK:STDOUT: %return.param: ref @Inner.A.%Outer.loc4_22.1 (%Outer.387) = out_param call_param0 // CHECK:STDOUT: %return: ref @Inner.A.%Outer.loc4_22.1 (%Outer.387) = return_slot %return.param // CHECK:STDOUT: } // CHECK:STDOUT: %Inner.B.decl: @Inner.%Inner.B.type (%Inner.B.type.f42) = fn_decl @Inner.B [symbolic = @Inner.%Inner.B (constants.%Inner.B.b14)] { // CHECK:STDOUT: %return.patt: @Inner.B.%pattern_type (%pattern_type.760) = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: @Inner.B.%pattern_type (%pattern_type.760) = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %Outer.ref: %Outer.type = name_ref Outer, file.%Outer.decl [concrete = constants.%Outer.generic] // CHECK:STDOUT: %U.ref: type = name_ref U, @Inner.%U.loc3_15.2 [symbolic = %U (constants.%U)] // CHECK:STDOUT: %Outer.loc7_22.2: type = class_type @Outer, @Outer(constants.%U) [symbolic = %Outer.loc7_22.1 (constants.%Outer.d2f)] // CHECK:STDOUT: %.loc7_22.2: Core.Form = init_form %Outer.loc7_22.2 [symbolic = %.loc7_22.1 (constants.%.a87)] // CHECK:STDOUT: %return.param: ref @Inner.B.%Outer.loc7_22.1 (%Outer.d2f) = out_param call_param0 // CHECK:STDOUT: %return: ref @Inner.B.%Outer.loc7_22.1 (%Outer.d2f) = return_slot %return.param // CHECK:STDOUT: } // CHECK:STDOUT: %Inner.C.decl: @Inner.%Inner.C.type (%Inner.C.type.912) = fn_decl @Inner.C [symbolic = @Inner.%Inner.C (constants.%Inner.C.125)] { // CHECK:STDOUT: %return.patt: @Inner.C.%pattern_type (%pattern_type.81e) = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: @Inner.C.%pattern_type (%pattern_type.81e) = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %.loc10_15: @Inner.C.%Inner.type (%Inner.type.e0d) = specific_constant @Outer.%Inner.decl, @Outer(constants.%T) [symbolic = %Inner.generic (constants.%Inner.generic.ada)] // CHECK:STDOUT: %Inner.ref: @Inner.C.%Inner.type (%Inner.type.e0d) = name_ref Inner, %.loc10_15 [symbolic = %Inner.generic (constants.%Inner.generic.ada)] // CHECK:STDOUT: %T.ref: type = name_ref T, @Outer.%T.loc2_13.2 [symbolic = %T (constants.%T)] // CHECK:STDOUT: %Inner.loc10_22.2: type = class_type @Inner, @Inner(constants.%T, constants.%T) [symbolic = %Inner.loc10_22.1 (constants.%Inner.ddc)] // CHECK:STDOUT: %.loc10_22.2: Core.Form = init_form %Inner.loc10_22.2 [symbolic = %.loc10_22.1 (constants.%.fdd)] // CHECK:STDOUT: %return.param: ref @Inner.C.%Inner.loc10_22.1 (%Inner.ddc) = out_param call_param0 // CHECK:STDOUT: %return: ref @Inner.C.%Inner.loc10_22.1 (%Inner.ddc) = return_slot %return.param // CHECK:STDOUT: } // CHECK:STDOUT: %Inner.D.decl: @Inner.%Inner.D.type (%Inner.D.type.cd0) = fn_decl @Inner.D [symbolic = @Inner.%Inner.D (constants.%Inner.D.147)] { // CHECK:STDOUT: %return.patt: @Inner.D.%pattern_type (%pattern_type.0d1) = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: @Inner.D.%pattern_type (%pattern_type.0d1) = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %.loc13_15: @Inner.D.%Inner.type (%Inner.type.e0d) = specific_constant @Outer.%Inner.decl, @Outer(constants.%T) [symbolic = %Inner.generic (constants.%Inner.generic.ada)] // CHECK:STDOUT: %Inner.ref: @Inner.D.%Inner.type (%Inner.type.e0d) = name_ref Inner, %.loc13_15 [symbolic = %Inner.generic (constants.%Inner.generic.ada)] // CHECK:STDOUT: %U.ref: type = name_ref U, @Inner.%U.loc3_15.2 [symbolic = %U (constants.%U)] // CHECK:STDOUT: %Inner.loc13_22.2: type = class_type @Inner, @Inner(constants.%T, constants.%U) [symbolic = %Inner.loc13_22.1 (constants.%Inner.e21)] // CHECK:STDOUT: %.loc13_22.2: Core.Form = init_form %Inner.loc13_22.2 [symbolic = %.loc13_22.1 (constants.%.223)] // CHECK:STDOUT: %return.param: ref @Inner.D.%Inner.loc13_22.1 (%Inner.e21) = out_param call_param0 // CHECK:STDOUT: %return: ref @Inner.D.%Inner.loc13_22.1 (%Inner.e21) = return_slot %return.param // CHECK:STDOUT: } // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%Inner.e21 // CHECK:STDOUT: .Outer = // CHECK:STDOUT: .T = // CHECK:STDOUT: .A = %Inner.A.decl // CHECK:STDOUT: .U = // CHECK:STDOUT: .B = %Inner.B.decl // CHECK:STDOUT: .Inner = // CHECK:STDOUT: .C = %Inner.C.decl // CHECK:STDOUT: .D = %Inner.D.decl // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic fn @Inner.A(@Outer.%T.loc2_13.2: type, @Inner.%U.loc3_15.2: type) { // CHECK:STDOUT: %T: type = symbolic_binding T, 0 [symbolic = %T (constants.%T)] // CHECK:STDOUT: %Outer.loc4_22.1: type = class_type @Outer, @Outer(%T) [symbolic = %Outer.loc4_22.1 (constants.%Outer.387)] // CHECK:STDOUT: %.loc4_22.1: Core.Form = init_form %Outer.loc4_22.1 [symbolic = %.loc4_22.1 (constants.%.b11)] // CHECK:STDOUT: %pattern_type: type = pattern_type %Outer.loc4_22.1 [symbolic = %pattern_type (constants.%pattern_type.130)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete: = require_complete_type %Outer.loc4_22.1 [symbolic = %require_complete (constants.%require_complete.448)] // CHECK:STDOUT: %Outer.val: @Inner.A.%Outer.loc4_22.1 (%Outer.387) = struct_value () [symbolic = %Outer.val (constants.%Outer.val.ca1)] // CHECK:STDOUT: // CHECK:STDOUT: fn() -> out %return.param: @Inner.A.%Outer.loc4_22.1 (%Outer.387) { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %.loc5_15.1: %empty_struct_type = struct_literal () [concrete = constants.%empty_struct] // CHECK:STDOUT: %.loc5_15.2: init @Inner.A.%Outer.loc4_22.1 (%Outer.387) to %return.param = class_init () [symbolic = %Outer.val (constants.%Outer.val.ca1)] // CHECK:STDOUT: %.loc5_16: init @Inner.A.%Outer.loc4_22.1 (%Outer.387) = converted %.loc5_15.1, %.loc5_15.2 [symbolic = %Outer.val (constants.%Outer.val.ca1)] // CHECK:STDOUT: return %.loc5_16 to %return.param // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic fn @Inner.B(@Outer.%T.loc2_13.2: type, @Inner.%U.loc3_15.2: type) { // CHECK:STDOUT: %U: type = symbolic_binding U, 1 [symbolic = %U (constants.%U)] // CHECK:STDOUT: %Outer.loc7_22.1: type = class_type @Outer, @Outer(%U) [symbolic = %Outer.loc7_22.1 (constants.%Outer.d2f)] // CHECK:STDOUT: %.loc7_22.1: Core.Form = init_form %Outer.loc7_22.1 [symbolic = %.loc7_22.1 (constants.%.a87)] // CHECK:STDOUT: %pattern_type: type = pattern_type %Outer.loc7_22.1 [symbolic = %pattern_type (constants.%pattern_type.760)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete: = require_complete_type %Outer.loc7_22.1 [symbolic = %require_complete (constants.%require_complete.ade)] // CHECK:STDOUT: %Outer.val: @Inner.B.%Outer.loc7_22.1 (%Outer.d2f) = struct_value () [symbolic = %Outer.val (constants.%Outer.val.756)] // CHECK:STDOUT: // CHECK:STDOUT: fn() -> out %return.param: @Inner.B.%Outer.loc7_22.1 (%Outer.d2f) { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %.loc8_15.1: %empty_struct_type = struct_literal () [concrete = constants.%empty_struct] // CHECK:STDOUT: %.loc8_15.2: init @Inner.B.%Outer.loc7_22.1 (%Outer.d2f) to %return.param = class_init () [symbolic = %Outer.val (constants.%Outer.val.756)] // CHECK:STDOUT: %.loc8_16: init @Inner.B.%Outer.loc7_22.1 (%Outer.d2f) = converted %.loc8_15.1, %.loc8_15.2 [symbolic = %Outer.val (constants.%Outer.val.756)] // CHECK:STDOUT: return %.loc8_16 to %return.param // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic fn @Inner.C(@Outer.%T.loc2_13.2: type, @Inner.%U.loc3_15.2: type) { // CHECK:STDOUT: %T: type = symbolic_binding T, 0 [symbolic = %T (constants.%T)] // CHECK:STDOUT: %Inner.type: type = generic_class_type @Inner, @Outer(%T) [symbolic = %Inner.type (constants.%Inner.type.e0d)] // CHECK:STDOUT: %Inner.generic: @Inner.C.%Inner.type (%Inner.type.e0d) = struct_value () [symbolic = %Inner.generic (constants.%Inner.generic.ada)] // CHECK:STDOUT: %Inner.loc10_22.1: type = class_type @Inner, @Inner(%T, %T) [symbolic = %Inner.loc10_22.1 (constants.%Inner.ddc)] // CHECK:STDOUT: %.loc10_22.1: Core.Form = init_form %Inner.loc10_22.1 [symbolic = %.loc10_22.1 (constants.%.fdd)] // CHECK:STDOUT: %pattern_type: type = pattern_type %Inner.loc10_22.1 [symbolic = %pattern_type (constants.%pattern_type.81e)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete: = require_complete_type %Inner.loc10_22.1 [symbolic = %require_complete (constants.%require_complete.de9)] // CHECK:STDOUT: %Inner.val: @Inner.C.%Inner.loc10_22.1 (%Inner.ddc) = struct_value () [symbolic = %Inner.val (constants.%Inner.val.240)] // CHECK:STDOUT: // CHECK:STDOUT: fn() -> out %return.param: @Inner.C.%Inner.loc10_22.1 (%Inner.ddc) { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %.loc11_15.1: %empty_struct_type = struct_literal () [concrete = constants.%empty_struct] // CHECK:STDOUT: %.loc11_15.2: init @Inner.C.%Inner.loc10_22.1 (%Inner.ddc) to %return.param = class_init () [symbolic = %Inner.val (constants.%Inner.val.240)] // CHECK:STDOUT: %.loc11_16: init @Inner.C.%Inner.loc10_22.1 (%Inner.ddc) = converted %.loc11_15.1, %.loc11_15.2 [symbolic = %Inner.val (constants.%Inner.val.240)] // CHECK:STDOUT: return %.loc11_16 to %return.param // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic fn @Inner.D(@Outer.%T.loc2_13.2: type, @Inner.%U.loc3_15.2: type) { // CHECK:STDOUT: %T: type = symbolic_binding T, 0 [symbolic = %T (constants.%T)] // CHECK:STDOUT: %Inner.type: type = generic_class_type @Inner, @Outer(%T) [symbolic = %Inner.type (constants.%Inner.type.e0d)] // CHECK:STDOUT: %Inner.generic: @Inner.D.%Inner.type (%Inner.type.e0d) = struct_value () [symbolic = %Inner.generic (constants.%Inner.generic.ada)] // CHECK:STDOUT: %U: type = symbolic_binding U, 1 [symbolic = %U (constants.%U)] // CHECK:STDOUT: %Inner.loc13_22.1: type = class_type @Inner, @Inner(%T, %U) [symbolic = %Inner.loc13_22.1 (constants.%Inner.e21)] // CHECK:STDOUT: %.loc13_22.1: Core.Form = init_form %Inner.loc13_22.1 [symbolic = %.loc13_22.1 (constants.%.223)] // CHECK:STDOUT: %pattern_type: type = pattern_type %Inner.loc13_22.1 [symbolic = %pattern_type (constants.%pattern_type.0d1)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete: = require_complete_type %Inner.loc13_22.1 [symbolic = %require_complete (constants.%require_complete.41c)] // CHECK:STDOUT: %Inner.val: @Inner.D.%Inner.loc13_22.1 (%Inner.e21) = struct_value () [symbolic = %Inner.val (constants.%Inner.val.43a)] // CHECK:STDOUT: // CHECK:STDOUT: fn() -> out %return.param: @Inner.D.%Inner.loc13_22.1 (%Inner.e21) { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %.loc14_15.1: %empty_struct_type = struct_literal () [concrete = constants.%empty_struct] // CHECK:STDOUT: %.loc14_15.2: init @Inner.D.%Inner.loc13_22.1 (%Inner.e21) to %return.param = class_init () [symbolic = %Inner.val (constants.%Inner.val.43a)] // CHECK:STDOUT: %.loc14_16: init @Inner.D.%Inner.loc13_22.1 (%Inner.e21) = converted %.loc14_15.1, %.loc14_15.2 [symbolic = %Inner.val (constants.%Inner.val.43a)] // CHECK:STDOUT: return %.loc14_16 to %return.param // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @Outer(constants.%T) { // CHECK:STDOUT: %T.loc2_13.1 => constants.%T // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %Inner.type => constants.%Inner.type.e0d // CHECK:STDOUT: %Inner.generic => constants.%Inner.generic.ada // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @Inner(constants.%T, constants.%U) { // CHECK:STDOUT: %U.loc3_15.1 => constants.%U // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %T => constants.%T // CHECK:STDOUT: %Inner.A.type => constants.%Inner.A.type.c2f // CHECK:STDOUT: %Inner.A => constants.%Inner.A.07b // CHECK:STDOUT: %Inner.B.type => constants.%Inner.B.type.f42 // CHECK:STDOUT: %Inner.B => constants.%Inner.B.b14 // CHECK:STDOUT: %Inner.C.type => constants.%Inner.C.type.912 // CHECK:STDOUT: %Inner.C => constants.%Inner.C.125 // CHECK:STDOUT: %Inner.D.type => constants.%Inner.D.type.cd0 // CHECK:STDOUT: %Inner.D => constants.%Inner.D.147 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @Inner.A(constants.%T, constants.%U) { // CHECK:STDOUT: %T => constants.%T // CHECK:STDOUT: %Outer.loc4_22.1 => constants.%Outer.387 // CHECK:STDOUT: %.loc4_22.1 => constants.%.b11 // CHECK:STDOUT: %pattern_type => constants.%pattern_type.130 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @Outer(constants.%U) { // CHECK:STDOUT: %T.loc2_13.1 => constants.%U // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %Inner.type => constants.%Inner.type.26a // CHECK:STDOUT: %Inner.generic => constants.%Inner.generic.ddc // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @Inner.B(constants.%T, constants.%U) { // CHECK:STDOUT: %U => constants.%U // CHECK:STDOUT: %Outer.loc7_22.1 => constants.%Outer.d2f // CHECK:STDOUT: %.loc7_22.1 => constants.%.a87 // CHECK:STDOUT: %pattern_type => constants.%pattern_type.760 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @Inner(constants.%T, constants.%T) { // CHECK:STDOUT: %U.loc3_15.1 => constants.%T // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %T => constants.%T // CHECK:STDOUT: %Inner.A.type => constants.%Inner.A.type.e74 // CHECK:STDOUT: %Inner.A => constants.%Inner.A.d94 // CHECK:STDOUT: %Inner.B.type => constants.%Inner.B.type.539 // CHECK:STDOUT: %Inner.B => constants.%Inner.B.455 // CHECK:STDOUT: %Inner.C.type => constants.%Inner.C.type.4b3 // CHECK:STDOUT: %Inner.C => constants.%Inner.C.f04 // CHECK:STDOUT: %Inner.D.type => constants.%Inner.D.type.d17 // CHECK:STDOUT: %Inner.D => constants.%Inner.D.c1c // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @Inner.C(constants.%T, constants.%U) { // CHECK:STDOUT: %T => constants.%T // CHECK:STDOUT: %Inner.type => constants.%Inner.type.e0d // CHECK:STDOUT: %Inner.generic => constants.%Inner.generic.ada // CHECK:STDOUT: %Inner.loc10_22.1 => constants.%Inner.ddc // CHECK:STDOUT: %.loc10_22.1 => constants.%.fdd // CHECK:STDOUT: %pattern_type => constants.%pattern_type.81e // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @Inner.D(constants.%T, constants.%U) { // CHECK:STDOUT: %T => constants.%T // CHECK:STDOUT: %Inner.type => constants.%Inner.type.e0d // CHECK:STDOUT: %Inner.generic => constants.%Inner.generic.ada // CHECK:STDOUT: %U => constants.%U // CHECK:STDOUT: %Inner.loc13_22.1 => constants.%Inner.e21 // CHECK:STDOUT: %.loc13_22.1 => constants.%.223 // CHECK:STDOUT: %pattern_type => constants.%pattern_type.0d1 // CHECK:STDOUT: } // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/class/generic/complete_in_conversion.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/int.carbon // TODO: Add ranges and switch to "--dump-sem-ir-ranges=only". // EXTRA-ARGS: --dump-sem-ir-ranges=if-present // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/class/generic/complete_in_conversion.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/class/generic/complete_in_conversion.carbon // --- fail_derived_to_base.carbon fn Int(N: Core.IntLiteral()) -> type = "int.make_type_signed"; base class B {} class A(N:! i32) { extend base: B; var n: Int(N); } fn F(a: A(0)*) { // CHECK:STDERR: fail_derived_to_base.carbon:[[@LINE+7]]:22: error: unable to monomorphize specific `A(0)` [ResolvingSpecificHere] // CHECK:STDERR: let unused b: B* = a; // CHECK:STDERR: ^ // CHECK:STDERR: fail_derived_to_base.carbon:[[@LINE-7]]:10: note: integer type width of 0 is not positive [IntWidthNotPositive] // CHECK:STDERR: var n: Int(N); // CHECK:STDERR: ^~~~~~ // CHECK:STDERR: let unused b: B* = a; } // CHECK:STDOUT: --- fail_derived_to_base.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %IntLiteral.type: type = fn_type @IntLiteral [concrete] // CHECK:STDOUT: %IntLiteral: %IntLiteral.type = struct_value () [concrete] // CHECK:STDOUT: %pattern_type.98f: type = pattern_type type [concrete] // CHECK:STDOUT: %.805: Core.Form = init_form type [concrete] // CHECK:STDOUT: %pattern_type.dc0: type = pattern_type Core.IntLiteral [concrete] // CHECK:STDOUT: %Int.type.b3e: type = fn_type @Int.loc2 [concrete] // CHECK:STDOUT: %Int.d6d: %Int.type.b3e = struct_value () [concrete] // CHECK:STDOUT: %B: type = class_type @B [concrete] // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %complete_type.357: = complete_type_witness %empty_struct_type [concrete] // CHECK:STDOUT: %type: type = facet_type [concrete] // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self] // CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [concrete] // CHECK:STDOUT: %Int.type.878: type = generic_class_type @Int.1 [concrete] // CHECK:STDOUT: %Int.generic: %Int.type.878 = struct_value () [concrete] // CHECK:STDOUT: %i32: type = class_type @Int.1, @Int.1(%int_32) [concrete] // CHECK:STDOUT: %pattern_type.7ce: type = pattern_type %i32 [concrete] // CHECK:STDOUT: %N.5de: %i32 = symbolic_binding N, 0 [symbolic] // CHECK:STDOUT: %A.type: type = generic_class_type @A [concrete] // CHECK:STDOUT: %A.generic: %A.type = struct_value () [concrete] // CHECK:STDOUT: %A.54d: type = class_type @A, @A(%N.5de) [symbolic] // CHECK:STDOUT: %A.elem.ade: type = unbound_element_type %A.54d, %B [symbolic] // CHECK:STDOUT: %ImplicitAs.type.cc7: type = generic_interface_type @ImplicitAs [concrete] // CHECK:STDOUT: %ImplicitAs.generic: %ImplicitAs.type.cc7 = struct_value () [concrete] // CHECK:STDOUT: %ImplicitAs.type.139: type = facet_type <@ImplicitAs, @ImplicitAs(Core.IntLiteral)> [concrete] // CHECK:STDOUT: %To: Core.IntLiteral = symbolic_binding To, 0 [symbolic] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.4e6: type = fn_type @Core.IntLiteral.as.ImplicitAs.impl.Convert, @Core.IntLiteral.as.ImplicitAs.impl(%To) [symbolic] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.3c2: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.4e6 = struct_value () [symbolic] // CHECK:STDOUT: %From: Core.IntLiteral = symbolic_binding From, 0 [symbolic] // CHECK:STDOUT: %Int.as.ImplicitAs.impl.Convert.type.2ed: type = fn_type @Int.as.ImplicitAs.impl.Convert, @Int.as.ImplicitAs.impl(%From) [symbolic] // CHECK:STDOUT: %Int.as.ImplicitAs.impl.Convert.d29: %Int.as.ImplicitAs.impl.Convert.type.2ed = struct_value () [symbolic] // CHECK:STDOUT: %ImplicitAs.impl_witness.640: = impl_witness imports.%ImplicitAs.impl_witness_table.ea2, @Int.as.ImplicitAs.impl(%int_32) [concrete] // CHECK:STDOUT: %Int.as.ImplicitAs.impl.Convert.type.240: type = fn_type @Int.as.ImplicitAs.impl.Convert, @Int.as.ImplicitAs.impl(%int_32) [concrete] // CHECK:STDOUT: %Int.as.ImplicitAs.impl.Convert.dd4: %Int.as.ImplicitAs.impl.Convert.type.240 = struct_value () [concrete] // CHECK:STDOUT: %ImplicitAs.facet.290: %ImplicitAs.type.139 = facet_value %i32, (%ImplicitAs.impl_witness.640) [concrete] // CHECK:STDOUT: %ImplicitAs.WithSelf.Convert.type.462: type = fn_type @ImplicitAs.WithSelf.Convert, @ImplicitAs.WithSelf(Core.IntLiteral, %ImplicitAs.facet.290) [concrete] // CHECK:STDOUT: %.0a7: type = fn_type_with_self_type %ImplicitAs.WithSelf.Convert.type.462, %ImplicitAs.facet.290 [concrete] // CHECK:STDOUT: %Int.as.ImplicitAs.impl.Convert.bound.d32: = bound_method %N.5de, %Int.as.ImplicitAs.impl.Convert.dd4 [symbolic] // CHECK:STDOUT: %Int.as.ImplicitAs.impl.Convert.specific_fn: = specific_function %Int.as.ImplicitAs.impl.Convert.dd4, @Int.as.ImplicitAs.impl.Convert(%int_32) [concrete] // CHECK:STDOUT: %bound_method.d76: = bound_method %N.5de, %Int.as.ImplicitAs.impl.Convert.specific_fn [symbolic] // CHECK:STDOUT: %Int.as.ImplicitAs.impl.Convert.call: init Core.IntLiteral = call %bound_method.d76(%N.5de) [symbolic] // CHECK:STDOUT: %iN.builtin.f1b: type = int_type signed, %Int.as.ImplicitAs.impl.Convert.call [symbolic] // CHECK:STDOUT: %require_complete.c9d: = require_complete_type %iN.builtin.f1b [symbolic] // CHECK:STDOUT: %A.elem.a53: type = unbound_element_type %A.54d, %iN.builtin.f1b [symbolic] // CHECK:STDOUT: %struct_type.base.n: type = struct_type {.base: %B, .n: %iN.builtin.f1b} [symbolic] // CHECK:STDOUT: %complete_type.8c7: = complete_type_witness %struct_type.base.n [symbolic] // CHECK:STDOUT: %int_0.5c6: Core.IntLiteral = int_value 0 [concrete] // CHECK:STDOUT: %ImplicitAs.type.e8c: type = facet_type <@ImplicitAs, @ImplicitAs(%i32)> [concrete] // CHECK:STDOUT: %ImplicitAs.impl_witness.6bc: = impl_witness imports.%ImplicitAs.impl_witness_table.74f, @Core.IntLiteral.as.ImplicitAs.impl(%int_32) [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.e0d: type = fn_type @Core.IntLiteral.as.ImplicitAs.impl.Convert, @Core.IntLiteral.as.ImplicitAs.impl(%int_32) [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.e0d = struct_value () [concrete] // CHECK:STDOUT: %ImplicitAs.facet.b94: %ImplicitAs.type.e8c = facet_value Core.IntLiteral, (%ImplicitAs.impl_witness.6bc) [concrete] // CHECK:STDOUT: %ImplicitAs.WithSelf.Convert.type.b37: type = fn_type @ImplicitAs.WithSelf.Convert, @ImplicitAs.WithSelf(%i32, %ImplicitAs.facet.b94) [concrete] // CHECK:STDOUT: %.545: type = fn_type_with_self_type %ImplicitAs.WithSelf.Convert.type.b37, %ImplicitAs.facet.b94 [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.bound: = bound_method %int_0.5c6, %Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5 [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn: = specific_function %Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5, @Core.IntLiteral.as.ImplicitAs.impl.Convert(%int_32) [concrete] // CHECK:STDOUT: %bound_method.d2e: = bound_method %int_0.5c6, %Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn [concrete] // CHECK:STDOUT: %int_0.6a9: %i32 = int_value 0 [concrete] // CHECK:STDOUT: %A.dc6: type = class_type @A, @A(%int_0.6a9) [concrete] // CHECK:STDOUT: %ptr.0e2: type = ptr_type %A.dc6 [concrete] // CHECK:STDOUT: %pattern_type.f32: type = pattern_type %ptr.0e2 [concrete] // CHECK:STDOUT: %F.type: type = fn_type @F [concrete] // CHECK:STDOUT: %F: %F.type = struct_value () [concrete] // CHECK:STDOUT: %ptr.27c: type = ptr_type %B [concrete] // CHECK:STDOUT: %pattern_type.191: type = pattern_type %ptr.27c [concrete] // CHECK:STDOUT: %A.elem.665: type = unbound_element_type %A.dc6, %B [concrete] // CHECK:STDOUT: %Int.as.ImplicitAs.impl.Convert.bound.564: = bound_method %int_0.6a9, %Int.as.ImplicitAs.impl.Convert.dd4 [concrete] // CHECK:STDOUT: %bound_method.77d: = bound_method %int_0.6a9, %Int.as.ImplicitAs.impl.Convert.specific_fn [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: .IntLiteral = %Core.IntLiteral // CHECK:STDOUT: .Int = %Core.Int // CHECK:STDOUT: .ImplicitAs = %Core.ImplicitAs // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: %Core.IntLiteral: %IntLiteral.type = import_ref Core//prelude/parts/int_literal, IntLiteral, loaded [concrete = constants.%IntLiteral] // CHECK:STDOUT: %Core.Int: %Int.type.878 = import_ref Core//prelude/parts/int, Int, loaded [concrete = constants.%Int.generic] // CHECK:STDOUT: %Core.ImplicitAs: %ImplicitAs.type.cc7 = import_ref Core//prelude/parts/as, ImplicitAs, loaded [concrete = constants.%ImplicitAs.generic] // CHECK:STDOUT: %Core.import_ref.42d: @Core.IntLiteral.as.ImplicitAs.impl.%Core.IntLiteral.as.ImplicitAs.impl.Convert.type (%Core.IntLiteral.as.ImplicitAs.impl.Convert.type.4e6) = import_ref Core//prelude/parts/int, loc{{\d+_\d+}}, loaded [symbolic = @Core.IntLiteral.as.ImplicitAs.impl.%Core.IntLiteral.as.ImplicitAs.impl.Convert (constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.3c2)] // CHECK:STDOUT: %ImplicitAs.impl_witness_table.74f = impl_witness_table (%Core.import_ref.42d), @Core.IntLiteral.as.ImplicitAs.impl [concrete] // CHECK:STDOUT: %Core.import_ref.0bc: @Int.as.ImplicitAs.impl.%Int.as.ImplicitAs.impl.Convert.type (%Int.as.ImplicitAs.impl.Convert.type.2ed) = import_ref Core//prelude/parts/int, loc{{\d+_\d+}}, loaded [symbolic = @Int.as.ImplicitAs.impl.%Int.as.ImplicitAs.impl.Convert (constants.%Int.as.ImplicitAs.impl.Convert.d29)] // CHECK:STDOUT: %ImplicitAs.impl_witness_table.ea2 = impl_witness_table (%Core.import_ref.0bc), @Int.as.ImplicitAs.impl [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .Int = %Int.decl // CHECK:STDOUT: .B = %B.decl // CHECK:STDOUT: .A = %A.decl // CHECK:STDOUT: .F = %F.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %Int.decl: %Int.type.b3e = fn_decl @Int.loc2 [concrete = constants.%Int.d6d] { // CHECK:STDOUT: %N.patt: %pattern_type.dc0 = value_binding_pattern N [concrete] // CHECK:STDOUT: %N.param_patt: %pattern_type.dc0 = value_param_pattern %N.patt [concrete] // CHECK:STDOUT: %return.patt: %pattern_type.98f = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: %pattern_type.98f = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %.loc2_33.1: type = type_literal type [concrete = type] // CHECK:STDOUT: %.loc2_33.2: Core.Form = init_form %.loc2_33.1 [concrete = constants.%.805] // CHECK:STDOUT: %N.param: Core.IntLiteral = value_param call_param0 // CHECK:STDOUT: %.loc2_27.1: type = splice_block %.loc2_27.3 [concrete = Core.IntLiteral] { // CHECK:STDOUT: %Core.ref: = name_ref Core, imports.%Core [concrete = imports.%Core] // CHECK:STDOUT: %IntLiteral.ref: %IntLiteral.type = name_ref IntLiteral, imports.%Core.IntLiteral [concrete = constants.%IntLiteral] // CHECK:STDOUT: %IntLiteral.call: init type = call %IntLiteral.ref() [concrete = Core.IntLiteral] // CHECK:STDOUT: %.loc2_27.2: type = value_of_initializer %IntLiteral.call [concrete = Core.IntLiteral] // CHECK:STDOUT: %.loc2_27.3: type = converted %IntLiteral.call, %.loc2_27.2 [concrete = Core.IntLiteral] // CHECK:STDOUT: } // CHECK:STDOUT: %N: Core.IntLiteral = value_binding N, %N.param // CHECK:STDOUT: %return.param: ref type = out_param call_param1 // CHECK:STDOUT: %return: ref type = return_slot %return.param // CHECK:STDOUT: } // CHECK:STDOUT: %B.decl: type = class_decl @B [concrete = constants.%B] {} {} // CHECK:STDOUT: %A.decl: %A.type = class_decl @A [concrete = constants.%A.generic] { // CHECK:STDOUT: %N.patt: %pattern_type.7ce = symbolic_binding_pattern N, 0 [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %.loc6: type = splice_block %i32 [concrete = constants.%i32] { // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %i32: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: } // CHECK:STDOUT: %N.loc6_9.2: %i32 = symbolic_binding N, 0 [symbolic = %N.loc6_9.1 (constants.%N.5de)] // CHECK:STDOUT: } // CHECK:STDOUT: %F.decl: %F.type = fn_decl @F [concrete = constants.%F] { // CHECK:STDOUT: %a.patt: %pattern_type.f32 = value_binding_pattern a [concrete] // CHECK:STDOUT: %a.param_patt: %pattern_type.f32 = value_param_pattern %a.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %a.param: %ptr.0e2 = value_param call_param0 // CHECK:STDOUT: %.loc12_13: type = splice_block %ptr.loc12 [concrete = constants.%ptr.0e2] { // CHECK:STDOUT: %A.ref: %A.type = name_ref A, file.%A.decl [concrete = constants.%A.generic] // CHECK:STDOUT: %int_0: Core.IntLiteral = int_value 0 [concrete = constants.%int_0.5c6] // CHECK:STDOUT: %impl.elem0: %.545 = impl_witness_access constants.%ImplicitAs.impl_witness.6bc, element0 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5] // CHECK:STDOUT: %bound_method.loc12_12.1: = bound_method %int_0, %impl.elem0 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.bound] // CHECK:STDOUT: %specific_fn: = specific_function %impl.elem0, @Core.IntLiteral.as.ImplicitAs.impl.Convert(constants.%int_32) [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn] // CHECK:STDOUT: %bound_method.loc12_12.2: = bound_method %int_0, %specific_fn [concrete = constants.%bound_method.d2e] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.call: init %i32 = call %bound_method.loc12_12.2(%int_0) [concrete = constants.%int_0.6a9] // CHECK:STDOUT: %.loc12_12.1: %i32 = value_of_initializer %Core.IntLiteral.as.ImplicitAs.impl.Convert.call [concrete = constants.%int_0.6a9] // CHECK:STDOUT: %.loc12_12.2: %i32 = converted %int_0, %.loc12_12.1 [concrete = constants.%int_0.6a9] // CHECK:STDOUT: %A: type = class_type @A, @A(constants.%int_0.6a9) [concrete = constants.%A.dc6] // CHECK:STDOUT: %ptr.loc12: type = ptr_type %A [concrete = constants.%ptr.0e2] // CHECK:STDOUT: } // CHECK:STDOUT: %a: %ptr.0e2 = value_binding a, %a.param // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @B { // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type.357] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%B // CHECK:STDOUT: .Int = // CHECK:STDOUT: .N = // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic class @A(%N.loc6_9.2: %i32) { // CHECK:STDOUT: %N.loc6_9.1: %i32 = symbolic_binding N, 0 [symbolic = %N.loc6_9.1 (constants.%N.5de)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %A: type = class_type @A, @A(%N.loc6_9.1) [symbolic = %A (constants.%A.54d)] // CHECK:STDOUT: %A.elem.loc7: type = unbound_element_type %A, constants.%B [symbolic = %A.elem.loc7 (constants.%A.elem.ade)] // CHECK:STDOUT: %Int.as.ImplicitAs.impl.Convert.bound: = bound_method %N.loc6_9.1, constants.%Int.as.ImplicitAs.impl.Convert.dd4 [symbolic = %Int.as.ImplicitAs.impl.Convert.bound (constants.%Int.as.ImplicitAs.impl.Convert.bound.d32)] // CHECK:STDOUT: %bound_method.loc9_14.3: = bound_method %N.loc6_9.1, constants.%Int.as.ImplicitAs.impl.Convert.specific_fn [symbolic = %bound_method.loc9_14.3 (constants.%bound_method.d76)] // CHECK:STDOUT: %Int.as.ImplicitAs.impl.Convert.call.loc9_14.2: init Core.IntLiteral = call %bound_method.loc9_14.3(%N.loc6_9.1) [symbolic = %Int.as.ImplicitAs.impl.Convert.call.loc9_14.2 (constants.%Int.as.ImplicitAs.impl.Convert.call)] // CHECK:STDOUT: %iN.builtin: type = int_type signed, %Int.as.ImplicitAs.impl.Convert.call.loc9_14.2 [symbolic = %iN.builtin (constants.%iN.builtin.f1b)] // CHECK:STDOUT: %require_complete: = require_complete_type %iN.builtin [symbolic = %require_complete (constants.%require_complete.c9d)] // CHECK:STDOUT: %A.elem.loc9: type = unbound_element_type %A, %iN.builtin [symbolic = %A.elem.loc9 (constants.%A.elem.a53)] // CHECK:STDOUT: %struct_type.base.n: type = struct_type {.base: %B, .n: @A.%iN.builtin (%iN.builtin.f1b)} [symbolic = %struct_type.base.n (constants.%struct_type.base.n)] // CHECK:STDOUT: %complete_type.loc10_1.2: = complete_type_witness %struct_type.base.n [symbolic = %complete_type.loc10_1.2 (constants.%complete_type.8c7)] // CHECK:STDOUT: // CHECK:STDOUT: class { // CHECK:STDOUT: %B.ref: type = name_ref B, file.%B.decl [concrete = constants.%B] // CHECK:STDOUT: %.loc7: @A.%A.elem.loc7 (%A.elem.ade) = base_decl %B.ref, element0 [concrete] // CHECK:STDOUT: %Int.ref: %Int.type.b3e = name_ref Int, file.%Int.decl [concrete = constants.%Int.d6d] // CHECK:STDOUT: %N.ref: %i32 = name_ref N, %N.loc6_9.2 [symbolic = %N.loc6_9.1 (constants.%N.5de)] // CHECK:STDOUT: %impl.elem0: %.0a7 = impl_witness_access constants.%ImplicitAs.impl_witness.640, element0 [concrete = constants.%Int.as.ImplicitAs.impl.Convert.dd4] // CHECK:STDOUT: %bound_method.loc9_14.1: = bound_method %N.ref, %impl.elem0 [symbolic = %Int.as.ImplicitAs.impl.Convert.bound (constants.%Int.as.ImplicitAs.impl.Convert.bound.d32)] // CHECK:STDOUT: %specific_fn: = specific_function %impl.elem0, @Int.as.ImplicitAs.impl.Convert(constants.%int_32) [concrete = constants.%Int.as.ImplicitAs.impl.Convert.specific_fn] // CHECK:STDOUT: %bound_method.loc9_14.2: = bound_method %N.ref, %specific_fn [symbolic = %bound_method.loc9_14.3 (constants.%bound_method.d76)] // CHECK:STDOUT: %Int.as.ImplicitAs.impl.Convert.call.loc9_14.1: init Core.IntLiteral = call %bound_method.loc9_14.2(%N.ref) [symbolic = %Int.as.ImplicitAs.impl.Convert.call.loc9_14.2 (constants.%Int.as.ImplicitAs.impl.Convert.call)] // CHECK:STDOUT: %.loc9_14.1: Core.IntLiteral = value_of_initializer %Int.as.ImplicitAs.impl.Convert.call.loc9_14.1 [symbolic = %Int.as.ImplicitAs.impl.Convert.call.loc9_14.2 (constants.%Int.as.ImplicitAs.impl.Convert.call)] // CHECK:STDOUT: %.loc9_14.2: Core.IntLiteral = converted %N.ref, %.loc9_14.1 [symbolic = %Int.as.ImplicitAs.impl.Convert.call.loc9_14.2 (constants.%Int.as.ImplicitAs.impl.Convert.call)] // CHECK:STDOUT: %Int.call: init type = call %Int.ref(%.loc9_14.2) [symbolic = %iN.builtin (constants.%iN.builtin.f1b)] // CHECK:STDOUT: %.loc9_15.1: type = value_of_initializer %Int.call [symbolic = %iN.builtin (constants.%iN.builtin.f1b)] // CHECK:STDOUT: %.loc9_15.2: type = converted %Int.call, %.loc9_15.1 [symbolic = %iN.builtin (constants.%iN.builtin.f1b)] // CHECK:STDOUT: %.loc9_8: @A.%A.elem.loc9 (%A.elem.a53) = field_decl n, element1 [concrete] // CHECK:STDOUT: %complete_type.loc10_1.1: = complete_type_witness constants.%struct_type.base.n [symbolic = %complete_type.loc10_1.2 (constants.%complete_type.8c7)] // CHECK:STDOUT: complete_type_witness = %complete_type.loc10_1.1 // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%A.54d // CHECK:STDOUT: .B = // CHECK:STDOUT: .base = %.loc7 // CHECK:STDOUT: .Int = // CHECK:STDOUT: .N = // CHECK:STDOUT: .n = %.loc9_8 // CHECK:STDOUT: extend %B.ref // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @Int.loc2(%N.param: Core.IntLiteral) -> out %return.param: type = "int.make_type_signed"; // CHECK:STDOUT: // CHECK:STDOUT: fn @F(%a.param: %ptr.0e2) { // CHECK:STDOUT: !entry: // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %b.patt: %pattern_type.191 = value_binding_pattern b [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %a.ref: %ptr.0e2 = name_ref a, %a // CHECK:STDOUT: %.loc20_18: type = splice_block %ptr.loc20 [concrete = constants.%ptr.27c] { // CHECK:STDOUT: %B.ref: type = name_ref B, file.%B.decl [concrete = constants.%B] // CHECK:STDOUT: %ptr.loc20: type = ptr_type %B.ref [concrete = constants.%ptr.27c] // CHECK:STDOUT: } // CHECK:STDOUT: %.loc20_22.1: ref %A.dc6 = deref %a.ref // CHECK:STDOUT: %.loc20_22.2: ref %B = class_element_access %.loc20_22.1, element0 // CHECK:STDOUT: %addr: %ptr.27c = addr_of %.loc20_22.2 // CHECK:STDOUT: %.loc20_22.3: %ptr.27c = converted %a.ref, %addr // CHECK:STDOUT: %b: %ptr.27c = value_binding b, %.loc20_22.3 // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @A(constants.%N.5de) { // CHECK:STDOUT: %N.loc6_9.1 => constants.%N.5de // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @A(constants.%int_0.6a9) { // CHECK:STDOUT: %N.loc6_9.1 => constants.%int_0.6a9 // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %A => constants.%A.dc6 // CHECK:STDOUT: %A.elem.loc7 => constants.%A.elem.665 // CHECK:STDOUT: %Int.as.ImplicitAs.impl.Convert.bound => constants.%Int.as.ImplicitAs.impl.Convert.bound.564 // CHECK:STDOUT: %bound_method.loc9_14.3 => constants.%bound_method.77d // CHECK:STDOUT: %Int.as.ImplicitAs.impl.Convert.call.loc9_14.2 => constants.%int_0.5c6 // CHECK:STDOUT: %iN.builtin => // CHECK:STDOUT: %require_complete => // CHECK:STDOUT: %A.elem.loc9 => // CHECK:STDOUT: %struct_type.base.n => // CHECK:STDOUT: %complete_type.loc10_1.2 => // CHECK:STDOUT: } // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/class/generic/empty_params.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/convert.carbon // TODO: Add ranges and switch to "--dump-sem-ir-ranges=only". // EXTRA-ARGS: --dump-sem-ir-ranges=if-present // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/class/generic/empty_params.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/class/generic/empty_params.carbon class C[](); // CHECK:STDOUT: --- empty_params.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %C.type: type = generic_class_type @C [concrete] // CHECK:STDOUT: %C.generic: %C.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .C = %C.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %C.decl: %C.type = class_decl @C [concrete = constants.%C.generic] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @C; // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/class/generic/field.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/int.carbon // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/class/generic/field.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/class/generic/field.carbon // --- field.carbon library "[[@TEST_NAME]]"; //@dump-sem-ir-begin class Class(T:! type) { var x: T; } fn F(c: Class(i32)) -> i32 { return c.x; } fn G(T:! Core.Copy, c: Class(T)) -> T { return c.x; } fn H(U:! Core.Copy, c: Class(U)) -> U { return c.x; } //@dump-sem-ir-end // CHECK:STDOUT: --- field.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %pattern_type.98f: type = pattern_type type [concrete] // CHECK:STDOUT: %T.67d: type = symbolic_binding T, 0 [symbolic] // CHECK:STDOUT: %Class.type: type = generic_class_type @Class [concrete] // CHECK:STDOUT: %Class.generic: %Class.type = struct_value () [concrete] // CHECK:STDOUT: %Class.0db: type = class_type @Class, @Class(%T.67d) [symbolic] // CHECK:STDOUT: %require_complete.944: = require_complete_type %T.67d [symbolic] // CHECK:STDOUT: %Class.elem.fdf: type = unbound_element_type %Class.0db, %T.67d [symbolic] // CHECK:STDOUT: %struct_type.x.0c5: type = struct_type {.x: %T.67d} [symbolic] // CHECK:STDOUT: %complete_type.735: = complete_type_witness %struct_type.x.0c5 [symbolic] // CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [concrete] // CHECK:STDOUT: %Int.type: type = generic_class_type @Int [concrete] // CHECK:STDOUT: %Int.generic: %Int.type = struct_value () [concrete] // CHECK:STDOUT: %N: Core.IntLiteral = symbolic_binding N, 0 [symbolic] // CHECK:STDOUT: %i32: type = class_type @Int, @Int(%int_32) [concrete] // CHECK:STDOUT: %Class.805: type = class_type @Class, @Class(%i32) [concrete] // CHECK:STDOUT: %pattern_type.1c2: type = pattern_type %Class.805 [concrete] // CHECK:STDOUT: %.ff5: Core.Form = init_form %i32 [concrete] // CHECK:STDOUT: %pattern_type.7ce: type = pattern_type %i32 [concrete] // CHECK:STDOUT: %F.type: type = fn_type @F [concrete] // CHECK:STDOUT: %F: %F.type = struct_value () [concrete] // CHECK:STDOUT: %i32.builtin: type = int_type signed, %int_32 [concrete] // CHECK:STDOUT: %complete_type.f8a: = complete_type_witness %i32.builtin [concrete] // CHECK:STDOUT: %Class.elem.927: type = unbound_element_type %Class.805, %i32 [concrete] // CHECK:STDOUT: %struct_type.x.ed6: type = struct_type {.x: %i32} [concrete] // CHECK:STDOUT: %complete_type.1ec: = complete_type_witness %struct_type.x.ed6 [concrete] // CHECK:STDOUT: %Copy.type: type = facet_type <@Copy> [concrete] // CHECK:STDOUT: %Int.as.Copy.impl.Op.type.824: type = fn_type @Int.as.Copy.impl.Op, @Int.as.Copy.impl(%N) [symbolic] // CHECK:STDOUT: %Int.as.Copy.impl.Op.9b9: %Int.as.Copy.impl.Op.type.824 = struct_value () [symbolic] // CHECK:STDOUT: %T.035: %Copy.type = symbolic_binding T, 0 [symbolic] // CHECK:STDOUT: %T.binding.as_type: type = symbolic_binding_type T, 0, %T.035 [symbolic] // CHECK:STDOUT: %Copy.lookup_impl_witness.58dce0.1: = lookup_impl_witness %T.035, @Copy [symbolic] // CHECK:STDOUT: %Copy.WithSelf.Op.type.735e75.2: type = fn_type @Copy.WithSelf.Op, @Copy.WithSelf(%T.035) [symbolic] // CHECK:STDOUT: %.023143.1: type = fn_type_with_self_type %Copy.WithSelf.Op.type.735e75.2, %T.035 [symbolic] // CHECK:STDOUT: %impl.elem0.594c59.1: %.023143.1 = impl_witness_access %Copy.lookup_impl_witness.58dce0.1, element0 [symbolic] // CHECK:STDOUT: %specific_impl_fn.bdce5c.1: = specific_impl_function %impl.elem0.594c59.1, @Copy.WithSelf.Op(%T.035) [symbolic] // CHECK:STDOUT: %.076a48.2: Core.Form = init_form %T.binding.as_type [symbolic] // CHECK:STDOUT: %pattern_type.9b9f0c.2: type = pattern_type %T.binding.as_type [symbolic] // CHECK:STDOUT: %require_complete.67ca8d.1: = require_complete_type %T.binding.as_type [symbolic] // CHECK:STDOUT: %pattern_type.ce2: type = pattern_type %Copy.type [concrete] // CHECK:STDOUT: %Copy.impl_witness.f17: = impl_witness imports.%Copy.impl_witness_table.e76, @Int.as.Copy.impl(%int_32) [concrete] // CHECK:STDOUT: %Int.as.Copy.impl.Op.type.546: type = fn_type @Int.as.Copy.impl.Op, @Int.as.Copy.impl(%int_32) [concrete] // CHECK:STDOUT: %Int.as.Copy.impl.Op.664: %Int.as.Copy.impl.Op.type.546 = struct_value () [concrete] // CHECK:STDOUT: %Copy.facet: %Copy.type = facet_value %i32, (%Copy.impl_witness.f17) [concrete] // CHECK:STDOUT: %Copy.WithSelf.Op.type.081: type = fn_type @Copy.WithSelf.Op, @Copy.WithSelf(%Copy.facet) [concrete] // CHECK:STDOUT: %.8e2: type = fn_type_with_self_type %Copy.WithSelf.Op.type.081, %Copy.facet [concrete] // CHECK:STDOUT: %Int.as.Copy.impl.Op.specific_fn: = specific_function %Int.as.Copy.impl.Op.664, @Int.as.Copy.impl.Op(%int_32) [concrete] // CHECK:STDOUT: %Class.3168aa.1: type = class_type @Class, @Class(%T.binding.as_type) [symbolic] // CHECK:STDOUT: %pattern_type.c542f5.1: type = pattern_type %Class.3168aa.1 [symbolic] // CHECK:STDOUT: %G.type: type = fn_type @G [concrete] // CHECK:STDOUT: %G: %G.type = struct_value () [concrete] // CHECK:STDOUT: %Class.elem.7657d6.1: type = unbound_element_type %Class.3168aa.1, %T.binding.as_type [symbolic] // CHECK:STDOUT: %struct_type.x.8dcd6b.1: type = struct_type {.x: %T.binding.as_type} [symbolic] // CHECK:STDOUT: %complete_type.e78b36.1: = complete_type_witness %struct_type.x.8dcd6b.1 [symbolic] // CHECK:STDOUT: %require_complete.ae7bfa.1: = require_complete_type %Class.3168aa.1 [symbolic] // CHECK:STDOUT: %U.035: %Copy.type = symbolic_binding U, 0 [symbolic] // CHECK:STDOUT: %U.binding.as_type.14b: type = symbolic_binding_type U, 0, %U.035 [symbolic] // CHECK:STDOUT: %Class.3168aa.2: type = class_type @Class, @Class(%U.binding.as_type.14b) [symbolic] // CHECK:STDOUT: %pattern_type.c542f5.2: type = pattern_type %Class.3168aa.2 [symbolic] // CHECK:STDOUT: %.076a48.3: Core.Form = init_form %U.binding.as_type.14b [symbolic] // CHECK:STDOUT: %pattern_type.9b9f0c.3: type = pattern_type %U.binding.as_type.14b [symbolic] // CHECK:STDOUT: %H.type: type = fn_type @H [concrete] // CHECK:STDOUT: %H: %H.type = struct_value () [concrete] // CHECK:STDOUT: %require_complete.67ca8d.2: = require_complete_type %U.binding.as_type.14b [symbolic] // CHECK:STDOUT: %Class.elem.7657d6.2: type = unbound_element_type %Class.3168aa.2, %U.binding.as_type.14b [symbolic] // CHECK:STDOUT: %struct_type.x.8dcd6b.2: type = struct_type {.x: %U.binding.as_type.14b} [symbolic] // CHECK:STDOUT: %complete_type.e78b36.2: = complete_type_witness %struct_type.x.8dcd6b.2 [symbolic] // CHECK:STDOUT: %require_complete.ae7bfa.2: = require_complete_type %Class.3168aa.2 [symbolic] // CHECK:STDOUT: %Copy.lookup_impl_witness.58dce0.2: = lookup_impl_witness %U.035, @Copy [symbolic] // CHECK:STDOUT: %Copy.WithSelf.Op.type.735e75.3: type = fn_type @Copy.WithSelf.Op, @Copy.WithSelf(%U.035) [symbolic] // CHECK:STDOUT: %.023143.2: type = fn_type_with_self_type %Copy.WithSelf.Op.type.735e75.3, %U.035 [symbolic] // CHECK:STDOUT: %impl.elem0.594c59.2: %.023143.2 = impl_witness_access %Copy.lookup_impl_witness.58dce0.2, element0 [symbolic] // CHECK:STDOUT: %specific_impl_fn.bdce5c.2: = specific_impl_function %impl.elem0.594c59.2, @Copy.WithSelf.Op(%U.035) [symbolic] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: .Int = %Core.Int // CHECK:STDOUT: .Copy = %Core.Copy // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: %Core.Int: %Int.type = import_ref Core//prelude/parts/int, Int, loaded [concrete = constants.%Int.generic] // CHECK:STDOUT: %Core.Copy: type = import_ref Core//prelude/parts/copy, Copy, loaded [concrete = constants.%Copy.type] // CHECK:STDOUT: %Core.import_ref.18d: @Int.as.Copy.impl.%Int.as.Copy.impl.Op.type (%Int.as.Copy.impl.Op.type.824) = import_ref Core//prelude/parts/int, loc{{\d+_\d+}}, loaded [symbolic = @Int.as.Copy.impl.%Int.as.Copy.impl.Op (constants.%Int.as.Copy.impl.Op.9b9)] // CHECK:STDOUT: %Copy.impl_witness_table.e76 = impl_witness_table (%Core.import_ref.18d), @Int.as.Copy.impl [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: %Class.decl: %Class.type = class_decl @Class [concrete = constants.%Class.generic] { // CHECK:STDOUT: %T.patt: %pattern_type.98f = symbolic_binding_pattern T, 0 [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %.loc5_17.1: type = splice_block %.loc5_17.2 [concrete = type] { // CHECK:STDOUT: // CHECK:STDOUT: %.loc5_17.2: type = type_literal type [concrete = type] // CHECK:STDOUT: } // CHECK:STDOUT: %T.loc5_13.2: type = symbolic_binding T, 0 [symbolic = %T.loc5_13.1 (constants.%T.67d)] // CHECK:STDOUT: } // CHECK:STDOUT: %F.decl: %F.type = fn_decl @F [concrete = constants.%F] { // CHECK:STDOUT: %c.patt: %pattern_type.1c2 = value_binding_pattern c [concrete] // CHECK:STDOUT: %c.param_patt: %pattern_type.1c2 = value_param_pattern %c.patt [concrete] // CHECK:STDOUT: %return.patt: %pattern_type.7ce = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: %pattern_type.7ce = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %i32.loc9_24: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %.loc9_24: Core.Form = init_form %i32.loc9_24 [concrete = constants.%.ff5] // CHECK:STDOUT: %c.param: %Class.805 = value_param call_param0 // CHECK:STDOUT: %.loc9_18: type = splice_block %Class [concrete = constants.%Class.805] { // CHECK:STDOUT: %Class.ref: %Class.type = name_ref Class, file.%Class.decl [concrete = constants.%Class.generic] // CHECK:STDOUT: %i32.loc9_15: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %Class: type = class_type @Class, @Class(constants.%i32) [concrete = constants.%Class.805] // CHECK:STDOUT: } // CHECK:STDOUT: %c: %Class.805 = value_binding c, %c.param // CHECK:STDOUT: %return.param: ref %i32 = out_param call_param1 // CHECK:STDOUT: %return: ref %i32 = return_slot %return.param // CHECK:STDOUT: } // CHECK:STDOUT: %G.decl: %G.type = fn_decl @G [concrete = constants.%G] { // CHECK:STDOUT: %T.patt: %pattern_type.ce2 = symbolic_binding_pattern T, 0 [concrete] // CHECK:STDOUT: %c.patt: @G.%pattern_type.loc13_21 (%pattern_type.c542f5.1) = value_binding_pattern c [concrete] // CHECK:STDOUT: %c.param_patt: @G.%pattern_type.loc13_21 (%pattern_type.c542f5.1) = value_param_pattern %c.patt [concrete] // CHECK:STDOUT: %return.patt: @G.%pattern_type.loc13_34 (%pattern_type.9b9f0c.2) = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: @G.%pattern_type.loc13_34 (%pattern_type.9b9f0c.2) = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %T.ref.loc13_37: %Copy.type = name_ref T, %T.loc13_6.2 [symbolic = %T.loc13_6.1 (constants.%T.035)] // CHECK:STDOUT: %T.as_type.loc13_37: type = facet_access_type %T.ref.loc13_37 [symbolic = %T.binding.as_type (constants.%T.binding.as_type)] // CHECK:STDOUT: %.loc13_37.3: type = converted %T.ref.loc13_37, %T.as_type.loc13_37 [symbolic = %T.binding.as_type (constants.%T.binding.as_type)] // CHECK:STDOUT: %.loc13_37.4: Core.Form = init_form %.loc13_37.3 [symbolic = %.loc13_37.2 (constants.%.076a48.2)] // CHECK:STDOUT: %.loc13_14: type = splice_block %Copy.ref [concrete = constants.%Copy.type] { // CHECK:STDOUT: // CHECK:STDOUT: %Core.ref: = name_ref Core, imports.%Core [concrete = imports.%Core] // CHECK:STDOUT: %Copy.ref: type = name_ref Copy, imports.%Core.Copy [concrete = constants.%Copy.type] // CHECK:STDOUT: } // CHECK:STDOUT: %T.loc13_6.2: %Copy.type = symbolic_binding T, 0 [symbolic = %T.loc13_6.1 (constants.%T.035)] // CHECK:STDOUT: %c.param: @G.%Class.loc13_31.1 (%Class.3168aa.1) = value_param call_param0 // CHECK:STDOUT: %.loc13_31.1: type = splice_block %Class.loc13_31.2 [symbolic = %Class.loc13_31.1 (constants.%Class.3168aa.1)] { // CHECK:STDOUT: %Class.ref: %Class.type = name_ref Class, file.%Class.decl [concrete = constants.%Class.generic] // CHECK:STDOUT: %T.ref.loc13_30: %Copy.type = name_ref T, %T.loc13_6.2 [symbolic = %T.loc13_6.1 (constants.%T.035)] // CHECK:STDOUT: %T.as_type.loc13_31: type = facet_access_type %T.ref.loc13_30 [symbolic = %T.binding.as_type (constants.%T.binding.as_type)] // CHECK:STDOUT: %.loc13_31.2: type = converted %T.ref.loc13_30, %T.as_type.loc13_31 [symbolic = %T.binding.as_type (constants.%T.binding.as_type)] // CHECK:STDOUT: %Class.loc13_31.2: type = class_type @Class, @Class(constants.%T.binding.as_type) [symbolic = %Class.loc13_31.1 (constants.%Class.3168aa.1)] // CHECK:STDOUT: } // CHECK:STDOUT: %c: @G.%Class.loc13_31.1 (%Class.3168aa.1) = value_binding c, %c.param // CHECK:STDOUT: %return.param: ref @G.%T.binding.as_type (%T.binding.as_type) = out_param call_param1 // CHECK:STDOUT: %return: ref @G.%T.binding.as_type (%T.binding.as_type) = return_slot %return.param // CHECK:STDOUT: } // CHECK:STDOUT: %H.decl: %H.type = fn_decl @H [concrete = constants.%H] { // CHECK:STDOUT: %U.patt: %pattern_type.ce2 = symbolic_binding_pattern U, 0 [concrete] // CHECK:STDOUT: %c.patt: @H.%pattern_type.loc17_21 (%pattern_type.c542f5.2) = value_binding_pattern c [concrete] // CHECK:STDOUT: %c.param_patt: @H.%pattern_type.loc17_21 (%pattern_type.c542f5.2) = value_param_pattern %c.patt [concrete] // CHECK:STDOUT: %return.patt: @H.%pattern_type.loc17_34 (%pattern_type.9b9f0c.3) = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: @H.%pattern_type.loc17_34 (%pattern_type.9b9f0c.3) = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %U.ref.loc17_37: %Copy.type = name_ref U, %U.loc17_6.2 [symbolic = %U.loc17_6.1 (constants.%U.035)] // CHECK:STDOUT: %U.as_type.loc17_37: type = facet_access_type %U.ref.loc17_37 [symbolic = %U.binding.as_type (constants.%U.binding.as_type.14b)] // CHECK:STDOUT: %.loc17_37.3: type = converted %U.ref.loc17_37, %U.as_type.loc17_37 [symbolic = %U.binding.as_type (constants.%U.binding.as_type.14b)] // CHECK:STDOUT: %.loc17_37.4: Core.Form = init_form %.loc17_37.3 [symbolic = %.loc17_37.2 (constants.%.076a48.3)] // CHECK:STDOUT: %.loc17_14: type = splice_block %Copy.ref [concrete = constants.%Copy.type] { // CHECK:STDOUT: // CHECK:STDOUT: %Core.ref: = name_ref Core, imports.%Core [concrete = imports.%Core] // CHECK:STDOUT: %Copy.ref: type = name_ref Copy, imports.%Core.Copy [concrete = constants.%Copy.type] // CHECK:STDOUT: } // CHECK:STDOUT: %U.loc17_6.2: %Copy.type = symbolic_binding U, 0 [symbolic = %U.loc17_6.1 (constants.%U.035)] // CHECK:STDOUT: %c.param: @H.%Class.loc17_31.1 (%Class.3168aa.2) = value_param call_param0 // CHECK:STDOUT: %.loc17_31.1: type = splice_block %Class.loc17_31.2 [symbolic = %Class.loc17_31.1 (constants.%Class.3168aa.2)] { // CHECK:STDOUT: %Class.ref: %Class.type = name_ref Class, file.%Class.decl [concrete = constants.%Class.generic] // CHECK:STDOUT: %U.ref.loc17_30: %Copy.type = name_ref U, %U.loc17_6.2 [symbolic = %U.loc17_6.1 (constants.%U.035)] // CHECK:STDOUT: %U.as_type.loc17_31: type = facet_access_type %U.ref.loc17_30 [symbolic = %U.binding.as_type (constants.%U.binding.as_type.14b)] // CHECK:STDOUT: %.loc17_31.2: type = converted %U.ref.loc17_30, %U.as_type.loc17_31 [symbolic = %U.binding.as_type (constants.%U.binding.as_type.14b)] // CHECK:STDOUT: %Class.loc17_31.2: type = class_type @Class, @Class(constants.%U.binding.as_type.14b) [symbolic = %Class.loc17_31.1 (constants.%Class.3168aa.2)] // CHECK:STDOUT: } // CHECK:STDOUT: %c: @H.%Class.loc17_31.1 (%Class.3168aa.2) = value_binding c, %c.param // CHECK:STDOUT: %return.param: ref @H.%U.binding.as_type (%U.binding.as_type.14b) = out_param call_param1 // CHECK:STDOUT: %return: ref @H.%U.binding.as_type (%U.binding.as_type.14b) = return_slot %return.param // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic class @Class(%T.loc5_13.2: type) { // CHECK:STDOUT: %T.loc5_13.1: type = symbolic_binding T, 0 [symbolic = %T.loc5_13.1 (constants.%T.67d)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete: = require_complete_type %T.loc5_13.1 [symbolic = %require_complete (constants.%require_complete.944)] // CHECK:STDOUT: %Class: type = class_type @Class, @Class(%T.loc5_13.1) [symbolic = %Class (constants.%Class.0db)] // CHECK:STDOUT: %Class.elem: type = unbound_element_type %Class, %T.loc5_13.1 [symbolic = %Class.elem (constants.%Class.elem.fdf)] // CHECK:STDOUT: %struct_type.x: type = struct_type {.x: @Class.%T.loc5_13.1 (%T.67d)} [symbolic = %struct_type.x (constants.%struct_type.x.0c5)] // CHECK:STDOUT: %complete_type.loc7_1.2: = complete_type_witness %struct_type.x [symbolic = %complete_type.loc7_1.2 (constants.%complete_type.735)] // CHECK:STDOUT: // CHECK:STDOUT: class { // CHECK:STDOUT: %T.ref: type = name_ref T, %T.loc5_13.2 [symbolic = %T.loc5_13.1 (constants.%T.67d)] // CHECK:STDOUT: %.loc6: @Class.%Class.elem (%Class.elem.fdf) = field_decl x, element0 [concrete] // CHECK:STDOUT: %complete_type.loc7_1.1: = complete_type_witness constants.%struct_type.x.0c5 [symbolic = %complete_type.loc7_1.2 (constants.%complete_type.735)] // CHECK:STDOUT: complete_type_witness = %complete_type.loc7_1.1 // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%Class.0db // CHECK:STDOUT: .T = // CHECK:STDOUT: .x = %.loc6 // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @F(%c.param: %Class.805) -> out %return.param: %i32 { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %c.ref: %Class.805 = name_ref c, %c // CHECK:STDOUT: %x.ref: %Class.elem.927 = name_ref x, @Class.%.loc6 [concrete = @Class.%.loc6] // CHECK:STDOUT: %.loc10_11.1: ref %i32 = class_element_access %c.ref, element0 // CHECK:STDOUT: %.loc10_11.2: %i32 = acquire_value %.loc10_11.1 // CHECK:STDOUT: %impl.elem0: %.8e2 = impl_witness_access constants.%Copy.impl_witness.f17, element0 [concrete = constants.%Int.as.Copy.impl.Op.664] // CHECK:STDOUT: %bound_method.loc10_11.1: = bound_method %.loc10_11.2, %impl.elem0 // CHECK:STDOUT: %specific_fn: = specific_function %impl.elem0, @Int.as.Copy.impl.Op(constants.%int_32) [concrete = constants.%Int.as.Copy.impl.Op.specific_fn] // CHECK:STDOUT: %bound_method.loc10_11.2: = bound_method %.loc10_11.2, %specific_fn // CHECK:STDOUT: %Int.as.Copy.impl.Op.call: init %i32 = call %bound_method.loc10_11.2(%.loc10_11.2) // CHECK:STDOUT: return %Int.as.Copy.impl.Op.call // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic fn @G(%T.loc13_6.2: %Copy.type) { // CHECK:STDOUT: %T.loc13_6.1: %Copy.type = symbolic_binding T, 0 [symbolic = %T.loc13_6.1 (constants.%T.035)] // CHECK:STDOUT: %T.binding.as_type: type = symbolic_binding_type T, 0, %T.loc13_6.1 [symbolic = %T.binding.as_type (constants.%T.binding.as_type)] // CHECK:STDOUT: %Class.loc13_31.1: type = class_type @Class, @Class(%T.binding.as_type) [symbolic = %Class.loc13_31.1 (constants.%Class.3168aa.1)] // CHECK:STDOUT: %pattern_type.loc13_21: type = pattern_type %Class.loc13_31.1 [symbolic = %pattern_type.loc13_21 (constants.%pattern_type.c542f5.1)] // CHECK:STDOUT: %.loc13_37.2: Core.Form = init_form %T.binding.as_type [symbolic = %.loc13_37.2 (constants.%.076a48.2)] // CHECK:STDOUT: %pattern_type.loc13_34: type = pattern_type %T.binding.as_type [symbolic = %pattern_type.loc13_34 (constants.%pattern_type.9b9f0c.2)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete.loc13: = require_complete_type %Class.loc13_31.1 [symbolic = %require_complete.loc13 (constants.%require_complete.ae7bfa.1)] // CHECK:STDOUT: %Class.elem: type = unbound_element_type %Class.loc13_31.1, %T.binding.as_type [symbolic = %Class.elem (constants.%Class.elem.7657d6.1)] // CHECK:STDOUT: %require_complete.loc14: = require_complete_type %T.binding.as_type [symbolic = %require_complete.loc14 (constants.%require_complete.67ca8d.1)] // CHECK:STDOUT: %Copy.lookup_impl_witness: = lookup_impl_witness %T.loc13_6.1, @Copy [symbolic = %Copy.lookup_impl_witness (constants.%Copy.lookup_impl_witness.58dce0.1)] // CHECK:STDOUT: %Copy.WithSelf.Op.type: type = fn_type @Copy.WithSelf.Op, @Copy.WithSelf(%T.loc13_6.1) [symbolic = %Copy.WithSelf.Op.type (constants.%Copy.WithSelf.Op.type.735e75.2)] // CHECK:STDOUT: %.loc14_11.3: type = fn_type_with_self_type %Copy.WithSelf.Op.type, %T.loc13_6.1 [symbolic = %.loc14_11.3 (constants.%.023143.1)] // CHECK:STDOUT: %impl.elem0.loc14_11.2: @G.%.loc14_11.3 (%.023143.1) = impl_witness_access %Copy.lookup_impl_witness, element0 [symbolic = %impl.elem0.loc14_11.2 (constants.%impl.elem0.594c59.1)] // CHECK:STDOUT: %specific_impl_fn.loc14_11.2: = specific_impl_function %impl.elem0.loc14_11.2, @Copy.WithSelf.Op(%T.loc13_6.1) [symbolic = %specific_impl_fn.loc14_11.2 (constants.%specific_impl_fn.bdce5c.1)] // CHECK:STDOUT: // CHECK:STDOUT: fn(%c.param: @G.%Class.loc13_31.1 (%Class.3168aa.1)) -> out %return.param: @G.%T.binding.as_type (%T.binding.as_type) { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %c.ref: @G.%Class.loc13_31.1 (%Class.3168aa.1) = name_ref c, %c // CHECK:STDOUT: %x.ref: @G.%Class.elem (%Class.elem.7657d6.1) = name_ref x, @Class.%.loc6 [concrete = @Class.%.loc6] // CHECK:STDOUT: %.loc14_11.1: ref @G.%T.binding.as_type (%T.binding.as_type) = class_element_access %c.ref, element0 // CHECK:STDOUT: %.loc14_11.2: @G.%T.binding.as_type (%T.binding.as_type) = acquire_value %.loc14_11.1 // CHECK:STDOUT: %impl.elem0.loc14_11.1: @G.%.loc14_11.3 (%.023143.1) = impl_witness_access constants.%Copy.lookup_impl_witness.58dce0.1, element0 [symbolic = %impl.elem0.loc14_11.2 (constants.%impl.elem0.594c59.1)] // CHECK:STDOUT: %bound_method.loc14_11.1: = bound_method %.loc14_11.2, %impl.elem0.loc14_11.1 // CHECK:STDOUT: %specific_impl_fn.loc14_11.1: = specific_impl_function %impl.elem0.loc14_11.1, @Copy.WithSelf.Op(constants.%T.035) [symbolic = %specific_impl_fn.loc14_11.2 (constants.%specific_impl_fn.bdce5c.1)] // CHECK:STDOUT: %bound_method.loc14_11.2: = bound_method %.loc14_11.2, %specific_impl_fn.loc14_11.1 // CHECK:STDOUT: %.loc13_37.1: ref @G.%T.binding.as_type (%T.binding.as_type) = splice_block %return.param {} // CHECK:STDOUT: %Copy.WithSelf.Op.call: init @G.%T.binding.as_type (%T.binding.as_type) to %.loc13_37.1 = call %bound_method.loc14_11.2(%.loc14_11.2) // CHECK:STDOUT: return %Copy.WithSelf.Op.call to %return.param // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic fn @H(%U.loc17_6.2: %Copy.type) { // CHECK:STDOUT: %U.loc17_6.1: %Copy.type = symbolic_binding U, 0 [symbolic = %U.loc17_6.1 (constants.%U.035)] // CHECK:STDOUT: %U.binding.as_type: type = symbolic_binding_type U, 0, %U.loc17_6.1 [symbolic = %U.binding.as_type (constants.%U.binding.as_type.14b)] // CHECK:STDOUT: %Class.loc17_31.1: type = class_type @Class, @Class(%U.binding.as_type) [symbolic = %Class.loc17_31.1 (constants.%Class.3168aa.2)] // CHECK:STDOUT: %pattern_type.loc17_21: type = pattern_type %Class.loc17_31.1 [symbolic = %pattern_type.loc17_21 (constants.%pattern_type.c542f5.2)] // CHECK:STDOUT: %.loc17_37.2: Core.Form = init_form %U.binding.as_type [symbolic = %.loc17_37.2 (constants.%.076a48.3)] // CHECK:STDOUT: %pattern_type.loc17_34: type = pattern_type %U.binding.as_type [symbolic = %pattern_type.loc17_34 (constants.%pattern_type.9b9f0c.3)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete.loc17: = require_complete_type %Class.loc17_31.1 [symbolic = %require_complete.loc17 (constants.%require_complete.ae7bfa.2)] // CHECK:STDOUT: %Class.elem: type = unbound_element_type %Class.loc17_31.1, %U.binding.as_type [symbolic = %Class.elem (constants.%Class.elem.7657d6.2)] // CHECK:STDOUT: %require_complete.loc18: = require_complete_type %U.binding.as_type [symbolic = %require_complete.loc18 (constants.%require_complete.67ca8d.2)] // CHECK:STDOUT: %Copy.lookup_impl_witness: = lookup_impl_witness %U.loc17_6.1, @Copy [symbolic = %Copy.lookup_impl_witness (constants.%Copy.lookup_impl_witness.58dce0.2)] // CHECK:STDOUT: %Copy.WithSelf.Op.type: type = fn_type @Copy.WithSelf.Op, @Copy.WithSelf(%U.loc17_6.1) [symbolic = %Copy.WithSelf.Op.type (constants.%Copy.WithSelf.Op.type.735e75.3)] // CHECK:STDOUT: %.loc18_11.3: type = fn_type_with_self_type %Copy.WithSelf.Op.type, %U.loc17_6.1 [symbolic = %.loc18_11.3 (constants.%.023143.2)] // CHECK:STDOUT: %impl.elem0.loc18_11.2: @H.%.loc18_11.3 (%.023143.2) = impl_witness_access %Copy.lookup_impl_witness, element0 [symbolic = %impl.elem0.loc18_11.2 (constants.%impl.elem0.594c59.2)] // CHECK:STDOUT: %specific_impl_fn.loc18_11.2: = specific_impl_function %impl.elem0.loc18_11.2, @Copy.WithSelf.Op(%U.loc17_6.1) [symbolic = %specific_impl_fn.loc18_11.2 (constants.%specific_impl_fn.bdce5c.2)] // CHECK:STDOUT: // CHECK:STDOUT: fn(%c.param: @H.%Class.loc17_31.1 (%Class.3168aa.2)) -> out %return.param: @H.%U.binding.as_type (%U.binding.as_type.14b) { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %c.ref: @H.%Class.loc17_31.1 (%Class.3168aa.2) = name_ref c, %c // CHECK:STDOUT: %x.ref: @H.%Class.elem (%Class.elem.7657d6.2) = name_ref x, @Class.%.loc6 [concrete = @Class.%.loc6] // CHECK:STDOUT: %.loc18_11.1: ref @H.%U.binding.as_type (%U.binding.as_type.14b) = class_element_access %c.ref, element0 // CHECK:STDOUT: %.loc18_11.2: @H.%U.binding.as_type (%U.binding.as_type.14b) = acquire_value %.loc18_11.1 // CHECK:STDOUT: %impl.elem0.loc18_11.1: @H.%.loc18_11.3 (%.023143.2) = impl_witness_access constants.%Copy.lookup_impl_witness.58dce0.2, element0 [symbolic = %impl.elem0.loc18_11.2 (constants.%impl.elem0.594c59.2)] // CHECK:STDOUT: %bound_method.loc18_11.1: = bound_method %.loc18_11.2, %impl.elem0.loc18_11.1 // CHECK:STDOUT: %specific_impl_fn.loc18_11.1: = specific_impl_function %impl.elem0.loc18_11.1, @Copy.WithSelf.Op(constants.%U.035) [symbolic = %specific_impl_fn.loc18_11.2 (constants.%specific_impl_fn.bdce5c.2)] // CHECK:STDOUT: %bound_method.loc18_11.2: = bound_method %.loc18_11.2, %specific_impl_fn.loc18_11.1 // CHECK:STDOUT: %.loc17_37.1: ref @H.%U.binding.as_type (%U.binding.as_type.14b) = splice_block %return.param {} // CHECK:STDOUT: %Copy.WithSelf.Op.call: init @H.%U.binding.as_type (%U.binding.as_type.14b) to %.loc17_37.1 = call %bound_method.loc18_11.2(%.loc18_11.2) // CHECK:STDOUT: return %Copy.WithSelf.Op.call to %return.param // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @Class(constants.%T.67d) { // CHECK:STDOUT: %T.loc5_13.1 => constants.%T.67d // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @Class(constants.%i32) { // CHECK:STDOUT: %T.loc5_13.1 => constants.%i32 // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete => constants.%complete_type.f8a // CHECK:STDOUT: %Class => constants.%Class.805 // CHECK:STDOUT: %Class.elem => constants.%Class.elem.927 // CHECK:STDOUT: %struct_type.x => constants.%struct_type.x.ed6 // CHECK:STDOUT: %complete_type.loc7_1.2 => constants.%complete_type.1ec // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @Class(constants.%T.binding.as_type) { // CHECK:STDOUT: %T.loc5_13.1 => constants.%T.binding.as_type // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete => constants.%require_complete.67ca8d.1 // CHECK:STDOUT: %Class => constants.%Class.3168aa.1 // CHECK:STDOUT: %Class.elem => constants.%Class.elem.7657d6.1 // CHECK:STDOUT: %struct_type.x => constants.%struct_type.x.8dcd6b.1 // CHECK:STDOUT: %complete_type.loc7_1.2 => constants.%complete_type.e78b36.1 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @G(constants.%T.035) { // CHECK:STDOUT: %T.loc13_6.1 => constants.%T.035 // CHECK:STDOUT: %T.binding.as_type => constants.%T.binding.as_type // CHECK:STDOUT: %Class.loc13_31.1 => constants.%Class.3168aa.1 // CHECK:STDOUT: %pattern_type.loc13_21 => constants.%pattern_type.c542f5.1 // CHECK:STDOUT: %.loc13_37.2 => constants.%.076a48.2 // CHECK:STDOUT: %pattern_type.loc13_34 => constants.%pattern_type.9b9f0c.2 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @Class(constants.%U.binding.as_type.14b) { // CHECK:STDOUT: %T.loc5_13.1 => constants.%U.binding.as_type.14b // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete => constants.%require_complete.67ca8d.2 // CHECK:STDOUT: %Class => constants.%Class.3168aa.2 // CHECK:STDOUT: %Class.elem => constants.%Class.elem.7657d6.2 // CHECK:STDOUT: %struct_type.x => constants.%struct_type.x.8dcd6b.2 // CHECK:STDOUT: %complete_type.loc7_1.2 => constants.%complete_type.e78b36.2 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @H(constants.%U.035) { // CHECK:STDOUT: %U.loc17_6.1 => constants.%U.035 // CHECK:STDOUT: %U.binding.as_type => constants.%U.binding.as_type.14b // CHECK:STDOUT: %Class.loc17_31.1 => constants.%Class.3168aa.2 // CHECK:STDOUT: %pattern_type.loc17_21 => constants.%pattern_type.c542f5.2 // CHECK:STDOUT: %.loc17_37.2 => constants.%.076a48.3 // CHECK:STDOUT: %pattern_type.loc17_34 => constants.%pattern_type.9b9f0c.3 // CHECK:STDOUT: } // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/class/generic/generic_vs_params.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/none.carbon // TODO: Add ranges and switch to "--dump-sem-ir-ranges=only". // EXTRA-ARGS: --dump-sem-ir-ranges=if-present // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/class/generic/generic_vs_params.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/class/generic/generic_vs_params.carbon // --- params.carbon library "[[@TEST_NAME]]"; class NotGenericNoParams {} class NotGenericButParams() {} class GenericAndParams(T:! type) {} class C(T:! type) { class GenericNoParams {} class GenericAndParams(U:! type) {} } class X {} var a: NotGenericNoParams = {}; var b: NotGenericButParams() = {}; var c: GenericAndParams(X) = {}; var d: C(X).GenericNoParams = {}; var e: C(X).GenericAndParams(X) = {}; // --- fail_non_generic_implicit_params.carbon library "[[@TEST_NAME]]"; // CHECK:STDERR: fail_non_generic_implicit_params.carbon:[[@LINE+4]]:9: error: parameters of generic types must be constant [GenericParamMustBeConstant] // CHECK:STDERR: class A[T: type]() {} // CHECK:STDERR: ^~~~~~~ // CHECK:STDERR: class A[T: type]() {} // --- fail_non_generic_params.carbon library "[[@TEST_NAME]]"; // CHECK:STDERR: fail_non_generic_params.carbon:[[@LINE+4]]:9: error: parameters of generic types must be constant [GenericParamMustBeConstant] // CHECK:STDERR: class A(T: type) {} // CHECK:STDERR: ^~~~~~~ // CHECK:STDERR: class A(T: type) {} // This is testing a use of the invalid type. fn F(T:! type) { A(T); } // --- fail_implicit_params_only_empty.carbon library "[[@TEST_NAME]]"; // CHECK:STDERR: fail_implicit_params_only_empty.carbon:[[@LINE+4]]:10: error: expected explicit parameters after implicit parameters [GenericMissingExplicitParameters] // CHECK:STDERR: class Foo[]; // CHECK:STDERR: ^~ // CHECK:STDERR: class Foo[]; // --- fail_implicit_params_only.carbon library "[[@TEST_NAME]]"; // CHECK:STDERR: fail_implicit_params_only.carbon:[[@LINE+4]]:10: error: expected explicit parameters after implicit parameters [GenericMissingExplicitParameters] // CHECK:STDERR: class Foo[T:! type]; // CHECK:STDERR: ^~~~~~~~~~ // CHECK:STDERR: class Foo[T:! type]; // CHECK:STDOUT: --- params.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %NotGenericNoParams: type = class_type @NotGenericNoParams [concrete] // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %complete_type: = complete_type_witness %empty_struct_type [concrete] // CHECK:STDOUT: %NotGenericButParams.type: type = generic_class_type @NotGenericButParams [concrete] // CHECK:STDOUT: %NotGenericButParams.generic: %NotGenericButParams.type = struct_value () [concrete] // CHECK:STDOUT: %NotGenericButParams: type = class_type @NotGenericButParams [concrete] // CHECK:STDOUT: %type: type = facet_type [concrete] // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self] // CHECK:STDOUT: %pattern_type.98f: type = pattern_type type [concrete] // CHECK:STDOUT: %T: type = symbolic_binding T, 0 [symbolic] // CHECK:STDOUT: %GenericAndParams.type.fa0: type = generic_class_type @GenericAndParams.loc6 [concrete] // CHECK:STDOUT: %GenericAndParams.generic.e6f: %GenericAndParams.type.fa0 = struct_value () [concrete] // CHECK:STDOUT: %GenericAndParams.2b4: type = class_type @GenericAndParams.loc6, @GenericAndParams.loc6(%T) [symbolic] // CHECK:STDOUT: %C.type: type = generic_class_type @C [concrete] // CHECK:STDOUT: %C.generic: %C.type = struct_value () [concrete] // CHECK:STDOUT: %C.5a3: type = class_type @C, @C(%T) [symbolic] // CHECK:STDOUT: %GenericNoParams.6cf: type = class_type @GenericNoParams, @GenericNoParams(%T) [symbolic] // CHECK:STDOUT: %U: type = symbolic_binding U, 1 [symbolic] // CHECK:STDOUT: %GenericAndParams.type.208: type = generic_class_type @GenericAndParams.loc10, @C(%T) [symbolic] // CHECK:STDOUT: %GenericAndParams.generic.cc2: %GenericAndParams.type.208 = struct_value () [symbolic] // CHECK:STDOUT: %GenericAndParams.015: type = class_type @GenericAndParams.loc10, @GenericAndParams.loc10(%T, %U) [symbolic] // CHECK:STDOUT: %X: type = class_type @X [concrete] // CHECK:STDOUT: %pattern_type.673: type = pattern_type %NotGenericNoParams [concrete] // CHECK:STDOUT: %empty_struct: %empty_struct_type = struct_value () [concrete] // CHECK:STDOUT: %NotGenericNoParams.val: %NotGenericNoParams = struct_value () [concrete] // CHECK:STDOUT: %pattern_type.749: type = pattern_type %NotGenericButParams [concrete] // CHECK:STDOUT: %NotGenericButParams.val: %NotGenericButParams = struct_value () [concrete] // CHECK:STDOUT: %GenericAndParams.d3d: type = class_type @GenericAndParams.loc6, @GenericAndParams.loc6(%X) [concrete] // CHECK:STDOUT: %pattern_type.856: type = pattern_type %GenericAndParams.d3d [concrete] // CHECK:STDOUT: %GenericAndParams.val.835: %GenericAndParams.d3d = struct_value () [concrete] // CHECK:STDOUT: %C.513: type = class_type @C, @C(%X) [concrete] // CHECK:STDOUT: %GenericNoParams.efa: type = class_type @GenericNoParams, @GenericNoParams(%X) [concrete] // CHECK:STDOUT: %GenericAndParams.type.23d: type = generic_class_type @GenericAndParams.loc10, @C(%X) [concrete] // CHECK:STDOUT: %GenericAndParams.generic.fd2: %GenericAndParams.type.23d = struct_value () [concrete] // CHECK:STDOUT: %pattern_type.1fb: type = pattern_type %GenericNoParams.efa [concrete] // CHECK:STDOUT: %GenericNoParams.val: %GenericNoParams.efa = struct_value () [concrete] // CHECK:STDOUT: %GenericAndParams.ed1: type = class_type @GenericAndParams.loc10, @GenericAndParams.loc10(%X, %X) [concrete] // CHECK:STDOUT: %pattern_type.1b8: type = pattern_type %GenericAndParams.ed1 [concrete] // CHECK:STDOUT: %GenericAndParams.val.e35: %GenericAndParams.ed1 = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .NotGenericNoParams = %NotGenericNoParams.decl // CHECK:STDOUT: .NotGenericButParams = %NotGenericButParams.decl // CHECK:STDOUT: .GenericAndParams = %GenericAndParams.decl // CHECK:STDOUT: .C = %C.decl // CHECK:STDOUT: .X = %X.decl // CHECK:STDOUT: .a = %a // CHECK:STDOUT: .b = %b // CHECK:STDOUT: .c = %c // CHECK:STDOUT: .d = %d // CHECK:STDOUT: .e = %e // CHECK:STDOUT: } // CHECK:STDOUT: %NotGenericNoParams.decl: type = class_decl @NotGenericNoParams [concrete = constants.%NotGenericNoParams] {} {} // CHECK:STDOUT: %NotGenericButParams.decl: %NotGenericButParams.type = class_decl @NotGenericButParams [concrete = constants.%NotGenericButParams.generic] {} {} // CHECK:STDOUT: %GenericAndParams.decl: %GenericAndParams.type.fa0 = class_decl @GenericAndParams.loc6 [concrete = constants.%GenericAndParams.generic.e6f] { // CHECK:STDOUT: %T.patt: %pattern_type.98f = symbolic_binding_pattern T, 0 [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %.loc6_28.1: type = splice_block %.loc6_28.2 [concrete = type] { // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %.loc6_28.2: type = type_literal type [concrete = type] // CHECK:STDOUT: } // CHECK:STDOUT: %T.loc6_24.2: type = symbolic_binding T, 0 [symbolic = %T.loc6_24.1 (constants.%T)] // CHECK:STDOUT: } // CHECK:STDOUT: %C.decl: %C.type = class_decl @C [concrete = constants.%C.generic] { // CHECK:STDOUT: %T.patt: %pattern_type.98f = symbolic_binding_pattern T, 0 [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %.loc8_13.1: type = splice_block %.loc8_13.2 [concrete = type] { // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %.loc8_13.2: type = type_literal type [concrete = type] // CHECK:STDOUT: } // CHECK:STDOUT: %T.loc8_9.2: type = symbolic_binding T, 0 [symbolic = %T.loc8_9.1 (constants.%T)] // CHECK:STDOUT: } // CHECK:STDOUT: %X.decl: type = class_decl @X [concrete = constants.%X] {} {} // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %a.patt: %pattern_type.673 = ref_binding_pattern a [concrete] // CHECK:STDOUT: %a.var_patt: %pattern_type.673 = var_pattern %a.patt [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %a.var: ref %NotGenericNoParams = var %a.var_patt [concrete] // CHECK:STDOUT: %NotGenericNoParams.ref: type = name_ref NotGenericNoParams, %NotGenericNoParams.decl [concrete = constants.%NotGenericNoParams] // CHECK:STDOUT: %a: ref %NotGenericNoParams = ref_binding a, %a.var [concrete = %a.var] // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %b.patt: %pattern_type.749 = ref_binding_pattern b [concrete] // CHECK:STDOUT: %b.var_patt: %pattern_type.749 = var_pattern %b.patt [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %b.var: ref %NotGenericButParams = var %b.var_patt [concrete] // CHECK:STDOUT: %.loc16: type = splice_block %NotGenericButParams [concrete = constants.%NotGenericButParams] { // CHECK:STDOUT: %NotGenericButParams.ref: %NotGenericButParams.type = name_ref NotGenericButParams, %NotGenericButParams.decl [concrete = constants.%NotGenericButParams.generic] // CHECK:STDOUT: %NotGenericButParams: type = class_type @NotGenericButParams [concrete = constants.%NotGenericButParams] // CHECK:STDOUT: } // CHECK:STDOUT: %b: ref %NotGenericButParams = ref_binding b, %b.var [concrete = %b.var] // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %c.patt: %pattern_type.856 = ref_binding_pattern c [concrete] // CHECK:STDOUT: %c.var_patt: %pattern_type.856 = var_pattern %c.patt [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %c.var: ref %GenericAndParams.d3d = var %c.var_patt [concrete] // CHECK:STDOUT: %.loc17: type = splice_block %GenericAndParams.loc17 [concrete = constants.%GenericAndParams.d3d] { // CHECK:STDOUT: %GenericAndParams.ref.loc17: %GenericAndParams.type.fa0 = name_ref GenericAndParams, %GenericAndParams.decl [concrete = constants.%GenericAndParams.generic.e6f] // CHECK:STDOUT: %X.ref.loc17: type = name_ref X, %X.decl [concrete = constants.%X] // CHECK:STDOUT: %GenericAndParams.loc17: type = class_type @GenericAndParams.loc6, @GenericAndParams.loc6(constants.%X) [concrete = constants.%GenericAndParams.d3d] // CHECK:STDOUT: } // CHECK:STDOUT: %c: ref %GenericAndParams.d3d = ref_binding c, %c.var [concrete = %c.var] // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %d.patt: %pattern_type.1fb = ref_binding_pattern d [concrete] // CHECK:STDOUT: %d.var_patt: %pattern_type.1fb = var_pattern %d.patt [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %d.var: ref %GenericNoParams.efa = var %d.var_patt [concrete] // CHECK:STDOUT: %.loc18_12.1: type = splice_block %GenericNoParams.ref [concrete = constants.%GenericNoParams.efa] { // CHECK:STDOUT: %C.ref.loc18: %C.type = name_ref C, %C.decl [concrete = constants.%C.generic] // CHECK:STDOUT: %X.ref.loc18: type = name_ref X, %X.decl [concrete = constants.%X] // CHECK:STDOUT: %C.loc18: type = class_type @C, @C(constants.%X) [concrete = constants.%C.513] // CHECK:STDOUT: %.loc18_12.2: type = specific_constant @C.%GenericNoParams.decl, @C(constants.%X) [concrete = constants.%GenericNoParams.efa] // CHECK:STDOUT: %GenericNoParams.ref: type = name_ref GenericNoParams, %.loc18_12.2 [concrete = constants.%GenericNoParams.efa] // CHECK:STDOUT: } // CHECK:STDOUT: %d: ref %GenericNoParams.efa = ref_binding d, %d.var [concrete = %d.var] // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %e.patt: %pattern_type.1b8 = ref_binding_pattern e [concrete] // CHECK:STDOUT: %e.var_patt: %pattern_type.1b8 = var_pattern %e.patt [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %e.var: ref %GenericAndParams.ed1 = var %e.var_patt [concrete] // CHECK:STDOUT: %.loc19_31: type = splice_block %GenericAndParams.loc19 [concrete = constants.%GenericAndParams.ed1] { // CHECK:STDOUT: %C.ref.loc19: %C.type = name_ref C, %C.decl [concrete = constants.%C.generic] // CHECK:STDOUT: %X.ref.loc19_10: type = name_ref X, %X.decl [concrete = constants.%X] // CHECK:STDOUT: %C.loc19: type = class_type @C, @C(constants.%X) [concrete = constants.%C.513] // CHECK:STDOUT: %.loc19_12: %GenericAndParams.type.23d = specific_constant @C.%GenericAndParams.decl, @C(constants.%X) [concrete = constants.%GenericAndParams.generic.fd2] // CHECK:STDOUT: %GenericAndParams.ref.loc19: %GenericAndParams.type.23d = name_ref GenericAndParams, %.loc19_12 [concrete = constants.%GenericAndParams.generic.fd2] // CHECK:STDOUT: %X.ref.loc19_30: type = name_ref X, %X.decl [concrete = constants.%X] // CHECK:STDOUT: %GenericAndParams.loc19: type = class_type @GenericAndParams.loc10, @GenericAndParams.loc10(constants.%X, constants.%X) [concrete = constants.%GenericAndParams.ed1] // CHECK:STDOUT: } // CHECK:STDOUT: %e: ref %GenericAndParams.ed1 = ref_binding e, %e.var [concrete = %e.var] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @NotGenericNoParams { // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%NotGenericNoParams // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @NotGenericButParams { // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%NotGenericButParams // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic class @GenericAndParams.loc6(%T.loc6_24.2: type) { // CHECK:STDOUT: %T.loc6_24.1: type = symbolic_binding T, 0 [symbolic = %T.loc6_24.1 (constants.%T)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: // CHECK:STDOUT: class { // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%GenericAndParams.2b4 // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic class @C(%T.loc8_9.2: type) { // CHECK:STDOUT: %T.loc8_9.1: type = symbolic_binding T, 0 [symbolic = %T.loc8_9.1 (constants.%T)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %GenericNoParams: type = class_type @GenericNoParams, @GenericNoParams(%T.loc8_9.1) [symbolic = %GenericNoParams (constants.%GenericNoParams.6cf)] // CHECK:STDOUT: %GenericAndParams.type: type = generic_class_type @GenericAndParams.loc10, @C(%T.loc8_9.1) [symbolic = %GenericAndParams.type (constants.%GenericAndParams.type.208)] // CHECK:STDOUT: %GenericAndParams.generic: @C.%GenericAndParams.type (%GenericAndParams.type.208) = struct_value () [symbolic = %GenericAndParams.generic (constants.%GenericAndParams.generic.cc2)] // CHECK:STDOUT: // CHECK:STDOUT: class { // CHECK:STDOUT: %GenericNoParams.decl: type = class_decl @GenericNoParams [symbolic = @C.%GenericNoParams (constants.%GenericNoParams.6cf)] {} {} // CHECK:STDOUT: %GenericAndParams.decl: @C.%GenericAndParams.type (%GenericAndParams.type.208) = class_decl @GenericAndParams.loc10 [symbolic = @C.%GenericAndParams.generic (constants.%GenericAndParams.generic.cc2)] { // CHECK:STDOUT: %U.patt: %pattern_type.98f = symbolic_binding_pattern U, 1 [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %.loc10_30.1: type = splice_block %.loc10_30.2 [concrete = type] { // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %.loc10_30.2: type = type_literal type [concrete = type] // CHECK:STDOUT: } // CHECK:STDOUT: %U.loc10_26.2: type = symbolic_binding U, 1 [symbolic = %U.loc10_26.1 (constants.%U)] // CHECK:STDOUT: } // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%C.5a3 // CHECK:STDOUT: .GenericNoParams = %GenericNoParams.decl // CHECK:STDOUT: .GenericAndParams = %GenericAndParams.decl // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic class @GenericNoParams(@C.%T.loc8_9.2: type) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: // CHECK:STDOUT: class { // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%GenericNoParams.6cf // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic class @GenericAndParams.loc10(@C.%T.loc8_9.2: type, %U.loc10_26.2: type) { // CHECK:STDOUT: %U.loc10_26.1: type = symbolic_binding U, 1 [symbolic = %U.loc10_26.1 (constants.%U)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: // CHECK:STDOUT: class { // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%GenericAndParams.015 // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @X { // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%X // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @__global_init() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %.loc15_30.1: %empty_struct_type = struct_literal () [concrete = constants.%empty_struct] // CHECK:STDOUT: %.loc15_30.2: init %NotGenericNoParams to file.%a.var = class_init () [concrete = constants.%NotGenericNoParams.val] // CHECK:STDOUT: %.loc15_1: init %NotGenericNoParams = converted %.loc15_30.1, %.loc15_30.2 [concrete = constants.%NotGenericNoParams.val] // CHECK:STDOUT: assign file.%a.var, %.loc15_1 // CHECK:STDOUT: %.loc16_33.1: %empty_struct_type = struct_literal () [concrete = constants.%empty_struct] // CHECK:STDOUT: %.loc16_33.2: init %NotGenericButParams to file.%b.var = class_init () [concrete = constants.%NotGenericButParams.val] // CHECK:STDOUT: %.loc16_1: init %NotGenericButParams = converted %.loc16_33.1, %.loc16_33.2 [concrete = constants.%NotGenericButParams.val] // CHECK:STDOUT: assign file.%b.var, %.loc16_1 // CHECK:STDOUT: %.loc17_31.1: %empty_struct_type = struct_literal () [concrete = constants.%empty_struct] // CHECK:STDOUT: %.loc17_31.2: init %GenericAndParams.d3d to file.%c.var = class_init () [concrete = constants.%GenericAndParams.val.835] // CHECK:STDOUT: %.loc17_1: init %GenericAndParams.d3d = converted %.loc17_31.1, %.loc17_31.2 [concrete = constants.%GenericAndParams.val.835] // CHECK:STDOUT: assign file.%c.var, %.loc17_1 // CHECK:STDOUT: %.loc18_32.1: %empty_struct_type = struct_literal () [concrete = constants.%empty_struct] // CHECK:STDOUT: %.loc18_32.2: init %GenericNoParams.efa to file.%d.var = class_init () [concrete = constants.%GenericNoParams.val] // CHECK:STDOUT: %.loc18_1: init %GenericNoParams.efa = converted %.loc18_32.1, %.loc18_32.2 [concrete = constants.%GenericNoParams.val] // CHECK:STDOUT: assign file.%d.var, %.loc18_1 // CHECK:STDOUT: %.loc19_36.1: %empty_struct_type = struct_literal () [concrete = constants.%empty_struct] // CHECK:STDOUT: %.loc19_36.2: init %GenericAndParams.ed1 to file.%e.var = class_init () [concrete = constants.%GenericAndParams.val.e35] // CHECK:STDOUT: %.loc19_1: init %GenericAndParams.ed1 = converted %.loc19_36.1, %.loc19_36.2 [concrete = constants.%GenericAndParams.val.e35] // CHECK:STDOUT: assign file.%e.var, %.loc19_1 // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @GenericAndParams.loc6(constants.%T) { // CHECK:STDOUT: %T.loc6_24.1 => constants.%T // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @C(constants.%T) { // CHECK:STDOUT: %T.loc8_9.1 => constants.%T // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @GenericNoParams(constants.%T) {} // CHECK:STDOUT: // CHECK:STDOUT: specific @GenericAndParams.loc10(constants.%T, constants.%U) { // CHECK:STDOUT: %U.loc10_26.1 => constants.%U // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @GenericAndParams.loc6(constants.%X) { // CHECK:STDOUT: %T.loc6_24.1 => constants.%X // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @C(constants.%X) { // CHECK:STDOUT: %T.loc8_9.1 => constants.%X // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %GenericNoParams => constants.%GenericNoParams.efa // CHECK:STDOUT: %GenericAndParams.type => constants.%GenericAndParams.type.23d // CHECK:STDOUT: %GenericAndParams.generic => constants.%GenericAndParams.generic.fd2 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @GenericNoParams(constants.%X) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @GenericAndParams.loc10(constants.%X, constants.%X) { // CHECK:STDOUT: %U.loc10_26.1 => constants.%X // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- fail_non_generic_implicit_params.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %A.type: type = generic_class_type @A [concrete] // CHECK:STDOUT: %A.generic: %A.type = struct_value () [concrete] // CHECK:STDOUT: %A: type = class_type @A [concrete] // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %complete_type: = complete_type_witness %empty_struct_type [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .A = %A.decl // CHECK:STDOUT: } // CHECK:STDOUT: %A.decl: %A.type = class_decl @A [concrete = constants.%A.generic] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @A { // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%A // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- fail_non_generic_params.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %A.type: type = generic_class_type @A [concrete] // CHECK:STDOUT: %A.generic: %A.type = struct_value () [concrete] // CHECK:STDOUT: %A: type = class_type @A [concrete] // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %complete_type: = complete_type_witness %empty_struct_type [concrete] // CHECK:STDOUT: %type: type = facet_type [concrete] // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self] // CHECK:STDOUT: %pattern_type: type = pattern_type type [concrete] // CHECK:STDOUT: %T: type = symbolic_binding T, 0 [symbolic] // CHECK:STDOUT: %F.type: type = fn_type @F [concrete] // CHECK:STDOUT: %F: %F.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .A = %A.decl // CHECK:STDOUT: .F = %F.decl // CHECK:STDOUT: } // CHECK:STDOUT: %A.decl: %A.type = class_decl @A [concrete = constants.%A.generic] {} {} // CHECK:STDOUT: %F.decl: %F.type = fn_decl @F [concrete = constants.%F] { // CHECK:STDOUT: %T.patt: %pattern_type = symbolic_binding_pattern T, 0 [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %.loc11_10.1: type = splice_block %.loc11_10.2 [concrete = type] { // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %.loc11_10.2: type = type_literal type [concrete = type] // CHECK:STDOUT: } // CHECK:STDOUT: %T.loc11_6.2: type = symbolic_binding T, 0 [symbolic = %T.loc11_6.1 (constants.%T)] // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @A { // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%A // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic fn @F(%T.loc11_6.2: type) { // CHECK:STDOUT: %T.loc11_6.1: type = symbolic_binding T, 0 [symbolic = %T.loc11_6.1 (constants.%T)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: // CHECK:STDOUT: fn() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %A.ref: %A.type = name_ref A, file.%A.decl [concrete = constants.%A.generic] // CHECK:STDOUT: %T.ref: type = name_ref T, %T.loc11_6.2 [symbolic = %T.loc11_6.1 (constants.%T)] // CHECK:STDOUT: %A: type = class_type @A [concrete = constants.%A] // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @F(constants.%T) { // CHECK:STDOUT: %T.loc11_6.1 => constants.%T // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- fail_implicit_params_only_empty.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %Foo.type: type = generic_class_type @Foo [concrete] // CHECK:STDOUT: %Foo.generic: %Foo.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Foo = %Foo.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Foo.decl: %Foo.type = class_decl @Foo [concrete = constants.%Foo.generic] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @Foo; // CHECK:STDOUT: // CHECK:STDOUT: --- fail_implicit_params_only.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %type: type = facet_type [concrete] // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self] // CHECK:STDOUT: %pattern_type: type = pattern_type type [concrete] // CHECK:STDOUT: %T: type = symbolic_binding T, 0 [symbolic] // CHECK:STDOUT: %Foo.type: type = generic_class_type @Foo [concrete] // CHECK:STDOUT: %Foo.generic: %Foo.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Foo = %Foo.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Foo.decl: %Foo.type = class_decl @Foo [concrete = constants.%Foo.generic] { // CHECK:STDOUT: %T.patt: %pattern_type = symbolic_binding_pattern T, 0 [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %.loc8_15.1: type = splice_block %.loc8_15.2 [concrete = type] { // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %.loc8_15.2: type = type_literal type [concrete = type] // CHECK:STDOUT: } // CHECK:STDOUT: %T.loc8_11.2: type = symbolic_binding T, 0 [symbolic = %T.loc8_11.1 (constants.%T)] // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic class @Foo(%T.loc8_11.2: type) { // CHECK:STDOUT: %T.loc8_11.1: type = symbolic_binding T, 0 [symbolic = %T.loc8_11.1 (constants.%T)] // CHECK:STDOUT: // CHECK:STDOUT: class; // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @Foo(constants.%T) { // CHECK:STDOUT: %T.loc8_11.1 => constants.%T // CHECK:STDOUT: } // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/class/generic/import.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/int.carbon // TODO: Add ranges and switch to "--dump-sem-ir-ranges=only". // EXTRA-ARGS: --dump-sem-ir-ranges=if-present // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/class/generic/import.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/class/generic/import.carbon // --- foo.carbon library "[[@TEST_NAME]]"; class Class(T:! type); class CompleteClass(T:! type) { var n: i32; fn F() -> i32 { return 0; } } fn F() -> CompleteClass(i32); // --- foo.impl.carbon impl library "[[@TEST_NAME]]"; class Class(T:! type) { var x: T; } fn F() -> CompleteClass(i32) { return {.n = 1}; } // --- use_foo.carbon library "[[@TEST_NAME]]"; import library "foo"; fn UseMethod() -> i32 { var v: CompleteClass(i32) = F(); return v.F(); } fn UseField() -> i32 { var v: CompleteClass(i32) = F(); return v.n; } // --- fail_generic_arg_mismatch.carbon library "[[@TEST_NAME]]"; import library "foo"; fn Use() { // TODO: Include the generic arguments in the formatted type name. // CHECK:STDERR: fail_generic_arg_mismatch.carbon:[[@LINE+7]]:3: error: cannot implicitly convert expression of type `CompleteClass(i32)` to `CompleteClass(i32*)` [ConversionFailure] // CHECK:STDERR: var unused v: CompleteClass(i32*) = F(); // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: fail_generic_arg_mismatch.carbon:[[@LINE+4]]:3: note: type `CompleteClass(i32)` does not implement interface `Core.ImplicitAs(CompleteClass(i32*))` [MissingImplInMemberAccessInContext] // CHECK:STDERR: var unused v: CompleteClass(i32*) = F(); // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: var unused v: CompleteClass(i32*) = F(); } // --- fail_foo.impl.carbon impl library "[[@TEST_NAME]]"; // CHECK:STDERR: fail_foo.impl.carbon:[[@LINE+8]]:13: error: redeclaration differs at parameter 1 [RedeclParamDiffers] // CHECK:STDERR: class Class(U:! type) { // CHECK:STDERR: ^ // CHECK:STDERR: fail_foo.impl.carbon:[[@LINE-5]]:1: in import [InImport] // CHECK:STDERR: foo.carbon:4:13: note: previous declaration's corresponding parameter here [RedeclParamPrevious] // CHECK:STDERR: class Class(T:! type); // CHECK:STDERR: ^ // CHECK:STDERR: class Class(U:! type) { // CHECK:STDERR: fail_foo.impl.carbon:[[@LINE+4]]:10: error: name `T` not found [NameNotFound] // CHECK:STDERR: var x: T; // CHECK:STDERR: ^ // CHECK:STDERR: var x: T; } // CHECK:STDOUT: --- foo.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %type: type = facet_type [concrete] // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self] // CHECK:STDOUT: %pattern_type.98f: type = pattern_type type [concrete] // CHECK:STDOUT: %T: type = symbolic_binding T, 0 [symbolic] // CHECK:STDOUT: %Class.type: type = generic_class_type @Class [concrete] // CHECK:STDOUT: %Class.generic: %Class.type = struct_value () [concrete] // CHECK:STDOUT: %CompleteClass.type: type = generic_class_type @CompleteClass [concrete] // CHECK:STDOUT: %CompleteClass.generic: %CompleteClass.type = struct_value () [concrete] // CHECK:STDOUT: %CompleteClass.152: type = class_type @CompleteClass, @CompleteClass(%T) [symbolic] // CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [concrete] // CHECK:STDOUT: %Int.type: type = generic_class_type @Int [concrete] // CHECK:STDOUT: %Int.generic: %Int.type = struct_value () [concrete] // CHECK:STDOUT: %i32: type = class_type @Int, @Int(%int_32) [concrete] // CHECK:STDOUT: %CompleteClass.elem: type = unbound_element_type %CompleteClass.152, %i32 [symbolic] // CHECK:STDOUT: %.ff5: Core.Form = init_form %i32 [concrete] // CHECK:STDOUT: %pattern_type.7ce: type = pattern_type %i32 [concrete] // CHECK:STDOUT: %CompleteClass.F.type: type = fn_type @CompleteClass.F, @CompleteClass(%T) [symbolic] // CHECK:STDOUT: %CompleteClass.F: %CompleteClass.F.type = struct_value () [symbolic] // CHECK:STDOUT: %struct_type.n: type = struct_type {.n: %i32} [concrete] // CHECK:STDOUT: %complete_type.54b: = complete_type_witness %struct_type.n [concrete] // CHECK:STDOUT: %int_0.5c6: Core.IntLiteral = int_value 0 [concrete] // CHECK:STDOUT: %ImplicitAs.type.cc7: type = generic_interface_type @ImplicitAs [concrete] // CHECK:STDOUT: %ImplicitAs.generic: %ImplicitAs.type.cc7 = struct_value () [concrete] // CHECK:STDOUT: %ImplicitAs.type.e8c: type = facet_type <@ImplicitAs, @ImplicitAs(%i32)> [concrete] // CHECK:STDOUT: %To: Core.IntLiteral = symbolic_binding To, 0 [symbolic] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.4e6: type = fn_type @Core.IntLiteral.as.ImplicitAs.impl.Convert, @Core.IntLiteral.as.ImplicitAs.impl(%To) [symbolic] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.3c2: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.4e6 = struct_value () [symbolic] // CHECK:STDOUT: %ImplicitAs.impl_witness.6bc: = impl_witness imports.%ImplicitAs.impl_witness_table.74f, @Core.IntLiteral.as.ImplicitAs.impl(%int_32) [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.e0d: type = fn_type @Core.IntLiteral.as.ImplicitAs.impl.Convert, @Core.IntLiteral.as.ImplicitAs.impl(%int_32) [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.e0d = struct_value () [concrete] // CHECK:STDOUT: %ImplicitAs.facet: %ImplicitAs.type.e8c = facet_value Core.IntLiteral, (%ImplicitAs.impl_witness.6bc) [concrete] // CHECK:STDOUT: %ImplicitAs.WithSelf.Convert.type.b37: type = fn_type @ImplicitAs.WithSelf.Convert, @ImplicitAs.WithSelf(%i32, %ImplicitAs.facet) [concrete] // CHECK:STDOUT: %.545: type = fn_type_with_self_type %ImplicitAs.WithSelf.Convert.type.b37, %ImplicitAs.facet [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.bound: = bound_method %int_0.5c6, %Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5 [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn: = specific_function %Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5, @Core.IntLiteral.as.ImplicitAs.impl.Convert(%int_32) [concrete] // CHECK:STDOUT: %bound_method: = bound_method %int_0.5c6, %Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn [concrete] // CHECK:STDOUT: %int_0.6a9: %i32 = int_value 0 [concrete] // CHECK:STDOUT: %CompleteClass.d85: type = class_type @CompleteClass, @CompleteClass(%i32) [concrete] // CHECK:STDOUT: %.63d: Core.Form = init_form %CompleteClass.d85 [concrete] // CHECK:STDOUT: %pattern_type.cb5: type = pattern_type %CompleteClass.d85 [concrete] // CHECK:STDOUT: %F.type: type = fn_type @F [concrete] // CHECK:STDOUT: %F: %F.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: .Int = %Core.Int // CHECK:STDOUT: .ImplicitAs = %Core.ImplicitAs // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: %Core.Int: %Int.type = import_ref Core//prelude/parts/int, Int, loaded [concrete = constants.%Int.generic] // CHECK:STDOUT: %Core.ImplicitAs: %ImplicitAs.type.cc7 = import_ref Core//prelude/parts/as, ImplicitAs, loaded [concrete = constants.%ImplicitAs.generic] // CHECK:STDOUT: %Core.import_ref.42d: @Core.IntLiteral.as.ImplicitAs.impl.%Core.IntLiteral.as.ImplicitAs.impl.Convert.type (%Core.IntLiteral.as.ImplicitAs.impl.Convert.type.4e6) = import_ref Core//prelude/parts/int, loc{{\d+_\d+}}, loaded [symbolic = @Core.IntLiteral.as.ImplicitAs.impl.%Core.IntLiteral.as.ImplicitAs.impl.Convert (constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.3c2)] // CHECK:STDOUT: %ImplicitAs.impl_witness_table.74f = impl_witness_table (%Core.import_ref.42d), @Core.IntLiteral.as.ImplicitAs.impl [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .Class = %Class.decl // CHECK:STDOUT: .CompleteClass = %CompleteClass.decl // CHECK:STDOUT: .F = %F.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %Class.decl: %Class.type = class_decl @Class [concrete = constants.%Class.generic] { // CHECK:STDOUT: %T.patt: %pattern_type.98f = symbolic_binding_pattern T, 0 [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %.loc4_17.1: type = splice_block %.loc4_17.2 [concrete = type] { // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %.loc4_17.2: type = type_literal type [concrete = type] // CHECK:STDOUT: } // CHECK:STDOUT: %T.loc4_13.2: type = symbolic_binding T, 0 [symbolic = %T.loc4_13.1 (constants.%T)] // CHECK:STDOUT: } // CHECK:STDOUT: %CompleteClass.decl: %CompleteClass.type = class_decl @CompleteClass [concrete = constants.%CompleteClass.generic] { // CHECK:STDOUT: %T.patt: %pattern_type.98f = symbolic_binding_pattern T, 0 [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %.loc6_25.1: type = splice_block %.loc6_25.2 [concrete = type] { // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %.loc6_25.2: type = type_literal type [concrete = type] // CHECK:STDOUT: } // CHECK:STDOUT: %T.loc6_21.2: type = symbolic_binding T, 0 [symbolic = %T.loc6_21.1 (constants.%T)] // CHECK:STDOUT: } // CHECK:STDOUT: %F.decl: %F.type = fn_decl @F [concrete = constants.%F] { // CHECK:STDOUT: %return.patt: %pattern_type.cb5 = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: %pattern_type.cb5 = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %CompleteClass.ref: %CompleteClass.type = name_ref CompleteClass, file.%CompleteClass.decl [concrete = constants.%CompleteClass.generic] // CHECK:STDOUT: %i32: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %CompleteClass: type = class_type @CompleteClass, @CompleteClass(constants.%i32) [concrete = constants.%CompleteClass.d85] // CHECK:STDOUT: %.loc11: Core.Form = init_form %CompleteClass [concrete = constants.%.63d] // CHECK:STDOUT: %return.param: ref %CompleteClass.d85 = out_param call_param0 // CHECK:STDOUT: %return: ref %CompleteClass.d85 = return_slot %return.param // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic class @Class(%T.loc4_13.2: type) { // CHECK:STDOUT: %T.loc4_13.1: type = symbolic_binding T, 0 [symbolic = %T.loc4_13.1 (constants.%T)] // CHECK:STDOUT: // CHECK:STDOUT: class; // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic class @CompleteClass(%T.loc6_21.2: type) { // CHECK:STDOUT: %T.loc6_21.1: type = symbolic_binding T, 0 [symbolic = %T.loc6_21.1 (constants.%T)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %CompleteClass: type = class_type @CompleteClass, @CompleteClass(%T.loc6_21.1) [symbolic = %CompleteClass (constants.%CompleteClass.152)] // CHECK:STDOUT: %CompleteClass.elem: type = unbound_element_type %CompleteClass, constants.%i32 [symbolic = %CompleteClass.elem (constants.%CompleteClass.elem)] // CHECK:STDOUT: %CompleteClass.F.type: type = fn_type @CompleteClass.F, @CompleteClass(%T.loc6_21.1) [symbolic = %CompleteClass.F.type (constants.%CompleteClass.F.type)] // CHECK:STDOUT: %CompleteClass.F: @CompleteClass.%CompleteClass.F.type (%CompleteClass.F.type) = struct_value () [symbolic = %CompleteClass.F (constants.%CompleteClass.F)] // CHECK:STDOUT: // CHECK:STDOUT: class { // CHECK:STDOUT: %i32: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %.loc7: @CompleteClass.%CompleteClass.elem (%CompleteClass.elem) = field_decl n, element0 [concrete] // CHECK:STDOUT: %CompleteClass.F.decl: @CompleteClass.%CompleteClass.F.type (%CompleteClass.F.type) = fn_decl @CompleteClass.F [symbolic = @CompleteClass.%CompleteClass.F (constants.%CompleteClass.F)] { // CHECK:STDOUT: %return.patt: %pattern_type.7ce = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: %pattern_type.7ce = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %i32: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %.loc8_13: Core.Form = init_form %i32 [concrete = constants.%.ff5] // CHECK:STDOUT: %return.param: ref %i32 = out_param call_param0 // CHECK:STDOUT: %return: ref %i32 = return_slot %return.param // CHECK:STDOUT: } // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%struct_type.n [concrete = constants.%complete_type.54b] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%CompleteClass.152 // CHECK:STDOUT: .n = %.loc7 // CHECK:STDOUT: .F = %CompleteClass.F.decl // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic fn @CompleteClass.F(@CompleteClass.%T.loc6_21.2: type) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: // CHECK:STDOUT: fn() -> out %return.param: %i32 { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %int_0: Core.IntLiteral = int_value 0 [concrete = constants.%int_0.5c6] // CHECK:STDOUT: %impl.elem0: %.545 = impl_witness_access constants.%ImplicitAs.impl_witness.6bc, element0 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5] // CHECK:STDOUT: %bound_method.loc8_27.1: = bound_method %int_0, %impl.elem0 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.bound] // CHECK:STDOUT: %specific_fn: = specific_function %impl.elem0, @Core.IntLiteral.as.ImplicitAs.impl.Convert(constants.%int_32) [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn] // CHECK:STDOUT: %bound_method.loc8_27.2: = bound_method %int_0, %specific_fn [concrete = constants.%bound_method] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.call: init %i32 = call %bound_method.loc8_27.2(%int_0) [concrete = constants.%int_0.6a9] // CHECK:STDOUT: %.loc8_27: init %i32 = converted %int_0, %Core.IntLiteral.as.ImplicitAs.impl.Convert.call [concrete = constants.%int_0.6a9] // CHECK:STDOUT: return %.loc8_27 // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @F() -> out %return.param: %CompleteClass.d85; // CHECK:STDOUT: // CHECK:STDOUT: specific @Class(constants.%T) { // CHECK:STDOUT: %T.loc4_13.1 => constants.%T // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @CompleteClass(constants.%T) { // CHECK:STDOUT: %T.loc6_21.1 => constants.%T // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %CompleteClass => constants.%CompleteClass.152 // CHECK:STDOUT: %CompleteClass.elem => constants.%CompleteClass.elem // CHECK:STDOUT: %CompleteClass.F.type => constants.%CompleteClass.F.type // CHECK:STDOUT: %CompleteClass.F => constants.%CompleteClass.F // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @CompleteClass.F(constants.%T) {} // CHECK:STDOUT: // CHECK:STDOUT: specific @CompleteClass(constants.%i32) { // CHECK:STDOUT: %T.loc6_21.1 => constants.%i32 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- foo.impl.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %To: Core.IntLiteral = symbolic_binding To, 0 [symbolic] // CHECK:STDOUT: %Int.type: type = generic_class_type @Int [concrete] // CHECK:STDOUT: %Int.generic: %Int.type = struct_value () [concrete] // CHECK:STDOUT: %ImplicitAs.type.595: type = generic_interface_type @ImplicitAs [concrete] // CHECK:STDOUT: %ImplicitAs.generic: %ImplicitAs.type.595 = struct_value () [concrete] // CHECK:STDOUT: %pattern_type.98f: type = pattern_type type [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.3b3: type = fn_type @Core.IntLiteral.as.ImplicitAs.impl.Convert, @Core.IntLiteral.as.ImplicitAs.impl(%To) [symbolic] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.ba2: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.3b3 = struct_value () [symbolic] // CHECK:STDOUT: %type: type = facet_type [concrete] // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self] // CHECK:STDOUT: %T: type = symbolic_binding T, 0 [symbolic] // CHECK:STDOUT: %Class.type: type = generic_class_type @Class [concrete] // CHECK:STDOUT: %Class.generic: %Class.type = struct_value () [concrete] // CHECK:STDOUT: %Class: type = class_type @Class, @Class(%T) [symbolic] // CHECK:STDOUT: %require_complete.944: = require_complete_type %T [symbolic] // CHECK:STDOUT: %Class.elem: type = unbound_element_type %Class, %T [symbolic] // CHECK:STDOUT: %struct_type.x: type = struct_type {.x: %T} [symbolic] // CHECK:STDOUT: %complete_type.735: = complete_type_witness %struct_type.x [symbolic] // CHECK:STDOUT: %CompleteClass.type: type = generic_class_type @CompleteClass [concrete] // CHECK:STDOUT: %CompleteClass.generic: %CompleteClass.type = struct_value () [concrete] // CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [concrete] // CHECK:STDOUT: %i32: type = class_type @Int, @Int(%int_32) [concrete] // CHECK:STDOUT: %struct_type.n.4d6: type = struct_type {.n: %i32} [concrete] // CHECK:STDOUT: %complete_type.a68: = complete_type_witness %struct_type.n.4d6 [concrete] // CHECK:STDOUT: %CompleteClass.152: type = class_type @CompleteClass, @CompleteClass(%T) [symbolic] // CHECK:STDOUT: %CompleteClass.F.type.6b4: type = fn_type @CompleteClass.F, @CompleteClass(%T) [symbolic] // CHECK:STDOUT: %CompleteClass.F.5ed: %CompleteClass.F.type.6b4 = struct_value () [symbolic] // CHECK:STDOUT: %CompleteClass.elem.2b9: type = unbound_element_type %CompleteClass.152, %i32 [symbolic] // CHECK:STDOUT: %CompleteClass.667: type = class_type @CompleteClass, @CompleteClass(%i32) [concrete] // CHECK:STDOUT: %.bdc: Core.Form = init_form %CompleteClass.667 [concrete] // CHECK:STDOUT: %pattern_type.b91: type = pattern_type %CompleteClass.667 [concrete] // CHECK:STDOUT: %F.type: type = fn_type @F [concrete] // CHECK:STDOUT: %F: %F.type = struct_value () [concrete] // CHECK:STDOUT: %CompleteClass.elem.995: type = unbound_element_type %CompleteClass.667, %i32 [concrete] // CHECK:STDOUT: %CompleteClass.F.type.111: type = fn_type @CompleteClass.F, @CompleteClass(%i32) [concrete] // CHECK:STDOUT: %CompleteClass.F.3a7: %CompleteClass.F.type.111 = struct_value () [concrete] // CHECK:STDOUT: %int_1.5b8: Core.IntLiteral = int_value 1 [concrete] // CHECK:STDOUT: %struct_type.n.44a: type = struct_type {.n: Core.IntLiteral} [concrete] // CHECK:STDOUT: %struct: %struct_type.n.44a = struct_value (%int_1.5b8) [concrete] // CHECK:STDOUT: %ImplicitAs.type.ea1: type = facet_type <@ImplicitAs, @ImplicitAs(%i32)> [concrete] // CHECK:STDOUT: %ImplicitAs.impl_witness.574: = impl_witness imports.%ImplicitAs.impl_witness_table.4e9, @Core.IntLiteral.as.ImplicitAs.impl(%int_32) [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.dbb: type = fn_type @Core.IntLiteral.as.ImplicitAs.impl.Convert, @Core.IntLiteral.as.ImplicitAs.impl(%int_32) [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.022: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.dbb = struct_value () [concrete] // CHECK:STDOUT: %ImplicitAs.facet: %ImplicitAs.type.ea1 = facet_value Core.IntLiteral, (%ImplicitAs.impl_witness.574) [concrete] // CHECK:STDOUT: %ImplicitAs.WithSelf.Convert.type.7cd6: type = fn_type @ImplicitAs.WithSelf.Convert, @ImplicitAs.WithSelf(%i32, %ImplicitAs.facet) [concrete] // CHECK:STDOUT: %.d94: type = fn_type_with_self_type %ImplicitAs.WithSelf.Convert.type.7cd6, %ImplicitAs.facet [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.bound: = bound_method %int_1.5b8, %Core.IntLiteral.as.ImplicitAs.impl.Convert.022 [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn: = specific_function %Core.IntLiteral.as.ImplicitAs.impl.Convert.022, @Core.IntLiteral.as.ImplicitAs.impl.Convert(%int_32) [concrete] // CHECK:STDOUT: %bound_method: = bound_method %int_1.5b8, %Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn [concrete] // CHECK:STDOUT: %int_1.47b: %i32 = int_value 1 [concrete] // CHECK:STDOUT: %CompleteClass.val: %CompleteClass.667 = struct_value (%int_1.47b) [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Main.CompleteClass: %CompleteClass.type = import_ref Main//foo, CompleteClass, loaded [concrete = constants.%CompleteClass.generic] // CHECK:STDOUT: %Core.ece: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: .Int = %Core.Int // CHECK:STDOUT: .ImplicitAs = %Core.ImplicitAs // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: %Main.import_ref.e6d: @Core.IntLiteral.as.ImplicitAs.impl.%Core.IntLiteral.as.ImplicitAs.impl.Convert.type (%Core.IntLiteral.as.ImplicitAs.impl.Convert.type.3b3) = import_ref Main//foo, inst{{[0-9A-F]+}} [indirect], loaded [symbolic = @Core.IntLiteral.as.ImplicitAs.impl.%Core.IntLiteral.as.ImplicitAs.impl.Convert (constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.ba2)] // CHECK:STDOUT: %ImplicitAs.impl_witness_table.4e9 = impl_witness_table (%Main.import_ref.e6d), @Core.IntLiteral.as.ImplicitAs.impl [concrete] // CHECK:STDOUT: %Main.import_ref.b3bc94.1: type = import_ref Main//foo, loc4_13, loaded [symbolic = @Class.%T.1 (constants.%T)] // CHECK:STDOUT: %Main.import_ref.b3bc94.2: type = import_ref Main//foo, loc6_21, loaded [symbolic = @CompleteClass.%T (constants.%T)] // CHECK:STDOUT: %Main.import_ref.eb1: = import_ref Main//foo, loc9_1, loaded [concrete = constants.%complete_type.a68] // CHECK:STDOUT: %Main.import_ref.09e = import_ref Main//foo, inst{{[0-9A-F]+}} [no loc], unloaded // CHECK:STDOUT: %Main.import_ref.1bb = import_ref Main//foo, loc7_8, unloaded // CHECK:STDOUT: %Main.import_ref.469 = import_ref Main//foo, loc8_17, unloaded // CHECK:STDOUT: %Main.import_ref.b3bc94.3: type = import_ref Main//foo, loc6_21, loaded [symbolic = @CompleteClass.%T (constants.%T)] // CHECK:STDOUT: %Core.Int: %Int.type = import_ref Core//prelude/parts/int, Int, loaded [concrete = constants.%Int.generic] // CHECK:STDOUT: %Core.ImplicitAs: %ImplicitAs.type.595 = import_ref Core//prelude/parts/as, ImplicitAs, loaded [concrete = constants.%ImplicitAs.generic] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Class = %Class.decl // CHECK:STDOUT: .CompleteClass = imports.%Main.CompleteClass // CHECK:STDOUT: .F = %F.decl // CHECK:STDOUT: .Core = imports.%Core.ece // CHECK:STDOUT: } // CHECK:STDOUT: %default.import.loc2_19.1 = import // CHECK:STDOUT: %default.import.loc2_19.2 = import // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %Class.decl: %Class.type = class_decl @Class [concrete = constants.%Class.generic] { // CHECK:STDOUT: %T.patt: %pattern_type.98f = symbolic_binding_pattern T, 0 [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %.loc4_17.1: type = splice_block %.loc4_17.2 [concrete = type] { // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %.loc4_17.2: type = type_literal type [concrete = type] // CHECK:STDOUT: } // CHECK:STDOUT: %T.loc4: type = symbolic_binding T, 0 [symbolic = %T.1 (constants.%T)] // CHECK:STDOUT: } // CHECK:STDOUT: %F.decl: %F.type = fn_decl @F [concrete = constants.%F] { // CHECK:STDOUT: %return.patt: %pattern_type.b91 = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: %pattern_type.b91 = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %CompleteClass.ref: %CompleteClass.type = name_ref CompleteClass, imports.%Main.CompleteClass [concrete = constants.%CompleteClass.generic] // CHECK:STDOUT: %i32: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %CompleteClass: type = class_type @CompleteClass, @CompleteClass(constants.%i32) [concrete = constants.%CompleteClass.667] // CHECK:STDOUT: %.loc8: Core.Form = init_form %CompleteClass [concrete = constants.%.bdc] // CHECK:STDOUT: %return.param: ref %CompleteClass.667 = out_param call_param0 // CHECK:STDOUT: %return: ref %CompleteClass.667 = return_slot %return.param // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic class @Class(imports.%Main.import_ref.b3bc94.1: type) { // CHECK:STDOUT: %T.1: type = symbolic_binding T, 0 [symbolic = %T.1 (constants.%T)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete: = require_complete_type %T.1 [symbolic = %require_complete (constants.%require_complete.944)] // CHECK:STDOUT: %Class: type = class_type @Class, @Class(%T.1) [symbolic = %Class (constants.%Class)] // CHECK:STDOUT: %Class.elem: type = unbound_element_type %Class, %T.1 [symbolic = %Class.elem (constants.%Class.elem)] // CHECK:STDOUT: %struct_type.x: type = struct_type {.x: @Class.%T.1 (%T)} [symbolic = %struct_type.x (constants.%struct_type.x)] // CHECK:STDOUT: %complete_type.loc6_1.2: = complete_type_witness %struct_type.x [symbolic = %complete_type.loc6_1.2 (constants.%complete_type.735)] // CHECK:STDOUT: // CHECK:STDOUT: class { // CHECK:STDOUT: %T.ref: type = name_ref T, %T.loc4 [symbolic = %T.1 (constants.%T)] // CHECK:STDOUT: %.loc5: @Class.%Class.elem (%Class.elem) = field_decl x, element0 [concrete] // CHECK:STDOUT: %complete_type.loc6_1.1: = complete_type_witness constants.%struct_type.x [symbolic = %complete_type.loc6_1.2 (constants.%complete_type.735)] // CHECK:STDOUT: complete_type_witness = %complete_type.loc6_1.1 // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%Class // CHECK:STDOUT: .T = // CHECK:STDOUT: .x = %.loc5 // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic class @CompleteClass(imports.%Main.import_ref.b3bc94.3: type) [from "foo.carbon"] { // CHECK:STDOUT: %T: type = symbolic_binding T, 0 [symbolic = %T (constants.%T)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %CompleteClass: type = class_type @CompleteClass, @CompleteClass(%T) [symbolic = %CompleteClass (constants.%CompleteClass.152)] // CHECK:STDOUT: %CompleteClass.elem: type = unbound_element_type %CompleteClass, constants.%i32 [symbolic = %CompleteClass.elem (constants.%CompleteClass.elem.2b9)] // CHECK:STDOUT: %CompleteClass.F.type: type = fn_type @CompleteClass.F, @CompleteClass(%T) [symbolic = %CompleteClass.F.type (constants.%CompleteClass.F.type.6b4)] // CHECK:STDOUT: %CompleteClass.F: @CompleteClass.%CompleteClass.F.type (%CompleteClass.F.type.6b4) = struct_value () [symbolic = %CompleteClass.F (constants.%CompleteClass.F.5ed)] // CHECK:STDOUT: // CHECK:STDOUT: class { // CHECK:STDOUT: complete_type_witness = imports.%Main.import_ref.eb1 // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = imports.%Main.import_ref.09e // CHECK:STDOUT: .n = imports.%Main.import_ref.1bb // CHECK:STDOUT: .F = imports.%Main.import_ref.469 // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic fn @CompleteClass.F(imports.%Main.import_ref.b3bc94.2: type) [from "foo.carbon"] { // CHECK:STDOUT: !definition: // CHECK:STDOUT: // CHECK:STDOUT: fn; // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @F() -> out %return.param: %CompleteClass.667 [from "foo.carbon"] { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %int_1: Core.IntLiteral = int_value 1 [concrete = constants.%int_1.5b8] // CHECK:STDOUT: %.loc9_17.1: %struct_type.n.44a = struct_literal (%int_1) [concrete = constants.%struct] // CHECK:STDOUT: %impl.elem0: %.d94 = impl_witness_access constants.%ImplicitAs.impl_witness.574, element0 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.022] // CHECK:STDOUT: %bound_method.loc9_17.1: = bound_method %int_1, %impl.elem0 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.bound] // CHECK:STDOUT: %specific_fn: = specific_function %impl.elem0, @Core.IntLiteral.as.ImplicitAs.impl.Convert(constants.%int_32) [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn] // CHECK:STDOUT: %bound_method.loc9_17.2: = bound_method %int_1, %specific_fn [concrete = constants.%bound_method] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.call: init %i32 = call %bound_method.loc9_17.2(%int_1) [concrete = constants.%int_1.47b] // CHECK:STDOUT: %.loc9_17.2: init %i32 = converted %int_1, %Core.IntLiteral.as.ImplicitAs.impl.Convert.call [concrete = constants.%int_1.47b] // CHECK:STDOUT: %.loc9_17.3: ref %i32 = class_element_access %return.param, element0 // CHECK:STDOUT: %.loc9_17.4: init %i32 to %.loc9_17.3 = in_place_init %.loc9_17.2 [concrete = constants.%int_1.47b] // CHECK:STDOUT: %.loc9_17.5: init %CompleteClass.667 to %return.param = class_init (%.loc9_17.4) [concrete = constants.%CompleteClass.val] // CHECK:STDOUT: %.loc9_18: init %CompleteClass.667 = converted %.loc9_17.1, %.loc9_17.5 [concrete = constants.%CompleteClass.val] // CHECK:STDOUT: return %.loc9_18 to %return.param // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @Class(constants.%T) { // CHECK:STDOUT: %T.1 => constants.%T // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @CompleteClass(constants.%T) { // CHECK:STDOUT: %T => constants.%T // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %CompleteClass => constants.%CompleteClass.152 // CHECK:STDOUT: %CompleteClass.elem => constants.%CompleteClass.elem.2b9 // CHECK:STDOUT: %CompleteClass.F.type => constants.%CompleteClass.F.type.6b4 // CHECK:STDOUT: %CompleteClass.F => constants.%CompleteClass.F.5ed // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @CompleteClass.F(constants.%T) {} // CHECK:STDOUT: // CHECK:STDOUT: specific @CompleteClass(constants.%i32) { // CHECK:STDOUT: %T => constants.%i32 // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %CompleteClass => constants.%CompleteClass.667 // CHECK:STDOUT: %CompleteClass.elem => constants.%CompleteClass.elem.995 // CHECK:STDOUT: %CompleteClass.F.type => constants.%CompleteClass.F.type.111 // CHECK:STDOUT: %CompleteClass.F => constants.%CompleteClass.F.3a7 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- use_foo.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [concrete] // CHECK:STDOUT: %Int.type: type = generic_class_type @Int [concrete] // CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] // CHECK:STDOUT: %Int.generic: %Int.type = struct_value () [concrete] // CHECK:STDOUT: %N: Core.IntLiteral = symbolic_binding N, 0 [symbolic] // CHECK:STDOUT: %i32: type = class_type @Int, @Int(%int_32) [concrete] // CHECK:STDOUT: %.ff5: Core.Form = init_form %i32 [concrete] // CHECK:STDOUT: %pattern_type.7ce: type = pattern_type %i32 [concrete] // CHECK:STDOUT: %UseMethod.type: type = fn_type @UseMethod [concrete] // CHECK:STDOUT: %UseMethod: %UseMethod.type = struct_value () [concrete] // CHECK:STDOUT: %CompleteClass.type: type = generic_class_type @CompleteClass [concrete] // CHECK:STDOUT: %CompleteClass.generic: %CompleteClass.type = struct_value () [concrete] // CHECK:STDOUT: %struct_type.n: type = struct_type {.n: %i32} [concrete] // CHECK:STDOUT: %complete_type.54b: = complete_type_witness %struct_type.n [concrete] // CHECK:STDOUT: %T.67d: type = symbolic_binding T, 0 [symbolic] // CHECK:STDOUT: %CompleteClass.152: type = class_type @CompleteClass, @CompleteClass(%T.67d) [symbolic] // CHECK:STDOUT: %CompleteClass.F.type.6b4: type = fn_type @CompleteClass.F, @CompleteClass(%T.67d) [symbolic] // CHECK:STDOUT: %CompleteClass.F.5ed: %CompleteClass.F.type.6b4 = struct_value () [symbolic] // CHECK:STDOUT: %CompleteClass.elem.4f4: type = unbound_element_type %CompleteClass.152, %i32 [symbolic] // CHECK:STDOUT: %CompleteClass.d85: type = class_type @CompleteClass, @CompleteClass(%i32) [concrete] // CHECK:STDOUT: %CompleteClass.elem.2bc: type = unbound_element_type %CompleteClass.d85, %i32 [concrete] // CHECK:STDOUT: %CompleteClass.F.type.942: type = fn_type @CompleteClass.F, @CompleteClass(%i32) [concrete] // CHECK:STDOUT: %CompleteClass.F.456: %CompleteClass.F.type.942 = struct_value () [concrete] // CHECK:STDOUT: %pattern_type.cb5: type = pattern_type %CompleteClass.d85 [concrete] // CHECK:STDOUT: %F.type: type = fn_type @F [concrete] // CHECK:STDOUT: %F: %F.type = struct_value () [concrete] // CHECK:STDOUT: %CompleteClass.F.specific_fn: = specific_function %CompleteClass.F.456, @CompleteClass.F(%i32) [concrete] // CHECK:STDOUT: %Destroy.type: type = facet_type <@Destroy> [concrete] // CHECK:STDOUT: %Destroy.Op.type: type = fn_type @Destroy.Op [concrete] // CHECK:STDOUT: %Destroy.Op: %Destroy.Op.type = struct_value () [concrete] // CHECK:STDOUT: %UseField.type: type = fn_type @UseField [concrete] // CHECK:STDOUT: %UseField: %UseField.type = struct_value () [concrete] // CHECK:STDOUT: %Copy.type: type = facet_type <@Copy> [concrete] // CHECK:STDOUT: %Int.as.Copy.impl.Op.type.824: type = fn_type @Int.as.Copy.impl.Op, @Int.as.Copy.impl(%N) [symbolic] // CHECK:STDOUT: %Int.as.Copy.impl.Op.9b9: %Int.as.Copy.impl.Op.type.824 = struct_value () [symbolic] // CHECK:STDOUT: %Copy.impl_witness.f17: = impl_witness imports.%Copy.impl_witness_table.e76, @Int.as.Copy.impl(%int_32) [concrete] // CHECK:STDOUT: %Int.as.Copy.impl.Op.type.546: type = fn_type @Int.as.Copy.impl.Op, @Int.as.Copy.impl(%int_32) [concrete] // CHECK:STDOUT: %Int.as.Copy.impl.Op.664: %Int.as.Copy.impl.Op.type.546 = struct_value () [concrete] // CHECK:STDOUT: %Copy.facet: %Copy.type = facet_value %i32, (%Copy.impl_witness.f17) [concrete] // CHECK:STDOUT: %Copy.WithSelf.Op.type.081: type = fn_type @Copy.WithSelf.Op, @Copy.WithSelf(%Copy.facet) [concrete] // CHECK:STDOUT: %.8e2: type = fn_type_with_self_type %Copy.WithSelf.Op.type.081, %Copy.facet [concrete] // CHECK:STDOUT: %Int.as.Copy.impl.Op.specific_fn: = specific_function %Int.as.Copy.impl.Op.664, @Int.as.Copy.impl.Op(%int_32) [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Main.Class = import_ref Main//foo, Class, unloaded // CHECK:STDOUT: %Main.CompleteClass: %CompleteClass.type = import_ref Main//foo, CompleteClass, loaded [concrete = constants.%CompleteClass.generic] // CHECK:STDOUT: %Main.F: %F.type = import_ref Main//foo, F, loaded [concrete = constants.%F] // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: .Int = %Core.Int // CHECK:STDOUT: .Destroy = %Core.Destroy // CHECK:STDOUT: .Copy = %Core.Copy // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: %Core.Int: %Int.type = import_ref Core//prelude/parts/int, Int, loaded [concrete = constants.%Int.generic] // CHECK:STDOUT: %Main.import_ref.b3bc94.1: type = import_ref Main//foo, loc6_21, loaded [symbolic = @CompleteClass.%T (constants.%T.67d)] // CHECK:STDOUT: %Main.import_ref.eb1: = import_ref Main//foo, loc9_1, loaded [concrete = constants.%complete_type.54b] // CHECK:STDOUT: %Main.import_ref.09e = import_ref Main//foo, inst{{[0-9A-F]+}} [no loc], unloaded // CHECK:STDOUT: %Main.import_ref.a08: @CompleteClass.%CompleteClass.elem (%CompleteClass.elem.4f4) = import_ref Main//foo, loc7_8, loaded [concrete = %.744] // CHECK:STDOUT: %Main.import_ref.577: @CompleteClass.%CompleteClass.F.type (%CompleteClass.F.type.6b4) = import_ref Main//foo, loc8_17, loaded [symbolic = @CompleteClass.%CompleteClass.F (constants.%CompleteClass.F.5ed)] // CHECK:STDOUT: %Main.import_ref.b3bc94.2: type = import_ref Main//foo, loc6_21, loaded [symbolic = @CompleteClass.%T (constants.%T.67d)] // CHECK:STDOUT: %Core.Destroy: type = import_ref Core//prelude/parts/destroy, Destroy, loaded [concrete = constants.%Destroy.type] // CHECK:STDOUT: %.744: @CompleteClass.%CompleteClass.elem (%CompleteClass.elem.4f4) = field_decl n, element0 [concrete] // CHECK:STDOUT: %Core.Copy: type = import_ref Core//prelude/parts/copy, Copy, loaded [concrete = constants.%Copy.type] // CHECK:STDOUT: %Core.import_ref.18d: @Int.as.Copy.impl.%Int.as.Copy.impl.Op.type (%Int.as.Copy.impl.Op.type.824) = import_ref Core//prelude/parts/int, loc{{\d+_\d+}}, loaded [symbolic = @Int.as.Copy.impl.%Int.as.Copy.impl.Op (constants.%Int.as.Copy.impl.Op.9b9)] // CHECK:STDOUT: %Copy.impl_witness_table.e76 = impl_witness_table (%Core.import_ref.18d), @Int.as.Copy.impl [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Class = imports.%Main.Class // CHECK:STDOUT: .CompleteClass = imports.%Main.CompleteClass // CHECK:STDOUT: .F = imports.%Main.F // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .UseMethod = %UseMethod.decl // CHECK:STDOUT: .UseField = %UseField.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %default.import = import // CHECK:STDOUT: %UseMethod.decl: %UseMethod.type = fn_decl @UseMethod [concrete = constants.%UseMethod] { // CHECK:STDOUT: %return.patt: %pattern_type.7ce = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: %pattern_type.7ce = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %i32.loc5: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %.loc5: Core.Form = init_form %i32.loc5 [concrete = constants.%.ff5] // CHECK:STDOUT: %return.param: ref %i32 = out_param call_param0 // CHECK:STDOUT: %return: ref %i32 = return_slot %return.param // CHECK:STDOUT: } // CHECK:STDOUT: %UseField.decl: %UseField.type = fn_decl @UseField [concrete = constants.%UseField] { // CHECK:STDOUT: %return.patt: %pattern_type.7ce = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: %pattern_type.7ce = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %i32.loc10: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %.loc10: Core.Form = init_form %i32.loc10 [concrete = constants.%.ff5] // CHECK:STDOUT: %return.param: ref %i32 = out_param call_param0 // CHECK:STDOUT: %return: ref %i32 = return_slot %return.param // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic class @CompleteClass(imports.%Main.import_ref.b3bc94.2: type) [from "foo.carbon"] { // CHECK:STDOUT: %T: type = symbolic_binding T, 0 [symbolic = %T (constants.%T.67d)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %CompleteClass: type = class_type @CompleteClass, @CompleteClass(%T) [symbolic = %CompleteClass (constants.%CompleteClass.152)] // CHECK:STDOUT: %CompleteClass.elem: type = unbound_element_type %CompleteClass, constants.%i32 [symbolic = %CompleteClass.elem (constants.%CompleteClass.elem.4f4)] // CHECK:STDOUT: %CompleteClass.F.type: type = fn_type @CompleteClass.F, @CompleteClass(%T) [symbolic = %CompleteClass.F.type (constants.%CompleteClass.F.type.6b4)] // CHECK:STDOUT: %CompleteClass.F: @CompleteClass.%CompleteClass.F.type (%CompleteClass.F.type.6b4) = struct_value () [symbolic = %CompleteClass.F (constants.%CompleteClass.F.5ed)] // CHECK:STDOUT: // CHECK:STDOUT: class { // CHECK:STDOUT: complete_type_witness = imports.%Main.import_ref.eb1 // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = imports.%Main.import_ref.09e // CHECK:STDOUT: .n = imports.%Main.import_ref.a08 // CHECK:STDOUT: .F = imports.%Main.import_ref.577 // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @UseMethod() -> out %return.param: %i32 { // CHECK:STDOUT: !entry: // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %v.patt: %pattern_type.cb5 = ref_binding_pattern v [concrete] // CHECK:STDOUT: %v.var_patt: %pattern_type.cb5 = var_pattern %v.patt [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %v.var: ref %CompleteClass.d85 = var %v.var_patt // CHECK:STDOUT: %F.ref.loc6: %F.type = name_ref F, imports.%Main.F [concrete = constants.%F] // CHECK:STDOUT: %.loc6_3: ref %CompleteClass.d85 = splice_block %v.var {} // CHECK:STDOUT: %F.call: init %CompleteClass.d85 to %.loc6_3 = call %F.ref.loc6() // CHECK:STDOUT: assign %v.var, %F.call // CHECK:STDOUT: %.loc6_27: type = splice_block %CompleteClass [concrete = constants.%CompleteClass.d85] { // CHECK:STDOUT: %CompleteClass.ref: %CompleteClass.type = name_ref CompleteClass, imports.%Main.CompleteClass [concrete = constants.%CompleteClass.generic] // CHECK:STDOUT: %i32.loc6: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %CompleteClass: type = class_type @CompleteClass, @CompleteClass(constants.%i32) [concrete = constants.%CompleteClass.d85] // CHECK:STDOUT: } // CHECK:STDOUT: %v: ref %CompleteClass.d85 = ref_binding v, %v.var // CHECK:STDOUT: %v.ref: ref %CompleteClass.d85 = name_ref v, %v // CHECK:STDOUT: %.loc7: %CompleteClass.F.type.942 = specific_constant imports.%Main.import_ref.577, @CompleteClass(constants.%i32) [concrete = constants.%CompleteClass.F.456] // CHECK:STDOUT: %F.ref.loc7: %CompleteClass.F.type.942 = name_ref F, %.loc7 [concrete = constants.%CompleteClass.F.456] // CHECK:STDOUT: %CompleteClass.F.specific_fn: = specific_function %F.ref.loc7, @CompleteClass.F(constants.%i32) [concrete = constants.%CompleteClass.F.specific_fn] // CHECK:STDOUT: %CompleteClass.F.call: init %i32 = call %CompleteClass.F.specific_fn() // CHECK:STDOUT: %Destroy.Op.bound: = bound_method %v.var, constants.%Destroy.Op // CHECK:STDOUT: %Destroy.Op.call: init %empty_tuple.type = call %Destroy.Op.bound(%v.var) // CHECK:STDOUT: return %CompleteClass.F.call // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic fn @CompleteClass.F(imports.%Main.import_ref.b3bc94.1: type) [from "foo.carbon"] { // CHECK:STDOUT: !definition: // CHECK:STDOUT: // CHECK:STDOUT: fn; // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @F [from "foo.carbon"]; // CHECK:STDOUT: // CHECK:STDOUT: fn @Destroy.Op(%self.param: ref %CompleteClass.d85) = "no_op"; // CHECK:STDOUT: // CHECK:STDOUT: fn @UseField() -> out %return.param: %i32 { // CHECK:STDOUT: !entry: // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %v.patt: %pattern_type.cb5 = ref_binding_pattern v [concrete] // CHECK:STDOUT: %v.var_patt: %pattern_type.cb5 = var_pattern %v.patt [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %v.var: ref %CompleteClass.d85 = var %v.var_patt // CHECK:STDOUT: %F.ref: %F.type = name_ref F, imports.%Main.F [concrete = constants.%F] // CHECK:STDOUT: %.loc11_3: ref %CompleteClass.d85 = splice_block %v.var {} // CHECK:STDOUT: %F.call: init %CompleteClass.d85 to %.loc11_3 = call %F.ref() // CHECK:STDOUT: assign %v.var, %F.call // CHECK:STDOUT: %.loc11_27: type = splice_block %CompleteClass [concrete = constants.%CompleteClass.d85] { // CHECK:STDOUT: %CompleteClass.ref: %CompleteClass.type = name_ref CompleteClass, imports.%Main.CompleteClass [concrete = constants.%CompleteClass.generic] // CHECK:STDOUT: %i32.loc11: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %CompleteClass: type = class_type @CompleteClass, @CompleteClass(constants.%i32) [concrete = constants.%CompleteClass.d85] // CHECK:STDOUT: } // CHECK:STDOUT: %v: ref %CompleteClass.d85 = ref_binding v, %v.var // CHECK:STDOUT: %v.ref: ref %CompleteClass.d85 = name_ref v, %v // CHECK:STDOUT: %n.ref: %CompleteClass.elem.2bc = name_ref n, imports.%Main.import_ref.a08 [concrete = imports.%.744] // CHECK:STDOUT: %.loc12_11.1: ref %i32 = class_element_access %v.ref, element0 // CHECK:STDOUT: %.loc12_11.2: %i32 = acquire_value %.loc12_11.1 // CHECK:STDOUT: %impl.elem0: %.8e2 = impl_witness_access constants.%Copy.impl_witness.f17, element0 [concrete = constants.%Int.as.Copy.impl.Op.664] // CHECK:STDOUT: %bound_method.loc12_11.1: = bound_method %.loc12_11.2, %impl.elem0 // CHECK:STDOUT: %specific_fn: = specific_function %impl.elem0, @Int.as.Copy.impl.Op(constants.%int_32) [concrete = constants.%Int.as.Copy.impl.Op.specific_fn] // CHECK:STDOUT: %bound_method.loc12_11.2: = bound_method %.loc12_11.2, %specific_fn // CHECK:STDOUT: %Int.as.Copy.impl.Op.call: init %i32 = call %bound_method.loc12_11.2(%.loc12_11.2) // CHECK:STDOUT: %Destroy.Op.bound: = bound_method %v.var, constants.%Destroy.Op // CHECK:STDOUT: %Destroy.Op.call: init %empty_tuple.type = call %Destroy.Op.bound(%v.var) // CHECK:STDOUT: return %Int.as.Copy.impl.Op.call // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @CompleteClass(constants.%T.67d) { // CHECK:STDOUT: %T => constants.%T.67d // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %CompleteClass => constants.%CompleteClass.152 // CHECK:STDOUT: %CompleteClass.elem => constants.%CompleteClass.elem.4f4 // CHECK:STDOUT: %CompleteClass.F.type => constants.%CompleteClass.F.type.6b4 // CHECK:STDOUT: %CompleteClass.F => constants.%CompleteClass.F.5ed // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @CompleteClass.F(constants.%T.67d) {} // CHECK:STDOUT: // CHECK:STDOUT: specific @CompleteClass(constants.%i32) { // CHECK:STDOUT: %T => constants.%i32 // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %CompleteClass => constants.%CompleteClass.d85 // CHECK:STDOUT: %CompleteClass.elem => constants.%CompleteClass.elem.2bc // CHECK:STDOUT: %CompleteClass.F.type => constants.%CompleteClass.F.type.942 // CHECK:STDOUT: %CompleteClass.F => constants.%CompleteClass.F.456 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @CompleteClass.F(constants.%i32) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- fail_generic_arg_mismatch.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %Use.type: type = fn_type @Use [concrete] // CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] // CHECK:STDOUT: %Use: %Use.type = struct_value () [concrete] // CHECK:STDOUT: %CompleteClass.type: type = generic_class_type @CompleteClass [concrete] // CHECK:STDOUT: %CompleteClass.generic: %CompleteClass.type = struct_value () [concrete] // CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [concrete] // CHECK:STDOUT: %Int.type: type = generic_class_type @Int [concrete] // CHECK:STDOUT: %Int.generic: %Int.type = struct_value () [concrete] // CHECK:STDOUT: %i32: type = class_type @Int, @Int(%int_32) [concrete] // CHECK:STDOUT: %struct_type.n: type = struct_type {.n: %i32} [concrete] // CHECK:STDOUT: %complete_type.a68: = complete_type_witness %struct_type.n [concrete] // CHECK:STDOUT: %T: type = symbolic_binding T, 0 [symbolic] // CHECK:STDOUT: %CompleteClass.152: type = class_type @CompleteClass, @CompleteClass(%T) [symbolic] // CHECK:STDOUT: %CompleteClass.F.type.6b4: type = fn_type @CompleteClass.F, @CompleteClass(%T) [symbolic] // CHECK:STDOUT: %CompleteClass.F.5ed: %CompleteClass.F.type.6b4 = struct_value () [symbolic] // CHECK:STDOUT: %CompleteClass.elem.2b9: type = unbound_element_type %CompleteClass.152, %i32 [symbolic] // CHECK:STDOUT: %ptr.9e1: type = ptr_type %i32 [concrete] // CHECK:STDOUT: %CompleteClass.582: type = class_type @CompleteClass, @CompleteClass(%ptr.9e1) [concrete] // CHECK:STDOUT: %CompleteClass.elem.166: type = unbound_element_type %CompleteClass.582, %i32 [concrete] // CHECK:STDOUT: %CompleteClass.F.type.160: type = fn_type @CompleteClass.F, @CompleteClass(%ptr.9e1) [concrete] // CHECK:STDOUT: %CompleteClass.F.f06: %CompleteClass.F.type.160 = struct_value () [concrete] // CHECK:STDOUT: %pattern_type.a6d: type = pattern_type %CompleteClass.582 [concrete] // CHECK:STDOUT: %F.type: type = fn_type @F [concrete] // CHECK:STDOUT: %F: %F.type = struct_value () [concrete] // CHECK:STDOUT: %CompleteClass.667: type = class_type @CompleteClass, @CompleteClass(%i32) [concrete] // CHECK:STDOUT: %CompleteClass.elem.995: type = unbound_element_type %CompleteClass.667, %i32 [concrete] // CHECK:STDOUT: %CompleteClass.F.type.111: type = fn_type @CompleteClass.F, @CompleteClass(%i32) [concrete] // CHECK:STDOUT: %CompleteClass.F.3a7: %CompleteClass.F.type.111 = struct_value () [concrete] // CHECK:STDOUT: %ImplicitAs.type.cc7: type = generic_interface_type @ImplicitAs [concrete] // CHECK:STDOUT: %ImplicitAs.generic: %ImplicitAs.type.cc7 = struct_value () [concrete] // CHECK:STDOUT: %Destroy.type: type = facet_type <@Destroy> [concrete] // CHECK:STDOUT: %Destroy.Op.type: type = fn_type @Destroy.Op [concrete] // CHECK:STDOUT: %Destroy.Op: %Destroy.Op.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Main.Class = import_ref Main//foo, Class, unloaded // CHECK:STDOUT: %Main.CompleteClass: %CompleteClass.type = import_ref Main//foo, CompleteClass, loaded [concrete = constants.%CompleteClass.generic] // CHECK:STDOUT: %Main.F: %F.type = import_ref Main//foo, F, loaded [concrete = constants.%F] // CHECK:STDOUT: %Core.ece: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: .Int = %Core.Int // CHECK:STDOUT: .ImplicitAs = %Core.ImplicitAs // CHECK:STDOUT: .Destroy = %Core.Destroy // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: %Main.import_ref.b3bc94.1: type = import_ref Main//foo, loc6_21, loaded [symbolic = @CompleteClass.%T (constants.%T)] // CHECK:STDOUT: %Main.import_ref.eb1: = import_ref Main//foo, loc9_1, loaded [concrete = constants.%complete_type.a68] // CHECK:STDOUT: %Main.import_ref.09e = import_ref Main//foo, inst{{[0-9A-F]+}} [no loc], unloaded // CHECK:STDOUT: %Main.import_ref.1bb = import_ref Main//foo, loc7_8, unloaded // CHECK:STDOUT: %Main.import_ref.469 = import_ref Main//foo, loc8_17, unloaded // CHECK:STDOUT: %Main.import_ref.b3bc94.2: type = import_ref Main//foo, loc6_21, loaded [symbolic = @CompleteClass.%T (constants.%T)] // CHECK:STDOUT: %Core.Int: %Int.type = import_ref Core//prelude/parts/int, Int, loaded [concrete = constants.%Int.generic] // CHECK:STDOUT: %Core.ImplicitAs: %ImplicitAs.type.cc7 = import_ref Core//prelude/parts/as, ImplicitAs, loaded [concrete = constants.%ImplicitAs.generic] // CHECK:STDOUT: %Core.Destroy: type = import_ref Core//prelude/parts/destroy, Destroy, loaded [concrete = constants.%Destroy.type] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Class = imports.%Main.Class // CHECK:STDOUT: .CompleteClass = imports.%Main.CompleteClass // CHECK:STDOUT: .F = imports.%Main.F // CHECK:STDOUT: .Core = imports.%Core.ece // CHECK:STDOUT: .Use = %Use.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %default.import = import // CHECK:STDOUT: %Use.decl: %Use.type = fn_decl @Use [concrete = constants.%Use] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic class @CompleteClass(imports.%Main.import_ref.b3bc94.2: type) [from "foo.carbon"] { // CHECK:STDOUT: %T: type = symbolic_binding T, 0 [symbolic = %T (constants.%T)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %CompleteClass: type = class_type @CompleteClass, @CompleteClass(%T) [symbolic = %CompleteClass (constants.%CompleteClass.152)] // CHECK:STDOUT: %CompleteClass.elem: type = unbound_element_type %CompleteClass, constants.%i32 [symbolic = %CompleteClass.elem (constants.%CompleteClass.elem.2b9)] // CHECK:STDOUT: %CompleteClass.F.type: type = fn_type @CompleteClass.F, @CompleteClass(%T) [symbolic = %CompleteClass.F.type (constants.%CompleteClass.F.type.6b4)] // CHECK:STDOUT: %CompleteClass.F: @CompleteClass.%CompleteClass.F.type (%CompleteClass.F.type.6b4) = struct_value () [symbolic = %CompleteClass.F (constants.%CompleteClass.F.5ed)] // CHECK:STDOUT: // CHECK:STDOUT: class { // CHECK:STDOUT: complete_type_witness = imports.%Main.import_ref.eb1 // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = imports.%Main.import_ref.09e // CHECK:STDOUT: .n = imports.%Main.import_ref.1bb // CHECK:STDOUT: .F = imports.%Main.import_ref.469 // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @Use() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %v.patt: %pattern_type.a6d = ref_binding_pattern v [concrete] // CHECK:STDOUT: %v.var_patt: %pattern_type.a6d = var_pattern %v.patt [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %v.var: ref %CompleteClass.582 = var %v.var_patt // CHECK:STDOUT: %F.ref: %F.type = name_ref F, imports.%Main.F [concrete = constants.%F] // CHECK:STDOUT: %.loc14_41: ref %CompleteClass.667 = temporary_storage // CHECK:STDOUT: %F.call: init %CompleteClass.667 to %.loc14_41 = call %F.ref() // CHECK:STDOUT: %.loc14_3: %CompleteClass.582 = converted %F.call, [concrete = ] // CHECK:STDOUT: assign %v.var, // CHECK:STDOUT: %.loc14_35: type = splice_block %CompleteClass [concrete = constants.%CompleteClass.582] { // CHECK:STDOUT: %CompleteClass.ref: %CompleteClass.type = name_ref CompleteClass, imports.%Main.CompleteClass [concrete = constants.%CompleteClass.generic] // CHECK:STDOUT: %i32: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %ptr: type = ptr_type %i32 [concrete = constants.%ptr.9e1] // CHECK:STDOUT: %CompleteClass: type = class_type @CompleteClass, @CompleteClass(constants.%ptr.9e1) [concrete = constants.%CompleteClass.582] // CHECK:STDOUT: } // CHECK:STDOUT: %v: ref %CompleteClass.582 = ref_binding v, %v.var // CHECK:STDOUT: %Destroy.Op.bound: = bound_method %v.var, constants.%Destroy.Op // CHECK:STDOUT: %Destroy.Op.call: init %empty_tuple.type = call %Destroy.Op.bound(%v.var) // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic fn @CompleteClass.F(imports.%Main.import_ref.b3bc94.1: type) [from "foo.carbon"] { // CHECK:STDOUT: !definition: // CHECK:STDOUT: // CHECK:STDOUT: fn; // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @F [from "foo.carbon"]; // CHECK:STDOUT: // CHECK:STDOUT: fn @Destroy.Op(%self.param: ref %CompleteClass.582) = "no_op"; // CHECK:STDOUT: // CHECK:STDOUT: specific @CompleteClass(constants.%T) { // CHECK:STDOUT: %T => constants.%T // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %CompleteClass => constants.%CompleteClass.152 // CHECK:STDOUT: %CompleteClass.elem => constants.%CompleteClass.elem.2b9 // CHECK:STDOUT: %CompleteClass.F.type => constants.%CompleteClass.F.type.6b4 // CHECK:STDOUT: %CompleteClass.F => constants.%CompleteClass.F.5ed // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @CompleteClass.F(constants.%T) {} // CHECK:STDOUT: // CHECK:STDOUT: specific @CompleteClass(constants.%ptr.9e1) { // CHECK:STDOUT: %T => constants.%ptr.9e1 // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %CompleteClass => constants.%CompleteClass.582 // CHECK:STDOUT: %CompleteClass.elem => constants.%CompleteClass.elem.166 // CHECK:STDOUT: %CompleteClass.F.type => constants.%CompleteClass.F.type.160 // CHECK:STDOUT: %CompleteClass.F => constants.%CompleteClass.F.f06 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @CompleteClass(constants.%i32) { // CHECK:STDOUT: %T => constants.%i32 // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %CompleteClass => constants.%CompleteClass.667 // CHECK:STDOUT: %CompleteClass.elem => constants.%CompleteClass.elem.995 // CHECK:STDOUT: %CompleteClass.F.type => constants.%CompleteClass.F.type.111 // CHECK:STDOUT: %CompleteClass.F => constants.%CompleteClass.F.3a7 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- fail_foo.impl.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %pattern_type.98f: type = pattern_type type [concrete] // CHECK:STDOUT: %type: type = facet_type [concrete] // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self] // CHECK:STDOUT: %U: type = symbolic_binding U, 0 [symbolic] // CHECK:STDOUT: %Class.type.a9b607.1: type = generic_class_type @Class.1 [concrete] // CHECK:STDOUT: %Class.generic.f12661.1: %Class.type.a9b607.1 = struct_value () [concrete] // CHECK:STDOUT: %T: type = symbolic_binding T, 0 [symbolic] // CHECK:STDOUT: %Class.type.a9b607.2: type = generic_class_type @Class.loc12 [concrete] // CHECK:STDOUT: %Class.generic.f12661.2: %Class.type.a9b607.2 = struct_value () [concrete] // CHECK:STDOUT: %Class.0db33e.2: type = class_type @Class.loc12, @Class.loc12(%U) [symbolic] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Main.Class: %Class.type.a9b607.1 = import_ref Main//foo, Class, loaded [concrete = constants.%Class.generic.f12661.1] // CHECK:STDOUT: %Main.CompleteClass = import_ref Main//foo, CompleteClass, unloaded // CHECK:STDOUT: %Main.F = import_ref Main//foo, F, unloaded // CHECK:STDOUT: %Core.ece: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: %Main.import_ref.b3b: type = import_ref Main//foo, loc4_13, loaded [symbolic = @Class.1.%T (constants.%T)] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Class = imports.%Main.Class // CHECK:STDOUT: .CompleteClass = imports.%Main.CompleteClass // CHECK:STDOUT: .F = imports.%Main.F // CHECK:STDOUT: .Core = imports.%Core.ece // CHECK:STDOUT: .T = // CHECK:STDOUT: } // CHECK:STDOUT: %default.import.loc2_19.1 = import // CHECK:STDOUT: %default.import.loc2_19.2 = import // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %Class.decl: %Class.type.a9b607.2 = class_decl @Class.loc12 [concrete = constants.%Class.generic.f12661.2] { // CHECK:STDOUT: %U.patt: %pattern_type.98f = symbolic_binding_pattern U, 0 [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %.loc12_17.1: type = splice_block %.loc12_17.2 [concrete = type] { // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %.loc12_17.2: type = type_literal type [concrete = type] // CHECK:STDOUT: } // CHECK:STDOUT: %U.loc12_13.2: type = symbolic_binding U, 0 [symbolic = %U.loc12_13.1 (constants.%U)] // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic class @Class.1(imports.%Main.import_ref.b3b: type) [from "foo.carbon"] { // CHECK:STDOUT: %T: type = symbolic_binding T, 0 [symbolic = %T (constants.%T)] // CHECK:STDOUT: // CHECK:STDOUT: class; // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic class @Class.loc12(%U.loc12_13.2: type) { // CHECK:STDOUT: %U.loc12_13.1: type = symbolic_binding U, 0 [symbolic = %U.loc12_13.1 (constants.%U)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: // CHECK:STDOUT: class { // CHECK:STDOUT: %T.ref: = name_ref T, [concrete = ] // CHECK:STDOUT: %.loc17: = field_decl x, element0 [concrete] // CHECK:STDOUT: %complete_type: = complete_type_witness [concrete = ] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%Class.0db33e.2 // CHECK:STDOUT: .T = // CHECK:STDOUT: .x = %.loc17 // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @Class.1(constants.%T) { // CHECK:STDOUT: %T => constants.%T // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @Class.loc12(constants.%U) { // CHECK:STDOUT: %U.loc12_13.1 => constants.%U // CHECK:STDOUT: } // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/class/generic/init.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/int.carbon // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/class/generic/init.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/class/generic/init.carbon // --- from_struct.carbon library "[[@TEST_NAME]]"; class Class(T:! type) { var k: T; } //@dump-sem-ir-begin fn InitFromStructGeneric(T:! Core.Copy, x: T) -> T { var v: Class(T) = {.k = x}; return v.k; } fn InitFromStructSpecific(x: i32) -> i32 { var v: Class(i32) = {.k = x}; return v.k; } //@dump-sem-ir-end // --- adapt.carbon library "[[@TEST_NAME]]"; class Adapt(T:! type) { adapt T; } //@dump-sem-ir-begin fn InitFromAdaptedGeneric(T:! Core.Copy, x: T) -> T { return (x as Adapt(T)) as T; } fn InitFromAdaptedSpecific(x: i32) -> i32 { return (x as Adapt(i32)) as i32; } //@dump-sem-ir-end // CHECK:STDOUT: --- from_struct.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %Class.type: type = generic_class_type @Class [concrete] // CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] // CHECK:STDOUT: %Class.generic: %Class.type = struct_value () [concrete] // CHECK:STDOUT: %Copy.type: type = facet_type <@Copy> [concrete] // CHECK:STDOUT: %pattern_type.ce2: type = pattern_type %Copy.type [concrete] // CHECK:STDOUT: %T.035: %Copy.type = symbolic_binding T, 0 [symbolic] // CHECK:STDOUT: %T.binding.as_type: type = symbolic_binding_type T, 0, %T.035 [symbolic] // CHECK:STDOUT: %pattern_type.9b9f0c.2: type = pattern_type %T.binding.as_type [symbolic] // CHECK:STDOUT: %.076a48.2: Core.Form = init_form %T.binding.as_type [symbolic] // CHECK:STDOUT: %InitFromStructGeneric.type: type = fn_type @InitFromStructGeneric [concrete] // CHECK:STDOUT: %InitFromStructGeneric: %InitFromStructGeneric.type = struct_value () [concrete] // CHECK:STDOUT: %require_complete.67c: = require_complete_type %T.binding.as_type [symbolic] // CHECK:STDOUT: %Class.316: type = class_type @Class, @Class(%T.binding.as_type) [symbolic] // CHECK:STDOUT: %Class.elem.765: type = unbound_element_type %Class.316, %T.binding.as_type [symbolic] // CHECK:STDOUT: %struct_type.k.436: type = struct_type {.k: %T.binding.as_type} [symbolic] // CHECK:STDOUT: %require_complete.ae7: = require_complete_type %Class.316 [symbolic] // CHECK:STDOUT: %pattern_type.c54: type = pattern_type %Class.316 [symbolic] // CHECK:STDOUT: %Copy.lookup_impl_witness.58d: = lookup_impl_witness %T.035, @Copy [symbolic] // CHECK:STDOUT: %Copy.WithSelf.Op.type.735e75.2: type = fn_type @Copy.WithSelf.Op, @Copy.WithSelf(%T.035) [symbolic] // CHECK:STDOUT: %.023: type = fn_type_with_self_type %Copy.WithSelf.Op.type.735e75.2, %T.035 [symbolic] // CHECK:STDOUT: %impl.elem0.594: %.023 = impl_witness_access %Copy.lookup_impl_witness.58d, element0 [symbolic] // CHECK:STDOUT: %specific_impl_fn.bdc: = specific_impl_function %impl.elem0.594, @Copy.WithSelf.Op(%T.035) [symbolic] // CHECK:STDOUT: %Destroy.type: type = facet_type <@Destroy> [concrete] // CHECK:STDOUT: %Destroy.lookup_impl_witness: = lookup_impl_witness %Class.316, @Destroy [symbolic] // CHECK:STDOUT: %Destroy.facet.88b: %Destroy.type = facet_value %Class.316, (%Destroy.lookup_impl_witness) [symbolic] // CHECK:STDOUT: %Destroy.WithSelf.Op.type.5c9: type = fn_type @Destroy.WithSelf.Op, @Destroy.WithSelf(%Destroy.facet.88b) [symbolic] // CHECK:STDOUT: %.0dd: type = fn_type_with_self_type %Destroy.WithSelf.Op.type.5c9, %Destroy.facet.88b [symbolic] // CHECK:STDOUT: %impl.elem0.799: %.0dd = impl_witness_access %Destroy.lookup_impl_witness, element0 [symbolic] // CHECK:STDOUT: %specific_impl_fn.d17: = specific_impl_function %impl.elem0.799, @Destroy.WithSelf.Op(%Destroy.facet.88b) [symbolic] // CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [concrete] // CHECK:STDOUT: %Int.type: type = generic_class_type @Int [concrete] // CHECK:STDOUT: %Int.generic: %Int.type = struct_value () [concrete] // CHECK:STDOUT: %N: Core.IntLiteral = symbolic_binding N, 0 [symbolic] // CHECK:STDOUT: %i32: type = class_type @Int, @Int(%int_32) [concrete] // CHECK:STDOUT: %pattern_type.7ce: type = pattern_type %i32 [concrete] // CHECK:STDOUT: %.ff5: Core.Form = init_form %i32 [concrete] // CHECK:STDOUT: %InitFromStructSpecific.type: type = fn_type @InitFromStructSpecific [concrete] // CHECK:STDOUT: %InitFromStructSpecific: %InitFromStructSpecific.type = struct_value () [concrete] // CHECK:STDOUT: %Class.805: type = class_type @Class, @Class(%i32) [concrete] // CHECK:STDOUT: %Class.elem.927: type = unbound_element_type %Class.805, %i32 [concrete] // CHECK:STDOUT: %struct_type.k.0bf: type = struct_type {.k: %i32} [concrete] // CHECK:STDOUT: %pattern_type.1c2: type = pattern_type %Class.805 [concrete] // CHECK:STDOUT: %Int.as.Copy.impl.Op.type.824: type = fn_type @Int.as.Copy.impl.Op, @Int.as.Copy.impl(%N) [symbolic] // CHECK:STDOUT: %Int.as.Copy.impl.Op.9b9: %Int.as.Copy.impl.Op.type.824 = struct_value () [symbolic] // CHECK:STDOUT: %Copy.impl_witness.f17: = impl_witness imports.%Copy.impl_witness_table.e76, @Int.as.Copy.impl(%int_32) [concrete] // CHECK:STDOUT: %Int.as.Copy.impl.Op.type.546: type = fn_type @Int.as.Copy.impl.Op, @Int.as.Copy.impl(%int_32) [concrete] // CHECK:STDOUT: %Int.as.Copy.impl.Op.664: %Int.as.Copy.impl.Op.type.546 = struct_value () [concrete] // CHECK:STDOUT: %Copy.facet: %Copy.type = facet_value %i32, (%Copy.impl_witness.f17) [concrete] // CHECK:STDOUT: %Copy.WithSelf.Op.type.081: type = fn_type @Copy.WithSelf.Op, @Copy.WithSelf(%Copy.facet) [concrete] // CHECK:STDOUT: %.8e2: type = fn_type_with_self_type %Copy.WithSelf.Op.type.081, %Copy.facet [concrete] // CHECK:STDOUT: %Int.as.Copy.impl.Op.specific_fn: = specific_function %Int.as.Copy.impl.Op.664, @Int.as.Copy.impl.Op(%int_32) [concrete] // CHECK:STDOUT: %Destroy.Op.type: type = fn_type @Destroy.Op [concrete] // CHECK:STDOUT: %Destroy.Op: %Destroy.Op.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: .Copy = %Core.Copy // CHECK:STDOUT: .Destroy = %Core.Destroy // CHECK:STDOUT: .Int = %Core.Int // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: %Core.Copy: type = import_ref Core//prelude/parts/copy, Copy, loaded [concrete = constants.%Copy.type] // CHECK:STDOUT: %Core.Destroy: type = import_ref Core//prelude/parts/destroy, Destroy, loaded [concrete = constants.%Destroy.type] // CHECK:STDOUT: %Core.Int: %Int.type = import_ref Core//prelude/parts/int, Int, loaded [concrete = constants.%Int.generic] // CHECK:STDOUT: %Core.import_ref.18d: @Int.as.Copy.impl.%Int.as.Copy.impl.Op.type (%Int.as.Copy.impl.Op.type.824) = import_ref Core//prelude/parts/int, loc{{\d+_\d+}}, loaded [symbolic = @Int.as.Copy.impl.%Int.as.Copy.impl.Op (constants.%Int.as.Copy.impl.Op.9b9)] // CHECK:STDOUT: %Copy.impl_witness_table.e76 = impl_witness_table (%Core.import_ref.18d), @Int.as.Copy.impl [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: %InitFromStructGeneric.decl: %InitFromStructGeneric.type = fn_decl @InitFromStructGeneric [concrete = constants.%InitFromStructGeneric] { // CHECK:STDOUT: %T.patt: %pattern_type.ce2 = symbolic_binding_pattern T, 0 [concrete] // CHECK:STDOUT: %x.patt: @InitFromStructGeneric.%pattern_type.loc9 (%pattern_type.9b9f0c.2) = value_binding_pattern x [concrete] // CHECK:STDOUT: %x.param_patt: @InitFromStructGeneric.%pattern_type.loc9 (%pattern_type.9b9f0c.2) = value_param_pattern %x.patt [concrete] // CHECK:STDOUT: %return.patt: @InitFromStructGeneric.%pattern_type.loc9 (%pattern_type.9b9f0c.2) = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: @InitFromStructGeneric.%pattern_type.loc9 (%pattern_type.9b9f0c.2) = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %T.ref.loc9_50: %Copy.type = name_ref T, %T.loc9_26.2 [symbolic = %T.loc9_26.1 (constants.%T.035)] // CHECK:STDOUT: %T.as_type.loc9_50: type = facet_access_type %T.ref.loc9_50 [symbolic = %T.binding.as_type (constants.%T.binding.as_type)] // CHECK:STDOUT: %.loc9_50.3: type = converted %T.ref.loc9_50, %T.as_type.loc9_50 [symbolic = %T.binding.as_type (constants.%T.binding.as_type)] // CHECK:STDOUT: %.loc9_50.4: Core.Form = init_form %.loc9_50.3 [symbolic = %.loc9_50.2 (constants.%.076a48.2)] // CHECK:STDOUT: %.loc9_34: type = splice_block %Copy.ref [concrete = constants.%Copy.type] { // CHECK:STDOUT: // CHECK:STDOUT: %Core.ref: = name_ref Core, imports.%Core [concrete = imports.%Core] // CHECK:STDOUT: %Copy.ref: type = name_ref Copy, imports.%Core.Copy [concrete = constants.%Copy.type] // CHECK:STDOUT: } // CHECK:STDOUT: %T.loc9_26.2: %Copy.type = symbolic_binding T, 0 [symbolic = %T.loc9_26.1 (constants.%T.035)] // CHECK:STDOUT: %x.param: @InitFromStructGeneric.%T.binding.as_type (%T.binding.as_type) = value_param call_param0 // CHECK:STDOUT: %.loc9_44.1: type = splice_block %.loc9_44.2 [symbolic = %T.binding.as_type (constants.%T.binding.as_type)] { // CHECK:STDOUT: %T.ref.loc9_44: %Copy.type = name_ref T, %T.loc9_26.2 [symbolic = %T.loc9_26.1 (constants.%T.035)] // CHECK:STDOUT: %T.as_type.loc9_44: type = facet_access_type %T.ref.loc9_44 [symbolic = %T.binding.as_type (constants.%T.binding.as_type)] // CHECK:STDOUT: %.loc9_44.2: type = converted %T.ref.loc9_44, %T.as_type.loc9_44 [symbolic = %T.binding.as_type (constants.%T.binding.as_type)] // CHECK:STDOUT: } // CHECK:STDOUT: %x: @InitFromStructGeneric.%T.binding.as_type (%T.binding.as_type) = value_binding x, %x.param // CHECK:STDOUT: %return.param: ref @InitFromStructGeneric.%T.binding.as_type (%T.binding.as_type) = out_param call_param1 // CHECK:STDOUT: %return: ref @InitFromStructGeneric.%T.binding.as_type (%T.binding.as_type) = return_slot %return.param // CHECK:STDOUT: } // CHECK:STDOUT: %InitFromStructSpecific.decl: %InitFromStructSpecific.type = fn_decl @InitFromStructSpecific [concrete = constants.%InitFromStructSpecific] { // CHECK:STDOUT: %x.patt: %pattern_type.7ce = value_binding_pattern x [concrete] // CHECK:STDOUT: %x.param_patt: %pattern_type.7ce = value_param_pattern %x.patt [concrete] // CHECK:STDOUT: %return.patt: %pattern_type.7ce = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: %pattern_type.7ce = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %i32.loc14_38: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %.loc14: Core.Form = init_form %i32.loc14_38 [concrete = constants.%.ff5] // CHECK:STDOUT: %x.param: %i32 = value_param call_param0 // CHECK:STDOUT: %i32.loc14_30: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %x: %i32 = value_binding x, %x.param // CHECK:STDOUT: %return.param: ref %i32 = out_param call_param1 // CHECK:STDOUT: %return: ref %i32 = return_slot %return.param // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic fn @InitFromStructGeneric(%T.loc9_26.2: %Copy.type) { // CHECK:STDOUT: %T.loc9_26.1: %Copy.type = symbolic_binding T, 0 [symbolic = %T.loc9_26.1 (constants.%T.035)] // CHECK:STDOUT: %T.binding.as_type: type = symbolic_binding_type T, 0, %T.loc9_26.1 [symbolic = %T.binding.as_type (constants.%T.binding.as_type)] // CHECK:STDOUT: %pattern_type.loc9: type = pattern_type %T.binding.as_type [symbolic = %pattern_type.loc9 (constants.%pattern_type.9b9f0c.2)] // CHECK:STDOUT: %.loc9_50.2: Core.Form = init_form %T.binding.as_type [symbolic = %.loc9_50.2 (constants.%.076a48.2)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete.loc9: = require_complete_type %T.binding.as_type [symbolic = %require_complete.loc9 (constants.%require_complete.67c)] // CHECK:STDOUT: %Class.loc10_17.2: type = class_type @Class, @Class(%T.binding.as_type) [symbolic = %Class.loc10_17.2 (constants.%Class.316)] // CHECK:STDOUT: %require_complete.loc10: = require_complete_type %Class.loc10_17.2 [symbolic = %require_complete.loc10 (constants.%require_complete.ae7)] // CHECK:STDOUT: %pattern_type.loc10: type = pattern_type %Class.loc10_17.2 [symbolic = %pattern_type.loc10 (constants.%pattern_type.c54)] // CHECK:STDOUT: %struct_type.k: type = struct_type {.k: @InitFromStructGeneric.%T.binding.as_type (%T.binding.as_type)} [symbolic = %struct_type.k (constants.%struct_type.k.436)] // CHECK:STDOUT: %Copy.lookup_impl_witness: = lookup_impl_witness %T.loc9_26.1, @Copy [symbolic = %Copy.lookup_impl_witness (constants.%Copy.lookup_impl_witness.58d)] // CHECK:STDOUT: %Copy.WithSelf.Op.type: type = fn_type @Copy.WithSelf.Op, @Copy.WithSelf(%T.loc9_26.1) [symbolic = %Copy.WithSelf.Op.type (constants.%Copy.WithSelf.Op.type.735e75.2)] // CHECK:STDOUT: %.loc10_27: type = fn_type_with_self_type %Copy.WithSelf.Op.type, %T.loc9_26.1 [symbolic = %.loc10_27 (constants.%.023)] // CHECK:STDOUT: %impl.elem0.loc10_27.2: @InitFromStructGeneric.%.loc10_27 (%.023) = impl_witness_access %Copy.lookup_impl_witness, element0 [symbolic = %impl.elem0.loc10_27.2 (constants.%impl.elem0.594)] // CHECK:STDOUT: %specific_impl_fn.loc10_27.2: = specific_impl_function %impl.elem0.loc10_27.2, @Copy.WithSelf.Op(%T.loc9_26.1) [symbolic = %specific_impl_fn.loc10_27.2 (constants.%specific_impl_fn.bdc)] // CHECK:STDOUT: %Class.elem: type = unbound_element_type %Class.loc10_17.2, %T.binding.as_type [symbolic = %Class.elem (constants.%Class.elem.765)] // CHECK:STDOUT: %Destroy.lookup_impl_witness: = lookup_impl_witness %Class.loc10_17.2, @Destroy [symbolic = %Destroy.lookup_impl_witness (constants.%Destroy.lookup_impl_witness)] // CHECK:STDOUT: %Destroy.facet: %Destroy.type = facet_value %Class.loc10_17.2, (%Destroy.lookup_impl_witness) [symbolic = %Destroy.facet (constants.%Destroy.facet.88b)] // CHECK:STDOUT: %Destroy.WithSelf.Op.type: type = fn_type @Destroy.WithSelf.Op, @Destroy.WithSelf(%Destroy.facet) [symbolic = %Destroy.WithSelf.Op.type (constants.%Destroy.WithSelf.Op.type.5c9)] // CHECK:STDOUT: %.loc10_3.2: type = fn_type_with_self_type %Destroy.WithSelf.Op.type, %Destroy.facet [symbolic = %.loc10_3.2 (constants.%.0dd)] // CHECK:STDOUT: %impl.elem0.loc10_3.2: @InitFromStructGeneric.%.loc10_3.2 (%.0dd) = impl_witness_access %Destroy.lookup_impl_witness, element0 [symbolic = %impl.elem0.loc10_3.2 (constants.%impl.elem0.799)] // CHECK:STDOUT: %specific_impl_fn.loc10_3.2: = specific_impl_function %impl.elem0.loc10_3.2, @Destroy.WithSelf.Op(%Destroy.facet) [symbolic = %specific_impl_fn.loc10_3.2 (constants.%specific_impl_fn.d17)] // CHECK:STDOUT: // CHECK:STDOUT: fn(%x.param: @InitFromStructGeneric.%T.binding.as_type (%T.binding.as_type)) -> out %return.param: @InitFromStructGeneric.%T.binding.as_type (%T.binding.as_type) { // CHECK:STDOUT: !entry: // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %v.patt: @InitFromStructGeneric.%pattern_type.loc10 (%pattern_type.c54) = ref_binding_pattern v [concrete] // CHECK:STDOUT: %v.var_patt: @InitFromStructGeneric.%pattern_type.loc10 (%pattern_type.c54) = var_pattern %v.patt [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %v.var: ref @InitFromStructGeneric.%Class.loc10_17.2 (%Class.316) = var %v.var_patt // CHECK:STDOUT: %x.ref: @InitFromStructGeneric.%T.binding.as_type (%T.binding.as_type) = name_ref x, %x // CHECK:STDOUT: %.loc10_28.1: @InitFromStructGeneric.%struct_type.k (%struct_type.k.436) = struct_literal (%x.ref) // CHECK:STDOUT: %impl.elem0.loc10_27.1: @InitFromStructGeneric.%.loc10_27 (%.023) = impl_witness_access constants.%Copy.lookup_impl_witness.58d, element0 [symbolic = %impl.elem0.loc10_27.2 (constants.%impl.elem0.594)] // CHECK:STDOUT: %bound_method.loc10_27.1: = bound_method %x.ref, %impl.elem0.loc10_27.1 // CHECK:STDOUT: %specific_impl_fn.loc10_27.1: = specific_impl_function %impl.elem0.loc10_27.1, @Copy.WithSelf.Op(constants.%T.035) [symbolic = %specific_impl_fn.loc10_27.2 (constants.%specific_impl_fn.bdc)] // CHECK:STDOUT: %bound_method.loc10_27.2: = bound_method %x.ref, %specific_impl_fn.loc10_27.1 // CHECK:STDOUT: %.loc10_28.2: ref @InitFromStructGeneric.%T.binding.as_type (%T.binding.as_type) = class_element_access %v.var, element0 // CHECK:STDOUT: %Copy.WithSelf.Op.call.loc10: init @InitFromStructGeneric.%T.binding.as_type (%T.binding.as_type) to %.loc10_28.2 = call %bound_method.loc10_27.2(%x.ref) // CHECK:STDOUT: %.loc10_28.3: init @InitFromStructGeneric.%T.binding.as_type (%T.binding.as_type) to %.loc10_28.2 = in_place_init %Copy.WithSelf.Op.call.loc10 // CHECK:STDOUT: %.loc10_28.4: init @InitFromStructGeneric.%Class.loc10_17.2 (%Class.316) to %v.var = class_init (%.loc10_28.3) // CHECK:STDOUT: %.loc10_3.1: init @InitFromStructGeneric.%Class.loc10_17.2 (%Class.316) = converted %.loc10_28.1, %.loc10_28.4 // CHECK:STDOUT: assign %v.var, %.loc10_3.1 // CHECK:STDOUT: %.loc10_17.1: type = splice_block %Class.loc10_17.1 [symbolic = %Class.loc10_17.2 (constants.%Class.316)] { // CHECK:STDOUT: %Class.ref: %Class.type = name_ref Class, file.%Class.decl [concrete = constants.%Class.generic] // CHECK:STDOUT: %T.ref.loc10: %Copy.type = name_ref T, %T.loc9_26.2 [symbolic = %T.loc9_26.1 (constants.%T.035)] // CHECK:STDOUT: %T.as_type.loc10: type = facet_access_type %T.ref.loc10 [symbolic = %T.binding.as_type (constants.%T.binding.as_type)] // CHECK:STDOUT: %.loc10_17.2: type = converted %T.ref.loc10, %T.as_type.loc10 [symbolic = %T.binding.as_type (constants.%T.binding.as_type)] // CHECK:STDOUT: %Class.loc10_17.1: type = class_type @Class, @Class(constants.%T.binding.as_type) [symbolic = %Class.loc10_17.2 (constants.%Class.316)] // CHECK:STDOUT: } // CHECK:STDOUT: %v: ref @InitFromStructGeneric.%Class.loc10_17.2 (%Class.316) = ref_binding v, %v.var // CHECK:STDOUT: %v.ref: ref @InitFromStructGeneric.%Class.loc10_17.2 (%Class.316) = name_ref v, %v // CHECK:STDOUT: %k.ref: @InitFromStructGeneric.%Class.elem (%Class.elem.765) = name_ref k, @Class.%.loc5 [concrete = @Class.%.loc5] // CHECK:STDOUT: %.loc11_11.1: ref @InitFromStructGeneric.%T.binding.as_type (%T.binding.as_type) = class_element_access %v.ref, element0 // CHECK:STDOUT: %.loc11_11.2: @InitFromStructGeneric.%T.binding.as_type (%T.binding.as_type) = acquire_value %.loc11_11.1 // CHECK:STDOUT: %impl.elem0.loc11: @InitFromStructGeneric.%.loc10_27 (%.023) = impl_witness_access constants.%Copy.lookup_impl_witness.58d, element0 [symbolic = %impl.elem0.loc10_27.2 (constants.%impl.elem0.594)] // CHECK:STDOUT: %bound_method.loc11_11.1: = bound_method %.loc11_11.2, %impl.elem0.loc11 // CHECK:STDOUT: %specific_impl_fn.loc11: = specific_impl_function %impl.elem0.loc11, @Copy.WithSelf.Op(constants.%T.035) [symbolic = %specific_impl_fn.loc10_27.2 (constants.%specific_impl_fn.bdc)] // CHECK:STDOUT: %bound_method.loc11_11.2: = bound_method %.loc11_11.2, %specific_impl_fn.loc11 // CHECK:STDOUT: %.loc9_50.1: ref @InitFromStructGeneric.%T.binding.as_type (%T.binding.as_type) = splice_block %return.param {} // CHECK:STDOUT: %Copy.WithSelf.Op.call.loc11: init @InitFromStructGeneric.%T.binding.as_type (%T.binding.as_type) to %.loc9_50.1 = call %bound_method.loc11_11.2(%.loc11_11.2) // CHECK:STDOUT: %impl.elem0.loc10_3.1: @InitFromStructGeneric.%.loc10_3.2 (%.0dd) = impl_witness_access constants.%Destroy.lookup_impl_witness, element0 [symbolic = %impl.elem0.loc10_3.2 (constants.%impl.elem0.799)] // CHECK:STDOUT: %bound_method.loc10_3.1: = bound_method %v.var, %impl.elem0.loc10_3.1 // CHECK:STDOUT: %specific_impl_fn.loc10_3.1: = specific_impl_function %impl.elem0.loc10_3.1, @Destroy.WithSelf.Op(constants.%Destroy.facet.88b) [symbolic = %specific_impl_fn.loc10_3.2 (constants.%specific_impl_fn.d17)] // CHECK:STDOUT: %bound_method.loc10_3.2: = bound_method %v.var, %specific_impl_fn.loc10_3.1 // CHECK:STDOUT: %Destroy.WithSelf.Op.call: init %empty_tuple.type = call %bound_method.loc10_3.2(%v.var) // CHECK:STDOUT: return %Copy.WithSelf.Op.call.loc11 to %return.param // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @InitFromStructSpecific(%x.param: %i32) -> out %return.param: %i32 { // CHECK:STDOUT: !entry: // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %v.patt: %pattern_type.1c2 = ref_binding_pattern v [concrete] // CHECK:STDOUT: %v.var_patt: %pattern_type.1c2 = var_pattern %v.patt [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %v.var: ref %Class.805 = var %v.var_patt // CHECK:STDOUT: %x.ref: %i32 = name_ref x, %x // CHECK:STDOUT: %.loc15_30.1: %struct_type.k.0bf = struct_literal (%x.ref) // CHECK:STDOUT: %impl.elem0.loc15: %.8e2 = impl_witness_access constants.%Copy.impl_witness.f17, element0 [concrete = constants.%Int.as.Copy.impl.Op.664] // CHECK:STDOUT: %bound_method.loc15_29.1: = bound_method %x.ref, %impl.elem0.loc15 // CHECK:STDOUT: %specific_fn.loc15: = specific_function %impl.elem0.loc15, @Int.as.Copy.impl.Op(constants.%int_32) [concrete = constants.%Int.as.Copy.impl.Op.specific_fn] // CHECK:STDOUT: %bound_method.loc15_29.2: = bound_method %x.ref, %specific_fn.loc15 // CHECK:STDOUT: %Int.as.Copy.impl.Op.call.loc15: init %i32 = call %bound_method.loc15_29.2(%x.ref) // CHECK:STDOUT: %.loc15_30.2: ref %i32 = class_element_access %v.var, element0 // CHECK:STDOUT: %.loc15_30.3: init %i32 to %.loc15_30.2 = in_place_init %Int.as.Copy.impl.Op.call.loc15 // CHECK:STDOUT: %.loc15_30.4: init %Class.805 to %v.var = class_init (%.loc15_30.3) // CHECK:STDOUT: %.loc15_3: init %Class.805 = converted %.loc15_30.1, %.loc15_30.4 // CHECK:STDOUT: assign %v.var, %.loc15_3 // CHECK:STDOUT: %.loc15_19: type = splice_block %Class [concrete = constants.%Class.805] { // CHECK:STDOUT: %Class.ref: %Class.type = name_ref Class, file.%Class.decl [concrete = constants.%Class.generic] // CHECK:STDOUT: %i32.loc15: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %Class: type = class_type @Class, @Class(constants.%i32) [concrete = constants.%Class.805] // CHECK:STDOUT: } // CHECK:STDOUT: %v: ref %Class.805 = ref_binding v, %v.var // CHECK:STDOUT: %v.ref: ref %Class.805 = name_ref v, %v // CHECK:STDOUT: %k.ref: %Class.elem.927 = name_ref k, @Class.%.loc5 [concrete = @Class.%.loc5] // CHECK:STDOUT: %.loc16_11.1: ref %i32 = class_element_access %v.ref, element0 // CHECK:STDOUT: %.loc16_11.2: %i32 = acquire_value %.loc16_11.1 // CHECK:STDOUT: %impl.elem0.loc16: %.8e2 = impl_witness_access constants.%Copy.impl_witness.f17, element0 [concrete = constants.%Int.as.Copy.impl.Op.664] // CHECK:STDOUT: %bound_method.loc16_11.1: = bound_method %.loc16_11.2, %impl.elem0.loc16 // CHECK:STDOUT: %specific_fn.loc16: = specific_function %impl.elem0.loc16, @Int.as.Copy.impl.Op(constants.%int_32) [concrete = constants.%Int.as.Copy.impl.Op.specific_fn] // CHECK:STDOUT: %bound_method.loc16_11.2: = bound_method %.loc16_11.2, %specific_fn.loc16 // CHECK:STDOUT: %Int.as.Copy.impl.Op.call.loc16: init %i32 = call %bound_method.loc16_11.2(%.loc16_11.2) // CHECK:STDOUT: %Destroy.Op.bound: = bound_method %v.var, constants.%Destroy.Op // CHECK:STDOUT: %Destroy.Op.call: init %empty_tuple.type = call %Destroy.Op.bound(%v.var) // CHECK:STDOUT: return %Int.as.Copy.impl.Op.call.loc16 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @Destroy.Op(%self.param: ref %Class.805) = "no_op"; // CHECK:STDOUT: // CHECK:STDOUT: specific @InitFromStructGeneric(constants.%T.035) { // CHECK:STDOUT: %T.loc9_26.1 => constants.%T.035 // CHECK:STDOUT: %T.binding.as_type => constants.%T.binding.as_type // CHECK:STDOUT: %pattern_type.loc9 => constants.%pattern_type.9b9f0c.2 // CHECK:STDOUT: %.loc9_50.2 => constants.%.076a48.2 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- adapt.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %Adapt.type: type = generic_class_type @Adapt [concrete] // CHECK:STDOUT: %Adapt.generic: %Adapt.type = struct_value () [concrete] // CHECK:STDOUT: %Copy.type: type = facet_type <@Copy> [concrete] // CHECK:STDOUT: %pattern_type.ce2: type = pattern_type %Copy.type [concrete] // CHECK:STDOUT: %T.035: %Copy.type = symbolic_binding T, 0 [symbolic] // CHECK:STDOUT: %T.binding.as_type: type = symbolic_binding_type T, 0, %T.035 [symbolic] // CHECK:STDOUT: %pattern_type.9b9f0c.2: type = pattern_type %T.binding.as_type [symbolic] // CHECK:STDOUT: %.076a48.2: Core.Form = init_form %T.binding.as_type [symbolic] // CHECK:STDOUT: %InitFromAdaptedGeneric.type: type = fn_type @InitFromAdaptedGeneric [concrete] // CHECK:STDOUT: %InitFromAdaptedGeneric: %InitFromAdaptedGeneric.type = struct_value () [concrete] // CHECK:STDOUT: %require_complete.67c: = require_complete_type %T.binding.as_type [symbolic] // CHECK:STDOUT: %Adapt.f64: type = class_type @Adapt, @Adapt(%T.binding.as_type) [symbolic] // CHECK:STDOUT: %require_complete.888: = require_complete_type %Adapt.f64 [symbolic] // CHECK:STDOUT: %Copy.lookup_impl_witness.58d: = lookup_impl_witness %T.035, @Copy [symbolic] // CHECK:STDOUT: %Copy.WithSelf.Op.type.735e75.2: type = fn_type @Copy.WithSelf.Op, @Copy.WithSelf(%T.035) [symbolic] // CHECK:STDOUT: %.023: type = fn_type_with_self_type %Copy.WithSelf.Op.type.735e75.2, %T.035 [symbolic] // CHECK:STDOUT: %impl.elem0.594: %.023 = impl_witness_access %Copy.lookup_impl_witness.58d, element0 [symbolic] // CHECK:STDOUT: %specific_impl_fn.bdc: = specific_impl_function %impl.elem0.594, @Copy.WithSelf.Op(%T.035) [symbolic] // CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [concrete] // CHECK:STDOUT: %Int.type: type = generic_class_type @Int [concrete] // CHECK:STDOUT: %Int.generic: %Int.type = struct_value () [concrete] // CHECK:STDOUT: %N: Core.IntLiteral = symbolic_binding N, 0 [symbolic] // CHECK:STDOUT: %i32: type = class_type @Int, @Int(%int_32) [concrete] // CHECK:STDOUT: %pattern_type.7ce: type = pattern_type %i32 [concrete] // CHECK:STDOUT: %.ff5: Core.Form = init_form %i32 [concrete] // CHECK:STDOUT: %InitFromAdaptedSpecific.type: type = fn_type @InitFromAdaptedSpecific [concrete] // CHECK:STDOUT: %InitFromAdaptedSpecific: %InitFromAdaptedSpecific.type = struct_value () [concrete] // CHECK:STDOUT: %Adapt.808: type = class_type @Adapt, @Adapt(%i32) [concrete] // CHECK:STDOUT: %Int.as.Copy.impl.Op.type.824: type = fn_type @Int.as.Copy.impl.Op, @Int.as.Copy.impl(%N) [symbolic] // CHECK:STDOUT: %Int.as.Copy.impl.Op.9b9: %Int.as.Copy.impl.Op.type.824 = struct_value () [symbolic] // CHECK:STDOUT: %Copy.impl_witness.f17: = impl_witness imports.%Copy.impl_witness_table.e76, @Int.as.Copy.impl(%int_32) [concrete] // CHECK:STDOUT: %Int.as.Copy.impl.Op.type.546: type = fn_type @Int.as.Copy.impl.Op, @Int.as.Copy.impl(%int_32) [concrete] // CHECK:STDOUT: %Int.as.Copy.impl.Op.664: %Int.as.Copy.impl.Op.type.546 = struct_value () [concrete] // CHECK:STDOUT: %Copy.facet: %Copy.type = facet_value %i32, (%Copy.impl_witness.f17) [concrete] // CHECK:STDOUT: %Copy.WithSelf.Op.type.081: type = fn_type @Copy.WithSelf.Op, @Copy.WithSelf(%Copy.facet) [concrete] // CHECK:STDOUT: %.8e2: type = fn_type_with_self_type %Copy.WithSelf.Op.type.081, %Copy.facet [concrete] // CHECK:STDOUT: %Int.as.Copy.impl.Op.specific_fn: = specific_function %Int.as.Copy.impl.Op.664, @Int.as.Copy.impl.Op(%int_32) [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: .Copy = %Core.Copy // CHECK:STDOUT: .Int = %Core.Int // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: %Core.Copy: type = import_ref Core//prelude/parts/copy, Copy, loaded [concrete = constants.%Copy.type] // CHECK:STDOUT: %Core.Int: %Int.type = import_ref Core//prelude/parts/int, Int, loaded [concrete = constants.%Int.generic] // CHECK:STDOUT: %Core.import_ref.18d: @Int.as.Copy.impl.%Int.as.Copy.impl.Op.type (%Int.as.Copy.impl.Op.type.824) = import_ref Core//prelude/parts/int, loc{{\d+_\d+}}, loaded [symbolic = @Int.as.Copy.impl.%Int.as.Copy.impl.Op (constants.%Int.as.Copy.impl.Op.9b9)] // CHECK:STDOUT: %Copy.impl_witness_table.e76 = impl_witness_table (%Core.import_ref.18d), @Int.as.Copy.impl [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: %InitFromAdaptedGeneric.decl: %InitFromAdaptedGeneric.type = fn_decl @InitFromAdaptedGeneric [concrete = constants.%InitFromAdaptedGeneric] { // CHECK:STDOUT: %T.patt: %pattern_type.ce2 = symbolic_binding_pattern T, 0 [concrete] // CHECK:STDOUT: %x.patt: @InitFromAdaptedGeneric.%pattern_type (%pattern_type.9b9f0c.2) = value_binding_pattern x [concrete] // CHECK:STDOUT: %x.param_patt: @InitFromAdaptedGeneric.%pattern_type (%pattern_type.9b9f0c.2) = value_param_pattern %x.patt [concrete] // CHECK:STDOUT: %return.patt: @InitFromAdaptedGeneric.%pattern_type (%pattern_type.9b9f0c.2) = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: @InitFromAdaptedGeneric.%pattern_type (%pattern_type.9b9f0c.2) = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %T.ref.loc9_51: %Copy.type = name_ref T, %T.loc9_27.2 [symbolic = %T.loc9_27.1 (constants.%T.035)] // CHECK:STDOUT: %T.as_type.loc9_51: type = facet_access_type %T.ref.loc9_51 [symbolic = %T.binding.as_type (constants.%T.binding.as_type)] // CHECK:STDOUT: %.loc9_51.3: type = converted %T.ref.loc9_51, %T.as_type.loc9_51 [symbolic = %T.binding.as_type (constants.%T.binding.as_type)] // CHECK:STDOUT: %.loc9_51.4: Core.Form = init_form %.loc9_51.3 [symbolic = %.loc9_51.2 (constants.%.076a48.2)] // CHECK:STDOUT: %.loc9_35: type = splice_block %Copy.ref [concrete = constants.%Copy.type] { // CHECK:STDOUT: // CHECK:STDOUT: %Core.ref: = name_ref Core, imports.%Core [concrete = imports.%Core] // CHECK:STDOUT: %Copy.ref: type = name_ref Copy, imports.%Core.Copy [concrete = constants.%Copy.type] // CHECK:STDOUT: } // CHECK:STDOUT: %T.loc9_27.2: %Copy.type = symbolic_binding T, 0 [symbolic = %T.loc9_27.1 (constants.%T.035)] // CHECK:STDOUT: %x.param: @InitFromAdaptedGeneric.%T.binding.as_type (%T.binding.as_type) = value_param call_param0 // CHECK:STDOUT: %.loc9_45.1: type = splice_block %.loc9_45.2 [symbolic = %T.binding.as_type (constants.%T.binding.as_type)] { // CHECK:STDOUT: %T.ref.loc9_45: %Copy.type = name_ref T, %T.loc9_27.2 [symbolic = %T.loc9_27.1 (constants.%T.035)] // CHECK:STDOUT: %T.as_type.loc9_45: type = facet_access_type %T.ref.loc9_45 [symbolic = %T.binding.as_type (constants.%T.binding.as_type)] // CHECK:STDOUT: %.loc9_45.2: type = converted %T.ref.loc9_45, %T.as_type.loc9_45 [symbolic = %T.binding.as_type (constants.%T.binding.as_type)] // CHECK:STDOUT: } // CHECK:STDOUT: %x: @InitFromAdaptedGeneric.%T.binding.as_type (%T.binding.as_type) = value_binding x, %x.param // CHECK:STDOUT: %return.param: ref @InitFromAdaptedGeneric.%T.binding.as_type (%T.binding.as_type) = out_param call_param1 // CHECK:STDOUT: %return: ref @InitFromAdaptedGeneric.%T.binding.as_type (%T.binding.as_type) = return_slot %return.param // CHECK:STDOUT: } // CHECK:STDOUT: %InitFromAdaptedSpecific.decl: %InitFromAdaptedSpecific.type = fn_decl @InitFromAdaptedSpecific [concrete = constants.%InitFromAdaptedSpecific] { // CHECK:STDOUT: %x.patt: %pattern_type.7ce = value_binding_pattern x [concrete] // CHECK:STDOUT: %x.param_patt: %pattern_type.7ce = value_param_pattern %x.patt [concrete] // CHECK:STDOUT: %return.patt: %pattern_type.7ce = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: %pattern_type.7ce = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %i32.loc13_39: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %.loc13: Core.Form = init_form %i32.loc13_39 [concrete = constants.%.ff5] // CHECK:STDOUT: %x.param: %i32 = value_param call_param0 // CHECK:STDOUT: %i32.loc13_31: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %x: %i32 = value_binding x, %x.param // CHECK:STDOUT: %return.param: ref %i32 = out_param call_param1 // CHECK:STDOUT: %return: ref %i32 = return_slot %return.param // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic fn @InitFromAdaptedGeneric(%T.loc9_27.2: %Copy.type) { // CHECK:STDOUT: %T.loc9_27.1: %Copy.type = symbolic_binding T, 0 [symbolic = %T.loc9_27.1 (constants.%T.035)] // CHECK:STDOUT: %T.binding.as_type: type = symbolic_binding_type T, 0, %T.loc9_27.1 [symbolic = %T.binding.as_type (constants.%T.binding.as_type)] // CHECK:STDOUT: %pattern_type: type = pattern_type %T.binding.as_type [symbolic = %pattern_type (constants.%pattern_type.9b9f0c.2)] // CHECK:STDOUT: %.loc9_51.2: Core.Form = init_form %T.binding.as_type [symbolic = %.loc9_51.2 (constants.%.076a48.2)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete.loc9: = require_complete_type %T.binding.as_type [symbolic = %require_complete.loc9 (constants.%require_complete.67c)] // CHECK:STDOUT: %Adapt.loc10_23.2: type = class_type @Adapt, @Adapt(%T.binding.as_type) [symbolic = %Adapt.loc10_23.2 (constants.%Adapt.f64)] // CHECK:STDOUT: %require_complete.loc10: = require_complete_type %Adapt.loc10_23.2 [symbolic = %require_complete.loc10 (constants.%require_complete.888)] // CHECK:STDOUT: %Copy.lookup_impl_witness: = lookup_impl_witness %T.loc9_27.1, @Copy [symbolic = %Copy.lookup_impl_witness (constants.%Copy.lookup_impl_witness.58d)] // CHECK:STDOUT: %Copy.WithSelf.Op.type: type = fn_type @Copy.WithSelf.Op, @Copy.WithSelf(%T.loc9_27.1) [symbolic = %Copy.WithSelf.Op.type (constants.%Copy.WithSelf.Op.type.735e75.2)] // CHECK:STDOUT: %.loc10_26.3: type = fn_type_with_self_type %Copy.WithSelf.Op.type, %T.loc9_27.1 [symbolic = %.loc10_26.3 (constants.%.023)] // CHECK:STDOUT: %impl.elem0.loc10_26.2: @InitFromAdaptedGeneric.%.loc10_26.3 (%.023) = impl_witness_access %Copy.lookup_impl_witness, element0 [symbolic = %impl.elem0.loc10_26.2 (constants.%impl.elem0.594)] // CHECK:STDOUT: %specific_impl_fn.loc10_26.2: = specific_impl_function %impl.elem0.loc10_26.2, @Copy.WithSelf.Op(%T.loc9_27.1) [symbolic = %specific_impl_fn.loc10_26.2 (constants.%specific_impl_fn.bdc)] // CHECK:STDOUT: // CHECK:STDOUT: fn(%x.param: @InitFromAdaptedGeneric.%T.binding.as_type (%T.binding.as_type)) -> out %return.param: @InitFromAdaptedGeneric.%T.binding.as_type (%T.binding.as_type) { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %x.ref: @InitFromAdaptedGeneric.%T.binding.as_type (%T.binding.as_type) = name_ref x, %x // CHECK:STDOUT: %Adapt.ref: %Adapt.type = name_ref Adapt, file.%Adapt.decl [concrete = constants.%Adapt.generic] // CHECK:STDOUT: %T.ref.loc10_22: %Copy.type = name_ref T, %T.loc9_27.2 [symbolic = %T.loc9_27.1 (constants.%T.035)] // CHECK:STDOUT: %T.as_type.loc10_23: type = facet_access_type %T.ref.loc10_22 [symbolic = %T.binding.as_type (constants.%T.binding.as_type)] // CHECK:STDOUT: %.loc10_23: type = converted %T.ref.loc10_22, %T.as_type.loc10_23 [symbolic = %T.binding.as_type (constants.%T.binding.as_type)] // CHECK:STDOUT: %Adapt.loc10_23.1: type = class_type @Adapt, @Adapt(constants.%T.binding.as_type) [symbolic = %Adapt.loc10_23.2 (constants.%Adapt.f64)] // CHECK:STDOUT: %.loc10_13.1: @InitFromAdaptedGeneric.%Adapt.loc10_23.2 (%Adapt.f64) = as_compatible %x.ref // CHECK:STDOUT: %.loc10_13.2: @InitFromAdaptedGeneric.%Adapt.loc10_23.2 (%Adapt.f64) = converted %x.ref, %.loc10_13.1 // CHECK:STDOUT: %T.ref.loc10_29: %Copy.type = name_ref T, %T.loc9_27.2 [symbolic = %T.loc9_27.1 (constants.%T.035)] // CHECK:STDOUT: %T.as_type.loc10_29: type = facet_access_type %T.ref.loc10_29 [symbolic = %T.binding.as_type (constants.%T.binding.as_type)] // CHECK:STDOUT: %.loc10_29: type = converted %T.ref.loc10_29, %T.as_type.loc10_29 [symbolic = %T.binding.as_type (constants.%T.binding.as_type)] // CHECK:STDOUT: %.loc10_26.1: @InitFromAdaptedGeneric.%T.binding.as_type (%T.binding.as_type) = as_compatible %.loc10_13.2 // CHECK:STDOUT: %.loc10_26.2: @InitFromAdaptedGeneric.%T.binding.as_type (%T.binding.as_type) = converted %.loc10_13.2, %.loc10_26.1 // CHECK:STDOUT: %impl.elem0.loc10_26.1: @InitFromAdaptedGeneric.%.loc10_26.3 (%.023) = impl_witness_access constants.%Copy.lookup_impl_witness.58d, element0 [symbolic = %impl.elem0.loc10_26.2 (constants.%impl.elem0.594)] // CHECK:STDOUT: %bound_method.loc10_26.1: = bound_method %.loc10_26.2, %impl.elem0.loc10_26.1 // CHECK:STDOUT: %specific_impl_fn.loc10_26.1: = specific_impl_function %impl.elem0.loc10_26.1, @Copy.WithSelf.Op(constants.%T.035) [symbolic = %specific_impl_fn.loc10_26.2 (constants.%specific_impl_fn.bdc)] // CHECK:STDOUT: %bound_method.loc10_26.2: = bound_method %.loc10_26.2, %specific_impl_fn.loc10_26.1 // CHECK:STDOUT: %.loc9_51.1: ref @InitFromAdaptedGeneric.%T.binding.as_type (%T.binding.as_type) = splice_block %return.param {} // CHECK:STDOUT: %Copy.WithSelf.Op.call: init @InitFromAdaptedGeneric.%T.binding.as_type (%T.binding.as_type) to %.loc9_51.1 = call %bound_method.loc10_26.2(%.loc10_26.2) // CHECK:STDOUT: return %Copy.WithSelf.Op.call to %return.param // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @InitFromAdaptedSpecific(%x.param: %i32) -> out %return.param: %i32 { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %x.ref: %i32 = name_ref x, %x // CHECK:STDOUT: %Adapt.ref: %Adapt.type = name_ref Adapt, file.%Adapt.decl [concrete = constants.%Adapt.generic] // CHECK:STDOUT: %i32.loc14_22: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %Adapt: type = class_type @Adapt, @Adapt(constants.%i32) [concrete = constants.%Adapt.808] // CHECK:STDOUT: %.loc14_13.1: %Adapt.808 = as_compatible %x.ref // CHECK:STDOUT: %.loc14_13.2: %Adapt.808 = converted %x.ref, %.loc14_13.1 // CHECK:STDOUT: %i32.loc14_31: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %.loc14_28.1: %i32 = as_compatible %.loc14_13.2 // CHECK:STDOUT: %.loc14_28.2: %i32 = converted %.loc14_13.2, %.loc14_28.1 // CHECK:STDOUT: %impl.elem0: %.8e2 = impl_witness_access constants.%Copy.impl_witness.f17, element0 [concrete = constants.%Int.as.Copy.impl.Op.664] // CHECK:STDOUT: %bound_method.loc14_28.1: = bound_method %.loc14_28.2, %impl.elem0 // CHECK:STDOUT: %specific_fn: = specific_function %impl.elem0, @Int.as.Copy.impl.Op(constants.%int_32) [concrete = constants.%Int.as.Copy.impl.Op.specific_fn] // CHECK:STDOUT: %bound_method.loc14_28.2: = bound_method %.loc14_28.2, %specific_fn // CHECK:STDOUT: %Int.as.Copy.impl.Op.call: init %i32 = call %bound_method.loc14_28.2(%.loc14_28.2) // CHECK:STDOUT: return %Int.as.Copy.impl.Op.call // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @InitFromAdaptedGeneric(constants.%T.035) { // CHECK:STDOUT: %T.loc9_27.1 => constants.%T.035 // CHECK:STDOUT: %T.binding.as_type => constants.%T.binding.as_type // CHECK:STDOUT: %pattern_type => constants.%pattern_type.9b9f0c.2 // CHECK:STDOUT: %.loc9_51.2 => constants.%.076a48.2 // CHECK:STDOUT: } // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/class/generic/member_access.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/int.carbon // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/class/generic/member_access.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/class/generic/member_access.carbon // --- member_access.carbon library "[[@TEST_NAME]]"; class Class(T:! Core.Copy) { var x: T; fn Get[self: Self]() -> T { //@dump-sem-ir-begin return self.x; //@dump-sem-ir-end } fn GetAddr[ref self: Self]() -> T* { //@dump-sem-ir-begin return &self.x; //@dump-sem-ir-end } } fn DirectFieldAccess(x: Class(i32)) -> i32 { //@dump-sem-ir-begin return x.x; //@dump-sem-ir-end } fn MethodCall(x: Class(i32)) -> i32 { //@dump-sem-ir-begin return x.Get(); //@dump-sem-ir-end } fn AddrMethodCall(p: Class(i32)*) -> i32 { //@dump-sem-ir-begin return *p->GetAddr(); //@dump-sem-ir-end } // --- static_member_fn_call.carbon library "[[@TEST_NAME]]"; class Class(T:! type) { fn Make() -> Class(T) { return {}; } } fn StaticMemberFunctionCall(T:! type) -> Class(T) { //@dump-sem-ir-begin return Class(T).Make(); //@dump-sem-ir-end } // CHECK:STDOUT: --- member_access.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %Copy.type: type = facet_type <@Copy> [concrete] // CHECK:STDOUT: %T.035: %Copy.type = symbolic_binding T, 0 [symbolic] // CHECK:STDOUT: %Class.847: type = class_type @Class, @Class(%T.035) [symbolic] // CHECK:STDOUT: %T.binding.as_type: type = symbolic_binding_type T, 0, %T.035 [symbolic] // CHECK:STDOUT: %require_complete.67c: = require_complete_type %T.binding.as_type [symbolic] // CHECK:STDOUT: %Class.elem.05d: type = unbound_element_type %Class.847, %T.binding.as_type [symbolic] // CHECK:STDOUT: %pattern_type.893: type = pattern_type %Class.847 [symbolic] // CHECK:STDOUT: %.076a48.2: Core.Form = init_form %T.binding.as_type [symbolic] // CHECK:STDOUT: %pattern_type.9b9f0c.2: type = pattern_type %T.binding.as_type [symbolic] // CHECK:STDOUT: %Class.Get.type.8ea: type = fn_type @Class.Get, @Class(%T.035) [symbolic] // CHECK:STDOUT: %Class.Get.7d3: %Class.Get.type.8ea = struct_value () [symbolic] // CHECK:STDOUT: %ptr.e7d: type = ptr_type %T.binding.as_type [symbolic] // CHECK:STDOUT: %.66f: Core.Form = init_form %ptr.e7d [symbolic] // CHECK:STDOUT: %pattern_type.65a: type = pattern_type %ptr.e7d [symbolic] // CHECK:STDOUT: %Class.GetAddr.type.437: type = fn_type @Class.GetAddr, @Class(%T.035) [symbolic] // CHECK:STDOUT: %Class.GetAddr.7a1: %Class.GetAddr.type.437 = struct_value () [symbolic] // CHECK:STDOUT: %struct_type.x.8dc: type = struct_type {.x: %T.binding.as_type} [symbolic] // CHECK:STDOUT: %complete_type.e78: = complete_type_witness %struct_type.x.8dc [symbolic] // CHECK:STDOUT: %Copy.lookup_impl_witness.58d: = lookup_impl_witness %T.035, @Copy [symbolic] // CHECK:STDOUT: %Copy.WithSelf.Op.type.735e75.2: type = fn_type @Copy.WithSelf.Op, @Copy.WithSelf(%T.035) [symbolic] // CHECK:STDOUT: %.023: type = fn_type_with_self_type %Copy.WithSelf.Op.type.735e75.2, %T.035 [symbolic] // CHECK:STDOUT: %impl.elem0.594: %.023 = impl_witness_access %Copy.lookup_impl_witness.58d, element0 [symbolic] // CHECK:STDOUT: %specific_impl_fn.bdc: = specific_impl_function %impl.elem0.594, @Copy.WithSelf.Op(%T.035) [symbolic] // CHECK:STDOUT: %T.67d: type = symbolic_binding T, 0 [symbolic] // CHECK:STDOUT: %ptr.as.Copy.impl.Op.type.2d4: type = fn_type @ptr.as.Copy.impl.Op, @ptr.as.Copy.impl(%T.67d) [symbolic] // CHECK:STDOUT: %ptr.as.Copy.impl.Op.74e: %ptr.as.Copy.impl.Op.type.2d4 = struct_value () [symbolic] // CHECK:STDOUT: %.3a3: require_specific_def_type = require_specific_def @ptr.as.Copy.impl(%T.binding.as_type) [symbolic] // CHECK:STDOUT: %Copy.lookup_impl_witness.2e7: = lookup_impl_witness %ptr.e7d, @Copy [symbolic] // CHECK:STDOUT: %Copy.facet.8e7: %Copy.type = facet_value %ptr.e7d, (%Copy.lookup_impl_witness.2e7) [symbolic] // CHECK:STDOUT: %Copy.WithSelf.Op.type.486: type = fn_type @Copy.WithSelf.Op, @Copy.WithSelf(%Copy.facet.8e7) [symbolic] // CHECK:STDOUT: %.b63: type = fn_type_with_self_type %Copy.WithSelf.Op.type.486, %Copy.facet.8e7 [symbolic] // CHECK:STDOUT: %impl.elem0.387: %.b63 = impl_witness_access %Copy.lookup_impl_witness.2e7, element0 [symbolic] // CHECK:STDOUT: %specific_impl_fn.7f8: = specific_impl_function %impl.elem0.387, @Copy.WithSelf.Op(%Copy.facet.8e7) [symbolic] // CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [concrete] // CHECK:STDOUT: %N: Core.IntLiteral = symbolic_binding N, 0 [symbolic] // CHECK:STDOUT: %i32: type = class_type @Int, @Int(%int_32) [concrete] // CHECK:STDOUT: %Int.as.Copy.impl.Op.type.824: type = fn_type @Int.as.Copy.impl.Op, @Int.as.Copy.impl(%N) [symbolic] // CHECK:STDOUT: %Int.as.Copy.impl.Op.9b9: %Int.as.Copy.impl.Op.type.824 = struct_value () [symbolic] // CHECK:STDOUT: %Copy.impl_witness.f17: = impl_witness imports.%Copy.impl_witness_table.e76, @Int.as.Copy.impl(%int_32) [concrete] // CHECK:STDOUT: %Int.as.Copy.impl.Op.type.546: type = fn_type @Int.as.Copy.impl.Op, @Int.as.Copy.impl(%int_32) [concrete] // CHECK:STDOUT: %Int.as.Copy.impl.Op.664: %Int.as.Copy.impl.Op.type.546 = struct_value () [concrete] // CHECK:STDOUT: %i32.builtin: type = int_type signed, %int_32 [concrete] // CHECK:STDOUT: %complete_type.f8a: = complete_type_witness %i32.builtin [concrete] // CHECK:STDOUT: %Copy.facet.de4: %Copy.type = facet_value %i32, (%Copy.impl_witness.f17) [concrete] // CHECK:STDOUT: %Class.06a: type = class_type @Class, @Class(%Copy.facet.de4) [concrete] // CHECK:STDOUT: %pattern_type.cea: type = pattern_type %Class.06a [concrete] // CHECK:STDOUT: %.ff5: Core.Form = init_form %i32 [concrete] // CHECK:STDOUT: %pattern_type.7ce: type = pattern_type %i32 [concrete] // CHECK:STDOUT: %Class.elem.da5: type = unbound_element_type %Class.06a, %i32 [concrete] // CHECK:STDOUT: %Class.Get.type.bea: type = fn_type @Class.Get, @Class(%Copy.facet.de4) [concrete] // CHECK:STDOUT: %Class.Get.275: %Class.Get.type.bea = struct_value () [concrete] // CHECK:STDOUT: %Class.GetAddr.type.d64: type = fn_type @Class.GetAddr, @Class(%Copy.facet.de4) [concrete] // CHECK:STDOUT: %Class.GetAddr.7d7: %Class.GetAddr.type.d64 = struct_value () [concrete] // CHECK:STDOUT: %struct_type.x.ed6: type = struct_type {.x: %i32} [concrete] // CHECK:STDOUT: %complete_type.1ec: = complete_type_witness %struct_type.x.ed6 [concrete] // CHECK:STDOUT: %Copy.WithSelf.Op.type.081: type = fn_type @Copy.WithSelf.Op, @Copy.WithSelf(%Copy.facet.de4) [concrete] // CHECK:STDOUT: %.8e2: type = fn_type_with_self_type %Copy.WithSelf.Op.type.081, %Copy.facet.de4 [concrete] // CHECK:STDOUT: %Int.as.Copy.impl.Op.specific_fn: = specific_function %Int.as.Copy.impl.Op.664, @Int.as.Copy.impl.Op(%int_32) [concrete] // CHECK:STDOUT: %Class.Get.specific_fn: = specific_function %Class.Get.275, @Class.Get(%Copy.facet.de4) [concrete] // CHECK:STDOUT: %ptr.7d6: type = ptr_type %Class.06a [concrete] // CHECK:STDOUT: %ptr.235: type = ptr_type %i32 [concrete] // CHECK:STDOUT: %.605: Core.Form = init_form %ptr.235 [concrete] // CHECK:STDOUT: %pattern_type.fe8: type = pattern_type %ptr.235 [concrete] // CHECK:STDOUT: %Class.GetAddr.specific_fn: = specific_function %Class.GetAddr.7d7, @Class.GetAddr(%Copy.facet.de4) [concrete] // CHECK:STDOUT: %complete_type.3d0: = complete_type_witness %ptr.235 [concrete] // CHECK:STDOUT: %Copy.impl_witness.843: = impl_witness imports.%Copy.impl_witness_table.c3a, @ptr.as.Copy.impl(%i32) [concrete] // CHECK:STDOUT: %ptr.as.Copy.impl.Op.type.c3c: type = fn_type @ptr.as.Copy.impl.Op, @ptr.as.Copy.impl(%i32) [concrete] // CHECK:STDOUT: %ptr.as.Copy.impl.Op.011: %ptr.as.Copy.impl.Op.type.c3c = struct_value () [concrete] // CHECK:STDOUT: %.cab: require_specific_def_type = require_specific_def @ptr.as.Copy.impl(%i32) [concrete] // CHECK:STDOUT: %Copy.facet.a7b: %Copy.type = facet_value %ptr.235, (%Copy.impl_witness.843) [concrete] // CHECK:STDOUT: %Copy.WithSelf.Op.type.e01: type = fn_type @Copy.WithSelf.Op, @Copy.WithSelf(%Copy.facet.a7b) [concrete] // CHECK:STDOUT: %.a62: type = fn_type_with_self_type %Copy.WithSelf.Op.type.e01, %Copy.facet.a7b [concrete] // CHECK:STDOUT: %ptr.as.Copy.impl.Op.specific_fn: = specific_function %ptr.as.Copy.impl.Op.011, @ptr.as.Copy.impl.Op(%i32) [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core.import_ref.203: @ptr.as.Copy.impl.%ptr.as.Copy.impl.Op.type (%ptr.as.Copy.impl.Op.type.2d4) = import_ref Core//prelude/parts/copy, loc{{\d+_\d+}}, loaded [symbolic = @ptr.as.Copy.impl.%ptr.as.Copy.impl.Op (constants.%ptr.as.Copy.impl.Op.74e)] // CHECK:STDOUT: %Copy.impl_witness_table.c3a = impl_witness_table (%Core.import_ref.203), @ptr.as.Copy.impl [concrete] // CHECK:STDOUT: %Core.import_ref.18d: @Int.as.Copy.impl.%Int.as.Copy.impl.Op.type (%Int.as.Copy.impl.Op.type.824) = import_ref Core//prelude/parts/int, loc{{\d+_\d+}}, loaded [symbolic = @Int.as.Copy.impl.%Int.as.Copy.impl.Op (constants.%Int.as.Copy.impl.Op.9b9)] // CHECK:STDOUT: %Copy.impl_witness_table.e76 = impl_witness_table (%Core.import_ref.18d), @Int.as.Copy.impl [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic class @Class(%T.loc4_13.2: %Copy.type) { // CHECK:STDOUT: // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: // CHECK:STDOUT: // CHECK:STDOUT: class { // CHECK:STDOUT: // CHECK:STDOUT: complete_type_witness = %complete_type.loc18_1.1 // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%Class.847 // CHECK:STDOUT: .T = // CHECK:STDOUT: .x = %.loc5_8 // CHECK:STDOUT: .Get = %Class.Get.decl // CHECK:STDOUT: .GetAddr = %Class.GetAddr.decl // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic fn @Class.Get(@Class.%T.loc4_13.2: %Copy.type) { // CHECK:STDOUT: // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: // CHECK:STDOUT: %Class.elem: type = unbound_element_type %Class, %T.binding.as_type [symbolic = %Class.elem (constants.%Class.elem.05d)] // CHECK:STDOUT: %require_complete.loc9: = require_complete_type %T.binding.as_type [symbolic = %require_complete.loc9 (constants.%require_complete.67c)] // CHECK:STDOUT: %Copy.lookup_impl_witness: = lookup_impl_witness %T, @Copy [symbolic = %Copy.lookup_impl_witness (constants.%Copy.lookup_impl_witness.58d)] // CHECK:STDOUT: %Copy.WithSelf.Op.type: type = fn_type @Copy.WithSelf.Op, @Copy.WithSelf(%T) [symbolic = %Copy.WithSelf.Op.type (constants.%Copy.WithSelf.Op.type.735e75.2)] // CHECK:STDOUT: %.loc9_16.3: type = fn_type_with_self_type %Copy.WithSelf.Op.type, %T [symbolic = %.loc9_16.3 (constants.%.023)] // CHECK:STDOUT: %impl.elem0.loc9_16.2: @Class.Get.%.loc9_16.3 (%.023) = impl_witness_access %Copy.lookup_impl_witness, element0 [symbolic = %impl.elem0.loc9_16.2 (constants.%impl.elem0.594)] // CHECK:STDOUT: %specific_impl_fn.loc9_16.2: = specific_impl_function %impl.elem0.loc9_16.2, @Copy.WithSelf.Op(%T) [symbolic = %specific_impl_fn.loc9_16.2 (constants.%specific_impl_fn.bdc)] // CHECK:STDOUT: // CHECK:STDOUT: fn(%self.param: @Class.Get.%Class (%Class.847)) -> out %return.param: @Class.Get.%T.binding.as_type (%T.binding.as_type) { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %self.ref: @Class.Get.%Class (%Class.847) = name_ref self, %self // CHECK:STDOUT: %x.ref: @Class.Get.%Class.elem (%Class.elem.05d) = name_ref x, @Class.%.loc5_8 [concrete = @Class.%.loc5_8] // CHECK:STDOUT: %.loc9_16.1: ref @Class.Get.%T.binding.as_type (%T.binding.as_type) = class_element_access %self.ref, element0 // CHECK:STDOUT: %.loc9_16.2: @Class.Get.%T.binding.as_type (%T.binding.as_type) = acquire_value %.loc9_16.1 // CHECK:STDOUT: %impl.elem0.loc9_16.1: @Class.Get.%.loc9_16.3 (%.023) = impl_witness_access constants.%Copy.lookup_impl_witness.58d, element0 [symbolic = %impl.elem0.loc9_16.2 (constants.%impl.elem0.594)] // CHECK:STDOUT: %bound_method.loc9_16.1: = bound_method %.loc9_16.2, %impl.elem0.loc9_16.1 // CHECK:STDOUT: %specific_impl_fn.loc9_16.1: = specific_impl_function %impl.elem0.loc9_16.1, @Copy.WithSelf.Op(constants.%T.035) [symbolic = %specific_impl_fn.loc9_16.2 (constants.%specific_impl_fn.bdc)] // CHECK:STDOUT: %bound_method.loc9_16.2: = bound_method %.loc9_16.2, %specific_impl_fn.loc9_16.1 // CHECK:STDOUT: // CHECK:STDOUT: %Copy.WithSelf.Op.call: init @Class.Get.%T.binding.as_type (%T.binding.as_type) to %.loc7_27.1 = call %bound_method.loc9_16.2(%.loc9_16.2) // CHECK:STDOUT: return %Copy.WithSelf.Op.call to %return.param // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic fn @Class.GetAddr(@Class.%T.loc4_13.2: %Copy.type) { // CHECK:STDOUT: // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: // CHECK:STDOUT: %Class.elem: type = unbound_element_type %Class, %T.binding.as_type [symbolic = %Class.elem (constants.%Class.elem.05d)] // CHECK:STDOUT: %.loc15_12.1: require_specific_def_type = require_specific_def @ptr.as.Copy.impl(%T.binding.as_type) [symbolic = %.loc15_12.1 (constants.%.3a3)] // CHECK:STDOUT: %Copy.lookup_impl_witness: = lookup_impl_witness %ptr.loc13_36.1, @Copy [symbolic = %Copy.lookup_impl_witness (constants.%Copy.lookup_impl_witness.2e7)] // CHECK:STDOUT: %Copy.facet: %Copy.type = facet_value %ptr.loc13_36.1, (%Copy.lookup_impl_witness) [symbolic = %Copy.facet (constants.%Copy.facet.8e7)] // CHECK:STDOUT: %Copy.WithSelf.Op.type: type = fn_type @Copy.WithSelf.Op, @Copy.WithSelf(%Copy.facet) [symbolic = %Copy.WithSelf.Op.type (constants.%Copy.WithSelf.Op.type.486)] // CHECK:STDOUT: %.loc15_12.2: type = fn_type_with_self_type %Copy.WithSelf.Op.type, %Copy.facet [symbolic = %.loc15_12.2 (constants.%.b63)] // CHECK:STDOUT: %impl.elem0.loc15_12.2: @Class.GetAddr.%.loc15_12.2 (%.b63) = impl_witness_access %Copy.lookup_impl_witness, element0 [symbolic = %impl.elem0.loc15_12.2 (constants.%impl.elem0.387)] // CHECK:STDOUT: %specific_impl_fn.loc15_12.2: = specific_impl_function %impl.elem0.loc15_12.2, @Copy.WithSelf.Op(%Copy.facet) [symbolic = %specific_impl_fn.loc15_12.2 (constants.%specific_impl_fn.7f8)] // CHECK:STDOUT: // CHECK:STDOUT: fn(%self.param: ref @Class.GetAddr.%Class (%Class.847)) -> out %return.param: @Class.GetAddr.%ptr.loc13_36.1 (%ptr.e7d) { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %self.ref: ref @Class.GetAddr.%Class (%Class.847) = name_ref self, %self // CHECK:STDOUT: %x.ref: @Class.GetAddr.%Class.elem (%Class.elem.05d) = name_ref x, @Class.%.loc5_8 [concrete = @Class.%.loc5_8] // CHECK:STDOUT: %.loc15_17: ref @Class.GetAddr.%T.binding.as_type (%T.binding.as_type) = class_element_access %self.ref, element0 // CHECK:STDOUT: %addr: @Class.GetAddr.%ptr.loc13_36.1 (%ptr.e7d) = addr_of %.loc15_17 // CHECK:STDOUT: %impl.elem0.loc15_12.1: @Class.GetAddr.%.loc15_12.2 (%.b63) = impl_witness_access constants.%Copy.lookup_impl_witness.2e7, element0 [symbolic = %impl.elem0.loc15_12.2 (constants.%impl.elem0.387)] // CHECK:STDOUT: %bound_method.loc15_12.1: = bound_method %addr, %impl.elem0.loc15_12.1 // CHECK:STDOUT: %specific_impl_fn.loc15_12.1: = specific_impl_function %impl.elem0.loc15_12.1, @Copy.WithSelf.Op(constants.%Copy.facet.8e7) [symbolic = %specific_impl_fn.loc15_12.2 (constants.%specific_impl_fn.7f8)] // CHECK:STDOUT: %bound_method.loc15_12.2: = bound_method %addr, %specific_impl_fn.loc15_12.1 // CHECK:STDOUT: %Copy.WithSelf.Op.call: init @Class.GetAddr.%ptr.loc13_36.1 (%ptr.e7d) = call %bound_method.loc15_12.2(%addr) // CHECK:STDOUT: return %Copy.WithSelf.Op.call // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @DirectFieldAccess(%x.param: %Class.06a) -> out %return.param: %i32 { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %x.ref.loc22_10: %Class.06a = name_ref x, %x // CHECK:STDOUT: %x.ref.loc22_11: %Class.elem.da5 = name_ref x, @Class.%.loc5_8 [concrete = @Class.%.loc5_8] // CHECK:STDOUT: %.loc22_11.1: ref %i32 = class_element_access %x.ref.loc22_10, element0 // CHECK:STDOUT: %.loc22_11.2: %i32 = acquire_value %.loc22_11.1 // CHECK:STDOUT: %impl.elem0: %.8e2 = impl_witness_access constants.%Copy.impl_witness.f17, element0 [concrete = constants.%Int.as.Copy.impl.Op.664] // CHECK:STDOUT: %bound_method.loc22_11.1: = bound_method %.loc22_11.2, %impl.elem0 // CHECK:STDOUT: %specific_fn: = specific_function %impl.elem0, @Int.as.Copy.impl.Op(constants.%int_32) [concrete = constants.%Int.as.Copy.impl.Op.specific_fn] // CHECK:STDOUT: %bound_method.loc22_11.2: = bound_method %.loc22_11.2, %specific_fn // CHECK:STDOUT: %Int.as.Copy.impl.Op.call: init %i32 = call %bound_method.loc22_11.2(%.loc22_11.2) // CHECK:STDOUT: return %Int.as.Copy.impl.Op.call // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @MethodCall(%x.param: %Class.06a) -> out %return.param: %i32 { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %x.ref: %Class.06a = name_ref x, %x // CHECK:STDOUT: %.loc28: %Class.Get.type.bea = specific_constant @Class.%Class.Get.decl, @Class(constants.%Copy.facet.de4) [concrete = constants.%Class.Get.275] // CHECK:STDOUT: %Get.ref: %Class.Get.type.bea = name_ref Get, %.loc28 [concrete = constants.%Class.Get.275] // CHECK:STDOUT: %Class.Get.bound: = bound_method %x.ref, %Get.ref // CHECK:STDOUT: %Class.Get.specific_fn: = specific_function %Get.ref, @Class.Get(constants.%Copy.facet.de4) [concrete = constants.%Class.Get.specific_fn] // CHECK:STDOUT: %bound_method: = bound_method %x.ref, %Class.Get.specific_fn // CHECK:STDOUT: %Class.Get.call: init %i32 = call %bound_method(%x.ref) // CHECK:STDOUT: return %Class.Get.call // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @AddrMethodCall(%p.param: %ptr.7d6) -> out %return.param: %i32 { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %p.ref: %ptr.7d6 = name_ref p, %p // CHECK:STDOUT: %.loc34_12.1: ref %Class.06a = deref %p.ref // CHECK:STDOUT: %.loc34_12.2: %Class.GetAddr.type.d64 = specific_constant @Class.%Class.GetAddr.decl, @Class(constants.%Copy.facet.de4) [concrete = constants.%Class.GetAddr.7d7] // CHECK:STDOUT: %GetAddr.ref: %Class.GetAddr.type.d64 = name_ref GetAddr, %.loc34_12.2 [concrete = constants.%Class.GetAddr.7d7] // CHECK:STDOUT: %Class.GetAddr.bound: = bound_method %.loc34_12.1, %GetAddr.ref // CHECK:STDOUT: %Class.GetAddr.specific_fn: = specific_function %GetAddr.ref, @Class.GetAddr(constants.%Copy.facet.de4) [concrete = constants.%Class.GetAddr.specific_fn] // CHECK:STDOUT: %bound_method.loc34_22: = bound_method %.loc34_12.1, %Class.GetAddr.specific_fn // CHECK:STDOUT: %Class.GetAddr.call: init %ptr.235 = call %bound_method.loc34_22(%.loc34_12.1) // CHECK:STDOUT: %.loc34_22.1: %ptr.235 = value_of_initializer %Class.GetAddr.call // CHECK:STDOUT: %.loc34_22.2: %ptr.235 = converted %Class.GetAddr.call, %.loc34_22.1 // CHECK:STDOUT: %.loc34_10.1: ref %i32 = deref %.loc34_22.2 // CHECK:STDOUT: %.loc34_10.2: %i32 = acquire_value %.loc34_10.1 // CHECK:STDOUT: %impl.elem0: %.8e2 = impl_witness_access constants.%Copy.impl_witness.f17, element0 [concrete = constants.%Int.as.Copy.impl.Op.664] // CHECK:STDOUT: %bound_method.loc34_10.1: = bound_method %.loc34_10.2, %impl.elem0 // CHECK:STDOUT: %specific_fn: = specific_function %impl.elem0, @Int.as.Copy.impl.Op(constants.%int_32) [concrete = constants.%Int.as.Copy.impl.Op.specific_fn] // CHECK:STDOUT: %bound_method.loc34_10.2: = bound_method %.loc34_10.2, %specific_fn // CHECK:STDOUT: %Int.as.Copy.impl.Op.call: init %i32 = call %bound_method.loc34_10.2(%.loc34_10.2) // CHECK:STDOUT: return %Int.as.Copy.impl.Op.call // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @Class(constants.%T.035) { // CHECK:STDOUT: %T.loc4_13.1 => constants.%T.035 // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %T.binding.as_type => constants.%T.binding.as_type // CHECK:STDOUT: %require_complete => constants.%require_complete.67c // CHECK:STDOUT: %Class => constants.%Class.847 // CHECK:STDOUT: %Class.elem => constants.%Class.elem.05d // CHECK:STDOUT: %Class.Get.type => constants.%Class.Get.type.8ea // CHECK:STDOUT: %Class.Get => constants.%Class.Get.7d3 // CHECK:STDOUT: %Class.GetAddr.type => constants.%Class.GetAddr.type.437 // CHECK:STDOUT: %Class.GetAddr => constants.%Class.GetAddr.7a1 // CHECK:STDOUT: %struct_type.x => constants.%struct_type.x.8dc // CHECK:STDOUT: %complete_type.loc18_1.2 => constants.%complete_type.e78 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @Class.Get(constants.%T.035) { // CHECK:STDOUT: %T => constants.%T.035 // CHECK:STDOUT: %Class => constants.%Class.847 // CHECK:STDOUT: %pattern_type.loc7_10 => constants.%pattern_type.893 // CHECK:STDOUT: %T.binding.as_type => constants.%T.binding.as_type // CHECK:STDOUT: %.loc7_27.2 => constants.%.076a48.2 // CHECK:STDOUT: %pattern_type.loc7_24 => constants.%pattern_type.9b9f0c.2 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @Class.GetAddr(constants.%T.035) { // CHECK:STDOUT: %T => constants.%T.035 // CHECK:STDOUT: %Class => constants.%Class.847 // CHECK:STDOUT: %pattern_type.loc13_18 => constants.%pattern_type.893 // CHECK:STDOUT: %T.binding.as_type => constants.%T.binding.as_type // CHECK:STDOUT: %ptr.loc13_36.1 => constants.%ptr.e7d // CHECK:STDOUT: %.loc13_36.1 => constants.%.66f // CHECK:STDOUT: %pattern_type.loc13_32 => constants.%pattern_type.65a // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @Class(constants.%Copy.facet.de4) { // CHECK:STDOUT: %T.loc4_13.1 => constants.%Copy.facet.de4 // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %T.binding.as_type => constants.%i32 // CHECK:STDOUT: %require_complete => constants.%complete_type.f8a // CHECK:STDOUT: %Class => constants.%Class.06a // CHECK:STDOUT: %Class.elem => constants.%Class.elem.da5 // CHECK:STDOUT: %Class.Get.type => constants.%Class.Get.type.bea // CHECK:STDOUT: %Class.Get => constants.%Class.Get.275 // CHECK:STDOUT: %Class.GetAddr.type => constants.%Class.GetAddr.type.d64 // CHECK:STDOUT: %Class.GetAddr => constants.%Class.GetAddr.7d7 // CHECK:STDOUT: %struct_type.x => constants.%struct_type.x.ed6 // CHECK:STDOUT: %complete_type.loc18_1.2 => constants.%complete_type.1ec // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @Class.Get(constants.%Copy.facet.de4) { // CHECK:STDOUT: %T => constants.%Copy.facet.de4 // CHECK:STDOUT: %Class => constants.%Class.06a // CHECK:STDOUT: %pattern_type.loc7_10 => constants.%pattern_type.cea // CHECK:STDOUT: %T.binding.as_type => constants.%i32 // CHECK:STDOUT: %.loc7_27.2 => constants.%.ff5 // CHECK:STDOUT: %pattern_type.loc7_24 => constants.%pattern_type.7ce // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete.loc7 => constants.%complete_type.1ec // CHECK:STDOUT: %Class.elem => constants.%Class.elem.da5 // CHECK:STDOUT: %require_complete.loc9 => constants.%complete_type.f8a // CHECK:STDOUT: %Copy.lookup_impl_witness => constants.%Copy.impl_witness.f17 // CHECK:STDOUT: %Copy.WithSelf.Op.type => constants.%Copy.WithSelf.Op.type.081 // CHECK:STDOUT: %.loc9_16.3 => constants.%.8e2 // CHECK:STDOUT: %impl.elem0.loc9_16.2 => constants.%Int.as.Copy.impl.Op.664 // CHECK:STDOUT: %specific_impl_fn.loc9_16.2 => constants.%Int.as.Copy.impl.Op.specific_fn // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @Class.GetAddr(constants.%Copy.facet.de4) { // CHECK:STDOUT: %T => constants.%Copy.facet.de4 // CHECK:STDOUT: %Class => constants.%Class.06a // CHECK:STDOUT: %pattern_type.loc13_18 => constants.%pattern_type.cea // CHECK:STDOUT: %T.binding.as_type => constants.%i32 // CHECK:STDOUT: %ptr.loc13_36.1 => constants.%ptr.235 // CHECK:STDOUT: %.loc13_36.1 => constants.%.605 // CHECK:STDOUT: %pattern_type.loc13_32 => constants.%pattern_type.fe8 // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete.loc13_22 => constants.%complete_type.1ec // CHECK:STDOUT: %require_complete.loc13_36 => constants.%complete_type.3d0 // CHECK:STDOUT: %Class.elem => constants.%Class.elem.da5 // CHECK:STDOUT: %.loc15_12.1 => constants.%.cab // CHECK:STDOUT: %Copy.lookup_impl_witness => constants.%Copy.impl_witness.843 // CHECK:STDOUT: %Copy.facet => constants.%Copy.facet.a7b // CHECK:STDOUT: %Copy.WithSelf.Op.type => constants.%Copy.WithSelf.Op.type.e01 // CHECK:STDOUT: %.loc15_12.2 => constants.%.a62 // CHECK:STDOUT: %impl.elem0.loc15_12.2 => constants.%ptr.as.Copy.impl.Op.011 // CHECK:STDOUT: %specific_impl_fn.loc15_12.2 => constants.%ptr.as.Copy.impl.Op.specific_fn // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- static_member_fn_call.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %T: type = symbolic_binding T, 0 [symbolic] // CHECK:STDOUT: %Class.type: type = generic_class_type @Class [concrete] // CHECK:STDOUT: %Class.generic: %Class.type = struct_value () [concrete] // CHECK:STDOUT: %Class: type = class_type @Class, @Class(%T) [symbolic] // CHECK:STDOUT: %.6f9: Core.Form = init_form %Class [symbolic] // CHECK:STDOUT: %pattern_type.466: type = pattern_type %Class [symbolic] // CHECK:STDOUT: %Class.Make.type: type = fn_type @Class.Make, @Class(%T) [symbolic] // CHECK:STDOUT: %Class.Make: %Class.Make.type = struct_value () [symbolic] // CHECK:STDOUT: %require_complete: = require_complete_type %Class [symbolic] // CHECK:STDOUT: %Class.Make.specific_fn: = specific_function %Class.Make, @Class.Make(%T) [symbolic] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic fn @StaticMemberFunctionCall(%T.loc8_29.2: type) { // CHECK:STDOUT: // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete: = require_complete_type %Class.loc8_49.1 [symbolic = %require_complete (constants.%require_complete)] // CHECK:STDOUT: %Class.Make.type: type = fn_type @Class.Make, @Class(%T.loc8_29.1) [symbolic = %Class.Make.type (constants.%Class.Make.type)] // CHECK:STDOUT: %Class.Make: @StaticMemberFunctionCall.%Class.Make.type (%Class.Make.type) = struct_value () [symbolic = %Class.Make (constants.%Class.Make)] // CHECK:STDOUT: %Class.Make.specific_fn.loc10_18.2: = specific_function %Class.Make, @Class.Make(%T.loc8_29.1) [symbolic = %Class.Make.specific_fn.loc10_18.2 (constants.%Class.Make.specific_fn)] // CHECK:STDOUT: // CHECK:STDOUT: fn() -> out %return.param: @StaticMemberFunctionCall.%Class.loc8_49.1 (%Class) { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %Class.ref.loc10: %Class.type = name_ref Class, file.%Class.decl [concrete = constants.%Class.generic] // CHECK:STDOUT: %T.ref.loc10: type = name_ref T, %T.loc8_29.2 [symbolic = %T.loc8_29.1 (constants.%T)] // CHECK:STDOUT: %Class.loc10: type = class_type @Class, @Class(constants.%T) [symbolic = %Class.loc8_49.1 (constants.%Class)] // CHECK:STDOUT: %.loc10: @StaticMemberFunctionCall.%Class.Make.type (%Class.Make.type) = specific_constant @Class.%Class.Make.decl, @Class(constants.%T) [symbolic = %Class.Make (constants.%Class.Make)] // CHECK:STDOUT: %Make.ref: @StaticMemberFunctionCall.%Class.Make.type (%Class.Make.type) = name_ref Make, %.loc10 [symbolic = %Class.Make (constants.%Class.Make)] // CHECK:STDOUT: %Class.Make.specific_fn.loc10_18.1: = specific_function %Make.ref, @Class.Make(constants.%T) [symbolic = %Class.Make.specific_fn.loc10_18.2 (constants.%Class.Make.specific_fn)] // CHECK:STDOUT: // CHECK:STDOUT: %Class.Make.call: init @StaticMemberFunctionCall.%Class.loc8_49.1 (%Class) to %.loc8_49.1 = call %Class.Make.specific_fn.loc10_18.1() // CHECK:STDOUT: return %Class.Make.call to %return.param // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @StaticMemberFunctionCall(constants.%T) { // CHECK:STDOUT: %T.loc8_29.1 => constants.%T // CHECK:STDOUT: %Class.loc8_49.1 => constants.%Class // CHECK:STDOUT: %.loc8_49.2 => constants.%.6f9 // CHECK:STDOUT: %pattern_type => constants.%pattern_type.466 // CHECK:STDOUT: } // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/class/generic/member_inline.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/convert.carbon // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/class/generic/member_inline.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/class/generic/member_inline.carbon // --- member_inline.carbon library "[[@TEST_NAME]]"; //@dump-sem-ir-begin class Class(T:! Core.Copy) { fn F(n: T) -> T { return n; } fn G[self: Self]() -> T { return self.n; } var n: T; } //@dump-sem-ir-end // --- fail_member_inline_missing_self_dot.carbon library "[[@TEST_NAME]]"; //@dump-sem-ir-begin class C(T:! Core.Copy) { fn F() { // CHECK:STDERR: fail_member_inline_missing_self_dot.carbon:[[@LINE+4]]:5: error: expression cannot be used as a value [UseOfNonExprAsValue] // CHECK:STDERR: data; // CHECK:STDERR: ^~~~ // CHECK:STDERR: data; } var data: {}; } //@dump-sem-ir-end // CHECK:STDOUT: --- member_inline.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %Copy.type: type = facet_type <@Copy> [concrete] // CHECK:STDOUT: %pattern_type.ce2: type = pattern_type %Copy.type [concrete] // CHECK:STDOUT: %T.035: %Copy.type = symbolic_binding T, 0 [symbolic] // CHECK:STDOUT: %Class.type: type = generic_class_type @Class [concrete] // CHECK:STDOUT: %Class.generic: %Class.type = struct_value () [concrete] // CHECK:STDOUT: %Class: type = class_type @Class, @Class(%T.035) [symbolic] // CHECK:STDOUT: %T.binding.as_type: type = symbolic_binding_type T, 0, %T.035 [symbolic] // CHECK:STDOUT: %pattern_type.9b9f0c.2: type = pattern_type %T.binding.as_type [symbolic] // CHECK:STDOUT: %.076a48.2: Core.Form = init_form %T.binding.as_type [symbolic] // CHECK:STDOUT: %Class.F.type: type = fn_type @Class.F, @Class(%T.035) [symbolic] // CHECK:STDOUT: %Class.F: %Class.F.type = struct_value () [symbolic] // CHECK:STDOUT: %pattern_type.893: type = pattern_type %Class [symbolic] // CHECK:STDOUT: %Class.G.type: type = fn_type @Class.G, @Class(%T.035) [symbolic] // CHECK:STDOUT: %Class.G: %Class.G.type = struct_value () [symbolic] // CHECK:STDOUT: %require_complete.67c: = require_complete_type %T.binding.as_type [symbolic] // CHECK:STDOUT: %Class.elem: type = unbound_element_type %Class, %T.binding.as_type [symbolic] // CHECK:STDOUT: %struct_type.n: type = struct_type {.n: %T.binding.as_type} [symbolic] // CHECK:STDOUT: %complete_type: = complete_type_witness %struct_type.n [symbolic] // CHECK:STDOUT: %Copy.lookup_impl_witness.58d: = lookup_impl_witness %T.035, @Copy [symbolic] // CHECK:STDOUT: %Copy.WithSelf.Op.type.735e75.2: type = fn_type @Copy.WithSelf.Op, @Copy.WithSelf(%T.035) [symbolic] // CHECK:STDOUT: %.023: type = fn_type_with_self_type %Copy.WithSelf.Op.type.735e75.2, %T.035 [symbolic] // CHECK:STDOUT: %impl.elem0.594: %.023 = impl_witness_access %Copy.lookup_impl_witness.58d, element0 [symbolic] // CHECK:STDOUT: %specific_impl_fn.bdc: = specific_impl_function %impl.elem0.594, @Copy.WithSelf.Op(%T.035) [symbolic] // CHECK:STDOUT: %require_complete.904: = require_complete_type %Class [symbolic] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: .Copy = %Core.Copy // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: %Core.Copy: type = import_ref Core//prelude/parts/copy, Copy, loaded [concrete = constants.%Copy.type] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: %Class.decl: %Class.type = class_decl @Class [concrete = constants.%Class.generic] { // CHECK:STDOUT: %T.patt: %pattern_type.ce2 = symbolic_binding_pattern T, 0 [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %.loc5: type = splice_block %Copy.ref [concrete = constants.%Copy.type] { // CHECK:STDOUT: // CHECK:STDOUT: %Core.ref: = name_ref Core, imports.%Core [concrete = imports.%Core] // CHECK:STDOUT: %Copy.ref: type = name_ref Copy, imports.%Core.Copy [concrete = constants.%Copy.type] // CHECK:STDOUT: } // CHECK:STDOUT: %T.loc5_13.2: %Copy.type = symbolic_binding T, 0 [symbolic = %T.loc5_13.1 (constants.%T.035)] // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic class @Class(%T.loc5_13.2: %Copy.type) { // CHECK:STDOUT: %T.loc5_13.1: %Copy.type = symbolic_binding T, 0 [symbolic = %T.loc5_13.1 (constants.%T.035)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %Class.F.type: type = fn_type @Class.F, @Class(%T.loc5_13.1) [symbolic = %Class.F.type (constants.%Class.F.type)] // CHECK:STDOUT: %Class.F: @Class.%Class.F.type (%Class.F.type) = struct_value () [symbolic = %Class.F (constants.%Class.F)] // CHECK:STDOUT: %Class.G.type: type = fn_type @Class.G, @Class(%T.loc5_13.1) [symbolic = %Class.G.type (constants.%Class.G.type)] // CHECK:STDOUT: %Class.G: @Class.%Class.G.type (%Class.G.type) = struct_value () [symbolic = %Class.G (constants.%Class.G)] // CHECK:STDOUT: %T.binding.as_type: type = symbolic_binding_type T, 0, %T.loc5_13.1 [symbolic = %T.binding.as_type (constants.%T.binding.as_type)] // CHECK:STDOUT: %require_complete: = require_complete_type %T.binding.as_type [symbolic = %require_complete (constants.%require_complete.67c)] // CHECK:STDOUT: %Class: type = class_type @Class, @Class(%T.loc5_13.1) [symbolic = %Class (constants.%Class)] // CHECK:STDOUT: %Class.elem: type = unbound_element_type %Class, %T.binding.as_type [symbolic = %Class.elem (constants.%Class.elem)] // CHECK:STDOUT: %struct_type.n: type = struct_type {.n: @Class.%T.binding.as_type (%T.binding.as_type)} [symbolic = %struct_type.n (constants.%struct_type.n)] // CHECK:STDOUT: %complete_type.loc15_1.2: = complete_type_witness %struct_type.n [symbolic = %complete_type.loc15_1.2 (constants.%complete_type)] // CHECK:STDOUT: // CHECK:STDOUT: class { // CHECK:STDOUT: %Class.F.decl: @Class.%Class.F.type (%Class.F.type) = fn_decl @Class.F [symbolic = @Class.%Class.F (constants.%Class.F)] { // CHECK:STDOUT: %n.patt: @Class.F.%pattern_type (%pattern_type.9b9f0c.2) = value_binding_pattern n [concrete] // CHECK:STDOUT: %n.param_patt: @Class.F.%pattern_type (%pattern_type.9b9f0c.2) = value_param_pattern %n.patt [concrete] // CHECK:STDOUT: %return.patt: @Class.F.%pattern_type (%pattern_type.9b9f0c.2) = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: @Class.F.%pattern_type (%pattern_type.9b9f0c.2) = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %T.ref.loc6_17: %Copy.type = name_ref T, @Class.%T.loc5_13.2 [symbolic = %T (constants.%T.035)] // CHECK:STDOUT: %T.as_type.loc6_17: type = facet_access_type %T.ref.loc6_17 [symbolic = %T.binding.as_type (constants.%T.binding.as_type)] // CHECK:STDOUT: %.loc6_17.3: type = converted %T.ref.loc6_17, %T.as_type.loc6_17 [symbolic = %T.binding.as_type (constants.%T.binding.as_type)] // CHECK:STDOUT: %.loc6_17.4: Core.Form = init_form %.loc6_17.3 [symbolic = %.loc6_17.2 (constants.%.076a48.2)] // CHECK:STDOUT: %n.param: @Class.F.%T.binding.as_type (%T.binding.as_type) = value_param call_param0 // CHECK:STDOUT: %.loc6_11.1: type = splice_block %.loc6_11.2 [symbolic = %T.binding.as_type (constants.%T.binding.as_type)] { // CHECK:STDOUT: %T.ref.loc6_11: %Copy.type = name_ref T, @Class.%T.loc5_13.2 [symbolic = %T (constants.%T.035)] // CHECK:STDOUT: %T.as_type.loc6_11: type = facet_access_type %T.ref.loc6_11 [symbolic = %T.binding.as_type (constants.%T.binding.as_type)] // CHECK:STDOUT: %.loc6_11.2: type = converted %T.ref.loc6_11, %T.as_type.loc6_11 [symbolic = %T.binding.as_type (constants.%T.binding.as_type)] // CHECK:STDOUT: } // CHECK:STDOUT: %n: @Class.F.%T.binding.as_type (%T.binding.as_type) = value_binding n, %n.param // CHECK:STDOUT: %return.param: ref @Class.F.%T.binding.as_type (%T.binding.as_type) = out_param call_param1 // CHECK:STDOUT: %return: ref @Class.F.%T.binding.as_type (%T.binding.as_type) = return_slot %return.param // CHECK:STDOUT: } // CHECK:STDOUT: %Class.G.decl: @Class.%Class.G.type (%Class.G.type) = fn_decl @Class.G [symbolic = @Class.%Class.G (constants.%Class.G)] { // CHECK:STDOUT: %self.patt: @Class.G.%pattern_type.loc10_8 (%pattern_type.893) = value_binding_pattern self [concrete] // CHECK:STDOUT: %self.param_patt: @Class.G.%pattern_type.loc10_8 (%pattern_type.893) = value_param_pattern %self.patt [concrete] // CHECK:STDOUT: %return.patt: @Class.G.%pattern_type.loc10_22 (%pattern_type.9b9f0c.2) = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: @Class.G.%pattern_type.loc10_22 (%pattern_type.9b9f0c.2) = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %T.ref: %Copy.type = name_ref T, @Class.%T.loc5_13.2 [symbolic = %T (constants.%T.035)] // CHECK:STDOUT: %T.as_type: type = facet_access_type %T.ref [symbolic = %T.binding.as_type (constants.%T.binding.as_type)] // CHECK:STDOUT: %.loc10_25.3: type = converted %T.ref, %T.as_type [symbolic = %T.binding.as_type (constants.%T.binding.as_type)] // CHECK:STDOUT: %.loc10_25.4: Core.Form = init_form %.loc10_25.3 [symbolic = %.loc10_25.2 (constants.%.076a48.2)] // CHECK:STDOUT: %self.param: @Class.G.%Class (%Class) = value_param call_param0 // CHECK:STDOUT: %.loc10_14.1: type = splice_block %Self.ref [symbolic = %Class (constants.%Class)] { // CHECK:STDOUT: %.loc10_14.2: type = specific_constant constants.%Class, @Class(constants.%T.035) [symbolic = %Class (constants.%Class)] // CHECK:STDOUT: %Self.ref: type = name_ref Self, %.loc10_14.2 [symbolic = %Class (constants.%Class)] // CHECK:STDOUT: } // CHECK:STDOUT: %self: @Class.G.%Class (%Class) = value_binding self, %self.param // CHECK:STDOUT: %return.param: ref @Class.G.%T.binding.as_type (%T.binding.as_type) = out_param call_param1 // CHECK:STDOUT: %return: ref @Class.G.%T.binding.as_type (%T.binding.as_type) = return_slot %return.param // CHECK:STDOUT: } // CHECK:STDOUT: %T.ref: %Copy.type = name_ref T, %T.loc5_13.2 [symbolic = %T.loc5_13.1 (constants.%T.035)] // CHECK:STDOUT: %T.as_type: type = facet_access_type %T.ref [symbolic = %T.binding.as_type (constants.%T.binding.as_type)] // CHECK:STDOUT: %.loc14_10: type = converted %T.ref, %T.as_type [symbolic = %T.binding.as_type (constants.%T.binding.as_type)] // CHECK:STDOUT: %.loc14_8: @Class.%Class.elem (%Class.elem) = field_decl n, element0 [concrete] // CHECK:STDOUT: %complete_type.loc15_1.1: = complete_type_witness constants.%struct_type.n [symbolic = %complete_type.loc15_1.2 (constants.%complete_type)] // CHECK:STDOUT: complete_type_witness = %complete_type.loc15_1.1 // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%Class // CHECK:STDOUT: .T = // CHECK:STDOUT: .F = %Class.F.decl // CHECK:STDOUT: .G = %Class.G.decl // CHECK:STDOUT: .n = %.loc14_8 // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic fn @Class.F(@Class.%T.loc5_13.2: %Copy.type) { // CHECK:STDOUT: %T: %Copy.type = symbolic_binding T, 0 [symbolic = %T (constants.%T.035)] // CHECK:STDOUT: %T.binding.as_type: type = symbolic_binding_type T, 0, %T [symbolic = %T.binding.as_type (constants.%T.binding.as_type)] // CHECK:STDOUT: %pattern_type: type = pattern_type %T.binding.as_type [symbolic = %pattern_type (constants.%pattern_type.9b9f0c.2)] // CHECK:STDOUT: %.loc6_17.2: Core.Form = init_form %T.binding.as_type [symbolic = %.loc6_17.2 (constants.%.076a48.2)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete: = require_complete_type %T.binding.as_type [symbolic = %require_complete (constants.%require_complete.67c)] // CHECK:STDOUT: %Copy.lookup_impl_witness: = lookup_impl_witness %T, @Copy [symbolic = %Copy.lookup_impl_witness (constants.%Copy.lookup_impl_witness.58d)] // CHECK:STDOUT: %Copy.WithSelf.Op.type: type = fn_type @Copy.WithSelf.Op, @Copy.WithSelf(%T) [symbolic = %Copy.WithSelf.Op.type (constants.%Copy.WithSelf.Op.type.735e75.2)] // CHECK:STDOUT: %.loc7: type = fn_type_with_self_type %Copy.WithSelf.Op.type, %T [symbolic = %.loc7 (constants.%.023)] // CHECK:STDOUT: %impl.elem0.loc7_12.2: @Class.F.%.loc7 (%.023) = impl_witness_access %Copy.lookup_impl_witness, element0 [symbolic = %impl.elem0.loc7_12.2 (constants.%impl.elem0.594)] // CHECK:STDOUT: %specific_impl_fn.loc7_12.2: = specific_impl_function %impl.elem0.loc7_12.2, @Copy.WithSelf.Op(%T) [symbolic = %specific_impl_fn.loc7_12.2 (constants.%specific_impl_fn.bdc)] // CHECK:STDOUT: // CHECK:STDOUT: fn(%n.param: @Class.F.%T.binding.as_type (%T.binding.as_type)) -> out %return.param: @Class.F.%T.binding.as_type (%T.binding.as_type) { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %n.ref: @Class.F.%T.binding.as_type (%T.binding.as_type) = name_ref n, %n // CHECK:STDOUT: %impl.elem0.loc7_12.1: @Class.F.%.loc7 (%.023) = impl_witness_access constants.%Copy.lookup_impl_witness.58d, element0 [symbolic = %impl.elem0.loc7_12.2 (constants.%impl.elem0.594)] // CHECK:STDOUT: %bound_method.loc7_12.1: = bound_method %n.ref, %impl.elem0.loc7_12.1 // CHECK:STDOUT: %specific_impl_fn.loc7_12.1: = specific_impl_function %impl.elem0.loc7_12.1, @Copy.WithSelf.Op(constants.%T.035) [symbolic = %specific_impl_fn.loc7_12.2 (constants.%specific_impl_fn.bdc)] // CHECK:STDOUT: %bound_method.loc7_12.2: = bound_method %n.ref, %specific_impl_fn.loc7_12.1 // CHECK:STDOUT: %.loc6_17.1: ref @Class.F.%T.binding.as_type (%T.binding.as_type) = splice_block %return.param {} // CHECK:STDOUT: %Copy.WithSelf.Op.call: init @Class.F.%T.binding.as_type (%T.binding.as_type) to %.loc6_17.1 = call %bound_method.loc7_12.2(%n.ref) // CHECK:STDOUT: return %Copy.WithSelf.Op.call to %return.param // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic fn @Class.G(@Class.%T.loc5_13.2: %Copy.type) { // CHECK:STDOUT: %T: %Copy.type = symbolic_binding T, 0 [symbolic = %T (constants.%T.035)] // CHECK:STDOUT: %Class: type = class_type @Class, @Class(%T) [symbolic = %Class (constants.%Class)] // CHECK:STDOUT: %pattern_type.loc10_8: type = pattern_type %Class [symbolic = %pattern_type.loc10_8 (constants.%pattern_type.893)] // CHECK:STDOUT: %T.binding.as_type: type = symbolic_binding_type T, 0, %T [symbolic = %T.binding.as_type (constants.%T.binding.as_type)] // CHECK:STDOUT: %.loc10_25.2: Core.Form = init_form %T.binding.as_type [symbolic = %.loc10_25.2 (constants.%.076a48.2)] // CHECK:STDOUT: %pattern_type.loc10_22: type = pattern_type %T.binding.as_type [symbolic = %pattern_type.loc10_22 (constants.%pattern_type.9b9f0c.2)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete.loc10: = require_complete_type %Class [symbolic = %require_complete.loc10 (constants.%require_complete.904)] // CHECK:STDOUT: %Class.elem: type = unbound_element_type %Class, %T.binding.as_type [symbolic = %Class.elem (constants.%Class.elem)] // CHECK:STDOUT: %require_complete.loc11: = require_complete_type %T.binding.as_type [symbolic = %require_complete.loc11 (constants.%require_complete.67c)] // CHECK:STDOUT: %Copy.lookup_impl_witness: = lookup_impl_witness %T, @Copy [symbolic = %Copy.lookup_impl_witness (constants.%Copy.lookup_impl_witness.58d)] // CHECK:STDOUT: %Copy.WithSelf.Op.type: type = fn_type @Copy.WithSelf.Op, @Copy.WithSelf(%T) [symbolic = %Copy.WithSelf.Op.type (constants.%Copy.WithSelf.Op.type.735e75.2)] // CHECK:STDOUT: %.loc11_16.3: type = fn_type_with_self_type %Copy.WithSelf.Op.type, %T [symbolic = %.loc11_16.3 (constants.%.023)] // CHECK:STDOUT: %impl.elem0.loc11_16.2: @Class.G.%.loc11_16.3 (%.023) = impl_witness_access %Copy.lookup_impl_witness, element0 [symbolic = %impl.elem0.loc11_16.2 (constants.%impl.elem0.594)] // CHECK:STDOUT: %specific_impl_fn.loc11_16.2: = specific_impl_function %impl.elem0.loc11_16.2, @Copy.WithSelf.Op(%T) [symbolic = %specific_impl_fn.loc11_16.2 (constants.%specific_impl_fn.bdc)] // CHECK:STDOUT: // CHECK:STDOUT: fn(%self.param: @Class.G.%Class (%Class)) -> out %return.param: @Class.G.%T.binding.as_type (%T.binding.as_type) { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %self.ref: @Class.G.%Class (%Class) = name_ref self, %self // CHECK:STDOUT: %n.ref: @Class.G.%Class.elem (%Class.elem) = name_ref n, @Class.%.loc14_8 [concrete = @Class.%.loc14_8] // CHECK:STDOUT: %.loc11_16.1: ref @Class.G.%T.binding.as_type (%T.binding.as_type) = class_element_access %self.ref, element0 // CHECK:STDOUT: %.loc11_16.2: @Class.G.%T.binding.as_type (%T.binding.as_type) = acquire_value %.loc11_16.1 // CHECK:STDOUT: %impl.elem0.loc11_16.1: @Class.G.%.loc11_16.3 (%.023) = impl_witness_access constants.%Copy.lookup_impl_witness.58d, element0 [symbolic = %impl.elem0.loc11_16.2 (constants.%impl.elem0.594)] // CHECK:STDOUT: %bound_method.loc11_16.1: = bound_method %.loc11_16.2, %impl.elem0.loc11_16.1 // CHECK:STDOUT: %specific_impl_fn.loc11_16.1: = specific_impl_function %impl.elem0.loc11_16.1, @Copy.WithSelf.Op(constants.%T.035) [symbolic = %specific_impl_fn.loc11_16.2 (constants.%specific_impl_fn.bdc)] // CHECK:STDOUT: %bound_method.loc11_16.2: = bound_method %.loc11_16.2, %specific_impl_fn.loc11_16.1 // CHECK:STDOUT: %.loc10_25.1: ref @Class.G.%T.binding.as_type (%T.binding.as_type) = splice_block %return.param {} // CHECK:STDOUT: %Copy.WithSelf.Op.call: init @Class.G.%T.binding.as_type (%T.binding.as_type) to %.loc10_25.1 = call %bound_method.loc11_16.2(%.loc11_16.2) // CHECK:STDOUT: return %Copy.WithSelf.Op.call to %return.param // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @Class(constants.%T.035) { // CHECK:STDOUT: %T.loc5_13.1 => constants.%T.035 // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %Class.F.type => constants.%Class.F.type // CHECK:STDOUT: %Class.F => constants.%Class.F // CHECK:STDOUT: %Class.G.type => constants.%Class.G.type // CHECK:STDOUT: %Class.G => constants.%Class.G // CHECK:STDOUT: %T.binding.as_type => constants.%T.binding.as_type // CHECK:STDOUT: %require_complete => constants.%require_complete.67c // CHECK:STDOUT: %Class => constants.%Class // CHECK:STDOUT: %Class.elem => constants.%Class.elem // CHECK:STDOUT: %struct_type.n => constants.%struct_type.n // CHECK:STDOUT: %complete_type.loc15_1.2 => constants.%complete_type // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @Class.F(constants.%T.035) { // CHECK:STDOUT: %T => constants.%T.035 // CHECK:STDOUT: %T.binding.as_type => constants.%T.binding.as_type // CHECK:STDOUT: %pattern_type => constants.%pattern_type.9b9f0c.2 // CHECK:STDOUT: %.loc6_17.2 => constants.%.076a48.2 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @Class.G(constants.%T.035) { // CHECK:STDOUT: %T => constants.%T.035 // CHECK:STDOUT: %Class => constants.%Class // CHECK:STDOUT: %pattern_type.loc10_8 => constants.%pattern_type.893 // CHECK:STDOUT: %T.binding.as_type => constants.%T.binding.as_type // CHECK:STDOUT: %.loc10_25.2 => constants.%.076a48.2 // CHECK:STDOUT: %pattern_type.loc10_22 => constants.%pattern_type.9b9f0c.2 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- fail_member_inline_missing_self_dot.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %Copy.type: type = facet_type <@Copy> [concrete] // CHECK:STDOUT: %pattern_type.ce2: type = pattern_type %Copy.type [concrete] // CHECK:STDOUT: %T: %Copy.type = symbolic_binding T, 0 [symbolic] // CHECK:STDOUT: %C.type: type = generic_class_type @C [concrete] // CHECK:STDOUT: %C.generic: %C.type = struct_value () [concrete] // CHECK:STDOUT: %C: type = class_type @C, @C(%T) [symbolic] // CHECK:STDOUT: %C.F.type: type = fn_type @C.F, @C(%T) [symbolic] // CHECK:STDOUT: %C.F: %C.F.type = struct_value () [symbolic] // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %empty_struct: %empty_struct_type = struct_value () [concrete] // CHECK:STDOUT: %C.elem: type = unbound_element_type %C, %empty_struct_type [symbolic] // CHECK:STDOUT: %struct_type.data: type = struct_type {.data: %empty_struct_type} [concrete] // CHECK:STDOUT: %complete_type: = complete_type_witness %struct_type.data [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: .Copy = %Core.Copy // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: %Core.Copy: type = import_ref Core//prelude/parts/copy, Copy, loaded [concrete = constants.%Copy.type] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: %C.decl: %C.type = class_decl @C [concrete = constants.%C.generic] { // CHECK:STDOUT: %T.patt: %pattern_type.ce2 = symbolic_binding_pattern T, 0 [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %.loc5: type = splice_block %Copy.ref [concrete = constants.%Copy.type] { // CHECK:STDOUT: // CHECK:STDOUT: %Core.ref: = name_ref Core, imports.%Core [concrete = imports.%Core] // CHECK:STDOUT: %Copy.ref: type = name_ref Copy, imports.%Core.Copy [concrete = constants.%Copy.type] // CHECK:STDOUT: } // CHECK:STDOUT: %T.loc5_9.2: %Copy.type = symbolic_binding T, 0 [symbolic = %T.loc5_9.1 (constants.%T)] // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic class @C(%T.loc5_9.2: %Copy.type) { // CHECK:STDOUT: %T.loc5_9.1: %Copy.type = symbolic_binding T, 0 [symbolic = %T.loc5_9.1 (constants.%T)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %C.F.type: type = fn_type @C.F, @C(%T.loc5_9.1) [symbolic = %C.F.type (constants.%C.F.type)] // CHECK:STDOUT: %C.F: @C.%C.F.type (%C.F.type) = struct_value () [symbolic = %C.F (constants.%C.F)] // CHECK:STDOUT: %C: type = class_type @C, @C(%T.loc5_9.1) [symbolic = %C (constants.%C)] // CHECK:STDOUT: %C.elem: type = unbound_element_type %C, constants.%empty_struct_type [symbolic = %C.elem (constants.%C.elem)] // CHECK:STDOUT: // CHECK:STDOUT: class { // CHECK:STDOUT: %C.F.decl: @C.%C.F.type (%C.F.type) = fn_decl @C.F [symbolic = @C.%C.F (constants.%C.F)] {} {} // CHECK:STDOUT: %.loc13_14.1: %empty_struct_type = struct_literal () [concrete = constants.%empty_struct] // CHECK:STDOUT: %.loc13_14.2: type = converted %.loc13_14.1, constants.%empty_struct_type [concrete = constants.%empty_struct_type] // CHECK:STDOUT: %.loc13_11: @C.%C.elem (%C.elem) = field_decl data, element0 [concrete] // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%struct_type.data [concrete = constants.%complete_type] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%C // CHECK:STDOUT: .F = %C.F.decl // CHECK:STDOUT: .data = %.loc13_11 // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic fn @C.F(@C.%T.loc5_9.2: %Copy.type) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: %T: %Copy.type = symbolic_binding T, 0 [symbolic = %T (constants.%T)] // CHECK:STDOUT: %C: type = class_type @C, @C(%T) [symbolic = %C (constants.%C)] // CHECK:STDOUT: %C.elem: type = unbound_element_type %C, constants.%empty_struct_type [symbolic = %C.elem (constants.%C.elem)] // CHECK:STDOUT: // CHECK:STDOUT: fn() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %data.ref: @C.F.%C.elem (%C.elem) = name_ref data, @C.%.loc13_11 [concrete = @C.%.loc13_11] // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @C(constants.%T) { // CHECK:STDOUT: %T.loc5_9.1 => constants.%T // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %C.F.type => constants.%C.F.type // CHECK:STDOUT: %C.F => constants.%C.F // CHECK:STDOUT: %C => constants.%C // CHECK:STDOUT: %C.elem => constants.%C.elem // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @C.F(constants.%T) {} // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/class/generic/member_lookup.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/int.carbon // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/class/generic/member_lookup.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/class/generic/member_lookup.carbon // --- member_access.carbon library "[[@TEST_NAME]]"; base class Base(T:! type) { var b: T; } class Derived(T:! type) { extend base: Base(T); var d: T; } fn AccessDerived[T:! Core.Copy](x: Derived(T)) -> T { //@dump-sem-ir-begin return x.d; //@dump-sem-ir-end } fn AccessBase[T:! Core.Copy](x: Derived(T)) -> T { //@dump-sem-ir-begin return x.b; //@dump-sem-ir-end } fn AccessConcrete(x: Derived(i32)) -> i32 { return x.b; } // --- fail_no_member.carbon library "[[@TEST_NAME]]"; base class Base(T:! type) { var b: T; } class Derived(T:! type) { extend base: Base(T); var d: T; } fn AccessMissingBase[T:! type](x: Base(T)) -> T { // CHECK:STDERR: fail_no_member.carbon:[[@LINE+4]]:10: error: member name `nonesuch` not found in `Base(T)` [MemberNameNotFoundInSpecificScope] // CHECK:STDERR: return x.nonesuch; // CHECK:STDERR: ^~~~~~~~~~ // CHECK:STDERR: return x.nonesuch; } fn AccessMissingDerived[T:! type](x: Derived(T)) -> T { // CHECK:STDERR: fail_no_member.carbon:[[@LINE+4]]:10: error: member name `nonesuch` not found in `Derived(T)` [MemberNameNotFoundInSpecificScope] // CHECK:STDERR: return x.nonesuch; // CHECK:STDERR: ^~~~~~~~~~ // CHECK:STDERR: return x.nonesuch; } fn AccessMissingConcrete(x: Derived(i32)) -> i32 { // CHECK:STDERR: fail_no_member.carbon:[[@LINE+4]]:10: error: member name `nonesuch` not found in `Derived(i32)` [MemberNameNotFoundInSpecificScope] // CHECK:STDERR: return x.nonesuch; // CHECK:STDERR: ^~~~~~~~~~ // CHECK:STDERR: return x.nonesuch; } // CHECK:STDOUT: --- member_access.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %Copy.type: type = facet_type <@Copy> [concrete] // CHECK:STDOUT: %T.035: %Copy.type = symbolic_binding T, 0 [symbolic] // CHECK:STDOUT: %T.binding.as_type: type = symbolic_binding_type T, 0, %T.035 [symbolic] // CHECK:STDOUT: %Derived.ad7: type = class_type @Derived, @Derived(%T.binding.as_type) [symbolic] // CHECK:STDOUT: %pattern_type.d85: type = pattern_type %Derived.ad7 [symbolic] // CHECK:STDOUT: %.076a48.2: Core.Form = init_form %T.binding.as_type [symbolic] // CHECK:STDOUT: %pattern_type.9b9f0c.2: type = pattern_type %T.binding.as_type [symbolic] // CHECK:STDOUT: %Base.ab3: type = class_type @Base, @Base(%T.binding.as_type) [symbolic] // CHECK:STDOUT: %require_complete.b68: = require_complete_type %Base.ab3 [symbolic] // CHECK:STDOUT: %require_complete.67c: = require_complete_type %T.binding.as_type [symbolic] // CHECK:STDOUT: %Derived.elem.d6f: type = unbound_element_type %Derived.ad7, %T.binding.as_type [symbolic] // CHECK:STDOUT: %Base.elem.384: type = unbound_element_type %Base.ab3, %T.binding.as_type [symbolic] // CHECK:STDOUT: %Copy.lookup_impl_witness.58d: = lookup_impl_witness %T.035, @Copy [symbolic] // CHECK:STDOUT: %Copy.WithSelf.Op.type.735e75.2: type = fn_type @Copy.WithSelf.Op, @Copy.WithSelf(%T.035) [symbolic] // CHECK:STDOUT: %.023: type = fn_type_with_self_type %Copy.WithSelf.Op.type.735e75.2, %T.035 [symbolic] // CHECK:STDOUT: %impl.elem0.594: %.023 = impl_witness_access %Copy.lookup_impl_witness.58d, element0 [symbolic] // CHECK:STDOUT: %specific_impl_fn.bdc: = specific_impl_function %impl.elem0.594, @Copy.WithSelf.Op(%T.035) [symbolic] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic fn @AccessDerived(%T.loc13_18.2: %Copy.type) { // CHECK:STDOUT: // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: // CHECK:STDOUT: %Derived.elem: type = unbound_element_type %Derived.loc13_45.1, %T.binding.as_type [symbolic = %Derived.elem (constants.%Derived.elem.d6f)] // CHECK:STDOUT: %require_complete.loc15: = require_complete_type %T.binding.as_type [symbolic = %require_complete.loc15 (constants.%require_complete.67c)] // CHECK:STDOUT: %Copy.lookup_impl_witness: = lookup_impl_witness %T.loc13_18.1, @Copy [symbolic = %Copy.lookup_impl_witness (constants.%Copy.lookup_impl_witness.58d)] // CHECK:STDOUT: %Copy.WithSelf.Op.type: type = fn_type @Copy.WithSelf.Op, @Copy.WithSelf(%T.loc13_18.1) [symbolic = %Copy.WithSelf.Op.type (constants.%Copy.WithSelf.Op.type.735e75.2)] // CHECK:STDOUT: %.loc15_11.3: type = fn_type_with_self_type %Copy.WithSelf.Op.type, %T.loc13_18.1 [symbolic = %.loc15_11.3 (constants.%.023)] // CHECK:STDOUT: %impl.elem0.loc15_11.2: @AccessDerived.%.loc15_11.3 (%.023) = impl_witness_access %Copy.lookup_impl_witness, element0 [symbolic = %impl.elem0.loc15_11.2 (constants.%impl.elem0.594)] // CHECK:STDOUT: %specific_impl_fn.loc15_11.2: = specific_impl_function %impl.elem0.loc15_11.2, @Copy.WithSelf.Op(%T.loc13_18.1) [symbolic = %specific_impl_fn.loc15_11.2 (constants.%specific_impl_fn.bdc)] // CHECK:STDOUT: // CHECK:STDOUT: fn(%x.param: @AccessDerived.%Derived.loc13_45.1 (%Derived.ad7)) -> out %return.param: @AccessDerived.%T.binding.as_type (%T.binding.as_type) { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %x.ref: @AccessDerived.%Derived.loc13_45.1 (%Derived.ad7) = name_ref x, %x // CHECK:STDOUT: %d.ref: @AccessDerived.%Derived.elem (%Derived.elem.d6f) = name_ref d, @Derived.%.loc10 [concrete = @Derived.%.loc10] // CHECK:STDOUT: %.loc15_11.1: ref @AccessDerived.%T.binding.as_type (%T.binding.as_type) = class_element_access %x.ref, element1 // CHECK:STDOUT: %.loc15_11.2: @AccessDerived.%T.binding.as_type (%T.binding.as_type) = acquire_value %.loc15_11.1 // CHECK:STDOUT: %impl.elem0.loc15_11.1: @AccessDerived.%.loc15_11.3 (%.023) = impl_witness_access constants.%Copy.lookup_impl_witness.58d, element0 [symbolic = %impl.elem0.loc15_11.2 (constants.%impl.elem0.594)] // CHECK:STDOUT: %bound_method.loc15_11.1: = bound_method %.loc15_11.2, %impl.elem0.loc15_11.1 // CHECK:STDOUT: %specific_impl_fn.loc15_11.1: = specific_impl_function %impl.elem0.loc15_11.1, @Copy.WithSelf.Op(constants.%T.035) [symbolic = %specific_impl_fn.loc15_11.2 (constants.%specific_impl_fn.bdc)] // CHECK:STDOUT: %bound_method.loc15_11.2: = bound_method %.loc15_11.2, %specific_impl_fn.loc15_11.1 // CHECK:STDOUT: // CHECK:STDOUT: %Copy.WithSelf.Op.call: init @AccessDerived.%T.binding.as_type (%T.binding.as_type) to %.loc13_51.1 = call %bound_method.loc15_11.2(%.loc15_11.2) // CHECK:STDOUT: return %Copy.WithSelf.Op.call to %return.param // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic fn @AccessBase(%T.loc19_15.2: %Copy.type) { // CHECK:STDOUT: // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: // CHECK:STDOUT: %Base: type = class_type @Base, @Base(%T.binding.as_type) [symbolic = %Base (constants.%Base.ab3)] // CHECK:STDOUT: %require_complete.loc21_11: = require_complete_type %Base [symbolic = %require_complete.loc21_11 (constants.%require_complete.b68)] // CHECK:STDOUT: %Base.elem: type = unbound_element_type %Base, %T.binding.as_type [symbolic = %Base.elem (constants.%Base.elem.384)] // CHECK:STDOUT: %require_complete.loc21_13: = require_complete_type %T.binding.as_type [symbolic = %require_complete.loc21_13 (constants.%require_complete.67c)] // CHECK:STDOUT: %Copy.lookup_impl_witness: = lookup_impl_witness %T.loc19_15.1, @Copy [symbolic = %Copy.lookup_impl_witness (constants.%Copy.lookup_impl_witness.58d)] // CHECK:STDOUT: %Copy.WithSelf.Op.type: type = fn_type @Copy.WithSelf.Op, @Copy.WithSelf(%T.loc19_15.1) [symbolic = %Copy.WithSelf.Op.type (constants.%Copy.WithSelf.Op.type.735e75.2)] // CHECK:STDOUT: %.loc21_11.5: type = fn_type_with_self_type %Copy.WithSelf.Op.type, %T.loc19_15.1 [symbolic = %.loc21_11.5 (constants.%.023)] // CHECK:STDOUT: %impl.elem0.loc21_11.2: @AccessBase.%.loc21_11.5 (%.023) = impl_witness_access %Copy.lookup_impl_witness, element0 [symbolic = %impl.elem0.loc21_11.2 (constants.%impl.elem0.594)] // CHECK:STDOUT: %specific_impl_fn.loc21_11.2: = specific_impl_function %impl.elem0.loc21_11.2, @Copy.WithSelf.Op(%T.loc19_15.1) [symbolic = %specific_impl_fn.loc21_11.2 (constants.%specific_impl_fn.bdc)] // CHECK:STDOUT: // CHECK:STDOUT: fn(%x.param: @AccessBase.%Derived.loc19_42.1 (%Derived.ad7)) -> out %return.param: @AccessBase.%T.binding.as_type (%T.binding.as_type) { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %x.ref: @AccessBase.%Derived.loc19_42.1 (%Derived.ad7) = name_ref x, %x // CHECK:STDOUT: %b.ref: @AccessBase.%Base.elem (%Base.elem.384) = name_ref b, @Base.%.loc5 [concrete = @Base.%.loc5] // CHECK:STDOUT: %.loc21_11.1: ref @AccessBase.%Base (%Base.ab3) = class_element_access %x.ref, element0 // CHECK:STDOUT: %.loc21_11.2: ref @AccessBase.%Base (%Base.ab3) = converted %x.ref, %.loc21_11.1 // CHECK:STDOUT: %.loc21_11.3: ref @AccessBase.%T.binding.as_type (%T.binding.as_type) = class_element_access %.loc21_11.2, element0 // CHECK:STDOUT: %.loc21_11.4: @AccessBase.%T.binding.as_type (%T.binding.as_type) = acquire_value %.loc21_11.3 // CHECK:STDOUT: %impl.elem0.loc21_11.1: @AccessBase.%.loc21_11.5 (%.023) = impl_witness_access constants.%Copy.lookup_impl_witness.58d, element0 [symbolic = %impl.elem0.loc21_11.2 (constants.%impl.elem0.594)] // CHECK:STDOUT: %bound_method.loc21_11.1: = bound_method %.loc21_11.4, %impl.elem0.loc21_11.1 // CHECK:STDOUT: %specific_impl_fn.loc21_11.1: = specific_impl_function %impl.elem0.loc21_11.1, @Copy.WithSelf.Op(constants.%T.035) [symbolic = %specific_impl_fn.loc21_11.2 (constants.%specific_impl_fn.bdc)] // CHECK:STDOUT: %bound_method.loc21_11.2: = bound_method %.loc21_11.4, %specific_impl_fn.loc21_11.1 // CHECK:STDOUT: // CHECK:STDOUT: %Copy.WithSelf.Op.call: init @AccessBase.%T.binding.as_type (%T.binding.as_type) to %.loc19_48.1 = call %bound_method.loc21_11.2(%.loc21_11.4) // CHECK:STDOUT: return %Copy.WithSelf.Op.call to %return.param // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @AccessDerived(constants.%T.035) { // CHECK:STDOUT: %T.loc13_18.1 => constants.%T.035 // CHECK:STDOUT: %T.binding.as_type => constants.%T.binding.as_type // CHECK:STDOUT: %Derived.loc13_45.1 => constants.%Derived.ad7 // CHECK:STDOUT: %pattern_type.loc13_33 => constants.%pattern_type.d85 // CHECK:STDOUT: %.loc13_51.2 => constants.%.076a48.2 // CHECK:STDOUT: %pattern_type.loc13_48 => constants.%pattern_type.9b9f0c.2 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @AccessBase(constants.%T.035) { // CHECK:STDOUT: %T.loc19_15.1 => constants.%T.035 // CHECK:STDOUT: %T.binding.as_type => constants.%T.binding.as_type // CHECK:STDOUT: %Derived.loc19_42.1 => constants.%Derived.ad7 // CHECK:STDOUT: %pattern_type.loc19_30 => constants.%pattern_type.d85 // CHECK:STDOUT: %.loc19_48.2 => constants.%.076a48.2 // CHECK:STDOUT: %pattern_type.loc19_45 => constants.%pattern_type.9b9f0c.2 // CHECK:STDOUT: } // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/class/generic/member_out_of_line.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/convert.carbon // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/class/generic/member_out_of_line.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/class/generic/member_out_of_line.carbon // --- basic.carbon library "[[@TEST_NAME]]"; //@dump-sem-ir-begin class Class(T:! Core.Copy) { fn F(n: T) -> T; fn G[self: Self]() -> T; var n: T; } fn Class(T:! Core.Copy).F(n: T) -> T { return n; } fn Class(T:! Core.Copy).G[self: Self]() -> T { return self.n; } //@dump-sem-ir-end // --- nested.carbon library "[[@TEST_NAME]]"; //@dump-sem-ir-begin class A(T:! type) { class B(_:! T) { fn F[self: Self](a: T); } } fn A(T:! type).B(_:! T).F[unused self: Self](unused a: T) {} //@dump-sem-ir-end // --- fail_mismatched_not_generic_vs_generic.carbon library "[[@TEST_NAME]]"; class NotGeneric { fn F(); } // CHECK:STDERR: fail_mismatched_not_generic_vs_generic.carbon:[[@LINE+7]]:4: error: redeclaration differs because of parameter list [RedeclParamListDiffers] // CHECK:STDERR: fn NotGeneric(unused T:! type).F() {} // CHECK:STDERR: ^~~~~~~~~~ // CHECK:STDERR: fail_mismatched_not_generic_vs_generic.carbon:[[@LINE-7]]:1: note: previously declared without parameter list [RedeclParamListPrevious] // CHECK:STDERR: class NotGeneric { // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~ // CHECK:STDERR: fn NotGeneric(unused T:! type).F() {} // --- fail_mismatched_too_few_args.carbon library "[[@TEST_NAME]]"; class Generic(unused T:! type) { fn TooFew(); } // CHECK:STDERR: fail_mismatched_too_few_args.carbon:[[@LINE+7]]:4: error: redeclaration differs because of parameter count of 0 [RedeclParamCountDiffers] // CHECK:STDERR: fn Generic().TooFew() {} // CHECK:STDERR: ^~~~~~~ // CHECK:STDERR: fail_mismatched_too_few_args.carbon:[[@LINE-7]]:1: note: previously declared with parameter count of 1 [RedeclParamCountPrevious] // CHECK:STDERR: class Generic(unused T:! type) { // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: fn Generic().TooFew() {} // --- fail_mismatched_too_many_args.carbon library "[[@TEST_NAME]]"; class Generic(T:! type) { fn TooMany(); } // CHECK:STDERR: fail_mismatched_too_many_args.carbon:[[@LINE+7]]:4: error: redeclaration differs because of parameter count of 2 [RedeclParamCountDiffers] // CHECK:STDERR: fn Generic(unused T:! type, unused U:! type).TooMany() {} // CHECK:STDERR: ^~~~~~~ // CHECK:STDERR: fail_mismatched_too_many_args.carbon:[[@LINE-7]]:1: note: previously declared with parameter count of 1 [RedeclParamCountPrevious] // CHECK:STDERR: class Generic(T:! type) { // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: fn Generic(unused T:! type, unused U:! type).TooMany() {} // --- fail_mismatched_wrong_arg_type.carbon library "[[@TEST_NAME]]"; class Generic(unused T:! type) { fn WrongType(); } // CHECK:STDERR: fail_mismatched_wrong_arg_type.carbon:[[@LINE+7]]:19: error: type `` of parameter 1 in redeclaration differs from previous parameter type `` [RedeclParamDiffersType] // CHECK:STDERR: fn Generic(unused T:! ()).WrongType() {} // CHECK:STDERR: ^ // CHECK:STDERR: fail_mismatched_wrong_arg_type.carbon:[[@LINE-7]]:22: note: previous declaration's corresponding parameter here [RedeclParamPrevious] // CHECK:STDERR: class Generic(unused T:! type) { // CHECK:STDERR: ^ // CHECK:STDERR: fn Generic(unused T:! ()).WrongType() {} // CHECK:STDOUT: --- basic.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %Copy.type: type = facet_type <@Copy> [concrete] // CHECK:STDOUT: %pattern_type.ce2: type = pattern_type %Copy.type [concrete] // CHECK:STDOUT: %T.035: %Copy.type = symbolic_binding T, 0 [symbolic] // CHECK:STDOUT: %Class.type: type = generic_class_type @Class [concrete] // CHECK:STDOUT: %Class.generic: %Class.type = struct_value () [concrete] // CHECK:STDOUT: %Class: type = class_type @Class, @Class(%T.035) [symbolic] // CHECK:STDOUT: %T.binding.as_type: type = symbolic_binding_type T, 0, %T.035 [symbolic] // CHECK:STDOUT: %pattern_type.9b9f0c.2: type = pattern_type %T.binding.as_type [symbolic] // CHECK:STDOUT: %.076a48.2: Core.Form = init_form %T.binding.as_type [symbolic] // CHECK:STDOUT: %Class.F.type: type = fn_type @Class.F, @Class(%T.035) [symbolic] // CHECK:STDOUT: %Class.F: %Class.F.type = struct_value () [symbolic] // CHECK:STDOUT: %pattern_type.893: type = pattern_type %Class [symbolic] // CHECK:STDOUT: %Class.G.type: type = fn_type @Class.G, @Class(%T.035) [symbolic] // CHECK:STDOUT: %Class.G: %Class.G.type = struct_value () [symbolic] // CHECK:STDOUT: %require_complete.67c: = require_complete_type %T.binding.as_type [symbolic] // CHECK:STDOUT: %Class.elem: type = unbound_element_type %Class, %T.binding.as_type [symbolic] // CHECK:STDOUT: %struct_type.n: type = struct_type {.n: %T.binding.as_type} [symbolic] // CHECK:STDOUT: %complete_type: = complete_type_witness %struct_type.n [symbolic] // CHECK:STDOUT: %Copy.lookup_impl_witness.58d: = lookup_impl_witness %T.035, @Copy [symbolic] // CHECK:STDOUT: %Copy.WithSelf.Op.type.735e75.2: type = fn_type @Copy.WithSelf.Op, @Copy.WithSelf(%T.035) [symbolic] // CHECK:STDOUT: %.023: type = fn_type_with_self_type %Copy.WithSelf.Op.type.735e75.2, %T.035 [symbolic] // CHECK:STDOUT: %impl.elem0.594: %.023 = impl_witness_access %Copy.lookup_impl_witness.58d, element0 [symbolic] // CHECK:STDOUT: %specific_impl_fn.bdc: = specific_impl_function %impl.elem0.594, @Copy.WithSelf.Op(%T.035) [symbolic] // CHECK:STDOUT: %require_complete.904: = require_complete_type %Class [symbolic] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: .Copy = %Core.Copy // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: %Core.Copy: type = import_ref Core//prelude/parts/copy, Copy, loaded [concrete = constants.%Copy.type] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: %Class.decl: %Class.type = class_decl @Class [concrete = constants.%Class.generic] { // CHECK:STDOUT: %T.patt: %pattern_type.ce2 = symbolic_binding_pattern T, 0 [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %.loc5: type = splice_block %Copy.ref [concrete = constants.%Copy.type] { // CHECK:STDOUT: // CHECK:STDOUT: %Core.ref: = name_ref Core, imports.%Core [concrete = imports.%Core] // CHECK:STDOUT: %Copy.ref: type = name_ref Copy, imports.%Core.Copy [concrete = constants.%Copy.type] // CHECK:STDOUT: } // CHECK:STDOUT: %T.loc5_13.2: %Copy.type = symbolic_binding T, 0 [symbolic = %T.loc5_13.1 (constants.%T.035)] // CHECK:STDOUT: } // CHECK:STDOUT: %Class.F.decl: %Class.F.type = fn_decl @Class.F [symbolic = constants.%Class.F] { // CHECK:STDOUT: %n.patt: @Class.F.%pattern_type (%pattern_type.9b9f0c.2) = value_binding_pattern n [concrete] // CHECK:STDOUT: %n.param_patt: @Class.F.%pattern_type (%pattern_type.9b9f0c.2) = value_param_pattern %n.patt [concrete] // CHECK:STDOUT: %return.patt: @Class.F.%pattern_type (%pattern_type.9b9f0c.2) = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: @Class.F.%pattern_type (%pattern_type.9b9f0c.2) = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %.loc11_18: type = splice_block %Copy.ref [concrete = constants.%Copy.type] { // CHECK:STDOUT: // CHECK:STDOUT: %Core.ref: = name_ref Core, imports.%Core [concrete = imports.%Core] // CHECK:STDOUT: %Copy.ref: type = name_ref Copy, imports.%Core.Copy [concrete = constants.%Copy.type] // CHECK:STDOUT: } // CHECK:STDOUT: %T.loc11: %Copy.type = symbolic_binding T, 0 [symbolic = @Class.%T.loc5_13.1 (constants.%T.035)] // CHECK:STDOUT: %T.ref.loc11_36: %Copy.type = name_ref T, %T.loc11 [symbolic = %T.loc6 (constants.%T.035)] // CHECK:STDOUT: %T.as_type.loc11_36: type = facet_access_type %T.ref.loc11_36 [symbolic = %T.binding.as_type (constants.%T.binding.as_type)] // CHECK:STDOUT: %.loc11_36.2: type = converted %T.ref.loc11_36, %T.as_type.loc11_36 [symbolic = %T.binding.as_type (constants.%T.binding.as_type)] // CHECK:STDOUT: %.loc11_36.3: Core.Form = init_form %.loc11_36.2 [symbolic = %.loc6_17.1 (constants.%.076a48.2)] // CHECK:STDOUT: %n.param.loc11: @Class.F.%T.binding.as_type (%T.binding.as_type) = value_param call_param0 // CHECK:STDOUT: %.loc11_30.1: type = splice_block %.loc11_30.2 [symbolic = %T.binding.as_type (constants.%T.binding.as_type)] { // CHECK:STDOUT: %T.ref.loc11_30: %Copy.type = name_ref T, %T.loc11 [symbolic = %T.loc6 (constants.%T.035)] // CHECK:STDOUT: %T.as_type.loc11_30: type = facet_access_type %T.ref.loc11_30 [symbolic = %T.binding.as_type (constants.%T.binding.as_type)] // CHECK:STDOUT: %.loc11_30.2: type = converted %T.ref.loc11_30, %T.as_type.loc11_30 [symbolic = %T.binding.as_type (constants.%T.binding.as_type)] // CHECK:STDOUT: } // CHECK:STDOUT: %n.loc11: @Class.F.%T.binding.as_type (%T.binding.as_type) = value_binding n, %n.param.loc11 // CHECK:STDOUT: %return.param.loc11: ref @Class.F.%T.binding.as_type (%T.binding.as_type) = out_param call_param1 // CHECK:STDOUT: %return.loc11: ref @Class.F.%T.binding.as_type (%T.binding.as_type) = return_slot %return.param.loc11 // CHECK:STDOUT: } // CHECK:STDOUT: %Class.G.decl: %Class.G.type = fn_decl @Class.G [symbolic = constants.%Class.G] { // CHECK:STDOUT: %self.patt: @Class.G.%pattern_type.loc7_8 (%pattern_type.893) = value_binding_pattern self [concrete] // CHECK:STDOUT: %self.param_patt: @Class.G.%pattern_type.loc7_8 (%pattern_type.893) = value_param_pattern %self.patt [concrete] // CHECK:STDOUT: %return.patt: @Class.G.%pattern_type.loc7_22 (%pattern_type.9b9f0c.2) = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: @Class.G.%pattern_type.loc7_22 (%pattern_type.9b9f0c.2) = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %.loc15_18: type = splice_block %Copy.ref [concrete = constants.%Copy.type] { // CHECK:STDOUT: // CHECK:STDOUT: %Core.ref: = name_ref Core, imports.%Core [concrete = imports.%Core] // CHECK:STDOUT: %Copy.ref: type = name_ref Copy, imports.%Core.Copy [concrete = constants.%Copy.type] // CHECK:STDOUT: } // CHECK:STDOUT: %T.loc15: %Copy.type = symbolic_binding T, 0 [symbolic = @Class.%T.loc5_13.1 (constants.%T.035)] // CHECK:STDOUT: %T.ref.loc15: %Copy.type = name_ref T, %T.loc15 [symbolic = %T.loc7 (constants.%T.035)] // CHECK:STDOUT: %T.as_type.loc15: type = facet_access_type %T.ref.loc15 [symbolic = %T.binding.as_type (constants.%T.binding.as_type)] // CHECK:STDOUT: %.loc15_44.2: type = converted %T.ref.loc15, %T.as_type.loc15 [symbolic = %T.binding.as_type (constants.%T.binding.as_type)] // CHECK:STDOUT: %.loc15_44.3: Core.Form = init_form %.loc15_44.2 [symbolic = %.loc7_25.1 (constants.%.076a48.2)] // CHECK:STDOUT: %self.param.loc15: @Class.G.%Class (%Class) = value_param call_param0 // CHECK:STDOUT: %.loc15_33.1: type = splice_block %Self.ref.loc15 [symbolic = %Class (constants.%Class)] { // CHECK:STDOUT: %.loc15_33.2: type = specific_constant constants.%Class, @Class(constants.%T.035) [symbolic = %Class (constants.%Class)] // CHECK:STDOUT: %Self.ref.loc15: type = name_ref Self, %.loc15_33.2 [symbolic = %Class (constants.%Class)] // CHECK:STDOUT: } // CHECK:STDOUT: %self.loc15: @Class.G.%Class (%Class) = value_binding self, %self.param.loc15 // CHECK:STDOUT: %return.param.loc15: ref @Class.G.%T.binding.as_type (%T.binding.as_type) = out_param call_param1 // CHECK:STDOUT: %return.loc15: ref @Class.G.%T.binding.as_type (%T.binding.as_type) = return_slot %return.param.loc15 // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic class @Class(%T.loc5_13.2: %Copy.type) { // CHECK:STDOUT: %T.loc5_13.1: %Copy.type = symbolic_binding T, 0 [symbolic = %T.loc5_13.1 (constants.%T.035)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %Class.F.type: type = fn_type @Class.F, @Class(%T.loc5_13.1) [symbolic = %Class.F.type (constants.%Class.F.type)] // CHECK:STDOUT: %Class.F: @Class.%Class.F.type (%Class.F.type) = struct_value () [symbolic = %Class.F (constants.%Class.F)] // CHECK:STDOUT: %Class.G.type: type = fn_type @Class.G, @Class(%T.loc5_13.1) [symbolic = %Class.G.type (constants.%Class.G.type)] // CHECK:STDOUT: %Class.G: @Class.%Class.G.type (%Class.G.type) = struct_value () [symbolic = %Class.G (constants.%Class.G)] // CHECK:STDOUT: %T.binding.as_type: type = symbolic_binding_type T, 0, %T.loc5_13.1 [symbolic = %T.binding.as_type (constants.%T.binding.as_type)] // CHECK:STDOUT: %require_complete: = require_complete_type %T.binding.as_type [symbolic = %require_complete (constants.%require_complete.67c)] // CHECK:STDOUT: %Class: type = class_type @Class, @Class(%T.loc5_13.1) [symbolic = %Class (constants.%Class)] // CHECK:STDOUT: %Class.elem: type = unbound_element_type %Class, %T.binding.as_type [symbolic = %Class.elem (constants.%Class.elem)] // CHECK:STDOUT: %struct_type.n: type = struct_type {.n: @Class.%T.binding.as_type (%T.binding.as_type)} [symbolic = %struct_type.n (constants.%struct_type.n)] // CHECK:STDOUT: %complete_type.loc9_1.2: = complete_type_witness %struct_type.n [symbolic = %complete_type.loc9_1.2 (constants.%complete_type)] // CHECK:STDOUT: // CHECK:STDOUT: class { // CHECK:STDOUT: %Class.F.decl: @Class.%Class.F.type (%Class.F.type) = fn_decl @Class.F [symbolic = @Class.%Class.F (constants.%Class.F)] { // CHECK:STDOUT: %n.patt: @Class.F.%pattern_type (%pattern_type.9b9f0c.2) = value_binding_pattern n [concrete] // CHECK:STDOUT: %n.param_patt: @Class.F.%pattern_type (%pattern_type.9b9f0c.2) = value_param_pattern %n.patt [concrete] // CHECK:STDOUT: %return.patt: @Class.F.%pattern_type (%pattern_type.9b9f0c.2) = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: @Class.F.%pattern_type (%pattern_type.9b9f0c.2) = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %T.ref.loc6_17: %Copy.type = name_ref T, @Class.%T.loc5_13.2 [symbolic = %T.loc6 (constants.%T.035)] // CHECK:STDOUT: %T.as_type.loc6_17: type = facet_access_type %T.ref.loc6_17 [symbolic = %T.binding.as_type (constants.%T.binding.as_type)] // CHECK:STDOUT: %.loc6_17.2: type = converted %T.ref.loc6_17, %T.as_type.loc6_17 [symbolic = %T.binding.as_type (constants.%T.binding.as_type)] // CHECK:STDOUT: %.loc6_17.3: Core.Form = init_form %.loc6_17.2 [symbolic = %.loc6_17.1 (constants.%.076a48.2)] // CHECK:STDOUT: %n.param.loc6: @Class.F.%T.binding.as_type (%T.binding.as_type) = value_param call_param0 // CHECK:STDOUT: %.loc6_11.1: type = splice_block %.loc6_11.2 [symbolic = %T.binding.as_type (constants.%T.binding.as_type)] { // CHECK:STDOUT: %T.ref.loc6_11: %Copy.type = name_ref T, @Class.%T.loc5_13.2 [symbolic = %T.loc6 (constants.%T.035)] // CHECK:STDOUT: %T.as_type.loc6_11: type = facet_access_type %T.ref.loc6_11 [symbolic = %T.binding.as_type (constants.%T.binding.as_type)] // CHECK:STDOUT: %.loc6_11.2: type = converted %T.ref.loc6_11, %T.as_type.loc6_11 [symbolic = %T.binding.as_type (constants.%T.binding.as_type)] // CHECK:STDOUT: } // CHECK:STDOUT: %n.loc6: @Class.F.%T.binding.as_type (%T.binding.as_type) = value_binding n, %n.param.loc6 // CHECK:STDOUT: %return.param.loc6: ref @Class.F.%T.binding.as_type (%T.binding.as_type) = out_param call_param1 // CHECK:STDOUT: %return.loc6: ref @Class.F.%T.binding.as_type (%T.binding.as_type) = return_slot %return.param.loc6 // CHECK:STDOUT: } // CHECK:STDOUT: %Class.G.decl: @Class.%Class.G.type (%Class.G.type) = fn_decl @Class.G [symbolic = @Class.%Class.G (constants.%Class.G)] { // CHECK:STDOUT: %self.patt: @Class.G.%pattern_type.loc7_8 (%pattern_type.893) = value_binding_pattern self [concrete] // CHECK:STDOUT: %self.param_patt: @Class.G.%pattern_type.loc7_8 (%pattern_type.893) = value_param_pattern %self.patt [concrete] // CHECK:STDOUT: %return.patt: @Class.G.%pattern_type.loc7_22 (%pattern_type.9b9f0c.2) = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: @Class.G.%pattern_type.loc7_22 (%pattern_type.9b9f0c.2) = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %T.ref.loc7: %Copy.type = name_ref T, @Class.%T.loc5_13.2 [symbolic = %T.loc7 (constants.%T.035)] // CHECK:STDOUT: %T.as_type.loc7: type = facet_access_type %T.ref.loc7 [symbolic = %T.binding.as_type (constants.%T.binding.as_type)] // CHECK:STDOUT: %.loc7_25.2: type = converted %T.ref.loc7, %T.as_type.loc7 [symbolic = %T.binding.as_type (constants.%T.binding.as_type)] // CHECK:STDOUT: %.loc7_25.3: Core.Form = init_form %.loc7_25.2 [symbolic = %.loc7_25.1 (constants.%.076a48.2)] // CHECK:STDOUT: %self.param.loc7: @Class.G.%Class (%Class) = value_param call_param0 // CHECK:STDOUT: %.loc7_14.1: type = splice_block %Self.ref.loc7 [symbolic = %Class (constants.%Class)] { // CHECK:STDOUT: %.loc7_14.2: type = specific_constant constants.%Class, @Class(constants.%T.035) [symbolic = %Class (constants.%Class)] // CHECK:STDOUT: %Self.ref.loc7: type = name_ref Self, %.loc7_14.2 [symbolic = %Class (constants.%Class)] // CHECK:STDOUT: } // CHECK:STDOUT: %self.loc7: @Class.G.%Class (%Class) = value_binding self, %self.param.loc7 // CHECK:STDOUT: %return.param.loc7: ref @Class.G.%T.binding.as_type (%T.binding.as_type) = out_param call_param1 // CHECK:STDOUT: %return.loc7: ref @Class.G.%T.binding.as_type (%T.binding.as_type) = return_slot %return.param.loc7 // CHECK:STDOUT: } // CHECK:STDOUT: %T.ref: %Copy.type = name_ref T, %T.loc5_13.2 [symbolic = %T.loc5_13.1 (constants.%T.035)] // CHECK:STDOUT: %T.as_type: type = facet_access_type %T.ref [symbolic = %T.binding.as_type (constants.%T.binding.as_type)] // CHECK:STDOUT: %.loc8_10: type = converted %T.ref, %T.as_type [symbolic = %T.binding.as_type (constants.%T.binding.as_type)] // CHECK:STDOUT: %.loc8_8: @Class.%Class.elem (%Class.elem) = field_decl n, element0 [concrete] // CHECK:STDOUT: %complete_type.loc9_1.1: = complete_type_witness constants.%struct_type.n [symbolic = %complete_type.loc9_1.2 (constants.%complete_type)] // CHECK:STDOUT: complete_type_witness = %complete_type.loc9_1.1 // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%Class // CHECK:STDOUT: .T = // CHECK:STDOUT: .F = %Class.F.decl // CHECK:STDOUT: .G = %Class.G.decl // CHECK:STDOUT: .n = %.loc8_8 // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic fn @Class.F(@Class.%T.loc5_13.2: %Copy.type) { // CHECK:STDOUT: %T.loc6: %Copy.type = symbolic_binding T, 0 [symbolic = %T.loc6 (constants.%T.035)] // CHECK:STDOUT: %T.binding.as_type: type = symbolic_binding_type T, 0, %T.loc6 [symbolic = %T.binding.as_type (constants.%T.binding.as_type)] // CHECK:STDOUT: %pattern_type: type = pattern_type %T.binding.as_type [symbolic = %pattern_type (constants.%pattern_type.9b9f0c.2)] // CHECK:STDOUT: %.loc6_17.1: Core.Form = init_form %T.binding.as_type [symbolic = %.loc6_17.1 (constants.%.076a48.2)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete: = require_complete_type %T.binding.as_type [symbolic = %require_complete (constants.%require_complete.67c)] // CHECK:STDOUT: %Copy.lookup_impl_witness: = lookup_impl_witness %T.loc6, @Copy [symbolic = %Copy.lookup_impl_witness (constants.%Copy.lookup_impl_witness.58d)] // CHECK:STDOUT: %Copy.WithSelf.Op.type: type = fn_type @Copy.WithSelf.Op, @Copy.WithSelf(%T.loc6) [symbolic = %Copy.WithSelf.Op.type (constants.%Copy.WithSelf.Op.type.735e75.2)] // CHECK:STDOUT: %.loc12: type = fn_type_with_self_type %Copy.WithSelf.Op.type, %T.loc6 [symbolic = %.loc12 (constants.%.023)] // CHECK:STDOUT: %impl.elem0.loc12_10.2: @Class.F.%.loc12 (%.023) = impl_witness_access %Copy.lookup_impl_witness, element0 [symbolic = %impl.elem0.loc12_10.2 (constants.%impl.elem0.594)] // CHECK:STDOUT: %specific_impl_fn.loc12_10.2: = specific_impl_function %impl.elem0.loc12_10.2, @Copy.WithSelf.Op(%T.loc6) [symbolic = %specific_impl_fn.loc12_10.2 (constants.%specific_impl_fn.bdc)] // CHECK:STDOUT: // CHECK:STDOUT: fn(%n.param.loc11: @Class.F.%T.binding.as_type (%T.binding.as_type)) -> out %return.param.loc11: @Class.F.%T.binding.as_type (%T.binding.as_type) { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %n.ref: @Class.F.%T.binding.as_type (%T.binding.as_type) = name_ref n, %n.loc11 // CHECK:STDOUT: %impl.elem0.loc12_10.1: @Class.F.%.loc12 (%.023) = impl_witness_access constants.%Copy.lookup_impl_witness.58d, element0 [symbolic = %impl.elem0.loc12_10.2 (constants.%impl.elem0.594)] // CHECK:STDOUT: %bound_method.loc12_10.1: = bound_method %n.ref, %impl.elem0.loc12_10.1 // CHECK:STDOUT: %specific_impl_fn.loc12_10.1: = specific_impl_function %impl.elem0.loc12_10.1, @Copy.WithSelf.Op(constants.%T.035) [symbolic = %specific_impl_fn.loc12_10.2 (constants.%specific_impl_fn.bdc)] // CHECK:STDOUT: %bound_method.loc12_10.2: = bound_method %n.ref, %specific_impl_fn.loc12_10.1 // CHECK:STDOUT: %.loc11_36.1: ref @Class.F.%T.binding.as_type (%T.binding.as_type) = splice_block %return.param.loc11 {} // CHECK:STDOUT: %Copy.WithSelf.Op.call: init @Class.F.%T.binding.as_type (%T.binding.as_type) to %.loc11_36.1 = call %bound_method.loc12_10.2(%n.ref) // CHECK:STDOUT: return %Copy.WithSelf.Op.call to %return.param.loc11 // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic fn @Class.G(@Class.%T.loc5_13.2: %Copy.type) { // CHECK:STDOUT: %T.loc7: %Copy.type = symbolic_binding T, 0 [symbolic = %T.loc7 (constants.%T.035)] // CHECK:STDOUT: %Class: type = class_type @Class, @Class(%T.loc7) [symbolic = %Class (constants.%Class)] // CHECK:STDOUT: %pattern_type.loc7_8: type = pattern_type %Class [symbolic = %pattern_type.loc7_8 (constants.%pattern_type.893)] // CHECK:STDOUT: %T.binding.as_type: type = symbolic_binding_type T, 0, %T.loc7 [symbolic = %T.binding.as_type (constants.%T.binding.as_type)] // CHECK:STDOUT: %.loc7_25.1: Core.Form = init_form %T.binding.as_type [symbolic = %.loc7_25.1 (constants.%.076a48.2)] // CHECK:STDOUT: %pattern_type.loc7_22: type = pattern_type %T.binding.as_type [symbolic = %pattern_type.loc7_22 (constants.%pattern_type.9b9f0c.2)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete.loc15: = require_complete_type %Class [symbolic = %require_complete.loc15 (constants.%require_complete.904)] // CHECK:STDOUT: %Class.elem: type = unbound_element_type %Class, %T.binding.as_type [symbolic = %Class.elem (constants.%Class.elem)] // CHECK:STDOUT: %require_complete.loc16: = require_complete_type %T.binding.as_type [symbolic = %require_complete.loc16 (constants.%require_complete.67c)] // CHECK:STDOUT: %Copy.lookup_impl_witness: = lookup_impl_witness %T.loc7, @Copy [symbolic = %Copy.lookup_impl_witness (constants.%Copy.lookup_impl_witness.58d)] // CHECK:STDOUT: %Copy.WithSelf.Op.type: type = fn_type @Copy.WithSelf.Op, @Copy.WithSelf(%T.loc7) [symbolic = %Copy.WithSelf.Op.type (constants.%Copy.WithSelf.Op.type.735e75.2)] // CHECK:STDOUT: %.loc16_14.3: type = fn_type_with_self_type %Copy.WithSelf.Op.type, %T.loc7 [symbolic = %.loc16_14.3 (constants.%.023)] // CHECK:STDOUT: %impl.elem0.loc16_14.2: @Class.G.%.loc16_14.3 (%.023) = impl_witness_access %Copy.lookup_impl_witness, element0 [symbolic = %impl.elem0.loc16_14.2 (constants.%impl.elem0.594)] // CHECK:STDOUT: %specific_impl_fn.loc16_14.2: = specific_impl_function %impl.elem0.loc16_14.2, @Copy.WithSelf.Op(%T.loc7) [symbolic = %specific_impl_fn.loc16_14.2 (constants.%specific_impl_fn.bdc)] // CHECK:STDOUT: // CHECK:STDOUT: fn(%self.param.loc15: @Class.G.%Class (%Class)) -> out %return.param.loc15: @Class.G.%T.binding.as_type (%T.binding.as_type) { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %self.ref: @Class.G.%Class (%Class) = name_ref self, %self.loc15 // CHECK:STDOUT: %n.ref: @Class.G.%Class.elem (%Class.elem) = name_ref n, @Class.%.loc8_8 [concrete = @Class.%.loc8_8] // CHECK:STDOUT: %.loc16_14.1: ref @Class.G.%T.binding.as_type (%T.binding.as_type) = class_element_access %self.ref, element0 // CHECK:STDOUT: %.loc16_14.2: @Class.G.%T.binding.as_type (%T.binding.as_type) = acquire_value %.loc16_14.1 // CHECK:STDOUT: %impl.elem0.loc16_14.1: @Class.G.%.loc16_14.3 (%.023) = impl_witness_access constants.%Copy.lookup_impl_witness.58d, element0 [symbolic = %impl.elem0.loc16_14.2 (constants.%impl.elem0.594)] // CHECK:STDOUT: %bound_method.loc16_14.1: = bound_method %.loc16_14.2, %impl.elem0.loc16_14.1 // CHECK:STDOUT: %specific_impl_fn.loc16_14.1: = specific_impl_function %impl.elem0.loc16_14.1, @Copy.WithSelf.Op(constants.%T.035) [symbolic = %specific_impl_fn.loc16_14.2 (constants.%specific_impl_fn.bdc)] // CHECK:STDOUT: %bound_method.loc16_14.2: = bound_method %.loc16_14.2, %specific_impl_fn.loc16_14.1 // CHECK:STDOUT: %.loc15_44.1: ref @Class.G.%T.binding.as_type (%T.binding.as_type) = splice_block %return.param.loc15 {} // CHECK:STDOUT: %Copy.WithSelf.Op.call: init @Class.G.%T.binding.as_type (%T.binding.as_type) to %.loc15_44.1 = call %bound_method.loc16_14.2(%.loc16_14.2) // CHECK:STDOUT: return %Copy.WithSelf.Op.call to %return.param.loc15 // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @Class(constants.%T.035) { // CHECK:STDOUT: %T.loc5_13.1 => constants.%T.035 // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %Class.F.type => constants.%Class.F.type // CHECK:STDOUT: %Class.F => constants.%Class.F // CHECK:STDOUT: %Class.G.type => constants.%Class.G.type // CHECK:STDOUT: %Class.G => constants.%Class.G // CHECK:STDOUT: %T.binding.as_type => constants.%T.binding.as_type // CHECK:STDOUT: %require_complete => constants.%require_complete.67c // CHECK:STDOUT: %Class => constants.%Class // CHECK:STDOUT: %Class.elem => constants.%Class.elem // CHECK:STDOUT: %struct_type.n => constants.%struct_type.n // CHECK:STDOUT: %complete_type.loc9_1.2 => constants.%complete_type // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @Class.F(constants.%T.035) { // CHECK:STDOUT: %T.loc6 => constants.%T.035 // CHECK:STDOUT: %T.binding.as_type => constants.%T.binding.as_type // CHECK:STDOUT: %pattern_type => constants.%pattern_type.9b9f0c.2 // CHECK:STDOUT: %.loc6_17.1 => constants.%.076a48.2 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @Class.G(constants.%T.035) { // CHECK:STDOUT: %T.loc7 => constants.%T.035 // CHECK:STDOUT: %Class => constants.%Class // CHECK:STDOUT: %pattern_type.loc7_8 => constants.%pattern_type.893 // CHECK:STDOUT: %T.binding.as_type => constants.%T.binding.as_type // CHECK:STDOUT: %.loc7_25.1 => constants.%.076a48.2 // CHECK:STDOUT: %pattern_type.loc7_22 => constants.%pattern_type.9b9f0c.2 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- nested.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %pattern_type.98f: type = pattern_type type [concrete] // CHECK:STDOUT: %T: type = symbolic_binding T, 0 [symbolic] // CHECK:STDOUT: %A.type: type = generic_class_type @A [concrete] // CHECK:STDOUT: %A.generic: %A.type = struct_value () [concrete] // CHECK:STDOUT: %A: type = class_type @A, @A(%T) [symbolic] // CHECK:STDOUT: %pattern_type.51d: type = pattern_type %T [symbolic] // CHECK:STDOUT: %_: %T = symbolic_binding _, 1 [symbolic] // CHECK:STDOUT: %B.type: type = generic_class_type @B, @A(%T) [symbolic] // CHECK:STDOUT: %B.generic: %B.type = struct_value () [symbolic] // CHECK:STDOUT: %B: type = class_type @B, @B(%T, %_) [symbolic] // CHECK:STDOUT: %pattern_type.830: type = pattern_type %B [symbolic] // CHECK:STDOUT: %B.F.type: type = fn_type @B.F, @B(%T, %_) [symbolic] // CHECK:STDOUT: %B.F: %B.F.type = struct_value () [symbolic] // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %complete_type: = complete_type_witness %empty_struct_type [concrete] // CHECK:STDOUT: %require_complete.7cc: = require_complete_type %B [symbolic] // CHECK:STDOUT: %require_complete.944: = require_complete_type %T [symbolic] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: %A.decl: %A.type = class_decl @A [concrete = constants.%A.generic] { // CHECK:STDOUT: %T.patt: %pattern_type.98f = symbolic_binding_pattern T, 0 [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %.loc5_13.1: type = splice_block %.loc5_13.2 [concrete = type] { // CHECK:STDOUT: // CHECK:STDOUT: %.loc5_13.2: type = type_literal type [concrete = type] // CHECK:STDOUT: } // CHECK:STDOUT: %T.loc5_9.2: type = symbolic_binding T, 0 [symbolic = %T.loc5_9.1 (constants.%T)] // CHECK:STDOUT: } // CHECK:STDOUT: %B.F.decl: %B.F.type = fn_decl @B.F [symbolic = constants.%B.F] { // CHECK:STDOUT: %self.patt: @B.F.%pattern_type.loc7_10 (%pattern_type.830) = value_binding_pattern self [concrete] // CHECK:STDOUT: %self.param_patt: @B.F.%pattern_type.loc7_10 (%pattern_type.830) = value_param_pattern %self.patt [concrete] // CHECK:STDOUT: %a.patt: @B.F.%pattern_type.loc7_22 (%pattern_type.51d) = value_binding_pattern a [concrete] // CHECK:STDOUT: %a.param_patt: @B.F.%pattern_type.loc7_22 (%pattern_type.51d) = value_param_pattern %a.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %.loc11_10.1: type = splice_block %.loc11_10.2 [concrete = type] { // CHECK:STDOUT: // CHECK:STDOUT: %.loc11_10.2: type = type_literal type [concrete = type] // CHECK:STDOUT: } // CHECK:STDOUT: %T.loc11: type = symbolic_binding T, 0 [symbolic = @A.%T.loc5_9.1 (constants.%T)] // CHECK:STDOUT: %.loc11_22: type = splice_block %T.ref.loc11_22 [symbolic = @B.%T (constants.%T)] { // CHECK:STDOUT: // CHECK:STDOUT: %T.ref.loc11_22: type = name_ref T, %T.loc11 [symbolic = @B.%T (constants.%T)] // CHECK:STDOUT: } // CHECK:STDOUT: %_.loc11: @B.%T (%T) = symbolic_binding _, 1 [symbolic = @B.%_.loc6_11.1 (constants.%_)] // CHECK:STDOUT: %self.param.loc11: @B.F.%B (%B) = value_param call_param0 // CHECK:STDOUT: %.loc11_40.1: type = splice_block %Self.ref.loc11 [symbolic = %B (constants.%B)] { // CHECK:STDOUT: %.loc11_40.2: type = specific_constant constants.%B, @B(constants.%T, constants.%_) [symbolic = %B (constants.%B)] // CHECK:STDOUT: %Self.ref.loc11: type = name_ref Self, %.loc11_40.2 [symbolic = %B (constants.%B)] // CHECK:STDOUT: } // CHECK:STDOUT: %self.loc11: @B.F.%B (%B) = value_binding self, %self.param.loc11 // CHECK:STDOUT: %a.param.loc11: @B.F.%T.loc7 (%T) = value_param call_param1 // CHECK:STDOUT: %T.ref.loc11_56: type = name_ref T, %T.loc11 [symbolic = %T.loc7 (constants.%T)] // CHECK:STDOUT: %a.loc11: @B.F.%T.loc7 (%T) = value_binding a, %a.param.loc11 // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic class @A(%T.loc5_9.2: type) { // CHECK:STDOUT: %T.loc5_9.1: type = symbolic_binding T, 0 [symbolic = %T.loc5_9.1 (constants.%T)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %B.type: type = generic_class_type @B, @A(%T.loc5_9.1) [symbolic = %B.type (constants.%B.type)] // CHECK:STDOUT: %B.generic: @A.%B.type (%B.type) = struct_value () [symbolic = %B.generic (constants.%B.generic)] // CHECK:STDOUT: // CHECK:STDOUT: class { // CHECK:STDOUT: %B.decl: @A.%B.type (%B.type) = class_decl @B [symbolic = @A.%B.generic (constants.%B.generic)] { // CHECK:STDOUT: %_.patt: @B.%pattern_type (%pattern_type.51d) = symbolic_binding_pattern _, 1 [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %.loc6: type = splice_block %T.ref [symbolic = %T (constants.%T)] { // CHECK:STDOUT: // CHECK:STDOUT: %T.ref: type = name_ref T, @A.%T.loc5_9.2 [symbolic = %T (constants.%T)] // CHECK:STDOUT: } // CHECK:STDOUT: %_.loc6_11.2: @B.%T (%T) = symbolic_binding _, 1 [symbolic = %_.loc6_11.1 (constants.%_)] // CHECK:STDOUT: } // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%A // CHECK:STDOUT: .T = // CHECK:STDOUT: .B = %B.decl // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic class @B(@A.%T.loc5_9.2: type, %_.loc6_11.2: @B.%T (%T)) { // CHECK:STDOUT: %T: type = symbolic_binding T, 0 [symbolic = %T (constants.%T)] // CHECK:STDOUT: %_.loc6_11.1: @B.%T (%T) = symbolic_binding _, 1 [symbolic = %_.loc6_11.1 (constants.%_)] // CHECK:STDOUT: %pattern_type: type = pattern_type %T [symbolic = %pattern_type (constants.%pattern_type.51d)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %B.F.type: type = fn_type @B.F, @B(%T, %_.loc6_11.1) [symbolic = %B.F.type (constants.%B.F.type)] // CHECK:STDOUT: %B.F: @B.%B.F.type (%B.F.type) = struct_value () [symbolic = %B.F (constants.%B.F)] // CHECK:STDOUT: // CHECK:STDOUT: class { // CHECK:STDOUT: %B.F.decl: @B.%B.F.type (%B.F.type) = fn_decl @B.F [symbolic = @B.%B.F (constants.%B.F)] { // CHECK:STDOUT: %self.patt: @B.F.%pattern_type.loc7_10 (%pattern_type.830) = value_binding_pattern self [concrete] // CHECK:STDOUT: %self.param_patt: @B.F.%pattern_type.loc7_10 (%pattern_type.830) = value_param_pattern %self.patt [concrete] // CHECK:STDOUT: %a.patt: @B.F.%pattern_type.loc7_22 (%pattern_type.51d) = value_binding_pattern a [concrete] // CHECK:STDOUT: %a.param_patt: @B.F.%pattern_type.loc7_22 (%pattern_type.51d) = value_param_pattern %a.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %self.param.loc7: @B.F.%B (%B) = value_param call_param0 // CHECK:STDOUT: %.loc7_16.1: type = splice_block %Self.ref.loc7 [symbolic = %B (constants.%B)] { // CHECK:STDOUT: %.loc7_16.2: type = specific_constant constants.%B, @B(constants.%T, constants.%_) [symbolic = %B (constants.%B)] // CHECK:STDOUT: %Self.ref.loc7: type = name_ref Self, %.loc7_16.2 [symbolic = %B (constants.%B)] // CHECK:STDOUT: } // CHECK:STDOUT: %self.loc7: @B.F.%B (%B) = value_binding self, %self.param.loc7 // CHECK:STDOUT: %a.param.loc7: @B.F.%T.loc7 (%T) = value_param call_param1 // CHECK:STDOUT: %T.ref.loc7: type = name_ref T, @A.%T.loc5_9.2 [symbolic = %T.loc7 (constants.%T)] // CHECK:STDOUT: %a.loc7: @B.F.%T.loc7 (%T) = value_binding a, %a.param.loc7 // CHECK:STDOUT: } // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%B // CHECK:STDOUT: .T = // CHECK:STDOUT: .F = %B.F.decl // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic fn @B.F(@A.%T.loc5_9.2: type, @B.%_.loc6_11.2: @B.%T (%T)) { // CHECK:STDOUT: %T.loc7: type = symbolic_binding T, 0 [symbolic = %T.loc7 (constants.%T)] // CHECK:STDOUT: %_.loc7: @B.F.%T.loc7 (%T) = symbolic_binding _, 1 [symbolic = %_.loc7 (constants.%_)] // CHECK:STDOUT: %B: type = class_type @B, @B(%T.loc7, %_.loc7) [symbolic = %B (constants.%B)] // CHECK:STDOUT: %pattern_type.loc7_10: type = pattern_type %B [symbolic = %pattern_type.loc7_10 (constants.%pattern_type.830)] // CHECK:STDOUT: %pattern_type.loc7_22: type = pattern_type %T.loc7 [symbolic = %pattern_type.loc7_22 (constants.%pattern_type.51d)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete.loc11_38: = require_complete_type %B [symbolic = %require_complete.loc11_38 (constants.%require_complete.7cc)] // CHECK:STDOUT: %require_complete.loc11_54: = require_complete_type %T.loc7 [symbolic = %require_complete.loc11_54 (constants.%require_complete.944)] // CHECK:STDOUT: // CHECK:STDOUT: fn(%self.param.loc11: @B.F.%B (%B), %a.param.loc11: @B.F.%T.loc7 (%T)) { // CHECK:STDOUT: !entry: // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @A(constants.%T) { // CHECK:STDOUT: %T.loc5_9.1 => constants.%T // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %B.type => constants.%B.type // CHECK:STDOUT: %B.generic => constants.%B.generic // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @B(constants.%T, constants.%_) { // CHECK:STDOUT: %T => constants.%T // CHECK:STDOUT: %_.loc6_11.1 => constants.%_ // CHECK:STDOUT: %pattern_type => constants.%pattern_type.51d // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %B.F.type => constants.%B.F.type // CHECK:STDOUT: %B.F => constants.%B.F // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @B.F(constants.%T, constants.%_) { // CHECK:STDOUT: %T.loc7 => constants.%T // CHECK:STDOUT: %_.loc7 => constants.%_ // CHECK:STDOUT: %B => constants.%B // CHECK:STDOUT: %pattern_type.loc7_10 => constants.%pattern_type.830 // CHECK:STDOUT: %pattern_type.loc7_22 => constants.%pattern_type.51d // CHECK:STDOUT: } // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/class/generic/member_type.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/int.carbon // TODO: Add ranges and switch to "--dump-sem-ir-ranges=only". // EXTRA-ARGS: --dump-sem-ir-ranges=if-present // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/class/generic/member_type.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/class/generic/member_type.carbon // --- class.carbon library "[[@TEST_NAME]]"; class Outer(T:! Core.Copy) { class Inner { var n: T; } fn F(n: T) -> Inner { return {.n = n}; } } fn Test() -> i32 { var c: Outer(i32).Inner = Outer(i32).F(1); return c.n; } // --- interface.carbon library "[[@TEST_NAME]]"; class Outer(T:! type) { interface Inner { fn F[self: Self]() -> T; } class C { impl as Inner { fn F[self: C]() -> T { return self.(Inner.F)(); } } } } class D { impl as Outer(i32).Inner { fn F[self: D]() -> i32; } } fn Test() -> i32 { var c: Outer(i32).C = {}; return c.(Outer(i32).Inner.F)(); } // CHECK:STDOUT: --- class.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %type: type = facet_type [concrete] // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self] // CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] // CHECK:STDOUT: %Copy.type: type = facet_type <@Copy> [concrete] // CHECK:STDOUT: %pattern_type.ce2: type = pattern_type %Copy.type [concrete] // CHECK:STDOUT: %T.035: %Copy.type = symbolic_binding T, 0 [symbolic] // CHECK:STDOUT: %Outer.type: type = generic_class_type @Outer [concrete] // CHECK:STDOUT: %Outer.generic: %Outer.type = struct_value () [concrete] // CHECK:STDOUT: %Outer.4b9: type = class_type @Outer, @Outer(%T.035) [symbolic] // CHECK:STDOUT: %Inner.bcf: type = class_type @Inner, @Inner(%T.035) [symbolic] // CHECK:STDOUT: %T.binding.as_type: type = symbolic_binding_type T, 0, %T.035 [symbolic] // CHECK:STDOUT: %require_complete.67c: = require_complete_type %T.binding.as_type [symbolic] // CHECK:STDOUT: %Inner.elem.f8d: type = unbound_element_type %Inner.bcf, %T.binding.as_type [symbolic] // CHECK:STDOUT: %struct_type.n.47a: type = struct_type {.n: %T.binding.as_type} [symbolic] // CHECK:STDOUT: %complete_type.072: = complete_type_witness %struct_type.n.47a [symbolic] // CHECK:STDOUT: %pattern_type.9b9f0c.2: type = pattern_type %T.binding.as_type [symbolic] // CHECK:STDOUT: %.d79: Core.Form = init_form %Inner.bcf [symbolic] // CHECK:STDOUT: %pattern_type.611: type = pattern_type %Inner.bcf [symbolic] // CHECK:STDOUT: %Outer.F.type.2fb: type = fn_type @Outer.F, @Outer(%T.035) [symbolic] // CHECK:STDOUT: %Outer.F.5e3: %Outer.F.type.2fb = struct_value () [symbolic] // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %complete_type.357: = complete_type_witness %empty_struct_type [concrete] // CHECK:STDOUT: %require_complete.e6c: = require_complete_type %Inner.bcf [symbolic] // CHECK:STDOUT: %Copy.lookup_impl_witness.58d: = lookup_impl_witness %T.035, @Copy [symbolic] // CHECK:STDOUT: %Copy.WithSelf.Op.type.735e75.2: type = fn_type @Copy.WithSelf.Op, @Copy.WithSelf(%T.035) [symbolic] // CHECK:STDOUT: %.023: type = fn_type_with_self_type %Copy.WithSelf.Op.type.735e75.2, %T.035 [symbolic] // CHECK:STDOUT: %impl.elem0.594: %.023 = impl_witness_access %Copy.lookup_impl_witness.58d, element0 [symbolic] // CHECK:STDOUT: %specific_impl_fn.bdc: = specific_impl_function %impl.elem0.594, @Copy.WithSelf.Op(%T.035) [symbolic] // CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [concrete] // CHECK:STDOUT: %Int.type: type = generic_class_type @Int [concrete] // CHECK:STDOUT: %Int.generic: %Int.type = struct_value () [concrete] // CHECK:STDOUT: %N: Core.IntLiteral = symbolic_binding N, 0 [symbolic] // CHECK:STDOUT: %i32: type = class_type @Int, @Int(%int_32) [concrete] // CHECK:STDOUT: %.ff5: Core.Form = init_form %i32 [concrete] // CHECK:STDOUT: %pattern_type.7ce: type = pattern_type %i32 [concrete] // CHECK:STDOUT: %Test.type: type = fn_type @Test [concrete] // CHECK:STDOUT: %Test: %Test.type = struct_value () [concrete] // CHECK:STDOUT: %i32.builtin: type = int_type signed, %int_32 [concrete] // CHECK:STDOUT: %complete_type.f8a: = complete_type_witness %i32.builtin [concrete] // CHECK:STDOUT: %Int.as.Copy.impl.Op.type.824: type = fn_type @Int.as.Copy.impl.Op, @Int.as.Copy.impl(%N) [symbolic] // CHECK:STDOUT: %Int.as.Copy.impl.Op.9b9: %Int.as.Copy.impl.Op.type.824 = struct_value () [symbolic] // CHECK:STDOUT: %Copy.impl_witness.f17: = impl_witness imports.%Copy.impl_witness_table.e76, @Int.as.Copy.impl(%int_32) [concrete] // CHECK:STDOUT: %Int.as.Copy.impl.Op.type.546: type = fn_type @Int.as.Copy.impl.Op, @Int.as.Copy.impl(%int_32) [concrete] // CHECK:STDOUT: %Int.as.Copy.impl.Op.664: %Int.as.Copy.impl.Op.type.546 = struct_value () [concrete] // CHECK:STDOUT: %Copy.facet.de4: %Copy.type = facet_value %i32, (%Copy.impl_witness.f17) [concrete] // CHECK:STDOUT: %Outer.a6c: type = class_type @Outer, @Outer(%Copy.facet.de4) [concrete] // CHECK:STDOUT: %Inner.74c: type = class_type @Inner, @Inner(%Copy.facet.de4) [concrete] // CHECK:STDOUT: %Outer.F.type.20b: type = fn_type @Outer.F, @Outer(%Copy.facet.de4) [concrete] // CHECK:STDOUT: %Outer.F.119: %Outer.F.type.20b = struct_value () [concrete] // CHECK:STDOUT: %Inner.elem.34c: type = unbound_element_type %Inner.74c, %i32 [concrete] // CHECK:STDOUT: %struct_type.n.033: type = struct_type {.n: %i32} [concrete] // CHECK:STDOUT: %complete_type.54b: = complete_type_witness %struct_type.n.033 [concrete] // CHECK:STDOUT: %pattern_type.35b: type = pattern_type %Inner.74c [concrete] // CHECK:STDOUT: %int_1.5b8: Core.IntLiteral = int_value 1 [concrete] // CHECK:STDOUT: %Copy.impl_witness.006: = impl_witness imports.%Copy.impl_witness_table.b6d [concrete] // CHECK:STDOUT: %Copy.facet.cdd: %Copy.type = facet_value Core.IntLiteral, (%Copy.impl_witness.006) [concrete] // CHECK:STDOUT: %.42e: Core.Form = init_form %Inner.74c [concrete] // CHECK:STDOUT: %Outer.F.specific_fn: = specific_function %Outer.F.119, @Outer.F(%Copy.facet.de4) [concrete] // CHECK:STDOUT: %ImplicitAs.type.cc7: type = generic_interface_type @ImplicitAs [concrete] // CHECK:STDOUT: %ImplicitAs.generic: %ImplicitAs.type.cc7 = struct_value () [concrete] // CHECK:STDOUT: %ImplicitAs.type.e8c: type = facet_type <@ImplicitAs, @ImplicitAs(%i32)> [concrete] // CHECK:STDOUT: %To: Core.IntLiteral = symbolic_binding To, 0 [symbolic] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.4e6: type = fn_type @Core.IntLiteral.as.ImplicitAs.impl.Convert, @Core.IntLiteral.as.ImplicitAs.impl(%To) [symbolic] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.3c2: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.4e6 = struct_value () [symbolic] // CHECK:STDOUT: %ImplicitAs.impl_witness.6bc: = impl_witness imports.%ImplicitAs.impl_witness_table.74f, @Core.IntLiteral.as.ImplicitAs.impl(%int_32) [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.e0d: type = fn_type @Core.IntLiteral.as.ImplicitAs.impl.Convert, @Core.IntLiteral.as.ImplicitAs.impl(%int_32) [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.e0d = struct_value () [concrete] // CHECK:STDOUT: %ImplicitAs.facet: %ImplicitAs.type.e8c = facet_value Core.IntLiteral, (%ImplicitAs.impl_witness.6bc) [concrete] // CHECK:STDOUT: %ImplicitAs.WithSelf.Convert.type.b37: type = fn_type @ImplicitAs.WithSelf.Convert, @ImplicitAs.WithSelf(%i32, %ImplicitAs.facet) [concrete] // CHECK:STDOUT: %.545: type = fn_type_with_self_type %ImplicitAs.WithSelf.Convert.type.b37, %ImplicitAs.facet [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.bound: = bound_method %int_1.5b8, %Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5 [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn: = specific_function %Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5, @Core.IntLiteral.as.ImplicitAs.impl.Convert(%int_32) [concrete] // CHECK:STDOUT: %bound_method: = bound_method %int_1.5b8, %Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn [concrete] // CHECK:STDOUT: %int_1.5d2: %i32 = int_value 1 [concrete] // CHECK:STDOUT: %Copy.WithSelf.Op.type.081: type = fn_type @Copy.WithSelf.Op, @Copy.WithSelf(%Copy.facet.de4) [concrete] // CHECK:STDOUT: %.8e2: type = fn_type_with_self_type %Copy.WithSelf.Op.type.081, %Copy.facet.de4 [concrete] // CHECK:STDOUT: %Int.as.Copy.impl.Op.specific_fn: = specific_function %Int.as.Copy.impl.Op.664, @Int.as.Copy.impl.Op(%int_32) [concrete] // CHECK:STDOUT: %Destroy.type: type = facet_type <@Destroy> [concrete] // CHECK:STDOUT: %Destroy.Op.type: type = fn_type @Destroy.Op [concrete] // CHECK:STDOUT: %Destroy.Op: %Destroy.Op.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: .Copy = %Core.Copy // CHECK:STDOUT: .Int = %Core.Int // CHECK:STDOUT: .ImplicitAs = %Core.ImplicitAs // CHECK:STDOUT: .Destroy = %Core.Destroy // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: %Core.Copy: type = import_ref Core//prelude/parts/copy, Copy, loaded [concrete = constants.%Copy.type] // CHECK:STDOUT: %Core.Int: %Int.type = import_ref Core//prelude/parts/int, Int, loaded [concrete = constants.%Int.generic] // CHECK:STDOUT: %Core.import_ref.18d: @Int.as.Copy.impl.%Int.as.Copy.impl.Op.type (%Int.as.Copy.impl.Op.type.824) = import_ref Core//prelude/parts/int, loc{{\d+_\d+}}, loaded [symbolic = @Int.as.Copy.impl.%Int.as.Copy.impl.Op (constants.%Int.as.Copy.impl.Op.9b9)] // CHECK:STDOUT: %Copy.impl_witness_table.e76 = impl_witness_table (%Core.import_ref.18d), @Int.as.Copy.impl [concrete] // CHECK:STDOUT: %Core.import_ref.bb6 = import_ref Core//prelude/parts/copy, loc{{\d+_\d+}}, unloaded // CHECK:STDOUT: %Copy.impl_witness_table.b6d = impl_witness_table (%Core.import_ref.bb6), @Core.IntLiteral.as.Copy.impl [concrete] // CHECK:STDOUT: %Core.ImplicitAs: %ImplicitAs.type.cc7 = import_ref Core//prelude/parts/as, ImplicitAs, loaded [concrete = constants.%ImplicitAs.generic] // CHECK:STDOUT: %Core.import_ref.42d: @Core.IntLiteral.as.ImplicitAs.impl.%Core.IntLiteral.as.ImplicitAs.impl.Convert.type (%Core.IntLiteral.as.ImplicitAs.impl.Convert.type.4e6) = import_ref Core//prelude/parts/int, loc{{\d+_\d+}}, loaded [symbolic = @Core.IntLiteral.as.ImplicitAs.impl.%Core.IntLiteral.as.ImplicitAs.impl.Convert (constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.3c2)] // CHECK:STDOUT: %ImplicitAs.impl_witness_table.74f = impl_witness_table (%Core.import_ref.42d), @Core.IntLiteral.as.ImplicitAs.impl [concrete] // CHECK:STDOUT: %Core.Destroy: type = import_ref Core//prelude/parts/destroy, Destroy, loaded [concrete = constants.%Destroy.type] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .Outer = %Outer.decl // CHECK:STDOUT: .Test = %Test.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %Outer.decl: %Outer.type = class_decl @Outer [concrete = constants.%Outer.generic] { // CHECK:STDOUT: %T.patt: %pattern_type.ce2 = symbolic_binding_pattern T, 0 [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %.loc4: type = splice_block %Copy.ref [concrete = constants.%Copy.type] { // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %Core.ref: = name_ref Core, imports.%Core [concrete = imports.%Core] // CHECK:STDOUT: %Copy.ref: type = name_ref Copy, imports.%Core.Copy [concrete = constants.%Copy.type] // CHECK:STDOUT: } // CHECK:STDOUT: %T.loc4_13.2: %Copy.type = symbolic_binding T, 0 [symbolic = %T.loc4_13.1 (constants.%T.035)] // CHECK:STDOUT: } // CHECK:STDOUT: %Test.decl: %Test.type = fn_decl @Test [concrete = constants.%Test] { // CHECK:STDOUT: %return.patt: %pattern_type.7ce = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: %pattern_type.7ce = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %i32.loc12: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %.loc12: Core.Form = init_form %i32.loc12 [concrete = constants.%.ff5] // CHECK:STDOUT: %return.param: ref %i32 = out_param call_param0 // CHECK:STDOUT: %return: ref %i32 = return_slot %return.param // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic class @Outer(%T.loc4_13.2: %Copy.type) { // CHECK:STDOUT: %T.loc4_13.1: %Copy.type = symbolic_binding T, 0 [symbolic = %T.loc4_13.1 (constants.%T.035)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %Inner: type = class_type @Inner, @Inner(%T.loc4_13.1) [symbolic = %Inner (constants.%Inner.bcf)] // CHECK:STDOUT: %Outer.F.type: type = fn_type @Outer.F, @Outer(%T.loc4_13.1) [symbolic = %Outer.F.type (constants.%Outer.F.type.2fb)] // CHECK:STDOUT: %Outer.F: @Outer.%Outer.F.type (%Outer.F.type.2fb) = struct_value () [symbolic = %Outer.F (constants.%Outer.F.5e3)] // CHECK:STDOUT: // CHECK:STDOUT: class { // CHECK:STDOUT: %Inner.decl: type = class_decl @Inner [symbolic = @Outer.%Inner (constants.%Inner.bcf)] {} {} // CHECK:STDOUT: %Outer.F.decl: @Outer.%Outer.F.type (%Outer.F.type.2fb) = fn_decl @Outer.F [symbolic = @Outer.%Outer.F (constants.%Outer.F.5e3)] { // CHECK:STDOUT: %n.patt: @Outer.F.%pattern_type.loc9_8 (%pattern_type.9b9f0c.2) = value_binding_pattern n [concrete] // CHECK:STDOUT: %n.param_patt: @Outer.F.%pattern_type.loc9_8 (%pattern_type.9b9f0c.2) = value_param_pattern %n.patt [concrete] // CHECK:STDOUT: %return.patt: @Outer.F.%pattern_type.loc9_14 (%pattern_type.611) = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: @Outer.F.%pattern_type.loc9_14 (%pattern_type.611) = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %.loc9_17.2: type = specific_constant @Outer.%Inner.decl, @Outer(constants.%T.035) [symbolic = %Inner (constants.%Inner.bcf)] // CHECK:STDOUT: %Inner.ref: type = name_ref Inner, %.loc9_17.2 [symbolic = %Inner (constants.%Inner.bcf)] // CHECK:STDOUT: %.loc9_17.3: Core.Form = init_form %Inner.ref [symbolic = %.loc9_17.1 (constants.%.d79)] // CHECK:STDOUT: %n.param: @Outer.F.%T.binding.as_type (%T.binding.as_type) = value_param call_param0 // CHECK:STDOUT: %.loc9_11.1: type = splice_block %.loc9_11.2 [symbolic = %T.binding.as_type (constants.%T.binding.as_type)] { // CHECK:STDOUT: %T.ref: %Copy.type = name_ref T, @Outer.%T.loc4_13.2 [symbolic = %T (constants.%T.035)] // CHECK:STDOUT: %T.as_type: type = facet_access_type %T.ref [symbolic = %T.binding.as_type (constants.%T.binding.as_type)] // CHECK:STDOUT: %.loc9_11.2: type = converted %T.ref, %T.as_type [symbolic = %T.binding.as_type (constants.%T.binding.as_type)] // CHECK:STDOUT: } // CHECK:STDOUT: %n: @Outer.F.%T.binding.as_type (%T.binding.as_type) = value_binding n, %n.param // CHECK:STDOUT: %return.param: ref @Outer.F.%Inner (%Inner.bcf) = out_param call_param1 // CHECK:STDOUT: %return: ref @Outer.F.%Inner (%Inner.bcf) = return_slot %return.param // CHECK:STDOUT: } // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type.357] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%Outer.4b9 // CHECK:STDOUT: .Inner = %Inner.decl // CHECK:STDOUT: .T = // CHECK:STDOUT: .F = %Outer.F.decl // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic class @Inner(@Outer.%T.loc4_13.2: %Copy.type) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: %T: %Copy.type = symbolic_binding T, 0 [symbolic = %T (constants.%T.035)] // CHECK:STDOUT: %T.binding.as_type: type = symbolic_binding_type T, 0, %T [symbolic = %T.binding.as_type (constants.%T.binding.as_type)] // CHECK:STDOUT: %require_complete: = require_complete_type %T.binding.as_type [symbolic = %require_complete (constants.%require_complete.67c)] // CHECK:STDOUT: %Inner: type = class_type @Inner, @Inner(%T) [symbolic = %Inner (constants.%Inner.bcf)] // CHECK:STDOUT: %Inner.elem: type = unbound_element_type %Inner, %T.binding.as_type [symbolic = %Inner.elem (constants.%Inner.elem.f8d)] // CHECK:STDOUT: %struct_type.n: type = struct_type {.n: @Inner.%T.binding.as_type (%T.binding.as_type)} [symbolic = %struct_type.n (constants.%struct_type.n.47a)] // CHECK:STDOUT: %complete_type.loc7_3.2: = complete_type_witness %struct_type.n [symbolic = %complete_type.loc7_3.2 (constants.%complete_type.072)] // CHECK:STDOUT: // CHECK:STDOUT: class { // CHECK:STDOUT: %T.ref: %Copy.type = name_ref T, @Outer.%T.loc4_13.2 [symbolic = %T (constants.%T.035)] // CHECK:STDOUT: %T.as_type: type = facet_access_type %T.ref [symbolic = %T.binding.as_type (constants.%T.binding.as_type)] // CHECK:STDOUT: %.loc6_12: type = converted %T.ref, %T.as_type [symbolic = %T.binding.as_type (constants.%T.binding.as_type)] // CHECK:STDOUT: %.loc6_10: @Inner.%Inner.elem (%Inner.elem.f8d) = field_decl n, element0 [concrete] // CHECK:STDOUT: %complete_type.loc7_3.1: = complete_type_witness constants.%struct_type.n.47a [symbolic = %complete_type.loc7_3.2 (constants.%complete_type.072)] // CHECK:STDOUT: complete_type_witness = %complete_type.loc7_3.1 // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%Inner.bcf // CHECK:STDOUT: .T = // CHECK:STDOUT: .n = %.loc6_10 // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic fn @Outer.F(@Outer.%T.loc4_13.2: %Copy.type) { // CHECK:STDOUT: %T: %Copy.type = symbolic_binding T, 0 [symbolic = %T (constants.%T.035)] // CHECK:STDOUT: %T.binding.as_type: type = symbolic_binding_type T, 0, %T [symbolic = %T.binding.as_type (constants.%T.binding.as_type)] // CHECK:STDOUT: %pattern_type.loc9_8: type = pattern_type %T.binding.as_type [symbolic = %pattern_type.loc9_8 (constants.%pattern_type.9b9f0c.2)] // CHECK:STDOUT: %Inner: type = class_type @Inner, @Inner(%T) [symbolic = %Inner (constants.%Inner.bcf)] // CHECK:STDOUT: %.loc9_17.1: Core.Form = init_form %Inner [symbolic = %.loc9_17.1 (constants.%.d79)] // CHECK:STDOUT: %pattern_type.loc9_14: type = pattern_type %Inner [symbolic = %pattern_type.loc9_14 (constants.%pattern_type.611)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete.loc9_9: = require_complete_type %T.binding.as_type [symbolic = %require_complete.loc9_9 (constants.%require_complete.67c)] // CHECK:STDOUT: %require_complete.loc9_17: = require_complete_type %Inner [symbolic = %require_complete.loc9_17 (constants.%require_complete.e6c)] // CHECK:STDOUT: %struct_type.n: type = struct_type {.n: @Outer.F.%T.binding.as_type (%T.binding.as_type)} [symbolic = %struct_type.n (constants.%struct_type.n.47a)] // CHECK:STDOUT: %Copy.lookup_impl_witness: = lookup_impl_witness %T, @Copy [symbolic = %Copy.lookup_impl_witness (constants.%Copy.lookup_impl_witness.58d)] // CHECK:STDOUT: %Copy.WithSelf.Op.type: type = fn_type @Copy.WithSelf.Op, @Copy.WithSelf(%T) [symbolic = %Copy.WithSelf.Op.type (constants.%Copy.WithSelf.Op.type.735e75.2)] // CHECK:STDOUT: %.loc9_38: type = fn_type_with_self_type %Copy.WithSelf.Op.type, %T [symbolic = %.loc9_38 (constants.%.023)] // CHECK:STDOUT: %impl.elem0.loc9_38.2: @Outer.F.%.loc9_38 (%.023) = impl_witness_access %Copy.lookup_impl_witness, element0 [symbolic = %impl.elem0.loc9_38.2 (constants.%impl.elem0.594)] // CHECK:STDOUT: %specific_impl_fn.loc9_38.2: = specific_impl_function %impl.elem0.loc9_38.2, @Copy.WithSelf.Op(%T) [symbolic = %specific_impl_fn.loc9_38.2 (constants.%specific_impl_fn.bdc)] // CHECK:STDOUT: // CHECK:STDOUT: fn(%n.param: @Outer.F.%T.binding.as_type (%T.binding.as_type)) -> out %return.param: @Outer.F.%Inner (%Inner.bcf) { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %n.ref: @Outer.F.%T.binding.as_type (%T.binding.as_type) = name_ref n, %n // CHECK:STDOUT: %.loc9_39.1: @Outer.F.%struct_type.n (%struct_type.n.47a) = struct_literal (%n.ref) // CHECK:STDOUT: %impl.elem0.loc9_38.1: @Outer.F.%.loc9_38 (%.023) = impl_witness_access constants.%Copy.lookup_impl_witness.58d, element0 [symbolic = %impl.elem0.loc9_38.2 (constants.%impl.elem0.594)] // CHECK:STDOUT: %bound_method.loc9_38.1: = bound_method %n.ref, %impl.elem0.loc9_38.1 // CHECK:STDOUT: %specific_impl_fn.loc9_38.1: = specific_impl_function %impl.elem0.loc9_38.1, @Copy.WithSelf.Op(constants.%T.035) [symbolic = %specific_impl_fn.loc9_38.2 (constants.%specific_impl_fn.bdc)] // CHECK:STDOUT: %bound_method.loc9_38.2: = bound_method %n.ref, %specific_impl_fn.loc9_38.1 // CHECK:STDOUT: %.loc9_39.2: ref @Outer.F.%T.binding.as_type (%T.binding.as_type) = class_element_access %return.param, element0 // CHECK:STDOUT: %Copy.WithSelf.Op.call: init @Outer.F.%T.binding.as_type (%T.binding.as_type) to %.loc9_39.2 = call %bound_method.loc9_38.2(%n.ref) // CHECK:STDOUT: %.loc9_39.3: init @Outer.F.%T.binding.as_type (%T.binding.as_type) to %.loc9_39.2 = in_place_init %Copy.WithSelf.Op.call // CHECK:STDOUT: %.loc9_39.4: init @Outer.F.%Inner (%Inner.bcf) to %return.param = class_init (%.loc9_39.3) // CHECK:STDOUT: %.loc9_40: init @Outer.F.%Inner (%Inner.bcf) = converted %.loc9_39.1, %.loc9_39.4 // CHECK:STDOUT: return %.loc9_40 to %return.param // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @Test() -> out %return.param: %i32 { // CHECK:STDOUT: !entry: // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %c.patt: %pattern_type.35b = ref_binding_pattern c [concrete] // CHECK:STDOUT: %c.var_patt: %pattern_type.35b = var_pattern %c.patt [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %c.var: ref %Inner.74c = var %c.var_patt // CHECK:STDOUT: %Outer.ref.loc13_29: %Outer.type = name_ref Outer, file.%Outer.decl [concrete = constants.%Outer.generic] // CHECK:STDOUT: %i32.loc13_35: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %Copy.facet.loc13_38: %Copy.type = facet_value %i32.loc13_35, (constants.%Copy.impl_witness.f17) [concrete = constants.%Copy.facet.de4] // CHECK:STDOUT: %.loc13_38: %Copy.type = converted %i32.loc13_35, %Copy.facet.loc13_38 [concrete = constants.%Copy.facet.de4] // CHECK:STDOUT: %Outer.loc13_38: type = class_type @Outer, @Outer(constants.%Copy.facet.de4) [concrete = constants.%Outer.a6c] // CHECK:STDOUT: %.loc13_39: %Outer.F.type.20b = specific_constant @Outer.%Outer.F.decl, @Outer(constants.%Copy.facet.de4) [concrete = constants.%Outer.F.119] // CHECK:STDOUT: %F.ref: %Outer.F.type.20b = name_ref F, %.loc13_39 [concrete = constants.%Outer.F.119] // CHECK:STDOUT: %int_1: Core.IntLiteral = int_value 1 [concrete = constants.%int_1.5b8] // CHECK:STDOUT: %Copy.facet.loc13_43: %Copy.type = facet_value Core.IntLiteral, (constants.%Copy.impl_witness.006) [concrete = constants.%Copy.facet.cdd] // CHECK:STDOUT: %.loc13_43: %Copy.type = converted Core.IntLiteral, %Copy.facet.loc13_43 [concrete = constants.%Copy.facet.cdd] // CHECK:STDOUT: %Outer.F.specific_fn: = specific_function %F.ref, @Outer.F(constants.%Copy.facet.de4) [concrete = constants.%Outer.F.specific_fn] // CHECK:STDOUT: %.loc13_3: ref %Inner.74c = splice_block %c.var {} // CHECK:STDOUT: %impl.elem0.loc13: %.545 = impl_witness_access constants.%ImplicitAs.impl_witness.6bc, element0 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5] // CHECK:STDOUT: %bound_method.loc13_42.1: = bound_method %int_1, %impl.elem0.loc13 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.bound] // CHECK:STDOUT: %specific_fn.loc13: = specific_function %impl.elem0.loc13, @Core.IntLiteral.as.ImplicitAs.impl.Convert(constants.%int_32) [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn] // CHECK:STDOUT: %bound_method.loc13_42.2: = bound_method %int_1, %specific_fn.loc13 [concrete = constants.%bound_method] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.call: init %i32 = call %bound_method.loc13_42.2(%int_1) [concrete = constants.%int_1.5d2] // CHECK:STDOUT: %.loc13_42.1: %i32 = value_of_initializer %Core.IntLiteral.as.ImplicitAs.impl.Convert.call [concrete = constants.%int_1.5d2] // CHECK:STDOUT: %.loc13_42.2: %i32 = converted %int_1, %.loc13_42.1 [concrete = constants.%int_1.5d2] // CHECK:STDOUT: %Outer.F.call: init %Inner.74c to %.loc13_3 = call %Outer.F.specific_fn(%.loc13_42.2) // CHECK:STDOUT: assign %c.var, %Outer.F.call // CHECK:STDOUT: %.loc13_20.1: type = splice_block %Inner.ref [concrete = constants.%Inner.74c] { // CHECK:STDOUT: %Outer.ref.loc13_10: %Outer.type = name_ref Outer, file.%Outer.decl [concrete = constants.%Outer.generic] // CHECK:STDOUT: %i32.loc13_16: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %Copy.facet.loc13_19: %Copy.type = facet_value %i32.loc13_16, (constants.%Copy.impl_witness.f17) [concrete = constants.%Copy.facet.de4] // CHECK:STDOUT: %.loc13_19: %Copy.type = converted %i32.loc13_16, %Copy.facet.loc13_19 [concrete = constants.%Copy.facet.de4] // CHECK:STDOUT: %Outer.loc13_19: type = class_type @Outer, @Outer(constants.%Copy.facet.de4) [concrete = constants.%Outer.a6c] // CHECK:STDOUT: %.loc13_20.2: type = specific_constant @Outer.%Inner.decl, @Outer(constants.%Copy.facet.de4) [concrete = constants.%Inner.74c] // CHECK:STDOUT: %Inner.ref: type = name_ref Inner, %.loc13_20.2 [concrete = constants.%Inner.74c] // CHECK:STDOUT: } // CHECK:STDOUT: %c: ref %Inner.74c = ref_binding c, %c.var // CHECK:STDOUT: %c.ref: ref %Inner.74c = name_ref c, %c // CHECK:STDOUT: %n.ref: %Inner.elem.34c = name_ref n, @Inner.%.loc6_10 [concrete = @Inner.%.loc6_10] // CHECK:STDOUT: %.loc14_11.1: ref %i32 = class_element_access %c.ref, element0 // CHECK:STDOUT: %.loc14_11.2: %i32 = acquire_value %.loc14_11.1 // CHECK:STDOUT: %impl.elem0.loc14: %.8e2 = impl_witness_access constants.%Copy.impl_witness.f17, element0 [concrete = constants.%Int.as.Copy.impl.Op.664] // CHECK:STDOUT: %bound_method.loc14_11.1: = bound_method %.loc14_11.2, %impl.elem0.loc14 // CHECK:STDOUT: %specific_fn.loc14: = specific_function %impl.elem0.loc14, @Int.as.Copy.impl.Op(constants.%int_32) [concrete = constants.%Int.as.Copy.impl.Op.specific_fn] // CHECK:STDOUT: %bound_method.loc14_11.2: = bound_method %.loc14_11.2, %specific_fn.loc14 // CHECK:STDOUT: %Int.as.Copy.impl.Op.call: init %i32 = call %bound_method.loc14_11.2(%.loc14_11.2) // CHECK:STDOUT: %Destroy.Op.bound: = bound_method %c.var, constants.%Destroy.Op // CHECK:STDOUT: %Destroy.Op.call: init %empty_tuple.type = call %Destroy.Op.bound(%c.var) // CHECK:STDOUT: return %Int.as.Copy.impl.Op.call // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @Destroy.Op(%self.param: ref %Inner.74c) = "no_op"; // CHECK:STDOUT: // CHECK:STDOUT: specific @Outer(constants.%T.035) { // CHECK:STDOUT: %T.loc4_13.1 => constants.%T.035 // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %Inner => constants.%Inner.bcf // CHECK:STDOUT: %Outer.F.type => constants.%Outer.F.type.2fb // CHECK:STDOUT: %Outer.F => constants.%Outer.F.5e3 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @Inner(constants.%T.035) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: %T => constants.%T.035 // CHECK:STDOUT: %T.binding.as_type => constants.%T.binding.as_type // CHECK:STDOUT: %require_complete => constants.%require_complete.67c // CHECK:STDOUT: %Inner => constants.%Inner.bcf // CHECK:STDOUT: %Inner.elem => constants.%Inner.elem.f8d // CHECK:STDOUT: %struct_type.n => constants.%struct_type.n.47a // CHECK:STDOUT: %complete_type.loc7_3.2 => constants.%complete_type.072 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @Outer.F(constants.%T.035) { // CHECK:STDOUT: %T => constants.%T.035 // CHECK:STDOUT: %T.binding.as_type => constants.%T.binding.as_type // CHECK:STDOUT: %pattern_type.loc9_8 => constants.%pattern_type.9b9f0c.2 // CHECK:STDOUT: %Inner => constants.%Inner.bcf // CHECK:STDOUT: %.loc9_17.1 => constants.%.d79 // CHECK:STDOUT: %pattern_type.loc9_14 => constants.%pattern_type.611 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @Outer(constants.%Copy.facet.de4) { // CHECK:STDOUT: %T.loc4_13.1 => constants.%Copy.facet.de4 // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %Inner => constants.%Inner.74c // CHECK:STDOUT: %Outer.F.type => constants.%Outer.F.type.20b // CHECK:STDOUT: %Outer.F => constants.%Outer.F.119 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @Inner(constants.%Copy.facet.de4) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: %T => constants.%Copy.facet.de4 // CHECK:STDOUT: %T.binding.as_type => constants.%i32 // CHECK:STDOUT: %require_complete => constants.%complete_type.f8a // CHECK:STDOUT: %Inner => constants.%Inner.74c // CHECK:STDOUT: %Inner.elem => constants.%Inner.elem.34c // CHECK:STDOUT: %struct_type.n => constants.%struct_type.n.033 // CHECK:STDOUT: %complete_type.loc7_3.2 => constants.%complete_type.54b // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @Outer.F(constants.%Copy.facet.de4) { // CHECK:STDOUT: %T => constants.%Copy.facet.de4 // CHECK:STDOUT: %T.binding.as_type => constants.%i32 // CHECK:STDOUT: %pattern_type.loc9_8 => constants.%pattern_type.7ce // CHECK:STDOUT: %Inner => constants.%Inner.74c // CHECK:STDOUT: %.loc9_17.1 => constants.%.42e // CHECK:STDOUT: %pattern_type.loc9_14 => constants.%pattern_type.35b // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete.loc9_9 => constants.%complete_type.f8a // CHECK:STDOUT: %require_complete.loc9_17 => constants.%complete_type.54b // CHECK:STDOUT: %struct_type.n => constants.%struct_type.n.033 // CHECK:STDOUT: %Copy.lookup_impl_witness => constants.%Copy.impl_witness.f17 // CHECK:STDOUT: %Copy.WithSelf.Op.type => constants.%Copy.WithSelf.Op.type.081 // CHECK:STDOUT: %.loc9_38 => constants.%.8e2 // CHECK:STDOUT: %impl.elem0.loc9_38.2 => constants.%Int.as.Copy.impl.Op.664 // CHECK:STDOUT: %specific_impl_fn.loc9_38.2 => constants.%Int.as.Copy.impl.Op.specific_fn // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- interface.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %type: type = facet_type [concrete] // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self] // CHECK:STDOUT: %pattern_type.98f: type = pattern_type type [concrete] // CHECK:STDOUT: %T: type = symbolic_binding T, 0 [symbolic] // CHECK:STDOUT: %Outer.type: type = generic_class_type @Outer [concrete] // CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] // CHECK:STDOUT: %Outer.generic: %Outer.type = struct_value () [concrete] // CHECK:STDOUT: %Outer.387: type = class_type @Outer, @Outer(%T) [symbolic] // CHECK:STDOUT: %Inner.type.6ef: type = facet_type <@Inner, @Inner(%T)> [symbolic] // CHECK:STDOUT: %Self.d55: %Inner.type.6ef = symbolic_binding Self, 1 [symbolic] // CHECK:STDOUT: %Self.binding.as_type.534: type = symbolic_binding_type Self, 1, %Self.d55 [symbolic] // CHECK:STDOUT: %pattern_type.72a: type = pattern_type %Self.binding.as_type.534 [symbolic] // CHECK:STDOUT: %.184: Core.Form = init_form %T [symbolic] // CHECK:STDOUT: %pattern_type.51d: type = pattern_type %T [symbolic] // CHECK:STDOUT: %Inner.WithSelf.F.type.675: type = fn_type @Inner.WithSelf.F, @Inner.WithSelf(%T, %Self.d55) [symbolic] // CHECK:STDOUT: %Inner.WithSelf.F.06f: %Inner.WithSelf.F.type.675 = struct_value () [symbolic] // CHECK:STDOUT: %Inner.assoc_type.be2: type = assoc_entity_type @Inner, @Inner(%T) [symbolic] // CHECK:STDOUT: %assoc0.058: %Inner.assoc_type.be2 = assoc_entity element0, @Inner.WithSelf.%Inner.WithSelf.F.decl [symbolic] // CHECK:STDOUT: %C.131: type = class_type @C, @C(%T) [symbolic] // CHECK:STDOUT: %Inner.impl_witness.eb2: = impl_witness @C.as.Inner.impl.%Inner.impl_witness_table, @C.as.Inner.impl(%T) [symbolic] // CHECK:STDOUT: %require_complete.8b6: = require_complete_type %Inner.type.6ef [symbolic] // CHECK:STDOUT: %pattern_type.fe7: type = pattern_type %C.131 [symbolic] // CHECK:STDOUT: %C.as.Inner.impl.F.type.72e: type = fn_type @C.as.Inner.impl.F, @C.as.Inner.impl(%T) [symbolic] // CHECK:STDOUT: %C.as.Inner.impl.F.28d: %C.as.Inner.impl.F.type.72e = struct_value () [symbolic] // CHECK:STDOUT: %Inner.facet.921: %Inner.type.6ef = facet_value %C.131, (%Inner.impl_witness.eb2) [symbolic] // CHECK:STDOUT: %Inner.WithSelf.F.type.704: type = fn_type @Inner.WithSelf.F, @Inner.WithSelf(%T, %Inner.facet.921) [symbolic] // CHECK:STDOUT: %Inner.WithSelf.F.118: %Inner.WithSelf.F.type.704 = struct_value () [symbolic] // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %complete_type.357: = complete_type_witness %empty_struct_type [concrete] // CHECK:STDOUT: %require_complete.4fd: = require_complete_type %C.131 [symbolic] // CHECK:STDOUT: %require_complete.944: = require_complete_type %T [symbolic] // CHECK:STDOUT: %.1f8: require_specific_def_type = require_specific_def @C.as.Inner.impl(%T) [symbolic] // CHECK:STDOUT: %Inner.lookup_impl_witness: = lookup_impl_witness %C.131, @Inner, @Inner(%T) [symbolic] // CHECK:STDOUT: %Inner.facet.f78: %Inner.type.6ef = facet_value %C.131, (%Inner.lookup_impl_witness) [symbolic] // CHECK:STDOUT: %Inner.WithSelf.F.type.d96: type = fn_type @Inner.WithSelf.F, @Inner.WithSelf(%T, %Inner.facet.f78) [symbolic] // CHECK:STDOUT: %Inner.WithSelf.F.66d: %Inner.WithSelf.F.type.d96 = struct_value () [symbolic] // CHECK:STDOUT: %.29b: type = fn_type_with_self_type %Inner.WithSelf.F.type.d96, %Inner.facet.f78 [symbolic] // CHECK:STDOUT: %impl.elem0: %.29b = impl_witness_access %Inner.lookup_impl_witness, element0 [symbolic] // CHECK:STDOUT: %specific_impl_fn: = specific_impl_function %impl.elem0, @Inner.WithSelf.F(%T, %Inner.facet.f78) [symbolic] // CHECK:STDOUT: %D: type = class_type @D [concrete] // CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [concrete] // CHECK:STDOUT: %Int.type: type = generic_class_type @Int [concrete] // CHECK:STDOUT: %Int.generic: %Int.type = struct_value () [concrete] // CHECK:STDOUT: %i32: type = class_type @Int, @Int(%int_32) [concrete] // CHECK:STDOUT: %Outer.d71: type = class_type @Outer, @Outer(%i32) [concrete] // CHECK:STDOUT: %Inner.type.94a: type = facet_type <@Inner, @Inner(%i32)> [concrete] // CHECK:STDOUT: %C.d3f: type = class_type @C, @C(%i32) [concrete] // CHECK:STDOUT: %Inner.impl_witness.667: = impl_witness @D.as.Inner.impl.%Inner.impl_witness_table [concrete] // CHECK:STDOUT: %Self.f19: %Inner.type.94a = symbolic_binding Self, 1 [symbolic] // CHECK:STDOUT: %Inner.WithSelf.F.type.74d: type = fn_type @Inner.WithSelf.F, @Inner.WithSelf(%i32, %Self.d55) [symbolic] // CHECK:STDOUT: %Inner.WithSelf.F.b36: %Inner.WithSelf.F.type.74d = struct_value () [symbolic] // CHECK:STDOUT: %Inner.assoc_type.564: type = assoc_entity_type @Inner, @Inner(%i32) [concrete] // CHECK:STDOUT: %assoc0.958: %Inner.assoc_type.564 = assoc_entity element0, @Inner.WithSelf.%Inner.WithSelf.F.decl [concrete] // CHECK:STDOUT: %pattern_type.9c8: type = pattern_type %D [concrete] // CHECK:STDOUT: %.ff5: Core.Form = init_form %i32 [concrete] // CHECK:STDOUT: %pattern_type.7ce: type = pattern_type %i32 [concrete] // CHECK:STDOUT: %D.as.Inner.impl.F.type: type = fn_type @D.as.Inner.impl.F [concrete] // CHECK:STDOUT: %D.as.Inner.impl.F: %D.as.Inner.impl.F.type = struct_value () [concrete] // CHECK:STDOUT: %Inner.facet.dc9: %Inner.type.94a = facet_value %D, (%Inner.impl_witness.667) [concrete] // CHECK:STDOUT: %Inner.WithSelf.F.type.6b0: type = fn_type @Inner.WithSelf.F, @Inner.WithSelf(%i32, %Inner.facet.dc9) [concrete] // CHECK:STDOUT: %Inner.WithSelf.F.17e: %Inner.WithSelf.F.type.6b0 = struct_value () [concrete] // CHECK:STDOUT: %Test.type: type = fn_type @Test [concrete] // CHECK:STDOUT: %Test: %Test.type = struct_value () [concrete] // CHECK:STDOUT: %i32.builtin: type = int_type signed, %int_32 [concrete] // CHECK:STDOUT: %complete_type.f8a: = complete_type_witness %i32.builtin [concrete] // CHECK:STDOUT: %pattern_type.129: type = pattern_type %C.d3f [concrete] // CHECK:STDOUT: %empty_struct: %empty_struct_type = struct_value () [concrete] // CHECK:STDOUT: %C.val: %C.d3f = struct_value () [concrete] // CHECK:STDOUT: %Inner.impl_witness.d48: = impl_witness @C.as.Inner.impl.%Inner.impl_witness_table, @C.as.Inner.impl(%i32) [concrete] // CHECK:STDOUT: %complete_type.087: = complete_type_witness %Inner.type.94a [concrete] // CHECK:STDOUT: %C.as.Inner.impl.F.type.7ce: type = fn_type @C.as.Inner.impl.F, @C.as.Inner.impl(%i32) [concrete] // CHECK:STDOUT: %C.as.Inner.impl.F.356: %C.as.Inner.impl.F.type.7ce = struct_value () [concrete] // CHECK:STDOUT: %.c0b: require_specific_def_type = require_specific_def @C.as.Inner.impl(%i32) [concrete] // CHECK:STDOUT: %Inner.facet.ac9: %Inner.type.94a = facet_value %C.d3f, (%Inner.impl_witness.d48) [concrete] // CHECK:STDOUT: %Inner.WithSelf.F.type.96b: type = fn_type @Inner.WithSelf.F, @Inner.WithSelf(%i32, %Inner.facet.ac9) [concrete] // CHECK:STDOUT: %Inner.WithSelf.F.4b0: %Inner.WithSelf.F.type.96b = struct_value () [concrete] // CHECK:STDOUT: %.fd6: type = fn_type_with_self_type %Inner.WithSelf.F.type.96b, %Inner.facet.ac9 [concrete] // CHECK:STDOUT: %C.as.Inner.impl.F.specific_fn: = specific_function %C.as.Inner.impl.F.356, @C.as.Inner.impl.F(%i32) [concrete] // CHECK:STDOUT: %Destroy.type: type = facet_type <@Destroy> [concrete] // CHECK:STDOUT: %Destroy.Op.type: type = fn_type @Destroy.Op [concrete] // CHECK:STDOUT: %Destroy.Op: %Destroy.Op.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: .Int = %Core.Int // CHECK:STDOUT: .Destroy = %Core.Destroy // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: %Core.Int: %Int.type = import_ref Core//prelude/parts/int, Int, loaded [concrete = constants.%Int.generic] // CHECK:STDOUT: %Core.Destroy: type = import_ref Core//prelude/parts/destroy, Destroy, loaded [concrete = constants.%Destroy.type] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .Outer = %Outer.decl // CHECK:STDOUT: .D = %D.decl // CHECK:STDOUT: .Test = %Test.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %Outer.decl: %Outer.type = class_decl @Outer [concrete = constants.%Outer.generic] { // CHECK:STDOUT: %T.patt: %pattern_type.98f = symbolic_binding_pattern T, 0 [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %.loc4_17.1: type = splice_block %.loc4_17.2 [concrete = type] { // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %.loc4_17.2: type = type_literal type [concrete = type] // CHECK:STDOUT: } // CHECK:STDOUT: %T.loc4_13.2: type = symbolic_binding T, 0 [symbolic = %T.loc4_13.1 (constants.%T)] // CHECK:STDOUT: } // CHECK:STDOUT: %D.decl: type = class_decl @D [concrete = constants.%D] {} {} // CHECK:STDOUT: %Test.decl: %Test.type = fn_decl @Test [concrete = constants.%Test] { // CHECK:STDOUT: %return.patt: %pattern_type.7ce = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: %pattern_type.7ce = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %i32.loc22: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %.loc22: Core.Form = init_form %i32.loc22 [concrete = constants.%.ff5] // CHECK:STDOUT: %return.param: ref %i32 = out_param call_param0 // CHECK:STDOUT: %return: ref %i32 = return_slot %return.param // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic interface @Inner(@Outer.%T.loc4_13.2: type) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: %T: type = symbolic_binding T, 0 [symbolic = %T (constants.%T)] // CHECK:STDOUT: %Inner.type: type = facet_type <@Inner, @Inner(%T)> [symbolic = %Inner.type (constants.%Inner.type.6ef)] // CHECK:STDOUT: %Self.loc5_19.2: @Inner.%Inner.type (%Inner.type.6ef) = symbolic_binding Self, 1 [symbolic = %Self.loc5_19.2 (constants.%Self.d55)] // CHECK:STDOUT: // CHECK:STDOUT: interface { // CHECK:STDOUT: %Self.loc5_19.1: @Inner.%Inner.type (%Inner.type.6ef) = symbolic_binding Self, 1 [symbolic = %Self.loc5_19.2 (constants.%Self.d55)] // CHECK:STDOUT: %Inner.WithSelf.decl = interface_with_self_decl @Inner [concrete] // CHECK:STDOUT: // CHECK:STDOUT: !with Self: // CHECK:STDOUT: %Inner.WithSelf.F.decl: @Inner.WithSelf.%Inner.WithSelf.F.type (%Inner.WithSelf.F.type.675) = fn_decl @Inner.WithSelf.F [symbolic = @Inner.WithSelf.%Inner.WithSelf.F (constants.%Inner.WithSelf.F.06f)] { // CHECK:STDOUT: %self.patt: @Inner.WithSelf.F.%pattern_type.loc6_10 (%pattern_type.72a) = value_binding_pattern self [concrete] // CHECK:STDOUT: %self.param_patt: @Inner.WithSelf.F.%pattern_type.loc6_10 (%pattern_type.72a) = value_param_pattern %self.patt [concrete] // CHECK:STDOUT: %return.patt: @Inner.WithSelf.F.%pattern_type.loc6_24 (%pattern_type.51d) = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: @Inner.WithSelf.F.%pattern_type.loc6_24 (%pattern_type.51d) = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %T.ref: type = name_ref T, @Outer.%T.loc4_13.2 [symbolic = %T (constants.%T)] // CHECK:STDOUT: %.loc6_27.2: Core.Form = init_form %T.ref [symbolic = %.loc6_27.1 (constants.%.184)] // CHECK:STDOUT: %self.param: @Inner.WithSelf.F.%Self.binding.as_type (%Self.binding.as_type.534) = value_param call_param0 // CHECK:STDOUT: %.loc6_16.1: type = splice_block %.loc6_16.3 [symbolic = %Self.binding.as_type (constants.%Self.binding.as_type.534)] { // CHECK:STDOUT: %.loc6_16.2: @Inner.WithSelf.F.%Inner.type (%Inner.type.6ef) = specific_constant @Inner.%Self.loc5_19.1, @Inner(constants.%T) [symbolic = %Self (constants.%Self.d55)] // CHECK:STDOUT: %Self.ref: @Inner.WithSelf.F.%Inner.type (%Inner.type.6ef) = name_ref Self, %.loc6_16.2 [symbolic = %Self (constants.%Self.d55)] // CHECK:STDOUT: %Self.as_type: type = facet_access_type %Self.ref [symbolic = %Self.binding.as_type (constants.%Self.binding.as_type.534)] // CHECK:STDOUT: %.loc6_16.3: type = converted %Self.ref, %Self.as_type [symbolic = %Self.binding.as_type (constants.%Self.binding.as_type.534)] // CHECK:STDOUT: } // CHECK:STDOUT: %self: @Inner.WithSelf.F.%Self.binding.as_type (%Self.binding.as_type.534) = value_binding self, %self.param // CHECK:STDOUT: %return.param: ref @Inner.WithSelf.F.%T (%T) = out_param call_param1 // CHECK:STDOUT: %return: ref @Inner.WithSelf.F.%T (%T) = return_slot %return.param // CHECK:STDOUT: } // CHECK:STDOUT: %assoc0.loc6_28.1: @Inner.WithSelf.%Inner.assoc_type (%Inner.assoc_type.be2) = assoc_entity element0, %Inner.WithSelf.F.decl [symbolic = %assoc0.loc6_28.2 (constants.%assoc0.058)] // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = %Self.loc5_19.1 // CHECK:STDOUT: .T = // CHECK:STDOUT: .T = // CHECK:STDOUT: .F = @Inner.WithSelf.%assoc0.loc6_28.1 // CHECK:STDOUT: witness = (@Inner.WithSelf.%Inner.WithSelf.F.decl) // CHECK:STDOUT: // CHECK:STDOUT: !requires: // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic impl @C.as.Inner.impl(@Outer.%T.loc4_13.2: type) { // CHECK:STDOUT: %T: type = symbolic_binding T, 0 [symbolic = %T (constants.%T)] // CHECK:STDOUT: %C: type = class_type @C, @C(%T) [symbolic = %C (constants.%C.131)] // CHECK:STDOUT: %Inner.type: type = facet_type <@Inner, @Inner(%T)> [symbolic = %Inner.type (constants.%Inner.type.6ef)] // CHECK:STDOUT: %Inner.impl_witness.loc10_19.2: = impl_witness %Inner.impl_witness_table, @C.as.Inner.impl(%T) [symbolic = %Inner.impl_witness.loc10_19.2 (constants.%Inner.impl_witness.eb2)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete: = require_complete_type %Inner.type [symbolic = %require_complete (constants.%require_complete.8b6)] // CHECK:STDOUT: %C.as.Inner.impl.F.type: type = fn_type @C.as.Inner.impl.F, @C.as.Inner.impl(%T) [symbolic = %C.as.Inner.impl.F.type (constants.%C.as.Inner.impl.F.type.72e)] // CHECK:STDOUT: %C.as.Inner.impl.F: @C.as.Inner.impl.%C.as.Inner.impl.F.type (%C.as.Inner.impl.F.type.72e) = struct_value () [symbolic = %C.as.Inner.impl.F (constants.%C.as.Inner.impl.F.28d)] // CHECK:STDOUT: // CHECK:STDOUT: impl: %Self.ref as %Inner.ref { // CHECK:STDOUT: %C.as.Inner.impl.F.decl: @C.as.Inner.impl.%C.as.Inner.impl.F.type (%C.as.Inner.impl.F.type.72e) = fn_decl @C.as.Inner.impl.F [symbolic = @C.as.Inner.impl.%C.as.Inner.impl.F (constants.%C.as.Inner.impl.F.28d)] { // CHECK:STDOUT: %self.patt: @C.as.Inner.impl.F.%pattern_type.loc11_12 (%pattern_type.fe7) = value_binding_pattern self [concrete] // CHECK:STDOUT: %self.param_patt: @C.as.Inner.impl.F.%pattern_type.loc11_12 (%pattern_type.fe7) = value_param_pattern %self.patt [concrete] // CHECK:STDOUT: %return.patt: @C.as.Inner.impl.F.%pattern_type.loc11_23 (%pattern_type.51d) = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: @C.as.Inner.impl.F.%pattern_type.loc11_23 (%pattern_type.51d) = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %T.ref: type = name_ref T, @Outer.%T.loc4_13.2 [symbolic = %T (constants.%T)] // CHECK:STDOUT: %.loc11_26.3: Core.Form = init_form %T.ref [symbolic = %.loc11_26.2 (constants.%.184)] // CHECK:STDOUT: %self.param: @C.as.Inner.impl.F.%C (%C.131) = value_param call_param0 // CHECK:STDOUT: %.loc11_18.1: type = splice_block %C.ref [symbolic = %C (constants.%C.131)] { // CHECK:STDOUT: %.loc11_18.2: type = specific_constant @Outer.%C.decl, @Outer(constants.%T) [symbolic = %C (constants.%C.131)] // CHECK:STDOUT: %C.ref: type = name_ref C, %.loc11_18.2 [symbolic = %C (constants.%C.131)] // CHECK:STDOUT: } // CHECK:STDOUT: %self: @C.as.Inner.impl.F.%C (%C.131) = value_binding self, %self.param // CHECK:STDOUT: %return.param: ref @C.as.Inner.impl.F.%T (%T) = out_param call_param1 // CHECK:STDOUT: %return: ref @C.as.Inner.impl.F.%T (%T) = return_slot %return.param // CHECK:STDOUT: } // CHECK:STDOUT: %Inner.impl_witness_table = impl_witness_table (%C.as.Inner.impl.F.decl), @C.as.Inner.impl [concrete] // CHECK:STDOUT: %Inner.impl_witness.loc10_19.1: = impl_witness %Inner.impl_witness_table, @C.as.Inner.impl(constants.%T) [symbolic = %Inner.impl_witness.loc10_19.2 (constants.%Inner.impl_witness.eb2)] // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .C = // CHECK:STDOUT: .T = // CHECK:STDOUT: .F = %C.as.Inner.impl.F.decl // CHECK:STDOUT: .Inner = // CHECK:STDOUT: witness = %Inner.impl_witness.loc10_19.1 // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: impl @D.as.Inner.impl: %Self.ref as %Inner.ref { // CHECK:STDOUT: %D.as.Inner.impl.F.decl: %D.as.Inner.impl.F.type = fn_decl @D.as.Inner.impl.F [concrete = constants.%D.as.Inner.impl.F] { // CHECK:STDOUT: %self.patt: %pattern_type.9c8 = value_binding_pattern self [concrete] // CHECK:STDOUT: %self.param_patt: %pattern_type.9c8 = value_param_pattern %self.patt [concrete] // CHECK:STDOUT: %return.patt: %pattern_type.7ce = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: %pattern_type.7ce = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %i32: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %.loc18: Core.Form = init_form %i32 [concrete = constants.%.ff5] // CHECK:STDOUT: %self.param: %D = value_param call_param0 // CHECK:STDOUT: %D.ref: type = name_ref D, file.%D.decl [concrete = constants.%D] // CHECK:STDOUT: %self: %D = value_binding self, %self.param // CHECK:STDOUT: %return.param: ref %i32 = out_param call_param1 // CHECK:STDOUT: %return: ref %i32 = return_slot %return.param // CHECK:STDOUT: } // CHECK:STDOUT: %Inner.impl_witness_table = impl_witness_table (%D.as.Inner.impl.F.decl), @D.as.Inner.impl [concrete] // CHECK:STDOUT: %Inner.impl_witness: = impl_witness %Inner.impl_witness_table [concrete = constants.%Inner.impl_witness.667] // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .D = // CHECK:STDOUT: .F = %D.as.Inner.impl.F.decl // CHECK:STDOUT: witness = %Inner.impl_witness // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic class @Outer(%T.loc4_13.2: type) { // CHECK:STDOUT: %T.loc4_13.1: type = symbolic_binding T, 0 [symbolic = %T.loc4_13.1 (constants.%T)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %Inner.type: type = facet_type <@Inner, @Inner(%T.loc4_13.1)> [symbolic = %Inner.type (constants.%Inner.type.6ef)] // CHECK:STDOUT: %C: type = class_type @C, @C(%T.loc4_13.1) [symbolic = %C (constants.%C.131)] // CHECK:STDOUT: // CHECK:STDOUT: class { // CHECK:STDOUT: %Inner.decl: type = interface_decl @Inner [symbolic = @Outer.%Inner.type (constants.%Inner.type.6ef)] {} {} // CHECK:STDOUT: %C.decl: type = class_decl @C [symbolic = @Outer.%C (constants.%C.131)] {} {} // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type.357] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%Outer.387 // CHECK:STDOUT: .Inner = %Inner.decl // CHECK:STDOUT: .T = // CHECK:STDOUT: .C = %C.decl // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic class @C(@Outer.%T.loc4_13.2: type) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: // CHECK:STDOUT: class { // CHECK:STDOUT: impl_decl @C.as.Inner.impl [concrete] {} { // CHECK:STDOUT: %Self.ref: type = name_ref Self, constants.%C.131 [symbolic = %C (constants.%C.131)] // CHECK:STDOUT: %.loc10: type = specific_constant @Outer.%Inner.decl, @Outer(constants.%T) [symbolic = %Inner.type (constants.%Inner.type.6ef)] // CHECK:STDOUT: %Inner.ref: type = name_ref Inner, %.loc10 [symbolic = %Inner.type (constants.%Inner.type.6ef)] // CHECK:STDOUT: } // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type.357] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%C.131 // CHECK:STDOUT: .Inner = // CHECK:STDOUT: .C = // CHECK:STDOUT: .T = // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @D { // CHECK:STDOUT: impl_decl @D.as.Inner.impl [concrete] {} { // CHECK:STDOUT: %Self.ref: type = name_ref Self, constants.%D [concrete = constants.%D] // CHECK:STDOUT: %Outer.ref: %Outer.type = name_ref Outer, file.%Outer.decl [concrete = constants.%Outer.generic] // CHECK:STDOUT: %i32: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %Outer: type = class_type @Outer, @Outer(constants.%i32) [concrete = constants.%Outer.d71] // CHECK:STDOUT: %.loc17: type = specific_constant @Outer.%Inner.decl, @Outer(constants.%i32) [concrete = constants.%Inner.type.94a] // CHECK:STDOUT: %Inner.ref: type = name_ref Inner, %.loc17 [concrete = constants.%Inner.type.94a] // CHECK:STDOUT: } // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type.357] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%D // CHECK:STDOUT: .Outer = // CHECK:STDOUT: .D = // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic fn @Inner.WithSelf.F(@Outer.%T.loc4_13.2: type, @Inner.%Self.loc5_19.1: @Inner.%Inner.type (%Inner.type.6ef)) { // CHECK:STDOUT: %T: type = symbolic_binding T, 0 [symbolic = %T (constants.%T)] // CHECK:STDOUT: %Inner.type: type = facet_type <@Inner, @Inner(%T)> [symbolic = %Inner.type (constants.%Inner.type.6ef)] // CHECK:STDOUT: %Self: @Inner.WithSelf.F.%Inner.type (%Inner.type.6ef) = symbolic_binding Self, 1 [symbolic = %Self (constants.%Self.d55)] // CHECK:STDOUT: %Self.binding.as_type: type = symbolic_binding_type Self, 1, %Self [symbolic = %Self.binding.as_type (constants.%Self.binding.as_type.534)] // CHECK:STDOUT: %pattern_type.loc6_10: type = pattern_type %Self.binding.as_type [symbolic = %pattern_type.loc6_10 (constants.%pattern_type.72a)] // CHECK:STDOUT: %.loc6_27.1: Core.Form = init_form %T [symbolic = %.loc6_27.1 (constants.%.184)] // CHECK:STDOUT: %pattern_type.loc6_24: type = pattern_type %T [symbolic = %pattern_type.loc6_24 (constants.%pattern_type.51d)] // CHECK:STDOUT: // CHECK:STDOUT: fn(%self.param: @Inner.WithSelf.F.%Self.binding.as_type (%Self.binding.as_type.534)) -> out %return.param: @Inner.WithSelf.F.%T (%T); // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic fn @C.as.Inner.impl.F(@Outer.%T.loc4_13.2: type) { // CHECK:STDOUT: %T: type = symbolic_binding T, 0 [symbolic = %T (constants.%T)] // CHECK:STDOUT: %C: type = class_type @C, @C(%T) [symbolic = %C (constants.%C.131)] // CHECK:STDOUT: %pattern_type.loc11_12: type = pattern_type %C [symbolic = %pattern_type.loc11_12 (constants.%pattern_type.fe7)] // CHECK:STDOUT: %.loc11_26.2: Core.Form = init_form %T [symbolic = %.loc11_26.2 (constants.%.184)] // CHECK:STDOUT: %pattern_type.loc11_23: type = pattern_type %T [symbolic = %pattern_type.loc11_23 (constants.%pattern_type.51d)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete.loc11_16: = require_complete_type %C [symbolic = %require_complete.loc11_16 (constants.%require_complete.4fd)] // CHECK:STDOUT: %require_complete.loc11_26: = require_complete_type %T [symbolic = %require_complete.loc11_26 (constants.%require_complete.944)] // CHECK:STDOUT: %Inner.type: type = facet_type <@Inner, @Inner(%T)> [symbolic = %Inner.type (constants.%Inner.type.6ef)] // CHECK:STDOUT: %require_complete.loc11_48: = require_complete_type %Inner.type [symbolic = %require_complete.loc11_48 (constants.%require_complete.8b6)] // CHECK:STDOUT: %Inner.assoc_type: type = assoc_entity_type @Inner, @Inner(%T) [symbolic = %Inner.assoc_type (constants.%Inner.assoc_type.be2)] // CHECK:STDOUT: %assoc0: @C.as.Inner.impl.F.%Inner.assoc_type (%Inner.assoc_type.be2) = assoc_entity element0, @Inner.WithSelf.%Inner.WithSelf.F.decl [symbolic = %assoc0 (constants.%assoc0.058)] // CHECK:STDOUT: %.loc11_41.1: require_specific_def_type = require_specific_def @C.as.Inner.impl(%T) [symbolic = %.loc11_41.1 (constants.%.1f8)] // CHECK:STDOUT: %Inner.lookup_impl_witness: = lookup_impl_witness %C, @Inner, @Inner(%T) [symbolic = %Inner.lookup_impl_witness (constants.%Inner.lookup_impl_witness)] // CHECK:STDOUT: %Inner.facet: @C.as.Inner.impl.F.%Inner.type (%Inner.type.6ef) = facet_value %C, (%Inner.lookup_impl_witness) [symbolic = %Inner.facet (constants.%Inner.facet.f78)] // CHECK:STDOUT: %Inner.WithSelf.F.type: type = fn_type @Inner.WithSelf.F, @Inner.WithSelf(%T, %Inner.facet) [symbolic = %Inner.WithSelf.F.type (constants.%Inner.WithSelf.F.type.d96)] // CHECK:STDOUT: %.loc11_41.2: type = fn_type_with_self_type %Inner.WithSelf.F.type, %Inner.facet [symbolic = %.loc11_41.2 (constants.%.29b)] // CHECK:STDOUT: %impl.elem0.loc11_41.2: @C.as.Inner.impl.F.%.loc11_41.2 (%.29b) = impl_witness_access %Inner.lookup_impl_witness, element0 [symbolic = %impl.elem0.loc11_41.2 (constants.%impl.elem0)] // CHECK:STDOUT: %specific_impl_fn.loc11_41.2: = specific_impl_function %impl.elem0.loc11_41.2, @Inner.WithSelf.F(%T, %Inner.facet) [symbolic = %specific_impl_fn.loc11_41.2 (constants.%specific_impl_fn)] // CHECK:STDOUT: // CHECK:STDOUT: fn(%self.param: @C.as.Inner.impl.F.%C (%C.131)) -> out %return.param: @C.as.Inner.impl.F.%T (%T) { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %self.ref: @C.as.Inner.impl.F.%C (%C.131) = name_ref self, %self // CHECK:STDOUT: %.loc11_43: type = specific_constant @Outer.%Inner.decl, @Outer(constants.%T) [symbolic = %Inner.type (constants.%Inner.type.6ef)] // CHECK:STDOUT: %Inner.ref: type = name_ref Inner, %.loc11_43 [symbolic = %Inner.type (constants.%Inner.type.6ef)] // CHECK:STDOUT: %.loc11_48: @C.as.Inner.impl.F.%Inner.assoc_type (%Inner.assoc_type.be2) = specific_constant @Inner.WithSelf.%assoc0.loc6_28.1, @Inner.WithSelf(constants.%T, constants.%Self.d55) [symbolic = %assoc0 (constants.%assoc0.058)] // CHECK:STDOUT: %F.ref: @C.as.Inner.impl.F.%Inner.assoc_type (%Inner.assoc_type.be2) = name_ref F, %.loc11_48 [symbolic = %assoc0 (constants.%assoc0.058)] // CHECK:STDOUT: %impl.elem0.loc11_41.1: @C.as.Inner.impl.F.%.loc11_41.2 (%.29b) = impl_witness_access constants.%Inner.lookup_impl_witness, element0 [symbolic = %impl.elem0.loc11_41.2 (constants.%impl.elem0)] // CHECK:STDOUT: %bound_method.loc11_41: = bound_method %self.ref, %impl.elem0.loc11_41.1 // CHECK:STDOUT: %specific_impl_fn.loc11_41.1: = specific_impl_function %impl.elem0.loc11_41.1, @Inner.WithSelf.F(constants.%T, constants.%Inner.facet.f78) [symbolic = %specific_impl_fn.loc11_41.2 (constants.%specific_impl_fn)] // CHECK:STDOUT: %bound_method.loc11_52: = bound_method %self.ref, %specific_impl_fn.loc11_41.1 // CHECK:STDOUT: %.loc11_26.1: ref @C.as.Inner.impl.F.%T (%T) = splice_block %return.param {} // CHECK:STDOUT: %Inner.WithSelf.F.call: init @C.as.Inner.impl.F.%T (%T) to %.loc11_26.1 = call %bound_method.loc11_52(%self.ref) // CHECK:STDOUT: return %Inner.WithSelf.F.call to %return.param // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @D.as.Inner.impl.F(%self.param: %D) -> out %return.param: %i32; // CHECK:STDOUT: // CHECK:STDOUT: fn @Test() -> out %return.param: %i32 { // CHECK:STDOUT: !entry: // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %c.patt: %pattern_type.129 = ref_binding_pattern c [concrete] // CHECK:STDOUT: %c.var_patt: %pattern_type.129 = var_pattern %c.patt [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %c.var: ref %C.d3f = var %c.var_patt // CHECK:STDOUT: %.loc23_26.1: %empty_struct_type = struct_literal () [concrete = constants.%empty_struct] // CHECK:STDOUT: %.loc23_26.2: init %C.d3f to %c.var = class_init () [concrete = constants.%C.val] // CHECK:STDOUT: %.loc23_3: init %C.d3f = converted %.loc23_26.1, %.loc23_26.2 [concrete = constants.%C.val] // CHECK:STDOUT: assign %c.var, %.loc23_3 // CHECK:STDOUT: %.loc23_20.1: type = splice_block %C.ref [concrete = constants.%C.d3f] { // CHECK:STDOUT: %Outer.ref.loc23: %Outer.type = name_ref Outer, file.%Outer.decl [concrete = constants.%Outer.generic] // CHECK:STDOUT: %i32.loc23: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %Outer.loc23: type = class_type @Outer, @Outer(constants.%i32) [concrete = constants.%Outer.d71] // CHECK:STDOUT: %.loc23_20.2: type = specific_constant @Outer.%C.decl, @Outer(constants.%i32) [concrete = constants.%C.d3f] // CHECK:STDOUT: %C.ref: type = name_ref C, %.loc23_20.2 [concrete = constants.%C.d3f] // CHECK:STDOUT: } // CHECK:STDOUT: %c: ref %C.d3f = ref_binding c, %c.var // CHECK:STDOUT: %c.ref: ref %C.d3f = name_ref c, %c // CHECK:STDOUT: %Outer.ref.loc24: %Outer.type = name_ref Outer, file.%Outer.decl [concrete = constants.%Outer.generic] // CHECK:STDOUT: %i32.loc24: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %Outer.loc24: type = class_type @Outer, @Outer(constants.%i32) [concrete = constants.%Outer.d71] // CHECK:STDOUT: %.loc24_23: type = specific_constant @Outer.%Inner.decl, @Outer(constants.%i32) [concrete = constants.%Inner.type.94a] // CHECK:STDOUT: %Inner.ref: type = name_ref Inner, %.loc24_23 [concrete = constants.%Inner.type.94a] // CHECK:STDOUT: %.loc24_29: %Inner.assoc_type.564 = specific_constant @Inner.WithSelf.%assoc0.loc6_28.1, @Inner.WithSelf(constants.%i32, constants.%Self.d55) [concrete = constants.%assoc0.958] // CHECK:STDOUT: %F.ref: %Inner.assoc_type.564 = name_ref F, %.loc24_29 [concrete = constants.%assoc0.958] // CHECK:STDOUT: %impl.elem0: %.fd6 = impl_witness_access constants.%Inner.impl_witness.d48, element0 [concrete = constants.%C.as.Inner.impl.F.356] // CHECK:STDOUT: %bound_method.loc24_11: = bound_method %c.ref, %impl.elem0 // CHECK:STDOUT: %specific_fn: = specific_function %impl.elem0, @C.as.Inner.impl.F(constants.%i32) [concrete = constants.%C.as.Inner.impl.F.specific_fn] // CHECK:STDOUT: %bound_method.loc24_33: = bound_method %c.ref, %specific_fn // CHECK:STDOUT: %.loc24_10: %C.d3f = acquire_value %c.ref // CHECK:STDOUT: %C.as.Inner.impl.F.call: init %i32 = call %bound_method.loc24_33(%.loc24_10) // CHECK:STDOUT: %Destroy.Op.bound: = bound_method %c.var, constants.%Destroy.Op // CHECK:STDOUT: %Destroy.Op.call: init %empty_tuple.type = call %Destroy.Op.bound(%c.var) // CHECK:STDOUT: return %C.as.Inner.impl.F.call // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @Destroy.Op(%self.param: ref %C.d3f) = "no_op"; // CHECK:STDOUT: // CHECK:STDOUT: specific @Outer(constants.%T) { // CHECK:STDOUT: %T.loc4_13.1 => constants.%T // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %Inner.type => constants.%Inner.type.6ef // CHECK:STDOUT: %C => constants.%C.131 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @Inner(constants.%T) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: %T => constants.%T // CHECK:STDOUT: %Inner.type => constants.%Inner.type.6ef // CHECK:STDOUT: %Self.loc5_19.2 => constants.%Self.d55 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @Inner.WithSelf(constants.%T, constants.%Self.d55) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: %T => constants.%T // CHECK:STDOUT: %Inner.type => constants.%Inner.type.6ef // CHECK:STDOUT: %Self => constants.%Self.d55 // CHECK:STDOUT: %Inner.WithSelf.F.type => constants.%Inner.WithSelf.F.type.675 // CHECK:STDOUT: %Inner.WithSelf.F => constants.%Inner.WithSelf.F.06f // CHECK:STDOUT: %Inner.assoc_type => constants.%Inner.assoc_type.be2 // CHECK:STDOUT: %assoc0.loc6_28.2 => constants.%assoc0.058 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @Inner.WithSelf.F(constants.%T, constants.%Self.d55) { // CHECK:STDOUT: %T => constants.%T // CHECK:STDOUT: %Inner.type => constants.%Inner.type.6ef // CHECK:STDOUT: %Self => constants.%Self.d55 // CHECK:STDOUT: %Self.binding.as_type => constants.%Self.binding.as_type.534 // CHECK:STDOUT: %pattern_type.loc6_10 => constants.%pattern_type.72a // CHECK:STDOUT: %.loc6_27.1 => constants.%.184 // CHECK:STDOUT: %pattern_type.loc6_24 => constants.%pattern_type.51d // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @C(constants.%T) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @C.as.Inner.impl(constants.%T) { // CHECK:STDOUT: %T => constants.%T // CHECK:STDOUT: %C => constants.%C.131 // CHECK:STDOUT: %Inner.type => constants.%Inner.type.6ef // CHECK:STDOUT: %Inner.impl_witness.loc10_19.2 => constants.%Inner.impl_witness.eb2 // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete => constants.%require_complete.8b6 // CHECK:STDOUT: %C.as.Inner.impl.F.type => constants.%C.as.Inner.impl.F.type.72e // CHECK:STDOUT: %C.as.Inner.impl.F => constants.%C.as.Inner.impl.F.28d // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @C.as.Inner.impl.F(constants.%T) { // CHECK:STDOUT: %T => constants.%T // CHECK:STDOUT: %C => constants.%C.131 // CHECK:STDOUT: %pattern_type.loc11_12 => constants.%pattern_type.fe7 // CHECK:STDOUT: %.loc11_26.2 => constants.%.184 // CHECK:STDOUT: %pattern_type.loc11_23 => constants.%pattern_type.51d // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @Inner.WithSelf(constants.%T, constants.%Inner.facet.921) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: %T => constants.%T // CHECK:STDOUT: %Inner.type => constants.%Inner.type.6ef // CHECK:STDOUT: %Self => constants.%Inner.facet.921 // CHECK:STDOUT: %Inner.WithSelf.F.type => constants.%Inner.WithSelf.F.type.704 // CHECK:STDOUT: %Inner.WithSelf.F => constants.%Inner.WithSelf.F.118 // CHECK:STDOUT: %Inner.assoc_type => constants.%Inner.assoc_type.be2 // CHECK:STDOUT: %assoc0.loc6_28.2 => constants.%assoc0.058 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @Inner.WithSelf.F(constants.%T, constants.%Inner.facet.921) { // CHECK:STDOUT: %T => constants.%T // CHECK:STDOUT: %Inner.type => constants.%Inner.type.6ef // CHECK:STDOUT: %Self => constants.%Inner.facet.921 // CHECK:STDOUT: %Self.binding.as_type => constants.%C.131 // CHECK:STDOUT: %pattern_type.loc6_10 => constants.%pattern_type.fe7 // CHECK:STDOUT: %.loc6_27.1 => constants.%.184 // CHECK:STDOUT: %pattern_type.loc6_24 => constants.%pattern_type.51d // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @Inner.WithSelf(constants.%T, constants.%Inner.facet.f78) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: %T => constants.%T // CHECK:STDOUT: %Inner.type => constants.%Inner.type.6ef // CHECK:STDOUT: %Self => constants.%Inner.facet.f78 // CHECK:STDOUT: %Inner.WithSelf.F.type => constants.%Inner.WithSelf.F.type.d96 // CHECK:STDOUT: %Inner.WithSelf.F => constants.%Inner.WithSelf.F.66d // CHECK:STDOUT: %Inner.assoc_type => constants.%Inner.assoc_type.be2 // CHECK:STDOUT: %assoc0.loc6_28.2 => constants.%assoc0.058 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @Inner.WithSelf.F(constants.%T, constants.%Inner.facet.f78) { // CHECK:STDOUT: %T => constants.%T // CHECK:STDOUT: %Inner.type => constants.%Inner.type.6ef // CHECK:STDOUT: %Self => constants.%Inner.facet.f78 // CHECK:STDOUT: %Self.binding.as_type => constants.%C.131 // CHECK:STDOUT: %pattern_type.loc6_10 => constants.%pattern_type.fe7 // CHECK:STDOUT: %.loc6_27.1 => constants.%.184 // CHECK:STDOUT: %pattern_type.loc6_24 => constants.%pattern_type.51d // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @Outer(constants.%i32) { // CHECK:STDOUT: %T.loc4_13.1 => constants.%i32 // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %Inner.type => constants.%Inner.type.94a // CHECK:STDOUT: %C => constants.%C.d3f // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @Inner(constants.%i32) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: %T => constants.%i32 // CHECK:STDOUT: %Inner.type => constants.%Inner.type.94a // CHECK:STDOUT: %Self.loc5_19.2 => constants.%Self.f19 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @C(constants.%i32) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @Inner.WithSelf(constants.%i32, constants.%Self.d55) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: %T => constants.%i32 // CHECK:STDOUT: %Inner.type => constants.%Inner.type.94a // CHECK:STDOUT: %Self => constants.%Self.d55 // CHECK:STDOUT: %Inner.WithSelf.F.type => constants.%Inner.WithSelf.F.type.74d // CHECK:STDOUT: %Inner.WithSelf.F => constants.%Inner.WithSelf.F.b36 // CHECK:STDOUT: %Inner.assoc_type => constants.%Inner.assoc_type.564 // CHECK:STDOUT: %assoc0.loc6_28.2 => constants.%assoc0.958 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @Inner.WithSelf(constants.%i32, constants.%Inner.facet.dc9) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: %T => constants.%i32 // CHECK:STDOUT: %Inner.type => constants.%Inner.type.94a // CHECK:STDOUT: %Self => constants.%Inner.facet.dc9 // CHECK:STDOUT: %Inner.WithSelf.F.type => constants.%Inner.WithSelf.F.type.6b0 // CHECK:STDOUT: %Inner.WithSelf.F => constants.%Inner.WithSelf.F.17e // CHECK:STDOUT: %Inner.assoc_type => constants.%Inner.assoc_type.564 // CHECK:STDOUT: %assoc0.loc6_28.2 => constants.%assoc0.958 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @Inner.WithSelf.F(constants.%i32, constants.%Inner.facet.dc9) { // CHECK:STDOUT: %T => constants.%i32 // CHECK:STDOUT: %Inner.type => constants.%Inner.type.94a // CHECK:STDOUT: %Self => constants.%Inner.facet.dc9 // CHECK:STDOUT: %Self.binding.as_type => constants.%D // CHECK:STDOUT: %pattern_type.loc6_10 => constants.%pattern_type.9c8 // CHECK:STDOUT: %.loc6_27.1 => constants.%.ff5 // CHECK:STDOUT: %pattern_type.loc6_24 => constants.%pattern_type.7ce // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @C.as.Inner.impl(constants.%i32) { // CHECK:STDOUT: %T => constants.%i32 // CHECK:STDOUT: %C => constants.%C.d3f // CHECK:STDOUT: %Inner.type => constants.%Inner.type.94a // CHECK:STDOUT: %Inner.impl_witness.loc10_19.2 => constants.%Inner.impl_witness.d48 // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete => constants.%complete_type.087 // CHECK:STDOUT: %C.as.Inner.impl.F.type => constants.%C.as.Inner.impl.F.type.7ce // CHECK:STDOUT: %C.as.Inner.impl.F => constants.%C.as.Inner.impl.F.356 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @Inner.WithSelf(constants.%i32, constants.%Inner.facet.ac9) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: %T => constants.%i32 // CHECK:STDOUT: %Inner.type => constants.%Inner.type.94a // CHECK:STDOUT: %Self => constants.%Inner.facet.ac9 // CHECK:STDOUT: %Inner.WithSelf.F.type => constants.%Inner.WithSelf.F.type.96b // CHECK:STDOUT: %Inner.WithSelf.F => constants.%Inner.WithSelf.F.4b0 // CHECK:STDOUT: %Inner.assoc_type => constants.%Inner.assoc_type.564 // CHECK:STDOUT: %assoc0.loc6_28.2 => constants.%assoc0.958 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @C.as.Inner.impl.F(constants.%i32) { // CHECK:STDOUT: %T => constants.%i32 // CHECK:STDOUT: %C => constants.%C.d3f // CHECK:STDOUT: %pattern_type.loc11_12 => constants.%pattern_type.129 // CHECK:STDOUT: %.loc11_26.2 => constants.%.ff5 // CHECK:STDOUT: %pattern_type.loc11_23 => constants.%pattern_type.7ce // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete.loc11_16 => constants.%complete_type.357 // CHECK:STDOUT: %require_complete.loc11_26 => constants.%complete_type.f8a // CHECK:STDOUT: %Inner.type => constants.%Inner.type.94a // CHECK:STDOUT: %require_complete.loc11_48 => constants.%complete_type.087 // CHECK:STDOUT: %Inner.assoc_type => constants.%Inner.assoc_type.564 // CHECK:STDOUT: %assoc0 => constants.%assoc0.958 // CHECK:STDOUT: %.loc11_41.1 => constants.%.c0b // CHECK:STDOUT: %Inner.lookup_impl_witness => constants.%Inner.impl_witness.d48 // CHECK:STDOUT: %Inner.facet => constants.%Inner.facet.ac9 // CHECK:STDOUT: %Inner.WithSelf.F.type => constants.%Inner.WithSelf.F.type.96b // CHECK:STDOUT: %.loc11_41.2 => constants.%.fd6 // CHECK:STDOUT: %impl.elem0.loc11_41.2 => constants.%C.as.Inner.impl.F.356 // CHECK:STDOUT: %specific_impl_fn.loc11_41.2 => constants.%C.as.Inner.impl.F.specific_fn // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @Inner.WithSelf.F(constants.%i32, constants.%Inner.facet.ac9) { // CHECK:STDOUT: %T => constants.%i32 // CHECK:STDOUT: %Inner.type => constants.%Inner.type.94a // CHECK:STDOUT: %Self => constants.%Inner.facet.ac9 // CHECK:STDOUT: %Self.binding.as_type => constants.%C.d3f // CHECK:STDOUT: %pattern_type.loc6_10 => constants.%pattern_type.129 // CHECK:STDOUT: %.loc6_27.1 => constants.%.ff5 // CHECK:STDOUT: %pattern_type.loc6_24 => constants.%pattern_type.7ce // CHECK:STDOUT: } // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/class/generic/method_deduce.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/convert.carbon // TODO: Add ranges and switch to "--dump-sem-ir-ranges=only". // EXTRA-ARGS: --dump-sem-ir-ranges=if-present // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/class/generic/method_deduce.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/class/generic/method_deduce.carbon class A {} class B {} class Class(T:! type) { fn Get(U:! type) -> (T, U) { return Get(U); } fn GetNoDeduce(x: T, U:! type) -> (T, U) { return GetNoDeduce(x, U); } } fn CallGenericMethod(c: Class(A)) -> (A, B) { return c.Get(B); } fn CallGenericMethodWithNonDeducedParam(c: Class(A)) -> (A, B) { return c.GetNoDeduce({}, B); } // CHECK:STDOUT: --- method_deduce.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %A: type = class_type @A [concrete] // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %complete_type.357: = complete_type_witness %empty_struct_type [concrete] // CHECK:STDOUT: %B: type = class_type @B [concrete] // CHECK:STDOUT: %type: type = facet_type [concrete] // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self] // CHECK:STDOUT: %pattern_type.98f: type = pattern_type type [concrete] // CHECK:STDOUT: %T: type = symbolic_binding T, 0 [symbolic] // CHECK:STDOUT: %Class.type: type = generic_class_type @Class [concrete] // CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] // CHECK:STDOUT: %Class.generic: %Class.type = struct_value () [concrete] // CHECK:STDOUT: %Class.0db: type = class_type @Class, @Class(%T) [symbolic] // CHECK:STDOUT: %U: type = symbolic_binding U, 1 [symbolic] // CHECK:STDOUT: %tuple.type.24b: type = tuple_type (type, type) [concrete] // CHECK:STDOUT: %tuple.4b9: %tuple.type.24b = tuple_value (%T, %U) [symbolic] // CHECK:STDOUT: %tuple.type.a5e: type = tuple_type (%T, %U) [symbolic] // CHECK:STDOUT: %.f18: Core.Form = init_form %tuple.type.a5e [symbolic] // CHECK:STDOUT: %pattern_type.eee: type = pattern_type %tuple.type.a5e [symbolic] // CHECK:STDOUT: %Class.Get.type.ab7: type = fn_type @Class.Get, @Class(%T) [symbolic] // CHECK:STDOUT: %Class.Get.ecd: %Class.Get.type.ab7 = struct_value () [symbolic] // CHECK:STDOUT: %pattern_type.51d: type = pattern_type %T [symbolic] // CHECK:STDOUT: %Class.GetNoDeduce.type.cf2: type = fn_type @Class.GetNoDeduce, @Class(%T) [symbolic] // CHECK:STDOUT: %Class.GetNoDeduce.1a5: %Class.GetNoDeduce.type.cf2 = struct_value () [symbolic] // CHECK:STDOUT: %require_complete.220: = require_complete_type %tuple.type.a5e [symbolic] // CHECK:STDOUT: %Class.Get.specific_fn.f51: = specific_function %Class.Get.ecd, @Class.Get(%T, %U) [symbolic] // CHECK:STDOUT: %require_complete.944: = require_complete_type %T [symbolic] // CHECK:STDOUT: %Class.GetNoDeduce.specific_fn.710: = specific_function %Class.GetNoDeduce.1a5, @Class.GetNoDeduce(%T, %U) [symbolic] // CHECK:STDOUT: %Class.802: type = class_type @Class, @Class(%A) [concrete] // CHECK:STDOUT: %pattern_type.36c: type = pattern_type %Class.802 [concrete] // CHECK:STDOUT: %tuple.5bc: %tuple.type.24b = tuple_value (%A, %B) [concrete] // CHECK:STDOUT: %tuple.type.e87: type = tuple_type (%A, %B) [concrete] // CHECK:STDOUT: %.3aa: Core.Form = init_form %tuple.type.e87 [concrete] // CHECK:STDOUT: %pattern_type.b74: type = pattern_type %tuple.type.e87 [concrete] // CHECK:STDOUT: %CallGenericMethod.type: type = fn_type @CallGenericMethod [concrete] // CHECK:STDOUT: %CallGenericMethod: %CallGenericMethod.type = struct_value () [concrete] // CHECK:STDOUT: %Class.Get.type.a01: type = fn_type @Class.Get, @Class(%A) [concrete] // CHECK:STDOUT: %Class.Get.5f3: %Class.Get.type.a01 = struct_value () [concrete] // CHECK:STDOUT: %Class.GetNoDeduce.type.902: type = fn_type @Class.GetNoDeduce, @Class(%A) [concrete] // CHECK:STDOUT: %Class.GetNoDeduce.472: %Class.GetNoDeduce.type.902 = struct_value () [concrete] // CHECK:STDOUT: %Class.Get.specific_fn.54d: = specific_function %Class.Get.5f3, @Class.Get(%A, %B) [concrete] // CHECK:STDOUT: %CallGenericMethodWithNonDeducedParam.type: type = fn_type @CallGenericMethodWithNonDeducedParam [concrete] // CHECK:STDOUT: %CallGenericMethodWithNonDeducedParam: %CallGenericMethodWithNonDeducedParam.type = struct_value () [concrete] // CHECK:STDOUT: %empty_struct: %empty_struct_type = struct_value () [concrete] // CHECK:STDOUT: %pattern_type.1ab: type = pattern_type %A [concrete] // CHECK:STDOUT: %Class.GetNoDeduce.specific_fn.83b: = specific_function %Class.GetNoDeduce.472, @Class.GetNoDeduce(%A, %B) [concrete] // CHECK:STDOUT: %A.val: %A = struct_value () [concrete] // CHECK:STDOUT: %Destroy.type: type = facet_type <@Destroy> [concrete] // CHECK:STDOUT: %Destroy.Op.type: type = fn_type @Destroy.Op [concrete] // CHECK:STDOUT: %Destroy.Op: %Destroy.Op.type = struct_value () [concrete] // CHECK:STDOUT: %complete_type.f71: = complete_type_witness %tuple.type.e87 [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: .Destroy = %Core.Destroy // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: %Core.Destroy: type = import_ref Core//prelude/parts/destroy, Destroy, loaded [concrete = constants.%Destroy.type] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .A = %A.decl // CHECK:STDOUT: .B = %B.decl // CHECK:STDOUT: .Class = %Class.decl // CHECK:STDOUT: .CallGenericMethod = %CallGenericMethod.decl // CHECK:STDOUT: .CallGenericMethodWithNonDeducedParam = %CallGenericMethodWithNonDeducedParam.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %A.decl: type = class_decl @A [concrete = constants.%A] {} {} // CHECK:STDOUT: %B.decl: type = class_decl @B [concrete = constants.%B] {} {} // CHECK:STDOUT: %Class.decl: %Class.type = class_decl @Class [concrete = constants.%Class.generic] { // CHECK:STDOUT: %T.patt: %pattern_type.98f = symbolic_binding_pattern T, 0 [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %.loc18_17.1: type = splice_block %.loc18_17.2 [concrete = type] { // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %.loc18_17.2: type = type_literal type [concrete = type] // CHECK:STDOUT: } // CHECK:STDOUT: %T.loc18_13.2: type = symbolic_binding T, 0 [symbolic = %T.loc18_13.1 (constants.%T)] // CHECK:STDOUT: } // CHECK:STDOUT: %CallGenericMethod.decl: %CallGenericMethod.type = fn_decl @CallGenericMethod [concrete = constants.%CallGenericMethod] { // CHECK:STDOUT: %c.patt: %pattern_type.36c = value_binding_pattern c [concrete] // CHECK:STDOUT: %c.param_patt: %pattern_type.36c = value_param_pattern %c.patt [concrete] // CHECK:STDOUT: %return.patt: %pattern_type.b74 = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: %pattern_type.b74 = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %A.ref.loc23_39: type = name_ref A, file.%A.decl [concrete = constants.%A] // CHECK:STDOUT: %B.ref.loc23: type = name_ref B, file.%B.decl [concrete = constants.%B] // CHECK:STDOUT: %.loc23_43.2: %tuple.type.24b = tuple_literal (%A.ref.loc23_39, %B.ref.loc23) [concrete = constants.%tuple.5bc] // CHECK:STDOUT: %.loc23_43.3: type = converted %.loc23_43.2, constants.%tuple.type.e87 [concrete = constants.%tuple.type.e87] // CHECK:STDOUT: %.loc23_43.4: Core.Form = init_form %.loc23_43.3 [concrete = constants.%.3aa] // CHECK:STDOUT: %c.param: %Class.802 = value_param call_param0 // CHECK:STDOUT: %.loc23_32: type = splice_block %Class [concrete = constants.%Class.802] { // CHECK:STDOUT: %Class.ref: %Class.type = name_ref Class, file.%Class.decl [concrete = constants.%Class.generic] // CHECK:STDOUT: %A.ref.loc23_31: type = name_ref A, file.%A.decl [concrete = constants.%A] // CHECK:STDOUT: %Class: type = class_type @Class, @Class(constants.%A) [concrete = constants.%Class.802] // CHECK:STDOUT: } // CHECK:STDOUT: %c: %Class.802 = value_binding c, %c.param // CHECK:STDOUT: %return.param: ref %tuple.type.e87 = out_param call_param1 // CHECK:STDOUT: %return: ref %tuple.type.e87 = return_slot %return.param // CHECK:STDOUT: } // CHECK:STDOUT: %CallGenericMethodWithNonDeducedParam.decl: %CallGenericMethodWithNonDeducedParam.type = fn_decl @CallGenericMethodWithNonDeducedParam [concrete = constants.%CallGenericMethodWithNonDeducedParam] { // CHECK:STDOUT: %c.patt: %pattern_type.36c = value_binding_pattern c [concrete] // CHECK:STDOUT: %c.param_patt: %pattern_type.36c = value_param_pattern %c.patt [concrete] // CHECK:STDOUT: %return.patt: %pattern_type.b74 = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: %pattern_type.b74 = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %A.ref.loc27_58: type = name_ref A, file.%A.decl [concrete = constants.%A] // CHECK:STDOUT: %B.ref.loc27: type = name_ref B, file.%B.decl [concrete = constants.%B] // CHECK:STDOUT: %.loc27_62.2: %tuple.type.24b = tuple_literal (%A.ref.loc27_58, %B.ref.loc27) [concrete = constants.%tuple.5bc] // CHECK:STDOUT: %.loc27_62.3: type = converted %.loc27_62.2, constants.%tuple.type.e87 [concrete = constants.%tuple.type.e87] // CHECK:STDOUT: %.loc27_62.4: Core.Form = init_form %.loc27_62.3 [concrete = constants.%.3aa] // CHECK:STDOUT: %c.param: %Class.802 = value_param call_param0 // CHECK:STDOUT: %.loc27_51: type = splice_block %Class [concrete = constants.%Class.802] { // CHECK:STDOUT: %Class.ref: %Class.type = name_ref Class, file.%Class.decl [concrete = constants.%Class.generic] // CHECK:STDOUT: %A.ref.loc27_50: type = name_ref A, file.%A.decl [concrete = constants.%A] // CHECK:STDOUT: %Class: type = class_type @Class, @Class(constants.%A) [concrete = constants.%Class.802] // CHECK:STDOUT: } // CHECK:STDOUT: %c: %Class.802 = value_binding c, %c.param // CHECK:STDOUT: %return.param: ref %tuple.type.e87 = out_param call_param1 // CHECK:STDOUT: %return: ref %tuple.type.e87 = return_slot %return.param // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @A { // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type.357] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%A // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @B { // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type.357] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%B // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic class @Class(%T.loc18_13.2: type) { // CHECK:STDOUT: %T.loc18_13.1: type = symbolic_binding T, 0 [symbolic = %T.loc18_13.1 (constants.%T)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %Class.Get.type: type = fn_type @Class.Get, @Class(%T.loc18_13.1) [symbolic = %Class.Get.type (constants.%Class.Get.type.ab7)] // CHECK:STDOUT: %Class.Get: @Class.%Class.Get.type (%Class.Get.type.ab7) = struct_value () [symbolic = %Class.Get (constants.%Class.Get.ecd)] // CHECK:STDOUT: %Class.GetNoDeduce.type: type = fn_type @Class.GetNoDeduce, @Class(%T.loc18_13.1) [symbolic = %Class.GetNoDeduce.type (constants.%Class.GetNoDeduce.type.cf2)] // CHECK:STDOUT: %Class.GetNoDeduce: @Class.%Class.GetNoDeduce.type (%Class.GetNoDeduce.type.cf2) = struct_value () [symbolic = %Class.GetNoDeduce (constants.%Class.GetNoDeduce.1a5)] // CHECK:STDOUT: // CHECK:STDOUT: class { // CHECK:STDOUT: %Class.Get.decl: @Class.%Class.Get.type (%Class.Get.type.ab7) = fn_decl @Class.Get [symbolic = @Class.%Class.Get (constants.%Class.Get.ecd)] { // CHECK:STDOUT: %U.patt: %pattern_type.98f = symbolic_binding_pattern U, 1 [concrete] // CHECK:STDOUT: %return.patt: @Class.Get.%pattern_type (%pattern_type.eee) = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: @Class.Get.%pattern_type (%pattern_type.eee) = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %T.ref: type = name_ref T, @Class.%T.loc18_13.2 [symbolic = %T (constants.%T)] // CHECK:STDOUT: %U.ref.loc19_27: type = name_ref U, %U.loc19_10.2 [symbolic = %U.loc19_10.1 (constants.%U)] // CHECK:STDOUT: %.loc19_28.3: %tuple.type.24b = tuple_literal (%T.ref, %U.ref.loc19_27) [symbolic = %tuple (constants.%tuple.4b9)] // CHECK:STDOUT: %.loc19_28.4: type = converted %.loc19_28.3, constants.%tuple.type.a5e [symbolic = %tuple.type (constants.%tuple.type.a5e)] // CHECK:STDOUT: %.loc19_28.5: Core.Form = init_form %.loc19_28.4 [symbolic = %.loc19_28.2 (constants.%.f18)] // CHECK:STDOUT: %.loc19_14.1: type = splice_block %.loc19_14.2 [concrete = type] { // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %.loc19_14.2: type = type_literal type [concrete = type] // CHECK:STDOUT: } // CHECK:STDOUT: %U.loc19_10.2: type = symbolic_binding U, 1 [symbolic = %U.loc19_10.1 (constants.%U)] // CHECK:STDOUT: %return.param: ref @Class.Get.%tuple.type (%tuple.type.a5e) = out_param call_param0 // CHECK:STDOUT: %return: ref @Class.Get.%tuple.type (%tuple.type.a5e) = return_slot %return.param // CHECK:STDOUT: } // CHECK:STDOUT: %Class.GetNoDeduce.decl: @Class.%Class.GetNoDeduce.type (%Class.GetNoDeduce.type.cf2) = fn_decl @Class.GetNoDeduce [symbolic = @Class.%Class.GetNoDeduce (constants.%Class.GetNoDeduce.1a5)] { // CHECK:STDOUT: %x.patt: @Class.GetNoDeduce.%pattern_type.loc20_18 (%pattern_type.51d) = value_binding_pattern x [concrete] // CHECK:STDOUT: %x.param_patt: @Class.GetNoDeduce.%pattern_type.loc20_18 (%pattern_type.51d) = value_param_pattern %x.patt [concrete] // CHECK:STDOUT: %U.patt: %pattern_type.98f = symbolic_binding_pattern U, 1 [concrete] // CHECK:STDOUT: %return.patt: @Class.GetNoDeduce.%pattern_type.loc20_34 (%pattern_type.eee) = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: @Class.GetNoDeduce.%pattern_type.loc20_34 (%pattern_type.eee) = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %T.ref.loc20_38: type = name_ref T, @Class.%T.loc18_13.2 [symbolic = %T (constants.%T)] // CHECK:STDOUT: %U.ref.loc20_41: type = name_ref U, %U.loc20_24.2 [symbolic = %U.loc20_24.1 (constants.%U)] // CHECK:STDOUT: %.loc20_42.3: %tuple.type.24b = tuple_literal (%T.ref.loc20_38, %U.ref.loc20_41) [symbolic = %tuple (constants.%tuple.4b9)] // CHECK:STDOUT: %.loc20_42.4: type = converted %.loc20_42.3, constants.%tuple.type.a5e [symbolic = %tuple.type (constants.%tuple.type.a5e)] // CHECK:STDOUT: %.loc20_42.5: Core.Form = init_form %.loc20_42.4 [symbolic = %.loc20_42.2 (constants.%.f18)] // CHECK:STDOUT: %x.param: @Class.GetNoDeduce.%T (%T) = value_param call_param0 // CHECK:STDOUT: %T.ref.loc20_21: type = name_ref T, @Class.%T.loc18_13.2 [symbolic = %T (constants.%T)] // CHECK:STDOUT: %x: @Class.GetNoDeduce.%T (%T) = value_binding x, %x.param // CHECK:STDOUT: %.loc20_28.1: type = splice_block %.loc20_28.2 [concrete = type] { // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %.loc20_28.2: type = type_literal type [concrete = type] // CHECK:STDOUT: } // CHECK:STDOUT: %U.loc20_24.2: type = symbolic_binding U, 1 [symbolic = %U.loc20_24.1 (constants.%U)] // CHECK:STDOUT: %return.param: ref @Class.GetNoDeduce.%tuple.type (%tuple.type.a5e) = out_param call_param1 // CHECK:STDOUT: %return: ref @Class.GetNoDeduce.%tuple.type (%tuple.type.a5e) = return_slot %return.param // CHECK:STDOUT: } // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type.357] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%Class.0db // CHECK:STDOUT: .T = // CHECK:STDOUT: .Get = %Class.Get.decl // CHECK:STDOUT: .GetNoDeduce = %Class.GetNoDeduce.decl // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic fn @Class.Get(@Class.%T.loc18_13.2: type, %U.loc19_10.2: type) { // CHECK:STDOUT: %U.loc19_10.1: type = symbolic_binding U, 1 [symbolic = %U.loc19_10.1 (constants.%U)] // CHECK:STDOUT: %T: type = symbolic_binding T, 0 [symbolic = %T (constants.%T)] // CHECK:STDOUT: %tuple: %tuple.type.24b = tuple_value (%T, %U.loc19_10.1) [symbolic = %tuple (constants.%tuple.4b9)] // CHECK:STDOUT: %tuple.type: type = tuple_type (%T, %U.loc19_10.1) [symbolic = %tuple.type (constants.%tuple.type.a5e)] // CHECK:STDOUT: %.loc19_28.2: Core.Form = init_form %tuple.type [symbolic = %.loc19_28.2 (constants.%.f18)] // CHECK:STDOUT: %pattern_type: type = pattern_type %tuple.type [symbolic = %pattern_type (constants.%pattern_type.eee)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete: = require_complete_type %tuple.type [symbolic = %require_complete (constants.%require_complete.220)] // CHECK:STDOUT: %Class.Get.type: type = fn_type @Class.Get, @Class(%T) [symbolic = %Class.Get.type (constants.%Class.Get.type.ab7)] // CHECK:STDOUT: %Class.Get: @Class.Get.%Class.Get.type (%Class.Get.type.ab7) = struct_value () [symbolic = %Class.Get (constants.%Class.Get.ecd)] // CHECK:STDOUT: %Class.Get.specific_fn.loc19_39.2: = specific_function %Class.Get, @Class.Get(%T, %U.loc19_10.1) [symbolic = %Class.Get.specific_fn.loc19_39.2 (constants.%Class.Get.specific_fn.f51)] // CHECK:STDOUT: // CHECK:STDOUT: fn() -> out %return.param: @Class.Get.%tuple.type (%tuple.type.a5e) { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %.loc19_39: @Class.Get.%Class.Get.type (%Class.Get.type.ab7) = specific_constant @Class.%Class.Get.decl, @Class(constants.%T) [symbolic = %Class.Get (constants.%Class.Get.ecd)] // CHECK:STDOUT: %Get.ref: @Class.Get.%Class.Get.type (%Class.Get.type.ab7) = name_ref Get, %.loc19_39 [symbolic = %Class.Get (constants.%Class.Get.ecd)] // CHECK:STDOUT: %U.ref.loc19_43: type = name_ref U, %U.loc19_10.2 [symbolic = %U.loc19_10.1 (constants.%U)] // CHECK:STDOUT: %Class.Get.specific_fn.loc19_39.1: = specific_function %Get.ref, @Class.Get(constants.%T, constants.%U) [symbolic = %Class.Get.specific_fn.loc19_39.2 (constants.%Class.Get.specific_fn.f51)] // CHECK:STDOUT: %.loc19_28.1: ref @Class.Get.%tuple.type (%tuple.type.a5e) = splice_block %return.param {} // CHECK:STDOUT: %Class.Get.call: init @Class.Get.%tuple.type (%tuple.type.a5e) to %.loc19_28.1 = call %Class.Get.specific_fn.loc19_39.1() // CHECK:STDOUT: return %Class.Get.call to %return.param // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic fn @Class.GetNoDeduce(@Class.%T.loc18_13.2: type, %U.loc20_24.2: type) { // CHECK:STDOUT: %T: type = symbolic_binding T, 0 [symbolic = %T (constants.%T)] // CHECK:STDOUT: %pattern_type.loc20_18: type = pattern_type %T [symbolic = %pattern_type.loc20_18 (constants.%pattern_type.51d)] // CHECK:STDOUT: %U.loc20_24.1: type = symbolic_binding U, 1 [symbolic = %U.loc20_24.1 (constants.%U)] // CHECK:STDOUT: %tuple: %tuple.type.24b = tuple_value (%T, %U.loc20_24.1) [symbolic = %tuple (constants.%tuple.4b9)] // CHECK:STDOUT: %tuple.type: type = tuple_type (%T, %U.loc20_24.1) [symbolic = %tuple.type (constants.%tuple.type.a5e)] // CHECK:STDOUT: %.loc20_42.2: Core.Form = init_form %tuple.type [symbolic = %.loc20_42.2 (constants.%.f18)] // CHECK:STDOUT: %pattern_type.loc20_34: type = pattern_type %tuple.type [symbolic = %pattern_type.loc20_34 (constants.%pattern_type.eee)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete: = require_complete_type %T [symbolic = %require_complete (constants.%require_complete.944)] // CHECK:STDOUT: %Class.GetNoDeduce.type: type = fn_type @Class.GetNoDeduce, @Class(%T) [symbolic = %Class.GetNoDeduce.type (constants.%Class.GetNoDeduce.type.cf2)] // CHECK:STDOUT: %Class.GetNoDeduce: @Class.GetNoDeduce.%Class.GetNoDeduce.type (%Class.GetNoDeduce.type.cf2) = struct_value () [symbolic = %Class.GetNoDeduce (constants.%Class.GetNoDeduce.1a5)] // CHECK:STDOUT: %Class.GetNoDeduce.specific_fn.loc20_53.2: = specific_function %Class.GetNoDeduce, @Class.GetNoDeduce(%T, %U.loc20_24.1) [symbolic = %Class.GetNoDeduce.specific_fn.loc20_53.2 (constants.%Class.GetNoDeduce.specific_fn.710)] // CHECK:STDOUT: // CHECK:STDOUT: fn(%x.param: @Class.GetNoDeduce.%T (%T)) -> out %return.param: @Class.GetNoDeduce.%tuple.type (%tuple.type.a5e) { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %.loc20_53: @Class.GetNoDeduce.%Class.GetNoDeduce.type (%Class.GetNoDeduce.type.cf2) = specific_constant @Class.%Class.GetNoDeduce.decl, @Class(constants.%T) [symbolic = %Class.GetNoDeduce (constants.%Class.GetNoDeduce.1a5)] // CHECK:STDOUT: %GetNoDeduce.ref: @Class.GetNoDeduce.%Class.GetNoDeduce.type (%Class.GetNoDeduce.type.cf2) = name_ref GetNoDeduce, %.loc20_53 [symbolic = %Class.GetNoDeduce (constants.%Class.GetNoDeduce.1a5)] // CHECK:STDOUT: %x.ref: @Class.GetNoDeduce.%T (%T) = name_ref x, %x // CHECK:STDOUT: %U.ref.loc20_68: type = name_ref U, %U.loc20_24.2 [symbolic = %U.loc20_24.1 (constants.%U)] // CHECK:STDOUT: %Class.GetNoDeduce.specific_fn.loc20_53.1: = specific_function %GetNoDeduce.ref, @Class.GetNoDeduce(constants.%T, constants.%U) [symbolic = %Class.GetNoDeduce.specific_fn.loc20_53.2 (constants.%Class.GetNoDeduce.specific_fn.710)] // CHECK:STDOUT: %.loc20_42.1: ref @Class.GetNoDeduce.%tuple.type (%tuple.type.a5e) = splice_block %return.param {} // CHECK:STDOUT: %Class.GetNoDeduce.call: init @Class.GetNoDeduce.%tuple.type (%tuple.type.a5e) to %.loc20_42.1 = call %Class.GetNoDeduce.specific_fn.loc20_53.1(%x.ref) // CHECK:STDOUT: return %Class.GetNoDeduce.call to %return.param // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @CallGenericMethod(%c.param: %Class.802) -> out %return.param: %tuple.type.e87 { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %c.ref: %Class.802 = name_ref c, %c // CHECK:STDOUT: %.loc24: %Class.Get.type.a01 = specific_constant @Class.%Class.Get.decl, @Class(constants.%A) [concrete = constants.%Class.Get.5f3] // CHECK:STDOUT: %Get.ref: %Class.Get.type.a01 = name_ref Get, %.loc24 [concrete = constants.%Class.Get.5f3] // CHECK:STDOUT: %B.ref.loc24: type = name_ref B, file.%B.decl [concrete = constants.%B] // CHECK:STDOUT: %Class.Get.specific_fn: = specific_function %Get.ref, @Class.Get(constants.%A, constants.%B) [concrete = constants.%Class.Get.specific_fn.54d] // CHECK:STDOUT: %.loc23_43.1: ref %tuple.type.e87 = splice_block %return.param {} // CHECK:STDOUT: %Class.Get.call: init %tuple.type.e87 to %.loc23_43.1 = call %Class.Get.specific_fn() // CHECK:STDOUT: return %Class.Get.call to %return.param // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @CallGenericMethodWithNonDeducedParam(%c.param: %Class.802) -> out %return.param: %tuple.type.e87 { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %c.ref: %Class.802 = name_ref c, %c // CHECK:STDOUT: %.loc28_11: %Class.GetNoDeduce.type.902 = specific_constant @Class.%Class.GetNoDeduce.decl, @Class(constants.%A) [concrete = constants.%Class.GetNoDeduce.472] // CHECK:STDOUT: %GetNoDeduce.ref: %Class.GetNoDeduce.type.902 = name_ref GetNoDeduce, %.loc28_11 [concrete = constants.%Class.GetNoDeduce.472] // CHECK:STDOUT: %.loc28_25.1: %empty_struct_type = struct_literal () [concrete = constants.%empty_struct] // CHECK:STDOUT: %B.ref.loc28: type = name_ref B, file.%B.decl [concrete = constants.%B] // CHECK:STDOUT: %Class.GetNoDeduce.specific_fn: = specific_function %GetNoDeduce.ref, @Class.GetNoDeduce(constants.%A, constants.%B) [concrete = constants.%Class.GetNoDeduce.specific_fn.83b] // CHECK:STDOUT: %.loc27_62.1: ref %tuple.type.e87 = splice_block %return.param {} // CHECK:STDOUT: %.loc28_25.2: ref %A = temporary_storage // CHECK:STDOUT: %.loc28_25.3: init %A to %.loc28_25.2 = class_init () [concrete = constants.%A.val] // CHECK:STDOUT: %.loc28_25.4: init %A = converted %.loc28_25.1, %.loc28_25.3 [concrete = constants.%A.val] // CHECK:STDOUT: %.loc28_25.5: ref %A = temporary %.loc28_25.2, %.loc28_25.4 // CHECK:STDOUT: %.loc28_25.6: %A = acquire_value %.loc28_25.5 // CHECK:STDOUT: %Class.GetNoDeduce.call: init %tuple.type.e87 to %.loc27_62.1 = call %Class.GetNoDeduce.specific_fn(%.loc28_25.6) // CHECK:STDOUT: %Destroy.Op.bound: = bound_method %.loc28_25.5, constants.%Destroy.Op // CHECK:STDOUT: %Destroy.Op.call: init %empty_tuple.type = call %Destroy.Op.bound(%.loc28_25.5) // CHECK:STDOUT: return %Class.GetNoDeduce.call to %return.param // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @Destroy.Op(%self.param: ref %A) = "no_op"; // CHECK:STDOUT: // CHECK:STDOUT: specific @Class(constants.%T) { // CHECK:STDOUT: %T.loc18_13.1 => constants.%T // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %Class.Get.type => constants.%Class.Get.type.ab7 // CHECK:STDOUT: %Class.Get => constants.%Class.Get.ecd // CHECK:STDOUT: %Class.GetNoDeduce.type => constants.%Class.GetNoDeduce.type.cf2 // CHECK:STDOUT: %Class.GetNoDeduce => constants.%Class.GetNoDeduce.1a5 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @Class.Get(constants.%T, constants.%U) { // CHECK:STDOUT: %U.loc19_10.1 => constants.%U // CHECK:STDOUT: %T => constants.%T // CHECK:STDOUT: %tuple => constants.%tuple.4b9 // CHECK:STDOUT: %tuple.type => constants.%tuple.type.a5e // CHECK:STDOUT: %.loc19_28.2 => constants.%.f18 // CHECK:STDOUT: %pattern_type => constants.%pattern_type.eee // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete => constants.%require_complete.220 // CHECK:STDOUT: %Class.Get.type => constants.%Class.Get.type.ab7 // CHECK:STDOUT: %Class.Get => constants.%Class.Get.ecd // CHECK:STDOUT: %Class.Get.specific_fn.loc19_39.2 => constants.%Class.Get.specific_fn.f51 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @Class.GetNoDeduce(constants.%T, constants.%U) { // CHECK:STDOUT: %T => constants.%T // CHECK:STDOUT: %pattern_type.loc20_18 => constants.%pattern_type.51d // CHECK:STDOUT: %U.loc20_24.1 => constants.%U // CHECK:STDOUT: %tuple => constants.%tuple.4b9 // CHECK:STDOUT: %tuple.type => constants.%tuple.type.a5e // CHECK:STDOUT: %.loc20_42.2 => constants.%.f18 // CHECK:STDOUT: %pattern_type.loc20_34 => constants.%pattern_type.eee // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete => constants.%require_complete.944 // CHECK:STDOUT: %Class.GetNoDeduce.type => constants.%Class.GetNoDeduce.type.cf2 // CHECK:STDOUT: %Class.GetNoDeduce => constants.%Class.GetNoDeduce.1a5 // CHECK:STDOUT: %Class.GetNoDeduce.specific_fn.loc20_53.2 => constants.%Class.GetNoDeduce.specific_fn.710 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @Class(constants.%A) { // CHECK:STDOUT: %T.loc18_13.1 => constants.%A // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %Class.Get.type => constants.%Class.Get.type.a01 // CHECK:STDOUT: %Class.Get => constants.%Class.Get.5f3 // CHECK:STDOUT: %Class.GetNoDeduce.type => constants.%Class.GetNoDeduce.type.902 // CHECK:STDOUT: %Class.GetNoDeduce => constants.%Class.GetNoDeduce.472 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @Class.Get(constants.%A, constants.%B) { // CHECK:STDOUT: %U.loc19_10.1 => constants.%B // CHECK:STDOUT: %T => constants.%A // CHECK:STDOUT: %tuple => constants.%tuple.5bc // CHECK:STDOUT: %tuple.type => constants.%tuple.type.e87 // CHECK:STDOUT: %.loc19_28.2 => constants.%.3aa // CHECK:STDOUT: %pattern_type => constants.%pattern_type.b74 // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete => constants.%complete_type.f71 // CHECK:STDOUT: %Class.Get.type => constants.%Class.Get.type.a01 // CHECK:STDOUT: %Class.Get => constants.%Class.Get.5f3 // CHECK:STDOUT: %Class.Get.specific_fn.loc19_39.2 => constants.%Class.Get.specific_fn.54d // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @Class.GetNoDeduce(constants.%A, constants.%B) { // CHECK:STDOUT: %T => constants.%A // CHECK:STDOUT: %pattern_type.loc20_18 => constants.%pattern_type.1ab // CHECK:STDOUT: %U.loc20_24.1 => constants.%B // CHECK:STDOUT: %tuple => constants.%tuple.5bc // CHECK:STDOUT: %tuple.type => constants.%tuple.type.e87 // CHECK:STDOUT: %.loc20_42.2 => constants.%.3aa // CHECK:STDOUT: %pattern_type.loc20_34 => constants.%pattern_type.b74 // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete => constants.%complete_type.357 // CHECK:STDOUT: %Class.GetNoDeduce.type => constants.%Class.GetNoDeduce.type.902 // CHECK:STDOUT: %Class.GetNoDeduce => constants.%Class.GetNoDeduce.472 // CHECK:STDOUT: %Class.GetNoDeduce.specific_fn.loc20_53.2 => constants.%Class.GetNoDeduce.specific_fn.83b // CHECK:STDOUT: } // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/class/generic/redeclare.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/convert.carbon // TODO: Add ranges and switch to "--dump-sem-ir-ranges=only". // EXTRA-ARGS: --dump-sem-ir-ranges=if-present // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/class/generic/redeclare.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/class/generic/redeclare.carbon // --- valid.carbon library "[[@TEST_NAME]]"; class Generic(T:! type); class Generic(T:! type) { } // --- fail_mismatch_param_list.carbon library "[[@TEST_NAME]]"; class A; // CHECK:STDERR: fail_mismatch_param_list.carbon:[[@LINE+7]]:1: error: redeclaration differs because of parameter list [RedeclParamListDiffers] // CHECK:STDERR: class A(T:! type) {} // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: fail_mismatch_param_list.carbon:[[@LINE-4]]:1: note: previously declared without parameter list [RedeclParamListPrevious] // CHECK:STDERR: class A; // CHECK:STDERR: ^~~~~~~~ // CHECK:STDERR: class A(T:! type) {} // --- fail_mismatch_implicit_param_list.carbon library "[[@TEST_NAME]]"; class A {} class B(N:! A); // CHECK:STDERR: fail_mismatch_implicit_param_list.carbon:[[@LINE+7]]:1: error: redeclaration differs because of implicit parameter list [RedeclParamListDiffers] // CHECK:STDERR: class B[T:! type](N:! T) {} // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: fail_mismatch_implicit_param_list.carbon:[[@LINE-4]]:1: note: previously declared without implicit parameter list [RedeclParamListPrevious] // CHECK:STDERR: class B(N:! A); // CHECK:STDERR: ^~~~~~~~~~~~~~~ // CHECK:STDERR: class B[T:! type](N:! T) {} // --- fail_mismatch_param_count.carbon library "[[@TEST_NAME]]"; class A {} class C(T:! type); // CHECK:STDERR: fail_mismatch_param_count.carbon:[[@LINE+7]]:1: error: redeclaration differs because of parameter count of 2 [RedeclParamCountDiffers] // CHECK:STDERR: class C(T:! type, U:! A) {} // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: fail_mismatch_param_count.carbon:[[@LINE-4]]:1: note: previously declared with parameter count of 1 [RedeclParamCountPrevious] // CHECK:STDERR: class C(T:! type); // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~ // CHECK:STDERR: class C(T:! type, U:! A) {} // --- fail_mismatch_param_type.carbon library "[[@TEST_NAME]]"; class A {} class D(T:! type); // CHECK:STDERR: fail_mismatch_param_type.carbon:[[@LINE+7]]:9: error: type `` of parameter 1 in redeclaration differs from previous parameter type `` [RedeclParamDiffersType] // CHECK:STDERR: class D(T:! A) {} // CHECK:STDERR: ^ // CHECK:STDERR: fail_mismatch_param_type.carbon:[[@LINE-4]]:9: note: previous declaration's corresponding parameter here [RedeclParamPrevious] // CHECK:STDERR: class D(T:! type); // CHECK:STDERR: ^ // CHECK:STDERR: class D(T:! A) {} // --- fail_mismatch_param_name.carbon library "[[@TEST_NAME]]"; class E(T:! type); // CHECK:STDERR: fail_mismatch_param_name.carbon:[[@LINE+7]]:9: error: redeclaration differs at parameter 1 [RedeclParamDiffers] // CHECK:STDERR: class E(U:! type) {} // CHECK:STDERR: ^ // CHECK:STDERR: fail_mismatch_param_name.carbon:[[@LINE-4]]:9: note: previous declaration's corresponding parameter here [RedeclParamPrevious] // CHECK:STDERR: class E(T:! type); // CHECK:STDERR: ^ // CHECK:STDERR: class E(U:! type) {} // CHECK:STDOUT: --- valid.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %type: type = facet_type [concrete] // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self] // CHECK:STDOUT: %pattern_type: type = pattern_type type [concrete] // CHECK:STDOUT: %T: type = symbolic_binding T, 0 [symbolic] // CHECK:STDOUT: %Generic.type: type = generic_class_type @Generic [concrete] // CHECK:STDOUT: %Generic.generic: %Generic.type = struct_value () [concrete] // CHECK:STDOUT: %Generic: type = class_type @Generic, @Generic(%T) [symbolic] // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %complete_type: = complete_type_witness %empty_struct_type [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .Generic = %Generic.decl.loc4 // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %Generic.decl.loc4: %Generic.type = class_decl @Generic [concrete = constants.%Generic.generic] { // CHECK:STDOUT: %T.patt: %pattern_type = symbolic_binding_pattern T, 0 [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %.loc4_19.1: type = splice_block %.loc4_19.2 [concrete = type] { // CHECK:STDOUT: %.Self.2: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %.loc4_19.2: type = type_literal type [concrete = type] // CHECK:STDOUT: } // CHECK:STDOUT: %T.loc4_15.2: type = symbolic_binding T, 0 [symbolic = %T.loc4_15.1 (constants.%T)] // CHECK:STDOUT: } // CHECK:STDOUT: %Generic.decl.loc6: %Generic.type = class_decl @Generic [concrete = constants.%Generic.generic] { // CHECK:STDOUT: %T.patt: %pattern_type = symbolic_binding_pattern T, 0 [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %.loc6_19.1: type = splice_block %.loc6_19.2 [concrete = type] { // CHECK:STDOUT: %.Self.1: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %.loc6_19.2: type = type_literal type [concrete = type] // CHECK:STDOUT: } // CHECK:STDOUT: %T.loc6: type = symbolic_binding T, 0 [symbolic = %T.loc4_15.1 (constants.%T)] // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic class @Generic(%T.loc4_15.2: type) { // CHECK:STDOUT: %T.loc4_15.1: type = symbolic_binding T, 0 [symbolic = %T.loc4_15.1 (constants.%T)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: // CHECK:STDOUT: class { // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%Generic // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @Generic(constants.%T) { // CHECK:STDOUT: %T.loc4_15.1 => constants.%T // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- fail_mismatch_param_list.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %A.f82: type = class_type @A.loc4 [concrete] // CHECK:STDOUT: %type: type = facet_type [concrete] // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self] // CHECK:STDOUT: %pattern_type: type = pattern_type type [concrete] // CHECK:STDOUT: %T: type = symbolic_binding T, 0 [symbolic] // CHECK:STDOUT: %A.type: type = generic_class_type @A.loc12 [concrete] // CHECK:STDOUT: %A.generic: %A.type = struct_value () [concrete] // CHECK:STDOUT: %A.95c: type = class_type @A.loc12, @A.loc12(%T) [symbolic] // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %complete_type: = complete_type_witness %empty_struct_type [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .A = %A.decl.loc4 // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %A.decl.loc4: type = class_decl @A.loc4 [concrete = constants.%A.f82] {} {} // CHECK:STDOUT: %A.decl.loc12: %A.type = class_decl @A.loc12 [concrete = constants.%A.generic] { // CHECK:STDOUT: %T.patt: %pattern_type = symbolic_binding_pattern T, 0 [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %.loc12_13.1: type = splice_block %.loc12_13.2 [concrete = type] { // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %.loc12_13.2: type = type_literal type [concrete = type] // CHECK:STDOUT: } // CHECK:STDOUT: %T.loc12_9.2: type = symbolic_binding T, 0 [symbolic = %T.loc12_9.1 (constants.%T)] // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @A.loc4; // CHECK:STDOUT: // CHECK:STDOUT: generic class @A.loc12(%T.loc12_9.2: type) { // CHECK:STDOUT: %T.loc12_9.1: type = symbolic_binding T, 0 [symbolic = %T.loc12_9.1 (constants.%T)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: // CHECK:STDOUT: class { // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%A.95c // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @A.loc12(constants.%T) { // CHECK:STDOUT: %T.loc12_9.1 => constants.%T // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- fail_mismatch_implicit_param_list.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %A: type = class_type @A [concrete] // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %complete_type: = complete_type_witness %empty_struct_type [concrete] // CHECK:STDOUT: %type: type = facet_type [concrete] // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self] // CHECK:STDOUT: %pattern_type.1ab: type = pattern_type %A [concrete] // CHECK:STDOUT: %N.bad: %A = symbolic_binding N, 0 [symbolic] // CHECK:STDOUT: %B.type.f7e62c.1: type = generic_class_type @B.loc6 [concrete] // CHECK:STDOUT: %B.generic.8bc1c8.1: %B.type.f7e62c.1 = struct_value () [concrete] // CHECK:STDOUT: %pattern_type.98f: type = pattern_type type [concrete] // CHECK:STDOUT: %T: type = symbolic_binding T, 0 [symbolic] // CHECK:STDOUT: %pattern_type.51d: type = pattern_type %T [symbolic] // CHECK:STDOUT: %N.bd9: %T = symbolic_binding N, 1 [symbolic] // CHECK:STDOUT: %B.type.f7e62c.2: type = generic_class_type @B.loc14 [concrete] // CHECK:STDOUT: %B.generic.8bc1c8.2: %B.type.f7e62c.2 = struct_value () [concrete] // CHECK:STDOUT: %B.87e: type = class_type @B.loc14, @B.loc14(%T, %N.bd9) [symbolic] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .A = %A.decl // CHECK:STDOUT: .B = %B.decl.loc6 // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %A.decl: type = class_decl @A [concrete = constants.%A] {} {} // CHECK:STDOUT: %B.decl.loc6: %B.type.f7e62c.1 = class_decl @B.loc6 [concrete = constants.%B.generic.8bc1c8.1] { // CHECK:STDOUT: %N.patt: %pattern_type.1ab = symbolic_binding_pattern N, 0 [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %.loc6: type = splice_block %A.ref [concrete = constants.%A] { // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %A.ref: type = name_ref A, file.%A.decl [concrete = constants.%A] // CHECK:STDOUT: } // CHECK:STDOUT: %N.loc6_9.2: %A = symbolic_binding N, 0 [symbolic = %N.loc6_9.1 (constants.%N.bad)] // CHECK:STDOUT: } // CHECK:STDOUT: %B.decl.loc14: %B.type.f7e62c.2 = class_decl @B.loc14 [concrete = constants.%B.generic.8bc1c8.2] { // CHECK:STDOUT: %T.patt: %pattern_type.98f = symbolic_binding_pattern T, 0 [concrete] // CHECK:STDOUT: %N.patt: @B.loc14.%pattern_type (%pattern_type.51d) = symbolic_binding_pattern N, 1 [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %.loc14_13.1: type = splice_block %.loc14_13.2 [concrete = type] { // CHECK:STDOUT: %.Self.2: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %.loc14_13.2: type = type_literal type [concrete = type] // CHECK:STDOUT: } // CHECK:STDOUT: %T.loc14_9.2: type = symbolic_binding T, 0 [symbolic = %T.loc14_9.1 (constants.%T)] // CHECK:STDOUT: %.loc14_23: type = splice_block %T.ref [symbolic = %T.loc14_9.1 (constants.%T)] { // CHECK:STDOUT: %.Self.1: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %T.ref: type = name_ref T, %T.loc14_9.2 [symbolic = %T.loc14_9.1 (constants.%T)] // CHECK:STDOUT: } // CHECK:STDOUT: %N.loc14_19.2: @B.loc14.%T.loc14_9.1 (%T) = symbolic_binding N, 1 [symbolic = %N.loc14_19.1 (constants.%N.bd9)] // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @A { // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%A // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic class @B.loc6(%N.loc6_9.2: %A) { // CHECK:STDOUT: %N.loc6_9.1: %A = symbolic_binding N, 0 [symbolic = %N.loc6_9.1 (constants.%N.bad)] // CHECK:STDOUT: // CHECK:STDOUT: class; // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic class @B.loc14(%T.loc14_9.2: type, %N.loc14_19.2: @B.loc14.%T.loc14_9.1 (%T)) { // CHECK:STDOUT: %T.loc14_9.1: type = symbolic_binding T, 0 [symbolic = %T.loc14_9.1 (constants.%T)] // CHECK:STDOUT: %N.loc14_19.1: @B.loc14.%T.loc14_9.1 (%T) = symbolic_binding N, 1 [symbolic = %N.loc14_19.1 (constants.%N.bd9)] // CHECK:STDOUT: %pattern_type: type = pattern_type %T.loc14_9.1 [symbolic = %pattern_type (constants.%pattern_type.51d)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: // CHECK:STDOUT: class { // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%B.87e // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @B.loc6(constants.%N.bad) { // CHECK:STDOUT: %N.loc6_9.1 => constants.%N.bad // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @B.loc14(constants.%T, constants.%N.bd9) { // CHECK:STDOUT: %T.loc14_9.1 => constants.%T // CHECK:STDOUT: %N.loc14_19.1 => constants.%N.bd9 // CHECK:STDOUT: %pattern_type => constants.%pattern_type.51d // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- fail_mismatch_param_count.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %A: type = class_type @A [concrete] // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %complete_type: = complete_type_witness %empty_struct_type [concrete] // CHECK:STDOUT: %type: type = facet_type [concrete] // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self] // CHECK:STDOUT: %pattern_type.98f: type = pattern_type type [concrete] // CHECK:STDOUT: %T: type = symbolic_binding T, 0 [symbolic] // CHECK:STDOUT: %C.type.e297ae.1: type = generic_class_type @C.loc6 [concrete] // CHECK:STDOUT: %C.generic.65f314.1: %C.type.e297ae.1 = struct_value () [concrete] // CHECK:STDOUT: %pattern_type.1ab: type = pattern_type %A [concrete] // CHECK:STDOUT: %U: %A = symbolic_binding U, 1 [symbolic] // CHECK:STDOUT: %C.type.e297ae.2: type = generic_class_type @C.loc14 [concrete] // CHECK:STDOUT: %C.generic.65f314.2: %C.type.e297ae.2 = struct_value () [concrete] // CHECK:STDOUT: %C.74f: type = class_type @C.loc14, @C.loc14(%T, %U) [symbolic] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .A = %A.decl // CHECK:STDOUT: .C = %C.decl.loc6 // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %A.decl: type = class_decl @A [concrete = constants.%A] {} {} // CHECK:STDOUT: %C.decl.loc6: %C.type.e297ae.1 = class_decl @C.loc6 [concrete = constants.%C.generic.65f314.1] { // CHECK:STDOUT: %T.patt: %pattern_type.98f = symbolic_binding_pattern T, 0 [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %.loc6_13.1: type = splice_block %.loc6_13.2 [concrete = type] { // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %.loc6_13.2: type = type_literal type [concrete = type] // CHECK:STDOUT: } // CHECK:STDOUT: %T.loc6_9.2: type = symbolic_binding T, 0 [symbolic = %T.loc6_9.1 (constants.%T)] // CHECK:STDOUT: } // CHECK:STDOUT: %C.decl.loc14: %C.type.e297ae.2 = class_decl @C.loc14 [concrete = constants.%C.generic.65f314.2] { // CHECK:STDOUT: %T.patt: %pattern_type.98f = symbolic_binding_pattern T, 0 [concrete] // CHECK:STDOUT: %U.patt: %pattern_type.1ab = symbolic_binding_pattern U, 1 [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %.loc14_13.1: type = splice_block %.loc14_13.2 [concrete = type] { // CHECK:STDOUT: %.Self.2: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %.loc14_13.2: type = type_literal type [concrete = type] // CHECK:STDOUT: } // CHECK:STDOUT: %T.loc14_9.2: type = symbolic_binding T, 0 [symbolic = %T.loc14_9.1 (constants.%T)] // CHECK:STDOUT: %.loc14_23: type = splice_block %A.ref [concrete = constants.%A] { // CHECK:STDOUT: %.Self.1: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %A.ref: type = name_ref A, file.%A.decl [concrete = constants.%A] // CHECK:STDOUT: } // CHECK:STDOUT: %U.loc14_19.2: %A = symbolic_binding U, 1 [symbolic = %U.loc14_19.1 (constants.%U)] // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @A { // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%A // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic class @C.loc6(%T.loc6_9.2: type) { // CHECK:STDOUT: %T.loc6_9.1: type = symbolic_binding T, 0 [symbolic = %T.loc6_9.1 (constants.%T)] // CHECK:STDOUT: // CHECK:STDOUT: class; // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic class @C.loc14(%T.loc14_9.2: type, %U.loc14_19.2: %A) { // CHECK:STDOUT: %T.loc14_9.1: type = symbolic_binding T, 0 [symbolic = %T.loc14_9.1 (constants.%T)] // CHECK:STDOUT: %U.loc14_19.1: %A = symbolic_binding U, 1 [symbolic = %U.loc14_19.1 (constants.%U)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: // CHECK:STDOUT: class { // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%C.74f // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @C.loc6(constants.%T) { // CHECK:STDOUT: %T.loc6_9.1 => constants.%T // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @C.loc14(constants.%T, constants.%U) { // CHECK:STDOUT: %T.loc14_9.1 => constants.%T // CHECK:STDOUT: %U.loc14_19.1 => constants.%U // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- fail_mismatch_param_type.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %A: type = class_type @A [concrete] // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %complete_type: = complete_type_witness %empty_struct_type [concrete] // CHECK:STDOUT: %type: type = facet_type [concrete] // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self] // CHECK:STDOUT: %pattern_type.98f: type = pattern_type type [concrete] // CHECK:STDOUT: %T.67d: type = symbolic_binding T, 0 [symbolic] // CHECK:STDOUT: %D.type.6dc267.1: type = generic_class_type @D.loc6 [concrete] // CHECK:STDOUT: %D.generic.3c4a9e.1: %D.type.6dc267.1 = struct_value () [concrete] // CHECK:STDOUT: %pattern_type.1ab: type = pattern_type %A [concrete] // CHECK:STDOUT: %T.bad: %A = symbolic_binding T, 0 [symbolic] // CHECK:STDOUT: %D.type.6dc267.2: type = generic_class_type @D.loc14 [concrete] // CHECK:STDOUT: %D.generic.3c4a9e.2: %D.type.6dc267.2 = struct_value () [concrete] // CHECK:STDOUT: %D.d48: type = class_type @D.loc14, @D.loc14(%T.bad) [symbolic] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .A = %A.decl // CHECK:STDOUT: .D = %D.decl.loc6 // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %A.decl: type = class_decl @A [concrete = constants.%A] {} {} // CHECK:STDOUT: %D.decl.loc6: %D.type.6dc267.1 = class_decl @D.loc6 [concrete = constants.%D.generic.3c4a9e.1] { // CHECK:STDOUT: %T.patt: %pattern_type.98f = symbolic_binding_pattern T, 0 [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %.loc6_13.1: type = splice_block %.loc6_13.2 [concrete = type] { // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %.loc6_13.2: type = type_literal type [concrete = type] // CHECK:STDOUT: } // CHECK:STDOUT: %T.loc6_9.2: type = symbolic_binding T, 0 [symbolic = %T.loc6_9.1 (constants.%T.67d)] // CHECK:STDOUT: } // CHECK:STDOUT: %D.decl.loc14: %D.type.6dc267.2 = class_decl @D.loc14 [concrete = constants.%D.generic.3c4a9e.2] { // CHECK:STDOUT: %T.patt: %pattern_type.1ab = symbolic_binding_pattern T, 0 [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %.loc14: type = splice_block %A.ref [concrete = constants.%A] { // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %A.ref: type = name_ref A, file.%A.decl [concrete = constants.%A] // CHECK:STDOUT: } // CHECK:STDOUT: %T.loc14_9.2: %A = symbolic_binding T, 0 [symbolic = %T.loc14_9.1 (constants.%T.bad)] // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @A { // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%A // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic class @D.loc6(%T.loc6_9.2: type) { // CHECK:STDOUT: %T.loc6_9.1: type = symbolic_binding T, 0 [symbolic = %T.loc6_9.1 (constants.%T.67d)] // CHECK:STDOUT: // CHECK:STDOUT: class; // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic class @D.loc14(%T.loc14_9.2: %A) { // CHECK:STDOUT: %T.loc14_9.1: %A = symbolic_binding T, 0 [symbolic = %T.loc14_9.1 (constants.%T.bad)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: // CHECK:STDOUT: class { // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%D.d48 // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @D.loc6(constants.%T.67d) { // CHECK:STDOUT: %T.loc6_9.1 => constants.%T.67d // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @D.loc14(constants.%T.bad) { // CHECK:STDOUT: %T.loc14_9.1 => constants.%T.bad // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- fail_mismatch_param_name.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %type: type = facet_type [concrete] // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self] // CHECK:STDOUT: %pattern_type: type = pattern_type type [concrete] // CHECK:STDOUT: %T: type = symbolic_binding T, 0 [symbolic] // CHECK:STDOUT: %E.type.97c458.1: type = generic_class_type @E.loc4 [concrete] // CHECK:STDOUT: %E.generic.e80795.1: %E.type.97c458.1 = struct_value () [concrete] // CHECK:STDOUT: %U: type = symbolic_binding U, 0 [symbolic] // CHECK:STDOUT: %E.type.97c458.2: type = generic_class_type @E.loc12 [concrete] // CHECK:STDOUT: %E.generic.e80795.2: %E.type.97c458.2 = struct_value () [concrete] // CHECK:STDOUT: %E.421e93.2: type = class_type @E.loc12, @E.loc12(%U) [symbolic] // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %complete_type: = complete_type_witness %empty_struct_type [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .E = %E.decl.loc4 // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %E.decl.loc4: %E.type.97c458.1 = class_decl @E.loc4 [concrete = constants.%E.generic.e80795.1] { // CHECK:STDOUT: %T.patt: %pattern_type = symbolic_binding_pattern T, 0 [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %.loc4_13.1: type = splice_block %.loc4_13.2 [concrete = type] { // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %.loc4_13.2: type = type_literal type [concrete = type] // CHECK:STDOUT: } // CHECK:STDOUT: %T.loc4_9.2: type = symbolic_binding T, 0 [symbolic = %T.loc4_9.1 (constants.%T)] // CHECK:STDOUT: } // CHECK:STDOUT: %E.decl.loc12: %E.type.97c458.2 = class_decl @E.loc12 [concrete = constants.%E.generic.e80795.2] { // CHECK:STDOUT: %U.patt: %pattern_type = symbolic_binding_pattern U, 0 [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %.loc12_13.1: type = splice_block %.loc12_13.2 [concrete = type] { // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %.loc12_13.2: type = type_literal type [concrete = type] // CHECK:STDOUT: } // CHECK:STDOUT: %U.loc12_9.2: type = symbolic_binding U, 0 [symbolic = %U.loc12_9.1 (constants.%U)] // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic class @E.loc4(%T.loc4_9.2: type) { // CHECK:STDOUT: %T.loc4_9.1: type = symbolic_binding T, 0 [symbolic = %T.loc4_9.1 (constants.%T)] // CHECK:STDOUT: // CHECK:STDOUT: class; // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic class @E.loc12(%U.loc12_9.2: type) { // CHECK:STDOUT: %U.loc12_9.1: type = symbolic_binding U, 0 [symbolic = %U.loc12_9.1 (constants.%U)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: // CHECK:STDOUT: class { // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%E.421e93.2 // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @E.loc4(constants.%T) { // CHECK:STDOUT: %T.loc4_9.1 => constants.%T // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @E.loc12(constants.%U) { // CHECK:STDOUT: %U.loc12_9.1 => constants.%U // CHECK:STDOUT: } // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/class/generic/self.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/destroy.carbon // TODO: Add ranges and switch to "--dump-sem-ir-ranges=only". // EXTRA-ARGS: --dump-sem-ir-ranges=if-present // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/class/generic/self.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/class/generic/self.carbon class Class(T:! type) { // `Self` is the same as `Class(T)` here. // TODO: Find a better way to test two types are the same. fn MakeSelf() -> Self { return {}; } fn MakeClass() -> Class(T) { return {}; } fn F() { var unused c: Class(T) = MakeSelf(); var unused s: Self = MakeClass(); } } // CHECK:STDOUT: --- self.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %type: type = facet_type [concrete] // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self] // CHECK:STDOUT: %pattern_type.98f: type = pattern_type type [concrete] // CHECK:STDOUT: %T: type = symbolic_binding T, 0 [symbolic] // CHECK:STDOUT: %Class.type: type = generic_class_type @Class [concrete] // CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] // CHECK:STDOUT: %Class.generic: %Class.type = struct_value () [concrete] // CHECK:STDOUT: %Class: type = class_type @Class, @Class(%T) [symbolic] // CHECK:STDOUT: %.6f9: Core.Form = init_form %Class [symbolic] // CHECK:STDOUT: %pattern_type.466: type = pattern_type %Class [symbolic] // CHECK:STDOUT: %Class.MakeSelf.type: type = fn_type @Class.MakeSelf, @Class(%T) [symbolic] // CHECK:STDOUT: %Class.MakeSelf: %Class.MakeSelf.type = struct_value () [symbolic] // CHECK:STDOUT: %Class.MakeClass.type: type = fn_type @Class.MakeClass, @Class(%T) [symbolic] // CHECK:STDOUT: %Class.MakeClass: %Class.MakeClass.type = struct_value () [symbolic] // CHECK:STDOUT: %Class.F.type: type = fn_type @Class.F, @Class(%T) [symbolic] // CHECK:STDOUT: %Class.F: %Class.F.type = struct_value () [symbolic] // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %complete_type: = complete_type_witness %empty_struct_type [concrete] // CHECK:STDOUT: %require_complete: = require_complete_type %Class [symbolic] // CHECK:STDOUT: %empty_struct: %empty_struct_type = struct_value () [concrete] // CHECK:STDOUT: %Class.val: %Class = struct_value () [symbolic] // CHECK:STDOUT: %Class.MakeSelf.specific_fn: = specific_function %Class.MakeSelf, @Class.MakeSelf(%T) [symbolic] // CHECK:STDOUT: %Class.MakeClass.specific_fn: = specific_function %Class.MakeClass, @Class.MakeClass(%T) [symbolic] // CHECK:STDOUT: %Destroy.type: type = facet_type <@Destroy> [concrete] // CHECK:STDOUT: %Destroy.lookup_impl_witness: = lookup_impl_witness %Class, @Destroy [symbolic] // CHECK:STDOUT: %Destroy.facet: %Destroy.type = facet_value %Class, (%Destroy.lookup_impl_witness) [symbolic] // CHECK:STDOUT: %Destroy.WithSelf.Op.type.609: type = fn_type @Destroy.WithSelf.Op, @Destroy.WithSelf(%Destroy.facet) [symbolic] // CHECK:STDOUT: %.2f5: type = fn_type_with_self_type %Destroy.WithSelf.Op.type.609, %Destroy.facet [symbolic] // CHECK:STDOUT: %impl.elem0: %.2f5 = impl_witness_access %Destroy.lookup_impl_witness, element0 [symbolic] // CHECK:STDOUT: %specific_impl_fn: = specific_impl_function %impl.elem0, @Destroy.WithSelf.Op(%Destroy.facet) [symbolic] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: .Destroy = %Core.Destroy // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: %Core.Destroy: type = import_ref Core//prelude/parts/destroy, Destroy, loaded [concrete = constants.%Destroy.type] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .Class = %Class.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %Class.decl: %Class.type = class_decl @Class [concrete = constants.%Class.generic] { // CHECK:STDOUT: %T.patt: %pattern_type.98f = symbolic_binding_pattern T, 0 [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %.loc15_17.1: type = splice_block %.loc15_17.2 [concrete = type] { // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %.loc15_17.2: type = type_literal type [concrete = type] // CHECK:STDOUT: } // CHECK:STDOUT: %T.loc15_13.2: type = symbolic_binding T, 0 [symbolic = %T.loc15_13.1 (constants.%T)] // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic class @Class(%T.loc15_13.2: type) { // CHECK:STDOUT: %T.loc15_13.1: type = symbolic_binding T, 0 [symbolic = %T.loc15_13.1 (constants.%T)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %Class.MakeSelf.type: type = fn_type @Class.MakeSelf, @Class(%T.loc15_13.1) [symbolic = %Class.MakeSelf.type (constants.%Class.MakeSelf.type)] // CHECK:STDOUT: %Class.MakeSelf: @Class.%Class.MakeSelf.type (%Class.MakeSelf.type) = struct_value () [symbolic = %Class.MakeSelf (constants.%Class.MakeSelf)] // CHECK:STDOUT: %Class.MakeClass.type: type = fn_type @Class.MakeClass, @Class(%T.loc15_13.1) [symbolic = %Class.MakeClass.type (constants.%Class.MakeClass.type)] // CHECK:STDOUT: %Class.MakeClass: @Class.%Class.MakeClass.type (%Class.MakeClass.type) = struct_value () [symbolic = %Class.MakeClass (constants.%Class.MakeClass)] // CHECK:STDOUT: %Class.F.type: type = fn_type @Class.F, @Class(%T.loc15_13.1) [symbolic = %Class.F.type (constants.%Class.F.type)] // CHECK:STDOUT: %Class.F: @Class.%Class.F.type (%Class.F.type) = struct_value () [symbolic = %Class.F (constants.%Class.F)] // CHECK:STDOUT: // CHECK:STDOUT: class { // CHECK:STDOUT: %Class.MakeSelf.decl: @Class.%Class.MakeSelf.type (%Class.MakeSelf.type) = fn_decl @Class.MakeSelf [symbolic = @Class.%Class.MakeSelf (constants.%Class.MakeSelf)] { // CHECK:STDOUT: %return.patt: @Class.MakeSelf.%pattern_type (%pattern_type.466) = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: @Class.MakeSelf.%pattern_type (%pattern_type.466) = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %.loc18_20.2: type = specific_constant constants.%Class, @Class(constants.%T) [symbolic = %Class (constants.%Class)] // CHECK:STDOUT: %Self.ref: type = name_ref Self, %.loc18_20.2 [symbolic = %Class (constants.%Class)] // CHECK:STDOUT: %.loc18_20.3: Core.Form = init_form %Self.ref [symbolic = %.loc18_20.1 (constants.%.6f9)] // CHECK:STDOUT: %return.param: ref @Class.MakeSelf.%Class (%Class) = out_param call_param0 // CHECK:STDOUT: %return: ref @Class.MakeSelf.%Class (%Class) = return_slot %return.param // CHECK:STDOUT: } // CHECK:STDOUT: %Class.MakeClass.decl: @Class.%Class.MakeClass.type (%Class.MakeClass.type) = fn_decl @Class.MakeClass [symbolic = @Class.%Class.MakeClass (constants.%Class.MakeClass)] { // CHECK:STDOUT: %return.patt: @Class.MakeClass.%pattern_type (%pattern_type.466) = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: @Class.MakeClass.%pattern_type (%pattern_type.466) = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %Class.ref: %Class.type = name_ref Class, file.%Class.decl [concrete = constants.%Class.generic] // CHECK:STDOUT: %T.ref: type = name_ref T, @Class.%T.loc15_13.2 [symbolic = %T (constants.%T)] // CHECK:STDOUT: %Class.loc19_28.2: type = class_type @Class, @Class(constants.%T) [symbolic = %Class.loc19_28.1 (constants.%Class)] // CHECK:STDOUT: %.loc19_28.2: Core.Form = init_form %Class.loc19_28.2 [symbolic = %.loc19_28.1 (constants.%.6f9)] // CHECK:STDOUT: %return.param: ref @Class.MakeClass.%Class.loc19_28.1 (%Class) = out_param call_param0 // CHECK:STDOUT: %return: ref @Class.MakeClass.%Class.loc19_28.1 (%Class) = return_slot %return.param // CHECK:STDOUT: } // CHECK:STDOUT: %Class.F.decl: @Class.%Class.F.type (%Class.F.type) = fn_decl @Class.F [symbolic = @Class.%Class.F (constants.%Class.F)] {} {} // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%Class // CHECK:STDOUT: .MakeSelf = %Class.MakeSelf.decl // CHECK:STDOUT: .Class = // CHECK:STDOUT: .T = // CHECK:STDOUT: .MakeClass = %Class.MakeClass.decl // CHECK:STDOUT: .F = %Class.F.decl // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic fn @Class.MakeSelf(@Class.%T.loc15_13.2: type) { // CHECK:STDOUT: %T: type = symbolic_binding T, 0 [symbolic = %T (constants.%T)] // CHECK:STDOUT: %Class: type = class_type @Class, @Class(%T) [symbolic = %Class (constants.%Class)] // CHECK:STDOUT: %.loc18_20.1: Core.Form = init_form %Class [symbolic = %.loc18_20.1 (constants.%.6f9)] // CHECK:STDOUT: %pattern_type: type = pattern_type %Class [symbolic = %pattern_type (constants.%pattern_type.466)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete: = require_complete_type %Class [symbolic = %require_complete (constants.%require_complete)] // CHECK:STDOUT: %Class.val: @Class.MakeSelf.%Class (%Class) = struct_value () [symbolic = %Class.val (constants.%Class.val)] // CHECK:STDOUT: // CHECK:STDOUT: fn() -> out %return.param: @Class.MakeSelf.%Class (%Class) { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %.loc18_35.1: %empty_struct_type = struct_literal () [concrete = constants.%empty_struct] // CHECK:STDOUT: %.loc18_35.2: init @Class.MakeSelf.%Class (%Class) to %return.param = class_init () [symbolic = %Class.val (constants.%Class.val)] // CHECK:STDOUT: %.loc18_36: init @Class.MakeSelf.%Class (%Class) = converted %.loc18_35.1, %.loc18_35.2 [symbolic = %Class.val (constants.%Class.val)] // CHECK:STDOUT: return %.loc18_36 to %return.param // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic fn @Class.MakeClass(@Class.%T.loc15_13.2: type) { // CHECK:STDOUT: %T: type = symbolic_binding T, 0 [symbolic = %T (constants.%T)] // CHECK:STDOUT: %Class.loc19_28.1: type = class_type @Class, @Class(%T) [symbolic = %Class.loc19_28.1 (constants.%Class)] // CHECK:STDOUT: %.loc19_28.1: Core.Form = init_form %Class.loc19_28.1 [symbolic = %.loc19_28.1 (constants.%.6f9)] // CHECK:STDOUT: %pattern_type: type = pattern_type %Class.loc19_28.1 [symbolic = %pattern_type (constants.%pattern_type.466)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete: = require_complete_type %Class.loc19_28.1 [symbolic = %require_complete (constants.%require_complete)] // CHECK:STDOUT: %Class.val: @Class.MakeClass.%Class.loc19_28.1 (%Class) = struct_value () [symbolic = %Class.val (constants.%Class.val)] // CHECK:STDOUT: // CHECK:STDOUT: fn() -> out %return.param: @Class.MakeClass.%Class.loc19_28.1 (%Class) { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %.loc19_40.1: %empty_struct_type = struct_literal () [concrete = constants.%empty_struct] // CHECK:STDOUT: %.loc19_40.2: init @Class.MakeClass.%Class.loc19_28.1 (%Class) to %return.param = class_init () [symbolic = %Class.val (constants.%Class.val)] // CHECK:STDOUT: %.loc19_41: init @Class.MakeClass.%Class.loc19_28.1 (%Class) = converted %.loc19_40.1, %.loc19_40.2 [symbolic = %Class.val (constants.%Class.val)] // CHECK:STDOUT: return %.loc19_41 to %return.param // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic fn @Class.F(@Class.%T.loc15_13.2: type) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: %T: type = symbolic_binding T, 0 [symbolic = %T (constants.%T)] // CHECK:STDOUT: %Class.loc21_26.2: type = class_type @Class, @Class(%T) [symbolic = %Class.loc21_26.2 (constants.%Class)] // CHECK:STDOUT: %require_complete: = require_complete_type %Class.loc21_26.2 [symbolic = %require_complete (constants.%require_complete)] // CHECK:STDOUT: %pattern_type: type = pattern_type %Class.loc21_26.2 [symbolic = %pattern_type (constants.%pattern_type.466)] // CHECK:STDOUT: %Class.MakeSelf.type: type = fn_type @Class.MakeSelf, @Class(%T) [symbolic = %Class.MakeSelf.type (constants.%Class.MakeSelf.type)] // CHECK:STDOUT: %Class.MakeSelf: @Class.F.%Class.MakeSelf.type (%Class.MakeSelf.type) = struct_value () [symbolic = %Class.MakeSelf (constants.%Class.MakeSelf)] // CHECK:STDOUT: %Class.MakeSelf.specific_fn.loc21_30.2: = specific_function %Class.MakeSelf, @Class.MakeSelf(%T) [symbolic = %Class.MakeSelf.specific_fn.loc21_30.2 (constants.%Class.MakeSelf.specific_fn)] // CHECK:STDOUT: %Class.MakeClass.type: type = fn_type @Class.MakeClass, @Class(%T) [symbolic = %Class.MakeClass.type (constants.%Class.MakeClass.type)] // CHECK:STDOUT: %Class.MakeClass: @Class.F.%Class.MakeClass.type (%Class.MakeClass.type) = struct_value () [symbolic = %Class.MakeClass (constants.%Class.MakeClass)] // CHECK:STDOUT: %Class.MakeClass.specific_fn.loc22_26.2: = specific_function %Class.MakeClass, @Class.MakeClass(%T) [symbolic = %Class.MakeClass.specific_fn.loc22_26.2 (constants.%Class.MakeClass.specific_fn)] // CHECK:STDOUT: %Destroy.lookup_impl_witness: = lookup_impl_witness %Class.loc21_26.2, @Destroy [symbolic = %Destroy.lookup_impl_witness (constants.%Destroy.lookup_impl_witness)] // CHECK:STDOUT: %Destroy.facet: %Destroy.type = facet_value %Class.loc21_26.2, (%Destroy.lookup_impl_witness) [symbolic = %Destroy.facet (constants.%Destroy.facet)] // CHECK:STDOUT: %Destroy.WithSelf.Op.type: type = fn_type @Destroy.WithSelf.Op, @Destroy.WithSelf(%Destroy.facet) [symbolic = %Destroy.WithSelf.Op.type (constants.%Destroy.WithSelf.Op.type.609)] // CHECK:STDOUT: %.loc22_5.2: type = fn_type_with_self_type %Destroy.WithSelf.Op.type, %Destroy.facet [symbolic = %.loc22_5.2 (constants.%.2f5)] // CHECK:STDOUT: %impl.elem0.loc22_5.2: @Class.F.%.loc22_5.2 (%.2f5) = impl_witness_access %Destroy.lookup_impl_witness, element0 [symbolic = %impl.elem0.loc22_5.2 (constants.%impl.elem0)] // CHECK:STDOUT: %specific_impl_fn.loc22_5.2: = specific_impl_function %impl.elem0.loc22_5.2, @Destroy.WithSelf.Op(%Destroy.facet) [symbolic = %specific_impl_fn.loc22_5.2 (constants.%specific_impl_fn)] // CHECK:STDOUT: // CHECK:STDOUT: fn() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %c.patt: @Class.F.%pattern_type (%pattern_type.466) = ref_binding_pattern c [concrete] // CHECK:STDOUT: %c.var_patt: @Class.F.%pattern_type (%pattern_type.466) = var_pattern %c.patt [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %c.var: ref @Class.F.%Class.loc21_26.2 (%Class) = var %c.var_patt // CHECK:STDOUT: %.loc21_30: @Class.F.%Class.MakeSelf.type (%Class.MakeSelf.type) = specific_constant @Class.%Class.MakeSelf.decl, @Class(constants.%T) [symbolic = %Class.MakeSelf (constants.%Class.MakeSelf)] // CHECK:STDOUT: %MakeSelf.ref: @Class.F.%Class.MakeSelf.type (%Class.MakeSelf.type) = name_ref MakeSelf, %.loc21_30 [symbolic = %Class.MakeSelf (constants.%Class.MakeSelf)] // CHECK:STDOUT: %Class.MakeSelf.specific_fn.loc21_30.1: = specific_function %MakeSelf.ref, @Class.MakeSelf(constants.%T) [symbolic = %Class.MakeSelf.specific_fn.loc21_30.2 (constants.%Class.MakeSelf.specific_fn)] // CHECK:STDOUT: %.loc21_5: ref @Class.F.%Class.loc21_26.2 (%Class) = splice_block %c.var {} // CHECK:STDOUT: %Class.MakeSelf.call: init @Class.F.%Class.loc21_26.2 (%Class) to %.loc21_5 = call %Class.MakeSelf.specific_fn.loc21_30.1() // CHECK:STDOUT: assign %c.var, %Class.MakeSelf.call // CHECK:STDOUT: %.loc21_26: type = splice_block %Class.loc21_26.1 [symbolic = %Class.loc21_26.2 (constants.%Class)] { // CHECK:STDOUT: %Class.ref: %Class.type = name_ref Class, file.%Class.decl [concrete = constants.%Class.generic] // CHECK:STDOUT: %T.ref: type = name_ref T, @Class.%T.loc15_13.2 [symbolic = %T (constants.%T)] // CHECK:STDOUT: %Class.loc21_26.1: type = class_type @Class, @Class(constants.%T) [symbolic = %Class.loc21_26.2 (constants.%Class)] // CHECK:STDOUT: } // CHECK:STDOUT: %c: ref @Class.F.%Class.loc21_26.2 (%Class) = ref_binding c, %c.var // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %s.patt: @Class.F.%pattern_type (%pattern_type.466) = ref_binding_pattern s [concrete] // CHECK:STDOUT: %s.var_patt: @Class.F.%pattern_type (%pattern_type.466) = var_pattern %s.patt [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %s.var: ref @Class.F.%Class.loc21_26.2 (%Class) = var %s.var_patt // CHECK:STDOUT: %.loc22_26: @Class.F.%Class.MakeClass.type (%Class.MakeClass.type) = specific_constant @Class.%Class.MakeClass.decl, @Class(constants.%T) [symbolic = %Class.MakeClass (constants.%Class.MakeClass)] // CHECK:STDOUT: %MakeClass.ref: @Class.F.%Class.MakeClass.type (%Class.MakeClass.type) = name_ref MakeClass, %.loc22_26 [symbolic = %Class.MakeClass (constants.%Class.MakeClass)] // CHECK:STDOUT: %Class.MakeClass.specific_fn.loc22_26.1: = specific_function %MakeClass.ref, @Class.MakeClass(constants.%T) [symbolic = %Class.MakeClass.specific_fn.loc22_26.2 (constants.%Class.MakeClass.specific_fn)] // CHECK:STDOUT: %.loc22_5.1: ref @Class.F.%Class.loc21_26.2 (%Class) = splice_block %s.var {} // CHECK:STDOUT: %Class.MakeClass.call: init @Class.F.%Class.loc21_26.2 (%Class) to %.loc22_5.1 = call %Class.MakeClass.specific_fn.loc22_26.1() // CHECK:STDOUT: assign %s.var, %Class.MakeClass.call // CHECK:STDOUT: %.loc22_19.1: type = splice_block %Self.ref [symbolic = %Class.loc21_26.2 (constants.%Class)] { // CHECK:STDOUT: %.loc22_19.2: type = specific_constant constants.%Class, @Class(constants.%T) [symbolic = %Class.loc21_26.2 (constants.%Class)] // CHECK:STDOUT: %Self.ref: type = name_ref Self, %.loc22_19.2 [symbolic = %Class.loc21_26.2 (constants.%Class)] // CHECK:STDOUT: } // CHECK:STDOUT: %s: ref @Class.F.%Class.loc21_26.2 (%Class) = ref_binding s, %s.var // CHECK:STDOUT: %impl.elem0.loc22_5.1: @Class.F.%.loc22_5.2 (%.2f5) = impl_witness_access constants.%Destroy.lookup_impl_witness, element0 [symbolic = %impl.elem0.loc22_5.2 (constants.%impl.elem0)] // CHECK:STDOUT: %bound_method.loc22_5.1: = bound_method %s.var, %impl.elem0.loc22_5.1 // CHECK:STDOUT: %specific_impl_fn.loc22_5.1: = specific_impl_function %impl.elem0.loc22_5.1, @Destroy.WithSelf.Op(constants.%Destroy.facet) [symbolic = %specific_impl_fn.loc22_5.2 (constants.%specific_impl_fn)] // CHECK:STDOUT: %bound_method.loc22_5.2: = bound_method %s.var, %specific_impl_fn.loc22_5.1 // CHECK:STDOUT: %Destroy.WithSelf.Op.call.loc22: init %empty_tuple.type = call %bound_method.loc22_5.2(%s.var) // CHECK:STDOUT: %impl.elem0.loc21: @Class.F.%.loc22_5.2 (%.2f5) = impl_witness_access constants.%Destroy.lookup_impl_witness, element0 [symbolic = %impl.elem0.loc22_5.2 (constants.%impl.elem0)] // CHECK:STDOUT: %bound_method.loc21_5.1: = bound_method %c.var, %impl.elem0.loc21 // CHECK:STDOUT: %specific_impl_fn.loc21: = specific_impl_function %impl.elem0.loc21, @Destroy.WithSelf.Op(constants.%Destroy.facet) [symbolic = %specific_impl_fn.loc22_5.2 (constants.%specific_impl_fn)] // CHECK:STDOUT: %bound_method.loc21_5.2: = bound_method %c.var, %specific_impl_fn.loc21 // CHECK:STDOUT: %Destroy.WithSelf.Op.call.loc21: init %empty_tuple.type = call %bound_method.loc21_5.2(%c.var) // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @Class(constants.%T) { // CHECK:STDOUT: %T.loc15_13.1 => constants.%T // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %Class.MakeSelf.type => constants.%Class.MakeSelf.type // CHECK:STDOUT: %Class.MakeSelf => constants.%Class.MakeSelf // CHECK:STDOUT: %Class.MakeClass.type => constants.%Class.MakeClass.type // CHECK:STDOUT: %Class.MakeClass => constants.%Class.MakeClass // CHECK:STDOUT: %Class.F.type => constants.%Class.F.type // CHECK:STDOUT: %Class.F => constants.%Class.F // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @Class.MakeSelf(constants.%T) { // CHECK:STDOUT: %T => constants.%T // CHECK:STDOUT: %Class => constants.%Class // CHECK:STDOUT: %.loc18_20.1 => constants.%.6f9 // CHECK:STDOUT: %pattern_type => constants.%pattern_type.466 // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete => constants.%require_complete // CHECK:STDOUT: %Class.val => constants.%Class.val // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @Class.MakeClass(constants.%T) { // CHECK:STDOUT: %T => constants.%T // CHECK:STDOUT: %Class.loc19_28.1 => constants.%Class // CHECK:STDOUT: %.loc19_28.1 => constants.%.6f9 // CHECK:STDOUT: %pattern_type => constants.%pattern_type.466 // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete => constants.%require_complete // CHECK:STDOUT: %Class.val => constants.%Class.val // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @Class.F(constants.%T) {} // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/class/generic/stringify.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/int.carbon // TODO: Add ranges and switch to "--dump-sem-ir-ranges=only". // EXTRA-ARGS: --dump-sem-ir-ranges=if-present // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/class/generic/stringify.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/class/generic/stringify.carbon // --- fail_empty_params.carbon library "[[@TEST_NAME]]"; class NoParams {} class EmptyParams() {} var v: NoParams; // CHECK:STDERR: fail_empty_params.carbon:[[@LINE+7]]:1: error: cannot implicitly convert expression of type `NoParams` to `EmptyParams()` [ConversionFailure] // CHECK:STDERR: var w: EmptyParams() = v; // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: fail_empty_params.carbon:[[@LINE+4]]:1: note: type `NoParams` does not implement interface `Core.ImplicitAs(EmptyParams())` [MissingImplInMemberAccessInContext] // CHECK:STDERR: var w: EmptyParams() = v; // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: var w: EmptyParams() = v; // --- fail_nested.carbon library "[[@TEST_NAME]]"; class Outer(T:! type) { class Inner(U:! type) { } } var v: Outer({}*); // TODO: It would be nice to include the `Outer({}*).` prefix in the name of `Inner`. // CHECK:STDERR: fail_nested.carbon:[[@LINE+7]]:1: error: cannot implicitly convert expression of type `Outer({}*)` to `Inner({.a: i32}*)` [ConversionFailure] // CHECK:STDERR: var w: Outer({}*).Inner({.a: i32}*) = v; // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: fail_nested.carbon:[[@LINE+4]]:1: note: type `Outer({}*)` does not implement interface `Core.ImplicitAs(Inner({.a: i32}*))` [MissingImplInMemberAccessInContext] // CHECK:STDERR: var w: Outer({}*).Inner({.a: i32}*) = v; // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: var w: Outer({}*).Inner({.a: i32}*) = v; // --- fail_int_value.carbon library "[[@TEST_NAME]]"; class C(N:! i32) {} // CHECK:STDERR: fail_int_value.carbon:[[@LINE+7]]:1: error: cannot implicitly convert expression of type `()` to `C(123)` [ConversionFailure] // CHECK:STDERR: var v: C(123) = (); // CHECK:STDERR: ^~~~~~~~~~~~~ // CHECK:STDERR: fail_int_value.carbon:[[@LINE+4]]:1: note: type `()` does not implement interface `Core.ImplicitAs(C(123))` [MissingImplInMemberAccessInContext] // CHECK:STDERR: var v: C(123) = (); // CHECK:STDERR: ^~~~~~~~~~~~~ // CHECK:STDERR: var v: C(123) = (); // --- fail_class_param.carbon library "[[@TEST_NAME]]"; class D { var a: i32; var b: i32; } class E(F:! D) {} // CHECK:STDERR: fail_class_param.carbon:[[@LINE+14]]:8: error: argument for generic parameter is not a compile-time constant [CompTimeArgumentNotConstant] // CHECK:STDERR: var g: E({.a = 1, .b = 2}) = {} as E({.a = 3, .b = 4} as D); // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: fail_class_param.carbon:[[@LINE-5]]:9: note: initializing generic parameter `F` declared here [InitializingGenericParam] // CHECK:STDERR: class E(F:! D) {} // CHECK:STDERR: ^ // CHECK:STDERR: // CHECK:STDERR: fail_class_param.carbon:[[@LINE+7]]:36: error: argument for generic parameter is not a compile-time constant [CompTimeArgumentNotConstant] // CHECK:STDERR: var g: E({.a = 1, .b = 2}) = {} as E({.a = 3, .b = 4} as D); // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: fail_class_param.carbon:[[@LINE-12]]:9: note: initializing generic parameter `F` declared here [InitializingGenericParam] // CHECK:STDERR: class E(F:! D) {} // CHECK:STDERR: ^ // CHECK:STDERR: var g: E({.a = 1, .b = 2}) = {} as E({.a = 3, .b = 4} as D); // CHECK:STDOUT: --- fail_empty_params.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %NoParams: type = class_type @NoParams [concrete] // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %complete_type: = complete_type_witness %empty_struct_type [concrete] // CHECK:STDOUT: %EmptyParams.type: type = generic_class_type @EmptyParams [concrete] // CHECK:STDOUT: %EmptyParams.generic: %EmptyParams.type = struct_value () [concrete] // CHECK:STDOUT: %EmptyParams: type = class_type @EmptyParams [concrete] // CHECK:STDOUT: %pattern_type.8ec: type = pattern_type %NoParams [concrete] // CHECK:STDOUT: %DefaultOrUnformed.type: type = facet_type <@DefaultOrUnformed> [concrete] // CHECK:STDOUT: %T: type = symbolic_binding T, 0 [symbolic] // CHECK:STDOUT: %T.as.DefaultOrUnformed.impl.Op.type.6c6: type = fn_type @T.as.DefaultOrUnformed.impl.Op, @T.as.DefaultOrUnformed.impl(%T) [symbolic] // CHECK:STDOUT: %T.as.DefaultOrUnformed.impl.Op.ae1: %T.as.DefaultOrUnformed.impl.Op.type.6c6 = struct_value () [symbolic] // CHECK:STDOUT: %DefaultOrUnformed.impl_witness.d7c: = impl_witness imports.%DefaultOrUnformed.impl_witness_table, @T.as.DefaultOrUnformed.impl(%NoParams) [concrete] // CHECK:STDOUT: %T.as.DefaultOrUnformed.impl.Op.type.51c: type = fn_type @T.as.DefaultOrUnformed.impl.Op, @T.as.DefaultOrUnformed.impl(%NoParams) [concrete] // CHECK:STDOUT: %T.as.DefaultOrUnformed.impl.Op.5bb: %T.as.DefaultOrUnformed.impl.Op.type.51c = struct_value () [concrete] // CHECK:STDOUT: %DefaultOrUnformed.facet: %DefaultOrUnformed.type = facet_value %NoParams, (%DefaultOrUnformed.impl_witness.d7c) [concrete] // CHECK:STDOUT: %T.as.DefaultOrUnformed.impl.Op.specific_fn: = specific_function %T.as.DefaultOrUnformed.impl.Op.5bb, @T.as.DefaultOrUnformed.impl.Op(%NoParams) [concrete] // CHECK:STDOUT: %pattern_type.e19: type = pattern_type %EmptyParams [concrete] // CHECK:STDOUT: %ImplicitAs.type.cc7: type = generic_interface_type @ImplicitAs [concrete] // CHECK:STDOUT: %ImplicitAs.generic: %ImplicitAs.type.cc7 = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: .DefaultOrUnformed = %Core.DefaultOrUnformed // CHECK:STDOUT: .ImplicitAs = %Core.ImplicitAs // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: %Core.DefaultOrUnformed: type = import_ref Core//prelude/parts/default, DefaultOrUnformed, loaded [concrete = constants.%DefaultOrUnformed.type] // CHECK:STDOUT: %Core.import_ref.01d: @T.as.DefaultOrUnformed.impl.%T.as.DefaultOrUnformed.impl.Op.type (%T.as.DefaultOrUnformed.impl.Op.type.6c6) = import_ref Core//prelude/parts/default, loc{{\d+_\d+}}, loaded [symbolic = @T.as.DefaultOrUnformed.impl.%T.as.DefaultOrUnformed.impl.Op (constants.%T.as.DefaultOrUnformed.impl.Op.ae1)] // CHECK:STDOUT: %DefaultOrUnformed.impl_witness_table = impl_witness_table (%Core.import_ref.01d), @T.as.DefaultOrUnformed.impl [concrete] // CHECK:STDOUT: %Core.ImplicitAs: %ImplicitAs.type.cc7 = import_ref Core//prelude/parts/as, ImplicitAs, loaded [concrete = constants.%ImplicitAs.generic] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .NoParams = %NoParams.decl // CHECK:STDOUT: .EmptyParams = %EmptyParams.decl // CHECK:STDOUT: .v = %v // CHECK:STDOUT: .w = %w // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %NoParams.decl: type = class_decl @NoParams [concrete = constants.%NoParams] {} {} // CHECK:STDOUT: %EmptyParams.decl: %EmptyParams.type = class_decl @EmptyParams [concrete = constants.%EmptyParams.generic] {} {} // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %v.patt: %pattern_type.8ec = ref_binding_pattern v [concrete] // CHECK:STDOUT: %v.var_patt: %pattern_type.8ec = var_pattern %v.patt [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %v.var: ref %NoParams = var %v.var_patt [concrete] // CHECK:STDOUT: %NoParams.ref: type = name_ref NoParams, %NoParams.decl [concrete = constants.%NoParams] // CHECK:STDOUT: %v: ref %NoParams = ref_binding v, %v.var [concrete = %v.var] // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %w.patt: %pattern_type.e19 = ref_binding_pattern w [concrete] // CHECK:STDOUT: %w.var_patt: %pattern_type.e19 = var_pattern %w.patt [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %w.var: ref %EmptyParams = var %w.var_patt [concrete] // CHECK:STDOUT: %.loc15: type = splice_block %EmptyParams [concrete = constants.%EmptyParams] { // CHECK:STDOUT: %EmptyParams.ref: %EmptyParams.type = name_ref EmptyParams, %EmptyParams.decl [concrete = constants.%EmptyParams.generic] // CHECK:STDOUT: %EmptyParams: type = class_type @EmptyParams [concrete = constants.%EmptyParams] // CHECK:STDOUT: } // CHECK:STDOUT: %w: ref %EmptyParams = ref_binding w, %w.var [concrete = %w.var] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @NoParams { // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%NoParams // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @EmptyParams { // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%EmptyParams // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @__global_init() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %DefaultOrUnformed.facet: %DefaultOrUnformed.type = facet_value constants.%NoParams, (constants.%DefaultOrUnformed.impl_witness.d7c) [concrete = constants.%DefaultOrUnformed.facet] // CHECK:STDOUT: %.loc7_16.1: %DefaultOrUnformed.type = converted constants.%NoParams, %DefaultOrUnformed.facet [concrete = constants.%DefaultOrUnformed.facet] // CHECK:STDOUT: %as_type: type = facet_access_type %.loc7_16.1 [concrete = constants.%NoParams] // CHECK:STDOUT: %.loc7_16.2: type = converted %.loc7_16.1, %as_type [concrete = constants.%NoParams] // CHECK:STDOUT: %T.as.DefaultOrUnformed.impl.Op.specific_fn: = specific_function constants.%T.as.DefaultOrUnformed.impl.Op.5bb, @T.as.DefaultOrUnformed.impl.Op(constants.%NoParams) [concrete = constants.%T.as.DefaultOrUnformed.impl.Op.specific_fn] // CHECK:STDOUT: %.loc7_1: ref %NoParams = splice_block file.%v.var [concrete = file.%v.var] {} // CHECK:STDOUT: %T.as.DefaultOrUnformed.impl.Op.call: init %NoParams to %.loc7_1 = call %T.as.DefaultOrUnformed.impl.Op.specific_fn() // CHECK:STDOUT: assign file.%v.var, %T.as.DefaultOrUnformed.impl.Op.call // CHECK:STDOUT: %v.ref: ref %NoParams = name_ref v, file.%v [concrete = file.%v.var] // CHECK:STDOUT: %.loc15: %EmptyParams = converted %v.ref, [concrete = ] // CHECK:STDOUT: assign file.%w.var, // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- fail_nested.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %type: type = facet_type [concrete] // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self] // CHECK:STDOUT: %pattern_type.98f: type = pattern_type type [concrete] // CHECK:STDOUT: %T: type = symbolic_binding T, 0 [symbolic] // CHECK:STDOUT: %Outer.type: type = generic_class_type @Outer [concrete] // CHECK:STDOUT: %Outer.generic: %Outer.type = struct_value () [concrete] // CHECK:STDOUT: %Outer.387: type = class_type @Outer, @Outer(%T) [symbolic] // CHECK:STDOUT: %U: type = symbolic_binding U, 1 [symbolic] // CHECK:STDOUT: %Inner.type.e0d: type = generic_class_type @Inner, @Outer(%T) [symbolic] // CHECK:STDOUT: %Inner.generic.ada: %Inner.type.e0d = struct_value () [symbolic] // CHECK:STDOUT: %Inner.e21: type = class_type @Inner, @Inner(%T, %U) [symbolic] // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %complete_type.357: = complete_type_witness %empty_struct_type [concrete] // CHECK:STDOUT: %empty_struct: %empty_struct_type = struct_value () [concrete] // CHECK:STDOUT: %ptr.c28: type = ptr_type %empty_struct_type [concrete] // CHECK:STDOUT: %Outer.b7c: type = class_type @Outer, @Outer(%ptr.c28) [concrete] // CHECK:STDOUT: %Inner.type.cd9: type = generic_class_type @Inner, @Outer(%ptr.c28) [concrete] // CHECK:STDOUT: %Inner.generic.0f6: %Inner.type.cd9 = struct_value () [concrete] // CHECK:STDOUT: %pattern_type.acf: type = pattern_type %Outer.b7c [concrete] // CHECK:STDOUT: %DefaultOrUnformed.type: type = facet_type <@DefaultOrUnformed> [concrete] // CHECK:STDOUT: %T.as.DefaultOrUnformed.impl.Op.type.6c6: type = fn_type @T.as.DefaultOrUnformed.impl.Op, @T.as.DefaultOrUnformed.impl(%T) [symbolic] // CHECK:STDOUT: %T.as.DefaultOrUnformed.impl.Op.ae1: %T.as.DefaultOrUnformed.impl.Op.type.6c6 = struct_value () [symbolic] // CHECK:STDOUT: %DefaultOrUnformed.impl_witness.8e7: = impl_witness imports.%DefaultOrUnformed.impl_witness_table, @T.as.DefaultOrUnformed.impl(%Outer.b7c) [concrete] // CHECK:STDOUT: %T.as.DefaultOrUnformed.impl.Op.type.e70: type = fn_type @T.as.DefaultOrUnformed.impl.Op, @T.as.DefaultOrUnformed.impl(%Outer.b7c) [concrete] // CHECK:STDOUT: %T.as.DefaultOrUnformed.impl.Op.3a0: %T.as.DefaultOrUnformed.impl.Op.type.e70 = struct_value () [concrete] // CHECK:STDOUT: %DefaultOrUnformed.facet: %DefaultOrUnformed.type = facet_value %Outer.b7c, (%DefaultOrUnformed.impl_witness.8e7) [concrete] // CHECK:STDOUT: %T.as.DefaultOrUnformed.impl.Op.specific_fn: = specific_function %T.as.DefaultOrUnformed.impl.Op.3a0, @T.as.DefaultOrUnformed.impl.Op(%Outer.b7c) [concrete] // CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [concrete] // CHECK:STDOUT: %Int.type: type = generic_class_type @Int [concrete] // CHECK:STDOUT: %Int.generic: %Int.type = struct_value () [concrete] // CHECK:STDOUT: %i32: type = class_type @Int, @Int(%int_32) [concrete] // CHECK:STDOUT: %struct_type.a: type = struct_type {.a: %i32} [concrete] // CHECK:STDOUT: %ptr.1bb: type = ptr_type %struct_type.a [concrete] // CHECK:STDOUT: %Inner.304: type = class_type @Inner, @Inner(%ptr.c28, %ptr.1bb) [concrete] // CHECK:STDOUT: %pattern_type.536: type = pattern_type %Inner.304 [concrete] // CHECK:STDOUT: %ImplicitAs.type.cc7: type = generic_interface_type @ImplicitAs [concrete] // CHECK:STDOUT: %ImplicitAs.generic: %ImplicitAs.type.cc7 = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: .DefaultOrUnformed = %Core.DefaultOrUnformed // CHECK:STDOUT: .Int = %Core.Int // CHECK:STDOUT: .ImplicitAs = %Core.ImplicitAs // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: %Core.DefaultOrUnformed: type = import_ref Core//prelude/parts/default, DefaultOrUnformed, loaded [concrete = constants.%DefaultOrUnformed.type] // CHECK:STDOUT: %Core.import_ref.01d: @T.as.DefaultOrUnformed.impl.%T.as.DefaultOrUnformed.impl.Op.type (%T.as.DefaultOrUnformed.impl.Op.type.6c6) = import_ref Core//prelude/parts/default, loc{{\d+_\d+}}, loaded [symbolic = @T.as.DefaultOrUnformed.impl.%T.as.DefaultOrUnformed.impl.Op (constants.%T.as.DefaultOrUnformed.impl.Op.ae1)] // CHECK:STDOUT: %DefaultOrUnformed.impl_witness_table = impl_witness_table (%Core.import_ref.01d), @T.as.DefaultOrUnformed.impl [concrete] // CHECK:STDOUT: %Core.Int: %Int.type = import_ref Core//prelude/parts/int, Int, loaded [concrete = constants.%Int.generic] // CHECK:STDOUT: %Core.ImplicitAs: %ImplicitAs.type.cc7 = import_ref Core//prelude/parts/as, ImplicitAs, loaded [concrete = constants.%ImplicitAs.generic] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .Outer = %Outer.decl // CHECK:STDOUT: .v = %v // CHECK:STDOUT: .w = %w // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %Outer.decl: %Outer.type = class_decl @Outer [concrete = constants.%Outer.generic] { // CHECK:STDOUT: %T.patt: %pattern_type.98f = symbolic_binding_pattern T, 0 [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %.loc4_17.1: type = splice_block %.loc4_17.2 [concrete = type] { // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %.loc4_17.2: type = type_literal type [concrete = type] // CHECK:STDOUT: } // CHECK:STDOUT: %T.loc4_13.2: type = symbolic_binding T, 0 [symbolic = %T.loc4_13.1 (constants.%T)] // CHECK:STDOUT: } // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %v.patt: %pattern_type.acf = ref_binding_pattern v [concrete] // CHECK:STDOUT: %v.var_patt: %pattern_type.acf = var_pattern %v.patt [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %v.var: ref %Outer.b7c = var %v.var_patt [concrete] // CHECK:STDOUT: %.loc9_17: type = splice_block %Outer.loc9 [concrete = constants.%Outer.b7c] { // CHECK:STDOUT: %Outer.ref.loc9: %Outer.type = name_ref Outer, %Outer.decl [concrete = constants.%Outer.generic] // CHECK:STDOUT: %.loc9_15: %empty_struct_type = struct_literal () [concrete = constants.%empty_struct] // CHECK:STDOUT: %.loc9_16: type = converted %.loc9_15, constants.%empty_struct_type [concrete = constants.%empty_struct_type] // CHECK:STDOUT: %ptr.loc9: type = ptr_type %.loc9_16 [concrete = constants.%ptr.c28] // CHECK:STDOUT: %Outer.loc9: type = class_type @Outer, @Outer(constants.%ptr.c28) [concrete = constants.%Outer.b7c] // CHECK:STDOUT: } // CHECK:STDOUT: %v: ref %Outer.b7c = ref_binding v, %v.var [concrete = %v.var] // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %w.patt: %pattern_type.536 = ref_binding_pattern w [concrete] // CHECK:STDOUT: %w.var_patt: %pattern_type.536 = var_pattern %w.patt [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %w.var: ref %Inner.304 = var %w.var_patt [concrete] // CHECK:STDOUT: %.loc19_35: type = splice_block %Inner [concrete = constants.%Inner.304] { // CHECK:STDOUT: %Outer.ref.loc19: %Outer.type = name_ref Outer, %Outer.decl [concrete = constants.%Outer.generic] // CHECK:STDOUT: %.loc19_15: %empty_struct_type = struct_literal () [concrete = constants.%empty_struct] // CHECK:STDOUT: %.loc19_16: type = converted %.loc19_15, constants.%empty_struct_type [concrete = constants.%empty_struct_type] // CHECK:STDOUT: %ptr.loc19_16: type = ptr_type %.loc19_16 [concrete = constants.%ptr.c28] // CHECK:STDOUT: %Outer.loc19: type = class_type @Outer, @Outer(constants.%ptr.c28) [concrete = constants.%Outer.b7c] // CHECK:STDOUT: %.loc19_18: %Inner.type.cd9 = specific_constant @Outer.%Inner.decl, @Outer(constants.%ptr.c28) [concrete = constants.%Inner.generic.0f6] // CHECK:STDOUT: %Inner.ref: %Inner.type.cd9 = name_ref Inner, %.loc19_18 [concrete = constants.%Inner.generic.0f6] // CHECK:STDOUT: %i32: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %struct_type.a: type = struct_type {.a: %i32} [concrete = constants.%struct_type.a] // CHECK:STDOUT: %ptr.loc19_34: type = ptr_type %struct_type.a [concrete = constants.%ptr.1bb] // CHECK:STDOUT: %Inner: type = class_type @Inner, @Inner(constants.%ptr.c28, constants.%ptr.1bb) [concrete = constants.%Inner.304] // CHECK:STDOUT: } // CHECK:STDOUT: %w: ref %Inner.304 = ref_binding w, %w.var [concrete = %w.var] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic class @Outer(%T.loc4_13.2: type) { // CHECK:STDOUT: %T.loc4_13.1: type = symbolic_binding T, 0 [symbolic = %T.loc4_13.1 (constants.%T)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %Inner.type: type = generic_class_type @Inner, @Outer(%T.loc4_13.1) [symbolic = %Inner.type (constants.%Inner.type.e0d)] // CHECK:STDOUT: %Inner.generic: @Outer.%Inner.type (%Inner.type.e0d) = struct_value () [symbolic = %Inner.generic (constants.%Inner.generic.ada)] // CHECK:STDOUT: // CHECK:STDOUT: class { // CHECK:STDOUT: %Inner.decl: @Outer.%Inner.type (%Inner.type.e0d) = class_decl @Inner [symbolic = @Outer.%Inner.generic (constants.%Inner.generic.ada)] { // CHECK:STDOUT: %U.patt: %pattern_type.98f = symbolic_binding_pattern U, 1 [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %.loc5_19.1: type = splice_block %.loc5_19.2 [concrete = type] { // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %.loc5_19.2: type = type_literal type [concrete = type] // CHECK:STDOUT: } // CHECK:STDOUT: %U.loc5_15.2: type = symbolic_binding U, 1 [symbolic = %U.loc5_15.1 (constants.%U)] // CHECK:STDOUT: } // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type.357] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%Outer.387 // CHECK:STDOUT: .Inner = %Inner.decl // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic class @Inner(@Outer.%T.loc4_13.2: type, %U.loc5_15.2: type) { // CHECK:STDOUT: %U.loc5_15.1: type = symbolic_binding U, 1 [symbolic = %U.loc5_15.1 (constants.%U)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: // CHECK:STDOUT: class { // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type.357] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%Inner.e21 // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @__global_init() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %DefaultOrUnformed.facet: %DefaultOrUnformed.type = facet_value constants.%Outer.b7c, (constants.%DefaultOrUnformed.impl_witness.8e7) [concrete = constants.%DefaultOrUnformed.facet] // CHECK:STDOUT: %.loc9_18.1: %DefaultOrUnformed.type = converted constants.%Outer.b7c, %DefaultOrUnformed.facet [concrete = constants.%DefaultOrUnformed.facet] // CHECK:STDOUT: %as_type: type = facet_access_type %.loc9_18.1 [concrete = constants.%Outer.b7c] // CHECK:STDOUT: %.loc9_18.2: type = converted %.loc9_18.1, %as_type [concrete = constants.%Outer.b7c] // CHECK:STDOUT: %T.as.DefaultOrUnformed.impl.Op.specific_fn: = specific_function constants.%T.as.DefaultOrUnformed.impl.Op.3a0, @T.as.DefaultOrUnformed.impl.Op(constants.%Outer.b7c) [concrete = constants.%T.as.DefaultOrUnformed.impl.Op.specific_fn] // CHECK:STDOUT: %.loc9_1: ref %Outer.b7c = splice_block file.%v.var [concrete = file.%v.var] {} // CHECK:STDOUT: %T.as.DefaultOrUnformed.impl.Op.call: init %Outer.b7c to %.loc9_1 = call %T.as.DefaultOrUnformed.impl.Op.specific_fn() // CHECK:STDOUT: assign file.%v.var, %T.as.DefaultOrUnformed.impl.Op.call // CHECK:STDOUT: %v.ref: ref %Outer.b7c = name_ref v, file.%v [concrete = file.%v.var] // CHECK:STDOUT: %.loc19: %Inner.304 = converted %v.ref, [concrete = ] // CHECK:STDOUT: assign file.%w.var, // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @Outer(constants.%T) { // CHECK:STDOUT: %T.loc4_13.1 => constants.%T // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @Inner(constants.%T, constants.%U) { // CHECK:STDOUT: %U.loc5_15.1 => constants.%U // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @Outer(constants.%ptr.c28) { // CHECK:STDOUT: %T.loc4_13.1 => constants.%ptr.c28 // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %Inner.type => constants.%Inner.type.cd9 // CHECK:STDOUT: %Inner.generic => constants.%Inner.generic.0f6 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @Inner(constants.%ptr.c28, constants.%ptr.1bb) { // CHECK:STDOUT: %U.loc5_15.1 => constants.%ptr.1bb // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- fail_int_value.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %type: type = facet_type [concrete] // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self] // CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [concrete] // CHECK:STDOUT: %Int.type: type = generic_class_type @Int [concrete] // CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] // CHECK:STDOUT: %Int.generic: %Int.type = struct_value () [concrete] // CHECK:STDOUT: %i32: type = class_type @Int, @Int(%int_32) [concrete] // CHECK:STDOUT: %pattern_type.7ce: type = pattern_type %i32 [concrete] // CHECK:STDOUT: %N.5de: %i32 = symbolic_binding N, 0 [symbolic] // CHECK:STDOUT: %C.type: type = generic_class_type @C [concrete] // CHECK:STDOUT: %C.generic: %C.type = struct_value () [concrete] // CHECK:STDOUT: %C.ad6: type = class_type @C, @C(%N.5de) [symbolic] // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %complete_type.357: = complete_type_witness %empty_struct_type [concrete] // CHECK:STDOUT: %int_123.fff: Core.IntLiteral = int_value 123 [concrete] // CHECK:STDOUT: %ImplicitAs.type.cc7: type = generic_interface_type @ImplicitAs [concrete] // CHECK:STDOUT: %ImplicitAs.generic: %ImplicitAs.type.cc7 = struct_value () [concrete] // CHECK:STDOUT: %ImplicitAs.type.e8c: type = facet_type <@ImplicitAs, @ImplicitAs(%i32)> [concrete] // CHECK:STDOUT: %To: Core.IntLiteral = symbolic_binding To, 0 [symbolic] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.4e6: type = fn_type @Core.IntLiteral.as.ImplicitAs.impl.Convert, @Core.IntLiteral.as.ImplicitAs.impl(%To) [symbolic] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.3c2: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.4e6 = struct_value () [symbolic] // CHECK:STDOUT: %ImplicitAs.impl_witness.6bc: = impl_witness imports.%ImplicitAs.impl_witness_table.74f, @Core.IntLiteral.as.ImplicitAs.impl(%int_32) [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.e0d: type = fn_type @Core.IntLiteral.as.ImplicitAs.impl.Convert, @Core.IntLiteral.as.ImplicitAs.impl(%int_32) [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.e0d = struct_value () [concrete] // CHECK:STDOUT: %ImplicitAs.facet: %ImplicitAs.type.e8c = facet_value Core.IntLiteral, (%ImplicitAs.impl_witness.6bc) [concrete] // CHECK:STDOUT: %ImplicitAs.WithSelf.Convert.type.b37: type = fn_type @ImplicitAs.WithSelf.Convert, @ImplicitAs.WithSelf(%i32, %ImplicitAs.facet) [concrete] // CHECK:STDOUT: %.545: type = fn_type_with_self_type %ImplicitAs.WithSelf.Convert.type.b37, %ImplicitAs.facet [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.bound: = bound_method %int_123.fff, %Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5 [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn: = specific_function %Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5, @Core.IntLiteral.as.ImplicitAs.impl.Convert(%int_32) [concrete] // CHECK:STDOUT: %bound_method: = bound_method %int_123.fff, %Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn [concrete] // CHECK:STDOUT: %int_123.f7f: %i32 = int_value 123 [concrete] // CHECK:STDOUT: %C.9a3: type = class_type @C, @C(%int_123.f7f) [concrete] // CHECK:STDOUT: %pattern_type.a2c: type = pattern_type %C.9a3 [concrete] // CHECK:STDOUT: %empty_tuple: %empty_tuple.type = tuple_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: .Int = %Core.Int // CHECK:STDOUT: .ImplicitAs = %Core.ImplicitAs // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: %Core.Int: %Int.type = import_ref Core//prelude/parts/int, Int, loaded [concrete = constants.%Int.generic] // CHECK:STDOUT: %Core.ImplicitAs: %ImplicitAs.type.cc7 = import_ref Core//prelude/parts/as, ImplicitAs, loaded [concrete = constants.%ImplicitAs.generic] // CHECK:STDOUT: %Core.import_ref.42d: @Core.IntLiteral.as.ImplicitAs.impl.%Core.IntLiteral.as.ImplicitAs.impl.Convert.type (%Core.IntLiteral.as.ImplicitAs.impl.Convert.type.4e6) = import_ref Core//prelude/parts/int, loc{{\d+_\d+}}, loaded [symbolic = @Core.IntLiteral.as.ImplicitAs.impl.%Core.IntLiteral.as.ImplicitAs.impl.Convert (constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.3c2)] // CHECK:STDOUT: %ImplicitAs.impl_witness_table.74f = impl_witness_table (%Core.import_ref.42d), @Core.IntLiteral.as.ImplicitAs.impl [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .C = %C.decl // CHECK:STDOUT: .v = %v // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %C.decl: %C.type = class_decl @C [concrete = constants.%C.generic] { // CHECK:STDOUT: %N.patt: %pattern_type.7ce = symbolic_binding_pattern N, 0 [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %.loc4: type = splice_block %i32 [concrete = constants.%i32] { // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %i32: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: } // CHECK:STDOUT: %N.loc4_9.2: %i32 = symbolic_binding N, 0 [symbolic = %N.loc4_9.1 (constants.%N.5de)] // CHECK:STDOUT: } // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %v.patt: %pattern_type.a2c = ref_binding_pattern v [concrete] // CHECK:STDOUT: %v.var_patt: %pattern_type.a2c = var_pattern %v.patt [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %v.var: ref %C.9a3 = var %v.var_patt [concrete] // CHECK:STDOUT: %.loc13_13.1: type = splice_block %C [concrete = constants.%C.9a3] { // CHECK:STDOUT: %C.ref: %C.type = name_ref C, %C.decl [concrete = constants.%C.generic] // CHECK:STDOUT: %int_123: Core.IntLiteral = int_value 123 [concrete = constants.%int_123.fff] // CHECK:STDOUT: %impl.elem0: %.545 = impl_witness_access constants.%ImplicitAs.impl_witness.6bc, element0 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5] // CHECK:STDOUT: %bound_method.loc13_13.1: = bound_method %int_123, %impl.elem0 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.bound] // CHECK:STDOUT: %specific_fn: = specific_function %impl.elem0, @Core.IntLiteral.as.ImplicitAs.impl.Convert(constants.%int_32) [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn] // CHECK:STDOUT: %bound_method.loc13_13.2: = bound_method %int_123, %specific_fn [concrete = constants.%bound_method] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.call: init %i32 = call %bound_method.loc13_13.2(%int_123) [concrete = constants.%int_123.f7f] // CHECK:STDOUT: %.loc13_13.2: %i32 = value_of_initializer %Core.IntLiteral.as.ImplicitAs.impl.Convert.call [concrete = constants.%int_123.f7f] // CHECK:STDOUT: %.loc13_13.3: %i32 = converted %int_123, %.loc13_13.2 [concrete = constants.%int_123.f7f] // CHECK:STDOUT: %C: type = class_type @C, @C(constants.%int_123.f7f) [concrete = constants.%C.9a3] // CHECK:STDOUT: } // CHECK:STDOUT: %v: ref %C.9a3 = ref_binding v, %v.var [concrete = %v.var] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic class @C(%N.loc4_9.2: %i32) { // CHECK:STDOUT: %N.loc4_9.1: %i32 = symbolic_binding N, 0 [symbolic = %N.loc4_9.1 (constants.%N.5de)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: // CHECK:STDOUT: class { // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type.357] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%C.ad6 // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @__global_init() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %.loc13_18: %empty_tuple.type = tuple_literal () [concrete = constants.%empty_tuple] // CHECK:STDOUT: %.loc13_1: %C.9a3 = converted %.loc13_18, [concrete = ] // CHECK:STDOUT: assign file.%v.var, // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @C(constants.%N.5de) { // CHECK:STDOUT: %N.loc4_9.1 => constants.%N.5de // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @C(constants.%int_123.f7f) { // CHECK:STDOUT: %N.loc4_9.1 => constants.%int_123.f7f // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- fail_class_param.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %D: type = class_type @D [concrete] // CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [concrete] // CHECK:STDOUT: %Int.type: type = generic_class_type @Int [concrete] // CHECK:STDOUT: %Int.generic: %Int.type = struct_value () [concrete] // CHECK:STDOUT: %i32: type = class_type @Int, @Int(%int_32) [concrete] // CHECK:STDOUT: %D.elem: type = unbound_element_type %D, %i32 [concrete] // CHECK:STDOUT: %struct_type.a.b.501: type = struct_type {.a: %i32, .b: %i32} [concrete] // CHECK:STDOUT: %complete_type.705: = complete_type_witness %struct_type.a.b.501 [concrete] // CHECK:STDOUT: %type: type = facet_type [concrete] // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self] // CHECK:STDOUT: %pattern_type.9c8: type = pattern_type %D [concrete] // CHECK:STDOUT: %F: %D = symbolic_binding F, 0 [symbolic] // CHECK:STDOUT: %E.type: type = generic_class_type @E [concrete] // CHECK:STDOUT: %E.generic: %E.type = struct_value () [concrete] // CHECK:STDOUT: %E: type = class_type @E, @E(%F) [symbolic] // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %complete_type.357: = complete_type_witness %empty_struct_type [concrete] // CHECK:STDOUT: %int_1.5b8: Core.IntLiteral = int_value 1 [concrete] // CHECK:STDOUT: %int_2.ecc: Core.IntLiteral = int_value 2 [concrete] // CHECK:STDOUT: %struct_type.a.b.cfd: type = struct_type {.a: Core.IntLiteral, .b: Core.IntLiteral} [concrete] // CHECK:STDOUT: %struct.4aa: %struct_type.a.b.cfd = struct_value (%int_1.5b8, %int_2.ecc) [concrete] // CHECK:STDOUT: %ImplicitAs.type.cc7: type = generic_interface_type @ImplicitAs [concrete] // CHECK:STDOUT: %ImplicitAs.generic: %ImplicitAs.type.cc7 = struct_value () [concrete] // CHECK:STDOUT: %ImplicitAs.type.e8c: type = facet_type <@ImplicitAs, @ImplicitAs(%i32)> [concrete] // CHECK:STDOUT: %To: Core.IntLiteral = symbolic_binding To, 0 [symbolic] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.4e6: type = fn_type @Core.IntLiteral.as.ImplicitAs.impl.Convert, @Core.IntLiteral.as.ImplicitAs.impl(%To) [symbolic] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.3c2: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.4e6 = struct_value () [symbolic] // CHECK:STDOUT: %ImplicitAs.impl_witness.6bc: = impl_witness imports.%ImplicitAs.impl_witness_table.74f, @Core.IntLiteral.as.ImplicitAs.impl(%int_32) [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.e0d: type = fn_type @Core.IntLiteral.as.ImplicitAs.impl.Convert, @Core.IntLiteral.as.ImplicitAs.impl(%int_32) [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.e0d = struct_value () [concrete] // CHECK:STDOUT: %ImplicitAs.facet: %ImplicitAs.type.e8c = facet_value Core.IntLiteral, (%ImplicitAs.impl_witness.6bc) [concrete] // CHECK:STDOUT: %ImplicitAs.WithSelf.Convert.type.b37: type = fn_type @ImplicitAs.WithSelf.Convert, @ImplicitAs.WithSelf(%i32, %ImplicitAs.facet) [concrete] // CHECK:STDOUT: %.545: type = fn_type_with_self_type %ImplicitAs.WithSelf.Convert.type.b37, %ImplicitAs.facet [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.bound.215: = bound_method %int_1.5b8, %Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5 [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn: = specific_function %Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5, @Core.IntLiteral.as.ImplicitAs.impl.Convert(%int_32) [concrete] // CHECK:STDOUT: %bound_method.38b: = bound_method %int_1.5b8, %Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn [concrete] // CHECK:STDOUT: %int_1.5d2: %i32 = int_value 1 [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.bound.4e5: = bound_method %int_2.ecc, %Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5 [concrete] // CHECK:STDOUT: %bound_method.646: = bound_method %int_2.ecc, %Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn [concrete] // CHECK:STDOUT: %int_2.ef8: %i32 = int_value 2 [concrete] // CHECK:STDOUT: %D.val.525: %D = struct_value (%int_1.5d2, %int_2.ef8) [concrete] // CHECK:STDOUT: %empty_struct: %empty_struct_type = struct_value () [concrete] // CHECK:STDOUT: %int_3.1ba: Core.IntLiteral = int_value 3 [concrete] // CHECK:STDOUT: %int_4.0c1: Core.IntLiteral = int_value 4 [concrete] // CHECK:STDOUT: %struct.cb7: %struct_type.a.b.cfd = struct_value (%int_3.1ba, %int_4.0c1) [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.bound.061: = bound_method %int_3.1ba, %Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5 [concrete] // CHECK:STDOUT: %bound_method.fa7: = bound_method %int_3.1ba, %Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn [concrete] // CHECK:STDOUT: %int_3.822: %i32 = int_value 3 [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.bound.f0c: = bound_method %int_4.0c1, %Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5 [concrete] // CHECK:STDOUT: %bound_method.6d7: = bound_method %int_4.0c1, %Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn [concrete] // CHECK:STDOUT: %int_4.940: %i32 = int_value 4 [concrete] // CHECK:STDOUT: %D.val.659: %D = struct_value (%int_3.822, %int_4.940) [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: .Int = %Core.Int // CHECK:STDOUT: .ImplicitAs = %Core.ImplicitAs // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: %Core.Int: %Int.type = import_ref Core//prelude/parts/int, Int, loaded [concrete = constants.%Int.generic] // CHECK:STDOUT: %Core.ImplicitAs: %ImplicitAs.type.cc7 = import_ref Core//prelude/parts/as, ImplicitAs, loaded [concrete = constants.%ImplicitAs.generic] // CHECK:STDOUT: %Core.import_ref.42d: @Core.IntLiteral.as.ImplicitAs.impl.%Core.IntLiteral.as.ImplicitAs.impl.Convert.type (%Core.IntLiteral.as.ImplicitAs.impl.Convert.type.4e6) = import_ref Core//prelude/parts/int, loc{{\d+_\d+}}, loaded [symbolic = @Core.IntLiteral.as.ImplicitAs.impl.%Core.IntLiteral.as.ImplicitAs.impl.Convert (constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.3c2)] // CHECK:STDOUT: %ImplicitAs.impl_witness_table.74f = impl_witness_table (%Core.import_ref.42d), @Core.IntLiteral.as.ImplicitAs.impl [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .D = %D.decl // CHECK:STDOUT: .E = %E.decl // CHECK:STDOUT: .g = %g // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %D.decl: type = class_decl @D [concrete = constants.%D] {} {} // CHECK:STDOUT: %E.decl: %E.type = class_decl @E [concrete = constants.%E.generic] { // CHECK:STDOUT: %F.patt: %pattern_type.9c8 = symbolic_binding_pattern F, 0 [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %.loc9: type = splice_block %D.ref [concrete = constants.%D] { // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %D.ref: type = name_ref D, file.%D.decl [concrete = constants.%D] // CHECK:STDOUT: } // CHECK:STDOUT: %F.loc9_9.2: %D = symbolic_binding F, 0 [symbolic = %F.loc9_9.1 (constants.%F)] // CHECK:STDOUT: } // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %g.patt: = ref_binding_pattern g [concrete] // CHECK:STDOUT: %g.var_patt: = var_pattern %g.patt [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %g.var: ref = var %g.var_patt [concrete = ] // CHECK:STDOUT: %.1: = splice_block [concrete = ] { // CHECK:STDOUT: %E.ref: %E.type = name_ref E, %E.decl [concrete = constants.%E.generic] // CHECK:STDOUT: %int_1: Core.IntLiteral = int_value 1 [concrete = constants.%int_1.5b8] // CHECK:STDOUT: %int_2: Core.IntLiteral = int_value 2 [concrete = constants.%int_2.ecc] // CHECK:STDOUT: %.loc25_25.1: %struct_type.a.b.cfd = struct_literal (%int_1, %int_2) [concrete = constants.%struct.4aa] // CHECK:STDOUT: %impl.elem0.loc25_25.1: %.545 = impl_witness_access constants.%ImplicitAs.impl_witness.6bc, element0 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5] // CHECK:STDOUT: %bound_method.loc25_25.1: = bound_method %int_1, %impl.elem0.loc25_25.1 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.bound.215] // CHECK:STDOUT: %specific_fn.loc25_25.1: = specific_function %impl.elem0.loc25_25.1, @Core.IntLiteral.as.ImplicitAs.impl.Convert(constants.%int_32) [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn] // CHECK:STDOUT: %bound_method.loc25_25.2: = bound_method %int_1, %specific_fn.loc25_25.1 [concrete = constants.%bound_method.38b] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc25_25.1: init %i32 = call %bound_method.loc25_25.2(%int_1) [concrete = constants.%int_1.5d2] // CHECK:STDOUT: %.loc25_25.2: init %i32 = converted %int_1, %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc25_25.1 [concrete = constants.%int_1.5d2] // CHECK:STDOUT: %.loc25_25.3: ref %D = temporary_storage // CHECK:STDOUT: %.loc25_25.4: ref %i32 = class_element_access %.loc25_25.3, element0 // CHECK:STDOUT: %.loc25_25.5: init %i32 to %.loc25_25.4 = in_place_init %.loc25_25.2 [concrete = constants.%int_1.5d2] // CHECK:STDOUT: %impl.elem0.loc25_25.2: %.545 = impl_witness_access constants.%ImplicitAs.impl_witness.6bc, element0 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5] // CHECK:STDOUT: %bound_method.loc25_25.3: = bound_method %int_2, %impl.elem0.loc25_25.2 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.bound.4e5] // CHECK:STDOUT: %specific_fn.loc25_25.2: = specific_function %impl.elem0.loc25_25.2, @Core.IntLiteral.as.ImplicitAs.impl.Convert(constants.%int_32) [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn] // CHECK:STDOUT: %bound_method.loc25_25.4: = bound_method %int_2, %specific_fn.loc25_25.2 [concrete = constants.%bound_method.646] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc25_25.2: init %i32 = call %bound_method.loc25_25.4(%int_2) [concrete = constants.%int_2.ef8] // CHECK:STDOUT: %.loc25_25.6: init %i32 = converted %int_2, %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc25_25.2 [concrete = constants.%int_2.ef8] // CHECK:STDOUT: %.loc25_25.7: ref %i32 = class_element_access %.loc25_25.3, element1 // CHECK:STDOUT: %.loc25_25.8: init %i32 to %.loc25_25.7 = in_place_init %.loc25_25.6 [concrete = constants.%int_2.ef8] // CHECK:STDOUT: %.loc25_25.9: init %D to %.loc25_25.3 = class_init (%.loc25_25.5, %.loc25_25.8) [concrete = constants.%D.val.525] // CHECK:STDOUT: %.loc25_26.1: init %D = converted %.loc25_25.1, %.loc25_25.9 [concrete = constants.%D.val.525] // CHECK:STDOUT: %.loc25_26.2: ref %D = temporary %.loc25_25.3, %.loc25_26.1 // CHECK:STDOUT: %.loc25_26.3: %D = acquire_value %.loc25_26.2 // CHECK:STDOUT: } // CHECK:STDOUT: %g: ref = ref_binding g, [concrete = ] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @D { // CHECK:STDOUT: %i32.loc5: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %.loc5: %D.elem = field_decl a, element0 [concrete] // CHECK:STDOUT: %i32.loc6: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %.loc6: %D.elem = field_decl b, element1 [concrete] // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%struct_type.a.b.501 [concrete = constants.%complete_type.705] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%D // CHECK:STDOUT: .a = %.loc5 // CHECK:STDOUT: .b = %.loc6 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic class @E(%F.loc9_9.2: %D) { // CHECK:STDOUT: %F.loc9_9.1: %D = symbolic_binding F, 0 [symbolic = %F.loc9_9.1 (constants.%F)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: // CHECK:STDOUT: class { // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type.357] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%E // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @__global_init() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %.loc25_31: %empty_struct_type = struct_literal () [concrete = constants.%empty_struct] // CHECK:STDOUT: %E.ref: %E.type = name_ref E, file.%E.decl [concrete = constants.%E.generic] // CHECK:STDOUT: %int_3: Core.IntLiteral = int_value 3 [concrete = constants.%int_3.1ba] // CHECK:STDOUT: %int_4: Core.IntLiteral = int_value 4 [concrete = constants.%int_4.0c1] // CHECK:STDOUT: %.loc25_53.1: %struct_type.a.b.cfd = struct_literal (%int_3, %int_4) [concrete = constants.%struct.cb7] // CHECK:STDOUT: %D.ref: type = name_ref D, file.%D.decl [concrete = constants.%D] // CHECK:STDOUT: %impl.elem0.loc25_53.1: %.545 = impl_witness_access constants.%ImplicitAs.impl_witness.6bc, element0 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5] // CHECK:STDOUT: %bound_method.loc25_53.1: = bound_method %int_3, %impl.elem0.loc25_53.1 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.bound.061] // CHECK:STDOUT: %specific_fn.loc25_53.1: = specific_function %impl.elem0.loc25_53.1, @Core.IntLiteral.as.ImplicitAs.impl.Convert(constants.%int_32) [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn] // CHECK:STDOUT: %bound_method.loc25_53.2: = bound_method %int_3, %specific_fn.loc25_53.1 [concrete = constants.%bound_method.fa7] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc25_53.1: init %i32 = call %bound_method.loc25_53.2(%int_3) [concrete = constants.%int_3.822] // CHECK:STDOUT: %.loc25_53.2: init %i32 = converted %int_3, %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc25_53.1 [concrete = constants.%int_3.822] // CHECK:STDOUT: %.loc25_53.3: ref %D = temporary_storage // CHECK:STDOUT: %.loc25_53.4: ref %i32 = class_element_access %.loc25_53.3, element0 // CHECK:STDOUT: %.loc25_53.5: init %i32 to %.loc25_53.4 = in_place_init %.loc25_53.2 [concrete = constants.%int_3.822] // CHECK:STDOUT: %impl.elem0.loc25_53.2: %.545 = impl_witness_access constants.%ImplicitAs.impl_witness.6bc, element0 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5] // CHECK:STDOUT: %bound_method.loc25_53.3: = bound_method %int_4, %impl.elem0.loc25_53.2 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.bound.f0c] // CHECK:STDOUT: %specific_fn.loc25_53.2: = specific_function %impl.elem0.loc25_53.2, @Core.IntLiteral.as.ImplicitAs.impl.Convert(constants.%int_32) [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn] // CHECK:STDOUT: %bound_method.loc25_53.4: = bound_method %int_4, %specific_fn.loc25_53.2 [concrete = constants.%bound_method.6d7] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc25_53.2: init %i32 = call %bound_method.loc25_53.4(%int_4) [concrete = constants.%int_4.940] // CHECK:STDOUT: %.loc25_53.6: init %i32 = converted %int_4, %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc25_53.2 [concrete = constants.%int_4.940] // CHECK:STDOUT: %.loc25_53.7: ref %i32 = class_element_access %.loc25_53.3, element1 // CHECK:STDOUT: %.loc25_53.8: init %i32 to %.loc25_53.7 = in_place_init %.loc25_53.6 [concrete = constants.%int_4.940] // CHECK:STDOUT: %.loc25_53.9: init %D to %.loc25_53.3 = class_init (%.loc25_53.5, %.loc25_53.8) [concrete = constants.%D.val.659] // CHECK:STDOUT: %.loc25_55.1: init %D = converted %.loc25_53.1, %.loc25_53.9 [concrete = constants.%D.val.659] // CHECK:STDOUT: %.loc25_55.2: ref %D = temporary %.loc25_53.3, %.loc25_55.1 // CHECK:STDOUT: %.loc25_55.3: %D = acquire_value %.loc25_55.2 // CHECK:STDOUT: assign file.%g.var, // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @E(constants.%F) { // CHECK:STDOUT: %F.loc9_9.1 => constants.%F // CHECK:STDOUT: } // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/class/implicit_import.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/none.carbon // TODO: Add ranges and switch to "--dump-sem-ir-ranges=only". // EXTRA-ARGS: --dump-sem-ir-ranges=if-present // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/class/implicit_import.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/class/implicit_import.carbon // --- basic.carbon library "[[@TEST_NAME]]"; class C; // --- basic.impl.carbon impl library "[[@TEST_NAME]]"; class C {} // --- redecl_after_def.carbon library "[[@TEST_NAME]]"; class C {} // --- fail_redecl_after_def.impl.carbon impl library "[[@TEST_NAME]]"; // CHECK:STDERR: fail_redecl_after_def.impl.carbon:[[@LINE+8]]:1: error: redeclaration of `class C` is redundant [RedeclRedundant] // CHECK:STDERR: class C; // CHECK:STDERR: ^~~~~~~~ // CHECK:STDERR: fail_redecl_after_def.impl.carbon:[[@LINE-5]]:1: in import [InImport] // CHECK:STDERR: redecl_after_def.carbon:4:1: note: previously declared here [RedeclPrevDecl] // CHECK:STDERR: class C {} // CHECK:STDERR: ^~~~~~~~~ // CHECK:STDERR: class C; // --- redef_after_def.carbon library "[[@TEST_NAME]]"; class C {} // --- fail_redef_after_def.impl.carbon impl library "[[@TEST_NAME]]"; // CHECK:STDERR: fail_redef_after_def.impl.carbon:[[@LINE+8]]:1: error: redefinition of `class C` [RedeclRedef] // CHECK:STDERR: class C {} // CHECK:STDERR: ^~~~~~~~~ // CHECK:STDERR: fail_redef_after_def.impl.carbon:[[@LINE-5]]:1: in import [InImport] // CHECK:STDERR: redef_after_def.carbon:4:1: note: previously defined here [RedeclPrevDef] // CHECK:STDERR: class C {} // CHECK:STDERR: ^~~~~~~~~ // CHECK:STDERR: class C {} // --- def_alias.carbon library "[[@TEST_NAME]]"; class C; alias B = C; // --- fail_def_alias.impl.carbon impl library "[[@TEST_NAME]]"; // CHECK:STDERR: fail_def_alias.impl.carbon:[[@LINE+8]]:7: error: duplicate name `B` being declared in the same scope [NameDeclDuplicate] // CHECK:STDERR: class B {} // CHECK:STDERR: ^ // CHECK:STDERR: fail_def_alias.impl.carbon:[[@LINE-5]]:1: in import [InImport] // CHECK:STDERR: def_alias.carbon:5:7: note: name is previously declared here [NameDeclPrevious] // CHECK:STDERR: alias B = C; // CHECK:STDERR: ^ // CHECK:STDERR: class B {} // CHECK:STDOUT: --- basic.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %C: type = class_type @C [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .C = %C.decl // CHECK:STDOUT: } // CHECK:STDOUT: %C.decl: type = class_decl @C [concrete = constants.%C] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @C; // CHECK:STDOUT: // CHECK:STDOUT: --- basic.impl.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %C: type = class_type @C [concrete] // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %complete_type: = complete_type_witness %empty_struct_type [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .C = %C.decl // CHECK:STDOUT: } // CHECK:STDOUT: %default.import.loc2_21.1 = import // CHECK:STDOUT: %default.import.loc2_21.2 = import // CHECK:STDOUT: %C.decl: type = class_decl @C [concrete = constants.%C] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @C { // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%C // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- redecl_after_def.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %C: type = class_type @C [concrete] // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %complete_type: = complete_type_witness %empty_struct_type [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .C = %C.decl // CHECK:STDOUT: } // CHECK:STDOUT: %C.decl: type = class_decl @C [concrete = constants.%C] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @C { // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%C // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- fail_redecl_after_def.impl.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %C: type = class_type @C [concrete] // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %complete_type: = complete_type_witness %empty_struct_type [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Main.import_ref.8f2: = import_ref Main//redecl_after_def, loc4_10, loaded [concrete = constants.%complete_type] // CHECK:STDOUT: %Main.import_ref.743 = import_ref Main//redecl_after_def, inst{{[0-9A-F]+}} [no loc], unloaded // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .C = %C.decl // CHECK:STDOUT: } // CHECK:STDOUT: %default.import.loc2_32.1 = import // CHECK:STDOUT: %default.import.loc2_32.2 = import // CHECK:STDOUT: %C.decl: type = class_decl @C [concrete = constants.%C] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @C { // CHECK:STDOUT: complete_type_witness = imports.%Main.import_ref.8f2 // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = imports.%Main.import_ref.743 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- redef_after_def.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %C: type = class_type @C [concrete] // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %complete_type: = complete_type_witness %empty_struct_type [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .C = %C.decl // CHECK:STDOUT: } // CHECK:STDOUT: %C.decl: type = class_decl @C [concrete = constants.%C] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @C { // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%C // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- fail_redef_after_def.impl.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %C.17abfe.1: type = class_type @C.1 [concrete] // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %complete_type: = complete_type_witness %empty_struct_type [concrete] // CHECK:STDOUT: %C.17abfe.2: type = class_type @C.loc12 [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Main.C: type = import_ref Main//redef_after_def, C, loaded [concrete = constants.%C.17abfe.1] // CHECK:STDOUT: %Main.import_ref.8f2: = import_ref Main//redef_after_def, loc4_10, loaded [concrete = constants.%complete_type] // CHECK:STDOUT: %Main.import_ref.743 = import_ref Main//redef_after_def, inst{{[0-9A-F]+}} [no loc], unloaded // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .C = imports.%Main.C // CHECK:STDOUT: } // CHECK:STDOUT: %default.import.loc2_31.1 = import // CHECK:STDOUT: %default.import.loc2_31.2 = import // CHECK:STDOUT: %C.decl: type = class_decl @C.loc12 [concrete = constants.%C.17abfe.2] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @C.1 [from "redef_after_def.carbon"] { // CHECK:STDOUT: complete_type_witness = imports.%Main.import_ref.8f2 // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = imports.%Main.import_ref.743 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @C.loc12 { // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%C.17abfe.2 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- def_alias.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %C: type = class_type @C [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .C = %C.decl // CHECK:STDOUT: .B = %B // CHECK:STDOUT: } // CHECK:STDOUT: %C.decl: type = class_decl @C [concrete = constants.%C] {} {} // CHECK:STDOUT: %C.ref: type = name_ref C, %C.decl [concrete = constants.%C] // CHECK:STDOUT: %B: type = alias_binding B, %C.decl [concrete = constants.%C] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @C; // CHECK:STDOUT: // CHECK:STDOUT: --- fail_def_alias.impl.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %C: type = class_type @C [concrete] // CHECK:STDOUT: %B: type = class_type @B [concrete] // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %complete_type: = complete_type_witness %empty_struct_type [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Main.C = import_ref Main//def_alias, C, unloaded // CHECK:STDOUT: %Main.B: type = import_ref Main//def_alias, B, loaded [concrete = constants.%C] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .C = imports.%Main.C // CHECK:STDOUT: .B = imports.%Main.B // CHECK:STDOUT: } // CHECK:STDOUT: %default.import.loc2_25.1 = import // CHECK:STDOUT: %default.import.loc2_25.2 = import // CHECK:STDOUT: %B.decl: type = class_decl @B [concrete = constants.%B] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @C [from "def_alias.carbon"]; // CHECK:STDOUT: // CHECK:STDOUT: class @B { // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%B // CHECK:STDOUT: } // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/class/import.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/int.carbon // TODO: Add ranges and switch to "--dump-sem-ir-ranges=only". // EXTRA-ARGS: --dump-sem-ir-ranges=if-present // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/class/import.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/class/import.carbon // --- a.carbon library "[[@TEST_NAME]]"; class Empty { } class Field { var x: i32; } class ForwardDeclared; class ForwardDeclared { fn F[self: Self](); fn G[ref self: Self](); } class Incomplete; // --- b.carbon library "[[@TEST_NAME]]"; import library "a"; fn Run() { var unused a: Empty = {}; var b: Field = {.x = 1}; b.x = 2; var c: ForwardDeclared = {}; c.F(); c.G(); var unused d: ForwardDeclared* = &c; var unused e: Incomplete*; } // CHECK:STDOUT: --- a.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %Empty: type = class_type @Empty [concrete] // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %complete_type.357: = complete_type_witness %empty_struct_type [concrete] // CHECK:STDOUT: %Field: type = class_type @Field [concrete] // CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [concrete] // CHECK:STDOUT: %Int.type: type = generic_class_type @Int [concrete] // CHECK:STDOUT: %Int.generic: %Int.type = struct_value () [concrete] // CHECK:STDOUT: %i32: type = class_type @Int, @Int(%int_32) [concrete] // CHECK:STDOUT: %Field.elem: type = unbound_element_type %Field, %i32 [concrete] // CHECK:STDOUT: %struct_type.x: type = struct_type {.x: %i32} [concrete] // CHECK:STDOUT: %complete_type.1ec: = complete_type_witness %struct_type.x [concrete] // CHECK:STDOUT: %ForwardDeclared: type = class_type @ForwardDeclared [concrete] // CHECK:STDOUT: %pattern_type.af1: type = pattern_type %ForwardDeclared [concrete] // CHECK:STDOUT: %ForwardDeclared.F.type: type = fn_type @ForwardDeclared.F [concrete] // CHECK:STDOUT: %ForwardDeclared.F: %ForwardDeclared.F.type = struct_value () [concrete] // CHECK:STDOUT: %ForwardDeclared.G.type: type = fn_type @ForwardDeclared.G [concrete] // CHECK:STDOUT: %ForwardDeclared.G: %ForwardDeclared.G.type = struct_value () [concrete] // CHECK:STDOUT: %Incomplete: type = class_type @Incomplete [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: .Int = %Core.Int // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: %Core.Int: %Int.type = import_ref Core//prelude/parts/int, Int, loaded [concrete = constants.%Int.generic] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .Empty = %Empty.decl // CHECK:STDOUT: .Field = %Field.decl // CHECK:STDOUT: .ForwardDeclared = %ForwardDeclared.decl.loc11 // CHECK:STDOUT: .Incomplete = %Incomplete.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %Empty.decl: type = class_decl @Empty [concrete = constants.%Empty] {} {} // CHECK:STDOUT: %Field.decl: type = class_decl @Field [concrete = constants.%Field] {} {} // CHECK:STDOUT: %ForwardDeclared.decl.loc11: type = class_decl @ForwardDeclared [concrete = constants.%ForwardDeclared] {} {} // CHECK:STDOUT: %ForwardDeclared.decl.loc13: type = class_decl @ForwardDeclared [concrete = constants.%ForwardDeclared] {} {} // CHECK:STDOUT: %Incomplete.decl: type = class_decl @Incomplete [concrete = constants.%Incomplete] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @Empty { // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type.357] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%Empty // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @Field { // CHECK:STDOUT: %i32: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %.loc8: %Field.elem = field_decl x, element0 [concrete] // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%struct_type.x [concrete = constants.%complete_type.1ec] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%Field // CHECK:STDOUT: .x = %.loc8 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @ForwardDeclared { // CHECK:STDOUT: %ForwardDeclared.F.decl: %ForwardDeclared.F.type = fn_decl @ForwardDeclared.F [concrete = constants.%ForwardDeclared.F] { // CHECK:STDOUT: %self.patt: %pattern_type.af1 = value_binding_pattern self [concrete] // CHECK:STDOUT: %self.param_patt: %pattern_type.af1 = value_param_pattern %self.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %self.param: %ForwardDeclared = value_param call_param0 // CHECK:STDOUT: %Self.ref: type = name_ref Self, constants.%ForwardDeclared [concrete = constants.%ForwardDeclared] // CHECK:STDOUT: %self: %ForwardDeclared = value_binding self, %self.param // CHECK:STDOUT: } // CHECK:STDOUT: %ForwardDeclared.G.decl: %ForwardDeclared.G.type = fn_decl @ForwardDeclared.G [concrete = constants.%ForwardDeclared.G] { // CHECK:STDOUT: %self.patt: %pattern_type.af1 = ref_binding_pattern self [concrete] // CHECK:STDOUT: %self.param_patt: %pattern_type.af1 = ref_param_pattern %self.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %self.param: ref %ForwardDeclared = ref_param call_param0 // CHECK:STDOUT: %Self.ref: type = name_ref Self, constants.%ForwardDeclared [concrete = constants.%ForwardDeclared] // CHECK:STDOUT: %self: ref %ForwardDeclared = ref_binding self, %self.param // CHECK:STDOUT: } // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type.357] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%ForwardDeclared // CHECK:STDOUT: .F = %ForwardDeclared.F.decl // CHECK:STDOUT: .G = %ForwardDeclared.G.decl // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @Incomplete; // CHECK:STDOUT: // CHECK:STDOUT: fn @ForwardDeclared.F(%self.param: %ForwardDeclared); // CHECK:STDOUT: // CHECK:STDOUT: fn @ForwardDeclared.G(%self.param: ref %ForwardDeclared); // CHECK:STDOUT: // CHECK:STDOUT: --- b.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %Run.type: type = fn_type @Run [concrete] // CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] // CHECK:STDOUT: %Run: %Run.type = struct_value () [concrete] // CHECK:STDOUT: %Empty: type = class_type @Empty [concrete] // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %complete_type.357: = complete_type_witness %empty_struct_type [concrete] // CHECK:STDOUT: %pattern_type.37d: type = pattern_type %Empty [concrete] // CHECK:STDOUT: %empty_struct: %empty_struct_type = struct_value () [concrete] // CHECK:STDOUT: %Empty.val: %Empty = struct_value () [concrete] // CHECK:STDOUT: %Field: type = class_type @Field [concrete] // CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [concrete] // CHECK:STDOUT: %i32: type = class_type @Int, @Int(%int_32) [concrete] // CHECK:STDOUT: %struct_type.x.767: type = struct_type {.x: %i32} [concrete] // CHECK:STDOUT: %complete_type.c07: = complete_type_witness %struct_type.x.767 [concrete] // CHECK:STDOUT: %pattern_type.729: type = pattern_type %Field [concrete] // CHECK:STDOUT: %int_1.5b8: Core.IntLiteral = int_value 1 [concrete] // CHECK:STDOUT: %struct_type.x.c96: type = struct_type {.x: Core.IntLiteral} [concrete] // CHECK:STDOUT: %struct: %struct_type.x.c96 = struct_value (%int_1.5b8) [concrete] // CHECK:STDOUT: %ImplicitAs.type.cc7: type = generic_interface_type @ImplicitAs [concrete] // CHECK:STDOUT: %ImplicitAs.generic: %ImplicitAs.type.cc7 = struct_value () [concrete] // CHECK:STDOUT: %ImplicitAs.type.cf3: type = facet_type <@ImplicitAs, @ImplicitAs(%i32)> [concrete] // CHECK:STDOUT: %To: Core.IntLiteral = symbolic_binding To, 0 [symbolic] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.004: type = fn_type @Core.IntLiteral.as.ImplicitAs.impl.Convert, @Core.IntLiteral.as.ImplicitAs.impl(%To) [symbolic] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.255: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.004 = struct_value () [symbolic] // CHECK:STDOUT: %ImplicitAs.impl_witness.58d: = impl_witness imports.%ImplicitAs.impl_witness_table.e45, @Core.IntLiteral.as.ImplicitAs.impl(%int_32) [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.199: type = fn_type @Core.IntLiteral.as.ImplicitAs.impl.Convert, @Core.IntLiteral.as.ImplicitAs.impl(%int_32) [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.5f4: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.199 = struct_value () [concrete] // CHECK:STDOUT: %ImplicitAs.facet: %ImplicitAs.type.cf3 = facet_value Core.IntLiteral, (%ImplicitAs.impl_witness.58d) [concrete] // CHECK:STDOUT: %ImplicitAs.WithSelf.Convert.type.979: type = fn_type @ImplicitAs.WithSelf.Convert, @ImplicitAs.WithSelf(%i32, %ImplicitAs.facet) [concrete] // CHECK:STDOUT: %.7c3: type = fn_type_with_self_type %ImplicitAs.WithSelf.Convert.type.979, %ImplicitAs.facet [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.bound.f07: = bound_method %int_1.5b8, %Core.IntLiteral.as.ImplicitAs.impl.Convert.5f4 [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn: = specific_function %Core.IntLiteral.as.ImplicitAs.impl.Convert.5f4, @Core.IntLiteral.as.ImplicitAs.impl.Convert(%int_32) [concrete] // CHECK:STDOUT: %bound_method.307: = bound_method %int_1.5b8, %Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn [concrete] // CHECK:STDOUT: %int_1.47b: %i32 = int_value 1 [concrete] // CHECK:STDOUT: %Field.val: %Field = struct_value (%int_1.47b) [concrete] // CHECK:STDOUT: %Field.elem: type = unbound_element_type %Field, %i32 [concrete] // CHECK:STDOUT: %int_2.ecc: Core.IntLiteral = int_value 2 [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.bound.1eb: = bound_method %int_2.ecc, %Core.IntLiteral.as.ImplicitAs.impl.Convert.5f4 [concrete] // CHECK:STDOUT: %bound_method.ef3: = bound_method %int_2.ecc, %Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn [concrete] // CHECK:STDOUT: %int_2.d0d: %i32 = int_value 2 [concrete] // CHECK:STDOUT: %ForwardDeclared.20f323.1: type = class_type @ForwardDeclared.1 [concrete] // CHECK:STDOUT: %pattern_type.af1: type = pattern_type %ForwardDeclared.20f323.1 [concrete] // CHECK:STDOUT: %ForwardDeclared.val: %ForwardDeclared.20f323.1 = struct_value () [concrete] // CHECK:STDOUT: %ForwardDeclared.F.type: type = fn_type @ForwardDeclared.F [concrete] // CHECK:STDOUT: %ForwardDeclared.F: %ForwardDeclared.F.type = struct_value () [concrete] // CHECK:STDOUT: %ForwardDeclared.G.type: type = fn_type @ForwardDeclared.G [concrete] // CHECK:STDOUT: %ForwardDeclared.G: %ForwardDeclared.G.type = struct_value () [concrete] // CHECK:STDOUT: %ptr.006: type = ptr_type %ForwardDeclared.20f323.1 [concrete] // CHECK:STDOUT: %pattern_type.77e: type = pattern_type %ptr.006 [concrete] // CHECK:STDOUT: %Copy.type: type = facet_type <@Copy> [concrete] // CHECK:STDOUT: %T.67d: type = symbolic_binding T, 0 [symbolic] // CHECK:STDOUT: %ptr.as.Copy.impl.Op.type.2d4: type = fn_type @ptr.as.Copy.impl.Op, @ptr.as.Copy.impl(%T.67d) [symbolic] // CHECK:STDOUT: %ptr.as.Copy.impl.Op.74e: %ptr.as.Copy.impl.Op.type.2d4 = struct_value () [symbolic] // CHECK:STDOUT: %Copy.impl_witness.bef: = impl_witness imports.%Copy.impl_witness_table.c3a, @ptr.as.Copy.impl(%ForwardDeclared.20f323.1) [concrete] // CHECK:STDOUT: %ptr.as.Copy.impl.Op.type.5ba: type = fn_type @ptr.as.Copy.impl.Op, @ptr.as.Copy.impl(%ForwardDeclared.20f323.1) [concrete] // CHECK:STDOUT: %ptr.as.Copy.impl.Op.d3c: %ptr.as.Copy.impl.Op.type.5ba = struct_value () [concrete] // CHECK:STDOUT: %Copy.facet: %Copy.type = facet_value %ptr.006, (%Copy.impl_witness.bef) [concrete] // CHECK:STDOUT: %Copy.WithSelf.Op.type.4e2: type = fn_type @Copy.WithSelf.Op, @Copy.WithSelf(%Copy.facet) [concrete] // CHECK:STDOUT: %.89b: type = fn_type_with_self_type %Copy.WithSelf.Op.type.4e2, %Copy.facet [concrete] // CHECK:STDOUT: %ptr.as.Copy.impl.Op.specific_fn: = specific_function %ptr.as.Copy.impl.Op.d3c, @ptr.as.Copy.impl.Op(%ForwardDeclared.20f323.1) [concrete] // CHECK:STDOUT: %Incomplete: type = class_type @Incomplete [concrete] // CHECK:STDOUT: %ptr.8c3: type = ptr_type %Incomplete [concrete] // CHECK:STDOUT: %pattern_type.b98: type = pattern_type %ptr.8c3 [concrete] // CHECK:STDOUT: %DefaultOrUnformed.type: type = facet_type <@DefaultOrUnformed> [concrete] // CHECK:STDOUT: %T.as.DefaultOrUnformed.impl.Op.type.6c6: type = fn_type @T.as.DefaultOrUnformed.impl.Op, @T.as.DefaultOrUnformed.impl(%T.67d) [symbolic] // CHECK:STDOUT: %T.as.DefaultOrUnformed.impl.Op.ae1: %T.as.DefaultOrUnformed.impl.Op.type.6c6 = struct_value () [symbolic] // CHECK:STDOUT: %DefaultOrUnformed.impl_witness.fb9: = impl_witness imports.%DefaultOrUnformed.impl_witness_table, @T.as.DefaultOrUnformed.impl(%ptr.8c3) [concrete] // CHECK:STDOUT: %T.as.DefaultOrUnformed.impl.Op.type.3ad: type = fn_type @T.as.DefaultOrUnformed.impl.Op, @T.as.DefaultOrUnformed.impl(%ptr.8c3) [concrete] // CHECK:STDOUT: %T.as.DefaultOrUnformed.impl.Op.5f6: %T.as.DefaultOrUnformed.impl.Op.type.3ad = struct_value () [concrete] // CHECK:STDOUT: %DefaultOrUnformed.facet: %DefaultOrUnformed.type = facet_value %ptr.8c3, (%DefaultOrUnformed.impl_witness.fb9) [concrete] // CHECK:STDOUT: %T.as.DefaultOrUnformed.impl.Op.specific_fn: = specific_function %T.as.DefaultOrUnformed.impl.Op.5f6, @T.as.DefaultOrUnformed.impl.Op(%ptr.8c3) [concrete] // CHECK:STDOUT: %Destroy.type: type = facet_type <@Destroy> [concrete] // CHECK:STDOUT: %Destroy.Op.type.bae255.1: type = fn_type @Destroy.Op.loc18 [concrete] // CHECK:STDOUT: %Destroy.Op.651ba6.1: %Destroy.Op.type.bae255.1 = struct_value () [concrete] // CHECK:STDOUT: %Destroy.Op.type.bae255.2: type = fn_type @Destroy.Op.loc16 [concrete] // CHECK:STDOUT: %Destroy.Op.651ba6.2: %Destroy.Op.type.bae255.2 = struct_value () [concrete] // CHECK:STDOUT: %Destroy.Op.type.bae255.3: type = fn_type @Destroy.Op.loc12 [concrete] // CHECK:STDOUT: %Destroy.Op.651ba6.3: %Destroy.Op.type.bae255.3 = struct_value () [concrete] // CHECK:STDOUT: %Destroy.Op.type.bae255.4: type = fn_type @Destroy.Op.loc9 [concrete] // CHECK:STDOUT: %Destroy.Op.651ba6.4: %Destroy.Op.type.bae255.4 = struct_value () [concrete] // CHECK:STDOUT: %Destroy.Op.type.bae255.5: type = fn_type @Destroy.Op.loc7 [concrete] // CHECK:STDOUT: %Destroy.Op.651ba6.5: %Destroy.Op.type.bae255.5 = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Main.Empty: type = import_ref Main//a, Empty, loaded [concrete = constants.%Empty] // CHECK:STDOUT: %Main.Field: type = import_ref Main//a, Field, loaded [concrete = constants.%Field] // CHECK:STDOUT: %Main.ForwardDeclared: type = import_ref Main//a, ForwardDeclared, loaded [concrete = constants.%ForwardDeclared.20f323.1] // CHECK:STDOUT: %Main.Incomplete: type = import_ref Main//a, Incomplete, loaded [concrete = constants.%Incomplete] // CHECK:STDOUT: %Core.ece: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: .ImplicitAs = %Core.ImplicitAs // CHECK:STDOUT: .Copy = %Core.Copy // CHECK:STDOUT: .DefaultOrUnformed = %Core.DefaultOrUnformed // CHECK:STDOUT: .Destroy = %Core.Destroy // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: %Main.import_ref.8f24d3.1: = import_ref Main//a, loc5_1, loaded [concrete = constants.%complete_type.357] // CHECK:STDOUT: %Main.import_ref.d52 = import_ref Main//a, inst{{[0-9A-F]+}} [no loc], unloaded // CHECK:STDOUT: %Main.import_ref.709: = import_ref Main//a, loc9_1, loaded [concrete = constants.%complete_type.c07] // CHECK:STDOUT: %Main.import_ref.07a = import_ref Main//a, inst{{[0-9A-F]+}} [no loc], unloaded // CHECK:STDOUT: %Main.import_ref.df7: %Field.elem = import_ref Main//a, loc8_8, loaded [concrete = %.afd] // CHECK:STDOUT: %Core.ImplicitAs: %ImplicitAs.type.cc7 = import_ref Core//prelude/parts/as, ImplicitAs, loaded [concrete = constants.%ImplicitAs.generic] // CHECK:STDOUT: %Core.import_ref.b25: @Core.IntLiteral.as.ImplicitAs.impl.%Core.IntLiteral.as.ImplicitAs.impl.Convert.type (%Core.IntLiteral.as.ImplicitAs.impl.Convert.type.004) = import_ref Core//prelude/parts/int, loc{{\d+_\d+}}, loaded [symbolic = @Core.IntLiteral.as.ImplicitAs.impl.%Core.IntLiteral.as.ImplicitAs.impl.Convert (constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.255)] // CHECK:STDOUT: %ImplicitAs.impl_witness_table.e45 = impl_witness_table (%Core.import_ref.b25), @Core.IntLiteral.as.ImplicitAs.impl [concrete] // CHECK:STDOUT: %.afd: %Field.elem = field_decl x, element0 [concrete] // CHECK:STDOUT: %Main.import_ref.8f24d3.2: = import_ref Main//a, loc16_1, loaded [concrete = constants.%complete_type.357] // CHECK:STDOUT: %Main.import_ref.a87cf6.1 = import_ref Main//a, inst{{[0-9A-F]+}} [no loc], unloaded // CHECK:STDOUT: %Main.import_ref.019: %ForwardDeclared.F.type = import_ref Main//a, loc14_21, loaded [concrete = constants.%ForwardDeclared.F] // CHECK:STDOUT: %Main.import_ref.a45: %ForwardDeclared.G.type = import_ref Main//a, loc15_25, loaded [concrete = constants.%ForwardDeclared.G] // CHECK:STDOUT: %Main.import_ref.8f24d3.3: = import_ref Main//a, loc16_1, loaded [concrete = constants.%complete_type.357] // CHECK:STDOUT: %Main.import_ref.a87cf6.2 = import_ref Main//a, inst{{[0-9A-F]+}} [no loc], unloaded // CHECK:STDOUT: %Main.import_ref.a44 = import_ref Main//a, loc14_21, unloaded // CHECK:STDOUT: %Main.import_ref.38a = import_ref Main//a, loc15_25, unloaded // CHECK:STDOUT: %Core.Copy: type = import_ref Core//prelude/parts/copy, Copy, loaded [concrete = constants.%Copy.type] // CHECK:STDOUT: %Core.import_ref.203: @ptr.as.Copy.impl.%ptr.as.Copy.impl.Op.type (%ptr.as.Copy.impl.Op.type.2d4) = import_ref Core//prelude/parts/copy, loc{{\d+_\d+}}, loaded [symbolic = @ptr.as.Copy.impl.%ptr.as.Copy.impl.Op (constants.%ptr.as.Copy.impl.Op.74e)] // CHECK:STDOUT: %Copy.impl_witness_table.c3a = impl_witness_table (%Core.import_ref.203), @ptr.as.Copy.impl [concrete] // CHECK:STDOUT: %Core.DefaultOrUnformed: type = import_ref Core//prelude/parts/default, DefaultOrUnformed, loaded [concrete = constants.%DefaultOrUnformed.type] // CHECK:STDOUT: %Core.import_ref.01d: @T.as.DefaultOrUnformed.impl.%T.as.DefaultOrUnformed.impl.Op.type (%T.as.DefaultOrUnformed.impl.Op.type.6c6) = import_ref Core//prelude/parts/default, loc{{\d+_\d+}}, loaded [symbolic = @T.as.DefaultOrUnformed.impl.%T.as.DefaultOrUnformed.impl.Op (constants.%T.as.DefaultOrUnformed.impl.Op.ae1)] // CHECK:STDOUT: %DefaultOrUnformed.impl_witness_table = impl_witness_table (%Core.import_ref.01d), @T.as.DefaultOrUnformed.impl [concrete] // CHECK:STDOUT: %Core.Destroy: type = import_ref Core//prelude/parts/destroy, Destroy, loaded [concrete = constants.%Destroy.type] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Empty = imports.%Main.Empty // CHECK:STDOUT: .Field = imports.%Main.Field // CHECK:STDOUT: .ForwardDeclared = imports.%Main.ForwardDeclared // CHECK:STDOUT: .Incomplete = imports.%Main.Incomplete // CHECK:STDOUT: .Core = imports.%Core.ece // CHECK:STDOUT: .Run = %Run.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %default.import = import // CHECK:STDOUT: %Run.decl: %Run.type = fn_decl @Run [concrete = constants.%Run] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @Empty [from "a.carbon"] { // CHECK:STDOUT: complete_type_witness = imports.%Main.import_ref.8f24d3.1 // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = imports.%Main.import_ref.d52 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @Field [from "a.carbon"] { // CHECK:STDOUT: complete_type_witness = imports.%Main.import_ref.709 // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = imports.%Main.import_ref.07a // CHECK:STDOUT: .x = imports.%Main.import_ref.df7 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @ForwardDeclared.1 [from "a.carbon"] { // CHECK:STDOUT: complete_type_witness = imports.%Main.import_ref.8f24d3.2 // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = imports.%Main.import_ref.a87cf6.1 // CHECK:STDOUT: .F = imports.%Main.import_ref.019 // CHECK:STDOUT: .G = imports.%Main.import_ref.a45 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @ForwardDeclared.2 [from "a.carbon"] { // CHECK:STDOUT: complete_type_witness = imports.%Main.import_ref.8f24d3.3 // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = imports.%Main.import_ref.a87cf6.2 // CHECK:STDOUT: .F = imports.%Main.import_ref.a44 // CHECK:STDOUT: .G = imports.%Main.import_ref.38a // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @Incomplete [from "a.carbon"]; // CHECK:STDOUT: // CHECK:STDOUT: fn @Run() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %a.patt: %pattern_type.37d = ref_binding_pattern a [concrete] // CHECK:STDOUT: %a.var_patt: %pattern_type.37d = var_pattern %a.patt [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %a.var: ref %Empty = var %a.var_patt // CHECK:STDOUT: %.loc7_26.1: %empty_struct_type = struct_literal () [concrete = constants.%empty_struct] // CHECK:STDOUT: %.loc7_26.2: init %Empty to %a.var = class_init () [concrete = constants.%Empty.val] // CHECK:STDOUT: %.loc7_3: init %Empty = converted %.loc7_26.1, %.loc7_26.2 [concrete = constants.%Empty.val] // CHECK:STDOUT: assign %a.var, %.loc7_3 // CHECK:STDOUT: %Empty.ref: type = name_ref Empty, imports.%Main.Empty [concrete = constants.%Empty] // CHECK:STDOUT: %a: ref %Empty = ref_binding a, %a.var // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %b.patt: %pattern_type.729 = ref_binding_pattern b [concrete] // CHECK:STDOUT: %b.var_patt: %pattern_type.729 = var_pattern %b.patt [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %b.var: ref %Field = var %b.var_patt // CHECK:STDOUT: %int_1: Core.IntLiteral = int_value 1 [concrete = constants.%int_1.5b8] // CHECK:STDOUT: %.loc9_25.1: %struct_type.x.c96 = struct_literal (%int_1) [concrete = constants.%struct] // CHECK:STDOUT: %impl.elem0.loc9: %.7c3 = impl_witness_access constants.%ImplicitAs.impl_witness.58d, element0 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.5f4] // CHECK:STDOUT: %bound_method.loc9_25.1: = bound_method %int_1, %impl.elem0.loc9 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.bound.f07] // CHECK:STDOUT: %specific_fn.loc9: = specific_function %impl.elem0.loc9, @Core.IntLiteral.as.ImplicitAs.impl.Convert(constants.%int_32) [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn] // CHECK:STDOUT: %bound_method.loc9_25.2: = bound_method %int_1, %specific_fn.loc9 [concrete = constants.%bound_method.307] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc9: init %i32 = call %bound_method.loc9_25.2(%int_1) [concrete = constants.%int_1.47b] // CHECK:STDOUT: %.loc9_25.2: init %i32 = converted %int_1, %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc9 [concrete = constants.%int_1.47b] // CHECK:STDOUT: %.loc9_25.3: ref %i32 = class_element_access %b.var, element0 // CHECK:STDOUT: %.loc9_25.4: init %i32 to %.loc9_25.3 = in_place_init %.loc9_25.2 [concrete = constants.%int_1.47b] // CHECK:STDOUT: %.loc9_25.5: init %Field to %b.var = class_init (%.loc9_25.4) [concrete = constants.%Field.val] // CHECK:STDOUT: %.loc9_3: init %Field = converted %.loc9_25.1, %.loc9_25.5 [concrete = constants.%Field.val] // CHECK:STDOUT: assign %b.var, %.loc9_3 // CHECK:STDOUT: %Field.ref: type = name_ref Field, imports.%Main.Field [concrete = constants.%Field] // CHECK:STDOUT: %b: ref %Field = ref_binding b, %b.var // CHECK:STDOUT: %b.ref: ref %Field = name_ref b, %b // CHECK:STDOUT: %x.ref: %Field.elem = name_ref x, imports.%Main.import_ref.df7 [concrete = imports.%.afd] // CHECK:STDOUT: %.loc10_4: ref %i32 = class_element_access %b.ref, element0 // CHECK:STDOUT: %int_2: Core.IntLiteral = int_value 2 [concrete = constants.%int_2.ecc] // CHECK:STDOUT: %impl.elem0.loc10: %.7c3 = impl_witness_access constants.%ImplicitAs.impl_witness.58d, element0 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.5f4] // CHECK:STDOUT: %bound_method.loc10_7.1: = bound_method %int_2, %impl.elem0.loc10 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.bound.1eb] // CHECK:STDOUT: %specific_fn.loc10: = specific_function %impl.elem0.loc10, @Core.IntLiteral.as.ImplicitAs.impl.Convert(constants.%int_32) [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn] // CHECK:STDOUT: %bound_method.loc10_7.2: = bound_method %int_2, %specific_fn.loc10 [concrete = constants.%bound_method.ef3] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc10: init %i32 = call %bound_method.loc10_7.2(%int_2) [concrete = constants.%int_2.d0d] // CHECK:STDOUT: %.loc10_7: init %i32 = converted %int_2, %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc10 [concrete = constants.%int_2.d0d] // CHECK:STDOUT: assign %.loc10_4, %.loc10_7 // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %c.patt: %pattern_type.af1 = ref_binding_pattern c [concrete] // CHECK:STDOUT: %c.var_patt: %pattern_type.af1 = var_pattern %c.patt [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %c.var: ref %ForwardDeclared.20f323.1 = var %c.var_patt // CHECK:STDOUT: %.loc12_29.1: %empty_struct_type = struct_literal () [concrete = constants.%empty_struct] // CHECK:STDOUT: %.loc12_29.2: init %ForwardDeclared.20f323.1 to %c.var = class_init () [concrete = constants.%ForwardDeclared.val] // CHECK:STDOUT: %.loc12_3: init %ForwardDeclared.20f323.1 = converted %.loc12_29.1, %.loc12_29.2 [concrete = constants.%ForwardDeclared.val] // CHECK:STDOUT: assign %c.var, %.loc12_3 // CHECK:STDOUT: %ForwardDeclared.ref.loc12: type = name_ref ForwardDeclared, imports.%Main.ForwardDeclared [concrete = constants.%ForwardDeclared.20f323.1] // CHECK:STDOUT: %c: ref %ForwardDeclared.20f323.1 = ref_binding c, %c.var // CHECK:STDOUT: %c.ref.loc13: ref %ForwardDeclared.20f323.1 = name_ref c, %c // CHECK:STDOUT: %F.ref: %ForwardDeclared.F.type = name_ref F, imports.%Main.import_ref.019 [concrete = constants.%ForwardDeclared.F] // CHECK:STDOUT: %ForwardDeclared.F.bound: = bound_method %c.ref.loc13, %F.ref // CHECK:STDOUT: %.loc13: %ForwardDeclared.20f323.1 = acquire_value %c.ref.loc13 // CHECK:STDOUT: %ForwardDeclared.F.call: init %empty_tuple.type = call %ForwardDeclared.F.bound(%.loc13) // CHECK:STDOUT: %c.ref.loc14: ref %ForwardDeclared.20f323.1 = name_ref c, %c // CHECK:STDOUT: %G.ref: %ForwardDeclared.G.type = name_ref G, imports.%Main.import_ref.a45 [concrete = constants.%ForwardDeclared.G] // CHECK:STDOUT: %ForwardDeclared.G.bound: = bound_method %c.ref.loc14, %G.ref // CHECK:STDOUT: %ForwardDeclared.G.call: init %empty_tuple.type = call %ForwardDeclared.G.bound(%c.ref.loc14) // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %d.patt: %pattern_type.77e = ref_binding_pattern d [concrete] // CHECK:STDOUT: %d.var_patt: %pattern_type.77e = var_pattern %d.patt [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %d.var: ref %ptr.006 = var %d.var_patt // CHECK:STDOUT: %c.ref.loc16: ref %ForwardDeclared.20f323.1 = name_ref c, %c // CHECK:STDOUT: %addr: %ptr.006 = addr_of %c.ref.loc16 // CHECK:STDOUT: %impl.elem0.loc16: %.89b = impl_witness_access constants.%Copy.impl_witness.bef, element0 [concrete = constants.%ptr.as.Copy.impl.Op.d3c] // CHECK:STDOUT: %bound_method.loc16_36.1: = bound_method %addr, %impl.elem0.loc16 // CHECK:STDOUT: %specific_fn.loc16: = specific_function %impl.elem0.loc16, @ptr.as.Copy.impl.Op(constants.%ForwardDeclared.20f323.1) [concrete = constants.%ptr.as.Copy.impl.Op.specific_fn] // CHECK:STDOUT: %bound_method.loc16_36.2: = bound_method %addr, %specific_fn.loc16 // CHECK:STDOUT: %ptr.as.Copy.impl.Op.call: init %ptr.006 = call %bound_method.loc16_36.2(%addr) // CHECK:STDOUT: assign %d.var, %ptr.as.Copy.impl.Op.call // CHECK:STDOUT: %.loc16: type = splice_block %ptr.loc16 [concrete = constants.%ptr.006] { // CHECK:STDOUT: %ForwardDeclared.ref.loc16: type = name_ref ForwardDeclared, imports.%Main.ForwardDeclared [concrete = constants.%ForwardDeclared.20f323.1] // CHECK:STDOUT: %ptr.loc16: type = ptr_type %ForwardDeclared.ref.loc16 [concrete = constants.%ptr.006] // CHECK:STDOUT: } // CHECK:STDOUT: %d: ref %ptr.006 = ref_binding d, %d.var // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %e.patt: %pattern_type.b98 = ref_binding_pattern e [concrete] // CHECK:STDOUT: %e.var_patt: %pattern_type.b98 = var_pattern %e.patt [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %e.var: ref %ptr.8c3 = var %e.var_patt // CHECK:STDOUT: %DefaultOrUnformed.facet: %DefaultOrUnformed.type = facet_value constants.%ptr.8c3, (constants.%DefaultOrUnformed.impl_witness.fb9) [concrete = constants.%DefaultOrUnformed.facet] // CHECK:STDOUT: %.loc18_28.1: %DefaultOrUnformed.type = converted constants.%ptr.8c3, %DefaultOrUnformed.facet [concrete = constants.%DefaultOrUnformed.facet] // CHECK:STDOUT: %as_type: type = facet_access_type %.loc18_28.1 [concrete = constants.%ptr.8c3] // CHECK:STDOUT: %.loc18_28.2: type = converted %.loc18_28.1, %as_type [concrete = constants.%ptr.8c3] // CHECK:STDOUT: %T.as.DefaultOrUnformed.impl.Op.specific_fn: = specific_function constants.%T.as.DefaultOrUnformed.impl.Op.5f6, @T.as.DefaultOrUnformed.impl.Op(constants.%ptr.8c3) [concrete = constants.%T.as.DefaultOrUnformed.impl.Op.specific_fn] // CHECK:STDOUT: %T.as.DefaultOrUnformed.impl.Op.call: init %ptr.8c3 = call %T.as.DefaultOrUnformed.impl.Op.specific_fn() // CHECK:STDOUT: assign %e.var, %T.as.DefaultOrUnformed.impl.Op.call // CHECK:STDOUT: %.loc18_27: type = splice_block %ptr.loc18 [concrete = constants.%ptr.8c3] { // CHECK:STDOUT: %Incomplete.ref: type = name_ref Incomplete, imports.%Main.Incomplete [concrete = constants.%Incomplete] // CHECK:STDOUT: %ptr.loc18: type = ptr_type %Incomplete.ref [concrete = constants.%ptr.8c3] // CHECK:STDOUT: } // CHECK:STDOUT: %e: ref %ptr.8c3 = ref_binding e, %e.var // CHECK:STDOUT: %Destroy.Op.bound.loc18: = bound_method %e.var, constants.%Destroy.Op.651ba6.1 // CHECK:STDOUT: %Destroy.Op.call.loc18: init %empty_tuple.type = call %Destroy.Op.bound.loc18(%e.var) // CHECK:STDOUT: %Destroy.Op.bound.loc16: = bound_method %d.var, constants.%Destroy.Op.651ba6.2 // CHECK:STDOUT: %Destroy.Op.call.loc16: init %empty_tuple.type = call %Destroy.Op.bound.loc16(%d.var) // CHECK:STDOUT: %Destroy.Op.bound.loc12: = bound_method %c.var, constants.%Destroy.Op.651ba6.3 // CHECK:STDOUT: %Destroy.Op.call.loc12: init %empty_tuple.type = call %Destroy.Op.bound.loc12(%c.var) // CHECK:STDOUT: %Destroy.Op.bound.loc9: = bound_method %b.var, constants.%Destroy.Op.651ba6.4 // CHECK:STDOUT: %Destroy.Op.call.loc9: init %empty_tuple.type = call %Destroy.Op.bound.loc9(%b.var) // CHECK:STDOUT: %Destroy.Op.bound.loc7: = bound_method %a.var, constants.%Destroy.Op.651ba6.5 // CHECK:STDOUT: %Destroy.Op.call.loc7: init %empty_tuple.type = call %Destroy.Op.bound.loc7(%a.var) // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @ForwardDeclared.F [from "a.carbon"]; // CHECK:STDOUT: // CHECK:STDOUT: fn @ForwardDeclared.G [from "a.carbon"]; // CHECK:STDOUT: // CHECK:STDOUT: fn @Destroy.Op.loc18(%self.param: ref %ptr.8c3) = "no_op"; // CHECK:STDOUT: // CHECK:STDOUT: fn @Destroy.Op.loc16(%self.param: ref %ptr.006) = "no_op"; // CHECK:STDOUT: // CHECK:STDOUT: fn @Destroy.Op.loc12(%self.param: ref %ForwardDeclared.20f323.1) = "no_op"; // CHECK:STDOUT: // CHECK:STDOUT: fn @Destroy.Op.loc9(%self.param: ref %Field) = "no_op"; // CHECK:STDOUT: // CHECK:STDOUT: fn @Destroy.Op.loc7(%self.param: ref %Empty) = "no_op"; // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/class/import_forward_decl.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/convert.carbon // TODO: Add ranges and switch to "--dump-sem-ir-ranges=only". // EXTRA-ARGS: --dump-sem-ir-ranges=if-present // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/class/import_forward_decl.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/class/import_forward_decl.carbon // --- a.carbon library "[[@TEST_NAME]]"; class ForwardDecl; // --- a.impl.carbon impl library "[[@TEST_NAME]]"; class ForwardDecl { } // CHECK:STDOUT: --- a.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %ForwardDecl: type = class_type @ForwardDecl [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .ForwardDecl = %ForwardDecl.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %ForwardDecl.decl: type = class_decl @ForwardDecl [concrete = constants.%ForwardDecl] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @ForwardDecl; // CHECK:STDOUT: // CHECK:STDOUT: --- a.impl.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %ForwardDecl: type = class_type @ForwardDecl [concrete] // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %complete_type: = complete_type_witness %empty_struct_type [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .ForwardDecl = %ForwardDecl.decl // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: } // CHECK:STDOUT: %default.import.loc2_17.1 = import // CHECK:STDOUT: %default.import.loc2_17.2 = import // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %ForwardDecl.decl: type = class_decl @ForwardDecl [concrete = constants.%ForwardDecl] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @ForwardDecl { // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%ForwardDecl // CHECK:STDOUT: } // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/class/import_indirect.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/convert.carbon // TODO: Add ranges and switch to "--dump-sem-ir-ranges=only". // EXTRA-ARGS: --dump-sem-ir-ranges=if-present // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/class/import_indirect.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/class/import_indirect.carbon // Triangle: // // a <-- Define // |\ // | b <-- Re-export // |/ // X <-- Use both // // Diamond: // // a <-- Define // / \ // b c <-- Re-export // \ / // X <-- Use both // ============================================================================ // Setup files // ============================================================================ // --- a.carbon library "[[@TEST_NAME]]"; class C {} // --- b.carbon library "[[@TEST_NAME]]"; import library "a"; alias D = C; var b_val: C = {}; var b_ptr: D* = &b_val; // --- c.carbon library "[[@TEST_NAME]]"; import library "a"; alias E = C; var c_val: C = {}; var c_ptr: E* = &c_val; // ============================================================================ // Test files // ============================================================================ // --- triangle.carbon library "[[@TEST_NAME]]"; import library "a"; import library "b"; var value: C = {}; var ptr: D* = &value; // --- triangle_reverse.carbon library "[[@TEST_NAME]]"; import library "b"; import library "a"; var value: C = {}; var ptr: D* = &value; // --- diamond.carbon library "[[@TEST_NAME]]"; import library "b"; import library "c"; var value: D = {}; var ptr: E* = &value; // --- diamond_reverse.carbon library "[[@TEST_NAME]]"; import library "c"; import library "b"; var value: D = {}; var ptr: E* = &value; // CHECK:STDOUT: --- a.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %C: type = class_type @C [concrete] // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %complete_type: = complete_type_witness %empty_struct_type [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .C = %C.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %C.decl: type = class_decl @C [concrete = constants.%C] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @C { // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%C // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- b.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %C: type = class_type @C [concrete] // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %complete_type.357: = complete_type_witness %empty_struct_type [concrete] // CHECK:STDOUT: %pattern_type.7c7: type = pattern_type %C [concrete] // CHECK:STDOUT: %empty_struct: %empty_struct_type = struct_value () [concrete] // CHECK:STDOUT: %C.val: %C = struct_value () [concrete] // CHECK:STDOUT: %ptr.31e: type = ptr_type %C [concrete] // CHECK:STDOUT: %pattern_type.506: type = pattern_type %ptr.31e [concrete] // CHECK:STDOUT: %addr: %ptr.31e = addr_of file.%b_val.var [concrete] // CHECK:STDOUT: %Copy.type: type = facet_type <@Copy> [concrete] // CHECK:STDOUT: %T.67d: type = symbolic_binding T, 0 [symbolic] // CHECK:STDOUT: %ptr.as.Copy.impl.Op.type.2d4: type = fn_type @ptr.as.Copy.impl.Op, @ptr.as.Copy.impl(%T.67d) [symbolic] // CHECK:STDOUT: %ptr.as.Copy.impl.Op.74e: %ptr.as.Copy.impl.Op.type.2d4 = struct_value () [symbolic] // CHECK:STDOUT: %Copy.impl_witness.2c7: = impl_witness imports.%Copy.impl_witness_table.c3a, @ptr.as.Copy.impl(%C) [concrete] // CHECK:STDOUT: %ptr.as.Copy.impl.Op.type.411: type = fn_type @ptr.as.Copy.impl.Op, @ptr.as.Copy.impl(%C) [concrete] // CHECK:STDOUT: %ptr.as.Copy.impl.Op.ed9: %ptr.as.Copy.impl.Op.type.411 = struct_value () [concrete] // CHECK:STDOUT: %Copy.facet: %Copy.type = facet_value %ptr.31e, (%Copy.impl_witness.2c7) [concrete] // CHECK:STDOUT: %Copy.WithSelf.Op.type.259: type = fn_type @Copy.WithSelf.Op, @Copy.WithSelf(%Copy.facet) [concrete] // CHECK:STDOUT: %.64b: type = fn_type_with_self_type %Copy.WithSelf.Op.type.259, %Copy.facet [concrete] // CHECK:STDOUT: %ptr.as.Copy.impl.Op.bound: = bound_method %addr, %ptr.as.Copy.impl.Op.ed9 [concrete] // CHECK:STDOUT: %ptr.as.Copy.impl.Op.specific_fn: = specific_function %ptr.as.Copy.impl.Op.ed9, @ptr.as.Copy.impl.Op(%C) [concrete] // CHECK:STDOUT: %bound_method: = bound_method %addr, %ptr.as.Copy.impl.Op.specific_fn [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Main.C: type = import_ref Main//a, C, loaded [concrete = constants.%C] // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: .Copy = %Core.Copy // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: %Main.import_ref.8f2: = import_ref Main//a, loc4_10, loaded [concrete = constants.%complete_type.357] // CHECK:STDOUT: %Main.import_ref.743 = import_ref Main//a, inst{{[0-9A-F]+}} [no loc], unloaded // CHECK:STDOUT: %Core.Copy: type = import_ref Core//prelude/parts/copy, Copy, loaded [concrete = constants.%Copy.type] // CHECK:STDOUT: %Core.import_ref.203: @ptr.as.Copy.impl.%ptr.as.Copy.impl.Op.type (%ptr.as.Copy.impl.Op.type.2d4) = import_ref Core//prelude/parts/copy, loc{{\d+_\d+}}, loaded [symbolic = @ptr.as.Copy.impl.%ptr.as.Copy.impl.Op (constants.%ptr.as.Copy.impl.Op.74e)] // CHECK:STDOUT: %Copy.impl_witness_table.c3a = impl_witness_table (%Core.import_ref.203), @ptr.as.Copy.impl [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .C = imports.%Main.C // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .D = %D // CHECK:STDOUT: .b_val = %b_val // CHECK:STDOUT: .b_ptr = %b_ptr // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %default.import = import // CHECK:STDOUT: %C.ref.loc6: type = name_ref C, imports.%Main.C [concrete = constants.%C] // CHECK:STDOUT: %D: type = alias_binding D, imports.%Main.C [concrete = constants.%C] // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %b_val.patt: %pattern_type.7c7 = ref_binding_pattern b_val [concrete] // CHECK:STDOUT: %b_val.var_patt: %pattern_type.7c7 = var_pattern %b_val.patt [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %b_val.var: ref %C = var %b_val.var_patt [concrete] // CHECK:STDOUT: %C.ref.loc8: type = name_ref C, imports.%Main.C [concrete = constants.%C] // CHECK:STDOUT: %b_val: ref %C = ref_binding b_val, %b_val.var [concrete = %b_val.var] // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %b_ptr.patt: %pattern_type.506 = ref_binding_pattern b_ptr [concrete] // CHECK:STDOUT: %b_ptr.var_patt: %pattern_type.506 = var_pattern %b_ptr.patt [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %b_ptr.var: ref %ptr.31e = var %b_ptr.var_patt [concrete] // CHECK:STDOUT: %.loc9: type = splice_block %ptr [concrete = constants.%ptr.31e] { // CHECK:STDOUT: %D.ref: type = name_ref D, %D [concrete = constants.%C] // CHECK:STDOUT: %ptr: type = ptr_type %D.ref [concrete = constants.%ptr.31e] // CHECK:STDOUT: } // CHECK:STDOUT: %b_ptr: ref %ptr.31e = ref_binding b_ptr, %b_ptr.var [concrete = %b_ptr.var] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @C [from "a.carbon"] { // CHECK:STDOUT: complete_type_witness = imports.%Main.import_ref.8f2 // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = imports.%Main.import_ref.743 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @__global_init() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %.loc8_17.1: %empty_struct_type = struct_literal () [concrete = constants.%empty_struct] // CHECK:STDOUT: %.loc8_17.2: init %C to file.%b_val.var = class_init () [concrete = constants.%C.val] // CHECK:STDOUT: %.loc8_1: init %C = converted %.loc8_17.1, %.loc8_17.2 [concrete = constants.%C.val] // CHECK:STDOUT: assign file.%b_val.var, %.loc8_1 // CHECK:STDOUT: %b_val.ref: ref %C = name_ref b_val, file.%b_val [concrete = file.%b_val.var] // CHECK:STDOUT: %addr: %ptr.31e = addr_of %b_val.ref [concrete = constants.%addr] // CHECK:STDOUT: %impl.elem0: %.64b = impl_witness_access constants.%Copy.impl_witness.2c7, element0 [concrete = constants.%ptr.as.Copy.impl.Op.ed9] // CHECK:STDOUT: %bound_method.loc9_17.1: = bound_method %addr, %impl.elem0 [concrete = constants.%ptr.as.Copy.impl.Op.bound] // CHECK:STDOUT: %specific_fn: = specific_function %impl.elem0, @ptr.as.Copy.impl.Op(constants.%C) [concrete = constants.%ptr.as.Copy.impl.Op.specific_fn] // CHECK:STDOUT: %bound_method.loc9_17.2: = bound_method %addr, %specific_fn [concrete = constants.%bound_method] // CHECK:STDOUT: %ptr.as.Copy.impl.Op.call: init %ptr.31e = call %bound_method.loc9_17.2(%addr) [concrete = constants.%addr] // CHECK:STDOUT: assign file.%b_ptr.var, %ptr.as.Copy.impl.Op.call // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- c.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %C: type = class_type @C [concrete] // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %complete_type.357: = complete_type_witness %empty_struct_type [concrete] // CHECK:STDOUT: %pattern_type.7c7: type = pattern_type %C [concrete] // CHECK:STDOUT: %empty_struct: %empty_struct_type = struct_value () [concrete] // CHECK:STDOUT: %C.val: %C = struct_value () [concrete] // CHECK:STDOUT: %ptr.31e: type = ptr_type %C [concrete] // CHECK:STDOUT: %pattern_type.506: type = pattern_type %ptr.31e [concrete] // CHECK:STDOUT: %addr: %ptr.31e = addr_of file.%c_val.var [concrete] // CHECK:STDOUT: %Copy.type: type = facet_type <@Copy> [concrete] // CHECK:STDOUT: %T.67d: type = symbolic_binding T, 0 [symbolic] // CHECK:STDOUT: %ptr.as.Copy.impl.Op.type.2d4: type = fn_type @ptr.as.Copy.impl.Op, @ptr.as.Copy.impl(%T.67d) [symbolic] // CHECK:STDOUT: %ptr.as.Copy.impl.Op.74e: %ptr.as.Copy.impl.Op.type.2d4 = struct_value () [symbolic] // CHECK:STDOUT: %Copy.impl_witness.2c7: = impl_witness imports.%Copy.impl_witness_table.c3a, @ptr.as.Copy.impl(%C) [concrete] // CHECK:STDOUT: %ptr.as.Copy.impl.Op.type.411: type = fn_type @ptr.as.Copy.impl.Op, @ptr.as.Copy.impl(%C) [concrete] // CHECK:STDOUT: %ptr.as.Copy.impl.Op.ed9: %ptr.as.Copy.impl.Op.type.411 = struct_value () [concrete] // CHECK:STDOUT: %Copy.facet: %Copy.type = facet_value %ptr.31e, (%Copy.impl_witness.2c7) [concrete] // CHECK:STDOUT: %Copy.WithSelf.Op.type.259: type = fn_type @Copy.WithSelf.Op, @Copy.WithSelf(%Copy.facet) [concrete] // CHECK:STDOUT: %.64b: type = fn_type_with_self_type %Copy.WithSelf.Op.type.259, %Copy.facet [concrete] // CHECK:STDOUT: %ptr.as.Copy.impl.Op.bound: = bound_method %addr, %ptr.as.Copy.impl.Op.ed9 [concrete] // CHECK:STDOUT: %ptr.as.Copy.impl.Op.specific_fn: = specific_function %ptr.as.Copy.impl.Op.ed9, @ptr.as.Copy.impl.Op(%C) [concrete] // CHECK:STDOUT: %bound_method: = bound_method %addr, %ptr.as.Copy.impl.Op.specific_fn [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Main.C: type = import_ref Main//a, C, loaded [concrete = constants.%C] // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: .Copy = %Core.Copy // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: %Main.import_ref.8f2: = import_ref Main//a, loc4_10, loaded [concrete = constants.%complete_type.357] // CHECK:STDOUT: %Main.import_ref.743 = import_ref Main//a, inst{{[0-9A-F]+}} [no loc], unloaded // CHECK:STDOUT: %Core.Copy: type = import_ref Core//prelude/parts/copy, Copy, loaded [concrete = constants.%Copy.type] // CHECK:STDOUT: %Core.import_ref.203: @ptr.as.Copy.impl.%ptr.as.Copy.impl.Op.type (%ptr.as.Copy.impl.Op.type.2d4) = import_ref Core//prelude/parts/copy, loc{{\d+_\d+}}, loaded [symbolic = @ptr.as.Copy.impl.%ptr.as.Copy.impl.Op (constants.%ptr.as.Copy.impl.Op.74e)] // CHECK:STDOUT: %Copy.impl_witness_table.c3a = impl_witness_table (%Core.import_ref.203), @ptr.as.Copy.impl [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .C = imports.%Main.C // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .E = %E // CHECK:STDOUT: .c_val = %c_val // CHECK:STDOUT: .c_ptr = %c_ptr // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %default.import = import // CHECK:STDOUT: %C.ref.loc6: type = name_ref C, imports.%Main.C [concrete = constants.%C] // CHECK:STDOUT: %E: type = alias_binding E, imports.%Main.C [concrete = constants.%C] // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %c_val.patt: %pattern_type.7c7 = ref_binding_pattern c_val [concrete] // CHECK:STDOUT: %c_val.var_patt: %pattern_type.7c7 = var_pattern %c_val.patt [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %c_val.var: ref %C = var %c_val.var_patt [concrete] // CHECK:STDOUT: %C.ref.loc8: type = name_ref C, imports.%Main.C [concrete = constants.%C] // CHECK:STDOUT: %c_val: ref %C = ref_binding c_val, %c_val.var [concrete = %c_val.var] // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %c_ptr.patt: %pattern_type.506 = ref_binding_pattern c_ptr [concrete] // CHECK:STDOUT: %c_ptr.var_patt: %pattern_type.506 = var_pattern %c_ptr.patt [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %c_ptr.var: ref %ptr.31e = var %c_ptr.var_patt [concrete] // CHECK:STDOUT: %.loc9: type = splice_block %ptr [concrete = constants.%ptr.31e] { // CHECK:STDOUT: %E.ref: type = name_ref E, %E [concrete = constants.%C] // CHECK:STDOUT: %ptr: type = ptr_type %E.ref [concrete = constants.%ptr.31e] // CHECK:STDOUT: } // CHECK:STDOUT: %c_ptr: ref %ptr.31e = ref_binding c_ptr, %c_ptr.var [concrete = %c_ptr.var] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @C [from "a.carbon"] { // CHECK:STDOUT: complete_type_witness = imports.%Main.import_ref.8f2 // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = imports.%Main.import_ref.743 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @__global_init() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %.loc8_17.1: %empty_struct_type = struct_literal () [concrete = constants.%empty_struct] // CHECK:STDOUT: %.loc8_17.2: init %C to file.%c_val.var = class_init () [concrete = constants.%C.val] // CHECK:STDOUT: %.loc8_1: init %C = converted %.loc8_17.1, %.loc8_17.2 [concrete = constants.%C.val] // CHECK:STDOUT: assign file.%c_val.var, %.loc8_1 // CHECK:STDOUT: %c_val.ref: ref %C = name_ref c_val, file.%c_val [concrete = file.%c_val.var] // CHECK:STDOUT: %addr: %ptr.31e = addr_of %c_val.ref [concrete = constants.%addr] // CHECK:STDOUT: %impl.elem0: %.64b = impl_witness_access constants.%Copy.impl_witness.2c7, element0 [concrete = constants.%ptr.as.Copy.impl.Op.ed9] // CHECK:STDOUT: %bound_method.loc9_17.1: = bound_method %addr, %impl.elem0 [concrete = constants.%ptr.as.Copy.impl.Op.bound] // CHECK:STDOUT: %specific_fn: = specific_function %impl.elem0, @ptr.as.Copy.impl.Op(constants.%C) [concrete = constants.%ptr.as.Copy.impl.Op.specific_fn] // CHECK:STDOUT: %bound_method.loc9_17.2: = bound_method %addr, %specific_fn [concrete = constants.%bound_method] // CHECK:STDOUT: %ptr.as.Copy.impl.Op.call: init %ptr.31e = call %bound_method.loc9_17.2(%addr) [concrete = constants.%addr] // CHECK:STDOUT: assign file.%c_ptr.var, %ptr.as.Copy.impl.Op.call // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- triangle.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %C: type = class_type @C [concrete] // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %complete_type.357: = complete_type_witness %empty_struct_type [concrete] // CHECK:STDOUT: %pattern_type.7c7: type = pattern_type %C [concrete] // CHECK:STDOUT: %empty_struct: %empty_struct_type = struct_value () [concrete] // CHECK:STDOUT: %C.val: %C = struct_value () [concrete] // CHECK:STDOUT: %ptr.31e: type = ptr_type %C [concrete] // CHECK:STDOUT: %pattern_type.506: type = pattern_type %ptr.31e [concrete] // CHECK:STDOUT: %addr: %ptr.31e = addr_of file.%value.var [concrete] // CHECK:STDOUT: %Copy.type: type = facet_type <@Copy> [concrete] // CHECK:STDOUT: %T.67d: type = symbolic_binding T, 0 [symbolic] // CHECK:STDOUT: %ptr.as.Copy.impl.Op.type.2d4: type = fn_type @ptr.as.Copy.impl.Op, @ptr.as.Copy.impl(%T.67d) [symbolic] // CHECK:STDOUT: %ptr.as.Copy.impl.Op.74e: %ptr.as.Copy.impl.Op.type.2d4 = struct_value () [symbolic] // CHECK:STDOUT: %Copy.impl_witness.2c7: = impl_witness imports.%Copy.impl_witness_table.c3a, @ptr.as.Copy.impl(%C) [concrete] // CHECK:STDOUT: %ptr.as.Copy.impl.Op.type.411: type = fn_type @ptr.as.Copy.impl.Op, @ptr.as.Copy.impl(%C) [concrete] // CHECK:STDOUT: %ptr.as.Copy.impl.Op.ed9: %ptr.as.Copy.impl.Op.type.411 = struct_value () [concrete] // CHECK:STDOUT: %Copy.facet: %Copy.type = facet_value %ptr.31e, (%Copy.impl_witness.2c7) [concrete] // CHECK:STDOUT: %Copy.WithSelf.Op.type.259: type = fn_type @Copy.WithSelf.Op, @Copy.WithSelf(%Copy.facet) [concrete] // CHECK:STDOUT: %.64b: type = fn_type_with_self_type %Copy.WithSelf.Op.type.259, %Copy.facet [concrete] // CHECK:STDOUT: %ptr.as.Copy.impl.Op.bound: = bound_method %addr, %ptr.as.Copy.impl.Op.ed9 [concrete] // CHECK:STDOUT: %ptr.as.Copy.impl.Op.specific_fn: = specific_function %ptr.as.Copy.impl.Op.ed9, @ptr.as.Copy.impl.Op(%C) [concrete] // CHECK:STDOUT: %bound_method: = bound_method %addr, %ptr.as.Copy.impl.Op.specific_fn [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Main.C: type = import_ref Main//a, C, loaded [concrete = constants.%C] // CHECK:STDOUT: %Main.D: type = import_ref Main//b, D, loaded [concrete = constants.%C] // CHECK:STDOUT: %Main.b_val = import_ref Main//b, b_val, unloaded // CHECK:STDOUT: %Main.b_ptr = import_ref Main//b, b_ptr, unloaded // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: .Copy = %Core.Copy // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: %Main.import_ref.8f2: = import_ref Main//a, loc4_10, loaded [concrete = constants.%complete_type.357] // CHECK:STDOUT: %Main.import_ref.743 = import_ref Main//a, inst{{[0-9A-F]+}} [no loc], unloaded // CHECK:STDOUT: %Core.Copy: type = import_ref Core//prelude/parts/copy, Copy, loaded [concrete = constants.%Copy.type] // CHECK:STDOUT: %Core.import_ref.203: @ptr.as.Copy.impl.%ptr.as.Copy.impl.Op.type (%ptr.as.Copy.impl.Op.type.2d4) = import_ref Core//prelude/parts/copy, loc{{\d+_\d+}}, loaded [symbolic = @ptr.as.Copy.impl.%ptr.as.Copy.impl.Op (constants.%ptr.as.Copy.impl.Op.74e)] // CHECK:STDOUT: %Copy.impl_witness_table.c3a = impl_witness_table (%Core.import_ref.203), @ptr.as.Copy.impl [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .C = imports.%Main.C // CHECK:STDOUT: .D = imports.%Main.D // CHECK:STDOUT: .b_val = imports.%Main.b_val // CHECK:STDOUT: .b_ptr = imports.%Main.b_ptr // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .value = %value // CHECK:STDOUT: .ptr = %ptr.loc8_5 // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %default.import = import // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %value.patt: %pattern_type.7c7 = ref_binding_pattern value [concrete] // CHECK:STDOUT: %value.var_patt: %pattern_type.7c7 = var_pattern %value.patt [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %value.var: ref %C = var %value.var_patt [concrete] // CHECK:STDOUT: %C.ref: type = name_ref C, imports.%Main.C [concrete = constants.%C] // CHECK:STDOUT: %value: ref %C = ref_binding value, %value.var [concrete = %value.var] // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %ptr.patt: %pattern_type.506 = ref_binding_pattern ptr [concrete] // CHECK:STDOUT: %ptr.var_patt: %pattern_type.506 = var_pattern %ptr.patt [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %ptr.var: ref %ptr.31e = var %ptr.var_patt [concrete] // CHECK:STDOUT: %.loc8: type = splice_block %ptr.loc8_11 [concrete = constants.%ptr.31e] { // CHECK:STDOUT: %D.ref: type = name_ref D, imports.%Main.D [concrete = constants.%C] // CHECK:STDOUT: %ptr.loc8_11: type = ptr_type %D.ref [concrete = constants.%ptr.31e] // CHECK:STDOUT: } // CHECK:STDOUT: %ptr.loc8_5: ref %ptr.31e = ref_binding ptr, %ptr.var [concrete = %ptr.var] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @C [from "a.carbon"] { // CHECK:STDOUT: complete_type_witness = imports.%Main.import_ref.8f2 // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = imports.%Main.import_ref.743 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @__global_init() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %.loc7_17.1: %empty_struct_type = struct_literal () [concrete = constants.%empty_struct] // CHECK:STDOUT: %.loc7_17.2: init %C to file.%value.var = class_init () [concrete = constants.%C.val] // CHECK:STDOUT: %.loc7_1: init %C = converted %.loc7_17.1, %.loc7_17.2 [concrete = constants.%C.val] // CHECK:STDOUT: assign file.%value.var, %.loc7_1 // CHECK:STDOUT: %value.ref: ref %C = name_ref value, file.%value [concrete = file.%value.var] // CHECK:STDOUT: %addr: %ptr.31e = addr_of %value.ref [concrete = constants.%addr] // CHECK:STDOUT: %impl.elem0: %.64b = impl_witness_access constants.%Copy.impl_witness.2c7, element0 [concrete = constants.%ptr.as.Copy.impl.Op.ed9] // CHECK:STDOUT: %bound_method.loc8_15.1: = bound_method %addr, %impl.elem0 [concrete = constants.%ptr.as.Copy.impl.Op.bound] // CHECK:STDOUT: %specific_fn: = specific_function %impl.elem0, @ptr.as.Copy.impl.Op(constants.%C) [concrete = constants.%ptr.as.Copy.impl.Op.specific_fn] // CHECK:STDOUT: %bound_method.loc8_15.2: = bound_method %addr, %specific_fn [concrete = constants.%bound_method] // CHECK:STDOUT: %ptr.as.Copy.impl.Op.call: init %ptr.31e = call %bound_method.loc8_15.2(%addr) [concrete = constants.%addr] // CHECK:STDOUT: assign file.%ptr.var, %ptr.as.Copy.impl.Op.call // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- triangle_reverse.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %C: type = class_type @C [concrete] // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %complete_type.357: = complete_type_witness %empty_struct_type [concrete] // CHECK:STDOUT: %pattern_type.7c7: type = pattern_type %C [concrete] // CHECK:STDOUT: %empty_struct: %empty_struct_type = struct_value () [concrete] // CHECK:STDOUT: %C.val: %C = struct_value () [concrete] // CHECK:STDOUT: %ptr.31e: type = ptr_type %C [concrete] // CHECK:STDOUT: %pattern_type.506: type = pattern_type %ptr.31e [concrete] // CHECK:STDOUT: %addr: %ptr.31e = addr_of file.%value.var [concrete] // CHECK:STDOUT: %Copy.type: type = facet_type <@Copy> [concrete] // CHECK:STDOUT: %T.67d: type = symbolic_binding T, 0 [symbolic] // CHECK:STDOUT: %ptr.as.Copy.impl.Op.type.2d4: type = fn_type @ptr.as.Copy.impl.Op, @ptr.as.Copy.impl(%T.67d) [symbolic] // CHECK:STDOUT: %ptr.as.Copy.impl.Op.74e: %ptr.as.Copy.impl.Op.type.2d4 = struct_value () [symbolic] // CHECK:STDOUT: %Copy.impl_witness.2c7: = impl_witness imports.%Copy.impl_witness_table.c3a, @ptr.as.Copy.impl(%C) [concrete] // CHECK:STDOUT: %ptr.as.Copy.impl.Op.type.411: type = fn_type @ptr.as.Copy.impl.Op, @ptr.as.Copy.impl(%C) [concrete] // CHECK:STDOUT: %ptr.as.Copy.impl.Op.ed9: %ptr.as.Copy.impl.Op.type.411 = struct_value () [concrete] // CHECK:STDOUT: %Copy.facet: %Copy.type = facet_value %ptr.31e, (%Copy.impl_witness.2c7) [concrete] // CHECK:STDOUT: %Copy.WithSelf.Op.type.259: type = fn_type @Copy.WithSelf.Op, @Copy.WithSelf(%Copy.facet) [concrete] // CHECK:STDOUT: %.64b: type = fn_type_with_self_type %Copy.WithSelf.Op.type.259, %Copy.facet [concrete] // CHECK:STDOUT: %ptr.as.Copy.impl.Op.bound: = bound_method %addr, %ptr.as.Copy.impl.Op.ed9 [concrete] // CHECK:STDOUT: %ptr.as.Copy.impl.Op.specific_fn: = specific_function %ptr.as.Copy.impl.Op.ed9, @ptr.as.Copy.impl.Op(%C) [concrete] // CHECK:STDOUT: %bound_method: = bound_method %addr, %ptr.as.Copy.impl.Op.specific_fn [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Main.D: type = import_ref Main//b, D, loaded [concrete = constants.%C] // CHECK:STDOUT: %Main.b_val = import_ref Main//b, b_val, unloaded // CHECK:STDOUT: %Main.b_ptr = import_ref Main//b, b_ptr, unloaded // CHECK:STDOUT: %Main.C: type = import_ref Main//a, C, loaded [concrete = constants.%C] // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: .Copy = %Core.Copy // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: %Main.import_ref.8f2: = import_ref Main//a, loc4_10, loaded [concrete = constants.%complete_type.357] // CHECK:STDOUT: %Main.import_ref.743 = import_ref Main//a, inst{{[0-9A-F]+}} [no loc], unloaded // CHECK:STDOUT: %Core.Copy: type = import_ref Core//prelude/parts/copy, Copy, loaded [concrete = constants.%Copy.type] // CHECK:STDOUT: %Core.import_ref.203: @ptr.as.Copy.impl.%ptr.as.Copy.impl.Op.type (%ptr.as.Copy.impl.Op.type.2d4) = import_ref Core//prelude/parts/copy, loc{{\d+_\d+}}, loaded [symbolic = @ptr.as.Copy.impl.%ptr.as.Copy.impl.Op (constants.%ptr.as.Copy.impl.Op.74e)] // CHECK:STDOUT: %Copy.impl_witness_table.c3a = impl_witness_table (%Core.import_ref.203), @ptr.as.Copy.impl [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .D = imports.%Main.D // CHECK:STDOUT: .b_val = imports.%Main.b_val // CHECK:STDOUT: .b_ptr = imports.%Main.b_ptr // CHECK:STDOUT: .C = imports.%Main.C // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .value = %value // CHECK:STDOUT: .ptr = %ptr.loc8_5 // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %default.import = import // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %value.patt: %pattern_type.7c7 = ref_binding_pattern value [concrete] // CHECK:STDOUT: %value.var_patt: %pattern_type.7c7 = var_pattern %value.patt [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %value.var: ref %C = var %value.var_patt [concrete] // CHECK:STDOUT: %C.ref: type = name_ref C, imports.%Main.C [concrete = constants.%C] // CHECK:STDOUT: %value: ref %C = ref_binding value, %value.var [concrete = %value.var] // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %ptr.patt: %pattern_type.506 = ref_binding_pattern ptr [concrete] // CHECK:STDOUT: %ptr.var_patt: %pattern_type.506 = var_pattern %ptr.patt [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %ptr.var: ref %ptr.31e = var %ptr.var_patt [concrete] // CHECK:STDOUT: %.loc8: type = splice_block %ptr.loc8_11 [concrete = constants.%ptr.31e] { // CHECK:STDOUT: %D.ref: type = name_ref D, imports.%Main.D [concrete = constants.%C] // CHECK:STDOUT: %ptr.loc8_11: type = ptr_type %D.ref [concrete = constants.%ptr.31e] // CHECK:STDOUT: } // CHECK:STDOUT: %ptr.loc8_5: ref %ptr.31e = ref_binding ptr, %ptr.var [concrete = %ptr.var] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @C [from "a.carbon"] { // CHECK:STDOUT: complete_type_witness = imports.%Main.import_ref.8f2 // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = imports.%Main.import_ref.743 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @__global_init() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %.loc7_17.1: %empty_struct_type = struct_literal () [concrete = constants.%empty_struct] // CHECK:STDOUT: %.loc7_17.2: init %C to file.%value.var = class_init () [concrete = constants.%C.val] // CHECK:STDOUT: %.loc7_1: init %C = converted %.loc7_17.1, %.loc7_17.2 [concrete = constants.%C.val] // CHECK:STDOUT: assign file.%value.var, %.loc7_1 // CHECK:STDOUT: %value.ref: ref %C = name_ref value, file.%value [concrete = file.%value.var] // CHECK:STDOUT: %addr: %ptr.31e = addr_of %value.ref [concrete = constants.%addr] // CHECK:STDOUT: %impl.elem0: %.64b = impl_witness_access constants.%Copy.impl_witness.2c7, element0 [concrete = constants.%ptr.as.Copy.impl.Op.ed9] // CHECK:STDOUT: %bound_method.loc8_15.1: = bound_method %addr, %impl.elem0 [concrete = constants.%ptr.as.Copy.impl.Op.bound] // CHECK:STDOUT: %specific_fn: = specific_function %impl.elem0, @ptr.as.Copy.impl.Op(constants.%C) [concrete = constants.%ptr.as.Copy.impl.Op.specific_fn] // CHECK:STDOUT: %bound_method.loc8_15.2: = bound_method %addr, %specific_fn [concrete = constants.%bound_method] // CHECK:STDOUT: %ptr.as.Copy.impl.Op.call: init %ptr.31e = call %bound_method.loc8_15.2(%addr) [concrete = constants.%addr] // CHECK:STDOUT: assign file.%ptr.var, %ptr.as.Copy.impl.Op.call // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- diamond.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %C: type = class_type @C [concrete] // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %complete_type.357: = complete_type_witness %empty_struct_type [concrete] // CHECK:STDOUT: %pattern_type.7c7: type = pattern_type %C [concrete] // CHECK:STDOUT: %empty_struct: %empty_struct_type = struct_value () [concrete] // CHECK:STDOUT: %C.val: %C = struct_value () [concrete] // CHECK:STDOUT: %ptr.31e: type = ptr_type %C [concrete] // CHECK:STDOUT: %pattern_type.506: type = pattern_type %ptr.31e [concrete] // CHECK:STDOUT: %addr: %ptr.31e = addr_of file.%value.var [concrete] // CHECK:STDOUT: %Copy.type: type = facet_type <@Copy> [concrete] // CHECK:STDOUT: %T.67d: type = symbolic_binding T, 0 [symbolic] // CHECK:STDOUT: %ptr.as.Copy.impl.Op.type.2d4: type = fn_type @ptr.as.Copy.impl.Op, @ptr.as.Copy.impl(%T.67d) [symbolic] // CHECK:STDOUT: %ptr.as.Copy.impl.Op.74e: %ptr.as.Copy.impl.Op.type.2d4 = struct_value () [symbolic] // CHECK:STDOUT: %Copy.impl_witness.2c7: = impl_witness imports.%Copy.impl_witness_table.c3a, @ptr.as.Copy.impl(%C) [concrete] // CHECK:STDOUT: %ptr.as.Copy.impl.Op.type.411: type = fn_type @ptr.as.Copy.impl.Op, @ptr.as.Copy.impl(%C) [concrete] // CHECK:STDOUT: %ptr.as.Copy.impl.Op.ed9: %ptr.as.Copy.impl.Op.type.411 = struct_value () [concrete] // CHECK:STDOUT: %Copy.facet: %Copy.type = facet_value %ptr.31e, (%Copy.impl_witness.2c7) [concrete] // CHECK:STDOUT: %Copy.WithSelf.Op.type.259: type = fn_type @Copy.WithSelf.Op, @Copy.WithSelf(%Copy.facet) [concrete] // CHECK:STDOUT: %.64b: type = fn_type_with_self_type %Copy.WithSelf.Op.type.259, %Copy.facet [concrete] // CHECK:STDOUT: %ptr.as.Copy.impl.Op.bound: = bound_method %addr, %ptr.as.Copy.impl.Op.ed9 [concrete] // CHECK:STDOUT: %ptr.as.Copy.impl.Op.specific_fn: = specific_function %ptr.as.Copy.impl.Op.ed9, @ptr.as.Copy.impl.Op(%C) [concrete] // CHECK:STDOUT: %bound_method: = bound_method %addr, %ptr.as.Copy.impl.Op.specific_fn [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Main.D: type = import_ref Main//b, D, loaded [concrete = constants.%C] // CHECK:STDOUT: %Main.b_val = import_ref Main//b, b_val, unloaded // CHECK:STDOUT: %Main.b_ptr = import_ref Main//b, b_ptr, unloaded // CHECK:STDOUT: %Main.E: type = import_ref Main//c, E, loaded [concrete = constants.%C] // CHECK:STDOUT: %Main.c_val = import_ref Main//c, c_val, unloaded // CHECK:STDOUT: %Main.c_ptr = import_ref Main//c, c_ptr, unloaded // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: .Copy = %Core.Copy // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: %Main.import_ref.8db: = import_ref Main//b, inst{{[0-9A-F]+}} [indirect], loaded [concrete = constants.%complete_type.357] // CHECK:STDOUT: %Main.import_ref.a60 = import_ref Main//b, inst{{[0-9A-F]+}} [indirect], unloaded // CHECK:STDOUT: %Core.Copy: type = import_ref Core//prelude/parts/copy, Copy, loaded [concrete = constants.%Copy.type] // CHECK:STDOUT: %Core.import_ref.203: @ptr.as.Copy.impl.%ptr.as.Copy.impl.Op.type (%ptr.as.Copy.impl.Op.type.2d4) = import_ref Core//prelude/parts/copy, loc{{\d+_\d+}}, loaded [symbolic = @ptr.as.Copy.impl.%ptr.as.Copy.impl.Op (constants.%ptr.as.Copy.impl.Op.74e)] // CHECK:STDOUT: %Copy.impl_witness_table.c3a = impl_witness_table (%Core.import_ref.203), @ptr.as.Copy.impl [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .D = imports.%Main.D // CHECK:STDOUT: .b_val = imports.%Main.b_val // CHECK:STDOUT: .b_ptr = imports.%Main.b_ptr // CHECK:STDOUT: .E = imports.%Main.E // CHECK:STDOUT: .c_val = imports.%Main.c_val // CHECK:STDOUT: .c_ptr = imports.%Main.c_ptr // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .value = %value // CHECK:STDOUT: .ptr = %ptr.loc8_5 // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %default.import = import // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %value.patt: %pattern_type.7c7 = ref_binding_pattern value [concrete] // CHECK:STDOUT: %value.var_patt: %pattern_type.7c7 = var_pattern %value.patt [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %value.var: ref %C = var %value.var_patt [concrete] // CHECK:STDOUT: %D.ref: type = name_ref D, imports.%Main.D [concrete = constants.%C] // CHECK:STDOUT: %value: ref %C = ref_binding value, %value.var [concrete = %value.var] // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %ptr.patt: %pattern_type.506 = ref_binding_pattern ptr [concrete] // CHECK:STDOUT: %ptr.var_patt: %pattern_type.506 = var_pattern %ptr.patt [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %ptr.var: ref %ptr.31e = var %ptr.var_patt [concrete] // CHECK:STDOUT: %.loc8: type = splice_block %ptr.loc8_11 [concrete = constants.%ptr.31e] { // CHECK:STDOUT: %E.ref: type = name_ref E, imports.%Main.E [concrete = constants.%C] // CHECK:STDOUT: %ptr.loc8_11: type = ptr_type %E.ref [concrete = constants.%ptr.31e] // CHECK:STDOUT: } // CHECK:STDOUT: %ptr.loc8_5: ref %ptr.31e = ref_binding ptr, %ptr.var [concrete = %ptr.var] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @C [from "b.carbon"] { // CHECK:STDOUT: complete_type_witness = imports.%Main.import_ref.8db // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = imports.%Main.import_ref.a60 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @__global_init() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %.loc7_17.1: %empty_struct_type = struct_literal () [concrete = constants.%empty_struct] // CHECK:STDOUT: %.loc7_17.2: init %C to file.%value.var = class_init () [concrete = constants.%C.val] // CHECK:STDOUT: %.loc7_1: init %C = converted %.loc7_17.1, %.loc7_17.2 [concrete = constants.%C.val] // CHECK:STDOUT: assign file.%value.var, %.loc7_1 // CHECK:STDOUT: %value.ref: ref %C = name_ref value, file.%value [concrete = file.%value.var] // CHECK:STDOUT: %addr: %ptr.31e = addr_of %value.ref [concrete = constants.%addr] // CHECK:STDOUT: %impl.elem0: %.64b = impl_witness_access constants.%Copy.impl_witness.2c7, element0 [concrete = constants.%ptr.as.Copy.impl.Op.ed9] // CHECK:STDOUT: %bound_method.loc8_15.1: = bound_method %addr, %impl.elem0 [concrete = constants.%ptr.as.Copy.impl.Op.bound] // CHECK:STDOUT: %specific_fn: = specific_function %impl.elem0, @ptr.as.Copy.impl.Op(constants.%C) [concrete = constants.%ptr.as.Copy.impl.Op.specific_fn] // CHECK:STDOUT: %bound_method.loc8_15.2: = bound_method %addr, %specific_fn [concrete = constants.%bound_method] // CHECK:STDOUT: %ptr.as.Copy.impl.Op.call: init %ptr.31e = call %bound_method.loc8_15.2(%addr) [concrete = constants.%addr] // CHECK:STDOUT: assign file.%ptr.var, %ptr.as.Copy.impl.Op.call // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- diamond_reverse.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %C: type = class_type @C [concrete] // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %complete_type.357: = complete_type_witness %empty_struct_type [concrete] // CHECK:STDOUT: %pattern_type.7c7: type = pattern_type %C [concrete] // CHECK:STDOUT: %empty_struct: %empty_struct_type = struct_value () [concrete] // CHECK:STDOUT: %C.val: %C = struct_value () [concrete] // CHECK:STDOUT: %ptr.31e: type = ptr_type %C [concrete] // CHECK:STDOUT: %pattern_type.506: type = pattern_type %ptr.31e [concrete] // CHECK:STDOUT: %addr: %ptr.31e = addr_of file.%value.var [concrete] // CHECK:STDOUT: %Copy.type: type = facet_type <@Copy> [concrete] // CHECK:STDOUT: %T.67d: type = symbolic_binding T, 0 [symbolic] // CHECK:STDOUT: %ptr.as.Copy.impl.Op.type.2d4: type = fn_type @ptr.as.Copy.impl.Op, @ptr.as.Copy.impl(%T.67d) [symbolic] // CHECK:STDOUT: %ptr.as.Copy.impl.Op.74e: %ptr.as.Copy.impl.Op.type.2d4 = struct_value () [symbolic] // CHECK:STDOUT: %Copy.impl_witness.2c7: = impl_witness imports.%Copy.impl_witness_table.c3a, @ptr.as.Copy.impl(%C) [concrete] // CHECK:STDOUT: %ptr.as.Copy.impl.Op.type.411: type = fn_type @ptr.as.Copy.impl.Op, @ptr.as.Copy.impl(%C) [concrete] // CHECK:STDOUT: %ptr.as.Copy.impl.Op.ed9: %ptr.as.Copy.impl.Op.type.411 = struct_value () [concrete] // CHECK:STDOUT: %Copy.facet: %Copy.type = facet_value %ptr.31e, (%Copy.impl_witness.2c7) [concrete] // CHECK:STDOUT: %Copy.WithSelf.Op.type.259: type = fn_type @Copy.WithSelf.Op, @Copy.WithSelf(%Copy.facet) [concrete] // CHECK:STDOUT: %.64b: type = fn_type_with_self_type %Copy.WithSelf.Op.type.259, %Copy.facet [concrete] // CHECK:STDOUT: %ptr.as.Copy.impl.Op.bound: = bound_method %addr, %ptr.as.Copy.impl.Op.ed9 [concrete] // CHECK:STDOUT: %ptr.as.Copy.impl.Op.specific_fn: = specific_function %ptr.as.Copy.impl.Op.ed9, @ptr.as.Copy.impl.Op(%C) [concrete] // CHECK:STDOUT: %bound_method: = bound_method %addr, %ptr.as.Copy.impl.Op.specific_fn [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Main.E: type = import_ref Main//c, E, loaded [concrete = constants.%C] // CHECK:STDOUT: %Main.c_val = import_ref Main//c, c_val, unloaded // CHECK:STDOUT: %Main.c_ptr = import_ref Main//c, c_ptr, unloaded // CHECK:STDOUT: %Main.D: type = import_ref Main//b, D, loaded [concrete = constants.%C] // CHECK:STDOUT: %Main.b_val = import_ref Main//b, b_val, unloaded // CHECK:STDOUT: %Main.b_ptr = import_ref Main//b, b_ptr, unloaded // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: .Copy = %Core.Copy // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: %Main.import_ref.8db: = import_ref Main//b, inst{{[0-9A-F]+}} [indirect], loaded [concrete = constants.%complete_type.357] // CHECK:STDOUT: %Main.import_ref.a60 = import_ref Main//b, inst{{[0-9A-F]+}} [indirect], unloaded // CHECK:STDOUT: %Core.Copy: type = import_ref Core//prelude/parts/copy, Copy, loaded [concrete = constants.%Copy.type] // CHECK:STDOUT: %Core.import_ref.203: @ptr.as.Copy.impl.%ptr.as.Copy.impl.Op.type (%ptr.as.Copy.impl.Op.type.2d4) = import_ref Core//prelude/parts/copy, loc{{\d+_\d+}}, loaded [symbolic = @ptr.as.Copy.impl.%ptr.as.Copy.impl.Op (constants.%ptr.as.Copy.impl.Op.74e)] // CHECK:STDOUT: %Copy.impl_witness_table.c3a = impl_witness_table (%Core.import_ref.203), @ptr.as.Copy.impl [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .E = imports.%Main.E // CHECK:STDOUT: .c_val = imports.%Main.c_val // CHECK:STDOUT: .c_ptr = imports.%Main.c_ptr // CHECK:STDOUT: .D = imports.%Main.D // CHECK:STDOUT: .b_val = imports.%Main.b_val // CHECK:STDOUT: .b_ptr = imports.%Main.b_ptr // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .value = %value // CHECK:STDOUT: .ptr = %ptr.loc8_5 // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %default.import = import // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %value.patt: %pattern_type.7c7 = ref_binding_pattern value [concrete] // CHECK:STDOUT: %value.var_patt: %pattern_type.7c7 = var_pattern %value.patt [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %value.var: ref %C = var %value.var_patt [concrete] // CHECK:STDOUT: %D.ref: type = name_ref D, imports.%Main.D [concrete = constants.%C] // CHECK:STDOUT: %value: ref %C = ref_binding value, %value.var [concrete = %value.var] // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %ptr.patt: %pattern_type.506 = ref_binding_pattern ptr [concrete] // CHECK:STDOUT: %ptr.var_patt: %pattern_type.506 = var_pattern %ptr.patt [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %ptr.var: ref %ptr.31e = var %ptr.var_patt [concrete] // CHECK:STDOUT: %.loc8: type = splice_block %ptr.loc8_11 [concrete = constants.%ptr.31e] { // CHECK:STDOUT: %E.ref: type = name_ref E, imports.%Main.E [concrete = constants.%C] // CHECK:STDOUT: %ptr.loc8_11: type = ptr_type %E.ref [concrete = constants.%ptr.31e] // CHECK:STDOUT: } // CHECK:STDOUT: %ptr.loc8_5: ref %ptr.31e = ref_binding ptr, %ptr.var [concrete = %ptr.var] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @C [from "b.carbon"] { // CHECK:STDOUT: complete_type_witness = imports.%Main.import_ref.8db // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = imports.%Main.import_ref.a60 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @__global_init() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %.loc7_17.1: %empty_struct_type = struct_literal () [concrete = constants.%empty_struct] // CHECK:STDOUT: %.loc7_17.2: init %C to file.%value.var = class_init () [concrete = constants.%C.val] // CHECK:STDOUT: %.loc7_1: init %C = converted %.loc7_17.1, %.loc7_17.2 [concrete = constants.%C.val] // CHECK:STDOUT: assign file.%value.var, %.loc7_1 // CHECK:STDOUT: %value.ref: ref %C = name_ref value, file.%value [concrete = file.%value.var] // CHECK:STDOUT: %addr: %ptr.31e = addr_of %value.ref [concrete = constants.%addr] // CHECK:STDOUT: %impl.elem0: %.64b = impl_witness_access constants.%Copy.impl_witness.2c7, element0 [concrete = constants.%ptr.as.Copy.impl.Op.ed9] // CHECK:STDOUT: %bound_method.loc8_15.1: = bound_method %addr, %impl.elem0 [concrete = constants.%ptr.as.Copy.impl.Op.bound] // CHECK:STDOUT: %specific_fn: = specific_function %impl.elem0, @ptr.as.Copy.impl.Op(constants.%C) [concrete = constants.%ptr.as.Copy.impl.Op.specific_fn] // CHECK:STDOUT: %bound_method.loc8_15.2: = bound_method %addr, %specific_fn [concrete = constants.%bound_method] // CHECK:STDOUT: %ptr.as.Copy.impl.Op.call: init %ptr.31e = call %bound_method.loc8_15.2(%addr) [concrete = constants.%addr] // CHECK:STDOUT: assign file.%ptr.var, %ptr.as.Copy.impl.Op.call // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/class/import_member_cycle.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/convert.carbon // TODO: Add ranges and switch to "--dump-sem-ir-ranges=only". // EXTRA-ARGS: --dump-sem-ir-ranges=if-present // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/class/import_member_cycle.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/class/import_member_cycle.carbon // --- a.carbon library "[[@TEST_NAME]]"; class Cycle { var a: Cycle*; } // --- b.carbon library "[[@TEST_NAME]]"; import library "a"; fn Run() { var unused a: Cycle*; } // CHECK:STDOUT: --- a.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %Cycle: type = class_type @Cycle [concrete] // CHECK:STDOUT: %ptr: type = ptr_type %Cycle [concrete] // CHECK:STDOUT: %Cycle.elem: type = unbound_element_type %Cycle, %ptr [concrete] // CHECK:STDOUT: %struct_type.a: type = struct_type {.a: %ptr} [concrete] // CHECK:STDOUT: %complete_type: = complete_type_witness %struct_type.a [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .Cycle = %Cycle.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %Cycle.decl: type = class_decl @Cycle [concrete = constants.%Cycle] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @Cycle { // CHECK:STDOUT: %Cycle.ref: type = name_ref Cycle, file.%Cycle.decl [concrete = constants.%Cycle] // CHECK:STDOUT: %ptr: type = ptr_type %Cycle.ref [concrete = constants.%ptr] // CHECK:STDOUT: %.loc5: %Cycle.elem = field_decl a, element0 [concrete] // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%struct_type.a [concrete = constants.%complete_type] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%Cycle // CHECK:STDOUT: .Cycle = // CHECK:STDOUT: .a = %.loc5 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- b.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %Run.type: type = fn_type @Run [concrete] // CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] // CHECK:STDOUT: %Run: %Run.type = struct_value () [concrete] // CHECK:STDOUT: %Cycle: type = class_type @Cycle [concrete] // CHECK:STDOUT: %ptr: type = ptr_type %Cycle [concrete] // CHECK:STDOUT: %struct_type.a: type = struct_type {.a: %ptr} [concrete] // CHECK:STDOUT: %complete_type.e68: = complete_type_witness %struct_type.a [concrete] // CHECK:STDOUT: %pattern_type.e31: type = pattern_type %ptr [concrete] // CHECK:STDOUT: %DefaultOrUnformed.type: type = facet_type <@DefaultOrUnformed> [concrete] // CHECK:STDOUT: %T: type = symbolic_binding T, 0 [symbolic] // CHECK:STDOUT: %T.as.DefaultOrUnformed.impl.Op.type.6c6: type = fn_type @T.as.DefaultOrUnformed.impl.Op, @T.as.DefaultOrUnformed.impl(%T) [symbolic] // CHECK:STDOUT: %T.as.DefaultOrUnformed.impl.Op.ae1: %T.as.DefaultOrUnformed.impl.Op.type.6c6 = struct_value () [symbolic] // CHECK:STDOUT: %DefaultOrUnformed.impl_witness.446: = impl_witness imports.%DefaultOrUnformed.impl_witness_table, @T.as.DefaultOrUnformed.impl(%ptr) [concrete] // CHECK:STDOUT: %T.as.DefaultOrUnformed.impl.Op.type.4d8: type = fn_type @T.as.DefaultOrUnformed.impl.Op, @T.as.DefaultOrUnformed.impl(%ptr) [concrete] // CHECK:STDOUT: %T.as.DefaultOrUnformed.impl.Op.1cb: %T.as.DefaultOrUnformed.impl.Op.type.4d8 = struct_value () [concrete] // CHECK:STDOUT: %DefaultOrUnformed.facet: %DefaultOrUnformed.type = facet_value %ptr, (%DefaultOrUnformed.impl_witness.446) [concrete] // CHECK:STDOUT: %T.as.DefaultOrUnformed.impl.Op.specific_fn: = specific_function %T.as.DefaultOrUnformed.impl.Op.1cb, @T.as.DefaultOrUnformed.impl.Op(%ptr) [concrete] // CHECK:STDOUT: %Destroy.type: type = facet_type <@Destroy> [concrete] // CHECK:STDOUT: %Destroy.Op.type: type = fn_type @Destroy.Op [concrete] // CHECK:STDOUT: %Destroy.Op: %Destroy.Op.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Main.Cycle: type = import_ref Main//a, Cycle, loaded [concrete = constants.%Cycle] // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: .DefaultOrUnformed = %Core.DefaultOrUnformed // CHECK:STDOUT: .Destroy = %Core.Destroy // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: %Main.import_ref.4e3: = import_ref Main//a, loc6_1, loaded [concrete = constants.%complete_type.e68] // CHECK:STDOUT: %Main.import_ref.fd1 = import_ref Main//a, inst{{[0-9A-F]+}} [no loc], unloaded // CHECK:STDOUT: %Main.import_ref.465 = import_ref Main//a, loc5_8, unloaded // CHECK:STDOUT: %Core.DefaultOrUnformed: type = import_ref Core//prelude/parts/default, DefaultOrUnformed, loaded [concrete = constants.%DefaultOrUnformed.type] // CHECK:STDOUT: %Core.import_ref.01d: @T.as.DefaultOrUnformed.impl.%T.as.DefaultOrUnformed.impl.Op.type (%T.as.DefaultOrUnformed.impl.Op.type.6c6) = import_ref Core//prelude/parts/default, loc{{\d+_\d+}}, loaded [symbolic = @T.as.DefaultOrUnformed.impl.%T.as.DefaultOrUnformed.impl.Op (constants.%T.as.DefaultOrUnformed.impl.Op.ae1)] // CHECK:STDOUT: %DefaultOrUnformed.impl_witness_table = impl_witness_table (%Core.import_ref.01d), @T.as.DefaultOrUnformed.impl [concrete] // CHECK:STDOUT: %Core.Destroy: type = import_ref Core//prelude/parts/destroy, Destroy, loaded [concrete = constants.%Destroy.type] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Cycle = imports.%Main.Cycle // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .Run = %Run.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %default.import = import // CHECK:STDOUT: %Run.decl: %Run.type = fn_decl @Run [concrete = constants.%Run] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @Cycle [from "a.carbon"] { // CHECK:STDOUT: complete_type_witness = imports.%Main.import_ref.4e3 // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = imports.%Main.import_ref.fd1 // CHECK:STDOUT: .a = imports.%Main.import_ref.465 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @Run() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %a.patt: %pattern_type.e31 = ref_binding_pattern a [concrete] // CHECK:STDOUT: %a.var_patt: %pattern_type.e31 = var_pattern %a.patt [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %a.var: ref %ptr = var %a.var_patt // CHECK:STDOUT: %DefaultOrUnformed.facet: %DefaultOrUnformed.type = facet_value constants.%ptr, (constants.%DefaultOrUnformed.impl_witness.446) [concrete = constants.%DefaultOrUnformed.facet] // CHECK:STDOUT: %.loc7_23.1: %DefaultOrUnformed.type = converted constants.%ptr, %DefaultOrUnformed.facet [concrete = constants.%DefaultOrUnformed.facet] // CHECK:STDOUT: %as_type: type = facet_access_type %.loc7_23.1 [concrete = constants.%ptr] // CHECK:STDOUT: %.loc7_23.2: type = converted %.loc7_23.1, %as_type [concrete = constants.%ptr] // CHECK:STDOUT: %T.as.DefaultOrUnformed.impl.Op.specific_fn: = specific_function constants.%T.as.DefaultOrUnformed.impl.Op.1cb, @T.as.DefaultOrUnformed.impl.Op(constants.%ptr) [concrete = constants.%T.as.DefaultOrUnformed.impl.Op.specific_fn] // CHECK:STDOUT: %T.as.DefaultOrUnformed.impl.Op.call: init %ptr = call %T.as.DefaultOrUnformed.impl.Op.specific_fn() // CHECK:STDOUT: assign %a.var, %T.as.DefaultOrUnformed.impl.Op.call // CHECK:STDOUT: %.loc7_22: type = splice_block %ptr [concrete = constants.%ptr] { // CHECK:STDOUT: %Cycle.ref: type = name_ref Cycle, imports.%Main.Cycle [concrete = constants.%Cycle] // CHECK:STDOUT: %ptr: type = ptr_type %Cycle.ref [concrete = constants.%ptr] // CHECK:STDOUT: } // CHECK:STDOUT: %a: ref %ptr = ref_binding a, %a.var // CHECK:STDOUT: %Destroy.Op.bound: = bound_method %a.var, constants.%Destroy.Op // CHECK:STDOUT: %Destroy.Op.call: init %empty_tuple.type = call %Destroy.Op.bound(%a.var) // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @Destroy.Op(%self.param: ref %ptr) = "no_op"; // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/class/import_struct_cycle.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/convert.carbon // TODO: Add ranges and switch to "--dump-sem-ir-ranges=only". // EXTRA-ARGS: --dump-sem-ir-ranges=if-present // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/class/import_struct_cycle.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/class/import_struct_cycle.carbon // --- a.carbon library "[[@TEST_NAME]]"; class Cycle; var a: {.b: Cycle*}; class Cycle { // The type here is equivalent to the `a` above, but on import can be resolved first. var c: {.b: Cycle*}; } // --- b.carbon library "[[@TEST_NAME]]"; import library "a"; fn Run() { a.b = (*a.b).c.b; } // CHECK:STDOUT: --- a.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %Cycle: type = class_type @Cycle [concrete] // CHECK:STDOUT: %ptr: type = ptr_type %Cycle [concrete] // CHECK:STDOUT: %struct_type.b: type = struct_type {.b: %ptr} [concrete] // CHECK:STDOUT: %pattern_type.d79: type = pattern_type %struct_type.b [concrete] // CHECK:STDOUT: %DefaultOrUnformed.type: type = facet_type <@DefaultOrUnformed> [concrete] // CHECK:STDOUT: %T: type = symbolic_binding T, 0 [symbolic] // CHECK:STDOUT: %T.as.DefaultOrUnformed.impl.Op.type.6c6: type = fn_type @T.as.DefaultOrUnformed.impl.Op, @T.as.DefaultOrUnformed.impl(%T) [symbolic] // CHECK:STDOUT: %T.as.DefaultOrUnformed.impl.Op.ae1: %T.as.DefaultOrUnformed.impl.Op.type.6c6 = struct_value () [symbolic] // CHECK:STDOUT: %DefaultOrUnformed.impl_witness.64d: = impl_witness imports.%DefaultOrUnformed.impl_witness_table, @T.as.DefaultOrUnformed.impl(%struct_type.b) [concrete] // CHECK:STDOUT: %T.as.DefaultOrUnformed.impl.Op.type.d8d: type = fn_type @T.as.DefaultOrUnformed.impl.Op, @T.as.DefaultOrUnformed.impl(%struct_type.b) [concrete] // CHECK:STDOUT: %T.as.DefaultOrUnformed.impl.Op.654: %T.as.DefaultOrUnformed.impl.Op.type.d8d = struct_value () [concrete] // CHECK:STDOUT: %DefaultOrUnformed.facet: %DefaultOrUnformed.type = facet_value %struct_type.b, (%DefaultOrUnformed.impl_witness.64d) [concrete] // CHECK:STDOUT: %T.as.DefaultOrUnformed.impl.Op.specific_fn: = specific_function %T.as.DefaultOrUnformed.impl.Op.654, @T.as.DefaultOrUnformed.impl.Op(%struct_type.b) [concrete] // CHECK:STDOUT: %Cycle.elem: type = unbound_element_type %Cycle, %struct_type.b [concrete] // CHECK:STDOUT: %struct_type.c: type = struct_type {.c: %struct_type.b} [concrete] // CHECK:STDOUT: %complete_type.3fc: = complete_type_witness %struct_type.c [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: .DefaultOrUnformed = %Core.DefaultOrUnformed // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: %Core.DefaultOrUnformed: type = import_ref Core//prelude/parts/default, DefaultOrUnformed, loaded [concrete = constants.%DefaultOrUnformed.type] // CHECK:STDOUT: %Core.import_ref.01d: @T.as.DefaultOrUnformed.impl.%T.as.DefaultOrUnformed.impl.Op.type (%T.as.DefaultOrUnformed.impl.Op.type.6c6) = import_ref Core//prelude/parts/default, loc{{\d+_\d+}}, loaded [symbolic = @T.as.DefaultOrUnformed.impl.%T.as.DefaultOrUnformed.impl.Op (constants.%T.as.DefaultOrUnformed.impl.Op.ae1)] // CHECK:STDOUT: %DefaultOrUnformed.impl_witness_table = impl_witness_table (%Core.import_ref.01d), @T.as.DefaultOrUnformed.impl [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .Cycle = %Cycle.decl.loc4 // CHECK:STDOUT: .a = %a // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %Cycle.decl.loc4: type = class_decl @Cycle [concrete = constants.%Cycle] {} {} // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %a.patt: %pattern_type.d79 = ref_binding_pattern a [concrete] // CHECK:STDOUT: %a.var_patt: %pattern_type.d79 = var_pattern %a.patt [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %a.var: ref %struct_type.b = var %a.var_patt [concrete] // CHECK:STDOUT: %.loc6: type = splice_block %struct_type.b [concrete = constants.%struct_type.b] { // CHECK:STDOUT: %Cycle.ref: type = name_ref Cycle, %Cycle.decl.loc4 [concrete = constants.%Cycle] // CHECK:STDOUT: %ptr: type = ptr_type %Cycle.ref [concrete = constants.%ptr] // CHECK:STDOUT: %struct_type.b: type = struct_type {.b: %ptr} [concrete = constants.%struct_type.b] // CHECK:STDOUT: } // CHECK:STDOUT: %a: ref %struct_type.b = ref_binding a, %a.var [concrete = %a.var] // CHECK:STDOUT: %Cycle.decl.loc8: type = class_decl @Cycle [concrete = constants.%Cycle] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @Cycle { // CHECK:STDOUT: %Cycle.ref: type = name_ref Cycle, file.%Cycle.decl.loc4 [concrete = constants.%Cycle] // CHECK:STDOUT: %ptr: type = ptr_type %Cycle.ref [concrete = constants.%ptr] // CHECK:STDOUT: %struct_type.b: type = struct_type {.b: %ptr} [concrete = constants.%struct_type.b] // CHECK:STDOUT: %.loc10: %Cycle.elem = field_decl c, element0 [concrete] // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%struct_type.c [concrete = constants.%complete_type.3fc] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%Cycle // CHECK:STDOUT: .Cycle = // CHECK:STDOUT: .c = %.loc10 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @__global_init() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %DefaultOrUnformed.facet: %DefaultOrUnformed.type = facet_value constants.%struct_type.b, (constants.%DefaultOrUnformed.impl_witness.64d) [concrete = constants.%DefaultOrUnformed.facet] // CHECK:STDOUT: %.loc6_20.1: %DefaultOrUnformed.type = converted constants.%struct_type.b, %DefaultOrUnformed.facet [concrete = constants.%DefaultOrUnformed.facet] // CHECK:STDOUT: %as_type: type = facet_access_type %.loc6_20.1 [concrete = constants.%struct_type.b] // CHECK:STDOUT: %.loc6_20.2: type = converted %.loc6_20.1, %as_type [concrete = constants.%struct_type.b] // CHECK:STDOUT: %T.as.DefaultOrUnformed.impl.Op.specific_fn: = specific_function constants.%T.as.DefaultOrUnformed.impl.Op.654, @T.as.DefaultOrUnformed.impl.Op(constants.%struct_type.b) [concrete = constants.%T.as.DefaultOrUnformed.impl.Op.specific_fn] // CHECK:STDOUT: %T.as.DefaultOrUnformed.impl.Op.call: init %struct_type.b = call %T.as.DefaultOrUnformed.impl.Op.specific_fn() // CHECK:STDOUT: assign file.%a.var, %T.as.DefaultOrUnformed.impl.Op.call // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- b.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %Run.type: type = fn_type @Run [concrete] // CHECK:STDOUT: %Run: %Run.type = struct_value () [concrete] // CHECK:STDOUT: %Cycle: type = class_type @Cycle [concrete] // CHECK:STDOUT: %ptr.e6c: type = ptr_type %Cycle [concrete] // CHECK:STDOUT: %struct_type.b: type = struct_type {.b: %ptr.e6c} [concrete] // CHECK:STDOUT: %struct_type.c: type = struct_type {.c: %struct_type.b} [concrete] // CHECK:STDOUT: %complete_type.3fc: = complete_type_witness %struct_type.c [concrete] // CHECK:STDOUT: %pattern_type.d79: type = pattern_type %struct_type.b [concrete] // CHECK:STDOUT: %.c65: ref %ptr.e6c = struct_access imports.%a.var, element0 [concrete] // CHECK:STDOUT: %Cycle.elem: type = unbound_element_type %Cycle, %struct_type.b [concrete] // CHECK:STDOUT: %Copy.type: type = facet_type <@Copy> [concrete] // CHECK:STDOUT: %T.67d: type = symbolic_binding T, 0 [symbolic] // CHECK:STDOUT: %ptr.as.Copy.impl.Op.type.2d4: type = fn_type @ptr.as.Copy.impl.Op, @ptr.as.Copy.impl(%T.67d) [symbolic] // CHECK:STDOUT: %ptr.as.Copy.impl.Op.74e: %ptr.as.Copy.impl.Op.type.2d4 = struct_value () [symbolic] // CHECK:STDOUT: %Copy.impl_witness.b7e: = impl_witness imports.%Copy.impl_witness_table.c3a, @ptr.as.Copy.impl(%Cycle) [concrete] // CHECK:STDOUT: %ptr.as.Copy.impl.Op.type.6f4: type = fn_type @ptr.as.Copy.impl.Op, @ptr.as.Copy.impl(%Cycle) [concrete] // CHECK:STDOUT: %ptr.as.Copy.impl.Op.7c8: %ptr.as.Copy.impl.Op.type.6f4 = struct_value () [concrete] // CHECK:STDOUT: %Copy.facet: %Copy.type = facet_value %ptr.e6c, (%Copy.impl_witness.b7e) [concrete] // CHECK:STDOUT: %Copy.WithSelf.Op.type.706: type = fn_type @Copy.WithSelf.Op, @Copy.WithSelf(%Copy.facet) [concrete] // CHECK:STDOUT: %.e5b: type = fn_type_with_self_type %Copy.WithSelf.Op.type.706, %Copy.facet [concrete] // CHECK:STDOUT: %ptr.as.Copy.impl.Op.specific_fn: = specific_function %ptr.as.Copy.impl.Op.7c8, @ptr.as.Copy.impl.Op(%Cycle) [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Main.Cycle = import_ref Main//a, Cycle, unloaded // CHECK:STDOUT: %Main.a: ref %struct_type.b = import_ref Main//a, a, loaded [concrete = %a.var] // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: .Copy = %Core.Copy // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: %Main.import_ref.741: = import_ref Main//a, loc11_1, loaded [concrete = constants.%complete_type.3fc] // CHECK:STDOUT: %Main.import_ref.fd1 = import_ref Main//a, inst{{[0-9A-F]+}} [no loc], unloaded // CHECK:STDOUT: %Main.import_ref.c81: %Cycle.elem = import_ref Main//a, loc10_8, loaded [concrete = %.a9f] // CHECK:STDOUT: %a.patt: %pattern_type.d79 = ref_binding_pattern a [concrete] // CHECK:STDOUT: %a.var_patt: %pattern_type.d79 = var_pattern %a.patt [concrete] // CHECK:STDOUT: %a.var: ref %struct_type.b = var %a.var_patt [concrete] // CHECK:STDOUT: %.a9f: %Cycle.elem = field_decl c, element0 [concrete] // CHECK:STDOUT: %Core.Copy: type = import_ref Core//prelude/parts/copy, Copy, loaded [concrete = constants.%Copy.type] // CHECK:STDOUT: %Core.import_ref.203: @ptr.as.Copy.impl.%ptr.as.Copy.impl.Op.type (%ptr.as.Copy.impl.Op.type.2d4) = import_ref Core//prelude/parts/copy, loc{{\d+_\d+}}, loaded [symbolic = @ptr.as.Copy.impl.%ptr.as.Copy.impl.Op (constants.%ptr.as.Copy.impl.Op.74e)] // CHECK:STDOUT: %Copy.impl_witness_table.c3a = impl_witness_table (%Core.import_ref.203), @ptr.as.Copy.impl [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Cycle = imports.%Main.Cycle // CHECK:STDOUT: .a = imports.%Main.a // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .Run = %Run.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %default.import = import // CHECK:STDOUT: %Run.decl: %Run.type = fn_decl @Run [concrete = constants.%Run] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @Cycle [from "a.carbon"] { // CHECK:STDOUT: complete_type_witness = imports.%Main.import_ref.741 // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = imports.%Main.import_ref.fd1 // CHECK:STDOUT: .c = imports.%Main.import_ref.c81 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @Run() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %a.ref.loc7_3: ref %struct_type.b = name_ref a, imports.%Main.a [concrete = imports.%a.var] // CHECK:STDOUT: %.loc7_4: ref %ptr.e6c = struct_access %a.ref.loc7_3, element0 [concrete = constants.%.c65] // CHECK:STDOUT: %a.ref.loc7_11: ref %struct_type.b = name_ref a, imports.%Main.a [concrete = imports.%a.var] // CHECK:STDOUT: %.loc7_12.1: ref %ptr.e6c = struct_access %a.ref.loc7_11, element0 [concrete = constants.%.c65] // CHECK:STDOUT: %.loc7_12.2: %ptr.e6c = acquire_value %.loc7_12.1 // CHECK:STDOUT: %.loc7_10: ref %Cycle = deref %.loc7_12.2 // CHECK:STDOUT: %c.ref: %Cycle.elem = name_ref c, imports.%Main.import_ref.c81 [concrete = imports.%.a9f] // CHECK:STDOUT: %.loc7_15: ref %struct_type.b = class_element_access %.loc7_10, element0 // CHECK:STDOUT: %.loc7_17.1: ref %ptr.e6c = struct_access %.loc7_15, element0 // CHECK:STDOUT: %.loc7_17.2: %ptr.e6c = acquire_value %.loc7_17.1 // CHECK:STDOUT: %impl.elem0: %.e5b = impl_witness_access constants.%Copy.impl_witness.b7e, element0 [concrete = constants.%ptr.as.Copy.impl.Op.7c8] // CHECK:STDOUT: %bound_method.loc7_17.1: = bound_method %.loc7_17.2, %impl.elem0 // CHECK:STDOUT: %specific_fn: = specific_function %impl.elem0, @ptr.as.Copy.impl.Op(constants.%Cycle) [concrete = constants.%ptr.as.Copy.impl.Op.specific_fn] // CHECK:STDOUT: %bound_method.loc7_17.2: = bound_method %.loc7_17.2, %specific_fn // CHECK:STDOUT: %ptr.as.Copy.impl.Op.call: init %ptr.e6c = call %bound_method.loc7_17.2(%.loc7_17.2) // CHECK:STDOUT: assign %.loc7_4, %ptr.as.Copy.impl.Op.call // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/class/incomplete_ref.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/none.carbon // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/class/incomplete_ref.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/class/incomplete_ref.carbon class Class; class IncompleteRefSelf { fn F[ref self: Class](); } fn CallIncompleteRefSelf(p: Class*) { p->(IncompleteRefSelf.F)(); } ================================================ FILE: toolchain/check/testdata/class/indirect_import_member.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/none.carbon // TODO: Add ranges and switch to "--dump-sem-ir-ranges=only". // EXTRA-ARGS: --dump-sem-ir-ranges=if-present // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/class/indirect_import_member.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/class/indirect_import_member.carbon // ============================================================================ // Setup files // ============================================================================ // --- a.carbon library "[[@TEST_NAME]]"; class C { fn F() {} } // --- b.carbon library "[[@TEST_NAME]]"; export import library "a"; // --- c.carbon library "[[@TEST_NAME]]"; import library "b"; export C; // --- d.carbon library "[[@TEST_NAME]]"; export import library "c"; // --- e.carbon library "[[@TEST_NAME]]"; import library "c"; class D { alias C = package.C; } // --- f.carbon library "[[@TEST_NAME]]"; export import library "e"; // ============================================================================ // Test files // ============================================================================ // --- use_b.carbon library "[[@TEST_NAME]]"; import library "b"; var x: () = C.F(); // --- use_c.carbon library "[[@TEST_NAME]]"; import library "c"; var x: () = C.F(); // --- use_d.carbon library "[[@TEST_NAME]]"; import library "d"; var x: () = C.F(); // --- use_e.carbon library "[[@TEST_NAME]]"; import library "e"; var x: () = D.C.F(); // --- use_f.carbon library "[[@TEST_NAME]]"; import library "f"; var x: () = D.C.F(); // CHECK:STDOUT: --- a.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %C: type = class_type @C [concrete] // CHECK:STDOUT: %C.F.type: type = fn_type @C.F [concrete] // CHECK:STDOUT: %C.F: %C.F.type = struct_value () [concrete] // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %complete_type: = complete_type_witness %empty_struct_type [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .C = %C.decl // CHECK:STDOUT: } // CHECK:STDOUT: %C.decl: type = class_decl @C [concrete = constants.%C] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @C { // CHECK:STDOUT: %C.F.decl: %C.F.type = fn_decl @C.F [concrete = constants.%C.F] {} {} // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%C // CHECK:STDOUT: .F = %C.F.decl // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @C.F() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- b.carbon // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Main.C = import_ref Main//a, C, unloaded // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .C = imports.%Main.C // CHECK:STDOUT: } // CHECK:STDOUT: %default.import = import // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- c.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %C: type = class_type @C [concrete] // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %complete_type: = complete_type_witness %empty_struct_type [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Main.C: type = import_ref Main//a, C, loaded [concrete = constants.%C] // CHECK:STDOUT: %Main.import_ref.8f2: = import_ref Main//a, loc6_1, loaded [concrete = constants.%complete_type] // CHECK:STDOUT: %Main.import_ref.743 = import_ref Main//a, inst{{[0-9A-F]+}} [no loc], unloaded // CHECK:STDOUT: %Main.import_ref.071 = import_ref Main//a, loc5_10, unloaded // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .C = %C // CHECK:STDOUT: } // CHECK:STDOUT: %default.import = import // CHECK:STDOUT: %C: type = export C, imports.%Main.C [concrete = constants.%C] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @C [from "a.carbon"] { // CHECK:STDOUT: complete_type_witness = imports.%Main.import_ref.8f2 // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = imports.%Main.import_ref.743 // CHECK:STDOUT: .F = imports.%Main.import_ref.071 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- d.carbon // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Main.C = import_ref Main//c, C, unloaded // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .C = imports.%Main.C // CHECK:STDOUT: } // CHECK:STDOUT: %default.import = import // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- e.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %D: type = class_type @D [concrete] // CHECK:STDOUT: %C: type = class_type @C [concrete] // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %complete_type: = complete_type_witness %empty_struct_type [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Main.C: type = import_ref Main//c, C, loaded [concrete = constants.%C] // CHECK:STDOUT: %Main.import_ref.8db: = import_ref Main//c, inst{{[0-9A-F]+}} [indirect], loaded [concrete = constants.%complete_type] // CHECK:STDOUT: %Main.import_ref.a60 = import_ref Main//c, inst{{[0-9A-F]+}} [indirect], unloaded // CHECK:STDOUT: %Main.import_ref.2b4 = import_ref Main//c, inst{{[0-9A-F]+}} [indirect], unloaded // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .C = imports.%Main.C // CHECK:STDOUT: .D = %D.decl // CHECK:STDOUT: } // CHECK:STDOUT: %default.import = import // CHECK:STDOUT: %D.decl: type = class_decl @D [concrete = constants.%D] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @D { // CHECK:STDOUT: %package.ref: = name_ref package, package [concrete = package] // CHECK:STDOUT: %C.ref: type = name_ref C, imports.%Main.C [concrete = constants.%C] // CHECK:STDOUT: %C: type = alias_binding C, imports.%Main.C [concrete = constants.%C] // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%D // CHECK:STDOUT: .C = %C // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @C [from "c.carbon"] { // CHECK:STDOUT: complete_type_witness = imports.%Main.import_ref.8db // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = imports.%Main.import_ref.a60 // CHECK:STDOUT: .F = imports.%Main.import_ref.2b4 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- f.carbon // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Main.D = import_ref Main//e, D, unloaded // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .D = imports.%Main.D // CHECK:STDOUT: } // CHECK:STDOUT: %default.import = import // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- use_b.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] // CHECK:STDOUT: %empty_tuple: %empty_tuple.type = tuple_value () [concrete] // CHECK:STDOUT: %pattern_type: type = pattern_type %empty_tuple.type [concrete] // CHECK:STDOUT: %C: type = class_type @C [concrete] // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %complete_type: = complete_type_witness %empty_struct_type [concrete] // CHECK:STDOUT: %C.F.type: type = fn_type @C.F [concrete] // CHECK:STDOUT: %C.F: %C.F.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Main.C: type = import_ref Main//a, C, loaded [concrete = constants.%C] // CHECK:STDOUT: %Main.import_ref.8f2: = import_ref Main//a, loc6_1, loaded [concrete = constants.%complete_type] // CHECK:STDOUT: %Main.import_ref.743 = import_ref Main//a, inst{{[0-9A-F]+}} [no loc], unloaded // CHECK:STDOUT: %Main.import_ref.25c: %C.F.type = import_ref Main//a, loc5_10, loaded [concrete = constants.%C.F] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .C = imports.%Main.C // CHECK:STDOUT: .x = %x // CHECK:STDOUT: } // CHECK:STDOUT: %default.import = import // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %x.patt: %pattern_type = ref_binding_pattern x [concrete] // CHECK:STDOUT: %x.var_patt: %pattern_type = var_pattern %x.patt [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %x.var: ref %empty_tuple.type = var %x.var_patt [concrete] // CHECK:STDOUT: %.loc6_9.1: type = splice_block %.loc6_9.3 [concrete = constants.%empty_tuple.type] { // CHECK:STDOUT: %.loc6_9.2: %empty_tuple.type = tuple_literal () [concrete = constants.%empty_tuple] // CHECK:STDOUT: %.loc6_9.3: type = converted %.loc6_9.2, constants.%empty_tuple.type [concrete = constants.%empty_tuple.type] // CHECK:STDOUT: } // CHECK:STDOUT: %x: ref %empty_tuple.type = ref_binding x, %x.var [concrete = %x.var] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @C [from "a.carbon"] { // CHECK:STDOUT: complete_type_witness = imports.%Main.import_ref.8f2 // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = imports.%Main.import_ref.743 // CHECK:STDOUT: .F = imports.%Main.import_ref.25c // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @C.F [from "a.carbon"]; // CHECK:STDOUT: // CHECK:STDOUT: fn @__global_init() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %C.ref: type = name_ref C, imports.%Main.C [concrete = constants.%C] // CHECK:STDOUT: %F.ref: %C.F.type = name_ref F, imports.%Main.import_ref.25c [concrete = constants.%C.F] // CHECK:STDOUT: %C.F.call: init %empty_tuple.type = call %F.ref() // CHECK:STDOUT: assign file.%x.var, %C.F.call // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- use_c.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] // CHECK:STDOUT: %empty_tuple: %empty_tuple.type = tuple_value () [concrete] // CHECK:STDOUT: %pattern_type: type = pattern_type %empty_tuple.type [concrete] // CHECK:STDOUT: %C: type = class_type @C [concrete] // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %complete_type: = complete_type_witness %empty_struct_type [concrete] // CHECK:STDOUT: %C.F.type: type = fn_type @C.F [concrete] // CHECK:STDOUT: %C.F: %C.F.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Main.C: type = import_ref Main//c, C, loaded [concrete = constants.%C] // CHECK:STDOUT: %Main.import_ref.8db: = import_ref Main//c, inst{{[0-9A-F]+}} [indirect], loaded [concrete = constants.%complete_type] // CHECK:STDOUT: %Main.import_ref.a60 = import_ref Main//c, inst{{[0-9A-F]+}} [indirect], unloaded // CHECK:STDOUT: %Main.import_ref.7e5: %C.F.type = import_ref Main//c, inst{{[0-9A-F]+}} [indirect], loaded [concrete = constants.%C.F] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .C = imports.%Main.C // CHECK:STDOUT: .x = %x // CHECK:STDOUT: } // CHECK:STDOUT: %default.import = import // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %x.patt: %pattern_type = ref_binding_pattern x [concrete] // CHECK:STDOUT: %x.var_patt: %pattern_type = var_pattern %x.patt [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %x.var: ref %empty_tuple.type = var %x.var_patt [concrete] // CHECK:STDOUT: %.loc6_9.1: type = splice_block %.loc6_9.3 [concrete = constants.%empty_tuple.type] { // CHECK:STDOUT: %.loc6_9.2: %empty_tuple.type = tuple_literal () [concrete = constants.%empty_tuple] // CHECK:STDOUT: %.loc6_9.3: type = converted %.loc6_9.2, constants.%empty_tuple.type [concrete = constants.%empty_tuple.type] // CHECK:STDOUT: } // CHECK:STDOUT: %x: ref %empty_tuple.type = ref_binding x, %x.var [concrete = %x.var] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @C [from "c.carbon"] { // CHECK:STDOUT: complete_type_witness = imports.%Main.import_ref.8db // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = imports.%Main.import_ref.a60 // CHECK:STDOUT: .F = imports.%Main.import_ref.7e5 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @C.F [from "a.carbon"]; // CHECK:STDOUT: // CHECK:STDOUT: fn @__global_init() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %C.ref: type = name_ref C, imports.%Main.C [concrete = constants.%C] // CHECK:STDOUT: %F.ref: %C.F.type = name_ref F, imports.%Main.import_ref.7e5 [concrete = constants.%C.F] // CHECK:STDOUT: %C.F.call: init %empty_tuple.type = call %F.ref() // CHECK:STDOUT: assign file.%x.var, %C.F.call // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- use_d.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] // CHECK:STDOUT: %empty_tuple: %empty_tuple.type = tuple_value () [concrete] // CHECK:STDOUT: %pattern_type: type = pattern_type %empty_tuple.type [concrete] // CHECK:STDOUT: %C: type = class_type @C [concrete] // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %complete_type: = complete_type_witness %empty_struct_type [concrete] // CHECK:STDOUT: %C.F.type: type = fn_type @C.F [concrete] // CHECK:STDOUT: %C.F: %C.F.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Main.C: type = import_ref Main//c, C, loaded [concrete = constants.%C] // CHECK:STDOUT: %Main.import_ref.8db: = import_ref Main//c, inst{{[0-9A-F]+}} [indirect], loaded [concrete = constants.%complete_type] // CHECK:STDOUT: %Main.import_ref.a60 = import_ref Main//c, inst{{[0-9A-F]+}} [indirect], unloaded // CHECK:STDOUT: %Main.import_ref.7e5: %C.F.type = import_ref Main//c, inst{{[0-9A-F]+}} [indirect], loaded [concrete = constants.%C.F] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .C = imports.%Main.C // CHECK:STDOUT: .x = %x // CHECK:STDOUT: } // CHECK:STDOUT: %default.import = import // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %x.patt: %pattern_type = ref_binding_pattern x [concrete] // CHECK:STDOUT: %x.var_patt: %pattern_type = var_pattern %x.patt [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %x.var: ref %empty_tuple.type = var %x.var_patt [concrete] // CHECK:STDOUT: %.loc6_9.1: type = splice_block %.loc6_9.3 [concrete = constants.%empty_tuple.type] { // CHECK:STDOUT: %.loc6_9.2: %empty_tuple.type = tuple_literal () [concrete = constants.%empty_tuple] // CHECK:STDOUT: %.loc6_9.3: type = converted %.loc6_9.2, constants.%empty_tuple.type [concrete = constants.%empty_tuple.type] // CHECK:STDOUT: } // CHECK:STDOUT: %x: ref %empty_tuple.type = ref_binding x, %x.var [concrete = %x.var] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @C [from "c.carbon"] { // CHECK:STDOUT: complete_type_witness = imports.%Main.import_ref.8db // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = imports.%Main.import_ref.a60 // CHECK:STDOUT: .F = imports.%Main.import_ref.7e5 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @C.F [from "a.carbon"]; // CHECK:STDOUT: // CHECK:STDOUT: fn @__global_init() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %C.ref: type = name_ref C, imports.%Main.C [concrete = constants.%C] // CHECK:STDOUT: %F.ref: %C.F.type = name_ref F, imports.%Main.import_ref.7e5 [concrete = constants.%C.F] // CHECK:STDOUT: %C.F.call: init %empty_tuple.type = call %F.ref() // CHECK:STDOUT: assign file.%x.var, %C.F.call // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- use_e.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] // CHECK:STDOUT: %empty_tuple: %empty_tuple.type = tuple_value () [concrete] // CHECK:STDOUT: %pattern_type: type = pattern_type %empty_tuple.type [concrete] // CHECK:STDOUT: %D: type = class_type @D [concrete] // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %complete_type: = complete_type_witness %empty_struct_type [concrete] // CHECK:STDOUT: %C: type = class_type @C [concrete] // CHECK:STDOUT: %C.F.type: type = fn_type @C.F [concrete] // CHECK:STDOUT: %C.F: %C.F.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Main.D: type = import_ref Main//e, D, loaded [concrete = constants.%D] // CHECK:STDOUT: %Main.import_ref.8f2: = import_ref Main//e, loc8_1, loaded [concrete = constants.%complete_type] // CHECK:STDOUT: %Main.import_ref.1d5 = import_ref Main//e, inst{{[0-9A-F]+}} [no loc], unloaded // CHECK:STDOUT: %Main.import_ref.7a9: type = import_ref Main//e, loc7_9, loaded [concrete = constants.%C] // CHECK:STDOUT: %Main.import_ref.8f3: = import_ref Main//e, inst{{[0-9A-F]+}} [indirect], loaded [concrete = constants.%complete_type] // CHECK:STDOUT: %Main.import_ref.76e = import_ref Main//e, inst{{[0-9A-F]+}} [indirect], unloaded // CHECK:STDOUT: %Main.import_ref.c63: %C.F.type = import_ref Main//e, inst{{[0-9A-F]+}} [indirect], loaded [concrete = constants.%C.F] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .D = imports.%Main.D // CHECK:STDOUT: .x = %x // CHECK:STDOUT: } // CHECK:STDOUT: %default.import = import // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %x.patt: %pattern_type = ref_binding_pattern x [concrete] // CHECK:STDOUT: %x.var_patt: %pattern_type = var_pattern %x.patt [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %x.var: ref %empty_tuple.type = var %x.var_patt [concrete] // CHECK:STDOUT: %.loc6_9.1: type = splice_block %.loc6_9.3 [concrete = constants.%empty_tuple.type] { // CHECK:STDOUT: %.loc6_9.2: %empty_tuple.type = tuple_literal () [concrete = constants.%empty_tuple] // CHECK:STDOUT: %.loc6_9.3: type = converted %.loc6_9.2, constants.%empty_tuple.type [concrete = constants.%empty_tuple.type] // CHECK:STDOUT: } // CHECK:STDOUT: %x: ref %empty_tuple.type = ref_binding x, %x.var [concrete = %x.var] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @D [from "e.carbon"] { // CHECK:STDOUT: complete_type_witness = imports.%Main.import_ref.8f2 // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = imports.%Main.import_ref.1d5 // CHECK:STDOUT: .C = imports.%Main.import_ref.7a9 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @C [from "e.carbon"] { // CHECK:STDOUT: complete_type_witness = imports.%Main.import_ref.8f3 // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = imports.%Main.import_ref.76e // CHECK:STDOUT: .F = imports.%Main.import_ref.c63 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @C.F [from "a.carbon"]; // CHECK:STDOUT: // CHECK:STDOUT: fn @__global_init() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %D.ref: type = name_ref D, imports.%Main.D [concrete = constants.%D] // CHECK:STDOUT: %C.ref: type = name_ref C, imports.%Main.import_ref.7a9 [concrete = constants.%C] // CHECK:STDOUT: %F.ref: %C.F.type = name_ref F, imports.%Main.import_ref.c63 [concrete = constants.%C.F] // CHECK:STDOUT: %C.F.call: init %empty_tuple.type = call %F.ref() // CHECK:STDOUT: assign file.%x.var, %C.F.call // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- use_f.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] // CHECK:STDOUT: %empty_tuple: %empty_tuple.type = tuple_value () [concrete] // CHECK:STDOUT: %pattern_type: type = pattern_type %empty_tuple.type [concrete] // CHECK:STDOUT: %D: type = class_type @D [concrete] // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %complete_type: = complete_type_witness %empty_struct_type [concrete] // CHECK:STDOUT: %C: type = class_type @C [concrete] // CHECK:STDOUT: %C.F.type: type = fn_type @C.F [concrete] // CHECK:STDOUT: %C.F: %C.F.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Main.D: type = import_ref Main//e, D, loaded [concrete = constants.%D] // CHECK:STDOUT: %Main.import_ref.8f2: = import_ref Main//e, loc8_1, loaded [concrete = constants.%complete_type] // CHECK:STDOUT: %Main.import_ref.1d5 = import_ref Main//e, inst{{[0-9A-F]+}} [no loc], unloaded // CHECK:STDOUT: %Main.import_ref.7a9: type = import_ref Main//e, loc7_9, loaded [concrete = constants.%C] // CHECK:STDOUT: %Main.import_ref.8f3: = import_ref Main//e, inst{{[0-9A-F]+}} [indirect], loaded [concrete = constants.%complete_type] // CHECK:STDOUT: %Main.import_ref.76e = import_ref Main//e, inst{{[0-9A-F]+}} [indirect], unloaded // CHECK:STDOUT: %Main.import_ref.c63: %C.F.type = import_ref Main//e, inst{{[0-9A-F]+}} [indirect], loaded [concrete = constants.%C.F] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .D = imports.%Main.D // CHECK:STDOUT: .x = %x // CHECK:STDOUT: } // CHECK:STDOUT: %default.import = import // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %x.patt: %pattern_type = ref_binding_pattern x [concrete] // CHECK:STDOUT: %x.var_patt: %pattern_type = var_pattern %x.patt [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %x.var: ref %empty_tuple.type = var %x.var_patt [concrete] // CHECK:STDOUT: %.loc6_9.1: type = splice_block %.loc6_9.3 [concrete = constants.%empty_tuple.type] { // CHECK:STDOUT: %.loc6_9.2: %empty_tuple.type = tuple_literal () [concrete = constants.%empty_tuple] // CHECK:STDOUT: %.loc6_9.3: type = converted %.loc6_9.2, constants.%empty_tuple.type [concrete = constants.%empty_tuple.type] // CHECK:STDOUT: } // CHECK:STDOUT: %x: ref %empty_tuple.type = ref_binding x, %x.var [concrete = %x.var] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @D [from "e.carbon"] { // CHECK:STDOUT: complete_type_witness = imports.%Main.import_ref.8f2 // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = imports.%Main.import_ref.1d5 // CHECK:STDOUT: .C = imports.%Main.import_ref.7a9 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @C [from "e.carbon"] { // CHECK:STDOUT: complete_type_witness = imports.%Main.import_ref.8f3 // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = imports.%Main.import_ref.76e // CHECK:STDOUT: .F = imports.%Main.import_ref.c63 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @C.F [from "a.carbon"]; // CHECK:STDOUT: // CHECK:STDOUT: fn @__global_init() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %D.ref: type = name_ref D, imports.%Main.D [concrete = constants.%D] // CHECK:STDOUT: %C.ref: type = name_ref C, imports.%Main.import_ref.7a9 [concrete = constants.%C] // CHECK:STDOUT: %F.ref: %C.F.type = name_ref F, imports.%Main.import_ref.c63 [concrete = constants.%C.F] // CHECK:STDOUT: %C.F.call: init %empty_tuple.type = call %F.ref() // CHECK:STDOUT: assign file.%x.var, %C.F.call // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/class/inheritance/base.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/int.carbon // TODO: Add ranges and switch to "--dump-sem-ir-ranges=only". // EXTRA-ARGS: --dump-sem-ir-ranges=if-present // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/class/inheritance/base.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/class/inheritance/base.carbon // --- base.carbon package Base; base class Base { var b: i32; } class Derived { extend base: Base; var d: i32; } fn Make() -> Derived { return {.base = {.b = 4}, .d = 7}; } fn Access(d: Derived) -> (i32, i32) { return (d.d, d.base.b); } // --- fail_base_after_field.carbon package BaseAfterField; base class Base { } class Derived { var d: i32; // CHECK:STDERR: fail_base_after_field.carbon:[[@LINE+4]]:3: error: `base` declaration must appear before field declarations [BaseDeclAfterFieldDecl] // CHECK:STDERR: extend base: Base; // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~ // CHECK:STDERR: extend base: Base; } // CHECK:STDOUT: --- base.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %Base: type = class_type @Base [concrete] // CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [concrete] // CHECK:STDOUT: %Int.type: type = generic_class_type @Int [concrete] // CHECK:STDOUT: %Int.generic: %Int.type = struct_value () [concrete] // CHECK:STDOUT: %N: Core.IntLiteral = symbolic_binding N, 0 [symbolic] // CHECK:STDOUT: %i32: type = class_type @Int, @Int(%int_32) [concrete] // CHECK:STDOUT: %Base.elem: type = unbound_element_type %Base, %i32 [concrete] // CHECK:STDOUT: %struct_type.b.0a3: type = struct_type {.b: %i32} [concrete] // CHECK:STDOUT: %complete_type.ba8: = complete_type_witness %struct_type.b.0a3 [concrete] // CHECK:STDOUT: %Derived: type = class_type @Derived [concrete] // CHECK:STDOUT: %Derived.elem.b58: type = unbound_element_type %Derived, %Base [concrete] // CHECK:STDOUT: %Derived.elem.683: type = unbound_element_type %Derived, %i32 [concrete] // CHECK:STDOUT: %struct_type.base.d.81a: type = struct_type {.base: %Base, .d: %i32} [concrete] // CHECK:STDOUT: %complete_type.3b4: = complete_type_witness %struct_type.base.d.81a [concrete] // CHECK:STDOUT: %.c4f: Core.Form = init_form %Derived [concrete] // CHECK:STDOUT: %pattern_type.db9: type = pattern_type %Derived [concrete] // CHECK:STDOUT: %Make.type: type = fn_type @Make [concrete] // CHECK:STDOUT: %Make: %Make.type = struct_value () [concrete] // CHECK:STDOUT: %int_4.0c1: Core.IntLiteral = int_value 4 [concrete] // CHECK:STDOUT: %struct_type.b.a15: type = struct_type {.b: Core.IntLiteral} [concrete] // CHECK:STDOUT: %struct.a2e: %struct_type.b.a15 = struct_value (%int_4.0c1) [concrete] // CHECK:STDOUT: %int_7.29f: Core.IntLiteral = int_value 7 [concrete] // CHECK:STDOUT: %struct_type.base.d.a20: type = struct_type {.base: %struct_type.b.a15, .d: Core.IntLiteral} [concrete] // CHECK:STDOUT: %struct.ab7: %struct_type.base.d.a20 = struct_value (%struct.a2e, %int_7.29f) [concrete] // CHECK:STDOUT: %.dfa: type = partial_type %Base [concrete] // CHECK:STDOUT: %ImplicitAs.type.cc7: type = generic_interface_type @ImplicitAs [concrete] // CHECK:STDOUT: %ImplicitAs.generic: %ImplicitAs.type.cc7 = struct_value () [concrete] // CHECK:STDOUT: %ImplicitAs.type.e8c: type = facet_type <@ImplicitAs, @ImplicitAs(%i32)> [concrete] // CHECK:STDOUT: %To: Core.IntLiteral = symbolic_binding To, 0 [symbolic] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.4e6: type = fn_type @Core.IntLiteral.as.ImplicitAs.impl.Convert, @Core.IntLiteral.as.ImplicitAs.impl(%To) [symbolic] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.3c2: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.4e6 = struct_value () [symbolic] // CHECK:STDOUT: %ImplicitAs.impl_witness.6bc: = impl_witness imports.%ImplicitAs.impl_witness_table.74f, @Core.IntLiteral.as.ImplicitAs.impl(%int_32) [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.e0d: type = fn_type @Core.IntLiteral.as.ImplicitAs.impl.Convert, @Core.IntLiteral.as.ImplicitAs.impl(%int_32) [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.e0d = struct_value () [concrete] // CHECK:STDOUT: %ImplicitAs.facet: %ImplicitAs.type.e8c = facet_value Core.IntLiteral, (%ImplicitAs.impl_witness.6bc) [concrete] // CHECK:STDOUT: %ImplicitAs.WithSelf.Convert.type.b37: type = fn_type @ImplicitAs.WithSelf.Convert, @ImplicitAs.WithSelf(%i32, %ImplicitAs.facet) [concrete] // CHECK:STDOUT: %.545: type = fn_type_with_self_type %ImplicitAs.WithSelf.Convert.type.b37, %ImplicitAs.facet [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.bound.f0c: = bound_method %int_4.0c1, %Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5 [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn: = specific_function %Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5, @Core.IntLiteral.as.ImplicitAs.impl.Convert(%int_32) [concrete] // CHECK:STDOUT: %bound_method.6d7: = bound_method %int_4.0c1, %Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn [concrete] // CHECK:STDOUT: %int_4.940: %i32 = int_value 4 [concrete] // CHECK:STDOUT: %struct.5f8: %.dfa = struct_value (%int_4.940) [concrete] // CHECK:STDOUT: %Base.val: %Base = struct_value (%int_4.940) [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.bound.1e0: = bound_method %int_7.29f, %Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5 [concrete] // CHECK:STDOUT: %bound_method.bf2: = bound_method %int_7.29f, %Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn [concrete] // CHECK:STDOUT: %int_7.0b1: %i32 = int_value 7 [concrete] // CHECK:STDOUT: %Derived.val: %Derived = struct_value (%Base.val, %int_7.0b1) [concrete] // CHECK:STDOUT: %tuple.type.24b: type = tuple_type (type, type) [concrete] // CHECK:STDOUT: %tuple.95a: %tuple.type.24b = tuple_value (%i32, %i32) [concrete] // CHECK:STDOUT: %tuple.type.d07: type = tuple_type (%i32, %i32) [concrete] // CHECK:STDOUT: %.f32: Core.Form = init_form %tuple.type.d07 [concrete] // CHECK:STDOUT: %pattern_type.511: type = pattern_type %tuple.type.d07 [concrete] // CHECK:STDOUT: %Access.type: type = fn_type @Access [concrete] // CHECK:STDOUT: %Access: %Access.type = struct_value () [concrete] // CHECK:STDOUT: %Copy.type: type = facet_type <@Copy> [concrete] // CHECK:STDOUT: %Int.as.Copy.impl.Op.type.824: type = fn_type @Int.as.Copy.impl.Op, @Int.as.Copy.impl(%N) [symbolic] // CHECK:STDOUT: %Int.as.Copy.impl.Op.9b9: %Int.as.Copy.impl.Op.type.824 = struct_value () [symbolic] // CHECK:STDOUT: %Copy.impl_witness.f17: = impl_witness imports.%Copy.impl_witness_table.e76, @Int.as.Copy.impl(%int_32) [concrete] // CHECK:STDOUT: %Int.as.Copy.impl.Op.type.546: type = fn_type @Int.as.Copy.impl.Op, @Int.as.Copy.impl(%int_32) [concrete] // CHECK:STDOUT: %Int.as.Copy.impl.Op.664: %Int.as.Copy.impl.Op.type.546 = struct_value () [concrete] // CHECK:STDOUT: %Copy.facet: %Copy.type = facet_value %i32, (%Copy.impl_witness.f17) [concrete] // CHECK:STDOUT: %Copy.WithSelf.Op.type.081: type = fn_type @Copy.WithSelf.Op, @Copy.WithSelf(%Copy.facet) [concrete] // CHECK:STDOUT: %.8e2: type = fn_type_with_self_type %Copy.WithSelf.Op.type.081, %Copy.facet [concrete] // CHECK:STDOUT: %Int.as.Copy.impl.Op.specific_fn: = specific_function %Int.as.Copy.impl.Op.664, @Int.as.Copy.impl.Op(%int_32) [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: .Int = %Core.Int // CHECK:STDOUT: .ImplicitAs = %Core.ImplicitAs // CHECK:STDOUT: .Copy = %Core.Copy // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: %Core.Int: %Int.type = import_ref Core//prelude/parts/int, Int, loaded [concrete = constants.%Int.generic] // CHECK:STDOUT: %Core.ImplicitAs: %ImplicitAs.type.cc7 = import_ref Core//prelude/parts/as, ImplicitAs, loaded [concrete = constants.%ImplicitAs.generic] // CHECK:STDOUT: %Core.import_ref.42d: @Core.IntLiteral.as.ImplicitAs.impl.%Core.IntLiteral.as.ImplicitAs.impl.Convert.type (%Core.IntLiteral.as.ImplicitAs.impl.Convert.type.4e6) = import_ref Core//prelude/parts/int, loc{{\d+_\d+}}, loaded [symbolic = @Core.IntLiteral.as.ImplicitAs.impl.%Core.IntLiteral.as.ImplicitAs.impl.Convert (constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.3c2)] // CHECK:STDOUT: %ImplicitAs.impl_witness_table.74f = impl_witness_table (%Core.import_ref.42d), @Core.IntLiteral.as.ImplicitAs.impl [concrete] // CHECK:STDOUT: %Core.Copy: type = import_ref Core//prelude/parts/copy, Copy, loaded [concrete = constants.%Copy.type] // CHECK:STDOUT: %Core.import_ref.18d: @Int.as.Copy.impl.%Int.as.Copy.impl.Op.type (%Int.as.Copy.impl.Op.type.824) = import_ref Core//prelude/parts/int, loc{{\d+_\d+}}, loaded [symbolic = @Int.as.Copy.impl.%Int.as.Copy.impl.Op (constants.%Int.as.Copy.impl.Op.9b9)] // CHECK:STDOUT: %Copy.impl_witness_table.e76 = impl_witness_table (%Core.import_ref.18d), @Int.as.Copy.impl [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .Base = %Base.decl // CHECK:STDOUT: .Derived = %Derived.decl // CHECK:STDOUT: .Make = %Make.decl // CHECK:STDOUT: .Access = %Access.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %Base.decl: type = class_decl @Base [concrete = constants.%Base] {} {} // CHECK:STDOUT: %Derived.decl: type = class_decl @Derived [concrete = constants.%Derived] {} {} // CHECK:STDOUT: %Make.decl: %Make.type = fn_decl @Make [concrete = constants.%Make] { // CHECK:STDOUT: %return.patt: %pattern_type.db9 = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: %pattern_type.db9 = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %Derived.ref: type = name_ref Derived, file.%Derived.decl [concrete = constants.%Derived] // CHECK:STDOUT: %.loc13: Core.Form = init_form %Derived.ref [concrete = constants.%.c4f] // CHECK:STDOUT: %return.param: ref %Derived = out_param call_param0 // CHECK:STDOUT: %return: ref %Derived = return_slot %return.param // CHECK:STDOUT: } // CHECK:STDOUT: %Access.decl: %Access.type = fn_decl @Access [concrete = constants.%Access] { // CHECK:STDOUT: %d.patt: %pattern_type.db9 = value_binding_pattern d [concrete] // CHECK:STDOUT: %d.param_patt: %pattern_type.db9 = value_param_pattern %d.patt [concrete] // CHECK:STDOUT: %return.patt: %pattern_type.511 = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: %pattern_type.511 = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %i32.loc17_27: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %i32.loc17_32: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %.loc17_35.1: %tuple.type.24b = tuple_literal (%i32.loc17_27, %i32.loc17_32) [concrete = constants.%tuple.95a] // CHECK:STDOUT: %.loc17_35.2: type = converted %.loc17_35.1, constants.%tuple.type.d07 [concrete = constants.%tuple.type.d07] // CHECK:STDOUT: %.loc17_35.3: Core.Form = init_form %.loc17_35.2 [concrete = constants.%.f32] // CHECK:STDOUT: %d.param: %Derived = value_param call_param0 // CHECK:STDOUT: %Derived.ref: type = name_ref Derived, file.%Derived.decl [concrete = constants.%Derived] // CHECK:STDOUT: %d: %Derived = value_binding d, %d.param // CHECK:STDOUT: %return.param: ref %tuple.type.d07 = out_param call_param1 // CHECK:STDOUT: %return: ref %tuple.type.d07 = return_slot %return.param // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @Base { // CHECK:STDOUT: %i32: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %.loc4: %Base.elem = field_decl b, element0 [concrete] // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%struct_type.b.0a3 [concrete = constants.%complete_type.ba8] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%Base // CHECK:STDOUT: .b = %.loc4 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @Derived { // CHECK:STDOUT: %Base.ref: type = name_ref Base, file.%Base.decl [concrete = constants.%Base] // CHECK:STDOUT: %.loc8: %Derived.elem.b58 = base_decl %Base.ref, element0 [concrete] // CHECK:STDOUT: %i32: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %.loc10: %Derived.elem.683 = field_decl d, element1 [concrete] // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%struct_type.base.d.81a [concrete = constants.%complete_type.3b4] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%Derived // CHECK:STDOUT: .Base = // CHECK:STDOUT: .base = %.loc8 // CHECK:STDOUT: .d = %.loc10 // CHECK:STDOUT: extend %Base.ref // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @Make() -> out %return.param: %Derived { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %int_4: Core.IntLiteral = int_value 4 [concrete = constants.%int_4.0c1] // CHECK:STDOUT: %.loc14_26.1: %struct_type.b.a15 = struct_literal (%int_4) [concrete = constants.%struct.a2e] // CHECK:STDOUT: %int_7: Core.IntLiteral = int_value 7 [concrete = constants.%int_7.29f] // CHECK:STDOUT: %.loc14_35.1: %struct_type.base.d.a20 = struct_literal (%.loc14_26.1, %int_7) [concrete = constants.%struct.ab7] // CHECK:STDOUT: %impl.elem0.loc14_26: %.545 = impl_witness_access constants.%ImplicitAs.impl_witness.6bc, element0 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5] // CHECK:STDOUT: %bound_method.loc14_26.1: = bound_method %int_4, %impl.elem0.loc14_26 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.bound.f0c] // CHECK:STDOUT: %specific_fn.loc14_26: = specific_function %impl.elem0.loc14_26, @Core.IntLiteral.as.ImplicitAs.impl.Convert(constants.%int_32) [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn] // CHECK:STDOUT: %bound_method.loc14_26.2: = bound_method %int_4, %specific_fn.loc14_26 [concrete = constants.%bound_method.6d7] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc14_26: init %i32 = call %bound_method.loc14_26.2(%int_4) [concrete = constants.%int_4.940] // CHECK:STDOUT: %.loc14_26.2: init %i32 = converted %int_4, %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc14_26 [concrete = constants.%int_4.940] // CHECK:STDOUT: %.loc14_35.2: ref %.dfa = class_element_access %return.param, element0 // CHECK:STDOUT: %.loc14_26.3: ref %i32 = class_element_access %.loc14_35.2, element0 // CHECK:STDOUT: %.loc14_26.4: init %i32 to %.loc14_26.3 = in_place_init %.loc14_26.2 [concrete = constants.%int_4.940] // CHECK:STDOUT: %.loc14_26.5: init %.dfa to %.loc14_35.2 = class_init (%.loc14_26.4) [concrete = constants.%struct.5f8] // CHECK:STDOUT: %.loc14_35.3: init %.dfa = converted %.loc14_26.1, %.loc14_26.5 [concrete = constants.%struct.5f8] // CHECK:STDOUT: %.loc14_35.4: init %Base = as_compatible %.loc14_35.3 [concrete = constants.%Base.val] // CHECK:STDOUT: %impl.elem0.loc14_35: %.545 = impl_witness_access constants.%ImplicitAs.impl_witness.6bc, element0 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5] // CHECK:STDOUT: %bound_method.loc14_35.1: = bound_method %int_7, %impl.elem0.loc14_35 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.bound.1e0] // CHECK:STDOUT: %specific_fn.loc14_35: = specific_function %impl.elem0.loc14_35, @Core.IntLiteral.as.ImplicitAs.impl.Convert(constants.%int_32) [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn] // CHECK:STDOUT: %bound_method.loc14_35.2: = bound_method %int_7, %specific_fn.loc14_35 [concrete = constants.%bound_method.bf2] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc14_35: init %i32 = call %bound_method.loc14_35.2(%int_7) [concrete = constants.%int_7.0b1] // CHECK:STDOUT: %.loc14_35.5: init %i32 = converted %int_7, %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc14_35 [concrete = constants.%int_7.0b1] // CHECK:STDOUT: %.loc14_35.6: ref %i32 = class_element_access %return.param, element1 // CHECK:STDOUT: %.loc14_35.7: init %i32 to %.loc14_35.6 = in_place_init %.loc14_35.5 [concrete = constants.%int_7.0b1] // CHECK:STDOUT: %.loc14_35.8: init %Derived to %return.param = class_init (%.loc14_35.4, %.loc14_35.7) [concrete = constants.%Derived.val] // CHECK:STDOUT: %.loc14_36: init %Derived = converted %.loc14_35.1, %.loc14_35.8 [concrete = constants.%Derived.val] // CHECK:STDOUT: return %.loc14_36 to %return.param // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @Access(%d.param: %Derived) -> out %return.param: %tuple.type.d07 { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %d.ref.loc18_11: %Derived = name_ref d, %d // CHECK:STDOUT: %d.ref.loc18_12: %Derived.elem.683 = name_ref d, @Derived.%.loc10 [concrete = @Derived.%.loc10] // CHECK:STDOUT: %.loc18_12.1: ref %i32 = class_element_access %d.ref.loc18_11, element1 // CHECK:STDOUT: %.loc18_12.2: %i32 = acquire_value %.loc18_12.1 // CHECK:STDOUT: %d.ref.loc18_16: %Derived = name_ref d, %d // CHECK:STDOUT: %base.ref: %Derived.elem.b58 = name_ref base, @Derived.%.loc8 [concrete = @Derived.%.loc8] // CHECK:STDOUT: %.loc18_17.1: ref %Base = class_element_access %d.ref.loc18_16, element0 // CHECK:STDOUT: %.loc18_17.2: %Base = acquire_value %.loc18_17.1 // CHECK:STDOUT: %b.ref: %Base.elem = name_ref b, @Base.%.loc4 [concrete = @Base.%.loc4] // CHECK:STDOUT: %.loc18_22.1: ref %i32 = class_element_access %.loc18_17.2, element0 // CHECK:STDOUT: %.loc18_22.2: %i32 = acquire_value %.loc18_22.1 // CHECK:STDOUT: %.loc18_24.1: %tuple.type.d07 = tuple_literal (%.loc18_12.2, %.loc18_22.2) // CHECK:STDOUT: %impl.elem0.loc18_12: %.8e2 = impl_witness_access constants.%Copy.impl_witness.f17, element0 [concrete = constants.%Int.as.Copy.impl.Op.664] // CHECK:STDOUT: %bound_method.loc18_12.1: = bound_method %.loc18_12.2, %impl.elem0.loc18_12 // CHECK:STDOUT: %specific_fn.loc18_12: = specific_function %impl.elem0.loc18_12, @Int.as.Copy.impl.Op(constants.%int_32) [concrete = constants.%Int.as.Copy.impl.Op.specific_fn] // CHECK:STDOUT: %bound_method.loc18_12.2: = bound_method %.loc18_12.2, %specific_fn.loc18_12 // CHECK:STDOUT: %Int.as.Copy.impl.Op.call.loc18_12: init %i32 = call %bound_method.loc18_12.2(%.loc18_12.2) // CHECK:STDOUT: %tuple.elem0: ref %i32 = tuple_access %return.param, element0 // CHECK:STDOUT: %.loc18_24.2: init %i32 to %tuple.elem0 = in_place_init %Int.as.Copy.impl.Op.call.loc18_12 // CHECK:STDOUT: %impl.elem0.loc18_22: %.8e2 = impl_witness_access constants.%Copy.impl_witness.f17, element0 [concrete = constants.%Int.as.Copy.impl.Op.664] // CHECK:STDOUT: %bound_method.loc18_22.1: = bound_method %.loc18_22.2, %impl.elem0.loc18_22 // CHECK:STDOUT: %specific_fn.loc18_22: = specific_function %impl.elem0.loc18_22, @Int.as.Copy.impl.Op(constants.%int_32) [concrete = constants.%Int.as.Copy.impl.Op.specific_fn] // CHECK:STDOUT: %bound_method.loc18_22.2: = bound_method %.loc18_22.2, %specific_fn.loc18_22 // CHECK:STDOUT: %Int.as.Copy.impl.Op.call.loc18_22: init %i32 = call %bound_method.loc18_22.2(%.loc18_22.2) // CHECK:STDOUT: %tuple.elem1: ref %i32 = tuple_access %return.param, element1 // CHECK:STDOUT: %.loc18_24.3: init %i32 to %tuple.elem1 = in_place_init %Int.as.Copy.impl.Op.call.loc18_22 // CHECK:STDOUT: %.loc18_24.4: init %tuple.type.d07 to %return.param = tuple_init (%.loc18_24.2, %.loc18_24.3) // CHECK:STDOUT: %.loc18_25: init %tuple.type.d07 = converted %.loc18_24.1, %.loc18_24.4 // CHECK:STDOUT: return %.loc18_25 to %return.param // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- fail_base_after_field.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %Base: type = class_type @Base [concrete] // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %complete_type.357: = complete_type_witness %empty_struct_type [concrete] // CHECK:STDOUT: %Derived: type = class_type @Derived [concrete] // CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [concrete] // CHECK:STDOUT: %Int.type: type = generic_class_type @Int [concrete] // CHECK:STDOUT: %Int.generic: %Int.type = struct_value () [concrete] // CHECK:STDOUT: %i32: type = class_type @Int, @Int(%int_32) [concrete] // CHECK:STDOUT: %Derived.elem: type = unbound_element_type %Derived, %i32 [concrete] // CHECK:STDOUT: %struct_type.d: type = struct_type {.d: %i32} [concrete] // CHECK:STDOUT: %complete_type.860: = complete_type_witness %struct_type.d [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: .Int = %Core.Int // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: %Core.Int: %Int.type = import_ref Core//prelude/parts/int, Int, loaded [concrete = constants.%Int.generic] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .Base = %Base.decl // CHECK:STDOUT: .Derived = %Derived.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %Base.decl: type = class_decl @Base [concrete = constants.%Base] {} {} // CHECK:STDOUT: %Derived.decl: type = class_decl @Derived [concrete = constants.%Derived] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @Base { // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type.357] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%Base // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @Derived { // CHECK:STDOUT: %i32: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %.loc7: %Derived.elem = field_decl d, element0 [concrete] // CHECK:STDOUT: %Base.ref: type = name_ref Base, file.%Base.decl [concrete = constants.%Base] // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%struct_type.d [concrete = constants.%complete_type.860] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%Derived // CHECK:STDOUT: .d = %.loc7 // CHECK:STDOUT: .Base = // CHECK:STDOUT: } // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/class/inheritance/base_field.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/int.carbon // TODO: Add ranges and switch to "--dump-sem-ir-ranges=only". // EXTRA-ARGS: --dump-sem-ir-ranges=if-present // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/class/inheritance/base_field.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/class/inheritance/base_field.carbon base class Base { var a: i32; var b: i32; var c: i32; } class Derived { extend base: Base; var d: i32; var e: i32; } fn Access(p: Derived*) -> i32* { return &(*p).c; } // CHECK:STDOUT: --- base_field.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %Base: type = class_type @Base [concrete] // CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [concrete] // CHECK:STDOUT: %Int.type: type = generic_class_type @Int [concrete] // CHECK:STDOUT: %Int.generic: %Int.type = struct_value () [concrete] // CHECK:STDOUT: %i32: type = class_type @Int, @Int(%int_32) [concrete] // CHECK:STDOUT: %Base.elem: type = unbound_element_type %Base, %i32 [concrete] // CHECK:STDOUT: %struct_type.a.b.c: type = struct_type {.a: %i32, .b: %i32, .c: %i32} [concrete] // CHECK:STDOUT: %complete_type.ebc: = complete_type_witness %struct_type.a.b.c [concrete] // CHECK:STDOUT: %Derived: type = class_type @Derived [concrete] // CHECK:STDOUT: %Derived.elem.029: type = unbound_element_type %Derived, %Base [concrete] // CHECK:STDOUT: %Derived.elem.530: type = unbound_element_type %Derived, %i32 [concrete] // CHECK:STDOUT: %struct_type.base.d.e.b4b: type = struct_type {.base: %Base, .d: %i32, .e: %i32} [concrete] // CHECK:STDOUT: %complete_type.ea9: = complete_type_witness %struct_type.base.d.e.b4b [concrete] // CHECK:STDOUT: %ptr.f74: type = ptr_type %Derived [concrete] // CHECK:STDOUT: %pattern_type.0dd: type = pattern_type %ptr.f74 [concrete] // CHECK:STDOUT: %ptr.235: type = ptr_type %i32 [concrete] // CHECK:STDOUT: %.605: Core.Form = init_form %ptr.235 [concrete] // CHECK:STDOUT: %pattern_type.fe8: type = pattern_type %ptr.235 [concrete] // CHECK:STDOUT: %Access.type: type = fn_type @Access [concrete] // CHECK:STDOUT: %Access: %Access.type = struct_value () [concrete] // CHECK:STDOUT: %Copy.type: type = facet_type <@Copy> [concrete] // CHECK:STDOUT: %T.67d: type = symbolic_binding T, 0 [symbolic] // CHECK:STDOUT: %ptr.as.Copy.impl.Op.type.2d4: type = fn_type @ptr.as.Copy.impl.Op, @ptr.as.Copy.impl(%T.67d) [symbolic] // CHECK:STDOUT: %ptr.as.Copy.impl.Op.74e: %ptr.as.Copy.impl.Op.type.2d4 = struct_value () [symbolic] // CHECK:STDOUT: %Copy.impl_witness.843: = impl_witness imports.%Copy.impl_witness_table.c3a, @ptr.as.Copy.impl(%i32) [concrete] // CHECK:STDOUT: %ptr.as.Copy.impl.Op.type.c3c: type = fn_type @ptr.as.Copy.impl.Op, @ptr.as.Copy.impl(%i32) [concrete] // CHECK:STDOUT: %ptr.as.Copy.impl.Op.011: %ptr.as.Copy.impl.Op.type.c3c = struct_value () [concrete] // CHECK:STDOUT: %Copy.facet: %Copy.type = facet_value %ptr.235, (%Copy.impl_witness.843) [concrete] // CHECK:STDOUT: %Copy.WithSelf.Op.type.e01: type = fn_type @Copy.WithSelf.Op, @Copy.WithSelf(%Copy.facet) [concrete] // CHECK:STDOUT: %.a62: type = fn_type_with_self_type %Copy.WithSelf.Op.type.e01, %Copy.facet [concrete] // CHECK:STDOUT: %ptr.as.Copy.impl.Op.specific_fn: = specific_function %ptr.as.Copy.impl.Op.011, @ptr.as.Copy.impl.Op(%i32) [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: .Int = %Core.Int // CHECK:STDOUT: .Copy = %Core.Copy // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: %Core.Int: %Int.type = import_ref Core//prelude/parts/int, Int, loaded [concrete = constants.%Int.generic] // CHECK:STDOUT: %Core.Copy: type = import_ref Core//prelude/parts/copy, Copy, loaded [concrete = constants.%Copy.type] // CHECK:STDOUT: %Core.import_ref.203: @ptr.as.Copy.impl.%ptr.as.Copy.impl.Op.type (%ptr.as.Copy.impl.Op.type.2d4) = import_ref Core//prelude/parts/copy, loc{{\d+_\d+}}, loaded [symbolic = @ptr.as.Copy.impl.%ptr.as.Copy.impl.Op (constants.%ptr.as.Copy.impl.Op.74e)] // CHECK:STDOUT: %Copy.impl_witness_table.c3a = impl_witness_table (%Core.import_ref.203), @ptr.as.Copy.impl [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .Base = %Base.decl // CHECK:STDOUT: .Derived = %Derived.decl // CHECK:STDOUT: .Access = %Access.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %Base.decl: type = class_decl @Base [concrete = constants.%Base] {} {} // CHECK:STDOUT: %Derived.decl: type = class_decl @Derived [concrete = constants.%Derived] {} {} // CHECK:STDOUT: %Access.decl: %Access.type = fn_decl @Access [concrete = constants.%Access] { // CHECK:STDOUT: %p.patt: %pattern_type.0dd = value_binding_pattern p [concrete] // CHECK:STDOUT: %p.param_patt: %pattern_type.0dd = value_param_pattern %p.patt [concrete] // CHECK:STDOUT: %return.patt: %pattern_type.fe8 = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: %pattern_type.fe8 = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %i32: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %ptr.loc28_30: type = ptr_type %i32 [concrete = constants.%ptr.235] // CHECK:STDOUT: %.loc28_30: Core.Form = init_form %ptr.loc28_30 [concrete = constants.%.605] // CHECK:STDOUT: %p.param: %ptr.f74 = value_param call_param0 // CHECK:STDOUT: %.loc28_21: type = splice_block %ptr.loc28_21 [concrete = constants.%ptr.f74] { // CHECK:STDOUT: %Derived.ref: type = name_ref Derived, file.%Derived.decl [concrete = constants.%Derived] // CHECK:STDOUT: %ptr.loc28_21: type = ptr_type %Derived.ref [concrete = constants.%ptr.f74] // CHECK:STDOUT: } // CHECK:STDOUT: %p: %ptr.f74 = value_binding p, %p.param // CHECK:STDOUT: %return.param: ref %ptr.235 = out_param call_param1 // CHECK:STDOUT: %return: ref %ptr.235 = return_slot %return.param // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @Base { // CHECK:STDOUT: %i32.loc16: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %.loc16: %Base.elem = field_decl a, element0 [concrete] // CHECK:STDOUT: %i32.loc17: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %.loc17: %Base.elem = field_decl b, element1 [concrete] // CHECK:STDOUT: %i32.loc18: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %.loc18: %Base.elem = field_decl c, element2 [concrete] // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%struct_type.a.b.c [concrete = constants.%complete_type.ebc] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%Base // CHECK:STDOUT: .a = %.loc16 // CHECK:STDOUT: .b = %.loc17 // CHECK:STDOUT: .c = %.loc18 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @Derived { // CHECK:STDOUT: %Base.ref: type = name_ref Base, file.%Base.decl [concrete = constants.%Base] // CHECK:STDOUT: %.loc22: %Derived.elem.029 = base_decl %Base.ref, element0 [concrete] // CHECK:STDOUT: %i32.loc24: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %.loc24: %Derived.elem.530 = field_decl d, element1 [concrete] // CHECK:STDOUT: %i32.loc25: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %.loc25: %Derived.elem.530 = field_decl e, element2 [concrete] // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%struct_type.base.d.e.b4b [concrete = constants.%complete_type.ea9] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%Derived // CHECK:STDOUT: .Base = // CHECK:STDOUT: .base = %.loc22 // CHECK:STDOUT: .d = %.loc24 // CHECK:STDOUT: .e = %.loc25 // CHECK:STDOUT: .c = // CHECK:STDOUT: extend %Base.ref // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @Access(%p.param: %ptr.f74) -> out %return.param: %ptr.235 { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %p.ref: %ptr.f74 = name_ref p, %p // CHECK:STDOUT: %.loc29_12: ref %Derived = deref %p.ref // CHECK:STDOUT: %c.ref: %Base.elem = name_ref c, @Base.%.loc18 [concrete = @Base.%.loc18] // CHECK:STDOUT: %.loc29_15.1: ref %Base = class_element_access %.loc29_12, element0 // CHECK:STDOUT: %.loc29_15.2: ref %Base = converted %.loc29_12, %.loc29_15.1 // CHECK:STDOUT: %.loc29_15.3: ref %i32 = class_element_access %.loc29_15.2, element2 // CHECK:STDOUT: %addr: %ptr.235 = addr_of %.loc29_15.3 // CHECK:STDOUT: %impl.elem0: %.a62 = impl_witness_access constants.%Copy.impl_witness.843, element0 [concrete = constants.%ptr.as.Copy.impl.Op.011] // CHECK:STDOUT: %bound_method.loc29_10.1: = bound_method %addr, %impl.elem0 // CHECK:STDOUT: %specific_fn: = specific_function %impl.elem0, @ptr.as.Copy.impl.Op(constants.%i32) [concrete = constants.%ptr.as.Copy.impl.Op.specific_fn] // CHECK:STDOUT: %bound_method.loc29_10.2: = bound_method %addr, %specific_fn // CHECK:STDOUT: %ptr.as.Copy.impl.Op.call: init %ptr.235 = call %bound_method.loc29_10.2(%addr) // CHECK:STDOUT: return %ptr.as.Copy.impl.Op.call // CHECK:STDOUT: } // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/class/inheritance/base_function_unqualified.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/convert.carbon // TODO: Add ranges and switch to "--dump-sem-ir-ranges=only". // EXTRA-ARGS: --dump-sem-ir-ranges=if-present // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/class/inheritance/base_function_unqualified.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/class/inheritance/base_function_unqualified.carbon base class Base { fn F(); } class Derived { extend base: Base; fn G() { F(); } fn H(); } fn Derived.H() { F(); } // CHECK:STDOUT: --- base_function_unqualified.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %Base: type = class_type @Base [concrete] // CHECK:STDOUT: %Base.F.type: type = fn_type @Base.F [concrete] // CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] // CHECK:STDOUT: %Base.F: %Base.F.type = struct_value () [concrete] // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %complete_type.357: = complete_type_witness %empty_struct_type [concrete] // CHECK:STDOUT: %Derived: type = class_type @Derived [concrete] // CHECK:STDOUT: %Derived.elem: type = unbound_element_type %Derived, %Base [concrete] // CHECK:STDOUT: %Derived.G.type: type = fn_type @Derived.G [concrete] // CHECK:STDOUT: %Derived.G: %Derived.G.type = struct_value () [concrete] // CHECK:STDOUT: %Derived.H.type: type = fn_type @Derived.H [concrete] // CHECK:STDOUT: %Derived.H: %Derived.H.type = struct_value () [concrete] // CHECK:STDOUT: %struct_type.base: type = struct_type {.base: %Base} [concrete] // CHECK:STDOUT: %complete_type.5a1: = complete_type_witness %struct_type.base [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .Base = %Base.decl // CHECK:STDOUT: .Derived = %Derived.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %Base.decl: type = class_decl @Base [concrete = constants.%Base] {} {} // CHECK:STDOUT: %Derived.decl: type = class_decl @Derived [concrete = constants.%Derived] {} {} // CHECK:STDOUT: %Derived.H.decl: %Derived.H.type = fn_decl @Derived.H [concrete = constants.%Derived.H] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @Base { // CHECK:STDOUT: %Base.F.decl: %Base.F.type = fn_decl @Base.F [concrete = constants.%Base.F] {} {} // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type.357] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%Base // CHECK:STDOUT: .F = %Base.F.decl // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @Derived { // CHECK:STDOUT: %Base.ref: type = name_ref Base, file.%Base.decl [concrete = constants.%Base] // CHECK:STDOUT: %.loc20: %Derived.elem = base_decl %Base.ref, element0 [concrete] // CHECK:STDOUT: %Derived.G.decl: %Derived.G.type = fn_decl @Derived.G [concrete = constants.%Derived.G] {} {} // CHECK:STDOUT: %Derived.H.decl: %Derived.H.type = fn_decl @Derived.H [concrete = constants.%Derived.H] {} {} // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%struct_type.base [concrete = constants.%complete_type.5a1] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%Derived // CHECK:STDOUT: .Base = // CHECK:STDOUT: .base = %.loc20 // CHECK:STDOUT: .G = %Derived.G.decl // CHECK:STDOUT: .H = %Derived.H.decl // CHECK:STDOUT: .F = // CHECK:STDOUT: extend %Base.ref // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @Base.F(); // CHECK:STDOUT: // CHECK:STDOUT: fn @Derived.G() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %F.ref: %Base.F.type = name_ref F, @Base.%Base.F.decl [concrete = constants.%Base.F] // CHECK:STDOUT: %Base.F.call: init %empty_tuple.type = call %F.ref() // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @Derived.H() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %F.ref: %Base.F.type = name_ref F, @Base.%Base.F.decl [concrete = constants.%Base.F] // CHECK:STDOUT: %Base.F.call: init %empty_tuple.type = call %F.ref() // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/class/inheritance/base_method.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/int.carbon // TODO: Add ranges and switch to "--dump-sem-ir-ranges=only". // EXTRA-ARGS: --dump-sem-ir-ranges=if-present // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/class/inheritance/base_method.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/class/inheritance/base_method.carbon base class Base { var a: i32; fn F[ref self: Self](); } fn Base.F[ref self: Self]() { self.a = 1; } class Derived { extend base: Base; } fn Call(p: Derived*) { (*p).F(); } // CHECK:STDOUT: --- base_method.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %Base: type = class_type @Base [concrete] // CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [concrete] // CHECK:STDOUT: %Int.type: type = generic_class_type @Int [concrete] // CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] // CHECK:STDOUT: %Int.generic: %Int.type = struct_value () [concrete] // CHECK:STDOUT: %i32: type = class_type @Int, @Int(%int_32) [concrete] // CHECK:STDOUT: %Base.elem: type = unbound_element_type %Base, %i32 [concrete] // CHECK:STDOUT: %pattern_type.101: type = pattern_type %Base [concrete] // CHECK:STDOUT: %Base.F.type: type = fn_type @Base.F [concrete] // CHECK:STDOUT: %Base.F: %Base.F.type = struct_value () [concrete] // CHECK:STDOUT: %struct_type.a: type = struct_type {.a: %i32} [concrete] // CHECK:STDOUT: %complete_type.fd7: = complete_type_witness %struct_type.a [concrete] // CHECK:STDOUT: %int_1.5b8: Core.IntLiteral = int_value 1 [concrete] // CHECK:STDOUT: %ImplicitAs.type.cc7: type = generic_interface_type @ImplicitAs [concrete] // CHECK:STDOUT: %ImplicitAs.generic: %ImplicitAs.type.cc7 = struct_value () [concrete] // CHECK:STDOUT: %ImplicitAs.type.e8c: type = facet_type <@ImplicitAs, @ImplicitAs(%i32)> [concrete] // CHECK:STDOUT: %To: Core.IntLiteral = symbolic_binding To, 0 [symbolic] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.4e6: type = fn_type @Core.IntLiteral.as.ImplicitAs.impl.Convert, @Core.IntLiteral.as.ImplicitAs.impl(%To) [symbolic] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.3c2: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.4e6 = struct_value () [symbolic] // CHECK:STDOUT: %ImplicitAs.impl_witness.6bc: = impl_witness imports.%ImplicitAs.impl_witness_table.74f, @Core.IntLiteral.as.ImplicitAs.impl(%int_32) [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.e0d: type = fn_type @Core.IntLiteral.as.ImplicitAs.impl.Convert, @Core.IntLiteral.as.ImplicitAs.impl(%int_32) [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.e0d = struct_value () [concrete] // CHECK:STDOUT: %ImplicitAs.facet: %ImplicitAs.type.e8c = facet_value Core.IntLiteral, (%ImplicitAs.impl_witness.6bc) [concrete] // CHECK:STDOUT: %ImplicitAs.WithSelf.Convert.type.b37: type = fn_type @ImplicitAs.WithSelf.Convert, @ImplicitAs.WithSelf(%i32, %ImplicitAs.facet) [concrete] // CHECK:STDOUT: %.545: type = fn_type_with_self_type %ImplicitAs.WithSelf.Convert.type.b37, %ImplicitAs.facet [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.bound: = bound_method %int_1.5b8, %Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5 [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn: = specific_function %Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5, @Core.IntLiteral.as.ImplicitAs.impl.Convert(%int_32) [concrete] // CHECK:STDOUT: %bound_method: = bound_method %int_1.5b8, %Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn [concrete] // CHECK:STDOUT: %int_1.5d2: %i32 = int_value 1 [concrete] // CHECK:STDOUT: %Derived: type = class_type @Derived [concrete] // CHECK:STDOUT: %Derived.elem: type = unbound_element_type %Derived, %Base [concrete] // CHECK:STDOUT: %struct_type.base.27a: type = struct_type {.base: %Base} [concrete] // CHECK:STDOUT: %complete_type.5a1: = complete_type_witness %struct_type.base.27a [concrete] // CHECK:STDOUT: %ptr.f74: type = ptr_type %Derived [concrete] // CHECK:STDOUT: %pattern_type.0dd: type = pattern_type %ptr.f74 [concrete] // CHECK:STDOUT: %Call.type: type = fn_type @Call [concrete] // CHECK:STDOUT: %Call: %Call.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: .Int = %Core.Int // CHECK:STDOUT: .ImplicitAs = %Core.ImplicitAs // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: %Core.Int: %Int.type = import_ref Core//prelude/parts/int, Int, loaded [concrete = constants.%Int.generic] // CHECK:STDOUT: %Core.ImplicitAs: %ImplicitAs.type.cc7 = import_ref Core//prelude/parts/as, ImplicitAs, loaded [concrete = constants.%ImplicitAs.generic] // CHECK:STDOUT: %Core.import_ref.42d: @Core.IntLiteral.as.ImplicitAs.impl.%Core.IntLiteral.as.ImplicitAs.impl.Convert.type (%Core.IntLiteral.as.ImplicitAs.impl.Convert.type.4e6) = import_ref Core//prelude/parts/int, loc{{\d+_\d+}}, loaded [symbolic = @Core.IntLiteral.as.ImplicitAs.impl.%Core.IntLiteral.as.ImplicitAs.impl.Convert (constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.3c2)] // CHECK:STDOUT: %ImplicitAs.impl_witness_table.74f = impl_witness_table (%Core.import_ref.42d), @Core.IntLiteral.as.ImplicitAs.impl [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .Base = %Base.decl // CHECK:STDOUT: .Derived = %Derived.decl // CHECK:STDOUT: .Call = %Call.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %Base.decl: type = class_decl @Base [concrete = constants.%Base] {} {} // CHECK:STDOUT: %Base.F.decl: %Base.F.type = fn_decl @Base.F [concrete = constants.%Base.F] { // CHECK:STDOUT: %self.patt: %pattern_type.101 = ref_binding_pattern self [concrete] // CHECK:STDOUT: %self.param_patt: %pattern_type.101 = ref_param_pattern %self.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %self.param.loc21: ref %Base = ref_param call_param0 // CHECK:STDOUT: %Self.ref.loc21: type = name_ref Self, constants.%Base [concrete = constants.%Base] // CHECK:STDOUT: %self.loc21: ref %Base = ref_binding self, %self.param.loc21 // CHECK:STDOUT: } // CHECK:STDOUT: %Derived.decl: type = class_decl @Derived [concrete = constants.%Derived] {} {} // CHECK:STDOUT: %Call.decl: %Call.type = fn_decl @Call [concrete = constants.%Call] { // CHECK:STDOUT: %p.patt: %pattern_type.0dd = value_binding_pattern p [concrete] // CHECK:STDOUT: %p.param_patt: %pattern_type.0dd = value_param_pattern %p.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %p.param: %ptr.f74 = value_param call_param0 // CHECK:STDOUT: %.loc29: type = splice_block %ptr [concrete = constants.%ptr.f74] { // CHECK:STDOUT: %Derived.ref: type = name_ref Derived, file.%Derived.decl [concrete = constants.%Derived] // CHECK:STDOUT: %ptr: type = ptr_type %Derived.ref [concrete = constants.%ptr.f74] // CHECK:STDOUT: } // CHECK:STDOUT: %p: %ptr.f74 = value_binding p, %p.param // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @Base { // CHECK:STDOUT: %i32: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %.loc16: %Base.elem = field_decl a, element0 [concrete] // CHECK:STDOUT: %Base.F.decl: %Base.F.type = fn_decl @Base.F [concrete = constants.%Base.F] { // CHECK:STDOUT: %self.patt: %pattern_type.101 = ref_binding_pattern self [concrete] // CHECK:STDOUT: %self.param_patt: %pattern_type.101 = ref_param_pattern %self.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %self.param.loc18: ref %Base = ref_param call_param0 // CHECK:STDOUT: %Self.ref.loc18: type = name_ref Self, constants.%Base [concrete = constants.%Base] // CHECK:STDOUT: %self.loc18: ref %Base = ref_binding self, %self.param.loc18 // CHECK:STDOUT: } // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%struct_type.a [concrete = constants.%complete_type.fd7] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%Base // CHECK:STDOUT: .a = %.loc16 // CHECK:STDOUT: .F = %Base.F.decl // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @Derived { // CHECK:STDOUT: %Base.ref: type = name_ref Base, file.%Base.decl [concrete = constants.%Base] // CHECK:STDOUT: %.loc26: %Derived.elem = base_decl %Base.ref, element0 [concrete] // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%struct_type.base.27a [concrete = constants.%complete_type.5a1] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%Derived // CHECK:STDOUT: .Base = // CHECK:STDOUT: .base = %.loc26 // CHECK:STDOUT: .F = // CHECK:STDOUT: extend %Base.ref // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @Base.F(%self.param.loc21: ref %Base) { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %self.ref: ref %Base = name_ref self, %self.loc21 // CHECK:STDOUT: %a.ref: %Base.elem = name_ref a, @Base.%.loc16 [concrete = @Base.%.loc16] // CHECK:STDOUT: %.loc22_7: ref %i32 = class_element_access %self.ref, element0 // CHECK:STDOUT: %int_1: Core.IntLiteral = int_value 1 [concrete = constants.%int_1.5b8] // CHECK:STDOUT: %impl.elem0: %.545 = impl_witness_access constants.%ImplicitAs.impl_witness.6bc, element0 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5] // CHECK:STDOUT: %bound_method.loc22_10.1: = bound_method %int_1, %impl.elem0 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.bound] // CHECK:STDOUT: %specific_fn: = specific_function %impl.elem0, @Core.IntLiteral.as.ImplicitAs.impl.Convert(constants.%int_32) [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn] // CHECK:STDOUT: %bound_method.loc22_10.2: = bound_method %int_1, %specific_fn [concrete = constants.%bound_method] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.call: init %i32 = call %bound_method.loc22_10.2(%int_1) [concrete = constants.%int_1.5d2] // CHECK:STDOUT: %.loc22_10: init %i32 = converted %int_1, %Core.IntLiteral.as.ImplicitAs.impl.Convert.call [concrete = constants.%int_1.5d2] // CHECK:STDOUT: assign %.loc22_7, %.loc22_10 // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @Call(%p.param: %ptr.f74) { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %p.ref: %ptr.f74 = name_ref p, %p // CHECK:STDOUT: %.loc30_4.1: ref %Derived = deref %p.ref // CHECK:STDOUT: %F.ref: %Base.F.type = name_ref F, @Base.%Base.F.decl [concrete = constants.%Base.F] // CHECK:STDOUT: %Base.F.bound: = bound_method %.loc30_4.1, %F.ref // CHECK:STDOUT: %.loc30_4.2: ref %Base = class_element_access %.loc30_4.1, element0 // CHECK:STDOUT: %.loc30_4.3: ref %Base = converted %.loc30_4.1, %.loc30_4.2 // CHECK:STDOUT: %Base.F.call: init %empty_tuple.type = call %Base.F.bound(%.loc30_4.3) // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/class/inheritance/base_method_qualified.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/int.carbon // TODO: Add ranges and switch to "--dump-sem-ir-ranges=only". // EXTRA-ARGS: --dump-sem-ir-ranges=if-present // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/class/inheritance/base_method_qualified.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/class/inheritance/base_method_qualified.carbon class Derived; base class Base { fn F[self: Self]() -> i32; fn G[self: Derived]() -> i32; } class Derived { extend base: Base; fn F[self: Self](); fn G[self: Self](); } fn Call(a: Derived) -> i32 { return a.(Base.F)(); } fn CallIndirect(p: Derived*) -> i32 { return p->(Base.F)(); } fn PassDerivedToBase(a: Derived) -> i32 { return a.(Base.G)(); } fn PassDerivedToBaseIndirect(p: Derived*) -> i32 { return p->(Base.G)(); } // CHECK:STDOUT: --- base_method_qualified.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %Derived: type = class_type @Derived [concrete] // CHECK:STDOUT: %Base: type = class_type @Base [concrete] // CHECK:STDOUT: %pattern_type.101: type = pattern_type %Base [concrete] // CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [concrete] // CHECK:STDOUT: %Int.type: type = generic_class_type @Int [concrete] // CHECK:STDOUT: %Int.generic: %Int.type = struct_value () [concrete] // CHECK:STDOUT: %i32: type = class_type @Int, @Int(%int_32) [concrete] // CHECK:STDOUT: %.ff5: Core.Form = init_form %i32 [concrete] // CHECK:STDOUT: %pattern_type.7ce: type = pattern_type %i32 [concrete] // CHECK:STDOUT: %Base.F.type: type = fn_type @Base.F [concrete] // CHECK:STDOUT: %Base.F: %Base.F.type = struct_value () [concrete] // CHECK:STDOUT: %pattern_type.9f6: type = pattern_type %Derived [concrete] // CHECK:STDOUT: %Base.G.type: type = fn_type @Base.G [concrete] // CHECK:STDOUT: %Base.G: %Base.G.type = struct_value () [concrete] // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %complete_type.357: = complete_type_witness %empty_struct_type [concrete] // CHECK:STDOUT: %Derived.elem: type = unbound_element_type %Derived, %Base [concrete] // CHECK:STDOUT: %Derived.F.type: type = fn_type @Derived.F [concrete] // CHECK:STDOUT: %Derived.F: %Derived.F.type = struct_value () [concrete] // CHECK:STDOUT: %Derived.G.type: type = fn_type @Derived.G [concrete] // CHECK:STDOUT: %Derived.G: %Derived.G.type = struct_value () [concrete] // CHECK:STDOUT: %struct_type.base.27a: type = struct_type {.base: %Base} [concrete] // CHECK:STDOUT: %complete_type.5a1: = complete_type_witness %struct_type.base.27a [concrete] // CHECK:STDOUT: %Call.type: type = fn_type @Call [concrete] // CHECK:STDOUT: %Call: %Call.type = struct_value () [concrete] // CHECK:STDOUT: %ptr.f74: type = ptr_type %Derived [concrete] // CHECK:STDOUT: %pattern_type.0dd: type = pattern_type %ptr.f74 [concrete] // CHECK:STDOUT: %CallIndirect.type: type = fn_type @CallIndirect [concrete] // CHECK:STDOUT: %CallIndirect: %CallIndirect.type = struct_value () [concrete] // CHECK:STDOUT: %PassDerivedToBase.type: type = fn_type @PassDerivedToBase [concrete] // CHECK:STDOUT: %PassDerivedToBase: %PassDerivedToBase.type = struct_value () [concrete] // CHECK:STDOUT: %PassDerivedToBaseIndirect.type: type = fn_type @PassDerivedToBaseIndirect [concrete] // CHECK:STDOUT: %PassDerivedToBaseIndirect: %PassDerivedToBaseIndirect.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: .Int = %Core.Int // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: %Core.Int: %Int.type = import_ref Core//prelude/parts/int, Int, loaded [concrete = constants.%Int.generic] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .Derived = %Derived.decl.loc15 // CHECK:STDOUT: .Base = %Base.decl // CHECK:STDOUT: .Call = %Call.decl // CHECK:STDOUT: .CallIndirect = %CallIndirect.decl // CHECK:STDOUT: .PassDerivedToBase = %PassDerivedToBase.decl // CHECK:STDOUT: .PassDerivedToBaseIndirect = %PassDerivedToBaseIndirect.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %Derived.decl.loc15: type = class_decl @Derived [concrete = constants.%Derived] {} {} // CHECK:STDOUT: %Base.decl: type = class_decl @Base [concrete = constants.%Base] {} {} // CHECK:STDOUT: %Derived.decl.loc22: type = class_decl @Derived [concrete = constants.%Derived] {} {} // CHECK:STDOUT: %Call.decl: %Call.type = fn_decl @Call [concrete = constants.%Call] { // CHECK:STDOUT: %a.patt: %pattern_type.9f6 = value_binding_pattern a [concrete] // CHECK:STDOUT: %a.param_patt: %pattern_type.9f6 = value_param_pattern %a.patt [concrete] // CHECK:STDOUT: %return.patt: %pattern_type.7ce = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: %pattern_type.7ce = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %i32: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %.loc29: Core.Form = init_form %i32 [concrete = constants.%.ff5] // CHECK:STDOUT: %a.param: %Derived = value_param call_param0 // CHECK:STDOUT: %Derived.ref: type = name_ref Derived, file.%Derived.decl.loc15 [concrete = constants.%Derived] // CHECK:STDOUT: %a: %Derived = value_binding a, %a.param // CHECK:STDOUT: %return.param: ref %i32 = out_param call_param1 // CHECK:STDOUT: %return: ref %i32 = return_slot %return.param // CHECK:STDOUT: } // CHECK:STDOUT: %CallIndirect.decl: %CallIndirect.type = fn_decl @CallIndirect [concrete = constants.%CallIndirect] { // CHECK:STDOUT: %p.patt: %pattern_type.0dd = value_binding_pattern p [concrete] // CHECK:STDOUT: %p.param_patt: %pattern_type.0dd = value_param_pattern %p.patt [concrete] // CHECK:STDOUT: %return.patt: %pattern_type.7ce = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: %pattern_type.7ce = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %i32: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %.loc33_33: Core.Form = init_form %i32 [concrete = constants.%.ff5] // CHECK:STDOUT: %p.param: %ptr.f74 = value_param call_param0 // CHECK:STDOUT: %.loc33_27: type = splice_block %ptr [concrete = constants.%ptr.f74] { // CHECK:STDOUT: %Derived.ref: type = name_ref Derived, file.%Derived.decl.loc15 [concrete = constants.%Derived] // CHECK:STDOUT: %ptr: type = ptr_type %Derived.ref [concrete = constants.%ptr.f74] // CHECK:STDOUT: } // CHECK:STDOUT: %p: %ptr.f74 = value_binding p, %p.param // CHECK:STDOUT: %return.param: ref %i32 = out_param call_param1 // CHECK:STDOUT: %return: ref %i32 = return_slot %return.param // CHECK:STDOUT: } // CHECK:STDOUT: %PassDerivedToBase.decl: %PassDerivedToBase.type = fn_decl @PassDerivedToBase [concrete = constants.%PassDerivedToBase] { // CHECK:STDOUT: %a.patt: %pattern_type.9f6 = value_binding_pattern a [concrete] // CHECK:STDOUT: %a.param_patt: %pattern_type.9f6 = value_param_pattern %a.patt [concrete] // CHECK:STDOUT: %return.patt: %pattern_type.7ce = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: %pattern_type.7ce = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %i32: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %.loc37: Core.Form = init_form %i32 [concrete = constants.%.ff5] // CHECK:STDOUT: %a.param: %Derived = value_param call_param0 // CHECK:STDOUT: %Derived.ref: type = name_ref Derived, file.%Derived.decl.loc15 [concrete = constants.%Derived] // CHECK:STDOUT: %a: %Derived = value_binding a, %a.param // CHECK:STDOUT: %return.param: ref %i32 = out_param call_param1 // CHECK:STDOUT: %return: ref %i32 = return_slot %return.param // CHECK:STDOUT: } // CHECK:STDOUT: %PassDerivedToBaseIndirect.decl: %PassDerivedToBaseIndirect.type = fn_decl @PassDerivedToBaseIndirect [concrete = constants.%PassDerivedToBaseIndirect] { // CHECK:STDOUT: %p.patt: %pattern_type.0dd = value_binding_pattern p [concrete] // CHECK:STDOUT: %p.param_patt: %pattern_type.0dd = value_param_pattern %p.patt [concrete] // CHECK:STDOUT: %return.patt: %pattern_type.7ce = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: %pattern_type.7ce = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %i32: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %.loc41_46: Core.Form = init_form %i32 [concrete = constants.%.ff5] // CHECK:STDOUT: %p.param: %ptr.f74 = value_param call_param0 // CHECK:STDOUT: %.loc41_40: type = splice_block %ptr [concrete = constants.%ptr.f74] { // CHECK:STDOUT: %Derived.ref: type = name_ref Derived, file.%Derived.decl.loc15 [concrete = constants.%Derived] // CHECK:STDOUT: %ptr: type = ptr_type %Derived.ref [concrete = constants.%ptr.f74] // CHECK:STDOUT: } // CHECK:STDOUT: %p: %ptr.f74 = value_binding p, %p.param // CHECK:STDOUT: %return.param: ref %i32 = out_param call_param1 // CHECK:STDOUT: %return: ref %i32 = return_slot %return.param // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @Derived { // CHECK:STDOUT: %Base.ref: type = name_ref Base, file.%Base.decl [concrete = constants.%Base] // CHECK:STDOUT: %.loc23: %Derived.elem = base_decl %Base.ref, element0 [concrete] // CHECK:STDOUT: %Derived.F.decl: %Derived.F.type = fn_decl @Derived.F [concrete = constants.%Derived.F] { // CHECK:STDOUT: %self.patt: %pattern_type.9f6 = value_binding_pattern self [concrete] // CHECK:STDOUT: %self.param_patt: %pattern_type.9f6 = value_param_pattern %self.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %self.param: %Derived = value_param call_param0 // CHECK:STDOUT: %Self.ref: type = name_ref Self, constants.%Derived [concrete = constants.%Derived] // CHECK:STDOUT: %self: %Derived = value_binding self, %self.param // CHECK:STDOUT: } // CHECK:STDOUT: %Derived.G.decl: %Derived.G.type = fn_decl @Derived.G [concrete = constants.%Derived.G] { // CHECK:STDOUT: %self.patt: %pattern_type.9f6 = value_binding_pattern self [concrete] // CHECK:STDOUT: %self.param_patt: %pattern_type.9f6 = value_param_pattern %self.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %self.param: %Derived = value_param call_param0 // CHECK:STDOUT: %Self.ref: type = name_ref Self, constants.%Derived [concrete = constants.%Derived] // CHECK:STDOUT: %self: %Derived = value_binding self, %self.param // CHECK:STDOUT: } // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%struct_type.base.27a [concrete = constants.%complete_type.5a1] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%Derived // CHECK:STDOUT: .Base = // CHECK:STDOUT: .base = %.loc23 // CHECK:STDOUT: .F = %Derived.F.decl // CHECK:STDOUT: .G = %Derived.G.decl // CHECK:STDOUT: extend %Base.ref // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @Base { // CHECK:STDOUT: %Base.F.decl: %Base.F.type = fn_decl @Base.F [concrete = constants.%Base.F] { // CHECK:STDOUT: %self.patt: %pattern_type.101 = value_binding_pattern self [concrete] // CHECK:STDOUT: %self.param_patt: %pattern_type.101 = value_param_pattern %self.patt [concrete] // CHECK:STDOUT: %return.patt: %pattern_type.7ce = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: %pattern_type.7ce = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %i32: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %.loc18: Core.Form = init_form %i32 [concrete = constants.%.ff5] // CHECK:STDOUT: %self.param: %Base = value_param call_param0 // CHECK:STDOUT: %Self.ref: type = name_ref Self, constants.%Base [concrete = constants.%Base] // CHECK:STDOUT: %self: %Base = value_binding self, %self.param // CHECK:STDOUT: %return.param: ref %i32 = out_param call_param1 // CHECK:STDOUT: %return: ref %i32 = return_slot %return.param // CHECK:STDOUT: } // CHECK:STDOUT: %Base.G.decl: %Base.G.type = fn_decl @Base.G [concrete = constants.%Base.G] { // CHECK:STDOUT: %self.patt: %pattern_type.9f6 = value_binding_pattern self [concrete] // CHECK:STDOUT: %self.param_patt: %pattern_type.9f6 = value_param_pattern %self.patt [concrete] // CHECK:STDOUT: %return.patt: %pattern_type.7ce = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: %pattern_type.7ce = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %i32: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %.loc19: Core.Form = init_form %i32 [concrete = constants.%.ff5] // CHECK:STDOUT: %self.param: %Derived = value_param call_param0 // CHECK:STDOUT: %Derived.ref: type = name_ref Derived, file.%Derived.decl.loc15 [concrete = constants.%Derived] // CHECK:STDOUT: %self: %Derived = value_binding self, %self.param // CHECK:STDOUT: %return.param: ref %i32 = out_param call_param1 // CHECK:STDOUT: %return: ref %i32 = return_slot %return.param // CHECK:STDOUT: } // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type.357] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%Base // CHECK:STDOUT: .F = %Base.F.decl // CHECK:STDOUT: .Derived = // CHECK:STDOUT: .G = %Base.G.decl // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @Base.F(%self.param: %Base) -> out %return.param: %i32; // CHECK:STDOUT: // CHECK:STDOUT: fn @Base.G(%self.param: %Derived) -> out %return.param: %i32; // CHECK:STDOUT: // CHECK:STDOUT: fn @Derived.F(%self.param: %Derived); // CHECK:STDOUT: // CHECK:STDOUT: fn @Derived.G(%self.param: %Derived); // CHECK:STDOUT: // CHECK:STDOUT: fn @Call(%a.param: %Derived) -> out %return.param: %i32 { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %a.ref: %Derived = name_ref a, %a // CHECK:STDOUT: %Base.ref: type = name_ref Base, file.%Base.decl [concrete = constants.%Base] // CHECK:STDOUT: %F.ref: %Base.F.type = name_ref F, @Base.%Base.F.decl [concrete = constants.%Base.F] // CHECK:STDOUT: %Base.F.bound: = bound_method %a.ref, %F.ref // CHECK:STDOUT: %.loc30_10.1: ref %Base = class_element_access %a.ref, element0 // CHECK:STDOUT: %.loc30_10.2: ref %Base = converted %a.ref, %.loc30_10.1 // CHECK:STDOUT: %.loc30_10.3: %Base = acquire_value %.loc30_10.2 // CHECK:STDOUT: %Base.F.call: init %i32 = call %Base.F.bound(%.loc30_10.3) // CHECK:STDOUT: return %Base.F.call // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @CallIndirect(%p.param: %ptr.f74) -> out %return.param: %i32 { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %p.ref: %ptr.f74 = name_ref p, %p // CHECK:STDOUT: %Base.ref: type = name_ref Base, file.%Base.decl [concrete = constants.%Base] // CHECK:STDOUT: %F.ref: %Base.F.type = name_ref F, @Base.%Base.F.decl [concrete = constants.%Base.F] // CHECK:STDOUT: %.loc34_11.1: ref %Derived = deref %p.ref // CHECK:STDOUT: %Base.F.bound: = bound_method %.loc34_11.1, %F.ref // CHECK:STDOUT: %.loc34_11.2: ref %Base = class_element_access %.loc34_11.1, element0 // CHECK:STDOUT: %.loc34_11.3: ref %Base = converted %.loc34_11.1, %.loc34_11.2 // CHECK:STDOUT: %.loc34_11.4: %Base = acquire_value %.loc34_11.3 // CHECK:STDOUT: %Base.F.call: init %i32 = call %Base.F.bound(%.loc34_11.4) // CHECK:STDOUT: return %Base.F.call // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @PassDerivedToBase(%a.param: %Derived) -> out %return.param: %i32 { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %a.ref: %Derived = name_ref a, %a // CHECK:STDOUT: %Base.ref: type = name_ref Base, file.%Base.decl [concrete = constants.%Base] // CHECK:STDOUT: %G.ref: %Base.G.type = name_ref G, @Base.%Base.G.decl [concrete = constants.%Base.G] // CHECK:STDOUT: %Base.G.bound: = bound_method %a.ref, %G.ref // CHECK:STDOUT: %Base.G.call: init %i32 = call %Base.G.bound(%a.ref) // CHECK:STDOUT: return %Base.G.call // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @PassDerivedToBaseIndirect(%p.param: %ptr.f74) -> out %return.param: %i32 { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %p.ref: %ptr.f74 = name_ref p, %p // CHECK:STDOUT: %Base.ref: type = name_ref Base, file.%Base.decl [concrete = constants.%Base] // CHECK:STDOUT: %G.ref: %Base.G.type = name_ref G, @Base.%Base.G.decl [concrete = constants.%Base.G] // CHECK:STDOUT: %.loc42_11.1: ref %Derived = deref %p.ref // CHECK:STDOUT: %Base.G.bound: = bound_method %.loc42_11.1, %G.ref // CHECK:STDOUT: %.loc42_11.2: %Derived = acquire_value %.loc42_11.1 // CHECK:STDOUT: %Base.G.call: init %i32 = call %Base.G.bound(%.loc42_11.2) // CHECK:STDOUT: return %Base.G.call // CHECK:STDOUT: } // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/class/inheritance/base_method_shadow.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/convert.carbon // TODO: Add ranges and switch to "--dump-sem-ir-ranges=only". // EXTRA-ARGS: --dump-sem-ir-ranges=if-present // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/class/inheritance/base_method_shadow.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/class/inheritance/base_method_shadow.carbon base class A { fn F[ref self: Self](); } base class B { extend base: A; fn F[ref self: Self](); } class C { extend base: B; fn F[ref self: Self](); } class D { extend base: B; } fn Call(a: A*, b: B*, c: C*, d: D*) { (*a).F(); (*b).F(); (*c).F(); (*d).F(); } // CHECK:STDOUT: --- base_method_shadow.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %A: type = class_type @A [concrete] // CHECK:STDOUT: %pattern_type.1ab: type = pattern_type %A [concrete] // CHECK:STDOUT: %A.F.type: type = fn_type @A.F [concrete] // CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] // CHECK:STDOUT: %A.F: %A.F.type = struct_value () [concrete] // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %complete_type.357: = complete_type_witness %empty_struct_type [concrete] // CHECK:STDOUT: %B: type = class_type @B [concrete] // CHECK:STDOUT: %B.elem: type = unbound_element_type %B, %A [concrete] // CHECK:STDOUT: %pattern_type.1f4: type = pattern_type %B [concrete] // CHECK:STDOUT: %B.F.type: type = fn_type @B.F [concrete] // CHECK:STDOUT: %B.F: %B.F.type = struct_value () [concrete] // CHECK:STDOUT: %struct_type.base.5af: type = struct_type {.base: %A} [concrete] // CHECK:STDOUT: %complete_type.0d1: = complete_type_witness %struct_type.base.5af [concrete] // CHECK:STDOUT: %C: type = class_type @C [concrete] // CHECK:STDOUT: %C.elem: type = unbound_element_type %C, %B [concrete] // CHECK:STDOUT: %pattern_type.7c7: type = pattern_type %C [concrete] // CHECK:STDOUT: %C.F.type: type = fn_type @C.F [concrete] // CHECK:STDOUT: %C.F: %C.F.type = struct_value () [concrete] // CHECK:STDOUT: %struct_type.base.64a: type = struct_type {.base: %B} [concrete] // CHECK:STDOUT: %complete_type.021: = complete_type_witness %struct_type.base.64a [concrete] // CHECK:STDOUT: %D: type = class_type @D [concrete] // CHECK:STDOUT: %D.elem: type = unbound_element_type %D, %B [concrete] // CHECK:STDOUT: %ptr.643: type = ptr_type %A [concrete] // CHECK:STDOUT: %pattern_type.f29: type = pattern_type %ptr.643 [concrete] // CHECK:STDOUT: %ptr.27c: type = ptr_type %B [concrete] // CHECK:STDOUT: %pattern_type.191: type = pattern_type %ptr.27c [concrete] // CHECK:STDOUT: %ptr.31e: type = ptr_type %C [concrete] // CHECK:STDOUT: %pattern_type.506: type = pattern_type %ptr.31e [concrete] // CHECK:STDOUT: %ptr.805: type = ptr_type %D [concrete] // CHECK:STDOUT: %pattern_type.415: type = pattern_type %ptr.805 [concrete] // CHECK:STDOUT: %Call.type: type = fn_type @Call [concrete] // CHECK:STDOUT: %Call: %Call.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .A = %A.decl // CHECK:STDOUT: .B = %B.decl // CHECK:STDOUT: .C = %C.decl // CHECK:STDOUT: .D = %D.decl // CHECK:STDOUT: .Call = %Call.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %A.decl: type = class_decl @A [concrete = constants.%A] {} {} // CHECK:STDOUT: %B.decl: type = class_decl @B [concrete = constants.%B] {} {} // CHECK:STDOUT: %C.decl: type = class_decl @C [concrete = constants.%C] {} {} // CHECK:STDOUT: %D.decl: type = class_decl @D [concrete = constants.%D] {} {} // CHECK:STDOUT: %Call.decl: %Call.type = fn_decl @Call [concrete = constants.%Call] { // CHECK:STDOUT: %a.patt: %pattern_type.f29 = value_binding_pattern a [concrete] // CHECK:STDOUT: %a.param_patt: %pattern_type.f29 = value_param_pattern %a.patt [concrete] // CHECK:STDOUT: %b.patt: %pattern_type.191 = value_binding_pattern b [concrete] // CHECK:STDOUT: %b.param_patt: %pattern_type.191 = value_param_pattern %b.patt [concrete] // CHECK:STDOUT: %c.patt: %pattern_type.506 = value_binding_pattern c [concrete] // CHECK:STDOUT: %c.param_patt: %pattern_type.506 = value_param_pattern %c.patt [concrete] // CHECK:STDOUT: %d.patt: %pattern_type.415 = value_binding_pattern d [concrete] // CHECK:STDOUT: %d.param_patt: %pattern_type.415 = value_param_pattern %d.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %a.param: %ptr.643 = value_param call_param0 // CHECK:STDOUT: %.loc33_13: type = splice_block %ptr.loc33_13 [concrete = constants.%ptr.643] { // CHECK:STDOUT: %A.ref: type = name_ref A, file.%A.decl [concrete = constants.%A] // CHECK:STDOUT: %ptr.loc33_13: type = ptr_type %A.ref [concrete = constants.%ptr.643] // CHECK:STDOUT: } // CHECK:STDOUT: %a: %ptr.643 = value_binding a, %a.param // CHECK:STDOUT: %b.param: %ptr.27c = value_param call_param1 // CHECK:STDOUT: %.loc33_20: type = splice_block %ptr.loc33_20 [concrete = constants.%ptr.27c] { // CHECK:STDOUT: %B.ref: type = name_ref B, file.%B.decl [concrete = constants.%B] // CHECK:STDOUT: %ptr.loc33_20: type = ptr_type %B.ref [concrete = constants.%ptr.27c] // CHECK:STDOUT: } // CHECK:STDOUT: %b: %ptr.27c = value_binding b, %b.param // CHECK:STDOUT: %c.param: %ptr.31e = value_param call_param2 // CHECK:STDOUT: %.loc33_27: type = splice_block %ptr.loc33_27 [concrete = constants.%ptr.31e] { // CHECK:STDOUT: %C.ref: type = name_ref C, file.%C.decl [concrete = constants.%C] // CHECK:STDOUT: %ptr.loc33_27: type = ptr_type %C.ref [concrete = constants.%ptr.31e] // CHECK:STDOUT: } // CHECK:STDOUT: %c: %ptr.31e = value_binding c, %c.param // CHECK:STDOUT: %d.param: %ptr.805 = value_param call_param3 // CHECK:STDOUT: %.loc33_34: type = splice_block %ptr.loc33_34 [concrete = constants.%ptr.805] { // CHECK:STDOUT: %D.ref: type = name_ref D, file.%D.decl [concrete = constants.%D] // CHECK:STDOUT: %ptr.loc33_34: type = ptr_type %D.ref [concrete = constants.%ptr.805] // CHECK:STDOUT: } // CHECK:STDOUT: %d: %ptr.805 = value_binding d, %d.param // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @A { // CHECK:STDOUT: %A.F.decl: %A.F.type = fn_decl @A.F [concrete = constants.%A.F] { // CHECK:STDOUT: %self.patt: %pattern_type.1ab = ref_binding_pattern self [concrete] // CHECK:STDOUT: %self.param_patt: %pattern_type.1ab = ref_param_pattern %self.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %self.param: ref %A = ref_param call_param0 // CHECK:STDOUT: %Self.ref: type = name_ref Self, constants.%A [concrete = constants.%A] // CHECK:STDOUT: %self: ref %A = ref_binding self, %self.param // CHECK:STDOUT: } // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type.357] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%A // CHECK:STDOUT: .F = %A.F.decl // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @B { // CHECK:STDOUT: %A.ref: type = name_ref A, file.%A.decl [concrete = constants.%A] // CHECK:STDOUT: %.loc20: %B.elem = base_decl %A.ref, element0 [concrete] // CHECK:STDOUT: %B.F.decl: %B.F.type = fn_decl @B.F [concrete = constants.%B.F] { // CHECK:STDOUT: %self.patt: %pattern_type.1f4 = ref_binding_pattern self [concrete] // CHECK:STDOUT: %self.param_patt: %pattern_type.1f4 = ref_param_pattern %self.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %self.param: ref %B = ref_param call_param0 // CHECK:STDOUT: %Self.ref: type = name_ref Self, constants.%B [concrete = constants.%B] // CHECK:STDOUT: %self: ref %B = ref_binding self, %self.param // CHECK:STDOUT: } // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%struct_type.base.5af [concrete = constants.%complete_type.0d1] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%B // CHECK:STDOUT: .A = // CHECK:STDOUT: .base = %.loc20 // CHECK:STDOUT: .F = %B.F.decl // CHECK:STDOUT: extend %A.ref // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @C { // CHECK:STDOUT: %B.ref: type = name_ref B, file.%B.decl [concrete = constants.%B] // CHECK:STDOUT: %.loc25: %C.elem = base_decl %B.ref, element0 [concrete] // CHECK:STDOUT: %C.F.decl: %C.F.type = fn_decl @C.F [concrete = constants.%C.F] { // CHECK:STDOUT: %self.patt: %pattern_type.7c7 = ref_binding_pattern self [concrete] // CHECK:STDOUT: %self.param_patt: %pattern_type.7c7 = ref_param_pattern %self.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %self.param: ref %C = ref_param call_param0 // CHECK:STDOUT: %Self.ref: type = name_ref Self, constants.%C [concrete = constants.%C] // CHECK:STDOUT: %self: ref %C = ref_binding self, %self.param // CHECK:STDOUT: } // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%struct_type.base.64a [concrete = constants.%complete_type.021] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%C // CHECK:STDOUT: .B = // CHECK:STDOUT: .base = %.loc25 // CHECK:STDOUT: .F = %C.F.decl // CHECK:STDOUT: extend %B.ref // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @D { // CHECK:STDOUT: %B.ref: type = name_ref B, file.%B.decl [concrete = constants.%B] // CHECK:STDOUT: %.loc30: %D.elem = base_decl %B.ref, element0 [concrete] // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%struct_type.base.64a [concrete = constants.%complete_type.021] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%D // CHECK:STDOUT: .B = // CHECK:STDOUT: .base = %.loc30 // CHECK:STDOUT: .F = // CHECK:STDOUT: extend %B.ref // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @A.F(%self.param: ref %A); // CHECK:STDOUT: // CHECK:STDOUT: fn @B.F(%self.param: ref %B); // CHECK:STDOUT: // CHECK:STDOUT: fn @C.F(%self.param: ref %C); // CHECK:STDOUT: // CHECK:STDOUT: fn @Call(%a.param: %ptr.643, %b.param: %ptr.27c, %c.param: %ptr.31e, %d.param: %ptr.805) { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %a.ref: %ptr.643 = name_ref a, %a // CHECK:STDOUT: %.loc34: ref %A = deref %a.ref // CHECK:STDOUT: %F.ref.loc34: %A.F.type = name_ref F, @A.%A.F.decl [concrete = constants.%A.F] // CHECK:STDOUT: %A.F.bound: = bound_method %.loc34, %F.ref.loc34 // CHECK:STDOUT: %A.F.call: init %empty_tuple.type = call %A.F.bound(%.loc34) // CHECK:STDOUT: %b.ref: %ptr.27c = name_ref b, %b // CHECK:STDOUT: %.loc35: ref %B = deref %b.ref // CHECK:STDOUT: %F.ref.loc35: %B.F.type = name_ref F, @B.%B.F.decl [concrete = constants.%B.F] // CHECK:STDOUT: %B.F.bound.loc35: = bound_method %.loc35, %F.ref.loc35 // CHECK:STDOUT: %B.F.call.loc35: init %empty_tuple.type = call %B.F.bound.loc35(%.loc35) // CHECK:STDOUT: %c.ref: %ptr.31e = name_ref c, %c // CHECK:STDOUT: %.loc36: ref %C = deref %c.ref // CHECK:STDOUT: %F.ref.loc36: %C.F.type = name_ref F, @C.%C.F.decl [concrete = constants.%C.F] // CHECK:STDOUT: %C.F.bound: = bound_method %.loc36, %F.ref.loc36 // CHECK:STDOUT: %C.F.call: init %empty_tuple.type = call %C.F.bound(%.loc36) // CHECK:STDOUT: %d.ref: %ptr.805 = name_ref d, %d // CHECK:STDOUT: %.loc37_4.1: ref %D = deref %d.ref // CHECK:STDOUT: %F.ref.loc37: %B.F.type = name_ref F, @B.%B.F.decl [concrete = constants.%B.F] // CHECK:STDOUT: %B.F.bound.loc37: = bound_method %.loc37_4.1, %F.ref.loc37 // CHECK:STDOUT: %.loc37_4.2: ref %B = class_element_access %.loc37_4.1, element0 // CHECK:STDOUT: %.loc37_4.3: ref %B = converted %.loc37_4.1, %.loc37_4.2 // CHECK:STDOUT: %B.F.call.loc37: init %empty_tuple.type = call %B.F.bound.loc37(%.loc37_4.3) // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/class/inheritance/derived_to_base.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/int.carbon // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/class/inheritance/derived_to_base.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/class/inheritance/derived_to_base.carbon // --- basic.carbon library "[[@TEST_NAME]]"; base class A { var a: i32; } base class B { extend base: A; var b: i32; } class C { extend base: B; var c: i32; } //@dump-sem-ir-begin fn ConvertCToB(p: C*) -> B* { return p; } fn ConvertBToA(p: B*) -> A* { return p; } fn ConvertCToA(p: C*) -> A* { return p; } fn ConvertValue(c: C) { let unused a: A = c; } fn ConvertRef(c: C*) -> A* { return &(*c as A); } fn ConvertInit() { let unused a: A = {.base = {.base = {.a = 1}, .b = 2}, .c = 3} as C; } //@dump-sem-ir-end // --- qualified.carbon library "[[@TEST_NAME]]"; base class A { } class B { extend base: A; } fn TakeConstAPtr(p: const A*); fn PassNonConstBPtr(p: B*) { //@dump-sem-ir-begin TakeConstAPtr(p); //@dump-sem-ir-end } fn PassConstBPtr(p: const B*) { //@dump-sem-ir-begin TakeConstAPtr(p); //@dump-sem-ir-end } // --- fail_todo_qualified_non_ptr.carbon library "[[@TEST_NAME]]"; base class A { } class B { extend base: A; } fn TakeConstA(p: const A); fn PassNonConstB(p: B) { //@dump-sem-ir-begin // CHECK:STDERR: fail_todo_qualified_non_ptr.carbon:[[@LINE+10]]:14: error: cannot implicitly convert expression of type `B` to `const A` [ConversionFailure] // CHECK:STDERR: TakeConstA(p); // CHECK:STDERR: ^ // CHECK:STDERR: fail_todo_qualified_non_ptr.carbon:[[@LINE+7]]:14: note: type `B` does not implement interface `Core.ImplicitAs(const A)` [MissingImplInMemberAccessInContext] // CHECK:STDERR: TakeConstA(p); // CHECK:STDERR: ^ // CHECK:STDERR: fail_todo_qualified_non_ptr.carbon:[[@LINE-10]]:15: note: initializing function parameter [InCallToFunctionParam] // CHECK:STDERR: fn TakeConstA(p: const A); // CHECK:STDERR: ^~~~~~~~~~ // CHECK:STDERR: TakeConstA(p); //@dump-sem-ir-end } fn PassConstB(p: const B) { //@dump-sem-ir-begin // CHECK:STDERR: fail_todo_qualified_non_ptr.carbon:[[@LINE+10]]:14: error: cannot implicitly convert expression of type `const B` to `const A` [ConversionFailure] // CHECK:STDERR: TakeConstA(p); // CHECK:STDERR: ^ // CHECK:STDERR: fail_todo_qualified_non_ptr.carbon:[[@LINE+7]]:14: note: type `const B` does not implement interface `Core.ImplicitAs(const A)` [MissingImplInMemberAccessInContext] // CHECK:STDERR: TakeConstA(p); // CHECK:STDERR: ^ // CHECK:STDERR: fail_todo_qualified_non_ptr.carbon:[[@LINE-26]]:15: note: initializing function parameter [InCallToFunctionParam] // CHECK:STDERR: fn TakeConstA(p: const A); // CHECK:STDERR: ^~~~~~~~~~ // CHECK:STDERR: TakeConstA(p); //@dump-sem-ir-end } // CHECK:STDOUT: --- basic.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %A: type = class_type @A [concrete] // CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [concrete] // CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] // CHECK:STDOUT: %i32: type = class_type @Int, @Int(%int_32) [concrete] // CHECK:STDOUT: %B: type = class_type @B [concrete] // CHECK:STDOUT: %C: type = class_type @C [concrete] // CHECK:STDOUT: %ptr.31e: type = ptr_type %C [concrete] // CHECK:STDOUT: %pattern_type.506: type = pattern_type %ptr.31e [concrete] // CHECK:STDOUT: %ptr.27c: type = ptr_type %B [concrete] // CHECK:STDOUT: %.d19: Core.Form = init_form %ptr.27c [concrete] // CHECK:STDOUT: %pattern_type.191: type = pattern_type %ptr.27c [concrete] // CHECK:STDOUT: %ConvertCToB.type: type = fn_type @ConvertCToB [concrete] // CHECK:STDOUT: %ConvertCToB: %ConvertCToB.type = struct_value () [concrete] // CHECK:STDOUT: %Copy.type: type = facet_type <@Copy> [concrete] // CHECK:STDOUT: %T.67d: type = symbolic_binding T, 0 [symbolic] // CHECK:STDOUT: %ptr.as.Copy.impl.Op.type.2d4: type = fn_type @ptr.as.Copy.impl.Op, @ptr.as.Copy.impl(%T.67d) [symbolic] // CHECK:STDOUT: %ptr.as.Copy.impl.Op.74e: %ptr.as.Copy.impl.Op.type.2d4 = struct_value () [symbolic] // CHECK:STDOUT: %Copy.impl_witness.672: = impl_witness imports.%Copy.impl_witness_table.c3a, @ptr.as.Copy.impl(%B) [concrete] // CHECK:STDOUT: %ptr.as.Copy.impl.Op.type.0fc: type = fn_type @ptr.as.Copy.impl.Op, @ptr.as.Copy.impl(%B) [concrete] // CHECK:STDOUT: %ptr.as.Copy.impl.Op.20b: %ptr.as.Copy.impl.Op.type.0fc = struct_value () [concrete] // CHECK:STDOUT: %Copy.facet.069: %Copy.type = facet_value %ptr.27c, (%Copy.impl_witness.672) [concrete] // CHECK:STDOUT: %Copy.WithSelf.Op.type.d34: type = fn_type @Copy.WithSelf.Op, @Copy.WithSelf(%Copy.facet.069) [concrete] // CHECK:STDOUT: %.cc8: type = fn_type_with_self_type %Copy.WithSelf.Op.type.d34, %Copy.facet.069 [concrete] // CHECK:STDOUT: %ptr.as.Copy.impl.Op.specific_fn.695: = specific_function %ptr.as.Copy.impl.Op.20b, @ptr.as.Copy.impl.Op(%B) [concrete] // CHECK:STDOUT: %ptr.643: type = ptr_type %A [concrete] // CHECK:STDOUT: %.ebf: Core.Form = init_form %ptr.643 [concrete] // CHECK:STDOUT: %pattern_type.f29: type = pattern_type %ptr.643 [concrete] // CHECK:STDOUT: %ConvertBToA.type: type = fn_type @ConvertBToA [concrete] // CHECK:STDOUT: %ConvertBToA: %ConvertBToA.type = struct_value () [concrete] // CHECK:STDOUT: %Copy.impl_witness.551: = impl_witness imports.%Copy.impl_witness_table.c3a, @ptr.as.Copy.impl(%A) [concrete] // CHECK:STDOUT: %ptr.as.Copy.impl.Op.type.772: type = fn_type @ptr.as.Copy.impl.Op, @ptr.as.Copy.impl(%A) [concrete] // CHECK:STDOUT: %ptr.as.Copy.impl.Op.510: %ptr.as.Copy.impl.Op.type.772 = struct_value () [concrete] // CHECK:STDOUT: %Copy.facet.f6a: %Copy.type = facet_value %ptr.643, (%Copy.impl_witness.551) [concrete] // CHECK:STDOUT: %Copy.WithSelf.Op.type.f6f: type = fn_type @Copy.WithSelf.Op, @Copy.WithSelf(%Copy.facet.f6a) [concrete] // CHECK:STDOUT: %.2f8: type = fn_type_with_self_type %Copy.WithSelf.Op.type.f6f, %Copy.facet.f6a [concrete] // CHECK:STDOUT: %ptr.as.Copy.impl.Op.specific_fn.05b: = specific_function %ptr.as.Copy.impl.Op.510, @ptr.as.Copy.impl.Op(%A) [concrete] // CHECK:STDOUT: %ConvertCToA.type: type = fn_type @ConvertCToA [concrete] // CHECK:STDOUT: %ConvertCToA: %ConvertCToA.type = struct_value () [concrete] // CHECK:STDOUT: %pattern_type.7c7: type = pattern_type %C [concrete] // CHECK:STDOUT: %ConvertValue.type: type = fn_type @ConvertValue [concrete] // CHECK:STDOUT: %ConvertValue: %ConvertValue.type = struct_value () [concrete] // CHECK:STDOUT: %pattern_type.1ab: type = pattern_type %A [concrete] // CHECK:STDOUT: %ConvertRef.type: type = fn_type @ConvertRef [concrete] // CHECK:STDOUT: %ConvertRef: %ConvertRef.type = struct_value () [concrete] // CHECK:STDOUT: %ConvertInit.type: type = fn_type @ConvertInit [concrete] // CHECK:STDOUT: %ConvertInit: %ConvertInit.type = struct_value () [concrete] // CHECK:STDOUT: %int_1.5b8: Core.IntLiteral = int_value 1 [concrete] // CHECK:STDOUT: %struct_type.a.a6c: type = struct_type {.a: Core.IntLiteral} [concrete] // CHECK:STDOUT: %struct.48c: %struct_type.a.a6c = struct_value (%int_1.5b8) [concrete] // CHECK:STDOUT: %int_2.ecc: Core.IntLiteral = int_value 2 [concrete] // CHECK:STDOUT: %struct_type.base.b.bf0: type = struct_type {.base: %struct_type.a.a6c, .b: Core.IntLiteral} [concrete] // CHECK:STDOUT: %struct.ff9: %struct_type.base.b.bf0 = struct_value (%struct.48c, %int_2.ecc) [concrete] // CHECK:STDOUT: %int_3.1ba: Core.IntLiteral = int_value 3 [concrete] // CHECK:STDOUT: %struct_type.base.c.136: type = struct_type {.base: %struct_type.base.b.bf0, .c: Core.IntLiteral} [concrete] // CHECK:STDOUT: %struct.2aa: %struct_type.base.c.136 = struct_value (%struct.ff9, %int_3.1ba) [concrete] // CHECK:STDOUT: %.eaa: type = partial_type %B [concrete] // CHECK:STDOUT: %.157: type = partial_type %A [concrete] // CHECK:STDOUT: %ImplicitAs.type.e8c: type = facet_type <@ImplicitAs, @ImplicitAs(%i32)> [concrete] // CHECK:STDOUT: %To: Core.IntLiteral = symbolic_binding To, 0 [symbolic] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.4e6: type = fn_type @Core.IntLiteral.as.ImplicitAs.impl.Convert, @Core.IntLiteral.as.ImplicitAs.impl(%To) [symbolic] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.3c2: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.4e6 = struct_value () [symbolic] // CHECK:STDOUT: %ImplicitAs.impl_witness.6bc: = impl_witness imports.%ImplicitAs.impl_witness_table.74f, @Core.IntLiteral.as.ImplicitAs.impl(%int_32) [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.e0d: type = fn_type @Core.IntLiteral.as.ImplicitAs.impl.Convert, @Core.IntLiteral.as.ImplicitAs.impl(%int_32) [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.e0d = struct_value () [concrete] // CHECK:STDOUT: %ImplicitAs.facet: %ImplicitAs.type.e8c = facet_value Core.IntLiteral, (%ImplicitAs.impl_witness.6bc) [concrete] // CHECK:STDOUT: %ImplicitAs.WithSelf.Convert.type.b37: type = fn_type @ImplicitAs.WithSelf.Convert, @ImplicitAs.WithSelf(%i32, %ImplicitAs.facet) [concrete] // CHECK:STDOUT: %.545: type = fn_type_with_self_type %ImplicitAs.WithSelf.Convert.type.b37, %ImplicitAs.facet [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.bound.215: = bound_method %int_1.5b8, %Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5 [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn: = specific_function %Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5, @Core.IntLiteral.as.ImplicitAs.impl.Convert(%int_32) [concrete] // CHECK:STDOUT: %bound_method.38b: = bound_method %int_1.5b8, %Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn [concrete] // CHECK:STDOUT: %int_1.5d2: %i32 = int_value 1 [concrete] // CHECK:STDOUT: %struct.e01: %.157 = struct_value (%int_1.5d2) [concrete] // CHECK:STDOUT: %A.val: %A = struct_value (%int_1.5d2) [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.bound.4e5: = bound_method %int_2.ecc, %Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5 [concrete] // CHECK:STDOUT: %bound_method.646: = bound_method %int_2.ecc, %Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn [concrete] // CHECK:STDOUT: %int_2.ef8: %i32 = int_value 2 [concrete] // CHECK:STDOUT: %struct.bc2: %.eaa = struct_value (%A.val, %int_2.ef8) [concrete] // CHECK:STDOUT: %B.val: %B = struct_value (%A.val, %int_2.ef8) [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.bound.061: = bound_method %int_3.1ba, %Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5 [concrete] // CHECK:STDOUT: %bound_method.fa7: = bound_method %int_3.1ba, %Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn [concrete] // CHECK:STDOUT: %int_3.822: %i32 = int_value 3 [concrete] // CHECK:STDOUT: %C.val: %C = struct_value (%B.val, %int_3.822) [concrete] // CHECK:STDOUT: %Destroy.Op.type: type = fn_type @Destroy.Op [concrete] // CHECK:STDOUT: %Destroy.Op: %Destroy.Op.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core.import_ref.203: @ptr.as.Copy.impl.%ptr.as.Copy.impl.Op.type (%ptr.as.Copy.impl.Op.type.2d4) = import_ref Core//prelude/parts/copy, loc{{\d+_\d+}}, loaded [symbolic = @ptr.as.Copy.impl.%ptr.as.Copy.impl.Op (constants.%ptr.as.Copy.impl.Op.74e)] // CHECK:STDOUT: %Copy.impl_witness_table.c3a = impl_witness_table (%Core.import_ref.203), @ptr.as.Copy.impl [concrete] // CHECK:STDOUT: %Core.import_ref.42d: @Core.IntLiteral.as.ImplicitAs.impl.%Core.IntLiteral.as.ImplicitAs.impl.Convert.type (%Core.IntLiteral.as.ImplicitAs.impl.Convert.type.4e6) = import_ref Core//prelude/parts/int, loc{{\d+_\d+}}, loaded [symbolic = @Core.IntLiteral.as.ImplicitAs.impl.%Core.IntLiteral.as.ImplicitAs.impl.Convert (constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.3c2)] // CHECK:STDOUT: %ImplicitAs.impl_witness_table.74f = impl_witness_table (%Core.import_ref.42d), @Core.IntLiteral.as.ImplicitAs.impl [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: %ConvertCToB.decl: %ConvertCToB.type = fn_decl @ConvertCToB [concrete = constants.%ConvertCToB] { // CHECK:STDOUT: %p.patt: %pattern_type.506 = value_binding_pattern p [concrete] // CHECK:STDOUT: %p.param_patt: %pattern_type.506 = value_param_pattern %p.patt [concrete] // CHECK:STDOUT: %return.patt: %pattern_type.191 = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: %pattern_type.191 = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %B.ref: type = name_ref B, file.%B.decl [concrete = constants.%B] // CHECK:STDOUT: %ptr.loc19_27: type = ptr_type %B.ref [concrete = constants.%ptr.27c] // CHECK:STDOUT: %.loc19_27: Core.Form = init_form %ptr.loc19_27 [concrete = constants.%.d19] // CHECK:STDOUT: %p.param: %ptr.31e = value_param call_param0 // CHECK:STDOUT: %.loc19_20: type = splice_block %ptr.loc19_20 [concrete = constants.%ptr.31e] { // CHECK:STDOUT: %C.ref: type = name_ref C, file.%C.decl [concrete = constants.%C] // CHECK:STDOUT: %ptr.loc19_20: type = ptr_type %C.ref [concrete = constants.%ptr.31e] // CHECK:STDOUT: } // CHECK:STDOUT: %p: %ptr.31e = value_binding p, %p.param // CHECK:STDOUT: %return.param: ref %ptr.27c = out_param call_param1 // CHECK:STDOUT: %return: ref %ptr.27c = return_slot %return.param // CHECK:STDOUT: } // CHECK:STDOUT: %ConvertBToA.decl: %ConvertBToA.type = fn_decl @ConvertBToA [concrete = constants.%ConvertBToA] { // CHECK:STDOUT: %p.patt: %pattern_type.191 = value_binding_pattern p [concrete] // CHECK:STDOUT: %p.param_patt: %pattern_type.191 = value_param_pattern %p.patt [concrete] // CHECK:STDOUT: %return.patt: %pattern_type.f29 = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: %pattern_type.f29 = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %A.ref: type = name_ref A, file.%A.decl [concrete = constants.%A] // CHECK:STDOUT: %ptr.loc20_27: type = ptr_type %A.ref [concrete = constants.%ptr.643] // CHECK:STDOUT: %.loc20_27: Core.Form = init_form %ptr.loc20_27 [concrete = constants.%.ebf] // CHECK:STDOUT: %p.param: %ptr.27c = value_param call_param0 // CHECK:STDOUT: %.loc20_20: type = splice_block %ptr.loc20_20 [concrete = constants.%ptr.27c] { // CHECK:STDOUT: %B.ref: type = name_ref B, file.%B.decl [concrete = constants.%B] // CHECK:STDOUT: %ptr.loc20_20: type = ptr_type %B.ref [concrete = constants.%ptr.27c] // CHECK:STDOUT: } // CHECK:STDOUT: %p: %ptr.27c = value_binding p, %p.param // CHECK:STDOUT: %return.param: ref %ptr.643 = out_param call_param1 // CHECK:STDOUT: %return: ref %ptr.643 = return_slot %return.param // CHECK:STDOUT: } // CHECK:STDOUT: %ConvertCToA.decl: %ConvertCToA.type = fn_decl @ConvertCToA [concrete = constants.%ConvertCToA] { // CHECK:STDOUT: %p.patt: %pattern_type.506 = value_binding_pattern p [concrete] // CHECK:STDOUT: %p.param_patt: %pattern_type.506 = value_param_pattern %p.patt [concrete] // CHECK:STDOUT: %return.patt: %pattern_type.f29 = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: %pattern_type.f29 = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %A.ref: type = name_ref A, file.%A.decl [concrete = constants.%A] // CHECK:STDOUT: %ptr.loc21_27: type = ptr_type %A.ref [concrete = constants.%ptr.643] // CHECK:STDOUT: %.loc21_27: Core.Form = init_form %ptr.loc21_27 [concrete = constants.%.ebf] // CHECK:STDOUT: %p.param: %ptr.31e = value_param call_param0 // CHECK:STDOUT: %.loc21_20: type = splice_block %ptr.loc21_20 [concrete = constants.%ptr.31e] { // CHECK:STDOUT: %C.ref: type = name_ref C, file.%C.decl [concrete = constants.%C] // CHECK:STDOUT: %ptr.loc21_20: type = ptr_type %C.ref [concrete = constants.%ptr.31e] // CHECK:STDOUT: } // CHECK:STDOUT: %p: %ptr.31e = value_binding p, %p.param // CHECK:STDOUT: %return.param: ref %ptr.643 = out_param call_param1 // CHECK:STDOUT: %return: ref %ptr.643 = return_slot %return.param // CHECK:STDOUT: } // CHECK:STDOUT: %ConvertValue.decl: %ConvertValue.type = fn_decl @ConvertValue [concrete = constants.%ConvertValue] { // CHECK:STDOUT: %c.patt: %pattern_type.7c7 = value_binding_pattern c [concrete] // CHECK:STDOUT: %c.param_patt: %pattern_type.7c7 = value_param_pattern %c.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %c.param: %C = value_param call_param0 // CHECK:STDOUT: %C.ref: type = name_ref C, file.%C.decl [concrete = constants.%C] // CHECK:STDOUT: %c: %C = value_binding c, %c.param // CHECK:STDOUT: } // CHECK:STDOUT: %ConvertRef.decl: %ConvertRef.type = fn_decl @ConvertRef [concrete = constants.%ConvertRef] { // CHECK:STDOUT: %c.patt: %pattern_type.506 = value_binding_pattern c [concrete] // CHECK:STDOUT: %c.param_patt: %pattern_type.506 = value_param_pattern %c.patt [concrete] // CHECK:STDOUT: %return.patt: %pattern_type.f29 = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: %pattern_type.f29 = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %A.ref.loc27: type = name_ref A, file.%A.decl [concrete = constants.%A] // CHECK:STDOUT: %ptr.loc27_26: type = ptr_type %A.ref.loc27 [concrete = constants.%ptr.643] // CHECK:STDOUT: %.loc27_26: Core.Form = init_form %ptr.loc27_26 [concrete = constants.%.ebf] // CHECK:STDOUT: %c.param: %ptr.31e = value_param call_param0 // CHECK:STDOUT: %.loc27_19: type = splice_block %ptr.loc27_19 [concrete = constants.%ptr.31e] { // CHECK:STDOUT: %C.ref: type = name_ref C, file.%C.decl [concrete = constants.%C] // CHECK:STDOUT: %ptr.loc27_19: type = ptr_type %C.ref [concrete = constants.%ptr.31e] // CHECK:STDOUT: } // CHECK:STDOUT: %c: %ptr.31e = value_binding c, %c.param // CHECK:STDOUT: %return.param: ref %ptr.643 = out_param call_param1 // CHECK:STDOUT: %return: ref %ptr.643 = return_slot %return.param // CHECK:STDOUT: } // CHECK:STDOUT: %ConvertInit.decl: %ConvertInit.type = fn_decl @ConvertInit [concrete = constants.%ConvertInit] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @ConvertCToB(%p.param: %ptr.31e) -> out %return.param: %ptr.27c { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %p.ref: %ptr.31e = name_ref p, %p // CHECK:STDOUT: %.loc19_39.1: ref %C = deref %p.ref // CHECK:STDOUT: %.loc19_39.2: ref %B = class_element_access %.loc19_39.1, element0 // CHECK:STDOUT: %addr: %ptr.27c = addr_of %.loc19_39.2 // CHECK:STDOUT: %.loc19_39.3: %ptr.27c = converted %p.ref, %addr // CHECK:STDOUT: %impl.elem0: %.cc8 = impl_witness_access constants.%Copy.impl_witness.672, element0 [concrete = constants.%ptr.as.Copy.impl.Op.20b] // CHECK:STDOUT: %bound_method.loc19_39.1: = bound_method %.loc19_39.3, %impl.elem0 // CHECK:STDOUT: %specific_fn: = specific_function %impl.elem0, @ptr.as.Copy.impl.Op(constants.%B) [concrete = constants.%ptr.as.Copy.impl.Op.specific_fn.695] // CHECK:STDOUT: %bound_method.loc19_39.2: = bound_method %.loc19_39.3, %specific_fn // CHECK:STDOUT: %ptr.as.Copy.impl.Op.call: init %ptr.27c = call %bound_method.loc19_39.2(%.loc19_39.3) // CHECK:STDOUT: return %ptr.as.Copy.impl.Op.call // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @ConvertBToA(%p.param: %ptr.27c) -> out %return.param: %ptr.643 { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %p.ref: %ptr.27c = name_ref p, %p // CHECK:STDOUT: %.loc20_39.1: ref %B = deref %p.ref // CHECK:STDOUT: %.loc20_39.2: ref %A = class_element_access %.loc20_39.1, element0 // CHECK:STDOUT: %addr: %ptr.643 = addr_of %.loc20_39.2 // CHECK:STDOUT: %.loc20_39.3: %ptr.643 = converted %p.ref, %addr // CHECK:STDOUT: %impl.elem0: %.2f8 = impl_witness_access constants.%Copy.impl_witness.551, element0 [concrete = constants.%ptr.as.Copy.impl.Op.510] // CHECK:STDOUT: %bound_method.loc20_39.1: = bound_method %.loc20_39.3, %impl.elem0 // CHECK:STDOUT: %specific_fn: = specific_function %impl.elem0, @ptr.as.Copy.impl.Op(constants.%A) [concrete = constants.%ptr.as.Copy.impl.Op.specific_fn.05b] // CHECK:STDOUT: %bound_method.loc20_39.2: = bound_method %.loc20_39.3, %specific_fn // CHECK:STDOUT: %ptr.as.Copy.impl.Op.call: init %ptr.643 = call %bound_method.loc20_39.2(%.loc20_39.3) // CHECK:STDOUT: return %ptr.as.Copy.impl.Op.call // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @ConvertCToA(%p.param: %ptr.31e) -> out %return.param: %ptr.643 { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %p.ref: %ptr.31e = name_ref p, %p // CHECK:STDOUT: %.loc21_39.1: ref %C = deref %p.ref // CHECK:STDOUT: %.loc21_39.2: ref %B = class_element_access %.loc21_39.1, element0 // CHECK:STDOUT: %.loc21_39.3: ref %A = class_element_access %.loc21_39.2, element0 // CHECK:STDOUT: %addr: %ptr.643 = addr_of %.loc21_39.3 // CHECK:STDOUT: %.loc21_39.4: %ptr.643 = converted %p.ref, %addr // CHECK:STDOUT: %impl.elem0: %.2f8 = impl_witness_access constants.%Copy.impl_witness.551, element0 [concrete = constants.%ptr.as.Copy.impl.Op.510] // CHECK:STDOUT: %bound_method.loc21_39.1: = bound_method %.loc21_39.4, %impl.elem0 // CHECK:STDOUT: %specific_fn: = specific_function %impl.elem0, @ptr.as.Copy.impl.Op(constants.%A) [concrete = constants.%ptr.as.Copy.impl.Op.specific_fn.05b] // CHECK:STDOUT: %bound_method.loc21_39.2: = bound_method %.loc21_39.4, %specific_fn // CHECK:STDOUT: %ptr.as.Copy.impl.Op.call: init %ptr.643 = call %bound_method.loc21_39.2(%.loc21_39.4) // CHECK:STDOUT: return %ptr.as.Copy.impl.Op.call // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @ConvertValue(%c.param: %C) { // CHECK:STDOUT: !entry: // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %a.patt: %pattern_type.1ab = value_binding_pattern a [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %c.ref: %C = name_ref c, %c // CHECK:STDOUT: %A.ref: type = name_ref A, file.%A.decl [concrete = constants.%A] // CHECK:STDOUT: %.loc24_21.1: ref %B = class_element_access %c.ref, element0 // CHECK:STDOUT: %.loc24_21.2: ref %A = class_element_access %.loc24_21.1, element0 // CHECK:STDOUT: %.loc24_21.3: ref %A = converted %c.ref, %.loc24_21.2 // CHECK:STDOUT: %.loc24_21.4: %A = acquire_value %.loc24_21.3 // CHECK:STDOUT: %a: %A = value_binding a, %.loc24_21.4 // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @ConvertRef(%c.param: %ptr.31e) -> out %return.param: %ptr.643 { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %c.ref: %ptr.31e = name_ref c, %c // CHECK:STDOUT: %.loc28_12: ref %C = deref %c.ref // CHECK:STDOUT: %A.ref.loc28: type = name_ref A, file.%A.decl [concrete = constants.%A] // CHECK:STDOUT: %.loc28_15.1: ref %B = class_element_access %.loc28_12, element0 // CHECK:STDOUT: %.loc28_15.2: ref %A = class_element_access %.loc28_15.1, element0 // CHECK:STDOUT: %.loc28_15.3: ref %A = converted %.loc28_12, %.loc28_15.2 // CHECK:STDOUT: %addr: %ptr.643 = addr_of %.loc28_15.3 // CHECK:STDOUT: %impl.elem0: %.2f8 = impl_witness_access constants.%Copy.impl_witness.551, element0 [concrete = constants.%ptr.as.Copy.impl.Op.510] // CHECK:STDOUT: %bound_method.loc28_10.1: = bound_method %addr, %impl.elem0 // CHECK:STDOUT: %specific_fn: = specific_function %impl.elem0, @ptr.as.Copy.impl.Op(constants.%A) [concrete = constants.%ptr.as.Copy.impl.Op.specific_fn.05b] // CHECK:STDOUT: %bound_method.loc28_10.2: = bound_method %addr, %specific_fn // CHECK:STDOUT: %ptr.as.Copy.impl.Op.call: init %ptr.643 = call %bound_method.loc28_10.2(%addr) // CHECK:STDOUT: return %ptr.as.Copy.impl.Op.call // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @ConvertInit() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %a.patt: %pattern_type.1ab = value_binding_pattern a [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %int_1: Core.IntLiteral = int_value 1 [concrete = constants.%int_1.5b8] // CHECK:STDOUT: %.loc32_46.1: %struct_type.a.a6c = struct_literal (%int_1) [concrete = constants.%struct.48c] // CHECK:STDOUT: %int_2: Core.IntLiteral = int_value 2 [concrete = constants.%int_2.ecc] // CHECK:STDOUT: %.loc32_55.1: %struct_type.base.b.bf0 = struct_literal (%.loc32_46.1, %int_2) [concrete = constants.%struct.ff9] // CHECK:STDOUT: %int_3: Core.IntLiteral = int_value 3 [concrete = constants.%int_3.1ba] // CHECK:STDOUT: %.loc32_64.1: %struct_type.base.c.136 = struct_literal (%.loc32_55.1, %int_3) [concrete = constants.%struct.2aa] // CHECK:STDOUT: %C.ref: type = name_ref C, file.%C.decl [concrete = constants.%C] // CHECK:STDOUT: %impl.elem0.loc32_46: %.545 = impl_witness_access constants.%ImplicitAs.impl_witness.6bc, element0 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5] // CHECK:STDOUT: %bound_method.loc32_46.1: = bound_method %int_1, %impl.elem0.loc32_46 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.bound.215] // CHECK:STDOUT: %specific_fn.loc32_46: = specific_function %impl.elem0.loc32_46, @Core.IntLiteral.as.ImplicitAs.impl.Convert(constants.%int_32) [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn] // CHECK:STDOUT: %bound_method.loc32_46.2: = bound_method %int_1, %specific_fn.loc32_46 [concrete = constants.%bound_method.38b] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc32_46: init %i32 = call %bound_method.loc32_46.2(%int_1) [concrete = constants.%int_1.5d2] // CHECK:STDOUT: %.loc32_46.2: init %i32 = converted %int_1, %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc32_46 [concrete = constants.%int_1.5d2] // CHECK:STDOUT: %.loc32_64.2: ref %C = temporary_storage // CHECK:STDOUT: %.loc32_64.3: ref %.eaa = class_element_access %.loc32_64.2, element0 // CHECK:STDOUT: %.loc32_55.2: ref %.157 = class_element_access %.loc32_64.3, element0 // CHECK:STDOUT: %.loc32_46.3: ref %i32 = class_element_access %.loc32_55.2, element0 // CHECK:STDOUT: %.loc32_46.4: init %i32 to %.loc32_46.3 = in_place_init %.loc32_46.2 [concrete = constants.%int_1.5d2] // CHECK:STDOUT: %.loc32_46.5: init %.157 to %.loc32_55.2 = class_init (%.loc32_46.4) [concrete = constants.%struct.e01] // CHECK:STDOUT: %.loc32_55.3: init %.157 = converted %.loc32_46.1, %.loc32_46.5 [concrete = constants.%struct.e01] // CHECK:STDOUT: %.loc32_55.4: init %A = as_compatible %.loc32_55.3 [concrete = constants.%A.val] // CHECK:STDOUT: %impl.elem0.loc32_55: %.545 = impl_witness_access constants.%ImplicitAs.impl_witness.6bc, element0 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5] // CHECK:STDOUT: %bound_method.loc32_55.1: = bound_method %int_2, %impl.elem0.loc32_55 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.bound.4e5] // CHECK:STDOUT: %specific_fn.loc32_55: = specific_function %impl.elem0.loc32_55, @Core.IntLiteral.as.ImplicitAs.impl.Convert(constants.%int_32) [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn] // CHECK:STDOUT: %bound_method.loc32_55.2: = bound_method %int_2, %specific_fn.loc32_55 [concrete = constants.%bound_method.646] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc32_55: init %i32 = call %bound_method.loc32_55.2(%int_2) [concrete = constants.%int_2.ef8] // CHECK:STDOUT: %.loc32_55.5: init %i32 = converted %int_2, %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc32_55 [concrete = constants.%int_2.ef8] // CHECK:STDOUT: %.loc32_55.6: ref %i32 = class_element_access %.loc32_64.3, element1 // CHECK:STDOUT: %.loc32_55.7: init %i32 to %.loc32_55.6 = in_place_init %.loc32_55.5 [concrete = constants.%int_2.ef8] // CHECK:STDOUT: %.loc32_55.8: init %.eaa to %.loc32_64.3 = class_init (%.loc32_55.4, %.loc32_55.7) [concrete = constants.%struct.bc2] // CHECK:STDOUT: %.loc32_64.4: init %.eaa = converted %.loc32_55.1, %.loc32_55.8 [concrete = constants.%struct.bc2] // CHECK:STDOUT: %.loc32_64.5: init %B = as_compatible %.loc32_64.4 [concrete = constants.%B.val] // CHECK:STDOUT: %impl.elem0.loc32_64: %.545 = impl_witness_access constants.%ImplicitAs.impl_witness.6bc, element0 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5] // CHECK:STDOUT: %bound_method.loc32_64.1: = bound_method %int_3, %impl.elem0.loc32_64 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.bound.061] // CHECK:STDOUT: %specific_fn.loc32_64: = specific_function %impl.elem0.loc32_64, @Core.IntLiteral.as.ImplicitAs.impl.Convert(constants.%int_32) [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn] // CHECK:STDOUT: %bound_method.loc32_64.2: = bound_method %int_3, %specific_fn.loc32_64 [concrete = constants.%bound_method.fa7] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc32_64: init %i32 = call %bound_method.loc32_64.2(%int_3) [concrete = constants.%int_3.822] // CHECK:STDOUT: %.loc32_64.6: init %i32 = converted %int_3, %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc32_64 [concrete = constants.%int_3.822] // CHECK:STDOUT: %.loc32_64.7: ref %i32 = class_element_access %.loc32_64.2, element1 // CHECK:STDOUT: %.loc32_64.8: init %i32 to %.loc32_64.7 = in_place_init %.loc32_64.6 [concrete = constants.%int_3.822] // CHECK:STDOUT: %.loc32_64.9: init %C to %.loc32_64.2 = class_init (%.loc32_64.5, %.loc32_64.8) [concrete = constants.%C.val] // CHECK:STDOUT: %.loc32_66.1: init %C = converted %.loc32_64.1, %.loc32_64.9 [concrete = constants.%C.val] // CHECK:STDOUT: %A.ref: type = name_ref A, file.%A.decl [concrete = constants.%A] // CHECK:STDOUT: %.loc32_66.2: ref %C = temporary %.loc32_64.2, %.loc32_66.1 // CHECK:STDOUT: %.loc32_66.3: ref %B = class_element_access %.loc32_66.2, element0 // CHECK:STDOUT: %.loc32_66.4: ref %A = class_element_access %.loc32_66.3, element0 // CHECK:STDOUT: %.loc32_66.5: ref %A = converted %.loc32_66.1, %.loc32_66.4 // CHECK:STDOUT: %.loc32_66.6: %A = acquire_value %.loc32_66.5 // CHECK:STDOUT: %a: %A = value_binding a, %.loc32_66.6 // CHECK:STDOUT: %Destroy.Op.bound: = bound_method %.loc32_66.2, constants.%Destroy.Op // CHECK:STDOUT: %Destroy.Op.call: init %empty_tuple.type = call %Destroy.Op.bound(%.loc32_66.2) // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @Destroy.Op(%self.param: ref %C) = "no_op"; // CHECK:STDOUT: // CHECK:STDOUT: --- qualified.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %A: type = class_type @A [concrete] // CHECK:STDOUT: %B: type = class_type @B [concrete] // CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] // CHECK:STDOUT: %const.786: type = const_type %A [concrete] // CHECK:STDOUT: %ptr.3bd: type = ptr_type %const.786 [concrete] // CHECK:STDOUT: %TakeConstAPtr.type: type = fn_type @TakeConstAPtr [concrete] // CHECK:STDOUT: %TakeConstAPtr: %TakeConstAPtr.type = struct_value () [concrete] // CHECK:STDOUT: %ptr.27c: type = ptr_type %B [concrete] // CHECK:STDOUT: %const.30c: type = const_type %B [concrete] // CHECK:STDOUT: %ptr.375: type = ptr_type %const.30c [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @PassNonConstBPtr(%p.param: %ptr.27c) { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %TakeConstAPtr.ref: %TakeConstAPtr.type = name_ref TakeConstAPtr, file.%TakeConstAPtr.decl [concrete = constants.%TakeConstAPtr] // CHECK:STDOUT: %p.ref: %ptr.27c = name_ref p, %p // CHECK:STDOUT: %.loc15_17.1: ref %B = deref %p.ref // CHECK:STDOUT: %.loc15_17.2: ref %A = class_element_access %.loc15_17.1, element0 // CHECK:STDOUT: %addr: %ptr.3bd = addr_of %.loc15_17.2 // CHECK:STDOUT: %.loc15_17.3: %ptr.3bd = as_compatible %addr // CHECK:STDOUT: %.loc15_17.4: %ptr.3bd = converted %p.ref, %.loc15_17.3 // CHECK:STDOUT: %TakeConstAPtr.call: init %empty_tuple.type = call %TakeConstAPtr.ref(%.loc15_17.4) // CHECK:STDOUT: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @PassConstBPtr(%p.param: %ptr.375) { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %TakeConstAPtr.ref: %TakeConstAPtr.type = name_ref TakeConstAPtr, file.%TakeConstAPtr.decl [concrete = constants.%TakeConstAPtr] // CHECK:STDOUT: %p.ref: %ptr.375 = name_ref p, %p // CHECK:STDOUT: %.loc21_17.1: ref %const.30c = deref %p.ref // CHECK:STDOUT: %.loc21_17.2: ref %const.786 = class_element_access %.loc21_17.1, element0 // CHECK:STDOUT: %addr: %ptr.3bd = addr_of %.loc21_17.2 // CHECK:STDOUT: %.loc21_17.3: %ptr.3bd = converted %p.ref, %addr // CHECK:STDOUT: %TakeConstAPtr.call: init %empty_tuple.type = call %TakeConstAPtr.ref(%.loc21_17.3) // CHECK:STDOUT: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- fail_todo_qualified_non_ptr.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %A: type = class_type @A [concrete] // CHECK:STDOUT: %B: type = class_type @B [concrete] // CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] // CHECK:STDOUT: %const.786: type = const_type %A [concrete] // CHECK:STDOUT: %TakeConstA.type: type = fn_type @TakeConstA [concrete] // CHECK:STDOUT: %TakeConstA: %TakeConstA.type = struct_value () [concrete] // CHECK:STDOUT: %const.30c: type = const_type %B [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @PassNonConstB(%p.param: %B) { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %TakeConstA.ref: %TakeConstA.type = name_ref TakeConstA, file.%TakeConstA.decl [concrete = constants.%TakeConstA] // CHECK:STDOUT: %p.ref: %B = name_ref p, %p // CHECK:STDOUT: %.loc25: %const.786 = converted %p.ref, [concrete = ] // CHECK:STDOUT: %TakeConstA.call: init %empty_tuple.type = call %TakeConstA.ref() // CHECK:STDOUT: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @PassConstB(%p.param: %const.30c) { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %TakeConstA.ref: %TakeConstA.type = name_ref TakeConstA, file.%TakeConstA.decl [concrete = constants.%TakeConstA] // CHECK:STDOUT: %p.ref: %const.30c = name_ref p, %p // CHECK:STDOUT: %.loc41: %const.786 = converted %p.ref, [concrete = ] // CHECK:STDOUT: %TakeConstA.call: init %empty_tuple.type = call %TakeConstA.ref() // CHECK:STDOUT: // CHECK:STDOUT: } // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/class/inheritance/fail_base_as_declared_name.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/convert.carbon // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/class/inheritance/fail_base_as_declared_name.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/class/inheritance/fail_base_as_declared_name.carbon namespace N; // CHECK:STDERR: fail_base_as_declared_name.carbon:[[@LINE+8]]:6: error: `.` should be followed by a name [ExpectedDeclNameAfterPeriod] // CHECK:STDERR: fn N.base() {} // CHECK:STDERR: ^~~~ // CHECK:STDERR: // CHECK:STDERR: fail_base_as_declared_name.carbon:[[@LINE+4]]:6: error: semantics TODO: `handle invalid parse trees in `check`` [SemanticsTodo] // CHECK:STDERR: fn N.base() {} // CHECK:STDERR: ^~~~ // CHECK:STDERR: fn N.base() {} ================================================ FILE: toolchain/check/testdata/class/inheritance/fail_base_bad_type.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/int.carbon // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/class/inheritance/fail_base_bad_type.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/class/inheritance/fail_base_bad_type.carbon // --- fail_derive_from_error.carbon library "[[@TEST_NAME]]"; class DeriveFromError { // CHECK:STDERR: fail_derive_from_error.carbon:[[@LINE+4]]:16: error: name `error` not found [NameNotFound] // CHECK:STDERR: extend base: error; // CHECK:STDERR: ^~~~~ // CHECK:STDERR: extend base: error; } // This should not produce an error. fn AccessMemberWithInvalidBaseError(p: DeriveFromError*) -> i32 { return (*p).n; } // --- fail_derive_from_non_type.carbon library "[[@TEST_NAME]]"; class DeriveFromNonType { // CHECK:STDERR: fail_derive_from_non_type.carbon:[[@LINE+7]]:16: error: cannot implicitly convert non-type value of type `Core.IntLiteral` to `type` [ConversionFailureNonTypeToFacet] // CHECK:STDERR: extend base: 32; // CHECK:STDERR: ^~ // CHECK:STDERR: fail_derive_from_non_type.carbon:[[@LINE+4]]:16: note: type `Core.IntLiteral` does not implement interface `Core.ImplicitAs(type)` [MissingImplInMemberAccessInContext] // CHECK:STDERR: extend base: 32; // CHECK:STDERR: ^~ // CHECK:STDERR: extend base: 32; } fn AccessMemberWithInvalidBasNonType(p: DeriveFromNonType*) -> i32 { return (*p).n; } // --- fail_derive_from_i32.carbon library "[[@TEST_NAME]]"; class DeriveFromi32 { // CHECK:STDERR: fail_derive_from_i32.carbon:[[@LINE+4]]:16: error: deriving from final type `i32`; base type must be an `abstract` or `base` class [BaseIsFinal] // CHECK:STDERR: extend base: i32; // CHECK:STDERR: ^~~ // CHECK:STDERR: extend base: i32; } // It's not really important whether this conversion produces an error or not, // but it shouldn't crash. // CHECK:STDERR: fail_derive_from_i32.carbon:[[@LINE+7]]:53: error: cannot implicitly convert expression of type `DeriveFromi32*` to `i32*` [ConversionFailure] // CHECK:STDERR: fn ConvertToBadBasei32(p: DeriveFromi32*) -> i32* { return p; } // CHECK:STDERR: ^~~~~~~~~ // CHECK:STDERR: fail_derive_from_i32.carbon:[[@LINE+4]]:53: note: type `DeriveFromi32*` does not implement interface `Core.ImplicitAs(i32*)` [MissingImplInMemberAccessInContext] // CHECK:STDERR: fn ConvertToBadBasei32(p: DeriveFromi32*) -> i32* { return p; } // CHECK:STDERR: ^~~~~~~~~ // CHECK:STDERR: fn ConvertToBadBasei32(p: DeriveFromi32*) -> i32* { return p; } // CHECK:STDERR: fail_derive_from_i32.carbon:[[@LINE+4]]:70: error: member name `n` not found in `DeriveFromi32` [MemberNameNotFoundInInstScope] // CHECK:STDERR: fn AccessMemberWithInvalidBasei32(p: DeriveFromi32*) -> i32 { return (*p).n; } // CHECK:STDERR: ^~~~~~ // CHECK:STDERR: fn AccessMemberWithInvalidBasei32(p: DeriveFromi32*) -> i32 { return (*p).n; } // --- fail_derive_from_tuple.carbon library "[[@TEST_NAME]]"; base class Base {} class DeriveFromTuple { // CHECK:STDERR: fail_derive_from_tuple.carbon:[[@LINE+4]]:16: error: deriving from final type `(Base,)`; base type must be an `abstract` or `base` class [BaseIsFinal] // CHECK:STDERR: extend base: (Base,); // CHECK:STDERR: ^~~~~~~ // CHECK:STDERR: extend base: (Base,); } // CHECK:STDERR: fail_derive_from_tuple.carbon:[[@LINE+7]]:61: error: cannot implicitly convert expression of type `DeriveFromTuple*` to `(Base,)*` [ConversionFailure] // CHECK:STDERR: fn ConvertToBadBaseTuple(p: DeriveFromTuple*) -> (Base,)* { return p; } // CHECK:STDERR: ^~~~~~~~~ // CHECK:STDERR: fail_derive_from_tuple.carbon:[[@LINE+4]]:61: note: type `DeriveFromTuple*` does not implement interface `Core.ImplicitAs((Base,)*)` [MissingImplInMemberAccessInContext] // CHECK:STDERR: fn ConvertToBadBaseTuple(p: DeriveFromTuple*) -> (Base,)* { return p; } // CHECK:STDERR: ^~~~~~~~~ // CHECK:STDERR: fn ConvertToBadBaseTuple(p: DeriveFromTuple*) -> (Base,)* { return p; } fn AccessMemberWithInvalidBaseTuple(p: DeriveFromTuple*) -> i32 { return (*p).n; } // --- fail_derive_from_struct.carbon library "[[@TEST_NAME]]"; // TODO: Should we allow this? // We do allow `{.base = {.a: i32, .b: i32}}`. class DeriveFromStruct { // CHECK:STDERR: fail_derive_from_struct.carbon:[[@LINE+4]]:16: error: deriving from final type `{.a: i32, .b: i32}`; base type must be an `abstract` or `base` class [BaseIsFinal] // CHECK:STDERR: extend base: {.a: i32, .b: i32}; // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~ // CHECK:STDERR: extend base: {.a: i32, .b: i32}; } // CHECK:STDERR: fail_derive_from_struct.carbon:[[@LINE+7]]:74: error: cannot implicitly convert expression of type `DeriveFromStruct*` to `{.a: i32, .b: i32}*` [ConversionFailure] // CHECK:STDERR: fn ConvertToBadBaseStruct(p: DeriveFromStruct*) -> {.a: i32, .b: i32}* { return p; } // CHECK:STDERR: ^~~~~~~~~ // CHECK:STDERR: fail_derive_from_struct.carbon:[[@LINE+4]]:74: note: type `DeriveFromStruct*` does not implement interface `Core.ImplicitAs({.a: i32, .b: i32}*)` [MissingImplInMemberAccessInContext] // CHECK:STDERR: fn ConvertToBadBaseStruct(p: DeriveFromStruct*) -> {.a: i32, .b: i32}* { return p; } // CHECK:STDERR: ^~~~~~~~~ // CHECK:STDERR: fn ConvertToBadBaseStruct(p: DeriveFromStruct*) -> {.a: i32, .b: i32}* { return p; } // It would be OK to reject this if we start actually looking in the struct type. fn AccessMemberWithInvalidBaseStruct(p: DeriveFromStruct*) -> i32 { return (*p).n; } // --- fail_derive_from_incomplete.carbon library "[[@TEST_NAME]]"; // CHECK:STDERR: fail_derive_from_incomplete.carbon:[[@LINE+4]]:1: error: `base` not allowed on `class` forward declaration, only definition [ModifierOnlyAllowedOnDefinition] // CHECK:STDERR: base class Incomplete; // CHECK:STDERR: ^~~~ // CHECK:STDERR: base class Incomplete; class DeriveFromIncomplete { // CHECK:STDERR: fail_derive_from_incomplete.carbon:[[@LINE+7]]:16: error: base `Incomplete` is an incomplete type [IncompleteTypeInBaseDecl] // CHECK:STDERR: extend base: Incomplete; // CHECK:STDERR: ^~~~~~~~~~ // CHECK:STDERR: fail_derive_from_incomplete.carbon:[[@LINE-6]]:1: note: class was forward declared here [ClassForwardDeclaredHere] // CHECK:STDERR: base class Incomplete; // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: extend base: Incomplete; } // CHECK:STDERR: fail_derive_from_incomplete.carbon:[[@LINE+7]]:74: error: cannot implicitly convert expression of type `DeriveFromIncomplete*` to `Incomplete*` [ConversionFailure] // CHECK:STDERR: fn ConvertToBadBaseIncomplete(p: DeriveFromIncomplete*) -> Incomplete* { return p; } // CHECK:STDERR: ^~~~~~~~~ // CHECK:STDERR: fail_derive_from_incomplete.carbon:[[@LINE+4]]:74: note: type `DeriveFromIncomplete*` does not implement interface `Core.ImplicitAs(Incomplete*)` [MissingImplInMemberAccessInContext] // CHECK:STDERR: fn ConvertToBadBaseIncomplete(p: DeriveFromIncomplete*) -> Incomplete* { return p; } // CHECK:STDERR: ^~~~~~~~~ // CHECK:STDERR: fn ConvertToBadBaseIncomplete(p: DeriveFromIncomplete*) -> Incomplete* { return p; } fn AccessMemberWithInvalidBaseIncomplete(p: DeriveFromIncomplete*) -> i32 { return (*p).n; } // --- fail_derive_from_final.carbon library "[[@TEST_NAME]]"; class Final { var a: i32; } class DeriveFromFinal { // CHECK:STDERR: fail_derive_from_final.carbon:[[@LINE+4]]:16: error: deriving from final type `Final`; base type must be an `abstract` or `base` class [BaseIsFinal] // CHECK:STDERR: extend base: Final; // CHECK:STDERR: ^~~~~ // CHECK:STDERR: extend base: Final; } // For error recovery purposes, we derive from the final type anyway. fn ConvertToBadBaseFinal(p: DeriveFromFinal*) -> Final* { return p; } fn AccessMemberWithInvalidBaseFinal_WithMember(p: DeriveFromFinal*) -> i32 { return (*p).a; } fn AccessMemberWithInvalidBaseFinal_NoMember(p: DeriveFromFinal*) -> i32 { // CHECK:STDERR: fail_derive_from_final.carbon:[[@LINE+4]]:10: error: member name `b` not found in `DeriveFromFinal` [MemberNameNotFoundInInstScope] // CHECK:STDERR: return (*p).b; // CHECK:STDERR: ^~~~~~ // CHECK:STDERR: return (*p).b; } ================================================ FILE: toolchain/check/testdata/class/inheritance/fail_base_method_define.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/convert.carbon // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/class/inheritance/fail_base_method_define.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/class/inheritance/fail_base_method_define.carbon base class B { fn F(); class C { fn F(); } } class D { extend base: B; } // CHECK:STDERR: fail_base_method_define.carbon:[[@LINE+4]]:6: error: out-of-line declaration requires a declaration in scoped entity [QualifiedDeclOutsideScopeEntity] // CHECK:STDERR: fn D.F() {} // CHECK:STDERR: ^ // CHECK:STDERR: fn D.F() {} // CHECK:STDERR: fail_base_method_define.carbon:[[@LINE+4]]:6: error: name `C` not found [NameNotFound] // CHECK:STDERR: fn D.C.F() {} // CHECK:STDERR: ^ // CHECK:STDERR: fn D.C.F() {} ================================================ FILE: toolchain/check/testdata/class/inheritance/fail_base_misplaced.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/convert.carbon // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/class/inheritance/fail_base_misplaced.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/class/inheritance/fail_base_misplaced.carbon base class B {} // CHECK:STDERR: fail_base_misplaced.carbon:[[@LINE+4]]:1: error: `base` declaration outside class [ClassSpecificDeclOutsideClass] // CHECK:STDERR: extend base: B; // CHECK:STDERR: ^~~~~~~~~~~~~~~ // CHECK:STDERR: extend base: B; fn F() { // CHECK:STDERR: fail_base_misplaced.carbon:[[@LINE+4]]:3: error: `base` declaration outside class [ClassSpecificDeclOutsideClass] // CHECK:STDERR: extend base: B; // CHECK:STDERR: ^~~~~~~~~~~~~~~ // CHECK:STDERR: extend base: B; } class C { fn F() { // CHECK:STDERR: fail_base_misplaced.carbon:[[@LINE+4]]:5: error: `base` declaration outside class [ClassSpecificDeclOutsideClass] // CHECK:STDERR: extend base: B; // CHECK:STDERR: ^~~~~~~~~~~~~~~ // CHECK:STDERR: extend base: B; } } ================================================ FILE: toolchain/check/testdata/class/inheritance/fail_base_modifiers.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/convert.carbon // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/class/inheritance/fail_base_modifiers.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/class/inheritance/fail_base_modifiers.carbon base class B {} class C1 { // CHECK:STDERR: fail_base_modifiers.carbon:[[@LINE+4]]:3: error: `private` not allowed on `base` declaration [ModifierNotAllowedOnDeclaration] // CHECK:STDERR: private extend base: B; // CHECK:STDERR: ^~~~~~~ // CHECK:STDERR: private extend base: B; } class C2 { // CHECK:STDERR: fail_base_modifiers.carbon:[[@LINE+8]]:3: error: `abstract` not allowed on `base` declaration [ModifierNotAllowedOnDeclaration] // CHECK:STDERR: abstract base: B; // CHECK:STDERR: ^~~~~~~~ // CHECK:STDERR: // CHECK:STDERR: fail_base_modifiers.carbon:[[@LINE+4]]:3: error: missing `extend` before `base` declaration [BaseMissingExtend] // CHECK:STDERR: abstract base: B; // CHECK:STDERR: ^~~~~~~~~~~~~~~~~ // CHECK:STDERR: abstract base: B; } class C3 { // CHECK:STDERR: fail_base_modifiers.carbon:[[@LINE+4]]:10: error: `default` not allowed on `base` declaration [ModifierNotAllowedOnDeclaration] // CHECK:STDERR: extend default base: B; // CHECK:STDERR: ^~~~~~~ // CHECK:STDERR: extend default base: B; } class C4 { // CHECK:STDERR: fail_base_modifiers.carbon:[[@LINE+7]]:10: error: `extend` repeated on declaration [ModifierRepeated] // CHECK:STDERR: extend extend base: B; // CHECK:STDERR: ^~~~~~ // CHECK:STDERR: fail_base_modifiers.carbon:[[@LINE+4]]:3: note: `extend` previously appeared here [ModifierPrevious] // CHECK:STDERR: extend extend base: B; // CHECK:STDERR: ^~~~~~ // CHECK:STDERR: extend extend base: B; } ================================================ FILE: toolchain/check/testdata/class/inheritance/fail_base_no_extend.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/convert.carbon // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/class/inheritance/fail_base_no_extend.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/class/inheritance/fail_base_no_extend.carbon base class B {} class C { // CHECK:STDERR: fail_base_no_extend.carbon:[[@LINE+4]]:3: error: missing `extend` before `base` declaration [BaseMissingExtend] // CHECK:STDERR: base: B; // CHECK:STDERR: ^~~~~~~~ // CHECK:STDERR: base: B; } ================================================ FILE: toolchain/check/testdata/class/inheritance/fail_base_repeated.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/convert.carbon // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/class/inheritance/fail_base_repeated.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/class/inheritance/fail_base_repeated.carbon base class B1 {} base class B2 {} class C { extend base: B1; // CHECK:STDERR: fail_base_repeated.carbon:[[@LINE+7]]:3: error: multiple `base` declarations in class; multiple inheritance is not permitted [BaseDeclRepeated] // CHECK:STDERR: extend base: B2; // CHECK:STDERR: ^~~~~~~~~~~~~~~~ // CHECK:STDERR: fail_base_repeated.carbon:[[@LINE-4]]:3: note: previous `base` declaration is here [ClassSpecificDeclPrevious] // CHECK:STDERR: extend base: B1; // CHECK:STDERR: ^~~~~~~~~~~~~~~~ // CHECK:STDERR: extend base: B2; } class D { // TODO: Consider adding a custom diagnostic for this case. extend base: B1; // CHECK:STDERR: fail_base_repeated.carbon:[[@LINE+7]]:3: error: multiple `base` declarations in class; multiple inheritance is not permitted [BaseDeclRepeated] // CHECK:STDERR: extend base: B1; // CHECK:STDERR: ^~~~~~~~~~~~~~~~ // CHECK:STDERR: fail_base_repeated.carbon:[[@LINE-4]]:3: note: previous `base` declaration is here [ClassSpecificDeclPrevious] // CHECK:STDERR: extend base: B1; // CHECK:STDERR: ^~~~~~~~~~~~~~~~ // CHECK:STDERR: extend base: B1; } ================================================ FILE: toolchain/check/testdata/class/inheritance/fail_base_unbound.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/convert.carbon // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/class/inheritance/fail_base_unbound.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/class/inheritance/fail_base_unbound.carbon base class B {} class C { extend base: B; } // CHECK:STDERR: fail_base_unbound.carbon:[[@LINE+4]]:12: error: expression cannot be used as a value [UseOfNonExprAsValue] // CHECK:STDERR: let b: B = C.base; // CHECK:STDERR: ^~~~~~ // CHECK:STDERR: let b: B = C.base; ================================================ FILE: toolchain/check/testdata/class/inheritance/fail_derived_to_base.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/int.carbon // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/class/inheritance/fail_derived_to_base.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/class/inheritance/fail_derived_to_base.carbon base class A1 { var a: i32; } base class A2 { var a: i32; } class B2 { extend base: A2; var b: i32; } // CHECK:STDERR: fail_derived_to_base.carbon:[[@LINE+7]]:38: error: cannot implicitly convert expression of type `B2*` to `A1*` [ConversionFailure] // CHECK:STDERR: fn ConvertUnrelated(p: B2*) -> A1* { return p; } // CHECK:STDERR: ^~~~~~~~~ // CHECK:STDERR: fail_derived_to_base.carbon:[[@LINE+4]]:38: note: type `B2*` does not implement interface `Core.ImplicitAs(A1*)` [MissingImplInMemberAccessInContext] // CHECK:STDERR: fn ConvertUnrelated(p: B2*) -> A1* { return p; } // CHECK:STDERR: ^~~~~~~~~ // CHECK:STDERR: fn ConvertUnrelated(p: B2*) -> A1* { return p; } class Incomplete; // CHECK:STDERR: fail_derived_to_base.carbon:[[@LINE+7]]:47: error: cannot implicitly convert expression of type `Incomplete*` to `A2*` [ConversionFailure] // CHECK:STDERR: fn ConvertIncomplete(p: Incomplete*) -> A2* { return p; } // CHECK:STDERR: ^~~~~~~~~ // CHECK:STDERR: fail_derived_to_base.carbon:[[@LINE+4]]:47: note: type `Incomplete*` does not implement interface `Core.ImplicitAs(A2*)` [MissingImplInMemberAccessInContext] // CHECK:STDERR: fn ConvertIncomplete(p: Incomplete*) -> A2* { return p; } // CHECK:STDERR: ^~~~~~~~~ // CHECK:STDERR: fn ConvertIncomplete(p: Incomplete*) -> A2* { return p; } ================================================ FILE: toolchain/check/testdata/class/inheritance/fail_extend_cycle.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/convert.carbon // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/class/inheritance/fail_extend_cycle.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/class/inheritance/fail_extend_cycle.carbon base class A { } base class B { // This ensures that the compiler treats A as complete. extend base: A; } // CHECK:STDERR: fail_extend_cycle.carbon:[[@LINE+7]]:1: error: redefinition of `class A` [RedeclRedef] // CHECK:STDERR: base class A { // CHECK:STDERR: ^~~~~~~~~~~~~~ // CHECK:STDERR: fail_extend_cycle.carbon:[[@LINE-11]]:1: note: previously defined here [RedeclPrevDef] // CHECK:STDERR: base class A { // CHECK:STDERR: ^~~~~~~~~~~~~~ // CHECK:STDERR: base class A { extend base: A; // CHECK:STDERR: fail_extend_cycle.carbon:[[@LINE+4]]:10: error: name `C` not found [NameNotFound] // CHECK:STDERR: var c: C; // CHECK:STDERR: ^ // CHECK:STDERR: var c: C; } ================================================ FILE: toolchain/check/testdata/class/inheritance/import_base.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/int.carbon // TODO: Add ranges and switch to "--dump-sem-ir-ranges=only". // EXTRA-ARGS: --dump-sem-ir-ranges=if-present // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/class/inheritance/import_base.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/class/inheritance/import_base.carbon // --- a.carbon library "[[@TEST_NAME]]"; base class Base { fn F[self: Self](); fn Unused[self: Self](); var x: i32; var unused_y: i32; } class Child { extend base: Base; } // --- b.carbon library "[[@TEST_NAME]]"; import library "a"; fn Run() { var a: Child = {.base = {.x = 0, .unused_y = 1}}; a.x = 2; a.F(); } // CHECK:STDOUT: --- a.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %Base: type = class_type @Base [concrete] // CHECK:STDOUT: %pattern_type.101: type = pattern_type %Base [concrete] // CHECK:STDOUT: %Base.F.type: type = fn_type @Base.F [concrete] // CHECK:STDOUT: %Base.F: %Base.F.type = struct_value () [concrete] // CHECK:STDOUT: %Base.Unused.type: type = fn_type @Base.Unused [concrete] // CHECK:STDOUT: %Base.Unused: %Base.Unused.type = struct_value () [concrete] // CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [concrete] // CHECK:STDOUT: %Int.type: type = generic_class_type @Int [concrete] // CHECK:STDOUT: %Int.generic: %Int.type = struct_value () [concrete] // CHECK:STDOUT: %i32: type = class_type @Int, @Int(%int_32) [concrete] // CHECK:STDOUT: %Base.elem: type = unbound_element_type %Base, %i32 [concrete] // CHECK:STDOUT: %struct_type.x.unused_y: type = struct_type {.x: %i32, .unused_y: %i32} [concrete] // CHECK:STDOUT: %complete_type.cf1: = complete_type_witness %struct_type.x.unused_y [concrete] // CHECK:STDOUT: %Child: type = class_type @Child [concrete] // CHECK:STDOUT: %Child.elem: type = unbound_element_type %Child, %Base [concrete] // CHECK:STDOUT: %struct_type.base: type = struct_type {.base: %Base} [concrete] // CHECK:STDOUT: %complete_type.5a1: = complete_type_witness %struct_type.base [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: .Int = %Core.Int // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: %Core.Int: %Int.type = import_ref Core//prelude/parts/int, Int, loaded [concrete = constants.%Int.generic] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .Base = %Base.decl // CHECK:STDOUT: .Child = %Child.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %Base.decl: type = class_decl @Base [concrete = constants.%Base] {} {} // CHECK:STDOUT: %Child.decl: type = class_decl @Child [concrete = constants.%Child] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @Base { // CHECK:STDOUT: %Base.F.decl: %Base.F.type = fn_decl @Base.F [concrete = constants.%Base.F] { // CHECK:STDOUT: %self.patt: %pattern_type.101 = value_binding_pattern self [concrete] // CHECK:STDOUT: %self.param_patt: %pattern_type.101 = value_param_pattern %self.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %self.param: %Base = value_param call_param0 // CHECK:STDOUT: %Self.ref: type = name_ref Self, constants.%Base [concrete = constants.%Base] // CHECK:STDOUT: %self: %Base = value_binding self, %self.param // CHECK:STDOUT: } // CHECK:STDOUT: %Base.Unused.decl: %Base.Unused.type = fn_decl @Base.Unused [concrete = constants.%Base.Unused] { // CHECK:STDOUT: %self.patt: %pattern_type.101 = value_binding_pattern self [concrete] // CHECK:STDOUT: %self.param_patt: %pattern_type.101 = value_param_pattern %self.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %self.param: %Base = value_param call_param0 // CHECK:STDOUT: %Self.ref: type = name_ref Self, constants.%Base [concrete = constants.%Base] // CHECK:STDOUT: %self: %Base = value_binding self, %self.param // CHECK:STDOUT: } // CHECK:STDOUT: %i32.loc8: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %.loc8: %Base.elem = field_decl x, element0 [concrete] // CHECK:STDOUT: %i32.loc9: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %.loc9: %Base.elem = field_decl unused_y, element1 [concrete] // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%struct_type.x.unused_y [concrete = constants.%complete_type.cf1] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%Base // CHECK:STDOUT: .F = %Base.F.decl // CHECK:STDOUT: .Unused = %Base.Unused.decl // CHECK:STDOUT: .x = %.loc8 // CHECK:STDOUT: .unused_y = %.loc9 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @Child { // CHECK:STDOUT: %Base.ref: type = name_ref Base, file.%Base.decl [concrete = constants.%Base] // CHECK:STDOUT: %.loc13: %Child.elem = base_decl %Base.ref, element0 [concrete] // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%struct_type.base [concrete = constants.%complete_type.5a1] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%Child // CHECK:STDOUT: .Base = // CHECK:STDOUT: .base = %.loc13 // CHECK:STDOUT: extend %Base.ref // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @Base.F(%self.param: %Base); // CHECK:STDOUT: // CHECK:STDOUT: fn @Base.Unused(%self.param: %Base); // CHECK:STDOUT: // CHECK:STDOUT: --- b.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %Run.type: type = fn_type @Run [concrete] // CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] // CHECK:STDOUT: %Run: %Run.type = struct_value () [concrete] // CHECK:STDOUT: %Child: type = class_type @Child [concrete] // CHECK:STDOUT: %Base: type = class_type @Base [concrete] // CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [concrete] // CHECK:STDOUT: %i32: type = class_type @Int, @Int(%int_32) [concrete] // CHECK:STDOUT: %struct_type.x.unused_y.9fc: type = struct_type {.x: %i32, .unused_y: %i32} [concrete] // CHECK:STDOUT: %complete_type.2da: = complete_type_witness %struct_type.x.unused_y.9fc [concrete] // CHECK:STDOUT: %struct_type.base.27a: type = struct_type {.base: %Base} [concrete] // CHECK:STDOUT: %complete_type.5a1: = complete_type_witness %struct_type.base.27a [concrete] // CHECK:STDOUT: %pattern_type.454: type = pattern_type %Child [concrete] // CHECK:STDOUT: %int_0.5c6: Core.IntLiteral = int_value 0 [concrete] // CHECK:STDOUT: %int_1.5b8: Core.IntLiteral = int_value 1 [concrete] // CHECK:STDOUT: %struct_type.x.unused_y.76a: type = struct_type {.x: Core.IntLiteral, .unused_y: Core.IntLiteral} [concrete] // CHECK:STDOUT: %struct.0bf: %struct_type.x.unused_y.76a = struct_value (%int_0.5c6, %int_1.5b8) [concrete] // CHECK:STDOUT: %struct_type.base.503: type = struct_type {.base: %struct_type.x.unused_y.76a} [concrete] // CHECK:STDOUT: %struct.cb5: %struct_type.base.503 = struct_value (%struct.0bf) [concrete] // CHECK:STDOUT: %.7a5: type = partial_type %Base [concrete] // CHECK:STDOUT: %ImplicitAs.type.cc7: type = generic_interface_type @ImplicitAs [concrete] // CHECK:STDOUT: %ImplicitAs.generic: %ImplicitAs.type.cc7 = struct_value () [concrete] // CHECK:STDOUT: %ImplicitAs.type.cf3: type = facet_type <@ImplicitAs, @ImplicitAs(%i32)> [concrete] // CHECK:STDOUT: %To: Core.IntLiteral = symbolic_binding To, 0 [symbolic] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.004: type = fn_type @Core.IntLiteral.as.ImplicitAs.impl.Convert, @Core.IntLiteral.as.ImplicitAs.impl(%To) [symbolic] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.255: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.004 = struct_value () [symbolic] // CHECK:STDOUT: %ImplicitAs.impl_witness.58d: = impl_witness imports.%ImplicitAs.impl_witness_table.e45, @Core.IntLiteral.as.ImplicitAs.impl(%int_32) [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.199: type = fn_type @Core.IntLiteral.as.ImplicitAs.impl.Convert, @Core.IntLiteral.as.ImplicitAs.impl(%int_32) [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.5f4: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.199 = struct_value () [concrete] // CHECK:STDOUT: %ImplicitAs.facet: %ImplicitAs.type.cf3 = facet_value Core.IntLiteral, (%ImplicitAs.impl_witness.58d) [concrete] // CHECK:STDOUT: %ImplicitAs.WithSelf.Convert.type.979: type = fn_type @ImplicitAs.WithSelf.Convert, @ImplicitAs.WithSelf(%i32, %ImplicitAs.facet) [concrete] // CHECK:STDOUT: %.7c3: type = fn_type_with_self_type %ImplicitAs.WithSelf.Convert.type.979, %ImplicitAs.facet [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.bound.386: = bound_method %int_0.5c6, %Core.IntLiteral.as.ImplicitAs.impl.Convert.5f4 [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn: = specific_function %Core.IntLiteral.as.ImplicitAs.impl.Convert.5f4, @Core.IntLiteral.as.ImplicitAs.impl.Convert(%int_32) [concrete] // CHECK:STDOUT: %bound_method.9f1: = bound_method %int_0.5c6, %Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn [concrete] // CHECK:STDOUT: %int_0.263: %i32 = int_value 0 [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.bound.f07: = bound_method %int_1.5b8, %Core.IntLiteral.as.ImplicitAs.impl.Convert.5f4 [concrete] // CHECK:STDOUT: %bound_method.307: = bound_method %int_1.5b8, %Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn [concrete] // CHECK:STDOUT: %int_1.47b: %i32 = int_value 1 [concrete] // CHECK:STDOUT: %struct.f56: %.7a5 = struct_value (%int_0.263, %int_1.47b) [concrete] // CHECK:STDOUT: %Base.val: %Base = struct_value (%int_0.263, %int_1.47b) [concrete] // CHECK:STDOUT: %Child.val: %Child = struct_value (%Base.val) [concrete] // CHECK:STDOUT: %Base.elem: type = unbound_element_type %Base, %i32 [concrete] // CHECK:STDOUT: %int_2.ecc: Core.IntLiteral = int_value 2 [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.bound.1eb: = bound_method %int_2.ecc, %Core.IntLiteral.as.ImplicitAs.impl.Convert.5f4 [concrete] // CHECK:STDOUT: %bound_method.ef3: = bound_method %int_2.ecc, %Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn [concrete] // CHECK:STDOUT: %int_2.d0d: %i32 = int_value 2 [concrete] // CHECK:STDOUT: %Base.F.type: type = fn_type @Base.F [concrete] // CHECK:STDOUT: %Base.F: %Base.F.type = struct_value () [concrete] // CHECK:STDOUT: %Destroy.type: type = facet_type <@Destroy> [concrete] // CHECK:STDOUT: %Destroy.Op.type: type = fn_type @Destroy.Op [concrete] // CHECK:STDOUT: %Destroy.Op: %Destroy.Op.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Main.Base = import_ref Main//a, Base, unloaded // CHECK:STDOUT: %Main.Child: type = import_ref Main//a, Child, loaded [concrete = constants.%Child] // CHECK:STDOUT: %Core.ece: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: .ImplicitAs = %Core.ImplicitAs // CHECK:STDOUT: .Destroy = %Core.Destroy // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: %Main.import_ref.8f2: = import_ref Main//a, loc10_1, loaded [concrete = constants.%complete_type.2da] // CHECK:STDOUT: %Main.import_ref.691 = import_ref Main//a, inst{{[0-9A-F]+}} [no loc], unloaded // CHECK:STDOUT: %Main.import_ref.062: %Base.F.type = import_ref Main//a, loc5_21, loaded [concrete = constants.%Base.F] // CHECK:STDOUT: %Main.import_ref.7b8 = import_ref Main//a, loc6_26, unloaded // CHECK:STDOUT: %Main.import_ref.183: %Base.elem = import_ref Main//a, loc8_8, loaded [concrete = %.61a] // CHECK:STDOUT: %Main.import_ref.7c0 = import_ref Main//a, loc9_15, unloaded // CHECK:STDOUT: %Main.import_ref.44c: = import_ref Main//a, loc14_1, loaded [concrete = constants.%complete_type.5a1] // CHECK:STDOUT: %Main.import_ref.d37 = import_ref Main//a, inst{{[0-9A-F]+}} [no loc], unloaded // CHECK:STDOUT: %Main.import_ref.4a6 = import_ref Main//a, loc13_20, unloaded // CHECK:STDOUT: %Main.import_ref.bd3719.2: type = import_ref Main//a, loc13_16, loaded [concrete = constants.%Base] // CHECK:STDOUT: %Core.ImplicitAs: %ImplicitAs.type.cc7 = import_ref Core//prelude/parts/as, ImplicitAs, loaded [concrete = constants.%ImplicitAs.generic] // CHECK:STDOUT: %Core.import_ref.b25: @Core.IntLiteral.as.ImplicitAs.impl.%Core.IntLiteral.as.ImplicitAs.impl.Convert.type (%Core.IntLiteral.as.ImplicitAs.impl.Convert.type.004) = import_ref Core//prelude/parts/int, loc{{\d+_\d+}}, loaded [symbolic = @Core.IntLiteral.as.ImplicitAs.impl.%Core.IntLiteral.as.ImplicitAs.impl.Convert (constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.255)] // CHECK:STDOUT: %ImplicitAs.impl_witness_table.e45 = impl_witness_table (%Core.import_ref.b25), @Core.IntLiteral.as.ImplicitAs.impl [concrete] // CHECK:STDOUT: %.61a: %Base.elem = field_decl x, element0 [concrete] // CHECK:STDOUT: %Core.Destroy: type = import_ref Core//prelude/parts/destroy, Destroy, loaded [concrete = constants.%Destroy.type] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Base = imports.%Main.Base // CHECK:STDOUT: .Child = imports.%Main.Child // CHECK:STDOUT: .Core = imports.%Core.ece // CHECK:STDOUT: .Run = %Run.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %default.import = import // CHECK:STDOUT: %Run.decl: %Run.type = fn_decl @Run [concrete = constants.%Run] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @Child [from "a.carbon"] { // CHECK:STDOUT: complete_type_witness = imports.%Main.import_ref.44c // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = imports.%Main.import_ref.d37 // CHECK:STDOUT: .base = imports.%Main.import_ref.4a6 // CHECK:STDOUT: .x = // CHECK:STDOUT: .F = // CHECK:STDOUT: extend imports.%Main.import_ref.bd3719.2 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @Base [from "a.carbon"] { // CHECK:STDOUT: complete_type_witness = imports.%Main.import_ref.8f2 // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = imports.%Main.import_ref.691 // CHECK:STDOUT: .F = imports.%Main.import_ref.062 // CHECK:STDOUT: .Unused = imports.%Main.import_ref.7b8 // CHECK:STDOUT: .x = imports.%Main.import_ref.183 // CHECK:STDOUT: .unused_y = imports.%Main.import_ref.7c0 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @Run() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %a.patt: %pattern_type.454 = ref_binding_pattern a [concrete] // CHECK:STDOUT: %a.var_patt: %pattern_type.454 = var_pattern %a.patt [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %a.var: ref %Child = var %a.var_patt // CHECK:STDOUT: %int_0: Core.IntLiteral = int_value 0 [concrete = constants.%int_0.5c6] // CHECK:STDOUT: %int_1: Core.IntLiteral = int_value 1 [concrete = constants.%int_1.5b8] // CHECK:STDOUT: %.loc7_49.1: %struct_type.x.unused_y.76a = struct_literal (%int_0, %int_1) [concrete = constants.%struct.0bf] // CHECK:STDOUT: %.loc7_50.1: %struct_type.base.503 = struct_literal (%.loc7_49.1) [concrete = constants.%struct.cb5] // CHECK:STDOUT: %impl.elem0.loc7_49.1: %.7c3 = impl_witness_access constants.%ImplicitAs.impl_witness.58d, element0 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.5f4] // CHECK:STDOUT: %bound_method.loc7_49.1: = bound_method %int_0, %impl.elem0.loc7_49.1 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.bound.386] // CHECK:STDOUT: %specific_fn.loc7_49.1: = specific_function %impl.elem0.loc7_49.1, @Core.IntLiteral.as.ImplicitAs.impl.Convert(constants.%int_32) [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn] // CHECK:STDOUT: %bound_method.loc7_49.2: = bound_method %int_0, %specific_fn.loc7_49.1 [concrete = constants.%bound_method.9f1] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc7_49.1: init %i32 = call %bound_method.loc7_49.2(%int_0) [concrete = constants.%int_0.263] // CHECK:STDOUT: %.loc7_49.2: init %i32 = converted %int_0, %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc7_49.1 [concrete = constants.%int_0.263] // CHECK:STDOUT: %.loc7_50.2: ref %.7a5 = class_element_access %a.var, element0 // CHECK:STDOUT: %.loc7_49.3: ref %i32 = class_element_access %.loc7_50.2, element0 // CHECK:STDOUT: %.loc7_49.4: init %i32 to %.loc7_49.3 = in_place_init %.loc7_49.2 [concrete = constants.%int_0.263] // CHECK:STDOUT: %impl.elem0.loc7_49.2: %.7c3 = impl_witness_access constants.%ImplicitAs.impl_witness.58d, element0 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.5f4] // CHECK:STDOUT: %bound_method.loc7_49.3: = bound_method %int_1, %impl.elem0.loc7_49.2 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.bound.f07] // CHECK:STDOUT: %specific_fn.loc7_49.2: = specific_function %impl.elem0.loc7_49.2, @Core.IntLiteral.as.ImplicitAs.impl.Convert(constants.%int_32) [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn] // CHECK:STDOUT: %bound_method.loc7_49.4: = bound_method %int_1, %specific_fn.loc7_49.2 [concrete = constants.%bound_method.307] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc7_49.2: init %i32 = call %bound_method.loc7_49.4(%int_1) [concrete = constants.%int_1.47b] // CHECK:STDOUT: %.loc7_49.5: init %i32 = converted %int_1, %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc7_49.2 [concrete = constants.%int_1.47b] // CHECK:STDOUT: %.loc7_49.6: ref %i32 = class_element_access %.loc7_50.2, element1 // CHECK:STDOUT: %.loc7_49.7: init %i32 to %.loc7_49.6 = in_place_init %.loc7_49.5 [concrete = constants.%int_1.47b] // CHECK:STDOUT: %.loc7_49.8: init %.7a5 to %.loc7_50.2 = class_init (%.loc7_49.4, %.loc7_49.7) [concrete = constants.%struct.f56] // CHECK:STDOUT: %.loc7_50.3: init %.7a5 = converted %.loc7_49.1, %.loc7_49.8 [concrete = constants.%struct.f56] // CHECK:STDOUT: %.loc7_50.4: init %Base = as_compatible %.loc7_50.3 [concrete = constants.%Base.val] // CHECK:STDOUT: %.loc7_50.5: init %Child to %a.var = class_init (%.loc7_50.4) [concrete = constants.%Child.val] // CHECK:STDOUT: %.loc7_3: init %Child = converted %.loc7_50.1, %.loc7_50.5 [concrete = constants.%Child.val] // CHECK:STDOUT: assign %a.var, %.loc7_3 // CHECK:STDOUT: %Child.ref: type = name_ref Child, imports.%Main.Child [concrete = constants.%Child] // CHECK:STDOUT: %a: ref %Child = ref_binding a, %a.var // CHECK:STDOUT: %a.ref.loc8: ref %Child = name_ref a, %a // CHECK:STDOUT: %x.ref: %Base.elem = name_ref x, imports.%Main.import_ref.183 [concrete = imports.%.61a] // CHECK:STDOUT: %.loc8_4.1: ref %Base = class_element_access %a.ref.loc8, element0 // CHECK:STDOUT: %.loc8_4.2: ref %Base = converted %a.ref.loc8, %.loc8_4.1 // CHECK:STDOUT: %.loc8_4.3: ref %i32 = class_element_access %.loc8_4.2, element0 // CHECK:STDOUT: %int_2: Core.IntLiteral = int_value 2 [concrete = constants.%int_2.ecc] // CHECK:STDOUT: %impl.elem0.loc8: %.7c3 = impl_witness_access constants.%ImplicitAs.impl_witness.58d, element0 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.5f4] // CHECK:STDOUT: %bound_method.loc8_7.1: = bound_method %int_2, %impl.elem0.loc8 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.bound.1eb] // CHECK:STDOUT: %specific_fn.loc8: = specific_function %impl.elem0.loc8, @Core.IntLiteral.as.ImplicitAs.impl.Convert(constants.%int_32) [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn] // CHECK:STDOUT: %bound_method.loc8_7.2: = bound_method %int_2, %specific_fn.loc8 [concrete = constants.%bound_method.ef3] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc8: init %i32 = call %bound_method.loc8_7.2(%int_2) [concrete = constants.%int_2.d0d] // CHECK:STDOUT: %.loc8_7: init %i32 = converted %int_2, %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc8 [concrete = constants.%int_2.d0d] // CHECK:STDOUT: assign %.loc8_4.3, %.loc8_7 // CHECK:STDOUT: %a.ref.loc9: ref %Child = name_ref a, %a // CHECK:STDOUT: %F.ref: %Base.F.type = name_ref F, imports.%Main.import_ref.062 [concrete = constants.%Base.F] // CHECK:STDOUT: %Base.F.bound: = bound_method %a.ref.loc9, %F.ref // CHECK:STDOUT: %.loc9_3.1: ref %Base = class_element_access %a.ref.loc9, element0 // CHECK:STDOUT: %.loc9_3.2: ref %Base = converted %a.ref.loc9, %.loc9_3.1 // CHECK:STDOUT: %.loc9_3.3: %Base = acquire_value %.loc9_3.2 // CHECK:STDOUT: %Base.F.call: init %empty_tuple.type = call %Base.F.bound(%.loc9_3.3) // CHECK:STDOUT: %Destroy.Op.bound: = bound_method %a.var, constants.%Destroy.Op // CHECK:STDOUT: %Destroy.Op.call: init %empty_tuple.type = call %Destroy.Op.bound(%a.var) // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @Base.F [from "a.carbon"]; // CHECK:STDOUT: // CHECK:STDOUT: fn @Destroy.Op(%self.param: ref %Child) = "no_op"; // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/class/inheritance/self_conversion.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/int.carbon // TODO: Add ranges and switch to "--dump-sem-ir-ranges=only". // EXTRA-ARGS: --dump-sem-ir-ranges=if-present // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/class/inheritance/self_conversion.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/class/inheritance/self_conversion.carbon base class Base { var a: i32; } class Derived { extend base: Base; fn SelfBase[self: Base]() -> i32; fn RefSelfBase[ref self: Base](); } fn Derived.SelfBase[self: Base]() -> i32 { return self.a; } fn Derived.RefSelfBase[ref self: Base]() { self.a = 1; } fn Call(p: Derived*) -> i32 { (*p).RefSelfBase(); return (*p).SelfBase(); } // CHECK:STDOUT: --- self_conversion.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %Base: type = class_type @Base [concrete] // CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [concrete] // CHECK:STDOUT: %Int.type: type = generic_class_type @Int [concrete] // CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] // CHECK:STDOUT: %Int.generic: %Int.type = struct_value () [concrete] // CHECK:STDOUT: %N: Core.IntLiteral = symbolic_binding N, 0 [symbolic] // CHECK:STDOUT: %i32: type = class_type @Int, @Int(%int_32) [concrete] // CHECK:STDOUT: %Base.elem: type = unbound_element_type %Base, %i32 [concrete] // CHECK:STDOUT: %struct_type.a: type = struct_type {.a: %i32} [concrete] // CHECK:STDOUT: %complete_type.fd7: = complete_type_witness %struct_type.a [concrete] // CHECK:STDOUT: %Derived: type = class_type @Derived [concrete] // CHECK:STDOUT: %Derived.elem: type = unbound_element_type %Derived, %Base [concrete] // CHECK:STDOUT: %pattern_type.101: type = pattern_type %Base [concrete] // CHECK:STDOUT: %.ff5: Core.Form = init_form %i32 [concrete] // CHECK:STDOUT: %pattern_type.7ce: type = pattern_type %i32 [concrete] // CHECK:STDOUT: %Derived.SelfBase.type: type = fn_type @Derived.SelfBase [concrete] // CHECK:STDOUT: %Derived.SelfBase: %Derived.SelfBase.type = struct_value () [concrete] // CHECK:STDOUT: %Derived.RefSelfBase.type: type = fn_type @Derived.RefSelfBase [concrete] // CHECK:STDOUT: %Derived.RefSelfBase: %Derived.RefSelfBase.type = struct_value () [concrete] // CHECK:STDOUT: %struct_type.base.27a: type = struct_type {.base: %Base} [concrete] // CHECK:STDOUT: %complete_type.5a1: = complete_type_witness %struct_type.base.27a [concrete] // CHECK:STDOUT: %Copy.type: type = facet_type <@Copy> [concrete] // CHECK:STDOUT: %Int.as.Copy.impl.Op.type.824: type = fn_type @Int.as.Copy.impl.Op, @Int.as.Copy.impl(%N) [symbolic] // CHECK:STDOUT: %Int.as.Copy.impl.Op.9b9: %Int.as.Copy.impl.Op.type.824 = struct_value () [symbolic] // CHECK:STDOUT: %Copy.impl_witness.f17: = impl_witness imports.%Copy.impl_witness_table.e76, @Int.as.Copy.impl(%int_32) [concrete] // CHECK:STDOUT: %Int.as.Copy.impl.Op.type.546: type = fn_type @Int.as.Copy.impl.Op, @Int.as.Copy.impl(%int_32) [concrete] // CHECK:STDOUT: %Int.as.Copy.impl.Op.664: %Int.as.Copy.impl.Op.type.546 = struct_value () [concrete] // CHECK:STDOUT: %Copy.facet: %Copy.type = facet_value %i32, (%Copy.impl_witness.f17) [concrete] // CHECK:STDOUT: %Copy.WithSelf.Op.type.081: type = fn_type @Copy.WithSelf.Op, @Copy.WithSelf(%Copy.facet) [concrete] // CHECK:STDOUT: %.8e2: type = fn_type_with_self_type %Copy.WithSelf.Op.type.081, %Copy.facet [concrete] // CHECK:STDOUT: %Int.as.Copy.impl.Op.specific_fn: = specific_function %Int.as.Copy.impl.Op.664, @Int.as.Copy.impl.Op(%int_32) [concrete] // CHECK:STDOUT: %int_1.5b8: Core.IntLiteral = int_value 1 [concrete] // CHECK:STDOUT: %ImplicitAs.type.cc7: type = generic_interface_type @ImplicitAs [concrete] // CHECK:STDOUT: %ImplicitAs.generic: %ImplicitAs.type.cc7 = struct_value () [concrete] // CHECK:STDOUT: %ImplicitAs.type.e8c: type = facet_type <@ImplicitAs, @ImplicitAs(%i32)> [concrete] // CHECK:STDOUT: %To: Core.IntLiteral = symbolic_binding To, 0 [symbolic] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.4e6: type = fn_type @Core.IntLiteral.as.ImplicitAs.impl.Convert, @Core.IntLiteral.as.ImplicitAs.impl(%To) [symbolic] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.3c2: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.4e6 = struct_value () [symbolic] // CHECK:STDOUT: %ImplicitAs.impl_witness.6bc: = impl_witness imports.%ImplicitAs.impl_witness_table.74f, @Core.IntLiteral.as.ImplicitAs.impl(%int_32) [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.e0d: type = fn_type @Core.IntLiteral.as.ImplicitAs.impl.Convert, @Core.IntLiteral.as.ImplicitAs.impl(%int_32) [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.e0d = struct_value () [concrete] // CHECK:STDOUT: %ImplicitAs.facet: %ImplicitAs.type.e8c = facet_value Core.IntLiteral, (%ImplicitAs.impl_witness.6bc) [concrete] // CHECK:STDOUT: %ImplicitAs.WithSelf.Convert.type.b37: type = fn_type @ImplicitAs.WithSelf.Convert, @ImplicitAs.WithSelf(%i32, %ImplicitAs.facet) [concrete] // CHECK:STDOUT: %.545: type = fn_type_with_self_type %ImplicitAs.WithSelf.Convert.type.b37, %ImplicitAs.facet [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.bound: = bound_method %int_1.5b8, %Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5 [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn: = specific_function %Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5, @Core.IntLiteral.as.ImplicitAs.impl.Convert(%int_32) [concrete] // CHECK:STDOUT: %bound_method: = bound_method %int_1.5b8, %Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn [concrete] // CHECK:STDOUT: %int_1.5d2: %i32 = int_value 1 [concrete] // CHECK:STDOUT: %ptr.f74: type = ptr_type %Derived [concrete] // CHECK:STDOUT: %pattern_type.0dd: type = pattern_type %ptr.f74 [concrete] // CHECK:STDOUT: %Call.type: type = fn_type @Call [concrete] // CHECK:STDOUT: %Call: %Call.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: .Int = %Core.Int // CHECK:STDOUT: .Copy = %Core.Copy // CHECK:STDOUT: .ImplicitAs = %Core.ImplicitAs // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: %Core.Int: %Int.type = import_ref Core//prelude/parts/int, Int, loaded [concrete = constants.%Int.generic] // CHECK:STDOUT: %Core.Copy: type = import_ref Core//prelude/parts/copy, Copy, loaded [concrete = constants.%Copy.type] // CHECK:STDOUT: %Core.import_ref.18d: @Int.as.Copy.impl.%Int.as.Copy.impl.Op.type (%Int.as.Copy.impl.Op.type.824) = import_ref Core//prelude/parts/int, loc{{\d+_\d+}}, loaded [symbolic = @Int.as.Copy.impl.%Int.as.Copy.impl.Op (constants.%Int.as.Copy.impl.Op.9b9)] // CHECK:STDOUT: %Copy.impl_witness_table.e76 = impl_witness_table (%Core.import_ref.18d), @Int.as.Copy.impl [concrete] // CHECK:STDOUT: %Core.ImplicitAs: %ImplicitAs.type.cc7 = import_ref Core//prelude/parts/as, ImplicitAs, loaded [concrete = constants.%ImplicitAs.generic] // CHECK:STDOUT: %Core.import_ref.42d: @Core.IntLiteral.as.ImplicitAs.impl.%Core.IntLiteral.as.ImplicitAs.impl.Convert.type (%Core.IntLiteral.as.ImplicitAs.impl.Convert.type.4e6) = import_ref Core//prelude/parts/int, loc{{\d+_\d+}}, loaded [symbolic = @Core.IntLiteral.as.ImplicitAs.impl.%Core.IntLiteral.as.ImplicitAs.impl.Convert (constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.3c2)] // CHECK:STDOUT: %ImplicitAs.impl_witness_table.74f = impl_witness_table (%Core.import_ref.42d), @Core.IntLiteral.as.ImplicitAs.impl [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .Base = %Base.decl // CHECK:STDOUT: .Derived = %Derived.decl // CHECK:STDOUT: .Call = %Call.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %Base.decl: type = class_decl @Base [concrete = constants.%Base] {} {} // CHECK:STDOUT: %Derived.decl: type = class_decl @Derived [concrete = constants.%Derived] {} {} // CHECK:STDOUT: %Derived.SelfBase.decl: %Derived.SelfBase.type = fn_decl @Derived.SelfBase [concrete = constants.%Derived.SelfBase] { // CHECK:STDOUT: %self.patt: %pattern_type.101 = value_binding_pattern self [concrete] // CHECK:STDOUT: %self.param_patt: %pattern_type.101 = value_param_pattern %self.patt [concrete] // CHECK:STDOUT: %return.patt: %pattern_type.7ce = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: %pattern_type.7ce = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %i32.loc26: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %.loc26: Core.Form = init_form %i32.loc26 [concrete = constants.%.ff5] // CHECK:STDOUT: %self.param.loc26: %Base = value_param call_param0 // CHECK:STDOUT: %Base.ref.loc26: type = name_ref Base, file.%Base.decl [concrete = constants.%Base] // CHECK:STDOUT: %self.loc26: %Base = value_binding self, %self.param.loc26 // CHECK:STDOUT: %return.param.loc26: ref %i32 = out_param call_param1 // CHECK:STDOUT: %return.loc26: ref %i32 = return_slot %return.param.loc26 // CHECK:STDOUT: } // CHECK:STDOUT: %Derived.RefSelfBase.decl: %Derived.RefSelfBase.type = fn_decl @Derived.RefSelfBase [concrete = constants.%Derived.RefSelfBase] { // CHECK:STDOUT: %self.patt: %pattern_type.101 = ref_binding_pattern self [concrete] // CHECK:STDOUT: %self.param_patt: %pattern_type.101 = ref_param_pattern %self.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %self.param.loc30: ref %Base = ref_param call_param0 // CHECK:STDOUT: %Base.ref.loc30: type = name_ref Base, file.%Base.decl [concrete = constants.%Base] // CHECK:STDOUT: %self.loc30: ref %Base = ref_binding self, %self.param.loc30 // CHECK:STDOUT: } // CHECK:STDOUT: %Call.decl: %Call.type = fn_decl @Call [concrete = constants.%Call] { // CHECK:STDOUT: %p.patt: %pattern_type.0dd = value_binding_pattern p [concrete] // CHECK:STDOUT: %p.param_patt: %pattern_type.0dd = value_param_pattern %p.patt [concrete] // CHECK:STDOUT: %return.patt: %pattern_type.7ce = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: %pattern_type.7ce = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %i32: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %.loc34_25: Core.Form = init_form %i32 [concrete = constants.%.ff5] // CHECK:STDOUT: %p.param: %ptr.f74 = value_param call_param0 // CHECK:STDOUT: %.loc34_19: type = splice_block %ptr [concrete = constants.%ptr.f74] { // CHECK:STDOUT: %Derived.ref: type = name_ref Derived, file.%Derived.decl [concrete = constants.%Derived] // CHECK:STDOUT: %ptr: type = ptr_type %Derived.ref [concrete = constants.%ptr.f74] // CHECK:STDOUT: } // CHECK:STDOUT: %p: %ptr.f74 = value_binding p, %p.param // CHECK:STDOUT: %return.param: ref %i32 = out_param call_param1 // CHECK:STDOUT: %return: ref %i32 = return_slot %return.param // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @Base { // CHECK:STDOUT: %i32: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %.loc16: %Base.elem = field_decl a, element0 [concrete] // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%struct_type.a [concrete = constants.%complete_type.fd7] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%Base // CHECK:STDOUT: .a = %.loc16 // CHECK:STDOUT: .Base = // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @Derived { // CHECK:STDOUT: %Base.ref: type = name_ref Base, file.%Base.decl [concrete = constants.%Base] // CHECK:STDOUT: %.loc20: %Derived.elem = base_decl %Base.ref, element0 [concrete] // CHECK:STDOUT: %Derived.SelfBase.decl: %Derived.SelfBase.type = fn_decl @Derived.SelfBase [concrete = constants.%Derived.SelfBase] { // CHECK:STDOUT: %self.patt: %pattern_type.101 = value_binding_pattern self [concrete] // CHECK:STDOUT: %self.param_patt: %pattern_type.101 = value_param_pattern %self.patt [concrete] // CHECK:STDOUT: %return.patt: %pattern_type.7ce = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: %pattern_type.7ce = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %i32.loc22: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %.loc22: Core.Form = init_form %i32.loc22 [concrete = constants.%.ff5] // CHECK:STDOUT: %self.param.loc22: %Base = value_param call_param0 // CHECK:STDOUT: %Base.ref.loc22: type = name_ref Base, file.%Base.decl [concrete = constants.%Base] // CHECK:STDOUT: %self.loc22: %Base = value_binding self, %self.param.loc22 // CHECK:STDOUT: %return.param.loc22: ref %i32 = out_param call_param1 // CHECK:STDOUT: %return.loc22: ref %i32 = return_slot %return.param.loc22 // CHECK:STDOUT: } // CHECK:STDOUT: %Derived.RefSelfBase.decl: %Derived.RefSelfBase.type = fn_decl @Derived.RefSelfBase [concrete = constants.%Derived.RefSelfBase] { // CHECK:STDOUT: %self.patt: %pattern_type.101 = ref_binding_pattern self [concrete] // CHECK:STDOUT: %self.param_patt: %pattern_type.101 = ref_param_pattern %self.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %self.param.loc23: ref %Base = ref_param call_param0 // CHECK:STDOUT: %Base.ref.loc23: type = name_ref Base, file.%Base.decl [concrete = constants.%Base] // CHECK:STDOUT: %self.loc23: ref %Base = ref_binding self, %self.param.loc23 // CHECK:STDOUT: } // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%struct_type.base.27a [concrete = constants.%complete_type.5a1] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%Derived // CHECK:STDOUT: .Base = // CHECK:STDOUT: .base = %.loc20 // CHECK:STDOUT: .SelfBase = %Derived.SelfBase.decl // CHECK:STDOUT: .RefSelfBase = %Derived.RefSelfBase.decl // CHECK:STDOUT: extend %Base.ref // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @Derived.SelfBase(%self.param.loc26: %Base) -> out %return.param.loc26: %i32 { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %self.ref: %Base = name_ref self, %self.loc26 // CHECK:STDOUT: %a.ref: %Base.elem = name_ref a, @Base.%.loc16 [concrete = @Base.%.loc16] // CHECK:STDOUT: %.loc27_14.1: ref %i32 = class_element_access %self.ref, element0 // CHECK:STDOUT: %.loc27_14.2: %i32 = acquire_value %.loc27_14.1 // CHECK:STDOUT: %impl.elem0: %.8e2 = impl_witness_access constants.%Copy.impl_witness.f17, element0 [concrete = constants.%Int.as.Copy.impl.Op.664] // CHECK:STDOUT: %bound_method.loc27_14.1: = bound_method %.loc27_14.2, %impl.elem0 // CHECK:STDOUT: %specific_fn: = specific_function %impl.elem0, @Int.as.Copy.impl.Op(constants.%int_32) [concrete = constants.%Int.as.Copy.impl.Op.specific_fn] // CHECK:STDOUT: %bound_method.loc27_14.2: = bound_method %.loc27_14.2, %specific_fn // CHECK:STDOUT: %Int.as.Copy.impl.Op.call: init %i32 = call %bound_method.loc27_14.2(%.loc27_14.2) // CHECK:STDOUT: return %Int.as.Copy.impl.Op.call // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @Derived.RefSelfBase(%self.param.loc30: ref %Base) { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %self.ref: ref %Base = name_ref self, %self.loc30 // CHECK:STDOUT: %a.ref: %Base.elem = name_ref a, @Base.%.loc16 [concrete = @Base.%.loc16] // CHECK:STDOUT: %.loc31_7: ref %i32 = class_element_access %self.ref, element0 // CHECK:STDOUT: %int_1: Core.IntLiteral = int_value 1 [concrete = constants.%int_1.5b8] // CHECK:STDOUT: %impl.elem0: %.545 = impl_witness_access constants.%ImplicitAs.impl_witness.6bc, element0 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5] // CHECK:STDOUT: %bound_method.loc31_10.1: = bound_method %int_1, %impl.elem0 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.bound] // CHECK:STDOUT: %specific_fn: = specific_function %impl.elem0, @Core.IntLiteral.as.ImplicitAs.impl.Convert(constants.%int_32) [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn] // CHECK:STDOUT: %bound_method.loc31_10.2: = bound_method %int_1, %specific_fn [concrete = constants.%bound_method] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.call: init %i32 = call %bound_method.loc31_10.2(%int_1) [concrete = constants.%int_1.5d2] // CHECK:STDOUT: %.loc31_10: init %i32 = converted %int_1, %Core.IntLiteral.as.ImplicitAs.impl.Convert.call [concrete = constants.%int_1.5d2] // CHECK:STDOUT: assign %.loc31_7, %.loc31_10 // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @Call(%p.param: %ptr.f74) -> out %return.param: %i32 { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %p.ref.loc35: %ptr.f74 = name_ref p, %p // CHECK:STDOUT: %.loc35_4.1: ref %Derived = deref %p.ref.loc35 // CHECK:STDOUT: %RefSelfBase.ref: %Derived.RefSelfBase.type = name_ref RefSelfBase, @Derived.%Derived.RefSelfBase.decl [concrete = constants.%Derived.RefSelfBase] // CHECK:STDOUT: %Derived.RefSelfBase.bound: = bound_method %.loc35_4.1, %RefSelfBase.ref // CHECK:STDOUT: %.loc35_4.2: ref %Base = class_element_access %.loc35_4.1, element0 // CHECK:STDOUT: %.loc35_4.3: ref %Base = converted %.loc35_4.1, %.loc35_4.2 // CHECK:STDOUT: %Derived.RefSelfBase.call: init %empty_tuple.type = call %Derived.RefSelfBase.bound(%.loc35_4.3) // CHECK:STDOUT: %p.ref.loc36: %ptr.f74 = name_ref p, %p // CHECK:STDOUT: %.loc36_11.1: ref %Derived = deref %p.ref.loc36 // CHECK:STDOUT: %SelfBase.ref: %Derived.SelfBase.type = name_ref SelfBase, @Derived.%Derived.SelfBase.decl [concrete = constants.%Derived.SelfBase] // CHECK:STDOUT: %Derived.SelfBase.bound: = bound_method %.loc36_11.1, %SelfBase.ref // CHECK:STDOUT: %.loc36_11.2: ref %Base = class_element_access %.loc36_11.1, element0 // CHECK:STDOUT: %.loc36_11.3: ref %Base = converted %.loc36_11.1, %.loc36_11.2 // CHECK:STDOUT: %.loc36_11.4: %Base = acquire_value %.loc36_11.3 // CHECK:STDOUT: %Derived.SelfBase.call: init %i32 = call %Derived.SelfBase.bound(%.loc36_11.4) // CHECK:STDOUT: return %Derived.SelfBase.call // CHECK:STDOUT: } // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/class/init.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/int.carbon // TODO: Add ranges and switch to "--dump-sem-ir-ranges=only". // EXTRA-ARGS: --dump-sem-ir-ranges=if-present // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/class/init.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/class/init.carbon class Class { var n: i32; var next: Class*; } fn Make(n: i32, next: Class*) -> Class { return {.n = n, .next = next}; } fn MakeReorder(n: i32, next: Class*) -> Class { return {.next = next, .n = n}; } // CHECK:STDOUT: --- init.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %Class: type = class_type @Class [concrete] // CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [concrete] // CHECK:STDOUT: %Int.type: type = generic_class_type @Int [concrete] // CHECK:STDOUT: %Int.generic: %Int.type = struct_value () [concrete] // CHECK:STDOUT: %N: Core.IntLiteral = symbolic_binding N, 0 [symbolic] // CHECK:STDOUT: %i32: type = class_type @Int, @Int(%int_32) [concrete] // CHECK:STDOUT: %Class.elem.762: type = unbound_element_type %Class, %i32 [concrete] // CHECK:STDOUT: %ptr.8e5: type = ptr_type %Class [concrete] // CHECK:STDOUT: %Class.elem.f74: type = unbound_element_type %Class, %ptr.8e5 [concrete] // CHECK:STDOUT: %struct_type.n.next: type = struct_type {.n: %i32, .next: %ptr.8e5} [concrete] // CHECK:STDOUT: %complete_type.bf0: = complete_type_witness %struct_type.n.next [concrete] // CHECK:STDOUT: %pattern_type.7ce: type = pattern_type %i32 [concrete] // CHECK:STDOUT: %pattern_type.018: type = pattern_type %ptr.8e5 [concrete] // CHECK:STDOUT: %.cff: Core.Form = init_form %Class [concrete] // CHECK:STDOUT: %pattern_type.904: type = pattern_type %Class [concrete] // CHECK:STDOUT: %Make.type: type = fn_type @Make [concrete] // CHECK:STDOUT: %Make: %Make.type = struct_value () [concrete] // CHECK:STDOUT: %Copy.type: type = facet_type <@Copy> [concrete] // CHECK:STDOUT: %Int.as.Copy.impl.Op.type.824: type = fn_type @Int.as.Copy.impl.Op, @Int.as.Copy.impl(%N) [symbolic] // CHECK:STDOUT: %Int.as.Copy.impl.Op.9b9: %Int.as.Copy.impl.Op.type.824 = struct_value () [symbolic] // CHECK:STDOUT: %T.67d: type = symbolic_binding T, 0 [symbolic] // CHECK:STDOUT: %ptr.as.Copy.impl.Op.type.2d4: type = fn_type @ptr.as.Copy.impl.Op, @ptr.as.Copy.impl(%T.67d) [symbolic] // CHECK:STDOUT: %ptr.as.Copy.impl.Op.74e: %ptr.as.Copy.impl.Op.type.2d4 = struct_value () [symbolic] // CHECK:STDOUT: %Copy.impl_witness.f17: = impl_witness imports.%Copy.impl_witness_table.e76, @Int.as.Copy.impl(%int_32) [concrete] // CHECK:STDOUT: %Int.as.Copy.impl.Op.type.546: type = fn_type @Int.as.Copy.impl.Op, @Int.as.Copy.impl(%int_32) [concrete] // CHECK:STDOUT: %Int.as.Copy.impl.Op.664: %Int.as.Copy.impl.Op.type.546 = struct_value () [concrete] // CHECK:STDOUT: %Copy.facet.de4: %Copy.type = facet_value %i32, (%Copy.impl_witness.f17) [concrete] // CHECK:STDOUT: %Copy.WithSelf.Op.type.081: type = fn_type @Copy.WithSelf.Op, @Copy.WithSelf(%Copy.facet.de4) [concrete] // CHECK:STDOUT: %.8e2: type = fn_type_with_self_type %Copy.WithSelf.Op.type.081, %Copy.facet.de4 [concrete] // CHECK:STDOUT: %Int.as.Copy.impl.Op.specific_fn: = specific_function %Int.as.Copy.impl.Op.664, @Int.as.Copy.impl.Op(%int_32) [concrete] // CHECK:STDOUT: %Copy.impl_witness.9d3: = impl_witness imports.%Copy.impl_witness_table.c3a, @ptr.as.Copy.impl(%Class) [concrete] // CHECK:STDOUT: %ptr.as.Copy.impl.Op.type.02e: type = fn_type @ptr.as.Copy.impl.Op, @ptr.as.Copy.impl(%Class) [concrete] // CHECK:STDOUT: %ptr.as.Copy.impl.Op.120: %ptr.as.Copy.impl.Op.type.02e = struct_value () [concrete] // CHECK:STDOUT: %Copy.facet.734: %Copy.type = facet_value %ptr.8e5, (%Copy.impl_witness.9d3) [concrete] // CHECK:STDOUT: %Copy.WithSelf.Op.type.130: type = fn_type @Copy.WithSelf.Op, @Copy.WithSelf(%Copy.facet.734) [concrete] // CHECK:STDOUT: %.370: type = fn_type_with_self_type %Copy.WithSelf.Op.type.130, %Copy.facet.734 [concrete] // CHECK:STDOUT: %ptr.as.Copy.impl.Op.specific_fn: = specific_function %ptr.as.Copy.impl.Op.120, @ptr.as.Copy.impl.Op(%Class) [concrete] // CHECK:STDOUT: %MakeReorder.type: type = fn_type @MakeReorder [concrete] // CHECK:STDOUT: %MakeReorder: %MakeReorder.type = struct_value () [concrete] // CHECK:STDOUT: %struct_type.next.n: type = struct_type {.next: %ptr.8e5, .n: %i32} [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: .Int = %Core.Int // CHECK:STDOUT: .Copy = %Core.Copy // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: %Core.Int: %Int.type = import_ref Core//prelude/parts/int, Int, loaded [concrete = constants.%Int.generic] // CHECK:STDOUT: %Core.Copy: type = import_ref Core//prelude/parts/copy, Copy, loaded [concrete = constants.%Copy.type] // CHECK:STDOUT: %Core.import_ref.18d: @Int.as.Copy.impl.%Int.as.Copy.impl.Op.type (%Int.as.Copy.impl.Op.type.824) = import_ref Core//prelude/parts/int, loc{{\d+_\d+}}, loaded [symbolic = @Int.as.Copy.impl.%Int.as.Copy.impl.Op (constants.%Int.as.Copy.impl.Op.9b9)] // CHECK:STDOUT: %Copy.impl_witness_table.e76 = impl_witness_table (%Core.import_ref.18d), @Int.as.Copy.impl [concrete] // CHECK:STDOUT: %Core.import_ref.203: @ptr.as.Copy.impl.%ptr.as.Copy.impl.Op.type (%ptr.as.Copy.impl.Op.type.2d4) = import_ref Core//prelude/parts/copy, loc{{\d+_\d+}}, loaded [symbolic = @ptr.as.Copy.impl.%ptr.as.Copy.impl.Op (constants.%ptr.as.Copy.impl.Op.74e)] // CHECK:STDOUT: %Copy.impl_witness_table.c3a = impl_witness_table (%Core.import_ref.203), @ptr.as.Copy.impl [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .Class = %Class.decl // CHECK:STDOUT: .Make = %Make.decl // CHECK:STDOUT: .MakeReorder = %MakeReorder.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %Class.decl: type = class_decl @Class [concrete = constants.%Class] {} {} // CHECK:STDOUT: %Make.decl: %Make.type = fn_decl @Make [concrete = constants.%Make] { // CHECK:STDOUT: %n.patt: %pattern_type.7ce = value_binding_pattern n [concrete] // CHECK:STDOUT: %n.param_patt: %pattern_type.7ce = value_param_pattern %n.patt [concrete] // CHECK:STDOUT: %next.patt: %pattern_type.018 = value_binding_pattern next [concrete] // CHECK:STDOUT: %next.param_patt: %pattern_type.018 = value_param_pattern %next.patt [concrete] // CHECK:STDOUT: %return.patt: %pattern_type.904 = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: %pattern_type.904 = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %Class.ref.loc20_34: type = name_ref Class, file.%Class.decl [concrete = constants.%Class] // CHECK:STDOUT: %.loc20_34: Core.Form = init_form %Class.ref.loc20_34 [concrete = constants.%.cff] // CHECK:STDOUT: %n.param: %i32 = value_param call_param0 // CHECK:STDOUT: %i32: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %n: %i32 = value_binding n, %n.param // CHECK:STDOUT: %next.param: %ptr.8e5 = value_param call_param1 // CHECK:STDOUT: %.loc20_28: type = splice_block %ptr [concrete = constants.%ptr.8e5] { // CHECK:STDOUT: %Class.ref.loc20_23: type = name_ref Class, file.%Class.decl [concrete = constants.%Class] // CHECK:STDOUT: %ptr: type = ptr_type %Class.ref.loc20_23 [concrete = constants.%ptr.8e5] // CHECK:STDOUT: } // CHECK:STDOUT: %next: %ptr.8e5 = value_binding next, %next.param // CHECK:STDOUT: %return.param: ref %Class = out_param call_param2 // CHECK:STDOUT: %return: ref %Class = return_slot %return.param // CHECK:STDOUT: } // CHECK:STDOUT: %MakeReorder.decl: %MakeReorder.type = fn_decl @MakeReorder [concrete = constants.%MakeReorder] { // CHECK:STDOUT: %n.patt: %pattern_type.7ce = value_binding_pattern n [concrete] // CHECK:STDOUT: %n.param_patt: %pattern_type.7ce = value_param_pattern %n.patt [concrete] // CHECK:STDOUT: %next.patt: %pattern_type.018 = value_binding_pattern next [concrete] // CHECK:STDOUT: %next.param_patt: %pattern_type.018 = value_param_pattern %next.patt [concrete] // CHECK:STDOUT: %return.patt: %pattern_type.904 = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: %pattern_type.904 = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %Class.ref.loc24_41: type = name_ref Class, file.%Class.decl [concrete = constants.%Class] // CHECK:STDOUT: %.loc24_41: Core.Form = init_form %Class.ref.loc24_41 [concrete = constants.%.cff] // CHECK:STDOUT: %n.param: %i32 = value_param call_param0 // CHECK:STDOUT: %i32: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %n: %i32 = value_binding n, %n.param // CHECK:STDOUT: %next.param: %ptr.8e5 = value_param call_param1 // CHECK:STDOUT: %.loc24_35: type = splice_block %ptr [concrete = constants.%ptr.8e5] { // CHECK:STDOUT: %Class.ref.loc24_30: type = name_ref Class, file.%Class.decl [concrete = constants.%Class] // CHECK:STDOUT: %ptr: type = ptr_type %Class.ref.loc24_30 [concrete = constants.%ptr.8e5] // CHECK:STDOUT: } // CHECK:STDOUT: %next: %ptr.8e5 = value_binding next, %next.param // CHECK:STDOUT: %return.param: ref %Class = out_param call_param2 // CHECK:STDOUT: %return: ref %Class = return_slot %return.param // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @Class { // CHECK:STDOUT: %i32: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %.loc16: %Class.elem.762 = field_decl n, element0 [concrete] // CHECK:STDOUT: %Class.ref: type = name_ref Class, file.%Class.decl [concrete = constants.%Class] // CHECK:STDOUT: %ptr: type = ptr_type %Class.ref [concrete = constants.%ptr.8e5] // CHECK:STDOUT: %.loc17: %Class.elem.f74 = field_decl next, element1 [concrete] // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%struct_type.n.next [concrete = constants.%complete_type.bf0] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%Class // CHECK:STDOUT: .n = %.loc16 // CHECK:STDOUT: .Class = // CHECK:STDOUT: .next = %.loc17 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @Make(%n.param: %i32, %next.param: %ptr.8e5) -> out %return.param: %Class { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %n.ref: %i32 = name_ref n, %n // CHECK:STDOUT: %next.ref: %ptr.8e5 = name_ref next, %next // CHECK:STDOUT: %.loc21_31.1: %struct_type.n.next = struct_literal (%n.ref, %next.ref) // CHECK:STDOUT: %impl.elem0.loc21_16: %.8e2 = impl_witness_access constants.%Copy.impl_witness.f17, element0 [concrete = constants.%Int.as.Copy.impl.Op.664] // CHECK:STDOUT: %bound_method.loc21_16.1: = bound_method %n.ref, %impl.elem0.loc21_16 // CHECK:STDOUT: %specific_fn.loc21_16: = specific_function %impl.elem0.loc21_16, @Int.as.Copy.impl.Op(constants.%int_32) [concrete = constants.%Int.as.Copy.impl.Op.specific_fn] // CHECK:STDOUT: %bound_method.loc21_16.2: = bound_method %n.ref, %specific_fn.loc21_16 // CHECK:STDOUT: %Int.as.Copy.impl.Op.call: init %i32 = call %bound_method.loc21_16.2(%n.ref) // CHECK:STDOUT: %.loc21_31.2: ref %i32 = class_element_access %return.param, element0 // CHECK:STDOUT: %.loc21_31.3: init %i32 to %.loc21_31.2 = in_place_init %Int.as.Copy.impl.Op.call // CHECK:STDOUT: %impl.elem0.loc21_27: %.370 = impl_witness_access constants.%Copy.impl_witness.9d3, element0 [concrete = constants.%ptr.as.Copy.impl.Op.120] // CHECK:STDOUT: %bound_method.loc21_27.1: = bound_method %next.ref, %impl.elem0.loc21_27 // CHECK:STDOUT: %specific_fn.loc21_27: = specific_function %impl.elem0.loc21_27, @ptr.as.Copy.impl.Op(constants.%Class) [concrete = constants.%ptr.as.Copy.impl.Op.specific_fn] // CHECK:STDOUT: %bound_method.loc21_27.2: = bound_method %next.ref, %specific_fn.loc21_27 // CHECK:STDOUT: %ptr.as.Copy.impl.Op.call: init %ptr.8e5 = call %bound_method.loc21_27.2(%next.ref) // CHECK:STDOUT: %.loc21_31.4: ref %ptr.8e5 = class_element_access %return.param, element1 // CHECK:STDOUT: %.loc21_31.5: init %ptr.8e5 to %.loc21_31.4 = in_place_init %ptr.as.Copy.impl.Op.call // CHECK:STDOUT: %.loc21_31.6: init %Class to %return.param = class_init (%.loc21_31.3, %.loc21_31.5) // CHECK:STDOUT: %.loc21_32: init %Class = converted %.loc21_31.1, %.loc21_31.6 // CHECK:STDOUT: return %.loc21_32 to %return.param // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @MakeReorder(%n.param: %i32, %next.param: %ptr.8e5) -> out %return.param: %Class { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %next.ref: %ptr.8e5 = name_ref next, %next // CHECK:STDOUT: %n.ref: %i32 = name_ref n, %n // CHECK:STDOUT: %.loc25_31.1: %struct_type.next.n = struct_literal (%next.ref, %n.ref) // CHECK:STDOUT: %impl.elem0.loc25_30: %.8e2 = impl_witness_access constants.%Copy.impl_witness.f17, element0 [concrete = constants.%Int.as.Copy.impl.Op.664] // CHECK:STDOUT: %bound_method.loc25_30.1: = bound_method %n.ref, %impl.elem0.loc25_30 // CHECK:STDOUT: %specific_fn.loc25_30: = specific_function %impl.elem0.loc25_30, @Int.as.Copy.impl.Op(constants.%int_32) [concrete = constants.%Int.as.Copy.impl.Op.specific_fn] // CHECK:STDOUT: %bound_method.loc25_30.2: = bound_method %n.ref, %specific_fn.loc25_30 // CHECK:STDOUT: %Int.as.Copy.impl.Op.call: init %i32 = call %bound_method.loc25_30.2(%n.ref) // CHECK:STDOUT: %.loc25_31.2: ref %i32 = class_element_access %return.param, element1 // CHECK:STDOUT: %.loc25_31.3: init %i32 to %.loc25_31.2 = in_place_init %Int.as.Copy.impl.Op.call // CHECK:STDOUT: %impl.elem0.loc25_19: %.370 = impl_witness_access constants.%Copy.impl_witness.9d3, element0 [concrete = constants.%ptr.as.Copy.impl.Op.120] // CHECK:STDOUT: %bound_method.loc25_19.1: = bound_method %next.ref, %impl.elem0.loc25_19 // CHECK:STDOUT: %specific_fn.loc25_19: = specific_function %impl.elem0.loc25_19, @ptr.as.Copy.impl.Op(constants.%Class) [concrete = constants.%ptr.as.Copy.impl.Op.specific_fn] // CHECK:STDOUT: %bound_method.loc25_19.2: = bound_method %next.ref, %specific_fn.loc25_19 // CHECK:STDOUT: %ptr.as.Copy.impl.Op.call: init %ptr.8e5 = call %bound_method.loc25_19.2(%next.ref) // CHECK:STDOUT: %.loc25_31.4: ref %ptr.8e5 = class_element_access %return.param, element0 // CHECK:STDOUT: %.loc25_31.5: init %ptr.8e5 to %.loc25_31.4 = in_place_init %ptr.as.Copy.impl.Op.call // CHECK:STDOUT: %.loc25_31.6: init %Class to %return.param = class_init (%.loc25_31.3, %.loc25_31.5) // CHECK:STDOUT: %.loc25_32: init %Class = converted %.loc25_31.1, %.loc25_31.6 // CHECK:STDOUT: return %.loc25_32 to %return.param // CHECK:STDOUT: } // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/class/init_as.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/int.carbon // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/class/init_as.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/class/init_as.carbon class Class { var a: i32; var b: i32; } fn F() -> i32 { //@dump-sem-ir-begin return ({.a = 1, .b = 2} as Class).a; //@dump-sem-ir-end } fn G() -> i32 { //@dump-sem-ir-begin var v: Class = {.a = 1, .b = 2} as Class; //@dump-sem-ir-end return v.a; } // CHECK:STDOUT: --- init_as.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %Class: type = class_type @Class [concrete] // CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [concrete] // CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] // CHECK:STDOUT: %N: Core.IntLiteral = symbolic_binding N, 0 [symbolic] // CHECK:STDOUT: %i32: type = class_type @Int, @Int(%int_32) [concrete] // CHECK:STDOUT: %Class.elem: type = unbound_element_type %Class, %i32 [concrete] // CHECK:STDOUT: %int_1.5b8: Core.IntLiteral = int_value 1 [concrete] // CHECK:STDOUT: %int_2.ecc: Core.IntLiteral = int_value 2 [concrete] // CHECK:STDOUT: %struct_type.a.b.cfd: type = struct_type {.a: Core.IntLiteral, .b: Core.IntLiteral} [concrete] // CHECK:STDOUT: %struct: %struct_type.a.b.cfd = struct_value (%int_1.5b8, %int_2.ecc) [concrete] // CHECK:STDOUT: %ImplicitAs.type.e8c: type = facet_type <@ImplicitAs, @ImplicitAs(%i32)> [concrete] // CHECK:STDOUT: %To: Core.IntLiteral = symbolic_binding To, 0 [symbolic] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.4e6: type = fn_type @Core.IntLiteral.as.ImplicitAs.impl.Convert, @Core.IntLiteral.as.ImplicitAs.impl(%To) [symbolic] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.3c2: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.4e6 = struct_value () [symbolic] // CHECK:STDOUT: %ImplicitAs.impl_witness.6bc: = impl_witness imports.%ImplicitAs.impl_witness_table.74f, @Core.IntLiteral.as.ImplicitAs.impl(%int_32) [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.e0d: type = fn_type @Core.IntLiteral.as.ImplicitAs.impl.Convert, @Core.IntLiteral.as.ImplicitAs.impl(%int_32) [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.e0d = struct_value () [concrete] // CHECK:STDOUT: %ImplicitAs.facet: %ImplicitAs.type.e8c = facet_value Core.IntLiteral, (%ImplicitAs.impl_witness.6bc) [concrete] // CHECK:STDOUT: %ImplicitAs.WithSelf.Convert.type.b37: type = fn_type @ImplicitAs.WithSelf.Convert, @ImplicitAs.WithSelf(%i32, %ImplicitAs.facet) [concrete] // CHECK:STDOUT: %.545: type = fn_type_with_self_type %ImplicitAs.WithSelf.Convert.type.b37, %ImplicitAs.facet [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.bound.215: = bound_method %int_1.5b8, %Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5 [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn: = specific_function %Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5, @Core.IntLiteral.as.ImplicitAs.impl.Convert(%int_32) [concrete] // CHECK:STDOUT: %bound_method.38b: = bound_method %int_1.5b8, %Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn [concrete] // CHECK:STDOUT: %int_1.5d2: %i32 = int_value 1 [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.bound.4e5: = bound_method %int_2.ecc, %Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5 [concrete] // CHECK:STDOUT: %bound_method.646: = bound_method %int_2.ecc, %Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn [concrete] // CHECK:STDOUT: %int_2.ef8: %i32 = int_value 2 [concrete] // CHECK:STDOUT: %Class.val: %Class = struct_value (%int_1.5d2, %int_2.ef8) [concrete] // CHECK:STDOUT: %Copy.type: type = facet_type <@Copy> [concrete] // CHECK:STDOUT: %Int.as.Copy.impl.Op.type.824: type = fn_type @Int.as.Copy.impl.Op, @Int.as.Copy.impl(%N) [symbolic] // CHECK:STDOUT: %Int.as.Copy.impl.Op.9b9: %Int.as.Copy.impl.Op.type.824 = struct_value () [symbolic] // CHECK:STDOUT: %Copy.impl_witness.f17: = impl_witness imports.%Copy.impl_witness_table.e76, @Int.as.Copy.impl(%int_32) [concrete] // CHECK:STDOUT: %Int.as.Copy.impl.Op.type.546: type = fn_type @Int.as.Copy.impl.Op, @Int.as.Copy.impl(%int_32) [concrete] // CHECK:STDOUT: %Int.as.Copy.impl.Op.664: %Int.as.Copy.impl.Op.type.546 = struct_value () [concrete] // CHECK:STDOUT: %Copy.facet: %Copy.type = facet_value %i32, (%Copy.impl_witness.f17) [concrete] // CHECK:STDOUT: %Copy.WithSelf.Op.type.081: type = fn_type @Copy.WithSelf.Op, @Copy.WithSelf(%Copy.facet) [concrete] // CHECK:STDOUT: %.8e2: type = fn_type_with_self_type %Copy.WithSelf.Op.type.081, %Copy.facet [concrete] // CHECK:STDOUT: %Int.as.Copy.impl.Op.specific_fn: = specific_function %Int.as.Copy.impl.Op.664, @Int.as.Copy.impl.Op(%int_32) [concrete] // CHECK:STDOUT: %pattern_type.904: type = pattern_type %Class [concrete] // CHECK:STDOUT: %Destroy.Op.type: type = fn_type @Destroy.Op [concrete] // CHECK:STDOUT: %Destroy.Op: %Destroy.Op.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core.import_ref.42d: @Core.IntLiteral.as.ImplicitAs.impl.%Core.IntLiteral.as.ImplicitAs.impl.Convert.type (%Core.IntLiteral.as.ImplicitAs.impl.Convert.type.4e6) = import_ref Core//prelude/parts/int, loc{{\d+_\d+}}, loaded [symbolic = @Core.IntLiteral.as.ImplicitAs.impl.%Core.IntLiteral.as.ImplicitAs.impl.Convert (constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.3c2)] // CHECK:STDOUT: %ImplicitAs.impl_witness_table.74f = impl_witness_table (%Core.import_ref.42d), @Core.IntLiteral.as.ImplicitAs.impl [concrete] // CHECK:STDOUT: %Core.import_ref.18d: @Int.as.Copy.impl.%Int.as.Copy.impl.Op.type (%Int.as.Copy.impl.Op.type.824) = import_ref Core//prelude/parts/int, loc{{\d+_\d+}}, loaded [symbolic = @Int.as.Copy.impl.%Int.as.Copy.impl.Op (constants.%Int.as.Copy.impl.Op.9b9)] // CHECK:STDOUT: %Copy.impl_witness_table.e76 = impl_witness_table (%Core.import_ref.18d), @Int.as.Copy.impl [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @F() -> out %return.param: %i32 { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %int_1: Core.IntLiteral = int_value 1 [concrete = constants.%int_1.5b8] // CHECK:STDOUT: %int_2: Core.IntLiteral = int_value 2 [concrete = constants.%int_2.ecc] // CHECK:STDOUT: %.loc20_26.1: %struct_type.a.b.cfd = struct_literal (%int_1, %int_2) [concrete = constants.%struct] // CHECK:STDOUT: %Class.ref: type = name_ref Class, file.%Class.decl [concrete = constants.%Class] // CHECK:STDOUT: %impl.elem0.loc20_26.1: %.545 = impl_witness_access constants.%ImplicitAs.impl_witness.6bc, element0 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5] // CHECK:STDOUT: %bound_method.loc20_26.1: = bound_method %int_1, %impl.elem0.loc20_26.1 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.bound.215] // CHECK:STDOUT: %specific_fn.loc20_26.1: = specific_function %impl.elem0.loc20_26.1, @Core.IntLiteral.as.ImplicitAs.impl.Convert(constants.%int_32) [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn] // CHECK:STDOUT: %bound_method.loc20_26.2: = bound_method %int_1, %specific_fn.loc20_26.1 [concrete = constants.%bound_method.38b] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc20_26.1: init %i32 = call %bound_method.loc20_26.2(%int_1) [concrete = constants.%int_1.5d2] // CHECK:STDOUT: %.loc20_26.2: init %i32 = converted %int_1, %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc20_26.1 [concrete = constants.%int_1.5d2] // CHECK:STDOUT: %.loc20_26.3: ref %Class = temporary_storage // CHECK:STDOUT: %.loc20_26.4: ref %i32 = class_element_access %.loc20_26.3, element0 // CHECK:STDOUT: %.loc20_26.5: init %i32 to %.loc20_26.4 = in_place_init %.loc20_26.2 [concrete = constants.%int_1.5d2] // CHECK:STDOUT: %impl.elem0.loc20_26.2: %.545 = impl_witness_access constants.%ImplicitAs.impl_witness.6bc, element0 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5] // CHECK:STDOUT: %bound_method.loc20_26.3: = bound_method %int_2, %impl.elem0.loc20_26.2 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.bound.4e5] // CHECK:STDOUT: %specific_fn.loc20_26.2: = specific_function %impl.elem0.loc20_26.2, @Core.IntLiteral.as.ImplicitAs.impl.Convert(constants.%int_32) [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn] // CHECK:STDOUT: %bound_method.loc20_26.4: = bound_method %int_2, %specific_fn.loc20_26.2 [concrete = constants.%bound_method.646] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc20_26.2: init %i32 = call %bound_method.loc20_26.4(%int_2) [concrete = constants.%int_2.ef8] // CHECK:STDOUT: %.loc20_26.6: init %i32 = converted %int_2, %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc20_26.2 [concrete = constants.%int_2.ef8] // CHECK:STDOUT: %.loc20_26.7: ref %i32 = class_element_access %.loc20_26.3, element1 // CHECK:STDOUT: %.loc20_26.8: init %i32 to %.loc20_26.7 = in_place_init %.loc20_26.6 [concrete = constants.%int_2.ef8] // CHECK:STDOUT: %.loc20_26.9: init %Class to %.loc20_26.3 = class_init (%.loc20_26.5, %.loc20_26.8) [concrete = constants.%Class.val] // CHECK:STDOUT: %.loc20_28.1: init %Class = converted %.loc20_26.1, %.loc20_26.9 [concrete = constants.%Class.val] // CHECK:STDOUT: %.loc20_28.2: ref %Class = temporary %.loc20_26.3, %.loc20_28.1 // CHECK:STDOUT: %a.ref: %Class.elem = name_ref a, @Class.%.loc14 [concrete = @Class.%.loc14] // CHECK:STDOUT: %.loc20_37.1: ref %i32 = class_element_access %.loc20_28.2, element0 // CHECK:STDOUT: %.loc20_37.2: %i32 = acquire_value %.loc20_37.1 // CHECK:STDOUT: %impl.elem0.loc20_37: %.8e2 = impl_witness_access constants.%Copy.impl_witness.f17, element0 [concrete = constants.%Int.as.Copy.impl.Op.664] // CHECK:STDOUT: %bound_method.loc20_37.1: = bound_method %.loc20_37.2, %impl.elem0.loc20_37 // CHECK:STDOUT: %specific_fn.loc20_37: = specific_function %impl.elem0.loc20_37, @Int.as.Copy.impl.Op(constants.%int_32) [concrete = constants.%Int.as.Copy.impl.Op.specific_fn] // CHECK:STDOUT: %bound_method.loc20_37.2: = bound_method %.loc20_37.2, %specific_fn.loc20_37 // CHECK:STDOUT: %Int.as.Copy.impl.Op.call: init %i32 = call %bound_method.loc20_37.2(%.loc20_37.2) // CHECK:STDOUT: %Destroy.Op.bound: = bound_method %.loc20_28.2, constants.%Destroy.Op // CHECK:STDOUT: %Destroy.Op.call: init %empty_tuple.type = call %Destroy.Op.bound(%.loc20_28.2) // CHECK:STDOUT: return %Int.as.Copy.impl.Op.call // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @Destroy.Op(%self.param: ref %Class) = "no_op"; // CHECK:STDOUT: // CHECK:STDOUT: fn @G() -> out %return.param: %i32 { // CHECK:STDOUT: !entry: // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %v.patt: %pattern_type.904 = ref_binding_pattern v [concrete] // CHECK:STDOUT: %v.var_patt: %pattern_type.904 = var_pattern %v.patt [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %v.var: ref %Class = var %v.var_patt // CHECK:STDOUT: %int_1: Core.IntLiteral = int_value 1 [concrete = constants.%int_1.5b8] // CHECK:STDOUT: %int_2: Core.IntLiteral = int_value 2 [concrete = constants.%int_2.ecc] // CHECK:STDOUT: %.loc26_33.1: %struct_type.a.b.cfd = struct_literal (%int_1, %int_2) [concrete = constants.%struct] // CHECK:STDOUT: %Class.ref.loc26_38: type = name_ref Class, file.%Class.decl [concrete = constants.%Class] // CHECK:STDOUT: %impl.elem0.loc26_33.1: %.545 = impl_witness_access constants.%ImplicitAs.impl_witness.6bc, element0 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5] // CHECK:STDOUT: %bound_method.loc26_33.1: = bound_method %int_1, %impl.elem0.loc26_33.1 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.bound.215] // CHECK:STDOUT: %specific_fn.loc26_33.1: = specific_function %impl.elem0.loc26_33.1, @Core.IntLiteral.as.ImplicitAs.impl.Convert(constants.%int_32) [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn] // CHECK:STDOUT: %bound_method.loc26_33.2: = bound_method %int_1, %specific_fn.loc26_33.1 [concrete = constants.%bound_method.38b] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc26_33.1: init %i32 = call %bound_method.loc26_33.2(%int_1) [concrete = constants.%int_1.5d2] // CHECK:STDOUT: %.loc26_33.2: init %i32 = converted %int_1, %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc26_33.1 [concrete = constants.%int_1.5d2] // CHECK:STDOUT: %.loc26_3: ref %Class = splice_block %v.var {} // CHECK:STDOUT: %.loc26_33.3: ref %i32 = class_element_access %.loc26_3, element0 // CHECK:STDOUT: %.loc26_33.4: init %i32 to %.loc26_33.3 = in_place_init %.loc26_33.2 [concrete = constants.%int_1.5d2] // CHECK:STDOUT: %impl.elem0.loc26_33.2: %.545 = impl_witness_access constants.%ImplicitAs.impl_witness.6bc, element0 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5] // CHECK:STDOUT: %bound_method.loc26_33.3: = bound_method %int_2, %impl.elem0.loc26_33.2 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.bound.4e5] // CHECK:STDOUT: %specific_fn.loc26_33.2: = specific_function %impl.elem0.loc26_33.2, @Core.IntLiteral.as.ImplicitAs.impl.Convert(constants.%int_32) [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn] // CHECK:STDOUT: %bound_method.loc26_33.4: = bound_method %int_2, %specific_fn.loc26_33.2 [concrete = constants.%bound_method.646] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc26_33.2: init %i32 = call %bound_method.loc26_33.4(%int_2) [concrete = constants.%int_2.ef8] // CHECK:STDOUT: %.loc26_33.5: init %i32 = converted %int_2, %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc26_33.2 [concrete = constants.%int_2.ef8] // CHECK:STDOUT: %.loc26_33.6: ref %i32 = class_element_access %.loc26_3, element1 // CHECK:STDOUT: %.loc26_33.7: init %i32 to %.loc26_33.6 = in_place_init %.loc26_33.5 [concrete = constants.%int_2.ef8] // CHECK:STDOUT: %.loc26_33.8: init %Class to %.loc26_3 = class_init (%.loc26_33.4, %.loc26_33.7) [concrete = constants.%Class.val] // CHECK:STDOUT: %.loc26_35: init %Class = converted %.loc26_33.1, %.loc26_33.8 [concrete = constants.%Class.val] // CHECK:STDOUT: assign %v.var, %.loc26_35 // CHECK:STDOUT: %Class.ref.loc26_10: type = name_ref Class, file.%Class.decl [concrete = constants.%Class] // CHECK:STDOUT: %v: ref %Class = ref_binding v, %v.var // CHECK:STDOUT: // CHECK:STDOUT: %Destroy.Op.bound: = bound_method %v.var, constants.%Destroy.Op // CHECK:STDOUT: %Destroy.Op.call: init %empty_tuple.type = call %Destroy.Op.bound(%v.var) // CHECK:STDOUT: // CHECK:STDOUT: } // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/class/init_nested.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/int.carbon // TODO: Add ranges and switch to "--dump-sem-ir-ranges=only". // EXTRA-ARGS: --dump-sem-ir-ranges=if-present // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/class/init_nested.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/class/init_nested.carbon class Inner { var a: i32; var b: i32; } fn MakeInner() -> Inner; class Outer { var c: Inner; var d: Inner; } fn MakeOuter() -> Outer { return {.c = MakeInner(), .d = MakeInner()}; } // CHECK:STDOUT: --- init_nested.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %Inner: type = class_type @Inner [concrete] // CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [concrete] // CHECK:STDOUT: %Int.type: type = generic_class_type @Int [concrete] // CHECK:STDOUT: %Int.generic: %Int.type = struct_value () [concrete] // CHECK:STDOUT: %i32: type = class_type @Int, @Int(%int_32) [concrete] // CHECK:STDOUT: %Inner.elem: type = unbound_element_type %Inner, %i32 [concrete] // CHECK:STDOUT: %struct_type.a.b: type = struct_type {.a: %i32, .b: %i32} [concrete] // CHECK:STDOUT: %complete_type.705: = complete_type_witness %struct_type.a.b [concrete] // CHECK:STDOUT: %.413: Core.Form = init_form %Inner [concrete] // CHECK:STDOUT: %pattern_type.84b: type = pattern_type %Inner [concrete] // CHECK:STDOUT: %MakeInner.type: type = fn_type @MakeInner [concrete] // CHECK:STDOUT: %MakeInner: %MakeInner.type = struct_value () [concrete] // CHECK:STDOUT: %Outer: type = class_type @Outer [concrete] // CHECK:STDOUT: %Outer.elem: type = unbound_element_type %Outer, %Inner [concrete] // CHECK:STDOUT: %struct_type.c.d.8f4: type = struct_type {.c: %Inner, .d: %Inner} [concrete] // CHECK:STDOUT: %complete_type.3ed: = complete_type_witness %struct_type.c.d.8f4 [concrete] // CHECK:STDOUT: %.b00: Core.Form = init_form %Outer [concrete] // CHECK:STDOUT: %pattern_type.9ae: type = pattern_type %Outer [concrete] // CHECK:STDOUT: %MakeOuter.type: type = fn_type @MakeOuter [concrete] // CHECK:STDOUT: %MakeOuter: %MakeOuter.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: .Int = %Core.Int // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: %Core.Int: %Int.type = import_ref Core//prelude/parts/int, Int, loaded [concrete = constants.%Int.generic] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .Inner = %Inner.decl // CHECK:STDOUT: .MakeInner = %MakeInner.decl // CHECK:STDOUT: .Outer = %Outer.decl // CHECK:STDOUT: .MakeOuter = %MakeOuter.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %Inner.decl: type = class_decl @Inner [concrete = constants.%Inner] {} {} // CHECK:STDOUT: %MakeInner.decl: %MakeInner.type = fn_decl @MakeInner [concrete = constants.%MakeInner] { // CHECK:STDOUT: %return.patt: %pattern_type.84b = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: %pattern_type.84b = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %Inner.ref: type = name_ref Inner, file.%Inner.decl [concrete = constants.%Inner] // CHECK:STDOUT: %.loc20: Core.Form = init_form %Inner.ref [concrete = constants.%.413] // CHECK:STDOUT: %return.param: ref %Inner = out_param call_param0 // CHECK:STDOUT: %return: ref %Inner = return_slot %return.param // CHECK:STDOUT: } // CHECK:STDOUT: %Outer.decl: type = class_decl @Outer [concrete = constants.%Outer] {} {} // CHECK:STDOUT: %MakeOuter.decl: %MakeOuter.type = fn_decl @MakeOuter [concrete = constants.%MakeOuter] { // CHECK:STDOUT: %return.patt: %pattern_type.9ae = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: %pattern_type.9ae = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %Outer.ref: type = name_ref Outer, file.%Outer.decl [concrete = constants.%Outer] // CHECK:STDOUT: %.loc27: Core.Form = init_form %Outer.ref [concrete = constants.%.b00] // CHECK:STDOUT: %return.param: ref %Outer = out_param call_param0 // CHECK:STDOUT: %return: ref %Outer = return_slot %return.param // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @Inner { // CHECK:STDOUT: %i32.loc16: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %.loc16: %Inner.elem = field_decl a, element0 [concrete] // CHECK:STDOUT: %i32.loc17: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %.loc17: %Inner.elem = field_decl b, element1 [concrete] // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%struct_type.a.b [concrete = constants.%complete_type.705] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%Inner // CHECK:STDOUT: .a = %.loc16 // CHECK:STDOUT: .b = %.loc17 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @Outer { // CHECK:STDOUT: %Inner.ref.loc23: type = name_ref Inner, file.%Inner.decl [concrete = constants.%Inner] // CHECK:STDOUT: %.loc23: %Outer.elem = field_decl c, element0 [concrete] // CHECK:STDOUT: %Inner.ref.loc24: type = name_ref Inner, file.%Inner.decl [concrete = constants.%Inner] // CHECK:STDOUT: %.loc24: %Outer.elem = field_decl d, element1 [concrete] // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%struct_type.c.d.8f4 [concrete = constants.%complete_type.3ed] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%Outer // CHECK:STDOUT: .Inner = // CHECK:STDOUT: .c = %.loc23 // CHECK:STDOUT: .d = %.loc24 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @MakeInner() -> out %return.param: %Inner; // CHECK:STDOUT: // CHECK:STDOUT: fn @MakeOuter() -> out %return.param: %Outer { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %MakeInner.ref.loc28_16: %MakeInner.type = name_ref MakeInner, file.%MakeInner.decl [concrete = constants.%MakeInner] // CHECK:STDOUT: %.loc28_45.1: ref %Inner = class_element_access %return.param, element0 // CHECK:STDOUT: %MakeInner.call.loc28_26: init %Inner to %.loc28_45.1 = call %MakeInner.ref.loc28_16() // CHECK:STDOUT: %MakeInner.ref.loc28_34: %MakeInner.type = name_ref MakeInner, file.%MakeInner.decl [concrete = constants.%MakeInner] // CHECK:STDOUT: %.loc28_45.2: ref %Inner = class_element_access %return.param, element1 // CHECK:STDOUT: %MakeInner.call.loc28_44: init %Inner to %.loc28_45.2 = call %MakeInner.ref.loc28_34() // CHECK:STDOUT: %.loc28_45.3: %struct_type.c.d.8f4 = struct_literal (%MakeInner.call.loc28_26, %MakeInner.call.loc28_44) // CHECK:STDOUT: %.loc28_45.4: init %Outer to %return.param = class_init (%MakeInner.call.loc28_26, %MakeInner.call.loc28_44) // CHECK:STDOUT: %.loc28_46: init %Outer = converted %.loc28_45.3, %.loc28_45.4 // CHECK:STDOUT: return %.loc28_46 to %return.param // CHECK:STDOUT: } // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/class/local.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/int.carbon // TODO: Add ranges and switch to "--dump-sem-ir-ranges=only". // EXTRA-ARGS: --dump-sem-ir-ranges=if-present // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/class/local.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/class/local.carbon class A { fn F() -> i32 { class B { fn Make() -> Self { returned var b: Self = {.n = 1}; return var; } var n: i32; } return B.Make().n; } } // CHECK:STDOUT: --- local.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %A: type = class_type @A [concrete] // CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [concrete] // CHECK:STDOUT: %Int.type: type = generic_class_type @Int [concrete] // CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] // CHECK:STDOUT: %Int.generic: %Int.type = struct_value () [concrete] // CHECK:STDOUT: %N: Core.IntLiteral = symbolic_binding N, 0 [symbolic] // CHECK:STDOUT: %i32: type = class_type @Int, @Int(%int_32) [concrete] // CHECK:STDOUT: %.ff5: Core.Form = init_form %i32 [concrete] // CHECK:STDOUT: %pattern_type.7ce: type = pattern_type %i32 [concrete] // CHECK:STDOUT: %A.F.type: type = fn_type @A.F [concrete] // CHECK:STDOUT: %A.F: %A.F.type = struct_value () [concrete] // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %complete_type.357: = complete_type_witness %empty_struct_type [concrete] // CHECK:STDOUT: %B: type = class_type @B [concrete] // CHECK:STDOUT: %.18e: Core.Form = init_form %B [concrete] // CHECK:STDOUT: %pattern_type.971: type = pattern_type %B [concrete] // CHECK:STDOUT: %B.Make.type: type = fn_type @B.Make [concrete] // CHECK:STDOUT: %B.Make: %B.Make.type = struct_value () [concrete] // CHECK:STDOUT: %B.elem: type = unbound_element_type %B, %i32 [concrete] // CHECK:STDOUT: %struct_type.n.033: type = struct_type {.n: %i32} [concrete] // CHECK:STDOUT: %complete_type.54b: = complete_type_witness %struct_type.n.033 [concrete] // CHECK:STDOUT: %int_1.5b8: Core.IntLiteral = int_value 1 [concrete] // CHECK:STDOUT: %struct_type.n.44a: type = struct_type {.n: Core.IntLiteral} [concrete] // CHECK:STDOUT: %struct: %struct_type.n.44a = struct_value (%int_1.5b8) [concrete] // CHECK:STDOUT: %ImplicitAs.type.cc7: type = generic_interface_type @ImplicitAs [concrete] // CHECK:STDOUT: %ImplicitAs.generic: %ImplicitAs.type.cc7 = struct_value () [concrete] // CHECK:STDOUT: %ImplicitAs.type.e8c: type = facet_type <@ImplicitAs, @ImplicitAs(%i32)> [concrete] // CHECK:STDOUT: %To: Core.IntLiteral = symbolic_binding To, 0 [symbolic] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.4e6: type = fn_type @Core.IntLiteral.as.ImplicitAs.impl.Convert, @Core.IntLiteral.as.ImplicitAs.impl(%To) [symbolic] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.3c2: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.4e6 = struct_value () [symbolic] // CHECK:STDOUT: %ImplicitAs.impl_witness.6bc: = impl_witness imports.%ImplicitAs.impl_witness_table.74f, @Core.IntLiteral.as.ImplicitAs.impl(%int_32) [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.e0d: type = fn_type @Core.IntLiteral.as.ImplicitAs.impl.Convert, @Core.IntLiteral.as.ImplicitAs.impl(%int_32) [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.e0d = struct_value () [concrete] // CHECK:STDOUT: %ImplicitAs.facet: %ImplicitAs.type.e8c = facet_value Core.IntLiteral, (%ImplicitAs.impl_witness.6bc) [concrete] // CHECK:STDOUT: %ImplicitAs.WithSelf.Convert.type.b37: type = fn_type @ImplicitAs.WithSelf.Convert, @ImplicitAs.WithSelf(%i32, %ImplicitAs.facet) [concrete] // CHECK:STDOUT: %.545: type = fn_type_with_self_type %ImplicitAs.WithSelf.Convert.type.b37, %ImplicitAs.facet [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.bound: = bound_method %int_1.5b8, %Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5 [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn: = specific_function %Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5, @Core.IntLiteral.as.ImplicitAs.impl.Convert(%int_32) [concrete] // CHECK:STDOUT: %bound_method: = bound_method %int_1.5b8, %Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn [concrete] // CHECK:STDOUT: %int_1.5d2: %i32 = int_value 1 [concrete] // CHECK:STDOUT: %B.val: %B = struct_value (%int_1.5d2) [concrete] // CHECK:STDOUT: %Copy.type: type = facet_type <@Copy> [concrete] // CHECK:STDOUT: %Int.as.Copy.impl.Op.type.824: type = fn_type @Int.as.Copy.impl.Op, @Int.as.Copy.impl(%N) [symbolic] // CHECK:STDOUT: %Int.as.Copy.impl.Op.9b9: %Int.as.Copy.impl.Op.type.824 = struct_value () [symbolic] // CHECK:STDOUT: %Copy.impl_witness.f17: = impl_witness imports.%Copy.impl_witness_table.e76, @Int.as.Copy.impl(%int_32) [concrete] // CHECK:STDOUT: %Int.as.Copy.impl.Op.type.546: type = fn_type @Int.as.Copy.impl.Op, @Int.as.Copy.impl(%int_32) [concrete] // CHECK:STDOUT: %Int.as.Copy.impl.Op.664: %Int.as.Copy.impl.Op.type.546 = struct_value () [concrete] // CHECK:STDOUT: %Copy.facet: %Copy.type = facet_value %i32, (%Copy.impl_witness.f17) [concrete] // CHECK:STDOUT: %Copy.WithSelf.Op.type.081: type = fn_type @Copy.WithSelf.Op, @Copy.WithSelf(%Copy.facet) [concrete] // CHECK:STDOUT: %.8e2: type = fn_type_with_self_type %Copy.WithSelf.Op.type.081, %Copy.facet [concrete] // CHECK:STDOUT: %Int.as.Copy.impl.Op.specific_fn: = specific_function %Int.as.Copy.impl.Op.664, @Int.as.Copy.impl.Op(%int_32) [concrete] // CHECK:STDOUT: %Destroy.type: type = facet_type <@Destroy> [concrete] // CHECK:STDOUT: %Destroy.Op.type: type = fn_type @Destroy.Op [concrete] // CHECK:STDOUT: %Destroy.Op: %Destroy.Op.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: .Int = %Core.Int // CHECK:STDOUT: .ImplicitAs = %Core.ImplicitAs // CHECK:STDOUT: .Copy = %Core.Copy // CHECK:STDOUT: .Destroy = %Core.Destroy // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: %Core.Int: %Int.type = import_ref Core//prelude/parts/int, Int, loaded [concrete = constants.%Int.generic] // CHECK:STDOUT: %Core.ImplicitAs: %ImplicitAs.type.cc7 = import_ref Core//prelude/parts/as, ImplicitAs, loaded [concrete = constants.%ImplicitAs.generic] // CHECK:STDOUT: %Core.import_ref.42d: @Core.IntLiteral.as.ImplicitAs.impl.%Core.IntLiteral.as.ImplicitAs.impl.Convert.type (%Core.IntLiteral.as.ImplicitAs.impl.Convert.type.4e6) = import_ref Core//prelude/parts/int, loc{{\d+_\d+}}, loaded [symbolic = @Core.IntLiteral.as.ImplicitAs.impl.%Core.IntLiteral.as.ImplicitAs.impl.Convert (constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.3c2)] // CHECK:STDOUT: %ImplicitAs.impl_witness_table.74f = impl_witness_table (%Core.import_ref.42d), @Core.IntLiteral.as.ImplicitAs.impl [concrete] // CHECK:STDOUT: %Core.Copy: type = import_ref Core//prelude/parts/copy, Copy, loaded [concrete = constants.%Copy.type] // CHECK:STDOUT: %Core.import_ref.18d: @Int.as.Copy.impl.%Int.as.Copy.impl.Op.type (%Int.as.Copy.impl.Op.type.824) = import_ref Core//prelude/parts/int, loc{{\d+_\d+}}, loaded [symbolic = @Int.as.Copy.impl.%Int.as.Copy.impl.Op (constants.%Int.as.Copy.impl.Op.9b9)] // CHECK:STDOUT: %Copy.impl_witness_table.e76 = impl_witness_table (%Core.import_ref.18d), @Int.as.Copy.impl [concrete] // CHECK:STDOUT: %Core.Destroy: type = import_ref Core//prelude/parts/destroy, Destroy, loaded [concrete = constants.%Destroy.type] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .A = %A.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %A.decl: type = class_decl @A [concrete = constants.%A] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @A { // CHECK:STDOUT: %A.F.decl: %A.F.type = fn_decl @A.F [concrete = constants.%A.F] { // CHECK:STDOUT: %return.patt: %pattern_type.7ce = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: %pattern_type.7ce = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %i32: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %.loc16: Core.Form = init_form %i32 [concrete = constants.%.ff5] // CHECK:STDOUT: %return.param: ref %i32 = out_param call_param0 // CHECK:STDOUT: %return: ref %i32 = return_slot %return.param // CHECK:STDOUT: } // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type.357] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%A // CHECK:STDOUT: .F = %A.F.decl // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @B { // CHECK:STDOUT: %B.Make.decl: %B.Make.type = fn_decl @B.Make [concrete = constants.%B.Make] { // CHECK:STDOUT: %return.patt: %pattern_type.971 = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: %pattern_type.971 = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %Self.ref.loc18: type = name_ref Self, constants.%B [concrete = constants.%B] // CHECK:STDOUT: %.loc18: Core.Form = init_form %Self.ref.loc18 [concrete = constants.%.18e] // CHECK:STDOUT: %return.param: ref %B = out_param call_param0 // CHECK:STDOUT: %return: ref %B = return_slot %return.param // CHECK:STDOUT: } // CHECK:STDOUT: %i32: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %.loc23: %B.elem = field_decl n, element0 [concrete] // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%struct_type.n.033 [concrete = constants.%complete_type.54b] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%B // CHECK:STDOUT: .Make = %B.Make.decl // CHECK:STDOUT: .n = %.loc23 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @A.F() -> out %return.param: %i32 { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %B.decl: type = class_decl @B [concrete = constants.%B] {} {} // CHECK:STDOUT: %B.ref: type = name_ref B, %B.decl [concrete = constants.%B] // CHECK:STDOUT: %Make.ref: %B.Make.type = name_ref Make, @B.%B.Make.decl [concrete = constants.%B.Make] // CHECK:STDOUT: %.loc26_19.1: ref %B = temporary_storage // CHECK:STDOUT: %B.Make.call: init %B to %.loc26_19.1 = call %Make.ref() // CHECK:STDOUT: %.loc26_19.2: ref %B = temporary %.loc26_19.1, %B.Make.call // CHECK:STDOUT: %n.ref: %B.elem = name_ref n, @B.%.loc23 [concrete = @B.%.loc23] // CHECK:STDOUT: %.loc26_20.1: ref %i32 = class_element_access %.loc26_19.2, element0 // CHECK:STDOUT: %.loc26_20.2: %i32 = acquire_value %.loc26_20.1 // CHECK:STDOUT: %impl.elem0: %.8e2 = impl_witness_access constants.%Copy.impl_witness.f17, element0 [concrete = constants.%Int.as.Copy.impl.Op.664] // CHECK:STDOUT: %bound_method.loc26_20.1: = bound_method %.loc26_20.2, %impl.elem0 // CHECK:STDOUT: %specific_fn: = specific_function %impl.elem0, @Int.as.Copy.impl.Op(constants.%int_32) [concrete = constants.%Int.as.Copy.impl.Op.specific_fn] // CHECK:STDOUT: %bound_method.loc26_20.2: = bound_method %.loc26_20.2, %specific_fn // CHECK:STDOUT: %Int.as.Copy.impl.Op.call: init %i32 = call %bound_method.loc26_20.2(%.loc26_20.2) // CHECK:STDOUT: %Destroy.Op.bound: = bound_method %.loc26_19.2, constants.%Destroy.Op // CHECK:STDOUT: %Destroy.Op.call: init %empty_tuple.type = call %Destroy.Op.bound(%.loc26_19.2) // CHECK:STDOUT: return %Int.as.Copy.impl.Op.call // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @B.Make() -> out %return.param: %B { // CHECK:STDOUT: !entry: // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %b.patt: %pattern_type.971 = ref_binding_pattern b [concrete] // CHECK:STDOUT: %b.var_patt: %pattern_type.971 = var_pattern %b.patt [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %int_1: Core.IntLiteral = int_value 1 [concrete = constants.%int_1.5b8] // CHECK:STDOUT: %.loc19_39.1: %struct_type.n.44a = struct_literal (%int_1) [concrete = constants.%struct] // CHECK:STDOUT: %impl.elem0: %.545 = impl_witness_access constants.%ImplicitAs.impl_witness.6bc, element0 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5] // CHECK:STDOUT: %bound_method.loc19_39.1: = bound_method %int_1, %impl.elem0 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.bound] // CHECK:STDOUT: %specific_fn: = specific_function %impl.elem0, @Core.IntLiteral.as.ImplicitAs.impl.Convert(constants.%int_32) [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn] // CHECK:STDOUT: %bound_method.loc19_39.2: = bound_method %int_1, %specific_fn [concrete = constants.%bound_method] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.call: init %i32 = call %bound_method.loc19_39.2(%int_1) [concrete = constants.%int_1.5d2] // CHECK:STDOUT: %.loc19_39.2: init %i32 = converted %int_1, %Core.IntLiteral.as.ImplicitAs.impl.Convert.call [concrete = constants.%int_1.5d2] // CHECK:STDOUT: %.loc19_39.3: ref %i32 = class_element_access %return.param, element0 // CHECK:STDOUT: %.loc19_39.4: init %i32 to %.loc19_39.3 = in_place_init %.loc19_39.2 [concrete = constants.%int_1.5d2] // CHECK:STDOUT: %.loc19_39.5: init %B to %return.param = class_init (%.loc19_39.4) [concrete = constants.%B.val] // CHECK:STDOUT: %.loc19_18: init %B = converted %.loc19_39.1, %.loc19_39.5 [concrete = constants.%B.val] // CHECK:STDOUT: assign %return.param, %.loc19_18 // CHECK:STDOUT: %Self.ref.loc19: type = name_ref Self, constants.%B [concrete = constants.%B] // CHECK:STDOUT: %b: ref %B = ref_binding b, %return.param // CHECK:STDOUT: return %b to %return.param // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @Destroy.Op(%self.param: ref %B) = "no_op"; // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/class/method/fail_generic_method.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/convert.carbon // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/class/method/fail_generic_method.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/class/method/fail_generic_method.carbon class I {} class Class(T:! type) { var a: T; fn F[self: Self](n: T); } // TODO: The follow-on errors here aren't great. Investigate whether we can // enter the scope anyway if the parameters don't match. // CHECK:STDERR: fail_generic_method.carbon:[[@LINE+15]]:17: error: type `` of parameter 1 in redeclaration differs from previous parameter type `` [RedeclParamDiffersType] // CHECK:STDERR: fn Class(unused N:! I).F[unused self: Self](unused n: T) {} // CHECK:STDERR: ^ // CHECK:STDERR: fail_generic_method.carbon:[[@LINE-10]]:13: note: previous declaration's corresponding parameter here [RedeclParamPrevious] // CHECK:STDERR: class Class(T:! type) { // CHECK:STDERR: ^ // CHECK:STDERR: // CHECK:STDERR: fail_generic_method.carbon:[[@LINE+8]]:39: error: name `Self` not found [NameNotFound] // CHECK:STDERR: fn Class(unused N:! I).F[unused self: Self](unused n: T) {} // CHECK:STDERR: ^~~~ // CHECK:STDERR: // CHECK:STDERR: fail_generic_method.carbon:[[@LINE+4]]:55: error: name `T` not found [NameNotFound] // CHECK:STDERR: fn Class(unused N:! I).F[unused self: Self](unused n: T) {} // CHECK:STDERR: ^ // CHECK:STDERR: fn Class(unused N:! I).F[unused self: Self](unused n: T) {} ================================================ FILE: toolchain/check/testdata/class/method/fail_method.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/convert.carbon // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/class/method/fail_method.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/class/method/fail_method.carbon class Class { fn NoSelf(); fn WithSelf[self: Class](); } alias A = Class.WithSelf; fn F(c: Class) { c.NoSelf(); c.WithSelf(); Class.NoSelf(); // CHECK:STDERR: fail_method.carbon:[[@LINE+7]]:3: error: missing object argument in method call [MissingObjectInMethodCall] // CHECK:STDERR: Class.WithSelf(); // CHECK:STDERR: ^~~~~~~~~~~~~~~~ // CHECK:STDERR: fail_method.carbon:[[@LINE-13]]:3: note: calling function declared here [InCallToFunction] // CHECK:STDERR: fn WithSelf[self: Class](); // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: Class.WithSelf(); // CHECK:STDERR: fail_method.carbon:[[@LINE+7]]:3: error: 1 argument passed to function expecting 0 arguments [CallArgCountMismatch] // CHECK:STDERR: Class.WithSelf(c); // CHECK:STDERR: ^~~~~~~~~~~~~~~~~ // CHECK:STDERR: fail_method.carbon:[[@LINE-21]]:3: note: calling function declared here [InCallToEntity] // CHECK:STDERR: fn WithSelf[self: Class](); // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: Class.WithSelf(c); // CHECK:STDERR: fail_method.carbon:[[@LINE+7]]:3: error: missing object argument in method call [MissingObjectInMethodCall] // CHECK:STDERR: A(); // CHECK:STDERR: ^~~ // CHECK:STDERR: fail_method.carbon:[[@LINE-30]]:3: note: calling function declared here [InCallToFunction] // CHECK:STDERR: fn WithSelf[self: Class](); // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: A(); } ================================================ FILE: toolchain/check/testdata/class/method/fail_method_modifiers.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/convert.carbon // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/class/method/fail_method_modifiers.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/class/method/fail_method_modifiers.carbon class FinalClass { // CHECK:STDERR: fail_method_modifiers.carbon:[[@LINE+7]]:3: error: `abstract` not allowed; requires `abstract` class scope [ModifierAbstractNotAllowed] // CHECK:STDERR: abstract fn Abstract[self: Self](); // CHECK:STDERR: ^~~~~~~~ // CHECK:STDERR: fail_method_modifiers.carbon:[[@LINE-5]]:1: note: containing definition here [ModifierNotInContext] // CHECK:STDERR: class FinalClass { // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~ // CHECK:STDERR: abstract fn Abstract[self: Self](); // CHECK:STDERR: fail_method_modifiers.carbon:[[@LINE+7]]:3: error: `virtual` not allowed; requires `abstract` or `base` class scope [ModifierVirtualNotAllowed] // CHECK:STDERR: virtual fn Virtual[self: Self](); // CHECK:STDERR: ^~~~~~~ // CHECK:STDERR: fail_method_modifiers.carbon:[[@LINE-14]]:1: note: containing definition here [ModifierNotInContext] // CHECK:STDERR: class FinalClass { // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~ // CHECK:STDERR: virtual fn Virtual[self: Self](); } abstract class AbstractClass { // CHECK:STDERR: fail_method_modifiers.carbon:[[@LINE+4]]:3: error: `default` not allowed; requires interface scope [ModifierRequiresInterface] // CHECK:STDERR: default fn Default[self: Self](); // CHECK:STDERR: ^~~~~~~ // CHECK:STDERR: default fn Default[self: Self](); // CHECK:STDERR: fail_method_modifiers.carbon:[[@LINE+4]]:3: error: `final` not allowed; requires interface scope [ModifierRequiresInterface] // CHECK:STDERR: final fn Final[self: Self](); // CHECK:STDERR: ^~~~~ // CHECK:STDERR: final fn Final[self: Self](); } base class BaseClass { // CHECK:STDERR: fail_method_modifiers.carbon:[[@LINE+7]]:3: error: `abstract` not allowed; requires `abstract` class scope [ModifierAbstractNotAllowed] // CHECK:STDERR: abstract fn Abstract[self: Self](); // CHECK:STDERR: ^~~~~~~~ // CHECK:STDERR: fail_method_modifiers.carbon:[[@LINE-5]]:1: note: containing definition here [ModifierNotInContext] // CHECK:STDERR: base class BaseClass { // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: abstract fn Abstract[self: Self](); } ================================================ FILE: toolchain/check/testdata/class/method/fail_method_redefinition.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/convert.carbon // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/class/method/fail_method_redefinition.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/class/method/fail_method_redefinition.carbon class Class { fn F() {} // CHECK:STDERR: fail_method_redefinition.carbon:[[@LINE+7]]:3: error: redefinition of `fn F` [RedeclRedef] // CHECK:STDERR: fn F() {} // CHECK:STDERR: ^~~~~~~~ // CHECK:STDERR: fail_method_redefinition.carbon:[[@LINE-4]]:3: note: previously defined here [RedeclPrevDef] // CHECK:STDERR: fn F() {} // CHECK:STDERR: ^~~~~~~~ // CHECK:STDERR: fn F() {} } ================================================ FILE: toolchain/check/testdata/class/method/fail_out_of_line_decl.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/convert.carbon // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/class/method/fail_out_of_line_decl.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/class/method/fail_out_of_line_decl.carbon class C {} // CHECK:STDERR: fail_out_of_line_decl.carbon:[[@LINE+4]]:6: error: out-of-line declaration requires a declaration in scoped entity [QualifiedDeclOutsideScopeEntity] // CHECK:STDERR: fn C.F() {} // CHECK:STDERR: ^ // CHECK:STDERR: fn C.F() {} ================================================ FILE: toolchain/check/testdata/class/method/generic_method.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/convert.carbon // TODO: Add ranges and switch to "--dump-sem-ir-ranges=only". // EXTRA-ARGS: --dump-sem-ir-ranges=if-present // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/class/method/generic_method.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/class/method/generic_method.carbon class Class(T:! type) { var a: T; fn F[self: Self](n: T); } fn Class(T:! type).F[unused self: Self](unused n: T) {} // CHECK:STDOUT: --- generic_method.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %type: type = facet_type [concrete] // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self] // CHECK:STDOUT: %pattern_type.98f: type = pattern_type type [concrete] // CHECK:STDOUT: %T: type = symbolic_binding T, 0 [symbolic] // CHECK:STDOUT: %Class.type: type = generic_class_type @Class [concrete] // CHECK:STDOUT: %Class.generic: %Class.type = struct_value () [concrete] // CHECK:STDOUT: %Class: type = class_type @Class, @Class(%T) [symbolic] // CHECK:STDOUT: %require_complete.944: = require_complete_type %T [symbolic] // CHECK:STDOUT: %Class.elem: type = unbound_element_type %Class, %T [symbolic] // CHECK:STDOUT: %pattern_type.466: type = pattern_type %Class [symbolic] // CHECK:STDOUT: %pattern_type.51d: type = pattern_type %T [symbolic] // CHECK:STDOUT: %Class.F.type: type = fn_type @Class.F, @Class(%T) [symbolic] // CHECK:STDOUT: %Class.F: %Class.F.type = struct_value () [symbolic] // CHECK:STDOUT: %struct_type.a: type = struct_type {.a: %T} [symbolic] // CHECK:STDOUT: %complete_type: = complete_type_witness %struct_type.a [symbolic] // CHECK:STDOUT: %require_complete.43d: = require_complete_type %Class [symbolic] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .Class = %Class.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %Class.decl: %Class.type = class_decl @Class [concrete = constants.%Class.generic] { // CHECK:STDOUT: %T.patt: %pattern_type.98f = symbolic_binding_pattern T, 0 [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %.loc15_17.1: type = splice_block %.loc15_17.2 [concrete = type] { // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %.loc15_17.2: type = type_literal type [concrete = type] // CHECK:STDOUT: } // CHECK:STDOUT: %T.loc15_13.2: type = symbolic_binding T, 0 [symbolic = %T.loc15_13.1 (constants.%T)] // CHECK:STDOUT: } // CHECK:STDOUT: %Class.F.decl: %Class.F.type = fn_decl @Class.F [symbolic = constants.%Class.F] { // CHECK:STDOUT: %self.patt: @Class.F.%pattern_type.loc17_8 (%pattern_type.466) = value_binding_pattern self [concrete] // CHECK:STDOUT: %self.param_patt: @Class.F.%pattern_type.loc17_8 (%pattern_type.466) = value_param_pattern %self.patt [concrete] // CHECK:STDOUT: %n.patt: @Class.F.%pattern_type.loc17_20 (%pattern_type.51d) = value_binding_pattern n [concrete] // CHECK:STDOUT: %n.param_patt: @Class.F.%pattern_type.loc17_20 (%pattern_type.51d) = value_param_pattern %n.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %.loc20_14.1: type = splice_block %.loc20_14.2 [concrete = type] { // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %.loc20_14.2: type = type_literal type [concrete = type] // CHECK:STDOUT: } // CHECK:STDOUT: %T.loc20: type = symbolic_binding T, 0 [symbolic = @Class.%T.loc15_13.1 (constants.%T)] // CHECK:STDOUT: %self.param.loc20: @Class.F.%Class (%Class) = value_param call_param0 // CHECK:STDOUT: %.loc20_35.1: type = splice_block %Self.ref.loc20 [symbolic = %Class (constants.%Class)] { // CHECK:STDOUT: %.loc20_35.2: type = specific_constant constants.%Class, @Class(constants.%T) [symbolic = %Class (constants.%Class)] // CHECK:STDOUT: %Self.ref.loc20: type = name_ref Self, %.loc20_35.2 [symbolic = %Class (constants.%Class)] // CHECK:STDOUT: } // CHECK:STDOUT: %self.loc20: @Class.F.%Class (%Class) = value_binding self, %self.param.loc20 // CHECK:STDOUT: %n.param.loc20: @Class.F.%T.loc17 (%T) = value_param call_param1 // CHECK:STDOUT: %T.ref.loc20: type = name_ref T, %T.loc20 [symbolic = %T.loc17 (constants.%T)] // CHECK:STDOUT: %n.loc20: @Class.F.%T.loc17 (%T) = value_binding n, %n.param.loc20 // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic class @Class(%T.loc15_13.2: type) { // CHECK:STDOUT: %T.loc15_13.1: type = symbolic_binding T, 0 [symbolic = %T.loc15_13.1 (constants.%T)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete: = require_complete_type %T.loc15_13.1 [symbolic = %require_complete (constants.%require_complete.944)] // CHECK:STDOUT: %Class: type = class_type @Class, @Class(%T.loc15_13.1) [symbolic = %Class (constants.%Class)] // CHECK:STDOUT: %Class.elem: type = unbound_element_type %Class, %T.loc15_13.1 [symbolic = %Class.elem (constants.%Class.elem)] // CHECK:STDOUT: %Class.F.type: type = fn_type @Class.F, @Class(%T.loc15_13.1) [symbolic = %Class.F.type (constants.%Class.F.type)] // CHECK:STDOUT: %Class.F: @Class.%Class.F.type (%Class.F.type) = struct_value () [symbolic = %Class.F (constants.%Class.F)] // CHECK:STDOUT: %struct_type.a: type = struct_type {.a: @Class.%T.loc15_13.1 (%T)} [symbolic = %struct_type.a (constants.%struct_type.a)] // CHECK:STDOUT: %complete_type.loc18_1.2: = complete_type_witness %struct_type.a [symbolic = %complete_type.loc18_1.2 (constants.%complete_type)] // CHECK:STDOUT: // CHECK:STDOUT: class { // CHECK:STDOUT: %T.ref: type = name_ref T, %T.loc15_13.2 [symbolic = %T.loc15_13.1 (constants.%T)] // CHECK:STDOUT: %.loc16: @Class.%Class.elem (%Class.elem) = field_decl a, element0 [concrete] // CHECK:STDOUT: %Class.F.decl: @Class.%Class.F.type (%Class.F.type) = fn_decl @Class.F [symbolic = @Class.%Class.F (constants.%Class.F)] { // CHECK:STDOUT: %self.patt: @Class.F.%pattern_type.loc17_8 (%pattern_type.466) = value_binding_pattern self [concrete] // CHECK:STDOUT: %self.param_patt: @Class.F.%pattern_type.loc17_8 (%pattern_type.466) = value_param_pattern %self.patt [concrete] // CHECK:STDOUT: %n.patt: @Class.F.%pattern_type.loc17_20 (%pattern_type.51d) = value_binding_pattern n [concrete] // CHECK:STDOUT: %n.param_patt: @Class.F.%pattern_type.loc17_20 (%pattern_type.51d) = value_param_pattern %n.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %self.param.loc17: @Class.F.%Class (%Class) = value_param call_param0 // CHECK:STDOUT: %.loc17_14.1: type = splice_block %Self.ref.loc17 [symbolic = %Class (constants.%Class)] { // CHECK:STDOUT: %.loc17_14.2: type = specific_constant constants.%Class, @Class(constants.%T) [symbolic = %Class (constants.%Class)] // CHECK:STDOUT: %Self.ref.loc17: type = name_ref Self, %.loc17_14.2 [symbolic = %Class (constants.%Class)] // CHECK:STDOUT: } // CHECK:STDOUT: %self.loc17: @Class.F.%Class (%Class) = value_binding self, %self.param.loc17 // CHECK:STDOUT: %n.param.loc17: @Class.F.%T.loc17 (%T) = value_param call_param1 // CHECK:STDOUT: %T.ref.loc17: type = name_ref T, @Class.%T.loc15_13.2 [symbolic = %T.loc17 (constants.%T)] // CHECK:STDOUT: %n.loc17: @Class.F.%T.loc17 (%T) = value_binding n, %n.param.loc17 // CHECK:STDOUT: } // CHECK:STDOUT: %complete_type.loc18_1.1: = complete_type_witness constants.%struct_type.a [symbolic = %complete_type.loc18_1.2 (constants.%complete_type)] // CHECK:STDOUT: complete_type_witness = %complete_type.loc18_1.1 // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%Class // CHECK:STDOUT: .T = // CHECK:STDOUT: .a = %.loc16 // CHECK:STDOUT: .F = %Class.F.decl // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic fn @Class.F(@Class.%T.loc15_13.2: type) { // CHECK:STDOUT: %T.loc17: type = symbolic_binding T, 0 [symbolic = %T.loc17 (constants.%T)] // CHECK:STDOUT: %Class: type = class_type @Class, @Class(%T.loc17) [symbolic = %Class (constants.%Class)] // CHECK:STDOUT: %pattern_type.loc17_8: type = pattern_type %Class [symbolic = %pattern_type.loc17_8 (constants.%pattern_type.466)] // CHECK:STDOUT: %pattern_type.loc17_20: type = pattern_type %T.loc17 [symbolic = %pattern_type.loc17_20 (constants.%pattern_type.51d)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete.loc20_33: = require_complete_type %Class [symbolic = %require_complete.loc20_33 (constants.%require_complete.43d)] // CHECK:STDOUT: %require_complete.loc20_49: = require_complete_type %T.loc17 [symbolic = %require_complete.loc20_49 (constants.%require_complete.944)] // CHECK:STDOUT: // CHECK:STDOUT: fn(%self.param.loc20: @Class.F.%Class (%Class), %n.param.loc20: @Class.F.%T.loc17 (%T)) { // CHECK:STDOUT: !entry: // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @Class(constants.%T) { // CHECK:STDOUT: %T.loc15_13.1 => constants.%T // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete => constants.%require_complete.944 // CHECK:STDOUT: %Class => constants.%Class // CHECK:STDOUT: %Class.elem => constants.%Class.elem // CHECK:STDOUT: %Class.F.type => constants.%Class.F.type // CHECK:STDOUT: %Class.F => constants.%Class.F // CHECK:STDOUT: %struct_type.a => constants.%struct_type.a // CHECK:STDOUT: %complete_type.loc18_1.2 => constants.%complete_type // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @Class.F(constants.%T) { // CHECK:STDOUT: %T.loc17 => constants.%T // CHECK:STDOUT: %Class => constants.%Class // CHECK:STDOUT: %pattern_type.loc17_8 => constants.%pattern_type.466 // CHECK:STDOUT: %pattern_type.loc17_20 => constants.%pattern_type.51d // CHECK:STDOUT: } // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/class/method/method.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/int.carbon // TODO: Add ranges and switch to "--dump-sem-ir-ranges=only". // EXTRA-ARGS: --dump-sem-ir-ranges=if-present // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/class/method/method.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/class/method/method.carbon class Class { fn F[self: Self]() -> i32; fn G[ref self: Self]() -> i32; alias A = F; var k: i32; } fn Class.F[self: Self]() -> i32 { return self.k; } fn Call(c: Class) -> i32 { // TODO: The sem-ir for this call doesn't distinguish the `self` argument from // the explicit arguments. return c.F(); } fn CallAlias(c: Class) -> i32 { return c.A(); } fn CallOnConstBoundMethod() -> i32 { return ({.k = 1} as Class).F(); } fn CallWithRef() -> i32 { var c: Class; return c.G(); } fn CallFThroughPointer(p: Class*) -> i32 { return (*p).F(); } fn CallGThroughPointer(p: Class*) -> i32 { return (*p).G(); } fn Make() -> Class; fn CallFOnInitializingExpr() -> i32 { return Make().F(); } fn CallGOnInitializingExpr() -> i32 { return Make().G(); } // CHECK:STDOUT: --- method.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %Class: type = class_type @Class [concrete] // CHECK:STDOUT: %pattern_type.904: type = pattern_type %Class [concrete] // CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [concrete] // CHECK:STDOUT: %Int.type: type = generic_class_type @Int [concrete] // CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] // CHECK:STDOUT: %Int.generic: %Int.type = struct_value () [concrete] // CHECK:STDOUT: %N: Core.IntLiteral = symbolic_binding N, 0 [symbolic] // CHECK:STDOUT: %i32: type = class_type @Int, @Int(%int_32) [concrete] // CHECK:STDOUT: %.ff5: Core.Form = init_form %i32 [concrete] // CHECK:STDOUT: %pattern_type.7ce: type = pattern_type %i32 [concrete] // CHECK:STDOUT: %Class.F.type: type = fn_type @Class.F [concrete] // CHECK:STDOUT: %Class.F: %Class.F.type = struct_value () [concrete] // CHECK:STDOUT: %Class.G.type: type = fn_type @Class.G [concrete] // CHECK:STDOUT: %Class.G: %Class.G.type = struct_value () [concrete] // CHECK:STDOUT: %Class.elem: type = unbound_element_type %Class, %i32 [concrete] // CHECK:STDOUT: %struct_type.k.0bf: type = struct_type {.k: %i32} [concrete] // CHECK:STDOUT: %complete_type.954: = complete_type_witness %struct_type.k.0bf [concrete] // CHECK:STDOUT: %Copy.type: type = facet_type <@Copy> [concrete] // CHECK:STDOUT: %Int.as.Copy.impl.Op.type.824: type = fn_type @Int.as.Copy.impl.Op, @Int.as.Copy.impl(%N) [symbolic] // CHECK:STDOUT: %Int.as.Copy.impl.Op.9b9: %Int.as.Copy.impl.Op.type.824 = struct_value () [symbolic] // CHECK:STDOUT: %T.67d: type = symbolic_binding T, 0 [symbolic] // CHECK:STDOUT: %Copy.impl_witness.f17: = impl_witness imports.%Copy.impl_witness_table.e76, @Int.as.Copy.impl(%int_32) [concrete] // CHECK:STDOUT: %Int.as.Copy.impl.Op.type.546: type = fn_type @Int.as.Copy.impl.Op, @Int.as.Copy.impl(%int_32) [concrete] // CHECK:STDOUT: %Int.as.Copy.impl.Op.664: %Int.as.Copy.impl.Op.type.546 = struct_value () [concrete] // CHECK:STDOUT: %Copy.facet: %Copy.type = facet_value %i32, (%Copy.impl_witness.f17) [concrete] // CHECK:STDOUT: %Copy.WithSelf.Op.type.081: type = fn_type @Copy.WithSelf.Op, @Copy.WithSelf(%Copy.facet) [concrete] // CHECK:STDOUT: %.8e2: type = fn_type_with_self_type %Copy.WithSelf.Op.type.081, %Copy.facet [concrete] // CHECK:STDOUT: %Int.as.Copy.impl.Op.specific_fn: = specific_function %Int.as.Copy.impl.Op.664, @Int.as.Copy.impl.Op(%int_32) [concrete] // CHECK:STDOUT: %Call.type: type = fn_type @Call [concrete] // CHECK:STDOUT: %Call: %Call.type = struct_value () [concrete] // CHECK:STDOUT: %CallAlias.type: type = fn_type @CallAlias [concrete] // CHECK:STDOUT: %CallAlias: %CallAlias.type = struct_value () [concrete] // CHECK:STDOUT: %CallOnConstBoundMethod.type: type = fn_type @CallOnConstBoundMethod [concrete] // CHECK:STDOUT: %CallOnConstBoundMethod: %CallOnConstBoundMethod.type = struct_value () [concrete] // CHECK:STDOUT: %int_1.5b8: Core.IntLiteral = int_value 1 [concrete] // CHECK:STDOUT: %struct_type.k.240: type = struct_type {.k: Core.IntLiteral} [concrete] // CHECK:STDOUT: %struct: %struct_type.k.240 = struct_value (%int_1.5b8) [concrete] // CHECK:STDOUT: %ImplicitAs.type.cc7: type = generic_interface_type @ImplicitAs [concrete] // CHECK:STDOUT: %ImplicitAs.generic: %ImplicitAs.type.cc7 = struct_value () [concrete] // CHECK:STDOUT: %ImplicitAs.type.e8c: type = facet_type <@ImplicitAs, @ImplicitAs(%i32)> [concrete] // CHECK:STDOUT: %To: Core.IntLiteral = symbolic_binding To, 0 [symbolic] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.4e6: type = fn_type @Core.IntLiteral.as.ImplicitAs.impl.Convert, @Core.IntLiteral.as.ImplicitAs.impl(%To) [symbolic] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.3c2: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.4e6 = struct_value () [symbolic] // CHECK:STDOUT: %ImplicitAs.impl_witness.6bc: = impl_witness imports.%ImplicitAs.impl_witness_table.74f, @Core.IntLiteral.as.ImplicitAs.impl(%int_32) [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.e0d: type = fn_type @Core.IntLiteral.as.ImplicitAs.impl.Convert, @Core.IntLiteral.as.ImplicitAs.impl(%int_32) [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.e0d = struct_value () [concrete] // CHECK:STDOUT: %ImplicitAs.facet: %ImplicitAs.type.e8c = facet_value Core.IntLiteral, (%ImplicitAs.impl_witness.6bc) [concrete] // CHECK:STDOUT: %ImplicitAs.WithSelf.Convert.type.b37: type = fn_type @ImplicitAs.WithSelf.Convert, @ImplicitAs.WithSelf(%i32, %ImplicitAs.facet) [concrete] // CHECK:STDOUT: %.545: type = fn_type_with_self_type %ImplicitAs.WithSelf.Convert.type.b37, %ImplicitAs.facet [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.bound: = bound_method %int_1.5b8, %Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5 [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn: = specific_function %Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5, @Core.IntLiteral.as.ImplicitAs.impl.Convert(%int_32) [concrete] // CHECK:STDOUT: %bound_method: = bound_method %int_1.5b8, %Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn [concrete] // CHECK:STDOUT: %int_1.5d2: %i32 = int_value 1 [concrete] // CHECK:STDOUT: %Class.val: %Class = struct_value (%int_1.5d2) [concrete] // CHECK:STDOUT: %Destroy.type: type = facet_type <@Destroy> [concrete] // CHECK:STDOUT: %Destroy.Op.type: type = fn_type @Destroy.Op [concrete] // CHECK:STDOUT: %Destroy.Op: %Destroy.Op.type = struct_value () [concrete] // CHECK:STDOUT: %CallWithRef.type: type = fn_type @CallWithRef [concrete] // CHECK:STDOUT: %CallWithRef: %CallWithRef.type = struct_value () [concrete] // CHECK:STDOUT: %DefaultOrUnformed.type: type = facet_type <@DefaultOrUnformed> [concrete] // CHECK:STDOUT: %T.as.DefaultOrUnformed.impl.Op.type.6c6: type = fn_type @T.as.DefaultOrUnformed.impl.Op, @T.as.DefaultOrUnformed.impl(%T.67d) [symbolic] // CHECK:STDOUT: %T.as.DefaultOrUnformed.impl.Op.ae1: %T.as.DefaultOrUnformed.impl.Op.type.6c6 = struct_value () [symbolic] // CHECK:STDOUT: %DefaultOrUnformed.impl_witness.6ca: = impl_witness imports.%DefaultOrUnformed.impl_witness_table, @T.as.DefaultOrUnformed.impl(%Class) [concrete] // CHECK:STDOUT: %T.as.DefaultOrUnformed.impl.Op.type.3d0: type = fn_type @T.as.DefaultOrUnformed.impl.Op, @T.as.DefaultOrUnformed.impl(%Class) [concrete] // CHECK:STDOUT: %T.as.DefaultOrUnformed.impl.Op.cda: %T.as.DefaultOrUnformed.impl.Op.type.3d0 = struct_value () [concrete] // CHECK:STDOUT: %DefaultOrUnformed.facet: %DefaultOrUnformed.type = facet_value %Class, (%DefaultOrUnformed.impl_witness.6ca) [concrete] // CHECK:STDOUT: %.cff: Core.Form = init_form %Class [concrete] // CHECK:STDOUT: %T.as.DefaultOrUnformed.impl.Op.specific_fn: = specific_function %T.as.DefaultOrUnformed.impl.Op.cda, @T.as.DefaultOrUnformed.impl.Op(%Class) [concrete] // CHECK:STDOUT: %ptr.8e5: type = ptr_type %Class [concrete] // CHECK:STDOUT: %pattern_type.018: type = pattern_type %ptr.8e5 [concrete] // CHECK:STDOUT: %CallFThroughPointer.type: type = fn_type @CallFThroughPointer [concrete] // CHECK:STDOUT: %CallFThroughPointer: %CallFThroughPointer.type = struct_value () [concrete] // CHECK:STDOUT: %CallGThroughPointer.type: type = fn_type @CallGThroughPointer [concrete] // CHECK:STDOUT: %CallGThroughPointer: %CallGThroughPointer.type = struct_value () [concrete] // CHECK:STDOUT: %Make.type: type = fn_type @Make [concrete] // CHECK:STDOUT: %Make: %Make.type = struct_value () [concrete] // CHECK:STDOUT: %CallFOnInitializingExpr.type: type = fn_type @CallFOnInitializingExpr [concrete] // CHECK:STDOUT: %CallFOnInitializingExpr: %CallFOnInitializingExpr.type = struct_value () [concrete] // CHECK:STDOUT: %CallGOnInitializingExpr.type: type = fn_type @CallGOnInitializingExpr [concrete] // CHECK:STDOUT: %CallGOnInitializingExpr: %CallGOnInitializingExpr.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: .Int = %Core.Int // CHECK:STDOUT: .Copy = %Core.Copy // CHECK:STDOUT: .ImplicitAs = %Core.ImplicitAs // CHECK:STDOUT: .Destroy = %Core.Destroy // CHECK:STDOUT: .DefaultOrUnformed = %Core.DefaultOrUnformed // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: %Core.Int: %Int.type = import_ref Core//prelude/parts/int, Int, loaded [concrete = constants.%Int.generic] // CHECK:STDOUT: %Core.Copy: type = import_ref Core//prelude/parts/copy, Copy, loaded [concrete = constants.%Copy.type] // CHECK:STDOUT: %Core.import_ref.18d: @Int.as.Copy.impl.%Int.as.Copy.impl.Op.type (%Int.as.Copy.impl.Op.type.824) = import_ref Core//prelude/parts/int, loc{{\d+_\d+}}, loaded [symbolic = @Int.as.Copy.impl.%Int.as.Copy.impl.Op (constants.%Int.as.Copy.impl.Op.9b9)] // CHECK:STDOUT: %Copy.impl_witness_table.e76 = impl_witness_table (%Core.import_ref.18d), @Int.as.Copy.impl [concrete] // CHECK:STDOUT: %Core.ImplicitAs: %ImplicitAs.type.cc7 = import_ref Core//prelude/parts/as, ImplicitAs, loaded [concrete = constants.%ImplicitAs.generic] // CHECK:STDOUT: %Core.import_ref.42d: @Core.IntLiteral.as.ImplicitAs.impl.%Core.IntLiteral.as.ImplicitAs.impl.Convert.type (%Core.IntLiteral.as.ImplicitAs.impl.Convert.type.4e6) = import_ref Core//prelude/parts/int, loc{{\d+_\d+}}, loaded [symbolic = @Core.IntLiteral.as.ImplicitAs.impl.%Core.IntLiteral.as.ImplicitAs.impl.Convert (constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.3c2)] // CHECK:STDOUT: %ImplicitAs.impl_witness_table.74f = impl_witness_table (%Core.import_ref.42d), @Core.IntLiteral.as.ImplicitAs.impl [concrete] // CHECK:STDOUT: %Core.Destroy: type = import_ref Core//prelude/parts/destroy, Destroy, loaded [concrete = constants.%Destroy.type] // CHECK:STDOUT: %Core.DefaultOrUnformed: type = import_ref Core//prelude/parts/default, DefaultOrUnformed, loaded [concrete = constants.%DefaultOrUnformed.type] // CHECK:STDOUT: %Core.import_ref.01d: @T.as.DefaultOrUnformed.impl.%T.as.DefaultOrUnformed.impl.Op.type (%T.as.DefaultOrUnformed.impl.Op.type.6c6) = import_ref Core//prelude/parts/default, loc{{\d+_\d+}}, loaded [symbolic = @T.as.DefaultOrUnformed.impl.%T.as.DefaultOrUnformed.impl.Op (constants.%T.as.DefaultOrUnformed.impl.Op.ae1)] // CHECK:STDOUT: %DefaultOrUnformed.impl_witness_table = impl_witness_table (%Core.import_ref.01d), @T.as.DefaultOrUnformed.impl [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .Class = %Class.decl // CHECK:STDOUT: .Call = %Call.decl // CHECK:STDOUT: .CallAlias = %CallAlias.decl // CHECK:STDOUT: .CallOnConstBoundMethod = %CallOnConstBoundMethod.decl // CHECK:STDOUT: .CallWithRef = %CallWithRef.decl // CHECK:STDOUT: .CallFThroughPointer = %CallFThroughPointer.decl // CHECK:STDOUT: .CallGThroughPointer = %CallGThroughPointer.decl // CHECK:STDOUT: .Make = %Make.decl // CHECK:STDOUT: .CallFOnInitializingExpr = %CallFOnInitializingExpr.decl // CHECK:STDOUT: .CallGOnInitializingExpr = %CallGOnInitializingExpr.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %Class.decl: type = class_decl @Class [concrete = constants.%Class] {} {} // CHECK:STDOUT: %Class.F.decl: %Class.F.type = fn_decl @Class.F [concrete = constants.%Class.F] { // CHECK:STDOUT: %self.patt: %pattern_type.904 = value_binding_pattern self [concrete] // CHECK:STDOUT: %self.param_patt: %pattern_type.904 = value_param_pattern %self.patt [concrete] // CHECK:STDOUT: %return.patt: %pattern_type.7ce = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: %pattern_type.7ce = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %i32.loc24: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %.loc24: Core.Form = init_form %i32.loc24 [concrete = constants.%.ff5] // CHECK:STDOUT: %self.param.loc24: %Class = value_param call_param0 // CHECK:STDOUT: %Self.ref.loc24: type = name_ref Self, constants.%Class [concrete = constants.%Class] // CHECK:STDOUT: %self.loc24: %Class = value_binding self, %self.param.loc24 // CHECK:STDOUT: %return.param.loc24: ref %i32 = out_param call_param1 // CHECK:STDOUT: %return.loc24: ref %i32 = return_slot %return.param.loc24 // CHECK:STDOUT: } // CHECK:STDOUT: %Call.decl: %Call.type = fn_decl @Call [concrete = constants.%Call] { // CHECK:STDOUT: %c.patt: %pattern_type.904 = value_binding_pattern c [concrete] // CHECK:STDOUT: %c.param_patt: %pattern_type.904 = value_param_pattern %c.patt [concrete] // CHECK:STDOUT: %return.patt: %pattern_type.7ce = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: %pattern_type.7ce = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %i32: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %.loc28: Core.Form = init_form %i32 [concrete = constants.%.ff5] // CHECK:STDOUT: %c.param: %Class = value_param call_param0 // CHECK:STDOUT: %Class.ref: type = name_ref Class, file.%Class.decl [concrete = constants.%Class] // CHECK:STDOUT: %c: %Class = value_binding c, %c.param // CHECK:STDOUT: %return.param: ref %i32 = out_param call_param1 // CHECK:STDOUT: %return: ref %i32 = return_slot %return.param // CHECK:STDOUT: } // CHECK:STDOUT: %CallAlias.decl: %CallAlias.type = fn_decl @CallAlias [concrete = constants.%CallAlias] { // CHECK:STDOUT: %c.patt: %pattern_type.904 = value_binding_pattern c [concrete] // CHECK:STDOUT: %c.param_patt: %pattern_type.904 = value_param_pattern %c.patt [concrete] // CHECK:STDOUT: %return.patt: %pattern_type.7ce = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: %pattern_type.7ce = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %i32: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %.loc34: Core.Form = init_form %i32 [concrete = constants.%.ff5] // CHECK:STDOUT: %c.param: %Class = value_param call_param0 // CHECK:STDOUT: %Class.ref: type = name_ref Class, file.%Class.decl [concrete = constants.%Class] // CHECK:STDOUT: %c: %Class = value_binding c, %c.param // CHECK:STDOUT: %return.param: ref %i32 = out_param call_param1 // CHECK:STDOUT: %return: ref %i32 = return_slot %return.param // CHECK:STDOUT: } // CHECK:STDOUT: %CallOnConstBoundMethod.decl: %CallOnConstBoundMethod.type = fn_decl @CallOnConstBoundMethod [concrete = constants.%CallOnConstBoundMethod] { // CHECK:STDOUT: %return.patt: %pattern_type.7ce = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: %pattern_type.7ce = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %i32: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %.loc38: Core.Form = init_form %i32 [concrete = constants.%.ff5] // CHECK:STDOUT: %return.param: ref %i32 = out_param call_param0 // CHECK:STDOUT: %return: ref %i32 = return_slot %return.param // CHECK:STDOUT: } // CHECK:STDOUT: %CallWithRef.decl: %CallWithRef.type = fn_decl @CallWithRef [concrete = constants.%CallWithRef] { // CHECK:STDOUT: %return.patt: %pattern_type.7ce = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: %pattern_type.7ce = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %i32: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %.loc42: Core.Form = init_form %i32 [concrete = constants.%.ff5] // CHECK:STDOUT: %return.param: ref %i32 = out_param call_param0 // CHECK:STDOUT: %return: ref %i32 = return_slot %return.param // CHECK:STDOUT: } // CHECK:STDOUT: %CallFThroughPointer.decl: %CallFThroughPointer.type = fn_decl @CallFThroughPointer [concrete = constants.%CallFThroughPointer] { // CHECK:STDOUT: %p.patt: %pattern_type.018 = value_binding_pattern p [concrete] // CHECK:STDOUT: %p.param_patt: %pattern_type.018 = value_param_pattern %p.patt [concrete] // CHECK:STDOUT: %return.patt: %pattern_type.7ce = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: %pattern_type.7ce = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %i32: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %.loc47_38: Core.Form = init_form %i32 [concrete = constants.%.ff5] // CHECK:STDOUT: %p.param: %ptr.8e5 = value_param call_param0 // CHECK:STDOUT: %.loc47_32: type = splice_block %ptr [concrete = constants.%ptr.8e5] { // CHECK:STDOUT: %Class.ref: type = name_ref Class, file.%Class.decl [concrete = constants.%Class] // CHECK:STDOUT: %ptr: type = ptr_type %Class.ref [concrete = constants.%ptr.8e5] // CHECK:STDOUT: } // CHECK:STDOUT: %p: %ptr.8e5 = value_binding p, %p.param // CHECK:STDOUT: %return.param: ref %i32 = out_param call_param1 // CHECK:STDOUT: %return: ref %i32 = return_slot %return.param // CHECK:STDOUT: } // CHECK:STDOUT: %CallGThroughPointer.decl: %CallGThroughPointer.type = fn_decl @CallGThroughPointer [concrete = constants.%CallGThroughPointer] { // CHECK:STDOUT: %p.patt: %pattern_type.018 = value_binding_pattern p [concrete] // CHECK:STDOUT: %p.param_patt: %pattern_type.018 = value_param_pattern %p.patt [concrete] // CHECK:STDOUT: %return.patt: %pattern_type.7ce = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: %pattern_type.7ce = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %i32: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %.loc51_38: Core.Form = init_form %i32 [concrete = constants.%.ff5] // CHECK:STDOUT: %p.param: %ptr.8e5 = value_param call_param0 // CHECK:STDOUT: %.loc51_32: type = splice_block %ptr [concrete = constants.%ptr.8e5] { // CHECK:STDOUT: %Class.ref: type = name_ref Class, file.%Class.decl [concrete = constants.%Class] // CHECK:STDOUT: %ptr: type = ptr_type %Class.ref [concrete = constants.%ptr.8e5] // CHECK:STDOUT: } // CHECK:STDOUT: %p: %ptr.8e5 = value_binding p, %p.param // CHECK:STDOUT: %return.param: ref %i32 = out_param call_param1 // CHECK:STDOUT: %return: ref %i32 = return_slot %return.param // CHECK:STDOUT: } // CHECK:STDOUT: %Make.decl: %Make.type = fn_decl @Make [concrete = constants.%Make] { // CHECK:STDOUT: %return.patt: %pattern_type.904 = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: %pattern_type.904 = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %Class.ref: type = name_ref Class, file.%Class.decl [concrete = constants.%Class] // CHECK:STDOUT: %.loc55: Core.Form = init_form %Class.ref [concrete = constants.%.cff] // CHECK:STDOUT: %return.param: ref %Class = out_param call_param0 // CHECK:STDOUT: %return: ref %Class = return_slot %return.param // CHECK:STDOUT: } // CHECK:STDOUT: %CallFOnInitializingExpr.decl: %CallFOnInitializingExpr.type = fn_decl @CallFOnInitializingExpr [concrete = constants.%CallFOnInitializingExpr] { // CHECK:STDOUT: %return.patt: %pattern_type.7ce = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: %pattern_type.7ce = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %i32: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %.loc57: Core.Form = init_form %i32 [concrete = constants.%.ff5] // CHECK:STDOUT: %return.param: ref %i32 = out_param call_param0 // CHECK:STDOUT: %return: ref %i32 = return_slot %return.param // CHECK:STDOUT: } // CHECK:STDOUT: %CallGOnInitializingExpr.decl: %CallGOnInitializingExpr.type = fn_decl @CallGOnInitializingExpr [concrete = constants.%CallGOnInitializingExpr] { // CHECK:STDOUT: %return.patt: %pattern_type.7ce = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: %pattern_type.7ce = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %i32: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %.loc61: Core.Form = init_form %i32 [concrete = constants.%.ff5] // CHECK:STDOUT: %return.param: ref %i32 = out_param call_param0 // CHECK:STDOUT: %return: ref %i32 = return_slot %return.param // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @Class { // CHECK:STDOUT: %Class.F.decl: %Class.F.type = fn_decl @Class.F [concrete = constants.%Class.F] { // CHECK:STDOUT: %self.patt: %pattern_type.904 = value_binding_pattern self [concrete] // CHECK:STDOUT: %self.param_patt: %pattern_type.904 = value_param_pattern %self.patt [concrete] // CHECK:STDOUT: %return.patt: %pattern_type.7ce = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: %pattern_type.7ce = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %i32.loc16: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %.loc16: Core.Form = init_form %i32.loc16 [concrete = constants.%.ff5] // CHECK:STDOUT: %self.param.loc16: %Class = value_param call_param0 // CHECK:STDOUT: %Self.ref.loc16: type = name_ref Self, constants.%Class [concrete = constants.%Class] // CHECK:STDOUT: %self.loc16: %Class = value_binding self, %self.param.loc16 // CHECK:STDOUT: %return.param.loc16: ref %i32 = out_param call_param1 // CHECK:STDOUT: %return.loc16: ref %i32 = return_slot %return.param.loc16 // CHECK:STDOUT: } // CHECK:STDOUT: %Class.G.decl: %Class.G.type = fn_decl @Class.G [concrete = constants.%Class.G] { // CHECK:STDOUT: %self.patt: %pattern_type.904 = ref_binding_pattern self [concrete] // CHECK:STDOUT: %self.param_patt: %pattern_type.904 = ref_param_pattern %self.patt [concrete] // CHECK:STDOUT: %return.patt: %pattern_type.7ce = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: %pattern_type.7ce = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %i32: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %.loc17: Core.Form = init_form %i32 [concrete = constants.%.ff5] // CHECK:STDOUT: %self.param: ref %Class = ref_param call_param0 // CHECK:STDOUT: %Self.ref: type = name_ref Self, constants.%Class [concrete = constants.%Class] // CHECK:STDOUT: %self: ref %Class = ref_binding self, %self.param // CHECK:STDOUT: %return.param: ref %i32 = out_param call_param1 // CHECK:STDOUT: %return: ref %i32 = return_slot %return.param // CHECK:STDOUT: } // CHECK:STDOUT: %F.ref: %Class.F.type = name_ref F, %Class.F.decl [concrete = constants.%Class.F] // CHECK:STDOUT: %A: %Class.F.type = alias_binding A, %Class.F.decl [concrete = constants.%Class.F] // CHECK:STDOUT: %i32: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %.loc21: %Class.elem = field_decl k, element0 [concrete] // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%struct_type.k.0bf [concrete = constants.%complete_type.954] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%Class // CHECK:STDOUT: .F = %Class.F.decl // CHECK:STDOUT: .G = %Class.G.decl // CHECK:STDOUT: .A = %A // CHECK:STDOUT: .k = %.loc21 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @Class.F(%self.param.loc24: %Class) -> out %return.param.loc24: %i32 { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %self.ref: %Class = name_ref self, %self.loc24 // CHECK:STDOUT: %k.ref: %Class.elem = name_ref k, @Class.%.loc21 [concrete = @Class.%.loc21] // CHECK:STDOUT: %.loc25_14.1: ref %i32 = class_element_access %self.ref, element0 // CHECK:STDOUT: %.loc25_14.2: %i32 = acquire_value %.loc25_14.1 // CHECK:STDOUT: %impl.elem0: %.8e2 = impl_witness_access constants.%Copy.impl_witness.f17, element0 [concrete = constants.%Int.as.Copy.impl.Op.664] // CHECK:STDOUT: %bound_method.loc25_14.1: = bound_method %.loc25_14.2, %impl.elem0 // CHECK:STDOUT: %specific_fn: = specific_function %impl.elem0, @Int.as.Copy.impl.Op(constants.%int_32) [concrete = constants.%Int.as.Copy.impl.Op.specific_fn] // CHECK:STDOUT: %bound_method.loc25_14.2: = bound_method %.loc25_14.2, %specific_fn // CHECK:STDOUT: %Int.as.Copy.impl.Op.call: init %i32 = call %bound_method.loc25_14.2(%.loc25_14.2) // CHECK:STDOUT: return %Int.as.Copy.impl.Op.call // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @Class.G(%self.param: ref %Class) -> out %return.param: %i32; // CHECK:STDOUT: // CHECK:STDOUT: fn @Call(%c.param: %Class) -> out %return.param: %i32 { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %c.ref: %Class = name_ref c, %c // CHECK:STDOUT: %F.ref: %Class.F.type = name_ref F, @Class.%Class.F.decl [concrete = constants.%Class.F] // CHECK:STDOUT: %Class.F.bound: = bound_method %c.ref, %F.ref // CHECK:STDOUT: %Class.F.call: init %i32 = call %Class.F.bound(%c.ref) // CHECK:STDOUT: return %Class.F.call // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @CallAlias(%c.param: %Class) -> out %return.param: %i32 { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %c.ref: %Class = name_ref c, %c // CHECK:STDOUT: %A.ref: %Class.F.type = name_ref A, @Class.%A [concrete = constants.%Class.F] // CHECK:STDOUT: %Class.F.bound: = bound_method %c.ref, %A.ref // CHECK:STDOUT: %Class.F.call: init %i32 = call %Class.F.bound(%c.ref) // CHECK:STDOUT: return %Class.F.call // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @CallOnConstBoundMethod() -> out %return.param: %i32 { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %int_1: Core.IntLiteral = int_value 1 [concrete = constants.%int_1.5b8] // CHECK:STDOUT: %.loc39_18.1: %struct_type.k.240 = struct_literal (%int_1) [concrete = constants.%struct] // CHECK:STDOUT: %Class.ref: type = name_ref Class, file.%Class.decl [concrete = constants.%Class] // CHECK:STDOUT: %impl.elem0: %.545 = impl_witness_access constants.%ImplicitAs.impl_witness.6bc, element0 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5] // CHECK:STDOUT: %bound_method.loc39_18.1: = bound_method %int_1, %impl.elem0 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.bound] // CHECK:STDOUT: %specific_fn: = specific_function %impl.elem0, @Core.IntLiteral.as.ImplicitAs.impl.Convert(constants.%int_32) [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn] // CHECK:STDOUT: %bound_method.loc39_18.2: = bound_method %int_1, %specific_fn [concrete = constants.%bound_method] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.call: init %i32 = call %bound_method.loc39_18.2(%int_1) [concrete = constants.%int_1.5d2] // CHECK:STDOUT: %.loc39_18.2: init %i32 = converted %int_1, %Core.IntLiteral.as.ImplicitAs.impl.Convert.call [concrete = constants.%int_1.5d2] // CHECK:STDOUT: %.loc39_18.3: ref %Class = temporary_storage // CHECK:STDOUT: %.loc39_18.4: ref %i32 = class_element_access %.loc39_18.3, element0 // CHECK:STDOUT: %.loc39_18.5: init %i32 to %.loc39_18.4 = in_place_init %.loc39_18.2 [concrete = constants.%int_1.5d2] // CHECK:STDOUT: %.loc39_18.6: init %Class to %.loc39_18.3 = class_init (%.loc39_18.5) [concrete = constants.%Class.val] // CHECK:STDOUT: %.loc39_20.1: init %Class = converted %.loc39_18.1, %.loc39_18.6 [concrete = constants.%Class.val] // CHECK:STDOUT: %.loc39_20.2: ref %Class = temporary %.loc39_18.3, %.loc39_20.1 // CHECK:STDOUT: %F.ref: %Class.F.type = name_ref F, @Class.%Class.F.decl [concrete = constants.%Class.F] // CHECK:STDOUT: %Class.F.bound: = bound_method %.loc39_20.2, %F.ref // CHECK:STDOUT: %.loc39_20.3: %Class = acquire_value %.loc39_20.2 // CHECK:STDOUT: %Class.F.call: init %i32 = call %Class.F.bound(%.loc39_20.3) // CHECK:STDOUT: %Destroy.Op.bound: = bound_method %.loc39_20.2, constants.%Destroy.Op // CHECK:STDOUT: %Destroy.Op.call: init %empty_tuple.type = call %Destroy.Op.bound(%.loc39_20.2) // CHECK:STDOUT: return %Class.F.call // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @Destroy.Op(%self.param: ref %Class) = "no_op"; // CHECK:STDOUT: // CHECK:STDOUT: fn @CallWithRef() -> out %return.param: %i32 { // CHECK:STDOUT: !entry: // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %c.patt: %pattern_type.904 = ref_binding_pattern c [concrete] // CHECK:STDOUT: %c.var_patt: %pattern_type.904 = var_pattern %c.patt [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %c.var: ref %Class = var %c.var_patt // CHECK:STDOUT: %DefaultOrUnformed.facet: %DefaultOrUnformed.type = facet_value constants.%Class, (constants.%DefaultOrUnformed.impl_witness.6ca) [concrete = constants.%DefaultOrUnformed.facet] // CHECK:STDOUT: %.loc43_15.1: %DefaultOrUnformed.type = converted constants.%Class, %DefaultOrUnformed.facet [concrete = constants.%DefaultOrUnformed.facet] // CHECK:STDOUT: %as_type: type = facet_access_type %.loc43_15.1 [concrete = constants.%Class] // CHECK:STDOUT: %.loc43_15.2: type = converted %.loc43_15.1, %as_type [concrete = constants.%Class] // CHECK:STDOUT: %T.as.DefaultOrUnformed.impl.Op.specific_fn: = specific_function constants.%T.as.DefaultOrUnformed.impl.Op.cda, @T.as.DefaultOrUnformed.impl.Op(constants.%Class) [concrete = constants.%T.as.DefaultOrUnformed.impl.Op.specific_fn] // CHECK:STDOUT: %.loc43_3: ref %Class = splice_block %c.var {} // CHECK:STDOUT: %T.as.DefaultOrUnformed.impl.Op.call: init %Class to %.loc43_3 = call %T.as.DefaultOrUnformed.impl.Op.specific_fn() // CHECK:STDOUT: assign %c.var, %T.as.DefaultOrUnformed.impl.Op.call // CHECK:STDOUT: %Class.ref: type = name_ref Class, file.%Class.decl [concrete = constants.%Class] // CHECK:STDOUT: %c: ref %Class = ref_binding c, %c.var // CHECK:STDOUT: %c.ref: ref %Class = name_ref c, %c // CHECK:STDOUT: %G.ref: %Class.G.type = name_ref G, @Class.%Class.G.decl [concrete = constants.%Class.G] // CHECK:STDOUT: %Class.G.bound: = bound_method %c.ref, %G.ref // CHECK:STDOUT: %Class.G.call: init %i32 = call %Class.G.bound(%c.ref) // CHECK:STDOUT: %Destroy.Op.bound: = bound_method %c.var, constants.%Destroy.Op // CHECK:STDOUT: %Destroy.Op.call: init %empty_tuple.type = call %Destroy.Op.bound(%c.var) // CHECK:STDOUT: return %Class.G.call // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @CallFThroughPointer(%p.param: %ptr.8e5) -> out %return.param: %i32 { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %p.ref: %ptr.8e5 = name_ref p, %p // CHECK:STDOUT: %.loc48_11.1: ref %Class = deref %p.ref // CHECK:STDOUT: %F.ref: %Class.F.type = name_ref F, @Class.%Class.F.decl [concrete = constants.%Class.F] // CHECK:STDOUT: %Class.F.bound: = bound_method %.loc48_11.1, %F.ref // CHECK:STDOUT: %.loc48_11.2: %Class = acquire_value %.loc48_11.1 // CHECK:STDOUT: %Class.F.call: init %i32 = call %Class.F.bound(%.loc48_11.2) // CHECK:STDOUT: return %Class.F.call // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @CallGThroughPointer(%p.param: %ptr.8e5) -> out %return.param: %i32 { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %p.ref: %ptr.8e5 = name_ref p, %p // CHECK:STDOUT: %.loc52: ref %Class = deref %p.ref // CHECK:STDOUT: %G.ref: %Class.G.type = name_ref G, @Class.%Class.G.decl [concrete = constants.%Class.G] // CHECK:STDOUT: %Class.G.bound: = bound_method %.loc52, %G.ref // CHECK:STDOUT: %Class.G.call: init %i32 = call %Class.G.bound(%.loc52) // CHECK:STDOUT: return %Class.G.call // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @Make() -> out %return.param: %Class; // CHECK:STDOUT: // CHECK:STDOUT: fn @CallFOnInitializingExpr() -> out %return.param: %i32 { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %Make.ref: %Make.type = name_ref Make, file.%Make.decl [concrete = constants.%Make] // CHECK:STDOUT: %.loc58_15.1: ref %Class = temporary_storage // CHECK:STDOUT: %Make.call: init %Class to %.loc58_15.1 = call %Make.ref() // CHECK:STDOUT: %.loc58_15.2: ref %Class = temporary %.loc58_15.1, %Make.call // CHECK:STDOUT: %F.ref: %Class.F.type = name_ref F, @Class.%Class.F.decl [concrete = constants.%Class.F] // CHECK:STDOUT: %Class.F.bound: = bound_method %.loc58_15.2, %F.ref // CHECK:STDOUT: %.loc58_15.3: %Class = acquire_value %.loc58_15.2 // CHECK:STDOUT: %Class.F.call: init %i32 = call %Class.F.bound(%.loc58_15.3) // CHECK:STDOUT: %Destroy.Op.bound: = bound_method %.loc58_15.2, constants.%Destroy.Op // CHECK:STDOUT: %Destroy.Op.call: init %empty_tuple.type = call %Destroy.Op.bound(%.loc58_15.2) // CHECK:STDOUT: return %Class.F.call // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @CallGOnInitializingExpr() -> out %return.param: %i32 { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %Make.ref: %Make.type = name_ref Make, file.%Make.decl [concrete = constants.%Make] // CHECK:STDOUT: %.loc62_15.1: ref %Class = temporary_storage // CHECK:STDOUT: %Make.call: init %Class to %.loc62_15.1 = call %Make.ref() // CHECK:STDOUT: %.loc62_15.2: ref %Class = temporary %.loc62_15.1, %Make.call // CHECK:STDOUT: %G.ref: %Class.G.type = name_ref G, @Class.%Class.G.decl [concrete = constants.%Class.G] // CHECK:STDOUT: %Class.G.bound: = bound_method %.loc62_15.2, %G.ref // CHECK:STDOUT: %Class.G.call: init %i32 = call %Class.G.bound(%.loc62_15.2) // CHECK:STDOUT: %Destroy.Op.bound: = bound_method %.loc62_15.2, constants.%Destroy.Op // CHECK:STDOUT: %Destroy.Op.call: init %empty_tuple.type = call %Destroy.Op.bound(%.loc62_15.2) // CHECK:STDOUT: return %Class.G.call // CHECK:STDOUT: } // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/class/method/static_method.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/int.carbon // TODO: Add ranges and switch to "--dump-sem-ir-ranges=only". // EXTRA-ARGS: --dump-sem-ir-ranges=if-present // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/class/method/static_method.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/class/method/static_method.carbon class Class { fn F() -> i32; } fn Run() -> i32 { var c: Class; return c.F(); } // CHECK:STDOUT: --- static_method.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %Class: type = class_type @Class [concrete] // CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [concrete] // CHECK:STDOUT: %Int.type: type = generic_class_type @Int [concrete] // CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] // CHECK:STDOUT: %Int.generic: %Int.type = struct_value () [concrete] // CHECK:STDOUT: %i32: type = class_type @Int, @Int(%int_32) [concrete] // CHECK:STDOUT: %.ff5: Core.Form = init_form %i32 [concrete] // CHECK:STDOUT: %pattern_type.7ce: type = pattern_type %i32 [concrete] // CHECK:STDOUT: %Class.F.type: type = fn_type @Class.F [concrete] // CHECK:STDOUT: %Class.F: %Class.F.type = struct_value () [concrete] // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %complete_type.357: = complete_type_witness %empty_struct_type [concrete] // CHECK:STDOUT: %Run.type: type = fn_type @Run [concrete] // CHECK:STDOUT: %Run: %Run.type = struct_value () [concrete] // CHECK:STDOUT: %pattern_type.904: type = pattern_type %Class [concrete] // CHECK:STDOUT: %DefaultOrUnformed.type: type = facet_type <@DefaultOrUnformed> [concrete] // CHECK:STDOUT: %T: type = symbolic_binding T, 0 [symbolic] // CHECK:STDOUT: %T.as.DefaultOrUnformed.impl.Op.type.6c6: type = fn_type @T.as.DefaultOrUnformed.impl.Op, @T.as.DefaultOrUnformed.impl(%T) [symbolic] // CHECK:STDOUT: %T.as.DefaultOrUnformed.impl.Op.ae1: %T.as.DefaultOrUnformed.impl.Op.type.6c6 = struct_value () [symbolic] // CHECK:STDOUT: %DefaultOrUnformed.impl_witness.6ca: = impl_witness imports.%DefaultOrUnformed.impl_witness_table, @T.as.DefaultOrUnformed.impl(%Class) [concrete] // CHECK:STDOUT: %T.as.DefaultOrUnformed.impl.Op.type.3d0: type = fn_type @T.as.DefaultOrUnformed.impl.Op, @T.as.DefaultOrUnformed.impl(%Class) [concrete] // CHECK:STDOUT: %T.as.DefaultOrUnformed.impl.Op.cda: %T.as.DefaultOrUnformed.impl.Op.type.3d0 = struct_value () [concrete] // CHECK:STDOUT: %DefaultOrUnformed.facet: %DefaultOrUnformed.type = facet_value %Class, (%DefaultOrUnformed.impl_witness.6ca) [concrete] // CHECK:STDOUT: %T.as.DefaultOrUnformed.impl.Op.specific_fn: = specific_function %T.as.DefaultOrUnformed.impl.Op.cda, @T.as.DefaultOrUnformed.impl.Op(%Class) [concrete] // CHECK:STDOUT: %Destroy.type: type = facet_type <@Destroy> [concrete] // CHECK:STDOUT: %Destroy.Op.type: type = fn_type @Destroy.Op [concrete] // CHECK:STDOUT: %Destroy.Op: %Destroy.Op.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: .Int = %Core.Int // CHECK:STDOUT: .DefaultOrUnformed = %Core.DefaultOrUnformed // CHECK:STDOUT: .Destroy = %Core.Destroy // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: %Core.Int: %Int.type = import_ref Core//prelude/parts/int, Int, loaded [concrete = constants.%Int.generic] // CHECK:STDOUT: %Core.DefaultOrUnformed: type = import_ref Core//prelude/parts/default, DefaultOrUnformed, loaded [concrete = constants.%DefaultOrUnformed.type] // CHECK:STDOUT: %Core.import_ref.01d: @T.as.DefaultOrUnformed.impl.%T.as.DefaultOrUnformed.impl.Op.type (%T.as.DefaultOrUnformed.impl.Op.type.6c6) = import_ref Core//prelude/parts/default, loc{{\d+_\d+}}, loaded [symbolic = @T.as.DefaultOrUnformed.impl.%T.as.DefaultOrUnformed.impl.Op (constants.%T.as.DefaultOrUnformed.impl.Op.ae1)] // CHECK:STDOUT: %DefaultOrUnformed.impl_witness_table = impl_witness_table (%Core.import_ref.01d), @T.as.DefaultOrUnformed.impl [concrete] // CHECK:STDOUT: %Core.Destroy: type = import_ref Core//prelude/parts/destroy, Destroy, loaded [concrete = constants.%Destroy.type] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .Class = %Class.decl // CHECK:STDOUT: .Run = %Run.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %Class.decl: type = class_decl @Class [concrete = constants.%Class] {} {} // CHECK:STDOUT: %Run.decl: %Run.type = fn_decl @Run [concrete = constants.%Run] { // CHECK:STDOUT: %return.patt: %pattern_type.7ce = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: %pattern_type.7ce = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %i32: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %.loc19: Core.Form = init_form %i32 [concrete = constants.%.ff5] // CHECK:STDOUT: %return.param: ref %i32 = out_param call_param0 // CHECK:STDOUT: %return: ref %i32 = return_slot %return.param // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @Class { // CHECK:STDOUT: %Class.F.decl: %Class.F.type = fn_decl @Class.F [concrete = constants.%Class.F] { // CHECK:STDOUT: %return.patt: %pattern_type.7ce = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: %pattern_type.7ce = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %i32: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %.loc16: Core.Form = init_form %i32 [concrete = constants.%.ff5] // CHECK:STDOUT: %return.param: ref %i32 = out_param call_param0 // CHECK:STDOUT: %return: ref %i32 = return_slot %return.param // CHECK:STDOUT: } // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type.357] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%Class // CHECK:STDOUT: .F = %Class.F.decl // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @Class.F() -> out %return.param: %i32; // CHECK:STDOUT: // CHECK:STDOUT: fn @Run() -> out %return.param: %i32 { // CHECK:STDOUT: !entry: // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %c.patt: %pattern_type.904 = ref_binding_pattern c [concrete] // CHECK:STDOUT: %c.var_patt: %pattern_type.904 = var_pattern %c.patt [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %c.var: ref %Class = var %c.var_patt // CHECK:STDOUT: %DefaultOrUnformed.facet: %DefaultOrUnformed.type = facet_value constants.%Class, (constants.%DefaultOrUnformed.impl_witness.6ca) [concrete = constants.%DefaultOrUnformed.facet] // CHECK:STDOUT: %.loc20_15.1: %DefaultOrUnformed.type = converted constants.%Class, %DefaultOrUnformed.facet [concrete = constants.%DefaultOrUnformed.facet] // CHECK:STDOUT: %as_type: type = facet_access_type %.loc20_15.1 [concrete = constants.%Class] // CHECK:STDOUT: %.loc20_15.2: type = converted %.loc20_15.1, %as_type [concrete = constants.%Class] // CHECK:STDOUT: %T.as.DefaultOrUnformed.impl.Op.specific_fn: = specific_function constants.%T.as.DefaultOrUnformed.impl.Op.cda, @T.as.DefaultOrUnformed.impl.Op(constants.%Class) [concrete = constants.%T.as.DefaultOrUnformed.impl.Op.specific_fn] // CHECK:STDOUT: %.loc20_3: ref %Class = splice_block %c.var {} // CHECK:STDOUT: %T.as.DefaultOrUnformed.impl.Op.call: init %Class to %.loc20_3 = call %T.as.DefaultOrUnformed.impl.Op.specific_fn() // CHECK:STDOUT: assign %c.var, %T.as.DefaultOrUnformed.impl.Op.call // CHECK:STDOUT: %Class.ref: type = name_ref Class, file.%Class.decl [concrete = constants.%Class] // CHECK:STDOUT: %c: ref %Class = ref_binding c, %c.var // CHECK:STDOUT: %c.ref: ref %Class = name_ref c, %c // CHECK:STDOUT: %F.ref: %Class.F.type = name_ref F, @Class.%Class.F.decl [concrete = constants.%Class.F] // CHECK:STDOUT: %Class.F.call: init %i32 = call %F.ref() // CHECK:STDOUT: %Destroy.Op.bound: = bound_method %c.var, constants.%Destroy.Op // CHECK:STDOUT: %Destroy.Op.call: init %empty_tuple.type = call %Destroy.Op.bound(%c.var) // CHECK:STDOUT: return %Class.F.call // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @Destroy.Op(%self.param: ref %Class) = "no_op"; // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/class/name_poisoning.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/none.carbon // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/class/name_poisoning.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/class/name_poisoning.carbon // --- no_poison.carbon library "[[@TEST_NAME]]"; class C; // `N.F` uses `N.C` and not `package.C`. namespace N; class N.C {} fn N.F(x: C); fn TestCall(x: N.C) { // `N.F` accepts an `N.C` not a `package.C`. N.F(x); } // --- poison.carbon library "[[@TEST_NAME]]"; class C; namespace N; // Use `package.C` and poison `N.C`. fn N.F(x: C); // --- fail_declare_after_poison.carbon library "[[@TEST_NAME]]"; class C; namespace N; // Use `package.C` and poison `N.C`. // CHECK:STDERR: fail_declare_after_poison.carbon:[[@LINE+3]]:11: error: name `C` used before it was declared [NameUseBeforeDecl] // CHECK:STDERR: fn N.F(x: C); // CHECK:STDERR: ^ fn N.F(x: C); // Failure: `N.C` declared after it was poisoned. // CHECK:STDERR: fail_declare_after_poison.carbon:[[@LINE+4]]:9: note: declared here [NameUseBeforeDeclNote] // CHECK:STDERR: class N.C; // CHECK:STDERR: ^ // CHECK:STDERR: class N.C; // --- fail_use_poison.carbon library "[[@TEST_NAME]]"; class C; namespace N; // Use `package.C` and poison `N.C`. fn N.F1() -> C; // Use `N.C` which was poisoned and not declared. // CHECK:STDERR: fail_use_poison.carbon:[[@LINE+4]]:14: error: member name `C` not found in `N` [MemberNameNotFoundInInstScope] // CHECK:STDERR: fn N.F2() -> N.C; // CHECK:STDERR: ^~~ // CHECK:STDERR: fn N.F2() -> N.C; // --- fail_use_declaration_after_poison.carbon library "[[@TEST_NAME]]"; class C; namespace N; // Use `package.C` and poison `N.C`. // CHECK:STDERR: fail_use_declaration_after_poison.carbon:[[@LINE+3]]:12: error: name `C` used before it was declared [NameUseBeforeDecl] // CHECK:STDERR: fn N.F1(x: C); // CHECK:STDERR: ^ fn N.F1(x: C); // Failure: N.C declared after it was poisoned. // CHECK:STDERR: fail_use_declaration_after_poison.carbon:[[@LINE+4]]:9: note: declared here [NameUseBeforeDeclNote] // CHECK:STDERR: class N.C; // CHECK:STDERR: ^ // CHECK:STDERR: class N.C; // Failure: `N.C` used after declaration failed. // CHECK:STDERR: fail_use_declaration_after_poison.carbon:[[@LINE+4]]:12: error: member name `C` not found in `N` [MemberNameNotFoundInInstScope] // CHECK:STDERR: fn N.F2(x: N.C); // CHECK:STDERR: ^~~ // CHECK:STDERR: fn N.F2(x: N.C); // --- fail_alias.carbon library "[[@TEST_NAME]]"; class C; namespace N; // CHECK:STDERR: fail_alias.carbon:[[@LINE+7]]:13: error: name `C` used before it was declared [NameUseBeforeDecl] // CHECK:STDERR: alias N.C = C; // CHECK:STDERR: ^ // CHECK:STDERR: fail_alias.carbon:[[@LINE+4]]:9: note: declared here [NameUseBeforeDeclNote] // CHECK:STDERR: alias N.C = C; // CHECK:STDERR: ^ // CHECK:STDERR: alias N.C = C; // --- fail_poison_multiple_scopes.carbon library "[[@TEST_NAME]]"; class C1; class C2 { class C3 { class C4 { // Use `package.C1` and poison: // * `C2.C1` // * `C2.C3.C1` // * `C2.C3.C4.C1` // CHECK:STDERR: fail_poison_multiple_scopes.carbon:[[@LINE+3]]:15: error: name `C1` used before it was declared [NameUseBeforeDecl] // CHECK:STDERR: fn F(x: C1); // CHECK:STDERR: ^~ fn F(x: C1); // CHECK:STDERR: fail_poison_multiple_scopes.carbon:[[@LINE+7]]:13: note: declared here [NameUseBeforeDeclNote] // CHECK:STDERR: class C1; // CHECK:STDERR: ^~ // CHECK:STDERR: // CHECK:STDERR: fail_poison_multiple_scopes.carbon:[[@LINE-6]]:15: error: name `C1` used before it was declared [NameUseBeforeDecl] // CHECK:STDERR: fn F(x: C1); // CHECK:STDERR: ^~ class C1; } // CHECK:STDERR: fail_poison_multiple_scopes.carbon:[[@LINE+7]]:11: note: declared here [NameUseBeforeDeclNote] // CHECK:STDERR: class C1; // CHECK:STDERR: ^~ // CHECK:STDERR: // CHECK:STDERR: fail_poison_multiple_scopes.carbon:[[@LINE-15]]:15: error: name `C1` used before it was declared [NameUseBeforeDecl] // CHECK:STDERR: fn F(x: C1); // CHECK:STDERR: ^~ class C1; } // CHECK:STDERR: fail_poison_multiple_scopes.carbon:[[@LINE+4]]:9: note: declared here [NameUseBeforeDeclNote] // CHECK:STDERR: class C1; // CHECK:STDERR: ^~ // CHECK:STDERR: class C1; } // --- ignored_poison_in_import.carbon library "[[@TEST_NAME]]"; import library "poison"; // This doesn't fail. class N.C; // --- poison.impl.carbon impl library "[[@TEST_NAME]]"; // TODO: #4622 This should fail since `N.C` was poisoned in the api. class N.C {} // --- fail_poison_when_lookup_fails.carbon library "[[@TEST_NAME]]"; namespace N; // `package.C` and `N.C` poisoned when not found. // CHECK:STDERR: fail_poison_when_lookup_fails.carbon:[[@LINE+7]]:11: error: name `C` not found [NameNotFound] // CHECK:STDERR: fn N.F(x: C); // CHECK:STDERR: ^ // CHECK:STDERR: // CHECK:STDERR: fail_poison_when_lookup_fails.carbon:[[@LINE+3]]:11: error: name `C` used before it was declared [NameUseBeforeDecl] // CHECK:STDERR: fn N.F(x: C); // CHECK:STDERR: ^ fn N.F(x: C); // TODO: We should ideally only produce one diagnostic here. // CHECK:STDERR: fail_poison_when_lookup_fails.carbon:[[@LINE+7]]:7: note: declared here [NameUseBeforeDeclNote] // CHECK:STDERR: class C; // CHECK:STDERR: ^ // CHECK:STDERR: // CHECK:STDERR: fail_poison_when_lookup_fails.carbon:[[@LINE-7]]:11: error: name `C` used before it was declared [NameUseBeforeDecl] // CHECK:STDERR: fn N.F(x: C); // CHECK:STDERR: ^ class C; // CHECK:STDERR: fail_poison_when_lookup_fails.carbon:[[@LINE+4]]:9: note: declared here [NameUseBeforeDeclNote] // CHECK:STDERR: class N.C; // CHECK:STDERR: ^ // CHECK:STDERR: class N.C; // --- fail_poison_with_lexical_result.carbon library "[[@TEST_NAME]]"; fn F() { class C1 {} class C2 { // CHECK:STDERR: fail_poison_with_lexical_result.carbon:[[@LINE+3]]:12: error: name `C1` used before it was declared [NameUseBeforeDecl] // CHECK:STDERR: var v: C1; // CHECK:STDERR: ^~ var v: C1; // CHECK:STDERR: fail_poison_with_lexical_result.carbon:[[@LINE+4]]:11: note: declared here [NameUseBeforeDeclNote] // CHECK:STDERR: class C1; // CHECK:STDERR: ^~ // CHECK:STDERR: class C1; } } // --- fail_declare_data_member_after_poison.carbon library "[[@TEST_NAME]]"; class C1; class C2 { // Use `package.C1` and poison `C2.C1`. // CHECK:STDERR: fail_declare_data_member_after_poison.carbon:[[@LINE+3]]:11: error: name `C1` used before it was declared [NameUseBeforeDecl] // CHECK:STDERR: fn F(x: C1); // CHECK:STDERR: ^~ fn F(x: C1); class C2 {} // Failure: `C2.C1` declared after it was poisoned. // CHECK:STDERR: fail_declare_data_member_after_poison.carbon:[[@LINE+4]]:7: note: declared here [NameUseBeforeDeclNote] // CHECK:STDERR: var C1: C2; // CHECK:STDERR: ^~~~~~ // CHECK:STDERR: var C1: C2; } // --- fail_extend_poison_class_members.carbon library "[[@TEST_NAME]]"; base class B {} class C { // CHECK:STDERR: fail_extend_poison_class_members.carbon:[[@LINE+3]]:16: error: name `B` used before it was declared [NameUseBeforeDecl] // CHECK:STDERR: extend base: B; // CHECK:STDERR: ^ extend base: B; // CHECK:STDERR: fail_extend_poison_class_members.carbon:[[@LINE+4]]:6: note: declared here [NameUseBeforeDeclNote] // CHECK:STDERR: fn B(); // CHECK:STDERR: ^ // CHECK:STDERR: fn B(); } // --- todo_fail_poisoned_name_in_extend.carbon library "[[@TEST_NAME]]"; base class B { fn F1(); } class F1 {} class C { // Use `package.F1` and poison `C.F1`. fn F2(x: F1); // TODO: #4622 This should diagnose since `C.F1` is poisoned. extend base: B; } ================================================ FILE: toolchain/check/testdata/class/nested.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/convert.carbon // TODO: Add ranges and switch to "--dump-sem-ir-ranges=only". // EXTRA-ARGS: --dump-sem-ir-ranges=if-present // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/class/nested.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/class/nested.carbon class Outer { fn F() { // Outer and Inner are both complete here. var unused o: Outer; var unused i: Inner; } class Inner { var pi: Self*; var po: Outer*; var qi: Inner*; fn G() { // Outer and Inner are both complete here. var unused o: Outer; var unused i: Inner; } } fn H() { // Outer and Inner are both complete here. var unused o: Outer; var unused i: Inner; } var po: Self*; var qo: Outer*; var pi: Inner*; } fn F(a: Outer*) { let b: Outer.Inner* = (*a).pi; a->po = a; a->qo = a; a->pi = a->pi; b->po = a; b->pi = a->pi; b->qi = a->pi; } // CHECK:STDOUT: --- nested.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %Outer: type = class_type @Outer [concrete] // CHECK:STDOUT: %Outer.F.type: type = fn_type @Outer.F [concrete] // CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] // CHECK:STDOUT: %Outer.F: %Outer.F.type = struct_value () [concrete] // CHECK:STDOUT: %Inner: type = class_type @Inner [concrete] // CHECK:STDOUT: %ptr.78a: type = ptr_type %Inner [concrete] // CHECK:STDOUT: %Inner.elem.9c1: type = unbound_element_type %Inner, %ptr.78a [concrete] // CHECK:STDOUT: %ptr.56b: type = ptr_type %Outer [concrete] // CHECK:STDOUT: %Inner.elem.9e0: type = unbound_element_type %Inner, %ptr.56b [concrete] // CHECK:STDOUT: %Inner.G.type: type = fn_type @Inner.G [concrete] // CHECK:STDOUT: %Inner.G: %Inner.G.type = struct_value () [concrete] // CHECK:STDOUT: %struct_type.pi.po.qi: type = struct_type {.pi: %ptr.78a, .po: %ptr.56b, .qi: %ptr.78a} [concrete] // CHECK:STDOUT: %complete_type.486: = complete_type_witness %struct_type.pi.po.qi [concrete] // CHECK:STDOUT: %Outer.H.type: type = fn_type @Outer.H [concrete] // CHECK:STDOUT: %Outer.H: %Outer.H.type = struct_value () [concrete] // CHECK:STDOUT: %Outer.elem.1e5: type = unbound_element_type %Outer, %ptr.56b [concrete] // CHECK:STDOUT: %Outer.elem.6db: type = unbound_element_type %Outer, %ptr.78a [concrete] // CHECK:STDOUT: %struct_type.po.qo.pi: type = struct_type {.po: %ptr.56b, .qo: %ptr.56b, .pi: %ptr.78a} [concrete] // CHECK:STDOUT: %complete_type.c34: = complete_type_witness %struct_type.po.qo.pi [concrete] // CHECK:STDOUT: %pattern_type.9ae: type = pattern_type %Outer [concrete] // CHECK:STDOUT: %DefaultOrUnformed.type: type = facet_type <@DefaultOrUnformed> [concrete] // CHECK:STDOUT: %T.67d: type = symbolic_binding T, 0 [symbolic] // CHECK:STDOUT: %T.as.DefaultOrUnformed.impl.Op.type.6c6: type = fn_type @T.as.DefaultOrUnformed.impl.Op, @T.as.DefaultOrUnformed.impl(%T.67d) [symbolic] // CHECK:STDOUT: %T.as.DefaultOrUnformed.impl.Op.ae1: %T.as.DefaultOrUnformed.impl.Op.type.6c6 = struct_value () [symbolic] // CHECK:STDOUT: %DefaultOrUnformed.impl_witness.52f: = impl_witness imports.%DefaultOrUnformed.impl_witness_table, @T.as.DefaultOrUnformed.impl(%Outer) [concrete] // CHECK:STDOUT: %T.as.DefaultOrUnformed.impl.Op.type.8b8: type = fn_type @T.as.DefaultOrUnformed.impl.Op, @T.as.DefaultOrUnformed.impl(%Outer) [concrete] // CHECK:STDOUT: %T.as.DefaultOrUnformed.impl.Op.de4: %T.as.DefaultOrUnformed.impl.Op.type.8b8 = struct_value () [concrete] // CHECK:STDOUT: %DefaultOrUnformed.facet.83f: %DefaultOrUnformed.type = facet_value %Outer, (%DefaultOrUnformed.impl_witness.52f) [concrete] // CHECK:STDOUT: %T.as.DefaultOrUnformed.impl.Op.specific_fn.a30: = specific_function %T.as.DefaultOrUnformed.impl.Op.de4, @T.as.DefaultOrUnformed.impl.Op(%Outer) [concrete] // CHECK:STDOUT: %pattern_type.86a: type = pattern_type %Inner [concrete] // CHECK:STDOUT: %DefaultOrUnformed.impl_witness.994: = impl_witness imports.%DefaultOrUnformed.impl_witness_table, @T.as.DefaultOrUnformed.impl(%Inner) [concrete] // CHECK:STDOUT: %T.as.DefaultOrUnformed.impl.Op.type.036: type = fn_type @T.as.DefaultOrUnformed.impl.Op, @T.as.DefaultOrUnformed.impl(%Inner) [concrete] // CHECK:STDOUT: %T.as.DefaultOrUnformed.impl.Op.da3: %T.as.DefaultOrUnformed.impl.Op.type.036 = struct_value () [concrete] // CHECK:STDOUT: %DefaultOrUnformed.facet.6ba: %DefaultOrUnformed.type = facet_value %Inner, (%DefaultOrUnformed.impl_witness.994) [concrete] // CHECK:STDOUT: %T.as.DefaultOrUnformed.impl.Op.specific_fn.14b: = specific_function %T.as.DefaultOrUnformed.impl.Op.da3, @T.as.DefaultOrUnformed.impl.Op(%Inner) [concrete] // CHECK:STDOUT: %Destroy.type: type = facet_type <@Destroy> [concrete] // CHECK:STDOUT: %Destroy.Op.type.bae255.1: type = fn_type @Destroy.Op.loc19 [concrete] // CHECK:STDOUT: %Destroy.Op.651ba6.1: %Destroy.Op.type.bae255.1 = struct_value () [concrete] // CHECK:STDOUT: %Destroy.Op.type.bae255.2: type = fn_type @Destroy.Op.loc18 [concrete] // CHECK:STDOUT: %Destroy.Op.651ba6.2: %Destroy.Op.type.bae255.2 = struct_value () [concrete] // CHECK:STDOUT: %pattern_type.cd9: type = pattern_type %ptr.56b [concrete] // CHECK:STDOUT: %F.type: type = fn_type @F [concrete] // CHECK:STDOUT: %F: %F.type = struct_value () [concrete] // CHECK:STDOUT: %pattern_type.565: type = pattern_type %ptr.78a [concrete] // CHECK:STDOUT: %Copy.type: type = facet_type <@Copy> [concrete] // CHECK:STDOUT: %ptr.as.Copy.impl.Op.type.2d4: type = fn_type @ptr.as.Copy.impl.Op, @ptr.as.Copy.impl(%T.67d) [symbolic] // CHECK:STDOUT: %ptr.as.Copy.impl.Op.74e: %ptr.as.Copy.impl.Op.type.2d4 = struct_value () [symbolic] // CHECK:STDOUT: %Copy.impl_witness.3e0: = impl_witness imports.%Copy.impl_witness_table.c3a, @ptr.as.Copy.impl(%Outer) [concrete] // CHECK:STDOUT: %ptr.as.Copy.impl.Op.type.561: type = fn_type @ptr.as.Copy.impl.Op, @ptr.as.Copy.impl(%Outer) [concrete] // CHECK:STDOUT: %ptr.as.Copy.impl.Op.dc1: %ptr.as.Copy.impl.Op.type.561 = struct_value () [concrete] // CHECK:STDOUT: %Copy.facet.e2d: %Copy.type = facet_value %ptr.56b, (%Copy.impl_witness.3e0) [concrete] // CHECK:STDOUT: %Copy.WithSelf.Op.type.ed5: type = fn_type @Copy.WithSelf.Op, @Copy.WithSelf(%Copy.facet.e2d) [concrete] // CHECK:STDOUT: %.be6: type = fn_type_with_self_type %Copy.WithSelf.Op.type.ed5, %Copy.facet.e2d [concrete] // CHECK:STDOUT: %ptr.as.Copy.impl.Op.specific_fn.904: = specific_function %ptr.as.Copy.impl.Op.dc1, @ptr.as.Copy.impl.Op(%Outer) [concrete] // CHECK:STDOUT: %Copy.impl_witness.044: = impl_witness imports.%Copy.impl_witness_table.c3a, @ptr.as.Copy.impl(%Inner) [concrete] // CHECK:STDOUT: %ptr.as.Copy.impl.Op.type.72d: type = fn_type @ptr.as.Copy.impl.Op, @ptr.as.Copy.impl(%Inner) [concrete] // CHECK:STDOUT: %ptr.as.Copy.impl.Op.8ab: %ptr.as.Copy.impl.Op.type.72d = struct_value () [concrete] // CHECK:STDOUT: %Copy.facet.ea8: %Copy.type = facet_value %ptr.78a, (%Copy.impl_witness.044) [concrete] // CHECK:STDOUT: %Copy.WithSelf.Op.type.4b9: type = fn_type @Copy.WithSelf.Op, @Copy.WithSelf(%Copy.facet.ea8) [concrete] // CHECK:STDOUT: %.b9c: type = fn_type_with_self_type %Copy.WithSelf.Op.type.4b9, %Copy.facet.ea8 [concrete] // CHECK:STDOUT: %ptr.as.Copy.impl.Op.specific_fn.f24: = specific_function %ptr.as.Copy.impl.Op.8ab, @ptr.as.Copy.impl.Op(%Inner) [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: .DefaultOrUnformed = %Core.DefaultOrUnformed // CHECK:STDOUT: .Destroy = %Core.Destroy // CHECK:STDOUT: .Copy = %Core.Copy // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: %Core.DefaultOrUnformed: type = import_ref Core//prelude/parts/default, DefaultOrUnformed, loaded [concrete = constants.%DefaultOrUnformed.type] // CHECK:STDOUT: %Core.import_ref.01d: @T.as.DefaultOrUnformed.impl.%T.as.DefaultOrUnformed.impl.Op.type (%T.as.DefaultOrUnformed.impl.Op.type.6c6) = import_ref Core//prelude/parts/default, loc{{\d+_\d+}}, loaded [symbolic = @T.as.DefaultOrUnformed.impl.%T.as.DefaultOrUnformed.impl.Op (constants.%T.as.DefaultOrUnformed.impl.Op.ae1)] // CHECK:STDOUT: %DefaultOrUnformed.impl_witness_table = impl_witness_table (%Core.import_ref.01d), @T.as.DefaultOrUnformed.impl [concrete] // CHECK:STDOUT: %Core.Destroy: type = import_ref Core//prelude/parts/destroy, Destroy, loaded [concrete = constants.%Destroy.type] // CHECK:STDOUT: %Core.Copy: type = import_ref Core//prelude/parts/copy, Copy, loaded [concrete = constants.%Copy.type] // CHECK:STDOUT: %Core.import_ref.203: @ptr.as.Copy.impl.%ptr.as.Copy.impl.Op.type (%ptr.as.Copy.impl.Op.type.2d4) = import_ref Core//prelude/parts/copy, loc{{\d+_\d+}}, loaded [symbolic = @ptr.as.Copy.impl.%ptr.as.Copy.impl.Op (constants.%ptr.as.Copy.impl.Op.74e)] // CHECK:STDOUT: %Copy.impl_witness_table.c3a = impl_witness_table (%Core.import_ref.203), @ptr.as.Copy.impl [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .Outer = %Outer.decl // CHECK:STDOUT: .F = %F.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %Outer.decl: type = class_decl @Outer [concrete = constants.%Outer] {} {} // CHECK:STDOUT: %F.decl: %F.type = fn_decl @F [concrete = constants.%F] { // CHECK:STDOUT: %a.patt: %pattern_type.cd9 = value_binding_pattern a [concrete] // CHECK:STDOUT: %a.param_patt: %pattern_type.cd9 = value_param_pattern %a.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %a.param: %ptr.56b = value_param call_param0 // CHECK:STDOUT: %.loc45: type = splice_block %ptr.loc45 [concrete = constants.%ptr.56b] { // CHECK:STDOUT: %Outer.ref.loc45: type = name_ref Outer, file.%Outer.decl [concrete = constants.%Outer] // CHECK:STDOUT: %ptr.loc45: type = ptr_type %Outer.ref.loc45 [concrete = constants.%ptr.56b] // CHECK:STDOUT: } // CHECK:STDOUT: %a: %ptr.56b = value_binding a, %a.param // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @Outer { // CHECK:STDOUT: %Outer.F.decl: %Outer.F.type = fn_decl @Outer.F [concrete = constants.%Outer.F] {} {} // CHECK:STDOUT: %Inner.decl: type = class_decl @Inner [concrete = constants.%Inner] {} {} // CHECK:STDOUT: %Outer.H.decl: %Outer.H.type = fn_decl @Outer.H [concrete = constants.%Outer.H] {} {} // CHECK:STDOUT: %Self.ref: type = name_ref Self, constants.%Outer [concrete = constants.%Outer] // CHECK:STDOUT: %ptr.loc40: type = ptr_type %Self.ref [concrete = constants.%ptr.56b] // CHECK:STDOUT: %.loc40: %Outer.elem.1e5 = field_decl po, element0 [concrete] // CHECK:STDOUT: %Outer.ref: type = name_ref Outer, file.%Outer.decl [concrete = constants.%Outer] // CHECK:STDOUT: %ptr.loc41: type = ptr_type %Outer.ref [concrete = constants.%ptr.56b] // CHECK:STDOUT: %.loc41: %Outer.elem.1e5 = field_decl qo, element1 [concrete] // CHECK:STDOUT: %Inner.ref: type = name_ref Inner, %Inner.decl [concrete = constants.%Inner] // CHECK:STDOUT: %ptr.loc42: type = ptr_type %Inner.ref [concrete = constants.%ptr.78a] // CHECK:STDOUT: %.loc42: %Outer.elem.6db = field_decl pi, element2 [concrete] // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%struct_type.po.qo.pi [concrete = constants.%complete_type.c34] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%Outer // CHECK:STDOUT: .F = %Outer.F.decl // CHECK:STDOUT: .Inner = %Inner.decl // CHECK:STDOUT: .Outer = // CHECK:STDOUT: .H = %Outer.H.decl // CHECK:STDOUT: .po = %.loc40 // CHECK:STDOUT: .qo = %.loc41 // CHECK:STDOUT: .pi = %.loc42 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @Inner { // CHECK:STDOUT: %Self.ref: type = name_ref Self, constants.%Inner [concrete = constants.%Inner] // CHECK:STDOUT: %ptr.loc23: type = ptr_type %Self.ref [concrete = constants.%ptr.78a] // CHECK:STDOUT: %.loc23: %Inner.elem.9c1 = field_decl pi, element0 [concrete] // CHECK:STDOUT: %Outer.ref: type = name_ref Outer, file.%Outer.decl [concrete = constants.%Outer] // CHECK:STDOUT: %ptr.loc24: type = ptr_type %Outer.ref [concrete = constants.%ptr.56b] // CHECK:STDOUT: %.loc24: %Inner.elem.9e0 = field_decl po, element1 [concrete] // CHECK:STDOUT: %Inner.ref: type = name_ref Inner, @Outer.%Inner.decl [concrete = constants.%Inner] // CHECK:STDOUT: %ptr.loc25: type = ptr_type %Inner.ref [concrete = constants.%ptr.78a] // CHECK:STDOUT: %.loc25: %Inner.elem.9c1 = field_decl qi, element2 [concrete] // CHECK:STDOUT: %Inner.G.decl: %Inner.G.type = fn_decl @Inner.G [concrete = constants.%Inner.G] {} {} // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%struct_type.pi.po.qi [concrete = constants.%complete_type.486] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%Inner // CHECK:STDOUT: .pi = %.loc23 // CHECK:STDOUT: .Outer = // CHECK:STDOUT: .po = %.loc24 // CHECK:STDOUT: .Inner = // CHECK:STDOUT: .qi = %.loc25 // CHECK:STDOUT: .G = %Inner.G.decl // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @Outer.F() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %o.patt: %pattern_type.9ae = ref_binding_pattern o [concrete] // CHECK:STDOUT: %o.var_patt: %pattern_type.9ae = var_pattern %o.patt [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %o.var: ref %Outer = var %o.var_patt // CHECK:STDOUT: %DefaultOrUnformed.facet.loc18: %DefaultOrUnformed.type = facet_value constants.%Outer, (constants.%DefaultOrUnformed.impl_witness.52f) [concrete = constants.%DefaultOrUnformed.facet.83f] // CHECK:STDOUT: %.loc18_24.1: %DefaultOrUnformed.type = converted constants.%Outer, %DefaultOrUnformed.facet.loc18 [concrete = constants.%DefaultOrUnformed.facet.83f] // CHECK:STDOUT: %as_type.loc18: type = facet_access_type %.loc18_24.1 [concrete = constants.%Outer] // CHECK:STDOUT: %.loc18_24.2: type = converted %.loc18_24.1, %as_type.loc18 [concrete = constants.%Outer] // CHECK:STDOUT: %T.as.DefaultOrUnformed.impl.Op.specific_fn.1: = specific_function constants.%T.as.DefaultOrUnformed.impl.Op.de4, @T.as.DefaultOrUnformed.impl.Op(constants.%Outer) [concrete = constants.%T.as.DefaultOrUnformed.impl.Op.specific_fn.a30] // CHECK:STDOUT: %.loc18_5: ref %Outer = splice_block %o.var {} // CHECK:STDOUT: %T.as.DefaultOrUnformed.impl.Op.call.loc18: init %Outer to %.loc18_5 = call %T.as.DefaultOrUnformed.impl.Op.specific_fn.1() // CHECK:STDOUT: assign %o.var, %T.as.DefaultOrUnformed.impl.Op.call.loc18 // CHECK:STDOUT: %Outer.ref: type = name_ref Outer, file.%Outer.decl [concrete = constants.%Outer] // CHECK:STDOUT: %o: ref %Outer = ref_binding o, %o.var // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %i.patt: %pattern_type.86a = ref_binding_pattern i [concrete] // CHECK:STDOUT: %i.var_patt: %pattern_type.86a = var_pattern %i.patt [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %i.var: ref %Inner = var %i.var_patt // CHECK:STDOUT: %DefaultOrUnformed.facet.loc19: %DefaultOrUnformed.type = facet_value constants.%Inner, (constants.%DefaultOrUnformed.impl_witness.994) [concrete = constants.%DefaultOrUnformed.facet.6ba] // CHECK:STDOUT: %.loc19_24.1: %DefaultOrUnformed.type = converted constants.%Inner, %DefaultOrUnformed.facet.loc19 [concrete = constants.%DefaultOrUnformed.facet.6ba] // CHECK:STDOUT: %as_type.loc19: type = facet_access_type %.loc19_24.1 [concrete = constants.%Inner] // CHECK:STDOUT: %.loc19_24.2: type = converted %.loc19_24.1, %as_type.loc19 [concrete = constants.%Inner] // CHECK:STDOUT: %T.as.DefaultOrUnformed.impl.Op.specific_fn.2: = specific_function constants.%T.as.DefaultOrUnformed.impl.Op.da3, @T.as.DefaultOrUnformed.impl.Op(constants.%Inner) [concrete = constants.%T.as.DefaultOrUnformed.impl.Op.specific_fn.14b] // CHECK:STDOUT: %.loc19_5: ref %Inner = splice_block %i.var {} // CHECK:STDOUT: %T.as.DefaultOrUnformed.impl.Op.call.loc19: init %Inner to %.loc19_5 = call %T.as.DefaultOrUnformed.impl.Op.specific_fn.2() // CHECK:STDOUT: assign %i.var, %T.as.DefaultOrUnformed.impl.Op.call.loc19 // CHECK:STDOUT: %Inner.ref: type = name_ref Inner, @Outer.%Inner.decl [concrete = constants.%Inner] // CHECK:STDOUT: %i: ref %Inner = ref_binding i, %i.var // CHECK:STDOUT: %Destroy.Op.bound.loc19: = bound_method %i.var, constants.%Destroy.Op.651ba6.1 // CHECK:STDOUT: %Destroy.Op.call.loc19: init %empty_tuple.type = call %Destroy.Op.bound.loc19(%i.var) // CHECK:STDOUT: %Destroy.Op.bound.loc18: = bound_method %o.var, constants.%Destroy.Op.651ba6.2 // CHECK:STDOUT: %Destroy.Op.call.loc18: init %empty_tuple.type = call %Destroy.Op.bound.loc18(%o.var) // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @Inner.G() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %o.patt: %pattern_type.9ae = ref_binding_pattern o [concrete] // CHECK:STDOUT: %o.var_patt: %pattern_type.9ae = var_pattern %o.patt [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %o.var: ref %Outer = var %o.var_patt // CHECK:STDOUT: %DefaultOrUnformed.facet.loc29: %DefaultOrUnformed.type = facet_value constants.%Outer, (constants.%DefaultOrUnformed.impl_witness.52f) [concrete = constants.%DefaultOrUnformed.facet.83f] // CHECK:STDOUT: %.loc29_26.1: %DefaultOrUnformed.type = converted constants.%Outer, %DefaultOrUnformed.facet.loc29 [concrete = constants.%DefaultOrUnformed.facet.83f] // CHECK:STDOUT: %as_type.loc29: type = facet_access_type %.loc29_26.1 [concrete = constants.%Outer] // CHECK:STDOUT: %.loc29_26.2: type = converted %.loc29_26.1, %as_type.loc29 [concrete = constants.%Outer] // CHECK:STDOUT: %T.as.DefaultOrUnformed.impl.Op.specific_fn.1: = specific_function constants.%T.as.DefaultOrUnformed.impl.Op.de4, @T.as.DefaultOrUnformed.impl.Op(constants.%Outer) [concrete = constants.%T.as.DefaultOrUnformed.impl.Op.specific_fn.a30] // CHECK:STDOUT: %.loc29_7: ref %Outer = splice_block %o.var {} // CHECK:STDOUT: %T.as.DefaultOrUnformed.impl.Op.call.loc29: init %Outer to %.loc29_7 = call %T.as.DefaultOrUnformed.impl.Op.specific_fn.1() // CHECK:STDOUT: assign %o.var, %T.as.DefaultOrUnformed.impl.Op.call.loc29 // CHECK:STDOUT: %Outer.ref: type = name_ref Outer, file.%Outer.decl [concrete = constants.%Outer] // CHECK:STDOUT: %o: ref %Outer = ref_binding o, %o.var // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %i.patt: %pattern_type.86a = ref_binding_pattern i [concrete] // CHECK:STDOUT: %i.var_patt: %pattern_type.86a = var_pattern %i.patt [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %i.var: ref %Inner = var %i.var_patt // CHECK:STDOUT: %DefaultOrUnformed.facet.loc30: %DefaultOrUnformed.type = facet_value constants.%Inner, (constants.%DefaultOrUnformed.impl_witness.994) [concrete = constants.%DefaultOrUnformed.facet.6ba] // CHECK:STDOUT: %.loc30_26.1: %DefaultOrUnformed.type = converted constants.%Inner, %DefaultOrUnformed.facet.loc30 [concrete = constants.%DefaultOrUnformed.facet.6ba] // CHECK:STDOUT: %as_type.loc30: type = facet_access_type %.loc30_26.1 [concrete = constants.%Inner] // CHECK:STDOUT: %.loc30_26.2: type = converted %.loc30_26.1, %as_type.loc30 [concrete = constants.%Inner] // CHECK:STDOUT: %T.as.DefaultOrUnformed.impl.Op.specific_fn.2: = specific_function constants.%T.as.DefaultOrUnformed.impl.Op.da3, @T.as.DefaultOrUnformed.impl.Op(constants.%Inner) [concrete = constants.%T.as.DefaultOrUnformed.impl.Op.specific_fn.14b] // CHECK:STDOUT: %.loc30_7: ref %Inner = splice_block %i.var {} // CHECK:STDOUT: %T.as.DefaultOrUnformed.impl.Op.call.loc30: init %Inner to %.loc30_7 = call %T.as.DefaultOrUnformed.impl.Op.specific_fn.2() // CHECK:STDOUT: assign %i.var, %T.as.DefaultOrUnformed.impl.Op.call.loc30 // CHECK:STDOUT: %Inner.ref: type = name_ref Inner, @Outer.%Inner.decl [concrete = constants.%Inner] // CHECK:STDOUT: %i: ref %Inner = ref_binding i, %i.var // CHECK:STDOUT: %Destroy.Op.bound.loc30: = bound_method %i.var, constants.%Destroy.Op.651ba6.1 // CHECK:STDOUT: %Destroy.Op.call.loc30: init %empty_tuple.type = call %Destroy.Op.bound.loc30(%i.var) // CHECK:STDOUT: %Destroy.Op.bound.loc29: = bound_method %o.var, constants.%Destroy.Op.651ba6.2 // CHECK:STDOUT: %Destroy.Op.call.loc29: init %empty_tuple.type = call %Destroy.Op.bound.loc29(%o.var) // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @Outer.H() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %o.patt: %pattern_type.9ae = ref_binding_pattern o [concrete] // CHECK:STDOUT: %o.var_patt: %pattern_type.9ae = var_pattern %o.patt [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %o.var: ref %Outer = var %o.var_patt // CHECK:STDOUT: %DefaultOrUnformed.facet.loc36: %DefaultOrUnformed.type = facet_value constants.%Outer, (constants.%DefaultOrUnformed.impl_witness.52f) [concrete = constants.%DefaultOrUnformed.facet.83f] // CHECK:STDOUT: %.loc36_24.1: %DefaultOrUnformed.type = converted constants.%Outer, %DefaultOrUnformed.facet.loc36 [concrete = constants.%DefaultOrUnformed.facet.83f] // CHECK:STDOUT: %as_type.loc36: type = facet_access_type %.loc36_24.1 [concrete = constants.%Outer] // CHECK:STDOUT: %.loc36_24.2: type = converted %.loc36_24.1, %as_type.loc36 [concrete = constants.%Outer] // CHECK:STDOUT: %T.as.DefaultOrUnformed.impl.Op.specific_fn.1: = specific_function constants.%T.as.DefaultOrUnformed.impl.Op.de4, @T.as.DefaultOrUnformed.impl.Op(constants.%Outer) [concrete = constants.%T.as.DefaultOrUnformed.impl.Op.specific_fn.a30] // CHECK:STDOUT: %.loc36_5: ref %Outer = splice_block %o.var {} // CHECK:STDOUT: %T.as.DefaultOrUnformed.impl.Op.call.loc36: init %Outer to %.loc36_5 = call %T.as.DefaultOrUnformed.impl.Op.specific_fn.1() // CHECK:STDOUT: assign %o.var, %T.as.DefaultOrUnformed.impl.Op.call.loc36 // CHECK:STDOUT: %Outer.ref: type = name_ref Outer, file.%Outer.decl [concrete = constants.%Outer] // CHECK:STDOUT: %o: ref %Outer = ref_binding o, %o.var // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %i.patt: %pattern_type.86a = ref_binding_pattern i [concrete] // CHECK:STDOUT: %i.var_patt: %pattern_type.86a = var_pattern %i.patt [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %i.var: ref %Inner = var %i.var_patt // CHECK:STDOUT: %DefaultOrUnformed.facet.loc37: %DefaultOrUnformed.type = facet_value constants.%Inner, (constants.%DefaultOrUnformed.impl_witness.994) [concrete = constants.%DefaultOrUnformed.facet.6ba] // CHECK:STDOUT: %.loc37_24.1: %DefaultOrUnformed.type = converted constants.%Inner, %DefaultOrUnformed.facet.loc37 [concrete = constants.%DefaultOrUnformed.facet.6ba] // CHECK:STDOUT: %as_type.loc37: type = facet_access_type %.loc37_24.1 [concrete = constants.%Inner] // CHECK:STDOUT: %.loc37_24.2: type = converted %.loc37_24.1, %as_type.loc37 [concrete = constants.%Inner] // CHECK:STDOUT: %T.as.DefaultOrUnformed.impl.Op.specific_fn.2: = specific_function constants.%T.as.DefaultOrUnformed.impl.Op.da3, @T.as.DefaultOrUnformed.impl.Op(constants.%Inner) [concrete = constants.%T.as.DefaultOrUnformed.impl.Op.specific_fn.14b] // CHECK:STDOUT: %.loc37_5: ref %Inner = splice_block %i.var {} // CHECK:STDOUT: %T.as.DefaultOrUnformed.impl.Op.call.loc37: init %Inner to %.loc37_5 = call %T.as.DefaultOrUnformed.impl.Op.specific_fn.2() // CHECK:STDOUT: assign %i.var, %T.as.DefaultOrUnformed.impl.Op.call.loc37 // CHECK:STDOUT: %Inner.ref: type = name_ref Inner, @Outer.%Inner.decl [concrete = constants.%Inner] // CHECK:STDOUT: %i: ref %Inner = ref_binding i, %i.var // CHECK:STDOUT: %Destroy.Op.bound.loc37: = bound_method %i.var, constants.%Destroy.Op.651ba6.1 // CHECK:STDOUT: %Destroy.Op.call.loc37: init %empty_tuple.type = call %Destroy.Op.bound.loc37(%i.var) // CHECK:STDOUT: %Destroy.Op.bound.loc36: = bound_method %o.var, constants.%Destroy.Op.651ba6.2 // CHECK:STDOUT: %Destroy.Op.call.loc36: init %empty_tuple.type = call %Destroy.Op.bound.loc36(%o.var) // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @Destroy.Op.loc19(%self.param: ref %Inner) = "no_op"; // CHECK:STDOUT: // CHECK:STDOUT: fn @Destroy.Op.loc18(%self.param: ref %Outer) = "no_op"; // CHECK:STDOUT: // CHECK:STDOUT: fn @F(%a.param: %ptr.56b) { // CHECK:STDOUT: !entry: // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %b.patt: %pattern_type.565 = value_binding_pattern b [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %a.ref.loc46: %ptr.56b = name_ref a, %a // CHECK:STDOUT: %.loc46_26: ref %Outer = deref %a.ref.loc46 // CHECK:STDOUT: %pi.ref.loc46: %Outer.elem.6db = name_ref pi, @Outer.%.loc42 [concrete = @Outer.%.loc42] // CHECK:STDOUT: %.loc46_29.1: ref %ptr.78a = class_element_access %.loc46_26, element2 // CHECK:STDOUT: %.loc46_21: type = splice_block %ptr.loc46 [concrete = constants.%ptr.78a] { // CHECK:STDOUT: %Outer.ref.loc46: type = name_ref Outer, file.%Outer.decl [concrete = constants.%Outer] // CHECK:STDOUT: %Inner.ref: type = name_ref Inner, @Outer.%Inner.decl [concrete = constants.%Inner] // CHECK:STDOUT: %ptr.loc46: type = ptr_type %Inner.ref [concrete = constants.%ptr.78a] // CHECK:STDOUT: } // CHECK:STDOUT: %.loc46_29.2: %ptr.78a = acquire_value %.loc46_29.1 // CHECK:STDOUT: %b: %ptr.78a = value_binding b, %.loc46_29.2 // CHECK:STDOUT: %a.ref.loc48_3: %ptr.56b = name_ref a, %a // CHECK:STDOUT: %.loc48_4.1: ref %Outer = deref %a.ref.loc48_3 // CHECK:STDOUT: %po.ref.loc48: %Outer.elem.1e5 = name_ref po, @Outer.%.loc40 [concrete = @Outer.%.loc40] // CHECK:STDOUT: %.loc48_4.2: ref %ptr.56b = class_element_access %.loc48_4.1, element0 // CHECK:STDOUT: %a.ref.loc48_11: %ptr.56b = name_ref a, %a // CHECK:STDOUT: %impl.elem0.loc48: %.be6 = impl_witness_access constants.%Copy.impl_witness.3e0, element0 [concrete = constants.%ptr.as.Copy.impl.Op.dc1] // CHECK:STDOUT: %bound_method.loc48_11.1: = bound_method %a.ref.loc48_11, %impl.elem0.loc48 // CHECK:STDOUT: %specific_fn.loc48: = specific_function %impl.elem0.loc48, @ptr.as.Copy.impl.Op(constants.%Outer) [concrete = constants.%ptr.as.Copy.impl.Op.specific_fn.904] // CHECK:STDOUT: %bound_method.loc48_11.2: = bound_method %a.ref.loc48_11, %specific_fn.loc48 // CHECK:STDOUT: %ptr.as.Copy.impl.Op.call.loc48: init %ptr.56b = call %bound_method.loc48_11.2(%a.ref.loc48_11) // CHECK:STDOUT: assign %.loc48_4.2, %ptr.as.Copy.impl.Op.call.loc48 // CHECK:STDOUT: %a.ref.loc49_3: %ptr.56b = name_ref a, %a // CHECK:STDOUT: %.loc49_4.1: ref %Outer = deref %a.ref.loc49_3 // CHECK:STDOUT: %qo.ref: %Outer.elem.1e5 = name_ref qo, @Outer.%.loc41 [concrete = @Outer.%.loc41] // CHECK:STDOUT: %.loc49_4.2: ref %ptr.56b = class_element_access %.loc49_4.1, element1 // CHECK:STDOUT: %a.ref.loc49_11: %ptr.56b = name_ref a, %a // CHECK:STDOUT: %impl.elem0.loc49: %.be6 = impl_witness_access constants.%Copy.impl_witness.3e0, element0 [concrete = constants.%ptr.as.Copy.impl.Op.dc1] // CHECK:STDOUT: %bound_method.loc49_11.1: = bound_method %a.ref.loc49_11, %impl.elem0.loc49 // CHECK:STDOUT: %specific_fn.loc49: = specific_function %impl.elem0.loc49, @ptr.as.Copy.impl.Op(constants.%Outer) [concrete = constants.%ptr.as.Copy.impl.Op.specific_fn.904] // CHECK:STDOUT: %bound_method.loc49_11.2: = bound_method %a.ref.loc49_11, %specific_fn.loc49 // CHECK:STDOUT: %ptr.as.Copy.impl.Op.call.loc49: init %ptr.56b = call %bound_method.loc49_11.2(%a.ref.loc49_11) // CHECK:STDOUT: assign %.loc49_4.2, %ptr.as.Copy.impl.Op.call.loc49 // CHECK:STDOUT: %a.ref.loc50_3: %ptr.56b = name_ref a, %a // CHECK:STDOUT: %.loc50_4.1: ref %Outer = deref %a.ref.loc50_3 // CHECK:STDOUT: %pi.ref.loc50_4: %Outer.elem.6db = name_ref pi, @Outer.%.loc42 [concrete = @Outer.%.loc42] // CHECK:STDOUT: %.loc50_4.2: ref %ptr.78a = class_element_access %.loc50_4.1, element2 // CHECK:STDOUT: %a.ref.loc50_11: %ptr.56b = name_ref a, %a // CHECK:STDOUT: %.loc50_12.1: ref %Outer = deref %a.ref.loc50_11 // CHECK:STDOUT: %pi.ref.loc50_12: %Outer.elem.6db = name_ref pi, @Outer.%.loc42 [concrete = @Outer.%.loc42] // CHECK:STDOUT: %.loc50_12.2: ref %ptr.78a = class_element_access %.loc50_12.1, element2 // CHECK:STDOUT: %.loc50_12.3: %ptr.78a = acquire_value %.loc50_12.2 // CHECK:STDOUT: %impl.elem0.loc50: %.b9c = impl_witness_access constants.%Copy.impl_witness.044, element0 [concrete = constants.%ptr.as.Copy.impl.Op.8ab] // CHECK:STDOUT: %bound_method.loc50_12.1: = bound_method %.loc50_12.3, %impl.elem0.loc50 // CHECK:STDOUT: %specific_fn.loc50: = specific_function %impl.elem0.loc50, @ptr.as.Copy.impl.Op(constants.%Inner) [concrete = constants.%ptr.as.Copy.impl.Op.specific_fn.f24] // CHECK:STDOUT: %bound_method.loc50_12.2: = bound_method %.loc50_12.3, %specific_fn.loc50 // CHECK:STDOUT: %ptr.as.Copy.impl.Op.call.loc50: init %ptr.78a = call %bound_method.loc50_12.2(%.loc50_12.3) // CHECK:STDOUT: assign %.loc50_4.2, %ptr.as.Copy.impl.Op.call.loc50 // CHECK:STDOUT: %b.ref.loc51: %ptr.78a = name_ref b, %b // CHECK:STDOUT: %.loc51_4.1: ref %Inner = deref %b.ref.loc51 // CHECK:STDOUT: %po.ref.loc51: %Inner.elem.9e0 = name_ref po, @Inner.%.loc24 [concrete = @Inner.%.loc24] // CHECK:STDOUT: %.loc51_4.2: ref %ptr.56b = class_element_access %.loc51_4.1, element1 // CHECK:STDOUT: %a.ref.loc51: %ptr.56b = name_ref a, %a // CHECK:STDOUT: %impl.elem0.loc51: %.be6 = impl_witness_access constants.%Copy.impl_witness.3e0, element0 [concrete = constants.%ptr.as.Copy.impl.Op.dc1] // CHECK:STDOUT: %bound_method.loc51_11.1: = bound_method %a.ref.loc51, %impl.elem0.loc51 // CHECK:STDOUT: %specific_fn.loc51: = specific_function %impl.elem0.loc51, @ptr.as.Copy.impl.Op(constants.%Outer) [concrete = constants.%ptr.as.Copy.impl.Op.specific_fn.904] // CHECK:STDOUT: %bound_method.loc51_11.2: = bound_method %a.ref.loc51, %specific_fn.loc51 // CHECK:STDOUT: %ptr.as.Copy.impl.Op.call.loc51: init %ptr.56b = call %bound_method.loc51_11.2(%a.ref.loc51) // CHECK:STDOUT: assign %.loc51_4.2, %ptr.as.Copy.impl.Op.call.loc51 // CHECK:STDOUT: %b.ref.loc52: %ptr.78a = name_ref b, %b // CHECK:STDOUT: %.loc52_4.1: ref %Inner = deref %b.ref.loc52 // CHECK:STDOUT: %pi.ref.loc52_4: %Inner.elem.9c1 = name_ref pi, @Inner.%.loc23 [concrete = @Inner.%.loc23] // CHECK:STDOUT: %.loc52_4.2: ref %ptr.78a = class_element_access %.loc52_4.1, element0 // CHECK:STDOUT: %a.ref.loc52: %ptr.56b = name_ref a, %a // CHECK:STDOUT: %.loc52_12.1: ref %Outer = deref %a.ref.loc52 // CHECK:STDOUT: %pi.ref.loc52_12: %Outer.elem.6db = name_ref pi, @Outer.%.loc42 [concrete = @Outer.%.loc42] // CHECK:STDOUT: %.loc52_12.2: ref %ptr.78a = class_element_access %.loc52_12.1, element2 // CHECK:STDOUT: %.loc52_12.3: %ptr.78a = acquire_value %.loc52_12.2 // CHECK:STDOUT: %impl.elem0.loc52: %.b9c = impl_witness_access constants.%Copy.impl_witness.044, element0 [concrete = constants.%ptr.as.Copy.impl.Op.8ab] // CHECK:STDOUT: %bound_method.loc52_12.1: = bound_method %.loc52_12.3, %impl.elem0.loc52 // CHECK:STDOUT: %specific_fn.loc52: = specific_function %impl.elem0.loc52, @ptr.as.Copy.impl.Op(constants.%Inner) [concrete = constants.%ptr.as.Copy.impl.Op.specific_fn.f24] // CHECK:STDOUT: %bound_method.loc52_12.2: = bound_method %.loc52_12.3, %specific_fn.loc52 // CHECK:STDOUT: %ptr.as.Copy.impl.Op.call.loc52: init %ptr.78a = call %bound_method.loc52_12.2(%.loc52_12.3) // CHECK:STDOUT: assign %.loc52_4.2, %ptr.as.Copy.impl.Op.call.loc52 // CHECK:STDOUT: %b.ref.loc53: %ptr.78a = name_ref b, %b // CHECK:STDOUT: %.loc53_4.1: ref %Inner = deref %b.ref.loc53 // CHECK:STDOUT: %qi.ref: %Inner.elem.9c1 = name_ref qi, @Inner.%.loc25 [concrete = @Inner.%.loc25] // CHECK:STDOUT: %.loc53_4.2: ref %ptr.78a = class_element_access %.loc53_4.1, element2 // CHECK:STDOUT: %a.ref.loc53: %ptr.56b = name_ref a, %a // CHECK:STDOUT: %.loc53_12.1: ref %Outer = deref %a.ref.loc53 // CHECK:STDOUT: %pi.ref.loc53: %Outer.elem.6db = name_ref pi, @Outer.%.loc42 [concrete = @Outer.%.loc42] // CHECK:STDOUT: %.loc53_12.2: ref %ptr.78a = class_element_access %.loc53_12.1, element2 // CHECK:STDOUT: %.loc53_12.3: %ptr.78a = acquire_value %.loc53_12.2 // CHECK:STDOUT: %impl.elem0.loc53: %.b9c = impl_witness_access constants.%Copy.impl_witness.044, element0 [concrete = constants.%ptr.as.Copy.impl.Op.8ab] // CHECK:STDOUT: %bound_method.loc53_12.1: = bound_method %.loc53_12.3, %impl.elem0.loc53 // CHECK:STDOUT: %specific_fn.loc53: = specific_function %impl.elem0.loc53, @ptr.as.Copy.impl.Op(constants.%Inner) [concrete = constants.%ptr.as.Copy.impl.Op.specific_fn.f24] // CHECK:STDOUT: %bound_method.loc53_12.2: = bound_method %.loc53_12.3, %specific_fn.loc53 // CHECK:STDOUT: %ptr.as.Copy.impl.Op.call.loc53: init %ptr.78a = call %bound_method.loc53_12.2(%.loc53_12.3) // CHECK:STDOUT: assign %.loc53_4.2, %ptr.as.Copy.impl.Op.call.loc53 // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/class/nested_name.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/int.carbon // TODO: Add ranges and switch to "--dump-sem-ir-ranges=only". // EXTRA-ARGS: --dump-sem-ir-ranges=if-present // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/class/nested_name.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/class/nested_name.carbon class Outer { class Inner { var n: i32; } } fn F(oi: Outer.Inner) -> i32 { return oi.n; } fn G(o: Outer) { var unused i: o.Inner; } // CHECK:STDOUT: --- nested_name.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %Outer: type = class_type @Outer [concrete] // CHECK:STDOUT: %Inner: type = class_type @Inner [concrete] // CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [concrete] // CHECK:STDOUT: %Int.type: type = generic_class_type @Int [concrete] // CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] // CHECK:STDOUT: %Int.generic: %Int.type = struct_value () [concrete] // CHECK:STDOUT: %N: Core.IntLiteral = symbolic_binding N, 0 [symbolic] // CHECK:STDOUT: %i32: type = class_type @Int, @Int(%int_32) [concrete] // CHECK:STDOUT: %Inner.elem: type = unbound_element_type %Inner, %i32 [concrete] // CHECK:STDOUT: %struct_type.n: type = struct_type {.n: %i32} [concrete] // CHECK:STDOUT: %complete_type.54b: = complete_type_witness %struct_type.n [concrete] // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %complete_type.357: = complete_type_witness %empty_struct_type [concrete] // CHECK:STDOUT: %pattern_type.86a: type = pattern_type %Inner [concrete] // CHECK:STDOUT: %.ff5: Core.Form = init_form %i32 [concrete] // CHECK:STDOUT: %pattern_type.7ce: type = pattern_type %i32 [concrete] // CHECK:STDOUT: %F.type: type = fn_type @F [concrete] // CHECK:STDOUT: %F: %F.type = struct_value () [concrete] // CHECK:STDOUT: %Copy.type: type = facet_type <@Copy> [concrete] // CHECK:STDOUT: %Int.as.Copy.impl.Op.type.824: type = fn_type @Int.as.Copy.impl.Op, @Int.as.Copy.impl(%N) [symbolic] // CHECK:STDOUT: %Int.as.Copy.impl.Op.9b9: %Int.as.Copy.impl.Op.type.824 = struct_value () [symbolic] // CHECK:STDOUT: %T.67d: type = symbolic_binding T, 0 [symbolic] // CHECK:STDOUT: %Copy.impl_witness.f17: = impl_witness imports.%Copy.impl_witness_table.e76, @Int.as.Copy.impl(%int_32) [concrete] // CHECK:STDOUT: %Int.as.Copy.impl.Op.type.546: type = fn_type @Int.as.Copy.impl.Op, @Int.as.Copy.impl(%int_32) [concrete] // CHECK:STDOUT: %Int.as.Copy.impl.Op.664: %Int.as.Copy.impl.Op.type.546 = struct_value () [concrete] // CHECK:STDOUT: %Copy.facet: %Copy.type = facet_value %i32, (%Copy.impl_witness.f17) [concrete] // CHECK:STDOUT: %Copy.WithSelf.Op.type.081: type = fn_type @Copy.WithSelf.Op, @Copy.WithSelf(%Copy.facet) [concrete] // CHECK:STDOUT: %.8e2: type = fn_type_with_self_type %Copy.WithSelf.Op.type.081, %Copy.facet [concrete] // CHECK:STDOUT: %Int.as.Copy.impl.Op.specific_fn: = specific_function %Int.as.Copy.impl.Op.664, @Int.as.Copy.impl.Op(%int_32) [concrete] // CHECK:STDOUT: %pattern_type.9ae: type = pattern_type %Outer [concrete] // CHECK:STDOUT: %G.type: type = fn_type @G [concrete] // CHECK:STDOUT: %G: %G.type = struct_value () [concrete] // CHECK:STDOUT: %DefaultOrUnformed.type: type = facet_type <@DefaultOrUnformed> [concrete] // CHECK:STDOUT: %T.as.DefaultOrUnformed.impl.Op.type.6c6: type = fn_type @T.as.DefaultOrUnformed.impl.Op, @T.as.DefaultOrUnformed.impl(%T.67d) [symbolic] // CHECK:STDOUT: %T.as.DefaultOrUnformed.impl.Op.ae1: %T.as.DefaultOrUnformed.impl.Op.type.6c6 = struct_value () [symbolic] // CHECK:STDOUT: %DefaultOrUnformed.impl_witness.994: = impl_witness imports.%DefaultOrUnformed.impl_witness_table, @T.as.DefaultOrUnformed.impl(%Inner) [concrete] // CHECK:STDOUT: %T.as.DefaultOrUnformed.impl.Op.type.036: type = fn_type @T.as.DefaultOrUnformed.impl.Op, @T.as.DefaultOrUnformed.impl(%Inner) [concrete] // CHECK:STDOUT: %T.as.DefaultOrUnformed.impl.Op.da3: %T.as.DefaultOrUnformed.impl.Op.type.036 = struct_value () [concrete] // CHECK:STDOUT: %DefaultOrUnformed.facet: %DefaultOrUnformed.type = facet_value %Inner, (%DefaultOrUnformed.impl_witness.994) [concrete] // CHECK:STDOUT: %T.as.DefaultOrUnformed.impl.Op.specific_fn: = specific_function %T.as.DefaultOrUnformed.impl.Op.da3, @T.as.DefaultOrUnformed.impl.Op(%Inner) [concrete] // CHECK:STDOUT: %Destroy.type: type = facet_type <@Destroy> [concrete] // CHECK:STDOUT: %Destroy.Op.type: type = fn_type @Destroy.Op [concrete] // CHECK:STDOUT: %Destroy.Op: %Destroy.Op.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: .Int = %Core.Int // CHECK:STDOUT: .Copy = %Core.Copy // CHECK:STDOUT: .DefaultOrUnformed = %Core.DefaultOrUnformed // CHECK:STDOUT: .Destroy = %Core.Destroy // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: %Core.Int: %Int.type = import_ref Core//prelude/parts/int, Int, loaded [concrete = constants.%Int.generic] // CHECK:STDOUT: %Core.Copy: type = import_ref Core//prelude/parts/copy, Copy, loaded [concrete = constants.%Copy.type] // CHECK:STDOUT: %Core.import_ref.18d: @Int.as.Copy.impl.%Int.as.Copy.impl.Op.type (%Int.as.Copy.impl.Op.type.824) = import_ref Core//prelude/parts/int, loc{{\d+_\d+}}, loaded [symbolic = @Int.as.Copy.impl.%Int.as.Copy.impl.Op (constants.%Int.as.Copy.impl.Op.9b9)] // CHECK:STDOUT: %Copy.impl_witness_table.e76 = impl_witness_table (%Core.import_ref.18d), @Int.as.Copy.impl [concrete] // CHECK:STDOUT: %Core.DefaultOrUnformed: type = import_ref Core//prelude/parts/default, DefaultOrUnformed, loaded [concrete = constants.%DefaultOrUnformed.type] // CHECK:STDOUT: %Core.import_ref.01d: @T.as.DefaultOrUnformed.impl.%T.as.DefaultOrUnformed.impl.Op.type (%T.as.DefaultOrUnformed.impl.Op.type.6c6) = import_ref Core//prelude/parts/default, loc{{\d+_\d+}}, loaded [symbolic = @T.as.DefaultOrUnformed.impl.%T.as.DefaultOrUnformed.impl.Op (constants.%T.as.DefaultOrUnformed.impl.Op.ae1)] // CHECK:STDOUT: %DefaultOrUnformed.impl_witness_table = impl_witness_table (%Core.import_ref.01d), @T.as.DefaultOrUnformed.impl [concrete] // CHECK:STDOUT: %Core.Destroy: type = import_ref Core//prelude/parts/destroy, Destroy, loaded [concrete = constants.%Destroy.type] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .Outer = %Outer.decl // CHECK:STDOUT: .F = %F.decl // CHECK:STDOUT: .G = %G.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %Outer.decl: type = class_decl @Outer [concrete = constants.%Outer] {} {} // CHECK:STDOUT: %F.decl: %F.type = fn_decl @F [concrete = constants.%F] { // CHECK:STDOUT: %oi.patt: %pattern_type.86a = value_binding_pattern oi [concrete] // CHECK:STDOUT: %oi.param_patt: %pattern_type.86a = value_param_pattern %oi.patt [concrete] // CHECK:STDOUT: %return.patt: %pattern_type.7ce = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: %pattern_type.7ce = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %i32: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %.loc21_26: Core.Form = init_form %i32 [concrete = constants.%.ff5] // CHECK:STDOUT: %oi.param: %Inner = value_param call_param0 // CHECK:STDOUT: %.loc21_15: type = splice_block %Inner.ref [concrete = constants.%Inner] { // CHECK:STDOUT: %Outer.ref: type = name_ref Outer, file.%Outer.decl [concrete = constants.%Outer] // CHECK:STDOUT: %Inner.ref: type = name_ref Inner, @Outer.%Inner.decl [concrete = constants.%Inner] // CHECK:STDOUT: } // CHECK:STDOUT: %oi: %Inner = value_binding oi, %oi.param // CHECK:STDOUT: %return.param: ref %i32 = out_param call_param1 // CHECK:STDOUT: %return: ref %i32 = return_slot %return.param // CHECK:STDOUT: } // CHECK:STDOUT: %G.decl: %G.type = fn_decl @G [concrete = constants.%G] { // CHECK:STDOUT: %o.patt: %pattern_type.9ae = value_binding_pattern o [concrete] // CHECK:STDOUT: %o.param_patt: %pattern_type.9ae = value_param_pattern %o.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %o.param: %Outer = value_param call_param0 // CHECK:STDOUT: %Outer.ref: type = name_ref Outer, file.%Outer.decl [concrete = constants.%Outer] // CHECK:STDOUT: %o: %Outer = value_binding o, %o.param // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @Outer { // CHECK:STDOUT: %Inner.decl: type = class_decl @Inner [concrete = constants.%Inner] {} {} // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type.357] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%Outer // CHECK:STDOUT: .Inner = %Inner.decl // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @Inner { // CHECK:STDOUT: %i32: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %.loc17: %Inner.elem = field_decl n, element0 [concrete] // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%struct_type.n [concrete = constants.%complete_type.54b] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%Inner // CHECK:STDOUT: .n = %.loc17 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @F(%oi.param: %Inner) -> out %return.param: %i32 { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %oi.ref: %Inner = name_ref oi, %oi // CHECK:STDOUT: %n.ref: %Inner.elem = name_ref n, @Inner.%.loc17 [concrete = @Inner.%.loc17] // CHECK:STDOUT: %.loc22_12.1: ref %i32 = class_element_access %oi.ref, element0 // CHECK:STDOUT: %.loc22_12.2: %i32 = acquire_value %.loc22_12.1 // CHECK:STDOUT: %impl.elem0: %.8e2 = impl_witness_access constants.%Copy.impl_witness.f17, element0 [concrete = constants.%Int.as.Copy.impl.Op.664] // CHECK:STDOUT: %bound_method.loc22_12.1: = bound_method %.loc22_12.2, %impl.elem0 // CHECK:STDOUT: %specific_fn: = specific_function %impl.elem0, @Int.as.Copy.impl.Op(constants.%int_32) [concrete = constants.%Int.as.Copy.impl.Op.specific_fn] // CHECK:STDOUT: %bound_method.loc22_12.2: = bound_method %.loc22_12.2, %specific_fn // CHECK:STDOUT: %Int.as.Copy.impl.Op.call: init %i32 = call %bound_method.loc22_12.2(%.loc22_12.2) // CHECK:STDOUT: return %Int.as.Copy.impl.Op.call // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @G(%o.param: %Outer) { // CHECK:STDOUT: !entry: // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %i.patt: %pattern_type.86a = ref_binding_pattern i [concrete] // CHECK:STDOUT: %i.var_patt: %pattern_type.86a = var_pattern %i.patt [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %i.var: ref %Inner = var %i.var_patt // CHECK:STDOUT: %DefaultOrUnformed.facet: %DefaultOrUnformed.type = facet_value constants.%Inner, (constants.%DefaultOrUnformed.impl_witness.994) [concrete = constants.%DefaultOrUnformed.facet] // CHECK:STDOUT: %.loc26_24.1: %DefaultOrUnformed.type = converted constants.%Inner, %DefaultOrUnformed.facet [concrete = constants.%DefaultOrUnformed.facet] // CHECK:STDOUT: %as_type: type = facet_access_type %.loc26_24.1 [concrete = constants.%Inner] // CHECK:STDOUT: %.loc26_24.2: type = converted %.loc26_24.1, %as_type [concrete = constants.%Inner] // CHECK:STDOUT: %T.as.DefaultOrUnformed.impl.Op.specific_fn: = specific_function constants.%T.as.DefaultOrUnformed.impl.Op.da3, @T.as.DefaultOrUnformed.impl.Op(constants.%Inner) [concrete = constants.%T.as.DefaultOrUnformed.impl.Op.specific_fn] // CHECK:STDOUT: %.loc26_3: ref %Inner = splice_block %i.var {} // CHECK:STDOUT: %T.as.DefaultOrUnformed.impl.Op.call: init %Inner to %.loc26_3 = call %T.as.DefaultOrUnformed.impl.Op.specific_fn() // CHECK:STDOUT: assign %i.var, %T.as.DefaultOrUnformed.impl.Op.call // CHECK:STDOUT: %.loc26_18: type = splice_block %Inner.ref [concrete = constants.%Inner] { // CHECK:STDOUT: %o.ref: %Outer = name_ref o, %o // CHECK:STDOUT: %Inner.ref: type = name_ref Inner, @Outer.%Inner.decl [concrete = constants.%Inner] // CHECK:STDOUT: } // CHECK:STDOUT: %i: ref %Inner = ref_binding i, %i.var // CHECK:STDOUT: %Destroy.Op.bound: = bound_method %i.var, constants.%Destroy.Op // CHECK:STDOUT: %Destroy.Op.call: init %empty_tuple.type = call %Destroy.Op.bound(%i.var) // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @Destroy.Op(%self.param: ref %Inner) = "no_op"; // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/class/no_definition_in_impl_file.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/none.carbon // TODO: Add ranges and switch to "--dump-sem-ir-ranges=only". // EXTRA-ARGS: --dump-sem-ir-ranges=if-present // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/class/no_definition_in_impl_file.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/class/no_definition_in_impl_file.carbon // --- decl_in_api_definition_in_impl.carbon library "[[@TEST_NAME]]"; class A; // --- decl_in_api_definition_in_impl.impl.carbon impl library "[[@TEST_NAME]]"; class A; class A {} // --- use_decl_in_api.carbon library "[[@TEST_NAME]]"; // --- use_decl_in_api.impl.carbon impl library "[[@TEST_NAME]]"; import library "decl_in_api_definition_in_impl"; // --- decl_only_in_api.carbon library "[[@TEST_NAME]]"; class B; // --- decl_only_in_api.impl.carbon impl library "[[@TEST_NAME]]"; // --- decl_in_api_decl_in_impl.carbon library "[[@TEST_NAME]]"; class C; // --- fail_decl_in_api_decl_in_impl.impl.carbon impl library "[[@TEST_NAME]]"; // CHECK:STDERR: fail_decl_in_api_decl_in_impl.impl.carbon:[[@LINE+4]]:1: error: no definition found for declaration in impl file [MissingDefinitionInImpl] // CHECK:STDERR: class C; // CHECK:STDERR: ^~~~~~~~ // CHECK:STDERR: class C; // --- decl_only_in_impl.carbon library "[[@TEST_NAME]]"; // --- fail_decl_only_in_impl.impl.carbon impl library "[[@TEST_NAME]]"; // CHECK:STDERR: fail_decl_only_in_impl.impl.carbon:[[@LINE+4]]:1: error: no definition found for declaration in impl file [MissingDefinitionInImpl] // CHECK:STDERR: class D; // CHECK:STDERR: ^~~~~~~~ // CHECK:STDERR: class D; // CHECK:STDOUT: --- decl_in_api_definition_in_impl.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %A: type = class_type @A [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .A = %A.decl // CHECK:STDOUT: } // CHECK:STDOUT: %A.decl: type = class_decl @A [concrete = constants.%A] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @A; // CHECK:STDOUT: // CHECK:STDOUT: --- decl_in_api_definition_in_impl.impl.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %A: type = class_type @A [concrete] // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %complete_type: = complete_type_witness %empty_struct_type [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .A = %A.decl.loc4 // CHECK:STDOUT: } // CHECK:STDOUT: %default.import.loc2_46.1 = import // CHECK:STDOUT: %default.import.loc2_46.2 = import // CHECK:STDOUT: %A.decl.loc4: type = class_decl @A [concrete = constants.%A] {} {} // CHECK:STDOUT: %A.decl.loc6: type = class_decl @A [concrete = constants.%A] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @A { // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%A // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- use_decl_in_api.carbon // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- use_decl_in_api.impl.carbon // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Main.A = import_ref Main//decl_in_api_definition_in_impl, A, unloaded // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .A = imports.%Main.A // CHECK:STDOUT: } // CHECK:STDOUT: %default.import.loc2_31.1 = import // CHECK:STDOUT: %default.import.loc2_31.2 = import // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- decl_only_in_api.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %B: type = class_type @B [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .B = %B.decl // CHECK:STDOUT: } // CHECK:STDOUT: %B.decl: type = class_decl @B [concrete = constants.%B] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @B; // CHECK:STDOUT: // CHECK:STDOUT: --- decl_only_in_api.impl.carbon // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Main.B = import_ref Main//decl_only_in_api, B, unloaded // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .B = imports.%Main.B // CHECK:STDOUT: } // CHECK:STDOUT: %default.import.loc2_32.1 = import // CHECK:STDOUT: %default.import.loc2_32.2 = import // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- decl_in_api_decl_in_impl.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %C: type = class_type @C [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .C = %C.decl // CHECK:STDOUT: } // CHECK:STDOUT: %C.decl: type = class_decl @C [concrete = constants.%C] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @C; // CHECK:STDOUT: // CHECK:STDOUT: --- fail_decl_in_api_decl_in_impl.impl.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %C: type = class_type @C [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .C = %C.decl // CHECK:STDOUT: } // CHECK:STDOUT: %default.import.loc2_40.1 = import // CHECK:STDOUT: %default.import.loc2_40.2 = import // CHECK:STDOUT: %C.decl: type = class_decl @C [concrete = constants.%C] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @C; // CHECK:STDOUT: // CHECK:STDOUT: --- decl_only_in_impl.carbon // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- fail_decl_only_in_impl.impl.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %D: type = class_type @D [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .D = %D.decl // CHECK:STDOUT: } // CHECK:STDOUT: %default.import.loc2_33.1 = import // CHECK:STDOUT: %default.import.loc2_33.2 = import // CHECK:STDOUT: %D.decl: type = class_decl @D [concrete = constants.%D] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @D; // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/class/partial/init.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/convert.carbon // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/class/partial/init.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/class/partial/init.carbon // --- base.carbon library "[[@TEST_NAME]]"; base class Base { fn Make() -> partial Self { return {}; } } class Derived { extend base: Base; fn Make() -> Self { //@dump-sem-ir-begin return {.base = Base.Make()}; //@dump-sem-ir-end } } // CHECK:STDOUT: --- base.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %Base: type = class_type @Base [concrete] // CHECK:STDOUT: %.7a5: type = partial_type %Base [concrete] // CHECK:STDOUT: %Base.Make.type: type = fn_type @Base.Make [concrete] // CHECK:STDOUT: %Base.Make: %Base.Make.type = struct_value () [concrete] // CHECK:STDOUT: %Derived: type = class_type @Derived [concrete] // CHECK:STDOUT: %struct_type.base.cad: type = struct_type {.base: %.7a5} [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @Derived { // CHECK:STDOUT: // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%Derived // CHECK:STDOUT: .Base = // CHECK:STDOUT: .base = %.loc11 // CHECK:STDOUT: .Make = %Derived.Make.decl // CHECK:STDOUT: extend %Base.ref // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @Derived.Make() -> out %return.param: %Derived { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %Base.ref: type = name_ref Base, file.%Base.decl [concrete = constants.%Base] // CHECK:STDOUT: %Make.ref: %Base.Make.type = name_ref Make, @Base.%Base.Make.decl [concrete = constants.%Base.Make] // CHECK:STDOUT: %.loc14_32.1: ref %.7a5 = class_element_access %return.param, element0 // CHECK:STDOUT: %Base.Make.call: init %.7a5 to %.loc14_32.1 = call %Make.ref() // CHECK:STDOUT: %.loc14_32.2: %struct_type.base.cad = struct_literal (%Base.Make.call) // CHECK:STDOUT: %.loc14_32.3: init %Base = as_compatible %Base.Make.call // CHECK:STDOUT: %.loc14_32.4: init %Derived to %return.param = class_init (%.loc14_32.3) // CHECK:STDOUT: %.loc14_33: init %Derived = converted %.loc14_32.2, %.loc14_32.4 // CHECK:STDOUT: return %.loc14_33 to %return.param // CHECK:STDOUT: } // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/class/partial/qualifier.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/convert.carbon // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/class/partial/qualifier.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/class/partial/qualifier.carbon // --- base.carbon library "[[@TEST_NAME]]"; base class C { } //@dump-sem-ir-begin fn A(p: partial C); //@dump-sem-ir-end // --- abstract.carbon library "[[@TEST_NAME]]"; abstract class C { } //@dump-sem-ir-begin fn A(p: partial C); //@dump-sem-ir-end // --- abstract_var.carbon library "[[@TEST_NAME]]"; abstract class C { } //@dump-sem-ir-begin fn A(var p: partial C); //@dump-sem-ir-end // --- fail_partial_nondynamic.carbon library "[[@TEST_NAME]]"; class C { } //@dump-sem-ir-begin // CHECK:STDERR: fail_partial_nondynamic.carbon:[[@LINE+4]]:9: error: `partial` applied to final type `C` [PartialOnFinal] // CHECK:STDERR: fn G(p: partial C); // CHECK:STDERR: ^~~~~~~~~ // CHECK:STDERR: fn G(p: partial C); //@dump-sem-ir-end // --- fail_partial_final.carbon library "[[@TEST_NAME]]"; base class Base { } class Derived { extend base: Base; } //@dump-sem-ir-begin // CHECK:STDERR: fail_partial_final.carbon:[[@LINE+4]]:9: error: `partial` applied to final type `Derived` [PartialOnFinal] // CHECK:STDERR: fn G(p: partial Derived); // CHECK:STDERR: ^~~~~~~~~~~~~~~ // CHECK:STDERR: fn G(p: partial Derived); //@dump-sem-ir-end // --- fail_partial_decl.carbon library "[[@TEST_NAME]]"; class C; // TODO: This diagnostic could be more specific - the type might be non-final, // but since we only have a declaration, we don't know. //@dump-sem-ir-begin // applied to final type `C` [PartialOnFinal] CHECK:STDERR: fn G(p: partial C); // CHECK:STDERR: fail_partial_decl.carbon:[[@LINE+4]]:9: error: `partial` applied to final type `C` [PartialOnFinal] // CHECK:STDERR: fn G(p: partial C); // CHECK:STDERR: ^~~~~~~~~ // CHECK:STDERR: fn G(p: partial C); //@dump-sem-ir-end // --- fail_partial_tuple.carbon library "[[@TEST_NAME]]"; class C; //@dump-sem-ir-begin // CHECK:STDERR: fail_partial_tuple.carbon:[[@LINE+4]]:9: error: `partial` applied to final type `(C, C)` [PartialOnFinal] // CHECK:STDERR: fn G(p: partial (C, C)); // CHECK:STDERR: ^~~~~~~~~~~~~~ // CHECK:STDERR: fn G(p: partial (C, C)); //@dump-sem-ir-end // --- fail_partial_struct.carbon library "[[@TEST_NAME]]"; class C; //@dump-sem-ir-begin // CHECK:STDERR: fail_partial_struct.carbon:[[@LINE+4]]:9: error: `partial` applied to final type `{.x: C}` [PartialOnFinal] // CHECK:STDERR: fn G(p: partial {.x: C}); // CHECK:STDERR: ^~~~~~~~~~~~~~~ // CHECK:STDERR: fn G(p: partial {.x: C}); //@dump-sem-ir-end // --- fail_duplicate.carbon library "[[@TEST_NAME]]"; base class C { } //@dump-sem-ir-begin // CHECK:STDERR: fail_duplicate.carbon:[[@LINE+4]]:9: error: `partial` applied to final type `partial C` [PartialOnFinal] // CHECK:STDERR: fn F(p: partial (partial C)); // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: fn F(p: partial (partial C)); //@dump-sem-ir-end // --- fail_convert_to_nonpartial.carbon library "[[@TEST_NAME]]"; base class C { } fn G(p: partial C*) -> C* { // CHECK:STDERR: fail_convert_to_nonpartial.carbon:[[@LINE+7]]:3: error: cannot implicitly convert expression of type `partial C*` to `C*` [ConversionFailure] // CHECK:STDERR: return p; // CHECK:STDERR: ^~~~~~~~~ // CHECK:STDERR: fail_convert_to_nonpartial.carbon:[[@LINE+4]]:3: note: type `partial C*` does not implement interface `Core.ImplicitAs(C*)` [MissingImplInMemberAccessInContext] // CHECK:STDERR: return p; // CHECK:STDERR: ^~~~~~~~~ // CHECK:STDERR: return p; } // --- fail_derive_from_partial.carbon library "[[@TEST_NAME]]"; base class C { } class Derived { // CHECK:STDERR: fail_derive_from_partial.carbon:[[@LINE+4]]:16: error: deriving from final type `partial C`; base type must be an `abstract` or `base` class [BaseIsFinal] // CHECK:STDERR: extend base: partial C; // CHECK:STDERR: ^~~~~~~~~ // CHECK:STDERR: extend base: partial C; } // --- fail_todo_partial_template_dependent.carbon library "[[@TEST_NAME]]"; // TODO: This should be accepted because `T` might be final once we know what it // is. // CHECK:STDERR: fail_todo_partial_template_dependent.carbon:[[@LINE+4]]:28: error: `partial` applied to final type `T` [PartialOnFinal] // CHECK:STDERR: fn G[template T:! type](p: partial T*); // CHECK:STDERR: ^~~~~~~~~ // CHECK:STDERR: fn G[template T:! type](p: partial T*); // --- fail_partial_generic.carbon library "[[@TEST_NAME]]"; // TODO: Maybe rephrase this to use some common/generic phrasing to refer to the // generic type and its requirements, as distinct from the concrete type that // might be used here in any specific. // CHECK:STDERR: fail_partial_generic.carbon:[[@LINE+4]]:19: error: `partial` applied to final type `T` [PartialOnFinal] // CHECK:STDERR: fn F[T:! type](p: partial T*); // CHECK:STDERR: ^~~~~~~~~ // CHECK:STDERR: fn F[T:! type](p: partial T*); // CHECK:STDOUT: --- base.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %C: type = class_type @C [concrete] // CHECK:STDOUT: %.e97: type = partial_type %C [concrete] // CHECK:STDOUT: %pattern_type: type = pattern_type %.e97 [concrete] // CHECK:STDOUT: %A.type: type = fn_type @A [concrete] // CHECK:STDOUT: %A: %A.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: %A.decl: %A.type = fn_decl @A [concrete = constants.%A] { // CHECK:STDOUT: %p.patt: %pattern_type = value_binding_pattern p [concrete] // CHECK:STDOUT: %p.param_patt: %pattern_type = value_param_pattern %p.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %p.param: %.e97 = value_param call_param0 // CHECK:STDOUT: %.loc6_9.1: type = splice_block %.loc6_9.2 [concrete = constants.%.e97] { // CHECK:STDOUT: %C.ref: type = name_ref C, file.%C.decl [concrete = constants.%C] // CHECK:STDOUT: %.loc6_9.2: type = partial_type %C.ref [concrete = constants.%.e97] // CHECK:STDOUT: } // CHECK:STDOUT: %p: %.e97 = value_binding p, %p.param // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @A(%p.param: %.e97); // CHECK:STDOUT: // CHECK:STDOUT: --- abstract.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %C: type = class_type @C [concrete] // CHECK:STDOUT: %.e97: type = partial_type %C [concrete] // CHECK:STDOUT: %pattern_type: type = pattern_type %.e97 [concrete] // CHECK:STDOUT: %A.type: type = fn_type @A [concrete] // CHECK:STDOUT: %A: %A.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: %A.decl: %A.type = fn_decl @A [concrete = constants.%A] { // CHECK:STDOUT: %p.patt: %pattern_type = value_binding_pattern p [concrete] // CHECK:STDOUT: %p.param_patt: %pattern_type = value_param_pattern %p.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %p.param: %.e97 = value_param call_param0 // CHECK:STDOUT: %.loc6_9.1: type = splice_block %.loc6_9.2 [concrete = constants.%.e97] { // CHECK:STDOUT: %C.ref: type = name_ref C, file.%C.decl [concrete = constants.%C] // CHECK:STDOUT: %.loc6_9.2: type = partial_type %C.ref [concrete = constants.%.e97] // CHECK:STDOUT: } // CHECK:STDOUT: %p: %.e97 = value_binding p, %p.param // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @A(%p.param: %.e97); // CHECK:STDOUT: // CHECK:STDOUT: --- abstract_var.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %C: type = class_type @C [concrete] // CHECK:STDOUT: %.e97: type = partial_type %C [concrete] // CHECK:STDOUT: %pattern_type: type = pattern_type %.e97 [concrete] // CHECK:STDOUT: %A.type: type = fn_type @A [concrete] // CHECK:STDOUT: %A: %A.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: %A.decl: %A.type = fn_decl @A [concrete = constants.%A] { // CHECK:STDOUT: %p.patt: %pattern_type = ref_binding_pattern p [concrete] // CHECK:STDOUT: %p.param_patt: %pattern_type = var_param_pattern %p.patt [concrete] // CHECK:STDOUT: %p.var_patt: %pattern_type = var_pattern %p.param_patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %p.param: ref %.e97 = ref_param call_param0 // CHECK:STDOUT: %.loc6_13.1: type = splice_block %.loc6_13.2 [concrete = constants.%.e97] { // CHECK:STDOUT: %C.ref: type = name_ref C, file.%C.decl [concrete = constants.%C] // CHECK:STDOUT: %.loc6_13.2: type = partial_type %C.ref [concrete = constants.%.e97] // CHECK:STDOUT: } // CHECK:STDOUT: %p: ref %.e97 = ref_binding p, %p.param // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @A(%p.param: ref %.e97); // CHECK:STDOUT: // CHECK:STDOUT: --- fail_partial_nondynamic.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %C: type = class_type @C [concrete] // CHECK:STDOUT: %.e97: type = partial_type %C [concrete] // CHECK:STDOUT: %pattern_type: type = pattern_type %.e97 [concrete] // CHECK:STDOUT: %G.type: type = fn_type @G [concrete] // CHECK:STDOUT: %G: %G.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: %G.decl: %G.type = fn_decl @G [concrete = constants.%G] { // CHECK:STDOUT: %p.patt: %pattern_type = value_binding_pattern p [concrete] // CHECK:STDOUT: %p.param_patt: %pattern_type = value_param_pattern %p.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %p.param: %.e97 = value_param call_param0 // CHECK:STDOUT: %.loc10_9.1: type = splice_block %.loc10_9.2 [concrete = constants.%.e97] { // CHECK:STDOUT: %C.ref: type = name_ref C, file.%C.decl [concrete = constants.%C] // CHECK:STDOUT: %.loc10_9.2: type = partial_type %C.ref [concrete = constants.%.e97] // CHECK:STDOUT: } // CHECK:STDOUT: %p: %.e97 = value_binding p, %p.param // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @G(%p.param: %.e97); // CHECK:STDOUT: // CHECK:STDOUT: --- fail_partial_final.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %Derived: type = class_type @Derived [concrete] // CHECK:STDOUT: %.d37: type = partial_type %Derived [concrete] // CHECK:STDOUT: %pattern_type: type = pattern_type %.d37 [concrete] // CHECK:STDOUT: %G.type: type = fn_type @G [concrete] // CHECK:STDOUT: %G: %G.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: %G.decl: %G.type = fn_decl @G [concrete = constants.%G] { // CHECK:STDOUT: %p.patt: %pattern_type = value_binding_pattern p [concrete] // CHECK:STDOUT: %p.param_patt: %pattern_type = value_param_pattern %p.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %p.param: %.d37 = value_param call_param0 // CHECK:STDOUT: %.loc13_9.1: type = splice_block %.loc13_9.2 [concrete = constants.%.d37] { // CHECK:STDOUT: %Derived.ref: type = name_ref Derived, file.%Derived.decl [concrete = constants.%Derived] // CHECK:STDOUT: %.loc13_9.2: type = partial_type %Derived.ref [concrete = constants.%.d37] // CHECK:STDOUT: } // CHECK:STDOUT: %p: %.d37 = value_binding p, %p.param // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @G(%p.param: %.d37); // CHECK:STDOUT: // CHECK:STDOUT: --- fail_partial_decl.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %C: type = class_type @C [concrete] // CHECK:STDOUT: %.e97: type = partial_type %C [concrete] // CHECK:STDOUT: %pattern_type: type = pattern_type %.e97 [concrete] // CHECK:STDOUT: %G.type: type = fn_type @G [concrete] // CHECK:STDOUT: %G: %G.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: %G.decl: %G.type = fn_decl @G [concrete = constants.%G] { // CHECK:STDOUT: %p.patt: %pattern_type = value_binding_pattern p [concrete] // CHECK:STDOUT: %p.param_patt: %pattern_type = value_param_pattern %p.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %p.param: %.e97 = value_param call_param0 // CHECK:STDOUT: %.loc13_9.1: type = splice_block %.loc13_9.2 [concrete = constants.%.e97] { // CHECK:STDOUT: %C.ref: type = name_ref C, file.%C.decl [concrete = constants.%C] // CHECK:STDOUT: %.loc13_9.2: type = partial_type %C.ref [concrete = constants.%.e97] // CHECK:STDOUT: } // CHECK:STDOUT: %p: %.e97 = value_binding p, %p.param // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @G(%p.param: %.e97); // CHECK:STDOUT: // CHECK:STDOUT: --- fail_partial_tuple.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %C: type = class_type @C [concrete] // CHECK:STDOUT: %tuple.type.24b: type = tuple_type (type, type) [concrete] // CHECK:STDOUT: %tuple: %tuple.type.24b = tuple_value (%C, %C) [concrete] // CHECK:STDOUT: %tuple.type.d23: type = tuple_type (%C, %C) [concrete] // CHECK:STDOUT: %.42f: type = partial_type %tuple.type.d23 [concrete] // CHECK:STDOUT: %pattern_type: type = pattern_type %.42f [concrete] // CHECK:STDOUT: %G.type: type = fn_type @G [concrete] // CHECK:STDOUT: %G: %G.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: %G.decl: %G.type = fn_decl @G [concrete = constants.%G] { // CHECK:STDOUT: %p.patt: %pattern_type = value_binding_pattern p [concrete] // CHECK:STDOUT: %p.param_patt: %pattern_type = value_param_pattern %p.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %p.param: %.42f = value_param call_param0 // CHECK:STDOUT: %.loc10_9.1: type = splice_block %.loc10_9.3 [concrete = constants.%.42f] { // CHECK:STDOUT: %C.ref.loc10_18: type = name_ref C, file.%C.decl [concrete = constants.%C] // CHECK:STDOUT: %C.ref.loc10_21: type = name_ref C, file.%C.decl [concrete = constants.%C] // CHECK:STDOUT: %.loc10_22: %tuple.type.24b = tuple_literal (%C.ref.loc10_18, %C.ref.loc10_21) [concrete = constants.%tuple] // CHECK:STDOUT: %.loc10_9.2: type = converted %.loc10_22, constants.%tuple.type.d23 [concrete = constants.%tuple.type.d23] // CHECK:STDOUT: %.loc10_9.3: type = partial_type %.loc10_9.2 [concrete = constants.%.42f] // CHECK:STDOUT: } // CHECK:STDOUT: %p: %.42f = value_binding p, %p.param // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @G(%p.param: %.42f); // CHECK:STDOUT: // CHECK:STDOUT: --- fail_partial_struct.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %C: type = class_type @C [concrete] // CHECK:STDOUT: %struct_type.x: type = struct_type {.x: %C} [concrete] // CHECK:STDOUT: %.be9: type = partial_type %struct_type.x [concrete] // CHECK:STDOUT: %pattern_type: type = pattern_type %.be9 [concrete] // CHECK:STDOUT: %G.type: type = fn_type @G [concrete] // CHECK:STDOUT: %G: %G.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: %G.decl: %G.type = fn_decl @G [concrete = constants.%G] { // CHECK:STDOUT: %p.patt: %pattern_type = value_binding_pattern p [concrete] // CHECK:STDOUT: %p.param_patt: %pattern_type = value_param_pattern %p.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %p.param: %.be9 = value_param call_param0 // CHECK:STDOUT: %.loc10_9.1: type = splice_block %.loc10_9.2 [concrete = constants.%.be9] { // CHECK:STDOUT: %C.ref: type = name_ref C, file.%C.decl [concrete = constants.%C] // CHECK:STDOUT: %struct_type.x: type = struct_type {.x: %C} [concrete = constants.%struct_type.x] // CHECK:STDOUT: %.loc10_9.2: type = partial_type %struct_type.x [concrete = constants.%.be9] // CHECK:STDOUT: } // CHECK:STDOUT: %p: %.be9 = value_binding p, %p.param // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @G(%p.param: %.be9); // CHECK:STDOUT: // CHECK:STDOUT: --- fail_duplicate.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %C: type = class_type @C [concrete] // CHECK:STDOUT: %.e97: type = partial_type %C [concrete] // CHECK:STDOUT: %.d45: type = partial_type %.e97 [concrete] // CHECK:STDOUT: %pattern_type: type = pattern_type %.d45 [concrete] // CHECK:STDOUT: %F.type: type = fn_type @F [concrete] // CHECK:STDOUT: %F: %F.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: %F.decl: %F.type = fn_decl @F [concrete = constants.%F] { // CHECK:STDOUT: %p.patt: %pattern_type = value_binding_pattern p [concrete] // CHECK:STDOUT: %p.param_patt: %pattern_type = value_param_pattern %p.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %p.param: %.d45 = value_param call_param0 // CHECK:STDOUT: %.loc10_9.1: type = splice_block %.loc10_9.2 [concrete = constants.%.d45] { // CHECK:STDOUT: %C.ref: type = name_ref C, file.%C.decl [concrete = constants.%C] // CHECK:STDOUT: %.loc10_18: type = partial_type %C.ref [concrete = constants.%.e97] // CHECK:STDOUT: %.loc10_9.2: type = partial_type %.loc10_18 [concrete = constants.%.d45] // CHECK:STDOUT: } // CHECK:STDOUT: %p: %.d45 = value_binding p, %p.param // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @F(%p.param: %.d45); // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/class/redeclaration.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/convert.carbon // TODO: Add ranges and switch to "--dump-sem-ir-ranges=only". // EXTRA-ARGS: --dump-sem-ir-ranges=if-present // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/class/redeclaration.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/class/redeclaration.carbon class Class; class Class { fn F[self: Self](b: ()); } fn Class.F[unused self: Self](unused b: ()) {} // CHECK:STDOUT: --- redeclaration.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %Class: type = class_type @Class [concrete] // CHECK:STDOUT: %pattern_type.904: type = pattern_type %Class [concrete] // CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] // CHECK:STDOUT: %empty_tuple: %empty_tuple.type = tuple_value () [concrete] // CHECK:STDOUT: %pattern_type.cb1: type = pattern_type %empty_tuple.type [concrete] // CHECK:STDOUT: %Class.F.type: type = fn_type @Class.F [concrete] // CHECK:STDOUT: %Class.F: %Class.F.type = struct_value () [concrete] // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %complete_type: = complete_type_witness %empty_struct_type [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .Class = %Class.decl.loc15 // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %Class.decl.loc15: type = class_decl @Class [concrete = constants.%Class] {} {} // CHECK:STDOUT: %Class.decl.loc17: type = class_decl @Class [concrete = constants.%Class] {} {} // CHECK:STDOUT: %Class.F.decl: %Class.F.type = fn_decl @Class.F [concrete = constants.%Class.F] { // CHECK:STDOUT: %self.patt: %pattern_type.904 = value_binding_pattern self [concrete] // CHECK:STDOUT: %self.param_patt: %pattern_type.904 = value_param_pattern %self.patt [concrete] // CHECK:STDOUT: %b.patt: %pattern_type.cb1 = value_binding_pattern b [concrete] // CHECK:STDOUT: %b.param_patt: %pattern_type.cb1 = value_param_pattern %b.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %self.param.loc21: %Class = value_param call_param0 // CHECK:STDOUT: %Self.ref.loc21: type = name_ref Self, constants.%Class [concrete = constants.%Class] // CHECK:STDOUT: %self.loc21: %Class = value_binding self, %self.param.loc21 // CHECK:STDOUT: %b.param.loc21: %empty_tuple.type = value_param call_param1 // CHECK:STDOUT: %.loc21_42.1: type = splice_block %.loc21_42.3 [concrete = constants.%empty_tuple.type] { // CHECK:STDOUT: %.loc21_42.2: %empty_tuple.type = tuple_literal () [concrete = constants.%empty_tuple] // CHECK:STDOUT: %.loc21_42.3: type = converted %.loc21_42.2, constants.%empty_tuple.type [concrete = constants.%empty_tuple.type] // CHECK:STDOUT: } // CHECK:STDOUT: %b.loc21: %empty_tuple.type = value_binding b, %b.param.loc21 // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @Class { // CHECK:STDOUT: %Class.F.decl: %Class.F.type = fn_decl @Class.F [concrete = constants.%Class.F] { // CHECK:STDOUT: %self.patt: %pattern_type.904 = value_binding_pattern self [concrete] // CHECK:STDOUT: %self.param_patt: %pattern_type.904 = value_param_pattern %self.patt [concrete] // CHECK:STDOUT: %b.patt: %pattern_type.cb1 = value_binding_pattern b [concrete] // CHECK:STDOUT: %b.param_patt: %pattern_type.cb1 = value_param_pattern %b.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %self.param.loc18: %Class = value_param call_param0 // CHECK:STDOUT: %Self.ref.loc18: type = name_ref Self, constants.%Class [concrete = constants.%Class] // CHECK:STDOUT: %self.loc18: %Class = value_binding self, %self.param.loc18 // CHECK:STDOUT: %b.param.loc18: %empty_tuple.type = value_param call_param1 // CHECK:STDOUT: %.loc18_24.1: type = splice_block %.loc18_24.3 [concrete = constants.%empty_tuple.type] { // CHECK:STDOUT: %.loc18_24.2: %empty_tuple.type = tuple_literal () [concrete = constants.%empty_tuple] // CHECK:STDOUT: %.loc18_24.3: type = converted %.loc18_24.2, constants.%empty_tuple.type [concrete = constants.%empty_tuple.type] // CHECK:STDOUT: } // CHECK:STDOUT: %b.loc18: %empty_tuple.type = value_binding b, %b.param.loc18 // CHECK:STDOUT: } // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%Class // CHECK:STDOUT: .F = %Class.F.decl // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @Class.F(%self.param.loc21: %Class, %b.param.loc21: %empty_tuple.type) { // CHECK:STDOUT: !entry: // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/class/redeclaration_introducer.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/convert.carbon // TODO: Add ranges and switch to "--dump-sem-ir-ranges=only". // EXTRA-ARGS: --dump-sem-ir-ranges=if-present // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/class/redeclaration_introducer.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/class/redeclaration_introducer.carbon class A; class B; class C; class A {} base class B {} abstract class C {} // CHECK:STDOUT: --- redeclaration_introducer.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %A: type = class_type @A [concrete] // CHECK:STDOUT: %B: type = class_type @B [concrete] // CHECK:STDOUT: %C: type = class_type @C [concrete] // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %complete_type: = complete_type_witness %empty_struct_type [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .A = %A.decl.loc15 // CHECK:STDOUT: .B = %B.decl.loc16 // CHECK:STDOUT: .C = %C.decl.loc17 // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %A.decl.loc15: type = class_decl @A [concrete = constants.%A] {} {} // CHECK:STDOUT: %B.decl.loc16: type = class_decl @B [concrete = constants.%B] {} {} // CHECK:STDOUT: %C.decl.loc17: type = class_decl @C [concrete = constants.%C] {} {} // CHECK:STDOUT: %A.decl.loc19: type = class_decl @A [concrete = constants.%A] {} {} // CHECK:STDOUT: %B.decl.loc20: type = class_decl @B [concrete = constants.%B] {} {} // CHECK:STDOUT: %C.decl.loc21: type = class_decl @C [concrete = constants.%C] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @A { // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%A // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @B { // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%B // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @C { // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%C // CHECK:STDOUT: } // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/class/reenter_scope.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/int.carbon // TODO: Add ranges and switch to "--dump-sem-ir-ranges=only". // EXTRA-ARGS: --dump-sem-ir-ranges=if-present // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/class/reenter_scope.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/class/reenter_scope.carbon class Class { fn F() -> i32; fn G() -> i32; } fn Class.F() -> i32 { Self.G(); return G(); } // CHECK:STDOUT: --- reenter_scope.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %Class: type = class_type @Class [concrete] // CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [concrete] // CHECK:STDOUT: %Int.type: type = generic_class_type @Int [concrete] // CHECK:STDOUT: %Int.generic: %Int.type = struct_value () [concrete] // CHECK:STDOUT: %i32: type = class_type @Int, @Int(%int_32) [concrete] // CHECK:STDOUT: %.ff5: Core.Form = init_form %i32 [concrete] // CHECK:STDOUT: %pattern_type.7ce: type = pattern_type %i32 [concrete] // CHECK:STDOUT: %Class.F.type: type = fn_type @Class.F [concrete] // CHECK:STDOUT: %Class.F: %Class.F.type = struct_value () [concrete] // CHECK:STDOUT: %Class.G.type: type = fn_type @Class.G [concrete] // CHECK:STDOUT: %Class.G: %Class.G.type = struct_value () [concrete] // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %complete_type.357: = complete_type_witness %empty_struct_type [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: .Int = %Core.Int // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: %Core.Int: %Int.type = import_ref Core//prelude/parts/int, Int, loaded [concrete = constants.%Int.generic] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .Class = %Class.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %Class.decl: type = class_decl @Class [concrete = constants.%Class] {} {} // CHECK:STDOUT: %Class.F.decl: %Class.F.type = fn_decl @Class.F [concrete = constants.%Class.F] { // CHECK:STDOUT: %return.patt: %pattern_type.7ce = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: %pattern_type.7ce = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %i32.loc20: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %.loc20: Core.Form = init_form %i32.loc20 [concrete = constants.%.ff5] // CHECK:STDOUT: %return.param.loc20: ref %i32 = out_param call_param0 // CHECK:STDOUT: %return.loc20: ref %i32 = return_slot %return.param.loc20 // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @Class { // CHECK:STDOUT: %Class.F.decl: %Class.F.type = fn_decl @Class.F [concrete = constants.%Class.F] { // CHECK:STDOUT: %return.patt: %pattern_type.7ce = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: %pattern_type.7ce = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %i32.loc16: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %.loc16: Core.Form = init_form %i32.loc16 [concrete = constants.%.ff5] // CHECK:STDOUT: %return.param.loc16: ref %i32 = out_param call_param0 // CHECK:STDOUT: %return.loc16: ref %i32 = return_slot %return.param.loc16 // CHECK:STDOUT: } // CHECK:STDOUT: %Class.G.decl: %Class.G.type = fn_decl @Class.G [concrete = constants.%Class.G] { // CHECK:STDOUT: %return.patt: %pattern_type.7ce = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: %pattern_type.7ce = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %i32: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %.loc17: Core.Form = init_form %i32 [concrete = constants.%.ff5] // CHECK:STDOUT: %return.param: ref %i32 = out_param call_param0 // CHECK:STDOUT: %return: ref %i32 = return_slot %return.param // CHECK:STDOUT: } // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type.357] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%Class // CHECK:STDOUT: .F = %Class.F.decl // CHECK:STDOUT: .G = %Class.G.decl // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @Class.F() -> out %return.param.loc20: %i32 { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %Self.ref: type = name_ref Self, constants.%Class [concrete = constants.%Class] // CHECK:STDOUT: %G.ref.loc21: %Class.G.type = name_ref G, @Class.%Class.G.decl [concrete = constants.%Class.G] // CHECK:STDOUT: %Class.G.call.loc21: init %i32 = call %G.ref.loc21() // CHECK:STDOUT: %G.ref.loc22: %Class.G.type = name_ref G, @Class.%Class.G.decl [concrete = constants.%Class.G] // CHECK:STDOUT: %Class.G.call.loc22: init %i32 = call %G.ref.loc22() // CHECK:STDOUT: return %Class.G.call.loc22 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @Class.G() -> out %return.param: %i32; // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/class/reorder.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/int.carbon // TODO: Add ranges and switch to "--dump-sem-ir-ranges=only". // EXTRA-ARGS: --dump-sem-ir-ranges=if-present // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/class/reorder.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/class/reorder.carbon class Class { fn G() -> i32 { return Class.F(); } fn F() -> i32 { return 1; } } // CHECK:STDOUT: --- reorder.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %Class: type = class_type @Class [concrete] // CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [concrete] // CHECK:STDOUT: %Int.type: type = generic_class_type @Int [concrete] // CHECK:STDOUT: %Int.generic: %Int.type = struct_value () [concrete] // CHECK:STDOUT: %i32: type = class_type @Int, @Int(%int_32) [concrete] // CHECK:STDOUT: %.ff5: Core.Form = init_form %i32 [concrete] // CHECK:STDOUT: %pattern_type.7ce: type = pattern_type %i32 [concrete] // CHECK:STDOUT: %Class.G.type: type = fn_type @Class.G [concrete] // CHECK:STDOUT: %Class.G: %Class.G.type = struct_value () [concrete] // CHECK:STDOUT: %Class.F.type: type = fn_type @Class.F [concrete] // CHECK:STDOUT: %Class.F: %Class.F.type = struct_value () [concrete] // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %complete_type.357: = complete_type_witness %empty_struct_type [concrete] // CHECK:STDOUT: %int_1.5b8: Core.IntLiteral = int_value 1 [concrete] // CHECK:STDOUT: %ImplicitAs.type.cc7: type = generic_interface_type @ImplicitAs [concrete] // CHECK:STDOUT: %ImplicitAs.generic: %ImplicitAs.type.cc7 = struct_value () [concrete] // CHECK:STDOUT: %ImplicitAs.type.e8c: type = facet_type <@ImplicitAs, @ImplicitAs(%i32)> [concrete] // CHECK:STDOUT: %To: Core.IntLiteral = symbolic_binding To, 0 [symbolic] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.4e6: type = fn_type @Core.IntLiteral.as.ImplicitAs.impl.Convert, @Core.IntLiteral.as.ImplicitAs.impl(%To) [symbolic] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.3c2: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.4e6 = struct_value () [symbolic] // CHECK:STDOUT: %ImplicitAs.impl_witness.6bc: = impl_witness imports.%ImplicitAs.impl_witness_table.74f, @Core.IntLiteral.as.ImplicitAs.impl(%int_32) [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.e0d: type = fn_type @Core.IntLiteral.as.ImplicitAs.impl.Convert, @Core.IntLiteral.as.ImplicitAs.impl(%int_32) [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.e0d = struct_value () [concrete] // CHECK:STDOUT: %ImplicitAs.facet: %ImplicitAs.type.e8c = facet_value Core.IntLiteral, (%ImplicitAs.impl_witness.6bc) [concrete] // CHECK:STDOUT: %ImplicitAs.WithSelf.Convert.type.b37: type = fn_type @ImplicitAs.WithSelf.Convert, @ImplicitAs.WithSelf(%i32, %ImplicitAs.facet) [concrete] // CHECK:STDOUT: %.545: type = fn_type_with_self_type %ImplicitAs.WithSelf.Convert.type.b37, %ImplicitAs.facet [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.bound: = bound_method %int_1.5b8, %Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5 [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn: = specific_function %Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5, @Core.IntLiteral.as.ImplicitAs.impl.Convert(%int_32) [concrete] // CHECK:STDOUT: %bound_method: = bound_method %int_1.5b8, %Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn [concrete] // CHECK:STDOUT: %int_1.5d2: %i32 = int_value 1 [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: .Int = %Core.Int // CHECK:STDOUT: .ImplicitAs = %Core.ImplicitAs // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: %Core.Int: %Int.type = import_ref Core//prelude/parts/int, Int, loaded [concrete = constants.%Int.generic] // CHECK:STDOUT: %Core.ImplicitAs: %ImplicitAs.type.cc7 = import_ref Core//prelude/parts/as, ImplicitAs, loaded [concrete = constants.%ImplicitAs.generic] // CHECK:STDOUT: %Core.import_ref.42d: @Core.IntLiteral.as.ImplicitAs.impl.%Core.IntLiteral.as.ImplicitAs.impl.Convert.type (%Core.IntLiteral.as.ImplicitAs.impl.Convert.type.4e6) = import_ref Core//prelude/parts/int, loc{{\d+_\d+}}, loaded [symbolic = @Core.IntLiteral.as.ImplicitAs.impl.%Core.IntLiteral.as.ImplicitAs.impl.Convert (constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.3c2)] // CHECK:STDOUT: %ImplicitAs.impl_witness_table.74f = impl_witness_table (%Core.import_ref.42d), @Core.IntLiteral.as.ImplicitAs.impl [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .Class = %Class.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %Class.decl: type = class_decl @Class [concrete = constants.%Class] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @Class { // CHECK:STDOUT: %Class.G.decl: %Class.G.type = fn_decl @Class.G [concrete = constants.%Class.G] { // CHECK:STDOUT: %return.patt: %pattern_type.7ce = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: %pattern_type.7ce = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %i32: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %.loc16: Core.Form = init_form %i32 [concrete = constants.%.ff5] // CHECK:STDOUT: %return.param: ref %i32 = out_param call_param0 // CHECK:STDOUT: %return: ref %i32 = return_slot %return.param // CHECK:STDOUT: } // CHECK:STDOUT: %Class.F.decl: %Class.F.type = fn_decl @Class.F [concrete = constants.%Class.F] { // CHECK:STDOUT: %return.patt: %pattern_type.7ce = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: %pattern_type.7ce = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %i32: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %.loc20: Core.Form = init_form %i32 [concrete = constants.%.ff5] // CHECK:STDOUT: %return.param: ref %i32 = out_param call_param0 // CHECK:STDOUT: %return: ref %i32 = return_slot %return.param // CHECK:STDOUT: } // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type.357] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%Class // CHECK:STDOUT: .G = %Class.G.decl // CHECK:STDOUT: .F = %Class.F.decl // CHECK:STDOUT: .Class = // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @Class.G() -> out %return.param: %i32 { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %Class.ref: type = name_ref Class, file.%Class.decl [concrete = constants.%Class] // CHECK:STDOUT: %F.ref: %Class.F.type = name_ref F, @Class.%Class.F.decl [concrete = constants.%Class.F] // CHECK:STDOUT: %Class.F.call: init %i32 = call %F.ref() // CHECK:STDOUT: return %Class.F.call // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @Class.F() -> out %return.param: %i32 { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %int_1: Core.IntLiteral = int_value 1 [concrete = constants.%int_1.5b8] // CHECK:STDOUT: %impl.elem0: %.545 = impl_witness_access constants.%ImplicitAs.impl_witness.6bc, element0 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5] // CHECK:STDOUT: %bound_method.loc21_13.1: = bound_method %int_1, %impl.elem0 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.bound] // CHECK:STDOUT: %specific_fn: = specific_function %impl.elem0, @Core.IntLiteral.as.ImplicitAs.impl.Convert(constants.%int_32) [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn] // CHECK:STDOUT: %bound_method.loc21_13.2: = bound_method %int_1, %specific_fn [concrete = constants.%bound_method] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.call: init %i32 = call %bound_method.loc21_13.2(%int_1) [concrete = constants.%int_1.5d2] // CHECK:STDOUT: %.loc21: init %i32 = converted %int_1, %Core.IntLiteral.as.ImplicitAs.impl.Convert.call [concrete = constants.%int_1.5d2] // CHECK:STDOUT: return %.loc21 // CHECK:STDOUT: } // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/class/reorder_qualified.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/int.carbon // TODO: Add ranges and switch to "--dump-sem-ir-ranges=only". // EXTRA-ARGS: --dump-sem-ir-ranges=if-present // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/class/reorder_qualified.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/class/reorder_qualified.carbon class A { class B { class C; fn BF(); var b: i32; } class B.C { class D { fn F(); fn DF(); var d: i32; } fn D.DF() { // A, B, C, and D are complete here. var unused a: A = {.a = 1}; var unused b: B = {.b = 2}; var unused c: C = {.c = 3}; var unused d: D = {.d = 4}; // Unqualified lookup looks in all of them. AF(); BF(); CF(); DF(); } fn CF(); var c: i32; } fn AF(); var a: i32; } // CHECK:STDOUT: --- reorder_qualified.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %A: type = class_type @A [concrete] // CHECK:STDOUT: %B: type = class_type @B [concrete] // CHECK:STDOUT: %C: type = class_type @C [concrete] // CHECK:STDOUT: %B.BF.type: type = fn_type @B.BF [concrete] // CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] // CHECK:STDOUT: %B.BF: %B.BF.type = struct_value () [concrete] // CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [concrete] // CHECK:STDOUT: %Int.type: type = generic_class_type @Int [concrete] // CHECK:STDOUT: %Int.generic: %Int.type = struct_value () [concrete] // CHECK:STDOUT: %i32: type = class_type @Int, @Int(%int_32) [concrete] // CHECK:STDOUT: %B.elem: type = unbound_element_type %B, %i32 [concrete] // CHECK:STDOUT: %struct_type.b.0a3: type = struct_type {.b: %i32} [concrete] // CHECK:STDOUT: %complete_type.ba8: = complete_type_witness %struct_type.b.0a3 [concrete] // CHECK:STDOUT: %D: type = class_type @D [concrete] // CHECK:STDOUT: %D.F.type: type = fn_type @D.F [concrete] // CHECK:STDOUT: %D.F: %D.F.type = struct_value () [concrete] // CHECK:STDOUT: %D.DF.type: type = fn_type @D.DF [concrete] // CHECK:STDOUT: %D.DF: %D.DF.type = struct_value () [concrete] // CHECK:STDOUT: %D.elem: type = unbound_element_type %D, %i32 [concrete] // CHECK:STDOUT: %struct_type.d.b7b: type = struct_type {.d: %i32} [concrete] // CHECK:STDOUT: %complete_type.860: = complete_type_witness %struct_type.d.b7b [concrete] // CHECK:STDOUT: %C.CF.type: type = fn_type @C.CF [concrete] // CHECK:STDOUT: %C.CF: %C.CF.type = struct_value () [concrete] // CHECK:STDOUT: %C.elem: type = unbound_element_type %C, %i32 [concrete] // CHECK:STDOUT: %struct_type.c.b66: type = struct_type {.c: %i32} [concrete] // CHECK:STDOUT: %complete_type.836: = complete_type_witness %struct_type.c.b66 [concrete] // CHECK:STDOUT: %A.AF.type: type = fn_type @A.AF [concrete] // CHECK:STDOUT: %A.AF: %A.AF.type = struct_value () [concrete] // CHECK:STDOUT: %A.elem: type = unbound_element_type %A, %i32 [concrete] // CHECK:STDOUT: %struct_type.a.ba9: type = struct_type {.a: %i32} [concrete] // CHECK:STDOUT: %complete_type.fd7: = complete_type_witness %struct_type.a.ba9 [concrete] // CHECK:STDOUT: %pattern_type.1ab: type = pattern_type %A [concrete] // CHECK:STDOUT: %int_1.5b8: Core.IntLiteral = int_value 1 [concrete] // CHECK:STDOUT: %struct_type.a.a6c: type = struct_type {.a: Core.IntLiteral} [concrete] // CHECK:STDOUT: %struct.48c: %struct_type.a.a6c = struct_value (%int_1.5b8) [concrete] // CHECK:STDOUT: %ImplicitAs.type.cc7: type = generic_interface_type @ImplicitAs [concrete] // CHECK:STDOUT: %ImplicitAs.generic: %ImplicitAs.type.cc7 = struct_value () [concrete] // CHECK:STDOUT: %ImplicitAs.type.e8c: type = facet_type <@ImplicitAs, @ImplicitAs(%i32)> [concrete] // CHECK:STDOUT: %To: Core.IntLiteral = symbolic_binding To, 0 [symbolic] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.4e6: type = fn_type @Core.IntLiteral.as.ImplicitAs.impl.Convert, @Core.IntLiteral.as.ImplicitAs.impl(%To) [symbolic] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.3c2: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.4e6 = struct_value () [symbolic] // CHECK:STDOUT: %ImplicitAs.impl_witness.6bc: = impl_witness imports.%ImplicitAs.impl_witness_table.74f, @Core.IntLiteral.as.ImplicitAs.impl(%int_32) [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.e0d: type = fn_type @Core.IntLiteral.as.ImplicitAs.impl.Convert, @Core.IntLiteral.as.ImplicitAs.impl(%int_32) [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.e0d = struct_value () [concrete] // CHECK:STDOUT: %ImplicitAs.facet: %ImplicitAs.type.e8c = facet_value Core.IntLiteral, (%ImplicitAs.impl_witness.6bc) [concrete] // CHECK:STDOUT: %ImplicitAs.WithSelf.Convert.type.b37: type = fn_type @ImplicitAs.WithSelf.Convert, @ImplicitAs.WithSelf(%i32, %ImplicitAs.facet) [concrete] // CHECK:STDOUT: %.545: type = fn_type_with_self_type %ImplicitAs.WithSelf.Convert.type.b37, %ImplicitAs.facet [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.bound.215: = bound_method %int_1.5b8, %Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5 [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn: = specific_function %Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5, @Core.IntLiteral.as.ImplicitAs.impl.Convert(%int_32) [concrete] // CHECK:STDOUT: %bound_method.38b: = bound_method %int_1.5b8, %Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn [concrete] // CHECK:STDOUT: %int_1.5d2: %i32 = int_value 1 [concrete] // CHECK:STDOUT: %A.val: %A = struct_value (%int_1.5d2) [concrete] // CHECK:STDOUT: %pattern_type.438: type = pattern_type %B [concrete] // CHECK:STDOUT: %int_2.ecc: Core.IntLiteral = int_value 2 [concrete] // CHECK:STDOUT: %struct_type.b.a15: type = struct_type {.b: Core.IntLiteral} [concrete] // CHECK:STDOUT: %struct.26f: %struct_type.b.a15 = struct_value (%int_2.ecc) [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.bound.4e5: = bound_method %int_2.ecc, %Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5 [concrete] // CHECK:STDOUT: %bound_method.646: = bound_method %int_2.ecc, %Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn [concrete] // CHECK:STDOUT: %int_2.ef8: %i32 = int_value 2 [concrete] // CHECK:STDOUT: %B.val: %B = struct_value (%int_2.ef8) [concrete] // CHECK:STDOUT: %pattern_type.d99: type = pattern_type %C [concrete] // CHECK:STDOUT: %int_3.1ba: Core.IntLiteral = int_value 3 [concrete] // CHECK:STDOUT: %struct_type.c.5b8: type = struct_type {.c: Core.IntLiteral} [concrete] // CHECK:STDOUT: %struct.d98: %struct_type.c.5b8 = struct_value (%int_3.1ba) [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.bound.061: = bound_method %int_3.1ba, %Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5 [concrete] // CHECK:STDOUT: %bound_method.fa7: = bound_method %int_3.1ba, %Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn [concrete] // CHECK:STDOUT: %int_3.822: %i32 = int_value 3 [concrete] // CHECK:STDOUT: %C.val: %C = struct_value (%int_3.822) [concrete] // CHECK:STDOUT: %pattern_type.be4: type = pattern_type %D [concrete] // CHECK:STDOUT: %int_4.0c1: Core.IntLiteral = int_value 4 [concrete] // CHECK:STDOUT: %struct_type.d.3ea: type = struct_type {.d: Core.IntLiteral} [concrete] // CHECK:STDOUT: %struct.5a9: %struct_type.d.3ea = struct_value (%int_4.0c1) [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.bound.f0c: = bound_method %int_4.0c1, %Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5 [concrete] // CHECK:STDOUT: %bound_method.6d7: = bound_method %int_4.0c1, %Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn [concrete] // CHECK:STDOUT: %int_4.940: %i32 = int_value 4 [concrete] // CHECK:STDOUT: %D.val: %D = struct_value (%int_4.940) [concrete] // CHECK:STDOUT: %Destroy.type: type = facet_type <@Destroy> [concrete] // CHECK:STDOUT: %Destroy.Op.type.bae255.1: type = fn_type @Destroy.Op.loc36 [concrete] // CHECK:STDOUT: %Destroy.Op.651ba6.1: %Destroy.Op.type.bae255.1 = struct_value () [concrete] // CHECK:STDOUT: %Destroy.Op.type.bae255.2: type = fn_type @Destroy.Op.loc35 [concrete] // CHECK:STDOUT: %Destroy.Op.651ba6.2: %Destroy.Op.type.bae255.2 = struct_value () [concrete] // CHECK:STDOUT: %Destroy.Op.type.bae255.3: type = fn_type @Destroy.Op.loc34 [concrete] // CHECK:STDOUT: %Destroy.Op.651ba6.3: %Destroy.Op.type.bae255.3 = struct_value () [concrete] // CHECK:STDOUT: %Destroy.Op.type.bae255.4: type = fn_type @Destroy.Op.loc33 [concrete] // CHECK:STDOUT: %Destroy.Op.651ba6.4: %Destroy.Op.type.bae255.4 = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: .Int = %Core.Int // CHECK:STDOUT: .ImplicitAs = %Core.ImplicitAs // CHECK:STDOUT: .Destroy = %Core.Destroy // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: %Core.Int: %Int.type = import_ref Core//prelude/parts/int, Int, loaded [concrete = constants.%Int.generic] // CHECK:STDOUT: %Core.ImplicitAs: %ImplicitAs.type.cc7 = import_ref Core//prelude/parts/as, ImplicitAs, loaded [concrete = constants.%ImplicitAs.generic] // CHECK:STDOUT: %Core.import_ref.42d: @Core.IntLiteral.as.ImplicitAs.impl.%Core.IntLiteral.as.ImplicitAs.impl.Convert.type (%Core.IntLiteral.as.ImplicitAs.impl.Convert.type.4e6) = import_ref Core//prelude/parts/int, loc{{\d+_\d+}}, loaded [symbolic = @Core.IntLiteral.as.ImplicitAs.impl.%Core.IntLiteral.as.ImplicitAs.impl.Convert (constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.3c2)] // CHECK:STDOUT: %ImplicitAs.impl_witness_table.74f = impl_witness_table (%Core.import_ref.42d), @Core.IntLiteral.as.ImplicitAs.impl [concrete] // CHECK:STDOUT: %Core.Destroy: type = import_ref Core//prelude/parts/destroy, Destroy, loaded [concrete = constants.%Destroy.type] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .A = %A.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %A.decl: type = class_decl @A [concrete = constants.%A] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @A { // CHECK:STDOUT: %B.decl: type = class_decl @B [concrete = constants.%B] {} {} // CHECK:STDOUT: %C.decl: type = class_decl @C [concrete = constants.%C] {} {} // CHECK:STDOUT: %A.AF.decl: %A.AF.type = fn_decl @A.AF [concrete = constants.%A.AF] {} {} // CHECK:STDOUT: %i32: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %.loc50: %A.elem = field_decl a, element0 [concrete] // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%struct_type.a.ba9 [concrete = constants.%complete_type.fd7] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%A // CHECK:STDOUT: .B = %B.decl // CHECK:STDOUT: .AF = %A.AF.decl // CHECK:STDOUT: .a = %.loc50 // CHECK:STDOUT: .A = // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @B { // CHECK:STDOUT: %C.decl: type = class_decl @C [concrete = constants.%C] {} {} // CHECK:STDOUT: %B.BF.decl: %B.BF.type = fn_decl @B.BF [concrete = constants.%B.BF] {} {} // CHECK:STDOUT: %i32: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %.loc20: %B.elem = field_decl b, element0 [concrete] // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%struct_type.b.0a3 [concrete = constants.%complete_type.ba8] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%B // CHECK:STDOUT: .C = %C.decl // CHECK:STDOUT: .BF = %B.BF.decl // CHECK:STDOUT: .b = %.loc20 // CHECK:STDOUT: .A = // CHECK:STDOUT: .B = // CHECK:STDOUT: .AF = // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @C { // CHECK:STDOUT: %D.decl: type = class_decl @D [concrete = constants.%D] {} {} // CHECK:STDOUT: %D.DF.decl: %D.DF.type = fn_decl @D.DF [concrete = constants.%D.DF] {} {} // CHECK:STDOUT: %C.CF.decl: %C.CF.type = fn_decl @C.CF [concrete = constants.%C.CF] {} {} // CHECK:STDOUT: %i32: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %.loc46: %C.elem = field_decl c, element0 [concrete] // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%struct_type.c.b66 [concrete = constants.%complete_type.836] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%C // CHECK:STDOUT: .D = %D.decl // CHECK:STDOUT: .CF = %C.CF.decl // CHECK:STDOUT: .c = %.loc46 // CHECK:STDOUT: .A = // CHECK:STDOUT: .B = // CHECK:STDOUT: .C = // CHECK:STDOUT: .AF = // CHECK:STDOUT: .BF = // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @D { // CHECK:STDOUT: %D.F.decl: %D.F.type = fn_decl @D.F [concrete = constants.%D.F] {} {} // CHECK:STDOUT: %D.DF.decl: %D.DF.type = fn_decl @D.DF [concrete = constants.%D.DF] {} {} // CHECK:STDOUT: %i32: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %.loc28: %D.elem = field_decl d, element0 [concrete] // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%struct_type.d.b7b [concrete = constants.%complete_type.860] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%D // CHECK:STDOUT: .F = %D.F.decl // CHECK:STDOUT: .DF = %D.DF.decl // CHECK:STDOUT: .d = %.loc28 // CHECK:STDOUT: .A = // CHECK:STDOUT: .B = // CHECK:STDOUT: .C = // CHECK:STDOUT: .D = // CHECK:STDOUT: .AF = // CHECK:STDOUT: .BF = // CHECK:STDOUT: .CF = // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @B.BF(); // CHECK:STDOUT: // CHECK:STDOUT: fn @D.F(); // CHECK:STDOUT: // CHECK:STDOUT: fn @D.DF() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %a.patt: %pattern_type.1ab = ref_binding_pattern a [concrete] // CHECK:STDOUT: %a.var_patt: %pattern_type.1ab = var_pattern %a.patt [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %a.var: ref %A = var %a.var_patt // CHECK:STDOUT: %int_1: Core.IntLiteral = int_value 1 [concrete = constants.%int_1.5b8] // CHECK:STDOUT: %.loc33_32.1: %struct_type.a.a6c = struct_literal (%int_1) [concrete = constants.%struct.48c] // CHECK:STDOUT: %impl.elem0.loc33: %.545 = impl_witness_access constants.%ImplicitAs.impl_witness.6bc, element0 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5] // CHECK:STDOUT: %bound_method.loc33_32.1: = bound_method %int_1, %impl.elem0.loc33 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.bound.215] // CHECK:STDOUT: %specific_fn.loc33: = specific_function %impl.elem0.loc33, @Core.IntLiteral.as.ImplicitAs.impl.Convert(constants.%int_32) [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn] // CHECK:STDOUT: %bound_method.loc33_32.2: = bound_method %int_1, %specific_fn.loc33 [concrete = constants.%bound_method.38b] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc33: init %i32 = call %bound_method.loc33_32.2(%int_1) [concrete = constants.%int_1.5d2] // CHECK:STDOUT: %.loc33_32.2: init %i32 = converted %int_1, %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc33 [concrete = constants.%int_1.5d2] // CHECK:STDOUT: %.loc33_32.3: ref %i32 = class_element_access %a.var, element0 // CHECK:STDOUT: %.loc33_32.4: init %i32 to %.loc33_32.3 = in_place_init %.loc33_32.2 [concrete = constants.%int_1.5d2] // CHECK:STDOUT: %.loc33_32.5: init %A to %a.var = class_init (%.loc33_32.4) [concrete = constants.%A.val] // CHECK:STDOUT: %.loc33_7: init %A = converted %.loc33_32.1, %.loc33_32.5 [concrete = constants.%A.val] // CHECK:STDOUT: assign %a.var, %.loc33_7 // CHECK:STDOUT: %A.ref: type = name_ref A, file.%A.decl [concrete = constants.%A] // CHECK:STDOUT: %a: ref %A = ref_binding a, %a.var // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %b.patt: %pattern_type.438 = ref_binding_pattern b [concrete] // CHECK:STDOUT: %b.var_patt: %pattern_type.438 = var_pattern %b.patt [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %b.var: ref %B = var %b.var_patt // CHECK:STDOUT: %int_2: Core.IntLiteral = int_value 2 [concrete = constants.%int_2.ecc] // CHECK:STDOUT: %.loc34_32.1: %struct_type.b.a15 = struct_literal (%int_2) [concrete = constants.%struct.26f] // CHECK:STDOUT: %impl.elem0.loc34: %.545 = impl_witness_access constants.%ImplicitAs.impl_witness.6bc, element0 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5] // CHECK:STDOUT: %bound_method.loc34_32.1: = bound_method %int_2, %impl.elem0.loc34 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.bound.4e5] // CHECK:STDOUT: %specific_fn.loc34: = specific_function %impl.elem0.loc34, @Core.IntLiteral.as.ImplicitAs.impl.Convert(constants.%int_32) [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn] // CHECK:STDOUT: %bound_method.loc34_32.2: = bound_method %int_2, %specific_fn.loc34 [concrete = constants.%bound_method.646] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc34: init %i32 = call %bound_method.loc34_32.2(%int_2) [concrete = constants.%int_2.ef8] // CHECK:STDOUT: %.loc34_32.2: init %i32 = converted %int_2, %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc34 [concrete = constants.%int_2.ef8] // CHECK:STDOUT: %.loc34_32.3: ref %i32 = class_element_access %b.var, element0 // CHECK:STDOUT: %.loc34_32.4: init %i32 to %.loc34_32.3 = in_place_init %.loc34_32.2 [concrete = constants.%int_2.ef8] // CHECK:STDOUT: %.loc34_32.5: init %B to %b.var = class_init (%.loc34_32.4) [concrete = constants.%B.val] // CHECK:STDOUT: %.loc34_7: init %B = converted %.loc34_32.1, %.loc34_32.5 [concrete = constants.%B.val] // CHECK:STDOUT: assign %b.var, %.loc34_7 // CHECK:STDOUT: %B.ref: type = name_ref B, @A.%B.decl [concrete = constants.%B] // CHECK:STDOUT: %b: ref %B = ref_binding b, %b.var // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %c.patt: %pattern_type.d99 = ref_binding_pattern c [concrete] // CHECK:STDOUT: %c.var_patt: %pattern_type.d99 = var_pattern %c.patt [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %c.var: ref %C = var %c.var_patt // CHECK:STDOUT: %int_3: Core.IntLiteral = int_value 3 [concrete = constants.%int_3.1ba] // CHECK:STDOUT: %.loc35_32.1: %struct_type.c.5b8 = struct_literal (%int_3) [concrete = constants.%struct.d98] // CHECK:STDOUT: %impl.elem0.loc35: %.545 = impl_witness_access constants.%ImplicitAs.impl_witness.6bc, element0 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5] // CHECK:STDOUT: %bound_method.loc35_32.1: = bound_method %int_3, %impl.elem0.loc35 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.bound.061] // CHECK:STDOUT: %specific_fn.loc35: = specific_function %impl.elem0.loc35, @Core.IntLiteral.as.ImplicitAs.impl.Convert(constants.%int_32) [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn] // CHECK:STDOUT: %bound_method.loc35_32.2: = bound_method %int_3, %specific_fn.loc35 [concrete = constants.%bound_method.fa7] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc35: init %i32 = call %bound_method.loc35_32.2(%int_3) [concrete = constants.%int_3.822] // CHECK:STDOUT: %.loc35_32.2: init %i32 = converted %int_3, %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc35 [concrete = constants.%int_3.822] // CHECK:STDOUT: %.loc35_32.3: ref %i32 = class_element_access %c.var, element0 // CHECK:STDOUT: %.loc35_32.4: init %i32 to %.loc35_32.3 = in_place_init %.loc35_32.2 [concrete = constants.%int_3.822] // CHECK:STDOUT: %.loc35_32.5: init %C to %c.var = class_init (%.loc35_32.4) [concrete = constants.%C.val] // CHECK:STDOUT: %.loc35_7: init %C = converted %.loc35_32.1, %.loc35_32.5 [concrete = constants.%C.val] // CHECK:STDOUT: assign %c.var, %.loc35_7 // CHECK:STDOUT: %C.ref: type = name_ref C, @B.%C.decl [concrete = constants.%C] // CHECK:STDOUT: %c: ref %C = ref_binding c, %c.var // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %d.patt: %pattern_type.be4 = ref_binding_pattern d [concrete] // CHECK:STDOUT: %d.var_patt: %pattern_type.be4 = var_pattern %d.patt [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %d.var: ref %D = var %d.var_patt // CHECK:STDOUT: %int_4: Core.IntLiteral = int_value 4 [concrete = constants.%int_4.0c1] // CHECK:STDOUT: %.loc36_32.1: %struct_type.d.3ea = struct_literal (%int_4) [concrete = constants.%struct.5a9] // CHECK:STDOUT: %impl.elem0.loc36: %.545 = impl_witness_access constants.%ImplicitAs.impl_witness.6bc, element0 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5] // CHECK:STDOUT: %bound_method.loc36_32.1: = bound_method %int_4, %impl.elem0.loc36 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.bound.f0c] // CHECK:STDOUT: %specific_fn.loc36: = specific_function %impl.elem0.loc36, @Core.IntLiteral.as.ImplicitAs.impl.Convert(constants.%int_32) [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn] // CHECK:STDOUT: %bound_method.loc36_32.2: = bound_method %int_4, %specific_fn.loc36 [concrete = constants.%bound_method.6d7] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc36: init %i32 = call %bound_method.loc36_32.2(%int_4) [concrete = constants.%int_4.940] // CHECK:STDOUT: %.loc36_32.2: init %i32 = converted %int_4, %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc36 [concrete = constants.%int_4.940] // CHECK:STDOUT: %.loc36_32.3: ref %i32 = class_element_access %d.var, element0 // CHECK:STDOUT: %.loc36_32.4: init %i32 to %.loc36_32.3 = in_place_init %.loc36_32.2 [concrete = constants.%int_4.940] // CHECK:STDOUT: %.loc36_32.5: init %D to %d.var = class_init (%.loc36_32.4) [concrete = constants.%D.val] // CHECK:STDOUT: %.loc36_7: init %D = converted %.loc36_32.1, %.loc36_32.5 [concrete = constants.%D.val] // CHECK:STDOUT: assign %d.var, %.loc36_7 // CHECK:STDOUT: %D.ref: type = name_ref D, @C.%D.decl [concrete = constants.%D] // CHECK:STDOUT: %d: ref %D = ref_binding d, %d.var // CHECK:STDOUT: %AF.ref: %A.AF.type = name_ref AF, @A.%A.AF.decl [concrete = constants.%A.AF] // CHECK:STDOUT: %A.AF.call: init %empty_tuple.type = call %AF.ref() // CHECK:STDOUT: %BF.ref: %B.BF.type = name_ref BF, @B.%B.BF.decl [concrete = constants.%B.BF] // CHECK:STDOUT: %B.BF.call: init %empty_tuple.type = call %BF.ref() // CHECK:STDOUT: %CF.ref: %C.CF.type = name_ref CF, @C.%C.CF.decl [concrete = constants.%C.CF] // CHECK:STDOUT: %C.CF.call: init %empty_tuple.type = call %CF.ref() // CHECK:STDOUT: %DF.ref: %D.DF.type = name_ref DF, @D.%D.DF.decl [concrete = constants.%D.DF] // CHECK:STDOUT: %D.DF.call: init %empty_tuple.type = call %DF.ref() // CHECK:STDOUT: %Destroy.Op.bound.loc36: = bound_method %d.var, constants.%Destroy.Op.651ba6.1 // CHECK:STDOUT: %Destroy.Op.call.loc36: init %empty_tuple.type = call %Destroy.Op.bound.loc36(%d.var) // CHECK:STDOUT: %Destroy.Op.bound.loc35: = bound_method %c.var, constants.%Destroy.Op.651ba6.2 // CHECK:STDOUT: %Destroy.Op.call.loc35: init %empty_tuple.type = call %Destroy.Op.bound.loc35(%c.var) // CHECK:STDOUT: %Destroy.Op.bound.loc34: = bound_method %b.var, constants.%Destroy.Op.651ba6.3 // CHECK:STDOUT: %Destroy.Op.call.loc34: init %empty_tuple.type = call %Destroy.Op.bound.loc34(%b.var) // CHECK:STDOUT: %Destroy.Op.bound.loc33: = bound_method %a.var, constants.%Destroy.Op.651ba6.4 // CHECK:STDOUT: %Destroy.Op.call.loc33: init %empty_tuple.type = call %Destroy.Op.bound.loc33(%a.var) // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @C.CF(); // CHECK:STDOUT: // CHECK:STDOUT: fn @A.AF(); // CHECK:STDOUT: // CHECK:STDOUT: fn @Destroy.Op.loc36(%self.param: ref %D) = "no_op"; // CHECK:STDOUT: // CHECK:STDOUT: fn @Destroy.Op.loc35(%self.param: ref %C) = "no_op"; // CHECK:STDOUT: // CHECK:STDOUT: fn @Destroy.Op.loc34(%self.param: ref %B) = "no_op"; // CHECK:STDOUT: // CHECK:STDOUT: fn @Destroy.Op.loc33(%self.param: ref %A) = "no_op"; // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/class/scope.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/int.carbon // TODO: Add ranges and switch to "--dump-sem-ir-ranges=only". // EXTRA-ARGS: --dump-sem-ir-ranges=if-present // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/class/scope.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/class/scope.carbon class Class { fn F() -> i32 { return 1; } fn G() -> i32 { return F(); } } fn F() -> i32 { return 2; } fn Run() { var unused a: i32 = F(); var unused b: i32 = Class.F(); } // CHECK:STDOUT: --- scope.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %Class: type = class_type @Class [concrete] // CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [concrete] // CHECK:STDOUT: %Int.type: type = generic_class_type @Int [concrete] // CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] // CHECK:STDOUT: %Int.generic: %Int.type = struct_value () [concrete] // CHECK:STDOUT: %i32: type = class_type @Int, @Int(%int_32) [concrete] // CHECK:STDOUT: %.ff5: Core.Form = init_form %i32 [concrete] // CHECK:STDOUT: %pattern_type.7ce: type = pattern_type %i32 [concrete] // CHECK:STDOUT: %Class.F.type: type = fn_type @Class.F [concrete] // CHECK:STDOUT: %Class.F: %Class.F.type = struct_value () [concrete] // CHECK:STDOUT: %Class.G.type: type = fn_type @Class.G [concrete] // CHECK:STDOUT: %Class.G: %Class.G.type = struct_value () [concrete] // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %complete_type.357: = complete_type_witness %empty_struct_type [concrete] // CHECK:STDOUT: %int_1.5b8: Core.IntLiteral = int_value 1 [concrete] // CHECK:STDOUT: %ImplicitAs.type.cc7: type = generic_interface_type @ImplicitAs [concrete] // CHECK:STDOUT: %ImplicitAs.generic: %ImplicitAs.type.cc7 = struct_value () [concrete] // CHECK:STDOUT: %ImplicitAs.type.e8c: type = facet_type <@ImplicitAs, @ImplicitAs(%i32)> [concrete] // CHECK:STDOUT: %To: Core.IntLiteral = symbolic_binding To, 0 [symbolic] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.4e6: type = fn_type @Core.IntLiteral.as.ImplicitAs.impl.Convert, @Core.IntLiteral.as.ImplicitAs.impl(%To) [symbolic] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.3c2: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.4e6 = struct_value () [symbolic] // CHECK:STDOUT: %ImplicitAs.impl_witness.6bc: = impl_witness imports.%ImplicitAs.impl_witness_table.74f, @Core.IntLiteral.as.ImplicitAs.impl(%int_32) [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.e0d: type = fn_type @Core.IntLiteral.as.ImplicitAs.impl.Convert, @Core.IntLiteral.as.ImplicitAs.impl(%int_32) [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.e0d = struct_value () [concrete] // CHECK:STDOUT: %ImplicitAs.facet: %ImplicitAs.type.e8c = facet_value Core.IntLiteral, (%ImplicitAs.impl_witness.6bc) [concrete] // CHECK:STDOUT: %ImplicitAs.WithSelf.Convert.type.b37: type = fn_type @ImplicitAs.WithSelf.Convert, @ImplicitAs.WithSelf(%i32, %ImplicitAs.facet) [concrete] // CHECK:STDOUT: %.545: type = fn_type_with_self_type %ImplicitAs.WithSelf.Convert.type.b37, %ImplicitAs.facet [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.bound.215: = bound_method %int_1.5b8, %Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5 [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn: = specific_function %Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5, @Core.IntLiteral.as.ImplicitAs.impl.Convert(%int_32) [concrete] // CHECK:STDOUT: %bound_method.38b: = bound_method %int_1.5b8, %Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn [concrete] // CHECK:STDOUT: %int_1.5d2: %i32 = int_value 1 [concrete] // CHECK:STDOUT: %F.type: type = fn_type @F [concrete] // CHECK:STDOUT: %F: %F.type = struct_value () [concrete] // CHECK:STDOUT: %int_2.ecc: Core.IntLiteral = int_value 2 [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.bound.4e5: = bound_method %int_2.ecc, %Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5 [concrete] // CHECK:STDOUT: %bound_method.646: = bound_method %int_2.ecc, %Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn [concrete] // CHECK:STDOUT: %int_2.ef8: %i32 = int_value 2 [concrete] // CHECK:STDOUT: %Run.type: type = fn_type @Run [concrete] // CHECK:STDOUT: %Run: %Run.type = struct_value () [concrete] // CHECK:STDOUT: %Destroy.type: type = facet_type <@Destroy> [concrete] // CHECK:STDOUT: %Destroy.Op.type: type = fn_type @Destroy.Op [concrete] // CHECK:STDOUT: %Destroy.Op: %Destroy.Op.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: .Int = %Core.Int // CHECK:STDOUT: .ImplicitAs = %Core.ImplicitAs // CHECK:STDOUT: .Destroy = %Core.Destroy // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: %Core.Int: %Int.type = import_ref Core//prelude/parts/int, Int, loaded [concrete = constants.%Int.generic] // CHECK:STDOUT: %Core.ImplicitAs: %ImplicitAs.type.cc7 = import_ref Core//prelude/parts/as, ImplicitAs, loaded [concrete = constants.%ImplicitAs.generic] // CHECK:STDOUT: %Core.import_ref.42d: @Core.IntLiteral.as.ImplicitAs.impl.%Core.IntLiteral.as.ImplicitAs.impl.Convert.type (%Core.IntLiteral.as.ImplicitAs.impl.Convert.type.4e6) = import_ref Core//prelude/parts/int, loc{{\d+_\d+}}, loaded [symbolic = @Core.IntLiteral.as.ImplicitAs.impl.%Core.IntLiteral.as.ImplicitAs.impl.Convert (constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.3c2)] // CHECK:STDOUT: %ImplicitAs.impl_witness_table.74f = impl_witness_table (%Core.import_ref.42d), @Core.IntLiteral.as.ImplicitAs.impl [concrete] // CHECK:STDOUT: %Core.Destroy: type = import_ref Core//prelude/parts/destroy, Destroy, loaded [concrete = constants.%Destroy.type] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .Class = %Class.decl // CHECK:STDOUT: .F = %F.decl // CHECK:STDOUT: .Run = %Run.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %Class.decl: type = class_decl @Class [concrete = constants.%Class] {} {} // CHECK:STDOUT: %F.decl: %F.type = fn_decl @F [concrete = constants.%F] { // CHECK:STDOUT: %return.patt: %pattern_type.7ce = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: %pattern_type.7ce = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %i32: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %.loc25: Core.Form = init_form %i32 [concrete = constants.%.ff5] // CHECK:STDOUT: %return.param: ref %i32 = out_param call_param0 // CHECK:STDOUT: %return: ref %i32 = return_slot %return.param // CHECK:STDOUT: } // CHECK:STDOUT: %Run.decl: %Run.type = fn_decl @Run [concrete = constants.%Run] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @Class { // CHECK:STDOUT: %Class.F.decl: %Class.F.type = fn_decl @Class.F [concrete = constants.%Class.F] { // CHECK:STDOUT: %return.patt: %pattern_type.7ce = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: %pattern_type.7ce = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %i32: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %.loc16: Core.Form = init_form %i32 [concrete = constants.%.ff5] // CHECK:STDOUT: %return.param: ref %i32 = out_param call_param0 // CHECK:STDOUT: %return: ref %i32 = return_slot %return.param // CHECK:STDOUT: } // CHECK:STDOUT: %Class.G.decl: %Class.G.type = fn_decl @Class.G [concrete = constants.%Class.G] { // CHECK:STDOUT: %return.patt: %pattern_type.7ce = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: %pattern_type.7ce = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %i32: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %.loc20: Core.Form = init_form %i32 [concrete = constants.%.ff5] // CHECK:STDOUT: %return.param: ref %i32 = out_param call_param0 // CHECK:STDOUT: %return: ref %i32 = return_slot %return.param // CHECK:STDOUT: } // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type.357] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%Class // CHECK:STDOUT: .F = %Class.F.decl // CHECK:STDOUT: .G = %Class.G.decl // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @Class.F() -> out %return.param: %i32 { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %int_1: Core.IntLiteral = int_value 1 [concrete = constants.%int_1.5b8] // CHECK:STDOUT: %impl.elem0: %.545 = impl_witness_access constants.%ImplicitAs.impl_witness.6bc, element0 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5] // CHECK:STDOUT: %bound_method.loc17_13.1: = bound_method %int_1, %impl.elem0 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.bound.215] // CHECK:STDOUT: %specific_fn: = specific_function %impl.elem0, @Core.IntLiteral.as.ImplicitAs.impl.Convert(constants.%int_32) [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn] // CHECK:STDOUT: %bound_method.loc17_13.2: = bound_method %int_1, %specific_fn [concrete = constants.%bound_method.38b] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.call: init %i32 = call %bound_method.loc17_13.2(%int_1) [concrete = constants.%int_1.5d2] // CHECK:STDOUT: %.loc17: init %i32 = converted %int_1, %Core.IntLiteral.as.ImplicitAs.impl.Convert.call [concrete = constants.%int_1.5d2] // CHECK:STDOUT: return %.loc17 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @Class.G() -> out %return.param: %i32 { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %F.ref: %Class.F.type = name_ref F, @Class.%Class.F.decl [concrete = constants.%Class.F] // CHECK:STDOUT: %Class.F.call: init %i32 = call %F.ref() // CHECK:STDOUT: return %Class.F.call // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @F() -> out %return.param: %i32 { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %int_2: Core.IntLiteral = int_value 2 [concrete = constants.%int_2.ecc] // CHECK:STDOUT: %impl.elem0: %.545 = impl_witness_access constants.%ImplicitAs.impl_witness.6bc, element0 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5] // CHECK:STDOUT: %bound_method.loc26_11.1: = bound_method %int_2, %impl.elem0 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.bound.4e5] // CHECK:STDOUT: %specific_fn: = specific_function %impl.elem0, @Core.IntLiteral.as.ImplicitAs.impl.Convert(constants.%int_32) [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn] // CHECK:STDOUT: %bound_method.loc26_11.2: = bound_method %int_2, %specific_fn [concrete = constants.%bound_method.646] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.call: init %i32 = call %bound_method.loc26_11.2(%int_2) [concrete = constants.%int_2.ef8] // CHECK:STDOUT: %.loc26: init %i32 = converted %int_2, %Core.IntLiteral.as.ImplicitAs.impl.Convert.call [concrete = constants.%int_2.ef8] // CHECK:STDOUT: return %.loc26 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @Run() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %a.patt: %pattern_type.7ce = ref_binding_pattern a [concrete] // CHECK:STDOUT: %a.var_patt: %pattern_type.7ce = var_pattern %a.patt [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %a.var: ref %i32 = var %a.var_patt // CHECK:STDOUT: %F.ref.loc30: %F.type = name_ref F, file.%F.decl [concrete = constants.%F] // CHECK:STDOUT: %F.call: init %i32 = call %F.ref.loc30() // CHECK:STDOUT: assign %a.var, %F.call // CHECK:STDOUT: %i32.loc30: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %a: ref %i32 = ref_binding a, %a.var // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %b.patt: %pattern_type.7ce = ref_binding_pattern b [concrete] // CHECK:STDOUT: %b.var_patt: %pattern_type.7ce = var_pattern %b.patt [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %b.var: ref %i32 = var %b.var_patt // CHECK:STDOUT: %Class.ref: type = name_ref Class, file.%Class.decl [concrete = constants.%Class] // CHECK:STDOUT: %F.ref.loc31: %Class.F.type = name_ref F, @Class.%Class.F.decl [concrete = constants.%Class.F] // CHECK:STDOUT: %Class.F.call: init %i32 = call %F.ref.loc31() // CHECK:STDOUT: assign %b.var, %Class.F.call // CHECK:STDOUT: %i32.loc31: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %b: ref %i32 = ref_binding b, %b.var // CHECK:STDOUT: %Destroy.Op.bound.loc31: = bound_method %b.var, constants.%Destroy.Op // CHECK:STDOUT: %Destroy.Op.call.loc31: init %empty_tuple.type = call %Destroy.Op.bound.loc31(%b.var) // CHECK:STDOUT: %Destroy.Op.bound.loc30: = bound_method %a.var, constants.%Destroy.Op // CHECK:STDOUT: %Destroy.Op.call.loc30: init %empty_tuple.type = call %Destroy.Op.bound.loc30(%a.var) // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @Destroy.Op(%self.param: ref %i32) = "no_op"; // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/class/self/fail_ref_self.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/convert.carbon // TODO: Add ranges and switch to "--dump-sem-ir-ranges=only". // EXTRA-ARGS: --dump-sem-ir-ranges=if-present // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/class/self/fail_ref_self.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/class/self/fail_ref_self.carbon class Class { fn F[ref self: Self](); } fn Make() -> Class; fn F(c: Class, p: Class*) { // CHECK:STDERR: fail_ref_self.carbon:[[@LINE+7]]:3: error: value expression passed to reference parameter [ValueForRefParam] // CHECK:STDERR: c.F(); // CHECK:STDERR: ^ // CHECK:STDERR: fail_ref_self.carbon:[[@LINE-9]]:8: note: initializing function parameter [InCallToFunctionParam] // CHECK:STDERR: fn F[ref self: Self](); // CHECK:STDERR: ^~~~~~~~~~~~~~ // CHECK:STDERR: c.F(); // This call is OK. (*p).F(); // So is this one. Make().F(); } // CHECK:STDOUT: --- fail_ref_self.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %Class: type = class_type @Class [concrete] // CHECK:STDOUT: %pattern_type.904: type = pattern_type %Class [concrete] // CHECK:STDOUT: %Class.F.type: type = fn_type @Class.F [concrete] // CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] // CHECK:STDOUT: %Class.F: %Class.F.type = struct_value () [concrete] // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %complete_type: = complete_type_witness %empty_struct_type [concrete] // CHECK:STDOUT: %.cff: Core.Form = init_form %Class [concrete] // CHECK:STDOUT: %Make.type: type = fn_type @Make [concrete] // CHECK:STDOUT: %Make: %Make.type = struct_value () [concrete] // CHECK:STDOUT: %ptr.8e5: type = ptr_type %Class [concrete] // CHECK:STDOUT: %pattern_type.018: type = pattern_type %ptr.8e5 [concrete] // CHECK:STDOUT: %F.type: type = fn_type @F [concrete] // CHECK:STDOUT: %F: %F.type = struct_value () [concrete] // CHECK:STDOUT: %Destroy.type: type = facet_type <@Destroy> [concrete] // CHECK:STDOUT: %Destroy.Op.type: type = fn_type @Destroy.Op [concrete] // CHECK:STDOUT: %Destroy.Op: %Destroy.Op.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: .Destroy = %Core.Destroy // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: %Core.Destroy: type = import_ref Core//prelude/parts/destroy, Destroy, loaded [concrete = constants.%Destroy.type] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .Class = %Class.decl // CHECK:STDOUT: .Make = %Make.decl // CHECK:STDOUT: .F = %F.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %Class.decl: type = class_decl @Class [concrete = constants.%Class] {} {} // CHECK:STDOUT: %Make.decl: %Make.type = fn_decl @Make [concrete = constants.%Make] { // CHECK:STDOUT: %return.patt: %pattern_type.904 = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: %pattern_type.904 = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %Class.ref: type = name_ref Class, file.%Class.decl [concrete = constants.%Class] // CHECK:STDOUT: %.loc19: Core.Form = init_form %Class.ref [concrete = constants.%.cff] // CHECK:STDOUT: %return.param: ref %Class = out_param call_param0 // CHECK:STDOUT: %return: ref %Class = return_slot %return.param // CHECK:STDOUT: } // CHECK:STDOUT: %F.decl: %F.type = fn_decl @F [concrete = constants.%F] { // CHECK:STDOUT: %c.patt: %pattern_type.904 = value_binding_pattern c [concrete] // CHECK:STDOUT: %c.param_patt: %pattern_type.904 = value_param_pattern %c.patt [concrete] // CHECK:STDOUT: %p.patt: %pattern_type.018 = value_binding_pattern p [concrete] // CHECK:STDOUT: %p.param_patt: %pattern_type.018 = value_param_pattern %p.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %c.param: %Class = value_param call_param0 // CHECK:STDOUT: %Class.ref.loc21_9: type = name_ref Class, file.%Class.decl [concrete = constants.%Class] // CHECK:STDOUT: %c: %Class = value_binding c, %c.param // CHECK:STDOUT: %p.param: %ptr.8e5 = value_param call_param1 // CHECK:STDOUT: %.loc21: type = splice_block %ptr [concrete = constants.%ptr.8e5] { // CHECK:STDOUT: %Class.ref.loc21_19: type = name_ref Class, file.%Class.decl [concrete = constants.%Class] // CHECK:STDOUT: %ptr: type = ptr_type %Class.ref.loc21_19 [concrete = constants.%ptr.8e5] // CHECK:STDOUT: } // CHECK:STDOUT: %p: %ptr.8e5 = value_binding p, %p.param // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @Class { // CHECK:STDOUT: %Class.F.decl: %Class.F.type = fn_decl @Class.F [concrete = constants.%Class.F] { // CHECK:STDOUT: %self.patt: %pattern_type.904 = ref_binding_pattern self [concrete] // CHECK:STDOUT: %self.param_patt: %pattern_type.904 = ref_param_pattern %self.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %self.param: ref %Class = ref_param call_param0 // CHECK:STDOUT: %Self.ref: type = name_ref Self, constants.%Class [concrete = constants.%Class] // CHECK:STDOUT: %self: ref %Class = ref_binding self, %self.param // CHECK:STDOUT: } // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%Class // CHECK:STDOUT: .F = %Class.F.decl // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @Class.F(%self.param: ref %Class); // CHECK:STDOUT: // CHECK:STDOUT: fn @Make() -> out %return.param: %Class; // CHECK:STDOUT: // CHECK:STDOUT: fn @F(%c.param: %Class, %p.param: %ptr.8e5) { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %c.ref: %Class = name_ref c, %c // CHECK:STDOUT: %F.ref.loc29: %Class.F.type = name_ref F, @Class.%Class.F.decl [concrete = constants.%Class.F] // CHECK:STDOUT: %Class.F.bound.loc29: = bound_method %c.ref, %F.ref.loc29 // CHECK:STDOUT: %Class.F.call.loc29: init %empty_tuple.type = call %Class.F.bound.loc29() // CHECK:STDOUT: %p.ref: %ptr.8e5 = name_ref p, %p // CHECK:STDOUT: %.loc32: ref %Class = deref %p.ref // CHECK:STDOUT: %F.ref.loc32: %Class.F.type = name_ref F, @Class.%Class.F.decl [concrete = constants.%Class.F] // CHECK:STDOUT: %Class.F.bound.loc32: = bound_method %.loc32, %F.ref.loc32 // CHECK:STDOUT: %Class.F.call.loc32: init %empty_tuple.type = call %Class.F.bound.loc32(%.loc32) // CHECK:STDOUT: %Make.ref: %Make.type = name_ref Make, file.%Make.decl [concrete = constants.%Make] // CHECK:STDOUT: %.loc35_8.1: ref %Class = temporary_storage // CHECK:STDOUT: %Make.call: init %Class to %.loc35_8.1 = call %Make.ref() // CHECK:STDOUT: %.loc35_8.2: ref %Class = temporary %.loc35_8.1, %Make.call // CHECK:STDOUT: %F.ref.loc35: %Class.F.type = name_ref F, @Class.%Class.F.decl [concrete = constants.%Class.F] // CHECK:STDOUT: %Class.F.bound.loc35: = bound_method %.loc35_8.2, %F.ref.loc35 // CHECK:STDOUT: %Class.F.call.loc35: init %empty_tuple.type = call %Class.F.bound.loc35(%.loc35_8.2) // CHECK:STDOUT: %Destroy.Op.bound: = bound_method %.loc35_8.2, constants.%Destroy.Op // CHECK:STDOUT: %Destroy.Op.call: init %empty_tuple.type = call %Destroy.Op.bound(%.loc35_8.2) // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @Destroy.Op(%self.param: ref %Class) = "no_op"; // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/class/self/fail_self.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/convert.carbon // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/class/self/fail_self.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/class/self/fail_self.carbon class Class { // CHECK:STDERR: fail_self.carbon:[[@LINE+4]]:8: error: `self` can only be declared in an implicit parameter list [SelfOutsideImplicitParamList] // CHECK:STDERR: fn F(self: Self); // CHECK:STDERR: ^~~~~~~~~~ // CHECK:STDERR: fn F(self: Self); fn G() -> Self; } // CHECK:STDERR: fail_self.carbon:[[@LINE+4]]:19: error: `self` can only be declared in an implicit parameter list [SelfOutsideImplicitParamList] // CHECK:STDERR: fn Class.F(unused self: Self) { // CHECK:STDERR: ^~~~~~~~~~ // CHECK:STDERR: fn Class.F(unused self: Self) { } fn Class.G() -> Self { // CHECK:STDERR: fail_self.carbon:[[@LINE+4]]:7: error: `self` can only be declared in an implicit parameter list [SelfOutsideImplicitParamList] // CHECK:STDERR: var self: Self; // CHECK:STDERR: ^~~~~~~~~~ // CHECK:STDERR: var self: Self; // CHECK:STDERR: fail_self.carbon:[[@LINE+7]]:10: error: cannot copy value of type `Class` [CopyOfUncopyableType] // CHECK:STDERR: return self; // CHECK:STDERR: ^~~~ // CHECK:STDERR: fail_self.carbon:[[@LINE+4]]:10: note: type `Class` does not implement interface `Core.Copy` [MissingImplInMemberAccessInContext] // CHECK:STDERR: return self; // CHECK:STDERR: ^~~~ // CHECK:STDERR: return self; } class WrongSelf { fn F[self: Class](); } fn CallWrongSelf(ws: WrongSelf) { // CHECK:STDERR: fail_self.carbon:[[@LINE+10]]:3: error: cannot implicitly convert expression of type `WrongSelf` to `Class` [ConversionFailure] // CHECK:STDERR: ws.F(); // CHECK:STDERR: ^~ // CHECK:STDERR: fail_self.carbon:[[@LINE+7]]:3: note: type `WrongSelf` does not implement interface `Core.ImplicitAs(Class)` [MissingImplInMemberAccessInContext] // CHECK:STDERR: ws.F(); // CHECK:STDERR: ^~ // CHECK:STDERR: fail_self.carbon:[[@LINE-10]]:8: note: initializing function parameter [InCallToFunctionParam] // CHECK:STDERR: fn F[self: Class](); // CHECK:STDERR: ^~~~~~~~~~~ // CHECK:STDERR: ws.F(); } ================================================ FILE: toolchain/check/testdata/class/self/fail_self_param.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/convert.carbon // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/class/self/fail_self_param.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/class/self/fail_self_param.carbon // CHECK:STDERR: fail_self_param.carbon:[[@LINE+4]]:9: error: `self` parameter only allowed on functions [SelfParameterNotAllowed] // CHECK:STDERR: class C[self:! type](x:! self) {} // CHECK:STDERR: ^~~~~~~~~~~ // CHECK:STDERR: class C[self:! type](x:! self) {} var v: C(()); ================================================ FILE: toolchain/check/testdata/class/self/fail_self_type_member.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/bool.carbon // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/class/self/fail_self_type_member.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/class/self/fail_self_type_member.carbon class Class { var b: bool; } fn F() -> bool { var c1: Class = {.b = true}; // CHECK:STDERR: fail_self_type_member.carbon:[[@LINE+8]]:17: error: expected identifier after `.` [ExpectedIdentifierAfterPeriodOrArrow] // CHECK:STDERR: var c2: Class.Self = c1; // CHECK:STDERR: ^~~~ // CHECK:STDERR: // CHECK:STDERR: fail_self_type_member.carbon:[[@LINE+4]]:17: error: semantics TODO: `handle invalid parse trees in `check`` [SemanticsTodo] // CHECK:STDERR: var c2: Class.Self = c1; // CHECK:STDERR: ^~~~ // CHECK:STDERR: var c2: Class.Self = c1; return c2.b; } ================================================ FILE: toolchain/check/testdata/class/self/raw_self.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/int.carbon // TODO: Add ranges and switch to "--dump-sem-ir-ranges=only". // EXTRA-ARGS: --dump-sem-ir-ranges=if-present // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/class/self/raw_self.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/class/self/raw_self.carbon class Class { fn F[ref self: Self](r#self: i32); fn G[self: Self](r#self: i32) -> (i32, i32); var n: i32; } fn Class.F[ref self: Self](r#self: i32) { self.n = r#self; } fn Class.G[self: Self](r#self: i32) -> (i32, i32) { return (self.n, r#self); } // CHECK:STDOUT: --- raw_self.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %Class: type = class_type @Class [concrete] // CHECK:STDOUT: %pattern_type.904: type = pattern_type %Class [concrete] // CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [concrete] // CHECK:STDOUT: %Int.type: type = generic_class_type @Int [concrete] // CHECK:STDOUT: %Int.generic: %Int.type = struct_value () [concrete] // CHECK:STDOUT: %N: Core.IntLiteral = symbolic_binding N, 0 [symbolic] // CHECK:STDOUT: %i32: type = class_type @Int, @Int(%int_32) [concrete] // CHECK:STDOUT: %pattern_type.7ce: type = pattern_type %i32 [concrete] // CHECK:STDOUT: %Class.F.type: type = fn_type @Class.F [concrete] // CHECK:STDOUT: %Class.F: %Class.F.type = struct_value () [concrete] // CHECK:STDOUT: %tuple.type.24b: type = tuple_type (type, type) [concrete] // CHECK:STDOUT: %tuple.95a: %tuple.type.24b = tuple_value (%i32, %i32) [concrete] // CHECK:STDOUT: %tuple.type.d07: type = tuple_type (%i32, %i32) [concrete] // CHECK:STDOUT: %.f32: Core.Form = init_form %tuple.type.d07 [concrete] // CHECK:STDOUT: %pattern_type.511: type = pattern_type %tuple.type.d07 [concrete] // CHECK:STDOUT: %Class.G.type: type = fn_type @Class.G [concrete] // CHECK:STDOUT: %Class.G: %Class.G.type = struct_value () [concrete] // CHECK:STDOUT: %Class.elem: type = unbound_element_type %Class, %i32 [concrete] // CHECK:STDOUT: %struct_type.n: type = struct_type {.n: %i32} [concrete] // CHECK:STDOUT: %complete_type.54b: = complete_type_witness %struct_type.n [concrete] // CHECK:STDOUT: %Copy.type: type = facet_type <@Copy> [concrete] // CHECK:STDOUT: %Int.as.Copy.impl.Op.type.824: type = fn_type @Int.as.Copy.impl.Op, @Int.as.Copy.impl(%N) [symbolic] // CHECK:STDOUT: %Int.as.Copy.impl.Op.9b9: %Int.as.Copy.impl.Op.type.824 = struct_value () [symbolic] // CHECK:STDOUT: %Copy.impl_witness.f17: = impl_witness imports.%Copy.impl_witness_table.e76, @Int.as.Copy.impl(%int_32) [concrete] // CHECK:STDOUT: %Int.as.Copy.impl.Op.type.546: type = fn_type @Int.as.Copy.impl.Op, @Int.as.Copy.impl(%int_32) [concrete] // CHECK:STDOUT: %Int.as.Copy.impl.Op.664: %Int.as.Copy.impl.Op.type.546 = struct_value () [concrete] // CHECK:STDOUT: %Copy.facet: %Copy.type = facet_value %i32, (%Copy.impl_witness.f17) [concrete] // CHECK:STDOUT: %Copy.WithSelf.Op.type.081: type = fn_type @Copy.WithSelf.Op, @Copy.WithSelf(%Copy.facet) [concrete] // CHECK:STDOUT: %.8e2: type = fn_type_with_self_type %Copy.WithSelf.Op.type.081, %Copy.facet [concrete] // CHECK:STDOUT: %Int.as.Copy.impl.Op.specific_fn: = specific_function %Int.as.Copy.impl.Op.664, @Int.as.Copy.impl.Op(%int_32) [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: .Int = %Core.Int // CHECK:STDOUT: .Copy = %Core.Copy // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: %Core.Int: %Int.type = import_ref Core//prelude/parts/int, Int, loaded [concrete = constants.%Int.generic] // CHECK:STDOUT: %Core.Copy: type = import_ref Core//prelude/parts/copy, Copy, loaded [concrete = constants.%Copy.type] // CHECK:STDOUT: %Core.import_ref.18d: @Int.as.Copy.impl.%Int.as.Copy.impl.Op.type (%Int.as.Copy.impl.Op.type.824) = import_ref Core//prelude/parts/int, loc{{\d+_\d+}}, loaded [symbolic = @Int.as.Copy.impl.%Int.as.Copy.impl.Op (constants.%Int.as.Copy.impl.Op.9b9)] // CHECK:STDOUT: %Copy.impl_witness_table.e76 = impl_witness_table (%Core.import_ref.18d), @Int.as.Copy.impl [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .Class = %Class.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %Class.decl: type = class_decl @Class [concrete = constants.%Class] {} {} // CHECK:STDOUT: %Class.F.decl: %Class.F.type = fn_decl @Class.F [concrete = constants.%Class.F] { // CHECK:STDOUT: %self.patt.loc21_16: %pattern_type.904 = ref_binding_pattern self [concrete] // CHECK:STDOUT: %self.param_patt.loc21_20: %pattern_type.904 = ref_param_pattern %self.patt.loc21_16 [concrete] // CHECK:STDOUT: %self.patt.loc21_28: %pattern_type.7ce = value_binding_pattern r#self [concrete] // CHECK:STDOUT: %self.param_patt.loc21_34: %pattern_type.7ce = value_param_pattern %self.patt.loc21_28 [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %self.param.loc21_20: ref %Class = ref_param call_param0 // CHECK:STDOUT: %Self.ref.loc21: type = name_ref Self, constants.%Class [concrete = constants.%Class] // CHECK:STDOUT: %self.loc21_16: ref %Class = ref_binding self, %self.param.loc21_20 // CHECK:STDOUT: %self.param.loc21_34: %i32 = value_param call_param1 // CHECK:STDOUT: %i32.loc21: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %self.loc21_28: %i32 = value_binding r#self, %self.param.loc21_34 // CHECK:STDOUT: } // CHECK:STDOUT: %Class.G.decl: %Class.G.type = fn_decl @Class.G [concrete = constants.%Class.G] { // CHECK:STDOUT: %self.patt.loc25_12: %pattern_type.904 = value_binding_pattern self [concrete] // CHECK:STDOUT: %self.param_patt.loc25_16: %pattern_type.904 = value_param_pattern %self.patt.loc25_12 [concrete] // CHECK:STDOUT: %self.patt.loc25_24: %pattern_type.7ce = value_binding_pattern r#self [concrete] // CHECK:STDOUT: %self.param_patt.loc25_30: %pattern_type.7ce = value_param_pattern %self.patt.loc25_24 [concrete] // CHECK:STDOUT: %return.patt: %pattern_type.511 = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: %pattern_type.511 = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %i32.loc25_41: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %i32.loc25_46: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %.loc25_49.1: %tuple.type.24b = tuple_literal (%i32.loc25_41, %i32.loc25_46) [concrete = constants.%tuple.95a] // CHECK:STDOUT: %.loc25_49.2: type = converted %.loc25_49.1, constants.%tuple.type.d07 [concrete = constants.%tuple.type.d07] // CHECK:STDOUT: %.loc25_49.3: Core.Form = init_form %.loc25_49.2 [concrete = constants.%.f32] // CHECK:STDOUT: %self.param.loc25_16: %Class = value_param call_param0 // CHECK:STDOUT: %Self.ref.loc25: type = name_ref Self, constants.%Class [concrete = constants.%Class] // CHECK:STDOUT: %self.loc25_12: %Class = value_binding self, %self.param.loc25_16 // CHECK:STDOUT: %self.param.loc25_30: %i32 = value_param call_param1 // CHECK:STDOUT: %i32.loc25_32: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %self.loc25_24: %i32 = value_binding r#self, %self.param.loc25_30 // CHECK:STDOUT: %return.param.loc25: ref %tuple.type.d07 = out_param call_param2 // CHECK:STDOUT: %return.loc25: ref %tuple.type.d07 = return_slot %return.param.loc25 // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @Class { // CHECK:STDOUT: %Class.F.decl: %Class.F.type = fn_decl @Class.F [concrete = constants.%Class.F] { // CHECK:STDOUT: %self.patt.loc21_16: %pattern_type.904 = ref_binding_pattern self [concrete] // CHECK:STDOUT: %self.param_patt.loc21_20: %pattern_type.904 = ref_param_pattern %self.patt.loc21_16 [concrete] // CHECK:STDOUT: %self.patt.loc21_28: %pattern_type.7ce = value_binding_pattern r#self [concrete] // CHECK:STDOUT: %self.param_patt.loc21_34: %pattern_type.7ce = value_param_pattern %self.patt.loc21_28 [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %self.param.loc16_16: ref %Class = ref_param call_param0 // CHECK:STDOUT: %Self.ref.loc16: type = name_ref Self, constants.%Class [concrete = constants.%Class] // CHECK:STDOUT: %self.loc16_12: ref %Class = ref_binding self, %self.param.loc16_16 // CHECK:STDOUT: %self.param.loc16_30: %i32 = value_param call_param1 // CHECK:STDOUT: %i32.loc16: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %self.loc16_24: %i32 = value_binding r#self, %self.param.loc16_30 // CHECK:STDOUT: } // CHECK:STDOUT: %Class.G.decl: %Class.G.type = fn_decl @Class.G [concrete = constants.%Class.G] { // CHECK:STDOUT: %self.patt.loc25_12: %pattern_type.904 = value_binding_pattern self [concrete] // CHECK:STDOUT: %self.param_patt.loc25_16: %pattern_type.904 = value_param_pattern %self.patt.loc25_12 [concrete] // CHECK:STDOUT: %self.patt.loc25_24: %pattern_type.7ce = value_binding_pattern r#self [concrete] // CHECK:STDOUT: %self.param_patt.loc25_30: %pattern_type.7ce = value_param_pattern %self.patt.loc25_24 [concrete] // CHECK:STDOUT: %return.patt: %pattern_type.511 = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: %pattern_type.511 = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %i32.loc17_37: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %i32.loc17_42: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %.loc17_45.1: %tuple.type.24b = tuple_literal (%i32.loc17_37, %i32.loc17_42) [concrete = constants.%tuple.95a] // CHECK:STDOUT: %.loc17_45.2: type = converted %.loc17_45.1, constants.%tuple.type.d07 [concrete = constants.%tuple.type.d07] // CHECK:STDOUT: %.loc17_45.3: Core.Form = init_form %.loc17_45.2 [concrete = constants.%.f32] // CHECK:STDOUT: %self.param.loc17_12: %Class = value_param call_param0 // CHECK:STDOUT: %Self.ref.loc17: type = name_ref Self, constants.%Class [concrete = constants.%Class] // CHECK:STDOUT: %self.loc17_8: %Class = value_binding self, %self.param.loc17_12 // CHECK:STDOUT: %self.param.loc17_26: %i32 = value_param call_param1 // CHECK:STDOUT: %i32.loc17_28: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %self.loc17_20: %i32 = value_binding r#self, %self.param.loc17_26 // CHECK:STDOUT: %return.param.loc17: ref %tuple.type.d07 = out_param call_param2 // CHECK:STDOUT: %return.loc17: ref %tuple.type.d07 = return_slot %return.param.loc17 // CHECK:STDOUT: } // CHECK:STDOUT: %i32: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %.loc18: %Class.elem = field_decl n, element0 [concrete] // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%struct_type.n [concrete = constants.%complete_type.54b] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%Class // CHECK:STDOUT: .F = %Class.F.decl // CHECK:STDOUT: .G = %Class.G.decl // CHECK:STDOUT: .n = %.loc18 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @Class.F(%self.param.loc21_20: ref %Class, %self.param.loc21_34: %i32) { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %self.ref.loc22_3: ref %Class = name_ref self, %self.loc21_16 // CHECK:STDOUT: %n.ref: %Class.elem = name_ref n, @Class.%.loc18 [concrete = @Class.%.loc18] // CHECK:STDOUT: %.loc22: ref %i32 = class_element_access %self.ref.loc22_3, element0 // CHECK:STDOUT: %self.ref.loc22_12: %i32 = name_ref r#self, %self.loc21_28 // CHECK:STDOUT: %impl.elem0: %.8e2 = impl_witness_access constants.%Copy.impl_witness.f17, element0 [concrete = constants.%Int.as.Copy.impl.Op.664] // CHECK:STDOUT: %bound_method.loc22_12.1: = bound_method %self.ref.loc22_12, %impl.elem0 // CHECK:STDOUT: %specific_fn: = specific_function %impl.elem0, @Int.as.Copy.impl.Op(constants.%int_32) [concrete = constants.%Int.as.Copy.impl.Op.specific_fn] // CHECK:STDOUT: %bound_method.loc22_12.2: = bound_method %self.ref.loc22_12, %specific_fn // CHECK:STDOUT: %Int.as.Copy.impl.Op.call: init %i32 = call %bound_method.loc22_12.2(%self.ref.loc22_12) // CHECK:STDOUT: assign %.loc22, %Int.as.Copy.impl.Op.call // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @Class.G(%self.param.loc25_16: %Class, %self.param.loc25_30: %i32) -> out %return.param.loc25: %tuple.type.d07 { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %self.ref.loc26_11: %Class = name_ref self, %self.loc25_12 // CHECK:STDOUT: %n.ref: %Class.elem = name_ref n, @Class.%.loc18 [concrete = @Class.%.loc18] // CHECK:STDOUT: %.loc26_15.1: ref %i32 = class_element_access %self.ref.loc26_11, element0 // CHECK:STDOUT: %.loc26_15.2: %i32 = acquire_value %.loc26_15.1 // CHECK:STDOUT: %self.ref.loc26_19: %i32 = name_ref r#self, %self.loc25_24 // CHECK:STDOUT: %.loc26_25.1: %tuple.type.d07 = tuple_literal (%.loc26_15.2, %self.ref.loc26_19) // CHECK:STDOUT: %impl.elem0.loc26_15: %.8e2 = impl_witness_access constants.%Copy.impl_witness.f17, element0 [concrete = constants.%Int.as.Copy.impl.Op.664] // CHECK:STDOUT: %bound_method.loc26_15.1: = bound_method %.loc26_15.2, %impl.elem0.loc26_15 // CHECK:STDOUT: %specific_fn.loc26_15: = specific_function %impl.elem0.loc26_15, @Int.as.Copy.impl.Op(constants.%int_32) [concrete = constants.%Int.as.Copy.impl.Op.specific_fn] // CHECK:STDOUT: %bound_method.loc26_15.2: = bound_method %.loc26_15.2, %specific_fn.loc26_15 // CHECK:STDOUT: %Int.as.Copy.impl.Op.call.loc26_15: init %i32 = call %bound_method.loc26_15.2(%.loc26_15.2) // CHECK:STDOUT: %tuple.elem0: ref %i32 = tuple_access %return.param.loc25, element0 // CHECK:STDOUT: %.loc26_25.2: init %i32 to %tuple.elem0 = in_place_init %Int.as.Copy.impl.Op.call.loc26_15 // CHECK:STDOUT: %impl.elem0.loc26_19: %.8e2 = impl_witness_access constants.%Copy.impl_witness.f17, element0 [concrete = constants.%Int.as.Copy.impl.Op.664] // CHECK:STDOUT: %bound_method.loc26_19.1: = bound_method %self.ref.loc26_19, %impl.elem0.loc26_19 // CHECK:STDOUT: %specific_fn.loc26_19: = specific_function %impl.elem0.loc26_19, @Int.as.Copy.impl.Op(constants.%int_32) [concrete = constants.%Int.as.Copy.impl.Op.specific_fn] // CHECK:STDOUT: %bound_method.loc26_19.2: = bound_method %self.ref.loc26_19, %specific_fn.loc26_19 // CHECK:STDOUT: %Int.as.Copy.impl.Op.call.loc26_19: init %i32 = call %bound_method.loc26_19.2(%self.ref.loc26_19) // CHECK:STDOUT: %tuple.elem1: ref %i32 = tuple_access %return.param.loc25, element1 // CHECK:STDOUT: %.loc26_25.3: init %i32 to %tuple.elem1 = in_place_init %Int.as.Copy.impl.Op.call.loc26_19 // CHECK:STDOUT: %.loc26_25.4: init %tuple.type.d07 to %return.param.loc25 = tuple_init (%.loc26_25.2, %.loc26_25.3) // CHECK:STDOUT: %.loc26_26: init %tuple.type.d07 = converted %.loc26_25.1, %.loc26_25.4 // CHECK:STDOUT: return %.loc26_26 to %return.param.loc25 // CHECK:STDOUT: } // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/class/self/raw_self_type.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/convert.carbon // TODO: Add ranges and switch to "--dump-sem-ir-ranges=only". // EXTRA-ARGS: --dump-sem-ir-ranges=if-present // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/class/self/raw_self_type.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/class/self/raw_self_type.carbon class Class { fn F() { var r#Self: Self*; var unused p: Self* = r#Self; } } class MemberNamedSelf { class r#Self {} fn F(x: Self, y: r#Self); } fn MemberNamedSelf.F(unused x: Self, unused y: r#Self) {} // CHECK:STDOUT: --- raw_self_type.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %Class: type = class_type @Class [concrete] // CHECK:STDOUT: %Class.F.type: type = fn_type @Class.F [concrete] // CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] // CHECK:STDOUT: %Class.F: %Class.F.type = struct_value () [concrete] // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %complete_type.357: = complete_type_witness %empty_struct_type [concrete] // CHECK:STDOUT: %ptr.8e5: type = ptr_type %Class [concrete] // CHECK:STDOUT: %pattern_type.018: type = pattern_type %ptr.8e5 [concrete] // CHECK:STDOUT: %DefaultOrUnformed.type: type = facet_type <@DefaultOrUnformed> [concrete] // CHECK:STDOUT: %T.67d: type = symbolic_binding T, 0 [symbolic] // CHECK:STDOUT: %T.as.DefaultOrUnformed.impl.Op.type.6c6: type = fn_type @T.as.DefaultOrUnformed.impl.Op, @T.as.DefaultOrUnformed.impl(%T.67d) [symbolic] // CHECK:STDOUT: %T.as.DefaultOrUnformed.impl.Op.ae1: %T.as.DefaultOrUnformed.impl.Op.type.6c6 = struct_value () [symbolic] // CHECK:STDOUT: %DefaultOrUnformed.impl_witness.5ad: = impl_witness imports.%DefaultOrUnformed.impl_witness_table, @T.as.DefaultOrUnformed.impl(%ptr.8e5) [concrete] // CHECK:STDOUT: %T.as.DefaultOrUnformed.impl.Op.type.e77: type = fn_type @T.as.DefaultOrUnformed.impl.Op, @T.as.DefaultOrUnformed.impl(%ptr.8e5) [concrete] // CHECK:STDOUT: %T.as.DefaultOrUnformed.impl.Op.d9e: %T.as.DefaultOrUnformed.impl.Op.type.e77 = struct_value () [concrete] // CHECK:STDOUT: %DefaultOrUnformed.facet: %DefaultOrUnformed.type = facet_value %ptr.8e5, (%DefaultOrUnformed.impl_witness.5ad) [concrete] // CHECK:STDOUT: %T.as.DefaultOrUnformed.impl.Op.specific_fn: = specific_function %T.as.DefaultOrUnformed.impl.Op.d9e, @T.as.DefaultOrUnformed.impl.Op(%ptr.8e5) [concrete] // CHECK:STDOUT: %Copy.type: type = facet_type <@Copy> [concrete] // CHECK:STDOUT: %ptr.as.Copy.impl.Op.type.2d4: type = fn_type @ptr.as.Copy.impl.Op, @ptr.as.Copy.impl(%T.67d) [symbolic] // CHECK:STDOUT: %ptr.as.Copy.impl.Op.74e: %ptr.as.Copy.impl.Op.type.2d4 = struct_value () [symbolic] // CHECK:STDOUT: %Copy.impl_witness.9d3: = impl_witness imports.%Copy.impl_witness_table.c3a, @ptr.as.Copy.impl(%Class) [concrete] // CHECK:STDOUT: %ptr.as.Copy.impl.Op.type.02e: type = fn_type @ptr.as.Copy.impl.Op, @ptr.as.Copy.impl(%Class) [concrete] // CHECK:STDOUT: %ptr.as.Copy.impl.Op.120: %ptr.as.Copy.impl.Op.type.02e = struct_value () [concrete] // CHECK:STDOUT: %Copy.facet: %Copy.type = facet_value %ptr.8e5, (%Copy.impl_witness.9d3) [concrete] // CHECK:STDOUT: %Copy.WithSelf.Op.type.130: type = fn_type @Copy.WithSelf.Op, @Copy.WithSelf(%Copy.facet) [concrete] // CHECK:STDOUT: %.370: type = fn_type_with_self_type %Copy.WithSelf.Op.type.130, %Copy.facet [concrete] // CHECK:STDOUT: %ptr.as.Copy.impl.Op.specific_fn: = specific_function %ptr.as.Copy.impl.Op.120, @ptr.as.Copy.impl.Op(%Class) [concrete] // CHECK:STDOUT: %Destroy.type: type = facet_type <@Destroy> [concrete] // CHECK:STDOUT: %Destroy.Op.type: type = fn_type @Destroy.Op [concrete] // CHECK:STDOUT: %Destroy.Op: %Destroy.Op.type = struct_value () [concrete] // CHECK:STDOUT: %MemberNamedSelf: type = class_type @MemberNamedSelf [concrete] // CHECK:STDOUT: %Self.0d4: type = class_type @Self [concrete] // CHECK:STDOUT: %pattern_type.4b2: type = pattern_type %MemberNamedSelf [concrete] // CHECK:STDOUT: %pattern_type.f56: type = pattern_type %Self.0d4 [concrete] // CHECK:STDOUT: %MemberNamedSelf.F.type: type = fn_type @MemberNamedSelf.F [concrete] // CHECK:STDOUT: %MemberNamedSelf.F: %MemberNamedSelf.F.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: .DefaultOrUnformed = %Core.DefaultOrUnformed // CHECK:STDOUT: .Copy = %Core.Copy // CHECK:STDOUT: .Destroy = %Core.Destroy // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: %Core.DefaultOrUnformed: type = import_ref Core//prelude/parts/default, DefaultOrUnformed, loaded [concrete = constants.%DefaultOrUnformed.type] // CHECK:STDOUT: %Core.import_ref.01d: @T.as.DefaultOrUnformed.impl.%T.as.DefaultOrUnformed.impl.Op.type (%T.as.DefaultOrUnformed.impl.Op.type.6c6) = import_ref Core//prelude/parts/default, loc{{\d+_\d+}}, loaded [symbolic = @T.as.DefaultOrUnformed.impl.%T.as.DefaultOrUnformed.impl.Op (constants.%T.as.DefaultOrUnformed.impl.Op.ae1)] // CHECK:STDOUT: %DefaultOrUnformed.impl_witness_table = impl_witness_table (%Core.import_ref.01d), @T.as.DefaultOrUnformed.impl [concrete] // CHECK:STDOUT: %Core.Copy: type = import_ref Core//prelude/parts/copy, Copy, loaded [concrete = constants.%Copy.type] // CHECK:STDOUT: %Core.import_ref.203: @ptr.as.Copy.impl.%ptr.as.Copy.impl.Op.type (%ptr.as.Copy.impl.Op.type.2d4) = import_ref Core//prelude/parts/copy, loc{{\d+_\d+}}, loaded [symbolic = @ptr.as.Copy.impl.%ptr.as.Copy.impl.Op (constants.%ptr.as.Copy.impl.Op.74e)] // CHECK:STDOUT: %Copy.impl_witness_table.c3a = impl_witness_table (%Core.import_ref.203), @ptr.as.Copy.impl [concrete] // CHECK:STDOUT: %Core.Destroy: type = import_ref Core//prelude/parts/destroy, Destroy, loaded [concrete = constants.%Destroy.type] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .Class = %Class.decl // CHECK:STDOUT: .MemberNamedSelf = %MemberNamedSelf.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %Class.decl: type = class_decl @Class [concrete = constants.%Class] {} {} // CHECK:STDOUT: %MemberNamedSelf.decl: type = class_decl @MemberNamedSelf [concrete = constants.%MemberNamedSelf] {} {} // CHECK:STDOUT: %MemberNamedSelf.F.decl: %MemberNamedSelf.F.type = fn_decl @MemberNamedSelf.F [concrete = constants.%MemberNamedSelf.F] { // CHECK:STDOUT: %x.patt: %pattern_type.4b2 = value_binding_pattern x [concrete] // CHECK:STDOUT: %x.param_patt: %pattern_type.4b2 = value_param_pattern %x.patt [concrete] // CHECK:STDOUT: %y.patt: %pattern_type.f56 = value_binding_pattern y [concrete] // CHECK:STDOUT: %y.param_patt: %pattern_type.f56 = value_param_pattern %y.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %x.param.loc28: %MemberNamedSelf = value_param call_param0 // CHECK:STDOUT: %Self.ref.loc28_32: type = name_ref Self, constants.%MemberNamedSelf [concrete = constants.%MemberNamedSelf] // CHECK:STDOUT: %x.loc28: %MemberNamedSelf = value_binding x, %x.param.loc28 // CHECK:STDOUT: %y.param.loc28: %Self.0d4 = value_param call_param1 // CHECK:STDOUT: %Self.ref.loc28_48: type = name_ref r#Self, @MemberNamedSelf.%Self.decl [concrete = constants.%Self.0d4] // CHECK:STDOUT: %y.loc28: %Self.0d4 = value_binding y, %y.param.loc28 // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @Class { // CHECK:STDOUT: %Class.F.decl: %Class.F.type = fn_decl @Class.F [concrete = constants.%Class.F] {} {} // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type.357] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%Class // CHECK:STDOUT: .F = %Class.F.decl // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @MemberNamedSelf { // CHECK:STDOUT: %Self.decl: type = class_decl @Self [concrete = constants.%Self.0d4] {} {} // CHECK:STDOUT: %MemberNamedSelf.F.decl: %MemberNamedSelf.F.type = fn_decl @MemberNamedSelf.F [concrete = constants.%MemberNamedSelf.F] { // CHECK:STDOUT: %x.patt: %pattern_type.4b2 = value_binding_pattern x [concrete] // CHECK:STDOUT: %x.param_patt: %pattern_type.4b2 = value_param_pattern %x.patt [concrete] // CHECK:STDOUT: %y.patt: %pattern_type.f56 = value_binding_pattern y [concrete] // CHECK:STDOUT: %y.param_patt: %pattern_type.f56 = value_param_pattern %y.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %x.param.loc25: %MemberNamedSelf = value_param call_param0 // CHECK:STDOUT: %Self.ref.loc25_11: type = name_ref Self, constants.%MemberNamedSelf [concrete = constants.%MemberNamedSelf] // CHECK:STDOUT: %x.loc25: %MemberNamedSelf = value_binding x, %x.param.loc25 // CHECK:STDOUT: %y.param.loc25: %Self.0d4 = value_param call_param1 // CHECK:STDOUT: %Self.ref.loc25_20: type = name_ref r#Self, @MemberNamedSelf.%Self.decl [concrete = constants.%Self.0d4] // CHECK:STDOUT: %y.loc25: %Self.0d4 = value_binding y, %y.param.loc25 // CHECK:STDOUT: } // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type.357] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%MemberNamedSelf // CHECK:STDOUT: .r#Self = %Self.decl // CHECK:STDOUT: .F = %MemberNamedSelf.F.decl // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @Self { // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type.357] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%Self.0d4 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @Class.F() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %Self.patt: %pattern_type.018 = ref_binding_pattern r#Self [concrete] // CHECK:STDOUT: %Self.var_patt: %pattern_type.018 = var_pattern %Self.patt [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %Self.var: ref %ptr.8e5 = var %Self.var_patt // CHECK:STDOUT: %DefaultOrUnformed.facet: %DefaultOrUnformed.type = facet_value constants.%ptr.8e5, (constants.%DefaultOrUnformed.impl_witness.5ad) [concrete = constants.%DefaultOrUnformed.facet] // CHECK:STDOUT: %.loc17_22.1: %DefaultOrUnformed.type = converted constants.%ptr.8e5, %DefaultOrUnformed.facet [concrete = constants.%DefaultOrUnformed.facet] // CHECK:STDOUT: %as_type: type = facet_access_type %.loc17_22.1 [concrete = constants.%ptr.8e5] // CHECK:STDOUT: %.loc17_22.2: type = converted %.loc17_22.1, %as_type [concrete = constants.%ptr.8e5] // CHECK:STDOUT: %T.as.DefaultOrUnformed.impl.Op.specific_fn: = specific_function constants.%T.as.DefaultOrUnformed.impl.Op.d9e, @T.as.DefaultOrUnformed.impl.Op(constants.%ptr.8e5) [concrete = constants.%T.as.DefaultOrUnformed.impl.Op.specific_fn] // CHECK:STDOUT: %T.as.DefaultOrUnformed.impl.Op.call: init %ptr.8e5 = call %T.as.DefaultOrUnformed.impl.Op.specific_fn() // CHECK:STDOUT: assign %Self.var, %T.as.DefaultOrUnformed.impl.Op.call // CHECK:STDOUT: %.loc17_21: type = splice_block %ptr.loc17 [concrete = constants.%ptr.8e5] { // CHECK:STDOUT: %Self.ref.loc17: type = name_ref Self, constants.%Class [concrete = constants.%Class] // CHECK:STDOUT: %ptr.loc17: type = ptr_type %Self.ref.loc17 [concrete = constants.%ptr.8e5] // CHECK:STDOUT: } // CHECK:STDOUT: %Self: ref %ptr.8e5 = ref_binding r#Self, %Self.var // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %p.patt: %pattern_type.018 = ref_binding_pattern p [concrete] // CHECK:STDOUT: %p.var_patt: %pattern_type.018 = var_pattern %p.patt [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %p.var: ref %ptr.8e5 = var %p.var_patt // CHECK:STDOUT: %Self.ref.loc18_27: ref %ptr.8e5 = name_ref r#Self, %Self // CHECK:STDOUT: %.loc18_27: %ptr.8e5 = acquire_value %Self.ref.loc18_27 // CHECK:STDOUT: %impl.elem0: %.370 = impl_witness_access constants.%Copy.impl_witness.9d3, element0 [concrete = constants.%ptr.as.Copy.impl.Op.120] // CHECK:STDOUT: %bound_method.loc18_27.1: = bound_method %.loc18_27, %impl.elem0 // CHECK:STDOUT: %specific_fn: = specific_function %impl.elem0, @ptr.as.Copy.impl.Op(constants.%Class) [concrete = constants.%ptr.as.Copy.impl.Op.specific_fn] // CHECK:STDOUT: %bound_method.loc18_27.2: = bound_method %.loc18_27, %specific_fn // CHECK:STDOUT: %ptr.as.Copy.impl.Op.call: init %ptr.8e5 = call %bound_method.loc18_27.2(%.loc18_27) // CHECK:STDOUT: assign %p.var, %ptr.as.Copy.impl.Op.call // CHECK:STDOUT: %.loc18_23: type = splice_block %ptr.loc18 [concrete = constants.%ptr.8e5] { // CHECK:STDOUT: %Self.ref.loc18_19: type = name_ref Self, constants.%Class [concrete = constants.%Class] // CHECK:STDOUT: %ptr.loc18: type = ptr_type %Self.ref.loc18_19 [concrete = constants.%ptr.8e5] // CHECK:STDOUT: } // CHECK:STDOUT: %p: ref %ptr.8e5 = ref_binding p, %p.var // CHECK:STDOUT: %Destroy.Op.bound.loc18: = bound_method %p.var, constants.%Destroy.Op // CHECK:STDOUT: %Destroy.Op.call.loc18: init %empty_tuple.type = call %Destroy.Op.bound.loc18(%p.var) // CHECK:STDOUT: %Destroy.Op.bound.loc17: = bound_method %Self.var, constants.%Destroy.Op // CHECK:STDOUT: %Destroy.Op.call.loc17: init %empty_tuple.type = call %Destroy.Op.bound.loc17(%Self.var) // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @Destroy.Op(%self.param: ref %ptr.8e5) = "no_op"; // CHECK:STDOUT: // CHECK:STDOUT: fn @MemberNamedSelf.F(%x.param.loc28: %MemberNamedSelf, %y.param.loc28: %Self.0d4) { // CHECK:STDOUT: !entry: // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/class/self/self.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/int.carbon // TODO: Add ranges and switch to "--dump-sem-ir-ranges=only". // EXTRA-ARGS: --dump-sem-ir-ranges=if-present // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/class/self/self.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/class/self/self.carbon // --- self.carbon library "[[@TEST_NAME]]"; class Class { fn F[self: Self]() -> i32; fn G[ref self: Self]() -> i32; var n: i32; } fn Class.F[self: Self]() -> i32 { return self.n; } fn Class.G[ref self: Self]() -> i32 { return self.n; } // --- fail_return_self_value.carbon library "[[@TEST_NAME]]"; class Class { // CHECK:STDERR: fail_return_self_value.carbon:[[@LINE+7]]:25: error: cannot implicitly convert non-type value of type `Class` to `type` [ConversionFailureNonTypeToFacet] // CHECK:STDERR: fn F[self: Self]() -> self; // CHECK:STDERR: ^~~~ // CHECK:STDERR: fail_return_self_value.carbon:[[@LINE+4]]:25: note: type `Class` does not implement interface `Core.ImplicitAs(type)` [MissingImplInMemberAccessInContext] // CHECK:STDERR: fn F[self: Self]() -> self; // CHECK:STDERR: ^~~~ // CHECK:STDERR: fn F[self: Self]() -> self; } // CHECK:STDOUT: --- self.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %Class: type = class_type @Class [concrete] // CHECK:STDOUT: %pattern_type.904: type = pattern_type %Class [concrete] // CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [concrete] // CHECK:STDOUT: %Int.type: type = generic_class_type @Int [concrete] // CHECK:STDOUT: %Int.generic: %Int.type = struct_value () [concrete] // CHECK:STDOUT: %N: Core.IntLiteral = symbolic_binding N, 0 [symbolic] // CHECK:STDOUT: %i32: type = class_type @Int, @Int(%int_32) [concrete] // CHECK:STDOUT: %.ff5: Core.Form = init_form %i32 [concrete] // CHECK:STDOUT: %pattern_type.7ce: type = pattern_type %i32 [concrete] // CHECK:STDOUT: %Class.F.type: type = fn_type @Class.F [concrete] // CHECK:STDOUT: %Class.F: %Class.F.type = struct_value () [concrete] // CHECK:STDOUT: %Class.G.type: type = fn_type @Class.G [concrete] // CHECK:STDOUT: %Class.G: %Class.G.type = struct_value () [concrete] // CHECK:STDOUT: %Class.elem: type = unbound_element_type %Class, %i32 [concrete] // CHECK:STDOUT: %struct_type.n: type = struct_type {.n: %i32} [concrete] // CHECK:STDOUT: %complete_type.54b: = complete_type_witness %struct_type.n [concrete] // CHECK:STDOUT: %Copy.type: type = facet_type <@Copy> [concrete] // CHECK:STDOUT: %Int.as.Copy.impl.Op.type.824: type = fn_type @Int.as.Copy.impl.Op, @Int.as.Copy.impl(%N) [symbolic] // CHECK:STDOUT: %Int.as.Copy.impl.Op.9b9: %Int.as.Copy.impl.Op.type.824 = struct_value () [symbolic] // CHECK:STDOUT: %Copy.impl_witness.f17: = impl_witness imports.%Copy.impl_witness_table.e76, @Int.as.Copy.impl(%int_32) [concrete] // CHECK:STDOUT: %Int.as.Copy.impl.Op.type.546: type = fn_type @Int.as.Copy.impl.Op, @Int.as.Copy.impl(%int_32) [concrete] // CHECK:STDOUT: %Int.as.Copy.impl.Op.664: %Int.as.Copy.impl.Op.type.546 = struct_value () [concrete] // CHECK:STDOUT: %Copy.facet: %Copy.type = facet_value %i32, (%Copy.impl_witness.f17) [concrete] // CHECK:STDOUT: %Copy.WithSelf.Op.type.081: type = fn_type @Copy.WithSelf.Op, @Copy.WithSelf(%Copy.facet) [concrete] // CHECK:STDOUT: %.8e2: type = fn_type_with_self_type %Copy.WithSelf.Op.type.081, %Copy.facet [concrete] // CHECK:STDOUT: %Int.as.Copy.impl.Op.specific_fn: = specific_function %Int.as.Copy.impl.Op.664, @Int.as.Copy.impl.Op(%int_32) [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: .Int = %Core.Int // CHECK:STDOUT: .Copy = %Core.Copy // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: %Core.Int: %Int.type = import_ref Core//prelude/parts/int, Int, loaded [concrete = constants.%Int.generic] // CHECK:STDOUT: %Core.Copy: type = import_ref Core//prelude/parts/copy, Copy, loaded [concrete = constants.%Copy.type] // CHECK:STDOUT: %Core.import_ref.18d: @Int.as.Copy.impl.%Int.as.Copy.impl.Op.type (%Int.as.Copy.impl.Op.type.824) = import_ref Core//prelude/parts/int, loc{{\d+_\d+}}, loaded [symbolic = @Int.as.Copy.impl.%Int.as.Copy.impl.Op (constants.%Int.as.Copy.impl.Op.9b9)] // CHECK:STDOUT: %Copy.impl_witness_table.e76 = impl_witness_table (%Core.import_ref.18d), @Int.as.Copy.impl [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .Class = %Class.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %Class.decl: type = class_decl @Class [concrete = constants.%Class] {} {} // CHECK:STDOUT: %Class.F.decl: %Class.F.type = fn_decl @Class.F [concrete = constants.%Class.F] { // CHECK:STDOUT: %self.patt: %pattern_type.904 = value_binding_pattern self [concrete] // CHECK:STDOUT: %self.param_patt: %pattern_type.904 = value_param_pattern %self.patt [concrete] // CHECK:STDOUT: %return.patt: %pattern_type.7ce = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: %pattern_type.7ce = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %i32.loc11: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %.loc11: Core.Form = init_form %i32.loc11 [concrete = constants.%.ff5] // CHECK:STDOUT: %self.param.loc11: %Class = value_param call_param0 // CHECK:STDOUT: %Self.ref.loc11: type = name_ref Self, constants.%Class [concrete = constants.%Class] // CHECK:STDOUT: %self.loc11: %Class = value_binding self, %self.param.loc11 // CHECK:STDOUT: %return.param.loc11: ref %i32 = out_param call_param1 // CHECK:STDOUT: %return.loc11: ref %i32 = return_slot %return.param.loc11 // CHECK:STDOUT: } // CHECK:STDOUT: %Class.G.decl: %Class.G.type = fn_decl @Class.G [concrete = constants.%Class.G] { // CHECK:STDOUT: %self.patt: %pattern_type.904 = ref_binding_pattern self [concrete] // CHECK:STDOUT: %self.param_patt: %pattern_type.904 = ref_param_pattern %self.patt [concrete] // CHECK:STDOUT: %return.patt: %pattern_type.7ce = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: %pattern_type.7ce = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %i32.loc15: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %.loc15: Core.Form = init_form %i32.loc15 [concrete = constants.%.ff5] // CHECK:STDOUT: %self.param.loc15: ref %Class = ref_param call_param0 // CHECK:STDOUT: %Self.ref.loc15: type = name_ref Self, constants.%Class [concrete = constants.%Class] // CHECK:STDOUT: %self.loc15: ref %Class = ref_binding self, %self.param.loc15 // CHECK:STDOUT: %return.param.loc15: ref %i32 = out_param call_param1 // CHECK:STDOUT: %return.loc15: ref %i32 = return_slot %return.param.loc15 // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @Class { // CHECK:STDOUT: %Class.F.decl: %Class.F.type = fn_decl @Class.F [concrete = constants.%Class.F] { // CHECK:STDOUT: %self.patt: %pattern_type.904 = value_binding_pattern self [concrete] // CHECK:STDOUT: %self.param_patt: %pattern_type.904 = value_param_pattern %self.patt [concrete] // CHECK:STDOUT: %return.patt: %pattern_type.7ce = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: %pattern_type.7ce = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %i32.loc5: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %.loc5: Core.Form = init_form %i32.loc5 [concrete = constants.%.ff5] // CHECK:STDOUT: %self.param.loc5: %Class = value_param call_param0 // CHECK:STDOUT: %Self.ref.loc5: type = name_ref Self, constants.%Class [concrete = constants.%Class] // CHECK:STDOUT: %self.loc5: %Class = value_binding self, %self.param.loc5 // CHECK:STDOUT: %return.param.loc5: ref %i32 = out_param call_param1 // CHECK:STDOUT: %return.loc5: ref %i32 = return_slot %return.param.loc5 // CHECK:STDOUT: } // CHECK:STDOUT: %Class.G.decl: %Class.G.type = fn_decl @Class.G [concrete = constants.%Class.G] { // CHECK:STDOUT: %self.patt: %pattern_type.904 = ref_binding_pattern self [concrete] // CHECK:STDOUT: %self.param_patt: %pattern_type.904 = ref_param_pattern %self.patt [concrete] // CHECK:STDOUT: %return.patt: %pattern_type.7ce = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: %pattern_type.7ce = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %i32.loc6: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %.loc6: Core.Form = init_form %i32.loc6 [concrete = constants.%.ff5] // CHECK:STDOUT: %self.param.loc6: ref %Class = ref_param call_param0 // CHECK:STDOUT: %Self.ref.loc6: type = name_ref Self, constants.%Class [concrete = constants.%Class] // CHECK:STDOUT: %self.loc6: ref %Class = ref_binding self, %self.param.loc6 // CHECK:STDOUT: %return.param.loc6: ref %i32 = out_param call_param1 // CHECK:STDOUT: %return.loc6: ref %i32 = return_slot %return.param.loc6 // CHECK:STDOUT: } // CHECK:STDOUT: %i32: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %.loc8: %Class.elem = field_decl n, element0 [concrete] // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%struct_type.n [concrete = constants.%complete_type.54b] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%Class // CHECK:STDOUT: .F = %Class.F.decl // CHECK:STDOUT: .G = %Class.G.decl // CHECK:STDOUT: .n = %.loc8 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @Class.F(%self.param.loc11: %Class) -> out %return.param.loc11: %i32 { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %self.ref: %Class = name_ref self, %self.loc11 // CHECK:STDOUT: %n.ref: %Class.elem = name_ref n, @Class.%.loc8 [concrete = @Class.%.loc8] // CHECK:STDOUT: %.loc12_14.1: ref %i32 = class_element_access %self.ref, element0 // CHECK:STDOUT: %.loc12_14.2: %i32 = acquire_value %.loc12_14.1 // CHECK:STDOUT: %impl.elem0: %.8e2 = impl_witness_access constants.%Copy.impl_witness.f17, element0 [concrete = constants.%Int.as.Copy.impl.Op.664] // CHECK:STDOUT: %bound_method.loc12_14.1: = bound_method %.loc12_14.2, %impl.elem0 // CHECK:STDOUT: %specific_fn: = specific_function %impl.elem0, @Int.as.Copy.impl.Op(constants.%int_32) [concrete = constants.%Int.as.Copy.impl.Op.specific_fn] // CHECK:STDOUT: %bound_method.loc12_14.2: = bound_method %.loc12_14.2, %specific_fn // CHECK:STDOUT: %Int.as.Copy.impl.Op.call: init %i32 = call %bound_method.loc12_14.2(%.loc12_14.2) // CHECK:STDOUT: return %Int.as.Copy.impl.Op.call // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @Class.G(%self.param.loc15: ref %Class) -> out %return.param.loc15: %i32 { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %self.ref: ref %Class = name_ref self, %self.loc15 // CHECK:STDOUT: %n.ref: %Class.elem = name_ref n, @Class.%.loc8 [concrete = @Class.%.loc8] // CHECK:STDOUT: %.loc16_14.1: ref %i32 = class_element_access %self.ref, element0 // CHECK:STDOUT: %.loc16_14.2: %i32 = acquire_value %.loc16_14.1 // CHECK:STDOUT: %impl.elem0: %.8e2 = impl_witness_access constants.%Copy.impl_witness.f17, element0 [concrete = constants.%Int.as.Copy.impl.Op.664] // CHECK:STDOUT: %bound_method.loc16_14.1: = bound_method %.loc16_14.2, %impl.elem0 // CHECK:STDOUT: %specific_fn: = specific_function %impl.elem0, @Int.as.Copy.impl.Op(constants.%int_32) [concrete = constants.%Int.as.Copy.impl.Op.specific_fn] // CHECK:STDOUT: %bound_method.loc16_14.2: = bound_method %.loc16_14.2, %specific_fn // CHECK:STDOUT: %Int.as.Copy.impl.Op.call: init %i32 = call %bound_method.loc16_14.2(%.loc16_14.2) // CHECK:STDOUT: return %Int.as.Copy.impl.Op.call // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- fail_return_self_value.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %Class: type = class_type @Class [concrete] // CHECK:STDOUT: %pattern_type.904: type = pattern_type %Class [concrete] // CHECK:STDOUT: %ImplicitAs.type.cc7: type = generic_interface_type @ImplicitAs [concrete] // CHECK:STDOUT: %ImplicitAs.generic: %ImplicitAs.type.cc7 = struct_value () [concrete] // CHECK:STDOUT: %Class.F.type: type = fn_type @Class.F [concrete] // CHECK:STDOUT: %Class.F: %Class.F.type = struct_value () [concrete] // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %complete_type: = complete_type_witness %empty_struct_type [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: .ImplicitAs = %Core.ImplicitAs // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: %Core.ImplicitAs: %ImplicitAs.type.cc7 = import_ref Core//prelude/parts/as, ImplicitAs, loaded [concrete = constants.%ImplicitAs.generic] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .Class = %Class.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %Class.decl: type = class_decl @Class [concrete = constants.%Class] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @Class { // CHECK:STDOUT: %Class.F.decl: %Class.F.type = fn_decl @Class.F [concrete = constants.%Class.F] { // CHECK:STDOUT: %self.patt: %pattern_type.904 = value_binding_pattern self [concrete] // CHECK:STDOUT: %self.param_patt: %pattern_type.904 = value_param_pattern %self.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %self.ref: %Class = name_ref self, %self // CHECK:STDOUT: %.loc12: type = converted %self.ref, [concrete = ] // CHECK:STDOUT: %self.param: %Class = value_param call_param0 // CHECK:STDOUT: %Self.ref: type = name_ref Self, constants.%Class [concrete = constants.%Class] // CHECK:STDOUT: %self: %Class = value_binding self, %self.param // CHECK:STDOUT: } // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%Class // CHECK:STDOUT: .F = %Class.F.decl // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @Class.F(%self.param: %Class) -> ; // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/class/self/self_type.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/int.carbon // TODO: Add ranges and switch to "--dump-sem-ir-ranges=only". // EXTRA-ARGS: --dump-sem-ir-ranges=if-present // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/class/self/self_type.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/class/self/self_type.carbon class Class { fn F[self: Self]() -> i32; fn Make() -> Self { returned var s: Self; s = {.p = &s}; return var; } var p: Self*; } fn Class.F[self: Self]() -> i32 { return (*self.p).F(); } // CHECK:STDOUT: --- self_type.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %Class: type = class_type @Class [concrete] // CHECK:STDOUT: %pattern_type.904: type = pattern_type %Class [concrete] // CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [concrete] // CHECK:STDOUT: %Int.type: type = generic_class_type @Int [concrete] // CHECK:STDOUT: %Int.generic: %Int.type = struct_value () [concrete] // CHECK:STDOUT: %i32: type = class_type @Int, @Int(%int_32) [concrete] // CHECK:STDOUT: %.ff5: Core.Form = init_form %i32 [concrete] // CHECK:STDOUT: %pattern_type.7ce: type = pattern_type %i32 [concrete] // CHECK:STDOUT: %Class.F.type: type = fn_type @Class.F [concrete] // CHECK:STDOUT: %Class.F: %Class.F.type = struct_value () [concrete] // CHECK:STDOUT: %.cff: Core.Form = init_form %Class [concrete] // CHECK:STDOUT: %Class.Make.type: type = fn_type @Class.Make [concrete] // CHECK:STDOUT: %Class.Make: %Class.Make.type = struct_value () [concrete] // CHECK:STDOUT: %ptr.8e5: type = ptr_type %Class [concrete] // CHECK:STDOUT: %Class.elem: type = unbound_element_type %Class, %ptr.8e5 [concrete] // CHECK:STDOUT: %struct_type.p: type = struct_type {.p: %ptr.8e5} [concrete] // CHECK:STDOUT: %complete_type.141: = complete_type_witness %struct_type.p [concrete] // CHECK:STDOUT: %DefaultOrUnformed.type: type = facet_type <@DefaultOrUnformed> [concrete] // CHECK:STDOUT: %T.67d: type = symbolic_binding T, 0 [symbolic] // CHECK:STDOUT: %T.as.DefaultOrUnformed.impl.Op.type.6c6: type = fn_type @T.as.DefaultOrUnformed.impl.Op, @T.as.DefaultOrUnformed.impl(%T.67d) [symbolic] // CHECK:STDOUT: %T.as.DefaultOrUnformed.impl.Op.ae1: %T.as.DefaultOrUnformed.impl.Op.type.6c6 = struct_value () [symbolic] // CHECK:STDOUT: %DefaultOrUnformed.impl_witness.6ca: = impl_witness imports.%DefaultOrUnformed.impl_witness_table, @T.as.DefaultOrUnformed.impl(%Class) [concrete] // CHECK:STDOUT: %T.as.DefaultOrUnformed.impl.Op.type.3d0: type = fn_type @T.as.DefaultOrUnformed.impl.Op, @T.as.DefaultOrUnformed.impl(%Class) [concrete] // CHECK:STDOUT: %T.as.DefaultOrUnformed.impl.Op.cda: %T.as.DefaultOrUnformed.impl.Op.type.3d0 = struct_value () [concrete] // CHECK:STDOUT: %DefaultOrUnformed.facet: %DefaultOrUnformed.type = facet_value %Class, (%DefaultOrUnformed.impl_witness.6ca) [concrete] // CHECK:STDOUT: %T.as.DefaultOrUnformed.impl.Op.specific_fn: = specific_function %T.as.DefaultOrUnformed.impl.Op.cda, @T.as.DefaultOrUnformed.impl.Op(%Class) [concrete] // CHECK:STDOUT: %Copy.type: type = facet_type <@Copy> [concrete] // CHECK:STDOUT: %ptr.as.Copy.impl.Op.type.2d4: type = fn_type @ptr.as.Copy.impl.Op, @ptr.as.Copy.impl(%T.67d) [symbolic] // CHECK:STDOUT: %ptr.as.Copy.impl.Op.74e: %ptr.as.Copy.impl.Op.type.2d4 = struct_value () [symbolic] // CHECK:STDOUT: %Copy.impl_witness.9d3: = impl_witness imports.%Copy.impl_witness_table.c3a, @ptr.as.Copy.impl(%Class) [concrete] // CHECK:STDOUT: %ptr.as.Copy.impl.Op.type.02e: type = fn_type @ptr.as.Copy.impl.Op, @ptr.as.Copy.impl(%Class) [concrete] // CHECK:STDOUT: %ptr.as.Copy.impl.Op.120: %ptr.as.Copy.impl.Op.type.02e = struct_value () [concrete] // CHECK:STDOUT: %Copy.facet: %Copy.type = facet_value %ptr.8e5, (%Copy.impl_witness.9d3) [concrete] // CHECK:STDOUT: %Copy.WithSelf.Op.type.130: type = fn_type @Copy.WithSelf.Op, @Copy.WithSelf(%Copy.facet) [concrete] // CHECK:STDOUT: %.370: type = fn_type_with_self_type %Copy.WithSelf.Op.type.130, %Copy.facet [concrete] // CHECK:STDOUT: %ptr.as.Copy.impl.Op.specific_fn: = specific_function %ptr.as.Copy.impl.Op.120, @ptr.as.Copy.impl.Op(%Class) [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: .Int = %Core.Int // CHECK:STDOUT: .DefaultOrUnformed = %Core.DefaultOrUnformed // CHECK:STDOUT: .Copy = %Core.Copy // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: %Core.Int: %Int.type = import_ref Core//prelude/parts/int, Int, loaded [concrete = constants.%Int.generic] // CHECK:STDOUT: %Core.DefaultOrUnformed: type = import_ref Core//prelude/parts/default, DefaultOrUnformed, loaded [concrete = constants.%DefaultOrUnformed.type] // CHECK:STDOUT: %Core.import_ref.01d: @T.as.DefaultOrUnformed.impl.%T.as.DefaultOrUnformed.impl.Op.type (%T.as.DefaultOrUnformed.impl.Op.type.6c6) = import_ref Core//prelude/parts/default, loc{{\d+_\d+}}, loaded [symbolic = @T.as.DefaultOrUnformed.impl.%T.as.DefaultOrUnformed.impl.Op (constants.%T.as.DefaultOrUnformed.impl.Op.ae1)] // CHECK:STDOUT: %DefaultOrUnformed.impl_witness_table = impl_witness_table (%Core.import_ref.01d), @T.as.DefaultOrUnformed.impl [concrete] // CHECK:STDOUT: %Core.Copy: type = import_ref Core//prelude/parts/copy, Copy, loaded [concrete = constants.%Copy.type] // CHECK:STDOUT: %Core.import_ref.203: @ptr.as.Copy.impl.%ptr.as.Copy.impl.Op.type (%ptr.as.Copy.impl.Op.type.2d4) = import_ref Core//prelude/parts/copy, loc{{\d+_\d+}}, loaded [symbolic = @ptr.as.Copy.impl.%ptr.as.Copy.impl.Op (constants.%ptr.as.Copy.impl.Op.74e)] // CHECK:STDOUT: %Copy.impl_witness_table.c3a = impl_witness_table (%Core.import_ref.203), @ptr.as.Copy.impl [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .Class = %Class.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %Class.decl: type = class_decl @Class [concrete = constants.%Class] {} {} // CHECK:STDOUT: %Class.F.decl: %Class.F.type = fn_decl @Class.F [concrete = constants.%Class.F] { // CHECK:STDOUT: %self.patt: %pattern_type.904 = value_binding_pattern self [concrete] // CHECK:STDOUT: %self.param_patt: %pattern_type.904 = value_param_pattern %self.patt [concrete] // CHECK:STDOUT: %return.patt: %pattern_type.7ce = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: %pattern_type.7ce = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %i32.loc25: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %.loc25: Core.Form = init_form %i32.loc25 [concrete = constants.%.ff5] // CHECK:STDOUT: %self.param.loc25: %Class = value_param call_param0 // CHECK:STDOUT: %Self.ref.loc25: type = name_ref Self, constants.%Class [concrete = constants.%Class] // CHECK:STDOUT: %self.loc25: %Class = value_binding self, %self.param.loc25 // CHECK:STDOUT: %return.param.loc25: ref %i32 = out_param call_param1 // CHECK:STDOUT: %return.loc25: ref %i32 = return_slot %return.param.loc25 // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @Class { // CHECK:STDOUT: %Class.F.decl: %Class.F.type = fn_decl @Class.F [concrete = constants.%Class.F] { // CHECK:STDOUT: %self.patt: %pattern_type.904 = value_binding_pattern self [concrete] // CHECK:STDOUT: %self.param_patt: %pattern_type.904 = value_param_pattern %self.patt [concrete] // CHECK:STDOUT: %return.patt: %pattern_type.7ce = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: %pattern_type.7ce = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %i32.loc16: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %.loc16: Core.Form = init_form %i32.loc16 [concrete = constants.%.ff5] // CHECK:STDOUT: %self.param.loc16: %Class = value_param call_param0 // CHECK:STDOUT: %Self.ref.loc16: type = name_ref Self, constants.%Class [concrete = constants.%Class] // CHECK:STDOUT: %self.loc16: %Class = value_binding self, %self.param.loc16 // CHECK:STDOUT: %return.param.loc16: ref %i32 = out_param call_param1 // CHECK:STDOUT: %return.loc16: ref %i32 = return_slot %return.param.loc16 // CHECK:STDOUT: } // CHECK:STDOUT: %Class.Make.decl: %Class.Make.type = fn_decl @Class.Make [concrete = constants.%Class.Make] { // CHECK:STDOUT: %return.patt: %pattern_type.904 = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: %pattern_type.904 = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %Self.ref.loc17: type = name_ref Self, constants.%Class [concrete = constants.%Class] // CHECK:STDOUT: %.loc17_16.2: Core.Form = init_form %Self.ref.loc17 [concrete = constants.%.cff] // CHECK:STDOUT: %return.param: ref %Class = out_param call_param0 // CHECK:STDOUT: %return: ref %Class = return_slot %return.param // CHECK:STDOUT: } // CHECK:STDOUT: %Self.ref: type = name_ref Self, constants.%Class [concrete = constants.%Class] // CHECK:STDOUT: %ptr: type = ptr_type %Self.ref [concrete = constants.%ptr.8e5] // CHECK:STDOUT: %.loc22: %Class.elem = field_decl p, element0 [concrete] // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%struct_type.p [concrete = constants.%complete_type.141] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%Class // CHECK:STDOUT: .F = %Class.F.decl // CHECK:STDOUT: .Make = %Class.Make.decl // CHECK:STDOUT: .p = %.loc22 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @Class.F(%self.param.loc25: %Class) -> out %return.param.loc25: %i32 { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %self.ref: %Class = name_ref self, %self.loc25 // CHECK:STDOUT: %p.ref: %Class.elem = name_ref p, @Class.%.loc22 [concrete = @Class.%.loc22] // CHECK:STDOUT: %.loc26_16.1: ref %ptr.8e5 = class_element_access %self.ref, element0 // CHECK:STDOUT: %.loc26_16.2: %ptr.8e5 = acquire_value %.loc26_16.1 // CHECK:STDOUT: %.loc26_11.1: ref %Class = deref %.loc26_16.2 // CHECK:STDOUT: %F.ref: %Class.F.type = name_ref F, @Class.%Class.F.decl [concrete = constants.%Class.F] // CHECK:STDOUT: %Class.F.bound: = bound_method %.loc26_11.1, %F.ref // CHECK:STDOUT: %.loc26_11.2: %Class = acquire_value %.loc26_11.1 // CHECK:STDOUT: %Class.F.call: init %i32 = call %Class.F.bound(%.loc26_11.2) // CHECK:STDOUT: return %Class.F.call // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @Class.Make() -> out %return.param: %Class { // CHECK:STDOUT: !entry: // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %s.patt: %pattern_type.904 = ref_binding_pattern s [concrete] // CHECK:STDOUT: %s.var_patt: %pattern_type.904 = var_pattern %s.patt [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %DefaultOrUnformed.facet: %DefaultOrUnformed.type = facet_value constants.%Class, (constants.%DefaultOrUnformed.impl_witness.6ca) [concrete = constants.%DefaultOrUnformed.facet] // CHECK:STDOUT: %.loc18_25.1: %DefaultOrUnformed.type = converted constants.%Class, %DefaultOrUnformed.facet [concrete = constants.%DefaultOrUnformed.facet] // CHECK:STDOUT: %as_type: type = facet_access_type %.loc18_25.1 [concrete = constants.%Class] // CHECK:STDOUT: %.loc18_25.2: type = converted %.loc18_25.1, %as_type [concrete = constants.%Class] // CHECK:STDOUT: %T.as.DefaultOrUnformed.impl.Op.specific_fn: = specific_function constants.%T.as.DefaultOrUnformed.impl.Op.cda, @T.as.DefaultOrUnformed.impl.Op(constants.%Class) [concrete = constants.%T.as.DefaultOrUnformed.impl.Op.specific_fn] // CHECK:STDOUT: %.loc17_16.1: ref %Class = splice_block %return.param {} // CHECK:STDOUT: %T.as.DefaultOrUnformed.impl.Op.call: init %Class to %.loc17_16.1 = call %T.as.DefaultOrUnformed.impl.Op.specific_fn() // CHECK:STDOUT: assign %return.param, %T.as.DefaultOrUnformed.impl.Op.call // CHECK:STDOUT: %Self.ref.loc18: type = name_ref Self, constants.%Class [concrete = constants.%Class] // CHECK:STDOUT: %s: ref %Class = ref_binding s, %return.param // CHECK:STDOUT: %s.ref.loc19_5: ref %Class = name_ref s, %s // CHECK:STDOUT: %s.ref.loc19_16: ref %Class = name_ref s, %s // CHECK:STDOUT: %addr: %ptr.8e5 = addr_of %s.ref.loc19_16 // CHECK:STDOUT: %.loc19_17.1: %struct_type.p = struct_literal (%addr) // CHECK:STDOUT: %impl.elem0: %.370 = impl_witness_access constants.%Copy.impl_witness.9d3, element0 [concrete = constants.%ptr.as.Copy.impl.Op.120] // CHECK:STDOUT: %bound_method.loc19_15.1: = bound_method %addr, %impl.elem0 // CHECK:STDOUT: %specific_fn: = specific_function %impl.elem0, @ptr.as.Copy.impl.Op(constants.%Class) [concrete = constants.%ptr.as.Copy.impl.Op.specific_fn] // CHECK:STDOUT: %bound_method.loc19_15.2: = bound_method %addr, %specific_fn // CHECK:STDOUT: %ptr.as.Copy.impl.Op.call: init %ptr.8e5 = call %bound_method.loc19_15.2(%addr) // CHECK:STDOUT: %.loc19_17.2: ref %ptr.8e5 = class_element_access %s.ref.loc19_5, element0 // CHECK:STDOUT: %.loc19_17.3: init %ptr.8e5 to %.loc19_17.2 = in_place_init %ptr.as.Copy.impl.Op.call // CHECK:STDOUT: %.loc19_17.4: init %Class to %s.ref.loc19_5 = class_init (%.loc19_17.3) // CHECK:STDOUT: %.loc19_7: init %Class = converted %.loc19_17.1, %.loc19_17.4 // CHECK:STDOUT: assign %s.ref.loc19_5, %.loc19_7 // CHECK:STDOUT: return %s to %return.param // CHECK:STDOUT: } // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/class/syntactic_merge.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/none.carbon // TODO: Add ranges and switch to "--dump-sem-ir-ranges=only". // EXTRA-ARGS: --dump-sem-ir-ranges=if-present // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/class/syntactic_merge.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/class/syntactic_merge.carbon // --- basic.carbon library "[[@TEST_NAME]]"; class C {} alias D = C; class Foo(a:! C); class Foo(a:! C) {} class Bar(a:! D); class Bar(a:! D) {} // --- spacing.carbon library "[[@TEST_NAME]]"; class C {} class Foo [ ] ( a :! C ); class Foo[](a:! C) {} // --- fail_parens.carbon library "[[@TEST_NAME]]"; class C {} class Foo(a:! C); // CHECK:STDERR: fail_parens.carbon:[[@LINE+7]]:15: error: redeclaration syntax differs here [RedeclParamSyntaxDiffers] // CHECK:STDERR: class Foo(a:! (C)) {} // CHECK:STDERR: ^ // CHECK:STDERR: fail_parens.carbon:[[@LINE-4]]:15: note: comparing with previous declaration here [RedeclParamSyntaxPrevious] // CHECK:STDERR: class Foo(a:! C); // CHECK:STDERR: ^ // CHECK:STDERR: class Foo(a:! (C)) {} // --- todo_fail_raw_identifier.carbon library "[[@TEST_NAME]]"; class C {} class Foo(a:! C); class Foo(a:! r#C) {} // --- two_file.carbon library "[[@TEST_NAME]]"; class C {} alias D = C; class Foo(a:! C); class Bar(a:! D); // --- two_file.impl.carbon impl library "[[@TEST_NAME]]"; class Foo(a:! C) {} class Bar(a:! D) {} // --- fail_name_mismatch.carbon library "[[@TEST_NAME]]"; class C {} alias D = C; class Foo(a:! C); // CHECK:STDERR: fail_name_mismatch.carbon:[[@LINE+7]]:11: error: redeclaration differs at parameter 1 [RedeclParamDiffers] // CHECK:STDERR: class Foo(b:! D) {} // CHECK:STDERR: ^ // CHECK:STDERR: fail_name_mismatch.carbon:[[@LINE-4]]:11: note: previous declaration's corresponding parameter here [RedeclParamPrevious] // CHECK:STDERR: class Foo(a:! C); // CHECK:STDERR: ^ // CHECK:STDERR: class Foo(b:! D) {} // --- fail_alias.carbon library "[[@TEST_NAME]]"; class C {} alias D = C; class Foo(a:! C); // CHECK:STDERR: fail_alias.carbon:[[@LINE+7]]:15: error: redeclaration syntax differs here [RedeclParamSyntaxDiffers] // CHECK:STDERR: class Foo(a:! D) {} // CHECK:STDERR: ^ // CHECK:STDERR: fail_alias.carbon:[[@LINE-4]]:15: note: comparing with previous declaration here [RedeclParamSyntaxPrevious] // CHECK:STDERR: class Foo(a:! C); // CHECK:STDERR: ^ // CHECK:STDERR: class Foo(a:! D) {} // --- fail_deduced_alias.carbon library "[[@TEST_NAME]]"; class C {} alias D = C; class Foo[a:! C](); // CHECK:STDERR: fail_deduced_alias.carbon:[[@LINE+7]]:15: error: redeclaration syntax differs here [RedeclParamSyntaxDiffers] // CHECK:STDERR: class Foo[a:! D]() {} // CHECK:STDERR: ^ // CHECK:STDERR: fail_deduced_alias.carbon:[[@LINE-4]]:15: note: comparing with previous declaration here [RedeclParamSyntaxPrevious] // CHECK:STDERR: class Foo[a:! C](); // CHECK:STDERR: ^ // CHECK:STDERR: class Foo[a:! D]() {} // --- alias_two_file.carbon library "[[@TEST_NAME]]"; class C {} class Foo(a:! C); // --- todo_fail_alias_two_file.impl.carbon impl library "[[@TEST_NAME]]"; alias D = C; class Foo(a:! D) {} // --- fail_repeat_const.carbon library "[[@TEST_NAME]]"; class C {} class Foo(a:! const C); // CHECK:STDERR: fail_repeat_const.carbon:[[@LINE+11]]:15: warning: `const` applied repeatedly to the same type has no additional effect [RepeatedConst] // CHECK:STDERR: class Foo(a:! const (const C)) {} // CHECK:STDERR: ^~~~~~~~~~~~~~~ // CHECK:STDERR: // CHECK:STDERR: fail_repeat_const.carbon:[[@LINE+7]]:21: error: redeclaration syntax differs here [RedeclParamSyntaxDiffers] // CHECK:STDERR: class Foo(a:! const (const C)) {} // CHECK:STDERR: ^ // CHECK:STDERR: fail_repeat_const.carbon:[[@LINE-8]]:21: note: comparing with previous declaration here [RedeclParamSyntaxPrevious] // CHECK:STDERR: class Foo(a:! const C); // CHECK:STDERR: ^ // CHECK:STDERR: class Foo(a:! const (const C)) {} // --- fail_self_type.carbon library "[[@TEST_NAME]]"; base class Base { var a: (); fn F[ref self: Self](); } // CHECK:STDERR: fail_self_type.carbon:[[@LINE+7]]:21: error: redeclaration syntax differs here [RedeclParamSyntaxDiffers] // CHECK:STDERR: fn Base.F[ref self: Base]() { // CHECK:STDERR: ^~~~ // CHECK:STDERR: fail_self_type.carbon:[[@LINE-6]]:18: note: comparing with previous declaration here [RedeclParamSyntaxPrevious] // CHECK:STDERR: fn F[ref self: Self](); // CHECK:STDERR: ^~~~ // CHECK:STDERR: fn Base.F[ref self: Base]() { self.a = (); } // CHECK:STDOUT: --- basic.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %C: type = class_type @C [concrete] // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %complete_type: = complete_type_witness %empty_struct_type [concrete] // CHECK:STDOUT: %type: type = facet_type [concrete] // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self] // CHECK:STDOUT: %pattern_type: type = pattern_type %C [concrete] // CHECK:STDOUT: %a: %C = symbolic_binding a, 0 [symbolic] // CHECK:STDOUT: %Foo.type: type = generic_class_type @Foo [concrete] // CHECK:STDOUT: %Foo.generic: %Foo.type = struct_value () [concrete] // CHECK:STDOUT: %Foo: type = class_type @Foo, @Foo(%a) [symbolic] // CHECK:STDOUT: %Bar.type: type = generic_class_type @Bar [concrete] // CHECK:STDOUT: %Bar.generic: %Bar.type = struct_value () [concrete] // CHECK:STDOUT: %Bar: type = class_type @Bar, @Bar(%a) [symbolic] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .C = %C.decl // CHECK:STDOUT: .D = %D // CHECK:STDOUT: .Foo = %Foo.decl.loc7 // CHECK:STDOUT: .Bar = %Bar.decl.loc10 // CHECK:STDOUT: } // CHECK:STDOUT: %C.decl: type = class_decl @C [concrete = constants.%C] {} {} // CHECK:STDOUT: %C.ref: type = name_ref C, %C.decl [concrete = constants.%C] // CHECK:STDOUT: %D: type = alias_binding D, %C.decl [concrete = constants.%C] // CHECK:STDOUT: %Foo.decl.loc7: %Foo.type = class_decl @Foo [concrete = constants.%Foo.generic] { // CHECK:STDOUT: %a.patt: %pattern_type = symbolic_binding_pattern a, 0 [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %.loc7: type = splice_block %C.ref.loc7 [concrete = constants.%C] { // CHECK:STDOUT: %.Self.2: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %C.ref.loc7: type = name_ref C, file.%C.decl [concrete = constants.%C] // CHECK:STDOUT: } // CHECK:STDOUT: %a.loc7_11.2: %C = symbolic_binding a, 0 [symbolic = %a.loc7_11.1 (constants.%a)] // CHECK:STDOUT: } // CHECK:STDOUT: %Foo.decl.loc8: %Foo.type = class_decl @Foo [concrete = constants.%Foo.generic] { // CHECK:STDOUT: %a.patt: %pattern_type = symbolic_binding_pattern a, 0 [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %.loc8: type = splice_block %C.ref.loc8 [concrete = constants.%C] { // CHECK:STDOUT: %.Self.1: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %C.ref.loc8: type = name_ref C, file.%C.decl [concrete = constants.%C] // CHECK:STDOUT: } // CHECK:STDOUT: %a.loc8: %C = symbolic_binding a, 0 [symbolic = %a.loc7_11.1 (constants.%a)] // CHECK:STDOUT: } // CHECK:STDOUT: %Bar.decl.loc10: %Bar.type = class_decl @Bar [concrete = constants.%Bar.generic] { // CHECK:STDOUT: %a.patt: %pattern_type = symbolic_binding_pattern a, 0 [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %.loc10: type = splice_block %D.ref.loc10 [concrete = constants.%C] { // CHECK:STDOUT: %.Self.2: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %D.ref.loc10: type = name_ref D, file.%D [concrete = constants.%C] // CHECK:STDOUT: } // CHECK:STDOUT: %a.loc10_11.2: %C = symbolic_binding a, 0 [symbolic = %a.loc10_11.1 (constants.%a)] // CHECK:STDOUT: } // CHECK:STDOUT: %Bar.decl.loc11: %Bar.type = class_decl @Bar [concrete = constants.%Bar.generic] { // CHECK:STDOUT: %a.patt: %pattern_type = symbolic_binding_pattern a, 0 [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %.loc11: type = splice_block %D.ref.loc11 [concrete = constants.%C] { // CHECK:STDOUT: %.Self.1: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %D.ref.loc11: type = name_ref D, file.%D [concrete = constants.%C] // CHECK:STDOUT: } // CHECK:STDOUT: %a.loc11: %C = symbolic_binding a, 0 [symbolic = %a.loc10_11.1 (constants.%a)] // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @C { // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%C // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic class @Foo(%a.loc7_11.2: %C) { // CHECK:STDOUT: %a.loc7_11.1: %C = symbolic_binding a, 0 [symbolic = %a.loc7_11.1 (constants.%a)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: // CHECK:STDOUT: class { // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%Foo // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic class @Bar(%a.loc10_11.2: %C) { // CHECK:STDOUT: %a.loc10_11.1: %C = symbolic_binding a, 0 [symbolic = %a.loc10_11.1 (constants.%a)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: // CHECK:STDOUT: class { // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%Bar // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @Foo(constants.%a) { // CHECK:STDOUT: %a.loc7_11.1 => constants.%a // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @Bar(constants.%a) { // CHECK:STDOUT: %a.loc10_11.1 => constants.%a // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- spacing.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %C: type = class_type @C [concrete] // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %complete_type: = complete_type_witness %empty_struct_type [concrete] // CHECK:STDOUT: %type: type = facet_type [concrete] // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self] // CHECK:STDOUT: %pattern_type: type = pattern_type %C [concrete] // CHECK:STDOUT: %a: %C = symbolic_binding a, 0 [symbolic] // CHECK:STDOUT: %Foo.type: type = generic_class_type @Foo [concrete] // CHECK:STDOUT: %Foo.generic: %Foo.type = struct_value () [concrete] // CHECK:STDOUT: %Foo: type = class_type @Foo, @Foo(%a) [symbolic] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .C = %C.decl // CHECK:STDOUT: .Foo = %Foo.decl.loc6 // CHECK:STDOUT: } // CHECK:STDOUT: %C.decl: type = class_decl @C [concrete = constants.%C] {} {} // CHECK:STDOUT: %Foo.decl.loc6: %Foo.type = class_decl @Foo [concrete = constants.%Foo.generic] { // CHECK:STDOUT: %a.patt: %pattern_type = symbolic_binding_pattern a, 0 [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %.loc6: type = splice_block %C.ref.loc6 [concrete = constants.%C] { // CHECK:STDOUT: %.Self.2: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %C.ref.loc6: type = name_ref C, file.%C.decl [concrete = constants.%C] // CHECK:STDOUT: } // CHECK:STDOUT: %a.loc6_17.2: %C = symbolic_binding a, 0 [symbolic = %a.loc6_17.1 (constants.%a)] // CHECK:STDOUT: } // CHECK:STDOUT: %Foo.decl.loc7: %Foo.type = class_decl @Foo [concrete = constants.%Foo.generic] { // CHECK:STDOUT: %a.patt: %pattern_type = symbolic_binding_pattern a, 0 [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %.loc7: type = splice_block %C.ref.loc7 [concrete = constants.%C] { // CHECK:STDOUT: %.Self.1: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %C.ref.loc7: type = name_ref C, file.%C.decl [concrete = constants.%C] // CHECK:STDOUT: } // CHECK:STDOUT: %a.loc7: %C = symbolic_binding a, 0 [symbolic = %a.loc6_17.1 (constants.%a)] // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @C { // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%C // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic class @Foo(%a.loc6_17.2: %C) { // CHECK:STDOUT: %a.loc6_17.1: %C = symbolic_binding a, 0 [symbolic = %a.loc6_17.1 (constants.%a)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: // CHECK:STDOUT: class { // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%Foo // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @Foo(constants.%a) { // CHECK:STDOUT: %a.loc6_17.1 => constants.%a // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- fail_parens.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %C: type = class_type @C [concrete] // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %complete_type: = complete_type_witness %empty_struct_type [concrete] // CHECK:STDOUT: %type: type = facet_type [concrete] // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self] // CHECK:STDOUT: %pattern_type: type = pattern_type %C [concrete] // CHECK:STDOUT: %a: %C = symbolic_binding a, 0 [symbolic] // CHECK:STDOUT: %Foo.type.337be0.1: type = generic_class_type @Foo.loc6 [concrete] // CHECK:STDOUT: %Foo.generic.6e878d.1: %Foo.type.337be0.1 = struct_value () [concrete] // CHECK:STDOUT: %Foo.type.337be0.2: type = generic_class_type @Foo.loc14 [concrete] // CHECK:STDOUT: %Foo.generic.6e878d.2: %Foo.type.337be0.2 = struct_value () [concrete] // CHECK:STDOUT: %Foo.3958a5.2: type = class_type @Foo.loc14, @Foo.loc14(%a) [symbolic] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .C = %C.decl // CHECK:STDOUT: .Foo = %Foo.decl.loc6 // CHECK:STDOUT: } // CHECK:STDOUT: %C.decl: type = class_decl @C [concrete = constants.%C] {} {} // CHECK:STDOUT: %Foo.decl.loc6: %Foo.type.337be0.1 = class_decl @Foo.loc6 [concrete = constants.%Foo.generic.6e878d.1] { // CHECK:STDOUT: %a.patt: %pattern_type = symbolic_binding_pattern a, 0 [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %.loc6: type = splice_block %C.ref [concrete = constants.%C] { // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %C.ref: type = name_ref C, file.%C.decl [concrete = constants.%C] // CHECK:STDOUT: } // CHECK:STDOUT: %a.loc6_11.2: %C = symbolic_binding a, 0 [symbolic = %a.loc6_11.1 (constants.%a)] // CHECK:STDOUT: } // CHECK:STDOUT: %Foo.decl.loc14: %Foo.type.337be0.2 = class_decl @Foo.loc14 [concrete = constants.%Foo.generic.6e878d.2] { // CHECK:STDOUT: %a.patt: %pattern_type = symbolic_binding_pattern a, 0 [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %.loc14: type = splice_block %C.ref [concrete = constants.%C] { // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %C.ref: type = name_ref C, file.%C.decl [concrete = constants.%C] // CHECK:STDOUT: } // CHECK:STDOUT: %a.loc14_11.2: %C = symbolic_binding a, 0 [symbolic = %a.loc14_11.1 (constants.%a)] // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @C { // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%C // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic class @Foo.loc6(%a.loc6_11.2: %C) { // CHECK:STDOUT: %a.loc6_11.1: %C = symbolic_binding a, 0 [symbolic = %a.loc6_11.1 (constants.%a)] // CHECK:STDOUT: // CHECK:STDOUT: class; // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic class @Foo.loc14(%a.loc14_11.2: %C) { // CHECK:STDOUT: %a.loc14_11.1: %C = symbolic_binding a, 0 [symbolic = %a.loc14_11.1 (constants.%a)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: // CHECK:STDOUT: class { // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%Foo.3958a5.2 // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @Foo.loc6(constants.%a) { // CHECK:STDOUT: %a.loc6_11.1 => constants.%a // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @Foo.loc14(constants.%a) { // CHECK:STDOUT: %a.loc14_11.1 => constants.%a // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- todo_fail_raw_identifier.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %C: type = class_type @C [concrete] // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %complete_type: = complete_type_witness %empty_struct_type [concrete] // CHECK:STDOUT: %type: type = facet_type [concrete] // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self] // CHECK:STDOUT: %pattern_type: type = pattern_type %C [concrete] // CHECK:STDOUT: %a: %C = symbolic_binding a, 0 [symbolic] // CHECK:STDOUT: %Foo.type: type = generic_class_type @Foo [concrete] // CHECK:STDOUT: %Foo.generic: %Foo.type = struct_value () [concrete] // CHECK:STDOUT: %Foo: type = class_type @Foo, @Foo(%a) [symbolic] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .C = %C.decl // CHECK:STDOUT: .Foo = %Foo.decl.loc6 // CHECK:STDOUT: } // CHECK:STDOUT: %C.decl: type = class_decl @C [concrete = constants.%C] {} {} // CHECK:STDOUT: %Foo.decl.loc6: %Foo.type = class_decl @Foo [concrete = constants.%Foo.generic] { // CHECK:STDOUT: %a.patt: %pattern_type = symbolic_binding_pattern a, 0 [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %.loc6: type = splice_block %C.ref.loc6 [concrete = constants.%C] { // CHECK:STDOUT: %.Self.2: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %C.ref.loc6: type = name_ref C, file.%C.decl [concrete = constants.%C] // CHECK:STDOUT: } // CHECK:STDOUT: %a.loc6_11.2: %C = symbolic_binding a, 0 [symbolic = %a.loc6_11.1 (constants.%a)] // CHECK:STDOUT: } // CHECK:STDOUT: %Foo.decl.loc7: %Foo.type = class_decl @Foo [concrete = constants.%Foo.generic] { // CHECK:STDOUT: %a.patt: %pattern_type = symbolic_binding_pattern a, 0 [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %.loc7: type = splice_block %C.ref.loc7 [concrete = constants.%C] { // CHECK:STDOUT: %.Self.1: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %C.ref.loc7: type = name_ref C, file.%C.decl [concrete = constants.%C] // CHECK:STDOUT: } // CHECK:STDOUT: %a.loc7: %C = symbolic_binding a, 0 [symbolic = %a.loc6_11.1 (constants.%a)] // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @C { // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%C // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic class @Foo(%a.loc6_11.2: %C) { // CHECK:STDOUT: %a.loc6_11.1: %C = symbolic_binding a, 0 [symbolic = %a.loc6_11.1 (constants.%a)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: // CHECK:STDOUT: class { // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%Foo // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @Foo(constants.%a) { // CHECK:STDOUT: %a.loc6_11.1 => constants.%a // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- two_file.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %C: type = class_type @C [concrete] // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %complete_type: = complete_type_witness %empty_struct_type [concrete] // CHECK:STDOUT: %type: type = facet_type [concrete] // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self] // CHECK:STDOUT: %pattern_type: type = pattern_type %C [concrete] // CHECK:STDOUT: %a: %C = symbolic_binding a, 0 [symbolic] // CHECK:STDOUT: %Foo.type: type = generic_class_type @Foo [concrete] // CHECK:STDOUT: %Foo.generic: %Foo.type = struct_value () [concrete] // CHECK:STDOUT: %Bar.type: type = generic_class_type @Bar [concrete] // CHECK:STDOUT: %Bar.generic: %Bar.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .C = %C.decl // CHECK:STDOUT: .D = %D // CHECK:STDOUT: .Foo = %Foo.decl // CHECK:STDOUT: .Bar = %Bar.decl // CHECK:STDOUT: } // CHECK:STDOUT: %C.decl: type = class_decl @C [concrete = constants.%C] {} {} // CHECK:STDOUT: %C.ref: type = name_ref C, %C.decl [concrete = constants.%C] // CHECK:STDOUT: %D: type = alias_binding D, %C.decl [concrete = constants.%C] // CHECK:STDOUT: %Foo.decl: %Foo.type = class_decl @Foo [concrete = constants.%Foo.generic] { // CHECK:STDOUT: %a.patt: %pattern_type = symbolic_binding_pattern a, 0 [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %.loc7: type = splice_block %C.ref [concrete = constants.%C] { // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %C.ref: type = name_ref C, file.%C.decl [concrete = constants.%C] // CHECK:STDOUT: } // CHECK:STDOUT: %a.loc7_11.2: %C = symbolic_binding a, 0 [symbolic = %a.loc7_11.1 (constants.%a)] // CHECK:STDOUT: } // CHECK:STDOUT: %Bar.decl: %Bar.type = class_decl @Bar [concrete = constants.%Bar.generic] { // CHECK:STDOUT: %a.patt: %pattern_type = symbolic_binding_pattern a, 0 [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %.loc8: type = splice_block %D.ref [concrete = constants.%C] { // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %D.ref: type = name_ref D, file.%D [concrete = constants.%C] // CHECK:STDOUT: } // CHECK:STDOUT: %a.loc8_11.2: %C = symbolic_binding a, 0 [symbolic = %a.loc8_11.1 (constants.%a)] // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @C { // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%C // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic class @Foo(%a.loc7_11.2: %C) { // CHECK:STDOUT: %a.loc7_11.1: %C = symbolic_binding a, 0 [symbolic = %a.loc7_11.1 (constants.%a)] // CHECK:STDOUT: // CHECK:STDOUT: class; // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic class @Bar(%a.loc8_11.2: %C) { // CHECK:STDOUT: %a.loc8_11.1: %C = symbolic_binding a, 0 [symbolic = %a.loc8_11.1 (constants.%a)] // CHECK:STDOUT: // CHECK:STDOUT: class; // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @Foo(constants.%a) { // CHECK:STDOUT: %a.loc7_11.1 => constants.%a // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @Bar(constants.%a) { // CHECK:STDOUT: %a.loc8_11.1 => constants.%a // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- two_file.impl.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %type: type = facet_type [concrete] // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self] // CHECK:STDOUT: %C: type = class_type @C [concrete] // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %complete_type: = complete_type_witness %empty_struct_type [concrete] // CHECK:STDOUT: %pattern_type: type = pattern_type %C [concrete] // CHECK:STDOUT: %a: %C = symbolic_binding a, 0 [symbolic] // CHECK:STDOUT: %Foo.type: type = generic_class_type @Foo [concrete] // CHECK:STDOUT: %Foo.generic: %Foo.type = struct_value () [concrete] // CHECK:STDOUT: %Foo: type = class_type @Foo, @Foo(%a) [symbolic] // CHECK:STDOUT: %Bar.type: type = generic_class_type @Bar [concrete] // CHECK:STDOUT: %Bar.generic: %Bar.type = struct_value () [concrete] // CHECK:STDOUT: %Bar: type = class_type @Bar, @Bar(%a) [symbolic] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Main.C: type = import_ref Main//two_file, C, loaded [concrete = constants.%C] // CHECK:STDOUT: %Main.D: type = import_ref Main//two_file, D, loaded [concrete = constants.%C] // CHECK:STDOUT: %Main.import_ref.8f2: = import_ref Main//two_file, loc4_10, loaded [concrete = constants.%complete_type] // CHECK:STDOUT: %Main.import_ref.743 = import_ref Main//two_file, inst{{[0-9A-F]+}} [no loc], unloaded // CHECK:STDOUT: %Main.import_ref.a7fd84.1: %C = import_ref Main//two_file, loc7_11, loaded [symbolic = @Foo.%a.1 (constants.%a)] // CHECK:STDOUT: %Main.import_ref.a7fd84.2: %C = import_ref Main//two_file, loc8_11, loaded [symbolic = @Bar.%a.1 (constants.%a)] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .C = imports.%Main.C // CHECK:STDOUT: .D = imports.%Main.D // CHECK:STDOUT: .Foo = %Foo.decl // CHECK:STDOUT: .Bar = %Bar.decl // CHECK:STDOUT: } // CHECK:STDOUT: %default.import.loc2_24.1 = import // CHECK:STDOUT: %default.import.loc2_24.2 = import // CHECK:STDOUT: %Foo.decl: %Foo.type = class_decl @Foo [concrete = constants.%Foo.generic] { // CHECK:STDOUT: %a.patt: %pattern_type = symbolic_binding_pattern a, 0 [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %.loc4: type = splice_block %C.ref [concrete = constants.%C] { // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %C.ref: type = name_ref C, imports.%Main.C [concrete = constants.%C] // CHECK:STDOUT: } // CHECK:STDOUT: %a.loc4: %C = symbolic_binding a, 0 [symbolic = %a.1 (constants.%a)] // CHECK:STDOUT: } // CHECK:STDOUT: %Bar.decl: %Bar.type = class_decl @Bar [concrete = constants.%Bar.generic] { // CHECK:STDOUT: %a.patt: %pattern_type = symbolic_binding_pattern a, 0 [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %.loc5: type = splice_block %D.ref [concrete = constants.%C] { // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %D.ref: type = name_ref D, imports.%Main.D [concrete = constants.%C] // CHECK:STDOUT: } // CHECK:STDOUT: %a.loc5: %C = symbolic_binding a, 0 [symbolic = %a.1 (constants.%a)] // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @C [from "two_file.carbon"] { // CHECK:STDOUT: complete_type_witness = imports.%Main.import_ref.8f2 // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = imports.%Main.import_ref.743 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic class @Foo(imports.%Main.import_ref.a7fd84.1: %C) { // CHECK:STDOUT: %a.1: %C = symbolic_binding a, 0 [symbolic = %a.1 (constants.%a)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: // CHECK:STDOUT: class { // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%Foo // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic class @Bar(imports.%Main.import_ref.a7fd84.2: %C) { // CHECK:STDOUT: %a.1: %C = symbolic_binding a, 0 [symbolic = %a.1 (constants.%a)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: // CHECK:STDOUT: class { // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%Bar // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @Foo(constants.%a) { // CHECK:STDOUT: %a.1 => constants.%a // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @Bar(constants.%a) { // CHECK:STDOUT: %a.1 => constants.%a // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- fail_name_mismatch.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %C: type = class_type @C [concrete] // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %complete_type: = complete_type_witness %empty_struct_type [concrete] // CHECK:STDOUT: %type: type = facet_type [concrete] // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self] // CHECK:STDOUT: %pattern_type: type = pattern_type %C [concrete] // CHECK:STDOUT: %a: %C = symbolic_binding a, 0 [symbolic] // CHECK:STDOUT: %Foo.type.337be0.1: type = generic_class_type @Foo.loc7 [concrete] // CHECK:STDOUT: %Foo.generic.6e878d.1: %Foo.type.337be0.1 = struct_value () [concrete] // CHECK:STDOUT: %b: %C = symbolic_binding b, 0 [symbolic] // CHECK:STDOUT: %Foo.type.337be0.2: type = generic_class_type @Foo.loc15 [concrete] // CHECK:STDOUT: %Foo.generic.6e878d.2: %Foo.type.337be0.2 = struct_value () [concrete] // CHECK:STDOUT: %Foo.3958a5.2: type = class_type @Foo.loc15, @Foo.loc15(%b) [symbolic] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .C = %C.decl // CHECK:STDOUT: .D = %D // CHECK:STDOUT: .Foo = %Foo.decl.loc7 // CHECK:STDOUT: } // CHECK:STDOUT: %C.decl: type = class_decl @C [concrete = constants.%C] {} {} // CHECK:STDOUT: %C.ref: type = name_ref C, %C.decl [concrete = constants.%C] // CHECK:STDOUT: %D: type = alias_binding D, %C.decl [concrete = constants.%C] // CHECK:STDOUT: %Foo.decl.loc7: %Foo.type.337be0.1 = class_decl @Foo.loc7 [concrete = constants.%Foo.generic.6e878d.1] { // CHECK:STDOUT: %a.patt: %pattern_type = symbolic_binding_pattern a, 0 [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %.loc7: type = splice_block %C.ref [concrete = constants.%C] { // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %C.ref: type = name_ref C, file.%C.decl [concrete = constants.%C] // CHECK:STDOUT: } // CHECK:STDOUT: %a.loc7_11.2: %C = symbolic_binding a, 0 [symbolic = %a.loc7_11.1 (constants.%a)] // CHECK:STDOUT: } // CHECK:STDOUT: %Foo.decl.loc15: %Foo.type.337be0.2 = class_decl @Foo.loc15 [concrete = constants.%Foo.generic.6e878d.2] { // CHECK:STDOUT: %b.patt: %pattern_type = symbolic_binding_pattern b, 0 [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %.loc15: type = splice_block %D.ref [concrete = constants.%C] { // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %D.ref: type = name_ref D, file.%D [concrete = constants.%C] // CHECK:STDOUT: } // CHECK:STDOUT: %b.loc15_11.2: %C = symbolic_binding b, 0 [symbolic = %b.loc15_11.1 (constants.%b)] // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @C { // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%C // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic class @Foo.loc7(%a.loc7_11.2: %C) { // CHECK:STDOUT: %a.loc7_11.1: %C = symbolic_binding a, 0 [symbolic = %a.loc7_11.1 (constants.%a)] // CHECK:STDOUT: // CHECK:STDOUT: class; // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic class @Foo.loc15(%b.loc15_11.2: %C) { // CHECK:STDOUT: %b.loc15_11.1: %C = symbolic_binding b, 0 [symbolic = %b.loc15_11.1 (constants.%b)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: // CHECK:STDOUT: class { // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%Foo.3958a5.2 // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @Foo.loc7(constants.%a) { // CHECK:STDOUT: %a.loc7_11.1 => constants.%a // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @Foo.loc15(constants.%b) { // CHECK:STDOUT: %b.loc15_11.1 => constants.%b // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- fail_alias.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %C: type = class_type @C [concrete] // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %complete_type: = complete_type_witness %empty_struct_type [concrete] // CHECK:STDOUT: %type: type = facet_type [concrete] // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self] // CHECK:STDOUT: %pattern_type: type = pattern_type %C [concrete] // CHECK:STDOUT: %a: %C = symbolic_binding a, 0 [symbolic] // CHECK:STDOUT: %Foo.type.337be0.1: type = generic_class_type @Foo.loc7 [concrete] // CHECK:STDOUT: %Foo.generic.6e878d.1: %Foo.type.337be0.1 = struct_value () [concrete] // CHECK:STDOUT: %Foo.type.337be0.2: type = generic_class_type @Foo.loc15 [concrete] // CHECK:STDOUT: %Foo.generic.6e878d.2: %Foo.type.337be0.2 = struct_value () [concrete] // CHECK:STDOUT: %Foo.3958a5.2: type = class_type @Foo.loc15, @Foo.loc15(%a) [symbolic] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .C = %C.decl // CHECK:STDOUT: .D = %D // CHECK:STDOUT: .Foo = %Foo.decl.loc7 // CHECK:STDOUT: } // CHECK:STDOUT: %C.decl: type = class_decl @C [concrete = constants.%C] {} {} // CHECK:STDOUT: %C.ref: type = name_ref C, %C.decl [concrete = constants.%C] // CHECK:STDOUT: %D: type = alias_binding D, %C.decl [concrete = constants.%C] // CHECK:STDOUT: %Foo.decl.loc7: %Foo.type.337be0.1 = class_decl @Foo.loc7 [concrete = constants.%Foo.generic.6e878d.1] { // CHECK:STDOUT: %a.patt: %pattern_type = symbolic_binding_pattern a, 0 [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %.loc7: type = splice_block %C.ref [concrete = constants.%C] { // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %C.ref: type = name_ref C, file.%C.decl [concrete = constants.%C] // CHECK:STDOUT: } // CHECK:STDOUT: %a.loc7_11.2: %C = symbolic_binding a, 0 [symbolic = %a.loc7_11.1 (constants.%a)] // CHECK:STDOUT: } // CHECK:STDOUT: %Foo.decl.loc15: %Foo.type.337be0.2 = class_decl @Foo.loc15 [concrete = constants.%Foo.generic.6e878d.2] { // CHECK:STDOUT: %a.patt: %pattern_type = symbolic_binding_pattern a, 0 [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %.loc15: type = splice_block %D.ref [concrete = constants.%C] { // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %D.ref: type = name_ref D, file.%D [concrete = constants.%C] // CHECK:STDOUT: } // CHECK:STDOUT: %a.loc15_11.2: %C = symbolic_binding a, 0 [symbolic = %a.loc15_11.1 (constants.%a)] // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @C { // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%C // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic class @Foo.loc7(%a.loc7_11.2: %C) { // CHECK:STDOUT: %a.loc7_11.1: %C = symbolic_binding a, 0 [symbolic = %a.loc7_11.1 (constants.%a)] // CHECK:STDOUT: // CHECK:STDOUT: class; // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic class @Foo.loc15(%a.loc15_11.2: %C) { // CHECK:STDOUT: %a.loc15_11.1: %C = symbolic_binding a, 0 [symbolic = %a.loc15_11.1 (constants.%a)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: // CHECK:STDOUT: class { // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%Foo.3958a5.2 // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @Foo.loc7(constants.%a) { // CHECK:STDOUT: %a.loc7_11.1 => constants.%a // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @Foo.loc15(constants.%a) { // CHECK:STDOUT: %a.loc15_11.1 => constants.%a // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- fail_deduced_alias.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %C: type = class_type @C [concrete] // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %complete_type: = complete_type_witness %empty_struct_type [concrete] // CHECK:STDOUT: %type: type = facet_type [concrete] // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self] // CHECK:STDOUT: %pattern_type: type = pattern_type %C [concrete] // CHECK:STDOUT: %a: %C = symbolic_binding a, 0 [symbolic] // CHECK:STDOUT: %Foo.type.337be0.1: type = generic_class_type @Foo.loc7 [concrete] // CHECK:STDOUT: %Foo.generic.6e878d.1: %Foo.type.337be0.1 = struct_value () [concrete] // CHECK:STDOUT: %Foo.type.337be0.2: type = generic_class_type @Foo.loc15 [concrete] // CHECK:STDOUT: %Foo.generic.6e878d.2: %Foo.type.337be0.2 = struct_value () [concrete] // CHECK:STDOUT: %Foo.3958a5.2: type = class_type @Foo.loc15, @Foo.loc15(%a) [symbolic] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .C = %C.decl // CHECK:STDOUT: .D = %D // CHECK:STDOUT: .Foo = %Foo.decl.loc7 // CHECK:STDOUT: } // CHECK:STDOUT: %C.decl: type = class_decl @C [concrete = constants.%C] {} {} // CHECK:STDOUT: %C.ref: type = name_ref C, %C.decl [concrete = constants.%C] // CHECK:STDOUT: %D: type = alias_binding D, %C.decl [concrete = constants.%C] // CHECK:STDOUT: %Foo.decl.loc7: %Foo.type.337be0.1 = class_decl @Foo.loc7 [concrete = constants.%Foo.generic.6e878d.1] { // CHECK:STDOUT: %a.patt: %pattern_type = symbolic_binding_pattern a, 0 [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %.loc7: type = splice_block %C.ref [concrete = constants.%C] { // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %C.ref: type = name_ref C, file.%C.decl [concrete = constants.%C] // CHECK:STDOUT: } // CHECK:STDOUT: %a.loc7_11.2: %C = symbolic_binding a, 0 [symbolic = %a.loc7_11.1 (constants.%a)] // CHECK:STDOUT: } // CHECK:STDOUT: %Foo.decl.loc15: %Foo.type.337be0.2 = class_decl @Foo.loc15 [concrete = constants.%Foo.generic.6e878d.2] { // CHECK:STDOUT: %a.patt: %pattern_type = symbolic_binding_pattern a, 0 [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %.loc15: type = splice_block %D.ref [concrete = constants.%C] { // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %D.ref: type = name_ref D, file.%D [concrete = constants.%C] // CHECK:STDOUT: } // CHECK:STDOUT: %a.loc15_11.2: %C = symbolic_binding a, 0 [symbolic = %a.loc15_11.1 (constants.%a)] // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @C { // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%C // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic class @Foo.loc7(%a.loc7_11.2: %C) { // CHECK:STDOUT: %a.loc7_11.1: %C = symbolic_binding a, 0 [symbolic = %a.loc7_11.1 (constants.%a)] // CHECK:STDOUT: // CHECK:STDOUT: class; // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic class @Foo.loc15(%a.loc15_11.2: %C) { // CHECK:STDOUT: %a.loc15_11.1: %C = symbolic_binding a, 0 [symbolic = %a.loc15_11.1 (constants.%a)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: // CHECK:STDOUT: class { // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%Foo.3958a5.2 // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @Foo.loc7(constants.%a) { // CHECK:STDOUT: %a.loc7_11.1 => constants.%a // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @Foo.loc15(constants.%a) { // CHECK:STDOUT: %a.loc15_11.1 => constants.%a // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- alias_two_file.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %C: type = class_type @C [concrete] // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %complete_type: = complete_type_witness %empty_struct_type [concrete] // CHECK:STDOUT: %type: type = facet_type [concrete] // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self] // CHECK:STDOUT: %pattern_type: type = pattern_type %C [concrete] // CHECK:STDOUT: %a: %C = symbolic_binding a, 0 [symbolic] // CHECK:STDOUT: %Foo.type: type = generic_class_type @Foo [concrete] // CHECK:STDOUT: %Foo.generic: %Foo.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .C = %C.decl // CHECK:STDOUT: .Foo = %Foo.decl // CHECK:STDOUT: } // CHECK:STDOUT: %C.decl: type = class_decl @C [concrete = constants.%C] {} {} // CHECK:STDOUT: %Foo.decl: %Foo.type = class_decl @Foo [concrete = constants.%Foo.generic] { // CHECK:STDOUT: %a.patt: %pattern_type = symbolic_binding_pattern a, 0 [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %.loc6: type = splice_block %C.ref [concrete = constants.%C] { // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %C.ref: type = name_ref C, file.%C.decl [concrete = constants.%C] // CHECK:STDOUT: } // CHECK:STDOUT: %a.loc6_11.2: %C = symbolic_binding a, 0 [symbolic = %a.loc6_11.1 (constants.%a)] // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @C { // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%C // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic class @Foo(%a.loc6_11.2: %C) { // CHECK:STDOUT: %a.loc6_11.1: %C = symbolic_binding a, 0 [symbolic = %a.loc6_11.1 (constants.%a)] // CHECK:STDOUT: // CHECK:STDOUT: class; // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @Foo(constants.%a) { // CHECK:STDOUT: %a.loc6_11.1 => constants.%a // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- todo_fail_alias_two_file.impl.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %C: type = class_type @C [concrete] // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %complete_type: = complete_type_witness %empty_struct_type [concrete] // CHECK:STDOUT: %type: type = facet_type [concrete] // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self] // CHECK:STDOUT: %pattern_type: type = pattern_type %C [concrete] // CHECK:STDOUT: %a: %C = symbolic_binding a, 0 [symbolic] // CHECK:STDOUT: %Foo.type: type = generic_class_type @Foo [concrete] // CHECK:STDOUT: %Foo.generic: %Foo.type = struct_value () [concrete] // CHECK:STDOUT: %Foo: type = class_type @Foo, @Foo(%a) [symbolic] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Main.C: type = import_ref Main//alias_two_file, C, loaded [concrete = constants.%C] // CHECK:STDOUT: %Main.import_ref.8f2: = import_ref Main//alias_two_file, loc4_10, loaded [concrete = constants.%complete_type] // CHECK:STDOUT: %Main.import_ref.743 = import_ref Main//alias_two_file, inst{{[0-9A-F]+}} [no loc], unloaded // CHECK:STDOUT: %Main.import_ref.a7f: %C = import_ref Main//alias_two_file, loc6_11, loaded [symbolic = @Foo.%a.1 (constants.%a)] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .C = imports.%Main.C // CHECK:STDOUT: .Foo = %Foo.decl // CHECK:STDOUT: .D = %D // CHECK:STDOUT: } // CHECK:STDOUT: %default.import.loc2_30.1 = import // CHECK:STDOUT: %default.import.loc2_30.2 = import // CHECK:STDOUT: %C.ref: type = name_ref C, imports.%Main.C [concrete = constants.%C] // CHECK:STDOUT: %D: type = alias_binding D, imports.%Main.C [concrete = constants.%C] // CHECK:STDOUT: %Foo.decl: %Foo.type = class_decl @Foo [concrete = constants.%Foo.generic] { // CHECK:STDOUT: %a.patt: %pattern_type = symbolic_binding_pattern a, 0 [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %.loc6: type = splice_block %D.ref [concrete = constants.%C] { // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %D.ref: type = name_ref D, file.%D [concrete = constants.%C] // CHECK:STDOUT: } // CHECK:STDOUT: %a.loc6: %C = symbolic_binding a, 0 [symbolic = %a.1 (constants.%a)] // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @C [from "alias_two_file.carbon"] { // CHECK:STDOUT: complete_type_witness = imports.%Main.import_ref.8f2 // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = imports.%Main.import_ref.743 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic class @Foo(imports.%Main.import_ref.a7f: %C) { // CHECK:STDOUT: %a.1: %C = symbolic_binding a, 0 [symbolic = %a.1 (constants.%a)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: // CHECK:STDOUT: class { // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%Foo // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @Foo(constants.%a) { // CHECK:STDOUT: %a.1 => constants.%a // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- fail_repeat_const.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %C: type = class_type @C [concrete] // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %complete_type: = complete_type_witness %empty_struct_type [concrete] // CHECK:STDOUT: %type: type = facet_type [concrete] // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self] // CHECK:STDOUT: %const: type = const_type %C [concrete] // CHECK:STDOUT: %pattern_type: type = pattern_type %const [concrete] // CHECK:STDOUT: %a: %const = symbolic_binding a, 0 [symbolic] // CHECK:STDOUT: %Foo.type.337be0.1: type = generic_class_type @Foo.loc6 [concrete] // CHECK:STDOUT: %Foo.generic.6e878d.1: %Foo.type.337be0.1 = struct_value () [concrete] // CHECK:STDOUT: %Foo.type.337be0.2: type = generic_class_type @Foo.loc18 [concrete] // CHECK:STDOUT: %Foo.generic.6e878d.2: %Foo.type.337be0.2 = struct_value () [concrete] // CHECK:STDOUT: %Foo.e46e8f.2: type = class_type @Foo.loc18, @Foo.loc18(%a) [symbolic] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .C = %C.decl // CHECK:STDOUT: .Foo = %Foo.decl.loc6 // CHECK:STDOUT: } // CHECK:STDOUT: %C.decl: type = class_decl @C [concrete = constants.%C] {} {} // CHECK:STDOUT: %Foo.decl.loc6: %Foo.type.337be0.1 = class_decl @Foo.loc6 [concrete = constants.%Foo.generic.6e878d.1] { // CHECK:STDOUT: %a.patt: %pattern_type = symbolic_binding_pattern a, 0 [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %.loc6: type = splice_block %const [concrete = constants.%const] { // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %C.ref: type = name_ref C, file.%C.decl [concrete = constants.%C] // CHECK:STDOUT: %const: type = const_type %C.ref [concrete = constants.%const] // CHECK:STDOUT: } // CHECK:STDOUT: %a.loc6_11.2: %const = symbolic_binding a, 0 [symbolic = %a.loc6_11.1 (constants.%a)] // CHECK:STDOUT: } // CHECK:STDOUT: %Foo.decl.loc18: %Foo.type.337be0.2 = class_decl @Foo.loc18 [concrete = constants.%Foo.generic.6e878d.2] { // CHECK:STDOUT: %a.patt: %pattern_type = symbolic_binding_pattern a, 0 [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %.loc18: type = splice_block %const.loc18_15 [concrete = constants.%const] { // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %C.ref: type = name_ref C, file.%C.decl [concrete = constants.%C] // CHECK:STDOUT: %const.loc18_22: type = const_type %C.ref [concrete = constants.%const] // CHECK:STDOUT: %const.loc18_15: type = const_type %const.loc18_22 [concrete = constants.%const] // CHECK:STDOUT: } // CHECK:STDOUT: %a.loc18_11.2: %const = symbolic_binding a, 0 [symbolic = %a.loc18_11.1 (constants.%a)] // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @C { // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%C // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic class @Foo.loc6(%a.loc6_11.2: %const) { // CHECK:STDOUT: %a.loc6_11.1: %const = symbolic_binding a, 0 [symbolic = %a.loc6_11.1 (constants.%a)] // CHECK:STDOUT: // CHECK:STDOUT: class; // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic class @Foo.loc18(%a.loc18_11.2: %const) { // CHECK:STDOUT: %a.loc18_11.1: %const = symbolic_binding a, 0 [symbolic = %a.loc18_11.1 (constants.%a)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: // CHECK:STDOUT: class { // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%Foo.e46e8f.2 // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @Foo.loc6(constants.%a) { // CHECK:STDOUT: %a.loc6_11.1 => constants.%a // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @Foo.loc18(constants.%a) { // CHECK:STDOUT: %a.loc18_11.1 => constants.%a // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- fail_self_type.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %Base: type = class_type @Base [concrete] // CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] // CHECK:STDOUT: %empty_tuple: %empty_tuple.type = tuple_value () [concrete] // CHECK:STDOUT: %Base.elem: type = unbound_element_type %Base, %empty_tuple.type [concrete] // CHECK:STDOUT: %pattern_type: type = pattern_type %Base [concrete] // CHECK:STDOUT: %Base.F.type.c66019.1: type = fn_type @Base.F.loc7 [concrete] // CHECK:STDOUT: %Base.F.811e28.1: %Base.F.type.c66019.1 = struct_value () [concrete] // CHECK:STDOUT: %struct_type.a: type = struct_type {.a: %empty_tuple.type} [concrete] // CHECK:STDOUT: %complete_type: = complete_type_witness %struct_type.a [concrete] // CHECK:STDOUT: %Base.F.type.c66019.2: type = fn_type @Base.F.loc17 [concrete] // CHECK:STDOUT: %Base.F.811e28.2: %Base.F.type.c66019.2 = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Base = %Base.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Base.decl: type = class_decl @Base [concrete = constants.%Base] {} {} // CHECK:STDOUT: %Base.F.decl: %Base.F.type.c66019.2 = fn_decl @Base.F.loc17 [concrete = constants.%Base.F.811e28.2] { // CHECK:STDOUT: %self.patt: %pattern_type = ref_binding_pattern self [concrete] // CHECK:STDOUT: %self.param_patt: %pattern_type = ref_param_pattern %self.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %self.param: ref %Base = ref_param call_param0 // CHECK:STDOUT: %Base.ref: type = name_ref Base, file.%Base.decl [concrete = constants.%Base] // CHECK:STDOUT: %self: ref %Base = ref_binding self, %self.param // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @Base { // CHECK:STDOUT: %.loc5_11.1: %empty_tuple.type = tuple_literal () [concrete = constants.%empty_tuple] // CHECK:STDOUT: %.loc5_11.2: type = converted %.loc5_11.1, constants.%empty_tuple.type [concrete = constants.%empty_tuple.type] // CHECK:STDOUT: %.loc5_8: %Base.elem = field_decl a, element0 [concrete] // CHECK:STDOUT: %Base.F.decl: %Base.F.type.c66019.1 = fn_decl @Base.F.loc7 [concrete = constants.%Base.F.811e28.1] { // CHECK:STDOUT: %self.patt: %pattern_type = ref_binding_pattern self [concrete] // CHECK:STDOUT: %self.param_patt: %pattern_type = ref_param_pattern %self.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %self.param: ref %Base = ref_param call_param0 // CHECK:STDOUT: %Self.ref: type = name_ref Self, constants.%Base [concrete = constants.%Base] // CHECK:STDOUT: %self: ref %Base = ref_binding self, %self.param // CHECK:STDOUT: } // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%struct_type.a [concrete = constants.%complete_type] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%Base // CHECK:STDOUT: .a = %.loc5_8 // CHECK:STDOUT: .F = %Base.F.decl // CHECK:STDOUT: .Base = // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @Base.F.loc7(%self.param: ref %Base); // CHECK:STDOUT: // CHECK:STDOUT: fn @Base.F.loc17(%self.param: ref %Base) { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %self.ref: ref %Base = name_ref self, %self // CHECK:STDOUT: %a.ref: %Base.elem = name_ref a, @Base.%.loc5_8 [concrete = @Base.%.loc5_8] // CHECK:STDOUT: %.loc18_7: ref %empty_tuple.type = class_element_access %self.ref, element0 // CHECK:STDOUT: %.loc18_13.1: %empty_tuple.type = tuple_literal () [concrete = constants.%empty_tuple] // CHECK:STDOUT: %.loc18_13.2: init %empty_tuple.type = tuple_init () [concrete = constants.%empty_tuple] // CHECK:STDOUT: %.loc18_10: init %empty_tuple.type = converted %.loc18_13.1, %.loc18_13.2 [concrete = constants.%empty_tuple] // CHECK:STDOUT: assign %.loc18_7, %.loc18_10 // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/class/syntactic_merge_literal.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/int.carbon // TODO: Add ranges and switch to "--dump-sem-ir-ranges=only". // EXTRA-ARGS: --dump-sem-ir-ranges=if-present // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/class/syntactic_merge_literal.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/class/syntactic_merge_literal.carbon // --- int_match.carbon library "[[@TEST_NAME]]"; class C(a:! i32) {} class D(b:! C(1_000)); class D(b:! C(1_000)) {} // --- fail_int_mismatch.carbon library "[[@TEST_NAME]]"; class C(a:! i32) {} class D(b:! C(1000)); // CHECK:STDERR: fail_int_mismatch.carbon:[[@LINE+7]]:15: error: redeclaration syntax differs here [RedeclParamSyntaxDiffers] // CHECK:STDERR: class D(b:! C(1_000)) {} // CHECK:STDERR: ^~~~~ // CHECK:STDERR: fail_int_mismatch.carbon:[[@LINE-4]]:15: note: comparing with previous declaration here [RedeclParamSyntaxPrevious] // CHECK:STDERR: class D(b:! C(1000)); // CHECK:STDERR: ^~~~ // CHECK:STDERR: class D(b:! C(1_000)) {} // CHECK:STDOUT: --- int_match.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %type: type = facet_type [concrete] // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self] // CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [concrete] // CHECK:STDOUT: %Int.type: type = generic_class_type @Int [concrete] // CHECK:STDOUT: %Int.generic: %Int.type = struct_value () [concrete] // CHECK:STDOUT: %i32: type = class_type @Int, @Int(%int_32) [concrete] // CHECK:STDOUT: %pattern_type.7ce: type = pattern_type %i32 [concrete] // CHECK:STDOUT: %a: %i32 = symbolic_binding a, 0 [symbolic] // CHECK:STDOUT: %C.type: type = generic_class_type @C [concrete] // CHECK:STDOUT: %C.generic: %C.type = struct_value () [concrete] // CHECK:STDOUT: %C.ad6: type = class_type @C, @C(%a) [symbolic] // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %complete_type.357: = complete_type_witness %empty_struct_type [concrete] // CHECK:STDOUT: %int_1000.ff9: Core.IntLiteral = int_value 1000 [concrete] // CHECK:STDOUT: %ImplicitAs.type.cc7: type = generic_interface_type @ImplicitAs [concrete] // CHECK:STDOUT: %ImplicitAs.generic: %ImplicitAs.type.cc7 = struct_value () [concrete] // CHECK:STDOUT: %ImplicitAs.type.e8c: type = facet_type <@ImplicitAs, @ImplicitAs(%i32)> [concrete] // CHECK:STDOUT: %To: Core.IntLiteral = symbolic_binding To, 0 [symbolic] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.4e6: type = fn_type @Core.IntLiteral.as.ImplicitAs.impl.Convert, @Core.IntLiteral.as.ImplicitAs.impl(%To) [symbolic] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.3c2: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.4e6 = struct_value () [symbolic] // CHECK:STDOUT: %ImplicitAs.impl_witness.6bc: = impl_witness imports.%ImplicitAs.impl_witness_table.74f, @Core.IntLiteral.as.ImplicitAs.impl(%int_32) [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.e0d: type = fn_type @Core.IntLiteral.as.ImplicitAs.impl.Convert, @Core.IntLiteral.as.ImplicitAs.impl(%int_32) [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.e0d = struct_value () [concrete] // CHECK:STDOUT: %ImplicitAs.facet: %ImplicitAs.type.e8c = facet_value Core.IntLiteral, (%ImplicitAs.impl_witness.6bc) [concrete] // CHECK:STDOUT: %ImplicitAs.WithSelf.Convert.type.b37: type = fn_type @ImplicitAs.WithSelf.Convert, @ImplicitAs.WithSelf(%i32, %ImplicitAs.facet) [concrete] // CHECK:STDOUT: %.545: type = fn_type_with_self_type %ImplicitAs.WithSelf.Convert.type.b37, %ImplicitAs.facet [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.bound: = bound_method %int_1000.ff9, %Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5 [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn: = specific_function %Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5, @Core.IntLiteral.as.ImplicitAs.impl.Convert(%int_32) [concrete] // CHECK:STDOUT: %bound_method: = bound_method %int_1000.ff9, %Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn [concrete] // CHECK:STDOUT: %int_1000.1b6: %i32 = int_value 1000 [concrete] // CHECK:STDOUT: %C.a39: type = class_type @C, @C(%int_1000.1b6) [concrete] // CHECK:STDOUT: %pattern_type.f44: type = pattern_type %C.a39 [concrete] // CHECK:STDOUT: %b: %C.a39 = symbolic_binding b, 0 [symbolic] // CHECK:STDOUT: %D.type: type = generic_class_type @D [concrete] // CHECK:STDOUT: %D.generic: %D.type = struct_value () [concrete] // CHECK:STDOUT: %D: type = class_type @D, @D(%b) [symbolic] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: .Int = %Core.Int // CHECK:STDOUT: .ImplicitAs = %Core.ImplicitAs // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: %Core.Int: %Int.type = import_ref Core//prelude/parts/int, Int, loaded [concrete = constants.%Int.generic] // CHECK:STDOUT: %Core.ImplicitAs: %ImplicitAs.type.cc7 = import_ref Core//prelude/parts/as, ImplicitAs, loaded [concrete = constants.%ImplicitAs.generic] // CHECK:STDOUT: %Core.import_ref.42d: @Core.IntLiteral.as.ImplicitAs.impl.%Core.IntLiteral.as.ImplicitAs.impl.Convert.type (%Core.IntLiteral.as.ImplicitAs.impl.Convert.type.4e6) = import_ref Core//prelude/parts/int, loc{{\d+_\d+}}, loaded [symbolic = @Core.IntLiteral.as.ImplicitAs.impl.%Core.IntLiteral.as.ImplicitAs.impl.Convert (constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.3c2)] // CHECK:STDOUT: %ImplicitAs.impl_witness_table.74f = impl_witness_table (%Core.import_ref.42d), @Core.IntLiteral.as.ImplicitAs.impl [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .C = %C.decl // CHECK:STDOUT: .D = %D.decl.loc5 // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %C.decl: %C.type = class_decl @C [concrete = constants.%C.generic] { // CHECK:STDOUT: %a.patt: %pattern_type.7ce = symbolic_binding_pattern a, 0 [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %.loc4: type = splice_block %i32 [concrete = constants.%i32] { // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %i32: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: } // CHECK:STDOUT: %a.loc4_9.2: %i32 = symbolic_binding a, 0 [symbolic = %a.loc4_9.1 (constants.%a)] // CHECK:STDOUT: } // CHECK:STDOUT: %D.decl.loc5: %D.type = class_decl @D [concrete = constants.%D.generic] { // CHECK:STDOUT: %b.patt: %pattern_type.f44 = symbolic_binding_pattern b, 0 [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %.loc5_20.1: type = splice_block %C.loc5 [concrete = constants.%C.a39] { // CHECK:STDOUT: %.Self.2: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %C.ref.loc5: %C.type = name_ref C, file.%C.decl [concrete = constants.%C.generic] // CHECK:STDOUT: %int_1000.loc5: Core.IntLiteral = int_value 1000 [concrete = constants.%int_1000.ff9] // CHECK:STDOUT: %impl.elem0.loc5: %.545 = impl_witness_access constants.%ImplicitAs.impl_witness.6bc, element0 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5] // CHECK:STDOUT: %bound_method.loc5_20.1: = bound_method %int_1000.loc5, %impl.elem0.loc5 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.bound] // CHECK:STDOUT: %specific_fn.loc5: = specific_function %impl.elem0.loc5, @Core.IntLiteral.as.ImplicitAs.impl.Convert(constants.%int_32) [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn] // CHECK:STDOUT: %bound_method.loc5_20.2: = bound_method %int_1000.loc5, %specific_fn.loc5 [concrete = constants.%bound_method] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc5: init %i32 = call %bound_method.loc5_20.2(%int_1000.loc5) [concrete = constants.%int_1000.1b6] // CHECK:STDOUT: %.loc5_20.2: %i32 = value_of_initializer %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc5 [concrete = constants.%int_1000.1b6] // CHECK:STDOUT: %.loc5_20.3: %i32 = converted %int_1000.loc5, %.loc5_20.2 [concrete = constants.%int_1000.1b6] // CHECK:STDOUT: %C.loc5: type = class_type @C, @C(constants.%int_1000.1b6) [concrete = constants.%C.a39] // CHECK:STDOUT: } // CHECK:STDOUT: %b.loc5_9.2: %C.a39 = symbolic_binding b, 0 [symbolic = %b.loc5_9.1 (constants.%b)] // CHECK:STDOUT: } // CHECK:STDOUT: %D.decl.loc6: %D.type = class_decl @D [concrete = constants.%D.generic] { // CHECK:STDOUT: %b.patt: %pattern_type.f44 = symbolic_binding_pattern b, 0 [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %.loc6_20.1: type = splice_block %C.loc6 [concrete = constants.%C.a39] { // CHECK:STDOUT: %.Self.1: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %C.ref.loc6: %C.type = name_ref C, file.%C.decl [concrete = constants.%C.generic] // CHECK:STDOUT: %int_1000.loc6: Core.IntLiteral = int_value 1000 [concrete = constants.%int_1000.ff9] // CHECK:STDOUT: %impl.elem0.loc6: %.545 = impl_witness_access constants.%ImplicitAs.impl_witness.6bc, element0 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5] // CHECK:STDOUT: %bound_method.loc6_20.1: = bound_method %int_1000.loc6, %impl.elem0.loc6 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.bound] // CHECK:STDOUT: %specific_fn.loc6: = specific_function %impl.elem0.loc6, @Core.IntLiteral.as.ImplicitAs.impl.Convert(constants.%int_32) [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn] // CHECK:STDOUT: %bound_method.loc6_20.2: = bound_method %int_1000.loc6, %specific_fn.loc6 [concrete = constants.%bound_method] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc6: init %i32 = call %bound_method.loc6_20.2(%int_1000.loc6) [concrete = constants.%int_1000.1b6] // CHECK:STDOUT: %.loc6_20.2: %i32 = value_of_initializer %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc6 [concrete = constants.%int_1000.1b6] // CHECK:STDOUT: %.loc6_20.3: %i32 = converted %int_1000.loc6, %.loc6_20.2 [concrete = constants.%int_1000.1b6] // CHECK:STDOUT: %C.loc6: type = class_type @C, @C(constants.%int_1000.1b6) [concrete = constants.%C.a39] // CHECK:STDOUT: } // CHECK:STDOUT: %b.loc6: %C.a39 = symbolic_binding b, 0 [symbolic = %b.loc5_9.1 (constants.%b)] // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic class @C(%a.loc4_9.2: %i32) { // CHECK:STDOUT: %a.loc4_9.1: %i32 = symbolic_binding a, 0 [symbolic = %a.loc4_9.1 (constants.%a)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: // CHECK:STDOUT: class { // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type.357] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%C.ad6 // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic class @D(%b.loc5_9.2: %C.a39) { // CHECK:STDOUT: %b.loc5_9.1: %C.a39 = symbolic_binding b, 0 [symbolic = %b.loc5_9.1 (constants.%b)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: // CHECK:STDOUT: class { // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type.357] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%D // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @C(constants.%a) { // CHECK:STDOUT: %a.loc4_9.1 => constants.%a // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @C(constants.%int_1000.1b6) { // CHECK:STDOUT: %a.loc4_9.1 => constants.%int_1000.1b6 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @D(constants.%b) { // CHECK:STDOUT: %b.loc5_9.1 => constants.%b // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- fail_int_mismatch.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %type: type = facet_type [concrete] // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self] // CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [concrete] // CHECK:STDOUT: %Int.type: type = generic_class_type @Int [concrete] // CHECK:STDOUT: %Int.generic: %Int.type = struct_value () [concrete] // CHECK:STDOUT: %i32: type = class_type @Int, @Int(%int_32) [concrete] // CHECK:STDOUT: %pattern_type.7ce: type = pattern_type %i32 [concrete] // CHECK:STDOUT: %a: %i32 = symbolic_binding a, 0 [symbolic] // CHECK:STDOUT: %C.type: type = generic_class_type @C [concrete] // CHECK:STDOUT: %C.generic: %C.type = struct_value () [concrete] // CHECK:STDOUT: %C.ad6: type = class_type @C, @C(%a) [symbolic] // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %complete_type.357: = complete_type_witness %empty_struct_type [concrete] // CHECK:STDOUT: %int_1000.ff9: Core.IntLiteral = int_value 1000 [concrete] // CHECK:STDOUT: %ImplicitAs.type.cc7: type = generic_interface_type @ImplicitAs [concrete] // CHECK:STDOUT: %ImplicitAs.generic: %ImplicitAs.type.cc7 = struct_value () [concrete] // CHECK:STDOUT: %ImplicitAs.type.e8c: type = facet_type <@ImplicitAs, @ImplicitAs(%i32)> [concrete] // CHECK:STDOUT: %To: Core.IntLiteral = symbolic_binding To, 0 [symbolic] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.4e6: type = fn_type @Core.IntLiteral.as.ImplicitAs.impl.Convert, @Core.IntLiteral.as.ImplicitAs.impl(%To) [symbolic] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.3c2: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.4e6 = struct_value () [symbolic] // CHECK:STDOUT: %ImplicitAs.impl_witness.6bc: = impl_witness imports.%ImplicitAs.impl_witness_table.74f, @Core.IntLiteral.as.ImplicitAs.impl(%int_32) [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.e0d: type = fn_type @Core.IntLiteral.as.ImplicitAs.impl.Convert, @Core.IntLiteral.as.ImplicitAs.impl(%int_32) [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.e0d = struct_value () [concrete] // CHECK:STDOUT: %ImplicitAs.facet: %ImplicitAs.type.e8c = facet_value Core.IntLiteral, (%ImplicitAs.impl_witness.6bc) [concrete] // CHECK:STDOUT: %ImplicitAs.WithSelf.Convert.type.b37: type = fn_type @ImplicitAs.WithSelf.Convert, @ImplicitAs.WithSelf(%i32, %ImplicitAs.facet) [concrete] // CHECK:STDOUT: %.545: type = fn_type_with_self_type %ImplicitAs.WithSelf.Convert.type.b37, %ImplicitAs.facet [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.bound: = bound_method %int_1000.ff9, %Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5 [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn: = specific_function %Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5, @Core.IntLiteral.as.ImplicitAs.impl.Convert(%int_32) [concrete] // CHECK:STDOUT: %bound_method: = bound_method %int_1000.ff9, %Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn [concrete] // CHECK:STDOUT: %int_1000.1b6: %i32 = int_value 1000 [concrete] // CHECK:STDOUT: %C.a39: type = class_type @C, @C(%int_1000.1b6) [concrete] // CHECK:STDOUT: %pattern_type.f44: type = pattern_type %C.a39 [concrete] // CHECK:STDOUT: %b: %C.a39 = symbolic_binding b, 0 [symbolic] // CHECK:STDOUT: %D.type.6dc267.1: type = generic_class_type @D.loc5 [concrete] // CHECK:STDOUT: %D.generic.3c4a9e.1: %D.type.6dc267.1 = struct_value () [concrete] // CHECK:STDOUT: %D.type.6dc267.2: type = generic_class_type @D.loc13 [concrete] // CHECK:STDOUT: %D.generic.3c4a9e.2: %D.type.6dc267.2 = struct_value () [concrete] // CHECK:STDOUT: %D.54c2d1.2: type = class_type @D.loc13, @D.loc13(%b) [symbolic] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: .Int = %Core.Int // CHECK:STDOUT: .ImplicitAs = %Core.ImplicitAs // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: %Core.Int: %Int.type = import_ref Core//prelude/parts/int, Int, loaded [concrete = constants.%Int.generic] // CHECK:STDOUT: %Core.ImplicitAs: %ImplicitAs.type.cc7 = import_ref Core//prelude/parts/as, ImplicitAs, loaded [concrete = constants.%ImplicitAs.generic] // CHECK:STDOUT: %Core.import_ref.42d: @Core.IntLiteral.as.ImplicitAs.impl.%Core.IntLiteral.as.ImplicitAs.impl.Convert.type (%Core.IntLiteral.as.ImplicitAs.impl.Convert.type.4e6) = import_ref Core//prelude/parts/int, loc{{\d+_\d+}}, loaded [symbolic = @Core.IntLiteral.as.ImplicitAs.impl.%Core.IntLiteral.as.ImplicitAs.impl.Convert (constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.3c2)] // CHECK:STDOUT: %ImplicitAs.impl_witness_table.74f = impl_witness_table (%Core.import_ref.42d), @Core.IntLiteral.as.ImplicitAs.impl [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .C = %C.decl // CHECK:STDOUT: .D = %D.decl.loc5 // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %C.decl: %C.type = class_decl @C [concrete = constants.%C.generic] { // CHECK:STDOUT: %a.patt: %pattern_type.7ce = symbolic_binding_pattern a, 0 [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %.loc4: type = splice_block %i32 [concrete = constants.%i32] { // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %i32: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: } // CHECK:STDOUT: %a.loc4_9.2: %i32 = symbolic_binding a, 0 [symbolic = %a.loc4_9.1 (constants.%a)] // CHECK:STDOUT: } // CHECK:STDOUT: %D.decl.loc5: %D.type.6dc267.1 = class_decl @D.loc5 [concrete = constants.%D.generic.3c4a9e.1] { // CHECK:STDOUT: %b.patt: %pattern_type.f44 = symbolic_binding_pattern b, 0 [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %.loc5_19.1: type = splice_block %C [concrete = constants.%C.a39] { // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %C.ref: %C.type = name_ref C, file.%C.decl [concrete = constants.%C.generic] // CHECK:STDOUT: %int_1000: Core.IntLiteral = int_value 1000 [concrete = constants.%int_1000.ff9] // CHECK:STDOUT: %impl.elem0: %.545 = impl_witness_access constants.%ImplicitAs.impl_witness.6bc, element0 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5] // CHECK:STDOUT: %bound_method.loc5_19.1: = bound_method %int_1000, %impl.elem0 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.bound] // CHECK:STDOUT: %specific_fn: = specific_function %impl.elem0, @Core.IntLiteral.as.ImplicitAs.impl.Convert(constants.%int_32) [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn] // CHECK:STDOUT: %bound_method.loc5_19.2: = bound_method %int_1000, %specific_fn [concrete = constants.%bound_method] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.call: init %i32 = call %bound_method.loc5_19.2(%int_1000) [concrete = constants.%int_1000.1b6] // CHECK:STDOUT: %.loc5_19.2: %i32 = value_of_initializer %Core.IntLiteral.as.ImplicitAs.impl.Convert.call [concrete = constants.%int_1000.1b6] // CHECK:STDOUT: %.loc5_19.3: %i32 = converted %int_1000, %.loc5_19.2 [concrete = constants.%int_1000.1b6] // CHECK:STDOUT: %C: type = class_type @C, @C(constants.%int_1000.1b6) [concrete = constants.%C.a39] // CHECK:STDOUT: } // CHECK:STDOUT: %b.loc5_9.2: %C.a39 = symbolic_binding b, 0 [symbolic = %b.loc5_9.1 (constants.%b)] // CHECK:STDOUT: } // CHECK:STDOUT: %D.decl.loc13: %D.type.6dc267.2 = class_decl @D.loc13 [concrete = constants.%D.generic.3c4a9e.2] { // CHECK:STDOUT: %b.patt: %pattern_type.f44 = symbolic_binding_pattern b, 0 [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %.loc13_20.1: type = splice_block %C [concrete = constants.%C.a39] { // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %C.ref: %C.type = name_ref C, file.%C.decl [concrete = constants.%C.generic] // CHECK:STDOUT: %int_1000: Core.IntLiteral = int_value 1000 [concrete = constants.%int_1000.ff9] // CHECK:STDOUT: %impl.elem0: %.545 = impl_witness_access constants.%ImplicitAs.impl_witness.6bc, element0 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5] // CHECK:STDOUT: %bound_method.loc13_20.1: = bound_method %int_1000, %impl.elem0 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.bound] // CHECK:STDOUT: %specific_fn: = specific_function %impl.elem0, @Core.IntLiteral.as.ImplicitAs.impl.Convert(constants.%int_32) [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn] // CHECK:STDOUT: %bound_method.loc13_20.2: = bound_method %int_1000, %specific_fn [concrete = constants.%bound_method] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.call: init %i32 = call %bound_method.loc13_20.2(%int_1000) [concrete = constants.%int_1000.1b6] // CHECK:STDOUT: %.loc13_20.2: %i32 = value_of_initializer %Core.IntLiteral.as.ImplicitAs.impl.Convert.call [concrete = constants.%int_1000.1b6] // CHECK:STDOUT: %.loc13_20.3: %i32 = converted %int_1000, %.loc13_20.2 [concrete = constants.%int_1000.1b6] // CHECK:STDOUT: %C: type = class_type @C, @C(constants.%int_1000.1b6) [concrete = constants.%C.a39] // CHECK:STDOUT: } // CHECK:STDOUT: %b.loc13_9.2: %C.a39 = symbolic_binding b, 0 [symbolic = %b.loc13_9.1 (constants.%b)] // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic class @C(%a.loc4_9.2: %i32) { // CHECK:STDOUT: %a.loc4_9.1: %i32 = symbolic_binding a, 0 [symbolic = %a.loc4_9.1 (constants.%a)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: // CHECK:STDOUT: class { // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type.357] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%C.ad6 // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic class @D.loc5(%b.loc5_9.2: %C.a39) { // CHECK:STDOUT: %b.loc5_9.1: %C.a39 = symbolic_binding b, 0 [symbolic = %b.loc5_9.1 (constants.%b)] // CHECK:STDOUT: // CHECK:STDOUT: class; // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic class @D.loc13(%b.loc13_9.2: %C.a39) { // CHECK:STDOUT: %b.loc13_9.1: %C.a39 = symbolic_binding b, 0 [symbolic = %b.loc13_9.1 (constants.%b)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: // CHECK:STDOUT: class { // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type.357] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%D.54c2d1.2 // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @C(constants.%a) { // CHECK:STDOUT: %a.loc4_9.1 => constants.%a // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @C(constants.%int_1000.1b6) { // CHECK:STDOUT: %a.loc4_9.1 => constants.%int_1000.1b6 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @D.loc5(constants.%b) { // CHECK:STDOUT: %b.loc5_9.1 => constants.%b // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @D.loc13(constants.%b) { // CHECK:STDOUT: %b.loc13_9.1 => constants.%b // CHECK:STDOUT: } // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/class/virtual_modifiers.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/int.carbon // TODO: Add ranges and switch to "--dump-sem-ir-ranges=only". // EXTRA-ARGS: --dump-sem-ir-ranges=if-present // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/class/virtual_modifiers.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/class/virtual_modifiers.carbon // --- modifiers.carbon package Modifiers; base class Base { virtual fn H[self: Self](); } abstract class Abstract { abstract fn J[self: Self](); virtual fn K[self: Self](); } // --- override_import.carbon library "[[@TEST_NAME]]"; import Modifiers; class Derived { extend base: Modifiers.Base; override fn H[self: Self](); } fn Use() { var unused d: Derived = {.base = {}}; } // --- todo_fail_later_base.carbon library "[[@TEST_NAME]]"; import Modifiers; base class Derived { virtual fn F[self: Self](); extend base: Modifiers.Base; } // --- init.carbon library "[[@TEST_NAME]]"; import Modifiers; fn F() { var unused v: Modifiers.Base = {}; } // --- impl_abstract.carbon library "[[@TEST_NAME]]"; abstract class A1 { virtual fn F[self: Self](); } abstract class A2 { extend base: A1; override fn F[self: Self](); } // --- impl_base.carbon library "[[@TEST_NAME]]"; base class B1 { virtual fn F[self: Self](); } base class B2 { extend base: B1; override fn F[self: Self](); } class C { extend base: B2; override fn F[self: Self](); } fn Use() { var unused b1: B1 = {}; var unused b2: B2 = {.base = {}}; var unused c: C = {.base = {.base = {}}}; } // --- fail_modifiers.carbon library "[[@TEST_NAME]]"; class C { // CHECK:STDERR: fail_modifiers.carbon:[[@LINE+4]]:3: error: override without base class [OverrideWithoutBase] // CHECK:STDERR: override fn F[self: Self](); // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: override fn F[self: Self](); } // --- init_members.carbon library "[[@TEST_NAME]]"; base class Base { var m1: i32; var m2: i32; virtual fn F[self: Self](); } fn F() { var i: i32 = 3; var b1: Base = {.m2 = i, .m1 = i}; var unused b2: Base = {.m2 = 3, .m1 = 5}; b1.m2 = 4; } // --- fail_impl_without_base_declaration.carbon library "[[@TEST_NAME]]"; base class Base { } class Derived { extend base: Base; // CHECK:STDERR: fail_impl_without_base_declaration.carbon:[[@LINE+4]]:3: error: override without compatible virtual in base class [OverrideWithoutVirtualInBase] // CHECK:STDERR: override fn F[self: Self](); // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: override fn F[self: Self](); } // --- abstract_impl.carbon library "[[@TEST_NAME]]"; abstract class AbstractBase { abstract fn F[self: Self](); } abstract class AbstractIntermediate { extend base: AbstractBase; } class Derived { extend base: AbstractIntermediate; override fn F[self: Self](); } // --- virtual_impl.carbon library "[[@TEST_NAME]]"; base class VirtualBase { virtual fn F[self: Self](); } base class VirtualIntermediate { extend base: VirtualBase; } class Derived { extend base: VirtualIntermediate; override fn F[self: Self](); } // --- fail_impl_mismatch.carbon library "[[@TEST_NAME]]"; base class Base { virtual fn F[self: Self](); } class Derived { extend base: Base; // CHECK:STDERR: fail_impl_mismatch.carbon:[[@LINE+7]]:3: error: redeclaration differs because of parameter count of 1 [RedeclParamCountDiffers] // CHECK:STDERR: override fn F[self: Self](v: i32); // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: fail_impl_mismatch.carbon:[[@LINE-8]]:3: note: previously declared with parameter count of 0 [RedeclParamCountPrevious] // CHECK:STDERR: virtual fn F[self: Self](); // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: override fn F[self: Self](v: i32); } // --- fail_todo_impl_conversion.carbon library "[[@TEST_NAME]]"; class T1 { } class T2 { } impl T2 as Core.ImplicitAs(T1) { fn Convert[unused self: Self]() -> T1 { return {}; } } base class Base { virtual fn F[self: Self]() -> T1; } class Derived { extend base: Base; // CHECK:STDERR: fail_todo_impl_conversion.carbon:[[@LINE+7]]:3: error: function redeclaration differs because return type is `T2` [FunctionRedeclReturnTypeDiffers] // CHECK:STDERR: override fn F[self: Self]() -> T2; // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: fail_todo_impl_conversion.carbon:[[@LINE-8]]:3: note: previously declared with return type `T1` [FunctionRedeclReturnTypePrevious] // CHECK:STDERR: virtual fn F[self: Self]() -> T1; // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: override fn F[self: Self]() -> T2; } // --- fail_generic_virtual_decl.carbon library "[[@TEST_NAME]]"; // CHECK:STDERR: fail_generic_virtual_decl.carbon:[[@LINE+3]]:1: error: use of undefined generic function [MissingGenericFunctionDefinition] // CHECK:STDERR: base class Base(T:! type) { // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~ base class Base(T:! type) { // CHECK:STDERR: fail_generic_virtual_decl.carbon:[[@LINE+4]]:3: note: generic function declared here [MissingGenericFunctionDefinitionHere] // CHECK:STDERR: virtual fn F[self: Self](); // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: virtual fn F[self: Self](); } // --- impl_generic_base.carbon library "[[@TEST_NAME]]"; class T1 { } base class Base(T:! type) { virtual fn F[unused self: Self](unused t: T) { } } class Derived { extend base: Base(T1); override fn F[unused self: Self](unused t: T1) { } } // --- fail_virtual_without_self.carbon library "[[@TEST_NAME]]"; abstract class T1 { // CHECK:STDERR: fail_virtual_without_self.carbon:[[@LINE+4]]:3: error: virtual class function [VirtualWithoutSelf] // CHECK:STDERR: virtual fn F(); // CHECK:STDERR: ^~~~~~~~~~~~~~~ // CHECK:STDERR: virtual fn F(); // CHECK:STDERR: fail_virtual_without_self.carbon:[[@LINE+4]]:3: error: virtual class function [VirtualWithoutSelf] // CHECK:STDERR: abstract fn G(); // CHECK:STDERR: ^~~~~~~~~~~~~~~~ // CHECK:STDERR: abstract fn G(); } class T2 { extend base: T1; // CHECK:STDERR: fail_virtual_without_self.carbon:[[@LINE+4]]:3: error: virtual class function [VirtualWithoutSelf] // CHECK:STDERR: override fn F(); // CHECK:STDERR: ^~~~~~~~~~~~~~~~ // CHECK:STDERR: override fn F(); } // --- fail_ref_self_mismatch.carbon library "[[@TEST_NAME]]"; base class T1 { virtual fn F1[self: Self](); } class T2 { extend base: T1; // CHECK:STDERR: fail_ref_self_mismatch.carbon:[[@LINE+7]]:18: error: redeclaration differs at implicit parameter 1 [RedeclParamDiffers] // CHECK:STDERR: override fn F1[ref self: Self](); // CHECK:STDERR: ^~~~~~~~~~~~~~ // CHECK:STDERR: fail_ref_self_mismatch.carbon:[[@LINE-8]]:17: note: previous declaration's corresponding implicit parameter here [RedeclParamPrevious] // CHECK:STDERR: virtual fn F1[self: Self](); // CHECK:STDERR: ^~~~~~~~~~ // CHECK:STDERR: override fn F1[ref self: Self](); } // --- fail_generic_virtual.carbon library "[[@TEST_NAME]]"; base class T1 { // CHECK:STDERR: fail_generic_virtual.carbon:[[@LINE+4]]:3: error: generic virtual function [GenericVirtual] // CHECK:STDERR: virtual fn F[self: Self, T:! type](); // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: virtual fn F[self: Self, T:! type](); } // --- fail_generic_virtual_in_generic_class.carbon library "[[@TEST_NAME]]"; base class T1(T:! type) { // CHECK:STDERR: fail_generic_virtual_in_generic_class.carbon:[[@LINE+4]]:3: error: generic virtual function [GenericVirtual] // CHECK:STDERR: virtual fn F[self: Self, T:! type](); // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: virtual fn F[self: Self, T:! type](); } // --- generic_with_virtual.carbon library "[[@TEST_NAME]]"; base class T1(T:! type) { virtual fn F[unused self: Self]() { } } // --- with_dependent_arg.carbon library "[[@TEST_NAME]]"; base class T1(T:! type) { virtual fn F[unused self: Self](unused t: T) { } } // --- vtable_import_unneeded.carbon library "[[@TEST_NAME]]"; import Modifiers; fn F(b: Modifiers.Base); // --- generic_derived_from_nongeneric.carbon library "[[@TEST_NAME]]"; base class NonGenericBase { virtual fn F1[unused self: Self]() { } virtual fn F2[unused self: Self]() { } } base class GenericDerived(T:! type) { extend base: NonGenericBase; override fn F2[unused self: Self]() { } virtual fn F3[unused self: Self]() { } } // --- nongeneric_derived_from_generic.carbon library "[[@TEST_NAME]]"; base class GenericBase(T:! type) { virtual fn F1[unused self: Self]() { } virtual fn F2[unused self: Self]() { } } class T1; base class NonGenericDerived { extend base: GenericBase(T1); override fn F2[unused self: Self]() { } virtual fn F3[unused self: Self]() { } } // --- impl_generic_specifically.carbon library "[[@TEST_NAME]]"; base class Base(T:! type) { virtual fn F[unused self: Self](unused t: Base(T)*) { } } class T1; class D1 { extend base: Base(T1); override fn F[unused self: Self](unused t: Base(T1)*) { } } // --- fail_impl_generic_specifically_mismatch.carbon library "[[@TEST_NAME]]"; class T1; class T2; base class Base(T:! type) { virtual fn F[unused self: Self](unused t: T1*) { } } class D1 { extend base: Base(T1); // CHECK:STDERR: fail_impl_generic_specifically_mismatch.carbon:[[@LINE+7]]:43: error: type `` of parameter 1 in redeclaration differs from previous parameter type `` [RedeclParamDiffersType] // CHECK:STDERR: override fn F[unused self: Self](unused t: T2*) { } // CHECK:STDERR: ^~~~~~ // CHECK:STDERR: fail_impl_generic_specifically_mismatch.carbon:[[@LINE-8]]:42: note: previous declaration's corresponding parameter here [RedeclParamPrevious] // CHECK:STDERR: virtual fn F[unused self: Self](unused t: T1*) { } // CHECK:STDERR: ^~~~~~ // CHECK:STDERR: override fn F[unused self: Self](unused t: T2*) { } } // --- fail_impl_generic_generic_mismatch.carbon library "[[@TEST_NAME]]"; abstract class Base(T:! type) { virtual fn F[unused self: Self](unused t: T*) { } } class Derived(T:! type) { extend base: Base(T); // CHECK:STDERR: fail_impl_generic_generic_mismatch.carbon:[[@LINE+7]]:43: error: type `` of parameter 1 in redeclaration differs from previous parameter type `` [RedeclParamDiffersType] // CHECK:STDERR: override fn F[unused self: Self](unused t: T) { } // CHECK:STDERR: ^~~~ // CHECK:STDERR: fail_impl_generic_generic_mismatch.carbon:[[@LINE-7]]:42: note: previous declaration's corresponding parameter here [RedeclParamPrevious] // CHECK:STDERR: virtual fn F[unused self: Self](unused t: T*) { } // CHECK:STDERR: ^~~~~ // CHECK:STDERR: override fn F[unused self: Self](unused t: T) { } } // --- impl_generic_generic.carbon library "[[@TEST_NAME]]"; abstract class Base(T:! type) { virtual fn F[unused self: Self](unused t: T) { } } class Derived(T:! type) { extend base: Base(T*); override fn F[unused self: Self](unused t: T*) { } } // --- abstract_generic_undefined.carbon library "[[@TEST_NAME]]"; abstract class Base(T:! type) { abstract fn F[self: Self](); } class T1; class Derived { extend base: Base(T1); override fn F[unused self: Self]() { } } // --- generic_lib.carbon library "[[@TEST_NAME]]"; base class Base(T:! type) { virtual fn F[unused self: Self]() { } } // --- generic_import.carbon library "[[@TEST_NAME]]"; import library "generic_lib"; class T1; var v: Base(T1) = {}; // --- generic_derived_generic.carbon library "[[@TEST_NAME]]"; base class T1(G1:! type) { virtual fn F[unused self: Self]() { } } class T2(G2:! type) { extend base: T1(G2); } // --- generic_derived_generic_context.carbon library "[[@TEST_NAME]]"; base class T1(G1:! type) { virtual fn F[unused self: Self]() { } } class T2(G2:! type) { class T3 { extend base : T1(G2); } } // CHECK:STDOUT: --- modifiers.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %Base: type = class_type @Base [concrete] // CHECK:STDOUT: %pattern_type.dec: type = pattern_type %Base [concrete] // CHECK:STDOUT: %Base.H.type: type = fn_type @Base.H [concrete] // CHECK:STDOUT: %Base.H: %Base.H.type = struct_value () [concrete] // CHECK:STDOUT: %ptr: type = ptr_type [concrete] // CHECK:STDOUT: %Base.vtable_decl: ref %ptr = vtable_decl @Base.vtable [concrete] // CHECK:STDOUT: %struct_type.vptr: type = struct_type {.: %ptr} [concrete] // CHECK:STDOUT: %complete_type: = complete_type_witness %struct_type.vptr [concrete] // CHECK:STDOUT: %Abstract: type = class_type @Abstract [concrete] // CHECK:STDOUT: %pattern_type.3c9: type = pattern_type %Abstract [concrete] // CHECK:STDOUT: %Abstract.J.type: type = fn_type @Abstract.J [concrete] // CHECK:STDOUT: %Abstract.J: %Abstract.J.type = struct_value () [concrete] // CHECK:STDOUT: %Abstract.K.type: type = fn_type @Abstract.K [concrete] // CHECK:STDOUT: %Abstract.K: %Abstract.K.type = struct_value () [concrete] // CHECK:STDOUT: %Abstract.vtable_decl: ref %ptr = vtable_decl @Abstract.vtable [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .Base = %Base.decl // CHECK:STDOUT: .Abstract = %Abstract.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %Base.decl: type = class_decl @Base [concrete = constants.%Base] {} {} // CHECK:STDOUT: %Abstract.decl: type = class_decl @Abstract [concrete = constants.%Abstract] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @Base { // CHECK:STDOUT: %Base.H.decl: %Base.H.type = fn_decl @Base.H [concrete = constants.%Base.H] { // CHECK:STDOUT: %self.patt: %pattern_type.dec = value_binding_pattern self [concrete] // CHECK:STDOUT: %self.param_patt: %pattern_type.dec = value_param_pattern %self.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %self.param: %Base = value_param call_param0 // CHECK:STDOUT: %Self.ref: type = name_ref Self, constants.%Base [concrete = constants.%Base] // CHECK:STDOUT: %self: %Base = value_binding self, %self.param // CHECK:STDOUT: } // CHECK:STDOUT: %vtable_decl: ref %ptr = vtable_decl @Base.vtable [concrete = constants.%Base.vtable_decl] // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%struct_type.vptr [concrete = constants.%complete_type] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: vtable_decl = %vtable_decl // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%Base // CHECK:STDOUT: .H = %Base.H.decl // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @Abstract { // CHECK:STDOUT: %Abstract.J.decl: %Abstract.J.type = fn_decl @Abstract.J [concrete = constants.%Abstract.J] { // CHECK:STDOUT: %self.patt: %pattern_type.3c9 = value_binding_pattern self [concrete] // CHECK:STDOUT: %self.param_patt: %pattern_type.3c9 = value_param_pattern %self.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %self.param: %Abstract = value_param call_param0 // CHECK:STDOUT: %Self.ref: type = name_ref Self, constants.%Abstract [concrete = constants.%Abstract] // CHECK:STDOUT: %self: %Abstract = value_binding self, %self.param // CHECK:STDOUT: } // CHECK:STDOUT: %Abstract.K.decl: %Abstract.K.type = fn_decl @Abstract.K [concrete = constants.%Abstract.K] { // CHECK:STDOUT: %self.patt: %pattern_type.3c9 = value_binding_pattern self [concrete] // CHECK:STDOUT: %self.param_patt: %pattern_type.3c9 = value_param_pattern %self.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %self.param: %Abstract = value_param call_param0 // CHECK:STDOUT: %Self.ref: type = name_ref Self, constants.%Abstract [concrete = constants.%Abstract] // CHECK:STDOUT: %self: %Abstract = value_binding self, %self.param // CHECK:STDOUT: } // CHECK:STDOUT: %vtable_decl: ref %ptr = vtable_decl @Abstract.vtable [concrete = constants.%Abstract.vtable_decl] // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%struct_type.vptr [concrete = constants.%complete_type] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: vtable_decl = %vtable_decl // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%Abstract // CHECK:STDOUT: .J = %Abstract.J.decl // CHECK:STDOUT: .K = %Abstract.K.decl // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: vtable @Base.vtable { // CHECK:STDOUT: @Base.%Base.H.decl // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: vtable @Abstract.vtable { // CHECK:STDOUT: @Abstract.%Abstract.J.decl // CHECK:STDOUT: @Abstract.%Abstract.K.decl // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: virtual fn @Base.H(%self.param: %Base); // CHECK:STDOUT: // CHECK:STDOUT: abstract fn @Abstract.J(%self.param: %Abstract); // CHECK:STDOUT: // CHECK:STDOUT: virtual fn @Abstract.K(%self.param: %Abstract); // CHECK:STDOUT: // CHECK:STDOUT: --- override_import.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %Derived: type = class_type @Derived [concrete] // CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] // CHECK:STDOUT: %Base: type = class_type @Base [concrete] // CHECK:STDOUT: %ptr.454: type = ptr_type [concrete] // CHECK:STDOUT: %struct_type.vptr: type = struct_type {.: %ptr.454} [concrete] // CHECK:STDOUT: %complete_type.513: = complete_type_witness %struct_type.vptr [concrete] // CHECK:STDOUT: %Derived.elem: type = unbound_element_type %Derived, %Base [concrete] // CHECK:STDOUT: %pattern_type.9f6: type = pattern_type %Derived [concrete] // CHECK:STDOUT: %Derived.H.type: type = fn_type @Derived.H [concrete] // CHECK:STDOUT: %Derived.H: %Derived.H.type = struct_value () [concrete] // CHECK:STDOUT: %Base.H.type: type = fn_type @Base.H [concrete] // CHECK:STDOUT: %Base.H: %Base.H.type = struct_value () [concrete] // CHECK:STDOUT: %Base.vtable_decl: ref %ptr.454 = vtable_decl @Base.vtable [concrete] // CHECK:STDOUT: %Derived.vtable_decl: ref %ptr.454 = vtable_decl @Derived.vtable [concrete] // CHECK:STDOUT: %struct_type.base.96c: type = struct_type {.base: %Base} [concrete] // CHECK:STDOUT: %complete_type.0e2: = complete_type_witness %struct_type.base.96c [concrete] // CHECK:STDOUT: %Use.type: type = fn_type @Use [concrete] // CHECK:STDOUT: %Use: %Use.type = struct_value () [concrete] // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %empty_struct: %empty_struct_type = struct_value () [concrete] // CHECK:STDOUT: %struct_type.base.f5e: type = struct_type {.base: %empty_struct_type} [concrete] // CHECK:STDOUT: %struct.6b1: %struct_type.base.f5e = struct_value (%empty_struct) [concrete] // CHECK:STDOUT: %.a5d: type = partial_type %Base [concrete] // CHECK:STDOUT: %uninit: %ptr.454 = uninitialized_value [concrete] // CHECK:STDOUT: %struct.4dc: %.a5d = struct_value (%uninit) [concrete] // CHECK:STDOUT: %Base.val: %Base = struct_value (%uninit) [concrete] // CHECK:STDOUT: %Derived.val: %Derived = struct_value (%Base.val) [concrete] // CHECK:STDOUT: %Derived.vtable_ptr: ref %ptr.454 = vtable_ptr @Derived.vtable [concrete] // CHECK:STDOUT: %Destroy.type: type = facet_type <@Destroy> [concrete] // CHECK:STDOUT: %Destroy.Op.type: type = fn_type @Destroy.Op [concrete] // CHECK:STDOUT: %Destroy.Op: %Destroy.Op.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: .Destroy = %Core.Destroy // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: %Modifiers: = namespace file.%Modifiers.import, [concrete] { // CHECK:STDOUT: .Base = %Modifiers.Base // CHECK:STDOUT: import Modifiers//default // CHECK:STDOUT: } // CHECK:STDOUT: %Modifiers.Base: type = import_ref Modifiers//default, Base, loaded [concrete = constants.%Base] // CHECK:STDOUT: %Modifiers.import_ref.695: ref %ptr.454 = import_ref Modifiers//default, loc6_1, loaded [concrete = constants.%Base.vtable_decl] // CHECK:STDOUT: %Modifiers.import_ref.05e: = import_ref Modifiers//default, loc6_1, loaded [concrete = constants.%complete_type.513] // CHECK:STDOUT: %Modifiers.import_ref.d82 = import_ref Modifiers//default, inst{{[0-9A-F]+}} [no loc], unloaded // CHECK:STDOUT: %Modifiers.import_ref.d40 = import_ref Modifiers//default, loc5_29, unloaded // CHECK:STDOUT: %Core.Destroy: type = import_ref Core//prelude/parts/destroy, Destroy, loaded [concrete = constants.%Destroy.type] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .Modifiers = imports.%Modifiers // CHECK:STDOUT: .Derived = %Derived.decl // CHECK:STDOUT: .Use = %Use.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %Modifiers.import = import Modifiers // CHECK:STDOUT: %Derived.decl: type = class_decl @Derived [concrete = constants.%Derived] {} {} // CHECK:STDOUT: %Use.decl: %Use.type = fn_decl @Use [concrete = constants.%Use] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @Derived { // CHECK:STDOUT: %Modifiers.ref: = name_ref Modifiers, imports.%Modifiers [concrete = imports.%Modifiers] // CHECK:STDOUT: %Base.ref: type = name_ref Base, imports.%Modifiers.Base [concrete = constants.%Base] // CHECK:STDOUT: %.loc7: %Derived.elem = base_decl %Base.ref, element0 [concrete] // CHECK:STDOUT: %Derived.H.decl: %Derived.H.type = fn_decl @Derived.H [concrete = constants.%Derived.H] { // CHECK:STDOUT: %self.patt: %pattern_type.9f6 = value_binding_pattern self [concrete] // CHECK:STDOUT: %self.param_patt: %pattern_type.9f6 = value_param_pattern %self.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %self.param: %Derived = value_param call_param0 // CHECK:STDOUT: %Self.ref: type = name_ref Self, constants.%Derived [concrete = constants.%Derived] // CHECK:STDOUT: %self: %Derived = value_binding self, %self.param // CHECK:STDOUT: } // CHECK:STDOUT: %vtable_decl: ref %ptr.454 = vtable_decl @Derived.vtable [concrete = constants.%Derived.vtable_decl] // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%struct_type.base.96c [concrete = constants.%complete_type.0e2] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: vtable_decl = %vtable_decl // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%Derived // CHECK:STDOUT: .Modifiers = // CHECK:STDOUT: .base = %.loc7 // CHECK:STDOUT: .H = %Derived.H.decl // CHECK:STDOUT: extend %Base.ref // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @Base [from "modifiers.carbon"] { // CHECK:STDOUT: complete_type_witness = imports.%Modifiers.import_ref.05e // CHECK:STDOUT: vtable_decl = imports.%Modifiers.import_ref.695 // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = imports.%Modifiers.import_ref.d82 // CHECK:STDOUT: .H = imports.%Modifiers.import_ref.d40 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: vtable @Base.vtable { // CHECK:STDOUT: constants.%Base.H // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: vtable @Derived.vtable { // CHECK:STDOUT: @Derived.%Derived.H.decl // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: override fn @Derived.H(%self.param: %Derived); // CHECK:STDOUT: // CHECK:STDOUT: virtual fn @Base.H [from "modifiers.carbon"]; // CHECK:STDOUT: // CHECK:STDOUT: fn @Use() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %d.patt: %pattern_type.9f6 = ref_binding_pattern d [concrete] // CHECK:STDOUT: %d.var_patt: %pattern_type.9f6 = var_pattern %d.patt [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %d.var: ref %Derived = var %d.var_patt // CHECK:STDOUT: %.loc12_37.1: %empty_struct_type = struct_literal () [concrete = constants.%empty_struct] // CHECK:STDOUT: %.loc12_38.1: %struct_type.base.f5e = struct_literal (%.loc12_37.1) [concrete = constants.%struct.6b1] // CHECK:STDOUT: %.loc12_38.2: ref %.a5d = class_element_access %d.var, element0 // CHECK:STDOUT: %.loc12_37.2: ref %ptr.454 = class_element_access %.loc12_38.2, element0 // CHECK:STDOUT: %uninit: %ptr.454 = uninitialized_value [concrete = constants.%uninit] // CHECK:STDOUT: %.loc12_37.3: init %ptr.454 to %.loc12_37.2 = in_place_init %uninit [concrete = constants.%uninit] // CHECK:STDOUT: %.loc12_37.4: init %.a5d to %.loc12_38.2 = class_init (%.loc12_37.3) [concrete = constants.%struct.4dc] // CHECK:STDOUT: %.loc12_38.3: init %.a5d = converted %.loc12_37.1, %.loc12_37.4 [concrete = constants.%struct.4dc] // CHECK:STDOUT: %.loc12_38.4: init %Base = as_compatible %.loc12_38.3 [concrete = constants.%Base.val] // CHECK:STDOUT: %.loc12_38.5: init %Derived to %d.var = class_init (%.loc12_38.4) [concrete = constants.%Derived.val] // CHECK:STDOUT: %Derived.vtable_ptr: ref %ptr.454 = vtable_ptr @Derived.vtable [concrete = constants.%Derived.vtable_ptr] // CHECK:STDOUT: %.loc12_38.6: ref %Base = class_element_access %d.var, element0 // CHECK:STDOUT: %.loc12_38.7: ref %ptr.454 = class_element_access %.loc12_38.6, element0 // CHECK:STDOUT: %.loc12_38.8: init %ptr.454 to %.loc12_38.7 = in_place_init %Derived.vtable_ptr [concrete = constants.%Derived.vtable_ptr] // CHECK:STDOUT: %.loc12_38.9: init %Derived = update_init %.loc12_38.5, %.loc12_38.8 // CHECK:STDOUT: %.loc12_3: init %Derived = converted %.loc12_38.1, %.loc12_38.9 // CHECK:STDOUT: assign %d.var, %.loc12_3 // CHECK:STDOUT: %Derived.ref: type = name_ref Derived, file.%Derived.decl [concrete = constants.%Derived] // CHECK:STDOUT: %d: ref %Derived = ref_binding d, %d.var // CHECK:STDOUT: %Destroy.Op.bound: = bound_method %d.var, constants.%Destroy.Op // CHECK:STDOUT: %Destroy.Op.call: init %empty_tuple.type = call %Destroy.Op.bound(%d.var) // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @Destroy.Op(%self.param: ref %Derived) = "no_op"; // CHECK:STDOUT: // CHECK:STDOUT: --- todo_fail_later_base.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %Derived: type = class_type @Derived [concrete] // CHECK:STDOUT: %pattern_type.9f6: type = pattern_type %Derived [concrete] // CHECK:STDOUT: %Derived.F.type: type = fn_type @Derived.F [concrete] // CHECK:STDOUT: %Derived.F: %Derived.F.type = struct_value () [concrete] // CHECK:STDOUT: %Base: type = class_type @Base [concrete] // CHECK:STDOUT: %ptr.454: type = ptr_type [concrete] // CHECK:STDOUT: %struct_type.vptr: type = struct_type {.: %ptr.454} [concrete] // CHECK:STDOUT: %complete_type.513: = complete_type_witness %struct_type.vptr [concrete] // CHECK:STDOUT: %Derived.elem: type = unbound_element_type %Derived, %Base [concrete] // CHECK:STDOUT: %Base.H.type: type = fn_type @Base.H [concrete] // CHECK:STDOUT: %Base.H: %Base.H.type = struct_value () [concrete] // CHECK:STDOUT: %Base.vtable_decl: ref %ptr.454 = vtable_decl @Base.vtable [concrete] // CHECK:STDOUT: %Derived.vtable_decl: ref %ptr.454 = vtable_decl @Derived.vtable [concrete] // CHECK:STDOUT: %struct_type.base: type = struct_type {.base: %Base} [concrete] // CHECK:STDOUT: %complete_type.0e2: = complete_type_witness %struct_type.base [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: %Modifiers: = namespace file.%Modifiers.import, [concrete] { // CHECK:STDOUT: .Base = %Modifiers.Base // CHECK:STDOUT: import Modifiers//default // CHECK:STDOUT: } // CHECK:STDOUT: %Modifiers.Base: type = import_ref Modifiers//default, Base, loaded [concrete = constants.%Base] // CHECK:STDOUT: %Modifiers.import_ref.695: ref %ptr.454 = import_ref Modifiers//default, loc6_1, loaded [concrete = constants.%Base.vtable_decl] // CHECK:STDOUT: %Modifiers.import_ref.05e: = import_ref Modifiers//default, loc6_1, loaded [concrete = constants.%complete_type.513] // CHECK:STDOUT: %Modifiers.import_ref.d82 = import_ref Modifiers//default, inst{{[0-9A-F]+}} [no loc], unloaded // CHECK:STDOUT: %Modifiers.import_ref.d40 = import_ref Modifiers//default, loc5_29, unloaded // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .Modifiers = imports.%Modifiers // CHECK:STDOUT: .Derived = %Derived.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %Modifiers.import = import Modifiers // CHECK:STDOUT: %Derived.decl: type = class_decl @Derived [concrete = constants.%Derived] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @Derived { // CHECK:STDOUT: %Derived.F.decl: %Derived.F.type = fn_decl @Derived.F [concrete = constants.%Derived.F] { // CHECK:STDOUT: %self.patt: %pattern_type.9f6 = value_binding_pattern self [concrete] // CHECK:STDOUT: %self.param_patt: %pattern_type.9f6 = value_param_pattern %self.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %self.param: %Derived = value_param call_param0 // CHECK:STDOUT: %Self.ref: type = name_ref Self, constants.%Derived [concrete = constants.%Derived] // CHECK:STDOUT: %self: %Derived = value_binding self, %self.param // CHECK:STDOUT: } // CHECK:STDOUT: %Modifiers.ref: = name_ref Modifiers, imports.%Modifiers [concrete = imports.%Modifiers] // CHECK:STDOUT: %Base.ref: type = name_ref Base, imports.%Modifiers.Base [concrete = constants.%Base] // CHECK:STDOUT: %.loc8: %Derived.elem = base_decl %Base.ref, element0 [concrete] // CHECK:STDOUT: %vtable_decl: ref %ptr.454 = vtable_decl @Derived.vtable [concrete = constants.%Derived.vtable_decl] // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%struct_type.base [concrete = constants.%complete_type.0e2] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: vtable_decl = %vtable_decl // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%Derived // CHECK:STDOUT: .F = %Derived.F.decl // CHECK:STDOUT: .Modifiers = // CHECK:STDOUT: .base = %.loc8 // CHECK:STDOUT: extend %Base.ref // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @Base [from "modifiers.carbon"] { // CHECK:STDOUT: complete_type_witness = imports.%Modifiers.import_ref.05e // CHECK:STDOUT: vtable_decl = imports.%Modifiers.import_ref.695 // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = imports.%Modifiers.import_ref.d82 // CHECK:STDOUT: .H = imports.%Modifiers.import_ref.d40 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: vtable @Base.vtable { // CHECK:STDOUT: constants.%Base.H // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: vtable @Derived.vtable { // CHECK:STDOUT: constants.%Base.H // CHECK:STDOUT: @Derived.%Derived.F.decl // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: virtual fn @Derived.F(%self.param: %Derived); // CHECK:STDOUT: // CHECK:STDOUT: virtual fn @Base.H [from "modifiers.carbon"]; // CHECK:STDOUT: // CHECK:STDOUT: --- init.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %F.type: type = fn_type @F [concrete] // CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] // CHECK:STDOUT: %F: %F.type = struct_value () [concrete] // CHECK:STDOUT: %Base: type = class_type @Base [concrete] // CHECK:STDOUT: %ptr.454: type = ptr_type [concrete] // CHECK:STDOUT: %struct_type.vptr: type = struct_type {.: %ptr.454} [concrete] // CHECK:STDOUT: %complete_type: = complete_type_witness %struct_type.vptr [concrete] // CHECK:STDOUT: %pattern_type.80f: type = pattern_type %Base [concrete] // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %empty_struct: %empty_struct_type = struct_value () [concrete] // CHECK:STDOUT: %Base.H.type: type = fn_type @Base.H [concrete] // CHECK:STDOUT: %Base.H: %Base.H.type = struct_value () [concrete] // CHECK:STDOUT: %Base.vtable_decl: ref %ptr.454 = vtable_decl @Base.vtable [concrete] // CHECK:STDOUT: %Base.vtable_ptr: ref %ptr.454 = vtable_ptr @Base.vtable [concrete] // CHECK:STDOUT: %Base.val: %Base = struct_value (%Base.vtable_ptr) [concrete] // CHECK:STDOUT: %Destroy.type: type = facet_type <@Destroy> [concrete] // CHECK:STDOUT: %Destroy.Op.type: type = fn_type @Destroy.Op [concrete] // CHECK:STDOUT: %Destroy.Op: %Destroy.Op.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: .Destroy = %Core.Destroy // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: %Modifiers: = namespace file.%Modifiers.import, [concrete] { // CHECK:STDOUT: .Base = %Modifiers.Base // CHECK:STDOUT: import Modifiers//default // CHECK:STDOUT: } // CHECK:STDOUT: %Modifiers.Base: type = import_ref Modifiers//default, Base, loaded [concrete = constants.%Base] // CHECK:STDOUT: %Modifiers.import_ref.695: ref %ptr.454 = import_ref Modifiers//default, loc6_1, loaded [concrete = constants.%Base.vtable_decl] // CHECK:STDOUT: %Modifiers.import_ref.05e: = import_ref Modifiers//default, loc6_1, loaded [concrete = constants.%complete_type] // CHECK:STDOUT: %Modifiers.import_ref.d82 = import_ref Modifiers//default, inst{{[0-9A-F]+}} [no loc], unloaded // CHECK:STDOUT: %Modifiers.import_ref.d40 = import_ref Modifiers//default, loc5_29, unloaded // CHECK:STDOUT: %Core.Destroy: type = import_ref Core//prelude/parts/destroy, Destroy, loaded [concrete = constants.%Destroy.type] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .Modifiers = imports.%Modifiers // CHECK:STDOUT: .F = %F.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %Modifiers.import = import Modifiers // CHECK:STDOUT: %F.decl: %F.type = fn_decl @F [concrete = constants.%F] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @Base [from "modifiers.carbon"] { // CHECK:STDOUT: complete_type_witness = imports.%Modifiers.import_ref.05e // CHECK:STDOUT: vtable_decl = imports.%Modifiers.import_ref.695 // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = imports.%Modifiers.import_ref.d82 // CHECK:STDOUT: .H = imports.%Modifiers.import_ref.d40 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: vtable @Base.vtable { // CHECK:STDOUT: constants.%Base.H // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @F() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %v.patt: %pattern_type.80f = ref_binding_pattern v [concrete] // CHECK:STDOUT: %v.var_patt: %pattern_type.80f = var_pattern %v.patt [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %v.var: ref %Base = var %v.var_patt // CHECK:STDOUT: %.loc7_35.1: %empty_struct_type = struct_literal () [concrete = constants.%empty_struct] // CHECK:STDOUT: %.loc7_35.2: ref %ptr.454 = class_element_access %v.var, element0 // CHECK:STDOUT: %Base.vtable_ptr: ref %ptr.454 = vtable_ptr @Base.vtable [concrete = constants.%Base.vtable_ptr] // CHECK:STDOUT: %.loc7_35.3: init %ptr.454 to %.loc7_35.2 = in_place_init %Base.vtable_ptr [concrete = constants.%Base.vtable_ptr] // CHECK:STDOUT: %.loc7_35.4: init %Base to %v.var = class_init (%.loc7_35.3) [concrete = constants.%Base.val] // CHECK:STDOUT: %.loc7_3: init %Base = converted %.loc7_35.1, %.loc7_35.4 [concrete = constants.%Base.val] // CHECK:STDOUT: assign %v.var, %.loc7_3 // CHECK:STDOUT: %.loc7_26: type = splice_block %Base.ref [concrete = constants.%Base] { // CHECK:STDOUT: %Modifiers.ref: = name_ref Modifiers, imports.%Modifiers [concrete = imports.%Modifiers] // CHECK:STDOUT: %Base.ref: type = name_ref Base, imports.%Modifiers.Base [concrete = constants.%Base] // CHECK:STDOUT: } // CHECK:STDOUT: %v: ref %Base = ref_binding v, %v.var // CHECK:STDOUT: %Destroy.Op.bound: = bound_method %v.var, constants.%Destroy.Op // CHECK:STDOUT: %Destroy.Op.call: init %empty_tuple.type = call %Destroy.Op.bound(%v.var) // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: virtual fn @Base.H [from "modifiers.carbon"]; // CHECK:STDOUT: // CHECK:STDOUT: fn @Destroy.Op(%self.param: ref %Base) = "no_op"; // CHECK:STDOUT: // CHECK:STDOUT: --- impl_abstract.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %A1: type = class_type @A1 [concrete] // CHECK:STDOUT: %pattern_type.258: type = pattern_type %A1 [concrete] // CHECK:STDOUT: %A1.F.type: type = fn_type @A1.F [concrete] // CHECK:STDOUT: %A1.F: %A1.F.type = struct_value () [concrete] // CHECK:STDOUT: %ptr.454: type = ptr_type [concrete] // CHECK:STDOUT: %A1.vtable_decl: ref %ptr.454 = vtable_decl @A1.vtable [concrete] // CHECK:STDOUT: %struct_type.vptr: type = struct_type {.: %ptr.454} [concrete] // CHECK:STDOUT: %complete_type.513: = complete_type_witness %struct_type.vptr [concrete] // CHECK:STDOUT: %A2: type = class_type @A2 [concrete] // CHECK:STDOUT: %A2.elem: type = unbound_element_type %A2, %A1 [concrete] // CHECK:STDOUT: %pattern_type.f23: type = pattern_type %A2 [concrete] // CHECK:STDOUT: %A2.F.type: type = fn_type @A2.F [concrete] // CHECK:STDOUT: %A2.F: %A2.F.type = struct_value () [concrete] // CHECK:STDOUT: %A2.vtable_decl: ref %ptr.454 = vtable_decl @A2.vtable [concrete] // CHECK:STDOUT: %struct_type.base: type = struct_type {.base: %A1} [concrete] // CHECK:STDOUT: %complete_type.0a6: = complete_type_witness %struct_type.base [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .A1 = %A1.decl // CHECK:STDOUT: .A2 = %A2.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %A1.decl: type = class_decl @A1 [concrete = constants.%A1] {} {} // CHECK:STDOUT: %A2.decl: type = class_decl @A2 [concrete = constants.%A2] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @A1 { // CHECK:STDOUT: %A1.F.decl: %A1.F.type = fn_decl @A1.F [concrete = constants.%A1.F] { // CHECK:STDOUT: %self.patt: %pattern_type.258 = value_binding_pattern self [concrete] // CHECK:STDOUT: %self.param_patt: %pattern_type.258 = value_param_pattern %self.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %self.param: %A1 = value_param call_param0 // CHECK:STDOUT: %Self.ref: type = name_ref Self, constants.%A1 [concrete = constants.%A1] // CHECK:STDOUT: %self: %A1 = value_binding self, %self.param // CHECK:STDOUT: } // CHECK:STDOUT: %vtable_decl: ref %ptr.454 = vtable_decl @A1.vtable [concrete = constants.%A1.vtable_decl] // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%struct_type.vptr [concrete = constants.%complete_type.513] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: vtable_decl = %vtable_decl // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%A1 // CHECK:STDOUT: .F = %A1.F.decl // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @A2 { // CHECK:STDOUT: %A1.ref: type = name_ref A1, file.%A1.decl [concrete = constants.%A1] // CHECK:STDOUT: %.loc9: %A2.elem = base_decl %A1.ref, element0 [concrete] // CHECK:STDOUT: %A2.F.decl: %A2.F.type = fn_decl @A2.F [concrete = constants.%A2.F] { // CHECK:STDOUT: %self.patt: %pattern_type.f23 = value_binding_pattern self [concrete] // CHECK:STDOUT: %self.param_patt: %pattern_type.f23 = value_param_pattern %self.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %self.param: %A2 = value_param call_param0 // CHECK:STDOUT: %Self.ref: type = name_ref Self, constants.%A2 [concrete = constants.%A2] // CHECK:STDOUT: %self: %A2 = value_binding self, %self.param // CHECK:STDOUT: } // CHECK:STDOUT: %vtable_decl: ref %ptr.454 = vtable_decl @A2.vtable [concrete = constants.%A2.vtable_decl] // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%struct_type.base [concrete = constants.%complete_type.0a6] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: vtable_decl = %vtable_decl // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%A2 // CHECK:STDOUT: .A1 = // CHECK:STDOUT: .base = %.loc9 // CHECK:STDOUT: .F = %A2.F.decl // CHECK:STDOUT: extend %A1.ref // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: vtable @A1.vtable { // CHECK:STDOUT: @A1.%A1.F.decl // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: vtable @A2.vtable { // CHECK:STDOUT: @A2.%A2.F.decl // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: virtual fn @A1.F(%self.param: %A1); // CHECK:STDOUT: // CHECK:STDOUT: override fn @A2.F(%self.param: %A2); // CHECK:STDOUT: // CHECK:STDOUT: --- impl_base.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %B1: type = class_type @B1 [concrete] // CHECK:STDOUT: %pattern_type.748: type = pattern_type %B1 [concrete] // CHECK:STDOUT: %B1.F.type: type = fn_type @B1.F [concrete] // CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] // CHECK:STDOUT: %B1.F: %B1.F.type = struct_value () [concrete] // CHECK:STDOUT: %ptr.454: type = ptr_type [concrete] // CHECK:STDOUT: %B1.vtable_decl: ref %ptr.454 = vtable_decl @B1.vtable [concrete] // CHECK:STDOUT: %struct_type.vptr: type = struct_type {.: %ptr.454} [concrete] // CHECK:STDOUT: %complete_type.513: = complete_type_witness %struct_type.vptr [concrete] // CHECK:STDOUT: %B2: type = class_type @B2 [concrete] // CHECK:STDOUT: %B2.elem: type = unbound_element_type %B2, %B1 [concrete] // CHECK:STDOUT: %pattern_type.8b5: type = pattern_type %B2 [concrete] // CHECK:STDOUT: %B2.F.type: type = fn_type @B2.F [concrete] // CHECK:STDOUT: %B2.F: %B2.F.type = struct_value () [concrete] // CHECK:STDOUT: %B2.vtable_decl: ref %ptr.454 = vtable_decl @B2.vtable [concrete] // CHECK:STDOUT: %struct_type.base.056: type = struct_type {.base: %B1} [concrete] // CHECK:STDOUT: %complete_type.4f8: = complete_type_witness %struct_type.base.056 [concrete] // CHECK:STDOUT: %C: type = class_type @C [concrete] // CHECK:STDOUT: %C.elem: type = unbound_element_type %C, %B2 [concrete] // CHECK:STDOUT: %pattern_type.7c7: type = pattern_type %C [concrete] // CHECK:STDOUT: %C.F.type: type = fn_type @C.F [concrete] // CHECK:STDOUT: %C.F: %C.F.type = struct_value () [concrete] // CHECK:STDOUT: %C.vtable_decl: ref %ptr.454 = vtable_decl @C.vtable [concrete] // CHECK:STDOUT: %struct_type.base.812: type = struct_type {.base: %B2} [concrete] // CHECK:STDOUT: %complete_type.909: = complete_type_witness %struct_type.base.812 [concrete] // CHECK:STDOUT: %Use.type: type = fn_type @Use [concrete] // CHECK:STDOUT: %Use: %Use.type = struct_value () [concrete] // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %empty_struct: %empty_struct_type = struct_value () [concrete] // CHECK:STDOUT: %B1.vtable_ptr: ref %ptr.454 = vtable_ptr @B1.vtable [concrete] // CHECK:STDOUT: %B1.val.d17: %B1 = struct_value (%B1.vtable_ptr) [concrete] // CHECK:STDOUT: %struct_type.base.f5e: type = struct_type {.base: %empty_struct_type} [concrete] // CHECK:STDOUT: %struct.6b1: %struct_type.base.f5e = struct_value (%empty_struct) [concrete] // CHECK:STDOUT: %.5b3: type = partial_type %B1 [concrete] // CHECK:STDOUT: %uninit: %ptr.454 = uninitialized_value [concrete] // CHECK:STDOUT: %struct.571: %.5b3 = struct_value (%uninit) [concrete] // CHECK:STDOUT: %B1.val.fd5: %B1 = struct_value (%uninit) [concrete] // CHECK:STDOUT: %B2.val: %B2 = struct_value (%B1.val.fd5) [concrete] // CHECK:STDOUT: %B2.vtable_ptr: ref %ptr.454 = vtable_ptr @B2.vtable [concrete] // CHECK:STDOUT: %struct_type.base.a0c: type = struct_type {.base: %struct_type.base.f5e} [concrete] // CHECK:STDOUT: %struct.83e: %struct_type.base.a0c = struct_value (%struct.6b1) [concrete] // CHECK:STDOUT: %.312: type = partial_type %B2 [concrete] // CHECK:STDOUT: %struct.0cd: %.312 = struct_value (%B1.val.fd5) [concrete] // CHECK:STDOUT: %C.val: %C = struct_value (%B2.val) [concrete] // CHECK:STDOUT: %C.vtable_ptr: ref %ptr.454 = vtable_ptr @C.vtable [concrete] // CHECK:STDOUT: %Destroy.type: type = facet_type <@Destroy> [concrete] // CHECK:STDOUT: %Destroy.Op.type.bae255.1: type = fn_type @Destroy.Op.loc21 [concrete] // CHECK:STDOUT: %Destroy.Op.651ba6.1: %Destroy.Op.type.bae255.1 = struct_value () [concrete] // CHECK:STDOUT: %Destroy.Op.type.bae255.2: type = fn_type @Destroy.Op.loc20 [concrete] // CHECK:STDOUT: %Destroy.Op.651ba6.2: %Destroy.Op.type.bae255.2 = struct_value () [concrete] // CHECK:STDOUT: %Destroy.Op.type.bae255.3: type = fn_type @Destroy.Op.loc19 [concrete] // CHECK:STDOUT: %Destroy.Op.651ba6.3: %Destroy.Op.type.bae255.3 = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: .Destroy = %Core.Destroy // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: %Core.Destroy: type = import_ref Core//prelude/parts/destroy, Destroy, loaded [concrete = constants.%Destroy.type] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .B1 = %B1.decl // CHECK:STDOUT: .B2 = %B2.decl // CHECK:STDOUT: .C = %C.decl // CHECK:STDOUT: .Use = %Use.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %B1.decl: type = class_decl @B1 [concrete = constants.%B1] {} {} // CHECK:STDOUT: %B2.decl: type = class_decl @B2 [concrete = constants.%B2] {} {} // CHECK:STDOUT: %C.decl: type = class_decl @C [concrete = constants.%C] {} {} // CHECK:STDOUT: %Use.decl: %Use.type = fn_decl @Use [concrete = constants.%Use] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @B1 { // CHECK:STDOUT: %B1.F.decl: %B1.F.type = fn_decl @B1.F [concrete = constants.%B1.F] { // CHECK:STDOUT: %self.patt: %pattern_type.748 = value_binding_pattern self [concrete] // CHECK:STDOUT: %self.param_patt: %pattern_type.748 = value_param_pattern %self.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %self.param: %B1 = value_param call_param0 // CHECK:STDOUT: %Self.ref: type = name_ref Self, constants.%B1 [concrete = constants.%B1] // CHECK:STDOUT: %self: %B1 = value_binding self, %self.param // CHECK:STDOUT: } // CHECK:STDOUT: %vtable_decl: ref %ptr.454 = vtable_decl @B1.vtable [concrete = constants.%B1.vtable_decl] // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%struct_type.vptr [concrete = constants.%complete_type.513] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: vtable_decl = %vtable_decl // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%B1 // CHECK:STDOUT: .F = %B1.F.decl // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @B2 { // CHECK:STDOUT: %B1.ref: type = name_ref B1, file.%B1.decl [concrete = constants.%B1] // CHECK:STDOUT: %.loc9: %B2.elem = base_decl %B1.ref, element0 [concrete] // CHECK:STDOUT: %B2.F.decl: %B2.F.type = fn_decl @B2.F [concrete = constants.%B2.F] { // CHECK:STDOUT: %self.patt: %pattern_type.8b5 = value_binding_pattern self [concrete] // CHECK:STDOUT: %self.param_patt: %pattern_type.8b5 = value_param_pattern %self.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %self.param: %B2 = value_param call_param0 // CHECK:STDOUT: %Self.ref: type = name_ref Self, constants.%B2 [concrete = constants.%B2] // CHECK:STDOUT: %self: %B2 = value_binding self, %self.param // CHECK:STDOUT: } // CHECK:STDOUT: %vtable_decl: ref %ptr.454 = vtable_decl @B2.vtable [concrete = constants.%B2.vtable_decl] // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%struct_type.base.056 [concrete = constants.%complete_type.4f8] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: vtable_decl = %vtable_decl // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%B2 // CHECK:STDOUT: .B1 = // CHECK:STDOUT: .base = %.loc9 // CHECK:STDOUT: .F = %B2.F.decl // CHECK:STDOUT: extend %B1.ref // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @C { // CHECK:STDOUT: %B2.ref: type = name_ref B2, file.%B2.decl [concrete = constants.%B2] // CHECK:STDOUT: %.loc14: %C.elem = base_decl %B2.ref, element0 [concrete] // CHECK:STDOUT: %C.F.decl: %C.F.type = fn_decl @C.F [concrete = constants.%C.F] { // CHECK:STDOUT: %self.patt: %pattern_type.7c7 = value_binding_pattern self [concrete] // CHECK:STDOUT: %self.param_patt: %pattern_type.7c7 = value_param_pattern %self.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %self.param: %C = value_param call_param0 // CHECK:STDOUT: %Self.ref: type = name_ref Self, constants.%C [concrete = constants.%C] // CHECK:STDOUT: %self: %C = value_binding self, %self.param // CHECK:STDOUT: } // CHECK:STDOUT: %vtable_decl: ref %ptr.454 = vtable_decl @C.vtable [concrete = constants.%C.vtable_decl] // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%struct_type.base.812 [concrete = constants.%complete_type.909] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: vtable_decl = %vtable_decl // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%C // CHECK:STDOUT: .B2 = // CHECK:STDOUT: .base = %.loc14 // CHECK:STDOUT: .F = %C.F.decl // CHECK:STDOUT: extend %B2.ref // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: vtable @B1.vtable { // CHECK:STDOUT: @B1.%B1.F.decl // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: vtable @B2.vtable { // CHECK:STDOUT: @B2.%B2.F.decl // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: vtable @C.vtable { // CHECK:STDOUT: @C.%C.F.decl // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: virtual fn @B1.F(%self.param: %B1); // CHECK:STDOUT: // CHECK:STDOUT: override fn @B2.F(%self.param: %B2); // CHECK:STDOUT: // CHECK:STDOUT: override fn @C.F(%self.param: %C); // CHECK:STDOUT: // CHECK:STDOUT: fn @Use() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %b1.patt: %pattern_type.748 = ref_binding_pattern b1 [concrete] // CHECK:STDOUT: %b1.var_patt: %pattern_type.748 = var_pattern %b1.patt [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %b1.var: ref %B1 = var %b1.var_patt // CHECK:STDOUT: %.loc19_24.1: %empty_struct_type = struct_literal () [concrete = constants.%empty_struct] // CHECK:STDOUT: %.loc19_24.2: ref %ptr.454 = class_element_access %b1.var, element0 // CHECK:STDOUT: %B1.vtable_ptr: ref %ptr.454 = vtable_ptr @B1.vtable [concrete = constants.%B1.vtable_ptr] // CHECK:STDOUT: %.loc19_24.3: init %ptr.454 to %.loc19_24.2 = in_place_init %B1.vtable_ptr [concrete = constants.%B1.vtable_ptr] // CHECK:STDOUT: %.loc19_24.4: init %B1 to %b1.var = class_init (%.loc19_24.3) [concrete = constants.%B1.val.d17] // CHECK:STDOUT: %.loc19_3: init %B1 = converted %.loc19_24.1, %.loc19_24.4 [concrete = constants.%B1.val.d17] // CHECK:STDOUT: assign %b1.var, %.loc19_3 // CHECK:STDOUT: %B1.ref: type = name_ref B1, file.%B1.decl [concrete = constants.%B1] // CHECK:STDOUT: %b1: ref %B1 = ref_binding b1, %b1.var // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %b2.patt: %pattern_type.8b5 = ref_binding_pattern b2 [concrete] // CHECK:STDOUT: %b2.var_patt: %pattern_type.8b5 = var_pattern %b2.patt [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %b2.var: ref %B2 = var %b2.var_patt // CHECK:STDOUT: %.loc20_33.1: %empty_struct_type = struct_literal () [concrete = constants.%empty_struct] // CHECK:STDOUT: %.loc20_34.1: %struct_type.base.f5e = struct_literal (%.loc20_33.1) [concrete = constants.%struct.6b1] // CHECK:STDOUT: %.loc20_34.2: ref %.5b3 = class_element_access %b2.var, element0 // CHECK:STDOUT: %.loc20_33.2: ref %ptr.454 = class_element_access %.loc20_34.2, element0 // CHECK:STDOUT: %uninit.loc20: %ptr.454 = uninitialized_value [concrete = constants.%uninit] // CHECK:STDOUT: %.loc20_33.3: init %ptr.454 to %.loc20_33.2 = in_place_init %uninit.loc20 [concrete = constants.%uninit] // CHECK:STDOUT: %.loc20_33.4: init %.5b3 to %.loc20_34.2 = class_init (%.loc20_33.3) [concrete = constants.%struct.571] // CHECK:STDOUT: %.loc20_34.3: init %.5b3 = converted %.loc20_33.1, %.loc20_33.4 [concrete = constants.%struct.571] // CHECK:STDOUT: %.loc20_34.4: init %B1 = as_compatible %.loc20_34.3 [concrete = constants.%B1.val.fd5] // CHECK:STDOUT: %.loc20_34.5: init %B2 to %b2.var = class_init (%.loc20_34.4) [concrete = constants.%B2.val] // CHECK:STDOUT: %B2.vtable_ptr: ref %ptr.454 = vtable_ptr @B2.vtable [concrete = constants.%B2.vtable_ptr] // CHECK:STDOUT: %.loc20_34.6: ref %B1 = class_element_access %b2.var, element0 // CHECK:STDOUT: %.loc20_34.7: ref %ptr.454 = class_element_access %.loc20_34.6, element0 // CHECK:STDOUT: %.loc20_34.8: init %ptr.454 to %.loc20_34.7 = in_place_init %B2.vtable_ptr [concrete = constants.%B2.vtable_ptr] // CHECK:STDOUT: %.loc20_34.9: init %B2 = update_init %.loc20_34.5, %.loc20_34.8 // CHECK:STDOUT: %.loc20_3: init %B2 = converted %.loc20_34.1, %.loc20_34.9 // CHECK:STDOUT: assign %b2.var, %.loc20_3 // CHECK:STDOUT: %B2.ref: type = name_ref B2, file.%B2.decl [concrete = constants.%B2] // CHECK:STDOUT: %b2: ref %B2 = ref_binding b2, %b2.var // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %c.patt: %pattern_type.7c7 = ref_binding_pattern c [concrete] // CHECK:STDOUT: %c.var_patt: %pattern_type.7c7 = var_pattern %c.patt [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %c.var: ref %C = var %c.var_patt // CHECK:STDOUT: %.loc21_40.1: %empty_struct_type = struct_literal () [concrete = constants.%empty_struct] // CHECK:STDOUT: %.loc21_41.1: %struct_type.base.f5e = struct_literal (%.loc21_40.1) [concrete = constants.%struct.6b1] // CHECK:STDOUT: %.loc21_42.1: %struct_type.base.a0c = struct_literal (%.loc21_41.1) [concrete = constants.%struct.83e] // CHECK:STDOUT: %.loc21_42.2: ref %.312 = class_element_access %c.var, element0 // CHECK:STDOUT: %.loc21_41.2: ref %.5b3 = class_element_access %.loc21_42.2, element0 // CHECK:STDOUT: %.loc21_40.2: ref %ptr.454 = class_element_access %.loc21_41.2, element0 // CHECK:STDOUT: %uninit.loc21: %ptr.454 = uninitialized_value [concrete = constants.%uninit] // CHECK:STDOUT: %.loc21_40.3: init %ptr.454 to %.loc21_40.2 = in_place_init %uninit.loc21 [concrete = constants.%uninit] // CHECK:STDOUT: %.loc21_40.4: init %.5b3 to %.loc21_41.2 = class_init (%.loc21_40.3) [concrete = constants.%struct.571] // CHECK:STDOUT: %.loc21_41.3: init %.5b3 = converted %.loc21_40.1, %.loc21_40.4 [concrete = constants.%struct.571] // CHECK:STDOUT: %.loc21_41.4: init %B1 = as_compatible %.loc21_41.3 [concrete = constants.%B1.val.fd5] // CHECK:STDOUT: %.loc21_41.5: init %.312 to %.loc21_42.2 = class_init (%.loc21_41.4) [concrete = constants.%struct.0cd] // CHECK:STDOUT: %.loc21_42.3: init %.312 = converted %.loc21_41.1, %.loc21_41.5 [concrete = constants.%struct.0cd] // CHECK:STDOUT: %.loc21_42.4: init %B2 = as_compatible %.loc21_42.3 [concrete = constants.%B2.val] // CHECK:STDOUT: %.loc21_42.5: init %C to %c.var = class_init (%.loc21_42.4) [concrete = constants.%C.val] // CHECK:STDOUT: %C.vtable_ptr: ref %ptr.454 = vtable_ptr @C.vtable [concrete = constants.%C.vtable_ptr] // CHECK:STDOUT: %.loc21_42.6: ref %B2 = class_element_access %c.var, element0 // CHECK:STDOUT: %.loc21_42.7: ref %B1 = class_element_access %.loc21_42.6, element0 // CHECK:STDOUT: %.loc21_42.8: ref %ptr.454 = class_element_access %.loc21_42.7, element0 // CHECK:STDOUT: %.loc21_42.9: init %ptr.454 to %.loc21_42.8 = in_place_init %C.vtable_ptr [concrete = constants.%C.vtable_ptr] // CHECK:STDOUT: %.loc21_42.10: init %C = update_init %.loc21_42.5, %.loc21_42.9 // CHECK:STDOUT: %.loc21_3: init %C = converted %.loc21_42.1, %.loc21_42.10 // CHECK:STDOUT: assign %c.var, %.loc21_3 // CHECK:STDOUT: %C.ref: type = name_ref C, file.%C.decl [concrete = constants.%C] // CHECK:STDOUT: %c: ref %C = ref_binding c, %c.var // CHECK:STDOUT: %Destroy.Op.bound.loc21: = bound_method %c.var, constants.%Destroy.Op.651ba6.1 // CHECK:STDOUT: %Destroy.Op.call.loc21: init %empty_tuple.type = call %Destroy.Op.bound.loc21(%c.var) // CHECK:STDOUT: %Destroy.Op.bound.loc20: = bound_method %b2.var, constants.%Destroy.Op.651ba6.2 // CHECK:STDOUT: %Destroy.Op.call.loc20: init %empty_tuple.type = call %Destroy.Op.bound.loc20(%b2.var) // CHECK:STDOUT: %Destroy.Op.bound.loc19: = bound_method %b1.var, constants.%Destroy.Op.651ba6.3 // CHECK:STDOUT: %Destroy.Op.call.loc19: init %empty_tuple.type = call %Destroy.Op.bound.loc19(%b1.var) // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @Destroy.Op.loc21(%self.param: ref %C) = "no_op"; // CHECK:STDOUT: // CHECK:STDOUT: fn @Destroy.Op.loc20(%self.param: ref %B2) = "no_op"; // CHECK:STDOUT: // CHECK:STDOUT: fn @Destroy.Op.loc19(%self.param: ref %B1) = "no_op"; // CHECK:STDOUT: // CHECK:STDOUT: --- fail_modifiers.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %C: type = class_type @C [concrete] // CHECK:STDOUT: %pattern_type: type = pattern_type %C [concrete] // CHECK:STDOUT: %C.F.type: type = fn_type @C.F [concrete] // CHECK:STDOUT: %C.F: %C.F.type = struct_value () [concrete] // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %complete_type: = complete_type_witness %empty_struct_type [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .C = %C.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %C.decl: type = class_decl @C [concrete = constants.%C] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @C { // CHECK:STDOUT: %C.F.decl: %C.F.type = fn_decl @C.F [concrete = constants.%C.F] { // CHECK:STDOUT: %self.patt: %pattern_type = value_binding_pattern self [concrete] // CHECK:STDOUT: %self.param_patt: %pattern_type = value_param_pattern %self.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %self.param: %C = value_param call_param0 // CHECK:STDOUT: %Self.ref: type = name_ref Self, constants.%C [concrete = constants.%C] // CHECK:STDOUT: %self: %C = value_binding self, %self.param // CHECK:STDOUT: } // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%C // CHECK:STDOUT: .F = %C.F.decl // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: override fn @C.F(%self.param: %C); // CHECK:STDOUT: // CHECK:STDOUT: --- init_members.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %Base: type = class_type @Base [concrete] // CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [concrete] // CHECK:STDOUT: %Int.type: type = generic_class_type @Int [concrete] // CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] // CHECK:STDOUT: %Int.generic: %Int.type = struct_value () [concrete] // CHECK:STDOUT: %N: Core.IntLiteral = symbolic_binding N, 0 [symbolic] // CHECK:STDOUT: %i32: type = class_type @Int, @Int(%int_32) [concrete] // CHECK:STDOUT: %Base.elem: type = unbound_element_type %Base, %i32 [concrete] // CHECK:STDOUT: %pattern_type.101: type = pattern_type %Base [concrete] // CHECK:STDOUT: %Base.F.type: type = fn_type @Base.F [concrete] // CHECK:STDOUT: %Base.F: %Base.F.type = struct_value () [concrete] // CHECK:STDOUT: %ptr.454: type = ptr_type [concrete] // CHECK:STDOUT: %Base.vtable_decl: ref %ptr.454 = vtable_decl @Base.vtable [concrete] // CHECK:STDOUT: %struct_type.vptr.m1.m2: type = struct_type {.: %ptr.454, .m1: %i32, .m2: %i32} [concrete] // CHECK:STDOUT: %complete_type.cf7: = complete_type_witness %struct_type.vptr.m1.m2 [concrete] // CHECK:STDOUT: %F.type: type = fn_type @F [concrete] // CHECK:STDOUT: %F: %F.type = struct_value () [concrete] // CHECK:STDOUT: %pattern_type.7ce: type = pattern_type %i32 [concrete] // CHECK:STDOUT: %int_3.1ba: Core.IntLiteral = int_value 3 [concrete] // CHECK:STDOUT: %ImplicitAs.type.cc7: type = generic_interface_type @ImplicitAs [concrete] // CHECK:STDOUT: %ImplicitAs.generic: %ImplicitAs.type.cc7 = struct_value () [concrete] // CHECK:STDOUT: %ImplicitAs.type.e8c: type = facet_type <@ImplicitAs, @ImplicitAs(%i32)> [concrete] // CHECK:STDOUT: %To: Core.IntLiteral = symbolic_binding To, 0 [symbolic] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.4e6: type = fn_type @Core.IntLiteral.as.ImplicitAs.impl.Convert, @Core.IntLiteral.as.ImplicitAs.impl(%To) [symbolic] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.3c2: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.4e6 = struct_value () [symbolic] // CHECK:STDOUT: %ImplicitAs.impl_witness.6bc: = impl_witness imports.%ImplicitAs.impl_witness_table.74f, @Core.IntLiteral.as.ImplicitAs.impl(%int_32) [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.e0d: type = fn_type @Core.IntLiteral.as.ImplicitAs.impl.Convert, @Core.IntLiteral.as.ImplicitAs.impl(%int_32) [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.e0d = struct_value () [concrete] // CHECK:STDOUT: %ImplicitAs.facet: %ImplicitAs.type.e8c = facet_value Core.IntLiteral, (%ImplicitAs.impl_witness.6bc) [concrete] // CHECK:STDOUT: %ImplicitAs.WithSelf.Convert.type.b37: type = fn_type @ImplicitAs.WithSelf.Convert, @ImplicitAs.WithSelf(%i32, %ImplicitAs.facet) [concrete] // CHECK:STDOUT: %.545: type = fn_type_with_self_type %ImplicitAs.WithSelf.Convert.type.b37, %ImplicitAs.facet [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.bound.061: = bound_method %int_3.1ba, %Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5 [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn: = specific_function %Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5, @Core.IntLiteral.as.ImplicitAs.impl.Convert(%int_32) [concrete] // CHECK:STDOUT: %bound_method.fa7: = bound_method %int_3.1ba, %Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn [concrete] // CHECK:STDOUT: %int_3.822: %i32 = int_value 3 [concrete] // CHECK:STDOUT: %struct_type.m2.m1.68c: type = struct_type {.m2: %i32, .m1: %i32} [concrete] // CHECK:STDOUT: %Base.vtable_ptr: ref %ptr.454 = vtable_ptr @Base.vtable [concrete] // CHECK:STDOUT: %Copy.type: type = facet_type <@Copy> [concrete] // CHECK:STDOUT: %Int.as.Copy.impl.Op.type.824: type = fn_type @Int.as.Copy.impl.Op, @Int.as.Copy.impl(%N) [symbolic] // CHECK:STDOUT: %Int.as.Copy.impl.Op.9b9: %Int.as.Copy.impl.Op.type.824 = struct_value () [symbolic] // CHECK:STDOUT: %Copy.impl_witness.f17: = impl_witness imports.%Copy.impl_witness_table.e76, @Int.as.Copy.impl(%int_32) [concrete] // CHECK:STDOUT: %Int.as.Copy.impl.Op.type.546: type = fn_type @Int.as.Copy.impl.Op, @Int.as.Copy.impl(%int_32) [concrete] // CHECK:STDOUT: %Int.as.Copy.impl.Op.664: %Int.as.Copy.impl.Op.type.546 = struct_value () [concrete] // CHECK:STDOUT: %Copy.facet: %Copy.type = facet_value %i32, (%Copy.impl_witness.f17) [concrete] // CHECK:STDOUT: %Copy.WithSelf.Op.type.081: type = fn_type @Copy.WithSelf.Op, @Copy.WithSelf(%Copy.facet) [concrete] // CHECK:STDOUT: %.8e2: type = fn_type_with_self_type %Copy.WithSelf.Op.type.081, %Copy.facet [concrete] // CHECK:STDOUT: %Int.as.Copy.impl.Op.specific_fn: = specific_function %Int.as.Copy.impl.Op.664, @Int.as.Copy.impl.Op(%int_32) [concrete] // CHECK:STDOUT: %int_5.64b: Core.IntLiteral = int_value 5 [concrete] // CHECK:STDOUT: %struct_type.m2.m1.5f2: type = struct_type {.m2: Core.IntLiteral, .m1: Core.IntLiteral} [concrete] // CHECK:STDOUT: %struct: %struct_type.m2.m1.5f2 = struct_value (%int_3.1ba, %int_5.64b) [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.bound.005: = bound_method %int_5.64b, %Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5 [concrete] // CHECK:STDOUT: %bound_method.e9d: = bound_method %int_5.64b, %Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn [concrete] // CHECK:STDOUT: %int_5.0f6: %i32 = int_value 5 [concrete] // CHECK:STDOUT: %Base.val: %Base = struct_value (%Base.vtable_ptr, %int_5.0f6, %int_3.822) [concrete] // CHECK:STDOUT: %int_4.0c1: Core.IntLiteral = int_value 4 [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.bound.f0c: = bound_method %int_4.0c1, %Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5 [concrete] // CHECK:STDOUT: %bound_method.6d7: = bound_method %int_4.0c1, %Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn [concrete] // CHECK:STDOUT: %int_4.940: %i32 = int_value 4 [concrete] // CHECK:STDOUT: %Destroy.type: type = facet_type <@Destroy> [concrete] // CHECK:STDOUT: %Destroy.Op.type.bae255.1: type = fn_type @Destroy.Op.loc14 [concrete] // CHECK:STDOUT: %Destroy.Op.651ba6.1: %Destroy.Op.type.bae255.1 = struct_value () [concrete] // CHECK:STDOUT: %Destroy.Op.type.bae255.2: type = fn_type @Destroy.Op.loc12 [concrete] // CHECK:STDOUT: %Destroy.Op.651ba6.2: %Destroy.Op.type.bae255.2 = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: .Int = %Core.Int // CHECK:STDOUT: .ImplicitAs = %Core.ImplicitAs // CHECK:STDOUT: .Copy = %Core.Copy // CHECK:STDOUT: .Destroy = %Core.Destroy // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: %Core.Int: %Int.type = import_ref Core//prelude/parts/int, Int, loaded [concrete = constants.%Int.generic] // CHECK:STDOUT: %Core.ImplicitAs: %ImplicitAs.type.cc7 = import_ref Core//prelude/parts/as, ImplicitAs, loaded [concrete = constants.%ImplicitAs.generic] // CHECK:STDOUT: %Core.import_ref.42d: @Core.IntLiteral.as.ImplicitAs.impl.%Core.IntLiteral.as.ImplicitAs.impl.Convert.type (%Core.IntLiteral.as.ImplicitAs.impl.Convert.type.4e6) = import_ref Core//prelude/parts/int, loc{{\d+_\d+}}, loaded [symbolic = @Core.IntLiteral.as.ImplicitAs.impl.%Core.IntLiteral.as.ImplicitAs.impl.Convert (constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.3c2)] // CHECK:STDOUT: %ImplicitAs.impl_witness_table.74f = impl_witness_table (%Core.import_ref.42d), @Core.IntLiteral.as.ImplicitAs.impl [concrete] // CHECK:STDOUT: %Core.Copy: type = import_ref Core//prelude/parts/copy, Copy, loaded [concrete = constants.%Copy.type] // CHECK:STDOUT: %Core.import_ref.18d: @Int.as.Copy.impl.%Int.as.Copy.impl.Op.type (%Int.as.Copy.impl.Op.type.824) = import_ref Core//prelude/parts/int, loc{{\d+_\d+}}, loaded [symbolic = @Int.as.Copy.impl.%Int.as.Copy.impl.Op (constants.%Int.as.Copy.impl.Op.9b9)] // CHECK:STDOUT: %Copy.impl_witness_table.e76 = impl_witness_table (%Core.import_ref.18d), @Int.as.Copy.impl [concrete] // CHECK:STDOUT: %Core.Destroy: type = import_ref Core//prelude/parts/destroy, Destroy, loaded [concrete = constants.%Destroy.type] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .Base = %Base.decl // CHECK:STDOUT: .F = %F.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %Base.decl: type = class_decl @Base [concrete = constants.%Base] {} {} // CHECK:STDOUT: %F.decl: %F.type = fn_decl @F [concrete = constants.%F] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @Base { // CHECK:STDOUT: %i32.loc5: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %.loc5: %Base.elem = field_decl m1, element1 [concrete] // CHECK:STDOUT: %i32.loc6: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %.loc6: %Base.elem = field_decl m2, element2 [concrete] // CHECK:STDOUT: %Base.F.decl: %Base.F.type = fn_decl @Base.F [concrete = constants.%Base.F] { // CHECK:STDOUT: %self.patt: %pattern_type.101 = value_binding_pattern self [concrete] // CHECK:STDOUT: %self.param_patt: %pattern_type.101 = value_param_pattern %self.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %self.param: %Base = value_param call_param0 // CHECK:STDOUT: %Self.ref: type = name_ref Self, constants.%Base [concrete = constants.%Base] // CHECK:STDOUT: %self: %Base = value_binding self, %self.param // CHECK:STDOUT: } // CHECK:STDOUT: %vtable_decl: ref %ptr.454 = vtable_decl @Base.vtable [concrete = constants.%Base.vtable_decl] // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%struct_type.vptr.m1.m2 [concrete = constants.%complete_type.cf7] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: vtable_decl = %vtable_decl // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%Base // CHECK:STDOUT: .m1 = %.loc5 // CHECK:STDOUT: .m2 = %.loc6 // CHECK:STDOUT: .F = %Base.F.decl // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: vtable @Base.vtable { // CHECK:STDOUT: @Base.%Base.F.decl // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: virtual fn @Base.F(%self.param: %Base); // CHECK:STDOUT: // CHECK:STDOUT: fn @F() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %i.patt: %pattern_type.7ce = ref_binding_pattern i [concrete] // CHECK:STDOUT: %i.var_patt: %pattern_type.7ce = var_pattern %i.patt [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %i.var: ref %i32 = var %i.var_patt // CHECK:STDOUT: %int_3.loc12: Core.IntLiteral = int_value 3 [concrete = constants.%int_3.1ba] // CHECK:STDOUT: %impl.elem0.loc12: %.545 = impl_witness_access constants.%ImplicitAs.impl_witness.6bc, element0 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5] // CHECK:STDOUT: %bound_method.loc12_3.1: = bound_method %int_3.loc12, %impl.elem0.loc12 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.bound.061] // CHECK:STDOUT: %specific_fn.loc12: = specific_function %impl.elem0.loc12, @Core.IntLiteral.as.ImplicitAs.impl.Convert(constants.%int_32) [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn] // CHECK:STDOUT: %bound_method.loc12_3.2: = bound_method %int_3.loc12, %specific_fn.loc12 [concrete = constants.%bound_method.fa7] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc12: init %i32 = call %bound_method.loc12_3.2(%int_3.loc12) [concrete = constants.%int_3.822] // CHECK:STDOUT: %.loc12: init %i32 = converted %int_3.loc12, %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc12 [concrete = constants.%int_3.822] // CHECK:STDOUT: assign %i.var, %.loc12 // CHECK:STDOUT: %i32: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %i: ref %i32 = ref_binding i, %i.var // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %b1.patt: %pattern_type.101 = ref_binding_pattern b1 [concrete] // CHECK:STDOUT: %b1.var_patt: %pattern_type.101 = var_pattern %b1.patt [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %b1.var: ref %Base = var %b1.var_patt // CHECK:STDOUT: %i.ref.loc13_25: ref %i32 = name_ref i, %i // CHECK:STDOUT: %i.ref.loc13_34: ref %i32 = name_ref i, %i // CHECK:STDOUT: %.loc13_35.1: %struct_type.m2.m1.68c = struct_literal (%i.ref.loc13_25, %i.ref.loc13_34) // CHECK:STDOUT: %.loc13_35.2: ref %ptr.454 = class_element_access %b1.var, element0 // CHECK:STDOUT: %Base.vtable_ptr.loc13: ref %ptr.454 = vtable_ptr @Base.vtable [concrete = constants.%Base.vtable_ptr] // CHECK:STDOUT: %.loc13_35.3: init %ptr.454 to %.loc13_35.2 = in_place_init %Base.vtable_ptr.loc13 [concrete = constants.%Base.vtable_ptr] // CHECK:STDOUT: %.loc13_34: %i32 = acquire_value %i.ref.loc13_34 // CHECK:STDOUT: %impl.elem0.loc13_34: %.8e2 = impl_witness_access constants.%Copy.impl_witness.f17, element0 [concrete = constants.%Int.as.Copy.impl.Op.664] // CHECK:STDOUT: %bound_method.loc13_34.1: = bound_method %.loc13_34, %impl.elem0.loc13_34 // CHECK:STDOUT: %specific_fn.loc13_34: = specific_function %impl.elem0.loc13_34, @Int.as.Copy.impl.Op(constants.%int_32) [concrete = constants.%Int.as.Copy.impl.Op.specific_fn] // CHECK:STDOUT: %bound_method.loc13_34.2: = bound_method %.loc13_34, %specific_fn.loc13_34 // CHECK:STDOUT: %Int.as.Copy.impl.Op.call.loc13_34: init %i32 = call %bound_method.loc13_34.2(%.loc13_34) // CHECK:STDOUT: %.loc13_35.4: ref %i32 = class_element_access %b1.var, element2 // CHECK:STDOUT: %.loc13_35.5: init %i32 to %.loc13_35.4 = in_place_init %Int.as.Copy.impl.Op.call.loc13_34 // CHECK:STDOUT: %.loc13_25: %i32 = acquire_value %i.ref.loc13_25 // CHECK:STDOUT: %impl.elem0.loc13_25: %.8e2 = impl_witness_access constants.%Copy.impl_witness.f17, element0 [concrete = constants.%Int.as.Copy.impl.Op.664] // CHECK:STDOUT: %bound_method.loc13_25.1: = bound_method %.loc13_25, %impl.elem0.loc13_25 // CHECK:STDOUT: %specific_fn.loc13_25: = specific_function %impl.elem0.loc13_25, @Int.as.Copy.impl.Op(constants.%int_32) [concrete = constants.%Int.as.Copy.impl.Op.specific_fn] // CHECK:STDOUT: %bound_method.loc13_25.2: = bound_method %.loc13_25, %specific_fn.loc13_25 // CHECK:STDOUT: %Int.as.Copy.impl.Op.call.loc13_25: init %i32 = call %bound_method.loc13_25.2(%.loc13_25) // CHECK:STDOUT: %.loc13_35.6: ref %i32 = class_element_access %b1.var, element1 // CHECK:STDOUT: %.loc13_35.7: init %i32 to %.loc13_35.6 = in_place_init %Int.as.Copy.impl.Op.call.loc13_25 // CHECK:STDOUT: %.loc13_35.8: init %Base to %b1.var = class_init (%.loc13_35.3, %.loc13_35.5, %.loc13_35.7) // CHECK:STDOUT: %.loc13_3: init %Base = converted %.loc13_35.1, %.loc13_35.8 // CHECK:STDOUT: assign %b1.var, %.loc13_3 // CHECK:STDOUT: %Base.ref.loc13: type = name_ref Base, file.%Base.decl [concrete = constants.%Base] // CHECK:STDOUT: %b1: ref %Base = ref_binding b1, %b1.var // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %b2.patt: %pattern_type.101 = ref_binding_pattern b2 [concrete] // CHECK:STDOUT: %b2.var_patt: %pattern_type.101 = var_pattern %b2.patt [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %b2.var: ref %Base = var %b2.var_patt // CHECK:STDOUT: %int_3.loc14: Core.IntLiteral = int_value 3 [concrete = constants.%int_3.1ba] // CHECK:STDOUT: %int_5: Core.IntLiteral = int_value 5 [concrete = constants.%int_5.64b] // CHECK:STDOUT: %.loc14_42.1: %struct_type.m2.m1.5f2 = struct_literal (%int_3.loc14, %int_5) [concrete = constants.%struct] // CHECK:STDOUT: %.loc14_42.2: ref %ptr.454 = class_element_access %b2.var, element0 // CHECK:STDOUT: %Base.vtable_ptr.loc14: ref %ptr.454 = vtable_ptr @Base.vtable [concrete = constants.%Base.vtable_ptr] // CHECK:STDOUT: %.loc14_42.3: init %ptr.454 to %.loc14_42.2 = in_place_init %Base.vtable_ptr.loc14 [concrete = constants.%Base.vtable_ptr] // CHECK:STDOUT: %impl.elem0.loc14_42.1: %.545 = impl_witness_access constants.%ImplicitAs.impl_witness.6bc, element0 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5] // CHECK:STDOUT: %bound_method.loc14_42.1: = bound_method %int_5, %impl.elem0.loc14_42.1 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.bound.005] // CHECK:STDOUT: %specific_fn.loc14_42.1: = specific_function %impl.elem0.loc14_42.1, @Core.IntLiteral.as.ImplicitAs.impl.Convert(constants.%int_32) [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn] // CHECK:STDOUT: %bound_method.loc14_42.2: = bound_method %int_5, %specific_fn.loc14_42.1 [concrete = constants.%bound_method.e9d] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc14_42.1: init %i32 = call %bound_method.loc14_42.2(%int_5) [concrete = constants.%int_5.0f6] // CHECK:STDOUT: %.loc14_42.4: init %i32 = converted %int_5, %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc14_42.1 [concrete = constants.%int_5.0f6] // CHECK:STDOUT: %.loc14_42.5: ref %i32 = class_element_access %b2.var, element2 // CHECK:STDOUT: %.loc14_42.6: init %i32 to %.loc14_42.5 = in_place_init %.loc14_42.4 [concrete = constants.%int_5.0f6] // CHECK:STDOUT: %impl.elem0.loc14_42.2: %.545 = impl_witness_access constants.%ImplicitAs.impl_witness.6bc, element0 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5] // CHECK:STDOUT: %bound_method.loc14_42.3: = bound_method %int_3.loc14, %impl.elem0.loc14_42.2 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.bound.061] // CHECK:STDOUT: %specific_fn.loc14_42.2: = specific_function %impl.elem0.loc14_42.2, @Core.IntLiteral.as.ImplicitAs.impl.Convert(constants.%int_32) [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn] // CHECK:STDOUT: %bound_method.loc14_42.4: = bound_method %int_3.loc14, %specific_fn.loc14_42.2 [concrete = constants.%bound_method.fa7] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc14_42.2: init %i32 = call %bound_method.loc14_42.4(%int_3.loc14) [concrete = constants.%int_3.822] // CHECK:STDOUT: %.loc14_42.7: init %i32 = converted %int_3.loc14, %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc14_42.2 [concrete = constants.%int_3.822] // CHECK:STDOUT: %.loc14_42.8: ref %i32 = class_element_access %b2.var, element1 // CHECK:STDOUT: %.loc14_42.9: init %i32 to %.loc14_42.8 = in_place_init %.loc14_42.7 [concrete = constants.%int_3.822] // CHECK:STDOUT: %.loc14_42.10: init %Base to %b2.var = class_init (%.loc14_42.3, %.loc14_42.6, %.loc14_42.9) [concrete = constants.%Base.val] // CHECK:STDOUT: %.loc14_3: init %Base = converted %.loc14_42.1, %.loc14_42.10 [concrete = constants.%Base.val] // CHECK:STDOUT: assign %b2.var, %.loc14_3 // CHECK:STDOUT: %Base.ref.loc14: type = name_ref Base, file.%Base.decl [concrete = constants.%Base] // CHECK:STDOUT: %b2: ref %Base = ref_binding b2, %b2.var // CHECK:STDOUT: %b1.ref: ref %Base = name_ref b1, %b1 // CHECK:STDOUT: %m2.ref: %Base.elem = name_ref m2, @Base.%.loc6 [concrete = @Base.%.loc6] // CHECK:STDOUT: %.loc16_5: ref %i32 = class_element_access %b1.ref, element2 // CHECK:STDOUT: %int_4: Core.IntLiteral = int_value 4 [concrete = constants.%int_4.0c1] // CHECK:STDOUT: %impl.elem0.loc16: %.545 = impl_witness_access constants.%ImplicitAs.impl_witness.6bc, element0 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5] // CHECK:STDOUT: %bound_method.loc16_9.1: = bound_method %int_4, %impl.elem0.loc16 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.bound.f0c] // CHECK:STDOUT: %specific_fn.loc16: = specific_function %impl.elem0.loc16, @Core.IntLiteral.as.ImplicitAs.impl.Convert(constants.%int_32) [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn] // CHECK:STDOUT: %bound_method.loc16_9.2: = bound_method %int_4, %specific_fn.loc16 [concrete = constants.%bound_method.6d7] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc16: init %i32 = call %bound_method.loc16_9.2(%int_4) [concrete = constants.%int_4.940] // CHECK:STDOUT: %.loc16_9: init %i32 = converted %int_4, %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc16 [concrete = constants.%int_4.940] // CHECK:STDOUT: assign %.loc16_5, %.loc16_9 // CHECK:STDOUT: %Destroy.Op.bound.loc14: = bound_method %b2.var, constants.%Destroy.Op.651ba6.1 // CHECK:STDOUT: %Destroy.Op.call.loc14: init %empty_tuple.type = call %Destroy.Op.bound.loc14(%b2.var) // CHECK:STDOUT: %Destroy.Op.bound.loc13: = bound_method %b1.var, constants.%Destroy.Op.651ba6.1 // CHECK:STDOUT: %Destroy.Op.call.loc13: init %empty_tuple.type = call %Destroy.Op.bound.loc13(%b1.var) // CHECK:STDOUT: %Destroy.Op.bound.loc12: = bound_method %i.var, constants.%Destroy.Op.651ba6.2 // CHECK:STDOUT: %Destroy.Op.call.loc12: init %empty_tuple.type = call %Destroy.Op.bound.loc12(%i.var) // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @Destroy.Op.loc14(%self.param: ref %Base) = "no_op"; // CHECK:STDOUT: // CHECK:STDOUT: fn @Destroy.Op.loc12(%self.param: ref %i32) = "no_op"; // CHECK:STDOUT: // CHECK:STDOUT: --- fail_impl_without_base_declaration.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %Base: type = class_type @Base [concrete] // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %complete_type.357: = complete_type_witness %empty_struct_type [concrete] // CHECK:STDOUT: %Derived: type = class_type @Derived [concrete] // CHECK:STDOUT: %Derived.elem: type = unbound_element_type %Derived, %Base [concrete] // CHECK:STDOUT: %pattern_type: type = pattern_type %Derived [concrete] // CHECK:STDOUT: %Derived.F.type: type = fn_type @Derived.F [concrete] // CHECK:STDOUT: %Derived.F: %Derived.F.type = struct_value () [concrete] // CHECK:STDOUT: %ptr.454: type = ptr_type [concrete] // CHECK:STDOUT: %Derived.vtable_decl: ref %ptr.454 = vtable_decl @Derived.vtable [concrete] // CHECK:STDOUT: %struct_type.vptr.base: type = struct_type {.: %ptr.454, .base: %Base} [concrete] // CHECK:STDOUT: %complete_type.8aa: = complete_type_witness %struct_type.vptr.base [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .Base = %Base.decl // CHECK:STDOUT: .Derived = %Derived.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %Base.decl: type = class_decl @Base [concrete = constants.%Base] {} {} // CHECK:STDOUT: %Derived.decl: type = class_decl @Derived [concrete = constants.%Derived] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @Base { // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type.357] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%Base // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @Derived { // CHECK:STDOUT: %Base.ref: type = name_ref Base, file.%Base.decl [concrete = constants.%Base] // CHECK:STDOUT: %.loc8: %Derived.elem = base_decl %Base.ref, element1 [concrete] // CHECK:STDOUT: %Derived.F.decl: %Derived.F.type = fn_decl @Derived.F [concrete = constants.%Derived.F] { // CHECK:STDOUT: %self.patt: %pattern_type = value_binding_pattern self [concrete] // CHECK:STDOUT: %self.param_patt: %pattern_type = value_param_pattern %self.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %self.param: %Derived = value_param call_param0 // CHECK:STDOUT: %Self.ref: type = name_ref Self, constants.%Derived [concrete = constants.%Derived] // CHECK:STDOUT: %self: %Derived = value_binding self, %self.param // CHECK:STDOUT: } // CHECK:STDOUT: %vtable_decl: ref %ptr.454 = vtable_decl @Derived.vtable [concrete = constants.%Derived.vtable_decl] // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%struct_type.vptr.base [concrete = constants.%complete_type.8aa] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: vtable_decl = %vtable_decl // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%Derived // CHECK:STDOUT: .Base = // CHECK:STDOUT: .base = %.loc8 // CHECK:STDOUT: .F = %Derived.F.decl // CHECK:STDOUT: extend %Base.ref // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: vtable @Derived.vtable {} // CHECK:STDOUT: // CHECK:STDOUT: override fn @Derived.F(%self.param: %Derived); // CHECK:STDOUT: // CHECK:STDOUT: --- abstract_impl.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %AbstractBase: type = class_type @AbstractBase [concrete] // CHECK:STDOUT: %pattern_type.079: type = pattern_type %AbstractBase [concrete] // CHECK:STDOUT: %AbstractBase.F.type: type = fn_type @AbstractBase.F [concrete] // CHECK:STDOUT: %AbstractBase.F: %AbstractBase.F.type = struct_value () [concrete] // CHECK:STDOUT: %ptr.454: type = ptr_type [concrete] // CHECK:STDOUT: %AbstractBase.vtable_decl: ref %ptr.454 = vtable_decl @AbstractBase.vtable [concrete] // CHECK:STDOUT: %struct_type.vptr: type = struct_type {.: %ptr.454} [concrete] // CHECK:STDOUT: %complete_type.513: = complete_type_witness %struct_type.vptr [concrete] // CHECK:STDOUT: %AbstractIntermediate: type = class_type @AbstractIntermediate [concrete] // CHECK:STDOUT: %AbstractIntermediate.elem: type = unbound_element_type %AbstractIntermediate, %AbstractBase [concrete] // CHECK:STDOUT: %AbstractIntermediate.vtable_decl: ref %ptr.454 = vtable_decl @AbstractIntermediate.vtable [concrete] // CHECK:STDOUT: %struct_type.base.48d: type = struct_type {.base: %AbstractBase} [concrete] // CHECK:STDOUT: %complete_type.cae: = complete_type_witness %struct_type.base.48d [concrete] // CHECK:STDOUT: %Derived: type = class_type @Derived [concrete] // CHECK:STDOUT: %Derived.elem: type = unbound_element_type %Derived, %AbstractIntermediate [concrete] // CHECK:STDOUT: %pattern_type.9f6: type = pattern_type %Derived [concrete] // CHECK:STDOUT: %Derived.F.type: type = fn_type @Derived.F [concrete] // CHECK:STDOUT: %Derived.F: %Derived.F.type = struct_value () [concrete] // CHECK:STDOUT: %Derived.vtable_decl: ref %ptr.454 = vtable_decl @Derived.vtable [concrete] // CHECK:STDOUT: %struct_type.base.493: type = struct_type {.base: %AbstractIntermediate} [concrete] // CHECK:STDOUT: %complete_type.944: = complete_type_witness %struct_type.base.493 [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .AbstractBase = %AbstractBase.decl // CHECK:STDOUT: .AbstractIntermediate = %AbstractIntermediate.decl // CHECK:STDOUT: .Derived = %Derived.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %AbstractBase.decl: type = class_decl @AbstractBase [concrete = constants.%AbstractBase] {} {} // CHECK:STDOUT: %AbstractIntermediate.decl: type = class_decl @AbstractIntermediate [concrete = constants.%AbstractIntermediate] {} {} // CHECK:STDOUT: %Derived.decl: type = class_decl @Derived [concrete = constants.%Derived] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @AbstractBase { // CHECK:STDOUT: %AbstractBase.F.decl: %AbstractBase.F.type = fn_decl @AbstractBase.F [concrete = constants.%AbstractBase.F] { // CHECK:STDOUT: %self.patt: %pattern_type.079 = value_binding_pattern self [concrete] // CHECK:STDOUT: %self.param_patt: %pattern_type.079 = value_param_pattern %self.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %self.param: %AbstractBase = value_param call_param0 // CHECK:STDOUT: %Self.ref: type = name_ref Self, constants.%AbstractBase [concrete = constants.%AbstractBase] // CHECK:STDOUT: %self: %AbstractBase = value_binding self, %self.param // CHECK:STDOUT: } // CHECK:STDOUT: %vtable_decl: ref %ptr.454 = vtable_decl @AbstractBase.vtable [concrete = constants.%AbstractBase.vtable_decl] // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%struct_type.vptr [concrete = constants.%complete_type.513] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: vtable_decl = %vtable_decl // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%AbstractBase // CHECK:STDOUT: .F = %AbstractBase.F.decl // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @AbstractIntermediate { // CHECK:STDOUT: %AbstractBase.ref: type = name_ref AbstractBase, file.%AbstractBase.decl [concrete = constants.%AbstractBase] // CHECK:STDOUT: %.loc9: %AbstractIntermediate.elem = base_decl %AbstractBase.ref, element0 [concrete] // CHECK:STDOUT: %vtable_decl: ref %ptr.454 = vtable_decl @AbstractIntermediate.vtable [concrete = constants.%AbstractIntermediate.vtable_decl] // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%struct_type.base.48d [concrete = constants.%complete_type.cae] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: vtable_decl = %vtable_decl // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%AbstractIntermediate // CHECK:STDOUT: .AbstractBase = // CHECK:STDOUT: .base = %.loc9 // CHECK:STDOUT: extend %AbstractBase.ref // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @Derived { // CHECK:STDOUT: %AbstractIntermediate.ref: type = name_ref AbstractIntermediate, file.%AbstractIntermediate.decl [concrete = constants.%AbstractIntermediate] // CHECK:STDOUT: %.loc13: %Derived.elem = base_decl %AbstractIntermediate.ref, element0 [concrete] // CHECK:STDOUT: %Derived.F.decl: %Derived.F.type = fn_decl @Derived.F [concrete = constants.%Derived.F] { // CHECK:STDOUT: %self.patt: %pattern_type.9f6 = value_binding_pattern self [concrete] // CHECK:STDOUT: %self.param_patt: %pattern_type.9f6 = value_param_pattern %self.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %self.param: %Derived = value_param call_param0 // CHECK:STDOUT: %Self.ref: type = name_ref Self, constants.%Derived [concrete = constants.%Derived] // CHECK:STDOUT: %self: %Derived = value_binding self, %self.param // CHECK:STDOUT: } // CHECK:STDOUT: %vtable_decl: ref %ptr.454 = vtable_decl @Derived.vtable [concrete = constants.%Derived.vtable_decl] // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%struct_type.base.493 [concrete = constants.%complete_type.944] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: vtable_decl = %vtable_decl // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%Derived // CHECK:STDOUT: .AbstractIntermediate = // CHECK:STDOUT: .base = %.loc13 // CHECK:STDOUT: .F = %Derived.F.decl // CHECK:STDOUT: extend %AbstractIntermediate.ref // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: vtable @AbstractBase.vtable { // CHECK:STDOUT: @AbstractBase.%AbstractBase.F.decl // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: vtable @AbstractIntermediate.vtable { // CHECK:STDOUT: constants.%AbstractBase.F // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: vtable @Derived.vtable { // CHECK:STDOUT: @Derived.%Derived.F.decl // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: abstract fn @AbstractBase.F(%self.param: %AbstractBase); // CHECK:STDOUT: // CHECK:STDOUT: override fn @Derived.F(%self.param: %Derived); // CHECK:STDOUT: // CHECK:STDOUT: --- virtual_impl.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %VirtualBase: type = class_type @VirtualBase [concrete] // CHECK:STDOUT: %pattern_type.012: type = pattern_type %VirtualBase [concrete] // CHECK:STDOUT: %VirtualBase.F.type: type = fn_type @VirtualBase.F [concrete] // CHECK:STDOUT: %VirtualBase.F: %VirtualBase.F.type = struct_value () [concrete] // CHECK:STDOUT: %ptr.454: type = ptr_type [concrete] // CHECK:STDOUT: %VirtualBase.vtable_decl: ref %ptr.454 = vtable_decl @VirtualBase.vtable [concrete] // CHECK:STDOUT: %struct_type.vptr: type = struct_type {.: %ptr.454} [concrete] // CHECK:STDOUT: %complete_type.513: = complete_type_witness %struct_type.vptr [concrete] // CHECK:STDOUT: %VirtualIntermediate: type = class_type @VirtualIntermediate [concrete] // CHECK:STDOUT: %VirtualIntermediate.elem: type = unbound_element_type %VirtualIntermediate, %VirtualBase [concrete] // CHECK:STDOUT: %VirtualIntermediate.vtable_decl: ref %ptr.454 = vtable_decl @VirtualIntermediate.vtable [concrete] // CHECK:STDOUT: %struct_type.base.d36: type = struct_type {.base: %VirtualBase} [concrete] // CHECK:STDOUT: %complete_type.e60: = complete_type_witness %struct_type.base.d36 [concrete] // CHECK:STDOUT: %Derived: type = class_type @Derived [concrete] // CHECK:STDOUT: %Derived.elem: type = unbound_element_type %Derived, %VirtualIntermediate [concrete] // CHECK:STDOUT: %pattern_type.9f6: type = pattern_type %Derived [concrete] // CHECK:STDOUT: %Derived.F.type: type = fn_type @Derived.F [concrete] // CHECK:STDOUT: %Derived.F: %Derived.F.type = struct_value () [concrete] // CHECK:STDOUT: %Derived.vtable_decl: ref %ptr.454 = vtable_decl @Derived.vtable [concrete] // CHECK:STDOUT: %struct_type.base.cec: type = struct_type {.base: %VirtualIntermediate} [concrete] // CHECK:STDOUT: %complete_type.4c0: = complete_type_witness %struct_type.base.cec [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .VirtualBase = %VirtualBase.decl // CHECK:STDOUT: .VirtualIntermediate = %VirtualIntermediate.decl // CHECK:STDOUT: .Derived = %Derived.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %VirtualBase.decl: type = class_decl @VirtualBase [concrete = constants.%VirtualBase] {} {} // CHECK:STDOUT: %VirtualIntermediate.decl: type = class_decl @VirtualIntermediate [concrete = constants.%VirtualIntermediate] {} {} // CHECK:STDOUT: %Derived.decl: type = class_decl @Derived [concrete = constants.%Derived] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @VirtualBase { // CHECK:STDOUT: %VirtualBase.F.decl: %VirtualBase.F.type = fn_decl @VirtualBase.F [concrete = constants.%VirtualBase.F] { // CHECK:STDOUT: %self.patt: %pattern_type.012 = value_binding_pattern self [concrete] // CHECK:STDOUT: %self.param_patt: %pattern_type.012 = value_param_pattern %self.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %self.param: %VirtualBase = value_param call_param0 // CHECK:STDOUT: %Self.ref: type = name_ref Self, constants.%VirtualBase [concrete = constants.%VirtualBase] // CHECK:STDOUT: %self: %VirtualBase = value_binding self, %self.param // CHECK:STDOUT: } // CHECK:STDOUT: %vtable_decl: ref %ptr.454 = vtable_decl @VirtualBase.vtable [concrete = constants.%VirtualBase.vtable_decl] // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%struct_type.vptr [concrete = constants.%complete_type.513] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: vtable_decl = %vtable_decl // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%VirtualBase // CHECK:STDOUT: .F = %VirtualBase.F.decl // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @VirtualIntermediate { // CHECK:STDOUT: %VirtualBase.ref: type = name_ref VirtualBase, file.%VirtualBase.decl [concrete = constants.%VirtualBase] // CHECK:STDOUT: %.loc9: %VirtualIntermediate.elem = base_decl %VirtualBase.ref, element0 [concrete] // CHECK:STDOUT: %vtable_decl: ref %ptr.454 = vtable_decl @VirtualIntermediate.vtable [concrete = constants.%VirtualIntermediate.vtable_decl] // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%struct_type.base.d36 [concrete = constants.%complete_type.e60] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: vtable_decl = %vtable_decl // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%VirtualIntermediate // CHECK:STDOUT: .VirtualBase = // CHECK:STDOUT: .base = %.loc9 // CHECK:STDOUT: extend %VirtualBase.ref // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @Derived { // CHECK:STDOUT: %VirtualIntermediate.ref: type = name_ref VirtualIntermediate, file.%VirtualIntermediate.decl [concrete = constants.%VirtualIntermediate] // CHECK:STDOUT: %.loc13: %Derived.elem = base_decl %VirtualIntermediate.ref, element0 [concrete] // CHECK:STDOUT: %Derived.F.decl: %Derived.F.type = fn_decl @Derived.F [concrete = constants.%Derived.F] { // CHECK:STDOUT: %self.patt: %pattern_type.9f6 = value_binding_pattern self [concrete] // CHECK:STDOUT: %self.param_patt: %pattern_type.9f6 = value_param_pattern %self.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %self.param: %Derived = value_param call_param0 // CHECK:STDOUT: %Self.ref: type = name_ref Self, constants.%Derived [concrete = constants.%Derived] // CHECK:STDOUT: %self: %Derived = value_binding self, %self.param // CHECK:STDOUT: } // CHECK:STDOUT: %vtable_decl: ref %ptr.454 = vtable_decl @Derived.vtable [concrete = constants.%Derived.vtable_decl] // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%struct_type.base.cec [concrete = constants.%complete_type.4c0] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: vtable_decl = %vtable_decl // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%Derived // CHECK:STDOUT: .VirtualIntermediate = // CHECK:STDOUT: .base = %.loc13 // CHECK:STDOUT: .F = %Derived.F.decl // CHECK:STDOUT: extend %VirtualIntermediate.ref // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: vtable @VirtualBase.vtable { // CHECK:STDOUT: @VirtualBase.%VirtualBase.F.decl // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: vtable @VirtualIntermediate.vtable { // CHECK:STDOUT: constants.%VirtualBase.F // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: vtable @Derived.vtable { // CHECK:STDOUT: @Derived.%Derived.F.decl // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: virtual fn @VirtualBase.F(%self.param: %VirtualBase); // CHECK:STDOUT: // CHECK:STDOUT: override fn @Derived.F(%self.param: %Derived); // CHECK:STDOUT: // CHECK:STDOUT: --- fail_impl_mismatch.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %Base: type = class_type @Base [concrete] // CHECK:STDOUT: %pattern_type.101: type = pattern_type %Base [concrete] // CHECK:STDOUT: %Base.F.type: type = fn_type @Base.F [concrete] // CHECK:STDOUT: %Base.F: %Base.F.type = struct_value () [concrete] // CHECK:STDOUT: %ptr.454: type = ptr_type [concrete] // CHECK:STDOUT: %Base.vtable_decl: ref %ptr.454 = vtable_decl @Base.vtable [concrete] // CHECK:STDOUT: %struct_type.vptr: type = struct_type {.: %ptr.454} [concrete] // CHECK:STDOUT: %complete_type.513: = complete_type_witness %struct_type.vptr [concrete] // CHECK:STDOUT: %Derived: type = class_type @Derived [concrete] // CHECK:STDOUT: %Derived.elem: type = unbound_element_type %Derived, %Base [concrete] // CHECK:STDOUT: %pattern_type.9f6: type = pattern_type %Derived [concrete] // CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [concrete] // CHECK:STDOUT: %Int.type: type = generic_class_type @Int [concrete] // CHECK:STDOUT: %Int.generic: %Int.type = struct_value () [concrete] // CHECK:STDOUT: %i32: type = class_type @Int, @Int(%int_32) [concrete] // CHECK:STDOUT: %pattern_type.7ce: type = pattern_type %i32 [concrete] // CHECK:STDOUT: %Derived.F.type: type = fn_type @Derived.F [concrete] // CHECK:STDOUT: %Derived.F: %Derived.F.type = struct_value () [concrete] // CHECK:STDOUT: %Derived.vtable_decl: ref %ptr.454 = vtable_decl @Derived.vtable [concrete] // CHECK:STDOUT: %struct_type.base: type = struct_type {.base: %Base} [concrete] // CHECK:STDOUT: %complete_type.5a1: = complete_type_witness %struct_type.base [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: .Int = %Core.Int // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: %Core.Int: %Int.type = import_ref Core//prelude/parts/int, Int, loaded [concrete = constants.%Int.generic] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .Base = %Base.decl // CHECK:STDOUT: .Derived = %Derived.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %Base.decl: type = class_decl @Base [concrete = constants.%Base] {} {} // CHECK:STDOUT: %Derived.decl: type = class_decl @Derived [concrete = constants.%Derived] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @Base { // CHECK:STDOUT: %Base.F.decl: %Base.F.type = fn_decl @Base.F [concrete = constants.%Base.F] { // CHECK:STDOUT: %self.patt: %pattern_type.101 = value_binding_pattern self [concrete] // CHECK:STDOUT: %self.param_patt: %pattern_type.101 = value_param_pattern %self.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %self.param: %Base = value_param call_param0 // CHECK:STDOUT: %Self.ref: type = name_ref Self, constants.%Base [concrete = constants.%Base] // CHECK:STDOUT: %self: %Base = value_binding self, %self.param // CHECK:STDOUT: } // CHECK:STDOUT: %vtable_decl: ref %ptr.454 = vtable_decl @Base.vtable [concrete = constants.%Base.vtable_decl] // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%struct_type.vptr [concrete = constants.%complete_type.513] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: vtable_decl = %vtable_decl // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%Base // CHECK:STDOUT: .F = %Base.F.decl // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @Derived { // CHECK:STDOUT: %Base.ref: type = name_ref Base, file.%Base.decl [concrete = constants.%Base] // CHECK:STDOUT: %.loc9: %Derived.elem = base_decl %Base.ref, element0 [concrete] // CHECK:STDOUT: %Derived.F.decl: %Derived.F.type = fn_decl @Derived.F [concrete = constants.%Derived.F] { // CHECK:STDOUT: %self.patt: %pattern_type.9f6 = value_binding_pattern self [concrete] // CHECK:STDOUT: %self.param_patt: %pattern_type.9f6 = value_param_pattern %self.patt [concrete] // CHECK:STDOUT: %v.patt: %pattern_type.7ce = value_binding_pattern v [concrete] // CHECK:STDOUT: %v.param_patt: %pattern_type.7ce = value_param_pattern %v.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %self.param: %Derived = value_param call_param0 // CHECK:STDOUT: %Self.ref: type = name_ref Self, constants.%Derived [concrete = constants.%Derived] // CHECK:STDOUT: %self: %Derived = value_binding self, %self.param // CHECK:STDOUT: %v.param: %i32 = value_param call_param1 // CHECK:STDOUT: %i32: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %v: %i32 = value_binding v, %v.param // CHECK:STDOUT: } // CHECK:STDOUT: %vtable_decl: ref %ptr.454 = vtable_decl @Derived.vtable [concrete = constants.%Derived.vtable_decl] // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%struct_type.base [concrete = constants.%complete_type.5a1] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: vtable_decl = %vtable_decl // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%Derived // CHECK:STDOUT: .Base = // CHECK:STDOUT: .base = %.loc9 // CHECK:STDOUT: .F = %Derived.F.decl // CHECK:STDOUT: extend %Base.ref // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: vtable @Base.vtable { // CHECK:STDOUT: @Base.%Base.F.decl // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: vtable @Derived.vtable { // CHECK:STDOUT: @Derived.%Derived.F.decl // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: virtual fn @Base.F(%self.param: %Base); // CHECK:STDOUT: // CHECK:STDOUT: override fn @Derived.F(%self.param: %Derived, %v.param: %i32); // CHECK:STDOUT: // CHECK:STDOUT: --- fail_todo_impl_conversion.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %T1: type = class_type @T1 [concrete] // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %complete_type.357: = complete_type_witness %empty_struct_type [concrete] // CHECK:STDOUT: %T2: type = class_type @T2 [concrete] // CHECK:STDOUT: %ImplicitAs.type.cc7: type = generic_interface_type @ImplicitAs [concrete] // CHECK:STDOUT: %ImplicitAs.generic: %ImplicitAs.type.cc7 = struct_value () [concrete] // CHECK:STDOUT: %ImplicitAs.type.8c9: type = facet_type <@ImplicitAs, @ImplicitAs(%T1)> [concrete] // CHECK:STDOUT: %ImplicitAs.impl_witness: = impl_witness @T2.as.ImplicitAs.impl.%ImplicitAs.impl_witness_table [concrete] // CHECK:STDOUT: %pattern_type.b8b: type = pattern_type %T2 [concrete] // CHECK:STDOUT: %.a53: Core.Form = init_form %T1 [concrete] // CHECK:STDOUT: %pattern_type.818: type = pattern_type %T1 [concrete] // CHECK:STDOUT: %T2.as.ImplicitAs.impl.Convert.type: type = fn_type @T2.as.ImplicitAs.impl.Convert [concrete] // CHECK:STDOUT: %T2.as.ImplicitAs.impl.Convert: %T2.as.ImplicitAs.impl.Convert.type = struct_value () [concrete] // CHECK:STDOUT: %empty_struct: %empty_struct_type = struct_value () [concrete] // CHECK:STDOUT: %T1.val: %T1 = struct_value () [concrete] // CHECK:STDOUT: %Base: type = class_type @Base [concrete] // CHECK:STDOUT: %pattern_type.101: type = pattern_type %Base [concrete] // CHECK:STDOUT: %Base.F.type: type = fn_type @Base.F [concrete] // CHECK:STDOUT: %Base.F: %Base.F.type = struct_value () [concrete] // CHECK:STDOUT: %ptr.454: type = ptr_type [concrete] // CHECK:STDOUT: %Base.vtable_decl: ref %ptr.454 = vtable_decl @Base.vtable [concrete] // CHECK:STDOUT: %struct_type.vptr: type = struct_type {.: %ptr.454} [concrete] // CHECK:STDOUT: %complete_type.513: = complete_type_witness %struct_type.vptr [concrete] // CHECK:STDOUT: %Derived: type = class_type @Derived [concrete] // CHECK:STDOUT: %Derived.elem: type = unbound_element_type %Derived, %Base [concrete] // CHECK:STDOUT: %pattern_type.9f6: type = pattern_type %Derived [concrete] // CHECK:STDOUT: %.c13: Core.Form = init_form %T2 [concrete] // CHECK:STDOUT: %Derived.F.type: type = fn_type @Derived.F [concrete] // CHECK:STDOUT: %Derived.F: %Derived.F.type = struct_value () [concrete] // CHECK:STDOUT: %Derived.vtable_decl: ref %ptr.454 = vtable_decl @Derived.vtable [concrete] // CHECK:STDOUT: %struct_type.base: type = struct_type {.base: %Base} [concrete] // CHECK:STDOUT: %complete_type.5a1: = complete_type_witness %struct_type.base [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: .ImplicitAs = %Core.ImplicitAs // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: %Core.ImplicitAs: %ImplicitAs.type.cc7 = import_ref Core//prelude/parts/as, ImplicitAs, loaded [concrete = constants.%ImplicitAs.generic] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .T1 = %T1.decl // CHECK:STDOUT: .T2 = %T2.decl // CHECK:STDOUT: .Base = %Base.decl // CHECK:STDOUT: .Derived = %Derived.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %T1.decl: type = class_decl @T1 [concrete = constants.%T1] {} {} // CHECK:STDOUT: %T2.decl: type = class_decl @T2 [concrete = constants.%T2] {} {} // CHECK:STDOUT: impl_decl @T2.as.ImplicitAs.impl [concrete] {} { // CHECK:STDOUT: %T2.ref: type = name_ref T2, file.%T2.decl [concrete = constants.%T2] // CHECK:STDOUT: %Core.ref: = name_ref Core, imports.%Core [concrete = imports.%Core] // CHECK:STDOUT: %ImplicitAs.ref: %ImplicitAs.type.cc7 = name_ref ImplicitAs, imports.%Core.ImplicitAs [concrete = constants.%ImplicitAs.generic] // CHECK:STDOUT: %T1.ref: type = name_ref T1, file.%T1.decl [concrete = constants.%T1] // CHECK:STDOUT: %ImplicitAs.type: type = facet_type <@ImplicitAs, @ImplicitAs(constants.%T1)> [concrete = constants.%ImplicitAs.type.8c9] // CHECK:STDOUT: } // CHECK:STDOUT: %Base.decl: type = class_decl @Base [concrete = constants.%Base] {} {} // CHECK:STDOUT: %Derived.decl: type = class_decl @Derived [concrete = constants.%Derived] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: impl @T2.as.ImplicitAs.impl: %T2.ref as %ImplicitAs.type { // CHECK:STDOUT: %T2.as.ImplicitAs.impl.Convert.decl: %T2.as.ImplicitAs.impl.Convert.type = fn_decl @T2.as.ImplicitAs.impl.Convert [concrete = constants.%T2.as.ImplicitAs.impl.Convert] { // CHECK:STDOUT: %self.patt: %pattern_type.b8b = value_binding_pattern self [concrete] // CHECK:STDOUT: %self.param_patt: %pattern_type.b8b = value_param_pattern %self.patt [concrete] // CHECK:STDOUT: %return.patt: %pattern_type.818 = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: %pattern_type.818 = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %T1.ref: type = name_ref T1, file.%T1.decl [concrete = constants.%T1] // CHECK:STDOUT: %.loc11: Core.Form = init_form %T1.ref [concrete = constants.%.a53] // CHECK:STDOUT: %self.param: %T2 = value_param call_param0 // CHECK:STDOUT: %Self.ref: type = name_ref Self, @T2.as.ImplicitAs.impl.%T2.ref [concrete = constants.%T2] // CHECK:STDOUT: %self: %T2 = value_binding self, %self.param // CHECK:STDOUT: %return.param: ref %T1 = out_param call_param1 // CHECK:STDOUT: %return: ref %T1 = return_slot %return.param // CHECK:STDOUT: } // CHECK:STDOUT: %ImplicitAs.impl_witness_table = impl_witness_table (%T2.as.ImplicitAs.impl.Convert.decl), @T2.as.ImplicitAs.impl [concrete] // CHECK:STDOUT: %ImplicitAs.impl_witness: = impl_witness %ImplicitAs.impl_witness_table [concrete = constants.%ImplicitAs.impl_witness] // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .T1 = // CHECK:STDOUT: .Convert = %T2.as.ImplicitAs.impl.Convert.decl // CHECK:STDOUT: witness = %ImplicitAs.impl_witness // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @T1 { // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type.357] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%T1 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @T2 { // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type.357] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%T2 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @Base { // CHECK:STDOUT: %Base.F.decl: %Base.F.type = fn_decl @Base.F [concrete = constants.%Base.F] { // CHECK:STDOUT: %self.patt: %pattern_type.101 = value_binding_pattern self [concrete] // CHECK:STDOUT: %self.param_patt: %pattern_type.101 = value_param_pattern %self.patt [concrete] // CHECK:STDOUT: %return.patt: %pattern_type.818 = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: %pattern_type.818 = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %T1.ref: type = name_ref T1, file.%T1.decl [concrete = constants.%T1] // CHECK:STDOUT: %.loc17: Core.Form = init_form %T1.ref [concrete = constants.%.a53] // CHECK:STDOUT: %self.param: %Base = value_param call_param0 // CHECK:STDOUT: %Self.ref: type = name_ref Self, constants.%Base [concrete = constants.%Base] // CHECK:STDOUT: %self: %Base = value_binding self, %self.param // CHECK:STDOUT: %return.param: ref %T1 = out_param call_param1 // CHECK:STDOUT: %return: ref %T1 = return_slot %return.param // CHECK:STDOUT: } // CHECK:STDOUT: %vtable_decl: ref %ptr.454 = vtable_decl @Base.vtable [concrete = constants.%Base.vtable_decl] // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%struct_type.vptr [concrete = constants.%complete_type.513] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: vtable_decl = %vtable_decl // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%Base // CHECK:STDOUT: .T1 = // CHECK:STDOUT: .F = %Base.F.decl // CHECK:STDOUT: .T2 = // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @Derived { // CHECK:STDOUT: %Base.ref: type = name_ref Base, file.%Base.decl [concrete = constants.%Base] // CHECK:STDOUT: %.loc21: %Derived.elem = base_decl %Base.ref, element0 [concrete] // CHECK:STDOUT: %Derived.F.decl: %Derived.F.type = fn_decl @Derived.F [concrete = constants.%Derived.F] { // CHECK:STDOUT: %self.patt: %pattern_type.9f6 = value_binding_pattern self [concrete] // CHECK:STDOUT: %self.param_patt: %pattern_type.9f6 = value_param_pattern %self.patt [concrete] // CHECK:STDOUT: %return.patt: %pattern_type.b8b = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: %pattern_type.b8b = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %T2.ref: type = name_ref T2, file.%T2.decl [concrete = constants.%T2] // CHECK:STDOUT: %.loc29: Core.Form = init_form %T2.ref [concrete = constants.%.c13] // CHECK:STDOUT: %self.param: %Derived = value_param call_param0 // CHECK:STDOUT: %Self.ref: type = name_ref Self, constants.%Derived [concrete = constants.%Derived] // CHECK:STDOUT: %self: %Derived = value_binding self, %self.param // CHECK:STDOUT: %return.param: ref %T2 = out_param call_param1 // CHECK:STDOUT: %return: ref %T2 = return_slot %return.param // CHECK:STDOUT: } // CHECK:STDOUT: %vtable_decl: ref %ptr.454 = vtable_decl @Derived.vtable [concrete = constants.%Derived.vtable_decl] // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%struct_type.base [concrete = constants.%complete_type.5a1] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: vtable_decl = %vtable_decl // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%Derived // CHECK:STDOUT: .Base = // CHECK:STDOUT: .base = %.loc21 // CHECK:STDOUT: .T2 = // CHECK:STDOUT: .F = %Derived.F.decl // CHECK:STDOUT: extend %Base.ref // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: vtable @Base.vtable { // CHECK:STDOUT: @Base.%Base.F.decl // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: vtable @Derived.vtable { // CHECK:STDOUT: @Derived.%Derived.F.decl // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @T2.as.ImplicitAs.impl.Convert(%self.param: %T2) -> out %return.param: %T1 { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %.loc12_13.1: %empty_struct_type = struct_literal () [concrete = constants.%empty_struct] // CHECK:STDOUT: %.loc12_13.2: init %T1 to %return.param = class_init () [concrete = constants.%T1.val] // CHECK:STDOUT: %.loc12_14: init %T1 = converted %.loc12_13.1, %.loc12_13.2 [concrete = constants.%T1.val] // CHECK:STDOUT: return %.loc12_14 to %return.param // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: virtual fn @Base.F(%self.param: %Base) -> out %return.param: %T1; // CHECK:STDOUT: // CHECK:STDOUT: override fn @Derived.F(%self.param: %Derived) -> out %return.param: %T2; // CHECK:STDOUT: // CHECK:STDOUT: --- fail_generic_virtual_decl.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %type: type = facet_type [concrete] // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self] // CHECK:STDOUT: %pattern_type.98f: type = pattern_type type [concrete] // CHECK:STDOUT: %T: type = symbolic_binding T, 0 [symbolic] // CHECK:STDOUT: %Base.type: type = generic_class_type @Base [concrete] // CHECK:STDOUT: %Base.generic: %Base.type = struct_value () [concrete] // CHECK:STDOUT: %Base: type = class_type @Base, @Base(%T) [symbolic] // CHECK:STDOUT: %pattern_type.2f0: type = pattern_type %Base [symbolic] // CHECK:STDOUT: %Base.F.type: type = fn_type @Base.F, @Base(%T) [symbolic] // CHECK:STDOUT: %Base.F: %Base.F.type = struct_value () [symbolic] // CHECK:STDOUT: %ptr: type = ptr_type [concrete] // CHECK:STDOUT: %Base.F.specific_fn: = specific_function %Base.F, @Base.F(%T) [symbolic] // CHECK:STDOUT: %Base.vtable_decl: ref %ptr = vtable_decl @Base.vtable [concrete] // CHECK:STDOUT: %struct_type.vptr: type = struct_type {.: %ptr} [concrete] // CHECK:STDOUT: %complete_type: = complete_type_witness %struct_type.vptr [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .Base = %Base.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %Base.decl: %Base.type = class_decl @Base [concrete = constants.%Base.generic] { // CHECK:STDOUT: %T.patt: %pattern_type.98f = symbolic_binding_pattern T, 0 [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %.loc7_21.1: type = splice_block %.loc7_21.2 [concrete = type] { // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %.loc7_21.2: type = type_literal type [concrete = type] // CHECK:STDOUT: } // CHECK:STDOUT: %T.loc7_17.2: type = symbolic_binding T, 0 [symbolic = %T.loc7_17.1 (constants.%T)] // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic class @Base(%T.loc7_17.2: type) { // CHECK:STDOUT: %T.loc7_17.1: type = symbolic_binding T, 0 [symbolic = %T.loc7_17.1 (constants.%T)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %Base.F.type: type = fn_type @Base.F, @Base(%T.loc7_17.1) [symbolic = %Base.F.type (constants.%Base.F.type)] // CHECK:STDOUT: %Base.F: @Base.%Base.F.type (%Base.F.type) = struct_value () [symbolic = %Base.F (constants.%Base.F)] // CHECK:STDOUT: %Base.F.specific_fn.loc13_1.2: = specific_function %Base.F, @Base.F(%T.loc7_17.1) [symbolic = %Base.F.specific_fn.loc13_1.2 (constants.%Base.F.specific_fn)] // CHECK:STDOUT: // CHECK:STDOUT: class { // CHECK:STDOUT: %Base.F.decl: @Base.%Base.F.type (%Base.F.type) = fn_decl @Base.F [symbolic = @Base.%Base.F (constants.%Base.F)] { // CHECK:STDOUT: %self.patt: @Base.F.%pattern_type (%pattern_type.2f0) = value_binding_pattern self [concrete] // CHECK:STDOUT: %self.param_patt: @Base.F.%pattern_type (%pattern_type.2f0) = value_param_pattern %self.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %self.param: @Base.F.%Base (%Base) = value_param call_param0 // CHECK:STDOUT: %.loc12_22.1: type = splice_block %Self.ref [symbolic = %Base (constants.%Base)] { // CHECK:STDOUT: %.loc12_22.2: type = specific_constant constants.%Base, @Base(constants.%T) [symbolic = %Base (constants.%Base)] // CHECK:STDOUT: %Self.ref: type = name_ref Self, %.loc12_22.2 [symbolic = %Base (constants.%Base)] // CHECK:STDOUT: } // CHECK:STDOUT: %self: @Base.F.%Base (%Base) = value_binding self, %self.param // CHECK:STDOUT: } // CHECK:STDOUT: %Base.F.specific_fn.loc13_1.1: = specific_function %Base.F.decl, @Base.F(constants.%T) [symbolic = %Base.F.specific_fn.loc13_1.2 (constants.%Base.F.specific_fn)] // CHECK:STDOUT: %vtable_decl: ref %ptr = vtable_decl @Base.vtable [concrete = constants.%Base.vtable_decl] // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%struct_type.vptr [concrete = constants.%complete_type] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: vtable_decl = %vtable_decl // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%Base // CHECK:STDOUT: .F = %Base.F.decl // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: vtable @Base.vtable { // CHECK:STDOUT: @Base.%Base.F.specific_fn.loc13_1.1 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic virtual fn @Base.F(@Base.%T.loc7_17.2: type) { // CHECK:STDOUT: %T: type = symbolic_binding T, 0 [symbolic = %T (constants.%T)] // CHECK:STDOUT: %Base: type = class_type @Base, @Base(%T) [symbolic = %Base (constants.%Base)] // CHECK:STDOUT: %pattern_type: type = pattern_type %Base [symbolic = %pattern_type (constants.%pattern_type.2f0)] // CHECK:STDOUT: // CHECK:STDOUT: virtual fn(%self.param: @Base.F.%Base (%Base)); // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @Base(constants.%T) { // CHECK:STDOUT: %T.loc7_17.1 => constants.%T // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @Base.F(constants.%T) { // CHECK:STDOUT: %T => constants.%T // CHECK:STDOUT: %Base => constants.%Base // CHECK:STDOUT: %pattern_type => constants.%pattern_type.2f0 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- impl_generic_base.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %T1: type = class_type @T1 [concrete] // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %complete_type.357: = complete_type_witness %empty_struct_type [concrete] // CHECK:STDOUT: %type: type = facet_type [concrete] // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self] // CHECK:STDOUT: %pattern_type.98f: type = pattern_type type [concrete] // CHECK:STDOUT: %T: type = symbolic_binding T, 0 [symbolic] // CHECK:STDOUT: %Base.type: type = generic_class_type @Base [concrete] // CHECK:STDOUT: %Base.generic: %Base.type = struct_value () [concrete] // CHECK:STDOUT: %Base.d0c: type = class_type @Base, @Base(%T) [symbolic] // CHECK:STDOUT: %pattern_type.2f0: type = pattern_type %Base.d0c [symbolic] // CHECK:STDOUT: %pattern_type.51d: type = pattern_type %T [symbolic] // CHECK:STDOUT: %Base.F.type.0f1: type = fn_type @Base.F, @Base(%T) [symbolic] // CHECK:STDOUT: %Base.F.cd3: %Base.F.type.0f1 = struct_value () [symbolic] // CHECK:STDOUT: %ptr.454: type = ptr_type [concrete] // CHECK:STDOUT: %Base.F.specific_fn.f8c: = specific_function %Base.F.cd3, @Base.F(%T) [symbolic] // CHECK:STDOUT: %Base.vtable_decl: ref %ptr.454 = vtable_decl @Base.vtable [concrete] // CHECK:STDOUT: %struct_type.vptr: type = struct_type {.: %ptr.454} [concrete] // CHECK:STDOUT: %complete_type.513: = complete_type_witness %struct_type.vptr [concrete] // CHECK:STDOUT: %require_complete.1e0: = require_complete_type %Base.d0c [symbolic] // CHECK:STDOUT: %require_complete.944: = require_complete_type %T [symbolic] // CHECK:STDOUT: %Derived: type = class_type @Derived [concrete] // CHECK:STDOUT: %Base.d5d: type = class_type @Base, @Base(%T1) [concrete] // CHECK:STDOUT: %Base.F.type.cb1: type = fn_type @Base.F, @Base(%T1) [concrete] // CHECK:STDOUT: %Base.F.eab: %Base.F.type.cb1 = struct_value () [concrete] // CHECK:STDOUT: %pattern_type.64f: type = pattern_type %Base.d5d [concrete] // CHECK:STDOUT: %pattern_type.818: type = pattern_type %T1 [concrete] // CHECK:STDOUT: %Base.F.specific_fn.cd5: = specific_function %Base.F.eab, @Base.F(%T1) [concrete] // CHECK:STDOUT: %Derived.elem: type = unbound_element_type %Derived, %Base.d5d [concrete] // CHECK:STDOUT: %pattern_type.9f6: type = pattern_type %Derived [concrete] // CHECK:STDOUT: %Derived.F.type: type = fn_type @Derived.F [concrete] // CHECK:STDOUT: %Derived.F: %Derived.F.type = struct_value () [concrete] // CHECK:STDOUT: %Derived.vtable_decl: ref %ptr.454 = vtable_decl @Derived.vtable [concrete] // CHECK:STDOUT: %struct_type.base.cc2: type = struct_type {.base: %Base.d5d} [concrete] // CHECK:STDOUT: %complete_type.dcb: = complete_type_witness %struct_type.base.cc2 [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .T1 = %T1.decl // CHECK:STDOUT: .Base = %Base.decl // CHECK:STDOUT: .Derived = %Derived.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %T1.decl: type = class_decl @T1 [concrete = constants.%T1] {} {} // CHECK:STDOUT: %Base.decl: %Base.type = class_decl @Base [concrete = constants.%Base.generic] { // CHECK:STDOUT: %T.patt: %pattern_type.98f = symbolic_binding_pattern T, 0 [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %.loc7_21.1: type = splice_block %.loc7_21.2 [concrete = type] { // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %.loc7_21.2: type = type_literal type [concrete = type] // CHECK:STDOUT: } // CHECK:STDOUT: %T.loc7_17.2: type = symbolic_binding T, 0 [symbolic = %T.loc7_17.1 (constants.%T)] // CHECK:STDOUT: } // CHECK:STDOUT: %Derived.decl: type = class_decl @Derived [concrete = constants.%Derived] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @T1 { // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type.357] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%T1 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic class @Base(%T.loc7_17.2: type) { // CHECK:STDOUT: %T.loc7_17.1: type = symbolic_binding T, 0 [symbolic = %T.loc7_17.1 (constants.%T)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %Base.F.type: type = fn_type @Base.F, @Base(%T.loc7_17.1) [symbolic = %Base.F.type (constants.%Base.F.type.0f1)] // CHECK:STDOUT: %Base.F: @Base.%Base.F.type (%Base.F.type.0f1) = struct_value () [symbolic = %Base.F (constants.%Base.F.cd3)] // CHECK:STDOUT: %Base.F.specific_fn.loc9_1.2: = specific_function %Base.F, @Base.F(%T.loc7_17.1) [symbolic = %Base.F.specific_fn.loc9_1.2 (constants.%Base.F.specific_fn.f8c)] // CHECK:STDOUT: // CHECK:STDOUT: class { // CHECK:STDOUT: %Base.F.decl: @Base.%Base.F.type (%Base.F.type.0f1) = fn_decl @Base.F [symbolic = @Base.%Base.F (constants.%Base.F.cd3)] { // CHECK:STDOUT: %self.patt: @Base.F.%pattern_type.loc8_23 (%pattern_type.2f0) = value_binding_pattern self [concrete] // CHECK:STDOUT: %self.param_patt: @Base.F.%pattern_type.loc8_23 (%pattern_type.2f0) = value_param_pattern %self.patt [concrete] // CHECK:STDOUT: %t.patt: @Base.F.%pattern_type.loc8_42 (%pattern_type.51d) = value_binding_pattern t [concrete] // CHECK:STDOUT: %t.param_patt: @Base.F.%pattern_type.loc8_42 (%pattern_type.51d) = value_param_pattern %t.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %self.param: @Base.F.%Base (%Base.d0c) = value_param call_param0 // CHECK:STDOUT: %.loc8_29.1: type = splice_block %Self.ref [symbolic = %Base (constants.%Base.d0c)] { // CHECK:STDOUT: %.loc8_29.2: type = specific_constant constants.%Base.d0c, @Base(constants.%T) [symbolic = %Base (constants.%Base.d0c)] // CHECK:STDOUT: %Self.ref: type = name_ref Self, %.loc8_29.2 [symbolic = %Base (constants.%Base.d0c)] // CHECK:STDOUT: } // CHECK:STDOUT: %self: @Base.F.%Base (%Base.d0c) = value_binding self, %self.param // CHECK:STDOUT: %t.param: @Base.F.%T (%T) = value_param call_param1 // CHECK:STDOUT: %T.ref: type = name_ref T, @Base.%T.loc7_17.2 [symbolic = %T (constants.%T)] // CHECK:STDOUT: %t: @Base.F.%T (%T) = value_binding t, %t.param // CHECK:STDOUT: } // CHECK:STDOUT: %Base.F.specific_fn.loc9_1.1: = specific_function %Base.F.decl, @Base.F(constants.%T) [symbolic = %Base.F.specific_fn.loc9_1.2 (constants.%Base.F.specific_fn.f8c)] // CHECK:STDOUT: %vtable_decl: ref %ptr.454 = vtable_decl @Base.vtable [concrete = constants.%Base.vtable_decl] // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%struct_type.vptr [concrete = constants.%complete_type.513] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: vtable_decl = %vtable_decl // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%Base.d0c // CHECK:STDOUT: .T = // CHECK:STDOUT: .F = %Base.F.decl // CHECK:STDOUT: .T1 = // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @Derived { // CHECK:STDOUT: %Base.ref: %Base.type = name_ref Base, file.%Base.decl [concrete = constants.%Base.generic] // CHECK:STDOUT: %T1.ref: type = name_ref T1, file.%T1.decl [concrete = constants.%T1] // CHECK:STDOUT: %Base: type = class_type @Base, @Base(constants.%T1) [concrete = constants.%Base.d5d] // CHECK:STDOUT: %.loc12: %Derived.elem = base_decl %Base, element0 [concrete] // CHECK:STDOUT: %Derived.F.decl: %Derived.F.type = fn_decl @Derived.F [concrete = constants.%Derived.F] { // CHECK:STDOUT: %self.patt: %pattern_type.9f6 = value_binding_pattern self [concrete] // CHECK:STDOUT: %self.param_patt: %pattern_type.9f6 = value_param_pattern %self.patt [concrete] // CHECK:STDOUT: %t.patt: %pattern_type.818 = value_binding_pattern t [concrete] // CHECK:STDOUT: %t.param_patt: %pattern_type.818 = value_param_pattern %t.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %self.param: %Derived = value_param call_param0 // CHECK:STDOUT: %Self.ref: type = name_ref Self, constants.%Derived [concrete = constants.%Derived] // CHECK:STDOUT: %self: %Derived = value_binding self, %self.param // CHECK:STDOUT: %t.param: %T1 = value_param call_param1 // CHECK:STDOUT: %T1.ref: type = name_ref T1, file.%T1.decl [concrete = constants.%T1] // CHECK:STDOUT: %t: %T1 = value_binding t, %t.param // CHECK:STDOUT: } // CHECK:STDOUT: %vtable_decl: ref %ptr.454 = vtable_decl @Derived.vtable [concrete = constants.%Derived.vtable_decl] // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%struct_type.base.cc2 [concrete = constants.%complete_type.dcb] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: vtable_decl = %vtable_decl // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%Derived // CHECK:STDOUT: .Base = // CHECK:STDOUT: .T1 = // CHECK:STDOUT: .base = %.loc12 // CHECK:STDOUT: .F = %Derived.F.decl // CHECK:STDOUT: extend %Base // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: vtable @Base.vtable { // CHECK:STDOUT: @Base.%Base.F.specific_fn.loc9_1.1 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: vtable @Derived.vtable { // CHECK:STDOUT: @Derived.%Derived.F.decl // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic virtual fn @Base.F(@Base.%T.loc7_17.2: type) { // CHECK:STDOUT: %T: type = symbolic_binding T, 0 [symbolic = %T (constants.%T)] // CHECK:STDOUT: %Base: type = class_type @Base, @Base(%T) [symbolic = %Base (constants.%Base.d0c)] // CHECK:STDOUT: %pattern_type.loc8_23: type = pattern_type %Base [symbolic = %pattern_type.loc8_23 (constants.%pattern_type.2f0)] // CHECK:STDOUT: %pattern_type.loc8_42: type = pattern_type %T [symbolic = %pattern_type.loc8_42 (constants.%pattern_type.51d)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete.loc8_27: = require_complete_type %Base [symbolic = %require_complete.loc8_27 (constants.%require_complete.1e0)] // CHECK:STDOUT: %require_complete.loc8_43: = require_complete_type %T [symbolic = %require_complete.loc8_43 (constants.%require_complete.944)] // CHECK:STDOUT: // CHECK:STDOUT: virtual fn(%self.param: @Base.F.%Base (%Base.d0c), %t.param: @Base.F.%T (%T)) { // CHECK:STDOUT: !entry: // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: override fn @Derived.F(%self.param: %Derived, %t.param: %T1) { // CHECK:STDOUT: !entry: // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @Base(constants.%T) { // CHECK:STDOUT: %T.loc7_17.1 => constants.%T // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %Base.F.type => constants.%Base.F.type.0f1 // CHECK:STDOUT: %Base.F => constants.%Base.F.cd3 // CHECK:STDOUT: %Base.F.specific_fn.loc9_1.2 => constants.%Base.F.specific_fn.f8c // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @Base.F(constants.%T) { // CHECK:STDOUT: %T => constants.%T // CHECK:STDOUT: %Base => constants.%Base.d0c // CHECK:STDOUT: %pattern_type.loc8_23 => constants.%pattern_type.2f0 // CHECK:STDOUT: %pattern_type.loc8_42 => constants.%pattern_type.51d // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete.loc8_27 => constants.%require_complete.1e0 // CHECK:STDOUT: %require_complete.loc8_43 => constants.%require_complete.944 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @Base(constants.%T1) { // CHECK:STDOUT: %T.loc7_17.1 => constants.%T1 // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %Base.F.type => constants.%Base.F.type.cb1 // CHECK:STDOUT: %Base.F => constants.%Base.F.eab // CHECK:STDOUT: %Base.F.specific_fn.loc9_1.2 => constants.%Base.F.specific_fn.cd5 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @Base.F(constants.%T1) { // CHECK:STDOUT: %T => constants.%T1 // CHECK:STDOUT: %Base => constants.%Base.d5d // CHECK:STDOUT: %pattern_type.loc8_23 => constants.%pattern_type.64f // CHECK:STDOUT: %pattern_type.loc8_42 => constants.%pattern_type.818 // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete.loc8_27 => constants.%complete_type.513 // CHECK:STDOUT: %require_complete.loc8_43 => constants.%complete_type.357 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- fail_virtual_without_self.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %T1: type = class_type @T1 [concrete] // CHECK:STDOUT: %T1.F.type: type = fn_type @T1.F [concrete] // CHECK:STDOUT: %T1.F: %T1.F.type = struct_value () [concrete] // CHECK:STDOUT: %T1.G.type: type = fn_type @T1.G [concrete] // CHECK:STDOUT: %T1.G: %T1.G.type = struct_value () [concrete] // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %complete_type.357: = complete_type_witness %empty_struct_type [concrete] // CHECK:STDOUT: %T2: type = class_type @T2 [concrete] // CHECK:STDOUT: %T2.elem: type = unbound_element_type %T2, %T1 [concrete] // CHECK:STDOUT: %T2.F.type: type = fn_type @T2.F [concrete] // CHECK:STDOUT: %T2.F: %T2.F.type = struct_value () [concrete] // CHECK:STDOUT: %struct_type.base: type = struct_type {.base: %T1} [concrete] // CHECK:STDOUT: %complete_type.ad3: = complete_type_witness %struct_type.base [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .T1 = %T1.decl // CHECK:STDOUT: .T2 = %T2.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %T1.decl: type = class_decl @T1 [concrete = constants.%T1] {} {} // CHECK:STDOUT: %T2.decl: type = class_decl @T2 [concrete = constants.%T2] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @T1 { // CHECK:STDOUT: %T1.F.decl: %T1.F.type = fn_decl @T1.F [concrete = constants.%T1.F] {} {} // CHECK:STDOUT: %T1.G.decl: %T1.G.type = fn_decl @T1.G [concrete = constants.%T1.G] {} {} // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type.357] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%T1 // CHECK:STDOUT: .F = %T1.F.decl // CHECK:STDOUT: .G = %T1.G.decl // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @T2 { // CHECK:STDOUT: %T1.ref: type = name_ref T1, file.%T1.decl [concrete = constants.%T1] // CHECK:STDOUT: %.loc18: %T2.elem = base_decl %T1.ref, element0 [concrete] // CHECK:STDOUT: %T2.F.decl: %T2.F.type = fn_decl @T2.F [concrete = constants.%T2.F] {} {} // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%struct_type.base [concrete = constants.%complete_type.ad3] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%T2 // CHECK:STDOUT: .T1 = // CHECK:STDOUT: .base = %.loc18 // CHECK:STDOUT: .F = %T2.F.decl // CHECK:STDOUT: extend %T1.ref // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @T1.F(); // CHECK:STDOUT: // CHECK:STDOUT: fn @T1.G(); // CHECK:STDOUT: // CHECK:STDOUT: fn @T2.F(); // CHECK:STDOUT: // CHECK:STDOUT: --- fail_ref_self_mismatch.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %T1: type = class_type @T1 [concrete] // CHECK:STDOUT: %pattern_type.818: type = pattern_type %T1 [concrete] // CHECK:STDOUT: %T1.F1.type: type = fn_type @T1.F1 [concrete] // CHECK:STDOUT: %T1.F1: %T1.F1.type = struct_value () [concrete] // CHECK:STDOUT: %ptr.454: type = ptr_type [concrete] // CHECK:STDOUT: %T1.vtable_decl: ref %ptr.454 = vtable_decl @T1.vtable [concrete] // CHECK:STDOUT: %struct_type.vptr: type = struct_type {.: %ptr.454} [concrete] // CHECK:STDOUT: %complete_type.513: = complete_type_witness %struct_type.vptr [concrete] // CHECK:STDOUT: %T2: type = class_type @T2 [concrete] // CHECK:STDOUT: %T2.elem: type = unbound_element_type %T2, %T1 [concrete] // CHECK:STDOUT: %pattern_type.b8b: type = pattern_type %T2 [concrete] // CHECK:STDOUT: %T2.F1.type: type = fn_type @T2.F1 [concrete] // CHECK:STDOUT: %T2.F1: %T2.F1.type = struct_value () [concrete] // CHECK:STDOUT: %T2.vtable_decl: ref %ptr.454 = vtable_decl @T2.vtable [concrete] // CHECK:STDOUT: %struct_type.base: type = struct_type {.base: %T1} [concrete] // CHECK:STDOUT: %complete_type.ad3: = complete_type_witness %struct_type.base [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .T1 = %T1.decl // CHECK:STDOUT: .T2 = %T2.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %T1.decl: type = class_decl @T1 [concrete = constants.%T1] {} {} // CHECK:STDOUT: %T2.decl: type = class_decl @T2 [concrete = constants.%T2] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @T1 { // CHECK:STDOUT: %T1.F1.decl: %T1.F1.type = fn_decl @T1.F1 [concrete = constants.%T1.F1] { // CHECK:STDOUT: %self.patt: %pattern_type.818 = value_binding_pattern self [concrete] // CHECK:STDOUT: %self.param_patt: %pattern_type.818 = value_param_pattern %self.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %self.param: %T1 = value_param call_param0 // CHECK:STDOUT: %Self.ref: type = name_ref Self, constants.%T1 [concrete = constants.%T1] // CHECK:STDOUT: %self: %T1 = value_binding self, %self.param // CHECK:STDOUT: } // CHECK:STDOUT: %vtable_decl: ref %ptr.454 = vtable_decl @T1.vtable [concrete = constants.%T1.vtable_decl] // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%struct_type.vptr [concrete = constants.%complete_type.513] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: vtable_decl = %vtable_decl // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%T1 // CHECK:STDOUT: .F1 = %T1.F1.decl // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @T2 { // CHECK:STDOUT: %T1.ref: type = name_ref T1, file.%T1.decl [concrete = constants.%T1] // CHECK:STDOUT: %.loc9: %T2.elem = base_decl %T1.ref, element0 [concrete] // CHECK:STDOUT: %T2.F1.decl: %T2.F1.type = fn_decl @T2.F1 [concrete = constants.%T2.F1] { // CHECK:STDOUT: %self.patt: %pattern_type.b8b = ref_binding_pattern self [concrete] // CHECK:STDOUT: %self.param_patt: %pattern_type.b8b = ref_param_pattern %self.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %self.param: ref %T2 = ref_param call_param0 // CHECK:STDOUT: %Self.ref: type = name_ref Self, constants.%T2 [concrete = constants.%T2] // CHECK:STDOUT: %self: ref %T2 = ref_binding self, %self.param // CHECK:STDOUT: } // CHECK:STDOUT: %vtable_decl: ref %ptr.454 = vtable_decl @T2.vtable [concrete = constants.%T2.vtable_decl] // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%struct_type.base [concrete = constants.%complete_type.ad3] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: vtable_decl = %vtable_decl // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%T2 // CHECK:STDOUT: .T1 = // CHECK:STDOUT: .base = %.loc9 // CHECK:STDOUT: .F1 = %T2.F1.decl // CHECK:STDOUT: extend %T1.ref // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: vtable @T1.vtable { // CHECK:STDOUT: @T1.%T1.F1.decl // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: vtable @T2.vtable { // CHECK:STDOUT: @T2.%T2.F1.decl // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: virtual fn @T1.F1(%self.param: %T1); // CHECK:STDOUT: // CHECK:STDOUT: override fn @T2.F1(%self.param: ref %T2); // CHECK:STDOUT: // CHECK:STDOUT: --- fail_generic_virtual.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %T1: type = class_type @T1 [concrete] // CHECK:STDOUT: %pattern_type.818: type = pattern_type %T1 [concrete] // CHECK:STDOUT: %type: type = facet_type [concrete] // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self] // CHECK:STDOUT: %pattern_type.98f: type = pattern_type type [concrete] // CHECK:STDOUT: %T: type = symbolic_binding T, 0 [symbolic] // CHECK:STDOUT: %T1.F.type: type = fn_type @T1.F [concrete] // CHECK:STDOUT: %T1.F: %T1.F.type = struct_value () [concrete] // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %complete_type: = complete_type_witness %empty_struct_type [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .T1 = %T1.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %T1.decl: type = class_decl @T1 [concrete = constants.%T1] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @T1 { // CHECK:STDOUT: %T1.F.decl: %T1.F.type = fn_decl @T1.F [concrete = constants.%T1.F] { // CHECK:STDOUT: %self.patt: %pattern_type.818 = value_binding_pattern self [concrete] // CHECK:STDOUT: %self.param_patt: %pattern_type.818 = value_param_pattern %self.patt [concrete] // CHECK:STDOUT: %T.patt: %pattern_type.98f = symbolic_binding_pattern T, 0 [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %self.param: %T1 = value_param call_param0 // CHECK:STDOUT: %Self.ref: type = name_ref Self, constants.%T1 [concrete = constants.%T1] // CHECK:STDOUT: %self: %T1 = value_binding self, %self.param // CHECK:STDOUT: %.loc9_32.1: type = splice_block %.loc9_32.2 [concrete = type] { // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %.loc9_32.2: type = type_literal type [concrete = type] // CHECK:STDOUT: } // CHECK:STDOUT: %T.loc9_28.2: type = symbolic_binding T, 0 [symbolic = %T.loc9_28.1 (constants.%T)] // CHECK:STDOUT: } // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%T1 // CHECK:STDOUT: .F = %T1.F.decl // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic virtual fn @T1.F(%T.loc9_28.2: type) { // CHECK:STDOUT: %T.loc9_28.1: type = symbolic_binding T, 0 [symbolic = %T.loc9_28.1 (constants.%T)] // CHECK:STDOUT: // CHECK:STDOUT: virtual fn(%self.param: %T1); // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @T1.F(constants.%T) { // CHECK:STDOUT: %T.loc9_28.1 => constants.%T // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- fail_generic_virtual_in_generic_class.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %type: type = facet_type [concrete] // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self] // CHECK:STDOUT: %pattern_type.98f: type = pattern_type type [concrete] // CHECK:STDOUT: %T.67d: type = symbolic_binding T, 0 [symbolic] // CHECK:STDOUT: %T1.type: type = generic_class_type @T1 [concrete] // CHECK:STDOUT: %T1.generic: %T1.type = struct_value () [concrete] // CHECK:STDOUT: %T1: type = class_type @T1, @T1(%T.67d) [symbolic] // CHECK:STDOUT: %pattern_type.253: type = pattern_type %T1 [symbolic] // CHECK:STDOUT: %T.091: type = symbolic_binding T, 1 [symbolic] // CHECK:STDOUT: %T1.F.type: type = fn_type @T1.F, @T1(%T.67d) [symbolic] // CHECK:STDOUT: %T1.F: %T1.F.type = struct_value () [symbolic] // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %complete_type: = complete_type_witness %empty_struct_type [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .T1 = %T1.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %T1.decl: %T1.type = class_decl @T1 [concrete = constants.%T1.generic] { // CHECK:STDOUT: %T.patt: %pattern_type.98f = symbolic_binding_pattern T, 0 [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %.loc4_19.1: type = splice_block %.loc4_19.2 [concrete = type] { // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %.loc4_19.2: type = type_literal type [concrete = type] // CHECK:STDOUT: } // CHECK:STDOUT: %T.loc4_15.2: type = symbolic_binding T, 0 [symbolic = %T.loc4_15.1 (constants.%T.67d)] // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic class @T1(%T.loc4_15.2: type) { // CHECK:STDOUT: %T.loc4_15.1: type = symbolic_binding T, 0 [symbolic = %T.loc4_15.1 (constants.%T.67d)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %T1.F.type: type = fn_type @T1.F, @T1(%T.loc4_15.1) [symbolic = %T1.F.type (constants.%T1.F.type)] // CHECK:STDOUT: %T1.F: @T1.%T1.F.type (%T1.F.type) = struct_value () [symbolic = %T1.F (constants.%T1.F)] // CHECK:STDOUT: // CHECK:STDOUT: class { // CHECK:STDOUT: %T1.F.decl: @T1.%T1.F.type (%T1.F.type) = fn_decl @T1.F [symbolic = @T1.%T1.F (constants.%T1.F)] { // CHECK:STDOUT: %self.patt: @T1.F.%pattern_type (%pattern_type.253) = value_binding_pattern self [concrete] // CHECK:STDOUT: %self.param_patt: @T1.F.%pattern_type (%pattern_type.253) = value_param_pattern %self.patt [concrete] // CHECK:STDOUT: %T.patt: %pattern_type.98f = symbolic_binding_pattern T, 1 [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %self.param: @T1.F.%T1 (%T1) = value_param call_param0 // CHECK:STDOUT: %.loc9_22.1: type = splice_block %Self.ref [symbolic = %T1 (constants.%T1)] { // CHECK:STDOUT: %.loc9_22.2: type = specific_constant constants.%T1, @T1(constants.%T.67d) [symbolic = %T1 (constants.%T1)] // CHECK:STDOUT: %Self.ref: type = name_ref Self, %.loc9_22.2 [symbolic = %T1 (constants.%T1)] // CHECK:STDOUT: } // CHECK:STDOUT: %self: @T1.F.%T1 (%T1) = value_binding self, %self.param // CHECK:STDOUT: %.loc9_32.1: type = splice_block %.loc9_32.2 [concrete = type] { // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %.loc9_32.2: type = type_literal type [concrete = type] // CHECK:STDOUT: } // CHECK:STDOUT: %T.loc9_28.2: type = symbolic_binding T, 1 [symbolic = %T.loc9_28.1 (constants.%T.091)] // CHECK:STDOUT: } // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%T1 // CHECK:STDOUT: .F = %T1.F.decl // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic virtual fn @T1.F(@T1.%T.loc4_15.2: type, %T.loc9_28.2: type) { // CHECK:STDOUT: %T.loc9_22: type = symbolic_binding T, 0 [symbolic = %T.loc9_22 (constants.%T.67d)] // CHECK:STDOUT: %T1: type = class_type @T1, @T1(%T.loc9_22) [symbolic = %T1 (constants.%T1)] // CHECK:STDOUT: %pattern_type: type = pattern_type %T1 [symbolic = %pattern_type (constants.%pattern_type.253)] // CHECK:STDOUT: %T.loc9_28.1: type = symbolic_binding T, 1 [symbolic = %T.loc9_28.1 (constants.%T.091)] // CHECK:STDOUT: // CHECK:STDOUT: virtual fn(%self.param: @T1.F.%T1 (%T1)); // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @T1(constants.%T.67d) { // CHECK:STDOUT: %T.loc4_15.1 => constants.%T.67d // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @T1.F(constants.%T.67d, constants.%T.091) { // CHECK:STDOUT: %T.loc9_22 => constants.%T.67d // CHECK:STDOUT: %T1 => constants.%T1 // CHECK:STDOUT: %pattern_type => constants.%pattern_type.253 // CHECK:STDOUT: %T.loc9_28.1 => constants.%T.091 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- generic_with_virtual.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %type: type = facet_type [concrete] // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self] // CHECK:STDOUT: %pattern_type.98f: type = pattern_type type [concrete] // CHECK:STDOUT: %T: type = symbolic_binding T, 0 [symbolic] // CHECK:STDOUT: %T1.type: type = generic_class_type @T1 [concrete] // CHECK:STDOUT: %T1.generic: %T1.type = struct_value () [concrete] // CHECK:STDOUT: %T1: type = class_type @T1, @T1(%T) [symbolic] // CHECK:STDOUT: %pattern_type.253: type = pattern_type %T1 [symbolic] // CHECK:STDOUT: %T1.F.type: type = fn_type @T1.F, @T1(%T) [symbolic] // CHECK:STDOUT: %T1.F: %T1.F.type = struct_value () [symbolic] // CHECK:STDOUT: %ptr.454: type = ptr_type [concrete] // CHECK:STDOUT: %T1.F.specific_fn: = specific_function %T1.F, @T1.F(%T) [symbolic] // CHECK:STDOUT: %T1.vtable_decl: ref %ptr.454 = vtable_decl @T1.vtable [concrete] // CHECK:STDOUT: %struct_type.vptr: type = struct_type {.: %ptr.454} [concrete] // CHECK:STDOUT: %complete_type: = complete_type_witness %struct_type.vptr [concrete] // CHECK:STDOUT: %require_complete: = require_complete_type %T1 [symbolic] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .T1 = %T1.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %T1.decl: %T1.type = class_decl @T1 [concrete = constants.%T1.generic] { // CHECK:STDOUT: %T.patt: %pattern_type.98f = symbolic_binding_pattern T, 0 [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %.loc4_19.1: type = splice_block %.loc4_19.2 [concrete = type] { // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %.loc4_19.2: type = type_literal type [concrete = type] // CHECK:STDOUT: } // CHECK:STDOUT: %T.loc4_15.2: type = symbolic_binding T, 0 [symbolic = %T.loc4_15.1 (constants.%T)] // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic class @T1(%T.loc4_15.2: type) { // CHECK:STDOUT: %T.loc4_15.1: type = symbolic_binding T, 0 [symbolic = %T.loc4_15.1 (constants.%T)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %T1.F.type: type = fn_type @T1.F, @T1(%T.loc4_15.1) [symbolic = %T1.F.type (constants.%T1.F.type)] // CHECK:STDOUT: %T1.F: @T1.%T1.F.type (%T1.F.type) = struct_value () [symbolic = %T1.F (constants.%T1.F)] // CHECK:STDOUT: %T1.F.specific_fn.loc6_1.2: = specific_function %T1.F, @T1.F(%T.loc4_15.1) [symbolic = %T1.F.specific_fn.loc6_1.2 (constants.%T1.F.specific_fn)] // CHECK:STDOUT: // CHECK:STDOUT: class { // CHECK:STDOUT: %T1.F.decl: @T1.%T1.F.type (%T1.F.type) = fn_decl @T1.F [symbolic = @T1.%T1.F (constants.%T1.F)] { // CHECK:STDOUT: %self.patt: @T1.F.%pattern_type (%pattern_type.253) = value_binding_pattern self [concrete] // CHECK:STDOUT: %self.param_patt: @T1.F.%pattern_type (%pattern_type.253) = value_param_pattern %self.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %self.param: @T1.F.%T1 (%T1) = value_param call_param0 // CHECK:STDOUT: %.loc5_29.1: type = splice_block %Self.ref [symbolic = %T1 (constants.%T1)] { // CHECK:STDOUT: %.loc5_29.2: type = specific_constant constants.%T1, @T1(constants.%T) [symbolic = %T1 (constants.%T1)] // CHECK:STDOUT: %Self.ref: type = name_ref Self, %.loc5_29.2 [symbolic = %T1 (constants.%T1)] // CHECK:STDOUT: } // CHECK:STDOUT: %self: @T1.F.%T1 (%T1) = value_binding self, %self.param // CHECK:STDOUT: } // CHECK:STDOUT: %T1.F.specific_fn.loc6_1.1: = specific_function %T1.F.decl, @T1.F(constants.%T) [symbolic = %T1.F.specific_fn.loc6_1.2 (constants.%T1.F.specific_fn)] // CHECK:STDOUT: %vtable_decl: ref %ptr.454 = vtable_decl @T1.vtable [concrete = constants.%T1.vtable_decl] // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%struct_type.vptr [concrete = constants.%complete_type] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: vtable_decl = %vtable_decl // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%T1 // CHECK:STDOUT: .F = %T1.F.decl // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: vtable @T1.vtable { // CHECK:STDOUT: @T1.%T1.F.specific_fn.loc6_1.1 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic virtual fn @T1.F(@T1.%T.loc4_15.2: type) { // CHECK:STDOUT: %T: type = symbolic_binding T, 0 [symbolic = %T (constants.%T)] // CHECK:STDOUT: %T1: type = class_type @T1, @T1(%T) [symbolic = %T1 (constants.%T1)] // CHECK:STDOUT: %pattern_type: type = pattern_type %T1 [symbolic = %pattern_type (constants.%pattern_type.253)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete: = require_complete_type %T1 [symbolic = %require_complete (constants.%require_complete)] // CHECK:STDOUT: // CHECK:STDOUT: virtual fn(%self.param: @T1.F.%T1 (%T1)) { // CHECK:STDOUT: !entry: // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @T1(constants.%T) { // CHECK:STDOUT: %T.loc4_15.1 => constants.%T // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %T1.F.type => constants.%T1.F.type // CHECK:STDOUT: %T1.F => constants.%T1.F // CHECK:STDOUT: %T1.F.specific_fn.loc6_1.2 => constants.%T1.F.specific_fn // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @T1.F(constants.%T) { // CHECK:STDOUT: %T => constants.%T // CHECK:STDOUT: %T1 => constants.%T1 // CHECK:STDOUT: %pattern_type => constants.%pattern_type.253 // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete => constants.%require_complete // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- with_dependent_arg.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %type: type = facet_type [concrete] // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self] // CHECK:STDOUT: %pattern_type.98f: type = pattern_type type [concrete] // CHECK:STDOUT: %T: type = symbolic_binding T, 0 [symbolic] // CHECK:STDOUT: %T1.type: type = generic_class_type @T1 [concrete] // CHECK:STDOUT: %T1.generic: %T1.type = struct_value () [concrete] // CHECK:STDOUT: %T1: type = class_type @T1, @T1(%T) [symbolic] // CHECK:STDOUT: %pattern_type.253: type = pattern_type %T1 [symbolic] // CHECK:STDOUT: %pattern_type.51d: type = pattern_type %T [symbolic] // CHECK:STDOUT: %T1.F.type: type = fn_type @T1.F, @T1(%T) [symbolic] // CHECK:STDOUT: %T1.F: %T1.F.type = struct_value () [symbolic] // CHECK:STDOUT: %ptr.454: type = ptr_type [concrete] // CHECK:STDOUT: %T1.F.specific_fn: = specific_function %T1.F, @T1.F(%T) [symbolic] // CHECK:STDOUT: %T1.vtable_decl: ref %ptr.454 = vtable_decl @T1.vtable [concrete] // CHECK:STDOUT: %struct_type.vptr: type = struct_type {.: %ptr.454} [concrete] // CHECK:STDOUT: %complete_type: = complete_type_witness %struct_type.vptr [concrete] // CHECK:STDOUT: %require_complete.6d2: = require_complete_type %T1 [symbolic] // CHECK:STDOUT: %require_complete.944: = require_complete_type %T [symbolic] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .T1 = %T1.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %T1.decl: %T1.type = class_decl @T1 [concrete = constants.%T1.generic] { // CHECK:STDOUT: %T.patt: %pattern_type.98f = symbolic_binding_pattern T, 0 [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %.loc4_19.1: type = splice_block %.loc4_19.2 [concrete = type] { // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %.loc4_19.2: type = type_literal type [concrete = type] // CHECK:STDOUT: } // CHECK:STDOUT: %T.loc4_15.2: type = symbolic_binding T, 0 [symbolic = %T.loc4_15.1 (constants.%T)] // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic class @T1(%T.loc4_15.2: type) { // CHECK:STDOUT: %T.loc4_15.1: type = symbolic_binding T, 0 [symbolic = %T.loc4_15.1 (constants.%T)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %T1.F.type: type = fn_type @T1.F, @T1(%T.loc4_15.1) [symbolic = %T1.F.type (constants.%T1.F.type)] // CHECK:STDOUT: %T1.F: @T1.%T1.F.type (%T1.F.type) = struct_value () [symbolic = %T1.F (constants.%T1.F)] // CHECK:STDOUT: %T1.F.specific_fn.loc6_1.2: = specific_function %T1.F, @T1.F(%T.loc4_15.1) [symbolic = %T1.F.specific_fn.loc6_1.2 (constants.%T1.F.specific_fn)] // CHECK:STDOUT: // CHECK:STDOUT: class { // CHECK:STDOUT: %T1.F.decl: @T1.%T1.F.type (%T1.F.type) = fn_decl @T1.F [symbolic = @T1.%T1.F (constants.%T1.F)] { // CHECK:STDOUT: %self.patt: @T1.F.%pattern_type.loc5_23 (%pattern_type.253) = value_binding_pattern self [concrete] // CHECK:STDOUT: %self.param_patt: @T1.F.%pattern_type.loc5_23 (%pattern_type.253) = value_param_pattern %self.patt [concrete] // CHECK:STDOUT: %t.patt: @T1.F.%pattern_type.loc5_42 (%pattern_type.51d) = value_binding_pattern t [concrete] // CHECK:STDOUT: %t.param_patt: @T1.F.%pattern_type.loc5_42 (%pattern_type.51d) = value_param_pattern %t.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %self.param: @T1.F.%T1 (%T1) = value_param call_param0 // CHECK:STDOUT: %.loc5_29.1: type = splice_block %Self.ref [symbolic = %T1 (constants.%T1)] { // CHECK:STDOUT: %.loc5_29.2: type = specific_constant constants.%T1, @T1(constants.%T) [symbolic = %T1 (constants.%T1)] // CHECK:STDOUT: %Self.ref: type = name_ref Self, %.loc5_29.2 [symbolic = %T1 (constants.%T1)] // CHECK:STDOUT: } // CHECK:STDOUT: %self: @T1.F.%T1 (%T1) = value_binding self, %self.param // CHECK:STDOUT: %t.param: @T1.F.%T (%T) = value_param call_param1 // CHECK:STDOUT: %T.ref: type = name_ref T, @T1.%T.loc4_15.2 [symbolic = %T (constants.%T)] // CHECK:STDOUT: %t: @T1.F.%T (%T) = value_binding t, %t.param // CHECK:STDOUT: } // CHECK:STDOUT: %T1.F.specific_fn.loc6_1.1: = specific_function %T1.F.decl, @T1.F(constants.%T) [symbolic = %T1.F.specific_fn.loc6_1.2 (constants.%T1.F.specific_fn)] // CHECK:STDOUT: %vtable_decl: ref %ptr.454 = vtable_decl @T1.vtable [concrete = constants.%T1.vtable_decl] // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%struct_type.vptr [concrete = constants.%complete_type] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: vtable_decl = %vtable_decl // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%T1 // CHECK:STDOUT: .T = // CHECK:STDOUT: .F = %T1.F.decl // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: vtable @T1.vtable { // CHECK:STDOUT: @T1.%T1.F.specific_fn.loc6_1.1 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic virtual fn @T1.F(@T1.%T.loc4_15.2: type) { // CHECK:STDOUT: %T: type = symbolic_binding T, 0 [symbolic = %T (constants.%T)] // CHECK:STDOUT: %T1: type = class_type @T1, @T1(%T) [symbolic = %T1 (constants.%T1)] // CHECK:STDOUT: %pattern_type.loc5_23: type = pattern_type %T1 [symbolic = %pattern_type.loc5_23 (constants.%pattern_type.253)] // CHECK:STDOUT: %pattern_type.loc5_42: type = pattern_type %T [symbolic = %pattern_type.loc5_42 (constants.%pattern_type.51d)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete.loc5_27: = require_complete_type %T1 [symbolic = %require_complete.loc5_27 (constants.%require_complete.6d2)] // CHECK:STDOUT: %require_complete.loc5_43: = require_complete_type %T [symbolic = %require_complete.loc5_43 (constants.%require_complete.944)] // CHECK:STDOUT: // CHECK:STDOUT: virtual fn(%self.param: @T1.F.%T1 (%T1), %t.param: @T1.F.%T (%T)) { // CHECK:STDOUT: !entry: // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @T1(constants.%T) { // CHECK:STDOUT: %T.loc4_15.1 => constants.%T // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %T1.F.type => constants.%T1.F.type // CHECK:STDOUT: %T1.F => constants.%T1.F // CHECK:STDOUT: %T1.F.specific_fn.loc6_1.2 => constants.%T1.F.specific_fn // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @T1.F(constants.%T) { // CHECK:STDOUT: %T => constants.%T // CHECK:STDOUT: %T1 => constants.%T1 // CHECK:STDOUT: %pattern_type.loc5_23 => constants.%pattern_type.253 // CHECK:STDOUT: %pattern_type.loc5_42 => constants.%pattern_type.51d // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete.loc5_27 => constants.%require_complete.6d2 // CHECK:STDOUT: %require_complete.loc5_43 => constants.%require_complete.944 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- vtable_import_unneeded.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %Base: type = class_type @Base [concrete] // CHECK:STDOUT: %ptr: type = ptr_type [concrete] // CHECK:STDOUT: %struct_type.vptr: type = struct_type {.: %ptr} [concrete] // CHECK:STDOUT: %complete_type: = complete_type_witness %struct_type.vptr [concrete] // CHECK:STDOUT: %pattern_type: type = pattern_type %Base [concrete] // CHECK:STDOUT: %F.type: type = fn_type @F [concrete] // CHECK:STDOUT: %F: %F.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: %Modifiers: = namespace file.%Modifiers.import, [concrete] { // CHECK:STDOUT: .Base = %Modifiers.Base // CHECK:STDOUT: import Modifiers//default // CHECK:STDOUT: } // CHECK:STDOUT: %Modifiers.Base: type = import_ref Modifiers//default, Base, loaded [concrete = constants.%Base] // CHECK:STDOUT: %Modifiers.import_ref.d80231.2 = import_ref Modifiers//default, loc6_1, unloaded // CHECK:STDOUT: %Modifiers.import_ref.05e: = import_ref Modifiers//default, loc6_1, loaded [concrete = constants.%complete_type] // CHECK:STDOUT: %Modifiers.import_ref.d82 = import_ref Modifiers//default, inst{{[0-9A-F]+}} [no loc], unloaded // CHECK:STDOUT: %Modifiers.import_ref.d40 = import_ref Modifiers//default, loc5_29, unloaded // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .Modifiers = imports.%Modifiers // CHECK:STDOUT: .F = %F.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %Modifiers.import = import Modifiers // CHECK:STDOUT: %F.decl: %F.type = fn_decl @F [concrete = constants.%F] { // CHECK:STDOUT: %b.patt: %pattern_type = value_binding_pattern b [concrete] // CHECK:STDOUT: %b.param_patt: %pattern_type = value_param_pattern %b.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %b.param: %Base = value_param call_param0 // CHECK:STDOUT: %.loc6: type = splice_block %Base.ref [concrete = constants.%Base] { // CHECK:STDOUT: %Modifiers.ref: = name_ref Modifiers, imports.%Modifiers [concrete = imports.%Modifiers] // CHECK:STDOUT: %Base.ref: type = name_ref Base, imports.%Modifiers.Base [concrete = constants.%Base] // CHECK:STDOUT: } // CHECK:STDOUT: %b: %Base = value_binding b, %b.param // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @Base [from "modifiers.carbon"] { // CHECK:STDOUT: complete_type_witness = imports.%Modifiers.import_ref.05e // CHECK:STDOUT: vtable_decl = imports.%Modifiers.import_ref.d80231.2 // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = imports.%Modifiers.import_ref.d82 // CHECK:STDOUT: .H = imports.%Modifiers.import_ref.d40 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @F(%b.param: %Base); // CHECK:STDOUT: // CHECK:STDOUT: --- generic_derived_from_nongeneric.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %NonGenericBase: type = class_type @NonGenericBase [concrete] // CHECK:STDOUT: %pattern_type.126: type = pattern_type %NonGenericBase [concrete] // CHECK:STDOUT: %NonGenericBase.F1.type: type = fn_type @NonGenericBase.F1 [concrete] // CHECK:STDOUT: %NonGenericBase.F1: %NonGenericBase.F1.type = struct_value () [concrete] // CHECK:STDOUT: %NonGenericBase.F2.type: type = fn_type @NonGenericBase.F2 [concrete] // CHECK:STDOUT: %NonGenericBase.F2: %NonGenericBase.F2.type = struct_value () [concrete] // CHECK:STDOUT: %ptr.454: type = ptr_type [concrete] // CHECK:STDOUT: %NonGenericBase.vtable_decl: ref %ptr.454 = vtable_decl @NonGenericBase.vtable [concrete] // CHECK:STDOUT: %struct_type.vptr: type = struct_type {.: %ptr.454} [concrete] // CHECK:STDOUT: %complete_type.513: = complete_type_witness %struct_type.vptr [concrete] // CHECK:STDOUT: %type: type = facet_type [concrete] // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self] // CHECK:STDOUT: %pattern_type.98f: type = pattern_type type [concrete] // CHECK:STDOUT: %T: type = symbolic_binding T, 0 [symbolic] // CHECK:STDOUT: %GenericDerived.type: type = generic_class_type @GenericDerived [concrete] // CHECK:STDOUT: %GenericDerived.generic: %GenericDerived.type = struct_value () [concrete] // CHECK:STDOUT: %GenericDerived: type = class_type @GenericDerived, @GenericDerived(%T) [symbolic] // CHECK:STDOUT: %GenericDerived.elem: type = unbound_element_type %GenericDerived, %NonGenericBase [symbolic] // CHECK:STDOUT: %pattern_type.945: type = pattern_type %GenericDerived [symbolic] // CHECK:STDOUT: %GenericDerived.F2.type: type = fn_type @GenericDerived.F2, @GenericDerived(%T) [symbolic] // CHECK:STDOUT: %GenericDerived.F2: %GenericDerived.F2.type = struct_value () [symbolic] // CHECK:STDOUT: %GenericDerived.F3.type: type = fn_type @GenericDerived.F3, @GenericDerived(%T) [symbolic] // CHECK:STDOUT: %GenericDerived.F3: %GenericDerived.F3.type = struct_value () [symbolic] // CHECK:STDOUT: %GenericDerived.F2.specific_fn: = specific_function %GenericDerived.F2, @GenericDerived.F2(%T) [symbolic] // CHECK:STDOUT: %GenericDerived.F3.specific_fn: = specific_function %GenericDerived.F3, @GenericDerived.F3(%T) [symbolic] // CHECK:STDOUT: %GenericDerived.vtable_decl: ref %ptr.454 = vtable_decl @GenericDerived.vtable [concrete] // CHECK:STDOUT: %struct_type.base.66e: type = struct_type {.base: %NonGenericBase} [concrete] // CHECK:STDOUT: %complete_type.a92: = complete_type_witness %struct_type.base.66e [concrete] // CHECK:STDOUT: %require_complete: = require_complete_type %GenericDerived [symbolic] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .NonGenericBase = %NonGenericBase.decl // CHECK:STDOUT: .GenericDerived = %GenericDerived.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %NonGenericBase.decl: type = class_decl @NonGenericBase [concrete = constants.%NonGenericBase] {} {} // CHECK:STDOUT: %GenericDerived.decl: %GenericDerived.type = class_decl @GenericDerived [concrete = constants.%GenericDerived.generic] { // CHECK:STDOUT: %T.patt: %pattern_type.98f = symbolic_binding_pattern T, 0 [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %.loc9_31.1: type = splice_block %.loc9_31.2 [concrete = type] { // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %.loc9_31.2: type = type_literal type [concrete = type] // CHECK:STDOUT: } // CHECK:STDOUT: %T.loc9_27.2: type = symbolic_binding T, 0 [symbolic = %T.loc9_27.1 (constants.%T)] // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @NonGenericBase { // CHECK:STDOUT: %NonGenericBase.F1.decl: %NonGenericBase.F1.type = fn_decl @NonGenericBase.F1 [concrete = constants.%NonGenericBase.F1] { // CHECK:STDOUT: %self.patt: %pattern_type.126 = value_binding_pattern self [concrete] // CHECK:STDOUT: %self.param_patt: %pattern_type.126 = value_param_pattern %self.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %self.param: %NonGenericBase = value_param call_param0 // CHECK:STDOUT: %Self.ref: type = name_ref Self, constants.%NonGenericBase [concrete = constants.%NonGenericBase] // CHECK:STDOUT: %self: %NonGenericBase = value_binding self, %self.param // CHECK:STDOUT: } // CHECK:STDOUT: %NonGenericBase.F2.decl: %NonGenericBase.F2.type = fn_decl @NonGenericBase.F2 [concrete = constants.%NonGenericBase.F2] { // CHECK:STDOUT: %self.patt: %pattern_type.126 = value_binding_pattern self [concrete] // CHECK:STDOUT: %self.param_patt: %pattern_type.126 = value_param_pattern %self.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %self.param: %NonGenericBase = value_param call_param0 // CHECK:STDOUT: %Self.ref: type = name_ref Self, constants.%NonGenericBase [concrete = constants.%NonGenericBase] // CHECK:STDOUT: %self: %NonGenericBase = value_binding self, %self.param // CHECK:STDOUT: } // CHECK:STDOUT: %vtable_decl: ref %ptr.454 = vtable_decl @NonGenericBase.vtable [concrete = constants.%NonGenericBase.vtable_decl] // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%struct_type.vptr [concrete = constants.%complete_type.513] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: vtable_decl = %vtable_decl // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%NonGenericBase // CHECK:STDOUT: .F1 = %NonGenericBase.F1.decl // CHECK:STDOUT: .F2 = %NonGenericBase.F2.decl // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic class @GenericDerived(%T.loc9_27.2: type) { // CHECK:STDOUT: %T.loc9_27.1: type = symbolic_binding T, 0 [symbolic = %T.loc9_27.1 (constants.%T)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %GenericDerived: type = class_type @GenericDerived, @GenericDerived(%T.loc9_27.1) [symbolic = %GenericDerived (constants.%GenericDerived)] // CHECK:STDOUT: %GenericDerived.elem: type = unbound_element_type %GenericDerived, constants.%NonGenericBase [symbolic = %GenericDerived.elem (constants.%GenericDerived.elem)] // CHECK:STDOUT: %GenericDerived.F2.type: type = fn_type @GenericDerived.F2, @GenericDerived(%T.loc9_27.1) [symbolic = %GenericDerived.F2.type (constants.%GenericDerived.F2.type)] // CHECK:STDOUT: %GenericDerived.F2: @GenericDerived.%GenericDerived.F2.type (%GenericDerived.F2.type) = struct_value () [symbolic = %GenericDerived.F2 (constants.%GenericDerived.F2)] // CHECK:STDOUT: %GenericDerived.F3.type: type = fn_type @GenericDerived.F3, @GenericDerived(%T.loc9_27.1) [symbolic = %GenericDerived.F3.type (constants.%GenericDerived.F3.type)] // CHECK:STDOUT: %GenericDerived.F3: @GenericDerived.%GenericDerived.F3.type (%GenericDerived.F3.type) = struct_value () [symbolic = %GenericDerived.F3 (constants.%GenericDerived.F3)] // CHECK:STDOUT: %GenericDerived.F2.specific_fn.loc13_1.2: = specific_function %GenericDerived.F2, @GenericDerived.F2(%T.loc9_27.1) [symbolic = %GenericDerived.F2.specific_fn.loc13_1.2 (constants.%GenericDerived.F2.specific_fn)] // CHECK:STDOUT: %GenericDerived.F3.specific_fn.loc13_1.2: = specific_function %GenericDerived.F3, @GenericDerived.F3(%T.loc9_27.1) [symbolic = %GenericDerived.F3.specific_fn.loc13_1.2 (constants.%GenericDerived.F3.specific_fn)] // CHECK:STDOUT: // CHECK:STDOUT: class { // CHECK:STDOUT: %NonGenericBase.ref: type = name_ref NonGenericBase, file.%NonGenericBase.decl [concrete = constants.%NonGenericBase] // CHECK:STDOUT: %.loc10: @GenericDerived.%GenericDerived.elem (%GenericDerived.elem) = base_decl %NonGenericBase.ref, element0 [concrete] // CHECK:STDOUT: %GenericDerived.F2.decl: @GenericDerived.%GenericDerived.F2.type (%GenericDerived.F2.type) = fn_decl @GenericDerived.F2 [symbolic = @GenericDerived.%GenericDerived.F2 (constants.%GenericDerived.F2)] { // CHECK:STDOUT: %self.patt: @GenericDerived.F2.%pattern_type (%pattern_type.945) = value_binding_pattern self [concrete] // CHECK:STDOUT: %self.param_patt: @GenericDerived.F2.%pattern_type (%pattern_type.945) = value_param_pattern %self.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %self.param: @GenericDerived.F2.%GenericDerived (%GenericDerived) = value_param call_param0 // CHECK:STDOUT: %.loc11_31.1: type = splice_block %Self.ref [symbolic = %GenericDerived (constants.%GenericDerived)] { // CHECK:STDOUT: %.loc11_31.2: type = specific_constant constants.%GenericDerived, @GenericDerived(constants.%T) [symbolic = %GenericDerived (constants.%GenericDerived)] // CHECK:STDOUT: %Self.ref: type = name_ref Self, %.loc11_31.2 [symbolic = %GenericDerived (constants.%GenericDerived)] // CHECK:STDOUT: } // CHECK:STDOUT: %self: @GenericDerived.F2.%GenericDerived (%GenericDerived) = value_binding self, %self.param // CHECK:STDOUT: } // CHECK:STDOUT: %GenericDerived.F3.decl: @GenericDerived.%GenericDerived.F3.type (%GenericDerived.F3.type) = fn_decl @GenericDerived.F3 [symbolic = @GenericDerived.%GenericDerived.F3 (constants.%GenericDerived.F3)] { // CHECK:STDOUT: %self.patt: @GenericDerived.F3.%pattern_type (%pattern_type.945) = value_binding_pattern self [concrete] // CHECK:STDOUT: %self.param_patt: @GenericDerived.F3.%pattern_type (%pattern_type.945) = value_param_pattern %self.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %self.param: @GenericDerived.F3.%GenericDerived (%GenericDerived) = value_param call_param0 // CHECK:STDOUT: %.loc12_30.1: type = splice_block %Self.ref [symbolic = %GenericDerived (constants.%GenericDerived)] { // CHECK:STDOUT: %.loc12_30.2: type = specific_constant constants.%GenericDerived, @GenericDerived(constants.%T) [symbolic = %GenericDerived (constants.%GenericDerived)] // CHECK:STDOUT: %Self.ref: type = name_ref Self, %.loc12_30.2 [symbolic = %GenericDerived (constants.%GenericDerived)] // CHECK:STDOUT: } // CHECK:STDOUT: %self: @GenericDerived.F3.%GenericDerived (%GenericDerived) = value_binding self, %self.param // CHECK:STDOUT: } // CHECK:STDOUT: %GenericDerived.F2.specific_fn.loc13_1.1: = specific_function %GenericDerived.F2.decl, @GenericDerived.F2(constants.%T) [symbolic = %GenericDerived.F2.specific_fn.loc13_1.2 (constants.%GenericDerived.F2.specific_fn)] // CHECK:STDOUT: %GenericDerived.F3.specific_fn.loc13_1.1: = specific_function %GenericDerived.F3.decl, @GenericDerived.F3(constants.%T) [symbolic = %GenericDerived.F3.specific_fn.loc13_1.2 (constants.%GenericDerived.F3.specific_fn)] // CHECK:STDOUT: %vtable_decl: ref %ptr.454 = vtable_decl @GenericDerived.vtable [concrete = constants.%GenericDerived.vtable_decl] // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%struct_type.base.66e [concrete = constants.%complete_type.a92] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: vtable_decl = %vtable_decl // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%GenericDerived // CHECK:STDOUT: .NonGenericBase = // CHECK:STDOUT: .base = %.loc10 // CHECK:STDOUT: .F2 = %GenericDerived.F2.decl // CHECK:STDOUT: .F3 = %GenericDerived.F3.decl // CHECK:STDOUT: extend %NonGenericBase.ref // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: vtable @NonGenericBase.vtable { // CHECK:STDOUT: @NonGenericBase.%NonGenericBase.F1.decl // CHECK:STDOUT: @NonGenericBase.%NonGenericBase.F2.decl // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: vtable @GenericDerived.vtable { // CHECK:STDOUT: constants.%NonGenericBase.F1 // CHECK:STDOUT: @GenericDerived.%GenericDerived.F2.specific_fn.loc13_1.1 // CHECK:STDOUT: @GenericDerived.%GenericDerived.F3.specific_fn.loc13_1.1 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: virtual fn @NonGenericBase.F1(%self.param: %NonGenericBase) { // CHECK:STDOUT: !entry: // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: virtual fn @NonGenericBase.F2(%self.param: %NonGenericBase) { // CHECK:STDOUT: !entry: // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic override fn @GenericDerived.F2(@GenericDerived.%T.loc9_27.2: type) { // CHECK:STDOUT: %T: type = symbolic_binding T, 0 [symbolic = %T (constants.%T)] // CHECK:STDOUT: %GenericDerived: type = class_type @GenericDerived, @GenericDerived(%T) [symbolic = %GenericDerived (constants.%GenericDerived)] // CHECK:STDOUT: %pattern_type: type = pattern_type %GenericDerived [symbolic = %pattern_type (constants.%pattern_type.945)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete: = require_complete_type %GenericDerived [symbolic = %require_complete (constants.%require_complete)] // CHECK:STDOUT: // CHECK:STDOUT: override fn(%self.param: @GenericDerived.F2.%GenericDerived (%GenericDerived)) { // CHECK:STDOUT: !entry: // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic virtual fn @GenericDerived.F3(@GenericDerived.%T.loc9_27.2: type) { // CHECK:STDOUT: %T: type = symbolic_binding T, 0 [symbolic = %T (constants.%T)] // CHECK:STDOUT: %GenericDerived: type = class_type @GenericDerived, @GenericDerived(%T) [symbolic = %GenericDerived (constants.%GenericDerived)] // CHECK:STDOUT: %pattern_type: type = pattern_type %GenericDerived [symbolic = %pattern_type (constants.%pattern_type.945)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete: = require_complete_type %GenericDerived [symbolic = %require_complete (constants.%require_complete)] // CHECK:STDOUT: // CHECK:STDOUT: virtual fn(%self.param: @GenericDerived.F3.%GenericDerived (%GenericDerived)) { // CHECK:STDOUT: !entry: // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @GenericDerived(constants.%T) { // CHECK:STDOUT: %T.loc9_27.1 => constants.%T // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %GenericDerived => constants.%GenericDerived // CHECK:STDOUT: %GenericDerived.elem => constants.%GenericDerived.elem // CHECK:STDOUT: %GenericDerived.F2.type => constants.%GenericDerived.F2.type // CHECK:STDOUT: %GenericDerived.F2 => constants.%GenericDerived.F2 // CHECK:STDOUT: %GenericDerived.F3.type => constants.%GenericDerived.F3.type // CHECK:STDOUT: %GenericDerived.F3 => constants.%GenericDerived.F3 // CHECK:STDOUT: %GenericDerived.F2.specific_fn.loc13_1.2 => constants.%GenericDerived.F2.specific_fn // CHECK:STDOUT: %GenericDerived.F3.specific_fn.loc13_1.2 => constants.%GenericDerived.F3.specific_fn // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @GenericDerived.F2(constants.%T) { // CHECK:STDOUT: %T => constants.%T // CHECK:STDOUT: %GenericDerived => constants.%GenericDerived // CHECK:STDOUT: %pattern_type => constants.%pattern_type.945 // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete => constants.%require_complete // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @GenericDerived.F3(constants.%T) { // CHECK:STDOUT: %T => constants.%T // CHECK:STDOUT: %GenericDerived => constants.%GenericDerived // CHECK:STDOUT: %pattern_type => constants.%pattern_type.945 // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete => constants.%require_complete // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- nongeneric_derived_from_generic.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %type: type = facet_type [concrete] // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self] // CHECK:STDOUT: %pattern_type.98f: type = pattern_type type [concrete] // CHECK:STDOUT: %T: type = symbolic_binding T, 0 [symbolic] // CHECK:STDOUT: %GenericBase.type: type = generic_class_type @GenericBase [concrete] // CHECK:STDOUT: %GenericBase.generic: %GenericBase.type = struct_value () [concrete] // CHECK:STDOUT: %GenericBase.5e3: type = class_type @GenericBase, @GenericBase(%T) [symbolic] // CHECK:STDOUT: %pattern_type.3f7: type = pattern_type %GenericBase.5e3 [symbolic] // CHECK:STDOUT: %GenericBase.F1.type.c30: type = fn_type @GenericBase.F1, @GenericBase(%T) [symbolic] // CHECK:STDOUT: %GenericBase.F1.5f6: %GenericBase.F1.type.c30 = struct_value () [symbolic] // CHECK:STDOUT: %GenericBase.F2.type.3d4: type = fn_type @GenericBase.F2, @GenericBase(%T) [symbolic] // CHECK:STDOUT: %GenericBase.F2.9ed: %GenericBase.F2.type.3d4 = struct_value () [symbolic] // CHECK:STDOUT: %ptr.454: type = ptr_type [concrete] // CHECK:STDOUT: %GenericBase.F1.specific_fn.463: = specific_function %GenericBase.F1.5f6, @GenericBase.F1(%T) [symbolic] // CHECK:STDOUT: %GenericBase.F2.specific_fn.92d: = specific_function %GenericBase.F2.9ed, @GenericBase.F2(%T) [symbolic] // CHECK:STDOUT: %GenericBase.vtable_decl: ref %ptr.454 = vtable_decl @GenericBase.vtable [concrete] // CHECK:STDOUT: %struct_type.vptr: type = struct_type {.: %ptr.454} [concrete] // CHECK:STDOUT: %complete_type.513: = complete_type_witness %struct_type.vptr [concrete] // CHECK:STDOUT: %require_complete: = require_complete_type %GenericBase.5e3 [symbolic] // CHECK:STDOUT: %T1: type = class_type @T1 [concrete] // CHECK:STDOUT: %NonGenericDerived: type = class_type @NonGenericDerived [concrete] // CHECK:STDOUT: %GenericBase.52d: type = class_type @GenericBase, @GenericBase(%T1) [concrete] // CHECK:STDOUT: %GenericBase.F1.type.93d: type = fn_type @GenericBase.F1, @GenericBase(%T1) [concrete] // CHECK:STDOUT: %GenericBase.F1.c55: %GenericBase.F1.type.93d = struct_value () [concrete] // CHECK:STDOUT: %GenericBase.F2.type.14d: type = fn_type @GenericBase.F2, @GenericBase(%T1) [concrete] // CHECK:STDOUT: %GenericBase.F2.301: %GenericBase.F2.type.14d = struct_value () [concrete] // CHECK:STDOUT: %pattern_type.889: type = pattern_type %GenericBase.52d [concrete] // CHECK:STDOUT: %GenericBase.F1.specific_fn.9eb: = specific_function %GenericBase.F1.c55, @GenericBase.F1(%T1) [concrete] // CHECK:STDOUT: %GenericBase.F2.specific_fn.ed3: = specific_function %GenericBase.F2.301, @GenericBase.F2(%T1) [concrete] // CHECK:STDOUT: %NonGenericDerived.elem: type = unbound_element_type %NonGenericDerived, %GenericBase.52d [concrete] // CHECK:STDOUT: %pattern_type.c19: type = pattern_type %NonGenericDerived [concrete] // CHECK:STDOUT: %NonGenericDerived.F2.type: type = fn_type @NonGenericDerived.F2 [concrete] // CHECK:STDOUT: %NonGenericDerived.F2: %NonGenericDerived.F2.type = struct_value () [concrete] // CHECK:STDOUT: %NonGenericDerived.F3.type: type = fn_type @NonGenericDerived.F3 [concrete] // CHECK:STDOUT: %NonGenericDerived.F3: %NonGenericDerived.F3.type = struct_value () [concrete] // CHECK:STDOUT: %NonGenericDerived.vtable_decl: ref %ptr.454 = vtable_decl @NonGenericDerived.vtable [concrete] // CHECK:STDOUT: %struct_type.base.19b: type = struct_type {.base: %GenericBase.52d} [concrete] // CHECK:STDOUT: %complete_type.1f1: = complete_type_witness %struct_type.base.19b [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .GenericBase = %GenericBase.decl // CHECK:STDOUT: .T1 = %T1.decl // CHECK:STDOUT: .NonGenericDerived = %NonGenericDerived.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %GenericBase.decl: %GenericBase.type = class_decl @GenericBase [concrete = constants.%GenericBase.generic] { // CHECK:STDOUT: %T.patt: %pattern_type.98f = symbolic_binding_pattern T, 0 [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %.loc4_28.1: type = splice_block %.loc4_28.2 [concrete = type] { // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %.loc4_28.2: type = type_literal type [concrete = type] // CHECK:STDOUT: } // CHECK:STDOUT: %T.loc4_24.2: type = symbolic_binding T, 0 [symbolic = %T.loc4_24.1 (constants.%T)] // CHECK:STDOUT: } // CHECK:STDOUT: %T1.decl: type = class_decl @T1 [concrete = constants.%T1] {} {} // CHECK:STDOUT: %NonGenericDerived.decl: type = class_decl @NonGenericDerived [concrete = constants.%NonGenericDerived] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic class @GenericBase(%T.loc4_24.2: type) { // CHECK:STDOUT: %T.loc4_24.1: type = symbolic_binding T, 0 [symbolic = %T.loc4_24.1 (constants.%T)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %GenericBase.F1.type: type = fn_type @GenericBase.F1, @GenericBase(%T.loc4_24.1) [symbolic = %GenericBase.F1.type (constants.%GenericBase.F1.type.c30)] // CHECK:STDOUT: %GenericBase.F1: @GenericBase.%GenericBase.F1.type (%GenericBase.F1.type.c30) = struct_value () [symbolic = %GenericBase.F1 (constants.%GenericBase.F1.5f6)] // CHECK:STDOUT: %GenericBase.F2.type: type = fn_type @GenericBase.F2, @GenericBase(%T.loc4_24.1) [symbolic = %GenericBase.F2.type (constants.%GenericBase.F2.type.3d4)] // CHECK:STDOUT: %GenericBase.F2: @GenericBase.%GenericBase.F2.type (%GenericBase.F2.type.3d4) = struct_value () [symbolic = %GenericBase.F2 (constants.%GenericBase.F2.9ed)] // CHECK:STDOUT: %GenericBase.F1.specific_fn.loc7_1.2: = specific_function %GenericBase.F1, @GenericBase.F1(%T.loc4_24.1) [symbolic = %GenericBase.F1.specific_fn.loc7_1.2 (constants.%GenericBase.F1.specific_fn.463)] // CHECK:STDOUT: %GenericBase.F2.specific_fn.loc7_1.2: = specific_function %GenericBase.F2, @GenericBase.F2(%T.loc4_24.1) [symbolic = %GenericBase.F2.specific_fn.loc7_1.2 (constants.%GenericBase.F2.specific_fn.92d)] // CHECK:STDOUT: // CHECK:STDOUT: class { // CHECK:STDOUT: %GenericBase.F1.decl: @GenericBase.%GenericBase.F1.type (%GenericBase.F1.type.c30) = fn_decl @GenericBase.F1 [symbolic = @GenericBase.%GenericBase.F1 (constants.%GenericBase.F1.5f6)] { // CHECK:STDOUT: %self.patt: @GenericBase.F1.%pattern_type (%pattern_type.3f7) = value_binding_pattern self [concrete] // CHECK:STDOUT: %self.param_patt: @GenericBase.F1.%pattern_type (%pattern_type.3f7) = value_param_pattern %self.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %self.param: @GenericBase.F1.%GenericBase (%GenericBase.5e3) = value_param call_param0 // CHECK:STDOUT: %.loc5_30.1: type = splice_block %Self.ref [symbolic = %GenericBase (constants.%GenericBase.5e3)] { // CHECK:STDOUT: %.loc5_30.2: type = specific_constant constants.%GenericBase.5e3, @GenericBase(constants.%T) [symbolic = %GenericBase (constants.%GenericBase.5e3)] // CHECK:STDOUT: %Self.ref: type = name_ref Self, %.loc5_30.2 [symbolic = %GenericBase (constants.%GenericBase.5e3)] // CHECK:STDOUT: } // CHECK:STDOUT: %self: @GenericBase.F1.%GenericBase (%GenericBase.5e3) = value_binding self, %self.param // CHECK:STDOUT: } // CHECK:STDOUT: %GenericBase.F2.decl: @GenericBase.%GenericBase.F2.type (%GenericBase.F2.type.3d4) = fn_decl @GenericBase.F2 [symbolic = @GenericBase.%GenericBase.F2 (constants.%GenericBase.F2.9ed)] { // CHECK:STDOUT: %self.patt: @GenericBase.F2.%pattern_type (%pattern_type.3f7) = value_binding_pattern self [concrete] // CHECK:STDOUT: %self.param_patt: @GenericBase.F2.%pattern_type (%pattern_type.3f7) = value_param_pattern %self.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %self.param: @GenericBase.F2.%GenericBase (%GenericBase.5e3) = value_param call_param0 // CHECK:STDOUT: %.loc6_30.1: type = splice_block %Self.ref [symbolic = %GenericBase (constants.%GenericBase.5e3)] { // CHECK:STDOUT: %.loc6_30.2: type = specific_constant constants.%GenericBase.5e3, @GenericBase(constants.%T) [symbolic = %GenericBase (constants.%GenericBase.5e3)] // CHECK:STDOUT: %Self.ref: type = name_ref Self, %.loc6_30.2 [symbolic = %GenericBase (constants.%GenericBase.5e3)] // CHECK:STDOUT: } // CHECK:STDOUT: %self: @GenericBase.F2.%GenericBase (%GenericBase.5e3) = value_binding self, %self.param // CHECK:STDOUT: } // CHECK:STDOUT: %GenericBase.F1.specific_fn.loc7_1.1: = specific_function %GenericBase.F1.decl, @GenericBase.F1(constants.%T) [symbolic = %GenericBase.F1.specific_fn.loc7_1.2 (constants.%GenericBase.F1.specific_fn.463)] // CHECK:STDOUT: %GenericBase.F2.specific_fn.loc7_1.1: = specific_function %GenericBase.F2.decl, @GenericBase.F2(constants.%T) [symbolic = %GenericBase.F2.specific_fn.loc7_1.2 (constants.%GenericBase.F2.specific_fn.92d)] // CHECK:STDOUT: %vtable_decl: ref %ptr.454 = vtable_decl @GenericBase.vtable [concrete = constants.%GenericBase.vtable_decl] // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%struct_type.vptr [concrete = constants.%complete_type.513] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: vtable_decl = %vtable_decl // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%GenericBase.5e3 // CHECK:STDOUT: .F1 = %GenericBase.F1.decl // CHECK:STDOUT: .F2 = %GenericBase.F2.decl // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @T1; // CHECK:STDOUT: // CHECK:STDOUT: class @NonGenericDerived { // CHECK:STDOUT: %GenericBase.ref: %GenericBase.type = name_ref GenericBase, file.%GenericBase.decl [concrete = constants.%GenericBase.generic] // CHECK:STDOUT: %T1.ref: type = name_ref T1, file.%T1.decl [concrete = constants.%T1] // CHECK:STDOUT: %GenericBase: type = class_type @GenericBase, @GenericBase(constants.%T1) [concrete = constants.%GenericBase.52d] // CHECK:STDOUT: %.loc12: %NonGenericDerived.elem = base_decl %GenericBase, element0 [concrete] // CHECK:STDOUT: %NonGenericDerived.F2.decl: %NonGenericDerived.F2.type = fn_decl @NonGenericDerived.F2 [concrete = constants.%NonGenericDerived.F2] { // CHECK:STDOUT: %self.patt: %pattern_type.c19 = value_binding_pattern self [concrete] // CHECK:STDOUT: %self.param_patt: %pattern_type.c19 = value_param_pattern %self.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %self.param: %NonGenericDerived = value_param call_param0 // CHECK:STDOUT: %Self.ref: type = name_ref Self, constants.%NonGenericDerived [concrete = constants.%NonGenericDerived] // CHECK:STDOUT: %self: %NonGenericDerived = value_binding self, %self.param // CHECK:STDOUT: } // CHECK:STDOUT: %NonGenericDerived.F3.decl: %NonGenericDerived.F3.type = fn_decl @NonGenericDerived.F3 [concrete = constants.%NonGenericDerived.F3] { // CHECK:STDOUT: %self.patt: %pattern_type.c19 = value_binding_pattern self [concrete] // CHECK:STDOUT: %self.param_patt: %pattern_type.c19 = value_param_pattern %self.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %self.param: %NonGenericDerived = value_param call_param0 // CHECK:STDOUT: %Self.ref: type = name_ref Self, constants.%NonGenericDerived [concrete = constants.%NonGenericDerived] // CHECK:STDOUT: %self: %NonGenericDerived = value_binding self, %self.param // CHECK:STDOUT: } // CHECK:STDOUT: %vtable_decl: ref %ptr.454 = vtable_decl @NonGenericDerived.vtable [concrete = constants.%NonGenericDerived.vtable_decl] // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%struct_type.base.19b [concrete = constants.%complete_type.1f1] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: vtable_decl = %vtable_decl // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%NonGenericDerived // CHECK:STDOUT: .GenericBase = // CHECK:STDOUT: .T1 = // CHECK:STDOUT: .base = %.loc12 // CHECK:STDOUT: .F2 = %NonGenericDerived.F2.decl // CHECK:STDOUT: .F3 = %NonGenericDerived.F3.decl // CHECK:STDOUT: extend %GenericBase // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: vtable @GenericBase.vtable { // CHECK:STDOUT: @GenericBase.%GenericBase.F1.specific_fn.loc7_1.1 // CHECK:STDOUT: @GenericBase.%GenericBase.F2.specific_fn.loc7_1.1 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: vtable @NonGenericDerived.vtable { // CHECK:STDOUT: constants.%GenericBase.F1.specific_fn.9eb // CHECK:STDOUT: @NonGenericDerived.%NonGenericDerived.F2.decl // CHECK:STDOUT: @NonGenericDerived.%NonGenericDerived.F3.decl // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic virtual fn @GenericBase.F1(@GenericBase.%T.loc4_24.2: type) { // CHECK:STDOUT: %T: type = symbolic_binding T, 0 [symbolic = %T (constants.%T)] // CHECK:STDOUT: %GenericBase: type = class_type @GenericBase, @GenericBase(%T) [symbolic = %GenericBase (constants.%GenericBase.5e3)] // CHECK:STDOUT: %pattern_type: type = pattern_type %GenericBase [symbolic = %pattern_type (constants.%pattern_type.3f7)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete: = require_complete_type %GenericBase [symbolic = %require_complete (constants.%require_complete)] // CHECK:STDOUT: // CHECK:STDOUT: virtual fn(%self.param: @GenericBase.F1.%GenericBase (%GenericBase.5e3)) { // CHECK:STDOUT: !entry: // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic virtual fn @GenericBase.F2(@GenericBase.%T.loc4_24.2: type) { // CHECK:STDOUT: %T: type = symbolic_binding T, 0 [symbolic = %T (constants.%T)] // CHECK:STDOUT: %GenericBase: type = class_type @GenericBase, @GenericBase(%T) [symbolic = %GenericBase (constants.%GenericBase.5e3)] // CHECK:STDOUT: %pattern_type: type = pattern_type %GenericBase [symbolic = %pattern_type (constants.%pattern_type.3f7)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete: = require_complete_type %GenericBase [symbolic = %require_complete (constants.%require_complete)] // CHECK:STDOUT: // CHECK:STDOUT: virtual fn(%self.param: @GenericBase.F2.%GenericBase (%GenericBase.5e3)) { // CHECK:STDOUT: !entry: // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: override fn @NonGenericDerived.F2(%self.param: %NonGenericDerived) { // CHECK:STDOUT: !entry: // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: virtual fn @NonGenericDerived.F3(%self.param: %NonGenericDerived) { // CHECK:STDOUT: !entry: // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @GenericBase(constants.%T) { // CHECK:STDOUT: %T.loc4_24.1 => constants.%T // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %GenericBase.F1.type => constants.%GenericBase.F1.type.c30 // CHECK:STDOUT: %GenericBase.F1 => constants.%GenericBase.F1.5f6 // CHECK:STDOUT: %GenericBase.F2.type => constants.%GenericBase.F2.type.3d4 // CHECK:STDOUT: %GenericBase.F2 => constants.%GenericBase.F2.9ed // CHECK:STDOUT: %GenericBase.F1.specific_fn.loc7_1.2 => constants.%GenericBase.F1.specific_fn.463 // CHECK:STDOUT: %GenericBase.F2.specific_fn.loc7_1.2 => constants.%GenericBase.F2.specific_fn.92d // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @GenericBase.F1(constants.%T) { // CHECK:STDOUT: %T => constants.%T // CHECK:STDOUT: %GenericBase => constants.%GenericBase.5e3 // CHECK:STDOUT: %pattern_type => constants.%pattern_type.3f7 // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete => constants.%require_complete // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @GenericBase.F2(constants.%T) { // CHECK:STDOUT: %T => constants.%T // CHECK:STDOUT: %GenericBase => constants.%GenericBase.5e3 // CHECK:STDOUT: %pattern_type => constants.%pattern_type.3f7 // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete => constants.%require_complete // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @GenericBase(constants.%T1) { // CHECK:STDOUT: %T.loc4_24.1 => constants.%T1 // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %GenericBase.F1.type => constants.%GenericBase.F1.type.93d // CHECK:STDOUT: %GenericBase.F1 => constants.%GenericBase.F1.c55 // CHECK:STDOUT: %GenericBase.F2.type => constants.%GenericBase.F2.type.14d // CHECK:STDOUT: %GenericBase.F2 => constants.%GenericBase.F2.301 // CHECK:STDOUT: %GenericBase.F1.specific_fn.loc7_1.2 => constants.%GenericBase.F1.specific_fn.9eb // CHECK:STDOUT: %GenericBase.F2.specific_fn.loc7_1.2 => constants.%GenericBase.F2.specific_fn.ed3 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @GenericBase.F1(constants.%T1) { // CHECK:STDOUT: %T => constants.%T1 // CHECK:STDOUT: %GenericBase => constants.%GenericBase.52d // CHECK:STDOUT: %pattern_type => constants.%pattern_type.889 // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete => constants.%complete_type.513 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @GenericBase.F2(constants.%T1) { // CHECK:STDOUT: %T => constants.%T1 // CHECK:STDOUT: %GenericBase => constants.%GenericBase.52d // CHECK:STDOUT: %pattern_type => constants.%pattern_type.889 // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete => constants.%complete_type.513 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- impl_generic_specifically.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %type: type = facet_type [concrete] // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self] // CHECK:STDOUT: %pattern_type.98f: type = pattern_type type [concrete] // CHECK:STDOUT: %T: type = symbolic_binding T, 0 [symbolic] // CHECK:STDOUT: %Base.type: type = generic_class_type @Base [concrete] // CHECK:STDOUT: %Base.generic: %Base.type = struct_value () [concrete] // CHECK:STDOUT: %Base.d0c: type = class_type @Base, @Base(%T) [symbolic] // CHECK:STDOUT: %pattern_type.2f0: type = pattern_type %Base.d0c [symbolic] // CHECK:STDOUT: %ptr.4b1: type = ptr_type %Base.d0c [symbolic] // CHECK:STDOUT: %pattern_type.cf5: type = pattern_type %ptr.4b1 [symbolic] // CHECK:STDOUT: %Base.F.type.0f1: type = fn_type @Base.F, @Base(%T) [symbolic] // CHECK:STDOUT: %Base.F.cd3: %Base.F.type.0f1 = struct_value () [symbolic] // CHECK:STDOUT: %ptr.454: type = ptr_type [concrete] // CHECK:STDOUT: %Base.F.specific_fn.f8c: = specific_function %Base.F.cd3, @Base.F(%T) [symbolic] // CHECK:STDOUT: %Base.vtable_decl: ref %ptr.454 = vtable_decl @Base.vtable [concrete] // CHECK:STDOUT: %struct_type.vptr: type = struct_type {.: %ptr.454} [concrete] // CHECK:STDOUT: %complete_type.513: = complete_type_witness %struct_type.vptr [concrete] // CHECK:STDOUT: %require_complete.1e0: = require_complete_type %Base.d0c [symbolic] // CHECK:STDOUT: %require_complete.3cf: = require_complete_type %ptr.4b1 [symbolic] // CHECK:STDOUT: %T1: type = class_type @T1 [concrete] // CHECK:STDOUT: %D1: type = class_type @D1 [concrete] // CHECK:STDOUT: %Base.d5d: type = class_type @Base, @Base(%T1) [concrete] // CHECK:STDOUT: %Base.F.type.cb1: type = fn_type @Base.F, @Base(%T1) [concrete] // CHECK:STDOUT: %Base.F.eab: %Base.F.type.cb1 = struct_value () [concrete] // CHECK:STDOUT: %pattern_type.64f: type = pattern_type %Base.d5d [concrete] // CHECK:STDOUT: %ptr.6e0: type = ptr_type %Base.d5d [concrete] // CHECK:STDOUT: %pattern_type.912: type = pattern_type %ptr.6e0 [concrete] // CHECK:STDOUT: %Base.F.specific_fn.cd5: = specific_function %Base.F.eab, @Base.F(%T1) [concrete] // CHECK:STDOUT: %D1.elem: type = unbound_element_type %D1, %Base.d5d [concrete] // CHECK:STDOUT: %pattern_type.7f9: type = pattern_type %D1 [concrete] // CHECK:STDOUT: %D1.F.type: type = fn_type @D1.F [concrete] // CHECK:STDOUT: %D1.F: %D1.F.type = struct_value () [concrete] // CHECK:STDOUT: %D1.vtable_decl: ref %ptr.454 = vtable_decl @D1.vtable [concrete] // CHECK:STDOUT: %struct_type.base.cc2: type = struct_type {.base: %Base.d5d} [concrete] // CHECK:STDOUT: %complete_type.dcb: = complete_type_witness %struct_type.base.cc2 [concrete] // CHECK:STDOUT: %complete_type.96e: = complete_type_witness %ptr.6e0 [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .Base = %Base.decl // CHECK:STDOUT: .T1 = %T1.decl // CHECK:STDOUT: .D1 = %D1.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %Base.decl: %Base.type = class_decl @Base [concrete = constants.%Base.generic] { // CHECK:STDOUT: %T.patt: %pattern_type.98f = symbolic_binding_pattern T, 0 [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %.loc4_21.1: type = splice_block %.loc4_21.2 [concrete = type] { // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %.loc4_21.2: type = type_literal type [concrete = type] // CHECK:STDOUT: } // CHECK:STDOUT: %T.loc4_17.2: type = symbolic_binding T, 0 [symbolic = %T.loc4_17.1 (constants.%T)] // CHECK:STDOUT: } // CHECK:STDOUT: %T1.decl: type = class_decl @T1 [concrete = constants.%T1] {} {} // CHECK:STDOUT: %D1.decl: type = class_decl @D1 [concrete = constants.%D1] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic class @Base(%T.loc4_17.2: type) { // CHECK:STDOUT: %T.loc4_17.1: type = symbolic_binding T, 0 [symbolic = %T.loc4_17.1 (constants.%T)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %Base.F.type: type = fn_type @Base.F, @Base(%T.loc4_17.1) [symbolic = %Base.F.type (constants.%Base.F.type.0f1)] // CHECK:STDOUT: %Base.F: @Base.%Base.F.type (%Base.F.type.0f1) = struct_value () [symbolic = %Base.F (constants.%Base.F.cd3)] // CHECK:STDOUT: %Base.F.specific_fn.loc6_1.2: = specific_function %Base.F, @Base.F(%T.loc4_17.1) [symbolic = %Base.F.specific_fn.loc6_1.2 (constants.%Base.F.specific_fn.f8c)] // CHECK:STDOUT: // CHECK:STDOUT: class { // CHECK:STDOUT: %Base.F.decl: @Base.%Base.F.type (%Base.F.type.0f1) = fn_decl @Base.F [symbolic = @Base.%Base.F (constants.%Base.F.cd3)] { // CHECK:STDOUT: %self.patt: @Base.F.%pattern_type.loc5_23 (%pattern_type.2f0) = value_binding_pattern self [concrete] // CHECK:STDOUT: %self.param_patt: @Base.F.%pattern_type.loc5_23 (%pattern_type.2f0) = value_param_pattern %self.patt [concrete] // CHECK:STDOUT: %t.patt: @Base.F.%pattern_type.loc5_42 (%pattern_type.cf5) = value_binding_pattern t [concrete] // CHECK:STDOUT: %t.param_patt: @Base.F.%pattern_type.loc5_42 (%pattern_type.cf5) = value_param_pattern %t.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %self.param: @Base.F.%Base.loc5_29 (%Base.d0c) = value_param call_param0 // CHECK:STDOUT: %.loc5_29.1: type = splice_block %Self.ref [symbolic = %Base.loc5_29 (constants.%Base.d0c)] { // CHECK:STDOUT: %.loc5_29.2: type = specific_constant constants.%Base.d0c, @Base(constants.%T) [symbolic = %Base.loc5_29 (constants.%Base.d0c)] // CHECK:STDOUT: %Self.ref: type = name_ref Self, %.loc5_29.2 [symbolic = %Base.loc5_29 (constants.%Base.d0c)] // CHECK:STDOUT: } // CHECK:STDOUT: %self: @Base.F.%Base.loc5_29 (%Base.d0c) = value_binding self, %self.param // CHECK:STDOUT: %t.param: @Base.F.%ptr.loc5_52.1 (%ptr.4b1) = value_param call_param1 // CHECK:STDOUT: %.loc5_52: type = splice_block %ptr.loc5_52.2 [symbolic = %ptr.loc5_52.1 (constants.%ptr.4b1)] { // CHECK:STDOUT: %Base.ref: %Base.type = name_ref Base, file.%Base.decl [concrete = constants.%Base.generic] // CHECK:STDOUT: %T.ref: type = name_ref T, @Base.%T.loc4_17.2 [symbolic = %T (constants.%T)] // CHECK:STDOUT: %Base.loc5_51: type = class_type @Base, @Base(constants.%T) [symbolic = %Base.loc5_29 (constants.%Base.d0c)] // CHECK:STDOUT: %ptr.loc5_52.2: type = ptr_type %Base.loc5_51 [symbolic = %ptr.loc5_52.1 (constants.%ptr.4b1)] // CHECK:STDOUT: } // CHECK:STDOUT: %t: @Base.F.%ptr.loc5_52.1 (%ptr.4b1) = value_binding t, %t.param // CHECK:STDOUT: } // CHECK:STDOUT: %Base.F.specific_fn.loc6_1.1: = specific_function %Base.F.decl, @Base.F(constants.%T) [symbolic = %Base.F.specific_fn.loc6_1.2 (constants.%Base.F.specific_fn.f8c)] // CHECK:STDOUT: %vtable_decl: ref %ptr.454 = vtable_decl @Base.vtable [concrete = constants.%Base.vtable_decl] // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%struct_type.vptr [concrete = constants.%complete_type.513] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: vtable_decl = %vtable_decl // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%Base.d0c // CHECK:STDOUT: .Base = // CHECK:STDOUT: .T = // CHECK:STDOUT: .F = %Base.F.decl // CHECK:STDOUT: .T1 = // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @T1; // CHECK:STDOUT: // CHECK:STDOUT: class @D1 { // CHECK:STDOUT: %Base.ref: %Base.type = name_ref Base, file.%Base.decl [concrete = constants.%Base.generic] // CHECK:STDOUT: %T1.ref: type = name_ref T1, file.%T1.decl [concrete = constants.%T1] // CHECK:STDOUT: %Base: type = class_type @Base, @Base(constants.%T1) [concrete = constants.%Base.d5d] // CHECK:STDOUT: %.loc9: %D1.elem = base_decl %Base, element0 [concrete] // CHECK:STDOUT: %D1.F.decl: %D1.F.type = fn_decl @D1.F [concrete = constants.%D1.F] { // CHECK:STDOUT: %self.patt: %pattern_type.7f9 = value_binding_pattern self [concrete] // CHECK:STDOUT: %self.param_patt: %pattern_type.7f9 = value_param_pattern %self.patt [concrete] // CHECK:STDOUT: %t.patt: %pattern_type.912 = value_binding_pattern t [concrete] // CHECK:STDOUT: %t.param_patt: %pattern_type.912 = value_param_pattern %t.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %self.param: %D1 = value_param call_param0 // CHECK:STDOUT: %Self.ref: type = name_ref Self, constants.%D1 [concrete = constants.%D1] // CHECK:STDOUT: %self: %D1 = value_binding self, %self.param // CHECK:STDOUT: %t.param: %ptr.6e0 = value_param call_param1 // CHECK:STDOUT: %.loc10: type = splice_block %ptr [concrete = constants.%ptr.6e0] { // CHECK:STDOUT: %Base.ref: %Base.type = name_ref Base, file.%Base.decl [concrete = constants.%Base.generic] // CHECK:STDOUT: %T1.ref: type = name_ref T1, file.%T1.decl [concrete = constants.%T1] // CHECK:STDOUT: %Base: type = class_type @Base, @Base(constants.%T1) [concrete = constants.%Base.d5d] // CHECK:STDOUT: %ptr: type = ptr_type %Base [concrete = constants.%ptr.6e0] // CHECK:STDOUT: } // CHECK:STDOUT: %t: %ptr.6e0 = value_binding t, %t.param // CHECK:STDOUT: } // CHECK:STDOUT: %vtable_decl: ref %ptr.454 = vtable_decl @D1.vtable [concrete = constants.%D1.vtable_decl] // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%struct_type.base.cc2 [concrete = constants.%complete_type.dcb] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: vtable_decl = %vtable_decl // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%D1 // CHECK:STDOUT: .Base = // CHECK:STDOUT: .T1 = // CHECK:STDOUT: .base = %.loc9 // CHECK:STDOUT: .F = %D1.F.decl // CHECK:STDOUT: extend %Base // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: vtable @Base.vtable { // CHECK:STDOUT: @Base.%Base.F.specific_fn.loc6_1.1 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: vtable @D1.vtable { // CHECK:STDOUT: @D1.%D1.F.decl // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic virtual fn @Base.F(@Base.%T.loc4_17.2: type) { // CHECK:STDOUT: %T: type = symbolic_binding T, 0 [symbolic = %T (constants.%T)] // CHECK:STDOUT: %Base.loc5_29: type = class_type @Base, @Base(%T) [symbolic = %Base.loc5_29 (constants.%Base.d0c)] // CHECK:STDOUT: %pattern_type.loc5_23: type = pattern_type %Base.loc5_29 [symbolic = %pattern_type.loc5_23 (constants.%pattern_type.2f0)] // CHECK:STDOUT: %ptr.loc5_52.1: type = ptr_type %Base.loc5_29 [symbolic = %ptr.loc5_52.1 (constants.%ptr.4b1)] // CHECK:STDOUT: %pattern_type.loc5_42: type = pattern_type %ptr.loc5_52.1 [symbolic = %pattern_type.loc5_42 (constants.%pattern_type.cf5)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete.loc5_27: = require_complete_type %Base.loc5_29 [symbolic = %require_complete.loc5_27 (constants.%require_complete.1e0)] // CHECK:STDOUT: %require_complete.loc5_43: = require_complete_type %ptr.loc5_52.1 [symbolic = %require_complete.loc5_43 (constants.%require_complete.3cf)] // CHECK:STDOUT: // CHECK:STDOUT: virtual fn(%self.param: @Base.F.%Base.loc5_29 (%Base.d0c), %t.param: @Base.F.%ptr.loc5_52.1 (%ptr.4b1)) { // CHECK:STDOUT: !entry: // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: override fn @D1.F(%self.param: %D1, %t.param: %ptr.6e0) { // CHECK:STDOUT: !entry: // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @Base(constants.%T) { // CHECK:STDOUT: %T.loc4_17.1 => constants.%T // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %Base.F.type => constants.%Base.F.type.0f1 // CHECK:STDOUT: %Base.F => constants.%Base.F.cd3 // CHECK:STDOUT: %Base.F.specific_fn.loc6_1.2 => constants.%Base.F.specific_fn.f8c // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @Base.F(constants.%T) { // CHECK:STDOUT: %T => constants.%T // CHECK:STDOUT: %Base.loc5_29 => constants.%Base.d0c // CHECK:STDOUT: %pattern_type.loc5_23 => constants.%pattern_type.2f0 // CHECK:STDOUT: %ptr.loc5_52.1 => constants.%ptr.4b1 // CHECK:STDOUT: %pattern_type.loc5_42 => constants.%pattern_type.cf5 // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete.loc5_27 => constants.%require_complete.1e0 // CHECK:STDOUT: %require_complete.loc5_43 => constants.%require_complete.3cf // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @Base(constants.%T1) { // CHECK:STDOUT: %T.loc4_17.1 => constants.%T1 // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %Base.F.type => constants.%Base.F.type.cb1 // CHECK:STDOUT: %Base.F => constants.%Base.F.eab // CHECK:STDOUT: %Base.F.specific_fn.loc6_1.2 => constants.%Base.F.specific_fn.cd5 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @Base.F(constants.%T1) { // CHECK:STDOUT: %T => constants.%T1 // CHECK:STDOUT: %Base.loc5_29 => constants.%Base.d5d // CHECK:STDOUT: %pattern_type.loc5_23 => constants.%pattern_type.64f // CHECK:STDOUT: %ptr.loc5_52.1 => constants.%ptr.6e0 // CHECK:STDOUT: %pattern_type.loc5_42 => constants.%pattern_type.912 // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete.loc5_27 => constants.%complete_type.513 // CHECK:STDOUT: %require_complete.loc5_43 => constants.%complete_type.96e // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- fail_impl_generic_specifically_mismatch.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %T1: type = class_type @T1 [concrete] // CHECK:STDOUT: %T2: type = class_type @T2 [concrete] // CHECK:STDOUT: %type: type = facet_type [concrete] // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self] // CHECK:STDOUT: %pattern_type.98f: type = pattern_type type [concrete] // CHECK:STDOUT: %T: type = symbolic_binding T, 0 [symbolic] // CHECK:STDOUT: %Base.type: type = generic_class_type @Base [concrete] // CHECK:STDOUT: %Base.generic: %Base.type = struct_value () [concrete] // CHECK:STDOUT: %Base.d0c: type = class_type @Base, @Base(%T) [symbolic] // CHECK:STDOUT: %pattern_type.2f0: type = pattern_type %Base.d0c [symbolic] // CHECK:STDOUT: %ptr.9ce: type = ptr_type %T1 [concrete] // CHECK:STDOUT: %pattern_type.c16: type = pattern_type %ptr.9ce [concrete] // CHECK:STDOUT: %Base.F.type.0f1: type = fn_type @Base.F, @Base(%T) [symbolic] // CHECK:STDOUT: %Base.F.cd3: %Base.F.type.0f1 = struct_value () [symbolic] // CHECK:STDOUT: %ptr.454: type = ptr_type [concrete] // CHECK:STDOUT: %Base.F.specific_fn.f8c: = specific_function %Base.F.cd3, @Base.F(%T) [symbolic] // CHECK:STDOUT: %Base.vtable_decl: ref %ptr.454 = vtable_decl @Base.vtable [concrete] // CHECK:STDOUT: %struct_type.vptr: type = struct_type {.: %ptr.454} [concrete] // CHECK:STDOUT: %complete_type.513: = complete_type_witness %struct_type.vptr [concrete] // CHECK:STDOUT: %require_complete: = require_complete_type %Base.d0c [symbolic] // CHECK:STDOUT: %D1: type = class_type @D1 [concrete] // CHECK:STDOUT: %Base.d5d: type = class_type @Base, @Base(%T1) [concrete] // CHECK:STDOUT: %Base.F.type.cb1: type = fn_type @Base.F, @Base(%T1) [concrete] // CHECK:STDOUT: %Base.F.eab: %Base.F.type.cb1 = struct_value () [concrete] // CHECK:STDOUT: %pattern_type.64f: type = pattern_type %Base.d5d [concrete] // CHECK:STDOUT: %Base.F.specific_fn.cd5: = specific_function %Base.F.eab, @Base.F(%T1) [concrete] // CHECK:STDOUT: %D1.elem: type = unbound_element_type %D1, %Base.d5d [concrete] // CHECK:STDOUT: %pattern_type.7f9: type = pattern_type %D1 [concrete] // CHECK:STDOUT: %ptr.9f6: type = ptr_type %T2 [concrete] // CHECK:STDOUT: %pattern_type.64c: type = pattern_type %ptr.9f6 [concrete] // CHECK:STDOUT: %D1.F.type: type = fn_type @D1.F [concrete] // CHECK:STDOUT: %D1.F: %D1.F.type = struct_value () [concrete] // CHECK:STDOUT: %D1.vtable_decl: ref %ptr.454 = vtable_decl @D1.vtable [concrete] // CHECK:STDOUT: %struct_type.base.cc2: type = struct_type {.base: %Base.d5d} [concrete] // CHECK:STDOUT: %complete_type.dcb: = complete_type_witness %struct_type.base.cc2 [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .T1 = %T1.decl // CHECK:STDOUT: .T2 = %T2.decl // CHECK:STDOUT: .Base = %Base.decl // CHECK:STDOUT: .D1 = %D1.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %T1.decl: type = class_decl @T1 [concrete = constants.%T1] {} {} // CHECK:STDOUT: %T2.decl: type = class_decl @T2 [concrete = constants.%T2] {} {} // CHECK:STDOUT: %Base.decl: %Base.type = class_decl @Base [concrete = constants.%Base.generic] { // CHECK:STDOUT: %T.patt: %pattern_type.98f = symbolic_binding_pattern T, 0 [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %.loc7_21.1: type = splice_block %.loc7_21.2 [concrete = type] { // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %.loc7_21.2: type = type_literal type [concrete = type] // CHECK:STDOUT: } // CHECK:STDOUT: %T.loc7_17.2: type = symbolic_binding T, 0 [symbolic = %T.loc7_17.1 (constants.%T)] // CHECK:STDOUT: } // CHECK:STDOUT: %D1.decl: type = class_decl @D1 [concrete = constants.%D1] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @T1; // CHECK:STDOUT: // CHECK:STDOUT: class @T2; // CHECK:STDOUT: // CHECK:STDOUT: generic class @Base(%T.loc7_17.2: type) { // CHECK:STDOUT: %T.loc7_17.1: type = symbolic_binding T, 0 [symbolic = %T.loc7_17.1 (constants.%T)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %Base.F.type: type = fn_type @Base.F, @Base(%T.loc7_17.1) [symbolic = %Base.F.type (constants.%Base.F.type.0f1)] // CHECK:STDOUT: %Base.F: @Base.%Base.F.type (%Base.F.type.0f1) = struct_value () [symbolic = %Base.F (constants.%Base.F.cd3)] // CHECK:STDOUT: %Base.F.specific_fn.loc9_1.2: = specific_function %Base.F, @Base.F(%T.loc7_17.1) [symbolic = %Base.F.specific_fn.loc9_1.2 (constants.%Base.F.specific_fn.f8c)] // CHECK:STDOUT: // CHECK:STDOUT: class { // CHECK:STDOUT: %Base.F.decl: @Base.%Base.F.type (%Base.F.type.0f1) = fn_decl @Base.F [symbolic = @Base.%Base.F (constants.%Base.F.cd3)] { // CHECK:STDOUT: %self.patt: @Base.F.%pattern_type (%pattern_type.2f0) = value_binding_pattern self [concrete] // CHECK:STDOUT: %self.param_patt: @Base.F.%pattern_type (%pattern_type.2f0) = value_param_pattern %self.patt [concrete] // CHECK:STDOUT: %t.patt: %pattern_type.c16 = value_binding_pattern t [concrete] // CHECK:STDOUT: %t.param_patt: %pattern_type.c16 = value_param_pattern %t.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %self.param: @Base.F.%Base (%Base.d0c) = value_param call_param0 // CHECK:STDOUT: %.loc8_29.1: type = splice_block %Self.ref [symbolic = %Base (constants.%Base.d0c)] { // CHECK:STDOUT: %.loc8_29.2: type = specific_constant constants.%Base.d0c, @Base(constants.%T) [symbolic = %Base (constants.%Base.d0c)] // CHECK:STDOUT: %Self.ref: type = name_ref Self, %.loc8_29.2 [symbolic = %Base (constants.%Base.d0c)] // CHECK:STDOUT: } // CHECK:STDOUT: %self: @Base.F.%Base (%Base.d0c) = value_binding self, %self.param // CHECK:STDOUT: %t.param: %ptr.9ce = value_param call_param1 // CHECK:STDOUT: %.loc8_47: type = splice_block %ptr [concrete = constants.%ptr.9ce] { // CHECK:STDOUT: %T1.ref: type = name_ref T1, file.%T1.decl [concrete = constants.%T1] // CHECK:STDOUT: %ptr: type = ptr_type %T1.ref [concrete = constants.%ptr.9ce] // CHECK:STDOUT: } // CHECK:STDOUT: %t: %ptr.9ce = value_binding t, %t.param // CHECK:STDOUT: } // CHECK:STDOUT: %Base.F.specific_fn.loc9_1.1: = specific_function %Base.F.decl, @Base.F(constants.%T) [symbolic = %Base.F.specific_fn.loc9_1.2 (constants.%Base.F.specific_fn.f8c)] // CHECK:STDOUT: %vtable_decl: ref %ptr.454 = vtable_decl @Base.vtable [concrete = constants.%Base.vtable_decl] // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%struct_type.vptr [concrete = constants.%complete_type.513] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: vtable_decl = %vtable_decl // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%Base.d0c // CHECK:STDOUT: .T1 = // CHECK:STDOUT: .F = %Base.F.decl // CHECK:STDOUT: .T2 = // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @D1 { // CHECK:STDOUT: %Base.ref: %Base.type = name_ref Base, file.%Base.decl [concrete = constants.%Base.generic] // CHECK:STDOUT: %T1.ref: type = name_ref T1, file.%T1.decl [concrete = constants.%T1] // CHECK:STDOUT: %Base: type = class_type @Base, @Base(constants.%T1) [concrete = constants.%Base.d5d] // CHECK:STDOUT: %.loc12: %D1.elem = base_decl %Base, element0 [concrete] // CHECK:STDOUT: %D1.F.decl: %D1.F.type = fn_decl @D1.F [concrete = constants.%D1.F] { // CHECK:STDOUT: %self.patt: %pattern_type.7f9 = value_binding_pattern self [concrete] // CHECK:STDOUT: %self.param_patt: %pattern_type.7f9 = value_param_pattern %self.patt [concrete] // CHECK:STDOUT: %t.patt: %pattern_type.64c = value_binding_pattern t [concrete] // CHECK:STDOUT: %t.param_patt: %pattern_type.64c = value_param_pattern %t.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %self.param: %D1 = value_param call_param0 // CHECK:STDOUT: %Self.ref: type = name_ref Self, constants.%D1 [concrete = constants.%D1] // CHECK:STDOUT: %self: %D1 = value_binding self, %self.param // CHECK:STDOUT: %t.param: %ptr.9f6 = value_param call_param1 // CHECK:STDOUT: %.loc20: type = splice_block %ptr [concrete = constants.%ptr.9f6] { // CHECK:STDOUT: %T2.ref: type = name_ref T2, file.%T2.decl [concrete = constants.%T2] // CHECK:STDOUT: %ptr: type = ptr_type %T2.ref [concrete = constants.%ptr.9f6] // CHECK:STDOUT: } // CHECK:STDOUT: %t: %ptr.9f6 = value_binding t, %t.param // CHECK:STDOUT: } // CHECK:STDOUT: %vtable_decl: ref %ptr.454 = vtable_decl @D1.vtable [concrete = constants.%D1.vtable_decl] // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%struct_type.base.cc2 [concrete = constants.%complete_type.dcb] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: vtable_decl = %vtable_decl // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%D1 // CHECK:STDOUT: .Base = // CHECK:STDOUT: .T1 = // CHECK:STDOUT: .base = %.loc12 // CHECK:STDOUT: .T2 = // CHECK:STDOUT: .F = %D1.F.decl // CHECK:STDOUT: extend %Base // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: vtable @Base.vtable { // CHECK:STDOUT: @Base.%Base.F.specific_fn.loc9_1.1 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: vtable @D1.vtable { // CHECK:STDOUT: @D1.%D1.F.decl // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic virtual fn @Base.F(@Base.%T.loc7_17.2: type) { // CHECK:STDOUT: %T: type = symbolic_binding T, 0 [symbolic = %T (constants.%T)] // CHECK:STDOUT: %Base: type = class_type @Base, @Base(%T) [symbolic = %Base (constants.%Base.d0c)] // CHECK:STDOUT: %pattern_type: type = pattern_type %Base [symbolic = %pattern_type (constants.%pattern_type.2f0)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete: = require_complete_type %Base [symbolic = %require_complete (constants.%require_complete)] // CHECK:STDOUT: // CHECK:STDOUT: virtual fn(%self.param: @Base.F.%Base (%Base.d0c), %t.param: %ptr.9ce) { // CHECK:STDOUT: !entry: // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: override fn @D1.F(%self.param: %D1, %t.param: %ptr.9f6) { // CHECK:STDOUT: !entry: // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @Base(constants.%T) { // CHECK:STDOUT: %T.loc7_17.1 => constants.%T // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %Base.F.type => constants.%Base.F.type.0f1 // CHECK:STDOUT: %Base.F => constants.%Base.F.cd3 // CHECK:STDOUT: %Base.F.specific_fn.loc9_1.2 => constants.%Base.F.specific_fn.f8c // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @Base.F(constants.%T) { // CHECK:STDOUT: %T => constants.%T // CHECK:STDOUT: %Base => constants.%Base.d0c // CHECK:STDOUT: %pattern_type => constants.%pattern_type.2f0 // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete => constants.%require_complete // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @Base(constants.%T1) { // CHECK:STDOUT: %T.loc7_17.1 => constants.%T1 // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %Base.F.type => constants.%Base.F.type.cb1 // CHECK:STDOUT: %Base.F => constants.%Base.F.eab // CHECK:STDOUT: %Base.F.specific_fn.loc9_1.2 => constants.%Base.F.specific_fn.cd5 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @Base.F(constants.%T1) { // CHECK:STDOUT: %T => constants.%T1 // CHECK:STDOUT: %Base => constants.%Base.d5d // CHECK:STDOUT: %pattern_type => constants.%pattern_type.64f // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete => constants.%complete_type.513 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- fail_impl_generic_generic_mismatch.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %type: type = facet_type [concrete] // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self] // CHECK:STDOUT: %pattern_type.98f: type = pattern_type type [concrete] // CHECK:STDOUT: %T: type = symbolic_binding T, 0 [symbolic] // CHECK:STDOUT: %Base.type: type = generic_class_type @Base [concrete] // CHECK:STDOUT: %Base.generic: %Base.type = struct_value () [concrete] // CHECK:STDOUT: %Base: type = class_type @Base, @Base(%T) [symbolic] // CHECK:STDOUT: %pattern_type.2f0: type = pattern_type %Base [symbolic] // CHECK:STDOUT: %ptr.e8f: type = ptr_type %T [symbolic] // CHECK:STDOUT: %pattern_type.4f4: type = pattern_type %ptr.e8f [symbolic] // CHECK:STDOUT: %Base.F.type: type = fn_type @Base.F, @Base(%T) [symbolic] // CHECK:STDOUT: %Base.F: %Base.F.type = struct_value () [symbolic] // CHECK:STDOUT: %ptr.454: type = ptr_type [concrete] // CHECK:STDOUT: %Base.F.specific_fn: = specific_function %Base.F, @Base.F(%T) [symbolic] // CHECK:STDOUT: %Base.vtable_decl: ref %ptr.454 = vtable_decl @Base.vtable [concrete] // CHECK:STDOUT: %struct_type.vptr: type = struct_type {.: %ptr.454} [concrete] // CHECK:STDOUT: %complete_type.513: = complete_type_witness %struct_type.vptr [concrete] // CHECK:STDOUT: %require_complete.1e0: = require_complete_type %Base [symbolic] // CHECK:STDOUT: %require_complete.ef1: = require_complete_type %ptr.e8f [symbolic] // CHECK:STDOUT: %Derived.type: type = generic_class_type @Derived [concrete] // CHECK:STDOUT: %Derived.generic: %Derived.type = struct_value () [concrete] // CHECK:STDOUT: %Derived: type = class_type @Derived, @Derived(%T) [symbolic] // CHECK:STDOUT: %Derived.elem: type = unbound_element_type %Derived, %Base [symbolic] // CHECK:STDOUT: %pattern_type.b9d: type = pattern_type %Derived [symbolic] // CHECK:STDOUT: %pattern_type.51d: type = pattern_type %T [symbolic] // CHECK:STDOUT: %Derived.F.type: type = fn_type @Derived.F, @Derived(%T) [symbolic] // CHECK:STDOUT: %Derived.F: %Derived.F.type = struct_value () [symbolic] // CHECK:STDOUT: %Derived.F.specific_fn: = specific_function %Derived.F, @Derived.F(%T) [symbolic] // CHECK:STDOUT: %Derived.vtable_decl: ref %ptr.454 = vtable_decl @Derived.vtable [concrete] // CHECK:STDOUT: %struct_type.base.b5f: type = struct_type {.base: %Base} [symbolic] // CHECK:STDOUT: %complete_type.33f: = complete_type_witness %struct_type.base.b5f [symbolic] // CHECK:STDOUT: %require_complete.a43: = require_complete_type %Derived [symbolic] // CHECK:STDOUT: %require_complete.944: = require_complete_type %T [symbolic] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .Base = %Base.decl // CHECK:STDOUT: .Derived = %Derived.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %Base.decl: %Base.type = class_decl @Base [concrete = constants.%Base.generic] { // CHECK:STDOUT: %T.patt: %pattern_type.98f = symbolic_binding_pattern T, 0 [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %.loc4_25.1: type = splice_block %.loc4_25.2 [concrete = type] { // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %.loc4_25.2: type = type_literal type [concrete = type] // CHECK:STDOUT: } // CHECK:STDOUT: %T.loc4_21.2: type = symbolic_binding T, 0 [symbolic = %T.loc4_21.1 (constants.%T)] // CHECK:STDOUT: } // CHECK:STDOUT: %Derived.decl: %Derived.type = class_decl @Derived [concrete = constants.%Derived.generic] { // CHECK:STDOUT: %T.patt: %pattern_type.98f = symbolic_binding_pattern T, 0 [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %.loc7_19.1: type = splice_block %.loc7_19.2 [concrete = type] { // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %.loc7_19.2: type = type_literal type [concrete = type] // CHECK:STDOUT: } // CHECK:STDOUT: %T.loc7_15.2: type = symbolic_binding T, 0 [symbolic = %T.loc7_15.1 (constants.%T)] // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic class @Base(%T.loc4_21.2: type) { // CHECK:STDOUT: %T.loc4_21.1: type = symbolic_binding T, 0 [symbolic = %T.loc4_21.1 (constants.%T)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %Base.F.type: type = fn_type @Base.F, @Base(%T.loc4_21.1) [symbolic = %Base.F.type (constants.%Base.F.type)] // CHECK:STDOUT: %Base.F: @Base.%Base.F.type (%Base.F.type) = struct_value () [symbolic = %Base.F (constants.%Base.F)] // CHECK:STDOUT: %Base.F.specific_fn.loc6_1.2: = specific_function %Base.F, @Base.F(%T.loc4_21.1) [symbolic = %Base.F.specific_fn.loc6_1.2 (constants.%Base.F.specific_fn)] // CHECK:STDOUT: // CHECK:STDOUT: class { // CHECK:STDOUT: %Base.F.decl: @Base.%Base.F.type (%Base.F.type) = fn_decl @Base.F [symbolic = @Base.%Base.F (constants.%Base.F)] { // CHECK:STDOUT: %self.patt: @Base.F.%pattern_type.loc5_23 (%pattern_type.2f0) = value_binding_pattern self [concrete] // CHECK:STDOUT: %self.param_patt: @Base.F.%pattern_type.loc5_23 (%pattern_type.2f0) = value_param_pattern %self.patt [concrete] // CHECK:STDOUT: %t.patt: @Base.F.%pattern_type.loc5_42 (%pattern_type.4f4) = value_binding_pattern t [concrete] // CHECK:STDOUT: %t.param_patt: @Base.F.%pattern_type.loc5_42 (%pattern_type.4f4) = value_param_pattern %t.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %self.param: @Base.F.%Base (%Base) = value_param call_param0 // CHECK:STDOUT: %.loc5_29.1: type = splice_block %Self.ref [symbolic = %Base (constants.%Base)] { // CHECK:STDOUT: %.loc5_29.2: type = specific_constant constants.%Base, @Base(constants.%T) [symbolic = %Base (constants.%Base)] // CHECK:STDOUT: %Self.ref: type = name_ref Self, %.loc5_29.2 [symbolic = %Base (constants.%Base)] // CHECK:STDOUT: } // CHECK:STDOUT: %self: @Base.F.%Base (%Base) = value_binding self, %self.param // CHECK:STDOUT: %t.param: @Base.F.%ptr.loc5_46.1 (%ptr.e8f) = value_param call_param1 // CHECK:STDOUT: %.loc5_46: type = splice_block %ptr.loc5_46.2 [symbolic = %ptr.loc5_46.1 (constants.%ptr.e8f)] { // CHECK:STDOUT: %T.ref: type = name_ref T, @Base.%T.loc4_21.2 [symbolic = %T (constants.%T)] // CHECK:STDOUT: %ptr.loc5_46.2: type = ptr_type %T.ref [symbolic = %ptr.loc5_46.1 (constants.%ptr.e8f)] // CHECK:STDOUT: } // CHECK:STDOUT: %t: @Base.F.%ptr.loc5_46.1 (%ptr.e8f) = value_binding t, %t.param // CHECK:STDOUT: } // CHECK:STDOUT: %Base.F.specific_fn.loc6_1.1: = specific_function %Base.F.decl, @Base.F(constants.%T) [symbolic = %Base.F.specific_fn.loc6_1.2 (constants.%Base.F.specific_fn)] // CHECK:STDOUT: %vtable_decl: ref %ptr.454 = vtable_decl @Base.vtable [concrete = constants.%Base.vtable_decl] // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%struct_type.vptr [concrete = constants.%complete_type.513] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: vtable_decl = %vtable_decl // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%Base // CHECK:STDOUT: .T = // CHECK:STDOUT: .F = %Base.F.decl // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic class @Derived(%T.loc7_15.2: type) { // CHECK:STDOUT: %T.loc7_15.1: type = symbolic_binding T, 0 [symbolic = %T.loc7_15.1 (constants.%T)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %Base.loc8_22.2: type = class_type @Base, @Base(%T.loc7_15.1) [symbolic = %Base.loc8_22.2 (constants.%Base)] // CHECK:STDOUT: %require_complete: = require_complete_type %Base.loc8_22.2 [symbolic = %require_complete (constants.%require_complete.1e0)] // CHECK:STDOUT: %Derived: type = class_type @Derived, @Derived(%T.loc7_15.1) [symbolic = %Derived (constants.%Derived)] // CHECK:STDOUT: %Derived.elem: type = unbound_element_type %Derived, %Base.loc8_22.2 [symbolic = %Derived.elem (constants.%Derived.elem)] // CHECK:STDOUT: %Derived.F.type: type = fn_type @Derived.F, @Derived(%T.loc7_15.1) [symbolic = %Derived.F.type (constants.%Derived.F.type)] // CHECK:STDOUT: %Derived.F: @Derived.%Derived.F.type (%Derived.F.type) = struct_value () [symbolic = %Derived.F (constants.%Derived.F)] // CHECK:STDOUT: %Derived.F.specific_fn.loc17_1.2: = specific_function %Derived.F, @Derived.F(%T.loc7_15.1) [symbolic = %Derived.F.specific_fn.loc17_1.2 (constants.%Derived.F.specific_fn)] // CHECK:STDOUT: %struct_type.base: type = struct_type {.base: @Derived.%Base.loc8_22.2 (%Base)} [symbolic = %struct_type.base (constants.%struct_type.base.b5f)] // CHECK:STDOUT: %complete_type.loc17_1.2: = complete_type_witness %struct_type.base [symbolic = %complete_type.loc17_1.2 (constants.%complete_type.33f)] // CHECK:STDOUT: // CHECK:STDOUT: class { // CHECK:STDOUT: %Base.ref: %Base.type = name_ref Base, file.%Base.decl [concrete = constants.%Base.generic] // CHECK:STDOUT: %T.ref: type = name_ref T, %T.loc7_15.2 [symbolic = %T.loc7_15.1 (constants.%T)] // CHECK:STDOUT: %Base.loc8_22.1: type = class_type @Base, @Base(constants.%T) [symbolic = %Base.loc8_22.2 (constants.%Base)] // CHECK:STDOUT: %.loc8: @Derived.%Derived.elem (%Derived.elem) = base_decl %Base.loc8_22.1, element0 [concrete] // CHECK:STDOUT: %Derived.F.decl: @Derived.%Derived.F.type (%Derived.F.type) = fn_decl @Derived.F [symbolic = @Derived.%Derived.F (constants.%Derived.F)] { // CHECK:STDOUT: %self.patt: @Derived.F.%pattern_type.loc16_24 (%pattern_type.b9d) = value_binding_pattern self [concrete] // CHECK:STDOUT: %self.param_patt: @Derived.F.%pattern_type.loc16_24 (%pattern_type.b9d) = value_param_pattern %self.patt [concrete] // CHECK:STDOUT: %t.patt: @Derived.F.%pattern_type.loc16_43 (%pattern_type.51d) = value_binding_pattern t [concrete] // CHECK:STDOUT: %t.param_patt: @Derived.F.%pattern_type.loc16_43 (%pattern_type.51d) = value_param_pattern %t.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %self.param: @Derived.F.%Derived (%Derived) = value_param call_param0 // CHECK:STDOUT: %.loc16_30.1: type = splice_block %Self.ref [symbolic = %Derived (constants.%Derived)] { // CHECK:STDOUT: %.loc16_30.2: type = specific_constant constants.%Derived, @Derived(constants.%T) [symbolic = %Derived (constants.%Derived)] // CHECK:STDOUT: %Self.ref: type = name_ref Self, %.loc16_30.2 [symbolic = %Derived (constants.%Derived)] // CHECK:STDOUT: } // CHECK:STDOUT: %self: @Derived.F.%Derived (%Derived) = value_binding self, %self.param // CHECK:STDOUT: %t.param: @Derived.F.%T (%T) = value_param call_param1 // CHECK:STDOUT: %T.ref: type = name_ref T, @Derived.%T.loc7_15.2 [symbolic = %T (constants.%T)] // CHECK:STDOUT: %t: @Derived.F.%T (%T) = value_binding t, %t.param // CHECK:STDOUT: } // CHECK:STDOUT: %Derived.F.specific_fn.loc17_1.1: = specific_function %Derived.F.decl, @Derived.F(constants.%T) [symbolic = %Derived.F.specific_fn.loc17_1.2 (constants.%Derived.F.specific_fn)] // CHECK:STDOUT: %vtable_decl: ref %ptr.454 = vtable_decl @Derived.vtable [concrete = constants.%Derived.vtable_decl] // CHECK:STDOUT: %complete_type.loc17_1.1: = complete_type_witness constants.%struct_type.base.b5f [symbolic = %complete_type.loc17_1.2 (constants.%complete_type.33f)] // CHECK:STDOUT: complete_type_witness = %complete_type.loc17_1.1 // CHECK:STDOUT: vtable_decl = %vtable_decl // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%Derived // CHECK:STDOUT: .Base = // CHECK:STDOUT: .T = // CHECK:STDOUT: .base = %.loc8 // CHECK:STDOUT: .F = %Derived.F.decl // CHECK:STDOUT: extend %Base.loc8_22.1 // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: vtable @Base.vtable { // CHECK:STDOUT: @Base.%Base.F.specific_fn.loc6_1.1 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: vtable @Derived.vtable { // CHECK:STDOUT: @Derived.%Derived.F.specific_fn.loc17_1.1 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic virtual fn @Base.F(@Base.%T.loc4_21.2: type) { // CHECK:STDOUT: %T: type = symbolic_binding T, 0 [symbolic = %T (constants.%T)] // CHECK:STDOUT: %Base: type = class_type @Base, @Base(%T) [symbolic = %Base (constants.%Base)] // CHECK:STDOUT: %pattern_type.loc5_23: type = pattern_type %Base [symbolic = %pattern_type.loc5_23 (constants.%pattern_type.2f0)] // CHECK:STDOUT: %ptr.loc5_46.1: type = ptr_type %T [symbolic = %ptr.loc5_46.1 (constants.%ptr.e8f)] // CHECK:STDOUT: %pattern_type.loc5_42: type = pattern_type %ptr.loc5_46.1 [symbolic = %pattern_type.loc5_42 (constants.%pattern_type.4f4)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete.loc5_27: = require_complete_type %Base [symbolic = %require_complete.loc5_27 (constants.%require_complete.1e0)] // CHECK:STDOUT: %require_complete.loc5_43: = require_complete_type %ptr.loc5_46.1 [symbolic = %require_complete.loc5_43 (constants.%require_complete.ef1)] // CHECK:STDOUT: // CHECK:STDOUT: virtual fn(%self.param: @Base.F.%Base (%Base), %t.param: @Base.F.%ptr.loc5_46.1 (%ptr.e8f)) { // CHECK:STDOUT: !entry: // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic override fn @Derived.F(@Derived.%T.loc7_15.2: type) { // CHECK:STDOUT: %T: type = symbolic_binding T, 0 [symbolic = %T (constants.%T)] // CHECK:STDOUT: %Derived: type = class_type @Derived, @Derived(%T) [symbolic = %Derived (constants.%Derived)] // CHECK:STDOUT: %pattern_type.loc16_24: type = pattern_type %Derived [symbolic = %pattern_type.loc16_24 (constants.%pattern_type.b9d)] // CHECK:STDOUT: %Base: type = class_type @Base, @Base(%T) [symbolic = %Base (constants.%Base)] // CHECK:STDOUT: %require_complete.loc16_46: = require_complete_type %Base [symbolic = %require_complete.loc16_46 (constants.%require_complete.1e0)] // CHECK:STDOUT: %pattern_type.loc16_43: type = pattern_type %T [symbolic = %pattern_type.loc16_43 (constants.%pattern_type.51d)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete.loc16_28: = require_complete_type %Derived [symbolic = %require_complete.loc16_28 (constants.%require_complete.a43)] // CHECK:STDOUT: %require_complete.loc16_44: = require_complete_type %T [symbolic = %require_complete.loc16_44 (constants.%require_complete.944)] // CHECK:STDOUT: // CHECK:STDOUT: override fn(%self.param: @Derived.F.%Derived (%Derived), %t.param: @Derived.F.%T (%T)) { // CHECK:STDOUT: !entry: // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @Base(constants.%T) { // CHECK:STDOUT: %T.loc4_21.1 => constants.%T // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %Base.F.type => constants.%Base.F.type // CHECK:STDOUT: %Base.F => constants.%Base.F // CHECK:STDOUT: %Base.F.specific_fn.loc6_1.2 => constants.%Base.F.specific_fn // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @Base.F(constants.%T) { // CHECK:STDOUT: %T => constants.%T // CHECK:STDOUT: %Base => constants.%Base // CHECK:STDOUT: %pattern_type.loc5_23 => constants.%pattern_type.2f0 // CHECK:STDOUT: %ptr.loc5_46.1 => constants.%ptr.e8f // CHECK:STDOUT: %pattern_type.loc5_42 => constants.%pattern_type.4f4 // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete.loc5_27 => constants.%require_complete.1e0 // CHECK:STDOUT: %require_complete.loc5_43 => constants.%require_complete.ef1 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @Derived(constants.%T) { // CHECK:STDOUT: %T.loc7_15.1 => constants.%T // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %Base.loc8_22.2 => constants.%Base // CHECK:STDOUT: %require_complete => constants.%require_complete.1e0 // CHECK:STDOUT: %Derived => constants.%Derived // CHECK:STDOUT: %Derived.elem => constants.%Derived.elem // CHECK:STDOUT: %Derived.F.type => constants.%Derived.F.type // CHECK:STDOUT: %Derived.F => constants.%Derived.F // CHECK:STDOUT: %Derived.F.specific_fn.loc17_1.2 => constants.%Derived.F.specific_fn // CHECK:STDOUT: %struct_type.base => constants.%struct_type.base.b5f // CHECK:STDOUT: %complete_type.loc17_1.2 => constants.%complete_type.33f // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @Derived.F(constants.%T) { // CHECK:STDOUT: %T => constants.%T // CHECK:STDOUT: %Derived => constants.%Derived // CHECK:STDOUT: %pattern_type.loc16_24 => constants.%pattern_type.b9d // CHECK:STDOUT: %Base => constants.%Base // CHECK:STDOUT: %require_complete.loc16_46 => constants.%require_complete.1e0 // CHECK:STDOUT: %pattern_type.loc16_43 => constants.%pattern_type.51d // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete.loc16_28 => constants.%require_complete.a43 // CHECK:STDOUT: %require_complete.loc16_44 => constants.%require_complete.944 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- impl_generic_generic.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %type: type = facet_type [concrete] // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self] // CHECK:STDOUT: %pattern_type.98f: type = pattern_type type [concrete] // CHECK:STDOUT: %T: type = symbolic_binding T, 0 [symbolic] // CHECK:STDOUT: %Base.type: type = generic_class_type @Base [concrete] // CHECK:STDOUT: %Base.generic: %Base.type = struct_value () [concrete] // CHECK:STDOUT: %Base.d0c: type = class_type @Base, @Base(%T) [symbolic] // CHECK:STDOUT: %pattern_type.2f0: type = pattern_type %Base.d0c [symbolic] // CHECK:STDOUT: %pattern_type.51d: type = pattern_type %T [symbolic] // CHECK:STDOUT: %Base.F.type.0f1: type = fn_type @Base.F, @Base(%T) [symbolic] // CHECK:STDOUT: %Base.F.cd3: %Base.F.type.0f1 = struct_value () [symbolic] // CHECK:STDOUT: %ptr.454: type = ptr_type [concrete] // CHECK:STDOUT: %Base.F.specific_fn.f8c: = specific_function %Base.F.cd3, @Base.F(%T) [symbolic] // CHECK:STDOUT: %Base.vtable_decl: ref %ptr.454 = vtable_decl @Base.vtable [concrete] // CHECK:STDOUT: %struct_type.vptr: type = struct_type {.: %ptr.454} [concrete] // CHECK:STDOUT: %complete_type.513: = complete_type_witness %struct_type.vptr [concrete] // CHECK:STDOUT: %require_complete.1e0: = require_complete_type %Base.d0c [symbolic] // CHECK:STDOUT: %require_complete.944: = require_complete_type %T [symbolic] // CHECK:STDOUT: %Derived.type: type = generic_class_type @Derived [concrete] // CHECK:STDOUT: %Derived.generic: %Derived.type = struct_value () [concrete] // CHECK:STDOUT: %Derived: type = class_type @Derived, @Derived(%T) [symbolic] // CHECK:STDOUT: %ptr.e8f: type = ptr_type %T [symbolic] // CHECK:STDOUT: %Base.f29: type = class_type @Base, @Base(%ptr.e8f) [symbolic] // CHECK:STDOUT: %Base.F.type.c53: type = fn_type @Base.F, @Base(%ptr.e8f) [symbolic] // CHECK:STDOUT: %Base.F.c0a: %Base.F.type.c53 = struct_value () [symbolic] // CHECK:STDOUT: %pattern_type.d96: type = pattern_type %Base.f29 [symbolic] // CHECK:STDOUT: %pattern_type.4f4: type = pattern_type %ptr.e8f [symbolic] // CHECK:STDOUT: %Base.F.specific_fn.35d: = specific_function %Base.F.c0a, @Base.F(%ptr.e8f) [symbolic] // CHECK:STDOUT: %require_complete.b3c: = require_complete_type %Base.f29 [symbolic] // CHECK:STDOUT: %Derived.elem: type = unbound_element_type %Derived, %Base.f29 [symbolic] // CHECK:STDOUT: %pattern_type.b9d: type = pattern_type %Derived [symbolic] // CHECK:STDOUT: %Derived.F.type: type = fn_type @Derived.F, @Derived(%T) [symbolic] // CHECK:STDOUT: %Derived.F: %Derived.F.type = struct_value () [symbolic] // CHECK:STDOUT: %Derived.F.specific_fn: = specific_function %Derived.F, @Derived.F(%T) [symbolic] // CHECK:STDOUT: %Derived.vtable_decl: ref %ptr.454 = vtable_decl @Derived.vtable [concrete] // CHECK:STDOUT: %struct_type.base.aa5: type = struct_type {.base: %Base.f29} [symbolic] // CHECK:STDOUT: %complete_type.8c0: = complete_type_witness %struct_type.base.aa5 [symbolic] // CHECK:STDOUT: %require_complete.a43: = require_complete_type %Derived [symbolic] // CHECK:STDOUT: %require_complete.ef1: = require_complete_type %ptr.e8f [symbolic] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .Base = %Base.decl // CHECK:STDOUT: .Derived = %Derived.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %Base.decl: %Base.type = class_decl @Base [concrete = constants.%Base.generic] { // CHECK:STDOUT: %T.patt: %pattern_type.98f = symbolic_binding_pattern T, 0 [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %.loc4_25.1: type = splice_block %.loc4_25.2 [concrete = type] { // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %.loc4_25.2: type = type_literal type [concrete = type] // CHECK:STDOUT: } // CHECK:STDOUT: %T.loc4_21.2: type = symbolic_binding T, 0 [symbolic = %T.loc4_21.1 (constants.%T)] // CHECK:STDOUT: } // CHECK:STDOUT: %Derived.decl: %Derived.type = class_decl @Derived [concrete = constants.%Derived.generic] { // CHECK:STDOUT: %T.patt: %pattern_type.98f = symbolic_binding_pattern T, 0 [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %.loc7_19.1: type = splice_block %.loc7_19.2 [concrete = type] { // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %.loc7_19.2: type = type_literal type [concrete = type] // CHECK:STDOUT: } // CHECK:STDOUT: %T.loc7_15.2: type = symbolic_binding T, 0 [symbolic = %T.loc7_15.1 (constants.%T)] // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic class @Base(%T.loc4_21.2: type) { // CHECK:STDOUT: %T.loc4_21.1: type = symbolic_binding T, 0 [symbolic = %T.loc4_21.1 (constants.%T)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %Base.F.type: type = fn_type @Base.F, @Base(%T.loc4_21.1) [symbolic = %Base.F.type (constants.%Base.F.type.0f1)] // CHECK:STDOUT: %Base.F: @Base.%Base.F.type (%Base.F.type.0f1) = struct_value () [symbolic = %Base.F (constants.%Base.F.cd3)] // CHECK:STDOUT: %Base.F.specific_fn.loc6_1.2: = specific_function %Base.F, @Base.F(%T.loc4_21.1) [symbolic = %Base.F.specific_fn.loc6_1.2 (constants.%Base.F.specific_fn.f8c)] // CHECK:STDOUT: // CHECK:STDOUT: class { // CHECK:STDOUT: %Base.F.decl: @Base.%Base.F.type (%Base.F.type.0f1) = fn_decl @Base.F [symbolic = @Base.%Base.F (constants.%Base.F.cd3)] { // CHECK:STDOUT: %self.patt: @Base.F.%pattern_type.loc5_23 (%pattern_type.2f0) = value_binding_pattern self [concrete] // CHECK:STDOUT: %self.param_patt: @Base.F.%pattern_type.loc5_23 (%pattern_type.2f0) = value_param_pattern %self.patt [concrete] // CHECK:STDOUT: %t.patt: @Base.F.%pattern_type.loc5_42 (%pattern_type.51d) = value_binding_pattern t [concrete] // CHECK:STDOUT: %t.param_patt: @Base.F.%pattern_type.loc5_42 (%pattern_type.51d) = value_param_pattern %t.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %self.param: @Base.F.%Base (%Base.d0c) = value_param call_param0 // CHECK:STDOUT: %.loc5_29.1: type = splice_block %Self.ref [symbolic = %Base (constants.%Base.d0c)] { // CHECK:STDOUT: %.loc5_29.2: type = specific_constant constants.%Base.d0c, @Base(constants.%T) [symbolic = %Base (constants.%Base.d0c)] // CHECK:STDOUT: %Self.ref: type = name_ref Self, %.loc5_29.2 [symbolic = %Base (constants.%Base.d0c)] // CHECK:STDOUT: } // CHECK:STDOUT: %self: @Base.F.%Base (%Base.d0c) = value_binding self, %self.param // CHECK:STDOUT: %t.param: @Base.F.%T (%T) = value_param call_param1 // CHECK:STDOUT: %T.ref: type = name_ref T, @Base.%T.loc4_21.2 [symbolic = %T (constants.%T)] // CHECK:STDOUT: %t: @Base.F.%T (%T) = value_binding t, %t.param // CHECK:STDOUT: } // CHECK:STDOUT: %Base.F.specific_fn.loc6_1.1: = specific_function %Base.F.decl, @Base.F(constants.%T) [symbolic = %Base.F.specific_fn.loc6_1.2 (constants.%Base.F.specific_fn.f8c)] // CHECK:STDOUT: %vtable_decl: ref %ptr.454 = vtable_decl @Base.vtable [concrete = constants.%Base.vtable_decl] // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%struct_type.vptr [concrete = constants.%complete_type.513] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: vtable_decl = %vtable_decl // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%Base.d0c // CHECK:STDOUT: .T = // CHECK:STDOUT: .F = %Base.F.decl // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic class @Derived(%T.loc7_15.2: type) { // CHECK:STDOUT: %T.loc7_15.1: type = symbolic_binding T, 0 [symbolic = %T.loc7_15.1 (constants.%T)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %ptr.loc8_22.2: type = ptr_type %T.loc7_15.1 [symbolic = %ptr.loc8_22.2 (constants.%ptr.e8f)] // CHECK:STDOUT: %Base.loc8_23.2: type = class_type @Base, @Base(%ptr.loc8_22.2) [symbolic = %Base.loc8_23.2 (constants.%Base.f29)] // CHECK:STDOUT: %require_complete: = require_complete_type %Base.loc8_23.2 [symbolic = %require_complete (constants.%require_complete.b3c)] // CHECK:STDOUT: %Derived: type = class_type @Derived, @Derived(%T.loc7_15.1) [symbolic = %Derived (constants.%Derived)] // CHECK:STDOUT: %Derived.elem: type = unbound_element_type %Derived, %Base.loc8_23.2 [symbolic = %Derived.elem (constants.%Derived.elem)] // CHECK:STDOUT: %Derived.F.type: type = fn_type @Derived.F, @Derived(%T.loc7_15.1) [symbolic = %Derived.F.type (constants.%Derived.F.type)] // CHECK:STDOUT: %Derived.F: @Derived.%Derived.F.type (%Derived.F.type) = struct_value () [symbolic = %Derived.F (constants.%Derived.F)] // CHECK:STDOUT: %Derived.F.specific_fn.loc10_1.2: = specific_function %Derived.F, @Derived.F(%T.loc7_15.1) [symbolic = %Derived.F.specific_fn.loc10_1.2 (constants.%Derived.F.specific_fn)] // CHECK:STDOUT: %struct_type.base: type = struct_type {.base: @Derived.%Base.loc8_23.2 (%Base.f29)} [symbolic = %struct_type.base (constants.%struct_type.base.aa5)] // CHECK:STDOUT: %complete_type.loc10_1.2: = complete_type_witness %struct_type.base [symbolic = %complete_type.loc10_1.2 (constants.%complete_type.8c0)] // CHECK:STDOUT: // CHECK:STDOUT: class { // CHECK:STDOUT: %Base.ref: %Base.type = name_ref Base, file.%Base.decl [concrete = constants.%Base.generic] // CHECK:STDOUT: %T.ref: type = name_ref T, %T.loc7_15.2 [symbolic = %T.loc7_15.1 (constants.%T)] // CHECK:STDOUT: %ptr.loc8_22.1: type = ptr_type %T.ref [symbolic = %ptr.loc8_22.2 (constants.%ptr.e8f)] // CHECK:STDOUT: %Base.loc8_23.1: type = class_type @Base, @Base(constants.%ptr.e8f) [symbolic = %Base.loc8_23.2 (constants.%Base.f29)] // CHECK:STDOUT: %.loc8: @Derived.%Derived.elem (%Derived.elem) = base_decl %Base.loc8_23.1, element0 [concrete] // CHECK:STDOUT: %Derived.F.decl: @Derived.%Derived.F.type (%Derived.F.type) = fn_decl @Derived.F [symbolic = @Derived.%Derived.F (constants.%Derived.F)] { // CHECK:STDOUT: %self.patt: @Derived.F.%pattern_type.loc9_24 (%pattern_type.b9d) = value_binding_pattern self [concrete] // CHECK:STDOUT: %self.param_patt: @Derived.F.%pattern_type.loc9_24 (%pattern_type.b9d) = value_param_pattern %self.patt [concrete] // CHECK:STDOUT: %t.patt: @Derived.F.%pattern_type.loc9_43 (%pattern_type.4f4) = value_binding_pattern t [concrete] // CHECK:STDOUT: %t.param_patt: @Derived.F.%pattern_type.loc9_43 (%pattern_type.4f4) = value_param_pattern %t.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %self.param: @Derived.F.%Derived (%Derived) = value_param call_param0 // CHECK:STDOUT: %.loc9_30.1: type = splice_block %Self.ref [symbolic = %Derived (constants.%Derived)] { // CHECK:STDOUT: %.loc9_30.2: type = specific_constant constants.%Derived, @Derived(constants.%T) [symbolic = %Derived (constants.%Derived)] // CHECK:STDOUT: %Self.ref: type = name_ref Self, %.loc9_30.2 [symbolic = %Derived (constants.%Derived)] // CHECK:STDOUT: } // CHECK:STDOUT: %self: @Derived.F.%Derived (%Derived) = value_binding self, %self.param // CHECK:STDOUT: %t.param: @Derived.F.%ptr.loc9_46 (%ptr.e8f) = value_param call_param1 // CHECK:STDOUT: %.loc9_47: type = splice_block %ptr.loc9_47 [symbolic = %ptr.loc9_46 (constants.%ptr.e8f)] { // CHECK:STDOUT: %T.ref: type = name_ref T, @Derived.%T.loc7_15.2 [symbolic = %T (constants.%T)] // CHECK:STDOUT: %ptr.loc9_47: type = ptr_type %T.ref [symbolic = %ptr.loc9_46 (constants.%ptr.e8f)] // CHECK:STDOUT: } // CHECK:STDOUT: %t: @Derived.F.%ptr.loc9_46 (%ptr.e8f) = value_binding t, %t.param // CHECK:STDOUT: } // CHECK:STDOUT: %Derived.F.specific_fn.loc10_1.1: = specific_function %Derived.F.decl, @Derived.F(constants.%T) [symbolic = %Derived.F.specific_fn.loc10_1.2 (constants.%Derived.F.specific_fn)] // CHECK:STDOUT: %vtable_decl: ref %ptr.454 = vtable_decl @Derived.vtable [concrete = constants.%Derived.vtable_decl] // CHECK:STDOUT: %complete_type.loc10_1.1: = complete_type_witness constants.%struct_type.base.aa5 [symbolic = %complete_type.loc10_1.2 (constants.%complete_type.8c0)] // CHECK:STDOUT: complete_type_witness = %complete_type.loc10_1.1 // CHECK:STDOUT: vtable_decl = %vtable_decl // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%Derived // CHECK:STDOUT: .Base = // CHECK:STDOUT: .T = // CHECK:STDOUT: .base = %.loc8 // CHECK:STDOUT: .F = %Derived.F.decl // CHECK:STDOUT: extend %Base.loc8_23.1 // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: vtable @Base.vtable { // CHECK:STDOUT: @Base.%Base.F.specific_fn.loc6_1.1 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: vtable @Derived.vtable { // CHECK:STDOUT: @Derived.%Derived.F.specific_fn.loc10_1.1 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic virtual fn @Base.F(@Base.%T.loc4_21.2: type) { // CHECK:STDOUT: %T: type = symbolic_binding T, 0 [symbolic = %T (constants.%T)] // CHECK:STDOUT: %Base: type = class_type @Base, @Base(%T) [symbolic = %Base (constants.%Base.d0c)] // CHECK:STDOUT: %pattern_type.loc5_23: type = pattern_type %Base [symbolic = %pattern_type.loc5_23 (constants.%pattern_type.2f0)] // CHECK:STDOUT: %pattern_type.loc5_42: type = pattern_type %T [symbolic = %pattern_type.loc5_42 (constants.%pattern_type.51d)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete.loc5_27: = require_complete_type %Base [symbolic = %require_complete.loc5_27 (constants.%require_complete.1e0)] // CHECK:STDOUT: %require_complete.loc5_43: = require_complete_type %T [symbolic = %require_complete.loc5_43 (constants.%require_complete.944)] // CHECK:STDOUT: // CHECK:STDOUT: virtual fn(%self.param: @Base.F.%Base (%Base.d0c), %t.param: @Base.F.%T (%T)) { // CHECK:STDOUT: !entry: // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic override fn @Derived.F(@Derived.%T.loc7_15.2: type) { // CHECK:STDOUT: %T: type = symbolic_binding T, 0 [symbolic = %T (constants.%T)] // CHECK:STDOUT: %Derived: type = class_type @Derived, @Derived(%T) [symbolic = %Derived (constants.%Derived)] // CHECK:STDOUT: %pattern_type.loc9_24: type = pattern_type %Derived [symbolic = %pattern_type.loc9_24 (constants.%pattern_type.b9d)] // CHECK:STDOUT: %ptr.loc9_46: type = ptr_type %T [symbolic = %ptr.loc9_46 (constants.%ptr.e8f)] // CHECK:STDOUT: %Base: type = class_type @Base, @Base(%ptr.loc9_46) [symbolic = %Base (constants.%Base.f29)] // CHECK:STDOUT: %require_complete.loc9_46: = require_complete_type %Base [symbolic = %require_complete.loc9_46 (constants.%require_complete.b3c)] // CHECK:STDOUT: %pattern_type.loc9_43: type = pattern_type %ptr.loc9_46 [symbolic = %pattern_type.loc9_43 (constants.%pattern_type.4f4)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete.loc9_28: = require_complete_type %Derived [symbolic = %require_complete.loc9_28 (constants.%require_complete.a43)] // CHECK:STDOUT: %require_complete.loc9_44: = require_complete_type %ptr.loc9_46 [symbolic = %require_complete.loc9_44 (constants.%require_complete.ef1)] // CHECK:STDOUT: // CHECK:STDOUT: override fn(%self.param: @Derived.F.%Derived (%Derived), %t.param: @Derived.F.%ptr.loc9_46 (%ptr.e8f)) { // CHECK:STDOUT: !entry: // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @Base(constants.%T) { // CHECK:STDOUT: %T.loc4_21.1 => constants.%T // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %Base.F.type => constants.%Base.F.type.0f1 // CHECK:STDOUT: %Base.F => constants.%Base.F.cd3 // CHECK:STDOUT: %Base.F.specific_fn.loc6_1.2 => constants.%Base.F.specific_fn.f8c // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @Base.F(constants.%T) { // CHECK:STDOUT: %T => constants.%T // CHECK:STDOUT: %Base => constants.%Base.d0c // CHECK:STDOUT: %pattern_type.loc5_23 => constants.%pattern_type.2f0 // CHECK:STDOUT: %pattern_type.loc5_42 => constants.%pattern_type.51d // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete.loc5_27 => constants.%require_complete.1e0 // CHECK:STDOUT: %require_complete.loc5_43 => constants.%require_complete.944 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @Derived(constants.%T) { // CHECK:STDOUT: %T.loc7_15.1 => constants.%T // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %ptr.loc8_22.2 => constants.%ptr.e8f // CHECK:STDOUT: %Base.loc8_23.2 => constants.%Base.f29 // CHECK:STDOUT: %require_complete => constants.%require_complete.b3c // CHECK:STDOUT: %Derived => constants.%Derived // CHECK:STDOUT: %Derived.elem => constants.%Derived.elem // CHECK:STDOUT: %Derived.F.type => constants.%Derived.F.type // CHECK:STDOUT: %Derived.F => constants.%Derived.F // CHECK:STDOUT: %Derived.F.specific_fn.loc10_1.2 => constants.%Derived.F.specific_fn // CHECK:STDOUT: %struct_type.base => constants.%struct_type.base.aa5 // CHECK:STDOUT: %complete_type.loc10_1.2 => constants.%complete_type.8c0 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @Base(constants.%ptr.e8f) { // CHECK:STDOUT: %T.loc4_21.1 => constants.%ptr.e8f // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %Base.F.type => constants.%Base.F.type.c53 // CHECK:STDOUT: %Base.F => constants.%Base.F.c0a // CHECK:STDOUT: %Base.F.specific_fn.loc6_1.2 => constants.%Base.F.specific_fn.35d // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @Base.F(constants.%ptr.e8f) { // CHECK:STDOUT: %T => constants.%ptr.e8f // CHECK:STDOUT: %Base => constants.%Base.f29 // CHECK:STDOUT: %pattern_type.loc5_23 => constants.%pattern_type.d96 // CHECK:STDOUT: %pattern_type.loc5_42 => constants.%pattern_type.4f4 // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete.loc5_27 => constants.%require_complete.b3c // CHECK:STDOUT: %require_complete.loc5_43 => constants.%require_complete.ef1 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @Derived.F(constants.%T) { // CHECK:STDOUT: %T => constants.%T // CHECK:STDOUT: %Derived => constants.%Derived // CHECK:STDOUT: %pattern_type.loc9_24 => constants.%pattern_type.b9d // CHECK:STDOUT: %ptr.loc9_46 => constants.%ptr.e8f // CHECK:STDOUT: %Base => constants.%Base.f29 // CHECK:STDOUT: %require_complete.loc9_46 => constants.%require_complete.b3c // CHECK:STDOUT: %pattern_type.loc9_43 => constants.%pattern_type.4f4 // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete.loc9_28 => constants.%require_complete.a43 // CHECK:STDOUT: %require_complete.loc9_44 => constants.%require_complete.ef1 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- abstract_generic_undefined.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %type: type = facet_type [concrete] // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self] // CHECK:STDOUT: %pattern_type.98f: type = pattern_type type [concrete] // CHECK:STDOUT: %T: type = symbolic_binding T, 0 [symbolic] // CHECK:STDOUT: %Base.type: type = generic_class_type @Base [concrete] // CHECK:STDOUT: %Base.generic: %Base.type = struct_value () [concrete] // CHECK:STDOUT: %Base.d0c: type = class_type @Base, @Base(%T) [symbolic] // CHECK:STDOUT: %pattern_type.2f0: type = pattern_type %Base.d0c [symbolic] // CHECK:STDOUT: %Base.F.type.0f1: type = fn_type @Base.F, @Base(%T) [symbolic] // CHECK:STDOUT: %Base.F.cd3: %Base.F.type.0f1 = struct_value () [symbolic] // CHECK:STDOUT: %ptr.454: type = ptr_type [concrete] // CHECK:STDOUT: %Base.F.specific_fn.f8c: = specific_function %Base.F.cd3, @Base.F(%T) [symbolic] // CHECK:STDOUT: %Base.vtable_decl: ref %ptr.454 = vtable_decl @Base.vtable [concrete] // CHECK:STDOUT: %struct_type.vptr: type = struct_type {.: %ptr.454} [concrete] // CHECK:STDOUT: %complete_type.513: = complete_type_witness %struct_type.vptr [concrete] // CHECK:STDOUT: %T1: type = class_type @T1 [concrete] // CHECK:STDOUT: %Derived: type = class_type @Derived [concrete] // CHECK:STDOUT: %Base.d5d: type = class_type @Base, @Base(%T1) [concrete] // CHECK:STDOUT: %Base.F.type.cb1: type = fn_type @Base.F, @Base(%T1) [concrete] // CHECK:STDOUT: %Base.F.eab: %Base.F.type.cb1 = struct_value () [concrete] // CHECK:STDOUT: %pattern_type.64f: type = pattern_type %Base.d5d [concrete] // CHECK:STDOUT: %Base.F.specific_fn.cd5: = specific_function %Base.F.eab, @Base.F(%T1) [concrete] // CHECK:STDOUT: %Derived.elem: type = unbound_element_type %Derived, %Base.d5d [concrete] // CHECK:STDOUT: %pattern_type.9f6: type = pattern_type %Derived [concrete] // CHECK:STDOUT: %Derived.F.type: type = fn_type @Derived.F [concrete] // CHECK:STDOUT: %Derived.F: %Derived.F.type = struct_value () [concrete] // CHECK:STDOUT: %Derived.vtable_decl: ref %ptr.454 = vtable_decl @Derived.vtable [concrete] // CHECK:STDOUT: %struct_type.base.cc2: type = struct_type {.base: %Base.d5d} [concrete] // CHECK:STDOUT: %complete_type.dcb: = complete_type_witness %struct_type.base.cc2 [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .Base = %Base.decl // CHECK:STDOUT: .T1 = %T1.decl // CHECK:STDOUT: .Derived = %Derived.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %Base.decl: %Base.type = class_decl @Base [concrete = constants.%Base.generic] { // CHECK:STDOUT: %T.patt: %pattern_type.98f = symbolic_binding_pattern T, 0 [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %.loc4_25.1: type = splice_block %.loc4_25.2 [concrete = type] { // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %.loc4_25.2: type = type_literal type [concrete = type] // CHECK:STDOUT: } // CHECK:STDOUT: %T.loc4_21.2: type = symbolic_binding T, 0 [symbolic = %T.loc4_21.1 (constants.%T)] // CHECK:STDOUT: } // CHECK:STDOUT: %T1.decl: type = class_decl @T1 [concrete = constants.%T1] {} {} // CHECK:STDOUT: %Derived.decl: type = class_decl @Derived [concrete = constants.%Derived] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic class @Base(%T.loc4_21.2: type) { // CHECK:STDOUT: %T.loc4_21.1: type = symbolic_binding T, 0 [symbolic = %T.loc4_21.1 (constants.%T)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %Base.F.type: type = fn_type @Base.F, @Base(%T.loc4_21.1) [symbolic = %Base.F.type (constants.%Base.F.type.0f1)] // CHECK:STDOUT: %Base.F: @Base.%Base.F.type (%Base.F.type.0f1) = struct_value () [symbolic = %Base.F (constants.%Base.F.cd3)] // CHECK:STDOUT: %Base.F.specific_fn.loc6_1.2: = specific_function %Base.F, @Base.F(%T.loc4_21.1) [symbolic = %Base.F.specific_fn.loc6_1.2 (constants.%Base.F.specific_fn.f8c)] // CHECK:STDOUT: // CHECK:STDOUT: class { // CHECK:STDOUT: %Base.F.decl: @Base.%Base.F.type (%Base.F.type.0f1) = fn_decl @Base.F [symbolic = @Base.%Base.F (constants.%Base.F.cd3)] { // CHECK:STDOUT: %self.patt: @Base.F.%pattern_type (%pattern_type.2f0) = value_binding_pattern self [concrete] // CHECK:STDOUT: %self.param_patt: @Base.F.%pattern_type (%pattern_type.2f0) = value_param_pattern %self.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %self.param: @Base.F.%Base (%Base.d0c) = value_param call_param0 // CHECK:STDOUT: %.loc5_23.1: type = splice_block %Self.ref [symbolic = %Base (constants.%Base.d0c)] { // CHECK:STDOUT: %.loc5_23.2: type = specific_constant constants.%Base.d0c, @Base(constants.%T) [symbolic = %Base (constants.%Base.d0c)] // CHECK:STDOUT: %Self.ref: type = name_ref Self, %.loc5_23.2 [symbolic = %Base (constants.%Base.d0c)] // CHECK:STDOUT: } // CHECK:STDOUT: %self: @Base.F.%Base (%Base.d0c) = value_binding self, %self.param // CHECK:STDOUT: } // CHECK:STDOUT: %Base.F.specific_fn.loc6_1.1: = specific_function %Base.F.decl, @Base.F(constants.%T) [symbolic = %Base.F.specific_fn.loc6_1.2 (constants.%Base.F.specific_fn.f8c)] // CHECK:STDOUT: %vtable_decl: ref %ptr.454 = vtable_decl @Base.vtable [concrete = constants.%Base.vtable_decl] // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%struct_type.vptr [concrete = constants.%complete_type.513] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: vtable_decl = %vtable_decl // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%Base.d0c // CHECK:STDOUT: .F = %Base.F.decl // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @T1; // CHECK:STDOUT: // CHECK:STDOUT: class @Derived { // CHECK:STDOUT: %Base.ref: %Base.type = name_ref Base, file.%Base.decl [concrete = constants.%Base.generic] // CHECK:STDOUT: %T1.ref: type = name_ref T1, file.%T1.decl [concrete = constants.%T1] // CHECK:STDOUT: %Base: type = class_type @Base, @Base(constants.%T1) [concrete = constants.%Base.d5d] // CHECK:STDOUT: %.loc11: %Derived.elem = base_decl %Base, element0 [concrete] // CHECK:STDOUT: %Derived.F.decl: %Derived.F.type = fn_decl @Derived.F [concrete = constants.%Derived.F] { // CHECK:STDOUT: %self.patt: %pattern_type.9f6 = value_binding_pattern self [concrete] // CHECK:STDOUT: %self.param_patt: %pattern_type.9f6 = value_param_pattern %self.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %self.param: %Derived = value_param call_param0 // CHECK:STDOUT: %Self.ref: type = name_ref Self, constants.%Derived [concrete = constants.%Derived] // CHECK:STDOUT: %self: %Derived = value_binding self, %self.param // CHECK:STDOUT: } // CHECK:STDOUT: %vtable_decl: ref %ptr.454 = vtable_decl @Derived.vtable [concrete = constants.%Derived.vtable_decl] // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%struct_type.base.cc2 [concrete = constants.%complete_type.dcb] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: vtable_decl = %vtable_decl // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%Derived // CHECK:STDOUT: .Base = // CHECK:STDOUT: .T1 = // CHECK:STDOUT: .base = %.loc11 // CHECK:STDOUT: .F = %Derived.F.decl // CHECK:STDOUT: extend %Base // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: vtable @Base.vtable { // CHECK:STDOUT: @Base.%Base.F.specific_fn.loc6_1.1 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: vtable @Derived.vtable { // CHECK:STDOUT: @Derived.%Derived.F.decl // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic abstract fn @Base.F(@Base.%T.loc4_21.2: type) { // CHECK:STDOUT: %T: type = symbolic_binding T, 0 [symbolic = %T (constants.%T)] // CHECK:STDOUT: %Base: type = class_type @Base, @Base(%T) [symbolic = %Base (constants.%Base.d0c)] // CHECK:STDOUT: %pattern_type: type = pattern_type %Base [symbolic = %pattern_type (constants.%pattern_type.2f0)] // CHECK:STDOUT: // CHECK:STDOUT: abstract fn(%self.param: @Base.F.%Base (%Base.d0c)); // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: override fn @Derived.F(%self.param: %Derived) { // CHECK:STDOUT: !entry: // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @Base(constants.%T) { // CHECK:STDOUT: %T.loc4_21.1 => constants.%T // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @Base.F(constants.%T) { // CHECK:STDOUT: %T => constants.%T // CHECK:STDOUT: %Base => constants.%Base.d0c // CHECK:STDOUT: %pattern_type => constants.%pattern_type.2f0 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @Base(constants.%T1) { // CHECK:STDOUT: %T.loc4_21.1 => constants.%T1 // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %Base.F.type => constants.%Base.F.type.cb1 // CHECK:STDOUT: %Base.F => constants.%Base.F.eab // CHECK:STDOUT: %Base.F.specific_fn.loc6_1.2 => constants.%Base.F.specific_fn.cd5 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @Base.F(constants.%T1) { // CHECK:STDOUT: %T => constants.%T1 // CHECK:STDOUT: %Base => constants.%Base.d5d // CHECK:STDOUT: %pattern_type => constants.%pattern_type.64f // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- generic_lib.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %type: type = facet_type [concrete] // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self] // CHECK:STDOUT: %pattern_type.98f: type = pattern_type type [concrete] // CHECK:STDOUT: %T: type = symbolic_binding T, 0 [symbolic] // CHECK:STDOUT: %Base.type: type = generic_class_type @Base [concrete] // CHECK:STDOUT: %Base.generic: %Base.type = struct_value () [concrete] // CHECK:STDOUT: %Base: type = class_type @Base, @Base(%T) [symbolic] // CHECK:STDOUT: %pattern_type.2f0: type = pattern_type %Base [symbolic] // CHECK:STDOUT: %Base.F.type: type = fn_type @Base.F, @Base(%T) [symbolic] // CHECK:STDOUT: %Base.F: %Base.F.type = struct_value () [symbolic] // CHECK:STDOUT: %ptr.454: type = ptr_type [concrete] // CHECK:STDOUT: %Base.F.specific_fn: = specific_function %Base.F, @Base.F(%T) [symbolic] // CHECK:STDOUT: %Base.vtable_decl: ref %ptr.454 = vtable_decl @Base.vtable [concrete] // CHECK:STDOUT: %struct_type.vptr: type = struct_type {.: %ptr.454} [concrete] // CHECK:STDOUT: %complete_type: = complete_type_witness %struct_type.vptr [concrete] // CHECK:STDOUT: %require_complete: = require_complete_type %Base [symbolic] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .Base = %Base.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %Base.decl: %Base.type = class_decl @Base [concrete = constants.%Base.generic] { // CHECK:STDOUT: %T.patt: %pattern_type.98f = symbolic_binding_pattern T, 0 [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %.loc4_21.1: type = splice_block %.loc4_21.2 [concrete = type] { // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %.loc4_21.2: type = type_literal type [concrete = type] // CHECK:STDOUT: } // CHECK:STDOUT: %T.loc4_17.2: type = symbolic_binding T, 0 [symbolic = %T.loc4_17.1 (constants.%T)] // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic class @Base(%T.loc4_17.2: type) { // CHECK:STDOUT: %T.loc4_17.1: type = symbolic_binding T, 0 [symbolic = %T.loc4_17.1 (constants.%T)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %Base.F.type: type = fn_type @Base.F, @Base(%T.loc4_17.1) [symbolic = %Base.F.type (constants.%Base.F.type)] // CHECK:STDOUT: %Base.F: @Base.%Base.F.type (%Base.F.type) = struct_value () [symbolic = %Base.F (constants.%Base.F)] // CHECK:STDOUT: %Base.F.specific_fn.loc6_1.2: = specific_function %Base.F, @Base.F(%T.loc4_17.1) [symbolic = %Base.F.specific_fn.loc6_1.2 (constants.%Base.F.specific_fn)] // CHECK:STDOUT: // CHECK:STDOUT: class { // CHECK:STDOUT: %Base.F.decl: @Base.%Base.F.type (%Base.F.type) = fn_decl @Base.F [symbolic = @Base.%Base.F (constants.%Base.F)] { // CHECK:STDOUT: %self.patt: @Base.F.%pattern_type (%pattern_type.2f0) = value_binding_pattern self [concrete] // CHECK:STDOUT: %self.param_patt: @Base.F.%pattern_type (%pattern_type.2f0) = value_param_pattern %self.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %self.param: @Base.F.%Base (%Base) = value_param call_param0 // CHECK:STDOUT: %.loc5_29.1: type = splice_block %Self.ref [symbolic = %Base (constants.%Base)] { // CHECK:STDOUT: %.loc5_29.2: type = specific_constant constants.%Base, @Base(constants.%T) [symbolic = %Base (constants.%Base)] // CHECK:STDOUT: %Self.ref: type = name_ref Self, %.loc5_29.2 [symbolic = %Base (constants.%Base)] // CHECK:STDOUT: } // CHECK:STDOUT: %self: @Base.F.%Base (%Base) = value_binding self, %self.param // CHECK:STDOUT: } // CHECK:STDOUT: %Base.F.specific_fn.loc6_1.1: = specific_function %Base.F.decl, @Base.F(constants.%T) [symbolic = %Base.F.specific_fn.loc6_1.2 (constants.%Base.F.specific_fn)] // CHECK:STDOUT: %vtable_decl: ref %ptr.454 = vtable_decl @Base.vtable [concrete = constants.%Base.vtable_decl] // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%struct_type.vptr [concrete = constants.%complete_type] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: vtable_decl = %vtable_decl // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%Base // CHECK:STDOUT: .F = %Base.F.decl // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: vtable @Base.vtable { // CHECK:STDOUT: @Base.%Base.F.specific_fn.loc6_1.1 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic virtual fn @Base.F(@Base.%T.loc4_17.2: type) { // CHECK:STDOUT: %T: type = symbolic_binding T, 0 [symbolic = %T (constants.%T)] // CHECK:STDOUT: %Base: type = class_type @Base, @Base(%T) [symbolic = %Base (constants.%Base)] // CHECK:STDOUT: %pattern_type: type = pattern_type %Base [symbolic = %pattern_type (constants.%pattern_type.2f0)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete: = require_complete_type %Base [symbolic = %require_complete (constants.%require_complete)] // CHECK:STDOUT: // CHECK:STDOUT: virtual fn(%self.param: @Base.F.%Base (%Base)) { // CHECK:STDOUT: !entry: // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @Base(constants.%T) { // CHECK:STDOUT: %T.loc4_17.1 => constants.%T // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %Base.F.type => constants.%Base.F.type // CHECK:STDOUT: %Base.F => constants.%Base.F // CHECK:STDOUT: %Base.F.specific_fn.loc6_1.2 => constants.%Base.F.specific_fn // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @Base.F(constants.%T) { // CHECK:STDOUT: %T => constants.%T // CHECK:STDOUT: %Base => constants.%Base // CHECK:STDOUT: %pattern_type => constants.%pattern_type.2f0 // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete => constants.%require_complete // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- generic_import.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %T1: type = class_type @T1 [concrete] // CHECK:STDOUT: %Base.type: type = generic_class_type @Base [concrete] // CHECK:STDOUT: %Base.generic: %Base.type = struct_value () [concrete] // CHECK:STDOUT: %ptr.454: type = ptr_type [concrete] // CHECK:STDOUT: %struct_type.vptr: type = struct_type {.: %ptr.454} [concrete] // CHECK:STDOUT: %complete_type: = complete_type_witness %struct_type.vptr [concrete] // CHECK:STDOUT: %T: type = symbolic_binding T, 0 [symbolic] // CHECK:STDOUT: %Base.d0c: type = class_type @Base, @Base(%T) [symbolic] // CHECK:STDOUT: %Base.F.type.0f1: type = fn_type @Base.F, @Base(%T) [symbolic] // CHECK:STDOUT: %Base.F.cd3: %Base.F.type.0f1 = struct_value () [symbolic] // CHECK:STDOUT: %Base.F.specific_fn.f8c: = specific_function %Base.F.cd3, @Base.F(%T) [symbolic] // CHECK:STDOUT: %require_complete: = require_complete_type %Base.d0c [symbolic] // CHECK:STDOUT: %pattern_type.2f0: type = pattern_type %Base.d0c [symbolic] // CHECK:STDOUT: %Base.d5d: type = class_type @Base, @Base(%T1) [concrete] // CHECK:STDOUT: %Base.F.type.cb1: type = fn_type @Base.F, @Base(%T1) [concrete] // CHECK:STDOUT: %Base.F.eab: %Base.F.type.cb1 = struct_value () [concrete] // CHECK:STDOUT: %pattern_type.64f: type = pattern_type %Base.d5d [concrete] // CHECK:STDOUT: %Base.F.specific_fn.cd5: = specific_function %Base.F.eab, @Base.F(%T1) [concrete] // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %empty_struct: %empty_struct_type = struct_value () [concrete] // CHECK:STDOUT: %.ae5: ref %ptr.454 = class_element_access file.%v.var, element0 [concrete] // CHECK:STDOUT: %Base.vtable_decl: ref %ptr.454 = vtable_decl @Base.vtable [concrete] // CHECK:STDOUT: %Base.vtable_ptr: ref %ptr.454 = vtable_ptr @Base.vtable, @Base(%T1) [concrete] // CHECK:STDOUT: %Base.val: %Base.d5d = struct_value (%Base.vtable_ptr) [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Main.Base: %Base.type = import_ref Main//generic_lib, Base, loaded [concrete = constants.%Base.generic] // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: %Main.import_ref.b3bc94.1: type = import_ref Main//generic_lib, loc4_17, loaded [symbolic = @Base.%T (constants.%T)] // CHECK:STDOUT: %Main.import_ref.462: ref %ptr.454 = import_ref Main//generic_lib, loc6_1, loaded [concrete = constants.%Base.vtable_decl] // CHECK:STDOUT: %Main.import_ref.05e: = import_ref Main//generic_lib, loc6_1, loaded [concrete = constants.%complete_type] // CHECK:STDOUT: %Main.import_ref.2af = import_ref Main//generic_lib, inst{{[0-9A-F]+}} [no loc], unloaded // CHECK:STDOUT: %Main.import_ref.1cf = import_ref Main//generic_lib, loc5_37, unloaded // CHECK:STDOUT: %Main.import_ref.b3bc94.2: type = import_ref Main//generic_lib, loc4_17, loaded [symbolic = @Base.%T (constants.%T)] // CHECK:STDOUT: %Main.import_ref.4f0: = import_ref Main//generic_lib, loc6_1, loaded [symbolic = constants.%Base.F.specific_fn.f8c] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Base = imports.%Main.Base // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .T1 = %T1.decl // CHECK:STDOUT: .v = %v // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %default.import = import // CHECK:STDOUT: %T1.decl: type = class_decl @T1 [concrete = constants.%T1] {} {} // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %v.patt: %pattern_type.64f = ref_binding_pattern v [concrete] // CHECK:STDOUT: %v.var_patt: %pattern_type.64f = var_pattern %v.patt [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %v.var: ref %Base.d5d = var %v.var_patt [concrete] // CHECK:STDOUT: %.loc7: type = splice_block %Base [concrete = constants.%Base.d5d] { // CHECK:STDOUT: %Base.ref: %Base.type = name_ref Base, imports.%Main.Base [concrete = constants.%Base.generic] // CHECK:STDOUT: %T1.ref: type = name_ref T1, %T1.decl [concrete = constants.%T1] // CHECK:STDOUT: %Base: type = class_type @Base, @Base(constants.%T1) [concrete = constants.%Base.d5d] // CHECK:STDOUT: } // CHECK:STDOUT: %v: ref %Base.d5d = ref_binding v, %v.var [concrete = %v.var] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @T1; // CHECK:STDOUT: // CHECK:STDOUT: generic class @Base(imports.%Main.import_ref.b3bc94.2: type) [from "generic_lib.carbon"] { // CHECK:STDOUT: %T: type = symbolic_binding T, 0 [symbolic = %T (constants.%T)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %Base.F.type: type = fn_type @Base.F, @Base(%T) [symbolic = %Base.F.type (constants.%Base.F.type.0f1)] // CHECK:STDOUT: %Base.F: @Base.%Base.F.type (%Base.F.type.0f1) = struct_value () [symbolic = %Base.F (constants.%Base.F.cd3)] // CHECK:STDOUT: %Base.F.specific_fn: = specific_function %Base.F, @Base.F(%T) [symbolic = %Base.F.specific_fn (constants.%Base.F.specific_fn.f8c)] // CHECK:STDOUT: // CHECK:STDOUT: class { // CHECK:STDOUT: complete_type_witness = imports.%Main.import_ref.05e // CHECK:STDOUT: vtable_decl = imports.%Main.import_ref.462 // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = imports.%Main.import_ref.2af // CHECK:STDOUT: .F = imports.%Main.import_ref.1cf // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: vtable @Base.vtable { // CHECK:STDOUT: imports.%Main.import_ref.4f0 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic virtual fn @Base.F(imports.%Main.import_ref.b3bc94.1: type) [from "generic_lib.carbon"] { // CHECK:STDOUT: %T: type = symbolic_binding T, 0 [symbolic = %T (constants.%T)] // CHECK:STDOUT: %Base: type = class_type @Base, @Base(%T) [symbolic = %Base (constants.%Base.d0c)] // CHECK:STDOUT: %pattern_type: type = pattern_type %Base [symbolic = %pattern_type (constants.%pattern_type.2f0)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete: = require_complete_type %Base [symbolic = %require_complete (constants.%require_complete)] // CHECK:STDOUT: // CHECK:STDOUT: virtual fn; // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @__global_init() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %.loc7_20.1: %empty_struct_type = struct_literal () [concrete = constants.%empty_struct] // CHECK:STDOUT: %.loc7_20.2: ref %ptr.454 = class_element_access file.%v.var, element0 [concrete = constants.%.ae5] // CHECK:STDOUT: %Base.vtable_ptr: ref %ptr.454 = vtable_ptr @Base.vtable, @Base(constants.%T1) [concrete = constants.%Base.vtable_ptr] // CHECK:STDOUT: %.loc7_20.3: init %ptr.454 to %.loc7_20.2 = in_place_init %Base.vtable_ptr [concrete = constants.%Base.vtable_ptr] // CHECK:STDOUT: %.loc7_20.4: init %Base.d5d to file.%v.var = class_init (%.loc7_20.3) [concrete = constants.%Base.val] // CHECK:STDOUT: %.loc7_1: init %Base.d5d = converted %.loc7_20.1, %.loc7_20.4 [concrete = constants.%Base.val] // CHECK:STDOUT: assign file.%v.var, %.loc7_1 // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @Base(constants.%T) { // CHECK:STDOUT: %T => constants.%T // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %Base.F.type => constants.%Base.F.type.0f1 // CHECK:STDOUT: %Base.F => constants.%Base.F.cd3 // CHECK:STDOUT: %Base.F.specific_fn => constants.%Base.F.specific_fn.f8c // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @Base.F(constants.%T) { // CHECK:STDOUT: %T => constants.%T // CHECK:STDOUT: %Base => constants.%Base.d0c // CHECK:STDOUT: %pattern_type => constants.%pattern_type.2f0 // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete => constants.%require_complete // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @Base(constants.%T1) { // CHECK:STDOUT: %T => constants.%T1 // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %Base.F.type => constants.%Base.F.type.cb1 // CHECK:STDOUT: %Base.F => constants.%Base.F.eab // CHECK:STDOUT: %Base.F.specific_fn => constants.%Base.F.specific_fn.cd5 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @Base.F(constants.%T1) { // CHECK:STDOUT: %T => constants.%T1 // CHECK:STDOUT: %Base => constants.%Base.d5d // CHECK:STDOUT: %pattern_type => constants.%pattern_type.64f // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete => constants.%complete_type // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- generic_derived_generic.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %type: type = facet_type [concrete] // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self] // CHECK:STDOUT: %pattern_type.98f: type = pattern_type type [concrete] // CHECK:STDOUT: %G1: type = symbolic_binding G1, 0 [symbolic] // CHECK:STDOUT: %T1.type: type = generic_class_type @T1 [concrete] // CHECK:STDOUT: %T1.generic: %T1.type = struct_value () [concrete] // CHECK:STDOUT: %T1.6d4458.1: type = class_type @T1, @T1(%G1) [symbolic] // CHECK:STDOUT: %pattern_type.253617.1: type = pattern_type %T1.6d4458.1 [symbolic] // CHECK:STDOUT: %T1.F.type.e39697.1: type = fn_type @T1.F, @T1(%G1) [symbolic] // CHECK:STDOUT: %T1.F.9b864d.1: %T1.F.type.e39697.1 = struct_value () [symbolic] // CHECK:STDOUT: %ptr.454: type = ptr_type [concrete] // CHECK:STDOUT: %T1.F.specific_fn.46dc79.1: = specific_function %T1.F.9b864d.1, @T1.F(%G1) [symbolic] // CHECK:STDOUT: %T1.vtable_decl: ref %ptr.454 = vtable_decl @T1.vtable [concrete] // CHECK:STDOUT: %struct_type.vptr: type = struct_type {.: %ptr.454} [concrete] // CHECK:STDOUT: %complete_type.513: = complete_type_witness %struct_type.vptr [concrete] // CHECK:STDOUT: %require_complete.6d26d2.1: = require_complete_type %T1.6d4458.1 [symbolic] // CHECK:STDOUT: %G2: type = symbolic_binding G2, 0 [symbolic] // CHECK:STDOUT: %T2.type: type = generic_class_type @T2 [concrete] // CHECK:STDOUT: %T2.generic: %T2.type = struct_value () [concrete] // CHECK:STDOUT: %T2: type = class_type @T2, @T2(%G2) [symbolic] // CHECK:STDOUT: %T1.6d4458.2: type = class_type @T1, @T1(%G2) [symbolic] // CHECK:STDOUT: %T1.F.type.e39697.2: type = fn_type @T1.F, @T1(%G2) [symbolic] // CHECK:STDOUT: %T1.F.9b864d.2: %T1.F.type.e39697.2 = struct_value () [symbolic] // CHECK:STDOUT: %pattern_type.253617.2: type = pattern_type %T1.6d4458.2 [symbolic] // CHECK:STDOUT: %T1.F.specific_fn.46dc79.2: = specific_function %T1.F.9b864d.2, @T1.F(%G2) [symbolic] // CHECK:STDOUT: %require_complete.6d26d2.2: = require_complete_type %T1.6d4458.2 [symbolic] // CHECK:STDOUT: %T2.elem: type = unbound_element_type %T2, %T1.6d4458.2 [symbolic] // CHECK:STDOUT: %T2.vtable_decl: ref %ptr.454 = vtable_decl @T2.vtable [concrete] // CHECK:STDOUT: %struct_type.base: type = struct_type {.base: %T1.6d4458.2} [symbolic] // CHECK:STDOUT: %complete_type.fc6: = complete_type_witness %struct_type.base [symbolic] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .T1 = %T1.decl // CHECK:STDOUT: .T2 = %T2.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %T1.decl: %T1.type = class_decl @T1 [concrete = constants.%T1.generic] { // CHECK:STDOUT: %G1.patt: %pattern_type.98f = symbolic_binding_pattern G1, 0 [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %.loc4_20.1: type = splice_block %.loc4_20.2 [concrete = type] { // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %.loc4_20.2: type = type_literal type [concrete = type] // CHECK:STDOUT: } // CHECK:STDOUT: %G1.loc4_15.2: type = symbolic_binding G1, 0 [symbolic = %G1.loc4_15.1 (constants.%G1)] // CHECK:STDOUT: } // CHECK:STDOUT: %T2.decl: %T2.type = class_decl @T2 [concrete = constants.%T2.generic] { // CHECK:STDOUT: %G2.patt: %pattern_type.98f = symbolic_binding_pattern G2, 0 [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %.loc8_15.1: type = splice_block %.loc8_15.2 [concrete = type] { // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %.loc8_15.2: type = type_literal type [concrete = type] // CHECK:STDOUT: } // CHECK:STDOUT: %G2.loc8_10.2: type = symbolic_binding G2, 0 [symbolic = %G2.loc8_10.1 (constants.%G2)] // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic class @T1(%G1.loc4_15.2: type) { // CHECK:STDOUT: %G1.loc4_15.1: type = symbolic_binding G1, 0 [symbolic = %G1.loc4_15.1 (constants.%G1)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %T1.F.type: type = fn_type @T1.F, @T1(%G1.loc4_15.1) [symbolic = %T1.F.type (constants.%T1.F.type.e39697.1)] // CHECK:STDOUT: %T1.F: @T1.%T1.F.type (%T1.F.type.e39697.1) = struct_value () [symbolic = %T1.F (constants.%T1.F.9b864d.1)] // CHECK:STDOUT: %T1.F.specific_fn.loc6_1.2: = specific_function %T1.F, @T1.F(%G1.loc4_15.1) [symbolic = %T1.F.specific_fn.loc6_1.2 (constants.%T1.F.specific_fn.46dc79.1)] // CHECK:STDOUT: // CHECK:STDOUT: class { // CHECK:STDOUT: %T1.F.decl: @T1.%T1.F.type (%T1.F.type.e39697.1) = fn_decl @T1.F [symbolic = @T1.%T1.F (constants.%T1.F.9b864d.1)] { // CHECK:STDOUT: %self.patt: @T1.F.%pattern_type (%pattern_type.253617.1) = value_binding_pattern self [concrete] // CHECK:STDOUT: %self.param_patt: @T1.F.%pattern_type (%pattern_type.253617.1) = value_param_pattern %self.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %self.param: @T1.F.%T1 (%T1.6d4458.1) = value_param call_param0 // CHECK:STDOUT: %.loc5_29.1: type = splice_block %Self.ref [symbolic = %T1 (constants.%T1.6d4458.1)] { // CHECK:STDOUT: %.loc5_29.2: type = specific_constant constants.%T1.6d4458.1, @T1(constants.%G1) [symbolic = %T1 (constants.%T1.6d4458.1)] // CHECK:STDOUT: %Self.ref: type = name_ref Self, %.loc5_29.2 [symbolic = %T1 (constants.%T1.6d4458.1)] // CHECK:STDOUT: } // CHECK:STDOUT: %self: @T1.F.%T1 (%T1.6d4458.1) = value_binding self, %self.param // CHECK:STDOUT: } // CHECK:STDOUT: %T1.F.specific_fn.loc6_1.1: = specific_function %T1.F.decl, @T1.F(constants.%G1) [symbolic = %T1.F.specific_fn.loc6_1.2 (constants.%T1.F.specific_fn.46dc79.1)] // CHECK:STDOUT: %vtable_decl: ref %ptr.454 = vtable_decl @T1.vtable [concrete = constants.%T1.vtable_decl] // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%struct_type.vptr [concrete = constants.%complete_type.513] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: vtable_decl = %vtable_decl // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%T1.6d4458.1 // CHECK:STDOUT: .F = %T1.F.decl // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic class @T2(%G2.loc8_10.2: type) { // CHECK:STDOUT: %G2.loc8_10.1: type = symbolic_binding G2, 0 [symbolic = %G2.loc8_10.1 (constants.%G2)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %T1.loc9_21.2: type = class_type @T1, @T1(%G2.loc8_10.1) [symbolic = %T1.loc9_21.2 (constants.%T1.6d4458.2)] // CHECK:STDOUT: %require_complete: = require_complete_type %T1.loc9_21.2 [symbolic = %require_complete (constants.%require_complete.6d26d2.2)] // CHECK:STDOUT: %T2: type = class_type @T2, @T2(%G2.loc8_10.1) [symbolic = %T2 (constants.%T2)] // CHECK:STDOUT: %T2.elem: type = unbound_element_type %T2, %T1.loc9_21.2 [symbolic = %T2.elem (constants.%T2.elem)] // CHECK:STDOUT: %T1.F.type: type = fn_type @T1.F, @T1(%G2.loc8_10.1) [symbolic = %T1.F.type (constants.%T1.F.type.e39697.2)] // CHECK:STDOUT: %T1.F: @T2.%T1.F.type (%T1.F.type.e39697.2) = struct_value () [symbolic = %T1.F (constants.%T1.F.9b864d.2)] // CHECK:STDOUT: %T1.F.specific_fn.loc10_1.2: = specific_function %T1.F, @T1.F(%G2.loc8_10.1) [symbolic = %T1.F.specific_fn.loc10_1.2 (constants.%T1.F.specific_fn.46dc79.2)] // CHECK:STDOUT: %struct_type.base: type = struct_type {.base: @T2.%T1.loc9_21.2 (%T1.6d4458.2)} [symbolic = %struct_type.base (constants.%struct_type.base)] // CHECK:STDOUT: %complete_type.loc10_1.2: = complete_type_witness %struct_type.base [symbolic = %complete_type.loc10_1.2 (constants.%complete_type.fc6)] // CHECK:STDOUT: // CHECK:STDOUT: class { // CHECK:STDOUT: %T1.ref: %T1.type = name_ref T1, file.%T1.decl [concrete = constants.%T1.generic] // CHECK:STDOUT: %G2.ref: type = name_ref G2, %G2.loc8_10.2 [symbolic = %G2.loc8_10.1 (constants.%G2)] // CHECK:STDOUT: %T1.loc9_21.1: type = class_type @T1, @T1(constants.%G2) [symbolic = %T1.loc9_21.2 (constants.%T1.6d4458.2)] // CHECK:STDOUT: %.loc9: @T2.%T2.elem (%T2.elem) = base_decl %T1.loc9_21.1, element0 [concrete] // CHECK:STDOUT: %T1.F.specific_fn.loc10_1.1: = specific_function constants.%T1.F.9b864d.2, @T1.F(constants.%G2) [symbolic = %T1.F.specific_fn.loc10_1.2 (constants.%T1.F.specific_fn.46dc79.2)] // CHECK:STDOUT: %vtable_decl: ref %ptr.454 = vtable_decl @T2.vtable [concrete = constants.%T2.vtable_decl] // CHECK:STDOUT: %complete_type.loc10_1.1: = complete_type_witness constants.%struct_type.base [symbolic = %complete_type.loc10_1.2 (constants.%complete_type.fc6)] // CHECK:STDOUT: complete_type_witness = %complete_type.loc10_1.1 // CHECK:STDOUT: vtable_decl = %vtable_decl // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%T2 // CHECK:STDOUT: .T1 = // CHECK:STDOUT: .G2 = // CHECK:STDOUT: .base = %.loc9 // CHECK:STDOUT: extend %T1.loc9_21.1 // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: vtable @T1.vtable { // CHECK:STDOUT: @T1.%T1.F.specific_fn.loc6_1.1 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: vtable @T2.vtable { // CHECK:STDOUT: @T2.%T1.F.specific_fn.loc10_1.1 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic virtual fn @T1.F(@T1.%G1.loc4_15.2: type) { // CHECK:STDOUT: %G1: type = symbolic_binding G1, 0 [symbolic = %G1 (constants.%G1)] // CHECK:STDOUT: %T1: type = class_type @T1, @T1(%G1) [symbolic = %T1 (constants.%T1.6d4458.1)] // CHECK:STDOUT: %pattern_type: type = pattern_type %T1 [symbolic = %pattern_type (constants.%pattern_type.253617.1)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete: = require_complete_type %T1 [symbolic = %require_complete (constants.%require_complete.6d26d2.1)] // CHECK:STDOUT: // CHECK:STDOUT: virtual fn(%self.param: @T1.F.%T1 (%T1.6d4458.1)) { // CHECK:STDOUT: !entry: // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @T1(constants.%G1) { // CHECK:STDOUT: %G1.loc4_15.1 => constants.%G1 // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %T1.F.type => constants.%T1.F.type.e39697.1 // CHECK:STDOUT: %T1.F => constants.%T1.F.9b864d.1 // CHECK:STDOUT: %T1.F.specific_fn.loc6_1.2 => constants.%T1.F.specific_fn.46dc79.1 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @T1.F(constants.%G1) { // CHECK:STDOUT: %G1 => constants.%G1 // CHECK:STDOUT: %T1 => constants.%T1.6d4458.1 // CHECK:STDOUT: %pattern_type => constants.%pattern_type.253617.1 // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete => constants.%require_complete.6d26d2.1 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @T2(constants.%G2) { // CHECK:STDOUT: %G2.loc8_10.1 => constants.%G2 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @T1(constants.%G2) { // CHECK:STDOUT: %G1.loc4_15.1 => constants.%G2 // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %T1.F.type => constants.%T1.F.type.e39697.2 // CHECK:STDOUT: %T1.F => constants.%T1.F.9b864d.2 // CHECK:STDOUT: %T1.F.specific_fn.loc6_1.2 => constants.%T1.F.specific_fn.46dc79.2 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @T1.F(constants.%G2) { // CHECK:STDOUT: %G1 => constants.%G2 // CHECK:STDOUT: %T1 => constants.%T1.6d4458.2 // CHECK:STDOUT: %pattern_type => constants.%pattern_type.253617.2 // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete => constants.%require_complete.6d26d2.2 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- generic_derived_generic_context.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %type: type = facet_type [concrete] // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self] // CHECK:STDOUT: %pattern_type.98f: type = pattern_type type [concrete] // CHECK:STDOUT: %G1: type = symbolic_binding G1, 0 [symbolic] // CHECK:STDOUT: %T1.type: type = generic_class_type @T1 [concrete] // CHECK:STDOUT: %T1.generic: %T1.type = struct_value () [concrete] // CHECK:STDOUT: %T1.6d4458.1: type = class_type @T1, @T1(%G1) [symbolic] // CHECK:STDOUT: %pattern_type.253617.1: type = pattern_type %T1.6d4458.1 [symbolic] // CHECK:STDOUT: %T1.F.type.e39697.1: type = fn_type @T1.F, @T1(%G1) [symbolic] // CHECK:STDOUT: %T1.F.9b864d.1: %T1.F.type.e39697.1 = struct_value () [symbolic] // CHECK:STDOUT: %ptr.454: type = ptr_type [concrete] // CHECK:STDOUT: %T1.F.specific_fn.46dc79.1: = specific_function %T1.F.9b864d.1, @T1.F(%G1) [symbolic] // CHECK:STDOUT: %T1.vtable_decl: ref %ptr.454 = vtable_decl @T1.vtable [concrete] // CHECK:STDOUT: %struct_type.vptr: type = struct_type {.: %ptr.454} [concrete] // CHECK:STDOUT: %complete_type.513: = complete_type_witness %struct_type.vptr [concrete] // CHECK:STDOUT: %require_complete.6d26d2.1: = require_complete_type %T1.6d4458.1 [symbolic] // CHECK:STDOUT: %G2: type = symbolic_binding G2, 0 [symbolic] // CHECK:STDOUT: %T2.type: type = generic_class_type @T2 [concrete] // CHECK:STDOUT: %T2.generic: %T2.type = struct_value () [concrete] // CHECK:STDOUT: %T2: type = class_type @T2, @T2(%G2) [symbolic] // CHECK:STDOUT: %T3: type = class_type @T3, @T3(%G2) [symbolic] // CHECK:STDOUT: %T1.6d4458.2: type = class_type @T1, @T1(%G2) [symbolic] // CHECK:STDOUT: %T1.F.type.e39697.2: type = fn_type @T1.F, @T1(%G2) [symbolic] // CHECK:STDOUT: %T1.F.9b864d.2: %T1.F.type.e39697.2 = struct_value () [symbolic] // CHECK:STDOUT: %pattern_type.253617.2: type = pattern_type %T1.6d4458.2 [symbolic] // CHECK:STDOUT: %T1.F.specific_fn.46dc79.2: = specific_function %T1.F.9b864d.2, @T1.F(%G2) [symbolic] // CHECK:STDOUT: %require_complete.6d26d2.2: = require_complete_type %T1.6d4458.2 [symbolic] // CHECK:STDOUT: %T3.elem: type = unbound_element_type %T3, %T1.6d4458.2 [symbolic] // CHECK:STDOUT: %T3.vtable_decl: ref %ptr.454 = vtable_decl @T3.vtable [concrete] // CHECK:STDOUT: %struct_type.base: type = struct_type {.base: %T1.6d4458.2} [symbolic] // CHECK:STDOUT: %complete_type.fc6: = complete_type_witness %struct_type.base [symbolic] // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %complete_type.357: = complete_type_witness %empty_struct_type [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .T1 = %T1.decl // CHECK:STDOUT: .T2 = %T2.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %T1.decl: %T1.type = class_decl @T1 [concrete = constants.%T1.generic] { // CHECK:STDOUT: %G1.patt: %pattern_type.98f = symbolic_binding_pattern G1, 0 [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %.loc4_20.1: type = splice_block %.loc4_20.2 [concrete = type] { // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %.loc4_20.2: type = type_literal type [concrete = type] // CHECK:STDOUT: } // CHECK:STDOUT: %G1.loc4_15.2: type = symbolic_binding G1, 0 [symbolic = %G1.loc4_15.1 (constants.%G1)] // CHECK:STDOUT: } // CHECK:STDOUT: %T2.decl: %T2.type = class_decl @T2 [concrete = constants.%T2.generic] { // CHECK:STDOUT: %G2.patt: %pattern_type.98f = symbolic_binding_pattern G2, 0 [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %.loc8_15.1: type = splice_block %.loc8_15.2 [concrete = type] { // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %.loc8_15.2: type = type_literal type [concrete = type] // CHECK:STDOUT: } // CHECK:STDOUT: %G2.loc8_10.2: type = symbolic_binding G2, 0 [symbolic = %G2.loc8_10.1 (constants.%G2)] // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic class @T1(%G1.loc4_15.2: type) { // CHECK:STDOUT: %G1.loc4_15.1: type = symbolic_binding G1, 0 [symbolic = %G1.loc4_15.1 (constants.%G1)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %T1.F.type: type = fn_type @T1.F, @T1(%G1.loc4_15.1) [symbolic = %T1.F.type (constants.%T1.F.type.e39697.1)] // CHECK:STDOUT: %T1.F: @T1.%T1.F.type (%T1.F.type.e39697.1) = struct_value () [symbolic = %T1.F (constants.%T1.F.9b864d.1)] // CHECK:STDOUT: %T1.F.specific_fn.loc6_1.2: = specific_function %T1.F, @T1.F(%G1.loc4_15.1) [symbolic = %T1.F.specific_fn.loc6_1.2 (constants.%T1.F.specific_fn.46dc79.1)] // CHECK:STDOUT: // CHECK:STDOUT: class { // CHECK:STDOUT: %T1.F.decl: @T1.%T1.F.type (%T1.F.type.e39697.1) = fn_decl @T1.F [symbolic = @T1.%T1.F (constants.%T1.F.9b864d.1)] { // CHECK:STDOUT: %self.patt: @T1.F.%pattern_type (%pattern_type.253617.1) = value_binding_pattern self [concrete] // CHECK:STDOUT: %self.param_patt: @T1.F.%pattern_type (%pattern_type.253617.1) = value_param_pattern %self.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %self.param: @T1.F.%T1 (%T1.6d4458.1) = value_param call_param0 // CHECK:STDOUT: %.loc5_29.1: type = splice_block %Self.ref [symbolic = %T1 (constants.%T1.6d4458.1)] { // CHECK:STDOUT: %.loc5_29.2: type = specific_constant constants.%T1.6d4458.1, @T1(constants.%G1) [symbolic = %T1 (constants.%T1.6d4458.1)] // CHECK:STDOUT: %Self.ref: type = name_ref Self, %.loc5_29.2 [symbolic = %T1 (constants.%T1.6d4458.1)] // CHECK:STDOUT: } // CHECK:STDOUT: %self: @T1.F.%T1 (%T1.6d4458.1) = value_binding self, %self.param // CHECK:STDOUT: } // CHECK:STDOUT: %T1.F.specific_fn.loc6_1.1: = specific_function %T1.F.decl, @T1.F(constants.%G1) [symbolic = %T1.F.specific_fn.loc6_1.2 (constants.%T1.F.specific_fn.46dc79.1)] // CHECK:STDOUT: %vtable_decl: ref %ptr.454 = vtable_decl @T1.vtable [concrete = constants.%T1.vtable_decl] // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%struct_type.vptr [concrete = constants.%complete_type.513] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: vtable_decl = %vtable_decl // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%T1.6d4458.1 // CHECK:STDOUT: .F = %T1.F.decl // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic class @T2(%G2.loc8_10.2: type) { // CHECK:STDOUT: %G2.loc8_10.1: type = symbolic_binding G2, 0 [symbolic = %G2.loc8_10.1 (constants.%G2)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %T3: type = class_type @T3, @T3(%G2.loc8_10.1) [symbolic = %T3 (constants.%T3)] // CHECK:STDOUT: // CHECK:STDOUT: class { // CHECK:STDOUT: %T3.decl: type = class_decl @T3 [symbolic = @T2.%T3 (constants.%T3)] {} {} // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type.357] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%T2 // CHECK:STDOUT: .T3 = %T3.decl // CHECK:STDOUT: .T1 = // CHECK:STDOUT: .G2 = // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic class @T3(@T2.%G2.loc8_10.2: type) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: %G2: type = symbolic_binding G2, 0 [symbolic = %G2 (constants.%G2)] // CHECK:STDOUT: %T1.loc10_24.2: type = class_type @T1, @T1(%G2) [symbolic = %T1.loc10_24.2 (constants.%T1.6d4458.2)] // CHECK:STDOUT: %require_complete: = require_complete_type %T1.loc10_24.2 [symbolic = %require_complete (constants.%require_complete.6d26d2.2)] // CHECK:STDOUT: %T3: type = class_type @T3, @T3(%G2) [symbolic = %T3 (constants.%T3)] // CHECK:STDOUT: %T3.elem: type = unbound_element_type %T3, %T1.loc10_24.2 [symbolic = %T3.elem (constants.%T3.elem)] // CHECK:STDOUT: %T1.F.type: type = fn_type @T1.F, @T1(%G2) [symbolic = %T1.F.type (constants.%T1.F.type.e39697.2)] // CHECK:STDOUT: %T1.F: @T3.%T1.F.type (%T1.F.type.e39697.2) = struct_value () [symbolic = %T1.F (constants.%T1.F.9b864d.2)] // CHECK:STDOUT: %T1.F.specific_fn.loc11_3.2: = specific_function %T1.F, @T1.F(%G2) [symbolic = %T1.F.specific_fn.loc11_3.2 (constants.%T1.F.specific_fn.46dc79.2)] // CHECK:STDOUT: %struct_type.base: type = struct_type {.base: @T3.%T1.loc10_24.2 (%T1.6d4458.2)} [symbolic = %struct_type.base (constants.%struct_type.base)] // CHECK:STDOUT: %complete_type.loc11_3.2: = complete_type_witness %struct_type.base [symbolic = %complete_type.loc11_3.2 (constants.%complete_type.fc6)] // CHECK:STDOUT: // CHECK:STDOUT: class { // CHECK:STDOUT: %T1.ref: %T1.type = name_ref T1, file.%T1.decl [concrete = constants.%T1.generic] // CHECK:STDOUT: %G2.ref: type = name_ref G2, @T2.%G2.loc8_10.2 [symbolic = %G2 (constants.%G2)] // CHECK:STDOUT: %T1.loc10_24.1: type = class_type @T1, @T1(constants.%G2) [symbolic = %T1.loc10_24.2 (constants.%T1.6d4458.2)] // CHECK:STDOUT: %.loc10: @T3.%T3.elem (%T3.elem) = base_decl %T1.loc10_24.1, element0 [concrete] // CHECK:STDOUT: %T1.F.specific_fn.loc11_3.1: = specific_function constants.%T1.F.9b864d.2, @T1.F(constants.%G2) [symbolic = %T1.F.specific_fn.loc11_3.2 (constants.%T1.F.specific_fn.46dc79.2)] // CHECK:STDOUT: %vtable_decl: ref %ptr.454 = vtable_decl @T3.vtable [concrete = constants.%T3.vtable_decl] // CHECK:STDOUT: %complete_type.loc11_3.1: = complete_type_witness constants.%struct_type.base [symbolic = %complete_type.loc11_3.2 (constants.%complete_type.fc6)] // CHECK:STDOUT: complete_type_witness = %complete_type.loc11_3.1 // CHECK:STDOUT: vtable_decl = %vtable_decl // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%T3 // CHECK:STDOUT: .T1 = // CHECK:STDOUT: .G2 = // CHECK:STDOUT: .base = %.loc10 // CHECK:STDOUT: extend %T1.loc10_24.1 // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: vtable @T1.vtable { // CHECK:STDOUT: @T1.%T1.F.specific_fn.loc6_1.1 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: vtable @T3.vtable { // CHECK:STDOUT: @T3.%T1.F.specific_fn.loc11_3.1 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic virtual fn @T1.F(@T1.%G1.loc4_15.2: type) { // CHECK:STDOUT: %G1: type = symbolic_binding G1, 0 [symbolic = %G1 (constants.%G1)] // CHECK:STDOUT: %T1: type = class_type @T1, @T1(%G1) [symbolic = %T1 (constants.%T1.6d4458.1)] // CHECK:STDOUT: %pattern_type: type = pattern_type %T1 [symbolic = %pattern_type (constants.%pattern_type.253617.1)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete: = require_complete_type %T1 [symbolic = %require_complete (constants.%require_complete.6d26d2.1)] // CHECK:STDOUT: // CHECK:STDOUT: virtual fn(%self.param: @T1.F.%T1 (%T1.6d4458.1)) { // CHECK:STDOUT: !entry: // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @T1(constants.%G1) { // CHECK:STDOUT: %G1.loc4_15.1 => constants.%G1 // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %T1.F.type => constants.%T1.F.type.e39697.1 // CHECK:STDOUT: %T1.F => constants.%T1.F.9b864d.1 // CHECK:STDOUT: %T1.F.specific_fn.loc6_1.2 => constants.%T1.F.specific_fn.46dc79.1 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @T1.F(constants.%G1) { // CHECK:STDOUT: %G1 => constants.%G1 // CHECK:STDOUT: %T1 => constants.%T1.6d4458.1 // CHECK:STDOUT: %pattern_type => constants.%pattern_type.253617.1 // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete => constants.%require_complete.6d26d2.1 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @T2(constants.%G2) { // CHECK:STDOUT: %G2.loc8_10.1 => constants.%G2 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @T3(constants.%G2) {} // CHECK:STDOUT: // CHECK:STDOUT: specific @T1(constants.%G2) { // CHECK:STDOUT: %G1.loc4_15.1 => constants.%G2 // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %T1.F.type => constants.%T1.F.type.e39697.2 // CHECK:STDOUT: %T1.F => constants.%T1.F.9b864d.2 // CHECK:STDOUT: %T1.F.specific_fn.loc6_1.2 => constants.%T1.F.specific_fn.46dc79.2 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @T1.F(constants.%G2) { // CHECK:STDOUT: %G1 => constants.%G2 // CHECK:STDOUT: %T1 => constants.%T1.6d4458.2 // CHECK:STDOUT: %pattern_type => constants.%pattern_type.253617.2 // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete => constants.%require_complete.6d26d2.2 // CHECK:STDOUT: } // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/const/basics.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/convert.carbon // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/const/basics.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/const/basics.carbon // --- basic.carbon library "[[@TEST_NAME]]"; class C; //@dump-sem-ir-begin fn A(p: const C**) -> const C** { return p; } fn B(p: const (C*)) -> const (C*) { return p; } //@dump-sem-ir-end // --- collapse.carbon library "[[@TEST_NAME]]"; class C; // OK, `const (const C)` is the same type as `const C`. //@dump-sem-ir-begin // CHECK:STDERR: collapse.carbon:[[@LINE+4]]:23: warning: `const` applied repeatedly to the same type has no additional effect [RepeatedConst] // CHECK:STDERR: fn F(p: const C**) -> const (const C)** { // CHECK:STDERR: ^~~~~~~~~~~~~~~ // CHECK:STDERR: fn F(p: const C**) -> const (const C)** { return p; } //@dump-sem-ir-end // --- fail_collapse_in_error.carbon library "[[@TEST_NAME]]"; class C; // CHECK:STDERR: fail_collapse_in_error.carbon:[[@LINE+4]]:9: warning: `const` applied repeatedly to the same type has no additional effect [RepeatedConst] // CHECK:STDERR: fn G(p: const (const C)**) -> C** { // CHECK:STDERR: ^~~~~~~~~~~~~~~ // CHECK:STDERR: fn G(p: const (const C)**) -> C** { // CHECK:STDERR: fail_collapse_in_error.carbon:[[@LINE+7]]:3: error: cannot implicitly convert expression of type `const C**` to `C**` [ConversionFailure] // CHECK:STDERR: return p; // CHECK:STDERR: ^~~~~~~~~ // CHECK:STDERR: fail_collapse_in_error.carbon:[[@LINE+4]]:3: note: type `const C**` does not implement interface `Core.ImplicitAs(C**)` [MissingImplInMemberAccessInContext] // CHECK:STDERR: return p; // CHECK:STDERR: ^~~~~~~~~ // CHECK:STDERR: return p; } // --- add_or_remove_while_forming_value.carbon library "[[@TEST_NAME]]"; class X {} fn TakeValue(unused x: X) {} fn TakeConstValue(unused x: const X) {} fn PassConstValueToValue(a: const X) { TakeValue(a); } fn PassValueToConstValue(a: X) { TakeConstValue(a); } fn PassConstReferenceToValue(p: const X*) { TakeValue(*p); } fn PassReferenceToConstValue(p: X*) { TakeConstValue(*p); } // --- add_or_remove_while_initializing.carbon library "[[@TEST_NAME]]"; class X {} var init_non_const_from_non_const: X = {} as X; var init_non_const_from_const: X = ({} as X) as const X; var init_const_from_non_const: const X = {} as X; var init_const_from_const: const X = ({} as X) as const X; // --- add_while_forming_reference.carbon library "[[@TEST_NAME]]"; class X { fn TakeConstSelf[ref self: const Self](); } fn PassReferenceToConstReference(p: X*) { p->TakeConstSelf(); } // --- fail_remove_while_forming_reference.carbon library "[[@TEST_NAME]]"; class X { fn TakeSelf[ref self: Self](); } fn PassConstReferenceToReference(p: const X*) { // CHECK:STDERR: fail_remove_while_forming_reference.carbon:[[@LINE+10]]:3: error: cannot implicitly convert expression of type `const X` to `X` [ConversionFailure] // CHECK:STDERR: p->(X.TakeSelf)(); // CHECK:STDERR: ^~~~~~~~~~~~~~~ // CHECK:STDERR: fail_remove_while_forming_reference.carbon:[[@LINE+7]]:3: note: type `const X` does not implement interface `Core.ImplicitAs(X)` [MissingImplInMemberAccessInContext] // CHECK:STDERR: p->(X.TakeSelf)(); // CHECK:STDERR: ^~~~~~~~~~~~~~~ // CHECK:STDERR: fail_remove_while_forming_reference.carbon:[[@LINE-10]]:15: note: initializing function parameter [InCallToFunctionParam] // CHECK:STDERR: fn TakeSelf[ref self: Self](); // CHECK:STDERR: ^~~~~~~~~~~~~~ // CHECK:STDERR: p->(X.TakeSelf)(); } // CHECK:STDOUT: --- basic.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %C: type = class_type @C [concrete] // CHECK:STDOUT: %const.0e5: type = const_type %C [concrete] // CHECK:STDOUT: %ptr.c45: type = ptr_type %const.0e5 [concrete] // CHECK:STDOUT: %ptr.728: type = ptr_type %ptr.c45 [concrete] // CHECK:STDOUT: %pattern_type.559: type = pattern_type %ptr.728 [concrete] // CHECK:STDOUT: %.674: Core.Form = init_form %ptr.728 [concrete] // CHECK:STDOUT: %A.type: type = fn_type @A [concrete] // CHECK:STDOUT: %A: %A.type = struct_value () [concrete] // CHECK:STDOUT: %Copy.type: type = facet_type <@Copy> [concrete] // CHECK:STDOUT: %T.035: %Copy.type = symbolic_binding T, 0 [symbolic] // CHECK:STDOUT: %const.as.Copy.impl.Op.type.5eb: type = fn_type @const.as.Copy.impl.Op, @const.as.Copy.impl(%T.035) [symbolic] // CHECK:STDOUT: %const.as.Copy.impl.Op.cc6: %const.as.Copy.impl.Op.type.5eb = struct_value () [symbolic] // CHECK:STDOUT: %T.67d: type = symbolic_binding T, 0 [symbolic] // CHECK:STDOUT: %ptr.as.Copy.impl.Op.type.2d4: type = fn_type @ptr.as.Copy.impl.Op, @ptr.as.Copy.impl(%T.67d) [symbolic] // CHECK:STDOUT: %ptr.as.Copy.impl.Op.74e: %ptr.as.Copy.impl.Op.type.2d4 = struct_value () [symbolic] // CHECK:STDOUT: %Copy.impl_witness.795: = impl_witness imports.%Copy.impl_witness_table.c3a, @ptr.as.Copy.impl(%ptr.c45) [concrete] // CHECK:STDOUT: %ptr.as.Copy.impl.Op.type.f8c: type = fn_type @ptr.as.Copy.impl.Op, @ptr.as.Copy.impl(%ptr.c45) [concrete] // CHECK:STDOUT: %ptr.as.Copy.impl.Op.f64: %ptr.as.Copy.impl.Op.type.f8c = struct_value () [concrete] // CHECK:STDOUT: %Copy.facet.d1d: %Copy.type = facet_value %ptr.728, (%Copy.impl_witness.795) [concrete] // CHECK:STDOUT: %Copy.WithSelf.Op.type.b3a: type = fn_type @Copy.WithSelf.Op, @Copy.WithSelf(%Copy.facet.d1d) [concrete] // CHECK:STDOUT: %.53f: type = fn_type_with_self_type %Copy.WithSelf.Op.type.b3a, %Copy.facet.d1d [concrete] // CHECK:STDOUT: %ptr.as.Copy.impl.Op.specific_fn.6a7: = specific_function %ptr.as.Copy.impl.Op.f64, @ptr.as.Copy.impl.Op(%ptr.c45) [concrete] // CHECK:STDOUT: %ptr.31e: type = ptr_type %C [concrete] // CHECK:STDOUT: %const.8ce: type = const_type %ptr.31e [concrete] // CHECK:STDOUT: %pattern_type.665: type = pattern_type %const.8ce [concrete] // CHECK:STDOUT: %.d7d: Core.Form = init_form %const.8ce [concrete] // CHECK:STDOUT: %B.type: type = fn_type @B [concrete] // CHECK:STDOUT: %B: %B.type = struct_value () [concrete] // CHECK:STDOUT: %Copy.impl_witness.2c7: = impl_witness imports.%Copy.impl_witness_table.c3a, @ptr.as.Copy.impl(%C) [concrete] // CHECK:STDOUT: %Copy.facet.a7f: %Copy.type = facet_value %ptr.31e, (%Copy.impl_witness.2c7) [concrete] // CHECK:STDOUT: %Copy.impl_witness.e93: = impl_witness imports.%Copy.impl_witness_table.a26, @const.as.Copy.impl(%Copy.facet.a7f) [concrete] // CHECK:STDOUT: %const.as.Copy.impl.Op.type.368: type = fn_type @const.as.Copy.impl.Op, @const.as.Copy.impl(%Copy.facet.a7f) [concrete] // CHECK:STDOUT: %const.as.Copy.impl.Op.c19: %const.as.Copy.impl.Op.type.368 = struct_value () [concrete] // CHECK:STDOUT: %Copy.facet.5cc: %Copy.type = facet_value %const.8ce, (%Copy.impl_witness.e93) [concrete] // CHECK:STDOUT: %Copy.WithSelf.Op.type.b95: type = fn_type @Copy.WithSelf.Op, @Copy.WithSelf(%Copy.facet.5cc) [concrete] // CHECK:STDOUT: %.21b: type = fn_type_with_self_type %Copy.WithSelf.Op.type.b95, %Copy.facet.5cc [concrete] // CHECK:STDOUT: %const.as.Copy.impl.Op.specific_fn: = specific_function %const.as.Copy.impl.Op.c19, @const.as.Copy.impl.Op(%Copy.facet.a7f) [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core.import_ref.ae5: @const.as.Copy.impl.%const.as.Copy.impl.Op.type (%const.as.Copy.impl.Op.type.5eb) = import_ref Core//prelude/parts/copy, loc{{\d+_\d+}}, loaded [symbolic = @const.as.Copy.impl.%const.as.Copy.impl.Op (constants.%const.as.Copy.impl.Op.cc6)] // CHECK:STDOUT: %Copy.impl_witness_table.a26 = impl_witness_table (%Core.import_ref.ae5), @const.as.Copy.impl [concrete] // CHECK:STDOUT: %Core.import_ref.203: @ptr.as.Copy.impl.%ptr.as.Copy.impl.Op.type (%ptr.as.Copy.impl.Op.type.2d4) = import_ref Core//prelude/parts/copy, loc{{\d+_\d+}}, loaded [symbolic = @ptr.as.Copy.impl.%ptr.as.Copy.impl.Op (constants.%ptr.as.Copy.impl.Op.74e)] // CHECK:STDOUT: %Copy.impl_witness_table.c3a = impl_witness_table (%Core.import_ref.203), @ptr.as.Copy.impl [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: %A.decl: %A.type = fn_decl @A [concrete = constants.%A] { // CHECK:STDOUT: %p.patt: %pattern_type.559 = value_binding_pattern p [concrete] // CHECK:STDOUT: %p.param_patt: %pattern_type.559 = value_param_pattern %p.patt [concrete] // CHECK:STDOUT: %return.patt: %pattern_type.559 = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: %pattern_type.559 = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %C.ref.loc6_29: type = name_ref C, file.%C.decl [concrete = constants.%C] // CHECK:STDOUT: %const.loc6_23: type = const_type %C.ref.loc6_29 [concrete = constants.%const.0e5] // CHECK:STDOUT: %ptr.loc6_30: type = ptr_type %const.loc6_23 [concrete = constants.%ptr.c45] // CHECK:STDOUT: %ptr.loc6_31: type = ptr_type %ptr.loc6_30 [concrete = constants.%ptr.728] // CHECK:STDOUT: %.loc6_31: Core.Form = init_form %ptr.loc6_31 [concrete = constants.%.674] // CHECK:STDOUT: %p.param: %ptr.728 = value_param call_param0 // CHECK:STDOUT: %.loc6_17: type = splice_block %ptr.loc6_17 [concrete = constants.%ptr.728] { // CHECK:STDOUT: %C.ref.loc6_15: type = name_ref C, file.%C.decl [concrete = constants.%C] // CHECK:STDOUT: %const.loc6_9: type = const_type %C.ref.loc6_15 [concrete = constants.%const.0e5] // CHECK:STDOUT: %ptr.loc6_16: type = ptr_type %const.loc6_9 [concrete = constants.%ptr.c45] // CHECK:STDOUT: %ptr.loc6_17: type = ptr_type %ptr.loc6_16 [concrete = constants.%ptr.728] // CHECK:STDOUT: } // CHECK:STDOUT: %p: %ptr.728 = value_binding p, %p.param // CHECK:STDOUT: %return.param: ref %ptr.728 = out_param call_param1 // CHECK:STDOUT: %return: ref %ptr.728 = return_slot %return.param // CHECK:STDOUT: } // CHECK:STDOUT: %B.decl: %B.type = fn_decl @B [concrete = constants.%B] { // CHECK:STDOUT: %p.patt: %pattern_type.665 = value_binding_pattern p [concrete] // CHECK:STDOUT: %p.param_patt: %pattern_type.665 = value_param_pattern %p.patt [concrete] // CHECK:STDOUT: %return.patt: %pattern_type.665 = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: %pattern_type.665 = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %C.ref.loc10_31: type = name_ref C, file.%C.decl [concrete = constants.%C] // CHECK:STDOUT: %ptr.loc10_32: type = ptr_type %C.ref.loc10_31 [concrete = constants.%ptr.31e] // CHECK:STDOUT: %const.loc10_24: type = const_type %ptr.loc10_32 [concrete = constants.%const.8ce] // CHECK:STDOUT: %.loc10_24: Core.Form = init_form %const.loc10_24 [concrete = constants.%.d7d] // CHECK:STDOUT: %p.param: %const.8ce = value_param call_param0 // CHECK:STDOUT: %.loc10_9: type = splice_block %const.loc10_9 [concrete = constants.%const.8ce] { // CHECK:STDOUT: %C.ref.loc10_16: type = name_ref C, file.%C.decl [concrete = constants.%C] // CHECK:STDOUT: %ptr.loc10_17: type = ptr_type %C.ref.loc10_16 [concrete = constants.%ptr.31e] // CHECK:STDOUT: %const.loc10_9: type = const_type %ptr.loc10_17 [concrete = constants.%const.8ce] // CHECK:STDOUT: } // CHECK:STDOUT: %p: %const.8ce = value_binding p, %p.param // CHECK:STDOUT: %return.param: ref %const.8ce = out_param call_param1 // CHECK:STDOUT: %return: ref %const.8ce = return_slot %return.param // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @A(%p.param: %ptr.728) -> out %return.param: %ptr.728 { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %p.ref: %ptr.728 = name_ref p, %p // CHECK:STDOUT: %impl.elem0: %.53f = impl_witness_access constants.%Copy.impl_witness.795, element0 [concrete = constants.%ptr.as.Copy.impl.Op.f64] // CHECK:STDOUT: %bound_method.loc7_10.1: = bound_method %p.ref, %impl.elem0 // CHECK:STDOUT: %specific_fn: = specific_function %impl.elem0, @ptr.as.Copy.impl.Op(constants.%ptr.c45) [concrete = constants.%ptr.as.Copy.impl.Op.specific_fn.6a7] // CHECK:STDOUT: %bound_method.loc7_10.2: = bound_method %p.ref, %specific_fn // CHECK:STDOUT: %ptr.as.Copy.impl.Op.call: init %ptr.728 = call %bound_method.loc7_10.2(%p.ref) // CHECK:STDOUT: return %ptr.as.Copy.impl.Op.call // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @B(%p.param: %const.8ce) -> out %return.param: %const.8ce { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %p.ref: %const.8ce = name_ref p, %p // CHECK:STDOUT: %impl.elem0: %.21b = impl_witness_access constants.%Copy.impl_witness.e93, element0 [concrete = constants.%const.as.Copy.impl.Op.c19] // CHECK:STDOUT: %bound_method.loc11_10.1: = bound_method %p.ref, %impl.elem0 // CHECK:STDOUT: %specific_fn: = specific_function %impl.elem0, @const.as.Copy.impl.Op(constants.%Copy.facet.a7f) [concrete = constants.%const.as.Copy.impl.Op.specific_fn] // CHECK:STDOUT: %bound_method.loc11_10.2: = bound_method %p.ref, %specific_fn // CHECK:STDOUT: %const.as.Copy.impl.Op.call: init %const.8ce = call %bound_method.loc11_10.2(%p.ref) // CHECK:STDOUT: return %const.as.Copy.impl.Op.call // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- collapse.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %C: type = class_type @C [concrete] // CHECK:STDOUT: %const.0e5: type = const_type %C [concrete] // CHECK:STDOUT: %ptr.c45: type = ptr_type %const.0e5 [concrete] // CHECK:STDOUT: %ptr.728: type = ptr_type %ptr.c45 [concrete] // CHECK:STDOUT: %pattern_type.559: type = pattern_type %ptr.728 [concrete] // CHECK:STDOUT: %.674: Core.Form = init_form %ptr.728 [concrete] // CHECK:STDOUT: %F.type: type = fn_type @F [concrete] // CHECK:STDOUT: %F: %F.type = struct_value () [concrete] // CHECK:STDOUT: %Copy.type: type = facet_type <@Copy> [concrete] // CHECK:STDOUT: %T.67d: type = symbolic_binding T, 0 [symbolic] // CHECK:STDOUT: %ptr.as.Copy.impl.Op.type.2d4: type = fn_type @ptr.as.Copy.impl.Op, @ptr.as.Copy.impl(%T.67d) [symbolic] // CHECK:STDOUT: %ptr.as.Copy.impl.Op.74e: %ptr.as.Copy.impl.Op.type.2d4 = struct_value () [symbolic] // CHECK:STDOUT: %Copy.impl_witness.795: = impl_witness imports.%Copy.impl_witness_table.c3a, @ptr.as.Copy.impl(%ptr.c45) [concrete] // CHECK:STDOUT: %ptr.as.Copy.impl.Op.type.f8c: type = fn_type @ptr.as.Copy.impl.Op, @ptr.as.Copy.impl(%ptr.c45) [concrete] // CHECK:STDOUT: %ptr.as.Copy.impl.Op.f64: %ptr.as.Copy.impl.Op.type.f8c = struct_value () [concrete] // CHECK:STDOUT: %Copy.facet: %Copy.type = facet_value %ptr.728, (%Copy.impl_witness.795) [concrete] // CHECK:STDOUT: %Copy.WithSelf.Op.type.b3a: type = fn_type @Copy.WithSelf.Op, @Copy.WithSelf(%Copy.facet) [concrete] // CHECK:STDOUT: %.53f: type = fn_type_with_self_type %Copy.WithSelf.Op.type.b3a, %Copy.facet [concrete] // CHECK:STDOUT: %ptr.as.Copy.impl.Op.specific_fn: = specific_function %ptr.as.Copy.impl.Op.f64, @ptr.as.Copy.impl.Op(%ptr.c45) [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core.import_ref.203: @ptr.as.Copy.impl.%ptr.as.Copy.impl.Op.type (%ptr.as.Copy.impl.Op.type.2d4) = import_ref Core//prelude/parts/copy, loc{{\d+_\d+}}, loaded [symbolic = @ptr.as.Copy.impl.%ptr.as.Copy.impl.Op (constants.%ptr.as.Copy.impl.Op.74e)] // CHECK:STDOUT: %Copy.impl_witness_table.c3a = impl_witness_table (%Core.import_ref.203), @ptr.as.Copy.impl [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: %F.decl: %F.type = fn_decl @F [concrete = constants.%F] { // CHECK:STDOUT: %p.patt: %pattern_type.559 = value_binding_pattern p [concrete] // CHECK:STDOUT: %p.param_patt: %pattern_type.559 = value_param_pattern %p.patt [concrete] // CHECK:STDOUT: %return.patt: %pattern_type.559 = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: %pattern_type.559 = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %C.ref.loc11_36: type = name_ref C, file.%C.decl [concrete = constants.%C] // CHECK:STDOUT: %const.loc11_30: type = const_type %C.ref.loc11_36 [concrete = constants.%const.0e5] // CHECK:STDOUT: %const.loc11_23: type = const_type %const.loc11_30 [concrete = constants.%const.0e5] // CHECK:STDOUT: %ptr.loc11_38: type = ptr_type %const.loc11_23 [concrete = constants.%ptr.c45] // CHECK:STDOUT: %ptr.loc11_39: type = ptr_type %ptr.loc11_38 [concrete = constants.%ptr.728] // CHECK:STDOUT: %.loc11_39: Core.Form = init_form %ptr.loc11_39 [concrete = constants.%.674] // CHECK:STDOUT: %p.param: %ptr.728 = value_param call_param0 // CHECK:STDOUT: %.loc11_17: type = splice_block %ptr.loc11_17 [concrete = constants.%ptr.728] { // CHECK:STDOUT: %C.ref.loc11_15: type = name_ref C, file.%C.decl [concrete = constants.%C] // CHECK:STDOUT: %const.loc11_9: type = const_type %C.ref.loc11_15 [concrete = constants.%const.0e5] // CHECK:STDOUT: %ptr.loc11_16: type = ptr_type %const.loc11_9 [concrete = constants.%ptr.c45] // CHECK:STDOUT: %ptr.loc11_17: type = ptr_type %ptr.loc11_16 [concrete = constants.%ptr.728] // CHECK:STDOUT: } // CHECK:STDOUT: %p: %ptr.728 = value_binding p, %p.param // CHECK:STDOUT: %return.param: ref %ptr.728 = out_param call_param1 // CHECK:STDOUT: %return: ref %ptr.728 = return_slot %return.param // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @F(%p.param: %ptr.728) -> out %return.param: %ptr.728 { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %p.ref: %ptr.728 = name_ref p, %p // CHECK:STDOUT: %impl.elem0: %.53f = impl_witness_access constants.%Copy.impl_witness.795, element0 [concrete = constants.%ptr.as.Copy.impl.Op.f64] // CHECK:STDOUT: %bound_method.loc12_10.1: = bound_method %p.ref, %impl.elem0 // CHECK:STDOUT: %specific_fn: = specific_function %impl.elem0, @ptr.as.Copy.impl.Op(constants.%ptr.c45) [concrete = constants.%ptr.as.Copy.impl.Op.specific_fn] // CHECK:STDOUT: %bound_method.loc12_10.2: = bound_method %p.ref, %specific_fn // CHECK:STDOUT: %ptr.as.Copy.impl.Op.call: init %ptr.728 = call %bound_method.loc12_10.2(%p.ref) // CHECK:STDOUT: return %ptr.as.Copy.impl.Op.call // CHECK:STDOUT: } // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/const/import.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/convert.carbon // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/const/import.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/const/import.carbon // --- implicit.carbon library "[[@TEST_NAME]]"; class C {} fn F() -> const C; var a_ref: const C = F(); var a_ptr_ref: const C* = &a_ref; // --- implicit.impl.carbon impl library "[[@TEST_NAME]]"; // Take a reference to avoid unsupported copy logic. This still validates the // `const` is handled. //@dump-sem-ir-begin var a: const C* = &a_ref; var a_ptr: const C* = a_ptr_ref; //@dump-sem-ir-end // CHECK:STDOUT: --- implicit.impl.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %Copy.type: type = facet_type <@Copy> [concrete] // CHECK:STDOUT: %T.67d: type = symbolic_binding T, 0 [symbolic] // CHECK:STDOUT: %ptr.as.Copy.impl.Op.type.48f: type = fn_type @ptr.as.Copy.impl.Op, @ptr.as.Copy.impl(%T.67d) [symbolic] // CHECK:STDOUT: %ptr.as.Copy.impl.Op.970: %ptr.as.Copy.impl.Op.type.48f = struct_value () [symbolic] // CHECK:STDOUT: %C: type = class_type @C [concrete] // CHECK:STDOUT: %const.0e5: type = const_type %C [concrete] // CHECK:STDOUT: %ptr.c45: type = ptr_type %const.0e5 [concrete] // CHECK:STDOUT: %pattern_type.6eb: type = pattern_type %ptr.c45 [concrete] // CHECK:STDOUT: %pattern_type.03b: type = pattern_type %const.0e5 [concrete] // CHECK:STDOUT: %addr: %ptr.c45 = addr_of imports.%a_ref.var [concrete] // CHECK:STDOUT: %Copy.impl_witness.b9c: = impl_witness imports.%Copy.impl_witness_table.1ed, @ptr.as.Copy.impl(%const.0e5) [concrete] // CHECK:STDOUT: %ptr.as.Copy.impl.Op.type.8d3: type = fn_type @ptr.as.Copy.impl.Op, @ptr.as.Copy.impl(%const.0e5) [concrete] // CHECK:STDOUT: %ptr.as.Copy.impl.Op.09c: %ptr.as.Copy.impl.Op.type.8d3 = struct_value () [concrete] // CHECK:STDOUT: %Copy.facet: %Copy.type = facet_value %ptr.c45, (%Copy.impl_witness.b9c) [concrete] // CHECK:STDOUT: %Copy.WithSelf.Op.type.6aa: type = fn_type @Copy.WithSelf.Op, @Copy.WithSelf(%Copy.facet) [concrete] // CHECK:STDOUT: %.090: type = fn_type_with_self_type %Copy.WithSelf.Op.type.6aa, %Copy.facet [concrete] // CHECK:STDOUT: %ptr.as.Copy.impl.Op.bound: = bound_method %addr, %ptr.as.Copy.impl.Op.09c [concrete] // CHECK:STDOUT: %ptr.as.Copy.impl.Op.specific_fn: = specific_function %ptr.as.Copy.impl.Op.09c, @ptr.as.Copy.impl.Op(%const.0e5) [concrete] // CHECK:STDOUT: %bound_method: = bound_method %addr, %ptr.as.Copy.impl.Op.specific_fn [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Main.C: type = import_ref Main//implicit, C, loaded [concrete = constants.%C] // CHECK:STDOUT: %Main.a_ref: ref %const.0e5 = import_ref Main//implicit, a_ref, loaded [concrete = %a_ref.var] // CHECK:STDOUT: %Main.a_ptr_ref: ref %ptr.c45 = import_ref Main//implicit, a_ptr_ref, loaded [concrete = %a_ptr_ref.var] // CHECK:STDOUT: %Main.import_ref.1cc: @ptr.as.Copy.impl.%ptr.as.Copy.impl.Op.type (%ptr.as.Copy.impl.Op.type.48f) = import_ref Main//implicit, inst{{[0-9A-F]+}} [indirect], loaded [symbolic = @ptr.as.Copy.impl.%ptr.as.Copy.impl.Op (constants.%ptr.as.Copy.impl.Op.970)] // CHECK:STDOUT: %Copy.impl_witness_table.1ed = impl_witness_table (%Main.import_ref.1cc), @ptr.as.Copy.impl [concrete] // CHECK:STDOUT: %a_ref.patt: %pattern_type.03b = ref_binding_pattern a_ref [concrete] // CHECK:STDOUT: %a_ref.var_patt: %pattern_type.03b = var_pattern %a_ref.patt [concrete] // CHECK:STDOUT: %a_ref.var: ref %const.0e5 = var %a_ref.var_patt [concrete] // CHECK:STDOUT: %a_ptr_ref.patt: %pattern_type.6eb = ref_binding_pattern a_ptr_ref [concrete] // CHECK:STDOUT: %a_ptr_ref.var_patt: %pattern_type.6eb = var_pattern %a_ptr_ref.patt [concrete] // CHECK:STDOUT: %a_ptr_ref.var: ref %ptr.c45 = var %a_ptr_ref.var_patt [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %a.patt: %pattern_type.6eb = ref_binding_pattern a [concrete] // CHECK:STDOUT: %a.var_patt: %pattern_type.6eb = var_pattern %a.patt [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %a.var: ref %ptr.c45 = var %a.var_patt [concrete] // CHECK:STDOUT: %.loc6: type = splice_block %ptr.loc6 [concrete = constants.%ptr.c45] { // CHECK:STDOUT: %C.ref.loc6: type = name_ref C, imports.%Main.C [concrete = constants.%C] // CHECK:STDOUT: %const.loc6: type = const_type %C.ref.loc6 [concrete = constants.%const.0e5] // CHECK:STDOUT: %ptr.loc6: type = ptr_type %const.loc6 [concrete = constants.%ptr.c45] // CHECK:STDOUT: } // CHECK:STDOUT: %a: ref %ptr.c45 = ref_binding a, %a.var [concrete = %a.var] // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %a_ptr.patt: %pattern_type.6eb = ref_binding_pattern a_ptr [concrete] // CHECK:STDOUT: %a_ptr.var_patt: %pattern_type.6eb = var_pattern %a_ptr.patt [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %a_ptr.var: ref %ptr.c45 = var %a_ptr.var_patt [concrete] // CHECK:STDOUT: %.loc7: type = splice_block %ptr.loc7 [concrete = constants.%ptr.c45] { // CHECK:STDOUT: %C.ref.loc7: type = name_ref C, imports.%Main.C [concrete = constants.%C] // CHECK:STDOUT: %const.loc7: type = const_type %C.ref.loc7 [concrete = constants.%const.0e5] // CHECK:STDOUT: %ptr.loc7: type = ptr_type %const.loc7 [concrete = constants.%ptr.c45] // CHECK:STDOUT: } // CHECK:STDOUT: %a_ptr: ref %ptr.c45 = ref_binding a_ptr, %a_ptr.var [concrete = %a_ptr.var] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @__global_init() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %a_ref.ref: ref %const.0e5 = name_ref a_ref, imports.%Main.a_ref [concrete = imports.%a_ref.var] // CHECK:STDOUT: %addr: %ptr.c45 = addr_of %a_ref.ref [concrete = constants.%addr] // CHECK:STDOUT: %impl.elem0.loc6: %.090 = impl_witness_access constants.%Copy.impl_witness.b9c, element0 [concrete = constants.%ptr.as.Copy.impl.Op.09c] // CHECK:STDOUT: %bound_method.loc6_19.1: = bound_method %addr, %impl.elem0.loc6 [concrete = constants.%ptr.as.Copy.impl.Op.bound] // CHECK:STDOUT: %specific_fn.loc6: = specific_function %impl.elem0.loc6, @ptr.as.Copy.impl.Op(constants.%const.0e5) [concrete = constants.%ptr.as.Copy.impl.Op.specific_fn] // CHECK:STDOUT: %bound_method.loc6_19.2: = bound_method %addr, %specific_fn.loc6 [concrete = constants.%bound_method] // CHECK:STDOUT: %ptr.as.Copy.impl.Op.call.loc6: init %ptr.c45 = call %bound_method.loc6_19.2(%addr) [concrete = constants.%addr] // CHECK:STDOUT: assign file.%a.var, %ptr.as.Copy.impl.Op.call.loc6 // CHECK:STDOUT: %a_ptr_ref.ref: ref %ptr.c45 = name_ref a_ptr_ref, imports.%Main.a_ptr_ref [concrete = imports.%a_ptr_ref.var] // CHECK:STDOUT: %.loc7: %ptr.c45 = acquire_value %a_ptr_ref.ref // CHECK:STDOUT: %impl.elem0.loc7: %.090 = impl_witness_access constants.%Copy.impl_witness.b9c, element0 [concrete = constants.%ptr.as.Copy.impl.Op.09c] // CHECK:STDOUT: %bound_method.loc7_23.1: = bound_method %.loc7, %impl.elem0.loc7 // CHECK:STDOUT: %specific_fn.loc7: = specific_function %impl.elem0.loc7, @ptr.as.Copy.impl.Op(constants.%const.0e5) [concrete = constants.%ptr.as.Copy.impl.Op.specific_fn] // CHECK:STDOUT: %bound_method.loc7_23.2: = bound_method %.loc7, %specific_fn.loc7 // CHECK:STDOUT: %ptr.as.Copy.impl.Op.call.loc7: init %ptr.c45 = call %bound_method.loc7_23.2(%.loc7) // CHECK:STDOUT: assign file.%a_ptr.var, %ptr.as.Copy.impl.Op.call.loc7 // CHECK:STDOUT: // CHECK:STDOUT: } // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/deduce/array.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/int.carbon // TODO: Add ranges and switch to "--dump-sem-ir-ranges=only". // EXTRA-ARGS: --dump-sem-ir-ranges=if-present // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/deduce/array.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/deduce/array.carbon // --- type_only.carbon library "[[@TEST_NAME]]"; class C {} fn F[T:! type](a: array(T, 3)) -> T { return F(a); } fn G() -> C { var a: array(C, 3) = ({}, {}, {}); return F(a); } // --- bound_only.carbon library "[[@TEST_NAME]]"; class C {} fn F[N:! Core.IntLiteral()](unused a: array(C, N)) -> i32 { return N; } fn G() -> i32 { var a: array(C, 3) = ({}, {}, {}); return F(a); } // --- type_and_bound.carbon library "[[@TEST_NAME]]"; class C {} fn F[T:! type, N:! Core.IntLiteral()](unused a: array(T, N)) {} fn G() { var a: array(C, 3) = ({}, {}, {}); F(a); } // --- fail_bound_mismatch.carbon library "[[@TEST_NAME]]"; class C {} fn F[T:! type](a: array(T, 2)) -> T { return F(a); } fn G() -> C { // TODO: We succeed at deducing T here but fail to convert. Is this the right behavior? var a: array(C, 3) = ({}, {}, {}); // CHECK:STDERR: fail_bound_mismatch.carbon:[[@LINE+10]]:12: error: cannot implicitly convert expression of type `array(C, 3)` to `array(C, 2)` [ConversionFailure] // CHECK:STDERR: return F(a); // CHECK:STDERR: ^ // CHECK:STDERR: fail_bound_mismatch.carbon:[[@LINE+7]]:12: note: type `array(C, 3)` does not implement interface `Core.ImplicitAs(array(C, 2))` [MissingImplInMemberAccessInContext] // CHECK:STDERR: return F(a); // CHECK:STDERR: ^ // CHECK:STDERR: fail_bound_mismatch.carbon:[[@LINE-11]]:16: note: initializing function parameter [InCallToFunctionParam] // CHECK:STDERR: fn F[T:! type](a: array(T, 2)) -> T { return F(a); } // CHECK:STDERR: ^~~~~~~~~~~~~~ // CHECK:STDERR: return F(a); } // --- fail_type_mismatch.carbon library "[[@TEST_NAME]]"; class C {} class D {} fn F[N:! Core.IntLiteral()](unused a: array(C, N)) -> i32 { return N; } fn G() -> i32 { // TODO: We succeed at deducing N here but fail to convert. Is this the right behavior? var a: array(D, 3) = ({}, {}, {}); // CHECK:STDERR: fail_type_mismatch.carbon:[[@LINE+10]]:12: error: cannot implicitly convert expression of type `array(D, 3)` to `array(C, 3)` [ConversionFailure] // CHECK:STDERR: return F(a); // CHECK:STDERR: ^ // CHECK:STDERR: fail_type_mismatch.carbon:[[@LINE+7]]:12: note: type `array(D, 3)` does not implement interface `Core.ImplicitAs(array(C, 3))` [MissingImplInMemberAccessInContext] // CHECK:STDERR: return F(a); // CHECK:STDERR: ^ // CHECK:STDERR: fail_type_mismatch.carbon:[[@LINE-11]]:36: note: initializing function parameter [InCallToFunctionParam] // CHECK:STDERR: fn F[N:! Core.IntLiteral()](unused a: array(C, N)) -> i32 { return N; } // CHECK:STDERR: ^~~~~~~~~~~~~~ // CHECK:STDERR: return F(a); } // --- fail_bound_type_mismatch.carbon library "[[@TEST_NAME]]"; class C {} fn F[N:! i32](unused a: array(C, N)) -> i32 { return N; } fn G() -> i32 { var a: array(C, 3) = ({}, {}, {}); // TODO: This fails because the array bound in `F` is effectively // `N.(ImplicitAs(IntLiteral).Convert)()` // which we can't deduce through. We should decide if we want to support // deductions of that form. If not, it'd be nice to diagnose this situation // better. // CHECK:STDERR: fail_bound_type_mismatch.carbon:[[@LINE+7]]:10: error: cannot deduce value for generic parameter `N` [DeductionIncomplete] // CHECK:STDERR: return F(a); // CHECK:STDERR: ^~~~ // CHECK:STDERR: fail_bound_type_mismatch.carbon:[[@LINE-12]]:1: note: while deducing parameters of generic declared here [DeductionGenericHere] // CHECK:STDERR: fn F[N:! i32](unused a: array(C, N)) -> i32 { return N; } // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: return F(a); } // --- fail_todo_array_length_from_tuple.carbon library "[[@TEST_NAME]]"; class C {} fn F[N:! i32](unused a: array(C, N)) {} fn G() { // TODO: Deduce N as 3 from the tuple's size. // // CHECK:STDERR: fail_todo_array_length_from_tuple.carbon:[[@LINE+7]]:3: error: cannot deduce value for generic parameter `N` [DeductionIncomplete] // CHECK:STDERR: F(({}, {}, {})); // CHECK:STDERR: ^~~~~~~~~~~~~~~ // CHECK:STDERR: fail_todo_array_length_from_tuple.carbon:[[@LINE-8]]:1: note: while deducing parameters of generic declared here [DeductionGenericHere] // CHECK:STDERR: fn F[N:! i32](unused a: array(C, N)) {} // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: F(({}, {}, {})); } // CHECK:STDOUT: --- type_only.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %C: type = class_type @C [concrete] // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %complete_type.357: = complete_type_witness %empty_struct_type [concrete] // CHECK:STDOUT: %type: type = facet_type [concrete] // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self] // CHECK:STDOUT: %pattern_type.98f: type = pattern_type type [concrete] // CHECK:STDOUT: %T: type = symbolic_binding T, 0 [symbolic] // CHECK:STDOUT: %int_3: Core.IntLiteral = int_value 3 [concrete] // CHECK:STDOUT: %array_type.3ec: type = array_type %int_3, %T [symbolic] // CHECK:STDOUT: %pattern_type.b3f: type = pattern_type %array_type.3ec [symbolic] // CHECK:STDOUT: %.184: Core.Form = init_form %T [symbolic] // CHECK:STDOUT: %pattern_type.51d: type = pattern_type %T [symbolic] // CHECK:STDOUT: %F.type: type = fn_type @F [concrete] // CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] // CHECK:STDOUT: %F: %F.type = struct_value () [concrete] // CHECK:STDOUT: %require_complete: = require_complete_type %array_type.3ec [symbolic] // CHECK:STDOUT: %F.specific_fn.643: = specific_function %F, @F(%T) [symbolic] // CHECK:STDOUT: %.a69: Core.Form = init_form %C [concrete] // CHECK:STDOUT: %pattern_type.7c7: type = pattern_type %C [concrete] // CHECK:STDOUT: %G.type: type = fn_type @G [concrete] // CHECK:STDOUT: %G: %G.type = struct_value () [concrete] // CHECK:STDOUT: %array_type.931: type = array_type %int_3, %C [concrete] // CHECK:STDOUT: %pattern_type.f21: type = pattern_type %array_type.931 [concrete] // CHECK:STDOUT: %empty_struct: %empty_struct_type = struct_value () [concrete] // CHECK:STDOUT: %tuple.type.8d4: type = tuple_type (%empty_struct_type, %empty_struct_type, %empty_struct_type) [concrete] // CHECK:STDOUT: %tuple: %tuple.type.8d4 = tuple_value (%empty_struct, %empty_struct, %empty_struct) [concrete] // CHECK:STDOUT: %int_0: Core.IntLiteral = int_value 0 [concrete] // CHECK:STDOUT: %C.val: %C = struct_value () [concrete] // CHECK:STDOUT: %int_1: Core.IntLiteral = int_value 1 [concrete] // CHECK:STDOUT: %int_2: Core.IntLiteral = int_value 2 [concrete] // CHECK:STDOUT: %array: %array_type.931 = tuple_value (%C.val, %C.val, %C.val) [concrete] // CHECK:STDOUT: %F.specific_fn.540: = specific_function %F, @F(%C) [concrete] // CHECK:STDOUT: %Destroy.type: type = facet_type <@Destroy> [concrete] // CHECK:STDOUT: %Destroy.Op.type: type = fn_type @Destroy.Op [concrete] // CHECK:STDOUT: %Destroy.Op: %Destroy.Op.type = struct_value () [concrete] // CHECK:STDOUT: %complete_type.c7a: = complete_type_witness %array_type.931 [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: .Destroy = %Core.Destroy // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: %Core.Destroy: type = import_ref Core//prelude/parts/destroy, Destroy, loaded [concrete = constants.%Destroy.type] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .C = %C.decl // CHECK:STDOUT: .F = %F.decl // CHECK:STDOUT: .G = %G.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %C.decl: type = class_decl @C [concrete = constants.%C] {} {} // CHECK:STDOUT: %F.decl: %F.type = fn_decl @F [concrete = constants.%F] { // CHECK:STDOUT: %T.patt: %pattern_type.98f = symbolic_binding_pattern T, 0 [concrete] // CHECK:STDOUT: %a.patt: @F.%pattern_type.loc6_16 (%pattern_type.b3f) = value_binding_pattern a [concrete] // CHECK:STDOUT: %a.param_patt: @F.%pattern_type.loc6_16 (%pattern_type.b3f) = value_param_pattern %a.patt [concrete] // CHECK:STDOUT: %return.patt: @F.%pattern_type.loc6_32 (%pattern_type.51d) = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: @F.%pattern_type.loc6_32 (%pattern_type.51d) = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %T.ref.loc6_35: type = name_ref T, %T.loc6_6.2 [symbolic = %T.loc6_6.1 (constants.%T)] // CHECK:STDOUT: %.loc6_35.3: Core.Form = init_form %T.ref.loc6_35 [symbolic = %.loc6_35.2 (constants.%.184)] // CHECK:STDOUT: %.loc6_10.1: type = splice_block %.loc6_10.2 [concrete = type] { // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %.loc6_10.2: type = type_literal type [concrete = type] // CHECK:STDOUT: } // CHECK:STDOUT: %T.loc6_6.2: type = symbolic_binding T, 0 [symbolic = %T.loc6_6.1 (constants.%T)] // CHECK:STDOUT: %a.param: @F.%array_type.loc6_29.1 (%array_type.3ec) = value_param call_param0 // CHECK:STDOUT: %.loc6_29: type = splice_block %array_type.loc6_29.2 [symbolic = %array_type.loc6_29.1 (constants.%array_type.3ec)] { // CHECK:STDOUT: %T.ref.loc6_25: type = name_ref T, %T.loc6_6.2 [symbolic = %T.loc6_6.1 (constants.%T)] // CHECK:STDOUT: %int_3: Core.IntLiteral = int_value 3 [concrete = constants.%int_3] // CHECK:STDOUT: %array_type.loc6_29.2: type = array_type %int_3, %T.ref.loc6_25 [symbolic = %array_type.loc6_29.1 (constants.%array_type.3ec)] // CHECK:STDOUT: } // CHECK:STDOUT: %a: @F.%array_type.loc6_29.1 (%array_type.3ec) = value_binding a, %a.param // CHECK:STDOUT: %return.param: ref @F.%T.loc6_6.1 (%T) = out_param call_param1 // CHECK:STDOUT: %return: ref @F.%T.loc6_6.1 (%T) = return_slot %return.param // CHECK:STDOUT: } // CHECK:STDOUT: %G.decl: %G.type = fn_decl @G [concrete = constants.%G] { // CHECK:STDOUT: %return.patt: %pattern_type.7c7 = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: %pattern_type.7c7 = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %C.ref.loc8: type = name_ref C, file.%C.decl [concrete = constants.%C] // CHECK:STDOUT: %.loc8_11.2: Core.Form = init_form %C.ref.loc8 [concrete = constants.%.a69] // CHECK:STDOUT: %return.param: ref %C = out_param call_param0 // CHECK:STDOUT: %return: ref %C = return_slot %return.param // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @C { // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type.357] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%C // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic fn @F(%T.loc6_6.2: type) { // CHECK:STDOUT: %T.loc6_6.1: type = symbolic_binding T, 0 [symbolic = %T.loc6_6.1 (constants.%T)] // CHECK:STDOUT: %array_type.loc6_29.1: type = array_type constants.%int_3, %T.loc6_6.1 [symbolic = %array_type.loc6_29.1 (constants.%array_type.3ec)] // CHECK:STDOUT: %pattern_type.loc6_16: type = pattern_type %array_type.loc6_29.1 [symbolic = %pattern_type.loc6_16 (constants.%pattern_type.b3f)] // CHECK:STDOUT: %.loc6_35.2: Core.Form = init_form %T.loc6_6.1 [symbolic = %.loc6_35.2 (constants.%.184)] // CHECK:STDOUT: %pattern_type.loc6_32: type = pattern_type %T.loc6_6.1 [symbolic = %pattern_type.loc6_32 (constants.%pattern_type.51d)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete: = require_complete_type %array_type.loc6_29.1 [symbolic = %require_complete (constants.%require_complete)] // CHECK:STDOUT: %F.specific_fn.loc6_46.2: = specific_function constants.%F, @F(%T.loc6_6.1) [symbolic = %F.specific_fn.loc6_46.2 (constants.%F.specific_fn.643)] // CHECK:STDOUT: // CHECK:STDOUT: fn(%a.param: @F.%array_type.loc6_29.1 (%array_type.3ec)) -> out %return.param: @F.%T.loc6_6.1 (%T) { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %F.ref: %F.type = name_ref F, file.%F.decl [concrete = constants.%F] // CHECK:STDOUT: %a.ref: @F.%array_type.loc6_29.1 (%array_type.3ec) = name_ref a, %a // CHECK:STDOUT: %F.specific_fn.loc6_46.1: = specific_function %F.ref, @F(constants.%T) [symbolic = %F.specific_fn.loc6_46.2 (constants.%F.specific_fn.643)] // CHECK:STDOUT: %.loc6_35.1: ref @F.%T.loc6_6.1 (%T) = splice_block %return.param {} // CHECK:STDOUT: %F.call: init @F.%T.loc6_6.1 (%T) to %.loc6_35.1 = call %F.specific_fn.loc6_46.1(%a.ref) // CHECK:STDOUT: return %F.call to %return.param // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @G() -> out %return.param: %C { // CHECK:STDOUT: !entry: // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %a.patt: %pattern_type.f21 = ref_binding_pattern a [concrete] // CHECK:STDOUT: %a.var_patt: %pattern_type.f21 = var_pattern %a.patt [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %a.var: ref %array_type.931 = var %a.var_patt // CHECK:STDOUT: %.loc9_26.1: %empty_struct_type = struct_literal () [concrete = constants.%empty_struct] // CHECK:STDOUT: %.loc9_30.1: %empty_struct_type = struct_literal () [concrete = constants.%empty_struct] // CHECK:STDOUT: %.loc9_34.1: %empty_struct_type = struct_literal () [concrete = constants.%empty_struct] // CHECK:STDOUT: %.loc9_35.1: %tuple.type.8d4 = tuple_literal (%.loc9_26.1, %.loc9_30.1, %.loc9_34.1) [concrete = constants.%tuple] // CHECK:STDOUT: %int_0: Core.IntLiteral = int_value 0 [concrete = constants.%int_0] // CHECK:STDOUT: %.loc9_35.2: ref %C = array_index %a.var, %int_0 // CHECK:STDOUT: %.loc9_26.2: init %C to %.loc9_35.2 = class_init () [concrete = constants.%C.val] // CHECK:STDOUT: %.loc9_35.3: init %C = converted %.loc9_26.1, %.loc9_26.2 [concrete = constants.%C.val] // CHECK:STDOUT: %int_1: Core.IntLiteral = int_value 1 [concrete = constants.%int_1] // CHECK:STDOUT: %.loc9_35.4: ref %C = array_index %a.var, %int_1 // CHECK:STDOUT: %.loc9_30.2: init %C to %.loc9_35.4 = class_init () [concrete = constants.%C.val] // CHECK:STDOUT: %.loc9_35.5: init %C = converted %.loc9_30.1, %.loc9_30.2 [concrete = constants.%C.val] // CHECK:STDOUT: %int_2: Core.IntLiteral = int_value 2 [concrete = constants.%int_2] // CHECK:STDOUT: %.loc9_35.6: ref %C = array_index %a.var, %int_2 // CHECK:STDOUT: %.loc9_34.2: init %C to %.loc9_35.6 = class_init () [concrete = constants.%C.val] // CHECK:STDOUT: %.loc9_35.7: init %C = converted %.loc9_34.1, %.loc9_34.2 [concrete = constants.%C.val] // CHECK:STDOUT: %.loc9_35.8: init %array_type.931 to %a.var = array_init (%.loc9_35.3, %.loc9_35.5, %.loc9_35.7) [concrete = constants.%array] // CHECK:STDOUT: %.loc9_3: init %array_type.931 = converted %.loc9_35.1, %.loc9_35.8 [concrete = constants.%array] // CHECK:STDOUT: assign %a.var, %.loc9_3 // CHECK:STDOUT: %.loc9_20: type = splice_block %array_type [concrete = constants.%array_type.931] { // CHECK:STDOUT: %C.ref.loc9: type = name_ref C, file.%C.decl [concrete = constants.%C] // CHECK:STDOUT: %int_3: Core.IntLiteral = int_value 3 [concrete = constants.%int_3] // CHECK:STDOUT: %array_type: type = array_type %int_3, %C.ref.loc9 [concrete = constants.%array_type.931] // CHECK:STDOUT: } // CHECK:STDOUT: %a: ref %array_type.931 = ref_binding a, %a.var // CHECK:STDOUT: %F.ref: %F.type = name_ref F, file.%F.decl [concrete = constants.%F] // CHECK:STDOUT: %a.ref: ref %array_type.931 = name_ref a, %a // CHECK:STDOUT: %F.specific_fn: = specific_function %F.ref, @F(constants.%C) [concrete = constants.%F.specific_fn.540] // CHECK:STDOUT: %.loc8_11.1: ref %C = splice_block %return.param {} // CHECK:STDOUT: %.loc10: %array_type.931 = acquire_value %a.ref // CHECK:STDOUT: %F.call: init %C to %.loc8_11.1 = call %F.specific_fn(%.loc10) // CHECK:STDOUT: %Destroy.Op.bound: = bound_method %a.var, constants.%Destroy.Op // CHECK:STDOUT: %Destroy.Op.call: init %empty_tuple.type = call %Destroy.Op.bound(%a.var) // CHECK:STDOUT: return %F.call to %return.param // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @Destroy.Op(%self.param: ref %array_type.931) = "no_op"; // CHECK:STDOUT: // CHECK:STDOUT: specific @F(constants.%T) { // CHECK:STDOUT: %T.loc6_6.1 => constants.%T // CHECK:STDOUT: %array_type.loc6_29.1 => constants.%array_type.3ec // CHECK:STDOUT: %pattern_type.loc6_16 => constants.%pattern_type.b3f // CHECK:STDOUT: %.loc6_35.2 => constants.%.184 // CHECK:STDOUT: %pattern_type.loc6_32 => constants.%pattern_type.51d // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete => constants.%require_complete // CHECK:STDOUT: %F.specific_fn.loc6_46.2 => constants.%F.specific_fn.643 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @F(constants.%C) { // CHECK:STDOUT: %T.loc6_6.1 => constants.%C // CHECK:STDOUT: %array_type.loc6_29.1 => constants.%array_type.931 // CHECK:STDOUT: %pattern_type.loc6_16 => constants.%pattern_type.f21 // CHECK:STDOUT: %.loc6_35.2 => constants.%.a69 // CHECK:STDOUT: %pattern_type.loc6_32 => constants.%pattern_type.7c7 // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete => constants.%complete_type.c7a // CHECK:STDOUT: %F.specific_fn.loc6_46.2 => constants.%F.specific_fn.540 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- bound_only.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %C: type = class_type @C [concrete] // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %complete_type.357: = complete_type_witness %empty_struct_type [concrete] // CHECK:STDOUT: %type: type = facet_type [concrete] // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self] // CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] // CHECK:STDOUT: %IntLiteral.type: type = fn_type @IntLiteral [concrete] // CHECK:STDOUT: %IntLiteral: %IntLiteral.type = struct_value () [concrete] // CHECK:STDOUT: %pattern_type.dc0: type = pattern_type Core.IntLiteral [concrete] // CHECK:STDOUT: %N: Core.IntLiteral = symbolic_binding N, 0 [symbolic] // CHECK:STDOUT: %array_type.c79: type = array_type %N, %C [symbolic] // CHECK:STDOUT: %pattern_type.cc9: type = pattern_type %array_type.c79 [symbolic] // CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [concrete] // CHECK:STDOUT: %Int.type: type = generic_class_type @Int [concrete] // CHECK:STDOUT: %Int.generic: %Int.type = struct_value () [concrete] // CHECK:STDOUT: %i32: type = class_type @Int, @Int(%int_32) [concrete] // CHECK:STDOUT: %.ff5: Core.Form = init_form %i32 [concrete] // CHECK:STDOUT: %pattern_type.7ce: type = pattern_type %i32 [concrete] // CHECK:STDOUT: %F.type: type = fn_type @F [concrete] // CHECK:STDOUT: %F: %F.type = struct_value () [concrete] // CHECK:STDOUT: %require_complete.d0d: = require_complete_type %array_type.c79 [symbolic] // CHECK:STDOUT: %ImplicitAs.type.cc7: type = generic_interface_type @ImplicitAs [concrete] // CHECK:STDOUT: %ImplicitAs.generic: %ImplicitAs.type.cc7 = struct_value () [concrete] // CHECK:STDOUT: %ImplicitAs.type.e8c: type = facet_type <@ImplicitAs, @ImplicitAs(%i32)> [concrete] // CHECK:STDOUT: %To: Core.IntLiteral = symbolic_binding To, 0 [symbolic] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.4e6: type = fn_type @Core.IntLiteral.as.ImplicitAs.impl.Convert, @Core.IntLiteral.as.ImplicitAs.impl(%To) [symbolic] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.3c2: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.4e6 = struct_value () [symbolic] // CHECK:STDOUT: %ImplicitAs.impl_witness.6bc: = impl_witness imports.%ImplicitAs.impl_witness_table.74f, @Core.IntLiteral.as.ImplicitAs.impl(%int_32) [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.e0d: type = fn_type @Core.IntLiteral.as.ImplicitAs.impl.Convert, @Core.IntLiteral.as.ImplicitAs.impl(%int_32) [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.e0d = struct_value () [concrete] // CHECK:STDOUT: %ImplicitAs.facet: %ImplicitAs.type.e8c = facet_value Core.IntLiteral, (%ImplicitAs.impl_witness.6bc) [concrete] // CHECK:STDOUT: %ImplicitAs.WithSelf.Convert.type.b37: type = fn_type @ImplicitAs.WithSelf.Convert, @ImplicitAs.WithSelf(%i32, %ImplicitAs.facet) [concrete] // CHECK:STDOUT: %.545: type = fn_type_with_self_type %ImplicitAs.WithSelf.Convert.type.b37, %ImplicitAs.facet [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.bound.d6e: = bound_method %N, %Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5 [symbolic] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn: = specific_function %Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5, @Core.IntLiteral.as.ImplicitAs.impl.Convert(%int_32) [concrete] // CHECK:STDOUT: %bound_method.7fa: = bound_method %N, %Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn [symbolic] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.call: init %i32 = call %bound_method.7fa(%N) [symbolic] // CHECK:STDOUT: %G.type: type = fn_type @G [concrete] // CHECK:STDOUT: %G: %G.type = struct_value () [concrete] // CHECK:STDOUT: %int_3.1ba: Core.IntLiteral = int_value 3 [concrete] // CHECK:STDOUT: %array_type.931: type = array_type %int_3.1ba, %C [concrete] // CHECK:STDOUT: %pattern_type.f21: type = pattern_type %array_type.931 [concrete] // CHECK:STDOUT: %empty_struct: %empty_struct_type = struct_value () [concrete] // CHECK:STDOUT: %tuple.type.8d4: type = tuple_type (%empty_struct_type, %empty_struct_type, %empty_struct_type) [concrete] // CHECK:STDOUT: %tuple: %tuple.type.8d4 = tuple_value (%empty_struct, %empty_struct, %empty_struct) [concrete] // CHECK:STDOUT: %int_0: Core.IntLiteral = int_value 0 [concrete] // CHECK:STDOUT: %C.val: %C = struct_value () [concrete] // CHECK:STDOUT: %int_1: Core.IntLiteral = int_value 1 [concrete] // CHECK:STDOUT: %int_2: Core.IntLiteral = int_value 2 [concrete] // CHECK:STDOUT: %array: %array_type.931 = tuple_value (%C.val, %C.val, %C.val) [concrete] // CHECK:STDOUT: %F.specific_fn: = specific_function %F, @F(%int_3.1ba) [concrete] // CHECK:STDOUT: %Destroy.type: type = facet_type <@Destroy> [concrete] // CHECK:STDOUT: %Destroy.Op.type: type = fn_type @Destroy.Op [concrete] // CHECK:STDOUT: %Destroy.Op: %Destroy.Op.type = struct_value () [concrete] // CHECK:STDOUT: %complete_type.c7a: = complete_type_witness %array_type.931 [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.bound.061: = bound_method %int_3.1ba, %Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5 [concrete] // CHECK:STDOUT: %bound_method.fa7: = bound_method %int_3.1ba, %Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn [concrete] // CHECK:STDOUT: %int_3.822: %i32 = int_value 3 [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: .IntLiteral = %Core.IntLiteral // CHECK:STDOUT: .Int = %Core.Int // CHECK:STDOUT: .ImplicitAs = %Core.ImplicitAs // CHECK:STDOUT: .Destroy = %Core.Destroy // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: %Core.IntLiteral: %IntLiteral.type = import_ref Core//prelude/parts/int_literal, IntLiteral, loaded [concrete = constants.%IntLiteral] // CHECK:STDOUT: %Core.Int: %Int.type = import_ref Core//prelude/parts/int, Int, loaded [concrete = constants.%Int.generic] // CHECK:STDOUT: %Core.ImplicitAs: %ImplicitAs.type.cc7 = import_ref Core//prelude/parts/as, ImplicitAs, loaded [concrete = constants.%ImplicitAs.generic] // CHECK:STDOUT: %Core.import_ref.42d: @Core.IntLiteral.as.ImplicitAs.impl.%Core.IntLiteral.as.ImplicitAs.impl.Convert.type (%Core.IntLiteral.as.ImplicitAs.impl.Convert.type.4e6) = import_ref Core//prelude/parts/int, loc{{\d+_\d+}}, loaded [symbolic = @Core.IntLiteral.as.ImplicitAs.impl.%Core.IntLiteral.as.ImplicitAs.impl.Convert (constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.3c2)] // CHECK:STDOUT: %ImplicitAs.impl_witness_table.74f = impl_witness_table (%Core.import_ref.42d), @Core.IntLiteral.as.ImplicitAs.impl [concrete] // CHECK:STDOUT: %Core.Destroy: type = import_ref Core//prelude/parts/destroy, Destroy, loaded [concrete = constants.%Destroy.type] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .C = %C.decl // CHECK:STDOUT: .F = %F.decl // CHECK:STDOUT: .G = %G.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %C.decl: type = class_decl @C [concrete = constants.%C] {} {} // CHECK:STDOUT: %F.decl: %F.type = fn_decl @F [concrete = constants.%F] { // CHECK:STDOUT: %N.patt: %pattern_type.dc0 = symbolic_binding_pattern N, 0 [concrete] // CHECK:STDOUT: %a.patt: @F.%pattern_type (%pattern_type.cc9) = value_binding_pattern a [concrete] // CHECK:STDOUT: %a.param_patt: @F.%pattern_type (%pattern_type.cc9) = value_param_pattern %a.patt [concrete] // CHECK:STDOUT: %return.patt: %pattern_type.7ce = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: %pattern_type.7ce = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %i32: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %.loc6_55: Core.Form = init_form %i32 [concrete = constants.%.ff5] // CHECK:STDOUT: %.loc6_26.1: type = splice_block %.loc6_26.3 [concrete = Core.IntLiteral] { // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %Core.ref: = name_ref Core, imports.%Core [concrete = imports.%Core] // CHECK:STDOUT: %IntLiteral.ref: %IntLiteral.type = name_ref IntLiteral, imports.%Core.IntLiteral [concrete = constants.%IntLiteral] // CHECK:STDOUT: %IntLiteral.call: init type = call %IntLiteral.ref() [concrete = Core.IntLiteral] // CHECK:STDOUT: %.loc6_26.2: type = value_of_initializer %IntLiteral.call [concrete = Core.IntLiteral] // CHECK:STDOUT: %.loc6_26.3: type = converted %IntLiteral.call, %.loc6_26.2 [concrete = Core.IntLiteral] // CHECK:STDOUT: } // CHECK:STDOUT: %N.loc6_6.2: Core.IntLiteral = symbolic_binding N, 0 [symbolic = %N.loc6_6.1 (constants.%N)] // CHECK:STDOUT: %a.param: @F.%array_type.loc6_49.1 (%array_type.c79) = value_param call_param0 // CHECK:STDOUT: %.loc6_49: type = splice_block %array_type.loc6_49.2 [symbolic = %array_type.loc6_49.1 (constants.%array_type.c79)] { // CHECK:STDOUT: %C.ref: type = name_ref C, file.%C.decl [concrete = constants.%C] // CHECK:STDOUT: %N.ref.loc6_48: Core.IntLiteral = name_ref N, %N.loc6_6.2 [symbolic = %N.loc6_6.1 (constants.%N)] // CHECK:STDOUT: %array_type.loc6_49.2: type = array_type %N.ref.loc6_48, %C.ref [symbolic = %array_type.loc6_49.1 (constants.%array_type.c79)] // CHECK:STDOUT: } // CHECK:STDOUT: %a: @F.%array_type.loc6_49.1 (%array_type.c79) = value_binding a, %a.param // CHECK:STDOUT: %return.param: ref %i32 = out_param call_param1 // CHECK:STDOUT: %return: ref %i32 = return_slot %return.param // CHECK:STDOUT: } // CHECK:STDOUT: %G.decl: %G.type = fn_decl @G [concrete = constants.%G] { // CHECK:STDOUT: %return.patt: %pattern_type.7ce = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: %pattern_type.7ce = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %i32: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %.loc8: Core.Form = init_form %i32 [concrete = constants.%.ff5] // CHECK:STDOUT: %return.param: ref %i32 = out_param call_param0 // CHECK:STDOUT: %return: ref %i32 = return_slot %return.param // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @C { // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type.357] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%C // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic fn @F(%N.loc6_6.2: Core.IntLiteral) { // CHECK:STDOUT: %N.loc6_6.1: Core.IntLiteral = symbolic_binding N, 0 [symbolic = %N.loc6_6.1 (constants.%N)] // CHECK:STDOUT: %array_type.loc6_49.1: type = array_type %N.loc6_6.1, constants.%C [symbolic = %array_type.loc6_49.1 (constants.%array_type.c79)] // CHECK:STDOUT: %pattern_type: type = pattern_type %array_type.loc6_49.1 [symbolic = %pattern_type (constants.%pattern_type.cc9)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete: = require_complete_type %array_type.loc6_49.1 [symbolic = %require_complete (constants.%require_complete.d0d)] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.bound: = bound_method %N.loc6_6.1, constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5 [symbolic = %Core.IntLiteral.as.ImplicitAs.impl.Convert.bound (constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.bound.d6e)] // CHECK:STDOUT: %bound_method.loc6_69.3: = bound_method %N.loc6_6.1, constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn [symbolic = %bound_method.loc6_69.3 (constants.%bound_method.7fa)] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc6_69.2: init %i32 = call %bound_method.loc6_69.3(%N.loc6_6.1) [symbolic = %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc6_69.2 (constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.call)] // CHECK:STDOUT: // CHECK:STDOUT: fn(%a.param: @F.%array_type.loc6_49.1 (%array_type.c79)) -> out %return.param: %i32 { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %N.ref.loc6_68: Core.IntLiteral = name_ref N, %N.loc6_6.2 [symbolic = %N.loc6_6.1 (constants.%N)] // CHECK:STDOUT: %impl.elem0: %.545 = impl_witness_access constants.%ImplicitAs.impl_witness.6bc, element0 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5] // CHECK:STDOUT: %bound_method.loc6_69.1: = bound_method %N.ref.loc6_68, %impl.elem0 [symbolic = %Core.IntLiteral.as.ImplicitAs.impl.Convert.bound (constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.bound.d6e)] // CHECK:STDOUT: %specific_fn: = specific_function %impl.elem0, @Core.IntLiteral.as.ImplicitAs.impl.Convert(constants.%int_32) [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn] // CHECK:STDOUT: %bound_method.loc6_69.2: = bound_method %N.ref.loc6_68, %specific_fn [symbolic = %bound_method.loc6_69.3 (constants.%bound_method.7fa)] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc6_69.1: init %i32 = call %bound_method.loc6_69.2(%N.ref.loc6_68) [symbolic = %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc6_69.2 (constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.call)] // CHECK:STDOUT: %.loc6_69: init %i32 = converted %N.ref.loc6_68, %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc6_69.1 [symbolic = %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc6_69.2 (constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.call)] // CHECK:STDOUT: return %.loc6_69 // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @G() -> out %return.param: %i32 { // CHECK:STDOUT: !entry: // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %a.patt: %pattern_type.f21 = ref_binding_pattern a [concrete] // CHECK:STDOUT: %a.var_patt: %pattern_type.f21 = var_pattern %a.patt [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %a.var: ref %array_type.931 = var %a.var_patt // CHECK:STDOUT: %.loc9_26.1: %empty_struct_type = struct_literal () [concrete = constants.%empty_struct] // CHECK:STDOUT: %.loc9_30.1: %empty_struct_type = struct_literal () [concrete = constants.%empty_struct] // CHECK:STDOUT: %.loc9_34.1: %empty_struct_type = struct_literal () [concrete = constants.%empty_struct] // CHECK:STDOUT: %.loc9_35.1: %tuple.type.8d4 = tuple_literal (%.loc9_26.1, %.loc9_30.1, %.loc9_34.1) [concrete = constants.%tuple] // CHECK:STDOUT: %int_0: Core.IntLiteral = int_value 0 [concrete = constants.%int_0] // CHECK:STDOUT: %.loc9_35.2: ref %C = array_index %a.var, %int_0 // CHECK:STDOUT: %.loc9_26.2: init %C to %.loc9_35.2 = class_init () [concrete = constants.%C.val] // CHECK:STDOUT: %.loc9_35.3: init %C = converted %.loc9_26.1, %.loc9_26.2 [concrete = constants.%C.val] // CHECK:STDOUT: %int_1: Core.IntLiteral = int_value 1 [concrete = constants.%int_1] // CHECK:STDOUT: %.loc9_35.4: ref %C = array_index %a.var, %int_1 // CHECK:STDOUT: %.loc9_30.2: init %C to %.loc9_35.4 = class_init () [concrete = constants.%C.val] // CHECK:STDOUT: %.loc9_35.5: init %C = converted %.loc9_30.1, %.loc9_30.2 [concrete = constants.%C.val] // CHECK:STDOUT: %int_2: Core.IntLiteral = int_value 2 [concrete = constants.%int_2] // CHECK:STDOUT: %.loc9_35.6: ref %C = array_index %a.var, %int_2 // CHECK:STDOUT: %.loc9_34.2: init %C to %.loc9_35.6 = class_init () [concrete = constants.%C.val] // CHECK:STDOUT: %.loc9_35.7: init %C = converted %.loc9_34.1, %.loc9_34.2 [concrete = constants.%C.val] // CHECK:STDOUT: %.loc9_35.8: init %array_type.931 to %a.var = array_init (%.loc9_35.3, %.loc9_35.5, %.loc9_35.7) [concrete = constants.%array] // CHECK:STDOUT: %.loc9_3: init %array_type.931 = converted %.loc9_35.1, %.loc9_35.8 [concrete = constants.%array] // CHECK:STDOUT: assign %a.var, %.loc9_3 // CHECK:STDOUT: %.loc9_20: type = splice_block %array_type [concrete = constants.%array_type.931] { // CHECK:STDOUT: %C.ref: type = name_ref C, file.%C.decl [concrete = constants.%C] // CHECK:STDOUT: %int_3: Core.IntLiteral = int_value 3 [concrete = constants.%int_3.1ba] // CHECK:STDOUT: %array_type: type = array_type %int_3, %C.ref [concrete = constants.%array_type.931] // CHECK:STDOUT: } // CHECK:STDOUT: %a: ref %array_type.931 = ref_binding a, %a.var // CHECK:STDOUT: %F.ref: %F.type = name_ref F, file.%F.decl [concrete = constants.%F] // CHECK:STDOUT: %a.ref: ref %array_type.931 = name_ref a, %a // CHECK:STDOUT: %F.specific_fn: = specific_function %F.ref, @F(constants.%int_3.1ba) [concrete = constants.%F.specific_fn] // CHECK:STDOUT: %.loc10: %array_type.931 = acquire_value %a.ref // CHECK:STDOUT: %F.call: init %i32 = call %F.specific_fn(%.loc10) // CHECK:STDOUT: %Destroy.Op.bound: = bound_method %a.var, constants.%Destroy.Op // CHECK:STDOUT: %Destroy.Op.call: init %empty_tuple.type = call %Destroy.Op.bound(%a.var) // CHECK:STDOUT: return %F.call // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @Destroy.Op(%self.param: ref %array_type.931) = "no_op"; // CHECK:STDOUT: // CHECK:STDOUT: specific @F(constants.%N) { // CHECK:STDOUT: %N.loc6_6.1 => constants.%N // CHECK:STDOUT: %array_type.loc6_49.1 => constants.%array_type.c79 // CHECK:STDOUT: %pattern_type => constants.%pattern_type.cc9 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @F(constants.%int_3.1ba) { // CHECK:STDOUT: %N.loc6_6.1 => constants.%int_3.1ba // CHECK:STDOUT: %array_type.loc6_49.1 => constants.%array_type.931 // CHECK:STDOUT: %pattern_type => constants.%pattern_type.f21 // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete => constants.%complete_type.c7a // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.bound => constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.bound.061 // CHECK:STDOUT: %bound_method.loc6_69.3 => constants.%bound_method.fa7 // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc6_69.2 => constants.%int_3.822 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- type_and_bound.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %C: type = class_type @C [concrete] // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %complete_type.357: = complete_type_witness %empty_struct_type [concrete] // CHECK:STDOUT: %type: type = facet_type [concrete] // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self] // CHECK:STDOUT: %pattern_type.98f: type = pattern_type type [concrete] // CHECK:STDOUT: %T: type = symbolic_binding T, 0 [symbolic] // CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] // CHECK:STDOUT: %IntLiteral.type: type = fn_type @IntLiteral [concrete] // CHECK:STDOUT: %IntLiteral: %IntLiteral.type = struct_value () [concrete] // CHECK:STDOUT: %pattern_type.dc0: type = pattern_type Core.IntLiteral [concrete] // CHECK:STDOUT: %N: Core.IntLiteral = symbolic_binding N, 1 [symbolic] // CHECK:STDOUT: %array_type.6d2: type = array_type %N, %T [symbolic] // CHECK:STDOUT: %pattern_type.4d8: type = pattern_type %array_type.6d2 [symbolic] // CHECK:STDOUT: %F.type: type = fn_type @F [concrete] // CHECK:STDOUT: %F: %F.type = struct_value () [concrete] // CHECK:STDOUT: %require_complete: = require_complete_type %array_type.6d2 [symbolic] // CHECK:STDOUT: %G.type: type = fn_type @G [concrete] // CHECK:STDOUT: %G: %G.type = struct_value () [concrete] // CHECK:STDOUT: %int_3: Core.IntLiteral = int_value 3 [concrete] // CHECK:STDOUT: %array_type.931: type = array_type %int_3, %C [concrete] // CHECK:STDOUT: %pattern_type.f21: type = pattern_type %array_type.931 [concrete] // CHECK:STDOUT: %empty_struct: %empty_struct_type = struct_value () [concrete] // CHECK:STDOUT: %tuple.type.8d4: type = tuple_type (%empty_struct_type, %empty_struct_type, %empty_struct_type) [concrete] // CHECK:STDOUT: %tuple: %tuple.type.8d4 = tuple_value (%empty_struct, %empty_struct, %empty_struct) [concrete] // CHECK:STDOUT: %int_0: Core.IntLiteral = int_value 0 [concrete] // CHECK:STDOUT: %C.val: %C = struct_value () [concrete] // CHECK:STDOUT: %int_1: Core.IntLiteral = int_value 1 [concrete] // CHECK:STDOUT: %int_2: Core.IntLiteral = int_value 2 [concrete] // CHECK:STDOUT: %array: %array_type.931 = tuple_value (%C.val, %C.val, %C.val) [concrete] // CHECK:STDOUT: %F.specific_fn: = specific_function %F, @F(%C, %int_3) [concrete] // CHECK:STDOUT: %Destroy.type: type = facet_type <@Destroy> [concrete] // CHECK:STDOUT: %Destroy.Op.type: type = fn_type @Destroy.Op [concrete] // CHECK:STDOUT: %Destroy.Op: %Destroy.Op.type = struct_value () [concrete] // CHECK:STDOUT: %complete_type.c7a: = complete_type_witness %array_type.931 [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: .IntLiteral = %Core.IntLiteral // CHECK:STDOUT: .Destroy = %Core.Destroy // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: %Core.IntLiteral: %IntLiteral.type = import_ref Core//prelude/parts/int_literal, IntLiteral, loaded [concrete = constants.%IntLiteral] // CHECK:STDOUT: %Core.Destroy: type = import_ref Core//prelude/parts/destroy, Destroy, loaded [concrete = constants.%Destroy.type] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .C = %C.decl // CHECK:STDOUT: .F = %F.decl // CHECK:STDOUT: .G = %G.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %C.decl: type = class_decl @C [concrete = constants.%C] {} {} // CHECK:STDOUT: %F.decl: %F.type = fn_decl @F [concrete = constants.%F] { // CHECK:STDOUT: %T.patt: %pattern_type.98f = symbolic_binding_pattern T, 0 [concrete] // CHECK:STDOUT: %N.patt: %pattern_type.dc0 = symbolic_binding_pattern N, 1 [concrete] // CHECK:STDOUT: %a.patt: @F.%pattern_type (%pattern_type.4d8) = value_binding_pattern a [concrete] // CHECK:STDOUT: %a.param_patt: @F.%pattern_type (%pattern_type.4d8) = value_param_pattern %a.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %.loc6_10.1: type = splice_block %.loc6_10.2 [concrete = type] { // CHECK:STDOUT: %.Self.2: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %.loc6_10.2: type = type_literal type [concrete = type] // CHECK:STDOUT: } // CHECK:STDOUT: %T.loc6_6.2: type = symbolic_binding T, 0 [symbolic = %T.loc6_6.1 (constants.%T)] // CHECK:STDOUT: %.loc6_36.1: type = splice_block %.loc6_36.3 [concrete = Core.IntLiteral] { // CHECK:STDOUT: %.Self.1: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %Core.ref: = name_ref Core, imports.%Core [concrete = imports.%Core] // CHECK:STDOUT: %IntLiteral.ref: %IntLiteral.type = name_ref IntLiteral, imports.%Core.IntLiteral [concrete = constants.%IntLiteral] // CHECK:STDOUT: %IntLiteral.call: init type = call %IntLiteral.ref() [concrete = Core.IntLiteral] // CHECK:STDOUT: %.loc6_36.2: type = value_of_initializer %IntLiteral.call [concrete = Core.IntLiteral] // CHECK:STDOUT: %.loc6_36.3: type = converted %IntLiteral.call, %.loc6_36.2 [concrete = Core.IntLiteral] // CHECK:STDOUT: } // CHECK:STDOUT: %N.loc6_16.2: Core.IntLiteral = symbolic_binding N, 1 [symbolic = %N.loc6_16.1 (constants.%N)] // CHECK:STDOUT: %a.param: @F.%array_type.loc6_59.1 (%array_type.6d2) = value_param call_param0 // CHECK:STDOUT: %.loc6_59: type = splice_block %array_type.loc6_59.2 [symbolic = %array_type.loc6_59.1 (constants.%array_type.6d2)] { // CHECK:STDOUT: %T.ref: type = name_ref T, %T.loc6_6.2 [symbolic = %T.loc6_6.1 (constants.%T)] // CHECK:STDOUT: %N.ref: Core.IntLiteral = name_ref N, %N.loc6_16.2 [symbolic = %N.loc6_16.1 (constants.%N)] // CHECK:STDOUT: %array_type.loc6_59.2: type = array_type %N.ref, %T.ref [symbolic = %array_type.loc6_59.1 (constants.%array_type.6d2)] // CHECK:STDOUT: } // CHECK:STDOUT: %a: @F.%array_type.loc6_59.1 (%array_type.6d2) = value_binding a, %a.param // CHECK:STDOUT: } // CHECK:STDOUT: %G.decl: %G.type = fn_decl @G [concrete = constants.%G] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @C { // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type.357] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%C // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic fn @F(%T.loc6_6.2: type, %N.loc6_16.2: Core.IntLiteral) { // CHECK:STDOUT: %T.loc6_6.1: type = symbolic_binding T, 0 [symbolic = %T.loc6_6.1 (constants.%T)] // CHECK:STDOUT: %N.loc6_16.1: Core.IntLiteral = symbolic_binding N, 1 [symbolic = %N.loc6_16.1 (constants.%N)] // CHECK:STDOUT: %array_type.loc6_59.1: type = array_type %N.loc6_16.1, %T.loc6_6.1 [symbolic = %array_type.loc6_59.1 (constants.%array_type.6d2)] // CHECK:STDOUT: %pattern_type: type = pattern_type %array_type.loc6_59.1 [symbolic = %pattern_type (constants.%pattern_type.4d8)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete: = require_complete_type %array_type.loc6_59.1 [symbolic = %require_complete (constants.%require_complete)] // CHECK:STDOUT: // CHECK:STDOUT: fn(%a.param: @F.%array_type.loc6_59.1 (%array_type.6d2)) { // CHECK:STDOUT: !entry: // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @G() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %a.patt: %pattern_type.f21 = ref_binding_pattern a [concrete] // CHECK:STDOUT: %a.var_patt: %pattern_type.f21 = var_pattern %a.patt [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %a.var: ref %array_type.931 = var %a.var_patt // CHECK:STDOUT: %.loc9_26.1: %empty_struct_type = struct_literal () [concrete = constants.%empty_struct] // CHECK:STDOUT: %.loc9_30.1: %empty_struct_type = struct_literal () [concrete = constants.%empty_struct] // CHECK:STDOUT: %.loc9_34.1: %empty_struct_type = struct_literal () [concrete = constants.%empty_struct] // CHECK:STDOUT: %.loc9_35.1: %tuple.type.8d4 = tuple_literal (%.loc9_26.1, %.loc9_30.1, %.loc9_34.1) [concrete = constants.%tuple] // CHECK:STDOUT: %int_0: Core.IntLiteral = int_value 0 [concrete = constants.%int_0] // CHECK:STDOUT: %.loc9_35.2: ref %C = array_index %a.var, %int_0 // CHECK:STDOUT: %.loc9_26.2: init %C to %.loc9_35.2 = class_init () [concrete = constants.%C.val] // CHECK:STDOUT: %.loc9_35.3: init %C = converted %.loc9_26.1, %.loc9_26.2 [concrete = constants.%C.val] // CHECK:STDOUT: %int_1: Core.IntLiteral = int_value 1 [concrete = constants.%int_1] // CHECK:STDOUT: %.loc9_35.4: ref %C = array_index %a.var, %int_1 // CHECK:STDOUT: %.loc9_30.2: init %C to %.loc9_35.4 = class_init () [concrete = constants.%C.val] // CHECK:STDOUT: %.loc9_35.5: init %C = converted %.loc9_30.1, %.loc9_30.2 [concrete = constants.%C.val] // CHECK:STDOUT: %int_2: Core.IntLiteral = int_value 2 [concrete = constants.%int_2] // CHECK:STDOUT: %.loc9_35.6: ref %C = array_index %a.var, %int_2 // CHECK:STDOUT: %.loc9_34.2: init %C to %.loc9_35.6 = class_init () [concrete = constants.%C.val] // CHECK:STDOUT: %.loc9_35.7: init %C = converted %.loc9_34.1, %.loc9_34.2 [concrete = constants.%C.val] // CHECK:STDOUT: %.loc9_35.8: init %array_type.931 to %a.var = array_init (%.loc9_35.3, %.loc9_35.5, %.loc9_35.7) [concrete = constants.%array] // CHECK:STDOUT: %.loc9_3: init %array_type.931 = converted %.loc9_35.1, %.loc9_35.8 [concrete = constants.%array] // CHECK:STDOUT: assign %a.var, %.loc9_3 // CHECK:STDOUT: %.loc9_20: type = splice_block %array_type [concrete = constants.%array_type.931] { // CHECK:STDOUT: %C.ref: type = name_ref C, file.%C.decl [concrete = constants.%C] // CHECK:STDOUT: %int_3: Core.IntLiteral = int_value 3 [concrete = constants.%int_3] // CHECK:STDOUT: %array_type: type = array_type %int_3, %C.ref [concrete = constants.%array_type.931] // CHECK:STDOUT: } // CHECK:STDOUT: %a: ref %array_type.931 = ref_binding a, %a.var // CHECK:STDOUT: %F.ref: %F.type = name_ref F, file.%F.decl [concrete = constants.%F] // CHECK:STDOUT: %a.ref: ref %array_type.931 = name_ref a, %a // CHECK:STDOUT: %F.specific_fn: = specific_function %F.ref, @F(constants.%C, constants.%int_3) [concrete = constants.%F.specific_fn] // CHECK:STDOUT: %.loc10: %array_type.931 = acquire_value %a.ref // CHECK:STDOUT: %F.call: init %empty_tuple.type = call %F.specific_fn(%.loc10) // CHECK:STDOUT: %Destroy.Op.bound: = bound_method %a.var, constants.%Destroy.Op // CHECK:STDOUT: %Destroy.Op.call: init %empty_tuple.type = call %Destroy.Op.bound(%a.var) // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @Destroy.Op(%self.param: ref %array_type.931) = "no_op"; // CHECK:STDOUT: // CHECK:STDOUT: specific @F(constants.%T, constants.%N) { // CHECK:STDOUT: %T.loc6_6.1 => constants.%T // CHECK:STDOUT: %N.loc6_16.1 => constants.%N // CHECK:STDOUT: %array_type.loc6_59.1 => constants.%array_type.6d2 // CHECK:STDOUT: %pattern_type => constants.%pattern_type.4d8 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @F(constants.%C, constants.%int_3) { // CHECK:STDOUT: %T.loc6_6.1 => constants.%C // CHECK:STDOUT: %N.loc6_16.1 => constants.%int_3 // CHECK:STDOUT: %array_type.loc6_59.1 => constants.%array_type.931 // CHECK:STDOUT: %pattern_type => constants.%pattern_type.f21 // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete => constants.%complete_type.c7a // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- fail_bound_mismatch.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %C: type = class_type @C [concrete] // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %complete_type.357: = complete_type_witness %empty_struct_type [concrete] // CHECK:STDOUT: %type: type = facet_type [concrete] // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self] // CHECK:STDOUT: %pattern_type.98f: type = pattern_type type [concrete] // CHECK:STDOUT: %T: type = symbolic_binding T, 0 [symbolic] // CHECK:STDOUT: %int_2: Core.IntLiteral = int_value 2 [concrete] // CHECK:STDOUT: %array_type.a0b: type = array_type %int_2, %T [symbolic] // CHECK:STDOUT: %pattern_type.b42: type = pattern_type %array_type.a0b [symbolic] // CHECK:STDOUT: %.184347.1: Core.Form = init_form %T [symbolic] // CHECK:STDOUT: %pattern_type.51d1c4.1: type = pattern_type %T [symbolic] // CHECK:STDOUT: %F.type: type = fn_type @F [concrete] // CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] // CHECK:STDOUT: %F: %F.type = struct_value () [concrete] // CHECK:STDOUT: %require_complete: = require_complete_type %array_type.a0b [symbolic] // CHECK:STDOUT: %F.specific_fn.643: = specific_function %F, @F(%T) [symbolic] // CHECK:STDOUT: %.a69: Core.Form = init_form %C [concrete] // CHECK:STDOUT: %pattern_type.7c7: type = pattern_type %C [concrete] // CHECK:STDOUT: %G.type: type = fn_type @G [concrete] // CHECK:STDOUT: %G: %G.type = struct_value () [concrete] // CHECK:STDOUT: %int_3: Core.IntLiteral = int_value 3 [concrete] // CHECK:STDOUT: %array_type.931: type = array_type %int_3, %C [concrete] // CHECK:STDOUT: %pattern_type.f21: type = pattern_type %array_type.931 [concrete] // CHECK:STDOUT: %empty_struct: %empty_struct_type = struct_value () [concrete] // CHECK:STDOUT: %tuple.type.8d4: type = tuple_type (%empty_struct_type, %empty_struct_type, %empty_struct_type) [concrete] // CHECK:STDOUT: %tuple: %tuple.type.8d4 = tuple_value (%empty_struct, %empty_struct, %empty_struct) [concrete] // CHECK:STDOUT: %int_0: Core.IntLiteral = int_value 0 [concrete] // CHECK:STDOUT: %C.val: %C = struct_value () [concrete] // CHECK:STDOUT: %int_1: Core.IntLiteral = int_value 1 [concrete] // CHECK:STDOUT: %array: %array_type.931 = tuple_value (%C.val, %C.val, %C.val) [concrete] // CHECK:STDOUT: %array_type.158: type = array_type %int_2, %C [concrete] // CHECK:STDOUT: %pattern_type.6d3: type = pattern_type %array_type.158 [concrete] // CHECK:STDOUT: %F.specific_fn.540: = specific_function %F, @F(%C) [concrete] // CHECK:STDOUT: %ImplicitAs.type.cc7: type = generic_interface_type @ImplicitAs [concrete] // CHECK:STDOUT: %ImplicitAs.generic: %ImplicitAs.type.cc7 = struct_value () [concrete] // CHECK:STDOUT: %Destroy.type: type = facet_type <@Destroy> [concrete] // CHECK:STDOUT: %Destroy.Op.type: type = fn_type @Destroy.Op [concrete] // CHECK:STDOUT: %Destroy.Op: %Destroy.Op.type = struct_value () [concrete] // CHECK:STDOUT: %complete_type.b8b: = complete_type_witness %array_type.158 [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: .ImplicitAs = %Core.ImplicitAs // CHECK:STDOUT: .Destroy = %Core.Destroy // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: %Core.ImplicitAs: %ImplicitAs.type.cc7 = import_ref Core//prelude/parts/as, ImplicitAs, loaded [concrete = constants.%ImplicitAs.generic] // CHECK:STDOUT: %Core.Destroy: type = import_ref Core//prelude/parts/destroy, Destroy, loaded [concrete = constants.%Destroy.type] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .C = %C.decl // CHECK:STDOUT: .F = %F.decl // CHECK:STDOUT: .G = %G.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %C.decl: type = class_decl @C [concrete = constants.%C] {} {} // CHECK:STDOUT: %F.decl: %F.type = fn_decl @F [concrete = constants.%F] { // CHECK:STDOUT: %T.patt: %pattern_type.98f = symbolic_binding_pattern T, 0 [concrete] // CHECK:STDOUT: %a.patt: @F.%pattern_type.loc6_16 (%pattern_type.b42) = value_binding_pattern a [concrete] // CHECK:STDOUT: %a.param_patt: @F.%pattern_type.loc6_16 (%pattern_type.b42) = value_param_pattern %a.patt [concrete] // CHECK:STDOUT: %return.patt: @F.%pattern_type.loc6_32 (%pattern_type.51d1c4.1) = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: @F.%pattern_type.loc6_32 (%pattern_type.51d1c4.1) = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %T.ref.loc6_35: type = name_ref T, %T.loc6_6.2 [symbolic = %T.loc6_6.1 (constants.%T)] // CHECK:STDOUT: %.loc6_35.3: Core.Form = init_form %T.ref.loc6_35 [symbolic = %.loc6_35.2 (constants.%.184347.1)] // CHECK:STDOUT: %.loc6_10.1: type = splice_block %.loc6_10.2 [concrete = type] { // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %.loc6_10.2: type = type_literal type [concrete = type] // CHECK:STDOUT: } // CHECK:STDOUT: %T.loc6_6.2: type = symbolic_binding T, 0 [symbolic = %T.loc6_6.1 (constants.%T)] // CHECK:STDOUT: %a.param: @F.%array_type.loc6_29.1 (%array_type.a0b) = value_param call_param0 // CHECK:STDOUT: %.loc6_29: type = splice_block %array_type.loc6_29.2 [symbolic = %array_type.loc6_29.1 (constants.%array_type.a0b)] { // CHECK:STDOUT: %T.ref.loc6_25: type = name_ref T, %T.loc6_6.2 [symbolic = %T.loc6_6.1 (constants.%T)] // CHECK:STDOUT: %int_2: Core.IntLiteral = int_value 2 [concrete = constants.%int_2] // CHECK:STDOUT: %array_type.loc6_29.2: type = array_type %int_2, %T.ref.loc6_25 [symbolic = %array_type.loc6_29.1 (constants.%array_type.a0b)] // CHECK:STDOUT: } // CHECK:STDOUT: %a: @F.%array_type.loc6_29.1 (%array_type.a0b) = value_binding a, %a.param // CHECK:STDOUT: %return.param: ref @F.%T.loc6_6.1 (%T) = out_param call_param1 // CHECK:STDOUT: %return: ref @F.%T.loc6_6.1 (%T) = return_slot %return.param // CHECK:STDOUT: } // CHECK:STDOUT: %G.decl: %G.type = fn_decl @G [concrete = constants.%G] { // CHECK:STDOUT: %return.patt: %pattern_type.7c7 = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: %pattern_type.7c7 = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %C.ref.loc8: type = name_ref C, file.%C.decl [concrete = constants.%C] // CHECK:STDOUT: %.loc8_11.2: Core.Form = init_form %C.ref.loc8 [concrete = constants.%.a69] // CHECK:STDOUT: %return.param: ref %C = out_param call_param0 // CHECK:STDOUT: %return: ref %C = return_slot %return.param // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @C { // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type.357] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%C // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic fn @F(%T.loc6_6.2: type) { // CHECK:STDOUT: %T.loc6_6.1: type = symbolic_binding T, 0 [symbolic = %T.loc6_6.1 (constants.%T)] // CHECK:STDOUT: %array_type.loc6_29.1: type = array_type constants.%int_2, %T.loc6_6.1 [symbolic = %array_type.loc6_29.1 (constants.%array_type.a0b)] // CHECK:STDOUT: %pattern_type.loc6_16: type = pattern_type %array_type.loc6_29.1 [symbolic = %pattern_type.loc6_16 (constants.%pattern_type.b42)] // CHECK:STDOUT: %.loc6_35.2: Core.Form = init_form %T.loc6_6.1 [symbolic = %.loc6_35.2 (constants.%.184347.1)] // CHECK:STDOUT: %pattern_type.loc6_32: type = pattern_type %T.loc6_6.1 [symbolic = %pattern_type.loc6_32 (constants.%pattern_type.51d1c4.1)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete: = require_complete_type %array_type.loc6_29.1 [symbolic = %require_complete (constants.%require_complete)] // CHECK:STDOUT: %F.specific_fn.loc6_46.2: = specific_function constants.%F, @F(%T.loc6_6.1) [symbolic = %F.specific_fn.loc6_46.2 (constants.%F.specific_fn.643)] // CHECK:STDOUT: // CHECK:STDOUT: fn(%a.param: @F.%array_type.loc6_29.1 (%array_type.a0b)) -> out %return.param: @F.%T.loc6_6.1 (%T) { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %F.ref: %F.type = name_ref F, file.%F.decl [concrete = constants.%F] // CHECK:STDOUT: %a.ref: @F.%array_type.loc6_29.1 (%array_type.a0b) = name_ref a, %a // CHECK:STDOUT: %F.specific_fn.loc6_46.1: = specific_function %F.ref, @F(constants.%T) [symbolic = %F.specific_fn.loc6_46.2 (constants.%F.specific_fn.643)] // CHECK:STDOUT: %.loc6_35.1: ref @F.%T.loc6_6.1 (%T) = splice_block %return.param {} // CHECK:STDOUT: %F.call: init @F.%T.loc6_6.1 (%T) to %.loc6_35.1 = call %F.specific_fn.loc6_46.1(%a.ref) // CHECK:STDOUT: return %F.call to %return.param // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @G() -> out %return.param: %C { // CHECK:STDOUT: !entry: // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %a.patt: %pattern_type.f21 = ref_binding_pattern a [concrete] // CHECK:STDOUT: %a.var_patt: %pattern_type.f21 = var_pattern %a.patt [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %a.var: ref %array_type.931 = var %a.var_patt // CHECK:STDOUT: %.loc10_26.1: %empty_struct_type = struct_literal () [concrete = constants.%empty_struct] // CHECK:STDOUT: %.loc10_30.1: %empty_struct_type = struct_literal () [concrete = constants.%empty_struct] // CHECK:STDOUT: %.loc10_34.1: %empty_struct_type = struct_literal () [concrete = constants.%empty_struct] // CHECK:STDOUT: %.loc10_35.1: %tuple.type.8d4 = tuple_literal (%.loc10_26.1, %.loc10_30.1, %.loc10_34.1) [concrete = constants.%tuple] // CHECK:STDOUT: %int_0: Core.IntLiteral = int_value 0 [concrete = constants.%int_0] // CHECK:STDOUT: %.loc10_35.2: ref %C = array_index %a.var, %int_0 // CHECK:STDOUT: %.loc10_26.2: init %C to %.loc10_35.2 = class_init () [concrete = constants.%C.val] // CHECK:STDOUT: %.loc10_35.3: init %C = converted %.loc10_26.1, %.loc10_26.2 [concrete = constants.%C.val] // CHECK:STDOUT: %int_1: Core.IntLiteral = int_value 1 [concrete = constants.%int_1] // CHECK:STDOUT: %.loc10_35.4: ref %C = array_index %a.var, %int_1 // CHECK:STDOUT: %.loc10_30.2: init %C to %.loc10_35.4 = class_init () [concrete = constants.%C.val] // CHECK:STDOUT: %.loc10_35.5: init %C = converted %.loc10_30.1, %.loc10_30.2 [concrete = constants.%C.val] // CHECK:STDOUT: %int_2: Core.IntLiteral = int_value 2 [concrete = constants.%int_2] // CHECK:STDOUT: %.loc10_35.6: ref %C = array_index %a.var, %int_2 // CHECK:STDOUT: %.loc10_34.2: init %C to %.loc10_35.6 = class_init () [concrete = constants.%C.val] // CHECK:STDOUT: %.loc10_35.7: init %C = converted %.loc10_34.1, %.loc10_34.2 [concrete = constants.%C.val] // CHECK:STDOUT: %.loc10_35.8: init %array_type.931 to %a.var = array_init (%.loc10_35.3, %.loc10_35.5, %.loc10_35.7) [concrete = constants.%array] // CHECK:STDOUT: %.loc10_3: init %array_type.931 = converted %.loc10_35.1, %.loc10_35.8 [concrete = constants.%array] // CHECK:STDOUT: assign %a.var, %.loc10_3 // CHECK:STDOUT: %.loc10_20: type = splice_block %array_type [concrete = constants.%array_type.931] { // CHECK:STDOUT: %C.ref.loc10: type = name_ref C, file.%C.decl [concrete = constants.%C] // CHECK:STDOUT: %int_3: Core.IntLiteral = int_value 3 [concrete = constants.%int_3] // CHECK:STDOUT: %array_type: type = array_type %int_3, %C.ref.loc10 [concrete = constants.%array_type.931] // CHECK:STDOUT: } // CHECK:STDOUT: %a: ref %array_type.931 = ref_binding a, %a.var // CHECK:STDOUT: %F.ref: %F.type = name_ref F, file.%F.decl [concrete = constants.%F] // CHECK:STDOUT: %a.ref: ref %array_type.931 = name_ref a, %a // CHECK:STDOUT: %F.specific_fn: = specific_function %F.ref, @F(constants.%C) [concrete = constants.%F.specific_fn.540] // CHECK:STDOUT: %.loc8_11.1: ref %C = splice_block %return.param {} // CHECK:STDOUT: %.loc21: %array_type.158 = converted %a.ref, [concrete = ] // CHECK:STDOUT: %F.call: init %C to %.loc8_11.1 = call %F.specific_fn() // CHECK:STDOUT: %Destroy.Op.bound: = bound_method %a.var, constants.%Destroy.Op // CHECK:STDOUT: %Destroy.Op.call: init %empty_tuple.type = call %Destroy.Op.bound(%a.var) // CHECK:STDOUT: return %F.call to %return.param // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @Destroy.Op(%self.param: ref %array_type.931) = "no_op"; // CHECK:STDOUT: // CHECK:STDOUT: specific @F(constants.%T) { // CHECK:STDOUT: %T.loc6_6.1 => constants.%T // CHECK:STDOUT: %array_type.loc6_29.1 => constants.%array_type.a0b // CHECK:STDOUT: %pattern_type.loc6_16 => constants.%pattern_type.b42 // CHECK:STDOUT: %.loc6_35.2 => constants.%.184347.1 // CHECK:STDOUT: %pattern_type.loc6_32 => constants.%pattern_type.51d1c4.1 // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete => constants.%require_complete // CHECK:STDOUT: %F.specific_fn.loc6_46.2 => constants.%F.specific_fn.643 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @F(constants.%C) { // CHECK:STDOUT: %T.loc6_6.1 => constants.%C // CHECK:STDOUT: %array_type.loc6_29.1 => constants.%array_type.158 // CHECK:STDOUT: %pattern_type.loc6_16 => constants.%pattern_type.6d3 // CHECK:STDOUT: %.loc6_35.2 => constants.%.a69 // CHECK:STDOUT: %pattern_type.loc6_32 => constants.%pattern_type.7c7 // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete => constants.%complete_type.b8b // CHECK:STDOUT: %F.specific_fn.loc6_46.2 => constants.%F.specific_fn.540 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- fail_type_mismatch.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %C: type = class_type @C [concrete] // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %complete_type.357: = complete_type_witness %empty_struct_type [concrete] // CHECK:STDOUT: %D: type = class_type @D [concrete] // CHECK:STDOUT: %type: type = facet_type [concrete] // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self] // CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] // CHECK:STDOUT: %IntLiteral.type: type = fn_type @IntLiteral [concrete] // CHECK:STDOUT: %IntLiteral: %IntLiteral.type = struct_value () [concrete] // CHECK:STDOUT: %pattern_type.dc0: type = pattern_type Core.IntLiteral [concrete] // CHECK:STDOUT: %N: Core.IntLiteral = symbolic_binding N, 0 [symbolic] // CHECK:STDOUT: %array_type.c79: type = array_type %N, %C [symbolic] // CHECK:STDOUT: %pattern_type.cc9: type = pattern_type %array_type.c79 [symbolic] // CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [concrete] // CHECK:STDOUT: %Int.type: type = generic_class_type @Int [concrete] // CHECK:STDOUT: %Int.generic: %Int.type = struct_value () [concrete] // CHECK:STDOUT: %i32: type = class_type @Int, @Int(%int_32) [concrete] // CHECK:STDOUT: %.ff5: Core.Form = init_form %i32 [concrete] // CHECK:STDOUT: %pattern_type.7ce: type = pattern_type %i32 [concrete] // CHECK:STDOUT: %F.type: type = fn_type @F [concrete] // CHECK:STDOUT: %F: %F.type = struct_value () [concrete] // CHECK:STDOUT: %require_complete.d0d: = require_complete_type %array_type.c79 [symbolic] // CHECK:STDOUT: %ImplicitAs.type.cc7: type = generic_interface_type @ImplicitAs [concrete] // CHECK:STDOUT: %ImplicitAs.generic: %ImplicitAs.type.cc7 = struct_value () [concrete] // CHECK:STDOUT: %ImplicitAs.type.e8c: type = facet_type <@ImplicitAs, @ImplicitAs(%i32)> [concrete] // CHECK:STDOUT: %To: Core.IntLiteral = symbolic_binding To, 0 [symbolic] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.4e6: type = fn_type @Core.IntLiteral.as.ImplicitAs.impl.Convert, @Core.IntLiteral.as.ImplicitAs.impl(%To) [symbolic] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.3c2: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.4e6 = struct_value () [symbolic] // CHECK:STDOUT: %ImplicitAs.impl_witness.6bc: = impl_witness imports.%ImplicitAs.impl_witness_table.74f, @Core.IntLiteral.as.ImplicitAs.impl(%int_32) [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.e0d: type = fn_type @Core.IntLiteral.as.ImplicitAs.impl.Convert, @Core.IntLiteral.as.ImplicitAs.impl(%int_32) [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.e0d = struct_value () [concrete] // CHECK:STDOUT: %ImplicitAs.facet: %ImplicitAs.type.e8c = facet_value Core.IntLiteral, (%ImplicitAs.impl_witness.6bc) [concrete] // CHECK:STDOUT: %ImplicitAs.WithSelf.Convert.type.b37: type = fn_type @ImplicitAs.WithSelf.Convert, @ImplicitAs.WithSelf(%i32, %ImplicitAs.facet) [concrete] // CHECK:STDOUT: %.545: type = fn_type_with_self_type %ImplicitAs.WithSelf.Convert.type.b37, %ImplicitAs.facet [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.bound.d6e: = bound_method %N, %Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5 [symbolic] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn: = specific_function %Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5, @Core.IntLiteral.as.ImplicitAs.impl.Convert(%int_32) [concrete] // CHECK:STDOUT: %bound_method.7fa: = bound_method %N, %Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn [symbolic] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.call: init %i32 = call %bound_method.7fa(%N) [symbolic] // CHECK:STDOUT: %G.type: type = fn_type @G [concrete] // CHECK:STDOUT: %G: %G.type = struct_value () [concrete] // CHECK:STDOUT: %int_3.1ba: Core.IntLiteral = int_value 3 [concrete] // CHECK:STDOUT: %array_type.b6d: type = array_type %int_3.1ba, %D [concrete] // CHECK:STDOUT: %pattern_type.454: type = pattern_type %array_type.b6d [concrete] // CHECK:STDOUT: %empty_struct: %empty_struct_type = struct_value () [concrete] // CHECK:STDOUT: %tuple.type.8d4: type = tuple_type (%empty_struct_type, %empty_struct_type, %empty_struct_type) [concrete] // CHECK:STDOUT: %tuple: %tuple.type.8d4 = tuple_value (%empty_struct, %empty_struct, %empty_struct) [concrete] // CHECK:STDOUT: %int_0: Core.IntLiteral = int_value 0 [concrete] // CHECK:STDOUT: %D.val: %D = struct_value () [concrete] // CHECK:STDOUT: %int_1: Core.IntLiteral = int_value 1 [concrete] // CHECK:STDOUT: %int_2: Core.IntLiteral = int_value 2 [concrete] // CHECK:STDOUT: %array: %array_type.b6d = tuple_value (%D.val, %D.val, %D.val) [concrete] // CHECK:STDOUT: %array_type.931: type = array_type %int_3.1ba, %C [concrete] // CHECK:STDOUT: %pattern_type.f21: type = pattern_type %array_type.931 [concrete] // CHECK:STDOUT: %F.specific_fn: = specific_function %F, @F(%int_3.1ba) [concrete] // CHECK:STDOUT: %Destroy.type: type = facet_type <@Destroy> [concrete] // CHECK:STDOUT: %Destroy.Op.type: type = fn_type @Destroy.Op [concrete] // CHECK:STDOUT: %Destroy.Op: %Destroy.Op.type = struct_value () [concrete] // CHECK:STDOUT: %complete_type.c7a: = complete_type_witness %array_type.931 [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.bound.061: = bound_method %int_3.1ba, %Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5 [concrete] // CHECK:STDOUT: %bound_method.fa7: = bound_method %int_3.1ba, %Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn [concrete] // CHECK:STDOUT: %int_3.822: %i32 = int_value 3 [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: .IntLiteral = %Core.IntLiteral // CHECK:STDOUT: .Int = %Core.Int // CHECK:STDOUT: .ImplicitAs = %Core.ImplicitAs // CHECK:STDOUT: .Destroy = %Core.Destroy // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: %Core.IntLiteral: %IntLiteral.type = import_ref Core//prelude/parts/int_literal, IntLiteral, loaded [concrete = constants.%IntLiteral] // CHECK:STDOUT: %Core.Int: %Int.type = import_ref Core//prelude/parts/int, Int, loaded [concrete = constants.%Int.generic] // CHECK:STDOUT: %Core.ImplicitAs: %ImplicitAs.type.cc7 = import_ref Core//prelude/parts/as, ImplicitAs, loaded [concrete = constants.%ImplicitAs.generic] // CHECK:STDOUT: %Core.import_ref.42d: @Core.IntLiteral.as.ImplicitAs.impl.%Core.IntLiteral.as.ImplicitAs.impl.Convert.type (%Core.IntLiteral.as.ImplicitAs.impl.Convert.type.4e6) = import_ref Core//prelude/parts/int, loc{{\d+_\d+}}, loaded [symbolic = @Core.IntLiteral.as.ImplicitAs.impl.%Core.IntLiteral.as.ImplicitAs.impl.Convert (constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.3c2)] // CHECK:STDOUT: %ImplicitAs.impl_witness_table.74f = impl_witness_table (%Core.import_ref.42d), @Core.IntLiteral.as.ImplicitAs.impl [concrete] // CHECK:STDOUT: %Core.Destroy: type = import_ref Core//prelude/parts/destroy, Destroy, loaded [concrete = constants.%Destroy.type] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .C = %C.decl // CHECK:STDOUT: .D = %D.decl // CHECK:STDOUT: .F = %F.decl // CHECK:STDOUT: .G = %G.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %C.decl: type = class_decl @C [concrete = constants.%C] {} {} // CHECK:STDOUT: %D.decl: type = class_decl @D [concrete = constants.%D] {} {} // CHECK:STDOUT: %F.decl: %F.type = fn_decl @F [concrete = constants.%F] { // CHECK:STDOUT: %N.patt: %pattern_type.dc0 = symbolic_binding_pattern N, 0 [concrete] // CHECK:STDOUT: %a.patt: @F.%pattern_type (%pattern_type.cc9) = value_binding_pattern a [concrete] // CHECK:STDOUT: %a.param_patt: @F.%pattern_type (%pattern_type.cc9) = value_param_pattern %a.patt [concrete] // CHECK:STDOUT: %return.patt: %pattern_type.7ce = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: %pattern_type.7ce = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %i32: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %.loc7_55: Core.Form = init_form %i32 [concrete = constants.%.ff5] // CHECK:STDOUT: %.loc7_26.1: type = splice_block %.loc7_26.3 [concrete = Core.IntLiteral] { // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %Core.ref: = name_ref Core, imports.%Core [concrete = imports.%Core] // CHECK:STDOUT: %IntLiteral.ref: %IntLiteral.type = name_ref IntLiteral, imports.%Core.IntLiteral [concrete = constants.%IntLiteral] // CHECK:STDOUT: %IntLiteral.call: init type = call %IntLiteral.ref() [concrete = Core.IntLiteral] // CHECK:STDOUT: %.loc7_26.2: type = value_of_initializer %IntLiteral.call [concrete = Core.IntLiteral] // CHECK:STDOUT: %.loc7_26.3: type = converted %IntLiteral.call, %.loc7_26.2 [concrete = Core.IntLiteral] // CHECK:STDOUT: } // CHECK:STDOUT: %N.loc7_6.2: Core.IntLiteral = symbolic_binding N, 0 [symbolic = %N.loc7_6.1 (constants.%N)] // CHECK:STDOUT: %a.param: @F.%array_type.loc7_49.1 (%array_type.c79) = value_param call_param0 // CHECK:STDOUT: %.loc7_49: type = splice_block %array_type.loc7_49.2 [symbolic = %array_type.loc7_49.1 (constants.%array_type.c79)] { // CHECK:STDOUT: %C.ref: type = name_ref C, file.%C.decl [concrete = constants.%C] // CHECK:STDOUT: %N.ref.loc7_48: Core.IntLiteral = name_ref N, %N.loc7_6.2 [symbolic = %N.loc7_6.1 (constants.%N)] // CHECK:STDOUT: %array_type.loc7_49.2: type = array_type %N.ref.loc7_48, %C.ref [symbolic = %array_type.loc7_49.1 (constants.%array_type.c79)] // CHECK:STDOUT: } // CHECK:STDOUT: %a: @F.%array_type.loc7_49.1 (%array_type.c79) = value_binding a, %a.param // CHECK:STDOUT: %return.param: ref %i32 = out_param call_param1 // CHECK:STDOUT: %return: ref %i32 = return_slot %return.param // CHECK:STDOUT: } // CHECK:STDOUT: %G.decl: %G.type = fn_decl @G [concrete = constants.%G] { // CHECK:STDOUT: %return.patt: %pattern_type.7ce = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: %pattern_type.7ce = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %i32: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %.loc9: Core.Form = init_form %i32 [concrete = constants.%.ff5] // CHECK:STDOUT: %return.param: ref %i32 = out_param call_param0 // CHECK:STDOUT: %return: ref %i32 = return_slot %return.param // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @C { // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type.357] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%C // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @D { // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type.357] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%D // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic fn @F(%N.loc7_6.2: Core.IntLiteral) { // CHECK:STDOUT: %N.loc7_6.1: Core.IntLiteral = symbolic_binding N, 0 [symbolic = %N.loc7_6.1 (constants.%N)] // CHECK:STDOUT: %array_type.loc7_49.1: type = array_type %N.loc7_6.1, constants.%C [symbolic = %array_type.loc7_49.1 (constants.%array_type.c79)] // CHECK:STDOUT: %pattern_type: type = pattern_type %array_type.loc7_49.1 [symbolic = %pattern_type (constants.%pattern_type.cc9)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete: = require_complete_type %array_type.loc7_49.1 [symbolic = %require_complete (constants.%require_complete.d0d)] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.bound: = bound_method %N.loc7_6.1, constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5 [symbolic = %Core.IntLiteral.as.ImplicitAs.impl.Convert.bound (constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.bound.d6e)] // CHECK:STDOUT: %bound_method.loc7_69.3: = bound_method %N.loc7_6.1, constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn [symbolic = %bound_method.loc7_69.3 (constants.%bound_method.7fa)] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc7_69.2: init %i32 = call %bound_method.loc7_69.3(%N.loc7_6.1) [symbolic = %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc7_69.2 (constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.call)] // CHECK:STDOUT: // CHECK:STDOUT: fn(%a.param: @F.%array_type.loc7_49.1 (%array_type.c79)) -> out %return.param: %i32 { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %N.ref.loc7_68: Core.IntLiteral = name_ref N, %N.loc7_6.2 [symbolic = %N.loc7_6.1 (constants.%N)] // CHECK:STDOUT: %impl.elem0: %.545 = impl_witness_access constants.%ImplicitAs.impl_witness.6bc, element0 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5] // CHECK:STDOUT: %bound_method.loc7_69.1: = bound_method %N.ref.loc7_68, %impl.elem0 [symbolic = %Core.IntLiteral.as.ImplicitAs.impl.Convert.bound (constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.bound.d6e)] // CHECK:STDOUT: %specific_fn: = specific_function %impl.elem0, @Core.IntLiteral.as.ImplicitAs.impl.Convert(constants.%int_32) [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn] // CHECK:STDOUT: %bound_method.loc7_69.2: = bound_method %N.ref.loc7_68, %specific_fn [symbolic = %bound_method.loc7_69.3 (constants.%bound_method.7fa)] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc7_69.1: init %i32 = call %bound_method.loc7_69.2(%N.ref.loc7_68) [symbolic = %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc7_69.2 (constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.call)] // CHECK:STDOUT: %.loc7_69: init %i32 = converted %N.ref.loc7_68, %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc7_69.1 [symbolic = %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc7_69.2 (constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.call)] // CHECK:STDOUT: return %.loc7_69 // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @G() -> out %return.param: %i32 { // CHECK:STDOUT: !entry: // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %a.patt: %pattern_type.454 = ref_binding_pattern a [concrete] // CHECK:STDOUT: %a.var_patt: %pattern_type.454 = var_pattern %a.patt [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %a.var: ref %array_type.b6d = var %a.var_patt // CHECK:STDOUT: %.loc11_26.1: %empty_struct_type = struct_literal () [concrete = constants.%empty_struct] // CHECK:STDOUT: %.loc11_30.1: %empty_struct_type = struct_literal () [concrete = constants.%empty_struct] // CHECK:STDOUT: %.loc11_34.1: %empty_struct_type = struct_literal () [concrete = constants.%empty_struct] // CHECK:STDOUT: %.loc11_35.1: %tuple.type.8d4 = tuple_literal (%.loc11_26.1, %.loc11_30.1, %.loc11_34.1) [concrete = constants.%tuple] // CHECK:STDOUT: %int_0: Core.IntLiteral = int_value 0 [concrete = constants.%int_0] // CHECK:STDOUT: %.loc11_35.2: ref %D = array_index %a.var, %int_0 // CHECK:STDOUT: %.loc11_26.2: init %D to %.loc11_35.2 = class_init () [concrete = constants.%D.val] // CHECK:STDOUT: %.loc11_35.3: init %D = converted %.loc11_26.1, %.loc11_26.2 [concrete = constants.%D.val] // CHECK:STDOUT: %int_1: Core.IntLiteral = int_value 1 [concrete = constants.%int_1] // CHECK:STDOUT: %.loc11_35.4: ref %D = array_index %a.var, %int_1 // CHECK:STDOUT: %.loc11_30.2: init %D to %.loc11_35.4 = class_init () [concrete = constants.%D.val] // CHECK:STDOUT: %.loc11_35.5: init %D = converted %.loc11_30.1, %.loc11_30.2 [concrete = constants.%D.val] // CHECK:STDOUT: %int_2: Core.IntLiteral = int_value 2 [concrete = constants.%int_2] // CHECK:STDOUT: %.loc11_35.6: ref %D = array_index %a.var, %int_2 // CHECK:STDOUT: %.loc11_34.2: init %D to %.loc11_35.6 = class_init () [concrete = constants.%D.val] // CHECK:STDOUT: %.loc11_35.7: init %D = converted %.loc11_34.1, %.loc11_34.2 [concrete = constants.%D.val] // CHECK:STDOUT: %.loc11_35.8: init %array_type.b6d to %a.var = array_init (%.loc11_35.3, %.loc11_35.5, %.loc11_35.7) [concrete = constants.%array] // CHECK:STDOUT: %.loc11_3: init %array_type.b6d = converted %.loc11_35.1, %.loc11_35.8 [concrete = constants.%array] // CHECK:STDOUT: assign %a.var, %.loc11_3 // CHECK:STDOUT: %.loc11_20: type = splice_block %array_type [concrete = constants.%array_type.b6d] { // CHECK:STDOUT: %D.ref: type = name_ref D, file.%D.decl [concrete = constants.%D] // CHECK:STDOUT: %int_3: Core.IntLiteral = int_value 3 [concrete = constants.%int_3.1ba] // CHECK:STDOUT: %array_type: type = array_type %int_3, %D.ref [concrete = constants.%array_type.b6d] // CHECK:STDOUT: } // CHECK:STDOUT: %a: ref %array_type.b6d = ref_binding a, %a.var // CHECK:STDOUT: %F.ref: %F.type = name_ref F, file.%F.decl [concrete = constants.%F] // CHECK:STDOUT: %a.ref: ref %array_type.b6d = name_ref a, %a // CHECK:STDOUT: %F.specific_fn: = specific_function %F.ref, @F(constants.%int_3.1ba) [concrete = constants.%F.specific_fn] // CHECK:STDOUT: %.loc22: %array_type.931 = converted %a.ref, [concrete = ] // CHECK:STDOUT: %F.call: init %i32 = call %F.specific_fn() // CHECK:STDOUT: %Destroy.Op.bound: = bound_method %a.var, constants.%Destroy.Op // CHECK:STDOUT: %Destroy.Op.call: init %empty_tuple.type = call %Destroy.Op.bound(%a.var) // CHECK:STDOUT: return %F.call // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @Destroy.Op(%self.param: ref %array_type.b6d) = "no_op"; // CHECK:STDOUT: // CHECK:STDOUT: specific @F(constants.%N) { // CHECK:STDOUT: %N.loc7_6.1 => constants.%N // CHECK:STDOUT: %array_type.loc7_49.1 => constants.%array_type.c79 // CHECK:STDOUT: %pattern_type => constants.%pattern_type.cc9 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @F(constants.%int_3.1ba) { // CHECK:STDOUT: %N.loc7_6.1 => constants.%int_3.1ba // CHECK:STDOUT: %array_type.loc7_49.1 => constants.%array_type.931 // CHECK:STDOUT: %pattern_type => constants.%pattern_type.f21 // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete => constants.%complete_type.c7a // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.bound => constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.bound.061 // CHECK:STDOUT: %bound_method.loc7_69.3 => constants.%bound_method.fa7 // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc7_69.2 => constants.%int_3.822 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- fail_bound_type_mismatch.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %C: type = class_type @C [concrete] // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %complete_type.357: = complete_type_witness %empty_struct_type [concrete] // CHECK:STDOUT: %type: type = facet_type [concrete] // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self] // CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [concrete] // CHECK:STDOUT: %Int.type: type = generic_class_type @Int [concrete] // CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] // CHECK:STDOUT: %Int.generic: %Int.type = struct_value () [concrete] // CHECK:STDOUT: %N.fe9: Core.IntLiteral = symbolic_binding N, 0 [symbolic] // CHECK:STDOUT: %i32: type = class_type @Int, @Int(%int_32) [concrete] // CHECK:STDOUT: %pattern_type.7ce: type = pattern_type %i32 [concrete] // CHECK:STDOUT: %N.5de: %i32 = symbolic_binding N, 0 [symbolic] // CHECK:STDOUT: %ImplicitAs.type.cc7: type = generic_interface_type @ImplicitAs [concrete] // CHECK:STDOUT: %ImplicitAs.generic: %ImplicitAs.type.cc7 = struct_value () [concrete] // CHECK:STDOUT: %ImplicitAs.type.139: type = facet_type <@ImplicitAs, @ImplicitAs(Core.IntLiteral)> [concrete] // CHECK:STDOUT: %From: Core.IntLiteral = symbolic_binding From, 0 [symbolic] // CHECK:STDOUT: %Int.as.ImplicitAs.impl.Convert.type.2ed: type = fn_type @Int.as.ImplicitAs.impl.Convert, @Int.as.ImplicitAs.impl(%From) [symbolic] // CHECK:STDOUT: %Int.as.ImplicitAs.impl.Convert.d29: %Int.as.ImplicitAs.impl.Convert.type.2ed = struct_value () [symbolic] // CHECK:STDOUT: %ImplicitAs.impl_witness.640: = impl_witness imports.%ImplicitAs.impl_witness_table.ea2, @Int.as.ImplicitAs.impl(%int_32) [concrete] // CHECK:STDOUT: %Int.as.ImplicitAs.impl.Convert.type.240: type = fn_type @Int.as.ImplicitAs.impl.Convert, @Int.as.ImplicitAs.impl(%int_32) [concrete] // CHECK:STDOUT: %Int.as.ImplicitAs.impl.Convert.dd4: %Int.as.ImplicitAs.impl.Convert.type.240 = struct_value () [concrete] // CHECK:STDOUT: %ImplicitAs.facet: %ImplicitAs.type.139 = facet_value %i32, (%ImplicitAs.impl_witness.640) [concrete] // CHECK:STDOUT: %ImplicitAs.WithSelf.Convert.type.462: type = fn_type @ImplicitAs.WithSelf.Convert, @ImplicitAs.WithSelf(Core.IntLiteral, %ImplicitAs.facet) [concrete] // CHECK:STDOUT: %.0a7: type = fn_type_with_self_type %ImplicitAs.WithSelf.Convert.type.462, %ImplicitAs.facet [concrete] // CHECK:STDOUT: %Int.as.ImplicitAs.impl.Convert.bound: = bound_method %N.5de, %Int.as.ImplicitAs.impl.Convert.dd4 [symbolic] // CHECK:STDOUT: %Int.as.ImplicitAs.impl.Convert.specific_fn: = specific_function %Int.as.ImplicitAs.impl.Convert.dd4, @Int.as.ImplicitAs.impl.Convert(%int_32) [concrete] // CHECK:STDOUT: %bound_method.d76: = bound_method %N.5de, %Int.as.ImplicitAs.impl.Convert.specific_fn [symbolic] // CHECK:STDOUT: %Int.as.ImplicitAs.impl.Convert.call: init Core.IntLiteral = call %bound_method.d76(%N.5de) [symbolic] // CHECK:STDOUT: %array_type.8c3: type = array_type %Int.as.ImplicitAs.impl.Convert.call, %C [symbolic] // CHECK:STDOUT: %pattern_type.0fb: type = pattern_type %array_type.8c3 [symbolic] // CHECK:STDOUT: %.ff5: Core.Form = init_form %i32 [concrete] // CHECK:STDOUT: %F.type: type = fn_type @F [concrete] // CHECK:STDOUT: %F: %F.type = struct_value () [concrete] // CHECK:STDOUT: %require_complete.a24: = require_complete_type %array_type.8c3 [symbolic] // CHECK:STDOUT: %Copy.type: type = facet_type <@Copy> [concrete] // CHECK:STDOUT: %Int.as.Copy.impl.Op.type.824: type = fn_type @Int.as.Copy.impl.Op, @Int.as.Copy.impl(%N.fe9) [symbolic] // CHECK:STDOUT: %Int.as.Copy.impl.Op.9b9: %Int.as.Copy.impl.Op.type.824 = struct_value () [symbolic] // CHECK:STDOUT: %Copy.impl_witness.f17: = impl_witness imports.%Copy.impl_witness_table.e76, @Int.as.Copy.impl(%int_32) [concrete] // CHECK:STDOUT: %Int.as.Copy.impl.Op.type.546: type = fn_type @Int.as.Copy.impl.Op, @Int.as.Copy.impl(%int_32) [concrete] // CHECK:STDOUT: %Int.as.Copy.impl.Op.664: %Int.as.Copy.impl.Op.type.546 = struct_value () [concrete] // CHECK:STDOUT: %Copy.facet: %Copy.type = facet_value %i32, (%Copy.impl_witness.f17) [concrete] // CHECK:STDOUT: %Copy.WithSelf.Op.type.081: type = fn_type @Copy.WithSelf.Op, @Copy.WithSelf(%Copy.facet) [concrete] // CHECK:STDOUT: %.8e2: type = fn_type_with_self_type %Copy.WithSelf.Op.type.081, %Copy.facet [concrete] // CHECK:STDOUT: %Int.as.Copy.impl.Op.bound: = bound_method %N.5de, %Int.as.Copy.impl.Op.664 [symbolic] // CHECK:STDOUT: %Int.as.Copy.impl.Op.specific_fn: = specific_function %Int.as.Copy.impl.Op.664, @Int.as.Copy.impl.Op(%int_32) [concrete] // CHECK:STDOUT: %bound_method.207: = bound_method %N.5de, %Int.as.Copy.impl.Op.specific_fn [symbolic] // CHECK:STDOUT: %G.type: type = fn_type @G [concrete] // CHECK:STDOUT: %G: %G.type = struct_value () [concrete] // CHECK:STDOUT: %int_3: Core.IntLiteral = int_value 3 [concrete] // CHECK:STDOUT: %array_type.931: type = array_type %int_3, %C [concrete] // CHECK:STDOUT: %pattern_type.f21: type = pattern_type %array_type.931 [concrete] // CHECK:STDOUT: %empty_struct: %empty_struct_type = struct_value () [concrete] // CHECK:STDOUT: %tuple.type.8d4: type = tuple_type (%empty_struct_type, %empty_struct_type, %empty_struct_type) [concrete] // CHECK:STDOUT: %tuple.5c1: %tuple.type.8d4 = tuple_value (%empty_struct, %empty_struct, %empty_struct) [concrete] // CHECK:STDOUT: %int_0: Core.IntLiteral = int_value 0 [concrete] // CHECK:STDOUT: %C.val: %C = struct_value () [concrete] // CHECK:STDOUT: %int_1: Core.IntLiteral = int_value 1 [concrete] // CHECK:STDOUT: %int_2: Core.IntLiteral = int_value 2 [concrete] // CHECK:STDOUT: %array: %array_type.931 = tuple_value (%C.val, %C.val, %C.val) [concrete] // CHECK:STDOUT: %Destroy.type: type = facet_type <@Destroy> [concrete] // CHECK:STDOUT: %Destroy.Op.type: type = fn_type @Destroy.Op [concrete] // CHECK:STDOUT: %Destroy.Op: %Destroy.Op.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: .Int = %Core.Int // CHECK:STDOUT: .ImplicitAs = %Core.ImplicitAs // CHECK:STDOUT: .Copy = %Core.Copy // CHECK:STDOUT: .Destroy = %Core.Destroy // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: %Core.Int: %Int.type = import_ref Core//prelude/parts/int, Int, loaded [concrete = constants.%Int.generic] // CHECK:STDOUT: %Core.ImplicitAs: %ImplicitAs.type.cc7 = import_ref Core//prelude/parts/as, ImplicitAs, loaded [concrete = constants.%ImplicitAs.generic] // CHECK:STDOUT: %Core.import_ref.0bc: @Int.as.ImplicitAs.impl.%Int.as.ImplicitAs.impl.Convert.type (%Int.as.ImplicitAs.impl.Convert.type.2ed) = import_ref Core//prelude/parts/int, loc{{\d+_\d+}}, loaded [symbolic = @Int.as.ImplicitAs.impl.%Int.as.ImplicitAs.impl.Convert (constants.%Int.as.ImplicitAs.impl.Convert.d29)] // CHECK:STDOUT: %ImplicitAs.impl_witness_table.ea2 = impl_witness_table (%Core.import_ref.0bc), @Int.as.ImplicitAs.impl [concrete] // CHECK:STDOUT: %Core.Copy: type = import_ref Core//prelude/parts/copy, Copy, loaded [concrete = constants.%Copy.type] // CHECK:STDOUT: %Core.import_ref.18d: @Int.as.Copy.impl.%Int.as.Copy.impl.Op.type (%Int.as.Copy.impl.Op.type.824) = import_ref Core//prelude/parts/int, loc{{\d+_\d+}}, loaded [symbolic = @Int.as.Copy.impl.%Int.as.Copy.impl.Op (constants.%Int.as.Copy.impl.Op.9b9)] // CHECK:STDOUT: %Copy.impl_witness_table.e76 = impl_witness_table (%Core.import_ref.18d), @Int.as.Copy.impl [concrete] // CHECK:STDOUT: %Core.Destroy: type = import_ref Core//prelude/parts/destroy, Destroy, loaded [concrete = constants.%Destroy.type] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .C = %C.decl // CHECK:STDOUT: .F = %F.decl // CHECK:STDOUT: .G = %G.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %C.decl: type = class_decl @C [concrete = constants.%C] {} {} // CHECK:STDOUT: %F.decl: %F.type = fn_decl @F [concrete = constants.%F] { // CHECK:STDOUT: %N.patt: %pattern_type.7ce = symbolic_binding_pattern N, 0 [concrete] // CHECK:STDOUT: %a.patt: @F.%pattern_type (%pattern_type.0fb) = value_binding_pattern a [concrete] // CHECK:STDOUT: %a.param_patt: @F.%pattern_type (%pattern_type.0fb) = value_param_pattern %a.patt [concrete] // CHECK:STDOUT: %return.patt: %pattern_type.7ce = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: %pattern_type.7ce = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %i32.loc6_41: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %.loc6_41: Core.Form = init_form %i32.loc6_41 [concrete = constants.%.ff5] // CHECK:STDOUT: %.loc6_10: type = splice_block %i32.loc6_10 [concrete = constants.%i32] { // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %i32.loc6_10: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: } // CHECK:STDOUT: %N.loc6_6.2: %i32 = symbolic_binding N, 0 [symbolic = %N.loc6_6.1 (constants.%N.5de)] // CHECK:STDOUT: %a.param: @F.%array_type.loc6_35.1 (%array_type.8c3) = value_param call_param0 // CHECK:STDOUT: %.loc6_35: type = splice_block %array_type.loc6_35.2 [symbolic = %array_type.loc6_35.1 (constants.%array_type.8c3)] { // CHECK:STDOUT: %C.ref: type = name_ref C, file.%C.decl [concrete = constants.%C] // CHECK:STDOUT: %N.ref.loc6_34: %i32 = name_ref N, %N.loc6_6.2 [symbolic = %N.loc6_6.1 (constants.%N.5de)] // CHECK:STDOUT: %impl.elem0.loc6_34: %.0a7 = impl_witness_access constants.%ImplicitAs.impl_witness.640, element0 [concrete = constants.%Int.as.ImplicitAs.impl.Convert.dd4] // CHECK:STDOUT: %bound_method.loc6_34.2: = bound_method %N.ref.loc6_34, %impl.elem0.loc6_34 [symbolic = %Int.as.ImplicitAs.impl.Convert.bound (constants.%Int.as.ImplicitAs.impl.Convert.bound)] // CHECK:STDOUT: %specific_fn.loc6_34: = specific_function %impl.elem0.loc6_34, @Int.as.ImplicitAs.impl.Convert(constants.%int_32) [concrete = constants.%Int.as.ImplicitAs.impl.Convert.specific_fn] // CHECK:STDOUT: %bound_method.loc6_34.3: = bound_method %N.ref.loc6_34, %specific_fn.loc6_34 [symbolic = %bound_method.loc6_34.1 (constants.%bound_method.d76)] // CHECK:STDOUT: %Int.as.ImplicitAs.impl.Convert.call.loc6_34.2: init Core.IntLiteral = call %bound_method.loc6_34.3(%N.ref.loc6_34) [symbolic = %Int.as.ImplicitAs.impl.Convert.call.loc6_34.1 (constants.%Int.as.ImplicitAs.impl.Convert.call)] // CHECK:STDOUT: %.loc6_34.1: Core.IntLiteral = value_of_initializer %Int.as.ImplicitAs.impl.Convert.call.loc6_34.2 [symbolic = %Int.as.ImplicitAs.impl.Convert.call.loc6_34.1 (constants.%Int.as.ImplicitAs.impl.Convert.call)] // CHECK:STDOUT: %.loc6_34.2: Core.IntLiteral = converted %N.ref.loc6_34, %.loc6_34.1 [symbolic = %Int.as.ImplicitAs.impl.Convert.call.loc6_34.1 (constants.%Int.as.ImplicitAs.impl.Convert.call)] // CHECK:STDOUT: %array_type.loc6_35.2: type = array_type %.loc6_34.2, %C.ref [symbolic = %array_type.loc6_35.1 (constants.%array_type.8c3)] // CHECK:STDOUT: } // CHECK:STDOUT: %a: @F.%array_type.loc6_35.1 (%array_type.8c3) = value_binding a, %a.param // CHECK:STDOUT: %return.param: ref %i32 = out_param call_param1 // CHECK:STDOUT: %return: ref %i32 = return_slot %return.param // CHECK:STDOUT: } // CHECK:STDOUT: %G.decl: %G.type = fn_decl @G [concrete = constants.%G] { // CHECK:STDOUT: %return.patt: %pattern_type.7ce = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: %pattern_type.7ce = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %i32: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %.loc8: Core.Form = init_form %i32 [concrete = constants.%.ff5] // CHECK:STDOUT: %return.param: ref %i32 = out_param call_param0 // CHECK:STDOUT: %return: ref %i32 = return_slot %return.param // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @C { // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type.357] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%C // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic fn @F(%N.loc6_6.2: %i32) { // CHECK:STDOUT: %N.loc6_6.1: %i32 = symbolic_binding N, 0 [symbolic = %N.loc6_6.1 (constants.%N.5de)] // CHECK:STDOUT: %Int.as.ImplicitAs.impl.Convert.bound: = bound_method %N.loc6_6.1, constants.%Int.as.ImplicitAs.impl.Convert.dd4 [symbolic = %Int.as.ImplicitAs.impl.Convert.bound (constants.%Int.as.ImplicitAs.impl.Convert.bound)] // CHECK:STDOUT: %bound_method.loc6_34.1: = bound_method %N.loc6_6.1, constants.%Int.as.ImplicitAs.impl.Convert.specific_fn [symbolic = %bound_method.loc6_34.1 (constants.%bound_method.d76)] // CHECK:STDOUT: %Int.as.ImplicitAs.impl.Convert.call.loc6_34.1: init Core.IntLiteral = call %bound_method.loc6_34.1(%N.loc6_6.1) [symbolic = %Int.as.ImplicitAs.impl.Convert.call.loc6_34.1 (constants.%Int.as.ImplicitAs.impl.Convert.call)] // CHECK:STDOUT: %array_type.loc6_35.1: type = array_type %Int.as.ImplicitAs.impl.Convert.call.loc6_34.1, constants.%C [symbolic = %array_type.loc6_35.1 (constants.%array_type.8c3)] // CHECK:STDOUT: %pattern_type: type = pattern_type %array_type.loc6_35.1 [symbolic = %pattern_type (constants.%pattern_type.0fb)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete: = require_complete_type %array_type.loc6_35.1 [symbolic = %require_complete (constants.%require_complete.a24)] // CHECK:STDOUT: %Int.as.Copy.impl.Op.bound: = bound_method %N.loc6_6.1, constants.%Int.as.Copy.impl.Op.664 [symbolic = %Int.as.Copy.impl.Op.bound (constants.%Int.as.Copy.impl.Op.bound)] // CHECK:STDOUT: %bound_method.loc6_54.3: = bound_method %N.loc6_6.1, constants.%Int.as.Copy.impl.Op.specific_fn [symbolic = %bound_method.loc6_54.3 (constants.%bound_method.207)] // CHECK:STDOUT: // CHECK:STDOUT: fn(%a.param: @F.%array_type.loc6_35.1 (%array_type.8c3)) -> out %return.param: %i32 { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %N.ref.loc6_54: %i32 = name_ref N, %N.loc6_6.2 [symbolic = %N.loc6_6.1 (constants.%N.5de)] // CHECK:STDOUT: %impl.elem0.loc6_54: %.8e2 = impl_witness_access constants.%Copy.impl_witness.f17, element0 [concrete = constants.%Int.as.Copy.impl.Op.664] // CHECK:STDOUT: %bound_method.loc6_54.1: = bound_method %N.ref.loc6_54, %impl.elem0.loc6_54 [symbolic = %Int.as.Copy.impl.Op.bound (constants.%Int.as.Copy.impl.Op.bound)] // CHECK:STDOUT: %specific_fn.loc6_54: = specific_function %impl.elem0.loc6_54, @Int.as.Copy.impl.Op(constants.%int_32) [concrete = constants.%Int.as.Copy.impl.Op.specific_fn] // CHECK:STDOUT: %bound_method.loc6_54.2: = bound_method %N.ref.loc6_54, %specific_fn.loc6_54 [symbolic = %bound_method.loc6_54.3 (constants.%bound_method.207)] // CHECK:STDOUT: %Int.as.Copy.impl.Op.call: init %i32 = call %bound_method.loc6_54.2(%N.ref.loc6_54) [symbolic = %N.loc6_6.1 (constants.%N.5de)] // CHECK:STDOUT: return %Int.as.Copy.impl.Op.call // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @G() -> out %return.param: %i32 { // CHECK:STDOUT: !entry: // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %a.patt: %pattern_type.f21 = ref_binding_pattern a [concrete] // CHECK:STDOUT: %a.var_patt: %pattern_type.f21 = var_pattern %a.patt [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %a.var: ref %array_type.931 = var %a.var_patt // CHECK:STDOUT: %.loc9_26.1: %empty_struct_type = struct_literal () [concrete = constants.%empty_struct] // CHECK:STDOUT: %.loc9_30.1: %empty_struct_type = struct_literal () [concrete = constants.%empty_struct] // CHECK:STDOUT: %.loc9_34.1: %empty_struct_type = struct_literal () [concrete = constants.%empty_struct] // CHECK:STDOUT: %.loc9_35.1: %tuple.type.8d4 = tuple_literal (%.loc9_26.1, %.loc9_30.1, %.loc9_34.1) [concrete = constants.%tuple.5c1] // CHECK:STDOUT: %int_0: Core.IntLiteral = int_value 0 [concrete = constants.%int_0] // CHECK:STDOUT: %.loc9_35.2: ref %C = array_index %a.var, %int_0 // CHECK:STDOUT: %.loc9_26.2: init %C to %.loc9_35.2 = class_init () [concrete = constants.%C.val] // CHECK:STDOUT: %.loc9_35.3: init %C = converted %.loc9_26.1, %.loc9_26.2 [concrete = constants.%C.val] // CHECK:STDOUT: %int_1: Core.IntLiteral = int_value 1 [concrete = constants.%int_1] // CHECK:STDOUT: %.loc9_35.4: ref %C = array_index %a.var, %int_1 // CHECK:STDOUT: %.loc9_30.2: init %C to %.loc9_35.4 = class_init () [concrete = constants.%C.val] // CHECK:STDOUT: %.loc9_35.5: init %C = converted %.loc9_30.1, %.loc9_30.2 [concrete = constants.%C.val] // CHECK:STDOUT: %int_2: Core.IntLiteral = int_value 2 [concrete = constants.%int_2] // CHECK:STDOUT: %.loc9_35.6: ref %C = array_index %a.var, %int_2 // CHECK:STDOUT: %.loc9_34.2: init %C to %.loc9_35.6 = class_init () [concrete = constants.%C.val] // CHECK:STDOUT: %.loc9_35.7: init %C = converted %.loc9_34.1, %.loc9_34.2 [concrete = constants.%C.val] // CHECK:STDOUT: %.loc9_35.8: init %array_type.931 to %a.var = array_init (%.loc9_35.3, %.loc9_35.5, %.loc9_35.7) [concrete = constants.%array] // CHECK:STDOUT: %.loc9_3: init %array_type.931 = converted %.loc9_35.1, %.loc9_35.8 [concrete = constants.%array] // CHECK:STDOUT: assign %a.var, %.loc9_3 // CHECK:STDOUT: %.loc9_20: type = splice_block %array_type [concrete = constants.%array_type.931] { // CHECK:STDOUT: %C.ref: type = name_ref C, file.%C.decl [concrete = constants.%C] // CHECK:STDOUT: %int_3: Core.IntLiteral = int_value 3 [concrete = constants.%int_3] // CHECK:STDOUT: %array_type: type = array_type %int_3, %C.ref [concrete = constants.%array_type.931] // CHECK:STDOUT: } // CHECK:STDOUT: %a: ref %array_type.931 = ref_binding a, %a.var // CHECK:STDOUT: %F.ref: %F.type = name_ref F, file.%F.decl [concrete = constants.%F] // CHECK:STDOUT: %a.ref: ref %array_type.931 = name_ref a, %a // CHECK:STDOUT: %Destroy.Op.bound: = bound_method %a.var, constants.%Destroy.Op // CHECK:STDOUT: %Destroy.Op.call: init %empty_tuple.type = call %Destroy.Op.bound(%a.var) // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @Destroy.Op(%self.param: ref %array_type.931) = "no_op"; // CHECK:STDOUT: // CHECK:STDOUT: specific @F(constants.%N.5de) { // CHECK:STDOUT: %N.loc6_6.1 => constants.%N.5de // CHECK:STDOUT: %Int.as.ImplicitAs.impl.Convert.bound => constants.%Int.as.ImplicitAs.impl.Convert.bound // CHECK:STDOUT: %bound_method.loc6_34.1 => constants.%bound_method.d76 // CHECK:STDOUT: %Int.as.ImplicitAs.impl.Convert.call.loc6_34.1 => constants.%Int.as.ImplicitAs.impl.Convert.call // CHECK:STDOUT: %array_type.loc6_35.1 => constants.%array_type.8c3 // CHECK:STDOUT: %pattern_type => constants.%pattern_type.0fb // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- fail_todo_array_length_from_tuple.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %C: type = class_type @C [concrete] // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %complete_type.357: = complete_type_witness %empty_struct_type [concrete] // CHECK:STDOUT: %type: type = facet_type [concrete] // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self] // CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [concrete] // CHECK:STDOUT: %Int.type: type = generic_class_type @Int [concrete] // CHECK:STDOUT: %Int.generic: %Int.type = struct_value () [concrete] // CHECK:STDOUT: %i32: type = class_type @Int, @Int(%int_32) [concrete] // CHECK:STDOUT: %pattern_type.7ce: type = pattern_type %i32 [concrete] // CHECK:STDOUT: %N.5de: %i32 = symbolic_binding N, 0 [symbolic] // CHECK:STDOUT: %ImplicitAs.type.cc7: type = generic_interface_type @ImplicitAs [concrete] // CHECK:STDOUT: %ImplicitAs.generic: %ImplicitAs.type.cc7 = struct_value () [concrete] // CHECK:STDOUT: %ImplicitAs.type.139: type = facet_type <@ImplicitAs, @ImplicitAs(Core.IntLiteral)> [concrete] // CHECK:STDOUT: %From: Core.IntLiteral = symbolic_binding From, 0 [symbolic] // CHECK:STDOUT: %Int.as.ImplicitAs.impl.Convert.type.2ed: type = fn_type @Int.as.ImplicitAs.impl.Convert, @Int.as.ImplicitAs.impl(%From) [symbolic] // CHECK:STDOUT: %Int.as.ImplicitAs.impl.Convert.d29: %Int.as.ImplicitAs.impl.Convert.type.2ed = struct_value () [symbolic] // CHECK:STDOUT: %ImplicitAs.impl_witness.640: = impl_witness imports.%ImplicitAs.impl_witness_table.ea2, @Int.as.ImplicitAs.impl(%int_32) [concrete] // CHECK:STDOUT: %Int.as.ImplicitAs.impl.Convert.type.240: type = fn_type @Int.as.ImplicitAs.impl.Convert, @Int.as.ImplicitAs.impl(%int_32) [concrete] // CHECK:STDOUT: %Int.as.ImplicitAs.impl.Convert.dd4: %Int.as.ImplicitAs.impl.Convert.type.240 = struct_value () [concrete] // CHECK:STDOUT: %ImplicitAs.facet: %ImplicitAs.type.139 = facet_value %i32, (%ImplicitAs.impl_witness.640) [concrete] // CHECK:STDOUT: %ImplicitAs.WithSelf.Convert.type.462: type = fn_type @ImplicitAs.WithSelf.Convert, @ImplicitAs.WithSelf(Core.IntLiteral, %ImplicitAs.facet) [concrete] // CHECK:STDOUT: %.0a7: type = fn_type_with_self_type %ImplicitAs.WithSelf.Convert.type.462, %ImplicitAs.facet [concrete] // CHECK:STDOUT: %Int.as.ImplicitAs.impl.Convert.bound: = bound_method %N.5de, %Int.as.ImplicitAs.impl.Convert.dd4 [symbolic] // CHECK:STDOUT: %Int.as.ImplicitAs.impl.Convert.specific_fn: = specific_function %Int.as.ImplicitAs.impl.Convert.dd4, @Int.as.ImplicitAs.impl.Convert(%int_32) [concrete] // CHECK:STDOUT: %bound_method: = bound_method %N.5de, %Int.as.ImplicitAs.impl.Convert.specific_fn [symbolic] // CHECK:STDOUT: %Int.as.ImplicitAs.impl.Convert.call: init Core.IntLiteral = call %bound_method(%N.5de) [symbolic] // CHECK:STDOUT: %array_type: type = array_type %Int.as.ImplicitAs.impl.Convert.call, %C [symbolic] // CHECK:STDOUT: %pattern_type.0fb: type = pattern_type %array_type [symbolic] // CHECK:STDOUT: %F.type: type = fn_type @F [concrete] // CHECK:STDOUT: %F: %F.type = struct_value () [concrete] // CHECK:STDOUT: %require_complete.a24: = require_complete_type %array_type [symbolic] // CHECK:STDOUT: %G.type: type = fn_type @G [concrete] // CHECK:STDOUT: %G: %G.type = struct_value () [concrete] // CHECK:STDOUT: %empty_struct: %empty_struct_type = struct_value () [concrete] // CHECK:STDOUT: %tuple.type: type = tuple_type (%empty_struct_type, %empty_struct_type, %empty_struct_type) [concrete] // CHECK:STDOUT: %tuple: %tuple.type = tuple_value (%empty_struct, %empty_struct, %empty_struct) [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: .Int = %Core.Int // CHECK:STDOUT: .ImplicitAs = %Core.ImplicitAs // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: %Core.Int: %Int.type = import_ref Core//prelude/parts/int, Int, loaded [concrete = constants.%Int.generic] // CHECK:STDOUT: %Core.ImplicitAs: %ImplicitAs.type.cc7 = import_ref Core//prelude/parts/as, ImplicitAs, loaded [concrete = constants.%ImplicitAs.generic] // CHECK:STDOUT: %Core.import_ref.0bc: @Int.as.ImplicitAs.impl.%Int.as.ImplicitAs.impl.Convert.type (%Int.as.ImplicitAs.impl.Convert.type.2ed) = import_ref Core//prelude/parts/int, loc{{\d+_\d+}}, loaded [symbolic = @Int.as.ImplicitAs.impl.%Int.as.ImplicitAs.impl.Convert (constants.%Int.as.ImplicitAs.impl.Convert.d29)] // CHECK:STDOUT: %ImplicitAs.impl_witness_table.ea2 = impl_witness_table (%Core.import_ref.0bc), @Int.as.ImplicitAs.impl [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .C = %C.decl // CHECK:STDOUT: .F = %F.decl // CHECK:STDOUT: .G = %G.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %C.decl: type = class_decl @C [concrete = constants.%C] {} {} // CHECK:STDOUT: %F.decl: %F.type = fn_decl @F [concrete = constants.%F] { // CHECK:STDOUT: %N.patt: %pattern_type.7ce = symbolic_binding_pattern N, 0 [concrete] // CHECK:STDOUT: %a.patt: @F.%pattern_type (%pattern_type.0fb) = value_binding_pattern a [concrete] // CHECK:STDOUT: %a.param_patt: @F.%pattern_type (%pattern_type.0fb) = value_param_pattern %a.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %.loc5_10: type = splice_block %i32 [concrete = constants.%i32] { // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %i32: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: } // CHECK:STDOUT: %N.loc5_6.2: %i32 = symbolic_binding N, 0 [symbolic = %N.loc5_6.1 (constants.%N.5de)] // CHECK:STDOUT: %a.param: @F.%array_type.loc5_35.1 (%array_type) = value_param call_param0 // CHECK:STDOUT: %.loc5_35: type = splice_block %array_type.loc5_35.2 [symbolic = %array_type.loc5_35.1 (constants.%array_type)] { // CHECK:STDOUT: %C.ref: type = name_ref C, file.%C.decl [concrete = constants.%C] // CHECK:STDOUT: %N.ref: %i32 = name_ref N, %N.loc5_6.2 [symbolic = %N.loc5_6.1 (constants.%N.5de)] // CHECK:STDOUT: %impl.elem0: %.0a7 = impl_witness_access constants.%ImplicitAs.impl_witness.640, element0 [concrete = constants.%Int.as.ImplicitAs.impl.Convert.dd4] // CHECK:STDOUT: %bound_method.loc5_34.2: = bound_method %N.ref, %impl.elem0 [symbolic = %Int.as.ImplicitAs.impl.Convert.bound (constants.%Int.as.ImplicitAs.impl.Convert.bound)] // CHECK:STDOUT: %specific_fn: = specific_function %impl.elem0, @Int.as.ImplicitAs.impl.Convert(constants.%int_32) [concrete = constants.%Int.as.ImplicitAs.impl.Convert.specific_fn] // CHECK:STDOUT: %bound_method.loc5_34.3: = bound_method %N.ref, %specific_fn [symbolic = %bound_method.loc5_34.1 (constants.%bound_method)] // CHECK:STDOUT: %Int.as.ImplicitAs.impl.Convert.call.loc5_34.2: init Core.IntLiteral = call %bound_method.loc5_34.3(%N.ref) [symbolic = %Int.as.ImplicitAs.impl.Convert.call.loc5_34.1 (constants.%Int.as.ImplicitAs.impl.Convert.call)] // CHECK:STDOUT: %.loc5_34.1: Core.IntLiteral = value_of_initializer %Int.as.ImplicitAs.impl.Convert.call.loc5_34.2 [symbolic = %Int.as.ImplicitAs.impl.Convert.call.loc5_34.1 (constants.%Int.as.ImplicitAs.impl.Convert.call)] // CHECK:STDOUT: %.loc5_34.2: Core.IntLiteral = converted %N.ref, %.loc5_34.1 [symbolic = %Int.as.ImplicitAs.impl.Convert.call.loc5_34.1 (constants.%Int.as.ImplicitAs.impl.Convert.call)] // CHECK:STDOUT: %array_type.loc5_35.2: type = array_type %.loc5_34.2, %C.ref [symbolic = %array_type.loc5_35.1 (constants.%array_type)] // CHECK:STDOUT: } // CHECK:STDOUT: %a: @F.%array_type.loc5_35.1 (%array_type) = value_binding a, %a.param // CHECK:STDOUT: } // CHECK:STDOUT: %G.decl: %G.type = fn_decl @G [concrete = constants.%G] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @C { // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type.357] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%C // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic fn @F(%N.loc5_6.2: %i32) { // CHECK:STDOUT: %N.loc5_6.1: %i32 = symbolic_binding N, 0 [symbolic = %N.loc5_6.1 (constants.%N.5de)] // CHECK:STDOUT: %Int.as.ImplicitAs.impl.Convert.bound: = bound_method %N.loc5_6.1, constants.%Int.as.ImplicitAs.impl.Convert.dd4 [symbolic = %Int.as.ImplicitAs.impl.Convert.bound (constants.%Int.as.ImplicitAs.impl.Convert.bound)] // CHECK:STDOUT: %bound_method.loc5_34.1: = bound_method %N.loc5_6.1, constants.%Int.as.ImplicitAs.impl.Convert.specific_fn [symbolic = %bound_method.loc5_34.1 (constants.%bound_method)] // CHECK:STDOUT: %Int.as.ImplicitAs.impl.Convert.call.loc5_34.1: init Core.IntLiteral = call %bound_method.loc5_34.1(%N.loc5_6.1) [symbolic = %Int.as.ImplicitAs.impl.Convert.call.loc5_34.1 (constants.%Int.as.ImplicitAs.impl.Convert.call)] // CHECK:STDOUT: %array_type.loc5_35.1: type = array_type %Int.as.ImplicitAs.impl.Convert.call.loc5_34.1, constants.%C [symbolic = %array_type.loc5_35.1 (constants.%array_type)] // CHECK:STDOUT: %pattern_type: type = pattern_type %array_type.loc5_35.1 [symbolic = %pattern_type (constants.%pattern_type.0fb)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete: = require_complete_type %array_type.loc5_35.1 [symbolic = %require_complete (constants.%require_complete.a24)] // CHECK:STDOUT: // CHECK:STDOUT: fn(%a.param: @F.%array_type.loc5_35.1 (%array_type)) { // CHECK:STDOUT: !entry: // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @G() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %F.ref: %F.type = name_ref F, file.%F.decl [concrete = constants.%F] // CHECK:STDOUT: %.loc17_7: %empty_struct_type = struct_literal () [concrete = constants.%empty_struct] // CHECK:STDOUT: %.loc17_11: %empty_struct_type = struct_literal () [concrete = constants.%empty_struct] // CHECK:STDOUT: %.loc17_15: %empty_struct_type = struct_literal () [concrete = constants.%empty_struct] // CHECK:STDOUT: %.loc17_16: %tuple.type = tuple_literal (%.loc17_7, %.loc17_11, %.loc17_15) [concrete = constants.%tuple] // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @F(constants.%N.5de) { // CHECK:STDOUT: %N.loc5_6.1 => constants.%N.5de // CHECK:STDOUT: %Int.as.ImplicitAs.impl.Convert.bound => constants.%Int.as.ImplicitAs.impl.Convert.bound // CHECK:STDOUT: %bound_method.loc5_34.1 => constants.%bound_method // CHECK:STDOUT: %Int.as.ImplicitAs.impl.Convert.call.loc5_34.1 => constants.%Int.as.ImplicitAs.impl.Convert.call // CHECK:STDOUT: %array_type.loc5_35.1 => constants.%array_type // CHECK:STDOUT: %pattern_type => constants.%pattern_type.0fb // CHECK:STDOUT: } // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/deduce/binding_pattern.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/convert.carbon // TODO: Add ranges and switch to "--dump-sem-ir-ranges=only". // EXTRA-ARGS: --dump-sem-ir-ranges=if-present // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/deduce/binding_pattern.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/deduce/binding_pattern.carbon // --- fail_incompatible_deduce.carbon library "[[@TEST_NAME]]"; class C(T:! type) { fn Create(unused value: T) {} } fn F(unused U:! type, V:! type) { // CHECK:STDERR: fail_incompatible_deduce.carbon:[[@LINE+10]]:15: error: cannot implicitly convert expression of type `{}` to `V` [ConversionFailure] // CHECK:STDERR: C(V).Create({}); // CHECK:STDERR: ^~ // CHECK:STDERR: fail_incompatible_deduce.carbon:[[@LINE+7]]:15: note: type `{}` does not implement interface `Core.ImplicitAs(V)` [MissingImplInMemberAccessInContext] // CHECK:STDERR: C(V).Create({}); // CHECK:STDERR: ^~ // CHECK:STDERR: fail_incompatible_deduce.carbon:[[@LINE-10]]:20: note: initializing function parameter [InCallToFunctionParam] // CHECK:STDERR: fn Create(unused value: T) {} // CHECK:STDERR: ^~~~~~~~ // CHECK:STDERR: C(V).Create({}); } // --- fail_todo_compatible_deduce.carbon library "[[@TEST_NAME]]"; class C(T:! type) { fn Create(unused value: T) {} } // TODO: This `where` should be sufficient to say that `{} as V` works. fn F(unused U:! type, V:! type where {} impls Core.ImplicitAs(.Self)) { // CHECK:STDERR: fail_todo_compatible_deduce.carbon:[[@LINE+10]]:15: error: cannot implicitly convert expression of type `{}` to `V` [ConversionFailure] // CHECK:STDERR: C(V).Create({}); // CHECK:STDERR: ^~ // CHECK:STDERR: fail_todo_compatible_deduce.carbon:[[@LINE+7]]:15: note: type `{}` does not implement interface `Core.ImplicitAs(V)` [MissingImplInMemberAccessInContext] // CHECK:STDERR: C(V).Create({}); // CHECK:STDERR: ^~ // CHECK:STDERR: fail_todo_compatible_deduce.carbon:[[@LINE-11]]:20: note: initializing function parameter [InCallToFunctionParam] // CHECK:STDERR: fn Create(unused value: T) {} // CHECK:STDERR: ^~~~~~~~ // CHECK:STDERR: C(V).Create({}); } // CHECK:STDOUT: --- fail_incompatible_deduce.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %type: type = facet_type [concrete] // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self] // CHECK:STDOUT: %pattern_type.98f: type = pattern_type type [concrete] // CHECK:STDOUT: %T: type = symbolic_binding T, 0 [symbolic] // CHECK:STDOUT: %C.type: type = generic_class_type @C [concrete] // CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] // CHECK:STDOUT: %C.generic: %C.type = struct_value () [concrete] // CHECK:STDOUT: %C.5a3: type = class_type @C, @C(%T) [symbolic] // CHECK:STDOUT: %pattern_type.51d1c4.1: type = pattern_type %T [symbolic] // CHECK:STDOUT: %C.Create.type.c11: type = fn_type @C.Create, @C(%T) [symbolic] // CHECK:STDOUT: %C.Create.723: %C.Create.type.c11 = struct_value () [symbolic] // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %complete_type: = complete_type_witness %empty_struct_type [concrete] // CHECK:STDOUT: %require_complete.944: = require_complete_type %T [symbolic] // CHECK:STDOUT: %U: type = symbolic_binding U, 0 [symbolic] // CHECK:STDOUT: %V: type = symbolic_binding V, 1 [symbolic] // CHECK:STDOUT: %F.type: type = fn_type @F [concrete] // CHECK:STDOUT: %F: %F.type = struct_value () [concrete] // CHECK:STDOUT: %C.ee9: type = class_type @C, @C(%V) [symbolic] // CHECK:STDOUT: %C.Create.type.c3d: type = fn_type @C.Create, @C(%V) [symbolic] // CHECK:STDOUT: %C.Create.b40: %C.Create.type.c3d = struct_value () [symbolic] // CHECK:STDOUT: %require_complete.1fc: = require_complete_type %C.ee9 [symbolic] // CHECK:STDOUT: %empty_struct: %empty_struct_type = struct_value () [concrete] // CHECK:STDOUT: %pattern_type.946: type = pattern_type %V [symbolic] // CHECK:STDOUT: %C.Create.specific_fn: = specific_function %C.Create.b40, @C.Create(%V) [symbolic] // CHECK:STDOUT: %require_complete.441: = require_complete_type %V [symbolic] // CHECK:STDOUT: %ImplicitAs.type.cc7: type = generic_interface_type @ImplicitAs [concrete] // CHECK:STDOUT: %ImplicitAs.generic: %ImplicitAs.type.cc7 = struct_value () [concrete] // CHECK:STDOUT: %Dest: type = symbolic_binding Dest, 0 [symbolic] // CHECK:STDOUT: %ImplicitAs.type.031: type = facet_type <@ImplicitAs, @ImplicitAs(%Dest)> [symbolic] // CHECK:STDOUT: %Self.738: %ImplicitAs.type.031 = symbolic_binding Self, 1 [symbolic] // CHECK:STDOUT: %ImplicitAs.assoc_type.ff3: type = assoc_entity_type @ImplicitAs, @ImplicitAs(%Dest) [symbolic] // CHECK:STDOUT: %ImplicitAs.WithSelf.Convert.type.b3a: type = fn_type @ImplicitAs.WithSelf.Convert, @ImplicitAs.WithSelf(%Dest, %Self.738) [symbolic] // CHECK:STDOUT: %ImplicitAs.WithSelf.Convert.1de: %ImplicitAs.WithSelf.Convert.type.b3a = struct_value () [symbolic] // CHECK:STDOUT: %ImplicitAs.type.1e5: type = facet_type <@ImplicitAs, @ImplicitAs(%V)> [symbolic] // CHECK:STDOUT: %ImplicitAs.assoc_type.d88: type = assoc_entity_type @ImplicitAs, @ImplicitAs(%V) [symbolic] // CHECK:STDOUT: %assoc0.3d8: %ImplicitAs.assoc_type.d88 = assoc_entity element0, imports.%Core.import_ref.201 [symbolic] // CHECK:STDOUT: %require_complete.cc6: = require_complete_type %ImplicitAs.type.1e5 [symbolic] // CHECK:STDOUT: %assoc0.843: %ImplicitAs.assoc_type.ff3 = assoc_entity element0, imports.%Core.import_ref.cc1 [symbolic] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: .ImplicitAs = %Core.ImplicitAs // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: %Core.ImplicitAs: %ImplicitAs.type.cc7 = import_ref Core//prelude/parts/as, ImplicitAs, loaded [concrete = constants.%ImplicitAs.generic] // CHECK:STDOUT: %Core.import_ref.178: @ImplicitAs.WithSelf.%ImplicitAs.assoc_type (%ImplicitAs.assoc_type.ff3) = import_ref Core//prelude/parts/as, loc{{\d+_\d+}}, loaded [symbolic = @ImplicitAs.WithSelf.%assoc0 (constants.%assoc0.843)] // CHECK:STDOUT: %Core.import_ref.201: @ImplicitAs.WithSelf.%ImplicitAs.WithSelf.Convert.type (%ImplicitAs.WithSelf.Convert.type.b3a) = import_ref Core//prelude/parts/as, loc{{\d+_\d+}}, loaded [symbolic = @ImplicitAs.WithSelf.%ImplicitAs.WithSelf.Convert (constants.%ImplicitAs.WithSelf.Convert.1de)] // CHECK:STDOUT: %Core.import_ref.cc1 = import_ref Core//prelude/parts/as, loc{{\d+_\d+}}, unloaded // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .C = %C.decl // CHECK:STDOUT: .F = %F.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %C.decl: %C.type = class_decl @C [concrete = constants.%C.generic] { // CHECK:STDOUT: %T.patt: %pattern_type.98f = symbolic_binding_pattern T, 0 [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %.loc4_13.1: type = splice_block %.loc4_13.2 [concrete = type] { // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %.loc4_13.2: type = type_literal type [concrete = type] // CHECK:STDOUT: } // CHECK:STDOUT: %T.loc4_9.2: type = symbolic_binding T, 0 [symbolic = %T.loc4_9.1 (constants.%T)] // CHECK:STDOUT: } // CHECK:STDOUT: %F.decl: %F.type = fn_decl @F [concrete = constants.%F] { // CHECK:STDOUT: %U.patt: %pattern_type.98f = symbolic_binding_pattern U, 0 [concrete] // CHECK:STDOUT: %V.patt: %pattern_type.98f = symbolic_binding_pattern V, 1 [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %.loc8_17.1: type = splice_block %.loc8_17.2 [concrete = type] { // CHECK:STDOUT: %.Self.2: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %.loc8_17.2: type = type_literal type [concrete = type] // CHECK:STDOUT: } // CHECK:STDOUT: %U.loc8_13.2: type = symbolic_binding U, 0 [symbolic = %U.loc8_13.1 (constants.%U)] // CHECK:STDOUT: %.loc8_27.1: type = splice_block %.loc8_27.2 [concrete = type] { // CHECK:STDOUT: %.Self.1: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %.loc8_27.2: type = type_literal type [concrete = type] // CHECK:STDOUT: } // CHECK:STDOUT: %V.loc8_23.2: type = symbolic_binding V, 1 [symbolic = %V.loc8_23.1 (constants.%V)] // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic class @C(%T.loc4_9.2: type) { // CHECK:STDOUT: %T.loc4_9.1: type = symbolic_binding T, 0 [symbolic = %T.loc4_9.1 (constants.%T)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %C.Create.type: type = fn_type @C.Create, @C(%T.loc4_9.1) [symbolic = %C.Create.type (constants.%C.Create.type.c11)] // CHECK:STDOUT: %C.Create: @C.%C.Create.type (%C.Create.type.c11) = struct_value () [symbolic = %C.Create (constants.%C.Create.723)] // CHECK:STDOUT: // CHECK:STDOUT: class { // CHECK:STDOUT: %C.Create.decl: @C.%C.Create.type (%C.Create.type.c11) = fn_decl @C.Create [symbolic = @C.%C.Create (constants.%C.Create.723)] { // CHECK:STDOUT: %value.patt: @C.Create.%pattern_type (%pattern_type.51d1c4.1) = value_binding_pattern value [concrete] // CHECK:STDOUT: %value.param_patt: @C.Create.%pattern_type (%pattern_type.51d1c4.1) = value_param_pattern %value.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %value.param: @C.Create.%T (%T) = value_param call_param0 // CHECK:STDOUT: %T.ref: type = name_ref T, @C.%T.loc4_9.2 [symbolic = %T (constants.%T)] // CHECK:STDOUT: %value: @C.Create.%T (%T) = value_binding value, %value.param // CHECK:STDOUT: } // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%C.5a3 // CHECK:STDOUT: .T = // CHECK:STDOUT: .Create = %C.Create.decl // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic fn @C.Create(@C.%T.loc4_9.2: type) { // CHECK:STDOUT: %T: type = symbolic_binding T, 0 [symbolic = %T (constants.%T)] // CHECK:STDOUT: %pattern_type: type = pattern_type %T [symbolic = %pattern_type (constants.%pattern_type.51d1c4.1)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete: = require_complete_type %T [symbolic = %require_complete (constants.%require_complete.944)] // CHECK:STDOUT: // CHECK:STDOUT: fn(%value.param: @C.Create.%T (%T)) { // CHECK:STDOUT: !entry: // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic fn @F(%U.loc8_13.2: type, %V.loc8_23.2: type) { // CHECK:STDOUT: %U.loc8_13.1: type = symbolic_binding U, 0 [symbolic = %U.loc8_13.1 (constants.%U)] // CHECK:STDOUT: %V.loc8_23.1: type = symbolic_binding V, 1 [symbolic = %V.loc8_23.1 (constants.%V)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %C.loc19_6.2: type = class_type @C, @C(%V.loc8_23.1) [symbolic = %C.loc19_6.2 (constants.%C.ee9)] // CHECK:STDOUT: %require_complete.loc19_7: = require_complete_type %C.loc19_6.2 [symbolic = %require_complete.loc19_7 (constants.%require_complete.1fc)] // CHECK:STDOUT: %C.Create.type: type = fn_type @C.Create, @C(%V.loc8_23.1) [symbolic = %C.Create.type (constants.%C.Create.type.c3d)] // CHECK:STDOUT: %C.Create: @F.%C.Create.type (%C.Create.type.c3d) = struct_value () [symbolic = %C.Create (constants.%C.Create.b40)] // CHECK:STDOUT: %C.Create.specific_fn.loc19_7.2: = specific_function %C.Create, @C.Create(%V.loc8_23.1) [symbolic = %C.Create.specific_fn.loc19_7.2 (constants.%C.Create.specific_fn)] // CHECK:STDOUT: %require_complete.loc19_16.1: = require_complete_type %V.loc8_23.1 [symbolic = %require_complete.loc19_16.1 (constants.%require_complete.441)] // CHECK:STDOUT: %ImplicitAs.type.loc19_16.2: type = facet_type <@ImplicitAs, @ImplicitAs(%V.loc8_23.1)> [symbolic = %ImplicitAs.type.loc19_16.2 (constants.%ImplicitAs.type.1e5)] // CHECK:STDOUT: %require_complete.loc19_16.2: = require_complete_type %ImplicitAs.type.loc19_16.2 [symbolic = %require_complete.loc19_16.2 (constants.%require_complete.cc6)] // CHECK:STDOUT: %ImplicitAs.assoc_type: type = assoc_entity_type @ImplicitAs, @ImplicitAs(%V.loc8_23.1) [symbolic = %ImplicitAs.assoc_type (constants.%ImplicitAs.assoc_type.d88)] // CHECK:STDOUT: %assoc0: @F.%ImplicitAs.assoc_type (%ImplicitAs.assoc_type.d88) = assoc_entity element0, imports.%Core.import_ref.201 [symbolic = %assoc0 (constants.%assoc0.3d8)] // CHECK:STDOUT: // CHECK:STDOUT: fn() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %C.ref: %C.type = name_ref C, file.%C.decl [concrete = constants.%C.generic] // CHECK:STDOUT: %V.ref: type = name_ref V, %V.loc8_23.2 [symbolic = %V.loc8_23.1 (constants.%V)] // CHECK:STDOUT: %C.loc19_6.1: type = class_type @C, @C(constants.%V) [symbolic = %C.loc19_6.2 (constants.%C.ee9)] // CHECK:STDOUT: %.loc19_7: @F.%C.Create.type (%C.Create.type.c3d) = specific_constant @C.%C.Create.decl, @C(constants.%V) [symbolic = %C.Create (constants.%C.Create.b40)] // CHECK:STDOUT: %Create.ref: @F.%C.Create.type (%C.Create.type.c3d) = name_ref Create, %.loc19_7 [symbolic = %C.Create (constants.%C.Create.b40)] // CHECK:STDOUT: %.loc19_16.1: %empty_struct_type = struct_literal () [concrete = constants.%empty_struct] // CHECK:STDOUT: %C.Create.specific_fn.loc19_7.1: = specific_function %Create.ref, @C.Create(constants.%V) [symbolic = %C.Create.specific_fn.loc19_7.2 (constants.%C.Create.specific_fn)] // CHECK:STDOUT: %ImplicitAs.type.loc19_16.1: type = facet_type <@ImplicitAs, @ImplicitAs(constants.%V)> [symbolic = %ImplicitAs.type.loc19_16.2 (constants.%ImplicitAs.type.1e5)] // CHECK:STDOUT: %.loc19_16.2: @F.%ImplicitAs.assoc_type (%ImplicitAs.assoc_type.d88) = specific_constant imports.%Core.import_ref.178, @ImplicitAs.WithSelf(constants.%V, constants.%Self.738) [symbolic = %assoc0 (constants.%assoc0.3d8)] // CHECK:STDOUT: %Convert.ref: @F.%ImplicitAs.assoc_type (%ImplicitAs.assoc_type.d88) = name_ref Convert, %.loc19_16.2 [symbolic = %assoc0 (constants.%assoc0.3d8)] // CHECK:STDOUT: %.loc19_16.3: @F.%V.loc8_23.1 (%V) = converted %.loc19_16.1, [concrete = ] // CHECK:STDOUT: %C.Create.call: init %empty_tuple.type = call %C.Create.specific_fn.loc19_7.1() // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @C(constants.%T) { // CHECK:STDOUT: %T.loc4_9.1 => constants.%T // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %C.Create.type => constants.%C.Create.type.c11 // CHECK:STDOUT: %C.Create => constants.%C.Create.723 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @C.Create(constants.%T) { // CHECK:STDOUT: %T => constants.%T // CHECK:STDOUT: %pattern_type => constants.%pattern_type.51d1c4.1 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @F(constants.%U, constants.%V) { // CHECK:STDOUT: %U.loc8_13.1 => constants.%U // CHECK:STDOUT: %V.loc8_23.1 => constants.%V // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @C(constants.%V) { // CHECK:STDOUT: %T.loc4_9.1 => constants.%V // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %C.Create.type => constants.%C.Create.type.c3d // CHECK:STDOUT: %C.Create => constants.%C.Create.b40 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @C.Create(constants.%V) { // CHECK:STDOUT: %T => constants.%V // CHECK:STDOUT: %pattern_type => constants.%pattern_type.946 // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete => constants.%require_complete.441 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- fail_todo_compatible_deduce.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %type: type = facet_type [concrete] // CHECK:STDOUT: %.Self.c39: %type = symbolic_binding .Self [symbolic_self] // CHECK:STDOUT: %pattern_type.98f: type = pattern_type type [concrete] // CHECK:STDOUT: %T: type = symbolic_binding T, 0 [symbolic] // CHECK:STDOUT: %C.type: type = generic_class_type @C [concrete] // CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] // CHECK:STDOUT: %C.generic: %C.type = struct_value () [concrete] // CHECK:STDOUT: %C.5a3: type = class_type @C, @C(%T) [symbolic] // CHECK:STDOUT: %pattern_type.51d1c4.1: type = pattern_type %T [symbolic] // CHECK:STDOUT: %C.Create.type.c11: type = fn_type @C.Create, @C(%T) [symbolic] // CHECK:STDOUT: %C.Create.723: %C.Create.type.c11 = struct_value () [symbolic] // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %complete_type: = complete_type_witness %empty_struct_type [concrete] // CHECK:STDOUT: %require_complete.944: = require_complete_type %T [symbolic] // CHECK:STDOUT: %U: type = symbolic_binding U, 0 [symbolic] // CHECK:STDOUT: %.Self.16f: type = symbolic_binding .Self [symbolic_self] // CHECK:STDOUT: %empty_struct: %empty_struct_type = struct_value () [concrete] // CHECK:STDOUT: %ImplicitAs.type.cc7: type = generic_interface_type @ImplicitAs [concrete] // CHECK:STDOUT: %ImplicitAs.generic: %ImplicitAs.type.cc7 = struct_value () [concrete] // CHECK:STDOUT: %Dest: type = symbolic_binding Dest, 0 [symbolic] // CHECK:STDOUT: %ImplicitAs.type.031: type = facet_type <@ImplicitAs, @ImplicitAs(%Dest)> [symbolic] // CHECK:STDOUT: %Self.738: %ImplicitAs.type.031 = symbolic_binding Self, 1 [symbolic] // CHECK:STDOUT: %ImplicitAs.assoc_type.ff3: type = assoc_entity_type @ImplicitAs, @ImplicitAs(%Dest) [symbolic] // CHECK:STDOUT: %ImplicitAs.WithSelf.Convert.type.b3a: type = fn_type @ImplicitAs.WithSelf.Convert, @ImplicitAs.WithSelf(%Dest, %Self.738) [symbolic] // CHECK:STDOUT: %ImplicitAs.WithSelf.Convert.1de: %ImplicitAs.WithSelf.Convert.type.b3a = struct_value () [symbolic] // CHECK:STDOUT: %ImplicitAs.type.f60: type = facet_type <@ImplicitAs, @ImplicitAs(%.Self.16f)> [symbolic_self] // CHECK:STDOUT: %type_where: type = facet_type [concrete] // CHECK:STDOUT: %pattern_type.354: type = pattern_type %type_where [concrete] // CHECK:STDOUT: %V: %type_where = symbolic_binding V, 1 [symbolic] // CHECK:STDOUT: %F.type: type = fn_type @F [concrete] // CHECK:STDOUT: %F: %F.type = struct_value () [concrete] // CHECK:STDOUT: %V.binding.as_type: type = symbolic_binding_type V, 1, %V [symbolic] // CHECK:STDOUT: %C.bca: type = class_type @C, @C(%V.binding.as_type) [symbolic] // CHECK:STDOUT: %C.Create.type.242: type = fn_type @C.Create, @C(%V.binding.as_type) [symbolic] // CHECK:STDOUT: %C.Create.206: %C.Create.type.242 = struct_value () [symbolic] // CHECK:STDOUT: %require_complete.232: = require_complete_type %C.bca [symbolic] // CHECK:STDOUT: %pattern_type.20b: type = pattern_type %V.binding.as_type [symbolic] // CHECK:STDOUT: %C.Create.specific_fn: = specific_function %C.Create.206, @C.Create(%V.binding.as_type) [symbolic] // CHECK:STDOUT: %require_complete.94b: = require_complete_type %V.binding.as_type [symbolic] // CHECK:STDOUT: %ImplicitAs.type.ee4: type = facet_type <@ImplicitAs, @ImplicitAs(%V.binding.as_type)> [symbolic] // CHECK:STDOUT: %ImplicitAs.assoc_type.f30: type = assoc_entity_type @ImplicitAs, @ImplicitAs(%V.binding.as_type) [symbolic] // CHECK:STDOUT: %assoc0.a1c: %ImplicitAs.assoc_type.f30 = assoc_entity element0, imports.%Core.import_ref.201 [symbolic] // CHECK:STDOUT: %require_complete.24c: = require_complete_type %ImplicitAs.type.ee4 [symbolic] // CHECK:STDOUT: %assoc0.843: %ImplicitAs.assoc_type.ff3 = assoc_entity element0, imports.%Core.import_ref.cc1 [symbolic] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: .ImplicitAs = %Core.ImplicitAs // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: %Core.ImplicitAs: %ImplicitAs.type.cc7 = import_ref Core//prelude/parts/as, ImplicitAs, loaded [concrete = constants.%ImplicitAs.generic] // CHECK:STDOUT: %Core.import_ref.178: @ImplicitAs.WithSelf.%ImplicitAs.assoc_type (%ImplicitAs.assoc_type.ff3) = import_ref Core//prelude/parts/as, loc{{\d+_\d+}}, loaded [symbolic = @ImplicitAs.WithSelf.%assoc0 (constants.%assoc0.843)] // CHECK:STDOUT: %Core.import_ref.201: @ImplicitAs.WithSelf.%ImplicitAs.WithSelf.Convert.type (%ImplicitAs.WithSelf.Convert.type.b3a) = import_ref Core//prelude/parts/as, loc{{\d+_\d+}}, loaded [symbolic = @ImplicitAs.WithSelf.%ImplicitAs.WithSelf.Convert (constants.%ImplicitAs.WithSelf.Convert.1de)] // CHECK:STDOUT: %Core.import_ref.cc1 = import_ref Core//prelude/parts/as, loc{{\d+_\d+}}, unloaded // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .C = %C.decl // CHECK:STDOUT: .F = %F.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %C.decl: %C.type = class_decl @C [concrete = constants.%C.generic] { // CHECK:STDOUT: %T.patt: %pattern_type.98f = symbolic_binding_pattern T, 0 [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %.loc4_13.1: type = splice_block %.loc4_13.2 [concrete = type] { // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self = constants.%.Self.c39] // CHECK:STDOUT: %.loc4_13.2: type = type_literal type [concrete = type] // CHECK:STDOUT: } // CHECK:STDOUT: %T.loc4_9.2: type = symbolic_binding T, 0 [symbolic = %T.loc4_9.1 (constants.%T)] // CHECK:STDOUT: } // CHECK:STDOUT: %F.decl: %F.type = fn_decl @F [concrete = constants.%F] { // CHECK:STDOUT: %U.patt: %pattern_type.98f = symbolic_binding_pattern U, 0 [concrete] // CHECK:STDOUT: %V.patt: %pattern_type.354 = symbolic_binding_pattern V, 1 [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %.loc9_17.1: type = splice_block %.loc9_17.2 [concrete = type] { // CHECK:STDOUT: %.Self.3: %type = symbolic_binding .Self [symbolic_self = constants.%.Self.c39] // CHECK:STDOUT: %.loc9_17.2: type = type_literal type [concrete = type] // CHECK:STDOUT: } // CHECK:STDOUT: %U.loc9_13.2: type = symbolic_binding U, 0 [symbolic = %U.loc9_13.1 (constants.%U)] // CHECK:STDOUT: %.loc9_32.1: type = splice_block %.loc9_32.2 [concrete = constants.%type_where] { // CHECK:STDOUT: %.Self.1: %type = symbolic_binding .Self [symbolic_self = constants.%.Self.c39] // CHECK:STDOUT: %.loc9_27: type = type_literal type [concrete = type] // CHECK:STDOUT: %.Self.2: type = symbolic_binding .Self [symbolic_self = constants.%.Self.16f] // CHECK:STDOUT: %.loc9_39.1: %empty_struct_type = struct_literal () [concrete = constants.%empty_struct] // CHECK:STDOUT: %Core.ref: = name_ref Core, imports.%Core [concrete = imports.%Core] // CHECK:STDOUT: %ImplicitAs.ref: %ImplicitAs.type.cc7 = name_ref ImplicitAs, imports.%Core.ImplicitAs [concrete = constants.%ImplicitAs.generic] // CHECK:STDOUT: %.Self.ref: type = name_ref .Self, %.Self.2 [symbolic_self = constants.%.Self.16f] // CHECK:STDOUT: %ImplicitAs.type.loc9: type = facet_type <@ImplicitAs, @ImplicitAs(constants.%.Self.16f)> [symbolic_self = constants.%ImplicitAs.type.f60] // CHECK:STDOUT: %.loc9_39.2: type = converted %.loc9_39.1, constants.%empty_struct_type [concrete = constants.%empty_struct_type] // CHECK:STDOUT: %.loc9_32.2: type = where_expr %.Self.2 [concrete = constants.%type_where] { // CHECK:STDOUT: requirement_base_facet_type type // CHECK:STDOUT: requirement_impls %.loc9_39.2, %ImplicitAs.type.loc9 // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: %V.loc9_23.2: %type_where = symbolic_binding V, 1 [symbolic = %V.loc9_23.1 (constants.%V)] // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic class @C(%T.loc4_9.2: type) { // CHECK:STDOUT: %T.loc4_9.1: type = symbolic_binding T, 0 [symbolic = %T.loc4_9.1 (constants.%T)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %C.Create.type: type = fn_type @C.Create, @C(%T.loc4_9.1) [symbolic = %C.Create.type (constants.%C.Create.type.c11)] // CHECK:STDOUT: %C.Create: @C.%C.Create.type (%C.Create.type.c11) = struct_value () [symbolic = %C.Create (constants.%C.Create.723)] // CHECK:STDOUT: // CHECK:STDOUT: class { // CHECK:STDOUT: %C.Create.decl: @C.%C.Create.type (%C.Create.type.c11) = fn_decl @C.Create [symbolic = @C.%C.Create (constants.%C.Create.723)] { // CHECK:STDOUT: %value.patt: @C.Create.%pattern_type (%pattern_type.51d1c4.1) = value_binding_pattern value [concrete] // CHECK:STDOUT: %value.param_patt: @C.Create.%pattern_type (%pattern_type.51d1c4.1) = value_param_pattern %value.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %value.param: @C.Create.%T (%T) = value_param call_param0 // CHECK:STDOUT: %T.ref: type = name_ref T, @C.%T.loc4_9.2 [symbolic = %T (constants.%T)] // CHECK:STDOUT: %value: @C.Create.%T (%T) = value_binding value, %value.param // CHECK:STDOUT: } // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%C.5a3 // CHECK:STDOUT: .T = // CHECK:STDOUT: .Create = %C.Create.decl // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic fn @C.Create(@C.%T.loc4_9.2: type) { // CHECK:STDOUT: %T: type = symbolic_binding T, 0 [symbolic = %T (constants.%T)] // CHECK:STDOUT: %pattern_type: type = pattern_type %T [symbolic = %pattern_type (constants.%pattern_type.51d1c4.1)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete: = require_complete_type %T [symbolic = %require_complete (constants.%require_complete.944)] // CHECK:STDOUT: // CHECK:STDOUT: fn(%value.param: @C.Create.%T (%T)) { // CHECK:STDOUT: !entry: // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic fn @F(%U.loc9_13.2: type, %V.loc9_23.2: %type_where) { // CHECK:STDOUT: %U.loc9_13.1: type = symbolic_binding U, 0 [symbolic = %U.loc9_13.1 (constants.%U)] // CHECK:STDOUT: %V.loc9_23.1: %type_where = symbolic_binding V, 1 [symbolic = %V.loc9_23.1 (constants.%V)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %V.binding.as_type: type = symbolic_binding_type V, 1, %V.loc9_23.1 [symbolic = %V.binding.as_type (constants.%V.binding.as_type)] // CHECK:STDOUT: %C.loc20_6.2: type = class_type @C, @C(%V.binding.as_type) [symbolic = %C.loc20_6.2 (constants.%C.bca)] // CHECK:STDOUT: %require_complete.loc20_7: = require_complete_type %C.loc20_6.2 [symbolic = %require_complete.loc20_7 (constants.%require_complete.232)] // CHECK:STDOUT: %C.Create.type: type = fn_type @C.Create, @C(%V.binding.as_type) [symbolic = %C.Create.type (constants.%C.Create.type.242)] // CHECK:STDOUT: %C.Create: @F.%C.Create.type (%C.Create.type.242) = struct_value () [symbolic = %C.Create (constants.%C.Create.206)] // CHECK:STDOUT: %C.Create.specific_fn.loc20_7.2: = specific_function %C.Create, @C.Create(%V.binding.as_type) [symbolic = %C.Create.specific_fn.loc20_7.2 (constants.%C.Create.specific_fn)] // CHECK:STDOUT: %require_complete.loc20_16.1: = require_complete_type %V.binding.as_type [symbolic = %require_complete.loc20_16.1 (constants.%require_complete.94b)] // CHECK:STDOUT: %ImplicitAs.type.loc20_16.2: type = facet_type <@ImplicitAs, @ImplicitAs(%V.binding.as_type)> [symbolic = %ImplicitAs.type.loc20_16.2 (constants.%ImplicitAs.type.ee4)] // CHECK:STDOUT: %require_complete.loc20_16.2: = require_complete_type %ImplicitAs.type.loc20_16.2 [symbolic = %require_complete.loc20_16.2 (constants.%require_complete.24c)] // CHECK:STDOUT: %ImplicitAs.assoc_type: type = assoc_entity_type @ImplicitAs, @ImplicitAs(%V.binding.as_type) [symbolic = %ImplicitAs.assoc_type (constants.%ImplicitAs.assoc_type.f30)] // CHECK:STDOUT: %assoc0: @F.%ImplicitAs.assoc_type (%ImplicitAs.assoc_type.f30) = assoc_entity element0, imports.%Core.import_ref.201 [symbolic = %assoc0 (constants.%assoc0.a1c)] // CHECK:STDOUT: // CHECK:STDOUT: fn() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %C.ref: %C.type = name_ref C, file.%C.decl [concrete = constants.%C.generic] // CHECK:STDOUT: %V.ref: %type_where = name_ref V, %V.loc9_23.2 [symbolic = %V.loc9_23.1 (constants.%V)] // CHECK:STDOUT: %V.as_type: type = facet_access_type %V.ref [symbolic = %V.binding.as_type (constants.%V.binding.as_type)] // CHECK:STDOUT: %.loc20_6: type = converted %V.ref, %V.as_type [symbolic = %V.binding.as_type (constants.%V.binding.as_type)] // CHECK:STDOUT: %C.loc20_6.1: type = class_type @C, @C(constants.%V.binding.as_type) [symbolic = %C.loc20_6.2 (constants.%C.bca)] // CHECK:STDOUT: %.loc20_7: @F.%C.Create.type (%C.Create.type.242) = specific_constant @C.%C.Create.decl, @C(constants.%V.binding.as_type) [symbolic = %C.Create (constants.%C.Create.206)] // CHECK:STDOUT: %Create.ref: @F.%C.Create.type (%C.Create.type.242) = name_ref Create, %.loc20_7 [symbolic = %C.Create (constants.%C.Create.206)] // CHECK:STDOUT: %.loc20_16.1: %empty_struct_type = struct_literal () [concrete = constants.%empty_struct] // CHECK:STDOUT: %C.Create.specific_fn.loc20_7.1: = specific_function %Create.ref, @C.Create(constants.%V.binding.as_type) [symbolic = %C.Create.specific_fn.loc20_7.2 (constants.%C.Create.specific_fn)] // CHECK:STDOUT: %ImplicitAs.type.loc20_16.1: type = facet_type <@ImplicitAs, @ImplicitAs(constants.%V.binding.as_type)> [symbolic = %ImplicitAs.type.loc20_16.2 (constants.%ImplicitAs.type.ee4)] // CHECK:STDOUT: %.loc20_16.2: @F.%ImplicitAs.assoc_type (%ImplicitAs.assoc_type.f30) = specific_constant imports.%Core.import_ref.178, @ImplicitAs.WithSelf(constants.%V.binding.as_type, constants.%Self.738) [symbolic = %assoc0 (constants.%assoc0.a1c)] // CHECK:STDOUT: %Convert.ref: @F.%ImplicitAs.assoc_type (%ImplicitAs.assoc_type.f30) = name_ref Convert, %.loc20_16.2 [symbolic = %assoc0 (constants.%assoc0.a1c)] // CHECK:STDOUT: %.loc20_16.3: @F.%V.binding.as_type (%V.binding.as_type) = converted %.loc20_16.1, [concrete = ] // CHECK:STDOUT: %C.Create.call: init %empty_tuple.type = call %C.Create.specific_fn.loc20_7.1() // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @C(constants.%T) { // CHECK:STDOUT: %T.loc4_9.1 => constants.%T // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %C.Create.type => constants.%C.Create.type.c11 // CHECK:STDOUT: %C.Create => constants.%C.Create.723 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @C.Create(constants.%T) { // CHECK:STDOUT: %T => constants.%T // CHECK:STDOUT: %pattern_type => constants.%pattern_type.51d1c4.1 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @F(constants.%U, constants.%V) { // CHECK:STDOUT: %U.loc9_13.1 => constants.%U // CHECK:STDOUT: %V.loc9_23.1 => constants.%V // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @C(constants.%V.binding.as_type) { // CHECK:STDOUT: %T.loc4_9.1 => constants.%V.binding.as_type // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %C.Create.type => constants.%C.Create.type.242 // CHECK:STDOUT: %C.Create => constants.%C.Create.206 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @C.Create(constants.%V.binding.as_type) { // CHECK:STDOUT: %T => constants.%V.binding.as_type // CHECK:STDOUT: %pattern_type => constants.%pattern_type.20b // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete => constants.%require_complete.94b // CHECK:STDOUT: } // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/deduce/generic_type.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/int.carbon // TODO: Add ranges and switch to "--dump-sem-ir-ranges=only". // EXTRA-ARGS: --dump-sem-ir-ranges=if-present // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/deduce/generic_type.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/deduce/generic_type.carbon // --- class.carbon library "[[@TEST_NAME]]"; class C(T:! type) {} class D {} fn F[T:! type](p: C(T)) -> T { return F(p); } fn G(p: C(D)) -> D { return F(p); } // --- interface.carbon library "[[@TEST_NAME]]"; class I(T:! type) {} class C {} fn F[T:! type](p: I(T)) -> C { return F(p); } fn G(p: I(C)) -> C { return F(p); } // --- nested.carbon library "[[@TEST_NAME]]"; class Outer(T:! type) { class Inner(U:! type) {} } class C {} class D {} // C++ doesn't permit deducing `T` here because `Outer` might be specialized. // But that's not possible in Carbon, so we can deduce `T`. fn F[T:! type, U:! type](p: Outer(T).Inner(U)) -> (T, U) { return F(p); } fn G(p: Outer(C).Inner(D)) -> (C, D) { return F(p); } // --- nontype.carbon library "[[@TEST_NAME]]"; class WithNontype(N:! i32) {} fn F[N:! i32](unused x: WithNontype(N)) -> i32 { return N; } fn G() -> i32 { return F({} as WithNontype(0)); } // CHECK:STDOUT: --- class.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %type: type = facet_type [concrete] // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self] // CHECK:STDOUT: %pattern_type.98f: type = pattern_type type [concrete] // CHECK:STDOUT: %T: type = symbolic_binding T, 0 [symbolic] // CHECK:STDOUT: %C.type: type = generic_class_type @C [concrete] // CHECK:STDOUT: %C.generic: %C.type = struct_value () [concrete] // CHECK:STDOUT: %C.5a3: type = class_type @C, @C(%T) [symbolic] // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %complete_type: = complete_type_witness %empty_struct_type [concrete] // CHECK:STDOUT: %D: type = class_type @D [concrete] // CHECK:STDOUT: %pattern_type.3d5: type = pattern_type %C.5a3 [symbolic] // CHECK:STDOUT: %.184: Core.Form = init_form %T [symbolic] // CHECK:STDOUT: %pattern_type.51d: type = pattern_type %T [symbolic] // CHECK:STDOUT: %F.type: type = fn_type @F [concrete] // CHECK:STDOUT: %F: %F.type = struct_value () [concrete] // CHECK:STDOUT: %require_complete.32c: = require_complete_type %C.5a3 [symbolic] // CHECK:STDOUT: %require_complete.944: = require_complete_type %T [symbolic] // CHECK:STDOUT: %F.specific_fn.643: = specific_function %F, @F(%T) [symbolic] // CHECK:STDOUT: %C.302: type = class_type @C, @C(%D) [concrete] // CHECK:STDOUT: %pattern_type.a7e: type = pattern_type %C.302 [concrete] // CHECK:STDOUT: %.9a5: Core.Form = init_form %D [concrete] // CHECK:STDOUT: %pattern_type.9c8: type = pattern_type %D [concrete] // CHECK:STDOUT: %G.type: type = fn_type @G [concrete] // CHECK:STDOUT: %G: %G.type = struct_value () [concrete] // CHECK:STDOUT: %F.specific_fn.e8a: = specific_function %F, @F(%D) [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .C = %C.decl // CHECK:STDOUT: .D = %D.decl // CHECK:STDOUT: .F = %F.decl // CHECK:STDOUT: .G = %G.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %C.decl: %C.type = class_decl @C [concrete = constants.%C.generic] { // CHECK:STDOUT: %T.patt: %pattern_type.98f = symbolic_binding_pattern T, 0 [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %.loc4_13.1: type = splice_block %.loc4_13.2 [concrete = type] { // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %.loc4_13.2: type = type_literal type [concrete = type] // CHECK:STDOUT: } // CHECK:STDOUT: %T.loc4_9.2: type = symbolic_binding T, 0 [symbolic = %T.loc4_9.1 (constants.%T)] // CHECK:STDOUT: } // CHECK:STDOUT: %D.decl: type = class_decl @D [concrete = constants.%D] {} {} // CHECK:STDOUT: %F.decl: %F.type = fn_decl @F [concrete = constants.%F] { // CHECK:STDOUT: %T.patt: %pattern_type.98f = symbolic_binding_pattern T, 0 [concrete] // CHECK:STDOUT: %p.patt: @F.%pattern_type.loc7_16 (%pattern_type.3d5) = value_binding_pattern p [concrete] // CHECK:STDOUT: %p.param_patt: @F.%pattern_type.loc7_16 (%pattern_type.3d5) = value_param_pattern %p.patt [concrete] // CHECK:STDOUT: %return.patt: @F.%pattern_type.loc7_25 (%pattern_type.51d) = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: @F.%pattern_type.loc7_25 (%pattern_type.51d) = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %T.ref.loc7_28: type = name_ref T, %T.loc7_6.2 [symbolic = %T.loc7_6.1 (constants.%T)] // CHECK:STDOUT: %.loc7_28.3: Core.Form = init_form %T.ref.loc7_28 [symbolic = %.loc7_28.2 (constants.%.184)] // CHECK:STDOUT: %.loc7_10.1: type = splice_block %.loc7_10.2 [concrete = type] { // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %.loc7_10.2: type = type_literal type [concrete = type] // CHECK:STDOUT: } // CHECK:STDOUT: %T.loc7_6.2: type = symbolic_binding T, 0 [symbolic = %T.loc7_6.1 (constants.%T)] // CHECK:STDOUT: %p.param: @F.%C.loc7_22.1 (%C.5a3) = value_param call_param0 // CHECK:STDOUT: %.loc7_22: type = splice_block %C.loc7_22.2 [symbolic = %C.loc7_22.1 (constants.%C.5a3)] { // CHECK:STDOUT: %C.ref: %C.type = name_ref C, file.%C.decl [concrete = constants.%C.generic] // CHECK:STDOUT: %T.ref.loc7_21: type = name_ref T, %T.loc7_6.2 [symbolic = %T.loc7_6.1 (constants.%T)] // CHECK:STDOUT: %C.loc7_22.2: type = class_type @C, @C(constants.%T) [symbolic = %C.loc7_22.1 (constants.%C.5a3)] // CHECK:STDOUT: } // CHECK:STDOUT: %p: @F.%C.loc7_22.1 (%C.5a3) = value_binding p, %p.param // CHECK:STDOUT: %return.param: ref @F.%T.loc7_6.1 (%T) = out_param call_param1 // CHECK:STDOUT: %return: ref @F.%T.loc7_6.1 (%T) = return_slot %return.param // CHECK:STDOUT: } // CHECK:STDOUT: %G.decl: %G.type = fn_decl @G [concrete = constants.%G] { // CHECK:STDOUT: %p.patt: %pattern_type.a7e = value_binding_pattern p [concrete] // CHECK:STDOUT: %p.param_patt: %pattern_type.a7e = value_param_pattern %p.patt [concrete] // CHECK:STDOUT: %return.patt: %pattern_type.9c8 = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: %pattern_type.9c8 = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %D.ref.loc9_18: type = name_ref D, file.%D.decl [concrete = constants.%D] // CHECK:STDOUT: %.loc9_18.2: Core.Form = init_form %D.ref.loc9_18 [concrete = constants.%.9a5] // CHECK:STDOUT: %p.param: %C.302 = value_param call_param0 // CHECK:STDOUT: %.loc9_12: type = splice_block %C [concrete = constants.%C.302] { // CHECK:STDOUT: %C.ref: %C.type = name_ref C, file.%C.decl [concrete = constants.%C.generic] // CHECK:STDOUT: %D.ref.loc9_11: type = name_ref D, file.%D.decl [concrete = constants.%D] // CHECK:STDOUT: %C: type = class_type @C, @C(constants.%D) [concrete = constants.%C.302] // CHECK:STDOUT: } // CHECK:STDOUT: %p: %C.302 = value_binding p, %p.param // CHECK:STDOUT: %return.param: ref %D = out_param call_param1 // CHECK:STDOUT: %return: ref %D = return_slot %return.param // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic class @C(%T.loc4_9.2: type) { // CHECK:STDOUT: %T.loc4_9.1: type = symbolic_binding T, 0 [symbolic = %T.loc4_9.1 (constants.%T)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: // CHECK:STDOUT: class { // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%C.5a3 // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @D { // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%D // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic fn @F(%T.loc7_6.2: type) { // CHECK:STDOUT: %T.loc7_6.1: type = symbolic_binding T, 0 [symbolic = %T.loc7_6.1 (constants.%T)] // CHECK:STDOUT: %C.loc7_22.1: type = class_type @C, @C(%T.loc7_6.1) [symbolic = %C.loc7_22.1 (constants.%C.5a3)] // CHECK:STDOUT: %pattern_type.loc7_16: type = pattern_type %C.loc7_22.1 [symbolic = %pattern_type.loc7_16 (constants.%pattern_type.3d5)] // CHECK:STDOUT: %.loc7_28.2: Core.Form = init_form %T.loc7_6.1 [symbolic = %.loc7_28.2 (constants.%.184)] // CHECK:STDOUT: %pattern_type.loc7_25: type = pattern_type %T.loc7_6.1 [symbolic = %pattern_type.loc7_25 (constants.%pattern_type.51d)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete.loc7_17: = require_complete_type %C.loc7_22.1 [symbolic = %require_complete.loc7_17 (constants.%require_complete.32c)] // CHECK:STDOUT: %require_complete.loc7_28: = require_complete_type %T.loc7_6.1 [symbolic = %require_complete.loc7_28 (constants.%require_complete.944)] // CHECK:STDOUT: %F.specific_fn.loc7_39.2: = specific_function constants.%F, @F(%T.loc7_6.1) [symbolic = %F.specific_fn.loc7_39.2 (constants.%F.specific_fn.643)] // CHECK:STDOUT: // CHECK:STDOUT: fn(%p.param: @F.%C.loc7_22.1 (%C.5a3)) -> out %return.param: @F.%T.loc7_6.1 (%T) { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %F.ref: %F.type = name_ref F, file.%F.decl [concrete = constants.%F] // CHECK:STDOUT: %p.ref: @F.%C.loc7_22.1 (%C.5a3) = name_ref p, %p // CHECK:STDOUT: %F.specific_fn.loc7_39.1: = specific_function %F.ref, @F(constants.%T) [symbolic = %F.specific_fn.loc7_39.2 (constants.%F.specific_fn.643)] // CHECK:STDOUT: %.loc7_28.1: ref @F.%T.loc7_6.1 (%T) = splice_block %return.param {} // CHECK:STDOUT: %F.call: init @F.%T.loc7_6.1 (%T) to %.loc7_28.1 = call %F.specific_fn.loc7_39.1(%p.ref) // CHECK:STDOUT: return %F.call to %return.param // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @G(%p.param: %C.302) -> out %return.param: %D { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %F.ref: %F.type = name_ref F, file.%F.decl [concrete = constants.%F] // CHECK:STDOUT: %p.ref: %C.302 = name_ref p, %p // CHECK:STDOUT: %F.specific_fn: = specific_function %F.ref, @F(constants.%D) [concrete = constants.%F.specific_fn.e8a] // CHECK:STDOUT: %.loc9_18.1: ref %D = splice_block %return.param {} // CHECK:STDOUT: %F.call: init %D to %.loc9_18.1 = call %F.specific_fn(%p.ref) // CHECK:STDOUT: return %F.call to %return.param // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @C(constants.%T) { // CHECK:STDOUT: %T.loc4_9.1 => constants.%T // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @F(constants.%T) { // CHECK:STDOUT: %T.loc7_6.1 => constants.%T // CHECK:STDOUT: %C.loc7_22.1 => constants.%C.5a3 // CHECK:STDOUT: %pattern_type.loc7_16 => constants.%pattern_type.3d5 // CHECK:STDOUT: %.loc7_28.2 => constants.%.184 // CHECK:STDOUT: %pattern_type.loc7_25 => constants.%pattern_type.51d // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete.loc7_17 => constants.%require_complete.32c // CHECK:STDOUT: %require_complete.loc7_28 => constants.%require_complete.944 // CHECK:STDOUT: %F.specific_fn.loc7_39.2 => constants.%F.specific_fn.643 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @C(constants.%D) { // CHECK:STDOUT: %T.loc4_9.1 => constants.%D // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @F(constants.%D) { // CHECK:STDOUT: %T.loc7_6.1 => constants.%D // CHECK:STDOUT: %C.loc7_22.1 => constants.%C.302 // CHECK:STDOUT: %pattern_type.loc7_16 => constants.%pattern_type.a7e // CHECK:STDOUT: %.loc7_28.2 => constants.%.9a5 // CHECK:STDOUT: %pattern_type.loc7_25 => constants.%pattern_type.9c8 // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete.loc7_17 => constants.%complete_type // CHECK:STDOUT: %require_complete.loc7_28 => constants.%complete_type // CHECK:STDOUT: %F.specific_fn.loc7_39.2 => constants.%F.specific_fn.e8a // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- interface.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %type: type = facet_type [concrete] // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self] // CHECK:STDOUT: %pattern_type.98f: type = pattern_type type [concrete] // CHECK:STDOUT: %T: type = symbolic_binding T, 0 [symbolic] // CHECK:STDOUT: %I.type: type = generic_class_type @I [concrete] // CHECK:STDOUT: %I.generic: %I.type = struct_value () [concrete] // CHECK:STDOUT: %I.3cf: type = class_type @I, @I(%T) [symbolic] // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %complete_type: = complete_type_witness %empty_struct_type [concrete] // CHECK:STDOUT: %C: type = class_type @C [concrete] // CHECK:STDOUT: %pattern_type.7be: type = pattern_type %I.3cf [symbolic] // CHECK:STDOUT: %.a69: Core.Form = init_form %C [concrete] // CHECK:STDOUT: %pattern_type.7c7: type = pattern_type %C [concrete] // CHECK:STDOUT: %F.type: type = fn_type @F [concrete] // CHECK:STDOUT: %F: %F.type = struct_value () [concrete] // CHECK:STDOUT: %require_complete: = require_complete_type %I.3cf [symbolic] // CHECK:STDOUT: %F.specific_fn.643: = specific_function %F, @F(%T) [symbolic] // CHECK:STDOUT: %I.bce: type = class_type @I, @I(%C) [concrete] // CHECK:STDOUT: %pattern_type.767: type = pattern_type %I.bce [concrete] // CHECK:STDOUT: %G.type: type = fn_type @G [concrete] // CHECK:STDOUT: %G: %G.type = struct_value () [concrete] // CHECK:STDOUT: %F.specific_fn.540: = specific_function %F, @F(%C) [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .I = %I.decl // CHECK:STDOUT: .C = %C.decl // CHECK:STDOUT: .F = %F.decl // CHECK:STDOUT: .G = %G.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %I.decl: %I.type = class_decl @I [concrete = constants.%I.generic] { // CHECK:STDOUT: %T.patt: %pattern_type.98f = symbolic_binding_pattern T, 0 [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %.loc4_13.1: type = splice_block %.loc4_13.2 [concrete = type] { // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %.loc4_13.2: type = type_literal type [concrete = type] // CHECK:STDOUT: } // CHECK:STDOUT: %T.loc4_9.2: type = symbolic_binding T, 0 [symbolic = %T.loc4_9.1 (constants.%T)] // CHECK:STDOUT: } // CHECK:STDOUT: %C.decl: type = class_decl @C [concrete = constants.%C] {} {} // CHECK:STDOUT: %F.decl: %F.type = fn_decl @F [concrete = constants.%F] { // CHECK:STDOUT: %T.patt: %pattern_type.98f = symbolic_binding_pattern T, 0 [concrete] // CHECK:STDOUT: %p.patt: @F.%pattern_type (%pattern_type.7be) = value_binding_pattern p [concrete] // CHECK:STDOUT: %p.param_patt: @F.%pattern_type (%pattern_type.7be) = value_param_pattern %p.patt [concrete] // CHECK:STDOUT: %return.patt: %pattern_type.7c7 = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: %pattern_type.7c7 = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %C.ref: type = name_ref C, file.%C.decl [concrete = constants.%C] // CHECK:STDOUT: %.loc7_28.2: Core.Form = init_form %C.ref [concrete = constants.%.a69] // CHECK:STDOUT: %.loc7_10.1: type = splice_block %.loc7_10.2 [concrete = type] { // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %.loc7_10.2: type = type_literal type [concrete = type] // CHECK:STDOUT: } // CHECK:STDOUT: %T.loc7_6.2: type = symbolic_binding T, 0 [symbolic = %T.loc7_6.1 (constants.%T)] // CHECK:STDOUT: %p.param: @F.%I.loc7_22.1 (%I.3cf) = value_param call_param0 // CHECK:STDOUT: %.loc7_22: type = splice_block %I.loc7_22.2 [symbolic = %I.loc7_22.1 (constants.%I.3cf)] { // CHECK:STDOUT: %I.ref: %I.type = name_ref I, file.%I.decl [concrete = constants.%I.generic] // CHECK:STDOUT: %T.ref: type = name_ref T, %T.loc7_6.2 [symbolic = %T.loc7_6.1 (constants.%T)] // CHECK:STDOUT: %I.loc7_22.2: type = class_type @I, @I(constants.%T) [symbolic = %I.loc7_22.1 (constants.%I.3cf)] // CHECK:STDOUT: } // CHECK:STDOUT: %p: @F.%I.loc7_22.1 (%I.3cf) = value_binding p, %p.param // CHECK:STDOUT: %return.param: ref %C = out_param call_param1 // CHECK:STDOUT: %return: ref %C = return_slot %return.param // CHECK:STDOUT: } // CHECK:STDOUT: %G.decl: %G.type = fn_decl @G [concrete = constants.%G] { // CHECK:STDOUT: %p.patt: %pattern_type.767 = value_binding_pattern p [concrete] // CHECK:STDOUT: %p.param_patt: %pattern_type.767 = value_param_pattern %p.patt [concrete] // CHECK:STDOUT: %return.patt: %pattern_type.7c7 = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: %pattern_type.7c7 = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %C.ref.loc9_18: type = name_ref C, file.%C.decl [concrete = constants.%C] // CHECK:STDOUT: %.loc9_18.2: Core.Form = init_form %C.ref.loc9_18 [concrete = constants.%.a69] // CHECK:STDOUT: %p.param: %I.bce = value_param call_param0 // CHECK:STDOUT: %.loc9_12: type = splice_block %I [concrete = constants.%I.bce] { // CHECK:STDOUT: %I.ref: %I.type = name_ref I, file.%I.decl [concrete = constants.%I.generic] // CHECK:STDOUT: %C.ref.loc9_11: type = name_ref C, file.%C.decl [concrete = constants.%C] // CHECK:STDOUT: %I: type = class_type @I, @I(constants.%C) [concrete = constants.%I.bce] // CHECK:STDOUT: } // CHECK:STDOUT: %p: %I.bce = value_binding p, %p.param // CHECK:STDOUT: %return.param: ref %C = out_param call_param1 // CHECK:STDOUT: %return: ref %C = return_slot %return.param // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic class @I(%T.loc4_9.2: type) { // CHECK:STDOUT: %T.loc4_9.1: type = symbolic_binding T, 0 [symbolic = %T.loc4_9.1 (constants.%T)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: // CHECK:STDOUT: class { // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%I.3cf // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @C { // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%C // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic fn @F(%T.loc7_6.2: type) { // CHECK:STDOUT: %T.loc7_6.1: type = symbolic_binding T, 0 [symbolic = %T.loc7_6.1 (constants.%T)] // CHECK:STDOUT: %I.loc7_22.1: type = class_type @I, @I(%T.loc7_6.1) [symbolic = %I.loc7_22.1 (constants.%I.3cf)] // CHECK:STDOUT: %pattern_type: type = pattern_type %I.loc7_22.1 [symbolic = %pattern_type (constants.%pattern_type.7be)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete: = require_complete_type %I.loc7_22.1 [symbolic = %require_complete (constants.%require_complete)] // CHECK:STDOUT: %F.specific_fn.loc7_39.2: = specific_function constants.%F, @F(%T.loc7_6.1) [symbolic = %F.specific_fn.loc7_39.2 (constants.%F.specific_fn.643)] // CHECK:STDOUT: // CHECK:STDOUT: fn(%p.param: @F.%I.loc7_22.1 (%I.3cf)) -> out %return.param: %C { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %F.ref: %F.type = name_ref F, file.%F.decl [concrete = constants.%F] // CHECK:STDOUT: %p.ref: @F.%I.loc7_22.1 (%I.3cf) = name_ref p, %p // CHECK:STDOUT: %F.specific_fn.loc7_39.1: = specific_function %F.ref, @F(constants.%T) [symbolic = %F.specific_fn.loc7_39.2 (constants.%F.specific_fn.643)] // CHECK:STDOUT: %.loc7_28.1: ref %C = splice_block %return.param {} // CHECK:STDOUT: %F.call: init %C to %.loc7_28.1 = call %F.specific_fn.loc7_39.1(%p.ref) // CHECK:STDOUT: return %F.call to %return.param // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @G(%p.param: %I.bce) -> out %return.param: %C { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %F.ref: %F.type = name_ref F, file.%F.decl [concrete = constants.%F] // CHECK:STDOUT: %p.ref: %I.bce = name_ref p, %p // CHECK:STDOUT: %F.specific_fn: = specific_function %F.ref, @F(constants.%C) [concrete = constants.%F.specific_fn.540] // CHECK:STDOUT: %.loc9_18.1: ref %C = splice_block %return.param {} // CHECK:STDOUT: %F.call: init %C to %.loc9_18.1 = call %F.specific_fn(%p.ref) // CHECK:STDOUT: return %F.call to %return.param // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @I(constants.%T) { // CHECK:STDOUT: %T.loc4_9.1 => constants.%T // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @F(constants.%T) { // CHECK:STDOUT: %T.loc7_6.1 => constants.%T // CHECK:STDOUT: %I.loc7_22.1 => constants.%I.3cf // CHECK:STDOUT: %pattern_type => constants.%pattern_type.7be // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete => constants.%require_complete // CHECK:STDOUT: %F.specific_fn.loc7_39.2 => constants.%F.specific_fn.643 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @I(constants.%C) { // CHECK:STDOUT: %T.loc4_9.1 => constants.%C // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @F(constants.%C) { // CHECK:STDOUT: %T.loc7_6.1 => constants.%C // CHECK:STDOUT: %I.loc7_22.1 => constants.%I.bce // CHECK:STDOUT: %pattern_type => constants.%pattern_type.767 // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete => constants.%complete_type // CHECK:STDOUT: %F.specific_fn.loc7_39.2 => constants.%F.specific_fn.540 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- nested.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %type: type = facet_type [concrete] // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self] // CHECK:STDOUT: %pattern_type.98f: type = pattern_type type [concrete] // CHECK:STDOUT: %T: type = symbolic_binding T, 0 [symbolic] // CHECK:STDOUT: %Outer.type: type = generic_class_type @Outer [concrete] // CHECK:STDOUT: %Outer.generic: %Outer.type = struct_value () [concrete] // CHECK:STDOUT: %Outer.387: type = class_type @Outer, @Outer(%T) [symbolic] // CHECK:STDOUT: %U: type = symbolic_binding U, 1 [symbolic] // CHECK:STDOUT: %Inner.type.e0d: type = generic_class_type @Inner, @Outer(%T) [symbolic] // CHECK:STDOUT: %Inner.generic.ada: %Inner.type.e0d = struct_value () [symbolic] // CHECK:STDOUT: %Inner.e21: type = class_type @Inner, @Inner(%T, %U) [symbolic] // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %complete_type.357: = complete_type_witness %empty_struct_type [concrete] // CHECK:STDOUT: %C: type = class_type @C [concrete] // CHECK:STDOUT: %D: type = class_type @D [concrete] // CHECK:STDOUT: %require_complete.448: = require_complete_type %Outer.387 [symbolic] // CHECK:STDOUT: %pattern_type.0d1: type = pattern_type %Inner.e21 [symbolic] // CHECK:STDOUT: %tuple.type.24b: type = tuple_type (type, type) [concrete] // CHECK:STDOUT: %tuple.4b9: %tuple.type.24b = tuple_value (%T, %U) [symbolic] // CHECK:STDOUT: %tuple.type.a5e: type = tuple_type (%T, %U) [symbolic] // CHECK:STDOUT: %.f18: Core.Form = init_form %tuple.type.a5e [symbolic] // CHECK:STDOUT: %pattern_type.eee: type = pattern_type %tuple.type.a5e [symbolic] // CHECK:STDOUT: %F.type: type = fn_type @F [concrete] // CHECK:STDOUT: %F: %F.type = struct_value () [concrete] // CHECK:STDOUT: %require_complete.41c: = require_complete_type %Inner.e21 [symbolic] // CHECK:STDOUT: %require_complete.220: = require_complete_type %tuple.type.a5e [symbolic] // CHECK:STDOUT: %F.specific_fn.a54: = specific_function %F, @F(%T, %U) [symbolic] // CHECK:STDOUT: %Outer.eed: type = class_type @Outer, @Outer(%C) [concrete] // CHECK:STDOUT: %Inner.type.e8c: type = generic_class_type @Inner, @Outer(%C) [concrete] // CHECK:STDOUT: %Inner.generic.9a9: %Inner.type.e8c = struct_value () [concrete] // CHECK:STDOUT: %Inner.240: type = class_type @Inner, @Inner(%C, %D) [concrete] // CHECK:STDOUT: %pattern_type.204: type = pattern_type %Inner.240 [concrete] // CHECK:STDOUT: %tuple.a0a: %tuple.type.24b = tuple_value (%C, %D) [concrete] // CHECK:STDOUT: %tuple.type.281: type = tuple_type (%C, %D) [concrete] // CHECK:STDOUT: %.eae: Core.Form = init_form %tuple.type.281 [concrete] // CHECK:STDOUT: %pattern_type.881: type = pattern_type %tuple.type.281 [concrete] // CHECK:STDOUT: %G.type: type = fn_type @G [concrete] // CHECK:STDOUT: %G: %G.type = struct_value () [concrete] // CHECK:STDOUT: %F.specific_fn.107: = specific_function %F, @F(%C, %D) [concrete] // CHECK:STDOUT: %complete_type.734: = complete_type_witness %tuple.type.281 [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .Outer = %Outer.decl // CHECK:STDOUT: .C = %C.decl // CHECK:STDOUT: .D = %D.decl // CHECK:STDOUT: .F = %F.decl // CHECK:STDOUT: .G = %G.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %Outer.decl: %Outer.type = class_decl @Outer [concrete = constants.%Outer.generic] { // CHECK:STDOUT: %T.patt: %pattern_type.98f = symbolic_binding_pattern T, 0 [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %.loc4_17.1: type = splice_block %.loc4_17.2 [concrete = type] { // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %.loc4_17.2: type = type_literal type [concrete = type] // CHECK:STDOUT: } // CHECK:STDOUT: %T.loc4_13.2: type = symbolic_binding T, 0 [symbolic = %T.loc4_13.1 (constants.%T)] // CHECK:STDOUT: } // CHECK:STDOUT: %C.decl: type = class_decl @C [concrete = constants.%C] {} {} // CHECK:STDOUT: %D.decl: type = class_decl @D [concrete = constants.%D] {} {} // CHECK:STDOUT: %F.decl: %F.type = fn_decl @F [concrete = constants.%F] { // CHECK:STDOUT: %T.patt: %pattern_type.98f = symbolic_binding_pattern T, 0 [concrete] // CHECK:STDOUT: %U.patt: %pattern_type.98f = symbolic_binding_pattern U, 1 [concrete] // CHECK:STDOUT: %p.patt: @F.%pattern_type.loc13_26 (%pattern_type.0d1) = value_binding_pattern p [concrete] // CHECK:STDOUT: %p.param_patt: @F.%pattern_type.loc13_26 (%pattern_type.0d1) = value_param_pattern %p.patt [concrete] // CHECK:STDOUT: %return.patt: @F.%pattern_type.loc13_48 (%pattern_type.eee) = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: @F.%pattern_type.loc13_48 (%pattern_type.eee) = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %T.ref.loc13_52: type = name_ref T, %T.loc13_6.2 [symbolic = %T.loc13_6.1 (constants.%T)] // CHECK:STDOUT: %U.ref.loc13_55: type = name_ref U, %U.loc13_16.2 [symbolic = %U.loc13_16.1 (constants.%U)] // CHECK:STDOUT: %.loc13_56.3: %tuple.type.24b = tuple_literal (%T.ref.loc13_52, %U.ref.loc13_55) [symbolic = %tuple (constants.%tuple.4b9)] // CHECK:STDOUT: %.loc13_56.4: type = converted %.loc13_56.3, constants.%tuple.type.a5e [symbolic = %tuple.type (constants.%tuple.type.a5e)] // CHECK:STDOUT: %.loc13_56.5: Core.Form = init_form %.loc13_56.4 [symbolic = %.loc13_56.2 (constants.%.f18)] // CHECK:STDOUT: %.loc13_10.1: type = splice_block %.loc13_10.2 [concrete = type] { // CHECK:STDOUT: %.Self.2: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %.loc13_10.2: type = type_literal type [concrete = type] // CHECK:STDOUT: } // CHECK:STDOUT: %T.loc13_6.2: type = symbolic_binding T, 0 [symbolic = %T.loc13_6.1 (constants.%T)] // CHECK:STDOUT: %.loc13_20.1: type = splice_block %.loc13_20.2 [concrete = type] { // CHECK:STDOUT: %.Self.1: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %.loc13_20.2: type = type_literal type [concrete = type] // CHECK:STDOUT: } // CHECK:STDOUT: %U.loc13_16.2: type = symbolic_binding U, 1 [symbolic = %U.loc13_16.1 (constants.%U)] // CHECK:STDOUT: %p.param: @F.%Inner.loc13_45.1 (%Inner.e21) = value_param call_param0 // CHECK:STDOUT: %.loc13_45: type = splice_block %Inner.loc13_45.2 [symbolic = %Inner.loc13_45.1 (constants.%Inner.e21)] { // CHECK:STDOUT: %Outer.ref: %Outer.type = name_ref Outer, file.%Outer.decl [concrete = constants.%Outer.generic] // CHECK:STDOUT: %T.ref.loc13_35: type = name_ref T, %T.loc13_6.2 [symbolic = %T.loc13_6.1 (constants.%T)] // CHECK:STDOUT: %Outer.loc13_36.2: type = class_type @Outer, @Outer(constants.%T) [symbolic = %Outer.loc13_36.1 (constants.%Outer.387)] // CHECK:STDOUT: %.loc13_37: @F.%Inner.type (%Inner.type.e0d) = specific_constant @Outer.%Inner.decl, @Outer(constants.%T) [symbolic = %Inner.generic (constants.%Inner.generic.ada)] // CHECK:STDOUT: %Inner.ref: @F.%Inner.type (%Inner.type.e0d) = name_ref Inner, %.loc13_37 [symbolic = %Inner.generic (constants.%Inner.generic.ada)] // CHECK:STDOUT: %U.ref.loc13_44: type = name_ref U, %U.loc13_16.2 [symbolic = %U.loc13_16.1 (constants.%U)] // CHECK:STDOUT: %Inner.loc13_45.2: type = class_type @Inner, @Inner(constants.%T, constants.%U) [symbolic = %Inner.loc13_45.1 (constants.%Inner.e21)] // CHECK:STDOUT: } // CHECK:STDOUT: %p: @F.%Inner.loc13_45.1 (%Inner.e21) = value_binding p, %p.param // CHECK:STDOUT: %return.param: ref @F.%tuple.type (%tuple.type.a5e) = out_param call_param1 // CHECK:STDOUT: %return: ref @F.%tuple.type (%tuple.type.a5e) = return_slot %return.param // CHECK:STDOUT: } // CHECK:STDOUT: %G.decl: %G.type = fn_decl @G [concrete = constants.%G] { // CHECK:STDOUT: %p.patt: %pattern_type.204 = value_binding_pattern p [concrete] // CHECK:STDOUT: %p.param_patt: %pattern_type.204 = value_param_pattern %p.patt [concrete] // CHECK:STDOUT: %return.patt: %pattern_type.881 = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: %pattern_type.881 = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %C.ref.loc15_32: type = name_ref C, file.%C.decl [concrete = constants.%C] // CHECK:STDOUT: %D.ref.loc15_35: type = name_ref D, file.%D.decl [concrete = constants.%D] // CHECK:STDOUT: %.loc15_36.2: %tuple.type.24b = tuple_literal (%C.ref.loc15_32, %D.ref.loc15_35) [concrete = constants.%tuple.a0a] // CHECK:STDOUT: %.loc15_36.3: type = converted %.loc15_36.2, constants.%tuple.type.281 [concrete = constants.%tuple.type.281] // CHECK:STDOUT: %.loc15_36.4: Core.Form = init_form %.loc15_36.3 [concrete = constants.%.eae] // CHECK:STDOUT: %p.param: %Inner.240 = value_param call_param0 // CHECK:STDOUT: %.loc15_25: type = splice_block %Inner [concrete = constants.%Inner.240] { // CHECK:STDOUT: %Outer.ref: %Outer.type = name_ref Outer, file.%Outer.decl [concrete = constants.%Outer.generic] // CHECK:STDOUT: %C.ref.loc15_15: type = name_ref C, file.%C.decl [concrete = constants.%C] // CHECK:STDOUT: %Outer: type = class_type @Outer, @Outer(constants.%C) [concrete = constants.%Outer.eed] // CHECK:STDOUT: %.loc15_17: %Inner.type.e8c = specific_constant @Outer.%Inner.decl, @Outer(constants.%C) [concrete = constants.%Inner.generic.9a9] // CHECK:STDOUT: %Inner.ref: %Inner.type.e8c = name_ref Inner, %.loc15_17 [concrete = constants.%Inner.generic.9a9] // CHECK:STDOUT: %D.ref.loc15_24: type = name_ref D, file.%D.decl [concrete = constants.%D] // CHECK:STDOUT: %Inner: type = class_type @Inner, @Inner(constants.%C, constants.%D) [concrete = constants.%Inner.240] // CHECK:STDOUT: } // CHECK:STDOUT: %p: %Inner.240 = value_binding p, %p.param // CHECK:STDOUT: %return.param: ref %tuple.type.281 = out_param call_param1 // CHECK:STDOUT: %return: ref %tuple.type.281 = return_slot %return.param // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic class @Outer(%T.loc4_13.2: type) { // CHECK:STDOUT: %T.loc4_13.1: type = symbolic_binding T, 0 [symbolic = %T.loc4_13.1 (constants.%T)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %Inner.type: type = generic_class_type @Inner, @Outer(%T.loc4_13.1) [symbolic = %Inner.type (constants.%Inner.type.e0d)] // CHECK:STDOUT: %Inner.generic: @Outer.%Inner.type (%Inner.type.e0d) = struct_value () [symbolic = %Inner.generic (constants.%Inner.generic.ada)] // CHECK:STDOUT: // CHECK:STDOUT: class { // CHECK:STDOUT: %Inner.decl: @Outer.%Inner.type (%Inner.type.e0d) = class_decl @Inner [symbolic = @Outer.%Inner.generic (constants.%Inner.generic.ada)] { // CHECK:STDOUT: %U.patt: %pattern_type.98f = symbolic_binding_pattern U, 1 [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %.loc5_19.1: type = splice_block %.loc5_19.2 [concrete = type] { // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %.loc5_19.2: type = type_literal type [concrete = type] // CHECK:STDOUT: } // CHECK:STDOUT: %U.loc5_15.2: type = symbolic_binding U, 1 [symbolic = %U.loc5_15.1 (constants.%U)] // CHECK:STDOUT: } // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type.357] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%Outer.387 // CHECK:STDOUT: .Inner = %Inner.decl // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic class @Inner(@Outer.%T.loc4_13.2: type, %U.loc5_15.2: type) { // CHECK:STDOUT: %U.loc5_15.1: type = symbolic_binding U, 1 [symbolic = %U.loc5_15.1 (constants.%U)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: // CHECK:STDOUT: class { // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type.357] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%Inner.e21 // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @C { // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type.357] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%C // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @D { // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type.357] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%D // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic fn @F(%T.loc13_6.2: type, %U.loc13_16.2: type) { // CHECK:STDOUT: %T.loc13_6.1: type = symbolic_binding T, 0 [symbolic = %T.loc13_6.1 (constants.%T)] // CHECK:STDOUT: %U.loc13_16.1: type = symbolic_binding U, 1 [symbolic = %U.loc13_16.1 (constants.%U)] // CHECK:STDOUT: %Outer.loc13_36.1: type = class_type @Outer, @Outer(%T.loc13_6.1) [symbolic = %Outer.loc13_36.1 (constants.%Outer.387)] // CHECK:STDOUT: %require_complete.loc13_37: = require_complete_type %Outer.loc13_36.1 [symbolic = %require_complete.loc13_37 (constants.%require_complete.448)] // CHECK:STDOUT: %Inner.type: type = generic_class_type @Inner, @Outer(%T.loc13_6.1) [symbolic = %Inner.type (constants.%Inner.type.e0d)] // CHECK:STDOUT: %Inner.generic: @F.%Inner.type (%Inner.type.e0d) = struct_value () [symbolic = %Inner.generic (constants.%Inner.generic.ada)] // CHECK:STDOUT: %Inner.loc13_45.1: type = class_type @Inner, @Inner(%T.loc13_6.1, %U.loc13_16.1) [symbolic = %Inner.loc13_45.1 (constants.%Inner.e21)] // CHECK:STDOUT: %pattern_type.loc13_26: type = pattern_type %Inner.loc13_45.1 [symbolic = %pattern_type.loc13_26 (constants.%pattern_type.0d1)] // CHECK:STDOUT: %tuple: %tuple.type.24b = tuple_value (%T.loc13_6.1, %U.loc13_16.1) [symbolic = %tuple (constants.%tuple.4b9)] // CHECK:STDOUT: %tuple.type: type = tuple_type (%T.loc13_6.1, %U.loc13_16.1) [symbolic = %tuple.type (constants.%tuple.type.a5e)] // CHECK:STDOUT: %.loc13_56.2: Core.Form = init_form %tuple.type [symbolic = %.loc13_56.2 (constants.%.f18)] // CHECK:STDOUT: %pattern_type.loc13_48: type = pattern_type %tuple.type [symbolic = %pattern_type.loc13_48 (constants.%pattern_type.eee)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete.loc13_27: = require_complete_type %Inner.loc13_45.1 [symbolic = %require_complete.loc13_27 (constants.%require_complete.41c)] // CHECK:STDOUT: %require_complete.loc13_56: = require_complete_type %tuple.type [symbolic = %require_complete.loc13_56 (constants.%require_complete.220)] // CHECK:STDOUT: %F.specific_fn.loc13_67.2: = specific_function constants.%F, @F(%T.loc13_6.1, %U.loc13_16.1) [symbolic = %F.specific_fn.loc13_67.2 (constants.%F.specific_fn.a54)] // CHECK:STDOUT: // CHECK:STDOUT: fn(%p.param: @F.%Inner.loc13_45.1 (%Inner.e21)) -> out %return.param: @F.%tuple.type (%tuple.type.a5e) { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %F.ref: %F.type = name_ref F, file.%F.decl [concrete = constants.%F] // CHECK:STDOUT: %p.ref: @F.%Inner.loc13_45.1 (%Inner.e21) = name_ref p, %p // CHECK:STDOUT: %F.specific_fn.loc13_67.1: = specific_function %F.ref, @F(constants.%T, constants.%U) [symbolic = %F.specific_fn.loc13_67.2 (constants.%F.specific_fn.a54)] // CHECK:STDOUT: %.loc13_56.1: ref @F.%tuple.type (%tuple.type.a5e) = splice_block %return.param {} // CHECK:STDOUT: %F.call: init @F.%tuple.type (%tuple.type.a5e) to %.loc13_56.1 = call %F.specific_fn.loc13_67.1(%p.ref) // CHECK:STDOUT: return %F.call to %return.param // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @G(%p.param: %Inner.240) -> out %return.param: %tuple.type.281 { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %F.ref: %F.type = name_ref F, file.%F.decl [concrete = constants.%F] // CHECK:STDOUT: %p.ref: %Inner.240 = name_ref p, %p // CHECK:STDOUT: %F.specific_fn: = specific_function %F.ref, @F(constants.%C, constants.%D) [concrete = constants.%F.specific_fn.107] // CHECK:STDOUT: %.loc15_36.1: ref %tuple.type.281 = splice_block %return.param {} // CHECK:STDOUT: %F.call: init %tuple.type.281 to %.loc15_36.1 = call %F.specific_fn(%p.ref) // CHECK:STDOUT: return %F.call to %return.param // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @Outer(constants.%T) { // CHECK:STDOUT: %T.loc4_13.1 => constants.%T // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %Inner.type => constants.%Inner.type.e0d // CHECK:STDOUT: %Inner.generic => constants.%Inner.generic.ada // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @Inner(constants.%T, constants.%U) { // CHECK:STDOUT: %U.loc5_15.1 => constants.%U // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @F(constants.%T, constants.%U) { // CHECK:STDOUT: %T.loc13_6.1 => constants.%T // CHECK:STDOUT: %U.loc13_16.1 => constants.%U // CHECK:STDOUT: %Outer.loc13_36.1 => constants.%Outer.387 // CHECK:STDOUT: %require_complete.loc13_37 => constants.%require_complete.448 // CHECK:STDOUT: %Inner.type => constants.%Inner.type.e0d // CHECK:STDOUT: %Inner.generic => constants.%Inner.generic.ada // CHECK:STDOUT: %Inner.loc13_45.1 => constants.%Inner.e21 // CHECK:STDOUT: %pattern_type.loc13_26 => constants.%pattern_type.0d1 // CHECK:STDOUT: %tuple => constants.%tuple.4b9 // CHECK:STDOUT: %tuple.type => constants.%tuple.type.a5e // CHECK:STDOUT: %.loc13_56.2 => constants.%.f18 // CHECK:STDOUT: %pattern_type.loc13_48 => constants.%pattern_type.eee // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete.loc13_27 => constants.%require_complete.41c // CHECK:STDOUT: %require_complete.loc13_56 => constants.%require_complete.220 // CHECK:STDOUT: %F.specific_fn.loc13_67.2 => constants.%F.specific_fn.a54 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @Outer(constants.%C) { // CHECK:STDOUT: %T.loc4_13.1 => constants.%C // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %Inner.type => constants.%Inner.type.e8c // CHECK:STDOUT: %Inner.generic => constants.%Inner.generic.9a9 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @Inner(constants.%C, constants.%D) { // CHECK:STDOUT: %U.loc5_15.1 => constants.%D // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @F(constants.%C, constants.%D) { // CHECK:STDOUT: %T.loc13_6.1 => constants.%C // CHECK:STDOUT: %U.loc13_16.1 => constants.%D // CHECK:STDOUT: %Outer.loc13_36.1 => constants.%Outer.eed // CHECK:STDOUT: %require_complete.loc13_37 => constants.%complete_type.357 // CHECK:STDOUT: %Inner.type => constants.%Inner.type.e8c // CHECK:STDOUT: %Inner.generic => constants.%Inner.generic.9a9 // CHECK:STDOUT: %Inner.loc13_45.1 => constants.%Inner.240 // CHECK:STDOUT: %pattern_type.loc13_26 => constants.%pattern_type.204 // CHECK:STDOUT: %tuple => constants.%tuple.a0a // CHECK:STDOUT: %tuple.type => constants.%tuple.type.281 // CHECK:STDOUT: %.loc13_56.2 => constants.%.eae // CHECK:STDOUT: %pattern_type.loc13_48 => constants.%pattern_type.881 // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete.loc13_27 => constants.%complete_type.357 // CHECK:STDOUT: %require_complete.loc13_56 => constants.%complete_type.734 // CHECK:STDOUT: %F.specific_fn.loc13_67.2 => constants.%F.specific_fn.107 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- nontype.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %type: type = facet_type [concrete] // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self] // CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [concrete] // CHECK:STDOUT: %Int.type: type = generic_class_type @Int [concrete] // CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] // CHECK:STDOUT: %Int.generic: %Int.type = struct_value () [concrete] // CHECK:STDOUT: %N.fe9: Core.IntLiteral = symbolic_binding N, 0 [symbolic] // CHECK:STDOUT: %i32: type = class_type @Int, @Int(%int_32) [concrete] // CHECK:STDOUT: %pattern_type.7ce: type = pattern_type %i32 [concrete] // CHECK:STDOUT: %N.5de: %i32 = symbolic_binding N, 0 [symbolic] // CHECK:STDOUT: %WithNontype.type: type = generic_class_type @WithNontype [concrete] // CHECK:STDOUT: %WithNontype.generic: %WithNontype.type = struct_value () [concrete] // CHECK:STDOUT: %WithNontype.205: type = class_type @WithNontype, @WithNontype(%N.5de) [symbolic] // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %complete_type.357: = complete_type_witness %empty_struct_type [concrete] // CHECK:STDOUT: %pattern_type.43d: type = pattern_type %WithNontype.205 [symbolic] // CHECK:STDOUT: %.ff5: Core.Form = init_form %i32 [concrete] // CHECK:STDOUT: %F.type: type = fn_type @F [concrete] // CHECK:STDOUT: %F: %F.type = struct_value () [concrete] // CHECK:STDOUT: %require_complete.643: = require_complete_type %WithNontype.205 [symbolic] // CHECK:STDOUT: %Copy.type: type = facet_type <@Copy> [concrete] // CHECK:STDOUT: %Int.as.Copy.impl.Op.type.824: type = fn_type @Int.as.Copy.impl.Op, @Int.as.Copy.impl(%N.fe9) [symbolic] // CHECK:STDOUT: %Int.as.Copy.impl.Op.9b9: %Int.as.Copy.impl.Op.type.824 = struct_value () [symbolic] // CHECK:STDOUT: %Copy.impl_witness.f17: = impl_witness imports.%Copy.impl_witness_table.e76, @Int.as.Copy.impl(%int_32) [concrete] // CHECK:STDOUT: %Int.as.Copy.impl.Op.type.546: type = fn_type @Int.as.Copy.impl.Op, @Int.as.Copy.impl(%int_32) [concrete] // CHECK:STDOUT: %Int.as.Copy.impl.Op.664: %Int.as.Copy.impl.Op.type.546 = struct_value () [concrete] // CHECK:STDOUT: %Copy.facet: %Copy.type = facet_value %i32, (%Copy.impl_witness.f17) [concrete] // CHECK:STDOUT: %Copy.WithSelf.Op.type.081: type = fn_type @Copy.WithSelf.Op, @Copy.WithSelf(%Copy.facet) [concrete] // CHECK:STDOUT: %.8e2: type = fn_type_with_self_type %Copy.WithSelf.Op.type.081, %Copy.facet [concrete] // CHECK:STDOUT: %Int.as.Copy.impl.Op.bound.e78: = bound_method %N.5de, %Int.as.Copy.impl.Op.664 [symbolic] // CHECK:STDOUT: %Int.as.Copy.impl.Op.specific_fn: = specific_function %Int.as.Copy.impl.Op.664, @Int.as.Copy.impl.Op(%int_32) [concrete] // CHECK:STDOUT: %bound_method.207: = bound_method %N.5de, %Int.as.Copy.impl.Op.specific_fn [symbolic] // CHECK:STDOUT: %G.type: type = fn_type @G [concrete] // CHECK:STDOUT: %G: %G.type = struct_value () [concrete] // CHECK:STDOUT: %empty_struct: %empty_struct_type = struct_value () [concrete] // CHECK:STDOUT: %int_0.5c6: Core.IntLiteral = int_value 0 [concrete] // CHECK:STDOUT: %ImplicitAs.type.cc7: type = generic_interface_type @ImplicitAs [concrete] // CHECK:STDOUT: %ImplicitAs.generic: %ImplicitAs.type.cc7 = struct_value () [concrete] // CHECK:STDOUT: %ImplicitAs.type.e8c: type = facet_type <@ImplicitAs, @ImplicitAs(%i32)> [concrete] // CHECK:STDOUT: %To: Core.IntLiteral = symbolic_binding To, 0 [symbolic] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.4e6: type = fn_type @Core.IntLiteral.as.ImplicitAs.impl.Convert, @Core.IntLiteral.as.ImplicitAs.impl(%To) [symbolic] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.3c2: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.4e6 = struct_value () [symbolic] // CHECK:STDOUT: %ImplicitAs.impl_witness.6bc: = impl_witness imports.%ImplicitAs.impl_witness_table.74f, @Core.IntLiteral.as.ImplicitAs.impl(%int_32) [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.e0d: type = fn_type @Core.IntLiteral.as.ImplicitAs.impl.Convert, @Core.IntLiteral.as.ImplicitAs.impl(%int_32) [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.e0d = struct_value () [concrete] // CHECK:STDOUT: %ImplicitAs.facet: %ImplicitAs.type.e8c = facet_value Core.IntLiteral, (%ImplicitAs.impl_witness.6bc) [concrete] // CHECK:STDOUT: %ImplicitAs.WithSelf.Convert.type.b37: type = fn_type @ImplicitAs.WithSelf.Convert, @ImplicitAs.WithSelf(%i32, %ImplicitAs.facet) [concrete] // CHECK:STDOUT: %.545: type = fn_type_with_self_type %ImplicitAs.WithSelf.Convert.type.b37, %ImplicitAs.facet [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.bound: = bound_method %int_0.5c6, %Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5 [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn: = specific_function %Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5, @Core.IntLiteral.as.ImplicitAs.impl.Convert(%int_32) [concrete] // CHECK:STDOUT: %bound_method.d2e: = bound_method %int_0.5c6, %Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn [concrete] // CHECK:STDOUT: %int_0.6a9: %i32 = int_value 0 [concrete] // CHECK:STDOUT: %WithNontype.6bb: type = class_type @WithNontype, @WithNontype(%int_0.6a9) [concrete] // CHECK:STDOUT: %WithNontype.val: %WithNontype.6bb = struct_value () [concrete] // CHECK:STDOUT: %pattern_type.7a0: type = pattern_type %WithNontype.6bb [concrete] // CHECK:STDOUT: %F.specific_fn: = specific_function %F, @F(%int_0.6a9) [concrete] // CHECK:STDOUT: %Destroy.type: type = facet_type <@Destroy> [concrete] // CHECK:STDOUT: %Destroy.Op.type: type = fn_type @Destroy.Op [concrete] // CHECK:STDOUT: %Destroy.Op: %Destroy.Op.type = struct_value () [concrete] // CHECK:STDOUT: %Int.as.Copy.impl.Op.bound.06d: = bound_method %int_0.6a9, %Int.as.Copy.impl.Op.664 [concrete] // CHECK:STDOUT: %bound_method.5f6: = bound_method %int_0.6a9, %Int.as.Copy.impl.Op.specific_fn [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: .Int = %Core.Int // CHECK:STDOUT: .Copy = %Core.Copy // CHECK:STDOUT: .ImplicitAs = %Core.ImplicitAs // CHECK:STDOUT: .Destroy = %Core.Destroy // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: %Core.Int: %Int.type = import_ref Core//prelude/parts/int, Int, loaded [concrete = constants.%Int.generic] // CHECK:STDOUT: %Core.Copy: type = import_ref Core//prelude/parts/copy, Copy, loaded [concrete = constants.%Copy.type] // CHECK:STDOUT: %Core.import_ref.18d: @Int.as.Copy.impl.%Int.as.Copy.impl.Op.type (%Int.as.Copy.impl.Op.type.824) = import_ref Core//prelude/parts/int, loc{{\d+_\d+}}, loaded [symbolic = @Int.as.Copy.impl.%Int.as.Copy.impl.Op (constants.%Int.as.Copy.impl.Op.9b9)] // CHECK:STDOUT: %Copy.impl_witness_table.e76 = impl_witness_table (%Core.import_ref.18d), @Int.as.Copy.impl [concrete] // CHECK:STDOUT: %Core.ImplicitAs: %ImplicitAs.type.cc7 = import_ref Core//prelude/parts/as, ImplicitAs, loaded [concrete = constants.%ImplicitAs.generic] // CHECK:STDOUT: %Core.import_ref.42d: @Core.IntLiteral.as.ImplicitAs.impl.%Core.IntLiteral.as.ImplicitAs.impl.Convert.type (%Core.IntLiteral.as.ImplicitAs.impl.Convert.type.4e6) = import_ref Core//prelude/parts/int, loc{{\d+_\d+}}, loaded [symbolic = @Core.IntLiteral.as.ImplicitAs.impl.%Core.IntLiteral.as.ImplicitAs.impl.Convert (constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.3c2)] // CHECK:STDOUT: %ImplicitAs.impl_witness_table.74f = impl_witness_table (%Core.import_ref.42d), @Core.IntLiteral.as.ImplicitAs.impl [concrete] // CHECK:STDOUT: %Core.Destroy: type = import_ref Core//prelude/parts/destroy, Destroy, loaded [concrete = constants.%Destroy.type] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .WithNontype = %WithNontype.decl // CHECK:STDOUT: .F = %F.decl // CHECK:STDOUT: .G = %G.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %WithNontype.decl: %WithNontype.type = class_decl @WithNontype [concrete = constants.%WithNontype.generic] { // CHECK:STDOUT: %N.patt: %pattern_type.7ce = symbolic_binding_pattern N, 0 [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %.loc4: type = splice_block %i32 [concrete = constants.%i32] { // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %i32: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: } // CHECK:STDOUT: %N.loc4_19.2: %i32 = symbolic_binding N, 0 [symbolic = %N.loc4_19.1 (constants.%N.5de)] // CHECK:STDOUT: } // CHECK:STDOUT: %F.decl: %F.type = fn_decl @F [concrete = constants.%F] { // CHECK:STDOUT: %N.patt: %pattern_type.7ce = symbolic_binding_pattern N, 0 [concrete] // CHECK:STDOUT: %x.patt: @F.%pattern_type (%pattern_type.43d) = value_binding_pattern x [concrete] // CHECK:STDOUT: %x.param_patt: @F.%pattern_type (%pattern_type.43d) = value_param_pattern %x.patt [concrete] // CHECK:STDOUT: %return.patt: %pattern_type.7ce = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: %pattern_type.7ce = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %i32.loc6_44: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %.loc6_44: Core.Form = init_form %i32.loc6_44 [concrete = constants.%.ff5] // CHECK:STDOUT: %.loc6_10: type = splice_block %i32.loc6_10 [concrete = constants.%i32] { // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %i32.loc6_10: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: } // CHECK:STDOUT: %N.loc6_6.2: %i32 = symbolic_binding N, 0 [symbolic = %N.loc6_6.1 (constants.%N.5de)] // CHECK:STDOUT: %x.param: @F.%WithNontype.loc6_38.1 (%WithNontype.205) = value_param call_param0 // CHECK:STDOUT: %.loc6_38: type = splice_block %WithNontype.loc6_38.2 [symbolic = %WithNontype.loc6_38.1 (constants.%WithNontype.205)] { // CHECK:STDOUT: %WithNontype.ref: %WithNontype.type = name_ref WithNontype, file.%WithNontype.decl [concrete = constants.%WithNontype.generic] // CHECK:STDOUT: %N.ref.loc6_37: %i32 = name_ref N, %N.loc6_6.2 [symbolic = %N.loc6_6.1 (constants.%N.5de)] // CHECK:STDOUT: %WithNontype.loc6_38.2: type = class_type @WithNontype, @WithNontype(constants.%N.5de) [symbolic = %WithNontype.loc6_38.1 (constants.%WithNontype.205)] // CHECK:STDOUT: } // CHECK:STDOUT: %x: @F.%WithNontype.loc6_38.1 (%WithNontype.205) = value_binding x, %x.param // CHECK:STDOUT: %return.param: ref %i32 = out_param call_param1 // CHECK:STDOUT: %return: ref %i32 = return_slot %return.param // CHECK:STDOUT: } // CHECK:STDOUT: %G.decl: %G.type = fn_decl @G [concrete = constants.%G] { // CHECK:STDOUT: %return.patt: %pattern_type.7ce = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: %pattern_type.7ce = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %i32: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %.loc8: Core.Form = init_form %i32 [concrete = constants.%.ff5] // CHECK:STDOUT: %return.param: ref %i32 = out_param call_param0 // CHECK:STDOUT: %return: ref %i32 = return_slot %return.param // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic class @WithNontype(%N.loc4_19.2: %i32) { // CHECK:STDOUT: %N.loc4_19.1: %i32 = symbolic_binding N, 0 [symbolic = %N.loc4_19.1 (constants.%N.5de)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: // CHECK:STDOUT: class { // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type.357] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%WithNontype.205 // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic fn @F(%N.loc6_6.2: %i32) { // CHECK:STDOUT: %N.loc6_6.1: %i32 = symbolic_binding N, 0 [symbolic = %N.loc6_6.1 (constants.%N.5de)] // CHECK:STDOUT: %WithNontype.loc6_38.1: type = class_type @WithNontype, @WithNontype(%N.loc6_6.1) [symbolic = %WithNontype.loc6_38.1 (constants.%WithNontype.205)] // CHECK:STDOUT: %pattern_type: type = pattern_type %WithNontype.loc6_38.1 [symbolic = %pattern_type (constants.%pattern_type.43d)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete: = require_complete_type %WithNontype.loc6_38.1 [symbolic = %require_complete (constants.%require_complete.643)] // CHECK:STDOUT: %Int.as.Copy.impl.Op.bound: = bound_method %N.loc6_6.1, constants.%Int.as.Copy.impl.Op.664 [symbolic = %Int.as.Copy.impl.Op.bound (constants.%Int.as.Copy.impl.Op.bound.e78)] // CHECK:STDOUT: %bound_method.loc6_57.3: = bound_method %N.loc6_6.1, constants.%Int.as.Copy.impl.Op.specific_fn [symbolic = %bound_method.loc6_57.3 (constants.%bound_method.207)] // CHECK:STDOUT: // CHECK:STDOUT: fn(%x.param: @F.%WithNontype.loc6_38.1 (%WithNontype.205)) -> out %return.param: %i32 { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %N.ref.loc6_57: %i32 = name_ref N, %N.loc6_6.2 [symbolic = %N.loc6_6.1 (constants.%N.5de)] // CHECK:STDOUT: %impl.elem0: %.8e2 = impl_witness_access constants.%Copy.impl_witness.f17, element0 [concrete = constants.%Int.as.Copy.impl.Op.664] // CHECK:STDOUT: %bound_method.loc6_57.1: = bound_method %N.ref.loc6_57, %impl.elem0 [symbolic = %Int.as.Copy.impl.Op.bound (constants.%Int.as.Copy.impl.Op.bound.e78)] // CHECK:STDOUT: %specific_fn: = specific_function %impl.elem0, @Int.as.Copy.impl.Op(constants.%int_32) [concrete = constants.%Int.as.Copy.impl.Op.specific_fn] // CHECK:STDOUT: %bound_method.loc6_57.2: = bound_method %N.ref.loc6_57, %specific_fn [symbolic = %bound_method.loc6_57.3 (constants.%bound_method.207)] // CHECK:STDOUT: %Int.as.Copy.impl.Op.call: init %i32 = call %bound_method.loc6_57.2(%N.ref.loc6_57) [symbolic = %N.loc6_6.1 (constants.%N.5de)] // CHECK:STDOUT: return %Int.as.Copy.impl.Op.call // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @G() -> out %return.param: %i32 { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %F.ref: %F.type = name_ref F, file.%F.decl [concrete = constants.%F] // CHECK:STDOUT: %.loc9_13.1: %empty_struct_type = struct_literal () [concrete = constants.%empty_struct] // CHECK:STDOUT: %WithNontype.ref: %WithNontype.type = name_ref WithNontype, file.%WithNontype.decl [concrete = constants.%WithNontype.generic] // CHECK:STDOUT: %int_0: Core.IntLiteral = int_value 0 [concrete = constants.%int_0.5c6] // CHECK:STDOUT: %impl.elem0: %.545 = impl_witness_access constants.%ImplicitAs.impl_witness.6bc, element0 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5] // CHECK:STDOUT: %bound_method.loc9_31.1: = bound_method %int_0, %impl.elem0 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.bound] // CHECK:STDOUT: %specific_fn: = specific_function %impl.elem0, @Core.IntLiteral.as.ImplicitAs.impl.Convert(constants.%int_32) [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn] // CHECK:STDOUT: %bound_method.loc9_31.2: = bound_method %int_0, %specific_fn [concrete = constants.%bound_method.d2e] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.call: init %i32 = call %bound_method.loc9_31.2(%int_0) [concrete = constants.%int_0.6a9] // CHECK:STDOUT: %.loc9_31.1: %i32 = value_of_initializer %Core.IntLiteral.as.ImplicitAs.impl.Convert.call [concrete = constants.%int_0.6a9] // CHECK:STDOUT: %.loc9_31.2: %i32 = converted %int_0, %.loc9_31.1 [concrete = constants.%int_0.6a9] // CHECK:STDOUT: %WithNontype: type = class_type @WithNontype, @WithNontype(constants.%int_0.6a9) [concrete = constants.%WithNontype.6bb] // CHECK:STDOUT: %.loc9_13.2: ref %WithNontype.6bb = temporary_storage // CHECK:STDOUT: %.loc9_13.3: init %WithNontype.6bb to %.loc9_13.2 = class_init () [concrete = constants.%WithNontype.val] // CHECK:STDOUT: %.loc9_15.1: init %WithNontype.6bb = converted %.loc9_13.1, %.loc9_13.3 [concrete = constants.%WithNontype.val] // CHECK:STDOUT: %F.specific_fn: = specific_function %F.ref, @F(constants.%int_0.6a9) [concrete = constants.%F.specific_fn] // CHECK:STDOUT: %.loc9_15.2: ref %WithNontype.6bb = temporary %.loc9_13.2, %.loc9_15.1 // CHECK:STDOUT: %.loc9_15.3: %WithNontype.6bb = acquire_value %.loc9_15.2 // CHECK:STDOUT: %F.call: init %i32 = call %F.specific_fn(%.loc9_15.3) // CHECK:STDOUT: %Destroy.Op.bound: = bound_method %.loc9_15.2, constants.%Destroy.Op // CHECK:STDOUT: %Destroy.Op.call: init %empty_tuple.type = call %Destroy.Op.bound(%.loc9_15.2) // CHECK:STDOUT: return %F.call // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @Destroy.Op(%self.param: ref %WithNontype.6bb) = "no_op"; // CHECK:STDOUT: // CHECK:STDOUT: specific @WithNontype(constants.%N.5de) { // CHECK:STDOUT: %N.loc4_19.1 => constants.%N.5de // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @F(constants.%N.5de) { // CHECK:STDOUT: %N.loc6_6.1 => constants.%N.5de // CHECK:STDOUT: %WithNontype.loc6_38.1 => constants.%WithNontype.205 // CHECK:STDOUT: %pattern_type => constants.%pattern_type.43d // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @WithNontype(constants.%int_0.6a9) { // CHECK:STDOUT: %N.loc4_19.1 => constants.%int_0.6a9 // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @F(constants.%int_0.6a9) { // CHECK:STDOUT: %N.loc6_6.1 => constants.%int_0.6a9 // CHECK:STDOUT: %WithNontype.loc6_38.1 => constants.%WithNontype.6bb // CHECK:STDOUT: %pattern_type => constants.%pattern_type.7a0 // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete => constants.%complete_type.357 // CHECK:STDOUT: %Int.as.Copy.impl.Op.bound => constants.%Int.as.Copy.impl.Op.bound.06d // CHECK:STDOUT: %bound_method.loc6_57.3 => constants.%bound_method.5f6 // CHECK:STDOUT: } // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/deduce/int_float.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/primitives.carbon // TODO: Add ranges and switch to "--dump-sem-ir-ranges=only". // EXTRA-ARGS: --dump-sem-ir-ranges=if-present // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/deduce/int_float.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/deduce/int_float.carbon // --- int.carbon library "[[@TEST_NAME]]"; fn F[N:! Core.IntLiteral()](unused n: Core.Int(N)) -> Core.IntLiteral() { return N; } fn G(a: i64) -> Core.IntLiteral() { return F(a); } // --- float.carbon library "[[@TEST_NAME]]"; fn F[N:! Core.IntLiteral()](unused n: Core.Float(N)) -> Core.IntLiteral() { return N; } fn G(a: f64) -> Core.IntLiteral() { return F(a); } // CHECK:STDOUT: --- int.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %type: type = facet_type [concrete] // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self] // CHECK:STDOUT: %IntLiteral.type: type = fn_type @IntLiteral [concrete] // CHECK:STDOUT: %IntLiteral: %IntLiteral.type = struct_value () [concrete] // CHECK:STDOUT: %pattern_type.dc0: type = pattern_type Core.IntLiteral [concrete] // CHECK:STDOUT: %N: Core.IntLiteral = symbolic_binding N, 0 [symbolic] // CHECK:STDOUT: %Int.type: type = generic_class_type @Int [concrete] // CHECK:STDOUT: %Int.generic: %Int.type = struct_value () [concrete] // CHECK:STDOUT: %Int: type = class_type @Int, @Int(%N) [symbolic] // CHECK:STDOUT: %pattern_type.764: type = pattern_type %Int [symbolic] // CHECK:STDOUT: %.f7d: Core.Form = init_form Core.IntLiteral [concrete] // CHECK:STDOUT: %F.type: type = fn_type @F [concrete] // CHECK:STDOUT: %F: %F.type = struct_value () [concrete] // CHECK:STDOUT: %require_complete.901: = require_complete_type %Int [symbolic] // CHECK:STDOUT: %Copy.type: type = facet_type <@Copy> [concrete] // CHECK:STDOUT: %Copy.impl_witness.98e: = impl_witness imports.%Copy.impl_witness_table.d8f [concrete] // CHECK:STDOUT: %Copy.facet: %Copy.type = facet_value Core.IntLiteral, (%Copy.impl_witness.98e) [concrete] // CHECK:STDOUT: %Copy.WithSelf.Op.type.42e: type = fn_type @Copy.WithSelf.Op, @Copy.WithSelf(%Copy.facet) [concrete] // CHECK:STDOUT: %.5e6: type = fn_type_with_self_type %Copy.WithSelf.Op.type.42e, %Copy.facet [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.Copy.impl.Op.type: type = fn_type @Core.IntLiteral.as.Copy.impl.Op [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.Copy.impl.Op: %Core.IntLiteral.as.Copy.impl.Op.type = struct_value () [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.Copy.impl.Op.bound.555: = bound_method %N, %Core.IntLiteral.as.Copy.impl.Op [symbolic] // CHECK:STDOUT: %int_64: Core.IntLiteral = int_value 64 [concrete] // CHECK:STDOUT: %i64: type = class_type @Int, @Int(%int_64) [concrete] // CHECK:STDOUT: %pattern_type.95b: type = pattern_type %i64 [concrete] // CHECK:STDOUT: %G.type: type = fn_type @G [concrete] // CHECK:STDOUT: %G: %G.type = struct_value () [concrete] // CHECK:STDOUT: %i64.builtin: type = int_type signed, %int_64 [concrete] // CHECK:STDOUT: %complete_type.4a1: = complete_type_witness %i64.builtin [concrete] // CHECK:STDOUT: %F.specific_fn: = specific_function %F, @F(%int_64) [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.Copy.impl.Op.bound.04a: = bound_method %int_64, %Core.IntLiteral.as.Copy.impl.Op [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: .IntLiteral = %Core.IntLiteral // CHECK:STDOUT: .Int = %Core.Int // CHECK:STDOUT: .Copy = %Core.Copy // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: %Core.IntLiteral: %IntLiteral.type = import_ref Core//prelude/parts/int_literal, IntLiteral, loaded [concrete = constants.%IntLiteral] // CHECK:STDOUT: %Core.Int: %Int.type = import_ref Core//prelude/parts/int, Int, loaded [concrete = constants.%Int.generic] // CHECK:STDOUT: %Core.Copy: type = import_ref Core//prelude/parts/copy, Copy, loaded [concrete = constants.%Copy.type] // CHECK:STDOUT: %Core.import_ref.66a: %Core.IntLiteral.as.Copy.impl.Op.type = import_ref Core//prelude/parts/copy, loc{{\d+_\d+}}, loaded [concrete = constants.%Core.IntLiteral.as.Copy.impl.Op] // CHECK:STDOUT: %Copy.impl_witness_table.d8f = impl_witness_table (%Core.import_ref.66a), @Core.IntLiteral.as.Copy.impl [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .F = %F.decl // CHECK:STDOUT: .G = %G.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %F.decl: %F.type = fn_decl @F [concrete = constants.%F] { // CHECK:STDOUT: %N.patt: %pattern_type.dc0 = symbolic_binding_pattern N, 0 [concrete] // CHECK:STDOUT: %n.patt: @F.%pattern_type (%pattern_type.764) = value_binding_pattern n [concrete] // CHECK:STDOUT: %n.param_patt: @F.%pattern_type (%pattern_type.764) = value_param_pattern %n.patt [concrete] // CHECK:STDOUT: %return.patt: %pattern_type.dc0 = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: %pattern_type.dc0 = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %Core.ref.loc4_55: = name_ref Core, imports.%Core [concrete = imports.%Core] // CHECK:STDOUT: %IntLiteral.ref.loc4_59: %IntLiteral.type = name_ref IntLiteral, imports.%Core.IntLiteral [concrete = constants.%IntLiteral] // CHECK:STDOUT: %IntLiteral.call.loc4_71: init type = call %IntLiteral.ref.loc4_59() [concrete = Core.IntLiteral] // CHECK:STDOUT: %.loc4_71.1: type = value_of_initializer %IntLiteral.call.loc4_71 [concrete = Core.IntLiteral] // CHECK:STDOUT: %.loc4_71.2: type = converted %IntLiteral.call.loc4_71, %.loc4_71.1 [concrete = Core.IntLiteral] // CHECK:STDOUT: %.loc4_71.3: Core.Form = init_form %.loc4_71.2 [concrete = constants.%.f7d] // CHECK:STDOUT: %.loc4_26.1: type = splice_block %.loc4_26.3 [concrete = Core.IntLiteral] { // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %Core.ref.loc4_10: = name_ref Core, imports.%Core [concrete = imports.%Core] // CHECK:STDOUT: %IntLiteral.ref.loc4_14: %IntLiteral.type = name_ref IntLiteral, imports.%Core.IntLiteral [concrete = constants.%IntLiteral] // CHECK:STDOUT: %IntLiteral.call.loc4_26: init type = call %IntLiteral.ref.loc4_14() [concrete = Core.IntLiteral] // CHECK:STDOUT: %.loc4_26.2: type = value_of_initializer %IntLiteral.call.loc4_26 [concrete = Core.IntLiteral] // CHECK:STDOUT: %.loc4_26.3: type = converted %IntLiteral.call.loc4_26, %.loc4_26.2 [concrete = Core.IntLiteral] // CHECK:STDOUT: } // CHECK:STDOUT: %N.loc4_6.2: Core.IntLiteral = symbolic_binding N, 0 [symbolic = %N.loc4_6.1 (constants.%N)] // CHECK:STDOUT: %n.param: @F.%Int.loc4_49.1 (%Int) = value_param call_param0 // CHECK:STDOUT: %.loc4_49: type = splice_block %Int.loc4_49.2 [symbolic = %Int.loc4_49.1 (constants.%Int)] { // CHECK:STDOUT: %Core.ref.loc4_39: = name_ref Core, imports.%Core [concrete = imports.%Core] // CHECK:STDOUT: %Int.ref: %Int.type = name_ref Int, imports.%Core.Int [concrete = constants.%Int.generic] // CHECK:STDOUT: %N.ref.loc4: Core.IntLiteral = name_ref N, %N.loc4_6.2 [symbolic = %N.loc4_6.1 (constants.%N)] // CHECK:STDOUT: %Int.loc4_49.2: type = class_type @Int, @Int(constants.%N) [symbolic = %Int.loc4_49.1 (constants.%Int)] // CHECK:STDOUT: } // CHECK:STDOUT: %n: @F.%Int.loc4_49.1 (%Int) = value_binding n, %n.param // CHECK:STDOUT: %return.param: ref Core.IntLiteral = out_param call_param1 // CHECK:STDOUT: %return: ref Core.IntLiteral = return_slot %return.param // CHECK:STDOUT: } // CHECK:STDOUT: %G.decl: %G.type = fn_decl @G [concrete = constants.%G] { // CHECK:STDOUT: %a.patt: %pattern_type.95b = value_binding_pattern a [concrete] // CHECK:STDOUT: %a.param_patt: %pattern_type.95b = value_param_pattern %a.patt [concrete] // CHECK:STDOUT: %return.patt: %pattern_type.dc0 = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: %pattern_type.dc0 = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %Core.ref: = name_ref Core, imports.%Core [concrete = imports.%Core] // CHECK:STDOUT: %IntLiteral.ref: %IntLiteral.type = name_ref IntLiteral, imports.%Core.IntLiteral [concrete = constants.%IntLiteral] // CHECK:STDOUT: %IntLiteral.call: init type = call %IntLiteral.ref() [concrete = Core.IntLiteral] // CHECK:STDOUT: %.loc8_33.1: type = value_of_initializer %IntLiteral.call [concrete = Core.IntLiteral] // CHECK:STDOUT: %.loc8_33.2: type = converted %IntLiteral.call, %.loc8_33.1 [concrete = Core.IntLiteral] // CHECK:STDOUT: %.loc8_33.3: Core.Form = init_form %.loc8_33.2 [concrete = constants.%.f7d] // CHECK:STDOUT: %a.param: %i64 = value_param call_param0 // CHECK:STDOUT: %i64: type = type_literal constants.%i64 [concrete = constants.%i64] // CHECK:STDOUT: %a: %i64 = value_binding a, %a.param // CHECK:STDOUT: %return.param: ref Core.IntLiteral = out_param call_param1 // CHECK:STDOUT: %return: ref Core.IntLiteral = return_slot %return.param // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic fn @F(%N.loc4_6.2: Core.IntLiteral) { // CHECK:STDOUT: %N.loc4_6.1: Core.IntLiteral = symbolic_binding N, 0 [symbolic = %N.loc4_6.1 (constants.%N)] // CHECK:STDOUT: %Int.loc4_49.1: type = class_type @Int, @Int(%N.loc4_6.1) [symbolic = %Int.loc4_49.1 (constants.%Int)] // CHECK:STDOUT: %pattern_type: type = pattern_type %Int.loc4_49.1 [symbolic = %pattern_type (constants.%pattern_type.764)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete: = require_complete_type %Int.loc4_49.1 [symbolic = %require_complete (constants.%require_complete.901)] // CHECK:STDOUT: %Core.IntLiteral.as.Copy.impl.Op.bound: = bound_method %N.loc4_6.1, constants.%Core.IntLiteral.as.Copy.impl.Op [symbolic = %Core.IntLiteral.as.Copy.impl.Op.bound (constants.%Core.IntLiteral.as.Copy.impl.Op.bound.555)] // CHECK:STDOUT: // CHECK:STDOUT: fn(%n.param: @F.%Int.loc4_49.1 (%Int)) -> out %return.param: Core.IntLiteral { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %N.ref.loc5: Core.IntLiteral = name_ref N, %N.loc4_6.2 [symbolic = %N.loc4_6.1 (constants.%N)] // CHECK:STDOUT: %impl.elem0: %.5e6 = impl_witness_access constants.%Copy.impl_witness.98e, element0 [concrete = constants.%Core.IntLiteral.as.Copy.impl.Op] // CHECK:STDOUT: %bound_method: = bound_method %N.ref.loc5, %impl.elem0 [symbolic = %Core.IntLiteral.as.Copy.impl.Op.bound (constants.%Core.IntLiteral.as.Copy.impl.Op.bound.555)] // CHECK:STDOUT: %Core.IntLiteral.as.Copy.impl.Op.call: init Core.IntLiteral = call %bound_method(%N.ref.loc5) [symbolic = %N.loc4_6.1 (constants.%N)] // CHECK:STDOUT: return %Core.IntLiteral.as.Copy.impl.Op.call // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @G(%a.param: %i64) -> out %return.param: Core.IntLiteral { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %F.ref: %F.type = name_ref F, file.%F.decl [concrete = constants.%F] // CHECK:STDOUT: %a.ref: %i64 = name_ref a, %a // CHECK:STDOUT: %F.specific_fn: = specific_function %F.ref, @F(constants.%int_64) [concrete = constants.%F.specific_fn] // CHECK:STDOUT: %F.call: init Core.IntLiteral = call %F.specific_fn(%a.ref) // CHECK:STDOUT: return %F.call // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @F(constants.%N) { // CHECK:STDOUT: %N.loc4_6.1 => constants.%N // CHECK:STDOUT: %Int.loc4_49.1 => constants.%Int // CHECK:STDOUT: %pattern_type => constants.%pattern_type.764 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @F(constants.%int_64) { // CHECK:STDOUT: %N.loc4_6.1 => constants.%int_64 // CHECK:STDOUT: %Int.loc4_49.1 => constants.%i64 // CHECK:STDOUT: %pattern_type => constants.%pattern_type.95b // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete => constants.%complete_type.4a1 // CHECK:STDOUT: %Core.IntLiteral.as.Copy.impl.Op.bound => constants.%Core.IntLiteral.as.Copy.impl.Op.bound.04a // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- float.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %type: type = facet_type [concrete] // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self] // CHECK:STDOUT: %IntLiteral.type: type = fn_type @IntLiteral [concrete] // CHECK:STDOUT: %IntLiteral: %IntLiteral.type = struct_value () [concrete] // CHECK:STDOUT: %pattern_type.dc0: type = pattern_type Core.IntLiteral [concrete] // CHECK:STDOUT: %N: Core.IntLiteral = symbolic_binding N, 0 [symbolic] // CHECK:STDOUT: %Float.type: type = generic_class_type @Float [concrete] // CHECK:STDOUT: %Float.generic: %Float.type = struct_value () [concrete] // CHECK:STDOUT: %Float: type = class_type @Float, @Float(%N) [symbolic] // CHECK:STDOUT: %pattern_type.7d0: type = pattern_type %Float [symbolic] // CHECK:STDOUT: %.f7d: Core.Form = init_form Core.IntLiteral [concrete] // CHECK:STDOUT: %F.type: type = fn_type @F [concrete] // CHECK:STDOUT: %F: %F.type = struct_value () [concrete] // CHECK:STDOUT: %require_complete.dc0: = require_complete_type %Float [symbolic] // CHECK:STDOUT: %Copy.type: type = facet_type <@Copy> [concrete] // CHECK:STDOUT: %Copy.impl_witness.98e: = impl_witness imports.%Copy.impl_witness_table.d8f [concrete] // CHECK:STDOUT: %Copy.facet: %Copy.type = facet_value Core.IntLiteral, (%Copy.impl_witness.98e) [concrete] // CHECK:STDOUT: %Copy.WithSelf.Op.type.42e: type = fn_type @Copy.WithSelf.Op, @Copy.WithSelf(%Copy.facet) [concrete] // CHECK:STDOUT: %.5e6: type = fn_type_with_self_type %Copy.WithSelf.Op.type.42e, %Copy.facet [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.Copy.impl.Op.type: type = fn_type @Core.IntLiteral.as.Copy.impl.Op [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.Copy.impl.Op: %Core.IntLiteral.as.Copy.impl.Op.type = struct_value () [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.Copy.impl.Op.bound.555: = bound_method %N, %Core.IntLiteral.as.Copy.impl.Op [symbolic] // CHECK:STDOUT: %int_64: Core.IntLiteral = int_value 64 [concrete] // CHECK:STDOUT: %f64.d77: type = class_type @Float, @Float(%int_64) [concrete] // CHECK:STDOUT: %pattern_type.0ae: type = pattern_type %f64.d77 [concrete] // CHECK:STDOUT: %G.type: type = fn_type @G [concrete] // CHECK:STDOUT: %G: %G.type = struct_value () [concrete] // CHECK:STDOUT: %f64.794: type = float_type %int_64, f64 [concrete] // CHECK:STDOUT: %complete_type.7b9: = complete_type_witness %f64.794 [concrete] // CHECK:STDOUT: %F.specific_fn: = specific_function %F, @F(%int_64) [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.Copy.impl.Op.bound.04a: = bound_method %int_64, %Core.IntLiteral.as.Copy.impl.Op [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: .IntLiteral = %Core.IntLiteral // CHECK:STDOUT: .Float = %Core.Float // CHECK:STDOUT: .Copy = %Core.Copy // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: %Core.IntLiteral: %IntLiteral.type = import_ref Core//prelude/parts/int_literal, IntLiteral, loaded [concrete = constants.%IntLiteral] // CHECK:STDOUT: %Core.Float: %Float.type = import_ref Core//prelude/parts/float, Float, loaded [concrete = constants.%Float.generic] // CHECK:STDOUT: %Core.Copy: type = import_ref Core//prelude/parts/copy, Copy, loaded [concrete = constants.%Copy.type] // CHECK:STDOUT: %Core.import_ref.66a: %Core.IntLiteral.as.Copy.impl.Op.type = import_ref Core//prelude/parts/copy, loc{{\d+_\d+}}, loaded [concrete = constants.%Core.IntLiteral.as.Copy.impl.Op] // CHECK:STDOUT: %Copy.impl_witness_table.d8f = impl_witness_table (%Core.import_ref.66a), @Core.IntLiteral.as.Copy.impl [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .F = %F.decl // CHECK:STDOUT: .G = %G.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %F.decl: %F.type = fn_decl @F [concrete = constants.%F] { // CHECK:STDOUT: %N.patt: %pattern_type.dc0 = symbolic_binding_pattern N, 0 [concrete] // CHECK:STDOUT: %n.patt: @F.%pattern_type (%pattern_type.7d0) = value_binding_pattern n [concrete] // CHECK:STDOUT: %n.param_patt: @F.%pattern_type (%pattern_type.7d0) = value_param_pattern %n.patt [concrete] // CHECK:STDOUT: %return.patt: %pattern_type.dc0 = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: %pattern_type.dc0 = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %Core.ref.loc4_57: = name_ref Core, imports.%Core [concrete = imports.%Core] // CHECK:STDOUT: %IntLiteral.ref.loc4_61: %IntLiteral.type = name_ref IntLiteral, imports.%Core.IntLiteral [concrete = constants.%IntLiteral] // CHECK:STDOUT: %IntLiteral.call.loc4_73: init type = call %IntLiteral.ref.loc4_61() [concrete = Core.IntLiteral] // CHECK:STDOUT: %.loc4_73.1: type = value_of_initializer %IntLiteral.call.loc4_73 [concrete = Core.IntLiteral] // CHECK:STDOUT: %.loc4_73.2: type = converted %IntLiteral.call.loc4_73, %.loc4_73.1 [concrete = Core.IntLiteral] // CHECK:STDOUT: %.loc4_73.3: Core.Form = init_form %.loc4_73.2 [concrete = constants.%.f7d] // CHECK:STDOUT: %.loc4_26.1: type = splice_block %.loc4_26.3 [concrete = Core.IntLiteral] { // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %Core.ref.loc4_10: = name_ref Core, imports.%Core [concrete = imports.%Core] // CHECK:STDOUT: %IntLiteral.ref.loc4_14: %IntLiteral.type = name_ref IntLiteral, imports.%Core.IntLiteral [concrete = constants.%IntLiteral] // CHECK:STDOUT: %IntLiteral.call.loc4_26: init type = call %IntLiteral.ref.loc4_14() [concrete = Core.IntLiteral] // CHECK:STDOUT: %.loc4_26.2: type = value_of_initializer %IntLiteral.call.loc4_26 [concrete = Core.IntLiteral] // CHECK:STDOUT: %.loc4_26.3: type = converted %IntLiteral.call.loc4_26, %.loc4_26.2 [concrete = Core.IntLiteral] // CHECK:STDOUT: } // CHECK:STDOUT: %N.loc4_6.2: Core.IntLiteral = symbolic_binding N, 0 [symbolic = %N.loc4_6.1 (constants.%N)] // CHECK:STDOUT: %n.param: @F.%Float.loc4_51.1 (%Float) = value_param call_param0 // CHECK:STDOUT: %.loc4_51: type = splice_block %Float.loc4_51.2 [symbolic = %Float.loc4_51.1 (constants.%Float)] { // CHECK:STDOUT: %Core.ref.loc4_39: = name_ref Core, imports.%Core [concrete = imports.%Core] // CHECK:STDOUT: %Float.ref: %Float.type = name_ref Float, imports.%Core.Float [concrete = constants.%Float.generic] // CHECK:STDOUT: %N.ref.loc4: Core.IntLiteral = name_ref N, %N.loc4_6.2 [symbolic = %N.loc4_6.1 (constants.%N)] // CHECK:STDOUT: %Float.loc4_51.2: type = class_type @Float, @Float(constants.%N) [symbolic = %Float.loc4_51.1 (constants.%Float)] // CHECK:STDOUT: } // CHECK:STDOUT: %n: @F.%Float.loc4_51.1 (%Float) = value_binding n, %n.param // CHECK:STDOUT: %return.param: ref Core.IntLiteral = out_param call_param1 // CHECK:STDOUT: %return: ref Core.IntLiteral = return_slot %return.param // CHECK:STDOUT: } // CHECK:STDOUT: %G.decl: %G.type = fn_decl @G [concrete = constants.%G] { // CHECK:STDOUT: %a.patt: %pattern_type.0ae = value_binding_pattern a [concrete] // CHECK:STDOUT: %a.param_patt: %pattern_type.0ae = value_param_pattern %a.patt [concrete] // CHECK:STDOUT: %return.patt: %pattern_type.dc0 = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: %pattern_type.dc0 = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %Core.ref: = name_ref Core, imports.%Core [concrete = imports.%Core] // CHECK:STDOUT: %IntLiteral.ref: %IntLiteral.type = name_ref IntLiteral, imports.%Core.IntLiteral [concrete = constants.%IntLiteral] // CHECK:STDOUT: %IntLiteral.call: init type = call %IntLiteral.ref() [concrete = Core.IntLiteral] // CHECK:STDOUT: %.loc8_33.1: type = value_of_initializer %IntLiteral.call [concrete = Core.IntLiteral] // CHECK:STDOUT: %.loc8_33.2: type = converted %IntLiteral.call, %.loc8_33.1 [concrete = Core.IntLiteral] // CHECK:STDOUT: %.loc8_33.3: Core.Form = init_form %.loc8_33.2 [concrete = constants.%.f7d] // CHECK:STDOUT: %a.param: %f64.d77 = value_param call_param0 // CHECK:STDOUT: %f64: type = type_literal constants.%f64.d77 [concrete = constants.%f64.d77] // CHECK:STDOUT: %a: %f64.d77 = value_binding a, %a.param // CHECK:STDOUT: %return.param: ref Core.IntLiteral = out_param call_param1 // CHECK:STDOUT: %return: ref Core.IntLiteral = return_slot %return.param // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic fn @F(%N.loc4_6.2: Core.IntLiteral) { // CHECK:STDOUT: %N.loc4_6.1: Core.IntLiteral = symbolic_binding N, 0 [symbolic = %N.loc4_6.1 (constants.%N)] // CHECK:STDOUT: %Float.loc4_51.1: type = class_type @Float, @Float(%N.loc4_6.1) [symbolic = %Float.loc4_51.1 (constants.%Float)] // CHECK:STDOUT: %pattern_type: type = pattern_type %Float.loc4_51.1 [symbolic = %pattern_type (constants.%pattern_type.7d0)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete: = require_complete_type %Float.loc4_51.1 [symbolic = %require_complete (constants.%require_complete.dc0)] // CHECK:STDOUT: %Core.IntLiteral.as.Copy.impl.Op.bound: = bound_method %N.loc4_6.1, constants.%Core.IntLiteral.as.Copy.impl.Op [symbolic = %Core.IntLiteral.as.Copy.impl.Op.bound (constants.%Core.IntLiteral.as.Copy.impl.Op.bound.555)] // CHECK:STDOUT: // CHECK:STDOUT: fn(%n.param: @F.%Float.loc4_51.1 (%Float)) -> out %return.param: Core.IntLiteral { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %N.ref.loc5: Core.IntLiteral = name_ref N, %N.loc4_6.2 [symbolic = %N.loc4_6.1 (constants.%N)] // CHECK:STDOUT: %impl.elem0: %.5e6 = impl_witness_access constants.%Copy.impl_witness.98e, element0 [concrete = constants.%Core.IntLiteral.as.Copy.impl.Op] // CHECK:STDOUT: %bound_method: = bound_method %N.ref.loc5, %impl.elem0 [symbolic = %Core.IntLiteral.as.Copy.impl.Op.bound (constants.%Core.IntLiteral.as.Copy.impl.Op.bound.555)] // CHECK:STDOUT: %Core.IntLiteral.as.Copy.impl.Op.call: init Core.IntLiteral = call %bound_method(%N.ref.loc5) [symbolic = %N.loc4_6.1 (constants.%N)] // CHECK:STDOUT: return %Core.IntLiteral.as.Copy.impl.Op.call // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @G(%a.param: %f64.d77) -> out %return.param: Core.IntLiteral { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %F.ref: %F.type = name_ref F, file.%F.decl [concrete = constants.%F] // CHECK:STDOUT: %a.ref: %f64.d77 = name_ref a, %a // CHECK:STDOUT: %F.specific_fn: = specific_function %F.ref, @F(constants.%int_64) [concrete = constants.%F.specific_fn] // CHECK:STDOUT: %F.call: init Core.IntLiteral = call %F.specific_fn(%a.ref) // CHECK:STDOUT: return %F.call // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @F(constants.%N) { // CHECK:STDOUT: %N.loc4_6.1 => constants.%N // CHECK:STDOUT: %Float.loc4_51.1 => constants.%Float // CHECK:STDOUT: %pattern_type => constants.%pattern_type.7d0 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @F(constants.%int_64) { // CHECK:STDOUT: %N.loc4_6.1 => constants.%int_64 // CHECK:STDOUT: %Float.loc4_51.1 => constants.%f64.d77 // CHECK:STDOUT: %pattern_type => constants.%pattern_type.0ae // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete => constants.%complete_type.7b9 // CHECK:STDOUT: %Core.IntLiteral.as.Copy.impl.Op.bound => constants.%Core.IntLiteral.as.Copy.impl.Op.bound.04a // CHECK:STDOUT: } // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/deduce/symbolic_facets.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/convert.carbon // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/deduce/symbolic_facets.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/deduce/symbolic_facets.carbon // By placing each interface inside a generic class, a facet type refering to // the interface becomes symbolic. Normally they would be concrete. This can // affect decisions in deduce which unwraps symbolic constants, but still needs to // ensure they convert correctly. // --- fail_missing_interface.carbon library "[[@TEST_NAME]]"; class C(CC:! type) { interface A {} fn F(unused T:! A) {} } class D(DD:! type) { interface B {} fn G(T:! B) { // T only implements D(DD).B, so should not convert to a facet value // implementing C(()).A. // // CHECK:STDERR: fail_missing_interface.carbon:[[@LINE+7]]:5: error: cannot convert type `T` that implements `B` into type implementing `A` [ConversionFailureFacetToFacet] // CHECK:STDERR: C(()).F(T); // CHECK:STDERR: ^~~~~~~~~~ // CHECK:STDERR: fail_missing_interface.carbon:[[@LINE-12]]:3: note: while deducing parameters of generic declared here [DeductionGenericHere] // CHECK:STDERR: fn F(unused T:! A) {} // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: C(()).F(T); } } // --- fail_interface_wrong_generic_param.carbon library "[[@TEST_NAME]]"; class C(CC:! type) { interface A {} fn F(unused T:! A) {} } class D(DD:! type) { interface B {} fn G(T:! B & C({}).A) { // T implements C({}).A and D(DD).B, so should not convert to a facet value // implementing C(()).A. // // CHECK:STDERR: fail_interface_wrong_generic_param.carbon:[[@LINE+7]]:5: error: cannot convert type `T` that implements `A & B` into type implementing `A` [ConversionFailureFacetToFacet] // CHECK:STDERR: C(()).F(T); // CHECK:STDERR: ^~~~~~~~~~ // CHECK:STDERR: fail_interface_wrong_generic_param.carbon:[[@LINE-12]]:3: note: while deducing parameters of generic declared here [DeductionGenericHere] // CHECK:STDERR: fn F(unused T:! A) {} // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: C(()).F(T); } } // --- compatible_deduce.carbon library "[[@TEST_NAME]]"; class C(CC:! type) { interface A {} fn F(unused T:! A) {} } class D(DD:! type) { interface B {} fn G(T:! B & C(()).A) { C(()).F(T); } } ================================================ FILE: toolchain/check/testdata/deduce/tuple.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/int.carbon // TODO: Add ranges and switch to "--dump-sem-ir-ranges=only". // EXTRA-ARGS: --dump-sem-ir-ranges=if-present // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/deduce/tuple.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/deduce/tuple.carbon // --- tuple_type.carbon library "[[@TEST_NAME]]"; class C {} class D {} fn F[T:! type, U:! type](pair: (T, U)) -> U { return F(pair); } fn G(pair: (C, D)) -> D { return F(pair); } // --- tuple_value.carbon library "[[@TEST_NAME]]"; class HasPair(Pair:! (i32, i32)) {} fn F[A:! i32, B:! i32](unused h: HasPair((A, B))) -> i32 { return B; } fn G(h: HasPair((1, 2))) -> i32 { return F(h); } // --- fail_inconsistent.carbon library "[[@TEST_NAME]]"; class C {} class D {} fn F[T:! type](pair: (T, T)) -> T; fn G(pair: (C, D)) -> D { // CHECK:STDERR: fail_inconsistent.carbon:[[@LINE+7]]:10: error: inconsistent deductions for value of generic parameter `T` [DeductionInconsistent] // CHECK:STDERR: return F(pair); // CHECK:STDERR: ^~~~~~~ // CHECK:STDERR: fail_inconsistent.carbon:[[@LINE-6]]:1: note: while deducing parameters of generic declared here [DeductionGenericHere] // CHECK:STDERR: fn F[T:! type](pair: (T, T)) -> T; // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: return F(pair); } // CHECK:STDOUT: --- tuple_type.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %C: type = class_type @C [concrete] // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %complete_type.357: = complete_type_witness %empty_struct_type [concrete] // CHECK:STDOUT: %D: type = class_type @D [concrete] // CHECK:STDOUT: %type: type = facet_type [concrete] // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self] // CHECK:STDOUT: %pattern_type.98f: type = pattern_type type [concrete] // CHECK:STDOUT: %T: type = symbolic_binding T, 0 [symbolic] // CHECK:STDOUT: %U: type = symbolic_binding U, 1 [symbolic] // CHECK:STDOUT: %tuple.type.24b: type = tuple_type (type, type) [concrete] // CHECK:STDOUT: %tuple.4b9: %tuple.type.24b = tuple_value (%T, %U) [symbolic] // CHECK:STDOUT: %tuple.type.a5e: type = tuple_type (%T, %U) [symbolic] // CHECK:STDOUT: %pattern_type.eee: type = pattern_type %tuple.type.a5e [symbolic] // CHECK:STDOUT: %.822: Core.Form = init_form %U [symbolic] // CHECK:STDOUT: %pattern_type.946: type = pattern_type %U [symbolic] // CHECK:STDOUT: %F.type: type = fn_type @F [concrete] // CHECK:STDOUT: %F: %F.type = struct_value () [concrete] // CHECK:STDOUT: %require_complete: = require_complete_type %tuple.type.a5e [symbolic] // CHECK:STDOUT: %F.specific_fn.a54: = specific_function %F, @F(%T, %U) [symbolic] // CHECK:STDOUT: %tuple.a0a: %tuple.type.24b = tuple_value (%C, %D) [concrete] // CHECK:STDOUT: %tuple.type.281: type = tuple_type (%C, %D) [concrete] // CHECK:STDOUT: %pattern_type.881: type = pattern_type %tuple.type.281 [concrete] // CHECK:STDOUT: %.9a5: Core.Form = init_form %D [concrete] // CHECK:STDOUT: %pattern_type.9c8: type = pattern_type %D [concrete] // CHECK:STDOUT: %G.type: type = fn_type @G [concrete] // CHECK:STDOUT: %G: %G.type = struct_value () [concrete] // CHECK:STDOUT: %F.specific_fn.107: = specific_function %F, @F(%C, %D) [concrete] // CHECK:STDOUT: %complete_type.734: = complete_type_witness %tuple.type.281 [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .C = %C.decl // CHECK:STDOUT: .D = %D.decl // CHECK:STDOUT: .F = %F.decl // CHECK:STDOUT: .G = %G.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %C.decl: type = class_decl @C [concrete = constants.%C] {} {} // CHECK:STDOUT: %D.decl: type = class_decl @D [concrete = constants.%D] {} {} // CHECK:STDOUT: %F.decl: %F.type = fn_decl @F [concrete = constants.%F] { // CHECK:STDOUT: %T.patt: %pattern_type.98f = symbolic_binding_pattern T, 0 [concrete] // CHECK:STDOUT: %U.patt: %pattern_type.98f = symbolic_binding_pattern U, 1 [concrete] // CHECK:STDOUT: %pair.patt: @F.%pattern_type.loc7_26 (%pattern_type.eee) = value_binding_pattern pair [concrete] // CHECK:STDOUT: %pair.param_patt: @F.%pattern_type.loc7_26 (%pattern_type.eee) = value_param_pattern %pair.patt [concrete] // CHECK:STDOUT: %return.patt: @F.%pattern_type.loc7_40 (%pattern_type.946) = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: @F.%pattern_type.loc7_40 (%pattern_type.946) = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %U.ref.loc7_43: type = name_ref U, %U.loc7_16.2 [symbolic = %U.loc7_16.1 (constants.%U)] // CHECK:STDOUT: %.loc7_43.3: Core.Form = init_form %U.ref.loc7_43 [symbolic = %.loc7_43.2 (constants.%.822)] // CHECK:STDOUT: %.loc7_10.1: type = splice_block %.loc7_10.2 [concrete = type] { // CHECK:STDOUT: %.Self.2: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %.loc7_10.2: type = type_literal type [concrete = type] // CHECK:STDOUT: } // CHECK:STDOUT: %T.loc7_6.2: type = symbolic_binding T, 0 [symbolic = %T.loc7_6.1 (constants.%T)] // CHECK:STDOUT: %.loc7_20.1: type = splice_block %.loc7_20.2 [concrete = type] { // CHECK:STDOUT: %.Self.1: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %.loc7_20.2: type = type_literal type [concrete = type] // CHECK:STDOUT: } // CHECK:STDOUT: %U.loc7_16.2: type = symbolic_binding U, 1 [symbolic = %U.loc7_16.1 (constants.%U)] // CHECK:STDOUT: %pair.param: @F.%tuple.type (%tuple.type.a5e) = value_param call_param0 // CHECK:STDOUT: %.loc7_37.1: type = splice_block %.loc7_37.3 [symbolic = %tuple.type (constants.%tuple.type.a5e)] { // CHECK:STDOUT: %T.ref: type = name_ref T, %T.loc7_6.2 [symbolic = %T.loc7_6.1 (constants.%T)] // CHECK:STDOUT: %U.ref.loc7_36: type = name_ref U, %U.loc7_16.2 [symbolic = %U.loc7_16.1 (constants.%U)] // CHECK:STDOUT: %.loc7_37.2: %tuple.type.24b = tuple_literal (%T.ref, %U.ref.loc7_36) [symbolic = %tuple (constants.%tuple.4b9)] // CHECK:STDOUT: %.loc7_37.3: type = converted %.loc7_37.2, constants.%tuple.type.a5e [symbolic = %tuple.type (constants.%tuple.type.a5e)] // CHECK:STDOUT: } // CHECK:STDOUT: %pair: @F.%tuple.type (%tuple.type.a5e) = value_binding pair, %pair.param // CHECK:STDOUT: %return.param: ref @F.%U.loc7_16.1 (%U) = out_param call_param1 // CHECK:STDOUT: %return: ref @F.%U.loc7_16.1 (%U) = return_slot %return.param // CHECK:STDOUT: } // CHECK:STDOUT: %G.decl: %G.type = fn_decl @G [concrete = constants.%G] { // CHECK:STDOUT: %pair.patt: %pattern_type.881 = value_binding_pattern pair [concrete] // CHECK:STDOUT: %pair.param_patt: %pattern_type.881 = value_param_pattern %pair.patt [concrete] // CHECK:STDOUT: %return.patt: %pattern_type.9c8 = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: %pattern_type.9c8 = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %D.ref.loc9_23: type = name_ref D, file.%D.decl [concrete = constants.%D] // CHECK:STDOUT: %.loc9_23.2: Core.Form = init_form %D.ref.loc9_23 [concrete = constants.%.9a5] // CHECK:STDOUT: %pair.param: %tuple.type.281 = value_param call_param0 // CHECK:STDOUT: %.loc9_17.1: type = splice_block %.loc9_17.3 [concrete = constants.%tuple.type.281] { // CHECK:STDOUT: %C.ref: type = name_ref C, file.%C.decl [concrete = constants.%C] // CHECK:STDOUT: %D.ref.loc9_16: type = name_ref D, file.%D.decl [concrete = constants.%D] // CHECK:STDOUT: %.loc9_17.2: %tuple.type.24b = tuple_literal (%C.ref, %D.ref.loc9_16) [concrete = constants.%tuple.a0a] // CHECK:STDOUT: %.loc9_17.3: type = converted %.loc9_17.2, constants.%tuple.type.281 [concrete = constants.%tuple.type.281] // CHECK:STDOUT: } // CHECK:STDOUT: %pair: %tuple.type.281 = value_binding pair, %pair.param // CHECK:STDOUT: %return.param: ref %D = out_param call_param1 // CHECK:STDOUT: %return: ref %D = return_slot %return.param // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @C { // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type.357] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%C // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @D { // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type.357] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%D // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic fn @F(%T.loc7_6.2: type, %U.loc7_16.2: type) { // CHECK:STDOUT: %T.loc7_6.1: type = symbolic_binding T, 0 [symbolic = %T.loc7_6.1 (constants.%T)] // CHECK:STDOUT: %U.loc7_16.1: type = symbolic_binding U, 1 [symbolic = %U.loc7_16.1 (constants.%U)] // CHECK:STDOUT: %tuple: %tuple.type.24b = tuple_value (%T.loc7_6.1, %U.loc7_16.1) [symbolic = %tuple (constants.%tuple.4b9)] // CHECK:STDOUT: %tuple.type: type = tuple_type (%T.loc7_6.1, %U.loc7_16.1) [symbolic = %tuple.type (constants.%tuple.type.a5e)] // CHECK:STDOUT: %pattern_type.loc7_26: type = pattern_type %tuple.type [symbolic = %pattern_type.loc7_26 (constants.%pattern_type.eee)] // CHECK:STDOUT: %.loc7_43.2: Core.Form = init_form %U.loc7_16.1 [symbolic = %.loc7_43.2 (constants.%.822)] // CHECK:STDOUT: %pattern_type.loc7_40: type = pattern_type %U.loc7_16.1 [symbolic = %pattern_type.loc7_40 (constants.%pattern_type.946)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete: = require_complete_type %tuple.type [symbolic = %require_complete (constants.%require_complete)] // CHECK:STDOUT: %F.specific_fn.loc7_54.2: = specific_function constants.%F, @F(%T.loc7_6.1, %U.loc7_16.1) [symbolic = %F.specific_fn.loc7_54.2 (constants.%F.specific_fn.a54)] // CHECK:STDOUT: // CHECK:STDOUT: fn(%pair.param: @F.%tuple.type (%tuple.type.a5e)) -> out %return.param: @F.%U.loc7_16.1 (%U) { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %F.ref: %F.type = name_ref F, file.%F.decl [concrete = constants.%F] // CHECK:STDOUT: %pair.ref: @F.%tuple.type (%tuple.type.a5e) = name_ref pair, %pair // CHECK:STDOUT: %F.specific_fn.loc7_54.1: = specific_function %F.ref, @F(constants.%T, constants.%U) [symbolic = %F.specific_fn.loc7_54.2 (constants.%F.specific_fn.a54)] // CHECK:STDOUT: %.loc7_43.1: ref @F.%U.loc7_16.1 (%U) = splice_block %return.param {} // CHECK:STDOUT: %F.call: init @F.%U.loc7_16.1 (%U) to %.loc7_43.1 = call %F.specific_fn.loc7_54.1(%pair.ref) // CHECK:STDOUT: return %F.call to %return.param // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @G(%pair.param: %tuple.type.281) -> out %return.param: %D { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %F.ref: %F.type = name_ref F, file.%F.decl [concrete = constants.%F] // CHECK:STDOUT: %pair.ref: %tuple.type.281 = name_ref pair, %pair // CHECK:STDOUT: %F.specific_fn: = specific_function %F.ref, @F(constants.%C, constants.%D) [concrete = constants.%F.specific_fn.107] // CHECK:STDOUT: %.loc9_23.1: ref %D = splice_block %return.param {} // CHECK:STDOUT: %F.call: init %D to %.loc9_23.1 = call %F.specific_fn(%pair.ref) // CHECK:STDOUT: return %F.call to %return.param // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @F(constants.%T, constants.%U) { // CHECK:STDOUT: %T.loc7_6.1 => constants.%T // CHECK:STDOUT: %U.loc7_16.1 => constants.%U // CHECK:STDOUT: %tuple => constants.%tuple.4b9 // CHECK:STDOUT: %tuple.type => constants.%tuple.type.a5e // CHECK:STDOUT: %pattern_type.loc7_26 => constants.%pattern_type.eee // CHECK:STDOUT: %.loc7_43.2 => constants.%.822 // CHECK:STDOUT: %pattern_type.loc7_40 => constants.%pattern_type.946 // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete => constants.%require_complete // CHECK:STDOUT: %F.specific_fn.loc7_54.2 => constants.%F.specific_fn.a54 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @F(constants.%C, constants.%D) { // CHECK:STDOUT: %T.loc7_6.1 => constants.%C // CHECK:STDOUT: %U.loc7_16.1 => constants.%D // CHECK:STDOUT: %tuple => constants.%tuple.a0a // CHECK:STDOUT: %tuple.type => constants.%tuple.type.281 // CHECK:STDOUT: %pattern_type.loc7_26 => constants.%pattern_type.881 // CHECK:STDOUT: %.loc7_43.2 => constants.%.9a5 // CHECK:STDOUT: %pattern_type.loc7_40 => constants.%pattern_type.9c8 // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete => constants.%complete_type.734 // CHECK:STDOUT: %F.specific_fn.loc7_54.2 => constants.%F.specific_fn.107 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- tuple_value.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %type: type = facet_type [concrete] // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self] // CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [concrete] // CHECK:STDOUT: %Int.type: type = generic_class_type @Int [concrete] // CHECK:STDOUT: %Int.generic: %Int.type = struct_value () [concrete] // CHECK:STDOUT: %N: Core.IntLiteral = symbolic_binding N, 0 [symbolic] // CHECK:STDOUT: %i32: type = class_type @Int, @Int(%int_32) [concrete] // CHECK:STDOUT: %tuple.type.24b: type = tuple_type (type, type) [concrete] // CHECK:STDOUT: %tuple.95a: %tuple.type.24b = tuple_value (%i32, %i32) [concrete] // CHECK:STDOUT: %tuple.type.d07: type = tuple_type (%i32, %i32) [concrete] // CHECK:STDOUT: %pattern_type.511: type = pattern_type %tuple.type.d07 [concrete] // CHECK:STDOUT: %Pair: %tuple.type.d07 = symbolic_binding Pair, 0 [symbolic] // CHECK:STDOUT: %HasPair.type: type = generic_class_type @HasPair [concrete] // CHECK:STDOUT: %HasPair.generic: %HasPair.type = struct_value () [concrete] // CHECK:STDOUT: %HasPair.7cc: type = class_type @HasPair, @HasPair(%Pair) [symbolic] // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %complete_type.357: = complete_type_witness %empty_struct_type [concrete] // CHECK:STDOUT: %pattern_type.7ce: type = pattern_type %i32 [concrete] // CHECK:STDOUT: %A: %i32 = symbolic_binding A, 0 [symbolic] // CHECK:STDOUT: %B: %i32 = symbolic_binding B, 1 [symbolic] // CHECK:STDOUT: %tuple.9c9: %tuple.type.d07 = tuple_value (%A, %B) [symbolic] // CHECK:STDOUT: %HasPair.2e7: type = class_type @HasPair, @HasPair(%tuple.9c9) [symbolic] // CHECK:STDOUT: %pattern_type.dc2: type = pattern_type %HasPair.2e7 [symbolic] // CHECK:STDOUT: %.ff5: Core.Form = init_form %i32 [concrete] // CHECK:STDOUT: %F.type: type = fn_type @F [concrete] // CHECK:STDOUT: %F: %F.type = struct_value () [concrete] // CHECK:STDOUT: %require_complete.76f: = require_complete_type %HasPair.2e7 [symbolic] // CHECK:STDOUT: %Copy.type: type = facet_type <@Copy> [concrete] // CHECK:STDOUT: %Int.as.Copy.impl.Op.type.824: type = fn_type @Int.as.Copy.impl.Op, @Int.as.Copy.impl(%N) [symbolic] // CHECK:STDOUT: %Int.as.Copy.impl.Op.9b9: %Int.as.Copy.impl.Op.type.824 = struct_value () [symbolic] // CHECK:STDOUT: %Copy.impl_witness.f17: = impl_witness imports.%Copy.impl_witness_table.e76, @Int.as.Copy.impl(%int_32) [concrete] // CHECK:STDOUT: %Int.as.Copy.impl.Op.type.546: type = fn_type @Int.as.Copy.impl.Op, @Int.as.Copy.impl(%int_32) [concrete] // CHECK:STDOUT: %Int.as.Copy.impl.Op.664: %Int.as.Copy.impl.Op.type.546 = struct_value () [concrete] // CHECK:STDOUT: %Copy.facet: %Copy.type = facet_value %i32, (%Copy.impl_witness.f17) [concrete] // CHECK:STDOUT: %Copy.WithSelf.Op.type.081: type = fn_type @Copy.WithSelf.Op, @Copy.WithSelf(%Copy.facet) [concrete] // CHECK:STDOUT: %.8e2: type = fn_type_with_self_type %Copy.WithSelf.Op.type.081, %Copy.facet [concrete] // CHECK:STDOUT: %Int.as.Copy.impl.Op.bound.5f8: = bound_method %B, %Int.as.Copy.impl.Op.664 [symbolic] // CHECK:STDOUT: %Int.as.Copy.impl.Op.specific_fn: = specific_function %Int.as.Copy.impl.Op.664, @Int.as.Copy.impl.Op(%int_32) [concrete] // CHECK:STDOUT: %bound_method.dfb: = bound_method %B, %Int.as.Copy.impl.Op.specific_fn [symbolic] // CHECK:STDOUT: %int_1.5b8: Core.IntLiteral = int_value 1 [concrete] // CHECK:STDOUT: %int_2.ecc: Core.IntLiteral = int_value 2 [concrete] // CHECK:STDOUT: %tuple.type.f94: type = tuple_type (Core.IntLiteral, Core.IntLiteral) [concrete] // CHECK:STDOUT: %tuple.ad8: %tuple.type.f94 = tuple_value (%int_1.5b8, %int_2.ecc) [concrete] // CHECK:STDOUT: %ImplicitAs.type.cc7: type = generic_interface_type @ImplicitAs [concrete] // CHECK:STDOUT: %ImplicitAs.generic: %ImplicitAs.type.cc7 = struct_value () [concrete] // CHECK:STDOUT: %ImplicitAs.type.e8c: type = facet_type <@ImplicitAs, @ImplicitAs(%i32)> [concrete] // CHECK:STDOUT: %To: Core.IntLiteral = symbolic_binding To, 0 [symbolic] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.4e6: type = fn_type @Core.IntLiteral.as.ImplicitAs.impl.Convert, @Core.IntLiteral.as.ImplicitAs.impl(%To) [symbolic] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.3c2: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.4e6 = struct_value () [symbolic] // CHECK:STDOUT: %ImplicitAs.impl_witness.6bc: = impl_witness imports.%ImplicitAs.impl_witness_table.74f, @Core.IntLiteral.as.ImplicitAs.impl(%int_32) [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.e0d: type = fn_type @Core.IntLiteral.as.ImplicitAs.impl.Convert, @Core.IntLiteral.as.ImplicitAs.impl(%int_32) [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.e0d = struct_value () [concrete] // CHECK:STDOUT: %ImplicitAs.facet: %ImplicitAs.type.e8c = facet_value Core.IntLiteral, (%ImplicitAs.impl_witness.6bc) [concrete] // CHECK:STDOUT: %ImplicitAs.WithSelf.Convert.type.b37: type = fn_type @ImplicitAs.WithSelf.Convert, @ImplicitAs.WithSelf(%i32, %ImplicitAs.facet) [concrete] // CHECK:STDOUT: %.545: type = fn_type_with_self_type %ImplicitAs.WithSelf.Convert.type.b37, %ImplicitAs.facet [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.bound.215: = bound_method %int_1.5b8, %Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5 [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn: = specific_function %Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5, @Core.IntLiteral.as.ImplicitAs.impl.Convert(%int_32) [concrete] // CHECK:STDOUT: %bound_method.38b: = bound_method %int_1.5b8, %Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn [concrete] // CHECK:STDOUT: %int_1.5d2: %i32 = int_value 1 [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.bound.4e5: = bound_method %int_2.ecc, %Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5 [concrete] // CHECK:STDOUT: %bound_method.646: = bound_method %int_2.ecc, %Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn [concrete] // CHECK:STDOUT: %int_2.ef8: %i32 = int_value 2 [concrete] // CHECK:STDOUT: %tuple.21c: %tuple.type.d07 = tuple_value (%int_1.5d2, %int_2.ef8) [concrete] // CHECK:STDOUT: %HasPair.867: type = class_type @HasPair, @HasPair(%tuple.21c) [concrete] // CHECK:STDOUT: %pattern_type.530: type = pattern_type %HasPair.867 [concrete] // CHECK:STDOUT: %G.type: type = fn_type @G [concrete] // CHECK:STDOUT: %G: %G.type = struct_value () [concrete] // CHECK:STDOUT: %F.specific_fn: = specific_function %F, @F(%int_1.5d2, %int_2.ef8) [concrete] // CHECK:STDOUT: %Int.as.Copy.impl.Op.bound.5e8: = bound_method %int_2.ef8, %Int.as.Copy.impl.Op.664 [concrete] // CHECK:STDOUT: %bound_method.f15: = bound_method %int_2.ef8, %Int.as.Copy.impl.Op.specific_fn [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: .Int = %Core.Int // CHECK:STDOUT: .Copy = %Core.Copy // CHECK:STDOUT: .ImplicitAs = %Core.ImplicitAs // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: %Core.Int: %Int.type = import_ref Core//prelude/parts/int, Int, loaded [concrete = constants.%Int.generic] // CHECK:STDOUT: %Core.Copy: type = import_ref Core//prelude/parts/copy, Copy, loaded [concrete = constants.%Copy.type] // CHECK:STDOUT: %Core.import_ref.18d: @Int.as.Copy.impl.%Int.as.Copy.impl.Op.type (%Int.as.Copy.impl.Op.type.824) = import_ref Core//prelude/parts/int, loc{{\d+_\d+}}, loaded [symbolic = @Int.as.Copy.impl.%Int.as.Copy.impl.Op (constants.%Int.as.Copy.impl.Op.9b9)] // CHECK:STDOUT: %Copy.impl_witness_table.e76 = impl_witness_table (%Core.import_ref.18d), @Int.as.Copy.impl [concrete] // CHECK:STDOUT: %Core.ImplicitAs: %ImplicitAs.type.cc7 = import_ref Core//prelude/parts/as, ImplicitAs, loaded [concrete = constants.%ImplicitAs.generic] // CHECK:STDOUT: %Core.import_ref.42d: @Core.IntLiteral.as.ImplicitAs.impl.%Core.IntLiteral.as.ImplicitAs.impl.Convert.type (%Core.IntLiteral.as.ImplicitAs.impl.Convert.type.4e6) = import_ref Core//prelude/parts/int, loc{{\d+_\d+}}, loaded [symbolic = @Core.IntLiteral.as.ImplicitAs.impl.%Core.IntLiteral.as.ImplicitAs.impl.Convert (constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.3c2)] // CHECK:STDOUT: %ImplicitAs.impl_witness_table.74f = impl_witness_table (%Core.import_ref.42d), @Core.IntLiteral.as.ImplicitAs.impl [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .HasPair = %HasPair.decl // CHECK:STDOUT: .F = %F.decl // CHECK:STDOUT: .G = %G.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %HasPair.decl: %HasPair.type = class_decl @HasPair [concrete = constants.%HasPair.generic] { // CHECK:STDOUT: %Pair.patt: %pattern_type.511 = symbolic_binding_pattern Pair, 0 [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %.loc4_31.1: type = splice_block %.loc4_31.3 [concrete = constants.%tuple.type.d07] { // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %i32.loc4_23: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %i32.loc4_28: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %.loc4_31.2: %tuple.type.24b = tuple_literal (%i32.loc4_23, %i32.loc4_28) [concrete = constants.%tuple.95a] // CHECK:STDOUT: %.loc4_31.3: type = converted %.loc4_31.2, constants.%tuple.type.d07 [concrete = constants.%tuple.type.d07] // CHECK:STDOUT: } // CHECK:STDOUT: %Pair.loc4_15.2: %tuple.type.d07 = symbolic_binding Pair, 0 [symbolic = %Pair.loc4_15.1 (constants.%Pair)] // CHECK:STDOUT: } // CHECK:STDOUT: %F.decl: %F.type = fn_decl @F [concrete = constants.%F] { // CHECK:STDOUT: %A.patt: %pattern_type.7ce = symbolic_binding_pattern A, 0 [concrete] // CHECK:STDOUT: %B.patt: %pattern_type.7ce = symbolic_binding_pattern B, 1 [concrete] // CHECK:STDOUT: %h.patt: @F.%pattern_type (%pattern_type.dc2) = value_binding_pattern h [concrete] // CHECK:STDOUT: %h.param_patt: @F.%pattern_type (%pattern_type.dc2) = value_param_pattern %h.patt [concrete] // CHECK:STDOUT: %return.patt: %pattern_type.7ce = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: %pattern_type.7ce = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %i32.loc6_54: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %.loc6_54: Core.Form = init_form %i32.loc6_54 [concrete = constants.%.ff5] // CHECK:STDOUT: %.loc6_10: type = splice_block %i32.loc6_10 [concrete = constants.%i32] { // CHECK:STDOUT: %.Self.2: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %i32.loc6_10: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: } // CHECK:STDOUT: %A.loc6_6.2: %i32 = symbolic_binding A, 0 [symbolic = %A.loc6_6.1 (constants.%A)] // CHECK:STDOUT: %.loc6_19: type = splice_block %i32.loc6_19 [concrete = constants.%i32] { // CHECK:STDOUT: %.Self.1: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %i32.loc6_19: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: } // CHECK:STDOUT: %B.loc6_15.2: %i32 = symbolic_binding B, 1 [symbolic = %B.loc6_15.1 (constants.%B)] // CHECK:STDOUT: %h.param: @F.%HasPair.loc6_48.1 (%HasPair.2e7) = value_param call_param0 // CHECK:STDOUT: %.loc6_48.1: type = splice_block %HasPair.loc6_48.2 [symbolic = %HasPair.loc6_48.1 (constants.%HasPair.2e7)] { // CHECK:STDOUT: %HasPair.ref: %HasPair.type = name_ref HasPair, file.%HasPair.decl [concrete = constants.%HasPair.generic] // CHECK:STDOUT: %A.ref: %i32 = name_ref A, %A.loc6_6.2 [symbolic = %A.loc6_6.1 (constants.%A)] // CHECK:STDOUT: %B.ref.loc6_46: %i32 = name_ref B, %B.loc6_15.2 [symbolic = %B.loc6_15.1 (constants.%B)] // CHECK:STDOUT: %.loc6_47: %tuple.type.d07 = tuple_literal (%A.ref, %B.ref.loc6_46) [symbolic = %tuple.loc6_47.1 (constants.%tuple.9c9)] // CHECK:STDOUT: %tuple.loc6_47.2: %tuple.type.d07 = tuple_value (%A.ref, %B.ref.loc6_46) [symbolic = %tuple.loc6_47.1 (constants.%tuple.9c9)] // CHECK:STDOUT: %.loc6_48.2: %tuple.type.d07 = converted %.loc6_47, %tuple.loc6_47.2 [symbolic = %tuple.loc6_47.1 (constants.%tuple.9c9)] // CHECK:STDOUT: %HasPair.loc6_48.2: type = class_type @HasPair, @HasPair(constants.%tuple.9c9) [symbolic = %HasPair.loc6_48.1 (constants.%HasPair.2e7)] // CHECK:STDOUT: } // CHECK:STDOUT: %h: @F.%HasPair.loc6_48.1 (%HasPair.2e7) = value_binding h, %h.param // CHECK:STDOUT: %return.param: ref %i32 = out_param call_param1 // CHECK:STDOUT: %return: ref %i32 = return_slot %return.param // CHECK:STDOUT: } // CHECK:STDOUT: %G.decl: %G.type = fn_decl @G [concrete = constants.%G] { // CHECK:STDOUT: %h.patt: %pattern_type.530 = value_binding_pattern h [concrete] // CHECK:STDOUT: %h.param_patt: %pattern_type.530 = value_param_pattern %h.patt [concrete] // CHECK:STDOUT: %return.patt: %pattern_type.7ce = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: %pattern_type.7ce = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %i32: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %.loc8_29: Core.Form = init_form %i32 [concrete = constants.%.ff5] // CHECK:STDOUT: %h.param: %HasPair.867 = value_param call_param0 // CHECK:STDOUT: %.loc8_23.1: type = splice_block %HasPair [concrete = constants.%HasPair.867] { // CHECK:STDOUT: %HasPair.ref: %HasPair.type = name_ref HasPair, file.%HasPair.decl [concrete = constants.%HasPair.generic] // CHECK:STDOUT: %int_1: Core.IntLiteral = int_value 1 [concrete = constants.%int_1.5b8] // CHECK:STDOUT: %int_2: Core.IntLiteral = int_value 2 [concrete = constants.%int_2.ecc] // CHECK:STDOUT: %.loc8_22.1: %tuple.type.f94 = tuple_literal (%int_1, %int_2) [concrete = constants.%tuple.ad8] // CHECK:STDOUT: %impl.elem0.loc8_22.1: %.545 = impl_witness_access constants.%ImplicitAs.impl_witness.6bc, element0 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5] // CHECK:STDOUT: %bound_method.loc8_22.1: = bound_method %int_1, %impl.elem0.loc8_22.1 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.bound.215] // CHECK:STDOUT: %specific_fn.loc8_22.1: = specific_function %impl.elem0.loc8_22.1, @Core.IntLiteral.as.ImplicitAs.impl.Convert(constants.%int_32) [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn] // CHECK:STDOUT: %bound_method.loc8_22.2: = bound_method %int_1, %specific_fn.loc8_22.1 [concrete = constants.%bound_method.38b] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc8_22.1: init %i32 = call %bound_method.loc8_22.2(%int_1) [concrete = constants.%int_1.5d2] // CHECK:STDOUT: %.loc8_22.2: %i32 = value_of_initializer %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc8_22.1 [concrete = constants.%int_1.5d2] // CHECK:STDOUT: %.loc8_22.3: %i32 = converted %int_1, %.loc8_22.2 [concrete = constants.%int_1.5d2] // CHECK:STDOUT: %impl.elem0.loc8_22.2: %.545 = impl_witness_access constants.%ImplicitAs.impl_witness.6bc, element0 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5] // CHECK:STDOUT: %bound_method.loc8_22.3: = bound_method %int_2, %impl.elem0.loc8_22.2 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.bound.4e5] // CHECK:STDOUT: %specific_fn.loc8_22.2: = specific_function %impl.elem0.loc8_22.2, @Core.IntLiteral.as.ImplicitAs.impl.Convert(constants.%int_32) [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn] // CHECK:STDOUT: %bound_method.loc8_22.4: = bound_method %int_2, %specific_fn.loc8_22.2 [concrete = constants.%bound_method.646] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc8_22.2: init %i32 = call %bound_method.loc8_22.4(%int_2) [concrete = constants.%int_2.ef8] // CHECK:STDOUT: %.loc8_22.4: %i32 = value_of_initializer %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc8_22.2 [concrete = constants.%int_2.ef8] // CHECK:STDOUT: %.loc8_22.5: %i32 = converted %int_2, %.loc8_22.4 [concrete = constants.%int_2.ef8] // CHECK:STDOUT: %tuple: %tuple.type.d07 = tuple_value (%.loc8_22.3, %.loc8_22.5) [concrete = constants.%tuple.21c] // CHECK:STDOUT: %.loc8_23.2: %tuple.type.d07 = converted %.loc8_22.1, %tuple [concrete = constants.%tuple.21c] // CHECK:STDOUT: %HasPair: type = class_type @HasPair, @HasPair(constants.%tuple.21c) [concrete = constants.%HasPair.867] // CHECK:STDOUT: } // CHECK:STDOUT: %h: %HasPair.867 = value_binding h, %h.param // CHECK:STDOUT: %return.param: ref %i32 = out_param call_param1 // CHECK:STDOUT: %return: ref %i32 = return_slot %return.param // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic class @HasPair(%Pair.loc4_15.2: %tuple.type.d07) { // CHECK:STDOUT: %Pair.loc4_15.1: %tuple.type.d07 = symbolic_binding Pair, 0 [symbolic = %Pair.loc4_15.1 (constants.%Pair)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: // CHECK:STDOUT: class { // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type.357] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%HasPair.7cc // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic fn @F(%A.loc6_6.2: %i32, %B.loc6_15.2: %i32) { // CHECK:STDOUT: %A.loc6_6.1: %i32 = symbolic_binding A, 0 [symbolic = %A.loc6_6.1 (constants.%A)] // CHECK:STDOUT: %B.loc6_15.1: %i32 = symbolic_binding B, 1 [symbolic = %B.loc6_15.1 (constants.%B)] // CHECK:STDOUT: %tuple.loc6_47.1: %tuple.type.d07 = tuple_value (%A.loc6_6.1, %B.loc6_15.1) [symbolic = %tuple.loc6_47.1 (constants.%tuple.9c9)] // CHECK:STDOUT: %HasPair.loc6_48.1: type = class_type @HasPair, @HasPair(%tuple.loc6_47.1) [symbolic = %HasPair.loc6_48.1 (constants.%HasPair.2e7)] // CHECK:STDOUT: %pattern_type: type = pattern_type %HasPair.loc6_48.1 [symbolic = %pattern_type (constants.%pattern_type.dc2)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete: = require_complete_type %HasPair.loc6_48.1 [symbolic = %require_complete (constants.%require_complete.76f)] // CHECK:STDOUT: %Int.as.Copy.impl.Op.bound: = bound_method %B.loc6_15.1, constants.%Int.as.Copy.impl.Op.664 [symbolic = %Int.as.Copy.impl.Op.bound (constants.%Int.as.Copy.impl.Op.bound.5f8)] // CHECK:STDOUT: %bound_method.loc6_67.3: = bound_method %B.loc6_15.1, constants.%Int.as.Copy.impl.Op.specific_fn [symbolic = %bound_method.loc6_67.3 (constants.%bound_method.dfb)] // CHECK:STDOUT: // CHECK:STDOUT: fn(%h.param: @F.%HasPair.loc6_48.1 (%HasPair.2e7)) -> out %return.param: %i32 { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %B.ref.loc6_67: %i32 = name_ref B, %B.loc6_15.2 [symbolic = %B.loc6_15.1 (constants.%B)] // CHECK:STDOUT: %impl.elem0: %.8e2 = impl_witness_access constants.%Copy.impl_witness.f17, element0 [concrete = constants.%Int.as.Copy.impl.Op.664] // CHECK:STDOUT: %bound_method.loc6_67.1: = bound_method %B.ref.loc6_67, %impl.elem0 [symbolic = %Int.as.Copy.impl.Op.bound (constants.%Int.as.Copy.impl.Op.bound.5f8)] // CHECK:STDOUT: %specific_fn: = specific_function %impl.elem0, @Int.as.Copy.impl.Op(constants.%int_32) [concrete = constants.%Int.as.Copy.impl.Op.specific_fn] // CHECK:STDOUT: %bound_method.loc6_67.2: = bound_method %B.ref.loc6_67, %specific_fn [symbolic = %bound_method.loc6_67.3 (constants.%bound_method.dfb)] // CHECK:STDOUT: %Int.as.Copy.impl.Op.call: init %i32 = call %bound_method.loc6_67.2(%B.ref.loc6_67) [symbolic = %B.loc6_15.1 (constants.%B)] // CHECK:STDOUT: return %Int.as.Copy.impl.Op.call // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @G(%h.param: %HasPair.867) -> out %return.param: %i32 { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %F.ref: %F.type = name_ref F, file.%F.decl [concrete = constants.%F] // CHECK:STDOUT: %h.ref: %HasPair.867 = name_ref h, %h // CHECK:STDOUT: %F.specific_fn: = specific_function %F.ref, @F(constants.%int_1.5d2, constants.%int_2.ef8) [concrete = constants.%F.specific_fn] // CHECK:STDOUT: %F.call: init %i32 = call %F.specific_fn(%h.ref) // CHECK:STDOUT: return %F.call // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @HasPair(constants.%Pair) { // CHECK:STDOUT: %Pair.loc4_15.1 => constants.%Pair // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @HasPair(constants.%tuple.9c9) { // CHECK:STDOUT: %Pair.loc4_15.1 => constants.%tuple.9c9 // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @F(constants.%A, constants.%B) { // CHECK:STDOUT: %A.loc6_6.1 => constants.%A // CHECK:STDOUT: %B.loc6_15.1 => constants.%B // CHECK:STDOUT: %tuple.loc6_47.1 => constants.%tuple.9c9 // CHECK:STDOUT: %HasPair.loc6_48.1 => constants.%HasPair.2e7 // CHECK:STDOUT: %pattern_type => constants.%pattern_type.dc2 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @HasPair(constants.%tuple.21c) { // CHECK:STDOUT: %Pair.loc4_15.1 => constants.%tuple.21c // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @F(constants.%int_1.5d2, constants.%int_2.ef8) { // CHECK:STDOUT: %A.loc6_6.1 => constants.%int_1.5d2 // CHECK:STDOUT: %B.loc6_15.1 => constants.%int_2.ef8 // CHECK:STDOUT: %tuple.loc6_47.1 => constants.%tuple.21c // CHECK:STDOUT: %HasPair.loc6_48.1 => constants.%HasPair.867 // CHECK:STDOUT: %pattern_type => constants.%pattern_type.530 // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete => constants.%complete_type.357 // CHECK:STDOUT: %Int.as.Copy.impl.Op.bound => constants.%Int.as.Copy.impl.Op.bound.5e8 // CHECK:STDOUT: %bound_method.loc6_67.3 => constants.%bound_method.f15 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- fail_inconsistent.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %C: type = class_type @C [concrete] // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %complete_type: = complete_type_witness %empty_struct_type [concrete] // CHECK:STDOUT: %D: type = class_type @D [concrete] // CHECK:STDOUT: %type: type = facet_type [concrete] // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self] // CHECK:STDOUT: %pattern_type.98f: type = pattern_type type [concrete] // CHECK:STDOUT: %T: type = symbolic_binding T, 0 [symbolic] // CHECK:STDOUT: %tuple.type.24b: type = tuple_type (type, type) [concrete] // CHECK:STDOUT: %tuple.11e: %tuple.type.24b = tuple_value (%T, %T) [symbolic] // CHECK:STDOUT: %tuple.type.07a: type = tuple_type (%T, %T) [symbolic] // CHECK:STDOUT: %pattern_type.c3f: type = pattern_type %tuple.type.07a [symbolic] // CHECK:STDOUT: %.184: Core.Form = init_form %T [symbolic] // CHECK:STDOUT: %pattern_type.51d: type = pattern_type %T [symbolic] // CHECK:STDOUT: %F.type: type = fn_type @F [concrete] // CHECK:STDOUT: %F: %F.type = struct_value () [concrete] // CHECK:STDOUT: %tuple.a0a: %tuple.type.24b = tuple_value (%C, %D) [concrete] // CHECK:STDOUT: %tuple.type.281: type = tuple_type (%C, %D) [concrete] // CHECK:STDOUT: %pattern_type.881: type = pattern_type %tuple.type.281 [concrete] // CHECK:STDOUT: %.9a5: Core.Form = init_form %D [concrete] // CHECK:STDOUT: %pattern_type.9c8: type = pattern_type %D [concrete] // CHECK:STDOUT: %G.type: type = fn_type @G [concrete] // CHECK:STDOUT: %G: %G.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .C = %C.decl // CHECK:STDOUT: .D = %D.decl // CHECK:STDOUT: .F = %F.decl // CHECK:STDOUT: .G = %G.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %C.decl: type = class_decl @C [concrete = constants.%C] {} {} // CHECK:STDOUT: %D.decl: type = class_decl @D [concrete = constants.%D] {} {} // CHECK:STDOUT: %F.decl: %F.type = fn_decl @F [concrete = constants.%F] { // CHECK:STDOUT: %T.patt: %pattern_type.98f = symbolic_binding_pattern T, 0 [concrete] // CHECK:STDOUT: %pair.patt: @F.%pattern_type.loc7_16 (%pattern_type.c3f) = value_binding_pattern pair [concrete] // CHECK:STDOUT: %pair.param_patt: @F.%pattern_type.loc7_16 (%pattern_type.c3f) = value_param_pattern %pair.patt [concrete] // CHECK:STDOUT: %return.patt: @F.%pattern_type.loc7_30 (%pattern_type.51d) = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: @F.%pattern_type.loc7_30 (%pattern_type.51d) = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %T.ref.loc7_33: type = name_ref T, %T.loc7_6.2 [symbolic = %T.loc7_6.1 (constants.%T)] // CHECK:STDOUT: %.loc7_33.2: Core.Form = init_form %T.ref.loc7_33 [symbolic = %.loc7_33.1 (constants.%.184)] // CHECK:STDOUT: %.loc7_10.1: type = splice_block %.loc7_10.2 [concrete = type] { // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %.loc7_10.2: type = type_literal type [concrete = type] // CHECK:STDOUT: } // CHECK:STDOUT: %T.loc7_6.2: type = symbolic_binding T, 0 [symbolic = %T.loc7_6.1 (constants.%T)] // CHECK:STDOUT: %pair.param: @F.%tuple.type (%tuple.type.07a) = value_param call_param0 // CHECK:STDOUT: %.loc7_27.1: type = splice_block %.loc7_27.3 [symbolic = %tuple.type (constants.%tuple.type.07a)] { // CHECK:STDOUT: %T.ref.loc7_23: type = name_ref T, %T.loc7_6.2 [symbolic = %T.loc7_6.1 (constants.%T)] // CHECK:STDOUT: %T.ref.loc7_26: type = name_ref T, %T.loc7_6.2 [symbolic = %T.loc7_6.1 (constants.%T)] // CHECK:STDOUT: %.loc7_27.2: %tuple.type.24b = tuple_literal (%T.ref.loc7_23, %T.ref.loc7_26) [symbolic = %tuple (constants.%tuple.11e)] // CHECK:STDOUT: %.loc7_27.3: type = converted %.loc7_27.2, constants.%tuple.type.07a [symbolic = %tuple.type (constants.%tuple.type.07a)] // CHECK:STDOUT: } // CHECK:STDOUT: %pair: @F.%tuple.type (%tuple.type.07a) = value_binding pair, %pair.param // CHECK:STDOUT: %return.param: ref @F.%T.loc7_6.1 (%T) = out_param call_param1 // CHECK:STDOUT: %return: ref @F.%T.loc7_6.1 (%T) = return_slot %return.param // CHECK:STDOUT: } // CHECK:STDOUT: %G.decl: %G.type = fn_decl @G [concrete = constants.%G] { // CHECK:STDOUT: %pair.patt: %pattern_type.881 = value_binding_pattern pair [concrete] // CHECK:STDOUT: %pair.param_patt: %pattern_type.881 = value_param_pattern %pair.patt [concrete] // CHECK:STDOUT: %return.patt: %pattern_type.9c8 = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: %pattern_type.9c8 = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %D.ref.loc9_23: type = name_ref D, file.%D.decl [concrete = constants.%D] // CHECK:STDOUT: %.loc9_23: Core.Form = init_form %D.ref.loc9_23 [concrete = constants.%.9a5] // CHECK:STDOUT: %pair.param: %tuple.type.281 = value_param call_param0 // CHECK:STDOUT: %.loc9_17.1: type = splice_block %.loc9_17.3 [concrete = constants.%tuple.type.281] { // CHECK:STDOUT: %C.ref: type = name_ref C, file.%C.decl [concrete = constants.%C] // CHECK:STDOUT: %D.ref.loc9_16: type = name_ref D, file.%D.decl [concrete = constants.%D] // CHECK:STDOUT: %.loc9_17.2: %tuple.type.24b = tuple_literal (%C.ref, %D.ref.loc9_16) [concrete = constants.%tuple.a0a] // CHECK:STDOUT: %.loc9_17.3: type = converted %.loc9_17.2, constants.%tuple.type.281 [concrete = constants.%tuple.type.281] // CHECK:STDOUT: } // CHECK:STDOUT: %pair: %tuple.type.281 = value_binding pair, %pair.param // CHECK:STDOUT: %return.param: ref %D = out_param call_param1 // CHECK:STDOUT: %return: ref %D = return_slot %return.param // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @C { // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%C // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @D { // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%D // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic fn @F(%T.loc7_6.2: type) { // CHECK:STDOUT: %T.loc7_6.1: type = symbolic_binding T, 0 [symbolic = %T.loc7_6.1 (constants.%T)] // CHECK:STDOUT: %tuple: %tuple.type.24b = tuple_value (%T.loc7_6.1, %T.loc7_6.1) [symbolic = %tuple (constants.%tuple.11e)] // CHECK:STDOUT: %tuple.type: type = tuple_type (%T.loc7_6.1, %T.loc7_6.1) [symbolic = %tuple.type (constants.%tuple.type.07a)] // CHECK:STDOUT: %pattern_type.loc7_16: type = pattern_type %tuple.type [symbolic = %pattern_type.loc7_16 (constants.%pattern_type.c3f)] // CHECK:STDOUT: %.loc7_33.1: Core.Form = init_form %T.loc7_6.1 [symbolic = %.loc7_33.1 (constants.%.184)] // CHECK:STDOUT: %pattern_type.loc7_30: type = pattern_type %T.loc7_6.1 [symbolic = %pattern_type.loc7_30 (constants.%pattern_type.51d)] // CHECK:STDOUT: // CHECK:STDOUT: fn(%pair.param: @F.%tuple.type (%tuple.type.07a)) -> out %return.param: @F.%T.loc7_6.1 (%T); // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @G(%pair.param: %tuple.type.281) -> out %return.param: %D { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %F.ref: %F.type = name_ref F, file.%F.decl [concrete = constants.%F] // CHECK:STDOUT: %pair.ref: %tuple.type.281 = name_ref pair, %pair // CHECK:STDOUT: return to %return.param // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @F(constants.%T) { // CHECK:STDOUT: %T.loc7_6.1 => constants.%T // CHECK:STDOUT: %tuple => constants.%tuple.11e // CHECK:STDOUT: %tuple.type => constants.%tuple.type.07a // CHECK:STDOUT: %pattern_type.loc7_16 => constants.%pattern_type.c3f // CHECK:STDOUT: %.loc7_33.1 => constants.%.184 // CHECK:STDOUT: %pattern_type.loc7_30 => constants.%pattern_type.51d // CHECK:STDOUT: } // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/deduce/type_operator.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/convert.carbon // TODO: Add ranges and switch to "--dump-sem-ir-ranges=only". // EXTRA-ARGS: --dump-sem-ir-ranges=if-present // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/deduce/type_operator.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/deduce/type_operator.carbon // --- pointer.carbon library "[[@TEST_NAME]]"; class C {} fn F[T:! type](p: T*) -> T { return F(p); } fn G(p: C*) -> C { return F(p); } // --- const.carbon library "[[@TEST_NAME]]"; class C {} fn F[T:! type](p: const T*) -> T { return F(p); } fn G(p: const C*) -> C { return F(p); } // --- nonconst_from_const.carbon library "[[@TEST_NAME]]"; class C {} fn F[T:! type](p: T*) -> T { return F(p); } fn G(p: const C*) -> const C { return F(p); } // --- fail_const_from_nonconst.carbon library "[[@TEST_NAME]]"; class C {} fn F[T:! type](p: const T*) -> T { return F(p); } fn G(p: C*) -> const C { // CHECK:STDERR: fail_const_from_nonconst.carbon:[[@LINE+7]]:10: error: cannot deduce value for generic parameter `T` [DeductionIncomplete] // CHECK:STDERR: return F(p); // CHECK:STDERR: ^~~~ // CHECK:STDERR: fail_const_from_nonconst.carbon:[[@LINE-6]]:1: note: while deducing parameters of generic declared here [DeductionGenericHere] // CHECK:STDERR: fn F[T:! type](p: const T*) -> T { return F(p); } // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: return F(p); } // CHECK:STDOUT: --- pointer.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %C: type = class_type @C [concrete] // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %complete_type.357: = complete_type_witness %empty_struct_type [concrete] // CHECK:STDOUT: %type: type = facet_type [concrete] // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self] // CHECK:STDOUT: %pattern_type.98f: type = pattern_type type [concrete] // CHECK:STDOUT: %T: type = symbolic_binding T, 0 [symbolic] // CHECK:STDOUT: %ptr.e8f: type = ptr_type %T [symbolic] // CHECK:STDOUT: %pattern_type.4f4: type = pattern_type %ptr.e8f [symbolic] // CHECK:STDOUT: %.184: Core.Form = init_form %T [symbolic] // CHECK:STDOUT: %pattern_type.51d: type = pattern_type %T [symbolic] // CHECK:STDOUT: %F.type: type = fn_type @F [concrete] // CHECK:STDOUT: %F: %F.type = struct_value () [concrete] // CHECK:STDOUT: %require_complete.ef1: = require_complete_type %ptr.e8f [symbolic] // CHECK:STDOUT: %require_complete.944: = require_complete_type %T [symbolic] // CHECK:STDOUT: %F.specific_fn.643: = specific_function %F, @F(%T) [symbolic] // CHECK:STDOUT: %ptr.31e: type = ptr_type %C [concrete] // CHECK:STDOUT: %pattern_type.506: type = pattern_type %ptr.31e [concrete] // CHECK:STDOUT: %.a69: Core.Form = init_form %C [concrete] // CHECK:STDOUT: %pattern_type.7c7: type = pattern_type %C [concrete] // CHECK:STDOUT: %G.type: type = fn_type @G [concrete] // CHECK:STDOUT: %G: %G.type = struct_value () [concrete] // CHECK:STDOUT: %F.specific_fn.540: = specific_function %F, @F(%C) [concrete] // CHECK:STDOUT: %complete_type.17a: = complete_type_witness %ptr.31e [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .C = %C.decl // CHECK:STDOUT: .F = %F.decl // CHECK:STDOUT: .G = %G.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %C.decl: type = class_decl @C [concrete = constants.%C] {} {} // CHECK:STDOUT: %F.decl: %F.type = fn_decl @F [concrete = constants.%F] { // CHECK:STDOUT: %T.patt: %pattern_type.98f = symbolic_binding_pattern T, 0 [concrete] // CHECK:STDOUT: %p.patt: @F.%pattern_type.loc6_16 (%pattern_type.4f4) = value_binding_pattern p [concrete] // CHECK:STDOUT: %p.param_patt: @F.%pattern_type.loc6_16 (%pattern_type.4f4) = value_param_pattern %p.patt [concrete] // CHECK:STDOUT: %return.patt: @F.%pattern_type.loc6_23 (%pattern_type.51d) = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: @F.%pattern_type.loc6_23 (%pattern_type.51d) = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %T.ref.loc6_26: type = name_ref T, %T.loc6_6.2 [symbolic = %T.loc6_6.1 (constants.%T)] // CHECK:STDOUT: %.loc6_26.3: Core.Form = init_form %T.ref.loc6_26 [symbolic = %.loc6_26.2 (constants.%.184)] // CHECK:STDOUT: %.loc6_10.1: type = splice_block %.loc6_10.2 [concrete = type] { // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %.loc6_10.2: type = type_literal type [concrete = type] // CHECK:STDOUT: } // CHECK:STDOUT: %T.loc6_6.2: type = symbolic_binding T, 0 [symbolic = %T.loc6_6.1 (constants.%T)] // CHECK:STDOUT: %p.param: @F.%ptr.loc6_20.1 (%ptr.e8f) = value_param call_param0 // CHECK:STDOUT: %.loc6_20: type = splice_block %ptr.loc6_20.2 [symbolic = %ptr.loc6_20.1 (constants.%ptr.e8f)] { // CHECK:STDOUT: %T.ref.loc6_19: type = name_ref T, %T.loc6_6.2 [symbolic = %T.loc6_6.1 (constants.%T)] // CHECK:STDOUT: %ptr.loc6_20.2: type = ptr_type %T.ref.loc6_19 [symbolic = %ptr.loc6_20.1 (constants.%ptr.e8f)] // CHECK:STDOUT: } // CHECK:STDOUT: %p: @F.%ptr.loc6_20.1 (%ptr.e8f) = value_binding p, %p.param // CHECK:STDOUT: %return.param: ref @F.%T.loc6_6.1 (%T) = out_param call_param1 // CHECK:STDOUT: %return: ref @F.%T.loc6_6.1 (%T) = return_slot %return.param // CHECK:STDOUT: } // CHECK:STDOUT: %G.decl: %G.type = fn_decl @G [concrete = constants.%G] { // CHECK:STDOUT: %p.patt: %pattern_type.506 = value_binding_pattern p [concrete] // CHECK:STDOUT: %p.param_patt: %pattern_type.506 = value_param_pattern %p.patt [concrete] // CHECK:STDOUT: %return.patt: %pattern_type.7c7 = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: %pattern_type.7c7 = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %C.ref.loc8_16: type = name_ref C, file.%C.decl [concrete = constants.%C] // CHECK:STDOUT: %.loc8_16.2: Core.Form = init_form %C.ref.loc8_16 [concrete = constants.%.a69] // CHECK:STDOUT: %p.param: %ptr.31e = value_param call_param0 // CHECK:STDOUT: %.loc8_10: type = splice_block %ptr [concrete = constants.%ptr.31e] { // CHECK:STDOUT: %C.ref.loc8_9: type = name_ref C, file.%C.decl [concrete = constants.%C] // CHECK:STDOUT: %ptr: type = ptr_type %C.ref.loc8_9 [concrete = constants.%ptr.31e] // CHECK:STDOUT: } // CHECK:STDOUT: %p: %ptr.31e = value_binding p, %p.param // CHECK:STDOUT: %return.param: ref %C = out_param call_param1 // CHECK:STDOUT: %return: ref %C = return_slot %return.param // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @C { // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type.357] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%C // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic fn @F(%T.loc6_6.2: type) { // CHECK:STDOUT: %T.loc6_6.1: type = symbolic_binding T, 0 [symbolic = %T.loc6_6.1 (constants.%T)] // CHECK:STDOUT: %ptr.loc6_20.1: type = ptr_type %T.loc6_6.1 [symbolic = %ptr.loc6_20.1 (constants.%ptr.e8f)] // CHECK:STDOUT: %pattern_type.loc6_16: type = pattern_type %ptr.loc6_20.1 [symbolic = %pattern_type.loc6_16 (constants.%pattern_type.4f4)] // CHECK:STDOUT: %.loc6_26.2: Core.Form = init_form %T.loc6_6.1 [symbolic = %.loc6_26.2 (constants.%.184)] // CHECK:STDOUT: %pattern_type.loc6_23: type = pattern_type %T.loc6_6.1 [symbolic = %pattern_type.loc6_23 (constants.%pattern_type.51d)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete.loc6_17: = require_complete_type %ptr.loc6_20.1 [symbolic = %require_complete.loc6_17 (constants.%require_complete.ef1)] // CHECK:STDOUT: %require_complete.loc6_26: = require_complete_type %T.loc6_6.1 [symbolic = %require_complete.loc6_26 (constants.%require_complete.944)] // CHECK:STDOUT: %F.specific_fn.loc6_37.2: = specific_function constants.%F, @F(%T.loc6_6.1) [symbolic = %F.specific_fn.loc6_37.2 (constants.%F.specific_fn.643)] // CHECK:STDOUT: // CHECK:STDOUT: fn(%p.param: @F.%ptr.loc6_20.1 (%ptr.e8f)) -> out %return.param: @F.%T.loc6_6.1 (%T) { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %F.ref: %F.type = name_ref F, file.%F.decl [concrete = constants.%F] // CHECK:STDOUT: %p.ref: @F.%ptr.loc6_20.1 (%ptr.e8f) = name_ref p, %p // CHECK:STDOUT: %F.specific_fn.loc6_37.1: = specific_function %F.ref, @F(constants.%T) [symbolic = %F.specific_fn.loc6_37.2 (constants.%F.specific_fn.643)] // CHECK:STDOUT: %.loc6_26.1: ref @F.%T.loc6_6.1 (%T) = splice_block %return.param {} // CHECK:STDOUT: %F.call: init @F.%T.loc6_6.1 (%T) to %.loc6_26.1 = call %F.specific_fn.loc6_37.1(%p.ref) // CHECK:STDOUT: return %F.call to %return.param // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @G(%p.param: %ptr.31e) -> out %return.param: %C { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %F.ref: %F.type = name_ref F, file.%F.decl [concrete = constants.%F] // CHECK:STDOUT: %p.ref: %ptr.31e = name_ref p, %p // CHECK:STDOUT: %F.specific_fn: = specific_function %F.ref, @F(constants.%C) [concrete = constants.%F.specific_fn.540] // CHECK:STDOUT: %.loc8_16.1: ref %C = splice_block %return.param {} // CHECK:STDOUT: %F.call: init %C to %.loc8_16.1 = call %F.specific_fn(%p.ref) // CHECK:STDOUT: return %F.call to %return.param // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @F(constants.%T) { // CHECK:STDOUT: %T.loc6_6.1 => constants.%T // CHECK:STDOUT: %ptr.loc6_20.1 => constants.%ptr.e8f // CHECK:STDOUT: %pattern_type.loc6_16 => constants.%pattern_type.4f4 // CHECK:STDOUT: %.loc6_26.2 => constants.%.184 // CHECK:STDOUT: %pattern_type.loc6_23 => constants.%pattern_type.51d // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete.loc6_17 => constants.%require_complete.ef1 // CHECK:STDOUT: %require_complete.loc6_26 => constants.%require_complete.944 // CHECK:STDOUT: %F.specific_fn.loc6_37.2 => constants.%F.specific_fn.643 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @F(constants.%C) { // CHECK:STDOUT: %T.loc6_6.1 => constants.%C // CHECK:STDOUT: %ptr.loc6_20.1 => constants.%ptr.31e // CHECK:STDOUT: %pattern_type.loc6_16 => constants.%pattern_type.506 // CHECK:STDOUT: %.loc6_26.2 => constants.%.a69 // CHECK:STDOUT: %pattern_type.loc6_23 => constants.%pattern_type.7c7 // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete.loc6_17 => constants.%complete_type.17a // CHECK:STDOUT: %require_complete.loc6_26 => constants.%complete_type.357 // CHECK:STDOUT: %F.specific_fn.loc6_37.2 => constants.%F.specific_fn.540 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- const.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %C: type = class_type @C [concrete] // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %complete_type.357: = complete_type_witness %empty_struct_type [concrete] // CHECK:STDOUT: %type: type = facet_type [concrete] // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self] // CHECK:STDOUT: %pattern_type.98f: type = pattern_type type [concrete] // CHECK:STDOUT: %T: type = symbolic_binding T, 0 [symbolic] // CHECK:STDOUT: %const.4ff: type = const_type %T [symbolic] // CHECK:STDOUT: %ptr.a15: type = ptr_type %const.4ff [symbolic] // CHECK:STDOUT: %pattern_type.26f: type = pattern_type %ptr.a15 [symbolic] // CHECK:STDOUT: %.184: Core.Form = init_form %T [symbolic] // CHECK:STDOUT: %pattern_type.51d: type = pattern_type %T [symbolic] // CHECK:STDOUT: %F.type: type = fn_type @F [concrete] // CHECK:STDOUT: %F: %F.type = struct_value () [concrete] // CHECK:STDOUT: %require_complete.0c1: = require_complete_type %ptr.a15 [symbolic] // CHECK:STDOUT: %require_complete.944: = require_complete_type %T [symbolic] // CHECK:STDOUT: %F.specific_fn.643: = specific_function %F, @F(%T) [symbolic] // CHECK:STDOUT: %const.0e5: type = const_type %C [concrete] // CHECK:STDOUT: %ptr.c45: type = ptr_type %const.0e5 [concrete] // CHECK:STDOUT: %pattern_type.6eb: type = pattern_type %ptr.c45 [concrete] // CHECK:STDOUT: %.a69: Core.Form = init_form %C [concrete] // CHECK:STDOUT: %pattern_type.7c7: type = pattern_type %C [concrete] // CHECK:STDOUT: %G.type: type = fn_type @G [concrete] // CHECK:STDOUT: %G: %G.type = struct_value () [concrete] // CHECK:STDOUT: %F.specific_fn.540: = specific_function %F, @F(%C) [concrete] // CHECK:STDOUT: %complete_type.e6f: = complete_type_witness %ptr.c45 [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .C = %C.decl // CHECK:STDOUT: .F = %F.decl // CHECK:STDOUT: .G = %G.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %C.decl: type = class_decl @C [concrete = constants.%C] {} {} // CHECK:STDOUT: %F.decl: %F.type = fn_decl @F [concrete = constants.%F] { // CHECK:STDOUT: %T.patt: %pattern_type.98f = symbolic_binding_pattern T, 0 [concrete] // CHECK:STDOUT: %p.patt: @F.%pattern_type.loc6_16 (%pattern_type.26f) = value_binding_pattern p [concrete] // CHECK:STDOUT: %p.param_patt: @F.%pattern_type.loc6_16 (%pattern_type.26f) = value_param_pattern %p.patt [concrete] // CHECK:STDOUT: %return.patt: @F.%pattern_type.loc6_29 (%pattern_type.51d) = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: @F.%pattern_type.loc6_29 (%pattern_type.51d) = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %T.ref.loc6_32: type = name_ref T, %T.loc6_6.2 [symbolic = %T.loc6_6.1 (constants.%T)] // CHECK:STDOUT: %.loc6_32.3: Core.Form = init_form %T.ref.loc6_32 [symbolic = %.loc6_32.2 (constants.%.184)] // CHECK:STDOUT: %.loc6_10.1: type = splice_block %.loc6_10.2 [concrete = type] { // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %.loc6_10.2: type = type_literal type [concrete = type] // CHECK:STDOUT: } // CHECK:STDOUT: %T.loc6_6.2: type = symbolic_binding T, 0 [symbolic = %T.loc6_6.1 (constants.%T)] // CHECK:STDOUT: %p.param: @F.%ptr.loc6_26.1 (%ptr.a15) = value_param call_param0 // CHECK:STDOUT: %.loc6_26: type = splice_block %ptr.loc6_26.2 [symbolic = %ptr.loc6_26.1 (constants.%ptr.a15)] { // CHECK:STDOUT: %T.ref.loc6_25: type = name_ref T, %T.loc6_6.2 [symbolic = %T.loc6_6.1 (constants.%T)] // CHECK:STDOUT: %const.loc6_19.2: type = const_type %T.ref.loc6_25 [symbolic = %const.loc6_19.1 (constants.%const.4ff)] // CHECK:STDOUT: %ptr.loc6_26.2: type = ptr_type %const.loc6_19.2 [symbolic = %ptr.loc6_26.1 (constants.%ptr.a15)] // CHECK:STDOUT: } // CHECK:STDOUT: %p: @F.%ptr.loc6_26.1 (%ptr.a15) = value_binding p, %p.param // CHECK:STDOUT: %return.param: ref @F.%T.loc6_6.1 (%T) = out_param call_param1 // CHECK:STDOUT: %return: ref @F.%T.loc6_6.1 (%T) = return_slot %return.param // CHECK:STDOUT: } // CHECK:STDOUT: %G.decl: %G.type = fn_decl @G [concrete = constants.%G] { // CHECK:STDOUT: %p.patt: %pattern_type.6eb = value_binding_pattern p [concrete] // CHECK:STDOUT: %p.param_patt: %pattern_type.6eb = value_param_pattern %p.patt [concrete] // CHECK:STDOUT: %return.patt: %pattern_type.7c7 = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: %pattern_type.7c7 = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %C.ref.loc8_22: type = name_ref C, file.%C.decl [concrete = constants.%C] // CHECK:STDOUT: %.loc8_22.2: Core.Form = init_form %C.ref.loc8_22 [concrete = constants.%.a69] // CHECK:STDOUT: %p.param: %ptr.c45 = value_param call_param0 // CHECK:STDOUT: %.loc8_16: type = splice_block %ptr [concrete = constants.%ptr.c45] { // CHECK:STDOUT: %C.ref.loc8_15: type = name_ref C, file.%C.decl [concrete = constants.%C] // CHECK:STDOUT: %const: type = const_type %C.ref.loc8_15 [concrete = constants.%const.0e5] // CHECK:STDOUT: %ptr: type = ptr_type %const [concrete = constants.%ptr.c45] // CHECK:STDOUT: } // CHECK:STDOUT: %p: %ptr.c45 = value_binding p, %p.param // CHECK:STDOUT: %return.param: ref %C = out_param call_param1 // CHECK:STDOUT: %return: ref %C = return_slot %return.param // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @C { // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type.357] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%C // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic fn @F(%T.loc6_6.2: type) { // CHECK:STDOUT: %T.loc6_6.1: type = symbolic_binding T, 0 [symbolic = %T.loc6_6.1 (constants.%T)] // CHECK:STDOUT: %const.loc6_19.1: type = const_type %T.loc6_6.1 [symbolic = %const.loc6_19.1 (constants.%const.4ff)] // CHECK:STDOUT: %ptr.loc6_26.1: type = ptr_type %const.loc6_19.1 [symbolic = %ptr.loc6_26.1 (constants.%ptr.a15)] // CHECK:STDOUT: %pattern_type.loc6_16: type = pattern_type %ptr.loc6_26.1 [symbolic = %pattern_type.loc6_16 (constants.%pattern_type.26f)] // CHECK:STDOUT: %.loc6_32.2: Core.Form = init_form %T.loc6_6.1 [symbolic = %.loc6_32.2 (constants.%.184)] // CHECK:STDOUT: %pattern_type.loc6_29: type = pattern_type %T.loc6_6.1 [symbolic = %pattern_type.loc6_29 (constants.%pattern_type.51d)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete.loc6_17: = require_complete_type %ptr.loc6_26.1 [symbolic = %require_complete.loc6_17 (constants.%require_complete.0c1)] // CHECK:STDOUT: %require_complete.loc6_32: = require_complete_type %T.loc6_6.1 [symbolic = %require_complete.loc6_32 (constants.%require_complete.944)] // CHECK:STDOUT: %F.specific_fn.loc6_43.2: = specific_function constants.%F, @F(%T.loc6_6.1) [symbolic = %F.specific_fn.loc6_43.2 (constants.%F.specific_fn.643)] // CHECK:STDOUT: // CHECK:STDOUT: fn(%p.param: @F.%ptr.loc6_26.1 (%ptr.a15)) -> out %return.param: @F.%T.loc6_6.1 (%T) { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %F.ref: %F.type = name_ref F, file.%F.decl [concrete = constants.%F] // CHECK:STDOUT: %p.ref: @F.%ptr.loc6_26.1 (%ptr.a15) = name_ref p, %p // CHECK:STDOUT: %F.specific_fn.loc6_43.1: = specific_function %F.ref, @F(constants.%T) [symbolic = %F.specific_fn.loc6_43.2 (constants.%F.specific_fn.643)] // CHECK:STDOUT: %.loc6_32.1: ref @F.%T.loc6_6.1 (%T) = splice_block %return.param {} // CHECK:STDOUT: %F.call: init @F.%T.loc6_6.1 (%T) to %.loc6_32.1 = call %F.specific_fn.loc6_43.1(%p.ref) // CHECK:STDOUT: return %F.call to %return.param // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @G(%p.param: %ptr.c45) -> out %return.param: %C { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %F.ref: %F.type = name_ref F, file.%F.decl [concrete = constants.%F] // CHECK:STDOUT: %p.ref: %ptr.c45 = name_ref p, %p // CHECK:STDOUT: %F.specific_fn: = specific_function %F.ref, @F(constants.%C) [concrete = constants.%F.specific_fn.540] // CHECK:STDOUT: %.loc8_22.1: ref %C = splice_block %return.param {} // CHECK:STDOUT: %F.call: init %C to %.loc8_22.1 = call %F.specific_fn(%p.ref) // CHECK:STDOUT: return %F.call to %return.param // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @F(constants.%T) { // CHECK:STDOUT: %T.loc6_6.1 => constants.%T // CHECK:STDOUT: %const.loc6_19.1 => constants.%const.4ff // CHECK:STDOUT: %ptr.loc6_26.1 => constants.%ptr.a15 // CHECK:STDOUT: %pattern_type.loc6_16 => constants.%pattern_type.26f // CHECK:STDOUT: %.loc6_32.2 => constants.%.184 // CHECK:STDOUT: %pattern_type.loc6_29 => constants.%pattern_type.51d // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete.loc6_17 => constants.%require_complete.0c1 // CHECK:STDOUT: %require_complete.loc6_32 => constants.%require_complete.944 // CHECK:STDOUT: %F.specific_fn.loc6_43.2 => constants.%F.specific_fn.643 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @F(constants.%C) { // CHECK:STDOUT: %T.loc6_6.1 => constants.%C // CHECK:STDOUT: %const.loc6_19.1 => constants.%const.0e5 // CHECK:STDOUT: %ptr.loc6_26.1 => constants.%ptr.c45 // CHECK:STDOUT: %pattern_type.loc6_16 => constants.%pattern_type.6eb // CHECK:STDOUT: %.loc6_32.2 => constants.%.a69 // CHECK:STDOUT: %pattern_type.loc6_29 => constants.%pattern_type.7c7 // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete.loc6_17 => constants.%complete_type.e6f // CHECK:STDOUT: %require_complete.loc6_32 => constants.%complete_type.357 // CHECK:STDOUT: %F.specific_fn.loc6_43.2 => constants.%F.specific_fn.540 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- nonconst_from_const.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %C: type = class_type @C [concrete] // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %complete_type.357: = complete_type_witness %empty_struct_type [concrete] // CHECK:STDOUT: %type: type = facet_type [concrete] // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self] // CHECK:STDOUT: %pattern_type.98f: type = pattern_type type [concrete] // CHECK:STDOUT: %T: type = symbolic_binding T, 0 [symbolic] // CHECK:STDOUT: %ptr.e8f: type = ptr_type %T [symbolic] // CHECK:STDOUT: %pattern_type.4f4: type = pattern_type %ptr.e8f [symbolic] // CHECK:STDOUT: %.184: Core.Form = init_form %T [symbolic] // CHECK:STDOUT: %pattern_type.51d: type = pattern_type %T [symbolic] // CHECK:STDOUT: %F.type: type = fn_type @F [concrete] // CHECK:STDOUT: %F: %F.type = struct_value () [concrete] // CHECK:STDOUT: %require_complete.ef1: = require_complete_type %ptr.e8f [symbolic] // CHECK:STDOUT: %require_complete.944: = require_complete_type %T [symbolic] // CHECK:STDOUT: %F.specific_fn.643: = specific_function %F, @F(%T) [symbolic] // CHECK:STDOUT: %const: type = const_type %C [concrete] // CHECK:STDOUT: %ptr.c45: type = ptr_type %const [concrete] // CHECK:STDOUT: %pattern_type.6eb: type = pattern_type %ptr.c45 [concrete] // CHECK:STDOUT: %.007: Core.Form = init_form %const [concrete] // CHECK:STDOUT: %pattern_type.03b: type = pattern_type %const [concrete] // CHECK:STDOUT: %G.type: type = fn_type @G [concrete] // CHECK:STDOUT: %G: %G.type = struct_value () [concrete] // CHECK:STDOUT: %F.specific_fn.98a: = specific_function %F, @F(%const) [concrete] // CHECK:STDOUT: %complete_type.e6f: = complete_type_witness %ptr.c45 [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .C = %C.decl // CHECK:STDOUT: .F = %F.decl // CHECK:STDOUT: .G = %G.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %C.decl: type = class_decl @C [concrete = constants.%C] {} {} // CHECK:STDOUT: %F.decl: %F.type = fn_decl @F [concrete = constants.%F] { // CHECK:STDOUT: %T.patt: %pattern_type.98f = symbolic_binding_pattern T, 0 [concrete] // CHECK:STDOUT: %p.patt: @F.%pattern_type.loc6_16 (%pattern_type.4f4) = value_binding_pattern p [concrete] // CHECK:STDOUT: %p.param_patt: @F.%pattern_type.loc6_16 (%pattern_type.4f4) = value_param_pattern %p.patt [concrete] // CHECK:STDOUT: %return.patt: @F.%pattern_type.loc6_23 (%pattern_type.51d) = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: @F.%pattern_type.loc6_23 (%pattern_type.51d) = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %T.ref.loc6_26: type = name_ref T, %T.loc6_6.2 [symbolic = %T.loc6_6.1 (constants.%T)] // CHECK:STDOUT: %.loc6_26.3: Core.Form = init_form %T.ref.loc6_26 [symbolic = %.loc6_26.2 (constants.%.184)] // CHECK:STDOUT: %.loc6_10.1: type = splice_block %.loc6_10.2 [concrete = type] { // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %.loc6_10.2: type = type_literal type [concrete = type] // CHECK:STDOUT: } // CHECK:STDOUT: %T.loc6_6.2: type = symbolic_binding T, 0 [symbolic = %T.loc6_6.1 (constants.%T)] // CHECK:STDOUT: %p.param: @F.%ptr.loc6_20.1 (%ptr.e8f) = value_param call_param0 // CHECK:STDOUT: %.loc6_20: type = splice_block %ptr.loc6_20.2 [symbolic = %ptr.loc6_20.1 (constants.%ptr.e8f)] { // CHECK:STDOUT: %T.ref.loc6_19: type = name_ref T, %T.loc6_6.2 [symbolic = %T.loc6_6.1 (constants.%T)] // CHECK:STDOUT: %ptr.loc6_20.2: type = ptr_type %T.ref.loc6_19 [symbolic = %ptr.loc6_20.1 (constants.%ptr.e8f)] // CHECK:STDOUT: } // CHECK:STDOUT: %p: @F.%ptr.loc6_20.1 (%ptr.e8f) = value_binding p, %p.param // CHECK:STDOUT: %return.param: ref @F.%T.loc6_6.1 (%T) = out_param call_param1 // CHECK:STDOUT: %return: ref @F.%T.loc6_6.1 (%T) = return_slot %return.param // CHECK:STDOUT: } // CHECK:STDOUT: %G.decl: %G.type = fn_decl @G [concrete = constants.%G] { // CHECK:STDOUT: %p.patt: %pattern_type.6eb = value_binding_pattern p [concrete] // CHECK:STDOUT: %p.param_patt: %pattern_type.6eb = value_param_pattern %p.patt [concrete] // CHECK:STDOUT: %return.patt: %pattern_type.03b = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: %pattern_type.03b = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %C.ref.loc8_28: type = name_ref C, file.%C.decl [concrete = constants.%C] // CHECK:STDOUT: %const.loc8_22: type = const_type %C.ref.loc8_28 [concrete = constants.%const] // CHECK:STDOUT: %.loc8_22.2: Core.Form = init_form %const.loc8_22 [concrete = constants.%.007] // CHECK:STDOUT: %p.param: %ptr.c45 = value_param call_param0 // CHECK:STDOUT: %.loc8_16: type = splice_block %ptr [concrete = constants.%ptr.c45] { // CHECK:STDOUT: %C.ref.loc8_15: type = name_ref C, file.%C.decl [concrete = constants.%C] // CHECK:STDOUT: %const.loc8_9: type = const_type %C.ref.loc8_15 [concrete = constants.%const] // CHECK:STDOUT: %ptr: type = ptr_type %const.loc8_9 [concrete = constants.%ptr.c45] // CHECK:STDOUT: } // CHECK:STDOUT: %p: %ptr.c45 = value_binding p, %p.param // CHECK:STDOUT: %return.param: ref %const = out_param call_param1 // CHECK:STDOUT: %return: ref %const = return_slot %return.param // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @C { // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type.357] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%C // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic fn @F(%T.loc6_6.2: type) { // CHECK:STDOUT: %T.loc6_6.1: type = symbolic_binding T, 0 [symbolic = %T.loc6_6.1 (constants.%T)] // CHECK:STDOUT: %ptr.loc6_20.1: type = ptr_type %T.loc6_6.1 [symbolic = %ptr.loc6_20.1 (constants.%ptr.e8f)] // CHECK:STDOUT: %pattern_type.loc6_16: type = pattern_type %ptr.loc6_20.1 [symbolic = %pattern_type.loc6_16 (constants.%pattern_type.4f4)] // CHECK:STDOUT: %.loc6_26.2: Core.Form = init_form %T.loc6_6.1 [symbolic = %.loc6_26.2 (constants.%.184)] // CHECK:STDOUT: %pattern_type.loc6_23: type = pattern_type %T.loc6_6.1 [symbolic = %pattern_type.loc6_23 (constants.%pattern_type.51d)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete.loc6_17: = require_complete_type %ptr.loc6_20.1 [symbolic = %require_complete.loc6_17 (constants.%require_complete.ef1)] // CHECK:STDOUT: %require_complete.loc6_26: = require_complete_type %T.loc6_6.1 [symbolic = %require_complete.loc6_26 (constants.%require_complete.944)] // CHECK:STDOUT: %F.specific_fn.loc6_37.2: = specific_function constants.%F, @F(%T.loc6_6.1) [symbolic = %F.specific_fn.loc6_37.2 (constants.%F.specific_fn.643)] // CHECK:STDOUT: // CHECK:STDOUT: fn(%p.param: @F.%ptr.loc6_20.1 (%ptr.e8f)) -> out %return.param: @F.%T.loc6_6.1 (%T) { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %F.ref: %F.type = name_ref F, file.%F.decl [concrete = constants.%F] // CHECK:STDOUT: %p.ref: @F.%ptr.loc6_20.1 (%ptr.e8f) = name_ref p, %p // CHECK:STDOUT: %F.specific_fn.loc6_37.1: = specific_function %F.ref, @F(constants.%T) [symbolic = %F.specific_fn.loc6_37.2 (constants.%F.specific_fn.643)] // CHECK:STDOUT: %.loc6_26.1: ref @F.%T.loc6_6.1 (%T) = splice_block %return.param {} // CHECK:STDOUT: %F.call: init @F.%T.loc6_6.1 (%T) to %.loc6_26.1 = call %F.specific_fn.loc6_37.1(%p.ref) // CHECK:STDOUT: return %F.call to %return.param // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @G(%p.param: %ptr.c45) -> out %return.param: %const { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %F.ref: %F.type = name_ref F, file.%F.decl [concrete = constants.%F] // CHECK:STDOUT: %p.ref: %ptr.c45 = name_ref p, %p // CHECK:STDOUT: %F.specific_fn: = specific_function %F.ref, @F(constants.%const) [concrete = constants.%F.specific_fn.98a] // CHECK:STDOUT: %.loc8_22.1: ref %const = splice_block %return.param {} // CHECK:STDOUT: %F.call: init %const to %.loc8_22.1 = call %F.specific_fn(%p.ref) // CHECK:STDOUT: return %F.call to %return.param // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @F(constants.%T) { // CHECK:STDOUT: %T.loc6_6.1 => constants.%T // CHECK:STDOUT: %ptr.loc6_20.1 => constants.%ptr.e8f // CHECK:STDOUT: %pattern_type.loc6_16 => constants.%pattern_type.4f4 // CHECK:STDOUT: %.loc6_26.2 => constants.%.184 // CHECK:STDOUT: %pattern_type.loc6_23 => constants.%pattern_type.51d // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete.loc6_17 => constants.%require_complete.ef1 // CHECK:STDOUT: %require_complete.loc6_26 => constants.%require_complete.944 // CHECK:STDOUT: %F.specific_fn.loc6_37.2 => constants.%F.specific_fn.643 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @F(constants.%const) { // CHECK:STDOUT: %T.loc6_6.1 => constants.%const // CHECK:STDOUT: %ptr.loc6_20.1 => constants.%ptr.c45 // CHECK:STDOUT: %pattern_type.loc6_16 => constants.%pattern_type.6eb // CHECK:STDOUT: %.loc6_26.2 => constants.%.007 // CHECK:STDOUT: %pattern_type.loc6_23 => constants.%pattern_type.03b // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete.loc6_17 => constants.%complete_type.e6f // CHECK:STDOUT: %require_complete.loc6_26 => constants.%complete_type.357 // CHECK:STDOUT: %F.specific_fn.loc6_37.2 => constants.%F.specific_fn.98a // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- fail_const_from_nonconst.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %C: type = class_type @C [concrete] // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %complete_type: = complete_type_witness %empty_struct_type [concrete] // CHECK:STDOUT: %type: type = facet_type [concrete] // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self] // CHECK:STDOUT: %pattern_type.98f: type = pattern_type type [concrete] // CHECK:STDOUT: %T: type = symbolic_binding T, 0 [symbolic] // CHECK:STDOUT: %const.4ff: type = const_type %T [symbolic] // CHECK:STDOUT: %ptr.a15: type = ptr_type %const.4ff [symbolic] // CHECK:STDOUT: %pattern_type.26f: type = pattern_type %ptr.a15 [symbolic] // CHECK:STDOUT: %.184: Core.Form = init_form %T [symbolic] // CHECK:STDOUT: %pattern_type.51d: type = pattern_type %T [symbolic] // CHECK:STDOUT: %F.type: type = fn_type @F [concrete] // CHECK:STDOUT: %F: %F.type = struct_value () [concrete] // CHECK:STDOUT: %require_complete.0c1: = require_complete_type %ptr.a15 [symbolic] // CHECK:STDOUT: %require_complete.944: = require_complete_type %T [symbolic] // CHECK:STDOUT: %F.specific_fn: = specific_function %F, @F(%T) [symbolic] // CHECK:STDOUT: %ptr.31e: type = ptr_type %C [concrete] // CHECK:STDOUT: %pattern_type.506: type = pattern_type %ptr.31e [concrete] // CHECK:STDOUT: %const.0e5: type = const_type %C [concrete] // CHECK:STDOUT: %.007: Core.Form = init_form %const.0e5 [concrete] // CHECK:STDOUT: %pattern_type.03b: type = pattern_type %const.0e5 [concrete] // CHECK:STDOUT: %G.type: type = fn_type @G [concrete] // CHECK:STDOUT: %G: %G.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .C = %C.decl // CHECK:STDOUT: .F = %F.decl // CHECK:STDOUT: .G = %G.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %C.decl: type = class_decl @C [concrete = constants.%C] {} {} // CHECK:STDOUT: %F.decl: %F.type = fn_decl @F [concrete = constants.%F] { // CHECK:STDOUT: %T.patt: %pattern_type.98f = symbolic_binding_pattern T, 0 [concrete] // CHECK:STDOUT: %p.patt: @F.%pattern_type.loc6_16 (%pattern_type.26f) = value_binding_pattern p [concrete] // CHECK:STDOUT: %p.param_patt: @F.%pattern_type.loc6_16 (%pattern_type.26f) = value_param_pattern %p.patt [concrete] // CHECK:STDOUT: %return.patt: @F.%pattern_type.loc6_29 (%pattern_type.51d) = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: @F.%pattern_type.loc6_29 (%pattern_type.51d) = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %T.ref.loc6_32: type = name_ref T, %T.loc6_6.2 [symbolic = %T.loc6_6.1 (constants.%T)] // CHECK:STDOUT: %.loc6_32.3: Core.Form = init_form %T.ref.loc6_32 [symbolic = %.loc6_32.2 (constants.%.184)] // CHECK:STDOUT: %.loc6_10.1: type = splice_block %.loc6_10.2 [concrete = type] { // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %.loc6_10.2: type = type_literal type [concrete = type] // CHECK:STDOUT: } // CHECK:STDOUT: %T.loc6_6.2: type = symbolic_binding T, 0 [symbolic = %T.loc6_6.1 (constants.%T)] // CHECK:STDOUT: %p.param: @F.%ptr.loc6_26.1 (%ptr.a15) = value_param call_param0 // CHECK:STDOUT: %.loc6_26: type = splice_block %ptr.loc6_26.2 [symbolic = %ptr.loc6_26.1 (constants.%ptr.a15)] { // CHECK:STDOUT: %T.ref.loc6_25: type = name_ref T, %T.loc6_6.2 [symbolic = %T.loc6_6.1 (constants.%T)] // CHECK:STDOUT: %const.loc6_19.2: type = const_type %T.ref.loc6_25 [symbolic = %const.loc6_19.1 (constants.%const.4ff)] // CHECK:STDOUT: %ptr.loc6_26.2: type = ptr_type %const.loc6_19.2 [symbolic = %ptr.loc6_26.1 (constants.%ptr.a15)] // CHECK:STDOUT: } // CHECK:STDOUT: %p: @F.%ptr.loc6_26.1 (%ptr.a15) = value_binding p, %p.param // CHECK:STDOUT: %return.param: ref @F.%T.loc6_6.1 (%T) = out_param call_param1 // CHECK:STDOUT: %return: ref @F.%T.loc6_6.1 (%T) = return_slot %return.param // CHECK:STDOUT: } // CHECK:STDOUT: %G.decl: %G.type = fn_decl @G [concrete = constants.%G] { // CHECK:STDOUT: %p.patt: %pattern_type.506 = value_binding_pattern p [concrete] // CHECK:STDOUT: %p.param_patt: %pattern_type.506 = value_param_pattern %p.patt [concrete] // CHECK:STDOUT: %return.patt: %pattern_type.03b = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: %pattern_type.03b = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %C.ref.loc8_22: type = name_ref C, file.%C.decl [concrete = constants.%C] // CHECK:STDOUT: %const: type = const_type %C.ref.loc8_22 [concrete = constants.%const.0e5] // CHECK:STDOUT: %.loc8_16: Core.Form = init_form %const [concrete = constants.%.007] // CHECK:STDOUT: %p.param: %ptr.31e = value_param call_param0 // CHECK:STDOUT: %.loc8_10: type = splice_block %ptr [concrete = constants.%ptr.31e] { // CHECK:STDOUT: %C.ref.loc8_9: type = name_ref C, file.%C.decl [concrete = constants.%C] // CHECK:STDOUT: %ptr: type = ptr_type %C.ref.loc8_9 [concrete = constants.%ptr.31e] // CHECK:STDOUT: } // CHECK:STDOUT: %p: %ptr.31e = value_binding p, %p.param // CHECK:STDOUT: %return.param: ref %const.0e5 = out_param call_param1 // CHECK:STDOUT: %return: ref %const.0e5 = return_slot %return.param // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @C { // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%C // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic fn @F(%T.loc6_6.2: type) { // CHECK:STDOUT: %T.loc6_6.1: type = symbolic_binding T, 0 [symbolic = %T.loc6_6.1 (constants.%T)] // CHECK:STDOUT: %const.loc6_19.1: type = const_type %T.loc6_6.1 [symbolic = %const.loc6_19.1 (constants.%const.4ff)] // CHECK:STDOUT: %ptr.loc6_26.1: type = ptr_type %const.loc6_19.1 [symbolic = %ptr.loc6_26.1 (constants.%ptr.a15)] // CHECK:STDOUT: %pattern_type.loc6_16: type = pattern_type %ptr.loc6_26.1 [symbolic = %pattern_type.loc6_16 (constants.%pattern_type.26f)] // CHECK:STDOUT: %.loc6_32.2: Core.Form = init_form %T.loc6_6.1 [symbolic = %.loc6_32.2 (constants.%.184)] // CHECK:STDOUT: %pattern_type.loc6_29: type = pattern_type %T.loc6_6.1 [symbolic = %pattern_type.loc6_29 (constants.%pattern_type.51d)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete.loc6_17: = require_complete_type %ptr.loc6_26.1 [symbolic = %require_complete.loc6_17 (constants.%require_complete.0c1)] // CHECK:STDOUT: %require_complete.loc6_32: = require_complete_type %T.loc6_6.1 [symbolic = %require_complete.loc6_32 (constants.%require_complete.944)] // CHECK:STDOUT: %F.specific_fn.loc6_43.2: = specific_function constants.%F, @F(%T.loc6_6.1) [symbolic = %F.specific_fn.loc6_43.2 (constants.%F.specific_fn)] // CHECK:STDOUT: // CHECK:STDOUT: fn(%p.param: @F.%ptr.loc6_26.1 (%ptr.a15)) -> out %return.param: @F.%T.loc6_6.1 (%T) { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %F.ref: %F.type = name_ref F, file.%F.decl [concrete = constants.%F] // CHECK:STDOUT: %p.ref: @F.%ptr.loc6_26.1 (%ptr.a15) = name_ref p, %p // CHECK:STDOUT: %F.specific_fn.loc6_43.1: = specific_function %F.ref, @F(constants.%T) [symbolic = %F.specific_fn.loc6_43.2 (constants.%F.specific_fn)] // CHECK:STDOUT: %.loc6_32.1: ref @F.%T.loc6_6.1 (%T) = splice_block %return.param {} // CHECK:STDOUT: %F.call: init @F.%T.loc6_6.1 (%T) to %.loc6_32.1 = call %F.specific_fn.loc6_43.1(%p.ref) // CHECK:STDOUT: return %F.call to %return.param // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @G(%p.param: %ptr.31e) -> out %return.param: %const.0e5 { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %F.ref: %F.type = name_ref F, file.%F.decl [concrete = constants.%F] // CHECK:STDOUT: %p.ref: %ptr.31e = name_ref p, %p // CHECK:STDOUT: return to %return.param // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @F(constants.%T) { // CHECK:STDOUT: %T.loc6_6.1 => constants.%T // CHECK:STDOUT: %const.loc6_19.1 => constants.%const.4ff // CHECK:STDOUT: %ptr.loc6_26.1 => constants.%ptr.a15 // CHECK:STDOUT: %pattern_type.loc6_16 => constants.%pattern_type.26f // CHECK:STDOUT: %.loc6_32.2 => constants.%.184 // CHECK:STDOUT: %pattern_type.loc6_29 => constants.%pattern_type.51d // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete.loc6_17 => constants.%require_complete.0c1 // CHECK:STDOUT: %require_complete.loc6_32 => constants.%require_complete.944 // CHECK:STDOUT: %F.specific_fn.loc6_43.2 => constants.%F.specific_fn // CHECK:STDOUT: } // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/deduce/value_with_type_through_access.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/int.carbon // TODO: Add ranges and switch to "--dump-sem-ir-ranges=only". // EXTRA-ARGS: --dump-sem-ir-ranges=if-present // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/deduce/value_with_type_through_access.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/deduce/value_with_type_through_access.carbon // --- tuple_access.carbon library "[[@TEST_NAME]]"; class HoldsType(T:! (type, )) {} // `a` is received as a value, and has a type that requires going through // TupleAccess. Building the value representation must handle this indirection. fn F[T:! (type, )](unused x: HoldsType(T), unused a: T.0) {} class C {} fn G() { F({} as HoldsType((C, )), {}); } // --- struct_access.carbon library "[[@TEST_NAME]]"; class HoldsType(T:! {.t: type}) {} // `a` is received as a value, and has a type that requires going through // StructAccess. Building the value representation must handle this indirection. fn F[T:! {.t: type}](unused x: HoldsType(T), unused a: T.t) {} class C {} fn G() { F({} as HoldsType({.t = C}), {}); } // --- fail_todo_class_access.carbon library "[[@TEST_NAME]]"; class Class { var t: type; } class HoldsType(T:! Class) {} // `a` is received as a value, and has a type that requires going through // StructAccess. Building the value representation must handle this indirection. // // TODO: The `Class` value in `T` is a comptime constant and this expression // should be able to eval to a type. We would expect the `.` in `T.t` to be a // ClassElementAccess but it is an AcquireValue, which has a runtime value. // // CHECK:STDERR: fail_todo_class_access.carbon:[[@LINE+4]]:51: error: cannot evaluate type expression [TypeExprEvaluationFailure] // CHECK:STDERR: fn F[T:! Class](unused x: HoldsType(T), unused a: T.t) {} // CHECK:STDERR: ^~~ // CHECK:STDERR: fn F[T:! Class](unused x: HoldsType(T), unused a: T.t) {} class C {} fn G(c:! Class) { F({} as HoldsType(c), {}); } fn H() { // CHECK:STDERR: fail_todo_class_access.carbon:[[@LINE+7]]:3: error: argument for generic parameter is not a compile-time constant [CompTimeArgumentNotConstant] // CHECK:STDERR: G({.t = C}); // CHECK:STDERR: ^~~~~~~~~~~ // CHECK:STDERR: fail_todo_class_access.carbon:[[@LINE-8]]:6: note: initializing generic parameter `c` declared here [InitializingGenericParam] // CHECK:STDERR: fn G(c:! Class) { // CHECK:STDERR: ^ // CHECK:STDERR: G({.t = C}); } // --- fail_todo_array_index.carbon library "[[@TEST_NAME]]"; class HoldsType(T:! array(type, 1)) {} // `a` is received as a value, and has a type that requires going through // ArrayIndex. Building the value representation must handle this indirection. // CHECK:STDERR: fail_todo_array_index.carbon:[[@LINE+4]]:60: error: cannot evaluate type expression [TypeExprEvaluationFailure] // CHECK:STDERR: fn F[T:! array(type, 1)](unused x: HoldsType(T), unused a: T[0]) {} // CHECK:STDERR: ^~~~ // CHECK:STDERR: fn F[T:! array(type, 1)](unused x: HoldsType(T), unused a: T[0]) {} class C {} fn G() { // CHECK:STDERR: fail_todo_array_index.carbon:[[@LINE+7]]:11: error: argument for generic parameter is not a compile-time constant [CompTimeArgumentNotConstant] // CHECK:STDERR: F({} as HoldsType((C, ) as array(type, 1)), {}); // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: fail_todo_array_index.carbon:[[@LINE-16]]:17: note: initializing generic parameter `T` declared here [InitializingGenericParam] // CHECK:STDERR: class HoldsType(T:! array(type, 1)) {} // CHECK:STDERR: ^ // CHECK:STDERR: F({} as HoldsType((C, ) as array(type, 1)), {}); } // CHECK:STDOUT: --- tuple_access.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %type: type = facet_type [concrete] // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self] // CHECK:STDOUT: %tuple.type: type = tuple_type (type) [concrete] // CHECK:STDOUT: %tuple.0d2: %tuple.type = tuple_value (type) [concrete] // CHECK:STDOUT: %pattern_type.f1e: type = pattern_type %tuple.type [concrete] // CHECK:STDOUT: %T: %tuple.type = symbolic_binding T, 0 [symbolic] // CHECK:STDOUT: %HoldsType.type: type = generic_class_type @HoldsType [concrete] // CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] // CHECK:STDOUT: %HoldsType.generic: %HoldsType.type = struct_value () [concrete] // CHECK:STDOUT: %HoldsType.f6a: type = class_type @HoldsType, @HoldsType(%T) [symbolic] // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %complete_type: = complete_type_witness %empty_struct_type [concrete] // CHECK:STDOUT: %pattern_type.17d: type = pattern_type %HoldsType.f6a [symbolic] // CHECK:STDOUT: %int_0: Core.IntLiteral = int_value 0 [concrete] // CHECK:STDOUT: %tuple.elem0: type = tuple_access %T, element0 [symbolic] // CHECK:STDOUT: %pattern_type.e66: type = pattern_type %tuple.elem0 [symbolic] // CHECK:STDOUT: %F.type: type = fn_type @F [concrete] // CHECK:STDOUT: %F: %F.type = struct_value () [concrete] // CHECK:STDOUT: %require_complete.7bf: = require_complete_type %HoldsType.f6a [symbolic] // CHECK:STDOUT: %require_complete.e7a: = require_complete_type %tuple.elem0 [symbolic] // CHECK:STDOUT: %C: type = class_type @C [concrete] // CHECK:STDOUT: %G.type: type = fn_type @G [concrete] // CHECK:STDOUT: %G: %G.type = struct_value () [concrete] // CHECK:STDOUT: %empty_struct: %empty_struct_type = struct_value () [concrete] // CHECK:STDOUT: %tuple.1f1: %tuple.type = tuple_value (%C) [concrete] // CHECK:STDOUT: %HoldsType.a31: type = class_type @HoldsType, @HoldsType(%tuple.1f1) [concrete] // CHECK:STDOUT: %HoldsType.val: %HoldsType.a31 = struct_value () [concrete] // CHECK:STDOUT: %pattern_type.a646: type = pattern_type %HoldsType.a31 [concrete] // CHECK:STDOUT: %pattern_type.7c7: type = pattern_type %C [concrete] // CHECK:STDOUT: %F.specific_fn: = specific_function %F, @F(%tuple.1f1) [concrete] // CHECK:STDOUT: %C.val: %C = struct_value () [concrete] // CHECK:STDOUT: %Destroy.type: type = facet_type <@Destroy> [concrete] // CHECK:STDOUT: %Destroy.Op.type.bae255.1: type = fn_type @Destroy.Op.loc13_30 [concrete] // CHECK:STDOUT: %Destroy.Op.651ba6.1: %Destroy.Op.type.bae255.1 = struct_value () [concrete] // CHECK:STDOUT: %Destroy.Op.type.bae255.2: type = fn_type @Destroy.Op.loc13_8 [concrete] // CHECK:STDOUT: %Destroy.Op.651ba6.2: %Destroy.Op.type.bae255.2 = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: .Destroy = %Core.Destroy // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: %Core.Destroy: type = import_ref Core//prelude/parts/destroy, Destroy, loaded [concrete = constants.%Destroy.type] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .HoldsType = %HoldsType.decl // CHECK:STDOUT: .F = %F.decl // CHECK:STDOUT: .C = %C.decl // CHECK:STDOUT: .G = %G.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %HoldsType.decl: %HoldsType.type = class_decl @HoldsType [concrete = constants.%HoldsType.generic] { // CHECK:STDOUT: %T.patt: %pattern_type.f1e = symbolic_binding_pattern T, 0 [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %.loc4_28.1: type = splice_block %.loc4_28.3 [concrete = constants.%tuple.type] { // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %.loc4_22: type = type_literal type [concrete = type] // CHECK:STDOUT: %.loc4_28.2: %tuple.type = tuple_literal (%.loc4_22) [concrete = constants.%tuple.0d2] // CHECK:STDOUT: %.loc4_28.3: type = converted %.loc4_28.2, constants.%tuple.type [concrete = constants.%tuple.type] // CHECK:STDOUT: } // CHECK:STDOUT: %T.loc4_17.2: %tuple.type = symbolic_binding T, 0 [symbolic = %T.loc4_17.1 (constants.%T)] // CHECK:STDOUT: } // CHECK:STDOUT: %F.decl: %F.type = fn_decl @F [concrete = constants.%F] { // CHECK:STDOUT: %T.patt: %pattern_type.f1e = symbolic_binding_pattern T, 0 [concrete] // CHECK:STDOUT: %x.patt: @F.%pattern_type.loc8_27 (%pattern_type.17d) = value_binding_pattern x [concrete] // CHECK:STDOUT: %x.param_patt: @F.%pattern_type.loc8_27 (%pattern_type.17d) = value_param_pattern %x.patt [concrete] // CHECK:STDOUT: %a.patt: @F.%pattern_type.loc8_51 (%pattern_type.e66) = value_binding_pattern a [concrete] // CHECK:STDOUT: %a.param_patt: @F.%pattern_type.loc8_51 (%pattern_type.e66) = value_param_pattern %a.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %.loc8_17.1: type = splice_block %.loc8_17.3 [concrete = constants.%tuple.type] { // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %.loc8_11: type = type_literal type [concrete = type] // CHECK:STDOUT: %.loc8_17.2: %tuple.type = tuple_literal (%.loc8_11) [concrete = constants.%tuple.0d2] // CHECK:STDOUT: %.loc8_17.3: type = converted %.loc8_17.2, constants.%tuple.type [concrete = constants.%tuple.type] // CHECK:STDOUT: } // CHECK:STDOUT: %T.loc8_6.2: %tuple.type = symbolic_binding T, 0 [symbolic = %T.loc8_6.1 (constants.%T)] // CHECK:STDOUT: %x.param: @F.%HoldsType.loc8_41.1 (%HoldsType.f6a) = value_param call_param0 // CHECK:STDOUT: %.loc8_41: type = splice_block %HoldsType.loc8_41.2 [symbolic = %HoldsType.loc8_41.1 (constants.%HoldsType.f6a)] { // CHECK:STDOUT: %HoldsType.ref: %HoldsType.type = name_ref HoldsType, file.%HoldsType.decl [concrete = constants.%HoldsType.generic] // CHECK:STDOUT: %T.ref.loc8_40: %tuple.type = name_ref T, %T.loc8_6.2 [symbolic = %T.loc8_6.1 (constants.%T)] // CHECK:STDOUT: %HoldsType.loc8_41.2: type = class_type @HoldsType, @HoldsType(constants.%T) [symbolic = %HoldsType.loc8_41.1 (constants.%HoldsType.f6a)] // CHECK:STDOUT: } // CHECK:STDOUT: %x: @F.%HoldsType.loc8_41.1 (%HoldsType.f6a) = value_binding x, %x.param // CHECK:STDOUT: %a.param: @F.%tuple.elem0.loc8_55.1 (%tuple.elem0) = value_param call_param1 // CHECK:STDOUT: %.loc8_55: type = splice_block %tuple.elem0.loc8_55.2 [symbolic = %tuple.elem0.loc8_55.1 (constants.%tuple.elem0)] { // CHECK:STDOUT: %T.ref.loc8_54: %tuple.type = name_ref T, %T.loc8_6.2 [symbolic = %T.loc8_6.1 (constants.%T)] // CHECK:STDOUT: %int_0: Core.IntLiteral = int_value 0 [concrete = constants.%int_0] // CHECK:STDOUT: %tuple.elem0.loc8_55.2: type = tuple_access %T.ref.loc8_54, element0 [symbolic = %tuple.elem0.loc8_55.1 (constants.%tuple.elem0)] // CHECK:STDOUT: } // CHECK:STDOUT: %a: @F.%tuple.elem0.loc8_55.1 (%tuple.elem0) = value_binding a, %a.param // CHECK:STDOUT: } // CHECK:STDOUT: %C.decl: type = class_decl @C [concrete = constants.%C] {} {} // CHECK:STDOUT: %G.decl: %G.type = fn_decl @G [concrete = constants.%G] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic class @HoldsType(%T.loc4_17.2: %tuple.type) { // CHECK:STDOUT: %T.loc4_17.1: %tuple.type = symbolic_binding T, 0 [symbolic = %T.loc4_17.1 (constants.%T)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: // CHECK:STDOUT: class { // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%HoldsType.f6a // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @C { // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%C // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic fn @F(%T.loc8_6.2: %tuple.type) { // CHECK:STDOUT: %T.loc8_6.1: %tuple.type = symbolic_binding T, 0 [symbolic = %T.loc8_6.1 (constants.%T)] // CHECK:STDOUT: %HoldsType.loc8_41.1: type = class_type @HoldsType, @HoldsType(%T.loc8_6.1) [symbolic = %HoldsType.loc8_41.1 (constants.%HoldsType.f6a)] // CHECK:STDOUT: %pattern_type.loc8_27: type = pattern_type %HoldsType.loc8_41.1 [symbolic = %pattern_type.loc8_27 (constants.%pattern_type.17d)] // CHECK:STDOUT: %tuple.elem0.loc8_55.1: type = tuple_access %T.loc8_6.1, element0 [symbolic = %tuple.elem0.loc8_55.1 (constants.%tuple.elem0)] // CHECK:STDOUT: %pattern_type.loc8_51: type = pattern_type %tuple.elem0.loc8_55.1 [symbolic = %pattern_type.loc8_51 (constants.%pattern_type.e66)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete.loc8_28: = require_complete_type %HoldsType.loc8_41.1 [symbolic = %require_complete.loc8_28 (constants.%require_complete.7bf)] // CHECK:STDOUT: %require_complete.loc8_52: = require_complete_type %tuple.elem0.loc8_55.1 [symbolic = %require_complete.loc8_52 (constants.%require_complete.e7a)] // CHECK:STDOUT: // CHECK:STDOUT: fn(%x.param: @F.%HoldsType.loc8_41.1 (%HoldsType.f6a), %a.param: @F.%tuple.elem0.loc8_55.1 (%tuple.elem0)) { // CHECK:STDOUT: !entry: // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @G() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %F.ref: %F.type = name_ref F, file.%F.decl [concrete = constants.%F] // CHECK:STDOUT: %.loc13_6.1: %empty_struct_type = struct_literal () [concrete = constants.%empty_struct] // CHECK:STDOUT: %HoldsType.ref: %HoldsType.type = name_ref HoldsType, file.%HoldsType.decl [concrete = constants.%HoldsType.generic] // CHECK:STDOUT: %C.ref: type = name_ref C, file.%C.decl [concrete = constants.%C] // CHECK:STDOUT: %.loc13_25: %tuple.type = tuple_literal (%C.ref) [concrete = constants.%tuple.1f1] // CHECK:STDOUT: %tuple: %tuple.type = tuple_value (%C.ref) [concrete = constants.%tuple.1f1] // CHECK:STDOUT: %.loc13_26: %tuple.type = converted %.loc13_25, %tuple [concrete = constants.%tuple.1f1] // CHECK:STDOUT: %HoldsType: type = class_type @HoldsType, @HoldsType(constants.%tuple.1f1) [concrete = constants.%HoldsType.a31] // CHECK:STDOUT: %.loc13_6.2: ref %HoldsType.a31 = temporary_storage // CHECK:STDOUT: %.loc13_6.3: init %HoldsType.a31 to %.loc13_6.2 = class_init () [concrete = constants.%HoldsType.val] // CHECK:STDOUT: %.loc13_8.1: init %HoldsType.a31 = converted %.loc13_6.1, %.loc13_6.3 [concrete = constants.%HoldsType.val] // CHECK:STDOUT: %.loc13_30.1: %empty_struct_type = struct_literal () [concrete = constants.%empty_struct] // CHECK:STDOUT: %F.specific_fn: = specific_function %F.ref, @F(constants.%tuple.1f1) [concrete = constants.%F.specific_fn] // CHECK:STDOUT: %.loc13_8.2: ref %HoldsType.a31 = temporary %.loc13_6.2, %.loc13_8.1 // CHECK:STDOUT: %.loc13_8.3: %HoldsType.a31 = acquire_value %.loc13_8.2 // CHECK:STDOUT: %.loc13_30.2: ref %C = temporary_storage // CHECK:STDOUT: %.loc13_30.3: init %C to %.loc13_30.2 = class_init () [concrete = constants.%C.val] // CHECK:STDOUT: %.loc13_30.4: init %C = converted %.loc13_30.1, %.loc13_30.3 [concrete = constants.%C.val] // CHECK:STDOUT: %.loc13_30.5: ref %C = temporary %.loc13_30.2, %.loc13_30.4 // CHECK:STDOUT: %.loc13_30.6: %C = acquire_value %.loc13_30.5 // CHECK:STDOUT: %F.call: init %empty_tuple.type = call %F.specific_fn(%.loc13_8.3, %.loc13_30.6) // CHECK:STDOUT: %Destroy.Op.bound.loc13_30: = bound_method %.loc13_30.5, constants.%Destroy.Op.651ba6.1 // CHECK:STDOUT: %Destroy.Op.call.loc13_30: init %empty_tuple.type = call %Destroy.Op.bound.loc13_30(%.loc13_30.5) // CHECK:STDOUT: %Destroy.Op.bound.loc13_8: = bound_method %.loc13_8.2, constants.%Destroy.Op.651ba6.2 // CHECK:STDOUT: %Destroy.Op.call.loc13_8: init %empty_tuple.type = call %Destroy.Op.bound.loc13_8(%.loc13_8.2) // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @Destroy.Op.loc13_30(%self.param: ref %C) = "no_op"; // CHECK:STDOUT: // CHECK:STDOUT: fn @Destroy.Op.loc13_8(%self.param: ref %HoldsType.a31) = "no_op"; // CHECK:STDOUT: // CHECK:STDOUT: specific @HoldsType(constants.%T) { // CHECK:STDOUT: %T.loc4_17.1 => constants.%T // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @F(constants.%T) { // CHECK:STDOUT: %T.loc8_6.1 => constants.%T // CHECK:STDOUT: %HoldsType.loc8_41.1 => constants.%HoldsType.f6a // CHECK:STDOUT: %pattern_type.loc8_27 => constants.%pattern_type.17d // CHECK:STDOUT: %tuple.elem0.loc8_55.1 => constants.%tuple.elem0 // CHECK:STDOUT: %pattern_type.loc8_51 => constants.%pattern_type.e66 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @HoldsType(constants.%tuple.1f1) { // CHECK:STDOUT: %T.loc4_17.1 => constants.%tuple.1f1 // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @F(constants.%tuple.1f1) { // CHECK:STDOUT: %T.loc8_6.1 => constants.%tuple.1f1 // CHECK:STDOUT: %HoldsType.loc8_41.1 => constants.%HoldsType.a31 // CHECK:STDOUT: %pattern_type.loc8_27 => constants.%pattern_type.a646 // CHECK:STDOUT: %tuple.elem0.loc8_55.1 => constants.%C // CHECK:STDOUT: %pattern_type.loc8_51 => constants.%pattern_type.7c7 // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete.loc8_28 => constants.%complete_type // CHECK:STDOUT: %require_complete.loc8_52 => constants.%complete_type // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- struct_access.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %type: type = facet_type [concrete] // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self] // CHECK:STDOUT: %struct_type.t: type = struct_type {.t: type} [concrete] // CHECK:STDOUT: %pattern_type.7f2: type = pattern_type %struct_type.t [concrete] // CHECK:STDOUT: %T: %struct_type.t = symbolic_binding T, 0 [symbolic] // CHECK:STDOUT: %HoldsType.type: type = generic_class_type @HoldsType [concrete] // CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] // CHECK:STDOUT: %HoldsType.generic: %HoldsType.type = struct_value () [concrete] // CHECK:STDOUT: %HoldsType.cac: type = class_type @HoldsType, @HoldsType(%T) [symbolic] // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %complete_type: = complete_type_witness %empty_struct_type [concrete] // CHECK:STDOUT: %pattern_type.2de: type = pattern_type %HoldsType.cac [symbolic] // CHECK:STDOUT: %.424: type = struct_access %T, element0 [symbolic] // CHECK:STDOUT: %pattern_type.92c: type = pattern_type %.424 [symbolic] // CHECK:STDOUT: %F.type: type = fn_type @F [concrete] // CHECK:STDOUT: %F: %F.type = struct_value () [concrete] // CHECK:STDOUT: %require_complete.276: = require_complete_type %HoldsType.cac [symbolic] // CHECK:STDOUT: %require_complete.ba0: = require_complete_type %.424 [symbolic] // CHECK:STDOUT: %C: type = class_type @C [concrete] // CHECK:STDOUT: %G.type: type = fn_type @G [concrete] // CHECK:STDOUT: %G: %G.type = struct_value () [concrete] // CHECK:STDOUT: %empty_struct: %empty_struct_type = struct_value () [concrete] // CHECK:STDOUT: %struct: %struct_type.t = struct_value (%C) [concrete] // CHECK:STDOUT: %HoldsType.673: type = class_type @HoldsType, @HoldsType(%struct) [concrete] // CHECK:STDOUT: %HoldsType.val: %HoldsType.673 = struct_value () [concrete] // CHECK:STDOUT: %pattern_type.4e6: type = pattern_type %HoldsType.673 [concrete] // CHECK:STDOUT: %pattern_type.7c7: type = pattern_type %C [concrete] // CHECK:STDOUT: %F.specific_fn: = specific_function %F, @F(%struct) [concrete] // CHECK:STDOUT: %C.val: %C = struct_value () [concrete] // CHECK:STDOUT: %Destroy.type: type = facet_type <@Destroy> [concrete] // CHECK:STDOUT: %Destroy.Op.type.bae255.1: type = fn_type @Destroy.Op.loc13_33 [concrete] // CHECK:STDOUT: %Destroy.Op.651ba6.1: %Destroy.Op.type.bae255.1 = struct_value () [concrete] // CHECK:STDOUT: %Destroy.Op.type.bae255.2: type = fn_type @Destroy.Op.loc13_8 [concrete] // CHECK:STDOUT: %Destroy.Op.651ba6.2: %Destroy.Op.type.bae255.2 = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: .Destroy = %Core.Destroy // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: %Core.Destroy: type = import_ref Core//prelude/parts/destroy, Destroy, loaded [concrete = constants.%Destroy.type] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .HoldsType = %HoldsType.decl // CHECK:STDOUT: .F = %F.decl // CHECK:STDOUT: .C = %C.decl // CHECK:STDOUT: .G = %G.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %HoldsType.decl: %HoldsType.type = class_decl @HoldsType [concrete = constants.%HoldsType.generic] { // CHECK:STDOUT: %T.patt: %pattern_type.7f2 = symbolic_binding_pattern T, 0 [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %.loc4_30: type = splice_block %struct_type.t [concrete = constants.%struct_type.t] { // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %.loc4_26: type = type_literal type [concrete = type] // CHECK:STDOUT: %struct_type.t: type = struct_type {.t: type} [concrete = constants.%struct_type.t] // CHECK:STDOUT: } // CHECK:STDOUT: %T.loc4_17.2: %struct_type.t = symbolic_binding T, 0 [symbolic = %T.loc4_17.1 (constants.%T)] // CHECK:STDOUT: } // CHECK:STDOUT: %F.decl: %F.type = fn_decl @F [concrete = constants.%F] { // CHECK:STDOUT: %T.patt: %pattern_type.7f2 = symbolic_binding_pattern T, 0 [concrete] // CHECK:STDOUT: %x.patt: @F.%pattern_type.loc8_29 (%pattern_type.2de) = value_binding_pattern x [concrete] // CHECK:STDOUT: %x.param_patt: @F.%pattern_type.loc8_29 (%pattern_type.2de) = value_param_pattern %x.patt [concrete] // CHECK:STDOUT: %a.patt: @F.%pattern_type.loc8_53 (%pattern_type.92c) = value_binding_pattern a [concrete] // CHECK:STDOUT: %a.param_patt: @F.%pattern_type.loc8_53 (%pattern_type.92c) = value_param_pattern %a.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %.loc8_19: type = splice_block %struct_type.t [concrete = constants.%struct_type.t] { // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %.loc8_15: type = type_literal type [concrete = type] // CHECK:STDOUT: %struct_type.t: type = struct_type {.t: type} [concrete = constants.%struct_type.t] // CHECK:STDOUT: } // CHECK:STDOUT: %T.loc8_6.2: %struct_type.t = symbolic_binding T, 0 [symbolic = %T.loc8_6.1 (constants.%T)] // CHECK:STDOUT: %x.param: @F.%HoldsType.loc8_43.1 (%HoldsType.cac) = value_param call_param0 // CHECK:STDOUT: %.loc8_43: type = splice_block %HoldsType.loc8_43.2 [symbolic = %HoldsType.loc8_43.1 (constants.%HoldsType.cac)] { // CHECK:STDOUT: %HoldsType.ref: %HoldsType.type = name_ref HoldsType, file.%HoldsType.decl [concrete = constants.%HoldsType.generic] // CHECK:STDOUT: %T.ref.loc8_42: %struct_type.t = name_ref T, %T.loc8_6.2 [symbolic = %T.loc8_6.1 (constants.%T)] // CHECK:STDOUT: %HoldsType.loc8_43.2: type = class_type @HoldsType, @HoldsType(constants.%T) [symbolic = %HoldsType.loc8_43.1 (constants.%HoldsType.cac)] // CHECK:STDOUT: } // CHECK:STDOUT: %x: @F.%HoldsType.loc8_43.1 (%HoldsType.cac) = value_binding x, %x.param // CHECK:STDOUT: %a.param: @F.%.loc8_57.1 (%.424) = value_param call_param1 // CHECK:STDOUT: %.loc8_57.2: type = splice_block %.loc8_57.3 [symbolic = %.loc8_57.1 (constants.%.424)] { // CHECK:STDOUT: %T.ref.loc8_56: %struct_type.t = name_ref T, %T.loc8_6.2 [symbolic = %T.loc8_6.1 (constants.%T)] // CHECK:STDOUT: %.loc8_57.3: type = struct_access %T.ref.loc8_56, element0 [symbolic = %.loc8_57.1 (constants.%.424)] // CHECK:STDOUT: } // CHECK:STDOUT: %a: @F.%.loc8_57.1 (%.424) = value_binding a, %a.param // CHECK:STDOUT: } // CHECK:STDOUT: %C.decl: type = class_decl @C [concrete = constants.%C] {} {} // CHECK:STDOUT: %G.decl: %G.type = fn_decl @G [concrete = constants.%G] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic class @HoldsType(%T.loc4_17.2: %struct_type.t) { // CHECK:STDOUT: %T.loc4_17.1: %struct_type.t = symbolic_binding T, 0 [symbolic = %T.loc4_17.1 (constants.%T)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: // CHECK:STDOUT: class { // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%HoldsType.cac // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @C { // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%C // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic fn @F(%T.loc8_6.2: %struct_type.t) { // CHECK:STDOUT: %T.loc8_6.1: %struct_type.t = symbolic_binding T, 0 [symbolic = %T.loc8_6.1 (constants.%T)] // CHECK:STDOUT: %HoldsType.loc8_43.1: type = class_type @HoldsType, @HoldsType(%T.loc8_6.1) [symbolic = %HoldsType.loc8_43.1 (constants.%HoldsType.cac)] // CHECK:STDOUT: %pattern_type.loc8_29: type = pattern_type %HoldsType.loc8_43.1 [symbolic = %pattern_type.loc8_29 (constants.%pattern_type.2de)] // CHECK:STDOUT: %.loc8_57.1: type = struct_access %T.loc8_6.1, element0 [symbolic = %.loc8_57.1 (constants.%.424)] // CHECK:STDOUT: %pattern_type.loc8_53: type = pattern_type %.loc8_57.1 [symbolic = %pattern_type.loc8_53 (constants.%pattern_type.92c)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete.loc8_30: = require_complete_type %HoldsType.loc8_43.1 [symbolic = %require_complete.loc8_30 (constants.%require_complete.276)] // CHECK:STDOUT: %require_complete.loc8_54: = require_complete_type %.loc8_57.1 [symbolic = %require_complete.loc8_54 (constants.%require_complete.ba0)] // CHECK:STDOUT: // CHECK:STDOUT: fn(%x.param: @F.%HoldsType.loc8_43.1 (%HoldsType.cac), %a.param: @F.%.loc8_57.1 (%.424)) { // CHECK:STDOUT: !entry: // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @G() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %F.ref: %F.type = name_ref F, file.%F.decl [concrete = constants.%F] // CHECK:STDOUT: %.loc13_6.1: %empty_struct_type = struct_literal () [concrete = constants.%empty_struct] // CHECK:STDOUT: %HoldsType.ref: %HoldsType.type = name_ref HoldsType, file.%HoldsType.decl [concrete = constants.%HoldsType.generic] // CHECK:STDOUT: %C.ref: type = name_ref C, file.%C.decl [concrete = constants.%C] // CHECK:STDOUT: %.loc13_28: %struct_type.t = struct_literal (%C.ref) [concrete = constants.%struct] // CHECK:STDOUT: %struct: %struct_type.t = struct_value (%C.ref) [concrete = constants.%struct] // CHECK:STDOUT: %.loc13_29: %struct_type.t = converted %.loc13_28, %struct [concrete = constants.%struct] // CHECK:STDOUT: %HoldsType: type = class_type @HoldsType, @HoldsType(constants.%struct) [concrete = constants.%HoldsType.673] // CHECK:STDOUT: %.loc13_6.2: ref %HoldsType.673 = temporary_storage // CHECK:STDOUT: %.loc13_6.3: init %HoldsType.673 to %.loc13_6.2 = class_init () [concrete = constants.%HoldsType.val] // CHECK:STDOUT: %.loc13_8.1: init %HoldsType.673 = converted %.loc13_6.1, %.loc13_6.3 [concrete = constants.%HoldsType.val] // CHECK:STDOUT: %.loc13_33.1: %empty_struct_type = struct_literal () [concrete = constants.%empty_struct] // CHECK:STDOUT: %F.specific_fn: = specific_function %F.ref, @F(constants.%struct) [concrete = constants.%F.specific_fn] // CHECK:STDOUT: %.loc13_8.2: ref %HoldsType.673 = temporary %.loc13_6.2, %.loc13_8.1 // CHECK:STDOUT: %.loc13_8.3: %HoldsType.673 = acquire_value %.loc13_8.2 // CHECK:STDOUT: %.loc13_33.2: ref %C = temporary_storage // CHECK:STDOUT: %.loc13_33.3: init %C to %.loc13_33.2 = class_init () [concrete = constants.%C.val] // CHECK:STDOUT: %.loc13_33.4: init %C = converted %.loc13_33.1, %.loc13_33.3 [concrete = constants.%C.val] // CHECK:STDOUT: %.loc13_33.5: ref %C = temporary %.loc13_33.2, %.loc13_33.4 // CHECK:STDOUT: %.loc13_33.6: %C = acquire_value %.loc13_33.5 // CHECK:STDOUT: %F.call: init %empty_tuple.type = call %F.specific_fn(%.loc13_8.3, %.loc13_33.6) // CHECK:STDOUT: %Destroy.Op.bound.loc13_33: = bound_method %.loc13_33.5, constants.%Destroy.Op.651ba6.1 // CHECK:STDOUT: %Destroy.Op.call.loc13_33: init %empty_tuple.type = call %Destroy.Op.bound.loc13_33(%.loc13_33.5) // CHECK:STDOUT: %Destroy.Op.bound.loc13_8: = bound_method %.loc13_8.2, constants.%Destroy.Op.651ba6.2 // CHECK:STDOUT: %Destroy.Op.call.loc13_8: init %empty_tuple.type = call %Destroy.Op.bound.loc13_8(%.loc13_8.2) // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @Destroy.Op.loc13_33(%self.param: ref %C) = "no_op"; // CHECK:STDOUT: // CHECK:STDOUT: fn @Destroy.Op.loc13_8(%self.param: ref %HoldsType.673) = "no_op"; // CHECK:STDOUT: // CHECK:STDOUT: specific @HoldsType(constants.%T) { // CHECK:STDOUT: %T.loc4_17.1 => constants.%T // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @F(constants.%T) { // CHECK:STDOUT: %T.loc8_6.1 => constants.%T // CHECK:STDOUT: %HoldsType.loc8_43.1 => constants.%HoldsType.cac // CHECK:STDOUT: %pattern_type.loc8_29 => constants.%pattern_type.2de // CHECK:STDOUT: %.loc8_57.1 => constants.%.424 // CHECK:STDOUT: %pattern_type.loc8_53 => constants.%pattern_type.92c // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @HoldsType(constants.%struct) { // CHECK:STDOUT: %T.loc4_17.1 => constants.%struct // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @F(constants.%struct) { // CHECK:STDOUT: %T.loc8_6.1 => constants.%struct // CHECK:STDOUT: %HoldsType.loc8_43.1 => constants.%HoldsType.673 // CHECK:STDOUT: %pattern_type.loc8_29 => constants.%pattern_type.4e6 // CHECK:STDOUT: %.loc8_57.1 => constants.%C // CHECK:STDOUT: %pattern_type.loc8_53 => constants.%pattern_type.7c7 // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete.loc8_30 => constants.%complete_type // CHECK:STDOUT: %require_complete.loc8_54 => constants.%complete_type // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- fail_todo_class_access.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %Class: type = class_type @Class [concrete] // CHECK:STDOUT: %Class.elem: type = unbound_element_type %Class, type [concrete] // CHECK:STDOUT: %struct_type.t: type = struct_type {.t: type} [concrete] // CHECK:STDOUT: %complete_type.509: = complete_type_witness %struct_type.t [concrete] // CHECK:STDOUT: %type: type = facet_type [concrete] // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self] // CHECK:STDOUT: %pattern_type.904: type = pattern_type %Class [concrete] // CHECK:STDOUT: %T.d7d: %Class = symbolic_binding T, 0 [symbolic] // CHECK:STDOUT: %HoldsType.type: type = generic_class_type @HoldsType [concrete] // CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] // CHECK:STDOUT: %HoldsType.generic: %HoldsType.type = struct_value () [concrete] // CHECK:STDOUT: %HoldsType.47b504.1: type = class_type @HoldsType, @HoldsType(%T.d7d) [symbolic] // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %complete_type.357: = complete_type_witness %empty_struct_type [concrete] // CHECK:STDOUT: %pattern_type.3b8: type = pattern_type %HoldsType.47b504.1 [symbolic] // CHECK:STDOUT: %.208: ref type = class_element_access %T.d7d, element0 [symbolic] // CHECK:STDOUT: %F.type: type = fn_type @F [concrete] // CHECK:STDOUT: %F: %F.type = struct_value () [concrete] // CHECK:STDOUT: %require_complete.9b8c71.1: = require_complete_type %HoldsType.47b504.1 [symbolic] // CHECK:STDOUT: %C: type = class_type @C [concrete] // CHECK:STDOUT: %c: %Class = symbolic_binding c, 0 [symbolic] // CHECK:STDOUT: %G.type: type = fn_type @G [concrete] // CHECK:STDOUT: %G: %G.type = struct_value () [concrete] // CHECK:STDOUT: %empty_struct: %empty_struct_type = struct_value () [concrete] // CHECK:STDOUT: %HoldsType.47b504.2: type = class_type @HoldsType, @HoldsType(%c) [symbolic] // CHECK:STDOUT: %require_complete.9b8c71.2: = require_complete_type %HoldsType.47b504.2 [symbolic] // CHECK:STDOUT: %HoldsType.val: %HoldsType.47b504.2 = struct_value () [symbolic] // CHECK:STDOUT: %H.type: type = fn_type @H [concrete] // CHECK:STDOUT: %H: %H.type = struct_value () [concrete] // CHECK:STDOUT: %struct: %struct_type.t = struct_value (%C) [concrete] // CHECK:STDOUT: %Copy.type: type = facet_type <@Copy> [concrete] // CHECK:STDOUT: %Copy.impl_witness.b47: = impl_witness imports.%Copy.impl_witness_table.b1c [concrete] // CHECK:STDOUT: %Copy.facet: %Copy.type = facet_value type, (%Copy.impl_witness.b47) [concrete] // CHECK:STDOUT: %Copy.WithSelf.Op.type.a4f: type = fn_type @Copy.WithSelf.Op, @Copy.WithSelf(%Copy.facet) [concrete] // CHECK:STDOUT: %.070: type = fn_type_with_self_type %Copy.WithSelf.Op.type.a4f, %Copy.facet [concrete] // CHECK:STDOUT: %type.as.Copy.impl.Op.type: type = fn_type @type.as.Copy.impl.Op [concrete] // CHECK:STDOUT: %type.as.Copy.impl.Op: %type.as.Copy.impl.Op.type = struct_value () [concrete] // CHECK:STDOUT: %type.as.Copy.impl.Op.bound: = bound_method %C, %type.as.Copy.impl.Op [concrete] // CHECK:STDOUT: %Class.val: %Class = struct_value (%C) [concrete] // CHECK:STDOUT: %Destroy.type: type = facet_type <@Destroy> [concrete] // CHECK:STDOUT: %Destroy.Op.type: type = fn_type @Destroy.Op [concrete] // CHECK:STDOUT: %Destroy.Op: %Destroy.Op.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: .Copy = %Core.Copy // CHECK:STDOUT: .Destroy = %Core.Destroy // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: %Core.Copy: type = import_ref Core//prelude/parts/copy, Copy, loaded [concrete = constants.%Copy.type] // CHECK:STDOUT: %Core.import_ref.1a7: %type.as.Copy.impl.Op.type = import_ref Core//prelude/parts/copy, loc{{\d+_\d+}}, loaded [concrete = constants.%type.as.Copy.impl.Op] // CHECK:STDOUT: %Copy.impl_witness_table.b1c = impl_witness_table (%Core.import_ref.1a7), @type.as.Copy.impl [concrete] // CHECK:STDOUT: %Core.Destroy: type = import_ref Core//prelude/parts/destroy, Destroy, loaded [concrete = constants.%Destroy.type] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .Class = %Class.decl // CHECK:STDOUT: .HoldsType = %HoldsType.decl // CHECK:STDOUT: .F = %F.decl // CHECK:STDOUT: .C = %C.decl // CHECK:STDOUT: .G = %G.decl // CHECK:STDOUT: .H = %H.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %Class.decl: type = class_decl @Class [concrete = constants.%Class] {} {} // CHECK:STDOUT: %HoldsType.decl: %HoldsType.type = class_decl @HoldsType [concrete = constants.%HoldsType.generic] { // CHECK:STDOUT: %T.patt: %pattern_type.904 = symbolic_binding_pattern T, 0 [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %.loc8: type = splice_block %Class.ref [concrete = constants.%Class] { // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %Class.ref: type = name_ref Class, file.%Class.decl [concrete = constants.%Class] // CHECK:STDOUT: } // CHECK:STDOUT: %T.loc8_17.2: %Class = symbolic_binding T, 0 [symbolic = %T.loc8_17.1 (constants.%T.d7d)] // CHECK:STDOUT: } // CHECK:STDOUT: %F.decl: %F.type = fn_decl @F [concrete = constants.%F] { // CHECK:STDOUT: %T.patt: %pattern_type.904 = symbolic_binding_pattern T, 0 [concrete] // CHECK:STDOUT: %x.patt: @F.%pattern_type (%pattern_type.3b8) = value_binding_pattern x [concrete] // CHECK:STDOUT: %x.param_patt: @F.%pattern_type (%pattern_type.3b8) = value_param_pattern %x.patt [concrete] // CHECK:STDOUT: %a.patt: = value_binding_pattern a [concrete] // CHECK:STDOUT: %a.param_patt: = value_param_pattern %a.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %.loc21_10: type = splice_block %Class.ref [concrete = constants.%Class] { // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %Class.ref: type = name_ref Class, file.%Class.decl [concrete = constants.%Class] // CHECK:STDOUT: } // CHECK:STDOUT: %T.loc21_6.2: %Class = symbolic_binding T, 0 [symbolic = %T.loc21_6.1 (constants.%T.d7d)] // CHECK:STDOUT: %x.param: @F.%HoldsType.loc21_38.1 (%HoldsType.47b504.1) = value_param call_param0 // CHECK:STDOUT: %.loc21_38: type = splice_block %HoldsType.loc21_38.2 [symbolic = %HoldsType.loc21_38.1 (constants.%HoldsType.47b504.1)] { // CHECK:STDOUT: %HoldsType.ref: %HoldsType.type = name_ref HoldsType, file.%HoldsType.decl [concrete = constants.%HoldsType.generic] // CHECK:STDOUT: %T.ref.loc21_37: %Class = name_ref T, %T.loc21_6.2 [symbolic = %T.loc21_6.1 (constants.%T.d7d)] // CHECK:STDOUT: %HoldsType.loc21_38.2: type = class_type @HoldsType, @HoldsType(constants.%T.d7d) [symbolic = %HoldsType.loc21_38.1 (constants.%HoldsType.47b504.1)] // CHECK:STDOUT: } // CHECK:STDOUT: %x: @F.%HoldsType.loc21_38.1 (%HoldsType.47b504.1) = value_binding x, %x.param // CHECK:STDOUT: %a.param: = value_param call_param1 // CHECK:STDOUT: %.1: = splice_block [concrete = ] { // CHECK:STDOUT: %T.ref.loc21_51: %Class = name_ref T, %T.loc21_6.2 [symbolic = %T.loc21_6.1 (constants.%T.d7d)] // CHECK:STDOUT: %t.ref: %Class.elem = name_ref t, @Class.%.loc5_8 [concrete = @Class.%.loc5_8] // CHECK:STDOUT: %.loc21_52.2: ref type = class_element_access %T.ref.loc21_51, element0 [symbolic = %.loc21_52.1 (constants.%.208)] // CHECK:STDOUT: %.loc21_52.3: type = acquire_value %.loc21_52.2 // CHECK:STDOUT: } // CHECK:STDOUT: %a: = value_binding a, %a.param // CHECK:STDOUT: } // CHECK:STDOUT: %C.decl: type = class_decl @C [concrete = constants.%C] {} {} // CHECK:STDOUT: %G.decl: %G.type = fn_decl @G [concrete = constants.%G] { // CHECK:STDOUT: %c.patt: %pattern_type.904 = symbolic_binding_pattern c, 0 [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %.loc25: type = splice_block %Class.ref [concrete = constants.%Class] { // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %Class.ref: type = name_ref Class, file.%Class.decl [concrete = constants.%Class] // CHECK:STDOUT: } // CHECK:STDOUT: %c.loc25_6.2: %Class = symbolic_binding c, 0 [symbolic = %c.loc25_6.1 (constants.%c)] // CHECK:STDOUT: } // CHECK:STDOUT: %H.decl: %H.type = fn_decl @H [concrete = constants.%H] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @Class { // CHECK:STDOUT: %.loc5_10: type = type_literal type [concrete = type] // CHECK:STDOUT: %.loc5_8: %Class.elem = field_decl t, element0 [concrete] // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%struct_type.t [concrete = constants.%complete_type.509] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%Class // CHECK:STDOUT: .t = %.loc5_8 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic class @HoldsType(%T.loc8_17.2: %Class) { // CHECK:STDOUT: %T.loc8_17.1: %Class = symbolic_binding T, 0 [symbolic = %T.loc8_17.1 (constants.%T.d7d)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: // CHECK:STDOUT: class { // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type.357] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%HoldsType.47b504.1 // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @C { // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type.357] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%C // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic fn @F(%T.loc21_6.2: %Class) { // CHECK:STDOUT: %T.loc21_6.1: %Class = symbolic_binding T, 0 [symbolic = %T.loc21_6.1 (constants.%T.d7d)] // CHECK:STDOUT: %HoldsType.loc21_38.1: type = class_type @HoldsType, @HoldsType(%T.loc21_6.1) [symbolic = %HoldsType.loc21_38.1 (constants.%HoldsType.47b504.1)] // CHECK:STDOUT: %pattern_type: type = pattern_type %HoldsType.loc21_38.1 [symbolic = %pattern_type (constants.%pattern_type.3b8)] // CHECK:STDOUT: %.loc21_52.1: ref type = class_element_access %T.loc21_6.1, element0 [symbolic = %.loc21_52.1 (constants.%.208)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete: = require_complete_type %HoldsType.loc21_38.1 [symbolic = %require_complete (constants.%require_complete.9b8c71.1)] // CHECK:STDOUT: // CHECK:STDOUT: fn(%x.param: @F.%HoldsType.loc21_38.1 (%HoldsType.47b504.1), %a.param: ) { // CHECK:STDOUT: !entry: // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic fn @G(%c.loc25_6.2: %Class) { // CHECK:STDOUT: %c.loc25_6.1: %Class = symbolic_binding c, 0 [symbolic = %c.loc25_6.1 (constants.%c)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %HoldsType.loc26_22.2: type = class_type @HoldsType, @HoldsType(%c.loc25_6.1) [symbolic = %HoldsType.loc26_22.2 (constants.%HoldsType.47b504.2)] // CHECK:STDOUT: %require_complete: = require_complete_type %HoldsType.loc26_22.2 [symbolic = %require_complete (constants.%require_complete.9b8c71.2)] // CHECK:STDOUT: %HoldsType.val: @G.%HoldsType.loc26_22.2 (%HoldsType.47b504.2) = struct_value () [symbolic = %HoldsType.val (constants.%HoldsType.val)] // CHECK:STDOUT: // CHECK:STDOUT: fn() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %F.ref: %F.type = name_ref F, file.%F.decl [concrete = constants.%F] // CHECK:STDOUT: %.loc26_6.1: %empty_struct_type = struct_literal () [concrete = constants.%empty_struct] // CHECK:STDOUT: %HoldsType.ref: %HoldsType.type = name_ref HoldsType, file.%HoldsType.decl [concrete = constants.%HoldsType.generic] // CHECK:STDOUT: %c.ref: %Class = name_ref c, %c.loc25_6.2 [symbolic = %c.loc25_6.1 (constants.%c)] // CHECK:STDOUT: %HoldsType.loc26_22.1: type = class_type @HoldsType, @HoldsType(constants.%c) [symbolic = %HoldsType.loc26_22.2 (constants.%HoldsType.47b504.2)] // CHECK:STDOUT: %.loc26_6.2: ref @G.%HoldsType.loc26_22.2 (%HoldsType.47b504.2) = temporary_storage // CHECK:STDOUT: %.loc26_6.3: init @G.%HoldsType.loc26_22.2 (%HoldsType.47b504.2) to %.loc26_6.2 = class_init () [symbolic = %HoldsType.val (constants.%HoldsType.val)] // CHECK:STDOUT: %.loc26_8: init @G.%HoldsType.loc26_22.2 (%HoldsType.47b504.2) = converted %.loc26_6.1, %.loc26_6.3 [symbolic = %HoldsType.val (constants.%HoldsType.val)] // CHECK:STDOUT: %.loc26_26: %empty_struct_type = struct_literal () [concrete = constants.%empty_struct] // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @H() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %G.ref: %G.type = name_ref G, file.%G.decl [concrete = constants.%G] // CHECK:STDOUT: %C.ref: type = name_ref C, file.%C.decl [concrete = constants.%C] // CHECK:STDOUT: %.loc37_12.1: %struct_type.t = struct_literal (%C.ref) [concrete = constants.%struct] // CHECK:STDOUT: %impl.elem0: %.070 = impl_witness_access constants.%Copy.impl_witness.b47, element0 [concrete = constants.%type.as.Copy.impl.Op] // CHECK:STDOUT: %bound_method: = bound_method %C.ref, %impl.elem0 [concrete = constants.%type.as.Copy.impl.Op.bound] // CHECK:STDOUT: %type.as.Copy.impl.Op.call: init type = call %bound_method(%C.ref) [concrete = constants.%C] // CHECK:STDOUT: %.loc37_12.2: ref %Class = temporary_storage // CHECK:STDOUT: %.loc37_12.3: ref type = class_element_access %.loc37_12.2, element0 // CHECK:STDOUT: %.loc37_12.4: init type to %.loc37_12.3 = in_place_init %type.as.Copy.impl.Op.call [concrete = constants.%C] // CHECK:STDOUT: %.loc37_12.5: init %Class to %.loc37_12.2 = class_init (%.loc37_12.4) [concrete = constants.%Class.val] // CHECK:STDOUT: %.loc37_13.1: init %Class = converted %.loc37_12.1, %.loc37_12.5 [concrete = constants.%Class.val] // CHECK:STDOUT: %.loc37_13.2: ref %Class = temporary %.loc37_12.2, %.loc37_13.1 // CHECK:STDOUT: %.loc37_13.3: %Class = acquire_value %.loc37_13.2 // CHECK:STDOUT: %Destroy.Op.bound: = bound_method %.loc37_13.2, constants.%Destroy.Op // CHECK:STDOUT: %Destroy.Op.call: init %empty_tuple.type = call %Destroy.Op.bound(%.loc37_13.2) // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @Destroy.Op(%self.param: ref %Class) = "no_op"; // CHECK:STDOUT: // CHECK:STDOUT: specific @HoldsType(constants.%T.d7d) { // CHECK:STDOUT: %T.loc8_17.1 => constants.%T.d7d // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @F(constants.%T.d7d) { // CHECK:STDOUT: %T.loc21_6.1 => constants.%T.d7d // CHECK:STDOUT: %HoldsType.loc21_38.1 => constants.%HoldsType.47b504.1 // CHECK:STDOUT: %pattern_type => constants.%pattern_type.3b8 // CHECK:STDOUT: %.loc21_52.1 => constants.%.208 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @G(constants.%c) { // CHECK:STDOUT: %c.loc25_6.1 => constants.%c // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @HoldsType(constants.%c) { // CHECK:STDOUT: %T.loc8_17.1 => constants.%c // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- fail_todo_array_index.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %type: type = facet_type [concrete] // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self] // CHECK:STDOUT: %int_1: Core.IntLiteral = int_value 1 [concrete] // CHECK:STDOUT: %array_type: type = array_type %int_1, type [concrete] // CHECK:STDOUT: %pattern_type.dcb: type = pattern_type %array_type [concrete] // CHECK:STDOUT: %T.9b7: %array_type = symbolic_binding T, 0 [symbolic] // CHECK:STDOUT: %HoldsType.type: type = generic_class_type @HoldsType [concrete] // CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] // CHECK:STDOUT: %HoldsType.generic: %HoldsType.type = struct_value () [concrete] // CHECK:STDOUT: %HoldsType: type = class_type @HoldsType, @HoldsType(%T.9b7) [symbolic] // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %complete_type.357: = complete_type_witness %empty_struct_type [concrete] // CHECK:STDOUT: %pattern_type.342: type = pattern_type %HoldsType [symbolic] // CHECK:STDOUT: %int_0.5c6: Core.IntLiteral = int_value 0 [concrete] // CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [concrete] // CHECK:STDOUT: %Int.type: type = generic_class_type @Int [concrete] // CHECK:STDOUT: %Int.generic: %Int.type = struct_value () [concrete] // CHECK:STDOUT: %i32: type = class_type @Int, @Int(%int_32) [concrete] // CHECK:STDOUT: %ImplicitAs.type.cc7: type = generic_interface_type @ImplicitAs [concrete] // CHECK:STDOUT: %ImplicitAs.generic: %ImplicitAs.type.cc7 = struct_value () [concrete] // CHECK:STDOUT: %ImplicitAs.type.e8c: type = facet_type <@ImplicitAs, @ImplicitAs(%i32)> [concrete] // CHECK:STDOUT: %To: Core.IntLiteral = symbolic_binding To, 0 [symbolic] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.4e6: type = fn_type @Core.IntLiteral.as.ImplicitAs.impl.Convert, @Core.IntLiteral.as.ImplicitAs.impl(%To) [symbolic] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.3c2: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.4e6 = struct_value () [symbolic] // CHECK:STDOUT: %ImplicitAs.impl_witness.6bc: = impl_witness imports.%ImplicitAs.impl_witness_table.74f, @Core.IntLiteral.as.ImplicitAs.impl(%int_32) [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.e0d: type = fn_type @Core.IntLiteral.as.ImplicitAs.impl.Convert, @Core.IntLiteral.as.ImplicitAs.impl(%int_32) [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.e0d = struct_value () [concrete] // CHECK:STDOUT: %ImplicitAs.facet: %ImplicitAs.type.e8c = facet_value Core.IntLiteral, (%ImplicitAs.impl_witness.6bc) [concrete] // CHECK:STDOUT: %ImplicitAs.WithSelf.Convert.type.b37: type = fn_type @ImplicitAs.WithSelf.Convert, @ImplicitAs.WithSelf(%i32, %ImplicitAs.facet) [concrete] // CHECK:STDOUT: %.545: type = fn_type_with_self_type %ImplicitAs.WithSelf.Convert.type.b37, %ImplicitAs.facet [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.bound: = bound_method %int_0.5c6, %Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5 [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn: = specific_function %Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5, @Core.IntLiteral.as.ImplicitAs.impl.Convert(%int_32) [concrete] // CHECK:STDOUT: %bound_method: = bound_method %int_0.5c6, %Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn [concrete] // CHECK:STDOUT: %int_0.6a9: %i32 = int_value 0 [concrete] // CHECK:STDOUT: %F.type: type = fn_type @F [concrete] // CHECK:STDOUT: %F: %F.type = struct_value () [concrete] // CHECK:STDOUT: %require_complete.bd5: = require_complete_type %HoldsType [symbolic] // CHECK:STDOUT: %C: type = class_type @C [concrete] // CHECK:STDOUT: %G.type: type = fn_type @G [concrete] // CHECK:STDOUT: %G: %G.type = struct_value () [concrete] // CHECK:STDOUT: %empty_struct: %empty_struct_type = struct_value () [concrete] // CHECK:STDOUT: %tuple.type.85c: type = tuple_type (type) [concrete] // CHECK:STDOUT: %tuple.1f1: %tuple.type.85c = tuple_value (%C) [concrete] // CHECK:STDOUT: %Copy.type: type = facet_type <@Copy> [concrete] // CHECK:STDOUT: %Copy.impl_witness.b47: = impl_witness imports.%Copy.impl_witness_table.b1c [concrete] // CHECK:STDOUT: %Copy.facet: %Copy.type = facet_value type, (%Copy.impl_witness.b47) [concrete] // CHECK:STDOUT: %Copy.WithSelf.Op.type.a4f: type = fn_type @Copy.WithSelf.Op, @Copy.WithSelf(%Copy.facet) [concrete] // CHECK:STDOUT: %.070: type = fn_type_with_self_type %Copy.WithSelf.Op.type.a4f, %Copy.facet [concrete] // CHECK:STDOUT: %type.as.Copy.impl.Op.type: type = fn_type @type.as.Copy.impl.Op [concrete] // CHECK:STDOUT: %type.as.Copy.impl.Op: %type.as.Copy.impl.Op.type = struct_value () [concrete] // CHECK:STDOUT: %type.as.Copy.impl.Op.bound: = bound_method %C, %type.as.Copy.impl.Op [concrete] // CHECK:STDOUT: %array: %array_type = tuple_value (%C) [concrete] // CHECK:STDOUT: %Destroy.type: type = facet_type <@Destroy> [concrete] // CHECK:STDOUT: %Destroy.Op.type: type = fn_type @Destroy.Op [concrete] // CHECK:STDOUT: %Destroy.Op: %Destroy.Op.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: .Int = %Core.Int // CHECK:STDOUT: .ImplicitAs = %Core.ImplicitAs // CHECK:STDOUT: .Copy = %Core.Copy // CHECK:STDOUT: .Destroy = %Core.Destroy // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: %Core.Int: %Int.type = import_ref Core//prelude/parts/int, Int, loaded [concrete = constants.%Int.generic] // CHECK:STDOUT: %Core.ImplicitAs: %ImplicitAs.type.cc7 = import_ref Core//prelude/parts/as, ImplicitAs, loaded [concrete = constants.%ImplicitAs.generic] // CHECK:STDOUT: %Core.import_ref.42d: @Core.IntLiteral.as.ImplicitAs.impl.%Core.IntLiteral.as.ImplicitAs.impl.Convert.type (%Core.IntLiteral.as.ImplicitAs.impl.Convert.type.4e6) = import_ref Core//prelude/parts/int, loc{{\d+_\d+}}, loaded [symbolic = @Core.IntLiteral.as.ImplicitAs.impl.%Core.IntLiteral.as.ImplicitAs.impl.Convert (constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.3c2)] // CHECK:STDOUT: %ImplicitAs.impl_witness_table.74f = impl_witness_table (%Core.import_ref.42d), @Core.IntLiteral.as.ImplicitAs.impl [concrete] // CHECK:STDOUT: %Core.Copy: type = import_ref Core//prelude/parts/copy, Copy, loaded [concrete = constants.%Copy.type] // CHECK:STDOUT: %Core.import_ref.1a7: %type.as.Copy.impl.Op.type = import_ref Core//prelude/parts/copy, loc{{\d+_\d+}}, loaded [concrete = constants.%type.as.Copy.impl.Op] // CHECK:STDOUT: %Copy.impl_witness_table.b1c = impl_witness_table (%Core.import_ref.1a7), @type.as.Copy.impl [concrete] // CHECK:STDOUT: %Core.Destroy: type = import_ref Core//prelude/parts/destroy, Destroy, loaded [concrete = constants.%Destroy.type] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .HoldsType = %HoldsType.decl // CHECK:STDOUT: .F = %F.decl // CHECK:STDOUT: .C = %C.decl // CHECK:STDOUT: .G = %G.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %HoldsType.decl: %HoldsType.type = class_decl @HoldsType [concrete = constants.%HoldsType.generic] { // CHECK:STDOUT: %T.patt: %pattern_type.dcb = symbolic_binding_pattern T, 0 [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %.loc4_34: type = splice_block %array_type [concrete = constants.%array_type] { // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %.loc4_27: type = type_literal type [concrete = type] // CHECK:STDOUT: %int_1: Core.IntLiteral = int_value 1 [concrete = constants.%int_1] // CHECK:STDOUT: %array_type: type = array_type %int_1, %.loc4_27 [concrete = constants.%array_type] // CHECK:STDOUT: } // CHECK:STDOUT: %T.loc4_17.2: %array_type = symbolic_binding T, 0 [symbolic = %T.loc4_17.1 (constants.%T.9b7)] // CHECK:STDOUT: } // CHECK:STDOUT: %F.decl: %F.type = fn_decl @F [concrete = constants.%F] { // CHECK:STDOUT: %T.patt: %pattern_type.dcb = symbolic_binding_pattern T, 0 [concrete] // CHECK:STDOUT: %x.patt: @F.%pattern_type (%pattern_type.342) = value_binding_pattern x [concrete] // CHECK:STDOUT: %x.param_patt: @F.%pattern_type (%pattern_type.342) = value_param_pattern %x.patt [concrete] // CHECK:STDOUT: %a.patt: = value_binding_pattern a [concrete] // CHECK:STDOUT: %a.param_patt: = value_param_pattern %a.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %.loc12_23: type = splice_block %array_type [concrete = constants.%array_type] { // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %.loc12_16: type = type_literal type [concrete = type] // CHECK:STDOUT: %int_1: Core.IntLiteral = int_value 1 [concrete = constants.%int_1] // CHECK:STDOUT: %array_type: type = array_type %int_1, %.loc12_16 [concrete = constants.%array_type] // CHECK:STDOUT: } // CHECK:STDOUT: %T.loc12_6.2: %array_type = symbolic_binding T, 0 [symbolic = %T.loc12_6.1 (constants.%T.9b7)] // CHECK:STDOUT: %x.param: @F.%HoldsType.loc12_47.1 (%HoldsType) = value_param call_param0 // CHECK:STDOUT: %.loc12_47: type = splice_block %HoldsType.loc12_47.2 [symbolic = %HoldsType.loc12_47.1 (constants.%HoldsType)] { // CHECK:STDOUT: %HoldsType.ref: %HoldsType.type = name_ref HoldsType, file.%HoldsType.decl [concrete = constants.%HoldsType.generic] // CHECK:STDOUT: %T.ref.loc12_46: %array_type = name_ref T, %T.loc12_6.2 [symbolic = %T.loc12_6.1 (constants.%T.9b7)] // CHECK:STDOUT: %HoldsType.loc12_47.2: type = class_type @HoldsType, @HoldsType(constants.%T.9b7) [symbolic = %HoldsType.loc12_47.1 (constants.%HoldsType)] // CHECK:STDOUT: } // CHECK:STDOUT: %x: @F.%HoldsType.loc12_47.1 (%HoldsType) = value_binding x, %x.param // CHECK:STDOUT: %a.param: = value_param call_param1 // CHECK:STDOUT: %.1: = splice_block [concrete = ] { // CHECK:STDOUT: %T.ref.loc12_60: %array_type = name_ref T, %T.loc12_6.2 [symbolic = %T.loc12_6.1 (constants.%T.9b7)] // CHECK:STDOUT: %int_0: Core.IntLiteral = int_value 0 [concrete = constants.%int_0.5c6] // CHECK:STDOUT: %impl.elem0: %.545 = impl_witness_access constants.%ImplicitAs.impl_witness.6bc, element0 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5] // CHECK:STDOUT: %bound_method.loc12_62.1: = bound_method %int_0, %impl.elem0 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.bound] // CHECK:STDOUT: %specific_fn: = specific_function %impl.elem0, @Core.IntLiteral.as.ImplicitAs.impl.Convert(constants.%int_32) [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn] // CHECK:STDOUT: %bound_method.loc12_62.2: = bound_method %int_0, %specific_fn [concrete = constants.%bound_method] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.call: init %i32 = call %bound_method.loc12_62.2(%int_0) [concrete = constants.%int_0.6a9] // CHECK:STDOUT: %.loc12_62.1: %i32 = value_of_initializer %Core.IntLiteral.as.ImplicitAs.impl.Convert.call [concrete = constants.%int_0.6a9] // CHECK:STDOUT: %.loc12_62.2: %i32 = converted %int_0, %.loc12_62.1 [concrete = constants.%int_0.6a9] // CHECK:STDOUT: %.loc12_63.1: ref %array_type = value_as_ref %T.ref.loc12_60 // CHECK:STDOUT: %.loc12_63.2: ref type = array_index %.loc12_63.1, %.loc12_62.2 // CHECK:STDOUT: %.loc12_63.3: type = acquire_value %.loc12_63.2 // CHECK:STDOUT: } // CHECK:STDOUT: %a: = value_binding a, %a.param // CHECK:STDOUT: } // CHECK:STDOUT: %C.decl: type = class_decl @C [concrete = constants.%C] {} {} // CHECK:STDOUT: %G.decl: %G.type = fn_decl @G [concrete = constants.%G] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic class @HoldsType(%T.loc4_17.2: %array_type) { // CHECK:STDOUT: %T.loc4_17.1: %array_type = symbolic_binding T, 0 [symbolic = %T.loc4_17.1 (constants.%T.9b7)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: // CHECK:STDOUT: class { // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type.357] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%HoldsType // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @C { // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type.357] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%C // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic fn @F(%T.loc12_6.2: %array_type) { // CHECK:STDOUT: %T.loc12_6.1: %array_type = symbolic_binding T, 0 [symbolic = %T.loc12_6.1 (constants.%T.9b7)] // CHECK:STDOUT: %HoldsType.loc12_47.1: type = class_type @HoldsType, @HoldsType(%T.loc12_6.1) [symbolic = %HoldsType.loc12_47.1 (constants.%HoldsType)] // CHECK:STDOUT: %pattern_type: type = pattern_type %HoldsType.loc12_47.1 [symbolic = %pattern_type (constants.%pattern_type.342)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete: = require_complete_type %HoldsType.loc12_47.1 [symbolic = %require_complete (constants.%require_complete.bd5)] // CHECK:STDOUT: // CHECK:STDOUT: fn(%x.param: @F.%HoldsType.loc12_47.1 (%HoldsType), %a.param: ) { // CHECK:STDOUT: !entry: // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @G() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %F.ref: %F.type = name_ref F, file.%F.decl [concrete = constants.%F] // CHECK:STDOUT: %.loc24_6: %empty_struct_type = struct_literal () [concrete = constants.%empty_struct] // CHECK:STDOUT: %HoldsType.ref: %HoldsType.type = name_ref HoldsType, file.%HoldsType.decl [concrete = constants.%HoldsType.generic] // CHECK:STDOUT: %C.ref: type = name_ref C, file.%C.decl [concrete = constants.%C] // CHECK:STDOUT: %.loc24_25.1: %tuple.type.85c = tuple_literal (%C.ref) [concrete = constants.%tuple.1f1] // CHECK:STDOUT: %.loc24_36: type = type_literal type [concrete = type] // CHECK:STDOUT: %int_1: Core.IntLiteral = int_value 1 [concrete = constants.%int_1] // CHECK:STDOUT: %array_type: type = array_type %int_1, %.loc24_36 [concrete = constants.%array_type] // CHECK:STDOUT: %impl.elem0: %.070 = impl_witness_access constants.%Copy.impl_witness.b47, element0 [concrete = constants.%type.as.Copy.impl.Op] // CHECK:STDOUT: %bound_method: = bound_method %C.ref, %impl.elem0 [concrete = constants.%type.as.Copy.impl.Op.bound] // CHECK:STDOUT: %type.as.Copy.impl.Op.call: init type = call %bound_method(%C.ref) [concrete = constants.%C] // CHECK:STDOUT: %.loc24_25.2: ref %array_type = temporary_storage // CHECK:STDOUT: %int_0: Core.IntLiteral = int_value 0 [concrete = constants.%int_0.5c6] // CHECK:STDOUT: %.loc24_25.3: ref type = array_index %.loc24_25.2, %int_0 // CHECK:STDOUT: %.loc24_25.4: init type to %.loc24_25.3 = in_place_init %type.as.Copy.impl.Op.call [concrete = constants.%C] // CHECK:STDOUT: %.loc24_25.5: init %array_type to %.loc24_25.2 = array_init (%.loc24_25.4) [concrete = constants.%array] // CHECK:STDOUT: %.loc24_27.1: init %array_type = converted %.loc24_25.1, %.loc24_25.5 [concrete = constants.%array] // CHECK:STDOUT: %.loc24_27.2: ref %array_type = temporary %.loc24_25.2, %.loc24_27.1 // CHECK:STDOUT: %.loc24_27.3: %array_type = acquire_value %.loc24_27.2 // CHECK:STDOUT: %.loc24_48: %empty_struct_type = struct_literal () [concrete = constants.%empty_struct] // CHECK:STDOUT: %Destroy.Op.bound: = bound_method %.loc24_27.2, constants.%Destroy.Op // CHECK:STDOUT: %Destroy.Op.call: init %empty_tuple.type = call %Destroy.Op.bound(%.loc24_27.2) // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @Destroy.Op(%self.param: ref %array_type) = "no_op"; // CHECK:STDOUT: // CHECK:STDOUT: specific @HoldsType(constants.%T.9b7) { // CHECK:STDOUT: %T.loc4_17.1 => constants.%T.9b7 // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @F(constants.%T.9b7) { // CHECK:STDOUT: %T.loc12_6.1 => constants.%T.9b7 // CHECK:STDOUT: %HoldsType.loc12_47.1 => constants.%HoldsType // CHECK:STDOUT: %pattern_type => constants.%pattern_type.342 // CHECK:STDOUT: } // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/deduce/where.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/none.carbon // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/deduce/where.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/deduce/where.carbon // --- fail_todo_where.carbon library "[[@TEST_NAME]]"; interface Z { let Z1:! type; } class C(unused T:! type) {} fn F[U:! type](unused T:! Z where .Z1 = C(U)) {} class D {} final impl () as Z where .Z1 = C(D) {} fn G() { // TODO: Should be able to see that ().(Z.Z1) is C(D), so deduce that C(U) // is C(D) and U is D. // // CHECK:STDERR: fail_todo_where.carbon:[[@LINE+7]]:3: error: cannot deduce value for generic parameter `U` [DeductionIncomplete] // CHECK:STDERR: F(() as type); // CHECK:STDERR: ^~~~~~~~~~~~~ // CHECK:STDERR: fail_todo_where.carbon:[[@LINE-12]]:1: note: while deducing parameters of generic declared here [DeductionGenericHere] // CHECK:STDERR: fn F[U:! type](unused T:! Z where .Z1 = C(U)) {} // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: F(() as type); } ================================================ FILE: toolchain/check/testdata/eval/aggregates.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/int.carbon // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/eval/aggregates.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/eval/aggregates.carbon // --- basics.carbon library "[[@TEST_NAME]]"; //@dump-sem-ir-begin var tuple_copy: (i32, i32) = (1, 2) as (i32, i32); var struct_copy: {.a: i32, .b: i32, .c: i32} = {.c = 3, .b = 2, .a = 1} as {.b: i32, .a: i32, .c: i32}; var tuple_index: array(i32, 1) = (0,) as array(i32, (5, 7, 1, 9).2); var struct_access: array(i32, 1) = (0,) as array(i32, {.a = 3, .b = 1}.b); //@dump-sem-ir-end // --- fail_todo_array_temporary.carbon library "[[@TEST_NAME]]"; // TODO: This creates an array temporary, which we don't yet support evaluating. //@dump-sem-ir-begin // CHECK:STDERR: fail_todo_array_temporary.carbon:[[@LINE+4]]:53: error: array bound is not a constant [InvalidArrayExpr] // CHECK:STDERR: var array_index: array(i32, 1) = (0,) as array(i32, ((5, 7, 1, 9) as array(i32, 4))[2]); // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: var array_index: array(i32, 1) = (0,) as array(i32, ((5, 7, 1, 9) as array(i32, 4))[2]); //@dump-sem-ir-end // --- symbolic.carbon library "[[@TEST_NAME]]"; // Check that we propagate the `symbolic` tag through evaluations. fn F(T:! type) { //@dump-sem-ir-begin var unused u: (T*, const T); var unused v: {.a: T}; var unused w: array(T, 5); //@dump-sem-ir-end } fn G(N:! i32) { //@dump-sem-ir-begin var unused k: array(i32, N); //@dump-sem-ir-end } // CHECK:STDOUT: --- basics.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [concrete] // CHECK:STDOUT: %N: Core.IntLiteral = symbolic_binding N, 0 [symbolic] // CHECK:STDOUT: %i32: type = class_type @Int, @Int(%int_32) [concrete] // CHECK:STDOUT: %tuple.type.24b: type = tuple_type (type, type) [concrete] // CHECK:STDOUT: %tuple.95a: %tuple.type.24b = tuple_value (%i32, %i32) [concrete] // CHECK:STDOUT: %tuple.type.d07: type = tuple_type (%i32, %i32) [concrete] // CHECK:STDOUT: %pattern_type.511: type = pattern_type %tuple.type.d07 [concrete] // CHECK:STDOUT: %int_1.5b8: Core.IntLiteral = int_value 1 [concrete] // CHECK:STDOUT: %int_2.ecc: Core.IntLiteral = int_value 2 [concrete] // CHECK:STDOUT: %tuple.type.f94: type = tuple_type (Core.IntLiteral, Core.IntLiteral) [concrete] // CHECK:STDOUT: %tuple.ad8: %tuple.type.f94 = tuple_value (%int_1.5b8, %int_2.ecc) [concrete] // CHECK:STDOUT: %ImplicitAs.type.e8c: type = facet_type <@ImplicitAs, @ImplicitAs(%i32)> [concrete] // CHECK:STDOUT: %To: Core.IntLiteral = symbolic_binding To, 0 [symbolic] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.4e6: type = fn_type @Core.IntLiteral.as.ImplicitAs.impl.Convert, @Core.IntLiteral.as.ImplicitAs.impl(%To) [symbolic] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.3c2: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.4e6 = struct_value () [symbolic] // CHECK:STDOUT: %ImplicitAs.impl_witness.6bc: = impl_witness imports.%ImplicitAs.impl_witness_table.74f, @Core.IntLiteral.as.ImplicitAs.impl(%int_32) [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.e0d: type = fn_type @Core.IntLiteral.as.ImplicitAs.impl.Convert, @Core.IntLiteral.as.ImplicitAs.impl(%int_32) [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.e0d = struct_value () [concrete] // CHECK:STDOUT: %ImplicitAs.facet: %ImplicitAs.type.e8c = facet_value Core.IntLiteral, (%ImplicitAs.impl_witness.6bc) [concrete] // CHECK:STDOUT: %ImplicitAs.WithSelf.Convert.type.b37: type = fn_type @ImplicitAs.WithSelf.Convert, @ImplicitAs.WithSelf(%i32, %ImplicitAs.facet) [concrete] // CHECK:STDOUT: %.545: type = fn_type_with_self_type %ImplicitAs.WithSelf.Convert.type.b37, %ImplicitAs.facet [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.bound.215: = bound_method %int_1.5b8, %Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5 [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn: = specific_function %Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5, @Core.IntLiteral.as.ImplicitAs.impl.Convert(%int_32) [concrete] // CHECK:STDOUT: %bound_method.38b: = bound_method %int_1.5b8, %Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn [concrete] // CHECK:STDOUT: %int_1.5d2: %i32 = int_value 1 [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.bound.4e5: = bound_method %int_2.ecc, %Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5 [concrete] // CHECK:STDOUT: %bound_method.646: = bound_method %int_2.ecc, %Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn [concrete] // CHECK:STDOUT: %int_2.ef8: %i32 = int_value 2 [concrete] // CHECK:STDOUT: %tuple.21c: %tuple.type.d07 = tuple_value (%int_1.5d2, %int_2.ef8) [concrete] // CHECK:STDOUT: %tuple.elem0: ref %i32 = tuple_access file.%tuple_copy.var, element0 [concrete] // CHECK:STDOUT: %Copy.type: type = facet_type <@Copy> [concrete] // CHECK:STDOUT: %Int.as.Copy.impl.Op.type.824: type = fn_type @Int.as.Copy.impl.Op, @Int.as.Copy.impl(%N) [symbolic] // CHECK:STDOUT: %Int.as.Copy.impl.Op.9b9: %Int.as.Copy.impl.Op.type.824 = struct_value () [symbolic] // CHECK:STDOUT: %Copy.impl_witness.f17: = impl_witness imports.%Copy.impl_witness_table.e76, @Int.as.Copy.impl(%int_32) [concrete] // CHECK:STDOUT: %Int.as.Copy.impl.Op.type.546: type = fn_type @Int.as.Copy.impl.Op, @Int.as.Copy.impl(%int_32) [concrete] // CHECK:STDOUT: %Int.as.Copy.impl.Op.664: %Int.as.Copy.impl.Op.type.546 = struct_value () [concrete] // CHECK:STDOUT: %Copy.facet: %Copy.type = facet_value %i32, (%Copy.impl_witness.f17) [concrete] // CHECK:STDOUT: %Copy.WithSelf.Op.type.081: type = fn_type @Copy.WithSelf.Op, @Copy.WithSelf(%Copy.facet) [concrete] // CHECK:STDOUT: %.8e2: type = fn_type_with_self_type %Copy.WithSelf.Op.type.081, %Copy.facet [concrete] // CHECK:STDOUT: %Int.as.Copy.impl.Op.bound.ee3: = bound_method %int_1.5d2, %Int.as.Copy.impl.Op.664 [concrete] // CHECK:STDOUT: %Int.as.Copy.impl.Op.specific_fn: = specific_function %Int.as.Copy.impl.Op.664, @Int.as.Copy.impl.Op(%int_32) [concrete] // CHECK:STDOUT: %bound_method.4d7: = bound_method %int_1.5d2, %Int.as.Copy.impl.Op.specific_fn [concrete] // CHECK:STDOUT: %tuple.elem1: ref %i32 = tuple_access file.%tuple_copy.var, element1 [concrete] // CHECK:STDOUT: %Int.as.Copy.impl.Op.bound.5e8: = bound_method %int_2.ef8, %Int.as.Copy.impl.Op.664 [concrete] // CHECK:STDOUT: %bound_method.f15: = bound_method %int_2.ef8, %Int.as.Copy.impl.Op.specific_fn [concrete] // CHECK:STDOUT: %struct_type.a.b.c: type = struct_type {.a: %i32, .b: %i32, .c: %i32} [concrete] // CHECK:STDOUT: %pattern_type.8ae: type = pattern_type %struct_type.a.b.c [concrete] // CHECK:STDOUT: %int_3.1ba: Core.IntLiteral = int_value 3 [concrete] // CHECK:STDOUT: %struct_type.c.b.a: type = struct_type {.c: Core.IntLiteral, .b: Core.IntLiteral, .a: Core.IntLiteral} [concrete] // CHECK:STDOUT: %struct.c75: %struct_type.c.b.a = struct_value (%int_3.1ba, %int_2.ecc, %int_1.5b8) [concrete] // CHECK:STDOUT: %struct_type.b.a.c: type = struct_type {.b: %i32, .a: %i32, .c: %i32} [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.bound.061: = bound_method %int_3.1ba, %Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5 [concrete] // CHECK:STDOUT: %bound_method.fa7: = bound_method %int_3.1ba, %Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn [concrete] // CHECK:STDOUT: %int_3.822: %i32 = int_value 3 [concrete] // CHECK:STDOUT: %struct.21d: %struct_type.b.a.c = struct_value (%int_2.ef8, %int_1.5d2, %int_3.822) [concrete] // CHECK:STDOUT: %.460: ref %i32 = struct_access file.%struct_copy.var, element1 [concrete] // CHECK:STDOUT: %.61f: ref %i32 = struct_access file.%struct_copy.var, element0 [concrete] // CHECK:STDOUT: %.f68: ref %i32 = struct_access file.%struct_copy.var, element2 [concrete] // CHECK:STDOUT: %Int.as.Copy.impl.Op.bound.ecc: = bound_method %int_3.822, %Int.as.Copy.impl.Op.664 [concrete] // CHECK:STDOUT: %bound_method.4f5: = bound_method %int_3.822, %Int.as.Copy.impl.Op.specific_fn [concrete] // CHECK:STDOUT: %struct.cff: %struct_type.a.b.c = struct_value (%int_1.5d2, %int_2.ef8, %int_3.822) [concrete] // CHECK:STDOUT: %array_type: type = array_type %int_1.5b8, %i32 [concrete] // CHECK:STDOUT: %pattern_type.a98: type = pattern_type %array_type [concrete] // CHECK:STDOUT: %int_0.5c6: Core.IntLiteral = int_value 0 [concrete] // CHECK:STDOUT: %tuple.type.985: type = tuple_type (Core.IntLiteral) [concrete] // CHECK:STDOUT: %tuple.4f2: %tuple.type.985 = tuple_value (%int_0.5c6) [concrete] // CHECK:STDOUT: %int_5: Core.IntLiteral = int_value 5 [concrete] // CHECK:STDOUT: %int_7: Core.IntLiteral = int_value 7 [concrete] // CHECK:STDOUT: %int_9: Core.IntLiteral = int_value 9 [concrete] // CHECK:STDOUT: %tuple.type.d46: type = tuple_type (Core.IntLiteral, Core.IntLiteral, Core.IntLiteral, Core.IntLiteral) [concrete] // CHECK:STDOUT: %tuple.869: %tuple.type.d46 = tuple_value (%int_5, %int_7, %int_1.5b8, %int_9) [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.bound.897: = bound_method %int_0.5c6, %Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5 [concrete] // CHECK:STDOUT: %bound_method.d2e: = bound_method %int_0.5c6, %Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn [concrete] // CHECK:STDOUT: %int_0.6a9: %i32 = int_value 0 [concrete] // CHECK:STDOUT: %array: %array_type = tuple_value (%int_0.6a9) [concrete] // CHECK:STDOUT: %struct_type.a.b: type = struct_type {.a: Core.IntLiteral, .b: Core.IntLiteral} [concrete] // CHECK:STDOUT: %struct.a81: %struct_type.a.b = struct_value (%int_3.1ba, %int_1.5b8) [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core.import_ref.42d: @Core.IntLiteral.as.ImplicitAs.impl.%Core.IntLiteral.as.ImplicitAs.impl.Convert.type (%Core.IntLiteral.as.ImplicitAs.impl.Convert.type.4e6) = import_ref Core//prelude/parts/int, loc{{\d+_\d+}}, loaded [symbolic = @Core.IntLiteral.as.ImplicitAs.impl.%Core.IntLiteral.as.ImplicitAs.impl.Convert (constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.3c2)] // CHECK:STDOUT: %ImplicitAs.impl_witness_table.74f = impl_witness_table (%Core.import_ref.42d), @Core.IntLiteral.as.ImplicitAs.impl [concrete] // CHECK:STDOUT: %Core.import_ref.18d: @Int.as.Copy.impl.%Int.as.Copy.impl.Op.type (%Int.as.Copy.impl.Op.type.824) = import_ref Core//prelude/parts/int, loc{{\d+_\d+}}, loaded [symbolic = @Int.as.Copy.impl.%Int.as.Copy.impl.Op (constants.%Int.as.Copy.impl.Op.9b9)] // CHECK:STDOUT: %Copy.impl_witness_table.e76 = impl_witness_table (%Core.import_ref.18d), @Int.as.Copy.impl [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %tuple_copy.patt: %pattern_type.511 = ref_binding_pattern tuple_copy [concrete] // CHECK:STDOUT: %tuple_copy.var_patt: %pattern_type.511 = var_pattern %tuple_copy.patt [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %tuple_copy.var: ref %tuple.type.d07 = var %tuple_copy.var_patt [concrete] // CHECK:STDOUT: %.loc4_26.1: type = splice_block %.loc4_26.3 [concrete = constants.%tuple.type.d07] { // CHECK:STDOUT: %i32.loc4_18: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %i32.loc4_23: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %.loc4_26.2: %tuple.type.24b = tuple_literal (%i32.loc4_18, %i32.loc4_23) [concrete = constants.%tuple.95a] // CHECK:STDOUT: %.loc4_26.3: type = converted %.loc4_26.2, constants.%tuple.type.d07 [concrete = constants.%tuple.type.d07] // CHECK:STDOUT: } // CHECK:STDOUT: %tuple_copy: ref %tuple.type.d07 = ref_binding tuple_copy, %tuple_copy.var [concrete = %tuple_copy.var] // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %struct_copy.patt: %pattern_type.8ae = ref_binding_pattern struct_copy [concrete] // CHECK:STDOUT: %struct_copy.var_patt: %pattern_type.8ae = var_pattern %struct_copy.patt [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %struct_copy.var: ref %struct_type.a.b.c = var %struct_copy.var_patt [concrete] // CHECK:STDOUT: %.loc6: type = splice_block %struct_type.a.b.c [concrete = constants.%struct_type.a.b.c] { // CHECK:STDOUT: %i32.loc6_23: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %i32.loc6_32: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %i32.loc6_41: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %struct_type.a.b.c: type = struct_type {.a: %i32, .b: %i32, .c: %i32} [concrete = constants.%struct_type.a.b.c] // CHECK:STDOUT: } // CHECK:STDOUT: %struct_copy: ref %struct_type.a.b.c = ref_binding struct_copy, %struct_copy.var [concrete = %struct_copy.var] // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %tuple_index.patt: %pattern_type.a98 = ref_binding_pattern tuple_index [concrete] // CHECK:STDOUT: %tuple_index.var_patt: %pattern_type.a98 = var_pattern %tuple_index.patt [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %tuple_index.var: ref %array_type = var %tuple_index.var_patt [concrete] // CHECK:STDOUT: %.loc8: type = splice_block %array_type.loc8 [concrete = constants.%array_type] { // CHECK:STDOUT: %i32.loc8: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %int_1.loc8: Core.IntLiteral = int_value 1 [concrete = constants.%int_1.5b8] // CHECK:STDOUT: %array_type.loc8: type = array_type %int_1.loc8, %i32.loc8 [concrete = constants.%array_type] // CHECK:STDOUT: } // CHECK:STDOUT: %tuple_index: ref %array_type = ref_binding tuple_index, %tuple_index.var [concrete = %tuple_index.var] // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %struct_access.patt: %pattern_type.a98 = ref_binding_pattern struct_access [concrete] // CHECK:STDOUT: %struct_access.var_patt: %pattern_type.a98 = var_pattern %struct_access.patt [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %struct_access.var: ref %array_type = var %struct_access.var_patt [concrete] // CHECK:STDOUT: %.loc10: type = splice_block %array_type.loc10 [concrete = constants.%array_type] { // CHECK:STDOUT: %i32.loc10: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %int_1.loc10: Core.IntLiteral = int_value 1 [concrete = constants.%int_1.5b8] // CHECK:STDOUT: %array_type.loc10: type = array_type %int_1.loc10, %i32.loc10 [concrete = constants.%array_type] // CHECK:STDOUT: } // CHECK:STDOUT: %struct_access: ref %array_type = ref_binding struct_access, %struct_access.var [concrete = %struct_access.var] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @__global_init() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %int_1.loc4: Core.IntLiteral = int_value 1 [concrete = constants.%int_1.5b8] // CHECK:STDOUT: %int_2.loc4: Core.IntLiteral = int_value 2 [concrete = constants.%int_2.ecc] // CHECK:STDOUT: %.loc4_35.1: %tuple.type.f94 = tuple_literal (%int_1.loc4, %int_2.loc4) [concrete = constants.%tuple.ad8] // CHECK:STDOUT: %i32.loc4_41: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %i32.loc4_46: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %.loc4_49.1: %tuple.type.24b = tuple_literal (%i32.loc4_41, %i32.loc4_46) [concrete = constants.%tuple.95a] // CHECK:STDOUT: %.loc4_49.2: type = converted %.loc4_49.1, constants.%tuple.type.d07 [concrete = constants.%tuple.type.d07] // CHECK:STDOUT: %impl.elem0.loc4_35.1: %.545 = impl_witness_access constants.%ImplicitAs.impl_witness.6bc, element0 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5] // CHECK:STDOUT: %bound_method.loc4_35.1: = bound_method %int_1.loc4, %impl.elem0.loc4_35.1 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.bound.215] // CHECK:STDOUT: %specific_fn.loc4_35.1: = specific_function %impl.elem0.loc4_35.1, @Core.IntLiteral.as.ImplicitAs.impl.Convert(constants.%int_32) [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn] // CHECK:STDOUT: %bound_method.loc4_35.2: = bound_method %int_1.loc4, %specific_fn.loc4_35.1 [concrete = constants.%bound_method.38b] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc4_35.1: init %i32 = call %bound_method.loc4_35.2(%int_1.loc4) [concrete = constants.%int_1.5d2] // CHECK:STDOUT: %.loc4_35.2: %i32 = value_of_initializer %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc4_35.1 [concrete = constants.%int_1.5d2] // CHECK:STDOUT: %.loc4_35.3: %i32 = converted %int_1.loc4, %.loc4_35.2 [concrete = constants.%int_1.5d2] // CHECK:STDOUT: %impl.elem0.loc4_35.2: %.545 = impl_witness_access constants.%ImplicitAs.impl_witness.6bc, element0 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5] // CHECK:STDOUT: %bound_method.loc4_35.3: = bound_method %int_2.loc4, %impl.elem0.loc4_35.2 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.bound.4e5] // CHECK:STDOUT: %specific_fn.loc4_35.2: = specific_function %impl.elem0.loc4_35.2, @Core.IntLiteral.as.ImplicitAs.impl.Convert(constants.%int_32) [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn] // CHECK:STDOUT: %bound_method.loc4_35.4: = bound_method %int_2.loc4, %specific_fn.loc4_35.2 [concrete = constants.%bound_method.646] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc4_35.2: init %i32 = call %bound_method.loc4_35.4(%int_2.loc4) [concrete = constants.%int_2.ef8] // CHECK:STDOUT: %.loc4_35.4: %i32 = value_of_initializer %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc4_35.2 [concrete = constants.%int_2.ef8] // CHECK:STDOUT: %.loc4_35.5: %i32 = converted %int_2.loc4, %.loc4_35.4 [concrete = constants.%int_2.ef8] // CHECK:STDOUT: %tuple.loc4: %tuple.type.d07 = tuple_value (%.loc4_35.3, %.loc4_35.5) [concrete = constants.%tuple.21c] // CHECK:STDOUT: %.loc4_37.1: %tuple.type.d07 = converted %.loc4_35.1, %tuple.loc4 [concrete = constants.%tuple.21c] // CHECK:STDOUT: %tuple.elem0.loc4_37.1: %i32 = tuple_access %.loc4_37.1, element0 [concrete = constants.%int_1.5d2] // CHECK:STDOUT: %impl.elem0.loc4_37.1: %.8e2 = impl_witness_access constants.%Copy.impl_witness.f17, element0 [concrete = constants.%Int.as.Copy.impl.Op.664] // CHECK:STDOUT: %bound_method.loc4_37.1: = bound_method %tuple.elem0.loc4_37.1, %impl.elem0.loc4_37.1 [concrete = constants.%Int.as.Copy.impl.Op.bound.ee3] // CHECK:STDOUT: %specific_fn.loc4_37.1: = specific_function %impl.elem0.loc4_37.1, @Int.as.Copy.impl.Op(constants.%int_32) [concrete = constants.%Int.as.Copy.impl.Op.specific_fn] // CHECK:STDOUT: %bound_method.loc4_37.2: = bound_method %tuple.elem0.loc4_37.1, %specific_fn.loc4_37.1 [concrete = constants.%bound_method.4d7] // CHECK:STDOUT: %Int.as.Copy.impl.Op.call.loc4_37.1: init %i32 = call %bound_method.loc4_37.2(%tuple.elem0.loc4_37.1) [concrete = constants.%int_1.5d2] // CHECK:STDOUT: %tuple.elem0.loc4_37.2: ref %i32 = tuple_access file.%tuple_copy.var, element0 [concrete = constants.%tuple.elem0] // CHECK:STDOUT: %.loc4_37.2: init %i32 to %tuple.elem0.loc4_37.2 = in_place_init %Int.as.Copy.impl.Op.call.loc4_37.1 [concrete = constants.%int_1.5d2] // CHECK:STDOUT: %tuple.elem1.loc4_37.1: %i32 = tuple_access %.loc4_37.1, element1 [concrete = constants.%int_2.ef8] // CHECK:STDOUT: %impl.elem0.loc4_37.2: %.8e2 = impl_witness_access constants.%Copy.impl_witness.f17, element0 [concrete = constants.%Int.as.Copy.impl.Op.664] // CHECK:STDOUT: %bound_method.loc4_37.3: = bound_method %tuple.elem1.loc4_37.1, %impl.elem0.loc4_37.2 [concrete = constants.%Int.as.Copy.impl.Op.bound.5e8] // CHECK:STDOUT: %specific_fn.loc4_37.2: = specific_function %impl.elem0.loc4_37.2, @Int.as.Copy.impl.Op(constants.%int_32) [concrete = constants.%Int.as.Copy.impl.Op.specific_fn] // CHECK:STDOUT: %bound_method.loc4_37.4: = bound_method %tuple.elem1.loc4_37.1, %specific_fn.loc4_37.2 [concrete = constants.%bound_method.f15] // CHECK:STDOUT: %Int.as.Copy.impl.Op.call.loc4_37.2: init %i32 = call %bound_method.loc4_37.4(%tuple.elem1.loc4_37.1) [concrete = constants.%int_2.ef8] // CHECK:STDOUT: %tuple.elem1.loc4_37.2: ref %i32 = tuple_access file.%tuple_copy.var, element1 [concrete = constants.%tuple.elem1] // CHECK:STDOUT: %.loc4_37.3: init %i32 to %tuple.elem1.loc4_37.2 = in_place_init %Int.as.Copy.impl.Op.call.loc4_37.2 [concrete = constants.%int_2.ef8] // CHECK:STDOUT: %.loc4_37.4: init %tuple.type.d07 to file.%tuple_copy.var = tuple_init (%.loc4_37.2, %.loc4_37.3) [concrete = constants.%tuple.21c] // CHECK:STDOUT: %.loc4_1: init %tuple.type.d07 = converted %.loc4_37.1, %.loc4_37.4 [concrete = constants.%tuple.21c] // CHECK:STDOUT: assign file.%tuple_copy.var, %.loc4_1 // CHECK:STDOUT: %int_3.loc6: Core.IntLiteral = int_value 3 [concrete = constants.%int_3.1ba] // CHECK:STDOUT: %int_2.loc6: Core.IntLiteral = int_value 2 [concrete = constants.%int_2.ecc] // CHECK:STDOUT: %int_1.loc6: Core.IntLiteral = int_value 1 [concrete = constants.%int_1.5b8] // CHECK:STDOUT: %.loc6_71.1: %struct_type.c.b.a = struct_literal (%int_3.loc6, %int_2.loc6, %int_1.loc6) [concrete = constants.%struct.c75] // CHECK:STDOUT: %i32.loc6_81: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %i32.loc6_90: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %i32.loc6_99: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %struct_type.b.a.c: type = struct_type {.b: %i32, .a: %i32, .c: %i32} [concrete = constants.%struct_type.b.a.c] // CHECK:STDOUT: %impl.elem0.loc6_71.1: %.545 = impl_witness_access constants.%ImplicitAs.impl_witness.6bc, element0 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5] // CHECK:STDOUT: %bound_method.loc6_71.1: = bound_method %int_2.loc6, %impl.elem0.loc6_71.1 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.bound.4e5] // CHECK:STDOUT: %specific_fn.loc6_71.1: = specific_function %impl.elem0.loc6_71.1, @Core.IntLiteral.as.ImplicitAs.impl.Convert(constants.%int_32) [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn] // CHECK:STDOUT: %bound_method.loc6_71.2: = bound_method %int_2.loc6, %specific_fn.loc6_71.1 [concrete = constants.%bound_method.646] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc6_71.1: init %i32 = call %bound_method.loc6_71.2(%int_2.loc6) [concrete = constants.%int_2.ef8] // CHECK:STDOUT: %.loc6_71.2: %i32 = value_of_initializer %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc6_71.1 [concrete = constants.%int_2.ef8] // CHECK:STDOUT: %.loc6_71.3: %i32 = converted %int_2.loc6, %.loc6_71.2 [concrete = constants.%int_2.ef8] // CHECK:STDOUT: %impl.elem0.loc6_71.2: %.545 = impl_witness_access constants.%ImplicitAs.impl_witness.6bc, element0 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5] // CHECK:STDOUT: %bound_method.loc6_71.3: = bound_method %int_1.loc6, %impl.elem0.loc6_71.2 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.bound.215] // CHECK:STDOUT: %specific_fn.loc6_71.2: = specific_function %impl.elem0.loc6_71.2, @Core.IntLiteral.as.ImplicitAs.impl.Convert(constants.%int_32) [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn] // CHECK:STDOUT: %bound_method.loc6_71.4: = bound_method %int_1.loc6, %specific_fn.loc6_71.2 [concrete = constants.%bound_method.38b] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc6_71.2: init %i32 = call %bound_method.loc6_71.4(%int_1.loc6) [concrete = constants.%int_1.5d2] // CHECK:STDOUT: %.loc6_71.4: %i32 = value_of_initializer %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc6_71.2 [concrete = constants.%int_1.5d2] // CHECK:STDOUT: %.loc6_71.5: %i32 = converted %int_1.loc6, %.loc6_71.4 [concrete = constants.%int_1.5d2] // CHECK:STDOUT: %impl.elem0.loc6_71.3: %.545 = impl_witness_access constants.%ImplicitAs.impl_witness.6bc, element0 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5] // CHECK:STDOUT: %bound_method.loc6_71.5: = bound_method %int_3.loc6, %impl.elem0.loc6_71.3 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.bound.061] // CHECK:STDOUT: %specific_fn.loc6_71.3: = specific_function %impl.elem0.loc6_71.3, @Core.IntLiteral.as.ImplicitAs.impl.Convert(constants.%int_32) [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn] // CHECK:STDOUT: %bound_method.loc6_71.6: = bound_method %int_3.loc6, %specific_fn.loc6_71.3 [concrete = constants.%bound_method.fa7] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc6_71.3: init %i32 = call %bound_method.loc6_71.6(%int_3.loc6) [concrete = constants.%int_3.822] // CHECK:STDOUT: %.loc6_71.6: %i32 = value_of_initializer %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc6_71.3 [concrete = constants.%int_3.822] // CHECK:STDOUT: %.loc6_71.7: %i32 = converted %int_3.loc6, %.loc6_71.6 [concrete = constants.%int_3.822] // CHECK:STDOUT: %struct.loc6: %struct_type.b.a.c = struct_value (%.loc6_71.3, %.loc6_71.5, %.loc6_71.7) [concrete = constants.%struct.21d] // CHECK:STDOUT: %.loc6_73.1: %struct_type.b.a.c = converted %.loc6_71.1, %struct.loc6 [concrete = constants.%struct.21d] // CHECK:STDOUT: %.loc6_73.2: %i32 = struct_access %.loc6_73.1, element1 [concrete = constants.%int_1.5d2] // CHECK:STDOUT: %impl.elem0.loc6_73.1: %.8e2 = impl_witness_access constants.%Copy.impl_witness.f17, element0 [concrete = constants.%Int.as.Copy.impl.Op.664] // CHECK:STDOUT: %bound_method.loc6_73.1: = bound_method %.loc6_73.2, %impl.elem0.loc6_73.1 [concrete = constants.%Int.as.Copy.impl.Op.bound.ee3] // CHECK:STDOUT: %specific_fn.loc6_73.1: = specific_function %impl.elem0.loc6_73.1, @Int.as.Copy.impl.Op(constants.%int_32) [concrete = constants.%Int.as.Copy.impl.Op.specific_fn] // CHECK:STDOUT: %bound_method.loc6_73.2: = bound_method %.loc6_73.2, %specific_fn.loc6_73.1 [concrete = constants.%bound_method.4d7] // CHECK:STDOUT: %Int.as.Copy.impl.Op.call.loc6_73.1: init %i32 = call %bound_method.loc6_73.2(%.loc6_73.2) [concrete = constants.%int_1.5d2] // CHECK:STDOUT: %.loc6_73.3: ref %i32 = struct_access file.%struct_copy.var, element1 [concrete = constants.%.460] // CHECK:STDOUT: %.loc6_73.4: init %i32 to %.loc6_73.3 = in_place_init %Int.as.Copy.impl.Op.call.loc6_73.1 [concrete = constants.%int_1.5d2] // CHECK:STDOUT: %.loc6_73.5: %i32 = struct_access %.loc6_73.1, element0 [concrete = constants.%int_2.ef8] // CHECK:STDOUT: %impl.elem0.loc6_73.2: %.8e2 = impl_witness_access constants.%Copy.impl_witness.f17, element0 [concrete = constants.%Int.as.Copy.impl.Op.664] // CHECK:STDOUT: %bound_method.loc6_73.3: = bound_method %.loc6_73.5, %impl.elem0.loc6_73.2 [concrete = constants.%Int.as.Copy.impl.Op.bound.5e8] // CHECK:STDOUT: %specific_fn.loc6_73.2: = specific_function %impl.elem0.loc6_73.2, @Int.as.Copy.impl.Op(constants.%int_32) [concrete = constants.%Int.as.Copy.impl.Op.specific_fn] // CHECK:STDOUT: %bound_method.loc6_73.4: = bound_method %.loc6_73.5, %specific_fn.loc6_73.2 [concrete = constants.%bound_method.f15] // CHECK:STDOUT: %Int.as.Copy.impl.Op.call.loc6_73.2: init %i32 = call %bound_method.loc6_73.4(%.loc6_73.5) [concrete = constants.%int_2.ef8] // CHECK:STDOUT: %.loc6_73.6: ref %i32 = struct_access file.%struct_copy.var, element0 [concrete = constants.%.61f] // CHECK:STDOUT: %.loc6_73.7: init %i32 to %.loc6_73.6 = in_place_init %Int.as.Copy.impl.Op.call.loc6_73.2 [concrete = constants.%int_2.ef8] // CHECK:STDOUT: %.loc6_73.8: %i32 = struct_access %.loc6_73.1, element2 [concrete = constants.%int_3.822] // CHECK:STDOUT: %impl.elem0.loc6_73.3: %.8e2 = impl_witness_access constants.%Copy.impl_witness.f17, element0 [concrete = constants.%Int.as.Copy.impl.Op.664] // CHECK:STDOUT: %bound_method.loc6_73.5: = bound_method %.loc6_73.8, %impl.elem0.loc6_73.3 [concrete = constants.%Int.as.Copy.impl.Op.bound.ecc] // CHECK:STDOUT: %specific_fn.loc6_73.3: = specific_function %impl.elem0.loc6_73.3, @Int.as.Copy.impl.Op(constants.%int_32) [concrete = constants.%Int.as.Copy.impl.Op.specific_fn] // CHECK:STDOUT: %bound_method.loc6_73.6: = bound_method %.loc6_73.8, %specific_fn.loc6_73.3 [concrete = constants.%bound_method.4f5] // CHECK:STDOUT: %Int.as.Copy.impl.Op.call.loc6_73.3: init %i32 = call %bound_method.loc6_73.6(%.loc6_73.8) [concrete = constants.%int_3.822] // CHECK:STDOUT: %.loc6_73.9: ref %i32 = struct_access file.%struct_copy.var, element2 [concrete = constants.%.f68] // CHECK:STDOUT: %.loc6_73.10: init %i32 to %.loc6_73.9 = in_place_init %Int.as.Copy.impl.Op.call.loc6_73.3 [concrete = constants.%int_3.822] // CHECK:STDOUT: %.loc6_73.11: init %struct_type.a.b.c to file.%struct_copy.var = struct_init (%.loc6_73.4, %.loc6_73.7, %.loc6_73.10) [concrete = constants.%struct.cff] // CHECK:STDOUT: %.loc6_1: init %struct_type.a.b.c = converted %.loc6_73.1, %.loc6_73.11 [concrete = constants.%struct.cff] // CHECK:STDOUT: assign file.%struct_copy.var, %.loc6_1 // CHECK:STDOUT: %int_0.loc8_35: Core.IntLiteral = int_value 0 [concrete = constants.%int_0.5c6] // CHECK:STDOUT: %.loc8_37.1: %tuple.type.985 = tuple_literal (%int_0.loc8_35) [concrete = constants.%tuple.4f2] // CHECK:STDOUT: %i32.loc8: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %int_5: Core.IntLiteral = int_value 5 [concrete = constants.%int_5] // CHECK:STDOUT: %int_7: Core.IntLiteral = int_value 7 [concrete = constants.%int_7] // CHECK:STDOUT: %int_1.loc8: Core.IntLiteral = int_value 1 [concrete = constants.%int_1.5b8] // CHECK:STDOUT: %int_9: Core.IntLiteral = int_value 9 [concrete = constants.%int_9] // CHECK:STDOUT: %.loc8_64.1: %tuple.type.d46 = tuple_literal (%int_5, %int_7, %int_1.loc8, %int_9) [concrete = constants.%tuple.869] // CHECK:STDOUT: %int_2.loc8: Core.IntLiteral = int_value 2 [concrete = constants.%int_2.ecc] // CHECK:STDOUT: %tuple.loc8: %tuple.type.d46 = tuple_value (%int_5, %int_7, %int_1.loc8, %int_9) [concrete = constants.%tuple.869] // CHECK:STDOUT: %.loc8_64.2: %tuple.type.d46 = converted %.loc8_64.1, %tuple.loc8 [concrete = constants.%tuple.869] // CHECK:STDOUT: %tuple.elem2: Core.IntLiteral = tuple_access %.loc8_64.2, element2 [concrete = constants.%int_1.5b8] // CHECK:STDOUT: %array_type.loc8: type = array_type %tuple.elem2, %i32.loc8 [concrete = constants.%array_type] // CHECK:STDOUT: %impl.elem0.loc8: %.545 = impl_witness_access constants.%ImplicitAs.impl_witness.6bc, element0 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5] // CHECK:STDOUT: %bound_method.loc8_37.1: = bound_method %int_0.loc8_35, %impl.elem0.loc8 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.bound.897] // CHECK:STDOUT: %specific_fn.loc8: = specific_function %impl.elem0.loc8, @Core.IntLiteral.as.ImplicitAs.impl.Convert(constants.%int_32) [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn] // CHECK:STDOUT: %bound_method.loc8_37.2: = bound_method %int_0.loc8_35, %specific_fn.loc8 [concrete = constants.%bound_method.d2e] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc8: init %i32 = call %bound_method.loc8_37.2(%int_0.loc8_35) [concrete = constants.%int_0.6a9] // CHECK:STDOUT: %.loc8_37.2: init %i32 = converted %int_0.loc8_35, %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc8 [concrete = constants.%int_0.6a9] // CHECK:STDOUT: %.loc8_1: ref %array_type = splice_block file.%tuple_index.var [concrete = file.%tuple_index.var] {} // CHECK:STDOUT: %int_0.loc8_37: Core.IntLiteral = int_value 0 [concrete = constants.%int_0.5c6] // CHECK:STDOUT: %.loc8_37.3: ref %i32 = array_index %.loc8_1, %int_0.loc8_37 // CHECK:STDOUT: %.loc8_37.4: init %i32 to %.loc8_37.3 = in_place_init %.loc8_37.2 [concrete = constants.%int_0.6a9] // CHECK:STDOUT: %.loc8_37.5: init %array_type to %.loc8_1 = array_init (%.loc8_37.4) [concrete = constants.%array] // CHECK:STDOUT: %.loc8_39: init %array_type = converted %.loc8_37.1, %.loc8_37.5 [concrete = constants.%array] // CHECK:STDOUT: assign file.%tuple_index.var, %.loc8_39 // CHECK:STDOUT: %int_0.loc10_37: Core.IntLiteral = int_value 0 [concrete = constants.%int_0.5c6] // CHECK:STDOUT: %.loc10_39.1: %tuple.type.985 = tuple_literal (%int_0.loc10_37) [concrete = constants.%tuple.4f2] // CHECK:STDOUT: %i32.loc10: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %int_3.loc10: Core.IntLiteral = int_value 3 [concrete = constants.%int_3.1ba] // CHECK:STDOUT: %int_1.loc10: Core.IntLiteral = int_value 1 [concrete = constants.%int_1.5b8] // CHECK:STDOUT: %.loc10_70.1: %struct_type.a.b = struct_literal (%int_3.loc10, %int_1.loc10) [concrete = constants.%struct.a81] // CHECK:STDOUT: %struct.loc10: %struct_type.a.b = struct_value (%int_3.loc10, %int_1.loc10) [concrete = constants.%struct.a81] // CHECK:STDOUT: %.loc10_70.2: %struct_type.a.b = converted %.loc10_70.1, %struct.loc10 [concrete = constants.%struct.a81] // CHECK:STDOUT: %.loc10_71: Core.IntLiteral = struct_access %.loc10_70.2, element1 [concrete = constants.%int_1.5b8] // CHECK:STDOUT: %array_type.loc10: type = array_type %.loc10_71, %i32.loc10 [concrete = constants.%array_type] // CHECK:STDOUT: %impl.elem0.loc10: %.545 = impl_witness_access constants.%ImplicitAs.impl_witness.6bc, element0 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5] // CHECK:STDOUT: %bound_method.loc10_39.1: = bound_method %int_0.loc10_37, %impl.elem0.loc10 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.bound.897] // CHECK:STDOUT: %specific_fn.loc10: = specific_function %impl.elem0.loc10, @Core.IntLiteral.as.ImplicitAs.impl.Convert(constants.%int_32) [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn] // CHECK:STDOUT: %bound_method.loc10_39.2: = bound_method %int_0.loc10_37, %specific_fn.loc10 [concrete = constants.%bound_method.d2e] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc10: init %i32 = call %bound_method.loc10_39.2(%int_0.loc10_37) [concrete = constants.%int_0.6a9] // CHECK:STDOUT: %.loc10_39.2: init %i32 = converted %int_0.loc10_37, %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc10 [concrete = constants.%int_0.6a9] // CHECK:STDOUT: %.loc10_1: ref %array_type = splice_block file.%struct_access.var [concrete = file.%struct_access.var] {} // CHECK:STDOUT: %int_0.loc10_39: Core.IntLiteral = int_value 0 [concrete = constants.%int_0.5c6] // CHECK:STDOUT: %.loc10_39.3: ref %i32 = array_index %.loc10_1, %int_0.loc10_39 // CHECK:STDOUT: %.loc10_39.4: init %i32 to %.loc10_39.3 = in_place_init %.loc10_39.2 [concrete = constants.%int_0.6a9] // CHECK:STDOUT: %.loc10_39.5: init %array_type to %.loc10_1 = array_init (%.loc10_39.4) [concrete = constants.%array] // CHECK:STDOUT: %.loc10_41: init %array_type = converted %.loc10_39.1, %.loc10_39.5 [concrete = constants.%array] // CHECK:STDOUT: assign file.%struct_access.var, %.loc10_41 // CHECK:STDOUT: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- fail_todo_array_temporary.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [concrete] // CHECK:STDOUT: %i32: type = class_type @Int, @Int(%int_32) [concrete] // CHECK:STDOUT: %int_1.5b8: Core.IntLiteral = int_value 1 [concrete] // CHECK:STDOUT: %array_type.0cb: type = array_type %int_1.5b8, %i32 [concrete] // CHECK:STDOUT: %pattern_type.a98: type = pattern_type %array_type.0cb [concrete] // CHECK:STDOUT: %int_0: Core.IntLiteral = int_value 0 [concrete] // CHECK:STDOUT: %tuple.type.985: type = tuple_type (Core.IntLiteral) [concrete] // CHECK:STDOUT: %tuple.4f2: %tuple.type.985 = tuple_value (%int_0) [concrete] // CHECK:STDOUT: %int_5.64b: Core.IntLiteral = int_value 5 [concrete] // CHECK:STDOUT: %int_7.29f: Core.IntLiteral = int_value 7 [concrete] // CHECK:STDOUT: %int_9.988: Core.IntLiteral = int_value 9 [concrete] // CHECK:STDOUT: %tuple.type.d46: type = tuple_type (Core.IntLiteral, Core.IntLiteral, Core.IntLiteral, Core.IntLiteral) [concrete] // CHECK:STDOUT: %tuple.869: %tuple.type.d46 = tuple_value (%int_5.64b, %int_7.29f, %int_1.5b8, %int_9.988) [concrete] // CHECK:STDOUT: %int_4: Core.IntLiteral = int_value 4 [concrete] // CHECK:STDOUT: %array_type.f32: type = array_type %int_4, %i32 [concrete] // CHECK:STDOUT: %ImplicitAs.type.e8c: type = facet_type <@ImplicitAs, @ImplicitAs(%i32)> [concrete] // CHECK:STDOUT: %To: Core.IntLiteral = symbolic_binding To, 0 [symbolic] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.4e6: type = fn_type @Core.IntLiteral.as.ImplicitAs.impl.Convert, @Core.IntLiteral.as.ImplicitAs.impl(%To) [symbolic] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.3c2: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.4e6 = struct_value () [symbolic] // CHECK:STDOUT: %ImplicitAs.impl_witness.6bc: = impl_witness imports.%ImplicitAs.impl_witness_table.74f, @Core.IntLiteral.as.ImplicitAs.impl(%int_32) [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.e0d: type = fn_type @Core.IntLiteral.as.ImplicitAs.impl.Convert, @Core.IntLiteral.as.ImplicitAs.impl(%int_32) [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.e0d = struct_value () [concrete] // CHECK:STDOUT: %ImplicitAs.facet: %ImplicitAs.type.e8c = facet_value Core.IntLiteral, (%ImplicitAs.impl_witness.6bc) [concrete] // CHECK:STDOUT: %ImplicitAs.WithSelf.Convert.type.b37: type = fn_type @ImplicitAs.WithSelf.Convert, @ImplicitAs.WithSelf(%i32, %ImplicitAs.facet) [concrete] // CHECK:STDOUT: %.545: type = fn_type_with_self_type %ImplicitAs.WithSelf.Convert.type.b37, %ImplicitAs.facet [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.bound.005: = bound_method %int_5.64b, %Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5 [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn: = specific_function %Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5, @Core.IntLiteral.as.ImplicitAs.impl.Convert(%int_32) [concrete] // CHECK:STDOUT: %bound_method.e9d: = bound_method %int_5.64b, %Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn [concrete] // CHECK:STDOUT: %int_5.0f6: %i32 = int_value 5 [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.bound.1e0: = bound_method %int_7.29f, %Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5 [concrete] // CHECK:STDOUT: %bound_method.bf2: = bound_method %int_7.29f, %Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn [concrete] // CHECK:STDOUT: %int_7.0b1: %i32 = int_value 7 [concrete] // CHECK:STDOUT: %int_2.ecc: Core.IntLiteral = int_value 2 [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.bound.215: = bound_method %int_1.5b8, %Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5 [concrete] // CHECK:STDOUT: %bound_method.38b: = bound_method %int_1.5b8, %Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn [concrete] // CHECK:STDOUT: %int_1.5d2: %i32 = int_value 1 [concrete] // CHECK:STDOUT: %int_3: Core.IntLiteral = int_value 3 [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.bound.87d: = bound_method %int_9.988, %Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5 [concrete] // CHECK:STDOUT: %bound_method.661: = bound_method %int_9.988, %Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn [concrete] // CHECK:STDOUT: %int_9.f88: %i32 = int_value 9 [concrete] // CHECK:STDOUT: %array: %array_type.f32 = tuple_value (%int_5.0f6, %int_7.0b1, %int_1.5d2, %int_9.f88) [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.bound.4e5: = bound_method %int_2.ecc, %Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5 [concrete] // CHECK:STDOUT: %bound_method.646: = bound_method %int_2.ecc, %Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn [concrete] // CHECK:STDOUT: %int_2.ef8: %i32 = int_value 2 [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core.import_ref.42d: @Core.IntLiteral.as.ImplicitAs.impl.%Core.IntLiteral.as.ImplicitAs.impl.Convert.type (%Core.IntLiteral.as.ImplicitAs.impl.Convert.type.4e6) = import_ref Core//prelude/parts/int, loc{{\d+_\d+}}, loaded [symbolic = @Core.IntLiteral.as.ImplicitAs.impl.%Core.IntLiteral.as.ImplicitAs.impl.Convert (constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.3c2)] // CHECK:STDOUT: %ImplicitAs.impl_witness_table.74f = impl_witness_table (%Core.import_ref.42d), @Core.IntLiteral.as.ImplicitAs.impl [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %array_index.patt: %pattern_type.a98 = ref_binding_pattern array_index [concrete] // CHECK:STDOUT: %array_index.var_patt: %pattern_type.a98 = var_pattern %array_index.patt [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %array_index.var: ref %array_type.0cb = var %array_index.var_patt [concrete] // CHECK:STDOUT: %.loc9: type = splice_block %array_type [concrete = constants.%array_type.0cb] { // CHECK:STDOUT: %i32: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %int_1: Core.IntLiteral = int_value 1 [concrete = constants.%int_1.5b8] // CHECK:STDOUT: %array_type: type = array_type %int_1, %i32 [concrete = constants.%array_type.0cb] // CHECK:STDOUT: } // CHECK:STDOUT: %array_index: ref %array_type.0cb = ref_binding array_index, %array_index.var [concrete = %array_index.var] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @__global_init() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %int_0.loc9_35: Core.IntLiteral = int_value 0 [concrete = constants.%int_0] // CHECK:STDOUT: %.loc9_37: %tuple.type.985 = tuple_literal (%int_0.loc9_35) [concrete = constants.%tuple.4f2] // CHECK:STDOUT: %i32.loc9_48: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %int_5: Core.IntLiteral = int_value 5 [concrete = constants.%int_5.64b] // CHECK:STDOUT: %int_7: Core.IntLiteral = int_value 7 [concrete = constants.%int_7.29f] // CHECK:STDOUT: %int_1.loc9_61: Core.IntLiteral = int_value 1 [concrete = constants.%int_1.5b8] // CHECK:STDOUT: %int_9: Core.IntLiteral = int_value 9 [concrete = constants.%int_9.988] // CHECK:STDOUT: %.loc9_65.1: %tuple.type.d46 = tuple_literal (%int_5, %int_7, %int_1.loc9_61, %int_9) [concrete = constants.%tuple.869] // CHECK:STDOUT: %i32.loc9_76: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %int_4: Core.IntLiteral = int_value 4 [concrete = constants.%int_4] // CHECK:STDOUT: %array_type: type = array_type %int_4, %i32.loc9_76 [concrete = constants.%array_type.f32] // CHECK:STDOUT: %impl.elem0.loc9_65.1: %.545 = impl_witness_access constants.%ImplicitAs.impl_witness.6bc, element0 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5] // CHECK:STDOUT: %bound_method.loc9_65.1: = bound_method %int_5, %impl.elem0.loc9_65.1 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.bound.005] // CHECK:STDOUT: %specific_fn.loc9_65.1: = specific_function %impl.elem0.loc9_65.1, @Core.IntLiteral.as.ImplicitAs.impl.Convert(constants.%int_32) [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn] // CHECK:STDOUT: %bound_method.loc9_65.2: = bound_method %int_5, %specific_fn.loc9_65.1 [concrete = constants.%bound_method.e9d] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc9_65.1: init %i32 = call %bound_method.loc9_65.2(%int_5) [concrete = constants.%int_5.0f6] // CHECK:STDOUT: %.loc9_65.2: init %i32 = converted %int_5, %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc9_65.1 [concrete = constants.%int_5.0f6] // CHECK:STDOUT: %.loc9_65.3: ref %array_type.f32 = temporary_storage // CHECK:STDOUT: %int_0.loc9_65: Core.IntLiteral = int_value 0 [concrete = constants.%int_0] // CHECK:STDOUT: %.loc9_65.4: ref %i32 = array_index %.loc9_65.3, %int_0.loc9_65 // CHECK:STDOUT: %.loc9_65.5: init %i32 to %.loc9_65.4 = in_place_init %.loc9_65.2 [concrete = constants.%int_5.0f6] // CHECK:STDOUT: %impl.elem0.loc9_65.2: %.545 = impl_witness_access constants.%ImplicitAs.impl_witness.6bc, element0 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5] // CHECK:STDOUT: %bound_method.loc9_65.3: = bound_method %int_7, %impl.elem0.loc9_65.2 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.bound.1e0] // CHECK:STDOUT: %specific_fn.loc9_65.2: = specific_function %impl.elem0.loc9_65.2, @Core.IntLiteral.as.ImplicitAs.impl.Convert(constants.%int_32) [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn] // CHECK:STDOUT: %bound_method.loc9_65.4: = bound_method %int_7, %specific_fn.loc9_65.2 [concrete = constants.%bound_method.bf2] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc9_65.2: init %i32 = call %bound_method.loc9_65.4(%int_7) [concrete = constants.%int_7.0b1] // CHECK:STDOUT: %.loc9_65.6: init %i32 = converted %int_7, %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc9_65.2 [concrete = constants.%int_7.0b1] // CHECK:STDOUT: %int_1.loc9_65: Core.IntLiteral = int_value 1 [concrete = constants.%int_1.5b8] // CHECK:STDOUT: %.loc9_65.7: ref %i32 = array_index %.loc9_65.3, %int_1.loc9_65 // CHECK:STDOUT: %.loc9_65.8: init %i32 to %.loc9_65.7 = in_place_init %.loc9_65.6 [concrete = constants.%int_7.0b1] // CHECK:STDOUT: %impl.elem0.loc9_65.3: %.545 = impl_witness_access constants.%ImplicitAs.impl_witness.6bc, element0 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5] // CHECK:STDOUT: %bound_method.loc9_65.5: = bound_method %int_1.loc9_61, %impl.elem0.loc9_65.3 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.bound.215] // CHECK:STDOUT: %specific_fn.loc9_65.3: = specific_function %impl.elem0.loc9_65.3, @Core.IntLiteral.as.ImplicitAs.impl.Convert(constants.%int_32) [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn] // CHECK:STDOUT: %bound_method.loc9_65.6: = bound_method %int_1.loc9_61, %specific_fn.loc9_65.3 [concrete = constants.%bound_method.38b] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc9_65.3: init %i32 = call %bound_method.loc9_65.6(%int_1.loc9_61) [concrete = constants.%int_1.5d2] // CHECK:STDOUT: %.loc9_65.9: init %i32 = converted %int_1.loc9_61, %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc9_65.3 [concrete = constants.%int_1.5d2] // CHECK:STDOUT: %int_2.loc9_65: Core.IntLiteral = int_value 2 [concrete = constants.%int_2.ecc] // CHECK:STDOUT: %.loc9_65.10: ref %i32 = array_index %.loc9_65.3, %int_2.loc9_65 // CHECK:STDOUT: %.loc9_65.11: init %i32 to %.loc9_65.10 = in_place_init %.loc9_65.9 [concrete = constants.%int_1.5d2] // CHECK:STDOUT: %impl.elem0.loc9_65.4: %.545 = impl_witness_access constants.%ImplicitAs.impl_witness.6bc, element0 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5] // CHECK:STDOUT: %bound_method.loc9_65.7: = bound_method %int_9, %impl.elem0.loc9_65.4 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.bound.87d] // CHECK:STDOUT: %specific_fn.loc9_65.4: = specific_function %impl.elem0.loc9_65.4, @Core.IntLiteral.as.ImplicitAs.impl.Convert(constants.%int_32) [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn] // CHECK:STDOUT: %bound_method.loc9_65.8: = bound_method %int_9, %specific_fn.loc9_65.4 [concrete = constants.%bound_method.661] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc9_65.4: init %i32 = call %bound_method.loc9_65.8(%int_9) [concrete = constants.%int_9.f88] // CHECK:STDOUT: %.loc9_65.12: init %i32 = converted %int_9, %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc9_65.4 [concrete = constants.%int_9.f88] // CHECK:STDOUT: %int_3: Core.IntLiteral = int_value 3 [concrete = constants.%int_3] // CHECK:STDOUT: %.loc9_65.13: ref %i32 = array_index %.loc9_65.3, %int_3 // CHECK:STDOUT: %.loc9_65.14: init %i32 to %.loc9_65.13 = in_place_init %.loc9_65.12 [concrete = constants.%int_9.f88] // CHECK:STDOUT: %.loc9_65.15: init %array_type.f32 to %.loc9_65.3 = array_init (%.loc9_65.5, %.loc9_65.8, %.loc9_65.11, %.loc9_65.14) [concrete = constants.%array] // CHECK:STDOUT: %.loc9_67.1: init %array_type.f32 = converted %.loc9_65.1, %.loc9_65.15 [concrete = constants.%array] // CHECK:STDOUT: %int_2.loc9_85: Core.IntLiteral = int_value 2 [concrete = constants.%int_2.ecc] // CHECK:STDOUT: %.loc9_67.2: ref %array_type.f32 = temporary %.loc9_65.3, %.loc9_67.1 // CHECK:STDOUT: %impl.elem0.loc9_85: %.545 = impl_witness_access constants.%ImplicitAs.impl_witness.6bc, element0 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5] // CHECK:STDOUT: %bound_method.loc9_85.1: = bound_method %int_2.loc9_85, %impl.elem0.loc9_85 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.bound.4e5] // CHECK:STDOUT: %specific_fn.loc9_85: = specific_function %impl.elem0.loc9_85, @Core.IntLiteral.as.ImplicitAs.impl.Convert(constants.%int_32) [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn] // CHECK:STDOUT: %bound_method.loc9_85.2: = bound_method %int_2.loc9_85, %specific_fn.loc9_85 [concrete = constants.%bound_method.646] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc9_85: init %i32 = call %bound_method.loc9_85.2(%int_2.loc9_85) [concrete = constants.%int_2.ef8] // CHECK:STDOUT: %.loc9_85.1: %i32 = value_of_initializer %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc9_85 [concrete = constants.%int_2.ef8] // CHECK:STDOUT: %.loc9_85.2: %i32 = converted %int_2.loc9_85, %.loc9_85.1 [concrete = constants.%int_2.ef8] // CHECK:STDOUT: %.loc9_86.1: ref %i32 = array_index %.loc9_67.2, %.loc9_85.2 // CHECK:STDOUT: %.loc9_86.2: %i32 = acquire_value %.loc9_86.1 // CHECK:STDOUT: assign file.%array_index.var, // CHECK:STDOUT: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- symbolic.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %T: type = symbolic_binding T, 0 [symbolic] // CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] // CHECK:STDOUT: %ptr.e8f: type = ptr_type %T [symbolic] // CHECK:STDOUT: %const: type = const_type %T [symbolic] // CHECK:STDOUT: %tuple.type.24b: type = tuple_type (type, type) [concrete] // CHECK:STDOUT: %tuple: %tuple.type.24b = tuple_value (%ptr.e8f, %const) [symbolic] // CHECK:STDOUT: %tuple.type.3c8: type = tuple_type (%ptr.e8f, %const) [symbolic] // CHECK:STDOUT: %require_complete.666: = require_complete_type %tuple.type.3c8 [symbolic] // CHECK:STDOUT: %pattern_type.4ac: type = pattern_type %tuple.type.3c8 [symbolic] // CHECK:STDOUT: %DefaultOrUnformed.type: type = facet_type <@DefaultOrUnformed> [concrete] // CHECK:STDOUT: %.f7e: require_specific_def_type = require_specific_def @T.as.DefaultOrUnformed.impl(%tuple.type.3c8) [symbolic] // CHECK:STDOUT: %DefaultOrUnformed.lookup_impl_witness.2ad: = lookup_impl_witness %tuple.type.3c8, @DefaultOrUnformed [symbolic] // CHECK:STDOUT: %DefaultOrUnformed.facet.9a0: %DefaultOrUnformed.type = facet_value %tuple.type.3c8, (%DefaultOrUnformed.lookup_impl_witness.2ad) [symbolic] // CHECK:STDOUT: %DefaultOrUnformed.WithSelf.Op.type.0af: type = fn_type @DefaultOrUnformed.WithSelf.Op, @DefaultOrUnformed.WithSelf(%DefaultOrUnformed.facet.9a0) [symbolic] // CHECK:STDOUT: %.246: type = fn_type_with_self_type %DefaultOrUnformed.WithSelf.Op.type.0af, %DefaultOrUnformed.facet.9a0 [symbolic] // CHECK:STDOUT: %impl.elem0.c6f: %.246 = impl_witness_access %DefaultOrUnformed.lookup_impl_witness.2ad, element0 [symbolic] // CHECK:STDOUT: %specific_impl_fn.58b: = specific_impl_function %impl.elem0.c6f, @DefaultOrUnformed.WithSelf.Op(%DefaultOrUnformed.facet.9a0) [symbolic] // CHECK:STDOUT: %struct_type.a: type = struct_type {.a: %T} [symbolic] // CHECK:STDOUT: %require_complete.5d6: = require_complete_type %struct_type.a [symbolic] // CHECK:STDOUT: %pattern_type.7b9: type = pattern_type %struct_type.a [symbolic] // CHECK:STDOUT: %.d26: require_specific_def_type = require_specific_def @T.as.DefaultOrUnformed.impl(%struct_type.a) [symbolic] // CHECK:STDOUT: %DefaultOrUnformed.lookup_impl_witness.169: = lookup_impl_witness %struct_type.a, @DefaultOrUnformed [symbolic] // CHECK:STDOUT: %DefaultOrUnformed.facet.ca5: %DefaultOrUnformed.type = facet_value %struct_type.a, (%DefaultOrUnformed.lookup_impl_witness.169) [symbolic] // CHECK:STDOUT: %DefaultOrUnformed.WithSelf.Op.type.0c5: type = fn_type @DefaultOrUnformed.WithSelf.Op, @DefaultOrUnformed.WithSelf(%DefaultOrUnformed.facet.ca5) [symbolic] // CHECK:STDOUT: %.9ac: type = fn_type_with_self_type %DefaultOrUnformed.WithSelf.Op.type.0c5, %DefaultOrUnformed.facet.ca5 [symbolic] // CHECK:STDOUT: %impl.elem0.e9c: %.9ac = impl_witness_access %DefaultOrUnformed.lookup_impl_witness.169, element0 [symbolic] // CHECK:STDOUT: %specific_impl_fn.cf6: = specific_impl_function %impl.elem0.e9c, @DefaultOrUnformed.WithSelf.Op(%DefaultOrUnformed.facet.ca5) [symbolic] // CHECK:STDOUT: %int_5: Core.IntLiteral = int_value 5 [concrete] // CHECK:STDOUT: %array_type.742: type = array_type %int_5, %T [symbolic] // CHECK:STDOUT: %require_complete.345: = require_complete_type %array_type.742 [symbolic] // CHECK:STDOUT: %pattern_type.d52: type = pattern_type %array_type.742 [symbolic] // CHECK:STDOUT: %.617: require_specific_def_type = require_specific_def @T.as.DefaultOrUnformed.impl(%array_type.742) [symbolic] // CHECK:STDOUT: %DefaultOrUnformed.lookup_impl_witness.945: = lookup_impl_witness %array_type.742, @DefaultOrUnformed [symbolic] // CHECK:STDOUT: %DefaultOrUnformed.facet.ad6: %DefaultOrUnformed.type = facet_value %array_type.742, (%DefaultOrUnformed.lookup_impl_witness.945) [symbolic] // CHECK:STDOUT: %DefaultOrUnformed.WithSelf.Op.type.2f0: type = fn_type @DefaultOrUnformed.WithSelf.Op, @DefaultOrUnformed.WithSelf(%DefaultOrUnformed.facet.ad6) [symbolic] // CHECK:STDOUT: %.544: type = fn_type_with_self_type %DefaultOrUnformed.WithSelf.Op.type.2f0, %DefaultOrUnformed.facet.ad6 [symbolic] // CHECK:STDOUT: %impl.elem0.97e: %.544 = impl_witness_access %DefaultOrUnformed.lookup_impl_witness.945, element0 [symbolic] // CHECK:STDOUT: %specific_impl_fn.261: = specific_impl_function %impl.elem0.97e, @DefaultOrUnformed.WithSelf.Op(%DefaultOrUnformed.facet.ad6) [symbolic] // CHECK:STDOUT: %Destroy.type: type = facet_type <@Destroy> [concrete] // CHECK:STDOUT: %Destroy.lookup_impl_witness.b29: = lookup_impl_witness %array_type.742, @Destroy [symbolic] // CHECK:STDOUT: %Destroy.facet.ddc: %Destroy.type = facet_value %array_type.742, (%Destroy.lookup_impl_witness.b29) [symbolic] // CHECK:STDOUT: %Destroy.WithSelf.Op.type.3c6: type = fn_type @Destroy.WithSelf.Op, @Destroy.WithSelf(%Destroy.facet.ddc) [symbolic] // CHECK:STDOUT: %.950: type = fn_type_with_self_type %Destroy.WithSelf.Op.type.3c6, %Destroy.facet.ddc [symbolic] // CHECK:STDOUT: %impl.elem0.fa5: %.950 = impl_witness_access %Destroy.lookup_impl_witness.b29, element0 [symbolic] // CHECK:STDOUT: %specific_impl_fn.ede: = specific_impl_function %impl.elem0.fa5, @Destroy.WithSelf.Op(%Destroy.facet.ddc) [symbolic] // CHECK:STDOUT: %Destroy.lookup_impl_witness.bed: = lookup_impl_witness %struct_type.a, @Destroy [symbolic] // CHECK:STDOUT: %Destroy.facet.d21: %Destroy.type = facet_value %struct_type.a, (%Destroy.lookup_impl_witness.bed) [symbolic] // CHECK:STDOUT: %Destroy.WithSelf.Op.type.873: type = fn_type @Destroy.WithSelf.Op, @Destroy.WithSelf(%Destroy.facet.d21) [symbolic] // CHECK:STDOUT: %.511: type = fn_type_with_self_type %Destroy.WithSelf.Op.type.873, %Destroy.facet.d21 [symbolic] // CHECK:STDOUT: %impl.elem0.12c: %.511 = impl_witness_access %Destroy.lookup_impl_witness.bed, element0 [symbolic] // CHECK:STDOUT: %specific_impl_fn.c3a: = specific_impl_function %impl.elem0.12c, @Destroy.WithSelf.Op(%Destroy.facet.d21) [symbolic] // CHECK:STDOUT: %Destroy.lookup_impl_witness.7eb: = lookup_impl_witness %tuple.type.3c8, @Destroy [symbolic] // CHECK:STDOUT: %Destroy.facet.66d: %Destroy.type = facet_value %tuple.type.3c8, (%Destroy.lookup_impl_witness.7eb) [symbolic] // CHECK:STDOUT: %Destroy.WithSelf.Op.type.f23: type = fn_type @Destroy.WithSelf.Op, @Destroy.WithSelf(%Destroy.facet.66d) [symbolic] // CHECK:STDOUT: %.f20: type = fn_type_with_self_type %Destroy.WithSelf.Op.type.f23, %Destroy.facet.66d [symbolic] // CHECK:STDOUT: %impl.elem0.f5f: %.f20 = impl_witness_access %Destroy.lookup_impl_witness.7eb, element0 [symbolic] // CHECK:STDOUT: %specific_impl_fn.87c: = specific_impl_function %impl.elem0.f5f, @Destroy.WithSelf.Op(%Destroy.facet.66d) [symbolic] // CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [concrete] // CHECK:STDOUT: %i32: type = class_type @Int, @Int(%int_32) [concrete] // CHECK:STDOUT: %N.5de: %i32 = symbolic_binding N, 0 [symbolic] // CHECK:STDOUT: %ImplicitAs.type.139: type = facet_type <@ImplicitAs, @ImplicitAs(Core.IntLiteral)> [concrete] // CHECK:STDOUT: %From: Core.IntLiteral = symbolic_binding From, 0 [symbolic] // CHECK:STDOUT: %Int.as.ImplicitAs.impl.Convert.type.2ed: type = fn_type @Int.as.ImplicitAs.impl.Convert, @Int.as.ImplicitAs.impl(%From) [symbolic] // CHECK:STDOUT: %Int.as.ImplicitAs.impl.Convert.d29: %Int.as.ImplicitAs.impl.Convert.type.2ed = struct_value () [symbolic] // CHECK:STDOUT: %ImplicitAs.impl_witness.640: = impl_witness imports.%ImplicitAs.impl_witness_table.ea2, @Int.as.ImplicitAs.impl(%int_32) [concrete] // CHECK:STDOUT: %Int.as.ImplicitAs.impl.Convert.type.240: type = fn_type @Int.as.ImplicitAs.impl.Convert, @Int.as.ImplicitAs.impl(%int_32) [concrete] // CHECK:STDOUT: %Int.as.ImplicitAs.impl.Convert.dd4: %Int.as.ImplicitAs.impl.Convert.type.240 = struct_value () [concrete] // CHECK:STDOUT: %ImplicitAs.facet: %ImplicitAs.type.139 = facet_value %i32, (%ImplicitAs.impl_witness.640) [concrete] // CHECK:STDOUT: %ImplicitAs.WithSelf.Convert.type.462: type = fn_type @ImplicitAs.WithSelf.Convert, @ImplicitAs.WithSelf(Core.IntLiteral, %ImplicitAs.facet) [concrete] // CHECK:STDOUT: %.0a7: type = fn_type_with_self_type %ImplicitAs.WithSelf.Convert.type.462, %ImplicitAs.facet [concrete] // CHECK:STDOUT: %Int.as.ImplicitAs.impl.Convert.bound: = bound_method %N.5de, %Int.as.ImplicitAs.impl.Convert.dd4 [symbolic] // CHECK:STDOUT: %Int.as.ImplicitAs.impl.Convert.specific_fn: = specific_function %Int.as.ImplicitAs.impl.Convert.dd4, @Int.as.ImplicitAs.impl.Convert(%int_32) [concrete] // CHECK:STDOUT: %bound_method: = bound_method %N.5de, %Int.as.ImplicitAs.impl.Convert.specific_fn [symbolic] // CHECK:STDOUT: %Int.as.ImplicitAs.impl.Convert.call: init Core.IntLiteral = call %bound_method(%N.5de) [symbolic] // CHECK:STDOUT: %array_type.2ec: type = array_type %Int.as.ImplicitAs.impl.Convert.call, %i32 [symbolic] // CHECK:STDOUT: %require_complete.5d1: = require_complete_type %array_type.2ec [symbolic] // CHECK:STDOUT: %pattern_type.99c: type = pattern_type %array_type.2ec [symbolic] // CHECK:STDOUT: %.d6f: require_specific_def_type = require_specific_def @T.as.DefaultOrUnformed.impl(%array_type.2ec) [symbolic] // CHECK:STDOUT: %DefaultOrUnformed.lookup_impl_witness.162: = lookup_impl_witness %array_type.2ec, @DefaultOrUnformed [symbolic] // CHECK:STDOUT: %DefaultOrUnformed.facet.0c7: %DefaultOrUnformed.type = facet_value %array_type.2ec, (%DefaultOrUnformed.lookup_impl_witness.162) [symbolic] // CHECK:STDOUT: %DefaultOrUnformed.WithSelf.Op.type.839: type = fn_type @DefaultOrUnformed.WithSelf.Op, @DefaultOrUnformed.WithSelf(%DefaultOrUnformed.facet.0c7) [symbolic] // CHECK:STDOUT: %.78e: type = fn_type_with_self_type %DefaultOrUnformed.WithSelf.Op.type.839, %DefaultOrUnformed.facet.0c7 [symbolic] // CHECK:STDOUT: %impl.elem0.82a: %.78e = impl_witness_access %DefaultOrUnformed.lookup_impl_witness.162, element0 [symbolic] // CHECK:STDOUT: %specific_impl_fn.fcf: = specific_impl_function %impl.elem0.82a, @DefaultOrUnformed.WithSelf.Op(%DefaultOrUnformed.facet.0c7) [symbolic] // CHECK:STDOUT: %Destroy.lookup_impl_witness.f5c: = lookup_impl_witness %array_type.2ec, @Destroy [symbolic] // CHECK:STDOUT: %Destroy.facet.d1e: %Destroy.type = facet_value %array_type.2ec, (%Destroy.lookup_impl_witness.f5c) [symbolic] // CHECK:STDOUT: %Destroy.WithSelf.Op.type.ff9: type = fn_type @Destroy.WithSelf.Op, @Destroy.WithSelf(%Destroy.facet.d1e) [symbolic] // CHECK:STDOUT: %.63e: type = fn_type_with_self_type %Destroy.WithSelf.Op.type.ff9, %Destroy.facet.d1e [symbolic] // CHECK:STDOUT: %impl.elem0.2bf: %.63e = impl_witness_access %Destroy.lookup_impl_witness.f5c, element0 [symbolic] // CHECK:STDOUT: %specific_impl_fn.0a5: = specific_impl_function %impl.elem0.2bf, @Destroy.WithSelf.Op(%Destroy.facet.d1e) [symbolic] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core.import_ref.0bc: @Int.as.ImplicitAs.impl.%Int.as.ImplicitAs.impl.Convert.type (%Int.as.ImplicitAs.impl.Convert.type.2ed) = import_ref Core//prelude/parts/int, loc{{\d+_\d+}}, loaded [symbolic = @Int.as.ImplicitAs.impl.%Int.as.ImplicitAs.impl.Convert (constants.%Int.as.ImplicitAs.impl.Convert.d29)] // CHECK:STDOUT: %ImplicitAs.impl_witness_table.ea2 = impl_witness_table (%Core.import_ref.0bc), @Int.as.ImplicitAs.impl [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic fn @F(%T.loc4_6.2: type) { // CHECK:STDOUT: // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %ptr.loc6_19.2: type = ptr_type %T.loc4_6.1 [symbolic = %ptr.loc6_19.2 (constants.%ptr.e8f)] // CHECK:STDOUT: %const.loc6_22.2: type = const_type %T.loc4_6.1 [symbolic = %const.loc6_22.2 (constants.%const)] // CHECK:STDOUT: %tuple: %tuple.type.24b = tuple_value (%ptr.loc6_19.2, %const.loc6_22.2) [symbolic = %tuple (constants.%tuple)] // CHECK:STDOUT: %tuple.type: type = tuple_type (%ptr.loc6_19.2, %const.loc6_22.2) [symbolic = %tuple.type (constants.%tuple.type.3c8)] // CHECK:STDOUT: %require_complete.loc6: = require_complete_type %tuple.type [symbolic = %require_complete.loc6 (constants.%require_complete.666)] // CHECK:STDOUT: %pattern_type.loc6: type = pattern_type %tuple.type [symbolic = %pattern_type.loc6 (constants.%pattern_type.4ac)] // CHECK:STDOUT: %.loc6_30.3: require_specific_def_type = require_specific_def @T.as.DefaultOrUnformed.impl(%tuple.type) [symbolic = %.loc6_30.3 (constants.%.f7e)] // CHECK:STDOUT: %DefaultOrUnformed.lookup_impl_witness.loc6: = lookup_impl_witness %tuple.type, @DefaultOrUnformed [symbolic = %DefaultOrUnformed.lookup_impl_witness.loc6 (constants.%DefaultOrUnformed.lookup_impl_witness.2ad)] // CHECK:STDOUT: %DefaultOrUnformed.facet.loc6_30.2: %DefaultOrUnformed.type = facet_value %tuple.type, (%DefaultOrUnformed.lookup_impl_witness.loc6) [symbolic = %DefaultOrUnformed.facet.loc6_30.2 (constants.%DefaultOrUnformed.facet.9a0)] // CHECK:STDOUT: %DefaultOrUnformed.WithSelf.Op.type.loc6: type = fn_type @DefaultOrUnformed.WithSelf.Op, @DefaultOrUnformed.WithSelf(%DefaultOrUnformed.facet.loc6_30.2) [symbolic = %DefaultOrUnformed.WithSelf.Op.type.loc6 (constants.%DefaultOrUnformed.WithSelf.Op.type.0af)] // CHECK:STDOUT: %.loc6_30.4: type = fn_type_with_self_type %DefaultOrUnformed.WithSelf.Op.type.loc6, %DefaultOrUnformed.facet.loc6_30.2 [symbolic = %.loc6_30.4 (constants.%.246)] // CHECK:STDOUT: %impl.elem0.loc6_30.2: @F.%.loc6_30.4 (%.246) = impl_witness_access %DefaultOrUnformed.lookup_impl_witness.loc6, element0 [symbolic = %impl.elem0.loc6_30.2 (constants.%impl.elem0.c6f)] // CHECK:STDOUT: %specific_impl_fn.loc6_30.2: = specific_impl_function %impl.elem0.loc6_30.2, @DefaultOrUnformed.WithSelf.Op(%DefaultOrUnformed.facet.loc6_30.2) [symbolic = %specific_impl_fn.loc6_30.2 (constants.%specific_impl_fn.58b)] // CHECK:STDOUT: %struct_type.a.loc7_23.2: type = struct_type {.a: @F.%T.loc4_6.1 (%T)} [symbolic = %struct_type.a.loc7_23.2 (constants.%struct_type.a)] // CHECK:STDOUT: %require_complete.loc7: = require_complete_type %struct_type.a.loc7_23.2 [symbolic = %require_complete.loc7 (constants.%require_complete.5d6)] // CHECK:STDOUT: %pattern_type.loc7: type = pattern_type %struct_type.a.loc7_23.2 [symbolic = %pattern_type.loc7 (constants.%pattern_type.7b9)] // CHECK:STDOUT: %.loc7_24.3: require_specific_def_type = require_specific_def @T.as.DefaultOrUnformed.impl(%struct_type.a.loc7_23.2) [symbolic = %.loc7_24.3 (constants.%.d26)] // CHECK:STDOUT: %DefaultOrUnformed.lookup_impl_witness.loc7: = lookup_impl_witness %struct_type.a.loc7_23.2, @DefaultOrUnformed [symbolic = %DefaultOrUnformed.lookup_impl_witness.loc7 (constants.%DefaultOrUnformed.lookup_impl_witness.169)] // CHECK:STDOUT: %DefaultOrUnformed.facet.loc7_24.2: %DefaultOrUnformed.type = facet_value %struct_type.a.loc7_23.2, (%DefaultOrUnformed.lookup_impl_witness.loc7) [symbolic = %DefaultOrUnformed.facet.loc7_24.2 (constants.%DefaultOrUnformed.facet.ca5)] // CHECK:STDOUT: %DefaultOrUnformed.WithSelf.Op.type.loc7: type = fn_type @DefaultOrUnformed.WithSelf.Op, @DefaultOrUnformed.WithSelf(%DefaultOrUnformed.facet.loc7_24.2) [symbolic = %DefaultOrUnformed.WithSelf.Op.type.loc7 (constants.%DefaultOrUnformed.WithSelf.Op.type.0c5)] // CHECK:STDOUT: %.loc7_24.4: type = fn_type_with_self_type %DefaultOrUnformed.WithSelf.Op.type.loc7, %DefaultOrUnformed.facet.loc7_24.2 [symbolic = %.loc7_24.4 (constants.%.9ac)] // CHECK:STDOUT: %impl.elem0.loc7_24.2: @F.%.loc7_24.4 (%.9ac) = impl_witness_access %DefaultOrUnformed.lookup_impl_witness.loc7, element0 [symbolic = %impl.elem0.loc7_24.2 (constants.%impl.elem0.e9c)] // CHECK:STDOUT: %specific_impl_fn.loc7_24.2: = specific_impl_function %impl.elem0.loc7_24.2, @DefaultOrUnformed.WithSelf.Op(%DefaultOrUnformed.facet.loc7_24.2) [symbolic = %specific_impl_fn.loc7_24.2 (constants.%specific_impl_fn.cf6)] // CHECK:STDOUT: %array_type.loc8_27.2: type = array_type constants.%int_5, %T.loc4_6.1 [symbolic = %array_type.loc8_27.2 (constants.%array_type.742)] // CHECK:STDOUT: %require_complete.loc8: = require_complete_type %array_type.loc8_27.2 [symbolic = %require_complete.loc8 (constants.%require_complete.345)] // CHECK:STDOUT: %pattern_type.loc8: type = pattern_type %array_type.loc8_27.2 [symbolic = %pattern_type.loc8 (constants.%pattern_type.d52)] // CHECK:STDOUT: %.loc8_28.3: require_specific_def_type = require_specific_def @T.as.DefaultOrUnformed.impl(%array_type.loc8_27.2) [symbolic = %.loc8_28.3 (constants.%.617)] // CHECK:STDOUT: %DefaultOrUnformed.lookup_impl_witness.loc8: = lookup_impl_witness %array_type.loc8_27.2, @DefaultOrUnformed [symbolic = %DefaultOrUnformed.lookup_impl_witness.loc8 (constants.%DefaultOrUnformed.lookup_impl_witness.945)] // CHECK:STDOUT: %DefaultOrUnformed.facet.loc8_28.2: %DefaultOrUnformed.type = facet_value %array_type.loc8_27.2, (%DefaultOrUnformed.lookup_impl_witness.loc8) [symbolic = %DefaultOrUnformed.facet.loc8_28.2 (constants.%DefaultOrUnformed.facet.ad6)] // CHECK:STDOUT: %DefaultOrUnformed.WithSelf.Op.type.loc8: type = fn_type @DefaultOrUnformed.WithSelf.Op, @DefaultOrUnformed.WithSelf(%DefaultOrUnformed.facet.loc8_28.2) [symbolic = %DefaultOrUnformed.WithSelf.Op.type.loc8 (constants.%DefaultOrUnformed.WithSelf.Op.type.2f0)] // CHECK:STDOUT: %.loc8_28.4: type = fn_type_with_self_type %DefaultOrUnformed.WithSelf.Op.type.loc8, %DefaultOrUnformed.facet.loc8_28.2 [symbolic = %.loc8_28.4 (constants.%.544)] // CHECK:STDOUT: %impl.elem0.loc8_28.2: @F.%.loc8_28.4 (%.544) = impl_witness_access %DefaultOrUnformed.lookup_impl_witness.loc8, element0 [symbolic = %impl.elem0.loc8_28.2 (constants.%impl.elem0.97e)] // CHECK:STDOUT: %specific_impl_fn.loc8_28.2: = specific_impl_function %impl.elem0.loc8_28.2, @DefaultOrUnformed.WithSelf.Op(%DefaultOrUnformed.facet.loc8_28.2) [symbolic = %specific_impl_fn.loc8_28.2 (constants.%specific_impl_fn.261)] // CHECK:STDOUT: %Destroy.lookup_impl_witness.loc8: = lookup_impl_witness %array_type.loc8_27.2, @Destroy [symbolic = %Destroy.lookup_impl_witness.loc8 (constants.%Destroy.lookup_impl_witness.b29)] // CHECK:STDOUT: %Destroy.facet.loc8: %Destroy.type = facet_value %array_type.loc8_27.2, (%Destroy.lookup_impl_witness.loc8) [symbolic = %Destroy.facet.loc8 (constants.%Destroy.facet.ddc)] // CHECK:STDOUT: %Destroy.WithSelf.Op.type.loc8: type = fn_type @Destroy.WithSelf.Op, @Destroy.WithSelf(%Destroy.facet.loc8) [symbolic = %Destroy.WithSelf.Op.type.loc8 (constants.%Destroy.WithSelf.Op.type.3c6)] // CHECK:STDOUT: %.loc8_3.2: type = fn_type_with_self_type %Destroy.WithSelf.Op.type.loc8, %Destroy.facet.loc8 [symbolic = %.loc8_3.2 (constants.%.950)] // CHECK:STDOUT: %impl.elem0.loc8_3.2: @F.%.loc8_3.2 (%.950) = impl_witness_access %Destroy.lookup_impl_witness.loc8, element0 [symbolic = %impl.elem0.loc8_3.2 (constants.%impl.elem0.fa5)] // CHECK:STDOUT: %specific_impl_fn.loc8_3.2: = specific_impl_function %impl.elem0.loc8_3.2, @Destroy.WithSelf.Op(%Destroy.facet.loc8) [symbolic = %specific_impl_fn.loc8_3.2 (constants.%specific_impl_fn.ede)] // CHECK:STDOUT: %Destroy.lookup_impl_witness.loc7: = lookup_impl_witness %struct_type.a.loc7_23.2, @Destroy [symbolic = %Destroy.lookup_impl_witness.loc7 (constants.%Destroy.lookup_impl_witness.bed)] // CHECK:STDOUT: %Destroy.facet.loc7: %Destroy.type = facet_value %struct_type.a.loc7_23.2, (%Destroy.lookup_impl_witness.loc7) [symbolic = %Destroy.facet.loc7 (constants.%Destroy.facet.d21)] // CHECK:STDOUT: %Destroy.WithSelf.Op.type.loc7: type = fn_type @Destroy.WithSelf.Op, @Destroy.WithSelf(%Destroy.facet.loc7) [symbolic = %Destroy.WithSelf.Op.type.loc7 (constants.%Destroy.WithSelf.Op.type.873)] // CHECK:STDOUT: %.loc7_3: type = fn_type_with_self_type %Destroy.WithSelf.Op.type.loc7, %Destroy.facet.loc7 [symbolic = %.loc7_3 (constants.%.511)] // CHECK:STDOUT: %impl.elem0.loc7_3.2: @F.%.loc7_3 (%.511) = impl_witness_access %Destroy.lookup_impl_witness.loc7, element0 [symbolic = %impl.elem0.loc7_3.2 (constants.%impl.elem0.12c)] // CHECK:STDOUT: %specific_impl_fn.loc7_3.2: = specific_impl_function %impl.elem0.loc7_3.2, @Destroy.WithSelf.Op(%Destroy.facet.loc7) [symbolic = %specific_impl_fn.loc7_3.2 (constants.%specific_impl_fn.c3a)] // CHECK:STDOUT: %Destroy.lookup_impl_witness.loc6: = lookup_impl_witness %tuple.type, @Destroy [symbolic = %Destroy.lookup_impl_witness.loc6 (constants.%Destroy.lookup_impl_witness.7eb)] // CHECK:STDOUT: %Destroy.facet.loc6: %Destroy.type = facet_value %tuple.type, (%Destroy.lookup_impl_witness.loc6) [symbolic = %Destroy.facet.loc6 (constants.%Destroy.facet.66d)] // CHECK:STDOUT: %Destroy.WithSelf.Op.type.loc6: type = fn_type @Destroy.WithSelf.Op, @Destroy.WithSelf(%Destroy.facet.loc6) [symbolic = %Destroy.WithSelf.Op.type.loc6 (constants.%Destroy.WithSelf.Op.type.f23)] // CHECK:STDOUT: %.loc6_3.2: type = fn_type_with_self_type %Destroy.WithSelf.Op.type.loc6, %Destroy.facet.loc6 [symbolic = %.loc6_3.2 (constants.%.f20)] // CHECK:STDOUT: %impl.elem0.loc6_3.2: @F.%.loc6_3.2 (%.f20) = impl_witness_access %Destroy.lookup_impl_witness.loc6, element0 [symbolic = %impl.elem0.loc6_3.2 (constants.%impl.elem0.f5f)] // CHECK:STDOUT: %specific_impl_fn.loc6_3.2: = specific_impl_function %impl.elem0.loc6_3.2, @Destroy.WithSelf.Op(%Destroy.facet.loc6) [symbolic = %specific_impl_fn.loc6_3.2 (constants.%specific_impl_fn.87c)] // CHECK:STDOUT: // CHECK:STDOUT: fn() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %u.patt: @F.%pattern_type.loc6 (%pattern_type.4ac) = ref_binding_pattern u [concrete] // CHECK:STDOUT: %u.var_patt: @F.%pattern_type.loc6 (%pattern_type.4ac) = var_pattern %u.patt [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %u.var: ref @F.%tuple.type (%tuple.type.3c8) = var %u.var_patt // CHECK:STDOUT: %DefaultOrUnformed.facet.loc6_30.1: %DefaultOrUnformed.type = facet_value constants.%tuple.type.3c8, (constants.%DefaultOrUnformed.lookup_impl_witness.2ad) [symbolic = %DefaultOrUnformed.facet.loc6_30.2 (constants.%DefaultOrUnformed.facet.9a0)] // CHECK:STDOUT: %.loc6_30.1: %DefaultOrUnformed.type = converted constants.%tuple.type.3c8, %DefaultOrUnformed.facet.loc6_30.1 [symbolic = %DefaultOrUnformed.facet.loc6_30.2 (constants.%DefaultOrUnformed.facet.9a0)] // CHECK:STDOUT: %as_type.loc6: type = facet_access_type %.loc6_30.1 [symbolic = %tuple.type (constants.%tuple.type.3c8)] // CHECK:STDOUT: %.loc6_30.2: type = converted %.loc6_30.1, %as_type.loc6 [symbolic = %tuple.type (constants.%tuple.type.3c8)] // CHECK:STDOUT: %impl.elem0.loc6_30.1: @F.%.loc6_30.4 (%.246) = impl_witness_access constants.%DefaultOrUnformed.lookup_impl_witness.2ad, element0 [symbolic = %impl.elem0.loc6_30.2 (constants.%impl.elem0.c6f)] // CHECK:STDOUT: %specific_impl_fn.loc6_30.1: = specific_impl_function %impl.elem0.loc6_30.1, @DefaultOrUnformed.WithSelf.Op(constants.%DefaultOrUnformed.facet.9a0) [symbolic = %specific_impl_fn.loc6_30.2 (constants.%specific_impl_fn.58b)] // CHECK:STDOUT: %.loc6_3.1: ref @F.%tuple.type (%tuple.type.3c8) = splice_block %u.var {} // CHECK:STDOUT: %DefaultOrUnformed.WithSelf.Op.call.loc6: init @F.%tuple.type (%tuple.type.3c8) to %.loc6_3.1 = call %specific_impl_fn.loc6_30.1() // CHECK:STDOUT: assign %u.var, %DefaultOrUnformed.WithSelf.Op.call.loc6 // CHECK:STDOUT: %.loc6_29.1: type = splice_block %.loc6_29.3 [symbolic = %tuple.type (constants.%tuple.type.3c8)] { // CHECK:STDOUT: %T.ref.loc6_18: type = name_ref T, %T.loc4_6.2 [symbolic = %T.loc4_6.1 (constants.%T)] // CHECK:STDOUT: %ptr.loc6_19.1: type = ptr_type %T.ref.loc6_18 [symbolic = %ptr.loc6_19.2 (constants.%ptr.e8f)] // CHECK:STDOUT: %T.ref.loc6_28: type = name_ref T, %T.loc4_6.2 [symbolic = %T.loc4_6.1 (constants.%T)] // CHECK:STDOUT: %const.loc6_22.1: type = const_type %T.ref.loc6_28 [symbolic = %const.loc6_22.2 (constants.%const)] // CHECK:STDOUT: %.loc6_29.2: %tuple.type.24b = tuple_literal (%ptr.loc6_19.1, %const.loc6_22.1) [symbolic = %tuple (constants.%tuple)] // CHECK:STDOUT: %.loc6_29.3: type = converted %.loc6_29.2, constants.%tuple.type.3c8 [symbolic = %tuple.type (constants.%tuple.type.3c8)] // CHECK:STDOUT: } // CHECK:STDOUT: %u: ref @F.%tuple.type (%tuple.type.3c8) = ref_binding u, %u.var // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %v.patt: @F.%pattern_type.loc7 (%pattern_type.7b9) = ref_binding_pattern v [concrete] // CHECK:STDOUT: %v.var_patt: @F.%pattern_type.loc7 (%pattern_type.7b9) = var_pattern %v.patt [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %v.var: ref @F.%struct_type.a.loc7_23.2 (%struct_type.a) = var %v.var_patt // CHECK:STDOUT: %DefaultOrUnformed.facet.loc7_24.1: %DefaultOrUnformed.type = facet_value constants.%struct_type.a, (constants.%DefaultOrUnformed.lookup_impl_witness.169) [symbolic = %DefaultOrUnformed.facet.loc7_24.2 (constants.%DefaultOrUnformed.facet.ca5)] // CHECK:STDOUT: %.loc7_24.1: %DefaultOrUnformed.type = converted constants.%struct_type.a, %DefaultOrUnformed.facet.loc7_24.1 [symbolic = %DefaultOrUnformed.facet.loc7_24.2 (constants.%DefaultOrUnformed.facet.ca5)] // CHECK:STDOUT: %as_type.loc7: type = facet_access_type %.loc7_24.1 [symbolic = %struct_type.a.loc7_23.2 (constants.%struct_type.a)] // CHECK:STDOUT: %.loc7_24.2: type = converted %.loc7_24.1, %as_type.loc7 [symbolic = %struct_type.a.loc7_23.2 (constants.%struct_type.a)] // CHECK:STDOUT: %impl.elem0.loc7_24.1: @F.%.loc7_24.4 (%.9ac) = impl_witness_access constants.%DefaultOrUnformed.lookup_impl_witness.169, element0 [symbolic = %impl.elem0.loc7_24.2 (constants.%impl.elem0.e9c)] // CHECK:STDOUT: %specific_impl_fn.loc7_24.1: = specific_impl_function %impl.elem0.loc7_24.1, @DefaultOrUnformed.WithSelf.Op(constants.%DefaultOrUnformed.facet.ca5) [symbolic = %specific_impl_fn.loc7_24.2 (constants.%specific_impl_fn.cf6)] // CHECK:STDOUT: %DefaultOrUnformed.WithSelf.Op.call.loc7: init @F.%struct_type.a.loc7_23.2 (%struct_type.a) = call %specific_impl_fn.loc7_24.1() // CHECK:STDOUT: assign %v.var, %DefaultOrUnformed.WithSelf.Op.call.loc7 // CHECK:STDOUT: %.loc7_23: type = splice_block %struct_type.a.loc7_23.1 [symbolic = %struct_type.a.loc7_23.2 (constants.%struct_type.a)] { // CHECK:STDOUT: %T.ref.loc7: type = name_ref T, %T.loc4_6.2 [symbolic = %T.loc4_6.1 (constants.%T)] // CHECK:STDOUT: %struct_type.a.loc7_23.1: type = struct_type {.a: @F.%T.loc4_6.1 (%T)} [symbolic = %struct_type.a.loc7_23.2 (constants.%struct_type.a)] // CHECK:STDOUT: } // CHECK:STDOUT: %v: ref @F.%struct_type.a.loc7_23.2 (%struct_type.a) = ref_binding v, %v.var // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %w.patt: @F.%pattern_type.loc8 (%pattern_type.d52) = ref_binding_pattern w [concrete] // CHECK:STDOUT: %w.var_patt: @F.%pattern_type.loc8 (%pattern_type.d52) = var_pattern %w.patt [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %w.var: ref @F.%array_type.loc8_27.2 (%array_type.742) = var %w.var_patt // CHECK:STDOUT: %DefaultOrUnformed.facet.loc8_28.1: %DefaultOrUnformed.type = facet_value constants.%array_type.742, (constants.%DefaultOrUnformed.lookup_impl_witness.945) [symbolic = %DefaultOrUnformed.facet.loc8_28.2 (constants.%DefaultOrUnformed.facet.ad6)] // CHECK:STDOUT: %.loc8_28.1: %DefaultOrUnformed.type = converted constants.%array_type.742, %DefaultOrUnformed.facet.loc8_28.1 [symbolic = %DefaultOrUnformed.facet.loc8_28.2 (constants.%DefaultOrUnformed.facet.ad6)] // CHECK:STDOUT: %as_type.loc8: type = facet_access_type %.loc8_28.1 [symbolic = %array_type.loc8_27.2 (constants.%array_type.742)] // CHECK:STDOUT: %.loc8_28.2: type = converted %.loc8_28.1, %as_type.loc8 [symbolic = %array_type.loc8_27.2 (constants.%array_type.742)] // CHECK:STDOUT: %impl.elem0.loc8_28.1: @F.%.loc8_28.4 (%.544) = impl_witness_access constants.%DefaultOrUnformed.lookup_impl_witness.945, element0 [symbolic = %impl.elem0.loc8_28.2 (constants.%impl.elem0.97e)] // CHECK:STDOUT: %specific_impl_fn.loc8_28.1: = specific_impl_function %impl.elem0.loc8_28.1, @DefaultOrUnformed.WithSelf.Op(constants.%DefaultOrUnformed.facet.ad6) [symbolic = %specific_impl_fn.loc8_28.2 (constants.%specific_impl_fn.261)] // CHECK:STDOUT: %.loc8_3.1: ref @F.%array_type.loc8_27.2 (%array_type.742) = splice_block %w.var {} // CHECK:STDOUT: %DefaultOrUnformed.WithSelf.Op.call.loc8: init @F.%array_type.loc8_27.2 (%array_type.742) to %.loc8_3.1 = call %specific_impl_fn.loc8_28.1() // CHECK:STDOUT: assign %w.var, %DefaultOrUnformed.WithSelf.Op.call.loc8 // CHECK:STDOUT: %.loc8_27: type = splice_block %array_type.loc8_27.1 [symbolic = %array_type.loc8_27.2 (constants.%array_type.742)] { // CHECK:STDOUT: %T.ref.loc8: type = name_ref T, %T.loc4_6.2 [symbolic = %T.loc4_6.1 (constants.%T)] // CHECK:STDOUT: %int_5: Core.IntLiteral = int_value 5 [concrete = constants.%int_5] // CHECK:STDOUT: %array_type.loc8_27.1: type = array_type %int_5, %T.ref.loc8 [symbolic = %array_type.loc8_27.2 (constants.%array_type.742)] // CHECK:STDOUT: } // CHECK:STDOUT: %w: ref @F.%array_type.loc8_27.2 (%array_type.742) = ref_binding w, %w.var // CHECK:STDOUT: %impl.elem0.loc8_3.1: @F.%.loc8_3.2 (%.950) = impl_witness_access constants.%Destroy.lookup_impl_witness.b29, element0 [symbolic = %impl.elem0.loc8_3.2 (constants.%impl.elem0.fa5)] // CHECK:STDOUT: %bound_method.loc8_3.1: = bound_method %w.var, %impl.elem0.loc8_3.1 // CHECK:STDOUT: %specific_impl_fn.loc8_3.1: = specific_impl_function %impl.elem0.loc8_3.1, @Destroy.WithSelf.Op(constants.%Destroy.facet.ddc) [symbolic = %specific_impl_fn.loc8_3.2 (constants.%specific_impl_fn.ede)] // CHECK:STDOUT: %bound_method.loc8_3.2: = bound_method %w.var, %specific_impl_fn.loc8_3.1 // CHECK:STDOUT: %Destroy.WithSelf.Op.call.loc8: init %empty_tuple.type = call %bound_method.loc8_3.2(%w.var) // CHECK:STDOUT: %impl.elem0.loc7_3.1: @F.%.loc7_3 (%.511) = impl_witness_access constants.%Destroy.lookup_impl_witness.bed, element0 [symbolic = %impl.elem0.loc7_3.2 (constants.%impl.elem0.12c)] // CHECK:STDOUT: %bound_method.loc7_3.1: = bound_method %v.var, %impl.elem0.loc7_3.1 // CHECK:STDOUT: %specific_impl_fn.loc7_3.1: = specific_impl_function %impl.elem0.loc7_3.1, @Destroy.WithSelf.Op(constants.%Destroy.facet.d21) [symbolic = %specific_impl_fn.loc7_3.2 (constants.%specific_impl_fn.c3a)] // CHECK:STDOUT: %bound_method.loc7_3.2: = bound_method %v.var, %specific_impl_fn.loc7_3.1 // CHECK:STDOUT: %Destroy.WithSelf.Op.call.loc7: init %empty_tuple.type = call %bound_method.loc7_3.2(%v.var) // CHECK:STDOUT: %impl.elem0.loc6_3.1: @F.%.loc6_3.2 (%.f20) = impl_witness_access constants.%Destroy.lookup_impl_witness.7eb, element0 [symbolic = %impl.elem0.loc6_3.2 (constants.%impl.elem0.f5f)] // CHECK:STDOUT: %bound_method.loc6_3.1: = bound_method %u.var, %impl.elem0.loc6_3.1 // CHECK:STDOUT: %specific_impl_fn.loc6_3.1: = specific_impl_function %impl.elem0.loc6_3.1, @Destroy.WithSelf.Op(constants.%Destroy.facet.66d) [symbolic = %specific_impl_fn.loc6_3.2 (constants.%specific_impl_fn.87c)] // CHECK:STDOUT: %bound_method.loc6_3.2: = bound_method %u.var, %specific_impl_fn.loc6_3.1 // CHECK:STDOUT: %Destroy.WithSelf.Op.call.loc6: init %empty_tuple.type = call %bound_method.loc6_3.2(%u.var) // CHECK:STDOUT: // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic fn @G(%N.loc12_6.2: %i32) { // CHECK:STDOUT: // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %Int.as.ImplicitAs.impl.Convert.bound: = bound_method %N.loc12_6.1, constants.%Int.as.ImplicitAs.impl.Convert.dd4 [symbolic = %Int.as.ImplicitAs.impl.Convert.bound (constants.%Int.as.ImplicitAs.impl.Convert.bound)] // CHECK:STDOUT: %bound_method.loc14_28.3: = bound_method %N.loc12_6.1, constants.%Int.as.ImplicitAs.impl.Convert.specific_fn [symbolic = %bound_method.loc14_28.3 (constants.%bound_method)] // CHECK:STDOUT: %Int.as.ImplicitAs.impl.Convert.call.loc14_28.2: init Core.IntLiteral = call %bound_method.loc14_28.3(%N.loc12_6.1) [symbolic = %Int.as.ImplicitAs.impl.Convert.call.loc14_28.2 (constants.%Int.as.ImplicitAs.impl.Convert.call)] // CHECK:STDOUT: %array_type.loc14_29.2: type = array_type %Int.as.ImplicitAs.impl.Convert.call.loc14_28.2, constants.%i32 [symbolic = %array_type.loc14_29.2 (constants.%array_type.2ec)] // CHECK:STDOUT: %require_complete: = require_complete_type %array_type.loc14_29.2 [symbolic = %require_complete (constants.%require_complete.5d1)] // CHECK:STDOUT: %pattern_type: type = pattern_type %array_type.loc14_29.2 [symbolic = %pattern_type (constants.%pattern_type.99c)] // CHECK:STDOUT: %.loc14_30.3: require_specific_def_type = require_specific_def @T.as.DefaultOrUnformed.impl(%array_type.loc14_29.2) [symbolic = %.loc14_30.3 (constants.%.d6f)] // CHECK:STDOUT: %DefaultOrUnformed.lookup_impl_witness: = lookup_impl_witness %array_type.loc14_29.2, @DefaultOrUnformed [symbolic = %DefaultOrUnformed.lookup_impl_witness (constants.%DefaultOrUnformed.lookup_impl_witness.162)] // CHECK:STDOUT: %DefaultOrUnformed.facet.loc14_30.2: %DefaultOrUnformed.type = facet_value %array_type.loc14_29.2, (%DefaultOrUnformed.lookup_impl_witness) [symbolic = %DefaultOrUnformed.facet.loc14_30.2 (constants.%DefaultOrUnformed.facet.0c7)] // CHECK:STDOUT: %DefaultOrUnformed.WithSelf.Op.type: type = fn_type @DefaultOrUnformed.WithSelf.Op, @DefaultOrUnformed.WithSelf(%DefaultOrUnformed.facet.loc14_30.2) [symbolic = %DefaultOrUnformed.WithSelf.Op.type (constants.%DefaultOrUnformed.WithSelf.Op.type.839)] // CHECK:STDOUT: %.loc14_30.4: type = fn_type_with_self_type %DefaultOrUnformed.WithSelf.Op.type, %DefaultOrUnformed.facet.loc14_30.2 [symbolic = %.loc14_30.4 (constants.%.78e)] // CHECK:STDOUT: %impl.elem0.loc14_30.2: @G.%.loc14_30.4 (%.78e) = impl_witness_access %DefaultOrUnformed.lookup_impl_witness, element0 [symbolic = %impl.elem0.loc14_30.2 (constants.%impl.elem0.82a)] // CHECK:STDOUT: %specific_impl_fn.loc14_30.2: = specific_impl_function %impl.elem0.loc14_30.2, @DefaultOrUnformed.WithSelf.Op(%DefaultOrUnformed.facet.loc14_30.2) [symbolic = %specific_impl_fn.loc14_30.2 (constants.%specific_impl_fn.fcf)] // CHECK:STDOUT: %Destroy.lookup_impl_witness: = lookup_impl_witness %array_type.loc14_29.2, @Destroy [symbolic = %Destroy.lookup_impl_witness (constants.%Destroy.lookup_impl_witness.f5c)] // CHECK:STDOUT: %Destroy.facet: %Destroy.type = facet_value %array_type.loc14_29.2, (%Destroy.lookup_impl_witness) [symbolic = %Destroy.facet (constants.%Destroy.facet.d1e)] // CHECK:STDOUT: %Destroy.WithSelf.Op.type: type = fn_type @Destroy.WithSelf.Op, @Destroy.WithSelf(%Destroy.facet) [symbolic = %Destroy.WithSelf.Op.type (constants.%Destroy.WithSelf.Op.type.ff9)] // CHECK:STDOUT: %.loc14_3.2: type = fn_type_with_self_type %Destroy.WithSelf.Op.type, %Destroy.facet [symbolic = %.loc14_3.2 (constants.%.63e)] // CHECK:STDOUT: %impl.elem0.loc14_3.2: @G.%.loc14_3.2 (%.63e) = impl_witness_access %Destroy.lookup_impl_witness, element0 [symbolic = %impl.elem0.loc14_3.2 (constants.%impl.elem0.2bf)] // CHECK:STDOUT: %specific_impl_fn.loc14_3.2: = specific_impl_function %impl.elem0.loc14_3.2, @Destroy.WithSelf.Op(%Destroy.facet) [symbolic = %specific_impl_fn.loc14_3.2 (constants.%specific_impl_fn.0a5)] // CHECK:STDOUT: // CHECK:STDOUT: fn() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %k.patt: @G.%pattern_type (%pattern_type.99c) = ref_binding_pattern k [concrete] // CHECK:STDOUT: %k.var_patt: @G.%pattern_type (%pattern_type.99c) = var_pattern %k.patt [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %k.var: ref @G.%array_type.loc14_29.2 (%array_type.2ec) = var %k.var_patt // CHECK:STDOUT: %DefaultOrUnformed.facet.loc14_30.1: %DefaultOrUnformed.type = facet_value constants.%array_type.2ec, (constants.%DefaultOrUnformed.lookup_impl_witness.162) [symbolic = %DefaultOrUnformed.facet.loc14_30.2 (constants.%DefaultOrUnformed.facet.0c7)] // CHECK:STDOUT: %.loc14_30.1: %DefaultOrUnformed.type = converted constants.%array_type.2ec, %DefaultOrUnformed.facet.loc14_30.1 [symbolic = %DefaultOrUnformed.facet.loc14_30.2 (constants.%DefaultOrUnformed.facet.0c7)] // CHECK:STDOUT: %as_type: type = facet_access_type %.loc14_30.1 [symbolic = %array_type.loc14_29.2 (constants.%array_type.2ec)] // CHECK:STDOUT: %.loc14_30.2: type = converted %.loc14_30.1, %as_type [symbolic = %array_type.loc14_29.2 (constants.%array_type.2ec)] // CHECK:STDOUT: %impl.elem0.loc14_30.1: @G.%.loc14_30.4 (%.78e) = impl_witness_access constants.%DefaultOrUnformed.lookup_impl_witness.162, element0 [symbolic = %impl.elem0.loc14_30.2 (constants.%impl.elem0.82a)] // CHECK:STDOUT: %specific_impl_fn.loc14_30.1: = specific_impl_function %impl.elem0.loc14_30.1, @DefaultOrUnformed.WithSelf.Op(constants.%DefaultOrUnformed.facet.0c7) [symbolic = %specific_impl_fn.loc14_30.2 (constants.%specific_impl_fn.fcf)] // CHECK:STDOUT: %.loc14_3.1: ref @G.%array_type.loc14_29.2 (%array_type.2ec) = splice_block %k.var {} // CHECK:STDOUT: %DefaultOrUnformed.WithSelf.Op.call: init @G.%array_type.loc14_29.2 (%array_type.2ec) to %.loc14_3.1 = call %specific_impl_fn.loc14_30.1() // CHECK:STDOUT: assign %k.var, %DefaultOrUnformed.WithSelf.Op.call // CHECK:STDOUT: %.loc14_29: type = splice_block %array_type.loc14_29.1 [symbolic = %array_type.loc14_29.2 (constants.%array_type.2ec)] { // CHECK:STDOUT: %i32.loc14: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %N.ref: %i32 = name_ref N, %N.loc12_6.2 [symbolic = %N.loc12_6.1 (constants.%N.5de)] // CHECK:STDOUT: %impl.elem0.loc14_28: %.0a7 = impl_witness_access constants.%ImplicitAs.impl_witness.640, element0 [concrete = constants.%Int.as.ImplicitAs.impl.Convert.dd4] // CHECK:STDOUT: %bound_method.loc14_28.1: = bound_method %N.ref, %impl.elem0.loc14_28 [symbolic = %Int.as.ImplicitAs.impl.Convert.bound (constants.%Int.as.ImplicitAs.impl.Convert.bound)] // CHECK:STDOUT: %specific_fn: = specific_function %impl.elem0.loc14_28, @Int.as.ImplicitAs.impl.Convert(constants.%int_32) [concrete = constants.%Int.as.ImplicitAs.impl.Convert.specific_fn] // CHECK:STDOUT: %bound_method.loc14_28.2: = bound_method %N.ref, %specific_fn [symbolic = %bound_method.loc14_28.3 (constants.%bound_method)] // CHECK:STDOUT: %Int.as.ImplicitAs.impl.Convert.call.loc14_28.1: init Core.IntLiteral = call %bound_method.loc14_28.2(%N.ref) [symbolic = %Int.as.ImplicitAs.impl.Convert.call.loc14_28.2 (constants.%Int.as.ImplicitAs.impl.Convert.call)] // CHECK:STDOUT: %.loc14_28.1: Core.IntLiteral = value_of_initializer %Int.as.ImplicitAs.impl.Convert.call.loc14_28.1 [symbolic = %Int.as.ImplicitAs.impl.Convert.call.loc14_28.2 (constants.%Int.as.ImplicitAs.impl.Convert.call)] // CHECK:STDOUT: %.loc14_28.2: Core.IntLiteral = converted %N.ref, %.loc14_28.1 [symbolic = %Int.as.ImplicitAs.impl.Convert.call.loc14_28.2 (constants.%Int.as.ImplicitAs.impl.Convert.call)] // CHECK:STDOUT: %array_type.loc14_29.1: type = array_type %.loc14_28.2, %i32.loc14 [symbolic = %array_type.loc14_29.2 (constants.%array_type.2ec)] // CHECK:STDOUT: } // CHECK:STDOUT: %k: ref @G.%array_type.loc14_29.2 (%array_type.2ec) = ref_binding k, %k.var // CHECK:STDOUT: %impl.elem0.loc14_3.1: @G.%.loc14_3.2 (%.63e) = impl_witness_access constants.%Destroy.lookup_impl_witness.f5c, element0 [symbolic = %impl.elem0.loc14_3.2 (constants.%impl.elem0.2bf)] // CHECK:STDOUT: %bound_method.loc14_3.1: = bound_method %k.var, %impl.elem0.loc14_3.1 // CHECK:STDOUT: %specific_impl_fn.loc14_3.1: = specific_impl_function %impl.elem0.loc14_3.1, @Destroy.WithSelf.Op(constants.%Destroy.facet.d1e) [symbolic = %specific_impl_fn.loc14_3.2 (constants.%specific_impl_fn.0a5)] // CHECK:STDOUT: %bound_method.loc14_3.2: = bound_method %k.var, %specific_impl_fn.loc14_3.1 // CHECK:STDOUT: %Destroy.WithSelf.Op.call: init %empty_tuple.type = call %bound_method.loc14_3.2(%k.var) // CHECK:STDOUT: // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @F(constants.%T) { // CHECK:STDOUT: %T.loc4_6.1 => constants.%T // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @G(constants.%N.5de) { // CHECK:STDOUT: %N.loc12_6.1 => constants.%N.5de // CHECK:STDOUT: } // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/eval/binding.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/int.carbon // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/eval/binding.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/eval/binding.carbon // --- local.carbon library "[[@TEST_NAME]]"; eval fn F(a: i32) -> i32 { let b: i32 = a; let c: i32 = b; return c; } //@dump-sem-ir-begin let n: array(i32, F(3)) = (1, 2, 3); //@dump-sem-ir-end // --- fail_global.carbon library "[[@TEST_NAME]]"; // This is a runtime binding, even though it has a compile-time-constant value, // so is not accessible from within G. let a: i32 = 3; eval fn G() -> i32 { // CHECK:STDERR: fail_global.carbon:[[@LINE+3]]:10: error: expression is runtime; expected constant [EvalRequiresConstantValue] // CHECK:STDERR: return a; // CHECK:STDERR: ^ return a; } // CHECK:STDERR: fail_global.carbon:[[@LINE+4]]:19: note: in call to G here [InCallToEvalFn] // CHECK:STDERR: let n: array(i32, G()) = (1, 2, 3); // CHECK:STDERR: ^~~ // CHECK:STDERR: let n: array(i32, G()) = (1, 2, 3); // --- fail_runtime_value.carbon library "[[@TEST_NAME]]"; fn F() -> i32; let a: i32 = F(); eval fn G() -> i32 { // CHECK:STDERR: fail_runtime_value.carbon:[[@LINE+3]]:10: error: expression is runtime; expected constant [EvalRequiresConstantValue] // CHECK:STDERR: return a; // CHECK:STDERR: ^ return a; } // CHECK:STDERR: fail_runtime_value.carbon:[[@LINE+4]]:19: note: in call to G here [InCallToEvalFn] // CHECK:STDERR: let n: array(i32, G()) = (1, 2, 3); // CHECK:STDERR: ^~~ // CHECK:STDERR: let n: array(i32, G()) = (1, 2, 3); // CHECK:STDOUT: --- local.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [concrete] // CHECK:STDOUT: %i32: type = class_type @Int, @Int(%int_32) [concrete] // CHECK:STDOUT: %F.type: type = fn_type @F [concrete] // CHECK:STDOUT: %F: %F.type = struct_value () [concrete] // CHECK:STDOUT: %int_3.1ba: Core.IntLiteral = int_value 3 [concrete] // CHECK:STDOUT: %ImplicitAs.type.e8c: type = facet_type <@ImplicitAs, @ImplicitAs(%i32)> [concrete] // CHECK:STDOUT: %To: Core.IntLiteral = symbolic_binding To, 0 [symbolic] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.4e6: type = fn_type @Core.IntLiteral.as.ImplicitAs.impl.Convert, @Core.IntLiteral.as.ImplicitAs.impl(%To) [symbolic] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.3c2: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.4e6 = struct_value () [symbolic] // CHECK:STDOUT: %ImplicitAs.type.139: type = facet_type <@ImplicitAs, @ImplicitAs(Core.IntLiteral)> [concrete] // CHECK:STDOUT: %From: Core.IntLiteral = symbolic_binding From, 0 [symbolic] // CHECK:STDOUT: %Int.as.ImplicitAs.impl.Convert.type.2ed: type = fn_type @Int.as.ImplicitAs.impl.Convert, @Int.as.ImplicitAs.impl(%From) [symbolic] // CHECK:STDOUT: %Int.as.ImplicitAs.impl.Convert.d29: %Int.as.ImplicitAs.impl.Convert.type.2ed = struct_value () [symbolic] // CHECK:STDOUT: %ImplicitAs.impl_witness.6bc: = impl_witness imports.%ImplicitAs.impl_witness_table.74f, @Core.IntLiteral.as.ImplicitAs.impl(%int_32) [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.e0d: type = fn_type @Core.IntLiteral.as.ImplicitAs.impl.Convert, @Core.IntLiteral.as.ImplicitAs.impl(%int_32) [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.e0d = struct_value () [concrete] // CHECK:STDOUT: %ImplicitAs.facet.b94: %ImplicitAs.type.e8c = facet_value Core.IntLiteral, (%ImplicitAs.impl_witness.6bc) [concrete] // CHECK:STDOUT: %ImplicitAs.WithSelf.Convert.type.b37: type = fn_type @ImplicitAs.WithSelf.Convert, @ImplicitAs.WithSelf(%i32, %ImplicitAs.facet.b94) [concrete] // CHECK:STDOUT: %.545: type = fn_type_with_self_type %ImplicitAs.WithSelf.Convert.type.b37, %ImplicitAs.facet.b94 [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.bound.061: = bound_method %int_3.1ba, %Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5 [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn: = specific_function %Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5, @Core.IntLiteral.as.ImplicitAs.impl.Convert(%int_32) [concrete] // CHECK:STDOUT: %bound_method.fa7: = bound_method %int_3.1ba, %Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn [concrete] // CHECK:STDOUT: %int_3.822: %i32 = int_value 3 [concrete] // CHECK:STDOUT: %ImplicitAs.impl_witness.640: = impl_witness imports.%ImplicitAs.impl_witness_table.ea2, @Int.as.ImplicitAs.impl(%int_32) [concrete] // CHECK:STDOUT: %Int.as.ImplicitAs.impl.Convert.type.240: type = fn_type @Int.as.ImplicitAs.impl.Convert, @Int.as.ImplicitAs.impl(%int_32) [concrete] // CHECK:STDOUT: %Int.as.ImplicitAs.impl.Convert.dd4: %Int.as.ImplicitAs.impl.Convert.type.240 = struct_value () [concrete] // CHECK:STDOUT: %ImplicitAs.facet.290: %ImplicitAs.type.139 = facet_value %i32, (%ImplicitAs.impl_witness.640) [concrete] // CHECK:STDOUT: %ImplicitAs.WithSelf.Convert.type.462: type = fn_type @ImplicitAs.WithSelf.Convert, @ImplicitAs.WithSelf(Core.IntLiteral, %ImplicitAs.facet.290) [concrete] // CHECK:STDOUT: %.0a7: type = fn_type_with_self_type %ImplicitAs.WithSelf.Convert.type.462, %ImplicitAs.facet.290 [concrete] // CHECK:STDOUT: %Int.as.ImplicitAs.impl.Convert.bound: = bound_method %int_3.822, %Int.as.ImplicitAs.impl.Convert.dd4 [concrete] // CHECK:STDOUT: %Int.as.ImplicitAs.impl.Convert.specific_fn: = specific_function %Int.as.ImplicitAs.impl.Convert.dd4, @Int.as.ImplicitAs.impl.Convert(%int_32) [concrete] // CHECK:STDOUT: %bound_method.17a: = bound_method %int_3.822, %Int.as.ImplicitAs.impl.Convert.specific_fn [concrete] // CHECK:STDOUT: %array_type: type = array_type %int_3.1ba, %i32 [concrete] // CHECK:STDOUT: %pattern_type.5d8: type = pattern_type %array_type [concrete] // CHECK:STDOUT: %int_1.5b8: Core.IntLiteral = int_value 1 [concrete] // CHECK:STDOUT: %int_2.ecc: Core.IntLiteral = int_value 2 [concrete] // CHECK:STDOUT: %tuple.type.37f: type = tuple_type (Core.IntLiteral, Core.IntLiteral, Core.IntLiteral) [concrete] // CHECK:STDOUT: %tuple.2d5: %tuple.type.37f = tuple_value (%int_1.5b8, %int_2.ecc, %int_3.1ba) [concrete] // CHECK:STDOUT: %int_0: Core.IntLiteral = int_value 0 [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.bound.215: = bound_method %int_1.5b8, %Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5 [concrete] // CHECK:STDOUT: %bound_method.38b: = bound_method %int_1.5b8, %Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn [concrete] // CHECK:STDOUT: %int_1.5d2: %i32 = int_value 1 [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.bound.4e5: = bound_method %int_2.ecc, %Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5 [concrete] // CHECK:STDOUT: %bound_method.646: = bound_method %int_2.ecc, %Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn [concrete] // CHECK:STDOUT: %int_2.ef8: %i32 = int_value 2 [concrete] // CHECK:STDOUT: %array: %array_type = tuple_value (%int_1.5d2, %int_2.ef8, %int_3.822) [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core.import_ref.42d: @Core.IntLiteral.as.ImplicitAs.impl.%Core.IntLiteral.as.ImplicitAs.impl.Convert.type (%Core.IntLiteral.as.ImplicitAs.impl.Convert.type.4e6) = import_ref Core//prelude/parts/int, loc{{\d+_\d+}}, loaded [symbolic = @Core.IntLiteral.as.ImplicitAs.impl.%Core.IntLiteral.as.ImplicitAs.impl.Convert (constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.3c2)] // CHECK:STDOUT: %ImplicitAs.impl_witness_table.74f = impl_witness_table (%Core.import_ref.42d), @Core.IntLiteral.as.ImplicitAs.impl [concrete] // CHECK:STDOUT: %Core.import_ref.0bc: @Int.as.ImplicitAs.impl.%Int.as.ImplicitAs.impl.Convert.type (%Int.as.ImplicitAs.impl.Convert.type.2ed) = import_ref Core//prelude/parts/int, loc{{\d+_\d+}}, loaded [symbolic = @Int.as.ImplicitAs.impl.%Int.as.ImplicitAs.impl.Convert (constants.%Int.as.ImplicitAs.impl.Convert.d29)] // CHECK:STDOUT: %ImplicitAs.impl_witness_table.ea2 = impl_witness_table (%Core.import_ref.0bc), @Int.as.ImplicitAs.impl [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %n.patt: %pattern_type.5d8 = value_binding_pattern n [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %.loc11_23: type = splice_block %array_type [concrete = constants.%array_type] { // CHECK:STDOUT: %i32: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %F.ref: %F.type = name_ref F, %F.decl [concrete = constants.%F] // CHECK:STDOUT: %int_3: Core.IntLiteral = int_value 3 [concrete = constants.%int_3.1ba] // CHECK:STDOUT: %impl.elem0.loc11_21: %.545 = impl_witness_access constants.%ImplicitAs.impl_witness.6bc, element0 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5] // CHECK:STDOUT: %bound_method.loc11_21.1: = bound_method %int_3, %impl.elem0.loc11_21 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.bound.061] // CHECK:STDOUT: %specific_fn.loc11_21: = specific_function %impl.elem0.loc11_21, @Core.IntLiteral.as.ImplicitAs.impl.Convert(constants.%int_32) [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn] // CHECK:STDOUT: %bound_method.loc11_21.2: = bound_method %int_3, %specific_fn.loc11_21 [concrete = constants.%bound_method.fa7] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc11_21: init %i32 = call %bound_method.loc11_21.2(%int_3) [concrete = constants.%int_3.822] // CHECK:STDOUT: %.loc11_21.1: %i32 = value_of_initializer %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc11_21 [concrete = constants.%int_3.822] // CHECK:STDOUT: %.loc11_21.2: %i32 = converted %int_3, %.loc11_21.1 [concrete = constants.%int_3.822] // CHECK:STDOUT: %F.call: init %i32 = call %F.ref(%.loc11_21.2) [concrete = constants.%int_3.822] // CHECK:STDOUT: %impl.elem0.loc11_22: %.0a7 = impl_witness_access constants.%ImplicitAs.impl_witness.640, element0 [concrete = constants.%Int.as.ImplicitAs.impl.Convert.dd4] // CHECK:STDOUT: %bound_method.loc11_22.1: = bound_method %F.call, %impl.elem0.loc11_22 [concrete = constants.%Int.as.ImplicitAs.impl.Convert.bound] // CHECK:STDOUT: %specific_fn.loc11_22: = specific_function %impl.elem0.loc11_22, @Int.as.ImplicitAs.impl.Convert(constants.%int_32) [concrete = constants.%Int.as.ImplicitAs.impl.Convert.specific_fn] // CHECK:STDOUT: %bound_method.loc11_22.2: = bound_method %F.call, %specific_fn.loc11_22 [concrete = constants.%bound_method.17a] // CHECK:STDOUT: %.loc11_22.1: %i32 = value_of_initializer %F.call [concrete = constants.%int_3.822] // CHECK:STDOUT: %.loc11_22.2: %i32 = converted %F.call, %.loc11_22.1 [concrete = constants.%int_3.822] // CHECK:STDOUT: %Int.as.ImplicitAs.impl.Convert.call: init Core.IntLiteral = call %bound_method.loc11_22.2(%.loc11_22.2) [concrete = constants.%int_3.1ba] // CHECK:STDOUT: %.loc11_22.3: Core.IntLiteral = value_of_initializer %Int.as.ImplicitAs.impl.Convert.call [concrete = constants.%int_3.1ba] // CHECK:STDOUT: %.loc11_22.4: Core.IntLiteral = converted %F.call, %.loc11_22.3 [concrete = constants.%int_3.1ba] // CHECK:STDOUT: %array_type: type = array_type %.loc11_22.4, %i32 [concrete = constants.%array_type] // CHECK:STDOUT: } // CHECK:STDOUT: %impl.elem0.loc11_35.1: %.545 = impl_witness_access constants.%ImplicitAs.impl_witness.6bc, element0 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5] // CHECK:STDOUT: %bound_method.loc11_35.1: = bound_method @__global_init.%int_1, %impl.elem0.loc11_35.1 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.bound.215] // CHECK:STDOUT: %specific_fn.loc11_35.1: = specific_function %impl.elem0.loc11_35.1, @Core.IntLiteral.as.ImplicitAs.impl.Convert(constants.%int_32) [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn] // CHECK:STDOUT: %bound_method.loc11_35.2: = bound_method @__global_init.%int_1, %specific_fn.loc11_35.1 [concrete = constants.%bound_method.38b] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc11_35.1: init %i32 = call %bound_method.loc11_35.2(@__global_init.%int_1) [concrete = constants.%int_1.5d2] // CHECK:STDOUT: %.loc11_35.1: init %i32 = converted @__global_init.%int_1, %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc11_35.1 [concrete = constants.%int_1.5d2] // CHECK:STDOUT: %.loc11_35.2: ref %array_type = temporary_storage // CHECK:STDOUT: %int_0: Core.IntLiteral = int_value 0 [concrete = constants.%int_0] // CHECK:STDOUT: %.loc11_35.3: ref %i32 = array_index %.loc11_35.2, %int_0 // CHECK:STDOUT: %.loc11_35.4: init %i32 to %.loc11_35.3 = in_place_init %.loc11_35.1 [concrete = constants.%int_1.5d2] // CHECK:STDOUT: %impl.elem0.loc11_35.2: %.545 = impl_witness_access constants.%ImplicitAs.impl_witness.6bc, element0 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5] // CHECK:STDOUT: %bound_method.loc11_35.3: = bound_method @__global_init.%int_2, %impl.elem0.loc11_35.2 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.bound.4e5] // CHECK:STDOUT: %specific_fn.loc11_35.2: = specific_function %impl.elem0.loc11_35.2, @Core.IntLiteral.as.ImplicitAs.impl.Convert(constants.%int_32) [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn] // CHECK:STDOUT: %bound_method.loc11_35.4: = bound_method @__global_init.%int_2, %specific_fn.loc11_35.2 [concrete = constants.%bound_method.646] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc11_35.2: init %i32 = call %bound_method.loc11_35.4(@__global_init.%int_2) [concrete = constants.%int_2.ef8] // CHECK:STDOUT: %.loc11_35.5: init %i32 = converted @__global_init.%int_2, %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc11_35.2 [concrete = constants.%int_2.ef8] // CHECK:STDOUT: %int_1: Core.IntLiteral = int_value 1 [concrete = constants.%int_1.5b8] // CHECK:STDOUT: %.loc11_35.6: ref %i32 = array_index %.loc11_35.2, %int_1 // CHECK:STDOUT: %.loc11_35.7: init %i32 to %.loc11_35.6 = in_place_init %.loc11_35.5 [concrete = constants.%int_2.ef8] // CHECK:STDOUT: %impl.elem0.loc11_35.3: %.545 = impl_witness_access constants.%ImplicitAs.impl_witness.6bc, element0 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5] // CHECK:STDOUT: %bound_method.loc11_35.5: = bound_method @__global_init.%int_3, %impl.elem0.loc11_35.3 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.bound.061] // CHECK:STDOUT: %specific_fn.loc11_35.3: = specific_function %impl.elem0.loc11_35.3, @Core.IntLiteral.as.ImplicitAs.impl.Convert(constants.%int_32) [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn] // CHECK:STDOUT: %bound_method.loc11_35.6: = bound_method @__global_init.%int_3, %specific_fn.loc11_35.3 [concrete = constants.%bound_method.fa7] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc11_35.3: init %i32 = call %bound_method.loc11_35.6(@__global_init.%int_3) [concrete = constants.%int_3.822] // CHECK:STDOUT: %.loc11_35.8: init %i32 = converted @__global_init.%int_3, %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc11_35.3 [concrete = constants.%int_3.822] // CHECK:STDOUT: %int_2: Core.IntLiteral = int_value 2 [concrete = constants.%int_2.ecc] // CHECK:STDOUT: %.loc11_35.9: ref %i32 = array_index %.loc11_35.2, %int_2 // CHECK:STDOUT: %.loc11_35.10: init %i32 to %.loc11_35.9 = in_place_init %.loc11_35.8 [concrete = constants.%int_3.822] // CHECK:STDOUT: %.loc11_35.11: init %array_type to %.loc11_35.2 = array_init (%.loc11_35.4, %.loc11_35.7, %.loc11_35.10) [concrete = constants.%array] // CHECK:STDOUT: %.loc11_35.12: init %array_type = converted @__global_init.%.loc11, %.loc11_35.11 [concrete = constants.%array] // CHECK:STDOUT: %.loc11_35.13: ref %array_type = temporary %.loc11_35.2, %.loc11_35.12 // CHECK:STDOUT: %.loc11_35.14: %array_type = acquire_value %.loc11_35.13 // CHECK:STDOUT: %n: %array_type = value_binding n, %.loc11_35.14 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @__global_init() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %int_1: Core.IntLiteral = int_value 1 [concrete = constants.%int_1.5b8] // CHECK:STDOUT: %int_2: Core.IntLiteral = int_value 2 [concrete = constants.%int_2.ecc] // CHECK:STDOUT: %int_3: Core.IntLiteral = int_value 3 [concrete = constants.%int_3.1ba] // CHECK:STDOUT: %.loc11: %tuple.type.37f = tuple_literal (%int_1, %int_2, %int_3) [concrete = constants.%tuple.2d5] // CHECK:STDOUT: // CHECK:STDOUT: } // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/eval/branch.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/bool.carbon // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/eval/branch.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/eval/branch.carbon // --- types.carbon library "[[@TEST_NAME]]"; class A {} class B {} fn MakeA() -> A { return {}; } fn MakeB() -> B { return {}; } // --- if_statement.carbon library "[[@TEST_NAME]]"; import library "types"; eval fn F(b: bool) -> type { if (b) { return A; } else { return B; } } //@dump-sem-ir-begin var a: F(true) = MakeA(); var b: F(false) = MakeB(); //@dump-sem-ir-end // --- if_expression_in_eval_fn.carbon library "[[@TEST_NAME]]"; import library "types"; eval fn F(b: bool) -> type { return if b then A else B; } //@dump-sem-ir-begin var a: F(true) = MakeA(); var b: F(false) = MakeB(); //@dump-sem-ir-end // --- fail_todo_if_expression_direct.carbon library "[[@TEST_NAME]]"; import library "types"; // CHECK:STDERR: fail_todo_if_expression_direct.carbon:[[@LINE+4]]:8: error: semantics TODO: `Control flow expressions are currently only supported inside functions.` [SemanticsTodo] // CHECK:STDERR: var a: if true then A else B = MakeA(); // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: var a: if true then A else B = MakeA(); var b: if false then A else B = MakeB(); // CHECK:STDOUT: --- if_statement.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %F.type: type = fn_type @F [concrete] // CHECK:STDOUT: %F: %F.type = struct_value () [concrete] // CHECK:STDOUT: %A: type = class_type @A [concrete] // CHECK:STDOUT: %B: type = class_type @B [concrete] // CHECK:STDOUT: %true: bool = bool_literal true [concrete] // CHECK:STDOUT: %pattern_type.1ab: type = pattern_type %A [concrete] // CHECK:STDOUT: %MakeA.type: type = fn_type @MakeA [concrete] // CHECK:STDOUT: %MakeA: %MakeA.type = struct_value () [concrete] // CHECK:STDOUT: %false: bool = bool_literal false [concrete] // CHECK:STDOUT: %pattern_type.1f4: type = pattern_type %B [concrete] // CHECK:STDOUT: %MakeB.type: type = fn_type @MakeB [concrete] // CHECK:STDOUT: %MakeB: %MakeB.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Main.MakeA: %MakeA.type = import_ref Main//types, MakeA, loaded [concrete = constants.%MakeA] // CHECK:STDOUT: %Main.MakeB: %MakeB.type = import_ref Main//types, MakeB, loaded [concrete = constants.%MakeB] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %a.patt: %pattern_type.1ab = ref_binding_pattern a [concrete] // CHECK:STDOUT: %a.var_patt: %pattern_type.1ab = var_pattern %a.patt [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %a.var: ref %A = var %a.var_patt [concrete] // CHECK:STDOUT: %.loc14_14.1: type = splice_block %.loc14_14.3 [concrete = constants.%A] { // CHECK:STDOUT: %F.ref.loc14: %F.type = name_ref F, %F.decl [concrete = constants.%F] // CHECK:STDOUT: %true: bool = bool_literal true [concrete = constants.%true] // CHECK:STDOUT: %F.call.loc14: init type = call %F.ref.loc14(%true) [concrete = constants.%A] // CHECK:STDOUT: %.loc14_14.2: type = value_of_initializer %F.call.loc14 [concrete = constants.%A] // CHECK:STDOUT: %.loc14_14.3: type = converted %F.call.loc14, %.loc14_14.2 [concrete = constants.%A] // CHECK:STDOUT: } // CHECK:STDOUT: %a: ref %A = ref_binding a, %a.var [concrete = %a.var] // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %b.patt: %pattern_type.1f4 = ref_binding_pattern b [concrete] // CHECK:STDOUT: %b.var_patt: %pattern_type.1f4 = var_pattern %b.patt [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %b.var: ref %B = var %b.var_patt [concrete] // CHECK:STDOUT: %.loc15_15.1: type = splice_block %.loc15_15.3 [concrete = constants.%B] { // CHECK:STDOUT: %F.ref.loc15: %F.type = name_ref F, %F.decl [concrete = constants.%F] // CHECK:STDOUT: %false: bool = bool_literal false [concrete = constants.%false] // CHECK:STDOUT: %F.call.loc15: init type = call %F.ref.loc15(%false) [concrete = constants.%B] // CHECK:STDOUT: %.loc15_15.2: type = value_of_initializer %F.call.loc15 [concrete = constants.%B] // CHECK:STDOUT: %.loc15_15.3: type = converted %F.call.loc15, %.loc15_15.2 [concrete = constants.%B] // CHECK:STDOUT: } // CHECK:STDOUT: %b: ref %B = ref_binding b, %b.var [concrete = %b.var] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @__global_init() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %MakeA.ref: %MakeA.type = name_ref MakeA, imports.%Main.MakeA [concrete = constants.%MakeA] // CHECK:STDOUT: %.loc14: ref %A = splice_block file.%a.var [concrete = file.%a.var] {} // CHECK:STDOUT: %MakeA.call: init %A to %.loc14 = call %MakeA.ref() // CHECK:STDOUT: assign file.%a.var, %MakeA.call // CHECK:STDOUT: %MakeB.ref: %MakeB.type = name_ref MakeB, imports.%Main.MakeB [concrete = constants.%MakeB] // CHECK:STDOUT: %.loc15: ref %B = splice_block file.%b.var [concrete = file.%b.var] {} // CHECK:STDOUT: %MakeB.call: init %B to %.loc15 = call %MakeB.ref() // CHECK:STDOUT: assign file.%b.var, %MakeB.call // CHECK:STDOUT: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- if_expression_in_eval_fn.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %F.type: type = fn_type @F [concrete] // CHECK:STDOUT: %F: %F.type = struct_value () [concrete] // CHECK:STDOUT: %A: type = class_type @A [concrete] // CHECK:STDOUT: %B: type = class_type @B [concrete] // CHECK:STDOUT: %true: bool = bool_literal true [concrete] // CHECK:STDOUT: %pattern_type.1ab: type = pattern_type %A [concrete] // CHECK:STDOUT: %MakeA.type: type = fn_type @MakeA [concrete] // CHECK:STDOUT: %MakeA: %MakeA.type = struct_value () [concrete] // CHECK:STDOUT: %false: bool = bool_literal false [concrete] // CHECK:STDOUT: %pattern_type.1f4: type = pattern_type %B [concrete] // CHECK:STDOUT: %MakeB.type: type = fn_type @MakeB [concrete] // CHECK:STDOUT: %MakeB: %MakeB.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Main.MakeA: %MakeA.type = import_ref Main//types, MakeA, loaded [concrete = constants.%MakeA] // CHECK:STDOUT: %Main.MakeB: %MakeB.type = import_ref Main//types, MakeB, loaded [concrete = constants.%MakeB] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %a.patt: %pattern_type.1ab = ref_binding_pattern a [concrete] // CHECK:STDOUT: %a.var_patt: %pattern_type.1ab = var_pattern %a.patt [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %a.var: ref %A = var %a.var_patt [concrete] // CHECK:STDOUT: %.loc10_14.1: type = splice_block %.loc10_14.3 [concrete = constants.%A] { // CHECK:STDOUT: %F.ref.loc10: %F.type = name_ref F, %F.decl [concrete = constants.%F] // CHECK:STDOUT: %true: bool = bool_literal true [concrete = constants.%true] // CHECK:STDOUT: %F.call.loc10: init type = call %F.ref.loc10(%true) [concrete = constants.%A] // CHECK:STDOUT: %.loc10_14.2: type = value_of_initializer %F.call.loc10 [concrete = constants.%A] // CHECK:STDOUT: %.loc10_14.3: type = converted %F.call.loc10, %.loc10_14.2 [concrete = constants.%A] // CHECK:STDOUT: } // CHECK:STDOUT: %a: ref %A = ref_binding a, %a.var [concrete = %a.var] // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %b.patt: %pattern_type.1f4 = ref_binding_pattern b [concrete] // CHECK:STDOUT: %b.var_patt: %pattern_type.1f4 = var_pattern %b.patt [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %b.var: ref %B = var %b.var_patt [concrete] // CHECK:STDOUT: %.loc11_15.1: type = splice_block %.loc11_15.3 [concrete = constants.%B] { // CHECK:STDOUT: %F.ref.loc11: %F.type = name_ref F, %F.decl [concrete = constants.%F] // CHECK:STDOUT: %false: bool = bool_literal false [concrete = constants.%false] // CHECK:STDOUT: %F.call.loc11: init type = call %F.ref.loc11(%false) [concrete = constants.%B] // CHECK:STDOUT: %.loc11_15.2: type = value_of_initializer %F.call.loc11 [concrete = constants.%B] // CHECK:STDOUT: %.loc11_15.3: type = converted %F.call.loc11, %.loc11_15.2 [concrete = constants.%B] // CHECK:STDOUT: } // CHECK:STDOUT: %b: ref %B = ref_binding b, %b.var [concrete = %b.var] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @__global_init() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %MakeA.ref: %MakeA.type = name_ref MakeA, imports.%Main.MakeA [concrete = constants.%MakeA] // CHECK:STDOUT: %.loc10: ref %A = splice_block file.%a.var [concrete = file.%a.var] {} // CHECK:STDOUT: %MakeA.call: init %A to %.loc10 = call %MakeA.ref() // CHECK:STDOUT: assign file.%a.var, %MakeA.call // CHECK:STDOUT: %MakeB.ref: %MakeB.type = name_ref MakeB, imports.%Main.MakeB [concrete = constants.%MakeB] // CHECK:STDOUT: %.loc11: ref %B = splice_block file.%b.var [concrete = file.%b.var] {} // CHECK:STDOUT: %MakeB.call: init %B to %.loc11 = call %MakeB.ref() // CHECK:STDOUT: assign file.%b.var, %MakeB.call // CHECK:STDOUT: // CHECK:STDOUT: } // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/eval/call.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/int.carbon // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/eval/call.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/eval/call.carbon // --- call.carbon library "[[@TEST_NAME]]"; eval fn F() -> i32 { return 3; } var a: array(i32, F()) = (0, 1, 2); // --- fail_call_wrong_value.carbon library "[[@TEST_NAME]]"; eval fn F() -> i32 { return 3; } // Ensure we get an error about the over-sized initializer and aren't just // treating all `eval fn` calls as silent errors. // CHECK:STDERR: fail_call_wrong_value.carbon:[[@LINE+4]]:26: error: cannot initialize array of 3 elements from 4 initializers [ArrayInitFromLiteralArgCountMismatch] // CHECK:STDERR: var a: array(i32, F()) = (0, 1, 2, 3); // CHECK:STDERR: ^~~~~~~~~~~~ // CHECK:STDERR: var a: array(i32, F()) = (0, 1, 2, 3); // --- fail_call_fn_not_eval.carbon library "[[@TEST_NAME]]"; fn F() -> i32 { return 3; } // CHECK:STDERR: fail_call_fn_not_eval.carbon:[[@LINE+4]]:19: error: array bound is not a constant [InvalidArrayExpr] // CHECK:STDERR: var a: array(i32, F()) = (0, 1, 2); // CHECK:STDERR: ^~~ // CHECK:STDERR: var a: array(i32, F()) = (0, 1, 2); // --- fail_call_eval_fn_not_defined.carbon library "[[@TEST_NAME]]"; eval fn F() -> i32; // TODO: We should be able to diagnose this better. // CHECK:STDERR: fail_call_eval_fn_not_defined.carbon:[[@LINE+4]]:19: error: array bound is not a constant [InvalidArrayExpr] // CHECK:STDERR: var a: array(i32, F()) = (0, 1, 2); // CHECK:STDERR: ^~~ // CHECK:STDERR: var a: array(i32, F()) = (0, 1, 2); // --- param_and_return.carbon library "[[@TEST_NAME]]"; eval fn F(x: i32) -> i32 { return x; } var a: array(i32, F(3)) = (1, 2, 3); // --- fail_todo_dependent_call.carbon library "[[@TEST_NAME]]"; eval fn F(x: i32) -> i32 { return x; } fn G(N:! i32) { // CHECK:STDERR: fail_todo_dependent_call.carbon:[[@LINE+4]]:17: error: cannot evaluate type expression [TypeExprEvaluationFailure] // CHECK:STDERR: var unused a: array(i32, F(N)) = (0, 1, 2); // CHECK:STDERR: ^~~~~~~~~~~~~~~~ // CHECK:STDERR: var unused a: array(i32, F(N)) = (0, 1, 2); } fn H() { G(3); } // --- fail_todo_dependent_call_type.carbon library "[[@TEST_NAME]]"; class C {} eval fn F(_: i32) -> type { return C; } fn UseFGenerically(X:! i32) { // CHECK:STDERR: fail_todo_dependent_call_type.carbon:[[@LINE+12]]:3: error: member name of type `` in compound member access is not an instance member or an interface member [CompoundMemberAccessDoesNotUseBase] // CHECK:STDERR: var unused v: F(X) = {}; // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~ // CHECK:STDERR: // CHECK:STDERR: fail_todo_dependent_call_type.carbon:[[@LINE+8]]:3: error: value of type `` is not callable [CallToNonCallable] // CHECK:STDERR: var unused v: F(X) = {}; // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~ // CHECK:STDERR: // CHECK:STDERR: fail_todo_dependent_call_type.carbon:[[@LINE+4]]:3: error: cannot access member of interface `Core.Destroy` in type `` that does not implement that interface [MissingImplInMemberAccess] // CHECK:STDERR: var unused v: F(X) = {}; // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~ // CHECK:STDERR: var unused v: F(X) = {}; } fn UseFSpecifically() { UseFGenerically(3); } ================================================ FILE: toolchain/check/testdata/eval/recursion.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // TODO: Extend the "int" min_prelude to support basic integer operations, so we // can use it here. // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/full.carbon // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/eval/recursion.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/eval/recursion.carbon // --- recursion.carbon library "[[@TEST_NAME]]"; eval fn F(n: i32) -> type { if (n == 0) { return i32; } return F(n - 1); } var i: F(3) = 0; // TODO: Diagnose infinite recursion. ================================================ FILE: toolchain/check/testdata/eval/symbolic.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/bool.carbon // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/eval/symbolic.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/eval/symbolic.carbon // --- symbolic_call_to_eval_fn.carbon library "[[@TEST_NAME]]"; eval fn F(B:! bool) -> type { if (B) { return (); } return ({}, {}); } fn G(B:! bool) { let unused n: F(B) = ({}, {}); } fn H() { G(false); } // --- fail_symbolic_call_to_eval_fn_invalid_instantiation.carbon // TODO: Provide a location for the error message. library "[[@TEST_NAME]]"; eval fn F(B:! bool) -> type { if (B) { return (); } return ({}, {}); } fn G(B:! bool) { let unused n: F(B) = ({}, {}); } fn H() { // CHECK:STDERR: fail_symbolic_call_to_eval_fn_invalid_instantiation.carbon:[[@LINE+5]]:3: error: unable to monomorphize specific `G(true)` [ResolvingSpecificHere] // CHECK:STDERR: G(true); // CHECK:STDERR: ^ // CHECK:STDERR: fail_symbolic_call_to_eval_fn_invalid_instantiation.carbon: note: cannot initialize tuple of 0 elements from tuple with 2 elements [TupleInitElementCountMismatch] // CHECK:STDERR: G(true); } ================================================ FILE: toolchain/check/testdata/eval/unexpected_runtime.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/convert.carbon // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/eval/unexpected_runtime.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/eval/unexpected_runtime.carbon // --- fail_unexpected_runtime.carbon var x: type; interface Z { let T:! type; } class D { // CHECK:STDERR: fail_unexpected_runtime.carbon:[[@LINE+4]]:31: error: expression is runtime; expected constant [EvalRequiresConstantValue] // CHECK:STDERR: extend impl as Z where .T = x {} // CHECK:STDERR: ^ // CHECK:STDERR: extend impl as Z where .T = x {} } ================================================ FILE: toolchain/check/testdata/facet/access.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/convert.carbon // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/facet/access.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/facet/access.carbon // --- access_assoc_fn.carbon library "[[@TEST_NAME]]"; interface I { fn DoIt(); } fn Use(T:! I) { //@dump-sem-ir-begin T.DoIt(); //@dump-sem-ir-end } // --- assoc_fn_using_self.carbon library "[[@TEST_NAME]]"; interface I { fn Make() -> Self; } fn Use(T:! I) -> T { //@dump-sem-ir-begin return T.Make(); //@dump-sem-ir-end } // --- access_assoc_method.carbon library "[[@TEST_NAME]]"; interface I { fn Copy[self: Self]() -> Self; } //@dump-sem-ir-begin fn Use[T:! I](x: T) -> T { return x.Copy(); } //@dump-sem-ir-end // --- access_selfless_method.carbon library "[[@TEST_NAME]]"; interface I { fn Hello(); } fn Use[T:! I](x: T){ //@dump-sem-ir-begin x.Hello(); //@dump-sem-ir-end } // --- access_assoc_method_indirect.carbon library "[[@TEST_NAME]]"; interface I { fn Copy[self: Self]() -> Self; } fn UseIndirect[T:! I](x: T) -> T { //@dump-sem-ir-begin return x.(T.Copy)(); //@dump-sem-ir-end } // --- fail_todo_convert_from_period_self_to_full_facet_value.carbon library "[[@TEST_NAME]]"; interface I { let I1:! type; } fn F(U:! I where .I1 = .Self) { // CHECK:STDERR: fail_todo_convert_from_period_self_to_full_facet_value.carbon:[[@LINE+4]]:3: error: cannot convert type `U` that implements `I where .(I.I1) = .Self` into type implementing `I where .(I.I1) = U` [ConversionFailureFacetToFacet] // CHECK:STDERR: U as (I where .I1 = U); // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: U as (I where .I1 = U); // CHECK:STDERR: fail_todo_convert_from_period_self_to_full_facet_value.carbon:[[@LINE+4]]:3: error: cannot convert type `U` that implements `I where .(I.I1) = .Self` into type implementing `I where .(I.I1) = U` [ConversionFailureFacetToFacet] // CHECK:STDERR: (U as type) as (I where .I1 = U); // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: (U as type) as (I where .I1 = U); } // --- convert_to_period_self.carbon library "[[@TEST_NAME]]"; interface I { let I1:! type; let I2:! type; } fn F(U:! I where .I1 = .Self and .I2 = ()) { U as (I where .I1 = .Self); (U as type) as (I where .I1 = .Self); } // --- access_through_call_once.carbon library "[[@TEST_NAME]]"; // TODO: Merge this test with the one below once it works. interface I { let X:! type; fn G() -> X*; } fn F2[U:! I](unused V: U*) {} fn F(U:! I where .X = .Self, unused V: U) { // The returned value of `G` type `U` which has access to the methods of `I`. U.G()->G(); (U as type).G()->G(); // The returned value of type `U` can be used as a value of type `U`. F2(U.G()); } // --- fail_todo_access_through_call.carbon library "[[@TEST_NAME]]"; interface I { let X:! type; fn G() -> X*; } fn F2[U:! I](unused V: U*) {} fn F3[U:! I where .X = .Self](unused V: U*) {} fn F(U:! I where .X = .Self, unused V: U*) { // The returned value of `G` type `U` which has access to the methods of `I`. // // TODO: These should work. // - The first `.` is on a NameRef of type FacetType for `I where .X = .Self`. // - This finds `G` through the FacetType. // - The second `.` is on a Call of type FacetAccessType into `SymbolicBinding` with type FacetType for `I`. // - This finds `G` through the FacetType (impl lookup strips off FacetAccessType). // - The third `.` is on a Call of type FacetAccessType into `ImplWitnessAccess` of `I.X` into `LookupImplWitness`, which has type `type` // - Can't make calls on an `ImplWitnessAccess`. // - We could expect that the constant value of the `ImplWitnessAccess` would // be the same type that we got for the second lookup. // CHECK:STDERR: fail_todo_access_through_call.carbon:[[@LINE+4]]:3: error: type `.(I.X)` does not support qualified expressions [QualifiedExprUnsupported] // CHECK:STDERR: U.G()->G()->G(); // CHECK:STDERR: ^~~~~~~~~~~~~ // CHECK:STDERR: U.G()->G()->G(); // CHECK:STDERR: fail_todo_access_through_call.carbon:[[@LINE+4]]:3: error: type `.(I.X)` does not support qualified expressions [QualifiedExprUnsupported] // CHECK:STDERR: (U as type).G()->G()->G(); // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: (U as type).G()->G()->G(); // The returned value of type `U` can be used as a value of type `U`. // // TODO: This should work. // CHECK:STDERR: fail_todo_access_through_call.carbon:[[@LINE+4]]:6: error: type `.(I.X)` does not support qualified expressions [QualifiedExprUnsupported] // CHECK:STDERR: F2(U.G()->G()->G()); // CHECK:STDERR: ^~~~~~~~~~~~~ // CHECK:STDERR: F2(U.G()->G()->G()); // The constraints in the type `U` are preserved. // // TODO: These should work. // CHECK:STDERR: fail_todo_access_through_call.carbon:[[@LINE+7]]:3: error: cannot convert type `.Self` that implements `I` into type implementing `I where .(I.X) = .Self` [ConversionFailureFacetToFacet] // CHECK:STDERR: F3(U.G()); // CHECK:STDERR: ^~~~~~~~~ // CHECK:STDERR: fail_todo_access_through_call.carbon:[[@LINE-40]]:1: note: while deducing parameters of generic declared here [DeductionGenericHere] // CHECK:STDERR: fn F3[U:! I where .X = .Self](unused V: U*) {} // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: F3(U.G()); // CHECK:STDERR: fail_todo_access_through_call.carbon:[[@LINE+4]]:6: error: type `.(I.X)` does not support qualified expressions [QualifiedExprUnsupported] // CHECK:STDERR: F3(U.G()->G()->G()); // CHECK:STDERR: ^~~~~~~~~~~~~ // CHECK:STDERR: F3(U.G()->G()->G()); } // --- fail_todo_compound_access_through_call.carbon library "[[@TEST_NAME]]"; interface I { let X:! type; fn G() -> X; } fn F(U:! I where .X = .Self) { // Compound member lookup through a non-type value is possible for methods // which take a `self` parameter. But it's not possible for methods without // `self`. For those you need to go directly throug the type. // See: https://github.com/carbon-language/carbon-lang/issues/6025 // TODO: This step should work. // // CHECK:STDERR: fail_todo_compound_access_through_call.carbon:[[@LINE+7]]:14: error: cannot implicitly convert expression of type `.Self` to `U` [ConversionFailure] // CHECK:STDERR: let u: U = U.(I.G)(); // CHECK:STDERR: ^~~~~~~~~ // CHECK:STDERR: fail_todo_compound_access_through_call.carbon:[[@LINE+4]]:14: note: type `.Self` does not implement interface `Core.ImplicitAs(U)` [MissingImplInMemberAccessInContext] // CHECK:STDERR: let u: U = U.(I.G)(); // CHECK:STDERR: ^~~~~~~~~ // CHECK:STDERR: let u: U = U.(I.G)(); // `u` is a non-type value. Can call methods with `self` through compound // member lookup, but can't call methods without `self`. See the // `compound_access_through_call_with_self_param.carbon` test for the former. // // CHECK:STDERR: fail_todo_compound_access_through_call.carbon:[[@LINE+7]]:3: error: cannot implicitly convert non-type value of type `U` into type implementing `I` [ConversionFailureNonTypeToFacet] // CHECK:STDERR: u.(I.G)(); // CHECK:STDERR: ^~~~~~~ // CHECK:STDERR: fail_todo_compound_access_through_call.carbon:[[@LINE+4]]:3: note: type `U` does not implement interface `Core.ImplicitAs(I)` [MissingImplInMemberAccessInContext] // CHECK:STDERR: u.(I.G)(); // CHECK:STDERR: ^~~~~~~ // CHECK:STDERR: u.(I.G)(); // This is the same as the above, since G() returns a non-type value of type // `U`. // // CHECK:STDERR: fail_todo_compound_access_through_call.carbon:[[@LINE+7]]:3: error: cannot implicitly convert non-type value of type `.Self` into type implementing `I` [ConversionFailureNonTypeToFacet] // CHECK:STDERR: U.(I.G)().(I.G)(); // CHECK:STDERR: ^~~~~~~~~~~~~~~ // CHECK:STDERR: fail_todo_compound_access_through_call.carbon:[[@LINE+4]]:3: note: type `.Self` does not implement interface `Core.ImplicitAs(I)` [MissingImplInMemberAccessInContext] // CHECK:STDERR: U.(I.G)().(I.G)(); // CHECK:STDERR: ^~~~~~~~~~~~~~~ // CHECK:STDERR: U.(I.G)().(I.G)(); } // --- fail_todo_compound_access_through_call_with_self_param.carbon library "[[@TEST_NAME]]"; interface I { let X:! type; fn G[self: Self]() -> X*; } fn F(U:! I where .X = .Self, v: U) { // Compound member lookup through a non-type value is possible for methods // which take a `self` parameter. // TODO: This should all work. // CHECK:STDERR: fail_todo_compound_access_through_call_with_self_param.carbon:[[@LINE+7]]:15: error: cannot implicitly convert expression of type `.Self*` to `U*` [ConversionFailure] // CHECK:STDERR: let u: U* = v.(I.G)(); // CHECK:STDERR: ^~~~~~~~~ // CHECK:STDERR: fail_todo_compound_access_through_call_with_self_param.carbon:[[@LINE+4]]:15: note: type `.Self*` does not implement interface `Core.ImplicitAs(U*)` [MissingImplInMemberAccessInContext] // CHECK:STDERR: let u: U* = v.(I.G)(); // CHECK:STDERR: ^~~~~~~~~ // CHECK:STDERR: let u: U* = v.(I.G)(); // `u` is a non-type value. Can call methods with `self` through compound // member lookup, but can't call methods without `self`. See the // `compound_access_through_call.carbon` test for the latter. u->(I.G)(); // This is the same as the above, since G() returns a non-type value of type // `U`. This works because G has a `self` parameter. v.(I.G)()->(I.G)(); } // --- fail_non_const_associated.carbon library "[[@TEST_NAME]]"; interface I { let T:! type; } fn Id[U:! type](x: U) -> U { return Id(x); } impl () as I where .T = () {} // Type of member expr is associated entity type, // but value is not constant. // CHECK:STDERR: fail_non_const_associated.carbon:[[@LINE+4]]:8: error: semantics TODO: `Non-constant associated entity value` [SemanticsTodo] // CHECK:STDERR: var v: ().(Id(I.T)); // CHECK:STDERR: ^~~~~~~~~~~~ // CHECK:STDERR: var v: ().(Id(I.T)); // --- fail_non_const_associated_in_interface.carbon library "[[@TEST_NAME]]"; fn Id[U:! type](x: U) -> U { return Id(x); } interface J { let T:! type; // CHECK:STDERR: fail_non_const_associated_in_interface.carbon:[[@LINE+4]]:13: error: cannot evaluate type expression [TypeExprEvaluationFailure] // CHECK:STDERR: fn F() -> Id(T); // CHECK:STDERR: ^~~~~ // CHECK:STDERR: fn F() -> Id(T); } // --- fail_alias_to_non_const_assoc_entity.carbon library "[[@TEST_NAME]]"; interface I { let T:! type; } // CHECK:STDERR: fail_alias_to_non_const_assoc_entity.carbon:[[@LINE+4]]:8: error: semantics TODO: `HandleAutoTypeLiteral` [SemanticsTodo] // CHECK:STDERR: let x: auto = I.T; // CHECK:STDERR: ^~~~ // CHECK:STDERR: let x: auto = I.T; interface J { // Is this valid? alias U = x; // type of U is an assoc entity type, but value is not constant. fn F() -> U; } // --- to_import.carbon library "[[@TEST_NAME]]"; interface I { let T:! type; } alias U = I.T; // --- fail_access_alias_in_imported_library.carbon library "[[@TEST_NAME]]"; import library "to_import"; interface J { // extend I; alias V = U; // CHECK:STDERR: fail_access_alias_in_imported_library.carbon:[[@LINE+4]]:13: error: cannot convert type `Self` that implements `J` into type implementing `I` [ConversionFailureFacetToFacet] // CHECK:STDERR: fn F() -> V; // CHECK:STDERR: ^ // CHECK:STDERR: fn F() -> V; } // --- access_constant_in_self_facet.carbon library "[[@TEST_NAME]]"; //@dump-sem-ir-begin interface A { let X:! type; } fn F(AA:! A where .X = ()) -> AA.X { return (); } //@dump-sem-ir-end // --- access_constant_in_self_facet_with_multiple_interfaces.carbon library "[[@TEST_NAME]]"; interface A { let X:! type; } interface B { let Y:! type; } // The rewrite rules of .X and .Y come in some canonically sorted order. The // ImplWitnessAccess instruction looks at them to try find a value for the // rewrite in the conversion target. We use different orderings to more reliably // create the scenario where the ImplWitnessAccess sees a rewrite of a value in // an interface other than the one it is accessing before finding the correct // rewrite. //@dump-sem-ir-begin fn F(AB:! A & B where .X = () and .Y = {}) -> AB.X { return (); } fn G(AB:! A & B where .X = () and .Y = {}) -> AB.Y { return {}; } //@dump-sem-ir-end // --- symbolic_binding_type_of_impl_witness_access.carbon interface Y {} impl () as Y {} interface Z { let Y1:! Y; fn G() -> Y1*; } // The type of `T` matches exactly the type of `Z.Y1`, which prevents the // specific in the call from F2 from deducing a `FacetValue`. Instead it just // passes in the `ImplWitnessAccess` instruction as is. // // The `t: T` creates a `T as type`, or a `SymbolicBindingType` that gets // evaluated against the specific. The facet value that it evaluates against is // the `ImplWitnessAccess` from the call in F2. // // This requires that `SymbolicBindingType` evaluation correctly handles // arbitrary facet value instructions. Not just the common case of `FacetValue` // or `SymbolicBinding`. fn F1(T:! Y, unused t: T*) {} fn F2(U:! Z) { F1(U.Y1, U.G()); } // CHECK:STDOUT: --- access_assoc_fn.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %I.type: type = facet_type <@I> [concrete] // CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] // CHECK:STDOUT: %I.assoc_type: type = assoc_entity_type @I [concrete] // CHECK:STDOUT: %assoc0: %I.assoc_type = assoc_entity element0, @I.WithSelf.%I.WithSelf.DoIt.decl [concrete] // CHECK:STDOUT: %T: %I.type = symbolic_binding T, 0 [symbolic] // CHECK:STDOUT: %I.WithSelf.DoIt.type.66b: type = fn_type @I.WithSelf.DoIt, @I.WithSelf(%T) [symbolic] // CHECK:STDOUT: %T.binding.as_type: type = symbolic_binding_type T, 0, %T [symbolic] // CHECK:STDOUT: %I.lookup_impl_witness: = lookup_impl_witness %T, @I [symbolic] // CHECK:STDOUT: %.f37: type = fn_type_with_self_type %I.WithSelf.DoIt.type.66b, %T [symbolic] // CHECK:STDOUT: %impl.elem0: %.f37 = impl_witness_access %I.lookup_impl_witness, element0 [symbolic] // CHECK:STDOUT: %specific_impl_fn: = specific_impl_function %impl.elem0, @I.WithSelf.DoIt(%T) [symbolic] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic fn @Use(%T.loc8_8.2: %I.type) { // CHECK:STDOUT: // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %T.binding.as_type: type = symbolic_binding_type T, 0, %T.loc8_8.1 [symbolic = %T.binding.as_type (constants.%T.binding.as_type)] // CHECK:STDOUT: %I.lookup_impl_witness: = lookup_impl_witness %T.loc8_8.1, @I [symbolic = %I.lookup_impl_witness (constants.%I.lookup_impl_witness)] // CHECK:STDOUT: %I.WithSelf.DoIt.type: type = fn_type @I.WithSelf.DoIt, @I.WithSelf(%T.loc8_8.1) [symbolic = %I.WithSelf.DoIt.type (constants.%I.WithSelf.DoIt.type.66b)] // CHECK:STDOUT: %.loc10_4.2: type = fn_type_with_self_type %I.WithSelf.DoIt.type, %T.loc8_8.1 [symbolic = %.loc10_4.2 (constants.%.f37)] // CHECK:STDOUT: %impl.elem0.loc10_4.2: @Use.%.loc10_4.2 (%.f37) = impl_witness_access %I.lookup_impl_witness, element0 [symbolic = %impl.elem0.loc10_4.2 (constants.%impl.elem0)] // CHECK:STDOUT: %specific_impl_fn.loc10_4.2: = specific_impl_function %impl.elem0.loc10_4.2, @I.WithSelf.DoIt(%T.loc8_8.1) [symbolic = %specific_impl_fn.loc10_4.2 (constants.%specific_impl_fn)] // CHECK:STDOUT: // CHECK:STDOUT: fn() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %T.ref: %I.type = name_ref T, %T.loc8_8.2 [symbolic = %T.loc8_8.1 (constants.%T)] // CHECK:STDOUT: %T.as_type: type = facet_access_type %T.ref [symbolic = %T.binding.as_type (constants.%T.binding.as_type)] // CHECK:STDOUT: %.loc10_4.1: type = converted %T.ref, %T.as_type [symbolic = %T.binding.as_type (constants.%T.binding.as_type)] // CHECK:STDOUT: %DoIt.ref: %I.assoc_type = name_ref DoIt, @I.WithSelf.%assoc0 [concrete = constants.%assoc0] // CHECK:STDOUT: %impl.elem0.loc10_4.1: @Use.%.loc10_4.2 (%.f37) = impl_witness_access constants.%I.lookup_impl_witness, element0 [symbolic = %impl.elem0.loc10_4.2 (constants.%impl.elem0)] // CHECK:STDOUT: %specific_impl_fn.loc10_4.1: = specific_impl_function %impl.elem0.loc10_4.1, @I.WithSelf.DoIt(constants.%T) [symbolic = %specific_impl_fn.loc10_4.2 (constants.%specific_impl_fn)] // CHECK:STDOUT: %I.WithSelf.DoIt.call: init %empty_tuple.type = call %specific_impl_fn.loc10_4.1() // CHECK:STDOUT: // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @Use(constants.%T) { // CHECK:STDOUT: %T.loc8_8.1 => constants.%T // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- assoc_fn_using_self.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %I.type: type = facet_type <@I> [concrete] // CHECK:STDOUT: %I.assoc_type: type = assoc_entity_type @I [concrete] // CHECK:STDOUT: %assoc0: %I.assoc_type = assoc_entity element0, @I.WithSelf.%I.WithSelf.Make.decl [concrete] // CHECK:STDOUT: %T: %I.type = symbolic_binding T, 0 [symbolic] // CHECK:STDOUT: %T.binding.as_type: type = symbolic_binding_type T, 0, %T [symbolic] // CHECK:STDOUT: %.ff7: Core.Form = init_form %T.binding.as_type [symbolic] // CHECK:STDOUT: %pattern_type.422: type = pattern_type %T.binding.as_type [symbolic] // CHECK:STDOUT: %I.WithSelf.Make.type.f20: type = fn_type @I.WithSelf.Make, @I.WithSelf(%T) [symbolic] // CHECK:STDOUT: %I.lookup_impl_witness: = lookup_impl_witness %T, @I [symbolic] // CHECK:STDOUT: %.8e2: type = fn_type_with_self_type %I.WithSelf.Make.type.f20, %T [symbolic] // CHECK:STDOUT: %impl.elem0: %.8e2 = impl_witness_access %I.lookup_impl_witness, element0 [symbolic] // CHECK:STDOUT: %specific_impl_fn: = specific_impl_function %impl.elem0, @I.WithSelf.Make(%T) [symbolic] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic fn @Use(%T.loc8_8.2: %I.type) { // CHECK:STDOUT: // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: // CHECK:STDOUT: %I.lookup_impl_witness: = lookup_impl_witness %T.loc8_8.1, @I [symbolic = %I.lookup_impl_witness (constants.%I.lookup_impl_witness)] // CHECK:STDOUT: %I.WithSelf.Make.type: type = fn_type @I.WithSelf.Make, @I.WithSelf(%T.loc8_8.1) [symbolic = %I.WithSelf.Make.type (constants.%I.WithSelf.Make.type.f20)] // CHECK:STDOUT: %.loc10_11.2: type = fn_type_with_self_type %I.WithSelf.Make.type, %T.loc8_8.1 [symbolic = %.loc10_11.2 (constants.%.8e2)] // CHECK:STDOUT: %impl.elem0.loc10_11.2: @Use.%.loc10_11.2 (%.8e2) = impl_witness_access %I.lookup_impl_witness, element0 [symbolic = %impl.elem0.loc10_11.2 (constants.%impl.elem0)] // CHECK:STDOUT: %specific_impl_fn.loc10_11.2: = specific_impl_function %impl.elem0.loc10_11.2, @I.WithSelf.Make(%T.loc8_8.1) [symbolic = %specific_impl_fn.loc10_11.2 (constants.%specific_impl_fn)] // CHECK:STDOUT: // CHECK:STDOUT: fn() -> out %return.param: @Use.%T.binding.as_type (%T.binding.as_type) { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %T.ref.loc10: %I.type = name_ref T, %T.loc8_8.2 [symbolic = %T.loc8_8.1 (constants.%T)] // CHECK:STDOUT: %T.as_type.loc10: type = facet_access_type %T.ref.loc10 [symbolic = %T.binding.as_type (constants.%T.binding.as_type)] // CHECK:STDOUT: %.loc10_11.1: type = converted %T.ref.loc10, %T.as_type.loc10 [symbolic = %T.binding.as_type (constants.%T.binding.as_type)] // CHECK:STDOUT: %Make.ref: %I.assoc_type = name_ref Make, @I.WithSelf.%assoc0 [concrete = constants.%assoc0] // CHECK:STDOUT: %impl.elem0.loc10_11.1: @Use.%.loc10_11.2 (%.8e2) = impl_witness_access constants.%I.lookup_impl_witness, element0 [symbolic = %impl.elem0.loc10_11.2 (constants.%impl.elem0)] // CHECK:STDOUT: %specific_impl_fn.loc10_11.1: = specific_impl_function %impl.elem0.loc10_11.1, @I.WithSelf.Make(constants.%T) [symbolic = %specific_impl_fn.loc10_11.2 (constants.%specific_impl_fn)] // CHECK:STDOUT: // CHECK:STDOUT: %I.WithSelf.Make.call: init @Use.%T.binding.as_type (%T.binding.as_type) to %.loc8_18.1 = call %specific_impl_fn.loc10_11.1() // CHECK:STDOUT: return %I.WithSelf.Make.call to %return.param // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @Use(constants.%T) { // CHECK:STDOUT: %T.loc8_8.1 => constants.%T // CHECK:STDOUT: %T.binding.as_type => constants.%T.binding.as_type // CHECK:STDOUT: %.loc8_18.2 => constants.%.ff7 // CHECK:STDOUT: %pattern_type => constants.%pattern_type.422 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- access_assoc_method.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %I.type: type = facet_type <@I> [concrete] // CHECK:STDOUT: %I.assoc_type: type = assoc_entity_type @I [concrete] // CHECK:STDOUT: %assoc0: %I.assoc_type = assoc_entity element0, @I.WithSelf.%I.WithSelf.Copy.decl [concrete] // CHECK:STDOUT: %pattern_type.9d9: type = pattern_type %I.type [concrete] // CHECK:STDOUT: %T: %I.type = symbolic_binding T, 0 [symbolic] // CHECK:STDOUT: %T.binding.as_type: type = symbolic_binding_type T, 0, %T [symbolic] // CHECK:STDOUT: %pattern_type.422: type = pattern_type %T.binding.as_type [symbolic] // CHECK:STDOUT: %.ff7: Core.Form = init_form %T.binding.as_type [symbolic] // CHECK:STDOUT: %Use.type: type = fn_type @Use [concrete] // CHECK:STDOUT: %Use: %Use.type = struct_value () [concrete] // CHECK:STDOUT: %require_complete: = require_complete_type %T.binding.as_type [symbolic] // CHECK:STDOUT: %I.WithSelf.Copy.type.035: type = fn_type @I.WithSelf.Copy, @I.WithSelf(%T) [symbolic] // CHECK:STDOUT: %I.lookup_impl_witness: = lookup_impl_witness %T, @I [symbolic] // CHECK:STDOUT: %.fa6: type = fn_type_with_self_type %I.WithSelf.Copy.type.035, %T [symbolic] // CHECK:STDOUT: %impl.elem0: %.fa6 = impl_witness_access %I.lookup_impl_witness, element0 [symbolic] // CHECK:STDOUT: %specific_impl_fn: = specific_impl_function %impl.elem0, @I.WithSelf.Copy(%T) [symbolic] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: %Use.decl: %Use.type = fn_decl @Use [concrete = constants.%Use] { // CHECK:STDOUT: %T.patt: %pattern_type.9d9 = symbolic_binding_pattern T, 0 [concrete] // CHECK:STDOUT: %x.patt: @Use.%pattern_type (%pattern_type.422) = value_binding_pattern x [concrete] // CHECK:STDOUT: %x.param_patt: @Use.%pattern_type (%pattern_type.422) = value_param_pattern %x.patt [concrete] // CHECK:STDOUT: %return.patt: @Use.%pattern_type (%pattern_type.422) = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: @Use.%pattern_type (%pattern_type.422) = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %T.ref.loc9_24: %I.type = name_ref T, %T.loc9_8.2 [symbolic = %T.loc9_8.1 (constants.%T)] // CHECK:STDOUT: %T.as_type.loc9_24: type = facet_access_type %T.ref.loc9_24 [symbolic = %T.binding.as_type (constants.%T.binding.as_type)] // CHECK:STDOUT: %.loc9_24.3: type = converted %T.ref.loc9_24, %T.as_type.loc9_24 [symbolic = %T.binding.as_type (constants.%T.binding.as_type)] // CHECK:STDOUT: %.loc9_24.4: Core.Form = init_form %.loc9_24.3 [symbolic = %.loc9_24.2 (constants.%.ff7)] // CHECK:STDOUT: %.loc9_12: type = splice_block %I.ref [concrete = constants.%I.type] { // CHECK:STDOUT: // CHECK:STDOUT: %I.ref: type = name_ref I, file.%I.decl [concrete = constants.%I.type] // CHECK:STDOUT: } // CHECK:STDOUT: %T.loc9_8.2: %I.type = symbolic_binding T, 0 [symbolic = %T.loc9_8.1 (constants.%T)] // CHECK:STDOUT: %x.param: @Use.%T.binding.as_type (%T.binding.as_type) = value_param call_param0 // CHECK:STDOUT: %.loc9_18.1: type = splice_block %.loc9_18.2 [symbolic = %T.binding.as_type (constants.%T.binding.as_type)] { // CHECK:STDOUT: %T.ref.loc9_18: %I.type = name_ref T, %T.loc9_8.2 [symbolic = %T.loc9_8.1 (constants.%T)] // CHECK:STDOUT: %T.as_type.loc9_18: type = facet_access_type %T.ref.loc9_18 [symbolic = %T.binding.as_type (constants.%T.binding.as_type)] // CHECK:STDOUT: %.loc9_18.2: type = converted %T.ref.loc9_18, %T.as_type.loc9_18 [symbolic = %T.binding.as_type (constants.%T.binding.as_type)] // CHECK:STDOUT: } // CHECK:STDOUT: %x: @Use.%T.binding.as_type (%T.binding.as_type) = value_binding x, %x.param // CHECK:STDOUT: %return.param: ref @Use.%T.binding.as_type (%T.binding.as_type) = out_param call_param1 // CHECK:STDOUT: %return: ref @Use.%T.binding.as_type (%T.binding.as_type) = return_slot %return.param // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic fn @Use(%T.loc9_8.2: %I.type) { // CHECK:STDOUT: %T.loc9_8.1: %I.type = symbolic_binding T, 0 [symbolic = %T.loc9_8.1 (constants.%T)] // CHECK:STDOUT: %T.binding.as_type: type = symbolic_binding_type T, 0, %T.loc9_8.1 [symbolic = %T.binding.as_type (constants.%T.binding.as_type)] // CHECK:STDOUT: %pattern_type: type = pattern_type %T.binding.as_type [symbolic = %pattern_type (constants.%pattern_type.422)] // CHECK:STDOUT: %.loc9_24.2: Core.Form = init_form %T.binding.as_type [symbolic = %.loc9_24.2 (constants.%.ff7)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete: = require_complete_type %T.binding.as_type [symbolic = %require_complete (constants.%require_complete)] // CHECK:STDOUT: %I.lookup_impl_witness: = lookup_impl_witness %T.loc9_8.1, @I [symbolic = %I.lookup_impl_witness (constants.%I.lookup_impl_witness)] // CHECK:STDOUT: %I.WithSelf.Copy.type: type = fn_type @I.WithSelf.Copy, @I.WithSelf(%T.loc9_8.1) [symbolic = %I.WithSelf.Copy.type (constants.%I.WithSelf.Copy.type.035)] // CHECK:STDOUT: %.loc10: type = fn_type_with_self_type %I.WithSelf.Copy.type, %T.loc9_8.1 [symbolic = %.loc10 (constants.%.fa6)] // CHECK:STDOUT: %impl.elem0.loc10_11.2: @Use.%.loc10 (%.fa6) = impl_witness_access %I.lookup_impl_witness, element0 [symbolic = %impl.elem0.loc10_11.2 (constants.%impl.elem0)] // CHECK:STDOUT: %specific_impl_fn.loc10_11.2: = specific_impl_function %impl.elem0.loc10_11.2, @I.WithSelf.Copy(%T.loc9_8.1) [symbolic = %specific_impl_fn.loc10_11.2 (constants.%specific_impl_fn)] // CHECK:STDOUT: // CHECK:STDOUT: fn(%x.param: @Use.%T.binding.as_type (%T.binding.as_type)) -> out %return.param: @Use.%T.binding.as_type (%T.binding.as_type) { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %x.ref: @Use.%T.binding.as_type (%T.binding.as_type) = name_ref x, %x // CHECK:STDOUT: %Copy.ref: %I.assoc_type = name_ref Copy, @I.WithSelf.%assoc0 [concrete = constants.%assoc0] // CHECK:STDOUT: %impl.elem0.loc10_11.1: @Use.%.loc10 (%.fa6) = impl_witness_access constants.%I.lookup_impl_witness, element0 [symbolic = %impl.elem0.loc10_11.2 (constants.%impl.elem0)] // CHECK:STDOUT: %bound_method.loc10_11: = bound_method %x.ref, %impl.elem0.loc10_11.1 // CHECK:STDOUT: %specific_impl_fn.loc10_11.1: = specific_impl_function %impl.elem0.loc10_11.1, @I.WithSelf.Copy(constants.%T) [symbolic = %specific_impl_fn.loc10_11.2 (constants.%specific_impl_fn)] // CHECK:STDOUT: %bound_method.loc10_17: = bound_method %x.ref, %specific_impl_fn.loc10_11.1 // CHECK:STDOUT: %.loc9_24.1: ref @Use.%T.binding.as_type (%T.binding.as_type) = splice_block %return.param {} // CHECK:STDOUT: %I.WithSelf.Copy.call: init @Use.%T.binding.as_type (%T.binding.as_type) to %.loc9_24.1 = call %bound_method.loc10_17(%x.ref) // CHECK:STDOUT: return %I.WithSelf.Copy.call to %return.param // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @Use(constants.%T) { // CHECK:STDOUT: %T.loc9_8.1 => constants.%T // CHECK:STDOUT: %T.binding.as_type => constants.%T.binding.as_type // CHECK:STDOUT: %pattern_type => constants.%pattern_type.422 // CHECK:STDOUT: %.loc9_24.2 => constants.%.ff7 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- access_selfless_method.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %I.type: type = facet_type <@I> [concrete] // CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] // CHECK:STDOUT: %I.assoc_type: type = assoc_entity_type @I [concrete] // CHECK:STDOUT: %assoc0: %I.assoc_type = assoc_entity element0, @I.WithSelf.%I.WithSelf.Hello.decl [concrete] // CHECK:STDOUT: %T: %I.type = symbolic_binding T, 0 [symbolic] // CHECK:STDOUT: %T.binding.as_type: type = symbolic_binding_type T, 0, %T [symbolic] // CHECK:STDOUT: %pattern_type.422: type = pattern_type %T.binding.as_type [symbolic] // CHECK:STDOUT: %I.WithSelf.Hello.type.3eb: type = fn_type @I.WithSelf.Hello, @I.WithSelf(%T) [symbolic] // CHECK:STDOUT: %I.lookup_impl_witness: = lookup_impl_witness %T, @I [symbolic] // CHECK:STDOUT: %.8de: type = fn_type_with_self_type %I.WithSelf.Hello.type.3eb, %T [symbolic] // CHECK:STDOUT: %impl.elem0: %.8de = impl_witness_access %I.lookup_impl_witness, element0 [symbolic] // CHECK:STDOUT: %specific_impl_fn: = specific_impl_function %impl.elem0, @I.WithSelf.Hello(%T) [symbolic] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic fn @Use(%T.loc8_8.2: %I.type) { // CHECK:STDOUT: // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: // CHECK:STDOUT: %I.lookup_impl_witness: = lookup_impl_witness %T.loc8_8.1, @I [symbolic = %I.lookup_impl_witness (constants.%I.lookup_impl_witness)] // CHECK:STDOUT: %I.WithSelf.Hello.type: type = fn_type @I.WithSelf.Hello, @I.WithSelf(%T.loc8_8.1) [symbolic = %I.WithSelf.Hello.type (constants.%I.WithSelf.Hello.type.3eb)] // CHECK:STDOUT: %.loc10: type = fn_type_with_self_type %I.WithSelf.Hello.type, %T.loc8_8.1 [symbolic = %.loc10 (constants.%.8de)] // CHECK:STDOUT: %impl.elem0.loc10_4.2: @Use.%.loc10 (%.8de) = impl_witness_access %I.lookup_impl_witness, element0 [symbolic = %impl.elem0.loc10_4.2 (constants.%impl.elem0)] // CHECK:STDOUT: %specific_impl_fn.loc10_4.2: = specific_impl_function %impl.elem0.loc10_4.2, @I.WithSelf.Hello(%T.loc8_8.1) [symbolic = %specific_impl_fn.loc10_4.2 (constants.%specific_impl_fn)] // CHECK:STDOUT: // CHECK:STDOUT: fn(%x.param: @Use.%T.binding.as_type (%T.binding.as_type)) { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %x.ref: @Use.%T.binding.as_type (%T.binding.as_type) = name_ref x, %x // CHECK:STDOUT: %Hello.ref: %I.assoc_type = name_ref Hello, @I.WithSelf.%assoc0 [concrete = constants.%assoc0] // CHECK:STDOUT: %impl.elem0.loc10_4.1: @Use.%.loc10 (%.8de) = impl_witness_access constants.%I.lookup_impl_witness, element0 [symbolic = %impl.elem0.loc10_4.2 (constants.%impl.elem0)] // CHECK:STDOUT: %specific_impl_fn.loc10_4.1: = specific_impl_function %impl.elem0.loc10_4.1, @I.WithSelf.Hello(constants.%T) [symbolic = %specific_impl_fn.loc10_4.2 (constants.%specific_impl_fn)] // CHECK:STDOUT: %I.WithSelf.Hello.call: init %empty_tuple.type = call %specific_impl_fn.loc10_4.1() // CHECK:STDOUT: // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @Use(constants.%T) { // CHECK:STDOUT: %T.loc8_8.1 => constants.%T // CHECK:STDOUT: %T.binding.as_type => constants.%T.binding.as_type // CHECK:STDOUT: %pattern_type => constants.%pattern_type.422 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- access_assoc_method_indirect.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %I.type: type = facet_type <@I> [concrete] // CHECK:STDOUT: %I.assoc_type: type = assoc_entity_type @I [concrete] // CHECK:STDOUT: %assoc0: %I.assoc_type = assoc_entity element0, @I.WithSelf.%I.WithSelf.Copy.decl [concrete] // CHECK:STDOUT: %T: %I.type = symbolic_binding T, 0 [symbolic] // CHECK:STDOUT: %T.binding.as_type: type = symbolic_binding_type T, 0, %T [symbolic] // CHECK:STDOUT: %pattern_type.422: type = pattern_type %T.binding.as_type [symbolic] // CHECK:STDOUT: %.ff7: Core.Form = init_form %T.binding.as_type [symbolic] // CHECK:STDOUT: %I.WithSelf.Copy.type.035: type = fn_type @I.WithSelf.Copy, @I.WithSelf(%T) [symbolic] // CHECK:STDOUT: %I.lookup_impl_witness: = lookup_impl_witness %T, @I [symbolic] // CHECK:STDOUT: %.fa6: type = fn_type_with_self_type %I.WithSelf.Copy.type.035, %T [symbolic] // CHECK:STDOUT: %impl.elem0: %.fa6 = impl_witness_access %I.lookup_impl_witness, element0 [symbolic] // CHECK:STDOUT: %specific_impl_fn: = specific_impl_function %impl.elem0, @I.WithSelf.Copy(%T) [symbolic] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic fn @UseIndirect(%T.loc8_16.2: %I.type) { // CHECK:STDOUT: // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: // CHECK:STDOUT: %I.lookup_impl_witness: = lookup_impl_witness %T.loc8_16.1, @I [symbolic = %I.lookup_impl_witness (constants.%I.lookup_impl_witness)] // CHECK:STDOUT: %I.WithSelf.Copy.type: type = fn_type @I.WithSelf.Copy, @I.WithSelf(%T.loc8_16.1) [symbolic = %I.WithSelf.Copy.type (constants.%I.WithSelf.Copy.type.035)] // CHECK:STDOUT: %.loc10_14.2: type = fn_type_with_self_type %I.WithSelf.Copy.type, %T.loc8_16.1 [symbolic = %.loc10_14.2 (constants.%.fa6)] // CHECK:STDOUT: %impl.elem0.loc10_14.2: @UseIndirect.%.loc10_14.2 (%.fa6) = impl_witness_access %I.lookup_impl_witness, element0 [symbolic = %impl.elem0.loc10_14.2 (constants.%impl.elem0)] // CHECK:STDOUT: %specific_impl_fn.loc10_14.2: = specific_impl_function %impl.elem0.loc10_14.2, @I.WithSelf.Copy(%T.loc8_16.1) [symbolic = %specific_impl_fn.loc10_14.2 (constants.%specific_impl_fn)] // CHECK:STDOUT: // CHECK:STDOUT: fn(%x.param: @UseIndirect.%T.binding.as_type (%T.binding.as_type)) -> out %return.param: @UseIndirect.%T.binding.as_type (%T.binding.as_type) { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %x.ref: @UseIndirect.%T.binding.as_type (%T.binding.as_type) = name_ref x, %x // CHECK:STDOUT: %T.ref.loc10: %I.type = name_ref T, %T.loc8_16.2 [symbolic = %T.loc8_16.1 (constants.%T)] // CHECK:STDOUT: %T.as_type.loc10: type = facet_access_type %T.ref.loc10 [symbolic = %T.binding.as_type (constants.%T.binding.as_type)] // CHECK:STDOUT: %.loc10_14.1: type = converted %T.ref.loc10, %T.as_type.loc10 [symbolic = %T.binding.as_type (constants.%T.binding.as_type)] // CHECK:STDOUT: %Copy.ref: %I.assoc_type = name_ref Copy, @I.WithSelf.%assoc0 [concrete = constants.%assoc0] // CHECK:STDOUT: %impl.elem0.loc10_14.1: @UseIndirect.%.loc10_14.2 (%.fa6) = impl_witness_access constants.%I.lookup_impl_witness, element0 [symbolic = %impl.elem0.loc10_14.2 (constants.%impl.elem0)] // CHECK:STDOUT: %bound_method.loc10_11: = bound_method %x.ref, %impl.elem0.loc10_14.1 // CHECK:STDOUT: %specific_impl_fn.loc10_14.1: = specific_impl_function %impl.elem0.loc10_14.1, @I.WithSelf.Copy(constants.%T) [symbolic = %specific_impl_fn.loc10_14.2 (constants.%specific_impl_fn)] // CHECK:STDOUT: %bound_method.loc10_21: = bound_method %x.ref, %specific_impl_fn.loc10_14.1 // CHECK:STDOUT: // CHECK:STDOUT: %I.WithSelf.Copy.call: init @UseIndirect.%T.binding.as_type (%T.binding.as_type) to %.loc8_32.1 = call %bound_method.loc10_21(%x.ref) // CHECK:STDOUT: return %I.WithSelf.Copy.call to %return.param // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @UseIndirect(constants.%T) { // CHECK:STDOUT: %T.loc8_16.1 => constants.%T // CHECK:STDOUT: %T.binding.as_type => constants.%T.binding.as_type // CHECK:STDOUT: %pattern_type => constants.%pattern_type.422 // CHECK:STDOUT: %.loc8_32.2 => constants.%.ff7 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- access_constant_in_self_facet.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %A.type: type = facet_type <@A> [concrete] // CHECK:STDOUT: %Self: %A.type = symbolic_binding Self, 0 [symbolic] // CHECK:STDOUT: %A.assoc_type: type = assoc_entity_type @A [concrete] // CHECK:STDOUT: %assoc0: %A.assoc_type = assoc_entity element0, @A.WithSelf.%X [concrete] // CHECK:STDOUT: %.Self.091: %A.type = symbolic_binding .Self [symbolic_self] // CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] // CHECK:STDOUT: %.Self.binding.as_type: type = symbolic_binding_type .Self, %.Self.091 [symbolic_self] // CHECK:STDOUT: %A.lookup_impl_witness.6a5: = lookup_impl_witness %.Self.091, @A [symbolic_self] // CHECK:STDOUT: %impl.elem0: type = impl_witness_access %A.lookup_impl_witness.6a5, element0 [symbolic_self] // CHECK:STDOUT: %empty_tuple: %empty_tuple.type = tuple_value () [concrete] // CHECK:STDOUT: %A_where.type: type = facet_type <@A where %impl.elem0 = %empty_tuple.type> [concrete] // CHECK:STDOUT: %pattern_type.dc8: type = pattern_type %A_where.type [concrete] // CHECK:STDOUT: %AA: %A_where.type = symbolic_binding AA, 0 [symbolic] // CHECK:STDOUT: %AA.binding.as_type: type = symbolic_binding_type AA, 0, %AA [symbolic] // CHECK:STDOUT: %A.lookup_impl_witness.6c3: = lookup_impl_witness %AA, @A [symbolic] // CHECK:STDOUT: %A.facet: %A.type = facet_value %AA.binding.as_type, (%A.lookup_impl_witness.6c3) [symbolic] // CHECK:STDOUT: %.262: Core.Form = init_form %empty_tuple.type [concrete] // CHECK:STDOUT: %pattern_type.cb1: type = pattern_type %empty_tuple.type [concrete] // CHECK:STDOUT: %F.type: type = fn_type @F [concrete] // CHECK:STDOUT: %F: %F.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: %A.decl: type = interface_decl @A [concrete = constants.%A.type] {} {} // CHECK:STDOUT: %F.decl: %F.type = fn_decl @F [concrete = constants.%F] { // CHECK:STDOUT: %AA.patt: %pattern_type.dc8 = symbolic_binding_pattern AA, 0 [concrete] // CHECK:STDOUT: %return.patt: %pattern_type.cb1 = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: %pattern_type.cb1 = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %AA.ref: %A_where.type = name_ref AA, %AA.loc6_6.2 [symbolic = %AA.loc6_6.1 (constants.%AA)] // CHECK:STDOUT: %AA.as_type: type = facet_access_type %AA.ref [symbolic = %AA.binding.as_type (constants.%AA.binding.as_type)] // CHECK:STDOUT: %.loc6_33.1: type = converted %AA.ref, %AA.as_type [symbolic = %AA.binding.as_type (constants.%AA.binding.as_type)] // CHECK:STDOUT: %X.ref.loc6_33: %A.assoc_type = name_ref X, @X.%assoc0 [concrete = constants.%assoc0] // CHECK:STDOUT: %impl.elem0.loc6_33: type = impl_witness_access constants.%A.lookup_impl_witness.6c3, element0 [concrete = constants.%empty_tuple.type] // CHECK:STDOUT: %.loc6_33.2: Core.Form = init_form %impl.elem0.loc6_33 [concrete = constants.%.262] // CHECK:STDOUT: %.loc6_13.1: type = splice_block %.loc6_13.2 [concrete = constants.%A_where.type] { // CHECK:STDOUT: // CHECK:STDOUT: %A.ref: type = name_ref A, file.%A.decl [concrete = constants.%A.type] // CHECK:STDOUT: // CHECK:STDOUT: %.Self.ref: %A.type = name_ref .Self, %.Self.2 [symbolic_self = constants.%.Self.091] // CHECK:STDOUT: %.Self.as_type: type = facet_access_type %.Self.ref [symbolic_self = constants.%.Self.binding.as_type] // CHECK:STDOUT: %.loc6_19: type = converted %.Self.ref, %.Self.as_type [symbolic_self = constants.%.Self.binding.as_type] // CHECK:STDOUT: %X.ref.loc6_19: %A.assoc_type = name_ref X, @X.%assoc0 [concrete = constants.%assoc0] // CHECK:STDOUT: %impl.elem0.loc6_19: type = impl_witness_access constants.%A.lookup_impl_witness.6a5, element0 [symbolic_self = constants.%impl.elem0] // CHECK:STDOUT: %.loc6_25.1: %empty_tuple.type = tuple_literal () [concrete = constants.%empty_tuple] // CHECK:STDOUT: %.loc6_25.2: type = converted %.loc6_25.1, constants.%empty_tuple.type [concrete = constants.%empty_tuple.type] // CHECK:STDOUT: %.loc6_13.2: type = where_expr %.Self.2 [concrete = constants.%A_where.type] { // CHECK:STDOUT: requirement_base_facet_type constants.%A.type // CHECK:STDOUT: requirement_rewrite %impl.elem0.loc6_19, %.loc6_25.2 // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: %AA.loc6_6.2: %A_where.type = symbolic_binding AA, 0 [symbolic = %AA.loc6_6.1 (constants.%AA)] // CHECK:STDOUT: %return.param: ref %empty_tuple.type = out_param call_param0 // CHECK:STDOUT: %return: ref %empty_tuple.type = return_slot %return.param // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: interface @A { // CHECK:STDOUT: %Self: %A.type = symbolic_binding Self, 0 [symbolic = constants.%Self] // CHECK:STDOUT: %A.WithSelf.decl = interface_with_self_decl @A [concrete] // CHECK:STDOUT: // CHECK:STDOUT: !with Self: // CHECK:STDOUT: %X: type = assoc_const_decl @X [concrete] { // CHECK:STDOUT: %assoc0: %A.assoc_type = assoc_entity element0, @A.WithSelf.%X [concrete = constants.%assoc0] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = %Self // CHECK:STDOUT: .X = @X.%assoc0 // CHECK:STDOUT: witness = (@A.WithSelf.%X) // CHECK:STDOUT: // CHECK:STDOUT: !requires: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic fn @F(%AA.loc6_6.2: %A_where.type) { // CHECK:STDOUT: %AA.loc6_6.1: %A_where.type = symbolic_binding AA, 0 [symbolic = %AA.loc6_6.1 (constants.%AA)] // CHECK:STDOUT: %AA.binding.as_type: type = symbolic_binding_type AA, 0, %AA.loc6_6.1 [symbolic = %AA.binding.as_type (constants.%AA.binding.as_type)] // CHECK:STDOUT: %A.lookup_impl_witness: = lookup_impl_witness %AA.loc6_6.1, @A [symbolic = %A.lookup_impl_witness (constants.%A.lookup_impl_witness.6c3)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: // CHECK:STDOUT: fn() -> out %return.param: %empty_tuple.type { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %.loc7_11.1: %empty_tuple.type = tuple_literal () [concrete = constants.%empty_tuple] // CHECK:STDOUT: %.loc7_11.2: init %empty_tuple.type = tuple_init () [concrete = constants.%empty_tuple] // CHECK:STDOUT: %.loc7_12: init %empty_tuple.type = converted %.loc7_11.1, %.loc7_11.2 [concrete = constants.%empty_tuple] // CHECK:STDOUT: return %.loc7_12 // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @A.WithSelf(constants.%Self) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @A.WithSelf(constants.%.Self.091) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @A.WithSelf(constants.%AA) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @A.WithSelf(constants.%A.facet) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @F(constants.%AA) { // CHECK:STDOUT: %AA.loc6_6.1 => constants.%AA // CHECK:STDOUT: %AA.binding.as_type => constants.%AA.binding.as_type // CHECK:STDOUT: %A.lookup_impl_witness => constants.%A.lookup_impl_witness.6c3 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- access_constant_in_self_facet_with_multiple_interfaces.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %A.type: type = facet_type <@A> [concrete] // CHECK:STDOUT: %A.assoc_type: type = assoc_entity_type @A [concrete] // CHECK:STDOUT: %assoc0.df7: %A.assoc_type = assoc_entity element0, @A.WithSelf.%X [concrete] // CHECK:STDOUT: %B.type: type = facet_type <@B> [concrete] // CHECK:STDOUT: %B.assoc_type: type = assoc_entity_type @B [concrete] // CHECK:STDOUT: %assoc0.6fa: %B.assoc_type = assoc_entity element0, @B.WithSelf.%Y [concrete] // CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] // CHECK:STDOUT: %BitAndWith.type.b10: type = facet_type <@BitAndWith, @BitAndWith(type)> [concrete] // CHECK:STDOUT: %BitAndWith.impl_witness: = impl_witness imports.%BitAndWith.impl_witness_table [concrete] // CHECK:STDOUT: %BitAndWith.facet: %BitAndWith.type.b10 = facet_value type, (%BitAndWith.impl_witness) [concrete] // CHECK:STDOUT: %BitAndWith.WithSelf.Op.type.4bd: type = fn_type @BitAndWith.WithSelf.Op, @BitAndWith.WithSelf(type, %BitAndWith.facet) [concrete] // CHECK:STDOUT: %.d15: type = fn_type_with_self_type %BitAndWith.WithSelf.Op.type.4bd, %BitAndWith.facet [concrete] // CHECK:STDOUT: %type.as.BitAndWith.impl.Op.type: type = fn_type @type.as.BitAndWith.impl.Op [concrete] // CHECK:STDOUT: %type.as.BitAndWith.impl.Op: %type.as.BitAndWith.impl.Op.type = struct_value () [concrete] // CHECK:STDOUT: %type.as.BitAndWith.impl.Op.bound: = bound_method %A.type, %type.as.BitAndWith.impl.Op [concrete] // CHECK:STDOUT: %facet_type.9bb: type = facet_type <@A & @B> [concrete] // CHECK:STDOUT: %.Self.e7e: %facet_type.9bb = symbolic_binding .Self [symbolic_self] // CHECK:STDOUT: %.Self.binding.as_type: type = symbolic_binding_type .Self, %.Self.e7e [symbolic_self] // CHECK:STDOUT: %A.lookup_impl_witness.6b3: = lookup_impl_witness %.Self.e7e, @A [symbolic_self] // CHECK:STDOUT: %impl.elem0.eb8: type = impl_witness_access %A.lookup_impl_witness.6b3, element0 [symbolic_self] // CHECK:STDOUT: %empty_tuple: %empty_tuple.type = tuple_value () [concrete] // CHECK:STDOUT: %B.lookup_impl_witness.d4f: = lookup_impl_witness %.Self.e7e, @B [symbolic_self] // CHECK:STDOUT: %impl.elem0.11d: type = impl_witness_access %B.lookup_impl_witness.d4f, element0 [symbolic_self] // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %empty_struct: %empty_struct_type = struct_value () [concrete] // CHECK:STDOUT: %facet_type.82c: type = facet_type <@A & @B where %impl.elem0.eb8 = %empty_tuple.type and %impl.elem0.11d = %empty_struct_type> [concrete] // CHECK:STDOUT: %pattern_type.77e: type = pattern_type %facet_type.82c [concrete] // CHECK:STDOUT: %AB: %facet_type.82c = symbolic_binding AB, 0 [symbolic] // CHECK:STDOUT: %AB.binding.as_type: type = symbolic_binding_type AB, 0, %AB [symbolic] // CHECK:STDOUT: %A.lookup_impl_witness.1b9: = lookup_impl_witness %AB, @A [symbolic] // CHECK:STDOUT: %.262: Core.Form = init_form %empty_tuple.type [concrete] // CHECK:STDOUT: %pattern_type.cb1: type = pattern_type %empty_tuple.type [concrete] // CHECK:STDOUT: %F.type: type = fn_type @F [concrete] // CHECK:STDOUT: %F: %F.type = struct_value () [concrete] // CHECK:STDOUT: %B.lookup_impl_witness.97b: = lookup_impl_witness %AB, @B [symbolic] // CHECK:STDOUT: %.469: Core.Form = init_form %empty_struct_type [concrete] // CHECK:STDOUT: %pattern_type.a96: type = pattern_type %empty_struct_type [concrete] // CHECK:STDOUT: %G.type: type = fn_type @G [concrete] // CHECK:STDOUT: %G: %G.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core.import_ref.8d3: %type.as.BitAndWith.impl.Op.type = import_ref Core//prelude/parts/as, loc{{\d+_\d+}}, loaded [concrete = constants.%type.as.BitAndWith.impl.Op] // CHECK:STDOUT: %BitAndWith.impl_witness_table = impl_witness_table (%Core.import_ref.8d3), @type.as.BitAndWith.impl [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: %F.decl: %F.type = fn_decl @F [concrete = constants.%F] { // CHECK:STDOUT: %AB.patt: %pattern_type.77e = symbolic_binding_pattern AB, 0 [concrete] // CHECK:STDOUT: %return.patt: %pattern_type.cb1 = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: %pattern_type.cb1 = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %AB.ref: %facet_type.82c = name_ref AB, %AB.loc14_6.2 [symbolic = %AB.loc14_6.1 (constants.%AB)] // CHECK:STDOUT: %AB.as_type: type = facet_access_type %AB.ref [symbolic = %AB.binding.as_type (constants.%AB.binding.as_type)] // CHECK:STDOUT: %.loc14_49.1: type = converted %AB.ref, %AB.as_type [symbolic = %AB.binding.as_type (constants.%AB.binding.as_type)] // CHECK:STDOUT: %X.ref.loc14_49: %A.assoc_type = name_ref X, @X.%assoc0 [concrete = constants.%assoc0.df7] // CHECK:STDOUT: %impl.elem0.loc14_49: type = impl_witness_access constants.%A.lookup_impl_witness.1b9, element0 [concrete = constants.%empty_tuple.type] // CHECK:STDOUT: %.loc14_49.2: Core.Form = init_form %impl.elem0.loc14_49 [concrete = constants.%.262] // CHECK:STDOUT: %.loc14_17.1: type = splice_block %.loc14_17.2 [concrete = constants.%facet_type.82c] { // CHECK:STDOUT: // CHECK:STDOUT: %A.ref: type = name_ref A, file.%A.decl [concrete = constants.%A.type] // CHECK:STDOUT: %B.ref: type = name_ref B, file.%B.decl [concrete = constants.%B.type] // CHECK:STDOUT: %impl.elem0.loc14_13: %.d15 = impl_witness_access constants.%BitAndWith.impl_witness, element0 [concrete = constants.%type.as.BitAndWith.impl.Op] // CHECK:STDOUT: %bound_method: = bound_method %A.ref, %impl.elem0.loc14_13 [concrete = constants.%type.as.BitAndWith.impl.Op.bound] // CHECK:STDOUT: %type.as.BitAndWith.impl.Op.call: init type = call %bound_method(%A.ref, %B.ref) [concrete = constants.%facet_type.9bb] // CHECK:STDOUT: %.loc14_13.1: type = value_of_initializer %type.as.BitAndWith.impl.Op.call [concrete = constants.%facet_type.9bb] // CHECK:STDOUT: %.loc14_13.2: type = converted %type.as.BitAndWith.impl.Op.call, %.loc14_13.1 [concrete = constants.%facet_type.9bb] // CHECK:STDOUT: // CHECK:STDOUT: %.Self.ref.loc14_23: %facet_type.9bb = name_ref .Self, %.Self.2 [symbolic_self = constants.%.Self.e7e] // CHECK:STDOUT: %.Self.as_type.loc14_23: type = facet_access_type %.Self.ref.loc14_23 [symbolic_self = constants.%.Self.binding.as_type] // CHECK:STDOUT: %.loc14_23: type = converted %.Self.ref.loc14_23, %.Self.as_type.loc14_23 [symbolic_self = constants.%.Self.binding.as_type] // CHECK:STDOUT: %X.ref.loc14_23: %A.assoc_type = name_ref X, @X.%assoc0 [concrete = constants.%assoc0.df7] // CHECK:STDOUT: %impl.elem0.loc14_23: type = impl_witness_access constants.%A.lookup_impl_witness.6b3, element0 [symbolic_self = constants.%impl.elem0.eb8] // CHECK:STDOUT: %.loc14_29.1: %empty_tuple.type = tuple_literal () [concrete = constants.%empty_tuple] // CHECK:STDOUT: %.loc14_29.2: type = converted %.loc14_29.1, constants.%empty_tuple.type [concrete = constants.%empty_tuple.type] // CHECK:STDOUT: %.Self.ref.loc14_35: %facet_type.9bb = name_ref .Self, %.Self.2 [symbolic_self = constants.%.Self.e7e] // CHECK:STDOUT: %.Self.as_type.loc14_35: type = facet_access_type %.Self.ref.loc14_35 [symbolic_self = constants.%.Self.binding.as_type] // CHECK:STDOUT: %.loc14_35: type = converted %.Self.ref.loc14_35, %.Self.as_type.loc14_35 [symbolic_self = constants.%.Self.binding.as_type] // CHECK:STDOUT: %Y.ref: %B.assoc_type = name_ref Y, @Y.%assoc0 [concrete = constants.%assoc0.6fa] // CHECK:STDOUT: %impl.elem0.loc14_35: type = impl_witness_access constants.%B.lookup_impl_witness.d4f, element0 [symbolic_self = constants.%impl.elem0.11d] // CHECK:STDOUT: %.loc14_41.1: %empty_struct_type = struct_literal () [concrete = constants.%empty_struct] // CHECK:STDOUT: %.loc14_41.2: type = converted %.loc14_41.1, constants.%empty_struct_type [concrete = constants.%empty_struct_type] // CHECK:STDOUT: %.loc14_17.2: type = where_expr %.Self.2 [concrete = constants.%facet_type.82c] { // CHECK:STDOUT: requirement_base_facet_type constants.%facet_type.9bb // CHECK:STDOUT: requirement_rewrite %impl.elem0.loc14_23, %.loc14_29.2 // CHECK:STDOUT: requirement_rewrite %impl.elem0.loc14_35, %.loc14_41.2 // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: %AB.loc14_6.2: %facet_type.82c = symbolic_binding AB, 0 [symbolic = %AB.loc14_6.1 (constants.%AB)] // CHECK:STDOUT: %return.param: ref %empty_tuple.type = out_param call_param0 // CHECK:STDOUT: %return: ref %empty_tuple.type = return_slot %return.param // CHECK:STDOUT: } // CHECK:STDOUT: %G.decl: %G.type = fn_decl @G [concrete = constants.%G] { // CHECK:STDOUT: %AB.patt: %pattern_type.77e = symbolic_binding_pattern AB, 0 [concrete] // CHECK:STDOUT: %return.patt: %pattern_type.a96 = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: %pattern_type.a96 = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %AB.ref: %facet_type.82c = name_ref AB, %AB.loc18_6.2 [symbolic = %AB.loc18_6.1 (constants.%AB)] // CHECK:STDOUT: %AB.as_type: type = facet_access_type %AB.ref [symbolic = %AB.binding.as_type (constants.%AB.binding.as_type)] // CHECK:STDOUT: %.loc18_49.1: type = converted %AB.ref, %AB.as_type [symbolic = %AB.binding.as_type (constants.%AB.binding.as_type)] // CHECK:STDOUT: %Y.ref.loc18_49: %B.assoc_type = name_ref Y, @Y.%assoc0 [concrete = constants.%assoc0.6fa] // CHECK:STDOUT: %impl.elem0.loc18_49: type = impl_witness_access constants.%B.lookup_impl_witness.97b, element0 [concrete = constants.%empty_struct_type] // CHECK:STDOUT: %.loc18_49.2: Core.Form = init_form %impl.elem0.loc18_49 [concrete = constants.%.469] // CHECK:STDOUT: %.loc18_17.1: type = splice_block %.loc18_17.2 [concrete = constants.%facet_type.82c] { // CHECK:STDOUT: // CHECK:STDOUT: %A.ref: type = name_ref A, file.%A.decl [concrete = constants.%A.type] // CHECK:STDOUT: %B.ref: type = name_ref B, file.%B.decl [concrete = constants.%B.type] // CHECK:STDOUT: %impl.elem0.loc18_13: %.d15 = impl_witness_access constants.%BitAndWith.impl_witness, element0 [concrete = constants.%type.as.BitAndWith.impl.Op] // CHECK:STDOUT: %bound_method: = bound_method %A.ref, %impl.elem0.loc18_13 [concrete = constants.%type.as.BitAndWith.impl.Op.bound] // CHECK:STDOUT: %type.as.BitAndWith.impl.Op.call: init type = call %bound_method(%A.ref, %B.ref) [concrete = constants.%facet_type.9bb] // CHECK:STDOUT: %.loc18_13.1: type = value_of_initializer %type.as.BitAndWith.impl.Op.call [concrete = constants.%facet_type.9bb] // CHECK:STDOUT: %.loc18_13.2: type = converted %type.as.BitAndWith.impl.Op.call, %.loc18_13.1 [concrete = constants.%facet_type.9bb] // CHECK:STDOUT: // CHECK:STDOUT: %.Self.ref.loc18_23: %facet_type.9bb = name_ref .Self, %.Self.2 [symbolic_self = constants.%.Self.e7e] // CHECK:STDOUT: %.Self.as_type.loc18_23: type = facet_access_type %.Self.ref.loc18_23 [symbolic_self = constants.%.Self.binding.as_type] // CHECK:STDOUT: %.loc18_23: type = converted %.Self.ref.loc18_23, %.Self.as_type.loc18_23 [symbolic_self = constants.%.Self.binding.as_type] // CHECK:STDOUT: %X.ref: %A.assoc_type = name_ref X, @X.%assoc0 [concrete = constants.%assoc0.df7] // CHECK:STDOUT: %impl.elem0.loc18_23: type = impl_witness_access constants.%A.lookup_impl_witness.6b3, element0 [symbolic_self = constants.%impl.elem0.eb8] // CHECK:STDOUT: %.loc18_29.1: %empty_tuple.type = tuple_literal () [concrete = constants.%empty_tuple] // CHECK:STDOUT: %.loc18_29.2: type = converted %.loc18_29.1, constants.%empty_tuple.type [concrete = constants.%empty_tuple.type] // CHECK:STDOUT: %.Self.ref.loc18_35: %facet_type.9bb = name_ref .Self, %.Self.2 [symbolic_self = constants.%.Self.e7e] // CHECK:STDOUT: %.Self.as_type.loc18_35: type = facet_access_type %.Self.ref.loc18_35 [symbolic_self = constants.%.Self.binding.as_type] // CHECK:STDOUT: %.loc18_35: type = converted %.Self.ref.loc18_35, %.Self.as_type.loc18_35 [symbolic_self = constants.%.Self.binding.as_type] // CHECK:STDOUT: %Y.ref.loc18_35: %B.assoc_type = name_ref Y, @Y.%assoc0 [concrete = constants.%assoc0.6fa] // CHECK:STDOUT: %impl.elem0.loc18_35: type = impl_witness_access constants.%B.lookup_impl_witness.d4f, element0 [symbolic_self = constants.%impl.elem0.11d] // CHECK:STDOUT: %.loc18_41.1: %empty_struct_type = struct_literal () [concrete = constants.%empty_struct] // CHECK:STDOUT: %.loc18_41.2: type = converted %.loc18_41.1, constants.%empty_struct_type [concrete = constants.%empty_struct_type] // CHECK:STDOUT: %.loc18_17.2: type = where_expr %.Self.2 [concrete = constants.%facet_type.82c] { // CHECK:STDOUT: requirement_base_facet_type constants.%facet_type.9bb // CHECK:STDOUT: requirement_rewrite %impl.elem0.loc18_23, %.loc18_29.2 // CHECK:STDOUT: requirement_rewrite %impl.elem0.loc18_35, %.loc18_41.2 // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: %AB.loc18_6.2: %facet_type.82c = symbolic_binding AB, 0 [symbolic = %AB.loc18_6.1 (constants.%AB)] // CHECK:STDOUT: %return.param: ref %empty_struct_type = out_param call_param0 // CHECK:STDOUT: %return: ref %empty_struct_type = return_slot %return.param // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic fn @F(%AB.loc14_6.2: %facet_type.82c) { // CHECK:STDOUT: %AB.loc14_6.1: %facet_type.82c = symbolic_binding AB, 0 [symbolic = %AB.loc14_6.1 (constants.%AB)] // CHECK:STDOUT: %AB.binding.as_type: type = symbolic_binding_type AB, 0, %AB.loc14_6.1 [symbolic = %AB.binding.as_type (constants.%AB.binding.as_type)] // CHECK:STDOUT: %A.lookup_impl_witness: = lookup_impl_witness %AB.loc14_6.1, @A [symbolic = %A.lookup_impl_witness (constants.%A.lookup_impl_witness.1b9)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: // CHECK:STDOUT: fn() -> out %return.param: %empty_tuple.type { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %.loc15_11.1: %empty_tuple.type = tuple_literal () [concrete = constants.%empty_tuple] // CHECK:STDOUT: %.loc15_11.2: init %empty_tuple.type = tuple_init () [concrete = constants.%empty_tuple] // CHECK:STDOUT: %.loc15_12: init %empty_tuple.type = converted %.loc15_11.1, %.loc15_11.2 [concrete = constants.%empty_tuple] // CHECK:STDOUT: return %.loc15_12 // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic fn @G(%AB.loc18_6.2: %facet_type.82c) { // CHECK:STDOUT: %AB.loc18_6.1: %facet_type.82c = symbolic_binding AB, 0 [symbolic = %AB.loc18_6.1 (constants.%AB)] // CHECK:STDOUT: %AB.binding.as_type: type = symbolic_binding_type AB, 0, %AB.loc18_6.1 [symbolic = %AB.binding.as_type (constants.%AB.binding.as_type)] // CHECK:STDOUT: %B.lookup_impl_witness: = lookup_impl_witness %AB.loc18_6.1, @B [symbolic = %B.lookup_impl_witness (constants.%B.lookup_impl_witness.97b)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: // CHECK:STDOUT: fn() -> out %return.param: %empty_struct_type { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %.loc19_11.1: %empty_struct_type = struct_literal () [concrete = constants.%empty_struct] // CHECK:STDOUT: %.loc19_11.2: init %empty_struct_type = struct_init () [concrete = constants.%empty_struct] // CHECK:STDOUT: %.loc19_12: init %empty_struct_type = converted %.loc19_11.1, %.loc19_11.2 [concrete = constants.%empty_struct] // CHECK:STDOUT: return %.loc19_12 // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @F(constants.%AB) { // CHECK:STDOUT: %AB.loc14_6.1 => constants.%AB // CHECK:STDOUT: %AB.binding.as_type => constants.%AB.binding.as_type // CHECK:STDOUT: %A.lookup_impl_witness => constants.%A.lookup_impl_witness.1b9 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @G(constants.%AB) { // CHECK:STDOUT: %AB.loc18_6.1 => constants.%AB // CHECK:STDOUT: %AB.binding.as_type => constants.%AB.binding.as_type // CHECK:STDOUT: %B.lookup_impl_witness => constants.%B.lookup_impl_witness.97b // CHECK:STDOUT: } // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/facet/aggregate_through_access.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/full.carbon // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/facet/aggregate_through_access.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/facet/aggregate_through_access.carbon // --- fail_todo_tuple_access_through_witness.carbon library "[[@TEST_NAME]]"; interface Z { let X:! type; } // CHECK:STDERR: fail_todo_tuple_access_through_witness.carbon:[[@LINE+4]]:34: error: type `type` does not support tuple indexing; only tuples can be indexed that way [TupleIndexOnANonTupleType] // CHECK:STDERR: fn F(T:! Z where .X = ({}, )) -> T.X.0 { // CHECK:STDERR: ^~~~~ // CHECK:STDERR: fn F(T:! Z where .X = ({}, )) -> T.X.0 { return {}; } // --- fail_todo_struct_access_through_witness.carbon library "[[@TEST_NAME]]"; interface Z { let X:! type; } // CHECK:STDERR: fail_todo_struct_access_through_witness.carbon:[[@LINE+4]]:36: error: type `type` does not support qualified expressions [QualifiedExprUnsupported] // CHECK:STDERR: fn F(T:! Z where .X = {.t: ()}) -> T.X.t { // CHECK:STDERR: ^~~~~ // CHECK:STDERR: fn F(T:! Z where .X = {.t: ()}) -> T.X.t { return (); } // --- fail_todo_array_access_through_witness.carbon library "[[@TEST_NAME]]"; interface Z { let X:! type; } // CHECK:STDERR: fail_todo_array_access_through_witness.carbon:[[@LINE+4]]:40: error: cannot access member of interface `Core.IndexWith(Core.IntLiteral)` in type `type` that does not implement that interface [MissingImplInMemberAccess] // CHECK:STDERR: fn F(T:! Z where .X = array({}, 1)) -> T.X[0] { // CHECK:STDERR: ^~~~~~ // CHECK:STDERR: fn F(T:! Z where .X = array({}, 1)) -> T.X[0] { return {}; } // --- impl_access_through_witness.carbon library "[[@TEST_NAME]]"; interface Z { let X1:! type; } interface Y { let X2:! type; } class C; impl C as Y where .X2 = {} {} fn F(T:! Z where .X1 = C) -> T.X1.(Y.X2) { return {}; } ================================================ FILE: toolchain/check/testdata/facet/call_combined_impl_witness.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // TODO: Add ranges and switch to "--dump-sem-ir-ranges=only". // EXTRA-ARGS: --dump-sem-ir-ranges=if-present // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/convert.carbon // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/facet/call_combined_impl_witness.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/facet/call_combined_impl_witness.carbon interface Empty { } interface A { fn AA(); } interface B { fn BB(); } class C {} impl C as Empty {} impl C as A { fn AA() {} } impl C as B { fn BB() {} } fn G[T:! A & Empty & B](t: T) { t.AA(); t.BB(); T.AA(); T.BB(); T.(A.AA)(); T.(B.BB)(); } fn F() { G({} as C); } // CHECK:STDOUT: --- call_combined_impl_witness.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %Empty.type: type = facet_type <@Empty> [concrete] // CHECK:STDOUT: %Self.61e: %Empty.type = symbolic_binding Self, 0 [symbolic] // CHECK:STDOUT: %A.type: type = facet_type <@A> [concrete] // CHECK:STDOUT: %Self.c51: %A.type = symbolic_binding Self, 0 [symbolic] // CHECK:STDOUT: %A.WithSelf.AA.type.236: type = fn_type @A.WithSelf.AA, @A.WithSelf(%Self.c51) [symbolic] // CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] // CHECK:STDOUT: %A.WithSelf.AA.0c0: %A.WithSelf.AA.type.236 = struct_value () [symbolic] // CHECK:STDOUT: %A.assoc_type: type = assoc_entity_type @A [concrete] // CHECK:STDOUT: %assoc0.a62: %A.assoc_type = assoc_entity element0, @A.WithSelf.%A.WithSelf.AA.decl [concrete] // CHECK:STDOUT: %B.type: type = facet_type <@B> [concrete] // CHECK:STDOUT: %Self.d0b: %B.type = symbolic_binding Self, 0 [symbolic] // CHECK:STDOUT: %B.WithSelf.BB.type.17c: type = fn_type @B.WithSelf.BB, @B.WithSelf(%Self.d0b) [symbolic] // CHECK:STDOUT: %B.WithSelf.BB.f00: %B.WithSelf.BB.type.17c = struct_value () [symbolic] // CHECK:STDOUT: %B.assoc_type: type = assoc_entity_type @B [concrete] // CHECK:STDOUT: %assoc0.00a: %B.assoc_type = assoc_entity element0, @B.WithSelf.%B.WithSelf.BB.decl [concrete] // CHECK:STDOUT: %C: type = class_type @C [concrete] // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %complete_type: = complete_type_witness %empty_struct_type [concrete] // CHECK:STDOUT: %Empty.impl_witness: = impl_witness @C.as.Empty.impl.%Empty.impl_witness_table [concrete] // CHECK:STDOUT: %Empty.facet: %Empty.type = facet_value %C, (%Empty.impl_witness) [concrete] // CHECK:STDOUT: %A.impl_witness: = impl_witness @C.as.A.impl.%A.impl_witness_table [concrete] // CHECK:STDOUT: %C.as.A.impl.AA.type: type = fn_type @C.as.A.impl.AA [concrete] // CHECK:STDOUT: %C.as.A.impl.AA: %C.as.A.impl.AA.type = struct_value () [concrete] // CHECK:STDOUT: %A.facet.34f: %A.type = facet_value %C, (%A.impl_witness) [concrete] // CHECK:STDOUT: %A.WithSelf.AA.type.6ff: type = fn_type @A.WithSelf.AA, @A.WithSelf(%A.facet.34f) [concrete] // CHECK:STDOUT: %A.WithSelf.AA.ff5: %A.WithSelf.AA.type.6ff = struct_value () [concrete] // CHECK:STDOUT: %B.impl_witness: = impl_witness @C.as.B.impl.%B.impl_witness_table [concrete] // CHECK:STDOUT: %C.as.B.impl.BB.type: type = fn_type @C.as.B.impl.BB [concrete] // CHECK:STDOUT: %C.as.B.impl.BB: %C.as.B.impl.BB.type = struct_value () [concrete] // CHECK:STDOUT: %B.facet.e93: %B.type = facet_value %C, (%B.impl_witness) [concrete] // CHECK:STDOUT: %B.WithSelf.BB.type.f5e: type = fn_type @B.WithSelf.BB, @B.WithSelf(%B.facet.e93) [concrete] // CHECK:STDOUT: %B.WithSelf.BB.09b: %B.WithSelf.BB.type.f5e = struct_value () [concrete] // CHECK:STDOUT: %type: type = facet_type [concrete] // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self] // CHECK:STDOUT: %BitAndWith.type.f2e: type = generic_interface_type @BitAndWith [concrete] // CHECK:STDOUT: %BitAndWith.generic: %BitAndWith.type.f2e = struct_value () [concrete] // CHECK:STDOUT: %BitAndWith.type.b10: type = facet_type <@BitAndWith, @BitAndWith(type)> [concrete] // CHECK:STDOUT: %BitAndWith.impl_witness: = impl_witness imports.%BitAndWith.impl_witness_table [concrete] // CHECK:STDOUT: %BitAndWith.facet: %BitAndWith.type.b10 = facet_value type, (%BitAndWith.impl_witness) [concrete] // CHECK:STDOUT: %BitAndWith.WithSelf.Op.type.4bd: type = fn_type @BitAndWith.WithSelf.Op, @BitAndWith.WithSelf(type, %BitAndWith.facet) [concrete] // CHECK:STDOUT: %.d15: type = fn_type_with_self_type %BitAndWith.WithSelf.Op.type.4bd, %BitAndWith.facet [concrete] // CHECK:STDOUT: %type.as.BitAndWith.impl.Op.type: type = fn_type @type.as.BitAndWith.impl.Op [concrete] // CHECK:STDOUT: %type.as.BitAndWith.impl.Op: %type.as.BitAndWith.impl.Op.type = struct_value () [concrete] // CHECK:STDOUT: %type.as.BitAndWith.impl.Op.bound.05b: = bound_method %A.type, %type.as.BitAndWith.impl.Op [concrete] // CHECK:STDOUT: %facet_type.902: type = facet_type <@Empty & @A> [concrete] // CHECK:STDOUT: %type.as.BitAndWith.impl.Op.bound.ab2: = bound_method %facet_type.902, %type.as.BitAndWith.impl.Op [concrete] // CHECK:STDOUT: %facet_type.dc7: type = facet_type <@Empty & @A & @B> [concrete] // CHECK:STDOUT: %pattern_type.773: type = pattern_type %facet_type.dc7 [concrete] // CHECK:STDOUT: %T: %facet_type.dc7 = symbolic_binding T, 0 [symbolic] // CHECK:STDOUT: %T.binding.as_type: type = symbolic_binding_type T, 0, %T [symbolic] // CHECK:STDOUT: %pattern_type.2ee: type = pattern_type %T.binding.as_type [symbolic] // CHECK:STDOUT: %G.type: type = fn_type @G [concrete] // CHECK:STDOUT: %G: %G.type = struct_value () [concrete] // CHECK:STDOUT: %require_complete: = require_complete_type %T.binding.as_type [symbolic] // CHECK:STDOUT: %A.WithSelf.AA.type.ce6: type = fn_type @A.WithSelf.AA, @A.WithSelf(%T) [symbolic] // CHECK:STDOUT: %A.WithSelf.AA.020: %A.WithSelf.AA.type.ce6 = struct_value () [symbolic] // CHECK:STDOUT: %B.WithSelf.BB.type.374: type = fn_type @B.WithSelf.BB, @B.WithSelf(%T) [symbolic] // CHECK:STDOUT: %B.WithSelf.BB.1c2: %B.WithSelf.BB.type.374 = struct_value () [symbolic] // CHECK:STDOUT: %A.lookup_impl_witness: = lookup_impl_witness %T, @A [symbolic] // CHECK:STDOUT: %A.facet.095: %A.type = facet_value %T.binding.as_type, (%A.lookup_impl_witness) [symbolic] // CHECK:STDOUT: %A.WithSelf.AA.type.7b2: type = fn_type @A.WithSelf.AA, @A.WithSelf(%A.facet.095) [symbolic] // CHECK:STDOUT: %A.WithSelf.AA.11e: %A.WithSelf.AA.type.7b2 = struct_value () [symbolic] // CHECK:STDOUT: %.84b: type = fn_type_with_self_type %A.WithSelf.AA.type.7b2, %A.facet.095 [symbolic] // CHECK:STDOUT: %impl.elem0.26a: %.84b = impl_witness_access %A.lookup_impl_witness, element0 [symbolic] // CHECK:STDOUT: %specific_impl_fn.567: = specific_impl_function %impl.elem0.26a, @A.WithSelf.AA(%A.facet.095) [symbolic] // CHECK:STDOUT: %B.lookup_impl_witness: = lookup_impl_witness %T, @B [symbolic] // CHECK:STDOUT: %B.facet.0e7: %B.type = facet_value %T.binding.as_type, (%B.lookup_impl_witness) [symbolic] // CHECK:STDOUT: %B.WithSelf.BB.type.5a2: type = fn_type @B.WithSelf.BB, @B.WithSelf(%B.facet.0e7) [symbolic] // CHECK:STDOUT: %B.WithSelf.BB.b76: %B.WithSelf.BB.type.5a2 = struct_value () [symbolic] // CHECK:STDOUT: %.dfd: type = fn_type_with_self_type %B.WithSelf.BB.type.5a2, %B.facet.0e7 [symbolic] // CHECK:STDOUT: %impl.elem0.557: %.dfd = impl_witness_access %B.lookup_impl_witness, element0 [symbolic] // CHECK:STDOUT: %specific_impl_fn.fc3: = specific_impl_function %impl.elem0.557, @B.WithSelf.BB(%B.facet.0e7) [symbolic] // CHECK:STDOUT: %F.type: type = fn_type @F [concrete] // CHECK:STDOUT: %F: %F.type = struct_value () [concrete] // CHECK:STDOUT: %empty_struct: %empty_struct_type = struct_value () [concrete] // CHECK:STDOUT: %C.val: %C = struct_value () [concrete] // CHECK:STDOUT: %facet_value: %facet_type.dc7 = facet_value %C, (%Empty.impl_witness, %A.impl_witness, %B.impl_witness) [concrete] // CHECK:STDOUT: %pattern_type.7c7: type = pattern_type %C [concrete] // CHECK:STDOUT: %G.specific_fn: = specific_function %G, @G(%facet_value) [concrete] // CHECK:STDOUT: %Destroy.type: type = facet_type <@Destroy> [concrete] // CHECK:STDOUT: %Destroy.Op.type: type = fn_type @Destroy.Op [concrete] // CHECK:STDOUT: %Destroy.Op: %Destroy.Op.type = struct_value () [concrete] // CHECK:STDOUT: %.171: type = fn_type_with_self_type %A.WithSelf.AA.type.6ff, %A.facet.34f [concrete] // CHECK:STDOUT: %.e04: type = fn_type_with_self_type %B.WithSelf.BB.type.f5e, %B.facet.e93 [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: .BitAndWith = %Core.BitAndWith // CHECK:STDOUT: .Destroy = %Core.Destroy // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: %Core.BitAndWith: %BitAndWith.type.f2e = import_ref Core//prelude/parts/as, BitAndWith, loaded [concrete = constants.%BitAndWith.generic] // CHECK:STDOUT: %Core.import_ref.8d3: %type.as.BitAndWith.impl.Op.type = import_ref Core//prelude/parts/as, loc{{\d+_\d+}}, loaded [concrete = constants.%type.as.BitAndWith.impl.Op] // CHECK:STDOUT: %BitAndWith.impl_witness_table = impl_witness_table (%Core.import_ref.8d3), @type.as.BitAndWith.impl [concrete] // CHECK:STDOUT: %Core.Destroy: type = import_ref Core//prelude/parts/destroy, Destroy, loaded [concrete = constants.%Destroy.type] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .Empty = %Empty.decl // CHECK:STDOUT: .A = %A.decl // CHECK:STDOUT: .B = %B.decl // CHECK:STDOUT: .C = %C.decl // CHECK:STDOUT: .G = %G.decl // CHECK:STDOUT: .F = %F.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %Empty.decl: type = interface_decl @Empty [concrete = constants.%Empty.type] {} {} // CHECK:STDOUT: %A.decl: type = interface_decl @A [concrete = constants.%A.type] {} {} // CHECK:STDOUT: %B.decl: type = interface_decl @B [concrete = constants.%B.type] {} {} // CHECK:STDOUT: %C.decl: type = class_decl @C [concrete = constants.%C] {} {} // CHECK:STDOUT: impl_decl @C.as.Empty.impl [concrete] {} { // CHECK:STDOUT: %C.ref: type = name_ref C, file.%C.decl [concrete = constants.%C] // CHECK:STDOUT: %Empty.ref: type = name_ref Empty, file.%Empty.decl [concrete = constants.%Empty.type] // CHECK:STDOUT: } // CHECK:STDOUT: impl_decl @C.as.A.impl [concrete] {} { // CHECK:STDOUT: %C.ref: type = name_ref C, file.%C.decl [concrete = constants.%C] // CHECK:STDOUT: %A.ref: type = name_ref A, file.%A.decl [concrete = constants.%A.type] // CHECK:STDOUT: } // CHECK:STDOUT: impl_decl @C.as.B.impl [concrete] {} { // CHECK:STDOUT: %C.ref: type = name_ref C, file.%C.decl [concrete = constants.%C] // CHECK:STDOUT: %B.ref: type = name_ref B, file.%B.decl [concrete = constants.%B.type] // CHECK:STDOUT: } // CHECK:STDOUT: %G.decl: %G.type = fn_decl @G [concrete = constants.%G] { // CHECK:STDOUT: %T.patt: %pattern_type.773 = symbolic_binding_pattern T, 0 [concrete] // CHECK:STDOUT: %t.patt: @G.%pattern_type (%pattern_type.2ee) = value_binding_pattern t [concrete] // CHECK:STDOUT: %t.param_patt: @G.%pattern_type (%pattern_type.2ee) = value_param_pattern %t.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %.loc33_20.1: type = splice_block %.loc33_20.3 [concrete = constants.%facet_type.dc7] { // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %A.ref.loc33: type = name_ref A, file.%A.decl [concrete = constants.%A.type] // CHECK:STDOUT: %Empty.ref: type = name_ref Empty, file.%Empty.decl [concrete = constants.%Empty.type] // CHECK:STDOUT: %impl.elem0.loc33_12: %.d15 = impl_witness_access constants.%BitAndWith.impl_witness, element0 [concrete = constants.%type.as.BitAndWith.impl.Op] // CHECK:STDOUT: %bound_method.loc33_12: = bound_method %A.ref.loc33, %impl.elem0.loc33_12 [concrete = constants.%type.as.BitAndWith.impl.Op.bound.05b] // CHECK:STDOUT: %type.as.BitAndWith.impl.Op.call.loc33_12: init type = call %bound_method.loc33_12(%A.ref.loc33, %Empty.ref) [concrete = constants.%facet_type.902] // CHECK:STDOUT: %B.ref.loc33: type = name_ref B, file.%B.decl [concrete = constants.%B.type] // CHECK:STDOUT: %impl.elem0.loc33_20: %.d15 = impl_witness_access constants.%BitAndWith.impl_witness, element0 [concrete = constants.%type.as.BitAndWith.impl.Op] // CHECK:STDOUT: %bound_method.loc33_20: = bound_method %type.as.BitAndWith.impl.Op.call.loc33_12, %impl.elem0.loc33_20 [concrete = constants.%type.as.BitAndWith.impl.Op.bound.ab2] // CHECK:STDOUT: %.loc33_12.1: type = value_of_initializer %type.as.BitAndWith.impl.Op.call.loc33_12 [concrete = constants.%facet_type.902] // CHECK:STDOUT: %.loc33_12.2: type = converted %type.as.BitAndWith.impl.Op.call.loc33_12, %.loc33_12.1 [concrete = constants.%facet_type.902] // CHECK:STDOUT: %type.as.BitAndWith.impl.Op.call.loc33_20: init type = call %bound_method.loc33_20(%.loc33_12.2, %B.ref.loc33) [concrete = constants.%facet_type.dc7] // CHECK:STDOUT: %.loc33_20.2: type = value_of_initializer %type.as.BitAndWith.impl.Op.call.loc33_20 [concrete = constants.%facet_type.dc7] // CHECK:STDOUT: %.loc33_20.3: type = converted %type.as.BitAndWith.impl.Op.call.loc33_20, %.loc33_20.2 [concrete = constants.%facet_type.dc7] // CHECK:STDOUT: } // CHECK:STDOUT: %T.loc33_6.2: %facet_type.dc7 = symbolic_binding T, 0 [symbolic = %T.loc33_6.1 (constants.%T)] // CHECK:STDOUT: %t.param: @G.%T.binding.as_type (%T.binding.as_type) = value_param call_param0 // CHECK:STDOUT: %.loc33_28.1: type = splice_block %.loc33_28.2 [symbolic = %T.binding.as_type (constants.%T.binding.as_type)] { // CHECK:STDOUT: %T.ref.loc33: %facet_type.dc7 = name_ref T, %T.loc33_6.2 [symbolic = %T.loc33_6.1 (constants.%T)] // CHECK:STDOUT: %T.as_type.loc33: type = facet_access_type %T.ref.loc33 [symbolic = %T.binding.as_type (constants.%T.binding.as_type)] // CHECK:STDOUT: %.loc33_28.2: type = converted %T.ref.loc33, %T.as_type.loc33 [symbolic = %T.binding.as_type (constants.%T.binding.as_type)] // CHECK:STDOUT: } // CHECK:STDOUT: %t: @G.%T.binding.as_type (%T.binding.as_type) = value_binding t, %t.param // CHECK:STDOUT: } // CHECK:STDOUT: %F.decl: %F.type = fn_decl @F [concrete = constants.%F] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: interface @Empty { // CHECK:STDOUT: %Self: %Empty.type = symbolic_binding Self, 0 [symbolic = constants.%Self.61e] // CHECK:STDOUT: %Empty.WithSelf.decl = interface_with_self_decl @Empty [concrete] // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = %Self // CHECK:STDOUT: .AA = // CHECK:STDOUT: .BB = // CHECK:STDOUT: witness = () // CHECK:STDOUT: // CHECK:STDOUT: !requires: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: interface @A { // CHECK:STDOUT: %Self: %A.type = symbolic_binding Self, 0 [symbolic = constants.%Self.c51] // CHECK:STDOUT: %A.WithSelf.decl = interface_with_self_decl @A [concrete] // CHECK:STDOUT: // CHECK:STDOUT: !with Self: // CHECK:STDOUT: %A.WithSelf.AA.decl: @A.WithSelf.%A.WithSelf.AA.type (%A.WithSelf.AA.type.236) = fn_decl @A.WithSelf.AA [symbolic = @A.WithSelf.%A.WithSelf.AA (constants.%A.WithSelf.AA.0c0)] {} {} // CHECK:STDOUT: %assoc0: %A.assoc_type = assoc_entity element0, %A.WithSelf.AA.decl [concrete = constants.%assoc0.a62] // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = %Self // CHECK:STDOUT: .AA = @A.WithSelf.%assoc0 // CHECK:STDOUT: .BB = // CHECK:STDOUT: witness = (@A.WithSelf.%A.WithSelf.AA.decl) // CHECK:STDOUT: // CHECK:STDOUT: !requires: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: interface @B { // CHECK:STDOUT: %Self: %B.type = symbolic_binding Self, 0 [symbolic = constants.%Self.d0b] // CHECK:STDOUT: %B.WithSelf.decl = interface_with_self_decl @B [concrete] // CHECK:STDOUT: // CHECK:STDOUT: !with Self: // CHECK:STDOUT: %B.WithSelf.BB.decl: @B.WithSelf.%B.WithSelf.BB.type (%B.WithSelf.BB.type.17c) = fn_decl @B.WithSelf.BB [symbolic = @B.WithSelf.%B.WithSelf.BB (constants.%B.WithSelf.BB.f00)] {} {} // CHECK:STDOUT: %assoc0: %B.assoc_type = assoc_entity element0, %B.WithSelf.BB.decl [concrete = constants.%assoc0.00a] // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = %Self // CHECK:STDOUT: .BB = @B.WithSelf.%assoc0 // CHECK:STDOUT: .AA = // CHECK:STDOUT: witness = (@B.WithSelf.%B.WithSelf.BB.decl) // CHECK:STDOUT: // CHECK:STDOUT: !requires: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: impl @C.as.Empty.impl: %C.ref as %Empty.ref { // CHECK:STDOUT: %Empty.impl_witness_table = impl_witness_table (), @C.as.Empty.impl [concrete] // CHECK:STDOUT: %Empty.impl_witness: = impl_witness %Empty.impl_witness_table [concrete = constants.%Empty.impl_witness] // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: witness = %Empty.impl_witness // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: impl @C.as.A.impl: %C.ref as %A.ref { // CHECK:STDOUT: %C.as.A.impl.AA.decl: %C.as.A.impl.AA.type = fn_decl @C.as.A.impl.AA [concrete = constants.%C.as.A.impl.AA] {} {} // CHECK:STDOUT: %A.impl_witness_table = impl_witness_table (%C.as.A.impl.AA.decl), @C.as.A.impl [concrete] // CHECK:STDOUT: %A.impl_witness: = impl_witness %A.impl_witness_table [concrete = constants.%A.impl_witness] // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .AA = %C.as.A.impl.AA.decl // CHECK:STDOUT: witness = %A.impl_witness // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: impl @C.as.B.impl: %C.ref as %B.ref { // CHECK:STDOUT: %C.as.B.impl.BB.decl: %C.as.B.impl.BB.type = fn_decl @C.as.B.impl.BB [concrete = constants.%C.as.B.impl.BB] {} {} // CHECK:STDOUT: %B.impl_witness_table = impl_witness_table (%C.as.B.impl.BB.decl), @C.as.B.impl [concrete] // CHECK:STDOUT: %B.impl_witness: = impl_witness %B.impl_witness_table [concrete = constants.%B.impl_witness] // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .BB = %C.as.B.impl.BB.decl // CHECK:STDOUT: witness = %B.impl_witness // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @C { // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%C // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic fn @A.WithSelf.AA(@A.%Self: %A.type) { // CHECK:STDOUT: fn(); // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic fn @B.WithSelf.BB(@B.%Self: %B.type) { // CHECK:STDOUT: fn(); // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @C.as.A.impl.AA() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @C.as.B.impl.BB() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic fn @G(%T.loc33_6.2: %facet_type.dc7) { // CHECK:STDOUT: %T.loc33_6.1: %facet_type.dc7 = symbolic_binding T, 0 [symbolic = %T.loc33_6.1 (constants.%T)] // CHECK:STDOUT: %T.binding.as_type: type = symbolic_binding_type T, 0, %T.loc33_6.1 [symbolic = %T.binding.as_type (constants.%T.binding.as_type)] // CHECK:STDOUT: %pattern_type: type = pattern_type %T.binding.as_type [symbolic = %pattern_type (constants.%pattern_type.2ee)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete: = require_complete_type %T.binding.as_type [symbolic = %require_complete (constants.%require_complete)] // CHECK:STDOUT: %A.lookup_impl_witness: = lookup_impl_witness %T.loc33_6.1, @A [symbolic = %A.lookup_impl_witness (constants.%A.lookup_impl_witness)] // CHECK:STDOUT: %A.facet.loc34: %A.type = facet_value %T.binding.as_type, (%A.lookup_impl_witness) [symbolic = %A.facet.loc34 (constants.%A.facet.095)] // CHECK:STDOUT: %A.WithSelf.AA.type: type = fn_type @A.WithSelf.AA, @A.WithSelf(%A.facet.loc34) [symbolic = %A.WithSelf.AA.type (constants.%A.WithSelf.AA.type.7b2)] // CHECK:STDOUT: %.loc34: type = fn_type_with_self_type %A.WithSelf.AA.type, %A.facet.loc34 [symbolic = %.loc34 (constants.%.84b)] // CHECK:STDOUT: %impl.elem0.loc34_4.2: @G.%.loc34 (%.84b) = impl_witness_access %A.lookup_impl_witness, element0 [symbolic = %impl.elem0.loc34_4.2 (constants.%impl.elem0.26a)] // CHECK:STDOUT: %specific_impl_fn.loc34_4.2: = specific_impl_function %impl.elem0.loc34_4.2, @A.WithSelf.AA(%A.facet.loc34) [symbolic = %specific_impl_fn.loc34_4.2 (constants.%specific_impl_fn.567)] // CHECK:STDOUT: %B.lookup_impl_witness: = lookup_impl_witness %T.loc33_6.1, @B [symbolic = %B.lookup_impl_witness (constants.%B.lookup_impl_witness)] // CHECK:STDOUT: %B.facet.loc35: %B.type = facet_value %T.binding.as_type, (%B.lookup_impl_witness) [symbolic = %B.facet.loc35 (constants.%B.facet.0e7)] // CHECK:STDOUT: %B.WithSelf.BB.type: type = fn_type @B.WithSelf.BB, @B.WithSelf(%B.facet.loc35) [symbolic = %B.WithSelf.BB.type (constants.%B.WithSelf.BB.type.5a2)] // CHECK:STDOUT: %.loc35: type = fn_type_with_self_type %B.WithSelf.BB.type, %B.facet.loc35 [symbolic = %.loc35 (constants.%.dfd)] // CHECK:STDOUT: %impl.elem0.loc35_4.2: @G.%.loc35 (%.dfd) = impl_witness_access %B.lookup_impl_witness, element0 [symbolic = %impl.elem0.loc35_4.2 (constants.%impl.elem0.557)] // CHECK:STDOUT: %specific_impl_fn.loc35_4.2: = specific_impl_function %impl.elem0.loc35_4.2, @B.WithSelf.BB(%B.facet.loc35) [symbolic = %specific_impl_fn.loc35_4.2 (constants.%specific_impl_fn.fc3)] // CHECK:STDOUT: // CHECK:STDOUT: fn(%t.param: @G.%T.binding.as_type (%T.binding.as_type)) { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %t.ref.loc34: @G.%T.binding.as_type (%T.binding.as_type) = name_ref t, %t // CHECK:STDOUT: %AA.ref.loc34: %A.assoc_type = name_ref AA, @A.WithSelf.%assoc0 [concrete = constants.%assoc0.a62] // CHECK:STDOUT: %impl.elem0.loc34_4.1: @G.%.loc34 (%.84b) = impl_witness_access constants.%A.lookup_impl_witness, element0 [symbolic = %impl.elem0.loc34_4.2 (constants.%impl.elem0.26a)] // CHECK:STDOUT: %specific_impl_fn.loc34_4.1: = specific_impl_function %impl.elem0.loc34_4.1, @A.WithSelf.AA(constants.%A.facet.095) [symbolic = %specific_impl_fn.loc34_4.2 (constants.%specific_impl_fn.567)] // CHECK:STDOUT: %A.WithSelf.AA.call.loc34: init %empty_tuple.type = call %specific_impl_fn.loc34_4.1() // CHECK:STDOUT: %t.ref.loc35: @G.%T.binding.as_type (%T.binding.as_type) = name_ref t, %t // CHECK:STDOUT: %BB.ref.loc35: %B.assoc_type = name_ref BB, @B.WithSelf.%assoc0 [concrete = constants.%assoc0.00a] // CHECK:STDOUT: %impl.elem0.loc35_4.1: @G.%.loc35 (%.dfd) = impl_witness_access constants.%B.lookup_impl_witness, element0 [symbolic = %impl.elem0.loc35_4.2 (constants.%impl.elem0.557)] // CHECK:STDOUT: %specific_impl_fn.loc35_4.1: = specific_impl_function %impl.elem0.loc35_4.1, @B.WithSelf.BB(constants.%B.facet.0e7) [symbolic = %specific_impl_fn.loc35_4.2 (constants.%specific_impl_fn.fc3)] // CHECK:STDOUT: %B.WithSelf.BB.call.loc35: init %empty_tuple.type = call %specific_impl_fn.loc35_4.1() // CHECK:STDOUT: %T.ref.loc37: %facet_type.dc7 = name_ref T, %T.loc33_6.2 [symbolic = %T.loc33_6.1 (constants.%T)] // CHECK:STDOUT: %T.as_type.loc37: type = facet_access_type %T.ref.loc37 [symbolic = %T.binding.as_type (constants.%T.binding.as_type)] // CHECK:STDOUT: %.loc37: type = converted %T.ref.loc37, %T.as_type.loc37 [symbolic = %T.binding.as_type (constants.%T.binding.as_type)] // CHECK:STDOUT: %AA.ref.loc37: %A.assoc_type = name_ref AA, @A.WithSelf.%assoc0 [concrete = constants.%assoc0.a62] // CHECK:STDOUT: %impl.elem0.loc37: @G.%.loc34 (%.84b) = impl_witness_access constants.%A.lookup_impl_witness, element0 [symbolic = %impl.elem0.loc34_4.2 (constants.%impl.elem0.26a)] // CHECK:STDOUT: %specific_impl_fn.loc37: = specific_impl_function %impl.elem0.loc37, @A.WithSelf.AA(constants.%A.facet.095) [symbolic = %specific_impl_fn.loc34_4.2 (constants.%specific_impl_fn.567)] // CHECK:STDOUT: %A.WithSelf.AA.call.loc37: init %empty_tuple.type = call %specific_impl_fn.loc37() // CHECK:STDOUT: %T.ref.loc38: %facet_type.dc7 = name_ref T, %T.loc33_6.2 [symbolic = %T.loc33_6.1 (constants.%T)] // CHECK:STDOUT: %T.as_type.loc38: type = facet_access_type %T.ref.loc38 [symbolic = %T.binding.as_type (constants.%T.binding.as_type)] // CHECK:STDOUT: %.loc38: type = converted %T.ref.loc38, %T.as_type.loc38 [symbolic = %T.binding.as_type (constants.%T.binding.as_type)] // CHECK:STDOUT: %BB.ref.loc38: %B.assoc_type = name_ref BB, @B.WithSelf.%assoc0 [concrete = constants.%assoc0.00a] // CHECK:STDOUT: %impl.elem0.loc38: @G.%.loc35 (%.dfd) = impl_witness_access constants.%B.lookup_impl_witness, element0 [symbolic = %impl.elem0.loc35_4.2 (constants.%impl.elem0.557)] // CHECK:STDOUT: %specific_impl_fn.loc38: = specific_impl_function %impl.elem0.loc38, @B.WithSelf.BB(constants.%B.facet.0e7) [symbolic = %specific_impl_fn.loc35_4.2 (constants.%specific_impl_fn.fc3)] // CHECK:STDOUT: %B.WithSelf.BB.call.loc38: init %empty_tuple.type = call %specific_impl_fn.loc38() // CHECK:STDOUT: %T.ref.loc40: %facet_type.dc7 = name_ref T, %T.loc33_6.2 [symbolic = %T.loc33_6.1 (constants.%T)] // CHECK:STDOUT: %A.ref.loc40: type = name_ref A, file.%A.decl [concrete = constants.%A.type] // CHECK:STDOUT: %AA.ref.loc40: %A.assoc_type = name_ref AA, @A.WithSelf.%assoc0 [concrete = constants.%assoc0.a62] // CHECK:STDOUT: %T.as_type.loc40: type = facet_access_type %T.ref.loc40 [symbolic = %T.binding.as_type (constants.%T.binding.as_type)] // CHECK:STDOUT: %A.facet.loc40: %A.type = facet_value %T.as_type.loc40, (constants.%A.lookup_impl_witness) [symbolic = %A.facet.loc34 (constants.%A.facet.095)] // CHECK:STDOUT: %.loc40: %A.type = converted %T.ref.loc40, %A.facet.loc40 [symbolic = %A.facet.loc34 (constants.%A.facet.095)] // CHECK:STDOUT: %impl.elem0.loc40: @G.%.loc34 (%.84b) = impl_witness_access constants.%A.lookup_impl_witness, element0 [symbolic = %impl.elem0.loc34_4.2 (constants.%impl.elem0.26a)] // CHECK:STDOUT: %specific_impl_fn.loc40: = specific_impl_function %impl.elem0.loc40, @A.WithSelf.AA(constants.%A.facet.095) [symbolic = %specific_impl_fn.loc34_4.2 (constants.%specific_impl_fn.567)] // CHECK:STDOUT: %A.WithSelf.AA.call.loc40: init %empty_tuple.type = call %specific_impl_fn.loc40() // CHECK:STDOUT: %T.ref.loc41: %facet_type.dc7 = name_ref T, %T.loc33_6.2 [symbolic = %T.loc33_6.1 (constants.%T)] // CHECK:STDOUT: %B.ref.loc41: type = name_ref B, file.%B.decl [concrete = constants.%B.type] // CHECK:STDOUT: %BB.ref.loc41: %B.assoc_type = name_ref BB, @B.WithSelf.%assoc0 [concrete = constants.%assoc0.00a] // CHECK:STDOUT: %T.as_type.loc41: type = facet_access_type %T.ref.loc41 [symbolic = %T.binding.as_type (constants.%T.binding.as_type)] // CHECK:STDOUT: %B.facet.loc41: %B.type = facet_value %T.as_type.loc41, (constants.%B.lookup_impl_witness) [symbolic = %B.facet.loc35 (constants.%B.facet.0e7)] // CHECK:STDOUT: %.loc41: %B.type = converted %T.ref.loc41, %B.facet.loc41 [symbolic = %B.facet.loc35 (constants.%B.facet.0e7)] // CHECK:STDOUT: %impl.elem0.loc41: @G.%.loc35 (%.dfd) = impl_witness_access constants.%B.lookup_impl_witness, element0 [symbolic = %impl.elem0.loc35_4.2 (constants.%impl.elem0.557)] // CHECK:STDOUT: %specific_impl_fn.loc41: = specific_impl_function %impl.elem0.loc41, @B.WithSelf.BB(constants.%B.facet.0e7) [symbolic = %specific_impl_fn.loc35_4.2 (constants.%specific_impl_fn.fc3)] // CHECK:STDOUT: %B.WithSelf.BB.call.loc41: init %empty_tuple.type = call %specific_impl_fn.loc41() // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @F() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %G.ref: %G.type = name_ref G, file.%G.decl [concrete = constants.%G] // CHECK:STDOUT: %.loc45_6.1: %empty_struct_type = struct_literal () [concrete = constants.%empty_struct] // CHECK:STDOUT: %C.ref: type = name_ref C, file.%C.decl [concrete = constants.%C] // CHECK:STDOUT: %.loc45_6.2: ref %C = temporary_storage // CHECK:STDOUT: %.loc45_6.3: init %C to %.loc45_6.2 = class_init () [concrete = constants.%C.val] // CHECK:STDOUT: %.loc45_8.1: init %C = converted %.loc45_6.1, %.loc45_6.3 [concrete = constants.%C.val] // CHECK:STDOUT: %facet_value: %facet_type.dc7 = facet_value constants.%C, (constants.%Empty.impl_witness, constants.%A.impl_witness, constants.%B.impl_witness) [concrete = constants.%facet_value] // CHECK:STDOUT: %.loc45_12: %facet_type.dc7 = converted constants.%C, %facet_value [concrete = constants.%facet_value] // CHECK:STDOUT: %G.specific_fn: = specific_function %G.ref, @G(constants.%facet_value) [concrete = constants.%G.specific_fn] // CHECK:STDOUT: %.loc45_8.2: ref %C = temporary %.loc45_6.2, %.loc45_8.1 // CHECK:STDOUT: %.loc45_8.3: %C = acquire_value %.loc45_8.2 // CHECK:STDOUT: %G.call: init %empty_tuple.type = call %G.specific_fn(%.loc45_8.3) // CHECK:STDOUT: %Destroy.Op.bound: = bound_method %.loc45_8.2, constants.%Destroy.Op // CHECK:STDOUT: %Destroy.Op.call: init %empty_tuple.type = call %Destroy.Op.bound(%.loc45_8.2) // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @Destroy.Op(%self.param: ref %C) = "no_op"; // CHECK:STDOUT: // CHECK:STDOUT: specific @Empty.WithSelf(constants.%Self.61e) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @A.WithSelf(constants.%Self.c51) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: %Self => constants.%Self.c51 // CHECK:STDOUT: %A.WithSelf.AA.type => constants.%A.WithSelf.AA.type.236 // CHECK:STDOUT: %A.WithSelf.AA => constants.%A.WithSelf.AA.0c0 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @A.WithSelf.AA(constants.%Self.c51) {} // CHECK:STDOUT: // CHECK:STDOUT: specific @B.WithSelf(constants.%Self.d0b) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: %Self => constants.%Self.d0b // CHECK:STDOUT: %B.WithSelf.BB.type => constants.%B.WithSelf.BB.type.17c // CHECK:STDOUT: %B.WithSelf.BB => constants.%B.WithSelf.BB.f00 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @B.WithSelf.BB(constants.%Self.d0b) {} // CHECK:STDOUT: // CHECK:STDOUT: specific @Empty.WithSelf(constants.%Empty.facet) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @A.WithSelf(constants.%A.facet.34f) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: %Self => constants.%A.facet.34f // CHECK:STDOUT: %A.WithSelf.AA.type => constants.%A.WithSelf.AA.type.6ff // CHECK:STDOUT: %A.WithSelf.AA => constants.%A.WithSelf.AA.ff5 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @A.WithSelf.AA(constants.%A.facet.34f) {} // CHECK:STDOUT: // CHECK:STDOUT: specific @B.WithSelf(constants.%B.facet.e93) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: %Self => constants.%B.facet.e93 // CHECK:STDOUT: %B.WithSelf.BB.type => constants.%B.WithSelf.BB.type.f5e // CHECK:STDOUT: %B.WithSelf.BB => constants.%B.WithSelf.BB.09b // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @B.WithSelf.BB(constants.%B.facet.e93) {} // CHECK:STDOUT: // CHECK:STDOUT: specific @G(constants.%T) { // CHECK:STDOUT: %T.loc33_6.1 => constants.%T // CHECK:STDOUT: %T.binding.as_type => constants.%T.binding.as_type // CHECK:STDOUT: %pattern_type => constants.%pattern_type.2ee // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @Empty.WithSelf(constants.%T) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @A.WithSelf(constants.%T) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: %Self => constants.%T // CHECK:STDOUT: %A.WithSelf.AA.type => constants.%A.WithSelf.AA.type.ce6 // CHECK:STDOUT: %A.WithSelf.AA => constants.%A.WithSelf.AA.020 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @B.WithSelf(constants.%T) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: %Self => constants.%T // CHECK:STDOUT: %B.WithSelf.BB.type => constants.%B.WithSelf.BB.type.374 // CHECK:STDOUT: %B.WithSelf.BB => constants.%B.WithSelf.BB.1c2 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @A.WithSelf(constants.%A.facet.095) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: %Self => constants.%A.facet.095 // CHECK:STDOUT: %A.WithSelf.AA.type => constants.%A.WithSelf.AA.type.7b2 // CHECK:STDOUT: %A.WithSelf.AA => constants.%A.WithSelf.AA.11e // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @A.WithSelf.AA(constants.%A.facet.095) {} // CHECK:STDOUT: // CHECK:STDOUT: specific @B.WithSelf(constants.%B.facet.0e7) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: %Self => constants.%B.facet.0e7 // CHECK:STDOUT: %B.WithSelf.BB.type => constants.%B.WithSelf.BB.type.5a2 // CHECK:STDOUT: %B.WithSelf.BB => constants.%B.WithSelf.BB.b76 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @B.WithSelf.BB(constants.%B.facet.0e7) {} // CHECK:STDOUT: // CHECK:STDOUT: specific @G(constants.%facet_value) { // CHECK:STDOUT: %T.loc33_6.1 => constants.%facet_value // CHECK:STDOUT: %T.binding.as_type => constants.%C // CHECK:STDOUT: %pattern_type => constants.%pattern_type.7c7 // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete => constants.%complete_type // CHECK:STDOUT: %A.lookup_impl_witness => constants.%A.impl_witness // CHECK:STDOUT: %A.facet.loc34 => constants.%A.facet.34f // CHECK:STDOUT: %A.WithSelf.AA.type => constants.%A.WithSelf.AA.type.6ff // CHECK:STDOUT: %.loc34 => constants.%.171 // CHECK:STDOUT: %impl.elem0.loc34_4.2 => constants.%C.as.A.impl.AA // CHECK:STDOUT: %specific_impl_fn.loc34_4.2 => constants.%C.as.A.impl.AA // CHECK:STDOUT: %B.lookup_impl_witness => constants.%B.impl_witness // CHECK:STDOUT: %B.facet.loc35 => constants.%B.facet.e93 // CHECK:STDOUT: %B.WithSelf.BB.type => constants.%B.WithSelf.BB.type.f5e // CHECK:STDOUT: %.loc35 => constants.%.e04 // CHECK:STDOUT: %impl.elem0.loc35_4.2 => constants.%C.as.B.impl.BB // CHECK:STDOUT: %specific_impl_fn.loc35_4.2 => constants.%C.as.B.impl.BB // CHECK:STDOUT: } // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/facet/combine.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/convert.carbon // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/facet/combine.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/facet/combine.carbon // --- fail_name_collision.carbon library "[[@TEST_NAME]]"; interface A { fn G(); } interface B { fn G(); } class C {} impl C as A { fn G(); } impl C as B { fn G() {} } fn F() { // TODO: This error message is wrong here, we are not using `extend`. // CHECK:STDERR: fail_name_collision.carbon:[[@LINE+4]]:14: error: ambiguous use of name `G` found in multiple extended scopes [NameAmbiguousDueToExtend] // CHECK:STDERR: ({} as C).((A & B).G)(); // CHECK:STDERR: ^~~~~~~~~ // CHECK:STDERR: ({} as C).((A & B).G)(); } // --- combine.carbon library "[[@TEST_NAME]]"; interface A {} interface B { fn BB[self: Self](); } class C {} impl C as A {} impl C as B { fn BB[unused self: Self]() {} } fn G[T:! A & B](unused t: T) {} fn F() { ({} as C).((A & B).BB)(); (({} as C) as (C as (A & B))).((A & B).BB)(); (({} as C) as (C as (A & B))).(B.BB)(); G({} as C); } // --- generic_interface.carbon library "[[@TEST_NAME]]"; interface A(T:! type) {} interface B {} class P1 {} class P2 {} class C {} impl C as A(P1) {} impl C as B {} fn G[T:! A(P1) & B](unused t: T) {} fn F() { G({} as C); } // --- fail_wrong_generic_interface.carbon library "[[@TEST_NAME]]"; interface A(T:! type) {} interface B {} class P1 {} class P2 {} class C {} impl C as A(P1) {} impl C as B {} fn G[T:! A(P2) & B](unused t: T) {} fn F() { // CHECK:STDERR: fail_wrong_generic_interface.carbon:[[@LINE+7]]:3: error: cannot convert type `C` into type implementing `A(P2) & B` [ConversionFailureTypeToFacet] // CHECK:STDERR: G({} as C); // CHECK:STDERR: ^~~~~~~~~~ // CHECK:STDERR: fail_wrong_generic_interface.carbon:[[@LINE-6]]:1: note: while deducing parameters of generic declared here [DeductionGenericHere] // CHECK:STDERR: fn G[T:! A(P2) & B](unused t: T) {} // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: G({} as C); } // --- generic_forall_impl.carbon library "[[@TEST_NAME]]"; interface Iface {} interface GenericIface(T:! type) {} class GenericClass(T:! type) {} class ImplIface {} impl ImplIface as Iface {} class C {} impl C as Iface {} impl forall [IfaceType:! Iface] C as GenericIface(GenericClass(IfaceType)) {} fn G[T:! Iface & GenericIface(GenericClass(ImplIface))](unused t: T) {} fn F() { G({} as C); } // --- compare_equal.carbon library "[[@TEST_NAME]]"; class WrapType(T:! type) {} fn AssertSame[T:! type](unused a: WrapType(T), unused b: WrapType(T)) {} fn Type(T:! type) -> WrapType(T) { return {}; } interface I; interface J; interface K(T:! type); fn TestIncomplete() { AssertSame(Type(I), Type(I & I)); AssertSame(Type(I), Type(I & I & I)); AssertSame(Type(I & J), Type(J & I)); AssertSame(Type(I & J), Type(I & I & J)); AssertSame(Type(I & J), Type(I & J & I)); AssertSame(Type(I & J), Type(J & I & I)); AssertSame(Type(I & K({})), Type(K({}) & I)); AssertSame(Type(I & K({}) & K(())), Type(K(()) & K({}) & I)); } interface I {} interface J {} interface K(T:! type) {} fn TestComplete() { AssertSame(Type(I), Type(I & I)); AssertSame(Type(I), Type(I & I & I)); AssertSame(Type(I & J), Type(J & I)); AssertSame(Type(I & J), Type(I & I & J)); AssertSame(Type(I & J), Type(I & J & I)); AssertSame(Type(I & J), Type(J & I & I)); AssertSame(Type(I & K({})), Type(K({}) & I)); AssertSame(Type(I & K({}) & K(())), Type(K(()) & K({}) & I)); } // --- fail_compare_not_equal.carbon library "[[@TEST_NAME]]"; class WrapType(T:! type) {} fn Same[T:! type](unused a: WrapType(T), unused b: WrapType(T)) {} fn Type(T:! type) -> WrapType(T) { return {}; } interface I {} interface J {} interface K {} fn Test() { // CHECK:STDERR: fail_compare_not_equal.carbon:[[@LINE+7]]:3: error: inconsistent deductions for value of generic parameter `T` [DeductionInconsistent] // CHECK:STDERR: Same(Type(I & J), Type(K & I & J)); // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: fail_compare_not_equal.carbon:[[@LINE-11]]:1: note: while deducing parameters of generic declared here [DeductionGenericHere] // CHECK:STDERR: fn Same[T:! type](unused a: WrapType(T), unused b: WrapType(T)) {} // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: Same(Type(I & J), Type(K & I & J)); } // --- fail_compare_not_equal_parameterized.carbon library "[[@TEST_NAME]]"; class WrapType(T:! type) {} fn Same[T:! type](unused a: WrapType(T), unused b: WrapType(T)) {} fn Type(T:! type) -> WrapType(T) { return {}; } interface I {} interface J(T:! type) {} fn Test() { // CHECK:STDERR: fail_compare_not_equal_parameterized.carbon:[[@LINE+7]]:3: error: inconsistent deductions for value of generic parameter `T` [DeductionInconsistent] // CHECK:STDERR: Same(Type(I & J(())), Type(J({}) & I)); // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: fail_compare_not_equal_parameterized.carbon:[[@LINE-10]]:1: note: while deducing parameters of generic declared here [DeductionGenericHere] // CHECK:STDERR: fn Same[T:! type](unused a: WrapType(T), unused b: WrapType(T)) {} // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: Same(Type(I & J(())), Type(J({}) & I)); } // --- fail_compare_not_equal_parameterized_extra.carbon library "[[@TEST_NAME]]"; class WrapType(T:! type) {} fn Same[T:! type](unused a: WrapType(T), unused b: WrapType(T)) {} fn Type(T:! type) -> WrapType(T) { return {}; } interface I {} interface J(T:! type) {} fn Test() { // CHECK:STDERR: fail_compare_not_equal_parameterized_extra.carbon:[[@LINE+7]]:3: error: inconsistent deductions for value of generic parameter `T` [DeductionInconsistent] // CHECK:STDERR: Same(Type(I & J(())), Type(J(()) & J({}) & I)); // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: fail_compare_not_equal_parameterized_extra.carbon:[[@LINE-10]]:1: note: while deducing parameters of generic declared here [DeductionGenericHere] // CHECK:STDERR: fn Same[T:! type](unused a: WrapType(T), unused b: WrapType(T)) {} // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: Same(Type(I & J(())), Type(J(()) & J({}) & I)); } ================================================ FILE: toolchain/check/testdata/facet/convert_class_type_to_facet_type.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // TODO: Add ranges and switch to "--dump-sem-ir-ranges=only". // EXTRA-ARGS: --dump-sem-ir-ranges=if-present // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/convert.carbon // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/facet/convert_class_type_to_facet_type.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/facet/convert_class_type_to_facet_type.carbon interface Animal {} class Goat {} impl Goat as Animal {} fn WalkAnimal(unused A:! Animal) {} fn F() { WalkAnimal(Goat); } // CHECK:STDOUT: --- convert_class_type_to_facet_type.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %Animal.type: type = facet_type <@Animal> [concrete] // CHECK:STDOUT: %Self: %Animal.type = symbolic_binding Self, 0 [symbolic] // CHECK:STDOUT: %Goat: type = class_type @Goat [concrete] // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %complete_type: = complete_type_witness %empty_struct_type [concrete] // CHECK:STDOUT: %Animal.impl_witness: = impl_witness @Goat.as.Animal.impl.%Animal.impl_witness_table [concrete] // CHECK:STDOUT: %Animal.facet: %Animal.type = facet_value %Goat, (%Animal.impl_witness) [concrete] // CHECK:STDOUT: %type: type = facet_type [concrete] // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self] // CHECK:STDOUT: %pattern_type: type = pattern_type %Animal.type [concrete] // CHECK:STDOUT: %A: %Animal.type = symbolic_binding A, 0 [symbolic] // CHECK:STDOUT: %WalkAnimal.type: type = fn_type @WalkAnimal [concrete] // CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] // CHECK:STDOUT: %WalkAnimal: %WalkAnimal.type = struct_value () [concrete] // CHECK:STDOUT: %F.type: type = fn_type @F [concrete] // CHECK:STDOUT: %F: %F.type = struct_value () [concrete] // CHECK:STDOUT: %WalkAnimal.specific_fn: = specific_function %WalkAnimal, @WalkAnimal(%Animal.facet) [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .Animal = %Animal.decl // CHECK:STDOUT: .Goat = %Goat.decl // CHECK:STDOUT: .WalkAnimal = %WalkAnimal.decl // CHECK:STDOUT: .F = %F.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %Animal.decl: type = interface_decl @Animal [concrete = constants.%Animal.type] {} {} // CHECK:STDOUT: %Goat.decl: type = class_decl @Goat [concrete = constants.%Goat] {} {} // CHECK:STDOUT: impl_decl @Goat.as.Animal.impl [concrete] {} { // CHECK:STDOUT: %Goat.ref: type = name_ref Goat, file.%Goat.decl [concrete = constants.%Goat] // CHECK:STDOUT: %Animal.ref: type = name_ref Animal, file.%Animal.decl [concrete = constants.%Animal.type] // CHECK:STDOUT: } // CHECK:STDOUT: %WalkAnimal.decl: %WalkAnimal.type = fn_decl @WalkAnimal [concrete = constants.%WalkAnimal] { // CHECK:STDOUT: %A.patt: %pattern_type = symbolic_binding_pattern A, 0 [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %.loc20: type = splice_block %Animal.ref [concrete = constants.%Animal.type] { // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %Animal.ref: type = name_ref Animal, file.%Animal.decl [concrete = constants.%Animal.type] // CHECK:STDOUT: } // CHECK:STDOUT: %A.loc20_22.2: %Animal.type = symbolic_binding A, 0 [symbolic = %A.loc20_22.1 (constants.%A)] // CHECK:STDOUT: } // CHECK:STDOUT: %F.decl: %F.type = fn_decl @F [concrete = constants.%F] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: interface @Animal { // CHECK:STDOUT: %Self: %Animal.type = symbolic_binding Self, 0 [symbolic = constants.%Self] // CHECK:STDOUT: %Animal.WithSelf.decl = interface_with_self_decl @Animal [concrete] // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = %Self // CHECK:STDOUT: witness = () // CHECK:STDOUT: // CHECK:STDOUT: !requires: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: impl @Goat.as.Animal.impl: %Goat.ref as %Animal.ref { // CHECK:STDOUT: %Animal.impl_witness_table = impl_witness_table (), @Goat.as.Animal.impl [concrete] // CHECK:STDOUT: %Animal.impl_witness: = impl_witness %Animal.impl_witness_table [concrete = constants.%Animal.impl_witness] // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: witness = %Animal.impl_witness // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @Goat { // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%Goat // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic fn @WalkAnimal(%A.loc20_22.2: %Animal.type) { // CHECK:STDOUT: %A.loc20_22.1: %Animal.type = symbolic_binding A, 0 [symbolic = %A.loc20_22.1 (constants.%A)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: // CHECK:STDOUT: fn() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @F() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %WalkAnimal.ref: %WalkAnimal.type = name_ref WalkAnimal, file.%WalkAnimal.decl [concrete = constants.%WalkAnimal] // CHECK:STDOUT: %Goat.ref: type = name_ref Goat, file.%Goat.decl [concrete = constants.%Goat] // CHECK:STDOUT: %Animal.facet: %Animal.type = facet_value %Goat.ref, (constants.%Animal.impl_witness) [concrete = constants.%Animal.facet] // CHECK:STDOUT: %.loc23: %Animal.type = converted %Goat.ref, %Animal.facet [concrete = constants.%Animal.facet] // CHECK:STDOUT: %WalkAnimal.specific_fn: = specific_function %WalkAnimal.ref, @WalkAnimal(constants.%Animal.facet) [concrete = constants.%WalkAnimal.specific_fn] // CHECK:STDOUT: %WalkAnimal.call: init %empty_tuple.type = call %WalkAnimal.specific_fn() // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @Animal.WithSelf(constants.%Self) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @Animal.WithSelf(constants.%Animal.facet) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @WalkAnimal(constants.%A) { // CHECK:STDOUT: %A.loc20_22.1 => constants.%A // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @WalkAnimal(constants.%Animal.facet) { // CHECK:STDOUT: %A.loc20_22.1 => constants.%Animal.facet // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: } // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/facet/convert_class_type_to_generic_facet_value.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // TODO: Add ranges and switch to "--dump-sem-ir-ranges=only". // EXTRA-ARGS: --dump-sem-ir-ranges=if-present // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/convert.carbon // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/facet/convert_class_type_to_generic_facet_value.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/facet/convert_class_type_to_generic_facet_value.carbon // --- generic_facet_type.carbon library "[[@TEST_NAME]]"; interface Generic(Scalar:! type) { fn F(); } class GenericParam {} class ImplsGeneric {} impl ImplsGeneric as Generic(GenericParam) { fn F() {} } fn CallGenericMethod(T:! type, unused U:! Generic(T)) {} fn G() { CallGenericMethod(GenericParam, ImplsGeneric); } fn PassThroughToGenericMethod(T:! type, U:! Generic(T)) { CallGenericMethod(T, U); } fn H() { PassThroughToGenericMethod(GenericParam, ImplsGeneric); } // --- generic_facet_type_from_implicit_param.carbon library "[[@TEST_NAME]]"; interface Generic(Scalar:! type) { fn F(); } class GenericParam {} class ImplsGeneric {} impl ImplsGeneric as Generic(GenericParam) { fn F() {} } fn CallGenericMethod[T:! type](unused U:! Generic(T), unused t: T) {} fn G() { CallGenericMethod(ImplsGeneric, {} as GenericParam); } // CHECK:STDOUT: --- generic_facet_type.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %type: type = facet_type [concrete] // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self] // CHECK:STDOUT: %pattern_type.98f: type = pattern_type type [concrete] // CHECK:STDOUT: %Scalar: type = symbolic_binding Scalar, 0 [symbolic] // CHECK:STDOUT: %Generic.type.835: type = generic_interface_type @Generic [concrete] // CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] // CHECK:STDOUT: %Generic.generic: %Generic.type.835 = struct_value () [concrete] // CHECK:STDOUT: %Generic.type.03dff7.1: type = facet_type <@Generic, @Generic(%Scalar)> [symbolic] // CHECK:STDOUT: %Self.15d2d5.1: %Generic.type.03dff7.1 = symbolic_binding Self, 1 [symbolic] // CHECK:STDOUT: %Generic.WithSelf.F.type.74390a.1: type = fn_type @Generic.WithSelf.F, @Generic.WithSelf(%Scalar, %Self.15d2d5.1) [symbolic] // CHECK:STDOUT: %Generic.WithSelf.F.baf95a.1: %Generic.WithSelf.F.type.74390a.1 = struct_value () [symbolic] // CHECK:STDOUT: %Generic.assoc_type.22afda.1: type = assoc_entity_type @Generic, @Generic(%Scalar) [symbolic] // CHECK:STDOUT: %assoc0.e0dc00.1: %Generic.assoc_type.22afda.1 = assoc_entity element0, @Generic.WithSelf.%Generic.WithSelf.F.decl [symbolic] // CHECK:STDOUT: %GenericParam: type = class_type @GenericParam [concrete] // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %complete_type: = complete_type_witness %empty_struct_type [concrete] // CHECK:STDOUT: %ImplsGeneric: type = class_type @ImplsGeneric [concrete] // CHECK:STDOUT: %Generic.type.498: type = facet_type <@Generic, @Generic(%GenericParam)> [concrete] // CHECK:STDOUT: %Generic.impl_witness: = impl_witness @ImplsGeneric.as.Generic.impl.%Generic.impl_witness_table [concrete] // CHECK:STDOUT: %Self.1f5: %Generic.type.498 = symbolic_binding Self, 1 [symbolic] // CHECK:STDOUT: %Generic.WithSelf.F.type.a23: type = fn_type @Generic.WithSelf.F, @Generic.WithSelf(%GenericParam, %Self.15d2d5.1) [symbolic] // CHECK:STDOUT: %Generic.WithSelf.F.fe0: %Generic.WithSelf.F.type.a23 = struct_value () [symbolic] // CHECK:STDOUT: %Generic.assoc_type.6dc: type = assoc_entity_type @Generic, @Generic(%GenericParam) [concrete] // CHECK:STDOUT: %assoc0.717: %Generic.assoc_type.6dc = assoc_entity element0, @Generic.WithSelf.%Generic.WithSelf.F.decl [concrete] // CHECK:STDOUT: %ImplsGeneric.as.Generic.impl.F.type: type = fn_type @ImplsGeneric.as.Generic.impl.F [concrete] // CHECK:STDOUT: %ImplsGeneric.as.Generic.impl.F: %ImplsGeneric.as.Generic.impl.F.type = struct_value () [concrete] // CHECK:STDOUT: %Generic.facet: %Generic.type.498 = facet_value %ImplsGeneric, (%Generic.impl_witness) [concrete] // CHECK:STDOUT: %Generic.WithSelf.F.type.c86: type = fn_type @Generic.WithSelf.F, @Generic.WithSelf(%GenericParam, %Generic.facet) [concrete] // CHECK:STDOUT: %Generic.WithSelf.F.1fd: %Generic.WithSelf.F.type.c86 = struct_value () [concrete] // CHECK:STDOUT: %T: type = symbolic_binding T, 0 [symbolic] // CHECK:STDOUT: %Generic.type.03dff7.2: type = facet_type <@Generic, @Generic(%T)> [symbolic] // CHECK:STDOUT: %pattern_type.c49: type = pattern_type %Generic.type.03dff7.2 [symbolic] // CHECK:STDOUT: %U.0f2266.1: %Generic.type.03dff7.2 = symbolic_binding U, 1 [symbolic] // CHECK:STDOUT: %CallGenericMethod.type: type = fn_type @CallGenericMethod [concrete] // CHECK:STDOUT: %CallGenericMethod: %CallGenericMethod.type = struct_value () [concrete] // CHECK:STDOUT: %G.type: type = fn_type @G [concrete] // CHECK:STDOUT: %G: %G.type = struct_value () [concrete] // CHECK:STDOUT: %pattern_type.cba: type = pattern_type %Generic.type.498 [concrete] // CHECK:STDOUT: %CallGenericMethod.specific_fn.d64: = specific_function %CallGenericMethod, @CallGenericMethod(%GenericParam, %Generic.facet) [concrete] // CHECK:STDOUT: %U.0f2266.2: %Generic.type.03dff7.2 = symbolic_binding U, 1 [symbolic] // CHECK:STDOUT: %PassThroughToGenericMethod.type: type = fn_type @PassThroughToGenericMethod [concrete] // CHECK:STDOUT: %PassThroughToGenericMethod: %PassThroughToGenericMethod.type = struct_value () [concrete] // CHECK:STDOUT: %Self.15d2d5.2: %Generic.type.03dff7.2 = symbolic_binding Self, 1 [symbolic] // CHECK:STDOUT: %Generic.WithSelf.F.type.74390a.2: type = fn_type @Generic.WithSelf.F, @Generic.WithSelf(%T, %Self.15d2d5.1) [symbolic] // CHECK:STDOUT: %Generic.WithSelf.F.baf95a.2: %Generic.WithSelf.F.type.74390a.2 = struct_value () [symbolic] // CHECK:STDOUT: %Generic.assoc_type.22afda.2: type = assoc_entity_type @Generic, @Generic(%T) [symbolic] // CHECK:STDOUT: %assoc0.e0dc00.2: %Generic.assoc_type.22afda.2 = assoc_entity element0, @Generic.WithSelf.%Generic.WithSelf.F.decl [symbolic] // CHECK:STDOUT: %CallGenericMethod.specific_fn.6c1: = specific_function %CallGenericMethod, @CallGenericMethod(%T, %U.0f2266.2) [symbolic] // CHECK:STDOUT: %H.type: type = fn_type @H [concrete] // CHECK:STDOUT: %H: %H.type = struct_value () [concrete] // CHECK:STDOUT: %PassThroughToGenericMethod.specific_fn: = specific_function %PassThroughToGenericMethod, @PassThroughToGenericMethod(%GenericParam, %Generic.facet) [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .Generic = %Generic.decl // CHECK:STDOUT: .GenericParam = %GenericParam.decl // CHECK:STDOUT: .ImplsGeneric = %ImplsGeneric.decl // CHECK:STDOUT: .CallGenericMethod = %CallGenericMethod.decl // CHECK:STDOUT: .G = %G.decl // CHECK:STDOUT: .PassThroughToGenericMethod = %PassThroughToGenericMethod.decl // CHECK:STDOUT: .H = %H.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %Generic.decl: %Generic.type.835 = interface_decl @Generic [concrete = constants.%Generic.generic] { // CHECK:STDOUT: %Scalar.patt: %pattern_type.98f = symbolic_binding_pattern Scalar, 0 [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %.loc4_28.1: type = splice_block %.loc4_28.2 [concrete = type] { // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %.loc4_28.2: type = type_literal type [concrete = type] // CHECK:STDOUT: } // CHECK:STDOUT: %Scalar.loc4_19.2: type = symbolic_binding Scalar, 0 [symbolic = %Scalar.loc4_19.1 (constants.%Scalar)] // CHECK:STDOUT: } // CHECK:STDOUT: %GenericParam.decl: type = class_decl @GenericParam [concrete = constants.%GenericParam] {} {} // CHECK:STDOUT: %ImplsGeneric.decl: type = class_decl @ImplsGeneric [concrete = constants.%ImplsGeneric] {} {} // CHECK:STDOUT: impl_decl @ImplsGeneric.as.Generic.impl [concrete] {} { // CHECK:STDOUT: %ImplsGeneric.ref: type = name_ref ImplsGeneric, file.%ImplsGeneric.decl [concrete = constants.%ImplsGeneric] // CHECK:STDOUT: %Generic.ref: %Generic.type.835 = name_ref Generic, file.%Generic.decl [concrete = constants.%Generic.generic] // CHECK:STDOUT: %GenericParam.ref: type = name_ref GenericParam, file.%GenericParam.decl [concrete = constants.%GenericParam] // CHECK:STDOUT: %Generic.type: type = facet_type <@Generic, @Generic(constants.%GenericParam)> [concrete = constants.%Generic.type.498] // CHECK:STDOUT: } // CHECK:STDOUT: %CallGenericMethod.decl: %CallGenericMethod.type = fn_decl @CallGenericMethod [concrete = constants.%CallGenericMethod] { // CHECK:STDOUT: %T.patt: %pattern_type.98f = symbolic_binding_pattern T, 0 [concrete] // CHECK:STDOUT: %U.patt: @CallGenericMethod.%pattern_type (%pattern_type.c49) = symbolic_binding_pattern U, 1 [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %.loc15_26.1: type = splice_block %.loc15_26.2 [concrete = type] { // CHECK:STDOUT: %.Self.2: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %.loc15_26.2: type = type_literal type [concrete = type] // CHECK:STDOUT: } // CHECK:STDOUT: %T.loc15_22.2: type = symbolic_binding T, 0 [symbolic = %T.loc15_22.1 (constants.%T)] // CHECK:STDOUT: %.loc15_52: type = splice_block %Generic.type.loc15_52.2 [symbolic = %Generic.type.loc15_52.1 (constants.%Generic.type.03dff7.2)] { // CHECK:STDOUT: %.Self.1: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %Generic.ref: %Generic.type.835 = name_ref Generic, file.%Generic.decl [concrete = constants.%Generic.generic] // CHECK:STDOUT: %T.ref: type = name_ref T, %T.loc15_22.2 [symbolic = %T.loc15_22.1 (constants.%T)] // CHECK:STDOUT: %Generic.type.loc15_52.2: type = facet_type <@Generic, @Generic(constants.%T)> [symbolic = %Generic.type.loc15_52.1 (constants.%Generic.type.03dff7.2)] // CHECK:STDOUT: } // CHECK:STDOUT: %U.loc15_39.2: @CallGenericMethod.%Generic.type.loc15_52.1 (%Generic.type.03dff7.2) = symbolic_binding U, 1 [symbolic = %U.loc15_39.1 (constants.%U.0f2266.1)] // CHECK:STDOUT: } // CHECK:STDOUT: %G.decl: %G.type = fn_decl @G [concrete = constants.%G] {} {} // CHECK:STDOUT: %PassThroughToGenericMethod.decl: %PassThroughToGenericMethod.type = fn_decl @PassThroughToGenericMethod [concrete = constants.%PassThroughToGenericMethod] { // CHECK:STDOUT: %T.patt: %pattern_type.98f = symbolic_binding_pattern T, 0 [concrete] // CHECK:STDOUT: %U.patt: @PassThroughToGenericMethod.%pattern_type (%pattern_type.c49) = symbolic_binding_pattern U, 1 [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %.loc21_35.1: type = splice_block %.loc21_35.2 [concrete = type] { // CHECK:STDOUT: %.Self.2: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %.loc21_35.2: type = type_literal type [concrete = type] // CHECK:STDOUT: } // CHECK:STDOUT: %T.loc21_31.2: type = symbolic_binding T, 0 [symbolic = %T.loc21_31.1 (constants.%T)] // CHECK:STDOUT: %.loc21_54: type = splice_block %Generic.type.loc21_54.2 [symbolic = %Generic.type.loc21_54.1 (constants.%Generic.type.03dff7.2)] { // CHECK:STDOUT: %.Self.1: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %Generic.ref: %Generic.type.835 = name_ref Generic, file.%Generic.decl [concrete = constants.%Generic.generic] // CHECK:STDOUT: %T.ref.loc21: type = name_ref T, %T.loc21_31.2 [symbolic = %T.loc21_31.1 (constants.%T)] // CHECK:STDOUT: %Generic.type.loc21_54.2: type = facet_type <@Generic, @Generic(constants.%T)> [symbolic = %Generic.type.loc21_54.1 (constants.%Generic.type.03dff7.2)] // CHECK:STDOUT: } // CHECK:STDOUT: %U.loc21_41.2: @PassThroughToGenericMethod.%Generic.type.loc21_54.1 (%Generic.type.03dff7.2) = symbolic_binding U, 1 [symbolic = %U.loc21_41.1 (constants.%U.0f2266.2)] // CHECK:STDOUT: } // CHECK:STDOUT: %H.decl: %H.type = fn_decl @H [concrete = constants.%H] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic interface @Generic(%Scalar.loc4_19.2: type) { // CHECK:STDOUT: %Scalar.loc4_19.1: type = symbolic_binding Scalar, 0 [symbolic = %Scalar.loc4_19.1 (constants.%Scalar)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %Generic.type: type = facet_type <@Generic, @Generic(%Scalar.loc4_19.1)> [symbolic = %Generic.type (constants.%Generic.type.03dff7.1)] // CHECK:STDOUT: %Self.loc4_34.2: @Generic.%Generic.type (%Generic.type.03dff7.1) = symbolic_binding Self, 1 [symbolic = %Self.loc4_34.2 (constants.%Self.15d2d5.1)] // CHECK:STDOUT: // CHECK:STDOUT: interface { // CHECK:STDOUT: %Self.loc4_34.1: @Generic.%Generic.type (%Generic.type.03dff7.1) = symbolic_binding Self, 1 [symbolic = %Self.loc4_34.2 (constants.%Self.15d2d5.1)] // CHECK:STDOUT: %Generic.WithSelf.decl = interface_with_self_decl @Generic [concrete] // CHECK:STDOUT: // CHECK:STDOUT: !with Self: // CHECK:STDOUT: %Generic.WithSelf.F.decl: @Generic.WithSelf.%Generic.WithSelf.F.type (%Generic.WithSelf.F.type.74390a.1) = fn_decl @Generic.WithSelf.F [symbolic = @Generic.WithSelf.%Generic.WithSelf.F (constants.%Generic.WithSelf.F.baf95a.1)] {} {} // CHECK:STDOUT: %assoc0.loc5_9.1: @Generic.WithSelf.%Generic.assoc_type (%Generic.assoc_type.22afda.1) = assoc_entity element0, %Generic.WithSelf.F.decl [symbolic = %assoc0.loc5_9.2 (constants.%assoc0.e0dc00.1)] // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = %Self.loc4_34.1 // CHECK:STDOUT: .F = @Generic.WithSelf.%assoc0.loc5_9.1 // CHECK:STDOUT: witness = (@Generic.WithSelf.%Generic.WithSelf.F.decl) // CHECK:STDOUT: // CHECK:STDOUT: !requires: // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: impl @ImplsGeneric.as.Generic.impl: %ImplsGeneric.ref as %Generic.type { // CHECK:STDOUT: %ImplsGeneric.as.Generic.impl.F.decl: %ImplsGeneric.as.Generic.impl.F.type = fn_decl @ImplsGeneric.as.Generic.impl.F [concrete = constants.%ImplsGeneric.as.Generic.impl.F] {} {} // CHECK:STDOUT: %Generic.impl_witness_table = impl_witness_table (%ImplsGeneric.as.Generic.impl.F.decl), @ImplsGeneric.as.Generic.impl [concrete] // CHECK:STDOUT: %Generic.impl_witness: = impl_witness %Generic.impl_witness_table [concrete = constants.%Generic.impl_witness] // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .F = %ImplsGeneric.as.Generic.impl.F.decl // CHECK:STDOUT: witness = %Generic.impl_witness // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @GenericParam { // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%GenericParam // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @ImplsGeneric { // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%ImplsGeneric // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic fn @Generic.WithSelf.F(@Generic.%Scalar.loc4_19.2: type, @Generic.%Self.loc4_34.1: @Generic.%Generic.type (%Generic.type.03dff7.1)) { // CHECK:STDOUT: fn(); // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @ImplsGeneric.as.Generic.impl.F() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic fn @CallGenericMethod(%T.loc15_22.2: type, %U.loc15_39.2: @CallGenericMethod.%Generic.type.loc15_52.1 (%Generic.type.03dff7.2)) { // CHECK:STDOUT: %T.loc15_22.1: type = symbolic_binding T, 0 [symbolic = %T.loc15_22.1 (constants.%T)] // CHECK:STDOUT: %Generic.type.loc15_52.1: type = facet_type <@Generic, @Generic(%T.loc15_22.1)> [symbolic = %Generic.type.loc15_52.1 (constants.%Generic.type.03dff7.2)] // CHECK:STDOUT: %U.loc15_39.1: @CallGenericMethod.%Generic.type.loc15_52.1 (%Generic.type.03dff7.2) = symbolic_binding U, 1 [symbolic = %U.loc15_39.1 (constants.%U.0f2266.1)] // CHECK:STDOUT: %pattern_type: type = pattern_type %Generic.type.loc15_52.1 [symbolic = %pattern_type (constants.%pattern_type.c49)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: // CHECK:STDOUT: fn() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @G() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %CallGenericMethod.ref: %CallGenericMethod.type = name_ref CallGenericMethod, file.%CallGenericMethod.decl [concrete = constants.%CallGenericMethod] // CHECK:STDOUT: %GenericParam.ref: type = name_ref GenericParam, file.%GenericParam.decl [concrete = constants.%GenericParam] // CHECK:STDOUT: %ImplsGeneric.ref: type = name_ref ImplsGeneric, file.%ImplsGeneric.decl [concrete = constants.%ImplsGeneric] // CHECK:STDOUT: %Generic.facet: %Generic.type.498 = facet_value constants.%ImplsGeneric, (constants.%Generic.impl_witness) [concrete = constants.%Generic.facet] // CHECK:STDOUT: %.loc18: %Generic.type.498 = converted constants.%ImplsGeneric, %Generic.facet [concrete = constants.%Generic.facet] // CHECK:STDOUT: %CallGenericMethod.specific_fn: = specific_function %CallGenericMethod.ref, @CallGenericMethod(constants.%GenericParam, constants.%Generic.facet) [concrete = constants.%CallGenericMethod.specific_fn.d64] // CHECK:STDOUT: %CallGenericMethod.call: init %empty_tuple.type = call %CallGenericMethod.specific_fn() // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic fn @PassThroughToGenericMethod(%T.loc21_31.2: type, %U.loc21_41.2: @PassThroughToGenericMethod.%Generic.type.loc21_54.1 (%Generic.type.03dff7.2)) { // CHECK:STDOUT: %T.loc21_31.1: type = symbolic_binding T, 0 [symbolic = %T.loc21_31.1 (constants.%T)] // CHECK:STDOUT: %Generic.type.loc21_54.1: type = facet_type <@Generic, @Generic(%T.loc21_31.1)> [symbolic = %Generic.type.loc21_54.1 (constants.%Generic.type.03dff7.2)] // CHECK:STDOUT: %U.loc21_41.1: @PassThroughToGenericMethod.%Generic.type.loc21_54.1 (%Generic.type.03dff7.2) = symbolic_binding U, 1 [symbolic = %U.loc21_41.1 (constants.%U.0f2266.2)] // CHECK:STDOUT: %pattern_type: type = pattern_type %Generic.type.loc21_54.1 [symbolic = %pattern_type (constants.%pattern_type.c49)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %CallGenericMethod.specific_fn.loc22_3.2: = specific_function constants.%CallGenericMethod, @CallGenericMethod(%T.loc21_31.1, %U.loc21_41.1) [symbolic = %CallGenericMethod.specific_fn.loc22_3.2 (constants.%CallGenericMethod.specific_fn.6c1)] // CHECK:STDOUT: // CHECK:STDOUT: fn() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %CallGenericMethod.ref: %CallGenericMethod.type = name_ref CallGenericMethod, file.%CallGenericMethod.decl [concrete = constants.%CallGenericMethod] // CHECK:STDOUT: %T.ref.loc22: type = name_ref T, %T.loc21_31.2 [symbolic = %T.loc21_31.1 (constants.%T)] // CHECK:STDOUT: %U.ref: @PassThroughToGenericMethod.%Generic.type.loc21_54.1 (%Generic.type.03dff7.2) = name_ref U, %U.loc21_41.2 [symbolic = %U.loc21_41.1 (constants.%U.0f2266.2)] // CHECK:STDOUT: %CallGenericMethod.specific_fn.loc22_3.1: = specific_function %CallGenericMethod.ref, @CallGenericMethod(constants.%T, constants.%U.0f2266.2) [symbolic = %CallGenericMethod.specific_fn.loc22_3.2 (constants.%CallGenericMethod.specific_fn.6c1)] // CHECK:STDOUT: %CallGenericMethod.call: init %empty_tuple.type = call %CallGenericMethod.specific_fn.loc22_3.1() // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @H() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %PassThroughToGenericMethod.ref: %PassThroughToGenericMethod.type = name_ref PassThroughToGenericMethod, file.%PassThroughToGenericMethod.decl [concrete = constants.%PassThroughToGenericMethod] // CHECK:STDOUT: %GenericParam.ref: type = name_ref GenericParam, file.%GenericParam.decl [concrete = constants.%GenericParam] // CHECK:STDOUT: %ImplsGeneric.ref: type = name_ref ImplsGeneric, file.%ImplsGeneric.decl [concrete = constants.%ImplsGeneric] // CHECK:STDOUT: %Generic.facet: %Generic.type.498 = facet_value constants.%ImplsGeneric, (constants.%Generic.impl_witness) [concrete = constants.%Generic.facet] // CHECK:STDOUT: %.loc26: %Generic.type.498 = converted constants.%ImplsGeneric, %Generic.facet [concrete = constants.%Generic.facet] // CHECK:STDOUT: %PassThroughToGenericMethod.specific_fn: = specific_function %PassThroughToGenericMethod.ref, @PassThroughToGenericMethod(constants.%GenericParam, constants.%Generic.facet) [concrete = constants.%PassThroughToGenericMethod.specific_fn] // CHECK:STDOUT: %PassThroughToGenericMethod.call: init %empty_tuple.type = call %PassThroughToGenericMethod.specific_fn() // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @Generic(constants.%Scalar) { // CHECK:STDOUT: %Scalar.loc4_19.1 => constants.%Scalar // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @Generic.WithSelf(constants.%Scalar, constants.%Self.15d2d5.1) {} // CHECK:STDOUT: // CHECK:STDOUT: specific @Generic.WithSelf.F(constants.%Scalar, constants.%Self.15d2d5.1) {} // CHECK:STDOUT: // CHECK:STDOUT: specific @Generic(constants.%GenericParam) { // CHECK:STDOUT: %Scalar.loc4_19.1 => constants.%GenericParam // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %Generic.type => constants.%Generic.type.498 // CHECK:STDOUT: %Self.loc4_34.2 => constants.%Self.1f5 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @Generic.WithSelf(constants.%GenericParam, constants.%Self.15d2d5.1) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: %Scalar => constants.%GenericParam // CHECK:STDOUT: %Generic.type => constants.%Generic.type.498 // CHECK:STDOUT: %Self => constants.%Self.15d2d5.1 // CHECK:STDOUT: %Generic.WithSelf.F.type => constants.%Generic.WithSelf.F.type.a23 // CHECK:STDOUT: %Generic.WithSelf.F => constants.%Generic.WithSelf.F.fe0 // CHECK:STDOUT: %Generic.assoc_type => constants.%Generic.assoc_type.6dc // CHECK:STDOUT: %assoc0.loc5_9.2 => constants.%assoc0.717 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @Generic.WithSelf(constants.%GenericParam, constants.%Generic.facet) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: %Scalar => constants.%GenericParam // CHECK:STDOUT: %Generic.type => constants.%Generic.type.498 // CHECK:STDOUT: %Self => constants.%Generic.facet // CHECK:STDOUT: %Generic.WithSelf.F.type => constants.%Generic.WithSelf.F.type.c86 // CHECK:STDOUT: %Generic.WithSelf.F => constants.%Generic.WithSelf.F.1fd // CHECK:STDOUT: %Generic.assoc_type => constants.%Generic.assoc_type.6dc // CHECK:STDOUT: %assoc0.loc5_9.2 => constants.%assoc0.717 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @Generic.WithSelf.F(constants.%GenericParam, constants.%Generic.facet) {} // CHECK:STDOUT: // CHECK:STDOUT: specific @Generic(constants.%T) { // CHECK:STDOUT: %Scalar.loc4_19.1 => constants.%T // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %Generic.type => constants.%Generic.type.03dff7.2 // CHECK:STDOUT: %Self.loc4_34.2 => constants.%Self.15d2d5.2 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @CallGenericMethod(constants.%T, constants.%U.0f2266.1) { // CHECK:STDOUT: %T.loc15_22.1 => constants.%T // CHECK:STDOUT: %Generic.type.loc15_52.1 => constants.%Generic.type.03dff7.2 // CHECK:STDOUT: %U.loc15_39.1 => constants.%U.0f2266.1 // CHECK:STDOUT: %pattern_type => constants.%pattern_type.c49 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @CallGenericMethod(constants.%GenericParam, constants.%Generic.facet) { // CHECK:STDOUT: %T.loc15_22.1 => constants.%GenericParam // CHECK:STDOUT: %Generic.type.loc15_52.1 => constants.%Generic.type.498 // CHECK:STDOUT: %U.loc15_39.1 => constants.%Generic.facet // CHECK:STDOUT: %pattern_type => constants.%pattern_type.cba // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @PassThroughToGenericMethod(constants.%T, constants.%U.0f2266.2) { // CHECK:STDOUT: %T.loc21_31.1 => constants.%T // CHECK:STDOUT: %Generic.type.loc21_54.1 => constants.%Generic.type.03dff7.2 // CHECK:STDOUT: %U.loc21_41.1 => constants.%U.0f2266.2 // CHECK:STDOUT: %pattern_type => constants.%pattern_type.c49 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @Generic.WithSelf(constants.%T, constants.%Self.15d2d5.1) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: %Scalar => constants.%T // CHECK:STDOUT: %Generic.type => constants.%Generic.type.03dff7.2 // CHECK:STDOUT: %Self => constants.%Self.15d2d5.1 // CHECK:STDOUT: %Generic.WithSelf.F.type => constants.%Generic.WithSelf.F.type.74390a.2 // CHECK:STDOUT: %Generic.WithSelf.F => constants.%Generic.WithSelf.F.baf95a.2 // CHECK:STDOUT: %Generic.assoc_type => constants.%Generic.assoc_type.22afda.2 // CHECK:STDOUT: %assoc0.loc5_9.2 => constants.%assoc0.e0dc00.2 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @CallGenericMethod(constants.%T, constants.%U.0f2266.2) { // CHECK:STDOUT: %T.loc15_22.1 => constants.%T // CHECK:STDOUT: %Generic.type.loc15_52.1 => constants.%Generic.type.03dff7.2 // CHECK:STDOUT: %U.loc15_39.1 => constants.%U.0f2266.2 // CHECK:STDOUT: %pattern_type => constants.%pattern_type.c49 // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @PassThroughToGenericMethod(constants.%GenericParam, constants.%Generic.facet) { // CHECK:STDOUT: %T.loc21_31.1 => constants.%GenericParam // CHECK:STDOUT: %Generic.type.loc21_54.1 => constants.%Generic.type.498 // CHECK:STDOUT: %U.loc21_41.1 => constants.%Generic.facet // CHECK:STDOUT: %pattern_type => constants.%pattern_type.cba // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %CallGenericMethod.specific_fn.loc22_3.2 => constants.%CallGenericMethod.specific_fn.d64 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- generic_facet_type_from_implicit_param.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %type: type = facet_type [concrete] // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self] // CHECK:STDOUT: %pattern_type.98f: type = pattern_type type [concrete] // CHECK:STDOUT: %Scalar: type = symbolic_binding Scalar, 0 [symbolic] // CHECK:STDOUT: %Generic.type.835: type = generic_interface_type @Generic [concrete] // CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] // CHECK:STDOUT: %Generic.generic: %Generic.type.835 = struct_value () [concrete] // CHECK:STDOUT: %Generic.type.03dff7.1: type = facet_type <@Generic, @Generic(%Scalar)> [symbolic] // CHECK:STDOUT: %Self.15d: %Generic.type.03dff7.1 = symbolic_binding Self, 1 [symbolic] // CHECK:STDOUT: %Generic.WithSelf.F.type.743: type = fn_type @Generic.WithSelf.F, @Generic.WithSelf(%Scalar, %Self.15d) [symbolic] // CHECK:STDOUT: %Generic.WithSelf.F.baf: %Generic.WithSelf.F.type.743 = struct_value () [symbolic] // CHECK:STDOUT: %Generic.assoc_type.22a: type = assoc_entity_type @Generic, @Generic(%Scalar) [symbolic] // CHECK:STDOUT: %assoc0.e0d: %Generic.assoc_type.22a = assoc_entity element0, @Generic.WithSelf.%Generic.WithSelf.F.decl [symbolic] // CHECK:STDOUT: %GenericParam: type = class_type @GenericParam [concrete] // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %complete_type: = complete_type_witness %empty_struct_type [concrete] // CHECK:STDOUT: %ImplsGeneric: type = class_type @ImplsGeneric [concrete] // CHECK:STDOUT: %Generic.type.498: type = facet_type <@Generic, @Generic(%GenericParam)> [concrete] // CHECK:STDOUT: %Generic.impl_witness: = impl_witness @ImplsGeneric.as.Generic.impl.%Generic.impl_witness_table [concrete] // CHECK:STDOUT: %Self.1f5: %Generic.type.498 = symbolic_binding Self, 1 [symbolic] // CHECK:STDOUT: %Generic.WithSelf.F.type.a23: type = fn_type @Generic.WithSelf.F, @Generic.WithSelf(%GenericParam, %Self.15d) [symbolic] // CHECK:STDOUT: %Generic.WithSelf.F.fe0: %Generic.WithSelf.F.type.a23 = struct_value () [symbolic] // CHECK:STDOUT: %Generic.assoc_type.6dc: type = assoc_entity_type @Generic, @Generic(%GenericParam) [concrete] // CHECK:STDOUT: %assoc0.717: %Generic.assoc_type.6dc = assoc_entity element0, @Generic.WithSelf.%Generic.WithSelf.F.decl [concrete] // CHECK:STDOUT: %ImplsGeneric.as.Generic.impl.F.type: type = fn_type @ImplsGeneric.as.Generic.impl.F [concrete] // CHECK:STDOUT: %ImplsGeneric.as.Generic.impl.F: %ImplsGeneric.as.Generic.impl.F.type = struct_value () [concrete] // CHECK:STDOUT: %Generic.facet: %Generic.type.498 = facet_value %ImplsGeneric, (%Generic.impl_witness) [concrete] // CHECK:STDOUT: %Generic.WithSelf.F.type.c86: type = fn_type @Generic.WithSelf.F, @Generic.WithSelf(%GenericParam, %Generic.facet) [concrete] // CHECK:STDOUT: %Generic.WithSelf.F.1fd: %Generic.WithSelf.F.type.c86 = struct_value () [concrete] // CHECK:STDOUT: %T: type = symbolic_binding T, 0 [symbolic] // CHECK:STDOUT: %Generic.type.03dff7.2: type = facet_type <@Generic, @Generic(%T)> [symbolic] // CHECK:STDOUT: %pattern_type.c49: type = pattern_type %Generic.type.03dff7.2 [symbolic] // CHECK:STDOUT: %U: %Generic.type.03dff7.2 = symbolic_binding U, 1 [symbolic] // CHECK:STDOUT: %pattern_type.51d: type = pattern_type %T [symbolic] // CHECK:STDOUT: %CallGenericMethod.type: type = fn_type @CallGenericMethod [concrete] // CHECK:STDOUT: %CallGenericMethod: %CallGenericMethod.type = struct_value () [concrete] // CHECK:STDOUT: %require_complete: = require_complete_type %T [symbolic] // CHECK:STDOUT: %G.type: type = fn_type @G [concrete] // CHECK:STDOUT: %G: %G.type = struct_value () [concrete] // CHECK:STDOUT: %empty_struct: %empty_struct_type = struct_value () [concrete] // CHECK:STDOUT: %GenericParam.val: %GenericParam = struct_value () [concrete] // CHECK:STDOUT: %pattern_type.cba: type = pattern_type %Generic.type.498 [concrete] // CHECK:STDOUT: %pattern_type.27a: type = pattern_type %GenericParam [concrete] // CHECK:STDOUT: %CallGenericMethod.specific_fn: = specific_function %CallGenericMethod, @CallGenericMethod(%GenericParam, %Generic.facet) [concrete] // CHECK:STDOUT: %Destroy.type: type = facet_type <@Destroy> [concrete] // CHECK:STDOUT: %Destroy.Op.type: type = fn_type @Destroy.Op [concrete] // CHECK:STDOUT: %Destroy.Op: %Destroy.Op.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: .Destroy = %Core.Destroy // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: %Core.Destroy: type = import_ref Core//prelude/parts/destroy, Destroy, loaded [concrete = constants.%Destroy.type] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .Generic = %Generic.decl // CHECK:STDOUT: .GenericParam = %GenericParam.decl // CHECK:STDOUT: .ImplsGeneric = %ImplsGeneric.decl // CHECK:STDOUT: .CallGenericMethod = %CallGenericMethod.decl // CHECK:STDOUT: .G = %G.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %Generic.decl: %Generic.type.835 = interface_decl @Generic [concrete = constants.%Generic.generic] { // CHECK:STDOUT: %Scalar.patt: %pattern_type.98f = symbolic_binding_pattern Scalar, 0 [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %.loc4_28.1: type = splice_block %.loc4_28.2 [concrete = type] { // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %.loc4_28.2: type = type_literal type [concrete = type] // CHECK:STDOUT: } // CHECK:STDOUT: %Scalar.loc4_19.2: type = symbolic_binding Scalar, 0 [symbolic = %Scalar.loc4_19.1 (constants.%Scalar)] // CHECK:STDOUT: } // CHECK:STDOUT: %GenericParam.decl: type = class_decl @GenericParam [concrete = constants.%GenericParam] {} {} // CHECK:STDOUT: %ImplsGeneric.decl: type = class_decl @ImplsGeneric [concrete = constants.%ImplsGeneric] {} {} // CHECK:STDOUT: impl_decl @ImplsGeneric.as.Generic.impl [concrete] {} { // CHECK:STDOUT: %ImplsGeneric.ref: type = name_ref ImplsGeneric, file.%ImplsGeneric.decl [concrete = constants.%ImplsGeneric] // CHECK:STDOUT: %Generic.ref: %Generic.type.835 = name_ref Generic, file.%Generic.decl [concrete = constants.%Generic.generic] // CHECK:STDOUT: %GenericParam.ref: type = name_ref GenericParam, file.%GenericParam.decl [concrete = constants.%GenericParam] // CHECK:STDOUT: %Generic.type: type = facet_type <@Generic, @Generic(constants.%GenericParam)> [concrete = constants.%Generic.type.498] // CHECK:STDOUT: } // CHECK:STDOUT: %CallGenericMethod.decl: %CallGenericMethod.type = fn_decl @CallGenericMethod [concrete = constants.%CallGenericMethod] { // CHECK:STDOUT: %T.patt: %pattern_type.98f = symbolic_binding_pattern T, 0 [concrete] // CHECK:STDOUT: %U.patt: @CallGenericMethod.%pattern_type.loc15_39 (%pattern_type.c49) = symbolic_binding_pattern U, 1 [concrete] // CHECK:STDOUT: %t.patt: @CallGenericMethod.%pattern_type.loc15_62 (%pattern_type.51d) = value_binding_pattern t [concrete] // CHECK:STDOUT: %t.param_patt: @CallGenericMethod.%pattern_type.loc15_62 (%pattern_type.51d) = value_param_pattern %t.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %.loc15_26.1: type = splice_block %.loc15_26.2 [concrete = type] { // CHECK:STDOUT: %.Self.2: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %.loc15_26.2: type = type_literal type [concrete = type] // CHECK:STDOUT: } // CHECK:STDOUT: %T.loc15_22.2: type = symbolic_binding T, 0 [symbolic = %T.loc15_22.1 (constants.%T)] // CHECK:STDOUT: %.loc15_52: type = splice_block %Generic.type.loc15_52.2 [symbolic = %Generic.type.loc15_52.1 (constants.%Generic.type.03dff7.2)] { // CHECK:STDOUT: %.Self.1: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %Generic.ref: %Generic.type.835 = name_ref Generic, file.%Generic.decl [concrete = constants.%Generic.generic] // CHECK:STDOUT: %T.ref.loc15_51: type = name_ref T, %T.loc15_22.2 [symbolic = %T.loc15_22.1 (constants.%T)] // CHECK:STDOUT: %Generic.type.loc15_52.2: type = facet_type <@Generic, @Generic(constants.%T)> [symbolic = %Generic.type.loc15_52.1 (constants.%Generic.type.03dff7.2)] // CHECK:STDOUT: } // CHECK:STDOUT: %U.loc15_39.2: @CallGenericMethod.%Generic.type.loc15_52.1 (%Generic.type.03dff7.2) = symbolic_binding U, 1 [symbolic = %U.loc15_39.1 (constants.%U)] // CHECK:STDOUT: %t.param: @CallGenericMethod.%T.loc15_22.1 (%T) = value_param call_param0 // CHECK:STDOUT: %T.ref.loc15_65: type = name_ref T, %T.loc15_22.2 [symbolic = %T.loc15_22.1 (constants.%T)] // CHECK:STDOUT: %t: @CallGenericMethod.%T.loc15_22.1 (%T) = value_binding t, %t.param // CHECK:STDOUT: } // CHECK:STDOUT: %G.decl: %G.type = fn_decl @G [concrete = constants.%G] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic interface @Generic(%Scalar.loc4_19.2: type) { // CHECK:STDOUT: %Scalar.loc4_19.1: type = symbolic_binding Scalar, 0 [symbolic = %Scalar.loc4_19.1 (constants.%Scalar)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %Generic.type: type = facet_type <@Generic, @Generic(%Scalar.loc4_19.1)> [symbolic = %Generic.type (constants.%Generic.type.03dff7.1)] // CHECK:STDOUT: %Self.loc4_34.2: @Generic.%Generic.type (%Generic.type.03dff7.1) = symbolic_binding Self, 1 [symbolic = %Self.loc4_34.2 (constants.%Self.15d)] // CHECK:STDOUT: // CHECK:STDOUT: interface { // CHECK:STDOUT: %Self.loc4_34.1: @Generic.%Generic.type (%Generic.type.03dff7.1) = symbolic_binding Self, 1 [symbolic = %Self.loc4_34.2 (constants.%Self.15d)] // CHECK:STDOUT: %Generic.WithSelf.decl = interface_with_self_decl @Generic [concrete] // CHECK:STDOUT: // CHECK:STDOUT: !with Self: // CHECK:STDOUT: %Generic.WithSelf.F.decl: @Generic.WithSelf.%Generic.WithSelf.F.type (%Generic.WithSelf.F.type.743) = fn_decl @Generic.WithSelf.F [symbolic = @Generic.WithSelf.%Generic.WithSelf.F (constants.%Generic.WithSelf.F.baf)] {} {} // CHECK:STDOUT: %assoc0.loc5_9.1: @Generic.WithSelf.%Generic.assoc_type (%Generic.assoc_type.22a) = assoc_entity element0, %Generic.WithSelf.F.decl [symbolic = %assoc0.loc5_9.2 (constants.%assoc0.e0d)] // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = %Self.loc4_34.1 // CHECK:STDOUT: .F = @Generic.WithSelf.%assoc0.loc5_9.1 // CHECK:STDOUT: witness = (@Generic.WithSelf.%Generic.WithSelf.F.decl) // CHECK:STDOUT: // CHECK:STDOUT: !requires: // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: impl @ImplsGeneric.as.Generic.impl: %ImplsGeneric.ref as %Generic.type { // CHECK:STDOUT: %ImplsGeneric.as.Generic.impl.F.decl: %ImplsGeneric.as.Generic.impl.F.type = fn_decl @ImplsGeneric.as.Generic.impl.F [concrete = constants.%ImplsGeneric.as.Generic.impl.F] {} {} // CHECK:STDOUT: %Generic.impl_witness_table = impl_witness_table (%ImplsGeneric.as.Generic.impl.F.decl), @ImplsGeneric.as.Generic.impl [concrete] // CHECK:STDOUT: %Generic.impl_witness: = impl_witness %Generic.impl_witness_table [concrete = constants.%Generic.impl_witness] // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .F = %ImplsGeneric.as.Generic.impl.F.decl // CHECK:STDOUT: witness = %Generic.impl_witness // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @GenericParam { // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%GenericParam // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @ImplsGeneric { // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%ImplsGeneric // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic fn @Generic.WithSelf.F(@Generic.%Scalar.loc4_19.2: type, @Generic.%Self.loc4_34.1: @Generic.%Generic.type (%Generic.type.03dff7.1)) { // CHECK:STDOUT: fn(); // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @ImplsGeneric.as.Generic.impl.F() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic fn @CallGenericMethod(%T.loc15_22.2: type, %U.loc15_39.2: @CallGenericMethod.%Generic.type.loc15_52.1 (%Generic.type.03dff7.2)) { // CHECK:STDOUT: %T.loc15_22.1: type = symbolic_binding T, 0 [symbolic = %T.loc15_22.1 (constants.%T)] // CHECK:STDOUT: %Generic.type.loc15_52.1: type = facet_type <@Generic, @Generic(%T.loc15_22.1)> [symbolic = %Generic.type.loc15_52.1 (constants.%Generic.type.03dff7.2)] // CHECK:STDOUT: %U.loc15_39.1: @CallGenericMethod.%Generic.type.loc15_52.1 (%Generic.type.03dff7.2) = symbolic_binding U, 1 [symbolic = %U.loc15_39.1 (constants.%U)] // CHECK:STDOUT: %pattern_type.loc15_39: type = pattern_type %Generic.type.loc15_52.1 [symbolic = %pattern_type.loc15_39 (constants.%pattern_type.c49)] // CHECK:STDOUT: %pattern_type.loc15_62: type = pattern_type %T.loc15_22.1 [symbolic = %pattern_type.loc15_62 (constants.%pattern_type.51d)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete: = require_complete_type %T.loc15_22.1 [symbolic = %require_complete (constants.%require_complete)] // CHECK:STDOUT: // CHECK:STDOUT: fn(%t.param: @CallGenericMethod.%T.loc15_22.1 (%T)) { // CHECK:STDOUT: !entry: // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @G() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %CallGenericMethod.ref: %CallGenericMethod.type = name_ref CallGenericMethod, file.%CallGenericMethod.decl [concrete = constants.%CallGenericMethod] // CHECK:STDOUT: %ImplsGeneric.ref: type = name_ref ImplsGeneric, file.%ImplsGeneric.decl [concrete = constants.%ImplsGeneric] // CHECK:STDOUT: %.loc18_36.1: %empty_struct_type = struct_literal () [concrete = constants.%empty_struct] // CHECK:STDOUT: %GenericParam.ref: type = name_ref GenericParam, file.%GenericParam.decl [concrete = constants.%GenericParam] // CHECK:STDOUT: %.loc18_36.2: ref %GenericParam = temporary_storage // CHECK:STDOUT: %.loc18_36.3: init %GenericParam to %.loc18_36.2 = class_init () [concrete = constants.%GenericParam.val] // CHECK:STDOUT: %.loc18_38.1: init %GenericParam = converted %.loc18_36.1, %.loc18_36.3 [concrete = constants.%GenericParam.val] // CHECK:STDOUT: %Generic.facet: %Generic.type.498 = facet_value constants.%ImplsGeneric, (constants.%Generic.impl_witness) [concrete = constants.%Generic.facet] // CHECK:STDOUT: %.loc18_53: %Generic.type.498 = converted constants.%ImplsGeneric, %Generic.facet [concrete = constants.%Generic.facet] // CHECK:STDOUT: %CallGenericMethod.specific_fn: = specific_function %CallGenericMethod.ref, @CallGenericMethod(constants.%GenericParam, constants.%Generic.facet) [concrete = constants.%CallGenericMethod.specific_fn] // CHECK:STDOUT: %.loc18_38.2: ref %GenericParam = temporary %.loc18_36.2, %.loc18_38.1 // CHECK:STDOUT: %.loc18_38.3: %GenericParam = acquire_value %.loc18_38.2 // CHECK:STDOUT: %CallGenericMethod.call: init %empty_tuple.type = call %CallGenericMethod.specific_fn(%.loc18_38.3) // CHECK:STDOUT: %Destroy.Op.bound: = bound_method %.loc18_38.2, constants.%Destroy.Op // CHECK:STDOUT: %Destroy.Op.call: init %empty_tuple.type = call %Destroy.Op.bound(%.loc18_38.2) // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @Destroy.Op(%self.param: ref %GenericParam) = "no_op"; // CHECK:STDOUT: // CHECK:STDOUT: specific @Generic(constants.%Scalar) { // CHECK:STDOUT: %Scalar.loc4_19.1 => constants.%Scalar // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @Generic.WithSelf(constants.%Scalar, constants.%Self.15d) {} // CHECK:STDOUT: // CHECK:STDOUT: specific @Generic.WithSelf.F(constants.%Scalar, constants.%Self.15d) {} // CHECK:STDOUT: // CHECK:STDOUT: specific @Generic(constants.%GenericParam) { // CHECK:STDOUT: %Scalar.loc4_19.1 => constants.%GenericParam // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %Generic.type => constants.%Generic.type.498 // CHECK:STDOUT: %Self.loc4_34.2 => constants.%Self.1f5 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @Generic.WithSelf(constants.%GenericParam, constants.%Self.15d) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: %Scalar => constants.%GenericParam // CHECK:STDOUT: %Generic.type => constants.%Generic.type.498 // CHECK:STDOUT: %Self => constants.%Self.15d // CHECK:STDOUT: %Generic.WithSelf.F.type => constants.%Generic.WithSelf.F.type.a23 // CHECK:STDOUT: %Generic.WithSelf.F => constants.%Generic.WithSelf.F.fe0 // CHECK:STDOUT: %Generic.assoc_type => constants.%Generic.assoc_type.6dc // CHECK:STDOUT: %assoc0.loc5_9.2 => constants.%assoc0.717 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @Generic.WithSelf(constants.%GenericParam, constants.%Generic.facet) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: %Scalar => constants.%GenericParam // CHECK:STDOUT: %Generic.type => constants.%Generic.type.498 // CHECK:STDOUT: %Self => constants.%Generic.facet // CHECK:STDOUT: %Generic.WithSelf.F.type => constants.%Generic.WithSelf.F.type.c86 // CHECK:STDOUT: %Generic.WithSelf.F => constants.%Generic.WithSelf.F.1fd // CHECK:STDOUT: %Generic.assoc_type => constants.%Generic.assoc_type.6dc // CHECK:STDOUT: %assoc0.loc5_9.2 => constants.%assoc0.717 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @Generic.WithSelf.F(constants.%GenericParam, constants.%Generic.facet) {} // CHECK:STDOUT: // CHECK:STDOUT: specific @Generic(constants.%T) { // CHECK:STDOUT: %Scalar.loc4_19.1 => constants.%T // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @CallGenericMethod(constants.%T, constants.%U) { // CHECK:STDOUT: %T.loc15_22.1 => constants.%T // CHECK:STDOUT: %Generic.type.loc15_52.1 => constants.%Generic.type.03dff7.2 // CHECK:STDOUT: %U.loc15_39.1 => constants.%U // CHECK:STDOUT: %pattern_type.loc15_39 => constants.%pattern_type.c49 // CHECK:STDOUT: %pattern_type.loc15_62 => constants.%pattern_type.51d // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @CallGenericMethod(constants.%GenericParam, constants.%Generic.facet) { // CHECK:STDOUT: %T.loc15_22.1 => constants.%GenericParam // CHECK:STDOUT: %Generic.type.loc15_52.1 => constants.%Generic.type.498 // CHECK:STDOUT: %U.loc15_39.1 => constants.%Generic.facet // CHECK:STDOUT: %pattern_type.loc15_39 => constants.%pattern_type.cba // CHECK:STDOUT: %pattern_type.loc15_62 => constants.%pattern_type.27a // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete => constants.%complete_type // CHECK:STDOUT: } // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/facet/convert_class_value_to_facet_value_value.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // TODO: Add ranges and switch to "--dump-sem-ir-ranges=only". // EXTRA-ARGS: --dump-sem-ir-ranges=if-present // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/convert.carbon // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/facet/convert_class_value_to_facet_value_value.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/facet/convert_class_value_to_facet_value_value.carbon interface Animal {} fn WalkAnimal[T:! Animal](unused a: T) {} class Goat {} impl Goat as Animal {} fn F() { WalkAnimal({} as Goat); } // CHECK:STDOUT: --- convert_class_value_to_facet_value_value.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %Animal.type: type = facet_type <@Animal> [concrete] // CHECK:STDOUT: %Self.c49: %Animal.type = symbolic_binding Self, 0 [symbolic] // CHECK:STDOUT: %type: type = facet_type [concrete] // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self] // CHECK:STDOUT: %pattern_type.e10: type = pattern_type %Animal.type [concrete] // CHECK:STDOUT: %T: %Animal.type = symbolic_binding T, 0 [symbolic] // CHECK:STDOUT: %T.binding.as_type: type = symbolic_binding_type T, 0, %T [symbolic] // CHECK:STDOUT: %pattern_type.892: type = pattern_type %T.binding.as_type [symbolic] // CHECK:STDOUT: %WalkAnimal.type: type = fn_type @WalkAnimal [concrete] // CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] // CHECK:STDOUT: %WalkAnimal: %WalkAnimal.type = struct_value () [concrete] // CHECK:STDOUT: %require_complete: = require_complete_type %T.binding.as_type [symbolic] // CHECK:STDOUT: %Goat: type = class_type @Goat [concrete] // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %complete_type: = complete_type_witness %empty_struct_type [concrete] // CHECK:STDOUT: %Animal.impl_witness: = impl_witness @Goat.as.Animal.impl.%Animal.impl_witness_table [concrete] // CHECK:STDOUT: %Animal.facet: %Animal.type = facet_value %Goat, (%Animal.impl_witness) [concrete] // CHECK:STDOUT: %F.type: type = fn_type @F [concrete] // CHECK:STDOUT: %F: %F.type = struct_value () [concrete] // CHECK:STDOUT: %empty_struct: %empty_struct_type = struct_value () [concrete] // CHECK:STDOUT: %Goat.val: %Goat = struct_value () [concrete] // CHECK:STDOUT: %pattern_type.234: type = pattern_type %Goat [concrete] // CHECK:STDOUT: %WalkAnimal.specific_fn: = specific_function %WalkAnimal, @WalkAnimal(%Animal.facet) [concrete] // CHECK:STDOUT: %Destroy.type: type = facet_type <@Destroy> [concrete] // CHECK:STDOUT: %Destroy.Op.type: type = fn_type @Destroy.Op [concrete] // CHECK:STDOUT: %Destroy.Op: %Destroy.Op.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: .Destroy = %Core.Destroy // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: %Core.Destroy: type = import_ref Core//prelude/parts/destroy, Destroy, loaded [concrete = constants.%Destroy.type] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .Animal = %Animal.decl // CHECK:STDOUT: .WalkAnimal = %WalkAnimal.decl // CHECK:STDOUT: .Goat = %Goat.decl // CHECK:STDOUT: .F = %F.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %Animal.decl: type = interface_decl @Animal [concrete = constants.%Animal.type] {} {} // CHECK:STDOUT: %WalkAnimal.decl: %WalkAnimal.type = fn_decl @WalkAnimal [concrete = constants.%WalkAnimal] { // CHECK:STDOUT: %T.patt: %pattern_type.e10 = symbolic_binding_pattern T, 0 [concrete] // CHECK:STDOUT: %a.patt: @WalkAnimal.%pattern_type (%pattern_type.892) = value_binding_pattern a [concrete] // CHECK:STDOUT: %a.param_patt: @WalkAnimal.%pattern_type (%pattern_type.892) = value_param_pattern %a.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %.loc17_19: type = splice_block %Animal.ref [concrete = constants.%Animal.type] { // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %Animal.ref: type = name_ref Animal, file.%Animal.decl [concrete = constants.%Animal.type] // CHECK:STDOUT: } // CHECK:STDOUT: %T.loc17_15.2: %Animal.type = symbolic_binding T, 0 [symbolic = %T.loc17_15.1 (constants.%T)] // CHECK:STDOUT: %a.param: @WalkAnimal.%T.binding.as_type (%T.binding.as_type) = value_param call_param0 // CHECK:STDOUT: %.loc17_37.1: type = splice_block %.loc17_37.2 [symbolic = %T.binding.as_type (constants.%T.binding.as_type)] { // CHECK:STDOUT: %T.ref: %Animal.type = name_ref T, %T.loc17_15.2 [symbolic = %T.loc17_15.1 (constants.%T)] // CHECK:STDOUT: %T.as_type: type = facet_access_type %T.ref [symbolic = %T.binding.as_type (constants.%T.binding.as_type)] // CHECK:STDOUT: %.loc17_37.2: type = converted %T.ref, %T.as_type [symbolic = %T.binding.as_type (constants.%T.binding.as_type)] // CHECK:STDOUT: } // CHECK:STDOUT: %a: @WalkAnimal.%T.binding.as_type (%T.binding.as_type) = value_binding a, %a.param // CHECK:STDOUT: } // CHECK:STDOUT: %Goat.decl: type = class_decl @Goat [concrete = constants.%Goat] {} {} // CHECK:STDOUT: impl_decl @Goat.as.Animal.impl [concrete] {} { // CHECK:STDOUT: %Goat.ref: type = name_ref Goat, file.%Goat.decl [concrete = constants.%Goat] // CHECK:STDOUT: %Animal.ref: type = name_ref Animal, file.%Animal.decl [concrete = constants.%Animal.type] // CHECK:STDOUT: } // CHECK:STDOUT: %F.decl: %F.type = fn_decl @F [concrete = constants.%F] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: interface @Animal { // CHECK:STDOUT: %Self: %Animal.type = symbolic_binding Self, 0 [symbolic = constants.%Self.c49] // CHECK:STDOUT: %Animal.WithSelf.decl = interface_with_self_decl @Animal [concrete] // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = %Self // CHECK:STDOUT: witness = () // CHECK:STDOUT: // CHECK:STDOUT: !requires: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: impl @Goat.as.Animal.impl: %Goat.ref as %Animal.ref { // CHECK:STDOUT: %Animal.impl_witness_table = impl_witness_table (), @Goat.as.Animal.impl [concrete] // CHECK:STDOUT: %Animal.impl_witness: = impl_witness %Animal.impl_witness_table [concrete = constants.%Animal.impl_witness] // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: witness = %Animal.impl_witness // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @Goat { // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%Goat // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic fn @WalkAnimal(%T.loc17_15.2: %Animal.type) { // CHECK:STDOUT: %T.loc17_15.1: %Animal.type = symbolic_binding T, 0 [symbolic = %T.loc17_15.1 (constants.%T)] // CHECK:STDOUT: %T.binding.as_type: type = symbolic_binding_type T, 0, %T.loc17_15.1 [symbolic = %T.binding.as_type (constants.%T.binding.as_type)] // CHECK:STDOUT: %pattern_type: type = pattern_type %T.binding.as_type [symbolic = %pattern_type (constants.%pattern_type.892)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete: = require_complete_type %T.binding.as_type [symbolic = %require_complete (constants.%require_complete)] // CHECK:STDOUT: // CHECK:STDOUT: fn(%a.param: @WalkAnimal.%T.binding.as_type (%T.binding.as_type)) { // CHECK:STDOUT: !entry: // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @F() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %WalkAnimal.ref: %WalkAnimal.type = name_ref WalkAnimal, file.%WalkAnimal.decl [concrete = constants.%WalkAnimal] // CHECK:STDOUT: %.loc23_15.1: %empty_struct_type = struct_literal () [concrete = constants.%empty_struct] // CHECK:STDOUT: %Goat.ref: type = name_ref Goat, file.%Goat.decl [concrete = constants.%Goat] // CHECK:STDOUT: %.loc23_15.2: ref %Goat = temporary_storage // CHECK:STDOUT: %.loc23_15.3: init %Goat to %.loc23_15.2 = class_init () [concrete = constants.%Goat.val] // CHECK:STDOUT: %.loc23_17.1: init %Goat = converted %.loc23_15.1, %.loc23_15.3 [concrete = constants.%Goat.val] // CHECK:STDOUT: %Animal.facet: %Animal.type = facet_value constants.%Goat, (constants.%Animal.impl_witness) [concrete = constants.%Animal.facet] // CHECK:STDOUT: %.loc23_24: %Animal.type = converted constants.%Goat, %Animal.facet [concrete = constants.%Animal.facet] // CHECK:STDOUT: %WalkAnimal.specific_fn: = specific_function %WalkAnimal.ref, @WalkAnimal(constants.%Animal.facet) [concrete = constants.%WalkAnimal.specific_fn] // CHECK:STDOUT: %.loc23_17.2: ref %Goat = temporary %.loc23_15.2, %.loc23_17.1 // CHECK:STDOUT: %.loc23_17.3: %Goat = acquire_value %.loc23_17.2 // CHECK:STDOUT: %WalkAnimal.call: init %empty_tuple.type = call %WalkAnimal.specific_fn(%.loc23_17.3) // CHECK:STDOUT: %Destroy.Op.bound: = bound_method %.loc23_17.2, constants.%Destroy.Op // CHECK:STDOUT: %Destroy.Op.call: init %empty_tuple.type = call %Destroy.Op.bound(%.loc23_17.2) // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @Destroy.Op(%self.param: ref %Goat) = "no_op"; // CHECK:STDOUT: // CHECK:STDOUT: specific @Animal.WithSelf(constants.%Self.c49) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @WalkAnimal(constants.%T) { // CHECK:STDOUT: %T.loc17_15.1 => constants.%T // CHECK:STDOUT: %T.binding.as_type => constants.%T.binding.as_type // CHECK:STDOUT: %pattern_type => constants.%pattern_type.892 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @Animal.WithSelf(constants.%Animal.facet) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @WalkAnimal(constants.%Animal.facet) { // CHECK:STDOUT: %T.loc17_15.1 => constants.%Animal.facet // CHECK:STDOUT: %T.binding.as_type => constants.%Goat // CHECK:STDOUT: %pattern_type => constants.%pattern_type.234 // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete => constants.%complete_type // CHECK:STDOUT: } // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/facet/convert_class_value_to_generic_facet_value_value.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // TODO: Add ranges and switch to "--dump-sem-ir-ranges=only". // EXTRA-ARGS: --dump-sem-ir-ranges=if-present // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/convert.carbon // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/facet/convert_class_value_to_generic_facet_value_value.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/facet/convert_class_value_to_generic_facet_value_value.carbon // --- convert_class_value_to_generic_facet_value_value.carbon library "[[@TEST_NAME]]"; interface Generic(Scalar:! type) { fn F(); } class GenericParam {} class ImplsGeneric {} impl ImplsGeneric as Generic(GenericParam) { fn F() {} } fn CallGenericMethod[T:! type, U:! Generic(T)](unused a: U, unused s: T) { U.F(); } fn G() { CallGenericMethod({} as ImplsGeneric, {} as GenericParam); } // --- multiple_generic_params_one_fixed_one_deduced.carbon library "[[@TEST_NAME]]"; interface I(V:! type, W:! type) {} class C {} impl forall [T:! type] C as I(T, ()) {} fn A[T:! I({}, ())](unused t: T) {} fn B() { A({} as C); } // --- fail_mismatch_impl_constraint_with_fixed_specific.carbon library "[[@TEST_NAME]]"; interface I(V:! type, W:! type) {} class C {} impl forall [T:! type] C as I(T, ()) {} fn A[T:! I({}, {})](unused t: T) {} fn B() { // CHECK:STDERR: fail_mismatch_impl_constraint_with_fixed_specific.carbon:[[@LINE+7]]:3: error: cannot convert type `C` into type implementing `I({}, {})` [ConversionFailureTypeToFacet] // CHECK:STDERR: A({} as C); // CHECK:STDERR: ^~~~~~~~~~ // CHECK:STDERR: fail_mismatch_impl_constraint_with_fixed_specific.carbon:[[@LINE-6]]:1: note: while deducing parameters of generic declared here [DeductionGenericHere] // CHECK:STDERR: fn A[T:! I({}, {})](unused t: T) {} // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: A({} as C); } // --- fail_mismatch_impl_self_with_fixed_specific.carbon library "[[@TEST_NAME]]"; interface I {} class C(V:! type, W:! type) {} impl forall [T:! type] C(T, ()) as I {} fn A[T:! I](unused t: T) {} fn B() { // CHECK:STDERR: fail_mismatch_impl_self_with_fixed_specific.carbon:[[@LINE+7]]:3: error: cannot convert type `C({}, {})` into type implementing `I` [ConversionFailureTypeToFacet] // CHECK:STDERR: A({} as C({}, {})); // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~ // CHECK:STDERR: fail_mismatch_impl_self_with_fixed_specific.carbon:[[@LINE-6]]:1: note: while deducing parameters of generic declared here [DeductionGenericHere] // CHECK:STDERR: fn A[T:! I](unused t: T) {} // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: A({} as C({}, {})); } // CHECK:STDOUT: --- convert_class_value_to_generic_facet_value_value.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %type: type = facet_type [concrete] // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self] // CHECK:STDOUT: %pattern_type.98f: type = pattern_type type [concrete] // CHECK:STDOUT: %Scalar: type = symbolic_binding Scalar, 0 [symbolic] // CHECK:STDOUT: %Generic.type.835: type = generic_interface_type @Generic [concrete] // CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] // CHECK:STDOUT: %Generic.generic: %Generic.type.835 = struct_value () [concrete] // CHECK:STDOUT: %Generic.type.03dff7.1: type = facet_type <@Generic, @Generic(%Scalar)> [symbolic] // CHECK:STDOUT: %Self.15d2d5.1: %Generic.type.03dff7.1 = symbolic_binding Self, 1 [symbolic] // CHECK:STDOUT: %Generic.WithSelf.F.type.74390a.1: type = fn_type @Generic.WithSelf.F, @Generic.WithSelf(%Scalar, %Self.15d2d5.1) [symbolic] // CHECK:STDOUT: %Generic.WithSelf.F.baf95a.1: %Generic.WithSelf.F.type.74390a.1 = struct_value () [symbolic] // CHECK:STDOUT: %Generic.assoc_type.22afda.1: type = assoc_entity_type @Generic, @Generic(%Scalar) [symbolic] // CHECK:STDOUT: %assoc0.e0dc00.1: %Generic.assoc_type.22afda.1 = assoc_entity element0, @Generic.WithSelf.%Generic.WithSelf.F.decl [symbolic] // CHECK:STDOUT: %GenericParam: type = class_type @GenericParam [concrete] // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %complete_type.357: = complete_type_witness %empty_struct_type [concrete] // CHECK:STDOUT: %ImplsGeneric: type = class_type @ImplsGeneric [concrete] // CHECK:STDOUT: %Generic.type.498: type = facet_type <@Generic, @Generic(%GenericParam)> [concrete] // CHECK:STDOUT: %Generic.impl_witness: = impl_witness @ImplsGeneric.as.Generic.impl.%Generic.impl_witness_table [concrete] // CHECK:STDOUT: %Self.1f5: %Generic.type.498 = symbolic_binding Self, 1 [symbolic] // CHECK:STDOUT: %Generic.WithSelf.F.type.a23: type = fn_type @Generic.WithSelf.F, @Generic.WithSelf(%GenericParam, %Self.15d2d5.1) [symbolic] // CHECK:STDOUT: %Generic.WithSelf.F.fe0: %Generic.WithSelf.F.type.a23 = struct_value () [symbolic] // CHECK:STDOUT: %Generic.assoc_type.6dc: type = assoc_entity_type @Generic, @Generic(%GenericParam) [concrete] // CHECK:STDOUT: %assoc0.717: %Generic.assoc_type.6dc = assoc_entity element0, @Generic.WithSelf.%Generic.WithSelf.F.decl [concrete] // CHECK:STDOUT: %ImplsGeneric.as.Generic.impl.F.type: type = fn_type @ImplsGeneric.as.Generic.impl.F [concrete] // CHECK:STDOUT: %ImplsGeneric.as.Generic.impl.F: %ImplsGeneric.as.Generic.impl.F.type = struct_value () [concrete] // CHECK:STDOUT: %Generic.facet: %Generic.type.498 = facet_value %ImplsGeneric, (%Generic.impl_witness) [concrete] // CHECK:STDOUT: %Generic.WithSelf.F.type.c86: type = fn_type @Generic.WithSelf.F, @Generic.WithSelf(%GenericParam, %Generic.facet) [concrete] // CHECK:STDOUT: %Generic.WithSelf.F.1fd: %Generic.WithSelf.F.type.c86 = struct_value () [concrete] // CHECK:STDOUT: %T: type = symbolic_binding T, 0 [symbolic] // CHECK:STDOUT: %Generic.type.03dff7.2: type = facet_type <@Generic, @Generic(%T)> [symbolic] // CHECK:STDOUT: %pattern_type.c49: type = pattern_type %Generic.type.03dff7.2 [symbolic] // CHECK:STDOUT: %U: %Generic.type.03dff7.2 = symbolic_binding U, 1 [symbolic] // CHECK:STDOUT: %Self.15d2d5.2: %Generic.type.03dff7.2 = symbolic_binding Self, 1 [symbolic] // CHECK:STDOUT: %Generic.WithSelf.F.type.74390a.2: type = fn_type @Generic.WithSelf.F, @Generic.WithSelf(%T, %Self.15d2d5.1) [symbolic] // CHECK:STDOUT: %Generic.WithSelf.F.baf95a.2: %Generic.WithSelf.F.type.74390a.2 = struct_value () [symbolic] // CHECK:STDOUT: %Generic.assoc_type.22afda.2: type = assoc_entity_type @Generic, @Generic(%T) [symbolic] // CHECK:STDOUT: %assoc0.e0dc00.2: %Generic.assoc_type.22afda.2 = assoc_entity element0, @Generic.WithSelf.%Generic.WithSelf.F.decl [symbolic] // CHECK:STDOUT: %U.binding.as_type: type = symbolic_binding_type U, 1, %U [symbolic] // CHECK:STDOUT: %pattern_type.46f: type = pattern_type %U.binding.as_type [symbolic] // CHECK:STDOUT: %pattern_type.51d: type = pattern_type %T [symbolic] // CHECK:STDOUT: %CallGenericMethod.type: type = fn_type @CallGenericMethod [concrete] // CHECK:STDOUT: %CallGenericMethod: %CallGenericMethod.type = struct_value () [concrete] // CHECK:STDOUT: %require_complete.186: = require_complete_type %U.binding.as_type [symbolic] // CHECK:STDOUT: %require_complete.944: = require_complete_type %T [symbolic] // CHECK:STDOUT: %require_complete.7da: = require_complete_type %Generic.type.03dff7.2 [symbolic] // CHECK:STDOUT: %Generic.WithSelf.F.type.56e: type = fn_type @Generic.WithSelf.F, @Generic.WithSelf(%T, %U) [symbolic] // CHECK:STDOUT: %Generic.WithSelf.F.a99: %Generic.WithSelf.F.type.56e = struct_value () [symbolic] // CHECK:STDOUT: %Generic.lookup_impl_witness: = lookup_impl_witness %U, @Generic, @Generic(%T) [symbolic] // CHECK:STDOUT: %.ab3: type = fn_type_with_self_type %Generic.WithSelf.F.type.56e, %U [symbolic] // CHECK:STDOUT: %impl.elem0: %.ab3 = impl_witness_access %Generic.lookup_impl_witness, element0 [symbolic] // CHECK:STDOUT: %specific_impl_fn: = specific_impl_function %impl.elem0, @Generic.WithSelf.F(%T, %U) [symbolic] // CHECK:STDOUT: %G.type: type = fn_type @G [concrete] // CHECK:STDOUT: %G: %G.type = struct_value () [concrete] // CHECK:STDOUT: %empty_struct: %empty_struct_type = struct_value () [concrete] // CHECK:STDOUT: %ImplsGeneric.val: %ImplsGeneric = struct_value () [concrete] // CHECK:STDOUT: %GenericParam.val: %GenericParam = struct_value () [concrete] // CHECK:STDOUT: %pattern_type.cba: type = pattern_type %Generic.type.498 [concrete] // CHECK:STDOUT: %pattern_type.4da: type = pattern_type %ImplsGeneric [concrete] // CHECK:STDOUT: %pattern_type.27a: type = pattern_type %GenericParam [concrete] // CHECK:STDOUT: %CallGenericMethod.specific_fn: = specific_function %CallGenericMethod, @CallGenericMethod(%GenericParam, %Generic.facet) [concrete] // CHECK:STDOUT: %Destroy.type: type = facet_type <@Destroy> [concrete] // CHECK:STDOUT: %Destroy.Op.type.bae255.1: type = fn_type @Destroy.Op.loc20_44 [concrete] // CHECK:STDOUT: %Destroy.Op.651ba6.1: %Destroy.Op.type.bae255.1 = struct_value () [concrete] // CHECK:STDOUT: %Destroy.Op.type.bae255.2: type = fn_type @Destroy.Op.loc20_24 [concrete] // CHECK:STDOUT: %Destroy.Op.651ba6.2: %Destroy.Op.type.bae255.2 = struct_value () [concrete] // CHECK:STDOUT: %complete_type.57a: = complete_type_witness %Generic.type.498 [concrete] // CHECK:STDOUT: %.fcf: type = fn_type_with_self_type %Generic.WithSelf.F.type.c86, %Generic.facet [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: .Destroy = %Core.Destroy // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: %Core.Destroy: type = import_ref Core//prelude/parts/destroy, Destroy, loaded [concrete = constants.%Destroy.type] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .Generic = %Generic.decl // CHECK:STDOUT: .GenericParam = %GenericParam.decl // CHECK:STDOUT: .ImplsGeneric = %ImplsGeneric.decl // CHECK:STDOUT: .CallGenericMethod = %CallGenericMethod.decl // CHECK:STDOUT: .G = %G.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %Generic.decl: %Generic.type.835 = interface_decl @Generic [concrete = constants.%Generic.generic] { // CHECK:STDOUT: %Scalar.patt: %pattern_type.98f = symbolic_binding_pattern Scalar, 0 [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %.loc4_28.1: type = splice_block %.loc4_28.2 [concrete = type] { // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %.loc4_28.2: type = type_literal type [concrete = type] // CHECK:STDOUT: } // CHECK:STDOUT: %Scalar.loc4_19.2: type = symbolic_binding Scalar, 0 [symbolic = %Scalar.loc4_19.1 (constants.%Scalar)] // CHECK:STDOUT: } // CHECK:STDOUT: %GenericParam.decl: type = class_decl @GenericParam [concrete = constants.%GenericParam] {} {} // CHECK:STDOUT: %ImplsGeneric.decl: type = class_decl @ImplsGeneric [concrete = constants.%ImplsGeneric] {} {} // CHECK:STDOUT: impl_decl @ImplsGeneric.as.Generic.impl [concrete] {} { // CHECK:STDOUT: %ImplsGeneric.ref: type = name_ref ImplsGeneric, file.%ImplsGeneric.decl [concrete = constants.%ImplsGeneric] // CHECK:STDOUT: %Generic.ref: %Generic.type.835 = name_ref Generic, file.%Generic.decl [concrete = constants.%Generic.generic] // CHECK:STDOUT: %GenericParam.ref: type = name_ref GenericParam, file.%GenericParam.decl [concrete = constants.%GenericParam] // CHECK:STDOUT: %Generic.type: type = facet_type <@Generic, @Generic(constants.%GenericParam)> [concrete = constants.%Generic.type.498] // CHECK:STDOUT: } // CHECK:STDOUT: %CallGenericMethod.decl: %CallGenericMethod.type = fn_decl @CallGenericMethod [concrete = constants.%CallGenericMethod] { // CHECK:STDOUT: %T.patt: %pattern_type.98f = symbolic_binding_pattern T, 0 [concrete] // CHECK:STDOUT: %U.patt: @CallGenericMethod.%pattern_type.loc15_32 (%pattern_type.c49) = symbolic_binding_pattern U, 1 [concrete] // CHECK:STDOUT: %a.patt: @CallGenericMethod.%pattern_type.loc15_55 (%pattern_type.46f) = value_binding_pattern a [concrete] // CHECK:STDOUT: %a.param_patt: @CallGenericMethod.%pattern_type.loc15_55 (%pattern_type.46f) = value_param_pattern %a.patt [concrete] // CHECK:STDOUT: %s.patt: @CallGenericMethod.%pattern_type.loc15_68 (%pattern_type.51d) = value_binding_pattern s [concrete] // CHECK:STDOUT: %s.param_patt: @CallGenericMethod.%pattern_type.loc15_68 (%pattern_type.51d) = value_param_pattern %s.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %.loc15_26.1: type = splice_block %.loc15_26.2 [concrete = type] { // CHECK:STDOUT: %.Self.2: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %.loc15_26.2: type = type_literal type [concrete = type] // CHECK:STDOUT: } // CHECK:STDOUT: %T.loc15_22.2: type = symbolic_binding T, 0 [symbolic = %T.loc15_22.1 (constants.%T)] // CHECK:STDOUT: %.loc15_45: type = splice_block %Generic.type.loc15_45.2 [symbolic = %Generic.type.loc15_45.1 (constants.%Generic.type.03dff7.2)] { // CHECK:STDOUT: %.Self.1: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %Generic.ref: %Generic.type.835 = name_ref Generic, file.%Generic.decl [concrete = constants.%Generic.generic] // CHECK:STDOUT: %T.ref.loc15_44: type = name_ref T, %T.loc15_22.2 [symbolic = %T.loc15_22.1 (constants.%T)] // CHECK:STDOUT: %Generic.type.loc15_45.2: type = facet_type <@Generic, @Generic(constants.%T)> [symbolic = %Generic.type.loc15_45.1 (constants.%Generic.type.03dff7.2)] // CHECK:STDOUT: } // CHECK:STDOUT: %U.loc15_32.2: @CallGenericMethod.%Generic.type.loc15_45.1 (%Generic.type.03dff7.2) = symbolic_binding U, 1 [symbolic = %U.loc15_32.1 (constants.%U)] // CHECK:STDOUT: %a.param: @CallGenericMethod.%U.binding.as_type (%U.binding.as_type) = value_param call_param0 // CHECK:STDOUT: %.loc15_58.1: type = splice_block %.loc15_58.2 [symbolic = %U.binding.as_type (constants.%U.binding.as_type)] { // CHECK:STDOUT: %U.ref.loc15: @CallGenericMethod.%Generic.type.loc15_45.1 (%Generic.type.03dff7.2) = name_ref U, %U.loc15_32.2 [symbolic = %U.loc15_32.1 (constants.%U)] // CHECK:STDOUT: %U.as_type.loc15: type = facet_access_type %U.ref.loc15 [symbolic = %U.binding.as_type (constants.%U.binding.as_type)] // CHECK:STDOUT: %.loc15_58.2: type = converted %U.ref.loc15, %U.as_type.loc15 [symbolic = %U.binding.as_type (constants.%U.binding.as_type)] // CHECK:STDOUT: } // CHECK:STDOUT: %a: @CallGenericMethod.%U.binding.as_type (%U.binding.as_type) = value_binding a, %a.param // CHECK:STDOUT: %s.param: @CallGenericMethod.%T.loc15_22.1 (%T) = value_param call_param1 // CHECK:STDOUT: %T.ref.loc15_71: type = name_ref T, %T.loc15_22.2 [symbolic = %T.loc15_22.1 (constants.%T)] // CHECK:STDOUT: %s: @CallGenericMethod.%T.loc15_22.1 (%T) = value_binding s, %s.param // CHECK:STDOUT: } // CHECK:STDOUT: %G.decl: %G.type = fn_decl @G [concrete = constants.%G] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic interface @Generic(%Scalar.loc4_19.2: type) { // CHECK:STDOUT: %Scalar.loc4_19.1: type = symbolic_binding Scalar, 0 [symbolic = %Scalar.loc4_19.1 (constants.%Scalar)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %Generic.type: type = facet_type <@Generic, @Generic(%Scalar.loc4_19.1)> [symbolic = %Generic.type (constants.%Generic.type.03dff7.1)] // CHECK:STDOUT: %Self.loc4_34.2: @Generic.%Generic.type (%Generic.type.03dff7.1) = symbolic_binding Self, 1 [symbolic = %Self.loc4_34.2 (constants.%Self.15d2d5.1)] // CHECK:STDOUT: // CHECK:STDOUT: interface { // CHECK:STDOUT: %Self.loc4_34.1: @Generic.%Generic.type (%Generic.type.03dff7.1) = symbolic_binding Self, 1 [symbolic = %Self.loc4_34.2 (constants.%Self.15d2d5.1)] // CHECK:STDOUT: %Generic.WithSelf.decl = interface_with_self_decl @Generic [concrete] // CHECK:STDOUT: // CHECK:STDOUT: !with Self: // CHECK:STDOUT: %Generic.WithSelf.F.decl: @Generic.WithSelf.%Generic.WithSelf.F.type (%Generic.WithSelf.F.type.74390a.1) = fn_decl @Generic.WithSelf.F [symbolic = @Generic.WithSelf.%Generic.WithSelf.F (constants.%Generic.WithSelf.F.baf95a.1)] {} {} // CHECK:STDOUT: %assoc0.loc5_9.1: @Generic.WithSelf.%Generic.assoc_type (%Generic.assoc_type.22afda.1) = assoc_entity element0, %Generic.WithSelf.F.decl [symbolic = %assoc0.loc5_9.2 (constants.%assoc0.e0dc00.1)] // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = %Self.loc4_34.1 // CHECK:STDOUT: .F = @Generic.WithSelf.%assoc0.loc5_9.1 // CHECK:STDOUT: witness = (@Generic.WithSelf.%Generic.WithSelf.F.decl) // CHECK:STDOUT: // CHECK:STDOUT: !requires: // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: impl @ImplsGeneric.as.Generic.impl: %ImplsGeneric.ref as %Generic.type { // CHECK:STDOUT: %ImplsGeneric.as.Generic.impl.F.decl: %ImplsGeneric.as.Generic.impl.F.type = fn_decl @ImplsGeneric.as.Generic.impl.F [concrete = constants.%ImplsGeneric.as.Generic.impl.F] {} {} // CHECK:STDOUT: %Generic.impl_witness_table = impl_witness_table (%ImplsGeneric.as.Generic.impl.F.decl), @ImplsGeneric.as.Generic.impl [concrete] // CHECK:STDOUT: %Generic.impl_witness: = impl_witness %Generic.impl_witness_table [concrete = constants.%Generic.impl_witness] // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .F = %ImplsGeneric.as.Generic.impl.F.decl // CHECK:STDOUT: witness = %Generic.impl_witness // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @GenericParam { // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type.357] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%GenericParam // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @ImplsGeneric { // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type.357] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%ImplsGeneric // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic fn @Generic.WithSelf.F(@Generic.%Scalar.loc4_19.2: type, @Generic.%Self.loc4_34.1: @Generic.%Generic.type (%Generic.type.03dff7.1)) { // CHECK:STDOUT: fn(); // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @ImplsGeneric.as.Generic.impl.F() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic fn @CallGenericMethod(%T.loc15_22.2: type, %U.loc15_32.2: @CallGenericMethod.%Generic.type.loc15_45.1 (%Generic.type.03dff7.2)) { // CHECK:STDOUT: %T.loc15_22.1: type = symbolic_binding T, 0 [symbolic = %T.loc15_22.1 (constants.%T)] // CHECK:STDOUT: %Generic.type.loc15_45.1: type = facet_type <@Generic, @Generic(%T.loc15_22.1)> [symbolic = %Generic.type.loc15_45.1 (constants.%Generic.type.03dff7.2)] // CHECK:STDOUT: %U.loc15_32.1: @CallGenericMethod.%Generic.type.loc15_45.1 (%Generic.type.03dff7.2) = symbolic_binding U, 1 [symbolic = %U.loc15_32.1 (constants.%U)] // CHECK:STDOUT: %pattern_type.loc15_32: type = pattern_type %Generic.type.loc15_45.1 [symbolic = %pattern_type.loc15_32 (constants.%pattern_type.c49)] // CHECK:STDOUT: %U.binding.as_type: type = symbolic_binding_type U, 1, %U.loc15_32.1 [symbolic = %U.binding.as_type (constants.%U.binding.as_type)] // CHECK:STDOUT: %pattern_type.loc15_55: type = pattern_type %U.binding.as_type [symbolic = %pattern_type.loc15_55 (constants.%pattern_type.46f)] // CHECK:STDOUT: %pattern_type.loc15_68: type = pattern_type %T.loc15_22.1 [symbolic = %pattern_type.loc15_68 (constants.%pattern_type.51d)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete.loc15_56: = require_complete_type %U.binding.as_type [symbolic = %require_complete.loc15_56 (constants.%require_complete.186)] // CHECK:STDOUT: %require_complete.loc15_69: = require_complete_type %T.loc15_22.1 [symbolic = %require_complete.loc15_69 (constants.%require_complete.944)] // CHECK:STDOUT: %require_complete.loc16: = require_complete_type %Generic.type.loc15_45.1 [symbolic = %require_complete.loc16 (constants.%require_complete.7da)] // CHECK:STDOUT: %Generic.assoc_type: type = assoc_entity_type @Generic, @Generic(%T.loc15_22.1) [symbolic = %Generic.assoc_type (constants.%Generic.assoc_type.22afda.2)] // CHECK:STDOUT: %assoc0: @CallGenericMethod.%Generic.assoc_type (%Generic.assoc_type.22afda.2) = assoc_entity element0, @Generic.WithSelf.%Generic.WithSelf.F.decl [symbolic = %assoc0 (constants.%assoc0.e0dc00.2)] // CHECK:STDOUT: %Generic.lookup_impl_witness: = lookup_impl_witness %U.loc15_32.1, @Generic, @Generic(%T.loc15_22.1) [symbolic = %Generic.lookup_impl_witness (constants.%Generic.lookup_impl_witness)] // CHECK:STDOUT: %Generic.WithSelf.F.type: type = fn_type @Generic.WithSelf.F, @Generic.WithSelf(%T.loc15_22.1, %U.loc15_32.1) [symbolic = %Generic.WithSelf.F.type (constants.%Generic.WithSelf.F.type.56e)] // CHECK:STDOUT: %.loc16_4.3: type = fn_type_with_self_type %Generic.WithSelf.F.type, %U.loc15_32.1 [symbolic = %.loc16_4.3 (constants.%.ab3)] // CHECK:STDOUT: %impl.elem0.loc16_4.2: @CallGenericMethod.%.loc16_4.3 (%.ab3) = impl_witness_access %Generic.lookup_impl_witness, element0 [symbolic = %impl.elem0.loc16_4.2 (constants.%impl.elem0)] // CHECK:STDOUT: %specific_impl_fn.loc16_4.2: = specific_impl_function %impl.elem0.loc16_4.2, @Generic.WithSelf.F(%T.loc15_22.1, %U.loc15_32.1) [symbolic = %specific_impl_fn.loc16_4.2 (constants.%specific_impl_fn)] // CHECK:STDOUT: // CHECK:STDOUT: fn(%a.param: @CallGenericMethod.%U.binding.as_type (%U.binding.as_type), %s.param: @CallGenericMethod.%T.loc15_22.1 (%T)) { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %U.ref.loc16: @CallGenericMethod.%Generic.type.loc15_45.1 (%Generic.type.03dff7.2) = name_ref U, %U.loc15_32.2 [symbolic = %U.loc15_32.1 (constants.%U)] // CHECK:STDOUT: %U.as_type.loc16: type = facet_access_type %U.ref.loc16 [symbolic = %U.binding.as_type (constants.%U.binding.as_type)] // CHECK:STDOUT: %.loc16_4.1: type = converted %U.ref.loc16, %U.as_type.loc16 [symbolic = %U.binding.as_type (constants.%U.binding.as_type)] // CHECK:STDOUT: %.loc16_4.2: @CallGenericMethod.%Generic.assoc_type (%Generic.assoc_type.22afda.2) = specific_constant @Generic.WithSelf.%assoc0.loc5_9.1, @Generic.WithSelf(constants.%T, constants.%U) [symbolic = %assoc0 (constants.%assoc0.e0dc00.2)] // CHECK:STDOUT: %F.ref: @CallGenericMethod.%Generic.assoc_type (%Generic.assoc_type.22afda.2) = name_ref F, %.loc16_4.2 [symbolic = %assoc0 (constants.%assoc0.e0dc00.2)] // CHECK:STDOUT: %impl.elem0.loc16_4.1: @CallGenericMethod.%.loc16_4.3 (%.ab3) = impl_witness_access constants.%Generic.lookup_impl_witness, element0 [symbolic = %impl.elem0.loc16_4.2 (constants.%impl.elem0)] // CHECK:STDOUT: %specific_impl_fn.loc16_4.1: = specific_impl_function %impl.elem0.loc16_4.1, @Generic.WithSelf.F(constants.%T, constants.%U) [symbolic = %specific_impl_fn.loc16_4.2 (constants.%specific_impl_fn)] // CHECK:STDOUT: %Generic.WithSelf.F.call: init %empty_tuple.type = call %specific_impl_fn.loc16_4.1() // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @G() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %CallGenericMethod.ref: %CallGenericMethod.type = name_ref CallGenericMethod, file.%CallGenericMethod.decl [concrete = constants.%CallGenericMethod] // CHECK:STDOUT: %.loc20_22.1: %empty_struct_type = struct_literal () [concrete = constants.%empty_struct] // CHECK:STDOUT: %ImplsGeneric.ref: type = name_ref ImplsGeneric, file.%ImplsGeneric.decl [concrete = constants.%ImplsGeneric] // CHECK:STDOUT: %.loc20_22.2: ref %ImplsGeneric = temporary_storage // CHECK:STDOUT: %.loc20_22.3: init %ImplsGeneric to %.loc20_22.2 = class_init () [concrete = constants.%ImplsGeneric.val] // CHECK:STDOUT: %.loc20_24.1: init %ImplsGeneric = converted %.loc20_22.1, %.loc20_22.3 [concrete = constants.%ImplsGeneric.val] // CHECK:STDOUT: %.loc20_42.1: %empty_struct_type = struct_literal () [concrete = constants.%empty_struct] // CHECK:STDOUT: %GenericParam.ref: type = name_ref GenericParam, file.%GenericParam.decl [concrete = constants.%GenericParam] // CHECK:STDOUT: %.loc20_42.2: ref %GenericParam = temporary_storage // CHECK:STDOUT: %.loc20_42.3: init %GenericParam to %.loc20_42.2 = class_init () [concrete = constants.%GenericParam.val] // CHECK:STDOUT: %.loc20_44.1: init %GenericParam = converted %.loc20_42.1, %.loc20_42.3 [concrete = constants.%GenericParam.val] // CHECK:STDOUT: %Generic.facet: %Generic.type.498 = facet_value constants.%ImplsGeneric, (constants.%Generic.impl_witness) [concrete = constants.%Generic.facet] // CHECK:STDOUT: %.loc20_59: %Generic.type.498 = converted constants.%ImplsGeneric, %Generic.facet [concrete = constants.%Generic.facet] // CHECK:STDOUT: %CallGenericMethod.specific_fn: = specific_function %CallGenericMethod.ref, @CallGenericMethod(constants.%GenericParam, constants.%Generic.facet) [concrete = constants.%CallGenericMethod.specific_fn] // CHECK:STDOUT: %.loc20_24.2: ref %ImplsGeneric = temporary %.loc20_22.2, %.loc20_24.1 // CHECK:STDOUT: %.loc20_24.3: %ImplsGeneric = acquire_value %.loc20_24.2 // CHECK:STDOUT: %.loc20_44.2: ref %GenericParam = temporary %.loc20_42.2, %.loc20_44.1 // CHECK:STDOUT: %.loc20_44.3: %GenericParam = acquire_value %.loc20_44.2 // CHECK:STDOUT: %CallGenericMethod.call: init %empty_tuple.type = call %CallGenericMethod.specific_fn(%.loc20_24.3, %.loc20_44.3) // CHECK:STDOUT: %Destroy.Op.bound.loc20_44: = bound_method %.loc20_44.2, constants.%Destroy.Op.651ba6.1 // CHECK:STDOUT: %Destroy.Op.call.loc20_44: init %empty_tuple.type = call %Destroy.Op.bound.loc20_44(%.loc20_44.2) // CHECK:STDOUT: %Destroy.Op.bound.loc20_24: = bound_method %.loc20_24.2, constants.%Destroy.Op.651ba6.2 // CHECK:STDOUT: %Destroy.Op.call.loc20_24: init %empty_tuple.type = call %Destroy.Op.bound.loc20_24(%.loc20_24.2) // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @Destroy.Op.loc20_44(%self.param: ref %GenericParam) = "no_op"; // CHECK:STDOUT: // CHECK:STDOUT: fn @Destroy.Op.loc20_24(%self.param: ref %ImplsGeneric) = "no_op"; // CHECK:STDOUT: // CHECK:STDOUT: specific @Generic(constants.%Scalar) { // CHECK:STDOUT: %Scalar.loc4_19.1 => constants.%Scalar // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @Generic.WithSelf(constants.%Scalar, constants.%Self.15d2d5.1) {} // CHECK:STDOUT: // CHECK:STDOUT: specific @Generic.WithSelf.F(constants.%Scalar, constants.%Self.15d2d5.1) {} // CHECK:STDOUT: // CHECK:STDOUT: specific @Generic(constants.%GenericParam) { // CHECK:STDOUT: %Scalar.loc4_19.1 => constants.%GenericParam // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %Generic.type => constants.%Generic.type.498 // CHECK:STDOUT: %Self.loc4_34.2 => constants.%Self.1f5 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @Generic.WithSelf(constants.%GenericParam, constants.%Self.15d2d5.1) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: %Scalar => constants.%GenericParam // CHECK:STDOUT: %Generic.type => constants.%Generic.type.498 // CHECK:STDOUT: %Self => constants.%Self.15d2d5.1 // CHECK:STDOUT: %Generic.WithSelf.F.type => constants.%Generic.WithSelf.F.type.a23 // CHECK:STDOUT: %Generic.WithSelf.F => constants.%Generic.WithSelf.F.fe0 // CHECK:STDOUT: %Generic.assoc_type => constants.%Generic.assoc_type.6dc // CHECK:STDOUT: %assoc0.loc5_9.2 => constants.%assoc0.717 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @Generic.WithSelf(constants.%GenericParam, constants.%Generic.facet) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: %Scalar => constants.%GenericParam // CHECK:STDOUT: %Generic.type => constants.%Generic.type.498 // CHECK:STDOUT: %Self => constants.%Generic.facet // CHECK:STDOUT: %Generic.WithSelf.F.type => constants.%Generic.WithSelf.F.type.c86 // CHECK:STDOUT: %Generic.WithSelf.F => constants.%Generic.WithSelf.F.1fd // CHECK:STDOUT: %Generic.assoc_type => constants.%Generic.assoc_type.6dc // CHECK:STDOUT: %assoc0.loc5_9.2 => constants.%assoc0.717 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @Generic.WithSelf.F(constants.%GenericParam, constants.%Generic.facet) {} // CHECK:STDOUT: // CHECK:STDOUT: specific @Generic(constants.%T) { // CHECK:STDOUT: %Scalar.loc4_19.1 => constants.%T // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %Generic.type => constants.%Generic.type.03dff7.2 // CHECK:STDOUT: %Self.loc4_34.2 => constants.%Self.15d2d5.2 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @Generic.WithSelf(constants.%T, constants.%Self.15d2d5.1) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: %Scalar => constants.%T // CHECK:STDOUT: %Generic.type => constants.%Generic.type.03dff7.2 // CHECK:STDOUT: %Self => constants.%Self.15d2d5.1 // CHECK:STDOUT: %Generic.WithSelf.F.type => constants.%Generic.WithSelf.F.type.74390a.2 // CHECK:STDOUT: %Generic.WithSelf.F => constants.%Generic.WithSelf.F.baf95a.2 // CHECK:STDOUT: %Generic.assoc_type => constants.%Generic.assoc_type.22afda.2 // CHECK:STDOUT: %assoc0.loc5_9.2 => constants.%assoc0.e0dc00.2 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @CallGenericMethod(constants.%T, constants.%U) { // CHECK:STDOUT: %T.loc15_22.1 => constants.%T // CHECK:STDOUT: %Generic.type.loc15_45.1 => constants.%Generic.type.03dff7.2 // CHECK:STDOUT: %U.loc15_32.1 => constants.%U // CHECK:STDOUT: %pattern_type.loc15_32 => constants.%pattern_type.c49 // CHECK:STDOUT: %U.binding.as_type => constants.%U.binding.as_type // CHECK:STDOUT: %pattern_type.loc15_55 => constants.%pattern_type.46f // CHECK:STDOUT: %pattern_type.loc15_68 => constants.%pattern_type.51d // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @Generic.WithSelf(constants.%T, constants.%U) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: %Scalar => constants.%T // CHECK:STDOUT: %Generic.type => constants.%Generic.type.03dff7.2 // CHECK:STDOUT: %Self => constants.%U // CHECK:STDOUT: %Generic.WithSelf.F.type => constants.%Generic.WithSelf.F.type.56e // CHECK:STDOUT: %Generic.WithSelf.F => constants.%Generic.WithSelf.F.a99 // CHECK:STDOUT: %Generic.assoc_type => constants.%Generic.assoc_type.22afda.2 // CHECK:STDOUT: %assoc0.loc5_9.2 => constants.%assoc0.e0dc00.2 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @Generic.WithSelf.F(constants.%T, constants.%U) {} // CHECK:STDOUT: // CHECK:STDOUT: specific @CallGenericMethod(constants.%GenericParam, constants.%Generic.facet) { // CHECK:STDOUT: %T.loc15_22.1 => constants.%GenericParam // CHECK:STDOUT: %Generic.type.loc15_45.1 => constants.%Generic.type.498 // CHECK:STDOUT: %U.loc15_32.1 => constants.%Generic.facet // CHECK:STDOUT: %pattern_type.loc15_32 => constants.%pattern_type.cba // CHECK:STDOUT: %U.binding.as_type => constants.%ImplsGeneric // CHECK:STDOUT: %pattern_type.loc15_55 => constants.%pattern_type.4da // CHECK:STDOUT: %pattern_type.loc15_68 => constants.%pattern_type.27a // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete.loc15_56 => constants.%complete_type.357 // CHECK:STDOUT: %require_complete.loc15_69 => constants.%complete_type.357 // CHECK:STDOUT: %require_complete.loc16 => constants.%complete_type.57a // CHECK:STDOUT: %Generic.assoc_type => constants.%Generic.assoc_type.6dc // CHECK:STDOUT: %assoc0 => constants.%assoc0.717 // CHECK:STDOUT: %Generic.lookup_impl_witness => constants.%Generic.impl_witness // CHECK:STDOUT: %Generic.WithSelf.F.type => constants.%Generic.WithSelf.F.type.c86 // CHECK:STDOUT: %.loc16_4.3 => constants.%.fcf // CHECK:STDOUT: %impl.elem0.loc16_4.2 => constants.%ImplsGeneric.as.Generic.impl.F // CHECK:STDOUT: %specific_impl_fn.loc16_4.2 => constants.%ImplsGeneric.as.Generic.impl.F // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- multiple_generic_params_one_fixed_one_deduced.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %type: type = facet_type [concrete] // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self] // CHECK:STDOUT: %pattern_type.98f: type = pattern_type type [concrete] // CHECK:STDOUT: %V: type = symbolic_binding V, 0 [symbolic] // CHECK:STDOUT: %W: type = symbolic_binding W, 1 [symbolic] // CHECK:STDOUT: %I.type.609: type = generic_interface_type @I [concrete] // CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] // CHECK:STDOUT: %I.generic: %I.type.609 = struct_value () [concrete] // CHECK:STDOUT: %I.type.54f: type = facet_type <@I, @I(%V, %W)> [symbolic] // CHECK:STDOUT: %Self.8fa: %I.type.54f = symbolic_binding Self, 2 [symbolic] // CHECK:STDOUT: %C: type = class_type @C [concrete] // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %complete_type.357: = complete_type_witness %empty_struct_type [concrete] // CHECK:STDOUT: %T.67d: type = symbolic_binding T, 0 [symbolic] // CHECK:STDOUT: %empty_tuple: %empty_tuple.type = tuple_value () [concrete] // CHECK:STDOUT: %I.type.eb9: type = facet_type <@I, @I(%T.67d, %empty_tuple.type)> [symbolic] // CHECK:STDOUT: %I.impl_witness.1cb: = impl_witness @C.as.I.impl.%I.impl_witness_table, @C.as.I.impl(%T.67d) [symbolic] // CHECK:STDOUT: %Self.061: %I.type.eb9 = symbolic_binding Self, 2 [symbolic] // CHECK:STDOUT: %require_complete.d0a: = require_complete_type %I.type.eb9 [symbolic] // CHECK:STDOUT: %I.facet.a6e: %I.type.eb9 = facet_value %C, (%I.impl_witness.1cb) [symbolic] // CHECK:STDOUT: %empty_struct: %empty_struct_type = struct_value () [concrete] // CHECK:STDOUT: %I.type.5f7: type = facet_type <@I, @I(%empty_struct_type, %empty_tuple.type)> [concrete] // CHECK:STDOUT: %pattern_type.eee: type = pattern_type %I.type.5f7 [concrete] // CHECK:STDOUT: %T.bc3: %I.type.5f7 = symbolic_binding T, 0 [symbolic] // CHECK:STDOUT: %Self.2a6: %I.type.5f7 = symbolic_binding Self, 2 [symbolic] // CHECK:STDOUT: %T.binding.as_type: type = symbolic_binding_type T, 0, %T.bc3 [symbolic] // CHECK:STDOUT: %pattern_type.826: type = pattern_type %T.binding.as_type [symbolic] // CHECK:STDOUT: %A.type: type = fn_type @A [concrete] // CHECK:STDOUT: %A: %A.type = struct_value () [concrete] // CHECK:STDOUT: %require_complete.d69: = require_complete_type %T.binding.as_type [symbolic] // CHECK:STDOUT: %B.type: type = fn_type @B [concrete] // CHECK:STDOUT: %B: %B.type = struct_value () [concrete] // CHECK:STDOUT: %C.val: %C = struct_value () [concrete] // CHECK:STDOUT: %I.impl_witness.1c4: = impl_witness @C.as.I.impl.%I.impl_witness_table, @C.as.I.impl(%empty_struct_type) [concrete] // CHECK:STDOUT: %complete_type.1c0: = complete_type_witness %I.type.5f7 [concrete] // CHECK:STDOUT: %I.facet.0e1: %I.type.5f7 = facet_value %C, (%I.impl_witness.1c4) [concrete] // CHECK:STDOUT: %pattern_type.7c7: type = pattern_type %C [concrete] // CHECK:STDOUT: %A.specific_fn: = specific_function %A, @A(%I.facet.0e1) [concrete] // CHECK:STDOUT: %Destroy.type: type = facet_type <@Destroy> [concrete] // CHECK:STDOUT: %Destroy.Op.type: type = fn_type @Destroy.Op [concrete] // CHECK:STDOUT: %Destroy.Op: %Destroy.Op.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: .Destroy = %Core.Destroy // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: %Core.Destroy: type = import_ref Core//prelude/parts/destroy, Destroy, loaded [concrete = constants.%Destroy.type] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .I = %I.decl // CHECK:STDOUT: .C = %C.decl // CHECK:STDOUT: .A = %A.decl // CHECK:STDOUT: .B = %B.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %I.decl: %I.type.609 = interface_decl @I [concrete = constants.%I.generic] { // CHECK:STDOUT: %V.patt: %pattern_type.98f = symbolic_binding_pattern V, 0 [concrete] // CHECK:STDOUT: %W.patt: %pattern_type.98f = symbolic_binding_pattern W, 1 [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %.loc3_17.1: type = splice_block %.loc3_17.2 [concrete = type] { // CHECK:STDOUT: %.Self.2: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %.loc3_17.2: type = type_literal type [concrete = type] // CHECK:STDOUT: } // CHECK:STDOUT: %V.loc3_13.2: type = symbolic_binding V, 0 [symbolic = %V.loc3_13.1 (constants.%V)] // CHECK:STDOUT: %.loc3_27.1: type = splice_block %.loc3_27.2 [concrete = type] { // CHECK:STDOUT: %.Self.1: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %.loc3_27.2: type = type_literal type [concrete = type] // CHECK:STDOUT: } // CHECK:STDOUT: %W.loc3_23.2: type = symbolic_binding W, 1 [symbolic = %W.loc3_23.1 (constants.%W)] // CHECK:STDOUT: } // CHECK:STDOUT: %C.decl: type = class_decl @C [concrete = constants.%C] {} {} // CHECK:STDOUT: impl_decl @C.as.I.impl [concrete] { // CHECK:STDOUT: %T.patt: %pattern_type.98f = symbolic_binding_pattern T, 0 [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %C.ref: type = name_ref C, file.%C.decl [concrete = constants.%C] // CHECK:STDOUT: %I.ref: %I.type.609 = name_ref I, file.%I.decl [concrete = constants.%I.generic] // CHECK:STDOUT: %T.ref: type = name_ref T, %T.loc7_14.1 [symbolic = %T.loc7_14.2 (constants.%T.67d)] // CHECK:STDOUT: %.loc7_35: %empty_tuple.type = tuple_literal () [concrete = constants.%empty_tuple] // CHECK:STDOUT: %.loc7_36: type = converted %.loc7_35, constants.%empty_tuple.type [concrete = constants.%empty_tuple.type] // CHECK:STDOUT: %I.type.loc7_36.1: type = facet_type <@I, @I(constants.%T.67d, constants.%empty_tuple.type)> [symbolic = %I.type.loc7_36.2 (constants.%I.type.eb9)] // CHECK:STDOUT: %.loc7_18.1: type = splice_block %.loc7_18.2 [concrete = type] { // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %.loc7_18.2: type = type_literal type [concrete = type] // CHECK:STDOUT: } // CHECK:STDOUT: %T.loc7_14.1: type = symbolic_binding T, 0 [symbolic = %T.loc7_14.2 (constants.%T.67d)] // CHECK:STDOUT: } // CHECK:STDOUT: %A.decl: %A.type = fn_decl @A [concrete = constants.%A] { // CHECK:STDOUT: %T.patt: %pattern_type.eee = symbolic_binding_pattern T, 0 [concrete] // CHECK:STDOUT: %t.patt: @A.%pattern_type (%pattern_type.826) = value_binding_pattern t [concrete] // CHECK:STDOUT: %t.param_patt: @A.%pattern_type (%pattern_type.826) = value_param_pattern %t.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %.loc9_18.1: type = splice_block %I.type [concrete = constants.%I.type.5f7] { // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %I.ref: %I.type.609 = name_ref I, file.%I.decl [concrete = constants.%I.generic] // CHECK:STDOUT: %.loc9_13: %empty_struct_type = struct_literal () [concrete = constants.%empty_struct] // CHECK:STDOUT: %.loc9_17: %empty_tuple.type = tuple_literal () [concrete = constants.%empty_tuple] // CHECK:STDOUT: %.loc9_18.2: type = converted %.loc9_13, constants.%empty_struct_type [concrete = constants.%empty_struct_type] // CHECK:STDOUT: %.loc9_18.3: type = converted %.loc9_17, constants.%empty_tuple.type [concrete = constants.%empty_tuple.type] // CHECK:STDOUT: %I.type: type = facet_type <@I, @I(constants.%empty_struct_type, constants.%empty_tuple.type)> [concrete = constants.%I.type.5f7] // CHECK:STDOUT: } // CHECK:STDOUT: %T.loc9_6.2: %I.type.5f7 = symbolic_binding T, 0 [symbolic = %T.loc9_6.1 (constants.%T.bc3)] // CHECK:STDOUT: %t.param: @A.%T.binding.as_type (%T.binding.as_type) = value_param call_param0 // CHECK:STDOUT: %.loc9_31.1: type = splice_block %.loc9_31.2 [symbolic = %T.binding.as_type (constants.%T.binding.as_type)] { // CHECK:STDOUT: %T.ref: %I.type.5f7 = name_ref T, %T.loc9_6.2 [symbolic = %T.loc9_6.1 (constants.%T.bc3)] // CHECK:STDOUT: %T.as_type: type = facet_access_type %T.ref [symbolic = %T.binding.as_type (constants.%T.binding.as_type)] // CHECK:STDOUT: %.loc9_31.2: type = converted %T.ref, %T.as_type [symbolic = %T.binding.as_type (constants.%T.binding.as_type)] // CHECK:STDOUT: } // CHECK:STDOUT: %t: @A.%T.binding.as_type (%T.binding.as_type) = value_binding t, %t.param // CHECK:STDOUT: } // CHECK:STDOUT: %B.decl: %B.type = fn_decl @B [concrete = constants.%B] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic interface @I(%V.loc3_13.2: type, %W.loc3_23.2: type) { // CHECK:STDOUT: %V.loc3_13.1: type = symbolic_binding V, 0 [symbolic = %V.loc3_13.1 (constants.%V)] // CHECK:STDOUT: %W.loc3_23.1: type = symbolic_binding W, 1 [symbolic = %W.loc3_23.1 (constants.%W)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %I.type: type = facet_type <@I, @I(%V.loc3_13.1, %W.loc3_23.1)> [symbolic = %I.type (constants.%I.type.54f)] // CHECK:STDOUT: %Self.loc3_33.2: @I.%I.type (%I.type.54f) = symbolic_binding Self, 2 [symbolic = %Self.loc3_33.2 (constants.%Self.8fa)] // CHECK:STDOUT: // CHECK:STDOUT: interface { // CHECK:STDOUT: %Self.loc3_33.1: @I.%I.type (%I.type.54f) = symbolic_binding Self, 2 [symbolic = %Self.loc3_33.2 (constants.%Self.8fa)] // CHECK:STDOUT: %I.WithSelf.decl = interface_with_self_decl @I [concrete] // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = %Self.loc3_33.1 // CHECK:STDOUT: witness = () // CHECK:STDOUT: // CHECK:STDOUT: !requires: // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic impl @C.as.I.impl(%T.loc7_14.1: type) { // CHECK:STDOUT: %T.loc7_14.2: type = symbolic_binding T, 0 [symbolic = %T.loc7_14.2 (constants.%T.67d)] // CHECK:STDOUT: %I.type.loc7_36.2: type = facet_type <@I, @I(%T.loc7_14.2, constants.%empty_tuple.type)> [symbolic = %I.type.loc7_36.2 (constants.%I.type.eb9)] // CHECK:STDOUT: %I.impl_witness.loc7_38.2: = impl_witness %I.impl_witness_table, @C.as.I.impl(%T.loc7_14.2) [symbolic = %I.impl_witness.loc7_38.2 (constants.%I.impl_witness.1cb)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete: = require_complete_type %I.type.loc7_36.2 [symbolic = %require_complete (constants.%require_complete.d0a)] // CHECK:STDOUT: // CHECK:STDOUT: impl: %C.ref as %I.type.loc7_36.1 { // CHECK:STDOUT: %I.impl_witness_table = impl_witness_table (), @C.as.I.impl [concrete] // CHECK:STDOUT: %I.impl_witness.loc7_38.1: = impl_witness %I.impl_witness_table, @C.as.I.impl(constants.%T.67d) [symbolic = %I.impl_witness.loc7_38.2 (constants.%I.impl_witness.1cb)] // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: witness = %I.impl_witness.loc7_38.1 // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @C { // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type.357] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%C // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic fn @A(%T.loc9_6.2: %I.type.5f7) { // CHECK:STDOUT: %T.loc9_6.1: %I.type.5f7 = symbolic_binding T, 0 [symbolic = %T.loc9_6.1 (constants.%T.bc3)] // CHECK:STDOUT: %T.binding.as_type: type = symbolic_binding_type T, 0, %T.loc9_6.1 [symbolic = %T.binding.as_type (constants.%T.binding.as_type)] // CHECK:STDOUT: %pattern_type: type = pattern_type %T.binding.as_type [symbolic = %pattern_type (constants.%pattern_type.826)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete: = require_complete_type %T.binding.as_type [symbolic = %require_complete (constants.%require_complete.d69)] // CHECK:STDOUT: // CHECK:STDOUT: fn(%t.param: @A.%T.binding.as_type (%T.binding.as_type)) { // CHECK:STDOUT: !entry: // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @B() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %A.ref: %A.type = name_ref A, file.%A.decl [concrete = constants.%A] // CHECK:STDOUT: %.loc12_6.1: %empty_struct_type = struct_literal () [concrete = constants.%empty_struct] // CHECK:STDOUT: %C.ref: type = name_ref C, file.%C.decl [concrete = constants.%C] // CHECK:STDOUT: %.loc12_6.2: ref %C = temporary_storage // CHECK:STDOUT: %.loc12_6.3: init %C to %.loc12_6.2 = class_init () [concrete = constants.%C.val] // CHECK:STDOUT: %.loc12_8.1: init %C = converted %.loc12_6.1, %.loc12_6.3 [concrete = constants.%C.val] // CHECK:STDOUT: %I.facet: %I.type.5f7 = facet_value constants.%C, (constants.%I.impl_witness.1c4) [concrete = constants.%I.facet.0e1] // CHECK:STDOUT: %.loc12_12: %I.type.5f7 = converted constants.%C, %I.facet [concrete = constants.%I.facet.0e1] // CHECK:STDOUT: %A.specific_fn: = specific_function %A.ref, @A(constants.%I.facet.0e1) [concrete = constants.%A.specific_fn] // CHECK:STDOUT: %.loc12_8.2: ref %C = temporary %.loc12_6.2, %.loc12_8.1 // CHECK:STDOUT: %.loc12_8.3: %C = acquire_value %.loc12_8.2 // CHECK:STDOUT: %A.call: init %empty_tuple.type = call %A.specific_fn(%.loc12_8.3) // CHECK:STDOUT: %Destroy.Op.bound: = bound_method %.loc12_8.2, constants.%Destroy.Op // CHECK:STDOUT: %Destroy.Op.call: init %empty_tuple.type = call %Destroy.Op.bound(%.loc12_8.2) // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @Destroy.Op(%self.param: ref %C) = "no_op"; // CHECK:STDOUT: // CHECK:STDOUT: specific @I(constants.%V, constants.%W) { // CHECK:STDOUT: %V.loc3_13.1 => constants.%V // CHECK:STDOUT: %W.loc3_23.1 => constants.%W // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @I.WithSelf(constants.%V, constants.%W, constants.%Self.8fa) {} // CHECK:STDOUT: // CHECK:STDOUT: specific @I(constants.%T.67d, constants.%empty_tuple.type) { // CHECK:STDOUT: %V.loc3_13.1 => constants.%T.67d // CHECK:STDOUT: %W.loc3_23.1 => constants.%empty_tuple.type // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %I.type => constants.%I.type.eb9 // CHECK:STDOUT: %Self.loc3_33.2 => constants.%Self.061 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @C.as.I.impl(constants.%T.67d) { // CHECK:STDOUT: %T.loc7_14.2 => constants.%T.67d // CHECK:STDOUT: %I.type.loc7_36.2 => constants.%I.type.eb9 // CHECK:STDOUT: %I.impl_witness.loc7_38.2 => constants.%I.impl_witness.1cb // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @I.WithSelf(constants.%T.67d, constants.%empty_tuple.type, constants.%Self.8fa) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @I.WithSelf(constants.%T.67d, constants.%empty_tuple.type, constants.%I.facet.a6e) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @I(constants.%empty_struct_type, constants.%empty_tuple.type) { // CHECK:STDOUT: %V.loc3_13.1 => constants.%empty_struct_type // CHECK:STDOUT: %W.loc3_23.1 => constants.%empty_tuple.type // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %I.type => constants.%I.type.5f7 // CHECK:STDOUT: %Self.loc3_33.2 => constants.%Self.2a6 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @I.WithSelf(constants.%empty_struct_type, constants.%empty_tuple.type, constants.%Self.8fa) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @A(constants.%T.bc3) { // CHECK:STDOUT: %T.loc9_6.1 => constants.%T.bc3 // CHECK:STDOUT: %T.binding.as_type => constants.%T.binding.as_type // CHECK:STDOUT: %pattern_type => constants.%pattern_type.826 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @C.as.I.impl(constants.%empty_struct_type) { // CHECK:STDOUT: %T.loc7_14.2 => constants.%empty_struct_type // CHECK:STDOUT: %I.type.loc7_36.2 => constants.%I.type.5f7 // CHECK:STDOUT: %I.impl_witness.loc7_38.2 => constants.%I.impl_witness.1c4 // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete => constants.%complete_type.1c0 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @A(constants.%I.facet.0e1) { // CHECK:STDOUT: %T.loc9_6.1 => constants.%I.facet.0e1 // CHECK:STDOUT: %T.binding.as_type => constants.%C // CHECK:STDOUT: %pattern_type => constants.%pattern_type.7c7 // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete => constants.%complete_type.357 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- fail_mismatch_impl_constraint_with_fixed_specific.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %type: type = facet_type [concrete] // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self] // CHECK:STDOUT: %pattern_type.98f: type = pattern_type type [concrete] // CHECK:STDOUT: %V: type = symbolic_binding V, 0 [symbolic] // CHECK:STDOUT: %W: type = symbolic_binding W, 1 [symbolic] // CHECK:STDOUT: %I.type.609: type = generic_interface_type @I [concrete] // CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] // CHECK:STDOUT: %I.generic: %I.type.609 = struct_value () [concrete] // CHECK:STDOUT: %I.type.54f: type = facet_type <@I, @I(%V, %W)> [symbolic] // CHECK:STDOUT: %Self.8fa: %I.type.54f = symbolic_binding Self, 2 [symbolic] // CHECK:STDOUT: %C: type = class_type @C [concrete] // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %complete_type: = complete_type_witness %empty_struct_type [concrete] // CHECK:STDOUT: %T.67d: type = symbolic_binding T, 0 [symbolic] // CHECK:STDOUT: %empty_tuple: %empty_tuple.type = tuple_value () [concrete] // CHECK:STDOUT: %I.type.eb9: type = facet_type <@I, @I(%T.67d, %empty_tuple.type)> [symbolic] // CHECK:STDOUT: %I.impl_witness: = impl_witness @C.as.I.impl.%I.impl_witness_table, @C.as.I.impl(%T.67d) [symbolic] // CHECK:STDOUT: %Self.061: %I.type.eb9 = symbolic_binding Self, 2 [symbolic] // CHECK:STDOUT: %require_complete.d0a: = require_complete_type %I.type.eb9 [symbolic] // CHECK:STDOUT: %I.facet: %I.type.eb9 = facet_value %C, (%I.impl_witness) [symbolic] // CHECK:STDOUT: %empty_struct: %empty_struct_type = struct_value () [concrete] // CHECK:STDOUT: %I.type.7b9: type = facet_type <@I, @I(%empty_struct_type, %empty_struct_type)> [concrete] // CHECK:STDOUT: %pattern_type.24a: type = pattern_type %I.type.7b9 [concrete] // CHECK:STDOUT: %T.274: %I.type.7b9 = symbolic_binding T, 0 [symbolic] // CHECK:STDOUT: %Self.c70: %I.type.7b9 = symbolic_binding Self, 2 [symbolic] // CHECK:STDOUT: %T.binding.as_type: type = symbolic_binding_type T, 0, %T.274 [symbolic] // CHECK:STDOUT: %pattern_type.ced: type = pattern_type %T.binding.as_type [symbolic] // CHECK:STDOUT: %A.type: type = fn_type @A [concrete] // CHECK:STDOUT: %A: %A.type = struct_value () [concrete] // CHECK:STDOUT: %require_complete.b50: = require_complete_type %T.binding.as_type [symbolic] // CHECK:STDOUT: %B.type: type = fn_type @B [concrete] // CHECK:STDOUT: %B: %B.type = struct_value () [concrete] // CHECK:STDOUT: %C.val: %C = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .I = %I.decl // CHECK:STDOUT: .C = %C.decl // CHECK:STDOUT: .A = %A.decl // CHECK:STDOUT: .B = %B.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %I.decl: %I.type.609 = interface_decl @I [concrete = constants.%I.generic] { // CHECK:STDOUT: %V.patt: %pattern_type.98f = symbolic_binding_pattern V, 0 [concrete] // CHECK:STDOUT: %W.patt: %pattern_type.98f = symbolic_binding_pattern W, 1 [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %.loc3_17.1: type = splice_block %.loc3_17.2 [concrete = type] { // CHECK:STDOUT: %.Self.2: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %.loc3_17.2: type = type_literal type [concrete = type] // CHECK:STDOUT: } // CHECK:STDOUT: %V.loc3_13.2: type = symbolic_binding V, 0 [symbolic = %V.loc3_13.1 (constants.%V)] // CHECK:STDOUT: %.loc3_27.1: type = splice_block %.loc3_27.2 [concrete = type] { // CHECK:STDOUT: %.Self.1: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %.loc3_27.2: type = type_literal type [concrete = type] // CHECK:STDOUT: } // CHECK:STDOUT: %W.loc3_23.2: type = symbolic_binding W, 1 [symbolic = %W.loc3_23.1 (constants.%W)] // CHECK:STDOUT: } // CHECK:STDOUT: %C.decl: type = class_decl @C [concrete = constants.%C] {} {} // CHECK:STDOUT: impl_decl @C.as.I.impl [concrete] { // CHECK:STDOUT: %T.patt: %pattern_type.98f = symbolic_binding_pattern T, 0 [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %C.ref: type = name_ref C, file.%C.decl [concrete = constants.%C] // CHECK:STDOUT: %I.ref: %I.type.609 = name_ref I, file.%I.decl [concrete = constants.%I.generic] // CHECK:STDOUT: %T.ref: type = name_ref T, %T.loc7_14.1 [symbolic = %T.loc7_14.2 (constants.%T.67d)] // CHECK:STDOUT: %.loc7_35: %empty_tuple.type = tuple_literal () [concrete = constants.%empty_tuple] // CHECK:STDOUT: %.loc7_36: type = converted %.loc7_35, constants.%empty_tuple.type [concrete = constants.%empty_tuple.type] // CHECK:STDOUT: %I.type.loc7_36.1: type = facet_type <@I, @I(constants.%T.67d, constants.%empty_tuple.type)> [symbolic = %I.type.loc7_36.2 (constants.%I.type.eb9)] // CHECK:STDOUT: %.loc7_18.1: type = splice_block %.loc7_18.2 [concrete = type] { // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %.loc7_18.2: type = type_literal type [concrete = type] // CHECK:STDOUT: } // CHECK:STDOUT: %T.loc7_14.1: type = symbolic_binding T, 0 [symbolic = %T.loc7_14.2 (constants.%T.67d)] // CHECK:STDOUT: } // CHECK:STDOUT: %A.decl: %A.type = fn_decl @A [concrete = constants.%A] { // CHECK:STDOUT: %T.patt: %pattern_type.24a = symbolic_binding_pattern T, 0 [concrete] // CHECK:STDOUT: %t.patt: @A.%pattern_type (%pattern_type.ced) = value_binding_pattern t [concrete] // CHECK:STDOUT: %t.param_patt: @A.%pattern_type (%pattern_type.ced) = value_param_pattern %t.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %.loc9_18.1: type = splice_block %I.type [concrete = constants.%I.type.7b9] { // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %I.ref: %I.type.609 = name_ref I, file.%I.decl [concrete = constants.%I.generic] // CHECK:STDOUT: %.loc9_13: %empty_struct_type = struct_literal () [concrete = constants.%empty_struct] // CHECK:STDOUT: %.loc9_17: %empty_struct_type = struct_literal () [concrete = constants.%empty_struct] // CHECK:STDOUT: %.loc9_18.2: type = converted %.loc9_13, constants.%empty_struct_type [concrete = constants.%empty_struct_type] // CHECK:STDOUT: %.loc9_18.3: type = converted %.loc9_17, constants.%empty_struct_type [concrete = constants.%empty_struct_type] // CHECK:STDOUT: %I.type: type = facet_type <@I, @I(constants.%empty_struct_type, constants.%empty_struct_type)> [concrete = constants.%I.type.7b9] // CHECK:STDOUT: } // CHECK:STDOUT: %T.loc9_6.2: %I.type.7b9 = symbolic_binding T, 0 [symbolic = %T.loc9_6.1 (constants.%T.274)] // CHECK:STDOUT: %t.param: @A.%T.binding.as_type (%T.binding.as_type) = value_param call_param0 // CHECK:STDOUT: %.loc9_31.1: type = splice_block %.loc9_31.2 [symbolic = %T.binding.as_type (constants.%T.binding.as_type)] { // CHECK:STDOUT: %T.ref: %I.type.7b9 = name_ref T, %T.loc9_6.2 [symbolic = %T.loc9_6.1 (constants.%T.274)] // CHECK:STDOUT: %T.as_type: type = facet_access_type %T.ref [symbolic = %T.binding.as_type (constants.%T.binding.as_type)] // CHECK:STDOUT: %.loc9_31.2: type = converted %T.ref, %T.as_type [symbolic = %T.binding.as_type (constants.%T.binding.as_type)] // CHECK:STDOUT: } // CHECK:STDOUT: %t: @A.%T.binding.as_type (%T.binding.as_type) = value_binding t, %t.param // CHECK:STDOUT: } // CHECK:STDOUT: %B.decl: %B.type = fn_decl @B [concrete = constants.%B] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic interface @I(%V.loc3_13.2: type, %W.loc3_23.2: type) { // CHECK:STDOUT: %V.loc3_13.1: type = symbolic_binding V, 0 [symbolic = %V.loc3_13.1 (constants.%V)] // CHECK:STDOUT: %W.loc3_23.1: type = symbolic_binding W, 1 [symbolic = %W.loc3_23.1 (constants.%W)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %I.type: type = facet_type <@I, @I(%V.loc3_13.1, %W.loc3_23.1)> [symbolic = %I.type (constants.%I.type.54f)] // CHECK:STDOUT: %Self.loc3_33.2: @I.%I.type (%I.type.54f) = symbolic_binding Self, 2 [symbolic = %Self.loc3_33.2 (constants.%Self.8fa)] // CHECK:STDOUT: // CHECK:STDOUT: interface { // CHECK:STDOUT: %Self.loc3_33.1: @I.%I.type (%I.type.54f) = symbolic_binding Self, 2 [symbolic = %Self.loc3_33.2 (constants.%Self.8fa)] // CHECK:STDOUT: %I.WithSelf.decl = interface_with_self_decl @I [concrete] // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = %Self.loc3_33.1 // CHECK:STDOUT: witness = () // CHECK:STDOUT: // CHECK:STDOUT: !requires: // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic impl @C.as.I.impl(%T.loc7_14.1: type) { // CHECK:STDOUT: %T.loc7_14.2: type = symbolic_binding T, 0 [symbolic = %T.loc7_14.2 (constants.%T.67d)] // CHECK:STDOUT: %I.type.loc7_36.2: type = facet_type <@I, @I(%T.loc7_14.2, constants.%empty_tuple.type)> [symbolic = %I.type.loc7_36.2 (constants.%I.type.eb9)] // CHECK:STDOUT: %I.impl_witness.loc7_38.2: = impl_witness %I.impl_witness_table, @C.as.I.impl(%T.loc7_14.2) [symbolic = %I.impl_witness.loc7_38.2 (constants.%I.impl_witness)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete: = require_complete_type %I.type.loc7_36.2 [symbolic = %require_complete (constants.%require_complete.d0a)] // CHECK:STDOUT: // CHECK:STDOUT: impl: %C.ref as %I.type.loc7_36.1 { // CHECK:STDOUT: %I.impl_witness_table = impl_witness_table (), @C.as.I.impl [concrete] // CHECK:STDOUT: %I.impl_witness.loc7_38.1: = impl_witness %I.impl_witness_table, @C.as.I.impl(constants.%T.67d) [symbolic = %I.impl_witness.loc7_38.2 (constants.%I.impl_witness)] // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: witness = %I.impl_witness.loc7_38.1 // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @C { // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%C // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic fn @A(%T.loc9_6.2: %I.type.7b9) { // CHECK:STDOUT: %T.loc9_6.1: %I.type.7b9 = symbolic_binding T, 0 [symbolic = %T.loc9_6.1 (constants.%T.274)] // CHECK:STDOUT: %T.binding.as_type: type = symbolic_binding_type T, 0, %T.loc9_6.1 [symbolic = %T.binding.as_type (constants.%T.binding.as_type)] // CHECK:STDOUT: %pattern_type: type = pattern_type %T.binding.as_type [symbolic = %pattern_type (constants.%pattern_type.ced)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete: = require_complete_type %T.binding.as_type [symbolic = %require_complete (constants.%require_complete.b50)] // CHECK:STDOUT: // CHECK:STDOUT: fn(%t.param: @A.%T.binding.as_type (%T.binding.as_type)) { // CHECK:STDOUT: !entry: // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @B() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %A.ref: %A.type = name_ref A, file.%A.decl [concrete = constants.%A] // CHECK:STDOUT: %.loc19_6.1: %empty_struct_type = struct_literal () [concrete = constants.%empty_struct] // CHECK:STDOUT: %C.ref: type = name_ref C, file.%C.decl [concrete = constants.%C] // CHECK:STDOUT: %.loc19_6.2: ref %C = temporary_storage // CHECK:STDOUT: %.loc19_6.3: init %C to %.loc19_6.2 = class_init () [concrete = constants.%C.val] // CHECK:STDOUT: %.loc19_8: init %C = converted %.loc19_6.1, %.loc19_6.3 [concrete = constants.%C.val] // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @I(constants.%V, constants.%W) { // CHECK:STDOUT: %V.loc3_13.1 => constants.%V // CHECK:STDOUT: %W.loc3_23.1 => constants.%W // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @I.WithSelf(constants.%V, constants.%W, constants.%Self.8fa) {} // CHECK:STDOUT: // CHECK:STDOUT: specific @I(constants.%T.67d, constants.%empty_tuple.type) { // CHECK:STDOUT: %V.loc3_13.1 => constants.%T.67d // CHECK:STDOUT: %W.loc3_23.1 => constants.%empty_tuple.type // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %I.type => constants.%I.type.eb9 // CHECK:STDOUT: %Self.loc3_33.2 => constants.%Self.061 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @C.as.I.impl(constants.%T.67d) { // CHECK:STDOUT: %T.loc7_14.2 => constants.%T.67d // CHECK:STDOUT: %I.type.loc7_36.2 => constants.%I.type.eb9 // CHECK:STDOUT: %I.impl_witness.loc7_38.2 => constants.%I.impl_witness // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @I.WithSelf(constants.%T.67d, constants.%empty_tuple.type, constants.%Self.8fa) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @I.WithSelf(constants.%T.67d, constants.%empty_tuple.type, constants.%I.facet) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @I(constants.%empty_struct_type, constants.%empty_struct_type) { // CHECK:STDOUT: %V.loc3_13.1 => constants.%empty_struct_type // CHECK:STDOUT: %W.loc3_23.1 => constants.%empty_struct_type // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %I.type => constants.%I.type.7b9 // CHECK:STDOUT: %Self.loc3_33.2 => constants.%Self.c70 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @I.WithSelf(constants.%empty_struct_type, constants.%empty_struct_type, constants.%Self.8fa) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @A(constants.%T.274) { // CHECK:STDOUT: %T.loc9_6.1 => constants.%T.274 // CHECK:STDOUT: %T.binding.as_type => constants.%T.binding.as_type // CHECK:STDOUT: %pattern_type => constants.%pattern_type.ced // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- fail_mismatch_impl_self_with_fixed_specific.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %I.type: type = facet_type <@I> [concrete] // CHECK:STDOUT: %Self: %I.type = symbolic_binding Self, 0 [symbolic] // CHECK:STDOUT: %type: type = facet_type [concrete] // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self] // CHECK:STDOUT: %pattern_type.98f: type = pattern_type type [concrete] // CHECK:STDOUT: %V: type = symbolic_binding V, 0 [symbolic] // CHECK:STDOUT: %W: type = symbolic_binding W, 1 [symbolic] // CHECK:STDOUT: %C.type: type = generic_class_type @C [concrete] // CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] // CHECK:STDOUT: %C.generic: %C.type = struct_value () [concrete] // CHECK:STDOUT: %C.2f8: type = class_type @C, @C(%V, %W) [symbolic] // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %complete_type: = complete_type_witness %empty_struct_type [concrete] // CHECK:STDOUT: %T.67d: type = symbolic_binding T, 0 [symbolic] // CHECK:STDOUT: %empty_tuple: %empty_tuple.type = tuple_value () [concrete] // CHECK:STDOUT: %C.13c: type = class_type @C, @C(%T.67d, %empty_tuple.type) [symbolic] // CHECK:STDOUT: %I.impl_witness: = impl_witness @C.as.I.impl.%I.impl_witness_table, @C.as.I.impl(%T.67d) [symbolic] // CHECK:STDOUT: %I.facet: %I.type = facet_value %C.13c, (%I.impl_witness) [symbolic] // CHECK:STDOUT: %pattern_type.9d9: type = pattern_type %I.type [concrete] // CHECK:STDOUT: %T.651: %I.type = symbolic_binding T, 0 [symbolic] // CHECK:STDOUT: %T.binding.as_type: type = symbolic_binding_type T, 0, %T.651 [symbolic] // CHECK:STDOUT: %pattern_type.422: type = pattern_type %T.binding.as_type [symbolic] // CHECK:STDOUT: %A.type: type = fn_type @A [concrete] // CHECK:STDOUT: %A: %A.type = struct_value () [concrete] // CHECK:STDOUT: %require_complete: = require_complete_type %T.binding.as_type [symbolic] // CHECK:STDOUT: %B.type: type = fn_type @B [concrete] // CHECK:STDOUT: %B: %B.type = struct_value () [concrete] // CHECK:STDOUT: %empty_struct: %empty_struct_type = struct_value () [concrete] // CHECK:STDOUT: %C.9f1: type = class_type @C, @C(%empty_struct_type, %empty_struct_type) [concrete] // CHECK:STDOUT: %C.val: %C.9f1 = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .I = %I.decl // CHECK:STDOUT: .C = %C.decl // CHECK:STDOUT: .A = %A.decl // CHECK:STDOUT: .B = %B.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %I.decl: type = interface_decl @I [concrete = constants.%I.type] {} {} // CHECK:STDOUT: %C.decl: %C.type = class_decl @C [concrete = constants.%C.generic] { // CHECK:STDOUT: %V.patt: %pattern_type.98f = symbolic_binding_pattern V, 0 [concrete] // CHECK:STDOUT: %W.patt: %pattern_type.98f = symbolic_binding_pattern W, 1 [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %.loc5_13.1: type = splice_block %.loc5_13.2 [concrete = type] { // CHECK:STDOUT: %.Self.2: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %.loc5_13.2: type = type_literal type [concrete = type] // CHECK:STDOUT: } // CHECK:STDOUT: %V.loc5_9.2: type = symbolic_binding V, 0 [symbolic = %V.loc5_9.1 (constants.%V)] // CHECK:STDOUT: %.loc5_23.1: type = splice_block %.loc5_23.2 [concrete = type] { // CHECK:STDOUT: %.Self.1: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %.loc5_23.2: type = type_literal type [concrete = type] // CHECK:STDOUT: } // CHECK:STDOUT: %W.loc5_19.2: type = symbolic_binding W, 1 [symbolic = %W.loc5_19.1 (constants.%W)] // CHECK:STDOUT: } // CHECK:STDOUT: impl_decl @C.as.I.impl [concrete] { // CHECK:STDOUT: %T.patt: %pattern_type.98f = symbolic_binding_pattern T, 0 [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %C.ref: %C.type = name_ref C, file.%C.decl [concrete = constants.%C.generic] // CHECK:STDOUT: %T.ref: type = name_ref T, %T.loc7_14.1 [symbolic = %T.loc7_14.2 (constants.%T.67d)] // CHECK:STDOUT: %.loc7_30: %empty_tuple.type = tuple_literal () [concrete = constants.%empty_tuple] // CHECK:STDOUT: %.loc7_31: type = converted %.loc7_30, constants.%empty_tuple.type [concrete = constants.%empty_tuple.type] // CHECK:STDOUT: %C.loc7_31.1: type = class_type @C, @C(constants.%T.67d, constants.%empty_tuple.type) [symbolic = %C.loc7_31.2 (constants.%C.13c)] // CHECK:STDOUT: %I.ref: type = name_ref I, file.%I.decl [concrete = constants.%I.type] // CHECK:STDOUT: %.loc7_18.1: type = splice_block %.loc7_18.2 [concrete = type] { // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %.loc7_18.2: type = type_literal type [concrete = type] // CHECK:STDOUT: } // CHECK:STDOUT: %T.loc7_14.1: type = symbolic_binding T, 0 [symbolic = %T.loc7_14.2 (constants.%T.67d)] // CHECK:STDOUT: } // CHECK:STDOUT: %A.decl: %A.type = fn_decl @A [concrete = constants.%A] { // CHECK:STDOUT: %T.patt: %pattern_type.9d9 = symbolic_binding_pattern T, 0 [concrete] // CHECK:STDOUT: %t.patt: @A.%pattern_type (%pattern_type.422) = value_binding_pattern t [concrete] // CHECK:STDOUT: %t.param_patt: @A.%pattern_type (%pattern_type.422) = value_param_pattern %t.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %.loc9_10: type = splice_block %I.ref [concrete = constants.%I.type] { // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %I.ref: type = name_ref I, file.%I.decl [concrete = constants.%I.type] // CHECK:STDOUT: } // CHECK:STDOUT: %T.loc9_6.2: %I.type = symbolic_binding T, 0 [symbolic = %T.loc9_6.1 (constants.%T.651)] // CHECK:STDOUT: %t.param: @A.%T.binding.as_type (%T.binding.as_type) = value_param call_param0 // CHECK:STDOUT: %.loc9_23.1: type = splice_block %.loc9_23.2 [symbolic = %T.binding.as_type (constants.%T.binding.as_type)] { // CHECK:STDOUT: %T.ref: %I.type = name_ref T, %T.loc9_6.2 [symbolic = %T.loc9_6.1 (constants.%T.651)] // CHECK:STDOUT: %T.as_type: type = facet_access_type %T.ref [symbolic = %T.binding.as_type (constants.%T.binding.as_type)] // CHECK:STDOUT: %.loc9_23.2: type = converted %T.ref, %T.as_type [symbolic = %T.binding.as_type (constants.%T.binding.as_type)] // CHECK:STDOUT: } // CHECK:STDOUT: %t: @A.%T.binding.as_type (%T.binding.as_type) = value_binding t, %t.param // CHECK:STDOUT: } // CHECK:STDOUT: %B.decl: %B.type = fn_decl @B [concrete = constants.%B] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: interface @I { // CHECK:STDOUT: %Self: %I.type = symbolic_binding Self, 0 [symbolic = constants.%Self] // CHECK:STDOUT: %I.WithSelf.decl = interface_with_self_decl @I [concrete] // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = %Self // CHECK:STDOUT: witness = () // CHECK:STDOUT: // CHECK:STDOUT: !requires: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic impl @C.as.I.impl(%T.loc7_14.1: type) { // CHECK:STDOUT: %T.loc7_14.2: type = symbolic_binding T, 0 [symbolic = %T.loc7_14.2 (constants.%T.67d)] // CHECK:STDOUT: %C.loc7_31.2: type = class_type @C, @C(%T.loc7_14.2, constants.%empty_tuple.type) [symbolic = %C.loc7_31.2 (constants.%C.13c)] // CHECK:STDOUT: %I.impl_witness.loc7_38.2: = impl_witness %I.impl_witness_table, @C.as.I.impl(%T.loc7_14.2) [symbolic = %I.impl_witness.loc7_38.2 (constants.%I.impl_witness)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: // CHECK:STDOUT: impl: %C.loc7_31.1 as %I.ref { // CHECK:STDOUT: %I.impl_witness_table = impl_witness_table (), @C.as.I.impl [concrete] // CHECK:STDOUT: %I.impl_witness.loc7_38.1: = impl_witness %I.impl_witness_table, @C.as.I.impl(constants.%T.67d) [symbolic = %I.impl_witness.loc7_38.2 (constants.%I.impl_witness)] // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: witness = %I.impl_witness.loc7_38.1 // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic class @C(%V.loc5_9.2: type, %W.loc5_19.2: type) { // CHECK:STDOUT: %V.loc5_9.1: type = symbolic_binding V, 0 [symbolic = %V.loc5_9.1 (constants.%V)] // CHECK:STDOUT: %W.loc5_19.1: type = symbolic_binding W, 1 [symbolic = %W.loc5_19.1 (constants.%W)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: // CHECK:STDOUT: class { // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%C.2f8 // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic fn @A(%T.loc9_6.2: %I.type) { // CHECK:STDOUT: %T.loc9_6.1: %I.type = symbolic_binding T, 0 [symbolic = %T.loc9_6.1 (constants.%T.651)] // CHECK:STDOUT: %T.binding.as_type: type = symbolic_binding_type T, 0, %T.loc9_6.1 [symbolic = %T.binding.as_type (constants.%T.binding.as_type)] // CHECK:STDOUT: %pattern_type: type = pattern_type %T.binding.as_type [symbolic = %pattern_type (constants.%pattern_type.422)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete: = require_complete_type %T.binding.as_type [symbolic = %require_complete (constants.%require_complete)] // CHECK:STDOUT: // CHECK:STDOUT: fn(%t.param: @A.%T.binding.as_type (%T.binding.as_type)) { // CHECK:STDOUT: !entry: // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @B() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %A.ref: %A.type = name_ref A, file.%A.decl [concrete = constants.%A] // CHECK:STDOUT: %.loc19_6.1: %empty_struct_type = struct_literal () [concrete = constants.%empty_struct] // CHECK:STDOUT: %C.ref: %C.type = name_ref C, file.%C.decl [concrete = constants.%C.generic] // CHECK:STDOUT: %.loc19_14: %empty_struct_type = struct_literal () [concrete = constants.%empty_struct] // CHECK:STDOUT: %.loc19_18: %empty_struct_type = struct_literal () [concrete = constants.%empty_struct] // CHECK:STDOUT: %.loc19_19.1: type = converted %.loc19_14, constants.%empty_struct_type [concrete = constants.%empty_struct_type] // CHECK:STDOUT: %.loc19_19.2: type = converted %.loc19_18, constants.%empty_struct_type [concrete = constants.%empty_struct_type] // CHECK:STDOUT: %C: type = class_type @C, @C(constants.%empty_struct_type, constants.%empty_struct_type) [concrete = constants.%C.9f1] // CHECK:STDOUT: %.loc19_6.2: ref %C.9f1 = temporary_storage // CHECK:STDOUT: %.loc19_6.3: init %C.9f1 to %.loc19_6.2 = class_init () [concrete = constants.%C.val] // CHECK:STDOUT: %.loc19_8: init %C.9f1 = converted %.loc19_6.1, %.loc19_6.3 [concrete = constants.%C.val] // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @I.WithSelf(constants.%Self) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @C(constants.%V, constants.%W) { // CHECK:STDOUT: %V.loc5_9.1 => constants.%V // CHECK:STDOUT: %W.loc5_19.1 => constants.%W // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @C(constants.%T.67d, constants.%empty_tuple.type) { // CHECK:STDOUT: %V.loc5_9.1 => constants.%T.67d // CHECK:STDOUT: %W.loc5_19.1 => constants.%empty_tuple.type // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @C.as.I.impl(constants.%T.67d) { // CHECK:STDOUT: %T.loc7_14.2 => constants.%T.67d // CHECK:STDOUT: %C.loc7_31.2 => constants.%C.13c // CHECK:STDOUT: %I.impl_witness.loc7_38.2 => constants.%I.impl_witness // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @I.WithSelf(constants.%I.facet) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @A(constants.%T.651) { // CHECK:STDOUT: %T.loc9_6.1 => constants.%T.651 // CHECK:STDOUT: %T.binding.as_type => constants.%T.binding.as_type // CHECK:STDOUT: %pattern_type => constants.%pattern_type.422 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @C(constants.%empty_struct_type, constants.%empty_struct_type) { // CHECK:STDOUT: %V.loc5_9.1 => constants.%empty_struct_type // CHECK:STDOUT: %W.loc5_19.1 => constants.%empty_struct_type // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: } // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/facet/convert_facet_type_to_facet_value.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/convert.carbon // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/facet/convert_facet_type_to_facet_value.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/facet/convert_facet_type_to_facet_value.carbon // --- convert_facet_value_to_facet_value.carbon library "[[@TEST_NAME]]"; interface Eats {} interface Animal {} // TODO: This may be rejected in the future. // https://github.com/carbon-language/carbon-lang/issues/4853 impl Animal as Eats {} fn Feed(unused e:! Eats) {} fn F() { Feed(Animal); } // --- fail_facet_value_to_facet_type.carbon library "[[@TEST_NAME]]"; interface Eats {} interface Animal {} // TODO: This may be rejected in the future. // https://github.com/carbon-language/carbon-lang/issues/4853 impl Animal as Eats {} fn Feed(unused e:! Eats) {} class Goat {} impl Goat as Animal {} fn F() { // CHECK:STDERR: fail_facet_value_to_facet_type.carbon:[[@LINE+7]]:3: error: cannot convert type `Goat as Animal` that implements `Animal` into type implementing `Eats` [ConversionFailureFacetToFacet] // CHECK:STDERR: Feed(Goat as Animal); // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: fail_facet_value_to_facet_type.carbon:[[@LINE-8]]:16: note: initializing generic parameter `e` declared here [InitializingGenericParam] // CHECK:STDERR: fn Feed(unused e:! Eats) {} // CHECK:STDERR: ^ // CHECK:STDERR: Feed(Goat as Animal); } // --- fail_convert_multi_interface_facet_value_to_facet_value.carbon library "[[@TEST_NAME]]"; interface Eats {} interface Animal {} interface Climbs {} // TODO: This may be rejected in the future. // https://github.com/carbon-language/carbon-lang/issues/4853 impl Animal as Eats {} fn Feed(unused e:! Eats) {} class Goat {} impl Goat as Animal {} impl Goat as Climbs {} // These are expected to fail: // https://github.com/carbon-language/carbon-lang/issues/4853#issuecomment-2707673344 fn F() { // CHECK:STDERR: fail_convert_multi_interface_facet_value_to_facet_value.carbon:[[@LINE+7]]:3: error: cannot convert type `Goat as Animal & Climbs` that implements `Animal & Climbs` into type implementing `Eats` [ConversionFailureFacetToFacet] // CHECK:STDERR: Feed(Goat as (Animal & Climbs)); // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: fail_convert_multi_interface_facet_value_to_facet_value.carbon:[[@LINE-11]]:16: note: initializing generic parameter `e` declared here [InitializingGenericParam] // CHECK:STDERR: fn Feed(unused e:! Eats) {} // CHECK:STDERR: ^ // CHECK:STDERR: Feed(Goat as (Animal & Climbs)); // CHECK:STDERR: fail_convert_multi_interface_facet_value_to_facet_value.carbon:[[@LINE+7]]:3: error: cannot convert type `Animal & Climbs` into type implementing `Eats` [ConversionFailureTypeToFacet] // CHECK:STDERR: Feed(Animal & Climbs); // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: fail_convert_multi_interface_facet_value_to_facet_value.carbon:[[@LINE-20]]:16: note: initializing generic parameter `e` declared here [InitializingGenericParam] // CHECK:STDERR: fn Feed(unused e:! Eats) {} // CHECK:STDERR: ^ // CHECK:STDERR: Feed(Animal & Climbs); } ================================================ FILE: toolchain/check/testdata/facet/convert_facet_value_as_type_knows_original_type.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // TODO: Add ranges and switch to "--dump-sem-ir-ranges=only". // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/convert.carbon // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/facet/convert_facet_value_as_type_knows_original_type.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/facet/convert_facet_value_as_type_knows_original_type.carbon // --- explicit_as_type.carbon library "[[@TEST_NAME]]"; interface Eats {} interface Animal {} class Goat {} impl Goat as Animal {} impl Goat as Eats {} fn Feed(unused e:! Eats) {} fn F() { //@dump-sem-ir-begin Feed((Goat as Animal) as type); //@dump-sem-ir-end } // --- facet_type_in_type_position.carbon library "[[@TEST_NAME]]"; interface Eats { fn Eat(); } interface Animal {} class Goat { fn Bleet() {} impl as Animal {} extend impl as Eats { fn Eat() {} } } fn F() { // `Goat as Animal` in the type position retains/recovers the original type // Goat, so member lookup can see more than just `Animal`. //@dump-sem-ir-begin let x: Goat as Animal = {} as Goat; x.Bleet(); x.Eat(); (({} as Goat) as (Goat as Animal)).Bleet(); (({} as Goat) as (Goat as Animal)).Eat(); //@dump-sem-ir-end } // --- facet_access_type_converts_back_to_original_facet_value.carbon library "[[@TEST_NAME]]"; interface J {} class C(A:! J, B:! A) {} // The class C is holding a specific with a 2 SymbolicBindings where one is a // *symbolic* type of the other. // // This means deduce will determine the argument type for `A`, then `B`. Then it // will traverse into deducing the type of `B` with the type of the parameter // `B`, which are both `FacetAccessType(A)`. This ultimately tries to deduce the // value for `A` again, so it needs to match, even though the argument is now // `FacetAccessType(A)`. // // This depends on unwrapping the `FacetAccessType(A)` when converting it to the // type of `A`, instead of evaluating to a `FacetValue` holding the // `FacetAccessType(A)` with a witness from the type of `A`. This preserves the // original facet value instead of constructing a different facet value // referring to the same symbolic type and with the same facet type. // Essentially, it allows lossless round trips through a FacetAccessType when // converting back to the type of its original facet value. fn G[A:! J, B:! A](unused x: C(A, B)) {} fn F[A:! J, B:! A](x: C(A, B)) { //@dump-sem-ir-begin G(x); //@dump-sem-ir-end } // CHECK:STDOUT: --- explicit_as_type.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %Eats.type: type = facet_type <@Eats> [concrete] // CHECK:STDOUT: %Animal.type: type = facet_type <@Animal> [concrete] // CHECK:STDOUT: %Goat: type = class_type @Goat [concrete] // CHECK:STDOUT: %Animal.impl_witness: = impl_witness @Goat.as.Animal.impl.%Animal.impl_witness_table [concrete] // CHECK:STDOUT: %Animal.facet: %Animal.type = facet_value %Goat, (%Animal.impl_witness) [concrete] // CHECK:STDOUT: %Eats.impl_witness: = impl_witness @Goat.as.Eats.impl.%Eats.impl_witness_table [concrete] // CHECK:STDOUT: %Eats.facet: %Eats.type = facet_value %Goat, (%Eats.impl_witness) [concrete] // CHECK:STDOUT: %Feed.type: type = fn_type @Feed [concrete] // CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] // CHECK:STDOUT: %Feed: %Feed.type = struct_value () [concrete] // CHECK:STDOUT: %Feed.specific_fn: = specific_function %Feed, @Feed(%Eats.facet) [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @F() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %Feed.ref: %Feed.type = name_ref Feed, file.%Feed.decl [concrete = constants.%Feed] // CHECK:STDOUT: %Goat.ref: type = name_ref Goat, file.%Goat.decl [concrete = constants.%Goat] // CHECK:STDOUT: %Animal.ref: type = name_ref Animal, file.%Animal.decl [concrete = constants.%Animal.type] // CHECK:STDOUT: %Animal.facet: %Animal.type = facet_value %Goat.ref, (constants.%Animal.impl_witness) [concrete = constants.%Animal.facet] // CHECK:STDOUT: %.loc14_14: %Animal.type = converted %Goat.ref, %Animal.facet [concrete = constants.%Animal.facet] // CHECK:STDOUT: %.loc14_28: type = type_literal type [concrete = type] // CHECK:STDOUT: %as_type: type = facet_access_type %.loc14_14 [concrete = constants.%Goat] // CHECK:STDOUT: %.loc14_25: type = converted %.loc14_14, %as_type [concrete = constants.%Goat] // CHECK:STDOUT: %Eats.facet: %Eats.type = facet_value %.loc14_25, (constants.%Eats.impl_witness) [concrete = constants.%Eats.facet] // CHECK:STDOUT: %.loc14_32: %Eats.type = converted %.loc14_25, %Eats.facet [concrete = constants.%Eats.facet] // CHECK:STDOUT: %Feed.specific_fn: = specific_function %Feed.ref, @Feed(constants.%Eats.facet) [concrete = constants.%Feed.specific_fn] // CHECK:STDOUT: %Feed.call: init %empty_tuple.type = call %Feed.specific_fn() // CHECK:STDOUT: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- facet_type_in_type_position.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %Eats.type: type = facet_type <@Eats> [concrete] // CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] // CHECK:STDOUT: %Eats.assoc_type: type = assoc_entity_type @Eats [concrete] // CHECK:STDOUT: %assoc0.083: %Eats.assoc_type = assoc_entity element0, @Eats.WithSelf.%Eats.WithSelf.Eat.decl [concrete] // CHECK:STDOUT: %Animal.type: type = facet_type <@Animal> [concrete] // CHECK:STDOUT: %Goat: type = class_type @Goat [concrete] // CHECK:STDOUT: %Goat.Bleet.type: type = fn_type @Goat.Bleet [concrete] // CHECK:STDOUT: %Goat.Bleet: %Goat.Bleet.type = struct_value () [concrete] // CHECK:STDOUT: %Animal.impl_witness: = impl_witness @Goat.as.Animal.impl.%Animal.impl_witness_table [concrete] // CHECK:STDOUT: %Animal.facet: %Animal.type = facet_value %Goat, (%Animal.impl_witness) [concrete] // CHECK:STDOUT: %Eats.impl_witness: = impl_witness @Goat.as.Eats.impl.%Eats.impl_witness_table [concrete] // CHECK:STDOUT: %Goat.as.Eats.impl.Eat.type: type = fn_type @Goat.as.Eats.impl.Eat [concrete] // CHECK:STDOUT: %Goat.as.Eats.impl.Eat: %Goat.as.Eats.impl.Eat.type = struct_value () [concrete] // CHECK:STDOUT: %Eats.facet: %Eats.type = facet_value %Goat, (%Eats.impl_witness) [concrete] // CHECK:STDOUT: %Eats.WithSelf.Eat.type.b4d: type = fn_type @Eats.WithSelf.Eat, @Eats.WithSelf(%Eats.facet) [concrete] // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %pattern_type.234: type = pattern_type %Goat [concrete] // CHECK:STDOUT: %empty_struct: %empty_struct_type = struct_value () [concrete] // CHECK:STDOUT: %Goat.val: %Goat = struct_value () [concrete] // CHECK:STDOUT: %.35f: type = fn_type_with_self_type %Eats.WithSelf.Eat.type.b4d, %Eats.facet [concrete] // CHECK:STDOUT: %Destroy.Op.type: type = fn_type @Destroy.Op [concrete] // CHECK:STDOUT: %Destroy.Op: %Destroy.Op.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @F() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %x.patt: %pattern_type.234 = value_binding_pattern x [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %.loc22_28.1: %empty_struct_type = struct_literal () [concrete = constants.%empty_struct] // CHECK:STDOUT: %Goat.ref.loc22_33: type = name_ref Goat, file.%Goat.decl [concrete = constants.%Goat] // CHECK:STDOUT: %.loc22_28.2: ref %Goat = temporary_storage // CHECK:STDOUT: %.loc22_28.3: init %Goat to %.loc22_28.2 = class_init () [concrete = constants.%Goat.val] // CHECK:STDOUT: %.loc22_30.1: init %Goat = converted %.loc22_28.1, %.loc22_28.3 [concrete = constants.%Goat.val] // CHECK:STDOUT: %.loc22_15.1: type = splice_block %.loc22_15.3 [concrete = constants.%Goat] { // CHECK:STDOUT: %Goat.ref.loc22_10: type = name_ref Goat, file.%Goat.decl [concrete = constants.%Goat] // CHECK:STDOUT: %Animal.ref.loc22: type = name_ref Animal, file.%Animal.decl [concrete = constants.%Animal.type] // CHECK:STDOUT: %Animal.facet.loc22: %Animal.type = facet_value %Goat.ref.loc22_10, (constants.%Animal.impl_witness) [concrete = constants.%Animal.facet] // CHECK:STDOUT: %.loc22_15.2: %Animal.type = converted %Goat.ref.loc22_10, %Animal.facet.loc22 [concrete = constants.%Animal.facet] // CHECK:STDOUT: %as_type.loc22: type = facet_access_type %.loc22_15.2 [concrete = constants.%Goat] // CHECK:STDOUT: %.loc22_15.3: type = converted %.loc22_15.2, %as_type.loc22 [concrete = constants.%Goat] // CHECK:STDOUT: } // CHECK:STDOUT: %.loc22_30.2: ref %Goat = temporary %.loc22_28.2, %.loc22_30.1 // CHECK:STDOUT: %.loc22_30.3: %Goat = acquire_value %.loc22_30.2 // CHECK:STDOUT: %x: %Goat = value_binding x, %.loc22_30.3 // CHECK:STDOUT: %x.ref.loc23: %Goat = name_ref x, %x // CHECK:STDOUT: %Bleet.ref.loc23: %Goat.Bleet.type = name_ref Bleet, @Goat.%Goat.Bleet.decl [concrete = constants.%Goat.Bleet] // CHECK:STDOUT: %Goat.Bleet.call.loc23: init %empty_tuple.type = call %Bleet.ref.loc23() // CHECK:STDOUT: %x.ref.loc24: %Goat = name_ref x, %x // CHECK:STDOUT: %Eat.ref.loc24: %Eats.assoc_type = name_ref Eat, @Eats.WithSelf.%assoc0 [concrete = constants.%assoc0.083] // CHECK:STDOUT: %impl.elem0.loc24: %.35f = impl_witness_access constants.%Eats.impl_witness, element0 [concrete = constants.%Goat.as.Eats.impl.Eat] // CHECK:STDOUT: %Goat.as.Eats.impl.Eat.call.loc24: init %empty_tuple.type = call %impl.elem0.loc24() // CHECK:STDOUT: %.loc26_6.1: %empty_struct_type = struct_literal () [concrete = constants.%empty_struct] // CHECK:STDOUT: %Goat.ref.loc26_11: type = name_ref Goat, file.%Goat.decl [concrete = constants.%Goat] // CHECK:STDOUT: %.loc26_6.2: ref %Goat = temporary_storage // CHECK:STDOUT: %.loc26_6.3: init %Goat to %.loc26_6.2 = class_init () [concrete = constants.%Goat.val] // CHECK:STDOUT: %.loc26_8.1: init %Goat = converted %.loc26_6.1, %.loc26_6.3 [concrete = constants.%Goat.val] // CHECK:STDOUT: %Goat.ref.loc26_21: type = name_ref Goat, file.%Goat.decl [concrete = constants.%Goat] // CHECK:STDOUT: %Animal.ref.loc26: type = name_ref Animal, file.%Animal.decl [concrete = constants.%Animal.type] // CHECK:STDOUT: %Animal.facet.loc26: %Animal.type = facet_value %Goat.ref.loc26_21, (constants.%Animal.impl_witness) [concrete = constants.%Animal.facet] // CHECK:STDOUT: %.loc26_26: %Animal.type = converted %Goat.ref.loc26_21, %Animal.facet.loc26 [concrete = constants.%Animal.facet] // CHECK:STDOUT: %as_type.loc26: type = facet_access_type %.loc26_26 [concrete = constants.%Goat] // CHECK:STDOUT: %.loc26_35: type = converted %.loc26_26, %as_type.loc26 [concrete = constants.%Goat] // CHECK:STDOUT: %.loc26_8.2: ref %Goat = temporary %.loc26_6.2, %.loc26_8.1 // CHECK:STDOUT: %Bleet.ref.loc26: %Goat.Bleet.type = name_ref Bleet, @Goat.%Goat.Bleet.decl [concrete = constants.%Goat.Bleet] // CHECK:STDOUT: %Goat.Bleet.call.loc26: init %empty_tuple.type = call %Bleet.ref.loc26() // CHECK:STDOUT: %.loc27_6.1: %empty_struct_type = struct_literal () [concrete = constants.%empty_struct] // CHECK:STDOUT: %Goat.ref.loc27_11: type = name_ref Goat, file.%Goat.decl [concrete = constants.%Goat] // CHECK:STDOUT: %.loc27_6.2: ref %Goat = temporary_storage // CHECK:STDOUT: %.loc27_6.3: init %Goat to %.loc27_6.2 = class_init () [concrete = constants.%Goat.val] // CHECK:STDOUT: %.loc27_8.1: init %Goat = converted %.loc27_6.1, %.loc27_6.3 [concrete = constants.%Goat.val] // CHECK:STDOUT: %Goat.ref.loc27_21: type = name_ref Goat, file.%Goat.decl [concrete = constants.%Goat] // CHECK:STDOUT: %Animal.ref.loc27: type = name_ref Animal, file.%Animal.decl [concrete = constants.%Animal.type] // CHECK:STDOUT: %Animal.facet.loc27: %Animal.type = facet_value %Goat.ref.loc27_21, (constants.%Animal.impl_witness) [concrete = constants.%Animal.facet] // CHECK:STDOUT: %.loc27_26: %Animal.type = converted %Goat.ref.loc27_21, %Animal.facet.loc27 [concrete = constants.%Animal.facet] // CHECK:STDOUT: %as_type.loc27: type = facet_access_type %.loc27_26 [concrete = constants.%Goat] // CHECK:STDOUT: %.loc27_35: type = converted %.loc27_26, %as_type.loc27 [concrete = constants.%Goat] // CHECK:STDOUT: %.loc27_8.2: ref %Goat = temporary %.loc27_6.2, %.loc27_8.1 // CHECK:STDOUT: %Eat.ref.loc27: %Eats.assoc_type = name_ref Eat, @Eats.WithSelf.%assoc0 [concrete = constants.%assoc0.083] // CHECK:STDOUT: %impl.elem0.loc27: %.35f = impl_witness_access constants.%Eats.impl_witness, element0 [concrete = constants.%Goat.as.Eats.impl.Eat] // CHECK:STDOUT: %Goat.as.Eats.impl.Eat.call.loc27: init %empty_tuple.type = call %impl.elem0.loc27() // CHECK:STDOUT: %Destroy.Op.bound.loc27: = bound_method %.loc27_8.2, constants.%Destroy.Op // CHECK:STDOUT: %Destroy.Op.call.loc27: init %empty_tuple.type = call %Destroy.Op.bound.loc27(%.loc27_8.2) // CHECK:STDOUT: %Destroy.Op.bound.loc26: = bound_method %.loc26_8.2, constants.%Destroy.Op // CHECK:STDOUT: %Destroy.Op.call.loc26: init %empty_tuple.type = call %Destroy.Op.bound.loc26(%.loc26_8.2) // CHECK:STDOUT: %Destroy.Op.bound.loc22: = bound_method %.loc22_30.2, constants.%Destroy.Op // CHECK:STDOUT: %Destroy.Op.call.loc22: init %empty_tuple.type = call %Destroy.Op.bound.loc22(%.loc22_30.2) // CHECK:STDOUT: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @Destroy.Op(%self.param: ref %Goat) = "no_op"; // CHECK:STDOUT: // CHECK:STDOUT: --- facet_access_type_converts_back_to_original_facet_value.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %J.type: type = facet_type <@J> [concrete] // CHECK:STDOUT: %A: %J.type = symbolic_binding A, 0 [symbolic] // CHECK:STDOUT: %A.binding.as_type: type = symbolic_binding_type A, 0, %A [symbolic] // CHECK:STDOUT: %pattern_type.14f: type = pattern_type %A.binding.as_type [symbolic] // CHECK:STDOUT: %B: %A.binding.as_type = symbolic_binding B, 1 [symbolic] // CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] // CHECK:STDOUT: %C: type = class_type @C, @C(%A, %B) [symbolic] // CHECK:STDOUT: %pattern_type.e04: type = pattern_type %C [symbolic] // CHECK:STDOUT: %G.type: type = fn_type @G [concrete] // CHECK:STDOUT: %G: %G.type = struct_value () [concrete] // CHECK:STDOUT: %G.specific_fn: = specific_function %G, @G(%A, %B) [symbolic] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic fn @F(%A.loc25_6.2: %J.type, %B.loc25_13.2: @F.%A.binding.as_type (%A.binding.as_type)) { // CHECK:STDOUT: // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: // CHECK:STDOUT: %G.specific_fn.loc27_3.2: = specific_function constants.%G, @G(%A.loc25_6.1, %B.loc25_13.1) [symbolic = %G.specific_fn.loc27_3.2 (constants.%G.specific_fn)] // CHECK:STDOUT: // CHECK:STDOUT: fn(%x.param: @F.%C.loc25_29.1 (%C)) { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %G.ref: %G.type = name_ref G, file.%G.decl [concrete = constants.%G] // CHECK:STDOUT: %x.ref: @F.%C.loc25_29.1 (%C) = name_ref x, %x // CHECK:STDOUT: %.loc27: %J.type = converted constants.%A.binding.as_type, constants.%A [symbolic = %A.loc25_6.1 (constants.%A)] // CHECK:STDOUT: %G.specific_fn.loc27_3.1: = specific_function %G.ref, @G(constants.%A, constants.%B) [symbolic = %G.specific_fn.loc27_3.2 (constants.%G.specific_fn)] // CHECK:STDOUT: %G.call: init %empty_tuple.type = call %G.specific_fn.loc27_3.1(%x.ref) // CHECK:STDOUT: // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @F(constants.%A, constants.%B) { // CHECK:STDOUT: %A.loc25_6.1 => constants.%A // CHECK:STDOUT: %A.binding.as_type => constants.%A.binding.as_type // CHECK:STDOUT: %B.loc25_13.1 => constants.%B // CHECK:STDOUT: %pattern_type.loc25_13 => constants.%pattern_type.14f // CHECK:STDOUT: %C.loc25_29.1 => constants.%C // CHECK:STDOUT: %pattern_type.loc25_20 => constants.%pattern_type.e04 // CHECK:STDOUT: } // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/facet/convert_facet_value_to_facet_value.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/convert.carbon // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/facet/convert_facet_value_to_facet_value.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/facet/convert_facet_value_to_facet_value.carbon // --- convert_concrete_facet_value_to_facet_value.carbon library "[[@TEST_NAME]]"; interface Z {} interface Y {} class C {} impl C as Z {} impl C as Y {} fn G(unused z:! Z) {} fn F() { G(C); G(C as Y); G(C as Z); G(C as (Y & Z)); G(((((C as Y) as type) as Y) as type) as Y); G((((((C as Y) as type) as Y) as type) as Y) as type); } // --- convert_symbolic_facet_value_to_facet_value.carbon library "[[@TEST_NAME]]"; interface Z {} interface Y {} impl forall [T:! Y] T as Z {} fn F(U:! Y) { U as Z; } ================================================ FILE: toolchain/check/testdata/facet/convert_facet_value_to_itself.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // TODO: Add ranges and switch to "--dump-sem-ir-ranges=only". // EXTRA-ARGS: --dump-sem-ir-ranges=if-present // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/convert.carbon // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/facet/convert_facet_value_to_itself.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/facet/convert_facet_value_to_itself.carbon interface Animal {} fn FeedAnimal(unused T:! Animal) {} fn HandleAnimal(T:! Animal) { FeedAnimal(T); } class Goat {} impl Goat as Animal {} fn F() { HandleAnimal(Goat); } // CHECK:STDOUT: --- convert_facet_value_to_itself.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %Animal.type: type = facet_type <@Animal> [concrete] // CHECK:STDOUT: %Self: %Animal.type = symbolic_binding Self, 0 [symbolic] // CHECK:STDOUT: %type: type = facet_type [concrete] // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self] // CHECK:STDOUT: %pattern_type: type = pattern_type %Animal.type [concrete] // CHECK:STDOUT: %T.99815a.1: %Animal.type = symbolic_binding T, 0 [symbolic] // CHECK:STDOUT: %FeedAnimal.type: type = fn_type @FeedAnimal [concrete] // CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] // CHECK:STDOUT: %FeedAnimal: %FeedAnimal.type = struct_value () [concrete] // CHECK:STDOUT: %T.99815a.2: %Animal.type = symbolic_binding T, 0 [symbolic] // CHECK:STDOUT: %HandleAnimal.type: type = fn_type @HandleAnimal [concrete] // CHECK:STDOUT: %HandleAnimal: %HandleAnimal.type = struct_value () [concrete] // CHECK:STDOUT: %FeedAnimal.specific_fn.ae9: = specific_function %FeedAnimal, @FeedAnimal(%T.99815a.2) [symbolic] // CHECK:STDOUT: %Goat: type = class_type @Goat [concrete] // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %complete_type: = complete_type_witness %empty_struct_type [concrete] // CHECK:STDOUT: %Animal.impl_witness: = impl_witness @Goat.as.Animal.impl.%Animal.impl_witness_table [concrete] // CHECK:STDOUT: %Animal.facet: %Animal.type = facet_value %Goat, (%Animal.impl_witness) [concrete] // CHECK:STDOUT: %F.type: type = fn_type @F [concrete] // CHECK:STDOUT: %F: %F.type = struct_value () [concrete] // CHECK:STDOUT: %HandleAnimal.specific_fn: = specific_function %HandleAnimal, @HandleAnimal(%Animal.facet) [concrete] // CHECK:STDOUT: %FeedAnimal.specific_fn.cc5: = specific_function %FeedAnimal, @FeedAnimal(%Animal.facet) [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .Animal = %Animal.decl // CHECK:STDOUT: .FeedAnimal = %FeedAnimal.decl // CHECK:STDOUT: .HandleAnimal = %HandleAnimal.decl // CHECK:STDOUT: .Goat = %Goat.decl // CHECK:STDOUT: .F = %F.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %Animal.decl: type = interface_decl @Animal [concrete = constants.%Animal.type] {} {} // CHECK:STDOUT: %FeedAnimal.decl: %FeedAnimal.type = fn_decl @FeedAnimal [concrete = constants.%FeedAnimal] { // CHECK:STDOUT: %T.patt: %pattern_type = symbolic_binding_pattern T, 0 [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %.loc17: type = splice_block %Animal.ref [concrete = constants.%Animal.type] { // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %Animal.ref: type = name_ref Animal, file.%Animal.decl [concrete = constants.%Animal.type] // CHECK:STDOUT: } // CHECK:STDOUT: %T.loc17_22.2: %Animal.type = symbolic_binding T, 0 [symbolic = %T.loc17_22.1 (constants.%T.99815a.1)] // CHECK:STDOUT: } // CHECK:STDOUT: %HandleAnimal.decl: %HandleAnimal.type = fn_decl @HandleAnimal [concrete = constants.%HandleAnimal] { // CHECK:STDOUT: %T.patt: %pattern_type = symbolic_binding_pattern T, 0 [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %.loc19: type = splice_block %Animal.ref [concrete = constants.%Animal.type] { // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %Animal.ref: type = name_ref Animal, file.%Animal.decl [concrete = constants.%Animal.type] // CHECK:STDOUT: } // CHECK:STDOUT: %T.loc19_17.2: %Animal.type = symbolic_binding T, 0 [symbolic = %T.loc19_17.1 (constants.%T.99815a.2)] // CHECK:STDOUT: } // CHECK:STDOUT: %Goat.decl: type = class_decl @Goat [concrete = constants.%Goat] {} {} // CHECK:STDOUT: impl_decl @Goat.as.Animal.impl [concrete] {} { // CHECK:STDOUT: %Goat.ref: type = name_ref Goat, file.%Goat.decl [concrete = constants.%Goat] // CHECK:STDOUT: %Animal.ref: type = name_ref Animal, file.%Animal.decl [concrete = constants.%Animal.type] // CHECK:STDOUT: } // CHECK:STDOUT: %F.decl: %F.type = fn_decl @F [concrete = constants.%F] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: interface @Animal { // CHECK:STDOUT: %Self: %Animal.type = symbolic_binding Self, 0 [symbolic = constants.%Self] // CHECK:STDOUT: %Animal.WithSelf.decl = interface_with_self_decl @Animal [concrete] // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = %Self // CHECK:STDOUT: witness = () // CHECK:STDOUT: // CHECK:STDOUT: !requires: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: impl @Goat.as.Animal.impl: %Goat.ref as %Animal.ref { // CHECK:STDOUT: %Animal.impl_witness_table = impl_witness_table (), @Goat.as.Animal.impl [concrete] // CHECK:STDOUT: %Animal.impl_witness: = impl_witness %Animal.impl_witness_table [concrete = constants.%Animal.impl_witness] // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: witness = %Animal.impl_witness // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @Goat { // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%Goat // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic fn @FeedAnimal(%T.loc17_22.2: %Animal.type) { // CHECK:STDOUT: %T.loc17_22.1: %Animal.type = symbolic_binding T, 0 [symbolic = %T.loc17_22.1 (constants.%T.99815a.1)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: // CHECK:STDOUT: fn() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic fn @HandleAnimal(%T.loc19_17.2: %Animal.type) { // CHECK:STDOUT: %T.loc19_17.1: %Animal.type = symbolic_binding T, 0 [symbolic = %T.loc19_17.1 (constants.%T.99815a.2)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %FeedAnimal.specific_fn.loc19_31.2: = specific_function constants.%FeedAnimal, @FeedAnimal(%T.loc19_17.1) [symbolic = %FeedAnimal.specific_fn.loc19_31.2 (constants.%FeedAnimal.specific_fn.ae9)] // CHECK:STDOUT: // CHECK:STDOUT: fn() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %FeedAnimal.ref: %FeedAnimal.type = name_ref FeedAnimal, file.%FeedAnimal.decl [concrete = constants.%FeedAnimal] // CHECK:STDOUT: %T.ref: %Animal.type = name_ref T, %T.loc19_17.2 [symbolic = %T.loc19_17.1 (constants.%T.99815a.2)] // CHECK:STDOUT: %FeedAnimal.specific_fn.loc19_31.1: = specific_function %FeedAnimal.ref, @FeedAnimal(constants.%T.99815a.2) [symbolic = %FeedAnimal.specific_fn.loc19_31.2 (constants.%FeedAnimal.specific_fn.ae9)] // CHECK:STDOUT: %FeedAnimal.call: init %empty_tuple.type = call %FeedAnimal.specific_fn.loc19_31.1() // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @F() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %HandleAnimal.ref: %HandleAnimal.type = name_ref HandleAnimal, file.%HandleAnimal.decl [concrete = constants.%HandleAnimal] // CHECK:STDOUT: %Goat.ref: type = name_ref Goat, file.%Goat.decl [concrete = constants.%Goat] // CHECK:STDOUT: %Animal.facet: %Animal.type = facet_value %Goat.ref, (constants.%Animal.impl_witness) [concrete = constants.%Animal.facet] // CHECK:STDOUT: %.loc25: %Animal.type = converted %Goat.ref, %Animal.facet [concrete = constants.%Animal.facet] // CHECK:STDOUT: %HandleAnimal.specific_fn: = specific_function %HandleAnimal.ref, @HandleAnimal(constants.%Animal.facet) [concrete = constants.%HandleAnimal.specific_fn] // CHECK:STDOUT: %HandleAnimal.call: init %empty_tuple.type = call %HandleAnimal.specific_fn() // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @Animal.WithSelf(constants.%Self) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @FeedAnimal(constants.%T.99815a.1) { // CHECK:STDOUT: %T.loc17_22.1 => constants.%T.99815a.1 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @HandleAnimal(constants.%T.99815a.2) { // CHECK:STDOUT: %T.loc19_17.1 => constants.%T.99815a.2 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @FeedAnimal(constants.%T.99815a.2) { // CHECK:STDOUT: %T.loc17_22.1 => constants.%T.99815a.2 // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @Animal.WithSelf(constants.%Animal.facet) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @HandleAnimal(constants.%Animal.facet) { // CHECK:STDOUT: %T.loc19_17.1 => constants.%Animal.facet // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %FeedAnimal.specific_fn.loc19_31.2 => constants.%FeedAnimal.specific_fn.cc5 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @FeedAnimal(constants.%Animal.facet) { // CHECK:STDOUT: %T.loc17_22.1 => constants.%Animal.facet // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: } // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/facet/convert_facet_value_to_narrowed_facet_type.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // TODO: Add ranges and switch to "--dump-sem-ir-ranges=only". // EXTRA-ARGS: --dump-sem-ir-ranges=if-present // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/convert.carbon // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/facet/convert_facet_value_to_narrowed_facet_type.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/facet/convert_facet_value_to_narrowed_facet_type.carbon // --- convert_to_narrowed_facet_type.carbon library "[[@TEST_NAME]]"; interface Eats {} interface Animal {} fn Feed[T:! Eats](unused e: T) {} fn HandleAnimal[U:! Animal & Eats](a: U) { Feed(a); } // --- bigger.carbon library "[[@TEST_NAME]]"; interface Eats {} interface Animal {} interface Tame {} fn FeedTame[V:! Tame & Eats](unused v: V) {} fn HandleTameAnimal[W:! Eats & Animal & Tame](w: W) { FeedTame(w); } // --- with_blanket.carbon library "[[@TEST_NAME]]"; interface Eats {} interface Animal {} interface Tame {} impl forall [A:! Animal] A as Eats {} fn FeedTame2[V:! Tame & Eats](unused v: V) {} fn HandleTameAnimal2[W:! Animal & Tame](w: W) { FeedTame2(w); } // --- equivalent.carbon library "[[@TEST_NAME]]"; // This is testing `FindWitnessInFacet` from impl_lookup.cpp interface A {} fn TakesA[T:! A](unused x: T) {} fn WithExtraWhere[U:! A where .Self impls type](y: U) { TakesA(y); } // --- no_interfaces_success.carbon library "[[@TEST_NAME]]"; fn TakesTypeDeduced[T:! type](unused x: T) {} fn CallsWithExtraWhere[U:! type where .Self impls type](y: U) { TakesTypeDeduced(y); } fn TakesTypeExplicit(unused T:! type) {} fn CallsWithExtraWhereExplicit(U:! type where .Self impls type) { TakesTypeExplicit(U); } // --- no_interfaces.carbon library "[[@TEST_NAME]]"; fn TakesExtraWhereDeduced[T:! type where .Self impls type](unused x: T) {} fn CallsWithType[U:! type](y: U) { TakesExtraWhereDeduced(y); } fn TakesExtraWhereExplicit(unused T:! type where .Self impls type) {} fn CallsWithTypeExplicit(U:! type) { TakesExtraWhereExplicit(U); } // CHECK:STDOUT: --- convert_to_narrowed_facet_type.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %Eats.type: type = facet_type <@Eats> [concrete] // CHECK:STDOUT: %Self.247: %Eats.type = symbolic_binding Self, 0 [symbolic] // CHECK:STDOUT: %Animal.type: type = facet_type <@Animal> [concrete] // CHECK:STDOUT: %Self.c49: %Animal.type = symbolic_binding Self, 0 [symbolic] // CHECK:STDOUT: %type: type = facet_type [concrete] // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self] // CHECK:STDOUT: %pattern_type.0dc: type = pattern_type %Eats.type [concrete] // CHECK:STDOUT: %T: %Eats.type = symbolic_binding T, 0 [symbolic] // CHECK:STDOUT: %T.binding.as_type: type = symbolic_binding_type T, 0, %T [symbolic] // CHECK:STDOUT: %pattern_type.93e: type = pattern_type %T.binding.as_type [symbolic] // CHECK:STDOUT: %Feed.type: type = fn_type @Feed [concrete] // CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] // CHECK:STDOUT: %Feed: %Feed.type = struct_value () [concrete] // CHECK:STDOUT: %require_complete.9d9: = require_complete_type %T.binding.as_type [symbolic] // CHECK:STDOUT: %BitAndWith.type.f2e: type = generic_interface_type @BitAndWith [concrete] // CHECK:STDOUT: %BitAndWith.generic: %BitAndWith.type.f2e = struct_value () [concrete] // CHECK:STDOUT: %BitAndWith.type.b10: type = facet_type <@BitAndWith, @BitAndWith(type)> [concrete] // CHECK:STDOUT: %BitAndWith.impl_witness: = impl_witness imports.%BitAndWith.impl_witness_table [concrete] // CHECK:STDOUT: %BitAndWith.facet: %BitAndWith.type.b10 = facet_value type, (%BitAndWith.impl_witness) [concrete] // CHECK:STDOUT: %BitAndWith.WithSelf.Op.type.4bd: type = fn_type @BitAndWith.WithSelf.Op, @BitAndWith.WithSelf(type, %BitAndWith.facet) [concrete] // CHECK:STDOUT: %.d15: type = fn_type_with_self_type %BitAndWith.WithSelf.Op.type.4bd, %BitAndWith.facet [concrete] // CHECK:STDOUT: %type.as.BitAndWith.impl.Op.type: type = fn_type @type.as.BitAndWith.impl.Op [concrete] // CHECK:STDOUT: %type.as.BitAndWith.impl.Op: %type.as.BitAndWith.impl.Op.type = struct_value () [concrete] // CHECK:STDOUT: %type.as.BitAndWith.impl.Op.bound: = bound_method %Animal.type, %type.as.BitAndWith.impl.Op [concrete] // CHECK:STDOUT: %facet_type: type = facet_type <@Eats & @Animal> [concrete] // CHECK:STDOUT: %pattern_type.37f: type = pattern_type %facet_type [concrete] // CHECK:STDOUT: %U: %facet_type = symbolic_binding U, 0 [symbolic] // CHECK:STDOUT: %U.binding.as_type: type = symbolic_binding_type U, 0, %U [symbolic] // CHECK:STDOUT: %pattern_type.256: type = pattern_type %U.binding.as_type [symbolic] // CHECK:STDOUT: %HandleAnimal.type: type = fn_type @HandleAnimal [concrete] // CHECK:STDOUT: %HandleAnimal: %HandleAnimal.type = struct_value () [concrete] // CHECK:STDOUT: %require_complete.ff4: = require_complete_type %U.binding.as_type [symbolic] // CHECK:STDOUT: %Eats.lookup_impl_witness: = lookup_impl_witness %U, @Eats [symbolic] // CHECK:STDOUT: %Eats.facet: %Eats.type = facet_value %U.binding.as_type, (%Eats.lookup_impl_witness) [symbolic] // CHECK:STDOUT: %Feed.specific_fn: = specific_function %Feed, @Feed(%Eats.facet) [symbolic] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: .BitAndWith = %Core.BitAndWith // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: %Core.BitAndWith: %BitAndWith.type.f2e = import_ref Core//prelude/parts/as, BitAndWith, loaded [concrete = constants.%BitAndWith.generic] // CHECK:STDOUT: %Core.import_ref.8d3: %type.as.BitAndWith.impl.Op.type = import_ref Core//prelude/parts/as, loc{{\d+_\d+}}, loaded [concrete = constants.%type.as.BitAndWith.impl.Op] // CHECK:STDOUT: %BitAndWith.impl_witness_table = impl_witness_table (%Core.import_ref.8d3), @type.as.BitAndWith.impl [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .Eats = %Eats.decl // CHECK:STDOUT: .Animal = %Animal.decl // CHECK:STDOUT: .Feed = %Feed.decl // CHECK:STDOUT: .HandleAnimal = %HandleAnimal.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %Eats.decl: type = interface_decl @Eats [concrete = constants.%Eats.type] {} {} // CHECK:STDOUT: %Animal.decl: type = interface_decl @Animal [concrete = constants.%Animal.type] {} {} // CHECK:STDOUT: %Feed.decl: %Feed.type = fn_decl @Feed [concrete = constants.%Feed] { // CHECK:STDOUT: %T.patt: %pattern_type.0dc = symbolic_binding_pattern T, 0 [concrete] // CHECK:STDOUT: %e.patt: @Feed.%pattern_type (%pattern_type.93e) = value_binding_pattern e [concrete] // CHECK:STDOUT: %e.param_patt: @Feed.%pattern_type (%pattern_type.93e) = value_param_pattern %e.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %.loc6_13: type = splice_block %Eats.ref [concrete = constants.%Eats.type] { // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %Eats.ref: type = name_ref Eats, file.%Eats.decl [concrete = constants.%Eats.type] // CHECK:STDOUT: } // CHECK:STDOUT: %T.loc6_9.2: %Eats.type = symbolic_binding T, 0 [symbolic = %T.loc6_9.1 (constants.%T)] // CHECK:STDOUT: %e.param: @Feed.%T.binding.as_type (%T.binding.as_type) = value_param call_param0 // CHECK:STDOUT: %.loc6_29.1: type = splice_block %.loc6_29.2 [symbolic = %T.binding.as_type (constants.%T.binding.as_type)] { // CHECK:STDOUT: %T.ref: %Eats.type = name_ref T, %T.loc6_9.2 [symbolic = %T.loc6_9.1 (constants.%T)] // CHECK:STDOUT: %T.as_type: type = facet_access_type %T.ref [symbolic = %T.binding.as_type (constants.%T.binding.as_type)] // CHECK:STDOUT: %.loc6_29.2: type = converted %T.ref, %T.as_type [symbolic = %T.binding.as_type (constants.%T.binding.as_type)] // CHECK:STDOUT: } // CHECK:STDOUT: %e: @Feed.%T.binding.as_type (%T.binding.as_type) = value_binding e, %e.param // CHECK:STDOUT: } // CHECK:STDOUT: %HandleAnimal.decl: %HandleAnimal.type = fn_decl @HandleAnimal [concrete = constants.%HandleAnimal] { // CHECK:STDOUT: %U.patt: %pattern_type.37f = symbolic_binding_pattern U, 0 [concrete] // CHECK:STDOUT: %a.patt: @HandleAnimal.%pattern_type (%pattern_type.256) = value_binding_pattern a [concrete] // CHECK:STDOUT: %a.param_patt: @HandleAnimal.%pattern_type (%pattern_type.256) = value_param_pattern %a.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %.loc8_28.1: type = splice_block %.loc8_28.3 [concrete = constants.%facet_type] { // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %Animal.ref: type = name_ref Animal, file.%Animal.decl [concrete = constants.%Animal.type] // CHECK:STDOUT: %Eats.ref: type = name_ref Eats, file.%Eats.decl [concrete = constants.%Eats.type] // CHECK:STDOUT: %impl.elem0: %.d15 = impl_witness_access constants.%BitAndWith.impl_witness, element0 [concrete = constants.%type.as.BitAndWith.impl.Op] // CHECK:STDOUT: %bound_method: = bound_method %Animal.ref, %impl.elem0 [concrete = constants.%type.as.BitAndWith.impl.Op.bound] // CHECK:STDOUT: %type.as.BitAndWith.impl.Op.call: init type = call %bound_method(%Animal.ref, %Eats.ref) [concrete = constants.%facet_type] // CHECK:STDOUT: %.loc8_28.2: type = value_of_initializer %type.as.BitAndWith.impl.Op.call [concrete = constants.%facet_type] // CHECK:STDOUT: %.loc8_28.3: type = converted %type.as.BitAndWith.impl.Op.call, %.loc8_28.2 [concrete = constants.%facet_type] // CHECK:STDOUT: } // CHECK:STDOUT: %U.loc8_17.2: %facet_type = symbolic_binding U, 0 [symbolic = %U.loc8_17.1 (constants.%U)] // CHECK:STDOUT: %a.param: @HandleAnimal.%U.binding.as_type (%U.binding.as_type) = value_param call_param0 // CHECK:STDOUT: %.loc8_39.1: type = splice_block %.loc8_39.2 [symbolic = %U.binding.as_type (constants.%U.binding.as_type)] { // CHECK:STDOUT: %U.ref: %facet_type = name_ref U, %U.loc8_17.2 [symbolic = %U.loc8_17.1 (constants.%U)] // CHECK:STDOUT: %U.as_type: type = facet_access_type %U.ref [symbolic = %U.binding.as_type (constants.%U.binding.as_type)] // CHECK:STDOUT: %.loc8_39.2: type = converted %U.ref, %U.as_type [symbolic = %U.binding.as_type (constants.%U.binding.as_type)] // CHECK:STDOUT: } // CHECK:STDOUT: %a: @HandleAnimal.%U.binding.as_type (%U.binding.as_type) = value_binding a, %a.param // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: interface @Eats { // CHECK:STDOUT: %Self: %Eats.type = symbolic_binding Self, 0 [symbolic = constants.%Self.247] // CHECK:STDOUT: %Eats.WithSelf.decl = interface_with_self_decl @Eats [concrete] // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = %Self // CHECK:STDOUT: witness = () // CHECK:STDOUT: // CHECK:STDOUT: !requires: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: interface @Animal { // CHECK:STDOUT: %Self: %Animal.type = symbolic_binding Self, 0 [symbolic = constants.%Self.c49] // CHECK:STDOUT: %Animal.WithSelf.decl = interface_with_self_decl @Animal [concrete] // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = %Self // CHECK:STDOUT: witness = () // CHECK:STDOUT: // CHECK:STDOUT: !requires: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic fn @Feed(%T.loc6_9.2: %Eats.type) { // CHECK:STDOUT: %T.loc6_9.1: %Eats.type = symbolic_binding T, 0 [symbolic = %T.loc6_9.1 (constants.%T)] // CHECK:STDOUT: %T.binding.as_type: type = symbolic_binding_type T, 0, %T.loc6_9.1 [symbolic = %T.binding.as_type (constants.%T.binding.as_type)] // CHECK:STDOUT: %pattern_type: type = pattern_type %T.binding.as_type [symbolic = %pattern_type (constants.%pattern_type.93e)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete: = require_complete_type %T.binding.as_type [symbolic = %require_complete (constants.%require_complete.9d9)] // CHECK:STDOUT: // CHECK:STDOUT: fn(%e.param: @Feed.%T.binding.as_type (%T.binding.as_type)) { // CHECK:STDOUT: !entry: // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic fn @HandleAnimal(%U.loc8_17.2: %facet_type) { // CHECK:STDOUT: %U.loc8_17.1: %facet_type = symbolic_binding U, 0 [symbolic = %U.loc8_17.1 (constants.%U)] // CHECK:STDOUT: %U.binding.as_type: type = symbolic_binding_type U, 0, %U.loc8_17.1 [symbolic = %U.binding.as_type (constants.%U.binding.as_type)] // CHECK:STDOUT: %pattern_type: type = pattern_type %U.binding.as_type [symbolic = %pattern_type (constants.%pattern_type.256)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete: = require_complete_type %U.binding.as_type [symbolic = %require_complete (constants.%require_complete.ff4)] // CHECK:STDOUT: %Eats.lookup_impl_witness: = lookup_impl_witness %U.loc8_17.1, @Eats [symbolic = %Eats.lookup_impl_witness (constants.%Eats.lookup_impl_witness)] // CHECK:STDOUT: %Eats.facet.loc8_50.2: %Eats.type = facet_value %U.binding.as_type, (%Eats.lookup_impl_witness) [symbolic = %Eats.facet.loc8_50.2 (constants.%Eats.facet)] // CHECK:STDOUT: %Feed.specific_fn.loc8_44.2: = specific_function constants.%Feed, @Feed(%Eats.facet.loc8_50.2) [symbolic = %Feed.specific_fn.loc8_44.2 (constants.%Feed.specific_fn)] // CHECK:STDOUT: // CHECK:STDOUT: fn(%a.param: @HandleAnimal.%U.binding.as_type (%U.binding.as_type)) { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %Feed.ref: %Feed.type = name_ref Feed, file.%Feed.decl [concrete = constants.%Feed] // CHECK:STDOUT: %a.ref: @HandleAnimal.%U.binding.as_type (%U.binding.as_type) = name_ref a, %a // CHECK:STDOUT: %Eats.facet.loc8_50.1: %Eats.type = facet_value constants.%U.binding.as_type, (constants.%Eats.lookup_impl_witness) [symbolic = %Eats.facet.loc8_50.2 (constants.%Eats.facet)] // CHECK:STDOUT: %.loc8_50: %Eats.type = converted constants.%U.binding.as_type, %Eats.facet.loc8_50.1 [symbolic = %Eats.facet.loc8_50.2 (constants.%Eats.facet)] // CHECK:STDOUT: %Feed.specific_fn.loc8_44.1: = specific_function %Feed.ref, @Feed(constants.%Eats.facet) [symbolic = %Feed.specific_fn.loc8_44.2 (constants.%Feed.specific_fn)] // CHECK:STDOUT: %Feed.call: init %empty_tuple.type = call %Feed.specific_fn.loc8_44.1(%a.ref) // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @Eats.WithSelf(constants.%Self.247) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @Animal.WithSelf(constants.%Self.c49) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @Feed(constants.%T) { // CHECK:STDOUT: %T.loc6_9.1 => constants.%T // CHECK:STDOUT: %T.binding.as_type => constants.%T.binding.as_type // CHECK:STDOUT: %pattern_type => constants.%pattern_type.93e // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @HandleAnimal(constants.%U) { // CHECK:STDOUT: %U.loc8_17.1 => constants.%U // CHECK:STDOUT: %U.binding.as_type => constants.%U.binding.as_type // CHECK:STDOUT: %pattern_type => constants.%pattern_type.256 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @Feed(constants.%Eats.facet) { // CHECK:STDOUT: %T.loc6_9.1 => constants.%Eats.facet // CHECK:STDOUT: %T.binding.as_type => constants.%U.binding.as_type // CHECK:STDOUT: %pattern_type => constants.%pattern_type.256 // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete => constants.%require_complete.ff4 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- bigger.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %Eats.type: type = facet_type <@Eats> [concrete] // CHECK:STDOUT: %Self.247: %Eats.type = symbolic_binding Self, 0 [symbolic] // CHECK:STDOUT: %Animal.type: type = facet_type <@Animal> [concrete] // CHECK:STDOUT: %Self.c49: %Animal.type = symbolic_binding Self, 0 [symbolic] // CHECK:STDOUT: %Tame.type: type = facet_type <@Tame> [concrete] // CHECK:STDOUT: %Self.ab8: %Tame.type = symbolic_binding Self, 0 [symbolic] // CHECK:STDOUT: %type: type = facet_type [concrete] // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self] // CHECK:STDOUT: %BitAndWith.type.f2e: type = generic_interface_type @BitAndWith [concrete] // CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] // CHECK:STDOUT: %BitAndWith.generic: %BitAndWith.type.f2e = struct_value () [concrete] // CHECK:STDOUT: %BitAndWith.type.b10: type = facet_type <@BitAndWith, @BitAndWith(type)> [concrete] // CHECK:STDOUT: %BitAndWith.impl_witness: = impl_witness imports.%BitAndWith.impl_witness_table [concrete] // CHECK:STDOUT: %BitAndWith.facet: %BitAndWith.type.b10 = facet_value type, (%BitAndWith.impl_witness) [concrete] // CHECK:STDOUT: %BitAndWith.WithSelf.Op.type.4bd: type = fn_type @BitAndWith.WithSelf.Op, @BitAndWith.WithSelf(type, %BitAndWith.facet) [concrete] // CHECK:STDOUT: %.d15: type = fn_type_with_self_type %BitAndWith.WithSelf.Op.type.4bd, %BitAndWith.facet [concrete] // CHECK:STDOUT: %type.as.BitAndWith.impl.Op.type: type = fn_type @type.as.BitAndWith.impl.Op [concrete] // CHECK:STDOUT: %type.as.BitAndWith.impl.Op: %type.as.BitAndWith.impl.Op.type = struct_value () [concrete] // CHECK:STDOUT: %type.as.BitAndWith.impl.Op.bound.a5f: = bound_method %Tame.type, %type.as.BitAndWith.impl.Op [concrete] // CHECK:STDOUT: %facet_type.2db: type = facet_type <@Eats & @Tame> [concrete] // CHECK:STDOUT: %pattern_type.fa7: type = pattern_type %facet_type.2db [concrete] // CHECK:STDOUT: %V: %facet_type.2db = symbolic_binding V, 0 [symbolic] // CHECK:STDOUT: %V.binding.as_type: type = symbolic_binding_type V, 0, %V [symbolic] // CHECK:STDOUT: %pattern_type.795: type = pattern_type %V.binding.as_type [symbolic] // CHECK:STDOUT: %FeedTame.type: type = fn_type @FeedTame [concrete] // CHECK:STDOUT: %FeedTame: %FeedTame.type = struct_value () [concrete] // CHECK:STDOUT: %require_complete.12d: = require_complete_type %V.binding.as_type [symbolic] // CHECK:STDOUT: %type.as.BitAndWith.impl.Op.bound.10c: = bound_method %Eats.type, %type.as.BitAndWith.impl.Op [concrete] // CHECK:STDOUT: %facet_type.a63: type = facet_type <@Eats & @Animal> [concrete] // CHECK:STDOUT: %type.as.BitAndWith.impl.Op.bound.986: = bound_method %facet_type.a63, %type.as.BitAndWith.impl.Op [concrete] // CHECK:STDOUT: %facet_type.206: type = facet_type <@Eats & @Animal & @Tame> [concrete] // CHECK:STDOUT: %pattern_type.fb4: type = pattern_type %facet_type.206 [concrete] // CHECK:STDOUT: %W: %facet_type.206 = symbolic_binding W, 0 [symbolic] // CHECK:STDOUT: %W.binding.as_type: type = symbolic_binding_type W, 0, %W [symbolic] // CHECK:STDOUT: %pattern_type.e8e: type = pattern_type %W.binding.as_type [symbolic] // CHECK:STDOUT: %HandleTameAnimal.type: type = fn_type @HandleTameAnimal [concrete] // CHECK:STDOUT: %HandleTameAnimal: %HandleTameAnimal.type = struct_value () [concrete] // CHECK:STDOUT: %require_complete.817: = require_complete_type %W.binding.as_type [symbolic] // CHECK:STDOUT: %Eats.lookup_impl_witness: = lookup_impl_witness %W, @Eats [symbolic] // CHECK:STDOUT: %Tame.lookup_impl_witness: = lookup_impl_witness %W, @Tame [symbolic] // CHECK:STDOUT: %facet_value: %facet_type.2db = facet_value %W.binding.as_type, (%Eats.lookup_impl_witness, %Tame.lookup_impl_witness) [symbolic] // CHECK:STDOUT: %FeedTame.specific_fn: = specific_function %FeedTame, @FeedTame(%facet_value) [symbolic] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: .BitAndWith = %Core.BitAndWith // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: %Core.BitAndWith: %BitAndWith.type.f2e = import_ref Core//prelude/parts/as, BitAndWith, loaded [concrete = constants.%BitAndWith.generic] // CHECK:STDOUT: %Core.import_ref.8d3: %type.as.BitAndWith.impl.Op.type = import_ref Core//prelude/parts/as, loc{{\d+_\d+}}, loaded [concrete = constants.%type.as.BitAndWith.impl.Op] // CHECK:STDOUT: %BitAndWith.impl_witness_table = impl_witness_table (%Core.import_ref.8d3), @type.as.BitAndWith.impl [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .Eats = %Eats.decl // CHECK:STDOUT: .Animal = %Animal.decl // CHECK:STDOUT: .Tame = %Tame.decl // CHECK:STDOUT: .FeedTame = %FeedTame.decl // CHECK:STDOUT: .HandleTameAnimal = %HandleTameAnimal.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %Eats.decl: type = interface_decl @Eats [concrete = constants.%Eats.type] {} {} // CHECK:STDOUT: %Animal.decl: type = interface_decl @Animal [concrete = constants.%Animal.type] {} {} // CHECK:STDOUT: %Tame.decl: type = interface_decl @Tame [concrete = constants.%Tame.type] {} {} // CHECK:STDOUT: %FeedTame.decl: %FeedTame.type = fn_decl @FeedTame [concrete = constants.%FeedTame] { // CHECK:STDOUT: %V.patt: %pattern_type.fa7 = symbolic_binding_pattern V, 0 [concrete] // CHECK:STDOUT: %v.patt: @FeedTame.%pattern_type (%pattern_type.795) = value_binding_pattern v [concrete] // CHECK:STDOUT: %v.param_patt: @FeedTame.%pattern_type (%pattern_type.795) = value_param_pattern %v.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %.loc7_22.1: type = splice_block %.loc7_22.3 [concrete = constants.%facet_type.2db] { // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %Tame.ref: type = name_ref Tame, file.%Tame.decl [concrete = constants.%Tame.type] // CHECK:STDOUT: %Eats.ref: type = name_ref Eats, file.%Eats.decl [concrete = constants.%Eats.type] // CHECK:STDOUT: %impl.elem0: %.d15 = impl_witness_access constants.%BitAndWith.impl_witness, element0 [concrete = constants.%type.as.BitAndWith.impl.Op] // CHECK:STDOUT: %bound_method: = bound_method %Tame.ref, %impl.elem0 [concrete = constants.%type.as.BitAndWith.impl.Op.bound.a5f] // CHECK:STDOUT: %type.as.BitAndWith.impl.Op.call: init type = call %bound_method(%Tame.ref, %Eats.ref) [concrete = constants.%facet_type.2db] // CHECK:STDOUT: %.loc7_22.2: type = value_of_initializer %type.as.BitAndWith.impl.Op.call [concrete = constants.%facet_type.2db] // CHECK:STDOUT: %.loc7_22.3: type = converted %type.as.BitAndWith.impl.Op.call, %.loc7_22.2 [concrete = constants.%facet_type.2db] // CHECK:STDOUT: } // CHECK:STDOUT: %V.loc7_13.2: %facet_type.2db = symbolic_binding V, 0 [symbolic = %V.loc7_13.1 (constants.%V)] // CHECK:STDOUT: %v.param: @FeedTame.%V.binding.as_type (%V.binding.as_type) = value_param call_param0 // CHECK:STDOUT: %.loc7_40.1: type = splice_block %.loc7_40.2 [symbolic = %V.binding.as_type (constants.%V.binding.as_type)] { // CHECK:STDOUT: %V.ref: %facet_type.2db = name_ref V, %V.loc7_13.2 [symbolic = %V.loc7_13.1 (constants.%V)] // CHECK:STDOUT: %V.as_type: type = facet_access_type %V.ref [symbolic = %V.binding.as_type (constants.%V.binding.as_type)] // CHECK:STDOUT: %.loc7_40.2: type = converted %V.ref, %V.as_type [symbolic = %V.binding.as_type (constants.%V.binding.as_type)] // CHECK:STDOUT: } // CHECK:STDOUT: %v: @FeedTame.%V.binding.as_type (%V.binding.as_type) = value_binding v, %v.param // CHECK:STDOUT: } // CHECK:STDOUT: %HandleTameAnimal.decl: %HandleTameAnimal.type = fn_decl @HandleTameAnimal [concrete = constants.%HandleTameAnimal] { // CHECK:STDOUT: %W.patt: %pattern_type.fb4 = symbolic_binding_pattern W, 0 [concrete] // CHECK:STDOUT: %w.patt: @HandleTameAnimal.%pattern_type (%pattern_type.e8e) = value_binding_pattern w [concrete] // CHECK:STDOUT: %w.param_patt: @HandleTameAnimal.%pattern_type (%pattern_type.e8e) = value_param_pattern %w.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %.loc9_39.1: type = splice_block %.loc9_39.3 [concrete = constants.%facet_type.206] { // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %Eats.ref: type = name_ref Eats, file.%Eats.decl [concrete = constants.%Eats.type] // CHECK:STDOUT: %Animal.ref: type = name_ref Animal, file.%Animal.decl [concrete = constants.%Animal.type] // CHECK:STDOUT: %impl.elem0.loc9_30: %.d15 = impl_witness_access constants.%BitAndWith.impl_witness, element0 [concrete = constants.%type.as.BitAndWith.impl.Op] // CHECK:STDOUT: %bound_method.loc9_30: = bound_method %Eats.ref, %impl.elem0.loc9_30 [concrete = constants.%type.as.BitAndWith.impl.Op.bound.10c] // CHECK:STDOUT: %type.as.BitAndWith.impl.Op.call.loc9_30: init type = call %bound_method.loc9_30(%Eats.ref, %Animal.ref) [concrete = constants.%facet_type.a63] // CHECK:STDOUT: %Tame.ref: type = name_ref Tame, file.%Tame.decl [concrete = constants.%Tame.type] // CHECK:STDOUT: %impl.elem0.loc9_39: %.d15 = impl_witness_access constants.%BitAndWith.impl_witness, element0 [concrete = constants.%type.as.BitAndWith.impl.Op] // CHECK:STDOUT: %bound_method.loc9_39: = bound_method %type.as.BitAndWith.impl.Op.call.loc9_30, %impl.elem0.loc9_39 [concrete = constants.%type.as.BitAndWith.impl.Op.bound.986] // CHECK:STDOUT: %.loc9_30.1: type = value_of_initializer %type.as.BitAndWith.impl.Op.call.loc9_30 [concrete = constants.%facet_type.a63] // CHECK:STDOUT: %.loc9_30.2: type = converted %type.as.BitAndWith.impl.Op.call.loc9_30, %.loc9_30.1 [concrete = constants.%facet_type.a63] // CHECK:STDOUT: %type.as.BitAndWith.impl.Op.call.loc9_39: init type = call %bound_method.loc9_39(%.loc9_30.2, %Tame.ref) [concrete = constants.%facet_type.206] // CHECK:STDOUT: %.loc9_39.2: type = value_of_initializer %type.as.BitAndWith.impl.Op.call.loc9_39 [concrete = constants.%facet_type.206] // CHECK:STDOUT: %.loc9_39.3: type = converted %type.as.BitAndWith.impl.Op.call.loc9_39, %.loc9_39.2 [concrete = constants.%facet_type.206] // CHECK:STDOUT: } // CHECK:STDOUT: %W.loc9_21.2: %facet_type.206 = symbolic_binding W, 0 [symbolic = %W.loc9_21.1 (constants.%W)] // CHECK:STDOUT: %w.param: @HandleTameAnimal.%W.binding.as_type (%W.binding.as_type) = value_param call_param0 // CHECK:STDOUT: %.loc9_50.1: type = splice_block %.loc9_50.2 [symbolic = %W.binding.as_type (constants.%W.binding.as_type)] { // CHECK:STDOUT: %W.ref: %facet_type.206 = name_ref W, %W.loc9_21.2 [symbolic = %W.loc9_21.1 (constants.%W)] // CHECK:STDOUT: %W.as_type: type = facet_access_type %W.ref [symbolic = %W.binding.as_type (constants.%W.binding.as_type)] // CHECK:STDOUT: %.loc9_50.2: type = converted %W.ref, %W.as_type [symbolic = %W.binding.as_type (constants.%W.binding.as_type)] // CHECK:STDOUT: } // CHECK:STDOUT: %w: @HandleTameAnimal.%W.binding.as_type (%W.binding.as_type) = value_binding w, %w.param // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: interface @Eats { // CHECK:STDOUT: %Self: %Eats.type = symbolic_binding Self, 0 [symbolic = constants.%Self.247] // CHECK:STDOUT: %Eats.WithSelf.decl = interface_with_self_decl @Eats [concrete] // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = %Self // CHECK:STDOUT: witness = () // CHECK:STDOUT: // CHECK:STDOUT: !requires: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: interface @Animal { // CHECK:STDOUT: %Self: %Animal.type = symbolic_binding Self, 0 [symbolic = constants.%Self.c49] // CHECK:STDOUT: %Animal.WithSelf.decl = interface_with_self_decl @Animal [concrete] // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = %Self // CHECK:STDOUT: witness = () // CHECK:STDOUT: // CHECK:STDOUT: !requires: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: interface @Tame { // CHECK:STDOUT: %Self: %Tame.type = symbolic_binding Self, 0 [symbolic = constants.%Self.ab8] // CHECK:STDOUT: %Tame.WithSelf.decl = interface_with_self_decl @Tame [concrete] // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = %Self // CHECK:STDOUT: witness = () // CHECK:STDOUT: // CHECK:STDOUT: !requires: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic fn @FeedTame(%V.loc7_13.2: %facet_type.2db) { // CHECK:STDOUT: %V.loc7_13.1: %facet_type.2db = symbolic_binding V, 0 [symbolic = %V.loc7_13.1 (constants.%V)] // CHECK:STDOUT: %V.binding.as_type: type = symbolic_binding_type V, 0, %V.loc7_13.1 [symbolic = %V.binding.as_type (constants.%V.binding.as_type)] // CHECK:STDOUT: %pattern_type: type = pattern_type %V.binding.as_type [symbolic = %pattern_type (constants.%pattern_type.795)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete: = require_complete_type %V.binding.as_type [symbolic = %require_complete (constants.%require_complete.12d)] // CHECK:STDOUT: // CHECK:STDOUT: fn(%v.param: @FeedTame.%V.binding.as_type (%V.binding.as_type)) { // CHECK:STDOUT: !entry: // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic fn @HandleTameAnimal(%W.loc9_21.2: %facet_type.206) { // CHECK:STDOUT: %W.loc9_21.1: %facet_type.206 = symbolic_binding W, 0 [symbolic = %W.loc9_21.1 (constants.%W)] // CHECK:STDOUT: %W.binding.as_type: type = symbolic_binding_type W, 0, %W.loc9_21.1 [symbolic = %W.binding.as_type (constants.%W.binding.as_type)] // CHECK:STDOUT: %pattern_type: type = pattern_type %W.binding.as_type [symbolic = %pattern_type (constants.%pattern_type.e8e)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete: = require_complete_type %W.binding.as_type [symbolic = %require_complete (constants.%require_complete.817)] // CHECK:STDOUT: %Eats.lookup_impl_witness: = lookup_impl_witness %W.loc9_21.1, @Eats [symbolic = %Eats.lookup_impl_witness (constants.%Eats.lookup_impl_witness)] // CHECK:STDOUT: %Tame.lookup_impl_witness: = lookup_impl_witness %W.loc9_21.1, @Tame [symbolic = %Tame.lookup_impl_witness (constants.%Tame.lookup_impl_witness)] // CHECK:STDOUT: %facet_value.loc10_13.2: %facet_type.2db = facet_value %W.binding.as_type, (%Eats.lookup_impl_witness, %Tame.lookup_impl_witness) [symbolic = %facet_value.loc10_13.2 (constants.%facet_value)] // CHECK:STDOUT: %FeedTame.specific_fn.loc10_3.2: = specific_function constants.%FeedTame, @FeedTame(%facet_value.loc10_13.2) [symbolic = %FeedTame.specific_fn.loc10_3.2 (constants.%FeedTame.specific_fn)] // CHECK:STDOUT: // CHECK:STDOUT: fn(%w.param: @HandleTameAnimal.%W.binding.as_type (%W.binding.as_type)) { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %FeedTame.ref: %FeedTame.type = name_ref FeedTame, file.%FeedTame.decl [concrete = constants.%FeedTame] // CHECK:STDOUT: %w.ref: @HandleTameAnimal.%W.binding.as_type (%W.binding.as_type) = name_ref w, %w // CHECK:STDOUT: %facet_value.loc10_13.1: %facet_type.2db = facet_value constants.%W.binding.as_type, (constants.%Eats.lookup_impl_witness, constants.%Tame.lookup_impl_witness) [symbolic = %facet_value.loc10_13.2 (constants.%facet_value)] // CHECK:STDOUT: %.loc10: %facet_type.2db = converted constants.%W.binding.as_type, %facet_value.loc10_13.1 [symbolic = %facet_value.loc10_13.2 (constants.%facet_value)] // CHECK:STDOUT: %FeedTame.specific_fn.loc10_3.1: = specific_function %FeedTame.ref, @FeedTame(constants.%facet_value) [symbolic = %FeedTame.specific_fn.loc10_3.2 (constants.%FeedTame.specific_fn)] // CHECK:STDOUT: %FeedTame.call: init %empty_tuple.type = call %FeedTame.specific_fn.loc10_3.1(%w.ref) // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @Eats.WithSelf(constants.%Self.247) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @Animal.WithSelf(constants.%Self.c49) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @Tame.WithSelf(constants.%Self.ab8) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @FeedTame(constants.%V) { // CHECK:STDOUT: %V.loc7_13.1 => constants.%V // CHECK:STDOUT: %V.binding.as_type => constants.%V.binding.as_type // CHECK:STDOUT: %pattern_type => constants.%pattern_type.795 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @HandleTameAnimal(constants.%W) { // CHECK:STDOUT: %W.loc9_21.1 => constants.%W // CHECK:STDOUT: %W.binding.as_type => constants.%W.binding.as_type // CHECK:STDOUT: %pattern_type => constants.%pattern_type.e8e // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @FeedTame(constants.%facet_value) { // CHECK:STDOUT: %V.loc7_13.1 => constants.%facet_value // CHECK:STDOUT: %V.binding.as_type => constants.%W.binding.as_type // CHECK:STDOUT: %pattern_type => constants.%pattern_type.e8e // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete => constants.%require_complete.817 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- with_blanket.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %Eats.type: type = facet_type <@Eats> [concrete] // CHECK:STDOUT: %Self.247: %Eats.type = symbolic_binding Self, 0 [symbolic] // CHECK:STDOUT: %Animal.type: type = facet_type <@Animal> [concrete] // CHECK:STDOUT: %Self.c49: %Animal.type = symbolic_binding Self, 0 [symbolic] // CHECK:STDOUT: %Tame.type: type = facet_type <@Tame> [concrete] // CHECK:STDOUT: %Self.ab8: %Tame.type = symbolic_binding Self, 0 [symbolic] // CHECK:STDOUT: %type: type = facet_type [concrete] // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self] // CHECK:STDOUT: %pattern_type.e10: type = pattern_type %Animal.type [concrete] // CHECK:STDOUT: %A: %Animal.type = symbolic_binding A, 0 [symbolic] // CHECK:STDOUT: %A.binding.as_type: type = symbolic_binding_type A, 0, %A [symbolic] // CHECK:STDOUT: %Eats.impl_witness.760: = impl_witness @A.binding.as_type.as.Eats.impl.%Eats.impl_witness_table, @A.binding.as_type.as.Eats.impl(%A) [symbolic] // CHECK:STDOUT: %Eats.facet: %Eats.type = facet_value %A.binding.as_type, (%Eats.impl_witness.760) [symbolic] // CHECK:STDOUT: %BitAndWith.type.f2e: type = generic_interface_type @BitAndWith [concrete] // CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] // CHECK:STDOUT: %BitAndWith.generic: %BitAndWith.type.f2e = struct_value () [concrete] // CHECK:STDOUT: %BitAndWith.type.b10: type = facet_type <@BitAndWith, @BitAndWith(type)> [concrete] // CHECK:STDOUT: %BitAndWith.impl_witness: = impl_witness imports.%BitAndWith.impl_witness_table [concrete] // CHECK:STDOUT: %BitAndWith.facet: %BitAndWith.type.b10 = facet_value type, (%BitAndWith.impl_witness) [concrete] // CHECK:STDOUT: %BitAndWith.WithSelf.Op.type.4bd: type = fn_type @BitAndWith.WithSelf.Op, @BitAndWith.WithSelf(type, %BitAndWith.facet) [concrete] // CHECK:STDOUT: %.d15: type = fn_type_with_self_type %BitAndWith.WithSelf.Op.type.4bd, %BitAndWith.facet [concrete] // CHECK:STDOUT: %type.as.BitAndWith.impl.Op.type: type = fn_type @type.as.BitAndWith.impl.Op [concrete] // CHECK:STDOUT: %type.as.BitAndWith.impl.Op: %type.as.BitAndWith.impl.Op.type = struct_value () [concrete] // CHECK:STDOUT: %type.as.BitAndWith.impl.Op.bound.a5f: = bound_method %Tame.type, %type.as.BitAndWith.impl.Op [concrete] // CHECK:STDOUT: %facet_type.2db: type = facet_type <@Eats & @Tame> [concrete] // CHECK:STDOUT: %pattern_type.fa7: type = pattern_type %facet_type.2db [concrete] // CHECK:STDOUT: %V: %facet_type.2db = symbolic_binding V, 0 [symbolic] // CHECK:STDOUT: %V.binding.as_type: type = symbolic_binding_type V, 0, %V [symbolic] // CHECK:STDOUT: %pattern_type.795: type = pattern_type %V.binding.as_type [symbolic] // CHECK:STDOUT: %FeedTame2.type: type = fn_type @FeedTame2 [concrete] // CHECK:STDOUT: %FeedTame2: %FeedTame2.type = struct_value () [concrete] // CHECK:STDOUT: %require_complete.12d: = require_complete_type %V.binding.as_type [symbolic] // CHECK:STDOUT: %type.as.BitAndWith.impl.Op.bound.92c: = bound_method %Animal.type, %type.as.BitAndWith.impl.Op [concrete] // CHECK:STDOUT: %facet_type.afc: type = facet_type <@Animal & @Tame> [concrete] // CHECK:STDOUT: %pattern_type.310: type = pattern_type %facet_type.afc [concrete] // CHECK:STDOUT: %W: %facet_type.afc = symbolic_binding W, 0 [symbolic] // CHECK:STDOUT: %W.binding.as_type: type = symbolic_binding_type W, 0, %W [symbolic] // CHECK:STDOUT: %pattern_type.da7: type = pattern_type %W.binding.as_type [symbolic] // CHECK:STDOUT: %HandleTameAnimal2.type: type = fn_type @HandleTameAnimal2 [concrete] // CHECK:STDOUT: %HandleTameAnimal2: %HandleTameAnimal2.type = struct_value () [concrete] // CHECK:STDOUT: %require_complete.80f: = require_complete_type %W.binding.as_type [symbolic] // CHECK:STDOUT: %Animal.lookup_impl_witness: = lookup_impl_witness %W, @Animal [symbolic] // CHECK:STDOUT: %Animal.facet: %Animal.type = facet_value %W.binding.as_type, (%Animal.lookup_impl_witness) [symbolic] // CHECK:STDOUT: %Eats.impl_witness.8ab: = impl_witness @A.binding.as_type.as.Eats.impl.%Eats.impl_witness_table, @A.binding.as_type.as.Eats.impl(%Animal.facet) [symbolic] // CHECK:STDOUT: %.3cf: require_specific_def_type = require_specific_def @A.binding.as_type.as.Eats.impl(%Animal.facet) [symbolic] // CHECK:STDOUT: %Eats.lookup_impl_witness: = lookup_impl_witness %W, @Eats [symbolic] // CHECK:STDOUT: %Tame.lookup_impl_witness: = lookup_impl_witness %W, @Tame [symbolic] // CHECK:STDOUT: %facet_value: %facet_type.2db = facet_value %W.binding.as_type, (%Eats.lookup_impl_witness, %Tame.lookup_impl_witness) [symbolic] // CHECK:STDOUT: %FeedTame2.specific_fn: = specific_function %FeedTame2, @FeedTame2(%facet_value) [symbolic] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: .BitAndWith = %Core.BitAndWith // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: %Core.BitAndWith: %BitAndWith.type.f2e = import_ref Core//prelude/parts/as, BitAndWith, loaded [concrete = constants.%BitAndWith.generic] // CHECK:STDOUT: %Core.import_ref.8d3: %type.as.BitAndWith.impl.Op.type = import_ref Core//prelude/parts/as, loc{{\d+_\d+}}, loaded [concrete = constants.%type.as.BitAndWith.impl.Op] // CHECK:STDOUT: %BitAndWith.impl_witness_table = impl_witness_table (%Core.import_ref.8d3), @type.as.BitAndWith.impl [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .Eats = %Eats.decl // CHECK:STDOUT: .Animal = %Animal.decl // CHECK:STDOUT: .Tame = %Tame.decl // CHECK:STDOUT: .FeedTame2 = %FeedTame2.decl // CHECK:STDOUT: .HandleTameAnimal2 = %HandleTameAnimal2.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %Eats.decl: type = interface_decl @Eats [concrete = constants.%Eats.type] {} {} // CHECK:STDOUT: %Animal.decl: type = interface_decl @Animal [concrete = constants.%Animal.type] {} {} // CHECK:STDOUT: %Tame.decl: type = interface_decl @Tame [concrete = constants.%Tame.type] {} {} // CHECK:STDOUT: impl_decl @A.binding.as_type.as.Eats.impl [concrete] { // CHECK:STDOUT: %A.patt: %pattern_type.e10 = symbolic_binding_pattern A, 0 [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %A.ref: %Animal.type = name_ref A, %A.loc7_14.1 [symbolic = %A.loc7_14.2 (constants.%A)] // CHECK:STDOUT: %A.as_type: type = facet_access_type %A.ref [symbolic = %A.binding.as_type (constants.%A.binding.as_type)] // CHECK:STDOUT: %.loc7_26: type = converted %A.ref, %A.as_type [symbolic = %A.binding.as_type (constants.%A.binding.as_type)] // CHECK:STDOUT: %Eats.ref: type = name_ref Eats, file.%Eats.decl [concrete = constants.%Eats.type] // CHECK:STDOUT: %.loc7_18: type = splice_block %Animal.ref [concrete = constants.%Animal.type] { // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %Animal.ref: type = name_ref Animal, file.%Animal.decl [concrete = constants.%Animal.type] // CHECK:STDOUT: } // CHECK:STDOUT: %A.loc7_14.1: %Animal.type = symbolic_binding A, 0 [symbolic = %A.loc7_14.2 (constants.%A)] // CHECK:STDOUT: } // CHECK:STDOUT: %FeedTame2.decl: %FeedTame2.type = fn_decl @FeedTame2 [concrete = constants.%FeedTame2] { // CHECK:STDOUT: %V.patt: %pattern_type.fa7 = symbolic_binding_pattern V, 0 [concrete] // CHECK:STDOUT: %v.patt: @FeedTame2.%pattern_type (%pattern_type.795) = value_binding_pattern v [concrete] // CHECK:STDOUT: %v.param_patt: @FeedTame2.%pattern_type (%pattern_type.795) = value_param_pattern %v.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %.loc9_23.1: type = splice_block %.loc9_23.3 [concrete = constants.%facet_type.2db] { // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %Tame.ref: type = name_ref Tame, file.%Tame.decl [concrete = constants.%Tame.type] // CHECK:STDOUT: %Eats.ref: type = name_ref Eats, file.%Eats.decl [concrete = constants.%Eats.type] // CHECK:STDOUT: %impl.elem0: %.d15 = impl_witness_access constants.%BitAndWith.impl_witness, element0 [concrete = constants.%type.as.BitAndWith.impl.Op] // CHECK:STDOUT: %bound_method: = bound_method %Tame.ref, %impl.elem0 [concrete = constants.%type.as.BitAndWith.impl.Op.bound.a5f] // CHECK:STDOUT: %type.as.BitAndWith.impl.Op.call: init type = call %bound_method(%Tame.ref, %Eats.ref) [concrete = constants.%facet_type.2db] // CHECK:STDOUT: %.loc9_23.2: type = value_of_initializer %type.as.BitAndWith.impl.Op.call [concrete = constants.%facet_type.2db] // CHECK:STDOUT: %.loc9_23.3: type = converted %type.as.BitAndWith.impl.Op.call, %.loc9_23.2 [concrete = constants.%facet_type.2db] // CHECK:STDOUT: } // CHECK:STDOUT: %V.loc9_14.2: %facet_type.2db = symbolic_binding V, 0 [symbolic = %V.loc9_14.1 (constants.%V)] // CHECK:STDOUT: %v.param: @FeedTame2.%V.binding.as_type (%V.binding.as_type) = value_param call_param0 // CHECK:STDOUT: %.loc9_41.1: type = splice_block %.loc9_41.2 [symbolic = %V.binding.as_type (constants.%V.binding.as_type)] { // CHECK:STDOUT: %V.ref: %facet_type.2db = name_ref V, %V.loc9_14.2 [symbolic = %V.loc9_14.1 (constants.%V)] // CHECK:STDOUT: %V.as_type: type = facet_access_type %V.ref [symbolic = %V.binding.as_type (constants.%V.binding.as_type)] // CHECK:STDOUT: %.loc9_41.2: type = converted %V.ref, %V.as_type [symbolic = %V.binding.as_type (constants.%V.binding.as_type)] // CHECK:STDOUT: } // CHECK:STDOUT: %v: @FeedTame2.%V.binding.as_type (%V.binding.as_type) = value_binding v, %v.param // CHECK:STDOUT: } // CHECK:STDOUT: %HandleTameAnimal2.decl: %HandleTameAnimal2.type = fn_decl @HandleTameAnimal2 [concrete = constants.%HandleTameAnimal2] { // CHECK:STDOUT: %W.patt: %pattern_type.310 = symbolic_binding_pattern W, 0 [concrete] // CHECK:STDOUT: %w.patt: @HandleTameAnimal2.%pattern_type (%pattern_type.da7) = value_binding_pattern w [concrete] // CHECK:STDOUT: %w.param_patt: @HandleTameAnimal2.%pattern_type (%pattern_type.da7) = value_param_pattern %w.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %.loc11_33.1: type = splice_block %.loc11_33.3 [concrete = constants.%facet_type.afc] { // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %Animal.ref: type = name_ref Animal, file.%Animal.decl [concrete = constants.%Animal.type] // CHECK:STDOUT: %Tame.ref: type = name_ref Tame, file.%Tame.decl [concrete = constants.%Tame.type] // CHECK:STDOUT: %impl.elem0: %.d15 = impl_witness_access constants.%BitAndWith.impl_witness, element0 [concrete = constants.%type.as.BitAndWith.impl.Op] // CHECK:STDOUT: %bound_method: = bound_method %Animal.ref, %impl.elem0 [concrete = constants.%type.as.BitAndWith.impl.Op.bound.92c] // CHECK:STDOUT: %type.as.BitAndWith.impl.Op.call: init type = call %bound_method(%Animal.ref, %Tame.ref) [concrete = constants.%facet_type.afc] // CHECK:STDOUT: %.loc11_33.2: type = value_of_initializer %type.as.BitAndWith.impl.Op.call [concrete = constants.%facet_type.afc] // CHECK:STDOUT: %.loc11_33.3: type = converted %type.as.BitAndWith.impl.Op.call, %.loc11_33.2 [concrete = constants.%facet_type.afc] // CHECK:STDOUT: } // CHECK:STDOUT: %W.loc11_22.2: %facet_type.afc = symbolic_binding W, 0 [symbolic = %W.loc11_22.1 (constants.%W)] // CHECK:STDOUT: %w.param: @HandleTameAnimal2.%W.binding.as_type (%W.binding.as_type) = value_param call_param0 // CHECK:STDOUT: %.loc11_44.1: type = splice_block %.loc11_44.2 [symbolic = %W.binding.as_type (constants.%W.binding.as_type)] { // CHECK:STDOUT: %W.ref: %facet_type.afc = name_ref W, %W.loc11_22.2 [symbolic = %W.loc11_22.1 (constants.%W)] // CHECK:STDOUT: %W.as_type: type = facet_access_type %W.ref [symbolic = %W.binding.as_type (constants.%W.binding.as_type)] // CHECK:STDOUT: %.loc11_44.2: type = converted %W.ref, %W.as_type [symbolic = %W.binding.as_type (constants.%W.binding.as_type)] // CHECK:STDOUT: } // CHECK:STDOUT: %w: @HandleTameAnimal2.%W.binding.as_type (%W.binding.as_type) = value_binding w, %w.param // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: interface @Eats { // CHECK:STDOUT: %Self: %Eats.type = symbolic_binding Self, 0 [symbolic = constants.%Self.247] // CHECK:STDOUT: %Eats.WithSelf.decl = interface_with_self_decl @Eats [concrete] // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = %Self // CHECK:STDOUT: witness = () // CHECK:STDOUT: // CHECK:STDOUT: !requires: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: interface @Animal { // CHECK:STDOUT: %Self: %Animal.type = symbolic_binding Self, 0 [symbolic = constants.%Self.c49] // CHECK:STDOUT: %Animal.WithSelf.decl = interface_with_self_decl @Animal [concrete] // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = %Self // CHECK:STDOUT: witness = () // CHECK:STDOUT: // CHECK:STDOUT: !requires: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: interface @Tame { // CHECK:STDOUT: %Self: %Tame.type = symbolic_binding Self, 0 [symbolic = constants.%Self.ab8] // CHECK:STDOUT: %Tame.WithSelf.decl = interface_with_self_decl @Tame [concrete] // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = %Self // CHECK:STDOUT: witness = () // CHECK:STDOUT: // CHECK:STDOUT: !requires: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic impl @A.binding.as_type.as.Eats.impl(%A.loc7_14.1: %Animal.type) { // CHECK:STDOUT: %A.loc7_14.2: %Animal.type = symbolic_binding A, 0 [symbolic = %A.loc7_14.2 (constants.%A)] // CHECK:STDOUT: %A.binding.as_type: type = symbolic_binding_type A, 0, %A.loc7_14.2 [symbolic = %A.binding.as_type (constants.%A.binding.as_type)] // CHECK:STDOUT: %Eats.impl_witness.loc7_36.2: = impl_witness %Eats.impl_witness_table, @A.binding.as_type.as.Eats.impl(%A.loc7_14.2) [symbolic = %Eats.impl_witness.loc7_36.2 (constants.%Eats.impl_witness.760)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: // CHECK:STDOUT: impl: %.loc7_26 as %Eats.ref { // CHECK:STDOUT: %Eats.impl_witness_table = impl_witness_table (), @A.binding.as_type.as.Eats.impl [concrete] // CHECK:STDOUT: %Eats.impl_witness.loc7_36.1: = impl_witness %Eats.impl_witness_table, @A.binding.as_type.as.Eats.impl(constants.%A) [symbolic = %Eats.impl_witness.loc7_36.2 (constants.%Eats.impl_witness.760)] // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: witness = %Eats.impl_witness.loc7_36.1 // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic fn @FeedTame2(%V.loc9_14.2: %facet_type.2db) { // CHECK:STDOUT: %V.loc9_14.1: %facet_type.2db = symbolic_binding V, 0 [symbolic = %V.loc9_14.1 (constants.%V)] // CHECK:STDOUT: %V.binding.as_type: type = symbolic_binding_type V, 0, %V.loc9_14.1 [symbolic = %V.binding.as_type (constants.%V.binding.as_type)] // CHECK:STDOUT: %pattern_type: type = pattern_type %V.binding.as_type [symbolic = %pattern_type (constants.%pattern_type.795)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete: = require_complete_type %V.binding.as_type [symbolic = %require_complete (constants.%require_complete.12d)] // CHECK:STDOUT: // CHECK:STDOUT: fn(%v.param: @FeedTame2.%V.binding.as_type (%V.binding.as_type)) { // CHECK:STDOUT: !entry: // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic fn @HandleTameAnimal2(%W.loc11_22.2: %facet_type.afc) { // CHECK:STDOUT: %W.loc11_22.1: %facet_type.afc = symbolic_binding W, 0 [symbolic = %W.loc11_22.1 (constants.%W)] // CHECK:STDOUT: %W.binding.as_type: type = symbolic_binding_type W, 0, %W.loc11_22.1 [symbolic = %W.binding.as_type (constants.%W.binding.as_type)] // CHECK:STDOUT: %pattern_type: type = pattern_type %W.binding.as_type [symbolic = %pattern_type (constants.%pattern_type.da7)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete: = require_complete_type %W.binding.as_type [symbolic = %require_complete (constants.%require_complete.80f)] // CHECK:STDOUT: %Animal.lookup_impl_witness: = lookup_impl_witness %W.loc11_22.1, @Animal [symbolic = %Animal.lookup_impl_witness (constants.%Animal.lookup_impl_witness)] // CHECK:STDOUT: %Animal.facet: %Animal.type = facet_value %W.binding.as_type, (%Animal.lookup_impl_witness) [symbolic = %Animal.facet (constants.%Animal.facet)] // CHECK:STDOUT: %.loc12_14.2: require_specific_def_type = require_specific_def @A.binding.as_type.as.Eats.impl(%Animal.facet) [symbolic = %.loc12_14.2 (constants.%.3cf)] // CHECK:STDOUT: %Eats.lookup_impl_witness: = lookup_impl_witness %W.loc11_22.1, @Eats [symbolic = %Eats.lookup_impl_witness (constants.%Eats.lookup_impl_witness)] // CHECK:STDOUT: %Tame.lookup_impl_witness: = lookup_impl_witness %W.loc11_22.1, @Tame [symbolic = %Tame.lookup_impl_witness (constants.%Tame.lookup_impl_witness)] // CHECK:STDOUT: %facet_value.loc12_14.2: %facet_type.2db = facet_value %W.binding.as_type, (%Eats.lookup_impl_witness, %Tame.lookup_impl_witness) [symbolic = %facet_value.loc12_14.2 (constants.%facet_value)] // CHECK:STDOUT: %FeedTame2.specific_fn.loc12_3.2: = specific_function constants.%FeedTame2, @FeedTame2(%facet_value.loc12_14.2) [symbolic = %FeedTame2.specific_fn.loc12_3.2 (constants.%FeedTame2.specific_fn)] // CHECK:STDOUT: // CHECK:STDOUT: fn(%w.param: @HandleTameAnimal2.%W.binding.as_type (%W.binding.as_type)) { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %FeedTame2.ref: %FeedTame2.type = name_ref FeedTame2, file.%FeedTame2.decl [concrete = constants.%FeedTame2] // CHECK:STDOUT: %w.ref: @HandleTameAnimal2.%W.binding.as_type (%W.binding.as_type) = name_ref w, %w // CHECK:STDOUT: %facet_value.loc12_14.1: %facet_type.2db = facet_value constants.%W.binding.as_type, (constants.%Eats.lookup_impl_witness, constants.%Tame.lookup_impl_witness) [symbolic = %facet_value.loc12_14.2 (constants.%facet_value)] // CHECK:STDOUT: %.loc12_14.1: %facet_type.2db = converted constants.%W.binding.as_type, %facet_value.loc12_14.1 [symbolic = %facet_value.loc12_14.2 (constants.%facet_value)] // CHECK:STDOUT: %FeedTame2.specific_fn.loc12_3.1: = specific_function %FeedTame2.ref, @FeedTame2(constants.%facet_value) [symbolic = %FeedTame2.specific_fn.loc12_3.2 (constants.%FeedTame2.specific_fn)] // CHECK:STDOUT: %FeedTame2.call: init %empty_tuple.type = call %FeedTame2.specific_fn.loc12_3.1(%w.ref) // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @Eats.WithSelf(constants.%Self.247) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @Animal.WithSelf(constants.%Self.c49) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @Tame.WithSelf(constants.%Self.ab8) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @A.binding.as_type.as.Eats.impl(constants.%A) { // CHECK:STDOUT: %A.loc7_14.2 => constants.%A // CHECK:STDOUT: %A.binding.as_type => constants.%A.binding.as_type // CHECK:STDOUT: %Eats.impl_witness.loc7_36.2 => constants.%Eats.impl_witness.760 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @Eats.WithSelf(constants.%Eats.facet) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @FeedTame2(constants.%V) { // CHECK:STDOUT: %V.loc9_14.1 => constants.%V // CHECK:STDOUT: %V.binding.as_type => constants.%V.binding.as_type // CHECK:STDOUT: %pattern_type => constants.%pattern_type.795 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @HandleTameAnimal2(constants.%W) { // CHECK:STDOUT: %W.loc11_22.1 => constants.%W // CHECK:STDOUT: %W.binding.as_type => constants.%W.binding.as_type // CHECK:STDOUT: %pattern_type => constants.%pattern_type.da7 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @A.binding.as_type.as.Eats.impl(constants.%Animal.facet) { // CHECK:STDOUT: %A.loc7_14.2 => constants.%Animal.facet // CHECK:STDOUT: %A.binding.as_type => constants.%W.binding.as_type // CHECK:STDOUT: %Eats.impl_witness.loc7_36.2 => constants.%Eats.impl_witness.8ab // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @FeedTame2(constants.%facet_value) { // CHECK:STDOUT: %V.loc9_14.1 => constants.%facet_value // CHECK:STDOUT: %V.binding.as_type => constants.%W.binding.as_type // CHECK:STDOUT: %pattern_type => constants.%pattern_type.da7 // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete => constants.%require_complete.80f // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- equivalent.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %A.type: type = facet_type <@A> [concrete] // CHECK:STDOUT: %Self: %A.type = symbolic_binding Self, 0 [symbolic] // CHECK:STDOUT: %type: type = facet_type [concrete] // CHECK:STDOUT: %.Self.c39: %type = symbolic_binding .Self [symbolic_self] // CHECK:STDOUT: %pattern_type.faf: type = pattern_type %A.type [concrete] // CHECK:STDOUT: %T: %A.type = symbolic_binding T, 0 [symbolic] // CHECK:STDOUT: %T.binding.as_type: type = symbolic_binding_type T, 0, %T [symbolic] // CHECK:STDOUT: %pattern_type.057cf4.1: type = pattern_type %T.binding.as_type [symbolic] // CHECK:STDOUT: %TakesA.type: type = fn_type @TakesA [concrete] // CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] // CHECK:STDOUT: %TakesA: %TakesA.type = struct_value () [concrete] // CHECK:STDOUT: %require_complete.1a7376.1: = require_complete_type %T.binding.as_type [symbolic] // CHECK:STDOUT: %.Self.091: %A.type = symbolic_binding .Self [symbolic_self] // CHECK:STDOUT: %.Self.binding.as_type: type = symbolic_binding_type .Self, %.Self.091 [symbolic_self] // CHECK:STDOUT: %U: %A.type = symbolic_binding U, 0 [symbolic] // CHECK:STDOUT: %U.binding.as_type: type = symbolic_binding_type U, 0, %U [symbolic] // CHECK:STDOUT: %pattern_type.057cf4.2: type = pattern_type %U.binding.as_type [symbolic] // CHECK:STDOUT: %WithExtraWhere.type: type = fn_type @WithExtraWhere [concrete] // CHECK:STDOUT: %WithExtraWhere: %WithExtraWhere.type = struct_value () [concrete] // CHECK:STDOUT: %require_complete.1a7376.2: = require_complete_type %U.binding.as_type [symbolic] // CHECK:STDOUT: %TakesA.specific_fn: = specific_function %TakesA, @TakesA(%U) [symbolic] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .A = %A.decl // CHECK:STDOUT: .TakesA = %TakesA.decl // CHECK:STDOUT: .WithExtraWhere = %WithExtraWhere.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %A.decl: type = interface_decl @A [concrete = constants.%A.type] {} {} // CHECK:STDOUT: %TakesA.decl: %TakesA.type = fn_decl @TakesA [concrete = constants.%TakesA] { // CHECK:STDOUT: %T.patt: %pattern_type.faf = symbolic_binding_pattern T, 0 [concrete] // CHECK:STDOUT: %x.patt: @TakesA.%pattern_type (%pattern_type.057cf4.1) = value_binding_pattern x [concrete] // CHECK:STDOUT: %x.param_patt: @TakesA.%pattern_type (%pattern_type.057cf4.1) = value_param_pattern %x.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %.loc7_15: type = splice_block %A.ref [concrete = constants.%A.type] { // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self = constants.%.Self.c39] // CHECK:STDOUT: %A.ref: type = name_ref A, file.%A.decl [concrete = constants.%A.type] // CHECK:STDOUT: } // CHECK:STDOUT: %T.loc7_11.2: %A.type = symbolic_binding T, 0 [symbolic = %T.loc7_11.1 (constants.%T)] // CHECK:STDOUT: %x.param: @TakesA.%T.binding.as_type (%T.binding.as_type) = value_param call_param0 // CHECK:STDOUT: %.loc7_28.1: type = splice_block %.loc7_28.2 [symbolic = %T.binding.as_type (constants.%T.binding.as_type)] { // CHECK:STDOUT: %T.ref: %A.type = name_ref T, %T.loc7_11.2 [symbolic = %T.loc7_11.1 (constants.%T)] // CHECK:STDOUT: %T.as_type: type = facet_access_type %T.ref [symbolic = %T.binding.as_type (constants.%T.binding.as_type)] // CHECK:STDOUT: %.loc7_28.2: type = converted %T.ref, %T.as_type [symbolic = %T.binding.as_type (constants.%T.binding.as_type)] // CHECK:STDOUT: } // CHECK:STDOUT: %x: @TakesA.%T.binding.as_type (%T.binding.as_type) = value_binding x, %x.param // CHECK:STDOUT: } // CHECK:STDOUT: %WithExtraWhere.decl: %WithExtraWhere.type = fn_decl @WithExtraWhere [concrete = constants.%WithExtraWhere] { // CHECK:STDOUT: %U.patt: %pattern_type.faf = symbolic_binding_pattern U, 0 [concrete] // CHECK:STDOUT: %y.patt: @WithExtraWhere.%pattern_type (%pattern_type.057cf4.2) = value_binding_pattern y [concrete] // CHECK:STDOUT: %y.param_patt: @WithExtraWhere.%pattern_type (%pattern_type.057cf4.2) = value_param_pattern %y.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %.loc9_25.1: type = splice_block %.loc9_25.2 [concrete = constants.%A.type] { // CHECK:STDOUT: %.Self.1: %type = symbolic_binding .Self [symbolic_self = constants.%.Self.c39] // CHECK:STDOUT: %A.ref: type = name_ref A, file.%A.decl [concrete = constants.%A.type] // CHECK:STDOUT: %.Self.2: %A.type = symbolic_binding .Self [symbolic_self = constants.%.Self.091] // CHECK:STDOUT: %.Self.ref: %A.type = name_ref .Self, %.Self.2 [symbolic_self = constants.%.Self.091] // CHECK:STDOUT: %.loc9_43: type = type_literal type [concrete = type] // CHECK:STDOUT: %.Self.as_type: type = facet_access_type %.Self.ref [symbolic_self = constants.%.Self.binding.as_type] // CHECK:STDOUT: %.loc9_31: type = converted %.Self.ref, %.Self.as_type [symbolic_self = constants.%.Self.binding.as_type] // CHECK:STDOUT: %.loc9_25.2: type = where_expr %.Self.2 [concrete = constants.%A.type] { // CHECK:STDOUT: requirement_base_facet_type constants.%A.type // CHECK:STDOUT: requirement_impls %.loc9_31, %.loc9_43 // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: %U.loc9_19.2: %A.type = symbolic_binding U, 0 [symbolic = %U.loc9_19.1 (constants.%U)] // CHECK:STDOUT: %y.param: @WithExtraWhere.%U.binding.as_type (%U.binding.as_type) = value_param call_param0 // CHECK:STDOUT: %.loc9_52.1: type = splice_block %.loc9_52.2 [symbolic = %U.binding.as_type (constants.%U.binding.as_type)] { // CHECK:STDOUT: %U.ref: %A.type = name_ref U, %U.loc9_19.2 [symbolic = %U.loc9_19.1 (constants.%U)] // CHECK:STDOUT: %U.as_type: type = facet_access_type %U.ref [symbolic = %U.binding.as_type (constants.%U.binding.as_type)] // CHECK:STDOUT: %.loc9_52.2: type = converted %U.ref, %U.as_type [symbolic = %U.binding.as_type (constants.%U.binding.as_type)] // CHECK:STDOUT: } // CHECK:STDOUT: %y: @WithExtraWhere.%U.binding.as_type (%U.binding.as_type) = value_binding y, %y.param // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: interface @A { // CHECK:STDOUT: %Self: %A.type = symbolic_binding Self, 0 [symbolic = constants.%Self] // CHECK:STDOUT: %A.WithSelf.decl = interface_with_self_decl @A [concrete] // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = %Self // CHECK:STDOUT: witness = () // CHECK:STDOUT: // CHECK:STDOUT: !requires: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic fn @TakesA(%T.loc7_11.2: %A.type) { // CHECK:STDOUT: %T.loc7_11.1: %A.type = symbolic_binding T, 0 [symbolic = %T.loc7_11.1 (constants.%T)] // CHECK:STDOUT: %T.binding.as_type: type = symbolic_binding_type T, 0, %T.loc7_11.1 [symbolic = %T.binding.as_type (constants.%T.binding.as_type)] // CHECK:STDOUT: %pattern_type: type = pattern_type %T.binding.as_type [symbolic = %pattern_type (constants.%pattern_type.057cf4.1)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete: = require_complete_type %T.binding.as_type [symbolic = %require_complete (constants.%require_complete.1a7376.1)] // CHECK:STDOUT: // CHECK:STDOUT: fn(%x.param: @TakesA.%T.binding.as_type (%T.binding.as_type)) { // CHECK:STDOUT: !entry: // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic fn @WithExtraWhere(%U.loc9_19.2: %A.type) { // CHECK:STDOUT: %U.loc9_19.1: %A.type = symbolic_binding U, 0 [symbolic = %U.loc9_19.1 (constants.%U)] // CHECK:STDOUT: %U.binding.as_type: type = symbolic_binding_type U, 0, %U.loc9_19.1 [symbolic = %U.binding.as_type (constants.%U.binding.as_type)] // CHECK:STDOUT: %pattern_type: type = pattern_type %U.binding.as_type [symbolic = %pattern_type (constants.%pattern_type.057cf4.2)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete: = require_complete_type %U.binding.as_type [symbolic = %require_complete (constants.%require_complete.1a7376.2)] // CHECK:STDOUT: %TakesA.specific_fn.loc10_3.2: = specific_function constants.%TakesA, @TakesA(%U.loc9_19.1) [symbolic = %TakesA.specific_fn.loc10_3.2 (constants.%TakesA.specific_fn)] // CHECK:STDOUT: // CHECK:STDOUT: fn(%y.param: @WithExtraWhere.%U.binding.as_type (%U.binding.as_type)) { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %TakesA.ref: %TakesA.type = name_ref TakesA, file.%TakesA.decl [concrete = constants.%TakesA] // CHECK:STDOUT: %y.ref: @WithExtraWhere.%U.binding.as_type (%U.binding.as_type) = name_ref y, %y // CHECK:STDOUT: %.loc10: %A.type = converted constants.%U.binding.as_type, constants.%U [symbolic = %U.loc9_19.1 (constants.%U)] // CHECK:STDOUT: %TakesA.specific_fn.loc10_3.1: = specific_function %TakesA.ref, @TakesA(constants.%U) [symbolic = %TakesA.specific_fn.loc10_3.2 (constants.%TakesA.specific_fn)] // CHECK:STDOUT: %TakesA.call: init %empty_tuple.type = call %TakesA.specific_fn.loc10_3.1(%y.ref) // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @A.WithSelf(constants.%Self) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @TakesA(constants.%T) { // CHECK:STDOUT: %T.loc7_11.1 => constants.%T // CHECK:STDOUT: %T.binding.as_type => constants.%T.binding.as_type // CHECK:STDOUT: %pattern_type => constants.%pattern_type.057cf4.1 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @WithExtraWhere(constants.%U) { // CHECK:STDOUT: %U.loc9_19.1 => constants.%U // CHECK:STDOUT: %U.binding.as_type => constants.%U.binding.as_type // CHECK:STDOUT: %pattern_type => constants.%pattern_type.057cf4.2 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @TakesA(constants.%U) { // CHECK:STDOUT: %T.loc7_11.1 => constants.%U // CHECK:STDOUT: %T.binding.as_type => constants.%U.binding.as_type // CHECK:STDOUT: %pattern_type => constants.%pattern_type.057cf4.2 // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete => constants.%require_complete.1a7376.2 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- no_interfaces_success.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %type: type = facet_type [concrete] // CHECK:STDOUT: %.Self.c39: %type = symbolic_binding .Self [symbolic_self] // CHECK:STDOUT: %pattern_type.98f: type = pattern_type type [concrete] // CHECK:STDOUT: %T.67db0b.1: type = symbolic_binding T, 0 [symbolic] // CHECK:STDOUT: %pattern_type.51d: type = pattern_type %T.67db0b.1 [symbolic] // CHECK:STDOUT: %TakesTypeDeduced.type: type = fn_type @TakesTypeDeduced [concrete] // CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] // CHECK:STDOUT: %TakesTypeDeduced: %TakesTypeDeduced.type = struct_value () [concrete] // CHECK:STDOUT: %require_complete.944: = require_complete_type %T.67db0b.1 [symbolic] // CHECK:STDOUT: %.Self.16f: type = symbolic_binding .Self [symbolic_self] // CHECK:STDOUT: %pattern_type.9a5: type = pattern_type %type [concrete] // CHECK:STDOUT: %U: %type = symbolic_binding U, 0 [symbolic] // CHECK:STDOUT: %U.binding.as_type: type = symbolic_binding_type U, 0, %U [symbolic] // CHECK:STDOUT: %pattern_type.349: type = pattern_type %U.binding.as_type [symbolic] // CHECK:STDOUT: %CallsWithExtraWhere.type: type = fn_type @CallsWithExtraWhere [concrete] // CHECK:STDOUT: %CallsWithExtraWhere: %CallsWithExtraWhere.type = struct_value () [concrete] // CHECK:STDOUT: %require_complete.d55: = require_complete_type %U.binding.as_type [symbolic] // CHECK:STDOUT: %TakesTypeDeduced.specific_fn: = specific_function %TakesTypeDeduced, @TakesTypeDeduced(%U.binding.as_type) [symbolic] // CHECK:STDOUT: %T.67db0b.2: type = symbolic_binding T, 0 [symbolic] // CHECK:STDOUT: %TakesTypeExplicit.type: type = fn_type @TakesTypeExplicit [concrete] // CHECK:STDOUT: %TakesTypeExplicit: %TakesTypeExplicit.type = struct_value () [concrete] // CHECK:STDOUT: %CallsWithExtraWhereExplicit.type: type = fn_type @CallsWithExtraWhereExplicit [concrete] // CHECK:STDOUT: %CallsWithExtraWhereExplicit: %CallsWithExtraWhereExplicit.type = struct_value () [concrete] // CHECK:STDOUT: %TakesTypeExplicit.specific_fn: = specific_function %TakesTypeExplicit, @TakesTypeExplicit(%U.binding.as_type) [symbolic] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .TakesTypeDeduced = %TakesTypeDeduced.decl // CHECK:STDOUT: .CallsWithExtraWhere = %CallsWithExtraWhere.decl // CHECK:STDOUT: .TakesTypeExplicit = %TakesTypeExplicit.decl // CHECK:STDOUT: .CallsWithExtraWhereExplicit = %CallsWithExtraWhereExplicit.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %TakesTypeDeduced.decl: %TakesTypeDeduced.type = fn_decl @TakesTypeDeduced [concrete = constants.%TakesTypeDeduced] { // CHECK:STDOUT: %T.patt: %pattern_type.98f = symbolic_binding_pattern T, 0 [concrete] // CHECK:STDOUT: %x.patt: @TakesTypeDeduced.%pattern_type (%pattern_type.51d) = value_binding_pattern x [concrete] // CHECK:STDOUT: %x.param_patt: @TakesTypeDeduced.%pattern_type (%pattern_type.51d) = value_param_pattern %x.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %.loc3_25.1: type = splice_block %.loc3_25.2 [concrete = type] { // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self = constants.%.Self.c39] // CHECK:STDOUT: %.loc3_25.2: type = type_literal type [concrete = type] // CHECK:STDOUT: } // CHECK:STDOUT: %T.loc3_21.2: type = symbolic_binding T, 0 [symbolic = %T.loc3_21.1 (constants.%T.67db0b.1)] // CHECK:STDOUT: %x.param: @TakesTypeDeduced.%T.loc3_21.1 (%T.67db0b.1) = value_param call_param0 // CHECK:STDOUT: %T.ref: type = name_ref T, %T.loc3_21.2 [symbolic = %T.loc3_21.1 (constants.%T.67db0b.1)] // CHECK:STDOUT: %x: @TakesTypeDeduced.%T.loc3_21.1 (%T.67db0b.1) = value_binding x, %x.param // CHECK:STDOUT: } // CHECK:STDOUT: %CallsWithExtraWhere.decl: %CallsWithExtraWhere.type = fn_decl @CallsWithExtraWhere [concrete = constants.%CallsWithExtraWhere] { // CHECK:STDOUT: %U.patt: %pattern_type.9a5 = symbolic_binding_pattern U, 0 [concrete] // CHECK:STDOUT: %y.patt: @CallsWithExtraWhere.%pattern_type (%pattern_type.349) = value_binding_pattern y [concrete] // CHECK:STDOUT: %y.param_patt: @CallsWithExtraWhere.%pattern_type (%pattern_type.349) = value_param_pattern %y.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %.loc4_33.1: type = splice_block %.loc4_33.2 [concrete = constants.%type] { // CHECK:STDOUT: %.Self.1: %type = symbolic_binding .Self [symbolic_self = constants.%.Self.c39] // CHECK:STDOUT: %.loc4_28: type = type_literal type [concrete = type] // CHECK:STDOUT: %.Self.2: type = symbolic_binding .Self [symbolic_self = constants.%.Self.16f] // CHECK:STDOUT: %.Self.ref: type = name_ref .Self, %.Self.2 [symbolic_self = constants.%.Self.16f] // CHECK:STDOUT: %.loc4_51: type = type_literal type [concrete = type] // CHECK:STDOUT: %.loc4_33.2: type = where_expr %.Self.2 [concrete = constants.%type] { // CHECK:STDOUT: requirement_base_facet_type type // CHECK:STDOUT: requirement_impls %.Self.ref, %.loc4_51 // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: %U.loc4_24.2: %type = symbolic_binding U, 0 [symbolic = %U.loc4_24.1 (constants.%U)] // CHECK:STDOUT: %y.param: @CallsWithExtraWhere.%U.binding.as_type (%U.binding.as_type) = value_param call_param0 // CHECK:STDOUT: %.loc4_60.1: type = splice_block %.loc4_60.2 [symbolic = %U.binding.as_type (constants.%U.binding.as_type)] { // CHECK:STDOUT: %U.ref: %type = name_ref U, %U.loc4_24.2 [symbolic = %U.loc4_24.1 (constants.%U)] // CHECK:STDOUT: %U.as_type: type = facet_access_type %U.ref [symbolic = %U.binding.as_type (constants.%U.binding.as_type)] // CHECK:STDOUT: %.loc4_60.2: type = converted %U.ref, %U.as_type [symbolic = %U.binding.as_type (constants.%U.binding.as_type)] // CHECK:STDOUT: } // CHECK:STDOUT: %y: @CallsWithExtraWhere.%U.binding.as_type (%U.binding.as_type) = value_binding y, %y.param // CHECK:STDOUT: } // CHECK:STDOUT: %TakesTypeExplicit.decl: %TakesTypeExplicit.type = fn_decl @TakesTypeExplicit [concrete = constants.%TakesTypeExplicit] { // CHECK:STDOUT: %T.patt: %pattern_type.98f = symbolic_binding_pattern T, 0 [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %.loc8_33.1: type = splice_block %.loc8_33.2 [concrete = type] { // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self = constants.%.Self.c39] // CHECK:STDOUT: %.loc8_33.2: type = type_literal type [concrete = type] // CHECK:STDOUT: } // CHECK:STDOUT: %T.loc8_29.3: type = symbolic_binding T, 0 [symbolic = %T.loc8_29.2 (constants.%T.67db0b.2)] // CHECK:STDOUT: } // CHECK:STDOUT: %CallsWithExtraWhereExplicit.decl: %CallsWithExtraWhereExplicit.type = fn_decl @CallsWithExtraWhereExplicit [concrete = constants.%CallsWithExtraWhereExplicit] { // CHECK:STDOUT: %U.patt: %pattern_type.9a5 = symbolic_binding_pattern U, 0 [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %.loc9_41.1: type = splice_block %.loc9_41.2 [concrete = constants.%type] { // CHECK:STDOUT: %.Self.1: %type = symbolic_binding .Self [symbolic_self = constants.%.Self.c39] // CHECK:STDOUT: %.loc9_36: type = type_literal type [concrete = type] // CHECK:STDOUT: %.Self.2: type = symbolic_binding .Self [symbolic_self = constants.%.Self.16f] // CHECK:STDOUT: %.Self.ref: type = name_ref .Self, %.Self.2 [symbolic_self = constants.%.Self.16f] // CHECK:STDOUT: %.loc9_59: type = type_literal type [concrete = type] // CHECK:STDOUT: %.loc9_41.2: type = where_expr %.Self.2 [concrete = constants.%type] { // CHECK:STDOUT: requirement_base_facet_type type // CHECK:STDOUT: requirement_impls %.Self.ref, %.loc9_59 // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: %U.loc9_32.2: %type = symbolic_binding U, 0 [symbolic = %U.loc9_32.1 (constants.%U)] // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic fn @TakesTypeDeduced(%T.loc3_21.2: type) { // CHECK:STDOUT: %T.loc3_21.1: type = symbolic_binding T, 0 [symbolic = %T.loc3_21.1 (constants.%T.67db0b.1)] // CHECK:STDOUT: %pattern_type: type = pattern_type %T.loc3_21.1 [symbolic = %pattern_type (constants.%pattern_type.51d)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete: = require_complete_type %T.loc3_21.1 [symbolic = %require_complete (constants.%require_complete.944)] // CHECK:STDOUT: // CHECK:STDOUT: fn(%x.param: @TakesTypeDeduced.%T.loc3_21.1 (%T.67db0b.1)) { // CHECK:STDOUT: !entry: // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic fn @CallsWithExtraWhere(%U.loc4_24.2: %type) { // CHECK:STDOUT: %U.loc4_24.1: %type = symbolic_binding U, 0 [symbolic = %U.loc4_24.1 (constants.%U)] // CHECK:STDOUT: %U.binding.as_type: type = symbolic_binding_type U, 0, %U.loc4_24.1 [symbolic = %U.binding.as_type (constants.%U.binding.as_type)] // CHECK:STDOUT: %pattern_type: type = pattern_type %U.binding.as_type [symbolic = %pattern_type (constants.%pattern_type.349)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete: = require_complete_type %U.binding.as_type [symbolic = %require_complete (constants.%require_complete.d55)] // CHECK:STDOUT: %TakesTypeDeduced.specific_fn.loc5_3.2: = specific_function constants.%TakesTypeDeduced, @TakesTypeDeduced(%U.binding.as_type) [symbolic = %TakesTypeDeduced.specific_fn.loc5_3.2 (constants.%TakesTypeDeduced.specific_fn)] // CHECK:STDOUT: // CHECK:STDOUT: fn(%y.param: @CallsWithExtraWhere.%U.binding.as_type (%U.binding.as_type)) { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %TakesTypeDeduced.ref: %TakesTypeDeduced.type = name_ref TakesTypeDeduced, file.%TakesTypeDeduced.decl [concrete = constants.%TakesTypeDeduced] // CHECK:STDOUT: %y.ref: @CallsWithExtraWhere.%U.binding.as_type (%U.binding.as_type) = name_ref y, %y // CHECK:STDOUT: %TakesTypeDeduced.specific_fn.loc5_3.1: = specific_function %TakesTypeDeduced.ref, @TakesTypeDeduced(constants.%U.binding.as_type) [symbolic = %TakesTypeDeduced.specific_fn.loc5_3.2 (constants.%TakesTypeDeduced.specific_fn)] // CHECK:STDOUT: %TakesTypeDeduced.call: init %empty_tuple.type = call %TakesTypeDeduced.specific_fn.loc5_3.1(%y.ref) // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic fn @TakesTypeExplicit(%T.loc8_29.3: type) { // CHECK:STDOUT: %T.loc8_29.1: type = symbolic_binding T, 0 [symbolic = %T.loc8_29.1 (constants.%T.67db0b.1)] // CHECK:STDOUT: %T.loc8_29.2: type = symbolic_binding T, 0 [symbolic = %T.loc8_29.2 (constants.%T.67db0b.2)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: // CHECK:STDOUT: fn() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic fn @CallsWithExtraWhereExplicit(%U.loc9_32.2: %type) { // CHECK:STDOUT: %U.loc9_32.1: %type = symbolic_binding U, 0 [symbolic = %U.loc9_32.1 (constants.%U)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %U.binding.as_type: type = symbolic_binding_type U, 0, %U.loc9_32.1 [symbolic = %U.binding.as_type (constants.%U.binding.as_type)] // CHECK:STDOUT: %TakesTypeExplicit.specific_fn.loc10_3.2: = specific_function constants.%TakesTypeExplicit, @TakesTypeExplicit(%U.binding.as_type) [symbolic = %TakesTypeExplicit.specific_fn.loc10_3.2 (constants.%TakesTypeExplicit.specific_fn)] // CHECK:STDOUT: // CHECK:STDOUT: fn() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %TakesTypeExplicit.ref: %TakesTypeExplicit.type = name_ref TakesTypeExplicit, file.%TakesTypeExplicit.decl [concrete = constants.%TakesTypeExplicit] // CHECK:STDOUT: %U.ref: %type = name_ref U, %U.loc9_32.2 [symbolic = %U.loc9_32.1 (constants.%U)] // CHECK:STDOUT: %U.as_type: type = facet_access_type %U.ref [symbolic = %U.binding.as_type (constants.%U.binding.as_type)] // CHECK:STDOUT: %.loc10: type = converted %U.ref, %U.as_type [symbolic = %U.binding.as_type (constants.%U.binding.as_type)] // CHECK:STDOUT: %TakesTypeExplicit.specific_fn.loc10_3.1: = specific_function %TakesTypeExplicit.ref, @TakesTypeExplicit(constants.%U.binding.as_type) [symbolic = %TakesTypeExplicit.specific_fn.loc10_3.2 (constants.%TakesTypeExplicit.specific_fn)] // CHECK:STDOUT: %TakesTypeExplicit.call: init %empty_tuple.type = call %TakesTypeExplicit.specific_fn.loc10_3.1() // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @TakesTypeDeduced(constants.%T.67db0b.1) { // CHECK:STDOUT: %T.loc3_21.1 => constants.%T.67db0b.1 // CHECK:STDOUT: %pattern_type => constants.%pattern_type.51d // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @CallsWithExtraWhere(constants.%U) { // CHECK:STDOUT: %U.loc4_24.1 => constants.%U // CHECK:STDOUT: %U.binding.as_type => constants.%U.binding.as_type // CHECK:STDOUT: %pattern_type => constants.%pattern_type.349 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @TakesTypeDeduced(constants.%U.binding.as_type) { // CHECK:STDOUT: %T.loc3_21.1 => constants.%U.binding.as_type // CHECK:STDOUT: %pattern_type => constants.%pattern_type.349 // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete => constants.%require_complete.d55 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @TakesTypeExplicit(constants.%T.67db0b.2) { // CHECK:STDOUT: %T.loc8_29.1 => constants.%T.67db0b.2 // CHECK:STDOUT: %T.loc8_29.2 => constants.%T.67db0b.2 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @CallsWithExtraWhereExplicit(constants.%U) { // CHECK:STDOUT: %U.loc9_32.1 => constants.%U // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @TakesTypeExplicit(constants.%U.binding.as_type) { // CHECK:STDOUT: %T.loc8_29.1 => constants.%U.binding.as_type // CHECK:STDOUT: %T.loc8_29.2 => constants.%U.binding.as_type // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- no_interfaces.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %type: type = facet_type [concrete] // CHECK:STDOUT: %.Self.c39: %type = symbolic_binding .Self [symbolic_self] // CHECK:STDOUT: %.Self.16f: type = symbolic_binding .Self [symbolic_self] // CHECK:STDOUT: %pattern_type.9a5: type = pattern_type %type [concrete] // CHECK:STDOUT: %T.f45530.1: %type = symbolic_binding T, 0 [symbolic] // CHECK:STDOUT: %T.binding.as_type: type = symbolic_binding_type T, 0, %T.f45530.1 [symbolic] // CHECK:STDOUT: %pattern_type.349: type = pattern_type %T.binding.as_type [symbolic] // CHECK:STDOUT: %TakesExtraWhereDeduced.type: type = fn_type @TakesExtraWhereDeduced [concrete] // CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] // CHECK:STDOUT: %TakesExtraWhereDeduced: %TakesExtraWhereDeduced.type = struct_value () [concrete] // CHECK:STDOUT: %require_complete.d55: = require_complete_type %T.binding.as_type [symbolic] // CHECK:STDOUT: %pattern_type.98f: type = pattern_type type [concrete] // CHECK:STDOUT: %U: type = symbolic_binding U, 0 [symbolic] // CHECK:STDOUT: %pattern_type.51d: type = pattern_type %U [symbolic] // CHECK:STDOUT: %CallsWithType.type: type = fn_type @CallsWithType [concrete] // CHECK:STDOUT: %CallsWithType: %CallsWithType.type = struct_value () [concrete] // CHECK:STDOUT: %require_complete.944: = require_complete_type %U [symbolic] // CHECK:STDOUT: %facet_value: %type = facet_value %U, () [symbolic] // CHECK:STDOUT: %TakesExtraWhereDeduced.specific_fn: = specific_function %TakesExtraWhereDeduced, @TakesExtraWhereDeduced(%facet_value) [symbolic] // CHECK:STDOUT: %T.f45530.2: %type = symbolic_binding T, 0 [symbolic] // CHECK:STDOUT: %TakesExtraWhereExplicit.type: type = fn_type @TakesExtraWhereExplicit [concrete] // CHECK:STDOUT: %TakesExtraWhereExplicit: %TakesExtraWhereExplicit.type = struct_value () [concrete] // CHECK:STDOUT: %CallsWithTypeExplicit.type: type = fn_type @CallsWithTypeExplicit [concrete] // CHECK:STDOUT: %CallsWithTypeExplicit: %CallsWithTypeExplicit.type = struct_value () [concrete] // CHECK:STDOUT: %TakesExtraWhereExplicit.specific_fn: = specific_function %TakesExtraWhereExplicit, @TakesExtraWhereExplicit(%facet_value) [symbolic] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .TakesExtraWhereDeduced = %TakesExtraWhereDeduced.decl // CHECK:STDOUT: .CallsWithType = %CallsWithType.decl // CHECK:STDOUT: .TakesExtraWhereExplicit = %TakesExtraWhereExplicit.decl // CHECK:STDOUT: .CallsWithTypeExplicit = %CallsWithTypeExplicit.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %TakesExtraWhereDeduced.decl: %TakesExtraWhereDeduced.type = fn_decl @TakesExtraWhereDeduced [concrete = constants.%TakesExtraWhereDeduced] { // CHECK:STDOUT: %T.patt: %pattern_type.9a5 = symbolic_binding_pattern T, 0 [concrete] // CHECK:STDOUT: %x.patt: @TakesExtraWhereDeduced.%pattern_type (%pattern_type.349) = value_binding_pattern x [concrete] // CHECK:STDOUT: %x.param_patt: @TakesExtraWhereDeduced.%pattern_type (%pattern_type.349) = value_param_pattern %x.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %.loc3_36.1: type = splice_block %.loc3_36.2 [concrete = constants.%type] { // CHECK:STDOUT: %.Self.1: %type = symbolic_binding .Self [symbolic_self = constants.%.Self.c39] // CHECK:STDOUT: %.loc3_31: type = type_literal type [concrete = type] // CHECK:STDOUT: %.Self.2: type = symbolic_binding .Self [symbolic_self = constants.%.Self.16f] // CHECK:STDOUT: %.Self.ref: type = name_ref .Self, %.Self.2 [symbolic_self = constants.%.Self.16f] // CHECK:STDOUT: %.loc3_54: type = type_literal type [concrete = type] // CHECK:STDOUT: %.loc3_36.2: type = where_expr %.Self.2 [concrete = constants.%type] { // CHECK:STDOUT: requirement_base_facet_type type // CHECK:STDOUT: requirement_impls %.Self.ref, %.loc3_54 // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: %T.loc3_27.2: %type = symbolic_binding T, 0 [symbolic = %T.loc3_27.1 (constants.%T.f45530.1)] // CHECK:STDOUT: %x.param: @TakesExtraWhereDeduced.%T.binding.as_type (%T.binding.as_type) = value_param call_param0 // CHECK:STDOUT: %.loc3_70.1: type = splice_block %.loc3_70.2 [symbolic = %T.binding.as_type (constants.%T.binding.as_type)] { // CHECK:STDOUT: %T.ref: %type = name_ref T, %T.loc3_27.2 [symbolic = %T.loc3_27.1 (constants.%T.f45530.1)] // CHECK:STDOUT: %T.as_type: type = facet_access_type %T.ref [symbolic = %T.binding.as_type (constants.%T.binding.as_type)] // CHECK:STDOUT: %.loc3_70.2: type = converted %T.ref, %T.as_type [symbolic = %T.binding.as_type (constants.%T.binding.as_type)] // CHECK:STDOUT: } // CHECK:STDOUT: %x: @TakesExtraWhereDeduced.%T.binding.as_type (%T.binding.as_type) = value_binding x, %x.param // CHECK:STDOUT: } // CHECK:STDOUT: %CallsWithType.decl: %CallsWithType.type = fn_decl @CallsWithType [concrete = constants.%CallsWithType] { // CHECK:STDOUT: %U.patt: %pattern_type.98f = symbolic_binding_pattern U, 0 [concrete] // CHECK:STDOUT: %y.patt: @CallsWithType.%pattern_type (%pattern_type.51d) = value_binding_pattern y [concrete] // CHECK:STDOUT: %y.param_patt: @CallsWithType.%pattern_type (%pattern_type.51d) = value_param_pattern %y.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %.loc4_22.1: type = splice_block %.loc4_22.2 [concrete = type] { // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self = constants.%.Self.c39] // CHECK:STDOUT: %.loc4_22.2: type = type_literal type [concrete = type] // CHECK:STDOUT: } // CHECK:STDOUT: %U.loc4_18.2: type = symbolic_binding U, 0 [symbolic = %U.loc4_18.1 (constants.%U)] // CHECK:STDOUT: %y.param: @CallsWithType.%U.loc4_18.1 (%U) = value_param call_param0 // CHECK:STDOUT: %U.ref: type = name_ref U, %U.loc4_18.2 [symbolic = %U.loc4_18.1 (constants.%U)] // CHECK:STDOUT: %y: @CallsWithType.%U.loc4_18.1 (%U) = value_binding y, %y.param // CHECK:STDOUT: } // CHECK:STDOUT: %TakesExtraWhereExplicit.decl: %TakesExtraWhereExplicit.type = fn_decl @TakesExtraWhereExplicit [concrete = constants.%TakesExtraWhereExplicit] { // CHECK:STDOUT: %T.patt: %pattern_type.9a5 = symbolic_binding_pattern T, 0 [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %.loc8_44.1: type = splice_block %.loc8_44.2 [concrete = constants.%type] { // CHECK:STDOUT: %.Self.1: %type = symbolic_binding .Self [symbolic_self = constants.%.Self.c39] // CHECK:STDOUT: %.loc8_39: type = type_literal type [concrete = type] // CHECK:STDOUT: %.Self.2: type = symbolic_binding .Self [symbolic_self = constants.%.Self.16f] // CHECK:STDOUT: %.Self.ref: type = name_ref .Self, %.Self.2 [symbolic_self = constants.%.Self.16f] // CHECK:STDOUT: %.loc8_62: type = type_literal type [concrete = type] // CHECK:STDOUT: %.loc8_44.2: type = where_expr %.Self.2 [concrete = constants.%type] { // CHECK:STDOUT: requirement_base_facet_type type // CHECK:STDOUT: requirement_impls %.Self.ref, %.loc8_62 // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: %T.loc8_35.3: %type = symbolic_binding T, 0 [symbolic = %T.loc8_35.2 (constants.%T.f45530.2)] // CHECK:STDOUT: } // CHECK:STDOUT: %CallsWithTypeExplicit.decl: %CallsWithTypeExplicit.type = fn_decl @CallsWithTypeExplicit [concrete = constants.%CallsWithTypeExplicit] { // CHECK:STDOUT: %U.patt: %pattern_type.98f = symbolic_binding_pattern U, 0 [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %.loc9_30.1: type = splice_block %.loc9_30.2 [concrete = type] { // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self = constants.%.Self.c39] // CHECK:STDOUT: %.loc9_30.2: type = type_literal type [concrete = type] // CHECK:STDOUT: } // CHECK:STDOUT: %U.loc9_26.2: type = symbolic_binding U, 0 [symbolic = %U.loc9_26.1 (constants.%U)] // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic fn @TakesExtraWhereDeduced(%T.loc3_27.2: %type) { // CHECK:STDOUT: %T.loc3_27.1: %type = symbolic_binding T, 0 [symbolic = %T.loc3_27.1 (constants.%T.f45530.1)] // CHECK:STDOUT: %T.binding.as_type: type = symbolic_binding_type T, 0, %T.loc3_27.1 [symbolic = %T.binding.as_type (constants.%T.binding.as_type)] // CHECK:STDOUT: %pattern_type: type = pattern_type %T.binding.as_type [symbolic = %pattern_type (constants.%pattern_type.349)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete: = require_complete_type %T.binding.as_type [symbolic = %require_complete (constants.%require_complete.d55)] // CHECK:STDOUT: // CHECK:STDOUT: fn(%x.param: @TakesExtraWhereDeduced.%T.binding.as_type (%T.binding.as_type)) { // CHECK:STDOUT: !entry: // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic fn @CallsWithType(%U.loc4_18.2: type) { // CHECK:STDOUT: %U.loc4_18.1: type = symbolic_binding U, 0 [symbolic = %U.loc4_18.1 (constants.%U)] // CHECK:STDOUT: %pattern_type: type = pattern_type %U.loc4_18.1 [symbolic = %pattern_type (constants.%pattern_type.51d)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete: = require_complete_type %U.loc4_18.1 [symbolic = %require_complete (constants.%require_complete.944)] // CHECK:STDOUT: %facet_value.loc5_27.2: %type = facet_value %U.loc4_18.1, () [symbolic = %facet_value.loc5_27.2 (constants.%facet_value)] // CHECK:STDOUT: %TakesExtraWhereDeduced.specific_fn.loc5_3.2: = specific_function constants.%TakesExtraWhereDeduced, @TakesExtraWhereDeduced(%facet_value.loc5_27.2) [symbolic = %TakesExtraWhereDeduced.specific_fn.loc5_3.2 (constants.%TakesExtraWhereDeduced.specific_fn)] // CHECK:STDOUT: // CHECK:STDOUT: fn(%y.param: @CallsWithType.%U.loc4_18.1 (%U)) { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %TakesExtraWhereDeduced.ref: %TakesExtraWhereDeduced.type = name_ref TakesExtraWhereDeduced, file.%TakesExtraWhereDeduced.decl [concrete = constants.%TakesExtraWhereDeduced] // CHECK:STDOUT: %y.ref: @CallsWithType.%U.loc4_18.1 (%U) = name_ref y, %y // CHECK:STDOUT: %facet_value.loc5_27.1: %type = facet_value constants.%U, () [symbolic = %facet_value.loc5_27.2 (constants.%facet_value)] // CHECK:STDOUT: %.loc5: %type = converted constants.%U, %facet_value.loc5_27.1 [symbolic = %facet_value.loc5_27.2 (constants.%facet_value)] // CHECK:STDOUT: %TakesExtraWhereDeduced.specific_fn.loc5_3.1: = specific_function %TakesExtraWhereDeduced.ref, @TakesExtraWhereDeduced(constants.%facet_value) [symbolic = %TakesExtraWhereDeduced.specific_fn.loc5_3.2 (constants.%TakesExtraWhereDeduced.specific_fn)] // CHECK:STDOUT: %TakesExtraWhereDeduced.call: init %empty_tuple.type = call %TakesExtraWhereDeduced.specific_fn.loc5_3.1(%y.ref) // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic fn @TakesExtraWhereExplicit(%T.loc8_35.3: %type) { // CHECK:STDOUT: %T.loc8_35.1: %type = symbolic_binding T, 0 [symbolic = %T.loc8_35.1 (constants.%T.f45530.1)] // CHECK:STDOUT: %T.loc8_35.2: %type = symbolic_binding T, 0 [symbolic = %T.loc8_35.2 (constants.%T.f45530.2)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: // CHECK:STDOUT: fn() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic fn @CallsWithTypeExplicit(%U.loc9_26.2: type) { // CHECK:STDOUT: %U.loc9_26.1: type = symbolic_binding U, 0 [symbolic = %U.loc9_26.1 (constants.%U)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %facet_value.loc10_28.2: %type = facet_value %U.loc9_26.1, () [symbolic = %facet_value.loc10_28.2 (constants.%facet_value)] // CHECK:STDOUT: %TakesExtraWhereExplicit.specific_fn.loc10_3.2: = specific_function constants.%TakesExtraWhereExplicit, @TakesExtraWhereExplicit(%facet_value.loc10_28.2) [symbolic = %TakesExtraWhereExplicit.specific_fn.loc10_3.2 (constants.%TakesExtraWhereExplicit.specific_fn)] // CHECK:STDOUT: // CHECK:STDOUT: fn() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %TakesExtraWhereExplicit.ref: %TakesExtraWhereExplicit.type = name_ref TakesExtraWhereExplicit, file.%TakesExtraWhereExplicit.decl [concrete = constants.%TakesExtraWhereExplicit] // CHECK:STDOUT: %U.ref: type = name_ref U, %U.loc9_26.2 [symbolic = %U.loc9_26.1 (constants.%U)] // CHECK:STDOUT: %facet_value.loc10_28.1: %type = facet_value %U.ref, () [symbolic = %facet_value.loc10_28.2 (constants.%facet_value)] // CHECK:STDOUT: %.loc10: %type = converted %U.ref, %facet_value.loc10_28.1 [symbolic = %facet_value.loc10_28.2 (constants.%facet_value)] // CHECK:STDOUT: %TakesExtraWhereExplicit.specific_fn.loc10_3.1: = specific_function %TakesExtraWhereExplicit.ref, @TakesExtraWhereExplicit(constants.%facet_value) [symbolic = %TakesExtraWhereExplicit.specific_fn.loc10_3.2 (constants.%TakesExtraWhereExplicit.specific_fn)] // CHECK:STDOUT: %TakesExtraWhereExplicit.call: init %empty_tuple.type = call %TakesExtraWhereExplicit.specific_fn.loc10_3.1() // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @TakesExtraWhereDeduced(constants.%T.f45530.1) { // CHECK:STDOUT: %T.loc3_27.1 => constants.%T.f45530.1 // CHECK:STDOUT: %T.binding.as_type => constants.%T.binding.as_type // CHECK:STDOUT: %pattern_type => constants.%pattern_type.349 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @CallsWithType(constants.%U) { // CHECK:STDOUT: %U.loc4_18.1 => constants.%U // CHECK:STDOUT: %pattern_type => constants.%pattern_type.51d // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @TakesExtraWhereDeduced(constants.%facet_value) { // CHECK:STDOUT: %T.loc3_27.1 => constants.%facet_value // CHECK:STDOUT: %T.binding.as_type => constants.%U // CHECK:STDOUT: %pattern_type => constants.%pattern_type.51d // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete => constants.%require_complete.944 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @TakesExtraWhereExplicit(constants.%T.f45530.2) { // CHECK:STDOUT: %T.loc8_35.1 => constants.%T.f45530.2 // CHECK:STDOUT: %T.loc8_35.2 => constants.%T.f45530.2 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @CallsWithTypeExplicit(constants.%U) { // CHECK:STDOUT: %U.loc9_26.1 => constants.%U // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @TakesExtraWhereExplicit(constants.%facet_value) { // CHECK:STDOUT: %T.loc8_35.1 => constants.%facet_value // CHECK:STDOUT: %T.loc8_35.2 => constants.%facet_value // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: } // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/facet/convert_facet_value_value_to_blanket_impl.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // TODO: Add ranges and switch to "--dump-sem-ir-ranges=only". // EXTRA-ARGS: --dump-sem-ir-ranges=if-present // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/convert.carbon // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/facet/convert_facet_value_value_to_blanket_impl.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/facet/convert_facet_value_value_to_blanket_impl.carbon interface Eats {} interface Animal {} impl forall [A:! Animal] A as Eats {} fn Feed[T:! Eats](unused e: T) {} fn HandleAnimal[T:! Animal](a: T) { Feed(a); } // CHECK:STDOUT: --- convert_facet_value_value_to_blanket_impl.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %Eats.type: type = facet_type <@Eats> [concrete] // CHECK:STDOUT: %Self.247: %Eats.type = symbolic_binding Self, 0 [symbolic] // CHECK:STDOUT: %Animal.type: type = facet_type <@Animal> [concrete] // CHECK:STDOUT: %Self.c49: %Animal.type = symbolic_binding Self, 0 [symbolic] // CHECK:STDOUT: %type: type = facet_type [concrete] // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self] // CHECK:STDOUT: %pattern_type.e10: type = pattern_type %Animal.type [concrete] // CHECK:STDOUT: %A: %Animal.type = symbolic_binding A, 0 [symbolic] // CHECK:STDOUT: %A.binding.as_type: type = symbolic_binding_type A, 0, %A [symbolic] // CHECK:STDOUT: %Eats.impl_witness.760d13.1: = impl_witness @A.binding.as_type.as.Eats.impl.%Eats.impl_witness_table, @A.binding.as_type.as.Eats.impl(%A) [symbolic] // CHECK:STDOUT: %Eats.facet.6e5: %Eats.type = facet_value %A.binding.as_type, (%Eats.impl_witness.760d13.1) [symbolic] // CHECK:STDOUT: %pattern_type.0dc: type = pattern_type %Eats.type [concrete] // CHECK:STDOUT: %T.f11: %Eats.type = symbolic_binding T, 0 [symbolic] // CHECK:STDOUT: %T.binding.as_type.265: type = symbolic_binding_type T, 0, %T.f11 [symbolic] // CHECK:STDOUT: %pattern_type.93e: type = pattern_type %T.binding.as_type.265 [symbolic] // CHECK:STDOUT: %Feed.type: type = fn_type @Feed [concrete] // CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] // CHECK:STDOUT: %Feed: %Feed.type = struct_value () [concrete] // CHECK:STDOUT: %require_complete.9d9: = require_complete_type %T.binding.as_type.265 [symbolic] // CHECK:STDOUT: %T.998: %Animal.type = symbolic_binding T, 0 [symbolic] // CHECK:STDOUT: %T.binding.as_type.e4f: type = symbolic_binding_type T, 0, %T.998 [symbolic] // CHECK:STDOUT: %pattern_type.892: type = pattern_type %T.binding.as_type.e4f [symbolic] // CHECK:STDOUT: %HandleAnimal.type: type = fn_type @HandleAnimal [concrete] // CHECK:STDOUT: %HandleAnimal: %HandleAnimal.type = struct_value () [concrete] // CHECK:STDOUT: %require_complete.72f: = require_complete_type %T.binding.as_type.e4f [symbolic] // CHECK:STDOUT: %Eats.impl_witness.760d13.2: = impl_witness @A.binding.as_type.as.Eats.impl.%Eats.impl_witness_table, @A.binding.as_type.as.Eats.impl(%T.998) [symbolic] // CHECK:STDOUT: %.860: require_specific_def_type = require_specific_def @A.binding.as_type.as.Eats.impl(%T.998) [symbolic] // CHECK:STDOUT: %Eats.lookup_impl_witness: = lookup_impl_witness %T.998, @Eats [symbolic] // CHECK:STDOUT: %Eats.facet.a05: %Eats.type = facet_value %T.binding.as_type.e4f, (%Eats.lookup_impl_witness) [symbolic] // CHECK:STDOUT: %Feed.specific_fn: = specific_function %Feed, @Feed(%Eats.facet.a05) [symbolic] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .Eats = %Eats.decl // CHECK:STDOUT: .Animal = %Animal.decl // CHECK:STDOUT: .Feed = %Feed.decl // CHECK:STDOUT: .HandleAnimal = %HandleAnimal.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %Eats.decl: type = interface_decl @Eats [concrete = constants.%Eats.type] {} {} // CHECK:STDOUT: %Animal.decl: type = interface_decl @Animal [concrete = constants.%Animal.type] {} {} // CHECK:STDOUT: impl_decl @A.binding.as_type.as.Eats.impl [concrete] { // CHECK:STDOUT: %A.patt: %pattern_type.e10 = symbolic_binding_pattern A, 0 [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %A.ref: %Animal.type = name_ref A, %A.loc18_14.1 [symbolic = %A.loc18_14.2 (constants.%A)] // CHECK:STDOUT: %A.as_type: type = facet_access_type %A.ref [symbolic = %A.binding.as_type (constants.%A.binding.as_type)] // CHECK:STDOUT: %.loc18_26: type = converted %A.ref, %A.as_type [symbolic = %A.binding.as_type (constants.%A.binding.as_type)] // CHECK:STDOUT: %Eats.ref: type = name_ref Eats, file.%Eats.decl [concrete = constants.%Eats.type] // CHECK:STDOUT: %.loc18_18: type = splice_block %Animal.ref [concrete = constants.%Animal.type] { // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %Animal.ref: type = name_ref Animal, file.%Animal.decl [concrete = constants.%Animal.type] // CHECK:STDOUT: } // CHECK:STDOUT: %A.loc18_14.1: %Animal.type = symbolic_binding A, 0 [symbolic = %A.loc18_14.2 (constants.%A)] // CHECK:STDOUT: } // CHECK:STDOUT: %Feed.decl: %Feed.type = fn_decl @Feed [concrete = constants.%Feed] { // CHECK:STDOUT: %T.patt: %pattern_type.0dc = symbolic_binding_pattern T, 0 [concrete] // CHECK:STDOUT: %e.patt: @Feed.%pattern_type (%pattern_type.93e) = value_binding_pattern e [concrete] // CHECK:STDOUT: %e.param_patt: @Feed.%pattern_type (%pattern_type.93e) = value_param_pattern %e.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %.loc20_13: type = splice_block %Eats.ref [concrete = constants.%Eats.type] { // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %Eats.ref: type = name_ref Eats, file.%Eats.decl [concrete = constants.%Eats.type] // CHECK:STDOUT: } // CHECK:STDOUT: %T.loc20_9.2: %Eats.type = symbolic_binding T, 0 [symbolic = %T.loc20_9.1 (constants.%T.f11)] // CHECK:STDOUT: %e.param: @Feed.%T.binding.as_type (%T.binding.as_type.265) = value_param call_param0 // CHECK:STDOUT: %.loc20_29.1: type = splice_block %.loc20_29.2 [symbolic = %T.binding.as_type (constants.%T.binding.as_type.265)] { // CHECK:STDOUT: %T.ref: %Eats.type = name_ref T, %T.loc20_9.2 [symbolic = %T.loc20_9.1 (constants.%T.f11)] // CHECK:STDOUT: %T.as_type: type = facet_access_type %T.ref [symbolic = %T.binding.as_type (constants.%T.binding.as_type.265)] // CHECK:STDOUT: %.loc20_29.2: type = converted %T.ref, %T.as_type [symbolic = %T.binding.as_type (constants.%T.binding.as_type.265)] // CHECK:STDOUT: } // CHECK:STDOUT: %e: @Feed.%T.binding.as_type (%T.binding.as_type.265) = value_binding e, %e.param // CHECK:STDOUT: } // CHECK:STDOUT: %HandleAnimal.decl: %HandleAnimal.type = fn_decl @HandleAnimal [concrete = constants.%HandleAnimal] { // CHECK:STDOUT: %T.patt: %pattern_type.e10 = symbolic_binding_pattern T, 0 [concrete] // CHECK:STDOUT: %a.patt: @HandleAnimal.%pattern_type (%pattern_type.892) = value_binding_pattern a [concrete] // CHECK:STDOUT: %a.param_patt: @HandleAnimal.%pattern_type (%pattern_type.892) = value_param_pattern %a.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %.loc22_21: type = splice_block %Animal.ref [concrete = constants.%Animal.type] { // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %Animal.ref: type = name_ref Animal, file.%Animal.decl [concrete = constants.%Animal.type] // CHECK:STDOUT: } // CHECK:STDOUT: %T.loc22_17.2: %Animal.type = symbolic_binding T, 0 [symbolic = %T.loc22_17.1 (constants.%T.998)] // CHECK:STDOUT: %a.param: @HandleAnimal.%T.binding.as_type (%T.binding.as_type.e4f) = value_param call_param0 // CHECK:STDOUT: %.loc22_32.1: type = splice_block %.loc22_32.2 [symbolic = %T.binding.as_type (constants.%T.binding.as_type.e4f)] { // CHECK:STDOUT: %T.ref: %Animal.type = name_ref T, %T.loc22_17.2 [symbolic = %T.loc22_17.1 (constants.%T.998)] // CHECK:STDOUT: %T.as_type: type = facet_access_type %T.ref [symbolic = %T.binding.as_type (constants.%T.binding.as_type.e4f)] // CHECK:STDOUT: %.loc22_32.2: type = converted %T.ref, %T.as_type [symbolic = %T.binding.as_type (constants.%T.binding.as_type.e4f)] // CHECK:STDOUT: } // CHECK:STDOUT: %a: @HandleAnimal.%T.binding.as_type (%T.binding.as_type.e4f) = value_binding a, %a.param // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: interface @Eats { // CHECK:STDOUT: %Self: %Eats.type = symbolic_binding Self, 0 [symbolic = constants.%Self.247] // CHECK:STDOUT: %Eats.WithSelf.decl = interface_with_self_decl @Eats [concrete] // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = %Self // CHECK:STDOUT: witness = () // CHECK:STDOUT: // CHECK:STDOUT: !requires: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: interface @Animal { // CHECK:STDOUT: %Self: %Animal.type = symbolic_binding Self, 0 [symbolic = constants.%Self.c49] // CHECK:STDOUT: %Animal.WithSelf.decl = interface_with_self_decl @Animal [concrete] // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = %Self // CHECK:STDOUT: witness = () // CHECK:STDOUT: // CHECK:STDOUT: !requires: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic impl @A.binding.as_type.as.Eats.impl(%A.loc18_14.1: %Animal.type) { // CHECK:STDOUT: %A.loc18_14.2: %Animal.type = symbolic_binding A, 0 [symbolic = %A.loc18_14.2 (constants.%A)] // CHECK:STDOUT: %A.binding.as_type: type = symbolic_binding_type A, 0, %A.loc18_14.2 [symbolic = %A.binding.as_type (constants.%A.binding.as_type)] // CHECK:STDOUT: %Eats.impl_witness.loc18_36.2: = impl_witness %Eats.impl_witness_table, @A.binding.as_type.as.Eats.impl(%A.loc18_14.2) [symbolic = %Eats.impl_witness.loc18_36.2 (constants.%Eats.impl_witness.760d13.1)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: // CHECK:STDOUT: impl: %.loc18_26 as %Eats.ref { // CHECK:STDOUT: %Eats.impl_witness_table = impl_witness_table (), @A.binding.as_type.as.Eats.impl [concrete] // CHECK:STDOUT: %Eats.impl_witness.loc18_36.1: = impl_witness %Eats.impl_witness_table, @A.binding.as_type.as.Eats.impl(constants.%A) [symbolic = %Eats.impl_witness.loc18_36.2 (constants.%Eats.impl_witness.760d13.1)] // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: witness = %Eats.impl_witness.loc18_36.1 // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic fn @Feed(%T.loc20_9.2: %Eats.type) { // CHECK:STDOUT: %T.loc20_9.1: %Eats.type = symbolic_binding T, 0 [symbolic = %T.loc20_9.1 (constants.%T.f11)] // CHECK:STDOUT: %T.binding.as_type: type = symbolic_binding_type T, 0, %T.loc20_9.1 [symbolic = %T.binding.as_type (constants.%T.binding.as_type.265)] // CHECK:STDOUT: %pattern_type: type = pattern_type %T.binding.as_type [symbolic = %pattern_type (constants.%pattern_type.93e)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete: = require_complete_type %T.binding.as_type [symbolic = %require_complete (constants.%require_complete.9d9)] // CHECK:STDOUT: // CHECK:STDOUT: fn(%e.param: @Feed.%T.binding.as_type (%T.binding.as_type.265)) { // CHECK:STDOUT: !entry: // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic fn @HandleAnimal(%T.loc22_17.2: %Animal.type) { // CHECK:STDOUT: %T.loc22_17.1: %Animal.type = symbolic_binding T, 0 [symbolic = %T.loc22_17.1 (constants.%T.998)] // CHECK:STDOUT: %T.binding.as_type: type = symbolic_binding_type T, 0, %T.loc22_17.1 [symbolic = %T.binding.as_type (constants.%T.binding.as_type.e4f)] // CHECK:STDOUT: %pattern_type: type = pattern_type %T.binding.as_type [symbolic = %pattern_type (constants.%pattern_type.892)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete: = require_complete_type %T.binding.as_type [symbolic = %require_complete (constants.%require_complete.72f)] // CHECK:STDOUT: %.loc22_43.2: require_specific_def_type = require_specific_def @A.binding.as_type.as.Eats.impl(%T.loc22_17.1) [symbolic = %.loc22_43.2 (constants.%.860)] // CHECK:STDOUT: %Eats.lookup_impl_witness: = lookup_impl_witness %T.loc22_17.1, @Eats [symbolic = %Eats.lookup_impl_witness (constants.%Eats.lookup_impl_witness)] // CHECK:STDOUT: %Eats.facet.loc22_43.2: %Eats.type = facet_value %T.binding.as_type, (%Eats.lookup_impl_witness) [symbolic = %Eats.facet.loc22_43.2 (constants.%Eats.facet.a05)] // CHECK:STDOUT: %Feed.specific_fn.loc22_37.2: = specific_function constants.%Feed, @Feed(%Eats.facet.loc22_43.2) [symbolic = %Feed.specific_fn.loc22_37.2 (constants.%Feed.specific_fn)] // CHECK:STDOUT: // CHECK:STDOUT: fn(%a.param: @HandleAnimal.%T.binding.as_type (%T.binding.as_type.e4f)) { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %Feed.ref: %Feed.type = name_ref Feed, file.%Feed.decl [concrete = constants.%Feed] // CHECK:STDOUT: %a.ref: @HandleAnimal.%T.binding.as_type (%T.binding.as_type.e4f) = name_ref a, %a // CHECK:STDOUT: %Eats.facet.loc22_43.1: %Eats.type = facet_value constants.%T.binding.as_type.e4f, (constants.%Eats.lookup_impl_witness) [symbolic = %Eats.facet.loc22_43.2 (constants.%Eats.facet.a05)] // CHECK:STDOUT: %.loc22_43.1: %Eats.type = converted constants.%T.binding.as_type.e4f, %Eats.facet.loc22_43.1 [symbolic = %Eats.facet.loc22_43.2 (constants.%Eats.facet.a05)] // CHECK:STDOUT: %Feed.specific_fn.loc22_37.1: = specific_function %Feed.ref, @Feed(constants.%Eats.facet.a05) [symbolic = %Feed.specific_fn.loc22_37.2 (constants.%Feed.specific_fn)] // CHECK:STDOUT: %Feed.call: init %empty_tuple.type = call %Feed.specific_fn.loc22_37.1(%a.ref) // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @Eats.WithSelf(constants.%Self.247) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @Animal.WithSelf(constants.%Self.c49) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @A.binding.as_type.as.Eats.impl(constants.%A) { // CHECK:STDOUT: %A.loc18_14.2 => constants.%A // CHECK:STDOUT: %A.binding.as_type => constants.%A.binding.as_type // CHECK:STDOUT: %Eats.impl_witness.loc18_36.2 => constants.%Eats.impl_witness.760d13.1 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @Eats.WithSelf(constants.%Eats.facet.6e5) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @Feed(constants.%T.f11) { // CHECK:STDOUT: %T.loc20_9.1 => constants.%T.f11 // CHECK:STDOUT: %T.binding.as_type => constants.%T.binding.as_type.265 // CHECK:STDOUT: %pattern_type => constants.%pattern_type.93e // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @HandleAnimal(constants.%T.998) { // CHECK:STDOUT: %T.loc22_17.1 => constants.%T.998 // CHECK:STDOUT: %T.binding.as_type => constants.%T.binding.as_type.e4f // CHECK:STDOUT: %pattern_type => constants.%pattern_type.892 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @A.binding.as_type.as.Eats.impl(constants.%T.998) { // CHECK:STDOUT: %A.loc18_14.2 => constants.%T.998 // CHECK:STDOUT: %A.binding.as_type => constants.%T.binding.as_type.e4f // CHECK:STDOUT: %Eats.impl_witness.loc18_36.2 => constants.%Eats.impl_witness.760d13.2 // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @Feed(constants.%Eats.facet.a05) { // CHECK:STDOUT: %T.loc20_9.1 => constants.%Eats.facet.a05 // CHECK:STDOUT: %T.binding.as_type => constants.%T.binding.as_type.e4f // CHECK:STDOUT: %pattern_type => constants.%pattern_type.892 // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete => constants.%require_complete.72f // CHECK:STDOUT: } // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/facet/convert_facet_value_value_to_generic_facet_value_value.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // TODO: Add ranges and switch to "--dump-sem-ir-ranges=only". // EXTRA-ARGS: --dump-sem-ir-ranges=if-present // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/convert.carbon // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/facet/convert_facet_value_value_to_generic_facet_value_value.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/facet/convert_facet_value_value_to_generic_facet_value_value.carbon interface Edible {} class Grass {} impl Grass as Edible {} interface Animal {} interface Eats(Food:! type) {} // When answering a query "does Goat impl Animal", we must avoid trying to deduce // parameters for this impl. Not only is doing so unnecessary, it would start a new // "does Goat impl Animal" query, leading to a "cycle in impl lookup" error. impl forall [T:! Animal, U:! Edible] T as Eats(U) {} class Goat {} impl Goat as Animal {} fn Feed[Food:! Edible, T:! Eats(Food)](unused e: T, unused food: Food) {} fn HandleAnimal[A:! Animal, Food:! Edible](a: A, food: Food) { Feed(a, food); } fn F() { HandleAnimal({} as Goat, {} as Grass); } // CHECK:STDOUT: --- convert_facet_value_value_to_generic_facet_value_value.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %Edible.type: type = facet_type <@Edible> [concrete] // CHECK:STDOUT: %Self.461: %Edible.type = symbolic_binding Self, 0 [symbolic] // CHECK:STDOUT: %Grass: type = class_type @Grass [concrete] // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %complete_type.357: = complete_type_witness %empty_struct_type [concrete] // CHECK:STDOUT: %Edible.impl_witness: = impl_witness @Grass.as.Edible.impl.%Edible.impl_witness_table [concrete] // CHECK:STDOUT: %Edible.facet: %Edible.type = facet_value %Grass, (%Edible.impl_witness) [concrete] // CHECK:STDOUT: %Animal.type: type = facet_type <@Animal> [concrete] // CHECK:STDOUT: %Self.c49: %Animal.type = symbolic_binding Self, 0 [symbolic] // CHECK:STDOUT: %type: type = facet_type [concrete] // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self] // CHECK:STDOUT: %pattern_type.98f: type = pattern_type type [concrete] // CHECK:STDOUT: %Food.67d: type = symbolic_binding Food, 0 [symbolic] // CHECK:STDOUT: %Eats.type.321: type = generic_interface_type @Eats [concrete] // CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] // CHECK:STDOUT: %Eats.generic: %Eats.type.321 = struct_value () [concrete] // CHECK:STDOUT: %Eats.type.394: type = facet_type <@Eats, @Eats(%Food.67d)> [symbolic] // CHECK:STDOUT: %Self.857: %Eats.type.394 = symbolic_binding Self, 1 [symbolic] // CHECK:STDOUT: %pattern_type.e10: type = pattern_type %Animal.type [concrete] // CHECK:STDOUT: %T.998: %Animal.type = symbolic_binding T, 0 [symbolic] // CHECK:STDOUT: %pattern_type.b51: type = pattern_type %Edible.type [concrete] // CHECK:STDOUT: %U: %Edible.type = symbolic_binding U, 1 [symbolic] // CHECK:STDOUT: %T.binding.as_type.e4f: type = symbolic_binding_type T, 0, %T.998 [symbolic] // CHECK:STDOUT: %U.binding.as_type: type = symbolic_binding_type U, 1, %U [symbolic] // CHECK:STDOUT: %Eats.type.bb4cf6.1: type = facet_type <@Eats, @Eats(%U.binding.as_type)> [symbolic] // CHECK:STDOUT: %Eats.impl_witness.0150b7.1: = impl_witness @T.binding.as_type.as.Eats.impl.%Eats.impl_witness_table, @T.binding.as_type.as.Eats.impl(%T.998, %U) [symbolic] // CHECK:STDOUT: %Self.027: %Eats.type.bb4cf6.1 = symbolic_binding Self, 1 [symbolic] // CHECK:STDOUT: %require_complete.a2722e.1: = require_complete_type %Eats.type.bb4cf6.1 [symbolic] // CHECK:STDOUT: %Eats.facet.31f: %Eats.type.bb4cf6.1 = facet_value %T.binding.as_type.e4f, (%Eats.impl_witness.0150b7.1) [symbolic] // CHECK:STDOUT: %Goat: type = class_type @Goat [concrete] // CHECK:STDOUT: %Animal.impl_witness: = impl_witness @Goat.as.Animal.impl.%Animal.impl_witness_table [concrete] // CHECK:STDOUT: %Animal.facet: %Animal.type = facet_value %Goat, (%Animal.impl_witness) [concrete] // CHECK:STDOUT: %Food.d1c: %Edible.type = symbolic_binding Food, 0 [symbolic] // CHECK:STDOUT: %Food.binding.as_type.8cc: type = symbolic_binding_type Food, 0, %Food.d1c [symbolic] // CHECK:STDOUT: %Eats.type.570: type = facet_type <@Eats, @Eats(%Food.binding.as_type.8cc)> [symbolic] // CHECK:STDOUT: %pattern_type.350: type = pattern_type %Eats.type.570 [symbolic] // CHECK:STDOUT: %T.2b9: %Eats.type.570 = symbolic_binding T, 1 [symbolic] // CHECK:STDOUT: %Self.a66: %Eats.type.570 = symbolic_binding Self, 1 [symbolic] // CHECK:STDOUT: %T.binding.as_type.8ae: type = symbolic_binding_type T, 1, %T.2b9 [symbolic] // CHECK:STDOUT: %pattern_type.2b9: type = pattern_type %T.binding.as_type.8ae [symbolic] // CHECK:STDOUT: %pattern_type.12a: type = pattern_type %Food.binding.as_type.8cc [symbolic] // CHECK:STDOUT: %Feed.type: type = fn_type @Feed [concrete] // CHECK:STDOUT: %Feed: %Feed.type = struct_value () [concrete] // CHECK:STDOUT: %require_complete.0c1: = require_complete_type %T.binding.as_type.8ae [symbolic] // CHECK:STDOUT: %require_complete.bd7: = require_complete_type %Food.binding.as_type.8cc [symbolic] // CHECK:STDOUT: %A: %Animal.type = symbolic_binding A, 0 [symbolic] // CHECK:STDOUT: %Food.2fc: %Edible.type = symbolic_binding Food, 1 [symbolic] // CHECK:STDOUT: %A.binding.as_type: type = symbolic_binding_type A, 0, %A [symbolic] // CHECK:STDOUT: %pattern_type.892: type = pattern_type %A.binding.as_type [symbolic] // CHECK:STDOUT: %Food.binding.as_type.e7e: type = symbolic_binding_type Food, 1, %Food.2fc [symbolic] // CHECK:STDOUT: %pattern_type.9ed: type = pattern_type %Food.binding.as_type.e7e [symbolic] // CHECK:STDOUT: %HandleAnimal.type: type = fn_type @HandleAnimal [concrete] // CHECK:STDOUT: %HandleAnimal: %HandleAnimal.type = struct_value () [concrete] // CHECK:STDOUT: %require_complete.72f: = require_complete_type %A.binding.as_type [symbolic] // CHECK:STDOUT: %require_complete.0a9: = require_complete_type %Food.binding.as_type.e7e [symbolic] // CHECK:STDOUT: %Eats.type.bb4cf6.2: type = facet_type <@Eats, @Eats(%Food.binding.as_type.e7e)> [symbolic] // CHECK:STDOUT: %Eats.impl_witness.0150b7.2: = impl_witness @T.binding.as_type.as.Eats.impl.%Eats.impl_witness_table, @T.binding.as_type.as.Eats.impl(%A, %Food.2fc) [symbolic] // CHECK:STDOUT: %require_complete.a2722e.2: = require_complete_type %Eats.type.bb4cf6.2 [symbolic] // CHECK:STDOUT: %.f9e: require_specific_def_type = require_specific_def @T.binding.as_type.as.Eats.impl(%A, %Food.2fc) [symbolic] // CHECK:STDOUT: %Eats.lookup_impl_witness: = lookup_impl_witness %A, @Eats, @Eats(%Food.binding.as_type.e7e) [symbolic] // CHECK:STDOUT: %Eats.facet.702: %Eats.type.bb4cf6.2 = facet_value %A.binding.as_type, (%Eats.lookup_impl_witness) [symbolic] // CHECK:STDOUT: %pattern_type.67b: type = pattern_type %Eats.type.bb4cf6.2 [symbolic] // CHECK:STDOUT: %Feed.specific_fn.1f9: = specific_function %Feed, @Feed(%Food.2fc, %Eats.facet.702) [symbolic] // CHECK:STDOUT: %F.type: type = fn_type @F [concrete] // CHECK:STDOUT: %F: %F.type = struct_value () [concrete] // CHECK:STDOUT: %empty_struct: %empty_struct_type = struct_value () [concrete] // CHECK:STDOUT: %Goat.val: %Goat = struct_value () [concrete] // CHECK:STDOUT: %Grass.val: %Grass = struct_value () [concrete] // CHECK:STDOUT: %pattern_type.234: type = pattern_type %Goat [concrete] // CHECK:STDOUT: %pattern_type.df6: type = pattern_type %Grass [concrete] // CHECK:STDOUT: %HandleAnimal.specific_fn: = specific_function %HandleAnimal, @HandleAnimal(%Animal.facet, %Edible.facet) [concrete] // CHECK:STDOUT: %Destroy.type: type = facet_type <@Destroy> [concrete] // CHECK:STDOUT: %Destroy.Op.type.bae255.1: type = fn_type @Destroy.Op.loc35_31 [concrete] // CHECK:STDOUT: %Destroy.Op.651ba6.1: %Destroy.Op.type.bae255.1 = struct_value () [concrete] // CHECK:STDOUT: %Destroy.Op.type.bae255.2: type = fn_type @Destroy.Op.loc35_19 [concrete] // CHECK:STDOUT: %Destroy.Op.651ba6.2: %Destroy.Op.type.bae255.2 = struct_value () [concrete] // CHECK:STDOUT: %Eats.type.8c2: type = facet_type <@Eats, @Eats(%Grass)> [concrete] // CHECK:STDOUT: %Eats.impl_witness.f54: = impl_witness @T.binding.as_type.as.Eats.impl.%Eats.impl_witness_table, @T.binding.as_type.as.Eats.impl(%Animal.facet, %Edible.facet) [concrete] // CHECK:STDOUT: %Self.ebd: %Eats.type.8c2 = symbolic_binding Self, 1 [symbolic] // CHECK:STDOUT: %complete_type.cf8: = complete_type_witness %Eats.type.8c2 [concrete] // CHECK:STDOUT: %.767: require_specific_def_type = require_specific_def @T.binding.as_type.as.Eats.impl(%Animal.facet, %Edible.facet) [concrete] // CHECK:STDOUT: %Eats.facet.a4e: %Eats.type.8c2 = facet_value %Goat, (%Eats.impl_witness.f54) [concrete] // CHECK:STDOUT: %pattern_type.968: type = pattern_type %Eats.type.8c2 [concrete] // CHECK:STDOUT: %Feed.specific_fn.7dd: = specific_function %Feed, @Feed(%Edible.facet, %Eats.facet.a4e) [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: .Destroy = %Core.Destroy // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: %Core.Destroy: type = import_ref Core//prelude/parts/destroy, Destroy, loaded [concrete = constants.%Destroy.type] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .Edible = %Edible.decl // CHECK:STDOUT: .Grass = %Grass.decl // CHECK:STDOUT: .Animal = %Animal.decl // CHECK:STDOUT: .Eats = %Eats.decl // CHECK:STDOUT: .Goat = %Goat.decl // CHECK:STDOUT: .Feed = %Feed.decl // CHECK:STDOUT: .HandleAnimal = %HandleAnimal.decl // CHECK:STDOUT: .F = %F.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %Edible.decl: type = interface_decl @Edible [concrete = constants.%Edible.type] {} {} // CHECK:STDOUT: %Grass.decl: type = class_decl @Grass [concrete = constants.%Grass] {} {} // CHECK:STDOUT: impl_decl @Grass.as.Edible.impl [concrete] {} { // CHECK:STDOUT: %Grass.ref: type = name_ref Grass, file.%Grass.decl [concrete = constants.%Grass] // CHECK:STDOUT: %Edible.ref: type = name_ref Edible, file.%Edible.decl [concrete = constants.%Edible.type] // CHECK:STDOUT: } // CHECK:STDOUT: %Animal.decl: type = interface_decl @Animal [concrete = constants.%Animal.type] {} {} // CHECK:STDOUT: %Eats.decl: %Eats.type.321 = interface_decl @Eats [concrete = constants.%Eats.generic] { // CHECK:STDOUT: %Food.patt: %pattern_type.98f = symbolic_binding_pattern Food, 0 [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %.loc21_23.1: type = splice_block %.loc21_23.2 [concrete = type] { // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %.loc21_23.2: type = type_literal type [concrete = type] // CHECK:STDOUT: } // CHECK:STDOUT: %Food.loc21_16.2: type = symbolic_binding Food, 0 [symbolic = %Food.loc21_16.1 (constants.%Food.67d)] // CHECK:STDOUT: } // CHECK:STDOUT: impl_decl @T.binding.as_type.as.Eats.impl [concrete] { // CHECK:STDOUT: %T.patt: %pattern_type.e10 = symbolic_binding_pattern T, 0 [concrete] // CHECK:STDOUT: %U.patt: %pattern_type.b51 = symbolic_binding_pattern U, 1 [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %T.ref: %Animal.type = name_ref T, %T.loc26_14.1 [symbolic = %T.loc26_14.2 (constants.%T.998)] // CHECK:STDOUT: %T.as_type: type = facet_access_type %T.ref [symbolic = %T.binding.as_type (constants.%T.binding.as_type.e4f)] // CHECK:STDOUT: %.loc26_38: type = converted %T.ref, %T.as_type [symbolic = %T.binding.as_type (constants.%T.binding.as_type.e4f)] // CHECK:STDOUT: %Eats.ref: %Eats.type.321 = name_ref Eats, file.%Eats.decl [concrete = constants.%Eats.generic] // CHECK:STDOUT: %U.ref: %Edible.type = name_ref U, %U.loc26_26.1 [symbolic = %U.loc26_26.2 (constants.%U)] // CHECK:STDOUT: %U.as_type: type = facet_access_type %U.ref [symbolic = %U.binding.as_type (constants.%U.binding.as_type)] // CHECK:STDOUT: %.loc26_49: type = converted %U.ref, %U.as_type [symbolic = %U.binding.as_type (constants.%U.binding.as_type)] // CHECK:STDOUT: %Eats.type.loc26_49.1: type = facet_type <@Eats, @Eats(constants.%U.binding.as_type)> [symbolic = %Eats.type.loc26_49.2 (constants.%Eats.type.bb4cf6.1)] // CHECK:STDOUT: %.loc26_18: type = splice_block %Animal.ref [concrete = constants.%Animal.type] { // CHECK:STDOUT: %.Self.2: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %Animal.ref: type = name_ref Animal, file.%Animal.decl [concrete = constants.%Animal.type] // CHECK:STDOUT: } // CHECK:STDOUT: %T.loc26_14.1: %Animal.type = symbolic_binding T, 0 [symbolic = %T.loc26_14.2 (constants.%T.998)] // CHECK:STDOUT: %.loc26_30: type = splice_block %Edible.ref [concrete = constants.%Edible.type] { // CHECK:STDOUT: %.Self.1: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %Edible.ref: type = name_ref Edible, file.%Edible.decl [concrete = constants.%Edible.type] // CHECK:STDOUT: } // CHECK:STDOUT: %U.loc26_26.1: %Edible.type = symbolic_binding U, 1 [symbolic = %U.loc26_26.2 (constants.%U)] // CHECK:STDOUT: } // CHECK:STDOUT: %Goat.decl: type = class_decl @Goat [concrete = constants.%Goat] {} {} // CHECK:STDOUT: impl_decl @Goat.as.Animal.impl [concrete] {} { // CHECK:STDOUT: %Goat.ref: type = name_ref Goat, file.%Goat.decl [concrete = constants.%Goat] // CHECK:STDOUT: %Animal.ref: type = name_ref Animal, file.%Animal.decl [concrete = constants.%Animal.type] // CHECK:STDOUT: } // CHECK:STDOUT: %Feed.decl: %Feed.type = fn_decl @Feed [concrete = constants.%Feed] { // CHECK:STDOUT: %Food.patt: %pattern_type.b51 = symbolic_binding_pattern Food, 0 [concrete] // CHECK:STDOUT: %T.patt: @Feed.%pattern_type.loc31_24 (%pattern_type.350) = symbolic_binding_pattern T, 1 [concrete] // CHECK:STDOUT: %e.patt: @Feed.%pattern_type.loc31_47 (%pattern_type.2b9) = value_binding_pattern e [concrete] // CHECK:STDOUT: %e.param_patt: @Feed.%pattern_type.loc31_47 (%pattern_type.2b9) = value_param_pattern %e.patt [concrete] // CHECK:STDOUT: %food.patt: @Feed.%pattern_type.loc31_60 (%pattern_type.12a) = value_binding_pattern food [concrete] // CHECK:STDOUT: %food.param_patt: @Feed.%pattern_type.loc31_60 (%pattern_type.12a) = value_param_pattern %food.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %.loc31_16: type = splice_block %Edible.ref [concrete = constants.%Edible.type] { // CHECK:STDOUT: %.Self.2: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %Edible.ref: type = name_ref Edible, file.%Edible.decl [concrete = constants.%Edible.type] // CHECK:STDOUT: } // CHECK:STDOUT: %Food.loc31_9.2: %Edible.type = symbolic_binding Food, 0 [symbolic = %Food.loc31_9.1 (constants.%Food.d1c)] // CHECK:STDOUT: %.loc31_37.1: type = splice_block %Eats.type.loc31_37.2 [symbolic = %Eats.type.loc31_37.1 (constants.%Eats.type.570)] { // CHECK:STDOUT: %.Self.1: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %Eats.ref: %Eats.type.321 = name_ref Eats, file.%Eats.decl [concrete = constants.%Eats.generic] // CHECK:STDOUT: %Food.ref.loc31_33: %Edible.type = name_ref Food, %Food.loc31_9.2 [symbolic = %Food.loc31_9.1 (constants.%Food.d1c)] // CHECK:STDOUT: %Food.as_type.loc31_37: type = facet_access_type %Food.ref.loc31_33 [symbolic = %Food.binding.as_type (constants.%Food.binding.as_type.8cc)] // CHECK:STDOUT: %.loc31_37.2: type = converted %Food.ref.loc31_33, %Food.as_type.loc31_37 [symbolic = %Food.binding.as_type (constants.%Food.binding.as_type.8cc)] // CHECK:STDOUT: %Eats.type.loc31_37.2: type = facet_type <@Eats, @Eats(constants.%Food.binding.as_type.8cc)> [symbolic = %Eats.type.loc31_37.1 (constants.%Eats.type.570)] // CHECK:STDOUT: } // CHECK:STDOUT: %T.loc31_24.2: @Feed.%Eats.type.loc31_37.1 (%Eats.type.570) = symbolic_binding T, 1 [symbolic = %T.loc31_24.1 (constants.%T.2b9)] // CHECK:STDOUT: %e.param: @Feed.%T.binding.as_type (%T.binding.as_type.8ae) = value_param call_param0 // CHECK:STDOUT: %.loc31_50.1: type = splice_block %.loc31_50.2 [symbolic = %T.binding.as_type (constants.%T.binding.as_type.8ae)] { // CHECK:STDOUT: %T.ref: @Feed.%Eats.type.loc31_37.1 (%Eats.type.570) = name_ref T, %T.loc31_24.2 [symbolic = %T.loc31_24.1 (constants.%T.2b9)] // CHECK:STDOUT: %T.as_type: type = facet_access_type %T.ref [symbolic = %T.binding.as_type (constants.%T.binding.as_type.8ae)] // CHECK:STDOUT: %.loc31_50.2: type = converted %T.ref, %T.as_type [symbolic = %T.binding.as_type (constants.%T.binding.as_type.8ae)] // CHECK:STDOUT: } // CHECK:STDOUT: %e: @Feed.%T.binding.as_type (%T.binding.as_type.8ae) = value_binding e, %e.param // CHECK:STDOUT: %food.param: @Feed.%Food.binding.as_type (%Food.binding.as_type.8cc) = value_param call_param1 // CHECK:STDOUT: %.loc31_66.1: type = splice_block %.loc31_66.2 [symbolic = %Food.binding.as_type (constants.%Food.binding.as_type.8cc)] { // CHECK:STDOUT: %Food.ref.loc31_66: %Edible.type = name_ref Food, %Food.loc31_9.2 [symbolic = %Food.loc31_9.1 (constants.%Food.d1c)] // CHECK:STDOUT: %Food.as_type.loc31_66: type = facet_access_type %Food.ref.loc31_66 [symbolic = %Food.binding.as_type (constants.%Food.binding.as_type.8cc)] // CHECK:STDOUT: %.loc31_66.2: type = converted %Food.ref.loc31_66, %Food.as_type.loc31_66 [symbolic = %Food.binding.as_type (constants.%Food.binding.as_type.8cc)] // CHECK:STDOUT: } // CHECK:STDOUT: %food: @Feed.%Food.binding.as_type (%Food.binding.as_type.8cc) = value_binding food, %food.param // CHECK:STDOUT: } // CHECK:STDOUT: %HandleAnimal.decl: %HandleAnimal.type = fn_decl @HandleAnimal [concrete = constants.%HandleAnimal] { // CHECK:STDOUT: %A.patt: %pattern_type.e10 = symbolic_binding_pattern A, 0 [concrete] // CHECK:STDOUT: %Food.patt: %pattern_type.b51 = symbolic_binding_pattern Food, 1 [concrete] // CHECK:STDOUT: %a.patt: @HandleAnimal.%pattern_type.loc32_44 (%pattern_type.892) = value_binding_pattern a [concrete] // CHECK:STDOUT: %a.param_patt: @HandleAnimal.%pattern_type.loc32_44 (%pattern_type.892) = value_param_pattern %a.patt [concrete] // CHECK:STDOUT: %food.patt: @HandleAnimal.%pattern_type.loc32_50 (%pattern_type.9ed) = value_binding_pattern food [concrete] // CHECK:STDOUT: %food.param_patt: @HandleAnimal.%pattern_type.loc32_50 (%pattern_type.9ed) = value_param_pattern %food.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %.loc32_21: type = splice_block %Animal.ref [concrete = constants.%Animal.type] { // CHECK:STDOUT: %.Self.2: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %Animal.ref: type = name_ref Animal, file.%Animal.decl [concrete = constants.%Animal.type] // CHECK:STDOUT: } // CHECK:STDOUT: %A.loc32_17.2: %Animal.type = symbolic_binding A, 0 [symbolic = %A.loc32_17.1 (constants.%A)] // CHECK:STDOUT: %.loc32_36: type = splice_block %Edible.ref [concrete = constants.%Edible.type] { // CHECK:STDOUT: %.Self.1: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %Edible.ref: type = name_ref Edible, file.%Edible.decl [concrete = constants.%Edible.type] // CHECK:STDOUT: } // CHECK:STDOUT: %Food.loc32_29.2: %Edible.type = symbolic_binding Food, 1 [symbolic = %Food.loc32_29.1 (constants.%Food.2fc)] // CHECK:STDOUT: %a.param: @HandleAnimal.%A.binding.as_type (%A.binding.as_type) = value_param call_param0 // CHECK:STDOUT: %.loc32_47.1: type = splice_block %.loc32_47.2 [symbolic = %A.binding.as_type (constants.%A.binding.as_type)] { // CHECK:STDOUT: %A.ref: %Animal.type = name_ref A, %A.loc32_17.2 [symbolic = %A.loc32_17.1 (constants.%A)] // CHECK:STDOUT: %A.as_type: type = facet_access_type %A.ref [symbolic = %A.binding.as_type (constants.%A.binding.as_type)] // CHECK:STDOUT: %.loc32_47.2: type = converted %A.ref, %A.as_type [symbolic = %A.binding.as_type (constants.%A.binding.as_type)] // CHECK:STDOUT: } // CHECK:STDOUT: %a: @HandleAnimal.%A.binding.as_type (%A.binding.as_type) = value_binding a, %a.param // CHECK:STDOUT: %food.param: @HandleAnimal.%Food.binding.as_type (%Food.binding.as_type.e7e) = value_param call_param1 // CHECK:STDOUT: %.loc32_56.1: type = splice_block %.loc32_56.2 [symbolic = %Food.binding.as_type (constants.%Food.binding.as_type.e7e)] { // CHECK:STDOUT: %Food.ref: %Edible.type = name_ref Food, %Food.loc32_29.2 [symbolic = %Food.loc32_29.1 (constants.%Food.2fc)] // CHECK:STDOUT: %Food.as_type: type = facet_access_type %Food.ref [symbolic = %Food.binding.as_type (constants.%Food.binding.as_type.e7e)] // CHECK:STDOUT: %.loc32_56.2: type = converted %Food.ref, %Food.as_type [symbolic = %Food.binding.as_type (constants.%Food.binding.as_type.e7e)] // CHECK:STDOUT: } // CHECK:STDOUT: %food: @HandleAnimal.%Food.binding.as_type (%Food.binding.as_type.e7e) = value_binding food, %food.param // CHECK:STDOUT: } // CHECK:STDOUT: %F.decl: %F.type = fn_decl @F [concrete = constants.%F] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: interface @Edible { // CHECK:STDOUT: %Self: %Edible.type = symbolic_binding Self, 0 [symbolic = constants.%Self.461] // CHECK:STDOUT: %Edible.WithSelf.decl = interface_with_self_decl @Edible [concrete] // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = %Self // CHECK:STDOUT: witness = () // CHECK:STDOUT: // CHECK:STDOUT: !requires: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: interface @Animal { // CHECK:STDOUT: %Self: %Animal.type = symbolic_binding Self, 0 [symbolic = constants.%Self.c49] // CHECK:STDOUT: %Animal.WithSelf.decl = interface_with_self_decl @Animal [concrete] // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = %Self // CHECK:STDOUT: witness = () // CHECK:STDOUT: // CHECK:STDOUT: !requires: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic interface @Eats(%Food.loc21_16.2: type) { // CHECK:STDOUT: %Food.loc21_16.1: type = symbolic_binding Food, 0 [symbolic = %Food.loc21_16.1 (constants.%Food.67d)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %Eats.type: type = facet_type <@Eats, @Eats(%Food.loc21_16.1)> [symbolic = %Eats.type (constants.%Eats.type.394)] // CHECK:STDOUT: %Self.loc21_29.2: @Eats.%Eats.type (%Eats.type.394) = symbolic_binding Self, 1 [symbolic = %Self.loc21_29.2 (constants.%Self.857)] // CHECK:STDOUT: // CHECK:STDOUT: interface { // CHECK:STDOUT: %Self.loc21_29.1: @Eats.%Eats.type (%Eats.type.394) = symbolic_binding Self, 1 [symbolic = %Self.loc21_29.2 (constants.%Self.857)] // CHECK:STDOUT: %Eats.WithSelf.decl = interface_with_self_decl @Eats [concrete] // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = %Self.loc21_29.1 // CHECK:STDOUT: witness = () // CHECK:STDOUT: // CHECK:STDOUT: !requires: // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: impl @Grass.as.Edible.impl: %Grass.ref as %Edible.ref { // CHECK:STDOUT: %Edible.impl_witness_table = impl_witness_table (), @Grass.as.Edible.impl [concrete] // CHECK:STDOUT: %Edible.impl_witness: = impl_witness %Edible.impl_witness_table [concrete = constants.%Edible.impl_witness] // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: witness = %Edible.impl_witness // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic impl @T.binding.as_type.as.Eats.impl(%T.loc26_14.1: %Animal.type, %U.loc26_26.1: %Edible.type) { // CHECK:STDOUT: %T.loc26_14.2: %Animal.type = symbolic_binding T, 0 [symbolic = %T.loc26_14.2 (constants.%T.998)] // CHECK:STDOUT: %U.loc26_26.2: %Edible.type = symbolic_binding U, 1 [symbolic = %U.loc26_26.2 (constants.%U)] // CHECK:STDOUT: %T.binding.as_type: type = symbolic_binding_type T, 0, %T.loc26_14.2 [symbolic = %T.binding.as_type (constants.%T.binding.as_type.e4f)] // CHECK:STDOUT: %U.binding.as_type: type = symbolic_binding_type U, 1, %U.loc26_26.2 [symbolic = %U.binding.as_type (constants.%U.binding.as_type)] // CHECK:STDOUT: %Eats.type.loc26_49.2: type = facet_type <@Eats, @Eats(%U.binding.as_type)> [symbolic = %Eats.type.loc26_49.2 (constants.%Eats.type.bb4cf6.1)] // CHECK:STDOUT: %Eats.impl_witness.loc26_51.2: = impl_witness %Eats.impl_witness_table, @T.binding.as_type.as.Eats.impl(%T.loc26_14.2, %U.loc26_26.2) [symbolic = %Eats.impl_witness.loc26_51.2 (constants.%Eats.impl_witness.0150b7.1)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete: = require_complete_type %Eats.type.loc26_49.2 [symbolic = %require_complete (constants.%require_complete.a2722e.1)] // CHECK:STDOUT: // CHECK:STDOUT: impl: %.loc26_38 as %Eats.type.loc26_49.1 { // CHECK:STDOUT: %Eats.impl_witness_table = impl_witness_table (), @T.binding.as_type.as.Eats.impl [concrete] // CHECK:STDOUT: %Eats.impl_witness.loc26_51.1: = impl_witness %Eats.impl_witness_table, @T.binding.as_type.as.Eats.impl(constants.%T.998, constants.%U) [symbolic = %Eats.impl_witness.loc26_51.2 (constants.%Eats.impl_witness.0150b7.1)] // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: witness = %Eats.impl_witness.loc26_51.1 // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: impl @Goat.as.Animal.impl: %Goat.ref as %Animal.ref { // CHECK:STDOUT: %Animal.impl_witness_table = impl_witness_table (), @Goat.as.Animal.impl [concrete] // CHECK:STDOUT: %Animal.impl_witness: = impl_witness %Animal.impl_witness_table [concrete = constants.%Animal.impl_witness] // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: witness = %Animal.impl_witness // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @Grass { // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type.357] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%Grass // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @Goat { // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type.357] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%Goat // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic fn @Feed(%Food.loc31_9.2: %Edible.type, %T.loc31_24.2: @Feed.%Eats.type.loc31_37.1 (%Eats.type.570)) { // CHECK:STDOUT: %Food.loc31_9.1: %Edible.type = symbolic_binding Food, 0 [symbolic = %Food.loc31_9.1 (constants.%Food.d1c)] // CHECK:STDOUT: %Food.binding.as_type: type = symbolic_binding_type Food, 0, %Food.loc31_9.1 [symbolic = %Food.binding.as_type (constants.%Food.binding.as_type.8cc)] // CHECK:STDOUT: %Eats.type.loc31_37.1: type = facet_type <@Eats, @Eats(%Food.binding.as_type)> [symbolic = %Eats.type.loc31_37.1 (constants.%Eats.type.570)] // CHECK:STDOUT: %T.loc31_24.1: @Feed.%Eats.type.loc31_37.1 (%Eats.type.570) = symbolic_binding T, 1 [symbolic = %T.loc31_24.1 (constants.%T.2b9)] // CHECK:STDOUT: %pattern_type.loc31_24: type = pattern_type %Eats.type.loc31_37.1 [symbolic = %pattern_type.loc31_24 (constants.%pattern_type.350)] // CHECK:STDOUT: %T.binding.as_type: type = symbolic_binding_type T, 1, %T.loc31_24.1 [symbolic = %T.binding.as_type (constants.%T.binding.as_type.8ae)] // CHECK:STDOUT: %pattern_type.loc31_47: type = pattern_type %T.binding.as_type [symbolic = %pattern_type.loc31_47 (constants.%pattern_type.2b9)] // CHECK:STDOUT: %pattern_type.loc31_60: type = pattern_type %Food.binding.as_type [symbolic = %pattern_type.loc31_60 (constants.%pattern_type.12a)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete.loc31_48: = require_complete_type %T.binding.as_type [symbolic = %require_complete.loc31_48 (constants.%require_complete.0c1)] // CHECK:STDOUT: %require_complete.loc31_64: = require_complete_type %Food.binding.as_type [symbolic = %require_complete.loc31_64 (constants.%require_complete.bd7)] // CHECK:STDOUT: // CHECK:STDOUT: fn(%e.param: @Feed.%T.binding.as_type (%T.binding.as_type.8ae), %food.param: @Feed.%Food.binding.as_type (%Food.binding.as_type.8cc)) { // CHECK:STDOUT: !entry: // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic fn @HandleAnimal(%A.loc32_17.2: %Animal.type, %Food.loc32_29.2: %Edible.type) { // CHECK:STDOUT: %A.loc32_17.1: %Animal.type = symbolic_binding A, 0 [symbolic = %A.loc32_17.1 (constants.%A)] // CHECK:STDOUT: %Food.loc32_29.1: %Edible.type = symbolic_binding Food, 1 [symbolic = %Food.loc32_29.1 (constants.%Food.2fc)] // CHECK:STDOUT: %A.binding.as_type: type = symbolic_binding_type A, 0, %A.loc32_17.1 [symbolic = %A.binding.as_type (constants.%A.binding.as_type)] // CHECK:STDOUT: %pattern_type.loc32_44: type = pattern_type %A.binding.as_type [symbolic = %pattern_type.loc32_44 (constants.%pattern_type.892)] // CHECK:STDOUT: %Food.binding.as_type: type = symbolic_binding_type Food, 1, %Food.loc32_29.1 [symbolic = %Food.binding.as_type (constants.%Food.binding.as_type.e7e)] // CHECK:STDOUT: %pattern_type.loc32_50: type = pattern_type %Food.binding.as_type [symbolic = %pattern_type.loc32_50 (constants.%pattern_type.9ed)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete.loc32_45: = require_complete_type %A.binding.as_type [symbolic = %require_complete.loc32_45 (constants.%require_complete.72f)] // CHECK:STDOUT: %require_complete.loc32_54: = require_complete_type %Food.binding.as_type [symbolic = %require_complete.loc32_54 (constants.%require_complete.0a9)] // CHECK:STDOUT: %.loc32_76.3: require_specific_def_type = require_specific_def @T.binding.as_type.as.Eats.impl(%A.loc32_17.1, %Food.loc32_29.1) [symbolic = %.loc32_76.3 (constants.%.f9e)] // CHECK:STDOUT: %Eats.lookup_impl_witness: = lookup_impl_witness %A.loc32_17.1, @Eats, @Eats(%Food.binding.as_type) [symbolic = %Eats.lookup_impl_witness (constants.%Eats.lookup_impl_witness)] // CHECK:STDOUT: %Eats.type: type = facet_type <@Eats, @Eats(%Food.binding.as_type)> [symbolic = %Eats.type (constants.%Eats.type.bb4cf6.2)] // CHECK:STDOUT: %Eats.facet.loc32_76.2: @HandleAnimal.%Eats.type (%Eats.type.bb4cf6.2) = facet_value %A.binding.as_type, (%Eats.lookup_impl_witness) [symbolic = %Eats.facet.loc32_76.2 (constants.%Eats.facet.702)] // CHECK:STDOUT: %Feed.specific_fn.loc32_64.2: = specific_function constants.%Feed, @Feed(%Food.loc32_29.1, %Eats.facet.loc32_76.2) [symbolic = %Feed.specific_fn.loc32_64.2 (constants.%Feed.specific_fn.1f9)] // CHECK:STDOUT: // CHECK:STDOUT: fn(%a.param: @HandleAnimal.%A.binding.as_type (%A.binding.as_type), %food.param: @HandleAnimal.%Food.binding.as_type (%Food.binding.as_type.e7e)) { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %Feed.ref: %Feed.type = name_ref Feed, file.%Feed.decl [concrete = constants.%Feed] // CHECK:STDOUT: %a.ref: @HandleAnimal.%A.binding.as_type (%A.binding.as_type) = name_ref a, %a // CHECK:STDOUT: %food.ref: @HandleAnimal.%Food.binding.as_type (%Food.binding.as_type.e7e) = name_ref food, %food // CHECK:STDOUT: %.loc32_76.1: %Edible.type = converted constants.%Food.binding.as_type.e7e, constants.%Food.2fc [symbolic = %Food.loc32_29.1 (constants.%Food.2fc)] // CHECK:STDOUT: %Eats.facet.loc32_76.1: @HandleAnimal.%Eats.type (%Eats.type.bb4cf6.2) = facet_value constants.%A.binding.as_type, (constants.%Eats.lookup_impl_witness) [symbolic = %Eats.facet.loc32_76.2 (constants.%Eats.facet.702)] // CHECK:STDOUT: %.loc32_76.2: @HandleAnimal.%Eats.type (%Eats.type.bb4cf6.2) = converted constants.%A.binding.as_type, %Eats.facet.loc32_76.1 [symbolic = %Eats.facet.loc32_76.2 (constants.%Eats.facet.702)] // CHECK:STDOUT: %Feed.specific_fn.loc32_64.1: = specific_function %Feed.ref, @Feed(constants.%Food.2fc, constants.%Eats.facet.702) [symbolic = %Feed.specific_fn.loc32_64.2 (constants.%Feed.specific_fn.1f9)] // CHECK:STDOUT: %Feed.call: init %empty_tuple.type = call %Feed.specific_fn.loc32_64.1(%a.ref, %food.ref) // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @F() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %HandleAnimal.ref: %HandleAnimal.type = name_ref HandleAnimal, file.%HandleAnimal.decl [concrete = constants.%HandleAnimal] // CHECK:STDOUT: %.loc35_17.1: %empty_struct_type = struct_literal () [concrete = constants.%empty_struct] // CHECK:STDOUT: %Goat.ref: type = name_ref Goat, file.%Goat.decl [concrete = constants.%Goat] // CHECK:STDOUT: %.loc35_17.2: ref %Goat = temporary_storage // CHECK:STDOUT: %.loc35_17.3: init %Goat to %.loc35_17.2 = class_init () [concrete = constants.%Goat.val] // CHECK:STDOUT: %.loc35_19.1: init %Goat = converted %.loc35_17.1, %.loc35_17.3 [concrete = constants.%Goat.val] // CHECK:STDOUT: %.loc35_29.1: %empty_struct_type = struct_literal () [concrete = constants.%empty_struct] // CHECK:STDOUT: %Grass.ref: type = name_ref Grass, file.%Grass.decl [concrete = constants.%Grass] // CHECK:STDOUT: %.loc35_29.2: ref %Grass = temporary_storage // CHECK:STDOUT: %.loc35_29.3: init %Grass to %.loc35_29.2 = class_init () [concrete = constants.%Grass.val] // CHECK:STDOUT: %.loc35_31.1: init %Grass = converted %.loc35_29.1, %.loc35_29.3 [concrete = constants.%Grass.val] // CHECK:STDOUT: %Animal.facet: %Animal.type = facet_value constants.%Goat, (constants.%Animal.impl_witness) [concrete = constants.%Animal.facet] // CHECK:STDOUT: %.loc35_39.1: %Animal.type = converted constants.%Goat, %Animal.facet [concrete = constants.%Animal.facet] // CHECK:STDOUT: %Edible.facet: %Edible.type = facet_value constants.%Grass, (constants.%Edible.impl_witness) [concrete = constants.%Edible.facet] // CHECK:STDOUT: %.loc35_39.2: %Edible.type = converted constants.%Grass, %Edible.facet [concrete = constants.%Edible.facet] // CHECK:STDOUT: %HandleAnimal.specific_fn: = specific_function %HandleAnimal.ref, @HandleAnimal(constants.%Animal.facet, constants.%Edible.facet) [concrete = constants.%HandleAnimal.specific_fn] // CHECK:STDOUT: %.loc35_19.2: ref %Goat = temporary %.loc35_17.2, %.loc35_19.1 // CHECK:STDOUT: %.loc35_19.3: %Goat = acquire_value %.loc35_19.2 // CHECK:STDOUT: %.loc35_31.2: ref %Grass = temporary %.loc35_29.2, %.loc35_31.1 // CHECK:STDOUT: %.loc35_31.3: %Grass = acquire_value %.loc35_31.2 // CHECK:STDOUT: %HandleAnimal.call: init %empty_tuple.type = call %HandleAnimal.specific_fn(%.loc35_19.3, %.loc35_31.3) // CHECK:STDOUT: %Destroy.Op.bound.loc35_31: = bound_method %.loc35_31.2, constants.%Destroy.Op.651ba6.1 // CHECK:STDOUT: %Destroy.Op.call.loc35_31: init %empty_tuple.type = call %Destroy.Op.bound.loc35_31(%.loc35_31.2) // CHECK:STDOUT: %Destroy.Op.bound.loc35_19: = bound_method %.loc35_19.2, constants.%Destroy.Op.651ba6.2 // CHECK:STDOUT: %Destroy.Op.call.loc35_19: init %empty_tuple.type = call %Destroy.Op.bound.loc35_19(%.loc35_19.2) // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @Destroy.Op.loc35_31(%self.param: ref %Grass) = "no_op"; // CHECK:STDOUT: // CHECK:STDOUT: fn @Destroy.Op.loc35_19(%self.param: ref %Goat) = "no_op"; // CHECK:STDOUT: // CHECK:STDOUT: specific @Edible.WithSelf(constants.%Self.461) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @Edible.WithSelf(constants.%Edible.facet) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @Animal.WithSelf(constants.%Self.c49) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @Eats(constants.%Food.67d) { // CHECK:STDOUT: %Food.loc21_16.1 => constants.%Food.67d // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @Eats.WithSelf(constants.%Food.67d, constants.%Self.857) {} // CHECK:STDOUT: // CHECK:STDOUT: specific @Eats(constants.%U.binding.as_type) { // CHECK:STDOUT: %Food.loc21_16.1 => constants.%U.binding.as_type // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %Eats.type => constants.%Eats.type.bb4cf6.1 // CHECK:STDOUT: %Self.loc21_29.2 => constants.%Self.027 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @T.binding.as_type.as.Eats.impl(constants.%T.998, constants.%U) { // CHECK:STDOUT: %T.loc26_14.2 => constants.%T.998 // CHECK:STDOUT: %U.loc26_26.2 => constants.%U // CHECK:STDOUT: %T.binding.as_type => constants.%T.binding.as_type.e4f // CHECK:STDOUT: %U.binding.as_type => constants.%U.binding.as_type // CHECK:STDOUT: %Eats.type.loc26_49.2 => constants.%Eats.type.bb4cf6.1 // CHECK:STDOUT: %Eats.impl_witness.loc26_51.2 => constants.%Eats.impl_witness.0150b7.1 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @Eats.WithSelf(constants.%U.binding.as_type, constants.%Self.857) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @Eats.WithSelf(constants.%U.binding.as_type, constants.%Eats.facet.31f) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @Animal.WithSelf(constants.%Animal.facet) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @Eats(constants.%Food.binding.as_type.8cc) { // CHECK:STDOUT: %Food.loc21_16.1 => constants.%Food.binding.as_type.8cc // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %Eats.type => constants.%Eats.type.570 // CHECK:STDOUT: %Self.loc21_29.2 => constants.%Self.a66 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @Eats.WithSelf(constants.%Food.binding.as_type.8cc, constants.%Self.857) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @Feed(constants.%Food.d1c, constants.%T.2b9) { // CHECK:STDOUT: %Food.loc31_9.1 => constants.%Food.d1c // CHECK:STDOUT: %Food.binding.as_type => constants.%Food.binding.as_type.8cc // CHECK:STDOUT: %Eats.type.loc31_37.1 => constants.%Eats.type.570 // CHECK:STDOUT: %T.loc31_24.1 => constants.%T.2b9 // CHECK:STDOUT: %pattern_type.loc31_24 => constants.%pattern_type.350 // CHECK:STDOUT: %T.binding.as_type => constants.%T.binding.as_type.8ae // CHECK:STDOUT: %pattern_type.loc31_47 => constants.%pattern_type.2b9 // CHECK:STDOUT: %pattern_type.loc31_60 => constants.%pattern_type.12a // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @HandleAnimal(constants.%A, constants.%Food.2fc) { // CHECK:STDOUT: %A.loc32_17.1 => constants.%A // CHECK:STDOUT: %Food.loc32_29.1 => constants.%Food.2fc // CHECK:STDOUT: %A.binding.as_type => constants.%A.binding.as_type // CHECK:STDOUT: %pattern_type.loc32_44 => constants.%pattern_type.892 // CHECK:STDOUT: %Food.binding.as_type => constants.%Food.binding.as_type.e7e // CHECK:STDOUT: %pattern_type.loc32_50 => constants.%pattern_type.9ed // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @Eats(constants.%Food.binding.as_type.e7e) { // CHECK:STDOUT: %Food.loc21_16.1 => constants.%Food.binding.as_type.e7e // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @T.binding.as_type.as.Eats.impl(constants.%A, constants.%Food.2fc) { // CHECK:STDOUT: %T.loc26_14.2 => constants.%A // CHECK:STDOUT: %U.loc26_26.2 => constants.%Food.2fc // CHECK:STDOUT: %T.binding.as_type => constants.%A.binding.as_type // CHECK:STDOUT: %U.binding.as_type => constants.%Food.binding.as_type.e7e // CHECK:STDOUT: %Eats.type.loc26_49.2 => constants.%Eats.type.bb4cf6.2 // CHECK:STDOUT: %Eats.impl_witness.loc26_51.2 => constants.%Eats.impl_witness.0150b7.2 // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete => constants.%require_complete.a2722e.2 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @Feed(constants.%Food.2fc, constants.%Eats.facet.702) { // CHECK:STDOUT: %Food.loc31_9.1 => constants.%Food.2fc // CHECK:STDOUT: %Food.binding.as_type => constants.%Food.binding.as_type.e7e // CHECK:STDOUT: %Eats.type.loc31_37.1 => constants.%Eats.type.bb4cf6.2 // CHECK:STDOUT: %T.loc31_24.1 => constants.%Eats.facet.702 // CHECK:STDOUT: %pattern_type.loc31_24 => constants.%pattern_type.67b // CHECK:STDOUT: %T.binding.as_type => constants.%A.binding.as_type // CHECK:STDOUT: %pattern_type.loc31_47 => constants.%pattern_type.892 // CHECK:STDOUT: %pattern_type.loc31_60 => constants.%pattern_type.9ed // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete.loc31_48 => constants.%require_complete.72f // CHECK:STDOUT: %require_complete.loc31_64 => constants.%require_complete.0a9 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @HandleAnimal(constants.%Animal.facet, constants.%Edible.facet) { // CHECK:STDOUT: %A.loc32_17.1 => constants.%Animal.facet // CHECK:STDOUT: %Food.loc32_29.1 => constants.%Edible.facet // CHECK:STDOUT: %A.binding.as_type => constants.%Goat // CHECK:STDOUT: %pattern_type.loc32_44 => constants.%pattern_type.234 // CHECK:STDOUT: %Food.binding.as_type => constants.%Grass // CHECK:STDOUT: %pattern_type.loc32_50 => constants.%pattern_type.df6 // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete.loc32_45 => constants.%complete_type.357 // CHECK:STDOUT: %require_complete.loc32_54 => constants.%complete_type.357 // CHECK:STDOUT: %.loc32_76.3 => constants.%.767 // CHECK:STDOUT: %Eats.lookup_impl_witness => constants.%Eats.impl_witness.f54 // CHECK:STDOUT: %Eats.type => constants.%Eats.type.8c2 // CHECK:STDOUT: %Eats.facet.loc32_76.2 => constants.%Eats.facet.a4e // CHECK:STDOUT: %Feed.specific_fn.loc32_64.2 => constants.%Feed.specific_fn.7dd // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @T.binding.as_type.as.Eats.impl(constants.%Animal.facet, constants.%Edible.facet) { // CHECK:STDOUT: %T.loc26_14.2 => constants.%Animal.facet // CHECK:STDOUT: %U.loc26_26.2 => constants.%Edible.facet // CHECK:STDOUT: %T.binding.as_type => constants.%Goat // CHECK:STDOUT: %U.binding.as_type => constants.%Grass // CHECK:STDOUT: %Eats.type.loc26_49.2 => constants.%Eats.type.8c2 // CHECK:STDOUT: %Eats.impl_witness.loc26_51.2 => constants.%Eats.impl_witness.f54 // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete => constants.%complete_type.cf8 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @Eats(constants.%Grass) { // CHECK:STDOUT: %Food.loc21_16.1 => constants.%Grass // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %Eats.type => constants.%Eats.type.8c2 // CHECK:STDOUT: %Self.loc21_29.2 => constants.%Self.ebd // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @Eats.WithSelf(constants.%Grass, constants.%Self.857) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @Feed(constants.%Edible.facet, constants.%Eats.facet.a4e) { // CHECK:STDOUT: %Food.loc31_9.1 => constants.%Edible.facet // CHECK:STDOUT: %Food.binding.as_type => constants.%Grass // CHECK:STDOUT: %Eats.type.loc31_37.1 => constants.%Eats.type.8c2 // CHECK:STDOUT: %T.loc31_24.1 => constants.%Eats.facet.a4e // CHECK:STDOUT: %pattern_type.loc31_24 => constants.%pattern_type.968 // CHECK:STDOUT: %T.binding.as_type => constants.%Goat // CHECK:STDOUT: %pattern_type.loc31_47 => constants.%pattern_type.234 // CHECK:STDOUT: %pattern_type.loc31_60 => constants.%pattern_type.df6 // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete.loc31_48 => constants.%complete_type.357 // CHECK:STDOUT: %require_complete.loc31_64 => constants.%complete_type.357 // CHECK:STDOUT: } // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/facet/convert_facet_value_value_to_itself.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // TODO: Add ranges and switch to "--dump-sem-ir-ranges=only". // EXTRA-ARGS: --dump-sem-ir-ranges=if-present // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/convert.carbon // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/facet/convert_facet_value_value_to_itself.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/facet/convert_facet_value_value_to_itself.carbon interface Animal {} fn FeedAnimal[T:! Animal](unused a: T) {} fn HandleAnimal[T:! Animal](a: T) { FeedAnimal(a); } class Goat {} impl Goat as Animal {} fn F() { HandleAnimal({} as Goat); } // CHECK:STDOUT: --- convert_facet_value_value_to_itself.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %Animal.type: type = facet_type <@Animal> [concrete] // CHECK:STDOUT: %Self.c49: %Animal.type = symbolic_binding Self, 0 [symbolic] // CHECK:STDOUT: %type: type = facet_type [concrete] // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self] // CHECK:STDOUT: %pattern_type.e10: type = pattern_type %Animal.type [concrete] // CHECK:STDOUT: %T: %Animal.type = symbolic_binding T, 0 [symbolic] // CHECK:STDOUT: %T.binding.as_type: type = symbolic_binding_type T, 0, %T [symbolic] // CHECK:STDOUT: %pattern_type.892: type = pattern_type %T.binding.as_type [symbolic] // CHECK:STDOUT: %FeedAnimal.type: type = fn_type @FeedAnimal [concrete] // CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] // CHECK:STDOUT: %FeedAnimal: %FeedAnimal.type = struct_value () [concrete] // CHECK:STDOUT: %require_complete: = require_complete_type %T.binding.as_type [symbolic] // CHECK:STDOUT: %HandleAnimal.type: type = fn_type @HandleAnimal [concrete] // CHECK:STDOUT: %HandleAnimal: %HandleAnimal.type = struct_value () [concrete] // CHECK:STDOUT: %FeedAnimal.specific_fn.ae9: = specific_function %FeedAnimal, @FeedAnimal(%T) [symbolic] // CHECK:STDOUT: %Goat: type = class_type @Goat [concrete] // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %complete_type: = complete_type_witness %empty_struct_type [concrete] // CHECK:STDOUT: %Animal.impl_witness: = impl_witness @Goat.as.Animal.impl.%Animal.impl_witness_table [concrete] // CHECK:STDOUT: %Animal.facet: %Animal.type = facet_value %Goat, (%Animal.impl_witness) [concrete] // CHECK:STDOUT: %F.type: type = fn_type @F [concrete] // CHECK:STDOUT: %F: %F.type = struct_value () [concrete] // CHECK:STDOUT: %empty_struct: %empty_struct_type = struct_value () [concrete] // CHECK:STDOUT: %Goat.val: %Goat = struct_value () [concrete] // CHECK:STDOUT: %pattern_type.234: type = pattern_type %Goat [concrete] // CHECK:STDOUT: %HandleAnimal.specific_fn: = specific_function %HandleAnimal, @HandleAnimal(%Animal.facet) [concrete] // CHECK:STDOUT: %Destroy.type: type = facet_type <@Destroy> [concrete] // CHECK:STDOUT: %Destroy.Op.type: type = fn_type @Destroy.Op [concrete] // CHECK:STDOUT: %Destroy.Op: %Destroy.Op.type = struct_value () [concrete] // CHECK:STDOUT: %FeedAnimal.specific_fn.cc5: = specific_function %FeedAnimal, @FeedAnimal(%Animal.facet) [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: .Destroy = %Core.Destroy // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: %Core.Destroy: type = import_ref Core//prelude/parts/destroy, Destroy, loaded [concrete = constants.%Destroy.type] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .Animal = %Animal.decl // CHECK:STDOUT: .FeedAnimal = %FeedAnimal.decl // CHECK:STDOUT: .HandleAnimal = %HandleAnimal.decl // CHECK:STDOUT: .Goat = %Goat.decl // CHECK:STDOUT: .F = %F.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %Animal.decl: type = interface_decl @Animal [concrete = constants.%Animal.type] {} {} // CHECK:STDOUT: %FeedAnimal.decl: %FeedAnimal.type = fn_decl @FeedAnimal [concrete = constants.%FeedAnimal] { // CHECK:STDOUT: %T.patt: %pattern_type.e10 = symbolic_binding_pattern T, 0 [concrete] // CHECK:STDOUT: %a.patt: @FeedAnimal.%pattern_type (%pattern_type.892) = value_binding_pattern a [concrete] // CHECK:STDOUT: %a.param_patt: @FeedAnimal.%pattern_type (%pattern_type.892) = value_param_pattern %a.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %.loc17_19: type = splice_block %Animal.ref [concrete = constants.%Animal.type] { // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %Animal.ref: type = name_ref Animal, file.%Animal.decl [concrete = constants.%Animal.type] // CHECK:STDOUT: } // CHECK:STDOUT: %T.loc17_15.2: %Animal.type = symbolic_binding T, 0 [symbolic = %T.loc17_15.1 (constants.%T)] // CHECK:STDOUT: %a.param: @FeedAnimal.%T.binding.as_type (%T.binding.as_type) = value_param call_param0 // CHECK:STDOUT: %.loc17_37.1: type = splice_block %.loc17_37.2 [symbolic = %T.binding.as_type (constants.%T.binding.as_type)] { // CHECK:STDOUT: %T.ref: %Animal.type = name_ref T, %T.loc17_15.2 [symbolic = %T.loc17_15.1 (constants.%T)] // CHECK:STDOUT: %T.as_type: type = facet_access_type %T.ref [symbolic = %T.binding.as_type (constants.%T.binding.as_type)] // CHECK:STDOUT: %.loc17_37.2: type = converted %T.ref, %T.as_type [symbolic = %T.binding.as_type (constants.%T.binding.as_type)] // CHECK:STDOUT: } // CHECK:STDOUT: %a: @FeedAnimal.%T.binding.as_type (%T.binding.as_type) = value_binding a, %a.param // CHECK:STDOUT: } // CHECK:STDOUT: %HandleAnimal.decl: %HandleAnimal.type = fn_decl @HandleAnimal [concrete = constants.%HandleAnimal] { // CHECK:STDOUT: %T.patt: %pattern_type.e10 = symbolic_binding_pattern T, 0 [concrete] // CHECK:STDOUT: %a.patt: @HandleAnimal.%pattern_type (%pattern_type.892) = value_binding_pattern a [concrete] // CHECK:STDOUT: %a.param_patt: @HandleAnimal.%pattern_type (%pattern_type.892) = value_param_pattern %a.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %.loc19_21: type = splice_block %Animal.ref [concrete = constants.%Animal.type] { // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %Animal.ref: type = name_ref Animal, file.%Animal.decl [concrete = constants.%Animal.type] // CHECK:STDOUT: } // CHECK:STDOUT: %T.loc19_17.2: %Animal.type = symbolic_binding T, 0 [symbolic = %T.loc19_17.1 (constants.%T)] // CHECK:STDOUT: %a.param: @HandleAnimal.%T.binding.as_type (%T.binding.as_type) = value_param call_param0 // CHECK:STDOUT: %.loc19_32.1: type = splice_block %.loc19_32.2 [symbolic = %T.binding.as_type (constants.%T.binding.as_type)] { // CHECK:STDOUT: %T.ref: %Animal.type = name_ref T, %T.loc19_17.2 [symbolic = %T.loc19_17.1 (constants.%T)] // CHECK:STDOUT: %T.as_type: type = facet_access_type %T.ref [symbolic = %T.binding.as_type (constants.%T.binding.as_type)] // CHECK:STDOUT: %.loc19_32.2: type = converted %T.ref, %T.as_type [symbolic = %T.binding.as_type (constants.%T.binding.as_type)] // CHECK:STDOUT: } // CHECK:STDOUT: %a: @HandleAnimal.%T.binding.as_type (%T.binding.as_type) = value_binding a, %a.param // CHECK:STDOUT: } // CHECK:STDOUT: %Goat.decl: type = class_decl @Goat [concrete = constants.%Goat] {} {} // CHECK:STDOUT: impl_decl @Goat.as.Animal.impl [concrete] {} { // CHECK:STDOUT: %Goat.ref: type = name_ref Goat, file.%Goat.decl [concrete = constants.%Goat] // CHECK:STDOUT: %Animal.ref: type = name_ref Animal, file.%Animal.decl [concrete = constants.%Animal.type] // CHECK:STDOUT: } // CHECK:STDOUT: %F.decl: %F.type = fn_decl @F [concrete = constants.%F] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: interface @Animal { // CHECK:STDOUT: %Self: %Animal.type = symbolic_binding Self, 0 [symbolic = constants.%Self.c49] // CHECK:STDOUT: %Animal.WithSelf.decl = interface_with_self_decl @Animal [concrete] // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = %Self // CHECK:STDOUT: witness = () // CHECK:STDOUT: // CHECK:STDOUT: !requires: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: impl @Goat.as.Animal.impl: %Goat.ref as %Animal.ref { // CHECK:STDOUT: %Animal.impl_witness_table = impl_witness_table (), @Goat.as.Animal.impl [concrete] // CHECK:STDOUT: %Animal.impl_witness: = impl_witness %Animal.impl_witness_table [concrete = constants.%Animal.impl_witness] // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: witness = %Animal.impl_witness // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @Goat { // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%Goat // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic fn @FeedAnimal(%T.loc17_15.2: %Animal.type) { // CHECK:STDOUT: %T.loc17_15.1: %Animal.type = symbolic_binding T, 0 [symbolic = %T.loc17_15.1 (constants.%T)] // CHECK:STDOUT: %T.binding.as_type: type = symbolic_binding_type T, 0, %T.loc17_15.1 [symbolic = %T.binding.as_type (constants.%T.binding.as_type)] // CHECK:STDOUT: %pattern_type: type = pattern_type %T.binding.as_type [symbolic = %pattern_type (constants.%pattern_type.892)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete: = require_complete_type %T.binding.as_type [symbolic = %require_complete (constants.%require_complete)] // CHECK:STDOUT: // CHECK:STDOUT: fn(%a.param: @FeedAnimal.%T.binding.as_type (%T.binding.as_type)) { // CHECK:STDOUT: !entry: // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic fn @HandleAnimal(%T.loc19_17.2: %Animal.type) { // CHECK:STDOUT: %T.loc19_17.1: %Animal.type = symbolic_binding T, 0 [symbolic = %T.loc19_17.1 (constants.%T)] // CHECK:STDOUT: %T.binding.as_type: type = symbolic_binding_type T, 0, %T.loc19_17.1 [symbolic = %T.binding.as_type (constants.%T.binding.as_type)] // CHECK:STDOUT: %pattern_type: type = pattern_type %T.binding.as_type [symbolic = %pattern_type (constants.%pattern_type.892)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete: = require_complete_type %T.binding.as_type [symbolic = %require_complete (constants.%require_complete)] // CHECK:STDOUT: %FeedAnimal.specific_fn.loc19_37.2: = specific_function constants.%FeedAnimal, @FeedAnimal(%T.loc19_17.1) [symbolic = %FeedAnimal.specific_fn.loc19_37.2 (constants.%FeedAnimal.specific_fn.ae9)] // CHECK:STDOUT: // CHECK:STDOUT: fn(%a.param: @HandleAnimal.%T.binding.as_type (%T.binding.as_type)) { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %FeedAnimal.ref: %FeedAnimal.type = name_ref FeedAnimal, file.%FeedAnimal.decl [concrete = constants.%FeedAnimal] // CHECK:STDOUT: %a.ref: @HandleAnimal.%T.binding.as_type (%T.binding.as_type) = name_ref a, %a // CHECK:STDOUT: %.loc19_49: %Animal.type = converted constants.%T.binding.as_type, constants.%T [symbolic = %T.loc19_17.1 (constants.%T)] // CHECK:STDOUT: %FeedAnimal.specific_fn.loc19_37.1: = specific_function %FeedAnimal.ref, @FeedAnimal(constants.%T) [symbolic = %FeedAnimal.specific_fn.loc19_37.2 (constants.%FeedAnimal.specific_fn.ae9)] // CHECK:STDOUT: %FeedAnimal.call: init %empty_tuple.type = call %FeedAnimal.specific_fn.loc19_37.1(%a.ref) // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @F() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %HandleAnimal.ref: %HandleAnimal.type = name_ref HandleAnimal, file.%HandleAnimal.decl [concrete = constants.%HandleAnimal] // CHECK:STDOUT: %.loc25_17.1: %empty_struct_type = struct_literal () [concrete = constants.%empty_struct] // CHECK:STDOUT: %Goat.ref: type = name_ref Goat, file.%Goat.decl [concrete = constants.%Goat] // CHECK:STDOUT: %.loc25_17.2: ref %Goat = temporary_storage // CHECK:STDOUT: %.loc25_17.3: init %Goat to %.loc25_17.2 = class_init () [concrete = constants.%Goat.val] // CHECK:STDOUT: %.loc25_19.1: init %Goat = converted %.loc25_17.1, %.loc25_17.3 [concrete = constants.%Goat.val] // CHECK:STDOUT: %Animal.facet: %Animal.type = facet_value constants.%Goat, (constants.%Animal.impl_witness) [concrete = constants.%Animal.facet] // CHECK:STDOUT: %.loc25_26: %Animal.type = converted constants.%Goat, %Animal.facet [concrete = constants.%Animal.facet] // CHECK:STDOUT: %HandleAnimal.specific_fn: = specific_function %HandleAnimal.ref, @HandleAnimal(constants.%Animal.facet) [concrete = constants.%HandleAnimal.specific_fn] // CHECK:STDOUT: %.loc25_19.2: ref %Goat = temporary %.loc25_17.2, %.loc25_19.1 // CHECK:STDOUT: %.loc25_19.3: %Goat = acquire_value %.loc25_19.2 // CHECK:STDOUT: %HandleAnimal.call: init %empty_tuple.type = call %HandleAnimal.specific_fn(%.loc25_19.3) // CHECK:STDOUT: %Destroy.Op.bound: = bound_method %.loc25_19.2, constants.%Destroy.Op // CHECK:STDOUT: %Destroy.Op.call: init %empty_tuple.type = call %Destroy.Op.bound(%.loc25_19.2) // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @Destroy.Op(%self.param: ref %Goat) = "no_op"; // CHECK:STDOUT: // CHECK:STDOUT: specific @Animal.WithSelf(constants.%Self.c49) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @FeedAnimal(constants.%T) { // CHECK:STDOUT: %T.loc17_15.1 => constants.%T // CHECK:STDOUT: %T.binding.as_type => constants.%T.binding.as_type // CHECK:STDOUT: %pattern_type => constants.%pattern_type.892 // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete => constants.%require_complete // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @HandleAnimal(constants.%T) { // CHECK:STDOUT: %T.loc19_17.1 => constants.%T // CHECK:STDOUT: %T.binding.as_type => constants.%T.binding.as_type // CHECK:STDOUT: %pattern_type => constants.%pattern_type.892 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @Animal.WithSelf(constants.%Animal.facet) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @HandleAnimal(constants.%Animal.facet) { // CHECK:STDOUT: %T.loc19_17.1 => constants.%Animal.facet // CHECK:STDOUT: %T.binding.as_type => constants.%Goat // CHECK:STDOUT: %pattern_type => constants.%pattern_type.234 // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete => constants.%complete_type // CHECK:STDOUT: %FeedAnimal.specific_fn.loc19_37.2 => constants.%FeedAnimal.specific_fn.cc5 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @FeedAnimal(constants.%Animal.facet) { // CHECK:STDOUT: %T.loc17_15.1 => constants.%Animal.facet // CHECK:STDOUT: %T.binding.as_type => constants.%Goat // CHECK:STDOUT: %pattern_type => constants.%pattern_type.234 // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete => constants.%complete_type // CHECK:STDOUT: } // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/facet/convert_interface.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // TODO: Add ranges and switch to "--dump-sem-ir-ranges=only". // EXTRA-ARGS: --dump-sem-ir-ranges=if-present // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/convert.carbon // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/facet/convert_interface.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/facet/convert_interface.carbon interface Eats {} interface Animal {} // TODO: This may be rejected in the future. // https://github.com/carbon-language/carbon-lang/issues/4853 impl Animal as Eats {} fn F(unused e: Eats) {} fn G() { F(Animal); } // CHECK:STDOUT: --- convert_interface.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %Eats.type: type = facet_type <@Eats> [concrete] // CHECK:STDOUT: %Self.247: %Eats.type = symbolic_binding Self, 0 [symbolic] // CHECK:STDOUT: %Animal.type: type = facet_type <@Animal> [concrete] // CHECK:STDOUT: %Self.c49: %Animal.type = symbolic_binding Self, 0 [symbolic] // CHECK:STDOUT: %Eats.impl_witness: = impl_witness @Animal.type.as.Eats.impl.%Eats.impl_witness_table [concrete] // CHECK:STDOUT: %Eats.facet: %Eats.type = facet_value %Animal.type, (%Eats.impl_witness) [concrete] // CHECK:STDOUT: %pattern_type: type = pattern_type %Eats.type [concrete] // CHECK:STDOUT: %F.type: type = fn_type @F [concrete] // CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] // CHECK:STDOUT: %F: %F.type = struct_value () [concrete] // CHECK:STDOUT: %G.type: type = fn_type @G [concrete] // CHECK:STDOUT: %G: %G.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .Eats = %Eats.decl // CHECK:STDOUT: .Animal = %Animal.decl // CHECK:STDOUT: .F = %F.decl // CHECK:STDOUT: .G = %G.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %Eats.decl: type = interface_decl @Eats [concrete = constants.%Eats.type] {} {} // CHECK:STDOUT: %Animal.decl: type = interface_decl @Animal [concrete = constants.%Animal.type] {} {} // CHECK:STDOUT: impl_decl @Animal.type.as.Eats.impl [concrete] {} { // CHECK:STDOUT: %Animal.ref: type = name_ref Animal, file.%Animal.decl [concrete = constants.%Animal.type] // CHECK:STDOUT: %Eats.ref: type = name_ref Eats, file.%Eats.decl [concrete = constants.%Eats.type] // CHECK:STDOUT: } // CHECK:STDOUT: %F.decl: %F.type = fn_decl @F [concrete = constants.%F] { // CHECK:STDOUT: %e.patt: %pattern_type = value_binding_pattern e [concrete] // CHECK:STDOUT: %e.param_patt: %pattern_type = value_param_pattern %e.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %e.param: %Eats.type = value_param call_param0 // CHECK:STDOUT: %Eats.ref: type = name_ref Eats, file.%Eats.decl [concrete = constants.%Eats.type] // CHECK:STDOUT: %e: %Eats.type = value_binding e, %e.param // CHECK:STDOUT: } // CHECK:STDOUT: %G.decl: %G.type = fn_decl @G [concrete = constants.%G] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: interface @Eats { // CHECK:STDOUT: %Self: %Eats.type = symbolic_binding Self, 0 [symbolic = constants.%Self.247] // CHECK:STDOUT: %Eats.WithSelf.decl = interface_with_self_decl @Eats [concrete] // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = %Self // CHECK:STDOUT: witness = () // CHECK:STDOUT: // CHECK:STDOUT: !requires: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: interface @Animal { // CHECK:STDOUT: %Self: %Animal.type = symbolic_binding Self, 0 [symbolic = constants.%Self.c49] // CHECK:STDOUT: %Animal.WithSelf.decl = interface_with_self_decl @Animal [concrete] // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = %Self // CHECK:STDOUT: witness = () // CHECK:STDOUT: // CHECK:STDOUT: !requires: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: impl @Animal.type.as.Eats.impl: %Animal.ref as %Eats.ref { // CHECK:STDOUT: %Eats.impl_witness_table = impl_witness_table (), @Animal.type.as.Eats.impl [concrete] // CHECK:STDOUT: %Eats.impl_witness: = impl_witness %Eats.impl_witness_table [concrete = constants.%Eats.impl_witness] // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: witness = %Eats.impl_witness // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @F(%e.param: %Eats.type) { // CHECK:STDOUT: !entry: // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @G() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %F.ref: %F.type = name_ref F, file.%F.decl [concrete = constants.%F] // CHECK:STDOUT: %Animal.ref: type = name_ref Animal, file.%Animal.decl [concrete = constants.%Animal.type] // CHECK:STDOUT: %Eats.facet: %Eats.type = facet_value %Animal.ref, (constants.%Eats.impl_witness) [concrete = constants.%Eats.facet] // CHECK:STDOUT: %.loc23: %Eats.type = converted %Animal.ref, %Eats.facet [concrete = constants.%Eats.facet] // CHECK:STDOUT: %F.call: init %empty_tuple.type = call %F.ref(%.loc23) // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @Eats.WithSelf(constants.%Self.247) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @Animal.WithSelf(constants.%Self.c49) {} // CHECK:STDOUT: // CHECK:STDOUT: specific @Eats.WithSelf(constants.%Eats.facet) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: } // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/facet/early_rewrites.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/convert.carbon // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/facet/early_rewrites.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/facet/early_rewrites.carbon // Tests where `ImplWitnessAccess` on the RHS of a rewrite constraint is used in // other constraints within the same facet type in ways that require the RHS to // be evaluated to a concrete value before the facet type is resolved (such as // by passing it as a parameter to a generic class). This can be done either by // eagerly reading rewrite constraints from the same facet type, or by relying // on implied constraints which are checked later. // --- fail_todo_implied_constraint_on_self.carbon library "[[@TEST_NAME]]"; interface Z { let Z1:! type; let Z2:! type; } class C(T:! Z where .Z1 = ()) {} interface J { // Implied: .Z1 == () // CHECK:STDERR: fail_todo_implied_constraint_on_self.carbon:[[@LINE+7]]:26: error: cannot convert type `.Self` that implements `Z` into type implementing `Z where .(Z.Z1) = ()` [ConversionFailureFacetToFacet] // CHECK:STDERR: let J1:! Z where .Z2 = C(.Self); // CHECK:STDERR: ^~~~~~~~ // CHECK:STDERR: fail_todo_implied_constraint_on_self.carbon:[[@LINE-7]]:9: note: initializing generic parameter `T` declared here [InitializingGenericParam] // CHECK:STDERR: class C(T:! Z where .Z1 = ()) {} // CHECK:STDERR: ^ // CHECK:STDERR: let J1:! Z where .Z2 = C(.Self); } fn F(unused T:! J) {} class D; // Satisfies the implied constraint in J.J1. // CHECK:STDERR: fail_todo_implied_constraint_on_self.carbon:[[@LINE+7]]:38: error: cannot convert type `.Self` that implements `Z` into type implementing `Z where .(Z.Z1) = ()` [ConversionFailureFacetToFacet] // CHECK:STDERR: impl D as Z where .Z1 = () and .Z2 = C(.Self) {} // CHECK:STDERR: ^~~~~~~~ // CHECK:STDERR: fail_todo_implied_constraint_on_self.carbon:[[@LINE-21]]:9: note: initializing generic parameter `T` declared here [InitializingGenericParam] // CHECK:STDERR: class C(T:! Z where .Z1 = ()) {} // CHECK:STDERR: ^ // CHECK:STDERR: impl D as Z where .Z1 = () and .Z2 = C(.Self) {} fn G(T:! J where .J1 = D) { F(T); } // --- fail_todo_implied_constraint_on_associated_constant.carbon library "[[@TEST_NAME]]"; interface Z { let Z1:! type; let Z2:! type; } interface Tuple {} impl () as Tuple {} class C(T:! Tuple) {} interface J { // Implied: .Z1 impls Tuple // CHECK:STDERR: fail_todo_implied_constraint_on_associated_constant.carbon:[[@LINE+7]]:26: error: cannot convert type `.(Z.Z1)` into type implementing `Tuple` [ConversionFailureTypeToFacet] // CHECK:STDERR: let J1:! Z where .Z2 = C(.Z1); // CHECK:STDERR: ^~~~~~ // CHECK:STDERR: fail_todo_implied_constraint_on_associated_constant.carbon:[[@LINE-7]]:9: note: initializing generic parameter `T` declared here [InitializingGenericParam] // CHECK:STDERR: class C(T:! Tuple) {} // CHECK:STDERR: ^ // CHECK:STDERR: let J1:! Z where .Z2 = C(.Z1); } fn F(unused T:! J) {} class D; // Satisfies the implied constraint in J.J1. impl D as Z where .Z1 = () and .Z2 = C(()) {} fn G(T:! J where .J1 = D) { F(T); } // --- fail_todo_nested_constraint_visible_in_type_parameter.carbon library "[[@TEST_NAME]]"; interface Z { let Z1:! type; let Z2:! type; } class C(T:! Z where .Z1 = ()) {} interface J { // Implied: .Z1 == (), but this is also explicitly required for J1. // CHECK:STDERR: fail_todo_nested_constraint_visible_in_type_parameter.carbon:[[@LINE+7]]:43: error: cannot convert type `.Self` that implements `Z` into type implementing `Z where .(Z.Z1) = ()` [ConversionFailureFacetToFacet] // CHECK:STDERR: let J1:! (Z where .Z1 = ()) where .Z2 = C(.Self); // CHECK:STDERR: ^~~~~~~~ // CHECK:STDERR: fail_todo_nested_constraint_visible_in_type_parameter.carbon:[[@LINE-7]]:9: note: initializing generic parameter `T` declared here [InitializingGenericParam] // CHECK:STDERR: class C(T:! Z where .Z1 = ()) {} // CHECK:STDERR: ^ // CHECK:STDERR: let J1:! (Z where .Z1 = ()) where .Z2 = C(.Self); } fn F(unused T:! J) {} class D; // Satisfies the implied constraint in J.J1. // CHECK:STDERR: fail_todo_nested_constraint_visible_in_type_parameter.carbon:[[@LINE+7]]:38: error: cannot convert type `.Self` that implements `Z` into type implementing `Z where .(Z.Z1) = ()` [ConversionFailureFacetToFacet] // CHECK:STDERR: impl D as Z where .Z1 = () and .Z2 = C(.Self) {} // CHECK:STDERR: ^~~~~~~~ // CHECK:STDERR: fail_todo_nested_constraint_visible_in_type_parameter.carbon:[[@LINE-21]]:9: note: initializing generic parameter `T` declared here [InitializingGenericParam] // CHECK:STDERR: class C(T:! Z where .Z1 = ()) {} // CHECK:STDERR: ^ // CHECK:STDERR: impl D as Z where .Z1 = () and .Z2 = C(.Self) {} fn G(T:! J where .J1 = D) { F(T); } // --- fail_todo_outer_nested_constraint_visible_in_type_parameter.carbon library "[[@TEST_NAME]]"; interface Z { let Z1:! type; let Z2:! type; } class C(T:! Z where .Z1 = ()) {} interface J { // Implied: .Z1 == (), but this is also explicitly required for J1. // CHECK:STDERR: fail_todo_outer_nested_constraint_visible_in_type_parameter.carbon:[[@LINE+7]]:27: error: cannot convert type `.Self` that implements `Z` into type implementing `Z where .(Z.Z1) = ()` [ConversionFailureFacetToFacet] // CHECK:STDERR: let J1:! (Z where .Z2 = C(.Self)) where .Z1 = (); // CHECK:STDERR: ^~~~~~~~ // CHECK:STDERR: fail_todo_outer_nested_constraint_visible_in_type_parameter.carbon:[[@LINE-7]]:9: note: initializing generic parameter `T` declared here [InitializingGenericParam] // CHECK:STDERR: class C(T:! Z where .Z1 = ()) {} // CHECK:STDERR: ^ // CHECK:STDERR: let J1:! (Z where .Z2 = C(.Self)) where .Z1 = (); } fn F(unused T:! J) {} class D; // Satisfies the implied constraint in J.J1. // CHECK:STDERR: fail_todo_outer_nested_constraint_visible_in_type_parameter.carbon:[[@LINE+7]]:38: error: cannot convert type `.Self` that implements `Z` into type implementing `Z where .(Z.Z1) = ()` [ConversionFailureFacetToFacet] // CHECK:STDERR: impl D as Z where .Z1 = () and .Z2 = C(.Self) {} // CHECK:STDERR: ^~~~~~~~ // CHECK:STDERR: fail_todo_outer_nested_constraint_visible_in_type_parameter.carbon:[[@LINE-21]]:9: note: initializing generic parameter `T` declared here [InitializingGenericParam] // CHECK:STDERR: class C(T:! Z where .Z1 = ()) {} // CHECK:STDERR: ^ // CHECK:STDERR: impl D as Z where .Z1 = () and .Z2 = C(.Self) {} fn G(T:! J where .J1 = D) { F(T); } // --- fail_todo_earlier_constraint_visible_in_type_parameter.carbon library "[[@TEST_NAME]]"; interface Z { let Z1:! type; let Z2:! type; } class C(T:! Z where .Z1 = ()) {} interface J { // Implied: .Z1 == (), but this is also explicitly required for J1. // CHECK:STDERR: fail_todo_earlier_constraint_visible_in_type_parameter.carbon:[[@LINE+7]]:39: error: cannot convert type `.Self` that implements `Z` into type implementing `Z where .(Z.Z1) = ()` [ConversionFailureFacetToFacet] // CHECK:STDERR: let J1:! Z where .Z1 = () and .Z2 = C(.Self); // CHECK:STDERR: ^~~~~~~~ // CHECK:STDERR: fail_todo_earlier_constraint_visible_in_type_parameter.carbon:[[@LINE-7]]:9: note: initializing generic parameter `T` declared here [InitializingGenericParam] // CHECK:STDERR: class C(T:! Z where .Z1 = ()) {} // CHECK:STDERR: ^ // CHECK:STDERR: let J1:! Z where .Z1 = () and .Z2 = C(.Self); } fn F(unused T:! J) {} class D; // Satisfies the implied constraint in J.J1. // CHECK:STDERR: fail_todo_earlier_constraint_visible_in_type_parameter.carbon:[[@LINE+7]]:38: error: cannot convert type `.Self` that implements `Z` into type implementing `Z where .(Z.Z1) = ()` [ConversionFailureFacetToFacet] // CHECK:STDERR: impl D as Z where .Z1 = () and .Z2 = C(.Self) {} // CHECK:STDERR: ^~~~~~~~ // CHECK:STDERR: fail_todo_earlier_constraint_visible_in_type_parameter.carbon:[[@LINE-21]]:9: note: initializing generic parameter `T` declared here [InitializingGenericParam] // CHECK:STDERR: class C(T:! Z where .Z1 = ()) {} // CHECK:STDERR: ^ // CHECK:STDERR: impl D as Z where .Z1 = () and .Z2 = C(.Self) {} fn G(T:! J where .J1 = D) { F(T); } // --- fail_todo_later_constraint_visible_in_type_parameter.carbon library "[[@TEST_NAME]]"; interface Z { let Z1:! type; let Z2:! type; } class C(T:! Z where .Z1 = ()) {} interface J { // Implied: .Z1 == (), but this is also explicitly required for J1. // CHECK:STDERR: fail_todo_later_constraint_visible_in_type_parameter.carbon:[[@LINE+7]]:26: error: cannot convert type `.Self` that implements `Z` into type implementing `Z where .(Z.Z1) = ()` [ConversionFailureFacetToFacet] // CHECK:STDERR: let J1:! Z where .Z2 = C(.Self) and .Z1 = (); // CHECK:STDERR: ^~~~~~~~ // CHECK:STDERR: fail_todo_later_constraint_visible_in_type_parameter.carbon:[[@LINE-7]]:9: note: initializing generic parameter `T` declared here [InitializingGenericParam] // CHECK:STDERR: class C(T:! Z where .Z1 = ()) {} // CHECK:STDERR: ^ // CHECK:STDERR: let J1:! Z where .Z2 = C(.Self) and .Z1 = (); } fn F(unused T:! J) {} class D; // Satisfies the implied constraint in J.J1. // CHECK:STDERR: fail_todo_later_constraint_visible_in_type_parameter.carbon:[[@LINE+7]]:38: error: cannot convert type `.Self` that implements `Z` into type implementing `Z where .(Z.Z1) = ()` [ConversionFailureFacetToFacet] // CHECK:STDERR: impl D as Z where .Z1 = () and .Z2 = C(.Self) {} // CHECK:STDERR: ^~~~~~~~ // CHECK:STDERR: fail_todo_later_constraint_visible_in_type_parameter.carbon:[[@LINE-21]]:9: note: initializing generic parameter `T` declared here [InitializingGenericParam] // CHECK:STDERR: class C(T:! Z where .Z1 = ()) {} // CHECK:STDERR: ^ // CHECK:STDERR: impl D as Z where .Z1 = () and .Z2 = C(.Self) {} fn G(T:! J where .J1 = D) { F(T); } // --- early_rewrite_applied.carbon library "[[@TEST_NAME]]"; interface Z { let Z1:! type; let Z2:! type; } interface Tuple {} impl () as Tuple {} class C(T:! Tuple) {} interface J { // Implied: .Z1 == (), but this is also explicitly required for J1. let J1:! Z where .Z1 = () and .Z2 = C(.Z1); } fn F(unused T:! J) {} class D; // Satisfies the implied constraint in J.J1. impl D as Z where .Z1 = () and .Z2 = C(()) {} fn G(T:! J where .J1 = D) { F(T); } // --- early_rewrite_applied_from_nested_self.carbon library "[[@TEST_NAME]]"; interface Z { let Z1:! type; let Z2:! type; } interface Tuple {} impl () as Tuple {} class C(T:! Tuple) {} interface J { // Implied: .Z1 == (), but this is also explicitly required for J1. let J1:! (Z where .Z1 = ()) where .Z2 = C(.Z1); } fn F(unused T:! J) {} class D; // Satisfies the implied constraint in J.J1. impl D as Z where .Z1 = () and .Z2 = C(()) {} fn G(T:! J where .J1 = D) { F(T); } // --- fail_todo_early_rewrite_applied_extra_indirection.carbon library "[[@TEST_NAME]]"; interface Z { let Z1:! type; let Z2:! type; } interface Y { let Y1:! Z; } interface Tuple {} impl () as Tuple {} class C(T:! Tuple) {} interface J { // Implied: .Z1 == (), but this is also explicitly required for J1. let J1:! Y & Z where .Y1 = .Self and .Z1 = () and .Z2 = C(.Y1.Z1); } fn F(unused T:! J) {} class D; // Satisfies the implied constraint in J.J1. impl D as Z where .Z1 = () and .Z2 = C(()) {} impl D as Y where .Y1 = D {} // CHECK:STDERR: fail_todo_early_rewrite_applied_extra_indirection.carbon:[[@LINE+4]]:24: error: cannot convert type `D` into type implementing `Z & Y where .(Y.Y1) = .Self as Z and .(Z.Z1) = () and .(Z.Z2) = C(() as Tuple)` [ConversionFailureTypeToFacet] // CHECK:STDERR: fn G(T:! J where .J1 = D) { // CHECK:STDERR: ^ // CHECK:STDERR: fn G(T:! J where .J1 = D) { F(T); } // --- fail_todo_resolved_constraint_visible_in_type_parameter.carbon library "[[@TEST_NAME]]"; interface Y { let Y1:! type; } interface Z { let Z1:! Y; let Z2:! type; let Z3:! type; } interface Tuple {} impl () as Tuple {} class C(T:! Tuple) {} interface J { // Implied: .Y1 impls Tuple, but this is also explicitly required for J1. let J1:! Z & Y where .Y1 = () and .Z1 = .Self and .Z2 = .Z1.Y1 and .Z3 = C(.Z2); } fn F(unused T:! J) {} class D; // Satisfies the implied constraint in J.J1. impl D as Y where .Y1 = () {} // CHECK:STDERR: fail_todo_resolved_constraint_visible_in_type_parameter.carbon:[[@LINE+4]]:25: error: cannot convert type `.Self` that implements `Z` into type implementing `Y` [ConversionFailureFacetToFacet] // CHECK:STDERR: impl D as Z where .Z1 = .Self and .Z2 = () and .Z3 = C(()) {} // CHECK:STDERR: ^~~~~ // CHECK:STDERR: impl D as Z where .Z1 = .Self and .Z2 = () and .Z3 = C(()) {} // CHECK:STDERR: fail_todo_resolved_constraint_visible_in_type_parameter.carbon:[[@LINE+4]]:24: error: cannot convert type `D` into type implementing `Y & Z where .(Y.Y1) = () and .(Z.Z1) = .Self as Y and .(Z.Z2) = () and .(Z.Z3) = C(() as Tuple)` [ConversionFailureTypeToFacet] // CHECK:STDERR: fn G(T:! J where .J1 = D) { // CHECK:STDERR: ^ // CHECK:STDERR: fn G(T:! J where .J1 = D) { F(T); } ================================================ FILE: toolchain/check/testdata/facet/facet_assoc_const.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/convert.carbon // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/facet/facet_assoc_const.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/facet/facet_assoc_const.carbon // --- success.carbon library "[[@TEST_NAME]]"; interface I { let T:! type; } fn F(unused T:! I where .T = {}) {} // --- success_associated.carbon library "[[@TEST_NAME]]"; interface I { let T:! type; let U:! type; } fn F(unused T:! I where .T = .U) {} // --- fail_two_different.carbon library "[[@TEST_NAME]]"; interface L { let W:! type; } // CHECK:STDERR: fail_two_different.carbon:[[@LINE+4]]:17: error: associated constant `.(L.W)` given two different values `{}` and `()` [AssociatedConstantWithDifferentValues] // CHECK:STDERR: fn F(unused T:! L where .W = {} and .W = ()) {} // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: fn F(unused T:! L where .W = {} and .W = ()) {} // --- fail_two_different_first_associated.carbon library "[[@TEST_NAME]]"; interface L { let W:! type; let X:! type; } // CHECK:STDERR: fail_two_different_first_associated.carbon:[[@LINE+4]]:17: error: associated constant `.(L.W)` given two different values `.(L.X)` and `()` [AssociatedConstantWithDifferentValues] // CHECK:STDERR: fn F(unused T:! L where .W = .X and .W = ()) {} // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: fn F(unused T:! L where .W = .X and .W = ()) {} // --- fail_two_different_second_associated.carbon library "[[@TEST_NAME]]"; interface L { let W:! type; let X:! type; } // CHECK:STDERR: fail_two_different_second_associated.carbon:[[@LINE+4]]:17: error: associated constant `.(L.W)` given two different values `()` and `.(L.X)` [AssociatedConstantWithDifferentValues] // CHECK:STDERR: fn F(unused T:! L where .W = () and .W = .X) {} // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: fn F(unused T:! L where .W = () and .W = .X) {} // --- fail_two_different_first_bad.carbon library "[[@TEST_NAME]]"; interface L { let W:! type; } // CHECK:STDERR: fail_two_different_first_bad.carbon:[[@LINE+4]]:30: error: name `BAD5` not found [NameNotFound] // CHECK:STDERR: fn F(unused T:! L where .W = BAD5 and .W = ()) {} // CHECK:STDERR: ^~~~ // CHECK:STDERR: fn F(unused T:! L where .W = BAD5 and .W = ()) {} // --- fail_two_different_second_bad.carbon library "[[@TEST_NAME]]"; interface L { let W:! type; } // CHECK:STDERR: fail_two_different_second_bad.carbon:[[@LINE+4]]:42: error: name `BAD6` not found [NameNotFound] // CHECK:STDERR: fn F(unused T:! L where .W = {} and .W = BAD6) {} // CHECK:STDERR: ^~~~ // CHECK:STDERR: fn F(unused T:! L where .W = {} and .W = BAD6) {} // --- fail_two_different_both_bad.carbon library "[[@TEST_NAME]]"; interface L { let W:! type; } // CHECK:STDERR: fail_two_different_both_bad.carbon:[[@LINE+8]]:30: error: name `BAD7` not found [NameNotFound] // CHECK:STDERR: fn F(unused T:! L where .W = BAD7 and .W = BAD8) {} // CHECK:STDERR: ^~~~ // CHECK:STDERR: // CHECK:STDERR: fail_two_different_both_bad.carbon:[[@LINE+4]]:44: error: name `BAD8` not found [NameNotFound] // CHECK:STDERR: fn F(unused T:! L where .W = BAD7 and .W = BAD8) {} // CHECK:STDERR: ^~~~ // CHECK:STDERR: fn F(unused T:! L where .W = BAD7 and .W = BAD8) {} // --- fail_two_different_combined_from_bitand.carbon library "[[@TEST_NAME]]"; interface L { let W:! type; } // CHECK:STDERR: fail_two_different_combined_from_bitand.carbon:[[@LINE+4]]:17: error: associated constant `.(L.W)` given two different values `()` and `{}` [AssociatedConstantWithDifferentValues] // CHECK:STDERR: fn F(unused T:! (L where .W = {}) & (L where .W = ())) {} // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: fn F(unused T:! (L where .W = {}) & (L where .W = ())) {} // --- two_different_combined_from_impl_and_facet.carbon library "[[@TEST_NAME]]"; interface L { let W:! type; } interface M {} impl forall [T:! M] T as L where .W = () {} fn F(unused T:! M & (L where .W = {})) {} class C; impl C as L where .W = {} {} impl C as M {} fn G() { F(C); } // --- fail_two_different_combined_from_final_impl_and_facet.carbon library "[[@TEST_NAME]]"; interface L { let W:! type; } interface M {} final impl forall [T:! M] T as L where .W = () {} fn G(T:! M & L, a: T.W) -> () { return a; } fn H(T:! L where .W = {}, a: T.W) -> {} { return a; } fn F(T:! M & (L where .W = {}), a: T.W) { // One of `b` or `c` must fail, because `T.W` is either found to be `()` from // the impl or `{}` from the facet type of T. let unused b: () = G(T, a); // TODO: This diagnostic sucks. Can we make the facet type's value take // precidence over final, since that's what is written in the code and more // likely to show up in diagnostics? Or should we diagnose `T` as being // invalid directly, where we can see both `.W` values and print them? // // CHECK:STDERR: fail_two_different_combined_from_final_impl_and_facet.carbon:[[@LINE+7]]:22: error: cannot convert type `T` that implements `L & M where .(L.W) = {}` into type implementing `L where .(L.W) = {}` [ConversionFailureFacetToFacet] // CHECK:STDERR: let unused c: {} = H(T, a); // CHECK:STDERR: ^~~~~~~ // CHECK:STDERR: fail_two_different_combined_from_final_impl_and_facet.carbon:[[@LINE-15]]:6: note: initializing generic parameter `T` declared here [InitializingGenericParam] // CHECK:STDERR: fn H(T:! L where .W = {}, a: T.W) -> {} { return a; } // CHECK:STDERR: ^ // CHECK:STDERR: let unused c: {} = H(T, a); } // --- fail_many_different.carbon library "[[@TEST_NAME]]"; interface L { let W:! type; } // CHECK:STDERR: fail_many_different.carbon:[[@LINE+4]]:17: error: associated constant `.(L.W)` given two different values `((), (), ())` and `({}, (), ())` [AssociatedConstantWithDifferentValues] // CHECK:STDERR: fn G(unused T:! L where .W = ((), (), ()) and .W = ({}, (), ()) and .W = ({}, {}, ()) and .W = ({}, (), {})) {} // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: fn G(unused T:! L where .W = ((), (), ()) and .W = ({}, (), ()) and .W = ({}, {}, ()) and .W = ({}, (), {})) {} // --- rewrite_uses_second_facet.carbon library "[[@TEST_NAME]]"; interface M { let X:! type; let Y:! type; } fn F(T:! M where .X = (), U:! M where .Y = T.X) -> U.Y { return (); } // --- fail_rewrite_conflicts_with_second_facet.carbon library "[[@TEST_NAME]]"; interface M { let X:! type; let Y:! type; } // CHECK:STDERR: fail_rewrite_conflicts_with_second_facet.carbon:[[@LINE+4]]:38: error: associated constant `.(M.Y)` given two different values `T.(M.X)` and `.(M.X)` [AssociatedConstantWithDifferentValues] // CHECK:STDERR: fn F(T:! M where .X = (), unused U:! M where .Y = T.X and .Y = .X) {} // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: fn F(T:! M where .X = (), unused U:! M where .Y = T.X and .Y = .X) {} // --- repeated.carbon library "[[@TEST_NAME]]"; interface M { let X:! type; } fn F(unused T:! M where .X = {} and .X = {}) {} fn G(T:! M where .X = {}) { F(T); } // --- repeated_associated.carbon library "[[@TEST_NAME]]"; interface M { let X:! type; let Y:! type; } fn F(unused T:! M where .X = .Y and .X = .Y) {} fn G(T:! M where .X = () and .Y = ()) { F(T); } // --- repeated_concrete_value_and_associated.carbon library "[[@TEST_NAME]]"; interface M { let X:! type; let Y:! type; } fn F1(unused T:! M where .X = () and .Y = .X and .X = .Y) {} fn F2(unused T:! M where .X = () and .X = .X) {} fn G(T:! M where .X = () and .Y = ()) { F1(T); F2(T); } // --- repeated_with_bitand.carbon library "[[@TEST_NAME]]"; interface M { let X:! type; let Y:! type; } fn F1(T:! (M where .X = .Y) & (M where .X = .Y and .Y = ())) -> T.X { return (); } fn F2(T:! (M where .X = .Y and .Y = ()) & (M where .X = .Y)) -> T.X { return (); } // --- fail_repeated_and_different.carbon library "[[@TEST_NAME]]"; interface M { let X:! type; } // CHECK:STDERR: fail_repeated_and_different.carbon:[[@LINE+4]]:17: error: associated constant `.(M.X)` given two different values `{}` and `()` [AssociatedConstantWithDifferentValues] // CHECK:STDERR: fn F(unused T:! M where .X = {} and .X = () and .X = {}) {} // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: fn F(unused T:! M where .X = {} and .X = () and .X = {}) {} // --- fail_cycle_single.carbon library "[[@TEST_NAME]]"; interface M { let X:! type; } // This fails because it resolves to `.X = .X` which is cyclical. // // CHECK:STDERR: fail_cycle_single.carbon:[[@LINE+4]]:17: error: found cycle in facet type constraint for `.(M.X)` [FacetTypeConstraintCycle] // CHECK:STDERR: fn F(unused T:! M where .X = .X) {} // CHECK:STDERR: ^~~~~~~~~~~~~~~ // CHECK:STDERR: fn F(unused T:! M where .X = .X) {} // Even though `.X = ()` is specified, the rewrites are resolved left to right // and a cycle `.X = .X` is found first. // // CHECK:STDERR: fail_cycle_single.carbon:[[@LINE+4]]:17: error: found cycle in facet type constraint for `.(M.X)` [FacetTypeConstraintCycle] // CHECK:STDERR: fn G(unused T:! M where .X = .X and .X = ()) {} // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: fn G(unused T:! M where .X = .X and .X = ()) {} // --- fail_cycle.carbon library "[[@TEST_NAME]]"; interface M { let X:! type; let Y:! type; let Z:! type; } // This fails because it resolves to `.X = .X` which is cyclical. // The value of .X and .Y becomes but .Z is still valid. // //@dump-sem-ir-begin // CHECK:STDERR: fail_cycle.carbon:[[@LINE+4]]:17: error: found cycle in facet type constraint for `.(M.Y)` [FacetTypeConstraintCycle] // CHECK:STDERR: fn F(unused T:! M where .X = .Y and .Y = .X and .Z = ()) {} // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: fn F(unused T:! M where .X = .Y and .Y = .X and .Z = ()) {} //@dump-sem-ir-end // --- fail_cycle_between_interfaces.carbon library "[[@TEST_NAME]]"; interface I { let X1:! type; let X2:! type; } interface J { let X3:! type; } // This fails because it resolves to `.X1 = .X1` which is cyclical. // // CHECK:STDERR: fail_cycle_between_interfaces.carbon:[[@LINE+4]]:17: error: found cycle in facet type constraint for `.(J.X3)` [FacetTypeConstraintCycle] // CHECK:STDERR: fn G(unused T:! I & J where .X1 = .X3 and .X2 = .X1 and .X3 = .X2) {} // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: fn G(unused T:! I & J where .X1 = .X3 and .X2 = .X1 and .X3 = .X2) {} // --- fail_indirect_cycle.carbon library "[[@TEST_NAME]]"; interface I { let X1:! type; let X2:! type; } // This fails because it resolves to `.X1 = .X1**` which is cyclical. // // CHECK:STDERR: fail_indirect_cycle.carbon:[[@LINE+4]]:10: error: found cycle in facet type constraint for `.(I.X2)` [FacetTypeConstraintCycle] // CHECK:STDERR: fn F(T:! I where .X1 = .X2* and .X2 = .X1*); // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: fn F(T:! I where .X1 = .X2* and .X2 = .X1*); class C(T:! type); // This fails because it resolves to `.X1 = C(C(.X1))` which is cyclical. // // CHECK:STDERR: fail_indirect_cycle.carbon:[[@LINE+4]]:10: error: found cycle in facet type constraint for `.(I.X2)` [FacetTypeConstraintCycle] // CHECK:STDERR: fn G(T:! I where .X1 = C(.X2) and .X2 = C(.X1)); // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: fn G(T:! I where .X1 = C(.X2) and .X2 = C(.X1)); // --- fail_complex_indirect_cycle.carbon library "[[@TEST_NAME]]"; interface I { let X1:! type; let X2:! type; let X3:! type; } class C(T:! type, U:! type); // This fails because it resolves to `.X1 = C(C(.X3, .X1), .X3)` which is // cyclical. // // CHECK:STDERR: fail_complex_indirect_cycle.carbon:[[@LINE+4]]:10: error: found cycle in facet type constraint for `.(I.X2)` [FacetTypeConstraintCycle] // CHECK:STDERR: fn F(T:! I where .X1 = C(.X2, .X3) and .X2 = C(.X3, .X1)); // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: fn F(T:! I where .X1 = C(.X2, .X3) and .X2 = C(.X3, .X1)); // --- exponential_large.carbon library "[[@TEST_NAME]]"; interface Z { let T0:! type; let T1:! type; let T2:! type; let T3:! type; let T4:! type; let T5:! type; let T6:! type; let T7:! type; let T8:! type; let T9:! type; } // A naive attempt to resolve the rewrite rules will run take minutes to // complete, since the resulting RHS values are exponential in size, and a naive // approach can recursively rebuild the RHS values from the ground up // repeatedly. fn F( T:! Z where .T0 = (.T1, .T1, .T1, .T1, .T1, .T1, .T1, .T1, .T1, .T1, .T1, .T1) and .T1 = (.T2, .T2, .T2, .T2, .T2, .T2, .T2, .T2, .T2, .T2, .T2, .T2) and .T2 = (.T3, .T3, .T3, .T3, .T3, .T3, .T3, .T3, .T3, .T3, .T3, .T3) and .T3 = (.T4, .T4, .T4, .T4, .T4, .T4, .T4, .T4, .T4, .T4, .T4, .T4) and .T4 = (.T5, .T5, .T5, .T5, .T5, .T5, .T5, .T5, .T5, .T5, .T5, .T5) and .T5 = (.T6, .T6, .T6, .T6, .T6, .T6, .T6, .T6, .T6, .T6, .T6, .T6) and .T6 = (.T7, .T7, .T7, .T7, .T7, .T7, .T7, .T7, .T7, .T7, .T7, .T7) and .T7 = (.T8, .T8, .T8, .T8, .T8, .T8, .T8, .T8, .T8, .T8, .T8, .T8) and .T8 = (.T9, .T9, .T9, .T9, .T9, .T9, .T9, .T9, .T9, .T9, .T9, .T9) and .T9 = () ); // --- fail_exponential_large_cycle.carbon library "[[@TEST_NAME]]"; interface Z { let T0:! type; let T1:! type; let T2:! type; let T3:! type; let T4:! type; let T5:! type; let T6:! type; let T7:! type; let T8:! type; let T9:! type; } // A naive attempt to resolve the rewrite rules will run take minutes to // complete, since the resulting RHS values are exponential in size, and a naive // approach can recursively rebuild the RHS values from the ground up // repeatedly. fn F( // CHECK:STDERR: fail_exponential_large_cycle.carbon:[[@LINE+4]]:9: error: found cycle in facet type constraint for `.(Z.T0)` [FacetTypeConstraintCycle] // CHECK:STDERR: T:! Z where // CHECK:STDERR: ^~~~~~~ // CHECK:STDERR: T:! Z where .T9 = .T0 and .T8 = (.T9, .T9, .T9, .T9, .T9, .T9, .T9, .T9, .T9, .T9, .T9, .T9) and .T7 = (.T8, .T8, .T8, .T8, .T8, .T8, .T8, .T8, .T8, .T8, .T8, .T8) and .T6 = (.T7, .T7, .T7, .T7, .T7, .T7, .T7, .T7, .T7, .T7, .T7, .T7) and .T5 = (.T6, .T6, .T6, .T6, .T6, .T6, .T6, .T6, .T6, .T6, .T6, .T6) and .T4 = (.T5, .T5, .T5, .T5, .T5, .T5, .T5, .T5, .T5, .T5, .T5, .T5) and .T3 = (.T4, .T4, .T4, .T4, .T4, .T4, .T4, .T4, .T4, .T4, .T4, .T4) and .T2 = (.T3, .T3, .T3, .T3, .T3, .T3, .T3, .T3, .T3, .T3, .T3, .T3) and .T1 = (.T2, .T2, .T2, .T2, .T2, .T2, .T2, .T2, .T2, .T2, .T2, .T2) and .T0 = (.T1, .T1, .T1, .T1, .T1, .T1, .T1, .T1, .T1, .T1, .T1, .T1) ); // --- non-type.carbon library "[[@TEST_NAME]]"; interface N { let Y:! {.a: {}}; } fn F(unused T:! N where .Y = {.a = {}}) { } // --- non-type_repeated.carbon library "[[@TEST_NAME]]"; interface N { let Y:! {.a: {}}; } fn F(unused T:! N where .Y = {.a = {}} and .Y = {.a = {}}) { } // --- fail_non-type_different.carbon library "[[@TEST_NAME]]"; interface N { let Y:! {.a: type}; } // CHECK:STDERR: fail_non-type_different.carbon:[[@LINE+4]]:17: error: associated constant `.(N.Y)` given two different values `{.a = {}}` and `{.a = ()}` [AssociatedConstantWithDifferentValues] // CHECK:STDERR: fn F(unused T:! N where .Y = {.a = {}} and .Y = {.a = ()}) {} // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: fn F(unused T:! N where .Y = {.a = {}} and .Y = {.a = ()}) {} // --- self_repeated_explicitly.carbon library "[[@TEST_NAME]]"; interface N { let Y1:! type; let Y2:! type; } fn F(unused T:! N where .Y2 = .Y1 and .Y2 = .Self.Y1) { } // --- self_repeated_explicitly_with_value.carbon library "[[@TEST_NAME]]"; interface N { let Y1:! type; let Y2:! type; } fn F(unused T:! N where .Y1 = () and .Y2 = .Y1 and .Y2 = .Self.Y1) { } // --- fail_cycle_through_self_reference.carbon library "[[@TEST_NAME]]"; interface Z { let T:! type; let U:! Z; } // CHECK:STDERR: fail_cycle_through_self_reference.carbon:[[@LINE+4]]:17: error: found cycle in facet type constraint for `.(Z.T)` [FacetTypeConstraintCycle] // CHECK:STDERR: fn F(unused A:! Z where .T = .U.T and .U = .Self) {} // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: fn F(unused A:! Z where .T = .U.T and .U = .Self) {} // --- reference_same_constant_in_different_self.carbon library "[[@TEST_NAME]]"; interface Z { let T:! type; let U:! Z; } fn F(A:! Z where .T = (), unused B:! Z where .T = .U.T and .U = A) {} // --- non_cycle_with_self_reference.carbon library "[[@TEST_NAME]]"; interface Z { let T:! type; let U:! type; let V:! Z; } fn F(A:! Z where .T = .V.U and .V = .Self and .U = ()) -> A.T { return (); } // --- fail_cycle_with_unrelated_associated_constant.carbon library "[[@TEST_NAME]]"; interface Z { let T0:! type; let T1:! type; let T2:! type; let T3:! type; } // CHECK:STDERR: fail_cycle_with_unrelated_associated_constant.carbon:[[@LINE+4]]:17: error: found cycle in facet type constraint for `.(Z.T1)` [FacetTypeConstraintCycle] // CHECK:STDERR: fn F(unused T:! Z where .T0 = .T1 and .T1 = .T0 and .T2 = .T3) {} // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: fn F(unused T:! Z where .T0 = .T1 and .T1 = .T0 and .T2 = .T3) {} // --- fail_cycle_with_branching_in_rhs.carbon library "[[@TEST_NAME]]"; interface Z { let T0:! type; let T1:! type; let T2:! type; let T3:! type; let T4:! type; } // TODO: There should only be one diagnostic here. // // CHECK:STDERR: fail_cycle_with_branching_in_rhs.carbon:[[@LINE+4]]:17: error: found cycle in facet type constraint for `.(Z.T3)` [FacetTypeConstraintCycle] // CHECK:STDERR: fn F(unused T:! Z where .T0 = .T1 and .T1 = (.T2, .T3) and .T2 = .T4 and .T3 = .T1) {} // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: fn F(unused T:! Z where .T0 = .T1 and .T1 = (.T2, .T3) and .T2 = .T4 and .T3 = .T1) {} // CHECK:STDERR: fail_cycle_with_branching_in_rhs.carbon:[[@LINE+4]]:17: error: found cycle in facet type constraint for `.(Z.T1)` [FacetTypeConstraintCycle] // CHECK:STDERR: fn G(unused T:! Z where .T0 = .T1 and .T1 = (.T2, .T3) and .T2 = .T4 and .T3 = .T0) {} // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: fn G(unused T:! Z where .T0 = .T1 and .T1 = (.T2, .T3) and .T2 = .T4 and .T3 = .T0) {} // --- no_cycle_with_branching_in_rhs.carbon library "[[@TEST_NAME]]"; interface Z { let T0:! type; let T1:! type; let T2:! type; let T3:! type; let T4:! type; let T5:! type; } // These create misdiagnostics if the resolving algorithms messes up tracking // its stack during replacements by leaving either of .T2 or .T3 on the stack // (from the RHS of .T1) while resolving the other. Or it can fail to apply the // () up the chain correctly. fn F(T:! Z where .T0 = .T1 and .T1 = (.T2, .T3) and .T2 = .T4 and .T4 = () and .T3 = .T2) -> T.T0 { return ((), ()); } fn G(T:! Z where .T0 = .T1 and .T1 = (.T2, .T3) and .T2 = .T4 and .T4 = .T5 and .T5 = () and .T3 = .T2) -> T.T0 { return ((), ()); } fn H(T:! Z where .T0 = .T1 and .T1 = (.T2, .T3) and .T2 = (.T4, ()) and .T3 = .T2 and .T4 = {}) -> T.T0 { return (({}, ()), ({}, ())); } fn I(T:! Z where .T0 = .T1 and .T1 = (.T2, .T3) and .T2 = .T4 and .T3 = .T2 and .T4 = ()) -> T.T0 { return ((), ()); } fn J(T:! Z where .T0 = .T1 and .T1 = (.T2, .T3) and .T2 = .T4 and .T4 = .T5 and .T3 = .T2 and .T5 = ()) -> T.T0 { return ((), ()); } // --- indirection_through_self_rhs.carbon library "[[@TEST_NAME]]"; interface I { let I1:! type; let I2:! type; } interface J { let J1:! I; } // The value of .I1 is (), but to know that requires resolving .J1 first then // .J1.I2. fn F(T:! I & J where .J1 = .Self and .I1 = .J1.I2 and .I2 = ()) -> T.I1 { return (); } // --- indirection_through_not_self_rhs.carbon library "[[@TEST_NAME]]"; interface I { let I1:! type; let I2:! type; } interface J { let J1:! I; } // The value of .I1 is (), but to know that requires resolving .J1 first then // .J1.I2. fn F(U:! I where .I2 = (), T:! I & J where .J1 = U and .I1 = .J1.I2) -> T.I1 { return (); } // --- indirection_through_unresolved_access_rhs.carbon library "[[@TEST_NAME]]"; interface I { let I1:! type; let I2:! type; } interface J { let J1:! I; } // If we assume the nested `.J1` access will resolve to a facet value, we may // loop forever trying to resolve the `.I2` access. We should gracefully accept // that it does not resolve further. fn F(unused T:! I & J where .I1 = .J1.I2) {} // CHECK:STDOUT: --- fail_cycle.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %M.type: type = facet_type <@M> [concrete] // CHECK:STDOUT: %M.assoc_type: type = assoc_entity_type @M [concrete] // CHECK:STDOUT: %assoc0: %M.assoc_type = assoc_entity element0, @M.WithSelf.%X [concrete] // CHECK:STDOUT: %assoc1: %M.assoc_type = assoc_entity element1, @M.WithSelf.%Y [concrete] // CHECK:STDOUT: %assoc2: %M.assoc_type = assoc_entity element2, @M.WithSelf.%Z [concrete] // CHECK:STDOUT: %.Self.b4d: %M.type = symbolic_binding .Self [symbolic_self] // CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] // CHECK:STDOUT: %.Self.binding.as_type: type = symbolic_binding_type .Self, %.Self.b4d [symbolic_self] // CHECK:STDOUT: %M.lookup_impl_witness: = lookup_impl_witness %.Self.b4d, @M [symbolic_self] // CHECK:STDOUT: %impl.elem0: type = impl_witness_access %M.lookup_impl_witness, element0 [symbolic_self] // CHECK:STDOUT: %impl.elem1: type = impl_witness_access %M.lookup_impl_witness, element1 [symbolic_self] // CHECK:STDOUT: %impl.elem2: type = impl_witness_access %M.lookup_impl_witness, element2 [symbolic_self] // CHECK:STDOUT: %empty_tuple: %empty_tuple.type = tuple_value () [concrete] // CHECK:STDOUT: %F.type: type = fn_type @F [concrete] // CHECK:STDOUT: %F: %F.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: %F.decl: %F.type = fn_decl @F [concrete = constants.%F] { // CHECK:STDOUT: %T.patt: = symbolic_binding_pattern T, 0 [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %.loc13_19.1: type = splice_block %.loc13_19.2 [concrete = ] { // CHECK:STDOUT: // CHECK:STDOUT: %M.ref: type = name_ref M, file.%M.decl [concrete = constants.%M.type] // CHECK:STDOUT: // CHECK:STDOUT: %.Self.ref.loc13_25: %M.type = name_ref .Self, %.Self.2 [symbolic_self = constants.%.Self.b4d] // CHECK:STDOUT: %.Self.as_type.loc13_25: type = facet_access_type %.Self.ref.loc13_25 [symbolic_self = constants.%.Self.binding.as_type] // CHECK:STDOUT: %.loc13_25: type = converted %.Self.ref.loc13_25, %.Self.as_type.loc13_25 [symbolic_self = constants.%.Self.binding.as_type] // CHECK:STDOUT: %X.ref.loc13_25: %M.assoc_type = name_ref X, @X.%assoc0 [concrete = constants.%assoc0] // CHECK:STDOUT: %impl.elem0.loc13_25: type = impl_witness_access constants.%M.lookup_impl_witness, element0 [symbolic_self = constants.%impl.elem0] // CHECK:STDOUT: %.Self.ref.loc13_30: %M.type = name_ref .Self, %.Self.2 [symbolic_self = constants.%.Self.b4d] // CHECK:STDOUT: %.Self.as_type.loc13_30: type = facet_access_type %.Self.ref.loc13_30 [symbolic_self = constants.%.Self.binding.as_type] // CHECK:STDOUT: %.loc13_30: type = converted %.Self.ref.loc13_30, %.Self.as_type.loc13_30 [symbolic_self = constants.%.Self.binding.as_type] // CHECK:STDOUT: %Y.ref.loc13_30: %M.assoc_type = name_ref Y, @Y.%assoc1 [concrete = constants.%assoc1] // CHECK:STDOUT: %impl.elem1.loc13_30: type = impl_witness_access constants.%M.lookup_impl_witness, element1 [symbolic_self = constants.%impl.elem1] // CHECK:STDOUT: %.Self.ref.loc13_37: %M.type = name_ref .Self, %.Self.2 [symbolic_self = constants.%.Self.b4d] // CHECK:STDOUT: %.Self.as_type.loc13_37: type = facet_access_type %.Self.ref.loc13_37 [symbolic_self = constants.%.Self.binding.as_type] // CHECK:STDOUT: %.loc13_37: type = converted %.Self.ref.loc13_37, %.Self.as_type.loc13_37 [symbolic_self = constants.%.Self.binding.as_type] // CHECK:STDOUT: %Y.ref.loc13_37: %M.assoc_type = name_ref Y, @Y.%assoc1 [concrete = constants.%assoc1] // CHECK:STDOUT: %impl.elem1.loc13_37: type = impl_witness_access constants.%M.lookup_impl_witness, element1 [symbolic_self = constants.%impl.elem1] // CHECK:STDOUT: %.Self.ref.loc13_42: %M.type = name_ref .Self, %.Self.2 [symbolic_self = constants.%.Self.b4d] // CHECK:STDOUT: %.Self.as_type.loc13_42: type = facet_access_type %.Self.ref.loc13_42 [symbolic_self = constants.%.Self.binding.as_type] // CHECK:STDOUT: %.loc13_42: type = converted %.Self.ref.loc13_42, %.Self.as_type.loc13_42 [symbolic_self = constants.%.Self.binding.as_type] // CHECK:STDOUT: %X.ref.loc13_42: %M.assoc_type = name_ref X, @X.%assoc0 [concrete = constants.%assoc0] // CHECK:STDOUT: %impl.elem0.loc13_42: type = impl_witness_access constants.%M.lookup_impl_witness, element0 [symbolic_self = constants.%impl.elem0] // CHECK:STDOUT: %impl.elem0.subst: type = impl_witness_access_substituted %impl.elem0.loc13_42, %impl.elem1.loc13_30 [symbolic_self = constants.%impl.elem1] // CHECK:STDOUT: %.Self.ref.loc13_49: %M.type = name_ref .Self, %.Self.2 [symbolic_self = constants.%.Self.b4d] // CHECK:STDOUT: %.Self.as_type.loc13_49: type = facet_access_type %.Self.ref.loc13_49 [symbolic_self = constants.%.Self.binding.as_type] // CHECK:STDOUT: %.loc13_49: type = converted %.Self.ref.loc13_49, %.Self.as_type.loc13_49 [symbolic_self = constants.%.Self.binding.as_type] // CHECK:STDOUT: %Z.ref: %M.assoc_type = name_ref Z, @Z.%assoc2 [concrete = constants.%assoc2] // CHECK:STDOUT: %impl.elem2: type = impl_witness_access constants.%M.lookup_impl_witness, element2 [symbolic_self = constants.%impl.elem2] // CHECK:STDOUT: %.loc13_55.1: %empty_tuple.type = tuple_literal () [concrete = constants.%empty_tuple] // CHECK:STDOUT: %.loc13_55.2: type = converted %.loc13_55.1, constants.%empty_tuple.type [concrete = constants.%empty_tuple.type] // CHECK:STDOUT: %.loc13_19.2: type = where_expr %.Self.2 [concrete = ] { // CHECK:STDOUT: requirement_base_facet_type constants.%M.type // CHECK:STDOUT: requirement_rewrite %impl.elem0.loc13_25, %impl.elem1.loc13_30 // CHECK:STDOUT: requirement_rewrite %impl.elem1.loc13_37, %impl.elem0.subst // CHECK:STDOUT: requirement_rewrite %impl.elem2, %.loc13_55.2 // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: %T: = symbolic_binding T, 0 [concrete = ] // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic fn @F(%T: ) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: // CHECK:STDOUT: fn() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @F() {} // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/facet/fail_convert_class_type_to_generic_facet_value.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // TODO: Add ranges and switch to "--dump-sem-ir-ranges=only". // EXTRA-ARGS: --dump-sem-ir-ranges=if-present // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/convert.carbon // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/facet/fail_convert_class_type_to_generic_facet_value.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/facet/fail_convert_class_type_to_generic_facet_value.carbon interface Generic(Scalar:! type) { fn F(); } class GenericParam {} class WrongGenericParam {} class ImplsGeneric {} impl ImplsGeneric as Generic(GenericParam) { fn F() {} } fn CallGenericMethod(T:! type, unused U:! Generic(T)) {} fn G() { // CHECK:STDERR: fail_convert_class_type_to_generic_facet_value.carbon:[[@LINE+7]]:3: error: cannot convert type `ImplsGeneric` into type implementing `Generic(WrongGenericParam)` [ConversionFailureTypeToFacet] // CHECK:STDERR: CallGenericMethod(WrongGenericParam, ImplsGeneric); // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: fail_convert_class_type_to_generic_facet_value.carbon:[[@LINE-6]]:1: note: while deducing parameters of generic declared here [DeductionGenericHere] // CHECK:STDERR: fn CallGenericMethod(T:! type, unused U:! Generic(T)) {} // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: CallGenericMethod(WrongGenericParam, ImplsGeneric); } // CHECK:STDOUT: --- fail_convert_class_type_to_generic_facet_value.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %type: type = facet_type [concrete] // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self] // CHECK:STDOUT: %pattern_type.98f: type = pattern_type type [concrete] // CHECK:STDOUT: %Scalar: type = symbolic_binding Scalar, 0 [symbolic] // CHECK:STDOUT: %Generic.type.835: type = generic_interface_type @Generic [concrete] // CHECK:STDOUT: %Generic.generic: %Generic.type.835 = struct_value () [concrete] // CHECK:STDOUT: %Generic.type.03dff7.1: type = facet_type <@Generic, @Generic(%Scalar)> [symbolic] // CHECK:STDOUT: %Self.15d: %Generic.type.03dff7.1 = symbolic_binding Self, 1 [symbolic] // CHECK:STDOUT: %Generic.WithSelf.F.type.743: type = fn_type @Generic.WithSelf.F, @Generic.WithSelf(%Scalar, %Self.15d) [symbolic] // CHECK:STDOUT: %Generic.WithSelf.F.baf: %Generic.WithSelf.F.type.743 = struct_value () [symbolic] // CHECK:STDOUT: %Generic.assoc_type.22a: type = assoc_entity_type @Generic, @Generic(%Scalar) [symbolic] // CHECK:STDOUT: %assoc0.e0d: %Generic.assoc_type.22a = assoc_entity element0, @Generic.WithSelf.%Generic.WithSelf.F.decl [symbolic] // CHECK:STDOUT: %GenericParam: type = class_type @GenericParam [concrete] // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %complete_type: = complete_type_witness %empty_struct_type [concrete] // CHECK:STDOUT: %WrongGenericParam: type = class_type @WrongGenericParam [concrete] // CHECK:STDOUT: %ImplsGeneric: type = class_type @ImplsGeneric [concrete] // CHECK:STDOUT: %Generic.type.498: type = facet_type <@Generic, @Generic(%GenericParam)> [concrete] // CHECK:STDOUT: %Generic.impl_witness: = impl_witness @ImplsGeneric.as.Generic.impl.%Generic.impl_witness_table [concrete] // CHECK:STDOUT: %Self.1f5: %Generic.type.498 = symbolic_binding Self, 1 [symbolic] // CHECK:STDOUT: %Generic.WithSelf.F.type.a23: type = fn_type @Generic.WithSelf.F, @Generic.WithSelf(%GenericParam, %Self.15d) [symbolic] // CHECK:STDOUT: %Generic.WithSelf.F.fe0: %Generic.WithSelf.F.type.a23 = struct_value () [symbolic] // CHECK:STDOUT: %Generic.assoc_type.6dc: type = assoc_entity_type @Generic, @Generic(%GenericParam) [concrete] // CHECK:STDOUT: %assoc0.717: %Generic.assoc_type.6dc = assoc_entity element0, @Generic.WithSelf.%Generic.WithSelf.F.decl [concrete] // CHECK:STDOUT: %ImplsGeneric.as.Generic.impl.F.type: type = fn_type @ImplsGeneric.as.Generic.impl.F [concrete] // CHECK:STDOUT: %ImplsGeneric.as.Generic.impl.F: %ImplsGeneric.as.Generic.impl.F.type = struct_value () [concrete] // CHECK:STDOUT: %Generic.facet: %Generic.type.498 = facet_value %ImplsGeneric, (%Generic.impl_witness) [concrete] // CHECK:STDOUT: %Generic.WithSelf.F.type.c86: type = fn_type @Generic.WithSelf.F, @Generic.WithSelf(%GenericParam, %Generic.facet) [concrete] // CHECK:STDOUT: %Generic.WithSelf.F.1fd: %Generic.WithSelf.F.type.c86 = struct_value () [concrete] // CHECK:STDOUT: %T: type = symbolic_binding T, 0 [symbolic] // CHECK:STDOUT: %Generic.type.03dff7.2: type = facet_type <@Generic, @Generic(%T)> [symbolic] // CHECK:STDOUT: %pattern_type.c49: type = pattern_type %Generic.type.03dff7.2 [symbolic] // CHECK:STDOUT: %U: %Generic.type.03dff7.2 = symbolic_binding U, 1 [symbolic] // CHECK:STDOUT: %CallGenericMethod.type: type = fn_type @CallGenericMethod [concrete] // CHECK:STDOUT: %CallGenericMethod: %CallGenericMethod.type = struct_value () [concrete] // CHECK:STDOUT: %G.type: type = fn_type @G [concrete] // CHECK:STDOUT: %G: %G.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .Generic = %Generic.decl // CHECK:STDOUT: .GenericParam = %GenericParam.decl // CHECK:STDOUT: .WrongGenericParam = %WrongGenericParam.decl // CHECK:STDOUT: .ImplsGeneric = %ImplsGeneric.decl // CHECK:STDOUT: .CallGenericMethod = %CallGenericMethod.decl // CHECK:STDOUT: .G = %G.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %Generic.decl: %Generic.type.835 = interface_decl @Generic [concrete = constants.%Generic.generic] { // CHECK:STDOUT: %Scalar.patt: %pattern_type.98f = symbolic_binding_pattern Scalar, 0 [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %.loc15_28.1: type = splice_block %.loc15_28.2 [concrete = type] { // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %.loc15_28.2: type = type_literal type [concrete = type] // CHECK:STDOUT: } // CHECK:STDOUT: %Scalar.loc15_19.2: type = symbolic_binding Scalar, 0 [symbolic = %Scalar.loc15_19.1 (constants.%Scalar)] // CHECK:STDOUT: } // CHECK:STDOUT: %GenericParam.decl: type = class_decl @GenericParam [concrete = constants.%GenericParam] {} {} // CHECK:STDOUT: %WrongGenericParam.decl: type = class_decl @WrongGenericParam [concrete = constants.%WrongGenericParam] {} {} // CHECK:STDOUT: %ImplsGeneric.decl: type = class_decl @ImplsGeneric [concrete = constants.%ImplsGeneric] {} {} // CHECK:STDOUT: impl_decl @ImplsGeneric.as.Generic.impl [concrete] {} { // CHECK:STDOUT: %ImplsGeneric.ref: type = name_ref ImplsGeneric, file.%ImplsGeneric.decl [concrete = constants.%ImplsGeneric] // CHECK:STDOUT: %Generic.ref: %Generic.type.835 = name_ref Generic, file.%Generic.decl [concrete = constants.%Generic.generic] // CHECK:STDOUT: %GenericParam.ref: type = name_ref GenericParam, file.%GenericParam.decl [concrete = constants.%GenericParam] // CHECK:STDOUT: %Generic.type: type = facet_type <@Generic, @Generic(constants.%GenericParam)> [concrete = constants.%Generic.type.498] // CHECK:STDOUT: } // CHECK:STDOUT: %CallGenericMethod.decl: %CallGenericMethod.type = fn_decl @CallGenericMethod [concrete = constants.%CallGenericMethod] { // CHECK:STDOUT: %T.patt: %pattern_type.98f = symbolic_binding_pattern T, 0 [concrete] // CHECK:STDOUT: %U.patt: @CallGenericMethod.%pattern_type (%pattern_type.c49) = symbolic_binding_pattern U, 1 [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %.loc27_26.1: type = splice_block %.loc27_26.2 [concrete = type] { // CHECK:STDOUT: %.Self.2: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %.loc27_26.2: type = type_literal type [concrete = type] // CHECK:STDOUT: } // CHECK:STDOUT: %T.loc27_22.2: type = symbolic_binding T, 0 [symbolic = %T.loc27_22.1 (constants.%T)] // CHECK:STDOUT: %.loc27_52: type = splice_block %Generic.type.loc27_52.2 [symbolic = %Generic.type.loc27_52.1 (constants.%Generic.type.03dff7.2)] { // CHECK:STDOUT: %.Self.1: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %Generic.ref: %Generic.type.835 = name_ref Generic, file.%Generic.decl [concrete = constants.%Generic.generic] // CHECK:STDOUT: %T.ref: type = name_ref T, %T.loc27_22.2 [symbolic = %T.loc27_22.1 (constants.%T)] // CHECK:STDOUT: %Generic.type.loc27_52.2: type = facet_type <@Generic, @Generic(constants.%T)> [symbolic = %Generic.type.loc27_52.1 (constants.%Generic.type.03dff7.2)] // CHECK:STDOUT: } // CHECK:STDOUT: %U.loc27_39.2: @CallGenericMethod.%Generic.type.loc27_52.1 (%Generic.type.03dff7.2) = symbolic_binding U, 1 [symbolic = %U.loc27_39.1 (constants.%U)] // CHECK:STDOUT: } // CHECK:STDOUT: %G.decl: %G.type = fn_decl @G [concrete = constants.%G] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic interface @Generic(%Scalar.loc15_19.2: type) { // CHECK:STDOUT: %Scalar.loc15_19.1: type = symbolic_binding Scalar, 0 [symbolic = %Scalar.loc15_19.1 (constants.%Scalar)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %Generic.type: type = facet_type <@Generic, @Generic(%Scalar.loc15_19.1)> [symbolic = %Generic.type (constants.%Generic.type.03dff7.1)] // CHECK:STDOUT: %Self.loc15_34.2: @Generic.%Generic.type (%Generic.type.03dff7.1) = symbolic_binding Self, 1 [symbolic = %Self.loc15_34.2 (constants.%Self.15d)] // CHECK:STDOUT: // CHECK:STDOUT: interface { // CHECK:STDOUT: %Self.loc15_34.1: @Generic.%Generic.type (%Generic.type.03dff7.1) = symbolic_binding Self, 1 [symbolic = %Self.loc15_34.2 (constants.%Self.15d)] // CHECK:STDOUT: %Generic.WithSelf.decl = interface_with_self_decl @Generic [concrete] // CHECK:STDOUT: // CHECK:STDOUT: !with Self: // CHECK:STDOUT: %Generic.WithSelf.F.decl: @Generic.WithSelf.%Generic.WithSelf.F.type (%Generic.WithSelf.F.type.743) = fn_decl @Generic.WithSelf.F [symbolic = @Generic.WithSelf.%Generic.WithSelf.F (constants.%Generic.WithSelf.F.baf)] {} {} // CHECK:STDOUT: %assoc0.loc16_9.1: @Generic.WithSelf.%Generic.assoc_type (%Generic.assoc_type.22a) = assoc_entity element0, %Generic.WithSelf.F.decl [symbolic = %assoc0.loc16_9.2 (constants.%assoc0.e0d)] // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = %Self.loc15_34.1 // CHECK:STDOUT: .F = @Generic.WithSelf.%assoc0.loc16_9.1 // CHECK:STDOUT: witness = (@Generic.WithSelf.%Generic.WithSelf.F.decl) // CHECK:STDOUT: // CHECK:STDOUT: !requires: // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: impl @ImplsGeneric.as.Generic.impl: %ImplsGeneric.ref as %Generic.type { // CHECK:STDOUT: %ImplsGeneric.as.Generic.impl.F.decl: %ImplsGeneric.as.Generic.impl.F.type = fn_decl @ImplsGeneric.as.Generic.impl.F [concrete = constants.%ImplsGeneric.as.Generic.impl.F] {} {} // CHECK:STDOUT: %Generic.impl_witness_table = impl_witness_table (%ImplsGeneric.as.Generic.impl.F.decl), @ImplsGeneric.as.Generic.impl [concrete] // CHECK:STDOUT: %Generic.impl_witness: = impl_witness %Generic.impl_witness_table [concrete = constants.%Generic.impl_witness] // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .F = %ImplsGeneric.as.Generic.impl.F.decl // CHECK:STDOUT: witness = %Generic.impl_witness // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @GenericParam { // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%GenericParam // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @WrongGenericParam { // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%WrongGenericParam // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @ImplsGeneric { // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%ImplsGeneric // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic fn @Generic.WithSelf.F(@Generic.%Scalar.loc15_19.2: type, @Generic.%Self.loc15_34.1: @Generic.%Generic.type (%Generic.type.03dff7.1)) { // CHECK:STDOUT: fn(); // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @ImplsGeneric.as.Generic.impl.F() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic fn @CallGenericMethod(%T.loc27_22.2: type, %U.loc27_39.2: @CallGenericMethod.%Generic.type.loc27_52.1 (%Generic.type.03dff7.2)) { // CHECK:STDOUT: %T.loc27_22.1: type = symbolic_binding T, 0 [symbolic = %T.loc27_22.1 (constants.%T)] // CHECK:STDOUT: %Generic.type.loc27_52.1: type = facet_type <@Generic, @Generic(%T.loc27_22.1)> [symbolic = %Generic.type.loc27_52.1 (constants.%Generic.type.03dff7.2)] // CHECK:STDOUT: %U.loc27_39.1: @CallGenericMethod.%Generic.type.loc27_52.1 (%Generic.type.03dff7.2) = symbolic_binding U, 1 [symbolic = %U.loc27_39.1 (constants.%U)] // CHECK:STDOUT: %pattern_type: type = pattern_type %Generic.type.loc27_52.1 [symbolic = %pattern_type (constants.%pattern_type.c49)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: // CHECK:STDOUT: fn() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @G() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %CallGenericMethod.ref: %CallGenericMethod.type = name_ref CallGenericMethod, file.%CallGenericMethod.decl [concrete = constants.%CallGenericMethod] // CHECK:STDOUT: %WrongGenericParam.ref: type = name_ref WrongGenericParam, file.%WrongGenericParam.decl [concrete = constants.%WrongGenericParam] // CHECK:STDOUT: %ImplsGeneric.ref: type = name_ref ImplsGeneric, file.%ImplsGeneric.decl [concrete = constants.%ImplsGeneric] // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @Generic(constants.%Scalar) { // CHECK:STDOUT: %Scalar.loc15_19.1 => constants.%Scalar // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @Generic.WithSelf(constants.%Scalar, constants.%Self.15d) {} // CHECK:STDOUT: // CHECK:STDOUT: specific @Generic.WithSelf.F(constants.%Scalar, constants.%Self.15d) {} // CHECK:STDOUT: // CHECK:STDOUT: specific @Generic(constants.%GenericParam) { // CHECK:STDOUT: %Scalar.loc15_19.1 => constants.%GenericParam // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %Generic.type => constants.%Generic.type.498 // CHECK:STDOUT: %Self.loc15_34.2 => constants.%Self.1f5 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @Generic.WithSelf(constants.%GenericParam, constants.%Self.15d) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: %Scalar => constants.%GenericParam // CHECK:STDOUT: %Generic.type => constants.%Generic.type.498 // CHECK:STDOUT: %Self => constants.%Self.15d // CHECK:STDOUT: %Generic.WithSelf.F.type => constants.%Generic.WithSelf.F.type.a23 // CHECK:STDOUT: %Generic.WithSelf.F => constants.%Generic.WithSelf.F.fe0 // CHECK:STDOUT: %Generic.assoc_type => constants.%Generic.assoc_type.6dc // CHECK:STDOUT: %assoc0.loc16_9.2 => constants.%assoc0.717 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @Generic.WithSelf(constants.%GenericParam, constants.%Generic.facet) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: %Scalar => constants.%GenericParam // CHECK:STDOUT: %Generic.type => constants.%Generic.type.498 // CHECK:STDOUT: %Self => constants.%Generic.facet // CHECK:STDOUT: %Generic.WithSelf.F.type => constants.%Generic.WithSelf.F.type.c86 // CHECK:STDOUT: %Generic.WithSelf.F => constants.%Generic.WithSelf.F.1fd // CHECK:STDOUT: %Generic.assoc_type => constants.%Generic.assoc_type.6dc // CHECK:STDOUT: %assoc0.loc16_9.2 => constants.%assoc0.717 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @Generic.WithSelf.F(constants.%GenericParam, constants.%Generic.facet) {} // CHECK:STDOUT: // CHECK:STDOUT: specific @Generic(constants.%T) { // CHECK:STDOUT: %Scalar.loc15_19.1 => constants.%T // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @CallGenericMethod(constants.%T, constants.%U) { // CHECK:STDOUT: %T.loc27_22.1 => constants.%T // CHECK:STDOUT: %Generic.type.loc27_52.1 => constants.%Generic.type.03dff7.2 // CHECK:STDOUT: %U.loc27_39.1 => constants.%U // CHECK:STDOUT: %pattern_type => constants.%pattern_type.c49 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @Generic(constants.%WrongGenericParam) { // CHECK:STDOUT: %Scalar.loc15_19.1 => constants.%WrongGenericParam // CHECK:STDOUT: } // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/facet/fail_convert_facet_value_to_missing_impl.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // TODO: Add ranges and switch to "--dump-sem-ir-ranges=only". // EXTRA-ARGS: --dump-sem-ir-ranges=if-present // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/convert.carbon // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/facet/fail_convert_facet_value_to_missing_impl.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/facet/fail_convert_facet_value_to_missing_impl.carbon interface Eats {} interface Animal {} fn Feed[T:! Eats](unused e: T) {} // CHECK:STDERR: fail_convert_facet_value_to_missing_impl.carbon:[[@LINE+7]]:37: error: cannot convert type `T` that implements `Animal` into type implementing `Eats` [ConversionFailureFacetToFacet] // CHECK:STDERR: fn HandleAnimal[T:! Animal](a: T) { Feed(a); } // CHECK:STDERR: ^~~~~~~ // CHECK:STDERR: fail_convert_facet_value_to_missing_impl.carbon:[[@LINE-5]]:1: note: while deducing parameters of generic declared here [DeductionGenericHere] // CHECK:STDERR: fn Feed[T:! Eats](unused e: T) {} // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: fn HandleAnimal[T:! Animal](a: T) { Feed(a); } // CHECK:STDOUT: --- fail_convert_facet_value_to_missing_impl.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %Eats.type: type = facet_type <@Eats> [concrete] // CHECK:STDOUT: %Self.247: %Eats.type = symbolic_binding Self, 0 [symbolic] // CHECK:STDOUT: %Animal.type: type = facet_type <@Animal> [concrete] // CHECK:STDOUT: %Self.c49: %Animal.type = symbolic_binding Self, 0 [symbolic] // CHECK:STDOUT: %type: type = facet_type [concrete] // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self] // CHECK:STDOUT: %pattern_type.0dc: type = pattern_type %Eats.type [concrete] // CHECK:STDOUT: %T.f11: %Eats.type = symbolic_binding T, 0 [symbolic] // CHECK:STDOUT: %T.binding.as_type.265: type = symbolic_binding_type T, 0, %T.f11 [symbolic] // CHECK:STDOUT: %pattern_type.93e: type = pattern_type %T.binding.as_type.265 [symbolic] // CHECK:STDOUT: %Feed.type: type = fn_type @Feed [concrete] // CHECK:STDOUT: %Feed: %Feed.type = struct_value () [concrete] // CHECK:STDOUT: %require_complete.9d9: = require_complete_type %T.binding.as_type.265 [symbolic] // CHECK:STDOUT: %pattern_type.e10: type = pattern_type %Animal.type [concrete] // CHECK:STDOUT: %T.998: %Animal.type = symbolic_binding T, 0 [symbolic] // CHECK:STDOUT: %T.binding.as_type.e4f: type = symbolic_binding_type T, 0, %T.998 [symbolic] // CHECK:STDOUT: %pattern_type.892: type = pattern_type %T.binding.as_type.e4f [symbolic] // CHECK:STDOUT: %HandleAnimal.type: type = fn_type @HandleAnimal [concrete] // CHECK:STDOUT: %HandleAnimal: %HandleAnimal.type = struct_value () [concrete] // CHECK:STDOUT: %require_complete.72f: = require_complete_type %T.binding.as_type.e4f [symbolic] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .Eats = %Eats.decl // CHECK:STDOUT: .Animal = %Animal.decl // CHECK:STDOUT: .Feed = %Feed.decl // CHECK:STDOUT: .HandleAnimal = %HandleAnimal.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %Eats.decl: type = interface_decl @Eats [concrete = constants.%Eats.type] {} {} // CHECK:STDOUT: %Animal.decl: type = interface_decl @Animal [concrete = constants.%Animal.type] {} {} // CHECK:STDOUT: %Feed.decl: %Feed.type = fn_decl @Feed [concrete = constants.%Feed] { // CHECK:STDOUT: %T.patt: %pattern_type.0dc = symbolic_binding_pattern T, 0 [concrete] // CHECK:STDOUT: %e.patt: @Feed.%pattern_type (%pattern_type.93e) = value_binding_pattern e [concrete] // CHECK:STDOUT: %e.param_patt: @Feed.%pattern_type (%pattern_type.93e) = value_param_pattern %e.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %.loc18_13: type = splice_block %Eats.ref [concrete = constants.%Eats.type] { // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %Eats.ref: type = name_ref Eats, file.%Eats.decl [concrete = constants.%Eats.type] // CHECK:STDOUT: } // CHECK:STDOUT: %T.loc18_9.2: %Eats.type = symbolic_binding T, 0 [symbolic = %T.loc18_9.1 (constants.%T.f11)] // CHECK:STDOUT: %e.param: @Feed.%T.binding.as_type (%T.binding.as_type.265) = value_param call_param0 // CHECK:STDOUT: %.loc18_29.1: type = splice_block %.loc18_29.2 [symbolic = %T.binding.as_type (constants.%T.binding.as_type.265)] { // CHECK:STDOUT: %T.ref: %Eats.type = name_ref T, %T.loc18_9.2 [symbolic = %T.loc18_9.1 (constants.%T.f11)] // CHECK:STDOUT: %T.as_type: type = facet_access_type %T.ref [symbolic = %T.binding.as_type (constants.%T.binding.as_type.265)] // CHECK:STDOUT: %.loc18_29.2: type = converted %T.ref, %T.as_type [symbolic = %T.binding.as_type (constants.%T.binding.as_type.265)] // CHECK:STDOUT: } // CHECK:STDOUT: %e: @Feed.%T.binding.as_type (%T.binding.as_type.265) = value_binding e, %e.param // CHECK:STDOUT: } // CHECK:STDOUT: %HandleAnimal.decl: %HandleAnimal.type = fn_decl @HandleAnimal [concrete = constants.%HandleAnimal] { // CHECK:STDOUT: %T.patt: %pattern_type.e10 = symbolic_binding_pattern T, 0 [concrete] // CHECK:STDOUT: %a.patt: @HandleAnimal.%pattern_type (%pattern_type.892) = value_binding_pattern a [concrete] // CHECK:STDOUT: %a.param_patt: @HandleAnimal.%pattern_type (%pattern_type.892) = value_param_pattern %a.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %.loc27_21: type = splice_block %Animal.ref [concrete = constants.%Animal.type] { // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %Animal.ref: type = name_ref Animal, file.%Animal.decl [concrete = constants.%Animal.type] // CHECK:STDOUT: } // CHECK:STDOUT: %T.loc27_17.2: %Animal.type = symbolic_binding T, 0 [symbolic = %T.loc27_17.1 (constants.%T.998)] // CHECK:STDOUT: %a.param: @HandleAnimal.%T.binding.as_type (%T.binding.as_type.e4f) = value_param call_param0 // CHECK:STDOUT: %.loc27_32.1: type = splice_block %.loc27_32.2 [symbolic = %T.binding.as_type (constants.%T.binding.as_type.e4f)] { // CHECK:STDOUT: %T.ref: %Animal.type = name_ref T, %T.loc27_17.2 [symbolic = %T.loc27_17.1 (constants.%T.998)] // CHECK:STDOUT: %T.as_type: type = facet_access_type %T.ref [symbolic = %T.binding.as_type (constants.%T.binding.as_type.e4f)] // CHECK:STDOUT: %.loc27_32.2: type = converted %T.ref, %T.as_type [symbolic = %T.binding.as_type (constants.%T.binding.as_type.e4f)] // CHECK:STDOUT: } // CHECK:STDOUT: %a: @HandleAnimal.%T.binding.as_type (%T.binding.as_type.e4f) = value_binding a, %a.param // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: interface @Eats { // CHECK:STDOUT: %Self: %Eats.type = symbolic_binding Self, 0 [symbolic = constants.%Self.247] // CHECK:STDOUT: %Eats.WithSelf.decl = interface_with_self_decl @Eats [concrete] // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = %Self // CHECK:STDOUT: witness = () // CHECK:STDOUT: // CHECK:STDOUT: !requires: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: interface @Animal { // CHECK:STDOUT: %Self: %Animal.type = symbolic_binding Self, 0 [symbolic = constants.%Self.c49] // CHECK:STDOUT: %Animal.WithSelf.decl = interface_with_self_decl @Animal [concrete] // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = %Self // CHECK:STDOUT: witness = () // CHECK:STDOUT: // CHECK:STDOUT: !requires: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic fn @Feed(%T.loc18_9.2: %Eats.type) { // CHECK:STDOUT: %T.loc18_9.1: %Eats.type = symbolic_binding T, 0 [symbolic = %T.loc18_9.1 (constants.%T.f11)] // CHECK:STDOUT: %T.binding.as_type: type = symbolic_binding_type T, 0, %T.loc18_9.1 [symbolic = %T.binding.as_type (constants.%T.binding.as_type.265)] // CHECK:STDOUT: %pattern_type: type = pattern_type %T.binding.as_type [symbolic = %pattern_type (constants.%pattern_type.93e)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete: = require_complete_type %T.binding.as_type [symbolic = %require_complete (constants.%require_complete.9d9)] // CHECK:STDOUT: // CHECK:STDOUT: fn(%e.param: @Feed.%T.binding.as_type (%T.binding.as_type.265)) { // CHECK:STDOUT: !entry: // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic fn @HandleAnimal(%T.loc27_17.2: %Animal.type) { // CHECK:STDOUT: %T.loc27_17.1: %Animal.type = symbolic_binding T, 0 [symbolic = %T.loc27_17.1 (constants.%T.998)] // CHECK:STDOUT: %T.binding.as_type: type = symbolic_binding_type T, 0, %T.loc27_17.1 [symbolic = %T.binding.as_type (constants.%T.binding.as_type.e4f)] // CHECK:STDOUT: %pattern_type: type = pattern_type %T.binding.as_type [symbolic = %pattern_type (constants.%pattern_type.892)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete: = require_complete_type %T.binding.as_type [symbolic = %require_complete (constants.%require_complete.72f)] // CHECK:STDOUT: // CHECK:STDOUT: fn(%a.param: @HandleAnimal.%T.binding.as_type (%T.binding.as_type.e4f)) { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %Feed.ref: %Feed.type = name_ref Feed, file.%Feed.decl [concrete = constants.%Feed] // CHECK:STDOUT: %a.ref: @HandleAnimal.%T.binding.as_type (%T.binding.as_type.e4f) = name_ref a, %a // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @Eats.WithSelf(constants.%Self.247) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @Animal.WithSelf(constants.%Self.c49) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @Feed(constants.%T.f11) { // CHECK:STDOUT: %T.loc18_9.1 => constants.%T.f11 // CHECK:STDOUT: %T.binding.as_type => constants.%T.binding.as_type.265 // CHECK:STDOUT: %pattern_type => constants.%pattern_type.93e // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @HandleAnimal(constants.%T.998) { // CHECK:STDOUT: %T.loc27_17.1 => constants.%T.998 // CHECK:STDOUT: %T.binding.as_type => constants.%T.binding.as_type.e4f // CHECK:STDOUT: %pattern_type => constants.%pattern_type.892 // CHECK:STDOUT: } // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/facet/fail_convert_type_erased_type_to_facet.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // TODO: Add ranges and switch to "--dump-sem-ir-ranges=only". // EXTRA-ARGS: --dump-sem-ir-ranges=if-present // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/convert.carbon // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/facet/fail_convert_type_erased_type_to_facet.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/facet/fail_convert_type_erased_type_to_facet.carbon interface Animal {} class Goat {} impl Goat as Animal {} fn WalkAnimal(unused a:! Animal) {} fn F() { // CHECK:STDERR: fail_convert_type_erased_type_to_facet.carbon:[[@LINE+4]]:7: error: semantics TODO: `local `let :!` bindings are currently unsupported` [SemanticsTodo] // CHECK:STDERR: let x:! type = Goat; // CHECK:STDERR: ^~~~~~~~ // CHECK:STDERR: let x:! type = Goat; WalkAnimal(x); } // CHECK:STDOUT: --- fail_convert_type_erased_type_to_facet.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %Animal.type: type = facet_type <@Animal> [concrete] // CHECK:STDOUT: %Self: %Animal.type = symbolic_binding Self, 0 [symbolic] // CHECK:STDOUT: %Goat: type = class_type @Goat [concrete] // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %complete_type: = complete_type_witness %empty_struct_type [concrete] // CHECK:STDOUT: %Animal.impl_witness: = impl_witness .inst{{[0-9A-F]+}}.loc18_21 [concrete] // CHECK:STDOUT: %Animal.facet: %Animal.type = facet_value %Goat, (%Animal.impl_witness) [concrete] // CHECK:STDOUT: %a: %Animal.type = symbolic_binding a, 0 [symbolic] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: interface @Animal { // CHECK:STDOUT: %Self: %Animal.type = symbolic_binding Self, 0 [symbolic = constants.%Self] // CHECK:STDOUT: %Animal.WithSelf.decl = interface_with_self_decl @Animal [concrete] // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = %Self // CHECK:STDOUT: witness = () // CHECK:STDOUT: // CHECK:STDOUT: !requires: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: impl @: .inst{{[0-9A-F]+}}.loc18_6 as .inst{{[0-9A-F]+}}.loc18_14 { // CHECK:STDOUT: impl_witness_table (), @ [concrete] // CHECK:STDOUT: .inst{{[0-9A-F]+}}.loc18_21: = impl_witness .inst{{[0-9A-F]+}}.loc18_21 [concrete = constants.%Animal.impl_witness] // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: witness = .inst{{[0-9A-F]+}}.loc18_21 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @Goat { // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%Goat // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic fn @WalkAnimal(.inst{{[0-9A-F]+}}.loc20_22: %Animal.type) { // CHECK:STDOUT: %a: %Animal.type = symbolic_binding a, 0 [symbolic = %a (constants.%a)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: // CHECK:STDOUT: fn() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @F(); // CHECK:STDOUT: // CHECK:STDOUT: specific @Animal.WithSelf(constants.%Self) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @Animal.WithSelf(constants.%Animal.facet) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @WalkAnimal(constants.%a) { // CHECK:STDOUT: %a => constants.%a // CHECK:STDOUT: } // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/facet/fail_deduction_uses_runtime_type_conversion.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // TODO: Add ranges and switch to "--dump-sem-ir-ranges=only". // EXTRA-ARGS: --dump-sem-ir-ranges=if-present // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/convert.carbon // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/facet/fail_deduction_uses_runtime_type_conversion.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/facet/fail_deduction_uses_runtime_type_conversion.carbon // Uses a tuple to allow using the inner type as a type that isn't directly // deducible, without deducing through HoldsType. class HoldsType(T:! (type, )) {} class RuntimeConvertFrom {} class RuntimeConvertTo {} impl RuntimeConvertFrom as Core.ImplicitAs(RuntimeConvertTo) { // Runtime conversion function fn Convert[unused self: Self]() -> RuntimeConvertTo { return {}; } } fn F[T:! (type, )](unused A:! T.0, unused x: HoldsType(T)) {} fn G(holds_to: HoldsType((RuntimeConvertTo, )), from:! RuntimeConvertFrom) { // CHECK:STDERR: fail_deduction_uses_runtime_type_conversion.carbon:[[@LINE+10]]:3: error: compile-time value requires runtime conversion, constructing value of type `RuntimeConvertTo` [RuntimeConversionDuringCompTimeDeduction] // CHECK:STDERR: F(from, holds_to); // CHECK:STDERR: ^~~~~~~~~~~~~~~~~ // CHECK:STDERR: fail_deduction_uses_runtime_type_conversion.carbon:[[@LINE-6]]:1: note: while deducing parameters of generic declared here [DeductionGenericHere] // CHECK:STDERR: fn F[T:! (type, )](unused A:! T.0, unused x: HoldsType(T)) {} // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: fail_deduction_uses_runtime_type_conversion.carbon:[[@LINE-9]]:1: note: while deducing parameters of generic declared here [DeductionGenericHere] // CHECK:STDERR: fn F[T:! (type, )](unused A:! T.0, unused x: HoldsType(T)) {} // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: F(from, holds_to); } // CHECK:STDOUT: --- fail_deduction_uses_runtime_type_conversion.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %type: type = facet_type [concrete] // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self] // CHECK:STDOUT: %tuple.type: type = tuple_type (type) [concrete] // CHECK:STDOUT: %tuple.0d2: %tuple.type = tuple_value (type) [concrete] // CHECK:STDOUT: %pattern_type.f1e: type = pattern_type %tuple.type [concrete] // CHECK:STDOUT: %T: %tuple.type = symbolic_binding T, 0 [symbolic] // CHECK:STDOUT: %HoldsType.type: type = generic_class_type @HoldsType [concrete] // CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] // CHECK:STDOUT: %HoldsType.generic: %HoldsType.type = struct_value () [concrete] // CHECK:STDOUT: %HoldsType.f6a: type = class_type @HoldsType, @HoldsType(%T) [symbolic] // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %complete_type: = complete_type_witness %empty_struct_type [concrete] // CHECK:STDOUT: %RuntimeConvertFrom: type = class_type @RuntimeConvertFrom [concrete] // CHECK:STDOUT: %RuntimeConvertTo: type = class_type @RuntimeConvertTo [concrete] // CHECK:STDOUT: %ImplicitAs.type.cc7: type = generic_interface_type @ImplicitAs [concrete] // CHECK:STDOUT: %ImplicitAs.generic: %ImplicitAs.type.cc7 = struct_value () [concrete] // CHECK:STDOUT: %ImplicitAs.type.5cd: type = facet_type <@ImplicitAs, @ImplicitAs(%RuntimeConvertTo)> [concrete] // CHECK:STDOUT: %ImplicitAs.impl_witness: = impl_witness @RuntimeConvertFrom.as.ImplicitAs.impl.%ImplicitAs.impl_witness_table [concrete] // CHECK:STDOUT: %pattern_type.6bd: type = pattern_type %RuntimeConvertFrom [concrete] // CHECK:STDOUT: %.993: Core.Form = init_form %RuntimeConvertTo [concrete] // CHECK:STDOUT: %pattern_type.c89: type = pattern_type %RuntimeConvertTo [concrete] // CHECK:STDOUT: %RuntimeConvertFrom.as.ImplicitAs.impl.Convert.type: type = fn_type @RuntimeConvertFrom.as.ImplicitAs.impl.Convert [concrete] // CHECK:STDOUT: %RuntimeConvertFrom.as.ImplicitAs.impl.Convert: %RuntimeConvertFrom.as.ImplicitAs.impl.Convert.type = struct_value () [concrete] // CHECK:STDOUT: %ImplicitAs.facet: %ImplicitAs.type.5cd = facet_value %RuntimeConvertFrom, (%ImplicitAs.impl_witness) [concrete] // CHECK:STDOUT: %ImplicitAs.WithSelf.Convert.type.3a2: type = fn_type @ImplicitAs.WithSelf.Convert, @ImplicitAs.WithSelf(%RuntimeConvertTo, %ImplicitAs.facet) [concrete] // CHECK:STDOUT: %empty_struct: %empty_struct_type = struct_value () [concrete] // CHECK:STDOUT: %RuntimeConvertTo.val: %RuntimeConvertTo = struct_value () [concrete] // CHECK:STDOUT: %int_0: Core.IntLiteral = int_value 0 [concrete] // CHECK:STDOUT: %tuple.elem0: type = tuple_access %T, element0 [symbolic] // CHECK:STDOUT: %pattern_type.e66: type = pattern_type %tuple.elem0 [symbolic] // CHECK:STDOUT: %A: %tuple.elem0 = symbolic_binding A, 1 [symbolic] // CHECK:STDOUT: %pattern_type.17d: type = pattern_type %HoldsType.f6a [symbolic] // CHECK:STDOUT: %F.type: type = fn_type @F [concrete] // CHECK:STDOUT: %F: %F.type = struct_value () [concrete] // CHECK:STDOUT: %require_complete: = require_complete_type %HoldsType.f6a [symbolic] // CHECK:STDOUT: %tuple.b95: %tuple.type = tuple_value (%RuntimeConvertTo) [concrete] // CHECK:STDOUT: %HoldsType.0ca: type = class_type @HoldsType, @HoldsType(%tuple.b95) [concrete] // CHECK:STDOUT: %pattern_type.c6f: type = pattern_type %HoldsType.0ca [concrete] // CHECK:STDOUT: %from: %RuntimeConvertFrom = symbolic_binding from, 0 [symbolic] // CHECK:STDOUT: %G.type: type = fn_type @G [concrete] // CHECK:STDOUT: %G: %G.type = struct_value () [concrete] // CHECK:STDOUT: %.8ab: type = fn_type_with_self_type %ImplicitAs.WithSelf.Convert.type.3a2, %ImplicitAs.facet [concrete] // CHECK:STDOUT: %RuntimeConvertFrom.as.ImplicitAs.impl.Convert.bound: = bound_method %from, %RuntimeConvertFrom.as.ImplicitAs.impl.Convert [symbolic] // CHECK:STDOUT: %empty_tuple: %empty_tuple.type = tuple_value () [concrete] // CHECK:STDOUT: %Destroy.type: type = facet_type <@Destroy> [concrete] // CHECK:STDOUT: %Destroy.Op.type: type = fn_type @Destroy.Op [concrete] // CHECK:STDOUT: %Destroy.Op: %Destroy.Op.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: .ImplicitAs = %Core.ImplicitAs // CHECK:STDOUT: .Destroy = %Core.Destroy // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: %Core.ImplicitAs: %ImplicitAs.type.cc7 = import_ref Core//prelude/parts/as, ImplicitAs, loaded [concrete = constants.%ImplicitAs.generic] // CHECK:STDOUT: %Core.Destroy: type = import_ref Core//prelude/parts/destroy, Destroy, loaded [concrete = constants.%Destroy.type] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .HoldsType = %HoldsType.decl // CHECK:STDOUT: .RuntimeConvertFrom = %RuntimeConvertFrom.decl // CHECK:STDOUT: .RuntimeConvertTo = %RuntimeConvertTo.decl // CHECK:STDOUT: .F = %F.decl // CHECK:STDOUT: .G = %G.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %HoldsType.decl: %HoldsType.type = class_decl @HoldsType [concrete = constants.%HoldsType.generic] { // CHECK:STDOUT: %T.patt: %pattern_type.f1e = symbolic_binding_pattern T, 0 [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %.loc17_28.1: type = splice_block %.loc17_28.3 [concrete = constants.%tuple.type] { // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %.loc17_22: type = type_literal type [concrete = type] // CHECK:STDOUT: %.loc17_28.2: %tuple.type = tuple_literal (%.loc17_22) [concrete = constants.%tuple.0d2] // CHECK:STDOUT: %.loc17_28.3: type = converted %.loc17_28.2, constants.%tuple.type [concrete = constants.%tuple.type] // CHECK:STDOUT: } // CHECK:STDOUT: %T.loc17_17.2: %tuple.type = symbolic_binding T, 0 [symbolic = %T.loc17_17.1 (constants.%T)] // CHECK:STDOUT: } // CHECK:STDOUT: %RuntimeConvertFrom.decl: type = class_decl @RuntimeConvertFrom [concrete = constants.%RuntimeConvertFrom] {} {} // CHECK:STDOUT: %RuntimeConvertTo.decl: type = class_decl @RuntimeConvertTo [concrete = constants.%RuntimeConvertTo] {} {} // CHECK:STDOUT: impl_decl @RuntimeConvertFrom.as.ImplicitAs.impl [concrete] {} { // CHECK:STDOUT: %RuntimeConvertFrom.ref: type = name_ref RuntimeConvertFrom, file.%RuntimeConvertFrom.decl [concrete = constants.%RuntimeConvertFrom] // CHECK:STDOUT: %Core.ref: = name_ref Core, imports.%Core [concrete = imports.%Core] // CHECK:STDOUT: %ImplicitAs.ref: %ImplicitAs.type.cc7 = name_ref ImplicitAs, imports.%Core.ImplicitAs [concrete = constants.%ImplicitAs.generic] // CHECK:STDOUT: %RuntimeConvertTo.ref: type = name_ref RuntimeConvertTo, file.%RuntimeConvertTo.decl [concrete = constants.%RuntimeConvertTo] // CHECK:STDOUT: %ImplicitAs.type: type = facet_type <@ImplicitAs, @ImplicitAs(constants.%RuntimeConvertTo)> [concrete = constants.%ImplicitAs.type.5cd] // CHECK:STDOUT: } // CHECK:STDOUT: %F.decl: %F.type = fn_decl @F [concrete = constants.%F] { // CHECK:STDOUT: %T.patt: %pattern_type.f1e = symbolic_binding_pattern T, 0 [concrete] // CHECK:STDOUT: %A.patt: @F.%pattern_type.loc27_27 (%pattern_type.e66) = symbolic_binding_pattern A, 1 [concrete] // CHECK:STDOUT: %x.patt: @F.%pattern_type.loc27_43 (%pattern_type.17d) = value_binding_pattern x [concrete] // CHECK:STDOUT: %x.param_patt: @F.%pattern_type.loc27_43 (%pattern_type.17d) = value_param_pattern %x.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %.loc27_17.1: type = splice_block %.loc27_17.3 [concrete = constants.%tuple.type] { // CHECK:STDOUT: %.Self.2: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %.loc27_11: type = type_literal type [concrete = type] // CHECK:STDOUT: %.loc27_17.2: %tuple.type = tuple_literal (%.loc27_11) [concrete = constants.%tuple.0d2] // CHECK:STDOUT: %.loc27_17.3: type = converted %.loc27_17.2, constants.%tuple.type [concrete = constants.%tuple.type] // CHECK:STDOUT: } // CHECK:STDOUT: %T.loc27_6.2: %tuple.type = symbolic_binding T, 0 [symbolic = %T.loc27_6.1 (constants.%T)] // CHECK:STDOUT: %.loc27_32: type = splice_block %tuple.elem0.loc27_32.2 [symbolic = %tuple.elem0.loc27_32.1 (constants.%tuple.elem0)] { // CHECK:STDOUT: %.Self.1: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %T.ref.loc27_31: %tuple.type = name_ref T, %T.loc27_6.2 [symbolic = %T.loc27_6.1 (constants.%T)] // CHECK:STDOUT: %int_0: Core.IntLiteral = int_value 0 [concrete = constants.%int_0] // CHECK:STDOUT: %tuple.elem0.loc27_32.2: type = tuple_access %T.ref.loc27_31, element0 [symbolic = %tuple.elem0.loc27_32.1 (constants.%tuple.elem0)] // CHECK:STDOUT: } // CHECK:STDOUT: %A.loc27_27.2: @F.%tuple.elem0.loc27_32.1 (%tuple.elem0) = symbolic_binding A, 1 [symbolic = %A.loc27_27.1 (constants.%A)] // CHECK:STDOUT: %x.param: @F.%HoldsType.loc27_57.1 (%HoldsType.f6a) = value_param call_param0 // CHECK:STDOUT: %.loc27_57: type = splice_block %HoldsType.loc27_57.2 [symbolic = %HoldsType.loc27_57.1 (constants.%HoldsType.f6a)] { // CHECK:STDOUT: %HoldsType.ref: %HoldsType.type = name_ref HoldsType, file.%HoldsType.decl [concrete = constants.%HoldsType.generic] // CHECK:STDOUT: %T.ref.loc27_56: %tuple.type = name_ref T, %T.loc27_6.2 [symbolic = %T.loc27_6.1 (constants.%T)] // CHECK:STDOUT: %HoldsType.loc27_57.2: type = class_type @HoldsType, @HoldsType(constants.%T) [symbolic = %HoldsType.loc27_57.1 (constants.%HoldsType.f6a)] // CHECK:STDOUT: } // CHECK:STDOUT: %x: @F.%HoldsType.loc27_57.1 (%HoldsType.f6a) = value_binding x, %x.param // CHECK:STDOUT: } // CHECK:STDOUT: %G.decl: %G.type = fn_decl @G [concrete = constants.%G] { // CHECK:STDOUT: %holds_to.patt: %pattern_type.c6f = value_binding_pattern holds_to [concrete] // CHECK:STDOUT: %holds_to.param_patt: %pattern_type.c6f = value_param_pattern %holds_to.patt [concrete] // CHECK:STDOUT: %from.patt: %pattern_type.6bd = symbolic_binding_pattern from, 0 [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %holds_to.param: %HoldsType.0ca = value_param call_param0 // CHECK:STDOUT: %.loc29_46.1: type = splice_block %HoldsType [concrete = constants.%HoldsType.0ca] { // CHECK:STDOUT: %HoldsType.ref: %HoldsType.type = name_ref HoldsType, file.%HoldsType.decl [concrete = constants.%HoldsType.generic] // CHECK:STDOUT: %RuntimeConvertTo.ref: type = name_ref RuntimeConvertTo, file.%RuntimeConvertTo.decl [concrete = constants.%RuntimeConvertTo] // CHECK:STDOUT: %.loc29_45: %tuple.type = tuple_literal (%RuntimeConvertTo.ref) [concrete = constants.%tuple.b95] // CHECK:STDOUT: %tuple.loc29: %tuple.type = tuple_value (%RuntimeConvertTo.ref) [concrete = constants.%tuple.b95] // CHECK:STDOUT: %.loc29_46.2: %tuple.type = converted %.loc29_45, %tuple.loc29 [concrete = constants.%tuple.b95] // CHECK:STDOUT: %HoldsType: type = class_type @HoldsType, @HoldsType(constants.%tuple.b95) [concrete = constants.%HoldsType.0ca] // CHECK:STDOUT: } // CHECK:STDOUT: %holds_to: %HoldsType.0ca = value_binding holds_to, %holds_to.param // CHECK:STDOUT: %.loc29_56: type = splice_block %RuntimeConvertFrom.ref [concrete = constants.%RuntimeConvertFrom] { // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %RuntimeConvertFrom.ref: type = name_ref RuntimeConvertFrom, file.%RuntimeConvertFrom.decl [concrete = constants.%RuntimeConvertFrom] // CHECK:STDOUT: } // CHECK:STDOUT: %from.loc29_49.2: %RuntimeConvertFrom = symbolic_binding from, 0 [symbolic = %from.loc29_49.1 (constants.%from)] // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: impl @RuntimeConvertFrom.as.ImplicitAs.impl: %RuntimeConvertFrom.ref as %ImplicitAs.type { // CHECK:STDOUT: %RuntimeConvertFrom.as.ImplicitAs.impl.Convert.decl: %RuntimeConvertFrom.as.ImplicitAs.impl.Convert.type = fn_decl @RuntimeConvertFrom.as.ImplicitAs.impl.Convert [concrete = constants.%RuntimeConvertFrom.as.ImplicitAs.impl.Convert] { // CHECK:STDOUT: %self.patt: %pattern_type.6bd = value_binding_pattern self [concrete] // CHECK:STDOUT: %self.param_patt: %pattern_type.6bd = value_param_pattern %self.patt [concrete] // CHECK:STDOUT: %return.patt: %pattern_type.c89 = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: %pattern_type.c89 = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %RuntimeConvertTo.ref: type = name_ref RuntimeConvertTo, file.%RuntimeConvertTo.decl [concrete = constants.%RuntimeConvertTo] // CHECK:STDOUT: %.loc24_38: Core.Form = init_form %RuntimeConvertTo.ref [concrete = constants.%.993] // CHECK:STDOUT: %self.param: %RuntimeConvertFrom = value_param call_param0 // CHECK:STDOUT: %Self.ref: type = name_ref Self, @RuntimeConvertFrom.as.ImplicitAs.impl.%RuntimeConvertFrom.ref [concrete = constants.%RuntimeConvertFrom] // CHECK:STDOUT: %self: %RuntimeConvertFrom = value_binding self, %self.param // CHECK:STDOUT: %return.param: ref %RuntimeConvertTo = out_param call_param1 // CHECK:STDOUT: %return: ref %RuntimeConvertTo = return_slot %return.param // CHECK:STDOUT: } // CHECK:STDOUT: %ImplicitAs.impl_witness_table = impl_witness_table (%RuntimeConvertFrom.as.ImplicitAs.impl.Convert.decl), @RuntimeConvertFrom.as.ImplicitAs.impl [concrete] // CHECK:STDOUT: %ImplicitAs.impl_witness: = impl_witness %ImplicitAs.impl_witness_table [concrete = constants.%ImplicitAs.impl_witness] // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .RuntimeConvertTo = // CHECK:STDOUT: .Convert = %RuntimeConvertFrom.as.ImplicitAs.impl.Convert.decl // CHECK:STDOUT: witness = %ImplicitAs.impl_witness // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic class @HoldsType(%T.loc17_17.2: %tuple.type) { // CHECK:STDOUT: %T.loc17_17.1: %tuple.type = symbolic_binding T, 0 [symbolic = %T.loc17_17.1 (constants.%T)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: // CHECK:STDOUT: class { // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%HoldsType.f6a // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @RuntimeConvertFrom { // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%RuntimeConvertFrom // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @RuntimeConvertTo { // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%RuntimeConvertTo // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @RuntimeConvertFrom.as.ImplicitAs.impl.Convert(%self.param: %RuntimeConvertFrom) -> out %return.param: %RuntimeConvertTo { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %.loc24_65.1: %empty_struct_type = struct_literal () [concrete = constants.%empty_struct] // CHECK:STDOUT: %.loc24_65.2: init %RuntimeConvertTo to %return.param = class_init () [concrete = constants.%RuntimeConvertTo.val] // CHECK:STDOUT: %.loc24_66: init %RuntimeConvertTo = converted %.loc24_65.1, %.loc24_65.2 [concrete = constants.%RuntimeConvertTo.val] // CHECK:STDOUT: return %.loc24_66 to %return.param // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic fn @F(%T.loc27_6.2: %tuple.type, %A.loc27_27.2: @F.%tuple.elem0.loc27_32.1 (%tuple.elem0)) { // CHECK:STDOUT: %T.loc27_6.1: %tuple.type = symbolic_binding T, 0 [symbolic = %T.loc27_6.1 (constants.%T)] // CHECK:STDOUT: %tuple.elem0.loc27_32.1: type = tuple_access %T.loc27_6.1, element0 [symbolic = %tuple.elem0.loc27_32.1 (constants.%tuple.elem0)] // CHECK:STDOUT: %A.loc27_27.1: @F.%tuple.elem0.loc27_32.1 (%tuple.elem0) = symbolic_binding A, 1 [symbolic = %A.loc27_27.1 (constants.%A)] // CHECK:STDOUT: %pattern_type.loc27_27: type = pattern_type %tuple.elem0.loc27_32.1 [symbolic = %pattern_type.loc27_27 (constants.%pattern_type.e66)] // CHECK:STDOUT: %HoldsType.loc27_57.1: type = class_type @HoldsType, @HoldsType(%T.loc27_6.1) [symbolic = %HoldsType.loc27_57.1 (constants.%HoldsType.f6a)] // CHECK:STDOUT: %pattern_type.loc27_43: type = pattern_type %HoldsType.loc27_57.1 [symbolic = %pattern_type.loc27_43 (constants.%pattern_type.17d)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete: = require_complete_type %HoldsType.loc27_57.1 [symbolic = %require_complete (constants.%require_complete)] // CHECK:STDOUT: // CHECK:STDOUT: fn(%x.param: @F.%HoldsType.loc27_57.1 (%HoldsType.f6a)) { // CHECK:STDOUT: !entry: // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic fn @G(%from.loc29_49.2: %RuntimeConvertFrom) { // CHECK:STDOUT: %from.loc29_49.1: %RuntimeConvertFrom = symbolic_binding from, 0 [symbolic = %from.loc29_49.1 (constants.%from)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %RuntimeConvertFrom.as.ImplicitAs.impl.Convert.bound: = bound_method %from.loc29_49.1, constants.%RuntimeConvertFrom.as.ImplicitAs.impl.Convert [symbolic = %RuntimeConvertFrom.as.ImplicitAs.impl.Convert.bound (constants.%RuntimeConvertFrom.as.ImplicitAs.impl.Convert.bound)] // CHECK:STDOUT: // CHECK:STDOUT: fn(%holds_to.param: %HoldsType.0ca) { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %F.ref: %F.type = name_ref F, file.%F.decl [concrete = constants.%F] // CHECK:STDOUT: %from.ref: %RuntimeConvertFrom = name_ref from, %from.loc29_49.2 [symbolic = %from.loc29_49.1 (constants.%from)] // CHECK:STDOUT: %holds_to.ref: %HoldsType.0ca = name_ref holds_to, %holds_to // CHECK:STDOUT: %impl.elem0: %.8ab = impl_witness_access constants.%ImplicitAs.impl_witness, element0 [concrete = constants.%RuntimeConvertFrom.as.ImplicitAs.impl.Convert] // CHECK:STDOUT: %bound_method: = bound_method constants.%from, %impl.elem0 [symbolic = %RuntimeConvertFrom.as.ImplicitAs.impl.Convert.bound (constants.%RuntimeConvertFrom.as.ImplicitAs.impl.Convert.bound)] // CHECK:STDOUT: %.loc40_19.1: ref %RuntimeConvertTo = temporary_storage // CHECK:STDOUT: %RuntimeConvertFrom.as.ImplicitAs.impl.Convert.call: init %RuntimeConvertTo to %.loc40_19.1 = call %bound_method(constants.%from) // CHECK:STDOUT: %.loc40_19.2: init %RuntimeConvertTo = converted constants.%from, %RuntimeConvertFrom.as.ImplicitAs.impl.Convert.call // CHECK:STDOUT: %.loc40_19.3: ref %RuntimeConvertTo = temporary %.loc40_19.1, %.loc40_19.2 // CHECK:STDOUT: %.loc40_19.4: %RuntimeConvertTo = acquire_value %.loc40_19.3 // CHECK:STDOUT: %F.specific_fn: = specific_function %F.ref, @F(constants.%tuple.b95, ) [concrete = ] // CHECK:STDOUT: %.loc40_19.5: %empty_tuple.type = call %F.specific_fn(%holds_to.ref) // CHECK:STDOUT: %tuple.loc40: %empty_tuple.type = tuple_value () [concrete = constants.%empty_tuple] // CHECK:STDOUT: %.loc40_19.6: %empty_tuple.type = converted %.loc40_19.5, %tuple.loc40 [concrete = constants.%empty_tuple] // CHECK:STDOUT: %Destroy.Op.bound: = bound_method %.loc40_19.3, constants.%Destroy.Op // CHECK:STDOUT: %Destroy.Op.call: init %empty_tuple.type = call %Destroy.Op.bound(%.loc40_19.3) // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @Destroy.Op(%self.param: ref %RuntimeConvertTo) = "no_op"; // CHECK:STDOUT: // CHECK:STDOUT: specific @HoldsType(constants.%T) { // CHECK:STDOUT: %T.loc17_17.1 => constants.%T // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @F(constants.%T, constants.%A) { // CHECK:STDOUT: %T.loc27_6.1 => constants.%T // CHECK:STDOUT: %tuple.elem0.loc27_32.1 => constants.%tuple.elem0 // CHECK:STDOUT: %A.loc27_27.1 => constants.%A // CHECK:STDOUT: %pattern_type.loc27_27 => constants.%pattern_type.e66 // CHECK:STDOUT: %HoldsType.loc27_57.1 => constants.%HoldsType.f6a // CHECK:STDOUT: %pattern_type.loc27_43 => constants.%pattern_type.17d // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @HoldsType(constants.%tuple.b95) { // CHECK:STDOUT: %T.loc17_17.1 => constants.%tuple.b95 // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @G(constants.%from) { // CHECK:STDOUT: %from.loc29_49.1 => constants.%from // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @F(constants.%tuple.b95, ) { // CHECK:STDOUT: %T.loc27_6.1 => constants.%tuple.b95 // CHECK:STDOUT: %tuple.elem0.loc27_32.1 => constants.%RuntimeConvertTo // CHECK:STDOUT: %A.loc27_27.1 => // CHECK:STDOUT: %pattern_type.loc27_27 => constants.%pattern_type.c89 // CHECK:STDOUT: %HoldsType.loc27_57.1 => constants.%HoldsType.0ca // CHECK:STDOUT: %pattern_type.loc27_43 => constants.%pattern_type.c6f // CHECK:STDOUT: } // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/facet/fail_incomplete.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/none.carbon // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/facet/fail_incomplete.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/facet/fail_incomplete.carbon // --- fail_impl_lookup_incomplete.carbon library "[[@TEST_NAME]]"; constraint Z; fn AsZ(unused T:! Z) {} fn F() { // Requires Z identified. // CHECK:STDERR: fail_impl_lookup_incomplete.carbon:[[@LINE+10]]:3: error: facet type `Z` can not be identified [ImplLookupInUnidentifiedFacetType] // CHECK:STDERR: AsZ(()); // CHECK:STDERR: ^~~~~~~ // CHECK:STDERR: fail_impl_lookup_incomplete.carbon:[[@LINE-9]]:1: note: constraint was forward declared here [NamedConstraintForwardDeclaredHere] // CHECK:STDERR: constraint Z; // CHECK:STDERR: ^~~~~~~~~~~~~ // CHECK:STDERR: fail_impl_lookup_incomplete.carbon:[[@LINE-10]]:15: note: initializing generic parameter `T` declared here [InitializingGenericParam] // CHECK:STDERR: fn AsZ(unused T:! Z) {} // CHECK:STDERR: ^ // CHECK:STDERR: AsZ(()); } ================================================ FILE: toolchain/check/testdata/facet/fail_namespace_type.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/convert.carbon // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/facet/fail_namespace_type.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/facet/fail_namespace_type.carbon // --- fail_namespace_as.carbon library "[[@TEST_NAME]]"; namespace N; interface Z {} impl forall [T:! type] T as Z {} fn F() { // CHECK:STDERR: fail_namespace_as.carbon:[[@LINE+4]]:3: error: expression cannot be used as a value [UseOfNonExprAsValue] // CHECK:STDERR: N as Z; // CHECK:STDERR: ^ // CHECK:STDERR: N as Z; } // --- fail_namespace_argument.carbon library "[[@TEST_NAME]]"; namespace N; interface Z {} impl forall [T:! type] T as Z {} fn G[T:! Z](unused a: T) {} fn F() { // CHECK:STDERR: fail_namespace_argument.carbon:[[@LINE+7]]:5: error: expression cannot be used as a value [UseOfNonExprAsValue] // CHECK:STDERR: G(N); // CHECK:STDERR: ^ // CHECK:STDERR: fail_namespace_argument.carbon:[[@LINE-6]]:20: note: initializing function parameter [InCallToFunctionParam] // CHECK:STDERR: fn G[T:! Z](unused a: T) {} // CHECK:STDERR: ^~~~ // CHECK:STDERR: G(N); } ================================================ FILE: toolchain/check/testdata/facet/identify_self_canonicalized.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/convert.carbon // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/facet/identify_self_canonicalized.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/facet/identify_self_canonicalized.carbon // --- identify_self_canonicalized.carbon library "[[@TEST_NAME]]"; interface Z {} constraint W { extend require impls Z; } interface Y {} // T is a facet value (a SymbolicBinding), which is converted to `type`. The // identified facet type finds `(T as type) impls Z` from the top level facet // type. It also finds `T as Z` through `W`. It's possible for these to disagree // on the self and thus present as two different required interfaces in the // identified facet type. Instead we should only get a single interface with the // canonical `T` facet value as the self value. impl forall [T:! Y] T as W & Z {} ================================================ FILE: toolchain/check/testdata/facet/named_constant.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/convert.carbon // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/facet/named_constant.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/facet/named_constant.carbon // --- fail_todo_named_constant_in_rewrite.carbon library "[[@TEST_NAME]]"; interface I { let X:! type; let Y:! type; } fn Test() { // CHECK:STDERR: fail_todo_named_constant_in_rewrite.carbon:[[@LINE+4]]:7: error: semantics TODO: `local `let :!` bindings are currently unsupported` [SemanticsTodo] // CHECK:STDERR: let Constant:! type = (); // CHECK:STDERR: ^~~~~~~~~~~~~~~ // CHECK:STDERR: let Constant:! type = (); fn F(T:! I where .X = Constant) {} fn G(T:! I where .X = Constant) { // TODO: The facet type T in G should match the facet type T in F. F(T); } } // --- fail_todo_named_constant_two_levels_in_rewrite.carbon library "[[@TEST_NAME]]"; interface I { let X:! type; let Y:! type; } fn Test() { // CHECK:STDERR: fail_todo_named_constant_two_levels_in_rewrite.carbon:[[@LINE+4]]:7: error: semantics TODO: `local `let :!` bindings are currently unsupported` [SemanticsTodo] // CHECK:STDERR: let Constant2:! type = (); // CHECK:STDERR: ^~~~~~~~~~~~~~~~ // CHECK:STDERR: let Constant2:! type = (); let Constant:! type = (Constant2, ); fn F(T:! I where .X = Constant) {} fn G(T:! I where .X = Constant) { // TODO: The facet type T in G should match the facet type T in F. F(T); } } // --- fail_todo_named_constant_in_rewrite_callee.carbon library "[[@TEST_NAME]]"; interface I { let X:! type; let Y:! type; } fn Test() { // CHECK:STDERR: fail_todo_named_constant_in_rewrite_callee.carbon:[[@LINE+4]]:7: error: semantics TODO: `local `let :!` bindings are currently unsupported` [SemanticsTodo] // CHECK:STDERR: let Constant:! type = (); // CHECK:STDERR: ^~~~~~~~~~~~~~~ // CHECK:STDERR: let Constant:! type = (); fn F(T:! I where .X = Constant) {} fn G(T:! I where .X = ()) { // TODO: The facet type T in G should match the facet type T in F. F(T); } } // --- fail_todo_named_constant_in_rewrite_caller.carbon library "[[@TEST_NAME]]"; interface I { let X:! type; let Y:! type; } fn Test() { // CHECK:STDERR: fail_todo_named_constant_in_rewrite_caller.carbon:[[@LINE+4]]:7: error: semantics TODO: `local `let :!` bindings are currently unsupported` [SemanticsTodo] // CHECK:STDERR: let Constant:! type = (); // CHECK:STDERR: ^~~~~~~~~~~~~~~ // CHECK:STDERR: let Constant:! type = (); fn F(T:! I where .X = ()) {} fn G(T:! I where .X = Constant) { // TODO: The facet type T in G should match the facet type T in F. F(T); } } // --- fail_todo_named_constant_in_nested_facet_type.carbon library "[[@TEST_NAME]]"; interface I { let X:! type; let Y:! type; } fn Test() { // CHECK:STDERR: fail_todo_named_constant_in_nested_facet_type.carbon:[[@LINE+4]]:7: error: semantics TODO: `local `let :!` bindings are currently unsupported` [SemanticsTodo] // CHECK:STDERR: let Constant:! type = (); // CHECK:STDERR: ^~~~~~~~~~~~~~~ // CHECK:STDERR: let Constant:! type = (); // TODO: The .Self reference in each .X should be different, there should be // no cycle here. fn F(T:! I where .X = (I where .X = Constant)) {} fn G(T:! I where .X = (I where .X = Constant)) { // TODO: The facet type T in G should match the facet type T in F. F(T); } } // --- fail_todo_named_constant_in_nested_facet_type_caller.carbon library "[[@TEST_NAME]]"; interface I { let X:! type; let Y:! type; } fn Test() { // CHECK:STDERR: fail_todo_named_constant_in_nested_facet_type_caller.carbon:[[@LINE+4]]:7: error: semantics TODO: `local `let :!` bindings are currently unsupported` [SemanticsTodo] // CHECK:STDERR: let Constant:! type = (); // CHECK:STDERR: ^~~~~~~~~~~~~~~ // CHECK:STDERR: let Constant:! type = (); fn F(T:! I where .X = (I where .X = ())) {} // TODO: The .Self reference in each .X should be different, there should be // no cycle here. fn G(T:! I where .X = (I where .X = Constant)) { // TODO: The facet type T in G should match the facet type T in F. F(T); } } // --- fail_named_constant_in_nested_facet_type_callee.carbon library "[[@TEST_NAME]]"; interface I { let X:! type; let Y:! type; } fn Test() { // CHECK:STDERR: fail_named_constant_in_nested_facet_type_callee.carbon:[[@LINE+4]]:7: error: semantics TODO: `local `let :!` bindings are currently unsupported` [SemanticsTodo] // CHECK:STDERR: let Constant:! type = (); // CHECK:STDERR: ^~~~~~~~~~~~~~~ // CHECK:STDERR: let Constant:! type = (); // TODO: The .Self reference in each .X should be different, there should be // no cycle here. fn F(T:! I where .X = (I where .X = Constant)) {} fn G(T:! I where .X = (I where .X = ())) { // TODO: The facet type T in G should match the facet type T in F. F(T); } } ================================================ FILE: toolchain/check/testdata/facet/nested_facet_types.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/convert.carbon // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/facet/nested_facet_types.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/facet/nested_facet_types.carbon // --- nested_facet_types.carbon library "[[@TEST_NAME]]"; interface Z { let T:! type; let U:! type; } fn F(FF:! (Z where .T = ()) where .U = .T) -> FF.U { return (); } // --- nested_facet_types_same.carbon library "[[@TEST_NAME]]"; interface Z { let T:! type; } fn F(FF:! ((Z where .T = ()) where .T = ()) where .T = ()) -> FF.T { return (); } // --- nested_facet_types_same_associated.carbon library "[[@TEST_NAME]]"; interface Z { let T:! type; let U:! type; } fn F(unused FF:! ((Z where .T = .U) where .T = .U) where .T = .U) {} // --- nested_facet_types_same_two_associated.carbon library "[[@TEST_NAME]]"; interface Z { let T:! type; let U:! type; let V:! type; } fn F(unused FF:! ((Z where .T = .U and .U = .V) where .T = .U and .U = .V) where .T = .U and .U = .V) {} // --- nested_facet_types_same_associated_in_generic_parameter.carbon library "[[@TEST_NAME]]"; interface Z { let T:! type; let U:! type; } class C(T:! type) {} fn F(unused FF:! ((Z where .T = C(.U)) where .T = C(.U)) where .T = C(.U)) {} // --- fail_nested_facet_types_different.carbon library "[[@TEST_NAME]]"; interface Z { let T:! type; } // CHECK:STDERR: fail_nested_facet_types_different.carbon:[[@LINE+4]]:18: error: associated constant `.(Z.T)` given two different values `()` and `{}` [AssociatedConstantWithDifferentValues] // CHECK:STDERR: fn F(unused FF:! (Z where .T = ()) where .T = {}) {} // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: fn F(unused FF:! (Z where .T = ()) where .T = {}) {} // --- fail_nested_facet_types_different_with_associated.carbon library "[[@TEST_NAME]]"; interface Z { let T:! type; let U:! type; } // CHECK:STDERR: fail_nested_facet_types_different_with_associated.carbon:[[@LINE+4]]:18: error: associated constant `.(Z.T)` given two different values `.(Z.U)` and `{}` [AssociatedConstantWithDifferentValues] // CHECK:STDERR: fn F(unused FF:! (Z where .T = .U) where .T = {}) {} // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: fn F(unused FF:! (Z where .T = .U) where .T = {}) {} // --- fail_nested_facet_types_different_with_associated_in_generic_parameter.carbon library "[[@TEST_NAME]]"; interface Z { let T:! type; let U:! type; } class C(T:! type) {} // CHECK:STDERR: fail_nested_facet_types_different_with_associated_in_generic_parameter.carbon:[[@LINE+4]]:18: error: associated constant `.(Z.T)` given two different values `C(.(Z.U))` and `{}` [AssociatedConstantWithDifferentValues] // CHECK:STDERR: fn F(unused FF:! (Z where .T = C(.U)) where .T = {}) {} // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: fn F(unused FF:! (Z where .T = C(.U)) where .T = {}) {} // --- nested_facet_types_same_with_bitand.carbon library "[[@TEST_NAME]]"; interface Z { let T:! type; let U:! type; } fn F(unused FF:! ((Z where .T = .U) where .T = .U) & (Z where .T = .U)) {} // --- fail_nested_facet_types_different_with_bitand.carbon library "[[@TEST_NAME]]"; interface Z { let T:! type; let U:! type; } // CHECK:STDERR: fail_nested_facet_types_different_with_bitand.carbon:[[@LINE+4]]:18: error: associated constant `.(Z.T)` given two different values `.(Z.U)` and `{}` [AssociatedConstantWithDifferentValues] // CHECK:STDERR: fn F(unused FF:! ((Z where .T = .U) where .T = .U) & (Z where .T = {})) {} // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: fn F(unused FF:! ((Z where .T = .U) where .T = .U) & (Z where .T = {})) {} // --- fail_nested_facet_type_cycle.carbon library "[[@TEST_NAME]]"; interface Z { let T:! type; let U:! type; } // CHECK:STDERR: fail_nested_facet_type_cycle.carbon:[[@LINE+4]]:18: error: found cycle in facet type constraint for `.(Z.U)` [FacetTypeConstraintCycle] // CHECK:STDERR: fn F(unused FF:! (Z where .T = .U) where .U = .T) {} // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: fn F(unused FF:! (Z where .T = .U) where .U = .T) {} // --- fail_nested_facet_types_different_separated_order_one.carbon library "[[@TEST_NAME]]"; interface Z { let S:! type; let T:! type; let U:! type; let V:! type; } // The ordering here is meant to find a way to insert a LHS between the two `.S` // ids, by producing different orderings to get an id between them. // CHECK:STDERR: fail_nested_facet_types_different_separated_order_one.carbon:[[@LINE+4]]:18: error: associated constant `.(Z.S)` given two different values `.(Z.U)` and `{}` [AssociatedConstantWithDifferentValues] // CHECK:STDERR: fn F(unused FF:! (((Z where .S = .U) where .V = {}) where .T = .U) & (Z where .S = {})) {} // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: fn F(unused FF:! (((Z where .S = .U) where .V = {}) where .T = .U) & (Z where .S = {})) {} // --- fail_nested_facet_types_different_separated_order_two.carbon library "[[@TEST_NAME]]"; interface Z { let S:! type; let T:! type; let U:! type; let V:! type; } // The ordering here is meant to find a way to insert a LHS between the two `.S` // ids, by producing different orderings to get an id between them. As of today, // this is the case where `.T` is sorted between the two `.S` rewrite rules. // CHECK:STDERR: fail_nested_facet_types_different_separated_order_two.carbon:[[@LINE+4]]:18: error: associated constant `.(Z.S)` given two different values `{}` and `.(Z.U)` [AssociatedConstantWithDifferentValues] // CHECK:STDERR: fn F(unused FF:! (((Z where .V = {}) where .S = .U) where .T = .U) & (Z where .S = {})) {} // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: fn F(unused FF:! (((Z where .V = {}) where .S = .U) where .T = .U) & (Z where .S = {})) {} // --- fail_nested_facet_types_different_separated_order_three.carbon library "[[@TEST_NAME]]"; interface Z { let S:! type; let T:! type; let U:! type; let V:! type; } // The ordering here is meant to find a way to insert a LHS between the two `.S` // ids, by producing different orderings to get an id between them. // CHECK:STDERR: fail_nested_facet_types_different_separated_order_three.carbon:[[@LINE+4]]:18: error: associated constant `.(Z.S)` given two different values `{}` and `.(Z.U)` [AssociatedConstantWithDifferentValues] // CHECK:STDERR: fn F(unused FF:! (((Z where .V = {}) where .T = .U) where .S = .U) & (Z where .S = {})) {} // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: fn F(unused FF:! (((Z where .V = {}) where .T = .U) where .S = .U) & (Z where .S = {})) {} // --- repeated_with_value_available.carbon library "[[@TEST_NAME]]"; interface Z { let X:! type; let Y:! type; } fn F(T:! ((Z where .Y = ()) where .X = .Y) where .X = .Y) -> T.X { return (); } class C(T:! type) { adapt (); } fn F1(T:! (Z where .Y = C(()) and .X = C(.Y)) where .X = C(C(()))) -> T.X { return () as C(C(())); } fn F2(T:! ((Z where .Y = C(())) where .X = C(.Y)) where .X = C(C(()))) -> T.X { return () as C(C(())); } fn F3(T:! Z where .Y = C(()) and .X = C(.Y) and .X = C(C(()))) -> T.X { return () as C(C(())); } // --- conflicting_syntax_same_value.carbon library "[[@TEST_NAME]]"; interface Z { let X:! type; let Y:! type; } fn F1(T:! ((Z where .Y = ()) where .X = .Y) where .X = ()) -> T.X { return (); } fn F2(T:! ((Z where .Y = ()) where .X = ()) where .X = .Y) -> T.X { return (); } // --- nested_impls_repeated.carbon library "[[@TEST_NAME]]"; interface Y {} interface Z { let T:! type; let U:! type; } class C(T:! type) {} fn F(unused FF:! ((Y where .Self impls (Z where .T = C(.U))) where .Self impls (Z where .T = C(.U))) where .Self impls (Z where .T = C(.U))) {} // --- nested_impl_unique.carbon library "[[@TEST_NAME]]"; interface Y { let Y1:! type; } interface Z { let Z1:! type; let Z2:! type; } class C(T:! type) { adapt (); } fn F(FF:! ((Y where .Y1 = ()) where .Self impls (Z where .Z1 = C(.Z2) and .Z2 = ()))) -> FF.(Z.Z1) { return () as C(()); } ================================================ FILE: toolchain/check/testdata/facet/period_self.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/convert.carbon // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/facet/period_self.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/facet/period_self.carbon // --- period_self_param.carbon library "[[@TEST_NAME]]"; //@dump-sem-ir-begin interface I(T:! type) { let I1:! type; } fn F(T:! I(.Self) where .I1 = ()) -> T.I1 { return (); } fn G(T:! I(.Self as type) where .I1 = ()) -> T.I1 { return (); } //@dump-sem-ir-end // --- underscore_identifier_name.carbon library "[[@TEST_NAME]]"; interface I(T:! type) { let I1:! type; } // Underscore as the identifier name produces a different parse tree for the // binding pattern. fn G(_:! I(.Self) where .I1 = ()) {} // --- fail_period_self_as_type.carbon library "[[@TEST_NAME]]"; // TODO: We should diagnose this use of `.Self` directly rather than later when // it is converted to `type`. interface I(T:! .Self) { // CHECK:STDERR: fail_period_self_as_type.carbon:[[@LINE+7]]:13: error: cannot implicitly convert non-type value of type `.Self` to `type` [ConversionFailureNonTypeToFacet] // CHECK:STDERR: fn G() -> T; // CHECK:STDERR: ^ // CHECK:STDERR: fail_period_self_as_type.carbon:[[@LINE+4]]:13: note: type `.Self` does not implement interface `Core.ImplicitAs(type)` [MissingImplInMemberAccessInContext] // CHECK:STDERR: fn G() -> T; // CHECK:STDERR: ^ // CHECK:STDERR: fn G() -> T; } // --- fail_todo_convert_period_self_to_full_facet_value.carbon library "[[@TEST_NAME]]"; interface I(T:! type) {} fn F(U:! I(.Self)) { // CHECK:STDERR: fail_todo_convert_period_self_to_full_facet_value.carbon:[[@LINE+4]]:3: error: cannot convert type `U` that implements `I(.Self)` into type implementing `I(U)` [ConversionFailureFacetToFacet] // CHECK:STDERR: U as I(U); // CHECK:STDERR: ^~~~~~~~~ // CHECK:STDERR: U as I(U); // CHECK:STDERR: fail_todo_convert_period_self_to_full_facet_value.carbon:[[@LINE+4]]:3: error: cannot convert type `U` that implements `I(.Self)` into type implementing `I(U)` [ConversionFailureFacetToFacet] // CHECK:STDERR: (U as type) as I(U); // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: (U as type) as I(U); } // --- fail_todo_convert_period_self_to_full_facet_value_with_assoc_constant.carbon library "[[@TEST_NAME]]"; interface I(T:! type) { let X:! type; } fn F(U:! I(.Self) where .X = .Self) { // CHECK:STDERR: fail_todo_convert_period_self_to_full_facet_value_with_assoc_constant.carbon:[[@LINE+4]]:3: error: cannot convert type `U` that implements `I(.Self) where .(I(.Self).X) = .Self` into type implementing `I(U) where .(I(U).X) = U` [ConversionFailureFacetToFacet] // CHECK:STDERR: U as (I(U) where .X = U); // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: U as (I(U) where .X = U); // CHECK:STDERR: fail_todo_convert_period_self_to_full_facet_value_with_assoc_constant.carbon:[[@LINE+4]]:3: error: cannot convert type `U` that implements `I(.Self) where .(I(.Self).X) = .Self` into type implementing `I(U) where .(I(U).X) = U` [ConversionFailureFacetToFacet] // CHECK:STDERR: (U as type) as (I(U) where .X = U); // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: (U as type) as (I(U) where .X = U); } // --- fail_todo_return_of_type_period_self.carbon library "[[@TEST_NAME]]"; interface I(T:! type) { fn G() -> T*; } interface J(T:! type) { fn J1() -> T*; } fn F2[U:! I(.Self)](unused T: U*) {} fn F(U:! I(.Self)) { // Caller sees the returned `.Self` type from `F()` as the full `U`. // CHECK:STDERR: fail_todo_return_of_type_period_self.carbon:[[@LINE+4]]:3: error: member name `G` not found [MemberNameNotFound] // CHECK:STDERR: U.G()->G()->G(); // CHECK:STDERR: ^~~~~~~~ // CHECK:STDERR: U.G()->G()->G(); // Conversion to `type` retains access to all of `U`. // CHECK:STDERR: fail_todo_return_of_type_period_self.carbon:[[@LINE+4]]:3: error: member name `G` not found [MemberNameNotFound] // CHECK:STDERR: (U as type).G()->G()->G(); // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~ // CHECK:STDERR: (U as type).G()->G()->G(); // Using the returned `.Self` as a facet value works, not just member lookup // on its facet type. // CHECK:STDERR: fail_todo_return_of_type_period_self.carbon:[[@LINE+4]]:6: error: member name `G` not found [MemberNameNotFound] // CHECK:STDERR: F2(U.G()->G()->G()); // CHECK:STDERR: ^~~~~~~~ // CHECK:STDERR: F2(U.G()->G()->G()); } // --- fail_todo_return_of_type_period_self_extends_interface.carbon library "[[@TEST_NAME]]"; interface I(T:! type) { fn I1() -> T*; } interface J(T:! type) { fn J1() -> T*; } fn F2[U:! I(.Self) & J(.Self)](unused T: U*) {} fn F(U:! I(.Self) & J(.Self)) { // TODO: The returned value of `I1` and `J1` has type `U` which has access to // the methods of `I` and `J`. // CHECK:STDERR: fail_todo_return_of_type_period_self_extends_interface.carbon:[[@LINE+4]]:3: error: member name `J1` not found [MemberNameNotFound] // CHECK:STDERR: U.I1()->J1()->I1()->J1()->I1()->J1(); // CHECK:STDERR: ^~~~~~~~~~ // CHECK:STDERR: U.I1()->J1()->I1()->J1()->I1()->J1(); // CHECK:STDERR: fail_todo_return_of_type_period_self_extends_interface.carbon:[[@LINE+4]]:3: error: member name `J1` not found [MemberNameNotFound] // CHECK:STDERR: (U as type).I1()->J1()->I1()->J1()->I1()->J1(); // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: (U as type).I1()->J1()->I1()->J1()->I1()->J1(); // TODO: Using the returned value of type `U` as a facet value works. // CHECK:STDERR: fail_todo_return_of_type_period_self_extends_interface.carbon:[[@LINE+4]]:6: error: member name `J1` not found [MemberNameNotFound] // CHECK:STDERR: F2(U.I1()->J1()->I1()->J1()->I1()->J1()); // CHECK:STDERR: ^~~~~~~~~~ // CHECK:STDERR: F2(U.I1()->J1()->I1()->J1()->I1()->J1()); } // --- fail_todo_return_of_period_self_impls_interface.carbon library "[[@TEST_NAME]]"; interface I(T:! type) { fn I1() -> T; } interface J(T:! type) { fn J1() -> T; } fn G(U:! I(.Self) where .Self impls J(.Self)) { // Compound member lookup through a non-type value is possible for methods // which take a `self` parameter. But it's not possible for methods without // `self`. For those you need to go directly throug the type. // See: https://github.com/carbon-language/carbon-lang/issues/6025 // TODO: This step should work. // // CHECK:STDERR: fail_todo_return_of_period_self_impls_interface.carbon:[[@LINE+7]]:14: error: cannot implicitly convert expression of type `.Self` to `U` [ConversionFailure] // CHECK:STDERR: let u: U = U.I1(); // CHECK:STDERR: ^~~~~~ // CHECK:STDERR: fail_todo_return_of_period_self_impls_interface.carbon:[[@LINE+4]]:14: note: type `.Self` does not implement interface `Core.ImplicitAs(U)` [MissingImplInMemberAccessInContext] // CHECK:STDERR: let u: U = U.I1(); // CHECK:STDERR: ^~~~~~ // CHECK:STDERR: let u: U = U.I1(); // `u` is a non-type value. Can call methods with `self` through compound // member lookup, but can't call methods without `self`. See the // `compound_access_through_call_with_self_param.carbon` test for the former. // // CHECK:STDERR: fail_todo_return_of_period_self_impls_interface.carbon:[[@LINE+4]]:6: error: type `` does not support qualified expressions [QualifiedExprUnsupported] // CHECK:STDERR: u.(J.J1)(); // CHECK:STDERR: ^~~~ // CHECK:STDERR: u.(J.J1)(); // This is the same as the above, since U.I1() returns a non-type value of // type `U`. // // CHECK:STDERR: fail_todo_return_of_period_self_impls_interface.carbon:[[@LINE+4]]:11: error: type `` does not support qualified expressions [QualifiedExprUnsupported] // CHECK:STDERR: U.I1().(J.J1)(); // CHECK:STDERR: ^~~~ // CHECK:STDERR: U.I1().(J.J1)(); } // --- fail_todo_return_of_type_period_self_has_type_u.carbon library "[[@TEST_NAME]]"; interface I(T:! type) { fn G() -> T; } fn F(U:! I(.Self)) { // CHECK:STDERR: fail_todo_return_of_type_period_self_has_type_u.carbon:[[@LINE+7]]:21: error: cannot implicitly convert expression of type `.Self` to `U` [ConversionFailure] // CHECK:STDERR: let unused a: U = U.G(); // CHECK:STDERR: ^~~~~ // CHECK:STDERR: fail_todo_return_of_type_period_self_has_type_u.carbon:[[@LINE+4]]:21: note: type `.Self` does not implement interface `Core.ImplicitAs(U)` [MissingImplInMemberAccessInContext] // CHECK:STDERR: let unused a: U = U.G(); // CHECK:STDERR: ^~~~~ // CHECK:STDERR: let unused a: U = U.G(); } // --- fail_todo_return_of_type_period_self_assoc_const_has_type_u.carbon library "[[@TEST_NAME]]"; interface I { let X:! type; fn G() -> X; } fn F(U:! I where .X = .Self) { // CHECK:STDERR: fail_todo_return_of_type_period_self_assoc_const_has_type_u.carbon:[[@LINE+7]]:21: error: cannot implicitly convert expression of type `.Self` to `U` [ConversionFailure] // CHECK:STDERR: let unused a: U = U.G(); // CHECK:STDERR: ^~~~~ // CHECK:STDERR: fail_todo_return_of_type_period_self_assoc_const_has_type_u.carbon:[[@LINE+4]]:21: note: type `.Self` does not implement interface `Core.ImplicitAs(U)` [MissingImplInMemberAccessInContext] // CHECK:STDERR: let unused a: U = U.G(); // CHECK:STDERR: ^~~~~ // CHECK:STDERR: let unused a: U = U.G(); } // --- fail_todo_nested_period_self.carbon library "[[@TEST_NAME]]"; interface I(T:! type) { let A:! type; let B:! type; fn G() -> T; } // Both `.Self` refer to `T`. The first because it's the interface for the // binding. The second because it refers to the top level facet type which is // constraining the binding. fn F(T:! I(.Self) where .A = ((I(.Self) where .B = {}) where .A = {}) and .B = {}, U:! T.A) { // T.G() has type T. // CHECK:STDERR: fail_todo_nested_period_self.carbon:[[@LINE+7]]:21: error: cannot implicitly convert expression of type `.Self` to `T` [ConversionFailure] // CHECK:STDERR: let unused t: T = T.G(); // CHECK:STDERR: ^~~~~ // CHECK:STDERR: fail_todo_nested_period_self.carbon:[[@LINE+4]]:21: note: type `.Self` does not implement interface `Core.ImplicitAs(T)` [MissingImplInMemberAccessInContext] // CHECK:STDERR: let unused t: T = T.G(); // CHECK:STDERR: ^~~~~ // CHECK:STDERR: let unused t: T = T.G(); // U.G() has type T. // CHECK:STDERR: fail_todo_nested_period_self.carbon:[[@LINE+7]]:21: error: cannot implicitly convert expression of type `.Self` to `T` [ConversionFailure] // CHECK:STDERR: let unused u: T = U.G(); // CHECK:STDERR: ^~~~~ // CHECK:STDERR: fail_todo_nested_period_self.carbon:[[@LINE+4]]:21: note: type `.Self` does not implement interface `Core.ImplicitAs(T)` [MissingImplInMemberAccessInContext] // CHECK:STDERR: let unused u: T = U.G(); // CHECK:STDERR: ^~~~~ // CHECK:STDERR: let unused u: T = U.G(); // Shows both `I(.Self)` are `I(T)`. // CHECK:STDERR: fail_todo_nested_period_self.carbon:[[@LINE+4]]:9: error: found cycle in facet type constraint for `.(I(T).A)` [FacetTypeConstraintCycle] // CHECK:STDERR: T as (I(T) where .A = (I(T) where .A = {} and .B = {})); // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: T as (I(T) where .A = (I(T) where .A = {} and .B = {})); // CHECK:STDERR: fail_todo_nested_period_self.carbon:[[@LINE+4]]:3: error: cannot convert type `U` that implements `I(.Self) where .(I(.Self).B) = {} and .(I(.Self).A) = {}` into type implementing `I(T) where .(I(T).A) = {} and .(I(T).B) = {}` [ConversionFailureFacetToFacet] // CHECK:STDERR: U as (I(T) where .A = {} and .B = {}); // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: U as (I(T) where .A = {} and .B = {}); } // --- todo_fail_nested_period_self_ambiguous.carbon library "[[@TEST_NAME]]"; interface I(T:! type) { let A:! type; } // TODO: This should be an error: The third `.Self` becomes is not able to be // bound to anything unambiguous here, as it could refer to `T` or to something // later being constrained by `T.A`. fn F(unused T:! I(.Self) where .A = (I(.Self) where .A = I(.Self))) {} // --- period_self_parameter_sees_lhs_of_where_expr.carbon library "[[@TEST_NAME]]"; interface I(T:! Core.Destroy) {} // The `.Self` can see the LHS of the `where` to know `U` impls Core.Destroy. fn F(unused U:! Core.Destroy where .Self impls I(.Self)) {} // --- fail_todo_period_self_parameter_constraint_satisfied_with_type_and.carbon library "[[@TEST_NAME]]"; interface I(T:! Core.Destroy) {} // TODO: Implied constraints that `.Self` impls `Core.Destroy` are satisfied by // the `&` expression. // // CHECK:STDERR: fail_todo_period_self_parameter_constraint_satisfied_with_type_and.carbon:[[@LINE+7]]:32: error: cannot convert type `.Self` that implements `type` into type implementing `Core.Destroy` [ConversionFailureFacetToFacet] // CHECK:STDERR: fn F(unused U:! Core.Destroy & I(.Self)) {} // CHECK:STDERR: ^~~~~~~~ // CHECK:STDERR: fail_todo_period_self_parameter_constraint_satisfied_with_type_and.carbon:[[@LINE-8]]:13: note: initializing generic parameter `T` declared here [InitializingGenericParam] // CHECK:STDERR: interface I(T:! Core.Destroy) {} // CHECK:STDERR: ^ // CHECK:STDERR: fn F(unused U:! Core.Destroy & I(.Self)) {} // --- fail_todo_compound_lookup_on_returned_period_self_parameter.carbon library "[[@TEST_NAME]]"; interface I(T:! Core.Destroy) { fn G[self: Self]() -> T; } fn F[U:! Core.Destroy where .Self impls I(.Self)](u: U) { // This tests that both `I.G` is accessible and that `Destroy` is preserved; // we'd get an error for missing Destroy otherwise since G() returns an // initializing expression. // CHECK:STDERR: fail_todo_compound_lookup_on_returned_period_self_parameter.carbon:[[@LINE+4]]:3: error: cannot access member of interface `I(U as Core.Destroy)` in type `U` that does not implement that interface [MissingImplInMemberAccess] // CHECK:STDERR: u.(I(U).G)().(I(U).G)().(I(U).G)(); // CHECK:STDERR: ^~~~~~~~~~ // CHECK:STDERR: u.(I(U).G)().(I(U).G)().(I(U).G)(); } // CHECK:STDOUT: --- period_self_param.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %type: type = facet_type [concrete] // CHECK:STDOUT: %.Self.c39: %type = symbolic_binding .Self [symbolic_self] // CHECK:STDOUT: %pattern_type.98f: type = pattern_type type [concrete] // CHECK:STDOUT: %T.67d: type = symbolic_binding T, 0 [symbolic] // CHECK:STDOUT: %I.type.609: type = generic_interface_type @I [concrete] // CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] // CHECK:STDOUT: %I.generic: %I.type.609 = struct_value () [concrete] // CHECK:STDOUT: %I.type.1ab: type = facet_type <@I, @I(%T.67d)> [symbolic] // CHECK:STDOUT: %Self.fdb: %I.type.1ab = symbolic_binding Self, 1 [symbolic] // CHECK:STDOUT: %I.assoc_type.76c: type = assoc_entity_type @I, @I(%T.67d) [symbolic] // CHECK:STDOUT: %assoc0.99e: %I.assoc_type.76c = assoc_entity element0, @I.WithSelf.%I1 [symbolic] // CHECK:STDOUT: %.Self.binding.as_type.8db: type = symbolic_binding_type .Self, %.Self.c39 [symbolic_self] // CHECK:STDOUT: %I.type.bee: type = facet_type <@I, @I(%.Self.binding.as_type.8db)> [symbolic_self] // CHECK:STDOUT: %.Self.dad: %I.type.bee = symbolic_binding .Self [symbolic_self] // CHECK:STDOUT: %Self.fa8: %I.type.bee = symbolic_binding Self, 1 [symbolic] // CHECK:STDOUT: %I.assoc_type.c03: type = assoc_entity_type @I, @I(%.Self.binding.as_type.8db) [symbolic_self] // CHECK:STDOUT: %assoc0.fe4: %I.assoc_type.c03 = assoc_entity element0, @I.WithSelf.%I1 [symbolic_self] // CHECK:STDOUT: %.Self.binding.as_type.b0b: type = symbolic_binding_type .Self, %.Self.dad [symbolic_self] // CHECK:STDOUT: %I.lookup_impl_witness.3fc: = lookup_impl_witness %.Self.dad, @I, @I(%.Self.binding.as_type.8db) [symbolic_self] // CHECK:STDOUT: %impl.elem0: type = impl_witness_access %I.lookup_impl_witness.3fc, element0 [symbolic_self] // CHECK:STDOUT: %empty_tuple: %empty_tuple.type = tuple_value () [concrete] // CHECK:STDOUT: %I_where.type: type = facet_type <@I, @I(%.Self.binding.as_type.8db) where %impl.elem0 = %empty_tuple.type> [symbolic_self] // CHECK:STDOUT: %pattern_type.033: type = pattern_type %I_where.type [symbolic_self] // CHECK:STDOUT: %T.706: %I_where.type = symbolic_binding T, 0 [symbolic] // CHECK:STDOUT: %T.binding.as_type: type = symbolic_binding_type T, 0, %T.706 [symbolic] // CHECK:STDOUT: %I.lookup_impl_witness.94d: = lookup_impl_witness %T.706, @I, @I(%.Self.binding.as_type.8db) [symbolic] // CHECK:STDOUT: %I.facet: %I.type.bee = facet_value %T.binding.as_type, (%I.lookup_impl_witness.94d) [symbolic] // CHECK:STDOUT: %.262: Core.Form = init_form %empty_tuple.type [concrete] // CHECK:STDOUT: %pattern_type.cb1: type = pattern_type %empty_tuple.type [concrete] // CHECK:STDOUT: %F.type: type = fn_type @F [concrete] // CHECK:STDOUT: %F: %F.type = struct_value () [concrete] // CHECK:STDOUT: %G.type: type = fn_type @G [concrete] // CHECK:STDOUT: %G: %G.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: %I.decl: %I.type.609 = interface_decl @I [concrete = constants.%I.generic] { // CHECK:STDOUT: %T.patt: %pattern_type.98f = symbolic_binding_pattern T, 0 [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %.loc4_17.1: type = splice_block %.loc4_17.2 [concrete = type] { // CHECK:STDOUT: // CHECK:STDOUT: %.loc4_17.2: type = type_literal type [concrete = type] // CHECK:STDOUT: } // CHECK:STDOUT: %T.loc4_13.2: type = symbolic_binding T, 0 [symbolic = %T.loc4_13.1 (constants.%T.67d)] // CHECK:STDOUT: } // CHECK:STDOUT: %F.decl: %F.type = fn_decl @F [concrete = constants.%F] { // CHECK:STDOUT: %T.patt: %pattern_type.033 = symbolic_binding_pattern T, 0 [concrete] // CHECK:STDOUT: %return.patt: %pattern_type.cb1 = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: %pattern_type.cb1 = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %T.ref: %I_where.type = name_ref T, %T.loc8_6.2 [symbolic = %T.loc8_6.1 (constants.%T.706)] // CHECK:STDOUT: %T.as_type: type = facet_access_type %T.ref [symbolic = %T.binding.as_type (constants.%T.binding.as_type)] // CHECK:STDOUT: %.loc8_39.1: type = converted %T.ref, %T.as_type [symbolic = %T.binding.as_type (constants.%T.binding.as_type)] // CHECK:STDOUT: %.loc8_39.2: %I.assoc_type.c03 = specific_constant @I1.%assoc0, @I.WithSelf(constants.%.Self.binding.as_type.8db, constants.%T.706) [symbolic_self = constants.%assoc0.fe4] // CHECK:STDOUT: %I1.ref.loc8_39: %I.assoc_type.c03 = name_ref I1, %.loc8_39.2 [symbolic_self = constants.%assoc0.fe4] // CHECK:STDOUT: %impl.elem0.loc8_39: type = impl_witness_access constants.%I.lookup_impl_witness.94d, element0 [concrete = constants.%empty_tuple.type] // CHECK:STDOUT: %.loc8_39.3: Core.Form = init_form %impl.elem0.loc8_39 [concrete = constants.%.262] // CHECK:STDOUT: %.loc8_19.1: type = splice_block %.loc8_19.2 [symbolic_self = constants.%I_where.type] { // CHECK:STDOUT: // CHECK:STDOUT: %I.ref: %I.type.609 = name_ref I, file.%I.decl [concrete = constants.%I.generic] // CHECK:STDOUT: %.Self.ref.loc8_12: %type = name_ref .Self, %.Self.1 [symbolic_self = constants.%.Self.c39] // CHECK:STDOUT: %.Self.as_type.loc8_17: type = facet_access_type %.Self.ref.loc8_12 [symbolic_self = constants.%.Self.binding.as_type.8db] // CHECK:STDOUT: %.loc8_17: type = converted %.Self.ref.loc8_12, %.Self.as_type.loc8_17 [symbolic_self = constants.%.Self.binding.as_type.8db] // CHECK:STDOUT: %I.type: type = facet_type <@I, @I(constants.%.Self.binding.as_type.8db)> [symbolic_self = constants.%I.type.bee] // CHECK:STDOUT: // CHECK:STDOUT: %.Self.ref.loc8_25: %I.type.bee = name_ref .Self, %.Self.2 [symbolic_self = constants.%.Self.dad] // CHECK:STDOUT: %.Self.as_type.loc8_25: type = facet_access_type %.Self.ref.loc8_25 [symbolic_self = constants.%.Self.binding.as_type.b0b] // CHECK:STDOUT: %.loc8_25.1: type = converted %.Self.ref.loc8_25, %.Self.as_type.loc8_25 [symbolic_self = constants.%.Self.binding.as_type.b0b] // CHECK:STDOUT: %.loc8_25.2: %I.assoc_type.c03 = specific_constant @I1.%assoc0, @I.WithSelf(constants.%.Self.binding.as_type.8db, constants.%.Self.dad) [symbolic_self = constants.%assoc0.fe4] // CHECK:STDOUT: %I1.ref.loc8_25: %I.assoc_type.c03 = name_ref I1, %.loc8_25.2 [symbolic_self = constants.%assoc0.fe4] // CHECK:STDOUT: %impl.elem0.loc8_25: type = impl_witness_access constants.%I.lookup_impl_witness.3fc, element0 [symbolic_self = constants.%impl.elem0] // CHECK:STDOUT: %.loc8_32.1: %empty_tuple.type = tuple_literal () [concrete = constants.%empty_tuple] // CHECK:STDOUT: %.loc8_32.2: type = converted %.loc8_32.1, constants.%empty_tuple.type [concrete = constants.%empty_tuple.type] // CHECK:STDOUT: %.loc8_19.2: type = where_expr %.Self.2 [symbolic_self = constants.%I_where.type] { // CHECK:STDOUT: requirement_base_facet_type constants.%I.type.bee // CHECK:STDOUT: requirement_rewrite %impl.elem0.loc8_25, %.loc8_32.2 // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: %T.loc8_6.2: %I_where.type = symbolic_binding T, 0 [symbolic = %T.loc8_6.1 (constants.%T.706)] // CHECK:STDOUT: %return.param: ref %empty_tuple.type = out_param call_param0 // CHECK:STDOUT: %return: ref %empty_tuple.type = return_slot %return.param // CHECK:STDOUT: } // CHECK:STDOUT: %G.decl: %G.type = fn_decl @G [concrete = constants.%G] { // CHECK:STDOUT: %T.patt: %pattern_type.033 = symbolic_binding_pattern T, 0 [concrete] // CHECK:STDOUT: %return.patt: %pattern_type.cb1 = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: %pattern_type.cb1 = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %T.ref: %I_where.type = name_ref T, %T.loc12_6.2 [symbolic = %T.loc12_6.1 (constants.%T.706)] // CHECK:STDOUT: %T.as_type: type = facet_access_type %T.ref [symbolic = %T.binding.as_type (constants.%T.binding.as_type)] // CHECK:STDOUT: %.loc12_47.1: type = converted %T.ref, %T.as_type [symbolic = %T.binding.as_type (constants.%T.binding.as_type)] // CHECK:STDOUT: %.loc12_47.2: %I.assoc_type.c03 = specific_constant @I1.%assoc0, @I.WithSelf(constants.%.Self.binding.as_type.8db, constants.%T.706) [symbolic_self = constants.%assoc0.fe4] // CHECK:STDOUT: %I1.ref.loc12_47: %I.assoc_type.c03 = name_ref I1, %.loc12_47.2 [symbolic_self = constants.%assoc0.fe4] // CHECK:STDOUT: %impl.elem0.loc12_47: type = impl_witness_access constants.%I.lookup_impl_witness.94d, element0 [concrete = constants.%empty_tuple.type] // CHECK:STDOUT: %.loc12_47.3: Core.Form = init_form %impl.elem0.loc12_47 [concrete = constants.%.262] // CHECK:STDOUT: %.loc12_27.1: type = splice_block %.loc12_27.2 [symbolic_self = constants.%I_where.type] { // CHECK:STDOUT: // CHECK:STDOUT: %I.ref: %I.type.609 = name_ref I, file.%I.decl [concrete = constants.%I.generic] // CHECK:STDOUT: %.Self.ref.loc12_12: %type = name_ref .Self, %.Self.1 [symbolic_self = constants.%.Self.c39] // CHECK:STDOUT: %.loc12_21: type = type_literal type [concrete = type] // CHECK:STDOUT: %.Self.as_type.loc12_18: type = facet_access_type %.Self.ref.loc12_12 [symbolic_self = constants.%.Self.binding.as_type.8db] // CHECK:STDOUT: %.loc12_18: type = converted %.Self.ref.loc12_12, %.Self.as_type.loc12_18 [symbolic_self = constants.%.Self.binding.as_type.8db] // CHECK:STDOUT: %I.type: type = facet_type <@I, @I(constants.%.Self.binding.as_type.8db)> [symbolic_self = constants.%I.type.bee] // CHECK:STDOUT: // CHECK:STDOUT: %.Self.ref.loc12_33: %I.type.bee = name_ref .Self, %.Self.2 [symbolic_self = constants.%.Self.dad] // CHECK:STDOUT: %.Self.as_type.loc12_33: type = facet_access_type %.Self.ref.loc12_33 [symbolic_self = constants.%.Self.binding.as_type.b0b] // CHECK:STDOUT: %.loc12_33.1: type = converted %.Self.ref.loc12_33, %.Self.as_type.loc12_33 [symbolic_self = constants.%.Self.binding.as_type.b0b] // CHECK:STDOUT: %.loc12_33.2: %I.assoc_type.c03 = specific_constant @I1.%assoc0, @I.WithSelf(constants.%.Self.binding.as_type.8db, constants.%.Self.dad) [symbolic_self = constants.%assoc0.fe4] // CHECK:STDOUT: %I1.ref.loc12_33: %I.assoc_type.c03 = name_ref I1, %.loc12_33.2 [symbolic_self = constants.%assoc0.fe4] // CHECK:STDOUT: %impl.elem0.loc12_33: type = impl_witness_access constants.%I.lookup_impl_witness.3fc, element0 [symbolic_self = constants.%impl.elem0] // CHECK:STDOUT: %.loc12_40.1: %empty_tuple.type = tuple_literal () [concrete = constants.%empty_tuple] // CHECK:STDOUT: %.loc12_40.2: type = converted %.loc12_40.1, constants.%empty_tuple.type [concrete = constants.%empty_tuple.type] // CHECK:STDOUT: %.loc12_27.2: type = where_expr %.Self.2 [symbolic_self = constants.%I_where.type] { // CHECK:STDOUT: requirement_base_facet_type constants.%I.type.bee // CHECK:STDOUT: requirement_rewrite %impl.elem0.loc12_33, %.loc12_40.2 // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: %T.loc12_6.2: %I_where.type = symbolic_binding T, 0 [symbolic = %T.loc12_6.1 (constants.%T.706)] // CHECK:STDOUT: %return.param: ref %empty_tuple.type = out_param call_param0 // CHECK:STDOUT: %return: ref %empty_tuple.type = return_slot %return.param // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic interface @I(%T.loc4_13.2: type) { // CHECK:STDOUT: %T.loc4_13.1: type = symbolic_binding T, 0 [symbolic = %T.loc4_13.1 (constants.%T.67d)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %I.type: type = facet_type <@I, @I(%T.loc4_13.1)> [symbolic = %I.type (constants.%I.type.1ab)] // CHECK:STDOUT: %Self.loc4_23.2: @I.%I.type (%I.type.1ab) = symbolic_binding Self, 1 [symbolic = %Self.loc4_23.2 (constants.%Self.fdb)] // CHECK:STDOUT: // CHECK:STDOUT: interface { // CHECK:STDOUT: %Self.loc4_23.1: @I.%I.type (%I.type.1ab) = symbolic_binding Self, 1 [symbolic = %Self.loc4_23.2 (constants.%Self.fdb)] // CHECK:STDOUT: %I.WithSelf.decl = interface_with_self_decl @I [concrete] // CHECK:STDOUT: // CHECK:STDOUT: !with Self: // CHECK:STDOUT: %I1: type = assoc_const_decl @I1 [concrete] { // CHECK:STDOUT: %assoc0: @I.WithSelf.%I.assoc_type (%I.assoc_type.76c) = assoc_entity element0, @I.WithSelf.%I1 [symbolic = @I.WithSelf.%assoc0 (constants.%assoc0.99e)] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = %Self.loc4_23.1 // CHECK:STDOUT: .I1 = @I1.%assoc0 // CHECK:STDOUT: witness = (@I.WithSelf.%I1) // CHECK:STDOUT: // CHECK:STDOUT: !requires: // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic fn @F(%T.loc8_6.2: %I_where.type) { // CHECK:STDOUT: %T.loc8_6.1: %I_where.type = symbolic_binding T, 0 [symbolic = %T.loc8_6.1 (constants.%T.706)] // CHECK:STDOUT: %T.binding.as_type: type = symbolic_binding_type T, 0, %T.loc8_6.1 [symbolic = %T.binding.as_type (constants.%T.binding.as_type)] // CHECK:STDOUT: %I.lookup_impl_witness: = lookup_impl_witness %T.loc8_6.1, @I, @I(constants.%.Self.binding.as_type.8db) [symbolic = %I.lookup_impl_witness (constants.%I.lookup_impl_witness.94d)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: // CHECK:STDOUT: fn() -> out %return.param: %empty_tuple.type { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %.loc9_11.1: %empty_tuple.type = tuple_literal () [concrete = constants.%empty_tuple] // CHECK:STDOUT: %.loc9_11.2: init %empty_tuple.type = tuple_init () [concrete = constants.%empty_tuple] // CHECK:STDOUT: %.loc9_12: init %empty_tuple.type = converted %.loc9_11.1, %.loc9_11.2 [concrete = constants.%empty_tuple] // CHECK:STDOUT: return %.loc9_12 // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic fn @G(%T.loc12_6.2: %I_where.type) { // CHECK:STDOUT: %T.loc12_6.1: %I_where.type = symbolic_binding T, 0 [symbolic = %T.loc12_6.1 (constants.%T.706)] // CHECK:STDOUT: %T.binding.as_type: type = symbolic_binding_type T, 0, %T.loc12_6.1 [symbolic = %T.binding.as_type (constants.%T.binding.as_type)] // CHECK:STDOUT: %I.lookup_impl_witness: = lookup_impl_witness %T.loc12_6.1, @I, @I(constants.%.Self.binding.as_type.8db) [symbolic = %I.lookup_impl_witness (constants.%I.lookup_impl_witness.94d)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: // CHECK:STDOUT: fn() -> out %return.param: %empty_tuple.type { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %.loc13_11.1: %empty_tuple.type = tuple_literal () [concrete = constants.%empty_tuple] // CHECK:STDOUT: %.loc13_11.2: init %empty_tuple.type = tuple_init () [concrete = constants.%empty_tuple] // CHECK:STDOUT: %.loc13_12: init %empty_tuple.type = converted %.loc13_11.1, %.loc13_11.2 [concrete = constants.%empty_tuple] // CHECK:STDOUT: return %.loc13_12 // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @I(constants.%T.67d) { // CHECK:STDOUT: %T.loc4_13.1 => constants.%T.67d // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @I.WithSelf(constants.%T.67d, constants.%Self.fdb) {} // CHECK:STDOUT: // CHECK:STDOUT: specific @I(constants.%.Self.binding.as_type.8db) { // CHECK:STDOUT: %T.loc4_13.1 => constants.%.Self.binding.as_type.8db // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %I.type => constants.%I.type.bee // CHECK:STDOUT: %Self.loc4_23.2 => constants.%Self.fa8 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @I.WithSelf(constants.%.Self.binding.as_type.8db, constants.%Self.fdb) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: %T => constants.%.Self.binding.as_type.8db // CHECK:STDOUT: %I.assoc_type => constants.%I.assoc_type.c03 // CHECK:STDOUT: %assoc0 => constants.%assoc0.fe4 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @I.WithSelf(constants.%.Self.binding.as_type.8db, constants.%.Self.dad) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: %T => constants.%.Self.binding.as_type.8db // CHECK:STDOUT: %I.assoc_type => constants.%I.assoc_type.c03 // CHECK:STDOUT: %assoc0 => constants.%assoc0.fe4 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @I.WithSelf(constants.%.Self.binding.as_type.8db, constants.%T.706) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: %T => constants.%.Self.binding.as_type.8db // CHECK:STDOUT: %I.assoc_type => constants.%I.assoc_type.c03 // CHECK:STDOUT: %assoc0 => constants.%assoc0.fe4 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @I.WithSelf(constants.%.Self.binding.as_type.8db, constants.%I.facet) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: %T => constants.%.Self.binding.as_type.8db // CHECK:STDOUT: %I.assoc_type => constants.%I.assoc_type.c03 // CHECK:STDOUT: %assoc0 => constants.%assoc0.fe4 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @F(constants.%T.706) { // CHECK:STDOUT: %T.loc8_6.1 => constants.%T.706 // CHECK:STDOUT: %T.binding.as_type => constants.%T.binding.as_type // CHECK:STDOUT: %I.lookup_impl_witness => constants.%I.lookup_impl_witness.94d // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @G(constants.%T.706) { // CHECK:STDOUT: %T.loc12_6.1 => constants.%T.706 // CHECK:STDOUT: %T.binding.as_type => constants.%T.binding.as_type // CHECK:STDOUT: %I.lookup_impl_witness => constants.%I.lookup_impl_witness.94d // CHECK:STDOUT: } // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/facet/require_constrains_self.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/convert.carbon // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/facet/require_constrains_self.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/facet/require_constrains_self.carbon // --- fail_self_doesnt_meet_constraint.carbon library "[[@TEST_NAME]]"; interface Y2 {} interface Y(T:! Y2) {} // TODO: The error should be that Self does not implement Y2, not that it can't // be identified. We should allow identifying `Self`. // // CHECK:STDERR: fail_self_doesnt_meet_constraint.carbon:[[@LINE+17]]:37: error: facet type of value `Self` can not be identified [ImplLookupInUnidentifiedFacetTypeOfQuerySelf] // CHECK:STDERR: constraint W { extend require impls Y(Self); } // CHECK:STDERR: ^~~~~~~ // CHECK:STDERR: fail_self_doesnt_meet_constraint.carbon:[[@LINE+14]]:1: note: constraint is currently being defined [NamedConstraintIncompleteWithinDefinition] // CHECK:STDERR: constraint W { extend require impls Y(Self); } // CHECK:STDERR: ^~~~~~~~~~~~~~ // CHECK:STDERR: fail_self_doesnt_meet_constraint.carbon:[[@LINE-10]]:13: note: initializing generic parameter `T` declared here [InitializingGenericParam] // CHECK:STDERR: interface Y(T:! Y2) {} // CHECK:STDERR: ^ // CHECK:STDERR: // CHECK:STDERR: fail_self_doesnt_meet_constraint.carbon:[[@LINE+7]]:37: error: cannot convert type `Self` that implements `W` into type implementing `Y2` [ConversionFailureFacetToFacet] // CHECK:STDERR: constraint W { extend require impls Y(Self); } // CHECK:STDERR: ^~~~~~~ // CHECK:STDERR: fail_self_doesnt_meet_constraint.carbon:[[@LINE-17]]:13: note: initializing generic parameter `T` declared here [InitializingGenericParam] // CHECK:STDERR: interface Y(T:! Y2) {} // CHECK:STDERR: ^ // CHECK:STDERR: constraint W { extend require impls Y(Self); } // --- fail_todo_self_meets_constraint.carbon library "[[@TEST_NAME]]"; interface Y2 {} interface Y(T:! Y2) {} constraint W { // TODO: This should allow the next line to work, as Self should be known to impl Y2. require impls Y2; // CHECK:STDERR: fail_todo_self_meets_constraint.carbon:[[@LINE+17]]:24: error: facet type of value `Self` can not be identified [ImplLookupInUnidentifiedFacetTypeOfQuerySelf] // CHECK:STDERR: extend require impls Y(Self); // CHECK:STDERR: ^~~~~~~ // CHECK:STDERR: fail_todo_self_meets_constraint.carbon:[[@LINE-6]]:1: note: constraint is currently being defined [NamedConstraintIncompleteWithinDefinition] // CHECK:STDERR: constraint W { // CHECK:STDERR: ^~~~~~~~~~~~~~ // CHECK:STDERR: fail_todo_self_meets_constraint.carbon:[[@LINE-10]]:13: note: initializing generic parameter `T` declared here [InitializingGenericParam] // CHECK:STDERR: interface Y(T:! Y2) {} // CHECK:STDERR: ^ // CHECK:STDERR: // CHECK:STDERR: fail_todo_self_meets_constraint.carbon:[[@LINE+7]]:24: error: cannot convert type `Self` that implements `W` into type implementing `Y2` [ConversionFailureFacetToFacet] // CHECK:STDERR: extend require impls Y(Self); // CHECK:STDERR: ^~~~~~~ // CHECK:STDERR: fail_todo_self_meets_constraint.carbon:[[@LINE-17]]:13: note: initializing generic parameter `T` declared here [InitializingGenericParam] // CHECK:STDERR: interface Y(T:! Y2) {} // CHECK:STDERR: ^ // CHECK:STDERR: extend require impls Y(Self); } ================================================ FILE: toolchain/check/testdata/facet/require_import.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/none.carbon // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/facet/require_import.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/facet/require_import.carbon // --- a.carbon library "[[@TEST_NAME]]"; interface Z(T:! type) { fn F(); } interface Y { extend require impls Z(Self); } constraint X { extend require impls Z(Self); } // --- a.impl.carbon //@include-in-dumps impl library "[[@TEST_NAME]]"; fn F(A:! X, unused B:! Y) { A.F(); } // --- fail_todo_symbolic_facet_interface_requires_interface.carbon library "[[@TEST_NAME]]"; import library "a"; fn F(B:! Y) { // TODO: We find the name F, but then fail to do impl lookup for `F` in `B:! // Y` since the identified facet type doesn't include Z. We need to be able to // find a symbolic facet from the require decl in Y during impl lookup. // // CHECK:STDERR: fail_todo_symbolic_facet_interface_requires_interface.carbon:[[@LINE+4]]:3: error: cannot access member of interface `Z(B)` in type `B` that does not implement that interface [MissingImplInMemberAccess] // CHECK:STDERR: B.F(); // CHECK:STDERR: ^~~ // CHECK:STDERR: B.F(); } // CHECK:STDOUT: --- a.impl.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %type: type = facet_type [concrete] // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self] // CHECK:STDOUT: %X.type: type = facet_type <@X> [concrete] // CHECK:STDOUT: %Self.f45: %X.type = symbolic_binding Self, 0 [symbolic] // CHECK:STDOUT: %Self.binding.as_type.f60: type = symbolic_binding_type Self, 0, %Self.f45 [symbolic] // CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] // CHECK:STDOUT: %T: type = symbolic_binding T, 0 [symbolic] // CHECK:STDOUT: %Z.type.0ed: type = facet_type <@Z, @Z(%T)> [symbolic] // CHECK:STDOUT: %Self.984: %Z.type.0ed = symbolic_binding Self, 1 [symbolic] // CHECK:STDOUT: %Z.assoc_type.0bf: type = assoc_entity_type @Z, @Z(%T) [symbolic] // CHECK:STDOUT: %Z.WithSelf.F.type.091: type = fn_type @Z.WithSelf.F, @Z.WithSelf(%T, %Self.984) [symbolic] // CHECK:STDOUT: %Z.WithSelf.F.073: %Z.WithSelf.F.type.091 = struct_value () [symbolic] // CHECK:STDOUT: %Z.type.9f3244.1: type = facet_type <@Z, @Z(%Self.binding.as_type.f60)> [symbolic] // CHECK:STDOUT: %Self.fcd60c.1: %Z.type.9f3244.1 = symbolic_binding Self, 1 [symbolic] // CHECK:STDOUT: %require_complete.18ce0f.1: = require_complete_type %Z.type.9f3244.1 [symbolic] // CHECK:STDOUT: %pattern_type.9a5: type = pattern_type %X.type [concrete] // CHECK:STDOUT: %A: %X.type = symbolic_binding A, 0 [symbolic] // CHECK:STDOUT: %Y.type: type = facet_type <@Y> [concrete] // CHECK:STDOUT: %Self.d4d: %Y.type = symbolic_binding Self, 0 [symbolic] // CHECK:STDOUT: %Self.binding.as_type.63e: type = symbolic_binding_type Self, 0, %Self.d4d [symbolic] // CHECK:STDOUT: %Z.type.d16: type = facet_type <@Z, @Z(%Self.binding.as_type.63e)> [symbolic] // CHECK:STDOUT: %Self.890: %Z.type.d16 = symbolic_binding Self, 1 [symbolic] // CHECK:STDOUT: %pattern_type.95b: type = pattern_type %Y.type [concrete] // CHECK:STDOUT: %B: %Y.type = symbolic_binding B, 1 [symbolic] // CHECK:STDOUT: %F.type: type = fn_type @F [concrete] // CHECK:STDOUT: %F: %F.type = struct_value () [concrete] // CHECK:STDOUT: %A.binding.as_type: type = symbolic_binding_type A, 0, %A [symbolic] // CHECK:STDOUT: %Z.type.9f3244.2: type = facet_type <@Z, @Z(%A.binding.as_type)> [symbolic] // CHECK:STDOUT: %require_complete.18ce0f.2: = require_complete_type %Z.type.9f3244.2 [symbolic] // CHECK:STDOUT: %Self.fcd60c.2: %Z.type.9f3244.2 = symbolic_binding Self, 1 [symbolic] // CHECK:STDOUT: %Z.WithSelf.F.type.6cc: type = fn_type @Z.WithSelf.F, @Z.WithSelf(%A.binding.as_type, %Self.984) [symbolic] // CHECK:STDOUT: %Z.WithSelf.F.926: %Z.WithSelf.F.type.6cc = struct_value () [symbolic] // CHECK:STDOUT: %Z.assoc_type.449: type = assoc_entity_type @Z, @Z(%A.binding.as_type) [symbolic] // CHECK:STDOUT: %assoc0.9a4: %Z.assoc_type.449 = assoc_entity element0, imports.%Main.import_ref.7b4 [symbolic] // CHECK:STDOUT: %Z.WithSelf.F.type.048: type = fn_type @Z.WithSelf.F, @Z.WithSelf(%A.binding.as_type, %A) [symbolic] // CHECK:STDOUT: %Z.WithSelf.F.aaa: %Z.WithSelf.F.type.048 = struct_value () [symbolic] // CHECK:STDOUT: %assoc0.20d: %Z.assoc_type.0bf = assoc_entity element0, imports.%Main.import_ref.47b [symbolic] // CHECK:STDOUT: %Z.lookup_impl_witness: = lookup_impl_witness %A, @Z, @Z(%A.binding.as_type) [symbolic] // CHECK:STDOUT: %Z.facet: %Z.type.9f3244.2 = facet_value %A.binding.as_type, (%Z.lookup_impl_witness) [symbolic] // CHECK:STDOUT: %Z.WithSelf.F.type.501: type = fn_type @Z.WithSelf.F, @Z.WithSelf(%A.binding.as_type, %Z.facet) [symbolic] // CHECK:STDOUT: %Z.WithSelf.F.cf6: %Z.WithSelf.F.type.501 = struct_value () [symbolic] // CHECK:STDOUT: %.d31: type = fn_type_with_self_type %Z.WithSelf.F.type.501, %Z.facet [symbolic] // CHECK:STDOUT: %impl.elem0: %.d31 = impl_witness_access %Z.lookup_impl_witness, element0 [symbolic] // CHECK:STDOUT: %specific_impl_fn: = specific_impl_function %impl.elem0, @Z.WithSelf.F(%A.binding.as_type, %Z.facet) [symbolic] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Main.Z = import_ref Main//a, Z, unloaded // CHECK:STDOUT: %Main.Y: type = import_ref Main//a, Y, loaded [concrete = constants.%Y.type] // CHECK:STDOUT: %Main.X: type = import_ref Main//a, X, loaded [concrete = constants.%X.type] // CHECK:STDOUT: %Main.import_ref.13d: type = import_ref Main//a, loc12_31, loaded [symbolic = @X.WithSelf.%Z.type (constants.%Z.type.9f3244.1)] // CHECK:STDOUT: %Main.import_ref.f56: @Z.WithSelf.%Z.assoc_type (%Z.assoc_type.0bf) = import_ref Main//a, loc4_9, loaded [symbolic = @Z.WithSelf.%assoc0 (constants.%assoc0.20d)] // CHECK:STDOUT: %Main.F = import_ref Main//a, F, unloaded // CHECK:STDOUT: %Main.import_ref.7b4: @Z.WithSelf.%Z.WithSelf.F.type (%Z.WithSelf.F.type.091) = import_ref Main//a, loc4_9, loaded [symbolic = @Z.WithSelf.%Z.WithSelf.F (constants.%Z.WithSelf.F.073)] // CHECK:STDOUT: %Main.import_ref.b3bc94.2: type = import_ref Main//a, loc3_13, loaded [symbolic = @Z.%T (constants.%T)] // CHECK:STDOUT: %Main.import_ref.1dc578.2: @Z.%Z.type (%Z.type.0ed) = import_ref Main//a, loc3_23, loaded [symbolic = @Z.%Self (constants.%Self.984)] // CHECK:STDOUT: %Main.import_ref.145 = import_ref Main//a, loc3_23, unloaded // CHECK:STDOUT: %Main.import_ref.b3bc94.3: type = import_ref Main//a, loc3_13, loaded [symbolic = @Z.%T (constants.%T)] // CHECK:STDOUT: %Main.import_ref.67f: type = import_ref Main//a, loc12_18, loaded [symbolic = @X.WithSelf.Self.binding.as_type.impls.Z.type.require.%Self.binding.as_type (constants.%Self.binding.as_type.f60)] // CHECK:STDOUT: %Main.import_ref.8d6: type = import_ref Main//a, loc12_30, loaded [symbolic = @X.WithSelf.Self.binding.as_type.impls.Z.type.require.%Z.type (constants.%Z.type.9f3244.1)] // CHECK:STDOUT: %Main.import_ref.e33cd1.2: %X.type = import_ref Main//a, loc11_14, loaded [symbolic = constants.%Self.f45] // CHECK:STDOUT: %Main.import_ref.65b = import_ref Main//a, loc11_14, unloaded // CHECK:STDOUT: %Main.import_ref.b51 = import_ref Main//a, loc8_31, unloaded // CHECK:STDOUT: %Main.import_ref.7bb: type = import_ref Main//a, loc8_18, loaded [symbolic = @Y.WithSelf.Self.binding.as_type.impls.Z.type.require.%Self.binding.as_type (constants.%Self.binding.as_type.63e)] // CHECK:STDOUT: %Main.import_ref.5ab: type = import_ref Main//a, loc8_30, loaded [symbolic = @Y.WithSelf.Self.binding.as_type.impls.Z.type.require.%Z.type (constants.%Z.type.d16)] // CHECK:STDOUT: %Main.import_ref.8c73c0.2: %Y.type = import_ref Main//a, loc7_13, loaded [symbolic = constants.%Self.d4d] // CHECK:STDOUT: %Main.import_ref.3e2 = import_ref Main//a, loc7_13, unloaded // CHECK:STDOUT: %Main.import_ref.47b = import_ref Main//a, loc4_9, unloaded // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Z = imports.%Main.Z // CHECK:STDOUT: .Y = imports.%Main.Y // CHECK:STDOUT: .X = imports.%Main.X // CHECK:STDOUT: .F = %F.decl // CHECK:STDOUT: } // CHECK:STDOUT: %default.import.loc2_17.1 = import // CHECK:STDOUT: %default.import.loc2_17.2 = import // CHECK:STDOUT: %F.decl: %F.type = fn_decl @F [concrete = constants.%F] { // CHECK:STDOUT: %A.patt: %pattern_type.9a5 = symbolic_binding_pattern A, 0 [concrete] // CHECK:STDOUT: %B.patt: %pattern_type.95b = symbolic_binding_pattern B, 1 [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %.loc4_10: type = splice_block %X.ref [concrete = constants.%X.type] { // CHECK:STDOUT: %.Self.2: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %X.ref: type = name_ref X, imports.%Main.X [concrete = constants.%X.type] // CHECK:STDOUT: } // CHECK:STDOUT: %A.loc4_6.2: %X.type = symbolic_binding A, 0 [symbolic = %A.loc4_6.1 (constants.%A)] // CHECK:STDOUT: %.loc4_24: type = splice_block %Y.ref [concrete = constants.%Y.type] { // CHECK:STDOUT: %.Self.1: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %Y.ref: type = name_ref Y, imports.%Main.Y [concrete = constants.%Y.type] // CHECK:STDOUT: } // CHECK:STDOUT: %B.loc4_20.2: %Y.type = symbolic_binding B, 1 [symbolic = %B.loc4_20.1 (constants.%B)] // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic interface @Z(imports.%Main.import_ref.b3bc94.3: type) [from "a.carbon"] { // CHECK:STDOUT: %T: type = symbolic_binding T, 0 [symbolic = %T (constants.%T)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %Z.type: type = facet_type <@Z, @Z(%T)> [symbolic = %Z.type (constants.%Z.type.0ed)] // CHECK:STDOUT: %Self: @Z.%Z.type (%Z.type.0ed) = symbolic_binding Self, 1 [symbolic = %Self (constants.%Self.984)] // CHECK:STDOUT: // CHECK:STDOUT: interface { // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = imports.%Main.import_ref.145 // CHECK:STDOUT: .F = imports.%Main.import_ref.f56 // CHECK:STDOUT: witness = (imports.%Main.F) // CHECK:STDOUT: // CHECK:STDOUT: !requires: // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: interface @Y [from "a.carbon"] { // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = imports.%Main.import_ref.3e2 // CHECK:STDOUT: extend imports.%Main.import_ref.b51 // CHECK:STDOUT: witness = () // CHECK:STDOUT: // CHECK:STDOUT: !requires: // CHECK:STDOUT: @Y.WithSelf.Self.binding.as_type.impls.Z.type.require { // CHECK:STDOUT: require imports.%Main.import_ref.7bb impls imports.%Main.import_ref.5ab // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: constraint @X [from "a.carbon"] { // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = imports.%Main.import_ref.65b // CHECK:STDOUT: .F = // CHECK:STDOUT: extend imports.%Main.import_ref.13d // CHECK:STDOUT: // CHECK:STDOUT: !requires: // CHECK:STDOUT: @X.WithSelf.Self.binding.as_type.impls.Z.type.require { // CHECK:STDOUT: require imports.%Main.import_ref.67f impls imports.%Main.import_ref.8d6 // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic require @X.WithSelf.Self.binding.as_type.impls.Z.type.require(imports.%Main.import_ref.e33cd1.2: %X.type) [from "a.carbon"] { // CHECK:STDOUT: %Self: %X.type = symbolic_binding Self, 0 [symbolic = %Self (constants.%Self.f45)] // CHECK:STDOUT: %Self.binding.as_type: type = symbolic_binding_type Self, 0, %Self [symbolic = %Self.binding.as_type (constants.%Self.binding.as_type.f60)] // CHECK:STDOUT: %Z.type: type = facet_type <@Z, @Z(%Self.binding.as_type)> [symbolic = %Z.type (constants.%Z.type.9f3244.1)] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic require @Y.WithSelf.Self.binding.as_type.impls.Z.type.require(imports.%Main.import_ref.8c73c0.2: %Y.type) [from "a.carbon"] { // CHECK:STDOUT: %Self: %Y.type = symbolic_binding Self, 0 [symbolic = %Self (constants.%Self.d4d)] // CHECK:STDOUT: %Self.binding.as_type: type = symbolic_binding_type Self, 0, %Self [symbolic = %Self.binding.as_type (constants.%Self.binding.as_type.63e)] // CHECK:STDOUT: %Z.type: type = facet_type <@Z, @Z(%Self.binding.as_type)> [symbolic = %Z.type (constants.%Z.type.d16)] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic fn @Z.WithSelf.F(imports.%Main.import_ref.b3bc94.2: type, imports.%Main.import_ref.1dc578.2: @Z.%Z.type (%Z.type.0ed)) [from "a.carbon"] { // CHECK:STDOUT: fn; // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic fn @F(%A.loc4_6.2: %X.type, %B.loc4_20.2: %Y.type) { // CHECK:STDOUT: %A.loc4_6.1: %X.type = symbolic_binding A, 0 [symbolic = %A.loc4_6.1 (constants.%A)] // CHECK:STDOUT: %B.loc4_20.1: %Y.type = symbolic_binding B, 1 [symbolic = %B.loc4_20.1 (constants.%B)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %A.binding.as_type: type = symbolic_binding_type A, 0, %A.loc4_6.1 [symbolic = %A.binding.as_type (constants.%A.binding.as_type)] // CHECK:STDOUT: %Z.type: type = facet_type <@Z, @Z(%A.binding.as_type)> [symbolic = %Z.type (constants.%Z.type.9f3244.2)] // CHECK:STDOUT: %require_complete: = require_complete_type %Z.type [symbolic = %require_complete (constants.%require_complete.18ce0f.2)] // CHECK:STDOUT: %Z.assoc_type: type = assoc_entity_type @Z, @Z(%A.binding.as_type) [symbolic = %Z.assoc_type (constants.%Z.assoc_type.449)] // CHECK:STDOUT: %assoc0: @F.%Z.assoc_type (%Z.assoc_type.449) = assoc_entity element0, imports.%Main.import_ref.7b4 [symbolic = %assoc0 (constants.%assoc0.9a4)] // CHECK:STDOUT: %Z.lookup_impl_witness: = lookup_impl_witness %A.loc4_6.1, @Z, @Z(%A.binding.as_type) [symbolic = %Z.lookup_impl_witness (constants.%Z.lookup_impl_witness)] // CHECK:STDOUT: %Z.facet: @F.%Z.type (%Z.type.9f3244.2) = facet_value %A.binding.as_type, (%Z.lookup_impl_witness) [symbolic = %Z.facet (constants.%Z.facet)] // CHECK:STDOUT: %Z.WithSelf.F.type: type = fn_type @Z.WithSelf.F, @Z.WithSelf(%A.binding.as_type, %Z.facet) [symbolic = %Z.WithSelf.F.type (constants.%Z.WithSelf.F.type.501)] // CHECK:STDOUT: %.loc5_4.3: type = fn_type_with_self_type %Z.WithSelf.F.type, %Z.facet [symbolic = %.loc5_4.3 (constants.%.d31)] // CHECK:STDOUT: %impl.elem0.loc5_4.2: @F.%.loc5_4.3 (%.d31) = impl_witness_access %Z.lookup_impl_witness, element0 [symbolic = %impl.elem0.loc5_4.2 (constants.%impl.elem0)] // CHECK:STDOUT: %specific_impl_fn.loc5_4.2: = specific_impl_function %impl.elem0.loc5_4.2, @Z.WithSelf.F(%A.binding.as_type, %Z.facet) [symbolic = %specific_impl_fn.loc5_4.2 (constants.%specific_impl_fn)] // CHECK:STDOUT: // CHECK:STDOUT: fn() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %A.ref: %X.type = name_ref A, %A.loc4_6.2 [symbolic = %A.loc4_6.1 (constants.%A)] // CHECK:STDOUT: %A.as_type: type = facet_access_type %A.ref [symbolic = %A.binding.as_type (constants.%A.binding.as_type)] // CHECK:STDOUT: %.loc5_4.1: type = converted %A.ref, %A.as_type [symbolic = %A.binding.as_type (constants.%A.binding.as_type)] // CHECK:STDOUT: %.loc5_4.2: @F.%Z.assoc_type (%Z.assoc_type.449) = specific_constant imports.%Main.import_ref.f56, @Z.WithSelf(constants.%A.binding.as_type, constants.%A) [symbolic = %assoc0 (constants.%assoc0.9a4)] // CHECK:STDOUT: %F.ref: @F.%Z.assoc_type (%Z.assoc_type.449) = name_ref F, %.loc5_4.2 [symbolic = %assoc0 (constants.%assoc0.9a4)] // CHECK:STDOUT: %impl.elem0.loc5_4.1: @F.%.loc5_4.3 (%.d31) = impl_witness_access constants.%Z.lookup_impl_witness, element0 [symbolic = %impl.elem0.loc5_4.2 (constants.%impl.elem0)] // CHECK:STDOUT: %specific_impl_fn.loc5_4.1: = specific_impl_function %impl.elem0.loc5_4.1, @Z.WithSelf.F(constants.%A.binding.as_type, constants.%Z.facet) [symbolic = %specific_impl_fn.loc5_4.2 (constants.%specific_impl_fn)] // CHECK:STDOUT: %Z.WithSelf.F.call: init %empty_tuple.type = call %specific_impl_fn.loc5_4.1() // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @Z(constants.%T) { // CHECK:STDOUT: %T => constants.%T // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @Z.WithSelf(constants.%T, constants.%Self.984) {} // CHECK:STDOUT: // CHECK:STDOUT: specific @Z.WithSelf.F(constants.%T, constants.%Self.984) {} // CHECK:STDOUT: // CHECK:STDOUT: specific @Z(constants.%Self.binding.as_type.f60) { // CHECK:STDOUT: %T => constants.%Self.binding.as_type.f60 // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %Z.type => constants.%Z.type.9f3244.1 // CHECK:STDOUT: %Self => constants.%Self.fcd60c.1 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @X.WithSelf(constants.%Self.f45) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: %Self => constants.%Self.f45 // CHECK:STDOUT: %Self.binding.as_type => constants.%Self.binding.as_type.f60 // CHECK:STDOUT: %Z.type => constants.%Z.type.9f3244.1 // CHECK:STDOUT: %require_complete => constants.%require_complete.18ce0f.1 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @X.WithSelf.Self.binding.as_type.impls.Z.type.require(constants.%Self.f45) { // CHECK:STDOUT: %Self => constants.%Self.f45 // CHECK:STDOUT: %Self.binding.as_type => constants.%Self.binding.as_type.f60 // CHECK:STDOUT: %Z.type => constants.%Z.type.9f3244.1 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @Z(constants.%Self.binding.as_type.63e) { // CHECK:STDOUT: %T => constants.%Self.binding.as_type.63e // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %Z.type => constants.%Z.type.d16 // CHECK:STDOUT: %Self => constants.%Self.890 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @Y.WithSelf(constants.%Self.d4d) {} // CHECK:STDOUT: // CHECK:STDOUT: specific @Y.WithSelf.Self.binding.as_type.impls.Z.type.require(constants.%Self.d4d) { // CHECK:STDOUT: %Self => constants.%Self.d4d // CHECK:STDOUT: %Self.binding.as_type => constants.%Self.binding.as_type.63e // CHECK:STDOUT: %Z.type => constants.%Z.type.d16 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @F(constants.%A, constants.%B) { // CHECK:STDOUT: %A.loc4_6.1 => constants.%A // CHECK:STDOUT: %B.loc4_20.1 => constants.%B // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @X.WithSelf(constants.%A) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: %Self => constants.%A // CHECK:STDOUT: %Self.binding.as_type => constants.%A.binding.as_type // CHECK:STDOUT: %Z.type => constants.%Z.type.9f3244.2 // CHECK:STDOUT: %require_complete => constants.%require_complete.18ce0f.2 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @Z(constants.%A.binding.as_type) { // CHECK:STDOUT: %T => constants.%A.binding.as_type // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %Z.type => constants.%Z.type.9f3244.2 // CHECK:STDOUT: %Self => constants.%Self.fcd60c.2 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @Z.WithSelf(constants.%A.binding.as_type, constants.%Self.984) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: %T => constants.%A.binding.as_type // CHECK:STDOUT: %Z.type => constants.%Z.type.9f3244.2 // CHECK:STDOUT: %Self => constants.%Self.984 // CHECK:STDOUT: %Z.WithSelf.F.type => constants.%Z.WithSelf.F.type.6cc // CHECK:STDOUT: %Z.WithSelf.F => constants.%Z.WithSelf.F.926 // CHECK:STDOUT: %Z.assoc_type => constants.%Z.assoc_type.449 // CHECK:STDOUT: %assoc0 => constants.%assoc0.9a4 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @Z.WithSelf(constants.%A.binding.as_type, constants.%A) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: %T => constants.%A.binding.as_type // CHECK:STDOUT: %Z.type => constants.%Z.type.9f3244.2 // CHECK:STDOUT: %Self => constants.%A // CHECK:STDOUT: %Z.WithSelf.F.type => constants.%Z.WithSelf.F.type.048 // CHECK:STDOUT: %Z.WithSelf.F => constants.%Z.WithSelf.F.aaa // CHECK:STDOUT: %Z.assoc_type => constants.%Z.assoc_type.449 // CHECK:STDOUT: %assoc0 => constants.%assoc0.9a4 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @X.WithSelf.Self.binding.as_type.impls.Z.type.require(constants.%A) { // CHECK:STDOUT: %Self => constants.%A // CHECK:STDOUT: %Self.binding.as_type => constants.%A.binding.as_type // CHECK:STDOUT: %Z.type => constants.%Z.type.9f3244.2 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @Z.WithSelf(constants.%A.binding.as_type, constants.%Z.facet) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: %T => constants.%A.binding.as_type // CHECK:STDOUT: %Z.type => constants.%Z.type.9f3244.2 // CHECK:STDOUT: %Self => constants.%Z.facet // CHECK:STDOUT: %Z.WithSelf.F.type => constants.%Z.WithSelf.F.type.501 // CHECK:STDOUT: %Z.WithSelf.F => constants.%Z.WithSelf.F.cf6 // CHECK:STDOUT: %Z.assoc_type => constants.%Z.assoc_type.449 // CHECK:STDOUT: %assoc0 => constants.%assoc0.9a4 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @Z.WithSelf.F(constants.%A.binding.as_type, constants.%Z.facet) {} // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/facet/require_invalid.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/none.carbon // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/facet/require_invalid.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/facet/require_invalid.carbon // --- fail_require_outside_scope.carbon library "[[@TEST_NAME]]"; interface Y {} // CHECK:STDERR: fail_require_outside_scope.carbon:[[@LINE+4]]:1: error: `require` can only be used in an `interface` or `constraint` [RequireInWrongScope] // CHECK:STDERR: require impls Y; // CHECK:STDERR: ^~~~~~~ // CHECK:STDERR: require impls Y; // --- fail_require_in_class.carbon library "[[@TEST_NAME]]"; interface Y {} class C { // CHECK:STDERR: fail_require_in_class.carbon:[[@LINE+4]]:3: error: `require` can only be used in an `interface` or `constraint` [RequireInWrongScope] // CHECK:STDERR: require impls Y; // CHECK:STDERR: ^~~~~~~ // CHECK:STDERR: require impls Y; } // --- fail_require_in_fn.carbon library "[[@TEST_NAME]]"; interface Y {} fn F() { // require is not allowed outside of `interface` or `constraint`. // // CHECK:STDERR: fail_require_in_fn.carbon:[[@LINE+8]]:3: error: expected expression [ExpectedExpr] // CHECK:STDERR: require impls Y; // CHECK:STDERR: ^~~~~~~ // CHECK:STDERR: // CHECK:STDERR: fail_require_in_fn.carbon:[[@LINE+4]]:3: error: semantics TODO: `handle invalid parse trees in `check`` [SemanticsTodo] // CHECK:STDERR: require impls Y; // CHECK:STDERR: ^~~~~~~ // CHECK:STDERR: require impls Y; } // --- fail_require_in_nested_class.carbon library "[[@TEST_NAME]]"; interface Y {} interface Z { // TODO: Add `default` modifier. fn F() { class C { // CHECK:STDERR: fail_require_in_nested_class.carbon:[[@LINE+4]]:7: error: `require` can only be used in an `interface` or `constraint` [RequireInWrongScope] // CHECK:STDERR: require impls Y; // CHECK:STDERR: ^~~~~~~ // CHECK:STDERR: require impls Y; } } } // --- fail_require_in_nested_fn.carbon library "[[@TEST_NAME]]"; interface Y {} interface Z { // TODO: Add `default` modifier. fn F() { // require is not allowed outside of `interface` or `constraint`. // // CHECK:STDERR: fail_require_in_nested_fn.carbon:[[@LINE+8]]:5: error: expected expression [ExpectedExpr] // CHECK:STDERR: require impls Y; // CHECK:STDERR: ^~~~~~~ // CHECK:STDERR: // CHECK:STDERR: fail_require_in_nested_fn.carbon:[[@LINE+4]]:5: error: semantics TODO: `handle invalid parse trees in `check`` [SemanticsTodo] // CHECK:STDERR: require impls Y; // CHECK:STDERR: ^~~~~~~ // CHECK:STDERR: require impls Y; } } // --- fail_errors_in_require_still_found.carbon library "[[@TEST_NAME]]"; class C { // The `require` is diagnosed as being in the wrong place. But we can still // diagnose issues inside the decl too. // // CHECK:STDERR: fail_errors_in_require_still_found.carbon:[[@LINE+8]]:3: error: `require` can only be used in an `interface` or `constraint` [RequireInWrongScope] // CHECK:STDERR: require impls Y; // CHECK:STDERR: ^~~~~~~ // CHECK:STDERR: // CHECK:STDERR: fail_errors_in_require_still_found.carbon:[[@LINE+4]]:17: error: name `Y` not found [NameNotFound] // CHECK:STDERR: require impls Y; // CHECK:STDERR: ^ // CHECK:STDERR: require impls Y; } // --- fail_extend_require_type.carbon library "[[@TEST_NAME]]"; interface Y {} interface Z { // CHECK:STDERR: fail_extend_require_type.carbon:[[@LINE+4]]:18: error: `extend require impls` with explicit type [RequireImplsExtendWithExplicitSelf] // CHECK:STDERR: extend require () impls Y; // CHECK:STDERR: ^~ // CHECK:STDERR: extend require () impls Y; fn F() { // The erroneous self-type for an `extend` causes the error to be propagated // into the interface scope, which prevents errors if we fail to find a name // in that scope. Self.A; } } // --- fail_require_with_nonexistent_type.carbon library "[[@TEST_NAME]]"; interface Y {} interface Z { // CHECK:STDERR: fail_require_with_nonexistent_type.carbon:[[@LINE+4]]:11: error: name `nonexistent` not found [NameNotFound] // CHECK:STDERR: require nonexistent impls Y; // CHECK:STDERR: ^~~~~~~~~~~ // CHECK:STDERR: require nonexistent impls Y; fn F() { // The name lookup error still happens, since the `require` is not `extend`. // CHECK:STDERR: fail_require_with_nonexistent_type.carbon:[[@LINE+4]]:5: error: member name `A` not found in `Z` [MemberNameNotFoundInSpecificScope] // CHECK:STDERR: Self.A; // CHECK:STDERR: ^~~~~~ // CHECK:STDERR: Self.A; } } // --- fail_extend_require_with_nonexistent_type.carbon library "[[@TEST_NAME]]"; interface Y {} interface Z { // CHECK:STDERR: fail_extend_require_with_nonexistent_type.carbon:[[@LINE+4]]:18: error: name `nonexistent` not found [NameNotFound] // CHECK:STDERR: extend require nonexistent impls Y; // CHECK:STDERR: ^~~~~~~~~~~ // CHECK:STDERR: extend require nonexistent impls Y; fn F() { // The erroneous self-type for an `extend` causes the error to be propagated // into the interface scope, which prevents errors if we fail to find a name // in that scope. Self.A; } } // --- fail_extend_require_with_nonexistent_type_pointer.carbon library "[[@TEST_NAME]]"; interface Y {} interface Z { // CHECK:STDERR: fail_extend_require_with_nonexistent_type_pointer.carbon:[[@LINE+4]]:18: error: name `nonexistent` not found [NameNotFound] // CHECK:STDERR: extend require nonexistent* impls Y; // CHECK:STDERR: ^~~~~~~~~~~ // CHECK:STDERR: extend require nonexistent* impls Y; fn F() { // The erroneous self-type for an `extend` causes the error to be propagated // into the interface scope, which prevents errors if we fail to find a name // in that scope. Self.A; } } ================================================ FILE: toolchain/check/testdata/facet/require_satisified.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/int.carbon // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/facet/require_satisified.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/facet/require_satisified.carbon // --- fail_missing_require_for_concrete_type.carbon library "[[@TEST_NAME]]"; interface Z {} interface Y { extend require impls Z; } // () does not need to impl Z yet. impl () as Y; // () needs to impl Z at the start of the definition. // CHECK:STDERR: fail_missing_require_for_concrete_type.carbon:[[@LINE+4]]:1: error: interface `Y` being implemented requires that `()` implements `Z` [RequireImplsNotImplemented] // CHECK:STDERR: impl () as Y {} // CHECK:STDERR: ^~~~~~~~~~~~~~ // CHECK:STDERR: impl () as Y {} // --- fail_missing_require_for_symbolic_type.carbon library "[[@TEST_NAME]]"; interface Z {} interface Y { extend require impls Z; } // T does not need to impl Z yet. impl forall [T:! type] T as Y; // T needs to impl Z at the start of the definition. // CHECK:STDERR: fail_missing_require_for_symbolic_type.carbon:[[@LINE+4]]:1: error: interface `Y` being implemented requires that `T` implements `Z` [RequireImplsNotImplemented] // CHECK:STDERR: impl forall [T:! type] T as Y {} // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: impl forall [T:! type] T as Y {} // --- fail_missing_require_double.carbon library "[[@TEST_NAME]]"; interface Z1(T:! type) {} interface Z2(T:! type) {} class A; class B; interface Y(U:! type) { extend require impls Z1(U) & Z2(B); } // CHECK:STDERR: fail_missing_require_double.carbon:[[@LINE+4]]:1: error: interface `Y(A)` being implemented requires that `()` implements `Z1(A) & Z2(B)` [RequireImplsNotImplemented] // CHECK:STDERR: impl () as Y(A) {} // CHECK:STDERR: ^~~~~~~~~~~~~~~~~ // CHECK:STDERR: impl () as Y(A) {} // --- require_for_concrete_type.carbon library "[[@TEST_NAME]]"; interface Z {} interface Y { extend require impls Z; } // () does not need to impl Z yet. impl () as Y; // Now we know () impls Z. impl () as Z; // So no error here. impl () as Y {} impl () as Z {} // --- require_for_symbolic_type.carbon library "[[@TEST_NAME]]"; interface Z {} interface Y { extend require impls Z; } // T does not need to impl Z yet. impl forall [T:! type] T as Y; // Now we know T impls Z. impl forall [T:! type] T as Z; // So no error here. impl forall [T:! type] T as Y {} impl forall [T:! type] T as Z {} // --- require_for_symbolic_type_impl_matches_same_interface.carbon library "[[@TEST_NAME]]"; interface Z {} interface Y { extend require impls Z; } // This only matches T that impl Z so no error here. impl forall [T:! Z] T as Y {} // --- require_for_generic_interfaces.carbon library "[[@TEST_NAME]]"; interface Z1(T:! type) {} interface Z2(T:! type) {} class A; class B; interface Y(U:! type) { extend require impls Z1(U) & Z2(B); } // () does not need to impl Z1(A) and Z2(B) yet. impl () as Y(A); // Now we know () impls Z1(A) and Z2(B). impl () as Z1(A); impl () as Z2(B); // So no error here. impl () as Y(A) {} impl () as Z1(A) {} impl () as Z2(B) {} // --- fail_require_non_self_missing.carbon library "[[@TEST_NAME]]"; interface Z(T:! type) {} class C; interface Y(U:! type) { // `Self` must appear somewhere in the require decl, so it's in the interface // specific because we want to test a different self-type. require C impls Z(Self); } // Y requires that C impls Z(Self), but it does not do so. // CHECK:STDERR: fail_require_non_self_missing.carbon:[[@LINE+4]]:1: error: interface `Y(())` being implemented requires that `C` implements `Z(())` [RequireImplsNotImplemented] // CHECK:STDERR: impl () as Y(()) {} // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~ // CHECK:STDERR: impl () as Y(()) {} // --- require_non_self.carbon library "[[@TEST_NAME]]"; interface Z(T:! type) {} class C; interface Y(U:! type) { // `Self` must appear somewhere in the require decl, so it's in the interface // specific because we want to test a different self-type. require C impls Z(Self); } impl C as Z(()); // Y requires that C impls Z(Self), which is done above. impl () as Y(Self) {} impl C as Z(()) {} // --- impl_requires_itself_cycle.carbon library "[[@TEST_NAME]]"; interface A { fn AA(); } interface B { require impls A; } interface C { require impls B; } impl forall [T:! B] T as A { fn AA() {} } // When we get to the definition we check that anything B requires is satisfied. // - The interface B requires A, so we must check T impls A. // - The impl for A requires T impls B. // - This impl is what provides T impls B. impl forall [T:! C] T as B {} fn F(T:! C) { // If things go wrong, we find the impl `T as B`, but inside its definition is // a lookup for `T as A`, which (through deducing `T`) includes a lookup for // `T as B`. This creates a cycle of evaluating `T as B` recursively. // // This was solved by doing the check for requirements of `B` outside the // definition of `T as B`. // https://discord.com/channels/655572317891461132/941071822756143115/1463189087598022861 T.(A.AA)(); } // --- fail_impl_requires_itself_cycle_with_monomorphization_error.carbon library "[[@TEST_NAME]]"; class W(T:! type) { adapt {}; } interface A(N:! Core.IntLiteral()) { fn AA() -> W(array((), N)); } interface B(N:! Core.IntLiteral()) { require impls A(N); } interface C(N:! Core.IntLiteral()) { require impls B(N); } impl forall [N:! Core.IntLiteral(), T:! B(N)] T as A(N) { fn AA() -> W(array((), N)) { return {} as W(array((), N)); } } impl forall [N:! Core.IntLiteral(), T:! C(N)] T as B(N) { // The definition here does not contain the lookups verifying that C(N) impls // `A(N)`, so they do not get re-evaluated for a specific `N`. That doesn't // prevent us from producing a reasonable diagnostic when that `N` causes an // error in the specific use of this impl, which we use to get from `C(N)` to // `A(N)` (in the deduction of `T` in the impl as `A(N)`). The error just // happens where we use `N` in `A(N)` instead of inside the verification that // `T as B(N)` implies `T as A(N)`. } fn F(T:! C(-1)) { // CHECK:STDERR: fail_impl_requires_itself_cycle_with_monomorphization_error.carbon:[[@LINE+7]]:3: error: unable to monomorphize specific `AA()` [ResolvingSpecificHere] // CHECK:STDERR: T.(A(-1).AA)(); // CHECK:STDERR: ^~~~~~~~~~~~~~ // CHECK:STDERR: fail_impl_requires_itself_cycle_with_monomorphization_error.carbon:[[@LINE-27]]:26: note: array bound of -1 is negative [ArrayBoundNegative] // CHECK:STDERR: fn AA() -> W(array((), N)); // CHECK:STDERR: ^ // CHECK:STDERR: T.(A(-1).AA)(); } ================================================ FILE: toolchain/check/testdata/facet/runtime_value.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/convert.carbon // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/facet/runtime_value.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/facet/runtime_value.carbon // --- facet_value_copy_from_reference.carbon library "[[@TEST_NAME]]"; interface I {} class C { var i: I; } fn F(c: C) { // Member access produces a reference, which is copied into the value // binding. //@dump-sem-ir-begin let unused a: I = c.i; //@dump-sem-ir-end } // --- fail_todo_facet_copy_narrowing_from_reference.carbon library "[[@TEST_NAME]]"; interface I {} interface J {} class C { var ij: I & J; } fn F(c: C) { // Member access produces a reference, which is copied into the value // binding. // // TODO: This copy should work, but the narrowing prevents it currently. // //@dump-sem-ir-begin // CHECK:STDERR: fail_todo_facet_copy_narrowing_from_reference.carbon:[[@LINE+4]]:21: error: semantics TODO: `conversion of runtime facet value` [SemanticsTodo] // CHECK:STDERR: let unused a: I = c.ij; // CHECK:STDERR: ^~~~ // CHECK:STDERR: let unused a: I = c.ij; //@dump-sem-ir-end } // --- fail_compound_member_lookup_in_runtime_facet_without_value.carbon library "[[@TEST_NAME]]"; interface Z { let X:! type; } // CHECK:STDERR: fail_compound_member_lookup_in_runtime_facet_without_value.carbon:[[@LINE+4]]:15: error: semantics TODO: `associated value lookup on runtime facet value` [SemanticsTodo] // CHECK:STDERR: fn F(T: Z, v: T.(Z.X)); // CHECK:STDERR: ^~~~~~~ // CHECK:STDERR: fn F(T: Z, v: T.(Z.X)); // --- fail_member_lookup_in_runtime_facet_without_value.carbon library "[[@TEST_NAME]]"; interface Z { let X:! type; } // TODO: Improve this diagnostic. The problem is that T is a runtime facet // _and_ its facet type doesn't specific a value for `.X`. Or make this work // by producing an ImplWitnessAccess that functions against a runtime facet. // // CHECK:STDERR: fail_member_lookup_in_runtime_facet_without_value.carbon:[[@LINE+4]]:15: error: cannot access member of interface `Z` in type `Z` that does not implement that interface [MissingImplInMemberAccess] // CHECK:STDERR: fn F(T: Z, v: T.X); // CHECK:STDERR: ^~~ // CHECK:STDERR: fn F(T: Z, v: T.X); // --- fail_todo_compound_member_lookup_in_runtime_facet_with_value.carbon library "[[@TEST_NAME]]"; interface Z { let X:! type; } // TODO: We should be able to get the value of `.X` from the `FacetType` of // `T`. // // CHECK:STDERR: fail_todo_compound_member_lookup_in_runtime_facet_with_value.carbon:[[@LINE+4]]:29: error: semantics TODO: `conversion of runtime facet value` [SemanticsTodo] // CHECK:STDERR: fn F(T: Z where .X = (), v: T.(Z.X)); // CHECK:STDERR: ^~~~~~~ // CHECK:STDERR: fn F(T: Z where .X = (), v: T.(Z.X)); // --- fail_todo_member_lookup_in_runtime_facet_with_value.carbon library "[[@TEST_NAME]]"; interface Z { let X:! type; } // TODO: We should be able to get the value of `.X` from the `FacetType` of // `T`. // // CHECK:STDERR: fail_todo_member_lookup_in_runtime_facet_with_value.carbon:[[@LINE+4]]:29: error: cannot access member of interface `Z` in type `Z where .(Z.X) = ()` that does not implement that interface [MissingImplInMemberAccess] // CHECK:STDERR: fn F(T: Z where .X = (), v: T.X); // CHECK:STDERR: ^~~ // CHECK:STDERR: fn F(T: Z where .X = (), v: T.X); // --- fail_todo_member_lookup_in_type_of_runtime_facet_with_value.carbon library "[[@TEST_NAME]]"; interface Z { fn G(); } fn F(T: Z) { // CHECK:STDERR: fail_todo_member_lookup_in_type_of_runtime_facet_with_value.carbon:[[@LINE+4]]:3: error: cannot access member of interface `Z` in type `Z` that does not implement that interface [MissingImplInMemberAccess] // CHECK:STDERR: T.G(); // CHECK:STDERR: ^~~ // CHECK:STDERR: T.G(); } // CHECK:STDOUT: --- facet_value_copy_from_reference.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %I.type: type = facet_type <@I> [concrete] // CHECK:STDOUT: %C: type = class_type @C [concrete] // CHECK:STDOUT: %C.elem: type = unbound_element_type %C, %I.type [concrete] // CHECK:STDOUT: %pattern_type.9d9: type = pattern_type %I.type [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @F(%c.param: %C) { // CHECK:STDOUT: !entry: // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %a.patt: %pattern_type.9d9 = value_binding_pattern a [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %c.ref: %C = name_ref c, %c // CHECK:STDOUT: %i.ref: %C.elem = name_ref i, @C.%.loc6 [concrete = @C.%.loc6] // CHECK:STDOUT: %.loc13_22.1: ref %I.type = class_element_access %c.ref, element0 // CHECK:STDOUT: %.loc13_22.2: %I.type = acquire_value %.loc13_22.1 // CHECK:STDOUT: %I.ref: type = name_ref I, file.%I.decl [concrete = constants.%I.type] // CHECK:STDOUT: %a: %I.type = value_binding a, %.loc13_22.2 // CHECK:STDOUT: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- fail_todo_facet_copy_narrowing_from_reference.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %I.type: type = facet_type <@I> [concrete] // CHECK:STDOUT: %C: type = class_type @C [concrete] // CHECK:STDOUT: %facet_type: type = facet_type <@I & @J> [concrete] // CHECK:STDOUT: %C.elem: type = unbound_element_type %C, %facet_type [concrete] // CHECK:STDOUT: %pattern_type.9d9: type = pattern_type %I.type [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @F(%c.param: %C) { // CHECK:STDOUT: !entry: // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %a.patt: %pattern_type.9d9 = value_binding_pattern a [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %c.ref: %C = name_ref c, %c // CHECK:STDOUT: %ij.ref: %C.elem = name_ref ij, @C.%.loc7_9 [concrete = @C.%.loc7_9] // CHECK:STDOUT: %.loc21_22.1: ref %facet_type = class_element_access %c.ref, element0 // CHECK:STDOUT: %.loc21_22.2: %facet_type = acquire_value %.loc21_22.1 // CHECK:STDOUT: %I.ref: type = name_ref I, file.%I.decl [concrete = constants.%I.type] // CHECK:STDOUT: %a: %I.type = value_binding a, [concrete = ] // CHECK:STDOUT: // CHECK:STDOUT: } // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/facet/self_in_interface_param.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/none.carbon // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/facet/self_in_interface_param.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/facet/self_in_interface_param.carbon interface I(T:! type) { let I1:! type; } //@dump-sem-ir-begin fn F(T:! I(.Self) where .I1 = ()) -> T.I1 { return (); } //@dump-sem-ir-end fn G(_:! I(.Self) where .I1 = ()) {} // CHECK:STDOUT: --- self_in_interface_param.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %type: type = facet_type [concrete] // CHECK:STDOUT: %.Self.c39: %type = symbolic_binding .Self [symbolic_self] // CHECK:STDOUT: %I.type.609: type = generic_interface_type @I [concrete] // CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] // CHECK:STDOUT: %I.generic: %I.type.609 = struct_value () [concrete] // CHECK:STDOUT: %.Self.binding.as_type.8db: type = symbolic_binding_type .Self, %.Self.c39 [symbolic_self] // CHECK:STDOUT: %I.type.bee: type = facet_type <@I, @I(%.Self.binding.as_type.8db)> [symbolic_self] // CHECK:STDOUT: %.Self.dad: %I.type.bee = symbolic_binding .Self [symbolic_self] // CHECK:STDOUT: %I.assoc_type.c03: type = assoc_entity_type @I, @I(%.Self.binding.as_type.8db) [symbolic_self] // CHECK:STDOUT: %assoc0.fe4: %I.assoc_type.c03 = assoc_entity element0, @I.WithSelf.%I1 [symbolic_self] // CHECK:STDOUT: %.Self.binding.as_type.b0b: type = symbolic_binding_type .Self, %.Self.dad [symbolic_self] // CHECK:STDOUT: %I.lookup_impl_witness.3fc: = lookup_impl_witness %.Self.dad, @I, @I(%.Self.binding.as_type.8db) [symbolic_self] // CHECK:STDOUT: %impl.elem0: type = impl_witness_access %I.lookup_impl_witness.3fc, element0 [symbolic_self] // CHECK:STDOUT: %empty_tuple: %empty_tuple.type = tuple_value () [concrete] // CHECK:STDOUT: %I_where.type: type = facet_type <@I, @I(%.Self.binding.as_type.8db) where %impl.elem0 = %empty_tuple.type> [symbolic_self] // CHECK:STDOUT: %pattern_type.033: type = pattern_type %I_where.type [symbolic_self] // CHECK:STDOUT: %T.706: %I_where.type = symbolic_binding T, 0 [symbolic] // CHECK:STDOUT: %T.binding.as_type: type = symbolic_binding_type T, 0, %T.706 [symbolic] // CHECK:STDOUT: %I.lookup_impl_witness.94d: = lookup_impl_witness %T.706, @I, @I(%.Self.binding.as_type.8db) [symbolic] // CHECK:STDOUT: %.262: Core.Form = init_form %empty_tuple.type [concrete] // CHECK:STDOUT: %pattern_type.cb1: type = pattern_type %empty_tuple.type [concrete] // CHECK:STDOUT: %F.type: type = fn_type @F [concrete] // CHECK:STDOUT: %F: %F.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: %F.decl: %F.type = fn_decl @F [concrete = constants.%F] { // CHECK:STDOUT: %T.patt: %pattern_type.033 = symbolic_binding_pattern T, 0 [concrete] // CHECK:STDOUT: %return.patt: %pattern_type.cb1 = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: %pattern_type.cb1 = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %T.ref: %I_where.type = name_ref T, %T.loc18_6.2 [symbolic = %T.loc18_6.1 (constants.%T.706)] // CHECK:STDOUT: %T.as_type: type = facet_access_type %T.ref [symbolic = %T.binding.as_type (constants.%T.binding.as_type)] // CHECK:STDOUT: %.loc18_39.1: type = converted %T.ref, %T.as_type [symbolic = %T.binding.as_type (constants.%T.binding.as_type)] // CHECK:STDOUT: %.loc18_39.2: %I.assoc_type.c03 = specific_constant @I1.%assoc0, @I.WithSelf(constants.%.Self.binding.as_type.8db, constants.%T.706) [symbolic_self = constants.%assoc0.fe4] // CHECK:STDOUT: %I1.ref.loc18_39: %I.assoc_type.c03 = name_ref I1, %.loc18_39.2 [symbolic_self = constants.%assoc0.fe4] // CHECK:STDOUT: %impl.elem0.loc18_39: type = impl_witness_access constants.%I.lookup_impl_witness.94d, element0 [concrete = constants.%empty_tuple.type] // CHECK:STDOUT: %.loc18_39.3: Core.Form = init_form %impl.elem0.loc18_39 [concrete = constants.%.262] // CHECK:STDOUT: %.loc18_19.1: type = splice_block %.loc18_19.2 [symbolic_self = constants.%I_where.type] { // CHECK:STDOUT: // CHECK:STDOUT: %I.ref: %I.type.609 = name_ref I, file.%I.decl [concrete = constants.%I.generic] // CHECK:STDOUT: %.Self.ref.loc18_12: %type = name_ref .Self, %.Self.1 [symbolic_self = constants.%.Self.c39] // CHECK:STDOUT: %.Self.as_type.loc18_17: type = facet_access_type %.Self.ref.loc18_12 [symbolic_self = constants.%.Self.binding.as_type.8db] // CHECK:STDOUT: %.loc18_17: type = converted %.Self.ref.loc18_12, %.Self.as_type.loc18_17 [symbolic_self = constants.%.Self.binding.as_type.8db] // CHECK:STDOUT: %I.type: type = facet_type <@I, @I(constants.%.Self.binding.as_type.8db)> [symbolic_self = constants.%I.type.bee] // CHECK:STDOUT: // CHECK:STDOUT: %.Self.ref.loc18_25: %I.type.bee = name_ref .Self, %.Self.2 [symbolic_self = constants.%.Self.dad] // CHECK:STDOUT: %.Self.as_type.loc18_25: type = facet_access_type %.Self.ref.loc18_25 [symbolic_self = constants.%.Self.binding.as_type.b0b] // CHECK:STDOUT: %.loc18_25.1: type = converted %.Self.ref.loc18_25, %.Self.as_type.loc18_25 [symbolic_self = constants.%.Self.binding.as_type.b0b] // CHECK:STDOUT: %.loc18_25.2: %I.assoc_type.c03 = specific_constant @I1.%assoc0, @I.WithSelf(constants.%.Self.binding.as_type.8db, constants.%.Self.dad) [symbolic_self = constants.%assoc0.fe4] // CHECK:STDOUT: %I1.ref.loc18_25: %I.assoc_type.c03 = name_ref I1, %.loc18_25.2 [symbolic_self = constants.%assoc0.fe4] // CHECK:STDOUT: %impl.elem0.loc18_25: type = impl_witness_access constants.%I.lookup_impl_witness.3fc, element0 [symbolic_self = constants.%impl.elem0] // CHECK:STDOUT: %.loc18_32.1: %empty_tuple.type = tuple_literal () [concrete = constants.%empty_tuple] // CHECK:STDOUT: %.loc18_32.2: type = converted %.loc18_32.1, constants.%empty_tuple.type [concrete = constants.%empty_tuple.type] // CHECK:STDOUT: %.loc18_19.2: type = where_expr %.Self.2 [symbolic_self = constants.%I_where.type] { // CHECK:STDOUT: requirement_base_facet_type constants.%I.type.bee // CHECK:STDOUT: requirement_rewrite %impl.elem0.loc18_25, %.loc18_32.2 // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: %T.loc18_6.2: %I_where.type = symbolic_binding T, 0 [symbolic = %T.loc18_6.1 (constants.%T.706)] // CHECK:STDOUT: %return.param: ref %empty_tuple.type = out_param call_param0 // CHECK:STDOUT: %return: ref %empty_tuple.type = return_slot %return.param // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic fn @F(%T.loc18_6.2: %I_where.type) { // CHECK:STDOUT: %T.loc18_6.1: %I_where.type = symbolic_binding T, 0 [symbolic = %T.loc18_6.1 (constants.%T.706)] // CHECK:STDOUT: %T.binding.as_type: type = symbolic_binding_type T, 0, %T.loc18_6.1 [symbolic = %T.binding.as_type (constants.%T.binding.as_type)] // CHECK:STDOUT: %I.lookup_impl_witness: = lookup_impl_witness %T.loc18_6.1, @I, @I(constants.%.Self.binding.as_type.8db) [symbolic = %I.lookup_impl_witness (constants.%I.lookup_impl_witness.94d)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: // CHECK:STDOUT: fn() -> out %return.param: %empty_tuple.type { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %.loc19_11.1: %empty_tuple.type = tuple_literal () [concrete = constants.%empty_tuple] // CHECK:STDOUT: %.loc19_11.2: init %empty_tuple.type = tuple_init () [concrete = constants.%empty_tuple] // CHECK:STDOUT: %.loc19_12: init %empty_tuple.type = converted %.loc19_11.1, %.loc19_11.2 [concrete = constants.%empty_tuple] // CHECK:STDOUT: return %.loc19_12 // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @F(constants.%T.706) { // CHECK:STDOUT: %T.loc18_6.1 => constants.%T.706 // CHECK:STDOUT: %T.binding.as_type => constants.%T.binding.as_type // CHECK:STDOUT: %I.lookup_impl_witness => constants.%I.lookup_impl_witness.94d // CHECK:STDOUT: } // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/facet/tuple_and_struct_type_literal.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/convert.carbon // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/facet/tuple_and_struct_type_literal.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/facet/tuple_and_struct_type_literal.carbon // --- tuple_literal_to_facet_value.carbon library "[[@TEST_NAME]]"; interface Y {} interface Z { let X:! Y; } impl () as Y {} // The tuple literal converts implicitly to a type, and then to a facet value // satisfying `Y`. impl forall [T:! type] T as Z where .X = () {} // --- complex_tuple_literal_to_facet_value.carbon library "[[@TEST_NAME]]"; interface Y {} interface Z { let X:! Y; } impl ((), {}) as Y {} // The tuple literal converts implicitly to a type, and then to a facet value // satisfying `Y`. impl forall [T:! type] T as Z where .X = ((), {}) {} // --- fail_mismatch_tuple_literal_to_facet_value.carbon library "[[@TEST_NAME]]"; interface Y {} interface Z { let X:! Y; } impl ((), {}) as Y {} // CHECK:STDERR: fail_mismatch_tuple_literal_to_facet_value.carbon:[[@LINE+4]]:42: error: cannot convert type `({}, {})` into type implementing `Y` [ConversionFailureTypeToFacet] // CHECK:STDERR: impl forall [T:! type] T as Z where .X = ({}, {}) {} // CHECK:STDERR: ^~~~~~~~ // CHECK:STDERR: impl forall [T:! type] T as Z where .X = ({}, {}) {} // --- struct_literal_to_facet_value.carbon library "[[@TEST_NAME]]"; interface Y {} interface Z { let X:! Y; } impl {} as Y {} // The struct literal converts implicitly to a type, and then to a facet value // satisfying `Y`. impl forall [T:! type] T as Z where .X = {} {} // --- complex_struct_literal_to_facet_value.carbon library "[[@TEST_NAME]]"; interface Y {} interface Z { let X:! Y; } impl {.a: (), .b: {}} as Y {} // The struct literal converts implicitly to a type, and then to a facet value // satisfying `Y`. impl forall [T:! type] T as Z where .X = {.a: (), .b: {}} {} // --- fail_mismatch_struct_literal_to_facet_value.carbon library "[[@TEST_NAME]]"; interface Y {} interface Z { let X:! Y; } impl {.a: (), .b: {}} as Y {} // CHECK:STDERR: fail_mismatch_struct_literal_to_facet_value.carbon:[[@LINE+4]]:42: error: cannot convert type `{.a: {}, .b: {}}` into type implementing `Y` [ConversionFailureTypeToFacet] // CHECK:STDERR: impl forall [T:! type] T as Z where .X = {.a: {}, .b: {}} {} // CHECK:STDERR: ^~~~~~~~~~~~~~~~ // CHECK:STDERR: impl forall [T:! type] T as Z where .X = {.a: {}, .b: {}} {} // --- struct_literal_to_facet_type_parameter.carbon library "[[@TEST_NAME]]"; interface Z {} impl forall [U:! type] U as Z {} constraint N {} fn InterfaceParam(unused U:! Z) {} fn ConstraintParam(unused U:! N) {} fn F() { InterfaceParam({}); ConstraintParam({}); } // --- fail_struct_literal_with_field_to_facet_type_parameter.carbon library "[[@TEST_NAME]]"; interface Z {} impl forall [U:! type] U as Z {} constraint N {} fn InterfaceParam(unused U:! Z) {} fn ConstraintParam(unused U:! N) {} fn F() { // CHECK:STDERR: fail_struct_literal_with_field_to_facet_type_parameter.carbon:[[@LINE+10]]:3: error: cannot implicitly convert non-type value of type `{.a: ()}` into type implementing `Z` [ConversionFailureNonTypeToFacet] // CHECK:STDERR: InterfaceParam({.a = ()}); // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: fail_struct_literal_with_field_to_facet_type_parameter.carbon:[[@LINE+7]]:3: note: type `{.a: ()}` does not implement interface `Core.ImplicitAs(Z)` [MissingImplInMemberAccessInContext] // CHECK:STDERR: InterfaceParam({.a = ()}); // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: fail_struct_literal_with_field_to_facet_type_parameter.carbon:[[@LINE-10]]:26: note: initializing generic parameter `U` declared here [InitializingGenericParam] // CHECK:STDERR: fn InterfaceParam(unused U:! Z) {} // CHECK:STDERR: ^ // CHECK:STDERR: InterfaceParam({.a = ()}); // CHECK:STDERR: fail_struct_literal_with_field_to_facet_type_parameter.carbon:[[@LINE+10]]:3: error: cannot implicitly convert non-type value of type `{.a: ()}` into type implementing `N` [ConversionFailureNonTypeToFacet] // CHECK:STDERR: ConstraintParam({.a = ()}); // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: fail_struct_literal_with_field_to_facet_type_parameter.carbon:[[@LINE+7]]:3: note: type `{.a: ()}` does not implement interface `Core.ImplicitAs(N)` [MissingImplInMemberAccessInContext] // CHECK:STDERR: ConstraintParam({.a = ()}); // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: fail_struct_literal_with_field_to_facet_type_parameter.carbon:[[@LINE-20]]:27: note: initializing generic parameter `U` declared here [InitializingGenericParam] // CHECK:STDERR: fn ConstraintParam(unused U:! N) {} // CHECK:STDERR: ^ // CHECK:STDERR: ConstraintParam({.a = ()}); } // --- tuple_literal_to_facet_type_parameter.carbon library "[[@TEST_NAME]]"; interface Z {} impl forall [U:! type] U as Z {} constraint N {} fn InterfaceParam(unused U:! Z) {} fn ConstraintParam(unused U:! N) {} fn F() { InterfaceParam(()); ConstraintParam(()); InterfaceParam(({}, )); ConstraintParam(({}, )); class C; InterfaceParam(({}, C)); ConstraintParam(({}, C)); } // --- struct_literal_to_generic_facet_type_parameter_with_concrete_specific.carbon library "[[@TEST_NAME]]"; interface Z(U:! type) {} impl forall [T:! type, U:! type] U as Z(T) {} constraint N(U:! type) {} class C; fn InterfaceParam(unused U:! Z(C)) {} fn ConstraintParam(unused U:! N(C)) {} fn F() { InterfaceParam({}); ConstraintParam({}); } // --- tuple_literal_to_generic_facet_type_parameter_with_concrete_specific.carbon library "[[@TEST_NAME]]"; interface Z(U:! type) {} impl forall [T:! type, U:! type] U as Z(T) {} constraint N(U:! type) {} class C; fn InterfaceParam(unused U:! Z(C)) {} fn ConstraintParam(unused U:! N(C)) {} fn F() { InterfaceParam(()); ConstraintParam(()); InterfaceParam(({}, )); ConstraintParam(({}, )); class C; InterfaceParam(({}, C)); ConstraintParam(({}, C)); } // --- struct_literal_to_generic_facet_type_parameter_with_symbolic_specific.carbon library "[[@TEST_NAME]]"; interface Z(U:! type) {} impl forall [T:! type, U:! type] U as Z(T) {} constraint N(U:! type) {} fn InterfaceParam(T:! type, unused U:! Z(T)) {} fn ConstraintParam(T:! type, unused U:! N(T)) {} fn F(T:! type) { InterfaceParam((), {}); ConstraintParam((), {}); InterfaceParam(T, {}); ConstraintParam(T, {}); } // --- tuple_literal_to_generic_facet_type_parameter_with_symbolic_specific.carbon library "[[@TEST_NAME]]"; interface Z(U:! type) {} impl forall [T:! type, U:! type] U as Z(T) {} constraint N(U:! type) {} fn InterfaceParam(T:! type, unused U:! Z(T)) {} fn ConstraintParam(T:! type, unused U:! N(T)) {} fn F(T:! type) { InterfaceParam({}, ()); ConstraintParam({}, ()); InterfaceParam(T, ()); ConstraintParam(T, ()); InterfaceParam(T, ({}, )); ConstraintParam(T, ({}, )); class C; InterfaceParam(T, ({}, C)); ConstraintParam(T, ({}, C)); } ================================================ FILE: toolchain/check/testdata/facet/tuple_and_struct_type_value.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/convert.carbon // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/facet/tuple_and_struct_type_value.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/facet/tuple_and_struct_type_value.carbon // --- tuple_value_to_facet_value.carbon library "[[@TEST_NAME]]"; interface Z {} impl forall [U:! type] U as Z {} constraint N {} fn InterfaceParam(unused U:! Z) {} fn ConstraintParam(unused U:! N) {} fn F(T:! (type, ), U:! type, Empty:! ()) { // Passes a symbolic binding of type TupleType. InterfaceParam(T); ConstraintParam(T); // Passes a symbolic `type`. InterfaceParam(U); ConstraintParam(U); InterfaceParam(Empty); ConstraintParam(Empty); } fn G(T:! (type, )) { F(T, (T.0, ), ()); } fn H() { G(({}, )); } // --- struct_value_to_facet_value.carbon library "[[@TEST_NAME]]"; interface Z {} impl forall [U:! type] U as Z {} constraint N {} fn InterfaceParam(unused U:! Z) {} fn ConstraintParam(unused U:! N) {} fn F(T:! {}) { // Passes a symbolic binding of type StructType. InterfaceParam(T); ConstraintParam(T); } fn G() { F({}); } // --- fail_struct_value_with_field_to_facet_value.carbon library "[[@TEST_NAME]]"; interface Z {} impl forall [U:! type] U as Z {} constraint N {} fn InterfaceParam(unused U:! Z) {} fn ConstraintParam(unused U:! N) {} fn F(T:! {.a: type}) { // CHECK:STDERR: fail_struct_value_with_field_to_facet_value.carbon:[[@LINE+10]]:3: error: cannot implicitly convert non-type value of type `{.a: type}` into type implementing `Z` [ConversionFailureNonTypeToFacet] // CHECK:STDERR: InterfaceParam(T); // CHECK:STDERR: ^~~~~~~~~~~~~~~~~ // CHECK:STDERR: fail_struct_value_with_field_to_facet_value.carbon:[[@LINE+7]]:3: note: type `{.a: type}` does not implement interface `Core.ImplicitAs(Z)` [MissingImplInMemberAccessInContext] // CHECK:STDERR: InterfaceParam(T); // CHECK:STDERR: ^~~~~~~~~~~~~~~~~ // CHECK:STDERR: fail_struct_value_with_field_to_facet_value.carbon:[[@LINE-10]]:26: note: initializing generic parameter `U` declared here [InitializingGenericParam] // CHECK:STDERR: fn InterfaceParam(unused U:! Z) {} // CHECK:STDERR: ^ // CHECK:STDERR: InterfaceParam(T); // CHECK:STDERR: fail_struct_value_with_field_to_facet_value.carbon:[[@LINE+10]]:3: error: cannot implicitly convert non-type value of type `{.a: type}` into type implementing `N` [ConversionFailureNonTypeToFacet] // CHECK:STDERR: ConstraintParam(T); // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~ // CHECK:STDERR: fail_struct_value_with_field_to_facet_value.carbon:[[@LINE+7]]:3: note: type `{.a: type}` does not implement interface `Core.ImplicitAs(N)` [MissingImplInMemberAccessInContext] // CHECK:STDERR: ConstraintParam(T); // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~ // CHECK:STDERR: fail_struct_value_with_field_to_facet_value.carbon:[[@LINE-20]]:27: note: initializing generic parameter `U` declared here [InitializingGenericParam] // CHECK:STDERR: fn ConstraintParam(unused U:! N) {} // CHECK:STDERR: ^ // CHECK:STDERR: ConstraintParam(T); } fn G() { F({.a = ()}); } ================================================ FILE: toolchain/check/testdata/facet/validate_impl_constraints.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/convert.carbon // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/facet/validate_impl_constraints.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/facet/validate_impl_constraints.carbon // --- self_impls_modifies_assoc_constant.carbon library "[[@TEST_NAME]]"; interface I { let X:! type; } fn F(unused T:! I where .X = ()) {} fn G(T:! I where .Self impls (I where .X = ())) { F(T); } // --- fail_self_impls_modifies_assoc_constant_type_differs.carbon library "[[@TEST_NAME]]"; interface I { let X:! type; } fn F(unused T:! I where .X = ()) {} fn G(T:! I where .Self impls (I where .X = {})) { // CHECK:STDERR: fail_self_impls_modifies_assoc_constant_type_differs.carbon:[[@LINE+7]]:3: error: cannot convert type `T` that implements `I where .(I.X) = {}` into type implementing `I where .(I.X) = ()` [ConversionFailureFacetToFacet] // CHECK:STDERR: F(T); // CHECK:STDERR: ^~~~ // CHECK:STDERR: fail_self_impls_modifies_assoc_constant_type_differs.carbon:[[@LINE-6]]:13: note: initializing generic parameter `T` declared here [InitializingGenericParam] // CHECK:STDERR: fn F(unused T:! I where .X = ()) {} // CHECK:STDERR: ^ // CHECK:STDERR: F(T); } // --- fail_todo_where_impls_tests_associated_constant_of_generic_type.carbon library "[[@TEST_NAME]]"; class C(U:! type) {} // C(U) impls M if U impls L. interface L {} interface M { let M0:! type; } impl forall [U:! L] C(U) as M where .M0 = {} {} // U requires that C(.Self) impls M. // - C(.Self) impls M can be rewritten as C(U) impls M. // - C(U) impls M if U impls L => Requires U impls L. fn F(unused U:! type where C(.Self) impls (M where .M0 = {})) {} fn G(T:! L) { // CHECK:STDERR: fail_todo_where_impls_tests_associated_constant_of_generic_type.carbon:[[@LINE+7]]:3: error: cannot convert type `T` that implements `L` into type implementing `type where...` [ConversionFailureFacetToFacet] // CHECK:STDERR: F(T); // CHECK:STDERR: ^~~~ // CHECK:STDERR: fail_todo_where_impls_tests_associated_constant_of_generic_type.carbon:[[@LINE-6]]:13: note: initializing generic parameter `U` declared here [InitializingGenericParam] // CHECK:STDERR: fn F(unused U:! type where C(.Self) impls (M where .M0 = {})) {} // CHECK:STDERR: ^ // CHECK:STDERR: F(T); } // --- fail_where_impls_tests_associated_constant_of_generic_type_type_differs.carbon library "[[@TEST_NAME]]"; class C(U:! type) {} // C(U) impls M if U impls L. interface L {} interface M { let M0:! type; } impl forall [U:! L] C(U) as M where .M0 = () {} // U requires that C(.Self) impls M. // - C(.Self) impls M can be rewritten as C(U) impls M. // - C(U) impls M if U impls L => Requires U impls L. fn F(unused U:! type where C(.Self) impls (M where .M0 = {})) {} fn G(T:! L) { // CHECK:STDERR: fail_where_impls_tests_associated_constant_of_generic_type_type_differs.carbon:[[@LINE+7]]:3: error: cannot convert type `T` that implements `L` into type implementing `type where...` [ConversionFailureFacetToFacet] // CHECK:STDERR: F(T); // CHECK:STDERR: ^~~~ // CHECK:STDERR: fail_where_impls_tests_associated_constant_of_generic_type_type_differs.carbon:[[@LINE-6]]:13: note: initializing generic parameter `U` declared here [InitializingGenericParam] // CHECK:STDERR: fn F(unused U:! type where C(.Self) impls (M where .M0 = {})) {} // CHECK:STDERR: ^ // CHECK:STDERR: F(T); } // --- self_in_interface_generic_param_unconstrained.carbon library "[[@TEST_NAME]]"; interface Z {} interface I(T:! type) {} fn F(unused T:! I(.Self) where .Self impls Z) {} fn G(T:! Z & I(.Self)) { F(T); } // --- fail_todo_self_in_interface_generic_param_constrained.carbon library "[[@TEST_NAME]]"; interface Z {} interface I(T:! Z) {} // Implied constraint: .Self impls Z, which is satisfied and checked at the end // of the fn signature. // CHECK:STDERR: fail_todo_self_in_interface_generic_param_constrained.carbon:[[@LINE+7]]:17: error: cannot convert type `.Self` that implements `type` into type implementing `Z` [ConversionFailureFacetToFacet] // CHECK:STDERR: fn F(unused T:! I(.Self) where .Self impls Z) {} // CHECK:STDERR: ^~~~~~~~ // CHECK:STDERR: fail_todo_self_in_interface_generic_param_constrained.carbon:[[@LINE-7]]:13: note: initializing generic parameter `T` declared here [InitializingGenericParam] // CHECK:STDERR: interface I(T:! Z) {} // CHECK:STDERR: ^ // CHECK:STDERR: fn F(unused T:! I(.Self) where .Self impls Z) {} // CHECK:STDERR: fail_todo_self_in_interface_generic_param_constrained.carbon:[[@LINE+7]]:14: error: cannot convert type `.Self` that implements `type` into type implementing `Z` [ConversionFailureFacetToFacet] // CHECK:STDERR: fn G(T:! Z & I(.Self)) { // CHECK:STDERR: ^~~~~~~~ // CHECK:STDERR: fail_todo_self_in_interface_generic_param_constrained.carbon:[[@LINE-16]]:13: note: initializing generic parameter `T` declared here [InitializingGenericParam] // CHECK:STDERR: interface I(T:! Z) {} // CHECK:STDERR: ^ // CHECK:STDERR: fn G(T:! Z & I(.Self)) { F(T); } ================================================ FILE: toolchain/check/testdata/facet/validate_rewrite_constraints.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/convert.carbon // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/facet/validate_rewrite_constraints.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/facet/validate_rewrite_constraints.carbon // --- facet_value.carbon library "[[@TEST_NAME]]"; interface I { let X:! type; } fn F(unused T:! I where .X = ()) {} fn G(T:! I where .X = ()) { F(T); } // --- generic_interface_facet_value.carbon library "[[@TEST_NAME]]"; interface I(T:! type) { let X:! type; } class C; final impl forall [T:! type] T as I(()) where .X = () {} final impl forall [T:! type] T as I({}) where .X = {} {} fn F(U:! type, unused T:! I(U) where .X = ()) {} fn G() { F((), C); } // --- fail_generic_interface_facet_value_wrong_specific_impl.carbon library "[[@TEST_NAME]]"; interface I(T:! type) { let X:! type; } class C; final impl forall [T:! type] T as I(()) where .X = () {} final impl forall [T:! type] T as I({}) where .X = {} {} fn F(U:! type, unused T:! I(U) where .X = ()) {} fn G() { // This finds the impl where `.X = {}`, but F requires that `.X = ()`. // // CHECK:STDERR: fail_generic_interface_facet_value_wrong_specific_impl.carbon:[[@LINE+7]]:3: error: cannot convert type `C` into type implementing `I({}) where .(I({}).X) = ()` [ConversionFailureTypeToFacet] // CHECK:STDERR: F({}, C); // CHECK:STDERR: ^~~~~~~~ // CHECK:STDERR: fail_generic_interface_facet_value_wrong_specific_impl.carbon:[[@LINE-7]]:1: note: while deducing parameters of generic declared here [DeductionGenericHere] // CHECK:STDERR: fn F(U:! type, unused T:! I(U) where .X = ()) {} // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: F({}, C); } // --- dependent_rules.carbon library "[[@TEST_NAME]]"; interface I { let X:! type; } interface J { let Y:! type; } fn F(T:! I & J where .X = .Y) { // Allowed since `T impls I`, `T impls J`, and has constraint providing // T.(I.X) = T.(J.Y)`. F(T); } // --- fail_convert.carbon library "[[@TEST_NAME]]"; interface I { let X:! type; } fn F(unused T:! I where .X = {.a: (), .b: {}}) {} fn H() { class C; impl C as I where .X = {.b: {}, .a: ()} {} // CHECK:STDERR: fail_convert.carbon:[[@LINE+7]]:3: error: cannot convert type `C` into type implementing `I where .(I.X) = {.a: (), .b: {}}` [ConversionFailureTypeToFacet] // CHECK:STDERR: F(C); // CHECK:STDERR: ^~~~ // CHECK:STDERR: fail_convert.carbon:[[@LINE-8]]:13: note: initializing generic parameter `T` declared here [InitializingGenericParam] // CHECK:STDERR: fn F(unused T:! I where .X = {.a: (), .b: {}}) {} // CHECK:STDERR: ^ // CHECK:STDERR: F(C); } fn G(T:! I where .X = {.b: {}, .a: ()}) { // CHECK:STDERR: fail_convert.carbon:[[@LINE+7]]:3: error: cannot convert type `T` that implements `I where .(I.X) = {.b: {}, .a: ()}` into type implementing `I where .(I.X) = {.a: (), .b: {}}` [ConversionFailureFacetToFacet] // CHECK:STDERR: F(T); // CHECK:STDERR: ^~~~ // CHECK:STDERR: fail_convert.carbon:[[@LINE-19]]:13: note: initializing generic parameter `T` declared here [InitializingGenericParam] // CHECK:STDERR: fn F(unused T:! I where .X = {.a: (), .b: {}}) {} // CHECK:STDERR: ^ // CHECK:STDERR: F(T); } // --- fail_todo_dependent_rules_compound.carbon library "[[@TEST_NAME]]"; interface I { let X:! type; } interface J { let Y:! type; } // CHECK:STDERR: fail_todo_dependent_rules_compound.carbon:[[@LINE+8]]:23: error: expected identifier or `Self` after `.` [ExpectedIdentifierOrSelfAfterPeriod] // CHECK:STDERR: fn F(T:! I & J where .(I.X) = .(J.Y)) { // CHECK:STDERR: ^ // CHECK:STDERR: // CHECK:STDERR: fail_todo_dependent_rules_compound.carbon:[[@LINE+4]]:23: error: semantics TODO: `handle invalid parse trees in `check`` [SemanticsTodo] // CHECK:STDERR: fn F(T:! I & J where .(I.X) = .(J.Y)) { // CHECK:STDERR: ^ // CHECK:STDERR: fn F(T:! I & J where .(I.X) = .(J.Y)) { // Allowed since `T impls I`, `T impls J`, and has constraint providing // T.(I.X) = T.(J.Y)`. F(T); } // --- parameterized_interface.carbon library "[[@TEST_NAME]]"; interface I(T:! type) { let X:! type; } final impl forall [J:! I(()) where .X = ()] J as I({}) where .X = {} {} fn F(unused T:! I({}) where .X = {}) {} fn G(T:! I(()) where .X = ()) { F(T); } // --- fail_todo_parameterized_interface_compound.carbon library "[[@TEST_NAME]]"; interface I(T:! type) { let X:! type; } final impl forall [J:! I(())] J as I({}) where .X = {} {} // CHECK:STDERR: fail_todo_parameterized_interface_compound.carbon:[[@LINE+8]]:31: error: expected identifier or `Self` after `.` [ExpectedIdentifierOrSelfAfterPeriod] // CHECK:STDERR: fn F(T:! I(()) & I({}) where .(I(()).X) = () and .(I({}).X) = {}) {} // CHECK:STDERR: ^ // CHECK:STDERR: // CHECK:STDERR: fail_todo_parameterized_interface_compound.carbon:[[@LINE+4]]:31: error: semantics TODO: `handle invalid parse trees in `check`` [SemanticsTodo] // CHECK:STDERR: fn F(T:! I(()) & I({}) where .(I(()).X) = () and .(I({}).X) = {}) {} // CHECK:STDERR: ^ // CHECK:STDERR: fn F(T:! I(()) & I({}) where .(I(()).X) = () and .(I({}).X) = {}) {} fn G(T:! I(()) where .X = ()) { F(T); } // --- fail_parameterized_interface_with_wrong_where_in_impl_deduction.carbon library "[[@TEST_NAME]]"; interface I(T:! type) { let X:! type; } final impl forall [J:! I(()) where .X = {}] J as I({}) where .X = {} {} fn F(unused T:! I({}) where .X = {}) {} fn G(T:! I(()) where .X = ()) { // CHECK:STDERR: fail_parameterized_interface_with_wrong_where_in_impl_deduction.carbon:[[@LINE+7]]:3: error: cannot convert type `T` that implements `I(()) where .(I(()).X) = ()` into type implementing `I({}) where .(I({}).X) = {}` [ConversionFailureFacetToFacet] // CHECK:STDERR: F(T); // CHECK:STDERR: ^~~~ // CHECK:STDERR: fail_parameterized_interface_with_wrong_where_in_impl_deduction.carbon:[[@LINE-6]]:13: note: initializing generic parameter `T` declared here [InitializingGenericParam] // CHECK:STDERR: fn F(unused T:! I({}) where .X = {}) {} // CHECK:STDERR: ^ // CHECK:STDERR: F(T); } // --- source_rhs_is_assoc_constant.carbon library "[[@TEST_NAME]]"; interface I { let X1:! type; let X2:! type; } fn F(unused T:! I where .X1 = () and .X2 = ()) {} fn G() { class C; impl C as I where .X1 = .X2 and .X2 = () {} F(C); } fn H(T:! I where .X1 = .X2 and .X2 = ()) { F(T); } // --- target_rhs_is_assoc_constant.carbon library "[[@TEST_NAME]]"; interface I { let X1:! type; let X2:! type; } fn F(unused T:! I where .X1 = .X2 and .X2 = ()) {} fn G() { class C; impl C as I where .X1 = () and .X2 = () {} F(C); } fn H(T:! I where .X1 = () and .X2 = ()) { F(T); } // --- both_rhs_is_assoc_constant.carbon library "[[@TEST_NAME]]"; interface I { let X1:! type; let X2:! type; } fn F(unused T:! I where .X1 = .X2 and .X2 = ()) {} fn G() { class C1; impl C1 as I where .X1 = .X2 and .X2 = () {} F(C1); class C2; impl C2 as I where .X1 = () and .X2 = .X1 {} F(C2); } fn H1(T:! I where .X1 = .X2 and .X2 = ()) { F(T); } fn H2(T:! I where .X1 = () and .X2 = .X1) { F(T); } // --- fail_error_in_witness_table.carbon library "[[@TEST_NAME]]"; interface I { let X1:! type; let X2:! type; } fn F(unused T:! I where .X1 = .X2) {} class C; // .X2 is not set in the impl definition, so its value is an ErrorInst // in the impl's witness table. // // CHECK:STDERR: fail_error_in_witness_table.carbon:[[@LINE+7]]:1: error: associated constant X2 not given a value in impl of interface I [ImplAssociatedConstantNeedsValue] // CHECK:STDERR: impl C as I where .X1 = () {} // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: fail_error_in_witness_table.carbon:[[@LINE-12]]:7: note: associated constant declared here [AssociatedConstantHere] // CHECK:STDERR: let X2:! type; // CHECK:STDERR: ^~~~~~~~~ // CHECK:STDERR: impl C as I where .X1 = () {} fn G() { // CHECK:STDERR: fail_error_in_witness_table.carbon:[[@LINE+7]]:3: error: cannot convert type `C` into type implementing `I where .(I.X1) = .(I.X2)` [ConversionFailureTypeToFacet] // CHECK:STDERR: F(C); // CHECK:STDERR: ^~~~ // CHECK:STDERR: fail_error_in_witness_table.carbon:[[@LINE-19]]:13: note: initializing generic parameter `T` declared here [InitializingGenericParam] // CHECK:STDERR: fn F(unused T:! I where .X1 = .X2) {} // CHECK:STDERR: ^ // CHECK:STDERR: F(C); } // --- impl_provides_rewrite_requirements.carbon library "[[@TEST_NAME]]"; interface I {} interface J { let Y:! type; } class C; final impl forall [T:! I] T as J where .Y = C {} fn F(unused T:! I & J where .Y = C) {} fn G(T:! I) { F(T); } // --- facet_provides_rewrite_requirements.carbon library "[[@TEST_NAME]]"; interface I {} interface J { let Y:! type; } class C; final impl forall [T:! J] T as I {} fn F(unused T:! I & J where .Y = C) {} fn G(T:! J where .Y = C) { F(T); } // --- fail_non_final_impl_deduce.carbon library "[[@TEST_NAME]]"; interface I { let X:! type; } impl forall [U:! type] U as I where .X = () {} fn F(unused T:! I where .X = ()) {} class C(T:! type); fn G(T:! type) { // The type `C(T)` is generic so that the resulting impl witness will not be // effectively final. // // CHECK:STDERR: fail_non_final_impl_deduce.carbon:[[@LINE+7]]:3: error: cannot convert type `C(T)` into type implementing `I where .(I.X) = ()` [ConversionFailureTypeToFacet] // CHECK:STDERR: F(C(T)); // CHECK:STDERR: ^~~~~~~ // CHECK:STDERR: fail_non_final_impl_deduce.carbon:[[@LINE-11]]:13: note: initializing generic parameter `T` declared here [InitializingGenericParam] // CHECK:STDERR: fn F(unused T:! I where .X = ()) {} // CHECK:STDERR: ^ // CHECK:STDERR: F(C(T)); } // --- fail_non_final_impl_explicit.carbon library "[[@TEST_NAME]]"; interface I { let X:! type; } impl forall [U:! type] U as I where .X = () {} class C(T:! type); fn F(T:! type) { // The type `C(T)` is generic so that the resulting impl witness will not be // effectively final. // // CHECK:STDERR: fail_non_final_impl_explicit.carbon:[[@LINE+4]]:3: error: cannot convert type `C(T)` into type implementing `I where .(I.X) = ()` [ConversionFailureTypeToFacet] // CHECK:STDERR: C(T) as (I where .X = ()); // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: C(T) as (I where .X = ()); } // --- concrete_query_non_final_impl_deduce.carbon library "[[@TEST_NAME]]"; interface I { let X:! type; } impl forall [U:! type] U as I where .X = () {} fn F(unused T:! I where .X = ()) {} fn G() { class C; F(C); } // --- concrete_query_non_final_impl_explicit_as.carbon library "[[@TEST_NAME]]"; interface I { let X:! type; } impl forall [U:! type] U as I where .X = () {} fn F() { class C; C as (I where .X = ()); } // --- final_impl_deduce.carbon library "[[@TEST_NAME]]"; interface I { let X:! type; } final impl forall [U:! type] U as I where .X = () {} fn F(unused T:! I where .X = ()) {} fn G() { class C; F(C); } // --- final_impl_explicit_as.carbon library "[[@TEST_NAME]]"; interface I { let X:! type; } final impl forall [U:! type] U as I where .X = () {} fn F() { class C; C as (I where .X = ()); } // --- concrete_specialization_impl_deduce.carbon library "[[@TEST_NAME]]"; interface I { let X:! type; } class C; impl C as I where .X = () {} fn F(unused T:! I where .X = ()) {} fn G() { F(C); } // --- concrete_specialization_impl_explicit_as.carbon library "[[@TEST_NAME]]"; interface I { let X:! type; } class C; impl C as I where .X = () {} fn F() { C as (I where .X = ()); } // --- rewrite_value_in_class_param.carbon library "[[@TEST_NAME]]"; interface I { let X:! type; let Y:! type; } class C(T:! type); fn F(unused T:! I where .X = C(.Y)) {} fn G(T:! I where .X = C(()) and .Y = ()) { F(T); } // --- fail_wrong_rewrite_value_in_class_param.carbon library "[[@TEST_NAME]]"; interface I { let X:! type; let Y:! type; } class C(T:! type); fn F(unused T:! I where .X = C(.Y)) {} fn G(T:! I where .X = C(()) and .Y = {}) { // CHECK:STDERR: fail_wrong_rewrite_value_in_class_param.carbon:[[@LINE+7]]:3: error: cannot convert type `T` that implements `I where .(I.X) = C(()) and .(I.Y) = {}` into type implementing `I where .(I.X) = C(.(I.Y))` [ConversionFailureFacetToFacet] // CHECK:STDERR: F(T); // CHECK:STDERR: ^~~~ // CHECK:STDERR: fail_wrong_rewrite_value_in_class_param.carbon:[[@LINE-6]]:13: note: initializing generic parameter `T` declared here [InitializingGenericParam] // CHECK:STDERR: fn F(unused T:! I where .X = C(.Y)) {} // CHECK:STDERR: ^ // CHECK:STDERR: F(T); } // --- function_in_interface_ignored.carbon library "[[@TEST_NAME]]"; interface I { let X:! type; fn F(); } fn F(unused T:! I where .X = ()) {} fn G(T:! I where .X = ()) { F(T); } // --- function_as_rewrite_value.carbon library "[[@TEST_NAME]]"; interface I { let X:! type; let Y:! X; fn A(); } fn F(unused T:! I where .Y = .A) {} fn G(T:! I where .Y = .A) { F(T); } // --- fail_wrong_function_as_rewrite_value.carbon library "[[@TEST_NAME]]"; interface I { let X:! type; let Y:! X; fn A(); fn B(); } fn F(unused T:! I where .Y = .A) {} fn G(T:! I where .Y = .B) { // CHECK:STDERR: fail_wrong_function_as_rewrite_value.carbon:[[@LINE+7]]:3: error: cannot convert type `T` that implements `I where .(I.Y) = .(I.B)` into type implementing `I where .(I.Y) = .(I.A)` [ConversionFailureFacetToFacet] // CHECK:STDERR: F(T); // CHECK:STDERR: ^~~~ // CHECK:STDERR: fail_wrong_function_as_rewrite_value.carbon:[[@LINE-6]]:13: note: initializing generic parameter `T` declared here [InitializingGenericParam] // CHECK:STDERR: fn F(unused T:! I where .Y = .A) {} // CHECK:STDERR: ^ // CHECK:STDERR: F(T); } // --- fail_wrong_function_as_rewrite_value_in_other_interface.carbon library "[[@TEST_NAME]]"; interface I { let X:! type; let Y:! X; fn A(); } interface J { let J1:! type; let J2:! type; // B has the same index in J as A does in I, ensuring the index is not enough // for comparing them. fn B(); } fn F(unused T:! I & J where .Y = .A) {} fn G(T:! I & J where .Y = .B) { // CHECK:STDERR: fail_wrong_function_as_rewrite_value_in_other_interface.carbon:[[@LINE+7]]:3: error: cannot convert type `T` that implements `I & J where .(I.Y) = .(J.B)` into type implementing `I & J where .(I.Y) = .(I.A)` [ConversionFailureFacetToFacet] // CHECK:STDERR: F(T); // CHECK:STDERR: ^~~~ // CHECK:STDERR: fail_wrong_function_as_rewrite_value_in_other_interface.carbon:[[@LINE-6]]:13: note: initializing generic parameter `T` declared here [InitializingGenericParam] // CHECK:STDERR: fn F(unused T:! I & J where .Y = .A) {} // CHECK:STDERR: ^ // CHECK:STDERR: F(T); } // --- recursive_facet.carbon library "[[@TEST_NAME]]"; interface I { let X:! type; } interface K(Y:! type) { } fn F(unused T:! I where .X = {.k: K(I where .X = ())}) {} fn G(T:! I where .X = {.k: K(I where .X = ())}) { F(T); } // --- fail_facet_type_concrete_types_match_blanket_impl_concrete_types.carbon library "[[@TEST_NAME]]"; interface A { let X:! type; } interface B { let Y:! type; } interface C(BB:! B) { let AX:! type; let BY:! type; } impl forall [AA:! A, BB:! B] AA as C(BB) where .AX = () and .BY = {} {} fn F(AA:! A where .X = (), BB:! B where .Y = {}) { // The types match but there may be a specialization that specifies different // types and would be prefered for a specific `AA` or `BB`. // // CHECK:STDERR: fail_facet_type_concrete_types_match_blanket_impl_concrete_types.carbon:[[@LINE+4]]:3: error: cannot convert type `AA` that implements `A where .(A.X) = ()` into type implementing `C(BB as B) where .(C(BB as B).AX) = () and .(C(BB as B).BY) = {}` [ConversionFailureFacetToFacet] // CHECK:STDERR: AA as (C(BB) where .AX = () and .BY = {}); // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: AA as (C(BB) where .AX = () and .BY = {}); } // --- fail_facet_type_concrete_types_become_blank_impl_types.carbon library "[[@TEST_NAME]]"; interface A { let X:! type; } interface B { let Y:! type; } interface C(BB:! B) { let AX:! type; let BY:! type; } impl forall [AA:! A, BB:! B] AA as C(BB) where .AX = AA.X and .BY = BB.Y {} fn F(AA:! A where .X = (), BB:! B where .Y = {}) { // The types match but there may be a specialization that specifies different // types and would be prefered for a specific `AA` or `BB`. // // CHECK:STDERR: fail_facet_type_concrete_types_become_blank_impl_types.carbon:[[@LINE+4]]:3: error: cannot convert type `AA` that implements `A where .(A.X) = ()` into type implementing `C(BB as B) where .(C(BB as B).AX) = () and .(C(BB as B).BY) = {}` [ConversionFailureFacetToFacet] // CHECK:STDERR: AA as (C(BB) where .AX = () and .BY = {}); // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: AA as (C(BB) where .AX = () and .BY = {}); } // --- facet_type_concrete_types_match_final_blanket_impl_concrete_types.carbon library "[[@TEST_NAME]]"; interface A { let X:! type; } interface B { let Y:! type; } interface C(BB:! B) { let AX:! type; let BY:! type; } final impl forall [AA:! A, BB:! B] AA as C(BB) where .AX = () and .BY = {} {} fn F(AA:! A where .X = (), BB:! B where .Y = {}) { AA as (C(BB) where .AX = () and .BY = {}); } // --- facet_type_concrete_types_become_final_blanket_impl_types.carbon library "[[@TEST_NAME]]"; interface A { let X:! type; } interface B { let Y:! type; } interface C(BB:! B) { let AX:! type; let BY:! type; } final impl forall [AA:! A, BB:! B] AA as C(BB) where .AX = AA.X and .BY = BB.Y {} fn F(AA:! A where .X = (), BB:! B where .Y = {}) { AA as (C(BB) where .AX = () and .BY = {}); } // --- fail_facet_type_concrete_types_become_final_blanket_impl_types_wrong_types.carbon library "[[@TEST_NAME]]"; interface A { let X:! type; } interface B { let Y:! type; } interface C(BB:! B) { let AX:! type; let BY:! type; } final impl forall [AA:! A, BB:! B] AA as C(BB) where .AX = AA.X and .BY = BB.Y {} fn F(AA:! A where .X = (), BB:! B where .Y = {}) { // CHECK:STDERR: fail_facet_type_concrete_types_become_final_blanket_impl_types_wrong_types.carbon:[[@LINE+4]]:3: error: cannot convert type `AA` that implements `A where .(A.X) = ()` into type implementing `C(BB as B) where .(C(BB as B).AX) = {} and .(C(BB as B).BY) = ()` [ConversionFailureFacetToFacet] // CHECK:STDERR: AA as (C(BB) where .AX = {} and .BY = ()); // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: AA as (C(BB) where .AX = {} and .BY = ()); } // --- chain_in_target.carbon library "[[@TEST_NAME]]"; interface I { let X1:! type; let X2:! type; let X3:! type; } class C(T:! type); fn F(unused T:! I where .X1 = .X3 and .X2 = C(.X3) and .X3 = ()) {} fn G(T:! I where .X1 = () and .X2 = C(()) and .X3 = ()) { F(T); } // --- chain_in_source.carbon library "[[@TEST_NAME]]"; interface I { let X1:! type; let X2:! type; let X3:! type; } class C(T:! type); fn F(unused T:! I where .X1 = () and .X2 = C(()) and .X3 = .X1) {} fn G(T:! I where .X1 = .X3 and .X2 = C(.X1) and .X3 = ()) { F(T); } // --- reference_other_facet_value.carbon library "[[@TEST_NAME]]"; interface I { let X1:! type; let X2:! type; } fn F(U:! I where .X1 = {}, unused T:! I where .X1 = () and .X2 = U.X1) {} fn G(U:! I where .X1 = {} and .X2 = (), T:! I where .X1 = U.X2 and .X2 = {}) { F(U, T); } // --- value_through_self_value.carbon library "[[@TEST_NAME]]"; interface I { let X1:! I; let X2:! type; let X3:! type; } fn F(unused T:! I where .X1 = .Self and .X2 = .X1.X3 and .X3 = ()) {} fn G(T:! I where .X1 = .Self and .X2 = () and .X3 = ()) { F(T); } fn H(T:! I where .X1 = .Self and .X2 = .X1.X3 and .X3 = ()) { G(T); } // --- associated_constant_is_facet_type_of_same_interface.carbon library "[[@TEST_NAME]]"; interface I { let A:! type; let X:! type; } class C; // This looks for a bug where `.Self.A` resolves to `C` from `.T.A` in the // incoming facet value, which is incorrect. It should be `.T.X.A` which // resolves to `{}`. fn F(unused U:! I where .X = (I where .A = {})) {} fn G(T:! I where .X = (I where .A = {}) and .A = C) { F(T); } // --- rewrite_requires_subst_in_rhs.carbon library "[[@TEST_NAME]]"; interface I { let X:! type; let Y:! type; } class C(T:! type); fn F(unused T:! I where .X = C(.Y)) {} fn G(T:! I where .X = C({}) and .Y = {}) { F(T); } // --- fail_todo_rewrite_requires_subst_in_nested_facet_type.carbon library "[[@TEST_NAME]]"; interface I(T:! type) { let X:! type; let Y:! type; } class C; fn F(unused T:! I(C) where .X = (I(.Y) where .Y = ())) {} fn G(T:! I(C) where .X = (I({}) where .Y = ()) and .Y = {}) { // TODO: The T in G should match the T in F, once the .Self reference to the // top level facet value in `I(.Y)` is correctly substituted by tracking that // it is a .Self reference to the top level Self. // CHECK:STDERR: fail_todo_rewrite_requires_subst_in_nested_facet_type.carbon:[[@LINE+7]]:3: error: cannot convert type `T` that implements `I(C) where .(I(C).X) = I({}) where .(I({}).Y) = () and .(I(C).Y) = {}` into type implementing `I(C) where .(I(C).X) = I(.(I(C).Y)) where .(I(.(I(C).Y)).Y) = ()` [ConversionFailureFacetToFacet] // CHECK:STDERR: F(T); // CHECK:STDERR: ^~~~ // CHECK:STDERR: fail_todo_rewrite_requires_subst_in_nested_facet_type.carbon:[[@LINE-9]]:13: note: initializing generic parameter `T` declared here [InitializingGenericParam] // CHECK:STDERR: fn F(unused T:! I(C) where .X = (I(.Y) where .Y = ())) {} // CHECK:STDERR: ^ // CHECK:STDERR: F(T); } fn G2(T:! I(C) where .X = (I(.Y) where .Y = ()) and .Y = {}) { F(T); } // --- fail_rewrite_requires_subst_in_nested_facet_type_types_differ.carbon library "[[@TEST_NAME]]"; interface I(T:! type) { let X:! type; let Y:! type; } fn F(unused T:! I({}) where .X = (I(.Y) where .X = ())) {} // I(.Y) is I({}) which doesn't match I(()). fn G(T:! I({}) where .X = (I(()) where .X = ()) and .Y = {}) { // CHECK:STDERR: fail_rewrite_requires_subst_in_nested_facet_type_types_differ.carbon:[[@LINE+7]]:3: error: cannot convert type `T` that implements `I({}) where .(I({}).X) = I(()) where .(I(()).X) = () and .(I({}).Y) = {}` into type implementing `I({}) where .(I({}).X) = I(.(I({}).Y)) where .(I(.(I({}).Y)).X) = ()` [ConversionFailureFacetToFacet] // CHECK:STDERR: F(T); // CHECK:STDERR: ^~~~ // CHECK:STDERR: fail_rewrite_requires_subst_in_nested_facet_type_types_differ.carbon:[[@LINE-7]]:13: note: initializing generic parameter `T` declared here [InitializingGenericParam] // CHECK:STDERR: fn F(unused T:! I({}) where .X = (I(.Y) where .X = ())) {} // CHECK:STDERR: ^ // CHECK:STDERR: F(T); } // --- facet_type_in_assoc_constant.carbon library "[[@TEST_NAME]]"; interface I { let X:! type; let Y:! type; } fn F(unused T:! I where .X = (I where .X = () and .Y = ())) {} fn G(T:! I where .X = (I where .X = .Y and .Y = ())) { F(T); } // --- fail_facet_type_in_assoc_constant_differs.carbon library "[[@TEST_NAME]]"; interface I { let X:! type; let Y:! type; let Z:! type; } fn F(unused T:! I where .X = (I where .X = .Y)) {} fn G(T:! I where .X = (I where .X = () and .Y = () and .Z = {})) { // CHECK:STDERR: fail_facet_type_in_assoc_constant_differs.carbon:[[@LINE+7]]:3: error: cannot convert type `T` that implements `I where .(I.X) = I where .(I.X) = () and .(I.Y) = () and .(I.Z) = {}` into type implementing `I where .(I.X) = I where .(I.X) = .(I.Y)` [ConversionFailureFacetToFacet] // CHECK:STDERR: F(T); // CHECK:STDERR: ^~~~ // CHECK:STDERR: fail_facet_type_in_assoc_constant_differs.carbon:[[@LINE-6]]:13: note: initializing generic parameter `T` declared here [InitializingGenericParam] // CHECK:STDERR: fn F(unused T:! I where .X = (I where .X = .Y)) {} // CHECK:STDERR: ^ // CHECK:STDERR: F(T); } // --- nested_facet_type_in_assoc_constant.carbon library "[[@TEST_NAME]]"; interface I { let X:! type; let Y:! type; let Z:! type; } fn F(unused T:! I where .X = (I where .Y = (I where .X = () and .Y = ()))) {} fn G(T:! I where .X = (I where .Y = (I where .X = .Y and .Y = ()))) { F(T); } // --- fail_nested_facet_type_assigns_same_assoc_constant.carbon library "[[@TEST_NAME]]"; interface I { let X:! type; let Y:! type; } fn F(unused T:! I where .Y = ()) {} // The `.Y = ()` is on a different `.Self` than `T` (an unattached self), so // should not satisfy `F`. fn G(T:! I where .X = (I where .Y = ())) { // CHECK:STDERR: fail_nested_facet_type_assigns_same_assoc_constant.carbon:[[@LINE+7]]:3: error: cannot convert type `T` that implements `I where .(I.X) = I where .(I.Y) = ()` into type implementing `I where .(I.Y) = ()` [ConversionFailureFacetToFacet] // CHECK:STDERR: F(T); // CHECK:STDERR: ^~~~ // CHECK:STDERR: fail_nested_facet_type_assigns_same_assoc_constant.carbon:[[@LINE-8]]:13: note: initializing generic parameter `T` declared here [InitializingGenericParam] // CHECK:STDERR: fn F(unused T:! I where .Y = ()) {} // CHECK:STDERR: ^ // CHECK:STDERR: F(T); } // --- fail_nested_facet_type_in_assoc_constant_differs.carbon library "[[@TEST_NAME]]"; interface I { let X:! type; let Y:! type; let Z:! type; } fn F(unused T:! I where .X = (I where .Y = (I where .X = .Y))) {} // `.X = .Y` does not match `.X = () and .Y = ()` as they are different resolved // facet types. fn G(T:! I where .X = (I where .Y = (I where .X = () and .Y = ()))) { // CHECK:STDERR: fail_nested_facet_type_in_assoc_constant_differs.carbon:[[@LINE+7]]:3: error: cannot convert type `T` that implements `I where .(I.X) = I where .(I.Y) = I where .(I.X) = () and .(I.Y) = ()` into type implementing `I where .(I.X) = I where .(I.Y) = I where .(I.X) = .(I.Y)` [ConversionFailureFacetToFacet] // CHECK:STDERR: F(T); // CHECK:STDERR: ^~~~ // CHECK:STDERR: fail_nested_facet_type_in_assoc_constant_differs.carbon:[[@LINE-8]]:13: note: initializing generic parameter `T` declared here [InitializingGenericParam] // CHECK:STDERR: fn F(unused T:! I where .X = (I where .Y = (I where .X = .Y))) {} // CHECK:STDERR: ^ // CHECK:STDERR: F(T); } // The extra .Z rewrite makes a different resolved facet type which does not // match. fn G2(T:! I where .X = (I where .Y = (I where .X = .Y and .Z = {}))) { // CHECK:STDERR: fail_nested_facet_type_in_assoc_constant_differs.carbon:[[@LINE+7]]:3: error: cannot convert type `T` that implements `I where .(I.X) = I where .(I.Y) = I where .(I.X) = .(I.Y) and .(I.Z) = {}` into type implementing `I where .(I.X) = I where .(I.Y) = I where .(I.X) = .(I.Y)` [ConversionFailureFacetToFacet] // CHECK:STDERR: F(T); // CHECK:STDERR: ^~~~ // CHECK:STDERR: fail_nested_facet_type_in_assoc_constant_differs.carbon:[[@LINE-21]]:13: note: initializing generic parameter `T` declared here [InitializingGenericParam] // CHECK:STDERR: fn F(unused T:! I where .X = (I where .Y = (I where .X = .Y))) {} // CHECK:STDERR: ^ // CHECK:STDERR: F(T); } // --- fail_nested_facet_type_from_constant.carbon library "[[@TEST_NAME]]"; interface I { let X:! type; let Y:! type; } fn F(unused T:! I where .X = (I where .Y = (I where .X = .Y, ))) {} // References to named constants in a facet type don't work at all. If they did, // then when the `Constant` facet type is put into the RHS of a rewrite // constraint, its references to `.Self` must be modified to not refer to the // top level `.Self` which is `T`. If done correctly, they will match the // `.Self` references in the same position in the parameter of `F`. If not, the // `.X` within becomes self-referential and makes a cycle. fn G1() { // CHECK:STDERR: fail_nested_facet_type_from_constant.carbon:[[@LINE+4]]:7: error: semantics TODO: `local `let :!` bindings are currently unsupported` [SemanticsTodo] // CHECK:STDERR: let Constant:! type = I where .X = .Y; // CHECK:STDERR: ^~~~~~~~~~~~~~~ // CHECK:STDERR: let Constant:! type = I where .X = .Y; fn G(T:! I where .X = (I where .Y = (Constant, ))) { F(T); } } fn G2() { let Constant:! type = (I where .X = .Y, ); fn G(T:! I where .X = (I where .Y = Constant)) { F(T); } } fn G3() { let Constant:! type = I where .Y = (I where .X = .Y, ); fn G(T:! I where .X = Constant) { F(T); } } fn G4() { let Constant2:! type = (I where .X = .Y, ); let Constant:! type = Constant2; fn G(T:! I where .X = (I where .Y = Constant)) { F(T); } } fn G5() { let Constant2:! type = I where .X = .Y; let Constant:! type = (Constant2, ); fn G(T:! I where .X = (I where .Y = Constant)) { F(T); } } fn G6() { let Constant2:! type = (I where .X = .Y, ); let Constant:! type = I where .Y = Constant2; fn G(T:! I where .X = Constant) { F(T); } } // --- fail_nested_facet_type_from_constant_differs.carbon library "[[@TEST_NAME]]"; interface I { let X:! type; let Y:! type; } fn F(unused T:! I where .X = (I where .Y = (I where .X = .Y, ))) {} fn G1() { // CHECK:STDERR: fail_nested_facet_type_from_constant_differs.carbon:[[@LINE+4]]:7: error: semantics TODO: `local `let :!` bindings are currently unsupported` [SemanticsTodo] // CHECK:STDERR: let Constant:! type = I where .X = () and .Y = (); // CHECK:STDERR: ^~~~~~~~~~~~~~~ // CHECK:STDERR: let Constant:! type = I where .X = () and .Y = (); fn G(T:! I where .X = (I where .Y = (Constant, ))) { F(T); } } // --- rewrite_requires_subst_in_nested_access_of_self.carbon library "[[@TEST_NAME]]"; interface I { let I1:! type; let I2:! type; } interface J { let J1:! I; } interface K { let K1:! J; } fn F(unused T:! I & J & K where .K1 = .Self and .J1 = .Self and .I1 = (.K1.J1).I2) {} fn G(T:! I & J & K where .K1 = .Self and .J1 = .Self and .I1 = .I2) { F(T); } // --- fail_rewrite_requires_subst_in_nested_access_of_self_wrong_type.carbon library "[[@TEST_NAME]]"; interface I { let I1:! type; let I2:! type; } interface J { let J1:! I; } interface K { let K1:! J; } fn F(unused T:! I & J & K where .K1 = .Self and .J1 = .Self and .I1 = (.K1.J1).I2) {} // F's requirement I1 = I2 is not met, as I1 = () and I2 = {} fn G(T:! I & J & K where .K1 = .Self and .J1 = .Self and .I1 = () and .I2 = {}) { // CHECK:STDERR: fail_rewrite_requires_subst_in_nested_access_of_self_wrong_type.carbon:[[@LINE+7]]:3: error: cannot convert type `T` that implements `I & J & K where .(K.K1) = .Self as J and .(J.J1) = .Self as I and .(I.I1) = () and .(I.I2) = {}` into type implementing `I & J & K where .(K.K1) = .Self as J and .(J.J1) = .Self as I and .(I.I1) = .(I.I2)` [ConversionFailureFacetToFacet] // CHECK:STDERR: F(T); // CHECK:STDERR: ^~~~ // CHECK:STDERR: fail_rewrite_requires_subst_in_nested_access_of_self_wrong_type.carbon:[[@LINE-7]]:13: note: initializing generic parameter `T` declared here [InitializingGenericParam] // CHECK:STDERR: fn F(unused T:! I & J & K where .K1 = .Self and .J1 = .Self and .I1 = (.K1.J1).I2) {} // CHECK:STDERR: ^ // CHECK:STDERR: F(T); } // F's requirement I1 = I2 is not met, as I1 = {} and I2 is unspecified fn G2(T:! I & J & K where .K1 = .Self and .J1 = .Self and .I1 = {}) { // CHECK:STDERR: fail_rewrite_requires_subst_in_nested_access_of_self_wrong_type.carbon:[[@LINE+7]]:3: error: cannot convert type `T` that implements `I & J & K where .(K.K1) = .Self as J and .(J.J1) = .Self as I and .(I.I1) = {}` into type implementing `I & J & K where .(K.K1) = .Self as J and .(J.J1) = .Self as I and .(I.I1) = .(I.I2)` [ConversionFailureFacetToFacet] // CHECK:STDERR: F(T); // CHECK:STDERR: ^~~~ // CHECK:STDERR: fail_rewrite_requires_subst_in_nested_access_of_self_wrong_type.carbon:[[@LINE-19]]:13: note: initializing generic parameter `T` declared here [InitializingGenericParam] // CHECK:STDERR: fn F(unused T:! I & J & K where .K1 = .Self and .J1 = .Self and .I1 = (.K1.J1).I2) {} // CHECK:STDERR: ^ // CHECK:STDERR: F(T); } // F's requirement I1 = I2 is not met, as I2 = {} and I1 is unspecified fn G3(T:! I & J & K where .K1 = .Self and .J1 = .Self and .I2 = {}) { // CHECK:STDERR: fail_rewrite_requires_subst_in_nested_access_of_self_wrong_type.carbon:[[@LINE+7]]:3: error: cannot convert type `T` that implements `I & J & K where .(K.K1) = .Self as J and .(J.J1) = .Self as I and .(I.I2) = {}` into type implementing `I & J & K where .(K.K1) = .Self as J and .(J.J1) = .Self as I and .(I.I1) = .(I.I2)` [ConversionFailureFacetToFacet] // CHECK:STDERR: F(T); // CHECK:STDERR: ^~~~ // CHECK:STDERR: fail_rewrite_requires_subst_in_nested_access_of_self_wrong_type.carbon:[[@LINE-31]]:13: note: initializing generic parameter `T` declared here [InitializingGenericParam] // CHECK:STDERR: fn F(unused T:! I & J & K where .K1 = .Self and .J1 = .Self and .I1 = (.K1.J1).I2) {} // CHECK:STDERR: ^ // CHECK:STDERR: F(T); } // --- fail_todo_rewrite_requires_subst_in_nested_access_of_other.carbon library "[[@TEST_NAME]]"; interface I { let I1:! type; } interface J { let J1:! I; let J2:! type; } interface K { let K1:! J; } // .K1.J1 is an ImplWitnessAccess into Self. The Self witness will be subst'd in // for `U` and it will eval to the ImplWitnessAccess inside of `U.I1`. Then that // will need to be subst'd to find the value of I1 in U's witness. fn F(unused U:! I, unused T:! J & K where .J2 = (.K1.J1).I1) {} fn G(U:! I where .I1 = (), T:! J & K where .K1 = .Self and .J1 = U and .J2 = ()) { // CHECK:STDERR: fail_todo_rewrite_requires_subst_in_nested_access_of_other.carbon:[[@LINE+7]]:3: error: cannot convert type `T` that implements `J & K where .(J.J2) = () and .(K.K1) = .Self as J and .(J.J1) = U as I` into type implementing `J & K where .(J.J2) = .(K.K1).(J.J1).(I.I1)` [ConversionFailureFacetToFacet] // CHECK:STDERR: F(U, T); // CHECK:STDERR: ^~~~~~~ // CHECK:STDERR: fail_todo_rewrite_requires_subst_in_nested_access_of_other.carbon:[[@LINE-6]]:27: note: initializing generic parameter `T` declared here [InitializingGenericParam] // CHECK:STDERR: fn F(unused U:! I, unused T:! J & K where .J2 = (.K1.J1).I1) {} // CHECK:STDERR: ^ // CHECK:STDERR: F(U, T); } // --- fail_rewrite_requires_subst_in_nested_access_of_other_wrong_type.carbon library "[[@TEST_NAME]]"; interface I { let I1:! type; } interface J { let J1:! I; let J2:! type; } interface K { let K1:! J; } fn F(unused U:! I, unused T:! J & K where .J2 = (.K1.J1).I1) {} // F's requirement J2 = I1 is not met as J2 = () and I1 = {} fn G(U:! I where .I1 = {}, T:! J & K where .K1 = .Self and .J1 = U and .J2 = ()) { // CHECK:STDERR: fail_rewrite_requires_subst_in_nested_access_of_other_wrong_type.carbon:[[@LINE+7]]:3: error: cannot convert type `T` that implements `J & K where .(J.J2) = () and .(K.K1) = .Self as J and .(J.J1) = U as I` into type implementing `J & K where .(J.J2) = .(K.K1).(J.J1).(I.I1)` [ConversionFailureFacetToFacet] // CHECK:STDERR: F(U, T); // CHECK:STDERR: ^~~~~~~ // CHECK:STDERR: fail_rewrite_requires_subst_in_nested_access_of_other_wrong_type.carbon:[[@LINE-7]]:27: note: initializing generic parameter `T` declared here [InitializingGenericParam] // CHECK:STDERR: fn F(unused U:! I, unused T:! J & K where .J2 = (.K1.J1).I1) {} // CHECK:STDERR: ^ // CHECK:STDERR: F(U, T); } // F's requirement J2 = I1 is not met as J2 = () and I1 is unspecified fn G2(U:! I, T:! J & K where .K1 = .Self and .J1 = U and .J2 = ()) { // CHECK:STDERR: fail_rewrite_requires_subst_in_nested_access_of_other_wrong_type.carbon:[[@LINE+7]]:3: error: cannot convert type `T` that implements `J & K where .(J.J2) = () and .(K.K1) = .Self as J and .(J.J1) = U` into type implementing `J & K where .(J.J2) = .(K.K1).(J.J1).(I.I1)` [ConversionFailureFacetToFacet] // CHECK:STDERR: F(U, T); // CHECK:STDERR: ^~~~~~~ // CHECK:STDERR: fail_rewrite_requires_subst_in_nested_access_of_other_wrong_type.carbon:[[@LINE-19]]:27: note: initializing generic parameter `T` declared here [InitializingGenericParam] // CHECK:STDERR: fn F(unused U:! I, unused T:! J & K where .J2 = (.K1.J1).I1) {} // CHECK:STDERR: ^ // CHECK:STDERR: F(U, T); } // F's requirement J2 = I1 is not met as I1 = {} and J2 is unspecified fn G3(U:! I where .I1 = {}, T:! J & K where .K1 = .Self and .J1 = U) { // CHECK:STDERR: fail_rewrite_requires_subst_in_nested_access_of_other_wrong_type.carbon:[[@LINE+7]]:3: error: cannot convert type `T` that implements `J & K where .(K.K1) = .Self as J and .(J.J1) = U as I` into type implementing `J & K where .(J.J2) = .(K.K1).(J.J1).(I.I1)` [ConversionFailureFacetToFacet] // CHECK:STDERR: F(U, T); // CHECK:STDERR: ^~~~~~~ // CHECK:STDERR: fail_rewrite_requires_subst_in_nested_access_of_other_wrong_type.carbon:[[@LINE-31]]:27: note: initializing generic parameter `T` declared here [InitializingGenericParam] // CHECK:STDERR: fn F(unused U:! I, unused T:! J & K where .J2 = (.K1.J1).I1) {} // CHECK:STDERR: ^ // CHECK:STDERR: F(U, T); } // --- rewrite_requires_subst_in_nested_access_of_other_with_two_witnesses.carbon library "[[@TEST_NAME]]"; interface I { let I1:! type; } interface J { let J1:! type; } interface K { let K1:! J & I; let K2:! type; let K3:! type; } fn F(unused U:! I & J, unused T:! K where .K2 = .K1.I1 and .K3 = .K1.J1) {} fn G(U:! I & J where .I1 = () and .J1 = {}, T:! K where .K1 = U and .K2 = () and .K3 = {}) { F(U, T); } // --- fail_rewrite_requires_subst_in_nested_access_of_other_with_two_witnesses_wrong_type.carbon library "[[@TEST_NAME]]"; interface I { let I1:! type; } interface J { let J1:! type; } interface K { let K1:! J & I; let K2:! type; let K3:! type; } fn F(unused U:! I & J, unused T:! K where .K2 = .K1.I1 and .K3 = .K1.J1) {} // K2 = I1 fails since K2 = {} and I1 = () fn G(U:! I & J where .I1 = () and .J1 = {}, T:! K where .K1 = U and .K2 = {} and .K3 = {}) { // CHECK:STDERR: fail_rewrite_requires_subst_in_nested_access_of_other_with_two_witnesses_wrong_type.carbon:[[@LINE+7]]:3: error: cannot convert type `T` that implements `K where .(K.K2) = {} and .(K.K1) = U as I & J and .(K.K3) = {}` into type implementing `K where .(K.K2) = .(K.K1).(I.I1) and .(K.K3) = .(K.K1).(J.J1)` [ConversionFailureFacetToFacet] // CHECK:STDERR: F(U, T); // CHECK:STDERR: ^~~~~~~ // CHECK:STDERR: fail_rewrite_requires_subst_in_nested_access_of_other_with_two_witnesses_wrong_type.carbon:[[@LINE-7]]:31: note: initializing generic parameter `T` declared here [InitializingGenericParam] // CHECK:STDERR: fn F(unused U:! I & J, unused T:! K where .K2 = .K1.I1 and .K3 = .K1.J1) {} // CHECK:STDERR: ^ // CHECK:STDERR: F(U, T); } // K2 = I1 fails since I1 = () and K2 is unspecified. fn G2(U:! I & J where .I1 = () and .J1 = {}, T:! K where .K1 = U and .K3 = {}) { // CHECK:STDERR: fail_rewrite_requires_subst_in_nested_access_of_other_with_two_witnesses_wrong_type.carbon:[[@LINE+7]]:3: error: cannot convert type `T` that implements `K where .(K.K1) = U as I & J and .(K.K3) = {}` into type implementing `K where .(K.K2) = .(K.K1).(I.I1) and .(K.K3) = .(K.K1).(J.J1)` [ConversionFailureFacetToFacet] // CHECK:STDERR: F(U, T); // CHECK:STDERR: ^~~~~~~ // CHECK:STDERR: fail_rewrite_requires_subst_in_nested_access_of_other_with_two_witnesses_wrong_type.carbon:[[@LINE-19]]:31: note: initializing generic parameter `T` declared here [InitializingGenericParam] // CHECK:STDERR: fn F(unused U:! I & J, unused T:! K where .K2 = .K1.I1 and .K3 = .K1.J1) {} // CHECK:STDERR: ^ // CHECK:STDERR: F(U, T); } // K2 = I1 fails since K2 = () and I1 is unspecified. fn G3(U:! I & J where .J1 = {}, T:! K where .K1 = U and .K2 = () and .K3 = {}) { // CHECK:STDERR: fail_rewrite_requires_subst_in_nested_access_of_other_with_two_witnesses_wrong_type.carbon:[[@LINE+7]]:3: error: cannot convert type `T` that implements `K where .(K.K2) = () and .(K.K1) = U as I & J and .(K.K3) = {}` into type implementing `K where .(K.K2) = .(K.K1).(I.I1) and .(K.K3) = .(K.K1).(J.J1)` [ConversionFailureFacetToFacet] // CHECK:STDERR: F(U, T); // CHECK:STDERR: ^~~~~~~ // CHECK:STDERR: fail_rewrite_requires_subst_in_nested_access_of_other_with_two_witnesses_wrong_type.carbon:[[@LINE-31]]:31: note: initializing generic parameter `T` declared here [InitializingGenericParam] // CHECK:STDERR: fn F(unused U:! I & J, unused T:! K where .K2 = .K1.I1 and .K3 = .K1.J1) {} // CHECK:STDERR: ^ // CHECK:STDERR: F(U, T); } // K3 = J1 fails since J1 = {} and K3 is unspecified. fn G4(U:! I & J where .I1 = () and .J1 = {}, T:! K where .K1 = U and .K2 = ()) { // CHECK:STDERR: fail_rewrite_requires_subst_in_nested_access_of_other_with_two_witnesses_wrong_type.carbon:[[@LINE+7]]:3: error: cannot convert type `T` that implements `K where .(K.K2) = () and .(K.K1) = U as I & J` into type implementing `K where .(K.K2) = .(K.K1).(I.I1) and .(K.K3) = .(K.K1).(J.J1)` [ConversionFailureFacetToFacet] // CHECK:STDERR: F(U, T); // CHECK:STDERR: ^~~~~~~ // CHECK:STDERR: fail_rewrite_requires_subst_in_nested_access_of_other_with_two_witnesses_wrong_type.carbon:[[@LINE-43]]:31: note: initializing generic parameter `T` declared here [InitializingGenericParam] // CHECK:STDERR: fn F(unused U:! I & J, unused T:! K where .K2 = .K1.I1 and .K3 = .K1.J1) {} // CHECK:STDERR: ^ // CHECK:STDERR: F(U, T); } // K3 = J1 fails since K3 = {} and J1 is unspecified. fn G5(U:! I & J where .I1 = (), T:! K where .K1 = U and .K2 = () and .K3 = {}) { // CHECK:STDERR: fail_rewrite_requires_subst_in_nested_access_of_other_with_two_witnesses_wrong_type.carbon:[[@LINE+7]]:3: error: cannot convert type `T` that implements `K where .(K.K2) = () and .(K.K1) = U as I & J and .(K.K3) = {}` into type implementing `K where .(K.K2) = .(K.K1).(I.I1) and .(K.K3) = .(K.K1).(J.J1)` [ConversionFailureFacetToFacet] // CHECK:STDERR: F(U, T); // CHECK:STDERR: ^~~~~~~ // CHECK:STDERR: fail_rewrite_requires_subst_in_nested_access_of_other_with_two_witnesses_wrong_type.carbon:[[@LINE-55]]:31: note: initializing generic parameter `T` declared here [InitializingGenericParam] // CHECK:STDERR: fn F(unused U:! I & J, unused T:! K where .K2 = .K1.I1 and .K3 = .K1.J1) {} // CHECK:STDERR: ^ // CHECK:STDERR: F(U, T); } // --- fail_target_rewrites_dont_apply_to_source.carbon library "[[@TEST_NAME]]"; interface I { let I1:! type; let I2:! type; let I3:! type; let I4:! type; } fn F(unused T:! I where .I1 = () and .I2 = ()) {} fn G(T:! I where .I1 = .I2) { // CHECK:STDERR: fail_target_rewrites_dont_apply_to_source.carbon:[[@LINE+7]]:3: error: cannot convert type `T` that implements `I where .(I.I1) = .(I.I2)` into type implementing `I where .(I.I1) = () and .(I.I2) = ()` [ConversionFailureFacetToFacet] // CHECK:STDERR: F(T); // CHECK:STDERR: ^~~~ // CHECK:STDERR: fail_target_rewrites_dont_apply_to_source.carbon:[[@LINE-6]]:13: note: initializing generic parameter `T` declared here [InitializingGenericParam] // CHECK:STDERR: fn F(unused T:! I where .I1 = () and .I2 = ()) {} // CHECK:STDERR: ^ // CHECK:STDERR: F(T); } // --- nested_facet_type_used_as_root_facet_type.carbon library "[[@TEST_NAME]]"; interface I { let X:! type; let Y:! type; } fn F(T:! I where .X = (I where .Y = {}), unused U:! T.X) {} fn G(T:! I where .X = (I where .Y = {}), U:! I where .Y = {}) { F(T, U); } ================================================ FILE: toolchain/check/testdata/for/actual.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/full.carbon // EXTRA-ARGS: --dump-sem-ir-ranges=if-present // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/for/actual.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/for/actual.carbon // --- lib.carbon library "lib"; class IntRange(N:! Core.IntLiteral()) { fn Make(start: Core.Int(N), end: Core.Int(N)) -> Self { return {.start = start, .end = end}; } impl as Core.Iterate where .CursorType = Core.Int(N) and .ElementType = Core.Int(N) { fn NewCursor[self: Self]() -> Core.Int(N) { return self.start; } fn Next[self: Self](cursor: Core.Int(N)*) -> Core.Optional(Core.Int(N)) { var value: Core.Int(N) = *cursor; if (value < self.end) { ++*cursor; return Core.Optional(Core.Int(N)).Some(value); } else { return Core.Optional(Core.Int(N)).None(); } } } private var start: Core.Int(N); private var end: Core.Int(N); } fn Range(end: i32) -> IntRange(32) { return IntRange(32).Make(0, end); } // --- trivial.carbon import library "lib"; fn Read(y:! Core.IntLiteral()) { var unused x: IntRange(32) = Range(y); } // CHECK:STDOUT: --- lib.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %type: type = facet_type [concrete] // CHECK:STDOUT: %.Self.c39: %type = symbolic_binding .Self [symbolic_self] // CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] // CHECK:STDOUT: %IntLiteral.type: type = fn_type @IntLiteral [concrete] // CHECK:STDOUT: %IntLiteral: %IntLiteral.type = struct_value () [concrete] // CHECK:STDOUT: %pattern_type.dc0: type = pattern_type Core.IntLiteral [concrete] // CHECK:STDOUT: %N: Core.IntLiteral = symbolic_binding N, 0 [symbolic] // CHECK:STDOUT: %IntRange.type: type = generic_class_type @IntRange [concrete] // CHECK:STDOUT: %IntRange.generic: %IntRange.type = struct_value () [concrete] // CHECK:STDOUT: %IntRange.265: type = class_type @IntRange, @IntRange(%N) [symbolic] // CHECK:STDOUT: %Int.type: type = generic_class_type @Int [concrete] // CHECK:STDOUT: %Int.generic: %Int.type = struct_value () [concrete] // CHECK:STDOUT: %Int.fc6021.1: type = class_type @Int, @Int(%N) [symbolic] // CHECK:STDOUT: %pattern_type.764eab.1: type = pattern_type %Int.fc6021.1 [symbolic] // CHECK:STDOUT: %.e1e: Core.Form = init_form %IntRange.265 [symbolic] // CHECK:STDOUT: %pattern_type.b16: type = pattern_type %IntRange.265 [symbolic] // CHECK:STDOUT: %IntRange.Make.type.1df: type = fn_type @IntRange.Make, @IntRange(%N) [symbolic] // CHECK:STDOUT: %IntRange.Make.8a9: %IntRange.Make.type.1df = struct_value () [symbolic] // CHECK:STDOUT: %Iterate.type: type = facet_type <@Iterate> [concrete] // CHECK:STDOUT: %OptionalStorage.type: type = facet_type <@OptionalStorage> [concrete] // CHECK:STDOUT: %Destroy.type: type = facet_type <@Destroy> [concrete] // CHECK:STDOUT: %Copy.type: type = facet_type <@Copy> [concrete] // CHECK:STDOUT: %facet_type.7e2: type = facet_type <@Destroy & @Copy> [concrete] // CHECK:STDOUT: %Optional.type: type = generic_class_type @Optional [concrete] // CHECK:STDOUT: %Optional.generic: %Optional.type = struct_value () [concrete] // CHECK:STDOUT: %T.542: %OptionalStorage.type = symbolic_binding T, 0 [symbolic] // CHECK:STDOUT: %Optional.Some.type.eaa: type = fn_type @Optional.Some, @Optional(%T.542) [symbolic] // CHECK:STDOUT: %Optional.Some.6ca: %Optional.Some.type.eaa = struct_value () [symbolic] // CHECK:STDOUT: %Optional.None.type.fc5: type = fn_type @Optional.None, @Optional(%T.542) [symbolic] // CHECK:STDOUT: %Optional.None.fcb: %Optional.None.type.fc5 = struct_value () [symbolic] // CHECK:STDOUT: %.Self.2b2: %Iterate.type = symbolic_binding .Self [symbolic_self] // CHECK:STDOUT: %Iterate.lookup_impl_witness.53f: = lookup_impl_witness %.Self.2b2, @Iterate [symbolic_self] // CHECK:STDOUT: %impl.elem0.3b1: %facet_type.7e2 = impl_witness_access %Iterate.lookup_impl_witness.53f, element0 [symbolic_self] // CHECK:STDOUT: %.Self.binding.as_type: type = symbolic_binding_type .Self, %.Self.2b2 [symbolic_self] // CHECK:STDOUT: %Iterate.assoc_type: type = assoc_entity_type @Iterate [concrete] // CHECK:STDOUT: %assoc1.a27: %Iterate.assoc_type = assoc_entity element1, imports.%Core.import_ref.cd6 [concrete] // CHECK:STDOUT: %impl.elem1.49e: type = impl_witness_access %Iterate.lookup_impl_witness.53f, element1 [symbolic_self] // CHECK:STDOUT: %assoc0.4bd: %Iterate.assoc_type = assoc_entity element0, imports.%Core.import_ref.f74 [concrete] // CHECK:STDOUT: %Destroy.lookup_impl_witness.93c: = lookup_impl_witness %Int.fc6021.1, @Destroy [symbolic] // CHECK:STDOUT: %require_complete.9019d7.1: = require_complete_type %Int.fc6021.1 [symbolic] // CHECK:STDOUT: %Int.as.Copy.impl.Op.type.824: type = fn_type @Int.as.Copy.impl.Op, @Int.as.Copy.impl(%N) [symbolic] // CHECK:STDOUT: %Int.as.Copy.impl.Op.9b9: %Int.as.Copy.impl.Op.type.824 = struct_value () [symbolic] // CHECK:STDOUT: %.83cba3.1: Core.Form = init_form %Int.fc6021.1 [symbolic] // CHECK:STDOUT: %.4f8: require_specific_def_type = require_specific_def @Int.as.Copy.impl(%N) [symbolic] // CHECK:STDOUT: %Copy.lookup_impl_witness.7a8: = lookup_impl_witness %Int.fc6021.1, @Copy [symbolic] // CHECK:STDOUT: %facet_value: %facet_type.7e2 = facet_value %Int.fc6021.1, (%Destroy.lookup_impl_witness.93c, %Copy.lookup_impl_witness.7a8) [symbolic] // CHECK:STDOUT: %Iterate_where.type: type = facet_type <@Iterate where %impl.elem0.3b1 = %facet_value and %impl.elem1.49e = %Int.fc6021.1> [symbolic] // CHECK:STDOUT: %Iterate.impl_witness: = impl_witness @IntRange.as.Iterate.impl.%Iterate.impl_witness_table, @IntRange.as.Iterate.impl(%N) [symbolic] // CHECK:STDOUT: %require_complete.234: = require_complete_type %Iterate_where.type [symbolic] // CHECK:STDOUT: %IntRange.as.Iterate.impl.NewCursor.type: type = fn_type @IntRange.as.Iterate.impl.NewCursor, @IntRange.as.Iterate.impl(%N) [symbolic] // CHECK:STDOUT: %IntRange.as.Iterate.impl.NewCursor: %IntRange.as.Iterate.impl.NewCursor.type = struct_value () [symbolic] // CHECK:STDOUT: %ptr.c9c: type = ptr_type %Int.fc6021.1 [symbolic] // CHECK:STDOUT: %pattern_type.bda: type = pattern_type %ptr.c9c [symbolic] // CHECK:STDOUT: %.63c: require_specific_def_type = require_specific_def @T.binding.as_type.as.OptionalStorage.impl(%facet_value) [symbolic] // CHECK:STDOUT: %OptionalStorage.lookup_impl_witness.b62: = lookup_impl_witness %Int.fc6021.1, @OptionalStorage [symbolic] // CHECK:STDOUT: %OptionalStorage.facet.01e: %OptionalStorage.type = facet_value %Int.fc6021.1, (%OptionalStorage.lookup_impl_witness.b62) [symbolic] // CHECK:STDOUT: %Optional.e48: type = class_type @Optional, @Optional(%OptionalStorage.facet.01e) [symbolic] // CHECK:STDOUT: %.949: Core.Form = init_form %Optional.e48 [symbolic] // CHECK:STDOUT: %pattern_type.0c2: type = pattern_type %Optional.e48 [symbolic] // CHECK:STDOUT: %IntRange.as.Iterate.impl.Next.type: type = fn_type @IntRange.as.Iterate.impl.Next, @IntRange.as.Iterate.impl(%N) [symbolic] // CHECK:STDOUT: %IntRange.as.Iterate.impl.Next: %IntRange.as.Iterate.impl.Next.type = struct_value () [symbolic] // CHECK:STDOUT: %IntRange.elem.541: type = unbound_element_type %IntRange.265, %Int.fc6021.1 [symbolic] // CHECK:STDOUT: %struct_type.start.end.ff1: type = struct_type {.start: %Int.fc6021.1, .end: %Int.fc6021.1} [symbolic] // CHECK:STDOUT: %complete_type.427: = complete_type_witness %struct_type.start.end.ff1 [symbolic] // CHECK:STDOUT: %require_complete.8a1: = require_complete_type %IntRange.265 [symbolic] // CHECK:STDOUT: %Copy.facet.3b9: %Copy.type = facet_value %Int.fc6021.1, (%Copy.lookup_impl_witness.7a8) [symbolic] // CHECK:STDOUT: %Copy.WithSelf.Op.type.e13: type = fn_type @Copy.WithSelf.Op, @Copy.WithSelf(%Copy.facet.3b9) [symbolic] // CHECK:STDOUT: %.e29: type = fn_type_with_self_type %Copy.WithSelf.Op.type.e13, %Copy.facet.3b9 [symbolic] // CHECK:STDOUT: %impl.elem0.694: %.e29 = impl_witness_access %Copy.lookup_impl_witness.7a8, element0 [symbolic] // CHECK:STDOUT: %specific_impl_fn.bd4: = specific_impl_function %impl.elem0.694, @Copy.WithSelf.Op(%Copy.facet.3b9) [symbolic] // CHECK:STDOUT: %require_complete.45c: = require_complete_type %ptr.c9c [symbolic] // CHECK:STDOUT: %Optional.None.type.d56: type = fn_type @Optional.None, @Optional(%OptionalStorage.facet.01e) [symbolic] // CHECK:STDOUT: %Optional.None.b26: %Optional.None.type.d56 = struct_value () [symbolic] // CHECK:STDOUT: %Optional.Some.type.f74: type = fn_type @Optional.Some, @Optional(%OptionalStorage.facet.01e) [symbolic] // CHECK:STDOUT: %Optional.Some.076: %Optional.Some.type.f74 = struct_value () [symbolic] // CHECK:STDOUT: %require_complete.0bc: = require_complete_type %Optional.e48 [symbolic] // CHECK:STDOUT: %OrderedWith.type.270: type = generic_interface_type @OrderedWith [concrete] // CHECK:STDOUT: %OrderedWith.generic: %OrderedWith.type.270 = struct_value () [concrete] // CHECK:STDOUT: %Other: type = symbolic_binding Other, 0 [symbolic] // CHECK:STDOUT: %OrderedWith.type.ca8: type = facet_type <@OrderedWith, @OrderedWith(%Other)> [symbolic] // CHECK:STDOUT: %Self.adc: %OrderedWith.type.ca8 = symbolic_binding Self, 1 [symbolic] // CHECK:STDOUT: %OrderedWith.assoc_type.2b3: type = assoc_entity_type @OrderedWith, @OrderedWith(%Other) [symbolic] // CHECK:STDOUT: %OrderedWith.WithSelf.Less.type.df2: type = fn_type @OrderedWith.WithSelf.Less, @OrderedWith.WithSelf(%Other, %Self.adc) [symbolic] // CHECK:STDOUT: %OrderedWith.WithSelf.Less.1a4: %OrderedWith.WithSelf.Less.type.df2 = struct_value () [symbolic] // CHECK:STDOUT: %OrderedWith.type.e44: type = facet_type <@OrderedWith, @OrderedWith(%Int.fc6021.1)> [symbolic] // CHECK:STDOUT: %OrderedWith.assoc_type.215: type = assoc_entity_type @OrderedWith, @OrderedWith(%Int.fc6021.1) [symbolic] // CHECK:STDOUT: %assoc0.15c: %OrderedWith.assoc_type.215 = assoc_entity element0, imports.%Core.import_ref.255 [symbolic] // CHECK:STDOUT: %require_complete.dd3: = require_complete_type %OrderedWith.type.e44 [symbolic] // CHECK:STDOUT: %assoc0.666: %OrderedWith.assoc_type.2b3 = assoc_entity element0, imports.%Core.import_ref.e2b [symbolic] // CHECK:STDOUT: %M: Core.IntLiteral = symbolic_binding M, 1 [symbolic] // CHECK:STDOUT: %Int.as.OrderedWith.impl.Less.type.5f1: type = fn_type @Int.as.OrderedWith.impl.Less.1, @Int.as.OrderedWith.impl.2e6(%N, %M) [symbolic] // CHECK:STDOUT: %Int.as.OrderedWith.impl.Less.a9b: %Int.as.OrderedWith.impl.Less.type.5f1 = struct_value () [symbolic] // CHECK:STDOUT: %ImplicitAs.type.cc7: type = generic_interface_type @ImplicitAs [concrete] // CHECK:STDOUT: %ImplicitAs.generic: %ImplicitAs.type.cc7 = struct_value () [concrete] // CHECK:STDOUT: %OrderedWith.impl_witness.ea9: = impl_witness imports.%OrderedWith.impl_witness_table.b93, @Int.as.OrderedWith.impl.2e6(%N, %N) [symbolic] // CHECK:STDOUT: %Int.as.OrderedWith.impl.Less.type.3f1: type = fn_type @Int.as.OrderedWith.impl.Less.1, @Int.as.OrderedWith.impl.2e6(%N, %N) [symbolic] // CHECK:STDOUT: %Int.as.OrderedWith.impl.Less.4cf: %Int.as.OrderedWith.impl.Less.type.3f1 = struct_value () [symbolic] // CHECK:STDOUT: %.4b5: require_specific_def_type = require_specific_def @Int.as.OrderedWith.impl.2e6(%N, %N) [symbolic] // CHECK:STDOUT: %OrderedWith.facet: %OrderedWith.type.e44 = facet_value %Int.fc6021.1, (%OrderedWith.impl_witness.ea9) [symbolic] // CHECK:STDOUT: %OrderedWith.WithSelf.Less.type.3a2: type = fn_type @OrderedWith.WithSelf.Less, @OrderedWith.WithSelf(%Int.fc6021.1, %OrderedWith.facet) [symbolic] // CHECK:STDOUT: %.48d: type = fn_type_with_self_type %OrderedWith.WithSelf.Less.type.3a2, %OrderedWith.facet [symbolic] // CHECK:STDOUT: %Int.as.OrderedWith.impl.Less.specific_fn.4c2: = specific_function %Int.as.OrderedWith.impl.Less.4cf, @Int.as.OrderedWith.impl.Less.1(%N, %N) [symbolic] // CHECK:STDOUT: %Inc.type: type = facet_type <@Inc> [concrete] // CHECK:STDOUT: %.53b: require_specific_def_type = require_specific_def @Int.as.Inc.impl(%N) [symbolic] // CHECK:STDOUT: %Inc.lookup_impl_witness: = lookup_impl_witness %Int.fc6021.1, @Inc [symbolic] // CHECK:STDOUT: %Inc.facet: %Inc.type = facet_value %Int.fc6021.1, (%Inc.lookup_impl_witness) [symbolic] // CHECK:STDOUT: %Inc.WithSelf.Op.type.17b: type = fn_type @Inc.WithSelf.Op, @Inc.WithSelf(%Inc.facet) [symbolic] // CHECK:STDOUT: %.9a6: type = fn_type_with_self_type %Inc.WithSelf.Op.type.17b, %Inc.facet [symbolic] // CHECK:STDOUT: %impl.elem0.10c: %.9a6 = impl_witness_access %Inc.lookup_impl_witness, element0 [symbolic] // CHECK:STDOUT: %specific_impl_fn.ed3: = specific_impl_function %impl.elem0.10c, @Inc.WithSelf.Op(%Inc.facet) [symbolic] // CHECK:STDOUT: %Optional.Some.specific_fn: = specific_function %Optional.Some.076, @Optional.Some(%OptionalStorage.facet.01e) [symbolic] // CHECK:STDOUT: %Destroy.facet.1c0: %Destroy.type = facet_value %Int.fc6021.1, (%Destroy.lookup_impl_witness.93c) [symbolic] // CHECK:STDOUT: %Destroy.WithSelf.Op.type.297: type = fn_type @Destroy.WithSelf.Op, @Destroy.WithSelf(%Destroy.facet.1c0) [symbolic] // CHECK:STDOUT: %.e63: type = fn_type_with_self_type %Destroy.WithSelf.Op.type.297, %Destroy.facet.1c0 [symbolic] // CHECK:STDOUT: %impl.elem0.602: %.e63 = impl_witness_access %Destroy.lookup_impl_witness.93c, element0 [symbolic] // CHECK:STDOUT: %specific_impl_fn.d65: = specific_impl_function %impl.elem0.602, @Destroy.WithSelf.Op(%Destroy.facet.1c0) [symbolic] // CHECK:STDOUT: %Optional.None.specific_fn: = specific_function %Optional.None.b26, @Optional.None(%OptionalStorage.facet.01e) [symbolic] // CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [concrete] // CHECK:STDOUT: %i32: type = class_type @Int, @Int(%int_32) [concrete] // CHECK:STDOUT: %pattern_type.7ce: type = pattern_type %i32 [concrete] // CHECK:STDOUT: %IntRange.a89: type = class_type @IntRange, @IntRange(%int_32) [concrete] // CHECK:STDOUT: %.aec: Core.Form = init_form %IntRange.a89 [concrete] // CHECK:STDOUT: %pattern_type.615: type = pattern_type %IntRange.a89 [concrete] // CHECK:STDOUT: %Range.type: type = fn_type @Range [concrete] // CHECK:STDOUT: %Range: %Range.type = struct_value () [concrete] // CHECK:STDOUT: %i32.builtin: type = int_type signed, %int_32 [concrete] // CHECK:STDOUT: %complete_type.f8a: = complete_type_witness %i32.builtin [concrete] // CHECK:STDOUT: %IntRange.Make.type.045: type = fn_type @IntRange.Make, @IntRange(%int_32) [concrete] // CHECK:STDOUT: %IntRange.Make.3e9: %IntRange.Make.type.045 = struct_value () [concrete] // CHECK:STDOUT: %Copy.impl_witness.f17: = impl_witness imports.%Copy.impl_witness_table.e76, @Int.as.Copy.impl(%int_32) [concrete] // CHECK:STDOUT: %Int.as.Copy.impl.Op.type.546: type = fn_type @Int.as.Copy.impl.Op, @Int.as.Copy.impl(%int_32) [concrete] // CHECK:STDOUT: %Int.as.Copy.impl.Op.664: %Int.as.Copy.impl.Op.type.546 = struct_value () [concrete] // CHECK:STDOUT: %.14e: require_specific_def_type = require_specific_def @Int.as.Copy.impl(%int_32) [concrete] // CHECK:STDOUT: %IntRange.elem.a58: type = unbound_element_type %IntRange.a89, %i32 [concrete] // CHECK:STDOUT: %struct_type.start.end.d0a: type = struct_type {.start: %i32, .end: %i32} [concrete] // CHECK:STDOUT: %complete_type.c45: = complete_type_witness %struct_type.start.end.d0a [concrete] // CHECK:STDOUT: %int_0.5c6: Core.IntLiteral = int_value 0 [concrete] // CHECK:STDOUT: %IntRange.Make.specific_fn: = specific_function %IntRange.Make.3e9, @IntRange.Make(%int_32) [concrete] // CHECK:STDOUT: %ImplicitAs.type.e8c: type = facet_type <@ImplicitAs, @ImplicitAs(%i32)> [concrete] // CHECK:STDOUT: %To: Core.IntLiteral = symbolic_binding To, 0 [symbolic] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.4e6: type = fn_type @Core.IntLiteral.as.ImplicitAs.impl.Convert, @Core.IntLiteral.as.ImplicitAs.impl(%To) [symbolic] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.3c2: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.4e6 = struct_value () [symbolic] // CHECK:STDOUT: %ImplicitAs.impl_witness.6bc: = impl_witness imports.%ImplicitAs.impl_witness_table.74f, @Core.IntLiteral.as.ImplicitAs.impl(%int_32) [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.e0d: type = fn_type @Core.IntLiteral.as.ImplicitAs.impl.Convert, @Core.IntLiteral.as.ImplicitAs.impl(%int_32) [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.e0d = struct_value () [concrete] // CHECK:STDOUT: %ImplicitAs.facet.b94: %ImplicitAs.type.e8c = facet_value Core.IntLiteral, (%ImplicitAs.impl_witness.6bc) [concrete] // CHECK:STDOUT: %ImplicitAs.WithSelf.Convert.type.b37: type = fn_type @ImplicitAs.WithSelf.Convert, @ImplicitAs.WithSelf(%i32, %ImplicitAs.facet.b94) [concrete] // CHECK:STDOUT: %.545: type = fn_type_with_self_type %ImplicitAs.WithSelf.Convert.type.b37, %ImplicitAs.facet.b94 [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.bound: = bound_method %int_0.5c6, %Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5 [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn: = specific_function %Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5, @Core.IntLiteral.as.ImplicitAs.impl.Convert(%int_32) [concrete] // CHECK:STDOUT: %bound_method: = bound_method %int_0.5c6, %Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn [concrete] // CHECK:STDOUT: %int_0.6a9: %i32 = int_value 0 [concrete] // CHECK:STDOUT: %Copy.facet.de4: %Copy.type = facet_value %i32, (%Copy.impl_witness.f17) [concrete] // CHECK:STDOUT: %Copy.WithSelf.Op.type.081: type = fn_type @Copy.WithSelf.Op, @Copy.WithSelf(%Copy.facet.de4) [concrete] // CHECK:STDOUT: %.8e2: type = fn_type_with_self_type %Copy.WithSelf.Op.type.081, %Copy.facet.de4 [concrete] // CHECK:STDOUT: %Int.as.Copy.impl.Op.specific_fn: = specific_function %Int.as.Copy.impl.Op.664, @Int.as.Copy.impl.Op(%int_32) [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: .IntLiteral = %Core.IntLiteral // CHECK:STDOUT: .Int = %Core.Int // CHECK:STDOUT: .Iterate = %Core.Iterate // CHECK:STDOUT: .Optional = %Core.Optional // CHECK:STDOUT: .Copy = %Core.Copy // CHECK:STDOUT: .OrderedWith = %Core.OrderedWith // CHECK:STDOUT: .Inc = %Core.Inc // CHECK:STDOUT: .Destroy = %Core.Destroy // CHECK:STDOUT: .ImplicitAs = %Core.ImplicitAs // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: %Core.IntLiteral: %IntLiteral.type = import_ref Core//prelude/types/int_literal, IntLiteral, loaded [concrete = constants.%IntLiteral] // CHECK:STDOUT: %Core.Int: %Int.type = import_ref Core//prelude/types/int, Int, loaded [concrete = constants.%Int.generic] // CHECK:STDOUT: %Core.Iterate: type = import_ref Core//prelude/iterate, Iterate, loaded [concrete = constants.%Iterate.type] // CHECK:STDOUT: %Core.import_ref.39b: %Iterate.assoc_type = import_ref Core//prelude/iterate, loc{{\d+_\d+}}, loaded [concrete = constants.%assoc0.4bd] // CHECK:STDOUT: %Core.import_ref.0b0: %Iterate.assoc_type = import_ref Core//prelude/iterate, loc{{\d+_\d+}}, loaded [concrete = constants.%assoc1.a27] // CHECK:STDOUT: %Core.import_ref.d20: @Optional.%Optional.None.type (%Optional.None.type.fc5) = import_ref Core//prelude/iterate, inst{{[0-9A-F]+}} [indirect], loaded [symbolic = @Optional.%Optional.None (constants.%Optional.None.fcb)] // CHECK:STDOUT: %Core.import_ref.aeb: @Optional.%Optional.Some.type (%Optional.Some.type.eaa) = import_ref Core//prelude/iterate, inst{{[0-9A-F]+}} [indirect], loaded [symbolic = @Optional.%Optional.Some (constants.%Optional.Some.6ca)] // CHECK:STDOUT: %Core.import_ref.cd6: type = import_ref Core//prelude/iterate, loc{{\d+_\d+}}, loaded [concrete = %CursorType] // CHECK:STDOUT: %CursorType: type = assoc_const_decl @CursorType [concrete] {} // CHECK:STDOUT: %Core.import_ref.f74: %facet_type.7e2 = import_ref Core//prelude/iterate, loc{{\d+_\d+}}, loaded [concrete = %ElementType] // CHECK:STDOUT: %ElementType: %facet_type.7e2 = assoc_const_decl @ElementType [concrete] {} // CHECK:STDOUT: %Core.import_ref.18d: @Int.as.Copy.impl.%Int.as.Copy.impl.Op.type (%Int.as.Copy.impl.Op.type.824) = import_ref Core//prelude/types/int, loc{{\d+_\d+}}, loaded [symbolic = @Int.as.Copy.impl.%Int.as.Copy.impl.Op (constants.%Int.as.Copy.impl.Op.9b9)] // CHECK:STDOUT: %Copy.impl_witness_table.e76 = impl_witness_table (%Core.import_ref.18d), @Int.as.Copy.impl [concrete] // CHECK:STDOUT: %Core.Optional: %Optional.type = import_ref Core//prelude/types/optional, Optional, loaded [concrete = constants.%Optional.generic] // CHECK:STDOUT: %Core.Copy: type = import_ref Core//prelude/copy, Copy, loaded [concrete = constants.%Copy.type] // CHECK:STDOUT: %Core.OrderedWith: %OrderedWith.type.270 = import_ref Core//prelude/operators/comparison, OrderedWith, loaded [concrete = constants.%OrderedWith.generic] // CHECK:STDOUT: %Core.import_ref.7e9: @OrderedWith.WithSelf.%OrderedWith.assoc_type (%OrderedWith.assoc_type.2b3) = import_ref Core//prelude/operators/comparison, loc{{\d+_\d+}}, loaded [symbolic = @OrderedWith.WithSelf.%assoc0 (constants.%assoc0.666)] // CHECK:STDOUT: %Core.import_ref.255: @OrderedWith.WithSelf.%OrderedWith.WithSelf.Less.type (%OrderedWith.WithSelf.Less.type.df2) = import_ref Core//prelude/operators/comparison, loc{{\d+_\d+}}, loaded [symbolic = @OrderedWith.WithSelf.%OrderedWith.WithSelf.Less (constants.%OrderedWith.WithSelf.Less.1a4)] // CHECK:STDOUT: %Core.import_ref.e2b = import_ref Core//prelude/operators/comparison, loc{{\d+_\d+}}, unloaded // CHECK:STDOUT: %Core.import_ref.12b: @Int.as.OrderedWith.impl.2e6.%Int.as.OrderedWith.impl.Less.type (%Int.as.OrderedWith.impl.Less.type.5f1) = import_ref Core//prelude/types/int, loc{{\d+_\d+}}, loaded [symbolic = @Int.as.OrderedWith.impl.2e6.%Int.as.OrderedWith.impl.Less (constants.%Int.as.OrderedWith.impl.Less.a9b)] // CHECK:STDOUT: %Core.import_ref.d3d = import_ref Core//prelude/types/int, loc{{\d+_\d+}}, unloaded // CHECK:STDOUT: %Core.import_ref.5ef = import_ref Core//prelude/types/int, loc{{\d+_\d+}}, unloaded // CHECK:STDOUT: %Core.import_ref.806 = import_ref Core//prelude/types/int, loc{{\d+_\d+}}, unloaded // CHECK:STDOUT: %OrderedWith.impl_witness_table.b93 = impl_witness_table (%Core.import_ref.12b, %Core.import_ref.d3d, %Core.import_ref.5ef, %Core.import_ref.806), @Int.as.OrderedWith.impl.2e6 [concrete] // CHECK:STDOUT: %Core.Inc: type = import_ref Core//prelude/operators/arithmetic, Inc, loaded [concrete = constants.%Inc.type] // CHECK:STDOUT: %Core.Destroy: type = import_ref Core//prelude/destroy, Destroy, loaded [concrete = constants.%Destroy.type] // CHECK:STDOUT: %Core.ImplicitAs: %ImplicitAs.type.cc7 = import_ref Core//prelude/operators/as, ImplicitAs, loaded [concrete = constants.%ImplicitAs.generic] // CHECK:STDOUT: %Core.import_ref.42d: @Core.IntLiteral.as.ImplicitAs.impl.%Core.IntLiteral.as.ImplicitAs.impl.Convert.type (%Core.IntLiteral.as.ImplicitAs.impl.Convert.type.4e6) = import_ref Core//prelude/types/int, loc{{\d+_\d+}}, loaded [symbolic = @Core.IntLiteral.as.ImplicitAs.impl.%Core.IntLiteral.as.ImplicitAs.impl.Convert (constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.3c2)] // CHECK:STDOUT: %ImplicitAs.impl_witness_table.74f = impl_witness_table (%Core.import_ref.42d), @Core.IntLiteral.as.ImplicitAs.impl [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .IntRange = %IntRange.decl // CHECK:STDOUT: .Range = %Range.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %IntRange.decl: %IntRange.type = class_decl @IntRange [concrete = constants.%IntRange.generic] { // CHECK:STDOUT: %N.patt: %pattern_type.dc0 = symbolic_binding_pattern N, 0 [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %.loc4_36.1: type = splice_block %.loc4_36.3 [concrete = Core.IntLiteral] { // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self = constants.%.Self.c39] // CHECK:STDOUT: %Core.ref.loc4: = name_ref Core, imports.%Core [concrete = imports.%Core] // CHECK:STDOUT: %IntLiteral.ref: %IntLiteral.type = name_ref IntLiteral, imports.%Core.IntLiteral [concrete = constants.%IntLiteral] // CHECK:STDOUT: %IntLiteral.call: init type = call %IntLiteral.ref() [concrete = Core.IntLiteral] // CHECK:STDOUT: %.loc4_36.2: type = value_of_initializer %IntLiteral.call [concrete = Core.IntLiteral] // CHECK:STDOUT: %.loc4_36.3: type = converted %IntLiteral.call, %.loc4_36.2 [concrete = Core.IntLiteral] // CHECK:STDOUT: } // CHECK:STDOUT: %N.loc4_16.2: Core.IntLiteral = symbolic_binding N, 0 [symbolic = %N.loc4_16.1 (constants.%N)] // CHECK:STDOUT: } // CHECK:STDOUT: %Range.decl: %Range.type = fn_decl @Range [concrete = constants.%Range] { // CHECK:STDOUT: %end.patt: %pattern_type.7ce = value_binding_pattern end [concrete] // CHECK:STDOUT: %end.param_patt: %pattern_type.7ce = value_param_pattern %end.patt [concrete] // CHECK:STDOUT: %return.patt: %pattern_type.615 = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: %pattern_type.615 = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %IntRange.ref.loc26: %IntRange.type = name_ref IntRange, file.%IntRange.decl [concrete = constants.%IntRange.generic] // CHECK:STDOUT: %int_32.loc26: Core.IntLiteral = int_value 32 [concrete = constants.%int_32] // CHECK:STDOUT: %IntRange.loc26: type = class_type @IntRange, @IntRange(constants.%int_32) [concrete = constants.%IntRange.a89] // CHECK:STDOUT: %.loc26_34.2: Core.Form = init_form %IntRange.loc26 [concrete = constants.%.aec] // CHECK:STDOUT: %end.param: %i32 = value_param call_param0 // CHECK:STDOUT: %i32: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %end: %i32 = value_binding end, %end.param // CHECK:STDOUT: %return.param: ref %IntRange.a89 = out_param call_param1 // CHECK:STDOUT: %return: ref %IntRange.a89 = return_slot %return.param // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic impl @IntRange.as.Iterate.impl(@IntRange.%N.loc4_16.2: Core.IntLiteral) { // CHECK:STDOUT: %N: Core.IntLiteral = symbolic_binding N, 0 [symbolic = %N (constants.%N)] // CHECK:STDOUT: %IntRange: type = class_type @IntRange, @IntRange(%N) [symbolic = %IntRange (constants.%IntRange.265)] // CHECK:STDOUT: %Int.loc9_54.1: type = class_type @Int, @Int(%N) [symbolic = %Int.loc9_54.1 (constants.%Int.fc6021.1)] // CHECK:STDOUT: %Destroy.lookup_impl_witness: = lookup_impl_witness %Int.loc9_54.1, @Destroy [symbolic = %Destroy.lookup_impl_witness (constants.%Destroy.lookup_impl_witness.93c)] // CHECK:STDOUT: %.loc9_85.1: require_specific_def_type = require_specific_def @Int.as.Copy.impl(%N) [symbolic = %.loc9_85.1 (constants.%.4f8)] // CHECK:STDOUT: %Copy.lookup_impl_witness: = lookup_impl_witness %Int.loc9_54.1, @Copy [symbolic = %Copy.lookup_impl_witness (constants.%Copy.lookup_impl_witness.7a8)] // CHECK:STDOUT: %facet_value.loc9_85.1: %facet_type.7e2 = facet_value %Int.loc9_54.1, (%Destroy.lookup_impl_witness, %Copy.lookup_impl_witness) [symbolic = %facet_value.loc9_85.1 (constants.%facet_value)] // CHECK:STDOUT: %Iterate_where.type: type = facet_type <@Iterate where constants.%impl.elem0.3b1 = %facet_value.loc9_85.1 and constants.%impl.elem1.49e = %Int.loc9_54.1> [symbolic = %Iterate_where.type (constants.%Iterate_where.type)] // CHECK:STDOUT: %Iterate.impl_witness.loc9_87.2: = impl_witness %Iterate.impl_witness_table, @IntRange.as.Iterate.impl(%N) [symbolic = %Iterate.impl_witness.loc9_87.2 (constants.%Iterate.impl_witness)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete: = require_complete_type %Iterate_where.type [symbolic = %require_complete (constants.%require_complete.234)] // CHECK:STDOUT: %IntRange.as.Iterate.impl.NewCursor.type: type = fn_type @IntRange.as.Iterate.impl.NewCursor, @IntRange.as.Iterate.impl(%N) [symbolic = %IntRange.as.Iterate.impl.NewCursor.type (constants.%IntRange.as.Iterate.impl.NewCursor.type)] // CHECK:STDOUT: %IntRange.as.Iterate.impl.NewCursor: @IntRange.as.Iterate.impl.%IntRange.as.Iterate.impl.NewCursor.type (%IntRange.as.Iterate.impl.NewCursor.type) = struct_value () [symbolic = %IntRange.as.Iterate.impl.NewCursor (constants.%IntRange.as.Iterate.impl.NewCursor)] // CHECK:STDOUT: %.loc11: require_specific_def_type = require_specific_def @T.binding.as_type.as.OptionalStorage.impl(%facet_value.loc9_85.1) [symbolic = %.loc11 (constants.%.63c)] // CHECK:STDOUT: %IntRange.as.Iterate.impl.Next.type: type = fn_type @IntRange.as.Iterate.impl.Next, @IntRange.as.Iterate.impl(%N) [symbolic = %IntRange.as.Iterate.impl.Next.type (constants.%IntRange.as.Iterate.impl.Next.type)] // CHECK:STDOUT: %IntRange.as.Iterate.impl.Next: @IntRange.as.Iterate.impl.%IntRange.as.Iterate.impl.Next.type (%IntRange.as.Iterate.impl.Next.type) = struct_value () [symbolic = %IntRange.as.Iterate.impl.Next (constants.%IntRange.as.Iterate.impl.Next)] // CHECK:STDOUT: // CHECK:STDOUT: impl: %Self.ref as %.loc9_24 { // CHECK:STDOUT: %IntRange.as.Iterate.impl.NewCursor.decl: @IntRange.as.Iterate.impl.%IntRange.as.Iterate.impl.NewCursor.type (%IntRange.as.Iterate.impl.NewCursor.type) = fn_decl @IntRange.as.Iterate.impl.NewCursor [symbolic = @IntRange.as.Iterate.impl.%IntRange.as.Iterate.impl.NewCursor (constants.%IntRange.as.Iterate.impl.NewCursor)] { // CHECK:STDOUT: %self.patt: @IntRange.as.Iterate.impl.NewCursor.%pattern_type.loc10_18 (%pattern_type.b16) = value_binding_pattern self [concrete] // CHECK:STDOUT: %self.param_patt: @IntRange.as.Iterate.impl.NewCursor.%pattern_type.loc10_18 (%pattern_type.b16) = value_param_pattern %self.patt [concrete] // CHECK:STDOUT: %return.patt: @IntRange.as.Iterate.impl.NewCursor.%pattern_type.loc10_32 (%pattern_type.764eab.1) = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: @IntRange.as.Iterate.impl.NewCursor.%pattern_type.loc10_32 (%pattern_type.764eab.1) = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %Core.ref: = name_ref Core, imports.%Core [concrete = imports.%Core] // CHECK:STDOUT: %Int.ref: %Int.type = name_ref Int, imports.%Core.Int [concrete = constants.%Int.generic] // CHECK:STDOUT: %N.ref: Core.IntLiteral = name_ref N, @IntRange.%N.loc4_16.2 [symbolic = %N (constants.%N)] // CHECK:STDOUT: %Int.loc10_45.2: type = class_type @Int, @Int(constants.%N) [symbolic = %Int.loc10_45.1 (constants.%Int.fc6021.1)] // CHECK:STDOUT: %.loc10_45.2: Core.Form = init_form %Int.loc10_45.2 [symbolic = %.loc10_45.1 (constants.%.83cba3.1)] // CHECK:STDOUT: %self.param: @IntRange.as.Iterate.impl.NewCursor.%IntRange (%IntRange.265) = value_param call_param0 // CHECK:STDOUT: %.loc10_24.1: type = splice_block %Self.ref [symbolic = %IntRange (constants.%IntRange.265)] { // CHECK:STDOUT: %.loc10_24.2: type = specific_constant constants.%IntRange.265, @IntRange(constants.%N) [symbolic = %IntRange (constants.%IntRange.265)] // CHECK:STDOUT: %Self.ref: type = name_ref Self, %.loc10_24.2 [symbolic = %IntRange (constants.%IntRange.265)] // CHECK:STDOUT: } // CHECK:STDOUT: %self: @IntRange.as.Iterate.impl.NewCursor.%IntRange (%IntRange.265) = value_binding self, %self.param // CHECK:STDOUT: %return.param: ref @IntRange.as.Iterate.impl.NewCursor.%Int.loc10_45.1 (%Int.fc6021.1) = out_param call_param1 // CHECK:STDOUT: %return: ref @IntRange.as.Iterate.impl.NewCursor.%Int.loc10_45.1 (%Int.fc6021.1) = return_slot %return.param // CHECK:STDOUT: } // CHECK:STDOUT: %IntRange.as.Iterate.impl.Next.decl: @IntRange.as.Iterate.impl.%IntRange.as.Iterate.impl.Next.type (%IntRange.as.Iterate.impl.Next.type) = fn_decl @IntRange.as.Iterate.impl.Next [symbolic = @IntRange.as.Iterate.impl.%IntRange.as.Iterate.impl.Next (constants.%IntRange.as.Iterate.impl.Next)] { // CHECK:STDOUT: %self.patt: @IntRange.as.Iterate.impl.Next.%pattern_type.loc11_13 (%pattern_type.b16) = value_binding_pattern self [concrete] // CHECK:STDOUT: %self.param_patt: @IntRange.as.Iterate.impl.Next.%pattern_type.loc11_13 (%pattern_type.b16) = value_param_pattern %self.patt [concrete] // CHECK:STDOUT: %cursor.patt: @IntRange.as.Iterate.impl.Next.%pattern_type.loc11_25 (%pattern_type.bda) = value_binding_pattern cursor [concrete] // CHECK:STDOUT: %cursor.param_patt: @IntRange.as.Iterate.impl.Next.%pattern_type.loc11_25 (%pattern_type.bda) = value_param_pattern %cursor.patt [concrete] // CHECK:STDOUT: %return.patt: @IntRange.as.Iterate.impl.Next.%pattern_type.loc11_47 (%pattern_type.0c2) = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: @IntRange.as.Iterate.impl.Next.%pattern_type.loc11_47 (%pattern_type.0c2) = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %Core.ref.loc11_50: = name_ref Core, imports.%Core [concrete = imports.%Core] // CHECK:STDOUT: %Optional.ref.loc11: %Optional.type = name_ref Optional, imports.%Core.Optional [concrete = constants.%Optional.generic] // CHECK:STDOUT: %Core.ref.loc11_64: = name_ref Core, imports.%Core [concrete = imports.%Core] // CHECK:STDOUT: %Int.ref.loc11_68: %Int.type = name_ref Int, imports.%Core.Int [concrete = constants.%Int.generic] // CHECK:STDOUT: %N.ref.loc11_73: Core.IntLiteral = name_ref N, @IntRange.%N.loc4_16.2 [symbolic = %N (constants.%N)] // CHECK:STDOUT: %Int.loc11_74: type = class_type @Int, @Int(constants.%N) [symbolic = %Int.loc11_43.1 (constants.%Int.fc6021.1)] // CHECK:STDOUT: %OptionalStorage.facet.loc11_75.2: %OptionalStorage.type = facet_value %Int.loc11_74, (constants.%OptionalStorage.lookup_impl_witness.b62) [symbolic = %OptionalStorage.facet.loc11_75.1 (constants.%OptionalStorage.facet.01e)] // CHECK:STDOUT: %.loc11_75.5: %OptionalStorage.type = converted %Int.loc11_74, %OptionalStorage.facet.loc11_75.2 [symbolic = %OptionalStorage.facet.loc11_75.1 (constants.%OptionalStorage.facet.01e)] // CHECK:STDOUT: %Optional.loc11_75.2: type = class_type @Optional, @Optional(constants.%OptionalStorage.facet.01e) [symbolic = %Optional.loc11_75.1 (constants.%Optional.e48)] // CHECK:STDOUT: %.loc11_75.6: Core.Form = init_form %Optional.loc11_75.2 [symbolic = %.loc11_75.4 (constants.%.949)] // CHECK:STDOUT: %self.param: @IntRange.as.Iterate.impl.Next.%IntRange (%IntRange.265) = value_param call_param0 // CHECK:STDOUT: %.loc11_19.1: type = splice_block %Self.ref [symbolic = %IntRange (constants.%IntRange.265)] { // CHECK:STDOUT: %.loc11_19.2: type = specific_constant constants.%IntRange.265, @IntRange(constants.%N) [symbolic = %IntRange (constants.%IntRange.265)] // CHECK:STDOUT: %Self.ref: type = name_ref Self, %.loc11_19.2 [symbolic = %IntRange (constants.%IntRange.265)] // CHECK:STDOUT: } // CHECK:STDOUT: %self: @IntRange.as.Iterate.impl.Next.%IntRange (%IntRange.265) = value_binding self, %self.param // CHECK:STDOUT: %cursor.param: @IntRange.as.Iterate.impl.Next.%ptr.loc11_44.1 (%ptr.c9c) = value_param call_param1 // CHECK:STDOUT: %.loc11_44: type = splice_block %ptr.loc11_44.2 [symbolic = %ptr.loc11_44.1 (constants.%ptr.c9c)] { // CHECK:STDOUT: %Core.ref.loc11_33: = name_ref Core, imports.%Core [concrete = imports.%Core] // CHECK:STDOUT: %Int.ref.loc11_37: %Int.type = name_ref Int, imports.%Core.Int [concrete = constants.%Int.generic] // CHECK:STDOUT: %N.ref.loc11_42: Core.IntLiteral = name_ref N, @IntRange.%N.loc4_16.2 [symbolic = %N (constants.%N)] // CHECK:STDOUT: %Int.loc11_43.2: type = class_type @Int, @Int(constants.%N) [symbolic = %Int.loc11_43.1 (constants.%Int.fc6021.1)] // CHECK:STDOUT: %ptr.loc11_44.2: type = ptr_type %Int.loc11_43.2 [symbolic = %ptr.loc11_44.1 (constants.%ptr.c9c)] // CHECK:STDOUT: } // CHECK:STDOUT: %cursor: @IntRange.as.Iterate.impl.Next.%ptr.loc11_44.1 (%ptr.c9c) = value_binding cursor, %cursor.param // CHECK:STDOUT: %return.param: ref @IntRange.as.Iterate.impl.Next.%Optional.loc11_75.1 (%Optional.e48) = out_param call_param2 // CHECK:STDOUT: %return: ref @IntRange.as.Iterate.impl.Next.%Optional.loc11_75.1 (%Optional.e48) = return_slot %return.param // CHECK:STDOUT: } // CHECK:STDOUT: %Iterate.impl_witness_table = impl_witness_table (%impl_witness_assoc_constant.loc9_87.1, %impl_witness_assoc_constant.loc9_87.2, %IntRange.as.Iterate.impl.NewCursor.decl, %IntRange.as.Iterate.impl.Next.decl), @IntRange.as.Iterate.impl [concrete] // CHECK:STDOUT: %Iterate.impl_witness.loc9_87.1: = impl_witness %Iterate.impl_witness_table, @IntRange.as.Iterate.impl(constants.%N) [symbolic = %Iterate.impl_witness.loc9_87.2 (constants.%Iterate.impl_witness)] // CHECK:STDOUT: %impl_witness_assoc_constant.loc9_87.1: %facet_type.7e2 = impl_witness_assoc_constant constants.%facet_value [symbolic = %facet_value.loc9_85.1 (constants.%facet_value)] // CHECK:STDOUT: %impl_witness_assoc_constant.loc9_87.2: type = impl_witness_assoc_constant constants.%Int.fc6021.1 [symbolic = %Int.loc9_54.1 (constants.%Int.fc6021.1)] // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .N = // CHECK:STDOUT: .NewCursor = %IntRange.as.Iterate.impl.NewCursor.decl // CHECK:STDOUT: .Next = %IntRange.as.Iterate.impl.Next.decl // CHECK:STDOUT: witness = %Iterate.impl_witness.loc9_87.1 // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic class @IntRange(%N.loc4_16.2: Core.IntLiteral) { // CHECK:STDOUT: %N.loc4_16.1: Core.IntLiteral = symbolic_binding N, 0 [symbolic = %N.loc4_16.1 (constants.%N)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %IntRange.Make.type: type = fn_type @IntRange.Make, @IntRange(%N.loc4_16.1) [symbolic = %IntRange.Make.type (constants.%IntRange.Make.type.1df)] // CHECK:STDOUT: %IntRange.Make: @IntRange.%IntRange.Make.type (%IntRange.Make.type.1df) = struct_value () [symbolic = %IntRange.Make (constants.%IntRange.Make.8a9)] // CHECK:STDOUT: %.loc9: require_specific_def_type = require_specific_def @Int.as.Copy.impl(%N.loc4_16.1) [symbolic = %.loc9 (constants.%.4f8)] // CHECK:STDOUT: %Int.loc22_32.2: type = class_type @Int, @Int(%N.loc4_16.1) [symbolic = %Int.loc22_32.2 (constants.%Int.fc6021.1)] // CHECK:STDOUT: %require_complete: = require_complete_type %Int.loc22_32.2 [symbolic = %require_complete (constants.%require_complete.9019d7.1)] // CHECK:STDOUT: %IntRange: type = class_type @IntRange, @IntRange(%N.loc4_16.1) [symbolic = %IntRange (constants.%IntRange.265)] // CHECK:STDOUT: %IntRange.elem: type = unbound_element_type %IntRange, %Int.loc22_32.2 [symbolic = %IntRange.elem (constants.%IntRange.elem.541)] // CHECK:STDOUT: %struct_type.start.end: type = struct_type {.start: @IntRange.%Int.loc22_32.2 (%Int.fc6021.1), .end: @IntRange.%Int.loc22_32.2 (%Int.fc6021.1)} [symbolic = %struct_type.start.end (constants.%struct_type.start.end.ff1)] // CHECK:STDOUT: %complete_type.loc24_1.2: = complete_type_witness %struct_type.start.end [symbolic = %complete_type.loc24_1.2 (constants.%complete_type.427)] // CHECK:STDOUT: // CHECK:STDOUT: class { // CHECK:STDOUT: %IntRange.Make.decl: @IntRange.%IntRange.Make.type (%IntRange.Make.type.1df) = fn_decl @IntRange.Make [symbolic = @IntRange.%IntRange.Make (constants.%IntRange.Make.8a9)] { // CHECK:STDOUT: %start.patt: @IntRange.Make.%pattern_type.loc5_11 (%pattern_type.764eab.1) = value_binding_pattern start [concrete] // CHECK:STDOUT: %start.param_patt: @IntRange.Make.%pattern_type.loc5_11 (%pattern_type.764eab.1) = value_param_pattern %start.patt [concrete] // CHECK:STDOUT: %end.patt: @IntRange.Make.%pattern_type.loc5_11 (%pattern_type.764eab.1) = value_binding_pattern end [concrete] // CHECK:STDOUT: %end.param_patt: @IntRange.Make.%pattern_type.loc5_11 (%pattern_type.764eab.1) = value_param_pattern %end.patt [concrete] // CHECK:STDOUT: %return.patt: @IntRange.Make.%pattern_type.loc5_49 (%pattern_type.b16) = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: @IntRange.Make.%pattern_type.loc5_49 (%pattern_type.b16) = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %.loc5_52.2: type = specific_constant constants.%IntRange.265, @IntRange(constants.%N) [symbolic = %IntRange (constants.%IntRange.265)] // CHECK:STDOUT: %Self.ref: type = name_ref Self, %.loc5_52.2 [symbolic = %IntRange (constants.%IntRange.265)] // CHECK:STDOUT: %.loc5_52.3: Core.Form = init_form %Self.ref [symbolic = %.loc5_52.1 (constants.%.e1e)] // CHECK:STDOUT: %start.param: @IntRange.Make.%Int.loc5_28.1 (%Int.fc6021.1) = value_param call_param0 // CHECK:STDOUT: %.loc5_28: type = splice_block %Int.loc5_28.2 [symbolic = %Int.loc5_28.1 (constants.%Int.fc6021.1)] { // CHECK:STDOUT: %Core.ref.loc5_18: = name_ref Core, imports.%Core [concrete = imports.%Core] // CHECK:STDOUT: %Int.ref.loc5_22: %Int.type = name_ref Int, imports.%Core.Int [concrete = constants.%Int.generic] // CHECK:STDOUT: %N.ref.loc5_27: Core.IntLiteral = name_ref N, @IntRange.%N.loc4_16.2 [symbolic = %N (constants.%N)] // CHECK:STDOUT: %Int.loc5_28.2: type = class_type @Int, @Int(constants.%N) [symbolic = %Int.loc5_28.1 (constants.%Int.fc6021.1)] // CHECK:STDOUT: } // CHECK:STDOUT: %start: @IntRange.Make.%Int.loc5_28.1 (%Int.fc6021.1) = value_binding start, %start.param // CHECK:STDOUT: %end.param: @IntRange.Make.%Int.loc5_28.1 (%Int.fc6021.1) = value_param call_param1 // CHECK:STDOUT: %.loc5_46: type = splice_block %Int.loc5_46 [symbolic = %Int.loc5_28.1 (constants.%Int.fc6021.1)] { // CHECK:STDOUT: %Core.ref.loc5_36: = name_ref Core, imports.%Core [concrete = imports.%Core] // CHECK:STDOUT: %Int.ref.loc5_40: %Int.type = name_ref Int, imports.%Core.Int [concrete = constants.%Int.generic] // CHECK:STDOUT: %N.ref.loc5_45: Core.IntLiteral = name_ref N, @IntRange.%N.loc4_16.2 [symbolic = %N (constants.%N)] // CHECK:STDOUT: %Int.loc5_46: type = class_type @Int, @Int(constants.%N) [symbolic = %Int.loc5_28.1 (constants.%Int.fc6021.1)] // CHECK:STDOUT: } // CHECK:STDOUT: %end: @IntRange.Make.%Int.loc5_28.1 (%Int.fc6021.1) = value_binding end, %end.param // CHECK:STDOUT: %return.param: ref @IntRange.Make.%IntRange (%IntRange.265) = out_param call_param2 // CHECK:STDOUT: %return: ref @IntRange.Make.%IntRange (%IntRange.265) = return_slot %return.param // CHECK:STDOUT: } // CHECK:STDOUT: impl_decl @IntRange.as.Iterate.impl [concrete] {} { // CHECK:STDOUT: %Self.ref: type = name_ref Self, constants.%IntRange.265 [symbolic = %IntRange (constants.%IntRange.265)] // CHECK:STDOUT: %Core.ref.loc9_11: = name_ref Core, imports.%Core [concrete = imports.%Core] // CHECK:STDOUT: %Iterate.ref: type = name_ref Iterate, imports.%Core.Iterate [concrete = constants.%Iterate.type] // CHECK:STDOUT: %.Self: %Iterate.type = symbolic_binding .Self [symbolic_self = constants.%.Self.2b2] // CHECK:STDOUT: %.Self.ref.loc9_30: %Iterate.type = name_ref .Self, %.Self [symbolic_self = constants.%.Self.2b2] // CHECK:STDOUT: %.Self.as_type.loc9_30: type = facet_access_type %.Self.ref.loc9_30 [symbolic_self = constants.%.Self.binding.as_type] // CHECK:STDOUT: %.loc9_30: type = converted %.Self.ref.loc9_30, %.Self.as_type.loc9_30 [symbolic_self = constants.%.Self.binding.as_type] // CHECK:STDOUT: %CursorType.ref: %Iterate.assoc_type = name_ref CursorType, imports.%Core.import_ref.0b0 [concrete = constants.%assoc1.a27] // CHECK:STDOUT: %impl.elem1: type = impl_witness_access constants.%Iterate.lookup_impl_witness.53f, element1 [symbolic_self = constants.%impl.elem1.49e] // CHECK:STDOUT: %Core.ref.loc9_44: = name_ref Core, imports.%Core [concrete = imports.%Core] // CHECK:STDOUT: %Int.ref.loc9_48: %Int.type = name_ref Int, imports.%Core.Int [concrete = constants.%Int.generic] // CHECK:STDOUT: %N.ref.loc9_53: Core.IntLiteral = name_ref N, @IntRange.%N.loc4_16.2 [symbolic = %N (constants.%N)] // CHECK:STDOUT: %Int.loc9_54.2: type = class_type @Int, @Int(constants.%N) [symbolic = %Int.loc9_54.1 (constants.%Int.fc6021.1)] // CHECK:STDOUT: %.Self.ref.loc9_60: %Iterate.type = name_ref .Self, %.Self [symbolic_self = constants.%.Self.2b2] // CHECK:STDOUT: %.Self.as_type.loc9_60: type = facet_access_type %.Self.ref.loc9_60 [symbolic_self = constants.%.Self.binding.as_type] // CHECK:STDOUT: %.loc9_60: type = converted %.Self.ref.loc9_60, %.Self.as_type.loc9_60 [symbolic_self = constants.%.Self.binding.as_type] // CHECK:STDOUT: %ElementType.ref: %Iterate.assoc_type = name_ref ElementType, imports.%Core.import_ref.39b [concrete = constants.%assoc0.4bd] // CHECK:STDOUT: %impl.elem0: %facet_type.7e2 = impl_witness_access constants.%Iterate.lookup_impl_witness.53f, element0 [symbolic_self = constants.%impl.elem0.3b1] // CHECK:STDOUT: %Core.ref.loc9_75: = name_ref Core, imports.%Core [concrete = imports.%Core] // CHECK:STDOUT: %Int.ref.loc9_79: %Int.type = name_ref Int, imports.%Core.Int [concrete = constants.%Int.generic] // CHECK:STDOUT: %N.ref.loc9_84: Core.IntLiteral = name_ref N, @IntRange.%N.loc4_16.2 [symbolic = %N (constants.%N)] // CHECK:STDOUT: %Int.loc9_85: type = class_type @Int, @Int(constants.%N) [symbolic = %Int.loc9_54.1 (constants.%Int.fc6021.1)] // CHECK:STDOUT: %facet_value.loc9_85.2: %facet_type.7e2 = facet_value %Int.loc9_85, (constants.%Destroy.lookup_impl_witness.93c, constants.%Copy.lookup_impl_witness.7a8) [symbolic = %facet_value.loc9_85.1 (constants.%facet_value)] // CHECK:STDOUT: %.loc9_85.2: %facet_type.7e2 = converted %Int.loc9_85, %facet_value.loc9_85.2 [symbolic = %facet_value.loc9_85.1 (constants.%facet_value)] // CHECK:STDOUT: %.loc9_24: type = where_expr %.Self [symbolic = %Iterate_where.type (constants.%Iterate_where.type)] { // CHECK:STDOUT: requirement_base_facet_type constants.%Iterate.type // CHECK:STDOUT: requirement_rewrite %impl.elem1, %Int.loc9_54.2 // CHECK:STDOUT: requirement_rewrite %impl.elem0, %.loc9_85.2 // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: %Core.ref.loc22: = name_ref Core, imports.%Core [concrete = imports.%Core] // CHECK:STDOUT: %Int.ref.loc22: %Int.type = name_ref Int, imports.%Core.Int [concrete = constants.%Int.generic] // CHECK:STDOUT: %N.ref.loc22: Core.IntLiteral = name_ref N, %N.loc4_16.2 [symbolic = %N.loc4_16.1 (constants.%N)] // CHECK:STDOUT: %Int.loc22_32.1: type = class_type @Int, @Int(constants.%N) [symbolic = %Int.loc22_32.2 (constants.%Int.fc6021.1)] // CHECK:STDOUT: %.loc22: @IntRange.%IntRange.elem (%IntRange.elem.541) = field_decl start, element0 [concrete] // CHECK:STDOUT: %Core.ref.loc23: = name_ref Core, imports.%Core [concrete = imports.%Core] // CHECK:STDOUT: %Int.ref.loc23: %Int.type = name_ref Int, imports.%Core.Int [concrete = constants.%Int.generic] // CHECK:STDOUT: %N.ref.loc23: Core.IntLiteral = name_ref N, %N.loc4_16.2 [symbolic = %N.loc4_16.1 (constants.%N)] // CHECK:STDOUT: %Int.loc23: type = class_type @Int, @Int(constants.%N) [symbolic = %Int.loc22_32.2 (constants.%Int.fc6021.1)] // CHECK:STDOUT: %.loc23: @IntRange.%IntRange.elem (%IntRange.elem.541) = field_decl end, element1 [concrete] // CHECK:STDOUT: %complete_type.loc24_1.1: = complete_type_witness constants.%struct_type.start.end.ff1 [symbolic = %complete_type.loc24_1.2 (constants.%complete_type.427)] // CHECK:STDOUT: complete_type_witness = %complete_type.loc24_1.1 // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%IntRange.265 // CHECK:STDOUT: .N = // CHECK:STDOUT: .Make = %IntRange.Make.decl // CHECK:STDOUT: .start [private] = %.loc22 // CHECK:STDOUT: .end [private] = %.loc23 // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic fn @IntRange.Make(@IntRange.%N.loc4_16.2: Core.IntLiteral) { // CHECK:STDOUT: %N: Core.IntLiteral = symbolic_binding N, 0 [symbolic = %N (constants.%N)] // CHECK:STDOUT: %Int.loc5_28.1: type = class_type @Int, @Int(%N) [symbolic = %Int.loc5_28.1 (constants.%Int.fc6021.1)] // CHECK:STDOUT: %pattern_type.loc5_11: type = pattern_type %Int.loc5_28.1 [symbolic = %pattern_type.loc5_11 (constants.%pattern_type.764eab.1)] // CHECK:STDOUT: %IntRange: type = class_type @IntRange, @IntRange(%N) [symbolic = %IntRange (constants.%IntRange.265)] // CHECK:STDOUT: %.loc5_52.1: Core.Form = init_form %IntRange [symbolic = %.loc5_52.1 (constants.%.e1e)] // CHECK:STDOUT: %pattern_type.loc5_49: type = pattern_type %IntRange [symbolic = %pattern_type.loc5_49 (constants.%pattern_type.b16)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete.loc5_16: = require_complete_type %Int.loc5_28.1 [symbolic = %require_complete.loc5_16 (constants.%require_complete.9019d7.1)] // CHECK:STDOUT: %require_complete.loc5_52: = require_complete_type %IntRange [symbolic = %require_complete.loc5_52 (constants.%require_complete.8a1)] // CHECK:STDOUT: %struct_type.start.end: type = struct_type {.start: @IntRange.Make.%Int.loc5_28.1 (%Int.fc6021.1), .end: @IntRange.Make.%Int.loc5_28.1 (%Int.fc6021.1)} [symbolic = %struct_type.start.end (constants.%struct_type.start.end.ff1)] // CHECK:STDOUT: %.loc6_22.1: require_specific_def_type = require_specific_def @Int.as.Copy.impl(%N) [symbolic = %.loc6_22.1 (constants.%.4f8)] // CHECK:STDOUT: %Copy.lookup_impl_witness: = lookup_impl_witness %Int.loc5_28.1, @Copy [symbolic = %Copy.lookup_impl_witness (constants.%Copy.lookup_impl_witness.7a8)] // CHECK:STDOUT: %Copy.facet: %Copy.type = facet_value %Int.loc5_28.1, (%Copy.lookup_impl_witness) [symbolic = %Copy.facet (constants.%Copy.facet.3b9)] // CHECK:STDOUT: %Copy.WithSelf.Op.type: type = fn_type @Copy.WithSelf.Op, @Copy.WithSelf(%Copy.facet) [symbolic = %Copy.WithSelf.Op.type (constants.%Copy.WithSelf.Op.type.e13)] // CHECK:STDOUT: %.loc6_22.2: type = fn_type_with_self_type %Copy.WithSelf.Op.type, %Copy.facet [symbolic = %.loc6_22.2 (constants.%.e29)] // CHECK:STDOUT: %impl.elem0.loc6_22.2: @IntRange.Make.%.loc6_22.2 (%.e29) = impl_witness_access %Copy.lookup_impl_witness, element0 [symbolic = %impl.elem0.loc6_22.2 (constants.%impl.elem0.694)] // CHECK:STDOUT: %specific_impl_fn.loc6_22.2: = specific_impl_function %impl.elem0.loc6_22.2, @Copy.WithSelf.Op(%Copy.facet) [symbolic = %specific_impl_fn.loc6_22.2 (constants.%specific_impl_fn.bd4)] // CHECK:STDOUT: // CHECK:STDOUT: fn(%start.param: @IntRange.Make.%Int.loc5_28.1 (%Int.fc6021.1), %end.param: @IntRange.Make.%Int.loc5_28.1 (%Int.fc6021.1)) -> out %return.param: @IntRange.Make.%IntRange (%IntRange.265) { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %start.ref: @IntRange.Make.%Int.loc5_28.1 (%Int.fc6021.1) = name_ref start, %start // CHECK:STDOUT: %end.ref: @IntRange.Make.%Int.loc5_28.1 (%Int.fc6021.1) = name_ref end, %end // CHECK:STDOUT: %.loc6_39.1: @IntRange.Make.%struct_type.start.end (%struct_type.start.end.ff1) = struct_literal (%start.ref, %end.ref) // CHECK:STDOUT: %impl.elem0.loc6_22.1: @IntRange.Make.%.loc6_22.2 (%.e29) = impl_witness_access constants.%Copy.lookup_impl_witness.7a8, element0 [symbolic = %impl.elem0.loc6_22.2 (constants.%impl.elem0.694)] // CHECK:STDOUT: %bound_method.loc6_22.1: = bound_method %start.ref, %impl.elem0.loc6_22.1 // CHECK:STDOUT: %specific_impl_fn.loc6_22.1: = specific_impl_function %impl.elem0.loc6_22.1, @Copy.WithSelf.Op(constants.%Copy.facet.3b9) [symbolic = %specific_impl_fn.loc6_22.2 (constants.%specific_impl_fn.bd4)] // CHECK:STDOUT: %bound_method.loc6_22.2: = bound_method %start.ref, %specific_impl_fn.loc6_22.1 // CHECK:STDOUT: %Copy.WithSelf.Op.call.loc6_22: init @IntRange.Make.%Int.loc5_28.1 (%Int.fc6021.1) = call %bound_method.loc6_22.2(%start.ref) // CHECK:STDOUT: %.loc6_39.2: ref @IntRange.Make.%Int.loc5_28.1 (%Int.fc6021.1) = class_element_access %return.param, element0 // CHECK:STDOUT: %.loc6_39.3: init @IntRange.Make.%Int.loc5_28.1 (%Int.fc6021.1) to %.loc6_39.2 = in_place_init %Copy.WithSelf.Op.call.loc6_22 // CHECK:STDOUT: %impl.elem0.loc6_36: @IntRange.Make.%.loc6_22.2 (%.e29) = impl_witness_access constants.%Copy.lookup_impl_witness.7a8, element0 [symbolic = %impl.elem0.loc6_22.2 (constants.%impl.elem0.694)] // CHECK:STDOUT: %bound_method.loc6_36.1: = bound_method %end.ref, %impl.elem0.loc6_36 // CHECK:STDOUT: %specific_impl_fn.loc6_36: = specific_impl_function %impl.elem0.loc6_36, @Copy.WithSelf.Op(constants.%Copy.facet.3b9) [symbolic = %specific_impl_fn.loc6_22.2 (constants.%specific_impl_fn.bd4)] // CHECK:STDOUT: %bound_method.loc6_36.2: = bound_method %end.ref, %specific_impl_fn.loc6_36 // CHECK:STDOUT: %Copy.WithSelf.Op.call.loc6_36: init @IntRange.Make.%Int.loc5_28.1 (%Int.fc6021.1) = call %bound_method.loc6_36.2(%end.ref) // CHECK:STDOUT: %.loc6_39.4: ref @IntRange.Make.%Int.loc5_28.1 (%Int.fc6021.1) = class_element_access %return.param, element1 // CHECK:STDOUT: %.loc6_39.5: init @IntRange.Make.%Int.loc5_28.1 (%Int.fc6021.1) to %.loc6_39.4 = in_place_init %Copy.WithSelf.Op.call.loc6_36 // CHECK:STDOUT: %.loc6_39.6: init @IntRange.Make.%IntRange (%IntRange.265) to %return.param = class_init (%.loc6_39.3, %.loc6_39.5) // CHECK:STDOUT: %.loc6_40: init @IntRange.Make.%IntRange (%IntRange.265) = converted %.loc6_39.1, %.loc6_39.6 // CHECK:STDOUT: return %.loc6_40 to %return.param // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic fn @IntRange.as.Iterate.impl.NewCursor(@IntRange.%N.loc4_16.2: Core.IntLiteral) { // CHECK:STDOUT: %N: Core.IntLiteral = symbolic_binding N, 0 [symbolic = %N (constants.%N)] // CHECK:STDOUT: %IntRange: type = class_type @IntRange, @IntRange(%N) [symbolic = %IntRange (constants.%IntRange.265)] // CHECK:STDOUT: %pattern_type.loc10_18: type = pattern_type %IntRange [symbolic = %pattern_type.loc10_18 (constants.%pattern_type.b16)] // CHECK:STDOUT: %Int.loc10_45.1: type = class_type @Int, @Int(%N) [symbolic = %Int.loc10_45.1 (constants.%Int.fc6021.1)] // CHECK:STDOUT: %.loc10_45.1: Core.Form = init_form %Int.loc10_45.1 [symbolic = %.loc10_45.1 (constants.%.83cba3.1)] // CHECK:STDOUT: %pattern_type.loc10_32: type = pattern_type %Int.loc10_45.1 [symbolic = %pattern_type.loc10_32 (constants.%pattern_type.764eab.1)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete.loc10_22: = require_complete_type %IntRange [symbolic = %require_complete.loc10_22 (constants.%require_complete.8a1)] // CHECK:STDOUT: %IntRange.elem: type = unbound_element_type %IntRange, %Int.loc10_45.1 [symbolic = %IntRange.elem (constants.%IntRange.elem.541)] // CHECK:STDOUT: %require_complete.loc10_60: = require_complete_type %Int.loc10_45.1 [symbolic = %require_complete.loc10_60 (constants.%require_complete.9019d7.1)] // CHECK:STDOUT: %.loc10_60.3: require_specific_def_type = require_specific_def @Int.as.Copy.impl(%N) [symbolic = %.loc10_60.3 (constants.%.4f8)] // CHECK:STDOUT: %Copy.lookup_impl_witness: = lookup_impl_witness %Int.loc10_45.1, @Copy [symbolic = %Copy.lookup_impl_witness (constants.%Copy.lookup_impl_witness.7a8)] // CHECK:STDOUT: %Copy.facet: %Copy.type = facet_value %Int.loc10_45.1, (%Copy.lookup_impl_witness) [symbolic = %Copy.facet (constants.%Copy.facet.3b9)] // CHECK:STDOUT: %Copy.WithSelf.Op.type: type = fn_type @Copy.WithSelf.Op, @Copy.WithSelf(%Copy.facet) [symbolic = %Copy.WithSelf.Op.type (constants.%Copy.WithSelf.Op.type.e13)] // CHECK:STDOUT: %.loc10_60.4: type = fn_type_with_self_type %Copy.WithSelf.Op.type, %Copy.facet [symbolic = %.loc10_60.4 (constants.%.e29)] // CHECK:STDOUT: %impl.elem0.loc10_60.2: @IntRange.as.Iterate.impl.NewCursor.%.loc10_60.4 (%.e29) = impl_witness_access %Copy.lookup_impl_witness, element0 [symbolic = %impl.elem0.loc10_60.2 (constants.%impl.elem0.694)] // CHECK:STDOUT: %specific_impl_fn.loc10_60.2: = specific_impl_function %impl.elem0.loc10_60.2, @Copy.WithSelf.Op(%Copy.facet) [symbolic = %specific_impl_fn.loc10_60.2 (constants.%specific_impl_fn.bd4)] // CHECK:STDOUT: // CHECK:STDOUT: fn(%self.param: @IntRange.as.Iterate.impl.NewCursor.%IntRange (%IntRange.265)) -> out %return.param: @IntRange.as.Iterate.impl.NewCursor.%Int.loc10_45.1 (%Int.fc6021.1) { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %self.ref: @IntRange.as.Iterate.impl.NewCursor.%IntRange (%IntRange.265) = name_ref self, %self // CHECK:STDOUT: %start.ref: @IntRange.as.Iterate.impl.NewCursor.%IntRange.elem (%IntRange.elem.541) = name_ref start, @IntRange.%.loc22 [concrete = @IntRange.%.loc22] // CHECK:STDOUT: %.loc10_60.1: ref @IntRange.as.Iterate.impl.NewCursor.%Int.loc10_45.1 (%Int.fc6021.1) = class_element_access %self.ref, element0 // CHECK:STDOUT: %.loc10_60.2: @IntRange.as.Iterate.impl.NewCursor.%Int.loc10_45.1 (%Int.fc6021.1) = acquire_value %.loc10_60.1 // CHECK:STDOUT: %impl.elem0.loc10_60.1: @IntRange.as.Iterate.impl.NewCursor.%.loc10_60.4 (%.e29) = impl_witness_access constants.%Copy.lookup_impl_witness.7a8, element0 [symbolic = %impl.elem0.loc10_60.2 (constants.%impl.elem0.694)] // CHECK:STDOUT: %bound_method.loc10_60.1: = bound_method %.loc10_60.2, %impl.elem0.loc10_60.1 // CHECK:STDOUT: %specific_impl_fn.loc10_60.1: = specific_impl_function %impl.elem0.loc10_60.1, @Copy.WithSelf.Op(constants.%Copy.facet.3b9) [symbolic = %specific_impl_fn.loc10_60.2 (constants.%specific_impl_fn.bd4)] // CHECK:STDOUT: %bound_method.loc10_60.2: = bound_method %.loc10_60.2, %specific_impl_fn.loc10_60.1 // CHECK:STDOUT: %Copy.WithSelf.Op.call: init @IntRange.as.Iterate.impl.NewCursor.%Int.loc10_45.1 (%Int.fc6021.1) = call %bound_method.loc10_60.2(%.loc10_60.2) // CHECK:STDOUT: return %Copy.WithSelf.Op.call // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic fn @IntRange.as.Iterate.impl.Next(@IntRange.%N.loc4_16.2: Core.IntLiteral) { // CHECK:STDOUT: %N: Core.IntLiteral = symbolic_binding N, 0 [symbolic = %N (constants.%N)] // CHECK:STDOUT: %IntRange: type = class_type @IntRange, @IntRange(%N) [symbolic = %IntRange (constants.%IntRange.265)] // CHECK:STDOUT: %pattern_type.loc11_13: type = pattern_type %IntRange [symbolic = %pattern_type.loc11_13 (constants.%pattern_type.b16)] // CHECK:STDOUT: %Int.loc11_43.1: type = class_type @Int, @Int(%N) [symbolic = %Int.loc11_43.1 (constants.%Int.fc6021.1)] // CHECK:STDOUT: %ptr.loc11_44.1: type = ptr_type %Int.loc11_43.1 [symbolic = %ptr.loc11_44.1 (constants.%ptr.c9c)] // CHECK:STDOUT: %pattern_type.loc11_25: type = pattern_type %ptr.loc11_44.1 [symbolic = %pattern_type.loc11_25 (constants.%pattern_type.bda)] // CHECK:STDOUT: %Destroy.lookup_impl_witness: = lookup_impl_witness %Int.loc11_43.1, @Destroy [symbolic = %Destroy.lookup_impl_witness (constants.%Destroy.lookup_impl_witness.93c)] // CHECK:STDOUT: %Copy.lookup_impl_witness: = lookup_impl_witness %Int.loc11_43.1, @Copy [symbolic = %Copy.lookup_impl_witness (constants.%Copy.lookup_impl_witness.7a8)] // CHECK:STDOUT: %facet_value: %facet_type.7e2 = facet_value %Int.loc11_43.1, (%Destroy.lookup_impl_witness, %Copy.lookup_impl_witness) [symbolic = %facet_value (constants.%facet_value)] // CHECK:STDOUT: %.loc11_75.3: require_specific_def_type = require_specific_def @T.binding.as_type.as.OptionalStorage.impl(%facet_value) [symbolic = %.loc11_75.3 (constants.%.63c)] // CHECK:STDOUT: %OptionalStorage.lookup_impl_witness: = lookup_impl_witness %Int.loc11_43.1, @OptionalStorage [symbolic = %OptionalStorage.lookup_impl_witness (constants.%OptionalStorage.lookup_impl_witness.b62)] // CHECK:STDOUT: %OptionalStorage.facet.loc11_75.1: %OptionalStorage.type = facet_value %Int.loc11_43.1, (%OptionalStorage.lookup_impl_witness) [symbolic = %OptionalStorage.facet.loc11_75.1 (constants.%OptionalStorage.facet.01e)] // CHECK:STDOUT: %Optional.loc11_75.1: type = class_type @Optional, @Optional(%OptionalStorage.facet.loc11_75.1) [symbolic = %Optional.loc11_75.1 (constants.%Optional.e48)] // CHECK:STDOUT: %.loc11_75.4: Core.Form = init_form %Optional.loc11_75.1 [symbolic = %.loc11_75.4 (constants.%.949)] // CHECK:STDOUT: %pattern_type.loc11_47: type = pattern_type %Optional.loc11_75.1 [symbolic = %pattern_type.loc11_47 (constants.%pattern_type.0c2)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete.loc11_17: = require_complete_type %IntRange [symbolic = %require_complete.loc11_17 (constants.%require_complete.8a1)] // CHECK:STDOUT: %require_complete.loc11_31: = require_complete_type %ptr.loc11_44.1 [symbolic = %require_complete.loc11_31 (constants.%require_complete.45c)] // CHECK:STDOUT: %require_complete.loc11_75: = require_complete_type %Optional.loc11_75.1 [symbolic = %require_complete.loc11_75 (constants.%require_complete.0bc)] // CHECK:STDOUT: %require_complete.loc12: = require_complete_type %Int.loc11_43.1 [symbolic = %require_complete.loc12 (constants.%require_complete.9019d7.1)] // CHECK:STDOUT: %pattern_type.loc12: type = pattern_type %Int.loc11_43.1 [symbolic = %pattern_type.loc12 (constants.%pattern_type.764eab.1)] // CHECK:STDOUT: %.loc12_32.3: require_specific_def_type = require_specific_def @Int.as.Copy.impl(%N) [symbolic = %.loc12_32.3 (constants.%.4f8)] // CHECK:STDOUT: %Copy.facet: %Copy.type = facet_value %Int.loc11_43.1, (%Copy.lookup_impl_witness) [symbolic = %Copy.facet (constants.%Copy.facet.3b9)] // CHECK:STDOUT: %Copy.WithSelf.Op.type: type = fn_type @Copy.WithSelf.Op, @Copy.WithSelf(%Copy.facet) [symbolic = %Copy.WithSelf.Op.type (constants.%Copy.WithSelf.Op.type.e13)] // CHECK:STDOUT: %.loc12_32.4: type = fn_type_with_self_type %Copy.WithSelf.Op.type, %Copy.facet [symbolic = %.loc12_32.4 (constants.%.e29)] // CHECK:STDOUT: %impl.elem0.loc12_32.2: @IntRange.as.Iterate.impl.Next.%.loc12_32.4 (%.e29) = impl_witness_access %Copy.lookup_impl_witness, element0 [symbolic = %impl.elem0.loc12_32.2 (constants.%impl.elem0.694)] // CHECK:STDOUT: %specific_impl_fn.loc12_32.2: = specific_impl_function %impl.elem0.loc12_32.2, @Copy.WithSelf.Op(%Copy.facet) [symbolic = %specific_impl_fn.loc12_32.2 (constants.%specific_impl_fn.bd4)] // CHECK:STDOUT: %IntRange.elem: type = unbound_element_type %IntRange, %Int.loc11_43.1 [symbolic = %IntRange.elem (constants.%IntRange.elem.541)] // CHECK:STDOUT: %OrderedWith.type.loc13_17.2: type = facet_type <@OrderedWith, @OrderedWith(%Int.loc11_43.1)> [symbolic = %OrderedWith.type.loc13_17.2 (constants.%OrderedWith.type.e44)] // CHECK:STDOUT: %require_complete.loc13: = require_complete_type %OrderedWith.type.loc13_17.2 [symbolic = %require_complete.loc13 (constants.%require_complete.dd3)] // CHECK:STDOUT: %OrderedWith.assoc_type: type = assoc_entity_type @OrderedWith, @OrderedWith(%Int.loc11_43.1) [symbolic = %OrderedWith.assoc_type (constants.%OrderedWith.assoc_type.215)] // CHECK:STDOUT: %assoc0: @IntRange.as.Iterate.impl.Next.%OrderedWith.assoc_type (%OrderedWith.assoc_type.215) = assoc_entity element0, imports.%Core.import_ref.255 [symbolic = %assoc0 (constants.%assoc0.15c)] // CHECK:STDOUT: %.loc13_17.2: require_specific_def_type = require_specific_def @Int.as.OrderedWith.impl.2e6(%N, %N) [symbolic = %.loc13_17.2 (constants.%.4b5)] // CHECK:STDOUT: %OrderedWith.impl_witness: = impl_witness imports.%OrderedWith.impl_witness_table.b93, @Int.as.OrderedWith.impl.2e6(%N, %N) [symbolic = %OrderedWith.impl_witness (constants.%OrderedWith.impl_witness.ea9)] // CHECK:STDOUT: %OrderedWith.facet: @IntRange.as.Iterate.impl.Next.%OrderedWith.type.loc13_17.2 (%OrderedWith.type.e44) = facet_value %Int.loc11_43.1, (%OrderedWith.impl_witness) [symbolic = %OrderedWith.facet (constants.%OrderedWith.facet)] // CHECK:STDOUT: %OrderedWith.WithSelf.Less.type: type = fn_type @OrderedWith.WithSelf.Less, @OrderedWith.WithSelf(%Int.loc11_43.1, %OrderedWith.facet) [symbolic = %OrderedWith.WithSelf.Less.type (constants.%OrderedWith.WithSelf.Less.type.3a2)] // CHECK:STDOUT: %.loc13_17.3: type = fn_type_with_self_type %OrderedWith.WithSelf.Less.type, %OrderedWith.facet [symbolic = %.loc13_17.3 (constants.%.48d)] // CHECK:STDOUT: %Int.as.OrderedWith.impl.Less.type: type = fn_type @Int.as.OrderedWith.impl.Less.1, @Int.as.OrderedWith.impl.2e6(%N, %N) [symbolic = %Int.as.OrderedWith.impl.Less.type (constants.%Int.as.OrderedWith.impl.Less.type.3f1)] // CHECK:STDOUT: %Int.as.OrderedWith.impl.Less: @IntRange.as.Iterate.impl.Next.%Int.as.OrderedWith.impl.Less.type (%Int.as.OrderedWith.impl.Less.type.3f1) = struct_value () [symbolic = %Int.as.OrderedWith.impl.Less (constants.%Int.as.OrderedWith.impl.Less.4cf)] // CHECK:STDOUT: %Int.as.OrderedWith.impl.Less.specific_fn: = specific_function %Int.as.OrderedWith.impl.Less, @Int.as.OrderedWith.impl.Less.1(%N, %N) [symbolic = %Int.as.OrderedWith.impl.Less.specific_fn (constants.%Int.as.OrderedWith.impl.Less.specific_fn.4c2)] // CHECK:STDOUT: %.loc14_9.1: require_specific_def_type = require_specific_def @Int.as.Inc.impl(%N) [symbolic = %.loc14_9.1 (constants.%.53b)] // CHECK:STDOUT: %Inc.lookup_impl_witness: = lookup_impl_witness %Int.loc11_43.1, @Inc [symbolic = %Inc.lookup_impl_witness (constants.%Inc.lookup_impl_witness)] // CHECK:STDOUT: %Inc.facet: %Inc.type = facet_value %Int.loc11_43.1, (%Inc.lookup_impl_witness) [symbolic = %Inc.facet (constants.%Inc.facet)] // CHECK:STDOUT: %Inc.WithSelf.Op.type: type = fn_type @Inc.WithSelf.Op, @Inc.WithSelf(%Inc.facet) [symbolic = %Inc.WithSelf.Op.type (constants.%Inc.WithSelf.Op.type.17b)] // CHECK:STDOUT: %.loc14_9.2: type = fn_type_with_self_type %Inc.WithSelf.Op.type, %Inc.facet [symbolic = %.loc14_9.2 (constants.%.9a6)] // CHECK:STDOUT: %impl.elem0.loc14_9.2: @IntRange.as.Iterate.impl.Next.%.loc14_9.2 (%.9a6) = impl_witness_access %Inc.lookup_impl_witness, element0 [symbolic = %impl.elem0.loc14_9.2 (constants.%impl.elem0.10c)] // CHECK:STDOUT: %specific_impl_fn.loc14_9.2: = specific_impl_function %impl.elem0.loc14_9.2, @Inc.WithSelf.Op(%Inc.facet) [symbolic = %specific_impl_fn.loc14_9.2 (constants.%specific_impl_fn.ed3)] // CHECK:STDOUT: %Optional.Some.type: type = fn_type @Optional.Some, @Optional(%OptionalStorage.facet.loc11_75.1) [symbolic = %Optional.Some.type (constants.%Optional.Some.type.f74)] // CHECK:STDOUT: %Optional.Some: @IntRange.as.Iterate.impl.Next.%Optional.Some.type (%Optional.Some.type.f74) = struct_value () [symbolic = %Optional.Some (constants.%Optional.Some.076)] // CHECK:STDOUT: %Optional.Some.specific_fn.loc15_42.2: = specific_function %Optional.Some, @Optional.Some(%OptionalStorage.facet.loc11_75.1) [symbolic = %Optional.Some.specific_fn.loc15_42.2 (constants.%Optional.Some.specific_fn)] // CHECK:STDOUT: %Destroy.facet: %Destroy.type = facet_value %Int.loc11_43.1, (%Destroy.lookup_impl_witness) [symbolic = %Destroy.facet (constants.%Destroy.facet.1c0)] // CHECK:STDOUT: %Destroy.WithSelf.Op.type: type = fn_type @Destroy.WithSelf.Op, @Destroy.WithSelf(%Destroy.facet) [symbolic = %Destroy.WithSelf.Op.type (constants.%Destroy.WithSelf.Op.type.297)] // CHECK:STDOUT: %.loc12_7: type = fn_type_with_self_type %Destroy.WithSelf.Op.type, %Destroy.facet [symbolic = %.loc12_7 (constants.%.e63)] // CHECK:STDOUT: %impl.elem0.loc12_7.3: @IntRange.as.Iterate.impl.Next.%.loc12_7 (%.e63) = impl_witness_access %Destroy.lookup_impl_witness, element0 [symbolic = %impl.elem0.loc12_7.3 (constants.%impl.elem0.602)] // CHECK:STDOUT: %specific_impl_fn.loc12_7.3: = specific_impl_function %impl.elem0.loc12_7.3, @Destroy.WithSelf.Op(%Destroy.facet) [symbolic = %specific_impl_fn.loc12_7.3 (constants.%specific_impl_fn.d65)] // CHECK:STDOUT: %Optional.None.type: type = fn_type @Optional.None, @Optional(%OptionalStorage.facet.loc11_75.1) [symbolic = %Optional.None.type (constants.%Optional.None.type.d56)] // CHECK:STDOUT: %Optional.None: @IntRange.as.Iterate.impl.Next.%Optional.None.type (%Optional.None.type.d56) = struct_value () [symbolic = %Optional.None (constants.%Optional.None.b26)] // CHECK:STDOUT: %Optional.None.specific_fn.loc17_42.2: = specific_function %Optional.None, @Optional.None(%OptionalStorage.facet.loc11_75.1) [symbolic = %Optional.None.specific_fn.loc17_42.2 (constants.%Optional.None.specific_fn)] // CHECK:STDOUT: // CHECK:STDOUT: fn(%self.param: @IntRange.as.Iterate.impl.Next.%IntRange (%IntRange.265), %cursor.param: @IntRange.as.Iterate.impl.Next.%ptr.loc11_44.1 (%ptr.c9c)) -> out %return.param: @IntRange.as.Iterate.impl.Next.%Optional.loc11_75.1 (%Optional.e48) { // CHECK:STDOUT: !entry: // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %value.patt: @IntRange.as.Iterate.impl.Next.%pattern_type.loc12 (%pattern_type.764eab.1) = ref_binding_pattern value [concrete] // CHECK:STDOUT: %value.var_patt: @IntRange.as.Iterate.impl.Next.%pattern_type.loc12 (%pattern_type.764eab.1) = var_pattern %value.patt [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %value.var: ref @IntRange.as.Iterate.impl.Next.%Int.loc11_43.1 (%Int.fc6021.1) = var %value.var_patt // CHECK:STDOUT: %cursor.ref.loc12: @IntRange.as.Iterate.impl.Next.%ptr.loc11_44.1 (%ptr.c9c) = name_ref cursor, %cursor // CHECK:STDOUT: %.loc12_32.1: ref @IntRange.as.Iterate.impl.Next.%Int.loc11_43.1 (%Int.fc6021.1) = deref %cursor.ref.loc12 // CHECK:STDOUT: %.loc12_32.2: @IntRange.as.Iterate.impl.Next.%Int.loc11_43.1 (%Int.fc6021.1) = acquire_value %.loc12_32.1 // CHECK:STDOUT: %impl.elem0.loc12_32.1: @IntRange.as.Iterate.impl.Next.%.loc12_32.4 (%.e29) = impl_witness_access constants.%Copy.lookup_impl_witness.7a8, element0 [symbolic = %impl.elem0.loc12_32.2 (constants.%impl.elem0.694)] // CHECK:STDOUT: %bound_method.loc12_32.1: = bound_method %.loc12_32.2, %impl.elem0.loc12_32.1 // CHECK:STDOUT: %specific_impl_fn.loc12_32.1: = specific_impl_function %impl.elem0.loc12_32.1, @Copy.WithSelf.Op(constants.%Copy.facet.3b9) [symbolic = %specific_impl_fn.loc12_32.2 (constants.%specific_impl_fn.bd4)] // CHECK:STDOUT: %bound_method.loc12_32.2: = bound_method %.loc12_32.2, %specific_impl_fn.loc12_32.1 // CHECK:STDOUT: %Copy.WithSelf.Op.call: init @IntRange.as.Iterate.impl.Next.%Int.loc11_43.1 (%Int.fc6021.1) = call %bound_method.loc12_32.2(%.loc12_32.2) // CHECK:STDOUT: assign %value.var, %Copy.WithSelf.Op.call // CHECK:STDOUT: %.loc12_28: type = splice_block %Int.loc12 [symbolic = %Int.loc11_43.1 (constants.%Int.fc6021.1)] { // CHECK:STDOUT: %Core.ref.loc12: = name_ref Core, imports.%Core [concrete = imports.%Core] // CHECK:STDOUT: %Int.ref.loc12: %Int.type = name_ref Int, imports.%Core.Int [concrete = constants.%Int.generic] // CHECK:STDOUT: %N.ref.loc12: Core.IntLiteral = name_ref N, @IntRange.%N.loc4_16.2 [symbolic = %N (constants.%N)] // CHECK:STDOUT: %Int.loc12: type = class_type @Int, @Int(constants.%N) [symbolic = %Int.loc11_43.1 (constants.%Int.fc6021.1)] // CHECK:STDOUT: } // CHECK:STDOUT: %value: ref @IntRange.as.Iterate.impl.Next.%Int.loc11_43.1 (%Int.fc6021.1) = ref_binding value, %value.var // CHECK:STDOUT: %value.ref.loc13: ref @IntRange.as.Iterate.impl.Next.%Int.loc11_43.1 (%Int.fc6021.1) = name_ref value, %value // CHECK:STDOUT: %self.ref: @IntRange.as.Iterate.impl.Next.%IntRange (%IntRange.265) = name_ref self, %self // CHECK:STDOUT: %end.ref: @IntRange.as.Iterate.impl.Next.%IntRange.elem (%IntRange.elem.541) = name_ref end, @IntRange.%.loc23 [concrete = @IntRange.%.loc23] // CHECK:STDOUT: %.loc13_23.1: ref @IntRange.as.Iterate.impl.Next.%Int.loc11_43.1 (%Int.fc6021.1) = class_element_access %self.ref, element1 // CHECK:STDOUT: %.loc13_23.2: @IntRange.as.Iterate.impl.Next.%Int.loc11_43.1 (%Int.fc6021.1) = acquire_value %.loc13_23.1 // CHECK:STDOUT: %OrderedWith.type.loc13_17.1: type = facet_type <@OrderedWith, @OrderedWith(constants.%Int.fc6021.1)> [symbolic = %OrderedWith.type.loc13_17.2 (constants.%OrderedWith.type.e44)] // CHECK:STDOUT: %.loc13_17.1: @IntRange.as.Iterate.impl.Next.%OrderedWith.assoc_type (%OrderedWith.assoc_type.215) = specific_constant imports.%Core.import_ref.7e9, @OrderedWith.WithSelf(constants.%Int.fc6021.1, constants.%Self.adc) [symbolic = %assoc0 (constants.%assoc0.15c)] // CHECK:STDOUT: %Less.ref: @IntRange.as.Iterate.impl.Next.%OrderedWith.assoc_type (%OrderedWith.assoc_type.215) = name_ref Less, %.loc13_17.1 [symbolic = %assoc0 (constants.%assoc0.15c)] // CHECK:STDOUT: %impl.elem0.loc13: @IntRange.as.Iterate.impl.Next.%.loc13_17.3 (%.48d) = impl_witness_access constants.%OrderedWith.impl_witness.ea9, element0 [symbolic = %Int.as.OrderedWith.impl.Less (constants.%Int.as.OrderedWith.impl.Less.4cf)] // CHECK:STDOUT: %bound_method.loc13_17.1: = bound_method %value.ref.loc13, %impl.elem0.loc13 // CHECK:STDOUT: %specific_fn: = specific_function %impl.elem0.loc13, @Int.as.OrderedWith.impl.Less.1(constants.%N, constants.%N) [symbolic = %Int.as.OrderedWith.impl.Less.specific_fn (constants.%Int.as.OrderedWith.impl.Less.specific_fn.4c2)] // CHECK:STDOUT: %bound_method.loc13_17.2: = bound_method %value.ref.loc13, %specific_fn // CHECK:STDOUT: %.loc13_11: @IntRange.as.Iterate.impl.Next.%Int.loc11_43.1 (%Int.fc6021.1) = acquire_value %value.ref.loc13 // CHECK:STDOUT: %Int.as.OrderedWith.impl.Less.call: init bool = call %bound_method.loc13_17.2(%.loc13_11, %.loc13_23.2) // CHECK:STDOUT: %.loc13_27.1: bool = value_of_initializer %Int.as.OrderedWith.impl.Less.call // CHECK:STDOUT: %.loc13_27.2: bool = converted %Int.as.OrderedWith.impl.Less.call, %.loc13_27.1 // CHECK:STDOUT: if %.loc13_27.2 br !if.then else br !if.else // CHECK:STDOUT: // CHECK:STDOUT: !if.then: // CHECK:STDOUT: %cursor.ref.loc14: @IntRange.as.Iterate.impl.Next.%ptr.loc11_44.1 (%ptr.c9c) = name_ref cursor, %cursor // CHECK:STDOUT: %.loc14_11: ref @IntRange.as.Iterate.impl.Next.%Int.loc11_43.1 (%Int.fc6021.1) = deref %cursor.ref.loc14 // CHECK:STDOUT: %impl.elem0.loc14_9.1: @IntRange.as.Iterate.impl.Next.%.loc14_9.2 (%.9a6) = impl_witness_access constants.%Inc.lookup_impl_witness, element0 [symbolic = %impl.elem0.loc14_9.2 (constants.%impl.elem0.10c)] // CHECK:STDOUT: %bound_method.loc14_9.1: = bound_method %.loc14_11, %impl.elem0.loc14_9.1 // CHECK:STDOUT: %specific_impl_fn.loc14_9.1: = specific_impl_function %impl.elem0.loc14_9.1, @Inc.WithSelf.Op(constants.%Inc.facet) [symbolic = %specific_impl_fn.loc14_9.2 (constants.%specific_impl_fn.ed3)] // CHECK:STDOUT: %bound_method.loc14_9.2: = bound_method %.loc14_11, %specific_impl_fn.loc14_9.1 // CHECK:STDOUT: %Inc.WithSelf.Op.call: init %empty_tuple.type = call %bound_method.loc14_9.2(%.loc14_11) // CHECK:STDOUT: %Core.ref.loc15_16: = name_ref Core, imports.%Core [concrete = imports.%Core] // CHECK:STDOUT: %Optional.ref.loc15: %Optional.type = name_ref Optional, imports.%Core.Optional [concrete = constants.%Optional.generic] // CHECK:STDOUT: %Core.ref.loc15_30: = name_ref Core, imports.%Core [concrete = imports.%Core] // CHECK:STDOUT: %Int.ref.loc15: %Int.type = name_ref Int, imports.%Core.Int [concrete = constants.%Int.generic] // CHECK:STDOUT: %N.ref.loc15: Core.IntLiteral = name_ref N, @IntRange.%N.loc4_16.2 [symbolic = %N (constants.%N)] // CHECK:STDOUT: %Int.loc15: type = class_type @Int, @Int(constants.%N) [symbolic = %Int.loc11_43.1 (constants.%Int.fc6021.1)] // CHECK:STDOUT: %OptionalStorage.facet.loc15_41: %OptionalStorage.type = facet_value %Int.loc15, (constants.%OptionalStorage.lookup_impl_witness.b62) [symbolic = %OptionalStorage.facet.loc11_75.1 (constants.%OptionalStorage.facet.01e)] // CHECK:STDOUT: %.loc15_41: %OptionalStorage.type = converted %Int.loc15, %OptionalStorage.facet.loc15_41 [symbolic = %OptionalStorage.facet.loc11_75.1 (constants.%OptionalStorage.facet.01e)] // CHECK:STDOUT: %Optional.loc15: type = class_type @Optional, @Optional(constants.%OptionalStorage.facet.01e) [symbolic = %Optional.loc11_75.1 (constants.%Optional.e48)] // CHECK:STDOUT: %.loc15_42: @IntRange.as.Iterate.impl.Next.%Optional.Some.type (%Optional.Some.type.f74) = specific_constant imports.%Core.import_ref.aeb, @Optional(constants.%OptionalStorage.facet.01e) [symbolic = %Optional.Some (constants.%Optional.Some.076)] // CHECK:STDOUT: %Some.ref: @IntRange.as.Iterate.impl.Next.%Optional.Some.type (%Optional.Some.type.f74) = name_ref Some, %.loc15_42 [symbolic = %Optional.Some (constants.%Optional.Some.076)] // CHECK:STDOUT: %value.ref.loc15: ref @IntRange.as.Iterate.impl.Next.%Int.loc11_43.1 (%Int.fc6021.1) = name_ref value, %value // CHECK:STDOUT: %OptionalStorage.facet.loc15_53: %OptionalStorage.type = facet_value constants.%Int.fc6021.1, (constants.%OptionalStorage.lookup_impl_witness.b62) [symbolic = %OptionalStorage.facet.loc11_75.1 (constants.%OptionalStorage.facet.01e)] // CHECK:STDOUT: %.loc15_53: %OptionalStorage.type = converted constants.%Int.fc6021.1, %OptionalStorage.facet.loc15_53 [symbolic = %OptionalStorage.facet.loc11_75.1 (constants.%OptionalStorage.facet.01e)] // CHECK:STDOUT: %Optional.Some.specific_fn.loc15_42.1: = specific_function %Some.ref, @Optional.Some(constants.%OptionalStorage.facet.01e) [symbolic = %Optional.Some.specific_fn.loc15_42.2 (constants.%Optional.Some.specific_fn)] // CHECK:STDOUT: %.loc11_75.1: ref @IntRange.as.Iterate.impl.Next.%Optional.loc11_75.1 (%Optional.e48) = splice_block %return.param {} // CHECK:STDOUT: %.loc15_48: @IntRange.as.Iterate.impl.Next.%Int.loc11_43.1 (%Int.fc6021.1) = acquire_value %value.ref.loc15 // CHECK:STDOUT: %Optional.Some.call: init @IntRange.as.Iterate.impl.Next.%Optional.loc11_75.1 (%Optional.e48) to %.loc11_75.1 = call %Optional.Some.specific_fn.loc15_42.1(%.loc15_48) // CHECK:STDOUT: %impl.elem0.loc12_7.1: @IntRange.as.Iterate.impl.Next.%.loc12_7 (%.e63) = impl_witness_access constants.%Destroy.lookup_impl_witness.93c, element0 [symbolic = %impl.elem0.loc12_7.3 (constants.%impl.elem0.602)] // CHECK:STDOUT: %bound_method.loc12_7.1: = bound_method %value.var, %impl.elem0.loc12_7.1 // CHECK:STDOUT: %specific_impl_fn.loc12_7.1: = specific_impl_function %impl.elem0.loc12_7.1, @Destroy.WithSelf.Op(constants.%Destroy.facet.1c0) [symbolic = %specific_impl_fn.loc12_7.3 (constants.%specific_impl_fn.d65)] // CHECK:STDOUT: %bound_method.loc12_7.2: = bound_method %value.var, %specific_impl_fn.loc12_7.1 // CHECK:STDOUT: %Destroy.WithSelf.Op.call.loc12_7.1: init %empty_tuple.type = call %bound_method.loc12_7.2(%value.var) // CHECK:STDOUT: return %Optional.Some.call to %return.param // CHECK:STDOUT: // CHECK:STDOUT: !if.else: // CHECK:STDOUT: %Core.ref.loc17_16: = name_ref Core, imports.%Core [concrete = imports.%Core] // CHECK:STDOUT: %Optional.ref.loc17: %Optional.type = name_ref Optional, imports.%Core.Optional [concrete = constants.%Optional.generic] // CHECK:STDOUT: %Core.ref.loc17_30: = name_ref Core, imports.%Core [concrete = imports.%Core] // CHECK:STDOUT: %Int.ref.loc17: %Int.type = name_ref Int, imports.%Core.Int [concrete = constants.%Int.generic] // CHECK:STDOUT: %N.ref.loc17: Core.IntLiteral = name_ref N, @IntRange.%N.loc4_16.2 [symbolic = %N (constants.%N)] // CHECK:STDOUT: %Int.loc17: type = class_type @Int, @Int(constants.%N) [symbolic = %Int.loc11_43.1 (constants.%Int.fc6021.1)] // CHECK:STDOUT: %OptionalStorage.facet.loc17: %OptionalStorage.type = facet_value %Int.loc17, (constants.%OptionalStorage.lookup_impl_witness.b62) [symbolic = %OptionalStorage.facet.loc11_75.1 (constants.%OptionalStorage.facet.01e)] // CHECK:STDOUT: %.loc17_41: %OptionalStorage.type = converted %Int.loc17, %OptionalStorage.facet.loc17 [symbolic = %OptionalStorage.facet.loc11_75.1 (constants.%OptionalStorage.facet.01e)] // CHECK:STDOUT: %Optional.loc17: type = class_type @Optional, @Optional(constants.%OptionalStorage.facet.01e) [symbolic = %Optional.loc11_75.1 (constants.%Optional.e48)] // CHECK:STDOUT: %.loc17_42: @IntRange.as.Iterate.impl.Next.%Optional.None.type (%Optional.None.type.d56) = specific_constant imports.%Core.import_ref.d20, @Optional(constants.%OptionalStorage.facet.01e) [symbolic = %Optional.None (constants.%Optional.None.b26)] // CHECK:STDOUT: %None.ref: @IntRange.as.Iterate.impl.Next.%Optional.None.type (%Optional.None.type.d56) = name_ref None, %.loc17_42 [symbolic = %Optional.None (constants.%Optional.None.b26)] // CHECK:STDOUT: %Optional.None.specific_fn.loc17_42.1: = specific_function %None.ref, @Optional.None(constants.%OptionalStorage.facet.01e) [symbolic = %Optional.None.specific_fn.loc17_42.2 (constants.%Optional.None.specific_fn)] // CHECK:STDOUT: %.loc11_75.2: ref @IntRange.as.Iterate.impl.Next.%Optional.loc11_75.1 (%Optional.e48) = splice_block %return.param {} // CHECK:STDOUT: %Optional.None.call: init @IntRange.as.Iterate.impl.Next.%Optional.loc11_75.1 (%Optional.e48) to %.loc11_75.2 = call %Optional.None.specific_fn.loc17_42.1() // CHECK:STDOUT: %impl.elem0.loc12_7.2: @IntRange.as.Iterate.impl.Next.%.loc12_7 (%.e63) = impl_witness_access constants.%Destroy.lookup_impl_witness.93c, element0 [symbolic = %impl.elem0.loc12_7.3 (constants.%impl.elem0.602)] // CHECK:STDOUT: %bound_method.loc12_7.3: = bound_method %value.var, %impl.elem0.loc12_7.2 // CHECK:STDOUT: %specific_impl_fn.loc12_7.2: = specific_impl_function %impl.elem0.loc12_7.2, @Destroy.WithSelf.Op(constants.%Destroy.facet.1c0) [symbolic = %specific_impl_fn.loc12_7.3 (constants.%specific_impl_fn.d65)] // CHECK:STDOUT: %bound_method.loc12_7.4: = bound_method %value.var, %specific_impl_fn.loc12_7.2 // CHECK:STDOUT: %Destroy.WithSelf.Op.call.loc12_7.2: init %empty_tuple.type = call %bound_method.loc12_7.4(%value.var) // CHECK:STDOUT: return %Optional.None.call to %return.param // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @Range(%end.param: %i32) -> out %return.param: %IntRange.a89 { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %IntRange.ref.loc27: %IntRange.type = name_ref IntRange, file.%IntRange.decl [concrete = constants.%IntRange.generic] // CHECK:STDOUT: %int_32.loc27: Core.IntLiteral = int_value 32 [concrete = constants.%int_32] // CHECK:STDOUT: %IntRange.loc27: type = class_type @IntRange, @IntRange(constants.%int_32) [concrete = constants.%IntRange.a89] // CHECK:STDOUT: %.loc27_22: %IntRange.Make.type.045 = specific_constant @IntRange.%IntRange.Make.decl, @IntRange(constants.%int_32) [concrete = constants.%IntRange.Make.3e9] // CHECK:STDOUT: %Make.ref: %IntRange.Make.type.045 = name_ref Make, %.loc27_22 [concrete = constants.%IntRange.Make.3e9] // CHECK:STDOUT: %int_0: Core.IntLiteral = int_value 0 [concrete = constants.%int_0.5c6] // CHECK:STDOUT: %end.ref: %i32 = name_ref end, %end // CHECK:STDOUT: %IntRange.Make.specific_fn: = specific_function %Make.ref, @IntRange.Make(constants.%int_32) [concrete = constants.%IntRange.Make.specific_fn] // CHECK:STDOUT: %.loc26_34.1: ref %IntRange.a89 = splice_block %return.param {} // CHECK:STDOUT: %impl.elem0: %.545 = impl_witness_access constants.%ImplicitAs.impl_witness.6bc, element0 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5] // CHECK:STDOUT: %bound_method.loc27_28.1: = bound_method %int_0, %impl.elem0 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.bound] // CHECK:STDOUT: %specific_fn: = specific_function %impl.elem0, @Core.IntLiteral.as.ImplicitAs.impl.Convert(constants.%int_32) [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn] // CHECK:STDOUT: %bound_method.loc27_28.2: = bound_method %int_0, %specific_fn [concrete = constants.%bound_method] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.call: init %i32 = call %bound_method.loc27_28.2(%int_0) [concrete = constants.%int_0.6a9] // CHECK:STDOUT: %.loc27_28.1: %i32 = value_of_initializer %Core.IntLiteral.as.ImplicitAs.impl.Convert.call [concrete = constants.%int_0.6a9] // CHECK:STDOUT: %.loc27_28.2: %i32 = converted %int_0, %.loc27_28.1 [concrete = constants.%int_0.6a9] // CHECK:STDOUT: %IntRange.Make.call: init %IntRange.a89 to %.loc26_34.1 = call %IntRange.Make.specific_fn(%.loc27_28.2, %end.ref) // CHECK:STDOUT: return %IntRange.Make.call to %return.param // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @IntRange(constants.%N) { // CHECK:STDOUT: %N.loc4_16.1 => constants.%N // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %IntRange.Make.type => constants.%IntRange.Make.type.1df // CHECK:STDOUT: %IntRange.Make => constants.%IntRange.Make.8a9 // CHECK:STDOUT: %.loc9 => constants.%.4f8 // CHECK:STDOUT: %Int.loc22_32.2 => constants.%Int.fc6021.1 // CHECK:STDOUT: %require_complete => constants.%require_complete.9019d7.1 // CHECK:STDOUT: %IntRange => constants.%IntRange.265 // CHECK:STDOUT: %IntRange.elem => constants.%IntRange.elem.541 // CHECK:STDOUT: %struct_type.start.end => constants.%struct_type.start.end.ff1 // CHECK:STDOUT: %complete_type.loc24_1.2 => constants.%complete_type.427 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @IntRange.Make(constants.%N) { // CHECK:STDOUT: %N => constants.%N // CHECK:STDOUT: %Int.loc5_28.1 => constants.%Int.fc6021.1 // CHECK:STDOUT: %pattern_type.loc5_11 => constants.%pattern_type.764eab.1 // CHECK:STDOUT: %IntRange => constants.%IntRange.265 // CHECK:STDOUT: %.loc5_52.1 => constants.%.e1e // CHECK:STDOUT: %pattern_type.loc5_49 => constants.%pattern_type.b16 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @IntRange.as.Iterate.impl(constants.%N) { // CHECK:STDOUT: %N => constants.%N // CHECK:STDOUT: %IntRange => constants.%IntRange.265 // CHECK:STDOUT: %Int.loc9_54.1 => constants.%Int.fc6021.1 // CHECK:STDOUT: %Destroy.lookup_impl_witness => constants.%Destroy.lookup_impl_witness.93c // CHECK:STDOUT: %.loc9_85.1 => constants.%.4f8 // CHECK:STDOUT: %Copy.lookup_impl_witness => constants.%Copy.lookup_impl_witness.7a8 // CHECK:STDOUT: %facet_value.loc9_85.1 => constants.%facet_value // CHECK:STDOUT: %Iterate_where.type => constants.%Iterate_where.type // CHECK:STDOUT: %Iterate.impl_witness.loc9_87.2 => constants.%Iterate.impl_witness // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete => constants.%require_complete.234 // CHECK:STDOUT: %IntRange.as.Iterate.impl.NewCursor.type => constants.%IntRange.as.Iterate.impl.NewCursor.type // CHECK:STDOUT: %IntRange.as.Iterate.impl.NewCursor => constants.%IntRange.as.Iterate.impl.NewCursor // CHECK:STDOUT: %.loc11 => constants.%.63c // CHECK:STDOUT: %IntRange.as.Iterate.impl.Next.type => constants.%IntRange.as.Iterate.impl.Next.type // CHECK:STDOUT: %IntRange.as.Iterate.impl.Next => constants.%IntRange.as.Iterate.impl.Next // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @IntRange.as.Iterate.impl.NewCursor(constants.%N) { // CHECK:STDOUT: %N => constants.%N // CHECK:STDOUT: %IntRange => constants.%IntRange.265 // CHECK:STDOUT: %pattern_type.loc10_18 => constants.%pattern_type.b16 // CHECK:STDOUT: %Int.loc10_45.1 => constants.%Int.fc6021.1 // CHECK:STDOUT: %.loc10_45.1 => constants.%.83cba3.1 // CHECK:STDOUT: %pattern_type.loc10_32 => constants.%pattern_type.764eab.1 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @IntRange.as.Iterate.impl.Next(constants.%N) { // CHECK:STDOUT: %N => constants.%N // CHECK:STDOUT: %IntRange => constants.%IntRange.265 // CHECK:STDOUT: %pattern_type.loc11_13 => constants.%pattern_type.b16 // CHECK:STDOUT: %Int.loc11_43.1 => constants.%Int.fc6021.1 // CHECK:STDOUT: %ptr.loc11_44.1 => constants.%ptr.c9c // CHECK:STDOUT: %pattern_type.loc11_25 => constants.%pattern_type.bda // CHECK:STDOUT: %Destroy.lookup_impl_witness => constants.%Destroy.lookup_impl_witness.93c // CHECK:STDOUT: %Copy.lookup_impl_witness => constants.%Copy.lookup_impl_witness.7a8 // CHECK:STDOUT: %facet_value => constants.%facet_value // CHECK:STDOUT: %.loc11_75.3 => constants.%.63c // CHECK:STDOUT: %OptionalStorage.lookup_impl_witness => constants.%OptionalStorage.lookup_impl_witness.b62 // CHECK:STDOUT: %OptionalStorage.facet.loc11_75.1 => constants.%OptionalStorage.facet.01e // CHECK:STDOUT: %Optional.loc11_75.1 => constants.%Optional.e48 // CHECK:STDOUT: %.loc11_75.4 => constants.%.949 // CHECK:STDOUT: %pattern_type.loc11_47 => constants.%pattern_type.0c2 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @IntRange(constants.%int_32) { // CHECK:STDOUT: %N.loc4_16.1 => constants.%int_32 // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %IntRange.Make.type => constants.%IntRange.Make.type.045 // CHECK:STDOUT: %IntRange.Make => constants.%IntRange.Make.3e9 // CHECK:STDOUT: %.loc9 => constants.%.14e // CHECK:STDOUT: %Int.loc22_32.2 => constants.%i32 // CHECK:STDOUT: %require_complete => constants.%complete_type.f8a // CHECK:STDOUT: %IntRange => constants.%IntRange.a89 // CHECK:STDOUT: %IntRange.elem => constants.%IntRange.elem.a58 // CHECK:STDOUT: %struct_type.start.end => constants.%struct_type.start.end.d0a // CHECK:STDOUT: %complete_type.loc24_1.2 => constants.%complete_type.c45 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @IntRange.Make(constants.%int_32) { // CHECK:STDOUT: %N => constants.%int_32 // CHECK:STDOUT: %Int.loc5_28.1 => constants.%i32 // CHECK:STDOUT: %pattern_type.loc5_11 => constants.%pattern_type.7ce // CHECK:STDOUT: %IntRange => constants.%IntRange.a89 // CHECK:STDOUT: %.loc5_52.1 => constants.%.aec // CHECK:STDOUT: %pattern_type.loc5_49 => constants.%pattern_type.615 // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete.loc5_16 => constants.%complete_type.f8a // CHECK:STDOUT: %require_complete.loc5_52 => constants.%complete_type.c45 // CHECK:STDOUT: %struct_type.start.end => constants.%struct_type.start.end.d0a // CHECK:STDOUT: %.loc6_22.1 => constants.%.14e // CHECK:STDOUT: %Copy.lookup_impl_witness => constants.%Copy.impl_witness.f17 // CHECK:STDOUT: %Copy.facet => constants.%Copy.facet.de4 // CHECK:STDOUT: %Copy.WithSelf.Op.type => constants.%Copy.WithSelf.Op.type.081 // CHECK:STDOUT: %.loc6_22.2 => constants.%.8e2 // CHECK:STDOUT: %impl.elem0.loc6_22.2 => constants.%Int.as.Copy.impl.Op.664 // CHECK:STDOUT: %specific_impl_fn.loc6_22.2 => constants.%Int.as.Copy.impl.Op.specific_fn // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- trivial.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %type: type = facet_type [concrete] // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self] // CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] // CHECK:STDOUT: %IntLiteral.type: type = fn_type @IntLiteral [concrete] // CHECK:STDOUT: %IntLiteral: %IntLiteral.type = struct_value () [concrete] // CHECK:STDOUT: %pattern_type.dc0: type = pattern_type Core.IntLiteral [concrete] // CHECK:STDOUT: %y: Core.IntLiteral = symbolic_binding y, 0 [symbolic] // CHECK:STDOUT: %Read.type: type = fn_type @Read [concrete] // CHECK:STDOUT: %Read: %Read.type = struct_value () [concrete] // CHECK:STDOUT: %IntRange.type: type = generic_class_type @IntRange [concrete] // CHECK:STDOUT: %IntRange.generic: %IntRange.type = struct_value () [concrete] // CHECK:STDOUT: %N: Core.IntLiteral = symbolic_binding N, 0 [symbolic] // CHECK:STDOUT: %Int.b6d943.1: type = class_type @Int, @Int(%N) [symbolic] // CHECK:STDOUT: %struct_type.start.end.79a: type = struct_type {.start: %Int.b6d943.1, .end: %Int.b6d943.1} [symbolic] // CHECK:STDOUT: %complete_type.fb4: = complete_type_witness %struct_type.start.end.79a [symbolic] // CHECK:STDOUT: %IntRange.265: type = class_type @IntRange, @IntRange(%N) [symbolic] // CHECK:STDOUT: %IntRange.elem.302: type = unbound_element_type %IntRange.265, %Int.b6d943.1 [symbolic] // CHECK:STDOUT: %require_complete.2ded7d.1: = require_complete_type %Int.b6d943.1 [symbolic] // CHECK:STDOUT: %Copy.type: type = facet_type <@Copy> [concrete] // CHECK:STDOUT: %pattern_type.bb68b6.1: type = pattern_type %Int.b6d943.1 [symbolic] // CHECK:STDOUT: %.b26: require_specific_def_type = require_specific_def @Int.as.Copy.impl(%N) [symbolic] // CHECK:STDOUT: %IntRange.Make.type.1df: type = fn_type @IntRange.Make, @IntRange(%N) [symbolic] // CHECK:STDOUT: %IntRange.Make.8a9: %IntRange.Make.type.1df = struct_value () [symbolic] // CHECK:STDOUT: %pattern_type.b16: type = pattern_type %IntRange.265 [symbolic] // CHECK:STDOUT: %.e1e: Core.Form = init_form %IntRange.265 [symbolic] // CHECK:STDOUT: %Copy.lookup_impl_witness.4c7: = lookup_impl_witness %Int.b6d943.1, @Copy [symbolic] // CHECK:STDOUT: %Copy.facet: %Copy.type = facet_value %Int.b6d943.1, (%Copy.lookup_impl_witness.4c7) [symbolic] // CHECK:STDOUT: %Copy.WithSelf.Op.type.2e7: type = fn_type @Copy.WithSelf.Op, @Copy.WithSelf(%Copy.facet) [symbolic] // CHECK:STDOUT: %.cf8: type = fn_type_with_self_type %Copy.WithSelf.Op.type.2e7, %Copy.facet [symbolic] // CHECK:STDOUT: %impl.elem0.84f: %.cf8 = impl_witness_access %Copy.lookup_impl_witness.4c7, element0 [symbolic] // CHECK:STDOUT: %specific_impl_fn.508: = specific_impl_function %impl.elem0.84f, @Copy.WithSelf.Op(%Copy.facet) [symbolic] // CHECK:STDOUT: %require_complete.8a1: = require_complete_type %IntRange.265 [symbolic] // CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [concrete] // CHECK:STDOUT: %IntRange.a89: type = class_type @IntRange, @IntRange(%int_32) [concrete] // CHECK:STDOUT: %IntRange.Make.type.045: type = fn_type @IntRange.Make, @IntRange(%int_32) [concrete] // CHECK:STDOUT: %IntRange.Make.3e9: %IntRange.Make.type.045 = struct_value () [concrete] // CHECK:STDOUT: %i32: type = class_type @Int, @Int(%int_32) [concrete] // CHECK:STDOUT: %i32.builtin: type = int_type signed, %int_32 [concrete] // CHECK:STDOUT: %complete_type.f8a: = complete_type_witness %i32.builtin [concrete] // CHECK:STDOUT: %.fc8: require_specific_def_type = require_specific_def @Int.as.Copy.impl(%int_32) [concrete] // CHECK:STDOUT: %IntRange.elem.d76: type = unbound_element_type %IntRange.a89, %i32 [concrete] // CHECK:STDOUT: %struct_type.start.end.0fe: type = struct_type {.start: %i32, .end: %i32} [concrete] // CHECK:STDOUT: %complete_type.a43: = complete_type_witness %struct_type.start.end.0fe [concrete] // CHECK:STDOUT: %pattern_type.615: type = pattern_type %IntRange.a89 [concrete] // CHECK:STDOUT: %Range.type: type = fn_type @Range [concrete] // CHECK:STDOUT: %Range: %Range.type = struct_value () [concrete] // CHECK:STDOUT: %ImplicitAs.type.cc7: type = generic_interface_type @ImplicitAs [concrete] // CHECK:STDOUT: %ImplicitAs.generic: %ImplicitAs.type.cc7 = struct_value () [concrete] // CHECK:STDOUT: %ImplicitAs.type.cf3: type = facet_type <@ImplicitAs, @ImplicitAs(%i32)> [concrete] // CHECK:STDOUT: %To: Core.IntLiteral = symbolic_binding To, 0 [symbolic] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.004: type = fn_type @Core.IntLiteral.as.ImplicitAs.impl.Convert, @Core.IntLiteral.as.ImplicitAs.impl(%To) [symbolic] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.255: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.004 = struct_value () [symbolic] // CHECK:STDOUT: %ImplicitAs.impl_witness.58d: = impl_witness imports.%ImplicitAs.impl_witness_table.e45, @Core.IntLiteral.as.ImplicitAs.impl(%int_32) [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.199: type = fn_type @Core.IntLiteral.as.ImplicitAs.impl.Convert, @Core.IntLiteral.as.ImplicitAs.impl(%int_32) [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.5f4: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.199 = struct_value () [concrete] // CHECK:STDOUT: %ImplicitAs.facet: %ImplicitAs.type.cf3 = facet_value Core.IntLiteral, (%ImplicitAs.impl_witness.58d) [concrete] // CHECK:STDOUT: %ImplicitAs.WithSelf.Convert.type.979: type = fn_type @ImplicitAs.WithSelf.Convert, @ImplicitAs.WithSelf(%i32, %ImplicitAs.facet) [concrete] // CHECK:STDOUT: %.7c3: type = fn_type_with_self_type %ImplicitAs.WithSelf.Convert.type.979, %ImplicitAs.facet [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.bound: = bound_method %y, %Core.IntLiteral.as.ImplicitAs.impl.Convert.5f4 [symbolic] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn: = specific_function %Core.IntLiteral.as.ImplicitAs.impl.Convert.5f4, @Core.IntLiteral.as.ImplicitAs.impl.Convert(%int_32) [concrete] // CHECK:STDOUT: %bound_method: = bound_method %y, %Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn [symbolic] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.call: init %i32 = call %bound_method(%y) [symbolic] // CHECK:STDOUT: %Destroy.type: type = facet_type <@Destroy> [concrete] // CHECK:STDOUT: %Destroy.Op.type: type = fn_type @Destroy.Op [concrete] // CHECK:STDOUT: %Destroy.Op: %Destroy.Op.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Main.IntRange: %IntRange.type = import_ref Main//lib, IntRange, loaded [concrete = constants.%IntRange.generic] // CHECK:STDOUT: %Main.Range: %Range.type = import_ref Main//lib, Range, loaded [concrete = constants.%Range] // CHECK:STDOUT: %Core.ece: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: .IntLiteral = %Core.IntLiteral // CHECK:STDOUT: .ImplicitAs = %Core.ImplicitAs // CHECK:STDOUT: .Destroy = %Core.Destroy // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: %Core.IntLiteral: %IntLiteral.type = import_ref Core//prelude/types/int_literal, IntLiteral, loaded [concrete = constants.%IntLiteral] // CHECK:STDOUT: %Main.import_ref.6b552a.1: Core.IntLiteral = import_ref Main//lib, loc4_16, loaded [symbolic = @IntRange.%N (constants.%N)] // CHECK:STDOUT: %Main.import_ref.4b6: = import_ref Main//lib, loc24_1, loaded [symbolic = @IntRange.%complete_type (constants.%complete_type.fb4)] // CHECK:STDOUT: %Main.import_ref.011 = import_ref Main//lib, inst{{[0-9A-F]+}} [no loc], unloaded // CHECK:STDOUT: %Main.import_ref.f89 = import_ref Main//lib, loc5_57, unloaded // CHECK:STDOUT: %Main.import_ref.e7c = import_ref Main//lib, loc22_20, unloaded // CHECK:STDOUT: %Main.import_ref.8ef = import_ref Main//lib, loc23_18, unloaded // CHECK:STDOUT: %Main.import_ref.6b552a.2: Core.IntLiteral = import_ref Main//lib, loc4_16, loaded [symbolic = @IntRange.%N (constants.%N)] // CHECK:STDOUT: %Core.ImplicitAs: %ImplicitAs.type.cc7 = import_ref Core//prelude/operators/as, ImplicitAs, loaded [concrete = constants.%ImplicitAs.generic] // CHECK:STDOUT: %Core.import_ref.b25: @Core.IntLiteral.as.ImplicitAs.impl.%Core.IntLiteral.as.ImplicitAs.impl.Convert.type (%Core.IntLiteral.as.ImplicitAs.impl.Convert.type.004) = import_ref Core//prelude/types/int, loc{{\d+_\d+}}, loaded [symbolic = @Core.IntLiteral.as.ImplicitAs.impl.%Core.IntLiteral.as.ImplicitAs.impl.Convert (constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.255)] // CHECK:STDOUT: %ImplicitAs.impl_witness_table.e45 = impl_witness_table (%Core.import_ref.b25), @Core.IntLiteral.as.ImplicitAs.impl [concrete] // CHECK:STDOUT: %Core.Destroy: type = import_ref Core//prelude/destroy, Destroy, loaded [concrete = constants.%Destroy.type] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .IntRange = imports.%Main.IntRange // CHECK:STDOUT: .Range = imports.%Main.Range // CHECK:STDOUT: .Core = imports.%Core.ece // CHECK:STDOUT: .Read = %Read.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %default.import = import // CHECK:STDOUT: %Read.decl: %Read.type = fn_decl @Read [concrete = constants.%Read] { // CHECK:STDOUT: %y.patt: %pattern_type.dc0 = symbolic_binding_pattern y, 0 [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %.loc4_29.1: type = splice_block %.loc4_29.3 [concrete = Core.IntLiteral] { // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %Core.ref: = name_ref Core, imports.%Core.ece [concrete = imports.%Core.ece] // CHECK:STDOUT: %IntLiteral.ref: %IntLiteral.type = name_ref IntLiteral, imports.%Core.IntLiteral [concrete = constants.%IntLiteral] // CHECK:STDOUT: %IntLiteral.call: init type = call %IntLiteral.ref() [concrete = Core.IntLiteral] // CHECK:STDOUT: %.loc4_29.2: type = value_of_initializer %IntLiteral.call [concrete = Core.IntLiteral] // CHECK:STDOUT: %.loc4_29.3: type = converted %IntLiteral.call, %.loc4_29.2 [concrete = Core.IntLiteral] // CHECK:STDOUT: } // CHECK:STDOUT: %y.loc4_9.2: Core.IntLiteral = symbolic_binding y, 0 [symbolic = %y.loc4_9.1 (constants.%y)] // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic class @IntRange(imports.%Main.import_ref.6b552a.2: Core.IntLiteral) [from "lib.carbon"] { // CHECK:STDOUT: %N: Core.IntLiteral = symbolic_binding N, 0 [symbolic = %N (constants.%N)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %IntRange.Make.type: type = fn_type @IntRange.Make, @IntRange(%N) [symbolic = %IntRange.Make.type (constants.%IntRange.Make.type.1df)] // CHECK:STDOUT: %IntRange.Make: @IntRange.%IntRange.Make.type (%IntRange.Make.type.1df) = struct_value () [symbolic = %IntRange.Make (constants.%IntRange.Make.8a9)] // CHECK:STDOUT: %.1: require_specific_def_type = require_specific_def @Int.as.Copy.impl(%N) [symbolic = %.1 (constants.%.b26)] // CHECK:STDOUT: %Int: type = class_type @Int, @Int(%N) [symbolic = %Int (constants.%Int.b6d943.1)] // CHECK:STDOUT: %require_complete: = require_complete_type %Int [symbolic = %require_complete (constants.%require_complete.2ded7d.1)] // CHECK:STDOUT: %IntRange: type = class_type @IntRange, @IntRange(%N) [symbolic = %IntRange (constants.%IntRange.265)] // CHECK:STDOUT: %IntRange.elem: type = unbound_element_type %IntRange, %Int [symbolic = %IntRange.elem (constants.%IntRange.elem.302)] // CHECK:STDOUT: %struct_type.start.end: type = struct_type {.start: @IntRange.%Int (%Int.b6d943.1), .end: @IntRange.%Int (%Int.b6d943.1)} [symbolic = %struct_type.start.end (constants.%struct_type.start.end.79a)] // CHECK:STDOUT: %complete_type: = complete_type_witness %struct_type.start.end [symbolic = %complete_type (constants.%complete_type.fb4)] // CHECK:STDOUT: // CHECK:STDOUT: class { // CHECK:STDOUT: complete_type_witness = imports.%Main.import_ref.4b6 // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = imports.%Main.import_ref.011 // CHECK:STDOUT: .Make = imports.%Main.import_ref.f89 // CHECK:STDOUT: .start [private] = imports.%Main.import_ref.e7c // CHECK:STDOUT: .end [private] = imports.%Main.import_ref.8ef // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic fn @Read(%y.loc4_9.2: Core.IntLiteral) { // CHECK:STDOUT: %y.loc4_9.1: Core.IntLiteral = symbolic_binding y, 0 [symbolic = %y.loc4_9.1 (constants.%y)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.bound: = bound_method %y.loc4_9.1, constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.5f4 [symbolic = %Core.IntLiteral.as.ImplicitAs.impl.Convert.bound (constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.bound)] // CHECK:STDOUT: %bound_method.loc5_38.3: = bound_method %y.loc4_9.1, constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn [symbolic = %bound_method.loc5_38.3 (constants.%bound_method)] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc5_38.2: init %i32 = call %bound_method.loc5_38.3(%y.loc4_9.1) [symbolic = %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc5_38.2 (constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.call)] // CHECK:STDOUT: // CHECK:STDOUT: fn() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %x.patt: %pattern_type.615 = ref_binding_pattern x [concrete] // CHECK:STDOUT: %x.var_patt: %pattern_type.615 = var_pattern %x.patt [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %x.var: ref %IntRange.a89 = var %x.var_patt // CHECK:STDOUT: %Range.ref: %Range.type = name_ref Range, imports.%Main.Range [concrete = constants.%Range] // CHECK:STDOUT: %y.ref: Core.IntLiteral = name_ref y, %y.loc4_9.2 [symbolic = %y.loc4_9.1 (constants.%y)] // CHECK:STDOUT: %.loc5_3: ref %IntRange.a89 = splice_block %x.var {} // CHECK:STDOUT: %impl.elem0: %.7c3 = impl_witness_access constants.%ImplicitAs.impl_witness.58d, element0 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.5f4] // CHECK:STDOUT: %bound_method.loc5_38.1: = bound_method %y.ref, %impl.elem0 [symbolic = %Core.IntLiteral.as.ImplicitAs.impl.Convert.bound (constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.bound)] // CHECK:STDOUT: %specific_fn: = specific_function %impl.elem0, @Core.IntLiteral.as.ImplicitAs.impl.Convert(constants.%int_32) [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn] // CHECK:STDOUT: %bound_method.loc5_38.2: = bound_method %y.ref, %specific_fn [symbolic = %bound_method.loc5_38.3 (constants.%bound_method)] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc5_38.1: init %i32 = call %bound_method.loc5_38.2(%y.ref) [symbolic = %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc5_38.2 (constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.call)] // CHECK:STDOUT: %.loc5_38.1: %i32 = value_of_initializer %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc5_38.1 [symbolic = %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc5_38.2 (constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.call)] // CHECK:STDOUT: %.loc5_38.2: %i32 = converted %y.ref, %.loc5_38.1 [symbolic = %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc5_38.2 (constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.call)] // CHECK:STDOUT: %Range.call: init %IntRange.a89 to %.loc5_3 = call %Range.ref(%.loc5_38.2) // CHECK:STDOUT: assign %x.var, %Range.call // CHECK:STDOUT: %.loc5_28: type = splice_block %IntRange [concrete = constants.%IntRange.a89] { // CHECK:STDOUT: %IntRange.ref: %IntRange.type = name_ref IntRange, imports.%Main.IntRange [concrete = constants.%IntRange.generic] // CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [concrete = constants.%int_32] // CHECK:STDOUT: %IntRange: type = class_type @IntRange, @IntRange(constants.%int_32) [concrete = constants.%IntRange.a89] // CHECK:STDOUT: } // CHECK:STDOUT: %x: ref %IntRange.a89 = ref_binding x, %x.var // CHECK:STDOUT: %Destroy.Op.bound: = bound_method %x.var, constants.%Destroy.Op // CHECK:STDOUT: %Destroy.Op.call: init %empty_tuple.type = call %Destroy.Op.bound(%x.var) // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic fn @IntRange.Make(imports.%Main.import_ref.6b552a.1: Core.IntLiteral) [from "lib.carbon"] { // CHECK:STDOUT: %N: Core.IntLiteral = symbolic_binding N, 0 [symbolic = %N (constants.%N)] // CHECK:STDOUT: %Int: type = class_type @Int, @Int(%N) [symbolic = %Int (constants.%Int.b6d943.1)] // CHECK:STDOUT: %pattern_type.1: type = pattern_type %Int [symbolic = %pattern_type.1 (constants.%pattern_type.bb68b6.1)] // CHECK:STDOUT: %IntRange: type = class_type @IntRange, @IntRange(%N) [symbolic = %IntRange (constants.%IntRange.265)] // CHECK:STDOUT: %.1: Core.Form = init_form %IntRange [symbolic = %.1 (constants.%.e1e)] // CHECK:STDOUT: %pattern_type.2: type = pattern_type %IntRange [symbolic = %pattern_type.2 (constants.%pattern_type.b16)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete.1: = require_complete_type %Int [symbolic = %require_complete.1 (constants.%require_complete.2ded7d.1)] // CHECK:STDOUT: %require_complete.2: = require_complete_type %IntRange [symbolic = %require_complete.2 (constants.%require_complete.8a1)] // CHECK:STDOUT: %struct_type.start.end: type = struct_type {.start: @IntRange.Make.%Int (%Int.b6d943.1), .end: @IntRange.Make.%Int (%Int.b6d943.1)} [symbolic = %struct_type.start.end (constants.%struct_type.start.end.79a)] // CHECK:STDOUT: %.2: require_specific_def_type = require_specific_def @Int.as.Copy.impl(%N) [symbolic = %.2 (constants.%.b26)] // CHECK:STDOUT: %Copy.lookup_impl_witness: = lookup_impl_witness %Int, @Copy [symbolic = %Copy.lookup_impl_witness (constants.%Copy.lookup_impl_witness.4c7)] // CHECK:STDOUT: %Copy.facet: %Copy.type = facet_value %Int, (%Copy.lookup_impl_witness) [symbolic = %Copy.facet (constants.%Copy.facet)] // CHECK:STDOUT: %Copy.WithSelf.Op.type: type = fn_type @Copy.WithSelf.Op, @Copy.WithSelf(%Copy.facet) [symbolic = %Copy.WithSelf.Op.type (constants.%Copy.WithSelf.Op.type.2e7)] // CHECK:STDOUT: %.3: type = fn_type_with_self_type %Copy.WithSelf.Op.type, %Copy.facet [symbolic = %.3 (constants.%.cf8)] // CHECK:STDOUT: %impl.elem0: @IntRange.Make.%.3 (%.cf8) = impl_witness_access %Copy.lookup_impl_witness, element0 [symbolic = %impl.elem0 (constants.%impl.elem0.84f)] // CHECK:STDOUT: %specific_impl_fn: = specific_impl_function %impl.elem0, @Copy.WithSelf.Op(%Copy.facet) [symbolic = %specific_impl_fn (constants.%specific_impl_fn.508)] // CHECK:STDOUT: // CHECK:STDOUT: fn; // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @Range [from "lib.carbon"]; // CHECK:STDOUT: // CHECK:STDOUT: fn @Destroy.Op(%self.param: ref %IntRange.a89) = "no_op"; // CHECK:STDOUT: // CHECK:STDOUT: specific @Read(constants.%y) { // CHECK:STDOUT: %y.loc4_9.1 => constants.%y // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @IntRange(constants.%N) { // CHECK:STDOUT: %N => constants.%N // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %IntRange.Make.type => constants.%IntRange.Make.type.1df // CHECK:STDOUT: %IntRange.Make => constants.%IntRange.Make.8a9 // CHECK:STDOUT: %.1 => constants.%.b26 // CHECK:STDOUT: %Int => constants.%Int.b6d943.1 // CHECK:STDOUT: %require_complete => constants.%require_complete.2ded7d.1 // CHECK:STDOUT: %IntRange => constants.%IntRange.265 // CHECK:STDOUT: %IntRange.elem => constants.%IntRange.elem.302 // CHECK:STDOUT: %struct_type.start.end => constants.%struct_type.start.end.79a // CHECK:STDOUT: %complete_type => constants.%complete_type.fb4 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @IntRange.Make(constants.%N) { // CHECK:STDOUT: %N => constants.%N // CHECK:STDOUT: %Int => constants.%Int.b6d943.1 // CHECK:STDOUT: %pattern_type.1 => constants.%pattern_type.bb68b6.1 // CHECK:STDOUT: %IntRange => constants.%IntRange.265 // CHECK:STDOUT: %.1 => constants.%.e1e // CHECK:STDOUT: %pattern_type.2 => constants.%pattern_type.b16 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @IntRange(constants.%int_32) { // CHECK:STDOUT: %N => constants.%int_32 // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %IntRange.Make.type => constants.%IntRange.Make.type.045 // CHECK:STDOUT: %IntRange.Make => constants.%IntRange.Make.3e9 // CHECK:STDOUT: %.1 => constants.%.fc8 // CHECK:STDOUT: %Int => constants.%i32 // CHECK:STDOUT: %require_complete => constants.%complete_type.f8a // CHECK:STDOUT: %IntRange => constants.%IntRange.a89 // CHECK:STDOUT: %IntRange.elem => constants.%IntRange.elem.d76 // CHECK:STDOUT: %struct_type.start.end => constants.%struct_type.start.end.0fe // CHECK:STDOUT: %complete_type => constants.%complete_type.a43 // CHECK:STDOUT: } // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/for/basic.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/for.carbon // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/for/basic.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/for/basic.carbon // --- fail_not_range.carbon library "[[@TEST_NAME]]"; fn Run() { // TODO: These diagnostics could be better. If nothing else, we should only diagnose once. // CHECK:STDERR: fail_not_range.carbon:[[@LINE+8]]:7: error: cannot access member of interface `Core.Iterate` in type `{}` that does not implement that interface [MissingImplInMemberAccess] // CHECK:STDERR: for (unused c: {} in {}) { // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: // CHECK:STDERR: fail_not_range.carbon:[[@LINE+4]]:7: error: cannot access member of interface `Core.Iterate` in type `{}` that does not implement that interface [MissingImplInMemberAccess] // CHECK:STDERR: for (unused c: {} in {}) { // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: for (unused c: {} in {}) { } } // --- trivial.carbon library "[[@TEST_NAME]]"; class TrivialRange { impl as Core.Iterate where .CursorType = () and .ElementType = () { fn NewCursor[unused self: Self]() {} fn Next[unused self: Self](unused cursor: ()*) -> Core.Optional(()) { return Core.Optional(()).None(); } } } fn Body(); fn AfterLoop(); fn Run() { //@dump-sem-ir-begin for (_: () in {} as TrivialRange) { Body(); } AfterLoop(); //@dump-sem-ir-end } // CHECK:STDOUT: --- trivial.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %TrivialRange: type = class_type @TrivialRange [concrete] // CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] // CHECK:STDOUT: %Iterate.type: type = facet_type <@Iterate> [concrete] // CHECK:STDOUT: %Copy.type: type = facet_type <@Copy> [concrete] // CHECK:STDOUT: %T.035: %Copy.type = symbolic_binding T, 0 [symbolic] // CHECK:STDOUT: %Optional.Get.type.5bc: type = fn_type @Optional.Get, @Optional(%T.035) [symbolic] // CHECK:STDOUT: %Optional.Get.fa5: %Optional.Get.type.5bc = struct_value () [symbolic] // CHECK:STDOUT: %Optional.HasValue.type.958: type = fn_type @Optional.HasValue, @Optional(%T.035) [symbolic] // CHECK:STDOUT: %Optional.HasValue.d9c: %Optional.HasValue.type.958 = struct_value () [symbolic] // CHECK:STDOUT: %empty_tuple: %empty_tuple.type = tuple_value () [concrete] // CHECK:STDOUT: %Copy.impl_witness.927: = impl_witness imports.%Copy.impl_witness_table.951 [concrete] // CHECK:STDOUT: %Copy.facet: %Copy.type = facet_value %empty_tuple.type, (%Copy.impl_witness.927) [concrete] // CHECK:STDOUT: %Iterate.impl_witness: = impl_witness @TrivialRange.as.Iterate.impl.%Iterate.impl_witness_table [concrete] // CHECK:STDOUT: %TrivialRange.as.Iterate.impl.NewCursor.type.408fa8.1: type = fn_type @TrivialRange.as.Iterate.impl.NewCursor.loc6_39.1 [concrete] // CHECK:STDOUT: %TrivialRange.as.Iterate.impl.NewCursor.c71f42.1: %TrivialRange.as.Iterate.impl.NewCursor.type.408fa8.1 = struct_value () [concrete] // CHECK:STDOUT: %ptr.843: type = ptr_type %empty_tuple.type [concrete] // CHECK:STDOUT: %Optional.311: type = class_type @Optional, @Optional(%Copy.facet) [concrete] // CHECK:STDOUT: %TrivialRange.as.Iterate.impl.Next.type: type = fn_type @TrivialRange.as.Iterate.impl.Next [concrete] // CHECK:STDOUT: %TrivialRange.as.Iterate.impl.Next: %TrivialRange.as.Iterate.impl.Next.type = struct_value () [concrete] // CHECK:STDOUT: %Iterate.facet: %Iterate.type = facet_value %TrivialRange, (%Iterate.impl_witness) [concrete] // CHECK:STDOUT: %Iterate.WithSelf.NewCursor.type.a3c: type = fn_type @Iterate.WithSelf.NewCursor, @Iterate.WithSelf(%Iterate.facet) [concrete] // CHECK:STDOUT: %Iterate.WithSelf.Next.type.9dc: type = fn_type @Iterate.WithSelf.Next, @Iterate.WithSelf(%Iterate.facet) [concrete] // CHECK:STDOUT: %pattern_type.cb1: type = pattern_type %empty_tuple.type [concrete] // CHECK:STDOUT: %TrivialRange.as.Iterate.impl.NewCursor.type.408fa8.2: type = fn_type @TrivialRange.as.Iterate.impl.NewCursor.loc6_39.2 [concrete] // CHECK:STDOUT: %TrivialRange.as.Iterate.impl.NewCursor.c71f42.2: %TrivialRange.as.Iterate.impl.NewCursor.type.408fa8.2 = struct_value () [concrete] // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %Optional.HasValue.type.4db: type = fn_type @Optional.HasValue, @Optional(%Copy.facet) [concrete] // CHECK:STDOUT: %Optional.HasValue.4fc: %Optional.HasValue.type.4db = struct_value () [concrete] // CHECK:STDOUT: %Optional.Get.type.68c: type = fn_type @Optional.Get, @Optional(%Copy.facet) [concrete] // CHECK:STDOUT: %Optional.Get.756: %Optional.Get.type.68c = struct_value () [concrete] // CHECK:STDOUT: %Body.type: type = fn_type @Body [concrete] // CHECK:STDOUT: %Body: %Body.type = struct_value () [concrete] // CHECK:STDOUT: %AfterLoop.type: type = fn_type @AfterLoop [concrete] // CHECK:STDOUT: %AfterLoop: %AfterLoop.type = struct_value () [concrete] // CHECK:STDOUT: %empty_struct: %empty_struct_type = struct_value () [concrete] // CHECK:STDOUT: %TrivialRange.val: %TrivialRange = struct_value () [concrete] // CHECK:STDOUT: %.ccd: type = fn_type_with_self_type %Iterate.WithSelf.NewCursor.type.a3c, %Iterate.facet [concrete] // CHECK:STDOUT: %.827: type = fn_type_with_self_type %Iterate.WithSelf.Next.type.9dc, %Iterate.facet [concrete] // CHECK:STDOUT: %Optional.HasValue.specific_fn: = specific_function %Optional.HasValue.4fc, @Optional.HasValue(%Copy.facet) [concrete] // CHECK:STDOUT: %Optional.Get.specific_fn: = specific_function %Optional.Get.756, @Optional.Get(%Copy.facet) [concrete] // CHECK:STDOUT: %Destroy.Op.type.bae255.1: type = fn_type @Destroy.Op.loc18_35.1 [concrete] // CHECK:STDOUT: %Destroy.Op.651ba6.1: %Destroy.Op.type.bae255.1 = struct_value () [concrete] // CHECK:STDOUT: %Destroy.Op.type.bae255.2: type = fn_type @Destroy.Op.loc18_35.2 [concrete] // CHECK:STDOUT: %Destroy.Op.651ba6.2: %Destroy.Op.type.bae255.2 = struct_value () [concrete] // CHECK:STDOUT: %Destroy.Op.type.bae255.3: type = fn_type @Destroy.Op.loc18_20 [concrete] // CHECK:STDOUT: %Destroy.Op.651ba6.3: %Destroy.Op.type.bae255.3 = struct_value () [concrete] // CHECK:STDOUT: %empty_tuple.type.as.Copy.impl.Op.type: type = fn_type @empty_tuple.type.as.Copy.impl.Op [concrete] // CHECK:STDOUT: %empty_tuple.type.as.Copy.impl.Op: %empty_tuple.type.as.Copy.impl.Op.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core.import_ref.228: @Optional.%Optional.HasValue.type (%Optional.HasValue.type.958) = import_ref Core//prelude/parts/iterate, inst{{[0-9A-F]+}} [indirect], loaded [symbolic = @Optional.%Optional.HasValue (constants.%Optional.HasValue.d9c)] // CHECK:STDOUT: %Core.import_ref.f79: @Optional.%Optional.Get.type (%Optional.Get.type.5bc) = import_ref Core//prelude/parts/iterate, inst{{[0-9A-F]+}} [indirect], loaded [symbolic = @Optional.%Optional.Get (constants.%Optional.Get.fa5)] // CHECK:STDOUT: %Core.import_ref.903: %empty_tuple.type.as.Copy.impl.Op.type = import_ref Core//prelude/parts/copy, loc{{\d+_\d+}}, loaded [concrete = constants.%empty_tuple.type.as.Copy.impl.Op] // CHECK:STDOUT: %Copy.impl_witness_table.951 = impl_witness_table (%Core.import_ref.903), @empty_tuple.type.as.Copy.impl [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @Run() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %_.patt: %pattern_type.cb1 = value_binding_pattern _ [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %.loc18_18.1: %empty_struct_type = struct_literal () [concrete = constants.%empty_struct] // CHECK:STDOUT: %TrivialRange.ref: type = name_ref TrivialRange, file.%TrivialRange.decl [concrete = constants.%TrivialRange] // CHECK:STDOUT: %.loc18_18.2: ref %TrivialRange = temporary_storage // CHECK:STDOUT: %.loc18_18.3: init %TrivialRange to %.loc18_18.2 = class_init () [concrete = constants.%TrivialRange.val] // CHECK:STDOUT: %.loc18_20.1: init %TrivialRange = converted %.loc18_18.1, %.loc18_18.3 [concrete = constants.%TrivialRange.val] // CHECK:STDOUT: %.loc18_20.2: ref %TrivialRange = temporary %.loc18_18.2, %.loc18_20.1 // CHECK:STDOUT: %impl.elem2: %.ccd = impl_witness_access constants.%Iterate.impl_witness, element2 [concrete = constants.%TrivialRange.as.Iterate.impl.NewCursor.c71f42.2] // CHECK:STDOUT: %bound_method.loc18_35.1: = bound_method %.loc18_20.2, %impl.elem2 // CHECK:STDOUT: %.loc18_20.3: %TrivialRange = acquire_value %.loc18_20.2 // CHECK:STDOUT: %NewCursor.ref: %TrivialRange.as.Iterate.impl.NewCursor.type.408fa8.1 = name_ref NewCursor, @TrivialRange.as.Iterate.impl.%TrivialRange.as.Iterate.impl.NewCursor.decl.loc6_39.1 [concrete = constants.%TrivialRange.as.Iterate.impl.NewCursor.c71f42.1] // CHECK:STDOUT: %TrivialRange.as.Iterate.impl.NewCursor.bound: = bound_method %.loc18_20.3, %NewCursor.ref // CHECK:STDOUT: %TrivialRange.as.Iterate.impl.NewCursor.call: init %empty_tuple.type = call %TrivialRange.as.Iterate.impl.NewCursor.bound(%.loc18_20.3) // CHECK:STDOUT: %var: ref %empty_tuple.type = var invalid // CHECK:STDOUT: assign %var, %TrivialRange.as.Iterate.impl.NewCursor.call // CHECK:STDOUT: br !for.next // CHECK:STDOUT: // CHECK:STDOUT: !for.next: // CHECK:STDOUT: %addr: %ptr.843 = addr_of %var // CHECK:STDOUT: %impl.elem3: %.827 = impl_witness_access constants.%Iterate.impl_witness, element3 [concrete = constants.%TrivialRange.as.Iterate.impl.Next] // CHECK:STDOUT: %bound_method.loc18_35.2: = bound_method %.loc18_20.2, %impl.elem3 // CHECK:STDOUT: %.loc18_35.1: ref %Optional.311 = temporary_storage // CHECK:STDOUT: %.loc18_20.4: %TrivialRange = acquire_value %.loc18_20.2 // CHECK:STDOUT: %TrivialRange.as.Iterate.impl.Next.call: init %Optional.311 to %.loc18_35.1 = call %bound_method.loc18_35.2(%.loc18_20.4, %addr) // CHECK:STDOUT: %.loc18_35.2: ref %Optional.311 = temporary %.loc18_35.1, %TrivialRange.as.Iterate.impl.Next.call // CHECK:STDOUT: %.loc18_35.3: %Optional.HasValue.type.4db = specific_constant imports.%Core.import_ref.228, @Optional(constants.%Copy.facet) [concrete = constants.%Optional.HasValue.4fc] // CHECK:STDOUT: %HasValue.ref: %Optional.HasValue.type.4db = name_ref HasValue, %.loc18_35.3 [concrete = constants.%Optional.HasValue.4fc] // CHECK:STDOUT: %Optional.HasValue.bound: = bound_method %.loc18_35.2, %HasValue.ref // CHECK:STDOUT: %Optional.HasValue.specific_fn: = specific_function %HasValue.ref, @Optional.HasValue(constants.%Copy.facet) [concrete = constants.%Optional.HasValue.specific_fn] // CHECK:STDOUT: %bound_method.loc18_35.3: = bound_method %.loc18_35.2, %Optional.HasValue.specific_fn // CHECK:STDOUT: %.loc18_35.4: %Optional.311 = acquire_value %.loc18_35.2 // CHECK:STDOUT: %Optional.HasValue.call: init bool = call %bound_method.loc18_35.3(%.loc18_35.4) // CHECK:STDOUT: %.loc18_35.5: bool = value_of_initializer %Optional.HasValue.call // CHECK:STDOUT: %.loc18_35.6: bool = converted %Optional.HasValue.call, %.loc18_35.5 // CHECK:STDOUT: if %.loc18_35.6 br !for.body else br !for.done // CHECK:STDOUT: // CHECK:STDOUT: !for.body: // CHECK:STDOUT: %.loc18_35.7: %Optional.Get.type.68c = specific_constant imports.%Core.import_ref.f79, @Optional(constants.%Copy.facet) [concrete = constants.%Optional.Get.756] // CHECK:STDOUT: %Get.ref: %Optional.Get.type.68c = name_ref Get, %.loc18_35.7 [concrete = constants.%Optional.Get.756] // CHECK:STDOUT: %Optional.Get.bound: = bound_method %.loc18_35.2, %Get.ref // CHECK:STDOUT: %Optional.Get.specific_fn: = specific_function %Get.ref, @Optional.Get(constants.%Copy.facet) [concrete = constants.%Optional.Get.specific_fn] // CHECK:STDOUT: %bound_method.loc18_35.4: = bound_method %.loc18_35.2, %Optional.Get.specific_fn // CHECK:STDOUT: %.loc18_35.8: %Optional.311 = acquire_value %.loc18_35.2 // CHECK:STDOUT: %Optional.Get.call: init %empty_tuple.type = call %bound_method.loc18_35.4(%.loc18_35.8) // CHECK:STDOUT: %.loc18_12.1: type = splice_block %.loc18_12.3 [concrete = constants.%empty_tuple.type] { // CHECK:STDOUT: %.loc18_12.2: %empty_tuple.type = tuple_literal () [concrete = constants.%empty_tuple] // CHECK:STDOUT: %.loc18_12.3: type = converted %.loc18_12.2, constants.%empty_tuple.type [concrete = constants.%empty_tuple.type] // CHECK:STDOUT: } // CHECK:STDOUT: %.loc18_35.9: ref %empty_tuple.type = temporary_storage // CHECK:STDOUT: %.loc18_35.10: ref %empty_tuple.type = temporary %.loc18_35.9, %Optional.Get.call // CHECK:STDOUT: %tuple: %empty_tuple.type = tuple_value () [concrete = constants.%empty_tuple] // CHECK:STDOUT: %.loc18_35.11: %empty_tuple.type = converted %Optional.Get.call, %tuple [concrete = constants.%empty_tuple] // CHECK:STDOUT: %_: %empty_tuple.type = value_binding _, %.loc18_35.11 // CHECK:STDOUT: %Body.ref: %Body.type = name_ref Body, file.%Body.decl [concrete = constants.%Body] // CHECK:STDOUT: %Body.call: init %empty_tuple.type = call %Body.ref() // CHECK:STDOUT: br !for.next // CHECK:STDOUT: // CHECK:STDOUT: !for.done: // CHECK:STDOUT: %AfterLoop.ref: %AfterLoop.type = name_ref AfterLoop, file.%AfterLoop.decl [concrete = constants.%AfterLoop] // CHECK:STDOUT: %AfterLoop.call: init %empty_tuple.type = call %AfterLoop.ref() // CHECK:STDOUT: %Destroy.Op.bound.loc18_35.1: = bound_method %.loc18_35.10, constants.%Destroy.Op.651ba6.1 // CHECK:STDOUT: %Destroy.Op.call.loc18_35.1: init %empty_tuple.type = call %Destroy.Op.bound.loc18_35.1(%.loc18_35.10) // CHECK:STDOUT: %Destroy.Op.bound.loc18_35.2: = bound_method %.loc18_35.2, constants.%Destroy.Op.651ba6.2 // CHECK:STDOUT: %Destroy.Op.call.loc18_35.2: init %empty_tuple.type = call %Destroy.Op.bound.loc18_35.2(%.loc18_35.2) // CHECK:STDOUT: %Destroy.Op.bound.loc18_35.3: = bound_method %var, constants.%Destroy.Op.651ba6.1 // CHECK:STDOUT: %Destroy.Op.call.loc18_35.3: init %empty_tuple.type = call %Destroy.Op.bound.loc18_35.3(%var) // CHECK:STDOUT: %Destroy.Op.bound.loc18_20: = bound_method %.loc18_20.2, constants.%Destroy.Op.651ba6.3 // CHECK:STDOUT: %Destroy.Op.call.loc18_20: init %empty_tuple.type = call %Destroy.Op.bound.loc18_20(%.loc18_20.2) // CHECK:STDOUT: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @Destroy.Op.loc18_35.1(%self.param: ref %empty_tuple.type) = "no_op"; // CHECK:STDOUT: // CHECK:STDOUT: fn @Destroy.Op.loc18_35.2(%self.param: ref %Optional.311) = "no_op"; // CHECK:STDOUT: // CHECK:STDOUT: fn @Destroy.Op.loc18_20(%self.param: ref %TrivialRange) = "no_op"; // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/for/pattern.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/for.carbon // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/for/pattern.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/for/pattern.carbon // --- empty_range.carbon library "[[@TEST_NAME]]"; class EmptyRange(T:! Core.Copy) { fn Make() -> Self { return {}; } impl as Core.Iterate where .CursorType = {} and .ElementType = T { fn NewCursor[unused self: Self]() -> {} { return {}; } fn Next[unused self: Self](unused cursor: {}*) -> Core.Optional(T) { return Core.Optional(T).None(); } } } class C { impl as Core.Copy { fn Op[unused self: Self]() -> Self { return {}; } } } // --- value.carbon library "[[@TEST_NAME]]"; import library "empty_range"; fn Body(c: C); fn Run() { //@dump-sem-ir-begin for (c: C in EmptyRange(C).Make()) { Body(c); } //@dump-sem-ir-end } // --- var.carbon library "[[@TEST_NAME]]"; import library "empty_range"; fn Body(c: C*); fn Run() { //@dump-sem-ir-begin for (var c: C in EmptyRange(C).Make()) { Body(&c); } //@dump-sem-ir-end } // --- tuple.carbon library "[[@TEST_NAME]]"; import library "empty_range"; fn Body(a: bool, b: bool); fn Run() { //@dump-sem-ir-begin for ((a: bool, b: bool) in EmptyRange((bool, bool)).Make()) { Body(a, b); } //@dump-sem-ir-end } // --- tuple_class.carbon library "[[@TEST_NAME]]"; import library "empty_range"; fn Body(a: C, b: C); fn Run() { //@dump-sem-ir-begin for ((a: C, b: C) in EmptyRange((C, C)).Make()) { Body(a, b); } //@dump-sem-ir-end } // --- fail_bad_pattern.carbon library "[[@TEST_NAME]]"; import library "empty_range"; class X {} fn Run() { // CHECK:STDERR: fail_bad_pattern.carbon:[[@LINE+7]]:7: error: cannot implicitly convert expression of type `C` to `X` [ConversionFailure] // CHECK:STDERR: for (unused x: X in EmptyRange(C).Make()) { // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: fail_bad_pattern.carbon:[[@LINE+4]]:7: note: type `C` does not implement interface `Core.ImplicitAs(X)` [MissingImplInMemberAccessInContext] // CHECK:STDERR: for (unused x: X in EmptyRange(C).Make()) { // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: for (unused x: X in EmptyRange(C).Make()) { } } // CHECK:STDOUT: --- value.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %C: type = class_type @C [concrete] // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %pattern_type.7c7: type = pattern_type %C [concrete] // CHECK:STDOUT: %Body.type: type = fn_type @Body [concrete] // CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] // CHECK:STDOUT: %Body: %Body.type = struct_value () [concrete] // CHECK:STDOUT: %ptr.c28: type = ptr_type %empty_struct_type [concrete] // CHECK:STDOUT: %EmptyRange.type: type = generic_class_type @EmptyRange [concrete] // CHECK:STDOUT: %EmptyRange.generic: %EmptyRange.type = struct_value () [concrete] // CHECK:STDOUT: %Copy.type: type = facet_type <@Copy> [concrete] // CHECK:STDOUT: %T.56c: %Copy.type = symbolic_binding T, 0 [symbolic] // CHECK:STDOUT: %EmptyRange.Make.type.8d6: type = fn_type @EmptyRange.Make, @EmptyRange(%T.56c) [symbolic] // CHECK:STDOUT: %EmptyRange.Make.0dc: %EmptyRange.Make.type.8d6 = struct_value () [symbolic] // CHECK:STDOUT: %Copy.impl_witness.fab: = impl_witness imports.%Copy.impl_witness_table.c52 [concrete] // CHECK:STDOUT: %Copy.facet: %Copy.type = facet_value %C, (%Copy.impl_witness.fab) [concrete] // CHECK:STDOUT: %EmptyRange.77b: type = class_type @EmptyRange, @EmptyRange(%Copy.facet) [concrete] // CHECK:STDOUT: %EmptyRange.Make.type.c0f: type = fn_type @EmptyRange.Make, @EmptyRange(%Copy.facet) [concrete] // CHECK:STDOUT: %EmptyRange.Make.f9f: %EmptyRange.Make.type.c0f = struct_value () [concrete] // CHECK:STDOUT: %EmptyRange.Make.specific_fn: = specific_function %EmptyRange.Make.f9f, @EmptyRange.Make(%Copy.facet) [concrete] // CHECK:STDOUT: %Iterate.type: type = facet_type <@Iterate> [concrete] // CHECK:STDOUT: %Optional.Get.type.1ac: type = fn_type @Optional.Get, @Optional(%T.56c) [symbolic] // CHECK:STDOUT: %Optional.Get.748: %Optional.Get.type.1ac = struct_value () [symbolic] // CHECK:STDOUT: %Optional.HasValue.type.051: type = fn_type @Optional.HasValue, @Optional(%T.56c) [symbolic] // CHECK:STDOUT: %Optional.HasValue.6ae: %Optional.HasValue.type.051 = struct_value () [symbolic] // CHECK:STDOUT: %EmptyRange.as.Iterate.impl.Next.type.b9d: type = fn_type @EmptyRange.as.Iterate.impl.Next, @EmptyRange.as.Iterate.impl(%T.56c) [symbolic] // CHECK:STDOUT: %EmptyRange.as.Iterate.impl.Next.682: %EmptyRange.as.Iterate.impl.Next.type.b9d = struct_value () [symbolic] // CHECK:STDOUT: %EmptyRange.as.Iterate.impl.NewCursor.type.16c: type = fn_type @EmptyRange.as.Iterate.impl.NewCursor, @EmptyRange.as.Iterate.impl(%T.56c) [symbolic] // CHECK:STDOUT: %EmptyRange.as.Iterate.impl.NewCursor.cbe: %EmptyRange.as.Iterate.impl.NewCursor.type.16c = struct_value () [symbolic] // CHECK:STDOUT: %Iterate.impl_witness.96d: = impl_witness imports.%Iterate.impl_witness_table, @EmptyRange.as.Iterate.impl(%Copy.facet) [concrete] // CHECK:STDOUT: %EmptyRange.as.Iterate.impl.NewCursor.type.268: type = fn_type @EmptyRange.as.Iterate.impl.NewCursor, @EmptyRange.as.Iterate.impl(%Copy.facet) [concrete] // CHECK:STDOUT: %EmptyRange.as.Iterate.impl.NewCursor.da5: %EmptyRange.as.Iterate.impl.NewCursor.type.268 = struct_value () [concrete] // CHECK:STDOUT: %EmptyRange.as.Iterate.impl.Next.type.046: type = fn_type @EmptyRange.as.Iterate.impl.Next, @EmptyRange.as.Iterate.impl(%Copy.facet) [concrete] // CHECK:STDOUT: %EmptyRange.as.Iterate.impl.Next.d08: %EmptyRange.as.Iterate.impl.Next.type.046 = struct_value () [concrete] // CHECK:STDOUT: %Iterate.facet: %Iterate.type = facet_value %EmptyRange.77b, (%Iterate.impl_witness.96d) [concrete] // CHECK:STDOUT: %Iterate.WithSelf.NewCursor.type.a35: type = fn_type @Iterate.WithSelf.NewCursor, @Iterate.WithSelf(%Iterate.facet) [concrete] // CHECK:STDOUT: %Iterate.WithSelf.Next.type.7b4: type = fn_type @Iterate.WithSelf.Next, @Iterate.WithSelf(%Iterate.facet) [concrete] // CHECK:STDOUT: %.e1f: type = fn_type_with_self_type %Iterate.WithSelf.NewCursor.type.a35, %Iterate.facet [concrete] // CHECK:STDOUT: %EmptyRange.as.Iterate.impl.NewCursor.specific_fn: = specific_function %EmptyRange.as.Iterate.impl.NewCursor.da5, @EmptyRange.as.Iterate.impl.NewCursor(%Copy.facet) [concrete] // CHECK:STDOUT: %.527: type = fn_type_with_self_type %Iterate.WithSelf.Next.type.7b4, %Iterate.facet [concrete] // CHECK:STDOUT: %Optional.e59: type = class_type @Optional, @Optional(%Copy.facet) [concrete] // CHECK:STDOUT: %EmptyRange.as.Iterate.impl.Next.specific_fn: = specific_function %EmptyRange.as.Iterate.impl.Next.d08, @EmptyRange.as.Iterate.impl.Next(%Copy.facet) [concrete] // CHECK:STDOUT: %Optional.HasValue.type.26b: type = fn_type @Optional.HasValue, @Optional(%Copy.facet) [concrete] // CHECK:STDOUT: %Optional.HasValue.61c: %Optional.HasValue.type.26b = struct_value () [concrete] // CHECK:STDOUT: %Optional.Get.type.7ce: type = fn_type @Optional.Get, @Optional(%Copy.facet) [concrete] // CHECK:STDOUT: %Optional.Get.bce: %Optional.Get.type.7ce = struct_value () [concrete] // CHECK:STDOUT: %Optional.HasValue.specific_fn: = specific_function %Optional.HasValue.61c, @Optional.HasValue(%Copy.facet) [concrete] // CHECK:STDOUT: %Optional.Get.specific_fn: = specific_function %Optional.Get.bce, @Optional.Get(%Copy.facet) [concrete] // CHECK:STDOUT: %Destroy.Op.type.bae255.1: type = fn_type @Destroy.Op.loc10_36.1 [concrete] // CHECK:STDOUT: %Destroy.Op.651ba6.1: %Destroy.Op.type.bae255.1 = struct_value () [concrete] // CHECK:STDOUT: %Destroy.Op.type.bae255.2: type = fn_type @Destroy.Op.loc10_36.2 [concrete] // CHECK:STDOUT: %Destroy.Op.651ba6.2: %Destroy.Op.type.bae255.2 = struct_value () [concrete] // CHECK:STDOUT: %Destroy.Op.type.bae255.3: type = fn_type @Destroy.Op.loc10_36.3 [concrete] // CHECK:STDOUT: %Destroy.Op.651ba6.3: %Destroy.Op.type.bae255.3 = struct_value () [concrete] // CHECK:STDOUT: %Destroy.Op.type.bae255.4: type = fn_type @Destroy.Op.loc10_35 [concrete] // CHECK:STDOUT: %Destroy.Op.651ba6.4: %Destroy.Op.type.bae255.4 = struct_value () [concrete] // CHECK:STDOUT: %C.as.Copy.impl.Op.type: type = fn_type @C.as.Copy.impl.Op [concrete] // CHECK:STDOUT: %C.as.Copy.impl.Op: %C.as.Copy.impl.Op.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Main.EmptyRange: %EmptyRange.type = import_ref Main//empty_range, EmptyRange, loaded [concrete = constants.%EmptyRange.generic] // CHECK:STDOUT: %Main.C: type = import_ref Main//empty_range, C, loaded [concrete = constants.%C] // CHECK:STDOUT: %Main.import_ref.299: @EmptyRange.%EmptyRange.Make.type (%EmptyRange.Make.type.8d6) = import_ref Main//empty_range, loc5_21, loaded [symbolic = @EmptyRange.%EmptyRange.Make (constants.%EmptyRange.Make.0dc)] // CHECK:STDOUT: %Main.import_ref.cb5: %C.as.Copy.impl.Op.type = import_ref Main//empty_range, loc19_40, loaded [concrete = constants.%C.as.Copy.impl.Op] // CHECK:STDOUT: %Copy.impl_witness_table.c52 = impl_witness_table (%Main.import_ref.cb5), @C.as.Copy.impl [concrete] // CHECK:STDOUT: %Core.import_ref.f5d: @Optional.%Optional.HasValue.type (%Optional.HasValue.type.051) = import_ref Core//prelude/parts/iterate, inst{{[0-9A-F]+}} [indirect], loaded [symbolic = @Optional.%Optional.HasValue (constants.%Optional.HasValue.6ae)] // CHECK:STDOUT: %Core.import_ref.ab7: @Optional.%Optional.Get.type (%Optional.Get.type.1ac) = import_ref Core//prelude/parts/iterate, inst{{[0-9A-F]+}} [indirect], loaded [symbolic = @Optional.%Optional.Get (constants.%Optional.Get.748)] // CHECK:STDOUT: %Main.import_ref.888 = import_ref Main//empty_range, loc7_68, unloaded // CHECK:STDOUT: %Main.import_ref.999 = import_ref Main//empty_range, loc7_68, unloaded // CHECK:STDOUT: %Main.import_ref.868: @EmptyRange.as.Iterate.impl.%EmptyRange.as.Iterate.impl.NewCursor.type (%EmptyRange.as.Iterate.impl.NewCursor.type.16c) = import_ref Main//empty_range, loc8_45, loaded [symbolic = @EmptyRange.as.Iterate.impl.%EmptyRange.as.Iterate.impl.NewCursor (constants.%EmptyRange.as.Iterate.impl.NewCursor.cbe)] // CHECK:STDOUT: %Main.import_ref.574: @EmptyRange.as.Iterate.impl.%EmptyRange.as.Iterate.impl.Next.type (%EmptyRange.as.Iterate.impl.Next.type.b9d) = import_ref Main//empty_range, loc11_72, loaded [symbolic = @EmptyRange.as.Iterate.impl.%EmptyRange.as.Iterate.impl.Next (constants.%EmptyRange.as.Iterate.impl.Next.682)] // CHECK:STDOUT: %Iterate.impl_witness_table = impl_witness_table (%Main.import_ref.888, %Main.import_ref.999, %Main.import_ref.868, %Main.import_ref.574), @EmptyRange.as.Iterate.impl [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @Run() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %c.patt: %pattern_type.7c7 = value_binding_pattern c [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %EmptyRange.ref: %EmptyRange.type = name_ref EmptyRange, imports.%Main.EmptyRange [concrete = constants.%EmptyRange.generic] // CHECK:STDOUT: %C.ref.loc10_27: type = name_ref C, imports.%Main.C [concrete = constants.%C] // CHECK:STDOUT: %Copy.facet: %Copy.type = facet_value %C.ref.loc10_27, (constants.%Copy.impl_witness.fab) [concrete = constants.%Copy.facet] // CHECK:STDOUT: %.loc10_28: %Copy.type = converted %C.ref.loc10_27, %Copy.facet [concrete = constants.%Copy.facet] // CHECK:STDOUT: %EmptyRange: type = class_type @EmptyRange, @EmptyRange(constants.%Copy.facet) [concrete = constants.%EmptyRange.77b] // CHECK:STDOUT: %.loc10_29: %EmptyRange.Make.type.c0f = specific_constant imports.%Main.import_ref.299, @EmptyRange(constants.%Copy.facet) [concrete = constants.%EmptyRange.Make.f9f] // CHECK:STDOUT: %Make.ref: %EmptyRange.Make.type.c0f = name_ref Make, %.loc10_29 [concrete = constants.%EmptyRange.Make.f9f] // CHECK:STDOUT: %EmptyRange.Make.specific_fn: = specific_function %Make.ref, @EmptyRange.Make(constants.%Copy.facet) [concrete = constants.%EmptyRange.Make.specific_fn] // CHECK:STDOUT: %.loc10_35.1: ref %EmptyRange.77b = temporary_storage // CHECK:STDOUT: %EmptyRange.Make.call: init %EmptyRange.77b to %.loc10_35.1 = call %EmptyRange.Make.specific_fn() // CHECK:STDOUT: %.loc10_35.2: ref %EmptyRange.77b = temporary %.loc10_35.1, %EmptyRange.Make.call // CHECK:STDOUT: %impl.elem2: %.e1f = impl_witness_access constants.%Iterate.impl_witness.96d, element2 [concrete = constants.%EmptyRange.as.Iterate.impl.NewCursor.da5] // CHECK:STDOUT: %bound_method.loc10_36.1: = bound_method %.loc10_35.2, %impl.elem2 // CHECK:STDOUT: %specific_fn.loc10_36.1: = specific_function %impl.elem2, @EmptyRange.as.Iterate.impl.NewCursor(constants.%Copy.facet) [concrete = constants.%EmptyRange.as.Iterate.impl.NewCursor.specific_fn] // CHECK:STDOUT: %bound_method.loc10_36.2: = bound_method %.loc10_35.2, %specific_fn.loc10_36.1 // CHECK:STDOUT: %.loc10_35.3: %EmptyRange.77b = acquire_value %.loc10_35.2 // CHECK:STDOUT: %EmptyRange.as.Iterate.impl.NewCursor.call: init %empty_struct_type = call %bound_method.loc10_36.2(%.loc10_35.3) // CHECK:STDOUT: %var: ref %empty_struct_type = var invalid // CHECK:STDOUT: assign %var, %EmptyRange.as.Iterate.impl.NewCursor.call // CHECK:STDOUT: br !for.next // CHECK:STDOUT: // CHECK:STDOUT: !for.next: // CHECK:STDOUT: %addr: %ptr.c28 = addr_of %var // CHECK:STDOUT: %impl.elem3: %.527 = impl_witness_access constants.%Iterate.impl_witness.96d, element3 [concrete = constants.%EmptyRange.as.Iterate.impl.Next.d08] // CHECK:STDOUT: %bound_method.loc10_36.3: = bound_method %.loc10_35.2, %impl.elem3 // CHECK:STDOUT: %specific_fn.loc10_36.2: = specific_function %impl.elem3, @EmptyRange.as.Iterate.impl.Next(constants.%Copy.facet) [concrete = constants.%EmptyRange.as.Iterate.impl.Next.specific_fn] // CHECK:STDOUT: %bound_method.loc10_36.4: = bound_method %.loc10_35.2, %specific_fn.loc10_36.2 // CHECK:STDOUT: %.loc10_36.1: ref %Optional.e59 = temporary_storage // CHECK:STDOUT: %.loc10_35.4: %EmptyRange.77b = acquire_value %.loc10_35.2 // CHECK:STDOUT: %EmptyRange.as.Iterate.impl.Next.call: init %Optional.e59 to %.loc10_36.1 = call %bound_method.loc10_36.4(%.loc10_35.4, %addr) // CHECK:STDOUT: %.loc10_36.2: ref %Optional.e59 = temporary %.loc10_36.1, %EmptyRange.as.Iterate.impl.Next.call // CHECK:STDOUT: %.loc10_36.3: %Optional.HasValue.type.26b = specific_constant imports.%Core.import_ref.f5d, @Optional(constants.%Copy.facet) [concrete = constants.%Optional.HasValue.61c] // CHECK:STDOUT: %HasValue.ref: %Optional.HasValue.type.26b = name_ref HasValue, %.loc10_36.3 [concrete = constants.%Optional.HasValue.61c] // CHECK:STDOUT: %Optional.HasValue.bound: = bound_method %.loc10_36.2, %HasValue.ref // CHECK:STDOUT: %Optional.HasValue.specific_fn: = specific_function %HasValue.ref, @Optional.HasValue(constants.%Copy.facet) [concrete = constants.%Optional.HasValue.specific_fn] // CHECK:STDOUT: %bound_method.loc10_36.5: = bound_method %.loc10_36.2, %Optional.HasValue.specific_fn // CHECK:STDOUT: %.loc10_36.4: %Optional.e59 = acquire_value %.loc10_36.2 // CHECK:STDOUT: %Optional.HasValue.call: init bool = call %bound_method.loc10_36.5(%.loc10_36.4) // CHECK:STDOUT: %.loc10_36.5: bool = value_of_initializer %Optional.HasValue.call // CHECK:STDOUT: %.loc10_36.6: bool = converted %Optional.HasValue.call, %.loc10_36.5 // CHECK:STDOUT: if %.loc10_36.6 br !for.body else br !for.done // CHECK:STDOUT: // CHECK:STDOUT: !for.body: // CHECK:STDOUT: %.loc10_36.7: %Optional.Get.type.7ce = specific_constant imports.%Core.import_ref.ab7, @Optional(constants.%Copy.facet) [concrete = constants.%Optional.Get.bce] // CHECK:STDOUT: %Get.ref: %Optional.Get.type.7ce = name_ref Get, %.loc10_36.7 [concrete = constants.%Optional.Get.bce] // CHECK:STDOUT: %Optional.Get.bound: = bound_method %.loc10_36.2, %Get.ref // CHECK:STDOUT: %Optional.Get.specific_fn: = specific_function %Get.ref, @Optional.Get(constants.%Copy.facet) [concrete = constants.%Optional.Get.specific_fn] // CHECK:STDOUT: %bound_method.loc10_36.6: = bound_method %.loc10_36.2, %Optional.Get.specific_fn // CHECK:STDOUT: %.loc10_36.8: ref %C = temporary_storage // CHECK:STDOUT: %.loc10_36.9: %Optional.e59 = acquire_value %.loc10_36.2 // CHECK:STDOUT: %Optional.Get.call: init %C to %.loc10_36.8 = call %bound_method.loc10_36.6(%.loc10_36.9) // CHECK:STDOUT: %C.ref.loc10_11: type = name_ref C, imports.%Main.C [concrete = constants.%C] // CHECK:STDOUT: %.loc10_36.10: ref %C = temporary %.loc10_36.8, %Optional.Get.call // CHECK:STDOUT: %.loc10_36.11: %C = acquire_value %.loc10_36.10 // CHECK:STDOUT: %c: %C = value_binding c, %.loc10_36.11 // CHECK:STDOUT: %Body.ref: %Body.type = name_ref Body, file.%Body.decl [concrete = constants.%Body] // CHECK:STDOUT: %c.ref: %C = name_ref c, %c // CHECK:STDOUT: %Body.call: init %empty_tuple.type = call %Body.ref(%c.ref) // CHECK:STDOUT: br !for.next // CHECK:STDOUT: // CHECK:STDOUT: !for.done: // CHECK:STDOUT: %Destroy.Op.bound.loc10_36.1: = bound_method %.loc10_36.10, constants.%Destroy.Op.651ba6.1 // CHECK:STDOUT: %Destroy.Op.call.loc10_36.1: init %empty_tuple.type = call %Destroy.Op.bound.loc10_36.1(%.loc10_36.10) // CHECK:STDOUT: %Destroy.Op.bound.loc10_36.2: = bound_method %.loc10_36.2, constants.%Destroy.Op.651ba6.2 // CHECK:STDOUT: %Destroy.Op.call.loc10_36.2: init %empty_tuple.type = call %Destroy.Op.bound.loc10_36.2(%.loc10_36.2) // CHECK:STDOUT: %Destroy.Op.bound.loc10_36.3: = bound_method %var, constants.%Destroy.Op.651ba6.3 // CHECK:STDOUT: %Destroy.Op.call.loc10_36.3: init %empty_tuple.type = call %Destroy.Op.bound.loc10_36.3(%var) // CHECK:STDOUT: %Destroy.Op.bound.loc10_35: = bound_method %.loc10_35.2, constants.%Destroy.Op.651ba6.4 // CHECK:STDOUT: %Destroy.Op.call.loc10_35: init %empty_tuple.type = call %Destroy.Op.bound.loc10_35(%.loc10_35.2) // CHECK:STDOUT: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @Destroy.Op.loc10_36.1(%self.param: ref %C) = "no_op"; // CHECK:STDOUT: // CHECK:STDOUT: fn @Destroy.Op.loc10_36.2(%self.param: ref %Optional.e59) = "no_op"; // CHECK:STDOUT: // CHECK:STDOUT: fn @Destroy.Op.loc10_36.3(%self.param: ref %empty_struct_type) = "no_op"; // CHECK:STDOUT: // CHECK:STDOUT: fn @Destroy.Op.loc10_35(%self.param: ref %EmptyRange.77b) = "no_op"; // CHECK:STDOUT: // CHECK:STDOUT: --- var.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %C: type = class_type @C [concrete] // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %ptr.31e: type = ptr_type %C [concrete] // CHECK:STDOUT: %Body.type: type = fn_type @Body [concrete] // CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] // CHECK:STDOUT: %Body: %Body.type = struct_value () [concrete] // CHECK:STDOUT: %ptr.c28: type = ptr_type %empty_struct_type [concrete] // CHECK:STDOUT: %pattern_type.7c7: type = pattern_type %C [concrete] // CHECK:STDOUT: %EmptyRange.type: type = generic_class_type @EmptyRange [concrete] // CHECK:STDOUT: %EmptyRange.generic: %EmptyRange.type = struct_value () [concrete] // CHECK:STDOUT: %Copy.type: type = facet_type <@Copy> [concrete] // CHECK:STDOUT: %T.56c: %Copy.type = symbolic_binding T, 0 [symbolic] // CHECK:STDOUT: %EmptyRange.Make.type.8d6: type = fn_type @EmptyRange.Make, @EmptyRange(%T.56c) [symbolic] // CHECK:STDOUT: %EmptyRange.Make.0dc: %EmptyRange.Make.type.8d6 = struct_value () [symbolic] // CHECK:STDOUT: %Copy.impl_witness.fab: = impl_witness imports.%Copy.impl_witness_table.c52 [concrete] // CHECK:STDOUT: %Copy.facet: %Copy.type = facet_value %C, (%Copy.impl_witness.fab) [concrete] // CHECK:STDOUT: %EmptyRange.77b: type = class_type @EmptyRange, @EmptyRange(%Copy.facet) [concrete] // CHECK:STDOUT: %EmptyRange.Make.type.c0f: type = fn_type @EmptyRange.Make, @EmptyRange(%Copy.facet) [concrete] // CHECK:STDOUT: %EmptyRange.Make.f9f: %EmptyRange.Make.type.c0f = struct_value () [concrete] // CHECK:STDOUT: %EmptyRange.Make.specific_fn: = specific_function %EmptyRange.Make.f9f, @EmptyRange.Make(%Copy.facet) [concrete] // CHECK:STDOUT: %Iterate.type: type = facet_type <@Iterate> [concrete] // CHECK:STDOUT: %Optional.Get.type.1ac: type = fn_type @Optional.Get, @Optional(%T.56c) [symbolic] // CHECK:STDOUT: %Optional.Get.748: %Optional.Get.type.1ac = struct_value () [symbolic] // CHECK:STDOUT: %Optional.HasValue.type.051: type = fn_type @Optional.HasValue, @Optional(%T.56c) [symbolic] // CHECK:STDOUT: %Optional.HasValue.6ae: %Optional.HasValue.type.051 = struct_value () [symbolic] // CHECK:STDOUT: %EmptyRange.as.Iterate.impl.Next.type.b9d: type = fn_type @EmptyRange.as.Iterate.impl.Next, @EmptyRange.as.Iterate.impl(%T.56c) [symbolic] // CHECK:STDOUT: %EmptyRange.as.Iterate.impl.Next.682: %EmptyRange.as.Iterate.impl.Next.type.b9d = struct_value () [symbolic] // CHECK:STDOUT: %EmptyRange.as.Iterate.impl.NewCursor.type.16c: type = fn_type @EmptyRange.as.Iterate.impl.NewCursor, @EmptyRange.as.Iterate.impl(%T.56c) [symbolic] // CHECK:STDOUT: %EmptyRange.as.Iterate.impl.NewCursor.cbe: %EmptyRange.as.Iterate.impl.NewCursor.type.16c = struct_value () [symbolic] // CHECK:STDOUT: %Iterate.impl_witness.96d: = impl_witness imports.%Iterate.impl_witness_table, @EmptyRange.as.Iterate.impl(%Copy.facet) [concrete] // CHECK:STDOUT: %EmptyRange.as.Iterate.impl.NewCursor.type.268: type = fn_type @EmptyRange.as.Iterate.impl.NewCursor, @EmptyRange.as.Iterate.impl(%Copy.facet) [concrete] // CHECK:STDOUT: %EmptyRange.as.Iterate.impl.NewCursor.da5: %EmptyRange.as.Iterate.impl.NewCursor.type.268 = struct_value () [concrete] // CHECK:STDOUT: %EmptyRange.as.Iterate.impl.Next.type.046: type = fn_type @EmptyRange.as.Iterate.impl.Next, @EmptyRange.as.Iterate.impl(%Copy.facet) [concrete] // CHECK:STDOUT: %EmptyRange.as.Iterate.impl.Next.d08: %EmptyRange.as.Iterate.impl.Next.type.046 = struct_value () [concrete] // CHECK:STDOUT: %Iterate.facet: %Iterate.type = facet_value %EmptyRange.77b, (%Iterate.impl_witness.96d) [concrete] // CHECK:STDOUT: %Iterate.WithSelf.NewCursor.type.a35: type = fn_type @Iterate.WithSelf.NewCursor, @Iterate.WithSelf(%Iterate.facet) [concrete] // CHECK:STDOUT: %Iterate.WithSelf.Next.type.7b4: type = fn_type @Iterate.WithSelf.Next, @Iterate.WithSelf(%Iterate.facet) [concrete] // CHECK:STDOUT: %.e1f: type = fn_type_with_self_type %Iterate.WithSelf.NewCursor.type.a35, %Iterate.facet [concrete] // CHECK:STDOUT: %EmptyRange.as.Iterate.impl.NewCursor.specific_fn: = specific_function %EmptyRange.as.Iterate.impl.NewCursor.da5, @EmptyRange.as.Iterate.impl.NewCursor(%Copy.facet) [concrete] // CHECK:STDOUT: %.527: type = fn_type_with_self_type %Iterate.WithSelf.Next.type.7b4, %Iterate.facet [concrete] // CHECK:STDOUT: %Optional.e59: type = class_type @Optional, @Optional(%Copy.facet) [concrete] // CHECK:STDOUT: %EmptyRange.as.Iterate.impl.Next.specific_fn: = specific_function %EmptyRange.as.Iterate.impl.Next.d08, @EmptyRange.as.Iterate.impl.Next(%Copy.facet) [concrete] // CHECK:STDOUT: %Optional.HasValue.type.26b: type = fn_type @Optional.HasValue, @Optional(%Copy.facet) [concrete] // CHECK:STDOUT: %Optional.HasValue.61c: %Optional.HasValue.type.26b = struct_value () [concrete] // CHECK:STDOUT: %Optional.Get.type.7ce: type = fn_type @Optional.Get, @Optional(%Copy.facet) [concrete] // CHECK:STDOUT: %Optional.Get.bce: %Optional.Get.type.7ce = struct_value () [concrete] // CHECK:STDOUT: %Optional.HasValue.specific_fn: = specific_function %Optional.HasValue.61c, @Optional.HasValue(%Copy.facet) [concrete] // CHECK:STDOUT: %Optional.Get.specific_fn: = specific_function %Optional.Get.bce, @Optional.Get(%Copy.facet) [concrete] // CHECK:STDOUT: %Destroy.Op.type.bae255.1: type = fn_type @Destroy.Op.loc10_8 [concrete] // CHECK:STDOUT: %Destroy.Op.651ba6.1: %Destroy.Op.type.bae255.1 = struct_value () [concrete] // CHECK:STDOUT: %Destroy.Op.type.bae255.2: type = fn_type @Destroy.Op.loc10_40.1 [concrete] // CHECK:STDOUT: %Destroy.Op.651ba6.2: %Destroy.Op.type.bae255.2 = struct_value () [concrete] // CHECK:STDOUT: %Destroy.Op.type.bae255.3: type = fn_type @Destroy.Op.loc10_40.2 [concrete] // CHECK:STDOUT: %Destroy.Op.651ba6.3: %Destroy.Op.type.bae255.3 = struct_value () [concrete] // CHECK:STDOUT: %Destroy.Op.type.bae255.4: type = fn_type @Destroy.Op.loc10_39 [concrete] // CHECK:STDOUT: %Destroy.Op.651ba6.4: %Destroy.Op.type.bae255.4 = struct_value () [concrete] // CHECK:STDOUT: %C.as.Copy.impl.Op.type: type = fn_type @C.as.Copy.impl.Op [concrete] // CHECK:STDOUT: %C.as.Copy.impl.Op: %C.as.Copy.impl.Op.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Main.EmptyRange: %EmptyRange.type = import_ref Main//empty_range, EmptyRange, loaded [concrete = constants.%EmptyRange.generic] // CHECK:STDOUT: %Main.C: type = import_ref Main//empty_range, C, loaded [concrete = constants.%C] // CHECK:STDOUT: %Main.import_ref.299: @EmptyRange.%EmptyRange.Make.type (%EmptyRange.Make.type.8d6) = import_ref Main//empty_range, loc5_21, loaded [symbolic = @EmptyRange.%EmptyRange.Make (constants.%EmptyRange.Make.0dc)] // CHECK:STDOUT: %Main.import_ref.cb5: %C.as.Copy.impl.Op.type = import_ref Main//empty_range, loc19_40, loaded [concrete = constants.%C.as.Copy.impl.Op] // CHECK:STDOUT: %Copy.impl_witness_table.c52 = impl_witness_table (%Main.import_ref.cb5), @C.as.Copy.impl [concrete] // CHECK:STDOUT: %Core.import_ref.f5d: @Optional.%Optional.HasValue.type (%Optional.HasValue.type.051) = import_ref Core//prelude/parts/iterate, inst{{[0-9A-F]+}} [indirect], loaded [symbolic = @Optional.%Optional.HasValue (constants.%Optional.HasValue.6ae)] // CHECK:STDOUT: %Core.import_ref.ab7: @Optional.%Optional.Get.type (%Optional.Get.type.1ac) = import_ref Core//prelude/parts/iterate, inst{{[0-9A-F]+}} [indirect], loaded [symbolic = @Optional.%Optional.Get (constants.%Optional.Get.748)] // CHECK:STDOUT: %Main.import_ref.888 = import_ref Main//empty_range, loc7_68, unloaded // CHECK:STDOUT: %Main.import_ref.999 = import_ref Main//empty_range, loc7_68, unloaded // CHECK:STDOUT: %Main.import_ref.868: @EmptyRange.as.Iterate.impl.%EmptyRange.as.Iterate.impl.NewCursor.type (%EmptyRange.as.Iterate.impl.NewCursor.type.16c) = import_ref Main//empty_range, loc8_45, loaded [symbolic = @EmptyRange.as.Iterate.impl.%EmptyRange.as.Iterate.impl.NewCursor (constants.%EmptyRange.as.Iterate.impl.NewCursor.cbe)] // CHECK:STDOUT: %Main.import_ref.574: @EmptyRange.as.Iterate.impl.%EmptyRange.as.Iterate.impl.Next.type (%EmptyRange.as.Iterate.impl.Next.type.b9d) = import_ref Main//empty_range, loc11_72, loaded [symbolic = @EmptyRange.as.Iterate.impl.%EmptyRange.as.Iterate.impl.Next (constants.%EmptyRange.as.Iterate.impl.Next.682)] // CHECK:STDOUT: %Iterate.impl_witness_table = impl_witness_table (%Main.import_ref.888, %Main.import_ref.999, %Main.import_ref.868, %Main.import_ref.574), @EmptyRange.as.Iterate.impl [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @Run() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %c.patt: %pattern_type.7c7 = ref_binding_pattern c [concrete] // CHECK:STDOUT: %c.var_patt: %pattern_type.7c7 = var_pattern %c.patt [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %EmptyRange.ref: %EmptyRange.type = name_ref EmptyRange, imports.%Main.EmptyRange [concrete = constants.%EmptyRange.generic] // CHECK:STDOUT: %C.ref.loc10_31: type = name_ref C, imports.%Main.C [concrete = constants.%C] // CHECK:STDOUT: %Copy.facet: %Copy.type = facet_value %C.ref.loc10_31, (constants.%Copy.impl_witness.fab) [concrete = constants.%Copy.facet] // CHECK:STDOUT: %.loc10_32: %Copy.type = converted %C.ref.loc10_31, %Copy.facet [concrete = constants.%Copy.facet] // CHECK:STDOUT: %EmptyRange: type = class_type @EmptyRange, @EmptyRange(constants.%Copy.facet) [concrete = constants.%EmptyRange.77b] // CHECK:STDOUT: %.loc10_33: %EmptyRange.Make.type.c0f = specific_constant imports.%Main.import_ref.299, @EmptyRange(constants.%Copy.facet) [concrete = constants.%EmptyRange.Make.f9f] // CHECK:STDOUT: %Make.ref: %EmptyRange.Make.type.c0f = name_ref Make, %.loc10_33 [concrete = constants.%EmptyRange.Make.f9f] // CHECK:STDOUT: %EmptyRange.Make.specific_fn: = specific_function %Make.ref, @EmptyRange.Make(constants.%Copy.facet) [concrete = constants.%EmptyRange.Make.specific_fn] // CHECK:STDOUT: %.loc10_39.1: ref %EmptyRange.77b = temporary_storage // CHECK:STDOUT: %EmptyRange.Make.call: init %EmptyRange.77b to %.loc10_39.1 = call %EmptyRange.Make.specific_fn() // CHECK:STDOUT: %.loc10_39.2: ref %EmptyRange.77b = temporary %.loc10_39.1, %EmptyRange.Make.call // CHECK:STDOUT: %impl.elem2: %.e1f = impl_witness_access constants.%Iterate.impl_witness.96d, element2 [concrete = constants.%EmptyRange.as.Iterate.impl.NewCursor.da5] // CHECK:STDOUT: %bound_method.loc10_40.1: = bound_method %.loc10_39.2, %impl.elem2 // CHECK:STDOUT: %specific_fn.loc10_40.1: = specific_function %impl.elem2, @EmptyRange.as.Iterate.impl.NewCursor(constants.%Copy.facet) [concrete = constants.%EmptyRange.as.Iterate.impl.NewCursor.specific_fn] // CHECK:STDOUT: %bound_method.loc10_40.2: = bound_method %.loc10_39.2, %specific_fn.loc10_40.1 // CHECK:STDOUT: %.loc10_39.3: %EmptyRange.77b = acquire_value %.loc10_39.2 // CHECK:STDOUT: %EmptyRange.as.Iterate.impl.NewCursor.call: init %empty_struct_type = call %bound_method.loc10_40.2(%.loc10_39.3) // CHECK:STDOUT: %var: ref %empty_struct_type = var invalid // CHECK:STDOUT: assign %var, %EmptyRange.as.Iterate.impl.NewCursor.call // CHECK:STDOUT: br !for.next // CHECK:STDOUT: // CHECK:STDOUT: !for.next: // CHECK:STDOUT: %addr.loc10: %ptr.c28 = addr_of %var // CHECK:STDOUT: %impl.elem3: %.527 = impl_witness_access constants.%Iterate.impl_witness.96d, element3 [concrete = constants.%EmptyRange.as.Iterate.impl.Next.d08] // CHECK:STDOUT: %bound_method.loc10_40.3: = bound_method %.loc10_39.2, %impl.elem3 // CHECK:STDOUT: %specific_fn.loc10_40.2: = specific_function %impl.elem3, @EmptyRange.as.Iterate.impl.Next(constants.%Copy.facet) [concrete = constants.%EmptyRange.as.Iterate.impl.Next.specific_fn] // CHECK:STDOUT: %bound_method.loc10_40.4: = bound_method %.loc10_39.2, %specific_fn.loc10_40.2 // CHECK:STDOUT: %.loc10_40.1: ref %Optional.e59 = temporary_storage // CHECK:STDOUT: %.loc10_39.4: %EmptyRange.77b = acquire_value %.loc10_39.2 // CHECK:STDOUT: %EmptyRange.as.Iterate.impl.Next.call: init %Optional.e59 to %.loc10_40.1 = call %bound_method.loc10_40.4(%.loc10_39.4, %addr.loc10) // CHECK:STDOUT: %.loc10_40.2: ref %Optional.e59 = temporary %.loc10_40.1, %EmptyRange.as.Iterate.impl.Next.call // CHECK:STDOUT: %.loc10_40.3: %Optional.HasValue.type.26b = specific_constant imports.%Core.import_ref.f5d, @Optional(constants.%Copy.facet) [concrete = constants.%Optional.HasValue.61c] // CHECK:STDOUT: %HasValue.ref: %Optional.HasValue.type.26b = name_ref HasValue, %.loc10_40.3 [concrete = constants.%Optional.HasValue.61c] // CHECK:STDOUT: %Optional.HasValue.bound: = bound_method %.loc10_40.2, %HasValue.ref // CHECK:STDOUT: %Optional.HasValue.specific_fn: = specific_function %HasValue.ref, @Optional.HasValue(constants.%Copy.facet) [concrete = constants.%Optional.HasValue.specific_fn] // CHECK:STDOUT: %bound_method.loc10_40.5: = bound_method %.loc10_40.2, %Optional.HasValue.specific_fn // CHECK:STDOUT: %.loc10_40.4: %Optional.e59 = acquire_value %.loc10_40.2 // CHECK:STDOUT: %Optional.HasValue.call: init bool = call %bound_method.loc10_40.5(%.loc10_40.4) // CHECK:STDOUT: %.loc10_40.5: bool = value_of_initializer %Optional.HasValue.call // CHECK:STDOUT: %.loc10_40.6: bool = converted %Optional.HasValue.call, %.loc10_40.5 // CHECK:STDOUT: if %.loc10_40.6 br !for.body else br !for.done // CHECK:STDOUT: // CHECK:STDOUT: !for.body: // CHECK:STDOUT: %c.var: ref %C = var %c.var_patt // CHECK:STDOUT: %.loc10_40.7: %Optional.Get.type.7ce = specific_constant imports.%Core.import_ref.ab7, @Optional(constants.%Copy.facet) [concrete = constants.%Optional.Get.bce] // CHECK:STDOUT: %Get.ref: %Optional.Get.type.7ce = name_ref Get, %.loc10_40.7 [concrete = constants.%Optional.Get.bce] // CHECK:STDOUT: %Optional.Get.bound: = bound_method %.loc10_40.2, %Get.ref // CHECK:STDOUT: %Optional.Get.specific_fn: = specific_function %Get.ref, @Optional.Get(constants.%Copy.facet) [concrete = constants.%Optional.Get.specific_fn] // CHECK:STDOUT: %bound_method.loc10_40.6: = bound_method %.loc10_40.2, %Optional.Get.specific_fn // CHECK:STDOUT: %.loc10_8: ref %C = splice_block %c.var {} // CHECK:STDOUT: %.loc10_40.8: %Optional.e59 = acquire_value %.loc10_40.2 // CHECK:STDOUT: %Optional.Get.call: init %C to %.loc10_8 = call %bound_method.loc10_40.6(%.loc10_40.8) // CHECK:STDOUT: assign %c.var, %Optional.Get.call // CHECK:STDOUT: %C.ref.loc10_15: type = name_ref C, imports.%Main.C [concrete = constants.%C] // CHECK:STDOUT: %c: ref %C = ref_binding c, %c.var // CHECK:STDOUT: %Body.ref: %Body.type = name_ref Body, file.%Body.decl [concrete = constants.%Body] // CHECK:STDOUT: %c.ref: ref %C = name_ref c, %c // CHECK:STDOUT: %addr.loc11: %ptr.31e = addr_of %c.ref // CHECK:STDOUT: %Body.call: init %empty_tuple.type = call %Body.ref(%addr.loc11) // CHECK:STDOUT: br !for.next // CHECK:STDOUT: // CHECK:STDOUT: !for.done: // CHECK:STDOUT: %Destroy.Op.bound.loc10_8: = bound_method %c.var, constants.%Destroy.Op.651ba6.1 // CHECK:STDOUT: %Destroy.Op.call.loc10_8: init %empty_tuple.type = call %Destroy.Op.bound.loc10_8(%c.var) // CHECK:STDOUT: %Destroy.Op.bound.loc10_40.1: = bound_method %.loc10_40.2, constants.%Destroy.Op.651ba6.2 // CHECK:STDOUT: %Destroy.Op.call.loc10_40.1: init %empty_tuple.type = call %Destroy.Op.bound.loc10_40.1(%.loc10_40.2) // CHECK:STDOUT: %Destroy.Op.bound.loc10_40.2: = bound_method %var, constants.%Destroy.Op.651ba6.3 // CHECK:STDOUT: %Destroy.Op.call.loc10_40.2: init %empty_tuple.type = call %Destroy.Op.bound.loc10_40.2(%var) // CHECK:STDOUT: %Destroy.Op.bound.loc10_39: = bound_method %.loc10_39.2, constants.%Destroy.Op.651ba6.4 // CHECK:STDOUT: %Destroy.Op.call.loc10_39: init %empty_tuple.type = call %Destroy.Op.bound.loc10_39(%.loc10_39.2) // CHECK:STDOUT: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @Destroy.Op.loc10_8(%self.param: ref %C) = "no_op"; // CHECK:STDOUT: // CHECK:STDOUT: fn @Destroy.Op.loc10_40.1(%self.param: ref %Optional.e59) = "no_op"; // CHECK:STDOUT: // CHECK:STDOUT: fn @Destroy.Op.loc10_40.2(%self.param: ref %empty_struct_type) = "no_op"; // CHECK:STDOUT: // CHECK:STDOUT: fn @Destroy.Op.loc10_39(%self.param: ref %EmptyRange.77b) = "no_op"; // CHECK:STDOUT: // CHECK:STDOUT: --- tuple.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %pattern_type.831: type = pattern_type bool [concrete] // CHECK:STDOUT: %Body.type: type = fn_type @Body [concrete] // CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] // CHECK:STDOUT: %Body: %Body.type = struct_value () [concrete] // CHECK:STDOUT: %tuple.type.784: type = tuple_type (bool, bool) [concrete] // CHECK:STDOUT: %pattern_type.860: type = pattern_type %tuple.type.784 [concrete] // CHECK:STDOUT: %EmptyRange.type: type = generic_class_type @EmptyRange [concrete] // CHECK:STDOUT: %EmptyRange.generic: %EmptyRange.type = struct_value () [concrete] // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %Copy.type: type = facet_type <@Copy> [concrete] // CHECK:STDOUT: %T.56c: %Copy.type = symbolic_binding T, 0 [symbolic] // CHECK:STDOUT: %EmptyRange.Make.type.8d6: type = fn_type @EmptyRange.Make, @EmptyRange(%T.56c) [symbolic] // CHECK:STDOUT: %EmptyRange.Make.0dc: %EmptyRange.Make.type.8d6 = struct_value () [symbolic] // CHECK:STDOUT: %tuple.type.24b: type = tuple_type (type, type) [concrete] // CHECK:STDOUT: %tuple.9fa: %tuple.type.24b = tuple_value (bool, bool) [concrete] // CHECK:STDOUT: %U: %Copy.type = symbolic_binding U, 1 [symbolic] // CHECK:STDOUT: %tuple.type.as.Copy.impl.Op.type.57a: type = fn_type @tuple.type.as.Copy.impl.Op.1, @tuple.type.as.Copy.impl.da7(%T.56c, %U) [symbolic] // CHECK:STDOUT: %tuple.type.as.Copy.impl.Op.7f4: %tuple.type.as.Copy.impl.Op.type.57a = struct_value () [symbolic] // CHECK:STDOUT: %Copy.impl_witness.819: = impl_witness imports.%Copy.impl_witness_table.164 [concrete] // CHECK:STDOUT: %Copy.facet.07e: %Copy.type = facet_value bool, (%Copy.impl_witness.819) [concrete] // CHECK:STDOUT: %Copy.impl_witness.f9f: = impl_witness imports.%Copy.impl_witness_table.3c8, @tuple.type.as.Copy.impl.da7(%Copy.facet.07e, %Copy.facet.07e) [concrete] // CHECK:STDOUT: %Copy.facet.3ce: %Copy.type = facet_value %tuple.type.784, (%Copy.impl_witness.f9f) [concrete] // CHECK:STDOUT: %EmptyRange.717: type = class_type @EmptyRange, @EmptyRange(%Copy.facet.3ce) [concrete] // CHECK:STDOUT: %EmptyRange.Make.type.a76: type = fn_type @EmptyRange.Make, @EmptyRange(%Copy.facet.3ce) [concrete] // CHECK:STDOUT: %EmptyRange.Make.d35: %EmptyRange.Make.type.a76 = struct_value () [concrete] // CHECK:STDOUT: %ptr.c28: type = ptr_type %empty_struct_type [concrete] // CHECK:STDOUT: %EmptyRange.Make.specific_fn: = specific_function %EmptyRange.Make.d35, @EmptyRange.Make(%Copy.facet.3ce) [concrete] // CHECK:STDOUT: %Iterate.type: type = facet_type <@Iterate> [concrete] // CHECK:STDOUT: %Optional.Get.type.1ac: type = fn_type @Optional.Get, @Optional(%T.56c) [symbolic] // CHECK:STDOUT: %Optional.Get.748: %Optional.Get.type.1ac = struct_value () [symbolic] // CHECK:STDOUT: %Optional.HasValue.type.051: type = fn_type @Optional.HasValue, @Optional(%T.56c) [symbolic] // CHECK:STDOUT: %Optional.HasValue.6ae: %Optional.HasValue.type.051 = struct_value () [symbolic] // CHECK:STDOUT: %EmptyRange.as.Iterate.impl.Next.type.b9d: type = fn_type @EmptyRange.as.Iterate.impl.Next, @EmptyRange.as.Iterate.impl(%T.56c) [symbolic] // CHECK:STDOUT: %EmptyRange.as.Iterate.impl.Next.682: %EmptyRange.as.Iterate.impl.Next.type.b9d = struct_value () [symbolic] // CHECK:STDOUT: %EmptyRange.as.Iterate.impl.NewCursor.type.16c: type = fn_type @EmptyRange.as.Iterate.impl.NewCursor, @EmptyRange.as.Iterate.impl(%T.56c) [symbolic] // CHECK:STDOUT: %EmptyRange.as.Iterate.impl.NewCursor.cbe: %EmptyRange.as.Iterate.impl.NewCursor.type.16c = struct_value () [symbolic] // CHECK:STDOUT: %Iterate.impl_witness.04a: = impl_witness imports.%Iterate.impl_witness_table, @EmptyRange.as.Iterate.impl(%Copy.facet.3ce) [concrete] // CHECK:STDOUT: %EmptyRange.as.Iterate.impl.NewCursor.type.a15: type = fn_type @EmptyRange.as.Iterate.impl.NewCursor, @EmptyRange.as.Iterate.impl(%Copy.facet.3ce) [concrete] // CHECK:STDOUT: %EmptyRange.as.Iterate.impl.NewCursor.90c: %EmptyRange.as.Iterate.impl.NewCursor.type.a15 = struct_value () [concrete] // CHECK:STDOUT: %EmptyRange.as.Iterate.impl.Next.type.f9b: type = fn_type @EmptyRange.as.Iterate.impl.Next, @EmptyRange.as.Iterate.impl(%Copy.facet.3ce) [concrete] // CHECK:STDOUT: %EmptyRange.as.Iterate.impl.Next.4a9: %EmptyRange.as.Iterate.impl.Next.type.f9b = struct_value () [concrete] // CHECK:STDOUT: %Iterate.facet: %Iterate.type = facet_value %EmptyRange.717, (%Iterate.impl_witness.04a) [concrete] // CHECK:STDOUT: %Iterate.WithSelf.NewCursor.type.fea: type = fn_type @Iterate.WithSelf.NewCursor, @Iterate.WithSelf(%Iterate.facet) [concrete] // CHECK:STDOUT: %Iterate.WithSelf.Next.type.346: type = fn_type @Iterate.WithSelf.Next, @Iterate.WithSelf(%Iterate.facet) [concrete] // CHECK:STDOUT: %.a72: type = fn_type_with_self_type %Iterate.WithSelf.NewCursor.type.fea, %Iterate.facet [concrete] // CHECK:STDOUT: %EmptyRange.as.Iterate.impl.NewCursor.specific_fn: = specific_function %EmptyRange.as.Iterate.impl.NewCursor.90c, @EmptyRange.as.Iterate.impl.NewCursor(%Copy.facet.3ce) [concrete] // CHECK:STDOUT: %.28c: type = fn_type_with_self_type %Iterate.WithSelf.Next.type.346, %Iterate.facet [concrete] // CHECK:STDOUT: %Optional.678: type = class_type @Optional, @Optional(%Copy.facet.3ce) [concrete] // CHECK:STDOUT: %EmptyRange.as.Iterate.impl.Next.specific_fn: = specific_function %EmptyRange.as.Iterate.impl.Next.4a9, @EmptyRange.as.Iterate.impl.Next(%Copy.facet.3ce) [concrete] // CHECK:STDOUT: %Optional.HasValue.type.101: type = fn_type @Optional.HasValue, @Optional(%Copy.facet.3ce) [concrete] // CHECK:STDOUT: %Optional.HasValue.960: %Optional.HasValue.type.101 = struct_value () [concrete] // CHECK:STDOUT: %Optional.Get.type.758: type = fn_type @Optional.Get, @Optional(%Copy.facet.3ce) [concrete] // CHECK:STDOUT: %Optional.Get.2ff: %Optional.Get.type.758 = struct_value () [concrete] // CHECK:STDOUT: %Optional.HasValue.specific_fn: = specific_function %Optional.HasValue.960, @Optional.HasValue(%Copy.facet.3ce) [concrete] // CHECK:STDOUT: %Optional.Get.specific_fn: = specific_function %Optional.Get.2ff, @Optional.Get(%Copy.facet.3ce) [concrete] // CHECK:STDOUT: %Destroy.Op.type.bae255.1: type = fn_type @Destroy.Op.loc10_61.1 [concrete] // CHECK:STDOUT: %Destroy.Op.651ba6.1: %Destroy.Op.type.bae255.1 = struct_value () [concrete] // CHECK:STDOUT: %Destroy.Op.type.bae255.2: type = fn_type @Destroy.Op.loc10_61.2 [concrete] // CHECK:STDOUT: %Destroy.Op.651ba6.2: %Destroy.Op.type.bae255.2 = struct_value () [concrete] // CHECK:STDOUT: %Destroy.Op.type.bae255.3: type = fn_type @Destroy.Op.loc10_61.3 [concrete] // CHECK:STDOUT: %Destroy.Op.651ba6.3: %Destroy.Op.type.bae255.3 = struct_value () [concrete] // CHECK:STDOUT: %Destroy.Op.type.bae255.4: type = fn_type @Destroy.Op.loc10_60 [concrete] // CHECK:STDOUT: %Destroy.Op.651ba6.4: %Destroy.Op.type.bae255.4 = struct_value () [concrete] // CHECK:STDOUT: %bool.as.Copy.impl.Op.type: type = fn_type @bool.as.Copy.impl.Op [concrete] // CHECK:STDOUT: %bool.as.Copy.impl.Op: %bool.as.Copy.impl.Op.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Main.EmptyRange: %EmptyRange.type = import_ref Main//empty_range, EmptyRange, loaded [concrete = constants.%EmptyRange.generic] // CHECK:STDOUT: %Main.import_ref.299: @EmptyRange.%EmptyRange.Make.type (%EmptyRange.Make.type.8d6) = import_ref Main//empty_range, loc5_21, loaded [symbolic = @EmptyRange.%EmptyRange.Make (constants.%EmptyRange.Make.0dc)] // CHECK:STDOUT: %Core.import_ref.445: @tuple.type.as.Copy.impl.da7.%tuple.type.as.Copy.impl.Op.type (%tuple.type.as.Copy.impl.Op.type.57a) = import_ref Core//prelude/parts/copy, loc{{\d+_\d+}}, loaded [symbolic = @tuple.type.as.Copy.impl.da7.%tuple.type.as.Copy.impl.Op (constants.%tuple.type.as.Copy.impl.Op.7f4)] // CHECK:STDOUT: %Copy.impl_witness_table.3c8 = impl_witness_table (%Core.import_ref.445), @tuple.type.as.Copy.impl.da7 [concrete] // CHECK:STDOUT: %Core.import_ref.4d6: %bool.as.Copy.impl.Op.type = import_ref Core//prelude/parts/copy, loc{{\d+_\d+}}, loaded [concrete = constants.%bool.as.Copy.impl.Op] // CHECK:STDOUT: %Copy.impl_witness_table.164 = impl_witness_table (%Core.import_ref.4d6), @bool.as.Copy.impl [concrete] // CHECK:STDOUT: %Core.import_ref.f5d: @Optional.%Optional.HasValue.type (%Optional.HasValue.type.051) = import_ref Core//prelude/parts/iterate, inst{{[0-9A-F]+}} [indirect], loaded [symbolic = @Optional.%Optional.HasValue (constants.%Optional.HasValue.6ae)] // CHECK:STDOUT: %Core.import_ref.ab7: @Optional.%Optional.Get.type (%Optional.Get.type.1ac) = import_ref Core//prelude/parts/iterate, inst{{[0-9A-F]+}} [indirect], loaded [symbolic = @Optional.%Optional.Get (constants.%Optional.Get.748)] // CHECK:STDOUT: %Main.import_ref.888 = import_ref Main//empty_range, loc7_68, unloaded // CHECK:STDOUT: %Main.import_ref.999 = import_ref Main//empty_range, loc7_68, unloaded // CHECK:STDOUT: %Main.import_ref.868: @EmptyRange.as.Iterate.impl.%EmptyRange.as.Iterate.impl.NewCursor.type (%EmptyRange.as.Iterate.impl.NewCursor.type.16c) = import_ref Main//empty_range, loc8_45, loaded [symbolic = @EmptyRange.as.Iterate.impl.%EmptyRange.as.Iterate.impl.NewCursor (constants.%EmptyRange.as.Iterate.impl.NewCursor.cbe)] // CHECK:STDOUT: %Main.import_ref.574: @EmptyRange.as.Iterate.impl.%EmptyRange.as.Iterate.impl.Next.type (%EmptyRange.as.Iterate.impl.Next.type.b9d) = import_ref Main//empty_range, loc11_72, loaded [symbolic = @EmptyRange.as.Iterate.impl.%EmptyRange.as.Iterate.impl.Next (constants.%EmptyRange.as.Iterate.impl.Next.682)] // CHECK:STDOUT: %Iterate.impl_witness_table = impl_witness_table (%Main.import_ref.888, %Main.import_ref.999, %Main.import_ref.868, %Main.import_ref.574), @EmptyRange.as.Iterate.impl [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @Run() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %a.patt: %pattern_type.831 = value_binding_pattern a [concrete] // CHECK:STDOUT: %b.patt: %pattern_type.831 = value_binding_pattern b [concrete] // CHECK:STDOUT: %.loc10_25: %pattern_type.860 = tuple_pattern (%a.patt, %b.patt) [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %EmptyRange.ref: %EmptyRange.type = name_ref EmptyRange, imports.%Main.EmptyRange [concrete = constants.%EmptyRange.generic] // CHECK:STDOUT: %.loc10_42: type = type_literal bool [concrete = bool] // CHECK:STDOUT: %.loc10_48: type = type_literal bool [concrete = bool] // CHECK:STDOUT: %.loc10_52: %tuple.type.24b = tuple_literal (%.loc10_42, %.loc10_48) [concrete = constants.%tuple.9fa] // CHECK:STDOUT: %Copy.facet: %Copy.type = facet_value constants.%tuple.type.784, (constants.%Copy.impl_witness.f9f) [concrete = constants.%Copy.facet.3ce] // CHECK:STDOUT: %.loc10_53: %Copy.type = converted %.loc10_52, %Copy.facet [concrete = constants.%Copy.facet.3ce] // CHECK:STDOUT: %EmptyRange: type = class_type @EmptyRange, @EmptyRange(constants.%Copy.facet.3ce) [concrete = constants.%EmptyRange.717] // CHECK:STDOUT: %.loc10_54: %EmptyRange.Make.type.a76 = specific_constant imports.%Main.import_ref.299, @EmptyRange(constants.%Copy.facet.3ce) [concrete = constants.%EmptyRange.Make.d35] // CHECK:STDOUT: %Make.ref: %EmptyRange.Make.type.a76 = name_ref Make, %.loc10_54 [concrete = constants.%EmptyRange.Make.d35] // CHECK:STDOUT: %EmptyRange.Make.specific_fn: = specific_function %Make.ref, @EmptyRange.Make(constants.%Copy.facet.3ce) [concrete = constants.%EmptyRange.Make.specific_fn] // CHECK:STDOUT: %.loc10_60.1: ref %EmptyRange.717 = temporary_storage // CHECK:STDOUT: %EmptyRange.Make.call: init %EmptyRange.717 to %.loc10_60.1 = call %EmptyRange.Make.specific_fn() // CHECK:STDOUT: %.loc10_60.2: ref %EmptyRange.717 = temporary %.loc10_60.1, %EmptyRange.Make.call // CHECK:STDOUT: %impl.elem2: %.a72 = impl_witness_access constants.%Iterate.impl_witness.04a, element2 [concrete = constants.%EmptyRange.as.Iterate.impl.NewCursor.90c] // CHECK:STDOUT: %bound_method.loc10_61.1: = bound_method %.loc10_60.2, %impl.elem2 // CHECK:STDOUT: %specific_fn.loc10_61.1: = specific_function %impl.elem2, @EmptyRange.as.Iterate.impl.NewCursor(constants.%Copy.facet.3ce) [concrete = constants.%EmptyRange.as.Iterate.impl.NewCursor.specific_fn] // CHECK:STDOUT: %bound_method.loc10_61.2: = bound_method %.loc10_60.2, %specific_fn.loc10_61.1 // CHECK:STDOUT: %.loc10_60.3: %EmptyRange.717 = acquire_value %.loc10_60.2 // CHECK:STDOUT: %EmptyRange.as.Iterate.impl.NewCursor.call: init %empty_struct_type = call %bound_method.loc10_61.2(%.loc10_60.3) // CHECK:STDOUT: %var: ref %empty_struct_type = var invalid // CHECK:STDOUT: assign %var, %EmptyRange.as.Iterate.impl.NewCursor.call // CHECK:STDOUT: br !for.next // CHECK:STDOUT: // CHECK:STDOUT: !for.next: // CHECK:STDOUT: %addr: %ptr.c28 = addr_of %var // CHECK:STDOUT: %impl.elem3: %.28c = impl_witness_access constants.%Iterate.impl_witness.04a, element3 [concrete = constants.%EmptyRange.as.Iterate.impl.Next.4a9] // CHECK:STDOUT: %bound_method.loc10_61.3: = bound_method %.loc10_60.2, %impl.elem3 // CHECK:STDOUT: %specific_fn.loc10_61.2: = specific_function %impl.elem3, @EmptyRange.as.Iterate.impl.Next(constants.%Copy.facet.3ce) [concrete = constants.%EmptyRange.as.Iterate.impl.Next.specific_fn] // CHECK:STDOUT: %bound_method.loc10_61.4: = bound_method %.loc10_60.2, %specific_fn.loc10_61.2 // CHECK:STDOUT: %.loc10_61.1: ref %Optional.678 = temporary_storage // CHECK:STDOUT: %.loc10_60.4: %EmptyRange.717 = acquire_value %.loc10_60.2 // CHECK:STDOUT: %EmptyRange.as.Iterate.impl.Next.call: init %Optional.678 to %.loc10_61.1 = call %bound_method.loc10_61.4(%.loc10_60.4, %addr) // CHECK:STDOUT: %.loc10_61.2: ref %Optional.678 = temporary %.loc10_61.1, %EmptyRange.as.Iterate.impl.Next.call // CHECK:STDOUT: %.loc10_61.3: %Optional.HasValue.type.101 = specific_constant imports.%Core.import_ref.f5d, @Optional(constants.%Copy.facet.3ce) [concrete = constants.%Optional.HasValue.960] // CHECK:STDOUT: %HasValue.ref: %Optional.HasValue.type.101 = name_ref HasValue, %.loc10_61.3 [concrete = constants.%Optional.HasValue.960] // CHECK:STDOUT: %Optional.HasValue.bound: = bound_method %.loc10_61.2, %HasValue.ref // CHECK:STDOUT: %Optional.HasValue.specific_fn: = specific_function %HasValue.ref, @Optional.HasValue(constants.%Copy.facet.3ce) [concrete = constants.%Optional.HasValue.specific_fn] // CHECK:STDOUT: %bound_method.loc10_61.5: = bound_method %.loc10_61.2, %Optional.HasValue.specific_fn // CHECK:STDOUT: %.loc10_61.4: %Optional.678 = acquire_value %.loc10_61.2 // CHECK:STDOUT: %Optional.HasValue.call: init bool = call %bound_method.loc10_61.5(%.loc10_61.4) // CHECK:STDOUT: %.loc10_61.5: bool = value_of_initializer %Optional.HasValue.call // CHECK:STDOUT: %.loc10_61.6: bool = converted %Optional.HasValue.call, %.loc10_61.5 // CHECK:STDOUT: if %.loc10_61.6 br !for.body else br !for.done // CHECK:STDOUT: // CHECK:STDOUT: !for.body: // CHECK:STDOUT: %.loc10_61.7: %Optional.Get.type.758 = specific_constant imports.%Core.import_ref.ab7, @Optional(constants.%Copy.facet.3ce) [concrete = constants.%Optional.Get.2ff] // CHECK:STDOUT: %Get.ref: %Optional.Get.type.758 = name_ref Get, %.loc10_61.7 [concrete = constants.%Optional.Get.2ff] // CHECK:STDOUT: %Optional.Get.bound: = bound_method %.loc10_61.2, %Get.ref // CHECK:STDOUT: %Optional.Get.specific_fn: = specific_function %Get.ref, @Optional.Get(constants.%Copy.facet.3ce) [concrete = constants.%Optional.Get.specific_fn] // CHECK:STDOUT: %bound_method.loc10_61.6: = bound_method %.loc10_61.2, %Optional.Get.specific_fn // CHECK:STDOUT: %.loc10_61.8: ref %tuple.type.784 = temporary_storage // CHECK:STDOUT: %.loc10_61.9: %Optional.678 = acquire_value %.loc10_61.2 // CHECK:STDOUT: %Optional.Get.call: init %tuple.type.784 to %.loc10_61.8 = call %bound_method.loc10_61.6(%.loc10_61.9) // CHECK:STDOUT: %.loc10_61.10: ref %tuple.type.784 = temporary %.loc10_61.8, %Optional.Get.call // CHECK:STDOUT: %tuple.elem0: ref bool = tuple_access %.loc10_61.10, element0 // CHECK:STDOUT: %tuple.elem1: ref bool = tuple_access %.loc10_61.10, element1 // CHECK:STDOUT: %.loc10_12: type = type_literal bool [concrete = bool] // CHECK:STDOUT: %.loc10_61.11: bool = acquire_value %tuple.elem0 // CHECK:STDOUT: %a: bool = value_binding a, %.loc10_61.11 // CHECK:STDOUT: %.loc10_21: type = type_literal bool [concrete = bool] // CHECK:STDOUT: %.loc10_61.12: bool = acquire_value %tuple.elem1 // CHECK:STDOUT: %b: bool = value_binding b, %.loc10_61.12 // CHECK:STDOUT: %Body.ref: %Body.type = name_ref Body, file.%Body.decl [concrete = constants.%Body] // CHECK:STDOUT: %a.ref: bool = name_ref a, %a // CHECK:STDOUT: %b.ref: bool = name_ref b, %b // CHECK:STDOUT: %Body.call: init %empty_tuple.type = call %Body.ref(%a.ref, %b.ref) // CHECK:STDOUT: br !for.next // CHECK:STDOUT: // CHECK:STDOUT: !for.done: // CHECK:STDOUT: %Destroy.Op.bound.loc10_61.1: = bound_method %.loc10_61.10, constants.%Destroy.Op.651ba6.1 // CHECK:STDOUT: %Destroy.Op.call.loc10_61.1: init %empty_tuple.type = call %Destroy.Op.bound.loc10_61.1(%.loc10_61.10) // CHECK:STDOUT: %Destroy.Op.bound.loc10_61.2: = bound_method %.loc10_61.2, constants.%Destroy.Op.651ba6.2 // CHECK:STDOUT: %Destroy.Op.call.loc10_61.2: init %empty_tuple.type = call %Destroy.Op.bound.loc10_61.2(%.loc10_61.2) // CHECK:STDOUT: %Destroy.Op.bound.loc10_61.3: = bound_method %var, constants.%Destroy.Op.651ba6.3 // CHECK:STDOUT: %Destroy.Op.call.loc10_61.3: init %empty_tuple.type = call %Destroy.Op.bound.loc10_61.3(%var) // CHECK:STDOUT: %Destroy.Op.bound.loc10_60: = bound_method %.loc10_60.2, constants.%Destroy.Op.651ba6.4 // CHECK:STDOUT: %Destroy.Op.call.loc10_60: init %empty_tuple.type = call %Destroy.Op.bound.loc10_60(%.loc10_60.2) // CHECK:STDOUT: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @Destroy.Op.loc10_61.1(%self.param: ref %tuple.type.784) = "no_op"; // CHECK:STDOUT: // CHECK:STDOUT: fn @Destroy.Op.loc10_61.2(%self.param: ref %Optional.678) = "no_op"; // CHECK:STDOUT: // CHECK:STDOUT: fn @Destroy.Op.loc10_61.3(%self.param: ref %empty_struct_type) = "no_op"; // CHECK:STDOUT: // CHECK:STDOUT: fn @Destroy.Op.loc10_60(%self.param: ref %EmptyRange.717) = "no_op"; // CHECK:STDOUT: // CHECK:STDOUT: --- tuple_class.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %C: type = class_type @C [concrete] // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %pattern_type.7c7: type = pattern_type %C [concrete] // CHECK:STDOUT: %Body.type: type = fn_type @Body [concrete] // CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] // CHECK:STDOUT: %Body: %Body.type = struct_value () [concrete] // CHECK:STDOUT: %ptr.c28: type = ptr_type %empty_struct_type [concrete] // CHECK:STDOUT: %tuple.type.d23: type = tuple_type (%C, %C) [concrete] // CHECK:STDOUT: %pattern_type.c3b: type = pattern_type %tuple.type.d23 [concrete] // CHECK:STDOUT: %EmptyRange.type: type = generic_class_type @EmptyRange [concrete] // CHECK:STDOUT: %EmptyRange.generic: %EmptyRange.type = struct_value () [concrete] // CHECK:STDOUT: %Copy.type: type = facet_type <@Copy> [concrete] // CHECK:STDOUT: %T.56c: %Copy.type = symbolic_binding T, 0 [symbolic] // CHECK:STDOUT: %EmptyRange.Make.type.8d6: type = fn_type @EmptyRange.Make, @EmptyRange(%T.56c) [symbolic] // CHECK:STDOUT: %EmptyRange.Make.0dc: %EmptyRange.Make.type.8d6 = struct_value () [symbolic] // CHECK:STDOUT: %tuple.type.24b: type = tuple_type (type, type) [concrete] // CHECK:STDOUT: %tuple.6f4: %tuple.type.24b = tuple_value (%C, %C) [concrete] // CHECK:STDOUT: %U: %Copy.type = symbolic_binding U, 1 [symbolic] // CHECK:STDOUT: %tuple.type.as.Copy.impl.Op.type.57a: type = fn_type @tuple.type.as.Copy.impl.Op.1, @tuple.type.as.Copy.impl.da7(%T.56c, %U) [symbolic] // CHECK:STDOUT: %tuple.type.as.Copy.impl.Op.7f4: %tuple.type.as.Copy.impl.Op.type.57a = struct_value () [symbolic] // CHECK:STDOUT: %Copy.impl_witness.fab: = impl_witness imports.%Copy.impl_witness_table.c52 [concrete] // CHECK:STDOUT: %Copy.facet.54f: %Copy.type = facet_value %C, (%Copy.impl_witness.fab) [concrete] // CHECK:STDOUT: %Copy.impl_witness.9e2: = impl_witness imports.%Copy.impl_witness_table.3c8, @tuple.type.as.Copy.impl.da7(%Copy.facet.54f, %Copy.facet.54f) [concrete] // CHECK:STDOUT: %Copy.facet.ad0: %Copy.type = facet_value %tuple.type.d23, (%Copy.impl_witness.9e2) [concrete] // CHECK:STDOUT: %EmptyRange.849: type = class_type @EmptyRange, @EmptyRange(%Copy.facet.ad0) [concrete] // CHECK:STDOUT: %EmptyRange.Make.type.7da: type = fn_type @EmptyRange.Make, @EmptyRange(%Copy.facet.ad0) [concrete] // CHECK:STDOUT: %EmptyRange.Make.38a: %EmptyRange.Make.type.7da = struct_value () [concrete] // CHECK:STDOUT: %EmptyRange.Make.specific_fn: = specific_function %EmptyRange.Make.38a, @EmptyRange.Make(%Copy.facet.ad0) [concrete] // CHECK:STDOUT: %Iterate.type: type = facet_type <@Iterate> [concrete] // CHECK:STDOUT: %Optional.Get.type.1ac: type = fn_type @Optional.Get, @Optional(%T.56c) [symbolic] // CHECK:STDOUT: %Optional.Get.748: %Optional.Get.type.1ac = struct_value () [symbolic] // CHECK:STDOUT: %Optional.HasValue.type.051: type = fn_type @Optional.HasValue, @Optional(%T.56c) [symbolic] // CHECK:STDOUT: %Optional.HasValue.6ae: %Optional.HasValue.type.051 = struct_value () [symbolic] // CHECK:STDOUT: %EmptyRange.as.Iterate.impl.Next.type.b9d: type = fn_type @EmptyRange.as.Iterate.impl.Next, @EmptyRange.as.Iterate.impl(%T.56c) [symbolic] // CHECK:STDOUT: %EmptyRange.as.Iterate.impl.Next.682: %EmptyRange.as.Iterate.impl.Next.type.b9d = struct_value () [symbolic] // CHECK:STDOUT: %EmptyRange.as.Iterate.impl.NewCursor.type.16c: type = fn_type @EmptyRange.as.Iterate.impl.NewCursor, @EmptyRange.as.Iterate.impl(%T.56c) [symbolic] // CHECK:STDOUT: %EmptyRange.as.Iterate.impl.NewCursor.cbe: %EmptyRange.as.Iterate.impl.NewCursor.type.16c = struct_value () [symbolic] // CHECK:STDOUT: %Iterate.impl_witness.a9d: = impl_witness imports.%Iterate.impl_witness_table, @EmptyRange.as.Iterate.impl(%Copy.facet.ad0) [concrete] // CHECK:STDOUT: %EmptyRange.as.Iterate.impl.NewCursor.type.a4b: type = fn_type @EmptyRange.as.Iterate.impl.NewCursor, @EmptyRange.as.Iterate.impl(%Copy.facet.ad0) [concrete] // CHECK:STDOUT: %EmptyRange.as.Iterate.impl.NewCursor.94e: %EmptyRange.as.Iterate.impl.NewCursor.type.a4b = struct_value () [concrete] // CHECK:STDOUT: %EmptyRange.as.Iterate.impl.Next.type.24c: type = fn_type @EmptyRange.as.Iterate.impl.Next, @EmptyRange.as.Iterate.impl(%Copy.facet.ad0) [concrete] // CHECK:STDOUT: %EmptyRange.as.Iterate.impl.Next.77c: %EmptyRange.as.Iterate.impl.Next.type.24c = struct_value () [concrete] // CHECK:STDOUT: %Iterate.facet: %Iterate.type = facet_value %EmptyRange.849, (%Iterate.impl_witness.a9d) [concrete] // CHECK:STDOUT: %Iterate.WithSelf.NewCursor.type.43a: type = fn_type @Iterate.WithSelf.NewCursor, @Iterate.WithSelf(%Iterate.facet) [concrete] // CHECK:STDOUT: %Iterate.WithSelf.Next.type.cd1: type = fn_type @Iterate.WithSelf.Next, @Iterate.WithSelf(%Iterate.facet) [concrete] // CHECK:STDOUT: %.722: type = fn_type_with_self_type %Iterate.WithSelf.NewCursor.type.43a, %Iterate.facet [concrete] // CHECK:STDOUT: %EmptyRange.as.Iterate.impl.NewCursor.specific_fn: = specific_function %EmptyRange.as.Iterate.impl.NewCursor.94e, @EmptyRange.as.Iterate.impl.NewCursor(%Copy.facet.ad0) [concrete] // CHECK:STDOUT: %.44b: type = fn_type_with_self_type %Iterate.WithSelf.Next.type.cd1, %Iterate.facet [concrete] // CHECK:STDOUT: %Optional.2f4: type = class_type @Optional, @Optional(%Copy.facet.ad0) [concrete] // CHECK:STDOUT: %EmptyRange.as.Iterate.impl.Next.specific_fn: = specific_function %EmptyRange.as.Iterate.impl.Next.77c, @EmptyRange.as.Iterate.impl.Next(%Copy.facet.ad0) [concrete] // CHECK:STDOUT: %Optional.HasValue.type.6de: type = fn_type @Optional.HasValue, @Optional(%Copy.facet.ad0) [concrete] // CHECK:STDOUT: %Optional.HasValue.de9: %Optional.HasValue.type.6de = struct_value () [concrete] // CHECK:STDOUT: %Optional.Get.type.925: type = fn_type @Optional.Get, @Optional(%Copy.facet.ad0) [concrete] // CHECK:STDOUT: %Optional.Get.a9e: %Optional.Get.type.925 = struct_value () [concrete] // CHECK:STDOUT: %Optional.HasValue.specific_fn: = specific_function %Optional.HasValue.de9, @Optional.HasValue(%Copy.facet.ad0) [concrete] // CHECK:STDOUT: %Optional.Get.specific_fn: = specific_function %Optional.Get.a9e, @Optional.Get(%Copy.facet.ad0) [concrete] // CHECK:STDOUT: %Destroy.Op.type.bae255.1: type = fn_type @Destroy.Op.loc10_49.1 [concrete] // CHECK:STDOUT: %Destroy.Op.651ba6.1: %Destroy.Op.type.bae255.1 = struct_value () [concrete] // CHECK:STDOUT: %Destroy.Op.type.bae255.2: type = fn_type @Destroy.Op.loc10_49.2 [concrete] // CHECK:STDOUT: %Destroy.Op.651ba6.2: %Destroy.Op.type.bae255.2 = struct_value () [concrete] // CHECK:STDOUT: %Destroy.Op.type.bae255.3: type = fn_type @Destroy.Op.loc10_49.3 [concrete] // CHECK:STDOUT: %Destroy.Op.651ba6.3: %Destroy.Op.type.bae255.3 = struct_value () [concrete] // CHECK:STDOUT: %Destroy.Op.type.bae255.4: type = fn_type @Destroy.Op.loc10_48 [concrete] // CHECK:STDOUT: %Destroy.Op.651ba6.4: %Destroy.Op.type.bae255.4 = struct_value () [concrete] // CHECK:STDOUT: %C.as.Copy.impl.Op.type: type = fn_type @C.as.Copy.impl.Op [concrete] // CHECK:STDOUT: %C.as.Copy.impl.Op: %C.as.Copy.impl.Op.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Main.EmptyRange: %EmptyRange.type = import_ref Main//empty_range, EmptyRange, loaded [concrete = constants.%EmptyRange.generic] // CHECK:STDOUT: %Main.C: type = import_ref Main//empty_range, C, loaded [concrete = constants.%C] // CHECK:STDOUT: %Main.import_ref.299: @EmptyRange.%EmptyRange.Make.type (%EmptyRange.Make.type.8d6) = import_ref Main//empty_range, loc5_21, loaded [symbolic = @EmptyRange.%EmptyRange.Make (constants.%EmptyRange.Make.0dc)] // CHECK:STDOUT: %Core.import_ref.445: @tuple.type.as.Copy.impl.da7.%tuple.type.as.Copy.impl.Op.type (%tuple.type.as.Copy.impl.Op.type.57a) = import_ref Core//prelude/parts/copy, loc{{\d+_\d+}}, loaded [symbolic = @tuple.type.as.Copy.impl.da7.%tuple.type.as.Copy.impl.Op (constants.%tuple.type.as.Copy.impl.Op.7f4)] // CHECK:STDOUT: %Copy.impl_witness_table.3c8 = impl_witness_table (%Core.import_ref.445), @tuple.type.as.Copy.impl.da7 [concrete] // CHECK:STDOUT: %Main.import_ref.cb5: %C.as.Copy.impl.Op.type = import_ref Main//empty_range, loc19_40, loaded [concrete = constants.%C.as.Copy.impl.Op] // CHECK:STDOUT: %Copy.impl_witness_table.c52 = impl_witness_table (%Main.import_ref.cb5), @C.as.Copy.impl [concrete] // CHECK:STDOUT: %Core.import_ref.f5d: @Optional.%Optional.HasValue.type (%Optional.HasValue.type.051) = import_ref Core//prelude/parts/iterate, inst{{[0-9A-F]+}} [indirect], loaded [symbolic = @Optional.%Optional.HasValue (constants.%Optional.HasValue.6ae)] // CHECK:STDOUT: %Core.import_ref.ab7: @Optional.%Optional.Get.type (%Optional.Get.type.1ac) = import_ref Core//prelude/parts/iterate, inst{{[0-9A-F]+}} [indirect], loaded [symbolic = @Optional.%Optional.Get (constants.%Optional.Get.748)] // CHECK:STDOUT: %Main.import_ref.888 = import_ref Main//empty_range, loc7_68, unloaded // CHECK:STDOUT: %Main.import_ref.999 = import_ref Main//empty_range, loc7_68, unloaded // CHECK:STDOUT: %Main.import_ref.868: @EmptyRange.as.Iterate.impl.%EmptyRange.as.Iterate.impl.NewCursor.type (%EmptyRange.as.Iterate.impl.NewCursor.type.16c) = import_ref Main//empty_range, loc8_45, loaded [symbolic = @EmptyRange.as.Iterate.impl.%EmptyRange.as.Iterate.impl.NewCursor (constants.%EmptyRange.as.Iterate.impl.NewCursor.cbe)] // CHECK:STDOUT: %Main.import_ref.574: @EmptyRange.as.Iterate.impl.%EmptyRange.as.Iterate.impl.Next.type (%EmptyRange.as.Iterate.impl.Next.type.b9d) = import_ref Main//empty_range, loc11_72, loaded [symbolic = @EmptyRange.as.Iterate.impl.%EmptyRange.as.Iterate.impl.Next (constants.%EmptyRange.as.Iterate.impl.Next.682)] // CHECK:STDOUT: %Iterate.impl_witness_table = impl_witness_table (%Main.import_ref.888, %Main.import_ref.999, %Main.import_ref.868, %Main.import_ref.574), @EmptyRange.as.Iterate.impl [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @Run() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %a.patt: %pattern_type.7c7 = value_binding_pattern a [concrete] // CHECK:STDOUT: %b.patt: %pattern_type.7c7 = value_binding_pattern b [concrete] // CHECK:STDOUT: %.loc10_19: %pattern_type.c3b = tuple_pattern (%a.patt, %b.patt) [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %EmptyRange.ref: %EmptyRange.type = name_ref EmptyRange, imports.%Main.EmptyRange [concrete = constants.%EmptyRange.generic] // CHECK:STDOUT: %C.ref.loc10_36: type = name_ref C, imports.%Main.C [concrete = constants.%C] // CHECK:STDOUT: %C.ref.loc10_39: type = name_ref C, imports.%Main.C [concrete = constants.%C] // CHECK:STDOUT: %.loc10_40: %tuple.type.24b = tuple_literal (%C.ref.loc10_36, %C.ref.loc10_39) [concrete = constants.%tuple.6f4] // CHECK:STDOUT: %Copy.facet: %Copy.type = facet_value constants.%tuple.type.d23, (constants.%Copy.impl_witness.9e2) [concrete = constants.%Copy.facet.ad0] // CHECK:STDOUT: %.loc10_41: %Copy.type = converted %.loc10_40, %Copy.facet [concrete = constants.%Copy.facet.ad0] // CHECK:STDOUT: %EmptyRange: type = class_type @EmptyRange, @EmptyRange(constants.%Copy.facet.ad0) [concrete = constants.%EmptyRange.849] // CHECK:STDOUT: %.loc10_42: %EmptyRange.Make.type.7da = specific_constant imports.%Main.import_ref.299, @EmptyRange(constants.%Copy.facet.ad0) [concrete = constants.%EmptyRange.Make.38a] // CHECK:STDOUT: %Make.ref: %EmptyRange.Make.type.7da = name_ref Make, %.loc10_42 [concrete = constants.%EmptyRange.Make.38a] // CHECK:STDOUT: %EmptyRange.Make.specific_fn: = specific_function %Make.ref, @EmptyRange.Make(constants.%Copy.facet.ad0) [concrete = constants.%EmptyRange.Make.specific_fn] // CHECK:STDOUT: %.loc10_48.1: ref %EmptyRange.849 = temporary_storage // CHECK:STDOUT: %EmptyRange.Make.call: init %EmptyRange.849 to %.loc10_48.1 = call %EmptyRange.Make.specific_fn() // CHECK:STDOUT: %.loc10_48.2: ref %EmptyRange.849 = temporary %.loc10_48.1, %EmptyRange.Make.call // CHECK:STDOUT: %impl.elem2: %.722 = impl_witness_access constants.%Iterate.impl_witness.a9d, element2 [concrete = constants.%EmptyRange.as.Iterate.impl.NewCursor.94e] // CHECK:STDOUT: %bound_method.loc10_49.1: = bound_method %.loc10_48.2, %impl.elem2 // CHECK:STDOUT: %specific_fn.loc10_49.1: = specific_function %impl.elem2, @EmptyRange.as.Iterate.impl.NewCursor(constants.%Copy.facet.ad0) [concrete = constants.%EmptyRange.as.Iterate.impl.NewCursor.specific_fn] // CHECK:STDOUT: %bound_method.loc10_49.2: = bound_method %.loc10_48.2, %specific_fn.loc10_49.1 // CHECK:STDOUT: %.loc10_48.3: %EmptyRange.849 = acquire_value %.loc10_48.2 // CHECK:STDOUT: %EmptyRange.as.Iterate.impl.NewCursor.call: init %empty_struct_type = call %bound_method.loc10_49.2(%.loc10_48.3) // CHECK:STDOUT: %var: ref %empty_struct_type = var invalid // CHECK:STDOUT: assign %var, %EmptyRange.as.Iterate.impl.NewCursor.call // CHECK:STDOUT: br !for.next // CHECK:STDOUT: // CHECK:STDOUT: !for.next: // CHECK:STDOUT: %addr: %ptr.c28 = addr_of %var // CHECK:STDOUT: %impl.elem3: %.44b = impl_witness_access constants.%Iterate.impl_witness.a9d, element3 [concrete = constants.%EmptyRange.as.Iterate.impl.Next.77c] // CHECK:STDOUT: %bound_method.loc10_49.3: = bound_method %.loc10_48.2, %impl.elem3 // CHECK:STDOUT: %specific_fn.loc10_49.2: = specific_function %impl.elem3, @EmptyRange.as.Iterate.impl.Next(constants.%Copy.facet.ad0) [concrete = constants.%EmptyRange.as.Iterate.impl.Next.specific_fn] // CHECK:STDOUT: %bound_method.loc10_49.4: = bound_method %.loc10_48.2, %specific_fn.loc10_49.2 // CHECK:STDOUT: %.loc10_49.1: ref %Optional.2f4 = temporary_storage // CHECK:STDOUT: %.loc10_48.4: %EmptyRange.849 = acquire_value %.loc10_48.2 // CHECK:STDOUT: %EmptyRange.as.Iterate.impl.Next.call: init %Optional.2f4 to %.loc10_49.1 = call %bound_method.loc10_49.4(%.loc10_48.4, %addr) // CHECK:STDOUT: %.loc10_49.2: ref %Optional.2f4 = temporary %.loc10_49.1, %EmptyRange.as.Iterate.impl.Next.call // CHECK:STDOUT: %.loc10_49.3: %Optional.HasValue.type.6de = specific_constant imports.%Core.import_ref.f5d, @Optional(constants.%Copy.facet.ad0) [concrete = constants.%Optional.HasValue.de9] // CHECK:STDOUT: %HasValue.ref: %Optional.HasValue.type.6de = name_ref HasValue, %.loc10_49.3 [concrete = constants.%Optional.HasValue.de9] // CHECK:STDOUT: %Optional.HasValue.bound: = bound_method %.loc10_49.2, %HasValue.ref // CHECK:STDOUT: %Optional.HasValue.specific_fn: = specific_function %HasValue.ref, @Optional.HasValue(constants.%Copy.facet.ad0) [concrete = constants.%Optional.HasValue.specific_fn] // CHECK:STDOUT: %bound_method.loc10_49.5: = bound_method %.loc10_49.2, %Optional.HasValue.specific_fn // CHECK:STDOUT: %.loc10_49.4: %Optional.2f4 = acquire_value %.loc10_49.2 // CHECK:STDOUT: %Optional.HasValue.call: init bool = call %bound_method.loc10_49.5(%.loc10_49.4) // CHECK:STDOUT: %.loc10_49.5: bool = value_of_initializer %Optional.HasValue.call // CHECK:STDOUT: %.loc10_49.6: bool = converted %Optional.HasValue.call, %.loc10_49.5 // CHECK:STDOUT: if %.loc10_49.6 br !for.body else br !for.done // CHECK:STDOUT: // CHECK:STDOUT: !for.body: // CHECK:STDOUT: %.loc10_49.7: %Optional.Get.type.925 = specific_constant imports.%Core.import_ref.ab7, @Optional(constants.%Copy.facet.ad0) [concrete = constants.%Optional.Get.a9e] // CHECK:STDOUT: %Get.ref: %Optional.Get.type.925 = name_ref Get, %.loc10_49.7 [concrete = constants.%Optional.Get.a9e] // CHECK:STDOUT: %Optional.Get.bound: = bound_method %.loc10_49.2, %Get.ref // CHECK:STDOUT: %Optional.Get.specific_fn: = specific_function %Get.ref, @Optional.Get(constants.%Copy.facet.ad0) [concrete = constants.%Optional.Get.specific_fn] // CHECK:STDOUT: %bound_method.loc10_49.6: = bound_method %.loc10_49.2, %Optional.Get.specific_fn // CHECK:STDOUT: %.loc10_49.8: ref %tuple.type.d23 = temporary_storage // CHECK:STDOUT: %.loc10_49.9: %Optional.2f4 = acquire_value %.loc10_49.2 // CHECK:STDOUT: %Optional.Get.call: init %tuple.type.d23 to %.loc10_49.8 = call %bound_method.loc10_49.6(%.loc10_49.9) // CHECK:STDOUT: %.loc10_49.10: ref %tuple.type.d23 = temporary %.loc10_49.8, %Optional.Get.call // CHECK:STDOUT: %tuple.elem0: ref %C = tuple_access %.loc10_49.10, element0 // CHECK:STDOUT: %tuple.elem1: ref %C = tuple_access %.loc10_49.10, element1 // CHECK:STDOUT: %C.ref.loc10_12: type = name_ref C, imports.%Main.C [concrete = constants.%C] // CHECK:STDOUT: %.loc10_49.11: %C = acquire_value %tuple.elem0 // CHECK:STDOUT: %a: %C = value_binding a, %.loc10_49.11 // CHECK:STDOUT: %C.ref.loc10_18: type = name_ref C, imports.%Main.C [concrete = constants.%C] // CHECK:STDOUT: %.loc10_49.12: %C = acquire_value %tuple.elem1 // CHECK:STDOUT: %b: %C = value_binding b, %.loc10_49.12 // CHECK:STDOUT: %Body.ref: %Body.type = name_ref Body, file.%Body.decl [concrete = constants.%Body] // CHECK:STDOUT: %a.ref: %C = name_ref a, %a // CHECK:STDOUT: %b.ref: %C = name_ref b, %b // CHECK:STDOUT: %Body.call: init %empty_tuple.type = call %Body.ref(%a.ref, %b.ref) // CHECK:STDOUT: br !for.next // CHECK:STDOUT: // CHECK:STDOUT: !for.done: // CHECK:STDOUT: %Destroy.Op.bound.loc10_49.1: = bound_method %.loc10_49.10, constants.%Destroy.Op.651ba6.1 // CHECK:STDOUT: %Destroy.Op.call.loc10_49.1: init %empty_tuple.type = call %Destroy.Op.bound.loc10_49.1(%.loc10_49.10) // CHECK:STDOUT: %Destroy.Op.bound.loc10_49.2: = bound_method %.loc10_49.2, constants.%Destroy.Op.651ba6.2 // CHECK:STDOUT: %Destroy.Op.call.loc10_49.2: init %empty_tuple.type = call %Destroy.Op.bound.loc10_49.2(%.loc10_49.2) // CHECK:STDOUT: %Destroy.Op.bound.loc10_49.3: = bound_method %var, constants.%Destroy.Op.651ba6.3 // CHECK:STDOUT: %Destroy.Op.call.loc10_49.3: init %empty_tuple.type = call %Destroy.Op.bound.loc10_49.3(%var) // CHECK:STDOUT: %Destroy.Op.bound.loc10_48: = bound_method %.loc10_48.2, constants.%Destroy.Op.651ba6.4 // CHECK:STDOUT: %Destroy.Op.call.loc10_48: init %empty_tuple.type = call %Destroy.Op.bound.loc10_48(%.loc10_48.2) // CHECK:STDOUT: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @Destroy.Op.loc10_49.1(%self.param: ref %tuple.type.d23) = "no_op"; // CHECK:STDOUT: // CHECK:STDOUT: fn @Destroy.Op.loc10_49.2(%self.param: ref %Optional.2f4) = "no_op"; // CHECK:STDOUT: // CHECK:STDOUT: fn @Destroy.Op.loc10_49.3(%self.param: ref %empty_struct_type) = "no_op"; // CHECK:STDOUT: // CHECK:STDOUT: fn @Destroy.Op.loc10_48(%self.param: ref %EmptyRange.849) = "no_op"; // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/function/builtin/adapted_type.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/none.carbon // TODO: Add ranges and switch to "--dump-sem-ir-ranges=only". // EXTRA-ARGS: --dump-sem-ir-ranges=if-present // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/function/builtin/adapted_type.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/function/builtin/adapted_type.carbon // --- adapt.carbon library "[[@TEST_NAME]]"; fn IntLiteral() -> type = "int_literal.make_type"; class MyIntLiteral { adapt IntLiteral(); } fn Int(N: MyIntLiteral) -> type = "int.make_type_signed"; class MyInt32 { adapt Int(32 as MyIntLiteral); fn Make(a: MyIntLiteral) -> MyInt32; } fn MyInt32.Make(a: MyIntLiteral) -> MyInt32 = "int.convert_checked"; fn MyAdd(a: MyInt32, b: MyInt32) -> MyInt32 = "int.sadd"; var v: MyInt32 = MyAdd(MyInt32.Make(1 as MyIntLiteral), MyInt32.Make(2 as MyIntLiteral)); // --- fail_bad_adapt.carbon library "[[@TEST_NAME]]"; class MyIntLiteral { adapt {}; } // CHECK:STDERR: fail_bad_adapt.carbon:[[@LINE+4]]:1: error: invalid signature for builtin function "int.make_type_signed" [InvalidBuiltinSignature] // CHECK:STDERR: fn Int(N: MyIntLiteral) -> type = "int.make_type_signed"; // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: fn Int(N: MyIntLiteral) -> type = "int.make_type_signed"; // CHECK:STDOUT: --- adapt.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %.805: Core.Form = init_form type [concrete] // CHECK:STDOUT: %pattern_type.98f: type = pattern_type type [concrete] // CHECK:STDOUT: %IntLiteral.type: type = fn_type @IntLiteral [concrete] // CHECK:STDOUT: %IntLiteral: %IntLiteral.type = struct_value () [concrete] // CHECK:STDOUT: %MyIntLiteral: type = class_type @MyIntLiteral [concrete] // CHECK:STDOUT: %complete_type.972: = complete_type_witness Core.IntLiteral [concrete] // CHECK:STDOUT: %pattern_type.d59: type = pattern_type %MyIntLiteral [concrete] // CHECK:STDOUT: %Int.type: type = fn_type @Int [concrete] // CHECK:STDOUT: %Int: %Int.type = struct_value () [concrete] // CHECK:STDOUT: %MyInt32: type = class_type @MyInt32 [concrete] // CHECK:STDOUT: %int_32.be0: Core.IntLiteral = int_value 32 [concrete] // CHECK:STDOUT: %int_32.2f2: %MyIntLiteral = int_value 32 [concrete] // CHECK:STDOUT: %i32.builtin: type = int_type signed, %int_32.2f2 [concrete] // CHECK:STDOUT: %.c88: Core.Form = init_form %MyInt32 [concrete] // CHECK:STDOUT: %pattern_type.4d1: type = pattern_type %MyInt32 [concrete] // CHECK:STDOUT: %MyInt32.Make.type: type = fn_type @MyInt32.Make [concrete] // CHECK:STDOUT: %MyInt32.Make: %MyInt32.Make.type = struct_value () [concrete] // CHECK:STDOUT: %complete_type.833: = complete_type_witness %i32.builtin [concrete] // CHECK:STDOUT: %MyAdd.type: type = fn_type @MyAdd [concrete] // CHECK:STDOUT: %MyAdd: %MyAdd.type = struct_value () [concrete] // CHECK:STDOUT: %int_1.5b8: Core.IntLiteral = int_value 1 [concrete] // CHECK:STDOUT: %int_1.ed9: %MyIntLiteral = int_value 1 [concrete] // CHECK:STDOUT: %int_1.6de: %MyInt32 = int_value 1 [concrete] // CHECK:STDOUT: %int_2.ecc: Core.IntLiteral = int_value 2 [concrete] // CHECK:STDOUT: %int_2.7bb: %MyIntLiteral = int_value 2 [concrete] // CHECK:STDOUT: %int_2.035: %MyInt32 = int_value 2 [concrete] // CHECK:STDOUT: %int_3: %MyInt32 = int_value 3 [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .IntLiteral = %IntLiteral.decl // CHECK:STDOUT: .MyIntLiteral = %MyIntLiteral.decl // CHECK:STDOUT: .Int = %Int.decl // CHECK:STDOUT: .MyInt32 = %MyInt32.decl // CHECK:STDOUT: .MyAdd = %MyAdd.decl // CHECK:STDOUT: .v = %v // CHECK:STDOUT: } // CHECK:STDOUT: %IntLiteral.decl: %IntLiteral.type = fn_decl @IntLiteral [concrete = constants.%IntLiteral] { // CHECK:STDOUT: %return.patt: %pattern_type.98f = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: %pattern_type.98f = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %.loc4_20.1: type = type_literal type [concrete = type] // CHECK:STDOUT: %.loc4_20.2: Core.Form = init_form %.loc4_20.1 [concrete = constants.%.805] // CHECK:STDOUT: %return.param: ref type = out_param call_param0 // CHECK:STDOUT: %return: ref type = return_slot %return.param // CHECK:STDOUT: } // CHECK:STDOUT: %MyIntLiteral.decl: type = class_decl @MyIntLiteral [concrete = constants.%MyIntLiteral] {} {} // CHECK:STDOUT: %Int.decl: %Int.type = fn_decl @Int [concrete = constants.%Int] { // CHECK:STDOUT: %N.patt: %pattern_type.d59 = value_binding_pattern N [concrete] // CHECK:STDOUT: %N.param_patt: %pattern_type.d59 = value_param_pattern %N.patt [concrete] // CHECK:STDOUT: %return.patt: %pattern_type.98f = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: %pattern_type.98f = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %.loc10_28.1: type = type_literal type [concrete = type] // CHECK:STDOUT: %.loc10_28.2: Core.Form = init_form %.loc10_28.1 [concrete = constants.%.805] // CHECK:STDOUT: %N.param: %MyIntLiteral = value_param call_param0 // CHECK:STDOUT: %MyIntLiteral.ref: type = name_ref MyIntLiteral, file.%MyIntLiteral.decl [concrete = constants.%MyIntLiteral] // CHECK:STDOUT: %N: %MyIntLiteral = value_binding N, %N.param // CHECK:STDOUT: %return.param: ref type = out_param call_param1 // CHECK:STDOUT: %return: ref type = return_slot %return.param // CHECK:STDOUT: } // CHECK:STDOUT: %MyInt32.decl: type = class_decl @MyInt32 [concrete = constants.%MyInt32] {} {} // CHECK:STDOUT: %MyInt32.Make.decl: %MyInt32.Make.type = fn_decl @MyInt32.Make [concrete = constants.%MyInt32.Make] { // CHECK:STDOUT: %a.patt: %pattern_type.d59 = value_binding_pattern a [concrete] // CHECK:STDOUT: %a.param_patt: %pattern_type.d59 = value_param_pattern %a.patt [concrete] // CHECK:STDOUT: %return.patt: %pattern_type.4d1 = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: %pattern_type.4d1 = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %MyInt32.ref.loc18: type = name_ref MyInt32, file.%MyInt32.decl [concrete = constants.%MyInt32] // CHECK:STDOUT: %.loc18: Core.Form = init_form %MyInt32.ref.loc18 [concrete = constants.%.c88] // CHECK:STDOUT: %a.param.loc18: %MyIntLiteral = value_param call_param0 // CHECK:STDOUT: %MyIntLiteral.ref.loc18: type = name_ref MyIntLiteral, file.%MyIntLiteral.decl [concrete = constants.%MyIntLiteral] // CHECK:STDOUT: %a.loc18: %MyIntLiteral = value_binding a, %a.param.loc18 // CHECK:STDOUT: %return.param.loc18: ref %MyInt32 = out_param call_param1 // CHECK:STDOUT: %return.loc18: ref %MyInt32 = return_slot %return.param.loc18 // CHECK:STDOUT: } // CHECK:STDOUT: %MyAdd.decl: %MyAdd.type = fn_decl @MyAdd [concrete = constants.%MyAdd] { // CHECK:STDOUT: %a.patt: %pattern_type.4d1 = value_binding_pattern a [concrete] // CHECK:STDOUT: %a.param_patt: %pattern_type.4d1 = value_param_pattern %a.patt [concrete] // CHECK:STDOUT: %b.patt: %pattern_type.4d1 = value_binding_pattern b [concrete] // CHECK:STDOUT: %b.param_patt: %pattern_type.4d1 = value_param_pattern %b.patt [concrete] // CHECK:STDOUT: %return.patt: %pattern_type.4d1 = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: %pattern_type.4d1 = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %MyInt32.ref.loc20_37: type = name_ref MyInt32, file.%MyInt32.decl [concrete = constants.%MyInt32] // CHECK:STDOUT: %.loc20: Core.Form = init_form %MyInt32.ref.loc20_37 [concrete = constants.%.c88] // CHECK:STDOUT: %a.param: %MyInt32 = value_param call_param0 // CHECK:STDOUT: %MyInt32.ref.loc20_13: type = name_ref MyInt32, file.%MyInt32.decl [concrete = constants.%MyInt32] // CHECK:STDOUT: %a: %MyInt32 = value_binding a, %a.param // CHECK:STDOUT: %b.param: %MyInt32 = value_param call_param1 // CHECK:STDOUT: %MyInt32.ref.loc20_25: type = name_ref MyInt32, file.%MyInt32.decl [concrete = constants.%MyInt32] // CHECK:STDOUT: %b: %MyInt32 = value_binding b, %b.param // CHECK:STDOUT: %return.param: ref %MyInt32 = out_param call_param2 // CHECK:STDOUT: %return: ref %MyInt32 = return_slot %return.param // CHECK:STDOUT: } // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %v.patt: %pattern_type.4d1 = ref_binding_pattern v [concrete] // CHECK:STDOUT: %v.var_patt: %pattern_type.4d1 = var_pattern %v.patt [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %v.var: ref %MyInt32 = var %v.var_patt [concrete] // CHECK:STDOUT: %MyInt32.ref: type = name_ref MyInt32, %MyInt32.decl [concrete = constants.%MyInt32] // CHECK:STDOUT: %v: ref %MyInt32 = ref_binding v, %v.var [concrete = %v.var] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @MyIntLiteral { // CHECK:STDOUT: %IntLiteral.ref: %IntLiteral.type = name_ref IntLiteral, file.%IntLiteral.decl [concrete = constants.%IntLiteral] // CHECK:STDOUT: %IntLiteral.call: init type = call %IntLiteral.ref() [concrete = Core.IntLiteral] // CHECK:STDOUT: %.loc7_21.1: type = value_of_initializer %IntLiteral.call [concrete = Core.IntLiteral] // CHECK:STDOUT: %.loc7_21.2: type = converted %IntLiteral.call, %.loc7_21.1 [concrete = Core.IntLiteral] // CHECK:STDOUT: adapt_decl %.loc7_21.2 [concrete] // CHECK:STDOUT: %complete_type: = complete_type_witness Core.IntLiteral [concrete = constants.%complete_type.972] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%MyIntLiteral // CHECK:STDOUT: .IntLiteral = // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @MyInt32 { // CHECK:STDOUT: %Int.ref: %Int.type = name_ref Int, file.%Int.decl [concrete = constants.%Int] // CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [concrete = constants.%int_32.be0] // CHECK:STDOUT: %MyIntLiteral.ref: type = name_ref MyIntLiteral, file.%MyIntLiteral.decl [concrete = constants.%MyIntLiteral] // CHECK:STDOUT: %.loc13_16.1: %MyIntLiteral = as_compatible %int_32 [concrete = constants.%int_32.2f2] // CHECK:STDOUT: %.loc13_16.2: %MyIntLiteral = converted %int_32, %.loc13_16.1 [concrete = constants.%int_32.2f2] // CHECK:STDOUT: %Int.call: init type = call %Int.ref(%.loc13_16.2) [concrete = constants.%i32.builtin] // CHECK:STDOUT: %.loc13_32.1: type = value_of_initializer %Int.call [concrete = constants.%i32.builtin] // CHECK:STDOUT: %.loc13_32.2: type = converted %Int.call, %.loc13_32.1 [concrete = constants.%i32.builtin] // CHECK:STDOUT: adapt_decl %.loc13_32.2 [concrete] // CHECK:STDOUT: %MyInt32.Make.decl: %MyInt32.Make.type = fn_decl @MyInt32.Make [concrete = constants.%MyInt32.Make] { // CHECK:STDOUT: %a.patt: %pattern_type.d59 = value_binding_pattern a [concrete] // CHECK:STDOUT: %a.param_patt: %pattern_type.d59 = value_param_pattern %a.patt [concrete] // CHECK:STDOUT: %return.patt: %pattern_type.4d1 = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: %pattern_type.4d1 = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %MyInt32.ref.loc15: type = name_ref MyInt32, file.%MyInt32.decl [concrete = constants.%MyInt32] // CHECK:STDOUT: %.loc15: Core.Form = init_form %MyInt32.ref.loc15 [concrete = constants.%.c88] // CHECK:STDOUT: %a.param.loc15: %MyIntLiteral = value_param call_param0 // CHECK:STDOUT: %MyIntLiteral.ref.loc15: type = name_ref MyIntLiteral, file.%MyIntLiteral.decl [concrete = constants.%MyIntLiteral] // CHECK:STDOUT: %a.loc15: %MyIntLiteral = value_binding a, %a.param.loc15 // CHECK:STDOUT: %return.param.loc15: ref %MyInt32 = out_param call_param1 // CHECK:STDOUT: %return.loc15: ref %MyInt32 = return_slot %return.param.loc15 // CHECK:STDOUT: } // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%i32.builtin [concrete = constants.%complete_type.833] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%MyInt32 // CHECK:STDOUT: .Int = // CHECK:STDOUT: .MyIntLiteral = // CHECK:STDOUT: .MyInt32 = // CHECK:STDOUT: .Make = %MyInt32.Make.decl // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @IntLiteral() -> out %return.param: type = "int_literal.make_type"; // CHECK:STDOUT: // CHECK:STDOUT: fn @Int(%N.param: %MyIntLiteral) -> out %return.param: type = "int.make_type_signed"; // CHECK:STDOUT: // CHECK:STDOUT: fn @MyInt32.Make(%a.param.loc18: %MyIntLiteral) -> out %return.param.loc18: %MyInt32 = "int.convert_checked"; // CHECK:STDOUT: // CHECK:STDOUT: fn @MyAdd(%a.param: %MyInt32, %b.param: %MyInt32) -> out %return.param: %MyInt32 = "int.sadd"; // CHECK:STDOUT: // CHECK:STDOUT: fn @__global_init() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %MyAdd.ref: %MyAdd.type = name_ref MyAdd, file.%MyAdd.decl [concrete = constants.%MyAdd] // CHECK:STDOUT: %MyInt32.ref.loc22_24: type = name_ref MyInt32, file.%MyInt32.decl [concrete = constants.%MyInt32] // CHECK:STDOUT: %Make.ref.loc22_31: %MyInt32.Make.type = name_ref Make, @MyInt32.%MyInt32.Make.decl [concrete = constants.%MyInt32.Make] // CHECK:STDOUT: %int_1: Core.IntLiteral = int_value 1 [concrete = constants.%int_1.5b8] // CHECK:STDOUT: %MyIntLiteral.ref.loc22_42: type = name_ref MyIntLiteral, file.%MyIntLiteral.decl [concrete = constants.%MyIntLiteral] // CHECK:STDOUT: %.loc22_39.1: %MyIntLiteral = as_compatible %int_1 [concrete = constants.%int_1.ed9] // CHECK:STDOUT: %.loc22_39.2: %MyIntLiteral = converted %int_1, %.loc22_39.1 [concrete = constants.%int_1.ed9] // CHECK:STDOUT: %MyInt32.Make.call.loc22_54: init %MyInt32 = call %Make.ref.loc22_31(%.loc22_39.2) [concrete = constants.%int_1.6de] // CHECK:STDOUT: %MyInt32.ref.loc22_57: type = name_ref MyInt32, file.%MyInt32.decl [concrete = constants.%MyInt32] // CHECK:STDOUT: %Make.ref.loc22_64: %MyInt32.Make.type = name_ref Make, @MyInt32.%MyInt32.Make.decl [concrete = constants.%MyInt32.Make] // CHECK:STDOUT: %int_2: Core.IntLiteral = int_value 2 [concrete = constants.%int_2.ecc] // CHECK:STDOUT: %MyIntLiteral.ref.loc22_75: type = name_ref MyIntLiteral, file.%MyIntLiteral.decl [concrete = constants.%MyIntLiteral] // CHECK:STDOUT: %.loc22_72.1: %MyIntLiteral = as_compatible %int_2 [concrete = constants.%int_2.7bb] // CHECK:STDOUT: %.loc22_72.2: %MyIntLiteral = converted %int_2, %.loc22_72.1 [concrete = constants.%int_2.7bb] // CHECK:STDOUT: %MyInt32.Make.call.loc22_87: init %MyInt32 = call %Make.ref.loc22_64(%.loc22_72.2) [concrete = constants.%int_2.035] // CHECK:STDOUT: %.loc22_54.1: %MyInt32 = value_of_initializer %MyInt32.Make.call.loc22_54 [concrete = constants.%int_1.6de] // CHECK:STDOUT: %.loc22_54.2: %MyInt32 = converted %MyInt32.Make.call.loc22_54, %.loc22_54.1 [concrete = constants.%int_1.6de] // CHECK:STDOUT: %.loc22_87.1: %MyInt32 = value_of_initializer %MyInt32.Make.call.loc22_87 [concrete = constants.%int_2.035] // CHECK:STDOUT: %.loc22_87.2: %MyInt32 = converted %MyInt32.Make.call.loc22_87, %.loc22_87.1 [concrete = constants.%int_2.035] // CHECK:STDOUT: %MyAdd.call: init %MyInt32 = call %MyAdd.ref(%.loc22_54.2, %.loc22_87.2) [concrete = constants.%int_3] // CHECK:STDOUT: assign file.%v.var, %MyAdd.call // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- fail_bad_adapt.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %MyIntLiteral: type = class_type @MyIntLiteral [concrete] // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %empty_struct: %empty_struct_type = struct_value () [concrete] // CHECK:STDOUT: %complete_type: = complete_type_witness %empty_struct_type [concrete] // CHECK:STDOUT: %pattern_type.d59: type = pattern_type %MyIntLiteral [concrete] // CHECK:STDOUT: %.805: Core.Form = init_form type [concrete] // CHECK:STDOUT: %pattern_type.98f: type = pattern_type type [concrete] // CHECK:STDOUT: %Int.type: type = fn_type @Int [concrete] // CHECK:STDOUT: %Int: %Int.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .MyIntLiteral = %MyIntLiteral.decl // CHECK:STDOUT: .Int = %Int.decl // CHECK:STDOUT: } // CHECK:STDOUT: %MyIntLiteral.decl: type = class_decl @MyIntLiteral [concrete = constants.%MyIntLiteral] {} {} // CHECK:STDOUT: %Int.decl: %Int.type = fn_decl @Int [concrete = constants.%Int] { // CHECK:STDOUT: %N.patt: %pattern_type.d59 = value_binding_pattern N [concrete] // CHECK:STDOUT: %N.param_patt: %pattern_type.d59 = value_param_pattern %N.patt [concrete] // CHECK:STDOUT: %return.patt: %pattern_type.98f = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: %pattern_type.98f = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %.loc12_28.1: type = type_literal type [concrete = type] // CHECK:STDOUT: %.loc12_28.2: Core.Form = init_form %.loc12_28.1 [concrete = constants.%.805] // CHECK:STDOUT: %N.param: %MyIntLiteral = value_param call_param0 // CHECK:STDOUT: %MyIntLiteral.ref: type = name_ref MyIntLiteral, file.%MyIntLiteral.decl [concrete = constants.%MyIntLiteral] // CHECK:STDOUT: %N: %MyIntLiteral = value_binding N, %N.param // CHECK:STDOUT: %return.param: ref type = out_param call_param1 // CHECK:STDOUT: %return: ref type = return_slot %return.param // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @MyIntLiteral { // CHECK:STDOUT: %.loc5_10: %empty_struct_type = struct_literal () [concrete = constants.%empty_struct] // CHECK:STDOUT: %.loc5_11: type = converted %.loc5_10, constants.%empty_struct_type [concrete = constants.%empty_struct_type] // CHECK:STDOUT: adapt_decl %.loc5_11 [concrete] // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%MyIntLiteral // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @Int(%N.param: %MyIntLiteral) -> out %return.param: type; // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/function/builtin/call.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/int.carbon // TODO: Add ranges and switch to "--dump-sem-ir-ranges=only". // EXTRA-ARGS: --dump-sem-ir-ranges=if-present // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/function/builtin/call.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/function/builtin/call.carbon fn Add(a: i32, b: i32) -> i32 = "int.sadd"; var arr: array(i32, Add(1, 2)); fn RuntimeCall(a: i32, b: i32) -> i32 { return Add(a, b); } // CHECK:STDOUT: --- call.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [concrete] // CHECK:STDOUT: %Int.type: type = generic_class_type @Int [concrete] // CHECK:STDOUT: %Int.generic: %Int.type = struct_value () [concrete] // CHECK:STDOUT: %i32: type = class_type @Int, @Int(%int_32) [concrete] // CHECK:STDOUT: %pattern_type.7ce: type = pattern_type %i32 [concrete] // CHECK:STDOUT: %.ff5: Core.Form = init_form %i32 [concrete] // CHECK:STDOUT: %Add.type: type = fn_type @Add [concrete] // CHECK:STDOUT: %Add: %Add.type = struct_value () [concrete] // CHECK:STDOUT: %int_1.5b8: Core.IntLiteral = int_value 1 [concrete] // CHECK:STDOUT: %int_2.ecc: Core.IntLiteral = int_value 2 [concrete] // CHECK:STDOUT: %ImplicitAs.type.cc7: type = generic_interface_type @ImplicitAs [concrete] // CHECK:STDOUT: %ImplicitAs.generic: %ImplicitAs.type.cc7 = struct_value () [concrete] // CHECK:STDOUT: %ImplicitAs.type.e8c: type = facet_type <@ImplicitAs, @ImplicitAs(%i32)> [concrete] // CHECK:STDOUT: %To: Core.IntLiteral = symbolic_binding To, 0 [symbolic] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.4e6: type = fn_type @Core.IntLiteral.as.ImplicitAs.impl.Convert, @Core.IntLiteral.as.ImplicitAs.impl(%To) [symbolic] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.3c2: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.4e6 = struct_value () [symbolic] // CHECK:STDOUT: %ImplicitAs.type.139: type = facet_type <@ImplicitAs, @ImplicitAs(Core.IntLiteral)> [concrete] // CHECK:STDOUT: %From: Core.IntLiteral = symbolic_binding From, 0 [symbolic] // CHECK:STDOUT: %Int.as.ImplicitAs.impl.Convert.type.2ed: type = fn_type @Int.as.ImplicitAs.impl.Convert, @Int.as.ImplicitAs.impl(%From) [symbolic] // CHECK:STDOUT: %Int.as.ImplicitAs.impl.Convert.d29: %Int.as.ImplicitAs.impl.Convert.type.2ed = struct_value () [symbolic] // CHECK:STDOUT: %ImplicitAs.impl_witness.6bc: = impl_witness imports.%ImplicitAs.impl_witness_table.74f, @Core.IntLiteral.as.ImplicitAs.impl(%int_32) [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.e0d: type = fn_type @Core.IntLiteral.as.ImplicitAs.impl.Convert, @Core.IntLiteral.as.ImplicitAs.impl(%int_32) [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.e0d = struct_value () [concrete] // CHECK:STDOUT: %ImplicitAs.facet.b94: %ImplicitAs.type.e8c = facet_value Core.IntLiteral, (%ImplicitAs.impl_witness.6bc) [concrete] // CHECK:STDOUT: %ImplicitAs.WithSelf.Convert.type.b37: type = fn_type @ImplicitAs.WithSelf.Convert, @ImplicitAs.WithSelf(%i32, %ImplicitAs.facet.b94) [concrete] // CHECK:STDOUT: %.545: type = fn_type_with_self_type %ImplicitAs.WithSelf.Convert.type.b37, %ImplicitAs.facet.b94 [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.bound.215: = bound_method %int_1.5b8, %Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5 [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn: = specific_function %Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5, @Core.IntLiteral.as.ImplicitAs.impl.Convert(%int_32) [concrete] // CHECK:STDOUT: %bound_method.38b: = bound_method %int_1.5b8, %Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn [concrete] // CHECK:STDOUT: %int_1.5d2: %i32 = int_value 1 [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.bound.4e5: = bound_method %int_2.ecc, %Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5 [concrete] // CHECK:STDOUT: %bound_method.646: = bound_method %int_2.ecc, %Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn [concrete] // CHECK:STDOUT: %int_2.ef8: %i32 = int_value 2 [concrete] // CHECK:STDOUT: %int_3.822: %i32 = int_value 3 [concrete] // CHECK:STDOUT: %ImplicitAs.impl_witness.640: = impl_witness imports.%ImplicitAs.impl_witness_table.ea2, @Int.as.ImplicitAs.impl(%int_32) [concrete] // CHECK:STDOUT: %Int.as.ImplicitAs.impl.Convert.type.240: type = fn_type @Int.as.ImplicitAs.impl.Convert, @Int.as.ImplicitAs.impl(%int_32) [concrete] // CHECK:STDOUT: %Int.as.ImplicitAs.impl.Convert.dd4: %Int.as.ImplicitAs.impl.Convert.type.240 = struct_value () [concrete] // CHECK:STDOUT: %ImplicitAs.facet.290: %ImplicitAs.type.139 = facet_value %i32, (%ImplicitAs.impl_witness.640) [concrete] // CHECK:STDOUT: %ImplicitAs.WithSelf.Convert.type.462: type = fn_type @ImplicitAs.WithSelf.Convert, @ImplicitAs.WithSelf(Core.IntLiteral, %ImplicitAs.facet.290) [concrete] // CHECK:STDOUT: %.0a7: type = fn_type_with_self_type %ImplicitAs.WithSelf.Convert.type.462, %ImplicitAs.facet.290 [concrete] // CHECK:STDOUT: %Int.as.ImplicitAs.impl.Convert.bound: = bound_method %int_3.822, %Int.as.ImplicitAs.impl.Convert.dd4 [concrete] // CHECK:STDOUT: %Int.as.ImplicitAs.impl.Convert.specific_fn: = specific_function %Int.as.ImplicitAs.impl.Convert.dd4, @Int.as.ImplicitAs.impl.Convert(%int_32) [concrete] // CHECK:STDOUT: %bound_method.17a: = bound_method %int_3.822, %Int.as.ImplicitAs.impl.Convert.specific_fn [concrete] // CHECK:STDOUT: %int_3.1ba: Core.IntLiteral = int_value 3 [concrete] // CHECK:STDOUT: %array_type: type = array_type %int_3.1ba, %i32 [concrete] // CHECK:STDOUT: %pattern_type.5d8: type = pattern_type %array_type [concrete] // CHECK:STDOUT: %DefaultOrUnformed.type: type = facet_type <@DefaultOrUnformed> [concrete] // CHECK:STDOUT: %T: type = symbolic_binding T, 0 [symbolic] // CHECK:STDOUT: %T.as.DefaultOrUnformed.impl.Op.type.6c6: type = fn_type @T.as.DefaultOrUnformed.impl.Op, @T.as.DefaultOrUnformed.impl(%T) [symbolic] // CHECK:STDOUT: %T.as.DefaultOrUnformed.impl.Op.ae1: %T.as.DefaultOrUnformed.impl.Op.type.6c6 = struct_value () [symbolic] // CHECK:STDOUT: %DefaultOrUnformed.impl_witness.d11: = impl_witness imports.%DefaultOrUnformed.impl_witness_table, @T.as.DefaultOrUnformed.impl(%array_type) [concrete] // CHECK:STDOUT: %T.as.DefaultOrUnformed.impl.Op.type.576: type = fn_type @T.as.DefaultOrUnformed.impl.Op, @T.as.DefaultOrUnformed.impl(%array_type) [concrete] // CHECK:STDOUT: %T.as.DefaultOrUnformed.impl.Op.9c0: %T.as.DefaultOrUnformed.impl.Op.type.576 = struct_value () [concrete] // CHECK:STDOUT: %DefaultOrUnformed.facet: %DefaultOrUnformed.type = facet_value %array_type, (%DefaultOrUnformed.impl_witness.d11) [concrete] // CHECK:STDOUT: %T.as.DefaultOrUnformed.impl.Op.specific_fn: = specific_function %T.as.DefaultOrUnformed.impl.Op.9c0, @T.as.DefaultOrUnformed.impl.Op(%array_type) [concrete] // CHECK:STDOUT: %RuntimeCall.type: type = fn_type @RuntimeCall [concrete] // CHECK:STDOUT: %RuntimeCall: %RuntimeCall.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: .Int = %Core.Int // CHECK:STDOUT: .ImplicitAs = %Core.ImplicitAs // CHECK:STDOUT: .DefaultOrUnformed = %Core.DefaultOrUnformed // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: %Core.Int: %Int.type = import_ref Core//prelude/parts/int, Int, loaded [concrete = constants.%Int.generic] // CHECK:STDOUT: %Core.ImplicitAs: %ImplicitAs.type.cc7 = import_ref Core//prelude/parts/as, ImplicitAs, loaded [concrete = constants.%ImplicitAs.generic] // CHECK:STDOUT: %Core.import_ref.42d: @Core.IntLiteral.as.ImplicitAs.impl.%Core.IntLiteral.as.ImplicitAs.impl.Convert.type (%Core.IntLiteral.as.ImplicitAs.impl.Convert.type.4e6) = import_ref Core//prelude/parts/int, loc{{\d+_\d+}}, loaded [symbolic = @Core.IntLiteral.as.ImplicitAs.impl.%Core.IntLiteral.as.ImplicitAs.impl.Convert (constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.3c2)] // CHECK:STDOUT: %ImplicitAs.impl_witness_table.74f = impl_witness_table (%Core.import_ref.42d), @Core.IntLiteral.as.ImplicitAs.impl [concrete] // CHECK:STDOUT: %Core.import_ref.0bc: @Int.as.ImplicitAs.impl.%Int.as.ImplicitAs.impl.Convert.type (%Int.as.ImplicitAs.impl.Convert.type.2ed) = import_ref Core//prelude/parts/int, loc{{\d+_\d+}}, loaded [symbolic = @Int.as.ImplicitAs.impl.%Int.as.ImplicitAs.impl.Convert (constants.%Int.as.ImplicitAs.impl.Convert.d29)] // CHECK:STDOUT: %ImplicitAs.impl_witness_table.ea2 = impl_witness_table (%Core.import_ref.0bc), @Int.as.ImplicitAs.impl [concrete] // CHECK:STDOUT: %Core.DefaultOrUnformed: type = import_ref Core//prelude/parts/default, DefaultOrUnformed, loaded [concrete = constants.%DefaultOrUnformed.type] // CHECK:STDOUT: %Core.import_ref.01d: @T.as.DefaultOrUnformed.impl.%T.as.DefaultOrUnformed.impl.Op.type (%T.as.DefaultOrUnformed.impl.Op.type.6c6) = import_ref Core//prelude/parts/default, loc{{\d+_\d+}}, loaded [symbolic = @T.as.DefaultOrUnformed.impl.%T.as.DefaultOrUnformed.impl.Op (constants.%T.as.DefaultOrUnformed.impl.Op.ae1)] // CHECK:STDOUT: %DefaultOrUnformed.impl_witness_table = impl_witness_table (%Core.import_ref.01d), @T.as.DefaultOrUnformed.impl [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .Add = %Add.decl // CHECK:STDOUT: .arr = %arr // CHECK:STDOUT: .RuntimeCall = %RuntimeCall.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %Add.decl: %Add.type = fn_decl @Add [concrete = constants.%Add] { // CHECK:STDOUT: %a.patt: %pattern_type.7ce = value_binding_pattern a [concrete] // CHECK:STDOUT: %a.param_patt: %pattern_type.7ce = value_param_pattern %a.patt [concrete] // CHECK:STDOUT: %b.patt: %pattern_type.7ce = value_binding_pattern b [concrete] // CHECK:STDOUT: %b.param_patt: %pattern_type.7ce = value_param_pattern %b.patt [concrete] // CHECK:STDOUT: %return.patt: %pattern_type.7ce = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: %pattern_type.7ce = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %i32.loc15_27: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %.loc15: Core.Form = init_form %i32.loc15_27 [concrete = constants.%.ff5] // CHECK:STDOUT: %a.param: %i32 = value_param call_param0 // CHECK:STDOUT: %i32.loc15_11: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %a: %i32 = value_binding a, %a.param // CHECK:STDOUT: %b.param: %i32 = value_param call_param1 // CHECK:STDOUT: %i32.loc15_19: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %b: %i32 = value_binding b, %b.param // CHECK:STDOUT: %return.param: ref %i32 = out_param call_param2 // CHECK:STDOUT: %return: ref %i32 = return_slot %return.param // CHECK:STDOUT: } // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %arr.patt: %pattern_type.5d8 = ref_binding_pattern arr [concrete] // CHECK:STDOUT: %arr.var_patt: %pattern_type.5d8 = var_pattern %arr.patt [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %arr.var: ref %array_type = var %arr.var_patt [concrete] // CHECK:STDOUT: %.loc17_30: type = splice_block %array_type [concrete = constants.%array_type] { // CHECK:STDOUT: %i32: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %Add.ref: %Add.type = name_ref Add, %Add.decl [concrete = constants.%Add] // CHECK:STDOUT: %int_1: Core.IntLiteral = int_value 1 [concrete = constants.%int_1.5b8] // CHECK:STDOUT: %int_2: Core.IntLiteral = int_value 2 [concrete = constants.%int_2.ecc] // CHECK:STDOUT: %impl.elem0.loc17_25: %.545 = impl_witness_access constants.%ImplicitAs.impl_witness.6bc, element0 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5] // CHECK:STDOUT: %bound_method.loc17_25.1: = bound_method %int_1, %impl.elem0.loc17_25 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.bound.215] // CHECK:STDOUT: %specific_fn.loc17_25: = specific_function %impl.elem0.loc17_25, @Core.IntLiteral.as.ImplicitAs.impl.Convert(constants.%int_32) [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn] // CHECK:STDOUT: %bound_method.loc17_25.2: = bound_method %int_1, %specific_fn.loc17_25 [concrete = constants.%bound_method.38b] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc17_25: init %i32 = call %bound_method.loc17_25.2(%int_1) [concrete = constants.%int_1.5d2] // CHECK:STDOUT: %.loc17_25.1: %i32 = value_of_initializer %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc17_25 [concrete = constants.%int_1.5d2] // CHECK:STDOUT: %.loc17_25.2: %i32 = converted %int_1, %.loc17_25.1 [concrete = constants.%int_1.5d2] // CHECK:STDOUT: %impl.elem0.loc17_28: %.545 = impl_witness_access constants.%ImplicitAs.impl_witness.6bc, element0 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5] // CHECK:STDOUT: %bound_method.loc17_28.1: = bound_method %int_2, %impl.elem0.loc17_28 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.bound.4e5] // CHECK:STDOUT: %specific_fn.loc17_28: = specific_function %impl.elem0.loc17_28, @Core.IntLiteral.as.ImplicitAs.impl.Convert(constants.%int_32) [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn] // CHECK:STDOUT: %bound_method.loc17_28.2: = bound_method %int_2, %specific_fn.loc17_28 [concrete = constants.%bound_method.646] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc17_28: init %i32 = call %bound_method.loc17_28.2(%int_2) [concrete = constants.%int_2.ef8] // CHECK:STDOUT: %.loc17_28.1: %i32 = value_of_initializer %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc17_28 [concrete = constants.%int_2.ef8] // CHECK:STDOUT: %.loc17_28.2: %i32 = converted %int_2, %.loc17_28.1 [concrete = constants.%int_2.ef8] // CHECK:STDOUT: %Add.call: init %i32 = call %Add.ref(%.loc17_25.2, %.loc17_28.2) [concrete = constants.%int_3.822] // CHECK:STDOUT: %impl.elem0.loc17_29: %.0a7 = impl_witness_access constants.%ImplicitAs.impl_witness.640, element0 [concrete = constants.%Int.as.ImplicitAs.impl.Convert.dd4] // CHECK:STDOUT: %bound_method.loc17_29.1: = bound_method %Add.call, %impl.elem0.loc17_29 [concrete = constants.%Int.as.ImplicitAs.impl.Convert.bound] // CHECK:STDOUT: %specific_fn.loc17_29: = specific_function %impl.elem0.loc17_29, @Int.as.ImplicitAs.impl.Convert(constants.%int_32) [concrete = constants.%Int.as.ImplicitAs.impl.Convert.specific_fn] // CHECK:STDOUT: %bound_method.loc17_29.2: = bound_method %Add.call, %specific_fn.loc17_29 [concrete = constants.%bound_method.17a] // CHECK:STDOUT: %.loc17_29.1: %i32 = value_of_initializer %Add.call [concrete = constants.%int_3.822] // CHECK:STDOUT: %.loc17_29.2: %i32 = converted %Add.call, %.loc17_29.1 [concrete = constants.%int_3.822] // CHECK:STDOUT: %Int.as.ImplicitAs.impl.Convert.call: init Core.IntLiteral = call %bound_method.loc17_29.2(%.loc17_29.2) [concrete = constants.%int_3.1ba] // CHECK:STDOUT: %.loc17_29.3: Core.IntLiteral = value_of_initializer %Int.as.ImplicitAs.impl.Convert.call [concrete = constants.%int_3.1ba] // CHECK:STDOUT: %.loc17_29.4: Core.IntLiteral = converted %Add.call, %.loc17_29.3 [concrete = constants.%int_3.1ba] // CHECK:STDOUT: %array_type: type = array_type %.loc17_29.4, %i32 [concrete = constants.%array_type] // CHECK:STDOUT: } // CHECK:STDOUT: %arr: ref %array_type = ref_binding arr, %arr.var [concrete = %arr.var] // CHECK:STDOUT: %RuntimeCall.decl: %RuntimeCall.type = fn_decl @RuntimeCall [concrete = constants.%RuntimeCall] { // CHECK:STDOUT: %a.patt: %pattern_type.7ce = value_binding_pattern a [concrete] // CHECK:STDOUT: %a.param_patt: %pattern_type.7ce = value_param_pattern %a.patt [concrete] // CHECK:STDOUT: %b.patt: %pattern_type.7ce = value_binding_pattern b [concrete] // CHECK:STDOUT: %b.param_patt: %pattern_type.7ce = value_param_pattern %b.patt [concrete] // CHECK:STDOUT: %return.patt: %pattern_type.7ce = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: %pattern_type.7ce = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %i32.loc19_35: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %.loc19: Core.Form = init_form %i32.loc19_35 [concrete = constants.%.ff5] // CHECK:STDOUT: %a.param: %i32 = value_param call_param0 // CHECK:STDOUT: %i32.loc19_19: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %a: %i32 = value_binding a, %a.param // CHECK:STDOUT: %b.param: %i32 = value_param call_param1 // CHECK:STDOUT: %i32.loc19_27: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %b: %i32 = value_binding b, %b.param // CHECK:STDOUT: %return.param: ref %i32 = out_param call_param2 // CHECK:STDOUT: %return: ref %i32 = return_slot %return.param // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @Add(%a.param: %i32, %b.param: %i32) -> out %return.param: %i32 = "int.sadd"; // CHECK:STDOUT: // CHECK:STDOUT: fn @RuntimeCall(%a.param: %i32, %b.param: %i32) -> out %return.param: %i32 { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %Add.ref: %Add.type = name_ref Add, file.%Add.decl [concrete = constants.%Add] // CHECK:STDOUT: %a.ref: %i32 = name_ref a, %a // CHECK:STDOUT: %b.ref: %i32 = name_ref b, %b // CHECK:STDOUT: %Add.call: init %i32 = call %Add.ref(%a.ref, %b.ref) // CHECK:STDOUT: return %Add.call // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @__global_init() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %DefaultOrUnformed.facet: %DefaultOrUnformed.type = facet_value constants.%array_type, (constants.%DefaultOrUnformed.impl_witness.d11) [concrete = constants.%DefaultOrUnformed.facet] // CHECK:STDOUT: %.loc17_31.1: %DefaultOrUnformed.type = converted constants.%array_type, %DefaultOrUnformed.facet [concrete = constants.%DefaultOrUnformed.facet] // CHECK:STDOUT: %as_type: type = facet_access_type %.loc17_31.1 [concrete = constants.%array_type] // CHECK:STDOUT: %.loc17_31.2: type = converted %.loc17_31.1, %as_type [concrete = constants.%array_type] // CHECK:STDOUT: %T.as.DefaultOrUnformed.impl.Op.specific_fn: = specific_function constants.%T.as.DefaultOrUnformed.impl.Op.9c0, @T.as.DefaultOrUnformed.impl.Op(constants.%array_type) [concrete = constants.%T.as.DefaultOrUnformed.impl.Op.specific_fn] // CHECK:STDOUT: %.loc17_1: ref %array_type = splice_block file.%arr.var [concrete = file.%arr.var] {} // CHECK:STDOUT: %T.as.DefaultOrUnformed.impl.Op.call: init %array_type to %.loc17_1 = call %T.as.DefaultOrUnformed.impl.Op.specific_fn() // CHECK:STDOUT: assign file.%arr.var, %T.as.DefaultOrUnformed.impl.Op.call // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/function/builtin/call_from_operator.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/none.carbon // TODO: Add ranges and switch to "--dump-sem-ir-ranges=only". // EXTRA-ARGS: --dump-sem-ir-ranges=if-present // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/function/builtin/call_from_operator.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/function/builtin/call_from_operator.carbon // --- core.carbon package Core; fn IntLiteral() -> type = "int_literal.make_type"; fn Int(N: IntLiteral()) -> type = "int.make_type_signed"; interface AddWith(T:! type) { fn Op[self: Self](other: Self) -> Self; } interface As(T:! type) { fn Convert[self: Self]() -> T; } interface ImplicitAs(T:! type) { fn Convert[self: Self]() -> T; } impl i32 as AddWith(i32) { fn Op[self: Self](other: Self) -> Self = "int.sadd"; } impl IntLiteral() as As(i32) { fn Convert[self: Self]() -> i32 = "int.convert_checked"; } impl IntLiteral() as ImplicitAs(i32) { fn Convert[self: Self]() -> i32 = "int.convert_checked"; } impl i32 as ImplicitAs(IntLiteral()) { fn Convert[self: Self]() -> IntLiteral() = "int.convert_checked"; } // --- user.carbon import Core; var arr: array(i32, (1 as i32) + (2 as i32)) = (3, 4, (3 as i32) + (4 as i32)); // CHECK:STDOUT: --- core.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %.805: Core.Form = init_form type [concrete] // CHECK:STDOUT: %pattern_type.98f: type = pattern_type type [concrete] // CHECK:STDOUT: %IntLiteral.type: type = fn_type @IntLiteral [concrete] // CHECK:STDOUT: %IntLiteral: %IntLiteral.type = struct_value () [concrete] // CHECK:STDOUT: %pattern_type.dc0: type = pattern_type Core.IntLiteral [concrete] // CHECK:STDOUT: %Int.type: type = fn_type @Int [concrete] // CHECK:STDOUT: %Int: %Int.type = struct_value () [concrete] // CHECK:STDOUT: %type: type = facet_type [concrete] // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self] // CHECK:STDOUT: %T: type = symbolic_binding T, 0 [symbolic] // CHECK:STDOUT: %AddWith.type.b56: type = generic_interface_type @AddWith [concrete] // CHECK:STDOUT: %AddWith.generic: %AddWith.type.b56 = struct_value () [concrete] // CHECK:STDOUT: %AddWith.type.26b: type = facet_type <@AddWith, @AddWith(%T)> [symbolic] // CHECK:STDOUT: %Self.a37: %AddWith.type.26b = symbolic_binding Self, 1 [symbolic] // CHECK:STDOUT: %Self.binding.as_type.73e: type = symbolic_binding_type Self, 1, %Self.a37 [symbolic] // CHECK:STDOUT: %pattern_type.1f3: type = pattern_type %Self.binding.as_type.73e [symbolic] // CHECK:STDOUT: %.409: Core.Form = init_form %Self.binding.as_type.73e [symbolic] // CHECK:STDOUT: %AddWith.WithSelf.Op.type.65a: type = fn_type @AddWith.WithSelf.Op, @AddWith.WithSelf(%T, %Self.a37) [symbolic] // CHECK:STDOUT: %AddWith.WithSelf.Op.647: %AddWith.WithSelf.Op.type.65a = struct_value () [symbolic] // CHECK:STDOUT: %AddWith.assoc_type.5ad: type = assoc_entity_type @AddWith, @AddWith(%T) [symbolic] // CHECK:STDOUT: %assoc0.90d: %AddWith.assoc_type.5ad = assoc_entity element0, @AddWith.WithSelf.%AddWith.WithSelf.Op.decl [symbolic] // CHECK:STDOUT: %As.type.3c9: type = generic_interface_type @As [concrete] // CHECK:STDOUT: %As.generic: %As.type.3c9 = struct_value () [concrete] // CHECK:STDOUT: %As.type.b54: type = facet_type <@As, @As(%T)> [symbolic] // CHECK:STDOUT: %Self.a8c: %As.type.b54 = symbolic_binding Self, 1 [symbolic] // CHECK:STDOUT: %Self.binding.as_type.69d: type = symbolic_binding_type Self, 1, %Self.a8c [symbolic] // CHECK:STDOUT: %pattern_type.24e: type = pattern_type %Self.binding.as_type.69d [symbolic] // CHECK:STDOUT: %.184: Core.Form = init_form %T [symbolic] // CHECK:STDOUT: %pattern_type.51d: type = pattern_type %T [symbolic] // CHECK:STDOUT: %As.WithSelf.Convert.type.aef: type = fn_type @As.WithSelf.Convert, @As.WithSelf(%T, %Self.a8c) [symbolic] // CHECK:STDOUT: %As.WithSelf.Convert.a95: %As.WithSelf.Convert.type.aef = struct_value () [symbolic] // CHECK:STDOUT: %As.assoc_type.520: type = assoc_entity_type @As, @As(%T) [symbolic] // CHECK:STDOUT: %assoc0.84c: %As.assoc_type.520 = assoc_entity element0, @As.WithSelf.%As.WithSelf.Convert.decl [symbolic] // CHECK:STDOUT: %ImplicitAs.type.595: type = generic_interface_type @ImplicitAs [concrete] // CHECK:STDOUT: %ImplicitAs.generic: %ImplicitAs.type.595 = struct_value () [concrete] // CHECK:STDOUT: %ImplicitAs.type.9fe: type = facet_type <@ImplicitAs, @ImplicitAs(%T)> [symbolic] // CHECK:STDOUT: %Self.7c0: %ImplicitAs.type.9fe = symbolic_binding Self, 1 [symbolic] // CHECK:STDOUT: %Self.binding.as_type.984: type = symbolic_binding_type Self, 1, %Self.7c0 [symbolic] // CHECK:STDOUT: %pattern_type.8de: type = pattern_type %Self.binding.as_type.984 [symbolic] // CHECK:STDOUT: %ImplicitAs.WithSelf.Convert.type.f6b: type = fn_type @ImplicitAs.WithSelf.Convert, @ImplicitAs.WithSelf(%T, %Self.7c0) [symbolic] // CHECK:STDOUT: %ImplicitAs.WithSelf.Convert.de0: %ImplicitAs.WithSelf.Convert.type.f6b = struct_value () [symbolic] // CHECK:STDOUT: %ImplicitAs.assoc_type.b04: type = assoc_entity_type @ImplicitAs, @ImplicitAs(%T) [symbolic] // CHECK:STDOUT: %assoc0.361: %ImplicitAs.assoc_type.b04 = assoc_entity element0, @ImplicitAs.WithSelf.%ImplicitAs.WithSelf.Convert.decl [symbolic] // CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [concrete] // CHECK:STDOUT: %i32.builtin: type = int_type signed, %int_32 [concrete] // CHECK:STDOUT: %AddWith.type.aed: type = facet_type <@AddWith, @AddWith(%i32.builtin)> [concrete] // CHECK:STDOUT: %AddWith.impl_witness: = impl_witness @i32.builtin.as.AddWith.impl.%AddWith.impl_witness_table [concrete] // CHECK:STDOUT: %Self.aad: %AddWith.type.aed = symbolic_binding Self, 1 [symbolic] // CHECK:STDOUT: %AddWith.WithSelf.Op.type.479: type = fn_type @AddWith.WithSelf.Op, @AddWith.WithSelf(%i32.builtin, %Self.a37) [symbolic] // CHECK:STDOUT: %AddWith.WithSelf.Op.8bc: %AddWith.WithSelf.Op.type.479 = struct_value () [symbolic] // CHECK:STDOUT: %AddWith.assoc_type.97c: type = assoc_entity_type @AddWith, @AddWith(%i32.builtin) [concrete] // CHECK:STDOUT: %assoc0.4d8: %AddWith.assoc_type.97c = assoc_entity element0, @AddWith.WithSelf.%AddWith.WithSelf.Op.decl [concrete] // CHECK:STDOUT: %pattern_type.956: type = pattern_type %i32.builtin [concrete] // CHECK:STDOUT: %.9cb: Core.Form = init_form %i32.builtin [concrete] // CHECK:STDOUT: %i32.builtin.as.AddWith.impl.Op.type: type = fn_type @i32.builtin.as.AddWith.impl.Op [concrete] // CHECK:STDOUT: %i32.builtin.as.AddWith.impl.Op: %i32.builtin.as.AddWith.impl.Op.type = struct_value () [concrete] // CHECK:STDOUT: %AddWith.facet: %AddWith.type.aed = facet_value %i32.builtin, (%AddWith.impl_witness) [concrete] // CHECK:STDOUT: %AddWith.WithSelf.Op.type.493: type = fn_type @AddWith.WithSelf.Op, @AddWith.WithSelf(%i32.builtin, %AddWith.facet) [concrete] // CHECK:STDOUT: %AddWith.WithSelf.Op.d6a: %AddWith.WithSelf.Op.type.493 = struct_value () [concrete] // CHECK:STDOUT: %As.type.1ed: type = facet_type <@As, @As(%i32.builtin)> [concrete] // CHECK:STDOUT: %As.impl_witness: = impl_witness @Core.IntLiteral.as.As.impl.%As.impl_witness_table [concrete] // CHECK:STDOUT: %Self.037: %As.type.1ed = symbolic_binding Self, 1 [symbolic] // CHECK:STDOUT: %As.WithSelf.Convert.type.a02: type = fn_type @As.WithSelf.Convert, @As.WithSelf(%i32.builtin, %Self.a8c) [symbolic] // CHECK:STDOUT: %As.WithSelf.Convert.98f: %As.WithSelf.Convert.type.a02 = struct_value () [symbolic] // CHECK:STDOUT: %As.assoc_type.c44: type = assoc_entity_type @As, @As(%i32.builtin) [concrete] // CHECK:STDOUT: %assoc0.43d: %As.assoc_type.c44 = assoc_entity element0, @As.WithSelf.%As.WithSelf.Convert.decl [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.As.impl.Convert.type: type = fn_type @Core.IntLiteral.as.As.impl.Convert [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.As.impl.Convert: %Core.IntLiteral.as.As.impl.Convert.type = struct_value () [concrete] // CHECK:STDOUT: %As.facet: %As.type.1ed = facet_value Core.IntLiteral, (%As.impl_witness) [concrete] // CHECK:STDOUT: %As.WithSelf.Convert.type.204: type = fn_type @As.WithSelf.Convert, @As.WithSelf(%i32.builtin, %As.facet) [concrete] // CHECK:STDOUT: %As.WithSelf.Convert.24c: %As.WithSelf.Convert.type.204 = struct_value () [concrete] // CHECK:STDOUT: %ImplicitAs.type.92b: type = facet_type <@ImplicitAs, @ImplicitAs(%i32.builtin)> [concrete] // CHECK:STDOUT: %ImplicitAs.impl_witness.ccd: = impl_witness @Core.IntLiteral.as.ImplicitAs.impl.%ImplicitAs.impl_witness_table [concrete] // CHECK:STDOUT: %Self.597: %ImplicitAs.type.92b = symbolic_binding Self, 1 [symbolic] // CHECK:STDOUT: %ImplicitAs.WithSelf.Convert.type.d4c: type = fn_type @ImplicitAs.WithSelf.Convert, @ImplicitAs.WithSelf(%i32.builtin, %Self.7c0) [symbolic] // CHECK:STDOUT: %ImplicitAs.WithSelf.Convert.086: %ImplicitAs.WithSelf.Convert.type.d4c = struct_value () [symbolic] // CHECK:STDOUT: %ImplicitAs.assoc_type.c3f: type = assoc_entity_type @ImplicitAs, @ImplicitAs(%i32.builtin) [concrete] // CHECK:STDOUT: %assoc0.124: %ImplicitAs.assoc_type.c3f = assoc_entity element0, @ImplicitAs.WithSelf.%ImplicitAs.WithSelf.Convert.decl [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type: type = fn_type @Core.IntLiteral.as.ImplicitAs.impl.Convert [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type = struct_value () [concrete] // CHECK:STDOUT: %ImplicitAs.facet.b36: %ImplicitAs.type.92b = facet_value Core.IntLiteral, (%ImplicitAs.impl_witness.ccd) [concrete] // CHECK:STDOUT: %ImplicitAs.WithSelf.Convert.type.07c: type = fn_type @ImplicitAs.WithSelf.Convert, @ImplicitAs.WithSelf(%i32.builtin, %ImplicitAs.facet.b36) [concrete] // CHECK:STDOUT: %ImplicitAs.WithSelf.Convert.a38: %ImplicitAs.WithSelf.Convert.type.07c = struct_value () [concrete] // CHECK:STDOUT: %ImplicitAs.type.79c: type = facet_type <@ImplicitAs, @ImplicitAs(Core.IntLiteral)> [concrete] // CHECK:STDOUT: %ImplicitAs.impl_witness.985: = impl_witness @i32.builtin.as.ImplicitAs.impl.%ImplicitAs.impl_witness_table [concrete] // CHECK:STDOUT: %Self.55f: %ImplicitAs.type.79c = symbolic_binding Self, 1 [symbolic] // CHECK:STDOUT: %ImplicitAs.WithSelf.Convert.type.25a: type = fn_type @ImplicitAs.WithSelf.Convert, @ImplicitAs.WithSelf(Core.IntLiteral, %Self.7c0) [symbolic] // CHECK:STDOUT: %ImplicitAs.WithSelf.Convert.1ae: %ImplicitAs.WithSelf.Convert.type.25a = struct_value () [symbolic] // CHECK:STDOUT: %ImplicitAs.assoc_type.793: type = assoc_entity_type @ImplicitAs, @ImplicitAs(Core.IntLiteral) [concrete] // CHECK:STDOUT: %assoc0.e18: %ImplicitAs.assoc_type.793 = assoc_entity element0, @ImplicitAs.WithSelf.%ImplicitAs.WithSelf.Convert.decl [concrete] // CHECK:STDOUT: %.f7d: Core.Form = init_form Core.IntLiteral [concrete] // CHECK:STDOUT: %i32.builtin.as.ImplicitAs.impl.Convert.type: type = fn_type @i32.builtin.as.ImplicitAs.impl.Convert [concrete] // CHECK:STDOUT: %i32.builtin.as.ImplicitAs.impl.Convert: %i32.builtin.as.ImplicitAs.impl.Convert.type = struct_value () [concrete] // CHECK:STDOUT: %ImplicitAs.facet.c5c: %ImplicitAs.type.79c = facet_value %i32.builtin, (%ImplicitAs.impl_witness.985) [concrete] // CHECK:STDOUT: %ImplicitAs.WithSelf.Convert.type.7aa: type = fn_type @ImplicitAs.WithSelf.Convert, @ImplicitAs.WithSelf(Core.IntLiteral, %ImplicitAs.facet.c5c) [concrete] // CHECK:STDOUT: %ImplicitAs.WithSelf.Convert.090: %ImplicitAs.WithSelf.Convert.type.7aa = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .IntLiteral = %IntLiteral.decl // CHECK:STDOUT: .Int = %Int.decl // CHECK:STDOUT: .AddWith = %AddWith.decl // CHECK:STDOUT: .As = %As.decl // CHECK:STDOUT: .ImplicitAs = %ImplicitAs.decl // CHECK:STDOUT: } // CHECK:STDOUT: %IntLiteral.decl: %IntLiteral.type = fn_decl @IntLiteral [concrete = constants.%IntLiteral] { // CHECK:STDOUT: %return.patt: %pattern_type.98f = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: %pattern_type.98f = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %.loc4_20.1: type = type_literal type [concrete = type] // CHECK:STDOUT: %.loc4_20.2: Core.Form = init_form %.loc4_20.1 [concrete = constants.%.805] // CHECK:STDOUT: %return.param: ref type = out_param call_param0 // CHECK:STDOUT: %return: ref type = return_slot %return.param // CHECK:STDOUT: } // CHECK:STDOUT: %Int.decl: %Int.type = fn_decl @Int [concrete = constants.%Int] { // CHECK:STDOUT: %N.patt: %pattern_type.dc0 = value_binding_pattern N [concrete] // CHECK:STDOUT: %N.param_patt: %pattern_type.dc0 = value_param_pattern %N.patt [concrete] // CHECK:STDOUT: %return.patt: %pattern_type.98f = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: %pattern_type.98f = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %.loc5_28.1: type = type_literal type [concrete = type] // CHECK:STDOUT: %.loc5_28.2: Core.Form = init_form %.loc5_28.1 [concrete = constants.%.805] // CHECK:STDOUT: %N.param: Core.IntLiteral = value_param call_param0 // CHECK:STDOUT: %.loc5_22.1: type = splice_block %.loc5_22.3 [concrete = Core.IntLiteral] { // CHECK:STDOUT: %IntLiteral.ref: %IntLiteral.type = name_ref IntLiteral, file.%IntLiteral.decl [concrete = constants.%IntLiteral] // CHECK:STDOUT: %IntLiteral.call: init type = call %IntLiteral.ref() [concrete = Core.IntLiteral] // CHECK:STDOUT: %.loc5_22.2: type = value_of_initializer %IntLiteral.call [concrete = Core.IntLiteral] // CHECK:STDOUT: %.loc5_22.3: type = converted %IntLiteral.call, %.loc5_22.2 [concrete = Core.IntLiteral] // CHECK:STDOUT: } // CHECK:STDOUT: %N: Core.IntLiteral = value_binding N, %N.param // CHECK:STDOUT: %return.param: ref type = out_param call_param1 // CHECK:STDOUT: %return: ref type = return_slot %return.param // CHECK:STDOUT: } // CHECK:STDOUT: %AddWith.decl: %AddWith.type.b56 = interface_decl @AddWith [concrete = constants.%AddWith.generic] { // CHECK:STDOUT: %T.patt: %pattern_type.98f = symbolic_binding_pattern T, 0 [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %.loc7_23.1: type = splice_block %.loc7_23.2 [concrete = type] { // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %.loc7_23.2: type = type_literal type [concrete = type] // CHECK:STDOUT: } // CHECK:STDOUT: %T.loc7_19.2: type = symbolic_binding T, 0 [symbolic = %T.loc7_19.1 (constants.%T)] // CHECK:STDOUT: } // CHECK:STDOUT: %As.decl: %As.type.3c9 = interface_decl @As [concrete = constants.%As.generic] { // CHECK:STDOUT: %T.patt: %pattern_type.98f = symbolic_binding_pattern T, 0 [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %.loc11_18.1: type = splice_block %.loc11_18.2 [concrete = type] { // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %.loc11_18.2: type = type_literal type [concrete = type] // CHECK:STDOUT: } // CHECK:STDOUT: %T.loc11_14.2: type = symbolic_binding T, 0 [symbolic = %T.loc11_14.1 (constants.%T)] // CHECK:STDOUT: } // CHECK:STDOUT: %ImplicitAs.decl: %ImplicitAs.type.595 = interface_decl @ImplicitAs [concrete = constants.%ImplicitAs.generic] { // CHECK:STDOUT: %T.patt: %pattern_type.98f = symbolic_binding_pattern T, 0 [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %.loc15_26.1: type = splice_block %.loc15_26.2 [concrete = type] { // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %.loc15_26.2: type = type_literal type [concrete = type] // CHECK:STDOUT: } // CHECK:STDOUT: %T.loc15_22.2: type = symbolic_binding T, 0 [symbolic = %T.loc15_22.1 (constants.%T)] // CHECK:STDOUT: } // CHECK:STDOUT: impl_decl @i32.builtin.as.AddWith.impl [concrete] {} { // CHECK:STDOUT: %.loc19_6: type = type_literal constants.%i32.builtin [concrete = constants.%i32.builtin] // CHECK:STDOUT: %AddWith.ref: %AddWith.type.b56 = name_ref AddWith, file.%AddWith.decl [concrete = constants.%AddWith.generic] // CHECK:STDOUT: %.loc19_21: type = type_literal constants.%i32.builtin [concrete = constants.%i32.builtin] // CHECK:STDOUT: %AddWith.type: type = facet_type <@AddWith, @AddWith(constants.%i32.builtin)> [concrete = constants.%AddWith.type.aed] // CHECK:STDOUT: } // CHECK:STDOUT: impl_decl @Core.IntLiteral.as.As.impl [concrete] {} { // CHECK:STDOUT: %IntLiteral.ref: %IntLiteral.type = name_ref IntLiteral, file.%IntLiteral.decl [concrete = constants.%IntLiteral] // CHECK:STDOUT: %IntLiteral.call: init type = call %IntLiteral.ref() [concrete = Core.IntLiteral] // CHECK:STDOUT: %.loc23_17.1: type = value_of_initializer %IntLiteral.call [concrete = Core.IntLiteral] // CHECK:STDOUT: %.loc23_17.2: type = converted %IntLiteral.call, %.loc23_17.1 [concrete = Core.IntLiteral] // CHECK:STDOUT: %As.ref: %As.type.3c9 = name_ref As, file.%As.decl [concrete = constants.%As.generic] // CHECK:STDOUT: %.loc23_25: type = type_literal constants.%i32.builtin [concrete = constants.%i32.builtin] // CHECK:STDOUT: %As.type: type = facet_type <@As, @As(constants.%i32.builtin)> [concrete = constants.%As.type.1ed] // CHECK:STDOUT: } // CHECK:STDOUT: impl_decl @Core.IntLiteral.as.ImplicitAs.impl [concrete] {} { // CHECK:STDOUT: %IntLiteral.ref: %IntLiteral.type = name_ref IntLiteral, file.%IntLiteral.decl [concrete = constants.%IntLiteral] // CHECK:STDOUT: %IntLiteral.call: init type = call %IntLiteral.ref() [concrete = Core.IntLiteral] // CHECK:STDOUT: %.loc27_17.1: type = value_of_initializer %IntLiteral.call [concrete = Core.IntLiteral] // CHECK:STDOUT: %.loc27_17.2: type = converted %IntLiteral.call, %.loc27_17.1 [concrete = Core.IntLiteral] // CHECK:STDOUT: %ImplicitAs.ref: %ImplicitAs.type.595 = name_ref ImplicitAs, file.%ImplicitAs.decl [concrete = constants.%ImplicitAs.generic] // CHECK:STDOUT: %.loc27_33: type = type_literal constants.%i32.builtin [concrete = constants.%i32.builtin] // CHECK:STDOUT: %ImplicitAs.type: type = facet_type <@ImplicitAs, @ImplicitAs(constants.%i32.builtin)> [concrete = constants.%ImplicitAs.type.92b] // CHECK:STDOUT: } // CHECK:STDOUT: impl_decl @i32.builtin.as.ImplicitAs.impl [concrete] {} { // CHECK:STDOUT: %.loc31_6: type = type_literal constants.%i32.builtin [concrete = constants.%i32.builtin] // CHECK:STDOUT: %ImplicitAs.ref: %ImplicitAs.type.595 = name_ref ImplicitAs, file.%ImplicitAs.decl [concrete = constants.%ImplicitAs.generic] // CHECK:STDOUT: %IntLiteral.ref: %IntLiteral.type = name_ref IntLiteral, file.%IntLiteral.decl [concrete = constants.%IntLiteral] // CHECK:STDOUT: %IntLiteral.call: init type = call %IntLiteral.ref() [concrete = Core.IntLiteral] // CHECK:STDOUT: %.loc31_36.1: type = value_of_initializer %IntLiteral.call [concrete = Core.IntLiteral] // CHECK:STDOUT: %.loc31_36.2: type = converted %IntLiteral.call, %.loc31_36.1 [concrete = Core.IntLiteral] // CHECK:STDOUT: %ImplicitAs.type: type = facet_type <@ImplicitAs, @ImplicitAs(Core.IntLiteral)> [concrete = constants.%ImplicitAs.type.79c] // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic interface @AddWith(%T.loc7_19.2: type) { // CHECK:STDOUT: %T.loc7_19.1: type = symbolic_binding T, 0 [symbolic = %T.loc7_19.1 (constants.%T)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %AddWith.type: type = facet_type <@AddWith, @AddWith(%T.loc7_19.1)> [symbolic = %AddWith.type (constants.%AddWith.type.26b)] // CHECK:STDOUT: %Self.loc7_29.2: @AddWith.%AddWith.type (%AddWith.type.26b) = symbolic_binding Self, 1 [symbolic = %Self.loc7_29.2 (constants.%Self.a37)] // CHECK:STDOUT: // CHECK:STDOUT: interface { // CHECK:STDOUT: %Self.loc7_29.1: @AddWith.%AddWith.type (%AddWith.type.26b) = symbolic_binding Self, 1 [symbolic = %Self.loc7_29.2 (constants.%Self.a37)] // CHECK:STDOUT: %AddWith.WithSelf.decl = interface_with_self_decl @AddWith [concrete] // CHECK:STDOUT: // CHECK:STDOUT: !with Self: // CHECK:STDOUT: %AddWith.WithSelf.Op.decl: @AddWith.WithSelf.%AddWith.WithSelf.Op.type (%AddWith.WithSelf.Op.type.65a) = fn_decl @AddWith.WithSelf.Op [symbolic = @AddWith.WithSelf.%AddWith.WithSelf.Op (constants.%AddWith.WithSelf.Op.647)] { // CHECK:STDOUT: %self.patt: @AddWith.WithSelf.Op.%pattern_type (%pattern_type.1f3) = value_binding_pattern self [concrete] // CHECK:STDOUT: %self.param_patt: @AddWith.WithSelf.Op.%pattern_type (%pattern_type.1f3) = value_param_pattern %self.patt [concrete] // CHECK:STDOUT: %other.patt: @AddWith.WithSelf.Op.%pattern_type (%pattern_type.1f3) = value_binding_pattern other [concrete] // CHECK:STDOUT: %other.param_patt: @AddWith.WithSelf.Op.%pattern_type (%pattern_type.1f3) = value_param_pattern %other.patt [concrete] // CHECK:STDOUT: %return.patt: @AddWith.WithSelf.Op.%pattern_type (%pattern_type.1f3) = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: @AddWith.WithSelf.Op.%pattern_type (%pattern_type.1f3) = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %.loc8_37.2: @AddWith.WithSelf.Op.%AddWith.type (%AddWith.type.26b) = specific_constant @AddWith.%Self.loc7_29.1, @AddWith(constants.%T) [symbolic = %Self (constants.%Self.a37)] // CHECK:STDOUT: %Self.ref.loc8_37: @AddWith.WithSelf.Op.%AddWith.type (%AddWith.type.26b) = name_ref Self, %.loc8_37.2 [symbolic = %Self (constants.%Self.a37)] // CHECK:STDOUT: %Self.as_type.loc8_37: type = facet_access_type %Self.ref.loc8_37 [symbolic = %Self.binding.as_type (constants.%Self.binding.as_type.73e)] // CHECK:STDOUT: %.loc8_37.3: type = converted %Self.ref.loc8_37, %Self.as_type.loc8_37 [symbolic = %Self.binding.as_type (constants.%Self.binding.as_type.73e)] // CHECK:STDOUT: %.loc8_37.4: Core.Form = init_form %.loc8_37.3 [symbolic = %.loc8_37.1 (constants.%.409)] // CHECK:STDOUT: %self.param: @AddWith.WithSelf.Op.%Self.binding.as_type (%Self.binding.as_type.73e) = value_param call_param0 // CHECK:STDOUT: %.loc8_15.1: type = splice_block %.loc8_15.3 [symbolic = %Self.binding.as_type (constants.%Self.binding.as_type.73e)] { // CHECK:STDOUT: %.loc8_15.2: @AddWith.WithSelf.Op.%AddWith.type (%AddWith.type.26b) = specific_constant @AddWith.%Self.loc7_29.1, @AddWith(constants.%T) [symbolic = %Self (constants.%Self.a37)] // CHECK:STDOUT: %Self.ref.loc8_15: @AddWith.WithSelf.Op.%AddWith.type (%AddWith.type.26b) = name_ref Self, %.loc8_15.2 [symbolic = %Self (constants.%Self.a37)] // CHECK:STDOUT: %Self.as_type.loc8_15: type = facet_access_type %Self.ref.loc8_15 [symbolic = %Self.binding.as_type (constants.%Self.binding.as_type.73e)] // CHECK:STDOUT: %.loc8_15.3: type = converted %Self.ref.loc8_15, %Self.as_type.loc8_15 [symbolic = %Self.binding.as_type (constants.%Self.binding.as_type.73e)] // CHECK:STDOUT: } // CHECK:STDOUT: %self: @AddWith.WithSelf.Op.%Self.binding.as_type (%Self.binding.as_type.73e) = value_binding self, %self.param // CHECK:STDOUT: %other.param: @AddWith.WithSelf.Op.%Self.binding.as_type (%Self.binding.as_type.73e) = value_param call_param1 // CHECK:STDOUT: %.loc8_28.1: type = splice_block %.loc8_28.3 [symbolic = %Self.binding.as_type (constants.%Self.binding.as_type.73e)] { // CHECK:STDOUT: %.loc8_28.2: @AddWith.WithSelf.Op.%AddWith.type (%AddWith.type.26b) = specific_constant @AddWith.%Self.loc7_29.1, @AddWith(constants.%T) [symbolic = %Self (constants.%Self.a37)] // CHECK:STDOUT: %Self.ref.loc8_28: @AddWith.WithSelf.Op.%AddWith.type (%AddWith.type.26b) = name_ref Self, %.loc8_28.2 [symbolic = %Self (constants.%Self.a37)] // CHECK:STDOUT: %Self.as_type.loc8_28: type = facet_access_type %Self.ref.loc8_28 [symbolic = %Self.binding.as_type (constants.%Self.binding.as_type.73e)] // CHECK:STDOUT: %.loc8_28.3: type = converted %Self.ref.loc8_28, %Self.as_type.loc8_28 [symbolic = %Self.binding.as_type (constants.%Self.binding.as_type.73e)] // CHECK:STDOUT: } // CHECK:STDOUT: %other: @AddWith.WithSelf.Op.%Self.binding.as_type (%Self.binding.as_type.73e) = value_binding other, %other.param // CHECK:STDOUT: %return.param: ref @AddWith.WithSelf.Op.%Self.binding.as_type (%Self.binding.as_type.73e) = out_param call_param2 // CHECK:STDOUT: %return: ref @AddWith.WithSelf.Op.%Self.binding.as_type (%Self.binding.as_type.73e) = return_slot %return.param // CHECK:STDOUT: } // CHECK:STDOUT: %assoc0.loc8_41.1: @AddWith.WithSelf.%AddWith.assoc_type (%AddWith.assoc_type.5ad) = assoc_entity element0, %AddWith.WithSelf.Op.decl [symbolic = %assoc0.loc8_41.2 (constants.%assoc0.90d)] // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = %Self.loc7_29.1 // CHECK:STDOUT: .Op = @AddWith.WithSelf.%assoc0.loc8_41.1 // CHECK:STDOUT: witness = (@AddWith.WithSelf.%AddWith.WithSelf.Op.decl) // CHECK:STDOUT: // CHECK:STDOUT: !requires: // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic interface @As(%T.loc11_14.2: type) { // CHECK:STDOUT: %T.loc11_14.1: type = symbolic_binding T, 0 [symbolic = %T.loc11_14.1 (constants.%T)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %As.type: type = facet_type <@As, @As(%T.loc11_14.1)> [symbolic = %As.type (constants.%As.type.b54)] // CHECK:STDOUT: %Self.loc11_24.2: @As.%As.type (%As.type.b54) = symbolic_binding Self, 1 [symbolic = %Self.loc11_24.2 (constants.%Self.a8c)] // CHECK:STDOUT: // CHECK:STDOUT: interface { // CHECK:STDOUT: %Self.loc11_24.1: @As.%As.type (%As.type.b54) = symbolic_binding Self, 1 [symbolic = %Self.loc11_24.2 (constants.%Self.a8c)] // CHECK:STDOUT: %As.WithSelf.decl = interface_with_self_decl @As [concrete] // CHECK:STDOUT: // CHECK:STDOUT: !with Self: // CHECK:STDOUT: %As.WithSelf.Convert.decl: @As.WithSelf.%As.WithSelf.Convert.type (%As.WithSelf.Convert.type.aef) = fn_decl @As.WithSelf.Convert [symbolic = @As.WithSelf.%As.WithSelf.Convert (constants.%As.WithSelf.Convert.a95)] { // CHECK:STDOUT: %self.patt: @As.WithSelf.Convert.%pattern_type.loc12_14 (%pattern_type.24e) = value_binding_pattern self [concrete] // CHECK:STDOUT: %self.param_patt: @As.WithSelf.Convert.%pattern_type.loc12_14 (%pattern_type.24e) = value_param_pattern %self.patt [concrete] // CHECK:STDOUT: %return.patt: @As.WithSelf.Convert.%pattern_type.loc12_28 (%pattern_type.51d) = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: @As.WithSelf.Convert.%pattern_type.loc12_28 (%pattern_type.51d) = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %T.ref: type = name_ref T, @As.%T.loc11_14.2 [symbolic = %T (constants.%T)] // CHECK:STDOUT: %.loc12_31.2: Core.Form = init_form %T.ref [symbolic = %.loc12_31.1 (constants.%.184)] // CHECK:STDOUT: %self.param: @As.WithSelf.Convert.%Self.binding.as_type (%Self.binding.as_type.69d) = value_param call_param0 // CHECK:STDOUT: %.loc12_20.1: type = splice_block %.loc12_20.3 [symbolic = %Self.binding.as_type (constants.%Self.binding.as_type.69d)] { // CHECK:STDOUT: %.loc12_20.2: @As.WithSelf.Convert.%As.type (%As.type.b54) = specific_constant @As.%Self.loc11_24.1, @As(constants.%T) [symbolic = %Self (constants.%Self.a8c)] // CHECK:STDOUT: %Self.ref: @As.WithSelf.Convert.%As.type (%As.type.b54) = name_ref Self, %.loc12_20.2 [symbolic = %Self (constants.%Self.a8c)] // CHECK:STDOUT: %Self.as_type: type = facet_access_type %Self.ref [symbolic = %Self.binding.as_type (constants.%Self.binding.as_type.69d)] // CHECK:STDOUT: %.loc12_20.3: type = converted %Self.ref, %Self.as_type [symbolic = %Self.binding.as_type (constants.%Self.binding.as_type.69d)] // CHECK:STDOUT: } // CHECK:STDOUT: %self: @As.WithSelf.Convert.%Self.binding.as_type (%Self.binding.as_type.69d) = value_binding self, %self.param // CHECK:STDOUT: %return.param: ref @As.WithSelf.Convert.%T (%T) = out_param call_param1 // CHECK:STDOUT: %return: ref @As.WithSelf.Convert.%T (%T) = return_slot %return.param // CHECK:STDOUT: } // CHECK:STDOUT: %assoc0.loc12_32.1: @As.WithSelf.%As.assoc_type (%As.assoc_type.520) = assoc_entity element0, %As.WithSelf.Convert.decl [symbolic = %assoc0.loc12_32.2 (constants.%assoc0.84c)] // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = %Self.loc11_24.1 // CHECK:STDOUT: .T = // CHECK:STDOUT: .T = // CHECK:STDOUT: .Convert = @As.WithSelf.%assoc0.loc12_32.1 // CHECK:STDOUT: witness = (@As.WithSelf.%As.WithSelf.Convert.decl) // CHECK:STDOUT: // CHECK:STDOUT: !requires: // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic interface @ImplicitAs(%T.loc15_22.2: type) { // CHECK:STDOUT: %T.loc15_22.1: type = symbolic_binding T, 0 [symbolic = %T.loc15_22.1 (constants.%T)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %ImplicitAs.type: type = facet_type <@ImplicitAs, @ImplicitAs(%T.loc15_22.1)> [symbolic = %ImplicitAs.type (constants.%ImplicitAs.type.9fe)] // CHECK:STDOUT: %Self.loc15_32.2: @ImplicitAs.%ImplicitAs.type (%ImplicitAs.type.9fe) = symbolic_binding Self, 1 [symbolic = %Self.loc15_32.2 (constants.%Self.7c0)] // CHECK:STDOUT: // CHECK:STDOUT: interface { // CHECK:STDOUT: %Self.loc15_32.1: @ImplicitAs.%ImplicitAs.type (%ImplicitAs.type.9fe) = symbolic_binding Self, 1 [symbolic = %Self.loc15_32.2 (constants.%Self.7c0)] // CHECK:STDOUT: %ImplicitAs.WithSelf.decl = interface_with_self_decl @ImplicitAs [concrete] // CHECK:STDOUT: // CHECK:STDOUT: !with Self: // CHECK:STDOUT: %ImplicitAs.WithSelf.Convert.decl: @ImplicitAs.WithSelf.%ImplicitAs.WithSelf.Convert.type (%ImplicitAs.WithSelf.Convert.type.f6b) = fn_decl @ImplicitAs.WithSelf.Convert [symbolic = @ImplicitAs.WithSelf.%ImplicitAs.WithSelf.Convert (constants.%ImplicitAs.WithSelf.Convert.de0)] { // CHECK:STDOUT: %self.patt: @ImplicitAs.WithSelf.Convert.%pattern_type.loc16_14 (%pattern_type.8de) = value_binding_pattern self [concrete] // CHECK:STDOUT: %self.param_patt: @ImplicitAs.WithSelf.Convert.%pattern_type.loc16_14 (%pattern_type.8de) = value_param_pattern %self.patt [concrete] // CHECK:STDOUT: %return.patt: @ImplicitAs.WithSelf.Convert.%pattern_type.loc16_28 (%pattern_type.51d) = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: @ImplicitAs.WithSelf.Convert.%pattern_type.loc16_28 (%pattern_type.51d) = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %T.ref: type = name_ref T, @ImplicitAs.%T.loc15_22.2 [symbolic = %T (constants.%T)] // CHECK:STDOUT: %.loc16_31.2: Core.Form = init_form %T.ref [symbolic = %.loc16_31.1 (constants.%.184)] // CHECK:STDOUT: %self.param: @ImplicitAs.WithSelf.Convert.%Self.binding.as_type (%Self.binding.as_type.984) = value_param call_param0 // CHECK:STDOUT: %.loc16_20.1: type = splice_block %.loc16_20.3 [symbolic = %Self.binding.as_type (constants.%Self.binding.as_type.984)] { // CHECK:STDOUT: %.loc16_20.2: @ImplicitAs.WithSelf.Convert.%ImplicitAs.type (%ImplicitAs.type.9fe) = specific_constant @ImplicitAs.%Self.loc15_32.1, @ImplicitAs(constants.%T) [symbolic = %Self (constants.%Self.7c0)] // CHECK:STDOUT: %Self.ref: @ImplicitAs.WithSelf.Convert.%ImplicitAs.type (%ImplicitAs.type.9fe) = name_ref Self, %.loc16_20.2 [symbolic = %Self (constants.%Self.7c0)] // CHECK:STDOUT: %Self.as_type: type = facet_access_type %Self.ref [symbolic = %Self.binding.as_type (constants.%Self.binding.as_type.984)] // CHECK:STDOUT: %.loc16_20.3: type = converted %Self.ref, %Self.as_type [symbolic = %Self.binding.as_type (constants.%Self.binding.as_type.984)] // CHECK:STDOUT: } // CHECK:STDOUT: %self: @ImplicitAs.WithSelf.Convert.%Self.binding.as_type (%Self.binding.as_type.984) = value_binding self, %self.param // CHECK:STDOUT: %return.param: ref @ImplicitAs.WithSelf.Convert.%T (%T) = out_param call_param1 // CHECK:STDOUT: %return: ref @ImplicitAs.WithSelf.Convert.%T (%T) = return_slot %return.param // CHECK:STDOUT: } // CHECK:STDOUT: %assoc0.loc16_32.1: @ImplicitAs.WithSelf.%ImplicitAs.assoc_type (%ImplicitAs.assoc_type.b04) = assoc_entity element0, %ImplicitAs.WithSelf.Convert.decl [symbolic = %assoc0.loc16_32.2 (constants.%assoc0.361)] // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = %Self.loc15_32.1 // CHECK:STDOUT: .T = // CHECK:STDOUT: .T = // CHECK:STDOUT: .Convert = @ImplicitAs.WithSelf.%assoc0.loc16_32.1 // CHECK:STDOUT: witness = (@ImplicitAs.WithSelf.%ImplicitAs.WithSelf.Convert.decl) // CHECK:STDOUT: // CHECK:STDOUT: !requires: // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: impl @i32.builtin.as.AddWith.impl: %.loc19_6 as %AddWith.type { // CHECK:STDOUT: %i32.builtin.as.AddWith.impl.Op.decl: %i32.builtin.as.AddWith.impl.Op.type = fn_decl @i32.builtin.as.AddWith.impl.Op [concrete = constants.%i32.builtin.as.AddWith.impl.Op] { // CHECK:STDOUT: %self.patt: %pattern_type.956 = value_binding_pattern self [concrete] // CHECK:STDOUT: %self.param_patt: %pattern_type.956 = value_param_pattern %self.patt [concrete] // CHECK:STDOUT: %other.patt: %pattern_type.956 = value_binding_pattern other [concrete] // CHECK:STDOUT: %other.param_patt: %pattern_type.956 = value_param_pattern %other.patt [concrete] // CHECK:STDOUT: %return.patt: %pattern_type.956 = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: %pattern_type.956 = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %Self.ref.loc20_37: type = name_ref Self, @i32.builtin.as.AddWith.impl.%.loc19_6 [concrete = constants.%i32.builtin] // CHECK:STDOUT: %.loc20: Core.Form = init_form %Self.ref.loc20_37 [concrete = constants.%.9cb] // CHECK:STDOUT: %self.param: %i32.builtin = value_param call_param0 // CHECK:STDOUT: %Self.ref.loc20_15: type = name_ref Self, @i32.builtin.as.AddWith.impl.%.loc19_6 [concrete = constants.%i32.builtin] // CHECK:STDOUT: %self: %i32.builtin = value_binding self, %self.param // CHECK:STDOUT: %other.param: %i32.builtin = value_param call_param1 // CHECK:STDOUT: %Self.ref.loc20_28: type = name_ref Self, @i32.builtin.as.AddWith.impl.%.loc19_6 [concrete = constants.%i32.builtin] // CHECK:STDOUT: %other: %i32.builtin = value_binding other, %other.param // CHECK:STDOUT: %return.param: ref %i32.builtin = out_param call_param2 // CHECK:STDOUT: %return: ref %i32.builtin = return_slot %return.param // CHECK:STDOUT: } // CHECK:STDOUT: %AddWith.impl_witness_table = impl_witness_table (%i32.builtin.as.AddWith.impl.Op.decl), @i32.builtin.as.AddWith.impl [concrete] // CHECK:STDOUT: %AddWith.impl_witness: = impl_witness %AddWith.impl_witness_table [concrete = constants.%AddWith.impl_witness] // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Op = %i32.builtin.as.AddWith.impl.Op.decl // CHECK:STDOUT: witness = %AddWith.impl_witness // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: impl @Core.IntLiteral.as.As.impl: %.loc23_17.2 as %As.type { // CHECK:STDOUT: %Core.IntLiteral.as.As.impl.Convert.decl: %Core.IntLiteral.as.As.impl.Convert.type = fn_decl @Core.IntLiteral.as.As.impl.Convert [concrete = constants.%Core.IntLiteral.as.As.impl.Convert] { // CHECK:STDOUT: %self.patt: %pattern_type.dc0 = value_binding_pattern self [concrete] // CHECK:STDOUT: %self.param_patt: %pattern_type.dc0 = value_param_pattern %self.patt [concrete] // CHECK:STDOUT: %return.patt: %pattern_type.956 = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: %pattern_type.956 = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %.loc24_31.1: type = type_literal constants.%i32.builtin [concrete = constants.%i32.builtin] // CHECK:STDOUT: %.loc24_31.2: Core.Form = init_form %.loc24_31.1 [concrete = constants.%.9cb] // CHECK:STDOUT: %self.param: Core.IntLiteral = value_param call_param0 // CHECK:STDOUT: %Self.ref: type = name_ref Self, @Core.IntLiteral.as.As.impl.%.loc23_17.2 [concrete = Core.IntLiteral] // CHECK:STDOUT: %self: Core.IntLiteral = value_binding self, %self.param // CHECK:STDOUT: %return.param: ref %i32.builtin = out_param call_param1 // CHECK:STDOUT: %return: ref %i32.builtin = return_slot %return.param // CHECK:STDOUT: } // CHECK:STDOUT: %As.impl_witness_table = impl_witness_table (%Core.IntLiteral.as.As.impl.Convert.decl), @Core.IntLiteral.as.As.impl [concrete] // CHECK:STDOUT: %As.impl_witness: = impl_witness %As.impl_witness_table [concrete = constants.%As.impl_witness] // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Convert = %Core.IntLiteral.as.As.impl.Convert.decl // CHECK:STDOUT: witness = %As.impl_witness // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: impl @Core.IntLiteral.as.ImplicitAs.impl: %.loc27_17.2 as %ImplicitAs.type { // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.decl: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type = fn_decl @Core.IntLiteral.as.ImplicitAs.impl.Convert [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert] { // CHECK:STDOUT: %self.patt: %pattern_type.dc0 = value_binding_pattern self [concrete] // CHECK:STDOUT: %self.param_patt: %pattern_type.dc0 = value_param_pattern %self.patt [concrete] // CHECK:STDOUT: %return.patt: %pattern_type.956 = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: %pattern_type.956 = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %.loc28_31.1: type = type_literal constants.%i32.builtin [concrete = constants.%i32.builtin] // CHECK:STDOUT: %.loc28_31.2: Core.Form = init_form %.loc28_31.1 [concrete = constants.%.9cb] // CHECK:STDOUT: %self.param: Core.IntLiteral = value_param call_param0 // CHECK:STDOUT: %Self.ref: type = name_ref Self, @Core.IntLiteral.as.ImplicitAs.impl.%.loc27_17.2 [concrete = Core.IntLiteral] // CHECK:STDOUT: %self: Core.IntLiteral = value_binding self, %self.param // CHECK:STDOUT: %return.param: ref %i32.builtin = out_param call_param1 // CHECK:STDOUT: %return: ref %i32.builtin = return_slot %return.param // CHECK:STDOUT: } // CHECK:STDOUT: %ImplicitAs.impl_witness_table = impl_witness_table (%Core.IntLiteral.as.ImplicitAs.impl.Convert.decl), @Core.IntLiteral.as.ImplicitAs.impl [concrete] // CHECK:STDOUT: %ImplicitAs.impl_witness: = impl_witness %ImplicitAs.impl_witness_table [concrete = constants.%ImplicitAs.impl_witness.ccd] // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Convert = %Core.IntLiteral.as.ImplicitAs.impl.Convert.decl // CHECK:STDOUT: witness = %ImplicitAs.impl_witness // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: impl @i32.builtin.as.ImplicitAs.impl: %.loc31_6 as %ImplicitAs.type { // CHECK:STDOUT: %i32.builtin.as.ImplicitAs.impl.Convert.decl: %i32.builtin.as.ImplicitAs.impl.Convert.type = fn_decl @i32.builtin.as.ImplicitAs.impl.Convert [concrete = constants.%i32.builtin.as.ImplicitAs.impl.Convert] { // CHECK:STDOUT: %self.patt: %pattern_type.956 = value_binding_pattern self [concrete] // CHECK:STDOUT: %self.param_patt: %pattern_type.956 = value_param_pattern %self.patt [concrete] // CHECK:STDOUT: %return.patt: %pattern_type.dc0 = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: %pattern_type.dc0 = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %IntLiteral.ref: %IntLiteral.type = name_ref IntLiteral, file.%IntLiteral.decl [concrete = constants.%IntLiteral] // CHECK:STDOUT: %IntLiteral.call: init type = call %IntLiteral.ref() [concrete = Core.IntLiteral] // CHECK:STDOUT: %.loc32_42.1: type = value_of_initializer %IntLiteral.call [concrete = Core.IntLiteral] // CHECK:STDOUT: %.loc32_42.2: type = converted %IntLiteral.call, %.loc32_42.1 [concrete = Core.IntLiteral] // CHECK:STDOUT: %.loc32_42.3: Core.Form = init_form %.loc32_42.2 [concrete = constants.%.f7d] // CHECK:STDOUT: %self.param: %i32.builtin = value_param call_param0 // CHECK:STDOUT: %Self.ref: type = name_ref Self, @i32.builtin.as.ImplicitAs.impl.%.loc31_6 [concrete = constants.%i32.builtin] // CHECK:STDOUT: %self: %i32.builtin = value_binding self, %self.param // CHECK:STDOUT: %return.param: ref Core.IntLiteral = out_param call_param1 // CHECK:STDOUT: %return: ref Core.IntLiteral = return_slot %return.param // CHECK:STDOUT: } // CHECK:STDOUT: %ImplicitAs.impl_witness_table = impl_witness_table (%i32.builtin.as.ImplicitAs.impl.Convert.decl), @i32.builtin.as.ImplicitAs.impl [concrete] // CHECK:STDOUT: %ImplicitAs.impl_witness: = impl_witness %ImplicitAs.impl_witness_table [concrete = constants.%ImplicitAs.impl_witness.985] // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .IntLiteral = // CHECK:STDOUT: .Convert = %i32.builtin.as.ImplicitAs.impl.Convert.decl // CHECK:STDOUT: witness = %ImplicitAs.impl_witness // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @IntLiteral() -> out %return.param: type = "int_literal.make_type"; // CHECK:STDOUT: // CHECK:STDOUT: fn @Int(%N.param: Core.IntLiteral) -> out %return.param: type = "int.make_type_signed"; // CHECK:STDOUT: // CHECK:STDOUT: generic fn @AddWith.WithSelf.Op(@AddWith.%T.loc7_19.2: type, @AddWith.%Self.loc7_29.1: @AddWith.%AddWith.type (%AddWith.type.26b)) { // CHECK:STDOUT: %T: type = symbolic_binding T, 0 [symbolic = %T (constants.%T)] // CHECK:STDOUT: %AddWith.type: type = facet_type <@AddWith, @AddWith(%T)> [symbolic = %AddWith.type (constants.%AddWith.type.26b)] // CHECK:STDOUT: %Self: @AddWith.WithSelf.Op.%AddWith.type (%AddWith.type.26b) = symbolic_binding Self, 1 [symbolic = %Self (constants.%Self.a37)] // CHECK:STDOUT: %Self.binding.as_type: type = symbolic_binding_type Self, 1, %Self [symbolic = %Self.binding.as_type (constants.%Self.binding.as_type.73e)] // CHECK:STDOUT: %pattern_type: type = pattern_type %Self.binding.as_type [symbolic = %pattern_type (constants.%pattern_type.1f3)] // CHECK:STDOUT: %.loc8_37.1: Core.Form = init_form %Self.binding.as_type [symbolic = %.loc8_37.1 (constants.%.409)] // CHECK:STDOUT: // CHECK:STDOUT: fn(%self.param: @AddWith.WithSelf.Op.%Self.binding.as_type (%Self.binding.as_type.73e), %other.param: @AddWith.WithSelf.Op.%Self.binding.as_type (%Self.binding.as_type.73e)) -> out %return.param: @AddWith.WithSelf.Op.%Self.binding.as_type (%Self.binding.as_type.73e); // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic fn @As.WithSelf.Convert(@As.%T.loc11_14.2: type, @As.%Self.loc11_24.1: @As.%As.type (%As.type.b54)) { // CHECK:STDOUT: %T: type = symbolic_binding T, 0 [symbolic = %T (constants.%T)] // CHECK:STDOUT: %As.type: type = facet_type <@As, @As(%T)> [symbolic = %As.type (constants.%As.type.b54)] // CHECK:STDOUT: %Self: @As.WithSelf.Convert.%As.type (%As.type.b54) = symbolic_binding Self, 1 [symbolic = %Self (constants.%Self.a8c)] // CHECK:STDOUT: %Self.binding.as_type: type = symbolic_binding_type Self, 1, %Self [symbolic = %Self.binding.as_type (constants.%Self.binding.as_type.69d)] // CHECK:STDOUT: %pattern_type.loc12_14: type = pattern_type %Self.binding.as_type [symbolic = %pattern_type.loc12_14 (constants.%pattern_type.24e)] // CHECK:STDOUT: %.loc12_31.1: Core.Form = init_form %T [symbolic = %.loc12_31.1 (constants.%.184)] // CHECK:STDOUT: %pattern_type.loc12_28: type = pattern_type %T [symbolic = %pattern_type.loc12_28 (constants.%pattern_type.51d)] // CHECK:STDOUT: // CHECK:STDOUT: fn(%self.param: @As.WithSelf.Convert.%Self.binding.as_type (%Self.binding.as_type.69d)) -> out %return.param: @As.WithSelf.Convert.%T (%T); // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic fn @ImplicitAs.WithSelf.Convert(@ImplicitAs.%T.loc15_22.2: type, @ImplicitAs.%Self.loc15_32.1: @ImplicitAs.%ImplicitAs.type (%ImplicitAs.type.9fe)) { // CHECK:STDOUT: %T: type = symbolic_binding T, 0 [symbolic = %T (constants.%T)] // CHECK:STDOUT: %ImplicitAs.type: type = facet_type <@ImplicitAs, @ImplicitAs(%T)> [symbolic = %ImplicitAs.type (constants.%ImplicitAs.type.9fe)] // CHECK:STDOUT: %Self: @ImplicitAs.WithSelf.Convert.%ImplicitAs.type (%ImplicitAs.type.9fe) = symbolic_binding Self, 1 [symbolic = %Self (constants.%Self.7c0)] // CHECK:STDOUT: %Self.binding.as_type: type = symbolic_binding_type Self, 1, %Self [symbolic = %Self.binding.as_type (constants.%Self.binding.as_type.984)] // CHECK:STDOUT: %pattern_type.loc16_14: type = pattern_type %Self.binding.as_type [symbolic = %pattern_type.loc16_14 (constants.%pattern_type.8de)] // CHECK:STDOUT: %.loc16_31.1: Core.Form = init_form %T [symbolic = %.loc16_31.1 (constants.%.184)] // CHECK:STDOUT: %pattern_type.loc16_28: type = pattern_type %T [symbolic = %pattern_type.loc16_28 (constants.%pattern_type.51d)] // CHECK:STDOUT: // CHECK:STDOUT: fn(%self.param: @ImplicitAs.WithSelf.Convert.%Self.binding.as_type (%Self.binding.as_type.984)) -> out %return.param: @ImplicitAs.WithSelf.Convert.%T (%T); // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @i32.builtin.as.AddWith.impl.Op(%self.param: %i32.builtin, %other.param: %i32.builtin) -> out %return.param: %i32.builtin = "int.sadd"; // CHECK:STDOUT: // CHECK:STDOUT: fn @Core.IntLiteral.as.As.impl.Convert(%self.param: Core.IntLiteral) -> out %return.param: %i32.builtin = "int.convert_checked"; // CHECK:STDOUT: // CHECK:STDOUT: fn @Core.IntLiteral.as.ImplicitAs.impl.Convert(%self.param: Core.IntLiteral) -> out %return.param: %i32.builtin = "int.convert_checked"; // CHECK:STDOUT: // CHECK:STDOUT: fn @i32.builtin.as.ImplicitAs.impl.Convert(%self.param: %i32.builtin) -> out %return.param: Core.IntLiteral = "int.convert_checked"; // CHECK:STDOUT: // CHECK:STDOUT: specific @AddWith(constants.%T) { // CHECK:STDOUT: %T.loc7_19.1 => constants.%T // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @AddWith.WithSelf(constants.%T, constants.%Self.a37) {} // CHECK:STDOUT: // CHECK:STDOUT: specific @AddWith.WithSelf.Op(constants.%T, constants.%Self.a37) { // CHECK:STDOUT: %T => constants.%T // CHECK:STDOUT: %AddWith.type => constants.%AddWith.type.26b // CHECK:STDOUT: %Self => constants.%Self.a37 // CHECK:STDOUT: %Self.binding.as_type => constants.%Self.binding.as_type.73e // CHECK:STDOUT: %pattern_type => constants.%pattern_type.1f3 // CHECK:STDOUT: %.loc8_37.1 => constants.%.409 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @As(constants.%T) { // CHECK:STDOUT: %T.loc11_14.1 => constants.%T // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @As.WithSelf(constants.%T, constants.%Self.a8c) {} // CHECK:STDOUT: // CHECK:STDOUT: specific @As.WithSelf.Convert(constants.%T, constants.%Self.a8c) { // CHECK:STDOUT: %T => constants.%T // CHECK:STDOUT: %As.type => constants.%As.type.b54 // CHECK:STDOUT: %Self => constants.%Self.a8c // CHECK:STDOUT: %Self.binding.as_type => constants.%Self.binding.as_type.69d // CHECK:STDOUT: %pattern_type.loc12_14 => constants.%pattern_type.24e // CHECK:STDOUT: %.loc12_31.1 => constants.%.184 // CHECK:STDOUT: %pattern_type.loc12_28 => constants.%pattern_type.51d // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @ImplicitAs(constants.%T) { // CHECK:STDOUT: %T.loc15_22.1 => constants.%T // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @ImplicitAs.WithSelf(constants.%T, constants.%Self.7c0) {} // CHECK:STDOUT: // CHECK:STDOUT: specific @ImplicitAs.WithSelf.Convert(constants.%T, constants.%Self.7c0) { // CHECK:STDOUT: %T => constants.%T // CHECK:STDOUT: %ImplicitAs.type => constants.%ImplicitAs.type.9fe // CHECK:STDOUT: %Self => constants.%Self.7c0 // CHECK:STDOUT: %Self.binding.as_type => constants.%Self.binding.as_type.984 // CHECK:STDOUT: %pattern_type.loc16_14 => constants.%pattern_type.8de // CHECK:STDOUT: %.loc16_31.1 => constants.%.184 // CHECK:STDOUT: %pattern_type.loc16_28 => constants.%pattern_type.51d // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @AddWith(constants.%i32.builtin) { // CHECK:STDOUT: %T.loc7_19.1 => constants.%i32.builtin // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %AddWith.type => constants.%AddWith.type.aed // CHECK:STDOUT: %Self.loc7_29.2 => constants.%Self.aad // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @AddWith.WithSelf(constants.%i32.builtin, constants.%Self.a37) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: %T => constants.%i32.builtin // CHECK:STDOUT: %AddWith.type => constants.%AddWith.type.aed // CHECK:STDOUT: %Self => constants.%Self.a37 // CHECK:STDOUT: %AddWith.WithSelf.Op.type => constants.%AddWith.WithSelf.Op.type.479 // CHECK:STDOUT: %AddWith.WithSelf.Op => constants.%AddWith.WithSelf.Op.8bc // CHECK:STDOUT: %AddWith.assoc_type => constants.%AddWith.assoc_type.97c // CHECK:STDOUT: %assoc0.loc8_41.2 => constants.%assoc0.4d8 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @AddWith.WithSelf(constants.%i32.builtin, constants.%AddWith.facet) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: %T => constants.%i32.builtin // CHECK:STDOUT: %AddWith.type => constants.%AddWith.type.aed // CHECK:STDOUT: %Self => constants.%AddWith.facet // CHECK:STDOUT: %AddWith.WithSelf.Op.type => constants.%AddWith.WithSelf.Op.type.493 // CHECK:STDOUT: %AddWith.WithSelf.Op => constants.%AddWith.WithSelf.Op.d6a // CHECK:STDOUT: %AddWith.assoc_type => constants.%AddWith.assoc_type.97c // CHECK:STDOUT: %assoc0.loc8_41.2 => constants.%assoc0.4d8 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @AddWith.WithSelf.Op(constants.%i32.builtin, constants.%AddWith.facet) { // CHECK:STDOUT: %T => constants.%i32.builtin // CHECK:STDOUT: %AddWith.type => constants.%AddWith.type.aed // CHECK:STDOUT: %Self => constants.%AddWith.facet // CHECK:STDOUT: %Self.binding.as_type => constants.%i32.builtin // CHECK:STDOUT: %pattern_type => constants.%pattern_type.956 // CHECK:STDOUT: %.loc8_37.1 => constants.%.9cb // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @As(constants.%i32.builtin) { // CHECK:STDOUT: %T.loc11_14.1 => constants.%i32.builtin // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %As.type => constants.%As.type.1ed // CHECK:STDOUT: %Self.loc11_24.2 => constants.%Self.037 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @As.WithSelf(constants.%i32.builtin, constants.%Self.a8c) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: %T => constants.%i32.builtin // CHECK:STDOUT: %As.type => constants.%As.type.1ed // CHECK:STDOUT: %Self => constants.%Self.a8c // CHECK:STDOUT: %As.WithSelf.Convert.type => constants.%As.WithSelf.Convert.type.a02 // CHECK:STDOUT: %As.WithSelf.Convert => constants.%As.WithSelf.Convert.98f // CHECK:STDOUT: %As.assoc_type => constants.%As.assoc_type.c44 // CHECK:STDOUT: %assoc0.loc12_32.2 => constants.%assoc0.43d // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @As.WithSelf(constants.%i32.builtin, constants.%As.facet) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: %T => constants.%i32.builtin // CHECK:STDOUT: %As.type => constants.%As.type.1ed // CHECK:STDOUT: %Self => constants.%As.facet // CHECK:STDOUT: %As.WithSelf.Convert.type => constants.%As.WithSelf.Convert.type.204 // CHECK:STDOUT: %As.WithSelf.Convert => constants.%As.WithSelf.Convert.24c // CHECK:STDOUT: %As.assoc_type => constants.%As.assoc_type.c44 // CHECK:STDOUT: %assoc0.loc12_32.2 => constants.%assoc0.43d // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @As.WithSelf.Convert(constants.%i32.builtin, constants.%As.facet) { // CHECK:STDOUT: %T => constants.%i32.builtin // CHECK:STDOUT: %As.type => constants.%As.type.1ed // CHECK:STDOUT: %Self => constants.%As.facet // CHECK:STDOUT: %Self.binding.as_type => Core.IntLiteral // CHECK:STDOUT: %pattern_type.loc12_14 => constants.%pattern_type.dc0 // CHECK:STDOUT: %.loc12_31.1 => constants.%.9cb // CHECK:STDOUT: %pattern_type.loc12_28 => constants.%pattern_type.956 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @ImplicitAs(constants.%i32.builtin) { // CHECK:STDOUT: %T.loc15_22.1 => constants.%i32.builtin // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %ImplicitAs.type => constants.%ImplicitAs.type.92b // CHECK:STDOUT: %Self.loc15_32.2 => constants.%Self.597 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @ImplicitAs.WithSelf(constants.%i32.builtin, constants.%Self.7c0) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: %T => constants.%i32.builtin // CHECK:STDOUT: %ImplicitAs.type => constants.%ImplicitAs.type.92b // CHECK:STDOUT: %Self => constants.%Self.7c0 // CHECK:STDOUT: %ImplicitAs.WithSelf.Convert.type => constants.%ImplicitAs.WithSelf.Convert.type.d4c // CHECK:STDOUT: %ImplicitAs.WithSelf.Convert => constants.%ImplicitAs.WithSelf.Convert.086 // CHECK:STDOUT: %ImplicitAs.assoc_type => constants.%ImplicitAs.assoc_type.c3f // CHECK:STDOUT: %assoc0.loc16_32.2 => constants.%assoc0.124 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @ImplicitAs.WithSelf(constants.%i32.builtin, constants.%ImplicitAs.facet.b36) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: %T => constants.%i32.builtin // CHECK:STDOUT: %ImplicitAs.type => constants.%ImplicitAs.type.92b // CHECK:STDOUT: %Self => constants.%ImplicitAs.facet.b36 // CHECK:STDOUT: %ImplicitAs.WithSelf.Convert.type => constants.%ImplicitAs.WithSelf.Convert.type.07c // CHECK:STDOUT: %ImplicitAs.WithSelf.Convert => constants.%ImplicitAs.WithSelf.Convert.a38 // CHECK:STDOUT: %ImplicitAs.assoc_type => constants.%ImplicitAs.assoc_type.c3f // CHECK:STDOUT: %assoc0.loc16_32.2 => constants.%assoc0.124 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @ImplicitAs.WithSelf.Convert(constants.%i32.builtin, constants.%ImplicitAs.facet.b36) { // CHECK:STDOUT: %T => constants.%i32.builtin // CHECK:STDOUT: %ImplicitAs.type => constants.%ImplicitAs.type.92b // CHECK:STDOUT: %Self => constants.%ImplicitAs.facet.b36 // CHECK:STDOUT: %Self.binding.as_type => Core.IntLiteral // CHECK:STDOUT: %pattern_type.loc16_14 => constants.%pattern_type.dc0 // CHECK:STDOUT: %.loc16_31.1 => constants.%.9cb // CHECK:STDOUT: %pattern_type.loc16_28 => constants.%pattern_type.956 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @ImplicitAs(Core.IntLiteral) { // CHECK:STDOUT: %T.loc15_22.1 => Core.IntLiteral // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %ImplicitAs.type => constants.%ImplicitAs.type.79c // CHECK:STDOUT: %Self.loc15_32.2 => constants.%Self.55f // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @ImplicitAs.WithSelf(Core.IntLiteral, constants.%Self.7c0) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: %T => Core.IntLiteral // CHECK:STDOUT: %ImplicitAs.type => constants.%ImplicitAs.type.79c // CHECK:STDOUT: %Self => constants.%Self.7c0 // CHECK:STDOUT: %ImplicitAs.WithSelf.Convert.type => constants.%ImplicitAs.WithSelf.Convert.type.25a // CHECK:STDOUT: %ImplicitAs.WithSelf.Convert => constants.%ImplicitAs.WithSelf.Convert.1ae // CHECK:STDOUT: %ImplicitAs.assoc_type => constants.%ImplicitAs.assoc_type.793 // CHECK:STDOUT: %assoc0.loc16_32.2 => constants.%assoc0.e18 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @ImplicitAs.WithSelf(Core.IntLiteral, constants.%ImplicitAs.facet.c5c) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: %T => Core.IntLiteral // CHECK:STDOUT: %ImplicitAs.type => constants.%ImplicitAs.type.79c // CHECK:STDOUT: %Self => constants.%ImplicitAs.facet.c5c // CHECK:STDOUT: %ImplicitAs.WithSelf.Convert.type => constants.%ImplicitAs.WithSelf.Convert.type.7aa // CHECK:STDOUT: %ImplicitAs.WithSelf.Convert => constants.%ImplicitAs.WithSelf.Convert.090 // CHECK:STDOUT: %ImplicitAs.assoc_type => constants.%ImplicitAs.assoc_type.793 // CHECK:STDOUT: %assoc0.loc16_32.2 => constants.%assoc0.e18 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @ImplicitAs.WithSelf.Convert(Core.IntLiteral, constants.%ImplicitAs.facet.c5c) { // CHECK:STDOUT: %T => Core.IntLiteral // CHECK:STDOUT: %ImplicitAs.type => constants.%ImplicitAs.type.79c // CHECK:STDOUT: %Self => constants.%ImplicitAs.facet.c5c // CHECK:STDOUT: %Self.binding.as_type => constants.%i32.builtin // CHECK:STDOUT: %pattern_type.loc16_14 => constants.%pattern_type.956 // CHECK:STDOUT: %.loc16_31.1 => constants.%.f7d // CHECK:STDOUT: %pattern_type.loc16_28 => constants.%pattern_type.dc0 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- user.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [concrete] // CHECK:STDOUT: %Int.type: type = fn_type @Int [concrete] // CHECK:STDOUT: %Int: %Int.type = struct_value () [concrete] // CHECK:STDOUT: %i32.builtin: type = int_type signed, %int_32 [concrete] // CHECK:STDOUT: %int_1.5b8: Core.IntLiteral = int_value 1 [concrete] // CHECK:STDOUT: %As.type.90f: type = generic_interface_type @As [concrete] // CHECK:STDOUT: %As.generic: %As.type.90f = struct_value () [concrete] // CHECK:STDOUT: %T: type = symbolic_binding T, 0 [symbolic] // CHECK:STDOUT: %As.type.223: type = facet_type <@As, @As(%T)> [symbolic] // CHECK:STDOUT: %Self.2d0: %As.type.223 = symbolic_binding Self, 1 [symbolic] // CHECK:STDOUT: %As.assoc_type.752: type = assoc_entity_type @As, @As(%T) [symbolic] // CHECK:STDOUT: %As.WithSelf.Convert.type.bb7: type = fn_type @As.WithSelf.Convert, @As.WithSelf(%T, %Self.2d0) [symbolic] // CHECK:STDOUT: %As.WithSelf.Convert.923: %As.WithSelf.Convert.type.bb7 = struct_value () [symbolic] // CHECK:STDOUT: %pattern_type.51d: type = pattern_type %T [symbolic] // CHECK:STDOUT: %Self.binding.as_type.297: type = symbolic_binding_type Self, 1, %Self.2d0 [symbolic] // CHECK:STDOUT: %pattern_type.760: type = pattern_type %Self.binding.as_type.297 [symbolic] // CHECK:STDOUT: %.184: Core.Form = init_form %T [symbolic] // CHECK:STDOUT: %As.type.ffe: type = facet_type <@As, @As(%i32.builtin)> [concrete] // CHECK:STDOUT: %Self.af0: %As.type.ffe = symbolic_binding Self, 1 [symbolic] // CHECK:STDOUT: %As.WithSelf.Convert.type.06b: type = fn_type @As.WithSelf.Convert, @As.WithSelf(%i32.builtin, %Self.2d0) [symbolic] // CHECK:STDOUT: %As.WithSelf.Convert.b84: %As.WithSelf.Convert.type.06b = struct_value () [symbolic] // CHECK:STDOUT: %As.assoc_type.bc2: type = assoc_entity_type @As, @As(%i32.builtin) [concrete] // CHECK:STDOUT: %assoc0.5d8: %As.assoc_type.bc2 = assoc_entity element0, imports.%Core.import_ref.0ec [concrete] // CHECK:STDOUT: %assoc0.2f9: %As.assoc_type.752 = assoc_entity element0, imports.%Core.import_ref.4c6 [symbolic] // CHECK:STDOUT: %As.impl_witness: = impl_witness imports.%As.impl_witness_table [concrete] // CHECK:STDOUT: %As.facet: %As.type.ffe = facet_value Core.IntLiteral, (%As.impl_witness) [concrete] // CHECK:STDOUT: %As.WithSelf.Convert.type.b07: type = fn_type @As.WithSelf.Convert, @As.WithSelf(%i32.builtin, %As.facet) [concrete] // CHECK:STDOUT: %As.WithSelf.Convert.2d0: %As.WithSelf.Convert.type.b07 = struct_value () [concrete] // CHECK:STDOUT: %.e3a: type = fn_type_with_self_type %As.WithSelf.Convert.type.b07, %As.facet [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.As.impl.Convert.type: type = fn_type @Core.IntLiteral.as.As.impl.Convert [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.As.impl.Convert: %Core.IntLiteral.as.As.impl.Convert.type = struct_value () [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.As.impl.Convert.bound.4b3: = bound_method %int_1.5b8, %Core.IntLiteral.as.As.impl.Convert [concrete] // CHECK:STDOUT: %int_1.f38: %i32.builtin = int_value 1 [concrete] // CHECK:STDOUT: %int_2.ecc: Core.IntLiteral = int_value 2 [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.As.impl.Convert.bound.7b2: = bound_method %int_2.ecc, %Core.IntLiteral.as.As.impl.Convert [concrete] // CHECK:STDOUT: %int_2.5a1: %i32.builtin = int_value 2 [concrete] // CHECK:STDOUT: %AddWith.type.e05: type = generic_interface_type @AddWith [concrete] // CHECK:STDOUT: %AddWith.generic: %AddWith.type.e05 = struct_value () [concrete] // CHECK:STDOUT: %AddWith.type.6d9: type = facet_type <@AddWith, @AddWith(%T)> [symbolic] // CHECK:STDOUT: %Self.b7c: %AddWith.type.6d9 = symbolic_binding Self, 1 [symbolic] // CHECK:STDOUT: %AddWith.assoc_type.b6a: type = assoc_entity_type @AddWith, @AddWith(%T) [symbolic] // CHECK:STDOUT: %AddWith.WithSelf.Op.type.08f: type = fn_type @AddWith.WithSelf.Op, @AddWith.WithSelf(%T, %Self.b7c) [symbolic] // CHECK:STDOUT: %AddWith.WithSelf.Op.fb8: %AddWith.WithSelf.Op.type.08f = struct_value () [symbolic] // CHECK:STDOUT: %Self.binding.as_type.14b: type = symbolic_binding_type Self, 1, %Self.b7c [symbolic] // CHECK:STDOUT: %pattern_type.259: type = pattern_type %Self.binding.as_type.14b [symbolic] // CHECK:STDOUT: %.730: Core.Form = init_form %Self.binding.as_type.14b [symbolic] // CHECK:STDOUT: %AddWith.type.46d: type = facet_type <@AddWith, @AddWith(%i32.builtin)> [concrete] // CHECK:STDOUT: %Self.365: %AddWith.type.46d = symbolic_binding Self, 1 [symbolic] // CHECK:STDOUT: %AddWith.WithSelf.Op.type.de8: type = fn_type @AddWith.WithSelf.Op, @AddWith.WithSelf(%i32.builtin, %Self.b7c) [symbolic] // CHECK:STDOUT: %AddWith.WithSelf.Op.31e: %AddWith.WithSelf.Op.type.de8 = struct_value () [symbolic] // CHECK:STDOUT: %AddWith.assoc_type.dff: type = assoc_entity_type @AddWith, @AddWith(%i32.builtin) [concrete] // CHECK:STDOUT: %assoc0.6d5: %AddWith.assoc_type.dff = assoc_entity element0, imports.%Core.import_ref.1b1 [concrete] // CHECK:STDOUT: %assoc0.20f: %AddWith.assoc_type.b6a = assoc_entity element0, imports.%Core.import_ref.a84 [symbolic] // CHECK:STDOUT: %AddWith.impl_witness: = impl_witness imports.%AddWith.impl_witness_table [concrete] // CHECK:STDOUT: %AddWith.facet: %AddWith.type.46d = facet_value %i32.builtin, (%AddWith.impl_witness) [concrete] // CHECK:STDOUT: %AddWith.WithSelf.Op.type.78e: type = fn_type @AddWith.WithSelf.Op, @AddWith.WithSelf(%i32.builtin, %AddWith.facet) [concrete] // CHECK:STDOUT: %AddWith.WithSelf.Op.d4d: %AddWith.WithSelf.Op.type.78e = struct_value () [concrete] // CHECK:STDOUT: %.0ff: type = fn_type_with_self_type %AddWith.WithSelf.Op.type.78e, %AddWith.facet [concrete] // CHECK:STDOUT: %i32.builtin.as.AddWith.impl.Op.type: type = fn_type @i32.builtin.as.AddWith.impl.Op [concrete] // CHECK:STDOUT: %i32.builtin.as.AddWith.impl.Op: %i32.builtin.as.AddWith.impl.Op.type = struct_value () [concrete] // CHECK:STDOUT: %i32.builtin.as.AddWith.impl.Op.bound.abd: = bound_method %int_1.f38, %i32.builtin.as.AddWith.impl.Op [concrete] // CHECK:STDOUT: %int_3.a0f: %i32.builtin = int_value 3 [concrete] // CHECK:STDOUT: %ImplicitAs.type.cc7: type = generic_interface_type @ImplicitAs [concrete] // CHECK:STDOUT: %ImplicitAs.generic: %ImplicitAs.type.cc7 = struct_value () [concrete] // CHECK:STDOUT: %ImplicitAs.type.031: type = facet_type <@ImplicitAs, @ImplicitAs(%T)> [symbolic] // CHECK:STDOUT: %Self.738: %ImplicitAs.type.031 = symbolic_binding Self, 1 [symbolic] // CHECK:STDOUT: %ImplicitAs.assoc_type.ff3: type = assoc_entity_type @ImplicitAs, @ImplicitAs(%T) [symbolic] // CHECK:STDOUT: %ImplicitAs.WithSelf.Convert.type.b3a: type = fn_type @ImplicitAs.WithSelf.Convert, @ImplicitAs.WithSelf(%T, %Self.738) [symbolic] // CHECK:STDOUT: %ImplicitAs.WithSelf.Convert.1de: %ImplicitAs.WithSelf.Convert.type.b3a = struct_value () [symbolic] // CHECK:STDOUT: %Self.binding.as_type.a44: type = symbolic_binding_type Self, 1, %Self.738 [symbolic] // CHECK:STDOUT: %pattern_type.e9a: type = pattern_type %Self.binding.as_type.a44 [symbolic] // CHECK:STDOUT: %ImplicitAs.type.139: type = facet_type <@ImplicitAs, @ImplicitAs(Core.IntLiteral)> [concrete] // CHECK:STDOUT: %Self.cce: %ImplicitAs.type.139 = symbolic_binding Self, 1 [symbolic] // CHECK:STDOUT: %ImplicitAs.WithSelf.Convert.type.577: type = fn_type @ImplicitAs.WithSelf.Convert, @ImplicitAs.WithSelf(Core.IntLiteral, %Self.738) [symbolic] // CHECK:STDOUT: %ImplicitAs.WithSelf.Convert.f0a: %ImplicitAs.WithSelf.Convert.type.577 = struct_value () [symbolic] // CHECK:STDOUT: %ImplicitAs.assoc_type.959: type = assoc_entity_type @ImplicitAs, @ImplicitAs(Core.IntLiteral) [concrete] // CHECK:STDOUT: %assoc0.461: %ImplicitAs.assoc_type.959 = assoc_entity element0, imports.%Core.import_ref.201 [concrete] // CHECK:STDOUT: %assoc0.843: %ImplicitAs.assoc_type.ff3 = assoc_entity element0, imports.%Core.import_ref.cc1 [symbolic] // CHECK:STDOUT: %ImplicitAs.type.78a: type = facet_type <@ImplicitAs, @ImplicitAs(%i32.builtin)> [concrete] // CHECK:STDOUT: %Self.dd8: %ImplicitAs.type.78a = symbolic_binding Self, 1 [symbolic] // CHECK:STDOUT: %ImplicitAs.impl_witness.304: = impl_witness imports.%ImplicitAs.impl_witness_table.267 [concrete] // CHECK:STDOUT: %ImplicitAs.facet.7f3: %ImplicitAs.type.139 = facet_value %i32.builtin, (%ImplicitAs.impl_witness.304) [concrete] // CHECK:STDOUT: %ImplicitAs.WithSelf.Convert.type.e78: type = fn_type @ImplicitAs.WithSelf.Convert, @ImplicitAs.WithSelf(Core.IntLiteral, %ImplicitAs.facet.7f3) [concrete] // CHECK:STDOUT: %ImplicitAs.WithSelf.Convert.e56: %ImplicitAs.WithSelf.Convert.type.e78 = struct_value () [concrete] // CHECK:STDOUT: %.626: type = fn_type_with_self_type %ImplicitAs.WithSelf.Convert.type.e78, %ImplicitAs.facet.7f3 [concrete] // CHECK:STDOUT: %i32.builtin.as.ImplicitAs.impl.Convert.type: type = fn_type @i32.builtin.as.ImplicitAs.impl.Convert [concrete] // CHECK:STDOUT: %i32.builtin.as.ImplicitAs.impl.Convert: %i32.builtin.as.ImplicitAs.impl.Convert.type = struct_value () [concrete] // CHECK:STDOUT: %i32.builtin.as.ImplicitAs.impl.Convert.bound: = bound_method %int_3.a0f, %i32.builtin.as.ImplicitAs.impl.Convert [concrete] // CHECK:STDOUT: %int_3.1ba: Core.IntLiteral = int_value 3 [concrete] // CHECK:STDOUT: %array_type: type = array_type %int_3.1ba, %i32.builtin [concrete] // CHECK:STDOUT: %pattern_type.9e2: type = pattern_type %array_type [concrete] // CHECK:STDOUT: %int_4.0c1: Core.IntLiteral = int_value 4 [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.As.impl.Convert.bound.2b2: = bound_method %int_3.1ba, %Core.IntLiteral.as.As.impl.Convert [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.As.impl.Convert.bound.2d0: = bound_method %int_4.0c1, %Core.IntLiteral.as.As.impl.Convert [concrete] // CHECK:STDOUT: %int_4.4f1: %i32.builtin = int_value 4 [concrete] // CHECK:STDOUT: %i32.builtin.as.AddWith.impl.Op.bound.bf8: = bound_method %int_3.a0f, %i32.builtin.as.AddWith.impl.Op [concrete] // CHECK:STDOUT: %int_7: %i32.builtin = int_value 7 [concrete] // CHECK:STDOUT: %tuple.type: type = tuple_type (Core.IntLiteral, Core.IntLiteral, %i32.builtin) [concrete] // CHECK:STDOUT: %tuple: %tuple.type = tuple_value (%int_3.1ba, %int_4.0c1, %int_7) [concrete] // CHECK:STDOUT: %int_0: Core.IntLiteral = int_value 0 [concrete] // CHECK:STDOUT: %ImplicitAs.WithSelf.Convert.type.ea9: type = fn_type @ImplicitAs.WithSelf.Convert, @ImplicitAs.WithSelf(%i32.builtin, %Self.738) [symbolic] // CHECK:STDOUT: %ImplicitAs.WithSelf.Convert.149: %ImplicitAs.WithSelf.Convert.type.ea9 = struct_value () [symbolic] // CHECK:STDOUT: %ImplicitAs.assoc_type.398: type = assoc_entity_type @ImplicitAs, @ImplicitAs(%i32.builtin) [concrete] // CHECK:STDOUT: %assoc0.4a0: %ImplicitAs.assoc_type.398 = assoc_entity element0, imports.%Core.import_ref.201 [concrete] // CHECK:STDOUT: %ImplicitAs.impl_witness.586: = impl_witness imports.%ImplicitAs.impl_witness_table.7a2 [concrete] // CHECK:STDOUT: %ImplicitAs.facet.fa4: %ImplicitAs.type.78a = facet_value Core.IntLiteral, (%ImplicitAs.impl_witness.586) [concrete] // CHECK:STDOUT: %ImplicitAs.WithSelf.Convert.type.66f: type = fn_type @ImplicitAs.WithSelf.Convert, @ImplicitAs.WithSelf(%i32.builtin, %ImplicitAs.facet.fa4) [concrete] // CHECK:STDOUT: %ImplicitAs.WithSelf.Convert.e20: %ImplicitAs.WithSelf.Convert.type.66f = struct_value () [concrete] // CHECK:STDOUT: %.714: type = fn_type_with_self_type %ImplicitAs.WithSelf.Convert.type.66f, %ImplicitAs.facet.fa4 [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type: type = fn_type @Core.IntLiteral.as.ImplicitAs.impl.Convert [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type = struct_value () [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.bound.a95: = bound_method %int_3.1ba, %Core.IntLiteral.as.ImplicitAs.impl.Convert [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.bound.762: = bound_method %int_4.0c1, %Core.IntLiteral.as.ImplicitAs.impl.Convert [concrete] // CHECK:STDOUT: %array: %array_type = tuple_value (%int_3.a0f, %int_4.4f1, %int_7) [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: .Int = %Core.Int // CHECK:STDOUT: .As = %Core.As // CHECK:STDOUT: .AddWith = %Core.AddWith // CHECK:STDOUT: .ImplicitAs = %Core.ImplicitAs // CHECK:STDOUT: import Core//default // CHECK:STDOUT: } // CHECK:STDOUT: %Core.Int: %Int.type = import_ref Core//default, Int, loaded [concrete = constants.%Int] // CHECK:STDOUT: %Core.As: %As.type.90f = import_ref Core//default, As, loaded [concrete = constants.%As.generic] // CHECK:STDOUT: %Core.import_ref.8db: @As.WithSelf.%As.assoc_type (%As.assoc_type.752) = import_ref Core//default, loc{{\d+_\d+}}, loaded [symbolic = @As.WithSelf.%assoc0 (constants.%assoc0.2f9)] // CHECK:STDOUT: %Core.Convert.b81 = import_ref Core//default, Convert, unloaded // CHECK:STDOUT: %Core.import_ref.0ec: @As.WithSelf.%As.WithSelf.Convert.type (%As.WithSelf.Convert.type.bb7) = import_ref Core//default, loc{{\d+_\d+}}, loaded [symbolic = @As.WithSelf.%As.WithSelf.Convert (constants.%As.WithSelf.Convert.923)] // CHECK:STDOUT: %Core.import_ref.b3bc94.2: type = import_ref Core//default, loc{{\d+_\d+}}, loaded [symbolic = @As.%T (constants.%T)] // CHECK:STDOUT: %Core.import_ref.1ac891.2: @As.%As.type (%As.type.223) = import_ref Core//default, loc{{\d+_\d+}}, loaded [symbolic = @As.%Self (constants.%Self.2d0)] // CHECK:STDOUT: %Core.import_ref.0e7 = import_ref Core//default, loc{{\d+_\d+}}, unloaded // CHECK:STDOUT: %Core.import_ref.b3bc94.3: type = import_ref Core//default, loc{{\d+_\d+}}, loaded [symbolic = @As.%T (constants.%T)] // CHECK:STDOUT: %Core.import_ref.4c6 = import_ref Core//default, loc{{\d+_\d+}}, unloaded // CHECK:STDOUT: %Core.import_ref.ad5: = import_ref Core//default, loc{{\d+_\d+}}, loaded [concrete = constants.%As.impl_witness] // CHECK:STDOUT: %Core.import_ref.a86459.1: type = import_ref Core//default, loc{{\d+_\d+}}, loaded [concrete = Core.IntLiteral] // CHECK:STDOUT: %Core.import_ref.412: type = import_ref Core//default, loc{{\d+_\d+}}, loaded [concrete = constants.%As.type.ffe] // CHECK:STDOUT: %Core.import_ref.d9d: %Core.IntLiteral.as.As.impl.Convert.type = import_ref Core//default, loc{{\d+_\d+}}, loaded [concrete = constants.%Core.IntLiteral.as.As.impl.Convert] // CHECK:STDOUT: %As.impl_witness_table = impl_witness_table (%Core.import_ref.d9d), @Core.IntLiteral.as.As.impl [concrete] // CHECK:STDOUT: %Core.AddWith: %AddWith.type.e05 = import_ref Core//default, AddWith, loaded [concrete = constants.%AddWith.generic] // CHECK:STDOUT: %Core.import_ref.3f3: @AddWith.WithSelf.%AddWith.assoc_type (%AddWith.assoc_type.b6a) = import_ref Core//default, loc{{\d+_\d+}}, loaded [symbolic = @AddWith.WithSelf.%assoc0 (constants.%assoc0.20f)] // CHECK:STDOUT: %Core.Op = import_ref Core//default, Op, unloaded // CHECK:STDOUT: %Core.import_ref.1b1: @AddWith.WithSelf.%AddWith.WithSelf.Op.type (%AddWith.WithSelf.Op.type.08f) = import_ref Core//default, loc{{\d+_\d+}}, loaded [symbolic = @AddWith.WithSelf.%AddWith.WithSelf.Op (constants.%AddWith.WithSelf.Op.fb8)] // CHECK:STDOUT: %Core.import_ref.b3bc94.5: type = import_ref Core//default, loc{{\d+_\d+}}, loaded [symbolic = @AddWith.%T (constants.%T)] // CHECK:STDOUT: %Core.import_ref.c10145.2: @AddWith.%AddWith.type (%AddWith.type.6d9) = import_ref Core//default, loc{{\d+_\d+}}, loaded [symbolic = @AddWith.%Self (constants.%Self.b7c)] // CHECK:STDOUT: %Core.import_ref.a40 = import_ref Core//default, loc{{\d+_\d+}}, unloaded // CHECK:STDOUT: %Core.import_ref.b3bc94.6: type = import_ref Core//default, loc{{\d+_\d+}}, loaded [symbolic = @AddWith.%T (constants.%T)] // CHECK:STDOUT: %Core.import_ref.a84 = import_ref Core//default, loc{{\d+_\d+}}, unloaded // CHECK:STDOUT: %Core.import_ref.bf0: = import_ref Core//default, loc{{\d+_\d+}}, loaded [concrete = constants.%AddWith.impl_witness] // CHECK:STDOUT: %Core.import_ref.63cbb1.2: type = import_ref Core//default, loc{{\d+_\d+}}, loaded [concrete = constants.%i32.builtin] // CHECK:STDOUT: %Core.import_ref.a41: type = import_ref Core//default, loc{{\d+_\d+}}, loaded [concrete = constants.%AddWith.type.46d] // CHECK:STDOUT: %Core.import_ref.b8a: %i32.builtin.as.AddWith.impl.Op.type = import_ref Core//default, loc{{\d+_\d+}}, loaded [concrete = constants.%i32.builtin.as.AddWith.impl.Op] // CHECK:STDOUT: %AddWith.impl_witness_table = impl_witness_table (%Core.import_ref.b8a), @i32.builtin.as.AddWith.impl [concrete] // CHECK:STDOUT: %Core.ImplicitAs: %ImplicitAs.type.cc7 = import_ref Core//default, ImplicitAs, loaded [concrete = constants.%ImplicitAs.generic] // CHECK:STDOUT: %Core.import_ref.178: @ImplicitAs.WithSelf.%ImplicitAs.assoc_type (%ImplicitAs.assoc_type.ff3) = import_ref Core//default, loc{{\d+_\d+}}, loaded [symbolic = @ImplicitAs.WithSelf.%assoc0 (constants.%assoc0.843)] // CHECK:STDOUT: %Core.Convert.2a3 = import_ref Core//default, Convert, unloaded // CHECK:STDOUT: %Core.import_ref.201: @ImplicitAs.WithSelf.%ImplicitAs.WithSelf.Convert.type (%ImplicitAs.WithSelf.Convert.type.b3a) = import_ref Core//default, loc{{\d+_\d+}}, loaded [symbolic = @ImplicitAs.WithSelf.%ImplicitAs.WithSelf.Convert (constants.%ImplicitAs.WithSelf.Convert.1de)] // CHECK:STDOUT: %Core.import_ref.b3bc94.8: type = import_ref Core//default, loc{{\d+_\d+}}, loaded [symbolic = @ImplicitAs.%T (constants.%T)] // CHECK:STDOUT: %Core.import_ref.ac4dc5.2: @ImplicitAs.%ImplicitAs.type (%ImplicitAs.type.031) = import_ref Core//default, loc{{\d+_\d+}}, loaded [symbolic = @ImplicitAs.%Self (constants.%Self.738)] // CHECK:STDOUT: %Core.import_ref.9ec = import_ref Core//default, loc{{\d+_\d+}}, unloaded // CHECK:STDOUT: %Core.import_ref.b3bc94.9: type = import_ref Core//default, loc{{\d+_\d+}}, loaded [symbolic = @ImplicitAs.%T (constants.%T)] // CHECK:STDOUT: %Core.import_ref.cc1 = import_ref Core//default, loc{{\d+_\d+}}, unloaded // CHECK:STDOUT: %Core.import_ref.d1a: = import_ref Core//default, loc{{\d+_\d+}}, loaded [concrete = constants.%ImplicitAs.impl_witness.586] // CHECK:STDOUT: %Core.import_ref.a86459.2: type = import_ref Core//default, loc{{\d+_\d+}}, loaded [concrete = Core.IntLiteral] // CHECK:STDOUT: %Core.import_ref.7e7: type = import_ref Core//default, loc{{\d+_\d+}}, loaded [concrete = constants.%ImplicitAs.type.78a] // CHECK:STDOUT: %Core.import_ref.8be: = import_ref Core//default, loc{{\d+_\d+}}, loaded [concrete = constants.%ImplicitAs.impl_witness.304] // CHECK:STDOUT: %Core.import_ref.63cbb1.3: type = import_ref Core//default, loc{{\d+_\d+}}, loaded [concrete = constants.%i32.builtin] // CHECK:STDOUT: %Core.import_ref.79b: type = import_ref Core//default, loc{{\d+_\d+}}, loaded [concrete = constants.%ImplicitAs.type.139] // CHECK:STDOUT: %Core.import_ref.954: %i32.builtin.as.ImplicitAs.impl.Convert.type = import_ref Core//default, loc{{\d+_\d+}}, loaded [concrete = constants.%i32.builtin.as.ImplicitAs.impl.Convert] // CHECK:STDOUT: %ImplicitAs.impl_witness_table.267 = impl_witness_table (%Core.import_ref.954), @i32.builtin.as.ImplicitAs.impl [concrete] // CHECK:STDOUT: %Core.import_ref.d85: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type = import_ref Core//default, loc{{\d+_\d+}}, loaded [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert] // CHECK:STDOUT: %ImplicitAs.impl_witness_table.7a2 = impl_witness_table (%Core.import_ref.d85), @Core.IntLiteral.as.ImplicitAs.impl [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .arr = %arr // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %arr.patt: %pattern_type.9e2 = ref_binding_pattern arr [concrete] // CHECK:STDOUT: %arr.var_patt: %pattern_type.9e2 = var_pattern %arr.patt [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %arr.var: ref %array_type = var %arr.var_patt [concrete] // CHECK:STDOUT: %.loc4_44: type = splice_block %array_type [concrete = constants.%array_type] { // CHECK:STDOUT: %.loc4_16: type = type_literal constants.%i32.builtin [concrete = constants.%i32.builtin] // CHECK:STDOUT: %int_1: Core.IntLiteral = int_value 1 [concrete = constants.%int_1.5b8] // CHECK:STDOUT: %.loc4_27: type = type_literal constants.%i32.builtin [concrete = constants.%i32.builtin] // CHECK:STDOUT: %impl.elem0.loc4_24: %.e3a = impl_witness_access constants.%As.impl_witness, element0 [concrete = constants.%Core.IntLiteral.as.As.impl.Convert] // CHECK:STDOUT: %bound_method.loc4_24: = bound_method %int_1, %impl.elem0.loc4_24 [concrete = constants.%Core.IntLiteral.as.As.impl.Convert.bound.4b3] // CHECK:STDOUT: %Core.IntLiteral.as.As.impl.Convert.call.loc4_24: init %i32.builtin = call %bound_method.loc4_24(%int_1) [concrete = constants.%int_1.f38] // CHECK:STDOUT: %.loc4_24.1: %i32.builtin = value_of_initializer %Core.IntLiteral.as.As.impl.Convert.call.loc4_24 [concrete = constants.%int_1.f38] // CHECK:STDOUT: %.loc4_24.2: %i32.builtin = converted %int_1, %.loc4_24.1 [concrete = constants.%int_1.f38] // CHECK:STDOUT: %int_2: Core.IntLiteral = int_value 2 [concrete = constants.%int_2.ecc] // CHECK:STDOUT: %.loc4_40: type = type_literal constants.%i32.builtin [concrete = constants.%i32.builtin] // CHECK:STDOUT: %impl.elem0.loc4_37: %.e3a = impl_witness_access constants.%As.impl_witness, element0 [concrete = constants.%Core.IntLiteral.as.As.impl.Convert] // CHECK:STDOUT: %bound_method.loc4_37: = bound_method %int_2, %impl.elem0.loc4_37 [concrete = constants.%Core.IntLiteral.as.As.impl.Convert.bound.7b2] // CHECK:STDOUT: %Core.IntLiteral.as.As.impl.Convert.call.loc4_37: init %i32.builtin = call %bound_method.loc4_37(%int_2) [concrete = constants.%int_2.5a1] // CHECK:STDOUT: %.loc4_37.1: %i32.builtin = value_of_initializer %Core.IntLiteral.as.As.impl.Convert.call.loc4_37 [concrete = constants.%int_2.5a1] // CHECK:STDOUT: %.loc4_37.2: %i32.builtin = converted %int_2, %.loc4_37.1 [concrete = constants.%int_2.5a1] // CHECK:STDOUT: %impl.elem0.loc4_32.1: %.0ff = impl_witness_access constants.%AddWith.impl_witness, element0 [concrete = constants.%i32.builtin.as.AddWith.impl.Op] // CHECK:STDOUT: %bound_method.loc4_32.1: = bound_method %.loc4_24.2, %impl.elem0.loc4_32.1 [concrete = constants.%i32.builtin.as.AddWith.impl.Op.bound.abd] // CHECK:STDOUT: %i32.builtin.as.AddWith.impl.Op.call: init %i32.builtin = call %bound_method.loc4_32.1(%.loc4_24.2, %.loc4_37.2) [concrete = constants.%int_3.a0f] // CHECK:STDOUT: %impl.elem0.loc4_32.2: %.626 = impl_witness_access constants.%ImplicitAs.impl_witness.304, element0 [concrete = constants.%i32.builtin.as.ImplicitAs.impl.Convert] // CHECK:STDOUT: %bound_method.loc4_32.2: = bound_method %i32.builtin.as.AddWith.impl.Op.call, %impl.elem0.loc4_32.2 [concrete = constants.%i32.builtin.as.ImplicitAs.impl.Convert.bound] // CHECK:STDOUT: %.loc4_32.1: %i32.builtin = value_of_initializer %i32.builtin.as.AddWith.impl.Op.call [concrete = constants.%int_3.a0f] // CHECK:STDOUT: %.loc4_32.2: %i32.builtin = converted %i32.builtin.as.AddWith.impl.Op.call, %.loc4_32.1 [concrete = constants.%int_3.a0f] // CHECK:STDOUT: %i32.builtin.as.ImplicitAs.impl.Convert.call: init Core.IntLiteral = call %bound_method.loc4_32.2(%.loc4_32.2) [concrete = constants.%int_3.1ba] // CHECK:STDOUT: %.loc4_32.3: Core.IntLiteral = value_of_initializer %i32.builtin.as.ImplicitAs.impl.Convert.call [concrete = constants.%int_3.1ba] // CHECK:STDOUT: %.loc4_32.4: Core.IntLiteral = converted %i32.builtin.as.AddWith.impl.Op.call, %.loc4_32.3 [concrete = constants.%int_3.1ba] // CHECK:STDOUT: %array_type: type = array_type %.loc4_32.4, %.loc4_16 [concrete = constants.%array_type] // CHECK:STDOUT: } // CHECK:STDOUT: %arr: ref %array_type = ref_binding arr, %arr.var [concrete = %arr.var] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic interface @As(imports.%Core.import_ref.b3bc94.3: type) [from "core.carbon"] { // CHECK:STDOUT: %T: type = symbolic_binding T, 0 [symbolic = %T (constants.%T)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %As.type: type = facet_type <@As, @As(%T)> [symbolic = %As.type (constants.%As.type.223)] // CHECK:STDOUT: %Self: @As.%As.type (%As.type.223) = symbolic_binding Self, 1 [symbolic = %Self (constants.%Self.2d0)] // CHECK:STDOUT: // CHECK:STDOUT: interface { // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = imports.%Core.import_ref.0e7 // CHECK:STDOUT: .Convert = imports.%Core.import_ref.8db // CHECK:STDOUT: witness = (imports.%Core.Convert.b81) // CHECK:STDOUT: // CHECK:STDOUT: !requires: // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic interface @AddWith(imports.%Core.import_ref.b3bc94.6: type) [from "core.carbon"] { // CHECK:STDOUT: %T: type = symbolic_binding T, 0 [symbolic = %T (constants.%T)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %AddWith.type: type = facet_type <@AddWith, @AddWith(%T)> [symbolic = %AddWith.type (constants.%AddWith.type.6d9)] // CHECK:STDOUT: %Self: @AddWith.%AddWith.type (%AddWith.type.6d9) = symbolic_binding Self, 1 [symbolic = %Self (constants.%Self.b7c)] // CHECK:STDOUT: // CHECK:STDOUT: interface { // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = imports.%Core.import_ref.a40 // CHECK:STDOUT: .Op = imports.%Core.import_ref.3f3 // CHECK:STDOUT: witness = (imports.%Core.Op) // CHECK:STDOUT: // CHECK:STDOUT: !requires: // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic interface @ImplicitAs(imports.%Core.import_ref.b3bc94.9: type) [from "core.carbon"] { // CHECK:STDOUT: %T: type = symbolic_binding T, 0 [symbolic = %T (constants.%T)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %ImplicitAs.type: type = facet_type <@ImplicitAs, @ImplicitAs(%T)> [symbolic = %ImplicitAs.type (constants.%ImplicitAs.type.031)] // CHECK:STDOUT: %Self: @ImplicitAs.%ImplicitAs.type (%ImplicitAs.type.031) = symbolic_binding Self, 1 [symbolic = %Self (constants.%Self.738)] // CHECK:STDOUT: // CHECK:STDOUT: interface { // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = imports.%Core.import_ref.9ec // CHECK:STDOUT: .Convert = imports.%Core.import_ref.178 // CHECK:STDOUT: witness = (imports.%Core.Convert.2a3) // CHECK:STDOUT: // CHECK:STDOUT: !requires: // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: impl @Core.IntLiteral.as.As.impl: imports.%Core.import_ref.a86459.1 as imports.%Core.import_ref.412 [from "core.carbon"] { // CHECK:STDOUT: !members: // CHECK:STDOUT: witness = imports.%Core.import_ref.ad5 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: impl @i32.builtin.as.AddWith.impl: imports.%Core.import_ref.63cbb1.2 as imports.%Core.import_ref.a41 [from "core.carbon"] { // CHECK:STDOUT: !members: // CHECK:STDOUT: witness = imports.%Core.import_ref.bf0 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: impl @Core.IntLiteral.as.ImplicitAs.impl: imports.%Core.import_ref.a86459.2 as imports.%Core.import_ref.7e7 [from "core.carbon"] { // CHECK:STDOUT: !members: // CHECK:STDOUT: witness = imports.%Core.import_ref.d1a // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: impl @i32.builtin.as.ImplicitAs.impl: imports.%Core.import_ref.63cbb1.3 as imports.%Core.import_ref.79b [from "core.carbon"] { // CHECK:STDOUT: !members: // CHECK:STDOUT: witness = imports.%Core.import_ref.8be // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @Int = "int.make_type_signed" [from "core.carbon"]; // CHECK:STDOUT: // CHECK:STDOUT: generic fn @As.WithSelf.Convert(imports.%Core.import_ref.b3bc94.2: type, imports.%Core.import_ref.1ac891.2: @As.%As.type (%As.type.223)) [from "core.carbon"] { // CHECK:STDOUT: %T: type = symbolic_binding T, 0 [symbolic = %T (constants.%T)] // CHECK:STDOUT: %As.type: type = facet_type <@As, @As(%T)> [symbolic = %As.type (constants.%As.type.223)] // CHECK:STDOUT: %Self: @As.WithSelf.Convert.%As.type (%As.type.223) = symbolic_binding Self, 1 [symbolic = %Self (constants.%Self.2d0)] // CHECK:STDOUT: %Self.binding.as_type: type = symbolic_binding_type Self, 1, %Self [symbolic = %Self.binding.as_type (constants.%Self.binding.as_type.297)] // CHECK:STDOUT: %pattern_type.1: type = pattern_type %Self.binding.as_type [symbolic = %pattern_type.1 (constants.%pattern_type.760)] // CHECK:STDOUT: %.1: Core.Form = init_form %T [symbolic = %.1 (constants.%.184)] // CHECK:STDOUT: %pattern_type.2: type = pattern_type %T [symbolic = %pattern_type.2 (constants.%pattern_type.51d)] // CHECK:STDOUT: // CHECK:STDOUT: fn; // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @Core.IntLiteral.as.As.impl.Convert = "int.convert_checked" [from "core.carbon"]; // CHECK:STDOUT: // CHECK:STDOUT: generic fn @AddWith.WithSelf.Op(imports.%Core.import_ref.b3bc94.5: type, imports.%Core.import_ref.c10145.2: @AddWith.%AddWith.type (%AddWith.type.6d9)) [from "core.carbon"] { // CHECK:STDOUT: %T: type = symbolic_binding T, 0 [symbolic = %T (constants.%T)] // CHECK:STDOUT: %AddWith.type: type = facet_type <@AddWith, @AddWith(%T)> [symbolic = %AddWith.type (constants.%AddWith.type.6d9)] // CHECK:STDOUT: %Self: @AddWith.WithSelf.Op.%AddWith.type (%AddWith.type.6d9) = symbolic_binding Self, 1 [symbolic = %Self (constants.%Self.b7c)] // CHECK:STDOUT: %Self.binding.as_type: type = symbolic_binding_type Self, 1, %Self [symbolic = %Self.binding.as_type (constants.%Self.binding.as_type.14b)] // CHECK:STDOUT: %pattern_type: type = pattern_type %Self.binding.as_type [symbolic = %pattern_type (constants.%pattern_type.259)] // CHECK:STDOUT: %.1: Core.Form = init_form %Self.binding.as_type [symbolic = %.1 (constants.%.730)] // CHECK:STDOUT: // CHECK:STDOUT: fn; // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @i32.builtin.as.AddWith.impl.Op = "int.sadd" [from "core.carbon"]; // CHECK:STDOUT: // CHECK:STDOUT: generic fn @ImplicitAs.WithSelf.Convert(imports.%Core.import_ref.b3bc94.8: type, imports.%Core.import_ref.ac4dc5.2: @ImplicitAs.%ImplicitAs.type (%ImplicitAs.type.031)) [from "core.carbon"] { // CHECK:STDOUT: %T: type = symbolic_binding T, 0 [symbolic = %T (constants.%T)] // CHECK:STDOUT: %ImplicitAs.type: type = facet_type <@ImplicitAs, @ImplicitAs(%T)> [symbolic = %ImplicitAs.type (constants.%ImplicitAs.type.031)] // CHECK:STDOUT: %Self: @ImplicitAs.WithSelf.Convert.%ImplicitAs.type (%ImplicitAs.type.031) = symbolic_binding Self, 1 [symbolic = %Self (constants.%Self.738)] // CHECK:STDOUT: %Self.binding.as_type: type = symbolic_binding_type Self, 1, %Self [symbolic = %Self.binding.as_type (constants.%Self.binding.as_type.a44)] // CHECK:STDOUT: %pattern_type.1: type = pattern_type %Self.binding.as_type [symbolic = %pattern_type.1 (constants.%pattern_type.e9a)] // CHECK:STDOUT: %.1: Core.Form = init_form %T [symbolic = %.1 (constants.%.184)] // CHECK:STDOUT: %pattern_type.2: type = pattern_type %T [symbolic = %pattern_type.2 (constants.%pattern_type.51d)] // CHECK:STDOUT: // CHECK:STDOUT: fn; // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @i32.builtin.as.ImplicitAs.impl.Convert = "int.convert_checked" [from "core.carbon"]; // CHECK:STDOUT: // CHECK:STDOUT: fn @Core.IntLiteral.as.ImplicitAs.impl.Convert = "int.convert_checked" [from "core.carbon"]; // CHECK:STDOUT: // CHECK:STDOUT: fn @__global_init() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %int_3.loc4_49: Core.IntLiteral = int_value 3 [concrete = constants.%int_3.1ba] // CHECK:STDOUT: %int_4.loc4_52: Core.IntLiteral = int_value 4 [concrete = constants.%int_4.0c1] // CHECK:STDOUT: %int_3.loc4_56: Core.IntLiteral = int_value 3 [concrete = constants.%int_3.1ba] // CHECK:STDOUT: %.loc4_61: type = type_literal constants.%i32.builtin [concrete = constants.%i32.builtin] // CHECK:STDOUT: %impl.elem0.loc4_58: %.e3a = impl_witness_access constants.%As.impl_witness, element0 [concrete = constants.%Core.IntLiteral.as.As.impl.Convert] // CHECK:STDOUT: %bound_method.loc4_58: = bound_method %int_3.loc4_56, %impl.elem0.loc4_58 [concrete = constants.%Core.IntLiteral.as.As.impl.Convert.bound.2b2] // CHECK:STDOUT: %Core.IntLiteral.as.As.impl.Convert.call.loc4_58: init %i32.builtin = call %bound_method.loc4_58(%int_3.loc4_56) [concrete = constants.%int_3.a0f] // CHECK:STDOUT: %.loc4_58.1: %i32.builtin = value_of_initializer %Core.IntLiteral.as.As.impl.Convert.call.loc4_58 [concrete = constants.%int_3.a0f] // CHECK:STDOUT: %.loc4_58.2: %i32.builtin = converted %int_3.loc4_56, %.loc4_58.1 [concrete = constants.%int_3.a0f] // CHECK:STDOUT: %int_4.loc4_69: Core.IntLiteral = int_value 4 [concrete = constants.%int_4.0c1] // CHECK:STDOUT: %.loc4_74: type = type_literal constants.%i32.builtin [concrete = constants.%i32.builtin] // CHECK:STDOUT: %impl.elem0.loc4_71: %.e3a = impl_witness_access constants.%As.impl_witness, element0 [concrete = constants.%Core.IntLiteral.as.As.impl.Convert] // CHECK:STDOUT: %bound_method.loc4_71: = bound_method %int_4.loc4_69, %impl.elem0.loc4_71 [concrete = constants.%Core.IntLiteral.as.As.impl.Convert.bound.2d0] // CHECK:STDOUT: %Core.IntLiteral.as.As.impl.Convert.call.loc4_71: init %i32.builtin = call %bound_method.loc4_71(%int_4.loc4_69) [concrete = constants.%int_4.4f1] // CHECK:STDOUT: %.loc4_71.1: %i32.builtin = value_of_initializer %Core.IntLiteral.as.As.impl.Convert.call.loc4_71 [concrete = constants.%int_4.4f1] // CHECK:STDOUT: %.loc4_71.2: %i32.builtin = converted %int_4.loc4_69, %.loc4_71.1 [concrete = constants.%int_4.4f1] // CHECK:STDOUT: %impl.elem0.loc4_66: %.0ff = impl_witness_access constants.%AddWith.impl_witness, element0 [concrete = constants.%i32.builtin.as.AddWith.impl.Op] // CHECK:STDOUT: %bound_method.loc4_66: = bound_method %.loc4_58.2, %impl.elem0.loc4_66 [concrete = constants.%i32.builtin.as.AddWith.impl.Op.bound.bf8] // CHECK:STDOUT: %i32.builtin.as.AddWith.impl.Op.call: init %i32.builtin = call %bound_method.loc4_66(%.loc4_58.2, %.loc4_71.2) [concrete = constants.%int_7] // CHECK:STDOUT: %.loc4_78.1: %tuple.type = tuple_literal (%int_3.loc4_49, %int_4.loc4_52, %i32.builtin.as.AddWith.impl.Op.call) [concrete = constants.%tuple] // CHECK:STDOUT: %impl.elem0.loc4_78.1: %.714 = impl_witness_access constants.%ImplicitAs.impl_witness.586, element0 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert] // CHECK:STDOUT: %bound_method.loc4_78.1: = bound_method %int_3.loc4_49, %impl.elem0.loc4_78.1 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.bound.a95] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc4_78.1: init %i32.builtin = call %bound_method.loc4_78.1(%int_3.loc4_49) [concrete = constants.%int_3.a0f] // CHECK:STDOUT: %.loc4_78.2: init %i32.builtin = converted %int_3.loc4_49, %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc4_78.1 [concrete = constants.%int_3.a0f] // CHECK:STDOUT: %int_0: Core.IntLiteral = int_value 0 [concrete = constants.%int_0] // CHECK:STDOUT: %.loc4_78.3: ref %i32.builtin = array_index file.%arr.var, %int_0 // CHECK:STDOUT: %.loc4_78.4: init %i32.builtin to %.loc4_78.3 = in_place_init %.loc4_78.2 [concrete = constants.%int_3.a0f] // CHECK:STDOUT: %impl.elem0.loc4_78.2: %.714 = impl_witness_access constants.%ImplicitAs.impl_witness.586, element0 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert] // CHECK:STDOUT: %bound_method.loc4_78.2: = bound_method %int_4.loc4_52, %impl.elem0.loc4_78.2 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.bound.762] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc4_78.2: init %i32.builtin = call %bound_method.loc4_78.2(%int_4.loc4_52) [concrete = constants.%int_4.4f1] // CHECK:STDOUT: %.loc4_78.5: init %i32.builtin = converted %int_4.loc4_52, %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc4_78.2 [concrete = constants.%int_4.4f1] // CHECK:STDOUT: %int_1: Core.IntLiteral = int_value 1 [concrete = constants.%int_1.5b8] // CHECK:STDOUT: %.loc4_78.6: ref %i32.builtin = array_index file.%arr.var, %int_1 // CHECK:STDOUT: %.loc4_78.7: init %i32.builtin to %.loc4_78.6 = in_place_init %.loc4_78.5 [concrete = constants.%int_4.4f1] // CHECK:STDOUT: %int_2: Core.IntLiteral = int_value 2 [concrete = constants.%int_2.ecc] // CHECK:STDOUT: %.loc4_78.8: ref %i32.builtin = array_index file.%arr.var, %int_2 // CHECK:STDOUT: %.loc4_78.9: init %i32.builtin to %.loc4_78.8 = in_place_init %i32.builtin.as.AddWith.impl.Op.call [concrete = constants.%int_7] // CHECK:STDOUT: %.loc4_78.10: init %array_type to file.%arr.var = array_init (%.loc4_78.4, %.loc4_78.7, %.loc4_78.9) [concrete = constants.%array] // CHECK:STDOUT: %.loc4_1: init %array_type = converted %.loc4_78.1, %.loc4_78.10 [concrete = constants.%array] // CHECK:STDOUT: assign file.%arr.var, %.loc4_1 // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @As(constants.%T) { // CHECK:STDOUT: %T => constants.%T // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @As.WithSelf(constants.%T, constants.%Self.2d0) {} // CHECK:STDOUT: // CHECK:STDOUT: specific @As.WithSelf.Convert(constants.%T, constants.%Self.2d0) { // CHECK:STDOUT: %T => constants.%T // CHECK:STDOUT: %As.type => constants.%As.type.223 // CHECK:STDOUT: %Self => constants.%Self.2d0 // CHECK:STDOUT: %Self.binding.as_type => constants.%Self.binding.as_type.297 // CHECK:STDOUT: %pattern_type.1 => constants.%pattern_type.760 // CHECK:STDOUT: %.1 => constants.%.184 // CHECK:STDOUT: %pattern_type.2 => constants.%pattern_type.51d // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @As(constants.%i32.builtin) { // CHECK:STDOUT: %T => constants.%i32.builtin // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %As.type => constants.%As.type.ffe // CHECK:STDOUT: %Self => constants.%Self.af0 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @As.WithSelf(constants.%i32.builtin, constants.%Self.2d0) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: %T => constants.%i32.builtin // CHECK:STDOUT: %As.type => constants.%As.type.ffe // CHECK:STDOUT: %Self => constants.%Self.2d0 // CHECK:STDOUT: %As.WithSelf.Convert.type => constants.%As.WithSelf.Convert.type.06b // CHECK:STDOUT: %As.WithSelf.Convert => constants.%As.WithSelf.Convert.b84 // CHECK:STDOUT: %As.assoc_type => constants.%As.assoc_type.bc2 // CHECK:STDOUT: %assoc0 => constants.%assoc0.5d8 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @As.WithSelf(constants.%i32.builtin, constants.%As.facet) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: %T => constants.%i32.builtin // CHECK:STDOUT: %As.type => constants.%As.type.ffe // CHECK:STDOUT: %Self => constants.%As.facet // CHECK:STDOUT: %As.WithSelf.Convert.type => constants.%As.WithSelf.Convert.type.b07 // CHECK:STDOUT: %As.WithSelf.Convert => constants.%As.WithSelf.Convert.2d0 // CHECK:STDOUT: %As.assoc_type => constants.%As.assoc_type.bc2 // CHECK:STDOUT: %assoc0 => constants.%assoc0.5d8 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @AddWith(constants.%T) { // CHECK:STDOUT: %T => constants.%T // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @AddWith.WithSelf(constants.%T, constants.%Self.b7c) {} // CHECK:STDOUT: // CHECK:STDOUT: specific @AddWith.WithSelf.Op(constants.%T, constants.%Self.b7c) { // CHECK:STDOUT: %T => constants.%T // CHECK:STDOUT: %AddWith.type => constants.%AddWith.type.6d9 // CHECK:STDOUT: %Self => constants.%Self.b7c // CHECK:STDOUT: %Self.binding.as_type => constants.%Self.binding.as_type.14b // CHECK:STDOUT: %pattern_type => constants.%pattern_type.259 // CHECK:STDOUT: %.1 => constants.%.730 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @AddWith(constants.%i32.builtin) { // CHECK:STDOUT: %T => constants.%i32.builtin // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %AddWith.type => constants.%AddWith.type.46d // CHECK:STDOUT: %Self => constants.%Self.365 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @AddWith.WithSelf(constants.%i32.builtin, constants.%Self.b7c) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: %T => constants.%i32.builtin // CHECK:STDOUT: %AddWith.type => constants.%AddWith.type.46d // CHECK:STDOUT: %Self => constants.%Self.b7c // CHECK:STDOUT: %AddWith.WithSelf.Op.type => constants.%AddWith.WithSelf.Op.type.de8 // CHECK:STDOUT: %AddWith.WithSelf.Op => constants.%AddWith.WithSelf.Op.31e // CHECK:STDOUT: %AddWith.assoc_type => constants.%AddWith.assoc_type.dff // CHECK:STDOUT: %assoc0 => constants.%assoc0.6d5 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @AddWith.WithSelf(constants.%i32.builtin, constants.%AddWith.facet) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: %T => constants.%i32.builtin // CHECK:STDOUT: %AddWith.type => constants.%AddWith.type.46d // CHECK:STDOUT: %Self => constants.%AddWith.facet // CHECK:STDOUT: %AddWith.WithSelf.Op.type => constants.%AddWith.WithSelf.Op.type.78e // CHECK:STDOUT: %AddWith.WithSelf.Op => constants.%AddWith.WithSelf.Op.d4d // CHECK:STDOUT: %AddWith.assoc_type => constants.%AddWith.assoc_type.dff // CHECK:STDOUT: %assoc0 => constants.%assoc0.6d5 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @ImplicitAs(constants.%T) { // CHECK:STDOUT: %T => constants.%T // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @ImplicitAs.WithSelf(constants.%T, constants.%Self.738) {} // CHECK:STDOUT: // CHECK:STDOUT: specific @ImplicitAs.WithSelf.Convert(constants.%T, constants.%Self.738) { // CHECK:STDOUT: %T => constants.%T // CHECK:STDOUT: %ImplicitAs.type => constants.%ImplicitAs.type.031 // CHECK:STDOUT: %Self => constants.%Self.738 // CHECK:STDOUT: %Self.binding.as_type => constants.%Self.binding.as_type.a44 // CHECK:STDOUT: %pattern_type.1 => constants.%pattern_type.e9a // CHECK:STDOUT: %.1 => constants.%.184 // CHECK:STDOUT: %pattern_type.2 => constants.%pattern_type.51d // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @ImplicitAs(Core.IntLiteral) { // CHECK:STDOUT: %T => Core.IntLiteral // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %ImplicitAs.type => constants.%ImplicitAs.type.139 // CHECK:STDOUT: %Self => constants.%Self.cce // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @ImplicitAs.WithSelf(Core.IntLiteral, constants.%Self.738) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: %T => Core.IntLiteral // CHECK:STDOUT: %ImplicitAs.type => constants.%ImplicitAs.type.139 // CHECK:STDOUT: %Self => constants.%Self.738 // CHECK:STDOUT: %ImplicitAs.WithSelf.Convert.type => constants.%ImplicitAs.WithSelf.Convert.type.577 // CHECK:STDOUT: %ImplicitAs.WithSelf.Convert => constants.%ImplicitAs.WithSelf.Convert.f0a // CHECK:STDOUT: %ImplicitAs.assoc_type => constants.%ImplicitAs.assoc_type.959 // CHECK:STDOUT: %assoc0 => constants.%assoc0.461 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @ImplicitAs(constants.%i32.builtin) { // CHECK:STDOUT: %T => constants.%i32.builtin // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %ImplicitAs.type => constants.%ImplicitAs.type.78a // CHECK:STDOUT: %Self => constants.%Self.dd8 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @ImplicitAs.WithSelf(Core.IntLiteral, constants.%ImplicitAs.facet.7f3) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: %T => Core.IntLiteral // CHECK:STDOUT: %ImplicitAs.type => constants.%ImplicitAs.type.139 // CHECK:STDOUT: %Self => constants.%ImplicitAs.facet.7f3 // CHECK:STDOUT: %ImplicitAs.WithSelf.Convert.type => constants.%ImplicitAs.WithSelf.Convert.type.e78 // CHECK:STDOUT: %ImplicitAs.WithSelf.Convert => constants.%ImplicitAs.WithSelf.Convert.e56 // CHECK:STDOUT: %ImplicitAs.assoc_type => constants.%ImplicitAs.assoc_type.959 // CHECK:STDOUT: %assoc0 => constants.%assoc0.461 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @ImplicitAs.WithSelf(constants.%i32.builtin, constants.%Self.738) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: %T => constants.%i32.builtin // CHECK:STDOUT: %ImplicitAs.type => constants.%ImplicitAs.type.78a // CHECK:STDOUT: %Self => constants.%Self.738 // CHECK:STDOUT: %ImplicitAs.WithSelf.Convert.type => constants.%ImplicitAs.WithSelf.Convert.type.ea9 // CHECK:STDOUT: %ImplicitAs.WithSelf.Convert => constants.%ImplicitAs.WithSelf.Convert.149 // CHECK:STDOUT: %ImplicitAs.assoc_type => constants.%ImplicitAs.assoc_type.398 // CHECK:STDOUT: %assoc0 => constants.%assoc0.4a0 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @ImplicitAs.WithSelf(constants.%i32.builtin, constants.%ImplicitAs.facet.fa4) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: %T => constants.%i32.builtin // CHECK:STDOUT: %ImplicitAs.type => constants.%ImplicitAs.type.78a // CHECK:STDOUT: %Self => constants.%ImplicitAs.facet.fa4 // CHECK:STDOUT: %ImplicitAs.WithSelf.Convert.type => constants.%ImplicitAs.WithSelf.Convert.type.66f // CHECK:STDOUT: %ImplicitAs.WithSelf.Convert => constants.%ImplicitAs.WithSelf.Convert.e20 // CHECK:STDOUT: %ImplicitAs.assoc_type => constants.%ImplicitAs.assoc_type.398 // CHECK:STDOUT: %assoc0 => constants.%assoc0.4a0 // CHECK:STDOUT: } // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/function/builtin/definition.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/int.carbon // TODO: Add ranges and switch to "--dump-sem-ir-ranges=only". // EXTRA-ARGS: --dump-sem-ir-ranges=if-present // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/function/builtin/definition.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/function/builtin/definition.carbon fn Add(a: i32, b: i32) -> i32 = "int.sadd"; // CHECK:STDOUT: --- definition.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [concrete] // CHECK:STDOUT: %Int.type: type = generic_class_type @Int [concrete] // CHECK:STDOUT: %Int.generic: %Int.type = struct_value () [concrete] // CHECK:STDOUT: %i32: type = class_type @Int, @Int(%int_32) [concrete] // CHECK:STDOUT: %pattern_type.7ce: type = pattern_type %i32 [concrete] // CHECK:STDOUT: %.ff5: Core.Form = init_form %i32 [concrete] // CHECK:STDOUT: %Add.type: type = fn_type @Add [concrete] // CHECK:STDOUT: %Add: %Add.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: .Int = %Core.Int // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: %Core.Int: %Int.type = import_ref Core//prelude/parts/int, Int, loaded [concrete = constants.%Int.generic] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .Add = %Add.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %Add.decl: %Add.type = fn_decl @Add [concrete = constants.%Add] { // CHECK:STDOUT: %a.patt: %pattern_type.7ce = value_binding_pattern a [concrete] // CHECK:STDOUT: %a.param_patt: %pattern_type.7ce = value_param_pattern %a.patt [concrete] // CHECK:STDOUT: %b.patt: %pattern_type.7ce = value_binding_pattern b [concrete] // CHECK:STDOUT: %b.param_patt: %pattern_type.7ce = value_param_pattern %b.patt [concrete] // CHECK:STDOUT: %return.patt: %pattern_type.7ce = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: %pattern_type.7ce = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %i32.loc15_27: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %.loc15: Core.Form = init_form %i32.loc15_27 [concrete = constants.%.ff5] // CHECK:STDOUT: %a.param: %i32 = value_param call_param0 // CHECK:STDOUT: %i32.loc15_11: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %a: %i32 = value_binding a, %a.param // CHECK:STDOUT: %b.param: %i32 = value_param call_param1 // CHECK:STDOUT: %i32.loc15_19: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %b: %i32 = value_binding b, %b.param // CHECK:STDOUT: %return.param: ref %i32 = out_param call_param2 // CHECK:STDOUT: %return: ref %i32 = return_slot %return.param // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @Add(%a.param: %i32, %b.param: %i32) -> out %return.param: %i32 = "int.sadd"; // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/function/builtin/fail_redefined.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/int.carbon // TODO: Add ranges and switch to "--dump-sem-ir-ranges=only". // EXTRA-ARGS: --dump-sem-ir-ranges=if-present // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/function/builtin/fail_redefined.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/function/builtin/fail_redefined.carbon fn A(n: i32, m: i32) -> i32 = "int.sadd"; // CHECK:STDERR: fail_redefined.carbon:[[@LINE+7]]:1: error: redefinition of `fn A` [RedeclRedef] // CHECK:STDERR: fn A(n: i32, unused m: i32) -> i32 { return n; } // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: fail_redefined.carbon:[[@LINE-4]]:1: note: previously defined here [RedeclPrevDef] // CHECK:STDERR: fn A(n: i32, m: i32) -> i32 = "int.sadd"; // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: fn A(n: i32, unused m: i32) -> i32 { return n; } fn B(n: i32, unused m: i32) -> i32 { return n; } // CHECK:STDERR: fail_redefined.carbon:[[@LINE+7]]:1: error: redefinition of `fn B` [RedeclRedef] // CHECK:STDERR: fn B(n: i32, m: i32) -> i32 = "int.sadd"; // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: fail_redefined.carbon:[[@LINE-4]]:1: note: previously defined here [RedeclPrevDef] // CHECK:STDERR: fn B(n: i32, unused m: i32) -> i32 { return n; } // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: fn B(n: i32, m: i32) -> i32 = "int.sadd"; fn C(n: i32, m: i32) -> i32 = "int.sadd"; // CHECK:STDERR: fail_redefined.carbon:[[@LINE+7]]:1: error: redefinition of `fn C` [RedeclRedef] // CHECK:STDERR: fn C(n: i32, m: i32) -> i32 = "int.sadd"; // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: fail_redefined.carbon:[[@LINE-4]]:1: note: previously defined here [RedeclPrevDef] // CHECK:STDERR: fn C(n: i32, m: i32) -> i32 = "int.sadd"; // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: fn C(n: i32, m: i32) -> i32 = "int.sadd"; // CHECK:STDOUT: --- fail_redefined.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [concrete] // CHECK:STDOUT: %Int.type: type = generic_class_type @Int [concrete] // CHECK:STDOUT: %Int.generic: %Int.type = struct_value () [concrete] // CHECK:STDOUT: %N: Core.IntLiteral = symbolic_binding N, 0 [symbolic] // CHECK:STDOUT: %i32: type = class_type @Int, @Int(%int_32) [concrete] // CHECK:STDOUT: %pattern_type.7ce: type = pattern_type %i32 [concrete] // CHECK:STDOUT: %.ff5: Core.Form = init_form %i32 [concrete] // CHECK:STDOUT: %A.type.8165c1.1: type = fn_type @A.loc15 [concrete] // CHECK:STDOUT: %A.8aef9d.1: %A.type.8165c1.1 = struct_value () [concrete] // CHECK:STDOUT: %A.type.8165c1.2: type = fn_type @A.loc23 [concrete] // CHECK:STDOUT: %A.8aef9d.2: %A.type.8165c1.2 = struct_value () [concrete] // CHECK:STDOUT: %Copy.type: type = facet_type <@Copy> [concrete] // CHECK:STDOUT: %Int.as.Copy.impl.Op.type.824: type = fn_type @Int.as.Copy.impl.Op, @Int.as.Copy.impl(%N) [symbolic] // CHECK:STDOUT: %Int.as.Copy.impl.Op.9b9: %Int.as.Copy.impl.Op.type.824 = struct_value () [symbolic] // CHECK:STDOUT: %Copy.impl_witness.f17: = impl_witness imports.%Copy.impl_witness_table.e76, @Int.as.Copy.impl(%int_32) [concrete] // CHECK:STDOUT: %Int.as.Copy.impl.Op.type.546: type = fn_type @Int.as.Copy.impl.Op, @Int.as.Copy.impl(%int_32) [concrete] // CHECK:STDOUT: %Int.as.Copy.impl.Op.664: %Int.as.Copy.impl.Op.type.546 = struct_value () [concrete] // CHECK:STDOUT: %Copy.facet: %Copy.type = facet_value %i32, (%Copy.impl_witness.f17) [concrete] // CHECK:STDOUT: %Copy.WithSelf.Op.type.081: type = fn_type @Copy.WithSelf.Op, @Copy.WithSelf(%Copy.facet) [concrete] // CHECK:STDOUT: %.8e2: type = fn_type_with_self_type %Copy.WithSelf.Op.type.081, %Copy.facet [concrete] // CHECK:STDOUT: %Int.as.Copy.impl.Op.specific_fn: = specific_function %Int.as.Copy.impl.Op.664, @Int.as.Copy.impl.Op(%int_32) [concrete] // CHECK:STDOUT: %B.type.d6bcfe.1: type = fn_type @B.loc25 [concrete] // CHECK:STDOUT: %B.5d732e.1: %B.type.d6bcfe.1 = struct_value () [concrete] // CHECK:STDOUT: %B.type.d6bcfe.2: type = fn_type @B.loc33 [concrete] // CHECK:STDOUT: %B.5d732e.2: %B.type.d6bcfe.2 = struct_value () [concrete] // CHECK:STDOUT: %C.type.0036b9.1: type = fn_type @C.loc35 [concrete] // CHECK:STDOUT: %C.6a91b4.1: %C.type.0036b9.1 = struct_value () [concrete] // CHECK:STDOUT: %C.type.0036b9.2: type = fn_type @C.loc43 [concrete] // CHECK:STDOUT: %C.6a91b4.2: %C.type.0036b9.2 = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: .Int = %Core.Int // CHECK:STDOUT: .Copy = %Core.Copy // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: %Core.Int: %Int.type = import_ref Core//prelude/parts/int, Int, loaded [concrete = constants.%Int.generic] // CHECK:STDOUT: %Core.Copy: type = import_ref Core//prelude/parts/copy, Copy, loaded [concrete = constants.%Copy.type] // CHECK:STDOUT: %Core.import_ref.18d: @Int.as.Copy.impl.%Int.as.Copy.impl.Op.type (%Int.as.Copy.impl.Op.type.824) = import_ref Core//prelude/parts/int, loc{{\d+_\d+}}, loaded [symbolic = @Int.as.Copy.impl.%Int.as.Copy.impl.Op (constants.%Int.as.Copy.impl.Op.9b9)] // CHECK:STDOUT: %Copy.impl_witness_table.e76 = impl_witness_table (%Core.import_ref.18d), @Int.as.Copy.impl [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .A = %A.decl.loc15 // CHECK:STDOUT: .B = %B.decl.loc25 // CHECK:STDOUT: .C = %C.decl.loc35 // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %A.decl.loc15: %A.type.8165c1.1 = fn_decl @A.loc15 [concrete = constants.%A.8aef9d.1] { // CHECK:STDOUT: %n.patt: %pattern_type.7ce = value_binding_pattern n [concrete] // CHECK:STDOUT: %n.param_patt: %pattern_type.7ce = value_param_pattern %n.patt [concrete] // CHECK:STDOUT: %m.patt: %pattern_type.7ce = value_binding_pattern m [concrete] // CHECK:STDOUT: %m.param_patt: %pattern_type.7ce = value_param_pattern %m.patt [concrete] // CHECK:STDOUT: %return.patt: %pattern_type.7ce = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: %pattern_type.7ce = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %i32.loc15_25: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %.loc15: Core.Form = init_form %i32.loc15_25 [concrete = constants.%.ff5] // CHECK:STDOUT: %n.param: %i32 = value_param call_param0 // CHECK:STDOUT: %i32.loc15_9: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %n: %i32 = value_binding n, %n.param // CHECK:STDOUT: %m.param: %i32 = value_param call_param1 // CHECK:STDOUT: %i32.loc15_17: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %m: %i32 = value_binding m, %m.param // CHECK:STDOUT: %return.param: ref %i32 = out_param call_param2 // CHECK:STDOUT: %return: ref %i32 = return_slot %return.param // CHECK:STDOUT: } // CHECK:STDOUT: %A.decl.loc23: %A.type.8165c1.2 = fn_decl @A.loc23 [concrete = constants.%A.8aef9d.2] { // CHECK:STDOUT: %n.patt: %pattern_type.7ce = value_binding_pattern n [concrete] // CHECK:STDOUT: %n.param_patt: %pattern_type.7ce = value_param_pattern %n.patt [concrete] // CHECK:STDOUT: %m.patt: %pattern_type.7ce = value_binding_pattern m [concrete] // CHECK:STDOUT: %m.param_patt: %pattern_type.7ce = value_param_pattern %m.patt [concrete] // CHECK:STDOUT: %return.patt: %pattern_type.7ce = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: %pattern_type.7ce = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %i32.loc23_32: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %.loc23: Core.Form = init_form %i32.loc23_32 [concrete = constants.%.ff5] // CHECK:STDOUT: %n.param: %i32 = value_param call_param0 // CHECK:STDOUT: %i32.loc23_9: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %n: %i32 = value_binding n, %n.param // CHECK:STDOUT: %m.param: %i32 = value_param call_param1 // CHECK:STDOUT: %i32.loc23_24: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %m: %i32 = value_binding m, %m.param // CHECK:STDOUT: %return.param: ref %i32 = out_param call_param2 // CHECK:STDOUT: %return: ref %i32 = return_slot %return.param // CHECK:STDOUT: } // CHECK:STDOUT: %B.decl.loc25: %B.type.d6bcfe.1 = fn_decl @B.loc25 [concrete = constants.%B.5d732e.1] { // CHECK:STDOUT: %n.patt: %pattern_type.7ce = value_binding_pattern n [concrete] // CHECK:STDOUT: %n.param_patt: %pattern_type.7ce = value_param_pattern %n.patt [concrete] // CHECK:STDOUT: %m.patt: %pattern_type.7ce = value_binding_pattern m [concrete] // CHECK:STDOUT: %m.param_patt: %pattern_type.7ce = value_param_pattern %m.patt [concrete] // CHECK:STDOUT: %return.patt: %pattern_type.7ce = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: %pattern_type.7ce = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %i32.loc25_32: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %.loc25: Core.Form = init_form %i32.loc25_32 [concrete = constants.%.ff5] // CHECK:STDOUT: %n.param: %i32 = value_param call_param0 // CHECK:STDOUT: %i32.loc25_9: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %n: %i32 = value_binding n, %n.param // CHECK:STDOUT: %m.param: %i32 = value_param call_param1 // CHECK:STDOUT: %i32.loc25_24: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %m: %i32 = value_binding m, %m.param // CHECK:STDOUT: %return.param: ref %i32 = out_param call_param2 // CHECK:STDOUT: %return: ref %i32 = return_slot %return.param // CHECK:STDOUT: } // CHECK:STDOUT: %B.decl.loc33: %B.type.d6bcfe.2 = fn_decl @B.loc33 [concrete = constants.%B.5d732e.2] { // CHECK:STDOUT: %n.patt: %pattern_type.7ce = value_binding_pattern n [concrete] // CHECK:STDOUT: %n.param_patt: %pattern_type.7ce = value_param_pattern %n.patt [concrete] // CHECK:STDOUT: %m.patt: %pattern_type.7ce = value_binding_pattern m [concrete] // CHECK:STDOUT: %m.param_patt: %pattern_type.7ce = value_param_pattern %m.patt [concrete] // CHECK:STDOUT: %return.patt: %pattern_type.7ce = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: %pattern_type.7ce = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %i32.loc33_25: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %.loc33: Core.Form = init_form %i32.loc33_25 [concrete = constants.%.ff5] // CHECK:STDOUT: %n.param: %i32 = value_param call_param0 // CHECK:STDOUT: %i32.loc33_9: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %n: %i32 = value_binding n, %n.param // CHECK:STDOUT: %m.param: %i32 = value_param call_param1 // CHECK:STDOUT: %i32.loc33_17: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %m: %i32 = value_binding m, %m.param // CHECK:STDOUT: %return.param: ref %i32 = out_param call_param2 // CHECK:STDOUT: %return: ref %i32 = return_slot %return.param // CHECK:STDOUT: } // CHECK:STDOUT: %C.decl.loc35: %C.type.0036b9.1 = fn_decl @C.loc35 [concrete = constants.%C.6a91b4.1] { // CHECK:STDOUT: %n.patt: %pattern_type.7ce = value_binding_pattern n [concrete] // CHECK:STDOUT: %n.param_patt: %pattern_type.7ce = value_param_pattern %n.patt [concrete] // CHECK:STDOUT: %m.patt: %pattern_type.7ce = value_binding_pattern m [concrete] // CHECK:STDOUT: %m.param_patt: %pattern_type.7ce = value_param_pattern %m.patt [concrete] // CHECK:STDOUT: %return.patt: %pattern_type.7ce = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: %pattern_type.7ce = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %i32.loc35_25: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %.loc35: Core.Form = init_form %i32.loc35_25 [concrete = constants.%.ff5] // CHECK:STDOUT: %n.param: %i32 = value_param call_param0 // CHECK:STDOUT: %i32.loc35_9: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %n: %i32 = value_binding n, %n.param // CHECK:STDOUT: %m.param: %i32 = value_param call_param1 // CHECK:STDOUT: %i32.loc35_17: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %m: %i32 = value_binding m, %m.param // CHECK:STDOUT: %return.param: ref %i32 = out_param call_param2 // CHECK:STDOUT: %return: ref %i32 = return_slot %return.param // CHECK:STDOUT: } // CHECK:STDOUT: %C.decl.loc43: %C.type.0036b9.2 = fn_decl @C.loc43 [concrete = constants.%C.6a91b4.2] { // CHECK:STDOUT: %n.patt: %pattern_type.7ce = value_binding_pattern n [concrete] // CHECK:STDOUT: %n.param_patt: %pattern_type.7ce = value_param_pattern %n.patt [concrete] // CHECK:STDOUT: %m.patt: %pattern_type.7ce = value_binding_pattern m [concrete] // CHECK:STDOUT: %m.param_patt: %pattern_type.7ce = value_param_pattern %m.patt [concrete] // CHECK:STDOUT: %return.patt: %pattern_type.7ce = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: %pattern_type.7ce = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %i32.loc43_25: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %.loc43: Core.Form = init_form %i32.loc43_25 [concrete = constants.%.ff5] // CHECK:STDOUT: %n.param: %i32 = value_param call_param0 // CHECK:STDOUT: %i32.loc43_9: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %n: %i32 = value_binding n, %n.param // CHECK:STDOUT: %m.param: %i32 = value_param call_param1 // CHECK:STDOUT: %i32.loc43_17: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %m: %i32 = value_binding m, %m.param // CHECK:STDOUT: %return.param: ref %i32 = out_param call_param2 // CHECK:STDOUT: %return: ref %i32 = return_slot %return.param // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @A.loc15(%n.param: %i32, %m.param: %i32) -> out %return.param: %i32 = "int.sadd"; // CHECK:STDOUT: // CHECK:STDOUT: fn @A.loc23(%n.param: %i32, %m.param: %i32) -> out %return.param: %i32 { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %n.ref: %i32 = name_ref n, %n // CHECK:STDOUT: %impl.elem0: %.8e2 = impl_witness_access constants.%Copy.impl_witness.f17, element0 [concrete = constants.%Int.as.Copy.impl.Op.664] // CHECK:STDOUT: %bound_method.loc23_45.1: = bound_method %n.ref, %impl.elem0 // CHECK:STDOUT: %specific_fn: = specific_function %impl.elem0, @Int.as.Copy.impl.Op(constants.%int_32) [concrete = constants.%Int.as.Copy.impl.Op.specific_fn] // CHECK:STDOUT: %bound_method.loc23_45.2: = bound_method %n.ref, %specific_fn // CHECK:STDOUT: %Int.as.Copy.impl.Op.call: init %i32 = call %bound_method.loc23_45.2(%n.ref) // CHECK:STDOUT: return %Int.as.Copy.impl.Op.call // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @B.loc25(%n.param: %i32, %m.param: %i32) -> out %return.param: %i32 { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %n.ref: %i32 = name_ref n, %n // CHECK:STDOUT: %impl.elem0: %.8e2 = impl_witness_access constants.%Copy.impl_witness.f17, element0 [concrete = constants.%Int.as.Copy.impl.Op.664] // CHECK:STDOUT: %bound_method.loc25_45.1: = bound_method %n.ref, %impl.elem0 // CHECK:STDOUT: %specific_fn: = specific_function %impl.elem0, @Int.as.Copy.impl.Op(constants.%int_32) [concrete = constants.%Int.as.Copy.impl.Op.specific_fn] // CHECK:STDOUT: %bound_method.loc25_45.2: = bound_method %n.ref, %specific_fn // CHECK:STDOUT: %Int.as.Copy.impl.Op.call: init %i32 = call %bound_method.loc25_45.2(%n.ref) // CHECK:STDOUT: return %Int.as.Copy.impl.Op.call // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @B.loc33(%n.param: %i32, %m.param: %i32) -> out %return.param: %i32 = "int.sadd"; // CHECK:STDOUT: // CHECK:STDOUT: fn @C.loc35(%n.param: %i32, %m.param: %i32) -> out %return.param: %i32 = "int.sadd"; // CHECK:STDOUT: // CHECK:STDOUT: fn @C.loc43(%n.param: %i32, %m.param: %i32) -> out %return.param: %i32 = "int.sadd"; // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/function/builtin/fail_unknown.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/convert.carbon // TODO: Add ranges and switch to "--dump-sem-ir-ranges=only". // EXTRA-ARGS: --dump-sem-ir-ranges=if-present // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/function/builtin/fail_unknown.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/function/builtin/fail_unknown.carbon // CHECK:STDERR: fail_unknown.carbon:[[@LINE+4]]:23: error: unknown builtin function name "unknown.builtin.name" [UnknownBuiltinFunctionName] // CHECK:STDERR: fn UnknownBuiltin() = "unknown.builtin.name"; // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: fn UnknownBuiltin() = "unknown.builtin.name"; // CHECK:STDOUT: --- fail_unknown.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %UnknownBuiltin.type: type = fn_type @UnknownBuiltin [concrete] // CHECK:STDOUT: %UnknownBuiltin: %UnknownBuiltin.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .UnknownBuiltin = %UnknownBuiltin.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %UnknownBuiltin.decl: %UnknownBuiltin.type = fn_decl @UnknownBuiltin [concrete = constants.%UnknownBuiltin] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @UnknownBuiltin(); // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/function/builtin/import.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/none.carbon // TODO: Add ranges and switch to "--dump-sem-ir-ranges=only". // EXTRA-ARGS: --dump-sem-ir-ranges=if-present // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/function/builtin/import.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/function/builtin/import.carbon // --- core.carbon package Core library "[[@TEST_NAME]]"; fn IntLiteral() -> type = "int_literal.make_type"; fn Int(N: IntLiteral()) -> type = "int.make_type_signed"; fn AsI32(a: IntLiteral()) -> i32 = "int.convert_checked"; fn AsIntLiteral(a: i32) -> IntLiteral() = "int.convert_checked"; fn TestAdd(a: i32, b: i32) -> i32 = "int.sadd"; // --- use.carbon import Core library "core"; var arr: array(i32, Core.AsIntLiteral(Core.TestAdd(Core.AsI32(1), Core.AsI32(2)))) = (Core.AsI32(1), Core.AsI32(2), Core.AsI32(3)); // CHECK:STDOUT: --- core.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %.805: Core.Form = init_form type [concrete] // CHECK:STDOUT: %pattern_type.98f: type = pattern_type type [concrete] // CHECK:STDOUT: %IntLiteral.type: type = fn_type @IntLiteral [concrete] // CHECK:STDOUT: %IntLiteral: %IntLiteral.type = struct_value () [concrete] // CHECK:STDOUT: %pattern_type.dc0: type = pattern_type Core.IntLiteral [concrete] // CHECK:STDOUT: %Int.type: type = fn_type @Int [concrete] // CHECK:STDOUT: %Int: %Int.type = struct_value () [concrete] // CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [concrete] // CHECK:STDOUT: %i32.builtin: type = int_type signed, %int_32 [concrete] // CHECK:STDOUT: %.9cb: Core.Form = init_form %i32.builtin [concrete] // CHECK:STDOUT: %pattern_type.956: type = pattern_type %i32.builtin [concrete] // CHECK:STDOUT: %AsI32.type: type = fn_type @AsI32 [concrete] // CHECK:STDOUT: %AsI32: %AsI32.type = struct_value () [concrete] // CHECK:STDOUT: %.f7d: Core.Form = init_form Core.IntLiteral [concrete] // CHECK:STDOUT: %AsIntLiteral.type: type = fn_type @AsIntLiteral [concrete] // CHECK:STDOUT: %AsIntLiteral: %AsIntLiteral.type = struct_value () [concrete] // CHECK:STDOUT: %TestAdd.type: type = fn_type @TestAdd [concrete] // CHECK:STDOUT: %TestAdd: %TestAdd.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .IntLiteral = %IntLiteral.decl // CHECK:STDOUT: .Int = %Int.decl // CHECK:STDOUT: .AsI32 = %AsI32.decl // CHECK:STDOUT: .AsIntLiteral = %AsIntLiteral.decl // CHECK:STDOUT: .TestAdd = %TestAdd.decl // CHECK:STDOUT: } // CHECK:STDOUT: %IntLiteral.decl: %IntLiteral.type = fn_decl @IntLiteral [concrete = constants.%IntLiteral] { // CHECK:STDOUT: %return.patt: %pattern_type.98f = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: %pattern_type.98f = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %.loc5_20.1: type = type_literal type [concrete = type] // CHECK:STDOUT: %.loc5_20.2: Core.Form = init_form %.loc5_20.1 [concrete = constants.%.805] // CHECK:STDOUT: %return.param: ref type = out_param call_param0 // CHECK:STDOUT: %return: ref type = return_slot %return.param // CHECK:STDOUT: } // CHECK:STDOUT: %Int.decl: %Int.type = fn_decl @Int [concrete = constants.%Int] { // CHECK:STDOUT: %N.patt: %pattern_type.dc0 = value_binding_pattern N [concrete] // CHECK:STDOUT: %N.param_patt: %pattern_type.dc0 = value_param_pattern %N.patt [concrete] // CHECK:STDOUT: %return.patt: %pattern_type.98f = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: %pattern_type.98f = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %.loc6_28.1: type = type_literal type [concrete = type] // CHECK:STDOUT: %.loc6_28.2: Core.Form = init_form %.loc6_28.1 [concrete = constants.%.805] // CHECK:STDOUT: %N.param: Core.IntLiteral = value_param call_param0 // CHECK:STDOUT: %.loc6_22.1: type = splice_block %.loc6_22.3 [concrete = Core.IntLiteral] { // CHECK:STDOUT: %IntLiteral.ref: %IntLiteral.type = name_ref IntLiteral, file.%IntLiteral.decl [concrete = constants.%IntLiteral] // CHECK:STDOUT: %IntLiteral.call: init type = call %IntLiteral.ref() [concrete = Core.IntLiteral] // CHECK:STDOUT: %.loc6_22.2: type = value_of_initializer %IntLiteral.call [concrete = Core.IntLiteral] // CHECK:STDOUT: %.loc6_22.3: type = converted %IntLiteral.call, %.loc6_22.2 [concrete = Core.IntLiteral] // CHECK:STDOUT: } // CHECK:STDOUT: %N: Core.IntLiteral = value_binding N, %N.param // CHECK:STDOUT: %return.param: ref type = out_param call_param1 // CHECK:STDOUT: %return: ref type = return_slot %return.param // CHECK:STDOUT: } // CHECK:STDOUT: %AsI32.decl: %AsI32.type = fn_decl @AsI32 [concrete = constants.%AsI32] { // CHECK:STDOUT: %a.patt: %pattern_type.dc0 = value_binding_pattern a [concrete] // CHECK:STDOUT: %a.param_patt: %pattern_type.dc0 = value_param_pattern %a.patt [concrete] // CHECK:STDOUT: %return.patt: %pattern_type.956 = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: %pattern_type.956 = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %.loc8_30.1: type = type_literal constants.%i32.builtin [concrete = constants.%i32.builtin] // CHECK:STDOUT: %.loc8_30.2: Core.Form = init_form %.loc8_30.1 [concrete = constants.%.9cb] // CHECK:STDOUT: %a.param: Core.IntLiteral = value_param call_param0 // CHECK:STDOUT: %.loc8_24.1: type = splice_block %.loc8_24.3 [concrete = Core.IntLiteral] { // CHECK:STDOUT: %IntLiteral.ref: %IntLiteral.type = name_ref IntLiteral, file.%IntLiteral.decl [concrete = constants.%IntLiteral] // CHECK:STDOUT: %IntLiteral.call: init type = call %IntLiteral.ref() [concrete = Core.IntLiteral] // CHECK:STDOUT: %.loc8_24.2: type = value_of_initializer %IntLiteral.call [concrete = Core.IntLiteral] // CHECK:STDOUT: %.loc8_24.3: type = converted %IntLiteral.call, %.loc8_24.2 [concrete = Core.IntLiteral] // CHECK:STDOUT: } // CHECK:STDOUT: %a: Core.IntLiteral = value_binding a, %a.param // CHECK:STDOUT: %return.param: ref %i32.builtin = out_param call_param1 // CHECK:STDOUT: %return: ref %i32.builtin = return_slot %return.param // CHECK:STDOUT: } // CHECK:STDOUT: %AsIntLiteral.decl: %AsIntLiteral.type = fn_decl @AsIntLiteral [concrete = constants.%AsIntLiteral] { // CHECK:STDOUT: %a.patt: %pattern_type.956 = value_binding_pattern a [concrete] // CHECK:STDOUT: %a.param_patt: %pattern_type.956 = value_param_pattern %a.patt [concrete] // CHECK:STDOUT: %return.patt: %pattern_type.dc0 = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: %pattern_type.dc0 = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %IntLiteral.ref: %IntLiteral.type = name_ref IntLiteral, file.%IntLiteral.decl [concrete = constants.%IntLiteral] // CHECK:STDOUT: %IntLiteral.call: init type = call %IntLiteral.ref() [concrete = Core.IntLiteral] // CHECK:STDOUT: %.loc9_39.1: type = value_of_initializer %IntLiteral.call [concrete = Core.IntLiteral] // CHECK:STDOUT: %.loc9_39.2: type = converted %IntLiteral.call, %.loc9_39.1 [concrete = Core.IntLiteral] // CHECK:STDOUT: %.loc9_39.3: Core.Form = init_form %.loc9_39.2 [concrete = constants.%.f7d] // CHECK:STDOUT: %a.param: %i32.builtin = value_param call_param0 // CHECK:STDOUT: %.loc9_20: type = type_literal constants.%i32.builtin [concrete = constants.%i32.builtin] // CHECK:STDOUT: %a: %i32.builtin = value_binding a, %a.param // CHECK:STDOUT: %return.param: ref Core.IntLiteral = out_param call_param1 // CHECK:STDOUT: %return: ref Core.IntLiteral = return_slot %return.param // CHECK:STDOUT: } // CHECK:STDOUT: %TestAdd.decl: %TestAdd.type = fn_decl @TestAdd [concrete = constants.%TestAdd] { // CHECK:STDOUT: %a.patt: %pattern_type.956 = value_binding_pattern a [concrete] // CHECK:STDOUT: %a.param_patt: %pattern_type.956 = value_param_pattern %a.patt [concrete] // CHECK:STDOUT: %b.patt: %pattern_type.956 = value_binding_pattern b [concrete] // CHECK:STDOUT: %b.param_patt: %pattern_type.956 = value_param_pattern %b.patt [concrete] // CHECK:STDOUT: %return.patt: %pattern_type.956 = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: %pattern_type.956 = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %.loc11_31.1: type = type_literal constants.%i32.builtin [concrete = constants.%i32.builtin] // CHECK:STDOUT: %.loc11_31.2: Core.Form = init_form %.loc11_31.1 [concrete = constants.%.9cb] // CHECK:STDOUT: %a.param: %i32.builtin = value_param call_param0 // CHECK:STDOUT: %.loc11_15: type = type_literal constants.%i32.builtin [concrete = constants.%i32.builtin] // CHECK:STDOUT: %a: %i32.builtin = value_binding a, %a.param // CHECK:STDOUT: %b.param: %i32.builtin = value_param call_param1 // CHECK:STDOUT: %.loc11_23: type = type_literal constants.%i32.builtin [concrete = constants.%i32.builtin] // CHECK:STDOUT: %b: %i32.builtin = value_binding b, %b.param // CHECK:STDOUT: %return.param: ref %i32.builtin = out_param call_param2 // CHECK:STDOUT: %return: ref %i32.builtin = return_slot %return.param // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @IntLiteral() -> out %return.param: type = "int_literal.make_type"; // CHECK:STDOUT: // CHECK:STDOUT: fn @Int(%N.param: Core.IntLiteral) -> out %return.param: type = "int.make_type_signed"; // CHECK:STDOUT: // CHECK:STDOUT: fn @AsI32(%a.param: Core.IntLiteral) -> out %return.param: %i32.builtin = "int.convert_checked"; // CHECK:STDOUT: // CHECK:STDOUT: fn @AsIntLiteral(%a.param: %i32.builtin) -> out %return.param: Core.IntLiteral = "int.convert_checked"; // CHECK:STDOUT: // CHECK:STDOUT: fn @TestAdd(%a.param: %i32.builtin, %b.param: %i32.builtin) -> out %return.param: %i32.builtin = "int.sadd"; // CHECK:STDOUT: // CHECK:STDOUT: --- use.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [concrete] // CHECK:STDOUT: %Int.type: type = fn_type @Int [concrete] // CHECK:STDOUT: %Int: %Int.type = struct_value () [concrete] // CHECK:STDOUT: %i32.builtin: type = int_type signed, %int_32 [concrete] // CHECK:STDOUT: %AsIntLiteral.type: type = fn_type @AsIntLiteral [concrete] // CHECK:STDOUT: %AsIntLiteral: %AsIntLiteral.type = struct_value () [concrete] // CHECK:STDOUT: %TestAdd.type: type = fn_type @TestAdd [concrete] // CHECK:STDOUT: %TestAdd: %TestAdd.type = struct_value () [concrete] // CHECK:STDOUT: %AsI32.type: type = fn_type @AsI32 [concrete] // CHECK:STDOUT: %AsI32: %AsI32.type = struct_value () [concrete] // CHECK:STDOUT: %int_1.5b8: Core.IntLiteral = int_value 1 [concrete] // CHECK:STDOUT: %int_1.f38: %i32.builtin = int_value 1 [concrete] // CHECK:STDOUT: %int_2.ecc: Core.IntLiteral = int_value 2 [concrete] // CHECK:STDOUT: %int_2.5a1: %i32.builtin = int_value 2 [concrete] // CHECK:STDOUT: %int_3.a0f: %i32.builtin = int_value 3 [concrete] // CHECK:STDOUT: %int_3.1ba: Core.IntLiteral = int_value 3 [concrete] // CHECK:STDOUT: %array_type: type = array_type %int_3.1ba, %i32.builtin [concrete] // CHECK:STDOUT: %pattern_type.9e2: type = pattern_type %array_type [concrete] // CHECK:STDOUT: %tuple.type: type = tuple_type (%i32.builtin, %i32.builtin, %i32.builtin) [concrete] // CHECK:STDOUT: %tuple: %tuple.type = tuple_value (%int_1.f38, %int_2.5a1, %int_3.a0f) [concrete] // CHECK:STDOUT: %int_0: Core.IntLiteral = int_value 0 [concrete] // CHECK:STDOUT: %array: %array_type = tuple_value (%int_1.f38, %int_2.5a1, %int_3.a0f) [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: .Int = %Core.Int // CHECK:STDOUT: .AsIntLiteral = %Core.AsIntLiteral // CHECK:STDOUT: .TestAdd = %Core.TestAdd // CHECK:STDOUT: .AsI32 = %Core.AsI32 // CHECK:STDOUT: import Core//core // CHECK:STDOUT: } // CHECK:STDOUT: %Core.Int: %Int.type = import_ref Core//core, Int, loaded [concrete = constants.%Int] // CHECK:STDOUT: %Core.AsIntLiteral: %AsIntLiteral.type = import_ref Core//core, AsIntLiteral, loaded [concrete = constants.%AsIntLiteral] // CHECK:STDOUT: %Core.TestAdd: %TestAdd.type = import_ref Core//core, TestAdd, loaded [concrete = constants.%TestAdd] // CHECK:STDOUT: %Core.AsI32: %AsI32.type = import_ref Core//core, AsI32, loaded [concrete = constants.%AsI32] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .arr = %arr // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %arr.patt: %pattern_type.9e2 = ref_binding_pattern arr [concrete] // CHECK:STDOUT: %arr.var_patt: %pattern_type.9e2 = var_pattern %arr.patt [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %arr.var: ref %array_type = var %arr.var_patt [concrete] // CHECK:STDOUT: %.loc4_82: type = splice_block %array_type [concrete = constants.%array_type] { // CHECK:STDOUT: %.loc4_16: type = type_literal constants.%i32.builtin [concrete = constants.%i32.builtin] // CHECK:STDOUT: %Core.ref.loc4_21: = name_ref Core, imports.%Core [concrete = imports.%Core] // CHECK:STDOUT: %AsIntLiteral.ref: %AsIntLiteral.type = name_ref AsIntLiteral, imports.%Core.AsIntLiteral [concrete = constants.%AsIntLiteral] // CHECK:STDOUT: %Core.ref.loc4_39: = name_ref Core, imports.%Core [concrete = imports.%Core] // CHECK:STDOUT: %TestAdd.ref: %TestAdd.type = name_ref TestAdd, imports.%Core.TestAdd [concrete = constants.%TestAdd] // CHECK:STDOUT: %Core.ref.loc4_52: = name_ref Core, imports.%Core [concrete = imports.%Core] // CHECK:STDOUT: %AsI32.ref.loc4_56: %AsI32.type = name_ref AsI32, imports.%Core.AsI32 [concrete = constants.%AsI32] // CHECK:STDOUT: %int_1: Core.IntLiteral = int_value 1 [concrete = constants.%int_1.5b8] // CHECK:STDOUT: %AsI32.call.loc4_64: init %i32.builtin = call %AsI32.ref.loc4_56(%int_1) [concrete = constants.%int_1.f38] // CHECK:STDOUT: %Core.ref.loc4_67: = name_ref Core, imports.%Core [concrete = imports.%Core] // CHECK:STDOUT: %AsI32.ref.loc4_71: %AsI32.type = name_ref AsI32, imports.%Core.AsI32 [concrete = constants.%AsI32] // CHECK:STDOUT: %int_2: Core.IntLiteral = int_value 2 [concrete = constants.%int_2.ecc] // CHECK:STDOUT: %AsI32.call.loc4_79: init %i32.builtin = call %AsI32.ref.loc4_71(%int_2) [concrete = constants.%int_2.5a1] // CHECK:STDOUT: %.loc4_64.1: %i32.builtin = value_of_initializer %AsI32.call.loc4_64 [concrete = constants.%int_1.f38] // CHECK:STDOUT: %.loc4_64.2: %i32.builtin = converted %AsI32.call.loc4_64, %.loc4_64.1 [concrete = constants.%int_1.f38] // CHECK:STDOUT: %.loc4_79.1: %i32.builtin = value_of_initializer %AsI32.call.loc4_79 [concrete = constants.%int_2.5a1] // CHECK:STDOUT: %.loc4_79.2: %i32.builtin = converted %AsI32.call.loc4_79, %.loc4_79.1 [concrete = constants.%int_2.5a1] // CHECK:STDOUT: %TestAdd.call: init %i32.builtin = call %TestAdd.ref(%.loc4_64.2, %.loc4_79.2) [concrete = constants.%int_3.a0f] // CHECK:STDOUT: %.loc4_80.1: %i32.builtin = value_of_initializer %TestAdd.call [concrete = constants.%int_3.a0f] // CHECK:STDOUT: %.loc4_80.2: %i32.builtin = converted %TestAdd.call, %.loc4_80.1 [concrete = constants.%int_3.a0f] // CHECK:STDOUT: %AsIntLiteral.call: init Core.IntLiteral = call %AsIntLiteral.ref(%.loc4_80.2) [concrete = constants.%int_3.1ba] // CHECK:STDOUT: %.loc4_81.1: Core.IntLiteral = value_of_initializer %AsIntLiteral.call [concrete = constants.%int_3.1ba] // CHECK:STDOUT: %.loc4_81.2: Core.IntLiteral = converted %AsIntLiteral.call, %.loc4_81.1 [concrete = constants.%int_3.1ba] // CHECK:STDOUT: %array_type: type = array_type %.loc4_81.2, %.loc4_16 [concrete = constants.%array_type] // CHECK:STDOUT: } // CHECK:STDOUT: %arr: ref %array_type = ref_binding arr, %arr.var [concrete = %arr.var] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @Int = "int.make_type_signed" [from "core.carbon"]; // CHECK:STDOUT: // CHECK:STDOUT: fn @AsIntLiteral = "int.convert_checked" [from "core.carbon"]; // CHECK:STDOUT: // CHECK:STDOUT: fn @TestAdd = "int.sadd" [from "core.carbon"]; // CHECK:STDOUT: // CHECK:STDOUT: fn @AsI32 = "int.convert_checked" [from "core.carbon"]; // CHECK:STDOUT: // CHECK:STDOUT: fn @__global_init() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %Core.ref.loc4_87: = name_ref Core, imports.%Core [concrete = imports.%Core] // CHECK:STDOUT: %AsI32.ref.loc4_91: %AsI32.type = name_ref AsI32, imports.%Core.AsI32 [concrete = constants.%AsI32] // CHECK:STDOUT: %int_1.loc4_98: Core.IntLiteral = int_value 1 [concrete = constants.%int_1.5b8] // CHECK:STDOUT: %AsI32.call.loc4_99: init %i32.builtin = call %AsI32.ref.loc4_91(%int_1.loc4_98) [concrete = constants.%int_1.f38] // CHECK:STDOUT: %Core.ref.loc4_102: = name_ref Core, imports.%Core [concrete = imports.%Core] // CHECK:STDOUT: %AsI32.ref.loc4_106: %AsI32.type = name_ref AsI32, imports.%Core.AsI32 [concrete = constants.%AsI32] // CHECK:STDOUT: %int_2.loc4_113: Core.IntLiteral = int_value 2 [concrete = constants.%int_2.ecc] // CHECK:STDOUT: %AsI32.call.loc4_114: init %i32.builtin = call %AsI32.ref.loc4_106(%int_2.loc4_113) [concrete = constants.%int_2.5a1] // CHECK:STDOUT: %Core.ref.loc4_117: = name_ref Core, imports.%Core [concrete = imports.%Core] // CHECK:STDOUT: %AsI32.ref.loc4_121: %AsI32.type = name_ref AsI32, imports.%Core.AsI32 [concrete = constants.%AsI32] // CHECK:STDOUT: %int_3: Core.IntLiteral = int_value 3 [concrete = constants.%int_3.1ba] // CHECK:STDOUT: %AsI32.call.loc4_129: init %i32.builtin = call %AsI32.ref.loc4_121(%int_3) [concrete = constants.%int_3.a0f] // CHECK:STDOUT: %.loc4_130.1: %tuple.type = tuple_literal (%AsI32.call.loc4_99, %AsI32.call.loc4_114, %AsI32.call.loc4_129) [concrete = constants.%tuple] // CHECK:STDOUT: %int_0: Core.IntLiteral = int_value 0 [concrete = constants.%int_0] // CHECK:STDOUT: %.loc4_130.2: ref %i32.builtin = array_index file.%arr.var, %int_0 // CHECK:STDOUT: %.loc4_130.3: init %i32.builtin to %.loc4_130.2 = in_place_init %AsI32.call.loc4_99 [concrete = constants.%int_1.f38] // CHECK:STDOUT: %int_1.loc4_130: Core.IntLiteral = int_value 1 [concrete = constants.%int_1.5b8] // CHECK:STDOUT: %.loc4_130.4: ref %i32.builtin = array_index file.%arr.var, %int_1.loc4_130 // CHECK:STDOUT: %.loc4_130.5: init %i32.builtin to %.loc4_130.4 = in_place_init %AsI32.call.loc4_114 [concrete = constants.%int_2.5a1] // CHECK:STDOUT: %int_2.loc4_130: Core.IntLiteral = int_value 2 [concrete = constants.%int_2.ecc] // CHECK:STDOUT: %.loc4_130.6: ref %i32.builtin = array_index file.%arr.var, %int_2.loc4_130 // CHECK:STDOUT: %.loc4_130.7: init %i32.builtin to %.loc4_130.6 = in_place_init %AsI32.call.loc4_129 [concrete = constants.%int_3.a0f] // CHECK:STDOUT: %.loc4_130.8: init %array_type to file.%arr.var = array_init (%.loc4_130.3, %.loc4_130.5, %.loc4_130.7) [concrete = constants.%array] // CHECK:STDOUT: %.loc4_1: init %array_type = converted %.loc4_130.1, %.loc4_130.8 [concrete = constants.%array] // CHECK:STDOUT: assign file.%arr.var, %.loc4_1 // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/function/builtin/method.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/int.carbon // TODO: Add ranges and switch to "--dump-sem-ir-ranges=only". // EXTRA-ARGS: --dump-sem-ir-ranges=if-present // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/function/builtin/method.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/function/builtin/method.carbon interface I { fn F[self: Self](other: Self) -> Self; } impl i32 as I { fn F[self: i32](other: i32) -> i32 = "int.sadd"; } var arr: array(i32, (1 as i32).(I.F)(2)); // CHECK:STDOUT: --- method.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %I.type: type = facet_type <@I> [concrete] // CHECK:STDOUT: %Self.ab9: %I.type = symbolic_binding Self, 0 [symbolic] // CHECK:STDOUT: %Self.binding.as_type.d31: type = symbolic_binding_type Self, 0, %Self.ab9 [symbolic] // CHECK:STDOUT: %pattern_type.fa0: type = pattern_type %Self.binding.as_type.d31 [symbolic] // CHECK:STDOUT: %.e46: Core.Form = init_form %Self.binding.as_type.d31 [symbolic] // CHECK:STDOUT: %I.WithSelf.F.type.08c: type = fn_type @I.WithSelf.F, @I.WithSelf(%Self.ab9) [symbolic] // CHECK:STDOUT: %I.WithSelf.F.705: %I.WithSelf.F.type.08c = struct_value () [symbolic] // CHECK:STDOUT: %I.assoc_type: type = assoc_entity_type @I [concrete] // CHECK:STDOUT: %assoc0.18e: %I.assoc_type = assoc_entity element0, @I.WithSelf.%I.WithSelf.F.decl [concrete] // CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [concrete] // CHECK:STDOUT: %Int.type: type = generic_class_type @Int [concrete] // CHECK:STDOUT: %Int.generic: %Int.type = struct_value () [concrete] // CHECK:STDOUT: %i32: type = class_type @Int, @Int(%int_32) [concrete] // CHECK:STDOUT: %I.impl_witness: = impl_witness @i32.as.I.impl.%I.impl_witness_table [concrete] // CHECK:STDOUT: %pattern_type.7ce: type = pattern_type %i32 [concrete] // CHECK:STDOUT: %.ff5: Core.Form = init_form %i32 [concrete] // CHECK:STDOUT: %i32.as.I.impl.F.type: type = fn_type @i32.as.I.impl.F [concrete] // CHECK:STDOUT: %i32.as.I.impl.F: %i32.as.I.impl.F.type = struct_value () [concrete] // CHECK:STDOUT: %I.facet: %I.type = facet_value %i32, (%I.impl_witness) [concrete] // CHECK:STDOUT: %I.WithSelf.F.type.f37: type = fn_type @I.WithSelf.F, @I.WithSelf(%I.facet) [concrete] // CHECK:STDOUT: %I.WithSelf.F.b05: %I.WithSelf.F.type.f37 = struct_value () [concrete] // CHECK:STDOUT: %int_1.5b8: Core.IntLiteral = int_value 1 [concrete] // CHECK:STDOUT: %As.type.90f: type = generic_interface_type @As [concrete] // CHECK:STDOUT: %As.generic: %As.type.90f = struct_value () [concrete] // CHECK:STDOUT: %As.type.047: type = facet_type <@As, @As(%i32)> [concrete] // CHECK:STDOUT: %To: Core.IntLiteral = symbolic_binding To, 0 [symbolic] // CHECK:STDOUT: %Core.IntLiteral.as.As.impl.Convert.type.09e: type = fn_type @Core.IntLiteral.as.As.impl.Convert, @Core.IntLiteral.as.As.impl(%To) [symbolic] // CHECK:STDOUT: %Core.IntLiteral.as.As.impl.Convert.dbe: %Core.IntLiteral.as.As.impl.Convert.type.09e = struct_value () [symbolic] // CHECK:STDOUT: %From: Core.IntLiteral = symbolic_binding From, 0 [symbolic] // CHECK:STDOUT: %As.impl_witness.ab6: = impl_witness imports.%As.impl_witness_table.9fc, @Core.IntLiteral.as.As.impl(%int_32) [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.As.impl.Convert.type.8ec: type = fn_type @Core.IntLiteral.as.As.impl.Convert, @Core.IntLiteral.as.As.impl(%int_32) [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.As.impl.Convert.29b: %Core.IntLiteral.as.As.impl.Convert.type.8ec = struct_value () [concrete] // CHECK:STDOUT: %As.facet: %As.type.047 = facet_value Core.IntLiteral, (%As.impl_witness.ab6) [concrete] // CHECK:STDOUT: %As.WithSelf.Convert.type.e5b: type = fn_type @As.WithSelf.Convert, @As.WithSelf(%i32, %As.facet) [concrete] // CHECK:STDOUT: %.9ed: type = fn_type_with_self_type %As.WithSelf.Convert.type.e5b, %As.facet [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.As.impl.Convert.bound: = bound_method %int_1.5b8, %Core.IntLiteral.as.As.impl.Convert.29b [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.As.impl.Convert.specific_fn: = specific_function %Core.IntLiteral.as.As.impl.Convert.29b, @Core.IntLiteral.as.As.impl.Convert(%int_32) [concrete] // CHECK:STDOUT: %bound_method.290: = bound_method %int_1.5b8, %Core.IntLiteral.as.As.impl.Convert.specific_fn [concrete] // CHECK:STDOUT: %int_1.5d2: %i32 = int_value 1 [concrete] // CHECK:STDOUT: %.131: type = fn_type_with_self_type %I.WithSelf.F.type.f37, %I.facet [concrete] // CHECK:STDOUT: %i32.as.I.impl.F.bound: = bound_method %int_1.5d2, %i32.as.I.impl.F [concrete] // CHECK:STDOUT: %int_2.ecc: Core.IntLiteral = int_value 2 [concrete] // CHECK:STDOUT: %ImplicitAs.type.cc7: type = generic_interface_type @ImplicitAs [concrete] // CHECK:STDOUT: %ImplicitAs.generic: %ImplicitAs.type.cc7 = struct_value () [concrete] // CHECK:STDOUT: %ImplicitAs.type.e8c: type = facet_type <@ImplicitAs, @ImplicitAs(%i32)> [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.4e6: type = fn_type @Core.IntLiteral.as.ImplicitAs.impl.Convert, @Core.IntLiteral.as.ImplicitAs.impl(%To) [symbolic] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.3c2: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.4e6 = struct_value () [symbolic] // CHECK:STDOUT: %ImplicitAs.type.139: type = facet_type <@ImplicitAs, @ImplicitAs(Core.IntLiteral)> [concrete] // CHECK:STDOUT: %Int.as.ImplicitAs.impl.Convert.type.2ed: type = fn_type @Int.as.ImplicitAs.impl.Convert, @Int.as.ImplicitAs.impl(%From) [symbolic] // CHECK:STDOUT: %Int.as.ImplicitAs.impl.Convert.d29: %Int.as.ImplicitAs.impl.Convert.type.2ed = struct_value () [symbolic] // CHECK:STDOUT: %ImplicitAs.impl_witness.6bc: = impl_witness imports.%ImplicitAs.impl_witness_table.74f, @Core.IntLiteral.as.ImplicitAs.impl(%int_32) [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.e0d: type = fn_type @Core.IntLiteral.as.ImplicitAs.impl.Convert, @Core.IntLiteral.as.ImplicitAs.impl(%int_32) [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.e0d = struct_value () [concrete] // CHECK:STDOUT: %ImplicitAs.facet.b94: %ImplicitAs.type.e8c = facet_value Core.IntLiteral, (%ImplicitAs.impl_witness.6bc) [concrete] // CHECK:STDOUT: %ImplicitAs.WithSelf.Convert.type.b37: type = fn_type @ImplicitAs.WithSelf.Convert, @ImplicitAs.WithSelf(%i32, %ImplicitAs.facet.b94) [concrete] // CHECK:STDOUT: %.545: type = fn_type_with_self_type %ImplicitAs.WithSelf.Convert.type.b37, %ImplicitAs.facet.b94 [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.bound: = bound_method %int_2.ecc, %Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5 [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn: = specific_function %Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5, @Core.IntLiteral.as.ImplicitAs.impl.Convert(%int_32) [concrete] // CHECK:STDOUT: %bound_method.646: = bound_method %int_2.ecc, %Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn [concrete] // CHECK:STDOUT: %int_2.ef8: %i32 = int_value 2 [concrete] // CHECK:STDOUT: %int_3.822: %i32 = int_value 3 [concrete] // CHECK:STDOUT: %ImplicitAs.impl_witness.640: = impl_witness imports.%ImplicitAs.impl_witness_table.ea2, @Int.as.ImplicitAs.impl(%int_32) [concrete] // CHECK:STDOUT: %Int.as.ImplicitAs.impl.Convert.type.240: type = fn_type @Int.as.ImplicitAs.impl.Convert, @Int.as.ImplicitAs.impl(%int_32) [concrete] // CHECK:STDOUT: %Int.as.ImplicitAs.impl.Convert.dd4: %Int.as.ImplicitAs.impl.Convert.type.240 = struct_value () [concrete] // CHECK:STDOUT: %ImplicitAs.facet.290: %ImplicitAs.type.139 = facet_value %i32, (%ImplicitAs.impl_witness.640) [concrete] // CHECK:STDOUT: %ImplicitAs.WithSelf.Convert.type.462: type = fn_type @ImplicitAs.WithSelf.Convert, @ImplicitAs.WithSelf(Core.IntLiteral, %ImplicitAs.facet.290) [concrete] // CHECK:STDOUT: %.0a7: type = fn_type_with_self_type %ImplicitAs.WithSelf.Convert.type.462, %ImplicitAs.facet.290 [concrete] // CHECK:STDOUT: %Int.as.ImplicitAs.impl.Convert.bound: = bound_method %int_3.822, %Int.as.ImplicitAs.impl.Convert.dd4 [concrete] // CHECK:STDOUT: %Int.as.ImplicitAs.impl.Convert.specific_fn: = specific_function %Int.as.ImplicitAs.impl.Convert.dd4, @Int.as.ImplicitAs.impl.Convert(%int_32) [concrete] // CHECK:STDOUT: %bound_method.17a: = bound_method %int_3.822, %Int.as.ImplicitAs.impl.Convert.specific_fn [concrete] // CHECK:STDOUT: %int_3.1ba: Core.IntLiteral = int_value 3 [concrete] // CHECK:STDOUT: %array_type: type = array_type %int_3.1ba, %i32 [concrete] // CHECK:STDOUT: %pattern_type.5d8: type = pattern_type %array_type [concrete] // CHECK:STDOUT: %DefaultOrUnformed.type: type = facet_type <@DefaultOrUnformed> [concrete] // CHECK:STDOUT: %T: type = symbolic_binding T, 0 [symbolic] // CHECK:STDOUT: %T.as.DefaultOrUnformed.impl.Op.type.6c6: type = fn_type @T.as.DefaultOrUnformed.impl.Op, @T.as.DefaultOrUnformed.impl(%T) [symbolic] // CHECK:STDOUT: %T.as.DefaultOrUnformed.impl.Op.ae1: %T.as.DefaultOrUnformed.impl.Op.type.6c6 = struct_value () [symbolic] // CHECK:STDOUT: %DefaultOrUnformed.impl_witness.d11: = impl_witness imports.%DefaultOrUnformed.impl_witness_table, @T.as.DefaultOrUnformed.impl(%array_type) [concrete] // CHECK:STDOUT: %T.as.DefaultOrUnformed.impl.Op.type.576: type = fn_type @T.as.DefaultOrUnformed.impl.Op, @T.as.DefaultOrUnformed.impl(%array_type) [concrete] // CHECK:STDOUT: %T.as.DefaultOrUnformed.impl.Op.9c0: %T.as.DefaultOrUnformed.impl.Op.type.576 = struct_value () [concrete] // CHECK:STDOUT: %DefaultOrUnformed.facet: %DefaultOrUnformed.type = facet_value %array_type, (%DefaultOrUnformed.impl_witness.d11) [concrete] // CHECK:STDOUT: %T.as.DefaultOrUnformed.impl.Op.specific_fn: = specific_function %T.as.DefaultOrUnformed.impl.Op.9c0, @T.as.DefaultOrUnformed.impl.Op(%array_type) [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: .Int = %Core.Int // CHECK:STDOUT: .As = %Core.As // CHECK:STDOUT: .ImplicitAs = %Core.ImplicitAs // CHECK:STDOUT: .DefaultOrUnformed = %Core.DefaultOrUnformed // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: %Core.Int: %Int.type = import_ref Core//prelude/parts/int, Int, loaded [concrete = constants.%Int.generic] // CHECK:STDOUT: %Core.As: %As.type.90f = import_ref Core//prelude/parts/as, As, loaded [concrete = constants.%As.generic] // CHECK:STDOUT: %Core.import_ref.ca0: @Core.IntLiteral.as.As.impl.%Core.IntLiteral.as.As.impl.Convert.type (%Core.IntLiteral.as.As.impl.Convert.type.09e) = import_ref Core//prelude/parts/int, loc{{\d+_\d+}}, loaded [symbolic = @Core.IntLiteral.as.As.impl.%Core.IntLiteral.as.As.impl.Convert (constants.%Core.IntLiteral.as.As.impl.Convert.dbe)] // CHECK:STDOUT: %As.impl_witness_table.9fc = impl_witness_table (%Core.import_ref.ca0), @Core.IntLiteral.as.As.impl [concrete] // CHECK:STDOUT: %Core.ImplicitAs: %ImplicitAs.type.cc7 = import_ref Core//prelude/parts/as, ImplicitAs, loaded [concrete = constants.%ImplicitAs.generic] // CHECK:STDOUT: %Core.import_ref.42d: @Core.IntLiteral.as.ImplicitAs.impl.%Core.IntLiteral.as.ImplicitAs.impl.Convert.type (%Core.IntLiteral.as.ImplicitAs.impl.Convert.type.4e6) = import_ref Core//prelude/parts/int, loc{{\d+_\d+}}, loaded [symbolic = @Core.IntLiteral.as.ImplicitAs.impl.%Core.IntLiteral.as.ImplicitAs.impl.Convert (constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.3c2)] // CHECK:STDOUT: %ImplicitAs.impl_witness_table.74f = impl_witness_table (%Core.import_ref.42d), @Core.IntLiteral.as.ImplicitAs.impl [concrete] // CHECK:STDOUT: %Core.import_ref.0bc: @Int.as.ImplicitAs.impl.%Int.as.ImplicitAs.impl.Convert.type (%Int.as.ImplicitAs.impl.Convert.type.2ed) = import_ref Core//prelude/parts/int, loc{{\d+_\d+}}, loaded [symbolic = @Int.as.ImplicitAs.impl.%Int.as.ImplicitAs.impl.Convert (constants.%Int.as.ImplicitAs.impl.Convert.d29)] // CHECK:STDOUT: %ImplicitAs.impl_witness_table.ea2 = impl_witness_table (%Core.import_ref.0bc), @Int.as.ImplicitAs.impl [concrete] // CHECK:STDOUT: %Core.DefaultOrUnformed: type = import_ref Core//prelude/parts/default, DefaultOrUnformed, loaded [concrete = constants.%DefaultOrUnformed.type] // CHECK:STDOUT: %Core.import_ref.01d: @T.as.DefaultOrUnformed.impl.%T.as.DefaultOrUnformed.impl.Op.type (%T.as.DefaultOrUnformed.impl.Op.type.6c6) = import_ref Core//prelude/parts/default, loc{{\d+_\d+}}, loaded [symbolic = @T.as.DefaultOrUnformed.impl.%T.as.DefaultOrUnformed.impl.Op (constants.%T.as.DefaultOrUnformed.impl.Op.ae1)] // CHECK:STDOUT: %DefaultOrUnformed.impl_witness_table = impl_witness_table (%Core.import_ref.01d), @T.as.DefaultOrUnformed.impl [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .I = %I.decl // CHECK:STDOUT: .arr = %arr // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %I.decl: type = interface_decl @I [concrete = constants.%I.type] {} {} // CHECK:STDOUT: impl_decl @i32.as.I.impl [concrete] {} { // CHECK:STDOUT: %i32: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %I.ref: type = name_ref I, file.%I.decl [concrete = constants.%I.type] // CHECK:STDOUT: } // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %arr.patt: %pattern_type.5d8 = ref_binding_pattern arr [concrete] // CHECK:STDOUT: %arr.var_patt: %pattern_type.5d8 = var_pattern %arr.patt [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %arr.var: ref %array_type = var %arr.var_patt [concrete] // CHECK:STDOUT: %.loc23_40: type = splice_block %array_type [concrete = constants.%array_type] { // CHECK:STDOUT: %i32.loc23_16: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %int_1: Core.IntLiteral = int_value 1 [concrete = constants.%int_1.5b8] // CHECK:STDOUT: %i32.loc23_27: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %impl.elem0.loc23_24: %.9ed = impl_witness_access constants.%As.impl_witness.ab6, element0 [concrete = constants.%Core.IntLiteral.as.As.impl.Convert.29b] // CHECK:STDOUT: %bound_method.loc23_24.1: = bound_method %int_1, %impl.elem0.loc23_24 [concrete = constants.%Core.IntLiteral.as.As.impl.Convert.bound] // CHECK:STDOUT: %specific_fn.loc23_24: = specific_function %impl.elem0.loc23_24, @Core.IntLiteral.as.As.impl.Convert(constants.%int_32) [concrete = constants.%Core.IntLiteral.as.As.impl.Convert.specific_fn] // CHECK:STDOUT: %bound_method.loc23_24.2: = bound_method %int_1, %specific_fn.loc23_24 [concrete = constants.%bound_method.290] // CHECK:STDOUT: %Core.IntLiteral.as.As.impl.Convert.call: init %i32 = call %bound_method.loc23_24.2(%int_1) [concrete = constants.%int_1.5d2] // CHECK:STDOUT: %.loc23_24.1: %i32 = value_of_initializer %Core.IntLiteral.as.As.impl.Convert.call [concrete = constants.%int_1.5d2] // CHECK:STDOUT: %.loc23_24.2: %i32 = converted %int_1, %.loc23_24.1 [concrete = constants.%int_1.5d2] // CHECK:STDOUT: %I.ref: type = name_ref I, %I.decl [concrete = constants.%I.type] // CHECK:STDOUT: %F.ref: %I.assoc_type = name_ref F, @I.WithSelf.%assoc0 [concrete = constants.%assoc0.18e] // CHECK:STDOUT: %impl.elem0.loc23_31: %.131 = impl_witness_access constants.%I.impl_witness, element0 [concrete = constants.%i32.as.I.impl.F] // CHECK:STDOUT: %bound_method.loc23_31: = bound_method %.loc23_24.2, %impl.elem0.loc23_31 [concrete = constants.%i32.as.I.impl.F.bound] // CHECK:STDOUT: %int_2: Core.IntLiteral = int_value 2 [concrete = constants.%int_2.ecc] // CHECK:STDOUT: %impl.elem0.loc23_38: %.545 = impl_witness_access constants.%ImplicitAs.impl_witness.6bc, element0 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5] // CHECK:STDOUT: %bound_method.loc23_38.1: = bound_method %int_2, %impl.elem0.loc23_38 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.bound] // CHECK:STDOUT: %specific_fn.loc23_38: = specific_function %impl.elem0.loc23_38, @Core.IntLiteral.as.ImplicitAs.impl.Convert(constants.%int_32) [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn] // CHECK:STDOUT: %bound_method.loc23_38.2: = bound_method %int_2, %specific_fn.loc23_38 [concrete = constants.%bound_method.646] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.call: init %i32 = call %bound_method.loc23_38.2(%int_2) [concrete = constants.%int_2.ef8] // CHECK:STDOUT: %.loc23_38.1: %i32 = value_of_initializer %Core.IntLiteral.as.ImplicitAs.impl.Convert.call [concrete = constants.%int_2.ef8] // CHECK:STDOUT: %.loc23_38.2: %i32 = converted %int_2, %.loc23_38.1 [concrete = constants.%int_2.ef8] // CHECK:STDOUT: %i32.as.I.impl.F.call: init %i32 = call %bound_method.loc23_31(%.loc23_24.2, %.loc23_38.2) [concrete = constants.%int_3.822] // CHECK:STDOUT: %impl.elem0.loc23_39: %.0a7 = impl_witness_access constants.%ImplicitAs.impl_witness.640, element0 [concrete = constants.%Int.as.ImplicitAs.impl.Convert.dd4] // CHECK:STDOUT: %bound_method.loc23_39.1: = bound_method %i32.as.I.impl.F.call, %impl.elem0.loc23_39 [concrete = constants.%Int.as.ImplicitAs.impl.Convert.bound] // CHECK:STDOUT: %specific_fn.loc23_39: = specific_function %impl.elem0.loc23_39, @Int.as.ImplicitAs.impl.Convert(constants.%int_32) [concrete = constants.%Int.as.ImplicitAs.impl.Convert.specific_fn] // CHECK:STDOUT: %bound_method.loc23_39.2: = bound_method %i32.as.I.impl.F.call, %specific_fn.loc23_39 [concrete = constants.%bound_method.17a] // CHECK:STDOUT: %.loc23_39.1: %i32 = value_of_initializer %i32.as.I.impl.F.call [concrete = constants.%int_3.822] // CHECK:STDOUT: %.loc23_39.2: %i32 = converted %i32.as.I.impl.F.call, %.loc23_39.1 [concrete = constants.%int_3.822] // CHECK:STDOUT: %Int.as.ImplicitAs.impl.Convert.call: init Core.IntLiteral = call %bound_method.loc23_39.2(%.loc23_39.2) [concrete = constants.%int_3.1ba] // CHECK:STDOUT: %.loc23_39.3: Core.IntLiteral = value_of_initializer %Int.as.ImplicitAs.impl.Convert.call [concrete = constants.%int_3.1ba] // CHECK:STDOUT: %.loc23_39.4: Core.IntLiteral = converted %i32.as.I.impl.F.call, %.loc23_39.3 [concrete = constants.%int_3.1ba] // CHECK:STDOUT: %array_type: type = array_type %.loc23_39.4, %i32.loc23_16 [concrete = constants.%array_type] // CHECK:STDOUT: } // CHECK:STDOUT: %arr: ref %array_type = ref_binding arr, %arr.var [concrete = %arr.var] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: interface @I { // CHECK:STDOUT: %Self: %I.type = symbolic_binding Self, 0 [symbolic = constants.%Self.ab9] // CHECK:STDOUT: %I.WithSelf.decl = interface_with_self_decl @I [concrete] // CHECK:STDOUT: // CHECK:STDOUT: !with Self: // CHECK:STDOUT: %I.WithSelf.F.decl: @I.WithSelf.%I.WithSelf.F.type (%I.WithSelf.F.type.08c) = fn_decl @I.WithSelf.F [symbolic = @I.WithSelf.%I.WithSelf.F (constants.%I.WithSelf.F.705)] { // CHECK:STDOUT: %self.patt: @I.WithSelf.F.%pattern_type (%pattern_type.fa0) = value_binding_pattern self [concrete] // CHECK:STDOUT: %self.param_patt: @I.WithSelf.F.%pattern_type (%pattern_type.fa0) = value_param_pattern %self.patt [concrete] // CHECK:STDOUT: %other.patt: @I.WithSelf.F.%pattern_type (%pattern_type.fa0) = value_binding_pattern other [concrete] // CHECK:STDOUT: %other.param_patt: @I.WithSelf.F.%pattern_type (%pattern_type.fa0) = value_param_pattern %other.patt [concrete] // CHECK:STDOUT: %return.patt: @I.WithSelf.F.%pattern_type (%pattern_type.fa0) = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: @I.WithSelf.F.%pattern_type (%pattern_type.fa0) = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %Self.ref.loc16_36: %I.type = name_ref Self, @I.%Self [symbolic = %Self (constants.%Self.ab9)] // CHECK:STDOUT: %Self.as_type.loc16_36: type = facet_access_type %Self.ref.loc16_36 [symbolic = %Self.binding.as_type (constants.%Self.binding.as_type.d31)] // CHECK:STDOUT: %.loc16_36.2: type = converted %Self.ref.loc16_36, %Self.as_type.loc16_36 [symbolic = %Self.binding.as_type (constants.%Self.binding.as_type.d31)] // CHECK:STDOUT: %.loc16_36.3: Core.Form = init_form %.loc16_36.2 [symbolic = %.loc16_36.1 (constants.%.e46)] // CHECK:STDOUT: %self.param: @I.WithSelf.F.%Self.binding.as_type (%Self.binding.as_type.d31) = value_param call_param0 // CHECK:STDOUT: %.loc16_14.1: type = splice_block %.loc16_14.2 [symbolic = %Self.binding.as_type (constants.%Self.binding.as_type.d31)] { // CHECK:STDOUT: %Self.ref.loc16_14: %I.type = name_ref Self, @I.%Self [symbolic = %Self (constants.%Self.ab9)] // CHECK:STDOUT: %Self.as_type.loc16_14: type = facet_access_type %Self.ref.loc16_14 [symbolic = %Self.binding.as_type (constants.%Self.binding.as_type.d31)] // CHECK:STDOUT: %.loc16_14.2: type = converted %Self.ref.loc16_14, %Self.as_type.loc16_14 [symbolic = %Self.binding.as_type (constants.%Self.binding.as_type.d31)] // CHECK:STDOUT: } // CHECK:STDOUT: %self: @I.WithSelf.F.%Self.binding.as_type (%Self.binding.as_type.d31) = value_binding self, %self.param // CHECK:STDOUT: %other.param: @I.WithSelf.F.%Self.binding.as_type (%Self.binding.as_type.d31) = value_param call_param1 // CHECK:STDOUT: %.loc16_27.1: type = splice_block %.loc16_27.2 [symbolic = %Self.binding.as_type (constants.%Self.binding.as_type.d31)] { // CHECK:STDOUT: %Self.ref.loc16_27: %I.type = name_ref Self, @I.%Self [symbolic = %Self (constants.%Self.ab9)] // CHECK:STDOUT: %Self.as_type.loc16_27: type = facet_access_type %Self.ref.loc16_27 [symbolic = %Self.binding.as_type (constants.%Self.binding.as_type.d31)] // CHECK:STDOUT: %.loc16_27.2: type = converted %Self.ref.loc16_27, %Self.as_type.loc16_27 [symbolic = %Self.binding.as_type (constants.%Self.binding.as_type.d31)] // CHECK:STDOUT: } // CHECK:STDOUT: %other: @I.WithSelf.F.%Self.binding.as_type (%Self.binding.as_type.d31) = value_binding other, %other.param // CHECK:STDOUT: %return.param: ref @I.WithSelf.F.%Self.binding.as_type (%Self.binding.as_type.d31) = out_param call_param2 // CHECK:STDOUT: %return: ref @I.WithSelf.F.%Self.binding.as_type (%Self.binding.as_type.d31) = return_slot %return.param // CHECK:STDOUT: } // CHECK:STDOUT: %assoc0: %I.assoc_type = assoc_entity element0, %I.WithSelf.F.decl [concrete = constants.%assoc0.18e] // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = %Self // CHECK:STDOUT: .F = @I.WithSelf.%assoc0 // CHECK:STDOUT: witness = (@I.WithSelf.%I.WithSelf.F.decl) // CHECK:STDOUT: // CHECK:STDOUT: !requires: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: impl @i32.as.I.impl: %i32 as %I.ref { // CHECK:STDOUT: %i32.as.I.impl.F.decl: %i32.as.I.impl.F.type = fn_decl @i32.as.I.impl.F [concrete = constants.%i32.as.I.impl.F] { // CHECK:STDOUT: %self.patt: %pattern_type.7ce = value_binding_pattern self [concrete] // CHECK:STDOUT: %self.param_patt: %pattern_type.7ce = value_param_pattern %self.patt [concrete] // CHECK:STDOUT: %other.patt: %pattern_type.7ce = value_binding_pattern other [concrete] // CHECK:STDOUT: %other.param_patt: %pattern_type.7ce = value_param_pattern %other.patt [concrete] // CHECK:STDOUT: %return.patt: %pattern_type.7ce = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: %pattern_type.7ce = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %i32.loc20_34: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %.loc20: Core.Form = init_form %i32.loc20_34 [concrete = constants.%.ff5] // CHECK:STDOUT: %self.param: %i32 = value_param call_param0 // CHECK:STDOUT: %i32.loc20_14: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %self: %i32 = value_binding self, %self.param // CHECK:STDOUT: %other.param: %i32 = value_param call_param1 // CHECK:STDOUT: %i32.loc20_26: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %other: %i32 = value_binding other, %other.param // CHECK:STDOUT: %return.param: ref %i32 = out_param call_param2 // CHECK:STDOUT: %return: ref %i32 = return_slot %return.param // CHECK:STDOUT: } // CHECK:STDOUT: %I.impl_witness_table = impl_witness_table (%i32.as.I.impl.F.decl), @i32.as.I.impl [concrete] // CHECK:STDOUT: %I.impl_witness: = impl_witness %I.impl_witness_table [concrete = constants.%I.impl_witness] // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .F = %i32.as.I.impl.F.decl // CHECK:STDOUT: witness = %I.impl_witness // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic fn @I.WithSelf.F(@I.%Self: %I.type) { // CHECK:STDOUT: %Self: %I.type = symbolic_binding Self, 0 [symbolic = %Self (constants.%Self.ab9)] // CHECK:STDOUT: %Self.binding.as_type: type = symbolic_binding_type Self, 0, %Self [symbolic = %Self.binding.as_type (constants.%Self.binding.as_type.d31)] // CHECK:STDOUT: %pattern_type: type = pattern_type %Self.binding.as_type [symbolic = %pattern_type (constants.%pattern_type.fa0)] // CHECK:STDOUT: %.loc16_36.1: Core.Form = init_form %Self.binding.as_type [symbolic = %.loc16_36.1 (constants.%.e46)] // CHECK:STDOUT: // CHECK:STDOUT: fn(%self.param: @I.WithSelf.F.%Self.binding.as_type (%Self.binding.as_type.d31), %other.param: @I.WithSelf.F.%Self.binding.as_type (%Self.binding.as_type.d31)) -> out %return.param: @I.WithSelf.F.%Self.binding.as_type (%Self.binding.as_type.d31); // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @i32.as.I.impl.F(%self.param: %i32, %other.param: %i32) -> out %return.param: %i32 = "int.sadd"; // CHECK:STDOUT: // CHECK:STDOUT: fn @__global_init() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %DefaultOrUnformed.facet: %DefaultOrUnformed.type = facet_value constants.%array_type, (constants.%DefaultOrUnformed.impl_witness.d11) [concrete = constants.%DefaultOrUnformed.facet] // CHECK:STDOUT: %.loc23_41.1: %DefaultOrUnformed.type = converted constants.%array_type, %DefaultOrUnformed.facet [concrete = constants.%DefaultOrUnformed.facet] // CHECK:STDOUT: %as_type: type = facet_access_type %.loc23_41.1 [concrete = constants.%array_type] // CHECK:STDOUT: %.loc23_41.2: type = converted %.loc23_41.1, %as_type [concrete = constants.%array_type] // CHECK:STDOUT: %T.as.DefaultOrUnformed.impl.Op.specific_fn: = specific_function constants.%T.as.DefaultOrUnformed.impl.Op.9c0, @T.as.DefaultOrUnformed.impl.Op(constants.%array_type) [concrete = constants.%T.as.DefaultOrUnformed.impl.Op.specific_fn] // CHECK:STDOUT: %.loc23_1: ref %array_type = splice_block file.%arr.var [concrete = file.%arr.var] {} // CHECK:STDOUT: %T.as.DefaultOrUnformed.impl.Op.call: init %array_type to %.loc23_1 = call %T.as.DefaultOrUnformed.impl.Op.specific_fn() // CHECK:STDOUT: assign file.%arr.var, %T.as.DefaultOrUnformed.impl.Op.call // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @I.WithSelf(constants.%Self.ab9) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: %Self => constants.%Self.ab9 // CHECK:STDOUT: %I.WithSelf.F.type => constants.%I.WithSelf.F.type.08c // CHECK:STDOUT: %I.WithSelf.F => constants.%I.WithSelf.F.705 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @I.WithSelf.F(constants.%Self.ab9) { // CHECK:STDOUT: %Self => constants.%Self.ab9 // CHECK:STDOUT: %Self.binding.as_type => constants.%Self.binding.as_type.d31 // CHECK:STDOUT: %pattern_type => constants.%pattern_type.fa0 // CHECK:STDOUT: %.loc16_36.1 => constants.%.e46 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @I.WithSelf(constants.%I.facet) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: %Self => constants.%I.facet // CHECK:STDOUT: %I.WithSelf.F.type => constants.%I.WithSelf.F.type.f37 // CHECK:STDOUT: %I.WithSelf.F => constants.%I.WithSelf.F.b05 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @I.WithSelf.F(constants.%I.facet) { // CHECK:STDOUT: %Self => constants.%I.facet // CHECK:STDOUT: %Self.binding.as_type => constants.%i32 // CHECK:STDOUT: %pattern_type => constants.%pattern_type.7ce // CHECK:STDOUT: %.loc16_36.1 => constants.%.ff5 // CHECK:STDOUT: } // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/function/builtin/positional.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/int.carbon // TODO: Add ranges and switch to "--dump-sem-ir-ranges=only". // EXTRA-ARGS: --dump-sem-ir-ranges=if-present // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/function/builtin/positional.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/function/builtin/positional.carbon // --- fail_positional.carbon // CHECK:STDERR: fail_positional.carbon:[[@LINE+8]]:1: error: semantics TODO: `function with positional parameters` [SemanticsTodo] // CHECK:STDERR: fn Add -> i32 = "int.sadd"; // CHECK:STDERR: ^~~~~~~~~~~~~~~ // CHECK:STDERR: // CHECK:STDERR: fail_positional.carbon:[[@LINE+4]]:1: error: invalid signature for builtin function "int.sadd" [InvalidBuiltinSignature] // CHECK:STDERR: fn Add -> i32 = "int.sadd"; // CHECK:STDERR: ^~~~~~~~~~~~~~~ // CHECK:STDERR: fn Add -> i32 = "int.sadd"; // CHECK:STDERR: fail_positional.carbon:[[@LINE+8]]:1: error: semantics TODO: `function with positional parameters` [SemanticsTodo] // CHECK:STDERR: fn Mul = "int.smul"; // CHECK:STDERR: ^~~~~~~~ // CHECK:STDERR: // CHECK:STDERR: fail_positional.carbon:[[@LINE+4]]:1: error: invalid signature for builtin function "int.smul" [InvalidBuiltinSignature] // CHECK:STDERR: fn Mul = "int.smul"; // CHECK:STDERR: ^~~~~~~~ // CHECK:STDERR: fn Mul = "int.smul"; // CHECK:STDOUT: --- fail_positional.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [concrete] // CHECK:STDOUT: %Int.type: type = generic_class_type @Int [concrete] // CHECK:STDOUT: %Int.generic: %Int.type = struct_value () [concrete] // CHECK:STDOUT: %i32: type = class_type @Int, @Int(%int_32) [concrete] // CHECK:STDOUT: %.ff5: Core.Form = init_form %i32 [concrete] // CHECK:STDOUT: %pattern_type.7ce: type = pattern_type %i32 [concrete] // CHECK:STDOUT: %Add.type: type = fn_type @Add [concrete] // CHECK:STDOUT: %Add: %Add.type = struct_value () [concrete] // CHECK:STDOUT: %Mul.type: type = fn_type @Mul [concrete] // CHECK:STDOUT: %Mul: %Mul.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: .Int = %Core.Int // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: %Core.Int: %Int.type = import_ref Core//prelude/parts/int, Int, loaded [concrete = constants.%Int.generic] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .Add = %Add.decl // CHECK:STDOUT: .Mul = %Mul.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %Add.decl: %Add.type = fn_decl @Add [concrete = constants.%Add] { // CHECK:STDOUT: %return.patt: %pattern_type.7ce = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: %pattern_type.7ce = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %i32: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %.loc10: Core.Form = init_form %i32 [concrete = constants.%.ff5] // CHECK:STDOUT: %return.param: ref %i32 = out_param call_param0 // CHECK:STDOUT: %return: ref %i32 = return_slot %return.param // CHECK:STDOUT: } // CHECK:STDOUT: %Mul.decl: %Mul.type = fn_decl @Mul [concrete = constants.%Mul] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @Add() -> out %return.param: %i32; // CHECK:STDOUT: // CHECK:STDOUT: fn @Mul; // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/function/call/alias.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/destroy.carbon // TODO: Add ranges and switch to "--dump-sem-ir-ranges=only". // EXTRA-ARGS: --dump-sem-ir-ranges=if-present // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/function/call/alias.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/function/call/alias.carbon fn A() -> () { return (); } alias B = A; fn Main() { var unused b: () = B(); } // CHECK:STDOUT: --- alias.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] // CHECK:STDOUT: %empty_tuple: %empty_tuple.type = tuple_value () [concrete] // CHECK:STDOUT: %.262: Core.Form = init_form %empty_tuple.type [concrete] // CHECK:STDOUT: %pattern_type.cb1: type = pattern_type %empty_tuple.type [concrete] // CHECK:STDOUT: %A.type: type = fn_type @A [concrete] // CHECK:STDOUT: %A: %A.type = struct_value () [concrete] // CHECK:STDOUT: %Main.type: type = fn_type @Main [concrete] // CHECK:STDOUT: %Main: %Main.type = struct_value () [concrete] // CHECK:STDOUT: %Destroy.type: type = facet_type <@Destroy> [concrete] // CHECK:STDOUT: %Destroy.Op.type: type = fn_type @Destroy.Op [concrete] // CHECK:STDOUT: %Destroy.Op: %Destroy.Op.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: .Destroy = %Core.Destroy // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: %Core.Destroy: type = import_ref Core//prelude/parts/destroy, Destroy, loaded [concrete = constants.%Destroy.type] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .A = %A.decl // CHECK:STDOUT: .B = %B // CHECK:STDOUT: .Main = %Main.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %A.decl: %A.type = fn_decl @A [concrete = constants.%A] { // CHECK:STDOUT: %return.patt: %pattern_type.cb1 = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: %pattern_type.cb1 = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %.loc15_12.1: %empty_tuple.type = tuple_literal () [concrete = constants.%empty_tuple] // CHECK:STDOUT: %.loc15_12.2: type = converted %.loc15_12.1, constants.%empty_tuple.type [concrete = constants.%empty_tuple.type] // CHECK:STDOUT: %.loc15_12.3: Core.Form = init_form %.loc15_12.2 [concrete = constants.%.262] // CHECK:STDOUT: %return.param: ref %empty_tuple.type = out_param call_param0 // CHECK:STDOUT: %return: ref %empty_tuple.type = return_slot %return.param // CHECK:STDOUT: } // CHECK:STDOUT: %A.ref: %A.type = name_ref A, %A.decl [concrete = constants.%A] // CHECK:STDOUT: %B: %A.type = alias_binding B, %A.decl [concrete = constants.%A] // CHECK:STDOUT: %Main.decl: %Main.type = fn_decl @Main [concrete = constants.%Main] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @A() -> out %return.param: %empty_tuple.type { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %.loc15_24.1: %empty_tuple.type = tuple_literal () [concrete = constants.%empty_tuple] // CHECK:STDOUT: %.loc15_24.2: init %empty_tuple.type = tuple_init () [concrete = constants.%empty_tuple] // CHECK:STDOUT: %.loc15_25: init %empty_tuple.type = converted %.loc15_24.1, %.loc15_24.2 [concrete = constants.%empty_tuple] // CHECK:STDOUT: return %.loc15_25 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @Main() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %b.patt: %pattern_type.cb1 = ref_binding_pattern b [concrete] // CHECK:STDOUT: %b.var_patt: %pattern_type.cb1 = var_pattern %b.patt [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %b.var: ref %empty_tuple.type = var %b.var_patt // CHECK:STDOUT: %B.ref: %A.type = name_ref B, file.%B [concrete = constants.%A] // CHECK:STDOUT: %A.call: init %empty_tuple.type = call %B.ref() // CHECK:STDOUT: assign %b.var, %A.call // CHECK:STDOUT: %.loc20_18.1: type = splice_block %.loc20_18.3 [concrete = constants.%empty_tuple.type] { // CHECK:STDOUT: %.loc20_18.2: %empty_tuple.type = tuple_literal () [concrete = constants.%empty_tuple] // CHECK:STDOUT: %.loc20_18.3: type = converted %.loc20_18.2, constants.%empty_tuple.type [concrete = constants.%empty_tuple.type] // CHECK:STDOUT: } // CHECK:STDOUT: %b: ref %empty_tuple.type = ref_binding b, %b.var // CHECK:STDOUT: %Destroy.Op.bound: = bound_method %b.var, constants.%Destroy.Op // CHECK:STDOUT: %Destroy.Op.call: init %empty_tuple.type = call %Destroy.Op.bound(%b.var) // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @Destroy.Op(%self.param: ref %empty_tuple.type) = "no_op"; // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/function/call/empty_struct.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/none.carbon // TODO: Add ranges and switch to "--dump-sem-ir-ranges=only". // EXTRA-ARGS: --dump-sem-ir-ranges=if-present // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/function/call/empty_struct.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/function/call/empty_struct.carbon fn Echo(a: {}) -> {} { return a; } fn Main() { Echo({}); } // CHECK:STDOUT: --- empty_struct.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %empty_struct: %empty_struct_type = struct_value () [concrete] // CHECK:STDOUT: %pattern_type: type = pattern_type %empty_struct_type [concrete] // CHECK:STDOUT: %.469: Core.Form = init_form %empty_struct_type [concrete] // CHECK:STDOUT: %Echo.type: type = fn_type @Echo [concrete] // CHECK:STDOUT: %Echo: %Echo.type = struct_value () [concrete] // CHECK:STDOUT: %Main.type: type = fn_type @Main [concrete] // CHECK:STDOUT: %Main: %Main.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Echo = %Echo.decl // CHECK:STDOUT: .Main = %Main.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Echo.decl: %Echo.type = fn_decl @Echo [concrete = constants.%Echo] { // CHECK:STDOUT: %a.patt: %pattern_type = value_binding_pattern a [concrete] // CHECK:STDOUT: %a.param_patt: %pattern_type = value_param_pattern %a.patt [concrete] // CHECK:STDOUT: %return.patt: %pattern_type = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: %pattern_type = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %.loc15_20.1: %empty_struct_type = struct_literal () [concrete = constants.%empty_struct] // CHECK:STDOUT: %.loc15_20.2: type = converted %.loc15_20.1, constants.%empty_struct_type [concrete = constants.%empty_struct_type] // CHECK:STDOUT: %.loc15_20.3: Core.Form = init_form %.loc15_20.2 [concrete = constants.%.469] // CHECK:STDOUT: %a.param: %empty_struct_type = value_param call_param0 // CHECK:STDOUT: %.loc15_13.1: type = splice_block %.loc15_13.3 [concrete = constants.%empty_struct_type] { // CHECK:STDOUT: %.loc15_13.2: %empty_struct_type = struct_literal () [concrete = constants.%empty_struct] // CHECK:STDOUT: %.loc15_13.3: type = converted %.loc15_13.2, constants.%empty_struct_type [concrete = constants.%empty_struct_type] // CHECK:STDOUT: } // CHECK:STDOUT: %a: %empty_struct_type = value_binding a, %a.param // CHECK:STDOUT: %return.param: ref %empty_struct_type = out_param call_param1 // CHECK:STDOUT: %return: ref %empty_struct_type = return_slot %return.param // CHECK:STDOUT: } // CHECK:STDOUT: %Main.decl: %Main.type = fn_decl @Main [concrete = constants.%Main] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @Echo(%a.param: %empty_struct_type) -> out %return.param: %empty_struct_type { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %a.ref: %empty_struct_type = name_ref a, %a // CHECK:STDOUT: %.loc16_10: init %empty_struct_type = struct_init () [concrete = constants.%empty_struct] // CHECK:STDOUT: %.loc16_11: init %empty_struct_type = converted %a.ref, %.loc16_10 [concrete = constants.%empty_struct] // CHECK:STDOUT: return %.loc16_11 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @Main() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %Echo.ref: %Echo.type = name_ref Echo, file.%Echo.decl [concrete = constants.%Echo] // CHECK:STDOUT: %.loc20_9.1: %empty_struct_type = struct_literal () [concrete = constants.%empty_struct] // CHECK:STDOUT: %empty_struct: %empty_struct_type = struct_value () [concrete = constants.%empty_struct] // CHECK:STDOUT: %.loc20_9.2: %empty_struct_type = converted %.loc20_9.1, %empty_struct [concrete = constants.%empty_struct] // CHECK:STDOUT: %Echo.call: init %empty_struct_type = call %Echo.ref(%.loc20_9.2) // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/function/call/empty_tuple.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/none.carbon // TODO: Add ranges and switch to "--dump-sem-ir-ranges=only". // EXTRA-ARGS: --dump-sem-ir-ranges=if-present // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/function/call/empty_tuple.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/function/call/empty_tuple.carbon fn Echo(a: ()) -> () { return a; } fn Main() { Echo(()); } // CHECK:STDOUT: --- empty_tuple.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] // CHECK:STDOUT: %empty_tuple: %empty_tuple.type = tuple_value () [concrete] // CHECK:STDOUT: %pattern_type: type = pattern_type %empty_tuple.type [concrete] // CHECK:STDOUT: %.262: Core.Form = init_form %empty_tuple.type [concrete] // CHECK:STDOUT: %Echo.type: type = fn_type @Echo [concrete] // CHECK:STDOUT: %Echo: %Echo.type = struct_value () [concrete] // CHECK:STDOUT: %Main.type: type = fn_type @Main [concrete] // CHECK:STDOUT: %Main: %Main.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Echo = %Echo.decl // CHECK:STDOUT: .Main = %Main.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Echo.decl: %Echo.type = fn_decl @Echo [concrete = constants.%Echo] { // CHECK:STDOUT: %a.patt: %pattern_type = value_binding_pattern a [concrete] // CHECK:STDOUT: %a.param_patt: %pattern_type = value_param_pattern %a.patt [concrete] // CHECK:STDOUT: %return.patt: %pattern_type = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: %pattern_type = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %.loc15_20.1: %empty_tuple.type = tuple_literal () [concrete = constants.%empty_tuple] // CHECK:STDOUT: %.loc15_20.2: type = converted %.loc15_20.1, constants.%empty_tuple.type [concrete = constants.%empty_tuple.type] // CHECK:STDOUT: %.loc15_20.3: Core.Form = init_form %.loc15_20.2 [concrete = constants.%.262] // CHECK:STDOUT: %a.param: %empty_tuple.type = value_param call_param0 // CHECK:STDOUT: %.loc15_13.1: type = splice_block %.loc15_13.3 [concrete = constants.%empty_tuple.type] { // CHECK:STDOUT: %.loc15_13.2: %empty_tuple.type = tuple_literal () [concrete = constants.%empty_tuple] // CHECK:STDOUT: %.loc15_13.3: type = converted %.loc15_13.2, constants.%empty_tuple.type [concrete = constants.%empty_tuple.type] // CHECK:STDOUT: } // CHECK:STDOUT: %a: %empty_tuple.type = value_binding a, %a.param // CHECK:STDOUT: %return.param: ref %empty_tuple.type = out_param call_param1 // CHECK:STDOUT: %return: ref %empty_tuple.type = return_slot %return.param // CHECK:STDOUT: } // CHECK:STDOUT: %Main.decl: %Main.type = fn_decl @Main [concrete = constants.%Main] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @Echo(%a.param: %empty_tuple.type) -> out %return.param: %empty_tuple.type { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %a.ref: %empty_tuple.type = name_ref a, %a // CHECK:STDOUT: %.loc16_10: init %empty_tuple.type = tuple_init () [concrete = constants.%empty_tuple] // CHECK:STDOUT: %.loc16_11: init %empty_tuple.type = converted %a.ref, %.loc16_10 [concrete = constants.%empty_tuple] // CHECK:STDOUT: return %.loc16_11 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @Main() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %Echo.ref: %Echo.type = name_ref Echo, file.%Echo.decl [concrete = constants.%Echo] // CHECK:STDOUT: %.loc20_9.1: %empty_tuple.type = tuple_literal () [concrete = constants.%empty_tuple] // CHECK:STDOUT: %empty_tuple: %empty_tuple.type = tuple_value () [concrete = constants.%empty_tuple] // CHECK:STDOUT: %.loc20_9.2: %empty_tuple.type = converted %.loc20_9.1, %empty_tuple [concrete = constants.%empty_tuple] // CHECK:STDOUT: %Echo.call: init %empty_tuple.type = call %Echo.ref(%.loc20_9.2) // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/function/call/eval_musteval.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/int.carbon // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/function/call/eval_musteval.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/function/call/eval_musteval.carbon // --- no_eval.carbon library "[[@TEST_NAME]]"; fn Basic(unused n: i32) {} eval fn Eval(unused n: i32) {} fn RuntimeArg(n: i32) { Basic(n); Eval(n); } // --- fail_no_eval_musteval.carbon library "[[@TEST_NAME]]"; musteval fn MustEval(unused n: i32) {} fn RuntimeArg(n: i32) { // CHECK:STDERR: fail_no_eval_musteval.carbon:[[@LINE+7]]:3: error: non-constant call to compile-time-only function [NonConstantCallToCompTimeOnlyFunction] // CHECK:STDERR: MustEval(n); // CHECK:STDERR: ^~~~~~~~~~~ // CHECK:STDERR: fail_no_eval_musteval.carbon:[[@LINE-6]]:1: note: compile-time-only function declared here [CompTimeOnlyFunctionHere] // CHECK:STDERR: musteval fn MustEval(unused n: i32) {} // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: MustEval(n); } // --- eval.carbon library "[[@TEST_NAME]]"; fn Basic(unused n: i32) {} eval fn Eval(unused n: i32) {} musteval fn MustEval(unused n: i32) {} fn CompileTimeArg() { Basic(1); Eval(1); MustEval(1); } // --- fail_todo_musteval_calls_musteval.carbon library "[[@TEST_NAME]]"; musteval fn MustEval(unused n: i32) {} // TODO: This should be accepted. musteval fn CallMustEval(n: i32) { // CHECK:STDERR: fail_todo_musteval_calls_musteval.carbon:[[@LINE+7]]:3: error: non-constant call to compile-time-only function [NonConstantCallToCompTimeOnlyFunction] // CHECK:STDERR: MustEval(n); // CHECK:STDERR: ^~~~~~~~~~~ // CHECK:STDERR: fail_todo_musteval_calls_musteval.carbon:[[@LINE-7]]:1: note: compile-time-only function declared here [CompTimeOnlyFunctionHere] // CHECK:STDERR: musteval fn MustEval(unused n: i32) {} // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: MustEval(n); } ================================================ FILE: toolchain/check/testdata/function/call/fail_explicit_self_param.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/none.carbon // TODO: Add ranges and switch to "--dump-sem-ir-ranges=only". // EXTRA-ARGS: --dump-sem-ir-ranges=if-present // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/function/call/fail_explicit_self_param.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/function/call/fail_explicit_self_param.carbon // CHECK:STDERR: fail_explicit_self_param.carbon:[[@LINE+4]]:6: error: `self` can only be declared in an implicit parameter list [SelfOutsideImplicitParamList] // CHECK:STDERR: fn F(self: ()); // CHECK:STDERR: ^~~~~~~~ // CHECK:STDERR: fn F(self: ()); fn Run() { F(()); } // CHECK:STDOUT: --- fail_explicit_self_param.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] // CHECK:STDOUT: %empty_tuple: %empty_tuple.type = tuple_value () [concrete] // CHECK:STDOUT: %pattern_type: type = pattern_type %empty_tuple.type [concrete] // CHECK:STDOUT: %F.type: type = fn_type @F [concrete] // CHECK:STDOUT: %F: %F.type = struct_value () [concrete] // CHECK:STDOUT: %Run.type: type = fn_type @Run [concrete] // CHECK:STDOUT: %Run: %Run.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .F = %F.decl // CHECK:STDOUT: .Run = %Run.decl // CHECK:STDOUT: } // CHECK:STDOUT: %F.decl: %F.type = fn_decl @F [concrete = constants.%F] { // CHECK:STDOUT: %self.patt: %pattern_type = value_binding_pattern self [concrete] // CHECK:STDOUT: %self.param_patt: %pattern_type = value_param_pattern %self.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %self.param: %empty_tuple.type = value_param call_param0 // CHECK:STDOUT: %.loc19_13.1: type = splice_block %.loc19_13.3 [concrete = constants.%empty_tuple.type] { // CHECK:STDOUT: %.loc19_13.2: %empty_tuple.type = tuple_literal () [concrete = constants.%empty_tuple] // CHECK:STDOUT: %.loc19_13.3: type = converted %.loc19_13.2, constants.%empty_tuple.type [concrete = constants.%empty_tuple.type] // CHECK:STDOUT: } // CHECK:STDOUT: %self: %empty_tuple.type = value_binding self, %self.param // CHECK:STDOUT: } // CHECK:STDOUT: %Run.decl: %Run.type = fn_decl @Run [concrete = constants.%Run] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @F(%self.param: %empty_tuple.type); // CHECK:STDOUT: // CHECK:STDOUT: fn @Run() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %F.ref: %F.type = name_ref F, file.%F.decl [concrete = constants.%F] // CHECK:STDOUT: %.loc22_6.1: %empty_tuple.type = tuple_literal () [concrete = constants.%empty_tuple] // CHECK:STDOUT: %empty_tuple: %empty_tuple.type = tuple_value () [concrete = constants.%empty_tuple] // CHECK:STDOUT: %.loc22_6.2: %empty_tuple.type = converted %.loc22_6.1, %empty_tuple [concrete = constants.%empty_tuple] // CHECK:STDOUT: %F.call: init %empty_tuple.type = call %F.ref(%.loc22_6.2) // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/function/call/fail_not_callable.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/full.carbon // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/function/call/fail_not_callable.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/function/call/fail_not_callable.carbon fn Run() { // CHECK:STDERR: fail_not_callable.carbon:[[@LINE+4]]:23: error: value of type `str` is not callable [CallToNonCallable] // CHECK:STDERR: var unused x: i32 = "hello"(); // CHECK:STDERR: ^~~~~~~~~ // CHECK:STDERR: var unused x: i32 = "hello"(); } ================================================ FILE: toolchain/check/testdata/function/call/fail_param_count.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/int.carbon // TODO: Add ranges and switch to "--dump-sem-ir-ranges=only". // EXTRA-ARGS: --dump-sem-ir-ranges=if-present // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/function/call/fail_param_count.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/function/call/fail_param_count.carbon fn Run0() {} fn Run1(unused a: i32) {} fn Run2(unused a: i32, unused b: i32) {} fn Main() { // CHECK:STDERR: fail_param_count.carbon:[[@LINE+7]]:3: error: 1 argument passed to function expecting 0 arguments [CallArgCountMismatch] // CHECK:STDERR: Run0(1); // CHECK:STDERR: ^~~~~~~ // CHECK:STDERR: fail_param_count.carbon:[[@LINE-8]]:1: note: calling function declared here [InCallToEntity] // CHECK:STDERR: fn Run0() {} // CHECK:STDERR: ^~~~~~~~~~~ // CHECK:STDERR: Run0(1); // CHECK:STDERR: fail_param_count.carbon:[[@LINE+7]]:3: error: 2 arguments passed to function expecting 0 arguments [CallArgCountMismatch] // CHECK:STDERR: Run0(0, 1); // CHECK:STDERR: ^~~~~~~~~~ // CHECK:STDERR: fail_param_count.carbon:[[@LINE-16]]:1: note: calling function declared here [InCallToEntity] // CHECK:STDERR: fn Run0() {} // CHECK:STDERR: ^~~~~~~~~~~ // CHECK:STDERR: Run0(0, 1); // CHECK:STDERR: fail_param_count.carbon:[[@LINE+7]]:3: error: 0 arguments passed to function expecting 1 argument [CallArgCountMismatch] // CHECK:STDERR: Run1(); // CHECK:STDERR: ^~~~~~ // CHECK:STDERR: fail_param_count.carbon:[[@LINE-24]]:1: note: calling function declared here [InCallToEntity] // CHECK:STDERR: fn Run1(unused a: i32) {} // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: Run1(); // CHECK:STDERR: fail_param_count.carbon:[[@LINE+7]]:3: error: 2 arguments passed to function expecting 1 argument [CallArgCountMismatch] // CHECK:STDERR: Run1(0, 1); // CHECK:STDERR: ^~~~~~~~~~ // CHECK:STDERR: fail_param_count.carbon:[[@LINE-32]]:1: note: calling function declared here [InCallToEntity] // CHECK:STDERR: fn Run1(unused a: i32) {} // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: Run1(0, 1); // CHECK:STDERR: fail_param_count.carbon:[[@LINE+7]]:3: error: 0 arguments passed to function expecting 2 arguments [CallArgCountMismatch] // CHECK:STDERR: Run2(); // CHECK:STDERR: ^~~~~~ // CHECK:STDERR: fail_param_count.carbon:[[@LINE-40]]:1: note: calling function declared here [InCallToEntity] // CHECK:STDERR: fn Run2(unused a: i32, unused b: i32) {} // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: Run2(); // CHECK:STDERR: fail_param_count.carbon:[[@LINE+7]]:3: error: 1 argument passed to function expecting 2 arguments [CallArgCountMismatch] // CHECK:STDERR: Run2(0); // CHECK:STDERR: ^~~~~~~ // CHECK:STDERR: fail_param_count.carbon:[[@LINE-48]]:1: note: calling function declared here [InCallToEntity] // CHECK:STDERR: fn Run2(unused a: i32, unused b: i32) {} // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: Run2(0); } // CHECK:STDOUT: --- fail_param_count.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %Run0.type: type = fn_type @Run0 [concrete] // CHECK:STDOUT: %Run0: %Run0.type = struct_value () [concrete] // CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [concrete] // CHECK:STDOUT: %Int.type: type = generic_class_type @Int [concrete] // CHECK:STDOUT: %Int.generic: %Int.type = struct_value () [concrete] // CHECK:STDOUT: %i32: type = class_type @Int, @Int(%int_32) [concrete] // CHECK:STDOUT: %pattern_type.7ce: type = pattern_type %i32 [concrete] // CHECK:STDOUT: %Run1.type: type = fn_type @Run1 [concrete] // CHECK:STDOUT: %Run1: %Run1.type = struct_value () [concrete] // CHECK:STDOUT: %Run2.type: type = fn_type @Run2 [concrete] // CHECK:STDOUT: %Run2: %Run2.type = struct_value () [concrete] // CHECK:STDOUT: %Main.type: type = fn_type @Main [concrete] // CHECK:STDOUT: %Main: %Main.type = struct_value () [concrete] // CHECK:STDOUT: %int_1: Core.IntLiteral = int_value 1 [concrete] // CHECK:STDOUT: %int_0: Core.IntLiteral = int_value 0 [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: .Int = %Core.Int // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: %Core.Int: %Int.type = import_ref Core//prelude/parts/int, Int, loaded [concrete = constants.%Int.generic] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .Run0 = %Run0.decl // CHECK:STDOUT: .Run1 = %Run1.decl // CHECK:STDOUT: .Run2 = %Run2.decl // CHECK:STDOUT: .Main = %Main.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %Run0.decl: %Run0.type = fn_decl @Run0 [concrete = constants.%Run0] {} {} // CHECK:STDOUT: %Run1.decl: %Run1.type = fn_decl @Run1 [concrete = constants.%Run1] { // CHECK:STDOUT: %a.patt: %pattern_type.7ce = value_binding_pattern a [concrete] // CHECK:STDOUT: %a.param_patt: %pattern_type.7ce = value_param_pattern %a.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %a.param: %i32 = value_param call_param0 // CHECK:STDOUT: %i32: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %a: %i32 = value_binding a, %a.param // CHECK:STDOUT: } // CHECK:STDOUT: %Run2.decl: %Run2.type = fn_decl @Run2 [concrete = constants.%Run2] { // CHECK:STDOUT: %a.patt: %pattern_type.7ce = value_binding_pattern a [concrete] // CHECK:STDOUT: %a.param_patt: %pattern_type.7ce = value_param_pattern %a.patt [concrete] // CHECK:STDOUT: %b.patt: %pattern_type.7ce = value_binding_pattern b [concrete] // CHECK:STDOUT: %b.param_patt: %pattern_type.7ce = value_param_pattern %b.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %a.param: %i32 = value_param call_param0 // CHECK:STDOUT: %i32.loc17_19: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %a: %i32 = value_binding a, %a.param // CHECK:STDOUT: %b.param: %i32 = value_param call_param1 // CHECK:STDOUT: %i32.loc17_34: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %b: %i32 = value_binding b, %b.param // CHECK:STDOUT: } // CHECK:STDOUT: %Main.decl: %Main.type = fn_decl @Main [concrete = constants.%Main] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @Run0() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @Run1(%a.param: %i32) { // CHECK:STDOUT: !entry: // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @Run2(%a.param: %i32, %b.param: %i32) { // CHECK:STDOUT: !entry: // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @Main() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %Run0.ref.loc27: %Run0.type = name_ref Run0, file.%Run0.decl [concrete = constants.%Run0] // CHECK:STDOUT: %int_1.loc27: Core.IntLiteral = int_value 1 [concrete = constants.%int_1] // CHECK:STDOUT: %Run0.ref.loc35: %Run0.type = name_ref Run0, file.%Run0.decl [concrete = constants.%Run0] // CHECK:STDOUT: %int_0.loc35: Core.IntLiteral = int_value 0 [concrete = constants.%int_0] // CHECK:STDOUT: %int_1.loc35: Core.IntLiteral = int_value 1 [concrete = constants.%int_1] // CHECK:STDOUT: %Run1.ref.loc44: %Run1.type = name_ref Run1, file.%Run1.decl [concrete = constants.%Run1] // CHECK:STDOUT: %Run1.ref.loc52: %Run1.type = name_ref Run1, file.%Run1.decl [concrete = constants.%Run1] // CHECK:STDOUT: %int_0.loc52: Core.IntLiteral = int_value 0 [concrete = constants.%int_0] // CHECK:STDOUT: %int_1.loc52: Core.IntLiteral = int_value 1 [concrete = constants.%int_1] // CHECK:STDOUT: %Run2.ref.loc61: %Run2.type = name_ref Run2, file.%Run2.decl [concrete = constants.%Run2] // CHECK:STDOUT: %Run2.ref.loc69: %Run2.type = name_ref Run2, file.%Run2.decl [concrete = constants.%Run2] // CHECK:STDOUT: %int_0.loc69: Core.IntLiteral = int_value 0 [concrete = constants.%int_0] // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/function/call/fail_param_type.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/primitives.carbon // TODO: Add ranges and switch to "--dump-sem-ir-ranges=only". // EXTRA-ARGS: --dump-sem-ir-ranges=if-present // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/function/call/fail_param_type.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/function/call/fail_param_type.carbon fn G(unused a: i32) {} fn F() { // CHECK:STDERR: fail_param_type.carbon:[[@LINE+10]]:5: error: cannot implicitly convert expression of type `Core.FloatLiteral` to `i32` [ConversionFailure] // CHECK:STDERR: G(1.0); // CHECK:STDERR: ^~~ // CHECK:STDERR: fail_param_type.carbon:[[@LINE+7]]:5: note: type `Core.FloatLiteral` does not implement interface `Core.ImplicitAs(i32)` [MissingImplInMemberAccessInContext] // CHECK:STDERR: G(1.0); // CHECK:STDERR: ^~~ // CHECK:STDERR: fail_param_type.carbon:[[@LINE-9]]:13: note: initializing function parameter [InCallToFunctionParam] // CHECK:STDERR: fn G(unused a: i32) {} // CHECK:STDERR: ^~~~~~ // CHECK:STDERR: G(1.0); } // CHECK:STDOUT: --- fail_param_type.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [concrete] // CHECK:STDOUT: %Int.type: type = generic_class_type @Int [concrete] // CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] // CHECK:STDOUT: %Int.generic: %Int.type = struct_value () [concrete] // CHECK:STDOUT: %i32: type = class_type @Int, @Int(%int_32) [concrete] // CHECK:STDOUT: %pattern_type.7ce: type = pattern_type %i32 [concrete] // CHECK:STDOUT: %G.type: type = fn_type @G [concrete] // CHECK:STDOUT: %G: %G.type = struct_value () [concrete] // CHECK:STDOUT: %F.type: type = fn_type @F [concrete] // CHECK:STDOUT: %F: %F.type = struct_value () [concrete] // CHECK:STDOUT: %float: Core.FloatLiteral = float_literal_value 10e-1 [concrete] // CHECK:STDOUT: %ImplicitAs.type.cc7: type = generic_interface_type @ImplicitAs [concrete] // CHECK:STDOUT: %ImplicitAs.generic: %ImplicitAs.type.cc7 = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: .Int = %Core.Int // CHECK:STDOUT: .ImplicitAs = %Core.ImplicitAs // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: %Core.Int: %Int.type = import_ref Core//prelude/parts/int, Int, loaded [concrete = constants.%Int.generic] // CHECK:STDOUT: %Core.ImplicitAs: %ImplicitAs.type.cc7 = import_ref Core//prelude/parts/as, ImplicitAs, loaded [concrete = constants.%ImplicitAs.generic] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .G = %G.decl // CHECK:STDOUT: .F = %F.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %G.decl: %G.type = fn_decl @G [concrete = constants.%G] { // CHECK:STDOUT: %a.patt: %pattern_type.7ce = value_binding_pattern a [concrete] // CHECK:STDOUT: %a.param_patt: %pattern_type.7ce = value_param_pattern %a.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %a.param: %i32 = value_param call_param0 // CHECK:STDOUT: %i32: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %a: %i32 = value_binding a, %a.param // CHECK:STDOUT: } // CHECK:STDOUT: %F.decl: %F.type = fn_decl @F [concrete = constants.%F] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @G(%a.param: %i32) { // CHECK:STDOUT: !entry: // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @F() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %G.ref: %G.type = name_ref G, file.%G.decl [concrete = constants.%G] // CHECK:STDOUT: %float: Core.FloatLiteral = float_literal_value 10e-1 [concrete = constants.%float] // CHECK:STDOUT: %.loc28: %i32 = converted %float, [concrete = ] // CHECK:STDOUT: %G.call: init %empty_tuple.type = call %G.ref() // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/function/call/fail_return_type_mismatch.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/primitives.carbon // TODO: Add ranges and switch to "--dump-sem-ir-ranges=only". // EXTRA-ARGS: --dump-sem-ir-ranges=if-present // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/function/call/fail_return_type_mismatch.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/function/call/fail_return_type_mismatch.carbon fn Foo() -> f64 { return 1.0; } fn Run() { // CHECK:STDERR: fail_return_type_mismatch.carbon:[[@LINE+7]]:3: error: cannot implicitly convert expression of type `f64` to `i32` [ConversionFailure] // CHECK:STDERR: var unused x: i32 = Foo(); // CHECK:STDERR: ^~~~~~~~~~~~~~~~~ // CHECK:STDERR: fail_return_type_mismatch.carbon:[[@LINE+4]]:3: note: type `f64` does not implement interface `Core.ImplicitAs(i32)` [MissingImplInMemberAccessInContext] // CHECK:STDERR: var unused x: i32 = Foo(); // CHECK:STDERR: ^~~~~~~~~~~~~~~~~ // CHECK:STDERR: var unused x: i32 = Foo(); } // CHECK:STDOUT: --- fail_return_type_mismatch.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %int_64: Core.IntLiteral = int_value 64 [concrete] // CHECK:STDOUT: %Float.type: type = generic_class_type @Float [concrete] // CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] // CHECK:STDOUT: %Float.generic: %Float.type = struct_value () [concrete] // CHECK:STDOUT: %f64.d77: type = class_type @Float, @Float(%int_64) [concrete] // CHECK:STDOUT: %.49c: Core.Form = init_form %f64.d77 [concrete] // CHECK:STDOUT: %pattern_type.0ae: type = pattern_type %f64.d77 [concrete] // CHECK:STDOUT: %Foo.type: type = fn_type @Foo [concrete] // CHECK:STDOUT: %Foo: %Foo.type = struct_value () [concrete] // CHECK:STDOUT: %float.6da: Core.FloatLiteral = float_literal_value 10e-1 [concrete] // CHECK:STDOUT: %ImplicitAs.type.cc7: type = generic_interface_type @ImplicitAs [concrete] // CHECK:STDOUT: %ImplicitAs.generic: %ImplicitAs.type.cc7 = struct_value () [concrete] // CHECK:STDOUT: %ImplicitAs.type.4a8: type = facet_type <@ImplicitAs, @ImplicitAs(%f64.d77)> [concrete] // CHECK:STDOUT: %To: Core.IntLiteral = symbolic_binding To, 0 [symbolic] // CHECK:STDOUT: %Core.FloatLiteral.as.ImplicitAs.impl.Convert.type.02f: type = fn_type @Core.FloatLiteral.as.ImplicitAs.impl.Convert, @Core.FloatLiteral.as.ImplicitAs.impl(%To) [symbolic] // CHECK:STDOUT: %Core.FloatLiteral.as.ImplicitAs.impl.Convert.1f0: %Core.FloatLiteral.as.ImplicitAs.impl.Convert.type.02f = struct_value () [symbolic] // CHECK:STDOUT: %ImplicitAs.impl_witness.cb2: = impl_witness imports.%ImplicitAs.impl_witness_table.31a, @Core.FloatLiteral.as.ImplicitAs.impl(%int_64) [concrete] // CHECK:STDOUT: %Core.FloatLiteral.as.ImplicitAs.impl.Convert.type.2c7: type = fn_type @Core.FloatLiteral.as.ImplicitAs.impl.Convert, @Core.FloatLiteral.as.ImplicitAs.impl(%int_64) [concrete] // CHECK:STDOUT: %Core.FloatLiteral.as.ImplicitAs.impl.Convert.239: %Core.FloatLiteral.as.ImplicitAs.impl.Convert.type.2c7 = struct_value () [concrete] // CHECK:STDOUT: %ImplicitAs.facet: %ImplicitAs.type.4a8 = facet_value Core.FloatLiteral, (%ImplicitAs.impl_witness.cb2) [concrete] // CHECK:STDOUT: %ImplicitAs.WithSelf.Convert.type.a33: type = fn_type @ImplicitAs.WithSelf.Convert, @ImplicitAs.WithSelf(%f64.d77, %ImplicitAs.facet) [concrete] // CHECK:STDOUT: %.6c5: type = fn_type_with_self_type %ImplicitAs.WithSelf.Convert.type.a33, %ImplicitAs.facet [concrete] // CHECK:STDOUT: %Core.FloatLiteral.as.ImplicitAs.impl.Convert.bound: = bound_method %float.6da, %Core.FloatLiteral.as.ImplicitAs.impl.Convert.239 [concrete] // CHECK:STDOUT: %Core.FloatLiteral.as.ImplicitAs.impl.Convert.specific_fn: = specific_function %Core.FloatLiteral.as.ImplicitAs.impl.Convert.239, @Core.FloatLiteral.as.ImplicitAs.impl.Convert(%int_64) [concrete] // CHECK:STDOUT: %bound_method: = bound_method %float.6da, %Core.FloatLiteral.as.ImplicitAs.impl.Convert.specific_fn [concrete] // CHECK:STDOUT: %float.d20: %f64.d77 = float_value 1 [concrete] // CHECK:STDOUT: %Run.type: type = fn_type @Run [concrete] // CHECK:STDOUT: %Run: %Run.type = struct_value () [concrete] // CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [concrete] // CHECK:STDOUT: %Int.type: type = generic_class_type @Int [concrete] // CHECK:STDOUT: %Int.generic: %Int.type = struct_value () [concrete] // CHECK:STDOUT: %i32: type = class_type @Int, @Int(%int_32) [concrete] // CHECK:STDOUT: %pattern_type.7ce: type = pattern_type %i32 [concrete] // CHECK:STDOUT: %Destroy.type: type = facet_type <@Destroy> [concrete] // CHECK:STDOUT: %Destroy.Op.type: type = fn_type @Destroy.Op [concrete] // CHECK:STDOUT: %Destroy.Op: %Destroy.Op.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: .Float = %Core.Float // CHECK:STDOUT: .ImplicitAs = %Core.ImplicitAs // CHECK:STDOUT: .Int = %Core.Int // CHECK:STDOUT: .Destroy = %Core.Destroy // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: %Core.Float: %Float.type = import_ref Core//prelude/parts/float, Float, loaded [concrete = constants.%Float.generic] // CHECK:STDOUT: %Core.ImplicitAs: %ImplicitAs.type.cc7 = import_ref Core//prelude/parts/as, ImplicitAs, loaded [concrete = constants.%ImplicitAs.generic] // CHECK:STDOUT: %Core.import_ref.38a: @Core.FloatLiteral.as.ImplicitAs.impl.%Core.FloatLiteral.as.ImplicitAs.impl.Convert.type (%Core.FloatLiteral.as.ImplicitAs.impl.Convert.type.02f) = import_ref Core//prelude/parts/float, loc{{\d+_\d+}}, loaded [symbolic = @Core.FloatLiteral.as.ImplicitAs.impl.%Core.FloatLiteral.as.ImplicitAs.impl.Convert (constants.%Core.FloatLiteral.as.ImplicitAs.impl.Convert.1f0)] // CHECK:STDOUT: %ImplicitAs.impl_witness_table.31a = impl_witness_table (%Core.import_ref.38a), @Core.FloatLiteral.as.ImplicitAs.impl [concrete] // CHECK:STDOUT: %Core.Int: %Int.type = import_ref Core//prelude/parts/int, Int, loaded [concrete = constants.%Int.generic] // CHECK:STDOUT: %Core.Destroy: type = import_ref Core//prelude/parts/destroy, Destroy, loaded [concrete = constants.%Destroy.type] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .Foo = %Foo.decl // CHECK:STDOUT: .Run = %Run.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %Foo.decl: %Foo.type = fn_decl @Foo [concrete = constants.%Foo] { // CHECK:STDOUT: %return.patt: %pattern_type.0ae = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: %pattern_type.0ae = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %f64: type = type_literal constants.%f64.d77 [concrete = constants.%f64.d77] // CHECK:STDOUT: %.loc15_13: Core.Form = init_form %f64 [concrete = constants.%.49c] // CHECK:STDOUT: %return.param: ref %f64.d77 = out_param call_param0 // CHECK:STDOUT: %return: ref %f64.d77 = return_slot %return.param // CHECK:STDOUT: } // CHECK:STDOUT: %Run.decl: %Run.type = fn_decl @Run [concrete = constants.%Run] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @Foo() -> out %return.param: %f64.d77 { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %float: Core.FloatLiteral = float_literal_value 10e-1 [concrete = constants.%float.6da] // CHECK:STDOUT: %impl.elem0: %.6c5 = impl_witness_access constants.%ImplicitAs.impl_witness.cb2, element0 [concrete = constants.%Core.FloatLiteral.as.ImplicitAs.impl.Convert.239] // CHECK:STDOUT: %bound_method.loc15_29.1: = bound_method %float, %impl.elem0 [concrete = constants.%Core.FloatLiteral.as.ImplicitAs.impl.Convert.bound] // CHECK:STDOUT: %specific_fn: = specific_function %impl.elem0, @Core.FloatLiteral.as.ImplicitAs.impl.Convert(constants.%int_64) [concrete = constants.%Core.FloatLiteral.as.ImplicitAs.impl.Convert.specific_fn] // CHECK:STDOUT: %bound_method.loc15_29.2: = bound_method %float, %specific_fn [concrete = constants.%bound_method] // CHECK:STDOUT: %Core.FloatLiteral.as.ImplicitAs.impl.Convert.call: init %f64.d77 = call %bound_method.loc15_29.2(%float) [concrete = constants.%float.d20] // CHECK:STDOUT: %.loc15_29: init %f64.d77 = converted %float, %Core.FloatLiteral.as.ImplicitAs.impl.Convert.call [concrete = constants.%float.d20] // CHECK:STDOUT: return %.loc15_29 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @Run() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %x.patt: %pattern_type.7ce = ref_binding_pattern x [concrete] // CHECK:STDOUT: %x.var_patt: %pattern_type.7ce = var_pattern %x.patt [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %x.var: ref %i32 = var %x.var_patt // CHECK:STDOUT: %Foo.ref: %Foo.type = name_ref Foo, file.%Foo.decl [concrete = constants.%Foo] // CHECK:STDOUT: %Foo.call: init %f64.d77 = call %Foo.ref() // CHECK:STDOUT: %.loc25: %i32 = converted %Foo.call, [concrete = ] // CHECK:STDOUT: assign %x.var, // CHECK:STDOUT: %i32: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %x: ref %i32 = ref_binding x, %x.var // CHECK:STDOUT: %Destroy.Op.bound: = bound_method %x.var, constants.%Destroy.Op // CHECK:STDOUT: %Destroy.Op.call: init %empty_tuple.type = call %Destroy.Op.bound(%x.var) // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @Destroy.Op(%self.param: ref %i32) = "no_op"; // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/function/call/fail_runtime_implicit_param.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/none.carbon // TODO: Add ranges and switch to "--dump-sem-ir-ranges=only". // EXTRA-ARGS: --dump-sem-ir-ranges=if-present // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/function/call/fail_runtime_implicit_param.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/function/call/fail_runtime_implicit_param.carbon // CHECK:STDERR: fail_runtime_implicit_param.carbon:[[@LINE+4]]:6: error: implicit parameters of functions must be constant or `self` [ImplictParamMustBeConstant] // CHECK:STDERR: fn F[s: ()](); // CHECK:STDERR: ^~~~~ // CHECK:STDERR: fn F[s: ()](); fn Run() { F(); } // CHECK:STDOUT: --- fail_runtime_implicit_param.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] // CHECK:STDOUT: %F.type: type = fn_type @F [concrete] // CHECK:STDOUT: %F: %F.type = struct_value () [concrete] // CHECK:STDOUT: %Run.type: type = fn_type @Run [concrete] // CHECK:STDOUT: %Run: %Run.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .F = %F.decl // CHECK:STDOUT: .Run = %Run.decl // CHECK:STDOUT: } // CHECK:STDOUT: %F.decl: %F.type = fn_decl @F [concrete = constants.%F] {} {} // CHECK:STDOUT: %Run.decl: %Run.type = fn_decl @Run [concrete = constants.%Run] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @F(); // CHECK:STDOUT: // CHECK:STDOUT: fn @Run() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %F.ref: %F.type = name_ref F, file.%F.decl [concrete = constants.%F] // CHECK:STDOUT: %F.call: init %empty_tuple.type = call %F.ref() // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/function/call/form.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/int.carbon // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/function/call/form.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/function/call/form.carbon // --- ref_form_param.carbon library "[[@TEST_NAME]]"; //@dump-sem-ir-begin fn F(x:? form(ref i32)); //@dump-sem-ir-end fn G() { var y: i32 = 0; //@dump-sem-ir-begin F(ref y); //@dump-sem-ir-end } // --- var_form_param.carbon library "[[@TEST_NAME]]"; //@dump-sem-ir-begin fn F(x:? form(var i32)); //@dump-sem-ir-end fn G() { //@dump-sem-ir-begin F(0); //@dump-sem-ir-end } // --- val_form_param.carbon library "[[@TEST_NAME]]"; //@dump-sem-ir-begin fn F(x:? form(val i32)); //@dump-sem-ir-end fn G() { //@dump-sem-ir-begin F(0); //@dump-sem-ir-end } // --- type_component_needs_conversion.carbon library "[[@TEST_NAME]]"; //@dump-sem-ir-begin fn F(x:? form(ref ())); //@dump-sem-ir-end // --- fail_param_form_not_constant.carbon library "[[@TEST_NAME]]"; fn F() -> Core.Form(); // CHECK:STDERR: fail_param_form_not_constant.carbon:[[@LINE+4]]:10: error: cannot evaluate form expression [FormExprEvaluationFailure] // CHECK:STDERR: fn G(x:? F()); // CHECK:STDERR: ^~~ // CHECK:STDERR: fn G(x:? F()); // --- fail_form_param_not_form.carbon library "[[@TEST_NAME]]"; // CHECK:STDERR: fail_form_param_not_form.carbon:[[@LINE+7]]:10: error: cannot implicitly convert expression of type `type` to `Core.Form` [ConversionFailure] // CHECK:STDERR: fn F(x:? i32); // CHECK:STDERR: ^~~ // CHECK:STDERR: fail_form_param_not_form.carbon:[[@LINE+4]]:10: note: type `type` does not implement interface `Core.ImplicitAs(Core.Form)` [MissingImplInMemberAccessInContext] // CHECK:STDERR: fn F(x:? i32); // CHECK:STDERR: ^~~ // CHECK:STDERR: fn F(x:? i32); // CHECK:STDERR: fail_form_param_not_form.carbon:[[@LINE+8]]:6: error: semantics TODO: `handle invalid parse trees in `check`` [SemanticsTodo] // CHECK:STDERR: fn G(ref x:? i32); // CHECK:STDERR: ^~~~~ // CHECK:STDERR: // CHECK:STDERR: fail_form_param_not_form.carbon:[[@LINE+4]]:6: error: expected `:` binding after `ref` [ExpectedRuntimeBindingPatternAfterRef] // CHECK:STDERR: fn G(ref x:? i32); // CHECK:STDERR: ^~~ // CHECK:STDERR: fn G(ref x:? i32); // --- fail_missing_ref_tag.carbon library "[[@TEST_NAME]]"; fn F(x:? form(ref i32)); fn G() { var y: i32 = 0; // CHECK:STDERR: fail_missing_ref_tag.carbon:[[@LINE+7]]:5: error: argument to `ref` parameter not marked with `ref` [RefParamNoRefTag] // CHECK:STDERR: F(y); // CHECK:STDERR: ^ // CHECK:STDERR: fail_missing_ref_tag.carbon:[[@LINE-7]]:6: note: initializing function parameter [InCallToFunctionParam] // CHECK:STDERR: fn F(x:? form(ref i32)); // CHECK:STDERR: ^~~~~~~~~~~~~~~~~ // CHECK:STDERR: F(y); } // --- fail_todo_form_generic_param.carbon library "[[@TEST_NAME]]"; // CHECK:STDERR: fail_todo_form_generic_param.carbon:[[@LINE+4]]:26: error: semantics TODO: `Support symbolic form bindings` [SemanticsTodo] // CHECK:STDERR: fn F(Form:! Core.Form(), x:? Form); // CHECK:STDERR: ^~~~~~~~ // CHECK:STDERR: fn F(Form:! Core.Form(), x:? Form); fn G() { var x: i32; F(form(var i32), x); } // --- fail_todo_local_init_form_binding.carbon library "[[@TEST_NAME]]"; fn F() { // CHECK:STDERR: fail_todo_local_init_form_binding.carbon:[[@LINE+12]]:7: error: semantics TODO: `Support local initializing forms` [SemanticsTodo] // CHECK:STDERR: let x:? form(var i32) = 0; // CHECK:STDERR: ^ // CHECK:STDERR: // CHECK:STDERR: fail_todo_local_init_form_binding.carbon:[[@LINE+8]]:27: error: cannot bind durable reference to non-reference value of type `i32` [ConversionFailureNonRefToRef] // CHECK:STDERR: let x:? form(var i32) = 0; // CHECK:STDERR: ^ // CHECK:STDERR: // CHECK:STDERR: fail_todo_local_init_form_binding.carbon:[[@LINE+4]]:7: warning: binding `x` unused [UnusedBinding] // CHECK:STDERR: let x:? form(var i32) = 0; // CHECK:STDERR: ^ // CHECK:STDERR: let x:? form(var i32) = 0; } // --- fail_todo_local_symbolic_form_binding.carbon library "[[@TEST_NAME]]"; // TODO: this code should fail for a different reason, but right now we don't // have a way to write correct code that reaches the TODO we're exercising here. fn F(Form:! Core.Form()) { // CHECK:STDERR: fail_todo_local_symbolic_form_binding.carbon:[[@LINE+4]]:7: error: semantics TODO: `Support symbolic form bindings` [SemanticsTodo] // CHECK:STDERR: let y:? Form = 0; // CHECK:STDERR: ^~~~~~~~ // CHECK:STDERR: let y:? Form = 0; } // --- ref_return_form.carbon library "[[@TEST_NAME]]"; //@dump-sem-ir-begin fn F() ->? form(ref i32); //@dump-sem-ir-end fn G() { //@dump-sem-ir-begin let ref _: i32 = F(); //@dump-sem-ir-end } // --- var_return_form.carbon library "[[@TEST_NAME]]"; //@dump-sem-ir-begin fn F() ->? form(var i32); //@dump-sem-ir-end fn G() { //@dump-sem-ir-begin let var _: i32 = F(); //@dump-sem-ir-end } // --- val_return_form.carbon library "[[@TEST_NAME]]"; //@dump-sem-ir-begin fn F() ->? form(val i32); //@dump-sem-ir-end fn G() { //@dump-sem-ir-begin let _: i32 = F(); //@dump-sem-ir-end } // --- fail_return_form_not_form.carbon library "[[@TEST_NAME]]"; // CHECK:STDERR: fail_return_form_not_form.carbon:[[@LINE+7]]:12: error: cannot implicitly convert expression of type `type` to `Core.Form` [ConversionFailure] // CHECK:STDERR: fn F() ->? i32; // CHECK:STDERR: ^~~ // CHECK:STDERR: fail_return_form_not_form.carbon:[[@LINE+4]]:12: note: type `type` does not implement interface `Core.ImplicitAs(Core.Form)` [MissingImplInMemberAccessInContext] // CHECK:STDERR: fn F() ->? i32; // CHECK:STDERR: ^~~ // CHECK:STDERR: fn F() ->? i32; // CHECK:STDERR: fail_return_form_not_form.carbon:[[@LINE+11]]:12: error: `ref` tag is not an argument to a `ref` parameter [RefTagNoRefParam] // CHECK:STDERR: fn F() ->? ref i32; // CHECK:STDERR: ^~~~~~~ // CHECK:STDERR: // CHECK:STDERR: fail_return_form_not_form.carbon:[[@LINE+7]]:12: error: cannot implicitly convert expression of type `type` to `Core.Form` [ConversionFailure] // CHECK:STDERR: fn F() ->? ref i32; // CHECK:STDERR: ^~~~~~~ // CHECK:STDERR: fail_return_form_not_form.carbon:[[@LINE+4]]:12: note: type `type` does not implement interface `Core.ImplicitAs(Core.Form)` [MissingImplInMemberAccessInContext] // CHECK:STDERR: fn F() ->? ref i32; // CHECK:STDERR: ^~~~~~~ // CHECK:STDERR: fn F() ->? ref i32; // -- fail_todo_form_generic_return.carbon // CHECK:STDERR: fail_return_form_not_form.carbon:[[@LINE+11]]:26: error: semantics TODO: `Support symbolic return forms` [SemanticsTodo] // CHECK:STDERR: fn F(Form:! Core.Form()) ->? Form; // CHECK:STDERR: ^~~~~~~~ // CHECK:STDERR: // CHECK:STDERR: fail_return_form_not_form.carbon:[[@LINE+7]]:1: error: redeclaration differs because of parameter count of 1 [RedeclParamCountDiffers] // CHECK:STDERR: fn F(Form:! Core.Form()) ->? Form; // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: fail_return_form_not_form.carbon:[[@LINE-23]]:1: note: previously declared with parameter count of 0 [RedeclParamCountPrevious] // CHECK:STDERR: fn F() ->? i32; // CHECK:STDERR: ^~~~~~~~~~~~~~~ // CHECK:STDERR: fn F(Form:! Core.Form()) ->? Form; // CHECK:STDOUT: --- ref_form_param.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [concrete] // CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] // CHECK:STDOUT: %i32: type = class_type @Int, @Int(%int_32) [concrete] // CHECK:STDOUT: %.1da: Core.Form = ref_form %i32 [concrete] // CHECK:STDOUT: %pattern_type.7ce: type = pattern_type %i32 [concrete] // CHECK:STDOUT: %F.type: type = fn_type @F [concrete] // CHECK:STDOUT: %F: %F.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: %F.decl: %F.type = fn_decl @F [concrete = constants.%F] { // CHECK:STDOUT: %x.patt: %pattern_type.7ce = form_binding_pattern x [concrete] // CHECK:STDOUT: %.loc4_7: %pattern_type.7ce = form_param_pattern %x.patt, constants.%.1da [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %x.param: ref %i32 = ref_param call_param0 // CHECK:STDOUT: %.loc4_15.1: Core.Form = splice_block %.loc4_15.2 [concrete = constants.%.1da] { // CHECK:STDOUT: %i32: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %.loc4_15.2: Core.Form = ref_form %i32 [concrete = constants.%.1da] // CHECK:STDOUT: } // CHECK:STDOUT: %x: ref %i32 = form_binding x, %x.param // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @F(%x.param: ref %i32); // CHECK:STDOUT: // CHECK:STDOUT: fn @G() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: // CHECK:STDOUT: %F.ref: %F.type = name_ref F, file.%F.decl [concrete = constants.%F] // CHECK:STDOUT: %y.ref: ref %i32 = name_ref y, %y // CHECK:STDOUT: %.loc10: %i32 = ref_tag %y.ref // CHECK:STDOUT: %F.call: init %empty_tuple.type = call %F.ref(%.loc10) // CHECK:STDOUT: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- var_form_param.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [concrete] // CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] // CHECK:STDOUT: %i32: type = class_type @Int, @Int(%int_32) [concrete] // CHECK:STDOUT: %.ff5: Core.Form = init_form %i32 [concrete] // CHECK:STDOUT: %pattern_type.7ce: type = pattern_type %i32 [concrete] // CHECK:STDOUT: %F.type: type = fn_type @F [concrete] // CHECK:STDOUT: %F: %F.type = struct_value () [concrete] // CHECK:STDOUT: %int_0.5c6: Core.IntLiteral = int_value 0 [concrete] // CHECK:STDOUT: %ImplicitAs.type.e8c: type = facet_type <@ImplicitAs, @ImplicitAs(%i32)> [concrete] // CHECK:STDOUT: %To: Core.IntLiteral = symbolic_binding To, 0 [symbolic] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.4e6: type = fn_type @Core.IntLiteral.as.ImplicitAs.impl.Convert, @Core.IntLiteral.as.ImplicitAs.impl(%To) [symbolic] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.3c2: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.4e6 = struct_value () [symbolic] // CHECK:STDOUT: %ImplicitAs.impl_witness.6bc: = impl_witness imports.%ImplicitAs.impl_witness_table.74f, @Core.IntLiteral.as.ImplicitAs.impl(%int_32) [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.e0d: type = fn_type @Core.IntLiteral.as.ImplicitAs.impl.Convert, @Core.IntLiteral.as.ImplicitAs.impl(%int_32) [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.e0d = struct_value () [concrete] // CHECK:STDOUT: %ImplicitAs.facet: %ImplicitAs.type.e8c = facet_value Core.IntLiteral, (%ImplicitAs.impl_witness.6bc) [concrete] // CHECK:STDOUT: %ImplicitAs.WithSelf.Convert.type.b37: type = fn_type @ImplicitAs.WithSelf.Convert, @ImplicitAs.WithSelf(%i32, %ImplicitAs.facet) [concrete] // CHECK:STDOUT: %.545: type = fn_type_with_self_type %ImplicitAs.WithSelf.Convert.type.b37, %ImplicitAs.facet [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.bound: = bound_method %int_0.5c6, %Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5 [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn: = specific_function %Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5, @Core.IntLiteral.as.ImplicitAs.impl.Convert(%int_32) [concrete] // CHECK:STDOUT: %bound_method: = bound_method %int_0.5c6, %Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn [concrete] // CHECK:STDOUT: %int_0.6a9: %i32 = int_value 0 [concrete] // CHECK:STDOUT: %Destroy.Op.type: type = fn_type @Destroy.Op [concrete] // CHECK:STDOUT: %Destroy.Op: %Destroy.Op.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core.import_ref.42d: @Core.IntLiteral.as.ImplicitAs.impl.%Core.IntLiteral.as.ImplicitAs.impl.Convert.type (%Core.IntLiteral.as.ImplicitAs.impl.Convert.type.4e6) = import_ref Core//prelude/parts/int, loc{{\d+_\d+}}, loaded [symbolic = @Core.IntLiteral.as.ImplicitAs.impl.%Core.IntLiteral.as.ImplicitAs.impl.Convert (constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.3c2)] // CHECK:STDOUT: %ImplicitAs.impl_witness_table.74f = impl_witness_table (%Core.import_ref.42d), @Core.IntLiteral.as.ImplicitAs.impl [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: %F.decl: %F.type = fn_decl @F [concrete = constants.%F] { // CHECK:STDOUT: %x.patt: %pattern_type.7ce = form_binding_pattern x [concrete] // CHECK:STDOUT: %.loc4_7: %pattern_type.7ce = form_param_pattern %x.patt, constants.%.ff5 [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %x.param: ref %i32 = ref_param call_param0 // CHECK:STDOUT: %.loc4_15.1: Core.Form = splice_block %.loc4_15.2 [concrete = constants.%.ff5] { // CHECK:STDOUT: %i32: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %.loc4_15.2: Core.Form = init_form %i32 [concrete = constants.%.ff5] // CHECK:STDOUT: } // CHECK:STDOUT: %x: ref %i32 = form_binding x, %x.param // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @F(%x.param: ref %i32); // CHECK:STDOUT: // CHECK:STDOUT: fn @G() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %F.ref: %F.type = name_ref F, file.%F.decl [concrete = constants.%F] // CHECK:STDOUT: %int_0: Core.IntLiteral = int_value 0 [concrete = constants.%int_0.5c6] // CHECK:STDOUT: %.loc4_7.1: ref %i32 = temporary_storage // CHECK:STDOUT: %impl.elem0: %.545 = impl_witness_access constants.%ImplicitAs.impl_witness.6bc, element0 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5] // CHECK:STDOUT: %bound_method.loc4_7.1: = bound_method %int_0, %impl.elem0 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.bound] // CHECK:STDOUT: %specific_fn: = specific_function %impl.elem0, @Core.IntLiteral.as.ImplicitAs.impl.Convert(constants.%int_32) [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn] // CHECK:STDOUT: %bound_method.loc4_7.2: = bound_method %int_0, %specific_fn [concrete = constants.%bound_method] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.call: init %i32 = call %bound_method.loc4_7.2(%int_0) [concrete = constants.%int_0.6a9] // CHECK:STDOUT: %.loc4_7.2: init %i32 = converted %int_0, %Core.IntLiteral.as.ImplicitAs.impl.Convert.call [concrete = constants.%int_0.6a9] // CHECK:STDOUT: %.loc4_7.3: ref %i32 = temporary %.loc4_7.1, %.loc4_7.2 // CHECK:STDOUT: %F.call: init %empty_tuple.type = call %F.ref(%.loc4_7.3) // CHECK:STDOUT: %Destroy.Op.bound: = bound_method %.loc4_7.3, constants.%Destroy.Op // CHECK:STDOUT: %Destroy.Op.call: init %empty_tuple.type = call %Destroy.Op.bound(%.loc4_7.3) // CHECK:STDOUT: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @Destroy.Op(%self.param: ref %i32) = "no_op"; // CHECK:STDOUT: // CHECK:STDOUT: --- val_form_param.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [concrete] // CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] // CHECK:STDOUT: %i32: type = class_type @Int, @Int(%int_32) [concrete] // CHECK:STDOUT: %.754: Core.Form = value_form %i32 [concrete] // CHECK:STDOUT: %pattern_type.7ce: type = pattern_type %i32 [concrete] // CHECK:STDOUT: %F.type: type = fn_type @F [concrete] // CHECK:STDOUT: %F: %F.type = struct_value () [concrete] // CHECK:STDOUT: %int_0.5c6: Core.IntLiteral = int_value 0 [concrete] // CHECK:STDOUT: %ImplicitAs.type.e8c: type = facet_type <@ImplicitAs, @ImplicitAs(%i32)> [concrete] // CHECK:STDOUT: %To: Core.IntLiteral = symbolic_binding To, 0 [symbolic] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.4e6: type = fn_type @Core.IntLiteral.as.ImplicitAs.impl.Convert, @Core.IntLiteral.as.ImplicitAs.impl(%To) [symbolic] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.3c2: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.4e6 = struct_value () [symbolic] // CHECK:STDOUT: %ImplicitAs.impl_witness.6bc: = impl_witness imports.%ImplicitAs.impl_witness_table.74f, @Core.IntLiteral.as.ImplicitAs.impl(%int_32) [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.e0d: type = fn_type @Core.IntLiteral.as.ImplicitAs.impl.Convert, @Core.IntLiteral.as.ImplicitAs.impl(%int_32) [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.e0d = struct_value () [concrete] // CHECK:STDOUT: %ImplicitAs.facet: %ImplicitAs.type.e8c = facet_value Core.IntLiteral, (%ImplicitAs.impl_witness.6bc) [concrete] // CHECK:STDOUT: %ImplicitAs.WithSelf.Convert.type.b37: type = fn_type @ImplicitAs.WithSelf.Convert, @ImplicitAs.WithSelf(%i32, %ImplicitAs.facet) [concrete] // CHECK:STDOUT: %.545: type = fn_type_with_self_type %ImplicitAs.WithSelf.Convert.type.b37, %ImplicitAs.facet [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.bound: = bound_method %int_0.5c6, %Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5 [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn: = specific_function %Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5, @Core.IntLiteral.as.ImplicitAs.impl.Convert(%int_32) [concrete] // CHECK:STDOUT: %bound_method: = bound_method %int_0.5c6, %Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn [concrete] // CHECK:STDOUT: %int_0.6a9: %i32 = int_value 0 [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core.import_ref.42d: @Core.IntLiteral.as.ImplicitAs.impl.%Core.IntLiteral.as.ImplicitAs.impl.Convert.type (%Core.IntLiteral.as.ImplicitAs.impl.Convert.type.4e6) = import_ref Core//prelude/parts/int, loc{{\d+_\d+}}, loaded [symbolic = @Core.IntLiteral.as.ImplicitAs.impl.%Core.IntLiteral.as.ImplicitAs.impl.Convert (constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.3c2)] // CHECK:STDOUT: %ImplicitAs.impl_witness_table.74f = impl_witness_table (%Core.import_ref.42d), @Core.IntLiteral.as.ImplicitAs.impl [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: %F.decl: %F.type = fn_decl @F [concrete = constants.%F] { // CHECK:STDOUT: %x.patt: %pattern_type.7ce = form_binding_pattern x [concrete] // CHECK:STDOUT: %.loc4_7: %pattern_type.7ce = form_param_pattern %x.patt, constants.%.754 [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %x.param: %i32 = value_param call_param0 // CHECK:STDOUT: %.loc4_15.1: Core.Form = splice_block %.loc4_15.2 [concrete = constants.%.754] { // CHECK:STDOUT: %i32: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %.loc4_15.2: Core.Form = value_form %i32 [concrete = constants.%.754] // CHECK:STDOUT: } // CHECK:STDOUT: %x: %i32 = form_binding x, %x.param // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @F(%x.param: %i32); // CHECK:STDOUT: // CHECK:STDOUT: fn @G() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %F.ref: %F.type = name_ref F, file.%F.decl [concrete = constants.%F] // CHECK:STDOUT: %int_0: Core.IntLiteral = int_value 0 [concrete = constants.%int_0.5c6] // CHECK:STDOUT: %impl.elem0: %.545 = impl_witness_access constants.%ImplicitAs.impl_witness.6bc, element0 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5] // CHECK:STDOUT: %bound_method.loc9_5.1: = bound_method %int_0, %impl.elem0 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.bound] // CHECK:STDOUT: %specific_fn: = specific_function %impl.elem0, @Core.IntLiteral.as.ImplicitAs.impl.Convert(constants.%int_32) [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn] // CHECK:STDOUT: %bound_method.loc9_5.2: = bound_method %int_0, %specific_fn [concrete = constants.%bound_method] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.call: init %i32 = call %bound_method.loc9_5.2(%int_0) [concrete = constants.%int_0.6a9] // CHECK:STDOUT: %.loc9_5.1: %i32 = value_of_initializer %Core.IntLiteral.as.ImplicitAs.impl.Convert.call [concrete = constants.%int_0.6a9] // CHECK:STDOUT: %.loc9_5.2: %i32 = converted %int_0, %.loc9_5.1 [concrete = constants.%int_0.6a9] // CHECK:STDOUT: %F.call: init %empty_tuple.type = call %F.ref(%.loc9_5.2) // CHECK:STDOUT: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- type_component_needs_conversion.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] // CHECK:STDOUT: %empty_tuple: %empty_tuple.type = tuple_value () [concrete] // CHECK:STDOUT: %.9f9: Core.Form = ref_form %empty_tuple.type [concrete] // CHECK:STDOUT: %pattern_type: type = pattern_type %empty_tuple.type [concrete] // CHECK:STDOUT: %F.type: type = fn_type @F [concrete] // CHECK:STDOUT: %F: %F.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: %F.decl: %F.type = fn_decl @F [concrete = constants.%F] { // CHECK:STDOUT: %x.patt: %pattern_type = form_binding_pattern x [concrete] // CHECK:STDOUT: %.loc4_7: %pattern_type = form_param_pattern %x.patt, constants.%.9f9 [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %x.param: ref %empty_tuple.type = ref_param call_param0 // CHECK:STDOUT: %.loc4_15.1: Core.Form = splice_block %.loc4_15.2 [concrete = constants.%.9f9] { // CHECK:STDOUT: %.loc4_20.1: %empty_tuple.type = tuple_literal () [concrete = constants.%empty_tuple] // CHECK:STDOUT: %.loc4_20.2: type = converted %.loc4_20.1, constants.%empty_tuple.type [concrete = constants.%empty_tuple.type] // CHECK:STDOUT: %.loc4_15.2: Core.Form = ref_form %.loc4_20.2 [concrete = constants.%.9f9] // CHECK:STDOUT: } // CHECK:STDOUT: %x: ref %empty_tuple.type = form_binding x, %x.param // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @F(%x.param: ref %empty_tuple.type); // CHECK:STDOUT: // CHECK:STDOUT: --- ref_return_form.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [concrete] // CHECK:STDOUT: %i32: type = class_type @Int, @Int(%int_32) [concrete] // CHECK:STDOUT: %.1da: Core.Form = ref_form %i32 [concrete] // CHECK:STDOUT: %F.type: type = fn_type @F [concrete] // CHECK:STDOUT: %F: %F.type = struct_value () [concrete] // CHECK:STDOUT: %pattern_type.7ce: type = pattern_type %i32 [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: %F.decl: %F.type = fn_decl @F [concrete = constants.%F] {} { // CHECK:STDOUT: %i32: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %.loc4: Core.Form = ref_form %i32 [concrete = constants.%.1da] // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @F() -> ref %i32; // CHECK:STDOUT: // CHECK:STDOUT: fn @G() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %_.patt: %pattern_type.7ce = ref_binding_pattern _ [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %F.ref: %F.type = name_ref F, file.%F.decl [concrete = constants.%F] // CHECK:STDOUT: %F.call: ref %i32 = call %F.ref() // CHECK:STDOUT: %i32: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %_: ref %i32 = ref_binding _, %F.call // CHECK:STDOUT: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- var_return_form.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [concrete] // CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] // CHECK:STDOUT: %i32: type = class_type @Int, @Int(%int_32) [concrete] // CHECK:STDOUT: %.ff5: Core.Form = init_form %i32 [concrete] // CHECK:STDOUT: %pattern_type.7ce: type = pattern_type %i32 [concrete] // CHECK:STDOUT: %F.type: type = fn_type @F [concrete] // CHECK:STDOUT: %F: %F.type = struct_value () [concrete] // CHECK:STDOUT: %Destroy.Op.type: type = fn_type @Destroy.Op [concrete] // CHECK:STDOUT: %Destroy.Op: %Destroy.Op.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: %F.decl: %F.type = fn_decl @F [concrete = constants.%F] { // CHECK:STDOUT: %return.patt: %pattern_type.7ce = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: %pattern_type.7ce = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %i32: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %.loc4: Core.Form = init_form %i32 [concrete = constants.%.ff5] // CHECK:STDOUT: %return.param: ref %i32 = out_param call_param0 // CHECK:STDOUT: %return: ref %i32 = return_slot %return.param // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @F() -> out %return.param: %i32; // CHECK:STDOUT: // CHECK:STDOUT: fn @G() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %_.patt: %pattern_type.7ce = ref_binding_pattern _ [concrete] // CHECK:STDOUT: %_.var_patt: %pattern_type.7ce = var_pattern %_.patt [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %_.var: ref %i32 = var %_.var_patt // CHECK:STDOUT: %F.ref: %F.type = name_ref F, file.%F.decl [concrete = constants.%F] // CHECK:STDOUT: %F.call: init %i32 = call %F.ref() // CHECK:STDOUT: assign %_.var, %F.call // CHECK:STDOUT: %i32: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %_: ref %i32 = ref_binding _, %_.var // CHECK:STDOUT: %Destroy.Op.bound: = bound_method %_.var, constants.%Destroy.Op // CHECK:STDOUT: %Destroy.Op.call: init %empty_tuple.type = call %Destroy.Op.bound(%_.var) // CHECK:STDOUT: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @Destroy.Op(%self.param: ref %i32) = "no_op"; // CHECK:STDOUT: // CHECK:STDOUT: --- val_return_form.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [concrete] // CHECK:STDOUT: %i32: type = class_type @Int, @Int(%int_32) [concrete] // CHECK:STDOUT: %.754: Core.Form = value_form %i32 [concrete] // CHECK:STDOUT: %F.type: type = fn_type @F [concrete] // CHECK:STDOUT: %F: %F.type = struct_value () [concrete] // CHECK:STDOUT: %pattern_type.7ce: type = pattern_type %i32 [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: %F.decl: %F.type = fn_decl @F [concrete = constants.%F] {} { // CHECK:STDOUT: %i32: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %.loc4: Core.Form = value_form %i32 [concrete = constants.%.754] // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @F() -> val %i32; // CHECK:STDOUT: // CHECK:STDOUT: fn @G() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %_.patt: %pattern_type.7ce = value_binding_pattern _ [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %F.ref: %F.type = name_ref F, file.%F.decl [concrete = constants.%F] // CHECK:STDOUT: %F.call: %i32 = call %F.ref() // CHECK:STDOUT: %i32: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %_: %i32 = value_binding _, %F.call // CHECK:STDOUT: // CHECK:STDOUT: } // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/function/call/i32.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/int.carbon // TODO: Add ranges and switch to "--dump-sem-ir-ranges=only". // EXTRA-ARGS: --dump-sem-ir-ranges=if-present // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/function/call/i32.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/function/call/i32.carbon fn Echo(a: i32) -> i32 { return a; } fn Main() { var unused b: i32 = Echo(1); } // CHECK:STDOUT: --- i32.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [concrete] // CHECK:STDOUT: %Int.type: type = generic_class_type @Int [concrete] // CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] // CHECK:STDOUT: %Int.generic: %Int.type = struct_value () [concrete] // CHECK:STDOUT: %N: Core.IntLiteral = symbolic_binding N, 0 [symbolic] // CHECK:STDOUT: %i32: type = class_type @Int, @Int(%int_32) [concrete] // CHECK:STDOUT: %pattern_type.7ce: type = pattern_type %i32 [concrete] // CHECK:STDOUT: %.ff5: Core.Form = init_form %i32 [concrete] // CHECK:STDOUT: %Echo.type: type = fn_type @Echo [concrete] // CHECK:STDOUT: %Echo: %Echo.type = struct_value () [concrete] // CHECK:STDOUT: %Copy.type: type = facet_type <@Copy> [concrete] // CHECK:STDOUT: %Int.as.Copy.impl.Op.type.824: type = fn_type @Int.as.Copy.impl.Op, @Int.as.Copy.impl(%N) [symbolic] // CHECK:STDOUT: %Int.as.Copy.impl.Op.9b9: %Int.as.Copy.impl.Op.type.824 = struct_value () [symbolic] // CHECK:STDOUT: %Copy.impl_witness.f17: = impl_witness imports.%Copy.impl_witness_table.e76, @Int.as.Copy.impl(%int_32) [concrete] // CHECK:STDOUT: %Int.as.Copy.impl.Op.type.546: type = fn_type @Int.as.Copy.impl.Op, @Int.as.Copy.impl(%int_32) [concrete] // CHECK:STDOUT: %Int.as.Copy.impl.Op.664: %Int.as.Copy.impl.Op.type.546 = struct_value () [concrete] // CHECK:STDOUT: %Copy.facet: %Copy.type = facet_value %i32, (%Copy.impl_witness.f17) [concrete] // CHECK:STDOUT: %Copy.WithSelf.Op.type.081: type = fn_type @Copy.WithSelf.Op, @Copy.WithSelf(%Copy.facet) [concrete] // CHECK:STDOUT: %.8e2: type = fn_type_with_self_type %Copy.WithSelf.Op.type.081, %Copy.facet [concrete] // CHECK:STDOUT: %Int.as.Copy.impl.Op.specific_fn: = specific_function %Int.as.Copy.impl.Op.664, @Int.as.Copy.impl.Op(%int_32) [concrete] // CHECK:STDOUT: %Main.type: type = fn_type @Main [concrete] // CHECK:STDOUT: %Main: %Main.type = struct_value () [concrete] // CHECK:STDOUT: %int_1.5b8: Core.IntLiteral = int_value 1 [concrete] // CHECK:STDOUT: %ImplicitAs.type.cc7: type = generic_interface_type @ImplicitAs [concrete] // CHECK:STDOUT: %ImplicitAs.generic: %ImplicitAs.type.cc7 = struct_value () [concrete] // CHECK:STDOUT: %ImplicitAs.type.e8c: type = facet_type <@ImplicitAs, @ImplicitAs(%i32)> [concrete] // CHECK:STDOUT: %To: Core.IntLiteral = symbolic_binding To, 0 [symbolic] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.4e6: type = fn_type @Core.IntLiteral.as.ImplicitAs.impl.Convert, @Core.IntLiteral.as.ImplicitAs.impl(%To) [symbolic] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.3c2: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.4e6 = struct_value () [symbolic] // CHECK:STDOUT: %ImplicitAs.impl_witness.6bc: = impl_witness imports.%ImplicitAs.impl_witness_table.74f, @Core.IntLiteral.as.ImplicitAs.impl(%int_32) [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.e0d: type = fn_type @Core.IntLiteral.as.ImplicitAs.impl.Convert, @Core.IntLiteral.as.ImplicitAs.impl(%int_32) [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.e0d = struct_value () [concrete] // CHECK:STDOUT: %ImplicitAs.facet: %ImplicitAs.type.e8c = facet_value Core.IntLiteral, (%ImplicitAs.impl_witness.6bc) [concrete] // CHECK:STDOUT: %ImplicitAs.WithSelf.Convert.type.b37: type = fn_type @ImplicitAs.WithSelf.Convert, @ImplicitAs.WithSelf(%i32, %ImplicitAs.facet) [concrete] // CHECK:STDOUT: %.545: type = fn_type_with_self_type %ImplicitAs.WithSelf.Convert.type.b37, %ImplicitAs.facet [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.bound: = bound_method %int_1.5b8, %Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5 [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn: = specific_function %Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5, @Core.IntLiteral.as.ImplicitAs.impl.Convert(%int_32) [concrete] // CHECK:STDOUT: %bound_method: = bound_method %int_1.5b8, %Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn [concrete] // CHECK:STDOUT: %int_1.5d2: %i32 = int_value 1 [concrete] // CHECK:STDOUT: %Destroy.type: type = facet_type <@Destroy> [concrete] // CHECK:STDOUT: %Destroy.Op.type: type = fn_type @Destroy.Op [concrete] // CHECK:STDOUT: %Destroy.Op: %Destroy.Op.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: .Int = %Core.Int // CHECK:STDOUT: .Copy = %Core.Copy // CHECK:STDOUT: .ImplicitAs = %Core.ImplicitAs // CHECK:STDOUT: .Destroy = %Core.Destroy // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: %Core.Int: %Int.type = import_ref Core//prelude/parts/int, Int, loaded [concrete = constants.%Int.generic] // CHECK:STDOUT: %Core.Copy: type = import_ref Core//prelude/parts/copy, Copy, loaded [concrete = constants.%Copy.type] // CHECK:STDOUT: %Core.import_ref.18d: @Int.as.Copy.impl.%Int.as.Copy.impl.Op.type (%Int.as.Copy.impl.Op.type.824) = import_ref Core//prelude/parts/int, loc{{\d+_\d+}}, loaded [symbolic = @Int.as.Copy.impl.%Int.as.Copy.impl.Op (constants.%Int.as.Copy.impl.Op.9b9)] // CHECK:STDOUT: %Copy.impl_witness_table.e76 = impl_witness_table (%Core.import_ref.18d), @Int.as.Copy.impl [concrete] // CHECK:STDOUT: %Core.ImplicitAs: %ImplicitAs.type.cc7 = import_ref Core//prelude/parts/as, ImplicitAs, loaded [concrete = constants.%ImplicitAs.generic] // CHECK:STDOUT: %Core.import_ref.42d: @Core.IntLiteral.as.ImplicitAs.impl.%Core.IntLiteral.as.ImplicitAs.impl.Convert.type (%Core.IntLiteral.as.ImplicitAs.impl.Convert.type.4e6) = import_ref Core//prelude/parts/int, loc{{\d+_\d+}}, loaded [symbolic = @Core.IntLiteral.as.ImplicitAs.impl.%Core.IntLiteral.as.ImplicitAs.impl.Convert (constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.3c2)] // CHECK:STDOUT: %ImplicitAs.impl_witness_table.74f = impl_witness_table (%Core.import_ref.42d), @Core.IntLiteral.as.ImplicitAs.impl [concrete] // CHECK:STDOUT: %Core.Destroy: type = import_ref Core//prelude/parts/destroy, Destroy, loaded [concrete = constants.%Destroy.type] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .Echo = %Echo.decl // CHECK:STDOUT: .Main = %Main.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %Echo.decl: %Echo.type = fn_decl @Echo [concrete = constants.%Echo] { // CHECK:STDOUT: %a.patt: %pattern_type.7ce = value_binding_pattern a [concrete] // CHECK:STDOUT: %a.param_patt: %pattern_type.7ce = value_param_pattern %a.patt [concrete] // CHECK:STDOUT: %return.patt: %pattern_type.7ce = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: %pattern_type.7ce = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %i32.loc15_20: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %.loc15: Core.Form = init_form %i32.loc15_20 [concrete = constants.%.ff5] // CHECK:STDOUT: %a.param: %i32 = value_param call_param0 // CHECK:STDOUT: %i32.loc15_12: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %a: %i32 = value_binding a, %a.param // CHECK:STDOUT: %return.param: ref %i32 = out_param call_param1 // CHECK:STDOUT: %return: ref %i32 = return_slot %return.param // CHECK:STDOUT: } // CHECK:STDOUT: %Main.decl: %Main.type = fn_decl @Main [concrete = constants.%Main] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @Echo(%a.param: %i32) -> out %return.param: %i32 { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %a.ref: %i32 = name_ref a, %a // CHECK:STDOUT: %impl.elem0: %.8e2 = impl_witness_access constants.%Copy.impl_witness.f17, element0 [concrete = constants.%Int.as.Copy.impl.Op.664] // CHECK:STDOUT: %bound_method.loc16_10.1: = bound_method %a.ref, %impl.elem0 // CHECK:STDOUT: %specific_fn: = specific_function %impl.elem0, @Int.as.Copy.impl.Op(constants.%int_32) [concrete = constants.%Int.as.Copy.impl.Op.specific_fn] // CHECK:STDOUT: %bound_method.loc16_10.2: = bound_method %a.ref, %specific_fn // CHECK:STDOUT: %Int.as.Copy.impl.Op.call: init %i32 = call %bound_method.loc16_10.2(%a.ref) // CHECK:STDOUT: return %Int.as.Copy.impl.Op.call // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @Main() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %b.patt: %pattern_type.7ce = ref_binding_pattern b [concrete] // CHECK:STDOUT: %b.var_patt: %pattern_type.7ce = var_pattern %b.patt [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %b.var: ref %i32 = var %b.var_patt // CHECK:STDOUT: %Echo.ref: %Echo.type = name_ref Echo, file.%Echo.decl [concrete = constants.%Echo] // CHECK:STDOUT: %int_1: Core.IntLiteral = int_value 1 [concrete = constants.%int_1.5b8] // CHECK:STDOUT: %impl.elem0: %.545 = impl_witness_access constants.%ImplicitAs.impl_witness.6bc, element0 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5] // CHECK:STDOUT: %bound_method.loc20_28.1: = bound_method %int_1, %impl.elem0 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.bound] // CHECK:STDOUT: %specific_fn: = specific_function %impl.elem0, @Core.IntLiteral.as.ImplicitAs.impl.Convert(constants.%int_32) [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn] // CHECK:STDOUT: %bound_method.loc20_28.2: = bound_method %int_1, %specific_fn [concrete = constants.%bound_method] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.call: init %i32 = call %bound_method.loc20_28.2(%int_1) [concrete = constants.%int_1.5d2] // CHECK:STDOUT: %.loc20_28.1: %i32 = value_of_initializer %Core.IntLiteral.as.ImplicitAs.impl.Convert.call [concrete = constants.%int_1.5d2] // CHECK:STDOUT: %.loc20_28.2: %i32 = converted %int_1, %.loc20_28.1 [concrete = constants.%int_1.5d2] // CHECK:STDOUT: %Echo.call: init %i32 = call %Echo.ref(%.loc20_28.2) // CHECK:STDOUT: assign %b.var, %Echo.call // CHECK:STDOUT: %i32: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %b: ref %i32 = ref_binding b, %b.var // CHECK:STDOUT: %Destroy.Op.bound: = bound_method %b.var, constants.%Destroy.Op // CHECK:STDOUT: %Destroy.Op.call: init %empty_tuple.type = call %Destroy.Op.bound(%b.var) // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @Destroy.Op(%self.param: ref %i32) = "no_op"; // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/function/call/more_param_ir.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/int.carbon // TODO: Add ranges and switch to "--dump-sem-ir-ranges=only". // EXTRA-ARGS: --dump-sem-ir-ranges=if-present // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/function/call/more_param_ir.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/function/call/more_param_ir.carbon fn Foo(unused a: i32, unused b: i32) {} fn Main() { var x: (i32,) = (1,); // Generates multiple IR instructions for the first parameter. Foo(x.0, 6); } // CHECK:STDOUT: --- more_param_ir.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [concrete] // CHECK:STDOUT: %Int.type: type = generic_class_type @Int [concrete] // CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] // CHECK:STDOUT: %Int.generic: %Int.type = struct_value () [concrete] // CHECK:STDOUT: %i32: type = class_type @Int, @Int(%int_32) [concrete] // CHECK:STDOUT: %pattern_type.7ce: type = pattern_type %i32 [concrete] // CHECK:STDOUT: %Foo.type: type = fn_type @Foo [concrete] // CHECK:STDOUT: %Foo: %Foo.type = struct_value () [concrete] // CHECK:STDOUT: %Main.type: type = fn_type @Main [concrete] // CHECK:STDOUT: %Main: %Main.type = struct_value () [concrete] // CHECK:STDOUT: %tuple.type.85c: type = tuple_type (type) [concrete] // CHECK:STDOUT: %tuple.896: %tuple.type.85c = tuple_value (%i32) [concrete] // CHECK:STDOUT: %tuple.type.a1c: type = tuple_type (%i32) [concrete] // CHECK:STDOUT: %pattern_type.b74: type = pattern_type %tuple.type.a1c [concrete] // CHECK:STDOUT: %int_1.5b8: Core.IntLiteral = int_value 1 [concrete] // CHECK:STDOUT: %tuple.type.985: type = tuple_type (Core.IntLiteral) [concrete] // CHECK:STDOUT: %tuple.378: %tuple.type.985 = tuple_value (%int_1.5b8) [concrete] // CHECK:STDOUT: %ImplicitAs.type.cc7: type = generic_interface_type @ImplicitAs [concrete] // CHECK:STDOUT: %ImplicitAs.generic: %ImplicitAs.type.cc7 = struct_value () [concrete] // CHECK:STDOUT: %ImplicitAs.type.e8c: type = facet_type <@ImplicitAs, @ImplicitAs(%i32)> [concrete] // CHECK:STDOUT: %To: Core.IntLiteral = symbolic_binding To, 0 [symbolic] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.4e6: type = fn_type @Core.IntLiteral.as.ImplicitAs.impl.Convert, @Core.IntLiteral.as.ImplicitAs.impl(%To) [symbolic] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.3c2: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.4e6 = struct_value () [symbolic] // CHECK:STDOUT: %ImplicitAs.impl_witness.6bc: = impl_witness imports.%ImplicitAs.impl_witness_table.74f, @Core.IntLiteral.as.ImplicitAs.impl(%int_32) [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.e0d: type = fn_type @Core.IntLiteral.as.ImplicitAs.impl.Convert, @Core.IntLiteral.as.ImplicitAs.impl(%int_32) [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.e0d = struct_value () [concrete] // CHECK:STDOUT: %ImplicitAs.facet: %ImplicitAs.type.e8c = facet_value Core.IntLiteral, (%ImplicitAs.impl_witness.6bc) [concrete] // CHECK:STDOUT: %ImplicitAs.WithSelf.Convert.type.b37: type = fn_type @ImplicitAs.WithSelf.Convert, @ImplicitAs.WithSelf(%i32, %ImplicitAs.facet) [concrete] // CHECK:STDOUT: %.545: type = fn_type_with_self_type %ImplicitAs.WithSelf.Convert.type.b37, %ImplicitAs.facet [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.bound.215: = bound_method %int_1.5b8, %Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5 [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn: = specific_function %Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5, @Core.IntLiteral.as.ImplicitAs.impl.Convert(%int_32) [concrete] // CHECK:STDOUT: %bound_method.38b: = bound_method %int_1.5b8, %Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn [concrete] // CHECK:STDOUT: %int_1.5d2: %i32 = int_value 1 [concrete] // CHECK:STDOUT: %tuple.246: %tuple.type.a1c = tuple_value (%int_1.5d2) [concrete] // CHECK:STDOUT: %int_0: Core.IntLiteral = int_value 0 [concrete] // CHECK:STDOUT: %int_6.462: Core.IntLiteral = int_value 6 [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.bound.502: = bound_method %int_6.462, %Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5 [concrete] // CHECK:STDOUT: %bound_method.bc2: = bound_method %int_6.462, %Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn [concrete] // CHECK:STDOUT: %int_6.e56: %i32 = int_value 6 [concrete] // CHECK:STDOUT: %Destroy.type: type = facet_type <@Destroy> [concrete] // CHECK:STDOUT: %Destroy.Op.type: type = fn_type @Destroy.Op [concrete] // CHECK:STDOUT: %Destroy.Op: %Destroy.Op.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: .Int = %Core.Int // CHECK:STDOUT: .ImplicitAs = %Core.ImplicitAs // CHECK:STDOUT: .Destroy = %Core.Destroy // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: %Core.Int: %Int.type = import_ref Core//prelude/parts/int, Int, loaded [concrete = constants.%Int.generic] // CHECK:STDOUT: %Core.ImplicitAs: %ImplicitAs.type.cc7 = import_ref Core//prelude/parts/as, ImplicitAs, loaded [concrete = constants.%ImplicitAs.generic] // CHECK:STDOUT: %Core.import_ref.42d: @Core.IntLiteral.as.ImplicitAs.impl.%Core.IntLiteral.as.ImplicitAs.impl.Convert.type (%Core.IntLiteral.as.ImplicitAs.impl.Convert.type.4e6) = import_ref Core//prelude/parts/int, loc{{\d+_\d+}}, loaded [symbolic = @Core.IntLiteral.as.ImplicitAs.impl.%Core.IntLiteral.as.ImplicitAs.impl.Convert (constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.3c2)] // CHECK:STDOUT: %ImplicitAs.impl_witness_table.74f = impl_witness_table (%Core.import_ref.42d), @Core.IntLiteral.as.ImplicitAs.impl [concrete] // CHECK:STDOUT: %Core.Destroy: type = import_ref Core//prelude/parts/destroy, Destroy, loaded [concrete = constants.%Destroy.type] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .Foo = %Foo.decl // CHECK:STDOUT: .Main = %Main.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %Foo.decl: %Foo.type = fn_decl @Foo [concrete = constants.%Foo] { // CHECK:STDOUT: %a.patt: %pattern_type.7ce = value_binding_pattern a [concrete] // CHECK:STDOUT: %a.param_patt: %pattern_type.7ce = value_param_pattern %a.patt [concrete] // CHECK:STDOUT: %b.patt: %pattern_type.7ce = value_binding_pattern b [concrete] // CHECK:STDOUT: %b.param_patt: %pattern_type.7ce = value_param_pattern %b.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %a.param: %i32 = value_param call_param0 // CHECK:STDOUT: %i32.loc15_18: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %a: %i32 = value_binding a, %a.param // CHECK:STDOUT: %b.param: %i32 = value_param call_param1 // CHECK:STDOUT: %i32.loc15_33: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %b: %i32 = value_binding b, %b.param // CHECK:STDOUT: } // CHECK:STDOUT: %Main.decl: %Main.type = fn_decl @Main [concrete = constants.%Main] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @Foo(%a.param: %i32, %b.param: %i32) { // CHECK:STDOUT: !entry: // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @Main() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %x.patt: %pattern_type.b74 = ref_binding_pattern x [concrete] // CHECK:STDOUT: %x.var_patt: %pattern_type.b74 = var_pattern %x.patt [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %x.var: ref %tuple.type.a1c = var %x.var_patt // CHECK:STDOUT: %int_1: Core.IntLiteral = int_value 1 [concrete = constants.%int_1.5b8] // CHECK:STDOUT: %.loc18_22.1: %tuple.type.985 = tuple_literal (%int_1) [concrete = constants.%tuple.378] // CHECK:STDOUT: %impl.elem0.loc18: %.545 = impl_witness_access constants.%ImplicitAs.impl_witness.6bc, element0 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5] // CHECK:STDOUT: %bound_method.loc18_22.1: = bound_method %int_1, %impl.elem0.loc18 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.bound.215] // CHECK:STDOUT: %specific_fn.loc18: = specific_function %impl.elem0.loc18, @Core.IntLiteral.as.ImplicitAs.impl.Convert(constants.%int_32) [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn] // CHECK:STDOUT: %bound_method.loc18_22.2: = bound_method %int_1, %specific_fn.loc18 [concrete = constants.%bound_method.38b] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc18: init %i32 = call %bound_method.loc18_22.2(%int_1) [concrete = constants.%int_1.5d2] // CHECK:STDOUT: %.loc18_22.2: init %i32 = converted %int_1, %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc18 [concrete = constants.%int_1.5d2] // CHECK:STDOUT: %.loc18_22.3: init %tuple.type.a1c to %x.var = tuple_init (%.loc18_22.2) [concrete = constants.%tuple.246] // CHECK:STDOUT: %.loc18_3: init %tuple.type.a1c = converted %.loc18_22.1, %.loc18_22.3 [concrete = constants.%tuple.246] // CHECK:STDOUT: assign %x.var, %.loc18_3 // CHECK:STDOUT: %.loc18_15.1: type = splice_block %.loc18_15.3 [concrete = constants.%tuple.type.a1c] { // CHECK:STDOUT: %i32: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %.loc18_15.2: %tuple.type.85c = tuple_literal (%i32) [concrete = constants.%tuple.896] // CHECK:STDOUT: %.loc18_15.3: type = converted %.loc18_15.2, constants.%tuple.type.a1c [concrete = constants.%tuple.type.a1c] // CHECK:STDOUT: } // CHECK:STDOUT: %x: ref %tuple.type.a1c = ref_binding x, %x.var // CHECK:STDOUT: %Foo.ref: %Foo.type = name_ref Foo, file.%Foo.decl [concrete = constants.%Foo] // CHECK:STDOUT: %x.ref: ref %tuple.type.a1c = name_ref x, %x // CHECK:STDOUT: %int_0: Core.IntLiteral = int_value 0 [concrete = constants.%int_0] // CHECK:STDOUT: %tuple.elem0: ref %i32 = tuple_access %x.ref, element0 // CHECK:STDOUT: %int_6: Core.IntLiteral = int_value 6 [concrete = constants.%int_6.462] // CHECK:STDOUT: %.loc20_8: %i32 = acquire_value %tuple.elem0 // CHECK:STDOUT: %impl.elem0.loc20: %.545 = impl_witness_access constants.%ImplicitAs.impl_witness.6bc, element0 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5] // CHECK:STDOUT: %bound_method.loc20_12.1: = bound_method %int_6, %impl.elem0.loc20 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.bound.502] // CHECK:STDOUT: %specific_fn.loc20: = specific_function %impl.elem0.loc20, @Core.IntLiteral.as.ImplicitAs.impl.Convert(constants.%int_32) [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn] // CHECK:STDOUT: %bound_method.loc20_12.2: = bound_method %int_6, %specific_fn.loc20 [concrete = constants.%bound_method.bc2] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc20: init %i32 = call %bound_method.loc20_12.2(%int_6) [concrete = constants.%int_6.e56] // CHECK:STDOUT: %.loc20_12.1: %i32 = value_of_initializer %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc20 [concrete = constants.%int_6.e56] // CHECK:STDOUT: %.loc20_12.2: %i32 = converted %int_6, %.loc20_12.1 [concrete = constants.%int_6.e56] // CHECK:STDOUT: %Foo.call: init %empty_tuple.type = call %Foo.ref(%.loc20_8, %.loc20_12.2) // CHECK:STDOUT: %Destroy.Op.bound: = bound_method %x.var, constants.%Destroy.Op // CHECK:STDOUT: %Destroy.Op.call: init %empty_tuple.type = call %Destroy.Op.bound(%x.var) // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @Destroy.Op(%self.param: ref %tuple.type.a1c) = "no_op"; // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/function/call/params_one.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/int.carbon // TODO: Add ranges and switch to "--dump-sem-ir-ranges=only". // EXTRA-ARGS: --dump-sem-ir-ranges=if-present // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/function/call/params_one.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/function/call/params_one.carbon fn Foo(unused a: i32) {} fn Main() { Foo(1); } // CHECK:STDOUT: --- params_one.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [concrete] // CHECK:STDOUT: %Int.type: type = generic_class_type @Int [concrete] // CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] // CHECK:STDOUT: %Int.generic: %Int.type = struct_value () [concrete] // CHECK:STDOUT: %i32: type = class_type @Int, @Int(%int_32) [concrete] // CHECK:STDOUT: %pattern_type.7ce: type = pattern_type %i32 [concrete] // CHECK:STDOUT: %Foo.type: type = fn_type @Foo [concrete] // CHECK:STDOUT: %Foo: %Foo.type = struct_value () [concrete] // CHECK:STDOUT: %Main.type: type = fn_type @Main [concrete] // CHECK:STDOUT: %Main: %Main.type = struct_value () [concrete] // CHECK:STDOUT: %int_1.5b8: Core.IntLiteral = int_value 1 [concrete] // CHECK:STDOUT: %ImplicitAs.type.cc7: type = generic_interface_type @ImplicitAs [concrete] // CHECK:STDOUT: %ImplicitAs.generic: %ImplicitAs.type.cc7 = struct_value () [concrete] // CHECK:STDOUT: %ImplicitAs.type.e8c: type = facet_type <@ImplicitAs, @ImplicitAs(%i32)> [concrete] // CHECK:STDOUT: %To: Core.IntLiteral = symbolic_binding To, 0 [symbolic] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.4e6: type = fn_type @Core.IntLiteral.as.ImplicitAs.impl.Convert, @Core.IntLiteral.as.ImplicitAs.impl(%To) [symbolic] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.3c2: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.4e6 = struct_value () [symbolic] // CHECK:STDOUT: %ImplicitAs.impl_witness.6bc: = impl_witness imports.%ImplicitAs.impl_witness_table.74f, @Core.IntLiteral.as.ImplicitAs.impl(%int_32) [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.e0d: type = fn_type @Core.IntLiteral.as.ImplicitAs.impl.Convert, @Core.IntLiteral.as.ImplicitAs.impl(%int_32) [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.e0d = struct_value () [concrete] // CHECK:STDOUT: %ImplicitAs.facet: %ImplicitAs.type.e8c = facet_value Core.IntLiteral, (%ImplicitAs.impl_witness.6bc) [concrete] // CHECK:STDOUT: %ImplicitAs.WithSelf.Convert.type.b37: type = fn_type @ImplicitAs.WithSelf.Convert, @ImplicitAs.WithSelf(%i32, %ImplicitAs.facet) [concrete] // CHECK:STDOUT: %.545: type = fn_type_with_self_type %ImplicitAs.WithSelf.Convert.type.b37, %ImplicitAs.facet [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.bound: = bound_method %int_1.5b8, %Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5 [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn: = specific_function %Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5, @Core.IntLiteral.as.ImplicitAs.impl.Convert(%int_32) [concrete] // CHECK:STDOUT: %bound_method: = bound_method %int_1.5b8, %Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn [concrete] // CHECK:STDOUT: %int_1.5d2: %i32 = int_value 1 [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: .Int = %Core.Int // CHECK:STDOUT: .ImplicitAs = %Core.ImplicitAs // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: %Core.Int: %Int.type = import_ref Core//prelude/parts/int, Int, loaded [concrete = constants.%Int.generic] // CHECK:STDOUT: %Core.ImplicitAs: %ImplicitAs.type.cc7 = import_ref Core//prelude/parts/as, ImplicitAs, loaded [concrete = constants.%ImplicitAs.generic] // CHECK:STDOUT: %Core.import_ref.42d: @Core.IntLiteral.as.ImplicitAs.impl.%Core.IntLiteral.as.ImplicitAs.impl.Convert.type (%Core.IntLiteral.as.ImplicitAs.impl.Convert.type.4e6) = import_ref Core//prelude/parts/int, loc{{\d+_\d+}}, loaded [symbolic = @Core.IntLiteral.as.ImplicitAs.impl.%Core.IntLiteral.as.ImplicitAs.impl.Convert (constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.3c2)] // CHECK:STDOUT: %ImplicitAs.impl_witness_table.74f = impl_witness_table (%Core.import_ref.42d), @Core.IntLiteral.as.ImplicitAs.impl [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .Foo = %Foo.decl // CHECK:STDOUT: .Main = %Main.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %Foo.decl: %Foo.type = fn_decl @Foo [concrete = constants.%Foo] { // CHECK:STDOUT: %a.patt: %pattern_type.7ce = value_binding_pattern a [concrete] // CHECK:STDOUT: %a.param_patt: %pattern_type.7ce = value_param_pattern %a.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %a.param: %i32 = value_param call_param0 // CHECK:STDOUT: %i32: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %a: %i32 = value_binding a, %a.param // CHECK:STDOUT: } // CHECK:STDOUT: %Main.decl: %Main.type = fn_decl @Main [concrete = constants.%Main] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @Foo(%a.param: %i32) { // CHECK:STDOUT: !entry: // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @Main() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %Foo.ref: %Foo.type = name_ref Foo, file.%Foo.decl [concrete = constants.%Foo] // CHECK:STDOUT: %int_1: Core.IntLiteral = int_value 1 [concrete = constants.%int_1.5b8] // CHECK:STDOUT: %impl.elem0: %.545 = impl_witness_access constants.%ImplicitAs.impl_witness.6bc, element0 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5] // CHECK:STDOUT: %bound_method.loc18_7.1: = bound_method %int_1, %impl.elem0 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.bound] // CHECK:STDOUT: %specific_fn: = specific_function %impl.elem0, @Core.IntLiteral.as.ImplicitAs.impl.Convert(constants.%int_32) [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn] // CHECK:STDOUT: %bound_method.loc18_7.2: = bound_method %int_1, %specific_fn [concrete = constants.%bound_method] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.call: init %i32 = call %bound_method.loc18_7.2(%int_1) [concrete = constants.%int_1.5d2] // CHECK:STDOUT: %.loc18_7.1: %i32 = value_of_initializer %Core.IntLiteral.as.ImplicitAs.impl.Convert.call [concrete = constants.%int_1.5d2] // CHECK:STDOUT: %.loc18_7.2: %i32 = converted %int_1, %.loc18_7.1 [concrete = constants.%int_1.5d2] // CHECK:STDOUT: %Foo.call: init %empty_tuple.type = call %Foo.ref(%.loc18_7.2) // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/function/call/params_one_comma.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/int.carbon // TODO: Add ranges and switch to "--dump-sem-ir-ranges=only". // EXTRA-ARGS: --dump-sem-ir-ranges=if-present // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/function/call/params_one_comma.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/function/call/params_one_comma.carbon fn Foo(unused a: i32,) {} fn Main() { Foo(1); Foo(1,); } // CHECK:STDOUT: --- params_one_comma.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [concrete] // CHECK:STDOUT: %Int.type: type = generic_class_type @Int [concrete] // CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] // CHECK:STDOUT: %Int.generic: %Int.type = struct_value () [concrete] // CHECK:STDOUT: %i32: type = class_type @Int, @Int(%int_32) [concrete] // CHECK:STDOUT: %pattern_type.7ce: type = pattern_type %i32 [concrete] // CHECK:STDOUT: %Foo.type: type = fn_type @Foo [concrete] // CHECK:STDOUT: %Foo: %Foo.type = struct_value () [concrete] // CHECK:STDOUT: %Main.type: type = fn_type @Main [concrete] // CHECK:STDOUT: %Main: %Main.type = struct_value () [concrete] // CHECK:STDOUT: %int_1.5b8: Core.IntLiteral = int_value 1 [concrete] // CHECK:STDOUT: %ImplicitAs.type.cc7: type = generic_interface_type @ImplicitAs [concrete] // CHECK:STDOUT: %ImplicitAs.generic: %ImplicitAs.type.cc7 = struct_value () [concrete] // CHECK:STDOUT: %ImplicitAs.type.e8c: type = facet_type <@ImplicitAs, @ImplicitAs(%i32)> [concrete] // CHECK:STDOUT: %To: Core.IntLiteral = symbolic_binding To, 0 [symbolic] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.4e6: type = fn_type @Core.IntLiteral.as.ImplicitAs.impl.Convert, @Core.IntLiteral.as.ImplicitAs.impl(%To) [symbolic] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.3c2: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.4e6 = struct_value () [symbolic] // CHECK:STDOUT: %ImplicitAs.impl_witness.6bc: = impl_witness imports.%ImplicitAs.impl_witness_table.74f, @Core.IntLiteral.as.ImplicitAs.impl(%int_32) [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.e0d: type = fn_type @Core.IntLiteral.as.ImplicitAs.impl.Convert, @Core.IntLiteral.as.ImplicitAs.impl(%int_32) [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.e0d = struct_value () [concrete] // CHECK:STDOUT: %ImplicitAs.facet: %ImplicitAs.type.e8c = facet_value Core.IntLiteral, (%ImplicitAs.impl_witness.6bc) [concrete] // CHECK:STDOUT: %ImplicitAs.WithSelf.Convert.type.b37: type = fn_type @ImplicitAs.WithSelf.Convert, @ImplicitAs.WithSelf(%i32, %ImplicitAs.facet) [concrete] // CHECK:STDOUT: %.545: type = fn_type_with_self_type %ImplicitAs.WithSelf.Convert.type.b37, %ImplicitAs.facet [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.bound: = bound_method %int_1.5b8, %Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5 [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn: = specific_function %Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5, @Core.IntLiteral.as.ImplicitAs.impl.Convert(%int_32) [concrete] // CHECK:STDOUT: %bound_method: = bound_method %int_1.5b8, %Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn [concrete] // CHECK:STDOUT: %int_1.5d2: %i32 = int_value 1 [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: .Int = %Core.Int // CHECK:STDOUT: .ImplicitAs = %Core.ImplicitAs // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: %Core.Int: %Int.type = import_ref Core//prelude/parts/int, Int, loaded [concrete = constants.%Int.generic] // CHECK:STDOUT: %Core.ImplicitAs: %ImplicitAs.type.cc7 = import_ref Core//prelude/parts/as, ImplicitAs, loaded [concrete = constants.%ImplicitAs.generic] // CHECK:STDOUT: %Core.import_ref.42d: @Core.IntLiteral.as.ImplicitAs.impl.%Core.IntLiteral.as.ImplicitAs.impl.Convert.type (%Core.IntLiteral.as.ImplicitAs.impl.Convert.type.4e6) = import_ref Core//prelude/parts/int, loc{{\d+_\d+}}, loaded [symbolic = @Core.IntLiteral.as.ImplicitAs.impl.%Core.IntLiteral.as.ImplicitAs.impl.Convert (constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.3c2)] // CHECK:STDOUT: %ImplicitAs.impl_witness_table.74f = impl_witness_table (%Core.import_ref.42d), @Core.IntLiteral.as.ImplicitAs.impl [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .Foo = %Foo.decl // CHECK:STDOUT: .Main = %Main.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %Foo.decl: %Foo.type = fn_decl @Foo [concrete = constants.%Foo] { // CHECK:STDOUT: %a.patt: %pattern_type.7ce = value_binding_pattern a [concrete] // CHECK:STDOUT: %a.param_patt: %pattern_type.7ce = value_param_pattern %a.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %a.param: %i32 = value_param call_param0 // CHECK:STDOUT: %i32: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %a: %i32 = value_binding a, %a.param // CHECK:STDOUT: } // CHECK:STDOUT: %Main.decl: %Main.type = fn_decl @Main [concrete = constants.%Main] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @Foo(%a.param: %i32) { // CHECK:STDOUT: !entry: // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @Main() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %Foo.ref.loc18: %Foo.type = name_ref Foo, file.%Foo.decl [concrete = constants.%Foo] // CHECK:STDOUT: %int_1.loc18: Core.IntLiteral = int_value 1 [concrete = constants.%int_1.5b8] // CHECK:STDOUT: %impl.elem0.loc18: %.545 = impl_witness_access constants.%ImplicitAs.impl_witness.6bc, element0 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5] // CHECK:STDOUT: %bound_method.loc18_7.1: = bound_method %int_1.loc18, %impl.elem0.loc18 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.bound] // CHECK:STDOUT: %specific_fn.loc18: = specific_function %impl.elem0.loc18, @Core.IntLiteral.as.ImplicitAs.impl.Convert(constants.%int_32) [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn] // CHECK:STDOUT: %bound_method.loc18_7.2: = bound_method %int_1.loc18, %specific_fn.loc18 [concrete = constants.%bound_method] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc18: init %i32 = call %bound_method.loc18_7.2(%int_1.loc18) [concrete = constants.%int_1.5d2] // CHECK:STDOUT: %.loc18_7.1: %i32 = value_of_initializer %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc18 [concrete = constants.%int_1.5d2] // CHECK:STDOUT: %.loc18_7.2: %i32 = converted %int_1.loc18, %.loc18_7.1 [concrete = constants.%int_1.5d2] // CHECK:STDOUT: %Foo.call.loc18: init %empty_tuple.type = call %Foo.ref.loc18(%.loc18_7.2) // CHECK:STDOUT: %Foo.ref.loc19: %Foo.type = name_ref Foo, file.%Foo.decl [concrete = constants.%Foo] // CHECK:STDOUT: %int_1.loc19: Core.IntLiteral = int_value 1 [concrete = constants.%int_1.5b8] // CHECK:STDOUT: %impl.elem0.loc19: %.545 = impl_witness_access constants.%ImplicitAs.impl_witness.6bc, element0 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5] // CHECK:STDOUT: %bound_method.loc19_7.1: = bound_method %int_1.loc19, %impl.elem0.loc19 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.bound] // CHECK:STDOUT: %specific_fn.loc19: = specific_function %impl.elem0.loc19, @Core.IntLiteral.as.ImplicitAs.impl.Convert(constants.%int_32) [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn] // CHECK:STDOUT: %bound_method.loc19_7.2: = bound_method %int_1.loc19, %specific_fn.loc19 [concrete = constants.%bound_method] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc19: init %i32 = call %bound_method.loc19_7.2(%int_1.loc19) [concrete = constants.%int_1.5d2] // CHECK:STDOUT: %.loc19_7.1: %i32 = value_of_initializer %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc19 [concrete = constants.%int_1.5d2] // CHECK:STDOUT: %.loc19_7.2: %i32 = converted %int_1.loc19, %.loc19_7.1 [concrete = constants.%int_1.5d2] // CHECK:STDOUT: %Foo.call.loc19: init %empty_tuple.type = call %Foo.ref.loc19(%.loc19_7.2) // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/function/call/params_two.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/int.carbon // TODO: Add ranges and switch to "--dump-sem-ir-ranges=only". // EXTRA-ARGS: --dump-sem-ir-ranges=if-present // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/function/call/params_two.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/function/call/params_two.carbon fn Foo(unused a: i32, unused b: i32) {} fn Main() { Foo(1, 2); } // CHECK:STDOUT: --- params_two.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [concrete] // CHECK:STDOUT: %Int.type: type = generic_class_type @Int [concrete] // CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] // CHECK:STDOUT: %Int.generic: %Int.type = struct_value () [concrete] // CHECK:STDOUT: %i32: type = class_type @Int, @Int(%int_32) [concrete] // CHECK:STDOUT: %pattern_type.7ce: type = pattern_type %i32 [concrete] // CHECK:STDOUT: %Foo.type: type = fn_type @Foo [concrete] // CHECK:STDOUT: %Foo: %Foo.type = struct_value () [concrete] // CHECK:STDOUT: %Main.type: type = fn_type @Main [concrete] // CHECK:STDOUT: %Main: %Main.type = struct_value () [concrete] // CHECK:STDOUT: %int_1.5b8: Core.IntLiteral = int_value 1 [concrete] // CHECK:STDOUT: %int_2.ecc: Core.IntLiteral = int_value 2 [concrete] // CHECK:STDOUT: %ImplicitAs.type.cc7: type = generic_interface_type @ImplicitAs [concrete] // CHECK:STDOUT: %ImplicitAs.generic: %ImplicitAs.type.cc7 = struct_value () [concrete] // CHECK:STDOUT: %ImplicitAs.type.e8c: type = facet_type <@ImplicitAs, @ImplicitAs(%i32)> [concrete] // CHECK:STDOUT: %To: Core.IntLiteral = symbolic_binding To, 0 [symbolic] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.4e6: type = fn_type @Core.IntLiteral.as.ImplicitAs.impl.Convert, @Core.IntLiteral.as.ImplicitAs.impl(%To) [symbolic] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.3c2: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.4e6 = struct_value () [symbolic] // CHECK:STDOUT: %ImplicitAs.impl_witness.6bc: = impl_witness imports.%ImplicitAs.impl_witness_table.74f, @Core.IntLiteral.as.ImplicitAs.impl(%int_32) [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.e0d: type = fn_type @Core.IntLiteral.as.ImplicitAs.impl.Convert, @Core.IntLiteral.as.ImplicitAs.impl(%int_32) [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.e0d = struct_value () [concrete] // CHECK:STDOUT: %ImplicitAs.facet: %ImplicitAs.type.e8c = facet_value Core.IntLiteral, (%ImplicitAs.impl_witness.6bc) [concrete] // CHECK:STDOUT: %ImplicitAs.WithSelf.Convert.type.b37: type = fn_type @ImplicitAs.WithSelf.Convert, @ImplicitAs.WithSelf(%i32, %ImplicitAs.facet) [concrete] // CHECK:STDOUT: %.545: type = fn_type_with_self_type %ImplicitAs.WithSelf.Convert.type.b37, %ImplicitAs.facet [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.bound.215: = bound_method %int_1.5b8, %Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5 [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn: = specific_function %Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5, @Core.IntLiteral.as.ImplicitAs.impl.Convert(%int_32) [concrete] // CHECK:STDOUT: %bound_method.38b: = bound_method %int_1.5b8, %Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn [concrete] // CHECK:STDOUT: %int_1.5d2: %i32 = int_value 1 [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.bound.4e5: = bound_method %int_2.ecc, %Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5 [concrete] // CHECK:STDOUT: %bound_method.646: = bound_method %int_2.ecc, %Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn [concrete] // CHECK:STDOUT: %int_2.ef8: %i32 = int_value 2 [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: .Int = %Core.Int // CHECK:STDOUT: .ImplicitAs = %Core.ImplicitAs // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: %Core.Int: %Int.type = import_ref Core//prelude/parts/int, Int, loaded [concrete = constants.%Int.generic] // CHECK:STDOUT: %Core.ImplicitAs: %ImplicitAs.type.cc7 = import_ref Core//prelude/parts/as, ImplicitAs, loaded [concrete = constants.%ImplicitAs.generic] // CHECK:STDOUT: %Core.import_ref.42d: @Core.IntLiteral.as.ImplicitAs.impl.%Core.IntLiteral.as.ImplicitAs.impl.Convert.type (%Core.IntLiteral.as.ImplicitAs.impl.Convert.type.4e6) = import_ref Core//prelude/parts/int, loc{{\d+_\d+}}, loaded [symbolic = @Core.IntLiteral.as.ImplicitAs.impl.%Core.IntLiteral.as.ImplicitAs.impl.Convert (constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.3c2)] // CHECK:STDOUT: %ImplicitAs.impl_witness_table.74f = impl_witness_table (%Core.import_ref.42d), @Core.IntLiteral.as.ImplicitAs.impl [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .Foo = %Foo.decl // CHECK:STDOUT: .Main = %Main.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %Foo.decl: %Foo.type = fn_decl @Foo [concrete = constants.%Foo] { // CHECK:STDOUT: %a.patt: %pattern_type.7ce = value_binding_pattern a [concrete] // CHECK:STDOUT: %a.param_patt: %pattern_type.7ce = value_param_pattern %a.patt [concrete] // CHECK:STDOUT: %b.patt: %pattern_type.7ce = value_binding_pattern b [concrete] // CHECK:STDOUT: %b.param_patt: %pattern_type.7ce = value_param_pattern %b.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %a.param: %i32 = value_param call_param0 // CHECK:STDOUT: %i32.loc15_18: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %a: %i32 = value_binding a, %a.param // CHECK:STDOUT: %b.param: %i32 = value_param call_param1 // CHECK:STDOUT: %i32.loc15_33: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %b: %i32 = value_binding b, %b.param // CHECK:STDOUT: } // CHECK:STDOUT: %Main.decl: %Main.type = fn_decl @Main [concrete = constants.%Main] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @Foo(%a.param: %i32, %b.param: %i32) { // CHECK:STDOUT: !entry: // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @Main() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %Foo.ref: %Foo.type = name_ref Foo, file.%Foo.decl [concrete = constants.%Foo] // CHECK:STDOUT: %int_1: Core.IntLiteral = int_value 1 [concrete = constants.%int_1.5b8] // CHECK:STDOUT: %int_2: Core.IntLiteral = int_value 2 [concrete = constants.%int_2.ecc] // CHECK:STDOUT: %impl.elem0.loc18_7: %.545 = impl_witness_access constants.%ImplicitAs.impl_witness.6bc, element0 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5] // CHECK:STDOUT: %bound_method.loc18_7.1: = bound_method %int_1, %impl.elem0.loc18_7 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.bound.215] // CHECK:STDOUT: %specific_fn.loc18_7: = specific_function %impl.elem0.loc18_7, @Core.IntLiteral.as.ImplicitAs.impl.Convert(constants.%int_32) [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn] // CHECK:STDOUT: %bound_method.loc18_7.2: = bound_method %int_1, %specific_fn.loc18_7 [concrete = constants.%bound_method.38b] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc18_7: init %i32 = call %bound_method.loc18_7.2(%int_1) [concrete = constants.%int_1.5d2] // CHECK:STDOUT: %.loc18_7.1: %i32 = value_of_initializer %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc18_7 [concrete = constants.%int_1.5d2] // CHECK:STDOUT: %.loc18_7.2: %i32 = converted %int_1, %.loc18_7.1 [concrete = constants.%int_1.5d2] // CHECK:STDOUT: %impl.elem0.loc18_10: %.545 = impl_witness_access constants.%ImplicitAs.impl_witness.6bc, element0 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5] // CHECK:STDOUT: %bound_method.loc18_10.1: = bound_method %int_2, %impl.elem0.loc18_10 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.bound.4e5] // CHECK:STDOUT: %specific_fn.loc18_10: = specific_function %impl.elem0.loc18_10, @Core.IntLiteral.as.ImplicitAs.impl.Convert(constants.%int_32) [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn] // CHECK:STDOUT: %bound_method.loc18_10.2: = bound_method %int_2, %specific_fn.loc18_10 [concrete = constants.%bound_method.646] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc18_10: init %i32 = call %bound_method.loc18_10.2(%int_2) [concrete = constants.%int_2.ef8] // CHECK:STDOUT: %.loc18_10.1: %i32 = value_of_initializer %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc18_10 [concrete = constants.%int_2.ef8] // CHECK:STDOUT: %.loc18_10.2: %i32 = converted %int_2, %.loc18_10.1 [concrete = constants.%int_2.ef8] // CHECK:STDOUT: %Foo.call: init %empty_tuple.type = call %Foo.ref(%.loc18_7.2, %.loc18_10.2) // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/function/call/params_two_comma.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/int.carbon // TODO: Add ranges and switch to "--dump-sem-ir-ranges=only". // EXTRA-ARGS: --dump-sem-ir-ranges=if-present // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/function/call/params_two_comma.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/function/call/params_two_comma.carbon fn Foo(unused a: i32, unused b: i32,) {} fn Main() { Foo(1, 2); Foo(1, 2,); } // CHECK:STDOUT: --- params_two_comma.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [concrete] // CHECK:STDOUT: %Int.type: type = generic_class_type @Int [concrete] // CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] // CHECK:STDOUT: %Int.generic: %Int.type = struct_value () [concrete] // CHECK:STDOUT: %i32: type = class_type @Int, @Int(%int_32) [concrete] // CHECK:STDOUT: %pattern_type.7ce: type = pattern_type %i32 [concrete] // CHECK:STDOUT: %Foo.type: type = fn_type @Foo [concrete] // CHECK:STDOUT: %Foo: %Foo.type = struct_value () [concrete] // CHECK:STDOUT: %Main.type: type = fn_type @Main [concrete] // CHECK:STDOUT: %Main: %Main.type = struct_value () [concrete] // CHECK:STDOUT: %int_1.5b8: Core.IntLiteral = int_value 1 [concrete] // CHECK:STDOUT: %int_2.ecc: Core.IntLiteral = int_value 2 [concrete] // CHECK:STDOUT: %ImplicitAs.type.cc7: type = generic_interface_type @ImplicitAs [concrete] // CHECK:STDOUT: %ImplicitAs.generic: %ImplicitAs.type.cc7 = struct_value () [concrete] // CHECK:STDOUT: %ImplicitAs.type.e8c: type = facet_type <@ImplicitAs, @ImplicitAs(%i32)> [concrete] // CHECK:STDOUT: %To: Core.IntLiteral = symbolic_binding To, 0 [symbolic] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.4e6: type = fn_type @Core.IntLiteral.as.ImplicitAs.impl.Convert, @Core.IntLiteral.as.ImplicitAs.impl(%To) [symbolic] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.3c2: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.4e6 = struct_value () [symbolic] // CHECK:STDOUT: %ImplicitAs.impl_witness.6bc: = impl_witness imports.%ImplicitAs.impl_witness_table.74f, @Core.IntLiteral.as.ImplicitAs.impl(%int_32) [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.e0d: type = fn_type @Core.IntLiteral.as.ImplicitAs.impl.Convert, @Core.IntLiteral.as.ImplicitAs.impl(%int_32) [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.e0d = struct_value () [concrete] // CHECK:STDOUT: %ImplicitAs.facet: %ImplicitAs.type.e8c = facet_value Core.IntLiteral, (%ImplicitAs.impl_witness.6bc) [concrete] // CHECK:STDOUT: %ImplicitAs.WithSelf.Convert.type.b37: type = fn_type @ImplicitAs.WithSelf.Convert, @ImplicitAs.WithSelf(%i32, %ImplicitAs.facet) [concrete] // CHECK:STDOUT: %.545: type = fn_type_with_self_type %ImplicitAs.WithSelf.Convert.type.b37, %ImplicitAs.facet [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.bound.215: = bound_method %int_1.5b8, %Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5 [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn: = specific_function %Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5, @Core.IntLiteral.as.ImplicitAs.impl.Convert(%int_32) [concrete] // CHECK:STDOUT: %bound_method.38b: = bound_method %int_1.5b8, %Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn [concrete] // CHECK:STDOUT: %int_1.5d2: %i32 = int_value 1 [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.bound.4e5: = bound_method %int_2.ecc, %Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5 [concrete] // CHECK:STDOUT: %bound_method.646: = bound_method %int_2.ecc, %Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn [concrete] // CHECK:STDOUT: %int_2.ef8: %i32 = int_value 2 [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: .Int = %Core.Int // CHECK:STDOUT: .ImplicitAs = %Core.ImplicitAs // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: %Core.Int: %Int.type = import_ref Core//prelude/parts/int, Int, loaded [concrete = constants.%Int.generic] // CHECK:STDOUT: %Core.ImplicitAs: %ImplicitAs.type.cc7 = import_ref Core//prelude/parts/as, ImplicitAs, loaded [concrete = constants.%ImplicitAs.generic] // CHECK:STDOUT: %Core.import_ref.42d: @Core.IntLiteral.as.ImplicitAs.impl.%Core.IntLiteral.as.ImplicitAs.impl.Convert.type (%Core.IntLiteral.as.ImplicitAs.impl.Convert.type.4e6) = import_ref Core//prelude/parts/int, loc{{\d+_\d+}}, loaded [symbolic = @Core.IntLiteral.as.ImplicitAs.impl.%Core.IntLiteral.as.ImplicitAs.impl.Convert (constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.3c2)] // CHECK:STDOUT: %ImplicitAs.impl_witness_table.74f = impl_witness_table (%Core.import_ref.42d), @Core.IntLiteral.as.ImplicitAs.impl [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .Foo = %Foo.decl // CHECK:STDOUT: .Main = %Main.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %Foo.decl: %Foo.type = fn_decl @Foo [concrete = constants.%Foo] { // CHECK:STDOUT: %a.patt: %pattern_type.7ce = value_binding_pattern a [concrete] // CHECK:STDOUT: %a.param_patt: %pattern_type.7ce = value_param_pattern %a.patt [concrete] // CHECK:STDOUT: %b.patt: %pattern_type.7ce = value_binding_pattern b [concrete] // CHECK:STDOUT: %b.param_patt: %pattern_type.7ce = value_param_pattern %b.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %a.param: %i32 = value_param call_param0 // CHECK:STDOUT: %i32.loc15_18: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %a: %i32 = value_binding a, %a.param // CHECK:STDOUT: %b.param: %i32 = value_param call_param1 // CHECK:STDOUT: %i32.loc15_33: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %b: %i32 = value_binding b, %b.param // CHECK:STDOUT: } // CHECK:STDOUT: %Main.decl: %Main.type = fn_decl @Main [concrete = constants.%Main] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @Foo(%a.param: %i32, %b.param: %i32) { // CHECK:STDOUT: !entry: // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @Main() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %Foo.ref.loc18: %Foo.type = name_ref Foo, file.%Foo.decl [concrete = constants.%Foo] // CHECK:STDOUT: %int_1.loc18: Core.IntLiteral = int_value 1 [concrete = constants.%int_1.5b8] // CHECK:STDOUT: %int_2.loc18: Core.IntLiteral = int_value 2 [concrete = constants.%int_2.ecc] // CHECK:STDOUT: %impl.elem0.loc18_7: %.545 = impl_witness_access constants.%ImplicitAs.impl_witness.6bc, element0 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5] // CHECK:STDOUT: %bound_method.loc18_7.1: = bound_method %int_1.loc18, %impl.elem0.loc18_7 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.bound.215] // CHECK:STDOUT: %specific_fn.loc18_7: = specific_function %impl.elem0.loc18_7, @Core.IntLiteral.as.ImplicitAs.impl.Convert(constants.%int_32) [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn] // CHECK:STDOUT: %bound_method.loc18_7.2: = bound_method %int_1.loc18, %specific_fn.loc18_7 [concrete = constants.%bound_method.38b] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc18_7: init %i32 = call %bound_method.loc18_7.2(%int_1.loc18) [concrete = constants.%int_1.5d2] // CHECK:STDOUT: %.loc18_7.1: %i32 = value_of_initializer %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc18_7 [concrete = constants.%int_1.5d2] // CHECK:STDOUT: %.loc18_7.2: %i32 = converted %int_1.loc18, %.loc18_7.1 [concrete = constants.%int_1.5d2] // CHECK:STDOUT: %impl.elem0.loc18_10: %.545 = impl_witness_access constants.%ImplicitAs.impl_witness.6bc, element0 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5] // CHECK:STDOUT: %bound_method.loc18_10.1: = bound_method %int_2.loc18, %impl.elem0.loc18_10 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.bound.4e5] // CHECK:STDOUT: %specific_fn.loc18_10: = specific_function %impl.elem0.loc18_10, @Core.IntLiteral.as.ImplicitAs.impl.Convert(constants.%int_32) [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn] // CHECK:STDOUT: %bound_method.loc18_10.2: = bound_method %int_2.loc18, %specific_fn.loc18_10 [concrete = constants.%bound_method.646] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc18_10: init %i32 = call %bound_method.loc18_10.2(%int_2.loc18) [concrete = constants.%int_2.ef8] // CHECK:STDOUT: %.loc18_10.1: %i32 = value_of_initializer %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc18_10 [concrete = constants.%int_2.ef8] // CHECK:STDOUT: %.loc18_10.2: %i32 = converted %int_2.loc18, %.loc18_10.1 [concrete = constants.%int_2.ef8] // CHECK:STDOUT: %Foo.call.loc18: init %empty_tuple.type = call %Foo.ref.loc18(%.loc18_7.2, %.loc18_10.2) // CHECK:STDOUT: %Foo.ref.loc19: %Foo.type = name_ref Foo, file.%Foo.decl [concrete = constants.%Foo] // CHECK:STDOUT: %int_1.loc19: Core.IntLiteral = int_value 1 [concrete = constants.%int_1.5b8] // CHECK:STDOUT: %int_2.loc19: Core.IntLiteral = int_value 2 [concrete = constants.%int_2.ecc] // CHECK:STDOUT: %impl.elem0.loc19_7: %.545 = impl_witness_access constants.%ImplicitAs.impl_witness.6bc, element0 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5] // CHECK:STDOUT: %bound_method.loc19_7.1: = bound_method %int_1.loc19, %impl.elem0.loc19_7 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.bound.215] // CHECK:STDOUT: %specific_fn.loc19_7: = specific_function %impl.elem0.loc19_7, @Core.IntLiteral.as.ImplicitAs.impl.Convert(constants.%int_32) [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn] // CHECK:STDOUT: %bound_method.loc19_7.2: = bound_method %int_1.loc19, %specific_fn.loc19_7 [concrete = constants.%bound_method.38b] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc19_7: init %i32 = call %bound_method.loc19_7.2(%int_1.loc19) [concrete = constants.%int_1.5d2] // CHECK:STDOUT: %.loc19_7.1: %i32 = value_of_initializer %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc19_7 [concrete = constants.%int_1.5d2] // CHECK:STDOUT: %.loc19_7.2: %i32 = converted %int_1.loc19, %.loc19_7.1 [concrete = constants.%int_1.5d2] // CHECK:STDOUT: %impl.elem0.loc19_10: %.545 = impl_witness_access constants.%ImplicitAs.impl_witness.6bc, element0 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5] // CHECK:STDOUT: %bound_method.loc19_10.1: = bound_method %int_2.loc19, %impl.elem0.loc19_10 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.bound.4e5] // CHECK:STDOUT: %specific_fn.loc19_10: = specific_function %impl.elem0.loc19_10, @Core.IntLiteral.as.ImplicitAs.impl.Convert(constants.%int_32) [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn] // CHECK:STDOUT: %bound_method.loc19_10.2: = bound_method %int_2.loc19, %specific_fn.loc19_10 [concrete = constants.%bound_method.646] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc19_10: init %i32 = call %bound_method.loc19_10.2(%int_2.loc19) [concrete = constants.%int_2.ef8] // CHECK:STDOUT: %.loc19_10.1: %i32 = value_of_initializer %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc19_10 [concrete = constants.%int_2.ef8] // CHECK:STDOUT: %.loc19_10.2: %i32 = converted %int_2.loc19, %.loc19_10.1 [concrete = constants.%int_2.ef8] // CHECK:STDOUT: %Foo.call.loc19: init %empty_tuple.type = call %Foo.ref.loc19(%.loc19_7.2, %.loc19_10.2) // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/function/call/params_zero.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/none.carbon // TODO: Add ranges and switch to "--dump-sem-ir-ranges=only". // EXTRA-ARGS: --dump-sem-ir-ranges=if-present // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/function/call/params_zero.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/function/call/params_zero.carbon fn Foo() {} fn Main() { Foo(); } // CHECK:STDOUT: --- params_zero.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %Foo.type: type = fn_type @Foo [concrete] // CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] // CHECK:STDOUT: %Foo: %Foo.type = struct_value () [concrete] // CHECK:STDOUT: %Main.type: type = fn_type @Main [concrete] // CHECK:STDOUT: %Main: %Main.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Foo = %Foo.decl // CHECK:STDOUT: .Main = %Main.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Foo.decl: %Foo.type = fn_decl @Foo [concrete = constants.%Foo] {} {} // CHECK:STDOUT: %Main.decl: %Main.type = fn_decl @Main [concrete = constants.%Main] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @Foo() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @Main() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %Foo.ref: %Foo.type = name_ref Foo, file.%Foo.decl [concrete = constants.%Foo] // CHECK:STDOUT: %Foo.call: init %empty_tuple.type = call %Foo.ref() // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/function/call/prefer_unqualified_lookup.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/int.carbon // TODO: Add ranges and switch to "--dump-sem-ir-ranges=only". // EXTRA-ARGS: --dump-sem-ir-ranges=if-present // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/function/call/prefer_unqualified_lookup.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/function/call/prefer_unqualified_lookup.carbon // --- prefer_unqualified_lookup.carbon library "[[@TEST_NAME]]"; // F in the lexical scope. class Class(F:! type) { class Inner { // F in a non-lexical scope. fn F() -> i32 { return 0; } fn G() -> i32; } } fn Class(F:! type).Inner.G() -> i32 { return F(); } // CHECK:STDOUT: --- prefer_unqualified_lookup.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %type: type = facet_type [concrete] // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self] // CHECK:STDOUT: %pattern_type.98f: type = pattern_type type [concrete] // CHECK:STDOUT: %F: type = symbolic_binding F, 0 [symbolic] // CHECK:STDOUT: %Class.type: type = generic_class_type @Class [concrete] // CHECK:STDOUT: %Class.generic: %Class.type = struct_value () [concrete] // CHECK:STDOUT: %Class: type = class_type @Class, @Class(%F) [symbolic] // CHECK:STDOUT: %Inner: type = class_type @Inner, @Inner(%F) [symbolic] // CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [concrete] // CHECK:STDOUT: %Int.type: type = generic_class_type @Int [concrete] // CHECK:STDOUT: %Int.generic: %Int.type = struct_value () [concrete] // CHECK:STDOUT: %i32: type = class_type @Int, @Int(%int_32) [concrete] // CHECK:STDOUT: %.ff5: Core.Form = init_form %i32 [concrete] // CHECK:STDOUT: %pattern_type.7ce: type = pattern_type %i32 [concrete] // CHECK:STDOUT: %Inner.F.type: type = fn_type @Inner.F, @Inner(%F) [symbolic] // CHECK:STDOUT: %Inner.F: %Inner.F.type = struct_value () [symbolic] // CHECK:STDOUT: %Inner.G.type: type = fn_type @Inner.G, @Inner(%F) [symbolic] // CHECK:STDOUT: %Inner.G: %Inner.G.type = struct_value () [symbolic] // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %complete_type.357: = complete_type_witness %empty_struct_type [concrete] // CHECK:STDOUT: %int_0.5c6: Core.IntLiteral = int_value 0 [concrete] // CHECK:STDOUT: %ImplicitAs.type.cc7: type = generic_interface_type @ImplicitAs [concrete] // CHECK:STDOUT: %ImplicitAs.generic: %ImplicitAs.type.cc7 = struct_value () [concrete] // CHECK:STDOUT: %ImplicitAs.type.e8c: type = facet_type <@ImplicitAs, @ImplicitAs(%i32)> [concrete] // CHECK:STDOUT: %To: Core.IntLiteral = symbolic_binding To, 0 [symbolic] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.4e6: type = fn_type @Core.IntLiteral.as.ImplicitAs.impl.Convert, @Core.IntLiteral.as.ImplicitAs.impl(%To) [symbolic] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.3c2: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.4e6 = struct_value () [symbolic] // CHECK:STDOUT: %ImplicitAs.impl_witness.6bc: = impl_witness imports.%ImplicitAs.impl_witness_table.74f, @Core.IntLiteral.as.ImplicitAs.impl(%int_32) [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.e0d: type = fn_type @Core.IntLiteral.as.ImplicitAs.impl.Convert, @Core.IntLiteral.as.ImplicitAs.impl(%int_32) [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.e0d = struct_value () [concrete] // CHECK:STDOUT: %ImplicitAs.facet: %ImplicitAs.type.e8c = facet_value Core.IntLiteral, (%ImplicitAs.impl_witness.6bc) [concrete] // CHECK:STDOUT: %ImplicitAs.WithSelf.Convert.type.b37: type = fn_type @ImplicitAs.WithSelf.Convert, @ImplicitAs.WithSelf(%i32, %ImplicitAs.facet) [concrete] // CHECK:STDOUT: %.545: type = fn_type_with_self_type %ImplicitAs.WithSelf.Convert.type.b37, %ImplicitAs.facet [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.bound: = bound_method %int_0.5c6, %Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5 [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn: = specific_function %Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5, @Core.IntLiteral.as.ImplicitAs.impl.Convert(%int_32) [concrete] // CHECK:STDOUT: %bound_method: = bound_method %int_0.5c6, %Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn [concrete] // CHECK:STDOUT: %int_0.6a9: %i32 = int_value 0 [concrete] // CHECK:STDOUT: %Inner.F.specific_fn: = specific_function %Inner.F, @Inner.F(%F) [symbolic] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: .Int = %Core.Int // CHECK:STDOUT: .ImplicitAs = %Core.ImplicitAs // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: %Core.Int: %Int.type = import_ref Core//prelude/parts/int, Int, loaded [concrete = constants.%Int.generic] // CHECK:STDOUT: %Core.ImplicitAs: %ImplicitAs.type.cc7 = import_ref Core//prelude/parts/as, ImplicitAs, loaded [concrete = constants.%ImplicitAs.generic] // CHECK:STDOUT: %Core.import_ref.42d: @Core.IntLiteral.as.ImplicitAs.impl.%Core.IntLiteral.as.ImplicitAs.impl.Convert.type (%Core.IntLiteral.as.ImplicitAs.impl.Convert.type.4e6) = import_ref Core//prelude/parts/int, loc{{\d+_\d+}}, loaded [symbolic = @Core.IntLiteral.as.ImplicitAs.impl.%Core.IntLiteral.as.ImplicitAs.impl.Convert (constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.3c2)] // CHECK:STDOUT: %ImplicitAs.impl_witness_table.74f = impl_witness_table (%Core.import_ref.42d), @Core.IntLiteral.as.ImplicitAs.impl [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .Class = %Class.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %Class.decl: %Class.type = class_decl @Class [concrete = constants.%Class.generic] { // CHECK:STDOUT: %F.patt: %pattern_type.98f = symbolic_binding_pattern F, 0 [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %.loc5_17.1: type = splice_block %.loc5_17.2 [concrete = type] { // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %.loc5_17.2: type = type_literal type [concrete = type] // CHECK:STDOUT: } // CHECK:STDOUT: %F.loc5_13.2: type = symbolic_binding F, 0 [symbolic = %F.loc5_13.1 (constants.%F)] // CHECK:STDOUT: } // CHECK:STDOUT: %Inner.G.decl: %Inner.G.type = fn_decl @Inner.G [symbolic = constants.%Inner.G] { // CHECK:STDOUT: %return.patt: %pattern_type.7ce = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: %pattern_type.7ce = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %.loc13_14.1: type = splice_block %.loc13_14.2 [concrete = type] { // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %.loc13_14.2: type = type_literal type [concrete = type] // CHECK:STDOUT: } // CHECK:STDOUT: %F.loc13_10: type = symbolic_binding F, 0 [symbolic = @Class.%F.loc5_13.1 (constants.%F)] // CHECK:STDOUT: %i32.loc13: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %.loc13_33: Core.Form = init_form %i32.loc13 [concrete = constants.%.ff5] // CHECK:STDOUT: %return.param.loc13: ref %i32 = out_param call_param0 // CHECK:STDOUT: %return.loc13: ref %i32 = return_slot %return.param.loc13 // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic class @Class(%F.loc5_13.2: type) { // CHECK:STDOUT: %F.loc5_13.1: type = symbolic_binding F, 0 [symbolic = %F.loc5_13.1 (constants.%F)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %Inner: type = class_type @Inner, @Inner(%F.loc5_13.1) [symbolic = %Inner (constants.%Inner)] // CHECK:STDOUT: // CHECK:STDOUT: class { // CHECK:STDOUT: %Inner.decl: type = class_decl @Inner [symbolic = @Class.%Inner (constants.%Inner)] {} {} // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type.357] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%Class // CHECK:STDOUT: .Inner = %Inner.decl // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic class @Inner(@Class.%F.loc5_13.2: type) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: %F: type = symbolic_binding F, 0 [symbolic = %F (constants.%F)] // CHECK:STDOUT: %Inner.F.type: type = fn_type @Inner.F, @Inner(%F) [symbolic = %Inner.F.type (constants.%Inner.F.type)] // CHECK:STDOUT: %Inner.F: @Inner.%Inner.F.type (%Inner.F.type) = struct_value () [symbolic = %Inner.F (constants.%Inner.F)] // CHECK:STDOUT: %Inner.G.type: type = fn_type @Inner.G, @Inner(%F) [symbolic = %Inner.G.type (constants.%Inner.G.type)] // CHECK:STDOUT: %Inner.G: @Inner.%Inner.G.type (%Inner.G.type) = struct_value () [symbolic = %Inner.G (constants.%Inner.G)] // CHECK:STDOUT: // CHECK:STDOUT: class { // CHECK:STDOUT: %Inner.F.decl: @Inner.%Inner.F.type (%Inner.F.type) = fn_decl @Inner.F [symbolic = @Inner.%Inner.F (constants.%Inner.F)] { // CHECK:STDOUT: %return.patt: %pattern_type.7ce = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: %pattern_type.7ce = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %i32: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %.loc8_15: Core.Form = init_form %i32 [concrete = constants.%.ff5] // CHECK:STDOUT: %return.param: ref %i32 = out_param call_param0 // CHECK:STDOUT: %return: ref %i32 = return_slot %return.param // CHECK:STDOUT: } // CHECK:STDOUT: %Inner.G.decl: @Inner.%Inner.G.type (%Inner.G.type) = fn_decl @Inner.G [symbolic = @Inner.%Inner.G (constants.%Inner.G)] { // CHECK:STDOUT: %return.patt: %pattern_type.7ce = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: %pattern_type.7ce = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %i32.loc9: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %.loc9: Core.Form = init_form %i32.loc9 [concrete = constants.%.ff5] // CHECK:STDOUT: %return.param.loc9: ref %i32 = out_param call_param0 // CHECK:STDOUT: %return.loc9: ref %i32 = return_slot %return.param.loc9 // CHECK:STDOUT: } // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type.357] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%Inner // CHECK:STDOUT: .F = %Inner.F.decl // CHECK:STDOUT: .G = %Inner.G.decl // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic fn @Inner.F(@Class.%F.loc5_13.2: type) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: // CHECK:STDOUT: fn() -> out %return.param: %i32 { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %int_0: Core.IntLiteral = int_value 0 [concrete = constants.%int_0.5c6] // CHECK:STDOUT: %impl.elem0: %.545 = impl_witness_access constants.%ImplicitAs.impl_witness.6bc, element0 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5] // CHECK:STDOUT: %bound_method.loc8_29.1: = bound_method %int_0, %impl.elem0 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.bound] // CHECK:STDOUT: %specific_fn: = specific_function %impl.elem0, @Core.IntLiteral.as.ImplicitAs.impl.Convert(constants.%int_32) [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn] // CHECK:STDOUT: %bound_method.loc8_29.2: = bound_method %int_0, %specific_fn [concrete = constants.%bound_method] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.call: init %i32 = call %bound_method.loc8_29.2(%int_0) [concrete = constants.%int_0.6a9] // CHECK:STDOUT: %.loc8_29: init %i32 = converted %int_0, %Core.IntLiteral.as.ImplicitAs.impl.Convert.call [concrete = constants.%int_0.6a9] // CHECK:STDOUT: return %.loc8_29 // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic fn @Inner.G(@Class.%F.loc5_13.2: type) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: %F.loc13_46: type = symbolic_binding F, 0 [symbolic = %F.loc13_46 (constants.%F)] // CHECK:STDOUT: %Inner.F.type: type = fn_type @Inner.F, @Inner(%F.loc13_46) [symbolic = %Inner.F.type (constants.%Inner.F.type)] // CHECK:STDOUT: %Inner.F: @Inner.G.%Inner.F.type (%Inner.F.type) = struct_value () [symbolic = %Inner.F (constants.%Inner.F)] // CHECK:STDOUT: %Inner.F.specific_fn.loc13_46.2: = specific_function %Inner.F, @Inner.F(%F.loc13_46) [symbolic = %Inner.F.specific_fn.loc13_46.2 (constants.%Inner.F.specific_fn)] // CHECK:STDOUT: // CHECK:STDOUT: fn() -> out %return.param.loc13: %i32 { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %.loc13_46: @Inner.G.%Inner.F.type (%Inner.F.type) = specific_constant @Inner.%Inner.F.decl, @Inner(constants.%F) [symbolic = %Inner.F (constants.%Inner.F)] // CHECK:STDOUT: %F.ref: @Inner.G.%Inner.F.type (%Inner.F.type) = name_ref F, %.loc13_46 [symbolic = %Inner.F (constants.%Inner.F)] // CHECK:STDOUT: %Inner.F.specific_fn.loc13_46.1: = specific_function %F.ref, @Inner.F(constants.%F) [symbolic = %Inner.F.specific_fn.loc13_46.2 (constants.%Inner.F.specific_fn)] // CHECK:STDOUT: %Inner.F.call: init %i32 = call %Inner.F.specific_fn.loc13_46.1() // CHECK:STDOUT: return %Inner.F.call // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @Class(constants.%F) { // CHECK:STDOUT: %F.loc5_13.1 => constants.%F // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %Inner => constants.%Inner // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @Inner(constants.%F) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: %F => constants.%F // CHECK:STDOUT: %Inner.F.type => constants.%Inner.F.type // CHECK:STDOUT: %Inner.F => constants.%Inner.F // CHECK:STDOUT: %Inner.G.type => constants.%Inner.G.type // CHECK:STDOUT: %Inner.G => constants.%Inner.G // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @Inner.F(constants.%F) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @Inner.G(constants.%F) {} // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/function/call/ref.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/int.carbon // EXTRA-ARGS: --dump-sem-ir-ranges=only // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/function/call/ref.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/function/call/ref.carbon // --- basics.carbon library "[[@TEST_NAME]]"; //@dump-sem-ir-begin fn F(ref x: i32); fn G() { var y: i32 = 0; F(ref y); } //@dump-sem-ir-end // --- class.carbon library "[[@TEST_NAME]]"; //@dump-sem-ir-begin class C { fn F[ref self: Self](); } fn G() { var c: C; c.F(); } //@dump-sem-ir-end // --- fail_ref_not_durable_ref.carbon library "[[@TEST_NAME]]"; fn F(ref x: i32); fn G() { let y: i32 = 0; // CHECK:STDERR: fail_ref_not_durable_ref.carbon:[[@LINE+7]]:9: error: expression tagged with `ref` is not a durable reference [RefTagNotDurableRef] // CHECK:STDERR: F(ref y); // CHECK:STDERR: ^ // CHECK:STDERR: fail_ref_not_durable_ref.carbon:[[@LINE-7]]:6: note: initializing function parameter [InCallToFunctionParam] // CHECK:STDERR: fn F(ref x: i32); // CHECK:STDERR: ^~~~~~~~~~ // CHECK:STDERR: F(ref y); } // --- fail_missing_ref.carbon library "[[@TEST_NAME]]"; fn F(ref x: i32); fn G() { var y: i32 = 0; // CHECK:STDERR: fail_missing_ref.carbon:[[@LINE+7]]:5: error: argument to `ref` parameter not marked with `ref` [RefParamNoRefTag] // CHECK:STDERR: F(y); // CHECK:STDERR: ^ // CHECK:STDERR: fail_missing_ref.carbon:[[@LINE-7]]:6: note: initializing function parameter [InCallToFunctionParam] // CHECK:STDERR: fn F(ref x: i32); // CHECK:STDERR: ^~~~~~~~~~ // CHECK:STDERR: F(y); } // --- fail_unnecessary_ref.carbon library "[[@TEST_NAME]]"; fn F(x: i32); fn G() { var y: i32 = 0; // CHECK:STDERR: fail_unnecessary_ref.carbon:[[@LINE+4]]:27: error: `ref` tag is not an argument to a `ref` parameter [RefTagNoRefParam] // CHECK:STDERR: var unused z: (i32,) = (ref y,); // CHECK:STDERR: ^~~~~ // CHECK:STDERR: var unused z: (i32,) = (ref y,); // CHECK:STDERR: fail_unnecessary_ref.carbon:[[@LINE+7]]:5: error: `ref` tag is not an argument to a `ref` parameter [RefTagNoRefParam] // CHECK:STDERR: F(ref y); // CHECK:STDERR: ^~~~~ // CHECK:STDERR: fail_unnecessary_ref.carbon:[[@LINE-13]]:6: note: initializing function parameter [InCallToFunctionParam] // CHECK:STDERR: fn F(x: i32); // CHECK:STDERR: ^~~~~~ // CHECK:STDERR: F(ref y); } // CHECK:STDOUT: --- basics.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [concrete] // CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] // CHECK:STDOUT: %i32: type = class_type @Int, @Int(%int_32) [concrete] // CHECK:STDOUT: %pattern_type.7ce: type = pattern_type %i32 [concrete] // CHECK:STDOUT: %F.type: type = fn_type @F [concrete] // CHECK:STDOUT: %F: %F.type = struct_value () [concrete] // CHECK:STDOUT: %G.type: type = fn_type @G [concrete] // CHECK:STDOUT: %G: %G.type = struct_value () [concrete] // CHECK:STDOUT: %int_0.5c6: Core.IntLiteral = int_value 0 [concrete] // CHECK:STDOUT: %ImplicitAs.type.e8c: type = facet_type <@ImplicitAs, @ImplicitAs(%i32)> [concrete] // CHECK:STDOUT: %To: Core.IntLiteral = symbolic_binding To, 0 [symbolic] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.4e6: type = fn_type @Core.IntLiteral.as.ImplicitAs.impl.Convert, @Core.IntLiteral.as.ImplicitAs.impl(%To) [symbolic] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.3c2: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.4e6 = struct_value () [symbolic] // CHECK:STDOUT: %ImplicitAs.impl_witness.6bc: = impl_witness imports.%ImplicitAs.impl_witness_table.74f, @Core.IntLiteral.as.ImplicitAs.impl(%int_32) [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.e0d: type = fn_type @Core.IntLiteral.as.ImplicitAs.impl.Convert, @Core.IntLiteral.as.ImplicitAs.impl(%int_32) [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.e0d = struct_value () [concrete] // CHECK:STDOUT: %ImplicitAs.facet: %ImplicitAs.type.e8c = facet_value Core.IntLiteral, (%ImplicitAs.impl_witness.6bc) [concrete] // CHECK:STDOUT: %ImplicitAs.WithSelf.Convert.type.b37: type = fn_type @ImplicitAs.WithSelf.Convert, @ImplicitAs.WithSelf(%i32, %ImplicitAs.facet) [concrete] // CHECK:STDOUT: %.545: type = fn_type_with_self_type %ImplicitAs.WithSelf.Convert.type.b37, %ImplicitAs.facet [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.bound: = bound_method %int_0.5c6, %Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5 [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn: = specific_function %Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5, @Core.IntLiteral.as.ImplicitAs.impl.Convert(%int_32) [concrete] // CHECK:STDOUT: %bound_method: = bound_method %int_0.5c6, %Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn [concrete] // CHECK:STDOUT: %int_0.6a9: %i32 = int_value 0 [concrete] // CHECK:STDOUT: %Destroy.Op.type: type = fn_type @Destroy.Op [concrete] // CHECK:STDOUT: %Destroy.Op: %Destroy.Op.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core.import_ref.42d: @Core.IntLiteral.as.ImplicitAs.impl.%Core.IntLiteral.as.ImplicitAs.impl.Convert.type (%Core.IntLiteral.as.ImplicitAs.impl.Convert.type.4e6) = import_ref Core//prelude/parts/int, loc{{\d+_\d+}}, loaded [symbolic = @Core.IntLiteral.as.ImplicitAs.impl.%Core.IntLiteral.as.ImplicitAs.impl.Convert (constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.3c2)] // CHECK:STDOUT: %ImplicitAs.impl_witness_table.74f = impl_witness_table (%Core.import_ref.42d), @Core.IntLiteral.as.ImplicitAs.impl [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: %F.decl: %F.type = fn_decl @F [concrete = constants.%F] { // CHECK:STDOUT: %x.patt: %pattern_type.7ce = ref_binding_pattern x [concrete] // CHECK:STDOUT: %x.param_patt: %pattern_type.7ce = ref_param_pattern %x.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %x.param: ref %i32 = ref_param call_param0 // CHECK:STDOUT: %i32: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %x: ref %i32 = ref_binding x, %x.param // CHECK:STDOUT: } // CHECK:STDOUT: %G.decl: %G.type = fn_decl @G [concrete = constants.%G] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @F(%x.param: ref %i32); // CHECK:STDOUT: // CHECK:STDOUT: fn @G() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %y.patt: %pattern_type.7ce = ref_binding_pattern y [concrete] // CHECK:STDOUT: %y.var_patt: %pattern_type.7ce = var_pattern %y.patt [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %y.var: ref %i32 = var %y.var_patt // CHECK:STDOUT: %int_0: Core.IntLiteral = int_value 0 [concrete = constants.%int_0.5c6] // CHECK:STDOUT: %impl.elem0: %.545 = impl_witness_access constants.%ImplicitAs.impl_witness.6bc, element0 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5] // CHECK:STDOUT: %bound_method.loc8_3.1: = bound_method %int_0, %impl.elem0 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.bound] // CHECK:STDOUT: %specific_fn: = specific_function %impl.elem0, @Core.IntLiteral.as.ImplicitAs.impl.Convert(constants.%int_32) [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn] // CHECK:STDOUT: %bound_method.loc8_3.2: = bound_method %int_0, %specific_fn [concrete = constants.%bound_method] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.call: init %i32 = call %bound_method.loc8_3.2(%int_0) [concrete = constants.%int_0.6a9] // CHECK:STDOUT: %.loc8: init %i32 = converted %int_0, %Core.IntLiteral.as.ImplicitAs.impl.Convert.call [concrete = constants.%int_0.6a9] // CHECK:STDOUT: assign %y.var, %.loc8 // CHECK:STDOUT: %i32: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %y: ref %i32 = ref_binding y, %y.var // CHECK:STDOUT: %F.ref: %F.type = name_ref F, file.%F.decl [concrete = constants.%F] // CHECK:STDOUT: %y.ref: ref %i32 = name_ref y, %y // CHECK:STDOUT: %.loc10: %i32 = ref_tag %y.ref // CHECK:STDOUT: %F.call: init %empty_tuple.type = call %F.ref(%.loc10) // CHECK:STDOUT: %Destroy.Op.bound: = bound_method %y.var, constants.%Destroy.Op // CHECK:STDOUT: %Destroy.Op.call: init %empty_tuple.type = call %Destroy.Op.bound(%y.var) // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @Destroy.Op(%self.param: ref %i32) = "no_op"; // CHECK:STDOUT: // CHECK:STDOUT: --- class.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %C: type = class_type @C [concrete] // CHECK:STDOUT: %pattern_type.7c7: type = pattern_type %C [concrete] // CHECK:STDOUT: %C.F.type: type = fn_type @C.F [concrete] // CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] // CHECK:STDOUT: %C.F: %C.F.type = struct_value () [concrete] // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %complete_type: = complete_type_witness %empty_struct_type [concrete] // CHECK:STDOUT: %G.type: type = fn_type @G [concrete] // CHECK:STDOUT: %G: %G.type = struct_value () [concrete] // CHECK:STDOUT: %DefaultOrUnformed.type: type = facet_type <@DefaultOrUnformed> [concrete] // CHECK:STDOUT: %T: type = symbolic_binding T, 0 [symbolic] // CHECK:STDOUT: %T.as.DefaultOrUnformed.impl.Op.type.6c6: type = fn_type @T.as.DefaultOrUnformed.impl.Op, @T.as.DefaultOrUnformed.impl(%T) [symbolic] // CHECK:STDOUT: %T.as.DefaultOrUnformed.impl.Op.ae1: %T.as.DefaultOrUnformed.impl.Op.type.6c6 = struct_value () [symbolic] // CHECK:STDOUT: %DefaultOrUnformed.impl_witness.6fd: = impl_witness imports.%DefaultOrUnformed.impl_witness_table, @T.as.DefaultOrUnformed.impl(%C) [concrete] // CHECK:STDOUT: %DefaultOrUnformed.facet: %DefaultOrUnformed.type = facet_value %C, (%DefaultOrUnformed.impl_witness.6fd) [concrete] // CHECK:STDOUT: %Destroy.Op.type: type = fn_type @Destroy.Op [concrete] // CHECK:STDOUT: %Destroy.Op: %Destroy.Op.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core.import_ref.01d: @T.as.DefaultOrUnformed.impl.%T.as.DefaultOrUnformed.impl.Op.type (%T.as.DefaultOrUnformed.impl.Op.type.6c6) = import_ref Core//prelude/parts/default, loc{{\d+_\d+}}, loaded [symbolic = @T.as.DefaultOrUnformed.impl.%T.as.DefaultOrUnformed.impl.Op (constants.%T.as.DefaultOrUnformed.impl.Op.ae1)] // CHECK:STDOUT: %DefaultOrUnformed.impl_witness_table = impl_witness_table (%Core.import_ref.01d), @T.as.DefaultOrUnformed.impl [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: %C.decl: type = class_decl @C [concrete = constants.%C] {} {} // CHECK:STDOUT: %G.decl: %G.type = fn_decl @G [concrete = constants.%G] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @C { // CHECK:STDOUT: %C.F.decl: %C.F.type = fn_decl @C.F [concrete = constants.%C.F] { // CHECK:STDOUT: %self.patt: %pattern_type.7c7 = ref_binding_pattern self [concrete] // CHECK:STDOUT: %self.param_patt: %pattern_type.7c7 = ref_param_pattern %self.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %self.param: ref %C = ref_param call_param0 // CHECK:STDOUT: %Self.ref: type = name_ref Self, constants.%C [concrete = constants.%C] // CHECK:STDOUT: %self: ref %C = ref_binding self, %self.param // CHECK:STDOUT: } // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%C // CHECK:STDOUT: .F = %C.F.decl // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @C.F(%self.param: ref %C); // CHECK:STDOUT: // CHECK:STDOUT: fn @G() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %c.patt: %pattern_type.7c7 = ref_binding_pattern c [concrete] // CHECK:STDOUT: %c.var_patt: %pattern_type.7c7 = var_pattern %c.patt [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %c.var: ref %C = var %c.var_patt // CHECK:STDOUT: %DefaultOrUnformed.facet: %DefaultOrUnformed.type = facet_value constants.%C, (constants.%DefaultOrUnformed.impl_witness.6fd) [concrete = constants.%DefaultOrUnformed.facet] // CHECK:STDOUT: %.loc10_11.1: %DefaultOrUnformed.type = converted constants.%C, %DefaultOrUnformed.facet [concrete = constants.%DefaultOrUnformed.facet] // CHECK:STDOUT: %as_type: type = facet_access_type %.loc10_11.1 [concrete = constants.%C] // CHECK:STDOUT: %.loc10_11.2: type = converted %.loc10_11.1, %as_type [concrete = constants.%C] // CHECK:STDOUT: // CHECK:STDOUT: %.loc10_3: ref %C = splice_block %c.var {} // CHECK:STDOUT: %T.as.DefaultOrUnformed.impl.Op.call: init %C to %.loc10_3 = call %T.as.DefaultOrUnformed.impl.Op.specific_fn() // CHECK:STDOUT: assign %c.var, %T.as.DefaultOrUnformed.impl.Op.call // CHECK:STDOUT: %C.ref: type = name_ref C, file.%C.decl [concrete = constants.%C] // CHECK:STDOUT: %c: ref %C = ref_binding c, %c.var // CHECK:STDOUT: %c.ref: ref %C = name_ref c, %c // CHECK:STDOUT: %F.ref: %C.F.type = name_ref F, @C.%C.F.decl [concrete = constants.%C.F] // CHECK:STDOUT: %C.F.bound: = bound_method %c.ref, %F.ref // CHECK:STDOUT: %C.F.call: init %empty_tuple.type = call %C.F.bound(%c.ref) // CHECK:STDOUT: %Destroy.Op.bound: = bound_method %c.var, constants.%Destroy.Op // CHECK:STDOUT: %Destroy.Op.call: init %empty_tuple.type = call %Destroy.Op.bound(%c.var) // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @Destroy.Op(%self.param: ref %C) = "no_op"; // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/function/call/return_implicit.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/destroy.carbon // TODO: Add ranges and switch to "--dump-sem-ir-ranges=only". // EXTRA-ARGS: --dump-sem-ir-ranges=if-present // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/function/call/return_implicit.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/function/call/return_implicit.carbon fn MakeImplicitEmptyTuple() { } fn Main() { var unused b: () = MakeImplicitEmptyTuple(); } // CHECK:STDOUT: --- return_implicit.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %MakeImplicitEmptyTuple.type: type = fn_type @MakeImplicitEmptyTuple [concrete] // CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] // CHECK:STDOUT: %MakeImplicitEmptyTuple: %MakeImplicitEmptyTuple.type = struct_value () [concrete] // CHECK:STDOUT: %Main.type: type = fn_type @Main [concrete] // CHECK:STDOUT: %Main: %Main.type = struct_value () [concrete] // CHECK:STDOUT: %empty_tuple: %empty_tuple.type = tuple_value () [concrete] // CHECK:STDOUT: %pattern_type.cb1: type = pattern_type %empty_tuple.type [concrete] // CHECK:STDOUT: %Destroy.type: type = facet_type <@Destroy> [concrete] // CHECK:STDOUT: %Destroy.Op.type: type = fn_type @Destroy.Op [concrete] // CHECK:STDOUT: %Destroy.Op: %Destroy.Op.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: .Destroy = %Core.Destroy // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: %Core.Destroy: type = import_ref Core//prelude/parts/destroy, Destroy, loaded [concrete = constants.%Destroy.type] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .MakeImplicitEmptyTuple = %MakeImplicitEmptyTuple.decl // CHECK:STDOUT: .Main = %Main.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %MakeImplicitEmptyTuple.decl: %MakeImplicitEmptyTuple.type = fn_decl @MakeImplicitEmptyTuple [concrete = constants.%MakeImplicitEmptyTuple] {} {} // CHECK:STDOUT: %Main.decl: %Main.type = fn_decl @Main [concrete = constants.%Main] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @MakeImplicitEmptyTuple() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @Main() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %b.patt: %pattern_type.cb1 = ref_binding_pattern b [concrete] // CHECK:STDOUT: %b.var_patt: %pattern_type.cb1 = var_pattern %b.patt [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %b.var: ref %empty_tuple.type = var %b.var_patt // CHECK:STDOUT: %MakeImplicitEmptyTuple.ref: %MakeImplicitEmptyTuple.type = name_ref MakeImplicitEmptyTuple, file.%MakeImplicitEmptyTuple.decl [concrete = constants.%MakeImplicitEmptyTuple] // CHECK:STDOUT: %MakeImplicitEmptyTuple.call: init %empty_tuple.type = call %MakeImplicitEmptyTuple.ref() // CHECK:STDOUT: assign %b.var, %MakeImplicitEmptyTuple.call // CHECK:STDOUT: %.loc19_18.1: type = splice_block %.loc19_18.3 [concrete = constants.%empty_tuple.type] { // CHECK:STDOUT: %.loc19_18.2: %empty_tuple.type = tuple_literal () [concrete = constants.%empty_tuple] // CHECK:STDOUT: %.loc19_18.3: type = converted %.loc19_18.2, constants.%empty_tuple.type [concrete = constants.%empty_tuple.type] // CHECK:STDOUT: } // CHECK:STDOUT: %b: ref %empty_tuple.type = ref_binding b, %b.var // CHECK:STDOUT: %Destroy.Op.bound: = bound_method %b.var, constants.%Destroy.Op // CHECK:STDOUT: %Destroy.Op.call: init %empty_tuple.type = call %Destroy.Op.bound(%b.var) // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @Destroy.Op(%self.param: ref %empty_tuple.type) = "no_op"; // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/function/declaration/eval_musteval.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/int.carbon // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/function/declaration/eval_musteval.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/function/declaration/eval_musteval.carbon // --- basic.carbon library "[[@TEST_NAME]]"; fn Basic() -> i32 { return 0; } eval fn Eval() -> i32 { return 0; } musteval fn MustEval() -> i32 { return 0; } // --- member.carbon library "[[@TEST_NAME]]"; class C { eval fn Eval() {} musteval fn MustEval() {} } // --- private.carbon library "[[@TEST_NAME]]"; private eval fn Eval() -> i32 { return 0; } private musteval fn MustEval() -> i32 { return 0; } // --- fail_conflict.carbon library "[[@TEST_NAME]]"; // CHECK:STDERR: fail_conflict.carbon:[[@LINE+7]]:6: error: `musteval` not allowed on declaration with `eval` [ModifierNotAllowedWith] // CHECK:STDERR: eval musteval fn Mixed(); // CHECK:STDERR: ^~~~~~~~ // CHECK:STDERR: fail_conflict.carbon:[[@LINE+4]]:1: note: `eval` previously appeared here [ModifierPrevious] // CHECK:STDERR: eval musteval fn Mixed(); // CHECK:STDERR: ^~~~ // CHECK:STDERR: eval musteval fn Mixed(); // --- fail_order.carbon library "[[@TEST_NAME]]"; // CHECK:STDERR: fail_order.carbon:[[@LINE+7]]:6: error: `export` must appear before `eval` [ModifierMustAppearBefore] // CHECK:STDERR: eval export fn ExportEval(); // CHECK:STDERR: ^~~~~~ // CHECK:STDERR: fail_order.carbon:[[@LINE+4]]:1: note: `eval` previously appeared here [ModifierPrevious] // CHECK:STDERR: eval export fn ExportEval(); // CHECK:STDERR: ^~~~ // CHECK:STDERR: eval export fn ExportEval(); // CHECK:STDERR: fail_order.carbon:[[@LINE+7]]:6: error: `private` must appear before `eval` [ModifierMustAppearBefore] // CHECK:STDERR: eval private fn PrivateEval(); // CHECK:STDERR: ^~~~~~~ // CHECK:STDERR: fail_order.carbon:[[@LINE+4]]:1: note: `eval` previously appeared here [ModifierPrevious] // CHECK:STDERR: eval private fn PrivateEval(); // CHECK:STDERR: ^~~~ // CHECK:STDERR: eval private fn PrivateEval(); class C { // CHECK:STDERR: fail_order.carbon:[[@LINE+7]]:8: error: `virtual` must appear before `eval` [ModifierMustAppearBefore] // CHECK:STDERR: eval virtual fn VirtualEval(); // CHECK:STDERR: ^~~~~~~ // CHECK:STDERR: fail_order.carbon:[[@LINE+4]]:3: note: `eval` previously appeared here [ModifierPrevious] // CHECK:STDERR: eval virtual fn VirtualEval(); // CHECK:STDERR: ^~~~ // CHECK:STDERR: eval virtual fn VirtualEval(); } // --- fail_not_fn.carbon library "[[@TEST_NAME]]"; // CHECK:STDERR: fail_not_fn.carbon:[[@LINE+4]]:1: error: `eval` not allowed on `var` declaration [ModifierNotAllowedOnDeclaration] // CHECK:STDERR: eval var x: i32; // CHECK:STDERR: ^~~~ // CHECK:STDERR: eval var x: i32; // CHECK:STDERR: fail_not_fn.carbon:[[@LINE+4]]:1: error: `eval` not allowed on `class` declaration [ModifierNotAllowedOnDeclaration] // CHECK:STDERR: eval class C {}; // CHECK:STDERR: ^~~~ // CHECK:STDERR: eval class C {}; // --- redecl.carbon library "[[@TEST_NAME]]"; eval fn A() -> i32; eval fn A() -> i32 { return 0; } musteval fn B() -> i32; musteval fn B() -> i32 { return 0; } // --- fail_differ_in_redecl.carbon library "[[@TEST_NAME]]"; fn NoneThenEval(); fn NoneThenMustEval(); eval fn EvalThenNone(); eval fn EvalThenMustEval(); musteval fn MustEvalThenNone(); musteval fn MustEvalThenEval(); // CHECK:STDERR: fail_differ_in_redecl.carbon:[[@LINE+7]]:1: error: function redeclaration differs because new function is `eval` [FunctionRedeclEvaluationModeDiffers] // CHECK:STDERR: eval fn NoneThenEval() {} // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: fail_differ_in_redecl.carbon:[[@LINE-10]]:1: note: previously not declared as `eval` [FunctionRedeclEvaluationModePrevious] // CHECK:STDERR: fn NoneThenEval(); // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~ // CHECK:STDERR: eval fn NoneThenEval() {} // CHECK:STDERR: fail_differ_in_redecl.carbon:[[@LINE+7]]:1: error: function redeclaration differs because new function is `musteval` [FunctionRedeclEvaluationModeDiffers] // CHECK:STDERR: musteval fn NoneThenMustEval() {} // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: fail_differ_in_redecl.carbon:[[@LINE-18]]:1: note: previously not declared as `musteval` [FunctionRedeclEvaluationModePrevious] // CHECK:STDERR: fn NoneThenMustEval(); // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: musteval fn NoneThenMustEval() {} // CHECK:STDERR: fail_differ_in_redecl.carbon:[[@LINE+7]]:1: error: function redeclaration differs because new function is not `eval` [FunctionRedeclEvaluationModeDiffers] // CHECK:STDERR: fn EvalThenNone() {} // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: fail_differ_in_redecl.carbon:[[@LINE-26]]:1: note: previously declared as `eval` [FunctionRedeclEvaluationModePrevious] // CHECK:STDERR: eval fn EvalThenNone(); // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: fn EvalThenNone() {} // CHECK:STDERR: fail_differ_in_redecl.carbon:[[@LINE+7]]:1: error: function redeclaration differs because new function is `musteval` [FunctionRedeclEvaluationModeDiffers] // CHECK:STDERR: musteval fn EvalThenMustEval() {} // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: fail_differ_in_redecl.carbon:[[@LINE-34]]:1: note: previously declared as `eval` [FunctionRedeclEvaluationModePrevious] // CHECK:STDERR: eval fn EvalThenMustEval(); // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: musteval fn EvalThenMustEval() {} // CHECK:STDERR: fail_differ_in_redecl.carbon:[[@LINE+7]]:1: error: function redeclaration differs because new function is not `musteval` [FunctionRedeclEvaluationModeDiffers] // CHECK:STDERR: fn MustEvalThenNone() {} // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: fail_differ_in_redecl.carbon:[[@LINE-42]]:1: note: previously declared as `musteval` [FunctionRedeclEvaluationModePrevious] // CHECK:STDERR: musteval fn MustEvalThenNone(); // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: fn MustEvalThenNone() {} // CHECK:STDERR: fail_differ_in_redecl.carbon:[[@LINE+7]]:1: error: function redeclaration differs because new function is `eval` [FunctionRedeclEvaluationModeDiffers] // CHECK:STDERR: eval fn MustEvalThenEval() {} // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: fail_differ_in_redecl.carbon:[[@LINE-50]]:1: note: previously declared as `musteval` [FunctionRedeclEvaluationModePrevious] // CHECK:STDERR: musteval fn MustEvalThenEval(); // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: eval fn MustEvalThenEval() {} ================================================ FILE: toolchain/check/testdata/function/declaration/export_name.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/none.carbon // TODO: Add ranges and switch to "--dump-sem-ir-ranges=only". // EXTRA-ARGS: --dump-sem-ir-ranges=if-present // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/function/declaration/export_name.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/function/declaration/export_name.carbon // ============================================================================ // Setup files // ============================================================================ // --- base.carbon library "[[@TEST_NAME]]"; fn F(); // --- export.carbon library "[[@TEST_NAME]]"; import library "base"; export F; // ============================================================================ // Test files // ============================================================================ // --- use_export.carbon library "[[@TEST_NAME]]"; import library "export"; var f: () = F(); // CHECK:STDOUT: --- base.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %F.type: type = fn_type @F [concrete] // CHECK:STDOUT: %F: %F.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .F = %F.decl // CHECK:STDOUT: } // CHECK:STDOUT: %F.decl: %F.type = fn_decl @F [concrete = constants.%F] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @F(); // CHECK:STDOUT: // CHECK:STDOUT: --- export.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %F.type: type = fn_type @F [concrete] // CHECK:STDOUT: %F: %F.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Main.F: %F.type = import_ref Main//base, F, loaded [concrete = constants.%F] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .F = %F // CHECK:STDOUT: } // CHECK:STDOUT: %default.import = import // CHECK:STDOUT: %F: %F.type = export F, imports.%Main.F [concrete = constants.%F] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @F [from "base.carbon"]; // CHECK:STDOUT: // CHECK:STDOUT: --- use_export.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] // CHECK:STDOUT: %empty_tuple: %empty_tuple.type = tuple_value () [concrete] // CHECK:STDOUT: %pattern_type: type = pattern_type %empty_tuple.type [concrete] // CHECK:STDOUT: %F.type: type = fn_type @F [concrete] // CHECK:STDOUT: %F: %F.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Main.F: %F.type = import_ref Main//export, F, loaded [concrete = constants.%F] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .F = imports.%Main.F // CHECK:STDOUT: .f = %f // CHECK:STDOUT: } // CHECK:STDOUT: %default.import = import // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %f.patt: %pattern_type = ref_binding_pattern f [concrete] // CHECK:STDOUT: %f.var_patt: %pattern_type = var_pattern %f.patt [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %f.var: ref %empty_tuple.type = var %f.var_patt [concrete] // CHECK:STDOUT: %.loc6_9.1: type = splice_block %.loc6_9.3 [concrete = constants.%empty_tuple.type] { // CHECK:STDOUT: %.loc6_9.2: %empty_tuple.type = tuple_literal () [concrete = constants.%empty_tuple] // CHECK:STDOUT: %.loc6_9.3: type = converted %.loc6_9.2, constants.%empty_tuple.type [concrete = constants.%empty_tuple.type] // CHECK:STDOUT: } // CHECK:STDOUT: %f: ref %empty_tuple.type = ref_binding f, %f.var [concrete = %f.var] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @F [from "export.carbon"]; // CHECK:STDOUT: // CHECK:STDOUT: fn @__global_init() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %F.ref: %F.type = name_ref F, imports.%Main.F [concrete = constants.%F] // CHECK:STDOUT: %F.call: init %empty_tuple.type = call %F.ref() // CHECK:STDOUT: assign file.%f.var, %F.call // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/function/declaration/extern.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/none.carbon // TODO: Add ranges and switch to "--dump-sem-ir-ranges=only". // EXTRA-ARGS: --dump-sem-ir-ranges=if-present // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/function/declaration/extern.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/function/declaration/extern.carbon // --- basic.carbon library "[[@TEST_NAME]]"; extern fn F(); // --- basic_use.carbon import library "basic"; var x: () = F(); // --- fail_redecl.carbon library "[[@TEST_NAME]]"; extern fn F(); // CHECK:STDERR: fail_redecl.carbon:[[@LINE+7]]:1: error: redeclaration of `fn F` is redundant [RedeclRedundant] // CHECK:STDERR: extern fn F(); // CHECK:STDERR: ^~~~~~~~~~~~~~ // CHECK:STDERR: fail_redecl.carbon:[[@LINE-4]]:1: note: previously declared here [RedeclPrevDecl] // CHECK:STDERR: extern fn F(); // CHECK:STDERR: ^~~~~~~~~~~~~~ // CHECK:STDERR: extern fn F(); // --- fail_redecl_extern.carbon library "[[@TEST_NAME]]"; extern fn F(); // CHECK:STDERR: fail_redecl_extern.carbon:[[@LINE+7]]:1: error: redeclaration of `fn F` is redundant [RedeclRedundant] // CHECK:STDERR: fn F(); // CHECK:STDERR: ^~~~~~~ // CHECK:STDERR: fail_redecl_extern.carbon:[[@LINE-4]]:1: note: previously declared here [RedeclPrevDecl] // CHECK:STDERR: extern fn F(); // CHECK:STDERR: ^~~~~~~~~~~~~~ // CHECK:STDERR: fn F(); // --- fail_member_extern.carbon library "[[@TEST_NAME]]"; class C { // CHECK:STDERR: fail_member_extern.carbon:[[@LINE+4]]:3: error: `extern` not allowed; requires file or namespace scope [ModifierExternNotAllowed] // CHECK:STDERR: extern fn F(); // CHECK:STDERR: ^~~~~~ // CHECK:STDERR: extern fn F(); // CHECK:STDERR: fail_member_extern.carbon:[[@LINE+4]]:3: error: `extern` not allowed; requires file or namespace scope [ModifierExternNotAllowed] // CHECK:STDERR: extern fn G[self: Self](); // CHECK:STDERR: ^~~~~~ // CHECK:STDERR: extern fn G[self: Self](); } // --- fail_extern_library_in_importer.carbon library "[[@TEST_NAME]]"; import library "basic"; // CHECK:STDERR: fail_extern_library_in_importer.carbon:[[@LINE+8]]:1: error: cannot declare imported `fn F` as `extern library` [ExternLibraryInImporter] // CHECK:STDERR: extern library "basic" fn F(); // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: fail_extern_library_in_importer.carbon:[[@LINE-5]]:1: in import [InImport] // CHECK:STDERR: basic.carbon:4:1: note: previously declared here [RedeclPrevDecl] // CHECK:STDERR: extern fn F(); // CHECK:STDERR: ^~~~~~~~~~~~~~ // CHECK:STDERR: extern library "basic" fn F(); // CHECK:STDOUT: --- basic.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %F.type: type = fn_type @F [concrete] // CHECK:STDOUT: %F: %F.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .F = %F.decl // CHECK:STDOUT: } // CHECK:STDOUT: %F.decl: %F.type = fn_decl @F [concrete = constants.%F] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: extern fn @F(); // CHECK:STDOUT: // CHECK:STDOUT: --- basic_use.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] // CHECK:STDOUT: %empty_tuple: %empty_tuple.type = tuple_value () [concrete] // CHECK:STDOUT: %pattern_type: type = pattern_type %empty_tuple.type [concrete] // CHECK:STDOUT: %F.type: type = fn_type @F [concrete] // CHECK:STDOUT: %F: %F.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Main.F: %F.type = import_ref Main//basic, F, loaded [concrete = constants.%F] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .F = imports.%Main.F // CHECK:STDOUT: .x = %x // CHECK:STDOUT: } // CHECK:STDOUT: %default.import = import // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %x.patt: %pattern_type = ref_binding_pattern x [concrete] // CHECK:STDOUT: %x.var_patt: %pattern_type = var_pattern %x.patt [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %x.var: ref %empty_tuple.type = var %x.var_patt [concrete] // CHECK:STDOUT: %.loc4_9.1: type = splice_block %.loc4_9.3 [concrete = constants.%empty_tuple.type] { // CHECK:STDOUT: %.loc4_9.2: %empty_tuple.type = tuple_literal () [concrete = constants.%empty_tuple] // CHECK:STDOUT: %.loc4_9.3: type = converted %.loc4_9.2, constants.%empty_tuple.type [concrete = constants.%empty_tuple.type] // CHECK:STDOUT: } // CHECK:STDOUT: %x: ref %empty_tuple.type = ref_binding x, %x.var [concrete = %x.var] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: extern fn @F [from "basic.carbon"]; // CHECK:STDOUT: // CHECK:STDOUT: fn @__global_init() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %F.ref: %F.type = name_ref F, imports.%Main.F [concrete = constants.%F] // CHECK:STDOUT: %F.call: init %empty_tuple.type = call %F.ref() // CHECK:STDOUT: assign file.%x.var, %F.call // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- fail_redecl.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %F.type: type = fn_type @F [concrete] // CHECK:STDOUT: %F: %F.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .F = %F.decl.loc4 // CHECK:STDOUT: } // CHECK:STDOUT: %F.decl.loc4: %F.type = fn_decl @F [concrete = constants.%F] {} {} // CHECK:STDOUT: %F.decl.loc12: %F.type = fn_decl @F [concrete = constants.%F] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: extern fn @F(); // CHECK:STDOUT: // CHECK:STDOUT: --- fail_redecl_extern.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %F.type: type = fn_type @F [concrete] // CHECK:STDOUT: %F: %F.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .F = %F.decl.loc4 // CHECK:STDOUT: } // CHECK:STDOUT: %F.decl.loc4: %F.type = fn_decl @F [concrete = constants.%F] {} {} // CHECK:STDOUT: %F.decl.loc12: %F.type = fn_decl @F [concrete = constants.%F] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: extern fn @F(); // CHECK:STDOUT: // CHECK:STDOUT: --- fail_member_extern.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %C: type = class_type @C [concrete] // CHECK:STDOUT: %C.F.type: type = fn_type @C.F [concrete] // CHECK:STDOUT: %C.F: %C.F.type = struct_value () [concrete] // CHECK:STDOUT: %pattern_type: type = pattern_type %C [concrete] // CHECK:STDOUT: %C.G.type: type = fn_type @C.G [concrete] // CHECK:STDOUT: %C.G: %C.G.type = struct_value () [concrete] // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %complete_type: = complete_type_witness %empty_struct_type [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .C = %C.decl // CHECK:STDOUT: } // CHECK:STDOUT: %C.decl: type = class_decl @C [concrete = constants.%C] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @C { // CHECK:STDOUT: %C.F.decl: %C.F.type = fn_decl @C.F [concrete = constants.%C.F] {} {} // CHECK:STDOUT: %C.G.decl: %C.G.type = fn_decl @C.G [concrete = constants.%C.G] { // CHECK:STDOUT: %self.patt: %pattern_type = value_binding_pattern self [concrete] // CHECK:STDOUT: %self.param_patt: %pattern_type = value_param_pattern %self.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %self.param: %C = value_param call_param0 // CHECK:STDOUT: %Self.ref: type = name_ref Self, constants.%C [concrete = constants.%C] // CHECK:STDOUT: %self: %C = value_binding self, %self.param // CHECK:STDOUT: } // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%C // CHECK:STDOUT: .F = %C.F.decl // CHECK:STDOUT: .G = %C.G.decl // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @C.F(); // CHECK:STDOUT: // CHECK:STDOUT: fn @C.G(%self.param: %C); // CHECK:STDOUT: // CHECK:STDOUT: --- fail_extern_library_in_importer.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %F.type: type = fn_type @F [concrete] // CHECK:STDOUT: %F: %F.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .F = invalid // CHECK:STDOUT: } // CHECK:STDOUT: %default.import = import // CHECK:STDOUT: %F.decl: %F.type = fn_decl @F [concrete = constants.%F] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: extern fn @F [from "basic.carbon"]; // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/function/declaration/extern_library.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/none.carbon // TODO: Add ranges and switch to "--dump-sem-ir-ranges=only". // EXTRA-ARGS: --dump-sem-ir-ranges=if-present // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/function/declaration/extern_library.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/function/declaration/extern_library.carbon // --- extern_library.carbon library "[[@TEST_NAME]]"; extern library "extern_library_owner" fn F(); // --- extern_library_owner.carbon library "[[@TEST_NAME]]"; import library "extern_library"; extern fn F(); // --- fail_extern_library_nonowner.carbon library "[[@TEST_NAME]]"; import library "extern_library"; // CHECK:STDERR: fail_extern_library_nonowner.carbon:[[@LINE+8]]:1: error: declaration in library "extern_library_nonowner" doesn't match `extern library` declaration [ExternLibraryIncorrect] // CHECK:STDERR: extern fn F(); // CHECK:STDERR: ^~~~~~~~~~~~~~ // CHECK:STDERR: fail_extern_library_nonowner.carbon:[[@LINE-5]]:1: in import [InImport] // CHECK:STDERR: extern_library.carbon:4:1: note: previously declared with `extern library` here [ExternLibraryExpected] // CHECK:STDERR: extern library "extern_library_owner" fn F(); // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: extern fn F(); // --- fail_extern_library_nonextern.carbon library "[[@TEST_NAME]]"; import library "extern_library"; // CHECK:STDERR: fail_extern_library_nonextern.carbon:[[@LINE+8]]:1: error: redeclarations of `fn F` must match use of `extern` [RedeclExternMismatch] // CHECK:STDERR: fn F(); // CHECK:STDERR: ^~~~~~~ // CHECK:STDERR: fail_extern_library_nonextern.carbon:[[@LINE-5]]:1: in import [InImport] // CHECK:STDERR: extern_library.carbon:4:1: note: previously declared here [RedeclPrevDecl] // CHECK:STDERR: extern library "extern_library_owner" fn F(); // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: fn F(); // --- fail_extern_library_redecl.carbon library "[[@TEST_NAME]]"; import library "extern_library"; // CHECK:STDERR: fail_extern_library_redecl.carbon:[[@LINE+8]]:1: error: declaration in library "extern_library_redecl" doesn't match `extern library` declaration [ExternLibraryIncorrect] // CHECK:STDERR: extern library "extern_library_owner" fn F(); // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: fail_extern_library_redecl.carbon:[[@LINE-5]]:1: in import [InImport] // CHECK:STDERR: extern_library.carbon:4:1: note: previously declared with `extern library` here [ExternLibraryExpected] // CHECK:STDERR: extern library "extern_library_owner" fn F(); // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: extern library "extern_library_owner" fn F(); // --- extern_library_copy.carbon library "[[@TEST_NAME]]"; extern library "extern_library_owner" fn F(); // --- fail_extern_library_collision.carbon library "[[@TEST_NAME]]"; // CHECK:STDERR: fail_extern_library_collision.carbon:[[@LINE+9]]:1: in import [InImport] // CHECK:STDERR: extern_library_copy.carbon:4:1: error: duplicate name `F` being declared in the same scope [NameDeclDuplicate] // CHECK:STDERR: extern library "extern_library_owner" fn F(); // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: fail_extern_library_collision.carbon:[[@LINE+5]]:1: in import [InImport] // CHECK:STDERR: extern_library.carbon:4:1: note: name is previously declared here [NameDeclPrevious] // CHECK:STDERR: extern library "extern_library_owner" fn F(); // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: import library "extern_library"; import library "extern_library_copy"; // --- extern_library_mismatch.carbon library "[[@TEST_NAME]]"; extern library "extern_library_owner" fn F(); // --- fail_extern_library_mismatch_owner.carbon library "[[@TEST_NAME]]"; import library "extern_library_mismatch" // CHECK:STDERR: fail_extern_library_mismatch_owner.carbon:[[@LINE+8]]:1: error: `import` declarations must end with a `;` [ExpectedDeclSemi] // CHECK:STDERR: extern fn F(); // CHECK:STDERR: ^~~~~~ // CHECK:STDERR: // CHECK:STDERR: fail_extern_library_mismatch_owner.carbon:[[@LINE-6]]:1: error: semantics TODO: `handle invalid parse trees in `check`` [SemanticsTodo] // CHECK:STDERR: import library "extern_library_mismatch" // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: extern fn F(); // --- fail_extern_self_library.carbon library "[[@TEST_NAME]]"; // CHECK:STDERR: fail_extern_self_library.carbon:[[@LINE+4]]:1: error: `extern library` cannot specify the current library [ExternLibraryIsCurrentLibrary] // CHECK:STDERR: extern library "extern_self_library" fn F(); // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: extern library "extern_self_library" fn F(); // --- extern_of_import.carbon library "[[@TEST_NAME]]"; fn F(); // --- fail_extern_of_import_redecl.carbon library "[[@TEST_NAME]]"; import library "extern_of_import"; // CHECK:STDERR: fail_extern_of_import_redecl.carbon:[[@LINE+8]]:1: error: redeclarations of `fn F` must match use of `extern` [RedeclExternMismatch] // CHECK:STDERR: extern library "extern_of_import" fn F(); // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: fail_extern_of_import_redecl.carbon:[[@LINE-5]]:1: in import [InImport] // CHECK:STDERR: extern_of_import.carbon:4:1: note: previously declared here [RedeclPrevDecl] // CHECK:STDERR: fn F(); // CHECK:STDERR: ^~~~~~~ // CHECK:STDERR: extern library "extern_of_import" fn F(); // --- fail_extern_library_on_definition.carbon library "[[@TEST_NAME]]"; // CHECK:STDERR: fail_extern_library_on_definition.carbon:[[@LINE+4]]:1: error: a library cannot be provided for an `extern` modifier on a definition [ExternLibraryOnDefinition] // CHECK:STDERR: extern library "extern_library_owner" fn F() {} // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: extern library "extern_library_owner" fn F() {} // CHECK:STDOUT: --- extern_library.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %F.type: type = fn_type @F [concrete] // CHECK:STDOUT: %F: %F.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .F = %F.decl // CHECK:STDOUT: } // CHECK:STDOUT: %F.decl: %F.type = fn_decl @F [concrete = constants.%F] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: extern fn @F(); // CHECK:STDOUT: // CHECK:STDOUT: --- extern_library_owner.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %F.type: type = fn_type @F [concrete] // CHECK:STDOUT: %F: %F.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .F = %F.decl // CHECK:STDOUT: } // CHECK:STDOUT: %default.import = import // CHECK:STDOUT: %F.decl: %F.type = fn_decl @F [concrete = constants.%F] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: extern fn @F; // CHECK:STDOUT: // CHECK:STDOUT: --- fail_extern_library_nonowner.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %F.type: type = fn_type @F [concrete] // CHECK:STDOUT: %F: %F.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .F = %F.decl // CHECK:STDOUT: } // CHECK:STDOUT: %default.import = import // CHECK:STDOUT: %F.decl: %F.type = fn_decl @F [concrete = constants.%F] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: extern fn @F; // CHECK:STDOUT: // CHECK:STDOUT: --- fail_extern_library_nonextern.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %F.type: type = fn_type @F [concrete] // CHECK:STDOUT: %F: %F.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .F = %F.decl // CHECK:STDOUT: } // CHECK:STDOUT: %default.import = import // CHECK:STDOUT: %F.decl: %F.type = fn_decl @F [concrete = constants.%F] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: extern fn @F; // CHECK:STDOUT: // CHECK:STDOUT: --- fail_extern_library_redecl.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %F.type: type = fn_type @F [concrete] // CHECK:STDOUT: %F: %F.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .F = invalid // CHECK:STDOUT: } // CHECK:STDOUT: %default.import = import // CHECK:STDOUT: %F.decl: %F.type = fn_decl @F [concrete = constants.%F] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: extern fn @F; // CHECK:STDOUT: // CHECK:STDOUT: --- extern_library_copy.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %F.type: type = fn_type @F [concrete] // CHECK:STDOUT: %F: %F.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .F = %F.decl // CHECK:STDOUT: } // CHECK:STDOUT: %F.decl: %F.type = fn_decl @F [concrete = constants.%F] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: extern fn @F(); // CHECK:STDOUT: // CHECK:STDOUT: --- fail_extern_library_collision.carbon // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Main.F = import_ref Main//extern_library, F, unloaded // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .F = imports.%Main.F // CHECK:STDOUT: } // CHECK:STDOUT: %default.import = import // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- extern_library_mismatch.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %F.type: type = fn_type @F [concrete] // CHECK:STDOUT: %F: %F.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .F = %F.decl // CHECK:STDOUT: } // CHECK:STDOUT: %F.decl: %F.type = fn_decl @F [concrete = constants.%F] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: extern fn @F(); // CHECK:STDOUT: // CHECK:STDOUT: --- fail_extern_library_mismatch_owner.carbon // CHECK:STDOUT: // CHECK:STDOUT: --- fail_extern_self_library.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %F.type: type = fn_type @F [concrete] // CHECK:STDOUT: %F: %F.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .F = %F.decl // CHECK:STDOUT: } // CHECK:STDOUT: %F.decl: %F.type = fn_decl @F [concrete = constants.%F] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: extern fn @F(); // CHECK:STDOUT: // CHECK:STDOUT: --- extern_of_import.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %F.type: type = fn_type @F [concrete] // CHECK:STDOUT: %F: %F.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .F = %F.decl // CHECK:STDOUT: } // CHECK:STDOUT: %F.decl: %F.type = fn_decl @F [concrete = constants.%F] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @F(); // CHECK:STDOUT: // CHECK:STDOUT: --- fail_extern_of_import_redecl.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %F.type: type = fn_type @F [concrete] // CHECK:STDOUT: %F: %F.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .F = invalid // CHECK:STDOUT: } // CHECK:STDOUT: %default.import = import // CHECK:STDOUT: %F.decl: %F.type = fn_decl @F [concrete = constants.%F] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @F [from "extern_of_import.carbon"]; // CHECK:STDOUT: // CHECK:STDOUT: --- fail_extern_library_on_definition.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %F.type: type = fn_type @F [concrete] // CHECK:STDOUT: %F: %F.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .F = %F.decl // CHECK:STDOUT: } // CHECK:STDOUT: %F.decl: %F.type = fn_decl @F [concrete = constants.%F] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: extern fn @F() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/function/declaration/extern_library_for_default.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/none.carbon // TODO: Add ranges and switch to "--dump-sem-ir-ranges=only". // EXTRA-ARGS: --dump-sem-ir-ranges=if-present // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/function/declaration/extern_library_for_default.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/function/declaration/extern_library_for_default.carbon // --- default.carbon package Foo; extern library "expected" fn F(); // --- expected.carbon package Foo library "[[@TEST_NAME]]"; import library default; extern fn F(); // --- fail_wrong_library.carbon package Foo library "[[@TEST_NAME]]"; import library default; // CHECK:STDERR: fail_wrong_library.carbon:[[@LINE+8]]:1: error: declaration in library "wrong_library" doesn't match `extern library` declaration [ExternLibraryIncorrect] // CHECK:STDERR: extern fn F(); // CHECK:STDERR: ^~~~~~~~~~~~~~ // CHECK:STDERR: fail_wrong_library.carbon:[[@LINE-5]]:1: in import [InImport] // CHECK:STDERR: default.carbon:4:1: note: previously declared with `extern library` here [ExternLibraryExpected] // CHECK:STDERR: extern library "expected" fn F(); // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: extern fn F(); // CHECK:STDOUT: --- default.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %F.type: type = fn_type @F [concrete] // CHECK:STDOUT: %F: %F.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .F = %F.decl // CHECK:STDOUT: } // CHECK:STDOUT: %F.decl: %F.type = fn_decl @F [concrete = constants.%F] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: extern fn @F(); // CHECK:STDOUT: // CHECK:STDOUT: --- expected.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %F.type: type = fn_type @F [concrete] // CHECK:STDOUT: %F: %F.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .F = %F.decl // CHECK:STDOUT: } // CHECK:STDOUT: %default.import = import // CHECK:STDOUT: %F.decl: %F.type = fn_decl @F [concrete = constants.%F] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: extern fn @F; // CHECK:STDOUT: // CHECK:STDOUT: --- fail_wrong_library.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %F.type: type = fn_type @F [concrete] // CHECK:STDOUT: %F: %F.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .F = %F.decl // CHECK:STDOUT: } // CHECK:STDOUT: %default.import = import // CHECK:STDOUT: %F.decl: %F.type = fn_decl @F [concrete = constants.%F] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: extern fn @F; // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/function/declaration/extern_library_from_default.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/none.carbon // TODO: Add ranges and switch to "--dump-sem-ir-ranges=only". // EXTRA-ARGS: --dump-sem-ir-ranges=if-present // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/function/declaration/extern_library_from_default.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/function/declaration/extern_library_from_default.carbon // --- extern_library.carbon library "[[@TEST_NAME]]"; extern library default fn F(); // --- default.carbon import library "extern_library"; extern fn F(); // --- fail_wrong_library.carbon library "[[@TEST_NAME]]"; import library "extern_library"; // CHECK:STDERR: fail_wrong_library.carbon:[[@LINE+8]]:1: error: declaration in library "wrong_library" doesn't match `extern library` declaration [ExternLibraryIncorrect] // CHECK:STDERR: extern fn F(); // CHECK:STDERR: ^~~~~~~~~~~~~~ // CHECK:STDERR: fail_wrong_library.carbon:[[@LINE-5]]:1: in import [InImport] // CHECK:STDERR: extern_library.carbon:4:1: note: previously declared with `extern library` here [ExternLibraryExpected] // CHECK:STDERR: extern library default fn F(); // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: extern fn F(); // CHECK:STDOUT: --- extern_library.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %F.type: type = fn_type @F [concrete] // CHECK:STDOUT: %F: %F.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .F = %F.decl // CHECK:STDOUT: } // CHECK:STDOUT: %F.decl: %F.type = fn_decl @F [concrete = constants.%F] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: extern fn @F(); // CHECK:STDOUT: // CHECK:STDOUT: --- default.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %F.type: type = fn_type @F [concrete] // CHECK:STDOUT: %F: %F.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .F = %F.decl // CHECK:STDOUT: } // CHECK:STDOUT: %default.import = import // CHECK:STDOUT: %F.decl: %F.type = fn_decl @F [concrete = constants.%F] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: extern fn @F; // CHECK:STDOUT: // CHECK:STDOUT: --- fail_wrong_library.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %F.type: type = fn_type @F [concrete] // CHECK:STDOUT: %F: %F.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .F = %F.decl // CHECK:STDOUT: } // CHECK:STDOUT: %default.import = import // CHECK:STDOUT: %F.decl: %F.type = fn_decl @F [concrete = constants.%F] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: extern fn @F; // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/function/declaration/fail_import_incomplete_return.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/destroy.carbon // TODO: Add ranges and switch to "--dump-sem-ir-ranges=only". // EXTRA-ARGS: --dump-sem-ir-ranges=if-present // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/function/declaration/fail_import_incomplete_return.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/function/declaration/fail_import_incomplete_return.carbon // --- fail_incomplete_return.carbon library "[[@TEST_NAME]]"; class C; class D; fn ReturnCUnused() -> C; fn ReturnCUsed() -> C; fn ReturnDUnused() -> D; fn ReturnDUsed() -> D; fn Call() { // CHECK:STDERR: fail_incomplete_return.carbon:[[@LINE+10]]:3: error: function returns incomplete type `C` [IncompleteTypeInFunctionReturnType] // CHECK:STDERR: ReturnCUsed(); // CHECK:STDERR: ^~~~~~~~~~~~~ // CHECK:STDERR: fail_incomplete_return.carbon:[[@LINE-12]]:1: note: class was forward declared here [ClassForwardDeclaredHere] // CHECK:STDERR: class C; // CHECK:STDERR: ^~~~~~~~ // CHECK:STDERR: fail_incomplete_return.carbon:[[@LINE-11]]:21: note: return type declared here [IncompleteReturnTypeHere] // CHECK:STDERR: fn ReturnCUsed() -> C; // CHECK:STDERR: ^ // CHECK:STDERR: ReturnCUsed(); // CHECK:STDERR: fail_incomplete_return.carbon:[[@LINE+10]]:3: error: function returns incomplete type `D` [IncompleteTypeInFunctionReturnType] // CHECK:STDERR: ReturnDUsed(); // CHECK:STDERR: ^~~~~~~~~~~~~ // CHECK:STDERR: fail_incomplete_return.carbon:[[@LINE-22]]:1: note: class was forward declared here [ClassForwardDeclaredHere] // CHECK:STDERR: class D; // CHECK:STDERR: ^~~~~~~~ // CHECK:STDERR: fail_incomplete_return.carbon:[[@LINE-20]]:21: note: return type declared here [IncompleteReturnTypeHere] // CHECK:STDERR: fn ReturnDUsed() -> D; // CHECK:STDERR: ^ // CHECK:STDERR: ReturnDUsed(); } class D {} // --- fail_use_imported.carbon library "[[@TEST_NAME]]"; import library "incomplete_return"; fn CallFAndGIncomplete() { // CHECK:STDERR: fail_use_imported.carbon:[[@LINE+12]]:3: error: function returns incomplete type `C` [IncompleteTypeInFunctionReturnType] // CHECK:STDERR: ReturnCUnused(); // CHECK:STDERR: ^~~~~~~~~~~~~~~ // CHECK:STDERR: fail_use_imported.carbon:[[@LINE-6]]:1: in import [InImport] // CHECK:STDERR: fail_incomplete_return.carbon:4:1: note: class was forward declared here [ClassForwardDeclaredHere] // CHECK:STDERR: class C; // CHECK:STDERR: ^~~~~~~~ // CHECK:STDERR: fail_use_imported.carbon:[[@LINE-10]]:1: in import [InImport] // CHECK:STDERR: fail_incomplete_return.carbon:7:23: note: return type declared here [IncompleteReturnTypeHere] // CHECK:STDERR: fn ReturnCUnused() -> C; // CHECK:STDERR: ^ // CHECK:STDERR: ReturnCUnused(); // CHECK:STDERR: fail_use_imported.carbon:[[@LINE+12]]:3: error: function returns incomplete type `C` [IncompleteTypeInFunctionReturnType] // CHECK:STDERR: ReturnCUsed(); // CHECK:STDERR: ^~~~~~~~~~~~~ // CHECK:STDERR: fail_use_imported.carbon:[[@LINE-19]]:1: in import [InImport] // CHECK:STDERR: fail_incomplete_return.carbon:4:1: note: class was forward declared here [ClassForwardDeclaredHere] // CHECK:STDERR: class C; // CHECK:STDERR: ^~~~~~~~ // CHECK:STDERR: fail_use_imported.carbon:[[@LINE-23]]:1: in import [InImport] // CHECK:STDERR: fail_incomplete_return.carbon:8:21: note: return type declared here [IncompleteReturnTypeHere] // CHECK:STDERR: fn ReturnCUsed() -> C; // CHECK:STDERR: ^ // CHECK:STDERR: ReturnCUsed(); ReturnDUnused(); ReturnDUsed(); } // CHECK:STDOUT: --- fail_incomplete_return.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %C: type = class_type @C [concrete] // CHECK:STDOUT: %D: type = class_type @D [concrete] // CHECK:STDOUT: %.a69: Core.Form = init_form %C [concrete] // CHECK:STDOUT: %pattern_type.7c7: type = pattern_type %C [concrete] // CHECK:STDOUT: %ReturnCUnused.type: type = fn_type @ReturnCUnused [concrete] // CHECK:STDOUT: %ReturnCUnused: %ReturnCUnused.type = struct_value () [concrete] // CHECK:STDOUT: %ReturnCUsed.type: type = fn_type @ReturnCUsed [concrete] // CHECK:STDOUT: %ReturnCUsed: %ReturnCUsed.type = struct_value () [concrete] // CHECK:STDOUT: %.9a5: Core.Form = init_form %D [concrete] // CHECK:STDOUT: %pattern_type.9c8: type = pattern_type %D [concrete] // CHECK:STDOUT: %ReturnDUnused.type: type = fn_type @ReturnDUnused [concrete] // CHECK:STDOUT: %ReturnDUnused: %ReturnDUnused.type = struct_value () [concrete] // CHECK:STDOUT: %ReturnDUsed.type: type = fn_type @ReturnDUsed [concrete] // CHECK:STDOUT: %ReturnDUsed: %ReturnDUsed.type = struct_value () [concrete] // CHECK:STDOUT: %Call.type: type = fn_type @Call [concrete] // CHECK:STDOUT: %Call: %Call.type = struct_value () [concrete] // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %complete_type: = complete_type_witness %empty_struct_type [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .C = %C.decl // CHECK:STDOUT: .D = %D.decl.loc5 // CHECK:STDOUT: .ReturnCUnused = %ReturnCUnused.decl // CHECK:STDOUT: .ReturnCUsed = %ReturnCUsed.decl // CHECK:STDOUT: .ReturnDUnused = %ReturnDUnused.decl // CHECK:STDOUT: .ReturnDUsed = %ReturnDUsed.decl // CHECK:STDOUT: .Call = %Call.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %C.decl: type = class_decl @C [concrete = constants.%C] {} {} // CHECK:STDOUT: %D.decl.loc5: type = class_decl @D [concrete = constants.%D] {} {} // CHECK:STDOUT: %ReturnCUnused.decl: %ReturnCUnused.type = fn_decl @ReturnCUnused [concrete = constants.%ReturnCUnused] { // CHECK:STDOUT: %return.patt: %pattern_type.7c7 = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: %pattern_type.7c7 = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %C.ref: type = name_ref C, file.%C.decl [concrete = constants.%C] // CHECK:STDOUT: %.loc7: Core.Form = init_form %C.ref [concrete = constants.%.a69] // CHECK:STDOUT: %return.param: ref %C = out_param call_param0 // CHECK:STDOUT: %return: ref %C = return_slot %return.param // CHECK:STDOUT: } // CHECK:STDOUT: %ReturnCUsed.decl: %ReturnCUsed.type = fn_decl @ReturnCUsed [concrete = constants.%ReturnCUsed] { // CHECK:STDOUT: %return.patt: %pattern_type.7c7 = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: %pattern_type.7c7 = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %C.ref: type = name_ref C, file.%C.decl [concrete = constants.%C] // CHECK:STDOUT: %.loc8: Core.Form = init_form %C.ref [concrete = constants.%.a69] // CHECK:STDOUT: %return.param: ref %C = out_param call_param0 // CHECK:STDOUT: %return: ref %C = return_slot %return.param // CHECK:STDOUT: } // CHECK:STDOUT: %ReturnDUnused.decl: %ReturnDUnused.type = fn_decl @ReturnDUnused [concrete = constants.%ReturnDUnused] { // CHECK:STDOUT: %return.patt: %pattern_type.9c8 = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: %pattern_type.9c8 = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %D.ref: type = name_ref D, file.%D.decl.loc5 [concrete = constants.%D] // CHECK:STDOUT: %.loc9: Core.Form = init_form %D.ref [concrete = constants.%.9a5] // CHECK:STDOUT: %return.param: ref %D = out_param call_param0 // CHECK:STDOUT: %return: ref %D = return_slot %return.param // CHECK:STDOUT: } // CHECK:STDOUT: %ReturnDUsed.decl: %ReturnDUsed.type = fn_decl @ReturnDUsed [concrete = constants.%ReturnDUsed] { // CHECK:STDOUT: %return.patt: %pattern_type.9c8 = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: %pattern_type.9c8 = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %D.ref: type = name_ref D, file.%D.decl.loc5 [concrete = constants.%D] // CHECK:STDOUT: %.loc10: Core.Form = init_form %D.ref [concrete = constants.%.9a5] // CHECK:STDOUT: %return.param: ref %D = out_param call_param0 // CHECK:STDOUT: %return: ref %D = return_slot %return.param // CHECK:STDOUT: } // CHECK:STDOUT: %Call.decl: %Call.type = fn_decl @Call [concrete = constants.%Call] {} {} // CHECK:STDOUT: %D.decl.loc37: type = class_decl @D [concrete = constants.%D] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @C; // CHECK:STDOUT: // CHECK:STDOUT: class @D { // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%D // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @ReturnCUnused() -> out %return.param: %C; // CHECK:STDOUT: // CHECK:STDOUT: fn @ReturnCUsed() -> out %return.param: %C; // CHECK:STDOUT: // CHECK:STDOUT: fn @ReturnDUnused() -> out %return.param: %D; // CHECK:STDOUT: // CHECK:STDOUT: fn @ReturnDUsed() -> out %return.param: %D; // CHECK:STDOUT: // CHECK:STDOUT: fn @Call() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %ReturnCUsed.ref: %ReturnCUsed.type = name_ref ReturnCUsed, file.%ReturnCUsed.decl [concrete = constants.%ReturnCUsed] // CHECK:STDOUT: %ReturnCUsed.call: init = call %ReturnCUsed.ref() // CHECK:STDOUT: %ReturnDUsed.ref: %ReturnDUsed.type = name_ref ReturnDUsed, file.%ReturnDUsed.decl [concrete = constants.%ReturnDUsed] // CHECK:STDOUT: %ReturnDUsed.call: init = call %ReturnDUsed.ref() // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- fail_use_imported.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %CallFAndGIncomplete.type: type = fn_type @CallFAndGIncomplete [concrete] // CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] // CHECK:STDOUT: %CallFAndGIncomplete: %CallFAndGIncomplete.type = struct_value () [concrete] // CHECK:STDOUT: %ReturnCUnused.type: type = fn_type @ReturnCUnused [concrete] // CHECK:STDOUT: %ReturnCUnused: %ReturnCUnused.type = struct_value () [concrete] // CHECK:STDOUT: %ReturnCUsed.type: type = fn_type @ReturnCUsed [concrete] // CHECK:STDOUT: %ReturnCUsed: %ReturnCUsed.type = struct_value () [concrete] // CHECK:STDOUT: %ReturnDUnused.type: type = fn_type @ReturnDUnused [concrete] // CHECK:STDOUT: %ReturnDUnused: %ReturnDUnused.type = struct_value () [concrete] // CHECK:STDOUT: %D: type = class_type @D [concrete] // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %complete_type: = complete_type_witness %empty_struct_type [concrete] // CHECK:STDOUT: %ReturnDUsed.type: type = fn_type @ReturnDUsed [concrete] // CHECK:STDOUT: %ReturnDUsed: %ReturnDUsed.type = struct_value () [concrete] // CHECK:STDOUT: %Destroy.type: type = facet_type <@Destroy> [concrete] // CHECK:STDOUT: %Destroy.Op.type: type = fn_type @Destroy.Op [concrete] // CHECK:STDOUT: %Destroy.Op: %Destroy.Op.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Main.C = import_ref Main//incomplete_return, C, unloaded // CHECK:STDOUT: %Main.D = import_ref Main//incomplete_return, D, unloaded // CHECK:STDOUT: %Main.ReturnCUnused: %ReturnCUnused.type = import_ref Main//incomplete_return, ReturnCUnused, loaded [concrete = constants.%ReturnCUnused] // CHECK:STDOUT: %Main.ReturnCUsed: %ReturnCUsed.type = import_ref Main//incomplete_return, ReturnCUsed, loaded [concrete = constants.%ReturnCUsed] // CHECK:STDOUT: %Main.ReturnDUnused: %ReturnDUnused.type = import_ref Main//incomplete_return, ReturnDUnused, loaded [concrete = constants.%ReturnDUnused] // CHECK:STDOUT: %Main.ReturnDUsed: %ReturnDUsed.type = import_ref Main//incomplete_return, ReturnDUsed, loaded [concrete = constants.%ReturnDUsed] // CHECK:STDOUT: %Main.Call = import_ref Main//incomplete_return, Call, unloaded // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: .Destroy = %Core.Destroy // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: %Main.import_ref.8f2: = import_ref Main//incomplete_return, loc37_10, loaded [concrete = constants.%complete_type] // CHECK:STDOUT: %Main.import_ref.1d5 = import_ref Main//incomplete_return, inst{{[0-9A-F]+}} [no loc], unloaded // CHECK:STDOUT: %Core.Destroy: type = import_ref Core//prelude/parts/destroy, Destroy, loaded [concrete = constants.%Destroy.type] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .C = imports.%Main.C // CHECK:STDOUT: .D = imports.%Main.D // CHECK:STDOUT: .ReturnCUnused = imports.%Main.ReturnCUnused // CHECK:STDOUT: .ReturnCUsed = imports.%Main.ReturnCUsed // CHECK:STDOUT: .ReturnDUnused = imports.%Main.ReturnDUnused // CHECK:STDOUT: .ReturnDUsed = imports.%Main.ReturnDUsed // CHECK:STDOUT: .Call = imports.%Main.Call // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .CallFAndGIncomplete = %CallFAndGIncomplete.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %default.import = import // CHECK:STDOUT: %CallFAndGIncomplete.decl: %CallFAndGIncomplete.type = fn_decl @CallFAndGIncomplete [concrete = constants.%CallFAndGIncomplete] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @C [from "fail_incomplete_return.carbon"]; // CHECK:STDOUT: // CHECK:STDOUT: class @D [from "fail_incomplete_return.carbon"] { // CHECK:STDOUT: complete_type_witness = imports.%Main.import_ref.8f2 // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = imports.%Main.import_ref.1d5 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @CallFAndGIncomplete() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %ReturnCUnused.ref: %ReturnCUnused.type = name_ref ReturnCUnused, imports.%Main.ReturnCUnused [concrete = constants.%ReturnCUnused] // CHECK:STDOUT: %ReturnCUnused.call: init = call %ReturnCUnused.ref() // CHECK:STDOUT: %ReturnCUsed.ref: %ReturnCUsed.type = name_ref ReturnCUsed, imports.%Main.ReturnCUsed [concrete = constants.%ReturnCUsed] // CHECK:STDOUT: %ReturnCUsed.call: init = call %ReturnCUsed.ref() // CHECK:STDOUT: %ReturnDUnused.ref: %ReturnDUnused.type = name_ref ReturnDUnused, imports.%Main.ReturnDUnused [concrete = constants.%ReturnDUnused] // CHECK:STDOUT: %.loc33_17.1: ref %D = temporary_storage // CHECK:STDOUT: %ReturnDUnused.call: init %D to %.loc33_17.1 = call %ReturnDUnused.ref() // CHECK:STDOUT: %.loc33_17.2: ref %D = temporary %.loc33_17.1, %ReturnDUnused.call // CHECK:STDOUT: %ReturnDUsed.ref: %ReturnDUsed.type = name_ref ReturnDUsed, imports.%Main.ReturnDUsed [concrete = constants.%ReturnDUsed] // CHECK:STDOUT: %.loc34_15.1: ref %D = temporary_storage // CHECK:STDOUT: %ReturnDUsed.call: init %D to %.loc34_15.1 = call %ReturnDUsed.ref() // CHECK:STDOUT: %.loc34_15.2: ref %D = temporary %.loc34_15.1, %ReturnDUsed.call // CHECK:STDOUT: %Destroy.Op.bound.loc34: = bound_method %.loc34_15.2, constants.%Destroy.Op // CHECK:STDOUT: %Destroy.Op.call.loc34: init %empty_tuple.type = call %Destroy.Op.bound.loc34(%.loc34_15.2) // CHECK:STDOUT: %Destroy.Op.bound.loc33: = bound_method %.loc33_17.2, constants.%Destroy.Op // CHECK:STDOUT: %Destroy.Op.call.loc33: init %empty_tuple.type = call %Destroy.Op.bound.loc33(%.loc33_17.2) // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @ReturnCUnused [from "fail_incomplete_return.carbon"]; // CHECK:STDOUT: // CHECK:STDOUT: fn @ReturnCUsed [from "fail_incomplete_return.carbon"]; // CHECK:STDOUT: // CHECK:STDOUT: fn @ReturnDUnused [from "fail_incomplete_return.carbon"]; // CHECK:STDOUT: // CHECK:STDOUT: fn @ReturnDUsed [from "fail_incomplete_return.carbon"]; // CHECK:STDOUT: // CHECK:STDOUT: fn @Destroy.Op(%self.param: ref %D) = "no_op"; // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/function/declaration/fail_modifiers.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/none.carbon // TODO: Add ranges and switch to "--dump-sem-ir-ranges=only". // EXTRA-ARGS: --dump-sem-ir-ranges=if-present // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/function/declaration/fail_modifiers.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/function/declaration/fail_modifiers.carbon // CHECK:STDERR: fail_modifiers.carbon:[[@LINE+11]]:9: error: `protected` must appear before `default` [ModifierMustAppearBefore] // CHECK:STDERR: default protected fn WrongOrder(); // CHECK:STDERR: ^~~~~~~~~ // CHECK:STDERR: fail_modifiers.carbon:[[@LINE+8]]:1: note: `default` previously appeared here [ModifierPrevious] // CHECK:STDERR: default protected fn WrongOrder(); // CHECK:STDERR: ^~~~~~~ // CHECK:STDERR: // CHECK:STDERR: fail_modifiers.carbon:[[@LINE+4]]:1: error: `default` not allowed; requires interface scope [ModifierRequiresInterface] // CHECK:STDERR: default protected fn WrongOrder(); // CHECK:STDERR: ^~~~~~~ // CHECK:STDERR: default protected fn WrongOrder(); // CHECK:STDERR: fail_modifiers.carbon:[[@LINE+11]]:9: error: `virtual` repeated on declaration [ModifierRepeated] // CHECK:STDERR: virtual virtual fn DuplicateVirtual() {} // CHECK:STDERR: ^~~~~~~ // CHECK:STDERR: fail_modifiers.carbon:[[@LINE+8]]:1: note: `virtual` previously appeared here [ModifierPrevious] // CHECK:STDERR: virtual virtual fn DuplicateVirtual() {} // CHECK:STDERR: ^~~~~~~ // CHECK:STDERR: // CHECK:STDERR: fail_modifiers.carbon:[[@LINE+4]]:1: error: `virtual` not allowed; requires class scope [ModifierRequiresClass] // CHECK:STDERR: virtual virtual fn DuplicateVirtual() {} // CHECK:STDERR: ^~~~~~~ // CHECK:STDERR: virtual virtual fn DuplicateVirtual() {} // CHECK:STDERR: fail_modifiers.carbon:[[@LINE+7]]:9: error: `protected` not allowed on declaration with `private` [ModifierNotAllowedWith] // CHECK:STDERR: private protected fn TwoAccess(); // CHECK:STDERR: ^~~~~~~~~ // CHECK:STDERR: fail_modifiers.carbon:[[@LINE+4]]:1: note: `private` previously appeared here [ModifierPrevious] // CHECK:STDERR: private protected fn TwoAccess(); // CHECK:STDERR: ^~~~~~~ // CHECK:STDERR: private protected fn TwoAccess(); // CHECK:STDERR: fail_modifiers.carbon:[[@LINE+11]]:10: error: `virtual` not allowed on declaration with `abstract` [ModifierNotAllowedWith] // CHECK:STDERR: abstract virtual fn ModifiersConflict() {} // CHECK:STDERR: ^~~~~~~ // CHECK:STDERR: fail_modifiers.carbon:[[@LINE+8]]:1: note: `abstract` previously appeared here [ModifierPrevious] // CHECK:STDERR: abstract virtual fn ModifiersConflict() {} // CHECK:STDERR: ^~~~~~~~ // CHECK:STDERR: // CHECK:STDERR: fail_modifiers.carbon:[[@LINE+4]]:1: error: `abstract` not allowed; requires class scope [ModifierRequiresClass] // CHECK:STDERR: abstract virtual fn ModifiersConflict() {} // CHECK:STDERR: ^~~~~~~~ // CHECK:STDERR: abstract virtual fn ModifiersConflict() {} // CHECK:STDERR: fail_modifiers.carbon:[[@LINE+4]]:1: error: `base` not allowed on `fn` declaration [ModifierNotAllowedOnDeclaration] // CHECK:STDERR: base fn InvalidModifier(); // CHECK:STDERR: ^~~~ // CHECK:STDERR: base fn InvalidModifier(); // CHECK:STDERR: fail_modifiers.carbon:[[@LINE+18]]:9: error: `final` not allowed on declaration with `default` [ModifierNotAllowedWith] // CHECK:STDERR: default final virtual fn ModifiersConflict2() {} // CHECK:STDERR: ^~~~~ // CHECK:STDERR: fail_modifiers.carbon:[[@LINE+15]]:1: note: `default` previously appeared here [ModifierPrevious] // CHECK:STDERR: default final virtual fn ModifiersConflict2() {} // CHECK:STDERR: ^~~~~~~ // CHECK:STDERR: // CHECK:STDERR: fail_modifiers.carbon:[[@LINE+11]]:15: error: `virtual` not allowed on declaration with `default` [ModifierNotAllowedWith] // CHECK:STDERR: default final virtual fn ModifiersConflict2() {} // CHECK:STDERR: ^~~~~~~ // CHECK:STDERR: fail_modifiers.carbon:[[@LINE+8]]:1: note: `default` previously appeared here [ModifierPrevious] // CHECK:STDERR: default final virtual fn ModifiersConflict2() {} // CHECK:STDERR: ^~~~~~~ // CHECK:STDERR: // CHECK:STDERR: fail_modifiers.carbon:[[@LINE+4]]:1: error: `default` not allowed; requires interface scope [ModifierRequiresInterface] // CHECK:STDERR: default final virtual fn ModifiersConflict2() {} // CHECK:STDERR: ^~~~~~~ // CHECK:STDERR: default final virtual fn ModifiersConflict2() {} // CHECK:STDERR: fail_modifiers.carbon:[[@LINE+7]]:8: error: `private` must appear before `extern` [ModifierMustAppearBefore] // CHECK:STDERR: extern private fn ExternOrderAndConflict() {} // CHECK:STDERR: ^~~~~~~ // CHECK:STDERR: fail_modifiers.carbon:[[@LINE+4]]:1: note: `extern` previously appeared here [ModifierPrevious] // CHECK:STDERR: extern private fn ExternOrderAndConflict() {} // CHECK:STDERR: ^~~~~~ // CHECK:STDERR: extern private fn ExternOrderAndConflict() {} // CHECK:STDOUT: --- fail_modifiers.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %WrongOrder.type: type = fn_type @WrongOrder [concrete] // CHECK:STDOUT: %WrongOrder: %WrongOrder.type = struct_value () [concrete] // CHECK:STDOUT: %DuplicateVirtual.type: type = fn_type @DuplicateVirtual [concrete] // CHECK:STDOUT: %DuplicateVirtual: %DuplicateVirtual.type = struct_value () [concrete] // CHECK:STDOUT: %TwoAccess.type: type = fn_type @TwoAccess [concrete] // CHECK:STDOUT: %TwoAccess: %TwoAccess.type = struct_value () [concrete] // CHECK:STDOUT: %ModifiersConflict.type: type = fn_type @ModifiersConflict [concrete] // CHECK:STDOUT: %ModifiersConflict: %ModifiersConflict.type = struct_value () [concrete] // CHECK:STDOUT: %InvalidModifier.type: type = fn_type @InvalidModifier [concrete] // CHECK:STDOUT: %InvalidModifier: %InvalidModifier.type = struct_value () [concrete] // CHECK:STDOUT: %ModifiersConflict2.type: type = fn_type @ModifiersConflict2 [concrete] // CHECK:STDOUT: %ModifiersConflict2: %ModifiersConflict2.type = struct_value () [concrete] // CHECK:STDOUT: %ExternOrderAndConflict.type: type = fn_type @ExternOrderAndConflict [concrete] // CHECK:STDOUT: %ExternOrderAndConflict: %ExternOrderAndConflict.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .WrongOrder = %WrongOrder.decl // CHECK:STDOUT: .DuplicateVirtual = %DuplicateVirtual.decl // CHECK:STDOUT: .TwoAccess [private] = %TwoAccess.decl // CHECK:STDOUT: .ModifiersConflict = %ModifiersConflict.decl // CHECK:STDOUT: .InvalidModifier = %InvalidModifier.decl // CHECK:STDOUT: .ModifiersConflict2 = %ModifiersConflict2.decl // CHECK:STDOUT: .ExternOrderAndConflict = %ExternOrderAndConflict.decl // CHECK:STDOUT: } // CHECK:STDOUT: %WrongOrder.decl: %WrongOrder.type = fn_decl @WrongOrder [concrete = constants.%WrongOrder] {} {} // CHECK:STDOUT: %DuplicateVirtual.decl: %DuplicateVirtual.type = fn_decl @DuplicateVirtual [concrete = constants.%DuplicateVirtual] {} {} // CHECK:STDOUT: %TwoAccess.decl: %TwoAccess.type = fn_decl @TwoAccess [concrete = constants.%TwoAccess] {} {} // CHECK:STDOUT: %ModifiersConflict.decl: %ModifiersConflict.type = fn_decl @ModifiersConflict [concrete = constants.%ModifiersConflict] {} {} // CHECK:STDOUT: %InvalidModifier.decl: %InvalidModifier.type = fn_decl @InvalidModifier [concrete = constants.%InvalidModifier] {} {} // CHECK:STDOUT: %ModifiersConflict2.decl: %ModifiersConflict2.type = fn_decl @ModifiersConflict2 [concrete = constants.%ModifiersConflict2] {} {} // CHECK:STDOUT: %ExternOrderAndConflict.decl: %ExternOrderAndConflict.type = fn_decl @ExternOrderAndConflict [concrete = constants.%ExternOrderAndConflict] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @WrongOrder(); // CHECK:STDOUT: // CHECK:STDOUT: fn @DuplicateVirtual() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @TwoAccess(); // CHECK:STDOUT: // CHECK:STDOUT: fn @ModifiersConflict() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @InvalidModifier(); // CHECK:STDOUT: // CHECK:STDOUT: fn @ModifiersConflict2() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: extern fn @ExternOrderAndConflict() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/function/declaration/fail_param_in_type.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/int.carbon // TODO: Add ranges and switch to "--dump-sem-ir-ranges=only". // EXTRA-ARGS: --dump-sem-ir-ranges=if-present // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/function/declaration/fail_param_in_type.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/function/declaration/fail_param_in_type.carbon // CHECK:STDERR: fail_param_in_type.carbon:[[@LINE+4]]:28: error: array bound is not a constant [InvalidArrayExpr] // CHECK:STDERR: fn F(n: i32, a: array(i32, n)*); // CHECK:STDERR: ^ // CHECK:STDERR: fn F(n: i32, a: array(i32, n)*); // CHECK:STDOUT: --- fail_param_in_type.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [concrete] // CHECK:STDOUT: %Int.type: type = generic_class_type @Int [concrete] // CHECK:STDOUT: %Int.generic: %Int.type = struct_value () [concrete] // CHECK:STDOUT: %i32: type = class_type @Int, @Int(%int_32) [concrete] // CHECK:STDOUT: %pattern_type.7ce: type = pattern_type %i32 [concrete] // CHECK:STDOUT: %F.type: type = fn_type @F [concrete] // CHECK:STDOUT: %F: %F.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: .Int = %Core.Int // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: %Core.Int: %Int.type = import_ref Core//prelude/parts/int, Int, loaded [concrete = constants.%Int.generic] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .F = %F.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %F.decl: %F.type = fn_decl @F [concrete = constants.%F] { // CHECK:STDOUT: %n.patt: %pattern_type.7ce = value_binding_pattern n [concrete] // CHECK:STDOUT: %n.param_patt: %pattern_type.7ce = value_param_pattern %n.patt [concrete] // CHECK:STDOUT: %a.patt: = value_binding_pattern a [concrete] // CHECK:STDOUT: %a.param_patt: = value_param_pattern %a.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %n.param: %i32 = value_param call_param0 // CHECK:STDOUT: %i32.loc19_9: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %n: %i32 = value_binding n, %n.param // CHECK:STDOUT: %a.param: = value_param call_param1 // CHECK:STDOUT: %.loc19: type = splice_block %ptr [concrete = ] { // CHECK:STDOUT: %i32.loc19_23: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %n.ref: %i32 = name_ref n, %n // CHECK:STDOUT: %ptr: type = ptr_type [concrete = ] // CHECK:STDOUT: } // CHECK:STDOUT: %a: = value_binding a, %a.param // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @F(%n.param: %i32, %a.param: ); // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/function/declaration/fail_param_redecl.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/int.carbon // TODO: Add ranges and switch to "--dump-sem-ir-ranges=only". // EXTRA-ARGS: --dump-sem-ir-ranges=if-present // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/function/declaration/fail_param_redecl.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/function/declaration/fail_param_redecl.carbon // CHECK:STDERR: fail_param_redecl.carbon:[[@LINE+7]]:14: error: duplicate name `n` being declared in the same scope [NameDeclDuplicate] // CHECK:STDERR: fn F(n: i32, n: i32); // CHECK:STDERR: ^ // CHECK:STDERR: fail_param_redecl.carbon:[[@LINE+4]]:6: note: name is previously declared here [NameDeclPrevious] // CHECK:STDERR: fn F(n: i32, n: i32); // CHECK:STDERR: ^ // CHECK:STDERR: fn F(n: i32, n: i32); // CHECK:STDOUT: --- fail_param_redecl.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [concrete] // CHECK:STDOUT: %Int.type: type = generic_class_type @Int [concrete] // CHECK:STDOUT: %Int.generic: %Int.type = struct_value () [concrete] // CHECK:STDOUT: %i32: type = class_type @Int, @Int(%int_32) [concrete] // CHECK:STDOUT: %pattern_type.7ce: type = pattern_type %i32 [concrete] // CHECK:STDOUT: %F.type: type = fn_type @F [concrete] // CHECK:STDOUT: %F: %F.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: .Int = %Core.Int // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: %Core.Int: %Int.type = import_ref Core//prelude/parts/int, Int, loaded [concrete = constants.%Int.generic] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .F = %F.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %F.decl: %F.type = fn_decl @F [concrete = constants.%F] { // CHECK:STDOUT: %n.patt.loc22_6: %pattern_type.7ce = value_binding_pattern n [concrete] // CHECK:STDOUT: %n.param_patt.loc22_7: %pattern_type.7ce = value_param_pattern %n.patt.loc22_6 [concrete] // CHECK:STDOUT: %n.patt.loc22_14: %pattern_type.7ce = value_binding_pattern n [concrete] // CHECK:STDOUT: %n.param_patt.loc22_15: %pattern_type.7ce = value_param_pattern %n.patt.loc22_14 [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %n.param.loc22_7: %i32 = value_param call_param0 // CHECK:STDOUT: %i32.loc22_9: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %n.loc22_6: %i32 = value_binding n, %n.param.loc22_7 // CHECK:STDOUT: %n.param.loc22_15: %i32 = value_param call_param1 // CHECK:STDOUT: %i32.loc22_17: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %n.loc22_14: %i32 = value_binding n, %n.param.loc22_15 // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @F(%n.param.loc22_7: %i32, %n.param.loc22_15: %i32); // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/function/declaration/fail_redecl.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/none.carbon // TODO: Add ranges and switch to "--dump-sem-ir-ranges=only". // EXTRA-ARGS: --dump-sem-ir-ranges=if-present // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/function/declaration/fail_redecl.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/function/declaration/fail_redecl.carbon fn A(); // CHECK:STDERR: fail_redecl.carbon:[[@LINE+7]]:1: error: redeclaration of `fn A` is redundant [RedeclRedundant] // CHECK:STDERR: fn A(); // CHECK:STDERR: ^~~~~~~ // CHECK:STDERR: fail_redecl.carbon:[[@LINE-4]]:1: note: previously declared here [RedeclPrevDecl] // CHECK:STDERR: fn A(); // CHECK:STDERR: ^~~~~~~ // CHECK:STDERR: fn A(); fn B(x: ()); // CHECK:STDERR: fail_redecl.carbon:[[@LINE+7]]:1: error: redeclaration of `fn B` is redundant [RedeclRedundant] // CHECK:STDERR: fn B(x: ()); // CHECK:STDERR: ^~~~~~~~~~~~ // CHECK:STDERR: fail_redecl.carbon:[[@LINE-4]]:1: note: previously declared here [RedeclPrevDecl] // CHECK:STDERR: fn B(x: ()); // CHECK:STDERR: ^~~~~~~~~~~~ // CHECK:STDERR: fn B(x: ()); fn C(); // CHECK:STDERR: fail_redecl.carbon:[[@LINE+7]]:1: error: redeclaration differs because of parameter count of 1 [RedeclParamCountDiffers] // CHECK:STDERR: fn C(x: ()); // CHECK:STDERR: ^~~~~~~~~~~~ // CHECK:STDERR: fail_redecl.carbon:[[@LINE-4]]:1: note: previously declared with parameter count of 0 [RedeclParamCountPrevious] // CHECK:STDERR: fn C(); // CHECK:STDERR: ^~~~~~~ // CHECK:STDERR: fn C(x: ()); fn D() {} // CHECK:STDERR: fail_redecl.carbon:[[@LINE+7]]:1: error: redeclaration of `fn D` is redundant [RedeclRedundant] // CHECK:STDERR: fn D(); // CHECK:STDERR: ^~~~~~~ // CHECK:STDERR: fail_redecl.carbon:[[@LINE-4]]:1: note: previously declared here [RedeclPrevDecl] // CHECK:STDERR: fn D() {} // CHECK:STDERR: ^~~~~~~~ // CHECK:STDERR: fn D(); fn E() {} // CHECK:STDERR: fail_redecl.carbon:[[@LINE+7]]:1: error: redefinition of `fn E` [RedeclRedef] // CHECK:STDERR: fn E() {} // CHECK:STDERR: ^~~~~~~~ // CHECK:STDERR: fail_redecl.carbon:[[@LINE-4]]:1: note: previously defined here [RedeclPrevDef] // CHECK:STDERR: fn E() {} // CHECK:STDERR: ^~~~~~~~ // CHECK:STDERR: fn E() {} // CHECK:STDOUT: --- fail_redecl.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %A.type: type = fn_type @A [concrete] // CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] // CHECK:STDOUT: %A: %A.type = struct_value () [concrete] // CHECK:STDOUT: %empty_tuple: %empty_tuple.type = tuple_value () [concrete] // CHECK:STDOUT: %pattern_type: type = pattern_type %empty_tuple.type [concrete] // CHECK:STDOUT: %B.type: type = fn_type @B [concrete] // CHECK:STDOUT: %B: %B.type = struct_value () [concrete] // CHECK:STDOUT: %C.type.0036b9.1: type = fn_type @C.loc35 [concrete] // CHECK:STDOUT: %C.6a91b4.1: %C.type.0036b9.1 = struct_value () [concrete] // CHECK:STDOUT: %C.type.0036b9.2: type = fn_type @C.loc43 [concrete] // CHECK:STDOUT: %C.6a91b4.2: %C.type.0036b9.2 = struct_value () [concrete] // CHECK:STDOUT: %D.type: type = fn_type @D [concrete] // CHECK:STDOUT: %D: %D.type = struct_value () [concrete] // CHECK:STDOUT: %E.type.e738e1.1: type = fn_type @E.loc55 [concrete] // CHECK:STDOUT: %E.d326aa.1: %E.type.e738e1.1 = struct_value () [concrete] // CHECK:STDOUT: %E.type.e738e1.2: type = fn_type @E.loc63 [concrete] // CHECK:STDOUT: %E.d326aa.2: %E.type.e738e1.2 = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .A = %A.decl.loc15 // CHECK:STDOUT: .B = %B.decl.loc25 // CHECK:STDOUT: .C = %C.decl.loc35 // CHECK:STDOUT: .D = %D.decl.loc45 // CHECK:STDOUT: .E = %E.decl.loc55 // CHECK:STDOUT: } // CHECK:STDOUT: %A.decl.loc15: %A.type = fn_decl @A [concrete = constants.%A] {} {} // CHECK:STDOUT: %A.decl.loc23: %A.type = fn_decl @A [concrete = constants.%A] {} {} // CHECK:STDOUT: %B.decl.loc25: %B.type = fn_decl @B [concrete = constants.%B] { // CHECK:STDOUT: %x.patt: %pattern_type = value_binding_pattern x [concrete] // CHECK:STDOUT: %x.param_patt: %pattern_type = value_param_pattern %x.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %x.param.loc25: %empty_tuple.type = value_param call_param0 // CHECK:STDOUT: %.loc25_10.1: type = splice_block %.loc25_10.3 [concrete = constants.%empty_tuple.type] { // CHECK:STDOUT: %.loc25_10.2: %empty_tuple.type = tuple_literal () [concrete = constants.%empty_tuple] // CHECK:STDOUT: %.loc25_10.3: type = converted %.loc25_10.2, constants.%empty_tuple.type [concrete = constants.%empty_tuple.type] // CHECK:STDOUT: } // CHECK:STDOUT: %x.loc25: %empty_tuple.type = value_binding x, %x.param.loc25 // CHECK:STDOUT: } // CHECK:STDOUT: %B.decl.loc33: %B.type = fn_decl @B [concrete = constants.%B] { // CHECK:STDOUT: %x.patt: %pattern_type = value_binding_pattern x [concrete] // CHECK:STDOUT: %x.param_patt: %pattern_type = value_param_pattern %x.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %x.param.loc33: %empty_tuple.type = value_param call_param0 // CHECK:STDOUT: %.loc33_10.1: type = splice_block %.loc33_10.3 [concrete = constants.%empty_tuple.type] { // CHECK:STDOUT: %.loc33_10.2: %empty_tuple.type = tuple_literal () [concrete = constants.%empty_tuple] // CHECK:STDOUT: %.loc33_10.3: type = converted %.loc33_10.2, constants.%empty_tuple.type [concrete = constants.%empty_tuple.type] // CHECK:STDOUT: } // CHECK:STDOUT: %x.loc33: %empty_tuple.type = value_binding x, %x.param.loc33 // CHECK:STDOUT: } // CHECK:STDOUT: %C.decl.loc35: %C.type.0036b9.1 = fn_decl @C.loc35 [concrete = constants.%C.6a91b4.1] {} {} // CHECK:STDOUT: %C.decl.loc43: %C.type.0036b9.2 = fn_decl @C.loc43 [concrete = constants.%C.6a91b4.2] { // CHECK:STDOUT: %x.patt: %pattern_type = value_binding_pattern x [concrete] // CHECK:STDOUT: %x.param_patt: %pattern_type = value_param_pattern %x.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %x.param: %empty_tuple.type = value_param call_param0 // CHECK:STDOUT: %.loc43_10.1: type = splice_block %.loc43_10.3 [concrete = constants.%empty_tuple.type] { // CHECK:STDOUT: %.loc43_10.2: %empty_tuple.type = tuple_literal () [concrete = constants.%empty_tuple] // CHECK:STDOUT: %.loc43_10.3: type = converted %.loc43_10.2, constants.%empty_tuple.type [concrete = constants.%empty_tuple.type] // CHECK:STDOUT: } // CHECK:STDOUT: %x: %empty_tuple.type = value_binding x, %x.param // CHECK:STDOUT: } // CHECK:STDOUT: %D.decl.loc45: %D.type = fn_decl @D [concrete = constants.%D] {} {} // CHECK:STDOUT: %D.decl.loc53: %D.type = fn_decl @D [concrete = constants.%D] {} {} // CHECK:STDOUT: %E.decl.loc55: %E.type.e738e1.1 = fn_decl @E.loc55 [concrete = constants.%E.d326aa.1] {} {} // CHECK:STDOUT: %E.decl.loc63: %E.type.e738e1.2 = fn_decl @E.loc63 [concrete = constants.%E.d326aa.2] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @A(); // CHECK:STDOUT: // CHECK:STDOUT: fn @B(%x.param.loc25: %empty_tuple.type); // CHECK:STDOUT: // CHECK:STDOUT: fn @C.loc35(); // CHECK:STDOUT: // CHECK:STDOUT: fn @C.loc43(%x.param: %empty_tuple.type); // CHECK:STDOUT: // CHECK:STDOUT: fn @D() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @E.loc55() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @E.loc63() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/function/declaration/fail_todo_no_params.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/none.carbon // TODO: Add ranges and switch to "--dump-sem-ir-ranges=only". // EXTRA-ARGS: --dump-sem-ir-ranges=if-present // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/function/declaration/fail_todo_no_params.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/function/declaration/fail_todo_no_params.carbon // --- fail_no_body.carbon library "[[@TEST_NAME]]"; // CHECK:STDERR: fail_no_body.carbon:[[@LINE+4]]:1: error: semantics TODO: `function with positional parameters` [SemanticsTodo] // CHECK:STDERR: fn A; // CHECK:STDERR: ^~~~~ // CHECK:STDERR: fn A; // --- fail_todo_brace_body.carbon library "[[@TEST_NAME]]"; // CHECK:STDERR: fail_todo_brace_body.carbon:[[@LINE+4]]:1: error: semantics TODO: `function with positional parameters` [SemanticsTodo] // CHECK:STDERR: fn A { // CHECK:STDERR: ^~~~~~ // CHECK:STDERR: fn A { } // --- fail_todo_return_type.carbon library "[[@TEST_NAME]]"; // CHECK:STDERR: fail_todo_return_type.carbon:[[@LINE+4]]:1: error: semantics TODO: `function with positional parameters` [SemanticsTodo] // CHECK:STDERR: fn A -> (); // CHECK:STDERR: ^~~~~~~~~~~ // CHECK:STDERR: fn A -> (); // --- fail_todo_implicit_only.carbon library "[[@TEST_NAME]]"; // CHECK:STDERR: fail_todo_implicit_only.carbon:[[@LINE+4]]:1: error: semantics TODO: `function with positional parameters` [SemanticsTodo] // CHECK:STDERR: fn A[] -> (); // CHECK:STDERR: ^~~~~~~~~~~~~ // CHECK:STDERR: fn A[] -> (); // --- fail_todo_arrow_body.carbon // TODO: We don't have parsing support for this yet. library "[[@TEST_NAME]]"; // CHECK:STDERR: fail_todo_arrow_body.carbon:[[@LINE+8]]:1: error: semantics TODO: `function with positional parameters` [SemanticsTodo] // CHECK:STDERR: fn A => 0; // CHECK:STDERR: ^~~~~~~ // CHECK:STDERR: // CHECK:STDERR: fail_todo_arrow_body.carbon:[[@LINE+4]]:6: error: semantics TODO: `HandleTerseBodyArrow` [SemanticsTodo] // CHECK:STDERR: fn A => 0; // CHECK:STDERR: ^~ // CHECK:STDERR: fn A => 0; // --- fail_invalid_file_generic_regression_test.carbon library "[[@TEST_NAME]]"; // CHECK:STDERR: fail_invalid_file_generic_regression_test.carbon:[[@LINE+8]]:5: error: semantics TODO: `handle invalid parse trees in `check`` [SemanticsTodo] // CHECK:STDERR: var x:! () = (); // CHECK:STDERR: ^~~~~~ // CHECK:STDERR: // CHECK:STDERR: fail_invalid_file_generic_regression_test.carbon:[[@LINE+4]]:12: error: found `:!` pattern inside `var` pattern [NonRegularBindingInVarDecl] // CHECK:STDERR: var x:! () = (); // CHECK:STDERR: ^ // CHECK:STDERR: var x:! () = (); fn A { A(); } // CHECK:STDOUT: --- fail_no_body.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %A.type: type = fn_type @A [concrete] // CHECK:STDOUT: %A: %A.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .A = %A.decl // CHECK:STDOUT: } // CHECK:STDOUT: %A.decl: %A.type = fn_decl @A [concrete = constants.%A] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @A; // CHECK:STDOUT: // CHECK:STDOUT: --- fail_todo_brace_body.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %A.type: type = fn_type @A [concrete] // CHECK:STDOUT: %A: %A.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .A = %A.decl // CHECK:STDOUT: } // CHECK:STDOUT: %A.decl: %A.type = fn_decl @A [concrete = constants.%A] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @A { // CHECK:STDOUT: !entry: // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- fail_todo_return_type.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] // CHECK:STDOUT: %empty_tuple: %empty_tuple.type = tuple_value () [concrete] // CHECK:STDOUT: %.262: Core.Form = init_form %empty_tuple.type [concrete] // CHECK:STDOUT: %pattern_type: type = pattern_type %empty_tuple.type [concrete] // CHECK:STDOUT: %A.type: type = fn_type @A [concrete] // CHECK:STDOUT: %A: %A.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .A = %A.decl // CHECK:STDOUT: } // CHECK:STDOUT: %A.decl: %A.type = fn_decl @A [concrete = constants.%A] { // CHECK:STDOUT: %return.patt: %pattern_type = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: %pattern_type = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %.loc7_10.1: %empty_tuple.type = tuple_literal () [concrete = constants.%empty_tuple] // CHECK:STDOUT: %.loc7_10.2: type = converted %.loc7_10.1, constants.%empty_tuple.type [concrete = constants.%empty_tuple.type] // CHECK:STDOUT: %.loc7_10.3: Core.Form = init_form %.loc7_10.2 [concrete = constants.%.262] // CHECK:STDOUT: %return.param: ref %empty_tuple.type = out_param call_param0 // CHECK:STDOUT: %return: ref %empty_tuple.type = return_slot %return.param // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @A() -> out %return.param: %empty_tuple.type; // CHECK:STDOUT: // CHECK:STDOUT: --- fail_todo_implicit_only.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] // CHECK:STDOUT: %empty_tuple: %empty_tuple.type = tuple_value () [concrete] // CHECK:STDOUT: %.262: Core.Form = init_form %empty_tuple.type [concrete] // CHECK:STDOUT: %pattern_type: type = pattern_type %empty_tuple.type [concrete] // CHECK:STDOUT: %A.type: type = fn_type @A [concrete] // CHECK:STDOUT: %A: %A.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .A = %A.decl // CHECK:STDOUT: } // CHECK:STDOUT: %A.decl: %A.type = fn_decl @A [concrete = constants.%A] { // CHECK:STDOUT: %return.patt: %pattern_type = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: %pattern_type = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %.loc7_12.1: %empty_tuple.type = tuple_literal () [concrete = constants.%empty_tuple] // CHECK:STDOUT: %.loc7_12.2: type = converted %.loc7_12.1, constants.%empty_tuple.type [concrete = constants.%empty_tuple.type] // CHECK:STDOUT: %.loc7_12.3: Core.Form = init_form %.loc7_12.2 [concrete = constants.%.262] // CHECK:STDOUT: %return.param: ref %empty_tuple.type = out_param call_param0 // CHECK:STDOUT: %return: ref %empty_tuple.type = return_slot %return.param // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @A() -> out %return.param: %empty_tuple.type; // CHECK:STDOUT: // CHECK:STDOUT: --- fail_todo_arrow_body.carbon // CHECK:STDOUT: // CHECK:STDOUT: fn @A; // CHECK:STDOUT: // CHECK:STDOUT: --- fail_invalid_file_generic_regression_test.carbon // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/function/declaration/implicit_import.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/none.carbon // TODO: Add ranges and switch to "--dump-sem-ir-ranges=only". // EXTRA-ARGS: --dump-sem-ir-ranges=if-present // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/function/declaration/implicit_import.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/function/declaration/implicit_import.carbon // --- basic.carbon library "[[@TEST_NAME]]"; fn A(); // --- basic.impl.carbon impl library "[[@TEST_NAME]]"; fn A() {} // --- extern_api.carbon library "[[@TEST_NAME]]"; extern fn A(); // --- fail_extern_api.impl.carbon impl library "[[@TEST_NAME]]"; // CHECK:STDERR: fail_extern_api.impl.carbon:[[@LINE+12]]:1: error: redeclarations of `fn A` must match use of `extern` [RedeclExternMismatch] // CHECK:STDERR: fn A(); // CHECK:STDERR: ^~~~~~~ // CHECK:STDERR: fail_extern_api.impl.carbon:[[@LINE-5]]:1: in import [InImport] // CHECK:STDERR: extern_api.carbon:4:1: note: previously declared here [RedeclPrevDecl] // CHECK:STDERR: extern fn A(); // CHECK:STDERR: ^~~~~~~~~~~~~~ // CHECK:STDERR: // CHECK:STDERR: fail_extern_api.impl.carbon:[[@LINE+4]]:1: error: no definition found for declaration in impl file [MissingDefinitionInImpl] // CHECK:STDERR: fn A(); // CHECK:STDERR: ^~~~~~~ // CHECK:STDERR: fn A(); // --- extern_impl.carbon library "[[@TEST_NAME]]"; fn A(); // --- fail_extern_impl.impl.carbon impl library "[[@TEST_NAME]]"; // CHECK:STDERR: fail_extern_impl.impl.carbon:[[@LINE+8]]:1: error: redeclarations of `fn A` must match use of `extern` [RedeclExternMismatch] // CHECK:STDERR: extern fn A(); // CHECK:STDERR: ^~~~~~~~~~~~~~ // CHECK:STDERR: fail_extern_impl.impl.carbon:[[@LINE-5]]:1: in import [InImport] // CHECK:STDERR: extern_impl.carbon:4:1: note: previously declared here [RedeclPrevDecl] // CHECK:STDERR: fn A(); // CHECK:STDERR: ^~~~~~~ // CHECK:STDERR: extern fn A(); // CHECK:STDOUT: --- basic.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %A.type: type = fn_type @A [concrete] // CHECK:STDOUT: %A: %A.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .A = %A.decl // CHECK:STDOUT: } // CHECK:STDOUT: %A.decl: %A.type = fn_decl @A [concrete = constants.%A] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @A(); // CHECK:STDOUT: // CHECK:STDOUT: --- basic.impl.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %A.type: type = fn_type @A [concrete] // CHECK:STDOUT: %A: %A.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .A = %A.decl // CHECK:STDOUT: } // CHECK:STDOUT: %default.import.loc2_21.1 = import // CHECK:STDOUT: %default.import.loc2_21.2 = import // CHECK:STDOUT: %A.decl: %A.type = fn_decl @A [concrete = constants.%A] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @A() [from "basic.carbon"] { // CHECK:STDOUT: !entry: // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- extern_api.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %A.type: type = fn_type @A [concrete] // CHECK:STDOUT: %A: %A.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .A = %A.decl // CHECK:STDOUT: } // CHECK:STDOUT: %A.decl: %A.type = fn_decl @A [concrete = constants.%A] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: extern fn @A(); // CHECK:STDOUT: // CHECK:STDOUT: --- fail_extern_api.impl.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %A.type: type = fn_type @A [concrete] // CHECK:STDOUT: %A: %A.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .A = %A.decl // CHECK:STDOUT: } // CHECK:STDOUT: %default.import.loc2_26.1 = import // CHECK:STDOUT: %default.import.loc2_26.2 = import // CHECK:STDOUT: %A.decl: %A.type = fn_decl @A [concrete = constants.%A] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: extern fn @A [from "extern_api.carbon"]; // CHECK:STDOUT: // CHECK:STDOUT: --- extern_impl.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %A.type: type = fn_type @A [concrete] // CHECK:STDOUT: %A: %A.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .A = %A.decl // CHECK:STDOUT: } // CHECK:STDOUT: %A.decl: %A.type = fn_decl @A [concrete = constants.%A] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @A(); // CHECK:STDOUT: // CHECK:STDOUT: --- fail_extern_impl.impl.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %A.type: type = fn_type @A [concrete] // CHECK:STDOUT: %A: %A.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .A = %A.decl // CHECK:STDOUT: } // CHECK:STDOUT: %default.import.loc2_27.1 = import // CHECK:STDOUT: %default.import.loc2_27.2 = import // CHECK:STDOUT: %A.decl: %A.type = fn_decl @A [concrete = constants.%A] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @A [from "extern_impl.carbon"]; // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/function/declaration/import.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/int.carbon // TODO: Add ranges and switch to "--dump-sem-ir-ranges=only". // EXTRA-ARGS: --dump-sem-ir-ranges=if-present // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/function/declaration/import.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/function/declaration/import.carbon // ============================================================================ // Setup files // ============================================================================ // --- api.carbon library "[[@TEST_NAME]]"; fn A(); fn B(b: i32) -> i32; fn C(c: (i32,)) -> {.c: i32}; extern fn D(); namespace NS; fn NS.E(); // --- extern_api.carbon library "[[@TEST_NAME]]"; extern library "redecl_extern_api" fn A(); extern library "redecl_extern_api" fn B(b: i32) -> i32; extern library "redecl_extern_api" fn C(c: (i32,)) -> {.c: i32}; extern library "redecl_extern_api" fn D(); namespace NS; extern library "redecl_extern_api" fn NS.E(); // ============================================================================ // Test files // ============================================================================ // --- basics.carbon library "[[@TEST_NAME]]"; import library "api"; var a: () = A(); var b: i32 = B(1); var c: {.c: i32} = C((1,)); var d: () = D(); var e: () = NS.E(); // --- fail_redecl_api.carbon library "[[@TEST_NAME]]"; import library "api"; // CHECK:STDERR: fail_redecl_api.carbon:[[@LINE+8]]:1: error: redeclarations of `fn A` must match use of `extern` [RedeclExternMismatch] // CHECK:STDERR: extern fn A(); // CHECK:STDERR: ^~~~~~~~~~~~~~ // CHECK:STDERR: fail_redecl_api.carbon:[[@LINE-5]]:1: in import [InImport] // CHECK:STDERR: api.carbon:4:1: note: previously declared here [RedeclPrevDecl] // CHECK:STDERR: fn A(); // CHECK:STDERR: ^~~~~~~ // CHECK:STDERR: extern fn A(); // CHECK:STDERR: fail_redecl_api.carbon:[[@LINE+8]]:1: error: redeclarations of `fn B` must match use of `extern` [RedeclExternMismatch] // CHECK:STDERR: extern fn B(b: i32) -> i32; // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: fail_redecl_api.carbon:[[@LINE-14]]:1: in import [InImport] // CHECK:STDERR: api.carbon:5:1: note: previously declared here [RedeclPrevDecl] // CHECK:STDERR: fn B(b: i32) -> i32; // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: extern fn B(b: i32) -> i32; // CHECK:STDERR: fail_redecl_api.carbon:[[@LINE+8]]:1: error: redeclarations of `fn C` must match use of `extern` [RedeclExternMismatch] // CHECK:STDERR: extern fn C(c: (i32,)) -> {.c: i32}; // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: fail_redecl_api.carbon:[[@LINE-23]]:1: in import [InImport] // CHECK:STDERR: api.carbon:6:1: note: previously declared here [RedeclPrevDecl] // CHECK:STDERR: fn C(c: (i32,)) -> {.c: i32}; // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: extern fn C(c: (i32,)) -> {.c: i32}; // CHECK:STDERR: fail_redecl_api.carbon:[[@LINE+8]]:1: error: redeclaration of `fn D` is redundant [RedeclRedundant] // CHECK:STDERR: extern fn D(); // CHECK:STDERR: ^~~~~~~~~~~~~~ // CHECK:STDERR: fail_redecl_api.carbon:[[@LINE-32]]:1: in import [InImport] // CHECK:STDERR: api.carbon:7:1: note: previously declared here [RedeclPrevDecl] // CHECK:STDERR: extern fn D(); // CHECK:STDERR: ^~~~~~~~~~~~~~ // CHECK:STDERR: extern fn D(); // CHECK:STDERR: fail_redecl_api.carbon:[[@LINE+8]]:1: error: redeclarations of `fn E` must match use of `extern` [RedeclExternMismatch] // CHECK:STDERR: extern fn NS.E(); // CHECK:STDERR: ^~~~~~~~~~~~~~~~~ // CHECK:STDERR: fail_redecl_api.carbon:[[@LINE-41]]:1: in import [InImport] // CHECK:STDERR: api.carbon:10:1: note: previously declared here [RedeclPrevDecl] // CHECK:STDERR: fn NS.E(); // CHECK:STDERR: ^~~~~~~~~~ // CHECK:STDERR: extern fn NS.E(); var a: () = A(); var b: i32 = B(1); var c: {.c: i32} = C((1,)); var d: () = D(); var e: () = NS.E(); // --- redecl_extern_api.carbon library "[[@TEST_NAME]]"; import library "extern_api"; extern fn A(); extern fn B(b: i32) -> i32; extern fn C(c: (i32,)) -> {.c: i32}; extern fn D(); extern fn NS.E(); var a: () = A(); var b: i32 = B(1); var c: {.c: i32} = C((1,)); var d: () = D(); var e: () = NS.E(); // --- fail_merge.carbon library "[[@TEST_NAME]]"; // CHECK:STDERR: fail_merge.carbon:[[@LINE+45]]:1: in import [InImport] // CHECK:STDERR: extern_api.carbon:4:1: error: duplicate name `A` being declared in the same scope [NameDeclDuplicate] // CHECK:STDERR: extern library "redecl_extern_api" fn A(); // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: fail_merge.carbon:[[@LINE+41]]:1: in import [InImport] // CHECK:STDERR: api.carbon:4:1: note: name is previously declared here [NameDeclPrevious] // CHECK:STDERR: fn A(); // CHECK:STDERR: ^~~~~~~ // CHECK:STDERR: // CHECK:STDERR: fail_merge.carbon:[[@LINE+36]]:1: in import [InImport] // CHECK:STDERR: extern_api.carbon:5:1: error: duplicate name `B` being declared in the same scope [NameDeclDuplicate] // CHECK:STDERR: extern library "redecl_extern_api" fn B(b: i32) -> i32; // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: fail_merge.carbon:[[@LINE+32]]:1: in import [InImport] // CHECK:STDERR: api.carbon:5:1: note: name is previously declared here [NameDeclPrevious] // CHECK:STDERR: fn B(b: i32) -> i32; // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: // CHECK:STDERR: fail_merge.carbon:[[@LINE+27]]:1: in import [InImport] // CHECK:STDERR: extern_api.carbon:6:1: error: duplicate name `C` being declared in the same scope [NameDeclDuplicate] // CHECK:STDERR: extern library "redecl_extern_api" fn C(c: (i32,)) -> {.c: i32}; // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: fail_merge.carbon:[[@LINE+23]]:1: in import [InImport] // CHECK:STDERR: api.carbon:6:1: note: name is previously declared here [NameDeclPrevious] // CHECK:STDERR: fn C(c: (i32,)) -> {.c: i32}; // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: // CHECK:STDERR: fail_merge.carbon:[[@LINE+18]]:1: in import [InImport] // CHECK:STDERR: extern_api.carbon:7:1: error: duplicate name `D` being declared in the same scope [NameDeclDuplicate] // CHECK:STDERR: extern library "redecl_extern_api" fn D(); // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: fail_merge.carbon:[[@LINE+14]]:1: in import [InImport] // CHECK:STDERR: api.carbon:7:1: note: name is previously declared here [NameDeclPrevious] // CHECK:STDERR: extern fn D(); // CHECK:STDERR: ^~~~~~~~~~~~~~ // CHECK:STDERR: // CHECK:STDERR: fail_merge.carbon:[[@LINE+9]]:1: in import [InImport] // CHECK:STDERR: extern_api.carbon:10:1: error: duplicate name `E` being declared in the same scope [NameDeclDuplicate] // CHECK:STDERR: extern library "redecl_extern_api" fn NS.E(); // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: fail_merge.carbon:[[@LINE+5]]:1: in import [InImport] // CHECK:STDERR: api.carbon:10:1: note: name is previously declared here [NameDeclPrevious] // CHECK:STDERR: fn NS.E(); // CHECK:STDERR: ^~~~~~~~~~ // CHECK:STDERR: import library "api"; import library "extern_api"; var a: () = A(); var b: i32 = B(1); var c: {.c: i32} = C((1,)); var d: () = D(); var e: () = NS.E(); // --- fail_merge_reverse.carbon library "[[@TEST_NAME]]"; // CHECK:STDERR: fail_merge_reverse.carbon:[[@LINE+45]]:1: in import [InImport] // CHECK:STDERR: api.carbon:4:1: error: duplicate name `A` being declared in the same scope [NameDeclDuplicate] // CHECK:STDERR: fn A(); // CHECK:STDERR: ^~~~~~~ // CHECK:STDERR: fail_merge_reverse.carbon:[[@LINE+41]]:1: in import [InImport] // CHECK:STDERR: extern_api.carbon:4:1: note: name is previously declared here [NameDeclPrevious] // CHECK:STDERR: extern library "redecl_extern_api" fn A(); // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: // CHECK:STDERR: fail_merge_reverse.carbon:[[@LINE+36]]:1: in import [InImport] // CHECK:STDERR: api.carbon:5:1: error: duplicate name `B` being declared in the same scope [NameDeclDuplicate] // CHECK:STDERR: fn B(b: i32) -> i32; // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: fail_merge_reverse.carbon:[[@LINE+32]]:1: in import [InImport] // CHECK:STDERR: extern_api.carbon:5:1: note: name is previously declared here [NameDeclPrevious] // CHECK:STDERR: extern library "redecl_extern_api" fn B(b: i32) -> i32; // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: // CHECK:STDERR: fail_merge_reverse.carbon:[[@LINE+27]]:1: in import [InImport] // CHECK:STDERR: api.carbon:6:1: error: duplicate name `C` being declared in the same scope [NameDeclDuplicate] // CHECK:STDERR: fn C(c: (i32,)) -> {.c: i32}; // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: fail_merge_reverse.carbon:[[@LINE+23]]:1: in import [InImport] // CHECK:STDERR: extern_api.carbon:6:1: note: name is previously declared here [NameDeclPrevious] // CHECK:STDERR: extern library "redecl_extern_api" fn C(c: (i32,)) -> {.c: i32}; // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: // CHECK:STDERR: fail_merge_reverse.carbon:[[@LINE+18]]:1: in import [InImport] // CHECK:STDERR: api.carbon:7:1: error: duplicate name `D` being declared in the same scope [NameDeclDuplicate] // CHECK:STDERR: extern fn D(); // CHECK:STDERR: ^~~~~~~~~~~~~~ // CHECK:STDERR: fail_merge_reverse.carbon:[[@LINE+14]]:1: in import [InImport] // CHECK:STDERR: extern_api.carbon:7:1: note: name is previously declared here [NameDeclPrevious] // CHECK:STDERR: extern library "redecl_extern_api" fn D(); // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: // CHECK:STDERR: fail_merge_reverse.carbon:[[@LINE+9]]:1: in import [InImport] // CHECK:STDERR: api.carbon:10:1: error: duplicate name `E` being declared in the same scope [NameDeclDuplicate] // CHECK:STDERR: fn NS.E(); // CHECK:STDERR: ^~~~~~~~~~ // CHECK:STDERR: fail_merge_reverse.carbon:[[@LINE+5]]:1: in import [InImport] // CHECK:STDERR: extern_api.carbon:10:1: note: name is previously declared here [NameDeclPrevious] // CHECK:STDERR: extern library "redecl_extern_api" fn NS.E(); // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: import library "extern_api"; import library "api"; var a: () = A(); var b: i32 = B(1); var c: {.c: i32} = C((1,)); var d: () = D(); var e: () = NS.E(); // --- unloaded.carbon library "[[@TEST_NAME]]"; import library "api"; // --- unloaded_extern.carbon library "[[@TEST_NAME]]"; import library "extern_api"; // CHECK:STDOUT: --- api.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %A.type: type = fn_type @A [concrete] // CHECK:STDOUT: %A: %A.type = struct_value () [concrete] // CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [concrete] // CHECK:STDOUT: %Int.type: type = generic_class_type @Int [concrete] // CHECK:STDOUT: %Int.generic: %Int.type = struct_value () [concrete] // CHECK:STDOUT: %i32: type = class_type @Int, @Int(%int_32) [concrete] // CHECK:STDOUT: %pattern_type.7ce: type = pattern_type %i32 [concrete] // CHECK:STDOUT: %.ff5: Core.Form = init_form %i32 [concrete] // CHECK:STDOUT: %B.type: type = fn_type @B [concrete] // CHECK:STDOUT: %B: %B.type = struct_value () [concrete] // CHECK:STDOUT: %tuple.type.85c: type = tuple_type (type) [concrete] // CHECK:STDOUT: %tuple: %tuple.type.85c = tuple_value (%i32) [concrete] // CHECK:STDOUT: %tuple.type.a1c: type = tuple_type (%i32) [concrete] // CHECK:STDOUT: %pattern_type.b74: type = pattern_type %tuple.type.a1c [concrete] // CHECK:STDOUT: %struct_type.c: type = struct_type {.c: %i32} [concrete] // CHECK:STDOUT: %.da9: Core.Form = init_form %struct_type.c [concrete] // CHECK:STDOUT: %pattern_type.688: type = pattern_type %struct_type.c [concrete] // CHECK:STDOUT: %C.type: type = fn_type @C [concrete] // CHECK:STDOUT: %C: %C.type = struct_value () [concrete] // CHECK:STDOUT: %D.type: type = fn_type @D [concrete] // CHECK:STDOUT: %D: %D.type = struct_value () [concrete] // CHECK:STDOUT: %E.type: type = fn_type @E [concrete] // CHECK:STDOUT: %E: %E.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: .Int = %Core.Int // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: %Core.Int: %Int.type = import_ref Core//prelude/parts/int, Int, loaded [concrete = constants.%Int.generic] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .A = %A.decl // CHECK:STDOUT: .B = %B.decl // CHECK:STDOUT: .C = %C.decl // CHECK:STDOUT: .D = %D.decl // CHECK:STDOUT: .NS = %NS // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %A.decl: %A.type = fn_decl @A [concrete = constants.%A] {} {} // CHECK:STDOUT: %B.decl: %B.type = fn_decl @B [concrete = constants.%B] { // CHECK:STDOUT: %b.patt: %pattern_type.7ce = value_binding_pattern b [concrete] // CHECK:STDOUT: %b.param_patt: %pattern_type.7ce = value_param_pattern %b.patt [concrete] // CHECK:STDOUT: %return.patt: %pattern_type.7ce = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: %pattern_type.7ce = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %i32.loc5_17: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %.loc5: Core.Form = init_form %i32.loc5_17 [concrete = constants.%.ff5] // CHECK:STDOUT: %b.param: %i32 = value_param call_param0 // CHECK:STDOUT: %i32.loc5_9: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %b: %i32 = value_binding b, %b.param // CHECK:STDOUT: %return.param: ref %i32 = out_param call_param1 // CHECK:STDOUT: %return: ref %i32 = return_slot %return.param // CHECK:STDOUT: } // CHECK:STDOUT: %C.decl: %C.type = fn_decl @C [concrete = constants.%C] { // CHECK:STDOUT: %c.patt: %pattern_type.b74 = value_binding_pattern c [concrete] // CHECK:STDOUT: %c.param_patt: %pattern_type.b74 = value_param_pattern %c.patt [concrete] // CHECK:STDOUT: %return.patt: %pattern_type.688 = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: %pattern_type.688 = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %i32.loc6_25: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %struct_type.c: type = struct_type {.c: %i32} [concrete = constants.%struct_type.c] // CHECK:STDOUT: %.loc6_28: Core.Form = init_form %struct_type.c [concrete = constants.%.da9] // CHECK:STDOUT: %c.param: %tuple.type.a1c = value_param call_param0 // CHECK:STDOUT: %.loc6_14.1: type = splice_block %.loc6_14.3 [concrete = constants.%tuple.type.a1c] { // CHECK:STDOUT: %i32.loc6_10: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %.loc6_14.2: %tuple.type.85c = tuple_literal (%i32.loc6_10) [concrete = constants.%tuple] // CHECK:STDOUT: %.loc6_14.3: type = converted %.loc6_14.2, constants.%tuple.type.a1c [concrete = constants.%tuple.type.a1c] // CHECK:STDOUT: } // CHECK:STDOUT: %c: %tuple.type.a1c = value_binding c, %c.param // CHECK:STDOUT: %return.param: ref %struct_type.c = out_param call_param1 // CHECK:STDOUT: %return: ref %struct_type.c = return_slot %return.param // CHECK:STDOUT: } // CHECK:STDOUT: %D.decl: %D.type = fn_decl @D [concrete = constants.%D] {} {} // CHECK:STDOUT: %NS: = namespace [concrete] { // CHECK:STDOUT: .E = %E.decl // CHECK:STDOUT: } // CHECK:STDOUT: %E.decl: %E.type = fn_decl @E [concrete = constants.%E] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @A(); // CHECK:STDOUT: // CHECK:STDOUT: fn @B(%b.param: %i32) -> out %return.param: %i32; // CHECK:STDOUT: // CHECK:STDOUT: fn @C(%c.param: %tuple.type.a1c) -> out %return.param: %struct_type.c; // CHECK:STDOUT: // CHECK:STDOUT: extern fn @D(); // CHECK:STDOUT: // CHECK:STDOUT: fn @E(); // CHECK:STDOUT: // CHECK:STDOUT: --- extern_api.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %A.type: type = fn_type @A [concrete] // CHECK:STDOUT: %A: %A.type = struct_value () [concrete] // CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [concrete] // CHECK:STDOUT: %Int.type: type = generic_class_type @Int [concrete] // CHECK:STDOUT: %Int.generic: %Int.type = struct_value () [concrete] // CHECK:STDOUT: %i32: type = class_type @Int, @Int(%int_32) [concrete] // CHECK:STDOUT: %pattern_type.7ce: type = pattern_type %i32 [concrete] // CHECK:STDOUT: %.ff5: Core.Form = init_form %i32 [concrete] // CHECK:STDOUT: %B.type: type = fn_type @B [concrete] // CHECK:STDOUT: %B: %B.type = struct_value () [concrete] // CHECK:STDOUT: %tuple.type.85c: type = tuple_type (type) [concrete] // CHECK:STDOUT: %tuple: %tuple.type.85c = tuple_value (%i32) [concrete] // CHECK:STDOUT: %tuple.type.a1c: type = tuple_type (%i32) [concrete] // CHECK:STDOUT: %pattern_type.b74: type = pattern_type %tuple.type.a1c [concrete] // CHECK:STDOUT: %struct_type.c: type = struct_type {.c: %i32} [concrete] // CHECK:STDOUT: %.da9: Core.Form = init_form %struct_type.c [concrete] // CHECK:STDOUT: %pattern_type.688: type = pattern_type %struct_type.c [concrete] // CHECK:STDOUT: %C.type: type = fn_type @C [concrete] // CHECK:STDOUT: %C: %C.type = struct_value () [concrete] // CHECK:STDOUT: %D.type: type = fn_type @D [concrete] // CHECK:STDOUT: %D: %D.type = struct_value () [concrete] // CHECK:STDOUT: %E.type: type = fn_type @E [concrete] // CHECK:STDOUT: %E: %E.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: .Int = %Core.Int // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: %Core.Int: %Int.type = import_ref Core//prelude/parts/int, Int, loaded [concrete = constants.%Int.generic] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .A = %A.decl // CHECK:STDOUT: .B = %B.decl // CHECK:STDOUT: .C = %C.decl // CHECK:STDOUT: .D = %D.decl // CHECK:STDOUT: .NS = %NS // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %A.decl: %A.type = fn_decl @A [concrete = constants.%A] {} {} // CHECK:STDOUT: %B.decl: %B.type = fn_decl @B [concrete = constants.%B] { // CHECK:STDOUT: %b.patt: %pattern_type.7ce = value_binding_pattern b [concrete] // CHECK:STDOUT: %b.param_patt: %pattern_type.7ce = value_param_pattern %b.patt [concrete] // CHECK:STDOUT: %return.patt: %pattern_type.7ce = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: %pattern_type.7ce = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %i32.loc5_52: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %.loc5: Core.Form = init_form %i32.loc5_52 [concrete = constants.%.ff5] // CHECK:STDOUT: %b.param: %i32 = value_param call_param0 // CHECK:STDOUT: %i32.loc5_44: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %b: %i32 = value_binding b, %b.param // CHECK:STDOUT: %return.param: ref %i32 = out_param call_param1 // CHECK:STDOUT: %return: ref %i32 = return_slot %return.param // CHECK:STDOUT: } // CHECK:STDOUT: %C.decl: %C.type = fn_decl @C [concrete = constants.%C] { // CHECK:STDOUT: %c.patt: %pattern_type.b74 = value_binding_pattern c [concrete] // CHECK:STDOUT: %c.param_patt: %pattern_type.b74 = value_param_pattern %c.patt [concrete] // CHECK:STDOUT: %return.patt: %pattern_type.688 = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: %pattern_type.688 = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %i32.loc6_60: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %struct_type.c: type = struct_type {.c: %i32} [concrete = constants.%struct_type.c] // CHECK:STDOUT: %.loc6_63: Core.Form = init_form %struct_type.c [concrete = constants.%.da9] // CHECK:STDOUT: %c.param: %tuple.type.a1c = value_param call_param0 // CHECK:STDOUT: %.loc6_49.1: type = splice_block %.loc6_49.3 [concrete = constants.%tuple.type.a1c] { // CHECK:STDOUT: %i32.loc6_45: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %.loc6_49.2: %tuple.type.85c = tuple_literal (%i32.loc6_45) [concrete = constants.%tuple] // CHECK:STDOUT: %.loc6_49.3: type = converted %.loc6_49.2, constants.%tuple.type.a1c [concrete = constants.%tuple.type.a1c] // CHECK:STDOUT: } // CHECK:STDOUT: %c: %tuple.type.a1c = value_binding c, %c.param // CHECK:STDOUT: %return.param: ref %struct_type.c = out_param call_param1 // CHECK:STDOUT: %return: ref %struct_type.c = return_slot %return.param // CHECK:STDOUT: } // CHECK:STDOUT: %D.decl: %D.type = fn_decl @D [concrete = constants.%D] {} {} // CHECK:STDOUT: %NS: = namespace [concrete] { // CHECK:STDOUT: .E = %E.decl // CHECK:STDOUT: } // CHECK:STDOUT: %E.decl: %E.type = fn_decl @E [concrete = constants.%E] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: extern fn @A(); // CHECK:STDOUT: // CHECK:STDOUT: extern fn @B(%b.param: %i32) -> out %return.param: %i32; // CHECK:STDOUT: // CHECK:STDOUT: extern fn @C(%c.param: %tuple.type.a1c) -> out %return.param: %struct_type.c; // CHECK:STDOUT: // CHECK:STDOUT: extern fn @D(); // CHECK:STDOUT: // CHECK:STDOUT: extern fn @E(); // CHECK:STDOUT: // CHECK:STDOUT: --- basics.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] // CHECK:STDOUT: %empty_tuple: %empty_tuple.type = tuple_value () [concrete] // CHECK:STDOUT: %pattern_type.cb1: type = pattern_type %empty_tuple.type [concrete] // CHECK:STDOUT: %A.type: type = fn_type @A [concrete] // CHECK:STDOUT: %A: %A.type = struct_value () [concrete] // CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [concrete] // CHECK:STDOUT: %Int.type: type = generic_class_type @Int [concrete] // CHECK:STDOUT: %Int.generic: %Int.type = struct_value () [concrete] // CHECK:STDOUT: %i32: type = class_type @Int, @Int(%int_32) [concrete] // CHECK:STDOUT: %pattern_type.7ce: type = pattern_type %i32 [concrete] // CHECK:STDOUT: %B.type: type = fn_type @B [concrete] // CHECK:STDOUT: %B: %B.type = struct_value () [concrete] // CHECK:STDOUT: %int_1.5b8: Core.IntLiteral = int_value 1 [concrete] // CHECK:STDOUT: %ImplicitAs.type.cc7: type = generic_interface_type @ImplicitAs [concrete] // CHECK:STDOUT: %ImplicitAs.generic: %ImplicitAs.type.cc7 = struct_value () [concrete] // CHECK:STDOUT: %ImplicitAs.type.e8c: type = facet_type <@ImplicitAs, @ImplicitAs(%i32)> [concrete] // CHECK:STDOUT: %To: Core.IntLiteral = symbolic_binding To, 0 [symbolic] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.4e6: type = fn_type @Core.IntLiteral.as.ImplicitAs.impl.Convert, @Core.IntLiteral.as.ImplicitAs.impl(%To) [symbolic] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.3c2: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.4e6 = struct_value () [symbolic] // CHECK:STDOUT: %ImplicitAs.impl_witness.6bc: = impl_witness imports.%ImplicitAs.impl_witness_table.74f, @Core.IntLiteral.as.ImplicitAs.impl(%int_32) [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.e0d: type = fn_type @Core.IntLiteral.as.ImplicitAs.impl.Convert, @Core.IntLiteral.as.ImplicitAs.impl(%int_32) [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.e0d = struct_value () [concrete] // CHECK:STDOUT: %ImplicitAs.facet: %ImplicitAs.type.e8c = facet_value Core.IntLiteral, (%ImplicitAs.impl_witness.6bc) [concrete] // CHECK:STDOUT: %ImplicitAs.WithSelf.Convert.type.b37: type = fn_type @ImplicitAs.WithSelf.Convert, @ImplicitAs.WithSelf(%i32, %ImplicitAs.facet) [concrete] // CHECK:STDOUT: %.545: type = fn_type_with_self_type %ImplicitAs.WithSelf.Convert.type.b37, %ImplicitAs.facet [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.bound: = bound_method %int_1.5b8, %Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5 [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn: = specific_function %Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5, @Core.IntLiteral.as.ImplicitAs.impl.Convert(%int_32) [concrete] // CHECK:STDOUT: %bound_method: = bound_method %int_1.5b8, %Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn [concrete] // CHECK:STDOUT: %int_1.5d2: %i32 = int_value 1 [concrete] // CHECK:STDOUT: %struct_type.c: type = struct_type {.c: %i32} [concrete] // CHECK:STDOUT: %pattern_type.688: type = pattern_type %struct_type.c [concrete] // CHECK:STDOUT: %C.type: type = fn_type @C [concrete] // CHECK:STDOUT: %C: %C.type = struct_value () [concrete] // CHECK:STDOUT: %tuple.type.a1c: type = tuple_type (%i32) [concrete] // CHECK:STDOUT: %tuple.type.985: type = tuple_type (Core.IntLiteral) [concrete] // CHECK:STDOUT: %tuple.378: %tuple.type.985 = tuple_value (%int_1.5b8) [concrete] // CHECK:STDOUT: %tuple.246: %tuple.type.a1c = tuple_value (%int_1.5d2) [concrete] // CHECK:STDOUT: %D.type: type = fn_type @D [concrete] // CHECK:STDOUT: %D: %D.type = struct_value () [concrete] // CHECK:STDOUT: %E.type: type = fn_type @E [concrete] // CHECK:STDOUT: %E: %E.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Main.A: %A.type = import_ref Main//api, A, loaded [concrete = constants.%A] // CHECK:STDOUT: %Main.B: %B.type = import_ref Main//api, B, loaded [concrete = constants.%B] // CHECK:STDOUT: %Main.C: %C.type = import_ref Main//api, C, loaded [concrete = constants.%C] // CHECK:STDOUT: %Main.D: %D.type = import_ref Main//api, D, loaded [concrete = constants.%D] // CHECK:STDOUT: %Main.NS: = import_ref Main//api, NS, loaded // CHECK:STDOUT: %NS: = namespace %Main.NS, [concrete] { // CHECK:STDOUT: .E = %Main.E // CHECK:STDOUT: } // CHECK:STDOUT: %Main.E: %E.type = import_ref Main//api, E, loaded [concrete = constants.%E] // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: .Int = %Core.Int // CHECK:STDOUT: .ImplicitAs = %Core.ImplicitAs // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: %Core.Int: %Int.type = import_ref Core//prelude/parts/int, Int, loaded [concrete = constants.%Int.generic] // CHECK:STDOUT: %Core.ImplicitAs: %ImplicitAs.type.cc7 = import_ref Core//prelude/parts/as, ImplicitAs, loaded [concrete = constants.%ImplicitAs.generic] // CHECK:STDOUT: %Core.import_ref.42d: @Core.IntLiteral.as.ImplicitAs.impl.%Core.IntLiteral.as.ImplicitAs.impl.Convert.type (%Core.IntLiteral.as.ImplicitAs.impl.Convert.type.4e6) = import_ref Core//prelude/parts/int, loc{{\d+_\d+}}, loaded [symbolic = @Core.IntLiteral.as.ImplicitAs.impl.%Core.IntLiteral.as.ImplicitAs.impl.Convert (constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.3c2)] // CHECK:STDOUT: %ImplicitAs.impl_witness_table.74f = impl_witness_table (%Core.import_ref.42d), @Core.IntLiteral.as.ImplicitAs.impl [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .A = imports.%Main.A // CHECK:STDOUT: .B = imports.%Main.B // CHECK:STDOUT: .C = imports.%Main.C // CHECK:STDOUT: .D = imports.%Main.D // CHECK:STDOUT: .NS = imports.%NS // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .a = %a // CHECK:STDOUT: .b = %b // CHECK:STDOUT: .c = %c // CHECK:STDOUT: .d = %d // CHECK:STDOUT: .e = %e // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %default.import = import // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %a.patt: %pattern_type.cb1 = ref_binding_pattern a [concrete] // CHECK:STDOUT: %a.var_patt: %pattern_type.cb1 = var_pattern %a.patt [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %a.var: ref %empty_tuple.type = var %a.var_patt [concrete] // CHECK:STDOUT: %.loc6_9.1: type = splice_block %.loc6_9.3 [concrete = constants.%empty_tuple.type] { // CHECK:STDOUT: %.loc6_9.2: %empty_tuple.type = tuple_literal () [concrete = constants.%empty_tuple] // CHECK:STDOUT: %.loc6_9.3: type = converted %.loc6_9.2, constants.%empty_tuple.type [concrete = constants.%empty_tuple.type] // CHECK:STDOUT: } // CHECK:STDOUT: %a: ref %empty_tuple.type = ref_binding a, %a.var [concrete = %a.var] // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %b.patt: %pattern_type.7ce = ref_binding_pattern b [concrete] // CHECK:STDOUT: %b.var_patt: %pattern_type.7ce = var_pattern %b.patt [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %b.var: ref %i32 = var %b.var_patt [concrete] // CHECK:STDOUT: %i32.loc7: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %b: ref %i32 = ref_binding b, %b.var [concrete = %b.var] // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %c.patt: %pattern_type.688 = ref_binding_pattern c [concrete] // CHECK:STDOUT: %c.var_patt: %pattern_type.688 = var_pattern %c.patt [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %c.var: ref %struct_type.c = var %c.var_patt [concrete] // CHECK:STDOUT: %.loc8: type = splice_block %struct_type.c [concrete = constants.%struct_type.c] { // CHECK:STDOUT: %i32.loc8: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %struct_type.c: type = struct_type {.c: %i32} [concrete = constants.%struct_type.c] // CHECK:STDOUT: } // CHECK:STDOUT: %c: ref %struct_type.c = ref_binding c, %c.var [concrete = %c.var] // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %d.patt: %pattern_type.cb1 = ref_binding_pattern d [concrete] // CHECK:STDOUT: %d.var_patt: %pattern_type.cb1 = var_pattern %d.patt [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %d.var: ref %empty_tuple.type = var %d.var_patt [concrete] // CHECK:STDOUT: %.loc9_9.1: type = splice_block %.loc9_9.3 [concrete = constants.%empty_tuple.type] { // CHECK:STDOUT: %.loc9_9.2: %empty_tuple.type = tuple_literal () [concrete = constants.%empty_tuple] // CHECK:STDOUT: %.loc9_9.3: type = converted %.loc9_9.2, constants.%empty_tuple.type [concrete = constants.%empty_tuple.type] // CHECK:STDOUT: } // CHECK:STDOUT: %d: ref %empty_tuple.type = ref_binding d, %d.var [concrete = %d.var] // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %e.patt: %pattern_type.cb1 = ref_binding_pattern e [concrete] // CHECK:STDOUT: %e.var_patt: %pattern_type.cb1 = var_pattern %e.patt [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %e.var: ref %empty_tuple.type = var %e.var_patt [concrete] // CHECK:STDOUT: %.loc10_9.1: type = splice_block %.loc10_9.3 [concrete = constants.%empty_tuple.type] { // CHECK:STDOUT: %.loc10_9.2: %empty_tuple.type = tuple_literal () [concrete = constants.%empty_tuple] // CHECK:STDOUT: %.loc10_9.3: type = converted %.loc10_9.2, constants.%empty_tuple.type [concrete = constants.%empty_tuple.type] // CHECK:STDOUT: } // CHECK:STDOUT: %e: ref %empty_tuple.type = ref_binding e, %e.var [concrete = %e.var] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @A [from "api.carbon"]; // CHECK:STDOUT: // CHECK:STDOUT: fn @B [from "api.carbon"]; // CHECK:STDOUT: // CHECK:STDOUT: fn @C [from "api.carbon"]; // CHECK:STDOUT: // CHECK:STDOUT: extern fn @D [from "api.carbon"]; // CHECK:STDOUT: // CHECK:STDOUT: fn @E [from "api.carbon"]; // CHECK:STDOUT: // CHECK:STDOUT: fn @__global_init() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %A.ref: %A.type = name_ref A, imports.%Main.A [concrete = constants.%A] // CHECK:STDOUT: %A.call: init %empty_tuple.type = call %A.ref() // CHECK:STDOUT: assign file.%a.var, %A.call // CHECK:STDOUT: %B.ref: %B.type = name_ref B, imports.%Main.B [concrete = constants.%B] // CHECK:STDOUT: %int_1.loc7: Core.IntLiteral = int_value 1 [concrete = constants.%int_1.5b8] // CHECK:STDOUT: %impl.elem0.loc7: %.545 = impl_witness_access constants.%ImplicitAs.impl_witness.6bc, element0 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5] // CHECK:STDOUT: %bound_method.loc7_16.1: = bound_method %int_1.loc7, %impl.elem0.loc7 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.bound] // CHECK:STDOUT: %specific_fn.loc7: = specific_function %impl.elem0.loc7, @Core.IntLiteral.as.ImplicitAs.impl.Convert(constants.%int_32) [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn] // CHECK:STDOUT: %bound_method.loc7_16.2: = bound_method %int_1.loc7, %specific_fn.loc7 [concrete = constants.%bound_method] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc7: init %i32 = call %bound_method.loc7_16.2(%int_1.loc7) [concrete = constants.%int_1.5d2] // CHECK:STDOUT: %.loc7_16.1: %i32 = value_of_initializer %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc7 [concrete = constants.%int_1.5d2] // CHECK:STDOUT: %.loc7_16.2: %i32 = converted %int_1.loc7, %.loc7_16.1 [concrete = constants.%int_1.5d2] // CHECK:STDOUT: %B.call: init %i32 = call %B.ref(%.loc7_16.2) // CHECK:STDOUT: assign file.%b.var, %B.call // CHECK:STDOUT: %C.ref: %C.type = name_ref C, imports.%Main.C [concrete = constants.%C] // CHECK:STDOUT: %int_1.loc8: Core.IntLiteral = int_value 1 [concrete = constants.%int_1.5b8] // CHECK:STDOUT: %.loc8_25.1: %tuple.type.985 = tuple_literal (%int_1.loc8) [concrete = constants.%tuple.378] // CHECK:STDOUT: %impl.elem0.loc8: %.545 = impl_witness_access constants.%ImplicitAs.impl_witness.6bc, element0 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5] // CHECK:STDOUT: %bound_method.loc8_25.1: = bound_method %int_1.loc8, %impl.elem0.loc8 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.bound] // CHECK:STDOUT: %specific_fn.loc8: = specific_function %impl.elem0.loc8, @Core.IntLiteral.as.ImplicitAs.impl.Convert(constants.%int_32) [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn] // CHECK:STDOUT: %bound_method.loc8_25.2: = bound_method %int_1.loc8, %specific_fn.loc8 [concrete = constants.%bound_method] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc8: init %i32 = call %bound_method.loc8_25.2(%int_1.loc8) [concrete = constants.%int_1.5d2] // CHECK:STDOUT: %.loc8_25.2: %i32 = value_of_initializer %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc8 [concrete = constants.%int_1.5d2] // CHECK:STDOUT: %.loc8_25.3: %i32 = converted %int_1.loc8, %.loc8_25.2 [concrete = constants.%int_1.5d2] // CHECK:STDOUT: %tuple: %tuple.type.a1c = tuple_value (%.loc8_25.3) [concrete = constants.%tuple.246] // CHECK:STDOUT: %.loc8_25.4: %tuple.type.a1c = converted %.loc8_25.1, %tuple [concrete = constants.%tuple.246] // CHECK:STDOUT: %C.call: init %struct_type.c = call %C.ref(%.loc8_25.4) // CHECK:STDOUT: assign file.%c.var, %C.call // CHECK:STDOUT: %D.ref: %D.type = name_ref D, imports.%Main.D [concrete = constants.%D] // CHECK:STDOUT: %D.call: init %empty_tuple.type = call %D.ref() // CHECK:STDOUT: assign file.%d.var, %D.call // CHECK:STDOUT: %NS.ref: = name_ref NS, imports.%NS [concrete = imports.%NS] // CHECK:STDOUT: %E.ref: %E.type = name_ref E, imports.%Main.E [concrete = constants.%E] // CHECK:STDOUT: %E.call: init %empty_tuple.type = call %E.ref() // CHECK:STDOUT: assign file.%e.var, %E.call // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- fail_redecl_api.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %A.type: type = fn_type @A [concrete] // CHECK:STDOUT: %A: %A.type = struct_value () [concrete] // CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [concrete] // CHECK:STDOUT: %Int.type: type = generic_class_type @Int [concrete] // CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] // CHECK:STDOUT: %Int.generic: %Int.type = struct_value () [concrete] // CHECK:STDOUT: %i32: type = class_type @Int, @Int(%int_32) [concrete] // CHECK:STDOUT: %pattern_type.7ce: type = pattern_type %i32 [concrete] // CHECK:STDOUT: %.ff5: Core.Form = init_form %i32 [concrete] // CHECK:STDOUT: %B.type: type = fn_type @B [concrete] // CHECK:STDOUT: %B: %B.type = struct_value () [concrete] // CHECK:STDOUT: %tuple.type.85c: type = tuple_type (type) [concrete] // CHECK:STDOUT: %tuple.896: %tuple.type.85c = tuple_value (%i32) [concrete] // CHECK:STDOUT: %tuple.type.a1c: type = tuple_type (%i32) [concrete] // CHECK:STDOUT: %struct_type.c: type = struct_type {.c: %i32} [concrete] // CHECK:STDOUT: %.da9: Core.Form = init_form %struct_type.c [concrete] // CHECK:STDOUT: %pattern_type.688: type = pattern_type %struct_type.c [concrete] // CHECK:STDOUT: %C.type: type = fn_type @C [concrete] // CHECK:STDOUT: %C: %C.type = struct_value () [concrete] // CHECK:STDOUT: %D.type: type = fn_type @D [concrete] // CHECK:STDOUT: %D: %D.type = struct_value () [concrete] // CHECK:STDOUT: %E.type: type = fn_type @E [concrete] // CHECK:STDOUT: %E: %E.type = struct_value () [concrete] // CHECK:STDOUT: %empty_tuple: %empty_tuple.type = tuple_value () [concrete] // CHECK:STDOUT: %pattern_type.cb1: type = pattern_type %empty_tuple.type [concrete] // CHECK:STDOUT: %int_1.5b8: Core.IntLiteral = int_value 1 [concrete] // CHECK:STDOUT: %ImplicitAs.type.cc7: type = generic_interface_type @ImplicitAs [concrete] // CHECK:STDOUT: %ImplicitAs.generic: %ImplicitAs.type.cc7 = struct_value () [concrete] // CHECK:STDOUT: %ImplicitAs.type.e8c: type = facet_type <@ImplicitAs, @ImplicitAs(%i32)> [concrete] // CHECK:STDOUT: %To: Core.IntLiteral = symbolic_binding To, 0 [symbolic] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.4e6: type = fn_type @Core.IntLiteral.as.ImplicitAs.impl.Convert, @Core.IntLiteral.as.ImplicitAs.impl(%To) [symbolic] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.3c2: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.4e6 = struct_value () [symbolic] // CHECK:STDOUT: %ImplicitAs.impl_witness.6bc: = impl_witness imports.%ImplicitAs.impl_witness_table.74f, @Core.IntLiteral.as.ImplicitAs.impl(%int_32) [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.e0d: type = fn_type @Core.IntLiteral.as.ImplicitAs.impl.Convert, @Core.IntLiteral.as.ImplicitAs.impl(%int_32) [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.e0d = struct_value () [concrete] // CHECK:STDOUT: %ImplicitAs.facet: %ImplicitAs.type.e8c = facet_value Core.IntLiteral, (%ImplicitAs.impl_witness.6bc) [concrete] // CHECK:STDOUT: %ImplicitAs.WithSelf.Convert.type.b37: type = fn_type @ImplicitAs.WithSelf.Convert, @ImplicitAs.WithSelf(%i32, %ImplicitAs.facet) [concrete] // CHECK:STDOUT: %.545: type = fn_type_with_self_type %ImplicitAs.WithSelf.Convert.type.b37, %ImplicitAs.facet [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.bound: = bound_method %int_1.5b8, %Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5 [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn: = specific_function %Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5, @Core.IntLiteral.as.ImplicitAs.impl.Convert(%int_32) [concrete] // CHECK:STDOUT: %bound_method: = bound_method %int_1.5b8, %Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn [concrete] // CHECK:STDOUT: %int_1.5d2: %i32 = int_value 1 [concrete] // CHECK:STDOUT: %tuple.type.985: type = tuple_type (Core.IntLiteral) [concrete] // CHECK:STDOUT: %tuple.378: %tuple.type.985 = tuple_value (%int_1.5b8) [concrete] // CHECK:STDOUT: %tuple.246: %tuple.type.a1c = tuple_value (%int_1.5d2) [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Main.NS: = import_ref Main//api, NS, loaded // CHECK:STDOUT: %NS: = namespace %Main.NS, [concrete] { // CHECK:STDOUT: .E = file.%E.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: .Int = %Core.Int // CHECK:STDOUT: .ImplicitAs = %Core.ImplicitAs // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: %Core.Int: %Int.type = import_ref Core//prelude/parts/int, Int, loaded [concrete = constants.%Int.generic] // CHECK:STDOUT: %Core.ImplicitAs: %ImplicitAs.type.cc7 = import_ref Core//prelude/parts/as, ImplicitAs, loaded [concrete = constants.%ImplicitAs.generic] // CHECK:STDOUT: %Core.import_ref.42d: @Core.IntLiteral.as.ImplicitAs.impl.%Core.IntLiteral.as.ImplicitAs.impl.Convert.type (%Core.IntLiteral.as.ImplicitAs.impl.Convert.type.4e6) = import_ref Core//prelude/parts/int, loc{{\d+_\d+}}, loaded [symbolic = @Core.IntLiteral.as.ImplicitAs.impl.%Core.IntLiteral.as.ImplicitAs.impl.Convert (constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.3c2)] // CHECK:STDOUT: %ImplicitAs.impl_witness_table.74f = impl_witness_table (%Core.import_ref.42d), @Core.IntLiteral.as.ImplicitAs.impl [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .A = %A.decl // CHECK:STDOUT: .B = %B.decl // CHECK:STDOUT: .C = %C.decl // CHECK:STDOUT: .D = %D.decl // CHECK:STDOUT: .NS = imports.%NS // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .a = %a // CHECK:STDOUT: .b = %b // CHECK:STDOUT: .c = %c // CHECK:STDOUT: .d = %d // CHECK:STDOUT: .e = %e // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %default.import = import // CHECK:STDOUT: %A.decl: %A.type = fn_decl @A [concrete = constants.%A] {} {} // CHECK:STDOUT: %B.decl: %B.type = fn_decl @B [concrete = constants.%B] {} { // CHECK:STDOUT: %i32.loc23_24: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %.loc23: Core.Form = init_form %i32.loc23_24 [concrete = constants.%.ff5] // CHECK:STDOUT: %b.param: %i32 = value_param call_param0 // CHECK:STDOUT: %i32.loc23_16: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %b: %i32 = value_binding b, %b.param // CHECK:STDOUT: %return.param: ref %i32 = out_param call_param1 // CHECK:STDOUT: %return: ref %i32 = return_slot %return.param // CHECK:STDOUT: } // CHECK:STDOUT: %C.decl: %C.type = fn_decl @C [concrete = constants.%C] {} { // CHECK:STDOUT: %i32.loc32_32: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %struct_type.c: type = struct_type {.c: %i32} [concrete = constants.%struct_type.c] // CHECK:STDOUT: %.loc32_35: Core.Form = init_form %struct_type.c [concrete = constants.%.da9] // CHECK:STDOUT: %c.param: %tuple.type.a1c = value_param call_param0 // CHECK:STDOUT: %.loc32_21.1: type = splice_block %.loc32_21.3 [concrete = constants.%tuple.type.a1c] { // CHECK:STDOUT: %i32.loc32_17: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %.loc32_21.2: %tuple.type.85c = tuple_literal (%i32.loc32_17) [concrete = constants.%tuple.896] // CHECK:STDOUT: %.loc32_21.3: type = converted %.loc32_21.2, constants.%tuple.type.a1c [concrete = constants.%tuple.type.a1c] // CHECK:STDOUT: } // CHECK:STDOUT: %c: %tuple.type.a1c = value_binding c, %c.param // CHECK:STDOUT: %return.param: ref %struct_type.c = out_param call_param1 // CHECK:STDOUT: %return: ref %struct_type.c = return_slot %return.param // CHECK:STDOUT: } // CHECK:STDOUT: %D.decl: %D.type = fn_decl @D [concrete = constants.%D] {} {} // CHECK:STDOUT: %E.decl: %E.type = fn_decl @E [concrete = constants.%E] {} {} // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %a.patt: %pattern_type.cb1 = ref_binding_pattern a [concrete] // CHECK:STDOUT: %a.var_patt: %pattern_type.cb1 = var_pattern %a.patt [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %a.var: ref %empty_tuple.type = var %a.var_patt [concrete] // CHECK:STDOUT: %.loc52_9.1: type = splice_block %.loc52_9.3 [concrete = constants.%empty_tuple.type] { // CHECK:STDOUT: %.loc52_9.2: %empty_tuple.type = tuple_literal () [concrete = constants.%empty_tuple] // CHECK:STDOUT: %.loc52_9.3: type = converted %.loc52_9.2, constants.%empty_tuple.type [concrete = constants.%empty_tuple.type] // CHECK:STDOUT: } // CHECK:STDOUT: %a: ref %empty_tuple.type = ref_binding a, %a.var [concrete = %a.var] // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %b.patt: %pattern_type.7ce = ref_binding_pattern b [concrete] // CHECK:STDOUT: %b.var_patt: %pattern_type.7ce = var_pattern %b.patt [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %b.var: ref %i32 = var %b.var_patt [concrete] // CHECK:STDOUT: %i32.loc53: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %b: ref %i32 = ref_binding b, %b.var [concrete = %b.var] // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %c.patt: %pattern_type.688 = ref_binding_pattern c [concrete] // CHECK:STDOUT: %c.var_patt: %pattern_type.688 = var_pattern %c.patt [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %c.var: ref %struct_type.c = var %c.var_patt [concrete] // CHECK:STDOUT: %.loc54: type = splice_block %struct_type.c [concrete = constants.%struct_type.c] { // CHECK:STDOUT: %i32.loc54: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %struct_type.c: type = struct_type {.c: %i32} [concrete = constants.%struct_type.c] // CHECK:STDOUT: } // CHECK:STDOUT: %c: ref %struct_type.c = ref_binding c, %c.var [concrete = %c.var] // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %d.patt: %pattern_type.cb1 = ref_binding_pattern d [concrete] // CHECK:STDOUT: %d.var_patt: %pattern_type.cb1 = var_pattern %d.patt [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %d.var: ref %empty_tuple.type = var %d.var_patt [concrete] // CHECK:STDOUT: %.loc55_9.1: type = splice_block %.loc55_9.3 [concrete = constants.%empty_tuple.type] { // CHECK:STDOUT: %.loc55_9.2: %empty_tuple.type = tuple_literal () [concrete = constants.%empty_tuple] // CHECK:STDOUT: %.loc55_9.3: type = converted %.loc55_9.2, constants.%empty_tuple.type [concrete = constants.%empty_tuple.type] // CHECK:STDOUT: } // CHECK:STDOUT: %d: ref %empty_tuple.type = ref_binding d, %d.var [concrete = %d.var] // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %e.patt: %pattern_type.cb1 = ref_binding_pattern e [concrete] // CHECK:STDOUT: %e.var_patt: %pattern_type.cb1 = var_pattern %e.patt [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %e.var: ref %empty_tuple.type = var %e.var_patt [concrete] // CHECK:STDOUT: %.loc56_9.1: type = splice_block %.loc56_9.3 [concrete = constants.%empty_tuple.type] { // CHECK:STDOUT: %.loc56_9.2: %empty_tuple.type = tuple_literal () [concrete = constants.%empty_tuple] // CHECK:STDOUT: %.loc56_9.3: type = converted %.loc56_9.2, constants.%empty_tuple.type [concrete = constants.%empty_tuple.type] // CHECK:STDOUT: } // CHECK:STDOUT: %e: ref %empty_tuple.type = ref_binding e, %e.var [concrete = %e.var] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @A [from "api.carbon"]; // CHECK:STDOUT: // CHECK:STDOUT: fn @B [from "api.carbon"]; // CHECK:STDOUT: // CHECK:STDOUT: fn @C [from "api.carbon"]; // CHECK:STDOUT: // CHECK:STDOUT: extern fn @D [from "api.carbon"]; // CHECK:STDOUT: // CHECK:STDOUT: fn @E [from "api.carbon"]; // CHECK:STDOUT: // CHECK:STDOUT: fn @__global_init() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %A.ref: %A.type = name_ref A, file.%A.decl [concrete = constants.%A] // CHECK:STDOUT: %A.call: init %empty_tuple.type = call %A.ref() // CHECK:STDOUT: assign file.%a.var, %A.call // CHECK:STDOUT: %B.ref: %B.type = name_ref B, file.%B.decl [concrete = constants.%B] // CHECK:STDOUT: %int_1.loc53: Core.IntLiteral = int_value 1 [concrete = constants.%int_1.5b8] // CHECK:STDOUT: %impl.elem0.loc53: %.545 = impl_witness_access constants.%ImplicitAs.impl_witness.6bc, element0 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5] // CHECK:STDOUT: %bound_method.loc53_16.1: = bound_method %int_1.loc53, %impl.elem0.loc53 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.bound] // CHECK:STDOUT: %specific_fn.loc53: = specific_function %impl.elem0.loc53, @Core.IntLiteral.as.ImplicitAs.impl.Convert(constants.%int_32) [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn] // CHECK:STDOUT: %bound_method.loc53_16.2: = bound_method %int_1.loc53, %specific_fn.loc53 [concrete = constants.%bound_method] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc53: init %i32 = call %bound_method.loc53_16.2(%int_1.loc53) [concrete = constants.%int_1.5d2] // CHECK:STDOUT: %.loc53_16.1: %i32 = value_of_initializer %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc53 [concrete = constants.%int_1.5d2] // CHECK:STDOUT: %.loc53_16.2: %i32 = converted %int_1.loc53, %.loc53_16.1 [concrete = constants.%int_1.5d2] // CHECK:STDOUT: %B.call: init %i32 = call %B.ref(%.loc53_16.2) // CHECK:STDOUT: assign file.%b.var, %B.call // CHECK:STDOUT: %C.ref: %C.type = name_ref C, file.%C.decl [concrete = constants.%C] // CHECK:STDOUT: %int_1.loc54: Core.IntLiteral = int_value 1 [concrete = constants.%int_1.5b8] // CHECK:STDOUT: %.loc54_25.1: %tuple.type.985 = tuple_literal (%int_1.loc54) [concrete = constants.%tuple.378] // CHECK:STDOUT: %impl.elem0.loc54: %.545 = impl_witness_access constants.%ImplicitAs.impl_witness.6bc, element0 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5] // CHECK:STDOUT: %bound_method.loc54_25.1: = bound_method %int_1.loc54, %impl.elem0.loc54 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.bound] // CHECK:STDOUT: %specific_fn.loc54: = specific_function %impl.elem0.loc54, @Core.IntLiteral.as.ImplicitAs.impl.Convert(constants.%int_32) [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn] // CHECK:STDOUT: %bound_method.loc54_25.2: = bound_method %int_1.loc54, %specific_fn.loc54 [concrete = constants.%bound_method] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc54: init %i32 = call %bound_method.loc54_25.2(%int_1.loc54) [concrete = constants.%int_1.5d2] // CHECK:STDOUT: %.loc54_25.2: %i32 = value_of_initializer %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc54 [concrete = constants.%int_1.5d2] // CHECK:STDOUT: %.loc54_25.3: %i32 = converted %int_1.loc54, %.loc54_25.2 [concrete = constants.%int_1.5d2] // CHECK:STDOUT: %tuple: %tuple.type.a1c = tuple_value (%.loc54_25.3) [concrete = constants.%tuple.246] // CHECK:STDOUT: %.loc54_25.4: %tuple.type.a1c = converted %.loc54_25.1, %tuple [concrete = constants.%tuple.246] // CHECK:STDOUT: %C.call: init %struct_type.c = call %C.ref(%.loc54_25.4) // CHECK:STDOUT: assign file.%c.var, %C.call // CHECK:STDOUT: %D.ref: %D.type = name_ref D, file.%D.decl [concrete = constants.%D] // CHECK:STDOUT: %D.call: init %empty_tuple.type = call %D.ref() // CHECK:STDOUT: assign file.%d.var, %D.call // CHECK:STDOUT: %NS.ref: = name_ref NS, imports.%NS [concrete = imports.%NS] // CHECK:STDOUT: %E.ref: %E.type = name_ref E, file.%E.decl [concrete = constants.%E] // CHECK:STDOUT: %E.call: init %empty_tuple.type = call %E.ref() // CHECK:STDOUT: assign file.%e.var, %E.call // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- redecl_extern_api.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %A.type: type = fn_type @A [concrete] // CHECK:STDOUT: %A: %A.type = struct_value () [concrete] // CHECK:STDOUT: %B.type: type = fn_type @B [concrete] // CHECK:STDOUT: %B: %B.type = struct_value () [concrete] // CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [concrete] // CHECK:STDOUT: %Int.type: type = generic_class_type @Int [concrete] // CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] // CHECK:STDOUT: %Int.generic: %Int.type = struct_value () [concrete] // CHECK:STDOUT: %i32: type = class_type @Int, @Int(%int_32) [concrete] // CHECK:STDOUT: %pattern_type.501: type = pattern_type %i32 [concrete] // CHECK:STDOUT: %.4ca: Core.Form = init_form %i32 [concrete] // CHECK:STDOUT: %C.type: type = fn_type @C [concrete] // CHECK:STDOUT: %C: %C.type = struct_value () [concrete] // CHECK:STDOUT: %struct_type.c: type = struct_type {.c: %i32} [concrete] // CHECK:STDOUT: %pattern_type.f5f: type = pattern_type %struct_type.c [concrete] // CHECK:STDOUT: %tuple.type.dd4: type = tuple_type (%i32) [concrete] // CHECK:STDOUT: %.dce: Core.Form = init_form %struct_type.c [concrete] // CHECK:STDOUT: %D.type: type = fn_type @D [concrete] // CHECK:STDOUT: %D: %D.type = struct_value () [concrete] // CHECK:STDOUT: %E.type: type = fn_type @E [concrete] // CHECK:STDOUT: %E: %E.type = struct_value () [concrete] // CHECK:STDOUT: %tuple.type.85c: type = tuple_type (type) [concrete] // CHECK:STDOUT: %tuple.38e: %tuple.type.85c = tuple_value (%i32) [concrete] // CHECK:STDOUT: %empty_tuple: %empty_tuple.type = tuple_value () [concrete] // CHECK:STDOUT: %pattern_type.cb1: type = pattern_type %empty_tuple.type [concrete] // CHECK:STDOUT: %int_1.5b8: Core.IntLiteral = int_value 1 [concrete] // CHECK:STDOUT: %ImplicitAs.type.cc7: type = generic_interface_type @ImplicitAs [concrete] // CHECK:STDOUT: %ImplicitAs.generic: %ImplicitAs.type.cc7 = struct_value () [concrete] // CHECK:STDOUT: %ImplicitAs.type.cf3: type = facet_type <@ImplicitAs, @ImplicitAs(%i32)> [concrete] // CHECK:STDOUT: %To: Core.IntLiteral = symbolic_binding To, 0 [symbolic] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.004: type = fn_type @Core.IntLiteral.as.ImplicitAs.impl.Convert, @Core.IntLiteral.as.ImplicitAs.impl(%To) [symbolic] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.255: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.004 = struct_value () [symbolic] // CHECK:STDOUT: %ImplicitAs.impl_witness.58d: = impl_witness imports.%ImplicitAs.impl_witness_table.e45, @Core.IntLiteral.as.ImplicitAs.impl(%int_32) [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.199: type = fn_type @Core.IntLiteral.as.ImplicitAs.impl.Convert, @Core.IntLiteral.as.ImplicitAs.impl(%int_32) [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.5f4: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.199 = struct_value () [concrete] // CHECK:STDOUT: %ImplicitAs.facet: %ImplicitAs.type.cf3 = facet_value Core.IntLiteral, (%ImplicitAs.impl_witness.58d) [concrete] // CHECK:STDOUT: %ImplicitAs.WithSelf.Convert.type.979: type = fn_type @ImplicitAs.WithSelf.Convert, @ImplicitAs.WithSelf(%i32, %ImplicitAs.facet) [concrete] // CHECK:STDOUT: %.7c3: type = fn_type_with_self_type %ImplicitAs.WithSelf.Convert.type.979, %ImplicitAs.facet [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.bound: = bound_method %int_1.5b8, %Core.IntLiteral.as.ImplicitAs.impl.Convert.5f4 [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn: = specific_function %Core.IntLiteral.as.ImplicitAs.impl.Convert.5f4, @Core.IntLiteral.as.ImplicitAs.impl.Convert(%int_32) [concrete] // CHECK:STDOUT: %bound_method: = bound_method %int_1.5b8, %Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn [concrete] // CHECK:STDOUT: %int_1.47b: %i32 = int_value 1 [concrete] // CHECK:STDOUT: %tuple.type.985: type = tuple_type (Core.IntLiteral) [concrete] // CHECK:STDOUT: %tuple.378: %tuple.type.985 = tuple_value (%int_1.5b8) [concrete] // CHECK:STDOUT: %tuple.7dd: %tuple.type.dd4 = tuple_value (%int_1.47b) [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Main.NS: = import_ref Main//extern_api, NS, loaded // CHECK:STDOUT: %NS: = namespace %Main.NS, [concrete] { // CHECK:STDOUT: .E = file.%E.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Core.ece: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: .Int = %Core.Int // CHECK:STDOUT: .ImplicitAs = %Core.ImplicitAs // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: %Core.Int: %Int.type = import_ref Core//prelude/parts/int, Int, loaded [concrete = constants.%Int.generic] // CHECK:STDOUT: %Core.ImplicitAs: %ImplicitAs.type.cc7 = import_ref Core//prelude/parts/as, ImplicitAs, loaded [concrete = constants.%ImplicitAs.generic] // CHECK:STDOUT: %Core.import_ref.b25: @Core.IntLiteral.as.ImplicitAs.impl.%Core.IntLiteral.as.ImplicitAs.impl.Convert.type (%Core.IntLiteral.as.ImplicitAs.impl.Convert.type.004) = import_ref Core//prelude/parts/int, loc{{\d+_\d+}}, loaded [symbolic = @Core.IntLiteral.as.ImplicitAs.impl.%Core.IntLiteral.as.ImplicitAs.impl.Convert (constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.255)] // CHECK:STDOUT: %ImplicitAs.impl_witness_table.e45 = impl_witness_table (%Core.import_ref.b25), @Core.IntLiteral.as.ImplicitAs.impl [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .A = %A.decl // CHECK:STDOUT: .B = %B.decl // CHECK:STDOUT: .C = %C.decl // CHECK:STDOUT: .D = %D.decl // CHECK:STDOUT: .NS = imports.%NS // CHECK:STDOUT: .Core = imports.%Core.ece // CHECK:STDOUT: .a = %a // CHECK:STDOUT: .b = %b // CHECK:STDOUT: .c = %c // CHECK:STDOUT: .d = %d // CHECK:STDOUT: .e = %e // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %default.import = import // CHECK:STDOUT: %A.decl: %A.type = fn_decl @A [concrete = constants.%A] {} {} // CHECK:STDOUT: %B.decl: %B.type = fn_decl @B [concrete = constants.%B] {} { // CHECK:STDOUT: %i32.loc7_24: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %.loc7: Core.Form = init_form %i32.loc7_24 [concrete = constants.%.4ca] // CHECK:STDOUT: %b.param: %i32 = value_param call_param0 // CHECK:STDOUT: %i32.loc7_16: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %b: %i32 = value_binding b, %b.param // CHECK:STDOUT: %return.param: ref %i32 = out_param call_param1 // CHECK:STDOUT: %return: ref %i32 = return_slot %return.param // CHECK:STDOUT: } // CHECK:STDOUT: %C.decl: %C.type = fn_decl @C [concrete = constants.%C] {} { // CHECK:STDOUT: %i32.loc8_32: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %struct_type.c: type = struct_type {.c: %i32} [concrete = constants.%struct_type.c] // CHECK:STDOUT: %.loc8_35: Core.Form = init_form %struct_type.c [concrete = constants.%.dce] // CHECK:STDOUT: %c.param: %tuple.type.dd4 = value_param call_param0 // CHECK:STDOUT: %.loc8_21.1: type = splice_block %.loc8_21.3 [concrete = constants.%tuple.type.dd4] { // CHECK:STDOUT: %i32.loc8_17: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %.loc8_21.2: %tuple.type.85c = tuple_literal (%i32.loc8_17) [concrete = constants.%tuple.38e] // CHECK:STDOUT: %.loc8_21.3: type = converted %.loc8_21.2, constants.%tuple.type.dd4 [concrete = constants.%tuple.type.dd4] // CHECK:STDOUT: } // CHECK:STDOUT: %c: %tuple.type.dd4 = value_binding c, %c.param // CHECK:STDOUT: %return.param: ref %struct_type.c = out_param call_param1 // CHECK:STDOUT: %return: ref %struct_type.c = return_slot %return.param // CHECK:STDOUT: } // CHECK:STDOUT: %D.decl: %D.type = fn_decl @D [concrete = constants.%D] {} {} // CHECK:STDOUT: %E.decl: %E.type = fn_decl @E [concrete = constants.%E] {} {} // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %a.patt: %pattern_type.cb1 = ref_binding_pattern a [concrete] // CHECK:STDOUT: %a.var_patt: %pattern_type.cb1 = var_pattern %a.patt [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %a.var: ref %empty_tuple.type = var %a.var_patt [concrete] // CHECK:STDOUT: %.loc12_9.1: type = splice_block %.loc12_9.3 [concrete = constants.%empty_tuple.type] { // CHECK:STDOUT: %.loc12_9.2: %empty_tuple.type = tuple_literal () [concrete = constants.%empty_tuple] // CHECK:STDOUT: %.loc12_9.3: type = converted %.loc12_9.2, constants.%empty_tuple.type [concrete = constants.%empty_tuple.type] // CHECK:STDOUT: } // CHECK:STDOUT: %a: ref %empty_tuple.type = ref_binding a, %a.var [concrete = %a.var] // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %b.patt: %pattern_type.501 = ref_binding_pattern b [concrete] // CHECK:STDOUT: %b.var_patt: %pattern_type.501 = var_pattern %b.patt [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %b.var: ref %i32 = var %b.var_patt [concrete] // CHECK:STDOUT: %i32.loc13: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %b: ref %i32 = ref_binding b, %b.var [concrete = %b.var] // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %c.patt: %pattern_type.f5f = ref_binding_pattern c [concrete] // CHECK:STDOUT: %c.var_patt: %pattern_type.f5f = var_pattern %c.patt [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %c.var: ref %struct_type.c = var %c.var_patt [concrete] // CHECK:STDOUT: %.loc14: type = splice_block %struct_type.c [concrete = constants.%struct_type.c] { // CHECK:STDOUT: %i32.loc14: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %struct_type.c: type = struct_type {.c: %i32} [concrete = constants.%struct_type.c] // CHECK:STDOUT: } // CHECK:STDOUT: %c: ref %struct_type.c = ref_binding c, %c.var [concrete = %c.var] // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %d.patt: %pattern_type.cb1 = ref_binding_pattern d [concrete] // CHECK:STDOUT: %d.var_patt: %pattern_type.cb1 = var_pattern %d.patt [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %d.var: ref %empty_tuple.type = var %d.var_patt [concrete] // CHECK:STDOUT: %.loc15_9.1: type = splice_block %.loc15_9.3 [concrete = constants.%empty_tuple.type] { // CHECK:STDOUT: %.loc15_9.2: %empty_tuple.type = tuple_literal () [concrete = constants.%empty_tuple] // CHECK:STDOUT: %.loc15_9.3: type = converted %.loc15_9.2, constants.%empty_tuple.type [concrete = constants.%empty_tuple.type] // CHECK:STDOUT: } // CHECK:STDOUT: %d: ref %empty_tuple.type = ref_binding d, %d.var [concrete = %d.var] // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %e.patt: %pattern_type.cb1 = ref_binding_pattern e [concrete] // CHECK:STDOUT: %e.var_patt: %pattern_type.cb1 = var_pattern %e.patt [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %e.var: ref %empty_tuple.type = var %e.var_patt [concrete] // CHECK:STDOUT: %.loc16_9.1: type = splice_block %.loc16_9.3 [concrete = constants.%empty_tuple.type] { // CHECK:STDOUT: %.loc16_9.2: %empty_tuple.type = tuple_literal () [concrete = constants.%empty_tuple] // CHECK:STDOUT: %.loc16_9.3: type = converted %.loc16_9.2, constants.%empty_tuple.type [concrete = constants.%empty_tuple.type] // CHECK:STDOUT: } // CHECK:STDOUT: %e: ref %empty_tuple.type = ref_binding e, %e.var [concrete = %e.var] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: extern fn @A; // CHECK:STDOUT: // CHECK:STDOUT: extern fn @B; // CHECK:STDOUT: // CHECK:STDOUT: extern fn @C; // CHECK:STDOUT: // CHECK:STDOUT: extern fn @D; // CHECK:STDOUT: // CHECK:STDOUT: extern fn @E; // CHECK:STDOUT: // CHECK:STDOUT: fn @__global_init() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %A.ref: %A.type = name_ref A, file.%A.decl [concrete = constants.%A] // CHECK:STDOUT: %A.call: init %empty_tuple.type = call %A.ref() // CHECK:STDOUT: assign file.%a.var, %A.call // CHECK:STDOUT: %B.ref: %B.type = name_ref B, file.%B.decl [concrete = constants.%B] // CHECK:STDOUT: %int_1.loc13: Core.IntLiteral = int_value 1 [concrete = constants.%int_1.5b8] // CHECK:STDOUT: %impl.elem0.loc13: %.7c3 = impl_witness_access constants.%ImplicitAs.impl_witness.58d, element0 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.5f4] // CHECK:STDOUT: %bound_method.loc13_16.1: = bound_method %int_1.loc13, %impl.elem0.loc13 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.bound] // CHECK:STDOUT: %specific_fn.loc13: = specific_function %impl.elem0.loc13, @Core.IntLiteral.as.ImplicitAs.impl.Convert(constants.%int_32) [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn] // CHECK:STDOUT: %bound_method.loc13_16.2: = bound_method %int_1.loc13, %specific_fn.loc13 [concrete = constants.%bound_method] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc13: init %i32 = call %bound_method.loc13_16.2(%int_1.loc13) [concrete = constants.%int_1.47b] // CHECK:STDOUT: %.loc13_16.1: %i32 = value_of_initializer %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc13 [concrete = constants.%int_1.47b] // CHECK:STDOUT: %.loc13_16.2: %i32 = converted %int_1.loc13, %.loc13_16.1 [concrete = constants.%int_1.47b] // CHECK:STDOUT: %B.call: init %i32 = call %B.ref(%.loc13_16.2) // CHECK:STDOUT: assign file.%b.var, %B.call // CHECK:STDOUT: %C.ref: %C.type = name_ref C, file.%C.decl [concrete = constants.%C] // CHECK:STDOUT: %int_1.loc14: Core.IntLiteral = int_value 1 [concrete = constants.%int_1.5b8] // CHECK:STDOUT: %.loc14_25.1: %tuple.type.985 = tuple_literal (%int_1.loc14) [concrete = constants.%tuple.378] // CHECK:STDOUT: %impl.elem0.loc14: %.7c3 = impl_witness_access constants.%ImplicitAs.impl_witness.58d, element0 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.5f4] // CHECK:STDOUT: %bound_method.loc14_25.1: = bound_method %int_1.loc14, %impl.elem0.loc14 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.bound] // CHECK:STDOUT: %specific_fn.loc14: = specific_function %impl.elem0.loc14, @Core.IntLiteral.as.ImplicitAs.impl.Convert(constants.%int_32) [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn] // CHECK:STDOUT: %bound_method.loc14_25.2: = bound_method %int_1.loc14, %specific_fn.loc14 [concrete = constants.%bound_method] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc14: init %i32 = call %bound_method.loc14_25.2(%int_1.loc14) [concrete = constants.%int_1.47b] // CHECK:STDOUT: %.loc14_25.2: %i32 = value_of_initializer %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc14 [concrete = constants.%int_1.47b] // CHECK:STDOUT: %.loc14_25.3: %i32 = converted %int_1.loc14, %.loc14_25.2 [concrete = constants.%int_1.47b] // CHECK:STDOUT: %tuple: %tuple.type.dd4 = tuple_value (%.loc14_25.3) [concrete = constants.%tuple.7dd] // CHECK:STDOUT: %.loc14_25.4: %tuple.type.dd4 = converted %.loc14_25.1, %tuple [concrete = constants.%tuple.7dd] // CHECK:STDOUT: %C.call: init %struct_type.c = call %C.ref(%.loc14_25.4) // CHECK:STDOUT: assign file.%c.var, %C.call // CHECK:STDOUT: %D.ref: %D.type = name_ref D, file.%D.decl [concrete = constants.%D] // CHECK:STDOUT: %D.call: init %empty_tuple.type = call %D.ref() // CHECK:STDOUT: assign file.%d.var, %D.call // CHECK:STDOUT: %NS.ref: = name_ref NS, imports.%NS [concrete = imports.%NS] // CHECK:STDOUT: %E.ref: %E.type = name_ref E, file.%E.decl [concrete = constants.%E] // CHECK:STDOUT: %E.call: init %empty_tuple.type = call %E.ref() // CHECK:STDOUT: assign file.%e.var, %E.call // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- fail_merge.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] // CHECK:STDOUT: %empty_tuple: %empty_tuple.type = tuple_value () [concrete] // CHECK:STDOUT: %pattern_type.cb1: type = pattern_type %empty_tuple.type [concrete] // CHECK:STDOUT: %A.type: type = fn_type @A [concrete] // CHECK:STDOUT: %A: %A.type = struct_value () [concrete] // CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [concrete] // CHECK:STDOUT: %Int.type: type = generic_class_type @Int [concrete] // CHECK:STDOUT: %Int.generic: %Int.type = struct_value () [concrete] // CHECK:STDOUT: %i32: type = class_type @Int, @Int(%int_32) [concrete] // CHECK:STDOUT: %pattern_type.7ce: type = pattern_type %i32 [concrete] // CHECK:STDOUT: %B.type: type = fn_type @B [concrete] // CHECK:STDOUT: %B: %B.type = struct_value () [concrete] // CHECK:STDOUT: %int_1.5b8: Core.IntLiteral = int_value 1 [concrete] // CHECK:STDOUT: %ImplicitAs.type.cc7: type = generic_interface_type @ImplicitAs [concrete] // CHECK:STDOUT: %ImplicitAs.generic: %ImplicitAs.type.cc7 = struct_value () [concrete] // CHECK:STDOUT: %ImplicitAs.type.e8c: type = facet_type <@ImplicitAs, @ImplicitAs(%i32)> [concrete] // CHECK:STDOUT: %To: Core.IntLiteral = symbolic_binding To, 0 [symbolic] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.4e6: type = fn_type @Core.IntLiteral.as.ImplicitAs.impl.Convert, @Core.IntLiteral.as.ImplicitAs.impl(%To) [symbolic] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.3c2: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.4e6 = struct_value () [symbolic] // CHECK:STDOUT: %ImplicitAs.impl_witness.6bc: = impl_witness imports.%ImplicitAs.impl_witness_table.74f, @Core.IntLiteral.as.ImplicitAs.impl(%int_32) [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.e0d: type = fn_type @Core.IntLiteral.as.ImplicitAs.impl.Convert, @Core.IntLiteral.as.ImplicitAs.impl(%int_32) [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.e0d = struct_value () [concrete] // CHECK:STDOUT: %ImplicitAs.facet: %ImplicitAs.type.e8c = facet_value Core.IntLiteral, (%ImplicitAs.impl_witness.6bc) [concrete] // CHECK:STDOUT: %ImplicitAs.WithSelf.Convert.type.b37: type = fn_type @ImplicitAs.WithSelf.Convert, @ImplicitAs.WithSelf(%i32, %ImplicitAs.facet) [concrete] // CHECK:STDOUT: %.545: type = fn_type_with_self_type %ImplicitAs.WithSelf.Convert.type.b37, %ImplicitAs.facet [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.bound: = bound_method %int_1.5b8, %Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5 [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn: = specific_function %Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5, @Core.IntLiteral.as.ImplicitAs.impl.Convert(%int_32) [concrete] // CHECK:STDOUT: %bound_method: = bound_method %int_1.5b8, %Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn [concrete] // CHECK:STDOUT: %int_1.5d2: %i32 = int_value 1 [concrete] // CHECK:STDOUT: %struct_type.c: type = struct_type {.c: %i32} [concrete] // CHECK:STDOUT: %pattern_type.688: type = pattern_type %struct_type.c [concrete] // CHECK:STDOUT: %C.type: type = fn_type @C [concrete] // CHECK:STDOUT: %C: %C.type = struct_value () [concrete] // CHECK:STDOUT: %tuple.type.a1c: type = tuple_type (%i32) [concrete] // CHECK:STDOUT: %tuple.type.985: type = tuple_type (Core.IntLiteral) [concrete] // CHECK:STDOUT: %tuple.378: %tuple.type.985 = tuple_value (%int_1.5b8) [concrete] // CHECK:STDOUT: %tuple.246: %tuple.type.a1c = tuple_value (%int_1.5d2) [concrete] // CHECK:STDOUT: %D.type: type = fn_type @D [concrete] // CHECK:STDOUT: %D: %D.type = struct_value () [concrete] // CHECK:STDOUT: %E.type: type = fn_type @E [concrete] // CHECK:STDOUT: %E: %E.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Main.A: %A.type = import_ref Main//api, A, loaded [concrete = constants.%A] // CHECK:STDOUT: %Main.B: %B.type = import_ref Main//api, B, loaded [concrete = constants.%B] // CHECK:STDOUT: %Main.C: %C.type = import_ref Main//api, C, loaded [concrete = constants.%C] // CHECK:STDOUT: %Main.D: %D.type = import_ref Main//api, D, loaded [concrete = constants.%D] // CHECK:STDOUT: %Main.NS: = import_ref Main//api, NS, loaded // CHECK:STDOUT: %NS: = namespace %Main.NS, [concrete] { // CHECK:STDOUT: .E = %Main.E // CHECK:STDOUT: } // CHECK:STDOUT: %Main.E: %E.type = import_ref Main//api, E, loaded [concrete = constants.%E] // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: .Int = %Core.Int // CHECK:STDOUT: .ImplicitAs = %Core.ImplicitAs // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: %Core.Int: %Int.type = import_ref Core//prelude/parts/int, Int, loaded [concrete = constants.%Int.generic] // CHECK:STDOUT: %Core.ImplicitAs: %ImplicitAs.type.cc7 = import_ref Core//prelude/parts/as, ImplicitAs, loaded [concrete = constants.%ImplicitAs.generic] // CHECK:STDOUT: %Core.import_ref.42d: @Core.IntLiteral.as.ImplicitAs.impl.%Core.IntLiteral.as.ImplicitAs.impl.Convert.type (%Core.IntLiteral.as.ImplicitAs.impl.Convert.type.4e6) = import_ref Core//prelude/parts/int, loc{{\d+_\d+}}, loaded [symbolic = @Core.IntLiteral.as.ImplicitAs.impl.%Core.IntLiteral.as.ImplicitAs.impl.Convert (constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.3c2)] // CHECK:STDOUT: %ImplicitAs.impl_witness_table.74f = impl_witness_table (%Core.import_ref.42d), @Core.IntLiteral.as.ImplicitAs.impl [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .A = imports.%Main.A // CHECK:STDOUT: .B = imports.%Main.B // CHECK:STDOUT: .C = imports.%Main.C // CHECK:STDOUT: .D = imports.%Main.D // CHECK:STDOUT: .NS = imports.%NS // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .a = %a // CHECK:STDOUT: .b = %b // CHECK:STDOUT: .c = %c // CHECK:STDOUT: .d = %d // CHECK:STDOUT: .e = %e // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %default.import = import // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %a.patt: %pattern_type.cb1 = ref_binding_pattern a [concrete] // CHECK:STDOUT: %a.var_patt: %pattern_type.cb1 = var_pattern %a.patt [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %a.var: ref %empty_tuple.type = var %a.var_patt [concrete] // CHECK:STDOUT: %.loc52_9.1: type = splice_block %.loc52_9.3 [concrete = constants.%empty_tuple.type] { // CHECK:STDOUT: %.loc52_9.2: %empty_tuple.type = tuple_literal () [concrete = constants.%empty_tuple] // CHECK:STDOUT: %.loc52_9.3: type = converted %.loc52_9.2, constants.%empty_tuple.type [concrete = constants.%empty_tuple.type] // CHECK:STDOUT: } // CHECK:STDOUT: %a: ref %empty_tuple.type = ref_binding a, %a.var [concrete = %a.var] // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %b.patt: %pattern_type.7ce = ref_binding_pattern b [concrete] // CHECK:STDOUT: %b.var_patt: %pattern_type.7ce = var_pattern %b.patt [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %b.var: ref %i32 = var %b.var_patt [concrete] // CHECK:STDOUT: %i32.loc53: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %b: ref %i32 = ref_binding b, %b.var [concrete = %b.var] // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %c.patt: %pattern_type.688 = ref_binding_pattern c [concrete] // CHECK:STDOUT: %c.var_patt: %pattern_type.688 = var_pattern %c.patt [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %c.var: ref %struct_type.c = var %c.var_patt [concrete] // CHECK:STDOUT: %.loc54: type = splice_block %struct_type.c [concrete = constants.%struct_type.c] { // CHECK:STDOUT: %i32.loc54: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %struct_type.c: type = struct_type {.c: %i32} [concrete = constants.%struct_type.c] // CHECK:STDOUT: } // CHECK:STDOUT: %c: ref %struct_type.c = ref_binding c, %c.var [concrete = %c.var] // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %d.patt: %pattern_type.cb1 = ref_binding_pattern d [concrete] // CHECK:STDOUT: %d.var_patt: %pattern_type.cb1 = var_pattern %d.patt [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %d.var: ref %empty_tuple.type = var %d.var_patt [concrete] // CHECK:STDOUT: %.loc55_9.1: type = splice_block %.loc55_9.3 [concrete = constants.%empty_tuple.type] { // CHECK:STDOUT: %.loc55_9.2: %empty_tuple.type = tuple_literal () [concrete = constants.%empty_tuple] // CHECK:STDOUT: %.loc55_9.3: type = converted %.loc55_9.2, constants.%empty_tuple.type [concrete = constants.%empty_tuple.type] // CHECK:STDOUT: } // CHECK:STDOUT: %d: ref %empty_tuple.type = ref_binding d, %d.var [concrete = %d.var] // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %e.patt: %pattern_type.cb1 = ref_binding_pattern e [concrete] // CHECK:STDOUT: %e.var_patt: %pattern_type.cb1 = var_pattern %e.patt [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %e.var: ref %empty_tuple.type = var %e.var_patt [concrete] // CHECK:STDOUT: %.loc56_9.1: type = splice_block %.loc56_9.3 [concrete = constants.%empty_tuple.type] { // CHECK:STDOUT: %.loc56_9.2: %empty_tuple.type = tuple_literal () [concrete = constants.%empty_tuple] // CHECK:STDOUT: %.loc56_9.3: type = converted %.loc56_9.2, constants.%empty_tuple.type [concrete = constants.%empty_tuple.type] // CHECK:STDOUT: } // CHECK:STDOUT: %e: ref %empty_tuple.type = ref_binding e, %e.var [concrete = %e.var] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @A [from "api.carbon"]; // CHECK:STDOUT: // CHECK:STDOUT: fn @B [from "api.carbon"]; // CHECK:STDOUT: // CHECK:STDOUT: fn @C [from "api.carbon"]; // CHECK:STDOUT: // CHECK:STDOUT: extern fn @D [from "api.carbon"]; // CHECK:STDOUT: // CHECK:STDOUT: fn @E [from "api.carbon"]; // CHECK:STDOUT: // CHECK:STDOUT: fn @__global_init() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %A.ref: %A.type = name_ref A, imports.%Main.A [concrete = constants.%A] // CHECK:STDOUT: %A.call: init %empty_tuple.type = call %A.ref() // CHECK:STDOUT: assign file.%a.var, %A.call // CHECK:STDOUT: %B.ref: %B.type = name_ref B, imports.%Main.B [concrete = constants.%B] // CHECK:STDOUT: %int_1.loc53: Core.IntLiteral = int_value 1 [concrete = constants.%int_1.5b8] // CHECK:STDOUT: %impl.elem0.loc53: %.545 = impl_witness_access constants.%ImplicitAs.impl_witness.6bc, element0 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5] // CHECK:STDOUT: %bound_method.loc53_16.1: = bound_method %int_1.loc53, %impl.elem0.loc53 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.bound] // CHECK:STDOUT: %specific_fn.loc53: = specific_function %impl.elem0.loc53, @Core.IntLiteral.as.ImplicitAs.impl.Convert(constants.%int_32) [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn] // CHECK:STDOUT: %bound_method.loc53_16.2: = bound_method %int_1.loc53, %specific_fn.loc53 [concrete = constants.%bound_method] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc53: init %i32 = call %bound_method.loc53_16.2(%int_1.loc53) [concrete = constants.%int_1.5d2] // CHECK:STDOUT: %.loc53_16.1: %i32 = value_of_initializer %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc53 [concrete = constants.%int_1.5d2] // CHECK:STDOUT: %.loc53_16.2: %i32 = converted %int_1.loc53, %.loc53_16.1 [concrete = constants.%int_1.5d2] // CHECK:STDOUT: %B.call: init %i32 = call %B.ref(%.loc53_16.2) // CHECK:STDOUT: assign file.%b.var, %B.call // CHECK:STDOUT: %C.ref: %C.type = name_ref C, imports.%Main.C [concrete = constants.%C] // CHECK:STDOUT: %int_1.loc54: Core.IntLiteral = int_value 1 [concrete = constants.%int_1.5b8] // CHECK:STDOUT: %.loc54_25.1: %tuple.type.985 = tuple_literal (%int_1.loc54) [concrete = constants.%tuple.378] // CHECK:STDOUT: %impl.elem0.loc54: %.545 = impl_witness_access constants.%ImplicitAs.impl_witness.6bc, element0 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5] // CHECK:STDOUT: %bound_method.loc54_25.1: = bound_method %int_1.loc54, %impl.elem0.loc54 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.bound] // CHECK:STDOUT: %specific_fn.loc54: = specific_function %impl.elem0.loc54, @Core.IntLiteral.as.ImplicitAs.impl.Convert(constants.%int_32) [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn] // CHECK:STDOUT: %bound_method.loc54_25.2: = bound_method %int_1.loc54, %specific_fn.loc54 [concrete = constants.%bound_method] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc54: init %i32 = call %bound_method.loc54_25.2(%int_1.loc54) [concrete = constants.%int_1.5d2] // CHECK:STDOUT: %.loc54_25.2: %i32 = value_of_initializer %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc54 [concrete = constants.%int_1.5d2] // CHECK:STDOUT: %.loc54_25.3: %i32 = converted %int_1.loc54, %.loc54_25.2 [concrete = constants.%int_1.5d2] // CHECK:STDOUT: %tuple: %tuple.type.a1c = tuple_value (%.loc54_25.3) [concrete = constants.%tuple.246] // CHECK:STDOUT: %.loc54_25.4: %tuple.type.a1c = converted %.loc54_25.1, %tuple [concrete = constants.%tuple.246] // CHECK:STDOUT: %C.call: init %struct_type.c = call %C.ref(%.loc54_25.4) // CHECK:STDOUT: assign file.%c.var, %C.call // CHECK:STDOUT: %D.ref: %D.type = name_ref D, imports.%Main.D [concrete = constants.%D] // CHECK:STDOUT: %D.call: init %empty_tuple.type = call %D.ref() // CHECK:STDOUT: assign file.%d.var, %D.call // CHECK:STDOUT: %NS.ref: = name_ref NS, imports.%NS [concrete = imports.%NS] // CHECK:STDOUT: %E.ref: %E.type = name_ref E, imports.%Main.E [concrete = constants.%E] // CHECK:STDOUT: %E.call: init %empty_tuple.type = call %E.ref() // CHECK:STDOUT: assign file.%e.var, %E.call // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- fail_merge_reverse.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] // CHECK:STDOUT: %empty_tuple: %empty_tuple.type = tuple_value () [concrete] // CHECK:STDOUT: %pattern_type.cb1: type = pattern_type %empty_tuple.type [concrete] // CHECK:STDOUT: %A.type: type = fn_type @A [concrete] // CHECK:STDOUT: %A: %A.type = struct_value () [concrete] // CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [concrete] // CHECK:STDOUT: %Int.type: type = generic_class_type @Int [concrete] // CHECK:STDOUT: %Int.generic: %Int.type = struct_value () [concrete] // CHECK:STDOUT: %i32: type = class_type @Int, @Int(%int_32) [concrete] // CHECK:STDOUT: %pattern_type.7ce: type = pattern_type %i32 [concrete] // CHECK:STDOUT: %B.type: type = fn_type @B [concrete] // CHECK:STDOUT: %B: %B.type = struct_value () [concrete] // CHECK:STDOUT: %int_1.5b8: Core.IntLiteral = int_value 1 [concrete] // CHECK:STDOUT: %ImplicitAs.type.cc7: type = generic_interface_type @ImplicitAs [concrete] // CHECK:STDOUT: %ImplicitAs.generic: %ImplicitAs.type.cc7 = struct_value () [concrete] // CHECK:STDOUT: %ImplicitAs.type.e8c: type = facet_type <@ImplicitAs, @ImplicitAs(%i32)> [concrete] // CHECK:STDOUT: %To: Core.IntLiteral = symbolic_binding To, 0 [symbolic] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.4e6: type = fn_type @Core.IntLiteral.as.ImplicitAs.impl.Convert, @Core.IntLiteral.as.ImplicitAs.impl(%To) [symbolic] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.3c2: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.4e6 = struct_value () [symbolic] // CHECK:STDOUT: %ImplicitAs.impl_witness.6bc: = impl_witness imports.%ImplicitAs.impl_witness_table.74f, @Core.IntLiteral.as.ImplicitAs.impl(%int_32) [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.e0d: type = fn_type @Core.IntLiteral.as.ImplicitAs.impl.Convert, @Core.IntLiteral.as.ImplicitAs.impl(%int_32) [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.e0d = struct_value () [concrete] // CHECK:STDOUT: %ImplicitAs.facet: %ImplicitAs.type.e8c = facet_value Core.IntLiteral, (%ImplicitAs.impl_witness.6bc) [concrete] // CHECK:STDOUT: %ImplicitAs.WithSelf.Convert.type.b37: type = fn_type @ImplicitAs.WithSelf.Convert, @ImplicitAs.WithSelf(%i32, %ImplicitAs.facet) [concrete] // CHECK:STDOUT: %.545: type = fn_type_with_self_type %ImplicitAs.WithSelf.Convert.type.b37, %ImplicitAs.facet [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.bound: = bound_method %int_1.5b8, %Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5 [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn: = specific_function %Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5, @Core.IntLiteral.as.ImplicitAs.impl.Convert(%int_32) [concrete] // CHECK:STDOUT: %bound_method: = bound_method %int_1.5b8, %Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn [concrete] // CHECK:STDOUT: %int_1.5d2: %i32 = int_value 1 [concrete] // CHECK:STDOUT: %struct_type.c: type = struct_type {.c: %i32} [concrete] // CHECK:STDOUT: %pattern_type.688: type = pattern_type %struct_type.c [concrete] // CHECK:STDOUT: %C.type: type = fn_type @C [concrete] // CHECK:STDOUT: %C: %C.type = struct_value () [concrete] // CHECK:STDOUT: %tuple.type.a1c: type = tuple_type (%i32) [concrete] // CHECK:STDOUT: %tuple.type.985: type = tuple_type (Core.IntLiteral) [concrete] // CHECK:STDOUT: %tuple.378: %tuple.type.985 = tuple_value (%int_1.5b8) [concrete] // CHECK:STDOUT: %tuple.246: %tuple.type.a1c = tuple_value (%int_1.5d2) [concrete] // CHECK:STDOUT: %D.type: type = fn_type @D [concrete] // CHECK:STDOUT: %D: %D.type = struct_value () [concrete] // CHECK:STDOUT: %E.type: type = fn_type @E [concrete] // CHECK:STDOUT: %E: %E.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Main.A: %A.type = import_ref Main//extern_api, A, loaded [concrete = constants.%A] // CHECK:STDOUT: %Main.B: %B.type = import_ref Main//extern_api, B, loaded [concrete = constants.%B] // CHECK:STDOUT: %Main.C: %C.type = import_ref Main//extern_api, C, loaded [concrete = constants.%C] // CHECK:STDOUT: %Main.D: %D.type = import_ref Main//extern_api, D, loaded [concrete = constants.%D] // CHECK:STDOUT: %Main.NS: = import_ref Main//extern_api, NS, loaded // CHECK:STDOUT: %NS: = namespace %Main.NS, [concrete] { // CHECK:STDOUT: .E = %Main.E // CHECK:STDOUT: } // CHECK:STDOUT: %Main.E: %E.type = import_ref Main//extern_api, E, loaded [concrete = constants.%E] // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: .Int = %Core.Int // CHECK:STDOUT: .ImplicitAs = %Core.ImplicitAs // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: %Core.Int: %Int.type = import_ref Core//prelude/parts/int, Int, loaded [concrete = constants.%Int.generic] // CHECK:STDOUT: %Core.ImplicitAs: %ImplicitAs.type.cc7 = import_ref Core//prelude/parts/as, ImplicitAs, loaded [concrete = constants.%ImplicitAs.generic] // CHECK:STDOUT: %Core.import_ref.42d: @Core.IntLiteral.as.ImplicitAs.impl.%Core.IntLiteral.as.ImplicitAs.impl.Convert.type (%Core.IntLiteral.as.ImplicitAs.impl.Convert.type.4e6) = import_ref Core//prelude/parts/int, loc{{\d+_\d+}}, loaded [symbolic = @Core.IntLiteral.as.ImplicitAs.impl.%Core.IntLiteral.as.ImplicitAs.impl.Convert (constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.3c2)] // CHECK:STDOUT: %ImplicitAs.impl_witness_table.74f = impl_witness_table (%Core.import_ref.42d), @Core.IntLiteral.as.ImplicitAs.impl [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .A = imports.%Main.A // CHECK:STDOUT: .B = imports.%Main.B // CHECK:STDOUT: .C = imports.%Main.C // CHECK:STDOUT: .D = imports.%Main.D // CHECK:STDOUT: .NS = imports.%NS // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .a = %a // CHECK:STDOUT: .b = %b // CHECK:STDOUT: .c = %c // CHECK:STDOUT: .d = %d // CHECK:STDOUT: .e = %e // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %default.import = import // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %a.patt: %pattern_type.cb1 = ref_binding_pattern a [concrete] // CHECK:STDOUT: %a.var_patt: %pattern_type.cb1 = var_pattern %a.patt [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %a.var: ref %empty_tuple.type = var %a.var_patt [concrete] // CHECK:STDOUT: %.loc52_9.1: type = splice_block %.loc52_9.3 [concrete = constants.%empty_tuple.type] { // CHECK:STDOUT: %.loc52_9.2: %empty_tuple.type = tuple_literal () [concrete = constants.%empty_tuple] // CHECK:STDOUT: %.loc52_9.3: type = converted %.loc52_9.2, constants.%empty_tuple.type [concrete = constants.%empty_tuple.type] // CHECK:STDOUT: } // CHECK:STDOUT: %a: ref %empty_tuple.type = ref_binding a, %a.var [concrete = %a.var] // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %b.patt: %pattern_type.7ce = ref_binding_pattern b [concrete] // CHECK:STDOUT: %b.var_patt: %pattern_type.7ce = var_pattern %b.patt [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %b.var: ref %i32 = var %b.var_patt [concrete] // CHECK:STDOUT: %i32.loc53: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %b: ref %i32 = ref_binding b, %b.var [concrete = %b.var] // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %c.patt: %pattern_type.688 = ref_binding_pattern c [concrete] // CHECK:STDOUT: %c.var_patt: %pattern_type.688 = var_pattern %c.patt [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %c.var: ref %struct_type.c = var %c.var_patt [concrete] // CHECK:STDOUT: %.loc54: type = splice_block %struct_type.c [concrete = constants.%struct_type.c] { // CHECK:STDOUT: %i32.loc54: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %struct_type.c: type = struct_type {.c: %i32} [concrete = constants.%struct_type.c] // CHECK:STDOUT: } // CHECK:STDOUT: %c: ref %struct_type.c = ref_binding c, %c.var [concrete = %c.var] // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %d.patt: %pattern_type.cb1 = ref_binding_pattern d [concrete] // CHECK:STDOUT: %d.var_patt: %pattern_type.cb1 = var_pattern %d.patt [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %d.var: ref %empty_tuple.type = var %d.var_patt [concrete] // CHECK:STDOUT: %.loc55_9.1: type = splice_block %.loc55_9.3 [concrete = constants.%empty_tuple.type] { // CHECK:STDOUT: %.loc55_9.2: %empty_tuple.type = tuple_literal () [concrete = constants.%empty_tuple] // CHECK:STDOUT: %.loc55_9.3: type = converted %.loc55_9.2, constants.%empty_tuple.type [concrete = constants.%empty_tuple.type] // CHECK:STDOUT: } // CHECK:STDOUT: %d: ref %empty_tuple.type = ref_binding d, %d.var [concrete = %d.var] // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %e.patt: %pattern_type.cb1 = ref_binding_pattern e [concrete] // CHECK:STDOUT: %e.var_patt: %pattern_type.cb1 = var_pattern %e.patt [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %e.var: ref %empty_tuple.type = var %e.var_patt [concrete] // CHECK:STDOUT: %.loc56_9.1: type = splice_block %.loc56_9.3 [concrete = constants.%empty_tuple.type] { // CHECK:STDOUT: %.loc56_9.2: %empty_tuple.type = tuple_literal () [concrete = constants.%empty_tuple] // CHECK:STDOUT: %.loc56_9.3: type = converted %.loc56_9.2, constants.%empty_tuple.type [concrete = constants.%empty_tuple.type] // CHECK:STDOUT: } // CHECK:STDOUT: %e: ref %empty_tuple.type = ref_binding e, %e.var [concrete = %e.var] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: extern fn @A; // CHECK:STDOUT: // CHECK:STDOUT: extern fn @B; // CHECK:STDOUT: // CHECK:STDOUT: extern fn @C; // CHECK:STDOUT: // CHECK:STDOUT: extern fn @D; // CHECK:STDOUT: // CHECK:STDOUT: extern fn @E; // CHECK:STDOUT: // CHECK:STDOUT: fn @__global_init() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %A.ref: %A.type = name_ref A, imports.%Main.A [concrete = constants.%A] // CHECK:STDOUT: %A.call: init %empty_tuple.type = call %A.ref() // CHECK:STDOUT: assign file.%a.var, %A.call // CHECK:STDOUT: %B.ref: %B.type = name_ref B, imports.%Main.B [concrete = constants.%B] // CHECK:STDOUT: %int_1.loc53: Core.IntLiteral = int_value 1 [concrete = constants.%int_1.5b8] // CHECK:STDOUT: %impl.elem0.loc53: %.545 = impl_witness_access constants.%ImplicitAs.impl_witness.6bc, element0 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5] // CHECK:STDOUT: %bound_method.loc53_16.1: = bound_method %int_1.loc53, %impl.elem0.loc53 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.bound] // CHECK:STDOUT: %specific_fn.loc53: = specific_function %impl.elem0.loc53, @Core.IntLiteral.as.ImplicitAs.impl.Convert(constants.%int_32) [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn] // CHECK:STDOUT: %bound_method.loc53_16.2: = bound_method %int_1.loc53, %specific_fn.loc53 [concrete = constants.%bound_method] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc53: init %i32 = call %bound_method.loc53_16.2(%int_1.loc53) [concrete = constants.%int_1.5d2] // CHECK:STDOUT: %.loc53_16.1: %i32 = value_of_initializer %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc53 [concrete = constants.%int_1.5d2] // CHECK:STDOUT: %.loc53_16.2: %i32 = converted %int_1.loc53, %.loc53_16.1 [concrete = constants.%int_1.5d2] // CHECK:STDOUT: %B.call: init %i32 = call %B.ref(%.loc53_16.2) // CHECK:STDOUT: assign file.%b.var, %B.call // CHECK:STDOUT: %C.ref: %C.type = name_ref C, imports.%Main.C [concrete = constants.%C] // CHECK:STDOUT: %int_1.loc54: Core.IntLiteral = int_value 1 [concrete = constants.%int_1.5b8] // CHECK:STDOUT: %.loc54_25.1: %tuple.type.985 = tuple_literal (%int_1.loc54) [concrete = constants.%tuple.378] // CHECK:STDOUT: %impl.elem0.loc54: %.545 = impl_witness_access constants.%ImplicitAs.impl_witness.6bc, element0 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5] // CHECK:STDOUT: %bound_method.loc54_25.1: = bound_method %int_1.loc54, %impl.elem0.loc54 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.bound] // CHECK:STDOUT: %specific_fn.loc54: = specific_function %impl.elem0.loc54, @Core.IntLiteral.as.ImplicitAs.impl.Convert(constants.%int_32) [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn] // CHECK:STDOUT: %bound_method.loc54_25.2: = bound_method %int_1.loc54, %specific_fn.loc54 [concrete = constants.%bound_method] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc54: init %i32 = call %bound_method.loc54_25.2(%int_1.loc54) [concrete = constants.%int_1.5d2] // CHECK:STDOUT: %.loc54_25.2: %i32 = value_of_initializer %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc54 [concrete = constants.%int_1.5d2] // CHECK:STDOUT: %.loc54_25.3: %i32 = converted %int_1.loc54, %.loc54_25.2 [concrete = constants.%int_1.5d2] // CHECK:STDOUT: %tuple: %tuple.type.a1c = tuple_value (%.loc54_25.3) [concrete = constants.%tuple.246] // CHECK:STDOUT: %.loc54_25.4: %tuple.type.a1c = converted %.loc54_25.1, %tuple [concrete = constants.%tuple.246] // CHECK:STDOUT: %C.call: init %struct_type.c = call %C.ref(%.loc54_25.4) // CHECK:STDOUT: assign file.%c.var, %C.call // CHECK:STDOUT: %D.ref: %D.type = name_ref D, imports.%Main.D [concrete = constants.%D] // CHECK:STDOUT: %D.call: init %empty_tuple.type = call %D.ref() // CHECK:STDOUT: assign file.%d.var, %D.call // CHECK:STDOUT: %NS.ref: = name_ref NS, imports.%NS [concrete = imports.%NS] // CHECK:STDOUT: %E.ref: %E.type = name_ref E, imports.%Main.E [concrete = constants.%E] // CHECK:STDOUT: %E.call: init %empty_tuple.type = call %E.ref() // CHECK:STDOUT: assign file.%e.var, %E.call // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- unloaded.carbon // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Main.A = import_ref Main//api, A, unloaded // CHECK:STDOUT: %Main.B = import_ref Main//api, B, unloaded // CHECK:STDOUT: %Main.C = import_ref Main//api, C, unloaded // CHECK:STDOUT: %Main.D = import_ref Main//api, D, unloaded // CHECK:STDOUT: %Main.NS: = import_ref Main//api, NS, loaded // CHECK:STDOUT: %NS: = namespace %Main.NS, [concrete] { // CHECK:STDOUT: .E = %Main.E // CHECK:STDOUT: } // CHECK:STDOUT: %Main.E = import_ref Main//api, E, unloaded // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .A = imports.%Main.A // CHECK:STDOUT: .B = imports.%Main.B // CHECK:STDOUT: .C = imports.%Main.C // CHECK:STDOUT: .D = imports.%Main.D // CHECK:STDOUT: .NS = imports.%NS // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %default.import = import // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- unloaded_extern.carbon // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Main.A = import_ref Main//extern_api, A, unloaded // CHECK:STDOUT: %Main.B = import_ref Main//extern_api, B, unloaded // CHECK:STDOUT: %Main.C = import_ref Main//extern_api, C, unloaded // CHECK:STDOUT: %Main.D = import_ref Main//extern_api, D, unloaded // CHECK:STDOUT: %Main.NS: = import_ref Main//extern_api, NS, loaded // CHECK:STDOUT: %NS: = namespace %Main.NS, [concrete] { // CHECK:STDOUT: .E = %Main.E // CHECK:STDOUT: } // CHECK:STDOUT: %Main.E = import_ref Main//extern_api, E, unloaded // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .A = imports.%Main.A // CHECK:STDOUT: .B = imports.%Main.B // CHECK:STDOUT: .C = imports.%Main.C // CHECK:STDOUT: .D = imports.%Main.D // CHECK:STDOUT: .NS = imports.%NS // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %default.import = import // CHECK:STDOUT: } // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/function/declaration/name_poisoning.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/none.carbon // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/function/declaration/name_poisoning.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/function/declaration/name_poisoning.carbon // --- no_poison.carbon library "[[@TEST_NAME]]"; class C1 {} class C2 {} fn F1(x: C1); // `N.F2` uses `N.F1` and not `package.F1`. namespace N; fn N.F1(x: C2); alias N.F2 = F1; fn TestCall(x: C2) { // `N.F2` accepts a `C2` not a `C1`. N.F2(x); } // --- poison.carbon library "[[@TEST_NAME]]"; fn F1(); // Use `package.F1` and poison `N.F1`. namespace N; alias N.F2 = F1; // --- fail_declare_after_poison.carbon library "[[@TEST_NAME]]"; fn F1(); namespace N; // Use `package.F1` and poison `N.F1`. // CHECK:STDERR: fail_declare_after_poison.carbon:[[@LINE+3]]:14: error: name `F1` used before it was declared [NameUseBeforeDecl] // CHECK:STDERR: alias N.F2 = F1; // CHECK:STDERR: ^~ alias N.F2 = F1; // Failure: `N.F1` declared after it was poisoned. // CHECK:STDERR: fail_declare_after_poison.carbon:[[@LINE+4]]:6: note: declared here [NameUseBeforeDeclNote] // CHECK:STDERR: fn N.F1(); // CHECK:STDERR: ^~ // CHECK:STDERR: fn N.F1(); // --- fail_use_poison.carbon library "[[@TEST_NAME]]"; fn F1(); // Use `package.F1` and poison `N.F1`. namespace N; alias N.F2 = F1; // CHECK:STDERR: fail_use_poison.carbon:[[@LINE+4]]:14: error: member name `F1` not found in `N` [MemberNameNotFoundInInstScope] // CHECK:STDERR: alias N.F3 = N.F1; // CHECK:STDERR: ^~~~ // CHECK:STDERR: alias N.F3 = N.F1; // --- fail_use_declaration_after_poison.carbon library "[[@TEST_NAME]]"; fn F1(); namespace N; // Use `package.F1` and poison `N.F1`. // CHECK:STDERR: fail_use_declaration_after_poison.carbon:[[@LINE+3]]:14: error: name `F1` used before it was declared [NameUseBeforeDecl] // CHECK:STDERR: alias N.F2 = F1; // CHECK:STDERR: ^~ alias N.F2 = F1; // Failure: `N.F1` declared after it was poisoned. // CHECK:STDERR: fail_use_declaration_after_poison.carbon:[[@LINE+4]]:6: note: declared here [NameUseBeforeDeclNote] // CHECK:STDERR: fn N.F1(); // CHECK:STDERR: ^~ // CHECK:STDERR: fn N.F1(); // Failure: `N.F1` used after declaration failed. // TODO: #4622 - Allow defining a poisoned name so it would be found if used after it's declared. // CHECK:STDERR: fail_use_declaration_after_poison.carbon:[[@LINE+4]]:14: error: member name `F1` not found in `N` [MemberNameNotFoundInInstScope] // CHECK:STDERR: alias N.F3 = N.F1; // CHECK:STDERR: ^~~~ // CHECK:STDERR: alias N.F3 = N.F1; // --- fail_poison_multiple_scopes.carbon library "[[@TEST_NAME]]"; fn F1(); namespace N1; namespace N1.N2; namespace N1.N2.N3; // Use `package.F1` and poison: // * `N1.F1` // * `N1.N2.F1` // * `N1.N2.N3.F1` // CHECK:STDERR: fail_poison_multiple_scopes.carbon:[[@LINE+3]]:21: error: name `F1` used before it was declared [NameUseBeforeDecl] // CHECK:STDERR: alias N1.N2.N3.F2 = F1; // CHECK:STDERR: ^~ alias N1.N2.N3.F2 = F1; // CHECK:STDERR: fail_poison_multiple_scopes.carbon:[[@LINE+7]]:7: note: declared here [NameUseBeforeDeclNote] // CHECK:STDERR: fn N1.F1(); // CHECK:STDERR: ^~ // CHECK:STDERR: // CHECK:STDERR: fail_poison_multiple_scopes.carbon:[[@LINE-6]]:21: error: name `F1` used before it was declared [NameUseBeforeDecl] // CHECK:STDERR: alias N1.N2.N3.F2 = F1; // CHECK:STDERR: ^~ fn N1.F1(); // CHECK:STDERR: fail_poison_multiple_scopes.carbon:[[@LINE+7]]:10: note: declared here [NameUseBeforeDeclNote] // CHECK:STDERR: fn N1.N2.F1(); // CHECK:STDERR: ^~ // CHECK:STDERR: // CHECK:STDERR: fail_poison_multiple_scopes.carbon:[[@LINE-14]]:21: error: name `F1` used before it was declared [NameUseBeforeDecl] // CHECK:STDERR: alias N1.N2.N3.F2 = F1; // CHECK:STDERR: ^~ fn N1.N2.F1(); // CHECK:STDERR: fail_poison_multiple_scopes.carbon:[[@LINE+4]]:13: note: declared here [NameUseBeforeDeclNote] // CHECK:STDERR: fn N1.N2.N3.F1(); // CHECK:STDERR: ^~ // CHECK:STDERR: fn N1.N2.N3.F1(); // --- fail_alias.carbon library "[[@TEST_NAME]]"; fn F(); namespace N; // CHECK:STDERR: fail_alias.carbon:[[@LINE+7]]:13: error: name `F` used before it was declared [NameUseBeforeDecl] // CHECK:STDERR: alias N.F = F; // CHECK:STDERR: ^ // CHECK:STDERR: fail_alias.carbon:[[@LINE+4]]:9: note: declared here [NameUseBeforeDeclNote] // CHECK:STDERR: alias N.F = F; // CHECK:STDERR: ^ // CHECK:STDERR: alias N.F = F; // --- ignored_poison_in_import.carbon library "[[@TEST_NAME]]"; import library "poison"; // This doesn't fail. fn N.F1(); // --- poison.impl.carbon impl library "[[@TEST_NAME]]"; // TODO: #4622 This should fail since `N.F1` was poisoned in the api. fn N.F1() {} // --- fail_poison_when_lookup_fails.carbon library "[[@TEST_NAME]]"; namespace N; // `N.F1` poisoned when not found. // CHECK:STDERR: fail_poison_when_lookup_fails.carbon:[[@LINE+7]]:14: error: name `F1` not found [NameNotFound] // CHECK:STDERR: alias N.F2 = F1; // CHECK:STDERR: ^~ // CHECK:STDERR: // CHECK:STDERR: fail_poison_when_lookup_fails.carbon:[[@LINE+3]]:14: error: name `F1` used before it was declared [NameUseBeforeDecl] // CHECK:STDERR: alias N.F2 = F1; // CHECK:STDERR: ^~ alias N.F2 = F1; // TODO: We should ideally only produce one diagnostic here. // CHECK:STDERR: fail_poison_when_lookup_fails.carbon:[[@LINE+7]]:4: note: declared here [NameUseBeforeDeclNote] // CHECK:STDERR: fn F1(); // CHECK:STDERR: ^~ // CHECK:STDERR: // CHECK:STDERR: fail_poison_when_lookup_fails.carbon:[[@LINE-7]]:14: error: name `F1` used before it was declared [NameUseBeforeDecl] // CHECK:STDERR: alias N.F2 = F1; // CHECK:STDERR: ^~ fn F1(); // CHECK:STDERR: fail_poison_when_lookup_fails.carbon:[[@LINE+4]]:6: note: declared here [NameUseBeforeDeclNote] // CHECK:STDERR: fn N.F1(); // CHECK:STDERR: ^~ // CHECK:STDERR: fn N.F1(); // --- fail_poison_with_lexical_result.carbon library "[[@TEST_NAME]]"; fn F1() { fn F2(); class C { // CHECK:STDERR: fail_poison_with_lexical_result.carbon:[[@LINE+3]]:16: error: name `F2` used before it was declared [NameUseBeforeDecl] // CHECK:STDERR: alias F3 = F2; // CHECK:STDERR: ^~ alias F3 = F2; // CHECK:STDERR: fail_poison_with_lexical_result.carbon:[[@LINE+4]]:8: note: declared here [NameUseBeforeDeclNote] // CHECK:STDERR: fn F2(); // CHECK:STDERR: ^~ // CHECK:STDERR: fn F2(); } } ================================================ FILE: toolchain/check/testdata/function/declaration/no_definition_in_impl_file.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/none.carbon // TODO: Add ranges and switch to "--dump-sem-ir-ranges=only". // EXTRA-ARGS: --dump-sem-ir-ranges=if-present // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/function/declaration/no_definition_in_impl_file.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/function/declaration/no_definition_in_impl_file.carbon // --- decl_in_api_definition_in_impl.carbon library "[[@TEST_NAME]]"; fn A(); // --- decl_in_api_definition_in_impl.impl.carbon impl library "[[@TEST_NAME]]"; fn A(); fn A() {} // --- use_decl_in_api.carbon library "[[@TEST_NAME]]"; // --- use_decl_in_api.impl.carbon impl library "[[@TEST_NAME]]"; import library "decl_in_api_definition_in_impl"; // --- decl_only_in_api.carbon library "[[@TEST_NAME]]"; fn B(); // --- decl_only_in_api.impl.carbon impl library "[[@TEST_NAME]]"; // --- decl_in_api_decl_in_impl.carbon library "[[@TEST_NAME]]"; fn C(); // --- fail_decl_in_api_decl_in_impl.impl.carbon impl library "[[@TEST_NAME]]"; // CHECK:STDERR: fail_decl_in_api_decl_in_impl.impl.carbon:[[@LINE+4]]:1: error: no definition found for declaration in impl file [MissingDefinitionInImpl] // CHECK:STDERR: fn C(); // CHECK:STDERR: ^~~~~~~ // CHECK:STDERR: fn C(); // --- decl_only_in_impl.carbon library "[[@TEST_NAME]]"; // --- fail_decl_only_in_impl.impl.carbon impl library "[[@TEST_NAME]]"; // CHECK:STDERR: fail_decl_only_in_impl.impl.carbon:[[@LINE+4]]:1: error: no definition found for declaration in impl file [MissingDefinitionInImpl] // CHECK:STDERR: fn D(); // CHECK:STDERR: ^~~~~~~ // CHECK:STDERR: fn D(); // CHECK:STDOUT: --- decl_in_api_definition_in_impl.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %A.type: type = fn_type @A [concrete] // CHECK:STDOUT: %A: %A.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .A = %A.decl // CHECK:STDOUT: } // CHECK:STDOUT: %A.decl: %A.type = fn_decl @A [concrete = constants.%A] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @A(); // CHECK:STDOUT: // CHECK:STDOUT: --- decl_in_api_definition_in_impl.impl.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %A.type: type = fn_type @A [concrete] // CHECK:STDOUT: %A: %A.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .A = %A.decl.loc4 // CHECK:STDOUT: } // CHECK:STDOUT: %default.import.loc2_46.1 = import // CHECK:STDOUT: %default.import.loc2_46.2 = import // CHECK:STDOUT: %A.decl.loc4: %A.type = fn_decl @A [concrete = constants.%A] {} {} // CHECK:STDOUT: %A.decl.loc6: %A.type = fn_decl @A [concrete = constants.%A] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @A() [from "decl_in_api_definition_in_impl.carbon"] { // CHECK:STDOUT: !entry: // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- use_decl_in_api.carbon // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- use_decl_in_api.impl.carbon // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Main.A = import_ref Main//decl_in_api_definition_in_impl, A, unloaded // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .A = imports.%Main.A // CHECK:STDOUT: } // CHECK:STDOUT: %default.import.loc2_31.1 = import // CHECK:STDOUT: %default.import.loc2_31.2 = import // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- decl_only_in_api.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %B.type: type = fn_type @B [concrete] // CHECK:STDOUT: %B: %B.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .B = %B.decl // CHECK:STDOUT: } // CHECK:STDOUT: %B.decl: %B.type = fn_decl @B [concrete = constants.%B] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @B(); // CHECK:STDOUT: // CHECK:STDOUT: --- decl_only_in_api.impl.carbon // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Main.B = import_ref Main//decl_only_in_api, B, unloaded // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .B = imports.%Main.B // CHECK:STDOUT: } // CHECK:STDOUT: %default.import.loc2_32.1 = import // CHECK:STDOUT: %default.import.loc2_32.2 = import // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- decl_in_api_decl_in_impl.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %C.type: type = fn_type @C [concrete] // CHECK:STDOUT: %C: %C.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .C = %C.decl // CHECK:STDOUT: } // CHECK:STDOUT: %C.decl: %C.type = fn_decl @C [concrete = constants.%C] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @C(); // CHECK:STDOUT: // CHECK:STDOUT: --- fail_decl_in_api_decl_in_impl.impl.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %C.type: type = fn_type @C [concrete] // CHECK:STDOUT: %C: %C.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .C = %C.decl // CHECK:STDOUT: } // CHECK:STDOUT: %default.import.loc2_40.1 = import // CHECK:STDOUT: %default.import.loc2_40.2 = import // CHECK:STDOUT: %C.decl: %C.type = fn_decl @C [concrete = constants.%C] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @C [from "decl_in_api_decl_in_impl.carbon"]; // CHECK:STDOUT: // CHECK:STDOUT: --- decl_only_in_impl.carbon // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- fail_decl_only_in_impl.impl.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %D.type: type = fn_type @D [concrete] // CHECK:STDOUT: %D: %D.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .D = %D.decl // CHECK:STDOUT: } // CHECK:STDOUT: %default.import.loc2_33.1 = import // CHECK:STDOUT: %default.import.loc2_33.2 = import // CHECK:STDOUT: %D.decl: %D.type = fn_decl @D [concrete = constants.%D] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @D(); // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/function/declaration/param_same_name.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/int.carbon // TODO: Add ranges and switch to "--dump-sem-ir-ranges=only". // EXTRA-ARGS: --dump-sem-ir-ranges=if-present // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/function/declaration/param_same_name.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/function/declaration/param_same_name.carbon fn F(a: i32); fn G(a: i32); // CHECK:STDOUT: --- param_same_name.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [concrete] // CHECK:STDOUT: %Int.type: type = generic_class_type @Int [concrete] // CHECK:STDOUT: %Int.generic: %Int.type = struct_value () [concrete] // CHECK:STDOUT: %i32: type = class_type @Int, @Int(%int_32) [concrete] // CHECK:STDOUT: %pattern_type.7ce: type = pattern_type %i32 [concrete] // CHECK:STDOUT: %F.type: type = fn_type @F [concrete] // CHECK:STDOUT: %F: %F.type = struct_value () [concrete] // CHECK:STDOUT: %G.type: type = fn_type @G [concrete] // CHECK:STDOUT: %G: %G.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: .Int = %Core.Int // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: %Core.Int: %Int.type = import_ref Core//prelude/parts/int, Int, loaded [concrete = constants.%Int.generic] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .F = %F.decl // CHECK:STDOUT: .G = %G.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %F.decl: %F.type = fn_decl @F [concrete = constants.%F] { // CHECK:STDOUT: %a.patt: %pattern_type.7ce = value_binding_pattern a [concrete] // CHECK:STDOUT: %a.param_patt: %pattern_type.7ce = value_param_pattern %a.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %a.param: %i32 = value_param call_param0 // CHECK:STDOUT: %i32: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %a: %i32 = value_binding a, %a.param // CHECK:STDOUT: } // CHECK:STDOUT: %G.decl: %G.type = fn_decl @G [concrete = constants.%G] { // CHECK:STDOUT: %a.patt: %pattern_type.7ce = value_binding_pattern a [concrete] // CHECK:STDOUT: %a.param_patt: %pattern_type.7ce = value_param_pattern %a.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %a.param: %i32 = value_param call_param0 // CHECK:STDOUT: %i32: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %a: %i32 = value_binding a, %a.param // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @F(%a.param: %i32); // CHECK:STDOUT: // CHECK:STDOUT: fn @G(%a.param: %i32); // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/function/declaration/pattern_in_signature.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/none.carbon // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/function/declaration/pattern_in_signature.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/function/declaration/pattern_in_signature.carbon //@dump-sem-ir-begin fn F((unused a: {}, unused b: {}), unused c: {}) {} //@dump-sem-ir-end //@dump-sem-ir-begin fn CallF(ab: ({}, {}), c: {}) { F(ab, c); } //@dump-sem-ir-end // CHECK:STDOUT: --- pattern_in_signature.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %empty_struct: %empty_struct_type = struct_value () [concrete] // CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] // CHECK:STDOUT: %pattern_type.a96: type = pattern_type %empty_struct_type [concrete] // CHECK:STDOUT: %tuple.type.b6b: type = tuple_type (%empty_struct_type, %empty_struct_type) [concrete] // CHECK:STDOUT: %pattern_type.de4: type = pattern_type %tuple.type.b6b [concrete] // CHECK:STDOUT: %F.type: type = fn_type @F [concrete] // CHECK:STDOUT: %F: %F.type = struct_value () [concrete] // CHECK:STDOUT: %tuple: %tuple.type.b6b = tuple_value (%empty_struct, %empty_struct) [concrete] // CHECK:STDOUT: %CallF.type: type = fn_type @CallF [concrete] // CHECK:STDOUT: %CallF: %CallF.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: %F.decl: %F.type = fn_decl @F [concrete = constants.%F] { // CHECK:STDOUT: %a.patt: %pattern_type.a96 = value_binding_pattern a [concrete] // CHECK:STDOUT: %a.param_patt: %pattern_type.a96 = value_param_pattern %a.patt [concrete] // CHECK:STDOUT: %b.patt: %pattern_type.a96 = value_binding_pattern b [concrete] // CHECK:STDOUT: %b.param_patt: %pattern_type.a96 = value_param_pattern %b.patt [concrete] // CHECK:STDOUT: %.loc14_33: %pattern_type.de4 = tuple_pattern (%a.param_patt, %b.param_patt) [concrete] // CHECK:STDOUT: %c.patt: %pattern_type.a96 = value_binding_pattern c [concrete] // CHECK:STDOUT: %c.param_patt: %pattern_type.a96 = value_param_pattern %c.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %a.param: %empty_struct_type = value_param call_param0 // CHECK:STDOUT: %.loc14_18.1: type = splice_block %.loc14_18.3 [concrete = constants.%empty_struct_type] { // CHECK:STDOUT: %.loc14_18.2: %empty_struct_type = struct_literal () [concrete = constants.%empty_struct] // CHECK:STDOUT: %.loc14_18.3: type = converted %.loc14_18.2, constants.%empty_struct_type [concrete = constants.%empty_struct_type] // CHECK:STDOUT: } // CHECK:STDOUT: %a: %empty_struct_type = value_binding a, %a.param // CHECK:STDOUT: %b.param: %empty_struct_type = value_param call_param1 // CHECK:STDOUT: %.loc14_32.1: type = splice_block %.loc14_32.3 [concrete = constants.%empty_struct_type] { // CHECK:STDOUT: %.loc14_32.2: %empty_struct_type = struct_literal () [concrete = constants.%empty_struct] // CHECK:STDOUT: %.loc14_32.3: type = converted %.loc14_32.2, constants.%empty_struct_type [concrete = constants.%empty_struct_type] // CHECK:STDOUT: } // CHECK:STDOUT: %b: %empty_struct_type = value_binding b, %b.param // CHECK:STDOUT: %c.param: %empty_struct_type = value_param call_param2 // CHECK:STDOUT: %.loc14_47.1: type = splice_block %.loc14_47.3 [concrete = constants.%empty_struct_type] { // CHECK:STDOUT: %.loc14_47.2: %empty_struct_type = struct_literal () [concrete = constants.%empty_struct] // CHECK:STDOUT: %.loc14_47.3: type = converted %.loc14_47.2, constants.%empty_struct_type [concrete = constants.%empty_struct_type] // CHECK:STDOUT: } // CHECK:STDOUT: %c: %empty_struct_type = value_binding c, %c.param // CHECK:STDOUT: } // CHECK:STDOUT: %CallF.decl: %CallF.type = fn_decl @CallF [concrete = constants.%CallF] { // CHECK:STDOUT: %ab.patt: %pattern_type.de4 = value_binding_pattern ab [concrete] // CHECK:STDOUT: %ab.param_patt: %pattern_type.de4 = value_param_pattern %ab.patt [concrete] // CHECK:STDOUT: %c.patt: %pattern_type.a96 = value_binding_pattern c [concrete] // CHECK:STDOUT: %c.param_patt: %pattern_type.a96 = value_param_pattern %c.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %ab.param: %tuple.type.b6b = value_param call_param0 // CHECK:STDOUT: %.loc18_21.1: type = splice_block %.loc18_21.5 [concrete = constants.%tuple.type.b6b] { // CHECK:STDOUT: %.loc18_16: %empty_struct_type = struct_literal () [concrete = constants.%empty_struct] // CHECK:STDOUT: %.loc18_20: %empty_struct_type = struct_literal () [concrete = constants.%empty_struct] // CHECK:STDOUT: %.loc18_21.2: %tuple.type.b6b = tuple_literal (%.loc18_16, %.loc18_20) [concrete = constants.%tuple] // CHECK:STDOUT: %.loc18_21.3: type = converted constants.%empty_struct, constants.%empty_struct_type [concrete = constants.%empty_struct_type] // CHECK:STDOUT: %.loc18_21.4: type = converted constants.%empty_struct, constants.%empty_struct_type [concrete = constants.%empty_struct_type] // CHECK:STDOUT: %.loc18_21.5: type = converted %.loc18_21.2, constants.%tuple.type.b6b [concrete = constants.%tuple.type.b6b] // CHECK:STDOUT: } // CHECK:STDOUT: %ab: %tuple.type.b6b = value_binding ab, %ab.param // CHECK:STDOUT: %c.param: %empty_struct_type = value_param call_param1 // CHECK:STDOUT: %.loc18_28.1: type = splice_block %.loc18_28.3 [concrete = constants.%empty_struct_type] { // CHECK:STDOUT: %.loc18_28.2: %empty_struct_type = struct_literal () [concrete = constants.%empty_struct] // CHECK:STDOUT: %.loc18_28.3: type = converted %.loc18_28.2, constants.%empty_struct_type [concrete = constants.%empty_struct_type] // CHECK:STDOUT: } // CHECK:STDOUT: %c: %empty_struct_type = value_binding c, %c.param // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @F(%a.param: %empty_struct_type, %b.param: %empty_struct_type, %c.param: %empty_struct_type) { // CHECK:STDOUT: !entry: // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @CallF(%ab.param: %tuple.type.b6b, %c.param: %empty_struct_type) { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %F.ref: %F.type = name_ref F, file.%F.decl [concrete = constants.%F] // CHECK:STDOUT: %ab.ref: %tuple.type.b6b = name_ref ab, %ab // CHECK:STDOUT: %c.ref: %empty_struct_type = name_ref c, %c // CHECK:STDOUT: %tuple.elem0: %empty_struct_type = tuple_access %ab.ref, element0 // CHECK:STDOUT: %tuple.elem1: %empty_struct_type = tuple_access %ab.ref, element1 // CHECK:STDOUT: %F.call: init %empty_tuple.type = call %F.ref(%tuple.elem0, %tuple.elem1, %c.ref) // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/function/declaration/ref.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/int.carbon // EXTRA-ARGS: --dump-sem-ir-ranges=only // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/function/declaration/ref.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/function/declaration/ref.carbon // --- ref_return.carbon //@include-in-dumps fn F() -> ref i32; fn G() { let unused ref x: i32 = F(); } // CHECK:STDOUT: --- ref_return.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [concrete] // CHECK:STDOUT: %Int.type: type = generic_class_type @Int [concrete] // CHECK:STDOUT: %Int.generic: %Int.type = struct_value () [concrete] // CHECK:STDOUT: %i32: type = class_type @Int, @Int(%int_32) [concrete] // CHECK:STDOUT: %.1da: Core.Form = ref_form %i32 [concrete] // CHECK:STDOUT: %F.type: type = fn_type @F [concrete] // CHECK:STDOUT: %F: %F.type = struct_value () [concrete] // CHECK:STDOUT: %G.type: type = fn_type @G [concrete] // CHECK:STDOUT: %G: %G.type = struct_value () [concrete] // CHECK:STDOUT: %pattern_type.7ce: type = pattern_type %i32 [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: .Int = %Core.Int // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: %Core.Int: %Int.type = import_ref Core//prelude/parts/int, Int, loaded [concrete = constants.%Int.generic] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .F = %F.decl // CHECK:STDOUT: .G = %G.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %F.decl: %F.type = fn_decl @F [concrete = constants.%F] {} { // CHECK:STDOUT: %i32: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %.loc4_11.1: type = ref_tag %i32 // CHECK:STDOUT: %.loc4_11.2: Core.Form = ref_form %i32 [concrete = constants.%.1da] // CHECK:STDOUT: } // CHECK:STDOUT: %G.decl: %G.type = fn_decl @G [concrete = constants.%G] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @F() -> ref %i32; // CHECK:STDOUT: // CHECK:STDOUT: fn @G() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %x.patt: %pattern_type.7ce = ref_binding_pattern x [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %F.ref: %F.type = name_ref F, file.%F.decl [concrete = constants.%F] // CHECK:STDOUT: %F.call: ref %i32 = call %F.ref() // CHECK:STDOUT: %i32: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %x: ref %i32 = ref_binding x, %F.call // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/function/declaration/simple.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/none.carbon // TODO: Add ranges and switch to "--dump-sem-ir-ranges=only". // EXTRA-ARGS: --dump-sem-ir-ranges=if-present // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/function/declaration/simple.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/function/declaration/simple.carbon fn F(); fn G() { F(); } // CHECK:STDOUT: --- simple.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %F.type: type = fn_type @F [concrete] // CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] // CHECK:STDOUT: %F: %F.type = struct_value () [concrete] // CHECK:STDOUT: %G.type: type = fn_type @G [concrete] // CHECK:STDOUT: %G: %G.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .F = %F.decl // CHECK:STDOUT: .G = %G.decl // CHECK:STDOUT: } // CHECK:STDOUT: %F.decl: %F.type = fn_decl @F [concrete = constants.%F] {} {} // CHECK:STDOUT: %G.decl: %G.type = fn_decl @G [concrete = constants.%G] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @F(); // CHECK:STDOUT: // CHECK:STDOUT: fn @G() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %F.ref: %F.type = name_ref F, file.%F.decl [concrete = constants.%F] // CHECK:STDOUT: %F.call: init %empty_tuple.type = call %F.ref() // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/function/definition/basics.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/none.carbon // TODO: Add ranges and switch to "--dump-sem-ir-ranges=only". // EXTRA-ARGS: --dump-sem-ir-ranges=if-present // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/function/definition/basics.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/function/definition/basics.carbon // --- fail_incomplete_type.carbon class C; // CHECK:STDERR: fail_incomplete_type.carbon:[[@LINE+7]]:13: error: parameter has incomplete type `C` in function definition [IncompleteTypeInFunctionParam] // CHECK:STDERR: fn F(unused n: C) {} // CHECK:STDERR: ^~~~ // CHECK:STDERR: fail_incomplete_type.carbon:[[@LINE-5]]:1: note: class was forward declared here [ClassForwardDeclaredHere] // CHECK:STDERR: class C; // CHECK:STDERR: ^~~~~~~~ // CHECK:STDERR: fn F(unused n: C) {} // CHECK:STDOUT: --- fail_incomplete_type.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %C: type = class_type @C [concrete] // CHECK:STDOUT: %pattern_type: type = pattern_type %C [concrete] // CHECK:STDOUT: %F.type: type = fn_type @F [concrete] // CHECK:STDOUT: %F: %F.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .C = %C.decl // CHECK:STDOUT: .F = %F.decl // CHECK:STDOUT: } // CHECK:STDOUT: %C.decl: type = class_decl @C [concrete = constants.%C] {} {} // CHECK:STDOUT: %F.decl: %F.type = fn_decl @F [concrete = constants.%F] { // CHECK:STDOUT: %n.patt: %pattern_type = value_binding_pattern n [concrete] // CHECK:STDOUT: %n.param_patt: %pattern_type = value_param_pattern %n.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %n.param: %C = value_param call_param0 // CHECK:STDOUT: %C.ref: type = name_ref C, file.%C.decl [concrete = constants.%C] // CHECK:STDOUT: %n: %C = value_binding n, %n.param // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @C; // CHECK:STDOUT: // CHECK:STDOUT: fn @F(%n.param: %C) { // CHECK:STDOUT: !entry: // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/function/definition/extern.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/none.carbon // TODO: Add ranges and switch to "--dump-sem-ir-ranges=only". // EXTRA-ARGS: --dump-sem-ir-ranges=if-present // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/function/definition/extern.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/function/definition/extern.carbon // --- extern_def.carbon library "[[@TEST_NAME]]"; extern fn F() {} // --- def_for_extern_decl.carbon library "[[@TEST_NAME]]"; extern fn F(); extern fn F() {} // --- split_library.carbon library "[[@TEST_NAME]]"; extern fn F(); // --- split_library.impl.carbon impl library "[[@TEST_NAME]]"; extern fn F() {} // --- fail_def_extern_mismatch.carbon library "[[@TEST_NAME]]"; extern fn F(); // CHECK:STDERR: fail_def_extern_mismatch.carbon:[[@LINE+7]]:1: error: redeclarations of `fn F` must match use of `extern` [RedeclExternMismatch] // CHECK:STDERR: fn F() {} // CHECK:STDERR: ^~~~~~~~ // CHECK:STDERR: fail_def_extern_mismatch.carbon:[[@LINE-4]]:1: note: previously declared here [RedeclPrevDecl] // CHECK:STDERR: extern fn F(); // CHECK:STDERR: ^~~~~~~~~~~~~~ // CHECK:STDERR: fn F() {} // --- fail_def_extern_mismatch_reverse.carbon library "[[@TEST_NAME]]"; fn F(); // CHECK:STDERR: fail_def_extern_mismatch_reverse.carbon:[[@LINE+7]]:1: error: redeclarations of `fn F` must match use of `extern` [RedeclExternMismatch] // CHECK:STDERR: extern fn F() {} // CHECK:STDERR: ^~~~~~~~~~~~~~~ // CHECK:STDERR: fail_def_extern_mismatch_reverse.carbon:[[@LINE-4]]:1: note: previously declared here [RedeclPrevDecl] // CHECK:STDERR: fn F(); // CHECK:STDERR: ^~~~~~~ // CHECK:STDERR: extern fn F() {} // --- fail_extern_diag_suppressed.carbon library "[[@TEST_NAME]]"; extern fn F(); // CHECK:STDERR: fail_extern_diag_suppressed.carbon:[[@LINE+7]]:1: error: redeclaration of `fn F` is redundant [RedeclRedundant] // CHECK:STDERR: fn F(); // CHECK:STDERR: ^~~~~~~ // CHECK:STDERR: fail_extern_diag_suppressed.carbon:[[@LINE-4]]:1: note: previously declared here [RedeclPrevDecl] // CHECK:STDERR: extern fn F(); // CHECK:STDERR: ^~~~~~~~~~~~~~ // CHECK:STDERR: fn F(); // CHECK:STDERR: fail_extern_diag_suppressed.carbon:[[@LINE+7]]:1: error: redeclarations of `fn F` must match use of `extern` [RedeclExternMismatch] // CHECK:STDERR: fn F() {} // CHECK:STDERR: ^~~~~~~~ // CHECK:STDERR: fail_extern_diag_suppressed.carbon:[[@LINE-12]]:1: note: previously declared here [RedeclPrevDecl] // CHECK:STDERR: extern fn F(); // CHECK:STDERR: ^~~~~~~~~~~~~~ // CHECK:STDERR: fn F() {} // --- fail_extern_decl_after_def.carbon library "[[@TEST_NAME]]"; fn F() {} // CHECK:STDERR: fail_extern_decl_after_def.carbon:[[@LINE+7]]:1: error: redeclaration of `fn F` is redundant [RedeclRedundant] // CHECK:STDERR: extern fn F(); // CHECK:STDERR: ^~~~~~~~~~~~~~ // CHECK:STDERR: fail_extern_decl_after_def.carbon:[[@LINE-4]]:1: note: previously declared here [RedeclPrevDecl] // CHECK:STDERR: fn F() {} // CHECK:STDERR: ^~~~~~~~ // CHECK:STDERR: extern fn F(); // --- in_impl.carbon library "[[@TEST_NAME]]"; // --- fail_in_impl.impl.carbon impl library "[[@TEST_NAME]]"; // CHECK:STDERR: fail_in_impl.impl.carbon:[[@LINE+4]]:1: error: `extern` entities must have a declaration in the API file [ExternRequiresDeclInApiFile] // CHECK:STDERR: extern fn F() {} // CHECK:STDERR: ^~~~~~~~~~~~~~~ // CHECK:STDERR: extern fn F() {} // CHECK:STDOUT: --- extern_def.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %F.type: type = fn_type @F [concrete] // CHECK:STDOUT: %F: %F.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .F = %F.decl // CHECK:STDOUT: } // CHECK:STDOUT: %F.decl: %F.type = fn_decl @F [concrete = constants.%F] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: extern fn @F() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- def_for_extern_decl.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %F.type: type = fn_type @F [concrete] // CHECK:STDOUT: %F: %F.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .F = %F.decl.loc4 // CHECK:STDOUT: } // CHECK:STDOUT: %F.decl.loc4: %F.type = fn_decl @F [concrete = constants.%F] {} {} // CHECK:STDOUT: %F.decl.loc5: %F.type = fn_decl @F [concrete = constants.%F] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: extern fn @F() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- split_library.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %F.type: type = fn_type @F [concrete] // CHECK:STDOUT: %F: %F.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .F = %F.decl // CHECK:STDOUT: } // CHECK:STDOUT: %F.decl: %F.type = fn_decl @F [concrete = constants.%F] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: extern fn @F(); // CHECK:STDOUT: // CHECK:STDOUT: --- split_library.impl.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %F.type: type = fn_type @F [concrete] // CHECK:STDOUT: %F: %F.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .F = %F.decl // CHECK:STDOUT: } // CHECK:STDOUT: %default.import.loc2_29.1 = import // CHECK:STDOUT: %default.import.loc2_29.2 = import // CHECK:STDOUT: %F.decl: %F.type = fn_decl @F [concrete = constants.%F] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: extern fn @F() [from "split_library.carbon"] { // CHECK:STDOUT: !entry: // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- fail_def_extern_mismatch.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %F.type: type = fn_type @F [concrete] // CHECK:STDOUT: %F: %F.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .F = %F.decl.loc4 // CHECK:STDOUT: } // CHECK:STDOUT: %F.decl.loc4: %F.type = fn_decl @F [concrete = constants.%F] {} {} // CHECK:STDOUT: %F.decl.loc12: %F.type = fn_decl @F [concrete = constants.%F] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: extern fn @F() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- fail_def_extern_mismatch_reverse.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %F.type: type = fn_type @F [concrete] // CHECK:STDOUT: %F: %F.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .F = %F.decl.loc4 // CHECK:STDOUT: } // CHECK:STDOUT: %F.decl.loc4: %F.type = fn_decl @F [concrete = constants.%F] {} {} // CHECK:STDOUT: %F.decl.loc12: %F.type = fn_decl @F [concrete = constants.%F] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @F() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- fail_extern_diag_suppressed.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %F.type: type = fn_type @F [concrete] // CHECK:STDOUT: %F: %F.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .F = %F.decl.loc4 // CHECK:STDOUT: } // CHECK:STDOUT: %F.decl.loc4: %F.type = fn_decl @F [concrete = constants.%F] {} {} // CHECK:STDOUT: %F.decl.loc12: %F.type = fn_decl @F [concrete = constants.%F] {} {} // CHECK:STDOUT: %F.decl.loc20: %F.type = fn_decl @F [concrete = constants.%F] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: extern fn @F() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- fail_extern_decl_after_def.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %F.type: type = fn_type @F [concrete] // CHECK:STDOUT: %F: %F.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .F = %F.decl.loc4 // CHECK:STDOUT: } // CHECK:STDOUT: %F.decl.loc4: %F.type = fn_decl @F [concrete = constants.%F] {} {} // CHECK:STDOUT: %F.decl.loc12: %F.type = fn_decl @F [concrete = constants.%F] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @F() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- in_impl.carbon // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- fail_in_impl.impl.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %F.type: type = fn_type @F [concrete] // CHECK:STDOUT: %F: %F.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .F = %F.decl // CHECK:STDOUT: } // CHECK:STDOUT: %default.import.loc2_23.1 = import // CHECK:STDOUT: %default.import.loc2_23.2 = import // CHECK:STDOUT: %F.decl: %F.type = fn_decl @F [concrete = constants.%F] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: extern fn @F() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/function/definition/extern_library.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/none.carbon // TODO: Add ranges and switch to "--dump-sem-ir-ranges=only". // EXTRA-ARGS: --dump-sem-ir-ranges=if-present // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/function/definition/extern_library.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/function/definition/extern_library.carbon // --- one_file_extern.carbon library "[[@TEST_NAME]]"; extern library "one_file" fn F(); // --- one_file.carbon library "[[@TEST_NAME]]"; extern fn F() {} // --- two_file_extern.carbon library "[[@TEST_NAME]]"; extern library "two_file" fn F(); // --- two_file.carbon library "[[@TEST_NAME]]"; extern fn F(); // --- two_file.impl.carbon impl library "[[@TEST_NAME]]"; extern fn F() {} // --- two_file_impl_mismatch_extern.carbon library "[[@TEST_NAME]]"; extern library "two_file_impl_mismatch" fn F(); // --- two_file_impl_mismatch.carbon library "[[@TEST_NAME]]"; extern fn F(); // --- fail_two_file_impl_mismatch.impl.carbon impl library "[[@TEST_NAME]]"; // CHECK:STDERR: fail_two_file_impl_mismatch.impl.carbon:[[@LINE+8]]:1: error: redeclarations of `fn F` must match use of `extern` [RedeclExternMismatch] // CHECK:STDERR: fn F() {} // CHECK:STDERR: ^~~~~~~~ // CHECK:STDERR: fail_two_file_impl_mismatch.impl.carbon:[[@LINE-5]]:1: in import [InImport] // CHECK:STDERR: two_file_impl_mismatch.carbon:4:1: note: previously declared here [RedeclPrevDecl] // CHECK:STDERR: extern fn F(); // CHECK:STDERR: ^~~~~~~~~~~~~~ // CHECK:STDERR: fn F() {} // --- no_decl_extern.carbon library "[[@TEST_NAME]]"; extern library "no_decl" fn F(); // --- no_decl_extern.impl.carbon impl library "no_decl_extern"; // --- fail_no_decl.carbon library "[[@TEST_NAME]]"; // CHECK:STDERR: fail_no_decl.carbon:[[@LINE+5]]:1: in import [InImport] // CHECK:STDERR: no_decl_extern.carbon:4:1: error: owning declaration required for non-owning declaration [MissingOwningDeclarationInApi] // CHECK:STDERR: extern library "no_decl" fn F(); // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: import library "no_decl_extern"; // --- indirect_two_file_extern.carbon library "[[@TEST_NAME]]"; extern library "indirect_two_file" fn F(); // --- fail_indirect_two_file.carbon library "[[@TEST_NAME]]"; // CHECK:STDERR: fail_indirect_two_file.carbon:[[@LINE+5]]:1: in import [InImport] // CHECK:STDERR: indirect_two_file_extern.carbon:4:1: error: owning declaration required for non-owning declaration [MissingOwningDeclarationInApi] // CHECK:STDERR: extern library "indirect_two_file" fn F(); // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: import library "indirect_two_file_extern"; // --- fail_indirect_two_file.impl.carbon impl library "[[@TEST_NAME]]"; // CHECK:STDERR: fail_indirect_two_file.impl.carbon:[[@LINE+4]]:1: error: `extern` entities must have a declaration in the API file [ExternRequiresDeclInApiFile] // CHECK:STDERR: extern fn F() {} // CHECK:STDERR: ^~~~~~~~~~~~~~~ // CHECK:STDERR: extern fn F() {} // --- in_impl_extern.carbon library "[[@TEST_NAME]]"; extern library "in_impl" fn F(); // --- in_impl.carbon library "[[@TEST_NAME]]"; // --- fail_in_impl.impl.carbon impl library "[[@TEST_NAME]]"; import library "in_impl_extern"; // CHECK:STDERR: fail_in_impl.impl.carbon:[[@LINE+4]]:1: error: `extern` entities must have a declaration in the API file [ExternRequiresDeclInApiFile] // CHECK:STDERR: extern fn F() {} // CHECK:STDERR: ^~~~~~~~~~~~~~~ // CHECK:STDERR: extern fn F() {} // --- cross_package_extern.carbon package OtherPackage library "[[@TEST_NAME]]"; extern library "cross_package" fn Extern(); // --- cross_package.carbon package ThisPackage library "[[@TEST_NAME]]"; import OtherPackage library "cross_package_extern"; // This call causes the function to be imported. fn F() { OtherPackage.Extern(); } // --- unloaded_decl_extern.carbon library "[[@TEST_NAME]]"; extern library "unloaded_decl" fn ExternDecl(); fn NonExternDecl(); // --- unloaded_decl.carbon library "[[@TEST_NAME]]"; import library "unloaded_decl_extern"; extern fn ExternDecl(); // CHECK:STDOUT: --- one_file_extern.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %F.type: type = fn_type @F [concrete] // CHECK:STDOUT: %F: %F.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .F = %F.decl // CHECK:STDOUT: } // CHECK:STDOUT: %F.decl: %F.type = fn_decl @F [concrete = constants.%F] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: extern fn @F(); // CHECK:STDOUT: // CHECK:STDOUT: --- one_file.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %F.type: type = fn_type @F [concrete] // CHECK:STDOUT: %F: %F.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .F = %F.decl // CHECK:STDOUT: } // CHECK:STDOUT: %F.decl: %F.type = fn_decl @F [concrete = constants.%F] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: extern fn @F() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- two_file_extern.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %F.type: type = fn_type @F [concrete] // CHECK:STDOUT: %F: %F.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .F = %F.decl // CHECK:STDOUT: } // CHECK:STDOUT: %F.decl: %F.type = fn_decl @F [concrete = constants.%F] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: extern fn @F(); // CHECK:STDOUT: // CHECK:STDOUT: --- two_file.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %F.type: type = fn_type @F [concrete] // CHECK:STDOUT: %F: %F.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .F = %F.decl // CHECK:STDOUT: } // CHECK:STDOUT: %F.decl: %F.type = fn_decl @F [concrete = constants.%F] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: extern fn @F(); // CHECK:STDOUT: // CHECK:STDOUT: --- two_file.impl.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %F.type: type = fn_type @F [concrete] // CHECK:STDOUT: %F: %F.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .F = %F.decl // CHECK:STDOUT: } // CHECK:STDOUT: %default.import.loc2_24.1 = import // CHECK:STDOUT: %default.import.loc2_24.2 = import // CHECK:STDOUT: %F.decl: %F.type = fn_decl @F [concrete = constants.%F] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: extern fn @F() [from "two_file.carbon"] { // CHECK:STDOUT: !entry: // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- two_file_impl_mismatch_extern.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %F.type: type = fn_type @F [concrete] // CHECK:STDOUT: %F: %F.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .F = %F.decl // CHECK:STDOUT: } // CHECK:STDOUT: %F.decl: %F.type = fn_decl @F [concrete = constants.%F] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: extern fn @F(); // CHECK:STDOUT: // CHECK:STDOUT: --- two_file_impl_mismatch.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %F.type: type = fn_type @F [concrete] // CHECK:STDOUT: %F: %F.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .F = %F.decl // CHECK:STDOUT: } // CHECK:STDOUT: %F.decl: %F.type = fn_decl @F [concrete = constants.%F] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: extern fn @F(); // CHECK:STDOUT: // CHECK:STDOUT: --- fail_two_file_impl_mismatch.impl.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %F.type: type = fn_type @F [concrete] // CHECK:STDOUT: %F: %F.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .F = %F.decl // CHECK:STDOUT: } // CHECK:STDOUT: %default.import.loc2_38.1 = import // CHECK:STDOUT: %default.import.loc2_38.2 = import // CHECK:STDOUT: %F.decl: %F.type = fn_decl @F [concrete = constants.%F] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: extern fn @F() [from "two_file_impl_mismatch.carbon"] { // CHECK:STDOUT: !entry: // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- no_decl_extern.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %F.type: type = fn_type @F [concrete] // CHECK:STDOUT: %F: %F.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .F = %F.decl // CHECK:STDOUT: } // CHECK:STDOUT: %F.decl: %F.type = fn_decl @F [concrete = constants.%F] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: extern fn @F(); // CHECK:STDOUT: // CHECK:STDOUT: --- no_decl_extern.impl.carbon // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Main.F = import_ref Main//no_decl_extern, F, unloaded // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .F = imports.%Main.F // CHECK:STDOUT: } // CHECK:STDOUT: %default.import.loc2_30.1 = import // CHECK:STDOUT: %default.import.loc2_30.2 = import // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- fail_no_decl.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %F.type: type = fn_type @F [concrete] // CHECK:STDOUT: %F: %F.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Main.F: %F.type = import_ref Main//no_decl_extern, F, loaded [concrete = constants.%F] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .F = imports.%Main.F // CHECK:STDOUT: } // CHECK:STDOUT: %default.import = import // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: extern fn @F; // CHECK:STDOUT: // CHECK:STDOUT: --- indirect_two_file_extern.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %F.type: type = fn_type @F [concrete] // CHECK:STDOUT: %F: %F.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .F = %F.decl // CHECK:STDOUT: } // CHECK:STDOUT: %F.decl: %F.type = fn_decl @F [concrete = constants.%F] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: extern fn @F(); // CHECK:STDOUT: // CHECK:STDOUT: --- fail_indirect_two_file.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %F.type: type = fn_type @F [concrete] // CHECK:STDOUT: %F: %F.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Main.F: %F.type = import_ref Main//indirect_two_file_extern, F, loaded [concrete = constants.%F] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .F = imports.%Main.F // CHECK:STDOUT: } // CHECK:STDOUT: %default.import = import // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: extern fn @F; // CHECK:STDOUT: // CHECK:STDOUT: --- fail_indirect_two_file.impl.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %F.type: type = fn_type @F [concrete] // CHECK:STDOUT: %F: %F.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .F = %F.decl // CHECK:STDOUT: } // CHECK:STDOUT: %default.import.loc2_33.1 = import // CHECK:STDOUT: %default.import.loc2_33.2 = import // CHECK:STDOUT: %F.decl: %F.type = fn_decl @F [concrete = constants.%F] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: extern fn @F() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- in_impl_extern.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %F.type: type = fn_type @F [concrete] // CHECK:STDOUT: %F: %F.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .F = %F.decl // CHECK:STDOUT: } // CHECK:STDOUT: %F.decl: %F.type = fn_decl @F [concrete = constants.%F] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: extern fn @F(); // CHECK:STDOUT: // CHECK:STDOUT: --- in_impl.carbon // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- fail_in_impl.impl.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %F.type: type = fn_type @F [concrete] // CHECK:STDOUT: %F: %F.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .F = %F.decl // CHECK:STDOUT: } // CHECK:STDOUT: %default.import.loc2_23.1 = import // CHECK:STDOUT: %default.import.loc2_23.2 = import // CHECK:STDOUT: %F.decl: %F.type = fn_decl @F [concrete = constants.%F] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: extern fn @F() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- cross_package_extern.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %Extern.type: type = fn_type @Extern [concrete] // CHECK:STDOUT: %Extern: %Extern.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Extern = %Extern.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Extern.decl: %Extern.type = fn_decl @Extern [concrete = constants.%Extern] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: extern fn @Extern(); // CHECK:STDOUT: // CHECK:STDOUT: --- cross_package.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %F.type: type = fn_type @F [concrete] // CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] // CHECK:STDOUT: %F: %F.type = struct_value () [concrete] // CHECK:STDOUT: %Extern.type: type = fn_type @Extern [concrete] // CHECK:STDOUT: %Extern: %Extern.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %OtherPackage: = namespace file.%OtherPackage.import, [concrete] { // CHECK:STDOUT: .Extern = %OtherPackage.Extern // CHECK:STDOUT: import OtherPackage//cross_package_extern // CHECK:STDOUT: } // CHECK:STDOUT: %OtherPackage.Extern: %Extern.type = import_ref OtherPackage//cross_package_extern, Extern, loaded [concrete = constants.%Extern] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .OtherPackage = imports.%OtherPackage // CHECK:STDOUT: .F = %F.decl // CHECK:STDOUT: } // CHECK:STDOUT: %OtherPackage.import = import OtherPackage // CHECK:STDOUT: %F.decl: %F.type = fn_decl @F [concrete = constants.%F] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @F() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %OtherPackage.ref: = name_ref OtherPackage, imports.%OtherPackage [concrete = imports.%OtherPackage] // CHECK:STDOUT: %Extern.ref: %Extern.type = name_ref Extern, imports.%OtherPackage.Extern [concrete = constants.%Extern] // CHECK:STDOUT: %Extern.call: init %empty_tuple.type = call %Extern.ref() // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: extern fn @Extern; // CHECK:STDOUT: // CHECK:STDOUT: --- unloaded_decl_extern.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %ExternDecl.type: type = fn_type @ExternDecl [concrete] // CHECK:STDOUT: %ExternDecl: %ExternDecl.type = struct_value () [concrete] // CHECK:STDOUT: %NonExternDecl.type: type = fn_type @NonExternDecl [concrete] // CHECK:STDOUT: %NonExternDecl: %NonExternDecl.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .ExternDecl = %ExternDecl.decl // CHECK:STDOUT: .NonExternDecl = %NonExternDecl.decl // CHECK:STDOUT: } // CHECK:STDOUT: %ExternDecl.decl: %ExternDecl.type = fn_decl @ExternDecl [concrete = constants.%ExternDecl] {} {} // CHECK:STDOUT: %NonExternDecl.decl: %NonExternDecl.type = fn_decl @NonExternDecl [concrete = constants.%NonExternDecl] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: extern fn @ExternDecl(); // CHECK:STDOUT: // CHECK:STDOUT: fn @NonExternDecl(); // CHECK:STDOUT: // CHECK:STDOUT: --- unloaded_decl.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %ExternDecl.type: type = fn_type @ExternDecl [concrete] // CHECK:STDOUT: %ExternDecl: %ExternDecl.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Main.NonExternDecl = import_ref Main//unloaded_decl_extern, NonExternDecl, unloaded // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .ExternDecl = %ExternDecl.decl // CHECK:STDOUT: .NonExternDecl = imports.%Main.NonExternDecl // CHECK:STDOUT: } // CHECK:STDOUT: %default.import = import // CHECK:STDOUT: %ExternDecl.decl: %ExternDecl.type = fn_decl @ExternDecl [concrete = constants.%ExternDecl] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: extern fn @ExternDecl; // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/function/definition/fail_decl_param_mismatch.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/none.carbon // TODO: Add ranges and switch to "--dump-sem-ir-ranges=only". // EXTRA-ARGS: --dump-sem-ir-ranges=if-present // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/function/definition/fail_decl_param_mismatch.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/function/definition/fail_decl_param_mismatch.carbon fn F(); // CHECK:STDERR: fail_decl_param_mismatch.carbon:[[@LINE+7]]:1: error: redeclaration differs because of parameter count of 1 [RedeclParamCountDiffers] // CHECK:STDERR: fn F(unused x: ()) {} // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: fail_decl_param_mismatch.carbon:[[@LINE-4]]:1: note: previously declared with parameter count of 0 [RedeclParamCountPrevious] // CHECK:STDERR: fn F(); // CHECK:STDERR: ^~~~~~~ // CHECK:STDERR: fn F(unused x: ()) {} fn G(x: ()); // CHECK:STDERR: fail_decl_param_mismatch.carbon:[[@LINE+7]]:1: error: redeclaration differs because of parameter count of 0 [RedeclParamCountDiffers] // CHECK:STDERR: fn G() {} // CHECK:STDERR: ^~~~~~~~ // CHECK:STDERR: fail_decl_param_mismatch.carbon:[[@LINE-4]]:1: note: previously declared with parameter count of 1 [RedeclParamCountPrevious] // CHECK:STDERR: fn G(x: ()); // CHECK:STDERR: ^~~~~~~~~~~~ // CHECK:STDERR: fn G() {} fn H(x: ()); // CHECK:STDERR: fail_decl_param_mismatch.carbon:[[@LINE+4]]:16: error: `Core.Bool` implicitly referenced here, but package `Core` not found [CoreNotFound] // CHECK:STDERR: fn H(unused x: bool) {} // CHECK:STDERR: ^~~~ // CHECK:STDERR: fn H(unused x: bool) {} fn I(); // CHECK:STDERR: fail_decl_param_mismatch.carbon:[[@LINE+7]]:1: error: function redeclaration differs because return type is `()` [FunctionRedeclReturnTypeDiffers] // CHECK:STDERR: fn I() -> () { return (); } // CHECK:STDERR: ^~~~~~~~~~~~~~ // CHECK:STDERR: fail_decl_param_mismatch.carbon:[[@LINE-4]]:1: note: previously declared with no return type [FunctionRedeclReturnTypePreviousNoReturn] // CHECK:STDERR: fn I(); // CHECK:STDERR: ^~~~~~~ // CHECK:STDERR: fn I() -> () { return (); } fn J() -> (); // CHECK:STDERR: fail_decl_param_mismatch.carbon:[[@LINE+7]]:1: error: function redeclaration differs because no return type is provided [FunctionRedeclReturnTypeDiffersNoReturn] // CHECK:STDERR: fn J() {} // CHECK:STDERR: ^~~~~~~~ // CHECK:STDERR: fail_decl_param_mismatch.carbon:[[@LINE-4]]:1: note: previously declared with return type `()` [FunctionRedeclReturnTypePrevious] // CHECK:STDERR: fn J() -> (); // CHECK:STDERR: ^~~~~~~~~~~~~ // CHECK:STDERR: fn J() {} fn K() -> (); // CHECK:STDERR: fail_decl_param_mismatch.carbon:[[@LINE+7]]:1: error: function redeclaration differs because return type is `{}` [FunctionRedeclReturnTypeDiffers] // CHECK:STDERR: fn K() -> {} { return {}; } // CHECK:STDERR: ^~~~~~~~~~~~~~ // CHECK:STDERR: fail_decl_param_mismatch.carbon:[[@LINE-4]]:1: note: previously declared with return type `()` [FunctionRedeclReturnTypePrevious] // CHECK:STDERR: fn K() -> (); // CHECK:STDERR: ^~~~~~~~~~~~~ // CHECK:STDERR: fn K() -> {} { return {}; } // CHECK:STDOUT: --- fail_decl_param_mismatch.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %F.type.117dbc.1: type = fn_type @F.loc15 [concrete] // CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] // CHECK:STDOUT: %F.d98bd5.1: %F.type.117dbc.1 = struct_value () [concrete] // CHECK:STDOUT: %empty_tuple: %empty_tuple.type = tuple_value () [concrete] // CHECK:STDOUT: %pattern_type.cb1: type = pattern_type %empty_tuple.type [concrete] // CHECK:STDOUT: %F.type.117dbc.2: type = fn_type @F.loc23 [concrete] // CHECK:STDOUT: %F.d98bd5.2: %F.type.117dbc.2 = struct_value () [concrete] // CHECK:STDOUT: %G.type.90f93f.1: type = fn_type @G.loc25 [concrete] // CHECK:STDOUT: %G.4dbc02.1: %G.type.90f93f.1 = struct_value () [concrete] // CHECK:STDOUT: %G.type.90f93f.2: type = fn_type @G.loc33 [concrete] // CHECK:STDOUT: %G.4dbc02.2: %G.type.90f93f.2 = struct_value () [concrete] // CHECK:STDOUT: %H.type.7917a6.1: type = fn_type @H.loc35 [concrete] // CHECK:STDOUT: %H.414c03.1: %H.type.7917a6.1 = struct_value () [concrete] // CHECK:STDOUT: %H.type.7917a6.2: type = fn_type @H.loc40 [concrete] // CHECK:STDOUT: %H.414c03.2: %H.type.7917a6.2 = struct_value () [concrete] // CHECK:STDOUT: %I.type.f30c8e.1: type = fn_type @I.loc42 [concrete] // CHECK:STDOUT: %I.73832e.1: %I.type.f30c8e.1 = struct_value () [concrete] // CHECK:STDOUT: %.262: Core.Form = init_form %empty_tuple.type [concrete] // CHECK:STDOUT: %I.type.f30c8e.2: type = fn_type @I.loc50 [concrete] // CHECK:STDOUT: %I.73832e.2: %I.type.f30c8e.2 = struct_value () [concrete] // CHECK:STDOUT: %J.type.fe0423.1: type = fn_type @J.loc52 [concrete] // CHECK:STDOUT: %J.6d8208.1: %J.type.fe0423.1 = struct_value () [concrete] // CHECK:STDOUT: %J.type.fe0423.2: type = fn_type @J.loc60 [concrete] // CHECK:STDOUT: %J.6d8208.2: %J.type.fe0423.2 = struct_value () [concrete] // CHECK:STDOUT: %K.type.4b1c50.1: type = fn_type @K.loc62 [concrete] // CHECK:STDOUT: %K.548a8d.1: %K.type.4b1c50.1 = struct_value () [concrete] // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %empty_struct: %empty_struct_type = struct_value () [concrete] // CHECK:STDOUT: %.469: Core.Form = init_form %empty_struct_type [concrete] // CHECK:STDOUT: %pattern_type.a96: type = pattern_type %empty_struct_type [concrete] // CHECK:STDOUT: %K.type.4b1c50.2: type = fn_type @K.loc70 [concrete] // CHECK:STDOUT: %K.548a8d.2: %K.type.4b1c50.2 = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .F = %F.decl.loc15 // CHECK:STDOUT: .G = %G.decl.loc25 // CHECK:STDOUT: .H = %H.decl.loc35 // CHECK:STDOUT: .I = %I.decl.loc42 // CHECK:STDOUT: .J = %J.decl.loc52 // CHECK:STDOUT: .K = %K.decl.loc62 // CHECK:STDOUT: } // CHECK:STDOUT: %F.decl.loc15: %F.type.117dbc.1 = fn_decl @F.loc15 [concrete = constants.%F.d98bd5.1] {} {} // CHECK:STDOUT: %F.decl.loc23: %F.type.117dbc.2 = fn_decl @F.loc23 [concrete = constants.%F.d98bd5.2] { // CHECK:STDOUT: %x.patt: %pattern_type.cb1 = value_binding_pattern x [concrete] // CHECK:STDOUT: %x.param_patt: %pattern_type.cb1 = value_param_pattern %x.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %x.param: %empty_tuple.type = value_param call_param0 // CHECK:STDOUT: %.loc23_17.1: type = splice_block %.loc23_17.3 [concrete = constants.%empty_tuple.type] { // CHECK:STDOUT: %.loc23_17.2: %empty_tuple.type = tuple_literal () [concrete = constants.%empty_tuple] // CHECK:STDOUT: %.loc23_17.3: type = converted %.loc23_17.2, constants.%empty_tuple.type [concrete = constants.%empty_tuple.type] // CHECK:STDOUT: } // CHECK:STDOUT: %x: %empty_tuple.type = value_binding x, %x.param // CHECK:STDOUT: } // CHECK:STDOUT: %G.decl.loc25: %G.type.90f93f.1 = fn_decl @G.loc25 [concrete = constants.%G.4dbc02.1] { // CHECK:STDOUT: %x.patt: %pattern_type.cb1 = value_binding_pattern x [concrete] // CHECK:STDOUT: %x.param_patt: %pattern_type.cb1 = value_param_pattern %x.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %x.param: %empty_tuple.type = value_param call_param0 // CHECK:STDOUT: %.loc25_10.1: type = splice_block %.loc25_10.3 [concrete = constants.%empty_tuple.type] { // CHECK:STDOUT: %.loc25_10.2: %empty_tuple.type = tuple_literal () [concrete = constants.%empty_tuple] // CHECK:STDOUT: %.loc25_10.3: type = converted %.loc25_10.2, constants.%empty_tuple.type [concrete = constants.%empty_tuple.type] // CHECK:STDOUT: } // CHECK:STDOUT: %x: %empty_tuple.type = value_binding x, %x.param // CHECK:STDOUT: } // CHECK:STDOUT: %G.decl.loc33: %G.type.90f93f.2 = fn_decl @G.loc33 [concrete = constants.%G.4dbc02.2] {} {} // CHECK:STDOUT: %H.decl.loc35: %H.type.7917a6.1 = fn_decl @H.loc35 [concrete = constants.%H.414c03.1] { // CHECK:STDOUT: %x.patt: %pattern_type.cb1 = value_binding_pattern x [concrete] // CHECK:STDOUT: %x.param_patt: %pattern_type.cb1 = value_param_pattern %x.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %x.param: %empty_tuple.type = value_param call_param0 // CHECK:STDOUT: %.loc35_10.1: type = splice_block %.loc35_10.3 [concrete = constants.%empty_tuple.type] { // CHECK:STDOUT: %.loc35_10.2: %empty_tuple.type = tuple_literal () [concrete = constants.%empty_tuple] // CHECK:STDOUT: %.loc35_10.3: type = converted %.loc35_10.2, constants.%empty_tuple.type [concrete = constants.%empty_tuple.type] // CHECK:STDOUT: } // CHECK:STDOUT: %x: %empty_tuple.type = value_binding x, %x.param // CHECK:STDOUT: } // CHECK:STDOUT: %H.decl.loc40: %H.type.7917a6.2 = fn_decl @H.loc40 [concrete = constants.%H.414c03.2] { // CHECK:STDOUT: %x.patt: = value_binding_pattern x [concrete] // CHECK:STDOUT: %x.param_patt: = value_param_pattern %x.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %x.param: = value_param call_param0 // CHECK:STDOUT: %.loc40: type = type_literal [concrete = ] // CHECK:STDOUT: %x: = value_binding x, %x.param // CHECK:STDOUT: } // CHECK:STDOUT: %I.decl.loc42: %I.type.f30c8e.1 = fn_decl @I.loc42 [concrete = constants.%I.73832e.1] {} {} // CHECK:STDOUT: %I.decl.loc50: %I.type.f30c8e.2 = fn_decl @I.loc50 [concrete = constants.%I.73832e.2] { // CHECK:STDOUT: %return.patt: %pattern_type.cb1 = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: %pattern_type.cb1 = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %.loc50_12.1: %empty_tuple.type = tuple_literal () [concrete = constants.%empty_tuple] // CHECK:STDOUT: %.loc50_12.2: type = converted %.loc50_12.1, constants.%empty_tuple.type [concrete = constants.%empty_tuple.type] // CHECK:STDOUT: %.loc50_12.3: Core.Form = init_form %.loc50_12.2 [concrete = constants.%.262] // CHECK:STDOUT: %return.param: ref %empty_tuple.type = out_param call_param0 // CHECK:STDOUT: %return: ref %empty_tuple.type = return_slot %return.param // CHECK:STDOUT: } // CHECK:STDOUT: %J.decl.loc52: %J.type.fe0423.1 = fn_decl @J.loc52 [concrete = constants.%J.6d8208.1] { // CHECK:STDOUT: %return.patt: %pattern_type.cb1 = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: %pattern_type.cb1 = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %.loc52_12.1: %empty_tuple.type = tuple_literal () [concrete = constants.%empty_tuple] // CHECK:STDOUT: %.loc52_12.2: type = converted %.loc52_12.1, constants.%empty_tuple.type [concrete = constants.%empty_tuple.type] // CHECK:STDOUT: %.loc52_12.3: Core.Form = init_form %.loc52_12.2 [concrete = constants.%.262] // CHECK:STDOUT: %return.param: ref %empty_tuple.type = out_param call_param0 // CHECK:STDOUT: %return: ref %empty_tuple.type = return_slot %return.param // CHECK:STDOUT: } // CHECK:STDOUT: %J.decl.loc60: %J.type.fe0423.2 = fn_decl @J.loc60 [concrete = constants.%J.6d8208.2] {} {} // CHECK:STDOUT: %K.decl.loc62: %K.type.4b1c50.1 = fn_decl @K.loc62 [concrete = constants.%K.548a8d.1] { // CHECK:STDOUT: %return.patt: %pattern_type.cb1 = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: %pattern_type.cb1 = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %.loc62_12.1: %empty_tuple.type = tuple_literal () [concrete = constants.%empty_tuple] // CHECK:STDOUT: %.loc62_12.2: type = converted %.loc62_12.1, constants.%empty_tuple.type [concrete = constants.%empty_tuple.type] // CHECK:STDOUT: %.loc62_12.3: Core.Form = init_form %.loc62_12.2 [concrete = constants.%.262] // CHECK:STDOUT: %return.param: ref %empty_tuple.type = out_param call_param0 // CHECK:STDOUT: %return: ref %empty_tuple.type = return_slot %return.param // CHECK:STDOUT: } // CHECK:STDOUT: %K.decl.loc70: %K.type.4b1c50.2 = fn_decl @K.loc70 [concrete = constants.%K.548a8d.2] { // CHECK:STDOUT: %return.patt: %pattern_type.a96 = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: %pattern_type.a96 = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %.loc70_12.1: %empty_struct_type = struct_literal () [concrete = constants.%empty_struct] // CHECK:STDOUT: %.loc70_12.2: type = converted %.loc70_12.1, constants.%empty_struct_type [concrete = constants.%empty_struct_type] // CHECK:STDOUT: %.loc70_12.3: Core.Form = init_form %.loc70_12.2 [concrete = constants.%.469] // CHECK:STDOUT: %return.param: ref %empty_struct_type = out_param call_param0 // CHECK:STDOUT: %return: ref %empty_struct_type = return_slot %return.param // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @F.loc15(); // CHECK:STDOUT: // CHECK:STDOUT: fn @F.loc23(%x.param: %empty_tuple.type) { // CHECK:STDOUT: !entry: // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @G.loc25(%x.param: %empty_tuple.type); // CHECK:STDOUT: // CHECK:STDOUT: fn @G.loc33() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @H.loc35(%x.param: %empty_tuple.type); // CHECK:STDOUT: // CHECK:STDOUT: fn @H.loc40(%x.param: ) { // CHECK:STDOUT: !entry: // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @I.loc42(); // CHECK:STDOUT: // CHECK:STDOUT: fn @I.loc50() -> out %return.param: %empty_tuple.type { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %.loc50_24.1: %empty_tuple.type = tuple_literal () [concrete = constants.%empty_tuple] // CHECK:STDOUT: %.loc50_24.2: init %empty_tuple.type = tuple_init () [concrete = constants.%empty_tuple] // CHECK:STDOUT: %.loc50_25: init %empty_tuple.type = converted %.loc50_24.1, %.loc50_24.2 [concrete = constants.%empty_tuple] // CHECK:STDOUT: return %.loc50_25 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @J.loc52() -> out %return.param: %empty_tuple.type; // CHECK:STDOUT: // CHECK:STDOUT: fn @J.loc60() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @K.loc62() -> out %return.param: %empty_tuple.type; // CHECK:STDOUT: // CHECK:STDOUT: fn @K.loc70() -> out %return.param: %empty_struct_type { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %.loc70_24.1: %empty_struct_type = struct_literal () [concrete = constants.%empty_struct] // CHECK:STDOUT: %.loc70_24.2: init %empty_struct_type = struct_init () [concrete = constants.%empty_struct] // CHECK:STDOUT: %.loc70_25: init %empty_struct_type = converted %.loc70_24.1, %.loc70_24.2 [concrete = constants.%empty_struct] // CHECK:STDOUT: return %.loc70_25 // CHECK:STDOUT: } // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/function/definition/fail_local_decl.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/destroy.carbon // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/function/definition/fail_local_decl.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/function/definition/fail_local_decl.carbon // --- fail_virtual.carbon library "[[@TEST_NAME]]"; fn F() { // CHECK:STDERR: fail_virtual.carbon:[[@LINE+4]]:3: error: `default` not allowed; requires interface scope [ModifierRequiresInterface] // CHECK:STDERR: default fn F(); // CHECK:STDERR: ^~~~~~~ // CHECK:STDERR: default fn F(); // CHECK:STDERR: fail_virtual.carbon:[[@LINE+4]]:3: error: `override` not allowed; requires class scope [ModifierRequiresClass] // CHECK:STDERR: override fn G(); // CHECK:STDERR: ^~~~~~~~ // CHECK:STDERR: override fn G(); // CHECK:STDERR: fail_virtual.carbon:[[@LINE+4]]:3: error: `virtual` not allowed; requires class scope [ModifierRequiresClass] // CHECK:STDERR: virtual fn H(); // CHECK:STDERR: ^~~~~~~ // CHECK:STDERR: virtual fn H(); } // --- fail_access.carbon library "[[@TEST_NAME]]"; fn F() { // CHECK:STDERR: fail_access.carbon:[[@LINE+4]]:3: error: `private` not allowed; requires class or file scope [ModifierPrivateNotAllowed] // CHECK:STDERR: private var unused v: {} = {}; // CHECK:STDERR: ^~~~~~~ // CHECK:STDERR: private var unused v: {} = {}; // CHECK:STDERR: fail_access.carbon:[[@LINE+4]]:3: error: `protected` not allowed; requires class scope [ModifierProtectedNotAllowed] // CHECK:STDERR: protected var unused w: {} = {}; // CHECK:STDERR: ^~~~~~~~~ // CHECK:STDERR: protected var unused w: {} = {}; } ================================================ FILE: toolchain/check/testdata/function/definition/fail_redef.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/none.carbon // TODO: Add ranges and switch to "--dump-sem-ir-ranges=only". // EXTRA-ARGS: --dump-sem-ir-ranges=if-present // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/function/definition/fail_redef.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/function/definition/fail_redef.carbon fn F() {} // CHECK:STDERR: fail_redef.carbon:[[@LINE+7]]:1: error: redefinition of `fn F` [RedeclRedef] // CHECK:STDERR: fn F() {} // CHECK:STDERR: ^~~~~~~~ // CHECK:STDERR: fail_redef.carbon:[[@LINE-4]]:1: note: previously defined here [RedeclPrevDef] // CHECK:STDERR: fn F() {} // CHECK:STDERR: ^~~~~~~~ // CHECK:STDERR: fn F() {} // CHECK:STDOUT: --- fail_redef.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %F.type.117dbc.1: type = fn_type @F.loc15 [concrete] // CHECK:STDOUT: %F.d98bd5.1: %F.type.117dbc.1 = struct_value () [concrete] // CHECK:STDOUT: %F.type.117dbc.2: type = fn_type @F.loc23 [concrete] // CHECK:STDOUT: %F.d98bd5.2: %F.type.117dbc.2 = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .F = %F.decl.loc15 // CHECK:STDOUT: } // CHECK:STDOUT: %F.decl.loc15: %F.type.117dbc.1 = fn_decl @F.loc15 [concrete = constants.%F.d98bd5.1] {} {} // CHECK:STDOUT: %F.decl.loc23: %F.type.117dbc.2 = fn_decl @F.loc23 [concrete = constants.%F.d98bd5.2] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @F.loc15() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @F.loc23() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/function/definition/forward_decl.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/none.carbon // TODO: Add ranges and switch to "--dump-sem-ir-ranges=only". // EXTRA-ARGS: --dump-sem-ir-ranges=if-present // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/function/definition/forward_decl.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/function/definition/forward_decl.carbon fn Foo(); fn Foo() {} // CHECK:STDOUT: --- forward_decl.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %Foo.type: type = fn_type @Foo [concrete] // CHECK:STDOUT: %Foo: %Foo.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Foo = %Foo.decl.loc15 // CHECK:STDOUT: } // CHECK:STDOUT: %Foo.decl.loc15: %Foo.type = fn_decl @Foo [concrete = constants.%Foo] {} {} // CHECK:STDOUT: %Foo.decl.loc17: %Foo.type = fn_decl @Foo [concrete = constants.%Foo] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @Foo() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/function/definition/implicit_import.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/none.carbon // TODO: Add ranges and switch to "--dump-sem-ir-ranges=only". // EXTRA-ARGS: --dump-sem-ir-ranges=if-present // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/function/definition/implicit_import.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/function/definition/implicit_import.carbon // --- basic.carbon library "[[@TEST_NAME]]"; fn A(); // --- basic.impl.carbon impl library "[[@TEST_NAME]]"; fn A() {} // --- extern_api.carbon library "[[@TEST_NAME]]"; extern fn A(); // --- fail_extern_api.impl.carbon impl library "[[@TEST_NAME]]"; // CHECK:STDERR: fail_extern_api.impl.carbon:[[@LINE+8]]:1: error: redeclarations of `fn A` must match use of `extern` [RedeclExternMismatch] // CHECK:STDERR: fn A() {} // CHECK:STDERR: ^~~~~~~~ // CHECK:STDERR: fail_extern_api.impl.carbon:[[@LINE-5]]:1: in import [InImport] // CHECK:STDERR: extern_api.carbon:4:1: note: previously declared here [RedeclPrevDecl] // CHECK:STDERR: extern fn A(); // CHECK:STDERR: ^~~~~~~~~~~~~~ // CHECK:STDERR: fn A() {} // --- extern_impl.carbon library "[[@TEST_NAME]]"; fn A(); // --- fail_extern_impl.impl.carbon impl library "[[@TEST_NAME]]"; // CHECK:STDERR: fail_extern_impl.impl.carbon:[[@LINE+8]]:1: error: redeclarations of `fn A` must match use of `extern` [RedeclExternMismatch] // CHECK:STDERR: extern fn A() {} // CHECK:STDERR: ^~~~~~~~~~~~~~~ // CHECK:STDERR: fail_extern_impl.impl.carbon:[[@LINE-5]]:1: in import [InImport] // CHECK:STDERR: extern_impl.carbon:4:1: note: previously declared here [RedeclPrevDecl] // CHECK:STDERR: fn A(); // CHECK:STDERR: ^~~~~~~ // CHECK:STDERR: extern fn A() {} // --- redecl_after_def.carbon library "[[@TEST_NAME]]"; fn A() {} // --- fail_redecl_after_def.impl.carbon impl library "[[@TEST_NAME]]"; // CHECK:STDERR: fail_redecl_after_def.impl.carbon:[[@LINE+8]]:1: error: redeclaration of `fn A` is redundant [RedeclRedundant] // CHECK:STDERR: fn A(); // CHECK:STDERR: ^~~~~~~ // CHECK:STDERR: fail_redecl_after_def.impl.carbon:[[@LINE-5]]:1: in import [InImport] // CHECK:STDERR: redecl_after_def.carbon:4:1: note: previously declared here [RedeclPrevDecl] // CHECK:STDERR: fn A() {} // CHECK:STDERR: ^~~~~~~~ // CHECK:STDERR: fn A(); // --- redef_after_def.carbon library "[[@TEST_NAME]]"; fn A() {} // --- fail_redef_after_def.impl.carbon impl library "[[@TEST_NAME]]"; // CHECK:STDERR: fail_redef_after_def.impl.carbon:[[@LINE+8]]:1: error: redefinition of `fn A` [RedeclRedef] // CHECK:STDERR: fn A() {} // CHECK:STDERR: ^~~~~~~~ // CHECK:STDERR: fail_redef_after_def.impl.carbon:[[@LINE-5]]:1: in import [InImport] // CHECK:STDERR: redef_after_def.carbon:4:1: note: previously defined here [RedeclPrevDef] // CHECK:STDERR: fn A() {} // CHECK:STDERR: ^~~~~~~~ // CHECK:STDERR: fn A() {} // --- def_alias.carbon library "[[@TEST_NAME]]"; fn A(); alias B = A; // --- fail_def_alias.impl.carbon impl library "[[@TEST_NAME]]"; // CHECK:STDERR: fail_def_alias.impl.carbon:[[@LINE+8]]:4: error: duplicate name `B` being declared in the same scope [NameDeclDuplicate] // CHECK:STDERR: fn B() {} // CHECK:STDERR: ^ // CHECK:STDERR: fail_def_alias.impl.carbon:[[@LINE-5]]:1: in import [InImport] // CHECK:STDERR: def_alias.carbon:5:7: note: name is previously declared here [NameDeclPrevious] // CHECK:STDERR: alias B = A; // CHECK:STDERR: ^ // CHECK:STDERR: fn B() {} // CHECK:STDOUT: --- basic.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %A.type: type = fn_type @A [concrete] // CHECK:STDOUT: %A: %A.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .A = %A.decl // CHECK:STDOUT: } // CHECK:STDOUT: %A.decl: %A.type = fn_decl @A [concrete = constants.%A] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @A(); // CHECK:STDOUT: // CHECK:STDOUT: --- basic.impl.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %A.type: type = fn_type @A [concrete] // CHECK:STDOUT: %A: %A.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .A = %A.decl // CHECK:STDOUT: } // CHECK:STDOUT: %default.import.loc2_21.1 = import // CHECK:STDOUT: %default.import.loc2_21.2 = import // CHECK:STDOUT: %A.decl: %A.type = fn_decl @A [concrete = constants.%A] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @A() [from "basic.carbon"] { // CHECK:STDOUT: !entry: // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- extern_api.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %A.type: type = fn_type @A [concrete] // CHECK:STDOUT: %A: %A.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .A = %A.decl // CHECK:STDOUT: } // CHECK:STDOUT: %A.decl: %A.type = fn_decl @A [concrete = constants.%A] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: extern fn @A(); // CHECK:STDOUT: // CHECK:STDOUT: --- fail_extern_api.impl.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %A.type: type = fn_type @A [concrete] // CHECK:STDOUT: %A: %A.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .A = %A.decl // CHECK:STDOUT: } // CHECK:STDOUT: %default.import.loc2_26.1 = import // CHECK:STDOUT: %default.import.loc2_26.2 = import // CHECK:STDOUT: %A.decl: %A.type = fn_decl @A [concrete = constants.%A] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: extern fn @A() [from "extern_api.carbon"] { // CHECK:STDOUT: !entry: // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- extern_impl.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %A.type: type = fn_type @A [concrete] // CHECK:STDOUT: %A: %A.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .A = %A.decl // CHECK:STDOUT: } // CHECK:STDOUT: %A.decl: %A.type = fn_decl @A [concrete = constants.%A] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @A(); // CHECK:STDOUT: // CHECK:STDOUT: --- fail_extern_impl.impl.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %A.type: type = fn_type @A [concrete] // CHECK:STDOUT: %A: %A.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .A = %A.decl // CHECK:STDOUT: } // CHECK:STDOUT: %default.import.loc2_27.1 = import // CHECK:STDOUT: %default.import.loc2_27.2 = import // CHECK:STDOUT: %A.decl: %A.type = fn_decl @A [concrete = constants.%A] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @A() [from "extern_impl.carbon"] { // CHECK:STDOUT: !entry: // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- redecl_after_def.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %A.type: type = fn_type @A [concrete] // CHECK:STDOUT: %A: %A.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .A = %A.decl // CHECK:STDOUT: } // CHECK:STDOUT: %A.decl: %A.type = fn_decl @A [concrete = constants.%A] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @A() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- fail_redecl_after_def.impl.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %A.type: type = fn_type @A [concrete] // CHECK:STDOUT: %A: %A.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .A = %A.decl // CHECK:STDOUT: } // CHECK:STDOUT: %default.import.loc2_32.1 = import // CHECK:STDOUT: %default.import.loc2_32.2 = import // CHECK:STDOUT: %A.decl: %A.type = fn_decl @A [concrete = constants.%A] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @A [from "redecl_after_def.carbon"]; // CHECK:STDOUT: // CHECK:STDOUT: --- redef_after_def.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %A.type: type = fn_type @A [concrete] // CHECK:STDOUT: %A: %A.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .A = %A.decl // CHECK:STDOUT: } // CHECK:STDOUT: %A.decl: %A.type = fn_decl @A [concrete = constants.%A] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @A() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- fail_redef_after_def.impl.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %A.type.8165c1.1: type = fn_type @A.1 [concrete] // CHECK:STDOUT: %A.8aef9d.1: %A.type.8165c1.1 = struct_value () [concrete] // CHECK:STDOUT: %A.type.8165c1.2: type = fn_type @A.loc12 [concrete] // CHECK:STDOUT: %A.8aef9d.2: %A.type.8165c1.2 = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Main.A: %A.type.8165c1.1 = import_ref Main//redef_after_def, A, loaded [concrete = constants.%A.8aef9d.1] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .A = imports.%Main.A // CHECK:STDOUT: } // CHECK:STDOUT: %default.import.loc2_31.1 = import // CHECK:STDOUT: %default.import.loc2_31.2 = import // CHECK:STDOUT: %A.decl: %A.type.8165c1.2 = fn_decl @A.loc12 [concrete = constants.%A.8aef9d.2] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @A.1 [from "redef_after_def.carbon"]; // CHECK:STDOUT: // CHECK:STDOUT: fn @A.loc12() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- def_alias.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %A.type: type = fn_type @A [concrete] // CHECK:STDOUT: %A: %A.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .A = %A.decl // CHECK:STDOUT: .B = %B // CHECK:STDOUT: } // CHECK:STDOUT: %A.decl: %A.type = fn_decl @A [concrete = constants.%A] {} {} // CHECK:STDOUT: %A.ref: %A.type = name_ref A, %A.decl [concrete = constants.%A] // CHECK:STDOUT: %B: %A.type = alias_binding B, %A.decl [concrete = constants.%A] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @A(); // CHECK:STDOUT: // CHECK:STDOUT: --- fail_def_alias.impl.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %A.type: type = fn_type @A [concrete] // CHECK:STDOUT: %A: %A.type = struct_value () [concrete] // CHECK:STDOUT: %B.type: type = fn_type @B [concrete] // CHECK:STDOUT: %B: %B.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Main.A = import_ref Main//def_alias, A, unloaded // CHECK:STDOUT: %Main.B: %A.type = import_ref Main//def_alias, B, loaded [concrete = constants.%A] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .A = imports.%Main.A // CHECK:STDOUT: .B = imports.%Main.B // CHECK:STDOUT: } // CHECK:STDOUT: %default.import.loc2_25.1 = import // CHECK:STDOUT: %default.import.loc2_25.2 = import // CHECK:STDOUT: %B.decl: %B.type = fn_decl @B [concrete = constants.%B] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @A [from "def_alias.carbon"]; // CHECK:STDOUT: // CHECK:STDOUT: fn @B() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/function/definition/import.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/int.carbon // TODO: Add ranges and switch to "--dump-sem-ir-ranges=only". // EXTRA-ARGS: --dump-sem-ir-ranges=if-present // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/function/definition/import.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/function/definition/import.carbon // ============================================================================ // Setup files // ============================================================================ // --- fns.carbon library "[[@TEST_NAME]]"; fn A() {} fn B(b: i32) -> i32 { return b; } fn C(c: (i32,)) -> {.c: i32} { return {.c = c.0}; } fn D(); // --- extern.carbon library "[[@TEST_NAME]]"; extern fn A(); // ============================================================================ // Test files // ============================================================================ // --- basics.carbon library "[[@TEST_NAME]]"; import library "fns"; var a: () = A(); var b: i32 = B(1); var c: {.c: i32} = C((1,)); // --- fail_def_ownership.carbon library "[[@TEST_NAME]]"; import library "fns"; // CHECK:STDERR: fail_def_ownership.carbon:[[@LINE+8]]:1: error: redeclaration of `fn A` is redundant [RedeclRedundant] // CHECK:STDERR: fn A() {}; // CHECK:STDERR: ^~~~~~~~ // CHECK:STDERR: fail_def_ownership.carbon:[[@LINE-5]]:1: in import [InImport] // CHECK:STDERR: fns.carbon:4:1: note: previously declared here [RedeclPrevDecl] // CHECK:STDERR: fn A() {} // CHECK:STDERR: ^~~~~~~~ // CHECK:STDERR: fn A() {}; // CHECK:STDERR: fail_def_ownership.carbon:[[@LINE+8]]:1: error: redeclaration of `fn B` is redundant [RedeclRedundant] // CHECK:STDERR: fn B(b: i32) -> i32; // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: fail_def_ownership.carbon:[[@LINE-14]]:1: in import [InImport] // CHECK:STDERR: fns.carbon:5:1: note: previously declared here [RedeclPrevDecl] // CHECK:STDERR: fn B(b: i32) -> i32 { return b; } // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: fn B(b: i32) -> i32; // --- fail_redecl_then_def.carbon library "[[@TEST_NAME]]"; import library "extern"; // CHECK:STDERR: fail_redecl_then_def.carbon:[[@LINE+8]]:1: error: redeclarations of `fn A` must match use of `extern` [RedeclExternMismatch] // CHECK:STDERR: fn A(); // CHECK:STDERR: ^~~~~~~ // CHECK:STDERR: fail_redecl_then_def.carbon:[[@LINE-5]]:1: in import [InImport] // CHECK:STDERR: extern.carbon:4:1: note: previously declared here [RedeclPrevDecl] // CHECK:STDERR: extern fn A(); // CHECK:STDERR: ^~~~~~~~~~~~~~ // CHECK:STDERR: fn A(); // CHECK:STDERR: fail_redecl_then_def.carbon:[[@LINE+8]]:1: error: redeclarations of `fn A` must match use of `extern` [RedeclExternMismatch] // CHECK:STDERR: fn A() {} // CHECK:STDERR: ^~~~~~~~ // CHECK:STDERR: fail_redecl_then_def.carbon:[[@LINE-15]]:1: in import [InImport] // CHECK:STDERR: extern.carbon:4:1: note: previously declared here [RedeclPrevDecl] // CHECK:STDERR: extern fn A(); // CHECK:STDERR: ^~~~~~~~~~~~~~ // CHECK:STDERR: fn A() {} // --- fail_mix_extern_decl.carbon library "[[@TEST_NAME]]"; import library "fns"; // CHECK:STDERR: fail_mix_extern_decl.carbon:[[@LINE+8]]:1: error: redeclarations of `fn D` must match use of `extern` [RedeclExternMismatch] // CHECK:STDERR: extern fn D(); // CHECK:STDERR: ^~~~~~~~~~~~~~ // CHECK:STDERR: fail_mix_extern_decl.carbon:[[@LINE-5]]:1: in import [InImport] // CHECK:STDERR: fns.carbon:7:1: note: previously declared here [RedeclPrevDecl] // CHECK:STDERR: fn D(); // CHECK:STDERR: ^~~~~~~ // CHECK:STDERR: extern fn D(); fn D() {} // CHECK:STDOUT: --- fns.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %A.type: type = fn_type @A [concrete] // CHECK:STDOUT: %A: %A.type = struct_value () [concrete] // CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [concrete] // CHECK:STDOUT: %Int.type: type = generic_class_type @Int [concrete] // CHECK:STDOUT: %Int.generic: %Int.type = struct_value () [concrete] // CHECK:STDOUT: %N: Core.IntLiteral = symbolic_binding N, 0 [symbolic] // CHECK:STDOUT: %i32: type = class_type @Int, @Int(%int_32) [concrete] // CHECK:STDOUT: %pattern_type.7ce: type = pattern_type %i32 [concrete] // CHECK:STDOUT: %.ff5: Core.Form = init_form %i32 [concrete] // CHECK:STDOUT: %B.type: type = fn_type @B [concrete] // CHECK:STDOUT: %B: %B.type = struct_value () [concrete] // CHECK:STDOUT: %Copy.type: type = facet_type <@Copy> [concrete] // CHECK:STDOUT: %Int.as.Copy.impl.Op.type.824: type = fn_type @Int.as.Copy.impl.Op, @Int.as.Copy.impl(%N) [symbolic] // CHECK:STDOUT: %Int.as.Copy.impl.Op.9b9: %Int.as.Copy.impl.Op.type.824 = struct_value () [symbolic] // CHECK:STDOUT: %Copy.impl_witness.f17: = impl_witness imports.%Copy.impl_witness_table.e76, @Int.as.Copy.impl(%int_32) [concrete] // CHECK:STDOUT: %Int.as.Copy.impl.Op.type.546: type = fn_type @Int.as.Copy.impl.Op, @Int.as.Copy.impl(%int_32) [concrete] // CHECK:STDOUT: %Int.as.Copy.impl.Op.664: %Int.as.Copy.impl.Op.type.546 = struct_value () [concrete] // CHECK:STDOUT: %Copy.facet: %Copy.type = facet_value %i32, (%Copy.impl_witness.f17) [concrete] // CHECK:STDOUT: %Copy.WithSelf.Op.type.081: type = fn_type @Copy.WithSelf.Op, @Copy.WithSelf(%Copy.facet) [concrete] // CHECK:STDOUT: %.8e2: type = fn_type_with_self_type %Copy.WithSelf.Op.type.081, %Copy.facet [concrete] // CHECK:STDOUT: %Int.as.Copy.impl.Op.specific_fn: = specific_function %Int.as.Copy.impl.Op.664, @Int.as.Copy.impl.Op(%int_32) [concrete] // CHECK:STDOUT: %tuple.type.85c: type = tuple_type (type) [concrete] // CHECK:STDOUT: %tuple.896: %tuple.type.85c = tuple_value (%i32) [concrete] // CHECK:STDOUT: %tuple.type.a1c: type = tuple_type (%i32) [concrete] // CHECK:STDOUT: %pattern_type.b74: type = pattern_type %tuple.type.a1c [concrete] // CHECK:STDOUT: %struct_type.c: type = struct_type {.c: %i32} [concrete] // CHECK:STDOUT: %.da9: Core.Form = init_form %struct_type.c [concrete] // CHECK:STDOUT: %pattern_type.688: type = pattern_type %struct_type.c [concrete] // CHECK:STDOUT: %C.type: type = fn_type @C [concrete] // CHECK:STDOUT: %C: %C.type = struct_value () [concrete] // CHECK:STDOUT: %int_0: Core.IntLiteral = int_value 0 [concrete] // CHECK:STDOUT: %D.type: type = fn_type @D [concrete] // CHECK:STDOUT: %D: %D.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: .Int = %Core.Int // CHECK:STDOUT: .Copy = %Core.Copy // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: %Core.Int: %Int.type = import_ref Core//prelude/parts/int, Int, loaded [concrete = constants.%Int.generic] // CHECK:STDOUT: %Core.Copy: type = import_ref Core//prelude/parts/copy, Copy, loaded [concrete = constants.%Copy.type] // CHECK:STDOUT: %Core.import_ref.18d: @Int.as.Copy.impl.%Int.as.Copy.impl.Op.type (%Int.as.Copy.impl.Op.type.824) = import_ref Core//prelude/parts/int, loc{{\d+_\d+}}, loaded [symbolic = @Int.as.Copy.impl.%Int.as.Copy.impl.Op (constants.%Int.as.Copy.impl.Op.9b9)] // CHECK:STDOUT: %Copy.impl_witness_table.e76 = impl_witness_table (%Core.import_ref.18d), @Int.as.Copy.impl [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .A = %A.decl // CHECK:STDOUT: .B = %B.decl // CHECK:STDOUT: .C = %C.decl // CHECK:STDOUT: .D = %D.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %A.decl: %A.type = fn_decl @A [concrete = constants.%A] {} {} // CHECK:STDOUT: %B.decl: %B.type = fn_decl @B [concrete = constants.%B] { // CHECK:STDOUT: %b.patt: %pattern_type.7ce = value_binding_pattern b [concrete] // CHECK:STDOUT: %b.param_patt: %pattern_type.7ce = value_param_pattern %b.patt [concrete] // CHECK:STDOUT: %return.patt: %pattern_type.7ce = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: %pattern_type.7ce = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %i32.loc5_17: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %.loc5: Core.Form = init_form %i32.loc5_17 [concrete = constants.%.ff5] // CHECK:STDOUT: %b.param: %i32 = value_param call_param0 // CHECK:STDOUT: %i32.loc5_9: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %b: %i32 = value_binding b, %b.param // CHECK:STDOUT: %return.param: ref %i32 = out_param call_param1 // CHECK:STDOUT: %return: ref %i32 = return_slot %return.param // CHECK:STDOUT: } // CHECK:STDOUT: %C.decl: %C.type = fn_decl @C [concrete = constants.%C] { // CHECK:STDOUT: %c.patt: %pattern_type.b74 = value_binding_pattern c [concrete] // CHECK:STDOUT: %c.param_patt: %pattern_type.b74 = value_param_pattern %c.patt [concrete] // CHECK:STDOUT: %return.patt: %pattern_type.688 = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: %pattern_type.688 = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %i32.loc6_25: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %struct_type.c: type = struct_type {.c: %i32} [concrete = constants.%struct_type.c] // CHECK:STDOUT: %.loc6_28: Core.Form = init_form %struct_type.c [concrete = constants.%.da9] // CHECK:STDOUT: %c.param: %tuple.type.a1c = value_param call_param0 // CHECK:STDOUT: %.loc6_14.1: type = splice_block %.loc6_14.3 [concrete = constants.%tuple.type.a1c] { // CHECK:STDOUT: %i32.loc6_10: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %.loc6_14.2: %tuple.type.85c = tuple_literal (%i32.loc6_10) [concrete = constants.%tuple.896] // CHECK:STDOUT: %.loc6_14.3: type = converted %.loc6_14.2, constants.%tuple.type.a1c [concrete = constants.%tuple.type.a1c] // CHECK:STDOUT: } // CHECK:STDOUT: %c: %tuple.type.a1c = value_binding c, %c.param // CHECK:STDOUT: %return.param: ref %struct_type.c = out_param call_param1 // CHECK:STDOUT: %return: ref %struct_type.c = return_slot %return.param // CHECK:STDOUT: } // CHECK:STDOUT: %D.decl: %D.type = fn_decl @D [concrete = constants.%D] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @A() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @B(%b.param: %i32) -> out %return.param: %i32 { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %b.ref: %i32 = name_ref b, %b // CHECK:STDOUT: %impl.elem0: %.8e2 = impl_witness_access constants.%Copy.impl_witness.f17, element0 [concrete = constants.%Int.as.Copy.impl.Op.664] // CHECK:STDOUT: %bound_method.loc5_30.1: = bound_method %b.ref, %impl.elem0 // CHECK:STDOUT: %specific_fn: = specific_function %impl.elem0, @Int.as.Copy.impl.Op(constants.%int_32) [concrete = constants.%Int.as.Copy.impl.Op.specific_fn] // CHECK:STDOUT: %bound_method.loc5_30.2: = bound_method %b.ref, %specific_fn // CHECK:STDOUT: %Int.as.Copy.impl.Op.call: init %i32 = call %bound_method.loc5_30.2(%b.ref) // CHECK:STDOUT: return %Int.as.Copy.impl.Op.call // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @C(%c.param: %tuple.type.a1c) -> out %return.param: %struct_type.c { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %c.ref: %tuple.type.a1c = name_ref c, %c // CHECK:STDOUT: %int_0: Core.IntLiteral = int_value 0 [concrete = constants.%int_0] // CHECK:STDOUT: %tuple.elem0: %i32 = tuple_access %c.ref, element0 // CHECK:STDOUT: %.loc6_48.1: %struct_type.c = struct_literal (%tuple.elem0) // CHECK:STDOUT: %impl.elem0: %.8e2 = impl_witness_access constants.%Copy.impl_witness.f17, element0 [concrete = constants.%Int.as.Copy.impl.Op.664] // CHECK:STDOUT: %bound_method.loc6_46.1: = bound_method %tuple.elem0, %impl.elem0 // CHECK:STDOUT: %specific_fn: = specific_function %impl.elem0, @Int.as.Copy.impl.Op(constants.%int_32) [concrete = constants.%Int.as.Copy.impl.Op.specific_fn] // CHECK:STDOUT: %bound_method.loc6_46.2: = bound_method %tuple.elem0, %specific_fn // CHECK:STDOUT: %Int.as.Copy.impl.Op.call: init %i32 = call %bound_method.loc6_46.2(%tuple.elem0) // CHECK:STDOUT: %.loc6_48.2: init %struct_type.c = struct_init (%Int.as.Copy.impl.Op.call) // CHECK:STDOUT: %.loc6_49: init %struct_type.c = converted %.loc6_48.1, %.loc6_48.2 // CHECK:STDOUT: return %.loc6_49 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @D(); // CHECK:STDOUT: // CHECK:STDOUT: --- extern.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %A.type: type = fn_type @A [concrete] // CHECK:STDOUT: %A: %A.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .A = %A.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %A.decl: %A.type = fn_decl @A [concrete = constants.%A] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: extern fn @A(); // CHECK:STDOUT: // CHECK:STDOUT: --- basics.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] // CHECK:STDOUT: %empty_tuple: %empty_tuple.type = tuple_value () [concrete] // CHECK:STDOUT: %pattern_type.cb1: type = pattern_type %empty_tuple.type [concrete] // CHECK:STDOUT: %A.type: type = fn_type @A [concrete] // CHECK:STDOUT: %A: %A.type = struct_value () [concrete] // CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [concrete] // CHECK:STDOUT: %Int.type: type = generic_class_type @Int [concrete] // CHECK:STDOUT: %Int.generic: %Int.type = struct_value () [concrete] // CHECK:STDOUT: %i32: type = class_type @Int, @Int(%int_32) [concrete] // CHECK:STDOUT: %pattern_type.7ce: type = pattern_type %i32 [concrete] // CHECK:STDOUT: %B.type: type = fn_type @B [concrete] // CHECK:STDOUT: %B: %B.type = struct_value () [concrete] // CHECK:STDOUT: %int_1.5b8: Core.IntLiteral = int_value 1 [concrete] // CHECK:STDOUT: %ImplicitAs.type.cc7: type = generic_interface_type @ImplicitAs [concrete] // CHECK:STDOUT: %ImplicitAs.generic: %ImplicitAs.type.cc7 = struct_value () [concrete] // CHECK:STDOUT: %ImplicitAs.type.e8c: type = facet_type <@ImplicitAs, @ImplicitAs(%i32)> [concrete] // CHECK:STDOUT: %To: Core.IntLiteral = symbolic_binding To, 0 [symbolic] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.4e6: type = fn_type @Core.IntLiteral.as.ImplicitAs.impl.Convert, @Core.IntLiteral.as.ImplicitAs.impl(%To) [symbolic] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.3c2: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.4e6 = struct_value () [symbolic] // CHECK:STDOUT: %ImplicitAs.impl_witness.6bc: = impl_witness imports.%ImplicitAs.impl_witness_table.74f, @Core.IntLiteral.as.ImplicitAs.impl(%int_32) [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.e0d: type = fn_type @Core.IntLiteral.as.ImplicitAs.impl.Convert, @Core.IntLiteral.as.ImplicitAs.impl(%int_32) [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.e0d = struct_value () [concrete] // CHECK:STDOUT: %ImplicitAs.facet: %ImplicitAs.type.e8c = facet_value Core.IntLiteral, (%ImplicitAs.impl_witness.6bc) [concrete] // CHECK:STDOUT: %ImplicitAs.WithSelf.Convert.type.b37: type = fn_type @ImplicitAs.WithSelf.Convert, @ImplicitAs.WithSelf(%i32, %ImplicitAs.facet) [concrete] // CHECK:STDOUT: %.545: type = fn_type_with_self_type %ImplicitAs.WithSelf.Convert.type.b37, %ImplicitAs.facet [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.bound: = bound_method %int_1.5b8, %Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5 [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn: = specific_function %Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5, @Core.IntLiteral.as.ImplicitAs.impl.Convert(%int_32) [concrete] // CHECK:STDOUT: %bound_method: = bound_method %int_1.5b8, %Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn [concrete] // CHECK:STDOUT: %int_1.5d2: %i32 = int_value 1 [concrete] // CHECK:STDOUT: %struct_type.c: type = struct_type {.c: %i32} [concrete] // CHECK:STDOUT: %pattern_type.688: type = pattern_type %struct_type.c [concrete] // CHECK:STDOUT: %C.type: type = fn_type @C [concrete] // CHECK:STDOUT: %C: %C.type = struct_value () [concrete] // CHECK:STDOUT: %tuple.type.a1c: type = tuple_type (%i32) [concrete] // CHECK:STDOUT: %tuple.type.985: type = tuple_type (Core.IntLiteral) [concrete] // CHECK:STDOUT: %tuple.378: %tuple.type.985 = tuple_value (%int_1.5b8) [concrete] // CHECK:STDOUT: %tuple.246: %tuple.type.a1c = tuple_value (%int_1.5d2) [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Main.A: %A.type = import_ref Main//fns, A, loaded [concrete = constants.%A] // CHECK:STDOUT: %Main.B: %B.type = import_ref Main//fns, B, loaded [concrete = constants.%B] // CHECK:STDOUT: %Main.C: %C.type = import_ref Main//fns, C, loaded [concrete = constants.%C] // CHECK:STDOUT: %Main.D = import_ref Main//fns, D, unloaded // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: .Int = %Core.Int // CHECK:STDOUT: .ImplicitAs = %Core.ImplicitAs // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: %Core.Int: %Int.type = import_ref Core//prelude/parts/int, Int, loaded [concrete = constants.%Int.generic] // CHECK:STDOUT: %Core.ImplicitAs: %ImplicitAs.type.cc7 = import_ref Core//prelude/parts/as, ImplicitAs, loaded [concrete = constants.%ImplicitAs.generic] // CHECK:STDOUT: %Core.import_ref.42d: @Core.IntLiteral.as.ImplicitAs.impl.%Core.IntLiteral.as.ImplicitAs.impl.Convert.type (%Core.IntLiteral.as.ImplicitAs.impl.Convert.type.4e6) = import_ref Core//prelude/parts/int, loc{{\d+_\d+}}, loaded [symbolic = @Core.IntLiteral.as.ImplicitAs.impl.%Core.IntLiteral.as.ImplicitAs.impl.Convert (constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.3c2)] // CHECK:STDOUT: %ImplicitAs.impl_witness_table.74f = impl_witness_table (%Core.import_ref.42d), @Core.IntLiteral.as.ImplicitAs.impl [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .A = imports.%Main.A // CHECK:STDOUT: .B = imports.%Main.B // CHECK:STDOUT: .C = imports.%Main.C // CHECK:STDOUT: .D = imports.%Main.D // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .a = %a // CHECK:STDOUT: .b = %b // CHECK:STDOUT: .c = %c // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %default.import = import // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %a.patt: %pattern_type.cb1 = ref_binding_pattern a [concrete] // CHECK:STDOUT: %a.var_patt: %pattern_type.cb1 = var_pattern %a.patt [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %a.var: ref %empty_tuple.type = var %a.var_patt [concrete] // CHECK:STDOUT: %.loc6_9.1: type = splice_block %.loc6_9.3 [concrete = constants.%empty_tuple.type] { // CHECK:STDOUT: %.loc6_9.2: %empty_tuple.type = tuple_literal () [concrete = constants.%empty_tuple] // CHECK:STDOUT: %.loc6_9.3: type = converted %.loc6_9.2, constants.%empty_tuple.type [concrete = constants.%empty_tuple.type] // CHECK:STDOUT: } // CHECK:STDOUT: %a: ref %empty_tuple.type = ref_binding a, %a.var [concrete = %a.var] // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %b.patt: %pattern_type.7ce = ref_binding_pattern b [concrete] // CHECK:STDOUT: %b.var_patt: %pattern_type.7ce = var_pattern %b.patt [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %b.var: ref %i32 = var %b.var_patt [concrete] // CHECK:STDOUT: %i32.loc7: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %b: ref %i32 = ref_binding b, %b.var [concrete = %b.var] // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %c.patt: %pattern_type.688 = ref_binding_pattern c [concrete] // CHECK:STDOUT: %c.var_patt: %pattern_type.688 = var_pattern %c.patt [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %c.var: ref %struct_type.c = var %c.var_patt [concrete] // CHECK:STDOUT: %.loc8: type = splice_block %struct_type.c [concrete = constants.%struct_type.c] { // CHECK:STDOUT: %i32.loc8: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %struct_type.c: type = struct_type {.c: %i32} [concrete = constants.%struct_type.c] // CHECK:STDOUT: } // CHECK:STDOUT: %c: ref %struct_type.c = ref_binding c, %c.var [concrete = %c.var] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @A [from "fns.carbon"]; // CHECK:STDOUT: // CHECK:STDOUT: fn @B [from "fns.carbon"]; // CHECK:STDOUT: // CHECK:STDOUT: fn @C [from "fns.carbon"]; // CHECK:STDOUT: // CHECK:STDOUT: fn @__global_init() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %A.ref: %A.type = name_ref A, imports.%Main.A [concrete = constants.%A] // CHECK:STDOUT: %A.call: init %empty_tuple.type = call %A.ref() // CHECK:STDOUT: assign file.%a.var, %A.call // CHECK:STDOUT: %B.ref: %B.type = name_ref B, imports.%Main.B [concrete = constants.%B] // CHECK:STDOUT: %int_1.loc7: Core.IntLiteral = int_value 1 [concrete = constants.%int_1.5b8] // CHECK:STDOUT: %impl.elem0.loc7: %.545 = impl_witness_access constants.%ImplicitAs.impl_witness.6bc, element0 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5] // CHECK:STDOUT: %bound_method.loc7_16.1: = bound_method %int_1.loc7, %impl.elem0.loc7 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.bound] // CHECK:STDOUT: %specific_fn.loc7: = specific_function %impl.elem0.loc7, @Core.IntLiteral.as.ImplicitAs.impl.Convert(constants.%int_32) [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn] // CHECK:STDOUT: %bound_method.loc7_16.2: = bound_method %int_1.loc7, %specific_fn.loc7 [concrete = constants.%bound_method] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc7: init %i32 = call %bound_method.loc7_16.2(%int_1.loc7) [concrete = constants.%int_1.5d2] // CHECK:STDOUT: %.loc7_16.1: %i32 = value_of_initializer %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc7 [concrete = constants.%int_1.5d2] // CHECK:STDOUT: %.loc7_16.2: %i32 = converted %int_1.loc7, %.loc7_16.1 [concrete = constants.%int_1.5d2] // CHECK:STDOUT: %B.call: init %i32 = call %B.ref(%.loc7_16.2) // CHECK:STDOUT: assign file.%b.var, %B.call // CHECK:STDOUT: %C.ref: %C.type = name_ref C, imports.%Main.C [concrete = constants.%C] // CHECK:STDOUT: %int_1.loc8: Core.IntLiteral = int_value 1 [concrete = constants.%int_1.5b8] // CHECK:STDOUT: %.loc8_25.1: %tuple.type.985 = tuple_literal (%int_1.loc8) [concrete = constants.%tuple.378] // CHECK:STDOUT: %impl.elem0.loc8: %.545 = impl_witness_access constants.%ImplicitAs.impl_witness.6bc, element0 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5] // CHECK:STDOUT: %bound_method.loc8_25.1: = bound_method %int_1.loc8, %impl.elem0.loc8 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.bound] // CHECK:STDOUT: %specific_fn.loc8: = specific_function %impl.elem0.loc8, @Core.IntLiteral.as.ImplicitAs.impl.Convert(constants.%int_32) [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn] // CHECK:STDOUT: %bound_method.loc8_25.2: = bound_method %int_1.loc8, %specific_fn.loc8 [concrete = constants.%bound_method] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc8: init %i32 = call %bound_method.loc8_25.2(%int_1.loc8) [concrete = constants.%int_1.5d2] // CHECK:STDOUT: %.loc8_25.2: %i32 = value_of_initializer %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc8 [concrete = constants.%int_1.5d2] // CHECK:STDOUT: %.loc8_25.3: %i32 = converted %int_1.loc8, %.loc8_25.2 [concrete = constants.%int_1.5d2] // CHECK:STDOUT: %tuple: %tuple.type.a1c = tuple_value (%.loc8_25.3) [concrete = constants.%tuple.246] // CHECK:STDOUT: %.loc8_25.4: %tuple.type.a1c = converted %.loc8_25.1, %tuple [concrete = constants.%tuple.246] // CHECK:STDOUT: %C.call: init %struct_type.c = call %C.ref(%.loc8_25.4) // CHECK:STDOUT: assign file.%c.var, %C.call // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- fail_def_ownership.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %A.type.8165c1.1: type = fn_type @A.1 [concrete] // CHECK:STDOUT: %A.8aef9d.1: %A.type.8165c1.1 = struct_value () [concrete] // CHECK:STDOUT: %A.type.8165c1.2: type = fn_type @A.loc14 [concrete] // CHECK:STDOUT: %A.8aef9d.2: %A.type.8165c1.2 = struct_value () [concrete] // CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [concrete] // CHECK:STDOUT: %Int.type: type = generic_class_type @Int [concrete] // CHECK:STDOUT: %Int.generic: %Int.type = struct_value () [concrete] // CHECK:STDOUT: %i32: type = class_type @Int, @Int(%int_32) [concrete] // CHECK:STDOUT: %.ff5: Core.Form = init_form %i32 [concrete] // CHECK:STDOUT: %B.type: type = fn_type @B [concrete] // CHECK:STDOUT: %B: %B.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Main.A: %A.type.8165c1.1 = import_ref Main//fns, A, loaded [concrete = constants.%A.8aef9d.1] // CHECK:STDOUT: %Main.C = import_ref Main//fns, C, unloaded // CHECK:STDOUT: %Main.D = import_ref Main//fns, D, unloaded // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: .Int = %Core.Int // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: %Core.Int: %Int.type = import_ref Core//prelude/parts/int, Int, loaded [concrete = constants.%Int.generic] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .A = imports.%Main.A // CHECK:STDOUT: .B = %B.decl // CHECK:STDOUT: .C = imports.%Main.C // CHECK:STDOUT: .D = imports.%Main.D // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %default.import = import // CHECK:STDOUT: %A.decl: %A.type.8165c1.2 = fn_decl @A.loc14 [concrete = constants.%A.8aef9d.2] {} {} // CHECK:STDOUT: %B.decl: %B.type = fn_decl @B [concrete = constants.%B] {} { // CHECK:STDOUT: %i32.loc23_17: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %.loc23: Core.Form = init_form %i32.loc23_17 [concrete = constants.%.ff5] // CHECK:STDOUT: %b.param: %i32 = value_param call_param0 // CHECK:STDOUT: %i32.loc23_9: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %b: %i32 = value_binding b, %b.param // CHECK:STDOUT: %return.param: ref %i32 = out_param call_param1 // CHECK:STDOUT: %return: ref %i32 = return_slot %return.param // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @A.1 [from "fns.carbon"]; // CHECK:STDOUT: // CHECK:STDOUT: fn @A.loc14() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @B [from "fns.carbon"]; // CHECK:STDOUT: // CHECK:STDOUT: --- fail_redecl_then_def.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %A.type: type = fn_type @A [concrete] // CHECK:STDOUT: %A: %A.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .A = %A.decl.loc14 // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %default.import = import // CHECK:STDOUT: %A.decl.loc14: %A.type = fn_decl @A [concrete = constants.%A] {} {} // CHECK:STDOUT: %A.decl.loc24: %A.type = fn_decl @A [concrete = constants.%A] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: extern fn @A() [from "extern.carbon"] { // CHECK:STDOUT: !entry: // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- fail_mix_extern_decl.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %D.type: type = fn_type @D [concrete] // CHECK:STDOUT: %D: %D.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Main.A = import_ref Main//fns, A, unloaded // CHECK:STDOUT: %Main.B = import_ref Main//fns, B, unloaded // CHECK:STDOUT: %Main.C = import_ref Main//fns, C, unloaded // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .A = imports.%Main.A // CHECK:STDOUT: .B = imports.%Main.B // CHECK:STDOUT: .C = imports.%Main.C // CHECK:STDOUT: .D = %D.decl.loc14 // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %default.import = import // CHECK:STDOUT: %D.decl.loc14: %D.type = fn_decl @D [concrete = constants.%D] {} {} // CHECK:STDOUT: %D.decl.loc16: %D.type = fn_decl @D [concrete = constants.%D] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @D() [from "fns.carbon"] { // CHECK:STDOUT: !entry: // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/function/definition/import_access.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/none.carbon // TODO: Add ranges and switch to "--dump-sem-ir-ranges=only". // EXTRA-ARGS: --dump-sem-ir-ranges=if-present // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/function/definition/import_access.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/function/definition/import_access.carbon // ============================================================================ // Setup files // ============================================================================ // --- def.carbon package Test library "[[@TEST_NAME]]"; private fn Def() {} // --- forward_with_def.carbon package Test library "[[@TEST_NAME]]"; private fn ForwardWithDef(); fn ForwardWithDef() {} // --- forward.carbon package Test library "[[@TEST_NAME]]"; private fn Forward(); // ============================================================================ // Test files // ============================================================================ // --- def.impl.carbon impl package Test library "[[@TEST_NAME]]"; var f: () = Def(); // --- fail_local_def.carbon package Test library "[[@TEST_NAME]]"; import library "def"; // CHECK:STDERR: fail_local_def.carbon:[[@LINE+4]]:13: error: name `Def` not found [NameNotFound] // CHECK:STDERR: var f: () = Def(); // CHECK:STDERR: ^~~ // CHECK:STDERR: var f: () = Def(); // --- fail_other_def.carbon package Other library "[[@TEST_NAME]]"; import Test library "def"; // CHECK:STDERR: fail_other_def.carbon:[[@LINE+4]]:13: error: member name `Def` not found in `Test` [MemberNameNotFoundInInstScope] // CHECK:STDERR: var f: () = Test.Def(); // CHECK:STDERR: ^~~~~~~~ // CHECK:STDERR: var f: () = Test.Def(); // --- forward_with_def.impl.carbon impl package Test library "[[@TEST_NAME]]"; var f: () = ForwardWithDef(); // --- fail_local_forward_with_def.carbon package Test library "[[@TEST_NAME]]"; import library "forward_with_def"; // CHECK:STDERR: fail_local_forward_with_def.carbon:[[@LINE+4]]:13: error: name `ForwardWithDef` not found [NameNotFound] // CHECK:STDERR: var f: () = ForwardWithDef(); // CHECK:STDERR: ^~~~~~~~~~~~~~ // CHECK:STDERR: var f: () = ForwardWithDef(); // --- fail_other_forward_with_def.carbon package Other library "[[@TEST_NAME]]"; import Test library "forward_with_def"; // CHECK:STDERR: fail_other_forward_with_def.carbon:[[@LINE+4]]:13: error: member name `ForwardWithDef` not found in `Test` [MemberNameNotFoundInInstScope] // CHECK:STDERR: var f: () = Test.ForwardWithDef(); // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: var f: () = Test.ForwardWithDef(); // --- forward.impl.carbon impl package Test library "[[@TEST_NAME]]"; var f: () = Forward(); fn Forward() {} // --- fail_local_forward.carbon package Test library "[[@TEST_NAME]]"; import library "forward"; // CHECK:STDERR: fail_local_forward.carbon:[[@LINE+4]]:13: error: name `Forward` not found [NameNotFound] // CHECK:STDERR: var f: () = Forward(); // CHECK:STDERR: ^~~~~~~ // CHECK:STDERR: var f: () = Forward(); // --- fail_other_forward.carbon package Other library "[[@TEST_NAME]]"; import Test library "forward"; // CHECK:STDERR: fail_other_forward.carbon:[[@LINE+4]]:13: error: member name `Forward` not found in `Test` [MemberNameNotFoundInInstScope] // CHECK:STDERR: var f: () = Test.Forward(); // CHECK:STDERR: ^~~~~~~~~~~~ // CHECK:STDERR: var f: () = Test.Forward(); // --- todo_fail_private_on_redecl.carbon library "[[@TEST_NAME]]"; private fn Redecl(); private fn Redecl() {} // CHECK:STDOUT: --- def.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %Def.type: type = fn_type @Def [concrete] // CHECK:STDOUT: %Def: %Def.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Def [private] = %Def.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Def.decl: %Def.type = fn_decl @Def [concrete = constants.%Def] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @Def() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- forward_with_def.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %ForwardWithDef.type: type = fn_type @ForwardWithDef [concrete] // CHECK:STDOUT: %ForwardWithDef: %ForwardWithDef.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .ForwardWithDef [private] = %ForwardWithDef.decl.loc4 // CHECK:STDOUT: } // CHECK:STDOUT: %ForwardWithDef.decl.loc4: %ForwardWithDef.type = fn_decl @ForwardWithDef [concrete = constants.%ForwardWithDef] {} {} // CHECK:STDOUT: %ForwardWithDef.decl.loc6: %ForwardWithDef.type = fn_decl @ForwardWithDef [concrete = constants.%ForwardWithDef] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @ForwardWithDef() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- forward.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %Forward.type: type = fn_type @Forward [concrete] // CHECK:STDOUT: %Forward: %Forward.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Forward [private] = %Forward.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Forward.decl: %Forward.type = fn_decl @Forward [concrete = constants.%Forward] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @Forward(); // CHECK:STDOUT: // CHECK:STDOUT: --- def.impl.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] // CHECK:STDOUT: %empty_tuple: %empty_tuple.type = tuple_value () [concrete] // CHECK:STDOUT: %pattern_type: type = pattern_type %empty_tuple.type [concrete] // CHECK:STDOUT: %Def.type: type = fn_type @Def [concrete] // CHECK:STDOUT: %Def: %Def.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Test.Def: %Def.type = import_ref Test//def, Def, loaded [concrete = constants.%Def] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Def [private] = imports.%Test.Def // CHECK:STDOUT: .f = %f // CHECK:STDOUT: } // CHECK:STDOUT: %Test.import = import Test // CHECK:STDOUT: %default.import = import // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %f.patt: %pattern_type = ref_binding_pattern f [concrete] // CHECK:STDOUT: %f.var_patt: %pattern_type = var_pattern %f.patt [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %f.var: ref %empty_tuple.type = var %f.var_patt [concrete] // CHECK:STDOUT: %.loc4_9.1: type = splice_block %.loc4_9.3 [concrete = constants.%empty_tuple.type] { // CHECK:STDOUT: %.loc4_9.2: %empty_tuple.type = tuple_literal () [concrete = constants.%empty_tuple] // CHECK:STDOUT: %.loc4_9.3: type = converted %.loc4_9.2, constants.%empty_tuple.type [concrete = constants.%empty_tuple.type] // CHECK:STDOUT: } // CHECK:STDOUT: %f: ref %empty_tuple.type = ref_binding f, %f.var [concrete = %f.var] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @Def [from "def.carbon"]; // CHECK:STDOUT: // CHECK:STDOUT: fn @__global_init() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %Def.ref: %Def.type = name_ref Def, imports.%Test.Def [concrete = constants.%Def] // CHECK:STDOUT: %Def.call: init %empty_tuple.type = call %Def.ref() // CHECK:STDOUT: assign file.%f.var, %Def.call // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- fail_local_def.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] // CHECK:STDOUT: %empty_tuple: %empty_tuple.type = tuple_value () [concrete] // CHECK:STDOUT: %pattern_type: type = pattern_type %empty_tuple.type [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .f = %f // CHECK:STDOUT: .Def = // CHECK:STDOUT: } // CHECK:STDOUT: %default.import = import // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %f.patt: %pattern_type = ref_binding_pattern f [concrete] // CHECK:STDOUT: %f.var_patt: %pattern_type = var_pattern %f.patt [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %f.var: ref %empty_tuple.type = var %f.var_patt [concrete] // CHECK:STDOUT: %.loc10_9.1: type = splice_block %.loc10_9.3 [concrete = constants.%empty_tuple.type] { // CHECK:STDOUT: %.loc10_9.2: %empty_tuple.type = tuple_literal () [concrete = constants.%empty_tuple] // CHECK:STDOUT: %.loc10_9.3: type = converted %.loc10_9.2, constants.%empty_tuple.type [concrete = constants.%empty_tuple.type] // CHECK:STDOUT: } // CHECK:STDOUT: %f: ref %empty_tuple.type = ref_binding f, %f.var [concrete = %f.var] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @__global_init() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %Def.ref: = name_ref Def, [concrete = ] // CHECK:STDOUT: assign file.%f.var, // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- fail_other_def.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] // CHECK:STDOUT: %empty_tuple: %empty_tuple.type = tuple_value () [concrete] // CHECK:STDOUT: %pattern_type: type = pattern_type %empty_tuple.type [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Test: = namespace file.%Test.import, [concrete] { // CHECK:STDOUT: .Def = // CHECK:STDOUT: import Test//def // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Test = imports.%Test // CHECK:STDOUT: .f = %f // CHECK:STDOUT: } // CHECK:STDOUT: %Test.import = import Test // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %f.patt: %pattern_type = ref_binding_pattern f [concrete] // CHECK:STDOUT: %f.var_patt: %pattern_type = var_pattern %f.patt [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %f.var: ref %empty_tuple.type = var %f.var_patt [concrete] // CHECK:STDOUT: %.loc10_9.1: type = splice_block %.loc10_9.3 [concrete = constants.%empty_tuple.type] { // CHECK:STDOUT: %.loc10_9.2: %empty_tuple.type = tuple_literal () [concrete = constants.%empty_tuple] // CHECK:STDOUT: %.loc10_9.3: type = converted %.loc10_9.2, constants.%empty_tuple.type [concrete = constants.%empty_tuple.type] // CHECK:STDOUT: } // CHECK:STDOUT: %f: ref %empty_tuple.type = ref_binding f, %f.var [concrete = %f.var] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @__global_init() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %Test.ref: = name_ref Test, imports.%Test [concrete = imports.%Test] // CHECK:STDOUT: %Def.ref: = name_ref Def, [concrete = ] // CHECK:STDOUT: assign file.%f.var, // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- forward_with_def.impl.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] // CHECK:STDOUT: %empty_tuple: %empty_tuple.type = tuple_value () [concrete] // CHECK:STDOUT: %pattern_type: type = pattern_type %empty_tuple.type [concrete] // CHECK:STDOUT: %ForwardWithDef.type: type = fn_type @ForwardWithDef [concrete] // CHECK:STDOUT: %ForwardWithDef: %ForwardWithDef.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Test.ForwardWithDef: %ForwardWithDef.type = import_ref Test//forward_with_def, ForwardWithDef, loaded [concrete = constants.%ForwardWithDef] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .ForwardWithDef [private] = imports.%Test.ForwardWithDef // CHECK:STDOUT: .f = %f // CHECK:STDOUT: } // CHECK:STDOUT: %Test.import = import Test // CHECK:STDOUT: %default.import = import // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %f.patt: %pattern_type = ref_binding_pattern f [concrete] // CHECK:STDOUT: %f.var_patt: %pattern_type = var_pattern %f.patt [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %f.var: ref %empty_tuple.type = var %f.var_patt [concrete] // CHECK:STDOUT: %.loc4_9.1: type = splice_block %.loc4_9.3 [concrete = constants.%empty_tuple.type] { // CHECK:STDOUT: %.loc4_9.2: %empty_tuple.type = tuple_literal () [concrete = constants.%empty_tuple] // CHECK:STDOUT: %.loc4_9.3: type = converted %.loc4_9.2, constants.%empty_tuple.type [concrete = constants.%empty_tuple.type] // CHECK:STDOUT: } // CHECK:STDOUT: %f: ref %empty_tuple.type = ref_binding f, %f.var [concrete = %f.var] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @ForwardWithDef [from "forward_with_def.carbon"]; // CHECK:STDOUT: // CHECK:STDOUT: fn @__global_init() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %ForwardWithDef.ref: %ForwardWithDef.type = name_ref ForwardWithDef, imports.%Test.ForwardWithDef [concrete = constants.%ForwardWithDef] // CHECK:STDOUT: %ForwardWithDef.call: init %empty_tuple.type = call %ForwardWithDef.ref() // CHECK:STDOUT: assign file.%f.var, %ForwardWithDef.call // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- fail_local_forward_with_def.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] // CHECK:STDOUT: %empty_tuple: %empty_tuple.type = tuple_value () [concrete] // CHECK:STDOUT: %pattern_type: type = pattern_type %empty_tuple.type [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .f = %f // CHECK:STDOUT: .ForwardWithDef = // CHECK:STDOUT: } // CHECK:STDOUT: %default.import = import // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %f.patt: %pattern_type = ref_binding_pattern f [concrete] // CHECK:STDOUT: %f.var_patt: %pattern_type = var_pattern %f.patt [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %f.var: ref %empty_tuple.type = var %f.var_patt [concrete] // CHECK:STDOUT: %.loc10_9.1: type = splice_block %.loc10_9.3 [concrete = constants.%empty_tuple.type] { // CHECK:STDOUT: %.loc10_9.2: %empty_tuple.type = tuple_literal () [concrete = constants.%empty_tuple] // CHECK:STDOUT: %.loc10_9.3: type = converted %.loc10_9.2, constants.%empty_tuple.type [concrete = constants.%empty_tuple.type] // CHECK:STDOUT: } // CHECK:STDOUT: %f: ref %empty_tuple.type = ref_binding f, %f.var [concrete = %f.var] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @__global_init() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %ForwardWithDef.ref: = name_ref ForwardWithDef, [concrete = ] // CHECK:STDOUT: assign file.%f.var, // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- fail_other_forward_with_def.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] // CHECK:STDOUT: %empty_tuple: %empty_tuple.type = tuple_value () [concrete] // CHECK:STDOUT: %pattern_type: type = pattern_type %empty_tuple.type [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Test: = namespace file.%Test.import, [concrete] { // CHECK:STDOUT: .ForwardWithDef = // CHECK:STDOUT: import Test//forward_with_def // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Test = imports.%Test // CHECK:STDOUT: .f = %f // CHECK:STDOUT: } // CHECK:STDOUT: %Test.import = import Test // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %f.patt: %pattern_type = ref_binding_pattern f [concrete] // CHECK:STDOUT: %f.var_patt: %pattern_type = var_pattern %f.patt [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %f.var: ref %empty_tuple.type = var %f.var_patt [concrete] // CHECK:STDOUT: %.loc10_9.1: type = splice_block %.loc10_9.3 [concrete = constants.%empty_tuple.type] { // CHECK:STDOUT: %.loc10_9.2: %empty_tuple.type = tuple_literal () [concrete = constants.%empty_tuple] // CHECK:STDOUT: %.loc10_9.3: type = converted %.loc10_9.2, constants.%empty_tuple.type [concrete = constants.%empty_tuple.type] // CHECK:STDOUT: } // CHECK:STDOUT: %f: ref %empty_tuple.type = ref_binding f, %f.var [concrete = %f.var] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @__global_init() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %Test.ref: = name_ref Test, imports.%Test [concrete = imports.%Test] // CHECK:STDOUT: %ForwardWithDef.ref: = name_ref ForwardWithDef, [concrete = ] // CHECK:STDOUT: assign file.%f.var, // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- forward.impl.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] // CHECK:STDOUT: %empty_tuple: %empty_tuple.type = tuple_value () [concrete] // CHECK:STDOUT: %pattern_type: type = pattern_type %empty_tuple.type [concrete] // CHECK:STDOUT: %Forward.type: type = fn_type @Forward [concrete] // CHECK:STDOUT: %Forward: %Forward.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Test.Forward: %Forward.type = import_ref Test//forward, Forward, loaded [concrete = constants.%Forward] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Forward [private] = %Forward.decl // CHECK:STDOUT: .f = %f // CHECK:STDOUT: } // CHECK:STDOUT: %Test.import = import Test // CHECK:STDOUT: %default.import = import // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %f.patt: %pattern_type = ref_binding_pattern f [concrete] // CHECK:STDOUT: %f.var_patt: %pattern_type = var_pattern %f.patt [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %f.var: ref %empty_tuple.type = var %f.var_patt [concrete] // CHECK:STDOUT: %.loc4_9.1: type = splice_block %.loc4_9.3 [concrete = constants.%empty_tuple.type] { // CHECK:STDOUT: %.loc4_9.2: %empty_tuple.type = tuple_literal () [concrete = constants.%empty_tuple] // CHECK:STDOUT: %.loc4_9.3: type = converted %.loc4_9.2, constants.%empty_tuple.type [concrete = constants.%empty_tuple.type] // CHECK:STDOUT: } // CHECK:STDOUT: %f: ref %empty_tuple.type = ref_binding f, %f.var [concrete = %f.var] // CHECK:STDOUT: %Forward.decl: %Forward.type = fn_decl @Forward [concrete = constants.%Forward] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @Forward() [from "forward.carbon"] { // CHECK:STDOUT: !entry: // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @__global_init() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %Forward.ref: %Forward.type = name_ref Forward, imports.%Test.Forward [concrete = constants.%Forward] // CHECK:STDOUT: %Forward.call: init %empty_tuple.type = call %Forward.ref() // CHECK:STDOUT: assign file.%f.var, %Forward.call // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- fail_local_forward.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] // CHECK:STDOUT: %empty_tuple: %empty_tuple.type = tuple_value () [concrete] // CHECK:STDOUT: %pattern_type: type = pattern_type %empty_tuple.type [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .f = %f // CHECK:STDOUT: .Forward = // CHECK:STDOUT: } // CHECK:STDOUT: %default.import = import // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %f.patt: %pattern_type = ref_binding_pattern f [concrete] // CHECK:STDOUT: %f.var_patt: %pattern_type = var_pattern %f.patt [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %f.var: ref %empty_tuple.type = var %f.var_patt [concrete] // CHECK:STDOUT: %.loc10_9.1: type = splice_block %.loc10_9.3 [concrete = constants.%empty_tuple.type] { // CHECK:STDOUT: %.loc10_9.2: %empty_tuple.type = tuple_literal () [concrete = constants.%empty_tuple] // CHECK:STDOUT: %.loc10_9.3: type = converted %.loc10_9.2, constants.%empty_tuple.type [concrete = constants.%empty_tuple.type] // CHECK:STDOUT: } // CHECK:STDOUT: %f: ref %empty_tuple.type = ref_binding f, %f.var [concrete = %f.var] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @__global_init() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %Forward.ref: = name_ref Forward, [concrete = ] // CHECK:STDOUT: assign file.%f.var, // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- fail_other_forward.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] // CHECK:STDOUT: %empty_tuple: %empty_tuple.type = tuple_value () [concrete] // CHECK:STDOUT: %pattern_type: type = pattern_type %empty_tuple.type [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Test: = namespace file.%Test.import, [concrete] { // CHECK:STDOUT: .Forward = // CHECK:STDOUT: import Test//forward // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Test = imports.%Test // CHECK:STDOUT: .f = %f // CHECK:STDOUT: } // CHECK:STDOUT: %Test.import = import Test // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %f.patt: %pattern_type = ref_binding_pattern f [concrete] // CHECK:STDOUT: %f.var_patt: %pattern_type = var_pattern %f.patt [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %f.var: ref %empty_tuple.type = var %f.var_patt [concrete] // CHECK:STDOUT: %.loc10_9.1: type = splice_block %.loc10_9.3 [concrete = constants.%empty_tuple.type] { // CHECK:STDOUT: %.loc10_9.2: %empty_tuple.type = tuple_literal () [concrete = constants.%empty_tuple] // CHECK:STDOUT: %.loc10_9.3: type = converted %.loc10_9.2, constants.%empty_tuple.type [concrete = constants.%empty_tuple.type] // CHECK:STDOUT: } // CHECK:STDOUT: %f: ref %empty_tuple.type = ref_binding f, %f.var [concrete = %f.var] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @__global_init() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %Test.ref: = name_ref Test, imports.%Test [concrete = imports.%Test] // CHECK:STDOUT: %Forward.ref: = name_ref Forward, [concrete = ] // CHECK:STDOUT: assign file.%f.var, // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- todo_fail_private_on_redecl.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %Redecl.type: type = fn_type @Redecl [concrete] // CHECK:STDOUT: %Redecl: %Redecl.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Redecl [private] = %Redecl.decl.loc4 // CHECK:STDOUT: } // CHECK:STDOUT: %Redecl.decl.loc4: %Redecl.type = fn_decl @Redecl [concrete = constants.%Redecl] {} {} // CHECK:STDOUT: %Redecl.decl.loc6: %Redecl.type = fn_decl @Redecl [concrete = constants.%Redecl] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @Redecl() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/function/definition/order.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/none.carbon // TODO: Add ranges and switch to "--dump-sem-ir-ranges=only". // EXTRA-ARGS: --dump-sem-ir-ranges=if-present // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/function/definition/order.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/function/definition/order.carbon fn Foo() {} fn Bar() {} fn Baz() {} // CHECK:STDOUT: --- order.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %Foo.type: type = fn_type @Foo [concrete] // CHECK:STDOUT: %Foo: %Foo.type = struct_value () [concrete] // CHECK:STDOUT: %Bar.type: type = fn_type @Bar [concrete] // CHECK:STDOUT: %Bar: %Bar.type = struct_value () [concrete] // CHECK:STDOUT: %Baz.type: type = fn_type @Baz [concrete] // CHECK:STDOUT: %Baz: %Baz.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Foo = %Foo.decl // CHECK:STDOUT: .Bar = %Bar.decl // CHECK:STDOUT: .Baz = %Baz.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Foo.decl: %Foo.type = fn_decl @Foo [concrete = constants.%Foo] {} {} // CHECK:STDOUT: %Bar.decl: %Bar.type = fn_decl @Bar [concrete = constants.%Bar] {} {} // CHECK:STDOUT: %Baz.decl: %Baz.type = fn_decl @Baz [concrete = constants.%Baz] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @Foo() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @Bar() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @Baz() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/function/definition/params_one.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/int.carbon // TODO: Add ranges and switch to "--dump-sem-ir-ranges=only". // EXTRA-ARGS: --dump-sem-ir-ranges=if-present // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/function/definition/params_one.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/function/definition/params_one.carbon fn Foo(unused a: i32) {} // CHECK:STDOUT: --- params_one.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [concrete] // CHECK:STDOUT: %Int.type: type = generic_class_type @Int [concrete] // CHECK:STDOUT: %Int.generic: %Int.type = struct_value () [concrete] // CHECK:STDOUT: %i32: type = class_type @Int, @Int(%int_32) [concrete] // CHECK:STDOUT: %pattern_type.7ce: type = pattern_type %i32 [concrete] // CHECK:STDOUT: %Foo.type: type = fn_type @Foo [concrete] // CHECK:STDOUT: %Foo: %Foo.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: .Int = %Core.Int // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: %Core.Int: %Int.type = import_ref Core//prelude/parts/int, Int, loaded [concrete = constants.%Int.generic] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .Foo = %Foo.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %Foo.decl: %Foo.type = fn_decl @Foo [concrete = constants.%Foo] { // CHECK:STDOUT: %a.patt: %pattern_type.7ce = value_binding_pattern a [concrete] // CHECK:STDOUT: %a.param_patt: %pattern_type.7ce = value_param_pattern %a.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %a.param: %i32 = value_param call_param0 // CHECK:STDOUT: %i32: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %a: %i32 = value_binding a, %a.param // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @Foo(%a.param: %i32) { // CHECK:STDOUT: !entry: // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/function/definition/params_one_comma.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/int.carbon // TODO: Add ranges and switch to "--dump-sem-ir-ranges=only". // EXTRA-ARGS: --dump-sem-ir-ranges=if-present // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/function/definition/params_one_comma.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/function/definition/params_one_comma.carbon fn Foo(unused a: i32,) {} // CHECK:STDOUT: --- params_one_comma.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [concrete] // CHECK:STDOUT: %Int.type: type = generic_class_type @Int [concrete] // CHECK:STDOUT: %Int.generic: %Int.type = struct_value () [concrete] // CHECK:STDOUT: %i32: type = class_type @Int, @Int(%int_32) [concrete] // CHECK:STDOUT: %pattern_type.7ce: type = pattern_type %i32 [concrete] // CHECK:STDOUT: %Foo.type: type = fn_type @Foo [concrete] // CHECK:STDOUT: %Foo: %Foo.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: .Int = %Core.Int // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: %Core.Int: %Int.type = import_ref Core//prelude/parts/int, Int, loaded [concrete = constants.%Int.generic] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .Foo = %Foo.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %Foo.decl: %Foo.type = fn_decl @Foo [concrete = constants.%Foo] { // CHECK:STDOUT: %a.patt: %pattern_type.7ce = value_binding_pattern a [concrete] // CHECK:STDOUT: %a.param_patt: %pattern_type.7ce = value_param_pattern %a.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %a.param: %i32 = value_param call_param0 // CHECK:STDOUT: %i32: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %a: %i32 = value_binding a, %a.param // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @Foo(%a.param: %i32) { // CHECK:STDOUT: !entry: // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/function/definition/params_two.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/int.carbon // TODO: Add ranges and switch to "--dump-sem-ir-ranges=only". // EXTRA-ARGS: --dump-sem-ir-ranges=if-present // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/function/definition/params_two.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/function/definition/params_two.carbon fn Foo(unused a: i32, unused b: i32) {} // CHECK:STDOUT: --- params_two.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [concrete] // CHECK:STDOUT: %Int.type: type = generic_class_type @Int [concrete] // CHECK:STDOUT: %Int.generic: %Int.type = struct_value () [concrete] // CHECK:STDOUT: %i32: type = class_type @Int, @Int(%int_32) [concrete] // CHECK:STDOUT: %pattern_type.7ce: type = pattern_type %i32 [concrete] // CHECK:STDOUT: %Foo.type: type = fn_type @Foo [concrete] // CHECK:STDOUT: %Foo: %Foo.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: .Int = %Core.Int // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: %Core.Int: %Int.type = import_ref Core//prelude/parts/int, Int, loaded [concrete = constants.%Int.generic] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .Foo = %Foo.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %Foo.decl: %Foo.type = fn_decl @Foo [concrete = constants.%Foo] { // CHECK:STDOUT: %a.patt: %pattern_type.7ce = value_binding_pattern a [concrete] // CHECK:STDOUT: %a.param_patt: %pattern_type.7ce = value_param_pattern %a.patt [concrete] // CHECK:STDOUT: %b.patt: %pattern_type.7ce = value_binding_pattern b [concrete] // CHECK:STDOUT: %b.param_patt: %pattern_type.7ce = value_param_pattern %b.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %a.param: %i32 = value_param call_param0 // CHECK:STDOUT: %i32.loc15_18: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %a: %i32 = value_binding a, %a.param // CHECK:STDOUT: %b.param: %i32 = value_param call_param1 // CHECK:STDOUT: %i32.loc15_33: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %b: %i32 = value_binding b, %b.param // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @Foo(%a.param: %i32, %b.param: %i32) { // CHECK:STDOUT: !entry: // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/function/definition/params_two_comma.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/int.carbon // TODO: Add ranges and switch to "--dump-sem-ir-ranges=only". // EXTRA-ARGS: --dump-sem-ir-ranges=if-present // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/function/definition/params_two_comma.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/function/definition/params_two_comma.carbon fn Foo(unused a: i32, unused b: i32,) {} // CHECK:STDOUT: --- params_two_comma.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [concrete] // CHECK:STDOUT: %Int.type: type = generic_class_type @Int [concrete] // CHECK:STDOUT: %Int.generic: %Int.type = struct_value () [concrete] // CHECK:STDOUT: %i32: type = class_type @Int, @Int(%int_32) [concrete] // CHECK:STDOUT: %pattern_type.7ce: type = pattern_type %i32 [concrete] // CHECK:STDOUT: %Foo.type: type = fn_type @Foo [concrete] // CHECK:STDOUT: %Foo: %Foo.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: .Int = %Core.Int // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: %Core.Int: %Int.type = import_ref Core//prelude/parts/int, Int, loaded [concrete = constants.%Int.generic] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .Foo = %Foo.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %Foo.decl: %Foo.type = fn_decl @Foo [concrete = constants.%Foo] { // CHECK:STDOUT: %a.patt: %pattern_type.7ce = value_binding_pattern a [concrete] // CHECK:STDOUT: %a.param_patt: %pattern_type.7ce = value_param_pattern %a.patt [concrete] // CHECK:STDOUT: %b.patt: %pattern_type.7ce = value_binding_pattern b [concrete] // CHECK:STDOUT: %b.param_patt: %pattern_type.7ce = value_param_pattern %b.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %a.param: %i32 = value_param call_param0 // CHECK:STDOUT: %i32.loc15_18: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %a: %i32 = value_binding a, %a.param // CHECK:STDOUT: %b.param: %i32 = value_param call_param1 // CHECK:STDOUT: %i32.loc15_33: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %b: %i32 = value_binding b, %b.param // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @Foo(%a.param: %i32, %b.param: %i32) { // CHECK:STDOUT: !entry: // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/function/definition/params_zero.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/none.carbon // TODO: Add ranges and switch to "--dump-sem-ir-ranges=only". // EXTRA-ARGS: --dump-sem-ir-ranges=if-present // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/function/definition/params_zero.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/function/definition/params_zero.carbon fn Foo() {} // CHECK:STDOUT: --- params_zero.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %Foo.type: type = fn_type @Foo [concrete] // CHECK:STDOUT: %Foo: %Foo.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Foo = %Foo.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Foo.decl: %Foo.type = fn_decl @Foo [concrete = constants.%Foo] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @Foo() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/function/definition/syntactic_merge.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/none.carbon // TODO: Add ranges and switch to "--dump-sem-ir-ranges=only". // EXTRA-ARGS: --dump-sem-ir-ranges=if-present // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/function/definition/syntactic_merge.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/function/definition/syntactic_merge.carbon // --- basic.carbon library "[[@TEST_NAME]]"; class C {} alias D = C; fn Foo(a: C); fn Foo(unused a: C) {} fn Bar(a: D); fn Bar(unused a: D) {} // --- spacing.carbon library "[[@TEST_NAME]]"; class C {} fn Foo [ ] ( a : C ); fn Foo[](unused a: C) {} // --- fail_parens.carbon library "[[@TEST_NAME]]"; class C {} fn Foo(a: C); // CHECK:STDERR: fail_parens.carbon:[[@LINE+7]]:18: error: redeclaration syntax differs here [RedeclParamSyntaxDiffers] // CHECK:STDERR: fn Foo(unused a: (C)) {} // CHECK:STDERR: ^ // CHECK:STDERR: fail_parens.carbon:[[@LINE-4]]:11: note: comparing with previous declaration here [RedeclParamSyntaxPrevious] // CHECK:STDERR: fn Foo(a: C); // CHECK:STDERR: ^ // CHECK:STDERR: fn Foo(unused a: (C)) {} // --- fail_only_implicit_params.carbon library "[[@TEST_NAME]]"; class C {} // CHECK:STDERR: fail_only_implicit_params.carbon:[[@LINE+8]]:8: error: implicit parameters of functions must be constant or `self` [ImplictParamMustBeConstant] // CHECK:STDERR: fn Foo[a: C]; // CHECK:STDERR: ^~~~ // CHECK:STDERR: // CHECK:STDERR: fail_only_implicit_params.carbon:[[@LINE+4]]:1: error: semantics TODO: `function with positional parameters` [SemanticsTodo] // CHECK:STDERR: fn Foo[a: C]; // CHECK:STDERR: ^~~~~~~~~~~~~ // CHECK:STDERR: fn Foo[a: C]; // CHECK:STDERR: fail_only_implicit_params.carbon:[[@LINE+8]]:8: error: implicit parameters of functions must be constant or `self` [ImplictParamMustBeConstant] // CHECK:STDERR: fn Foo[a: (C)] {} // CHECK:STDERR: ^~~~~~ // CHECK:STDERR: // CHECK:STDERR: fail_only_implicit_params.carbon:[[@LINE+4]]:1: error: semantics TODO: `function with positional parameters` [SemanticsTodo] // CHECK:STDERR: fn Foo[a: (C)] {} // CHECK:STDERR: ^~~~~~~~~~~~~~~~ // CHECK:STDERR: fn Foo[a: (C)] {} // --- todo_fail_raw_identifier.carbon library "[[@TEST_NAME]]"; class C {} fn Foo(a: C); fn Foo(unused a: r#C) {} // --- two_file.carbon library "[[@TEST_NAME]]"; class C {} alias D = C; fn Foo(a: C); fn Bar(a: D); // --- two_file.impl.carbon impl library "[[@TEST_NAME]]"; fn Foo(unused a: C) {} fn Bar(unused a: D) {} // --- fail_name_mismatch.carbon library "[[@TEST_NAME]]"; class C {} alias D = C; fn Foo(a: C); // CHECK:STDERR: fail_name_mismatch.carbon:[[@LINE+7]]:15: error: redeclaration differs at parameter 1 [RedeclParamDiffers] // CHECK:STDERR: fn Foo(unused b: D) {} // CHECK:STDERR: ^~~~ // CHECK:STDERR: fail_name_mismatch.carbon:[[@LINE-4]]:8: note: previous declaration's corresponding parameter here [RedeclParamPrevious] // CHECK:STDERR: fn Foo(a: C); // CHECK:STDERR: ^~~~ // CHECK:STDERR: fn Foo(unused b: D) {} // --- fail_alias.carbon library "[[@TEST_NAME]]"; class C {} alias D = C; fn Foo(a: C); // CHECK:STDERR: fail_alias.carbon:[[@LINE+7]]:18: error: redeclaration syntax differs here [RedeclParamSyntaxDiffers] // CHECK:STDERR: fn Foo(unused a: D) {} // CHECK:STDERR: ^ // CHECK:STDERR: fail_alias.carbon:[[@LINE-4]]:11: note: comparing with previous declaration here [RedeclParamSyntaxPrevious] // CHECK:STDERR: fn Foo(a: C); // CHECK:STDERR: ^ // CHECK:STDERR: fn Foo(unused a: D) {} // --- fail_deduced_alias.carbon library "[[@TEST_NAME]]"; class C {} alias D = C; fn Foo[a:! C](); // CHECK:STDERR: fail_deduced_alias.carbon:[[@LINE+7]]:19: error: redeclaration syntax differs here [RedeclParamSyntaxDiffers] // CHECK:STDERR: fn Foo[unused a:! D]() {} // CHECK:STDERR: ^ // CHECK:STDERR: fail_deduced_alias.carbon:[[@LINE-4]]:12: note: comparing with previous declaration here [RedeclParamSyntaxPrevious] // CHECK:STDERR: fn Foo[a:! C](); // CHECK:STDERR: ^ // CHECK:STDERR: fn Foo[unused a:! D]() {} // --- todo_fail_alias_in_return.carbon library "[[@TEST_NAME]]"; class C {} alias D = C; fn Foo() -> C; fn Foo() -> D { return {}; } // --- alias_two_file.carbon library "[[@TEST_NAME]]"; class C {} fn Foo(a: C); // --- todo_fail_alias_two_file.impl.carbon impl library "[[@TEST_NAME]]"; alias D = C; fn Foo(unused a: D) {} // --- fail_repeat_const.carbon library "[[@TEST_NAME]]"; class C {} fn Foo(a: const C); // CHECK:STDERR: fail_repeat_const.carbon:[[@LINE+11]]:18: warning: `const` applied repeatedly to the same type has no additional effect [RepeatedConst] // CHECK:STDERR: fn Foo(unused a: const (const C)) {} // CHECK:STDERR: ^~~~~~~~~~~~~~~ // CHECK:STDERR: // CHECK:STDERR: fail_repeat_const.carbon:[[@LINE+7]]:24: error: redeclaration syntax differs here [RedeclParamSyntaxDiffers] // CHECK:STDERR: fn Foo(unused a: const (const C)) {} // CHECK:STDERR: ^ // CHECK:STDERR: fail_repeat_const.carbon:[[@LINE-8]]:17: note: comparing with previous declaration here [RedeclParamSyntaxPrevious] // CHECK:STDERR: fn Foo(a: const C); // CHECK:STDERR: ^ // CHECK:STDERR: fn Foo(unused a: const (const C)) {} // CHECK:STDOUT: --- basic.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %C: type = class_type @C [concrete] // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %complete_type: = complete_type_witness %empty_struct_type [concrete] // CHECK:STDOUT: %pattern_type: type = pattern_type %C [concrete] // CHECK:STDOUT: %Foo.type: type = fn_type @Foo [concrete] // CHECK:STDOUT: %Foo: %Foo.type = struct_value () [concrete] // CHECK:STDOUT: %Bar.type: type = fn_type @Bar [concrete] // CHECK:STDOUT: %Bar: %Bar.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .C = %C.decl // CHECK:STDOUT: .D = %D // CHECK:STDOUT: .Foo = %Foo.decl.loc7 // CHECK:STDOUT: .Bar = %Bar.decl.loc10 // CHECK:STDOUT: } // CHECK:STDOUT: %C.decl: type = class_decl @C [concrete = constants.%C] {} {} // CHECK:STDOUT: %C.ref: type = name_ref C, %C.decl [concrete = constants.%C] // CHECK:STDOUT: %D: type = alias_binding D, %C.decl [concrete = constants.%C] // CHECK:STDOUT: %Foo.decl.loc7: %Foo.type = fn_decl @Foo [concrete = constants.%Foo] { // CHECK:STDOUT: %a.patt: %pattern_type = value_binding_pattern a [concrete] // CHECK:STDOUT: %a.param_patt: %pattern_type = value_param_pattern %a.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %a.param.loc7: %C = value_param call_param0 // CHECK:STDOUT: %C.ref.loc7: type = name_ref C, file.%C.decl [concrete = constants.%C] // CHECK:STDOUT: %a.loc7: %C = value_binding a, %a.param.loc7 // CHECK:STDOUT: } // CHECK:STDOUT: %Foo.decl.loc8: %Foo.type = fn_decl @Foo [concrete = constants.%Foo] { // CHECK:STDOUT: %a.patt: %pattern_type = value_binding_pattern a [concrete] // CHECK:STDOUT: %a.param_patt: %pattern_type = value_param_pattern %a.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %a.param.loc8: %C = value_param call_param0 // CHECK:STDOUT: %C.ref.loc8: type = name_ref C, file.%C.decl [concrete = constants.%C] // CHECK:STDOUT: %a.loc8: %C = value_binding a, %a.param.loc8 // CHECK:STDOUT: } // CHECK:STDOUT: %Bar.decl.loc10: %Bar.type = fn_decl @Bar [concrete = constants.%Bar] { // CHECK:STDOUT: %a.patt: %pattern_type = value_binding_pattern a [concrete] // CHECK:STDOUT: %a.param_patt: %pattern_type = value_param_pattern %a.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %a.param.loc10: %C = value_param call_param0 // CHECK:STDOUT: %D.ref.loc10: type = name_ref D, file.%D [concrete = constants.%C] // CHECK:STDOUT: %a.loc10: %C = value_binding a, %a.param.loc10 // CHECK:STDOUT: } // CHECK:STDOUT: %Bar.decl.loc11: %Bar.type = fn_decl @Bar [concrete = constants.%Bar] { // CHECK:STDOUT: %a.patt: %pattern_type = value_binding_pattern a [concrete] // CHECK:STDOUT: %a.param_patt: %pattern_type = value_param_pattern %a.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %a.param.loc11: %C = value_param call_param0 // CHECK:STDOUT: %D.ref.loc11: type = name_ref D, file.%D [concrete = constants.%C] // CHECK:STDOUT: %a.loc11: %C = value_binding a, %a.param.loc11 // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @C { // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%C // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @Foo(%a.param.loc8: %C) { // CHECK:STDOUT: !entry: // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @Bar(%a.param.loc11: %C) { // CHECK:STDOUT: !entry: // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- spacing.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %C: type = class_type @C [concrete] // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %complete_type: = complete_type_witness %empty_struct_type [concrete] // CHECK:STDOUT: %pattern_type: type = pattern_type %C [concrete] // CHECK:STDOUT: %Foo.type: type = fn_type @Foo [concrete] // CHECK:STDOUT: %Foo: %Foo.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .C = %C.decl // CHECK:STDOUT: .Foo = %Foo.decl.loc6 // CHECK:STDOUT: } // CHECK:STDOUT: %C.decl: type = class_decl @C [concrete = constants.%C] {} {} // CHECK:STDOUT: %Foo.decl.loc6: %Foo.type = fn_decl @Foo [concrete = constants.%Foo] { // CHECK:STDOUT: %a.patt: %pattern_type = value_binding_pattern a [concrete] // CHECK:STDOUT: %a.param_patt: %pattern_type = value_param_pattern %a.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %a.param.loc6: %C = value_param call_param0 // CHECK:STDOUT: %C.ref.loc6: type = name_ref C, file.%C.decl [concrete = constants.%C] // CHECK:STDOUT: %a.loc6: %C = value_binding a, %a.param.loc6 // CHECK:STDOUT: } // CHECK:STDOUT: %Foo.decl.loc7: %Foo.type = fn_decl @Foo [concrete = constants.%Foo] { // CHECK:STDOUT: %a.patt: %pattern_type = value_binding_pattern a [concrete] // CHECK:STDOUT: %a.param_patt: %pattern_type = value_param_pattern %a.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %a.param.loc7: %C = value_param call_param0 // CHECK:STDOUT: %C.ref.loc7: type = name_ref C, file.%C.decl [concrete = constants.%C] // CHECK:STDOUT: %a.loc7: %C = value_binding a, %a.param.loc7 // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @C { // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%C // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @Foo(%a.param.loc7: %C) { // CHECK:STDOUT: !entry: // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- fail_parens.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %C: type = class_type @C [concrete] // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %complete_type: = complete_type_witness %empty_struct_type [concrete] // CHECK:STDOUT: %pattern_type: type = pattern_type %C [concrete] // CHECK:STDOUT: %Foo.type.47530a.1: type = fn_type @Foo.loc6 [concrete] // CHECK:STDOUT: %Foo.9ec12f.1: %Foo.type.47530a.1 = struct_value () [concrete] // CHECK:STDOUT: %Foo.type.47530a.2: type = fn_type @Foo.loc14 [concrete] // CHECK:STDOUT: %Foo.9ec12f.2: %Foo.type.47530a.2 = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .C = %C.decl // CHECK:STDOUT: .Foo = %Foo.decl.loc6 // CHECK:STDOUT: } // CHECK:STDOUT: %C.decl: type = class_decl @C [concrete = constants.%C] {} {} // CHECK:STDOUT: %Foo.decl.loc6: %Foo.type.47530a.1 = fn_decl @Foo.loc6 [concrete = constants.%Foo.9ec12f.1] { // CHECK:STDOUT: %a.patt: %pattern_type = value_binding_pattern a [concrete] // CHECK:STDOUT: %a.param_patt: %pattern_type = value_param_pattern %a.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %a.param: %C = value_param call_param0 // CHECK:STDOUT: %C.ref: type = name_ref C, file.%C.decl [concrete = constants.%C] // CHECK:STDOUT: %a: %C = value_binding a, %a.param // CHECK:STDOUT: } // CHECK:STDOUT: %Foo.decl.loc14: %Foo.type.47530a.2 = fn_decl @Foo.loc14 [concrete = constants.%Foo.9ec12f.2] { // CHECK:STDOUT: %a.patt: %pattern_type = value_binding_pattern a [concrete] // CHECK:STDOUT: %a.param_patt: %pattern_type = value_param_pattern %a.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %a.param: %C = value_param call_param0 // CHECK:STDOUT: %C.ref: type = name_ref C, file.%C.decl [concrete = constants.%C] // CHECK:STDOUT: %a: %C = value_binding a, %a.param // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @C { // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%C // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @Foo.loc6(%a.param: %C); // CHECK:STDOUT: // CHECK:STDOUT: fn @Foo.loc14(%a.param: %C) { // CHECK:STDOUT: !entry: // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- fail_only_implicit_params.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %C: type = class_type @C [concrete] // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %complete_type: = complete_type_witness %empty_struct_type [concrete] // CHECK:STDOUT: %Foo.type.47530a.1: type = fn_type @Foo.loc14 [concrete] // CHECK:STDOUT: %Foo.9ec12f.1: %Foo.type.47530a.1 = struct_value () [concrete] // CHECK:STDOUT: %Foo.type.47530a.2: type = fn_type @Foo.loc23 [concrete] // CHECK:STDOUT: %Foo.9ec12f.2: %Foo.type.47530a.2 = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .C = %C.decl // CHECK:STDOUT: .Foo = %Foo.decl.loc14 // CHECK:STDOUT: } // CHECK:STDOUT: %C.decl: type = class_decl @C [concrete = constants.%C] {} {} // CHECK:STDOUT: %Foo.decl.loc14: %Foo.type.47530a.1 = fn_decl @Foo.loc14 [concrete = constants.%Foo.9ec12f.1] {} {} // CHECK:STDOUT: %Foo.decl.loc23: %Foo.type.47530a.2 = fn_decl @Foo.loc23 [concrete = constants.%Foo.9ec12f.2] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @C { // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%C // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @Foo.loc14(); // CHECK:STDOUT: // CHECK:STDOUT: fn @Foo.loc23() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- todo_fail_raw_identifier.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %C: type = class_type @C [concrete] // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %complete_type: = complete_type_witness %empty_struct_type [concrete] // CHECK:STDOUT: %pattern_type: type = pattern_type %C [concrete] // CHECK:STDOUT: %Foo.type: type = fn_type @Foo [concrete] // CHECK:STDOUT: %Foo: %Foo.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .C = %C.decl // CHECK:STDOUT: .Foo = %Foo.decl.loc6 // CHECK:STDOUT: } // CHECK:STDOUT: %C.decl: type = class_decl @C [concrete = constants.%C] {} {} // CHECK:STDOUT: %Foo.decl.loc6: %Foo.type = fn_decl @Foo [concrete = constants.%Foo] { // CHECK:STDOUT: %a.patt: %pattern_type = value_binding_pattern a [concrete] // CHECK:STDOUT: %a.param_patt: %pattern_type = value_param_pattern %a.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %a.param.loc6: %C = value_param call_param0 // CHECK:STDOUT: %C.ref.loc6: type = name_ref C, file.%C.decl [concrete = constants.%C] // CHECK:STDOUT: %a.loc6: %C = value_binding a, %a.param.loc6 // CHECK:STDOUT: } // CHECK:STDOUT: %Foo.decl.loc7: %Foo.type = fn_decl @Foo [concrete = constants.%Foo] { // CHECK:STDOUT: %a.patt: %pattern_type = value_binding_pattern a [concrete] // CHECK:STDOUT: %a.param_patt: %pattern_type = value_param_pattern %a.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %a.param.loc7: %C = value_param call_param0 // CHECK:STDOUT: %C.ref.loc7: type = name_ref C, file.%C.decl [concrete = constants.%C] // CHECK:STDOUT: %a.loc7: %C = value_binding a, %a.param.loc7 // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @C { // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%C // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @Foo(%a.param.loc7: %C) { // CHECK:STDOUT: !entry: // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- two_file.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %C: type = class_type @C [concrete] // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %complete_type: = complete_type_witness %empty_struct_type [concrete] // CHECK:STDOUT: %pattern_type: type = pattern_type %C [concrete] // CHECK:STDOUT: %Foo.type: type = fn_type @Foo [concrete] // CHECK:STDOUT: %Foo: %Foo.type = struct_value () [concrete] // CHECK:STDOUT: %Bar.type: type = fn_type @Bar [concrete] // CHECK:STDOUT: %Bar: %Bar.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .C = %C.decl // CHECK:STDOUT: .D = %D // CHECK:STDOUT: .Foo = %Foo.decl // CHECK:STDOUT: .Bar = %Bar.decl // CHECK:STDOUT: } // CHECK:STDOUT: %C.decl: type = class_decl @C [concrete = constants.%C] {} {} // CHECK:STDOUT: %C.ref: type = name_ref C, %C.decl [concrete = constants.%C] // CHECK:STDOUT: %D: type = alias_binding D, %C.decl [concrete = constants.%C] // CHECK:STDOUT: %Foo.decl: %Foo.type = fn_decl @Foo [concrete = constants.%Foo] { // CHECK:STDOUT: %a.patt: %pattern_type = value_binding_pattern a [concrete] // CHECK:STDOUT: %a.param_patt: %pattern_type = value_param_pattern %a.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %a.param: %C = value_param call_param0 // CHECK:STDOUT: %C.ref: type = name_ref C, file.%C.decl [concrete = constants.%C] // CHECK:STDOUT: %a: %C = value_binding a, %a.param // CHECK:STDOUT: } // CHECK:STDOUT: %Bar.decl: %Bar.type = fn_decl @Bar [concrete = constants.%Bar] { // CHECK:STDOUT: %a.patt: %pattern_type = value_binding_pattern a [concrete] // CHECK:STDOUT: %a.param_patt: %pattern_type = value_param_pattern %a.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %a.param: %C = value_param call_param0 // CHECK:STDOUT: %D.ref: type = name_ref D, file.%D [concrete = constants.%C] // CHECK:STDOUT: %a: %C = value_binding a, %a.param // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @C { // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%C // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @Foo(%a.param: %C); // CHECK:STDOUT: // CHECK:STDOUT: fn @Bar(%a.param: %C); // CHECK:STDOUT: // CHECK:STDOUT: --- two_file.impl.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %C: type = class_type @C [concrete] // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %complete_type: = complete_type_witness %empty_struct_type [concrete] // CHECK:STDOUT: %pattern_type: type = pattern_type %C [concrete] // CHECK:STDOUT: %Foo.type: type = fn_type @Foo [concrete] // CHECK:STDOUT: %Foo: %Foo.type = struct_value () [concrete] // CHECK:STDOUT: %Bar.type: type = fn_type @Bar [concrete] // CHECK:STDOUT: %Bar: %Bar.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Main.C: type = import_ref Main//two_file, C, loaded [concrete = constants.%C] // CHECK:STDOUT: %Main.D: type = import_ref Main//two_file, D, loaded [concrete = constants.%C] // CHECK:STDOUT: %Main.import_ref.8f2: = import_ref Main//two_file, loc4_10, loaded [concrete = constants.%complete_type] // CHECK:STDOUT: %Main.import_ref.743 = import_ref Main//two_file, inst{{[0-9A-F]+}} [no loc], unloaded // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .C = imports.%Main.C // CHECK:STDOUT: .D = imports.%Main.D // CHECK:STDOUT: .Foo = %Foo.decl // CHECK:STDOUT: .Bar = %Bar.decl // CHECK:STDOUT: } // CHECK:STDOUT: %default.import.loc2_24.1 = import // CHECK:STDOUT: %default.import.loc2_24.2 = import // CHECK:STDOUT: %Foo.decl: %Foo.type = fn_decl @Foo [concrete = constants.%Foo] { // CHECK:STDOUT: %a.patt: %pattern_type = value_binding_pattern a [concrete] // CHECK:STDOUT: %a.param_patt: %pattern_type = value_param_pattern %a.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %a.param: %C = value_param call_param0 // CHECK:STDOUT: %C.ref: type = name_ref C, imports.%Main.C [concrete = constants.%C] // CHECK:STDOUT: %a: %C = value_binding a, %a.param // CHECK:STDOUT: } // CHECK:STDOUT: %Bar.decl: %Bar.type = fn_decl @Bar [concrete = constants.%Bar] { // CHECK:STDOUT: %a.patt: %pattern_type = value_binding_pattern a [concrete] // CHECK:STDOUT: %a.param_patt: %pattern_type = value_param_pattern %a.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %a.param: %C = value_param call_param0 // CHECK:STDOUT: %D.ref: type = name_ref D, imports.%Main.D [concrete = constants.%C] // CHECK:STDOUT: %a: %C = value_binding a, %a.param // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @C [from "two_file.carbon"] { // CHECK:STDOUT: complete_type_witness = imports.%Main.import_ref.8f2 // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = imports.%Main.import_ref.743 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @Foo(%a.param: %C) [from "two_file.carbon"] { // CHECK:STDOUT: !entry: // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @Bar(%a.param: %C) [from "two_file.carbon"] { // CHECK:STDOUT: !entry: // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- fail_name_mismatch.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %C: type = class_type @C [concrete] // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %complete_type: = complete_type_witness %empty_struct_type [concrete] // CHECK:STDOUT: %pattern_type: type = pattern_type %C [concrete] // CHECK:STDOUT: %Foo.type.47530a.1: type = fn_type @Foo.loc7 [concrete] // CHECK:STDOUT: %Foo.9ec12f.1: %Foo.type.47530a.1 = struct_value () [concrete] // CHECK:STDOUT: %Foo.type.47530a.2: type = fn_type @Foo.loc15 [concrete] // CHECK:STDOUT: %Foo.9ec12f.2: %Foo.type.47530a.2 = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .C = %C.decl // CHECK:STDOUT: .D = %D // CHECK:STDOUT: .Foo = %Foo.decl.loc7 // CHECK:STDOUT: } // CHECK:STDOUT: %C.decl: type = class_decl @C [concrete = constants.%C] {} {} // CHECK:STDOUT: %C.ref: type = name_ref C, %C.decl [concrete = constants.%C] // CHECK:STDOUT: %D: type = alias_binding D, %C.decl [concrete = constants.%C] // CHECK:STDOUT: %Foo.decl.loc7: %Foo.type.47530a.1 = fn_decl @Foo.loc7 [concrete = constants.%Foo.9ec12f.1] { // CHECK:STDOUT: %a.patt: %pattern_type = value_binding_pattern a [concrete] // CHECK:STDOUT: %a.param_patt: %pattern_type = value_param_pattern %a.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %a.param: %C = value_param call_param0 // CHECK:STDOUT: %C.ref: type = name_ref C, file.%C.decl [concrete = constants.%C] // CHECK:STDOUT: %a: %C = value_binding a, %a.param // CHECK:STDOUT: } // CHECK:STDOUT: %Foo.decl.loc15: %Foo.type.47530a.2 = fn_decl @Foo.loc15 [concrete = constants.%Foo.9ec12f.2] { // CHECK:STDOUT: %b.patt: %pattern_type = value_binding_pattern b [concrete] // CHECK:STDOUT: %b.param_patt: %pattern_type = value_param_pattern %b.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %b.param: %C = value_param call_param0 // CHECK:STDOUT: %D.ref: type = name_ref D, file.%D [concrete = constants.%C] // CHECK:STDOUT: %b: %C = value_binding b, %b.param // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @C { // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%C // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @Foo.loc7(%a.param: %C); // CHECK:STDOUT: // CHECK:STDOUT: fn @Foo.loc15(%b.param: %C) { // CHECK:STDOUT: !entry: // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- fail_alias.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %C: type = class_type @C [concrete] // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %complete_type: = complete_type_witness %empty_struct_type [concrete] // CHECK:STDOUT: %pattern_type: type = pattern_type %C [concrete] // CHECK:STDOUT: %Foo.type.47530a.1: type = fn_type @Foo.loc7 [concrete] // CHECK:STDOUT: %Foo.9ec12f.1: %Foo.type.47530a.1 = struct_value () [concrete] // CHECK:STDOUT: %Foo.type.47530a.2: type = fn_type @Foo.loc15 [concrete] // CHECK:STDOUT: %Foo.9ec12f.2: %Foo.type.47530a.2 = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .C = %C.decl // CHECK:STDOUT: .D = %D // CHECK:STDOUT: .Foo = %Foo.decl.loc7 // CHECK:STDOUT: } // CHECK:STDOUT: %C.decl: type = class_decl @C [concrete = constants.%C] {} {} // CHECK:STDOUT: %C.ref: type = name_ref C, %C.decl [concrete = constants.%C] // CHECK:STDOUT: %D: type = alias_binding D, %C.decl [concrete = constants.%C] // CHECK:STDOUT: %Foo.decl.loc7: %Foo.type.47530a.1 = fn_decl @Foo.loc7 [concrete = constants.%Foo.9ec12f.1] { // CHECK:STDOUT: %a.patt: %pattern_type = value_binding_pattern a [concrete] // CHECK:STDOUT: %a.param_patt: %pattern_type = value_param_pattern %a.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %a.param: %C = value_param call_param0 // CHECK:STDOUT: %C.ref: type = name_ref C, file.%C.decl [concrete = constants.%C] // CHECK:STDOUT: %a: %C = value_binding a, %a.param // CHECK:STDOUT: } // CHECK:STDOUT: %Foo.decl.loc15: %Foo.type.47530a.2 = fn_decl @Foo.loc15 [concrete = constants.%Foo.9ec12f.2] { // CHECK:STDOUT: %a.patt: %pattern_type = value_binding_pattern a [concrete] // CHECK:STDOUT: %a.param_patt: %pattern_type = value_param_pattern %a.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %a.param: %C = value_param call_param0 // CHECK:STDOUT: %D.ref: type = name_ref D, file.%D [concrete = constants.%C] // CHECK:STDOUT: %a: %C = value_binding a, %a.param // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @C { // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%C // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @Foo.loc7(%a.param: %C); // CHECK:STDOUT: // CHECK:STDOUT: fn @Foo.loc15(%a.param: %C) { // CHECK:STDOUT: !entry: // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- fail_deduced_alias.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %C: type = class_type @C [concrete] // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %complete_type: = complete_type_witness %empty_struct_type [concrete] // CHECK:STDOUT: %type: type = facet_type [concrete] // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self] // CHECK:STDOUT: %pattern_type: type = pattern_type %C [concrete] // CHECK:STDOUT: %a.9daf47.1: %C = symbolic_binding a, 0 [symbolic] // CHECK:STDOUT: %Foo.type.47530a.1: type = fn_type @Foo.loc7 [concrete] // CHECK:STDOUT: %Foo.9ec12f.1: %Foo.type.47530a.1 = struct_value () [concrete] // CHECK:STDOUT: %a.9daf47.2: %C = symbolic_binding a, 0 [symbolic] // CHECK:STDOUT: %Foo.type.47530a.2: type = fn_type @Foo.loc15 [concrete] // CHECK:STDOUT: %Foo.9ec12f.2: %Foo.type.47530a.2 = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .C = %C.decl // CHECK:STDOUT: .D = %D // CHECK:STDOUT: .Foo = %Foo.decl.loc7 // CHECK:STDOUT: } // CHECK:STDOUT: %C.decl: type = class_decl @C [concrete = constants.%C] {} {} // CHECK:STDOUT: %C.ref: type = name_ref C, %C.decl [concrete = constants.%C] // CHECK:STDOUT: %D: type = alias_binding D, %C.decl [concrete = constants.%C] // CHECK:STDOUT: %Foo.decl.loc7: %Foo.type.47530a.1 = fn_decl @Foo.loc7 [concrete = constants.%Foo.9ec12f.1] { // CHECK:STDOUT: %a.patt: %pattern_type = symbolic_binding_pattern a, 0 [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %.loc7: type = splice_block %C.ref [concrete = constants.%C] { // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %C.ref: type = name_ref C, file.%C.decl [concrete = constants.%C] // CHECK:STDOUT: } // CHECK:STDOUT: %a.loc7_8.2: %C = symbolic_binding a, 0 [symbolic = %a.loc7_8.1 (constants.%a.9daf47.1)] // CHECK:STDOUT: } // CHECK:STDOUT: %Foo.decl.loc15: %Foo.type.47530a.2 = fn_decl @Foo.loc15 [concrete = constants.%Foo.9ec12f.2] { // CHECK:STDOUT: %a.patt: %pattern_type = symbolic_binding_pattern a, 0 [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %.loc15: type = splice_block %D.ref [concrete = constants.%C] { // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %D.ref: type = name_ref D, file.%D [concrete = constants.%C] // CHECK:STDOUT: } // CHECK:STDOUT: %a.loc15_15.3: %C = symbolic_binding a, 0 [symbolic = %a.loc15_15.2 (constants.%a.9daf47.2)] // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @C { // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%C // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic fn @Foo.loc7(%a.loc7_8.2: %C) { // CHECK:STDOUT: %a.loc7_8.1: %C = symbolic_binding a, 0 [symbolic = %a.loc7_8.1 (constants.%a.9daf47.1)] // CHECK:STDOUT: // CHECK:STDOUT: fn(); // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic fn @Foo.loc15(%a.loc15_15.3: %C) { // CHECK:STDOUT: %a.loc15_15.1: %C = symbolic_binding a, 0 [symbolic = %a.loc15_15.1 (constants.%a.9daf47.1)] // CHECK:STDOUT: %a.loc15_15.2: %C = symbolic_binding a, 0 [symbolic = %a.loc15_15.2 (constants.%a.9daf47.2)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: // CHECK:STDOUT: fn() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @Foo.loc7(constants.%a.9daf47.1) { // CHECK:STDOUT: %a.loc7_8.1 => constants.%a.9daf47.1 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @Foo.loc15(constants.%a.9daf47.2) { // CHECK:STDOUT: %a.loc15_15.1 => constants.%a.9daf47.2 // CHECK:STDOUT: %a.loc15_15.2 => constants.%a.9daf47.2 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- todo_fail_alias_in_return.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %C: type = class_type @C [concrete] // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %complete_type: = complete_type_witness %empty_struct_type [concrete] // CHECK:STDOUT: %.a69: Core.Form = init_form %C [concrete] // CHECK:STDOUT: %pattern_type: type = pattern_type %C [concrete] // CHECK:STDOUT: %Foo.type: type = fn_type @Foo [concrete] // CHECK:STDOUT: %Foo: %Foo.type = struct_value () [concrete] // CHECK:STDOUT: %empty_struct: %empty_struct_type = struct_value () [concrete] // CHECK:STDOUT: %C.val: %C = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .C = %C.decl // CHECK:STDOUT: .D = %D // CHECK:STDOUT: .Foo = %Foo.decl.loc7 // CHECK:STDOUT: } // CHECK:STDOUT: %C.decl: type = class_decl @C [concrete = constants.%C] {} {} // CHECK:STDOUT: %C.ref: type = name_ref C, %C.decl [concrete = constants.%C] // CHECK:STDOUT: %D: type = alias_binding D, %C.decl [concrete = constants.%C] // CHECK:STDOUT: %Foo.decl.loc7: %Foo.type = fn_decl @Foo [concrete = constants.%Foo] { // CHECK:STDOUT: %return.patt: %pattern_type = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: %pattern_type = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %C.ref: type = name_ref C, file.%C.decl [concrete = constants.%C] // CHECK:STDOUT: %.loc7: Core.Form = init_form %C.ref [concrete = constants.%.a69] // CHECK:STDOUT: %return.param.loc7: ref %C = out_param call_param0 // CHECK:STDOUT: %return.loc7: ref %C = return_slot %return.param.loc7 // CHECK:STDOUT: } // CHECK:STDOUT: %Foo.decl.loc8: %Foo.type = fn_decl @Foo [concrete = constants.%Foo] { // CHECK:STDOUT: %return.patt: %pattern_type = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: %pattern_type = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %D.ref: type = name_ref D, file.%D [concrete = constants.%C] // CHECK:STDOUT: %.loc8_13: Core.Form = init_form %D.ref [concrete = constants.%.a69] // CHECK:STDOUT: %return.param.loc8: ref %C = out_param call_param0 // CHECK:STDOUT: %return.loc8: ref %C = return_slot %return.param.loc8 // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @C { // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%C // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @Foo() -> out %return.param.loc8: %C { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %.loc8_25.1: %empty_struct_type = struct_literal () [concrete = constants.%empty_struct] // CHECK:STDOUT: %.loc8_25.2: init %C to %return.param.loc8 = class_init () [concrete = constants.%C.val] // CHECK:STDOUT: %.loc8_26: init %C = converted %.loc8_25.1, %.loc8_25.2 [concrete = constants.%C.val] // CHECK:STDOUT: return %.loc8_26 to %return.param.loc8 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- alias_two_file.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %C: type = class_type @C [concrete] // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %complete_type: = complete_type_witness %empty_struct_type [concrete] // CHECK:STDOUT: %pattern_type: type = pattern_type %C [concrete] // CHECK:STDOUT: %Foo.type: type = fn_type @Foo [concrete] // CHECK:STDOUT: %Foo: %Foo.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .C = %C.decl // CHECK:STDOUT: .Foo = %Foo.decl // CHECK:STDOUT: } // CHECK:STDOUT: %C.decl: type = class_decl @C [concrete = constants.%C] {} {} // CHECK:STDOUT: %Foo.decl: %Foo.type = fn_decl @Foo [concrete = constants.%Foo] { // CHECK:STDOUT: %a.patt: %pattern_type = value_binding_pattern a [concrete] // CHECK:STDOUT: %a.param_patt: %pattern_type = value_param_pattern %a.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %a.param: %C = value_param call_param0 // CHECK:STDOUT: %C.ref: type = name_ref C, file.%C.decl [concrete = constants.%C] // CHECK:STDOUT: %a: %C = value_binding a, %a.param // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @C { // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%C // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @Foo(%a.param: %C); // CHECK:STDOUT: // CHECK:STDOUT: --- todo_fail_alias_two_file.impl.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %C: type = class_type @C [concrete] // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %complete_type: = complete_type_witness %empty_struct_type [concrete] // CHECK:STDOUT: %pattern_type: type = pattern_type %C [concrete] // CHECK:STDOUT: %Foo.type: type = fn_type @Foo [concrete] // CHECK:STDOUT: %Foo: %Foo.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Main.C: type = import_ref Main//alias_two_file, C, loaded [concrete = constants.%C] // CHECK:STDOUT: %Main.import_ref.8f2: = import_ref Main//alias_two_file, loc4_10, loaded [concrete = constants.%complete_type] // CHECK:STDOUT: %Main.import_ref.743 = import_ref Main//alias_two_file, inst{{[0-9A-F]+}} [no loc], unloaded // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .C = imports.%Main.C // CHECK:STDOUT: .Foo = %Foo.decl // CHECK:STDOUT: .D = %D // CHECK:STDOUT: } // CHECK:STDOUT: %default.import.loc2_30.1 = import // CHECK:STDOUT: %default.import.loc2_30.2 = import // CHECK:STDOUT: %C.ref: type = name_ref C, imports.%Main.C [concrete = constants.%C] // CHECK:STDOUT: %D: type = alias_binding D, imports.%Main.C [concrete = constants.%C] // CHECK:STDOUT: %Foo.decl: %Foo.type = fn_decl @Foo [concrete = constants.%Foo] { // CHECK:STDOUT: %a.patt: %pattern_type = value_binding_pattern a [concrete] // CHECK:STDOUT: %a.param_patt: %pattern_type = value_param_pattern %a.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %a.param: %C = value_param call_param0 // CHECK:STDOUT: %D.ref: type = name_ref D, file.%D [concrete = constants.%C] // CHECK:STDOUT: %a: %C = value_binding a, %a.param // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @C [from "alias_two_file.carbon"] { // CHECK:STDOUT: complete_type_witness = imports.%Main.import_ref.8f2 // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = imports.%Main.import_ref.743 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @Foo(%a.param: %C) [from "alias_two_file.carbon"] { // CHECK:STDOUT: !entry: // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- fail_repeat_const.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %C: type = class_type @C [concrete] // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %complete_type: = complete_type_witness %empty_struct_type [concrete] // CHECK:STDOUT: %const: type = const_type %C [concrete] // CHECK:STDOUT: %pattern_type: type = pattern_type %const [concrete] // CHECK:STDOUT: %Foo.type.47530a.1: type = fn_type @Foo.loc6 [concrete] // CHECK:STDOUT: %Foo.9ec12f.1: %Foo.type.47530a.1 = struct_value () [concrete] // CHECK:STDOUT: %Foo.type.47530a.2: type = fn_type @Foo.loc18 [concrete] // CHECK:STDOUT: %Foo.9ec12f.2: %Foo.type.47530a.2 = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .C = %C.decl // CHECK:STDOUT: .Foo = %Foo.decl.loc6 // CHECK:STDOUT: } // CHECK:STDOUT: %C.decl: type = class_decl @C [concrete = constants.%C] {} {} // CHECK:STDOUT: %Foo.decl.loc6: %Foo.type.47530a.1 = fn_decl @Foo.loc6 [concrete = constants.%Foo.9ec12f.1] { // CHECK:STDOUT: %a.patt: %pattern_type = value_binding_pattern a [concrete] // CHECK:STDOUT: %a.param_patt: %pattern_type = value_param_pattern %a.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %a.param: %const = value_param call_param0 // CHECK:STDOUT: %.loc6: type = splice_block %const [concrete = constants.%const] { // CHECK:STDOUT: %C.ref: type = name_ref C, file.%C.decl [concrete = constants.%C] // CHECK:STDOUT: %const: type = const_type %C.ref [concrete = constants.%const] // CHECK:STDOUT: } // CHECK:STDOUT: %a: %const = value_binding a, %a.param // CHECK:STDOUT: } // CHECK:STDOUT: %Foo.decl.loc18: %Foo.type.47530a.2 = fn_decl @Foo.loc18 [concrete = constants.%Foo.9ec12f.2] { // CHECK:STDOUT: %a.patt: %pattern_type = value_binding_pattern a [concrete] // CHECK:STDOUT: %a.param_patt: %pattern_type = value_param_pattern %a.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %a.param: %const = value_param call_param0 // CHECK:STDOUT: %.loc18: type = splice_block %const.loc18_18 [concrete = constants.%const] { // CHECK:STDOUT: %C.ref: type = name_ref C, file.%C.decl [concrete = constants.%C] // CHECK:STDOUT: %const.loc18_25: type = const_type %C.ref [concrete = constants.%const] // CHECK:STDOUT: %const.loc18_18: type = const_type %const.loc18_25 [concrete = constants.%const] // CHECK:STDOUT: } // CHECK:STDOUT: %a: %const = value_binding a, %a.param // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @C { // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%C // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @Foo.loc6(%a.param: %const); // CHECK:STDOUT: // CHECK:STDOUT: fn @Foo.loc18(%a.param: %const) { // CHECK:STDOUT: !entry: // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/function/generic/call.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/convert.carbon // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/function/generic/call.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/function/generic/call.carbon // --- explicit.carbon library "[[@TEST_NAME]]"; //@dump-sem-ir-begin fn Function(T:! Core.Copy, x: T) -> T { return x; } //@dump-sem-ir-end fn CallGeneric(T:! Core.Copy, x: T) -> T { //@dump-sem-ir-begin return Function(T, x); //@dump-sem-ir-end } fn CallGenericPtr(T:! type, x: T*) -> T* { //@dump-sem-ir-begin return Function(T*, x); //@dump-sem-ir-end } class C {} fn CallSpecific(x: C*) -> C* { //@dump-sem-ir-begin return Function(C*, x); //@dump-sem-ir-end } // --- deduced.carbon library "[[@TEST_NAME]]"; //@dump-sem-ir-begin fn Function[T:! Core.Copy](x: T) -> T { return x; } //@dump-sem-ir-end fn CallGeneric(T:! Core.Copy, x: T) -> T { //@dump-sem-ir-begin return Function(x); //@dump-sem-ir-end } fn CallGenericPtr(T:! type, x: T*) -> T* { //@dump-sem-ir-begin return Function(x); //@dump-sem-ir-end } class C {} fn CallSpecific(x: C*) -> C* { //@dump-sem-ir-begin return Function(x); //@dump-sem-ir-end } // CHECK:STDOUT: --- explicit.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %Copy.type: type = facet_type <@Copy> [concrete] // CHECK:STDOUT: %pattern_type.ce2: type = pattern_type %Copy.type [concrete] // CHECK:STDOUT: %T.035: %Copy.type = symbolic_binding T, 0 [symbolic] // CHECK:STDOUT: %T.binding.as_type: type = symbolic_binding_type T, 0, %T.035 [symbolic] // CHECK:STDOUT: %pattern_type.9b9f0c.2: type = pattern_type %T.binding.as_type [symbolic] // CHECK:STDOUT: %.076a48.2: Core.Form = init_form %T.binding.as_type [symbolic] // CHECK:STDOUT: %Function.type: type = fn_type @Function [concrete] // CHECK:STDOUT: %Function: %Function.type = struct_value () [concrete] // CHECK:STDOUT: %require_complete.67c: = require_complete_type %T.binding.as_type [symbolic] // CHECK:STDOUT: %Copy.lookup_impl_witness.58d: = lookup_impl_witness %T.035, @Copy [symbolic] // CHECK:STDOUT: %Copy.WithSelf.Op.type.735e75.2: type = fn_type @Copy.WithSelf.Op, @Copy.WithSelf(%T.035) [symbolic] // CHECK:STDOUT: %.023: type = fn_type_with_self_type %Copy.WithSelf.Op.type.735e75.2, %T.035 [symbolic] // CHECK:STDOUT: %impl.elem0.594: %.023 = impl_witness_access %Copy.lookup_impl_witness.58d, element0 [symbolic] // CHECK:STDOUT: %specific_impl_fn.bdc: = specific_impl_function %impl.elem0.594, @Copy.WithSelf.Op(%T.035) [symbolic] // CHECK:STDOUT: %T.67d: type = symbolic_binding T, 0 [symbolic] // CHECK:STDOUT: %ptr.e8f: type = ptr_type %T.67d [symbolic] // CHECK:STDOUT: %require_complete.ef1: = require_complete_type %ptr.e8f [symbolic] // CHECK:STDOUT: %ptr.as.Copy.impl.Op.type.2d4: type = fn_type @ptr.as.Copy.impl.Op, @ptr.as.Copy.impl(%T.67d) [symbolic] // CHECK:STDOUT: %ptr.as.Copy.impl.Op.74e: %ptr.as.Copy.impl.Op.type.2d4 = struct_value () [symbolic] // CHECK:STDOUT: %pattern_type.4f4: type = pattern_type %ptr.e8f [symbolic] // CHECK:STDOUT: %.cb6: Core.Form = init_form %ptr.e8f [symbolic] // CHECK:STDOUT: %Function.specific_fn.a87: = specific_function %Function, @Function(%T.035) [symbolic] // CHECK:STDOUT: %.2f2: require_specific_def_type = require_specific_def @ptr.as.Copy.impl(%T.67d) [symbolic] // CHECK:STDOUT: %Copy.lookup_impl_witness.2e6: = lookup_impl_witness %ptr.e8f, @Copy [symbolic] // CHECK:STDOUT: %Copy.facet.c25: %Copy.type = facet_value %ptr.e8f, (%Copy.lookup_impl_witness.2e6) [symbolic] // CHECK:STDOUT: %Function.specific_fn.919: = specific_function %Function, @Function(%Copy.facet.c25) [symbolic] // CHECK:STDOUT: %C: type = class_type @C [concrete] // CHECK:STDOUT: %ptr.31e: type = ptr_type %C [concrete] // CHECK:STDOUT: %pattern_type.506: type = pattern_type %ptr.31e [concrete] // CHECK:STDOUT: %.de8: Core.Form = init_form %ptr.31e [concrete] // CHECK:STDOUT: %Copy.impl_witness.2c7: = impl_witness imports.%Copy.impl_witness_table.c3a, @ptr.as.Copy.impl(%C) [concrete] // CHECK:STDOUT: %ptr.as.Copy.impl.Op.type.411: type = fn_type @ptr.as.Copy.impl.Op, @ptr.as.Copy.impl(%C) [concrete] // CHECK:STDOUT: %ptr.as.Copy.impl.Op.ed9: %ptr.as.Copy.impl.Op.type.411 = struct_value () [concrete] // CHECK:STDOUT: %complete_type.17a: = complete_type_witness %ptr.31e [concrete] // CHECK:STDOUT: %Copy.facet.a7f: %Copy.type = facet_value %ptr.31e, (%Copy.impl_witness.2c7) [concrete] // CHECK:STDOUT: %Function.specific_fn.9da: = specific_function %Function, @Function(%Copy.facet.a7f) [concrete] // CHECK:STDOUT: %Copy.WithSelf.Op.type.d82: type = fn_type @Copy.WithSelf.Op, @Copy.WithSelf(%Copy.facet.c25) [symbolic] // CHECK:STDOUT: %.299: type = fn_type_with_self_type %Copy.WithSelf.Op.type.d82, %Copy.facet.c25 [symbolic] // CHECK:STDOUT: %impl.elem0.1c7: %.299 = impl_witness_access %Copy.lookup_impl_witness.2e6, element0 [symbolic] // CHECK:STDOUT: %specific_impl_fn.366: = specific_impl_function %impl.elem0.1c7, @Copy.WithSelf.Op(%Copy.facet.c25) [symbolic] // CHECK:STDOUT: %Copy.WithSelf.Op.type.259: type = fn_type @Copy.WithSelf.Op, @Copy.WithSelf(%Copy.facet.a7f) [concrete] // CHECK:STDOUT: %.64b: type = fn_type_with_self_type %Copy.WithSelf.Op.type.259, %Copy.facet.a7f [concrete] // CHECK:STDOUT: %ptr.as.Copy.impl.Op.specific_fn: = specific_function %ptr.as.Copy.impl.Op.ed9, @ptr.as.Copy.impl.Op(%C) [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: .Copy = %Core.Copy // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: %Core.Copy: type = import_ref Core//prelude/parts/copy, Copy, loaded [concrete = constants.%Copy.type] // CHECK:STDOUT: %Core.import_ref.203: @ptr.as.Copy.impl.%ptr.as.Copy.impl.Op.type (%ptr.as.Copy.impl.Op.type.2d4) = import_ref Core//prelude/parts/copy, loc{{\d+_\d+}}, loaded [symbolic = @ptr.as.Copy.impl.%ptr.as.Copy.impl.Op (constants.%ptr.as.Copy.impl.Op.74e)] // CHECK:STDOUT: %Copy.impl_witness_table.c3a = impl_witness_table (%Core.import_ref.203), @ptr.as.Copy.impl [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: %Function.decl: %Function.type = fn_decl @Function [concrete = constants.%Function] { // CHECK:STDOUT: %T.patt: %pattern_type.ce2 = symbolic_binding_pattern T, 0 [concrete] // CHECK:STDOUT: %x.patt: @Function.%pattern_type (%pattern_type.9b9f0c.2) = value_binding_pattern x [concrete] // CHECK:STDOUT: %x.param_patt: @Function.%pattern_type (%pattern_type.9b9f0c.2) = value_param_pattern %x.patt [concrete] // CHECK:STDOUT: %return.patt: @Function.%pattern_type (%pattern_type.9b9f0c.2) = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: @Function.%pattern_type (%pattern_type.9b9f0c.2) = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %T.ref.loc5_37: %Copy.type = name_ref T, %T.loc5_13.2 [symbolic = %T.loc5_13.1 (constants.%T.035)] // CHECK:STDOUT: %T.as_type.loc5_37: type = facet_access_type %T.ref.loc5_37 [symbolic = %T.binding.as_type (constants.%T.binding.as_type)] // CHECK:STDOUT: %.loc5_37.3: type = converted %T.ref.loc5_37, %T.as_type.loc5_37 [symbolic = %T.binding.as_type (constants.%T.binding.as_type)] // CHECK:STDOUT: %.loc5_37.4: Core.Form = init_form %.loc5_37.3 [symbolic = %.loc5_37.2 (constants.%.076a48.2)] // CHECK:STDOUT: %.loc5_21: type = splice_block %Copy.ref [concrete = constants.%Copy.type] { // CHECK:STDOUT: // CHECK:STDOUT: %Core.ref: = name_ref Core, imports.%Core [concrete = imports.%Core] // CHECK:STDOUT: %Copy.ref: type = name_ref Copy, imports.%Core.Copy [concrete = constants.%Copy.type] // CHECK:STDOUT: } // CHECK:STDOUT: %T.loc5_13.2: %Copy.type = symbolic_binding T, 0 [symbolic = %T.loc5_13.1 (constants.%T.035)] // CHECK:STDOUT: %x.param: @Function.%T.binding.as_type (%T.binding.as_type) = value_param call_param0 // CHECK:STDOUT: %.loc5_31.1: type = splice_block %.loc5_31.2 [symbolic = %T.binding.as_type (constants.%T.binding.as_type)] { // CHECK:STDOUT: %T.ref.loc5_31: %Copy.type = name_ref T, %T.loc5_13.2 [symbolic = %T.loc5_13.1 (constants.%T.035)] // CHECK:STDOUT: %T.as_type.loc5_31: type = facet_access_type %T.ref.loc5_31 [symbolic = %T.binding.as_type (constants.%T.binding.as_type)] // CHECK:STDOUT: %.loc5_31.2: type = converted %T.ref.loc5_31, %T.as_type.loc5_31 [symbolic = %T.binding.as_type (constants.%T.binding.as_type)] // CHECK:STDOUT: } // CHECK:STDOUT: %x: @Function.%T.binding.as_type (%T.binding.as_type) = value_binding x, %x.param // CHECK:STDOUT: %return.param: ref @Function.%T.binding.as_type (%T.binding.as_type) = out_param call_param1 // CHECK:STDOUT: %return: ref @Function.%T.binding.as_type (%T.binding.as_type) = return_slot %return.param // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic fn @Function(%T.loc5_13.2: %Copy.type) { // CHECK:STDOUT: %T.loc5_13.1: %Copy.type = symbolic_binding T, 0 [symbolic = %T.loc5_13.1 (constants.%T.035)] // CHECK:STDOUT: %T.binding.as_type: type = symbolic_binding_type T, 0, %T.loc5_13.1 [symbolic = %T.binding.as_type (constants.%T.binding.as_type)] // CHECK:STDOUT: %pattern_type: type = pattern_type %T.binding.as_type [symbolic = %pattern_type (constants.%pattern_type.9b9f0c.2)] // CHECK:STDOUT: %.loc5_37.2: Core.Form = init_form %T.binding.as_type [symbolic = %.loc5_37.2 (constants.%.076a48.2)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete: = require_complete_type %T.binding.as_type [symbolic = %require_complete (constants.%require_complete.67c)] // CHECK:STDOUT: %Copy.lookup_impl_witness: = lookup_impl_witness %T.loc5_13.1, @Copy [symbolic = %Copy.lookup_impl_witness (constants.%Copy.lookup_impl_witness.58d)] // CHECK:STDOUT: %Copy.WithSelf.Op.type: type = fn_type @Copy.WithSelf.Op, @Copy.WithSelf(%T.loc5_13.1) [symbolic = %Copy.WithSelf.Op.type (constants.%Copy.WithSelf.Op.type.735e75.2)] // CHECK:STDOUT: %.loc6: type = fn_type_with_self_type %Copy.WithSelf.Op.type, %T.loc5_13.1 [symbolic = %.loc6 (constants.%.023)] // CHECK:STDOUT: %impl.elem0.loc6_10.2: @Function.%.loc6 (%.023) = impl_witness_access %Copy.lookup_impl_witness, element0 [symbolic = %impl.elem0.loc6_10.2 (constants.%impl.elem0.594)] // CHECK:STDOUT: %specific_impl_fn.loc6_10.2: = specific_impl_function %impl.elem0.loc6_10.2, @Copy.WithSelf.Op(%T.loc5_13.1) [symbolic = %specific_impl_fn.loc6_10.2 (constants.%specific_impl_fn.bdc)] // CHECK:STDOUT: // CHECK:STDOUT: fn(%x.param: @Function.%T.binding.as_type (%T.binding.as_type)) -> out %return.param: @Function.%T.binding.as_type (%T.binding.as_type) { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %x.ref: @Function.%T.binding.as_type (%T.binding.as_type) = name_ref x, %x // CHECK:STDOUT: %impl.elem0.loc6_10.1: @Function.%.loc6 (%.023) = impl_witness_access constants.%Copy.lookup_impl_witness.58d, element0 [symbolic = %impl.elem0.loc6_10.2 (constants.%impl.elem0.594)] // CHECK:STDOUT: %bound_method.loc6_10.1: = bound_method %x.ref, %impl.elem0.loc6_10.1 // CHECK:STDOUT: %specific_impl_fn.loc6_10.1: = specific_impl_function %impl.elem0.loc6_10.1, @Copy.WithSelf.Op(constants.%T.035) [symbolic = %specific_impl_fn.loc6_10.2 (constants.%specific_impl_fn.bdc)] // CHECK:STDOUT: %bound_method.loc6_10.2: = bound_method %x.ref, %specific_impl_fn.loc6_10.1 // CHECK:STDOUT: %.loc5_37.1: ref @Function.%T.binding.as_type (%T.binding.as_type) = splice_block %return.param {} // CHECK:STDOUT: %Copy.WithSelf.Op.call: init @Function.%T.binding.as_type (%T.binding.as_type) to %.loc5_37.1 = call %bound_method.loc6_10.2(%x.ref) // CHECK:STDOUT: return %Copy.WithSelf.Op.call to %return.param // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic fn @CallGeneric(%T.loc10_16.2: %Copy.type) { // CHECK:STDOUT: // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: // CHECK:STDOUT: %Function.specific_fn.loc12_10.2: = specific_function constants.%Function, @Function(%T.loc10_16.1) [symbolic = %Function.specific_fn.loc12_10.2 (constants.%Function.specific_fn.a87)] // CHECK:STDOUT: // CHECK:STDOUT: fn(%x.param: @CallGeneric.%T.binding.as_type (%T.binding.as_type)) -> out %return.param: @CallGeneric.%T.binding.as_type (%T.binding.as_type) { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %Function.ref: %Function.type = name_ref Function, file.%Function.decl [concrete = constants.%Function] // CHECK:STDOUT: %T.ref.loc12: %Copy.type = name_ref T, %T.loc10_16.2 [symbolic = %T.loc10_16.1 (constants.%T.035)] // CHECK:STDOUT: %x.ref: @CallGeneric.%T.binding.as_type (%T.binding.as_type) = name_ref x, %x // CHECK:STDOUT: %.loc12: %Copy.type = converted constants.%T.binding.as_type, constants.%T.035 [symbolic = %T.loc10_16.1 (constants.%T.035)] // CHECK:STDOUT: %Function.specific_fn.loc12_10.1: = specific_function %Function.ref, @Function(constants.%T.035) [symbolic = %Function.specific_fn.loc12_10.2 (constants.%Function.specific_fn.a87)] // CHECK:STDOUT: // CHECK:STDOUT: %Function.call: init @CallGeneric.%T.binding.as_type (%T.binding.as_type) to %.loc10_40.1 = call %Function.specific_fn.loc12_10.1(%x.ref) // CHECK:STDOUT: return %Function.call to %return.param // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic fn @CallGenericPtr(%T.loc16_19.2: type) { // CHECK:STDOUT: // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: // CHECK:STDOUT: %.loc18_24.3: require_specific_def_type = require_specific_def @ptr.as.Copy.impl(%T.loc16_19.1) [symbolic = %.loc18_24.3 (constants.%.2f2)] // CHECK:STDOUT: %Copy.lookup_impl_witness: = lookup_impl_witness %ptr.loc16_33.1, @Copy [symbolic = %Copy.lookup_impl_witness (constants.%Copy.lookup_impl_witness.2e6)] // CHECK:STDOUT: %Copy.facet.loc18_24.3: %Copy.type = facet_value %ptr.loc16_33.1, (%Copy.lookup_impl_witness) [symbolic = %Copy.facet.loc18_24.3 (constants.%Copy.facet.c25)] // CHECK:STDOUT: %Function.specific_fn.loc18_10.2: = specific_function constants.%Function, @Function(%Copy.facet.loc18_24.3) [symbolic = %Function.specific_fn.loc18_10.2 (constants.%Function.specific_fn.919)] // CHECK:STDOUT: // CHECK:STDOUT: fn(%x.param: @CallGenericPtr.%ptr.loc16_33.1 (%ptr.e8f)) -> out %return.param: @CallGenericPtr.%ptr.loc16_33.1 (%ptr.e8f) { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %Function.ref: %Function.type = name_ref Function, file.%Function.decl [concrete = constants.%Function] // CHECK:STDOUT: %T.ref.loc18: type = name_ref T, %T.loc16_19.2 [symbolic = %T.loc16_19.1 (constants.%T.67d)] // CHECK:STDOUT: %ptr.loc18: type = ptr_type %T.ref.loc18 [symbolic = %ptr.loc16_33.1 (constants.%ptr.e8f)] // CHECK:STDOUT: %x.ref: @CallGenericPtr.%ptr.loc16_33.1 (%ptr.e8f) = name_ref x, %x // CHECK:STDOUT: %Copy.facet.loc18_24.1: %Copy.type = facet_value %ptr.loc18, (constants.%Copy.lookup_impl_witness.2e6) [symbolic = %Copy.facet.loc18_24.3 (constants.%Copy.facet.c25)] // CHECK:STDOUT: %.loc18_24.1: %Copy.type = converted %ptr.loc18, %Copy.facet.loc18_24.1 [symbolic = %Copy.facet.loc18_24.3 (constants.%Copy.facet.c25)] // CHECK:STDOUT: %Copy.facet.loc18_24.2: %Copy.type = facet_value constants.%ptr.e8f, (constants.%Copy.lookup_impl_witness.2e6) [symbolic = %Copy.facet.loc18_24.3 (constants.%Copy.facet.c25)] // CHECK:STDOUT: %.loc18_24.2: %Copy.type = converted constants.%ptr.e8f, %Copy.facet.loc18_24.2 [symbolic = %Copy.facet.loc18_24.3 (constants.%Copy.facet.c25)] // CHECK:STDOUT: %Function.specific_fn.loc18_10.1: = specific_function %Function.ref, @Function(constants.%Copy.facet.c25) [symbolic = %Function.specific_fn.loc18_10.2 (constants.%Function.specific_fn.919)] // CHECK:STDOUT: %Function.call: init @CallGenericPtr.%ptr.loc16_33.1 (%ptr.e8f) = call %Function.specific_fn.loc18_10.1(%x.ref) // CHECK:STDOUT: return %Function.call // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @CallSpecific(%x.param: %ptr.31e) -> out %return.param: %ptr.31e { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %Function.ref: %Function.type = name_ref Function, file.%Function.decl [concrete = constants.%Function] // CHECK:STDOUT: %C.ref.loc26: type = name_ref C, file.%C.decl [concrete = constants.%C] // CHECK:STDOUT: %ptr.loc26: type = ptr_type %C.ref.loc26 [concrete = constants.%ptr.31e] // CHECK:STDOUT: %x.ref: %ptr.31e = name_ref x, %x // CHECK:STDOUT: %Copy.facet.loc26_24.1: %Copy.type = facet_value %ptr.loc26, (constants.%Copy.impl_witness.2c7) [concrete = constants.%Copy.facet.a7f] // CHECK:STDOUT: %.loc26_24.1: %Copy.type = converted %ptr.loc26, %Copy.facet.loc26_24.1 [concrete = constants.%Copy.facet.a7f] // CHECK:STDOUT: %Copy.facet.loc26_24.2: %Copy.type = facet_value constants.%ptr.31e, (constants.%Copy.impl_witness.2c7) [concrete = constants.%Copy.facet.a7f] // CHECK:STDOUT: %.loc26_24.2: %Copy.type = converted constants.%ptr.31e, %Copy.facet.loc26_24.2 [concrete = constants.%Copy.facet.a7f] // CHECK:STDOUT: %Function.specific_fn: = specific_function %Function.ref, @Function(constants.%Copy.facet.a7f) [concrete = constants.%Function.specific_fn.9da] // CHECK:STDOUT: %Function.call: init %ptr.31e = call %Function.specific_fn(%x.ref) // CHECK:STDOUT: return %Function.call // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @Function(constants.%T.035) { // CHECK:STDOUT: %T.loc5_13.1 => constants.%T.035 // CHECK:STDOUT: %T.binding.as_type => constants.%T.binding.as_type // CHECK:STDOUT: %pattern_type => constants.%pattern_type.9b9f0c.2 // CHECK:STDOUT: %.loc5_37.2 => constants.%.076a48.2 // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete => constants.%require_complete.67c // CHECK:STDOUT: %Copy.lookup_impl_witness => constants.%Copy.lookup_impl_witness.58d // CHECK:STDOUT: %Copy.WithSelf.Op.type => constants.%Copy.WithSelf.Op.type.735e75.2 // CHECK:STDOUT: %.loc6 => constants.%.023 // CHECK:STDOUT: %impl.elem0.loc6_10.2 => constants.%impl.elem0.594 // CHECK:STDOUT: %specific_impl_fn.loc6_10.2 => constants.%specific_impl_fn.bdc // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @CallGeneric(constants.%T.035) { // CHECK:STDOUT: %T.loc10_16.1 => constants.%T.035 // CHECK:STDOUT: %T.binding.as_type => constants.%T.binding.as_type // CHECK:STDOUT: %pattern_type => constants.%pattern_type.9b9f0c.2 // CHECK:STDOUT: %.loc10_40.2 => constants.%.076a48.2 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @CallGenericPtr(constants.%T.67d) { // CHECK:STDOUT: %T.loc16_19.1 => constants.%T.67d // CHECK:STDOUT: %ptr.loc16_33.1 => constants.%ptr.e8f // CHECK:STDOUT: %pattern_type => constants.%pattern_type.4f4 // CHECK:STDOUT: %.loc16_40.1 => constants.%.cb6 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @Function(constants.%Copy.facet.c25) { // CHECK:STDOUT: %T.loc5_13.1 => constants.%Copy.facet.c25 // CHECK:STDOUT: %T.binding.as_type => constants.%ptr.e8f // CHECK:STDOUT: %pattern_type => constants.%pattern_type.4f4 // CHECK:STDOUT: %.loc5_37.2 => constants.%.cb6 // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete => constants.%require_complete.ef1 // CHECK:STDOUT: %Copy.lookup_impl_witness => constants.%Copy.lookup_impl_witness.2e6 // CHECK:STDOUT: %Copy.WithSelf.Op.type => constants.%Copy.WithSelf.Op.type.d82 // CHECK:STDOUT: %.loc6 => constants.%.299 // CHECK:STDOUT: %impl.elem0.loc6_10.2 => constants.%impl.elem0.1c7 // CHECK:STDOUT: %specific_impl_fn.loc6_10.2 => constants.%specific_impl_fn.366 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @Function(constants.%Copy.facet.a7f) { // CHECK:STDOUT: %T.loc5_13.1 => constants.%Copy.facet.a7f // CHECK:STDOUT: %T.binding.as_type => constants.%ptr.31e // CHECK:STDOUT: %pattern_type => constants.%pattern_type.506 // CHECK:STDOUT: %.loc5_37.2 => constants.%.de8 // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete => constants.%complete_type.17a // CHECK:STDOUT: %Copy.lookup_impl_witness => constants.%Copy.impl_witness.2c7 // CHECK:STDOUT: %Copy.WithSelf.Op.type => constants.%Copy.WithSelf.Op.type.259 // CHECK:STDOUT: %.loc6 => constants.%.64b // CHECK:STDOUT: %impl.elem0.loc6_10.2 => constants.%ptr.as.Copy.impl.Op.ed9 // CHECK:STDOUT: %specific_impl_fn.loc6_10.2 => constants.%ptr.as.Copy.impl.Op.specific_fn // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- deduced.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %Copy.type: type = facet_type <@Copy> [concrete] // CHECK:STDOUT: %pattern_type.ce2: type = pattern_type %Copy.type [concrete] // CHECK:STDOUT: %T.035: %Copy.type = symbolic_binding T, 0 [symbolic] // CHECK:STDOUT: %T.binding.as_type: type = symbolic_binding_type T, 0, %T.035 [symbolic] // CHECK:STDOUT: %pattern_type.9b9f0c.2: type = pattern_type %T.binding.as_type [symbolic] // CHECK:STDOUT: %.076a48.2: Core.Form = init_form %T.binding.as_type [symbolic] // CHECK:STDOUT: %Function.type: type = fn_type @Function [concrete] // CHECK:STDOUT: %Function: %Function.type = struct_value () [concrete] // CHECK:STDOUT: %require_complete.67c: = require_complete_type %T.binding.as_type [symbolic] // CHECK:STDOUT: %Copy.lookup_impl_witness.58d: = lookup_impl_witness %T.035, @Copy [symbolic] // CHECK:STDOUT: %Copy.WithSelf.Op.type.735e75.2: type = fn_type @Copy.WithSelf.Op, @Copy.WithSelf(%T.035) [symbolic] // CHECK:STDOUT: %.023: type = fn_type_with_self_type %Copy.WithSelf.Op.type.735e75.2, %T.035 [symbolic] // CHECK:STDOUT: %impl.elem0.594: %.023 = impl_witness_access %Copy.lookup_impl_witness.58d, element0 [symbolic] // CHECK:STDOUT: %specific_impl_fn.bdc: = specific_impl_function %impl.elem0.594, @Copy.WithSelf.Op(%T.035) [symbolic] // CHECK:STDOUT: %T.67d: type = symbolic_binding T, 0 [symbolic] // CHECK:STDOUT: %ptr.e8f: type = ptr_type %T.67d [symbolic] // CHECK:STDOUT: %require_complete.ef1: = require_complete_type %ptr.e8f [symbolic] // CHECK:STDOUT: %ptr.as.Copy.impl.Op.type.2d4: type = fn_type @ptr.as.Copy.impl.Op, @ptr.as.Copy.impl(%T.67d) [symbolic] // CHECK:STDOUT: %ptr.as.Copy.impl.Op.74e: %ptr.as.Copy.impl.Op.type.2d4 = struct_value () [symbolic] // CHECK:STDOUT: %pattern_type.4f4: type = pattern_type %ptr.e8f [symbolic] // CHECK:STDOUT: %.cb6: Core.Form = init_form %ptr.e8f [symbolic] // CHECK:STDOUT: %Function.specific_fn.a87: = specific_function %Function, @Function(%T.035) [symbolic] // CHECK:STDOUT: %.2f2: require_specific_def_type = require_specific_def @ptr.as.Copy.impl(%T.67d) [symbolic] // CHECK:STDOUT: %Copy.lookup_impl_witness.2e6: = lookup_impl_witness %ptr.e8f, @Copy [symbolic] // CHECK:STDOUT: %Copy.facet.c25: %Copy.type = facet_value %ptr.e8f, (%Copy.lookup_impl_witness.2e6) [symbolic] // CHECK:STDOUT: %Function.specific_fn.919: = specific_function %Function, @Function(%Copy.facet.c25) [symbolic] // CHECK:STDOUT: %C: type = class_type @C [concrete] // CHECK:STDOUT: %ptr.31e: type = ptr_type %C [concrete] // CHECK:STDOUT: %pattern_type.506: type = pattern_type %ptr.31e [concrete] // CHECK:STDOUT: %.de8: Core.Form = init_form %ptr.31e [concrete] // CHECK:STDOUT: %Copy.impl_witness.2c7: = impl_witness imports.%Copy.impl_witness_table.c3a, @ptr.as.Copy.impl(%C) [concrete] // CHECK:STDOUT: %ptr.as.Copy.impl.Op.type.411: type = fn_type @ptr.as.Copy.impl.Op, @ptr.as.Copy.impl(%C) [concrete] // CHECK:STDOUT: %ptr.as.Copy.impl.Op.ed9: %ptr.as.Copy.impl.Op.type.411 = struct_value () [concrete] // CHECK:STDOUT: %complete_type.17a: = complete_type_witness %ptr.31e [concrete] // CHECK:STDOUT: %Copy.facet.a7f: %Copy.type = facet_value %ptr.31e, (%Copy.impl_witness.2c7) [concrete] // CHECK:STDOUT: %Function.specific_fn.9da: = specific_function %Function, @Function(%Copy.facet.a7f) [concrete] // CHECK:STDOUT: %Copy.WithSelf.Op.type.d82: type = fn_type @Copy.WithSelf.Op, @Copy.WithSelf(%Copy.facet.c25) [symbolic] // CHECK:STDOUT: %.299: type = fn_type_with_self_type %Copy.WithSelf.Op.type.d82, %Copy.facet.c25 [symbolic] // CHECK:STDOUT: %impl.elem0.1c7: %.299 = impl_witness_access %Copy.lookup_impl_witness.2e6, element0 [symbolic] // CHECK:STDOUT: %specific_impl_fn.366: = specific_impl_function %impl.elem0.1c7, @Copy.WithSelf.Op(%Copy.facet.c25) [symbolic] // CHECK:STDOUT: %Copy.WithSelf.Op.type.259: type = fn_type @Copy.WithSelf.Op, @Copy.WithSelf(%Copy.facet.a7f) [concrete] // CHECK:STDOUT: %.64b: type = fn_type_with_self_type %Copy.WithSelf.Op.type.259, %Copy.facet.a7f [concrete] // CHECK:STDOUT: %ptr.as.Copy.impl.Op.specific_fn: = specific_function %ptr.as.Copy.impl.Op.ed9, @ptr.as.Copy.impl.Op(%C) [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: .Copy = %Core.Copy // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: %Core.Copy: type = import_ref Core//prelude/parts/copy, Copy, loaded [concrete = constants.%Copy.type] // CHECK:STDOUT: %Core.import_ref.203: @ptr.as.Copy.impl.%ptr.as.Copy.impl.Op.type (%ptr.as.Copy.impl.Op.type.2d4) = import_ref Core//prelude/parts/copy, loc{{\d+_\d+}}, loaded [symbolic = @ptr.as.Copy.impl.%ptr.as.Copy.impl.Op (constants.%ptr.as.Copy.impl.Op.74e)] // CHECK:STDOUT: %Copy.impl_witness_table.c3a = impl_witness_table (%Core.import_ref.203), @ptr.as.Copy.impl [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: %Function.decl: %Function.type = fn_decl @Function [concrete = constants.%Function] { // CHECK:STDOUT: %T.patt: %pattern_type.ce2 = symbolic_binding_pattern T, 0 [concrete] // CHECK:STDOUT: %x.patt: @Function.%pattern_type (%pattern_type.9b9f0c.2) = value_binding_pattern x [concrete] // CHECK:STDOUT: %x.param_patt: @Function.%pattern_type (%pattern_type.9b9f0c.2) = value_param_pattern %x.patt [concrete] // CHECK:STDOUT: %return.patt: @Function.%pattern_type (%pattern_type.9b9f0c.2) = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: @Function.%pattern_type (%pattern_type.9b9f0c.2) = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %T.ref.loc5_37: %Copy.type = name_ref T, %T.loc5_13.2 [symbolic = %T.loc5_13.1 (constants.%T.035)] // CHECK:STDOUT: %T.as_type.loc5_37: type = facet_access_type %T.ref.loc5_37 [symbolic = %T.binding.as_type (constants.%T.binding.as_type)] // CHECK:STDOUT: %.loc5_37.3: type = converted %T.ref.loc5_37, %T.as_type.loc5_37 [symbolic = %T.binding.as_type (constants.%T.binding.as_type)] // CHECK:STDOUT: %.loc5_37.4: Core.Form = init_form %.loc5_37.3 [symbolic = %.loc5_37.2 (constants.%.076a48.2)] // CHECK:STDOUT: %.loc5_21: type = splice_block %Copy.ref [concrete = constants.%Copy.type] { // CHECK:STDOUT: // CHECK:STDOUT: %Core.ref: = name_ref Core, imports.%Core [concrete = imports.%Core] // CHECK:STDOUT: %Copy.ref: type = name_ref Copy, imports.%Core.Copy [concrete = constants.%Copy.type] // CHECK:STDOUT: } // CHECK:STDOUT: %T.loc5_13.2: %Copy.type = symbolic_binding T, 0 [symbolic = %T.loc5_13.1 (constants.%T.035)] // CHECK:STDOUT: %x.param: @Function.%T.binding.as_type (%T.binding.as_type) = value_param call_param0 // CHECK:STDOUT: %.loc5_31.1: type = splice_block %.loc5_31.2 [symbolic = %T.binding.as_type (constants.%T.binding.as_type)] { // CHECK:STDOUT: %T.ref.loc5_31: %Copy.type = name_ref T, %T.loc5_13.2 [symbolic = %T.loc5_13.1 (constants.%T.035)] // CHECK:STDOUT: %T.as_type.loc5_31: type = facet_access_type %T.ref.loc5_31 [symbolic = %T.binding.as_type (constants.%T.binding.as_type)] // CHECK:STDOUT: %.loc5_31.2: type = converted %T.ref.loc5_31, %T.as_type.loc5_31 [symbolic = %T.binding.as_type (constants.%T.binding.as_type)] // CHECK:STDOUT: } // CHECK:STDOUT: %x: @Function.%T.binding.as_type (%T.binding.as_type) = value_binding x, %x.param // CHECK:STDOUT: %return.param: ref @Function.%T.binding.as_type (%T.binding.as_type) = out_param call_param1 // CHECK:STDOUT: %return: ref @Function.%T.binding.as_type (%T.binding.as_type) = return_slot %return.param // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic fn @Function(%T.loc5_13.2: %Copy.type) { // CHECK:STDOUT: %T.loc5_13.1: %Copy.type = symbolic_binding T, 0 [symbolic = %T.loc5_13.1 (constants.%T.035)] // CHECK:STDOUT: %T.binding.as_type: type = symbolic_binding_type T, 0, %T.loc5_13.1 [symbolic = %T.binding.as_type (constants.%T.binding.as_type)] // CHECK:STDOUT: %pattern_type: type = pattern_type %T.binding.as_type [symbolic = %pattern_type (constants.%pattern_type.9b9f0c.2)] // CHECK:STDOUT: %.loc5_37.2: Core.Form = init_form %T.binding.as_type [symbolic = %.loc5_37.2 (constants.%.076a48.2)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete: = require_complete_type %T.binding.as_type [symbolic = %require_complete (constants.%require_complete.67c)] // CHECK:STDOUT: %Copy.lookup_impl_witness: = lookup_impl_witness %T.loc5_13.1, @Copy [symbolic = %Copy.lookup_impl_witness (constants.%Copy.lookup_impl_witness.58d)] // CHECK:STDOUT: %Copy.WithSelf.Op.type: type = fn_type @Copy.WithSelf.Op, @Copy.WithSelf(%T.loc5_13.1) [symbolic = %Copy.WithSelf.Op.type (constants.%Copy.WithSelf.Op.type.735e75.2)] // CHECK:STDOUT: %.loc6: type = fn_type_with_self_type %Copy.WithSelf.Op.type, %T.loc5_13.1 [symbolic = %.loc6 (constants.%.023)] // CHECK:STDOUT: %impl.elem0.loc6_10.2: @Function.%.loc6 (%.023) = impl_witness_access %Copy.lookup_impl_witness, element0 [symbolic = %impl.elem0.loc6_10.2 (constants.%impl.elem0.594)] // CHECK:STDOUT: %specific_impl_fn.loc6_10.2: = specific_impl_function %impl.elem0.loc6_10.2, @Copy.WithSelf.Op(%T.loc5_13.1) [symbolic = %specific_impl_fn.loc6_10.2 (constants.%specific_impl_fn.bdc)] // CHECK:STDOUT: // CHECK:STDOUT: fn(%x.param: @Function.%T.binding.as_type (%T.binding.as_type)) -> out %return.param: @Function.%T.binding.as_type (%T.binding.as_type) { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %x.ref: @Function.%T.binding.as_type (%T.binding.as_type) = name_ref x, %x // CHECK:STDOUT: %impl.elem0.loc6_10.1: @Function.%.loc6 (%.023) = impl_witness_access constants.%Copy.lookup_impl_witness.58d, element0 [symbolic = %impl.elem0.loc6_10.2 (constants.%impl.elem0.594)] // CHECK:STDOUT: %bound_method.loc6_10.1: = bound_method %x.ref, %impl.elem0.loc6_10.1 // CHECK:STDOUT: %specific_impl_fn.loc6_10.1: = specific_impl_function %impl.elem0.loc6_10.1, @Copy.WithSelf.Op(constants.%T.035) [symbolic = %specific_impl_fn.loc6_10.2 (constants.%specific_impl_fn.bdc)] // CHECK:STDOUT: %bound_method.loc6_10.2: = bound_method %x.ref, %specific_impl_fn.loc6_10.1 // CHECK:STDOUT: %.loc5_37.1: ref @Function.%T.binding.as_type (%T.binding.as_type) = splice_block %return.param {} // CHECK:STDOUT: %Copy.WithSelf.Op.call: init @Function.%T.binding.as_type (%T.binding.as_type) to %.loc5_37.1 = call %bound_method.loc6_10.2(%x.ref) // CHECK:STDOUT: return %Copy.WithSelf.Op.call to %return.param // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic fn @CallGeneric(%T.loc10_16.2: %Copy.type) { // CHECK:STDOUT: // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: // CHECK:STDOUT: %Function.specific_fn.loc12_10.2: = specific_function constants.%Function, @Function(%T.loc10_16.1) [symbolic = %Function.specific_fn.loc12_10.2 (constants.%Function.specific_fn.a87)] // CHECK:STDOUT: // CHECK:STDOUT: fn(%x.param: @CallGeneric.%T.binding.as_type (%T.binding.as_type)) -> out %return.param: @CallGeneric.%T.binding.as_type (%T.binding.as_type) { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %Function.ref: %Function.type = name_ref Function, file.%Function.decl [concrete = constants.%Function] // CHECK:STDOUT: %x.ref: @CallGeneric.%T.binding.as_type (%T.binding.as_type) = name_ref x, %x // CHECK:STDOUT: %.loc12: %Copy.type = converted constants.%T.binding.as_type, constants.%T.035 [symbolic = %T.loc10_16.1 (constants.%T.035)] // CHECK:STDOUT: %Function.specific_fn.loc12_10.1: = specific_function %Function.ref, @Function(constants.%T.035) [symbolic = %Function.specific_fn.loc12_10.2 (constants.%Function.specific_fn.a87)] // CHECK:STDOUT: // CHECK:STDOUT: %Function.call: init @CallGeneric.%T.binding.as_type (%T.binding.as_type) to %.loc10_40.1 = call %Function.specific_fn.loc12_10.1(%x.ref) // CHECK:STDOUT: return %Function.call to %return.param // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic fn @CallGenericPtr(%T.loc16_19.2: type) { // CHECK:STDOUT: // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: // CHECK:STDOUT: %.loc18_20.2: require_specific_def_type = require_specific_def @ptr.as.Copy.impl(%T.loc16_19.1) [symbolic = %.loc18_20.2 (constants.%.2f2)] // CHECK:STDOUT: %Copy.lookup_impl_witness: = lookup_impl_witness %ptr.loc16_33.1, @Copy [symbolic = %Copy.lookup_impl_witness (constants.%Copy.lookup_impl_witness.2e6)] // CHECK:STDOUT: %Copy.facet.loc18_20.2: %Copy.type = facet_value %ptr.loc16_33.1, (%Copy.lookup_impl_witness) [symbolic = %Copy.facet.loc18_20.2 (constants.%Copy.facet.c25)] // CHECK:STDOUT: %Function.specific_fn.loc18_10.2: = specific_function constants.%Function, @Function(%Copy.facet.loc18_20.2) [symbolic = %Function.specific_fn.loc18_10.2 (constants.%Function.specific_fn.919)] // CHECK:STDOUT: // CHECK:STDOUT: fn(%x.param: @CallGenericPtr.%ptr.loc16_33.1 (%ptr.e8f)) -> out %return.param: @CallGenericPtr.%ptr.loc16_33.1 (%ptr.e8f) { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %Function.ref: %Function.type = name_ref Function, file.%Function.decl [concrete = constants.%Function] // CHECK:STDOUT: %x.ref: @CallGenericPtr.%ptr.loc16_33.1 (%ptr.e8f) = name_ref x, %x // CHECK:STDOUT: %Copy.facet.loc18_20.1: %Copy.type = facet_value constants.%ptr.e8f, (constants.%Copy.lookup_impl_witness.2e6) [symbolic = %Copy.facet.loc18_20.2 (constants.%Copy.facet.c25)] // CHECK:STDOUT: %.loc18_20.1: %Copy.type = converted constants.%ptr.e8f, %Copy.facet.loc18_20.1 [symbolic = %Copy.facet.loc18_20.2 (constants.%Copy.facet.c25)] // CHECK:STDOUT: %Function.specific_fn.loc18_10.1: = specific_function %Function.ref, @Function(constants.%Copy.facet.c25) [symbolic = %Function.specific_fn.loc18_10.2 (constants.%Function.specific_fn.919)] // CHECK:STDOUT: %Function.call: init @CallGenericPtr.%ptr.loc16_33.1 (%ptr.e8f) = call %Function.specific_fn.loc18_10.1(%x.ref) // CHECK:STDOUT: return %Function.call // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @CallSpecific(%x.param: %ptr.31e) -> out %return.param: %ptr.31e { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %Function.ref: %Function.type = name_ref Function, file.%Function.decl [concrete = constants.%Function] // CHECK:STDOUT: %x.ref: %ptr.31e = name_ref x, %x // CHECK:STDOUT: %Copy.facet: %Copy.type = facet_value constants.%ptr.31e, (constants.%Copy.impl_witness.2c7) [concrete = constants.%Copy.facet.a7f] // CHECK:STDOUT: %.loc26: %Copy.type = converted constants.%ptr.31e, %Copy.facet [concrete = constants.%Copy.facet.a7f] // CHECK:STDOUT: %Function.specific_fn: = specific_function %Function.ref, @Function(constants.%Copy.facet.a7f) [concrete = constants.%Function.specific_fn.9da] // CHECK:STDOUT: %Function.call: init %ptr.31e = call %Function.specific_fn(%x.ref) // CHECK:STDOUT: return %Function.call // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @Function(constants.%T.035) { // CHECK:STDOUT: %T.loc5_13.1 => constants.%T.035 // CHECK:STDOUT: %T.binding.as_type => constants.%T.binding.as_type // CHECK:STDOUT: %pattern_type => constants.%pattern_type.9b9f0c.2 // CHECK:STDOUT: %.loc5_37.2 => constants.%.076a48.2 // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete => constants.%require_complete.67c // CHECK:STDOUT: %Copy.lookup_impl_witness => constants.%Copy.lookup_impl_witness.58d // CHECK:STDOUT: %Copy.WithSelf.Op.type => constants.%Copy.WithSelf.Op.type.735e75.2 // CHECK:STDOUT: %.loc6 => constants.%.023 // CHECK:STDOUT: %impl.elem0.loc6_10.2 => constants.%impl.elem0.594 // CHECK:STDOUT: %specific_impl_fn.loc6_10.2 => constants.%specific_impl_fn.bdc // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @CallGeneric(constants.%T.035) { // CHECK:STDOUT: %T.loc10_16.1 => constants.%T.035 // CHECK:STDOUT: %T.binding.as_type => constants.%T.binding.as_type // CHECK:STDOUT: %pattern_type => constants.%pattern_type.9b9f0c.2 // CHECK:STDOUT: %.loc10_40.2 => constants.%.076a48.2 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @CallGenericPtr(constants.%T.67d) { // CHECK:STDOUT: %T.loc16_19.1 => constants.%T.67d // CHECK:STDOUT: %ptr.loc16_33.1 => constants.%ptr.e8f // CHECK:STDOUT: %pattern_type => constants.%pattern_type.4f4 // CHECK:STDOUT: %.loc16_40.1 => constants.%.cb6 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @Function(constants.%Copy.facet.c25) { // CHECK:STDOUT: %T.loc5_13.1 => constants.%Copy.facet.c25 // CHECK:STDOUT: %T.binding.as_type => constants.%ptr.e8f // CHECK:STDOUT: %pattern_type => constants.%pattern_type.4f4 // CHECK:STDOUT: %.loc5_37.2 => constants.%.cb6 // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete => constants.%require_complete.ef1 // CHECK:STDOUT: %Copy.lookup_impl_witness => constants.%Copy.lookup_impl_witness.2e6 // CHECK:STDOUT: %Copy.WithSelf.Op.type => constants.%Copy.WithSelf.Op.type.d82 // CHECK:STDOUT: %.loc6 => constants.%.299 // CHECK:STDOUT: %impl.elem0.loc6_10.2 => constants.%impl.elem0.1c7 // CHECK:STDOUT: %specific_impl_fn.loc6_10.2 => constants.%specific_impl_fn.366 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @Function(constants.%Copy.facet.a7f) { // CHECK:STDOUT: %T.loc5_13.1 => constants.%Copy.facet.a7f // CHECK:STDOUT: %T.binding.as_type => constants.%ptr.31e // CHECK:STDOUT: %pattern_type => constants.%pattern_type.506 // CHECK:STDOUT: %.loc5_37.2 => constants.%.de8 // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete => constants.%complete_type.17a // CHECK:STDOUT: %Copy.lookup_impl_witness => constants.%Copy.impl_witness.2c7 // CHECK:STDOUT: %Copy.WithSelf.Op.type => constants.%Copy.WithSelf.Op.type.259 // CHECK:STDOUT: %.loc6 => constants.%.64b // CHECK:STDOUT: %impl.elem0.loc6_10.2 => constants.%ptr.as.Copy.impl.Op.ed9 // CHECK:STDOUT: %specific_impl_fn.loc6_10.2 => constants.%ptr.as.Copy.impl.Op.specific_fn // CHECK:STDOUT: } // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/function/generic/call_method_on_generic_facet.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/convert.carbon // TODO: Add ranges and switch to "--dump-sem-ir-ranges=only". // EXTRA-ARGS: --dump-sem-ir-ranges=if-present // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/function/generic/call_method_on_generic_facet.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/function/generic/call_method_on_generic_facet.carbon interface Generic(Scalar:! type) { fn F(); } class GenericParam {} class ImplsGeneric {} impl ImplsGeneric as Generic(GenericParam) { fn F() {} } interface Other { fn G(); } impl ImplsGeneric as Other { fn G(); } fn CallGenericMethod(T:! type, U:! Generic(T)) { U.F(); } fn G() { CallGenericMethod(GenericParam, ImplsGeneric); } // CHECK:STDOUT: --- call_method_on_generic_facet.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %type: type = facet_type [concrete] // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self] // CHECK:STDOUT: %pattern_type.98f: type = pattern_type type [concrete] // CHECK:STDOUT: %Scalar: type = symbolic_binding Scalar, 0 [symbolic] // CHECK:STDOUT: %Generic.type.835: type = generic_interface_type @Generic [concrete] // CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] // CHECK:STDOUT: %Generic.generic: %Generic.type.835 = struct_value () [concrete] // CHECK:STDOUT: %Generic.type.03dff7.1: type = facet_type <@Generic, @Generic(%Scalar)> [symbolic] // CHECK:STDOUT: %Self.15d2d5.1: %Generic.type.03dff7.1 = symbolic_binding Self, 1 [symbolic] // CHECK:STDOUT: %Generic.WithSelf.F.type.74390a.1: type = fn_type @Generic.WithSelf.F, @Generic.WithSelf(%Scalar, %Self.15d2d5.1) [symbolic] // CHECK:STDOUT: %Generic.WithSelf.F.baf95a.1: %Generic.WithSelf.F.type.74390a.1 = struct_value () [symbolic] // CHECK:STDOUT: %Generic.assoc_type.22afda.1: type = assoc_entity_type @Generic, @Generic(%Scalar) [symbolic] // CHECK:STDOUT: %assoc0.e0dc00.1: %Generic.assoc_type.22afda.1 = assoc_entity element0, @Generic.WithSelf.%Generic.WithSelf.F.decl [symbolic] // CHECK:STDOUT: %GenericParam: type = class_type @GenericParam [concrete] // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %complete_type.357: = complete_type_witness %empty_struct_type [concrete] // CHECK:STDOUT: %ImplsGeneric: type = class_type @ImplsGeneric [concrete] // CHECK:STDOUT: %Generic.type.498: type = facet_type <@Generic, @Generic(%GenericParam)> [concrete] // CHECK:STDOUT: %Generic.impl_witness: = impl_witness @ImplsGeneric.as.Generic.impl.%Generic.impl_witness_table [concrete] // CHECK:STDOUT: %Self.1f5: %Generic.type.498 = symbolic_binding Self, 1 [symbolic] // CHECK:STDOUT: %Generic.WithSelf.F.type.a23: type = fn_type @Generic.WithSelf.F, @Generic.WithSelf(%GenericParam, %Self.15d2d5.1) [symbolic] // CHECK:STDOUT: %Generic.WithSelf.F.fe0: %Generic.WithSelf.F.type.a23 = struct_value () [symbolic] // CHECK:STDOUT: %Generic.assoc_type.6dc: type = assoc_entity_type @Generic, @Generic(%GenericParam) [concrete] // CHECK:STDOUT: %assoc0.717: %Generic.assoc_type.6dc = assoc_entity element0, @Generic.WithSelf.%Generic.WithSelf.F.decl [concrete] // CHECK:STDOUT: %ImplsGeneric.as.Generic.impl.F.type: type = fn_type @ImplsGeneric.as.Generic.impl.F [concrete] // CHECK:STDOUT: %ImplsGeneric.as.Generic.impl.F: %ImplsGeneric.as.Generic.impl.F.type = struct_value () [concrete] // CHECK:STDOUT: %Generic.facet: %Generic.type.498 = facet_value %ImplsGeneric, (%Generic.impl_witness) [concrete] // CHECK:STDOUT: %Generic.WithSelf.F.type.c86: type = fn_type @Generic.WithSelf.F, @Generic.WithSelf(%GenericParam, %Generic.facet) [concrete] // CHECK:STDOUT: %Generic.WithSelf.F.1fd: %Generic.WithSelf.F.type.c86 = struct_value () [concrete] // CHECK:STDOUT: %Other.type: type = facet_type <@Other> [concrete] // CHECK:STDOUT: %Self.8c4: %Other.type = symbolic_binding Self, 0 [symbolic] // CHECK:STDOUT: %Other.WithSelf.G.type.acd: type = fn_type @Other.WithSelf.G, @Other.WithSelf(%Self.8c4) [symbolic] // CHECK:STDOUT: %Other.WithSelf.G.63d: %Other.WithSelf.G.type.acd = struct_value () [symbolic] // CHECK:STDOUT: %Other.assoc_type: type = assoc_entity_type @Other [concrete] // CHECK:STDOUT: %assoc0.205: %Other.assoc_type = assoc_entity element0, @Other.WithSelf.%Other.WithSelf.G.decl [concrete] // CHECK:STDOUT: %Other.impl_witness: = impl_witness @ImplsGeneric.as.Other.impl.%Other.impl_witness_table [concrete] // CHECK:STDOUT: %ImplsGeneric.as.Other.impl.G.type: type = fn_type @ImplsGeneric.as.Other.impl.G [concrete] // CHECK:STDOUT: %ImplsGeneric.as.Other.impl.G: %ImplsGeneric.as.Other.impl.G.type = struct_value () [concrete] // CHECK:STDOUT: %Other.facet: %Other.type = facet_value %ImplsGeneric, (%Other.impl_witness) [concrete] // CHECK:STDOUT: %Other.WithSelf.G.type.1df: type = fn_type @Other.WithSelf.G, @Other.WithSelf(%Other.facet) [concrete] // CHECK:STDOUT: %Other.WithSelf.G.13d: %Other.WithSelf.G.type.1df = struct_value () [concrete] // CHECK:STDOUT: %T: type = symbolic_binding T, 0 [symbolic] // CHECK:STDOUT: %Generic.type.03dff7.2: type = facet_type <@Generic, @Generic(%T)> [symbolic] // CHECK:STDOUT: %pattern_type.c49: type = pattern_type %Generic.type.03dff7.2 [symbolic] // CHECK:STDOUT: %U: %Generic.type.03dff7.2 = symbolic_binding U, 1 [symbolic] // CHECK:STDOUT: %CallGenericMethod.type: type = fn_type @CallGenericMethod [concrete] // CHECK:STDOUT: %CallGenericMethod: %CallGenericMethod.type = struct_value () [concrete] // CHECK:STDOUT: %Self.15d2d5.2: %Generic.type.03dff7.2 = symbolic_binding Self, 1 [symbolic] // CHECK:STDOUT: %Generic.WithSelf.F.type.74390a.2: type = fn_type @Generic.WithSelf.F, @Generic.WithSelf(%T, %Self.15d2d5.1) [symbolic] // CHECK:STDOUT: %Generic.WithSelf.F.baf95a.2: %Generic.WithSelf.F.type.74390a.2 = struct_value () [symbolic] // CHECK:STDOUT: %Generic.assoc_type.22afda.2: type = assoc_entity_type @Generic, @Generic(%T) [symbolic] // CHECK:STDOUT: %assoc0.e0dc00.2: %Generic.assoc_type.22afda.2 = assoc_entity element0, @Generic.WithSelf.%Generic.WithSelf.F.decl [symbolic] // CHECK:STDOUT: %require_complete: = require_complete_type %Generic.type.03dff7.2 [symbolic] // CHECK:STDOUT: %Generic.WithSelf.F.type.56e: type = fn_type @Generic.WithSelf.F, @Generic.WithSelf(%T, %U) [symbolic] // CHECK:STDOUT: %Generic.WithSelf.F.a99: %Generic.WithSelf.F.type.56e = struct_value () [symbolic] // CHECK:STDOUT: %U.binding.as_type: type = symbolic_binding_type U, 1, %U [symbolic] // CHECK:STDOUT: %Generic.lookup_impl_witness: = lookup_impl_witness %U, @Generic, @Generic(%T) [symbolic] // CHECK:STDOUT: %.ab3: type = fn_type_with_self_type %Generic.WithSelf.F.type.56e, %U [symbolic] // CHECK:STDOUT: %impl.elem0: %.ab3 = impl_witness_access %Generic.lookup_impl_witness, element0 [symbolic] // CHECK:STDOUT: %specific_impl_fn: = specific_impl_function %impl.elem0, @Generic.WithSelf.F(%T, %U) [symbolic] // CHECK:STDOUT: %G.type: type = fn_type @G [concrete] // CHECK:STDOUT: %G: %G.type = struct_value () [concrete] // CHECK:STDOUT: %pattern_type.cba: type = pattern_type %Generic.type.498 [concrete] // CHECK:STDOUT: %CallGenericMethod.specific_fn: = specific_function %CallGenericMethod, @CallGenericMethod(%GenericParam, %Generic.facet) [concrete] // CHECK:STDOUT: %complete_type.57a: = complete_type_witness %Generic.type.498 [concrete] // CHECK:STDOUT: %.fcf: type = fn_type_with_self_type %Generic.WithSelf.F.type.c86, %Generic.facet [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .Generic = %Generic.decl // CHECK:STDOUT: .GenericParam = %GenericParam.decl // CHECK:STDOUT: .ImplsGeneric = %ImplsGeneric.decl // CHECK:STDOUT: .Other = %Other.decl // CHECK:STDOUT: .CallGenericMethod = %CallGenericMethod.decl // CHECK:STDOUT: .G = %G.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %Generic.decl: %Generic.type.835 = interface_decl @Generic [concrete = constants.%Generic.generic] { // CHECK:STDOUT: %Scalar.patt: %pattern_type.98f = symbolic_binding_pattern Scalar, 0 [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %.loc15_28.1: type = splice_block %.loc15_28.2 [concrete = type] { // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %.loc15_28.2: type = type_literal type [concrete = type] // CHECK:STDOUT: } // CHECK:STDOUT: %Scalar.loc15_19.2: type = symbolic_binding Scalar, 0 [symbolic = %Scalar.loc15_19.1 (constants.%Scalar)] // CHECK:STDOUT: } // CHECK:STDOUT: %GenericParam.decl: type = class_decl @GenericParam [concrete = constants.%GenericParam] {} {} // CHECK:STDOUT: %ImplsGeneric.decl: type = class_decl @ImplsGeneric [concrete = constants.%ImplsGeneric] {} {} // CHECK:STDOUT: impl_decl @ImplsGeneric.as.Generic.impl [concrete] {} { // CHECK:STDOUT: %ImplsGeneric.ref: type = name_ref ImplsGeneric, file.%ImplsGeneric.decl [concrete = constants.%ImplsGeneric] // CHECK:STDOUT: %Generic.ref: %Generic.type.835 = name_ref Generic, file.%Generic.decl [concrete = constants.%Generic.generic] // CHECK:STDOUT: %GenericParam.ref: type = name_ref GenericParam, file.%GenericParam.decl [concrete = constants.%GenericParam] // CHECK:STDOUT: %Generic.type: type = facet_type <@Generic, @Generic(constants.%GenericParam)> [concrete = constants.%Generic.type.498] // CHECK:STDOUT: } // CHECK:STDOUT: %Other.decl: type = interface_decl @Other [concrete = constants.%Other.type] {} {} // CHECK:STDOUT: impl_decl @ImplsGeneric.as.Other.impl [concrete] {} { // CHECK:STDOUT: %ImplsGeneric.ref: type = name_ref ImplsGeneric, file.%ImplsGeneric.decl [concrete = constants.%ImplsGeneric] // CHECK:STDOUT: %Other.ref: type = name_ref Other, file.%Other.decl [concrete = constants.%Other.type] // CHECK:STDOUT: } // CHECK:STDOUT: %CallGenericMethod.decl: %CallGenericMethod.type = fn_decl @CallGenericMethod [concrete = constants.%CallGenericMethod] { // CHECK:STDOUT: %T.patt: %pattern_type.98f = symbolic_binding_pattern T, 0 [concrete] // CHECK:STDOUT: %U.patt: @CallGenericMethod.%pattern_type (%pattern_type.c49) = symbolic_binding_pattern U, 1 [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %.loc33_26.1: type = splice_block %.loc33_26.2 [concrete = type] { // CHECK:STDOUT: %.Self.2: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %.loc33_26.2: type = type_literal type [concrete = type] // CHECK:STDOUT: } // CHECK:STDOUT: %T.loc33_22.2: type = symbolic_binding T, 0 [symbolic = %T.loc33_22.1 (constants.%T)] // CHECK:STDOUT: %.loc33_45: type = splice_block %Generic.type.loc33_45.2 [symbolic = %Generic.type.loc33_45.1 (constants.%Generic.type.03dff7.2)] { // CHECK:STDOUT: %.Self.1: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %Generic.ref: %Generic.type.835 = name_ref Generic, file.%Generic.decl [concrete = constants.%Generic.generic] // CHECK:STDOUT: %T.ref: type = name_ref T, %T.loc33_22.2 [symbolic = %T.loc33_22.1 (constants.%T)] // CHECK:STDOUT: %Generic.type.loc33_45.2: type = facet_type <@Generic, @Generic(constants.%T)> [symbolic = %Generic.type.loc33_45.1 (constants.%Generic.type.03dff7.2)] // CHECK:STDOUT: } // CHECK:STDOUT: %U.loc33_32.2: @CallGenericMethod.%Generic.type.loc33_45.1 (%Generic.type.03dff7.2) = symbolic_binding U, 1 [symbolic = %U.loc33_32.1 (constants.%U)] // CHECK:STDOUT: } // CHECK:STDOUT: %G.decl: %G.type = fn_decl @G [concrete = constants.%G] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic interface @Generic(%Scalar.loc15_19.2: type) { // CHECK:STDOUT: %Scalar.loc15_19.1: type = symbolic_binding Scalar, 0 [symbolic = %Scalar.loc15_19.1 (constants.%Scalar)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %Generic.type: type = facet_type <@Generic, @Generic(%Scalar.loc15_19.1)> [symbolic = %Generic.type (constants.%Generic.type.03dff7.1)] // CHECK:STDOUT: %Self.loc15_34.2: @Generic.%Generic.type (%Generic.type.03dff7.1) = symbolic_binding Self, 1 [symbolic = %Self.loc15_34.2 (constants.%Self.15d2d5.1)] // CHECK:STDOUT: // CHECK:STDOUT: interface { // CHECK:STDOUT: %Self.loc15_34.1: @Generic.%Generic.type (%Generic.type.03dff7.1) = symbolic_binding Self, 1 [symbolic = %Self.loc15_34.2 (constants.%Self.15d2d5.1)] // CHECK:STDOUT: %Generic.WithSelf.decl = interface_with_self_decl @Generic [concrete] // CHECK:STDOUT: // CHECK:STDOUT: !with Self: // CHECK:STDOUT: %Generic.WithSelf.F.decl: @Generic.WithSelf.%Generic.WithSelf.F.type (%Generic.WithSelf.F.type.74390a.1) = fn_decl @Generic.WithSelf.F [symbolic = @Generic.WithSelf.%Generic.WithSelf.F (constants.%Generic.WithSelf.F.baf95a.1)] {} {} // CHECK:STDOUT: %assoc0.loc16_9.1: @Generic.WithSelf.%Generic.assoc_type (%Generic.assoc_type.22afda.1) = assoc_entity element0, %Generic.WithSelf.F.decl [symbolic = %assoc0.loc16_9.2 (constants.%assoc0.e0dc00.1)] // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = %Self.loc15_34.1 // CHECK:STDOUT: .F = @Generic.WithSelf.%assoc0.loc16_9.1 // CHECK:STDOUT: witness = (@Generic.WithSelf.%Generic.WithSelf.F.decl) // CHECK:STDOUT: // CHECK:STDOUT: !requires: // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: interface @Other { // CHECK:STDOUT: %Self: %Other.type = symbolic_binding Self, 0 [symbolic = constants.%Self.8c4] // CHECK:STDOUT: %Other.WithSelf.decl = interface_with_self_decl @Other [concrete] // CHECK:STDOUT: // CHECK:STDOUT: !with Self: // CHECK:STDOUT: %Other.WithSelf.G.decl: @Other.WithSelf.%Other.WithSelf.G.type (%Other.WithSelf.G.type.acd) = fn_decl @Other.WithSelf.G [symbolic = @Other.WithSelf.%Other.WithSelf.G (constants.%Other.WithSelf.G.63d)] {} {} // CHECK:STDOUT: %assoc0: %Other.assoc_type = assoc_entity element0, %Other.WithSelf.G.decl [concrete = constants.%assoc0.205] // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = %Self // CHECK:STDOUT: .G = @Other.WithSelf.%assoc0 // CHECK:STDOUT: witness = (@Other.WithSelf.%Other.WithSelf.G.decl) // CHECK:STDOUT: // CHECK:STDOUT: !requires: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: impl @ImplsGeneric.as.Generic.impl: %ImplsGeneric.ref as %Generic.type { // CHECK:STDOUT: %ImplsGeneric.as.Generic.impl.F.decl: %ImplsGeneric.as.Generic.impl.F.type = fn_decl @ImplsGeneric.as.Generic.impl.F [concrete = constants.%ImplsGeneric.as.Generic.impl.F] {} {} // CHECK:STDOUT: %Generic.impl_witness_table = impl_witness_table (%ImplsGeneric.as.Generic.impl.F.decl), @ImplsGeneric.as.Generic.impl [concrete] // CHECK:STDOUT: %Generic.impl_witness: = impl_witness %Generic.impl_witness_table [concrete = constants.%Generic.impl_witness] // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .F = %ImplsGeneric.as.Generic.impl.F.decl // CHECK:STDOUT: witness = %Generic.impl_witness // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: impl @ImplsGeneric.as.Other.impl: %ImplsGeneric.ref as %Other.ref { // CHECK:STDOUT: %ImplsGeneric.as.Other.impl.G.decl: %ImplsGeneric.as.Other.impl.G.type = fn_decl @ImplsGeneric.as.Other.impl.G [concrete = constants.%ImplsGeneric.as.Other.impl.G] {} {} // CHECK:STDOUT: %Other.impl_witness_table = impl_witness_table (%ImplsGeneric.as.Other.impl.G.decl), @ImplsGeneric.as.Other.impl [concrete] // CHECK:STDOUT: %Other.impl_witness: = impl_witness %Other.impl_witness_table [concrete = constants.%Other.impl_witness] // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .G = %ImplsGeneric.as.Other.impl.G.decl // CHECK:STDOUT: witness = %Other.impl_witness // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @GenericParam { // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type.357] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%GenericParam // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @ImplsGeneric { // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type.357] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%ImplsGeneric // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic fn @Generic.WithSelf.F(@Generic.%Scalar.loc15_19.2: type, @Generic.%Self.loc15_34.1: @Generic.%Generic.type (%Generic.type.03dff7.1)) { // CHECK:STDOUT: fn(); // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @ImplsGeneric.as.Generic.impl.F() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic fn @Other.WithSelf.G(@Other.%Self: %Other.type) { // CHECK:STDOUT: fn(); // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @ImplsGeneric.as.Other.impl.G(); // CHECK:STDOUT: // CHECK:STDOUT: generic fn @CallGenericMethod(%T.loc33_22.2: type, %U.loc33_32.2: @CallGenericMethod.%Generic.type.loc33_45.1 (%Generic.type.03dff7.2)) { // CHECK:STDOUT: %T.loc33_22.1: type = symbolic_binding T, 0 [symbolic = %T.loc33_22.1 (constants.%T)] // CHECK:STDOUT: %Generic.type.loc33_45.1: type = facet_type <@Generic, @Generic(%T.loc33_22.1)> [symbolic = %Generic.type.loc33_45.1 (constants.%Generic.type.03dff7.2)] // CHECK:STDOUT: %U.loc33_32.1: @CallGenericMethod.%Generic.type.loc33_45.1 (%Generic.type.03dff7.2) = symbolic_binding U, 1 [symbolic = %U.loc33_32.1 (constants.%U)] // CHECK:STDOUT: %pattern_type: type = pattern_type %Generic.type.loc33_45.1 [symbolic = %pattern_type (constants.%pattern_type.c49)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete: = require_complete_type %Generic.type.loc33_45.1 [symbolic = %require_complete (constants.%require_complete)] // CHECK:STDOUT: %U.binding.as_type: type = symbolic_binding_type U, 1, %U.loc33_32.1 [symbolic = %U.binding.as_type (constants.%U.binding.as_type)] // CHECK:STDOUT: %Generic.assoc_type: type = assoc_entity_type @Generic, @Generic(%T.loc33_22.1) [symbolic = %Generic.assoc_type (constants.%Generic.assoc_type.22afda.2)] // CHECK:STDOUT: %assoc0: @CallGenericMethod.%Generic.assoc_type (%Generic.assoc_type.22afda.2) = assoc_entity element0, @Generic.WithSelf.%Generic.WithSelf.F.decl [symbolic = %assoc0 (constants.%assoc0.e0dc00.2)] // CHECK:STDOUT: %Generic.lookup_impl_witness: = lookup_impl_witness %U.loc33_32.1, @Generic, @Generic(%T.loc33_22.1) [symbolic = %Generic.lookup_impl_witness (constants.%Generic.lookup_impl_witness)] // CHECK:STDOUT: %Generic.WithSelf.F.type: type = fn_type @Generic.WithSelf.F, @Generic.WithSelf(%T.loc33_22.1, %U.loc33_32.1) [symbolic = %Generic.WithSelf.F.type (constants.%Generic.WithSelf.F.type.56e)] // CHECK:STDOUT: %.loc34_4.3: type = fn_type_with_self_type %Generic.WithSelf.F.type, %U.loc33_32.1 [symbolic = %.loc34_4.3 (constants.%.ab3)] // CHECK:STDOUT: %impl.elem0.loc34_4.2: @CallGenericMethod.%.loc34_4.3 (%.ab3) = impl_witness_access %Generic.lookup_impl_witness, element0 [symbolic = %impl.elem0.loc34_4.2 (constants.%impl.elem0)] // CHECK:STDOUT: %specific_impl_fn.loc34_4.2: = specific_impl_function %impl.elem0.loc34_4.2, @Generic.WithSelf.F(%T.loc33_22.1, %U.loc33_32.1) [symbolic = %specific_impl_fn.loc34_4.2 (constants.%specific_impl_fn)] // CHECK:STDOUT: // CHECK:STDOUT: fn() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %U.ref: @CallGenericMethod.%Generic.type.loc33_45.1 (%Generic.type.03dff7.2) = name_ref U, %U.loc33_32.2 [symbolic = %U.loc33_32.1 (constants.%U)] // CHECK:STDOUT: %U.as_type: type = facet_access_type %U.ref [symbolic = %U.binding.as_type (constants.%U.binding.as_type)] // CHECK:STDOUT: %.loc34_4.1: type = converted %U.ref, %U.as_type [symbolic = %U.binding.as_type (constants.%U.binding.as_type)] // CHECK:STDOUT: %.loc34_4.2: @CallGenericMethod.%Generic.assoc_type (%Generic.assoc_type.22afda.2) = specific_constant @Generic.WithSelf.%assoc0.loc16_9.1, @Generic.WithSelf(constants.%T, constants.%U) [symbolic = %assoc0 (constants.%assoc0.e0dc00.2)] // CHECK:STDOUT: %F.ref: @CallGenericMethod.%Generic.assoc_type (%Generic.assoc_type.22afda.2) = name_ref F, %.loc34_4.2 [symbolic = %assoc0 (constants.%assoc0.e0dc00.2)] // CHECK:STDOUT: %impl.elem0.loc34_4.1: @CallGenericMethod.%.loc34_4.3 (%.ab3) = impl_witness_access constants.%Generic.lookup_impl_witness, element0 [symbolic = %impl.elem0.loc34_4.2 (constants.%impl.elem0)] // CHECK:STDOUT: %specific_impl_fn.loc34_4.1: = specific_impl_function %impl.elem0.loc34_4.1, @Generic.WithSelf.F(constants.%T, constants.%U) [symbolic = %specific_impl_fn.loc34_4.2 (constants.%specific_impl_fn)] // CHECK:STDOUT: %Generic.WithSelf.F.call: init %empty_tuple.type = call %specific_impl_fn.loc34_4.1() // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @G() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %CallGenericMethod.ref: %CallGenericMethod.type = name_ref CallGenericMethod, file.%CallGenericMethod.decl [concrete = constants.%CallGenericMethod] // CHECK:STDOUT: %GenericParam.ref: type = name_ref GenericParam, file.%GenericParam.decl [concrete = constants.%GenericParam] // CHECK:STDOUT: %ImplsGeneric.ref: type = name_ref ImplsGeneric, file.%ImplsGeneric.decl [concrete = constants.%ImplsGeneric] // CHECK:STDOUT: %Generic.facet: %Generic.type.498 = facet_value constants.%ImplsGeneric, (constants.%Generic.impl_witness) [concrete = constants.%Generic.facet] // CHECK:STDOUT: %.loc38: %Generic.type.498 = converted constants.%ImplsGeneric, %Generic.facet [concrete = constants.%Generic.facet] // CHECK:STDOUT: %CallGenericMethod.specific_fn: = specific_function %CallGenericMethod.ref, @CallGenericMethod(constants.%GenericParam, constants.%Generic.facet) [concrete = constants.%CallGenericMethod.specific_fn] // CHECK:STDOUT: %CallGenericMethod.call: init %empty_tuple.type = call %CallGenericMethod.specific_fn() // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @Generic(constants.%Scalar) { // CHECK:STDOUT: %Scalar.loc15_19.1 => constants.%Scalar // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @Generic.WithSelf(constants.%Scalar, constants.%Self.15d2d5.1) {} // CHECK:STDOUT: // CHECK:STDOUT: specific @Generic.WithSelf.F(constants.%Scalar, constants.%Self.15d2d5.1) {} // CHECK:STDOUT: // CHECK:STDOUT: specific @Generic(constants.%GenericParam) { // CHECK:STDOUT: %Scalar.loc15_19.1 => constants.%GenericParam // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %Generic.type => constants.%Generic.type.498 // CHECK:STDOUT: %Self.loc15_34.2 => constants.%Self.1f5 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @Generic.WithSelf(constants.%GenericParam, constants.%Self.15d2d5.1) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: %Scalar => constants.%GenericParam // CHECK:STDOUT: %Generic.type => constants.%Generic.type.498 // CHECK:STDOUT: %Self => constants.%Self.15d2d5.1 // CHECK:STDOUT: %Generic.WithSelf.F.type => constants.%Generic.WithSelf.F.type.a23 // CHECK:STDOUT: %Generic.WithSelf.F => constants.%Generic.WithSelf.F.fe0 // CHECK:STDOUT: %Generic.assoc_type => constants.%Generic.assoc_type.6dc // CHECK:STDOUT: %assoc0.loc16_9.2 => constants.%assoc0.717 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @Generic.WithSelf(constants.%GenericParam, constants.%Generic.facet) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: %Scalar => constants.%GenericParam // CHECK:STDOUT: %Generic.type => constants.%Generic.type.498 // CHECK:STDOUT: %Self => constants.%Generic.facet // CHECK:STDOUT: %Generic.WithSelf.F.type => constants.%Generic.WithSelf.F.type.c86 // CHECK:STDOUT: %Generic.WithSelf.F => constants.%Generic.WithSelf.F.1fd // CHECK:STDOUT: %Generic.assoc_type => constants.%Generic.assoc_type.6dc // CHECK:STDOUT: %assoc0.loc16_9.2 => constants.%assoc0.717 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @Generic.WithSelf.F(constants.%GenericParam, constants.%Generic.facet) {} // CHECK:STDOUT: // CHECK:STDOUT: specific @Other.WithSelf(constants.%Self.8c4) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: %Self => constants.%Self.8c4 // CHECK:STDOUT: %Other.WithSelf.G.type => constants.%Other.WithSelf.G.type.acd // CHECK:STDOUT: %Other.WithSelf.G => constants.%Other.WithSelf.G.63d // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @Other.WithSelf.G(constants.%Self.8c4) {} // CHECK:STDOUT: // CHECK:STDOUT: specific @Other.WithSelf(constants.%Other.facet) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: %Self => constants.%Other.facet // CHECK:STDOUT: %Other.WithSelf.G.type => constants.%Other.WithSelf.G.type.1df // CHECK:STDOUT: %Other.WithSelf.G => constants.%Other.WithSelf.G.13d // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @Other.WithSelf.G(constants.%Other.facet) {} // CHECK:STDOUT: // CHECK:STDOUT: specific @Generic(constants.%T) { // CHECK:STDOUT: %Scalar.loc15_19.1 => constants.%T // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %Generic.type => constants.%Generic.type.03dff7.2 // CHECK:STDOUT: %Self.loc15_34.2 => constants.%Self.15d2d5.2 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @CallGenericMethod(constants.%T, constants.%U) { // CHECK:STDOUT: %T.loc33_22.1 => constants.%T // CHECK:STDOUT: %Generic.type.loc33_45.1 => constants.%Generic.type.03dff7.2 // CHECK:STDOUT: %U.loc33_32.1 => constants.%U // CHECK:STDOUT: %pattern_type => constants.%pattern_type.c49 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @Generic.WithSelf(constants.%T, constants.%Self.15d2d5.1) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: %Scalar => constants.%T // CHECK:STDOUT: %Generic.type => constants.%Generic.type.03dff7.2 // CHECK:STDOUT: %Self => constants.%Self.15d2d5.1 // CHECK:STDOUT: %Generic.WithSelf.F.type => constants.%Generic.WithSelf.F.type.74390a.2 // CHECK:STDOUT: %Generic.WithSelf.F => constants.%Generic.WithSelf.F.baf95a.2 // CHECK:STDOUT: %Generic.assoc_type => constants.%Generic.assoc_type.22afda.2 // CHECK:STDOUT: %assoc0.loc16_9.2 => constants.%assoc0.e0dc00.2 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @Generic.WithSelf(constants.%T, constants.%U) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: %Scalar => constants.%T // CHECK:STDOUT: %Generic.type => constants.%Generic.type.03dff7.2 // CHECK:STDOUT: %Self => constants.%U // CHECK:STDOUT: %Generic.WithSelf.F.type => constants.%Generic.WithSelf.F.type.56e // CHECK:STDOUT: %Generic.WithSelf.F => constants.%Generic.WithSelf.F.a99 // CHECK:STDOUT: %Generic.assoc_type => constants.%Generic.assoc_type.22afda.2 // CHECK:STDOUT: %assoc0.loc16_9.2 => constants.%assoc0.e0dc00.2 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @Generic.WithSelf.F(constants.%T, constants.%U) {} // CHECK:STDOUT: // CHECK:STDOUT: specific @CallGenericMethod(constants.%GenericParam, constants.%Generic.facet) { // CHECK:STDOUT: %T.loc33_22.1 => constants.%GenericParam // CHECK:STDOUT: %Generic.type.loc33_45.1 => constants.%Generic.type.498 // CHECK:STDOUT: %U.loc33_32.1 => constants.%Generic.facet // CHECK:STDOUT: %pattern_type => constants.%pattern_type.cba // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete => constants.%complete_type.57a // CHECK:STDOUT: %U.binding.as_type => constants.%ImplsGeneric // CHECK:STDOUT: %Generic.assoc_type => constants.%Generic.assoc_type.6dc // CHECK:STDOUT: %assoc0 => constants.%assoc0.717 // CHECK:STDOUT: %Generic.lookup_impl_witness => constants.%Generic.impl_witness // CHECK:STDOUT: %Generic.WithSelf.F.type => constants.%Generic.WithSelf.F.type.c86 // CHECK:STDOUT: %.loc34_4.3 => constants.%.fcf // CHECK:STDOUT: %impl.elem0.loc34_4.2 => constants.%ImplsGeneric.as.Generic.impl.F // CHECK:STDOUT: %specific_impl_fn.loc34_4.2 => constants.%ImplsGeneric.as.Generic.impl.F // CHECK:STDOUT: } // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/function/generic/deduce.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/int.carbon // TODO: Add ranges and switch to "--dump-sem-ir-ranges=only". // EXTRA-ARGS: --dump-sem-ir-ranges=if-present // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/function/generic/deduce.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/function/generic/deduce.carbon // --- deduce_explicit.carbon library "[[@TEST_NAME]]"; fn ExplicitGenericParam(T:! type) -> T* { return ExplicitGenericParam(T); } fn CallExplicitGenericParam() -> i32* { return ExplicitGenericParam(i32); } fn CallExplicitGenericParamWithGenericArg(T:! type) -> {.a: T}* { return ExplicitGenericParam({.a: T}); } // --- fail_deduce_explicit_non_constant.carbon library "[[@TEST_NAME]]"; fn ExplicitGenericParam(T:! type) -> T* { return ExplicitGenericParam(T); } fn CallExplicitGenericParamConst(T:! type) { ExplicitGenericParam(T); } fn CallExplicitGenericParamNonConst(T: type) { // CHECK:STDERR: fail_deduce_explicit_non_constant.carbon:[[@LINE+7]]:3: error: argument for generic parameter is not a compile-time constant [CompTimeArgumentNotConstant] // CHECK:STDERR: ExplicitGenericParam(T); // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: fail_deduce_explicit_non_constant.carbon:[[@LINE-10]]:25: note: initializing generic parameter `T` declared here [InitializingGenericParam] // CHECK:STDERR: fn ExplicitGenericParam(T:! type) -> T* { return ExplicitGenericParam(T); } // CHECK:STDERR: ^ // CHECK:STDERR: ExplicitGenericParam(T); } // --- explicit_vs_deduced.carbon library "[[@TEST_NAME]]"; class A {} fn ExplicitAndAlsoDeduced(T:! type, x: T) -> T* { return ExplicitAndAlsoDeduced(T, x); } fn CallExplicitAndAlsoDeduced() -> A* { return ExplicitAndAlsoDeduced(A, {}); } // --- deduce_implicit.carbon library "[[@TEST_NAME]]"; fn ImplicitGenericParam[T:! type](x: T) -> T* { return ImplicitGenericParam(x); } fn CallImplicitGenericParam(n: i32) -> i32* { return ImplicitGenericParam(n); } // --- deduce_nested_tuple.carbon library "[[@TEST_NAME]]"; fn TupleParam[T:! type](unused x: (T, i32)) {} fn CallTupleParam() { TupleParam((1, 2)); } // --- deduce_nested_struct.carbon library "[[@TEST_NAME]]"; fn StructParam[T:! type](unused x: {.a: T, .b: i32}) {} fn CallStructParam() { StructParam({.a = 1, .b = 2}); } // --- fail_deduce_bigger_struct.carbon library "[[@TEST_NAME]]"; fn BigStructParam[T:! type](unused x: {.c: T, .d: i32, .e: i32}) {} fn CallBigStructParam() { // CHECK:STDERR: fail_deduce_bigger_struct.carbon:[[@LINE+7]]:3: error: cannot deduce value for generic parameter `T` [DeductionIncomplete] // CHECK:STDERR: BigStructParam({.c = 3, .d = 4}); // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: fail_deduce_bigger_struct.carbon:[[@LINE-6]]:1: note: while deducing parameters of generic declared here [DeductionGenericHere] // CHECK:STDERR: fn BigStructParam[T:! type](unused x: {.c: T, .d: i32, .e: i32}) {} // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: BigStructParam({.c = 3, .d = 4}); } // --- fail_deduce_smaller_struct.carbon library "[[@TEST_NAME]]"; fn SmallStructParam[T:! type](unused x: {.f: T, .g: i32}) {} fn CallSmallStructParam() { // CHECK:STDERR: fail_deduce_smaller_struct.carbon:[[@LINE+7]]:3: error: cannot deduce value for generic parameter `T` [DeductionIncomplete] // CHECK:STDERR: SmallStructParam({.f = 5, .g = 6, .h = 7}); // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: fail_deduce_smaller_struct.carbon:[[@LINE-6]]:1: note: while deducing parameters of generic declared here [DeductionGenericHere] // CHECK:STDERR: fn SmallStructParam[T:! type](unused x: {.f: T, .g: i32}) {} // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: SmallStructParam({.f = 5, .g = 6, .h = 7}); } // --- fail_deduce_struct_wrong_name.carbon library "[[@TEST_NAME]]"; fn WrongNameStructParam[T:! type](unused x: {.i: T, .different: i32}) {} fn CallWrongNameStructParam() { // CHECK:STDERR: fail_deduce_struct_wrong_name.carbon:[[@LINE+7]]:3: error: cannot deduce value for generic parameter `T` [DeductionIncomplete] // CHECK:STDERR: WrongNameStructParam({.i = 8, .j = 9}); // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: fail_deduce_struct_wrong_name.carbon:[[@LINE-6]]:1: note: while deducing parameters of generic declared here [DeductionGenericHere] // CHECK:STDERR: fn WrongNameStructParam[T:! type](unused x: {.i: T, .different: i32}) {} // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: WrongNameStructParam({.i = 8, .j = 9}); } // --- fail_todo_deduce_struct_wrong_order.carbon library "[[@TEST_NAME]]"; fn WrongOrderStructParam[T:! type](unused x: {.first: T, .second: i32}) {} fn CallWrongOrderStructParam() { // CHECK:STDERR: fail_todo_deduce_struct_wrong_order.carbon:[[@LINE+7]]:3: error: cannot deduce value for generic parameter `T` [DeductionIncomplete] // CHECK:STDERR: WrongOrderStructParam({.second = 11, .first = 10}); // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: fail_todo_deduce_struct_wrong_order.carbon:[[@LINE-6]]:1: note: while deducing parameters of generic declared here [DeductionGenericHere] // CHECK:STDERR: fn WrongOrderStructParam[T:! type](unused x: {.first: T, .second: i32}) {} // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: WrongOrderStructParam({.second = 11, .first = 10}); } // --- fail_deduce_incomplete.carbon library "[[@TEST_NAME]]"; // TODO: It would be nice to diagnose this at its point of declaration, because // U is not deducible. fn ImplicitNotDeducible[T:! type, U:! type](x: T) -> U; fn CallImplicitNotDeducible() { // CHECK:STDERR: fail_deduce_incomplete.carbon:[[@LINE+7]]:3: error: cannot deduce value for generic parameter `U` [DeductionIncomplete] // CHECK:STDERR: ImplicitNotDeducible(42); // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: fail_deduce_incomplete.carbon:[[@LINE-6]]:1: note: while deducing parameters of generic declared here [DeductionGenericHere] // CHECK:STDERR: fn ImplicitNotDeducible[T:! type, U:! type](x: T) -> U; // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: ImplicitNotDeducible(42); } // --- fail_deduce_inconsistent.carbon library "[[@TEST_NAME]]"; fn ImplicitNotDeducible[T:! type](x: T, y: T) -> T; fn CallImplicitNotDeducible() { // CHECK:STDERR: fail_deduce_inconsistent.carbon:[[@LINE+7]]:3: error: inconsistent deductions for value of generic parameter `T` [DeductionInconsistent] // CHECK:STDERR: ImplicitNotDeducible(42, {.x = 12}); // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: fail_deduce_inconsistent.carbon:[[@LINE-6]]:1: note: while deducing parameters of generic declared here [DeductionGenericHere] // CHECK:STDERR: fn ImplicitNotDeducible[T:! type](x: T, y: T) -> T; // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: ImplicitNotDeducible(42, {.x = 12}); } // --- deduce_nested_generic_class.carbon library "[[@TEST_NAME]]"; interface Z {} class EE {} impl EE as Z {} class DD(E:! type) {} impl forall [E:! type] DD(E) as Z {} class CC(D:! Z) {} impl forall [E:! type] CC(DD(E)) as Z {} fn F() { CC(DD(EE)) as Z; } // CHECK:STDOUT: --- deduce_explicit.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %type: type = facet_type [concrete] // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self] // CHECK:STDOUT: %pattern_type.98f: type = pattern_type type [concrete] // CHECK:STDOUT: %T: type = symbolic_binding T, 0 [symbolic] // CHECK:STDOUT: %ptr.e8f: type = ptr_type %T [symbolic] // CHECK:STDOUT: %.cb6: Core.Form = init_form %ptr.e8f [symbolic] // CHECK:STDOUT: %pattern_type.4f4: type = pattern_type %ptr.e8f [symbolic] // CHECK:STDOUT: %ExplicitGenericParam.type: type = fn_type @ExplicitGenericParam [concrete] // CHECK:STDOUT: %ExplicitGenericParam: %ExplicitGenericParam.type = struct_value () [concrete] // CHECK:STDOUT: %require_complete.ef1: = require_complete_type %ptr.e8f [symbolic] // CHECK:STDOUT: %ExplicitGenericParam.specific_fn.409: = specific_function %ExplicitGenericParam, @ExplicitGenericParam(%T) [symbolic] // CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [concrete] // CHECK:STDOUT: %Int.type: type = generic_class_type @Int [concrete] // CHECK:STDOUT: %Int.generic: %Int.type = struct_value () [concrete] // CHECK:STDOUT: %i32: type = class_type @Int, @Int(%int_32) [concrete] // CHECK:STDOUT: %ptr.235: type = ptr_type %i32 [concrete] // CHECK:STDOUT: %.605: Core.Form = init_form %ptr.235 [concrete] // CHECK:STDOUT: %pattern_type.fe8: type = pattern_type %ptr.235 [concrete] // CHECK:STDOUT: %CallExplicitGenericParam.type: type = fn_type @CallExplicitGenericParam [concrete] // CHECK:STDOUT: %CallExplicitGenericParam: %CallExplicitGenericParam.type = struct_value () [concrete] // CHECK:STDOUT: %ExplicitGenericParam.specific_fn.3d2: = specific_function %ExplicitGenericParam, @ExplicitGenericParam(%i32) [concrete] // CHECK:STDOUT: %struct_type.a: type = struct_type {.a: %T} [symbolic] // CHECK:STDOUT: %ptr.88d: type = ptr_type %struct_type.a [symbolic] // CHECK:STDOUT: %.edd: Core.Form = init_form %ptr.88d [symbolic] // CHECK:STDOUT: %pattern_type.1cb: type = pattern_type %ptr.88d [symbolic] // CHECK:STDOUT: %CallExplicitGenericParamWithGenericArg.type: type = fn_type @CallExplicitGenericParamWithGenericArg [concrete] // CHECK:STDOUT: %CallExplicitGenericParamWithGenericArg: %CallExplicitGenericParamWithGenericArg.type = struct_value () [concrete] // CHECK:STDOUT: %require_complete.61f: = require_complete_type %ptr.88d [symbolic] // CHECK:STDOUT: %ExplicitGenericParam.specific_fn.55c: = specific_function %ExplicitGenericParam, @ExplicitGenericParam(%struct_type.a) [symbolic] // CHECK:STDOUT: %complete_type.3d0: = complete_type_witness %ptr.235 [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: .Int = %Core.Int // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: %Core.Int: %Int.type = import_ref Core//prelude/parts/int, Int, loaded [concrete = constants.%Int.generic] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .ExplicitGenericParam = %ExplicitGenericParam.decl // CHECK:STDOUT: .CallExplicitGenericParam = %CallExplicitGenericParam.decl // CHECK:STDOUT: .CallExplicitGenericParamWithGenericArg = %CallExplicitGenericParamWithGenericArg.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %ExplicitGenericParam.decl: %ExplicitGenericParam.type = fn_decl @ExplicitGenericParam [concrete = constants.%ExplicitGenericParam] { // CHECK:STDOUT: %T.patt: %pattern_type.98f = symbolic_binding_pattern T, 0 [concrete] // CHECK:STDOUT: %return.patt: @ExplicitGenericParam.%pattern_type (%pattern_type.4f4) = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: @ExplicitGenericParam.%pattern_type (%pattern_type.4f4) = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %T.ref.loc4_38: type = name_ref T, %T.loc4_25.2 [symbolic = %T.loc4_25.1 (constants.%T)] // CHECK:STDOUT: %ptr.loc4_39.2: type = ptr_type %T.ref.loc4_38 [symbolic = %ptr.loc4_39.1 (constants.%ptr.e8f)] // CHECK:STDOUT: %.loc4_39.2: Core.Form = init_form %ptr.loc4_39.2 [symbolic = %.loc4_39.1 (constants.%.cb6)] // CHECK:STDOUT: %.loc4_29.1: type = splice_block %.loc4_29.2 [concrete = type] { // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %.loc4_29.2: type = type_literal type [concrete = type] // CHECK:STDOUT: } // CHECK:STDOUT: %T.loc4_25.2: type = symbolic_binding T, 0 [symbolic = %T.loc4_25.1 (constants.%T)] // CHECK:STDOUT: %return.param: ref @ExplicitGenericParam.%ptr.loc4_39.1 (%ptr.e8f) = out_param call_param0 // CHECK:STDOUT: %return: ref @ExplicitGenericParam.%ptr.loc4_39.1 (%ptr.e8f) = return_slot %return.param // CHECK:STDOUT: } // CHECK:STDOUT: %CallExplicitGenericParam.decl: %CallExplicitGenericParam.type = fn_decl @CallExplicitGenericParam [concrete = constants.%CallExplicitGenericParam] { // CHECK:STDOUT: %return.patt: %pattern_type.fe8 = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: %pattern_type.fe8 = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %i32.loc6: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %ptr: type = ptr_type %i32.loc6 [concrete = constants.%ptr.235] // CHECK:STDOUT: %.loc6: Core.Form = init_form %ptr [concrete = constants.%.605] // CHECK:STDOUT: %return.param: ref %ptr.235 = out_param call_param0 // CHECK:STDOUT: %return: ref %ptr.235 = return_slot %return.param // CHECK:STDOUT: } // CHECK:STDOUT: %CallExplicitGenericParamWithGenericArg.decl: %CallExplicitGenericParamWithGenericArg.type = fn_decl @CallExplicitGenericParamWithGenericArg [concrete = constants.%CallExplicitGenericParamWithGenericArg] { // CHECK:STDOUT: %T.patt: %pattern_type.98f = symbolic_binding_pattern T, 0 [concrete] // CHECK:STDOUT: %return.patt: @CallExplicitGenericParamWithGenericArg.%pattern_type (%pattern_type.1cb) = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: @CallExplicitGenericParamWithGenericArg.%pattern_type (%pattern_type.1cb) = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %T.ref.loc10: type = name_ref T, %T.loc10_43.2 [symbolic = %T.loc10_43.1 (constants.%T)] // CHECK:STDOUT: %struct_type.a.loc10_62.2: type = struct_type {.a: @CallExplicitGenericParamWithGenericArg.%T.loc10_43.1 (%T)} [symbolic = %struct_type.a.loc10_62.1 (constants.%struct_type.a)] // CHECK:STDOUT: %ptr.loc10_63.2: type = ptr_type %struct_type.a.loc10_62.2 [symbolic = %ptr.loc10_63.1 (constants.%ptr.88d)] // CHECK:STDOUT: %.loc10_63.2: Core.Form = init_form %ptr.loc10_63.2 [symbolic = %.loc10_63.1 (constants.%.edd)] // CHECK:STDOUT: %.loc10_47.1: type = splice_block %.loc10_47.2 [concrete = type] { // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %.loc10_47.2: type = type_literal type [concrete = type] // CHECK:STDOUT: } // CHECK:STDOUT: %T.loc10_43.2: type = symbolic_binding T, 0 [symbolic = %T.loc10_43.1 (constants.%T)] // CHECK:STDOUT: %return.param: ref @CallExplicitGenericParamWithGenericArg.%ptr.loc10_63.1 (%ptr.88d) = out_param call_param0 // CHECK:STDOUT: %return: ref @CallExplicitGenericParamWithGenericArg.%ptr.loc10_63.1 (%ptr.88d) = return_slot %return.param // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic fn @ExplicitGenericParam(%T.loc4_25.2: type) { // CHECK:STDOUT: %T.loc4_25.1: type = symbolic_binding T, 0 [symbolic = %T.loc4_25.1 (constants.%T)] // CHECK:STDOUT: %ptr.loc4_39.1: type = ptr_type %T.loc4_25.1 [symbolic = %ptr.loc4_39.1 (constants.%ptr.e8f)] // CHECK:STDOUT: %.loc4_39.1: Core.Form = init_form %ptr.loc4_39.1 [symbolic = %.loc4_39.1 (constants.%.cb6)] // CHECK:STDOUT: %pattern_type: type = pattern_type %ptr.loc4_39.1 [symbolic = %pattern_type (constants.%pattern_type.4f4)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete: = require_complete_type %ptr.loc4_39.1 [symbolic = %require_complete (constants.%require_complete.ef1)] // CHECK:STDOUT: %ExplicitGenericParam.specific_fn.loc4_50.2: = specific_function constants.%ExplicitGenericParam, @ExplicitGenericParam(%T.loc4_25.1) [symbolic = %ExplicitGenericParam.specific_fn.loc4_50.2 (constants.%ExplicitGenericParam.specific_fn.409)] // CHECK:STDOUT: // CHECK:STDOUT: fn() -> out %return.param: @ExplicitGenericParam.%ptr.loc4_39.1 (%ptr.e8f) { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %ExplicitGenericParam.ref: %ExplicitGenericParam.type = name_ref ExplicitGenericParam, file.%ExplicitGenericParam.decl [concrete = constants.%ExplicitGenericParam] // CHECK:STDOUT: %T.ref.loc4_71: type = name_ref T, %T.loc4_25.2 [symbolic = %T.loc4_25.1 (constants.%T)] // CHECK:STDOUT: %ExplicitGenericParam.specific_fn.loc4_50.1: = specific_function %ExplicitGenericParam.ref, @ExplicitGenericParam(constants.%T) [symbolic = %ExplicitGenericParam.specific_fn.loc4_50.2 (constants.%ExplicitGenericParam.specific_fn.409)] // CHECK:STDOUT: %ExplicitGenericParam.call: init @ExplicitGenericParam.%ptr.loc4_39.1 (%ptr.e8f) = call %ExplicitGenericParam.specific_fn.loc4_50.1() // CHECK:STDOUT: return %ExplicitGenericParam.call // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @CallExplicitGenericParam() -> out %return.param: %ptr.235 { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %ExplicitGenericParam.ref: %ExplicitGenericParam.type = name_ref ExplicitGenericParam, file.%ExplicitGenericParam.decl [concrete = constants.%ExplicitGenericParam] // CHECK:STDOUT: %i32.loc7: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %ExplicitGenericParam.specific_fn: = specific_function %ExplicitGenericParam.ref, @ExplicitGenericParam(constants.%i32) [concrete = constants.%ExplicitGenericParam.specific_fn.3d2] // CHECK:STDOUT: %ExplicitGenericParam.call: init %ptr.235 = call %ExplicitGenericParam.specific_fn() // CHECK:STDOUT: return %ExplicitGenericParam.call // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic fn @CallExplicitGenericParamWithGenericArg(%T.loc10_43.2: type) { // CHECK:STDOUT: %T.loc10_43.1: type = symbolic_binding T, 0 [symbolic = %T.loc10_43.1 (constants.%T)] // CHECK:STDOUT: %struct_type.a.loc10_62.1: type = struct_type {.a: @CallExplicitGenericParamWithGenericArg.%T.loc10_43.1 (%T)} [symbolic = %struct_type.a.loc10_62.1 (constants.%struct_type.a)] // CHECK:STDOUT: %ptr.loc10_63.1: type = ptr_type %struct_type.a.loc10_62.1 [symbolic = %ptr.loc10_63.1 (constants.%ptr.88d)] // CHECK:STDOUT: %.loc10_63.1: Core.Form = init_form %ptr.loc10_63.1 [symbolic = %.loc10_63.1 (constants.%.edd)] // CHECK:STDOUT: %pattern_type: type = pattern_type %ptr.loc10_63.1 [symbolic = %pattern_type (constants.%pattern_type.1cb)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete: = require_complete_type %ptr.loc10_63.1 [symbolic = %require_complete (constants.%require_complete.61f)] // CHECK:STDOUT: %ExplicitGenericParam.specific_fn.loc11_10.2: = specific_function constants.%ExplicitGenericParam, @ExplicitGenericParam(%struct_type.a.loc10_62.1) [symbolic = %ExplicitGenericParam.specific_fn.loc11_10.2 (constants.%ExplicitGenericParam.specific_fn.55c)] // CHECK:STDOUT: // CHECK:STDOUT: fn() -> out %return.param: @CallExplicitGenericParamWithGenericArg.%ptr.loc10_63.1 (%ptr.88d) { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %ExplicitGenericParam.ref: %ExplicitGenericParam.type = name_ref ExplicitGenericParam, file.%ExplicitGenericParam.decl [concrete = constants.%ExplicitGenericParam] // CHECK:STDOUT: %T.ref.loc11: type = name_ref T, %T.loc10_43.2 [symbolic = %T.loc10_43.1 (constants.%T)] // CHECK:STDOUT: %struct_type.a.loc11: type = struct_type {.a: @CallExplicitGenericParamWithGenericArg.%T.loc10_43.1 (%T)} [symbolic = %struct_type.a.loc10_62.1 (constants.%struct_type.a)] // CHECK:STDOUT: %ExplicitGenericParam.specific_fn.loc11_10.1: = specific_function %ExplicitGenericParam.ref, @ExplicitGenericParam(constants.%struct_type.a) [symbolic = %ExplicitGenericParam.specific_fn.loc11_10.2 (constants.%ExplicitGenericParam.specific_fn.55c)] // CHECK:STDOUT: %ExplicitGenericParam.call: init @CallExplicitGenericParamWithGenericArg.%ptr.loc10_63.1 (%ptr.88d) = call %ExplicitGenericParam.specific_fn.loc11_10.1() // CHECK:STDOUT: return %ExplicitGenericParam.call // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @ExplicitGenericParam(constants.%T) { // CHECK:STDOUT: %T.loc4_25.1 => constants.%T // CHECK:STDOUT: %ptr.loc4_39.1 => constants.%ptr.e8f // CHECK:STDOUT: %.loc4_39.1 => constants.%.cb6 // CHECK:STDOUT: %pattern_type => constants.%pattern_type.4f4 // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete => constants.%require_complete.ef1 // CHECK:STDOUT: %ExplicitGenericParam.specific_fn.loc4_50.2 => constants.%ExplicitGenericParam.specific_fn.409 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @ExplicitGenericParam(constants.%i32) { // CHECK:STDOUT: %T.loc4_25.1 => constants.%i32 // CHECK:STDOUT: %ptr.loc4_39.1 => constants.%ptr.235 // CHECK:STDOUT: %.loc4_39.1 => constants.%.605 // CHECK:STDOUT: %pattern_type => constants.%pattern_type.fe8 // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete => constants.%complete_type.3d0 // CHECK:STDOUT: %ExplicitGenericParam.specific_fn.loc4_50.2 => constants.%ExplicitGenericParam.specific_fn.3d2 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @CallExplicitGenericParamWithGenericArg(constants.%T) { // CHECK:STDOUT: %T.loc10_43.1 => constants.%T // CHECK:STDOUT: %struct_type.a.loc10_62.1 => constants.%struct_type.a // CHECK:STDOUT: %ptr.loc10_63.1 => constants.%ptr.88d // CHECK:STDOUT: %.loc10_63.1 => constants.%.edd // CHECK:STDOUT: %pattern_type => constants.%pattern_type.1cb // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @ExplicitGenericParam(constants.%struct_type.a) { // CHECK:STDOUT: %T.loc4_25.1 => constants.%struct_type.a // CHECK:STDOUT: %ptr.loc4_39.1 => constants.%ptr.88d // CHECK:STDOUT: %.loc4_39.1 => constants.%.edd // CHECK:STDOUT: %pattern_type => constants.%pattern_type.1cb // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete => constants.%require_complete.61f // CHECK:STDOUT: %ExplicitGenericParam.specific_fn.loc4_50.2 => constants.%ExplicitGenericParam.specific_fn.55c // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- fail_deduce_explicit_non_constant.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %type: type = facet_type [concrete] // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self] // CHECK:STDOUT: %pattern_type.98f: type = pattern_type type [concrete] // CHECK:STDOUT: %T: type = symbolic_binding T, 0 [symbolic] // CHECK:STDOUT: %ptr: type = ptr_type %T [symbolic] // CHECK:STDOUT: %.cb6: Core.Form = init_form %ptr [symbolic] // CHECK:STDOUT: %pattern_type.4f4: type = pattern_type %ptr [symbolic] // CHECK:STDOUT: %ExplicitGenericParam.type: type = fn_type @ExplicitGenericParam [concrete] // CHECK:STDOUT: %ExplicitGenericParam: %ExplicitGenericParam.type = struct_value () [concrete] // CHECK:STDOUT: %require_complete: = require_complete_type %ptr [symbolic] // CHECK:STDOUT: %ExplicitGenericParam.specific_fn: = specific_function %ExplicitGenericParam, @ExplicitGenericParam(%T) [symbolic] // CHECK:STDOUT: %CallExplicitGenericParamConst.type: type = fn_type @CallExplicitGenericParamConst [concrete] // CHECK:STDOUT: %CallExplicitGenericParamConst: %CallExplicitGenericParamConst.type = struct_value () [concrete] // CHECK:STDOUT: %CallExplicitGenericParamNonConst.type: type = fn_type @CallExplicitGenericParamNonConst [concrete] // CHECK:STDOUT: %CallExplicitGenericParamNonConst: %CallExplicitGenericParamNonConst.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .ExplicitGenericParam = %ExplicitGenericParam.decl // CHECK:STDOUT: .CallExplicitGenericParamConst = %CallExplicitGenericParamConst.decl // CHECK:STDOUT: .CallExplicitGenericParamNonConst = %CallExplicitGenericParamNonConst.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %ExplicitGenericParam.decl: %ExplicitGenericParam.type = fn_decl @ExplicitGenericParam [concrete = constants.%ExplicitGenericParam] { // CHECK:STDOUT: %T.patt: %pattern_type.98f = symbolic_binding_pattern T, 0 [concrete] // CHECK:STDOUT: %return.patt: @ExplicitGenericParam.%pattern_type (%pattern_type.4f4) = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: @ExplicitGenericParam.%pattern_type (%pattern_type.4f4) = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %T.ref.loc4_38: type = name_ref T, %T.loc4_25.2 [symbolic = %T.loc4_25.1 (constants.%T)] // CHECK:STDOUT: %ptr.loc4_39.2: type = ptr_type %T.ref.loc4_38 [symbolic = %ptr.loc4_39.1 (constants.%ptr)] // CHECK:STDOUT: %.loc4_39.2: Core.Form = init_form %ptr.loc4_39.2 [symbolic = %.loc4_39.1 (constants.%.cb6)] // CHECK:STDOUT: %.loc4_29.1: type = splice_block %.loc4_29.2 [concrete = type] { // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %.loc4_29.2: type = type_literal type [concrete = type] // CHECK:STDOUT: } // CHECK:STDOUT: %T.loc4_25.2: type = symbolic_binding T, 0 [symbolic = %T.loc4_25.1 (constants.%T)] // CHECK:STDOUT: %return.param: ref @ExplicitGenericParam.%ptr.loc4_39.1 (%ptr) = out_param call_param0 // CHECK:STDOUT: %return: ref @ExplicitGenericParam.%ptr.loc4_39.1 (%ptr) = return_slot %return.param // CHECK:STDOUT: } // CHECK:STDOUT: %CallExplicitGenericParamConst.decl: %CallExplicitGenericParamConst.type = fn_decl @CallExplicitGenericParamConst [concrete = constants.%CallExplicitGenericParamConst] { // CHECK:STDOUT: %T.patt: %pattern_type.98f = symbolic_binding_pattern T, 0 [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %.loc6_38.1: type = splice_block %.loc6_38.2 [concrete = type] { // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %.loc6_38.2: type = type_literal type [concrete = type] // CHECK:STDOUT: } // CHECK:STDOUT: %T.loc6_34.2: type = symbolic_binding T, 0 [symbolic = %T.loc6_34.1 (constants.%T)] // CHECK:STDOUT: } // CHECK:STDOUT: %CallExplicitGenericParamNonConst.decl: %CallExplicitGenericParamNonConst.type = fn_decl @CallExplicitGenericParamNonConst [concrete = constants.%CallExplicitGenericParamNonConst] { // CHECK:STDOUT: %T.patt: %pattern_type.98f = value_binding_pattern T [concrete] // CHECK:STDOUT: %T.param_patt: %pattern_type.98f = value_param_pattern %T.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %T.param: type = value_param call_param0 // CHECK:STDOUT: %.loc10: type = type_literal type [concrete = type] // CHECK:STDOUT: %T: type = value_binding T, %T.param // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic fn @ExplicitGenericParam(%T.loc4_25.2: type) { // CHECK:STDOUT: %T.loc4_25.1: type = symbolic_binding T, 0 [symbolic = %T.loc4_25.1 (constants.%T)] // CHECK:STDOUT: %ptr.loc4_39.1: type = ptr_type %T.loc4_25.1 [symbolic = %ptr.loc4_39.1 (constants.%ptr)] // CHECK:STDOUT: %.loc4_39.1: Core.Form = init_form %ptr.loc4_39.1 [symbolic = %.loc4_39.1 (constants.%.cb6)] // CHECK:STDOUT: %pattern_type: type = pattern_type %ptr.loc4_39.1 [symbolic = %pattern_type (constants.%pattern_type.4f4)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete: = require_complete_type %ptr.loc4_39.1 [symbolic = %require_complete (constants.%require_complete)] // CHECK:STDOUT: %ExplicitGenericParam.specific_fn.loc4_50.2: = specific_function constants.%ExplicitGenericParam, @ExplicitGenericParam(%T.loc4_25.1) [symbolic = %ExplicitGenericParam.specific_fn.loc4_50.2 (constants.%ExplicitGenericParam.specific_fn)] // CHECK:STDOUT: // CHECK:STDOUT: fn() -> out %return.param: @ExplicitGenericParam.%ptr.loc4_39.1 (%ptr) { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %ExplicitGenericParam.ref: %ExplicitGenericParam.type = name_ref ExplicitGenericParam, file.%ExplicitGenericParam.decl [concrete = constants.%ExplicitGenericParam] // CHECK:STDOUT: %T.ref.loc4_71: type = name_ref T, %T.loc4_25.2 [symbolic = %T.loc4_25.1 (constants.%T)] // CHECK:STDOUT: %ExplicitGenericParam.specific_fn.loc4_50.1: = specific_function %ExplicitGenericParam.ref, @ExplicitGenericParam(constants.%T) [symbolic = %ExplicitGenericParam.specific_fn.loc4_50.2 (constants.%ExplicitGenericParam.specific_fn)] // CHECK:STDOUT: %ExplicitGenericParam.call: init @ExplicitGenericParam.%ptr.loc4_39.1 (%ptr) = call %ExplicitGenericParam.specific_fn.loc4_50.1() // CHECK:STDOUT: return %ExplicitGenericParam.call // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic fn @CallExplicitGenericParamConst(%T.loc6_34.2: type) { // CHECK:STDOUT: %T.loc6_34.1: type = symbolic_binding T, 0 [symbolic = %T.loc6_34.1 (constants.%T)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %ExplicitGenericParam.specific_fn.loc7_3.2: = specific_function constants.%ExplicitGenericParam, @ExplicitGenericParam(%T.loc6_34.1) [symbolic = %ExplicitGenericParam.specific_fn.loc7_3.2 (constants.%ExplicitGenericParam.specific_fn)] // CHECK:STDOUT: %ptr: type = ptr_type %T.loc6_34.1 [symbolic = %ptr (constants.%ptr)] // CHECK:STDOUT: // CHECK:STDOUT: fn() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %ExplicitGenericParam.ref: %ExplicitGenericParam.type = name_ref ExplicitGenericParam, file.%ExplicitGenericParam.decl [concrete = constants.%ExplicitGenericParam] // CHECK:STDOUT: %T.ref: type = name_ref T, %T.loc6_34.2 [symbolic = %T.loc6_34.1 (constants.%T)] // CHECK:STDOUT: %ExplicitGenericParam.specific_fn.loc7_3.1: = specific_function %ExplicitGenericParam.ref, @ExplicitGenericParam(constants.%T) [symbolic = %ExplicitGenericParam.specific_fn.loc7_3.2 (constants.%ExplicitGenericParam.specific_fn)] // CHECK:STDOUT: %ExplicitGenericParam.call: init @CallExplicitGenericParamConst.%ptr (%ptr) = call %ExplicitGenericParam.specific_fn.loc7_3.1() // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @CallExplicitGenericParamNonConst(%T.param: type) { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %ExplicitGenericParam.ref: %ExplicitGenericParam.type = name_ref ExplicitGenericParam, file.%ExplicitGenericParam.decl [concrete = constants.%ExplicitGenericParam] // CHECK:STDOUT: %T.ref: type = name_ref T, %T // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @ExplicitGenericParam(constants.%T) { // CHECK:STDOUT: %T.loc4_25.1 => constants.%T // CHECK:STDOUT: %ptr.loc4_39.1 => constants.%ptr // CHECK:STDOUT: %.loc4_39.1 => constants.%.cb6 // CHECK:STDOUT: %pattern_type => constants.%pattern_type.4f4 // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete => constants.%require_complete // CHECK:STDOUT: %ExplicitGenericParam.specific_fn.loc4_50.2 => constants.%ExplicitGenericParam.specific_fn // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @CallExplicitGenericParamConst(constants.%T) { // CHECK:STDOUT: %T.loc6_34.1 => constants.%T // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- explicit_vs_deduced.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %A: type = class_type @A [concrete] // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %complete_type.357: = complete_type_witness %empty_struct_type [concrete] // CHECK:STDOUT: %type: type = facet_type [concrete] // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self] // CHECK:STDOUT: %pattern_type.98f: type = pattern_type type [concrete] // CHECK:STDOUT: %T: type = symbolic_binding T, 0 [symbolic] // CHECK:STDOUT: %pattern_type.51d: type = pattern_type %T [symbolic] // CHECK:STDOUT: %ptr.e8f: type = ptr_type %T [symbolic] // CHECK:STDOUT: %.cb6: Core.Form = init_form %ptr.e8f [symbolic] // CHECK:STDOUT: %pattern_type.4f4: type = pattern_type %ptr.e8f [symbolic] // CHECK:STDOUT: %ExplicitAndAlsoDeduced.type: type = fn_type @ExplicitAndAlsoDeduced [concrete] // CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] // CHECK:STDOUT: %ExplicitAndAlsoDeduced: %ExplicitAndAlsoDeduced.type = struct_value () [concrete] // CHECK:STDOUT: %require_complete.944: = require_complete_type %T [symbolic] // CHECK:STDOUT: %require_complete.ef1: = require_complete_type %ptr.e8f [symbolic] // CHECK:STDOUT: %ExplicitAndAlsoDeduced.specific_fn.7e7: = specific_function %ExplicitAndAlsoDeduced, @ExplicitAndAlsoDeduced(%T) [symbolic] // CHECK:STDOUT: %ptr.643: type = ptr_type %A [concrete] // CHECK:STDOUT: %.ebf: Core.Form = init_form %ptr.643 [concrete] // CHECK:STDOUT: %pattern_type.f29: type = pattern_type %ptr.643 [concrete] // CHECK:STDOUT: %CallExplicitAndAlsoDeduced.type: type = fn_type @CallExplicitAndAlsoDeduced [concrete] // CHECK:STDOUT: %CallExplicitAndAlsoDeduced: %CallExplicitAndAlsoDeduced.type = struct_value () [concrete] // CHECK:STDOUT: %empty_struct: %empty_struct_type = struct_value () [concrete] // CHECK:STDOUT: %pattern_type.1ab: type = pattern_type %A [concrete] // CHECK:STDOUT: %ExplicitAndAlsoDeduced.specific_fn.1f3: = specific_function %ExplicitAndAlsoDeduced, @ExplicitAndAlsoDeduced(%A) [concrete] // CHECK:STDOUT: %A.val: %A = struct_value () [concrete] // CHECK:STDOUT: %Destroy.type: type = facet_type <@Destroy> [concrete] // CHECK:STDOUT: %Destroy.Op.type: type = fn_type @Destroy.Op [concrete] // CHECK:STDOUT: %Destroy.Op: %Destroy.Op.type = struct_value () [concrete] // CHECK:STDOUT: %complete_type.8a0: = complete_type_witness %ptr.643 [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: .Destroy = %Core.Destroy // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: %Core.Destroy: type = import_ref Core//prelude/parts/destroy, Destroy, loaded [concrete = constants.%Destroy.type] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .A = %A.decl // CHECK:STDOUT: .ExplicitAndAlsoDeduced = %ExplicitAndAlsoDeduced.decl // CHECK:STDOUT: .CallExplicitAndAlsoDeduced = %CallExplicitAndAlsoDeduced.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %A.decl: type = class_decl @A [concrete = constants.%A] {} {} // CHECK:STDOUT: %ExplicitAndAlsoDeduced.decl: %ExplicitAndAlsoDeduced.type = fn_decl @ExplicitAndAlsoDeduced [concrete = constants.%ExplicitAndAlsoDeduced] { // CHECK:STDOUT: %T.patt: %pattern_type.98f = symbolic_binding_pattern T, 0 [concrete] // CHECK:STDOUT: %x.patt: @ExplicitAndAlsoDeduced.%pattern_type.loc6_37 (%pattern_type.51d) = value_binding_pattern x [concrete] // CHECK:STDOUT: %x.param_patt: @ExplicitAndAlsoDeduced.%pattern_type.loc6_37 (%pattern_type.51d) = value_param_pattern %x.patt [concrete] // CHECK:STDOUT: %return.patt: @ExplicitAndAlsoDeduced.%pattern_type.loc6_43 (%pattern_type.4f4) = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: @ExplicitAndAlsoDeduced.%pattern_type.loc6_43 (%pattern_type.4f4) = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %T.ref.loc6_46: type = name_ref T, %T.loc6_27.2 [symbolic = %T.loc6_27.1 (constants.%T)] // CHECK:STDOUT: %ptr.loc6_47.2: type = ptr_type %T.ref.loc6_46 [symbolic = %ptr.loc6_47.1 (constants.%ptr.e8f)] // CHECK:STDOUT: %.loc6_47.2: Core.Form = init_form %ptr.loc6_47.2 [symbolic = %.loc6_47.1 (constants.%.cb6)] // CHECK:STDOUT: %.loc6_31.1: type = splice_block %.loc6_31.2 [concrete = type] { // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %.loc6_31.2: type = type_literal type [concrete = type] // CHECK:STDOUT: } // CHECK:STDOUT: %T.loc6_27.2: type = symbolic_binding T, 0 [symbolic = %T.loc6_27.1 (constants.%T)] // CHECK:STDOUT: %x.param: @ExplicitAndAlsoDeduced.%T.loc6_27.1 (%T) = value_param call_param0 // CHECK:STDOUT: %T.ref.loc6_40: type = name_ref T, %T.loc6_27.2 [symbolic = %T.loc6_27.1 (constants.%T)] // CHECK:STDOUT: %x: @ExplicitAndAlsoDeduced.%T.loc6_27.1 (%T) = value_binding x, %x.param // CHECK:STDOUT: %return.param: ref @ExplicitAndAlsoDeduced.%ptr.loc6_47.1 (%ptr.e8f) = out_param call_param1 // CHECK:STDOUT: %return: ref @ExplicitAndAlsoDeduced.%ptr.loc6_47.1 (%ptr.e8f) = return_slot %return.param // CHECK:STDOUT: } // CHECK:STDOUT: %CallExplicitAndAlsoDeduced.decl: %CallExplicitAndAlsoDeduced.type = fn_decl @CallExplicitAndAlsoDeduced [concrete = constants.%CallExplicitAndAlsoDeduced] { // CHECK:STDOUT: %return.patt: %pattern_type.f29 = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: %pattern_type.f29 = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %A.ref.loc10: type = name_ref A, file.%A.decl [concrete = constants.%A] // CHECK:STDOUT: %ptr: type = ptr_type %A.ref.loc10 [concrete = constants.%ptr.643] // CHECK:STDOUT: %.loc10: Core.Form = init_form %ptr [concrete = constants.%.ebf] // CHECK:STDOUT: %return.param: ref %ptr.643 = out_param call_param0 // CHECK:STDOUT: %return: ref %ptr.643 = return_slot %return.param // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @A { // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type.357] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%A // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic fn @ExplicitAndAlsoDeduced(%T.loc6_27.2: type) { // CHECK:STDOUT: %T.loc6_27.1: type = symbolic_binding T, 0 [symbolic = %T.loc6_27.1 (constants.%T)] // CHECK:STDOUT: %pattern_type.loc6_37: type = pattern_type %T.loc6_27.1 [symbolic = %pattern_type.loc6_37 (constants.%pattern_type.51d)] // CHECK:STDOUT: %ptr.loc6_47.1: type = ptr_type %T.loc6_27.1 [symbolic = %ptr.loc6_47.1 (constants.%ptr.e8f)] // CHECK:STDOUT: %.loc6_47.1: Core.Form = init_form %ptr.loc6_47.1 [symbolic = %.loc6_47.1 (constants.%.cb6)] // CHECK:STDOUT: %pattern_type.loc6_43: type = pattern_type %ptr.loc6_47.1 [symbolic = %pattern_type.loc6_43 (constants.%pattern_type.4f4)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete.loc6_38: = require_complete_type %T.loc6_27.1 [symbolic = %require_complete.loc6_38 (constants.%require_complete.944)] // CHECK:STDOUT: %require_complete.loc6_47: = require_complete_type %ptr.loc6_47.1 [symbolic = %require_complete.loc6_47 (constants.%require_complete.ef1)] // CHECK:STDOUT: %ExplicitAndAlsoDeduced.specific_fn.loc7_10.2: = specific_function constants.%ExplicitAndAlsoDeduced, @ExplicitAndAlsoDeduced(%T.loc6_27.1) [symbolic = %ExplicitAndAlsoDeduced.specific_fn.loc7_10.2 (constants.%ExplicitAndAlsoDeduced.specific_fn.7e7)] // CHECK:STDOUT: // CHECK:STDOUT: fn(%x.param: @ExplicitAndAlsoDeduced.%T.loc6_27.1 (%T)) -> out %return.param: @ExplicitAndAlsoDeduced.%ptr.loc6_47.1 (%ptr.e8f) { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %ExplicitAndAlsoDeduced.ref: %ExplicitAndAlsoDeduced.type = name_ref ExplicitAndAlsoDeduced, file.%ExplicitAndAlsoDeduced.decl [concrete = constants.%ExplicitAndAlsoDeduced] // CHECK:STDOUT: %T.ref.loc7: type = name_ref T, %T.loc6_27.2 [symbolic = %T.loc6_27.1 (constants.%T)] // CHECK:STDOUT: %x.ref: @ExplicitAndAlsoDeduced.%T.loc6_27.1 (%T) = name_ref x, %x // CHECK:STDOUT: %ExplicitAndAlsoDeduced.specific_fn.loc7_10.1: = specific_function %ExplicitAndAlsoDeduced.ref, @ExplicitAndAlsoDeduced(constants.%T) [symbolic = %ExplicitAndAlsoDeduced.specific_fn.loc7_10.2 (constants.%ExplicitAndAlsoDeduced.specific_fn.7e7)] // CHECK:STDOUT: %ExplicitAndAlsoDeduced.call: init @ExplicitAndAlsoDeduced.%ptr.loc6_47.1 (%ptr.e8f) = call %ExplicitAndAlsoDeduced.specific_fn.loc7_10.1(%x.ref) // CHECK:STDOUT: return %ExplicitAndAlsoDeduced.call // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @CallExplicitAndAlsoDeduced() -> out %return.param: %ptr.643 { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %ExplicitAndAlsoDeduced.ref: %ExplicitAndAlsoDeduced.type = name_ref ExplicitAndAlsoDeduced, file.%ExplicitAndAlsoDeduced.decl [concrete = constants.%ExplicitAndAlsoDeduced] // CHECK:STDOUT: %A.ref.loc11: type = name_ref A, file.%A.decl [concrete = constants.%A] // CHECK:STDOUT: %.loc11_37.1: %empty_struct_type = struct_literal () [concrete = constants.%empty_struct] // CHECK:STDOUT: %ExplicitAndAlsoDeduced.specific_fn: = specific_function %ExplicitAndAlsoDeduced.ref, @ExplicitAndAlsoDeduced(constants.%A) [concrete = constants.%ExplicitAndAlsoDeduced.specific_fn.1f3] // CHECK:STDOUT: %.loc11_37.2: ref %A = temporary_storage // CHECK:STDOUT: %.loc11_37.3: init %A to %.loc11_37.2 = class_init () [concrete = constants.%A.val] // CHECK:STDOUT: %.loc11_37.4: init %A = converted %.loc11_37.1, %.loc11_37.3 [concrete = constants.%A.val] // CHECK:STDOUT: %.loc11_37.5: ref %A = temporary %.loc11_37.2, %.loc11_37.4 // CHECK:STDOUT: %.loc11_37.6: %A = acquire_value %.loc11_37.5 // CHECK:STDOUT: %ExplicitAndAlsoDeduced.call: init %ptr.643 = call %ExplicitAndAlsoDeduced.specific_fn(%.loc11_37.6) // CHECK:STDOUT: %Destroy.Op.bound: = bound_method %.loc11_37.5, constants.%Destroy.Op // CHECK:STDOUT: %Destroy.Op.call: init %empty_tuple.type = call %Destroy.Op.bound(%.loc11_37.5) // CHECK:STDOUT: return %ExplicitAndAlsoDeduced.call // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @Destroy.Op(%self.param: ref %A) = "no_op"; // CHECK:STDOUT: // CHECK:STDOUT: specific @ExplicitAndAlsoDeduced(constants.%T) { // CHECK:STDOUT: %T.loc6_27.1 => constants.%T // CHECK:STDOUT: %pattern_type.loc6_37 => constants.%pattern_type.51d // CHECK:STDOUT: %ptr.loc6_47.1 => constants.%ptr.e8f // CHECK:STDOUT: %.loc6_47.1 => constants.%.cb6 // CHECK:STDOUT: %pattern_type.loc6_43 => constants.%pattern_type.4f4 // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete.loc6_38 => constants.%require_complete.944 // CHECK:STDOUT: %require_complete.loc6_47 => constants.%require_complete.ef1 // CHECK:STDOUT: %ExplicitAndAlsoDeduced.specific_fn.loc7_10.2 => constants.%ExplicitAndAlsoDeduced.specific_fn.7e7 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @ExplicitAndAlsoDeduced(constants.%A) { // CHECK:STDOUT: %T.loc6_27.1 => constants.%A // CHECK:STDOUT: %pattern_type.loc6_37 => constants.%pattern_type.1ab // CHECK:STDOUT: %ptr.loc6_47.1 => constants.%ptr.643 // CHECK:STDOUT: %.loc6_47.1 => constants.%.ebf // CHECK:STDOUT: %pattern_type.loc6_43 => constants.%pattern_type.f29 // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete.loc6_38 => constants.%complete_type.357 // CHECK:STDOUT: %require_complete.loc6_47 => constants.%complete_type.8a0 // CHECK:STDOUT: %ExplicitAndAlsoDeduced.specific_fn.loc7_10.2 => constants.%ExplicitAndAlsoDeduced.specific_fn.1f3 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- deduce_implicit.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %type: type = facet_type [concrete] // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self] // CHECK:STDOUT: %pattern_type.98f: type = pattern_type type [concrete] // CHECK:STDOUT: %T: type = symbolic_binding T, 0 [symbolic] // CHECK:STDOUT: %pattern_type.51d: type = pattern_type %T [symbolic] // CHECK:STDOUT: %ptr.e8f: type = ptr_type %T [symbolic] // CHECK:STDOUT: %.cb6: Core.Form = init_form %ptr.e8f [symbolic] // CHECK:STDOUT: %pattern_type.4f4: type = pattern_type %ptr.e8f [symbolic] // CHECK:STDOUT: %ImplicitGenericParam.type: type = fn_type @ImplicitGenericParam [concrete] // CHECK:STDOUT: %ImplicitGenericParam: %ImplicitGenericParam.type = struct_value () [concrete] // CHECK:STDOUT: %require_complete.944: = require_complete_type %T [symbolic] // CHECK:STDOUT: %require_complete.ef1: = require_complete_type %ptr.e8f [symbolic] // CHECK:STDOUT: %ImplicitGenericParam.specific_fn.7cc: = specific_function %ImplicitGenericParam, @ImplicitGenericParam(%T) [symbolic] // CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [concrete] // CHECK:STDOUT: %Int.type: type = generic_class_type @Int [concrete] // CHECK:STDOUT: %Int.generic: %Int.type = struct_value () [concrete] // CHECK:STDOUT: %i32: type = class_type @Int, @Int(%int_32) [concrete] // CHECK:STDOUT: %pattern_type.7ce: type = pattern_type %i32 [concrete] // CHECK:STDOUT: %ptr.235: type = ptr_type %i32 [concrete] // CHECK:STDOUT: %.605: Core.Form = init_form %ptr.235 [concrete] // CHECK:STDOUT: %pattern_type.fe8: type = pattern_type %ptr.235 [concrete] // CHECK:STDOUT: %CallImplicitGenericParam.type: type = fn_type @CallImplicitGenericParam [concrete] // CHECK:STDOUT: %CallImplicitGenericParam: %CallImplicitGenericParam.type = struct_value () [concrete] // CHECK:STDOUT: %i32.builtin: type = int_type signed, %int_32 [concrete] // CHECK:STDOUT: %complete_type.f8a: = complete_type_witness %i32.builtin [concrete] // CHECK:STDOUT: %ImplicitGenericParam.specific_fn.be5: = specific_function %ImplicitGenericParam, @ImplicitGenericParam(%i32) [concrete] // CHECK:STDOUT: %complete_type.3d0: = complete_type_witness %ptr.235 [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: .Int = %Core.Int // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: %Core.Int: %Int.type = import_ref Core//prelude/parts/int, Int, loaded [concrete = constants.%Int.generic] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .ImplicitGenericParam = %ImplicitGenericParam.decl // CHECK:STDOUT: .CallImplicitGenericParam = %CallImplicitGenericParam.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %ImplicitGenericParam.decl: %ImplicitGenericParam.type = fn_decl @ImplicitGenericParam [concrete = constants.%ImplicitGenericParam] { // CHECK:STDOUT: %T.patt: %pattern_type.98f = symbolic_binding_pattern T, 0 [concrete] // CHECK:STDOUT: %x.patt: @ImplicitGenericParam.%pattern_type.loc4_35 (%pattern_type.51d) = value_binding_pattern x [concrete] // CHECK:STDOUT: %x.param_patt: @ImplicitGenericParam.%pattern_type.loc4_35 (%pattern_type.51d) = value_param_pattern %x.patt [concrete] // CHECK:STDOUT: %return.patt: @ImplicitGenericParam.%pattern_type.loc4_41 (%pattern_type.4f4) = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: @ImplicitGenericParam.%pattern_type.loc4_41 (%pattern_type.4f4) = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %T.ref.loc4_44: type = name_ref T, %T.loc4_25.2 [symbolic = %T.loc4_25.1 (constants.%T)] // CHECK:STDOUT: %ptr.loc4_45.2: type = ptr_type %T.ref.loc4_44 [symbolic = %ptr.loc4_45.1 (constants.%ptr.e8f)] // CHECK:STDOUT: %.loc4_45.2: Core.Form = init_form %ptr.loc4_45.2 [symbolic = %.loc4_45.1 (constants.%.cb6)] // CHECK:STDOUT: %.loc4_29.1: type = splice_block %.loc4_29.2 [concrete = type] { // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %.loc4_29.2: type = type_literal type [concrete = type] // CHECK:STDOUT: } // CHECK:STDOUT: %T.loc4_25.2: type = symbolic_binding T, 0 [symbolic = %T.loc4_25.1 (constants.%T)] // CHECK:STDOUT: %x.param: @ImplicitGenericParam.%T.loc4_25.1 (%T) = value_param call_param0 // CHECK:STDOUT: %T.ref.loc4_38: type = name_ref T, %T.loc4_25.2 [symbolic = %T.loc4_25.1 (constants.%T)] // CHECK:STDOUT: %x: @ImplicitGenericParam.%T.loc4_25.1 (%T) = value_binding x, %x.param // CHECK:STDOUT: %return.param: ref @ImplicitGenericParam.%ptr.loc4_45.1 (%ptr.e8f) = out_param call_param1 // CHECK:STDOUT: %return: ref @ImplicitGenericParam.%ptr.loc4_45.1 (%ptr.e8f) = return_slot %return.param // CHECK:STDOUT: } // CHECK:STDOUT: %CallImplicitGenericParam.decl: %CallImplicitGenericParam.type = fn_decl @CallImplicitGenericParam [concrete = constants.%CallImplicitGenericParam] { // CHECK:STDOUT: %n.patt: %pattern_type.7ce = value_binding_pattern n [concrete] // CHECK:STDOUT: %n.param_patt: %pattern_type.7ce = value_param_pattern %n.patt [concrete] // CHECK:STDOUT: %return.patt: %pattern_type.fe8 = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: %pattern_type.fe8 = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %i32.loc6_40: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %ptr: type = ptr_type %i32.loc6_40 [concrete = constants.%ptr.235] // CHECK:STDOUT: %.loc6: Core.Form = init_form %ptr [concrete = constants.%.605] // CHECK:STDOUT: %n.param: %i32 = value_param call_param0 // CHECK:STDOUT: %i32.loc6_32: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %n: %i32 = value_binding n, %n.param // CHECK:STDOUT: %return.param: ref %ptr.235 = out_param call_param1 // CHECK:STDOUT: %return: ref %ptr.235 = return_slot %return.param // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic fn @ImplicitGenericParam(%T.loc4_25.2: type) { // CHECK:STDOUT: %T.loc4_25.1: type = symbolic_binding T, 0 [symbolic = %T.loc4_25.1 (constants.%T)] // CHECK:STDOUT: %pattern_type.loc4_35: type = pattern_type %T.loc4_25.1 [symbolic = %pattern_type.loc4_35 (constants.%pattern_type.51d)] // CHECK:STDOUT: %ptr.loc4_45.1: type = ptr_type %T.loc4_25.1 [symbolic = %ptr.loc4_45.1 (constants.%ptr.e8f)] // CHECK:STDOUT: %.loc4_45.1: Core.Form = init_form %ptr.loc4_45.1 [symbolic = %.loc4_45.1 (constants.%.cb6)] // CHECK:STDOUT: %pattern_type.loc4_41: type = pattern_type %ptr.loc4_45.1 [symbolic = %pattern_type.loc4_41 (constants.%pattern_type.4f4)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete.loc4_36: = require_complete_type %T.loc4_25.1 [symbolic = %require_complete.loc4_36 (constants.%require_complete.944)] // CHECK:STDOUT: %require_complete.loc4_45: = require_complete_type %ptr.loc4_45.1 [symbolic = %require_complete.loc4_45 (constants.%require_complete.ef1)] // CHECK:STDOUT: %ImplicitGenericParam.specific_fn.loc4_56.2: = specific_function constants.%ImplicitGenericParam, @ImplicitGenericParam(%T.loc4_25.1) [symbolic = %ImplicitGenericParam.specific_fn.loc4_56.2 (constants.%ImplicitGenericParam.specific_fn.7cc)] // CHECK:STDOUT: // CHECK:STDOUT: fn(%x.param: @ImplicitGenericParam.%T.loc4_25.1 (%T)) -> out %return.param: @ImplicitGenericParam.%ptr.loc4_45.1 (%ptr.e8f) { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %ImplicitGenericParam.ref: %ImplicitGenericParam.type = name_ref ImplicitGenericParam, file.%ImplicitGenericParam.decl [concrete = constants.%ImplicitGenericParam] // CHECK:STDOUT: %x.ref: @ImplicitGenericParam.%T.loc4_25.1 (%T) = name_ref x, %x // CHECK:STDOUT: %ImplicitGenericParam.specific_fn.loc4_56.1: = specific_function %ImplicitGenericParam.ref, @ImplicitGenericParam(constants.%T) [symbolic = %ImplicitGenericParam.specific_fn.loc4_56.2 (constants.%ImplicitGenericParam.specific_fn.7cc)] // CHECK:STDOUT: %ImplicitGenericParam.call: init @ImplicitGenericParam.%ptr.loc4_45.1 (%ptr.e8f) = call %ImplicitGenericParam.specific_fn.loc4_56.1(%x.ref) // CHECK:STDOUT: return %ImplicitGenericParam.call // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @CallImplicitGenericParam(%n.param: %i32) -> out %return.param: %ptr.235 { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %ImplicitGenericParam.ref: %ImplicitGenericParam.type = name_ref ImplicitGenericParam, file.%ImplicitGenericParam.decl [concrete = constants.%ImplicitGenericParam] // CHECK:STDOUT: %n.ref: %i32 = name_ref n, %n // CHECK:STDOUT: %ImplicitGenericParam.specific_fn: = specific_function %ImplicitGenericParam.ref, @ImplicitGenericParam(constants.%i32) [concrete = constants.%ImplicitGenericParam.specific_fn.be5] // CHECK:STDOUT: %ImplicitGenericParam.call: init %ptr.235 = call %ImplicitGenericParam.specific_fn(%n.ref) // CHECK:STDOUT: return %ImplicitGenericParam.call // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @ImplicitGenericParam(constants.%T) { // CHECK:STDOUT: %T.loc4_25.1 => constants.%T // CHECK:STDOUT: %pattern_type.loc4_35 => constants.%pattern_type.51d // CHECK:STDOUT: %ptr.loc4_45.1 => constants.%ptr.e8f // CHECK:STDOUT: %.loc4_45.1 => constants.%.cb6 // CHECK:STDOUT: %pattern_type.loc4_41 => constants.%pattern_type.4f4 // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete.loc4_36 => constants.%require_complete.944 // CHECK:STDOUT: %require_complete.loc4_45 => constants.%require_complete.ef1 // CHECK:STDOUT: %ImplicitGenericParam.specific_fn.loc4_56.2 => constants.%ImplicitGenericParam.specific_fn.7cc // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @ImplicitGenericParam(constants.%i32) { // CHECK:STDOUT: %T.loc4_25.1 => constants.%i32 // CHECK:STDOUT: %pattern_type.loc4_35 => constants.%pattern_type.7ce // CHECK:STDOUT: %ptr.loc4_45.1 => constants.%ptr.235 // CHECK:STDOUT: %.loc4_45.1 => constants.%.605 // CHECK:STDOUT: %pattern_type.loc4_41 => constants.%pattern_type.fe8 // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete.loc4_36 => constants.%complete_type.f8a // CHECK:STDOUT: %require_complete.loc4_45 => constants.%complete_type.3d0 // CHECK:STDOUT: %ImplicitGenericParam.specific_fn.loc4_56.2 => constants.%ImplicitGenericParam.specific_fn.be5 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- deduce_nested_tuple.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %type: type = facet_type [concrete] // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self] // CHECK:STDOUT: %pattern_type.98f: type = pattern_type type [concrete] // CHECK:STDOUT: %T: type = symbolic_binding T, 0 [symbolic] // CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [concrete] // CHECK:STDOUT: %Int.type: type = generic_class_type @Int [concrete] // CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] // CHECK:STDOUT: %Int.generic: %Int.type = struct_value () [concrete] // CHECK:STDOUT: %i32: type = class_type @Int, @Int(%int_32) [concrete] // CHECK:STDOUT: %tuple.type.24b: type = tuple_type (type, type) [concrete] // CHECK:STDOUT: %tuple.c27: %tuple.type.24b = tuple_value (%T, %i32) [symbolic] // CHECK:STDOUT: %tuple.type.b01: type = tuple_type (%T, %i32) [symbolic] // CHECK:STDOUT: %pattern_type.e77: type = pattern_type %tuple.type.b01 [symbolic] // CHECK:STDOUT: %TupleParam.type: type = fn_type @TupleParam [concrete] // CHECK:STDOUT: %TupleParam: %TupleParam.type = struct_value () [concrete] // CHECK:STDOUT: %require_complete.9d8: = require_complete_type %tuple.type.b01 [symbolic] // CHECK:STDOUT: %CallTupleParam.type: type = fn_type @CallTupleParam [concrete] // CHECK:STDOUT: %CallTupleParam: %CallTupleParam.type = struct_value () [concrete] // CHECK:STDOUT: %int_1: Core.IntLiteral = int_value 1 [concrete] // CHECK:STDOUT: %int_2.ecc: Core.IntLiteral = int_value 2 [concrete] // CHECK:STDOUT: %tuple.type.f94: type = tuple_type (Core.IntLiteral, Core.IntLiteral) [concrete] // CHECK:STDOUT: %tuple.ad8: %tuple.type.f94 = tuple_value (%int_1, %int_2.ecc) [concrete] // CHECK:STDOUT: %tuple.ae9: %tuple.type.24b = tuple_value (Core.IntLiteral, %i32) [concrete] // CHECK:STDOUT: %tuple.type.4c8: type = tuple_type (Core.IntLiteral, %i32) [concrete] // CHECK:STDOUT: %pattern_type.6f2: type = pattern_type %tuple.type.4c8 [concrete] // CHECK:STDOUT: %TupleParam.specific_fn: = specific_function %TupleParam, @TupleParam(Core.IntLiteral) [concrete] // CHECK:STDOUT: %ImplicitAs.type.cc7: type = generic_interface_type @ImplicitAs [concrete] // CHECK:STDOUT: %ImplicitAs.generic: %ImplicitAs.type.cc7 = struct_value () [concrete] // CHECK:STDOUT: %ImplicitAs.type.e8c: type = facet_type <@ImplicitAs, @ImplicitAs(%i32)> [concrete] // CHECK:STDOUT: %To: Core.IntLiteral = symbolic_binding To, 0 [symbolic] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.4e6: type = fn_type @Core.IntLiteral.as.ImplicitAs.impl.Convert, @Core.IntLiteral.as.ImplicitAs.impl(%To) [symbolic] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.3c2: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.4e6 = struct_value () [symbolic] // CHECK:STDOUT: %ImplicitAs.impl_witness.6bc: = impl_witness imports.%ImplicitAs.impl_witness_table.74f, @Core.IntLiteral.as.ImplicitAs.impl(%int_32) [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.e0d: type = fn_type @Core.IntLiteral.as.ImplicitAs.impl.Convert, @Core.IntLiteral.as.ImplicitAs.impl(%int_32) [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.e0d = struct_value () [concrete] // CHECK:STDOUT: %ImplicitAs.facet: %ImplicitAs.type.e8c = facet_value Core.IntLiteral, (%ImplicitAs.impl_witness.6bc) [concrete] // CHECK:STDOUT: %ImplicitAs.WithSelf.Convert.type.b37: type = fn_type @ImplicitAs.WithSelf.Convert, @ImplicitAs.WithSelf(%i32, %ImplicitAs.facet) [concrete] // CHECK:STDOUT: %.545: type = fn_type_with_self_type %ImplicitAs.WithSelf.Convert.type.b37, %ImplicitAs.facet [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.bound: = bound_method %int_2.ecc, %Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5 [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn: = specific_function %Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5, @Core.IntLiteral.as.ImplicitAs.impl.Convert(%int_32) [concrete] // CHECK:STDOUT: %bound_method: = bound_method %int_2.ecc, %Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn [concrete] // CHECK:STDOUT: %int_2.ef8: %i32 = int_value 2 [concrete] // CHECK:STDOUT: %tuple.c87: %tuple.type.4c8 = tuple_value (%int_1, %int_2.ef8) [concrete] // CHECK:STDOUT: %complete_type.c49: = complete_type_witness %tuple.type.4c8 [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: .Int = %Core.Int // CHECK:STDOUT: .ImplicitAs = %Core.ImplicitAs // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: %Core.Int: %Int.type = import_ref Core//prelude/parts/int, Int, loaded [concrete = constants.%Int.generic] // CHECK:STDOUT: %Core.ImplicitAs: %ImplicitAs.type.cc7 = import_ref Core//prelude/parts/as, ImplicitAs, loaded [concrete = constants.%ImplicitAs.generic] // CHECK:STDOUT: %Core.import_ref.42d: @Core.IntLiteral.as.ImplicitAs.impl.%Core.IntLiteral.as.ImplicitAs.impl.Convert.type (%Core.IntLiteral.as.ImplicitAs.impl.Convert.type.4e6) = import_ref Core//prelude/parts/int, loc{{\d+_\d+}}, loaded [symbolic = @Core.IntLiteral.as.ImplicitAs.impl.%Core.IntLiteral.as.ImplicitAs.impl.Convert (constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.3c2)] // CHECK:STDOUT: %ImplicitAs.impl_witness_table.74f = impl_witness_table (%Core.import_ref.42d), @Core.IntLiteral.as.ImplicitAs.impl [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .TupleParam = %TupleParam.decl // CHECK:STDOUT: .CallTupleParam = %CallTupleParam.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %TupleParam.decl: %TupleParam.type = fn_decl @TupleParam [concrete = constants.%TupleParam] { // CHECK:STDOUT: %T.patt: %pattern_type.98f = symbolic_binding_pattern T, 0 [concrete] // CHECK:STDOUT: %x.patt: @TupleParam.%pattern_type (%pattern_type.e77) = value_binding_pattern x [concrete] // CHECK:STDOUT: %x.param_patt: @TupleParam.%pattern_type (%pattern_type.e77) = value_param_pattern %x.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %.loc4_19.1: type = splice_block %.loc4_19.2 [concrete = type] { // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %.loc4_19.2: type = type_literal type [concrete = type] // CHECK:STDOUT: } // CHECK:STDOUT: %T.loc4_15.2: type = symbolic_binding T, 0 [symbolic = %T.loc4_15.1 (constants.%T)] // CHECK:STDOUT: %x.param: @TupleParam.%tuple.type (%tuple.type.b01) = value_param call_param0 // CHECK:STDOUT: %.loc4_42.1: type = splice_block %.loc4_42.3 [symbolic = %tuple.type (constants.%tuple.type.b01)] { // CHECK:STDOUT: %T.ref: type = name_ref T, %T.loc4_15.2 [symbolic = %T.loc4_15.1 (constants.%T)] // CHECK:STDOUT: %i32: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %.loc4_42.2: %tuple.type.24b = tuple_literal (%T.ref, %i32) [symbolic = %tuple (constants.%tuple.c27)] // CHECK:STDOUT: %.loc4_42.3: type = converted %.loc4_42.2, constants.%tuple.type.b01 [symbolic = %tuple.type (constants.%tuple.type.b01)] // CHECK:STDOUT: } // CHECK:STDOUT: %x: @TupleParam.%tuple.type (%tuple.type.b01) = value_binding x, %x.param // CHECK:STDOUT: } // CHECK:STDOUT: %CallTupleParam.decl: %CallTupleParam.type = fn_decl @CallTupleParam [concrete = constants.%CallTupleParam] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic fn @TupleParam(%T.loc4_15.2: type) { // CHECK:STDOUT: %T.loc4_15.1: type = symbolic_binding T, 0 [symbolic = %T.loc4_15.1 (constants.%T)] // CHECK:STDOUT: %tuple: %tuple.type.24b = tuple_value (%T.loc4_15.1, constants.%i32) [symbolic = %tuple (constants.%tuple.c27)] // CHECK:STDOUT: %tuple.type: type = tuple_type (%T.loc4_15.1, constants.%i32) [symbolic = %tuple.type (constants.%tuple.type.b01)] // CHECK:STDOUT: %pattern_type: type = pattern_type %tuple.type [symbolic = %pattern_type (constants.%pattern_type.e77)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete: = require_complete_type %tuple.type [symbolic = %require_complete (constants.%require_complete.9d8)] // CHECK:STDOUT: // CHECK:STDOUT: fn(%x.param: @TupleParam.%tuple.type (%tuple.type.b01)) { // CHECK:STDOUT: !entry: // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @CallTupleParam() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %TupleParam.ref: %TupleParam.type = name_ref TupleParam, file.%TupleParam.decl [concrete = constants.%TupleParam] // CHECK:STDOUT: %int_1: Core.IntLiteral = int_value 1 [concrete = constants.%int_1] // CHECK:STDOUT: %int_2: Core.IntLiteral = int_value 2 [concrete = constants.%int_2.ecc] // CHECK:STDOUT: %.loc7_19.1: %tuple.type.f94 = tuple_literal (%int_1, %int_2) [concrete = constants.%tuple.ad8] // CHECK:STDOUT: %TupleParam.specific_fn: = specific_function %TupleParam.ref, @TupleParam(Core.IntLiteral) [concrete = constants.%TupleParam.specific_fn] // CHECK:STDOUT: %impl.elem0: %.545 = impl_witness_access constants.%ImplicitAs.impl_witness.6bc, element0 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5] // CHECK:STDOUT: %bound_method.loc7_19.1: = bound_method %int_2, %impl.elem0 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.bound] // CHECK:STDOUT: %specific_fn: = specific_function %impl.elem0, @Core.IntLiteral.as.ImplicitAs.impl.Convert(constants.%int_32) [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn] // CHECK:STDOUT: %bound_method.loc7_19.2: = bound_method %int_2, %specific_fn [concrete = constants.%bound_method] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.call: init %i32 = call %bound_method.loc7_19.2(%int_2) [concrete = constants.%int_2.ef8] // CHECK:STDOUT: %.loc7_19.2: %i32 = value_of_initializer %Core.IntLiteral.as.ImplicitAs.impl.Convert.call [concrete = constants.%int_2.ef8] // CHECK:STDOUT: %.loc7_19.3: %i32 = converted %int_2, %.loc7_19.2 [concrete = constants.%int_2.ef8] // CHECK:STDOUT: %tuple: %tuple.type.4c8 = tuple_value (%int_1, %.loc7_19.3) [concrete = constants.%tuple.c87] // CHECK:STDOUT: %.loc7_19.4: %tuple.type.4c8 = converted %.loc7_19.1, %tuple [concrete = constants.%tuple.c87] // CHECK:STDOUT: %TupleParam.call: init %empty_tuple.type = call %TupleParam.specific_fn(%.loc7_19.4) // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @TupleParam(constants.%T) { // CHECK:STDOUT: %T.loc4_15.1 => constants.%T // CHECK:STDOUT: %tuple => constants.%tuple.c27 // CHECK:STDOUT: %tuple.type => constants.%tuple.type.b01 // CHECK:STDOUT: %pattern_type => constants.%pattern_type.e77 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @TupleParam(Core.IntLiteral) { // CHECK:STDOUT: %T.loc4_15.1 => Core.IntLiteral // CHECK:STDOUT: %tuple => constants.%tuple.ae9 // CHECK:STDOUT: %tuple.type => constants.%tuple.type.4c8 // CHECK:STDOUT: %pattern_type => constants.%pattern_type.6f2 // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete => constants.%complete_type.c49 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- deduce_nested_struct.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %type: type = facet_type [concrete] // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self] // CHECK:STDOUT: %pattern_type.98f: type = pattern_type type [concrete] // CHECK:STDOUT: %T: type = symbolic_binding T, 0 [symbolic] // CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [concrete] // CHECK:STDOUT: %Int.type: type = generic_class_type @Int [concrete] // CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] // CHECK:STDOUT: %Int.generic: %Int.type = struct_value () [concrete] // CHECK:STDOUT: %i32: type = class_type @Int, @Int(%int_32) [concrete] // CHECK:STDOUT: %struct_type.a.b.702: type = struct_type {.a: %T, .b: %i32} [symbolic] // CHECK:STDOUT: %pattern_type.8a5: type = pattern_type %struct_type.a.b.702 [symbolic] // CHECK:STDOUT: %StructParam.type: type = fn_type @StructParam [concrete] // CHECK:STDOUT: %StructParam: %StructParam.type = struct_value () [concrete] // CHECK:STDOUT: %require_complete.19d: = require_complete_type %struct_type.a.b.702 [symbolic] // CHECK:STDOUT: %CallStructParam.type: type = fn_type @CallStructParam [concrete] // CHECK:STDOUT: %CallStructParam: %CallStructParam.type = struct_value () [concrete] // CHECK:STDOUT: %int_1: Core.IntLiteral = int_value 1 [concrete] // CHECK:STDOUT: %int_2.ecc: Core.IntLiteral = int_value 2 [concrete] // CHECK:STDOUT: %struct_type.a.b.cfd: type = struct_type {.a: Core.IntLiteral, .b: Core.IntLiteral} [concrete] // CHECK:STDOUT: %struct.4aa: %struct_type.a.b.cfd = struct_value (%int_1, %int_2.ecc) [concrete] // CHECK:STDOUT: %struct_type.a.b.a13: type = struct_type {.a: Core.IntLiteral, .b: %i32} [concrete] // CHECK:STDOUT: %pattern_type.cfa: type = pattern_type %struct_type.a.b.a13 [concrete] // CHECK:STDOUT: %StructParam.specific_fn: = specific_function %StructParam, @StructParam(Core.IntLiteral) [concrete] // CHECK:STDOUT: %ImplicitAs.type.cc7: type = generic_interface_type @ImplicitAs [concrete] // CHECK:STDOUT: %ImplicitAs.generic: %ImplicitAs.type.cc7 = struct_value () [concrete] // CHECK:STDOUT: %ImplicitAs.type.e8c: type = facet_type <@ImplicitAs, @ImplicitAs(%i32)> [concrete] // CHECK:STDOUT: %To: Core.IntLiteral = symbolic_binding To, 0 [symbolic] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.4e6: type = fn_type @Core.IntLiteral.as.ImplicitAs.impl.Convert, @Core.IntLiteral.as.ImplicitAs.impl(%To) [symbolic] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.3c2: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.4e6 = struct_value () [symbolic] // CHECK:STDOUT: %ImplicitAs.impl_witness.6bc: = impl_witness imports.%ImplicitAs.impl_witness_table.74f, @Core.IntLiteral.as.ImplicitAs.impl(%int_32) [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.e0d: type = fn_type @Core.IntLiteral.as.ImplicitAs.impl.Convert, @Core.IntLiteral.as.ImplicitAs.impl(%int_32) [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.e0d = struct_value () [concrete] // CHECK:STDOUT: %ImplicitAs.facet: %ImplicitAs.type.e8c = facet_value Core.IntLiteral, (%ImplicitAs.impl_witness.6bc) [concrete] // CHECK:STDOUT: %ImplicitAs.WithSelf.Convert.type.b37: type = fn_type @ImplicitAs.WithSelf.Convert, @ImplicitAs.WithSelf(%i32, %ImplicitAs.facet) [concrete] // CHECK:STDOUT: %.545: type = fn_type_with_self_type %ImplicitAs.WithSelf.Convert.type.b37, %ImplicitAs.facet [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.bound: = bound_method %int_2.ecc, %Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5 [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn: = specific_function %Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5, @Core.IntLiteral.as.ImplicitAs.impl.Convert(%int_32) [concrete] // CHECK:STDOUT: %bound_method: = bound_method %int_2.ecc, %Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn [concrete] // CHECK:STDOUT: %int_2.ef8: %i32 = int_value 2 [concrete] // CHECK:STDOUT: %struct.e87: %struct_type.a.b.a13 = struct_value (%int_1, %int_2.ef8) [concrete] // CHECK:STDOUT: %complete_type.a4a: = complete_type_witness %struct_type.a.b.a13 [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: .Int = %Core.Int // CHECK:STDOUT: .ImplicitAs = %Core.ImplicitAs // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: %Core.Int: %Int.type = import_ref Core//prelude/parts/int, Int, loaded [concrete = constants.%Int.generic] // CHECK:STDOUT: %Core.ImplicitAs: %ImplicitAs.type.cc7 = import_ref Core//prelude/parts/as, ImplicitAs, loaded [concrete = constants.%ImplicitAs.generic] // CHECK:STDOUT: %Core.import_ref.42d: @Core.IntLiteral.as.ImplicitAs.impl.%Core.IntLiteral.as.ImplicitAs.impl.Convert.type (%Core.IntLiteral.as.ImplicitAs.impl.Convert.type.4e6) = import_ref Core//prelude/parts/int, loc{{\d+_\d+}}, loaded [symbolic = @Core.IntLiteral.as.ImplicitAs.impl.%Core.IntLiteral.as.ImplicitAs.impl.Convert (constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.3c2)] // CHECK:STDOUT: %ImplicitAs.impl_witness_table.74f = impl_witness_table (%Core.import_ref.42d), @Core.IntLiteral.as.ImplicitAs.impl [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .StructParam = %StructParam.decl // CHECK:STDOUT: .CallStructParam = %CallStructParam.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %StructParam.decl: %StructParam.type = fn_decl @StructParam [concrete = constants.%StructParam] { // CHECK:STDOUT: %T.patt: %pattern_type.98f = symbolic_binding_pattern T, 0 [concrete] // CHECK:STDOUT: %x.patt: @StructParam.%pattern_type (%pattern_type.8a5) = value_binding_pattern x [concrete] // CHECK:STDOUT: %x.param_patt: @StructParam.%pattern_type (%pattern_type.8a5) = value_param_pattern %x.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %.loc4_20.1: type = splice_block %.loc4_20.2 [concrete = type] { // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %.loc4_20.2: type = type_literal type [concrete = type] // CHECK:STDOUT: } // CHECK:STDOUT: %T.loc4_16.2: type = symbolic_binding T, 0 [symbolic = %T.loc4_16.1 (constants.%T)] // CHECK:STDOUT: %x.param: @StructParam.%struct_type.a.b.loc4_51.1 (%struct_type.a.b.702) = value_param call_param0 // CHECK:STDOUT: %.loc4_51: type = splice_block %struct_type.a.b.loc4_51.2 [symbolic = %struct_type.a.b.loc4_51.1 (constants.%struct_type.a.b.702)] { // CHECK:STDOUT: %T.ref: type = name_ref T, %T.loc4_16.2 [symbolic = %T.loc4_16.1 (constants.%T)] // CHECK:STDOUT: %i32: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %struct_type.a.b.loc4_51.2: type = struct_type {.a: @StructParam.%T.loc4_16.1 (%T), .b: %i32} [symbolic = %struct_type.a.b.loc4_51.1 (constants.%struct_type.a.b.702)] // CHECK:STDOUT: } // CHECK:STDOUT: %x: @StructParam.%struct_type.a.b.loc4_51.1 (%struct_type.a.b.702) = value_binding x, %x.param // CHECK:STDOUT: } // CHECK:STDOUT: %CallStructParam.decl: %CallStructParam.type = fn_decl @CallStructParam [concrete = constants.%CallStructParam] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic fn @StructParam(%T.loc4_16.2: type) { // CHECK:STDOUT: %T.loc4_16.1: type = symbolic_binding T, 0 [symbolic = %T.loc4_16.1 (constants.%T)] // CHECK:STDOUT: %struct_type.a.b.loc4_51.1: type = struct_type {.a: @StructParam.%T.loc4_16.1 (%T), .b: %i32} [symbolic = %struct_type.a.b.loc4_51.1 (constants.%struct_type.a.b.702)] // CHECK:STDOUT: %pattern_type: type = pattern_type %struct_type.a.b.loc4_51.1 [symbolic = %pattern_type (constants.%pattern_type.8a5)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete: = require_complete_type %struct_type.a.b.loc4_51.1 [symbolic = %require_complete (constants.%require_complete.19d)] // CHECK:STDOUT: // CHECK:STDOUT: fn(%x.param: @StructParam.%struct_type.a.b.loc4_51.1 (%struct_type.a.b.702)) { // CHECK:STDOUT: !entry: // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @CallStructParam() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %StructParam.ref: %StructParam.type = name_ref StructParam, file.%StructParam.decl [concrete = constants.%StructParam] // CHECK:STDOUT: %int_1: Core.IntLiteral = int_value 1 [concrete = constants.%int_1] // CHECK:STDOUT: %int_2: Core.IntLiteral = int_value 2 [concrete = constants.%int_2.ecc] // CHECK:STDOUT: %.loc7_30.1: %struct_type.a.b.cfd = struct_literal (%int_1, %int_2) [concrete = constants.%struct.4aa] // CHECK:STDOUT: %StructParam.specific_fn: = specific_function %StructParam.ref, @StructParam(Core.IntLiteral) [concrete = constants.%StructParam.specific_fn] // CHECK:STDOUT: %impl.elem0: %.545 = impl_witness_access constants.%ImplicitAs.impl_witness.6bc, element0 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5] // CHECK:STDOUT: %bound_method.loc7_30.1: = bound_method %int_2, %impl.elem0 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.bound] // CHECK:STDOUT: %specific_fn: = specific_function %impl.elem0, @Core.IntLiteral.as.ImplicitAs.impl.Convert(constants.%int_32) [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn] // CHECK:STDOUT: %bound_method.loc7_30.2: = bound_method %int_2, %specific_fn [concrete = constants.%bound_method] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.call: init %i32 = call %bound_method.loc7_30.2(%int_2) [concrete = constants.%int_2.ef8] // CHECK:STDOUT: %.loc7_30.2: %i32 = value_of_initializer %Core.IntLiteral.as.ImplicitAs.impl.Convert.call [concrete = constants.%int_2.ef8] // CHECK:STDOUT: %.loc7_30.3: %i32 = converted %int_2, %.loc7_30.2 [concrete = constants.%int_2.ef8] // CHECK:STDOUT: %struct: %struct_type.a.b.a13 = struct_value (%int_1, %.loc7_30.3) [concrete = constants.%struct.e87] // CHECK:STDOUT: %.loc7_30.4: %struct_type.a.b.a13 = converted %.loc7_30.1, %struct [concrete = constants.%struct.e87] // CHECK:STDOUT: %StructParam.call: init %empty_tuple.type = call %StructParam.specific_fn(%.loc7_30.4) // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @StructParam(constants.%T) { // CHECK:STDOUT: %T.loc4_16.1 => constants.%T // CHECK:STDOUT: %struct_type.a.b.loc4_51.1 => constants.%struct_type.a.b.702 // CHECK:STDOUT: %pattern_type => constants.%pattern_type.8a5 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @StructParam(Core.IntLiteral) { // CHECK:STDOUT: %T.loc4_16.1 => Core.IntLiteral // CHECK:STDOUT: %struct_type.a.b.loc4_51.1 => constants.%struct_type.a.b.a13 // CHECK:STDOUT: %pattern_type => constants.%pattern_type.cfa // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete => constants.%complete_type.a4a // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- fail_deduce_bigger_struct.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %type: type = facet_type [concrete] // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self] // CHECK:STDOUT: %pattern_type.98f: type = pattern_type type [concrete] // CHECK:STDOUT: %T: type = symbolic_binding T, 0 [symbolic] // CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [concrete] // CHECK:STDOUT: %Int.type: type = generic_class_type @Int [concrete] // CHECK:STDOUT: %Int.generic: %Int.type = struct_value () [concrete] // CHECK:STDOUT: %i32: type = class_type @Int, @Int(%int_32) [concrete] // CHECK:STDOUT: %struct_type.c.d.e: type = struct_type {.c: %T, .d: %i32, .e: %i32} [symbolic] // CHECK:STDOUT: %pattern_type.1d5: type = pattern_type %struct_type.c.d.e [symbolic] // CHECK:STDOUT: %BigStructParam.type: type = fn_type @BigStructParam [concrete] // CHECK:STDOUT: %BigStructParam: %BigStructParam.type = struct_value () [concrete] // CHECK:STDOUT: %require_complete.32d: = require_complete_type %struct_type.c.d.e [symbolic] // CHECK:STDOUT: %CallBigStructParam.type: type = fn_type @CallBigStructParam [concrete] // CHECK:STDOUT: %CallBigStructParam: %CallBigStructParam.type = struct_value () [concrete] // CHECK:STDOUT: %int_3: Core.IntLiteral = int_value 3 [concrete] // CHECK:STDOUT: %int_4: Core.IntLiteral = int_value 4 [concrete] // CHECK:STDOUT: %struct_type.c.d: type = struct_type {.c: Core.IntLiteral, .d: Core.IntLiteral} [concrete] // CHECK:STDOUT: %struct: %struct_type.c.d = struct_value (%int_3, %int_4) [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: .Int = %Core.Int // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: %Core.Int: %Int.type = import_ref Core//prelude/parts/int, Int, loaded [concrete = constants.%Int.generic] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .BigStructParam = %BigStructParam.decl // CHECK:STDOUT: .CallBigStructParam = %CallBigStructParam.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %BigStructParam.decl: %BigStructParam.type = fn_decl @BigStructParam [concrete = constants.%BigStructParam] { // CHECK:STDOUT: %T.patt: %pattern_type.98f = symbolic_binding_pattern T, 0 [concrete] // CHECK:STDOUT: %x.patt: @BigStructParam.%pattern_type (%pattern_type.1d5) = value_binding_pattern x [concrete] // CHECK:STDOUT: %x.param_patt: @BigStructParam.%pattern_type (%pattern_type.1d5) = value_param_pattern %x.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %.loc4_23.1: type = splice_block %.loc4_23.2 [concrete = type] { // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %.loc4_23.2: type = type_literal type [concrete = type] // CHECK:STDOUT: } // CHECK:STDOUT: %T.loc4_19.2: type = symbolic_binding T, 0 [symbolic = %T.loc4_19.1 (constants.%T)] // CHECK:STDOUT: %x.param: @BigStructParam.%struct_type.c.d.e.loc4_63.1 (%struct_type.c.d.e) = value_param call_param0 // CHECK:STDOUT: %.loc4_63: type = splice_block %struct_type.c.d.e.loc4_63.2 [symbolic = %struct_type.c.d.e.loc4_63.1 (constants.%struct_type.c.d.e)] { // CHECK:STDOUT: %T.ref: type = name_ref T, %T.loc4_19.2 [symbolic = %T.loc4_19.1 (constants.%T)] // CHECK:STDOUT: %i32.loc4_51: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %i32.loc4_60: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %struct_type.c.d.e.loc4_63.2: type = struct_type {.c: @BigStructParam.%T.loc4_19.1 (%T), .d: %i32, .e: %i32} [symbolic = %struct_type.c.d.e.loc4_63.1 (constants.%struct_type.c.d.e)] // CHECK:STDOUT: } // CHECK:STDOUT: %x: @BigStructParam.%struct_type.c.d.e.loc4_63.1 (%struct_type.c.d.e) = value_binding x, %x.param // CHECK:STDOUT: } // CHECK:STDOUT: %CallBigStructParam.decl: %CallBigStructParam.type = fn_decl @CallBigStructParam [concrete = constants.%CallBigStructParam] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic fn @BigStructParam(%T.loc4_19.2: type) { // CHECK:STDOUT: %T.loc4_19.1: type = symbolic_binding T, 0 [symbolic = %T.loc4_19.1 (constants.%T)] // CHECK:STDOUT: %struct_type.c.d.e.loc4_63.1: type = struct_type {.c: @BigStructParam.%T.loc4_19.1 (%T), .d: %i32, .e: %i32} [symbolic = %struct_type.c.d.e.loc4_63.1 (constants.%struct_type.c.d.e)] // CHECK:STDOUT: %pattern_type: type = pattern_type %struct_type.c.d.e.loc4_63.1 [symbolic = %pattern_type (constants.%pattern_type.1d5)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete: = require_complete_type %struct_type.c.d.e.loc4_63.1 [symbolic = %require_complete (constants.%require_complete.32d)] // CHECK:STDOUT: // CHECK:STDOUT: fn(%x.param: @BigStructParam.%struct_type.c.d.e.loc4_63.1 (%struct_type.c.d.e)) { // CHECK:STDOUT: !entry: // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @CallBigStructParam() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %BigStructParam.ref: %BigStructParam.type = name_ref BigStructParam, file.%BigStructParam.decl [concrete = constants.%BigStructParam] // CHECK:STDOUT: %int_3: Core.IntLiteral = int_value 3 [concrete = constants.%int_3] // CHECK:STDOUT: %int_4: Core.IntLiteral = int_value 4 [concrete = constants.%int_4] // CHECK:STDOUT: %.loc14: %struct_type.c.d = struct_literal (%int_3, %int_4) [concrete = constants.%struct] // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @BigStructParam(constants.%T) { // CHECK:STDOUT: %T.loc4_19.1 => constants.%T // CHECK:STDOUT: %struct_type.c.d.e.loc4_63.1 => constants.%struct_type.c.d.e // CHECK:STDOUT: %pattern_type => constants.%pattern_type.1d5 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- fail_deduce_smaller_struct.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %type: type = facet_type [concrete] // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self] // CHECK:STDOUT: %pattern_type.98f: type = pattern_type type [concrete] // CHECK:STDOUT: %T: type = symbolic_binding T, 0 [symbolic] // CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [concrete] // CHECK:STDOUT: %Int.type: type = generic_class_type @Int [concrete] // CHECK:STDOUT: %Int.generic: %Int.type = struct_value () [concrete] // CHECK:STDOUT: %i32: type = class_type @Int, @Int(%int_32) [concrete] // CHECK:STDOUT: %struct_type.f.g: type = struct_type {.f: %T, .g: %i32} [symbolic] // CHECK:STDOUT: %pattern_type.958: type = pattern_type %struct_type.f.g [symbolic] // CHECK:STDOUT: %SmallStructParam.type: type = fn_type @SmallStructParam [concrete] // CHECK:STDOUT: %SmallStructParam: %SmallStructParam.type = struct_value () [concrete] // CHECK:STDOUT: %require_complete.ed5: = require_complete_type %struct_type.f.g [symbolic] // CHECK:STDOUT: %CallSmallStructParam.type: type = fn_type @CallSmallStructParam [concrete] // CHECK:STDOUT: %CallSmallStructParam: %CallSmallStructParam.type = struct_value () [concrete] // CHECK:STDOUT: %int_5: Core.IntLiteral = int_value 5 [concrete] // CHECK:STDOUT: %int_6: Core.IntLiteral = int_value 6 [concrete] // CHECK:STDOUT: %int_7: Core.IntLiteral = int_value 7 [concrete] // CHECK:STDOUT: %struct_type.f.g.h: type = struct_type {.f: Core.IntLiteral, .g: Core.IntLiteral, .h: Core.IntLiteral} [concrete] // CHECK:STDOUT: %struct: %struct_type.f.g.h = struct_value (%int_5, %int_6, %int_7) [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: .Int = %Core.Int // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: %Core.Int: %Int.type = import_ref Core//prelude/parts/int, Int, loaded [concrete = constants.%Int.generic] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .SmallStructParam = %SmallStructParam.decl // CHECK:STDOUT: .CallSmallStructParam = %CallSmallStructParam.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %SmallStructParam.decl: %SmallStructParam.type = fn_decl @SmallStructParam [concrete = constants.%SmallStructParam] { // CHECK:STDOUT: %T.patt: %pattern_type.98f = symbolic_binding_pattern T, 0 [concrete] // CHECK:STDOUT: %x.patt: @SmallStructParam.%pattern_type (%pattern_type.958) = value_binding_pattern x [concrete] // CHECK:STDOUT: %x.param_patt: @SmallStructParam.%pattern_type (%pattern_type.958) = value_param_pattern %x.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %.loc4_25.1: type = splice_block %.loc4_25.2 [concrete = type] { // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %.loc4_25.2: type = type_literal type [concrete = type] // CHECK:STDOUT: } // CHECK:STDOUT: %T.loc4_21.2: type = symbolic_binding T, 0 [symbolic = %T.loc4_21.1 (constants.%T)] // CHECK:STDOUT: %x.param: @SmallStructParam.%struct_type.f.g.loc4_56.1 (%struct_type.f.g) = value_param call_param0 // CHECK:STDOUT: %.loc4_56: type = splice_block %struct_type.f.g.loc4_56.2 [symbolic = %struct_type.f.g.loc4_56.1 (constants.%struct_type.f.g)] { // CHECK:STDOUT: %T.ref: type = name_ref T, %T.loc4_21.2 [symbolic = %T.loc4_21.1 (constants.%T)] // CHECK:STDOUT: %i32: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %struct_type.f.g.loc4_56.2: type = struct_type {.f: @SmallStructParam.%T.loc4_21.1 (%T), .g: %i32} [symbolic = %struct_type.f.g.loc4_56.1 (constants.%struct_type.f.g)] // CHECK:STDOUT: } // CHECK:STDOUT: %x: @SmallStructParam.%struct_type.f.g.loc4_56.1 (%struct_type.f.g) = value_binding x, %x.param // CHECK:STDOUT: } // CHECK:STDOUT: %CallSmallStructParam.decl: %CallSmallStructParam.type = fn_decl @CallSmallStructParam [concrete = constants.%CallSmallStructParam] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic fn @SmallStructParam(%T.loc4_21.2: type) { // CHECK:STDOUT: %T.loc4_21.1: type = symbolic_binding T, 0 [symbolic = %T.loc4_21.1 (constants.%T)] // CHECK:STDOUT: %struct_type.f.g.loc4_56.1: type = struct_type {.f: @SmallStructParam.%T.loc4_21.1 (%T), .g: %i32} [symbolic = %struct_type.f.g.loc4_56.1 (constants.%struct_type.f.g)] // CHECK:STDOUT: %pattern_type: type = pattern_type %struct_type.f.g.loc4_56.1 [symbolic = %pattern_type (constants.%pattern_type.958)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete: = require_complete_type %struct_type.f.g.loc4_56.1 [symbolic = %require_complete (constants.%require_complete.ed5)] // CHECK:STDOUT: // CHECK:STDOUT: fn(%x.param: @SmallStructParam.%struct_type.f.g.loc4_56.1 (%struct_type.f.g)) { // CHECK:STDOUT: !entry: // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @CallSmallStructParam() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %SmallStructParam.ref: %SmallStructParam.type = name_ref SmallStructParam, file.%SmallStructParam.decl [concrete = constants.%SmallStructParam] // CHECK:STDOUT: %int_5: Core.IntLiteral = int_value 5 [concrete = constants.%int_5] // CHECK:STDOUT: %int_6: Core.IntLiteral = int_value 6 [concrete = constants.%int_6] // CHECK:STDOUT: %int_7: Core.IntLiteral = int_value 7 [concrete = constants.%int_7] // CHECK:STDOUT: %.loc14: %struct_type.f.g.h = struct_literal (%int_5, %int_6, %int_7) [concrete = constants.%struct] // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @SmallStructParam(constants.%T) { // CHECK:STDOUT: %T.loc4_21.1 => constants.%T // CHECK:STDOUT: %struct_type.f.g.loc4_56.1 => constants.%struct_type.f.g // CHECK:STDOUT: %pattern_type => constants.%pattern_type.958 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- fail_deduce_struct_wrong_name.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %type: type = facet_type [concrete] // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self] // CHECK:STDOUT: %pattern_type.98f: type = pattern_type type [concrete] // CHECK:STDOUT: %T: type = symbolic_binding T, 0 [symbolic] // CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [concrete] // CHECK:STDOUT: %Int.type: type = generic_class_type @Int [concrete] // CHECK:STDOUT: %Int.generic: %Int.type = struct_value () [concrete] // CHECK:STDOUT: %i32: type = class_type @Int, @Int(%int_32) [concrete] // CHECK:STDOUT: %struct_type.i.different: type = struct_type {.i: %T, .different: %i32} [symbolic] // CHECK:STDOUT: %pattern_type.ce4: type = pattern_type %struct_type.i.different [symbolic] // CHECK:STDOUT: %WrongNameStructParam.type: type = fn_type @WrongNameStructParam [concrete] // CHECK:STDOUT: %WrongNameStructParam: %WrongNameStructParam.type = struct_value () [concrete] // CHECK:STDOUT: %require_complete.c84: = require_complete_type %struct_type.i.different [symbolic] // CHECK:STDOUT: %CallWrongNameStructParam.type: type = fn_type @CallWrongNameStructParam [concrete] // CHECK:STDOUT: %CallWrongNameStructParam: %CallWrongNameStructParam.type = struct_value () [concrete] // CHECK:STDOUT: %int_8: Core.IntLiteral = int_value 8 [concrete] // CHECK:STDOUT: %int_9: Core.IntLiteral = int_value 9 [concrete] // CHECK:STDOUT: %struct_type.i.j: type = struct_type {.i: Core.IntLiteral, .j: Core.IntLiteral} [concrete] // CHECK:STDOUT: %struct: %struct_type.i.j = struct_value (%int_8, %int_9) [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: .Int = %Core.Int // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: %Core.Int: %Int.type = import_ref Core//prelude/parts/int, Int, loaded [concrete = constants.%Int.generic] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .WrongNameStructParam = %WrongNameStructParam.decl // CHECK:STDOUT: .CallWrongNameStructParam = %CallWrongNameStructParam.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %WrongNameStructParam.decl: %WrongNameStructParam.type = fn_decl @WrongNameStructParam [concrete = constants.%WrongNameStructParam] { // CHECK:STDOUT: %T.patt: %pattern_type.98f = symbolic_binding_pattern T, 0 [concrete] // CHECK:STDOUT: %x.patt: @WrongNameStructParam.%pattern_type (%pattern_type.ce4) = value_binding_pattern x [concrete] // CHECK:STDOUT: %x.param_patt: @WrongNameStructParam.%pattern_type (%pattern_type.ce4) = value_param_pattern %x.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %.loc4_29.1: type = splice_block %.loc4_29.2 [concrete = type] { // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %.loc4_29.2: type = type_literal type [concrete = type] // CHECK:STDOUT: } // CHECK:STDOUT: %T.loc4_25.2: type = symbolic_binding T, 0 [symbolic = %T.loc4_25.1 (constants.%T)] // CHECK:STDOUT: %x.param: @WrongNameStructParam.%struct_type.i.different.loc4_68.1 (%struct_type.i.different) = value_param call_param0 // CHECK:STDOUT: %.loc4_68: type = splice_block %struct_type.i.different.loc4_68.2 [symbolic = %struct_type.i.different.loc4_68.1 (constants.%struct_type.i.different)] { // CHECK:STDOUT: %T.ref: type = name_ref T, %T.loc4_25.2 [symbolic = %T.loc4_25.1 (constants.%T)] // CHECK:STDOUT: %i32: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %struct_type.i.different.loc4_68.2: type = struct_type {.i: @WrongNameStructParam.%T.loc4_25.1 (%T), .different: %i32} [symbolic = %struct_type.i.different.loc4_68.1 (constants.%struct_type.i.different)] // CHECK:STDOUT: } // CHECK:STDOUT: %x: @WrongNameStructParam.%struct_type.i.different.loc4_68.1 (%struct_type.i.different) = value_binding x, %x.param // CHECK:STDOUT: } // CHECK:STDOUT: %CallWrongNameStructParam.decl: %CallWrongNameStructParam.type = fn_decl @CallWrongNameStructParam [concrete = constants.%CallWrongNameStructParam] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic fn @WrongNameStructParam(%T.loc4_25.2: type) { // CHECK:STDOUT: %T.loc4_25.1: type = symbolic_binding T, 0 [symbolic = %T.loc4_25.1 (constants.%T)] // CHECK:STDOUT: %struct_type.i.different.loc4_68.1: type = struct_type {.i: @WrongNameStructParam.%T.loc4_25.1 (%T), .different: %i32} [symbolic = %struct_type.i.different.loc4_68.1 (constants.%struct_type.i.different)] // CHECK:STDOUT: %pattern_type: type = pattern_type %struct_type.i.different.loc4_68.1 [symbolic = %pattern_type (constants.%pattern_type.ce4)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete: = require_complete_type %struct_type.i.different.loc4_68.1 [symbolic = %require_complete (constants.%require_complete.c84)] // CHECK:STDOUT: // CHECK:STDOUT: fn(%x.param: @WrongNameStructParam.%struct_type.i.different.loc4_68.1 (%struct_type.i.different)) { // CHECK:STDOUT: !entry: // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @CallWrongNameStructParam() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %WrongNameStructParam.ref: %WrongNameStructParam.type = name_ref WrongNameStructParam, file.%WrongNameStructParam.decl [concrete = constants.%WrongNameStructParam] // CHECK:STDOUT: %int_8: Core.IntLiteral = int_value 8 [concrete = constants.%int_8] // CHECK:STDOUT: %int_9: Core.IntLiteral = int_value 9 [concrete = constants.%int_9] // CHECK:STDOUT: %.loc14: %struct_type.i.j = struct_literal (%int_8, %int_9) [concrete = constants.%struct] // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @WrongNameStructParam(constants.%T) { // CHECK:STDOUT: %T.loc4_25.1 => constants.%T // CHECK:STDOUT: %struct_type.i.different.loc4_68.1 => constants.%struct_type.i.different // CHECK:STDOUT: %pattern_type => constants.%pattern_type.ce4 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- fail_todo_deduce_struct_wrong_order.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %type: type = facet_type [concrete] // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self] // CHECK:STDOUT: %pattern_type.98f: type = pattern_type type [concrete] // CHECK:STDOUT: %T: type = symbolic_binding T, 0 [symbolic] // CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [concrete] // CHECK:STDOUT: %Int.type: type = generic_class_type @Int [concrete] // CHECK:STDOUT: %Int.generic: %Int.type = struct_value () [concrete] // CHECK:STDOUT: %i32: type = class_type @Int, @Int(%int_32) [concrete] // CHECK:STDOUT: %struct_type.first.second: type = struct_type {.first: %T, .second: %i32} [symbolic] // CHECK:STDOUT: %pattern_type.ab3: type = pattern_type %struct_type.first.second [symbolic] // CHECK:STDOUT: %WrongOrderStructParam.type: type = fn_type @WrongOrderStructParam [concrete] // CHECK:STDOUT: %WrongOrderStructParam: %WrongOrderStructParam.type = struct_value () [concrete] // CHECK:STDOUT: %require_complete.311: = require_complete_type %struct_type.first.second [symbolic] // CHECK:STDOUT: %CallWrongOrderStructParam.type: type = fn_type @CallWrongOrderStructParam [concrete] // CHECK:STDOUT: %CallWrongOrderStructParam: %CallWrongOrderStructParam.type = struct_value () [concrete] // CHECK:STDOUT: %int_11: Core.IntLiteral = int_value 11 [concrete] // CHECK:STDOUT: %int_10: Core.IntLiteral = int_value 10 [concrete] // CHECK:STDOUT: %struct_type.second.first: type = struct_type {.second: Core.IntLiteral, .first: Core.IntLiteral} [concrete] // CHECK:STDOUT: %struct: %struct_type.second.first = struct_value (%int_11, %int_10) [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: .Int = %Core.Int // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: %Core.Int: %Int.type = import_ref Core//prelude/parts/int, Int, loaded [concrete = constants.%Int.generic] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .WrongOrderStructParam = %WrongOrderStructParam.decl // CHECK:STDOUT: .CallWrongOrderStructParam = %CallWrongOrderStructParam.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %WrongOrderStructParam.decl: %WrongOrderStructParam.type = fn_decl @WrongOrderStructParam [concrete = constants.%WrongOrderStructParam] { // CHECK:STDOUT: %T.patt: %pattern_type.98f = symbolic_binding_pattern T, 0 [concrete] // CHECK:STDOUT: %x.patt: @WrongOrderStructParam.%pattern_type (%pattern_type.ab3) = value_binding_pattern x [concrete] // CHECK:STDOUT: %x.param_patt: @WrongOrderStructParam.%pattern_type (%pattern_type.ab3) = value_param_pattern %x.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %.loc4_30.1: type = splice_block %.loc4_30.2 [concrete = type] { // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %.loc4_30.2: type = type_literal type [concrete = type] // CHECK:STDOUT: } // CHECK:STDOUT: %T.loc4_26.2: type = symbolic_binding T, 0 [symbolic = %T.loc4_26.1 (constants.%T)] // CHECK:STDOUT: %x.param: @WrongOrderStructParam.%struct_type.first.second.loc4_70.1 (%struct_type.first.second) = value_param call_param0 // CHECK:STDOUT: %.loc4_70: type = splice_block %struct_type.first.second.loc4_70.2 [symbolic = %struct_type.first.second.loc4_70.1 (constants.%struct_type.first.second)] { // CHECK:STDOUT: %T.ref: type = name_ref T, %T.loc4_26.2 [symbolic = %T.loc4_26.1 (constants.%T)] // CHECK:STDOUT: %i32: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %struct_type.first.second.loc4_70.2: type = struct_type {.first: @WrongOrderStructParam.%T.loc4_26.1 (%T), .second: %i32} [symbolic = %struct_type.first.second.loc4_70.1 (constants.%struct_type.first.second)] // CHECK:STDOUT: } // CHECK:STDOUT: %x: @WrongOrderStructParam.%struct_type.first.second.loc4_70.1 (%struct_type.first.second) = value_binding x, %x.param // CHECK:STDOUT: } // CHECK:STDOUT: %CallWrongOrderStructParam.decl: %CallWrongOrderStructParam.type = fn_decl @CallWrongOrderStructParam [concrete = constants.%CallWrongOrderStructParam] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic fn @WrongOrderStructParam(%T.loc4_26.2: type) { // CHECK:STDOUT: %T.loc4_26.1: type = symbolic_binding T, 0 [symbolic = %T.loc4_26.1 (constants.%T)] // CHECK:STDOUT: %struct_type.first.second.loc4_70.1: type = struct_type {.first: @WrongOrderStructParam.%T.loc4_26.1 (%T), .second: %i32} [symbolic = %struct_type.first.second.loc4_70.1 (constants.%struct_type.first.second)] // CHECK:STDOUT: %pattern_type: type = pattern_type %struct_type.first.second.loc4_70.1 [symbolic = %pattern_type (constants.%pattern_type.ab3)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete: = require_complete_type %struct_type.first.second.loc4_70.1 [symbolic = %require_complete (constants.%require_complete.311)] // CHECK:STDOUT: // CHECK:STDOUT: fn(%x.param: @WrongOrderStructParam.%struct_type.first.second.loc4_70.1 (%struct_type.first.second)) { // CHECK:STDOUT: !entry: // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @CallWrongOrderStructParam() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %WrongOrderStructParam.ref: %WrongOrderStructParam.type = name_ref WrongOrderStructParam, file.%WrongOrderStructParam.decl [concrete = constants.%WrongOrderStructParam] // CHECK:STDOUT: %int_11: Core.IntLiteral = int_value 11 [concrete = constants.%int_11] // CHECK:STDOUT: %int_10: Core.IntLiteral = int_value 10 [concrete = constants.%int_10] // CHECK:STDOUT: %.loc14: %struct_type.second.first = struct_literal (%int_11, %int_10) [concrete = constants.%struct] // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @WrongOrderStructParam(constants.%T) { // CHECK:STDOUT: %T.loc4_26.1 => constants.%T // CHECK:STDOUT: %struct_type.first.second.loc4_70.1 => constants.%struct_type.first.second // CHECK:STDOUT: %pattern_type => constants.%pattern_type.ab3 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- fail_deduce_incomplete.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %type: type = facet_type [concrete] // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self] // CHECK:STDOUT: %pattern_type.98f: type = pattern_type type [concrete] // CHECK:STDOUT: %T: type = symbolic_binding T, 0 [symbolic] // CHECK:STDOUT: %U: type = symbolic_binding U, 1 [symbolic] // CHECK:STDOUT: %pattern_type.51d: type = pattern_type %T [symbolic] // CHECK:STDOUT: %.822: Core.Form = init_form %U [symbolic] // CHECK:STDOUT: %pattern_type.946: type = pattern_type %U [symbolic] // CHECK:STDOUT: %ImplicitNotDeducible.type: type = fn_type @ImplicitNotDeducible [concrete] // CHECK:STDOUT: %ImplicitNotDeducible: %ImplicitNotDeducible.type = struct_value () [concrete] // CHECK:STDOUT: %CallImplicitNotDeducible.type: type = fn_type @CallImplicitNotDeducible [concrete] // CHECK:STDOUT: %CallImplicitNotDeducible: %CallImplicitNotDeducible.type = struct_value () [concrete] // CHECK:STDOUT: %int_42: Core.IntLiteral = int_value 42 [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .ImplicitNotDeducible = %ImplicitNotDeducible.decl // CHECK:STDOUT: .CallImplicitNotDeducible = %CallImplicitNotDeducible.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %ImplicitNotDeducible.decl: %ImplicitNotDeducible.type = fn_decl @ImplicitNotDeducible [concrete = constants.%ImplicitNotDeducible] { // CHECK:STDOUT: %T.patt: %pattern_type.98f = symbolic_binding_pattern T, 0 [concrete] // CHECK:STDOUT: %U.patt: %pattern_type.98f = symbolic_binding_pattern U, 1 [concrete] // CHECK:STDOUT: %x.patt: @ImplicitNotDeducible.%pattern_type.loc6_45 (%pattern_type.51d) = value_binding_pattern x [concrete] // CHECK:STDOUT: %x.param_patt: @ImplicitNotDeducible.%pattern_type.loc6_45 (%pattern_type.51d) = value_param_pattern %x.patt [concrete] // CHECK:STDOUT: %return.patt: @ImplicitNotDeducible.%pattern_type.loc6_51 (%pattern_type.946) = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: @ImplicitNotDeducible.%pattern_type.loc6_51 (%pattern_type.946) = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %U.ref: type = name_ref U, %U.loc6_35.2 [symbolic = %U.loc6_35.1 (constants.%U)] // CHECK:STDOUT: %.loc6_54.2: Core.Form = init_form %U.ref [symbolic = %.loc6_54.1 (constants.%.822)] // CHECK:STDOUT: %.loc6_29.1: type = splice_block %.loc6_29.2 [concrete = type] { // CHECK:STDOUT: %.Self.2: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %.loc6_29.2: type = type_literal type [concrete = type] // CHECK:STDOUT: } // CHECK:STDOUT: %T.loc6_25.2: type = symbolic_binding T, 0 [symbolic = %T.loc6_25.1 (constants.%T)] // CHECK:STDOUT: %.loc6_39.1: type = splice_block %.loc6_39.2 [concrete = type] { // CHECK:STDOUT: %.Self.1: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %.loc6_39.2: type = type_literal type [concrete = type] // CHECK:STDOUT: } // CHECK:STDOUT: %U.loc6_35.2: type = symbolic_binding U, 1 [symbolic = %U.loc6_35.1 (constants.%U)] // CHECK:STDOUT: %x.param: @ImplicitNotDeducible.%T.loc6_25.1 (%T) = value_param call_param0 // CHECK:STDOUT: %T.ref: type = name_ref T, %T.loc6_25.2 [symbolic = %T.loc6_25.1 (constants.%T)] // CHECK:STDOUT: %x: @ImplicitNotDeducible.%T.loc6_25.1 (%T) = value_binding x, %x.param // CHECK:STDOUT: %return.param: ref @ImplicitNotDeducible.%U.loc6_35.1 (%U) = out_param call_param1 // CHECK:STDOUT: %return: ref @ImplicitNotDeducible.%U.loc6_35.1 (%U) = return_slot %return.param // CHECK:STDOUT: } // CHECK:STDOUT: %CallImplicitNotDeducible.decl: %CallImplicitNotDeducible.type = fn_decl @CallImplicitNotDeducible [concrete = constants.%CallImplicitNotDeducible] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic fn @ImplicitNotDeducible(%T.loc6_25.2: type, %U.loc6_35.2: type) { // CHECK:STDOUT: %T.loc6_25.1: type = symbolic_binding T, 0 [symbolic = %T.loc6_25.1 (constants.%T)] // CHECK:STDOUT: %U.loc6_35.1: type = symbolic_binding U, 1 [symbolic = %U.loc6_35.1 (constants.%U)] // CHECK:STDOUT: %pattern_type.loc6_45: type = pattern_type %T.loc6_25.1 [symbolic = %pattern_type.loc6_45 (constants.%pattern_type.51d)] // CHECK:STDOUT: %.loc6_54.1: Core.Form = init_form %U.loc6_35.1 [symbolic = %.loc6_54.1 (constants.%.822)] // CHECK:STDOUT: %pattern_type.loc6_51: type = pattern_type %U.loc6_35.1 [symbolic = %pattern_type.loc6_51 (constants.%pattern_type.946)] // CHECK:STDOUT: // CHECK:STDOUT: fn(%x.param: @ImplicitNotDeducible.%T.loc6_25.1 (%T)) -> out %return.param: @ImplicitNotDeducible.%U.loc6_35.1 (%U); // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @CallImplicitNotDeducible() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %ImplicitNotDeducible.ref: %ImplicitNotDeducible.type = name_ref ImplicitNotDeducible, file.%ImplicitNotDeducible.decl [concrete = constants.%ImplicitNotDeducible] // CHECK:STDOUT: %int_42: Core.IntLiteral = int_value 42 [concrete = constants.%int_42] // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @ImplicitNotDeducible(constants.%T, constants.%U) { // CHECK:STDOUT: %T.loc6_25.1 => constants.%T // CHECK:STDOUT: %U.loc6_35.1 => constants.%U // CHECK:STDOUT: %pattern_type.loc6_45 => constants.%pattern_type.51d // CHECK:STDOUT: %.loc6_54.1 => constants.%.822 // CHECK:STDOUT: %pattern_type.loc6_51 => constants.%pattern_type.946 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- fail_deduce_inconsistent.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %type: type = facet_type [concrete] // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self] // CHECK:STDOUT: %pattern_type.98f: type = pattern_type type [concrete] // CHECK:STDOUT: %T: type = symbolic_binding T, 0 [symbolic] // CHECK:STDOUT: %pattern_type.51d: type = pattern_type %T [symbolic] // CHECK:STDOUT: %.184: Core.Form = init_form %T [symbolic] // CHECK:STDOUT: %ImplicitNotDeducible.type: type = fn_type @ImplicitNotDeducible [concrete] // CHECK:STDOUT: %ImplicitNotDeducible: %ImplicitNotDeducible.type = struct_value () [concrete] // CHECK:STDOUT: %CallImplicitNotDeducible.type: type = fn_type @CallImplicitNotDeducible [concrete] // CHECK:STDOUT: %CallImplicitNotDeducible: %CallImplicitNotDeducible.type = struct_value () [concrete] // CHECK:STDOUT: %int_42: Core.IntLiteral = int_value 42 [concrete] // CHECK:STDOUT: %int_12: Core.IntLiteral = int_value 12 [concrete] // CHECK:STDOUT: %struct_type.x: type = struct_type {.x: Core.IntLiteral} [concrete] // CHECK:STDOUT: %struct: %struct_type.x = struct_value (%int_12) [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .ImplicitNotDeducible = %ImplicitNotDeducible.decl // CHECK:STDOUT: .CallImplicitNotDeducible = %CallImplicitNotDeducible.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %ImplicitNotDeducible.decl: %ImplicitNotDeducible.type = fn_decl @ImplicitNotDeducible [concrete = constants.%ImplicitNotDeducible] { // CHECK:STDOUT: %T.patt: %pattern_type.98f = symbolic_binding_pattern T, 0 [concrete] // CHECK:STDOUT: %x.patt: @ImplicitNotDeducible.%pattern_type (%pattern_type.51d) = value_binding_pattern x [concrete] // CHECK:STDOUT: %x.param_patt: @ImplicitNotDeducible.%pattern_type (%pattern_type.51d) = value_param_pattern %x.patt [concrete] // CHECK:STDOUT: %y.patt: @ImplicitNotDeducible.%pattern_type (%pattern_type.51d) = value_binding_pattern y [concrete] // CHECK:STDOUT: %y.param_patt: @ImplicitNotDeducible.%pattern_type (%pattern_type.51d) = value_param_pattern %y.patt [concrete] // CHECK:STDOUT: %return.patt: @ImplicitNotDeducible.%pattern_type (%pattern_type.51d) = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: @ImplicitNotDeducible.%pattern_type (%pattern_type.51d) = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %T.ref.loc4_50: type = name_ref T, %T.loc4_25.2 [symbolic = %T.loc4_25.1 (constants.%T)] // CHECK:STDOUT: %.loc4_50.2: Core.Form = init_form %T.ref.loc4_50 [symbolic = %.loc4_50.1 (constants.%.184)] // CHECK:STDOUT: %.loc4_29.1: type = splice_block %.loc4_29.2 [concrete = type] { // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %.loc4_29.2: type = type_literal type [concrete = type] // CHECK:STDOUT: } // CHECK:STDOUT: %T.loc4_25.2: type = symbolic_binding T, 0 [symbolic = %T.loc4_25.1 (constants.%T)] // CHECK:STDOUT: %x.param: @ImplicitNotDeducible.%T.loc4_25.1 (%T) = value_param call_param0 // CHECK:STDOUT: %T.ref.loc4_38: type = name_ref T, %T.loc4_25.2 [symbolic = %T.loc4_25.1 (constants.%T)] // CHECK:STDOUT: %x: @ImplicitNotDeducible.%T.loc4_25.1 (%T) = value_binding x, %x.param // CHECK:STDOUT: %y.param: @ImplicitNotDeducible.%T.loc4_25.1 (%T) = value_param call_param1 // CHECK:STDOUT: %T.ref.loc4_44: type = name_ref T, %T.loc4_25.2 [symbolic = %T.loc4_25.1 (constants.%T)] // CHECK:STDOUT: %y: @ImplicitNotDeducible.%T.loc4_25.1 (%T) = value_binding y, %y.param // CHECK:STDOUT: %return.param: ref @ImplicitNotDeducible.%T.loc4_25.1 (%T) = out_param call_param2 // CHECK:STDOUT: %return: ref @ImplicitNotDeducible.%T.loc4_25.1 (%T) = return_slot %return.param // CHECK:STDOUT: } // CHECK:STDOUT: %CallImplicitNotDeducible.decl: %CallImplicitNotDeducible.type = fn_decl @CallImplicitNotDeducible [concrete = constants.%CallImplicitNotDeducible] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic fn @ImplicitNotDeducible(%T.loc4_25.2: type) { // CHECK:STDOUT: %T.loc4_25.1: type = symbolic_binding T, 0 [symbolic = %T.loc4_25.1 (constants.%T)] // CHECK:STDOUT: %pattern_type: type = pattern_type %T.loc4_25.1 [symbolic = %pattern_type (constants.%pattern_type.51d)] // CHECK:STDOUT: %.loc4_50.1: Core.Form = init_form %T.loc4_25.1 [symbolic = %.loc4_50.1 (constants.%.184)] // CHECK:STDOUT: // CHECK:STDOUT: fn(%x.param: @ImplicitNotDeducible.%T.loc4_25.1 (%T), %y.param: @ImplicitNotDeducible.%T.loc4_25.1 (%T)) -> out %return.param: @ImplicitNotDeducible.%T.loc4_25.1 (%T); // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @CallImplicitNotDeducible() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %ImplicitNotDeducible.ref: %ImplicitNotDeducible.type = name_ref ImplicitNotDeducible, file.%ImplicitNotDeducible.decl [concrete = constants.%ImplicitNotDeducible] // CHECK:STDOUT: %int_42: Core.IntLiteral = int_value 42 [concrete = constants.%int_42] // CHECK:STDOUT: %int_12: Core.IntLiteral = int_value 12 [concrete = constants.%int_12] // CHECK:STDOUT: %.loc14: %struct_type.x = struct_literal (%int_12) [concrete = constants.%struct] // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @ImplicitNotDeducible(constants.%T) { // CHECK:STDOUT: %T.loc4_25.1 => constants.%T // CHECK:STDOUT: %pattern_type => constants.%pattern_type.51d // CHECK:STDOUT: %.loc4_50.1 => constants.%.184 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- deduce_nested_generic_class.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %Z.type: type = facet_type <@Z> [concrete] // CHECK:STDOUT: %Self: %Z.type = symbolic_binding Self, 0 [symbolic] // CHECK:STDOUT: %EE: type = class_type @EE [concrete] // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %complete_type: = complete_type_witness %empty_struct_type [concrete] // CHECK:STDOUT: %Z.impl_witness.c05: = impl_witness @EE.as.Z.impl.%Z.impl_witness_table [concrete] // CHECK:STDOUT: %Z.facet.5dc: %Z.type = facet_value %EE, (%Z.impl_witness.c05) [concrete] // CHECK:STDOUT: %type: type = facet_type [concrete] // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self] // CHECK:STDOUT: %pattern_type.98f: type = pattern_type type [concrete] // CHECK:STDOUT: %E: type = symbolic_binding E, 0 [symbolic] // CHECK:STDOUT: %DD.type: type = generic_class_type @DD [concrete] // CHECK:STDOUT: %DD.generic: %DD.type = struct_value () [concrete] // CHECK:STDOUT: %DD.a29: type = class_type @DD, @DD(%E) [symbolic] // CHECK:STDOUT: %Z.impl_witness.437: = impl_witness @DD.as.Z.impl.%Z.impl_witness_table, @DD.as.Z.impl(%E) [symbolic] // CHECK:STDOUT: %Z.facet.dc0: %Z.type = facet_value %DD.a29, (%Z.impl_witness.437) [symbolic] // CHECK:STDOUT: %pattern_type.22b: type = pattern_type %Z.type [concrete] // CHECK:STDOUT: %D: %Z.type = symbolic_binding D, 0 [symbolic] // CHECK:STDOUT: %CC.type: type = generic_class_type @CC [concrete] // CHECK:STDOUT: %CC.generic: %CC.type = struct_value () [concrete] // CHECK:STDOUT: %CC.a1b: type = class_type @CC, @CC(%D) [symbolic] // CHECK:STDOUT: %.bf4: require_specific_def_type = require_specific_def @DD.as.Z.impl(%E) [symbolic] // CHECK:STDOUT: %Z.lookup_impl_witness: = lookup_impl_witness %DD.a29, @Z [symbolic] // CHECK:STDOUT: %Z.facet.892: %Z.type = facet_value %DD.a29, (%Z.lookup_impl_witness) [symbolic] // CHECK:STDOUT: %CC.4e1: type = class_type @CC, @CC(%Z.facet.892) [symbolic] // CHECK:STDOUT: %Z.impl_witness.40e: = impl_witness @CC.as.Z.impl.%Z.impl_witness_table, @CC.as.Z.impl(%E) [symbolic] // CHECK:STDOUT: %Z.facet.b7b: %Z.type = facet_value %CC.4e1, (%Z.impl_witness.40e) [symbolic] // CHECK:STDOUT: %F.type: type = fn_type @F [concrete] // CHECK:STDOUT: %F: %F.type = struct_value () [concrete] // CHECK:STDOUT: %DD.2e1: type = class_type @DD, @DD(%EE) [concrete] // CHECK:STDOUT: %Z.impl_witness.8a3: = impl_witness @DD.as.Z.impl.%Z.impl_witness_table, @DD.as.Z.impl(%EE) [concrete] // CHECK:STDOUT: %.36d: require_specific_def_type = require_specific_def @DD.as.Z.impl(%EE) [concrete] // CHECK:STDOUT: %Z.facet.74c: %Z.type = facet_value %DD.2e1, (%Z.impl_witness.8a3) [concrete] // CHECK:STDOUT: %CC.b19: type = class_type @CC, @CC(%Z.facet.74c) [concrete] // CHECK:STDOUT: %Z.impl_witness.cb4: = impl_witness @CC.as.Z.impl.%Z.impl_witness_table, @CC.as.Z.impl(%EE) [concrete] // CHECK:STDOUT: %Z.facet.145: %Z.type = facet_value %CC.b19, (%Z.impl_witness.cb4) [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .Z = %Z.decl // CHECK:STDOUT: .EE = %EE.decl // CHECK:STDOUT: .DD = %DD.decl // CHECK:STDOUT: .CC = %CC.decl // CHECK:STDOUT: .F = %F.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %Z.decl: type = interface_decl @Z [concrete = constants.%Z.type] {} {} // CHECK:STDOUT: %EE.decl: type = class_decl @EE [concrete = constants.%EE] {} {} // CHECK:STDOUT: impl_decl @EE.as.Z.impl [concrete] {} { // CHECK:STDOUT: %EE.ref: type = name_ref EE, file.%EE.decl [concrete = constants.%EE] // CHECK:STDOUT: %Z.ref: type = name_ref Z, file.%Z.decl [concrete = constants.%Z.type] // CHECK:STDOUT: } // CHECK:STDOUT: %DD.decl: %DD.type = class_decl @DD [concrete = constants.%DD.generic] { // CHECK:STDOUT: %E.patt: %pattern_type.98f = symbolic_binding_pattern E, 0 [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %.loc8_14.1: type = splice_block %.loc8_14.2 [concrete = type] { // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %.loc8_14.2: type = type_literal type [concrete = type] // CHECK:STDOUT: } // CHECK:STDOUT: %E.loc8_10.2: type = symbolic_binding E, 0 [symbolic = %E.loc8_10.1 (constants.%E)] // CHECK:STDOUT: } // CHECK:STDOUT: impl_decl @DD.as.Z.impl [concrete] { // CHECK:STDOUT: %E.patt: %pattern_type.98f = symbolic_binding_pattern E, 0 [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %DD.ref: %DD.type = name_ref DD, file.%DD.decl [concrete = constants.%DD.generic] // CHECK:STDOUT: %E.ref: type = name_ref E, %E.loc9_14.1 [symbolic = %E.loc9_14.2 (constants.%E)] // CHECK:STDOUT: %DD.loc9_28.1: type = class_type @DD, @DD(constants.%E) [symbolic = %DD.loc9_28.2 (constants.%DD.a29)] // CHECK:STDOUT: %Z.ref: type = name_ref Z, file.%Z.decl [concrete = constants.%Z.type] // CHECK:STDOUT: %.loc9_18.1: type = splice_block %.loc9_18.2 [concrete = type] { // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %.loc9_18.2: type = type_literal type [concrete = type] // CHECK:STDOUT: } // CHECK:STDOUT: %E.loc9_14.1: type = symbolic_binding E, 0 [symbolic = %E.loc9_14.2 (constants.%E)] // CHECK:STDOUT: } // CHECK:STDOUT: %CC.decl: %CC.type = class_decl @CC [concrete = constants.%CC.generic] { // CHECK:STDOUT: %D.patt: %pattern_type.22b = symbolic_binding_pattern D, 0 [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %.loc11: type = splice_block %Z.ref [concrete = constants.%Z.type] { // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %Z.ref: type = name_ref Z, file.%Z.decl [concrete = constants.%Z.type] // CHECK:STDOUT: } // CHECK:STDOUT: %D.loc11_10.2: %Z.type = symbolic_binding D, 0 [symbolic = %D.loc11_10.1 (constants.%D)] // CHECK:STDOUT: } // CHECK:STDOUT: impl_decl @CC.as.Z.impl [concrete] { // CHECK:STDOUT: %E.patt: %pattern_type.98f = symbolic_binding_pattern E, 0 [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %CC.ref: %CC.type = name_ref CC, file.%CC.decl [concrete = constants.%CC.generic] // CHECK:STDOUT: %DD.ref: %DD.type = name_ref DD, file.%DD.decl [concrete = constants.%DD.generic] // CHECK:STDOUT: %E.ref: type = name_ref E, %E.loc12_14.1 [symbolic = %E.loc12_14.2 (constants.%E)] // CHECK:STDOUT: %DD.loc12_31.1: type = class_type @DD, @DD(constants.%E) [symbolic = %DD.loc12_31.2 (constants.%DD.a29)] // CHECK:STDOUT: %Z.facet.loc12_32.1: %Z.type = facet_value %DD.loc12_31.1, (constants.%Z.lookup_impl_witness) [symbolic = %Z.facet.loc12_32.2 (constants.%Z.facet.892)] // CHECK:STDOUT: %.loc12_32.1: %Z.type = converted %DD.loc12_31.1, %Z.facet.loc12_32.1 [symbolic = %Z.facet.loc12_32.2 (constants.%Z.facet.892)] // CHECK:STDOUT: %CC.loc12_32.1: type = class_type @CC, @CC(constants.%Z.facet.892) [symbolic = %CC.loc12_32.2 (constants.%CC.4e1)] // CHECK:STDOUT: %Z.ref: type = name_ref Z, file.%Z.decl [concrete = constants.%Z.type] // CHECK:STDOUT: %.loc12_18.1: type = splice_block %.loc12_18.2 [concrete = type] { // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %.loc12_18.2: type = type_literal type [concrete = type] // CHECK:STDOUT: } // CHECK:STDOUT: %E.loc12_14.1: type = symbolic_binding E, 0 [symbolic = %E.loc12_14.2 (constants.%E)] // CHECK:STDOUT: } // CHECK:STDOUT: %F.decl: %F.type = fn_decl @F [concrete = constants.%F] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: interface @Z { // CHECK:STDOUT: %Self: %Z.type = symbolic_binding Self, 0 [symbolic = constants.%Self] // CHECK:STDOUT: %Z.WithSelf.decl = interface_with_self_decl @Z [concrete] // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = %Self // CHECK:STDOUT: witness = () // CHECK:STDOUT: // CHECK:STDOUT: !requires: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: impl @EE.as.Z.impl: %EE.ref as %Z.ref { // CHECK:STDOUT: %Z.impl_witness_table = impl_witness_table (), @EE.as.Z.impl [concrete] // CHECK:STDOUT: %Z.impl_witness: = impl_witness %Z.impl_witness_table [concrete = constants.%Z.impl_witness.c05] // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: witness = %Z.impl_witness // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic impl @DD.as.Z.impl(%E.loc9_14.1: type) { // CHECK:STDOUT: %E.loc9_14.2: type = symbolic_binding E, 0 [symbolic = %E.loc9_14.2 (constants.%E)] // CHECK:STDOUT: %DD.loc9_28.2: type = class_type @DD, @DD(%E.loc9_14.2) [symbolic = %DD.loc9_28.2 (constants.%DD.a29)] // CHECK:STDOUT: %Z.impl_witness.loc9_35.2: = impl_witness %Z.impl_witness_table, @DD.as.Z.impl(%E.loc9_14.2) [symbolic = %Z.impl_witness.loc9_35.2 (constants.%Z.impl_witness.437)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: // CHECK:STDOUT: impl: %DD.loc9_28.1 as %Z.ref { // CHECK:STDOUT: %Z.impl_witness_table = impl_witness_table (), @DD.as.Z.impl [concrete] // CHECK:STDOUT: %Z.impl_witness.loc9_35.1: = impl_witness %Z.impl_witness_table, @DD.as.Z.impl(constants.%E) [symbolic = %Z.impl_witness.loc9_35.2 (constants.%Z.impl_witness.437)] // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: witness = %Z.impl_witness.loc9_35.1 // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic impl @CC.as.Z.impl(%E.loc12_14.1: type) { // CHECK:STDOUT: %E.loc12_14.2: type = symbolic_binding E, 0 [symbolic = %E.loc12_14.2 (constants.%E)] // CHECK:STDOUT: %DD.loc12_31.2: type = class_type @DD, @DD(%E.loc12_14.2) [symbolic = %DD.loc12_31.2 (constants.%DD.a29)] // CHECK:STDOUT: %.loc12_32.2: require_specific_def_type = require_specific_def @DD.as.Z.impl(%E.loc12_14.2) [symbolic = %.loc12_32.2 (constants.%.bf4)] // CHECK:STDOUT: %Z.lookup_impl_witness: = lookup_impl_witness %DD.loc12_31.2, @Z [symbolic = %Z.lookup_impl_witness (constants.%Z.lookup_impl_witness)] // CHECK:STDOUT: %Z.facet.loc12_32.2: %Z.type = facet_value %DD.loc12_31.2, (%Z.lookup_impl_witness) [symbolic = %Z.facet.loc12_32.2 (constants.%Z.facet.892)] // CHECK:STDOUT: %CC.loc12_32.2: type = class_type @CC, @CC(%Z.facet.loc12_32.2) [symbolic = %CC.loc12_32.2 (constants.%CC.4e1)] // CHECK:STDOUT: %Z.impl_witness.loc12_39.2: = impl_witness %Z.impl_witness_table, @CC.as.Z.impl(%E.loc12_14.2) [symbolic = %Z.impl_witness.loc12_39.2 (constants.%Z.impl_witness.40e)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: // CHECK:STDOUT: impl: %CC.loc12_32.1 as %Z.ref { // CHECK:STDOUT: %Z.impl_witness_table = impl_witness_table (), @CC.as.Z.impl [concrete] // CHECK:STDOUT: %Z.impl_witness.loc12_39.1: = impl_witness %Z.impl_witness_table, @CC.as.Z.impl(constants.%E) [symbolic = %Z.impl_witness.loc12_39.2 (constants.%Z.impl_witness.40e)] // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: witness = %Z.impl_witness.loc12_39.1 // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @EE { // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%EE // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic class @DD(%E.loc8_10.2: type) { // CHECK:STDOUT: %E.loc8_10.1: type = symbolic_binding E, 0 [symbolic = %E.loc8_10.1 (constants.%E)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: // CHECK:STDOUT: class { // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%DD.a29 // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic class @CC(%D.loc11_10.2: %Z.type) { // CHECK:STDOUT: %D.loc11_10.1: %Z.type = symbolic_binding D, 0 [symbolic = %D.loc11_10.1 (constants.%D)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: // CHECK:STDOUT: class { // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%CC.a1b // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @F() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %CC.ref: %CC.type = name_ref CC, file.%CC.decl [concrete = constants.%CC.generic] // CHECK:STDOUT: %DD.ref: %DD.type = name_ref DD, file.%DD.decl [concrete = constants.%DD.generic] // CHECK:STDOUT: %EE.ref: type = name_ref EE, file.%EE.decl [concrete = constants.%EE] // CHECK:STDOUT: %DD: type = class_type @DD, @DD(constants.%EE) [concrete = constants.%DD.2e1] // CHECK:STDOUT: %Z.facet.loc15_12: %Z.type = facet_value %DD, (constants.%Z.impl_witness.8a3) [concrete = constants.%Z.facet.74c] // CHECK:STDOUT: %.loc15_12: %Z.type = converted %DD, %Z.facet.loc15_12 [concrete = constants.%Z.facet.74c] // CHECK:STDOUT: %CC: type = class_type @CC, @CC(constants.%Z.facet.74c) [concrete = constants.%CC.b19] // CHECK:STDOUT: %Z.ref: type = name_ref Z, file.%Z.decl [concrete = constants.%Z.type] // CHECK:STDOUT: %Z.facet.loc15_14: %Z.type = facet_value %CC, (constants.%Z.impl_witness.cb4) [concrete = constants.%Z.facet.145] // CHECK:STDOUT: %.loc15_14: %Z.type = converted %CC, %Z.facet.loc15_14 [concrete = constants.%Z.facet.145] // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @Z.WithSelf(constants.%Self) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @Z.WithSelf(constants.%Z.facet.5dc) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @DD(constants.%E) { // CHECK:STDOUT: %E.loc8_10.1 => constants.%E // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @DD.as.Z.impl(constants.%E) { // CHECK:STDOUT: %E.loc9_14.2 => constants.%E // CHECK:STDOUT: %DD.loc9_28.2 => constants.%DD.a29 // CHECK:STDOUT: %Z.impl_witness.loc9_35.2 => constants.%Z.impl_witness.437 // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @Z.WithSelf(constants.%Z.facet.dc0) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @CC(constants.%D) { // CHECK:STDOUT: %D.loc11_10.1 => constants.%D // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @CC(constants.%Z.facet.892) { // CHECK:STDOUT: %D.loc11_10.1 => constants.%Z.facet.892 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @CC.as.Z.impl(constants.%E) { // CHECK:STDOUT: %E.loc12_14.2 => constants.%E // CHECK:STDOUT: %DD.loc12_31.2 => constants.%DD.a29 // CHECK:STDOUT: %.loc12_32.2 => constants.%.bf4 // CHECK:STDOUT: %Z.lookup_impl_witness => constants.%Z.lookup_impl_witness // CHECK:STDOUT: %Z.facet.loc12_32.2 => constants.%Z.facet.892 // CHECK:STDOUT: %CC.loc12_32.2 => constants.%CC.4e1 // CHECK:STDOUT: %Z.impl_witness.loc12_39.2 => constants.%Z.impl_witness.40e // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @Z.WithSelf(constants.%Z.facet.b7b) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @DD(constants.%EE) { // CHECK:STDOUT: %E.loc8_10.1 => constants.%EE // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @DD.as.Z.impl(constants.%EE) { // CHECK:STDOUT: %E.loc9_14.2 => constants.%EE // CHECK:STDOUT: %DD.loc9_28.2 => constants.%DD.2e1 // CHECK:STDOUT: %Z.impl_witness.loc9_35.2 => constants.%Z.impl_witness.8a3 // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @CC(constants.%Z.facet.74c) { // CHECK:STDOUT: %D.loc11_10.1 => constants.%Z.facet.74c // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @CC.as.Z.impl(constants.%EE) { // CHECK:STDOUT: %E.loc12_14.2 => constants.%EE // CHECK:STDOUT: %DD.loc12_31.2 => constants.%DD.2e1 // CHECK:STDOUT: %.loc12_32.2 => constants.%.36d // CHECK:STDOUT: %Z.lookup_impl_witness => constants.%Z.impl_witness.8a3 // CHECK:STDOUT: %Z.facet.loc12_32.2 => constants.%Z.facet.74c // CHECK:STDOUT: %CC.loc12_32.2 => constants.%CC.b19 // CHECK:STDOUT: %Z.impl_witness.loc12_39.2 => constants.%Z.impl_witness.cb4 // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: } // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/function/generic/deduce_nested_facet_value.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/convert.carbon // TODO: Add ranges and switch to "--dump-sem-ir-ranges=only". // EXTRA-ARGS: --dump-sem-ir-ranges=if-present // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/function/generic/deduce_nested_facet_value.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/function/generic/deduce_nested_facet_value.carbon // --- deduce_nested_facet_value.carbon library "[[@TEST_NAME]]"; interface Y {} interface W {} // DD implements both Y and W. class DD {} impl DD as Y {} impl DD as W {} // CC requires D to implement Y. class CC(D:! Y) {} interface Z {} // The `D` interface provides `Y` but not `W`, so we need to see that the // parameter to `CC` is `DD` which provides `Y & W`, not just a FacetValue // abstractly providing `Y. impl forall [E:! Y & W] CC(E) as Z {} fn F() { (CC(DD)) as Z; } // CHECK:STDOUT: --- deduce_nested_facet_value.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %Y.type: type = facet_type <@Y> [concrete] // CHECK:STDOUT: %Self.162: %Y.type = symbolic_binding Self, 0 [symbolic] // CHECK:STDOUT: %W.type: type = facet_type <@W> [concrete] // CHECK:STDOUT: %Self.62a: %W.type = symbolic_binding Self, 0 [symbolic] // CHECK:STDOUT: %DD: type = class_type @DD [concrete] // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %complete_type: = complete_type_witness %empty_struct_type [concrete] // CHECK:STDOUT: %Y.impl_witness: = impl_witness @DD.as.Y.impl.%Y.impl_witness_table [concrete] // CHECK:STDOUT: %Y.facet.215: %Y.type = facet_value %DD, (%Y.impl_witness) [concrete] // CHECK:STDOUT: %W.impl_witness: = impl_witness @DD.as.W.impl.%W.impl_witness_table [concrete] // CHECK:STDOUT: %W.facet: %W.type = facet_value %DD, (%W.impl_witness) [concrete] // CHECK:STDOUT: %type: type = facet_type [concrete] // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self] // CHECK:STDOUT: %pattern_type.95b: type = pattern_type %Y.type [concrete] // CHECK:STDOUT: %D: %Y.type = symbolic_binding D, 0 [symbolic] // CHECK:STDOUT: %CC.type: type = generic_class_type @CC [concrete] // CHECK:STDOUT: %CC.generic: %CC.type = struct_value () [concrete] // CHECK:STDOUT: %CC.fe5: type = class_type @CC, @CC(%D) [symbolic] // CHECK:STDOUT: %Z.type: type = facet_type <@Z> [concrete] // CHECK:STDOUT: %Self.c59: %Z.type = symbolic_binding Self, 0 [symbolic] // CHECK:STDOUT: %BitAndWith.type.f2e: type = generic_interface_type @BitAndWith [concrete] // CHECK:STDOUT: %BitAndWith.generic: %BitAndWith.type.f2e = struct_value () [concrete] // CHECK:STDOUT: %BitAndWith.type.b10: type = facet_type <@BitAndWith, @BitAndWith(type)> [concrete] // CHECK:STDOUT: %BitAndWith.impl_witness: = impl_witness imports.%BitAndWith.impl_witness_table [concrete] // CHECK:STDOUT: %BitAndWith.facet: %BitAndWith.type.b10 = facet_value type, (%BitAndWith.impl_witness) [concrete] // CHECK:STDOUT: %BitAndWith.WithSelf.Op.type.4bd: type = fn_type @BitAndWith.WithSelf.Op, @BitAndWith.WithSelf(type, %BitAndWith.facet) [concrete] // CHECK:STDOUT: %.d15: type = fn_type_with_self_type %BitAndWith.WithSelf.Op.type.4bd, %BitAndWith.facet [concrete] // CHECK:STDOUT: %type.as.BitAndWith.impl.Op.type: type = fn_type @type.as.BitAndWith.impl.Op [concrete] // CHECK:STDOUT: %type.as.BitAndWith.impl.Op: %type.as.BitAndWith.impl.Op.type = struct_value () [concrete] // CHECK:STDOUT: %type.as.BitAndWith.impl.Op.bound: = bound_method %Y.type, %type.as.BitAndWith.impl.Op [concrete] // CHECK:STDOUT: %facet_type: type = facet_type <@Y & @W> [concrete] // CHECK:STDOUT: %pattern_type.935: type = pattern_type %facet_type [concrete] // CHECK:STDOUT: %E: %facet_type = symbolic_binding E, 0 [symbolic] // CHECK:STDOUT: %E.binding.as_type: type = symbolic_binding_type E, 0, %E [symbolic] // CHECK:STDOUT: %Y.lookup_impl_witness: = lookup_impl_witness %E, @Y [symbolic] // CHECK:STDOUT: %Y.facet.9df: %Y.type = facet_value %E.binding.as_type, (%Y.lookup_impl_witness) [symbolic] // CHECK:STDOUT: %CC.dc2: type = class_type @CC, @CC(%Y.facet.9df) [symbolic] // CHECK:STDOUT: %Z.impl_witness.506: = impl_witness @CC.as.Z.impl.%Z.impl_witness_table, @CC.as.Z.impl(%E) [symbolic] // CHECK:STDOUT: %Z.facet.8c4: %Z.type = facet_value %CC.dc2, (%Z.impl_witness.506) [symbolic] // CHECK:STDOUT: %F.type: type = fn_type @F [concrete] // CHECK:STDOUT: %F: %F.type = struct_value () [concrete] // CHECK:STDOUT: %CC.062: type = class_type @CC, @CC(%Y.facet.215) [concrete] // CHECK:STDOUT: %facet_value: %facet_type = facet_value %DD, (%Y.impl_witness, %W.impl_witness) [concrete] // CHECK:STDOUT: %Z.impl_witness.c39: = impl_witness @CC.as.Z.impl.%Z.impl_witness_table, @CC.as.Z.impl(%facet_value) [concrete] // CHECK:STDOUT: %Z.facet.d22: %Z.type = facet_value %CC.062, (%Z.impl_witness.c39) [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: .BitAndWith = %Core.BitAndWith // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: %Core.BitAndWith: %BitAndWith.type.f2e = import_ref Core//prelude/parts/as, BitAndWith, loaded [concrete = constants.%BitAndWith.generic] // CHECK:STDOUT: %Core.import_ref.8d3: %type.as.BitAndWith.impl.Op.type = import_ref Core//prelude/parts/as, loc{{\d+_\d+}}, loaded [concrete = constants.%type.as.BitAndWith.impl.Op] // CHECK:STDOUT: %BitAndWith.impl_witness_table = impl_witness_table (%Core.import_ref.8d3), @type.as.BitAndWith.impl [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .Y = %Y.decl // CHECK:STDOUT: .W = %W.decl // CHECK:STDOUT: .DD = %DD.decl // CHECK:STDOUT: .CC = %CC.decl // CHECK:STDOUT: .Z = %Z.decl // CHECK:STDOUT: .F = %F.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %Y.decl: type = interface_decl @Y [concrete = constants.%Y.type] {} {} // CHECK:STDOUT: %W.decl: type = interface_decl @W [concrete = constants.%W.type] {} {} // CHECK:STDOUT: %DD.decl: type = class_decl @DD [concrete = constants.%DD] {} {} // CHECK:STDOUT: impl_decl @DD.as.Y.impl [concrete] {} { // CHECK:STDOUT: %DD.ref: type = name_ref DD, file.%DD.decl [concrete = constants.%DD] // CHECK:STDOUT: %Y.ref: type = name_ref Y, file.%Y.decl [concrete = constants.%Y.type] // CHECK:STDOUT: } // CHECK:STDOUT: impl_decl @DD.as.W.impl [concrete] {} { // CHECK:STDOUT: %DD.ref: type = name_ref DD, file.%DD.decl [concrete = constants.%DD] // CHECK:STDOUT: %W.ref: type = name_ref W, file.%W.decl [concrete = constants.%W.type] // CHECK:STDOUT: } // CHECK:STDOUT: %CC.decl: %CC.type = class_decl @CC [concrete = constants.%CC.generic] { // CHECK:STDOUT: %D.patt: %pattern_type.95b = symbolic_binding_pattern D, 0 [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %.loc12: type = splice_block %Y.ref [concrete = constants.%Y.type] { // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %Y.ref: type = name_ref Y, file.%Y.decl [concrete = constants.%Y.type] // CHECK:STDOUT: } // CHECK:STDOUT: %D.loc12_10.2: %Y.type = symbolic_binding D, 0 [symbolic = %D.loc12_10.1 (constants.%D)] // CHECK:STDOUT: } // CHECK:STDOUT: %Z.decl: type = interface_decl @Z [concrete = constants.%Z.type] {} {} // CHECK:STDOUT: impl_decl @CC.as.Z.impl [concrete] { // CHECK:STDOUT: %E.patt: %pattern_type.935 = symbolic_binding_pattern E, 0 [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %CC.ref: %CC.type = name_ref CC, file.%CC.decl [concrete = constants.%CC.generic] // CHECK:STDOUT: %E.ref: %facet_type = name_ref E, %E.loc19_14.1 [symbolic = %E.loc19_14.2 (constants.%E)] // CHECK:STDOUT: %E.as_type: type = facet_access_type %E.ref [symbolic = %E.binding.as_type (constants.%E.binding.as_type)] // CHECK:STDOUT: %Y.facet.loc19_29.1: %Y.type = facet_value %E.as_type, (constants.%Y.lookup_impl_witness) [symbolic = %Y.facet.loc19_29.2 (constants.%Y.facet.9df)] // CHECK:STDOUT: %.loc19_29: %Y.type = converted %E.ref, %Y.facet.loc19_29.1 [symbolic = %Y.facet.loc19_29.2 (constants.%Y.facet.9df)] // CHECK:STDOUT: %CC.loc19_29.1: type = class_type @CC, @CC(constants.%Y.facet.9df) [symbolic = %CC.loc19_29.2 (constants.%CC.dc2)] // CHECK:STDOUT: %Z.ref: type = name_ref Z, file.%Z.decl [concrete = constants.%Z.type] // CHECK:STDOUT: %.loc19_20.1: type = splice_block %.loc19_20.3 [concrete = constants.%facet_type] { // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %Y.ref: type = name_ref Y, file.%Y.decl [concrete = constants.%Y.type] // CHECK:STDOUT: %W.ref: type = name_ref W, file.%W.decl [concrete = constants.%W.type] // CHECK:STDOUT: %impl.elem0: %.d15 = impl_witness_access constants.%BitAndWith.impl_witness, element0 [concrete = constants.%type.as.BitAndWith.impl.Op] // CHECK:STDOUT: %bound_method: = bound_method %Y.ref, %impl.elem0 [concrete = constants.%type.as.BitAndWith.impl.Op.bound] // CHECK:STDOUT: %type.as.BitAndWith.impl.Op.call: init type = call %bound_method(%Y.ref, %W.ref) [concrete = constants.%facet_type] // CHECK:STDOUT: %.loc19_20.2: type = value_of_initializer %type.as.BitAndWith.impl.Op.call [concrete = constants.%facet_type] // CHECK:STDOUT: %.loc19_20.3: type = converted %type.as.BitAndWith.impl.Op.call, %.loc19_20.2 [concrete = constants.%facet_type] // CHECK:STDOUT: } // CHECK:STDOUT: %E.loc19_14.1: %facet_type = symbolic_binding E, 0 [symbolic = %E.loc19_14.2 (constants.%E)] // CHECK:STDOUT: } // CHECK:STDOUT: %F.decl: %F.type = fn_decl @F [concrete = constants.%F] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: interface @Y { // CHECK:STDOUT: %Self: %Y.type = symbolic_binding Self, 0 [symbolic = constants.%Self.162] // CHECK:STDOUT: %Y.WithSelf.decl = interface_with_self_decl @Y [concrete] // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = %Self // CHECK:STDOUT: witness = () // CHECK:STDOUT: // CHECK:STDOUT: !requires: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: interface @W { // CHECK:STDOUT: %Self: %W.type = symbolic_binding Self, 0 [symbolic = constants.%Self.62a] // CHECK:STDOUT: %W.WithSelf.decl = interface_with_self_decl @W [concrete] // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = %Self // CHECK:STDOUT: witness = () // CHECK:STDOUT: // CHECK:STDOUT: !requires: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: interface @Z { // CHECK:STDOUT: %Self: %Z.type = symbolic_binding Self, 0 [symbolic = constants.%Self.c59] // CHECK:STDOUT: %Z.WithSelf.decl = interface_with_self_decl @Z [concrete] // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = %Self // CHECK:STDOUT: witness = () // CHECK:STDOUT: // CHECK:STDOUT: !requires: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: impl @DD.as.Y.impl: %DD.ref as %Y.ref { // CHECK:STDOUT: %Y.impl_witness_table = impl_witness_table (), @DD.as.Y.impl [concrete] // CHECK:STDOUT: %Y.impl_witness: = impl_witness %Y.impl_witness_table [concrete = constants.%Y.impl_witness] // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: witness = %Y.impl_witness // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: impl @DD.as.W.impl: %DD.ref as %W.ref { // CHECK:STDOUT: %W.impl_witness_table = impl_witness_table (), @DD.as.W.impl [concrete] // CHECK:STDOUT: %W.impl_witness: = impl_witness %W.impl_witness_table [concrete = constants.%W.impl_witness] // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: witness = %W.impl_witness // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic impl @CC.as.Z.impl(%E.loc19_14.1: %facet_type) { // CHECK:STDOUT: %E.loc19_14.2: %facet_type = symbolic_binding E, 0 [symbolic = %E.loc19_14.2 (constants.%E)] // CHECK:STDOUT: %E.binding.as_type: type = symbolic_binding_type E, 0, %E.loc19_14.2 [symbolic = %E.binding.as_type (constants.%E.binding.as_type)] // CHECK:STDOUT: %Y.lookup_impl_witness: = lookup_impl_witness %E.loc19_14.2, @Y [symbolic = %Y.lookup_impl_witness (constants.%Y.lookup_impl_witness)] // CHECK:STDOUT: %Y.facet.loc19_29.2: %Y.type = facet_value %E.binding.as_type, (%Y.lookup_impl_witness) [symbolic = %Y.facet.loc19_29.2 (constants.%Y.facet.9df)] // CHECK:STDOUT: %CC.loc19_29.2: type = class_type @CC, @CC(%Y.facet.loc19_29.2) [symbolic = %CC.loc19_29.2 (constants.%CC.dc2)] // CHECK:STDOUT: %Z.impl_witness.loc19_36.2: = impl_witness %Z.impl_witness_table, @CC.as.Z.impl(%E.loc19_14.2) [symbolic = %Z.impl_witness.loc19_36.2 (constants.%Z.impl_witness.506)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: // CHECK:STDOUT: impl: %CC.loc19_29.1 as %Z.ref { // CHECK:STDOUT: %Z.impl_witness_table = impl_witness_table (), @CC.as.Z.impl [concrete] // CHECK:STDOUT: %Z.impl_witness.loc19_36.1: = impl_witness %Z.impl_witness_table, @CC.as.Z.impl(constants.%E) [symbolic = %Z.impl_witness.loc19_36.2 (constants.%Z.impl_witness.506)] // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: witness = %Z.impl_witness.loc19_36.1 // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @DD { // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%DD // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic class @CC(%D.loc12_10.2: %Y.type) { // CHECK:STDOUT: %D.loc12_10.1: %Y.type = symbolic_binding D, 0 [symbolic = %D.loc12_10.1 (constants.%D)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: // CHECK:STDOUT: class { // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%CC.fe5 // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @F() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %CC.ref: %CC.type = name_ref CC, file.%CC.decl [concrete = constants.%CC.generic] // CHECK:STDOUT: %DD.ref: type = name_ref DD, file.%DD.decl [concrete = constants.%DD] // CHECK:STDOUT: %Y.facet: %Y.type = facet_value %DD.ref, (constants.%Y.impl_witness) [concrete = constants.%Y.facet.215] // CHECK:STDOUT: %.loc22_9: %Y.type = converted %DD.ref, %Y.facet [concrete = constants.%Y.facet.215] // CHECK:STDOUT: %CC: type = class_type @CC, @CC(constants.%Y.facet.215) [concrete = constants.%CC.062] // CHECK:STDOUT: %Z.ref: type = name_ref Z, file.%Z.decl [concrete = constants.%Z.type] // CHECK:STDOUT: %Z.facet: %Z.type = facet_value %CC, (constants.%Z.impl_witness.c39) [concrete = constants.%Z.facet.d22] // CHECK:STDOUT: %.loc22_12: %Z.type = converted %CC, %Z.facet [concrete = constants.%Z.facet.d22] // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @Y.WithSelf(constants.%Self.162) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @W.WithSelf(constants.%Self.62a) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @Y.WithSelf(constants.%Y.facet.215) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @W.WithSelf(constants.%W.facet) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @CC(constants.%D) { // CHECK:STDOUT: %D.loc12_10.1 => constants.%D // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @Z.WithSelf(constants.%Self.c59) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @CC(constants.%Y.facet.9df) { // CHECK:STDOUT: %D.loc12_10.1 => constants.%Y.facet.9df // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @CC.as.Z.impl(constants.%E) { // CHECK:STDOUT: %E.loc19_14.2 => constants.%E // CHECK:STDOUT: %E.binding.as_type => constants.%E.binding.as_type // CHECK:STDOUT: %Y.lookup_impl_witness => constants.%Y.lookup_impl_witness // CHECK:STDOUT: %Y.facet.loc19_29.2 => constants.%Y.facet.9df // CHECK:STDOUT: %CC.loc19_29.2 => constants.%CC.dc2 // CHECK:STDOUT: %Z.impl_witness.loc19_36.2 => constants.%Z.impl_witness.506 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @Z.WithSelf(constants.%Z.facet.8c4) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @CC(constants.%Y.facet.215) { // CHECK:STDOUT: %D.loc12_10.1 => constants.%Y.facet.215 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @CC.as.Z.impl(constants.%facet_value) { // CHECK:STDOUT: %E.loc19_14.2 => constants.%facet_value // CHECK:STDOUT: %E.binding.as_type => constants.%DD // CHECK:STDOUT: %Y.lookup_impl_witness => constants.%Y.impl_witness // CHECK:STDOUT: %Y.facet.loc19_29.2 => constants.%Y.facet.215 // CHECK:STDOUT: %CC.loc19_29.2 => constants.%CC.062 // CHECK:STDOUT: %Z.impl_witness.loc19_36.2 => constants.%Z.impl_witness.c39 // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: } // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/function/generic/fail_deduce_imported_function.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/none.carbon // TODO: Add ranges and switch to "--dump-sem-ir-ranges=only". // EXTRA-ARGS: --dump-sem-ir-ranges=if-present // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/function/generic/fail_deduce_imported_function.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/function/generic/fail_deduce_imported_function.carbon // --- lib.carbon package Lib; interface Z {} fn A[T:! Z](unused x: {.a: T}) {} // --- fail_deduce_imported_function.carbon import Lib; fn A[T:! Lib.Z](unused x: {.a: T}) {} fn B() { // CHECK:STDERR: fail_deduce_imported_function.carbon:[[@LINE+7]]:3: error: cannot deduce value for generic parameter `T` [DeductionIncomplete] // CHECK:STDERR: A({.b = {}}); // CHECK:STDERR: ^~~~~~~~~~~~ // CHECK:STDERR: fail_deduce_imported_function.carbon:[[@LINE-6]]:1: note: while deducing parameters of generic declared here [DeductionGenericHere] // CHECK:STDERR: fn A[T:! Lib.Z](unused x: {.a: T}) {} // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: A({.b = {}}); // CHECK:STDERR: fail_deduce_imported_function.carbon:[[@LINE+8]]:3: error: cannot deduce value for generic parameter `T` [DeductionIncomplete] // CHECK:STDERR: Lib.A({.b = {}}); // CHECK:STDERR: ^~~~~~~~~~~~~~~~ // CHECK:STDERR: fail_deduce_imported_function.carbon:[[@LINE-17]]:1: in import [InImport] // CHECK:STDERR: lib.carbon:4:1: note: while deducing parameters of generic declared here [DeductionGenericHere] // CHECK:STDERR: fn A[T:! Z](unused x: {.a: T}) {} // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: Lib.A({.b = {}}); } // CHECK:STDOUT: --- lib.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %Z.type: type = facet_type <@Z> [concrete] // CHECK:STDOUT: %Self: %Z.type = symbolic_binding Self, 0 [symbolic] // CHECK:STDOUT: %type: type = facet_type [concrete] // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self] // CHECK:STDOUT: %pattern_type.473: type = pattern_type %Z.type [concrete] // CHECK:STDOUT: %T: %Z.type = symbolic_binding T, 0 [symbolic] // CHECK:STDOUT: %T.binding.as_type: type = symbolic_binding_type T, 0, %T [symbolic] // CHECK:STDOUT: %struct_type.a: type = struct_type {.a: %T.binding.as_type} [symbolic] // CHECK:STDOUT: %pattern_type.3fc: type = pattern_type %struct_type.a [symbolic] // CHECK:STDOUT: %A.type: type = fn_type @A [concrete] // CHECK:STDOUT: %A: %A.type = struct_value () [concrete] // CHECK:STDOUT: %require_complete: = require_complete_type %struct_type.a [symbolic] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Z = %Z.decl // CHECK:STDOUT: .A = %A.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Z.decl: type = interface_decl @Z [concrete = constants.%Z.type] {} {} // CHECK:STDOUT: %A.decl: %A.type = fn_decl @A [concrete = constants.%A] { // CHECK:STDOUT: %T.patt: %pattern_type.473 = symbolic_binding_pattern T, 0 [concrete] // CHECK:STDOUT: %x.patt: @A.%pattern_type (%pattern_type.3fc) = value_binding_pattern x [concrete] // CHECK:STDOUT: %x.param_patt: @A.%pattern_type (%pattern_type.3fc) = value_param_pattern %x.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %.loc4_10: type = splice_block %Z.ref [concrete = constants.%Z.type] { // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %Z.ref: type = name_ref Z, file.%Z.decl [concrete = constants.%Z.type] // CHECK:STDOUT: } // CHECK:STDOUT: %T.loc4_6.2: %Z.type = symbolic_binding T, 0 [symbolic = %T.loc4_6.1 (constants.%T)] // CHECK:STDOUT: %x.param: @A.%struct_type.a.loc4_29.1 (%struct_type.a) = value_param call_param0 // CHECK:STDOUT: %.loc4_29: type = splice_block %struct_type.a.loc4_29.2 [symbolic = %struct_type.a.loc4_29.1 (constants.%struct_type.a)] { // CHECK:STDOUT: %T.ref: %Z.type = name_ref T, %T.loc4_6.2 [symbolic = %T.loc4_6.1 (constants.%T)] // CHECK:STDOUT: %T.as_type: type = facet_access_type %T.ref [symbolic = %T.binding.as_type (constants.%T.binding.as_type)] // CHECK:STDOUT: %.loc4_28: type = converted %T.ref, %T.as_type [symbolic = %T.binding.as_type (constants.%T.binding.as_type)] // CHECK:STDOUT: %struct_type.a.loc4_29.2: type = struct_type {.a: @A.%T.binding.as_type (%T.binding.as_type)} [symbolic = %struct_type.a.loc4_29.1 (constants.%struct_type.a)] // CHECK:STDOUT: } // CHECK:STDOUT: %x: @A.%struct_type.a.loc4_29.1 (%struct_type.a) = value_binding x, %x.param // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: interface @Z { // CHECK:STDOUT: %Self: %Z.type = symbolic_binding Self, 0 [symbolic = constants.%Self] // CHECK:STDOUT: %Z.WithSelf.decl = interface_with_self_decl @Z [concrete] // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = %Self // CHECK:STDOUT: witness = () // CHECK:STDOUT: // CHECK:STDOUT: !requires: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic fn @A(%T.loc4_6.2: %Z.type) { // CHECK:STDOUT: %T.loc4_6.1: %Z.type = symbolic_binding T, 0 [symbolic = %T.loc4_6.1 (constants.%T)] // CHECK:STDOUT: %T.binding.as_type: type = symbolic_binding_type T, 0, %T.loc4_6.1 [symbolic = %T.binding.as_type (constants.%T.binding.as_type)] // CHECK:STDOUT: %struct_type.a.loc4_29.1: type = struct_type {.a: @A.%T.binding.as_type (%T.binding.as_type)} [symbolic = %struct_type.a.loc4_29.1 (constants.%struct_type.a)] // CHECK:STDOUT: %pattern_type: type = pattern_type %struct_type.a.loc4_29.1 [symbolic = %pattern_type (constants.%pattern_type.3fc)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete: = require_complete_type %struct_type.a.loc4_29.1 [symbolic = %require_complete (constants.%require_complete)] // CHECK:STDOUT: // CHECK:STDOUT: fn(%x.param: @A.%struct_type.a.loc4_29.1 (%struct_type.a)) { // CHECK:STDOUT: !entry: // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @Z.WithSelf(constants.%Self) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @A(constants.%T) { // CHECK:STDOUT: %T.loc4_6.1 => constants.%T // CHECK:STDOUT: %T.binding.as_type => constants.%T.binding.as_type // CHECK:STDOUT: %struct_type.a.loc4_29.1 => constants.%struct_type.a // CHECK:STDOUT: %pattern_type => constants.%pattern_type.3fc // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- fail_deduce_imported_function.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %type: type = facet_type [concrete] // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self] // CHECK:STDOUT: %Z.type: type = facet_type <@Z> [concrete] // CHECK:STDOUT: %Self: %Z.type = symbolic_binding Self, 0 [symbolic] // CHECK:STDOUT: %pattern_type.e5e: type = pattern_type %Z.type [concrete] // CHECK:STDOUT: %T: %Z.type = symbolic_binding T, 0 [symbolic] // CHECK:STDOUT: %T.binding.as_type: type = symbolic_binding_type T, 0, %T [symbolic] // CHECK:STDOUT: %struct_type.a: type = struct_type {.a: %T.binding.as_type} [symbolic] // CHECK:STDOUT: %pattern_type.857: type = pattern_type %struct_type.a [symbolic] // CHECK:STDOUT: %A.type.816: type = fn_type @A.loc4 [concrete] // CHECK:STDOUT: %A.8ae: %A.type.816 = struct_value () [concrete] // CHECK:STDOUT: %require_complete: = require_complete_type %struct_type.a [symbolic] // CHECK:STDOUT: %B.type: type = fn_type @B [concrete] // CHECK:STDOUT: %B: %B.type = struct_value () [concrete] // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %empty_struct: %empty_struct_type = struct_value () [concrete] // CHECK:STDOUT: %struct_type.b: type = struct_type {.b: %empty_struct_type} [concrete] // CHECK:STDOUT: %struct: %struct_type.b = struct_value (%empty_struct) [concrete] // CHECK:STDOUT: %A.type.fad: type = fn_type @A.1 [concrete] // CHECK:STDOUT: %A.7a0: %A.type.fad = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Lib: = namespace file.%Lib.import, [concrete] { // CHECK:STDOUT: .Z = %Lib.Z // CHECK:STDOUT: .A = %Lib.A // CHECK:STDOUT: import Lib//default // CHECK:STDOUT: } // CHECK:STDOUT: %Lib.Z: type = import_ref Lib//default, Z, loaded [concrete = constants.%Z.type] // CHECK:STDOUT: %Lib.import_ref.7bd = import_ref Lib//default, loc3_13, unloaded // CHECK:STDOUT: %Lib.A: %A.type.fad = import_ref Lib//default, A, loaded [concrete = constants.%A.7a0] // CHECK:STDOUT: %Lib.import_ref.4c8: %Z.type = import_ref Lib//default, loc4_6, loaded [symbolic = @A.1.%T (constants.%T)] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Lib = imports.%Lib // CHECK:STDOUT: .A = %A.decl // CHECK:STDOUT: .B = %B.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Lib.import = import Lib // CHECK:STDOUT: %A.decl: %A.type.816 = fn_decl @A.loc4 [concrete = constants.%A.8ae] { // CHECK:STDOUT: %T.patt: %pattern_type.e5e = symbolic_binding_pattern T, 0 [concrete] // CHECK:STDOUT: %x.patt: @A.loc4.%pattern_type (%pattern_type.857) = value_binding_pattern x [concrete] // CHECK:STDOUT: %x.param_patt: @A.loc4.%pattern_type (%pattern_type.857) = value_param_pattern %x.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %.loc4_13: type = splice_block %Z.ref [concrete = constants.%Z.type] { // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %Lib.ref: = name_ref Lib, imports.%Lib [concrete = imports.%Lib] // CHECK:STDOUT: %Z.ref: type = name_ref Z, imports.%Lib.Z [concrete = constants.%Z.type] // CHECK:STDOUT: } // CHECK:STDOUT: %T.loc4_6.2: %Z.type = symbolic_binding T, 0 [symbolic = %T.loc4_6.1 (constants.%T)] // CHECK:STDOUT: %x.param: @A.loc4.%struct_type.a.loc4_33.1 (%struct_type.a) = value_param call_param0 // CHECK:STDOUT: %.loc4_33: type = splice_block %struct_type.a.loc4_33.2 [symbolic = %struct_type.a.loc4_33.1 (constants.%struct_type.a)] { // CHECK:STDOUT: %T.ref: %Z.type = name_ref T, %T.loc4_6.2 [symbolic = %T.loc4_6.1 (constants.%T)] // CHECK:STDOUT: %T.as_type: type = facet_access_type %T.ref [symbolic = %T.binding.as_type (constants.%T.binding.as_type)] // CHECK:STDOUT: %.loc4_32: type = converted %T.ref, %T.as_type [symbolic = %T.binding.as_type (constants.%T.binding.as_type)] // CHECK:STDOUT: %struct_type.a.loc4_33.2: type = struct_type {.a: @A.loc4.%T.binding.as_type (%T.binding.as_type)} [symbolic = %struct_type.a.loc4_33.1 (constants.%struct_type.a)] // CHECK:STDOUT: } // CHECK:STDOUT: %x: @A.loc4.%struct_type.a.loc4_33.1 (%struct_type.a) = value_binding x, %x.param // CHECK:STDOUT: } // CHECK:STDOUT: %B.decl: %B.type = fn_decl @B [concrete = constants.%B] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: interface @Z [from "lib.carbon"] { // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = imports.%Lib.import_ref.7bd // CHECK:STDOUT: witness = () // CHECK:STDOUT: // CHECK:STDOUT: !requires: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic fn @A.loc4(%T.loc4_6.2: %Z.type) { // CHECK:STDOUT: %T.loc4_6.1: %Z.type = symbolic_binding T, 0 [symbolic = %T.loc4_6.1 (constants.%T)] // CHECK:STDOUT: %T.binding.as_type: type = symbolic_binding_type T, 0, %T.loc4_6.1 [symbolic = %T.binding.as_type (constants.%T.binding.as_type)] // CHECK:STDOUT: %struct_type.a.loc4_33.1: type = struct_type {.a: @A.loc4.%T.binding.as_type (%T.binding.as_type)} [symbolic = %struct_type.a.loc4_33.1 (constants.%struct_type.a)] // CHECK:STDOUT: %pattern_type: type = pattern_type %struct_type.a.loc4_33.1 [symbolic = %pattern_type (constants.%pattern_type.857)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete: = require_complete_type %struct_type.a.loc4_33.1 [symbolic = %require_complete (constants.%require_complete)] // CHECK:STDOUT: // CHECK:STDOUT: fn(%x.param: @A.loc4.%struct_type.a.loc4_33.1 (%struct_type.a)) { // CHECK:STDOUT: !entry: // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @B() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %A.ref.loc14: %A.type.816 = name_ref A, file.%A.decl [concrete = constants.%A.8ae] // CHECK:STDOUT: %.loc14_12: %empty_struct_type = struct_literal () [concrete = constants.%empty_struct] // CHECK:STDOUT: %.loc14_13: %struct_type.b = struct_literal (%.loc14_12) [concrete = constants.%struct] // CHECK:STDOUT: %Lib.ref: = name_ref Lib, imports.%Lib [concrete = imports.%Lib] // CHECK:STDOUT: %A.ref.loc24: %A.type.fad = name_ref A, imports.%Lib.A [concrete = constants.%A.7a0] // CHECK:STDOUT: %.loc24_16: %empty_struct_type = struct_literal () [concrete = constants.%empty_struct] // CHECK:STDOUT: %.loc24_17: %struct_type.b = struct_literal (%.loc24_16) [concrete = constants.%struct] // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic fn @A.1(imports.%Lib.import_ref.4c8: %Z.type) [from "lib.carbon"] { // CHECK:STDOUT: %T: %Z.type = symbolic_binding T, 0 [symbolic = %T (constants.%T)] // CHECK:STDOUT: %T.binding.as_type: type = symbolic_binding_type T, 0, %T [symbolic = %T.binding.as_type (constants.%T.binding.as_type)] // CHECK:STDOUT: %struct_type.a: type = struct_type {.a: @A.1.%T.binding.as_type (%T.binding.as_type)} [symbolic = %struct_type.a (constants.%struct_type.a)] // CHECK:STDOUT: %pattern_type: type = pattern_type %struct_type.a [symbolic = %pattern_type (constants.%pattern_type.857)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete: = require_complete_type %struct_type.a [symbolic = %require_complete (constants.%require_complete)] // CHECK:STDOUT: // CHECK:STDOUT: fn; // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @Z.WithSelf(constants.%Self) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @A.loc4(constants.%T) { // CHECK:STDOUT: %T.loc4_6.1 => constants.%T // CHECK:STDOUT: %T.binding.as_type => constants.%T.binding.as_type // CHECK:STDOUT: %struct_type.a.loc4_33.1 => constants.%struct_type.a // CHECK:STDOUT: %pattern_type => constants.%pattern_type.857 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @A.1(constants.%T) { // CHECK:STDOUT: %T => constants.%T // CHECK:STDOUT: %T.binding.as_type => constants.%T.binding.as_type // CHECK:STDOUT: %struct_type.a => constants.%struct_type.a // CHECK:STDOUT: %pattern_type => constants.%pattern_type.857 // CHECK:STDOUT: } // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/function/generic/fail_type_param_mismatch.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/convert.carbon // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/function/generic/fail_type_param_mismatch.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/function/generic/fail_type_param_mismatch.carbon fn F(T:! type, U:! type) { var p: T*; // CHECK:STDERR: fail_type_param_mismatch.carbon:[[@LINE+7]]:21: error: cannot implicitly convert expression of type `T` to `U` [ConversionFailure] // CHECK:STDERR: let unused n: U = *p; // CHECK:STDERR: ^~ // CHECK:STDERR: fail_type_param_mismatch.carbon:[[@LINE+4]]:21: note: type `T` does not implement interface `Core.ImplicitAs(U)` [MissingImplInMemberAccessInContext] // CHECK:STDERR: let unused n: U = *p; // CHECK:STDERR: ^~ // CHECK:STDERR: let unused n: U = *p; } ================================================ FILE: toolchain/check/testdata/function/generic/forward_decl.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/none.carbon // TODO: Add ranges and switch to "--dump-sem-ir-ranges=only". // EXTRA-ARGS: --dump-sem-ir-ranges=if-present // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/function/generic/forward_decl.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/function/generic/forward_decl.carbon fn F(T:! type); // CHECK:STDOUT: --- forward_decl.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %type: type = facet_type [concrete] // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self] // CHECK:STDOUT: %pattern_type: type = pattern_type type [concrete] // CHECK:STDOUT: %T: type = symbolic_binding T, 0 [symbolic] // CHECK:STDOUT: %F.type: type = fn_type @F [concrete] // CHECK:STDOUT: %F: %F.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .F = %F.decl // CHECK:STDOUT: } // CHECK:STDOUT: %F.decl: %F.type = fn_decl @F [concrete = constants.%F] { // CHECK:STDOUT: %T.patt: %pattern_type = symbolic_binding_pattern T, 0 [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %.loc15_10.1: type = splice_block %.loc15_10.2 [concrete = type] { // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %.loc15_10.2: type = type_literal type [concrete = type] // CHECK:STDOUT: } // CHECK:STDOUT: %T.loc15_6.2: type = symbolic_binding T, 0 [symbolic = %T.loc15_6.1 (constants.%T)] // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic fn @F(%T.loc15_6.2: type) { // CHECK:STDOUT: %T.loc15_6.1: type = symbolic_binding T, 0 [symbolic = %T.loc15_6.1 (constants.%T)] // CHECK:STDOUT: // CHECK:STDOUT: fn(); // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @F(constants.%T) { // CHECK:STDOUT: %T.loc15_6.1 => constants.%T // CHECK:STDOUT: } // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/function/generic/import_specific.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/none.carbon // TODO: Add ranges and switch to "--dump-sem-ir-ranges=only". // EXTRA-ARGS: --dump-sem-ir-ranges=if-present // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/function/generic/import_specific.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/function/generic/import_specific.carbon // --- library.carbon library "[[@TEST_NAME]]"; fn F(unused T:! type) { } fn G(T:! type) { F(T); } // --- user.carbon library "[[@TEST_NAME]]"; import library "library"; class C {} fn H() { // TODO: We currently crash when importing G if we don't force F to be imported first. F(C); G(C); } // CHECK:STDOUT: --- library.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %type: type = facet_type [concrete] // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self] // CHECK:STDOUT: %pattern_type: type = pattern_type type [concrete] // CHECK:STDOUT: %T.67db0b.1: type = symbolic_binding T, 0 [symbolic] // CHECK:STDOUT: %F.type: type = fn_type @F [concrete] // CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] // CHECK:STDOUT: %F: %F.type = struct_value () [concrete] // CHECK:STDOUT: %T.67db0b.2: type = symbolic_binding T, 0 [symbolic] // CHECK:STDOUT: %G.type: type = fn_type @G [concrete] // CHECK:STDOUT: %G: %G.type = struct_value () [concrete] // CHECK:STDOUT: %F.specific_fn: = specific_function %F, @F(%T.67db0b.2) [symbolic] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .F = %F.decl // CHECK:STDOUT: .G = %G.decl // CHECK:STDOUT: } // CHECK:STDOUT: %F.decl: %F.type = fn_decl @F [concrete = constants.%F] { // CHECK:STDOUT: %T.patt: %pattern_type = symbolic_binding_pattern T, 0 [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %.loc4_17.1: type = splice_block %.loc4_17.2 [concrete = type] { // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %.loc4_17.2: type = type_literal type [concrete = type] // CHECK:STDOUT: } // CHECK:STDOUT: %T.loc4_13.2: type = symbolic_binding T, 0 [symbolic = %T.loc4_13.1 (constants.%T.67db0b.1)] // CHECK:STDOUT: } // CHECK:STDOUT: %G.decl: %G.type = fn_decl @G [concrete = constants.%G] { // CHECK:STDOUT: %T.patt: %pattern_type = symbolic_binding_pattern T, 0 [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %.loc7_10.1: type = splice_block %.loc7_10.2 [concrete = type] { // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %.loc7_10.2: type = type_literal type [concrete = type] // CHECK:STDOUT: } // CHECK:STDOUT: %T.loc7_6.2: type = symbolic_binding T, 0 [symbolic = %T.loc7_6.1 (constants.%T.67db0b.2)] // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic fn @F(%T.loc4_13.2: type) { // CHECK:STDOUT: %T.loc4_13.1: type = symbolic_binding T, 0 [symbolic = %T.loc4_13.1 (constants.%T.67db0b.1)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: // CHECK:STDOUT: fn() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic fn @G(%T.loc7_6.2: type) { // CHECK:STDOUT: %T.loc7_6.1: type = symbolic_binding T, 0 [symbolic = %T.loc7_6.1 (constants.%T.67db0b.2)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %F.specific_fn.loc8_3.2: = specific_function constants.%F, @F(%T.loc7_6.1) [symbolic = %F.specific_fn.loc8_3.2 (constants.%F.specific_fn)] // CHECK:STDOUT: // CHECK:STDOUT: fn() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %F.ref: %F.type = name_ref F, file.%F.decl [concrete = constants.%F] // CHECK:STDOUT: %T.ref: type = name_ref T, %T.loc7_6.2 [symbolic = %T.loc7_6.1 (constants.%T.67db0b.2)] // CHECK:STDOUT: %F.specific_fn.loc8_3.1: = specific_function %F.ref, @F(constants.%T.67db0b.2) [symbolic = %F.specific_fn.loc8_3.2 (constants.%F.specific_fn)] // CHECK:STDOUT: %F.call: init %empty_tuple.type = call %F.specific_fn.loc8_3.1() // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @F(constants.%T.67db0b.1) { // CHECK:STDOUT: %T.loc4_13.1 => constants.%T.67db0b.1 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @G(constants.%T.67db0b.2) { // CHECK:STDOUT: %T.loc7_6.1 => constants.%T.67db0b.2 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @F(constants.%T.67db0b.2) { // CHECK:STDOUT: %T.loc4_13.1 => constants.%T.67db0b.2 // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- user.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %C: type = class_type @C [concrete] // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %complete_type: = complete_type_witness %empty_struct_type [concrete] // CHECK:STDOUT: %H.type: type = fn_type @H [concrete] // CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] // CHECK:STDOUT: %H: %H.type = struct_value () [concrete] // CHECK:STDOUT: %F.type: type = fn_type @F [concrete] // CHECK:STDOUT: %F: %F.type = struct_value () [concrete] // CHECK:STDOUT: %T.67db0b.1: type = symbolic_binding T, 0 [symbolic] // CHECK:STDOUT: %F.specific_fn.540: = specific_function %F, @F(%C) [concrete] // CHECK:STDOUT: %G.type: type = fn_type @G [concrete] // CHECK:STDOUT: %G: %G.type = struct_value () [concrete] // CHECK:STDOUT: %T.67db0b.2: type = symbolic_binding T, 0 [symbolic] // CHECK:STDOUT: %F.specific_fn.643: = specific_function %F, @F(%T.67db0b.2) [symbolic] // CHECK:STDOUT: %G.specific_fn: = specific_function %G, @G(%C) [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Main.F: %F.type = import_ref Main//library, F, loaded [concrete = constants.%F] // CHECK:STDOUT: %Main.G: %G.type = import_ref Main//library, G, loaded [concrete = constants.%G] // CHECK:STDOUT: %Main.import_ref.b3bc94.1: type = import_ref Main//library, loc4_13, loaded [symbolic = @F.%T (constants.%T.67db0b.1)] // CHECK:STDOUT: %Main.import_ref.b3bc94.2: type = import_ref Main//library, loc7_6, loaded [symbolic = @G.%T (constants.%T.67db0b.2)] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .F = imports.%Main.F // CHECK:STDOUT: .G = imports.%Main.G // CHECK:STDOUT: .C = %C.decl // CHECK:STDOUT: .H = %H.decl // CHECK:STDOUT: } // CHECK:STDOUT: %default.import = import // CHECK:STDOUT: %C.decl: type = class_decl @C [concrete = constants.%C] {} {} // CHECK:STDOUT: %H.decl: %H.type = fn_decl @H [concrete = constants.%H] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @C { // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%C // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @H() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %F.ref: %F.type = name_ref F, imports.%Main.F [concrete = constants.%F] // CHECK:STDOUT: %C.ref.loc10: type = name_ref C, file.%C.decl [concrete = constants.%C] // CHECK:STDOUT: %F.specific_fn: = specific_function %F.ref, @F(constants.%C) [concrete = constants.%F.specific_fn.540] // CHECK:STDOUT: %F.call: init %empty_tuple.type = call %F.specific_fn() // CHECK:STDOUT: %G.ref: %G.type = name_ref G, imports.%Main.G [concrete = constants.%G] // CHECK:STDOUT: %C.ref.loc12: type = name_ref C, file.%C.decl [concrete = constants.%C] // CHECK:STDOUT: %G.specific_fn: = specific_function %G.ref, @G(constants.%C) [concrete = constants.%G.specific_fn] // CHECK:STDOUT: %G.call: init %empty_tuple.type = call %G.specific_fn() // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic fn @F(imports.%Main.import_ref.b3bc94.1: type) [from "library.carbon"] { // CHECK:STDOUT: %T: type = symbolic_binding T, 0 [symbolic = %T (constants.%T.67db0b.1)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: // CHECK:STDOUT: fn; // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic fn @G(imports.%Main.import_ref.b3bc94.2: type) [from "library.carbon"] { // CHECK:STDOUT: %T: type = symbolic_binding T, 0 [symbolic = %T (constants.%T.67db0b.2)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %F.specific_fn: = specific_function constants.%F, @F(%T) [symbolic = %F.specific_fn (constants.%F.specific_fn.643)] // CHECK:STDOUT: // CHECK:STDOUT: fn; // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @F(constants.%T.67db0b.1) { // CHECK:STDOUT: %T => constants.%T.67db0b.1 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @F(constants.%C) { // CHECK:STDOUT: %T => constants.%C // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @F(constants.%T.67db0b.2) { // CHECK:STDOUT: %T => constants.%T.67db0b.2 // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @G(constants.%T.67db0b.2) { // CHECK:STDOUT: %T => constants.%T.67db0b.2 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @G(constants.%C) { // CHECK:STDOUT: %T => constants.%C // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %F.specific_fn => constants.%F.specific_fn.540 // CHECK:STDOUT: } // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/function/generic/indirect_generic_type.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/convert.carbon // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/function/generic/indirect_generic_type.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/function/generic/indirect_generic_type.carbon // --- pointer_to_pointer.carbon library "[[@TEST_NAME]]"; fn F(T:! type, p: T**) -> T* { //@dump-sem-ir-begin return *p; //@dump-sem-ir-end } // CHECK:STDOUT: --- pointer_to_pointer.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %T.67d: type = symbolic_binding T, 0 [symbolic] // CHECK:STDOUT: %ptr.e8f: type = ptr_type %T.67d [symbolic] // CHECK:STDOUT: %ptr.125: type = ptr_type %ptr.e8f [symbolic] // CHECK:STDOUT: %pattern_type.8bb: type = pattern_type %ptr.125 [symbolic] // CHECK:STDOUT: %.cb6: Core.Form = init_form %ptr.e8f [symbolic] // CHECK:STDOUT: %pattern_type.4f4: type = pattern_type %ptr.e8f [symbolic] // CHECK:STDOUT: %Copy.type: type = facet_type <@Copy> [concrete] // CHECK:STDOUT: %.2f2: require_specific_def_type = require_specific_def @ptr.as.Copy.impl(%T.67d) [symbolic] // CHECK:STDOUT: %Copy.lookup_impl_witness.2e6: = lookup_impl_witness %ptr.e8f, @Copy [symbolic] // CHECK:STDOUT: %Copy.facet: %Copy.type = facet_value %ptr.e8f, (%Copy.lookup_impl_witness.2e6) [symbolic] // CHECK:STDOUT: %Copy.WithSelf.Op.type.d82: type = fn_type @Copy.WithSelf.Op, @Copy.WithSelf(%Copy.facet) [symbolic] // CHECK:STDOUT: %.299: type = fn_type_with_self_type %Copy.WithSelf.Op.type.d82, %Copy.facet [symbolic] // CHECK:STDOUT: %impl.elem0.1c7: %.299 = impl_witness_access %Copy.lookup_impl_witness.2e6, element0 [symbolic] // CHECK:STDOUT: %specific_impl_fn.366: = specific_impl_function %impl.elem0.1c7, @Copy.WithSelf.Op(%Copy.facet) [symbolic] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic fn @F(%T.loc4_6.2: type) { // CHECK:STDOUT: // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: // CHECK:STDOUT: %.loc6_10.3: require_specific_def_type = require_specific_def @ptr.as.Copy.impl(%T.loc4_6.1) [symbolic = %.loc6_10.3 (constants.%.2f2)] // CHECK:STDOUT: %Copy.lookup_impl_witness: = lookup_impl_witness %ptr.loc4_20.1, @Copy [symbolic = %Copy.lookup_impl_witness (constants.%Copy.lookup_impl_witness.2e6)] // CHECK:STDOUT: %Copy.facet: %Copy.type = facet_value %ptr.loc4_20.1, (%Copy.lookup_impl_witness) [symbolic = %Copy.facet (constants.%Copy.facet)] // CHECK:STDOUT: %Copy.WithSelf.Op.type: type = fn_type @Copy.WithSelf.Op, @Copy.WithSelf(%Copy.facet) [symbolic = %Copy.WithSelf.Op.type (constants.%Copy.WithSelf.Op.type.d82)] // CHECK:STDOUT: %.loc6_10.4: type = fn_type_with_self_type %Copy.WithSelf.Op.type, %Copy.facet [symbolic = %.loc6_10.4 (constants.%.299)] // CHECK:STDOUT: %impl.elem0.loc6_10.2: @F.%.loc6_10.4 (%.299) = impl_witness_access %Copy.lookup_impl_witness, element0 [symbolic = %impl.elem0.loc6_10.2 (constants.%impl.elem0.1c7)] // CHECK:STDOUT: %specific_impl_fn.loc6_10.2: = specific_impl_function %impl.elem0.loc6_10.2, @Copy.WithSelf.Op(%Copy.facet) [symbolic = %specific_impl_fn.loc6_10.2 (constants.%specific_impl_fn.366)] // CHECK:STDOUT: // CHECK:STDOUT: fn(%p.param: @F.%ptr.loc4_21.1 (%ptr.125)) -> out %return.param: @F.%ptr.loc4_20.1 (%ptr.e8f) { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %p.ref: @F.%ptr.loc4_21.1 (%ptr.125) = name_ref p, %p // CHECK:STDOUT: %.loc6_10.1: ref @F.%ptr.loc4_20.1 (%ptr.e8f) = deref %p.ref // CHECK:STDOUT: %.loc6_10.2: @F.%ptr.loc4_20.1 (%ptr.e8f) = acquire_value %.loc6_10.1 // CHECK:STDOUT: %impl.elem0.loc6_10.1: @F.%.loc6_10.4 (%.299) = impl_witness_access constants.%Copy.lookup_impl_witness.2e6, element0 [symbolic = %impl.elem0.loc6_10.2 (constants.%impl.elem0.1c7)] // CHECK:STDOUT: %bound_method.loc6_10.1: = bound_method %.loc6_10.2, %impl.elem0.loc6_10.1 // CHECK:STDOUT: %specific_impl_fn.loc6_10.1: = specific_impl_function %impl.elem0.loc6_10.1, @Copy.WithSelf.Op(constants.%Copy.facet) [symbolic = %specific_impl_fn.loc6_10.2 (constants.%specific_impl_fn.366)] // CHECK:STDOUT: %bound_method.loc6_10.2: = bound_method %.loc6_10.2, %specific_impl_fn.loc6_10.1 // CHECK:STDOUT: %Copy.WithSelf.Op.call: init @F.%ptr.loc4_20.1 (%ptr.e8f) = call %bound_method.loc6_10.2(%.loc6_10.2) // CHECK:STDOUT: return %Copy.WithSelf.Op.call // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @F(constants.%T.67d) { // CHECK:STDOUT: %T.loc4_6.1 => constants.%T.67d // CHECK:STDOUT: %ptr.loc4_20.1 => constants.%ptr.e8f // CHECK:STDOUT: %ptr.loc4_21.1 => constants.%ptr.125 // CHECK:STDOUT: %pattern_type.loc4_16 => constants.%pattern_type.8bb // CHECK:STDOUT: %.loc4_28.1 => constants.%.cb6 // CHECK:STDOUT: %pattern_type.loc4_24 => constants.%pattern_type.4f4 // CHECK:STDOUT: } // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/function/generic/param_in_type.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/int.carbon // TODO: Add ranges and switch to "--dump-sem-ir-ranges=only". // EXTRA-ARGS: --dump-sem-ir-ranges=if-present // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/function/generic/param_in_type.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/function/generic/param_in_type.carbon fn F(N:! i32, a: array(i32, N)*); // CHECK:STDOUT: --- param_in_type.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %type: type = facet_type [concrete] // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self] // CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [concrete] // CHECK:STDOUT: %Int.type: type = generic_class_type @Int [concrete] // CHECK:STDOUT: %Int.generic: %Int.type = struct_value () [concrete] // CHECK:STDOUT: %i32: type = class_type @Int, @Int(%int_32) [concrete] // CHECK:STDOUT: %pattern_type.7ce: type = pattern_type %i32 [concrete] // CHECK:STDOUT: %N.5de: %i32 = symbolic_binding N, 0 [symbolic] // CHECK:STDOUT: %ImplicitAs.type.cc7: type = generic_interface_type @ImplicitAs [concrete] // CHECK:STDOUT: %ImplicitAs.generic: %ImplicitAs.type.cc7 = struct_value () [concrete] // CHECK:STDOUT: %ImplicitAs.type.139: type = facet_type <@ImplicitAs, @ImplicitAs(Core.IntLiteral)> [concrete] // CHECK:STDOUT: %From: Core.IntLiteral = symbolic_binding From, 0 [symbolic] // CHECK:STDOUT: %Int.as.ImplicitAs.impl.Convert.type.2ed: type = fn_type @Int.as.ImplicitAs.impl.Convert, @Int.as.ImplicitAs.impl(%From) [symbolic] // CHECK:STDOUT: %Int.as.ImplicitAs.impl.Convert.d29: %Int.as.ImplicitAs.impl.Convert.type.2ed = struct_value () [symbolic] // CHECK:STDOUT: %ImplicitAs.impl_witness.640: = impl_witness imports.%ImplicitAs.impl_witness_table.ea2, @Int.as.ImplicitAs.impl(%int_32) [concrete] // CHECK:STDOUT: %Int.as.ImplicitAs.impl.Convert.type.240: type = fn_type @Int.as.ImplicitAs.impl.Convert, @Int.as.ImplicitAs.impl(%int_32) [concrete] // CHECK:STDOUT: %Int.as.ImplicitAs.impl.Convert.dd4: %Int.as.ImplicitAs.impl.Convert.type.240 = struct_value () [concrete] // CHECK:STDOUT: %ImplicitAs.facet: %ImplicitAs.type.139 = facet_value %i32, (%ImplicitAs.impl_witness.640) [concrete] // CHECK:STDOUT: %ImplicitAs.WithSelf.Convert.type.462: type = fn_type @ImplicitAs.WithSelf.Convert, @ImplicitAs.WithSelf(Core.IntLiteral, %ImplicitAs.facet) [concrete] // CHECK:STDOUT: %.0a7: type = fn_type_with_self_type %ImplicitAs.WithSelf.Convert.type.462, %ImplicitAs.facet [concrete] // CHECK:STDOUT: %Int.as.ImplicitAs.impl.Convert.bound: = bound_method %N.5de, %Int.as.ImplicitAs.impl.Convert.dd4 [symbolic] // CHECK:STDOUT: %Int.as.ImplicitAs.impl.Convert.specific_fn: = specific_function %Int.as.ImplicitAs.impl.Convert.dd4, @Int.as.ImplicitAs.impl.Convert(%int_32) [concrete] // CHECK:STDOUT: %bound_method: = bound_method %N.5de, %Int.as.ImplicitAs.impl.Convert.specific_fn [symbolic] // CHECK:STDOUT: %Int.as.ImplicitAs.impl.Convert.call: init Core.IntLiteral = call %bound_method(%N.5de) [symbolic] // CHECK:STDOUT: %array_type: type = array_type %Int.as.ImplicitAs.impl.Convert.call, %i32 [symbolic] // CHECK:STDOUT: %ptr: type = ptr_type %array_type [symbolic] // CHECK:STDOUT: %pattern_type.bb8: type = pattern_type %ptr [symbolic] // CHECK:STDOUT: %F.type: type = fn_type @F [concrete] // CHECK:STDOUT: %F: %F.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: .Int = %Core.Int // CHECK:STDOUT: .ImplicitAs = %Core.ImplicitAs // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: %Core.Int: %Int.type = import_ref Core//prelude/parts/int, Int, loaded [concrete = constants.%Int.generic] // CHECK:STDOUT: %Core.ImplicitAs: %ImplicitAs.type.cc7 = import_ref Core//prelude/parts/as, ImplicitAs, loaded [concrete = constants.%ImplicitAs.generic] // CHECK:STDOUT: %Core.import_ref.0bc: @Int.as.ImplicitAs.impl.%Int.as.ImplicitAs.impl.Convert.type (%Int.as.ImplicitAs.impl.Convert.type.2ed) = import_ref Core//prelude/parts/int, loc{{\d+_\d+}}, loaded [symbolic = @Int.as.ImplicitAs.impl.%Int.as.ImplicitAs.impl.Convert (constants.%Int.as.ImplicitAs.impl.Convert.d29)] // CHECK:STDOUT: %ImplicitAs.impl_witness_table.ea2 = impl_witness_table (%Core.import_ref.0bc), @Int.as.ImplicitAs.impl [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .F = %F.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %F.decl: %F.type = fn_decl @F [concrete = constants.%F] { // CHECK:STDOUT: %N.patt: %pattern_type.7ce = symbolic_binding_pattern N, 0 [concrete] // CHECK:STDOUT: %a.patt: @F.%pattern_type (%pattern_type.bb8) = value_binding_pattern a [concrete] // CHECK:STDOUT: %a.param_patt: @F.%pattern_type (%pattern_type.bb8) = value_param_pattern %a.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %.loc15_10: type = splice_block %i32.loc15_10 [concrete = constants.%i32] { // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %i32.loc15_10: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: } // CHECK:STDOUT: %N.loc15_6.2: %i32 = symbolic_binding N, 0 [symbolic = %N.loc15_6.1 (constants.%N.5de)] // CHECK:STDOUT: %a.param: @F.%ptr.loc15_31.1 (%ptr) = value_param call_param0 // CHECK:STDOUT: %.loc15_31: type = splice_block %ptr.loc15_31.2 [symbolic = %ptr.loc15_31.1 (constants.%ptr)] { // CHECK:STDOUT: %i32.loc15_24: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %N.ref: %i32 = name_ref N, %N.loc15_6.2 [symbolic = %N.loc15_6.1 (constants.%N.5de)] // CHECK:STDOUT: %impl.elem0: %.0a7 = impl_witness_access constants.%ImplicitAs.impl_witness.640, element0 [concrete = constants.%Int.as.ImplicitAs.impl.Convert.dd4] // CHECK:STDOUT: %bound_method.loc15_29.2: = bound_method %N.ref, %impl.elem0 [symbolic = %Int.as.ImplicitAs.impl.Convert.bound (constants.%Int.as.ImplicitAs.impl.Convert.bound)] // CHECK:STDOUT: %specific_fn: = specific_function %impl.elem0, @Int.as.ImplicitAs.impl.Convert(constants.%int_32) [concrete = constants.%Int.as.ImplicitAs.impl.Convert.specific_fn] // CHECK:STDOUT: %bound_method.loc15_29.3: = bound_method %N.ref, %specific_fn [symbolic = %bound_method.loc15_29.1 (constants.%bound_method)] // CHECK:STDOUT: %Int.as.ImplicitAs.impl.Convert.call.loc15_29.2: init Core.IntLiteral = call %bound_method.loc15_29.3(%N.ref) [symbolic = %Int.as.ImplicitAs.impl.Convert.call.loc15_29.1 (constants.%Int.as.ImplicitAs.impl.Convert.call)] // CHECK:STDOUT: %.loc15_29.1: Core.IntLiteral = value_of_initializer %Int.as.ImplicitAs.impl.Convert.call.loc15_29.2 [symbolic = %Int.as.ImplicitAs.impl.Convert.call.loc15_29.1 (constants.%Int.as.ImplicitAs.impl.Convert.call)] // CHECK:STDOUT: %.loc15_29.2: Core.IntLiteral = converted %N.ref, %.loc15_29.1 [symbolic = %Int.as.ImplicitAs.impl.Convert.call.loc15_29.1 (constants.%Int.as.ImplicitAs.impl.Convert.call)] // CHECK:STDOUT: %array_type.loc15_30.2: type = array_type %.loc15_29.2, %i32.loc15_24 [symbolic = %array_type.loc15_30.1 (constants.%array_type)] // CHECK:STDOUT: %ptr.loc15_31.2: type = ptr_type %array_type.loc15_30.2 [symbolic = %ptr.loc15_31.1 (constants.%ptr)] // CHECK:STDOUT: } // CHECK:STDOUT: %a: @F.%ptr.loc15_31.1 (%ptr) = value_binding a, %a.param // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic fn @F(%N.loc15_6.2: %i32) { // CHECK:STDOUT: %N.loc15_6.1: %i32 = symbolic_binding N, 0 [symbolic = %N.loc15_6.1 (constants.%N.5de)] // CHECK:STDOUT: %Int.as.ImplicitAs.impl.Convert.bound: = bound_method %N.loc15_6.1, constants.%Int.as.ImplicitAs.impl.Convert.dd4 [symbolic = %Int.as.ImplicitAs.impl.Convert.bound (constants.%Int.as.ImplicitAs.impl.Convert.bound)] // CHECK:STDOUT: %bound_method.loc15_29.1: = bound_method %N.loc15_6.1, constants.%Int.as.ImplicitAs.impl.Convert.specific_fn [symbolic = %bound_method.loc15_29.1 (constants.%bound_method)] // CHECK:STDOUT: %Int.as.ImplicitAs.impl.Convert.call.loc15_29.1: init Core.IntLiteral = call %bound_method.loc15_29.1(%N.loc15_6.1) [symbolic = %Int.as.ImplicitAs.impl.Convert.call.loc15_29.1 (constants.%Int.as.ImplicitAs.impl.Convert.call)] // CHECK:STDOUT: %array_type.loc15_30.1: type = array_type %Int.as.ImplicitAs.impl.Convert.call.loc15_29.1, constants.%i32 [symbolic = %array_type.loc15_30.1 (constants.%array_type)] // CHECK:STDOUT: %ptr.loc15_31.1: type = ptr_type %array_type.loc15_30.1 [symbolic = %ptr.loc15_31.1 (constants.%ptr)] // CHECK:STDOUT: %pattern_type: type = pattern_type %ptr.loc15_31.1 [symbolic = %pattern_type (constants.%pattern_type.bb8)] // CHECK:STDOUT: // CHECK:STDOUT: fn(%a.param: @F.%ptr.loc15_31.1 (%ptr)); // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @F(constants.%N.5de) { // CHECK:STDOUT: %N.loc15_6.1 => constants.%N.5de // CHECK:STDOUT: %Int.as.ImplicitAs.impl.Convert.bound => constants.%Int.as.ImplicitAs.impl.Convert.bound // CHECK:STDOUT: %bound_method.loc15_29.1 => constants.%bound_method // CHECK:STDOUT: %Int.as.ImplicitAs.impl.Convert.call.loc15_29.1 => constants.%Int.as.ImplicitAs.impl.Convert.call // CHECK:STDOUT: %array_type.loc15_30.1 => constants.%array_type // CHECK:STDOUT: %ptr.loc15_31.1 => constants.%ptr // CHECK:STDOUT: %pattern_type => constants.%pattern_type.bb8 // CHECK:STDOUT: } // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/function/generic/redeclare.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/convert.carbon // TODO: Add ranges and switch to "--dump-sem-ir-ranges=only". // EXTRA-ARGS: --dump-sem-ir-ranges=if-present // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/function/generic/redeclare.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/function/generic/redeclare.carbon // --- redeclare.carbon library "[[@TEST_NAME]]"; fn F(T:! type) -> T*; fn F(T:! type) -> T* { return F(T); } // --- fail_different_return_type.carbon library "[[@TEST_NAME]]"; fn F(T:! type, U:! type) -> T*; // CHECK:STDERR: fail_different_return_type.carbon:[[@LINE+7]]:1: error: function redeclaration differs because return type is `U*` [FunctionRedeclReturnTypeDiffers] // CHECK:STDERR: fn F(T:! type, U:! type) -> U* { // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: fail_different_return_type.carbon:[[@LINE-5]]:1: note: previously declared with return type `T*` [FunctionRedeclReturnTypePrevious] // CHECK:STDERR: fn F(T:! type, U:! type) -> T*; // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: fn F(T:! type, U:! type) -> U* { // CHECK:STDERR: fail_different_return_type.carbon:[[@LINE+7]]:10: error: 1 argument passed to function expecting 2 arguments [CallArgCountMismatch] // CHECK:STDERR: return F(T); // CHECK:STDERR: ^~~~ // CHECK:STDERR: fail_different_return_type.carbon:[[@LINE-13]]:1: note: calling function declared here [InCallToEntity] // CHECK:STDERR: fn F(T:! type, U:! type) -> T*; // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: return F(T); } // --- fail_reorder.carbon library "[[@TEST_NAME]]"; fn F(T:! type, U:! type) -> T*; // CHECK:STDERR: fail_reorder.carbon:[[@LINE+7]]:13: error: redeclaration differs at parameter 1 [RedeclParamDiffers] // CHECK:STDERR: fn F(unused U:! type, T:! type) -> T* { // CHECK:STDERR: ^ // CHECK:STDERR: fail_reorder.carbon:[[@LINE-5]]:6: note: previous declaration's corresponding parameter here [RedeclParamPrevious] // CHECK:STDERR: fn F(T:! type, U:! type) -> T*; // CHECK:STDERR: ^ // CHECK:STDERR: fn F(unused U:! type, T:! type) -> T* { // CHECK:STDERR: fail_reorder.carbon:[[@LINE+7]]:10: error: 1 argument passed to function expecting 2 arguments [CallArgCountMismatch] // CHECK:STDERR: return F(T); // CHECK:STDERR: ^~~~ // CHECK:STDERR: fail_reorder.carbon:[[@LINE-13]]:1: note: calling function declared here [InCallToEntity] // CHECK:STDERR: fn F(T:! type, U:! type) -> T*; // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: return F(T); } // --- fail_rename.carbon library "[[@TEST_NAME]]"; fn F(T:! type, U:! type) -> T*; // CHECK:STDERR: fail_rename.carbon:[[@LINE+7]]:6: error: redeclaration differs at parameter 1 [RedeclParamDiffers] // CHECK:STDERR: fn F(U:! type, T:! type) -> U* { // CHECK:STDERR: ^ // CHECK:STDERR: fail_rename.carbon:[[@LINE-5]]:6: note: previous declaration's corresponding parameter here [RedeclParamPrevious] // CHECK:STDERR: fn F(T:! type, U:! type) -> T*; // CHECK:STDERR: ^ // CHECK:STDERR: fn F(U:! type, T:! type) -> U* { // CHECK:STDERR: fail_rename.carbon:[[@LINE+7]]:10: error: 1 argument passed to function expecting 2 arguments [CallArgCountMismatch] // CHECK:STDERR: return F(T); // CHECK:STDERR: ^~~~ // CHECK:STDERR: fail_rename.carbon:[[@LINE-13]]:1: note: calling function declared here [InCallToEntity] // CHECK:STDERR: fn F(T:! type, U:! type) -> T*; // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: return F(T); } // CHECK:STDOUT: --- redeclare.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %type: type = facet_type [concrete] // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self] // CHECK:STDOUT: %pattern_type.98f: type = pattern_type type [concrete] // CHECK:STDOUT: %T: type = symbolic_binding T, 0 [symbolic] // CHECK:STDOUT: %ptr: type = ptr_type %T [symbolic] // CHECK:STDOUT: %.cb6: Core.Form = init_form %ptr [symbolic] // CHECK:STDOUT: %pattern_type.4f4: type = pattern_type %ptr [symbolic] // CHECK:STDOUT: %F.type: type = fn_type @F [concrete] // CHECK:STDOUT: %F: %F.type = struct_value () [concrete] // CHECK:STDOUT: %require_complete: = require_complete_type %ptr [symbolic] // CHECK:STDOUT: %F.specific_fn: = specific_function %F, @F(%T) [symbolic] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .F = %F.decl.loc4 // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %F.decl.loc4: %F.type = fn_decl @F [concrete = constants.%F] { // CHECK:STDOUT: %T.patt: %pattern_type.98f = symbolic_binding_pattern T, 0 [concrete] // CHECK:STDOUT: %return.patt: @F.%pattern_type (%pattern_type.4f4) = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: @F.%pattern_type (%pattern_type.4f4) = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %T.ref.loc4: type = name_ref T, %T.loc4_6.2 [symbolic = %T.loc4_6.1 (constants.%T)] // CHECK:STDOUT: %ptr.loc4_20.2: type = ptr_type %T.ref.loc4 [symbolic = %ptr.loc4_20.1 (constants.%ptr)] // CHECK:STDOUT: %.loc4_20.2: Core.Form = init_form %ptr.loc4_20.2 [symbolic = %.loc4_20.1 (constants.%.cb6)] // CHECK:STDOUT: %.loc4_10.1: type = splice_block %.loc4_10.2 [concrete = type] { // CHECK:STDOUT: %.Self.2: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %.loc4_10.2: type = type_literal type [concrete = type] // CHECK:STDOUT: } // CHECK:STDOUT: %T.loc4_6.2: type = symbolic_binding T, 0 [symbolic = %T.loc4_6.1 (constants.%T)] // CHECK:STDOUT: %return.param.loc4: ref @F.%ptr.loc4_20.1 (%ptr) = out_param call_param0 // CHECK:STDOUT: %return.loc4: ref @F.%ptr.loc4_20.1 (%ptr) = return_slot %return.param.loc4 // CHECK:STDOUT: } // CHECK:STDOUT: %F.decl.loc6: %F.type = fn_decl @F [concrete = constants.%F] { // CHECK:STDOUT: %T.patt: %pattern_type.98f = symbolic_binding_pattern T, 0 [concrete] // CHECK:STDOUT: %return.patt: @F.%pattern_type (%pattern_type.4f4) = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: @F.%pattern_type (%pattern_type.4f4) = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %T.ref.loc6: type = name_ref T, %T.loc6 [symbolic = %T.loc4_6.1 (constants.%T)] // CHECK:STDOUT: %ptr.loc6: type = ptr_type %T.ref.loc6 [symbolic = %ptr.loc4_20.1 (constants.%ptr)] // CHECK:STDOUT: %.loc6_20: Core.Form = init_form %ptr.loc6 [symbolic = %.loc4_20.1 (constants.%.cb6)] // CHECK:STDOUT: %.loc6_10.1: type = splice_block %.loc6_10.2 [concrete = type] { // CHECK:STDOUT: %.Self.1: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %.loc6_10.2: type = type_literal type [concrete = type] // CHECK:STDOUT: } // CHECK:STDOUT: %T.loc6: type = symbolic_binding T, 0 [symbolic = %T.loc4_6.1 (constants.%T)] // CHECK:STDOUT: %return.param.loc6: ref @F.%ptr.loc4_20.1 (%ptr) = out_param call_param0 // CHECK:STDOUT: %return.loc6: ref @F.%ptr.loc4_20.1 (%ptr) = return_slot %return.param.loc6 // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic fn @F(%T.loc4_6.2: type) { // CHECK:STDOUT: %T.loc4_6.1: type = symbolic_binding T, 0 [symbolic = %T.loc4_6.1 (constants.%T)] // CHECK:STDOUT: %ptr.loc4_20.1: type = ptr_type %T.loc4_6.1 [symbolic = %ptr.loc4_20.1 (constants.%ptr)] // CHECK:STDOUT: %.loc4_20.1: Core.Form = init_form %ptr.loc4_20.1 [symbolic = %.loc4_20.1 (constants.%.cb6)] // CHECK:STDOUT: %pattern_type: type = pattern_type %ptr.loc4_20.1 [symbolic = %pattern_type (constants.%pattern_type.4f4)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete: = require_complete_type %ptr.loc4_20.1 [symbolic = %require_complete (constants.%require_complete)] // CHECK:STDOUT: %F.specific_fn.loc7_10.2: = specific_function constants.%F, @F(%T.loc4_6.1) [symbolic = %F.specific_fn.loc7_10.2 (constants.%F.specific_fn)] // CHECK:STDOUT: // CHECK:STDOUT: fn() -> out %return.param.loc6: @F.%ptr.loc4_20.1 (%ptr) { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %F.ref: %F.type = name_ref F, file.%F.decl.loc4 [concrete = constants.%F] // CHECK:STDOUT: %T.ref.loc7: type = name_ref T, %T.loc6 [symbolic = %T.loc4_6.1 (constants.%T)] // CHECK:STDOUT: %F.specific_fn.loc7_10.1: = specific_function %F.ref, @F(constants.%T) [symbolic = %F.specific_fn.loc7_10.2 (constants.%F.specific_fn)] // CHECK:STDOUT: %F.call: init @F.%ptr.loc4_20.1 (%ptr) = call %F.specific_fn.loc7_10.1() // CHECK:STDOUT: return %F.call // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @F(constants.%T) { // CHECK:STDOUT: %T.loc4_6.1 => constants.%T // CHECK:STDOUT: %ptr.loc4_20.1 => constants.%ptr // CHECK:STDOUT: %.loc4_20.1 => constants.%.cb6 // CHECK:STDOUT: %pattern_type => constants.%pattern_type.4f4 // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete => constants.%require_complete // CHECK:STDOUT: %F.specific_fn.loc7_10.2 => constants.%F.specific_fn // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- fail_different_return_type.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %type: type = facet_type [concrete] // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self] // CHECK:STDOUT: %pattern_type.98f: type = pattern_type type [concrete] // CHECK:STDOUT: %T: type = symbolic_binding T, 0 [symbolic] // CHECK:STDOUT: %U: type = symbolic_binding U, 1 [symbolic] // CHECK:STDOUT: %ptr.e8f: type = ptr_type %T [symbolic] // CHECK:STDOUT: %.cb6: Core.Form = init_form %ptr.e8f [symbolic] // CHECK:STDOUT: %pattern_type.4f4: type = pattern_type %ptr.e8f [symbolic] // CHECK:STDOUT: %F.type.117dbc.1: type = fn_type @F.loc4 [concrete] // CHECK:STDOUT: %F.d98bd5.1: %F.type.117dbc.1 = struct_value () [concrete] // CHECK:STDOUT: %ptr.18e: type = ptr_type %U [symbolic] // CHECK:STDOUT: %.6d8: Core.Form = init_form %ptr.18e [symbolic] // CHECK:STDOUT: %pattern_type.423: type = pattern_type %ptr.18e [symbolic] // CHECK:STDOUT: %F.type.117dbc.2: type = fn_type @F.loc13 [concrete] // CHECK:STDOUT: %F.d98bd5.2: %F.type.117dbc.2 = struct_value () [concrete] // CHECK:STDOUT: %require_complete: = require_complete_type %ptr.18e [symbolic] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .F = %F.decl.loc4 // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %F.decl.loc4: %F.type.117dbc.1 = fn_decl @F.loc4 [concrete = constants.%F.d98bd5.1] { // CHECK:STDOUT: %T.patt: %pattern_type.98f = symbolic_binding_pattern T, 0 [concrete] // CHECK:STDOUT: %U.patt: %pattern_type.98f = symbolic_binding_pattern U, 1 [concrete] // CHECK:STDOUT: %return.patt: @F.loc4.%pattern_type (%pattern_type.4f4) = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: @F.loc4.%pattern_type (%pattern_type.4f4) = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %T.ref: type = name_ref T, %T.loc4_6.2 [symbolic = %T.loc4_6.1 (constants.%T)] // CHECK:STDOUT: %ptr.loc4_30.2: type = ptr_type %T.ref [symbolic = %ptr.loc4_30.1 (constants.%ptr.e8f)] // CHECK:STDOUT: %.loc4_30.2: Core.Form = init_form %ptr.loc4_30.2 [symbolic = %.loc4_30.1 (constants.%.cb6)] // CHECK:STDOUT: %.loc4_10.1: type = splice_block %.loc4_10.2 [concrete = type] { // CHECK:STDOUT: %.Self.2: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %.loc4_10.2: type = type_literal type [concrete = type] // CHECK:STDOUT: } // CHECK:STDOUT: %T.loc4_6.2: type = symbolic_binding T, 0 [symbolic = %T.loc4_6.1 (constants.%T)] // CHECK:STDOUT: %.loc4_20.1: type = splice_block %.loc4_20.2 [concrete = type] { // CHECK:STDOUT: %.Self.1: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %.loc4_20.2: type = type_literal type [concrete = type] // CHECK:STDOUT: } // CHECK:STDOUT: %U.loc4_16.2: type = symbolic_binding U, 1 [symbolic = %U.loc4_16.1 (constants.%U)] // CHECK:STDOUT: %return.param: ref @F.loc4.%ptr.loc4_30.1 (%ptr.e8f) = out_param call_param0 // CHECK:STDOUT: %return: ref @F.loc4.%ptr.loc4_30.1 (%ptr.e8f) = return_slot %return.param // CHECK:STDOUT: } // CHECK:STDOUT: %F.decl.loc13: %F.type.117dbc.2 = fn_decl @F.loc13 [concrete = constants.%F.d98bd5.2] { // CHECK:STDOUT: %T.patt: %pattern_type.98f = symbolic_binding_pattern T, 0 [concrete] // CHECK:STDOUT: %U.patt: %pattern_type.98f = symbolic_binding_pattern U, 1 [concrete] // CHECK:STDOUT: %return.patt: @F.loc13.%pattern_type (%pattern_type.423) = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: @F.loc13.%pattern_type (%pattern_type.423) = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %U.ref: type = name_ref U, %U.loc13_16.2 [symbolic = %U.loc13_16.1 (constants.%U)] // CHECK:STDOUT: %ptr.loc13_30.2: type = ptr_type %U.ref [symbolic = %ptr.loc13_30.1 (constants.%ptr.18e)] // CHECK:STDOUT: %.loc13_30.2: Core.Form = init_form %ptr.loc13_30.2 [symbolic = %.loc13_30.1 (constants.%.6d8)] // CHECK:STDOUT: %.loc13_10.1: type = splice_block %.loc13_10.2 [concrete = type] { // CHECK:STDOUT: %.Self.2: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %.loc13_10.2: type = type_literal type [concrete = type] // CHECK:STDOUT: } // CHECK:STDOUT: %T.loc13_6.2: type = symbolic_binding T, 0 [symbolic = %T.loc13_6.1 (constants.%T)] // CHECK:STDOUT: %.loc13_20.1: type = splice_block %.loc13_20.2 [concrete = type] { // CHECK:STDOUT: %.Self.1: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %.loc13_20.2: type = type_literal type [concrete = type] // CHECK:STDOUT: } // CHECK:STDOUT: %U.loc13_16.2: type = symbolic_binding U, 1 [symbolic = %U.loc13_16.1 (constants.%U)] // CHECK:STDOUT: %return.param: ref @F.loc13.%ptr.loc13_30.1 (%ptr.18e) = out_param call_param0 // CHECK:STDOUT: %return: ref @F.loc13.%ptr.loc13_30.1 (%ptr.18e) = return_slot %return.param // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic fn @F.loc4(%T.loc4_6.2: type, %U.loc4_16.2: type) { // CHECK:STDOUT: %T.loc4_6.1: type = symbolic_binding T, 0 [symbolic = %T.loc4_6.1 (constants.%T)] // CHECK:STDOUT: %U.loc4_16.1: type = symbolic_binding U, 1 [symbolic = %U.loc4_16.1 (constants.%U)] // CHECK:STDOUT: %ptr.loc4_30.1: type = ptr_type %T.loc4_6.1 [symbolic = %ptr.loc4_30.1 (constants.%ptr.e8f)] // CHECK:STDOUT: %.loc4_30.1: Core.Form = init_form %ptr.loc4_30.1 [symbolic = %.loc4_30.1 (constants.%.cb6)] // CHECK:STDOUT: %pattern_type: type = pattern_type %ptr.loc4_30.1 [symbolic = %pattern_type (constants.%pattern_type.4f4)] // CHECK:STDOUT: // CHECK:STDOUT: fn() -> out %return.param: @F.loc4.%ptr.loc4_30.1 (%ptr.e8f); // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic fn @F.loc13(%T.loc13_6.2: type, %U.loc13_16.2: type) { // CHECK:STDOUT: %T.loc13_6.1: type = symbolic_binding T, 0 [symbolic = %T.loc13_6.1 (constants.%T)] // CHECK:STDOUT: %U.loc13_16.1: type = symbolic_binding U, 1 [symbolic = %U.loc13_16.1 (constants.%U)] // CHECK:STDOUT: %ptr.loc13_30.1: type = ptr_type %U.loc13_16.1 [symbolic = %ptr.loc13_30.1 (constants.%ptr.18e)] // CHECK:STDOUT: %.loc13_30.1: Core.Form = init_form %ptr.loc13_30.1 [symbolic = %.loc13_30.1 (constants.%.6d8)] // CHECK:STDOUT: %pattern_type: type = pattern_type %ptr.loc13_30.1 [symbolic = %pattern_type (constants.%pattern_type.423)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete: = require_complete_type %ptr.loc13_30.1 [symbolic = %require_complete (constants.%require_complete)] // CHECK:STDOUT: // CHECK:STDOUT: fn() -> out %return.param: @F.loc13.%ptr.loc13_30.1 (%ptr.18e) { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %F.ref: %F.type.117dbc.1 = name_ref F, file.%F.decl.loc4 [concrete = constants.%F.d98bd5.1] // CHECK:STDOUT: %T.ref: type = name_ref T, %T.loc13_6.2 [symbolic = %T.loc13_6.1 (constants.%T)] // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @F.loc4(constants.%T, constants.%U) { // CHECK:STDOUT: %T.loc4_6.1 => constants.%T // CHECK:STDOUT: %U.loc4_16.1 => constants.%U // CHECK:STDOUT: %ptr.loc4_30.1 => constants.%ptr.e8f // CHECK:STDOUT: %.loc4_30.1 => constants.%.cb6 // CHECK:STDOUT: %pattern_type => constants.%pattern_type.4f4 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @F.loc13(constants.%T, constants.%U) { // CHECK:STDOUT: %T.loc13_6.1 => constants.%T // CHECK:STDOUT: %U.loc13_16.1 => constants.%U // CHECK:STDOUT: %ptr.loc13_30.1 => constants.%ptr.18e // CHECK:STDOUT: %.loc13_30.1 => constants.%.6d8 // CHECK:STDOUT: %pattern_type => constants.%pattern_type.423 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- fail_reorder.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %type: type = facet_type [concrete] // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self] // CHECK:STDOUT: %pattern_type.98f: type = pattern_type type [concrete] // CHECK:STDOUT: %T.67d: type = symbolic_binding T, 0 [symbolic] // CHECK:STDOUT: %U.091: type = symbolic_binding U, 1 [symbolic] // CHECK:STDOUT: %ptr.e8f: type = ptr_type %T.67d [symbolic] // CHECK:STDOUT: %.cb6: Core.Form = init_form %ptr.e8f [symbolic] // CHECK:STDOUT: %pattern_type.4f4: type = pattern_type %ptr.e8f [symbolic] // CHECK:STDOUT: %F.type.117dbc.1: type = fn_type @F.loc4 [concrete] // CHECK:STDOUT: %F.d98bd5.1: %F.type.117dbc.1 = struct_value () [concrete] // CHECK:STDOUT: %U.67d: type = symbolic_binding U, 0 [symbolic] // CHECK:STDOUT: %T.091: type = symbolic_binding T, 1 [symbolic] // CHECK:STDOUT: %ptr.18e: type = ptr_type %T.091 [symbolic] // CHECK:STDOUT: %.6d8: Core.Form = init_form %ptr.18e [symbolic] // CHECK:STDOUT: %pattern_type.423: type = pattern_type %ptr.18e [symbolic] // CHECK:STDOUT: %F.type.117dbc.2: type = fn_type @F.loc13 [concrete] // CHECK:STDOUT: %F.d98bd5.2: %F.type.117dbc.2 = struct_value () [concrete] // CHECK:STDOUT: %require_complete: = require_complete_type %ptr.18e [symbolic] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .F = %F.decl.loc4 // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %F.decl.loc4: %F.type.117dbc.1 = fn_decl @F.loc4 [concrete = constants.%F.d98bd5.1] { // CHECK:STDOUT: %T.patt: %pattern_type.98f = symbolic_binding_pattern T, 0 [concrete] // CHECK:STDOUT: %U.patt: %pattern_type.98f = symbolic_binding_pattern U, 1 [concrete] // CHECK:STDOUT: %return.patt: @F.loc4.%pattern_type (%pattern_type.4f4) = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: @F.loc4.%pattern_type (%pattern_type.4f4) = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %T.ref: type = name_ref T, %T.loc4_6.2 [symbolic = %T.loc4_6.1 (constants.%T.67d)] // CHECK:STDOUT: %ptr.loc4_30.2: type = ptr_type %T.ref [symbolic = %ptr.loc4_30.1 (constants.%ptr.e8f)] // CHECK:STDOUT: %.loc4_30.2: Core.Form = init_form %ptr.loc4_30.2 [symbolic = %.loc4_30.1 (constants.%.cb6)] // CHECK:STDOUT: %.loc4_10.1: type = splice_block %.loc4_10.2 [concrete = type] { // CHECK:STDOUT: %.Self.2: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %.loc4_10.2: type = type_literal type [concrete = type] // CHECK:STDOUT: } // CHECK:STDOUT: %T.loc4_6.2: type = symbolic_binding T, 0 [symbolic = %T.loc4_6.1 (constants.%T.67d)] // CHECK:STDOUT: %.loc4_20.1: type = splice_block %.loc4_20.2 [concrete = type] { // CHECK:STDOUT: %.Self.1: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %.loc4_20.2: type = type_literal type [concrete = type] // CHECK:STDOUT: } // CHECK:STDOUT: %U.loc4_16.2: type = symbolic_binding U, 1 [symbolic = %U.loc4_16.1 (constants.%U.091)] // CHECK:STDOUT: %return.param: ref @F.loc4.%ptr.loc4_30.1 (%ptr.e8f) = out_param call_param0 // CHECK:STDOUT: %return: ref @F.loc4.%ptr.loc4_30.1 (%ptr.e8f) = return_slot %return.param // CHECK:STDOUT: } // CHECK:STDOUT: %F.decl.loc13: %F.type.117dbc.2 = fn_decl @F.loc13 [concrete = constants.%F.d98bd5.2] { // CHECK:STDOUT: %U.patt: %pattern_type.98f = symbolic_binding_pattern U, 0 [concrete] // CHECK:STDOUT: %T.patt: %pattern_type.98f = symbolic_binding_pattern T, 1 [concrete] // CHECK:STDOUT: %return.patt: @F.loc13.%pattern_type (%pattern_type.423) = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: @F.loc13.%pattern_type (%pattern_type.423) = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %T.ref.loc13: type = name_ref T, %T.loc13_23.2 [symbolic = %T.loc13_23.1 (constants.%T.091)] // CHECK:STDOUT: %ptr.loc13_37.2: type = ptr_type %T.ref.loc13 [symbolic = %ptr.loc13_37.1 (constants.%ptr.18e)] // CHECK:STDOUT: %.loc13_37.2: Core.Form = init_form %ptr.loc13_37.2 [symbolic = %.loc13_37.1 (constants.%.6d8)] // CHECK:STDOUT: %.loc13_17.1: type = splice_block %.loc13_17.2 [concrete = type] { // CHECK:STDOUT: %.Self.2: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %.loc13_17.2: type = type_literal type [concrete = type] // CHECK:STDOUT: } // CHECK:STDOUT: %U.loc13_13.2: type = symbolic_binding U, 0 [symbolic = %U.loc13_13.1 (constants.%U.67d)] // CHECK:STDOUT: %.loc13_27.1: type = splice_block %.loc13_27.2 [concrete = type] { // CHECK:STDOUT: %.Self.1: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %.loc13_27.2: type = type_literal type [concrete = type] // CHECK:STDOUT: } // CHECK:STDOUT: %T.loc13_23.2: type = symbolic_binding T, 1 [symbolic = %T.loc13_23.1 (constants.%T.091)] // CHECK:STDOUT: %return.param: ref @F.loc13.%ptr.loc13_37.1 (%ptr.18e) = out_param call_param0 // CHECK:STDOUT: %return: ref @F.loc13.%ptr.loc13_37.1 (%ptr.18e) = return_slot %return.param // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic fn @F.loc4(%T.loc4_6.2: type, %U.loc4_16.2: type) { // CHECK:STDOUT: %T.loc4_6.1: type = symbolic_binding T, 0 [symbolic = %T.loc4_6.1 (constants.%T.67d)] // CHECK:STDOUT: %U.loc4_16.1: type = symbolic_binding U, 1 [symbolic = %U.loc4_16.1 (constants.%U.091)] // CHECK:STDOUT: %ptr.loc4_30.1: type = ptr_type %T.loc4_6.1 [symbolic = %ptr.loc4_30.1 (constants.%ptr.e8f)] // CHECK:STDOUT: %.loc4_30.1: Core.Form = init_form %ptr.loc4_30.1 [symbolic = %.loc4_30.1 (constants.%.cb6)] // CHECK:STDOUT: %pattern_type: type = pattern_type %ptr.loc4_30.1 [symbolic = %pattern_type (constants.%pattern_type.4f4)] // CHECK:STDOUT: // CHECK:STDOUT: fn() -> out %return.param: @F.loc4.%ptr.loc4_30.1 (%ptr.e8f); // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic fn @F.loc13(%U.loc13_13.2: type, %T.loc13_23.2: type) { // CHECK:STDOUT: %U.loc13_13.1: type = symbolic_binding U, 0 [symbolic = %U.loc13_13.1 (constants.%U.67d)] // CHECK:STDOUT: %T.loc13_23.1: type = symbolic_binding T, 1 [symbolic = %T.loc13_23.1 (constants.%T.091)] // CHECK:STDOUT: %ptr.loc13_37.1: type = ptr_type %T.loc13_23.1 [symbolic = %ptr.loc13_37.1 (constants.%ptr.18e)] // CHECK:STDOUT: %.loc13_37.1: Core.Form = init_form %ptr.loc13_37.1 [symbolic = %.loc13_37.1 (constants.%.6d8)] // CHECK:STDOUT: %pattern_type: type = pattern_type %ptr.loc13_37.1 [symbolic = %pattern_type (constants.%pattern_type.423)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete: = require_complete_type %ptr.loc13_37.1 [symbolic = %require_complete (constants.%require_complete)] // CHECK:STDOUT: // CHECK:STDOUT: fn() -> out %return.param: @F.loc13.%ptr.loc13_37.1 (%ptr.18e) { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %F.ref: %F.type.117dbc.1 = name_ref F, file.%F.decl.loc4 [concrete = constants.%F.d98bd5.1] // CHECK:STDOUT: %T.ref.loc21: type = name_ref T, %T.loc13_23.2 [symbolic = %T.loc13_23.1 (constants.%T.091)] // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @F.loc4(constants.%T.67d, constants.%U.091) { // CHECK:STDOUT: %T.loc4_6.1 => constants.%T.67d // CHECK:STDOUT: %U.loc4_16.1 => constants.%U.091 // CHECK:STDOUT: %ptr.loc4_30.1 => constants.%ptr.e8f // CHECK:STDOUT: %.loc4_30.1 => constants.%.cb6 // CHECK:STDOUT: %pattern_type => constants.%pattern_type.4f4 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @F.loc13(constants.%U.67d, constants.%T.091) { // CHECK:STDOUT: %U.loc13_13.1 => constants.%U.67d // CHECK:STDOUT: %T.loc13_23.1 => constants.%T.091 // CHECK:STDOUT: %ptr.loc13_37.1 => constants.%ptr.18e // CHECK:STDOUT: %.loc13_37.1 => constants.%.6d8 // CHECK:STDOUT: %pattern_type => constants.%pattern_type.423 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- fail_rename.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %type: type = facet_type [concrete] // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self] // CHECK:STDOUT: %pattern_type.98f: type = pattern_type type [concrete] // CHECK:STDOUT: %T.67d: type = symbolic_binding T, 0 [symbolic] // CHECK:STDOUT: %U.091: type = symbolic_binding U, 1 [symbolic] // CHECK:STDOUT: %ptr.e8f8f9.1: type = ptr_type %T.67d [symbolic] // CHECK:STDOUT: %.cb6cb9.1: Core.Form = init_form %ptr.e8f8f9.1 [symbolic] // CHECK:STDOUT: %pattern_type.4f4b84.1: type = pattern_type %ptr.e8f8f9.1 [symbolic] // CHECK:STDOUT: %F.type.117dbc.1: type = fn_type @F.loc4 [concrete] // CHECK:STDOUT: %F.d98bd5.1: %F.type.117dbc.1 = struct_value () [concrete] // CHECK:STDOUT: %U.67d: type = symbolic_binding U, 0 [symbolic] // CHECK:STDOUT: %T.091: type = symbolic_binding T, 1 [symbolic] // CHECK:STDOUT: %ptr.e8f8f9.2: type = ptr_type %U.67d [symbolic] // CHECK:STDOUT: %.cb6cb9.2: Core.Form = init_form %ptr.e8f8f9.2 [symbolic] // CHECK:STDOUT: %pattern_type.4f4b84.2: type = pattern_type %ptr.e8f8f9.2 [symbolic] // CHECK:STDOUT: %F.type.117dbc.2: type = fn_type @F.loc13 [concrete] // CHECK:STDOUT: %F.d98bd5.2: %F.type.117dbc.2 = struct_value () [concrete] // CHECK:STDOUT: %require_complete: = require_complete_type %ptr.e8f8f9.2 [symbolic] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .F = %F.decl.loc4 // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %F.decl.loc4: %F.type.117dbc.1 = fn_decl @F.loc4 [concrete = constants.%F.d98bd5.1] { // CHECK:STDOUT: %T.patt: %pattern_type.98f = symbolic_binding_pattern T, 0 [concrete] // CHECK:STDOUT: %U.patt: %pattern_type.98f = symbolic_binding_pattern U, 1 [concrete] // CHECK:STDOUT: %return.patt: @F.loc4.%pattern_type (%pattern_type.4f4b84.1) = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: @F.loc4.%pattern_type (%pattern_type.4f4b84.1) = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %T.ref: type = name_ref T, %T.loc4_6.2 [symbolic = %T.loc4_6.1 (constants.%T.67d)] // CHECK:STDOUT: %ptr.loc4_30.2: type = ptr_type %T.ref [symbolic = %ptr.loc4_30.1 (constants.%ptr.e8f8f9.1)] // CHECK:STDOUT: %.loc4_30.2: Core.Form = init_form %ptr.loc4_30.2 [symbolic = %.loc4_30.1 (constants.%.cb6cb9.1)] // CHECK:STDOUT: %.loc4_10.1: type = splice_block %.loc4_10.2 [concrete = type] { // CHECK:STDOUT: %.Self.2: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %.loc4_10.2: type = type_literal type [concrete = type] // CHECK:STDOUT: } // CHECK:STDOUT: %T.loc4_6.2: type = symbolic_binding T, 0 [symbolic = %T.loc4_6.1 (constants.%T.67d)] // CHECK:STDOUT: %.loc4_20.1: type = splice_block %.loc4_20.2 [concrete = type] { // CHECK:STDOUT: %.Self.1: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %.loc4_20.2: type = type_literal type [concrete = type] // CHECK:STDOUT: } // CHECK:STDOUT: %U.loc4_16.2: type = symbolic_binding U, 1 [symbolic = %U.loc4_16.1 (constants.%U.091)] // CHECK:STDOUT: %return.param: ref @F.loc4.%ptr.loc4_30.1 (%ptr.e8f8f9.1) = out_param call_param0 // CHECK:STDOUT: %return: ref @F.loc4.%ptr.loc4_30.1 (%ptr.e8f8f9.1) = return_slot %return.param // CHECK:STDOUT: } // CHECK:STDOUT: %F.decl.loc13: %F.type.117dbc.2 = fn_decl @F.loc13 [concrete = constants.%F.d98bd5.2] { // CHECK:STDOUT: %U.patt: %pattern_type.98f = symbolic_binding_pattern U, 0 [concrete] // CHECK:STDOUT: %T.patt: %pattern_type.98f = symbolic_binding_pattern T, 1 [concrete] // CHECK:STDOUT: %return.patt: @F.loc13.%pattern_type (%pattern_type.4f4b84.2) = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: @F.loc13.%pattern_type (%pattern_type.4f4b84.2) = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %U.ref: type = name_ref U, %U.loc13_6.2 [symbolic = %U.loc13_6.1 (constants.%U.67d)] // CHECK:STDOUT: %ptr.loc13_30.2: type = ptr_type %U.ref [symbolic = %ptr.loc13_30.1 (constants.%ptr.e8f8f9.2)] // CHECK:STDOUT: %.loc13_30.2: Core.Form = init_form %ptr.loc13_30.2 [symbolic = %.loc13_30.1 (constants.%.cb6cb9.2)] // CHECK:STDOUT: %.loc13_10.1: type = splice_block %.loc13_10.2 [concrete = type] { // CHECK:STDOUT: %.Self.2: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %.loc13_10.2: type = type_literal type [concrete = type] // CHECK:STDOUT: } // CHECK:STDOUT: %U.loc13_6.2: type = symbolic_binding U, 0 [symbolic = %U.loc13_6.1 (constants.%U.67d)] // CHECK:STDOUT: %.loc13_20.1: type = splice_block %.loc13_20.2 [concrete = type] { // CHECK:STDOUT: %.Self.1: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %.loc13_20.2: type = type_literal type [concrete = type] // CHECK:STDOUT: } // CHECK:STDOUT: %T.loc13_16.2: type = symbolic_binding T, 1 [symbolic = %T.loc13_16.1 (constants.%T.091)] // CHECK:STDOUT: %return.param: ref @F.loc13.%ptr.loc13_30.1 (%ptr.e8f8f9.2) = out_param call_param0 // CHECK:STDOUT: %return: ref @F.loc13.%ptr.loc13_30.1 (%ptr.e8f8f9.2) = return_slot %return.param // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic fn @F.loc4(%T.loc4_6.2: type, %U.loc4_16.2: type) { // CHECK:STDOUT: %T.loc4_6.1: type = symbolic_binding T, 0 [symbolic = %T.loc4_6.1 (constants.%T.67d)] // CHECK:STDOUT: %U.loc4_16.1: type = symbolic_binding U, 1 [symbolic = %U.loc4_16.1 (constants.%U.091)] // CHECK:STDOUT: %ptr.loc4_30.1: type = ptr_type %T.loc4_6.1 [symbolic = %ptr.loc4_30.1 (constants.%ptr.e8f8f9.1)] // CHECK:STDOUT: %.loc4_30.1: Core.Form = init_form %ptr.loc4_30.1 [symbolic = %.loc4_30.1 (constants.%.cb6cb9.1)] // CHECK:STDOUT: %pattern_type: type = pattern_type %ptr.loc4_30.1 [symbolic = %pattern_type (constants.%pattern_type.4f4b84.1)] // CHECK:STDOUT: // CHECK:STDOUT: fn() -> out %return.param: @F.loc4.%ptr.loc4_30.1 (%ptr.e8f8f9.1); // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic fn @F.loc13(%U.loc13_6.2: type, %T.loc13_16.2: type) { // CHECK:STDOUT: %U.loc13_6.1: type = symbolic_binding U, 0 [symbolic = %U.loc13_6.1 (constants.%U.67d)] // CHECK:STDOUT: %T.loc13_16.1: type = symbolic_binding T, 1 [symbolic = %T.loc13_16.1 (constants.%T.091)] // CHECK:STDOUT: %ptr.loc13_30.1: type = ptr_type %U.loc13_6.1 [symbolic = %ptr.loc13_30.1 (constants.%ptr.e8f8f9.2)] // CHECK:STDOUT: %.loc13_30.1: Core.Form = init_form %ptr.loc13_30.1 [symbolic = %.loc13_30.1 (constants.%.cb6cb9.2)] // CHECK:STDOUT: %pattern_type: type = pattern_type %ptr.loc13_30.1 [symbolic = %pattern_type (constants.%pattern_type.4f4b84.2)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete: = require_complete_type %ptr.loc13_30.1 [symbolic = %require_complete (constants.%require_complete)] // CHECK:STDOUT: // CHECK:STDOUT: fn() -> out %return.param: @F.loc13.%ptr.loc13_30.1 (%ptr.e8f8f9.2) { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %F.ref: %F.type.117dbc.1 = name_ref F, file.%F.decl.loc4 [concrete = constants.%F.d98bd5.1] // CHECK:STDOUT: %T.ref: type = name_ref T, %T.loc13_16.2 [symbolic = %T.loc13_16.1 (constants.%T.091)] // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @F.loc4(constants.%T.67d, constants.%U.091) { // CHECK:STDOUT: %T.loc4_6.1 => constants.%T.67d // CHECK:STDOUT: %U.loc4_16.1 => constants.%U.091 // CHECK:STDOUT: %ptr.loc4_30.1 => constants.%ptr.e8f8f9.1 // CHECK:STDOUT: %.loc4_30.1 => constants.%.cb6cb9.1 // CHECK:STDOUT: %pattern_type => constants.%pattern_type.4f4b84.1 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @F.loc13(constants.%U.67d, constants.%T.091) { // CHECK:STDOUT: %U.loc13_6.1 => constants.%U.67d // CHECK:STDOUT: %T.loc13_16.1 => constants.%T.091 // CHECK:STDOUT: %ptr.loc13_30.1 => constants.%ptr.e8f8f9.2 // CHECK:STDOUT: %.loc13_30.1 => constants.%.cb6cb9.2 // CHECK:STDOUT: %pattern_type => constants.%pattern_type.4f4b84.2 // CHECK:STDOUT: } // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/function/generic/resolve_used.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/int.carbon // TODO: Add ranges and switch to "--dump-sem-ir-ranges=only". // EXTRA-ARGS: --dump-sem-ir-ranges=if-present // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/function/generic/resolve_used.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/function/generic/resolve_used.carbon // --- fail_todo_call_monomorphization_error.carbon library "[[@TEST_NAME]]"; fn ErrorIfNIsZero(N:! Core.IntLiteral()) { // Check that we resolve the definition of a used specific function by // ensuring we produce an error when doing so. Notionally this error is // produced as a result of instantiating the `Core.Int` template, although // that's not how we currently model `Core.Int`. var unused v: Core.Int(N); } fn CallNegative() { // CHECK:STDERR: fail_todo_call_monomorphization_error.carbon:[[@LINE+10]]:3: error: unable to monomorphize specific `ErrorIfNIsZero(0)` [ResolvingSpecificHere] // CHECK:STDERR: ErrorIfNIsZero(0); // CHECK:STDERR: ^~~~~~~~~~~~~~ // CHECK:STDERR: fail_todo_call_monomorphization_error.carbon:[[@LINE-7]]:17: note: `Core.Int(N)` evaluates to incomplete type `i0` [IncompleteTypeInMonomorphization] // CHECK:STDERR: var unused v: Core.Int(N); // CHECK:STDERR: ^~~~~~~~~~~ // CHECK:STDERR: min_prelude/parts/int.carbon:11:9: note: integer type width of 0 is not positive [IntWidthNotPositive] // CHECK:STDERR: adapt MakeInt(N); // CHECK:STDERR: ^~~~~~~~~~ // CHECK:STDERR: ErrorIfNIsZero(0); } // CHECK:STDOUT: --- fail_todo_call_monomorphization_error.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %type: type = facet_type [concrete] // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self] // CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] // CHECK:STDOUT: %IntLiteral.type: type = fn_type @IntLiteral [concrete] // CHECK:STDOUT: %IntLiteral: %IntLiteral.type = struct_value () [concrete] // CHECK:STDOUT: %pattern_type.dc0: type = pattern_type Core.IntLiteral [concrete] // CHECK:STDOUT: %N: Core.IntLiteral = symbolic_binding N, 0 [symbolic] // CHECK:STDOUT: %ErrorIfNIsZero.type: type = fn_type @ErrorIfNIsZero [concrete] // CHECK:STDOUT: %ErrorIfNIsZero: %ErrorIfNIsZero.type = struct_value () [concrete] // CHECK:STDOUT: %Int.type: type = generic_class_type @Int [concrete] // CHECK:STDOUT: %Int.generic: %Int.type = struct_value () [concrete] // CHECK:STDOUT: %Int: type = class_type @Int, @Int(%N) [symbolic] // CHECK:STDOUT: %require_complete.901: = require_complete_type %Int [symbolic] // CHECK:STDOUT: %pattern_type.764: type = pattern_type %Int [symbolic] // CHECK:STDOUT: %DefaultOrUnformed.type: type = facet_type <@DefaultOrUnformed> [concrete] // CHECK:STDOUT: %T: type = symbolic_binding T, 0 [symbolic] // CHECK:STDOUT: %T.as.DefaultOrUnformed.impl.Op.type.6c6: type = fn_type @T.as.DefaultOrUnformed.impl.Op, @T.as.DefaultOrUnformed.impl(%T) [symbolic] // CHECK:STDOUT: %T.as.DefaultOrUnformed.impl.Op.ae1: %T.as.DefaultOrUnformed.impl.Op.type.6c6 = struct_value () [symbolic] // CHECK:STDOUT: %.4a9: require_specific_def_type = require_specific_def @T.as.DefaultOrUnformed.impl(%Int) [symbolic] // CHECK:STDOUT: %DefaultOrUnformed.lookup_impl_witness: = lookup_impl_witness %Int, @DefaultOrUnformed [symbolic] // CHECK:STDOUT: %DefaultOrUnformed.facet.c82: %DefaultOrUnformed.type = facet_value %Int, (%DefaultOrUnformed.lookup_impl_witness) [symbolic] // CHECK:STDOUT: %DefaultOrUnformed.WithSelf.Op.type.814: type = fn_type @DefaultOrUnformed.WithSelf.Op, @DefaultOrUnformed.WithSelf(%DefaultOrUnformed.facet.c82) [symbolic] // CHECK:STDOUT: %.ca4: type = fn_type_with_self_type %DefaultOrUnformed.WithSelf.Op.type.814, %DefaultOrUnformed.facet.c82 [symbolic] // CHECK:STDOUT: %impl.elem0.12f: %.ca4 = impl_witness_access %DefaultOrUnformed.lookup_impl_witness, element0 [symbolic] // CHECK:STDOUT: %specific_impl_fn.f0d: = specific_impl_function %impl.elem0.12f, @DefaultOrUnformed.WithSelf.Op(%DefaultOrUnformed.facet.c82) [symbolic] // CHECK:STDOUT: %Destroy.type: type = facet_type <@Destroy> [concrete] // CHECK:STDOUT: %Destroy.lookup_impl_witness: = lookup_impl_witness %Int, @Destroy [symbolic] // CHECK:STDOUT: %Destroy.facet.1c0: %Destroy.type = facet_value %Int, (%Destroy.lookup_impl_witness) [symbolic] // CHECK:STDOUT: %Destroy.WithSelf.Op.type.297: type = fn_type @Destroy.WithSelf.Op, @Destroy.WithSelf(%Destroy.facet.1c0) [symbolic] // CHECK:STDOUT: %.e63: type = fn_type_with_self_type %Destroy.WithSelf.Op.type.297, %Destroy.facet.1c0 [symbolic] // CHECK:STDOUT: %impl.elem0.602: %.e63 = impl_witness_access %Destroy.lookup_impl_witness, element0 [symbolic] // CHECK:STDOUT: %specific_impl_fn.d65: = specific_impl_function %impl.elem0.602, @Destroy.WithSelf.Op(%Destroy.facet.1c0) [symbolic] // CHECK:STDOUT: %CallNegative.type: type = fn_type @CallNegative [concrete] // CHECK:STDOUT: %CallNegative: %CallNegative.type = struct_value () [concrete] // CHECK:STDOUT: %int_0: Core.IntLiteral = int_value 0 [concrete] // CHECK:STDOUT: %ErrorIfNIsZero.specific_fn: = specific_function %ErrorIfNIsZero, @ErrorIfNIsZero(%int_0) [concrete] // CHECK:STDOUT: %i0: type = class_type @Int, @Int(%int_0) [concrete] // CHECK:STDOUT: %complete_type.d94: = complete_type_witness [concrete] // CHECK:STDOUT: %pattern_type.47b: type = pattern_type %i0 [concrete] // CHECK:STDOUT: %DefaultOrUnformed.impl_witness.879: = impl_witness imports.%DefaultOrUnformed.impl_witness_table, @T.as.DefaultOrUnformed.impl(%i0) [concrete] // CHECK:STDOUT: %T.as.DefaultOrUnformed.impl.Op.type.7ec: type = fn_type @T.as.DefaultOrUnformed.impl.Op, @T.as.DefaultOrUnformed.impl(%i0) [concrete] // CHECK:STDOUT: %T.as.DefaultOrUnformed.impl.Op.5b7: %T.as.DefaultOrUnformed.impl.Op.type.7ec = struct_value () [concrete] // CHECK:STDOUT: %.d6a: require_specific_def_type = require_specific_def @T.as.DefaultOrUnformed.impl(%i0) [concrete] // CHECK:STDOUT: %DefaultOrUnformed.facet.255: %DefaultOrUnformed.type = facet_value %i0, (%DefaultOrUnformed.impl_witness.879) [concrete] // CHECK:STDOUT: %DefaultOrUnformed.WithSelf.Op.type.bd6: type = fn_type @DefaultOrUnformed.WithSelf.Op, @DefaultOrUnformed.WithSelf(%DefaultOrUnformed.facet.255) [concrete] // CHECK:STDOUT: %.639: type = fn_type_with_self_type %DefaultOrUnformed.WithSelf.Op.type.bd6, %DefaultOrUnformed.facet.255 [concrete] // CHECK:STDOUT: %T.as.DefaultOrUnformed.impl.Op.specific_fn: = specific_function %T.as.DefaultOrUnformed.impl.Op.5b7, @T.as.DefaultOrUnformed.impl.Op(%i0) [concrete] // CHECK:STDOUT: %Destroy.Op.type: type = fn_type @Destroy.Op [concrete] // CHECK:STDOUT: %Destroy.Op: %Destroy.Op.type = struct_value () [concrete] // CHECK:STDOUT: %custom_witness.8d7: = custom_witness (%Destroy.Op), @Destroy [concrete] // CHECK:STDOUT: %Destroy.facet.eb2: %Destroy.type = facet_value %i0, (%custom_witness.8d7) [concrete] // CHECK:STDOUT: %Destroy.WithSelf.Op.type.cef: type = fn_type @Destroy.WithSelf.Op, @Destroy.WithSelf(%Destroy.facet.eb2) [concrete] // CHECK:STDOUT: %.98d: type = fn_type_with_self_type %Destroy.WithSelf.Op.type.cef, %Destroy.facet.eb2 [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: .IntLiteral = %Core.IntLiteral // CHECK:STDOUT: .Int = %Core.Int // CHECK:STDOUT: .DefaultOrUnformed = %Core.DefaultOrUnformed // CHECK:STDOUT: .Destroy = %Core.Destroy // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: %Core.IntLiteral: %IntLiteral.type = import_ref Core//prelude/parts/int_literal, IntLiteral, loaded [concrete = constants.%IntLiteral] // CHECK:STDOUT: %Core.Int: %Int.type = import_ref Core//prelude/parts/int, Int, loaded [concrete = constants.%Int.generic] // CHECK:STDOUT: %Core.DefaultOrUnformed: type = import_ref Core//prelude/parts/default, DefaultOrUnformed, loaded [concrete = constants.%DefaultOrUnformed.type] // CHECK:STDOUT: %Core.import_ref.01d: @T.as.DefaultOrUnformed.impl.%T.as.DefaultOrUnformed.impl.Op.type (%T.as.DefaultOrUnformed.impl.Op.type.6c6) = import_ref Core//prelude/parts/default, loc{{\d+_\d+}}, loaded [symbolic = @T.as.DefaultOrUnformed.impl.%T.as.DefaultOrUnformed.impl.Op (constants.%T.as.DefaultOrUnformed.impl.Op.ae1)] // CHECK:STDOUT: %DefaultOrUnformed.impl_witness_table = impl_witness_table (%Core.import_ref.01d), @T.as.DefaultOrUnformed.impl [concrete] // CHECK:STDOUT: %Core.Destroy: type = import_ref Core//prelude/parts/destroy, Destroy, loaded [concrete = constants.%Destroy.type] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .ErrorIfNIsZero = %ErrorIfNIsZero.decl // CHECK:STDOUT: .CallNegative = %CallNegative.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %ErrorIfNIsZero.decl: %ErrorIfNIsZero.type = fn_decl @ErrorIfNIsZero [concrete = constants.%ErrorIfNIsZero] { // CHECK:STDOUT: %N.patt: %pattern_type.dc0 = symbolic_binding_pattern N, 0 [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %.loc4_39.1: type = splice_block %.loc4_39.3 [concrete = Core.IntLiteral] { // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %Core.ref.loc4: = name_ref Core, imports.%Core [concrete = imports.%Core] // CHECK:STDOUT: %IntLiteral.ref: %IntLiteral.type = name_ref IntLiteral, imports.%Core.IntLiteral [concrete = constants.%IntLiteral] // CHECK:STDOUT: %IntLiteral.call: init type = call %IntLiteral.ref() [concrete = Core.IntLiteral] // CHECK:STDOUT: %.loc4_39.2: type = value_of_initializer %IntLiteral.call [concrete = Core.IntLiteral] // CHECK:STDOUT: %.loc4_39.3: type = converted %IntLiteral.call, %.loc4_39.2 [concrete = Core.IntLiteral] // CHECK:STDOUT: } // CHECK:STDOUT: %N.loc4_19.2: Core.IntLiteral = symbolic_binding N, 0 [symbolic = %N.loc4_19.1 (constants.%N)] // CHECK:STDOUT: } // CHECK:STDOUT: %CallNegative.decl: %CallNegative.type = fn_decl @CallNegative [concrete = constants.%CallNegative] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic fn @ErrorIfNIsZero(%N.loc4_19.2: Core.IntLiteral) { // CHECK:STDOUT: %N.loc4_19.1: Core.IntLiteral = symbolic_binding N, 0 [symbolic = %N.loc4_19.1 (constants.%N)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %Int.loc9_27.2: type = class_type @Int, @Int(%N.loc4_19.1) [symbolic = %Int.loc9_27.2 (constants.%Int)] // CHECK:STDOUT: %require_complete: = require_complete_type %Int.loc9_27.2 [symbolic = %require_complete (constants.%require_complete.901)] // CHECK:STDOUT: %pattern_type: type = pattern_type %Int.loc9_27.2 [symbolic = %pattern_type (constants.%pattern_type.764)] // CHECK:STDOUT: %.loc9_28.3: require_specific_def_type = require_specific_def @T.as.DefaultOrUnformed.impl(%Int.loc9_27.2) [symbolic = %.loc9_28.3 (constants.%.4a9)] // CHECK:STDOUT: %DefaultOrUnformed.lookup_impl_witness: = lookup_impl_witness %Int.loc9_27.2, @DefaultOrUnformed [symbolic = %DefaultOrUnformed.lookup_impl_witness (constants.%DefaultOrUnformed.lookup_impl_witness)] // CHECK:STDOUT: %DefaultOrUnformed.facet.loc9_28.2: %DefaultOrUnformed.type = facet_value %Int.loc9_27.2, (%DefaultOrUnformed.lookup_impl_witness) [symbolic = %DefaultOrUnformed.facet.loc9_28.2 (constants.%DefaultOrUnformed.facet.c82)] // CHECK:STDOUT: %DefaultOrUnformed.WithSelf.Op.type: type = fn_type @DefaultOrUnformed.WithSelf.Op, @DefaultOrUnformed.WithSelf(%DefaultOrUnformed.facet.loc9_28.2) [symbolic = %DefaultOrUnformed.WithSelf.Op.type (constants.%DefaultOrUnformed.WithSelf.Op.type.814)] // CHECK:STDOUT: %.loc9_28.4: type = fn_type_with_self_type %DefaultOrUnformed.WithSelf.Op.type, %DefaultOrUnformed.facet.loc9_28.2 [symbolic = %.loc9_28.4 (constants.%.ca4)] // CHECK:STDOUT: %impl.elem0.loc9_28.2: @ErrorIfNIsZero.%.loc9_28.4 (%.ca4) = impl_witness_access %DefaultOrUnformed.lookup_impl_witness, element0 [symbolic = %impl.elem0.loc9_28.2 (constants.%impl.elem0.12f)] // CHECK:STDOUT: %specific_impl_fn.loc9_28.2: = specific_impl_function %impl.elem0.loc9_28.2, @DefaultOrUnformed.WithSelf.Op(%DefaultOrUnformed.facet.loc9_28.2) [symbolic = %specific_impl_fn.loc9_28.2 (constants.%specific_impl_fn.f0d)] // CHECK:STDOUT: %Destroy.lookup_impl_witness: = lookup_impl_witness %Int.loc9_27.2, @Destroy [symbolic = %Destroy.lookup_impl_witness (constants.%Destroy.lookup_impl_witness)] // CHECK:STDOUT: %Destroy.facet: %Destroy.type = facet_value %Int.loc9_27.2, (%Destroy.lookup_impl_witness) [symbolic = %Destroy.facet (constants.%Destroy.facet.1c0)] // CHECK:STDOUT: %Destroy.WithSelf.Op.type: type = fn_type @Destroy.WithSelf.Op, @Destroy.WithSelf(%Destroy.facet) [symbolic = %Destroy.WithSelf.Op.type (constants.%Destroy.WithSelf.Op.type.297)] // CHECK:STDOUT: %.loc9_3: type = fn_type_with_self_type %Destroy.WithSelf.Op.type, %Destroy.facet [symbolic = %.loc9_3 (constants.%.e63)] // CHECK:STDOUT: %impl.elem0.loc9_3.2: @ErrorIfNIsZero.%.loc9_3 (%.e63) = impl_witness_access %Destroy.lookup_impl_witness, element0 [symbolic = %impl.elem0.loc9_3.2 (constants.%impl.elem0.602)] // CHECK:STDOUT: %specific_impl_fn.loc9_3.2: = specific_impl_function %impl.elem0.loc9_3.2, @Destroy.WithSelf.Op(%Destroy.facet) [symbolic = %specific_impl_fn.loc9_3.2 (constants.%specific_impl_fn.d65)] // CHECK:STDOUT: // CHECK:STDOUT: fn() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %v.patt: @ErrorIfNIsZero.%pattern_type (%pattern_type.764) = ref_binding_pattern v [concrete] // CHECK:STDOUT: %v.var_patt: @ErrorIfNIsZero.%pattern_type (%pattern_type.764) = var_pattern %v.patt [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %v.var: ref @ErrorIfNIsZero.%Int.loc9_27.2 (%Int) = var %v.var_patt // CHECK:STDOUT: %DefaultOrUnformed.facet.loc9_28.1: %DefaultOrUnformed.type = facet_value constants.%Int, (constants.%DefaultOrUnformed.lookup_impl_witness) [symbolic = %DefaultOrUnformed.facet.loc9_28.2 (constants.%DefaultOrUnformed.facet.c82)] // CHECK:STDOUT: %.loc9_28.1: %DefaultOrUnformed.type = converted constants.%Int, %DefaultOrUnformed.facet.loc9_28.1 [symbolic = %DefaultOrUnformed.facet.loc9_28.2 (constants.%DefaultOrUnformed.facet.c82)] // CHECK:STDOUT: %as_type: type = facet_access_type %.loc9_28.1 [symbolic = %Int.loc9_27.2 (constants.%Int)] // CHECK:STDOUT: %.loc9_28.2: type = converted %.loc9_28.1, %as_type [symbolic = %Int.loc9_27.2 (constants.%Int)] // CHECK:STDOUT: %impl.elem0.loc9_28.1: @ErrorIfNIsZero.%.loc9_28.4 (%.ca4) = impl_witness_access constants.%DefaultOrUnformed.lookup_impl_witness, element0 [symbolic = %impl.elem0.loc9_28.2 (constants.%impl.elem0.12f)] // CHECK:STDOUT: %specific_impl_fn.loc9_28.1: = specific_impl_function %impl.elem0.loc9_28.1, @DefaultOrUnformed.WithSelf.Op(constants.%DefaultOrUnformed.facet.c82) [symbolic = %specific_impl_fn.loc9_28.2 (constants.%specific_impl_fn.f0d)] // CHECK:STDOUT: %DefaultOrUnformed.WithSelf.Op.call: init @ErrorIfNIsZero.%Int.loc9_27.2 (%Int) = call %specific_impl_fn.loc9_28.1() // CHECK:STDOUT: assign %v.var, %DefaultOrUnformed.WithSelf.Op.call // CHECK:STDOUT: %.loc9_27: type = splice_block %Int.loc9_27.1 [symbolic = %Int.loc9_27.2 (constants.%Int)] { // CHECK:STDOUT: %Core.ref.loc9: = name_ref Core, imports.%Core [concrete = imports.%Core] // CHECK:STDOUT: %Int.ref: %Int.type = name_ref Int, imports.%Core.Int [concrete = constants.%Int.generic] // CHECK:STDOUT: %N.ref: Core.IntLiteral = name_ref N, %N.loc4_19.2 [symbolic = %N.loc4_19.1 (constants.%N)] // CHECK:STDOUT: %Int.loc9_27.1: type = class_type @Int, @Int(constants.%N) [symbolic = %Int.loc9_27.2 (constants.%Int)] // CHECK:STDOUT: } // CHECK:STDOUT: %v: ref @ErrorIfNIsZero.%Int.loc9_27.2 (%Int) = ref_binding v, %v.var // CHECK:STDOUT: %impl.elem0.loc9_3.1: @ErrorIfNIsZero.%.loc9_3 (%.e63) = impl_witness_access constants.%Destroy.lookup_impl_witness, element0 [symbolic = %impl.elem0.loc9_3.2 (constants.%impl.elem0.602)] // CHECK:STDOUT: %bound_method.loc9_3.1: = bound_method %v.var, %impl.elem0.loc9_3.1 // CHECK:STDOUT: %specific_impl_fn.loc9_3.1: = specific_impl_function %impl.elem0.loc9_3.1, @Destroy.WithSelf.Op(constants.%Destroy.facet.1c0) [symbolic = %specific_impl_fn.loc9_3.2 (constants.%specific_impl_fn.d65)] // CHECK:STDOUT: %bound_method.loc9_3.2: = bound_method %v.var, %specific_impl_fn.loc9_3.1 // CHECK:STDOUT: %Destroy.WithSelf.Op.call: init %empty_tuple.type = call %bound_method.loc9_3.2(%v.var) // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @CallNegative() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %ErrorIfNIsZero.ref: %ErrorIfNIsZero.type = name_ref ErrorIfNIsZero, file.%ErrorIfNIsZero.decl [concrete = constants.%ErrorIfNIsZero] // CHECK:STDOUT: %int_0: Core.IntLiteral = int_value 0 [concrete = constants.%int_0] // CHECK:STDOUT: %ErrorIfNIsZero.specific_fn: = specific_function %ErrorIfNIsZero.ref, @ErrorIfNIsZero(constants.%int_0) [concrete = constants.%ErrorIfNIsZero.specific_fn] // CHECK:STDOUT: %ErrorIfNIsZero.call: init %empty_tuple.type = call %ErrorIfNIsZero.specific_fn() // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @Destroy.Op(%self.param: ref %i0) = "no_op"; // CHECK:STDOUT: // CHECK:STDOUT: specific @ErrorIfNIsZero(constants.%N) { // CHECK:STDOUT: %N.loc4_19.1 => constants.%N // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @ErrorIfNIsZero(constants.%int_0) { // CHECK:STDOUT: %N.loc4_19.1 => constants.%int_0 // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %Int.loc9_27.2 => constants.%i0 // CHECK:STDOUT: %require_complete => constants.%complete_type.d94 // CHECK:STDOUT: %pattern_type => constants.%pattern_type.47b // CHECK:STDOUT: %.loc9_28.3 => constants.%.d6a // CHECK:STDOUT: %DefaultOrUnformed.lookup_impl_witness => constants.%DefaultOrUnformed.impl_witness.879 // CHECK:STDOUT: %DefaultOrUnformed.facet.loc9_28.2 => constants.%DefaultOrUnformed.facet.255 // CHECK:STDOUT: %DefaultOrUnformed.WithSelf.Op.type => constants.%DefaultOrUnformed.WithSelf.Op.type.bd6 // CHECK:STDOUT: %.loc9_28.4 => constants.%.639 // CHECK:STDOUT: %impl.elem0.loc9_28.2 => constants.%T.as.DefaultOrUnformed.impl.Op.5b7 // CHECK:STDOUT: %specific_impl_fn.loc9_28.2 => constants.%T.as.DefaultOrUnformed.impl.Op.specific_fn // CHECK:STDOUT: %Destroy.lookup_impl_witness => constants.%custom_witness.8d7 // CHECK:STDOUT: %Destroy.facet => constants.%Destroy.facet.eb2 // CHECK:STDOUT: %Destroy.WithSelf.Op.type => constants.%Destroy.WithSelf.Op.type.cef // CHECK:STDOUT: %.loc9_3 => constants.%.98d // CHECK:STDOUT: %impl.elem0.loc9_3.2 => constants.%Destroy.Op // CHECK:STDOUT: %specific_impl_fn.loc9_3.2 => constants.%Destroy.Op // CHECK:STDOUT: } // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/function/generic/return_slot.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/int.carbon // TODO: Add ranges and switch to "--dump-sem-ir-ranges=only". // EXTRA-ARGS: --dump-sem-ir-ranges=if-present // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/function/generic/return_slot.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/function/generic/return_slot.carbon class Wrap(T:! type) { fn Make() -> T { return Make(); } } class C { var arr: array(i32, 100); } fn G() { var unused a: i32 = Wrap(i32).Make(); var unused b: () = Wrap(()).Make(); var unused c: C = Wrap(C).Make(); } // CHECK:STDOUT: --- return_slot.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %type: type = facet_type [concrete] // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self] // CHECK:STDOUT: %pattern_type.98f: type = pattern_type type [concrete] // CHECK:STDOUT: %T: type = symbolic_binding T, 0 [symbolic] // CHECK:STDOUT: %Wrap.type: type = generic_class_type @Wrap [concrete] // CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] // CHECK:STDOUT: %Wrap.generic: %Wrap.type = struct_value () [concrete] // CHECK:STDOUT: %Wrap.063: type = class_type @Wrap, @Wrap(%T) [symbolic] // CHECK:STDOUT: %.184: Core.Form = init_form %T [symbolic] // CHECK:STDOUT: %pattern_type.51d: type = pattern_type %T [symbolic] // CHECK:STDOUT: %Wrap.Make.type.6e9: type = fn_type @Wrap.Make, @Wrap(%T) [symbolic] // CHECK:STDOUT: %Wrap.Make.e25: %Wrap.Make.type.6e9 = struct_value () [symbolic] // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %complete_type.357: = complete_type_witness %empty_struct_type [concrete] // CHECK:STDOUT: %require_complete.944: = require_complete_type %T [symbolic] // CHECK:STDOUT: %Wrap.Make.specific_fn.5c8: = specific_function %Wrap.Make.e25, @Wrap.Make(%T) [symbolic] // CHECK:STDOUT: %C: type = class_type @C [concrete] // CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [concrete] // CHECK:STDOUT: %Int.type: type = generic_class_type @Int [concrete] // CHECK:STDOUT: %Int.generic: %Int.type = struct_value () [concrete] // CHECK:STDOUT: %i32: type = class_type @Int, @Int(%int_32) [concrete] // CHECK:STDOUT: %int_100: Core.IntLiteral = int_value 100 [concrete] // CHECK:STDOUT: %array_type: type = array_type %int_100, %i32 [concrete] // CHECK:STDOUT: %i32.builtin: type = int_type signed, %int_32 [concrete] // CHECK:STDOUT: %complete_type.f8a: = complete_type_witness %i32.builtin [concrete] // CHECK:STDOUT: %C.elem: type = unbound_element_type %C, %array_type [concrete] // CHECK:STDOUT: %struct_type.arr.5f2: type = struct_type {.arr: %array_type} [concrete] // CHECK:STDOUT: %complete_type.22a: = complete_type_witness %struct_type.arr.5f2 [concrete] // CHECK:STDOUT: %G.type: type = fn_type @G [concrete] // CHECK:STDOUT: %G: %G.type = struct_value () [concrete] // CHECK:STDOUT: %pattern_type.7ce: type = pattern_type %i32 [concrete] // CHECK:STDOUT: %Wrap.e80: type = class_type @Wrap, @Wrap(%i32) [concrete] // CHECK:STDOUT: %Wrap.Make.type.939: type = fn_type @Wrap.Make, @Wrap(%i32) [concrete] // CHECK:STDOUT: %Wrap.Make.6cb: %Wrap.Make.type.939 = struct_value () [concrete] // CHECK:STDOUT: %.ff5: Core.Form = init_form %i32 [concrete] // CHECK:STDOUT: %Wrap.Make.specific_fn.35c: = specific_function %Wrap.Make.6cb, @Wrap.Make(%i32) [concrete] // CHECK:STDOUT: %empty_tuple: %empty_tuple.type = tuple_value () [concrete] // CHECK:STDOUT: %pattern_type.cb1: type = pattern_type %empty_tuple.type [concrete] // CHECK:STDOUT: %Wrap.0f1: type = class_type @Wrap, @Wrap(%empty_tuple.type) [concrete] // CHECK:STDOUT: %Wrap.Make.type.fb0: type = fn_type @Wrap.Make, @Wrap(%empty_tuple.type) [concrete] // CHECK:STDOUT: %Wrap.Make.b5c: %Wrap.Make.type.fb0 = struct_value () [concrete] // CHECK:STDOUT: %.262: Core.Form = init_form %empty_tuple.type [concrete] // CHECK:STDOUT: %Wrap.Make.specific_fn.c03: = specific_function %Wrap.Make.b5c, @Wrap.Make(%empty_tuple.type) [concrete] // CHECK:STDOUT: %pattern_type.7c7: type = pattern_type %C [concrete] // CHECK:STDOUT: %Wrap.3b1: type = class_type @Wrap, @Wrap(%C) [concrete] // CHECK:STDOUT: %Wrap.Make.type.a8f: type = fn_type @Wrap.Make, @Wrap(%C) [concrete] // CHECK:STDOUT: %Wrap.Make.62a: %Wrap.Make.type.a8f = struct_value () [concrete] // CHECK:STDOUT: %.a69: Core.Form = init_form %C [concrete] // CHECK:STDOUT: %Wrap.Make.specific_fn.cb9: = specific_function %Wrap.Make.62a, @Wrap.Make(%C) [concrete] // CHECK:STDOUT: %Destroy.type: type = facet_type <@Destroy> [concrete] // CHECK:STDOUT: %Destroy.Op.type.bae255.1: type = fn_type @Destroy.Op.loc24 [concrete] // CHECK:STDOUT: %Destroy.Op.651ba6.1: %Destroy.Op.type.bae255.1 = struct_value () [concrete] // CHECK:STDOUT: %Destroy.Op.type.bae255.2: type = fn_type @Destroy.Op.loc23 [concrete] // CHECK:STDOUT: %Destroy.Op.651ba6.2: %Destroy.Op.type.bae255.2 = struct_value () [concrete] // CHECK:STDOUT: %Destroy.Op.type.bae255.3: type = fn_type @Destroy.Op.loc22 [concrete] // CHECK:STDOUT: %Destroy.Op.651ba6.3: %Destroy.Op.type.bae255.3 = struct_value () [concrete] // CHECK:STDOUT: %complete_type.782: = complete_type_witness %empty_tuple.type [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: .Int = %Core.Int // CHECK:STDOUT: .Destroy = %Core.Destroy // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: %Core.Int: %Int.type = import_ref Core//prelude/parts/int, Int, loaded [concrete = constants.%Int.generic] // CHECK:STDOUT: %Core.Destroy: type = import_ref Core//prelude/parts/destroy, Destroy, loaded [concrete = constants.%Destroy.type] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .Wrap = %Wrap.decl // CHECK:STDOUT: .C = %C.decl // CHECK:STDOUT: .G = %G.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %Wrap.decl: %Wrap.type = class_decl @Wrap [concrete = constants.%Wrap.generic] { // CHECK:STDOUT: %T.patt: %pattern_type.98f = symbolic_binding_pattern T, 0 [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %.loc15_16.1: type = splice_block %.loc15_16.2 [concrete = type] { // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %.loc15_16.2: type = type_literal type [concrete = type] // CHECK:STDOUT: } // CHECK:STDOUT: %T.loc15_12.2: type = symbolic_binding T, 0 [symbolic = %T.loc15_12.1 (constants.%T)] // CHECK:STDOUT: } // CHECK:STDOUT: %C.decl: type = class_decl @C [concrete = constants.%C] {} {} // CHECK:STDOUT: %G.decl: %G.type = fn_decl @G [concrete = constants.%G] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic class @Wrap(%T.loc15_12.2: type) { // CHECK:STDOUT: %T.loc15_12.1: type = symbolic_binding T, 0 [symbolic = %T.loc15_12.1 (constants.%T)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %Wrap.Make.type: type = fn_type @Wrap.Make, @Wrap(%T.loc15_12.1) [symbolic = %Wrap.Make.type (constants.%Wrap.Make.type.6e9)] // CHECK:STDOUT: %Wrap.Make: @Wrap.%Wrap.Make.type (%Wrap.Make.type.6e9) = struct_value () [symbolic = %Wrap.Make (constants.%Wrap.Make.e25)] // CHECK:STDOUT: // CHECK:STDOUT: class { // CHECK:STDOUT: %Wrap.Make.decl: @Wrap.%Wrap.Make.type (%Wrap.Make.type.6e9) = fn_decl @Wrap.Make [symbolic = @Wrap.%Wrap.Make (constants.%Wrap.Make.e25)] { // CHECK:STDOUT: %return.patt: @Wrap.Make.%pattern_type (%pattern_type.51d) = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: @Wrap.Make.%pattern_type (%pattern_type.51d) = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %T.ref: type = name_ref T, @Wrap.%T.loc15_12.2 [symbolic = %T (constants.%T)] // CHECK:STDOUT: %.loc16_16.3: Core.Form = init_form %T.ref [symbolic = %.loc16_16.2 (constants.%.184)] // CHECK:STDOUT: %return.param: ref @Wrap.Make.%T (%T) = out_param call_param0 // CHECK:STDOUT: %return: ref @Wrap.Make.%T (%T) = return_slot %return.param // CHECK:STDOUT: } // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type.357] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%Wrap.063 // CHECK:STDOUT: .T = // CHECK:STDOUT: .Make = %Wrap.Make.decl // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @C { // CHECK:STDOUT: %i32: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %int_100: Core.IntLiteral = int_value 100 [concrete = constants.%int_100] // CHECK:STDOUT: %array_type: type = array_type %int_100, %i32 [concrete = constants.%array_type] // CHECK:STDOUT: %.loc19: %C.elem = field_decl arr, element0 [concrete] // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%struct_type.arr.5f2 [concrete = constants.%complete_type.22a] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%C // CHECK:STDOUT: .arr = %.loc19 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic fn @Wrap.Make(@Wrap.%T.loc15_12.2: type) { // CHECK:STDOUT: %T: type = symbolic_binding T, 0 [symbolic = %T (constants.%T)] // CHECK:STDOUT: %.loc16_16.2: Core.Form = init_form %T [symbolic = %.loc16_16.2 (constants.%.184)] // CHECK:STDOUT: %pattern_type: type = pattern_type %T [symbolic = %pattern_type (constants.%pattern_type.51d)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete: = require_complete_type %T [symbolic = %require_complete (constants.%require_complete.944)] // CHECK:STDOUT: %Wrap.Make.type: type = fn_type @Wrap.Make, @Wrap(%T) [symbolic = %Wrap.Make.type (constants.%Wrap.Make.type.6e9)] // CHECK:STDOUT: %Wrap.Make: @Wrap.Make.%Wrap.Make.type (%Wrap.Make.type.6e9) = struct_value () [symbolic = %Wrap.Make (constants.%Wrap.Make.e25)] // CHECK:STDOUT: %Wrap.Make.specific_fn.loc16_27.2: = specific_function %Wrap.Make, @Wrap.Make(%T) [symbolic = %Wrap.Make.specific_fn.loc16_27.2 (constants.%Wrap.Make.specific_fn.5c8)] // CHECK:STDOUT: // CHECK:STDOUT: fn() -> out %return.param: @Wrap.Make.%T (%T) { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %.loc16_27: @Wrap.Make.%Wrap.Make.type (%Wrap.Make.type.6e9) = specific_constant @Wrap.%Wrap.Make.decl, @Wrap(constants.%T) [symbolic = %Wrap.Make (constants.%Wrap.Make.e25)] // CHECK:STDOUT: %Make.ref: @Wrap.Make.%Wrap.Make.type (%Wrap.Make.type.6e9) = name_ref Make, %.loc16_27 [symbolic = %Wrap.Make (constants.%Wrap.Make.e25)] // CHECK:STDOUT: %Wrap.Make.specific_fn.loc16_27.1: = specific_function %Make.ref, @Wrap.Make(constants.%T) [symbolic = %Wrap.Make.specific_fn.loc16_27.2 (constants.%Wrap.Make.specific_fn.5c8)] // CHECK:STDOUT: %.loc16_16.1: ref @Wrap.Make.%T (%T) = splice_block %return.param {} // CHECK:STDOUT: %Wrap.Make.call: init @Wrap.Make.%T (%T) to %.loc16_16.1 = call %Wrap.Make.specific_fn.loc16_27.1() // CHECK:STDOUT: return %Wrap.Make.call to %return.param // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @G() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %a.patt: %pattern_type.7ce = ref_binding_pattern a [concrete] // CHECK:STDOUT: %a.var_patt: %pattern_type.7ce = var_pattern %a.patt [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %a.var: ref %i32 = var %a.var_patt // CHECK:STDOUT: %Wrap.ref.loc22: %Wrap.type = name_ref Wrap, file.%Wrap.decl [concrete = constants.%Wrap.generic] // CHECK:STDOUT: %i32.loc22_28: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %Wrap.loc22: type = class_type @Wrap, @Wrap(constants.%i32) [concrete = constants.%Wrap.e80] // CHECK:STDOUT: %.loc22: %Wrap.Make.type.939 = specific_constant @Wrap.%Wrap.Make.decl, @Wrap(constants.%i32) [concrete = constants.%Wrap.Make.6cb] // CHECK:STDOUT: %Make.ref.loc22: %Wrap.Make.type.939 = name_ref Make, %.loc22 [concrete = constants.%Wrap.Make.6cb] // CHECK:STDOUT: %Wrap.Make.specific_fn.loc22: = specific_function %Make.ref.loc22, @Wrap.Make(constants.%i32) [concrete = constants.%Wrap.Make.specific_fn.35c] // CHECK:STDOUT: %Wrap.Make.call.loc22: init %i32 = call %Wrap.Make.specific_fn.loc22() // CHECK:STDOUT: assign %a.var, %Wrap.Make.call.loc22 // CHECK:STDOUT: %i32.loc22_17: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %a: ref %i32 = ref_binding a, %a.var // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %b.patt: %pattern_type.cb1 = ref_binding_pattern b [concrete] // CHECK:STDOUT: %b.var_patt: %pattern_type.cb1 = var_pattern %b.patt [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %b.var: ref %empty_tuple.type = var %b.var_patt // CHECK:STDOUT: %Wrap.ref.loc23: %Wrap.type = name_ref Wrap, file.%Wrap.decl [concrete = constants.%Wrap.generic] // CHECK:STDOUT: %.loc23_28: %empty_tuple.type = tuple_literal () [concrete = constants.%empty_tuple] // CHECK:STDOUT: %.loc23_29: type = converted %.loc23_28, constants.%empty_tuple.type [concrete = constants.%empty_tuple.type] // CHECK:STDOUT: %Wrap.loc23: type = class_type @Wrap, @Wrap(constants.%empty_tuple.type) [concrete = constants.%Wrap.0f1] // CHECK:STDOUT: %.loc23_30: %Wrap.Make.type.fb0 = specific_constant @Wrap.%Wrap.Make.decl, @Wrap(constants.%empty_tuple.type) [concrete = constants.%Wrap.Make.b5c] // CHECK:STDOUT: %Make.ref.loc23: %Wrap.Make.type.fb0 = name_ref Make, %.loc23_30 [concrete = constants.%Wrap.Make.b5c] // CHECK:STDOUT: %Wrap.Make.specific_fn.loc23: = specific_function %Make.ref.loc23, @Wrap.Make(constants.%empty_tuple.type) [concrete = constants.%Wrap.Make.specific_fn.c03] // CHECK:STDOUT: %Wrap.Make.call.loc23: init %empty_tuple.type = call %Wrap.Make.specific_fn.loc23() // CHECK:STDOUT: assign %b.var, %Wrap.Make.call.loc23 // CHECK:STDOUT: %.loc23_18.1: type = splice_block %.loc23_18.3 [concrete = constants.%empty_tuple.type] { // CHECK:STDOUT: %.loc23_18.2: %empty_tuple.type = tuple_literal () [concrete = constants.%empty_tuple] // CHECK:STDOUT: %.loc23_18.3: type = converted %.loc23_18.2, constants.%empty_tuple.type [concrete = constants.%empty_tuple.type] // CHECK:STDOUT: } // CHECK:STDOUT: %b: ref %empty_tuple.type = ref_binding b, %b.var // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %c.patt: %pattern_type.7c7 = ref_binding_pattern c [concrete] // CHECK:STDOUT: %c.var_patt: %pattern_type.7c7 = var_pattern %c.patt [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %c.var: ref %C = var %c.var_patt // CHECK:STDOUT: %Wrap.ref.loc24: %Wrap.type = name_ref Wrap, file.%Wrap.decl [concrete = constants.%Wrap.generic] // CHECK:STDOUT: %C.ref.loc24_26: type = name_ref C, file.%C.decl [concrete = constants.%C] // CHECK:STDOUT: %Wrap.loc24: type = class_type @Wrap, @Wrap(constants.%C) [concrete = constants.%Wrap.3b1] // CHECK:STDOUT: %.loc24_28: %Wrap.Make.type.a8f = specific_constant @Wrap.%Wrap.Make.decl, @Wrap(constants.%C) [concrete = constants.%Wrap.Make.62a] // CHECK:STDOUT: %Make.ref.loc24: %Wrap.Make.type.a8f = name_ref Make, %.loc24_28 [concrete = constants.%Wrap.Make.62a] // CHECK:STDOUT: %Wrap.Make.specific_fn.loc24: = specific_function %Make.ref.loc24, @Wrap.Make(constants.%C) [concrete = constants.%Wrap.Make.specific_fn.cb9] // CHECK:STDOUT: %.loc24_3: ref %C = splice_block %c.var {} // CHECK:STDOUT: %Wrap.Make.call.loc24: init %C to %.loc24_3 = call %Wrap.Make.specific_fn.loc24() // CHECK:STDOUT: assign %c.var, %Wrap.Make.call.loc24 // CHECK:STDOUT: %C.ref.loc24_17: type = name_ref C, file.%C.decl [concrete = constants.%C] // CHECK:STDOUT: %c: ref %C = ref_binding c, %c.var // CHECK:STDOUT: %Destroy.Op.bound.loc24: = bound_method %c.var, constants.%Destroy.Op.651ba6.1 // CHECK:STDOUT: %Destroy.Op.call.loc24: init %empty_tuple.type = call %Destroy.Op.bound.loc24(%c.var) // CHECK:STDOUT: %Destroy.Op.bound.loc23: = bound_method %b.var, constants.%Destroy.Op.651ba6.2 // CHECK:STDOUT: %Destroy.Op.call.loc23: init %empty_tuple.type = call %Destroy.Op.bound.loc23(%b.var) // CHECK:STDOUT: %Destroy.Op.bound.loc22: = bound_method %a.var, constants.%Destroy.Op.651ba6.3 // CHECK:STDOUT: %Destroy.Op.call.loc22: init %empty_tuple.type = call %Destroy.Op.bound.loc22(%a.var) // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @Destroy.Op.loc24(%self.param: ref %C) = "no_op"; // CHECK:STDOUT: // CHECK:STDOUT: fn @Destroy.Op.loc23(%self.param: ref %empty_tuple.type) = "no_op"; // CHECK:STDOUT: // CHECK:STDOUT: fn @Destroy.Op.loc22(%self.param: ref %i32) = "no_op"; // CHECK:STDOUT: // CHECK:STDOUT: specific @Wrap(constants.%T) { // CHECK:STDOUT: %T.loc15_12.1 => constants.%T // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %Wrap.Make.type => constants.%Wrap.Make.type.6e9 // CHECK:STDOUT: %Wrap.Make => constants.%Wrap.Make.e25 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @Wrap.Make(constants.%T) { // CHECK:STDOUT: %T => constants.%T // CHECK:STDOUT: %.loc16_16.2 => constants.%.184 // CHECK:STDOUT: %pattern_type => constants.%pattern_type.51d // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete => constants.%require_complete.944 // CHECK:STDOUT: %Wrap.Make.type => constants.%Wrap.Make.type.6e9 // CHECK:STDOUT: %Wrap.Make => constants.%Wrap.Make.e25 // CHECK:STDOUT: %Wrap.Make.specific_fn.loc16_27.2 => constants.%Wrap.Make.specific_fn.5c8 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @Wrap(constants.%i32) { // CHECK:STDOUT: %T.loc15_12.1 => constants.%i32 // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %Wrap.Make.type => constants.%Wrap.Make.type.939 // CHECK:STDOUT: %Wrap.Make => constants.%Wrap.Make.6cb // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @Wrap.Make(constants.%i32) { // CHECK:STDOUT: %T => constants.%i32 // CHECK:STDOUT: %.loc16_16.2 => constants.%.ff5 // CHECK:STDOUT: %pattern_type => constants.%pattern_type.7ce // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete => constants.%complete_type.f8a // CHECK:STDOUT: %Wrap.Make.type => constants.%Wrap.Make.type.939 // CHECK:STDOUT: %Wrap.Make => constants.%Wrap.Make.6cb // CHECK:STDOUT: %Wrap.Make.specific_fn.loc16_27.2 => constants.%Wrap.Make.specific_fn.35c // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @Wrap(constants.%empty_tuple.type) { // CHECK:STDOUT: %T.loc15_12.1 => constants.%empty_tuple.type // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %Wrap.Make.type => constants.%Wrap.Make.type.fb0 // CHECK:STDOUT: %Wrap.Make => constants.%Wrap.Make.b5c // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @Wrap.Make(constants.%empty_tuple.type) { // CHECK:STDOUT: %T => constants.%empty_tuple.type // CHECK:STDOUT: %.loc16_16.2 => constants.%.262 // CHECK:STDOUT: %pattern_type => constants.%pattern_type.cb1 // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete => constants.%complete_type.782 // CHECK:STDOUT: %Wrap.Make.type => constants.%Wrap.Make.type.fb0 // CHECK:STDOUT: %Wrap.Make => constants.%Wrap.Make.b5c // CHECK:STDOUT: %Wrap.Make.specific_fn.loc16_27.2 => constants.%Wrap.Make.specific_fn.c03 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @Wrap(constants.%C) { // CHECK:STDOUT: %T.loc15_12.1 => constants.%C // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %Wrap.Make.type => constants.%Wrap.Make.type.a8f // CHECK:STDOUT: %Wrap.Make => constants.%Wrap.Make.62a // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @Wrap.Make(constants.%C) { // CHECK:STDOUT: %T => constants.%C // CHECK:STDOUT: %.loc16_16.2 => constants.%.a69 // CHECK:STDOUT: %pattern_type => constants.%pattern_type.7c7 // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete => constants.%complete_type.22a // CHECK:STDOUT: %Wrap.Make.type => constants.%Wrap.Make.type.a8f // CHECK:STDOUT: %Wrap.Make => constants.%Wrap.Make.62a // CHECK:STDOUT: %Wrap.Make.specific_fn.loc16_27.2 => constants.%Wrap.Make.specific_fn.cb9 // CHECK:STDOUT: } // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/function/generic/template_param.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/none.carbon // TODO: Add ranges and switch to "--dump-sem-ir-ranges=only". // EXTRA-ARGS: --dump-sem-ir-ranges=if-present // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/function/generic/template_param.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/function/generic/template_param.carbon // --- fn.carbon library "[[@TEST_NAME]]"; fn F(unused template T:! type) { } fn G() { F({}); } // CHECK:STDOUT: --- fn.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %type: type = facet_type [concrete] // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self] // CHECK:STDOUT: %pattern_type: type = pattern_type type [concrete] // CHECK:STDOUT: %T: type = symbolic_binding T, 0, template [template] // CHECK:STDOUT: %F.type: type = fn_type @F [concrete] // CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] // CHECK:STDOUT: %F: %F.type = struct_value () [concrete] // CHECK:STDOUT: %G.type: type = fn_type @G [concrete] // CHECK:STDOUT: %G: %G.type = struct_value () [concrete] // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %empty_struct: %empty_struct_type = struct_value () [concrete] // CHECK:STDOUT: %F.specific_fn: = specific_function %F, @F(%empty_struct_type) [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .F = %F.decl // CHECK:STDOUT: .G = %G.decl // CHECK:STDOUT: } // CHECK:STDOUT: %F.decl: %F.type = fn_decl @F [concrete = constants.%F] { // CHECK:STDOUT: %T.patt: %pattern_type = symbolic_binding_pattern T, 0, template [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %.loc4_26.1: type = splice_block %.loc4_26.2 [concrete = type] { // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %.loc4_26.2: type = type_literal type [concrete = type] // CHECK:STDOUT: } // CHECK:STDOUT: %T.loc4_22.2: type = symbolic_binding T, 0, template [template = %T.loc4_22.1 (constants.%T)] // CHECK:STDOUT: } // CHECK:STDOUT: %G.decl: %G.type = fn_decl @G [concrete = constants.%G] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic fn @F(%T.loc4_22.2: type) { // CHECK:STDOUT: %T.loc4_22.1: type = symbolic_binding T, 0, template [template = %T.loc4_22.1 (constants.%T)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: // CHECK:STDOUT: fn() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @G() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %F.ref: %F.type = name_ref F, file.%F.decl [concrete = constants.%F] // CHECK:STDOUT: %.loc8_6: %empty_struct_type = struct_literal () [concrete = constants.%empty_struct] // CHECK:STDOUT: %.loc8_7: type = converted %.loc8_6, constants.%empty_struct_type [concrete = constants.%empty_struct_type] // CHECK:STDOUT: %F.specific_fn: = specific_function %F.ref, @F(constants.%empty_struct_type) [concrete = constants.%F.specific_fn] // CHECK:STDOUT: %F.call: init %empty_tuple.type = call %F.specific_fn() // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @F(constants.%T) { // CHECK:STDOUT: %T.loc4_22.1 => constants.%T // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @F(constants.%empty_struct_type) { // CHECK:STDOUT: %T.loc4_22.1 => constants.%empty_struct_type // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: } // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/function/generic/type_param.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/convert.carbon // TODO: Add ranges and switch to "--dump-sem-ir-ranges=only". // EXTRA-ARGS: --dump-sem-ir-ranges=if-present // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/function/generic/type_param.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/function/generic/type_param.carbon fn F(T:! type) { var p: T*; let unused n: T = *p; } // CHECK:STDOUT: --- type_param.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %type: type = facet_type [concrete] // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self] // CHECK:STDOUT: %pattern_type.98f: type = pattern_type type [concrete] // CHECK:STDOUT: %T: type = symbolic_binding T, 0 [symbolic] // CHECK:STDOUT: %F.type: type = fn_type @F [concrete] // CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] // CHECK:STDOUT: %F: %F.type = struct_value () [concrete] // CHECK:STDOUT: %ptr: type = ptr_type %T [symbolic] // CHECK:STDOUT: %require_complete.ef1: = require_complete_type %ptr [symbolic] // CHECK:STDOUT: %pattern_type.4f4: type = pattern_type %ptr [symbolic] // CHECK:STDOUT: %DefaultOrUnformed.type: type = facet_type <@DefaultOrUnformed> [concrete] // CHECK:STDOUT: %require_complete.944: = require_complete_type %T [symbolic] // CHECK:STDOUT: %pattern_type.51d: type = pattern_type %T [symbolic] // CHECK:STDOUT: %.713: require_specific_def_type = require_specific_def @T.as.DefaultOrUnformed.impl(%ptr) [symbolic] // CHECK:STDOUT: %DefaultOrUnformed.lookup_impl_witness: = lookup_impl_witness %ptr, @DefaultOrUnformed [symbolic] // CHECK:STDOUT: %DefaultOrUnformed.facet: %DefaultOrUnformed.type = facet_value %ptr, (%DefaultOrUnformed.lookup_impl_witness) [symbolic] // CHECK:STDOUT: %DefaultOrUnformed.WithSelf.Op.type.d74: type = fn_type @DefaultOrUnformed.WithSelf.Op, @DefaultOrUnformed.WithSelf(%DefaultOrUnformed.facet) [symbolic] // CHECK:STDOUT: %.425: type = fn_type_with_self_type %DefaultOrUnformed.WithSelf.Op.type.d74, %DefaultOrUnformed.facet [symbolic] // CHECK:STDOUT: %impl.elem0.45d: %.425 = impl_witness_access %DefaultOrUnformed.lookup_impl_witness, element0 [symbolic] // CHECK:STDOUT: %specific_impl_fn.a93: = specific_impl_function %impl.elem0.45d, @DefaultOrUnformed.WithSelf.Op(%DefaultOrUnformed.facet) [symbolic] // CHECK:STDOUT: %Destroy.type: type = facet_type <@Destroy> [concrete] // CHECK:STDOUT: %Destroy.lookup_impl_witness: = lookup_impl_witness %ptr, @Destroy [symbolic] // CHECK:STDOUT: %Destroy.facet: %Destroy.type = facet_value %ptr, (%Destroy.lookup_impl_witness) [symbolic] // CHECK:STDOUT: %Destroy.WithSelf.Op.type.bb2: type = fn_type @Destroy.WithSelf.Op, @Destroy.WithSelf(%Destroy.facet) [symbolic] // CHECK:STDOUT: %.c3f: type = fn_type_with_self_type %Destroy.WithSelf.Op.type.bb2, %Destroy.facet [symbolic] // CHECK:STDOUT: %impl.elem0.b55: %.c3f = impl_witness_access %Destroy.lookup_impl_witness, element0 [symbolic] // CHECK:STDOUT: %specific_impl_fn.5c7: = specific_impl_function %impl.elem0.b55, @Destroy.WithSelf.Op(%Destroy.facet) [symbolic] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: .DefaultOrUnformed = %Core.DefaultOrUnformed // CHECK:STDOUT: .Destroy = %Core.Destroy // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: %Core.DefaultOrUnformed: type = import_ref Core//prelude/parts/default, DefaultOrUnformed, loaded [concrete = constants.%DefaultOrUnformed.type] // CHECK:STDOUT: %Core.Destroy: type = import_ref Core//prelude/parts/destroy, Destroy, loaded [concrete = constants.%Destroy.type] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .F = %F.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %F.decl: %F.type = fn_decl @F [concrete = constants.%F] { // CHECK:STDOUT: %T.patt: %pattern_type.98f = symbolic_binding_pattern T, 0 [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %.loc15_10.1: type = splice_block %.loc15_10.2 [concrete = type] { // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %.loc15_10.2: type = type_literal type [concrete = type] // CHECK:STDOUT: } // CHECK:STDOUT: %T.loc15_6.2: type = symbolic_binding T, 0 [symbolic = %T.loc15_6.1 (constants.%T)] // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic fn @F(%T.loc15_6.2: type) { // CHECK:STDOUT: %T.loc15_6.1: type = symbolic_binding T, 0 [symbolic = %T.loc15_6.1 (constants.%T)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %ptr.loc16_11.2: type = ptr_type %T.loc15_6.1 [symbolic = %ptr.loc16_11.2 (constants.%ptr)] // CHECK:STDOUT: %require_complete.loc16: = require_complete_type %ptr.loc16_11.2 [symbolic = %require_complete.loc16 (constants.%require_complete.ef1)] // CHECK:STDOUT: %pattern_type.loc16: type = pattern_type %ptr.loc16_11.2 [symbolic = %pattern_type.loc16 (constants.%pattern_type.4f4)] // CHECK:STDOUT: %.loc16_12.3: require_specific_def_type = require_specific_def @T.as.DefaultOrUnformed.impl(%ptr.loc16_11.2) [symbolic = %.loc16_12.3 (constants.%.713)] // CHECK:STDOUT: %DefaultOrUnformed.lookup_impl_witness: = lookup_impl_witness %ptr.loc16_11.2, @DefaultOrUnformed [symbolic = %DefaultOrUnformed.lookup_impl_witness (constants.%DefaultOrUnformed.lookup_impl_witness)] // CHECK:STDOUT: %DefaultOrUnformed.facet.loc16_12.2: %DefaultOrUnformed.type = facet_value %ptr.loc16_11.2, (%DefaultOrUnformed.lookup_impl_witness) [symbolic = %DefaultOrUnformed.facet.loc16_12.2 (constants.%DefaultOrUnformed.facet)] // CHECK:STDOUT: %DefaultOrUnformed.WithSelf.Op.type: type = fn_type @DefaultOrUnformed.WithSelf.Op, @DefaultOrUnformed.WithSelf(%DefaultOrUnformed.facet.loc16_12.2) [symbolic = %DefaultOrUnformed.WithSelf.Op.type (constants.%DefaultOrUnformed.WithSelf.Op.type.d74)] // CHECK:STDOUT: %.loc16_12.4: type = fn_type_with_self_type %DefaultOrUnformed.WithSelf.Op.type, %DefaultOrUnformed.facet.loc16_12.2 [symbolic = %.loc16_12.4 (constants.%.425)] // CHECK:STDOUT: %impl.elem0.loc16_12.2: @F.%.loc16_12.4 (%.425) = impl_witness_access %DefaultOrUnformed.lookup_impl_witness, element0 [symbolic = %impl.elem0.loc16_12.2 (constants.%impl.elem0.45d)] // CHECK:STDOUT: %specific_impl_fn.loc16_12.2: = specific_impl_function %impl.elem0.loc16_12.2, @DefaultOrUnformed.WithSelf.Op(%DefaultOrUnformed.facet.loc16_12.2) [symbolic = %specific_impl_fn.loc16_12.2 (constants.%specific_impl_fn.a93)] // CHECK:STDOUT: %require_complete.loc17: = require_complete_type %T.loc15_6.1 [symbolic = %require_complete.loc17 (constants.%require_complete.944)] // CHECK:STDOUT: %pattern_type.loc17: type = pattern_type %T.loc15_6.1 [symbolic = %pattern_type.loc17 (constants.%pattern_type.51d)] // CHECK:STDOUT: %Destroy.lookup_impl_witness: = lookup_impl_witness %ptr.loc16_11.2, @Destroy [symbolic = %Destroy.lookup_impl_witness (constants.%Destroy.lookup_impl_witness)] // CHECK:STDOUT: %Destroy.facet: %Destroy.type = facet_value %ptr.loc16_11.2, (%Destroy.lookup_impl_witness) [symbolic = %Destroy.facet (constants.%Destroy.facet)] // CHECK:STDOUT: %Destroy.WithSelf.Op.type: type = fn_type @Destroy.WithSelf.Op, @Destroy.WithSelf(%Destroy.facet) [symbolic = %Destroy.WithSelf.Op.type (constants.%Destroy.WithSelf.Op.type.bb2)] // CHECK:STDOUT: %.loc16_3: type = fn_type_with_self_type %Destroy.WithSelf.Op.type, %Destroy.facet [symbolic = %.loc16_3 (constants.%.c3f)] // CHECK:STDOUT: %impl.elem0.loc16_3.2: @F.%.loc16_3 (%.c3f) = impl_witness_access %Destroy.lookup_impl_witness, element0 [symbolic = %impl.elem0.loc16_3.2 (constants.%impl.elem0.b55)] // CHECK:STDOUT: %specific_impl_fn.loc16_3.2: = specific_impl_function %impl.elem0.loc16_3.2, @Destroy.WithSelf.Op(%Destroy.facet) [symbolic = %specific_impl_fn.loc16_3.2 (constants.%specific_impl_fn.5c7)] // CHECK:STDOUT: // CHECK:STDOUT: fn() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %p.patt: @F.%pattern_type.loc16 (%pattern_type.4f4) = ref_binding_pattern p [concrete] // CHECK:STDOUT: %p.var_patt: @F.%pattern_type.loc16 (%pattern_type.4f4) = var_pattern %p.patt [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %p.var: ref @F.%ptr.loc16_11.2 (%ptr) = var %p.var_patt // CHECK:STDOUT: %DefaultOrUnformed.facet.loc16_12.1: %DefaultOrUnformed.type = facet_value constants.%ptr, (constants.%DefaultOrUnformed.lookup_impl_witness) [symbolic = %DefaultOrUnformed.facet.loc16_12.2 (constants.%DefaultOrUnformed.facet)] // CHECK:STDOUT: %.loc16_12.1: %DefaultOrUnformed.type = converted constants.%ptr, %DefaultOrUnformed.facet.loc16_12.1 [symbolic = %DefaultOrUnformed.facet.loc16_12.2 (constants.%DefaultOrUnformed.facet)] // CHECK:STDOUT: %as_type: type = facet_access_type %.loc16_12.1 [symbolic = %ptr.loc16_11.2 (constants.%ptr)] // CHECK:STDOUT: %.loc16_12.2: type = converted %.loc16_12.1, %as_type [symbolic = %ptr.loc16_11.2 (constants.%ptr)] // CHECK:STDOUT: %impl.elem0.loc16_12.1: @F.%.loc16_12.4 (%.425) = impl_witness_access constants.%DefaultOrUnformed.lookup_impl_witness, element0 [symbolic = %impl.elem0.loc16_12.2 (constants.%impl.elem0.45d)] // CHECK:STDOUT: %specific_impl_fn.loc16_12.1: = specific_impl_function %impl.elem0.loc16_12.1, @DefaultOrUnformed.WithSelf.Op(constants.%DefaultOrUnformed.facet) [symbolic = %specific_impl_fn.loc16_12.2 (constants.%specific_impl_fn.a93)] // CHECK:STDOUT: %DefaultOrUnformed.WithSelf.Op.call: init @F.%ptr.loc16_11.2 (%ptr) = call %specific_impl_fn.loc16_12.1() // CHECK:STDOUT: assign %p.var, %DefaultOrUnformed.WithSelf.Op.call // CHECK:STDOUT: %.loc16_11: type = splice_block %ptr.loc16_11.1 [symbolic = %ptr.loc16_11.2 (constants.%ptr)] { // CHECK:STDOUT: %T.ref.loc16: type = name_ref T, %T.loc15_6.2 [symbolic = %T.loc15_6.1 (constants.%T)] // CHECK:STDOUT: %ptr.loc16_11.1: type = ptr_type %T.ref.loc16 [symbolic = %ptr.loc16_11.2 (constants.%ptr)] // CHECK:STDOUT: } // CHECK:STDOUT: %p: ref @F.%ptr.loc16_11.2 (%ptr) = ref_binding p, %p.var // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %n.patt: @F.%pattern_type.loc17 (%pattern_type.51d) = value_binding_pattern n [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %p.ref: ref @F.%ptr.loc16_11.2 (%ptr) = name_ref p, %p // CHECK:STDOUT: %.loc17_22: @F.%ptr.loc16_11.2 (%ptr) = acquire_value %p.ref // CHECK:STDOUT: %.loc17_21.1: ref @F.%T.loc15_6.1 (%T) = deref %.loc17_22 // CHECK:STDOUT: %T.ref.loc17: type = name_ref T, %T.loc15_6.2 [symbolic = %T.loc15_6.1 (constants.%T)] // CHECK:STDOUT: %.loc17_21.2: @F.%T.loc15_6.1 (%T) = acquire_value %.loc17_21.1 // CHECK:STDOUT: %n: @F.%T.loc15_6.1 (%T) = value_binding n, %.loc17_21.2 // CHECK:STDOUT: %impl.elem0.loc16_3.1: @F.%.loc16_3 (%.c3f) = impl_witness_access constants.%Destroy.lookup_impl_witness, element0 [symbolic = %impl.elem0.loc16_3.2 (constants.%impl.elem0.b55)] // CHECK:STDOUT: %bound_method.loc16_3.1: = bound_method %p.var, %impl.elem0.loc16_3.1 // CHECK:STDOUT: %specific_impl_fn.loc16_3.1: = specific_impl_function %impl.elem0.loc16_3.1, @Destroy.WithSelf.Op(constants.%Destroy.facet) [symbolic = %specific_impl_fn.loc16_3.2 (constants.%specific_impl_fn.5c7)] // CHECK:STDOUT: %bound_method.loc16_3.2: = bound_method %p.var, %specific_impl_fn.loc16_3.1 // CHECK:STDOUT: %Destroy.WithSelf.Op.call: init %empty_tuple.type = call %bound_method.loc16_3.2(%p.var) // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @F(constants.%T) { // CHECK:STDOUT: %T.loc15_6.1 => constants.%T // CHECK:STDOUT: } // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/function/generic/type_param_scope.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/convert.carbon // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/function/generic/type_param_scope.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/function/generic/type_param_scope.carbon // --- local_let.carbon library "[[@TEST_NAME]]"; fn F(T:! type, n: T*) -> T* { //@dump-sem-ir-begin let m: T* = n; return m; //@dump-sem-ir-end } // CHECK:STDOUT: --- local_let.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %T.67d: type = symbolic_binding T, 0 [symbolic] // CHECK:STDOUT: %ptr: type = ptr_type %T.67d [symbolic] // CHECK:STDOUT: %pattern_type.4f4: type = pattern_type %ptr [symbolic] // CHECK:STDOUT: %.cb6: Core.Form = init_form %ptr [symbolic] // CHECK:STDOUT: %Copy.type: type = facet_type <@Copy> [concrete] // CHECK:STDOUT: %.2f2: require_specific_def_type = require_specific_def @ptr.as.Copy.impl(%T.67d) [symbolic] // CHECK:STDOUT: %Copy.lookup_impl_witness.2e6: = lookup_impl_witness %ptr, @Copy [symbolic] // CHECK:STDOUT: %Copy.facet: %Copy.type = facet_value %ptr, (%Copy.lookup_impl_witness.2e6) [symbolic] // CHECK:STDOUT: %Copy.WithSelf.Op.type.d82: type = fn_type @Copy.WithSelf.Op, @Copy.WithSelf(%Copy.facet) [symbolic] // CHECK:STDOUT: %.299: type = fn_type_with_self_type %Copy.WithSelf.Op.type.d82, %Copy.facet [symbolic] // CHECK:STDOUT: %impl.elem0.1c7: %.299 = impl_witness_access %Copy.lookup_impl_witness.2e6, element0 [symbolic] // CHECK:STDOUT: %specific_impl_fn.366: = specific_impl_function %impl.elem0.1c7, @Copy.WithSelf.Op(%Copy.facet) [symbolic] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic fn @F(%T.loc4_6.2: type) { // CHECK:STDOUT: // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: // CHECK:STDOUT: %.loc7_10.1: require_specific_def_type = require_specific_def @ptr.as.Copy.impl(%T.loc4_6.1) [symbolic = %.loc7_10.1 (constants.%.2f2)] // CHECK:STDOUT: %Copy.lookup_impl_witness: = lookup_impl_witness %ptr.loc4_20.1, @Copy [symbolic = %Copy.lookup_impl_witness (constants.%Copy.lookup_impl_witness.2e6)] // CHECK:STDOUT: %Copy.facet: %Copy.type = facet_value %ptr.loc4_20.1, (%Copy.lookup_impl_witness) [symbolic = %Copy.facet (constants.%Copy.facet)] // CHECK:STDOUT: %Copy.WithSelf.Op.type: type = fn_type @Copy.WithSelf.Op, @Copy.WithSelf(%Copy.facet) [symbolic = %Copy.WithSelf.Op.type (constants.%Copy.WithSelf.Op.type.d82)] // CHECK:STDOUT: %.loc7_10.2: type = fn_type_with_self_type %Copy.WithSelf.Op.type, %Copy.facet [symbolic = %.loc7_10.2 (constants.%.299)] // CHECK:STDOUT: %impl.elem0.loc7_10.2: @F.%.loc7_10.2 (%.299) = impl_witness_access %Copy.lookup_impl_witness, element0 [symbolic = %impl.elem0.loc7_10.2 (constants.%impl.elem0.1c7)] // CHECK:STDOUT: %specific_impl_fn.loc7_10.2: = specific_impl_function %impl.elem0.loc7_10.2, @Copy.WithSelf.Op(%Copy.facet) [symbolic = %specific_impl_fn.loc7_10.2 (constants.%specific_impl_fn.366)] // CHECK:STDOUT: // CHECK:STDOUT: fn(%n.param: @F.%ptr.loc4_20.1 (%ptr)) -> out %return.param: @F.%ptr.loc4_20.1 (%ptr) { // CHECK:STDOUT: !entry: // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %m.patt: @F.%pattern_type (%pattern_type.4f4) = value_binding_pattern m [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %n.ref: @F.%ptr.loc4_20.1 (%ptr) = name_ref n, %n // CHECK:STDOUT: %.loc6: type = splice_block %ptr.loc6 [symbolic = %ptr.loc4_20.1 (constants.%ptr)] { // CHECK:STDOUT: %T.ref.loc6: type = name_ref T, %T.loc4_6.2 [symbolic = %T.loc4_6.1 (constants.%T.67d)] // CHECK:STDOUT: %ptr.loc6: type = ptr_type %T.ref.loc6 [symbolic = %ptr.loc4_20.1 (constants.%ptr)] // CHECK:STDOUT: } // CHECK:STDOUT: %m: @F.%ptr.loc4_20.1 (%ptr) = value_binding m, %n.ref // CHECK:STDOUT: %m.ref: @F.%ptr.loc4_20.1 (%ptr) = name_ref m, %m // CHECK:STDOUT: %impl.elem0.loc7_10.1: @F.%.loc7_10.2 (%.299) = impl_witness_access constants.%Copy.lookup_impl_witness.2e6, element0 [symbolic = %impl.elem0.loc7_10.2 (constants.%impl.elem0.1c7)] // CHECK:STDOUT: %bound_method.loc7_10.1: = bound_method %m.ref, %impl.elem0.loc7_10.1 // CHECK:STDOUT: %specific_impl_fn.loc7_10.1: = specific_impl_function %impl.elem0.loc7_10.1, @Copy.WithSelf.Op(constants.%Copy.facet) [symbolic = %specific_impl_fn.loc7_10.2 (constants.%specific_impl_fn.366)] // CHECK:STDOUT: %bound_method.loc7_10.2: = bound_method %m.ref, %specific_impl_fn.loc7_10.1 // CHECK:STDOUT: %Copy.WithSelf.Op.call: init @F.%ptr.loc4_20.1 (%ptr) = call %bound_method.loc7_10.2(%m.ref) // CHECK:STDOUT: return %Copy.WithSelf.Op.call // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @F(constants.%T.67d) { // CHECK:STDOUT: %T.loc4_6.1 => constants.%T.67d // CHECK:STDOUT: %ptr.loc4_20.1 => constants.%ptr // CHECK:STDOUT: %pattern_type => constants.%pattern_type.4f4 // CHECK:STDOUT: %.loc4_27.1 => constants.%.cb6 // CHECK:STDOUT: } // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/function/generic/undefined.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/int.carbon // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/function/generic/undefined.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/function/generic/undefined.carbon // --- call_defined.carbon library "[[@TEST_NAME]]"; fn Defined[T:! type](unused x: T) {} fn CallDefined() { Defined(0 as i32); } // --- call_defined_late.carbon library "[[@TEST_NAME]]"; fn Defined[T:! type](x: T); fn CallDefined() { Defined(0 as i32); } fn Defined[T:! type](unused x: T) {} // --- fail_call_undefined.carbon library "[[@TEST_NAME]]"; fn Undefined[T:! type](x: T); fn CallUndefined() { // CHECK:STDERR: fail_call_undefined.carbon:[[@LINE+7]]:3: error: use of undefined generic function [MissingGenericFunctionDefinition] // CHECK:STDERR: Undefined(0 as i32); // CHECK:STDERR: ^~~~~~~~~ // CHECK:STDERR: fail_call_undefined.carbon:[[@LINE-6]]:1: note: generic function declared here [MissingGenericFunctionDefinitionHere] // CHECK:STDERR: fn Undefined[T:! type](x: T); // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: Undefined(0 as i32); } ================================================ FILE: toolchain/check/testdata/generic/call_basic_depth.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/destroy.carbon // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/generic/call_basic_depth.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/generic/call_basic_depth.carbon class C { fn Cfn[unused self: Self, T:! type](unused x: T) { } } //@dump-sem-ir-begin fn F[T:! type](unused x: T) { } //@dump-sem-ir-end fn H[T:! type](x: T) { //@dump-sem-ir-begin F(x); //@dump-sem-ir-end } fn G[T:! type](x: T) { //@dump-sem-ir-begin H(x); F(x); //@dump-sem-ir-end var c: C = {}; c.Cfn(x); } fn M() { var n: C = {}; //@dump-sem-ir-begin F(n); G(n); //@dump-sem-ir-end } // CHECK:STDOUT: --- call_basic_depth.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %C: type = class_type @C [concrete] // CHECK:STDOUT: %pattern_type.7c7: type = pattern_type %C [concrete] // CHECK:STDOUT: %pattern_type.98f: type = pattern_type type [concrete] // CHECK:STDOUT: %T: type = symbolic_binding T, 0 [symbolic] // CHECK:STDOUT: %pattern_type.51d: type = pattern_type %T [symbolic] // CHECK:STDOUT: %C.Cfn.type: type = fn_type @C.Cfn [concrete] // CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] // CHECK:STDOUT: %C.Cfn: %C.Cfn.type = struct_value () [concrete] // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %complete_type: = complete_type_witness %empty_struct_type [concrete] // CHECK:STDOUT: %require_complete: = require_complete_type %T [symbolic] // CHECK:STDOUT: %F.type: type = fn_type @F [concrete] // CHECK:STDOUT: %F: %F.type = struct_value () [concrete] // CHECK:STDOUT: %H.type: type = fn_type @H [concrete] // CHECK:STDOUT: %H: %H.type = struct_value () [concrete] // CHECK:STDOUT: %F.specific_fn.643: = specific_function %F, @F(%T) [symbolic] // CHECK:STDOUT: %G.type: type = fn_type @G [concrete] // CHECK:STDOUT: %G: %G.type = struct_value () [concrete] // CHECK:STDOUT: %H.specific_fn.965: = specific_function %H, @H(%T) [symbolic] // CHECK:STDOUT: %F.specific_fn.540: = specific_function %F, @F(%C) [concrete] // CHECK:STDOUT: %G.specific_fn: = specific_function %G, @G(%C) [concrete] // CHECK:STDOUT: %H.specific_fn.d42: = specific_function %H, @H(%C) [concrete] // CHECK:STDOUT: %C.Cfn.specific_fn.f5e: = specific_function %C.Cfn, @C.Cfn(%C) [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: %F.decl: %F.type = fn_decl @F [concrete = constants.%F] { // CHECK:STDOUT: %T.patt: %pattern_type.98f = symbolic_binding_pattern T, 0 [concrete] // CHECK:STDOUT: %x.patt: @F.%pattern_type (%pattern_type.51d) = value_binding_pattern x [concrete] // CHECK:STDOUT: %x.param_patt: @F.%pattern_type (%pattern_type.51d) = value_param_pattern %x.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %.loc19_10.1: type = splice_block %.loc19_10.2 [concrete = type] { // CHECK:STDOUT: // CHECK:STDOUT: %.loc19_10.2: type = type_literal type [concrete = type] // CHECK:STDOUT: } // CHECK:STDOUT: %T.loc19_6.2: type = symbolic_binding T, 0 [symbolic = %T.loc19_6.1 (constants.%T)] // CHECK:STDOUT: %x.param: @F.%T.loc19_6.1 (%T) = value_param call_param0 // CHECK:STDOUT: %T.ref: type = name_ref T, %T.loc19_6.2 [symbolic = %T.loc19_6.1 (constants.%T)] // CHECK:STDOUT: %x: @F.%T.loc19_6.1 (%T) = value_binding x, %x.param // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic fn @F(%T.loc19_6.2: type) { // CHECK:STDOUT: %T.loc19_6.1: type = symbolic_binding T, 0 [symbolic = %T.loc19_6.1 (constants.%T)] // CHECK:STDOUT: %pattern_type: type = pattern_type %T.loc19_6.1 [symbolic = %pattern_type (constants.%pattern_type.51d)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete: = require_complete_type %T.loc19_6.1 [symbolic = %require_complete (constants.%require_complete)] // CHECK:STDOUT: // CHECK:STDOUT: fn(%x.param: @F.%T.loc19_6.1 (%T)) { // CHECK:STDOUT: !entry: // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic fn @H(%T.loc23_6.2: type) { // CHECK:STDOUT: // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: // CHECK:STDOUT: %F.specific_fn.loc25_3.2: = specific_function constants.%F, @F(%T.loc23_6.1) [symbolic = %F.specific_fn.loc25_3.2 (constants.%F.specific_fn.643)] // CHECK:STDOUT: // CHECK:STDOUT: fn(%x.param: @H.%T.loc23_6.1 (%T)) { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %F.ref: %F.type = name_ref F, file.%F.decl [concrete = constants.%F] // CHECK:STDOUT: %x.ref: @H.%T.loc23_6.1 (%T) = name_ref x, %x // CHECK:STDOUT: %F.specific_fn.loc25_3.1: = specific_function %F.ref, @F(constants.%T) [symbolic = %F.specific_fn.loc25_3.2 (constants.%F.specific_fn.643)] // CHECK:STDOUT: %F.call: init %empty_tuple.type = call %F.specific_fn.loc25_3.1(%x.ref) // CHECK:STDOUT: // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic fn @G(%T.loc29_6.2: type) { // CHECK:STDOUT: // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: // CHECK:STDOUT: %H.specific_fn.loc31_3.2: = specific_function constants.%H, @H(%T.loc29_6.1) [symbolic = %H.specific_fn.loc31_3.2 (constants.%H.specific_fn.965)] // CHECK:STDOUT: %F.specific_fn.loc32_3.2: = specific_function constants.%F, @F(%T.loc29_6.1) [symbolic = %F.specific_fn.loc32_3.2 (constants.%F.specific_fn.643)] // CHECK:STDOUT: // CHECK:STDOUT: // CHECK:STDOUT: fn(%x.param: @G.%T.loc29_6.1 (%T)) { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %H.ref: %H.type = name_ref H, file.%H.decl [concrete = constants.%H] // CHECK:STDOUT: %x.ref.loc31: @G.%T.loc29_6.1 (%T) = name_ref x, %x // CHECK:STDOUT: %H.specific_fn.loc31_3.1: = specific_function %H.ref, @H(constants.%T) [symbolic = %H.specific_fn.loc31_3.2 (constants.%H.specific_fn.965)] // CHECK:STDOUT: %H.call: init %empty_tuple.type = call %H.specific_fn.loc31_3.1(%x.ref.loc31) // CHECK:STDOUT: %F.ref: %F.type = name_ref F, file.%F.decl [concrete = constants.%F] // CHECK:STDOUT: %x.ref.loc32: @G.%T.loc29_6.1 (%T) = name_ref x, %x // CHECK:STDOUT: %F.specific_fn.loc32_3.1: = specific_function %F.ref, @F(constants.%T) [symbolic = %F.specific_fn.loc32_3.2 (constants.%F.specific_fn.643)] // CHECK:STDOUT: %F.call: init %empty_tuple.type = call %F.specific_fn.loc32_3.1(%x.ref.loc32) // CHECK:STDOUT: // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @M() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: // CHECK:STDOUT: %F.ref: %F.type = name_ref F, file.%F.decl [concrete = constants.%F] // CHECK:STDOUT: %n.ref.loc43: ref %C = name_ref n, %n // CHECK:STDOUT: %F.specific_fn: = specific_function %F.ref, @F(constants.%C) [concrete = constants.%F.specific_fn.540] // CHECK:STDOUT: %.loc43: %C = acquire_value %n.ref.loc43 // CHECK:STDOUT: %F.call: init %empty_tuple.type = call %F.specific_fn(%.loc43) // CHECK:STDOUT: %G.ref: %G.type = name_ref G, file.%G.decl [concrete = constants.%G] // CHECK:STDOUT: %n.ref.loc44: ref %C = name_ref n, %n // CHECK:STDOUT: %G.specific_fn: = specific_function %G.ref, @G(constants.%C) [concrete = constants.%G.specific_fn] // CHECK:STDOUT: %.loc44: %C = acquire_value %n.ref.loc44 // CHECK:STDOUT: %G.call: init %empty_tuple.type = call %G.specific_fn(%.loc44) // CHECK:STDOUT: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @F(constants.%T) { // CHECK:STDOUT: %T.loc19_6.1 => constants.%T // CHECK:STDOUT: %pattern_type => constants.%pattern_type.51d // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete => constants.%require_complete // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @H(constants.%T) { // CHECK:STDOUT: %T.loc23_6.1 => constants.%T // CHECK:STDOUT: %pattern_type => constants.%pattern_type.51d // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete => constants.%require_complete // CHECK:STDOUT: %F.specific_fn.loc25_3.2 => constants.%F.specific_fn.643 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @G(constants.%T) { // CHECK:STDOUT: %T.loc29_6.1 => constants.%T // CHECK:STDOUT: %pattern_type => constants.%pattern_type.51d // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @F(constants.%C) { // CHECK:STDOUT: %T.loc19_6.1 => constants.%C // CHECK:STDOUT: %pattern_type => constants.%pattern_type.7c7 // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete => constants.%complete_type // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @G(constants.%C) { // CHECK:STDOUT: %T.loc29_6.1 => constants.%C // CHECK:STDOUT: %pattern_type => constants.%pattern_type.7c7 // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete => constants.%complete_type // CHECK:STDOUT: %H.specific_fn.loc31_3.2 => constants.%H.specific_fn.d42 // CHECK:STDOUT: %F.specific_fn.loc32_3.2 => constants.%F.specific_fn.540 // CHECK:STDOUT: %C.Cfn.specific_fn.loc36_4.2 => constants.%C.Cfn.specific_fn.f5e // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @H(constants.%C) { // CHECK:STDOUT: %T.loc23_6.1 => constants.%C // CHECK:STDOUT: %pattern_type => constants.%pattern_type.7c7 // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete => constants.%complete_type // CHECK:STDOUT: %F.specific_fn.loc25_3.2 => constants.%F.specific_fn.540 // CHECK:STDOUT: } // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/generic/complete_type.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/destroy.carbon // TODO: Add ranges and switch to "--dump-sem-ir-ranges=only". // EXTRA-ARGS: --dump-sem-ir-ranges=if-present // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/generic/complete_type.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/generic/complete_type.carbon // --- fail_incomplete_in_class.carbon library "[[@TEST_NAME]]"; class B; class A(T:! type) { var v: T; } // CHECK:STDERR: fail_incomplete_in_class.carbon:[[@LINE+17]]:13: error: parameter has incomplete type `A(B)` in function definition [IncompleteTypeInFunctionParam] // CHECK:STDERR: fn F(unused x: A(B)) {} // CHECK:STDERR: ^~~~~~~ // CHECK:STDERR: fail_incomplete_in_class.carbon:[[@LINE-6]]:10: note: `T` evaluates to incomplete type `B` [IncompleteTypeInMonomorphization] // CHECK:STDERR: var v: T; // CHECK:STDERR: ^ // CHECK:STDERR: fail_incomplete_in_class.carbon:[[@LINE-12]]:1: note: class was forward declared here [ClassForwardDeclaredHere] // CHECK:STDERR: class B; // CHECK:STDERR: ^~~~~~~~ // CHECK:STDERR: // CHECK:STDERR: fail_incomplete_in_class.carbon:[[@LINE+7]]:13: error: parameter has incomplete type `A(B)` in function definition [IncompleteTypeInFunctionParam] // CHECK:STDERR: fn F(unused x: A(B)) {} // CHECK:STDERR: ^~~~~~~ // CHECK:STDERR: fail_incomplete_in_class.carbon:[[@LINE-19]]:1: note: class was forward declared here [ClassForwardDeclaredHere] // CHECK:STDERR: class B; // CHECK:STDERR: ^~~~~~~~ // CHECK:STDERR: fn F(unused x: A(B)) {} class B {} // --- incomplete_in_function.carbon library "[[@TEST_NAME]]"; class B; fn F(T:! type, v: T*) { *v; } // F(B) isn't resolved until the end of the file. fn G(p: B*) { F(B, p); } class B {} // --- fail_incomplete_in_function_at_eof.carbon library "[[@TEST_NAME]]"; class B; fn F(T:! type, p: T*) { let _: T = *p; } // CHECK:STDERR: fail_incomplete_in_function_at_eof.carbon:[[@LINE+10]]:15: error: unable to monomorphize specific `F(B)` [ResolvingSpecificHere] // CHECK:STDERR: fn G(p: B*) { F(B, p); } // CHECK:STDERR: ^ // CHECK:STDERR: fail_incomplete_in_function_at_eof.carbon:[[@LINE-6]]:10: note: `T` evaluates to incomplete type `B` [IncompleteTypeInMonomorphization] // CHECK:STDERR: let _: T = *p; // CHECK:STDERR: ^ // CHECK:STDERR: fail_incomplete_in_function_at_eof.carbon:[[@LINE-12]]:1: note: class was forward declared here [ClassForwardDeclaredHere] // CHECK:STDERR: class B; // CHECK:STDERR: ^~~~~~~~ // CHECK:STDERR: fn G(p: B*) { F(B, p); } // CHECK:STDOUT: --- fail_incomplete_in_class.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %B: type = class_type @B [concrete] // CHECK:STDOUT: %type: type = facet_type [concrete] // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self] // CHECK:STDOUT: %pattern_type.98f: type = pattern_type type [concrete] // CHECK:STDOUT: %T: type = symbolic_binding T, 0 [symbolic] // CHECK:STDOUT: %A.type: type = generic_class_type @A [concrete] // CHECK:STDOUT: %A.generic: %A.type = struct_value () [concrete] // CHECK:STDOUT: %A.95c: type = class_type @A, @A(%T) [symbolic] // CHECK:STDOUT: %require_complete: = require_complete_type %T [symbolic] // CHECK:STDOUT: %A.elem.8a2: type = unbound_element_type %A.95c, %T [symbolic] // CHECK:STDOUT: %struct_type.v.a4d: type = struct_type {.v: %T} [symbolic] // CHECK:STDOUT: %complete_type.136: = complete_type_witness %struct_type.v.a4d [symbolic] // CHECK:STDOUT: %A.764: type = class_type @A, @A(%B) [concrete] // CHECK:STDOUT: %pattern_type.fa6: type = pattern_type %A.764 [concrete] // CHECK:STDOUT: %F.type: type = fn_type @F [concrete] // CHECK:STDOUT: %F: %F.type = struct_value () [concrete] // CHECK:STDOUT: %A.elem.46a: type = unbound_element_type %A.764, %B [concrete] // CHECK:STDOUT: %struct_type.v.b74: type = struct_type {.v: %B} [concrete] // CHECK:STDOUT: %complete_type.aac: = complete_type_witness %struct_type.v.b74 [concrete] // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %complete_type.357: = complete_type_witness %empty_struct_type [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .B = %B.decl.loc4 // CHECK:STDOUT: .A = %A.decl // CHECK:STDOUT: .F = %F.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %B.decl.loc4: type = class_decl @B [concrete = constants.%B] {} {} // CHECK:STDOUT: %A.decl: %A.type = class_decl @A [concrete = constants.%A.generic] { // CHECK:STDOUT: %T.patt: %pattern_type.98f = symbolic_binding_pattern T, 0 [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %.loc6_13.1: type = splice_block %.loc6_13.2 [concrete = type] { // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %.loc6_13.2: type = type_literal type [concrete = type] // CHECK:STDOUT: } // CHECK:STDOUT: %T.loc6_9.2: type = symbolic_binding T, 0 [symbolic = %T.loc6_9.1 (constants.%T)] // CHECK:STDOUT: } // CHECK:STDOUT: %F.decl: %F.type = fn_decl @F [concrete = constants.%F] { // CHECK:STDOUT: %x.patt: %pattern_type.fa6 = value_binding_pattern x [concrete] // CHECK:STDOUT: %x.param_patt: %pattern_type.fa6 = value_param_pattern %x.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %x.param: %A.764 = value_param call_param0 // CHECK:STDOUT: %.loc27: type = splice_block %A [concrete = constants.%A.764] { // CHECK:STDOUT: %A.ref: %A.type = name_ref A, file.%A.decl [concrete = constants.%A.generic] // CHECK:STDOUT: %B.ref: type = name_ref B, file.%B.decl.loc4 [concrete = constants.%B] // CHECK:STDOUT: %A: type = class_type @A, @A(constants.%B) [concrete = constants.%A.764] // CHECK:STDOUT: } // CHECK:STDOUT: %x: %A.764 = value_binding x, %x.param // CHECK:STDOUT: } // CHECK:STDOUT: %B.decl.loc29: type = class_decl @B [concrete = constants.%B] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @B { // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type.357] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%B // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic class @A(%T.loc6_9.2: type) { // CHECK:STDOUT: %T.loc6_9.1: type = symbolic_binding T, 0 [symbolic = %T.loc6_9.1 (constants.%T)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete: = require_complete_type %T.loc6_9.1 [symbolic = %require_complete (constants.%require_complete)] // CHECK:STDOUT: %A: type = class_type @A, @A(%T.loc6_9.1) [symbolic = %A (constants.%A.95c)] // CHECK:STDOUT: %A.elem: type = unbound_element_type %A, %T.loc6_9.1 [symbolic = %A.elem (constants.%A.elem.8a2)] // CHECK:STDOUT: %struct_type.v: type = struct_type {.v: @A.%T.loc6_9.1 (%T)} [symbolic = %struct_type.v (constants.%struct_type.v.a4d)] // CHECK:STDOUT: %complete_type.loc8_1.2: = complete_type_witness %struct_type.v [symbolic = %complete_type.loc8_1.2 (constants.%complete_type.136)] // CHECK:STDOUT: // CHECK:STDOUT: class { // CHECK:STDOUT: %T.ref: type = name_ref T, %T.loc6_9.2 [symbolic = %T.loc6_9.1 (constants.%T)] // CHECK:STDOUT: %.loc7: @A.%A.elem (%A.elem.8a2) = field_decl v, element0 [concrete] // CHECK:STDOUT: %complete_type.loc8_1.1: = complete_type_witness constants.%struct_type.v.a4d [symbolic = %complete_type.loc8_1.2 (constants.%complete_type.136)] // CHECK:STDOUT: complete_type_witness = %complete_type.loc8_1.1 // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%A.95c // CHECK:STDOUT: .T = // CHECK:STDOUT: .v = %.loc7 // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @F(%x.param: %A.764) { // CHECK:STDOUT: !entry: // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @A(constants.%T) { // CHECK:STDOUT: %T.loc6_9.1 => constants.%T // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @A(constants.%B) { // CHECK:STDOUT: %T.loc6_9.1 => constants.%B // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete => // CHECK:STDOUT: %A => constants.%A.764 // CHECK:STDOUT: %A.elem => constants.%A.elem.46a // CHECK:STDOUT: %struct_type.v => constants.%struct_type.v.b74 // CHECK:STDOUT: %complete_type.loc8_1.2 => constants.%complete_type.aac // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- incomplete_in_function.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %B: type = class_type @B [concrete] // CHECK:STDOUT: %type: type = facet_type [concrete] // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self] // CHECK:STDOUT: %pattern_type.98f: type = pattern_type type [concrete] // CHECK:STDOUT: %T: type = symbolic_binding T, 0 [symbolic] // CHECK:STDOUT: %ptr.e8f: type = ptr_type %T [symbolic] // CHECK:STDOUT: %pattern_type.4f4: type = pattern_type %ptr.e8f [symbolic] // CHECK:STDOUT: %F.type: type = fn_type @F [concrete] // CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] // CHECK:STDOUT: %F: %F.type = struct_value () [concrete] // CHECK:STDOUT: %require_complete: = require_complete_type %ptr.e8f [symbolic] // CHECK:STDOUT: %ptr.27c: type = ptr_type %B [concrete] // CHECK:STDOUT: %pattern_type.191: type = pattern_type %ptr.27c [concrete] // CHECK:STDOUT: %G.type: type = fn_type @G [concrete] // CHECK:STDOUT: %G: %G.type = struct_value () [concrete] // CHECK:STDOUT: %F.specific_fn: = specific_function %F, @F(%B) [concrete] // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %complete_type.357: = complete_type_witness %empty_struct_type [concrete] // CHECK:STDOUT: %complete_type.04a: = complete_type_witness %ptr.27c [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .B = %B.decl.loc4 // CHECK:STDOUT: .F = %F.decl // CHECK:STDOUT: .G = %G.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %B.decl.loc4: type = class_decl @B [concrete = constants.%B] {} {} // CHECK:STDOUT: %F.decl: %F.type = fn_decl @F [concrete = constants.%F] { // CHECK:STDOUT: %T.patt: %pattern_type.98f = symbolic_binding_pattern T, 0 [concrete] // CHECK:STDOUT: %v.patt: @F.%pattern_type (%pattern_type.4f4) = value_binding_pattern v [concrete] // CHECK:STDOUT: %v.param_patt: @F.%pattern_type (%pattern_type.4f4) = value_param_pattern %v.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %.loc6_10.1: type = splice_block %.loc6_10.2 [concrete = type] { // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %.loc6_10.2: type = type_literal type [concrete = type] // CHECK:STDOUT: } // CHECK:STDOUT: %T.loc6_6.2: type = symbolic_binding T, 0 [symbolic = %T.loc6_6.1 (constants.%T)] // CHECK:STDOUT: %v.param: @F.%ptr.loc6_20.1 (%ptr.e8f) = value_param call_param0 // CHECK:STDOUT: %.loc6_20: type = splice_block %ptr.loc6_20.2 [symbolic = %ptr.loc6_20.1 (constants.%ptr.e8f)] { // CHECK:STDOUT: %T.ref: type = name_ref T, %T.loc6_6.2 [symbolic = %T.loc6_6.1 (constants.%T)] // CHECK:STDOUT: %ptr.loc6_20.2: type = ptr_type %T.ref [symbolic = %ptr.loc6_20.1 (constants.%ptr.e8f)] // CHECK:STDOUT: } // CHECK:STDOUT: %v: @F.%ptr.loc6_20.1 (%ptr.e8f) = value_binding v, %v.param // CHECK:STDOUT: } // CHECK:STDOUT: %G.decl: %G.type = fn_decl @G [concrete = constants.%G] { // CHECK:STDOUT: %p.patt: %pattern_type.191 = value_binding_pattern p [concrete] // CHECK:STDOUT: %p.param_patt: %pattern_type.191 = value_param_pattern %p.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %p.param: %ptr.27c = value_param call_param0 // CHECK:STDOUT: %.loc11: type = splice_block %ptr [concrete = constants.%ptr.27c] { // CHECK:STDOUT: %B.ref.loc11_9: type = name_ref B, file.%B.decl.loc4 [concrete = constants.%B] // CHECK:STDOUT: %ptr: type = ptr_type %B.ref.loc11_9 [concrete = constants.%ptr.27c] // CHECK:STDOUT: } // CHECK:STDOUT: %p: %ptr.27c = value_binding p, %p.param // CHECK:STDOUT: } // CHECK:STDOUT: %B.decl.loc13: type = class_decl @B [concrete = constants.%B] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @B { // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type.357] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%B // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic fn @F(%T.loc6_6.2: type) { // CHECK:STDOUT: %T.loc6_6.1: type = symbolic_binding T, 0 [symbolic = %T.loc6_6.1 (constants.%T)] // CHECK:STDOUT: %ptr.loc6_20.1: type = ptr_type %T.loc6_6.1 [symbolic = %ptr.loc6_20.1 (constants.%ptr.e8f)] // CHECK:STDOUT: %pattern_type: type = pattern_type %ptr.loc6_20.1 [symbolic = %pattern_type (constants.%pattern_type.4f4)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete: = require_complete_type %ptr.loc6_20.1 [symbolic = %require_complete (constants.%require_complete)] // CHECK:STDOUT: // CHECK:STDOUT: fn(%v.param: @F.%ptr.loc6_20.1 (%ptr.e8f)) { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %v.ref: @F.%ptr.loc6_20.1 (%ptr.e8f) = name_ref v, %v // CHECK:STDOUT: %.loc7: ref @F.%T.loc6_6.1 (%T) = deref %v.ref // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @G(%p.param: %ptr.27c) { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %F.ref: %F.type = name_ref F, file.%F.decl [concrete = constants.%F] // CHECK:STDOUT: %B.ref.loc11_17: type = name_ref B, file.%B.decl.loc4 [concrete = constants.%B] // CHECK:STDOUT: %p.ref: %ptr.27c = name_ref p, %p // CHECK:STDOUT: %F.specific_fn: = specific_function %F.ref, @F(constants.%B) [concrete = constants.%F.specific_fn] // CHECK:STDOUT: %F.call: init %empty_tuple.type = call %F.specific_fn(%p.ref) // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @F(constants.%T) { // CHECK:STDOUT: %T.loc6_6.1 => constants.%T // CHECK:STDOUT: %ptr.loc6_20.1 => constants.%ptr.e8f // CHECK:STDOUT: %pattern_type => constants.%pattern_type.4f4 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @F(constants.%B) { // CHECK:STDOUT: %T.loc6_6.1 => constants.%B // CHECK:STDOUT: %ptr.loc6_20.1 => constants.%ptr.27c // CHECK:STDOUT: %pattern_type => constants.%pattern_type.191 // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete => constants.%complete_type.04a // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- fail_incomplete_in_function_at_eof.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %B: type = class_type @B [concrete] // CHECK:STDOUT: %type: type = facet_type [concrete] // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self] // CHECK:STDOUT: %pattern_type.98f: type = pattern_type type [concrete] // CHECK:STDOUT: %T: type = symbolic_binding T, 0 [symbolic] // CHECK:STDOUT: %ptr.e8f: type = ptr_type %T [symbolic] // CHECK:STDOUT: %pattern_type.4f4: type = pattern_type %ptr.e8f [symbolic] // CHECK:STDOUT: %F.type: type = fn_type @F [concrete] // CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] // CHECK:STDOUT: %F: %F.type = struct_value () [concrete] // CHECK:STDOUT: %require_complete.ef1: = require_complete_type %ptr.e8f [symbolic] // CHECK:STDOUT: %require_complete.944: = require_complete_type %T [symbolic] // CHECK:STDOUT: %pattern_type.51d: type = pattern_type %T [symbolic] // CHECK:STDOUT: %ptr.27c: type = ptr_type %B [concrete] // CHECK:STDOUT: %pattern_type.191: type = pattern_type %ptr.27c [concrete] // CHECK:STDOUT: %G.type: type = fn_type @G [concrete] // CHECK:STDOUT: %G: %G.type = struct_value () [concrete] // CHECK:STDOUT: %F.specific_fn: = specific_function %F, @F(%B) [concrete] // CHECK:STDOUT: %complete_type: = complete_type_witness %ptr.27c [concrete] // CHECK:STDOUT: %pattern_type.1f4: type = pattern_type %B [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .B = %B.decl // CHECK:STDOUT: .F = %F.decl // CHECK:STDOUT: .G = %G.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %B.decl: type = class_decl @B [concrete = constants.%B] {} {} // CHECK:STDOUT: %F.decl: %F.type = fn_decl @F [concrete = constants.%F] { // CHECK:STDOUT: %T.patt: %pattern_type.98f = symbolic_binding_pattern T, 0 [concrete] // CHECK:STDOUT: %p.patt: @F.%pattern_type.loc6 (%pattern_type.4f4) = value_binding_pattern p [concrete] // CHECK:STDOUT: %p.param_patt: @F.%pattern_type.loc6 (%pattern_type.4f4) = value_param_pattern %p.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %.loc6_10.1: type = splice_block %.loc6_10.2 [concrete = type] { // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %.loc6_10.2: type = type_literal type [concrete = type] // CHECK:STDOUT: } // CHECK:STDOUT: %T.loc6_6.2: type = symbolic_binding T, 0 [symbolic = %T.loc6_6.1 (constants.%T)] // CHECK:STDOUT: %p.param: @F.%ptr.loc6_20.1 (%ptr.e8f) = value_param call_param0 // CHECK:STDOUT: %.loc6_20: type = splice_block %ptr.loc6_20.2 [symbolic = %ptr.loc6_20.1 (constants.%ptr.e8f)] { // CHECK:STDOUT: %T.ref.loc6: type = name_ref T, %T.loc6_6.2 [symbolic = %T.loc6_6.1 (constants.%T)] // CHECK:STDOUT: %ptr.loc6_20.2: type = ptr_type %T.ref.loc6 [symbolic = %ptr.loc6_20.1 (constants.%ptr.e8f)] // CHECK:STDOUT: } // CHECK:STDOUT: %p: @F.%ptr.loc6_20.1 (%ptr.e8f) = value_binding p, %p.param // CHECK:STDOUT: } // CHECK:STDOUT: %G.decl: %G.type = fn_decl @G [concrete = constants.%G] { // CHECK:STDOUT: %p.patt: %pattern_type.191 = value_binding_pattern p [concrete] // CHECK:STDOUT: %p.param_patt: %pattern_type.191 = value_param_pattern %p.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %p.param: %ptr.27c = value_param call_param0 // CHECK:STDOUT: %.loc20: type = splice_block %ptr [concrete = constants.%ptr.27c] { // CHECK:STDOUT: %B.ref.loc20_9: type = name_ref B, file.%B.decl [concrete = constants.%B] // CHECK:STDOUT: %ptr: type = ptr_type %B.ref.loc20_9 [concrete = constants.%ptr.27c] // CHECK:STDOUT: } // CHECK:STDOUT: %p: %ptr.27c = value_binding p, %p.param // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @B; // CHECK:STDOUT: // CHECK:STDOUT: generic fn @F(%T.loc6_6.2: type) { // CHECK:STDOUT: %T.loc6_6.1: type = symbolic_binding T, 0 [symbolic = %T.loc6_6.1 (constants.%T)] // CHECK:STDOUT: %ptr.loc6_20.1: type = ptr_type %T.loc6_6.1 [symbolic = %ptr.loc6_20.1 (constants.%ptr.e8f)] // CHECK:STDOUT: %pattern_type.loc6: type = pattern_type %ptr.loc6_20.1 [symbolic = %pattern_type.loc6 (constants.%pattern_type.4f4)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete.loc6: = require_complete_type %ptr.loc6_20.1 [symbolic = %require_complete.loc6 (constants.%require_complete.ef1)] // CHECK:STDOUT: %require_complete.loc7: = require_complete_type %T.loc6_6.1 [symbolic = %require_complete.loc7 (constants.%require_complete.944)] // CHECK:STDOUT: %pattern_type.loc7: type = pattern_type %T.loc6_6.1 [symbolic = %pattern_type.loc7 (constants.%pattern_type.51d)] // CHECK:STDOUT: // CHECK:STDOUT: fn(%p.param: @F.%ptr.loc6_20.1 (%ptr.e8f)) { // CHECK:STDOUT: !entry: // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %_.patt: @F.%pattern_type.loc7 (%pattern_type.51d) = value_binding_pattern _ [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %p.ref: @F.%ptr.loc6_20.1 (%ptr.e8f) = name_ref p, %p // CHECK:STDOUT: %.loc7_14.1: ref @F.%T.loc6_6.1 (%T) = deref %p.ref // CHECK:STDOUT: %T.ref.loc7: type = name_ref T, %T.loc6_6.2 [symbolic = %T.loc6_6.1 (constants.%T)] // CHECK:STDOUT: %.loc7_14.2: @F.%T.loc6_6.1 (%T) = acquire_value %.loc7_14.1 // CHECK:STDOUT: %_: @F.%T.loc6_6.1 (%T) = value_binding _, %.loc7_14.2 // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @G(%p.param: %ptr.27c) { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %F.ref: %F.type = name_ref F, file.%F.decl [concrete = constants.%F] // CHECK:STDOUT: %B.ref.loc20_17: type = name_ref B, file.%B.decl [concrete = constants.%B] // CHECK:STDOUT: %p.ref: %ptr.27c = name_ref p, %p // CHECK:STDOUT: %F.specific_fn: = specific_function %F.ref, @F(constants.%B) [concrete = constants.%F.specific_fn] // CHECK:STDOUT: %F.call: init %empty_tuple.type = call %F.specific_fn(%p.ref) // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @F(constants.%T) { // CHECK:STDOUT: %T.loc6_6.1 => constants.%T // CHECK:STDOUT: %ptr.loc6_20.1 => constants.%ptr.e8f // CHECK:STDOUT: %pattern_type.loc6 => constants.%pattern_type.4f4 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @F(constants.%B) { // CHECK:STDOUT: %T.loc6_6.1 => constants.%B // CHECK:STDOUT: %ptr.loc6_20.1 => constants.%ptr.27c // CHECK:STDOUT: %pattern_type.loc6 => constants.%pattern_type.191 // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete.loc6 => constants.%complete_type // CHECK:STDOUT: %require_complete.loc7 => // CHECK:STDOUT: %pattern_type.loc7 => constants.%pattern_type.1f4 // CHECK:STDOUT: } // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/generic/dependent_param.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/int.carbon // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/generic/dependent_param.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/generic/dependent_param.carbon // --- nested_class.carbon library "[[@TEST_NAME]]"; class Outer(T:! Core.Copy) { class Inner(U:! T) { //@dump-sem-ir-begin fn Get() -> T { return U; } //@dump-sem-ir-end } } //@dump-sem-ir-begin var n: i32 = Outer(i32).Inner(42).Get(); //@dump-sem-ir-end // CHECK:STDOUT: --- nested_class.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %Copy.type: type = facet_type <@Copy> [concrete] // CHECK:STDOUT: %T.035: %Copy.type = symbolic_binding T, 0 [symbolic] // CHECK:STDOUT: %Outer.type: type = generic_class_type @Outer [concrete] // CHECK:STDOUT: %Outer.generic: %Outer.type = struct_value () [concrete] // CHECK:STDOUT: %Outer.4b9: type = class_type @Outer, @Outer(%T.035) [symbolic] // CHECK:STDOUT: %T.binding.as_type: type = symbolic_binding_type T, 0, %T.035 [symbolic] // CHECK:STDOUT: %pattern_type.9b9f0c.2: type = pattern_type %T.binding.as_type [symbolic] // CHECK:STDOUT: %U.959: %T.binding.as_type = symbolic_binding U, 1 [symbolic] // CHECK:STDOUT: %Inner.type.af4: type = generic_class_type @Inner, @Outer(%T.035) [symbolic] // CHECK:STDOUT: %Inner.generic.994: %Inner.type.af4 = struct_value () [symbolic] // CHECK:STDOUT: %Inner.c23: type = class_type @Inner, @Inner(%T.035, %U.959) [symbolic] // CHECK:STDOUT: %.076a48.2: Core.Form = init_form %T.binding.as_type [symbolic] // CHECK:STDOUT: %Inner.Get.type.6ef: type = fn_type @Inner.Get, @Inner(%T.035, %U.959) [symbolic] // CHECK:STDOUT: %Inner.Get.ba5: %Inner.Get.type.6ef = struct_value () [symbolic] // CHECK:STDOUT: %require_complete.67c: = require_complete_type %T.binding.as_type [symbolic] // CHECK:STDOUT: %Copy.lookup_impl_witness.58d: = lookup_impl_witness %T.035, @Copy [symbolic] // CHECK:STDOUT: %Copy.WithSelf.Op.type.735e75.2: type = fn_type @Copy.WithSelf.Op, @Copy.WithSelf(%T.035) [symbolic] // CHECK:STDOUT: %.023: type = fn_type_with_self_type %Copy.WithSelf.Op.type.735e75.2, %T.035 [symbolic] // CHECK:STDOUT: %impl.elem0.594: %.023 = impl_witness_access %Copy.lookup_impl_witness.58d, element0 [symbolic] // CHECK:STDOUT: %specific_impl_fn.bdc: = specific_impl_function %impl.elem0.594, @Copy.WithSelf.Op(%T.035) [symbolic] // CHECK:STDOUT: %bound_method.068: = bound_method %U.959, %impl.elem0.594 [symbolic] // CHECK:STDOUT: %bound_method.d5d: = bound_method %U.959, %specific_impl_fn.bdc [symbolic] // CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [concrete] // CHECK:STDOUT: %N: Core.IntLiteral = symbolic_binding N, 0 [symbolic] // CHECK:STDOUT: %i32: type = class_type @Int, @Int(%int_32) [concrete] // CHECK:STDOUT: %i32.builtin: type = int_type signed, %int_32 [concrete] // CHECK:STDOUT: %complete_type.f8a: = complete_type_witness %i32.builtin [concrete] // CHECK:STDOUT: %pattern_type.7ce: type = pattern_type %i32 [concrete] // CHECK:STDOUT: %Int.as.Copy.impl.Op.type.824: type = fn_type @Int.as.Copy.impl.Op, @Int.as.Copy.impl(%N) [symbolic] // CHECK:STDOUT: %Int.as.Copy.impl.Op.9b9: %Int.as.Copy.impl.Op.type.824 = struct_value () [symbolic] // CHECK:STDOUT: %Copy.impl_witness.f17: = impl_witness imports.%Copy.impl_witness_table.e76, @Int.as.Copy.impl(%int_32) [concrete] // CHECK:STDOUT: %Int.as.Copy.impl.Op.type.546: type = fn_type @Int.as.Copy.impl.Op, @Int.as.Copy.impl(%int_32) [concrete] // CHECK:STDOUT: %Int.as.Copy.impl.Op.664: %Int.as.Copy.impl.Op.type.546 = struct_value () [concrete] // CHECK:STDOUT: %Copy.facet.de4: %Copy.type = facet_value %i32, (%Copy.impl_witness.f17) [concrete] // CHECK:STDOUT: %Outer.a6c: type = class_type @Outer, @Outer(%Copy.facet.de4) [concrete] // CHECK:STDOUT: %Inner.type.518: type = generic_class_type @Inner, @Outer(%Copy.facet.de4) [concrete] // CHECK:STDOUT: %Inner.generic.887: %Inner.type.518 = struct_value () [concrete] // CHECK:STDOUT: %int_42.20e: Core.IntLiteral = int_value 42 [concrete] // CHECK:STDOUT: %Copy.impl_witness.006: = impl_witness imports.%Copy.impl_witness_table.b6d [concrete] // CHECK:STDOUT: %Copy.facet.cdd: %Copy.type = facet_value Core.IntLiteral, (%Copy.impl_witness.006) [concrete] // CHECK:STDOUT: %ImplicitAs.type.e8c: type = facet_type <@ImplicitAs, @ImplicitAs(%i32)> [concrete] // CHECK:STDOUT: %To: Core.IntLiteral = symbolic_binding To, 0 [symbolic] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.4e6: type = fn_type @Core.IntLiteral.as.ImplicitAs.impl.Convert, @Core.IntLiteral.as.ImplicitAs.impl(%To) [symbolic] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.3c2: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.4e6 = struct_value () [symbolic] // CHECK:STDOUT: %ImplicitAs.impl_witness.6bc: = impl_witness imports.%ImplicitAs.impl_witness_table.74f, @Core.IntLiteral.as.ImplicitAs.impl(%int_32) [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.e0d: type = fn_type @Core.IntLiteral.as.ImplicitAs.impl.Convert, @Core.IntLiteral.as.ImplicitAs.impl(%int_32) [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.e0d = struct_value () [concrete] // CHECK:STDOUT: %ImplicitAs.facet: %ImplicitAs.type.e8c = facet_value Core.IntLiteral, (%ImplicitAs.impl_witness.6bc) [concrete] // CHECK:STDOUT: %ImplicitAs.WithSelf.Convert.type.b37: type = fn_type @ImplicitAs.WithSelf.Convert, @ImplicitAs.WithSelf(%i32, %ImplicitAs.facet) [concrete] // CHECK:STDOUT: %.545: type = fn_type_with_self_type %ImplicitAs.WithSelf.Convert.type.b37, %ImplicitAs.facet [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.bound: = bound_method %int_42.20e, %Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5 [concrete] // CHECK:STDOUT: %.ff5: Core.Form = init_form %i32 [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn: = specific_function %Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5, @Core.IntLiteral.as.ImplicitAs.impl.Convert(%int_32) [concrete] // CHECK:STDOUT: %bound_method.cb9: = bound_method %int_42.20e, %Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn [concrete] // CHECK:STDOUT: %int_42.c68: %i32 = int_value 42 [concrete] // CHECK:STDOUT: %Inner.e4a: type = class_type @Inner, @Inner(%Copy.facet.de4, %int_42.c68) [concrete] // CHECK:STDOUT: %Inner.Get.type.20d: type = fn_type @Inner.Get, @Inner(%Copy.facet.de4, %int_42.c68) [concrete] // CHECK:STDOUT: %Inner.Get.f96: %Inner.Get.type.20d = struct_value () [concrete] // CHECK:STDOUT: %Inner.Get.specific_fn: = specific_function %Inner.Get.f96, @Inner.Get(%Copy.facet.de4, %int_42.c68) [concrete] // CHECK:STDOUT: %Copy.WithSelf.Op.type.081: type = fn_type @Copy.WithSelf.Op, @Copy.WithSelf(%Copy.facet.de4) [concrete] // CHECK:STDOUT: %.8e2: type = fn_type_with_self_type %Copy.WithSelf.Op.type.081, %Copy.facet.de4 [concrete] // CHECK:STDOUT: %Int.as.Copy.impl.Op.bound: = bound_method %int_42.c68, %Int.as.Copy.impl.Op.664 [concrete] // CHECK:STDOUT: %Int.as.Copy.impl.Op.specific_fn: = specific_function %Int.as.Copy.impl.Op.664, @Int.as.Copy.impl.Op(%int_32) [concrete] // CHECK:STDOUT: %bound_method.d85: = bound_method %int_42.c68, %Int.as.Copy.impl.Op.specific_fn [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core.import_ref.18d: @Int.as.Copy.impl.%Int.as.Copy.impl.Op.type (%Int.as.Copy.impl.Op.type.824) = import_ref Core//prelude/parts/int, loc{{\d+_\d+}}, loaded [symbolic = @Int.as.Copy.impl.%Int.as.Copy.impl.Op (constants.%Int.as.Copy.impl.Op.9b9)] // CHECK:STDOUT: %Copy.impl_witness_table.e76 = impl_witness_table (%Core.import_ref.18d), @Int.as.Copy.impl [concrete] // CHECK:STDOUT: %Core.import_ref.bb6 = import_ref Core//prelude/parts/copy, loc{{\d+_\d+}}, unloaded // CHECK:STDOUT: %Copy.impl_witness_table.b6d = impl_witness_table (%Core.import_ref.bb6), @Core.IntLiteral.as.Copy.impl [concrete] // CHECK:STDOUT: %Core.import_ref.42d: @Core.IntLiteral.as.ImplicitAs.impl.%Core.IntLiteral.as.ImplicitAs.impl.Convert.type (%Core.IntLiteral.as.ImplicitAs.impl.Convert.type.4e6) = import_ref Core//prelude/parts/int, loc{{\d+_\d+}}, loaded [symbolic = @Core.IntLiteral.as.ImplicitAs.impl.%Core.IntLiteral.as.ImplicitAs.impl.Convert (constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.3c2)] // CHECK:STDOUT: %ImplicitAs.impl_witness_table.74f = impl_witness_table (%Core.import_ref.42d), @Core.IntLiteral.as.ImplicitAs.impl [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %n.patt: %pattern_type.7ce = ref_binding_pattern n [concrete] // CHECK:STDOUT: %n.var_patt: %pattern_type.7ce = var_pattern %n.patt [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %n.var: ref %i32 = var %n.var_patt [concrete] // CHECK:STDOUT: %i32: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %n: ref %i32 = ref_binding n, %n.var [concrete = %n.var] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic class @Outer(%T.loc4_13.2: %Copy.type) { // CHECK:STDOUT: // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: // CHECK:STDOUT: // CHECK:STDOUT: class { // CHECK:STDOUT: // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%Outer.4b9 // CHECK:STDOUT: .T = // CHECK:STDOUT: .Inner = %Inner.decl // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic class @Inner(@Outer.%T.loc4_13.2: %Copy.type, %U.loc5_15.2: @Inner.%T.binding.as_type (%T.binding.as_type)) { // CHECK:STDOUT: // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %Inner.Get.type: type = fn_type @Inner.Get, @Inner(%T, %U.loc5_15.1) [symbolic = %Inner.Get.type (constants.%Inner.Get.type.6ef)] // CHECK:STDOUT: %Inner.Get: @Inner.%Inner.Get.type (%Inner.Get.type.6ef) = struct_value () [symbolic = %Inner.Get (constants.%Inner.Get.ba5)] // CHECK:STDOUT: // CHECK:STDOUT: class { // CHECK:STDOUT: %Inner.Get.decl: @Inner.%Inner.Get.type (%Inner.Get.type.6ef) = fn_decl @Inner.Get [symbolic = @Inner.%Inner.Get (constants.%Inner.Get.ba5)] { // CHECK:STDOUT: %return.patt: @Inner.Get.%pattern_type (%pattern_type.9b9f0c.2) = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: @Inner.Get.%pattern_type (%pattern_type.9b9f0c.2) = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %T.ref: %Copy.type = name_ref T, @Outer.%T.loc4_13.2 [symbolic = %T (constants.%T.035)] // CHECK:STDOUT: %T.as_type: type = facet_access_type %T.ref [symbolic = %T.binding.as_type (constants.%T.binding.as_type)] // CHECK:STDOUT: %.loc7_17.3: type = converted %T.ref, %T.as_type [symbolic = %T.binding.as_type (constants.%T.binding.as_type)] // CHECK:STDOUT: %.loc7_17.4: Core.Form = init_form %.loc7_17.3 [symbolic = %.loc7_17.2 (constants.%.076a48.2)] // CHECK:STDOUT: %return.param: ref @Inner.Get.%T.binding.as_type (%T.binding.as_type) = out_param call_param0 // CHECK:STDOUT: %return: ref @Inner.Get.%T.binding.as_type (%T.binding.as_type) = return_slot %return.param // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%Inner.c23 // CHECK:STDOUT: .T = // CHECK:STDOUT: .Get = %Inner.Get.decl // CHECK:STDOUT: .U = // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic fn @Inner.Get(@Outer.%T.loc4_13.2: %Copy.type, @Inner.%U.loc5_15.2: @Inner.%T.binding.as_type (%T.binding.as_type)) { // CHECK:STDOUT: %T: %Copy.type = symbolic_binding T, 0 [symbolic = %T (constants.%T.035)] // CHECK:STDOUT: %T.binding.as_type: type = symbolic_binding_type T, 0, %T [symbolic = %T.binding.as_type (constants.%T.binding.as_type)] // CHECK:STDOUT: %.loc7_17.2: Core.Form = init_form %T.binding.as_type [symbolic = %.loc7_17.2 (constants.%.076a48.2)] // CHECK:STDOUT: %pattern_type: type = pattern_type %T.binding.as_type [symbolic = %pattern_type (constants.%pattern_type.9b9f0c.2)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete: = require_complete_type %T.binding.as_type [symbolic = %require_complete (constants.%require_complete.67c)] // CHECK:STDOUT: %U: @Inner.Get.%T.binding.as_type (%T.binding.as_type) = symbolic_binding U, 1 [symbolic = %U (constants.%U.959)] // CHECK:STDOUT: %Copy.lookup_impl_witness: = lookup_impl_witness %T, @Copy [symbolic = %Copy.lookup_impl_witness (constants.%Copy.lookup_impl_witness.58d)] // CHECK:STDOUT: %Copy.WithSelf.Op.type: type = fn_type @Copy.WithSelf.Op, @Copy.WithSelf(%T) [symbolic = %Copy.WithSelf.Op.type (constants.%Copy.WithSelf.Op.type.735e75.2)] // CHECK:STDOUT: %.loc7_28: type = fn_type_with_self_type %Copy.WithSelf.Op.type, %T [symbolic = %.loc7_28 (constants.%.023)] // CHECK:STDOUT: %impl.elem0.loc7_28.2: @Inner.Get.%.loc7_28 (%.023) = impl_witness_access %Copy.lookup_impl_witness, element0 [symbolic = %impl.elem0.loc7_28.2 (constants.%impl.elem0.594)] // CHECK:STDOUT: %bound_method.loc7_28.3: = bound_method %U, %impl.elem0.loc7_28.2 [symbolic = %bound_method.loc7_28.3 (constants.%bound_method.068)] // CHECK:STDOUT: %specific_impl_fn.loc7_28.2: = specific_impl_function %impl.elem0.loc7_28.2, @Copy.WithSelf.Op(%T) [symbolic = %specific_impl_fn.loc7_28.2 (constants.%specific_impl_fn.bdc)] // CHECK:STDOUT: %bound_method.loc7_28.4: = bound_method %U, %specific_impl_fn.loc7_28.2 [symbolic = %bound_method.loc7_28.4 (constants.%bound_method.d5d)] // CHECK:STDOUT: // CHECK:STDOUT: fn() -> out %return.param: @Inner.Get.%T.binding.as_type (%T.binding.as_type) { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %U.ref: @Inner.Get.%T.binding.as_type (%T.binding.as_type) = name_ref U, @Inner.%U.loc5_15.2 [symbolic = %U (constants.%U.959)] // CHECK:STDOUT: %impl.elem0.loc7_28.1: @Inner.Get.%.loc7_28 (%.023) = impl_witness_access constants.%Copy.lookup_impl_witness.58d, element0 [symbolic = %impl.elem0.loc7_28.2 (constants.%impl.elem0.594)] // CHECK:STDOUT: %bound_method.loc7_28.1: = bound_method %U.ref, %impl.elem0.loc7_28.1 [symbolic = %bound_method.loc7_28.3 (constants.%bound_method.068)] // CHECK:STDOUT: %specific_impl_fn.loc7_28.1: = specific_impl_function %impl.elem0.loc7_28.1, @Copy.WithSelf.Op(constants.%T.035) [symbolic = %specific_impl_fn.loc7_28.2 (constants.%specific_impl_fn.bdc)] // CHECK:STDOUT: %bound_method.loc7_28.2: = bound_method %U.ref, %specific_impl_fn.loc7_28.1 [symbolic = %bound_method.loc7_28.4 (constants.%bound_method.d5d)] // CHECK:STDOUT: %.loc7_17.1: ref @Inner.Get.%T.binding.as_type (%T.binding.as_type) = splice_block %return.param {} // CHECK:STDOUT: %Copy.WithSelf.Op.call: init @Inner.Get.%T.binding.as_type (%T.binding.as_type) to %.loc7_17.1 = call %bound_method.loc7_28.2(%U.ref) // CHECK:STDOUT: return %Copy.WithSelf.Op.call to %return.param // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @__global_init() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %Outer.ref: %Outer.type = name_ref Outer, file.%Outer.decl [concrete = constants.%Outer.generic] // CHECK:STDOUT: %i32: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %Copy.facet.loc13_23: %Copy.type = facet_value %i32, (constants.%Copy.impl_witness.f17) [concrete = constants.%Copy.facet.de4] // CHECK:STDOUT: %.loc13_23: %Copy.type = converted %i32, %Copy.facet.loc13_23 [concrete = constants.%Copy.facet.de4] // CHECK:STDOUT: %Outer: type = class_type @Outer, @Outer(constants.%Copy.facet.de4) [concrete = constants.%Outer.a6c] // CHECK:STDOUT: %.loc13_24: %Inner.type.518 = specific_constant @Outer.%Inner.decl, @Outer(constants.%Copy.facet.de4) [concrete = constants.%Inner.generic.887] // CHECK:STDOUT: %Inner.ref: %Inner.type.518 = name_ref Inner, %.loc13_24 [concrete = constants.%Inner.generic.887] // CHECK:STDOUT: %int_42: Core.IntLiteral = int_value 42 [concrete = constants.%int_42.20e] // CHECK:STDOUT: %Copy.facet.loc13_33: %Copy.type = facet_value Core.IntLiteral, (constants.%Copy.impl_witness.006) [concrete = constants.%Copy.facet.cdd] // CHECK:STDOUT: %.loc13_33.1: %Copy.type = converted Core.IntLiteral, %Copy.facet.loc13_33 [concrete = constants.%Copy.facet.cdd] // CHECK:STDOUT: %impl.elem0: %.545 = impl_witness_access constants.%ImplicitAs.impl_witness.6bc, element0 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5] // CHECK:STDOUT: %bound_method.loc13_33.1: = bound_method constants.%int_42.20e, %impl.elem0 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.bound] // CHECK:STDOUT: %specific_fn: = specific_function %impl.elem0, @Core.IntLiteral.as.ImplicitAs.impl.Convert(constants.%int_32) [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn] // CHECK:STDOUT: %bound_method.loc13_33.2: = bound_method constants.%int_42.20e, %specific_fn [concrete = constants.%bound_method.cb9] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.call: init %i32 = call %bound_method.loc13_33.2(constants.%int_42.20e) [concrete = constants.%int_42.c68] // CHECK:STDOUT: %.loc13_33.2: %i32 = value_of_initializer %Core.IntLiteral.as.ImplicitAs.impl.Convert.call [concrete = constants.%int_42.c68] // CHECK:STDOUT: %.loc13_33.3: %i32 = converted constants.%int_42.20e, %.loc13_33.2 [concrete = constants.%int_42.c68] // CHECK:STDOUT: %Inner: type = class_type @Inner, @Inner(constants.%Copy.facet.de4, constants.%int_42.c68) [concrete = constants.%Inner.e4a] // CHECK:STDOUT: %.loc13_34: %Inner.Get.type.20d = specific_constant @Inner.%Inner.Get.decl, @Inner(constants.%Copy.facet.de4, constants.%int_42.c68) [concrete = constants.%Inner.Get.f96] // CHECK:STDOUT: %Get.ref: %Inner.Get.type.20d = name_ref Get, %.loc13_34 [concrete = constants.%Inner.Get.f96] // CHECK:STDOUT: %Inner.Get.specific_fn: = specific_function %Get.ref, @Inner.Get(constants.%Copy.facet.de4, constants.%int_42.c68) [concrete = constants.%Inner.Get.specific_fn] // CHECK:STDOUT: %Inner.Get.call: init %i32 = call %Inner.Get.specific_fn() // CHECK:STDOUT: assign file.%n.var, %Inner.Get.call // CHECK:STDOUT: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @Outer(constants.%T.035) { // CHECK:STDOUT: %T.loc4_13.1 => constants.%T.035 // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %Inner.type => constants.%Inner.type.af4 // CHECK:STDOUT: %Inner.generic => constants.%Inner.generic.994 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @Inner(constants.%T.035, constants.%U.959) { // CHECK:STDOUT: %T => constants.%T.035 // CHECK:STDOUT: %T.binding.as_type => constants.%T.binding.as_type // CHECK:STDOUT: %U.loc5_15.1 => constants.%U.959 // CHECK:STDOUT: %pattern_type => constants.%pattern_type.9b9f0c.2 // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %Inner.Get.type => constants.%Inner.Get.type.6ef // CHECK:STDOUT: %Inner.Get => constants.%Inner.Get.ba5 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @Inner.Get(constants.%T.035, constants.%U.959) { // CHECK:STDOUT: %T => constants.%T.035 // CHECK:STDOUT: %T.binding.as_type => constants.%T.binding.as_type // CHECK:STDOUT: %.loc7_17.2 => constants.%.076a48.2 // CHECK:STDOUT: %pattern_type => constants.%pattern_type.9b9f0c.2 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @Outer(constants.%Copy.facet.de4) { // CHECK:STDOUT: %T.loc4_13.1 => constants.%Copy.facet.de4 // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %Inner.type => constants.%Inner.type.518 // CHECK:STDOUT: %Inner.generic => constants.%Inner.generic.887 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @Inner(constants.%Copy.facet.de4, constants.%int_42.c68) { // CHECK:STDOUT: %T => constants.%Copy.facet.de4 // CHECK:STDOUT: %T.binding.as_type => constants.%i32 // CHECK:STDOUT: %U.loc5_15.1 => constants.%int_42.c68 // CHECK:STDOUT: %pattern_type => constants.%pattern_type.7ce // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %Inner.Get.type => constants.%Inner.Get.type.20d // CHECK:STDOUT: %Inner.Get => constants.%Inner.Get.f96 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @Inner.Get(constants.%Copy.facet.de4, constants.%int_42.c68) { // CHECK:STDOUT: %T => constants.%Copy.facet.de4 // CHECK:STDOUT: %T.binding.as_type => constants.%i32 // CHECK:STDOUT: %.loc7_17.2 => constants.%.ff5 // CHECK:STDOUT: %pattern_type => constants.%pattern_type.7ce // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete => constants.%complete_type.f8a // CHECK:STDOUT: %U => constants.%int_42.c68 // CHECK:STDOUT: %Copy.lookup_impl_witness => constants.%Copy.impl_witness.f17 // CHECK:STDOUT: %Copy.WithSelf.Op.type => constants.%Copy.WithSelf.Op.type.081 // CHECK:STDOUT: %.loc7_28 => constants.%.8e2 // CHECK:STDOUT: %impl.elem0.loc7_28.2 => constants.%Int.as.Copy.impl.Op.664 // CHECK:STDOUT: %bound_method.loc7_28.3 => constants.%Int.as.Copy.impl.Op.bound // CHECK:STDOUT: %specific_impl_fn.loc7_28.2 => constants.%Int.as.Copy.impl.Op.specific_fn // CHECK:STDOUT: %bound_method.loc7_28.4 => constants.%bound_method.d85 // CHECK:STDOUT: } // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/generic/dot_self_symbolic_type.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/none.carbon // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/generic/dot_self_symbolic_type.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/generic/dot_self_symbolic_type.carbon // --- fail_dot_self_symbolic_type.carbon library "[[@TEST_NAME]]"; interface A(AA:! type) { let X:! type; } class C(CC:! type) { //@dump-sem-ir-begin // The `.Self` bind_symbolic_name here should have a // symbolic constant value, not symbolic_self, because // its type depends on `CC`. fn F(unused T:! A(CC) where .X = CC) {} //@dump-sem-ir-end } class D(DD:! type) { fn G(T:! type) { //@dump-sem-ir-begin // CHECK:STDERR: fail_dot_self_symbolic_type.carbon:[[@LINE+7]]:5: error: cannot convert type `T` into type implementing `A(DD) where .(A(DD).X) = DD` [ConversionFailureTypeToFacet] // CHECK:STDERR: C(DD).F(T); // CHECK:STDERR: ^~~~~~~~~~ // CHECK:STDERR: fail_dot_self_symbolic_type.carbon:[[@LINE-10]]:3: note: while deducing parameters of generic declared here [DeductionGenericHere] // CHECK:STDERR: fn F(unused T:! A(CC) where .X = CC) {} // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: C(DD).F(T); //@dump-sem-ir-end } } fn H(T:! type) { D({}).G(T); } // --- dot_self_symbolic_in_impl_lookup.carbon library "[[@TEST_NAME]]"; interface A(AA:! type) { let X:! type; } interface B(AA:! type) {} //@dump-sem-ir-begin impl forall [AA:! type, BB:! A(AA) where .X = ()] BB as B(AA*) {} //@dump-sem-ir-end impl forall [AA:! type, BB:! type] BB as B(AA) {} class D(DD:! type) { fn G(T:! type) { // TODO: This produces a `lookup_impl_witness` instruction looking // for `.Self` as `A(DD)` in the generic eval block for `G`. That // is from an impl we ended up not using, so shouldn't be included // in the eval block. //@dump-sem-ir-begin T as B(DD*); //@dump-sem-ir-end } } fn H(T:! type) { D({}).G(T); } // CHECK:STDOUT: --- fail_dot_self_symbolic_type.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %A.type.464: type = generic_interface_type @A [concrete] // CHECK:STDOUT: %A.generic: %A.type.464 = struct_value () [concrete] // CHECK:STDOUT: %CC: type = symbolic_binding CC, 0 [symbolic] // CHECK:STDOUT: %C.type: type = generic_class_type @C [concrete] // CHECK:STDOUT: %C.generic: %C.type = struct_value () [concrete] // CHECK:STDOUT: %C.5a3350.1: type = class_type @C, @C(%CC) [symbolic] // CHECK:STDOUT: %A.type.ade3d9.2: type = facet_type <@A, @A(%CC)> [symbolic] // CHECK:STDOUT: %.Self.660472.1: %A.type.ade3d9.2 = symbolic_binding .Self [symbolic] // CHECK:STDOUT: %A.assoc_type.9f4820.2: type = assoc_entity_type @A, @A(%CC) [symbolic] // CHECK:STDOUT: %assoc0.9fa860.2: %A.assoc_type.9f4820.2 = assoc_entity element0, @A.WithSelf.%X [symbolic] // CHECK:STDOUT: %require_complete.f58: = require_complete_type %A.type.ade3d9.2 [symbolic] // CHECK:STDOUT: %.Self.binding.as_type: type = symbolic_binding_type .Self, %.Self.660472.1 [symbolic] // CHECK:STDOUT: %A.lookup_impl_witness.04abbb.1: = lookup_impl_witness %.Self.660472.1, @A, @A(%CC) [symbolic] // CHECK:STDOUT: %impl.elem0.f51d29.1: type = impl_witness_access %A.lookup_impl_witness.04abbb.1, element0 [symbolic] // CHECK:STDOUT: %A_where.type.4028e0.1: type = facet_type <@A, @A(%CC) where %impl.elem0.f51d29.1 = %CC> [symbolic] // CHECK:STDOUT: %pattern_type.1d5: type = pattern_type %A_where.type.4028e0.1 [symbolic] // CHECK:STDOUT: %T.6c0: %A_where.type.4028e0.1 = symbolic_binding T, 1 [symbolic] // CHECK:STDOUT: %C.F.type.1f899e.1: type = fn_type @C.F, @C(%CC) [symbolic] // CHECK:STDOUT: %C.F.faeec9.1: %C.F.type.1f899e.1 = struct_value () [symbolic] // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %complete_type: = complete_type_witness %empty_struct_type [concrete] // CHECK:STDOUT: %DD: type = symbolic_binding DD, 0 [symbolic] // CHECK:STDOUT: %D.bd6: type = class_type @D, @D(%DD) [symbolic] // CHECK:STDOUT: %T.091: type = symbolic_binding T, 1 [symbolic] // CHECK:STDOUT: %D.G.type.49d: type = fn_type @D.G, @D(%DD) [symbolic] // CHECK:STDOUT: %D.G.891: %D.G.type.49d = struct_value () [symbolic] // CHECK:STDOUT: %C.5a3350.2: type = class_type @C, @C(%DD) [symbolic] // CHECK:STDOUT: %C.F.type.1f899e.2: type = fn_type @C.F, @C(%DD) [symbolic] // CHECK:STDOUT: %C.F.faeec9.2: %C.F.type.1f899e.2 = struct_value () [symbolic] // CHECK:STDOUT: %require_complete.32c: = require_complete_type %C.5a3350.2 [symbolic] // CHECK:STDOUT: %A.type.ade3d9.3: type = facet_type <@A, @A(%DD)> [symbolic] // CHECK:STDOUT: %.Self.660472.2: %A.type.ade3d9.3 = symbolic_binding .Self [symbolic] // CHECK:STDOUT: %A.lookup_impl_witness.04abbb.2: = lookup_impl_witness %.Self.660472.2, @A, @A(%DD) [symbolic] // CHECK:STDOUT: %impl.elem0.f51d29.2: type = impl_witness_access %A.lookup_impl_witness.04abbb.2, element0 [symbolic] // CHECK:STDOUT: %T.67d: type = symbolic_binding T, 0 [symbolic] // CHECK:STDOUT: %D.G.type.235: type = fn_type @D.G, @D(%empty_struct_type) [concrete] // CHECK:STDOUT: %D.G.be1: %D.G.type.235 = struct_value () [concrete] // CHECK:STDOUT: %C.850: type = class_type @C, @C(%empty_struct_type) [concrete] // CHECK:STDOUT: %C.F.type.3ff: type = fn_type @C.F, @C(%empty_struct_type) [concrete] // CHECK:STDOUT: %C.F.240: %C.F.type.3ff = struct_value () [concrete] // CHECK:STDOUT: %A.type.69e: type = facet_type <@A, @A(%empty_struct_type)> [concrete] // CHECK:STDOUT: %.Self.f50: %A.type.69e = symbolic_binding .Self [symbolic_self] // CHECK:STDOUT: %A.lookup_impl_witness.219: = lookup_impl_witness %.Self.f50, @A, @A(%empty_struct_type) [symbolic_self] // CHECK:STDOUT: %impl.elem0.d97: type = impl_witness_access %A.lookup_impl_witness.219, element0 [symbolic_self] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic class @C(%CC.loc6_9.2: type) { // CHECK:STDOUT: // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %C.F.type: type = fn_type @C.F, @C(%CC.loc6_9.1) [symbolic = %C.F.type (constants.%C.F.type.1f899e.1)] // CHECK:STDOUT: %C.F: @C.%C.F.type (%C.F.type.1f899e.1) = struct_value () [symbolic = %C.F (constants.%C.F.faeec9.1)] // CHECK:STDOUT: // CHECK:STDOUT: class { // CHECK:STDOUT: %C.F.decl: @C.%C.F.type (%C.F.type.1f899e.1) = fn_decl @C.F [symbolic = @C.%C.F (constants.%C.F.faeec9.1)] { // CHECK:STDOUT: %T.patt: @C.F.%pattern_type (%pattern_type.1d5) = symbolic_binding_pattern T, 1 [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %.loc11_25.1: type = splice_block %.loc11_25.2 [symbolic = %A_where.type (constants.%A_where.type.4028e0.1)] { // CHECK:STDOUT: // CHECK:STDOUT: %A.ref: %A.type.464 = name_ref A, file.%A.decl [concrete = constants.%A.generic] // CHECK:STDOUT: %CC.ref.loc11_21: type = name_ref CC, @C.%CC.loc6_9.2 [symbolic = %CC (constants.%CC)] // CHECK:STDOUT: %A.type.loc11_23.2: type = facet_type <@A, @A(constants.%CC)> [symbolic = %A.type.loc11_23.1 (constants.%A.type.ade3d9.2)] // CHECK:STDOUT: // CHECK:STDOUT: %.Self.ref: @C.F.%A.type.loc11_23.1 (%A.type.ade3d9.2) = name_ref .Self, %.Self.3 [symbolic = %.Self.1 (constants.%.Self.660472.1)] // CHECK:STDOUT: %.Self.as_type: type = facet_access_type %.Self.ref [symbolic = %.Self.binding.as_type (constants.%.Self.binding.as_type)] // CHECK:STDOUT: %.loc11_31.1: type = converted %.Self.ref, %.Self.as_type [symbolic = %.Self.binding.as_type (constants.%.Self.binding.as_type)] // CHECK:STDOUT: %.loc11_31.2: @C.F.%A.assoc_type (%A.assoc_type.9f4820.2) = specific_constant @X.%assoc0, @A.WithSelf(constants.%CC, constants.%.Self.660472.1) [symbolic = %assoc0 (constants.%assoc0.9fa860.2)] // CHECK:STDOUT: %X.ref: @C.F.%A.assoc_type (%A.assoc_type.9f4820.2) = name_ref X, %.loc11_31.2 [symbolic = %assoc0 (constants.%assoc0.9fa860.2)] // CHECK:STDOUT: %impl.elem0.loc11_31.2: type = impl_witness_access constants.%A.lookup_impl_witness.04abbb.1, element0 [symbolic = %impl.elem0.loc11_31.1 (constants.%impl.elem0.f51d29.1)] // CHECK:STDOUT: %CC.ref.loc11_36: type = name_ref CC, @C.%CC.loc6_9.2 [symbolic = %CC (constants.%CC)] // CHECK:STDOUT: %.loc11_25.2: type = where_expr %.Self.3 [symbolic = %A_where.type (constants.%A_where.type.4028e0.1)] { // CHECK:STDOUT: requirement_base_facet_type constants.%A.type.ade3d9.2 // CHECK:STDOUT: requirement_rewrite %impl.elem0.loc11_31.2, %CC.ref.loc11_36 // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: %T.loc11_15.2: @C.F.%A_where.type (%A_where.type.4028e0.1) = symbolic_binding T, 1 [symbolic = %T.loc11_15.1 (constants.%T.6c0)] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%C.5a3350.1 // CHECK:STDOUT: .A = // CHECK:STDOUT: .CC = // CHECK:STDOUT: .F = %C.F.decl // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic class @D(%DD.loc15_9.2: type) { // CHECK:STDOUT: // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: // CHECK:STDOUT: // CHECK:STDOUT: class { // CHECK:STDOUT: // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%D.bd6 // CHECK:STDOUT: .G = %D.G.decl // CHECK:STDOUT: .C = // CHECK:STDOUT: .DD = // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic fn @C.F(@C.%CC.loc6_9.2: type, %T.loc11_15.2: @C.F.%A_where.type (%A_where.type.4028e0.1)) { // CHECK:STDOUT: %CC: type = symbolic_binding CC, 0 [symbolic = %CC (constants.%CC)] // CHECK:STDOUT: %A.type.loc11_23.1: type = facet_type <@A, @A(%CC)> [symbolic = %A.type.loc11_23.1 (constants.%A.type.ade3d9.2)] // CHECK:STDOUT: // CHECK:STDOUT: %require_complete: = require_complete_type %A.type.loc11_23.1 [symbolic = %require_complete (constants.%require_complete.f58)] // CHECK:STDOUT: %.Self.binding.as_type: type = symbolic_binding_type .Self, %.Self.1 [symbolic = %.Self.binding.as_type (constants.%.Self.binding.as_type)] // CHECK:STDOUT: %A.assoc_type: type = assoc_entity_type @A, @A(%CC) [symbolic = %A.assoc_type (constants.%A.assoc_type.9f4820.2)] // CHECK:STDOUT: %assoc0: @C.F.%A.assoc_type (%A.assoc_type.9f4820.2) = assoc_entity element0, @A.WithSelf.%X [symbolic = %assoc0 (constants.%assoc0.9fa860.2)] // CHECK:STDOUT: %A.lookup_impl_witness: = lookup_impl_witness %.Self.1, @A, @A(%CC) [symbolic = %A.lookup_impl_witness (constants.%A.lookup_impl_witness.04abbb.1)] // CHECK:STDOUT: %impl.elem0.loc11_31.1: type = impl_witness_access %A.lookup_impl_witness, element0 [symbolic = %impl.elem0.loc11_31.1 (constants.%impl.elem0.f51d29.1)] // CHECK:STDOUT: %A_where.type: type = facet_type <@A, @A(%CC) where %impl.elem0.loc11_31.1 = %CC> [symbolic = %A_where.type (constants.%A_where.type.4028e0.1)] // CHECK:STDOUT: %T.loc11_15.1: @C.F.%A_where.type (%A_where.type.4028e0.1) = symbolic_binding T, 1 [symbolic = %T.loc11_15.1 (constants.%T.6c0)] // CHECK:STDOUT: %pattern_type: type = pattern_type %A_where.type [symbolic = %pattern_type (constants.%pattern_type.1d5)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: // CHECK:STDOUT: fn() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic fn @D.G(@D.%DD.loc15_9.2: type, %T.loc16_8.2: type) { // CHECK:STDOUT: // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %DD: type = symbolic_binding DD, 0 [symbolic = %DD (constants.%DD)] // CHECK:STDOUT: %C.loc25_9.2: type = class_type @C, @C(%DD) [symbolic = %C.loc25_9.2 (constants.%C.5a3350.2)] // CHECK:STDOUT: %require_complete: = require_complete_type %C.loc25_9.2 [symbolic = %require_complete (constants.%require_complete.32c)] // CHECK:STDOUT: %C.F.type: type = fn_type @C.F, @C(%DD) [symbolic = %C.F.type (constants.%C.F.type.1f899e.2)] // CHECK:STDOUT: %C.F: @D.G.%C.F.type (%C.F.type.1f899e.2) = struct_value () [symbolic = %C.F (constants.%C.F.faeec9.2)] // CHECK:STDOUT: %A.type: type = facet_type <@A, @A(%DD)> [symbolic = %A.type (constants.%A.type.ade3d9.3)] // CHECK:STDOUT: %.Self.loc11: @D.G.%A.type (%A.type.ade3d9.3) = symbolic_binding .Self [symbolic = %.Self.loc11 (constants.%.Self.660472.2)] // CHECK:STDOUT: %A.lookup_impl_witness: = lookup_impl_witness %.Self.loc11, @A, @A(%DD) [symbolic = %A.lookup_impl_witness (constants.%A.lookup_impl_witness.04abbb.2)] // CHECK:STDOUT: %impl.elem0: type = impl_witness_access %A.lookup_impl_witness, element0 [symbolic = %impl.elem0 (constants.%impl.elem0.f51d29.2)] // CHECK:STDOUT: // CHECK:STDOUT: fn() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %C.ref: %C.type = name_ref C, file.%C.decl [concrete = constants.%C.generic] // CHECK:STDOUT: %DD.ref: type = name_ref DD, @D.%DD.loc15_9.2 [symbolic = %DD (constants.%DD)] // CHECK:STDOUT: %C.loc25_9.1: type = class_type @C, @C(constants.%DD) [symbolic = %C.loc25_9.2 (constants.%C.5a3350.2)] // CHECK:STDOUT: %.loc25: @D.G.%C.F.type (%C.F.type.1f899e.2) = specific_constant @C.%C.F.decl, @C(constants.%DD) [symbolic = %C.F (constants.%C.F.faeec9.2)] // CHECK:STDOUT: %F.ref: @D.G.%C.F.type (%C.F.type.1f899e.2) = name_ref F, %.loc25 [symbolic = %C.F (constants.%C.F.faeec9.2)] // CHECK:STDOUT: %T.ref: type = name_ref T, %T.loc16_8.2 [symbolic = %T.loc16_8.1 (constants.%T.091)] // CHECK:STDOUT: // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @C(constants.%CC) { // CHECK:STDOUT: %CC.loc6_9.1 => constants.%CC // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %C.F.type => constants.%C.F.type.1f899e.1 // CHECK:STDOUT: %C.F => constants.%C.F.faeec9.1 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @C.F(constants.%CC, constants.%T.6c0) { // CHECK:STDOUT: %CC => constants.%CC // CHECK:STDOUT: %A.type.loc11_23.1 => constants.%A.type.ade3d9.2 // CHECK:STDOUT: %.Self.1 => constants.%.Self.660472.1 // CHECK:STDOUT: %require_complete => constants.%require_complete.f58 // CHECK:STDOUT: %.Self.binding.as_type => constants.%.Self.binding.as_type // CHECK:STDOUT: %A.assoc_type => constants.%A.assoc_type.9f4820.2 // CHECK:STDOUT: %assoc0 => constants.%assoc0.9fa860.2 // CHECK:STDOUT: %A.lookup_impl_witness => constants.%A.lookup_impl_witness.04abbb.1 // CHECK:STDOUT: %impl.elem0.loc11_31.1 => constants.%impl.elem0.f51d29.1 // CHECK:STDOUT: %A_where.type => constants.%A_where.type.4028e0.1 // CHECK:STDOUT: %T.loc11_15.1 => constants.%T.6c0 // CHECK:STDOUT: %pattern_type => constants.%pattern_type.1d5 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @D(constants.%DD) { // CHECK:STDOUT: %DD.loc15_9.1 => constants.%DD // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %D.G.type => constants.%D.G.type.49d // CHECK:STDOUT: %D.G => constants.%D.G.891 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @D.G(constants.%DD, constants.%T.091) { // CHECK:STDOUT: %T.loc16_8.1 => constants.%T.091 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @C(constants.%DD) { // CHECK:STDOUT: %CC.loc6_9.1 => constants.%DD // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %C.F.type => constants.%C.F.type.1f899e.2 // CHECK:STDOUT: %C.F => constants.%C.F.faeec9.2 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @D(constants.%empty_struct_type) { // CHECK:STDOUT: %DD.loc15_9.1 => constants.%empty_struct_type // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %D.G.type => constants.%D.G.type.235 // CHECK:STDOUT: %D.G => constants.%D.G.be1 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @D.G(constants.%empty_struct_type, constants.%T.67d) { // CHECK:STDOUT: %T.loc16_8.1 => constants.%T.67d // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %DD => constants.%empty_struct_type // CHECK:STDOUT: %C.loc25_9.2 => constants.%C.850 // CHECK:STDOUT: %require_complete => constants.%complete_type // CHECK:STDOUT: %C.F.type => constants.%C.F.type.3ff // CHECK:STDOUT: %C.F => constants.%C.F.240 // CHECK:STDOUT: %A.type => constants.%A.type.69e // CHECK:STDOUT: %.Self.loc11 => constants.%.Self.f50 // CHECK:STDOUT: %A.lookup_impl_witness => constants.%A.lookup_impl_witness.219 // CHECK:STDOUT: %impl.elem0 => constants.%impl.elem0.d97 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @C(constants.%empty_struct_type) { // CHECK:STDOUT: %CC.loc6_9.1 => constants.%empty_struct_type // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %C.F.type => constants.%C.F.type.3ff // CHECK:STDOUT: %C.F => constants.%C.F.240 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- dot_self_symbolic_in_impl_lookup.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %pattern_type.98f: type = pattern_type type [concrete] // CHECK:STDOUT: %AA: type = symbolic_binding AA, 0 [symbolic] // CHECK:STDOUT: %A.type.464: type = generic_interface_type @A [concrete] // CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] // CHECK:STDOUT: %A.generic: %A.type.464 = struct_value () [concrete] // CHECK:STDOUT: %A.type.ade3d9.1: type = facet_type <@A, @A(%AA)> [symbolic] // CHECK:STDOUT: %A.assoc_type: type = assoc_entity_type @A, @A(%AA) [symbolic] // CHECK:STDOUT: %assoc0: %A.assoc_type = assoc_entity element0, @A.WithSelf.%X [symbolic] // CHECK:STDOUT: %B.type.5ae: type = generic_interface_type @B [concrete] // CHECK:STDOUT: %B.generic: %B.type.5ae = struct_value () [concrete] // CHECK:STDOUT: %.Self.660472.1: %A.type.ade3d9.1 = symbolic_binding .Self [symbolic] // CHECK:STDOUT: %require_complete.f58: = require_complete_type %A.type.ade3d9.1 [symbolic] // CHECK:STDOUT: %.Self.binding.as_type: type = symbolic_binding_type .Self, %.Self.660472.1 [symbolic] // CHECK:STDOUT: %A.lookup_impl_witness.04abbb.1: = lookup_impl_witness %.Self.660472.1, @A, @A(%AA) [symbolic] // CHECK:STDOUT: %impl.elem0.f51d29.1: type = impl_witness_access %A.lookup_impl_witness.04abbb.1, element0 [symbolic] // CHECK:STDOUT: %empty_tuple: %empty_tuple.type = tuple_value () [concrete] // CHECK:STDOUT: %A_where.type.29f165.1: type = facet_type <@A, @A(%AA) where %impl.elem0.f51d29.1 = %empty_tuple.type> [symbolic] // CHECK:STDOUT: %pattern_type.12a: type = pattern_type %A_where.type.29f165.1 [symbolic] // CHECK:STDOUT: %BB.0b1: %A_where.type.29f165.1 = symbolic_binding BB, 1 [symbolic] // CHECK:STDOUT: %BB.binding.as_type: type = symbolic_binding_type BB, 1, %BB.0b1 [symbolic] // CHECK:STDOUT: %ptr.e8f8f9.1: type = ptr_type %AA [symbolic] // CHECK:STDOUT: %B.type.abcc5d.1: type = facet_type <@B, @B(%ptr.e8f8f9.1)> [symbolic] // CHECK:STDOUT: %B.impl_witness.bef: = impl_witness @BB.binding.as_type.as.B.impl.%B.impl_witness_table, @BB.binding.as_type.as.B.impl(%AA, %BB.0b1) [symbolic] // CHECK:STDOUT: %require_complete.fe18c9.1: = require_complete_type %B.type.abcc5d.1 [symbolic] // CHECK:STDOUT: %DD: type = symbolic_binding DD, 0 [symbolic] // CHECK:STDOUT: %D.bd6: type = class_type @D, @D(%DD) [symbolic] // CHECK:STDOUT: %T.091: type = symbolic_binding T, 1 [symbolic] // CHECK:STDOUT: %D.G.type.49d: type = fn_type @D.G, @D(%DD) [symbolic] // CHECK:STDOUT: %D.G.891: %D.G.type.49d = struct_value () [symbolic] // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %ptr.e8f8f9.2: type = ptr_type %DD [symbolic] // CHECK:STDOUT: %B.type.abcc5d.2: type = facet_type <@B, @B(%ptr.e8f8f9.2)> [symbolic] // CHECK:STDOUT: %.77d: require_specific_def_type = require_specific_def @BB.as.B.impl(%ptr.e8f8f9.2, %T.091) [symbolic] // CHECK:STDOUT: %B.lookup_impl_witness.52c: = lookup_impl_witness %T.091, @B, @B(%ptr.e8f8f9.2) [symbolic] // CHECK:STDOUT: %B.facet.b5c: %B.type.abcc5d.2 = facet_value %T.091, (%B.lookup_impl_witness.52c) [symbolic] // CHECK:STDOUT: %T.67d: type = symbolic_binding T, 0 [symbolic] // CHECK:STDOUT: %D.G.type.235: type = fn_type @D.G, @D(%empty_struct_type) [concrete] // CHECK:STDOUT: %D.G.be1: %D.G.type.235 = struct_value () [concrete] // CHECK:STDOUT: %ptr.c28: type = ptr_type %empty_struct_type [concrete] // CHECK:STDOUT: %B.type.a69: type = facet_type <@B, @B(%ptr.c28)> [concrete] // CHECK:STDOUT: %.5ef: require_specific_def_type = require_specific_def @BB.as.B.impl(%ptr.c28, %T.67d) [symbolic] // CHECK:STDOUT: %B.lookup_impl_witness.eb0: = lookup_impl_witness %T.67d, @B, @B(%ptr.c28) [symbolic] // CHECK:STDOUT: %B.facet.4b0: %B.type.a69 = facet_value %T.67d, (%B.lookup_impl_witness.eb0) [symbolic] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: impl_decl @BB.binding.as_type.as.B.impl [concrete] { // CHECK:STDOUT: %AA.patt: %pattern_type.98f = symbolic_binding_pattern AA, 0 [concrete] // CHECK:STDOUT: %BB.patt: @BB.binding.as_type.as.B.impl.%pattern_type (%pattern_type.12a) = symbolic_binding_pattern BB, 1 [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %BB.ref: @BB.binding.as_type.as.B.impl.%A_where.type (%A_where.type.29f165.1) = name_ref BB, %BB.loc9_25.1 [symbolic = %BB.loc9_25.2 (constants.%BB.0b1)] // CHECK:STDOUT: %BB.as_type: type = facet_access_type %BB.ref [symbolic = %BB.binding.as_type (constants.%BB.binding.as_type)] // CHECK:STDOUT: %.loc9_51: type = converted %BB.ref, %BB.as_type [symbolic = %BB.binding.as_type (constants.%BB.binding.as_type)] // CHECK:STDOUT: %B.ref: %B.type.5ae = name_ref B, file.%B.decl [concrete = constants.%B.generic] // CHECK:STDOUT: %AA.ref.loc9_59: type = name_ref AA, %AA.loc9_14.1 [symbolic = %AA.loc9_14.2 (constants.%AA)] // CHECK:STDOUT: %ptr.loc9_61.1: type = ptr_type %AA.ref.loc9_59 [symbolic = %ptr.loc9_61.2 (constants.%ptr.e8f8f9.1)] // CHECK:STDOUT: %B.type.loc9_62.1: type = facet_type <@B, @B(constants.%ptr.e8f8f9.1)> [symbolic = %B.type.loc9_62.2 (constants.%B.type.abcc5d.1)] // CHECK:STDOUT: %.loc9_19.1: type = splice_block %.loc9_19.2 [concrete = type] { // CHECK:STDOUT: // CHECK:STDOUT: %.loc9_19.2: type = type_literal type [concrete = type] // CHECK:STDOUT: } // CHECK:STDOUT: %AA.loc9_14.1: type = symbolic_binding AA, 0 [symbolic = %AA.loc9_14.2 (constants.%AA)] // CHECK:STDOUT: %.loc9_36.1: type = splice_block %.loc9_36.2 [symbolic = %A_where.type (constants.%A_where.type.29f165.1)] { // CHECK:STDOUT: // CHECK:STDOUT: %A.ref: %A.type.464 = name_ref A, file.%A.decl [concrete = constants.%A.generic] // CHECK:STDOUT: %AA.ref.loc9_32: type = name_ref AA, %AA.loc9_14.1 [symbolic = %AA.loc9_14.2 (constants.%AA)] // CHECK:STDOUT: %A.type.loc9_34.1: type = facet_type <@A, @A(constants.%AA)> [symbolic = %A.type.loc9_34.2 (constants.%A.type.ade3d9.1)] // CHECK:STDOUT: // CHECK:STDOUT: %.Self.ref: @BB.binding.as_type.as.B.impl.%A.type.loc9_34.2 (%A.type.ade3d9.1) = name_ref .Self, %.Self.2 [symbolic = %.Self.4 (constants.%.Self.660472.1)] // CHECK:STDOUT: %.Self.as_type: type = facet_access_type %.Self.ref [symbolic = %.Self.binding.as_type (constants.%.Self.binding.as_type)] // CHECK:STDOUT: %.loc9_42.1: type = converted %.Self.ref, %.Self.as_type [symbolic = %.Self.binding.as_type (constants.%.Self.binding.as_type)] // CHECK:STDOUT: %.loc9_42.2: @BB.binding.as_type.as.B.impl.%A.assoc_type (%A.assoc_type) = specific_constant @X.%assoc0, @A.WithSelf(constants.%AA, constants.%.Self.660472.1) [symbolic = %assoc0 (constants.%assoc0)] // CHECK:STDOUT: %X.ref: @BB.binding.as_type.as.B.impl.%A.assoc_type (%A.assoc_type) = name_ref X, %.loc9_42.2 [symbolic = %assoc0 (constants.%assoc0)] // CHECK:STDOUT: %impl.elem0.loc9_42.1: type = impl_witness_access constants.%A.lookup_impl_witness.04abbb.1, element0 [symbolic = %impl.elem0.loc9_42.2 (constants.%impl.elem0.f51d29.1)] // CHECK:STDOUT: %.loc9_48.1: %empty_tuple.type = tuple_literal () [concrete = constants.%empty_tuple] // CHECK:STDOUT: %.loc9_48.2: type = converted %.loc9_48.1, constants.%empty_tuple.type [concrete = constants.%empty_tuple.type] // CHECK:STDOUT: %.loc9_36.2: type = where_expr %.Self.2 [symbolic = %A_where.type (constants.%A_where.type.29f165.1)] { // CHECK:STDOUT: requirement_base_facet_type constants.%A.type.ade3d9.1 // CHECK:STDOUT: requirement_rewrite %impl.elem0.loc9_42.1, %.loc9_48.2 // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: %BB.loc9_25.1: @BB.binding.as_type.as.B.impl.%A_where.type (%A_where.type.29f165.1) = symbolic_binding BB, 1 [symbolic = %BB.loc9_25.2 (constants.%BB.0b1)] // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic impl @BB.binding.as_type.as.B.impl(%AA.loc9_14.1: type, %BB.loc9_25.1: @BB.binding.as_type.as.B.impl.%A_where.type (%A_where.type.29f165.1)) { // CHECK:STDOUT: %AA.loc9_14.2: type = symbolic_binding AA, 0 [symbolic = %AA.loc9_14.2 (constants.%AA)] // CHECK:STDOUT: %A.type.loc9_34.2: type = facet_type <@A, @A(%AA.loc9_14.2)> [symbolic = %A.type.loc9_34.2 (constants.%A.type.ade3d9.1)] // CHECK:STDOUT: // CHECK:STDOUT: %require_complete.loc9_42: = require_complete_type %A.type.loc9_34.2 [symbolic = %require_complete.loc9_42 (constants.%require_complete.f58)] // CHECK:STDOUT: %.Self.binding.as_type: type = symbolic_binding_type .Self, %.Self.4 [symbolic = %.Self.binding.as_type (constants.%.Self.binding.as_type)] // CHECK:STDOUT: %A.assoc_type: type = assoc_entity_type @A, @A(%AA.loc9_14.2) [symbolic = %A.assoc_type (constants.%A.assoc_type)] // CHECK:STDOUT: %assoc0: @BB.binding.as_type.as.B.impl.%A.assoc_type (%A.assoc_type) = assoc_entity element0, @A.WithSelf.%X [symbolic = %assoc0 (constants.%assoc0)] // CHECK:STDOUT: %A.lookup_impl_witness: = lookup_impl_witness %.Self.4, @A, @A(%AA.loc9_14.2) [symbolic = %A.lookup_impl_witness (constants.%A.lookup_impl_witness.04abbb.1)] // CHECK:STDOUT: %impl.elem0.loc9_42.2: type = impl_witness_access %A.lookup_impl_witness, element0 [symbolic = %impl.elem0.loc9_42.2 (constants.%impl.elem0.f51d29.1)] // CHECK:STDOUT: %A_where.type: type = facet_type <@A, @A(%AA.loc9_14.2) where %impl.elem0.loc9_42.2 = constants.%empty_tuple.type> [symbolic = %A_where.type (constants.%A_where.type.29f165.1)] // CHECK:STDOUT: %BB.loc9_25.2: @BB.binding.as_type.as.B.impl.%A_where.type (%A_where.type.29f165.1) = symbolic_binding BB, 1 [symbolic = %BB.loc9_25.2 (constants.%BB.0b1)] // CHECK:STDOUT: %pattern_type: type = pattern_type %A_where.type [symbolic = %pattern_type (constants.%pattern_type.12a)] // CHECK:STDOUT: %BB.binding.as_type: type = symbolic_binding_type BB, 1, %BB.loc9_25.2 [symbolic = %BB.binding.as_type (constants.%BB.binding.as_type)] // CHECK:STDOUT: %ptr.loc9_61.2: type = ptr_type %AA.loc9_14.2 [symbolic = %ptr.loc9_61.2 (constants.%ptr.e8f8f9.1)] // CHECK:STDOUT: %B.type.loc9_62.2: type = facet_type <@B, @B(%ptr.loc9_61.2)> [symbolic = %B.type.loc9_62.2 (constants.%B.type.abcc5d.1)] // CHECK:STDOUT: %B.impl_witness.loc9_64.2: = impl_witness %B.impl_witness_table, @BB.binding.as_type.as.B.impl(%AA.loc9_14.2, %BB.loc9_25.2) [symbolic = %B.impl_witness.loc9_64.2 (constants.%B.impl_witness.bef)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete.loc9_62: = require_complete_type %B.type.loc9_62.2 [symbolic = %require_complete.loc9_62 (constants.%require_complete.fe18c9.1)] // CHECK:STDOUT: // CHECK:STDOUT: impl: %.loc9_51 as %B.type.loc9_62.1 { // CHECK:STDOUT: %B.impl_witness_table = impl_witness_table (), @BB.binding.as_type.as.B.impl [concrete] // CHECK:STDOUT: %B.impl_witness.loc9_64.1: = impl_witness %B.impl_witness_table, @BB.binding.as_type.as.B.impl(constants.%AA, constants.%BB.0b1) [symbolic = %B.impl_witness.loc9_64.2 (constants.%B.impl_witness.bef)] // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: witness = %B.impl_witness.loc9_64.1 // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic class @D(%DD.loc14_9.2: type) { // CHECK:STDOUT: // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: // CHECK:STDOUT: // CHECK:STDOUT: class { // CHECK:STDOUT: // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%D.bd6 // CHECK:STDOUT: .G = %D.G.decl // CHECK:STDOUT: .B = // CHECK:STDOUT: .DD = // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic fn @D.G(@D.%DD.loc14_9.2: type, %T.loc15_8.2: type) { // CHECK:STDOUT: // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %DD: type = symbolic_binding DD, 0 [symbolic = %DD (constants.%DD)] // CHECK:STDOUT: %ptr.loc21_14.2: type = ptr_type %DD [symbolic = %ptr.loc21_14.2 (constants.%ptr.e8f8f9.2)] // CHECK:STDOUT: %B.type.loc21_15.2: type = facet_type <@B, @B(%ptr.loc21_14.2)> [symbolic = %B.type.loc21_15.2 (constants.%B.type.abcc5d.2)] // CHECK:STDOUT: %.loc21_7.2: require_specific_def_type = require_specific_def @BB.as.B.impl(%ptr.loc21_14.2, %T.loc15_8.1) [symbolic = %.loc21_7.2 (constants.%.77d)] // CHECK:STDOUT: %B.lookup_impl_witness: = lookup_impl_witness %T.loc15_8.1, @B, @B(%ptr.loc21_14.2) [symbolic = %B.lookup_impl_witness (constants.%B.lookup_impl_witness.52c)] // CHECK:STDOUT: %B.facet.loc21_7.2: @D.G.%B.type.loc21_15.2 (%B.type.abcc5d.2) = facet_value %T.loc15_8.1, (%B.lookup_impl_witness) [symbolic = %B.facet.loc21_7.2 (constants.%B.facet.b5c)] // CHECK:STDOUT: // CHECK:STDOUT: fn() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %T.ref: type = name_ref T, %T.loc15_8.2 [symbolic = %T.loc15_8.1 (constants.%T.091)] // CHECK:STDOUT: %B.ref: %B.type.5ae = name_ref B, file.%B.decl [concrete = constants.%B.generic] // CHECK:STDOUT: %DD.ref: type = name_ref DD, @D.%DD.loc14_9.2 [symbolic = %DD (constants.%DD)] // CHECK:STDOUT: %ptr.loc21_14.1: type = ptr_type %DD.ref [symbolic = %ptr.loc21_14.2 (constants.%ptr.e8f8f9.2)] // CHECK:STDOUT: %B.type.loc21_15.1: type = facet_type <@B, @B(constants.%ptr.e8f8f9.2)> [symbolic = %B.type.loc21_15.2 (constants.%B.type.abcc5d.2)] // CHECK:STDOUT: %B.facet.loc21_7.1: @D.G.%B.type.loc21_15.2 (%B.type.abcc5d.2) = facet_value %T.ref, (constants.%B.lookup_impl_witness.52c) [symbolic = %B.facet.loc21_7.2 (constants.%B.facet.b5c)] // CHECK:STDOUT: %.loc21_7.1: @D.G.%B.type.loc21_15.2 (%B.type.abcc5d.2) = converted %T.ref, %B.facet.loc21_7.1 [symbolic = %B.facet.loc21_7.2 (constants.%B.facet.b5c)] // CHECK:STDOUT: // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @BB.binding.as_type.as.B.impl(constants.%AA, constants.%BB.0b1) { // CHECK:STDOUT: %AA.loc9_14.2 => constants.%AA // CHECK:STDOUT: %A.type.loc9_34.2 => constants.%A.type.ade3d9.1 // CHECK:STDOUT: %.Self.4 => constants.%.Self.660472.1 // CHECK:STDOUT: %require_complete.loc9_42 => constants.%require_complete.f58 // CHECK:STDOUT: %.Self.binding.as_type => constants.%.Self.binding.as_type // CHECK:STDOUT: %A.assoc_type => constants.%A.assoc_type // CHECK:STDOUT: %assoc0 => constants.%assoc0 // CHECK:STDOUT: %A.lookup_impl_witness => constants.%A.lookup_impl_witness.04abbb.1 // CHECK:STDOUT: %impl.elem0.loc9_42.2 => constants.%impl.elem0.f51d29.1 // CHECK:STDOUT: %A_where.type => constants.%A_where.type.29f165.1 // CHECK:STDOUT: %BB.loc9_25.2 => constants.%BB.0b1 // CHECK:STDOUT: %pattern_type => constants.%pattern_type.12a // CHECK:STDOUT: %BB.binding.as_type => constants.%BB.binding.as_type // CHECK:STDOUT: %ptr.loc9_61.2 => constants.%ptr.e8f8f9.1 // CHECK:STDOUT: %B.type.loc9_62.2 => constants.%B.type.abcc5d.1 // CHECK:STDOUT: %B.impl_witness.loc9_64.2 => constants.%B.impl_witness.bef // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @D(constants.%DD) { // CHECK:STDOUT: %DD.loc14_9.1 => constants.%DD // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %D.G.type => constants.%D.G.type.49d // CHECK:STDOUT: %D.G => constants.%D.G.891 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @D.G(constants.%DD, constants.%T.091) { // CHECK:STDOUT: %T.loc15_8.1 => constants.%T.091 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @D(constants.%empty_struct_type) { // CHECK:STDOUT: %DD.loc14_9.1 => constants.%empty_struct_type // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %D.G.type => constants.%D.G.type.235 // CHECK:STDOUT: %D.G => constants.%D.G.be1 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @D.G(constants.%empty_struct_type, constants.%T.67d) { // CHECK:STDOUT: %T.loc15_8.1 => constants.%T.67d // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %DD => constants.%empty_struct_type // CHECK:STDOUT: %ptr.loc21_14.2 => constants.%ptr.c28 // CHECK:STDOUT: %B.type.loc21_15.2 => constants.%B.type.a69 // CHECK:STDOUT: %.loc21_7.2 => constants.%.5ef // CHECK:STDOUT: %B.lookup_impl_witness => constants.%B.lookup_impl_witness.eb0 // CHECK:STDOUT: %B.facet.loc21_7.2 => constants.%B.facet.4b0 // CHECK:STDOUT: } // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/generic/extend_type_completion.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/int.carbon // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/generic/extend_type_completion.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/generic/extend_type_completion.carbon // --- class_impl_doesnt_need_complete_interface.carbon library "[[@TEST_NAME]]"; interface K(T:! type) {} class C(N:! i32) { impl as K(array(i32, N)) {} } // C does not extend K so the type of K is not completed. No error. fn F(unused v: C(-1)) {} // --- fail_class_extend_impl_does_need_complete_interface.carbon library "[[@TEST_NAME]]"; interface K(T:! type) {} class C(N:! i32) { extend impl as K(array(i32, N)) {} } // C extends K so the type of K is completed, but is invalid. // CHECK:STDERR: fail_class_extend_impl_does_need_complete_interface.carbon:[[@LINE+7]]:13: error: parameter has incomplete type `C(-1)` in function definition [IncompleteTypeInFunctionParam] // CHECK:STDERR: fn F(unused v: C(-1)) {} // CHECK:STDERR: ^~~~~~~~ // CHECK:STDERR: fail_class_extend_impl_does_need_complete_interface.carbon:[[@LINE-7]]:18: note: array bound of -1 is negative [ArrayBoundNegative] // CHECK:STDERR: extend impl as K(array(i32, N)) {} // CHECK:STDERR: ^~~~~~~~~~~~~~~~ // CHECK:STDERR: fn F(unused v: C(-1)) {} // --- interface_require_impls_doesnt_need_complete_interface.carbon library "[[@TEST_NAME]]"; interface K(T:! type) {} interface J(N:! i32) { require impls K(array(i32, N)); } // J does not extend K so the type of K is not completed. No error. fn F(unused v: J(-1)) {} // --- fail_interface_extend_require_impls_does_need_complete_interface.carbon library "[[@TEST_NAME]]"; interface K(T:! type) {} interface J(N:! i32) { extend require impls K(array(i32, N)); } interface I(N:! i32) { extend require impls J(N); } // I extends J extends K so the type of K is completed, but is invalid. // // TODO: The error location should be the type, like in the class case above. We // need a location for the type in context.bind_name_map() to use as the // location to Convert(). // // CHECK:STDERR: fail_interface_extend_require_impls_does_need_complete_interface.carbon:[[@LINE+10]]:13: error: parameter has incomplete type `I(-1)` in function definition [IncompleteTypeInFunctionParam] // CHECK:STDERR: fn F(unused v: I(-1)) {} // CHECK:STDERR: ^~~~~~~~ // CHECK:STDERR: fail_interface_extend_require_impls_does_need_complete_interface.carbon:[[@LINE-12]]:24: note: `J(N)` evaluates to incomplete type `J(-1)` [IncompleteTypeInMonomorphization] // CHECK:STDERR: extend require impls J(N); // CHECK:STDERR: ^~~~ // CHECK:STDERR: fail_interface_extend_require_impls_does_need_complete_interface.carbon:[[@LINE-18]]:24: note: array bound of -1 is negative [ArrayBoundNegative] // CHECK:STDERR: extend require impls K(array(i32, N)); // CHECK:STDERR: ^~~~~~~~~~~~~~~~ // CHECK:STDERR: fn F(unused v: I(-1)) {} // --- constraint_require_impls_doesnt_need_complete_interface.carbon library "[[@TEST_NAME]]"; interface K(T:! type) {} constraint J(N:! i32) { require impls K(array(i32, N)); } // J does not extend K so the type of K is not completed. No error. fn F(unused v: J(-1)) {} // --- fail_constraint_extend_require_impls_does_need_complete_interface.carbon library "[[@TEST_NAME]]"; interface K(T:! type) {} constraint J(N:! i32) { extend require impls K(array(i32, N)); } constraint I(N:! i32) { extend require impls J(N); } // I extends J extends K so the type of K is completed, but is invalid. // CHECK:STDERR: fail_constraint_extend_require_impls_does_need_complete_interface.carbon:[[@LINE+10]]:13: error: parameter has incomplete type `I(-1)` in function definition [IncompleteTypeInFunctionParam] // CHECK:STDERR: fn F(unused v: I(-1)) {} // CHECK:STDERR: ^~~~~~~~ // CHECK:STDERR: fail_constraint_extend_require_impls_does_need_complete_interface.carbon:[[@LINE-7]]:24: note: `J(N)` evaluates to incomplete type `J(-1)` [IncompleteTypeInMonomorphization] // CHECK:STDERR: extend require impls J(N); // CHECK:STDERR: ^~~~ // CHECK:STDERR: fail_constraint_extend_require_impls_does_need_complete_interface.carbon:[[@LINE-13]]:24: note: array bound of -1 is negative [ArrayBoundNegative] // CHECK:STDERR: extend require impls K(array(i32, N)); // CHECK:STDERR: ^~~~~~~~~~~~~~~~ // CHECK:STDERR: fn F(unused v: I(-1)) {} // --- interface_require_impls_doesnt_need_complete_self.carbon library "[[@TEST_NAME]]"; interface K {} interface J(N:! i32) { require array(Self, N) impls K; } // J does not extend K so the type of Self impling K is not completed. No error. fn F(unused v: J(-1)) {} // --- constraint_require_impls_doesnt_need_complete_self.carbon library "[[@TEST_NAME]]"; interface K {} constraint J(N:! i32) { require array(Self, N) impls K; } // J does not extend K so the type of Self impling K is not completed. No error. fn F(unused v: J(-1)) {} ================================================ FILE: toolchain/check/testdata/generic/fail_generic_copy.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/convert.carbon // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/generic/fail_generic_copy.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/generic/fail_generic_copy.carbon // --- fail_return.carbon library "[[@TEST_NAME]]"; fn F[T:! type](x: T) -> T { // CHECK:STDERR: fail_return.carbon:[[@LINE+7]]:10: error: cannot copy value of type `T` [CopyOfUncopyableType] // CHECK:STDERR: return x; // CHECK:STDERR: ^ // CHECK:STDERR: fail_return.carbon:[[@LINE+4]]:10: note: type `T` does not implement interface `Core.Copy` [MissingImplInMemberAccessInContext] // CHECK:STDERR: return x; // CHECK:STDERR: ^ // CHECK:STDERR: return x; } // --- fail_var.carbon library "[[@TEST_NAME]]"; fn F[T:! Core.Destroy](x: T) { // CHECK:STDERR: fail_var.carbon:[[@LINE+7]]:21: error: cannot copy value of type `T` [CopyOfUncopyableType] // CHECK:STDERR: var unused y: T = x; // CHECK:STDERR: ^ // CHECK:STDERR: fail_var.carbon:[[@LINE+4]]:21: note: type `T` does not implement interface `Core.Copy` [MissingImplInMemberAccessInContext] // CHECK:STDERR: var unused y: T = x; // CHECK:STDERR: ^ // CHECK:STDERR: var unused y: T = x; } ================================================ FILE: toolchain/check/testdata/generic/forward_decl.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/destroy.carbon // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/generic/forward_decl.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/generic/forward_decl.carbon interface Z {} class C {} impl C as Z {} fn G[T:! Z](p: T); fn F() { G({} as C); } fn G[T:! Z](unused p: T) {} ================================================ FILE: toolchain/check/testdata/generic/identify_specific_facet_type.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/int.carbon // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/generic/identify_specific_facet_type.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/generic/identify_specific_facet_type.carbon // --- fail_error_in_identifying_require_facet_type.carbon library "[[@TEST_NAME]]"; // Identification tries to form the specific interface `K(array(i32, -1))` which // is invalid, so the impl as a whole is invalid. interface K(T:! type) {} constraint L(N:! i32) { require impls K(array(i32, N)); } constraint J(N:! i32) { require impls L(N); } // CHECK:STDERR: fail_error_in_identifying_require_facet_type.carbon:[[@LINE+7]]:1: error: facet type `J(-1)` cannot be identified in `impl as` [ImplOfUnidentifiedFacetType] // CHECK:STDERR: impl {} as J(-1) {} // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~ // CHECK:STDERR: fail_error_in_identifying_require_facet_type.carbon:[[@LINE-9]]:30: note: array bound of -1 is negative [ArrayBoundNegative] // CHECK:STDERR: require impls K(array(i32, N)); // CHECK:STDERR: ^ // CHECK:STDERR: impl {} as J(-1) {} // --- fail_error_in_identifying_extend_require_facet_type.carbon library "[[@TEST_NAME]]"; // Identification tries to form the specific interface `K(array(i32, -1))` which // is invalid, so the impl as a whole is invalid. interface K(T:! type) {} constraint J(N:! i32) { extend require impls K(array(i32, N)); } // CHECK:STDERR: fail_error_in_identifying_extend_require_facet_type.carbon:[[@LINE+7]]:1: error: facet type `J(-1)` cannot be identified in `impl as` [ImplOfUnidentifiedFacetType] // CHECK:STDERR: impl {} as J(-1) {} // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~ // CHECK:STDERR: fail_error_in_identifying_extend_require_facet_type.carbon:[[@LINE-6]]:24: note: array bound of -1 is negative [ArrayBoundNegative] // CHECK:STDERR: extend require impls K(array(i32, N)); // CHECK:STDERR: ^~~~~~~~~~~~~~~~ // CHECK:STDERR: impl {} as J(-1) {} ================================================ FILE: toolchain/check/testdata/generic/local.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/int.carbon // TODO: Add ranges and switch to "--dump-sem-ir-ranges=only". // EXTRA-ARGS: --dump-sem-ir-ranges=if-present // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/generic/local.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/generic/local.carbon // --- class.carbon library "[[@TEST_NAME]]"; fn F() { class C(T:! type) { var x: T; } var unused v: C(i32) = {.x = 1}; } // --- fail_param_shadows_class.carbon library "[[@TEST_NAME]]"; fn F() { // TODO: Decide on what behavior we want here. We don't reject the corresponding case outside of a function. // CHECK:STDERR: fail_param_shadows_class.carbon:[[@LINE+7]]:9: error: duplicate name `C` being declared in the same scope [NameDeclDuplicate] // CHECK:STDERR: class C(C:! type) { // CHECK:STDERR: ^ // CHECK:STDERR: fail_param_shadows_class.carbon:[[@LINE+4]]:11: note: name is previously declared here [NameDeclPrevious] // CHECK:STDERR: class C(C:! type) { // CHECK:STDERR: ^ // CHECK:STDERR: class C(C:! type) { } } // --- nonlocal_param_shadows_class.carbon library "[[@TEST_NAME]]"; class C(C:! type) { } // CHECK:STDOUT: --- class.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %F.type: type = fn_type @F [concrete] // CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] // CHECK:STDOUT: %F: %F.type = struct_value () [concrete] // CHECK:STDOUT: %type: type = facet_type [concrete] // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self] // CHECK:STDOUT: %pattern_type.98f: type = pattern_type type [concrete] // CHECK:STDOUT: %T: type = symbolic_binding T, 0 [symbolic] // CHECK:STDOUT: %C.type: type = generic_class_type @C [concrete] // CHECK:STDOUT: %C.generic: %C.type = struct_value () [concrete] // CHECK:STDOUT: %C.5d4: type = class_type @C, @C(%T) [symbolic] // CHECK:STDOUT: %require_complete.944: = require_complete_type %T [symbolic] // CHECK:STDOUT: %C.elem.d15: type = unbound_element_type %C.5d4, %T [symbolic] // CHECK:STDOUT: %struct_type.x.0c5: type = struct_type {.x: %T} [symbolic] // CHECK:STDOUT: %complete_type.735: = complete_type_witness %struct_type.x.0c5 [symbolic] // CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [concrete] // CHECK:STDOUT: %Int.type: type = generic_class_type @Int [concrete] // CHECK:STDOUT: %Int.generic: %Int.type = struct_value () [concrete] // CHECK:STDOUT: %i32: type = class_type @Int, @Int(%int_32) [concrete] // CHECK:STDOUT: %C.d45: type = class_type @C, @C(%i32) [concrete] // CHECK:STDOUT: %i32.builtin: type = int_type signed, %int_32 [concrete] // CHECK:STDOUT: %complete_type.f8a: = complete_type_witness %i32.builtin [concrete] // CHECK:STDOUT: %C.elem.f74: type = unbound_element_type %C.d45, %i32 [concrete] // CHECK:STDOUT: %struct_type.x.ed6: type = struct_type {.x: %i32} [concrete] // CHECK:STDOUT: %complete_type.1ec: = complete_type_witness %struct_type.x.ed6 [concrete] // CHECK:STDOUT: %pattern_type.7db: type = pattern_type %C.d45 [concrete] // CHECK:STDOUT: %int_1.5b8: Core.IntLiteral = int_value 1 [concrete] // CHECK:STDOUT: %struct_type.x.c96: type = struct_type {.x: Core.IntLiteral} [concrete] // CHECK:STDOUT: %struct: %struct_type.x.c96 = struct_value (%int_1.5b8) [concrete] // CHECK:STDOUT: %ImplicitAs.type.cc7: type = generic_interface_type @ImplicitAs [concrete] // CHECK:STDOUT: %ImplicitAs.generic: %ImplicitAs.type.cc7 = struct_value () [concrete] // CHECK:STDOUT: %ImplicitAs.type.e8c: type = facet_type <@ImplicitAs, @ImplicitAs(%i32)> [concrete] // CHECK:STDOUT: %To: Core.IntLiteral = symbolic_binding To, 0 [symbolic] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.4e6: type = fn_type @Core.IntLiteral.as.ImplicitAs.impl.Convert, @Core.IntLiteral.as.ImplicitAs.impl(%To) [symbolic] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.3c2: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.4e6 = struct_value () [symbolic] // CHECK:STDOUT: %ImplicitAs.impl_witness.6bc: = impl_witness imports.%ImplicitAs.impl_witness_table.74f, @Core.IntLiteral.as.ImplicitAs.impl(%int_32) [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.e0d: type = fn_type @Core.IntLiteral.as.ImplicitAs.impl.Convert, @Core.IntLiteral.as.ImplicitAs.impl(%int_32) [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.e0d = struct_value () [concrete] // CHECK:STDOUT: %ImplicitAs.facet: %ImplicitAs.type.e8c = facet_value Core.IntLiteral, (%ImplicitAs.impl_witness.6bc) [concrete] // CHECK:STDOUT: %ImplicitAs.WithSelf.Convert.type.b37: type = fn_type @ImplicitAs.WithSelf.Convert, @ImplicitAs.WithSelf(%i32, %ImplicitAs.facet) [concrete] // CHECK:STDOUT: %.545: type = fn_type_with_self_type %ImplicitAs.WithSelf.Convert.type.b37, %ImplicitAs.facet [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.bound: = bound_method %int_1.5b8, %Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5 [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn: = specific_function %Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5, @Core.IntLiteral.as.ImplicitAs.impl.Convert(%int_32) [concrete] // CHECK:STDOUT: %bound_method: = bound_method %int_1.5b8, %Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn [concrete] // CHECK:STDOUT: %int_1.5d2: %i32 = int_value 1 [concrete] // CHECK:STDOUT: %C.val: %C.d45 = struct_value (%int_1.5d2) [concrete] // CHECK:STDOUT: %Destroy.type: type = facet_type <@Destroy> [concrete] // CHECK:STDOUT: %Destroy.Op.type: type = fn_type @Destroy.Op [concrete] // CHECK:STDOUT: %Destroy.Op: %Destroy.Op.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: .Int = %Core.Int // CHECK:STDOUT: .ImplicitAs = %Core.ImplicitAs // CHECK:STDOUT: .Destroy = %Core.Destroy // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: %Core.Int: %Int.type = import_ref Core//prelude/parts/int, Int, loaded [concrete = constants.%Int.generic] // CHECK:STDOUT: %Core.ImplicitAs: %ImplicitAs.type.cc7 = import_ref Core//prelude/parts/as, ImplicitAs, loaded [concrete = constants.%ImplicitAs.generic] // CHECK:STDOUT: %Core.import_ref.42d: @Core.IntLiteral.as.ImplicitAs.impl.%Core.IntLiteral.as.ImplicitAs.impl.Convert.type (%Core.IntLiteral.as.ImplicitAs.impl.Convert.type.4e6) = import_ref Core//prelude/parts/int, loc{{\d+_\d+}}, loaded [symbolic = @Core.IntLiteral.as.ImplicitAs.impl.%Core.IntLiteral.as.ImplicitAs.impl.Convert (constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.3c2)] // CHECK:STDOUT: %ImplicitAs.impl_witness_table.74f = impl_witness_table (%Core.import_ref.42d), @Core.IntLiteral.as.ImplicitAs.impl [concrete] // CHECK:STDOUT: %Core.Destroy: type = import_ref Core//prelude/parts/destroy, Destroy, loaded [concrete = constants.%Destroy.type] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .F = %F.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %F.decl: %F.type = fn_decl @F [concrete = constants.%F] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic class @C(%T.loc5_11.2: type) { // CHECK:STDOUT: %T.loc5_11.1: type = symbolic_binding T, 0 [symbolic = %T.loc5_11.1 (constants.%T)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete: = require_complete_type %T.loc5_11.1 [symbolic = %require_complete (constants.%require_complete.944)] // CHECK:STDOUT: %C: type = class_type @C, @C(%T.loc5_11.1) [symbolic = %C (constants.%C.5d4)] // CHECK:STDOUT: %C.elem: type = unbound_element_type %C, %T.loc5_11.1 [symbolic = %C.elem (constants.%C.elem.d15)] // CHECK:STDOUT: %struct_type.x: type = struct_type {.x: @C.%T.loc5_11.1 (%T)} [symbolic = %struct_type.x (constants.%struct_type.x.0c5)] // CHECK:STDOUT: %complete_type.loc7_3.2: = complete_type_witness %struct_type.x [symbolic = %complete_type.loc7_3.2 (constants.%complete_type.735)] // CHECK:STDOUT: // CHECK:STDOUT: class { // CHECK:STDOUT: %T.ref: type = name_ref T, %T.loc5_11.2 [symbolic = %T.loc5_11.1 (constants.%T)] // CHECK:STDOUT: %.loc6: @C.%C.elem (%C.elem.d15) = field_decl x, element0 [concrete] // CHECK:STDOUT: %complete_type.loc7_3.1: = complete_type_witness constants.%struct_type.x.0c5 [symbolic = %complete_type.loc7_3.2 (constants.%complete_type.735)] // CHECK:STDOUT: complete_type_witness = %complete_type.loc7_3.1 // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%C.5d4 // CHECK:STDOUT: .T = // CHECK:STDOUT: .x = %.loc6 // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @F() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %C.decl: %C.type = class_decl @C [concrete = constants.%C.generic] { // CHECK:STDOUT: %T.patt: %pattern_type.98f = symbolic_binding_pattern T, 0 [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %.loc5_15.1: type = splice_block %.loc5_15.2 [concrete = type] { // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %.loc5_15.2: type = type_literal type [concrete = type] // CHECK:STDOUT: } // CHECK:STDOUT: %T.loc5_11.2: type = symbolic_binding T, 0 [symbolic = %T.loc5_11.1 (constants.%T)] // CHECK:STDOUT: } // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %v.patt: %pattern_type.7db = ref_binding_pattern v [concrete] // CHECK:STDOUT: %v.var_patt: %pattern_type.7db = var_pattern %v.patt [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %v.var: ref %C.d45 = var %v.var_patt // CHECK:STDOUT: %int_1: Core.IntLiteral = int_value 1 [concrete = constants.%int_1.5b8] // CHECK:STDOUT: %.loc8_33.1: %struct_type.x.c96 = struct_literal (%int_1) [concrete = constants.%struct] // CHECK:STDOUT: %impl.elem0: %.545 = impl_witness_access constants.%ImplicitAs.impl_witness.6bc, element0 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5] // CHECK:STDOUT: %bound_method.loc8_33.1: = bound_method %int_1, %impl.elem0 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.bound] // CHECK:STDOUT: %specific_fn: = specific_function %impl.elem0, @Core.IntLiteral.as.ImplicitAs.impl.Convert(constants.%int_32) [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn] // CHECK:STDOUT: %bound_method.loc8_33.2: = bound_method %int_1, %specific_fn [concrete = constants.%bound_method] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.call: init %i32 = call %bound_method.loc8_33.2(%int_1) [concrete = constants.%int_1.5d2] // CHECK:STDOUT: %.loc8_33.2: init %i32 = converted %int_1, %Core.IntLiteral.as.ImplicitAs.impl.Convert.call [concrete = constants.%int_1.5d2] // CHECK:STDOUT: %.loc8_33.3: ref %i32 = class_element_access %v.var, element0 // CHECK:STDOUT: %.loc8_33.4: init %i32 to %.loc8_33.3 = in_place_init %.loc8_33.2 [concrete = constants.%int_1.5d2] // CHECK:STDOUT: %.loc8_33.5: init %C.d45 to %v.var = class_init (%.loc8_33.4) [concrete = constants.%C.val] // CHECK:STDOUT: %.loc8_3: init %C.d45 = converted %.loc8_33.1, %.loc8_33.5 [concrete = constants.%C.val] // CHECK:STDOUT: assign %v.var, %.loc8_3 // CHECK:STDOUT: %.loc8_22: type = splice_block %C [concrete = constants.%C.d45] { // CHECK:STDOUT: %C.ref: %C.type = name_ref C, %C.decl [concrete = constants.%C.generic] // CHECK:STDOUT: %i32: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %C: type = class_type @C, @C(constants.%i32) [concrete = constants.%C.d45] // CHECK:STDOUT: } // CHECK:STDOUT: %v: ref %C.d45 = ref_binding v, %v.var // CHECK:STDOUT: %Destroy.Op.bound: = bound_method %v.var, constants.%Destroy.Op // CHECK:STDOUT: %Destroy.Op.call: init %empty_tuple.type = call %Destroy.Op.bound(%v.var) // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @Destroy.Op(%self.param: ref %C.d45) = "no_op"; // CHECK:STDOUT: // CHECK:STDOUT: specific @C(constants.%T) { // CHECK:STDOUT: %T.loc5_11.1 => constants.%T // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @C(constants.%i32) { // CHECK:STDOUT: %T.loc5_11.1 => constants.%i32 // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete => constants.%complete_type.f8a // CHECK:STDOUT: %C => constants.%C.d45 // CHECK:STDOUT: %C.elem => constants.%C.elem.f74 // CHECK:STDOUT: %struct_type.x => constants.%struct_type.x.ed6 // CHECK:STDOUT: %complete_type.loc7_3.2 => constants.%complete_type.1ec // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- fail_param_shadows_class.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %F.type: type = fn_type @F [concrete] // CHECK:STDOUT: %F: %F.type = struct_value () [concrete] // CHECK:STDOUT: %type: type = facet_type [concrete] // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self] // CHECK:STDOUT: %pattern_type: type = pattern_type type [concrete] // CHECK:STDOUT: %C.67d: type = symbolic_binding C, 0 [symbolic] // CHECK:STDOUT: %C.type: type = generic_class_type @C [concrete] // CHECK:STDOUT: %C.generic: %C.type = struct_value () [concrete] // CHECK:STDOUT: %C.5d4: type = class_type @C, @C(%C.67d) [symbolic] // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %complete_type: = complete_type_witness %empty_struct_type [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .F = %F.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %F.decl: %F.type = fn_decl @F [concrete = constants.%F] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic class @C(%C.loc13_11.2: type) { // CHECK:STDOUT: %C.loc13_11.1: type = symbolic_binding C, 0 [symbolic = %C.loc13_11.1 (constants.%C.67d)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: // CHECK:STDOUT: class { // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%C.5d4 // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @F() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %C.decl: %C.type = class_decl @C [concrete = constants.%C.generic] { // CHECK:STDOUT: %C.patt: %pattern_type = symbolic_binding_pattern C, 0 [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %.loc13_15.1: type = splice_block %.loc13_15.2 [concrete = type] { // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %.loc13_15.2: type = type_literal type [concrete = type] // CHECK:STDOUT: } // CHECK:STDOUT: %C.loc13_11.2: type = symbolic_binding C, 0 [symbolic = %C.loc13_11.1 (constants.%C.67d)] // CHECK:STDOUT: } // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @C(constants.%C.67d) { // CHECK:STDOUT: %C.loc13_11.1 => constants.%C.67d // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- nonlocal_param_shadows_class.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %type: type = facet_type [concrete] // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self] // CHECK:STDOUT: %pattern_type: type = pattern_type type [concrete] // CHECK:STDOUT: %C.67d: type = symbolic_binding C, 0 [symbolic] // CHECK:STDOUT: %C.type: type = generic_class_type @C [concrete] // CHECK:STDOUT: %C.generic: %C.type = struct_value () [concrete] // CHECK:STDOUT: %C.5a3: type = class_type @C, @C(%C.67d) [symbolic] // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %complete_type: = complete_type_witness %empty_struct_type [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .C = %C.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %C.decl: %C.type = class_decl @C [concrete = constants.%C.generic] { // CHECK:STDOUT: %C.patt: %pattern_type = symbolic_binding_pattern C, 0 [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %.loc4_13.1: type = splice_block %.loc4_13.2 [concrete = type] { // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %.loc4_13.2: type = type_literal type [concrete = type] // CHECK:STDOUT: } // CHECK:STDOUT: %C.loc4_9.2: type = symbolic_binding C, 0 [symbolic = %C.loc4_9.1 (constants.%C.67d)] // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic class @C(%C.loc4_9.2: type) { // CHECK:STDOUT: %C.loc4_9.1: type = symbolic_binding C, 0 [symbolic = %C.loc4_9.1 (constants.%C.67d)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: // CHECK:STDOUT: class { // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%C.5a3 // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @C(constants.%C.67d) { // CHECK:STDOUT: %C.loc4_9.1 => constants.%C.67d // CHECK:STDOUT: } // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/generic/template/convert.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/int.carbon // TODO: Add ranges and switch to "--dump-sem-ir-ranges=only". // EXTRA-ARGS: --dump-sem-ir-ranges=if-present // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/generic/template/convert.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/generic/template/convert.carbon // --- convert.carbon library "[[@TEST_NAME]]"; fn F[template T:! type](x: T) -> i32 { let n: i32 = x; return n; } fn Test1(n: i32) -> i32 { return F(n); } class C { var n: i32; impl as Core.ImplicitAs(i32) { fn Convert[self: Self]() -> i32 { return self.n; } } } fn Test2(c: C) -> i32 { return F(c); } // --- fail_cannot_convert.carbon library "[[@TEST_NAME]]"; fn F[template T:! type](x: T) -> i32 { let n: i32 = x; return n; } class D {} fn Test(d: D) -> i32 { // CHECK:STDERR: fail_cannot_convert.carbon:[[@LINE+10]]:10: error: unable to monomorphize specific `F(D)` [ResolvingSpecificHere] // CHECK:STDERR: return F(d); // CHECK:STDERR: ^ // CHECK:STDERR: fail_cannot_convert.carbon:[[@LINE-10]]:16: note: cannot implicitly convert expression of type `D` to `i32` [ConversionFailure] // CHECK:STDERR: let n: i32 = x; // CHECK:STDERR: ^ // CHECK:STDERR: fail_cannot_convert.carbon:[[@LINE-13]]:16: note: type `D` does not implement interface `Core.ImplicitAs(i32)` [MissingImplInMemberAccessInContext] // CHECK:STDERR: let n: i32 = x; // CHECK:STDERR: ^ // CHECK:STDERR: return F(d); } // CHECK:STDOUT: --- convert.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %type: type = facet_type [concrete] // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self] // CHECK:STDOUT: %pattern_type.98f: type = pattern_type type [concrete] // CHECK:STDOUT: %T.67db0b.1: type = symbolic_binding T, 0, template [template] // CHECK:STDOUT: %pattern_type.51d1c4.1: type = pattern_type %T.67db0b.1 [template] // CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [concrete] // CHECK:STDOUT: %Int.type: type = generic_class_type @Int [concrete] // CHECK:STDOUT: %Int.generic: %Int.type = struct_value () [concrete] // CHECK:STDOUT: %N: Core.IntLiteral = symbolic_binding N, 0 [symbolic] // CHECK:STDOUT: %i32: type = class_type @Int, @Int(%int_32) [concrete] // CHECK:STDOUT: %.ff5: Core.Form = init_form %i32 [concrete] // CHECK:STDOUT: %pattern_type.7ce: type = pattern_type %i32 [concrete] // CHECK:STDOUT: %F.type: type = fn_type @F [concrete] // CHECK:STDOUT: %F: %F.type = struct_value () [concrete] // CHECK:STDOUT: %require_complete.944: = require_complete_type %T.67db0b.1 [template] // CHECK:STDOUT: %i32.builtin: type = int_type signed, %int_32 [concrete] // CHECK:STDOUT: %complete_type.f8a: = complete_type_witness %i32.builtin [concrete] // CHECK:STDOUT: %Copy.type: type = facet_type <@Copy> [concrete] // CHECK:STDOUT: %Int.as.Copy.impl.Op.type.824: type = fn_type @Int.as.Copy.impl.Op, @Int.as.Copy.impl(%N) [symbolic] // CHECK:STDOUT: %Int.as.Copy.impl.Op.9b9: %Int.as.Copy.impl.Op.type.824 = struct_value () [symbolic] // CHECK:STDOUT: %Copy.impl_witness.f17: = impl_witness imports.%Copy.impl_witness_table.e76, @Int.as.Copy.impl(%int_32) [concrete] // CHECK:STDOUT: %Int.as.Copy.impl.Op.type.546: type = fn_type @Int.as.Copy.impl.Op, @Int.as.Copy.impl(%int_32) [concrete] // CHECK:STDOUT: %Int.as.Copy.impl.Op.664: %Int.as.Copy.impl.Op.type.546 = struct_value () [concrete] // CHECK:STDOUT: %Copy.facet: %Copy.type = facet_value %i32, (%Copy.impl_witness.f17) [concrete] // CHECK:STDOUT: %Copy.WithSelf.Op.type.081: type = fn_type @Copy.WithSelf.Op, @Copy.WithSelf(%Copy.facet) [concrete] // CHECK:STDOUT: %.8e2: type = fn_type_with_self_type %Copy.WithSelf.Op.type.081, %Copy.facet [concrete] // CHECK:STDOUT: %Int.as.Copy.impl.Op.specific_fn: = specific_function %Int.as.Copy.impl.Op.664, @Int.as.Copy.impl.Op(%int_32) [concrete] // CHECK:STDOUT: %Test1.type: type = fn_type @Test1 [concrete] // CHECK:STDOUT: %Test1: %Test1.type = struct_value () [concrete] // CHECK:STDOUT: %F.specific_fn.d18: = specific_function %F, @F(%i32) [concrete] // CHECK:STDOUT: %C: type = class_type @C [concrete] // CHECK:STDOUT: %C.elem: type = unbound_element_type %C, %i32 [concrete] // CHECK:STDOUT: %ImplicitAs.type.cc7: type = generic_interface_type @ImplicitAs [concrete] // CHECK:STDOUT: %ImplicitAs.generic: %ImplicitAs.type.cc7 = struct_value () [concrete] // CHECK:STDOUT: %ImplicitAs.type.e8c: type = facet_type <@ImplicitAs, @ImplicitAs(%i32)> [concrete] // CHECK:STDOUT: %ImplicitAs.impl_witness.892: = impl_witness @C.as.ImplicitAs.impl.%ImplicitAs.impl_witness_table [concrete] // CHECK:STDOUT: %pattern_type.7c7: type = pattern_type %C [concrete] // CHECK:STDOUT: %C.as.ImplicitAs.impl.Convert.type: type = fn_type @C.as.ImplicitAs.impl.Convert [concrete] // CHECK:STDOUT: %C.as.ImplicitAs.impl.Convert: %C.as.ImplicitAs.impl.Convert.type = struct_value () [concrete] // CHECK:STDOUT: %ImplicitAs.facet: %ImplicitAs.type.e8c = facet_value %C, (%ImplicitAs.impl_witness.892) [concrete] // CHECK:STDOUT: %ImplicitAs.WithSelf.Convert.type.ee5: type = fn_type @ImplicitAs.WithSelf.Convert, @ImplicitAs.WithSelf(%i32, %ImplicitAs.facet) [concrete] // CHECK:STDOUT: %struct_type.n: type = struct_type {.n: %i32} [concrete] // CHECK:STDOUT: %complete_type.54b: = complete_type_witness %struct_type.n [concrete] // CHECK:STDOUT: %Test2.type: type = fn_type @Test2 [concrete] // CHECK:STDOUT: %Test2: %Test2.type = struct_value () [concrete] // CHECK:STDOUT: %F.specific_fn.540: = specific_function %F, @F(%C) [concrete] // CHECK:STDOUT: %inst.as_compatible.db5: = inst_value [concrete] { // CHECK:STDOUT: %.9b1: %i32 = as_compatible @F.%x.ref // CHECK:STDOUT: } // CHECK:STDOUT: %inst.splice_block.e80: = inst_value [concrete] { // CHECK:STDOUT: %.457: %i32 = splice_block %.9b1 {} // CHECK:STDOUT: } // CHECK:STDOUT: %inst.as_compatible.97f: = inst_value [concrete] { // CHECK:STDOUT: %.b3d: %C = as_compatible @F.%x.ref // CHECK:STDOUT: } // CHECK:STDOUT: %.e4e: type = fn_type_with_self_type %ImplicitAs.WithSelf.Convert.type.ee5, %ImplicitAs.facet [concrete] // CHECK:STDOUT: %inst.splice_block.f09: = inst_value [concrete] { // CHECK:STDOUT: %.774: %i32 = splice_block %.9e4 { // CHECK:STDOUT: %impl.elem0.65e: %.e4e = impl_witness_access %ImplicitAs.impl_witness.892, element0 [concrete = %C.as.ImplicitAs.impl.Convert] // CHECK:STDOUT: %bound_method: = bound_method %.b3d, %impl.elem0.65e // CHECK:STDOUT: %C.as.ImplicitAs.impl.Convert.call: init %i32 = call %bound_method(%.b3d) // CHECK:STDOUT: %.6c6: %i32 = value_of_initializer %C.as.ImplicitAs.impl.Convert.call // CHECK:STDOUT: %.9e4: %i32 = converted %.b3d, %.6c6 // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: .Int = %Core.Int // CHECK:STDOUT: .Copy = %Core.Copy // CHECK:STDOUT: .ImplicitAs = %Core.ImplicitAs // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: %Core.Int: %Int.type = import_ref Core//prelude/parts/int, Int, loaded [concrete = constants.%Int.generic] // CHECK:STDOUT: %Core.Copy: type = import_ref Core//prelude/parts/copy, Copy, loaded [concrete = constants.%Copy.type] // CHECK:STDOUT: %Core.import_ref.18d: @Int.as.Copy.impl.%Int.as.Copy.impl.Op.type (%Int.as.Copy.impl.Op.type.824) = import_ref Core//prelude/parts/int, loc{{\d+_\d+}}, loaded [symbolic = @Int.as.Copy.impl.%Int.as.Copy.impl.Op (constants.%Int.as.Copy.impl.Op.9b9)] // CHECK:STDOUT: %Copy.impl_witness_table.e76 = impl_witness_table (%Core.import_ref.18d), @Int.as.Copy.impl [concrete] // CHECK:STDOUT: %Core.ImplicitAs: %ImplicitAs.type.cc7 = import_ref Core//prelude/parts/as, ImplicitAs, loaded [concrete = constants.%ImplicitAs.generic] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .F = %F.decl // CHECK:STDOUT: .Test1 = %Test1.decl // CHECK:STDOUT: .C = %C.decl // CHECK:STDOUT: .Test2 = %Test2.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %F.decl: %F.type = fn_decl @F [concrete = constants.%F] { // CHECK:STDOUT: %T.patt: %pattern_type.98f = symbolic_binding_pattern T, 0, template [concrete] // CHECK:STDOUT: %x.patt: @F.%pattern_type (%pattern_type.51d1c4.1) = value_binding_pattern x [concrete] // CHECK:STDOUT: %x.param_patt: @F.%pattern_type (%pattern_type.51d1c4.1) = value_param_pattern %x.patt [concrete] // CHECK:STDOUT: %return.patt: %pattern_type.7ce = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: %pattern_type.7ce = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %i32.loc4: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %.loc4_34: Core.Form = init_form %i32.loc4 [concrete = constants.%.ff5] // CHECK:STDOUT: %.loc4_19.1: type = splice_block %.loc4_19.2 [concrete = type] { // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %.loc4_19.2: type = type_literal type [concrete = type] // CHECK:STDOUT: } // CHECK:STDOUT: %T.loc4_15.2: type = symbolic_binding T, 0, template [template = %T.loc4_15.1 (constants.%T.67db0b.1)] // CHECK:STDOUT: %x.param: @F.%T.loc4_15.1 (%T.67db0b.1) = value_param call_param0 // CHECK:STDOUT: %T.ref: type = name_ref T, %T.loc4_15.2 [template = %T.loc4_15.1 (constants.%T.67db0b.1)] // CHECK:STDOUT: %x: @F.%T.loc4_15.1 (%T.67db0b.1) = value_binding x, %x.param // CHECK:STDOUT: %return.param: ref %i32 = out_param call_param1 // CHECK:STDOUT: %return: ref %i32 = return_slot %return.param // CHECK:STDOUT: } // CHECK:STDOUT: %Test1.decl: %Test1.type = fn_decl @Test1 [concrete = constants.%Test1] { // CHECK:STDOUT: %n.patt: %pattern_type.7ce = value_binding_pattern n [concrete] // CHECK:STDOUT: %n.param_patt: %pattern_type.7ce = value_param_pattern %n.patt [concrete] // CHECK:STDOUT: %return.patt: %pattern_type.7ce = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: %pattern_type.7ce = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %i32.loc9_21: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %.loc9: Core.Form = init_form %i32.loc9_21 [concrete = constants.%.ff5] // CHECK:STDOUT: %n.param: %i32 = value_param call_param0 // CHECK:STDOUT: %i32.loc9_13: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %n: %i32 = value_binding n, %n.param // CHECK:STDOUT: %return.param: ref %i32 = out_param call_param1 // CHECK:STDOUT: %return: ref %i32 = return_slot %return.param // CHECK:STDOUT: } // CHECK:STDOUT: %C.decl: type = class_decl @C [concrete = constants.%C] {} {} // CHECK:STDOUT: %Test2.decl: %Test2.type = fn_decl @Test2 [concrete = constants.%Test2] { // CHECK:STDOUT: %c.patt: %pattern_type.7c7 = value_binding_pattern c [concrete] // CHECK:STDOUT: %c.param_patt: %pattern_type.7c7 = value_param_pattern %c.patt [concrete] // CHECK:STDOUT: %return.patt: %pattern_type.7ce = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: %pattern_type.7ce = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %i32: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %.loc20: Core.Form = init_form %i32 [concrete = constants.%.ff5] // CHECK:STDOUT: %c.param: %C = value_param call_param0 // CHECK:STDOUT: %C.ref: type = name_ref C, file.%C.decl [concrete = constants.%C] // CHECK:STDOUT: %c: %C = value_binding c, %c.param // CHECK:STDOUT: %return.param: ref %i32 = out_param call_param1 // CHECK:STDOUT: %return: ref %i32 = return_slot %return.param // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: impl @C.as.ImplicitAs.impl: %Self.ref as %ImplicitAs.type { // CHECK:STDOUT: %C.as.ImplicitAs.impl.Convert.decl: %C.as.ImplicitAs.impl.Convert.type = fn_decl @C.as.ImplicitAs.impl.Convert [concrete = constants.%C.as.ImplicitAs.impl.Convert] { // CHECK:STDOUT: %self.patt: %pattern_type.7c7 = value_binding_pattern self [concrete] // CHECK:STDOUT: %self.param_patt: %pattern_type.7c7 = value_param_pattern %self.patt [concrete] // CHECK:STDOUT: %return.patt: %pattern_type.7ce = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: %pattern_type.7ce = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %i32: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %.loc16_33: Core.Form = init_form %i32 [concrete = constants.%.ff5] // CHECK:STDOUT: %self.param: %C = value_param call_param0 // CHECK:STDOUT: %Self.ref: type = name_ref Self, constants.%C [concrete = constants.%C] // CHECK:STDOUT: %self: %C = value_binding self, %self.param // CHECK:STDOUT: %return.param: ref %i32 = out_param call_param1 // CHECK:STDOUT: %return: ref %i32 = return_slot %return.param // CHECK:STDOUT: } // CHECK:STDOUT: %ImplicitAs.impl_witness_table = impl_witness_table (%C.as.ImplicitAs.impl.Convert.decl), @C.as.ImplicitAs.impl [concrete] // CHECK:STDOUT: %ImplicitAs.impl_witness: = impl_witness %ImplicitAs.impl_witness_table [concrete = constants.%ImplicitAs.impl_witness.892] // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Convert = %C.as.ImplicitAs.impl.Convert.decl // CHECK:STDOUT: witness = %ImplicitAs.impl_witness // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @C { // CHECK:STDOUT: %i32: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %.loc14: %C.elem = field_decl n, element0 [concrete] // CHECK:STDOUT: impl_decl @C.as.ImplicitAs.impl [concrete] {} { // CHECK:STDOUT: %Self.ref: type = name_ref Self, constants.%C [concrete = constants.%C] // CHECK:STDOUT: %Core.ref: = name_ref Core, imports.%Core [concrete = imports.%Core] // CHECK:STDOUT: %ImplicitAs.ref: %ImplicitAs.type.cc7 = name_ref ImplicitAs, imports.%Core.ImplicitAs [concrete = constants.%ImplicitAs.generic] // CHECK:STDOUT: %i32: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %ImplicitAs.type: type = facet_type <@ImplicitAs, @ImplicitAs(constants.%i32)> [concrete = constants.%ImplicitAs.type.e8c] // CHECK:STDOUT: } // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%struct_type.n [concrete = constants.%complete_type.54b] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%C // CHECK:STDOUT: .n = %.loc14 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic fn @F(%T.loc4_15.2: type) { // CHECK:STDOUT: %T.loc4_15.1: type = symbolic_binding T, 0, template [template = %T.loc4_15.1 (constants.%T.67db0b.1)] // CHECK:STDOUT: %pattern_type: type = pattern_type %T.loc4_15.1 [template = %pattern_type (constants.%pattern_type.51d1c4.1)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete: = require_complete_type %T.loc4_15.1 [template = %require_complete (constants.%require_complete.944)] // CHECK:STDOUT: %.loc5_16.3: = refine_type_action %x.ref, %T.loc4_15.1 [template] // CHECK:STDOUT: %.loc5_16.4: = convert_to_value_action %.loc5_16.1, constants.%i32 [template] // CHECK:STDOUT: // CHECK:STDOUT: fn(%x.param: @F.%T.loc4_15.1 (%T.67db0b.1)) -> out %return.param: %i32 { // CHECK:STDOUT: !entry: // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %n.patt: %pattern_type.7ce = value_binding_pattern n [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %x.ref: @F.%T.loc4_15.1 (%T.67db0b.1) = name_ref x, %x // CHECK:STDOUT: %i32.loc5: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %.loc5_16.1: @F.%T.loc4_15.1 (%T.67db0b.1) = splice_inst %.loc5_16.3 // CHECK:STDOUT: %.loc5_16.2: %i32 = splice_inst %.loc5_16.4 // CHECK:STDOUT: %n: %i32 = value_binding n, %.loc5_16.2 // CHECK:STDOUT: %n.ref: %i32 = name_ref n, %n // CHECK:STDOUT: %impl.elem0: %.8e2 = impl_witness_access constants.%Copy.impl_witness.f17, element0 [concrete = constants.%Int.as.Copy.impl.Op.664] // CHECK:STDOUT: %bound_method.loc6_10.1: = bound_method %n.ref, %impl.elem0 // CHECK:STDOUT: %specific_fn: = specific_function %impl.elem0, @Int.as.Copy.impl.Op(constants.%int_32) [concrete = constants.%Int.as.Copy.impl.Op.specific_fn] // CHECK:STDOUT: %bound_method.loc6_10.2: = bound_method %n.ref, %specific_fn // CHECK:STDOUT: %Int.as.Copy.impl.Op.call: init %i32 = call %bound_method.loc6_10.2(%n.ref) // CHECK:STDOUT: return %Int.as.Copy.impl.Op.call // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @Test1(%n.param: %i32) -> out %return.param: %i32 { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %F.ref: %F.type = name_ref F, file.%F.decl [concrete = constants.%F] // CHECK:STDOUT: %n.ref: %i32 = name_ref n, %n // CHECK:STDOUT: %F.specific_fn: = specific_function %F.ref, @F(constants.%i32) [concrete = constants.%F.specific_fn.d18] // CHECK:STDOUT: %F.call: init %i32 = call %F.specific_fn(%n.ref) // CHECK:STDOUT: return %F.call // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @C.as.ImplicitAs.impl.Convert(%self.param: %C) -> out %return.param: %i32 { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %self.ref: %C = name_ref self, %self // CHECK:STDOUT: %n.ref: %C.elem = name_ref n, @C.%.loc14 [concrete = @C.%.loc14] // CHECK:STDOUT: %.loc16_50.1: ref %i32 = class_element_access %self.ref, element0 // CHECK:STDOUT: %.loc16_50.2: %i32 = acquire_value %.loc16_50.1 // CHECK:STDOUT: %impl.elem0: %.8e2 = impl_witness_access constants.%Copy.impl_witness.f17, element0 [concrete = constants.%Int.as.Copy.impl.Op.664] // CHECK:STDOUT: %bound_method.loc16_50.1: = bound_method %.loc16_50.2, %impl.elem0 // CHECK:STDOUT: %specific_fn: = specific_function %impl.elem0, @Int.as.Copy.impl.Op(constants.%int_32) [concrete = constants.%Int.as.Copy.impl.Op.specific_fn] // CHECK:STDOUT: %bound_method.loc16_50.2: = bound_method %.loc16_50.2, %specific_fn // CHECK:STDOUT: %Int.as.Copy.impl.Op.call: init %i32 = call %bound_method.loc16_50.2(%.loc16_50.2) // CHECK:STDOUT: return %Int.as.Copy.impl.Op.call // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @Test2(%c.param: %C) -> out %return.param: %i32 { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %F.ref: %F.type = name_ref F, file.%F.decl [concrete = constants.%F] // CHECK:STDOUT: %c.ref: %C = name_ref c, %c // CHECK:STDOUT: %F.specific_fn: = specific_function %F.ref, @F(constants.%C) [concrete = constants.%F.specific_fn.540] // CHECK:STDOUT: %F.call: init %i32 = call %F.specific_fn(%c.ref) // CHECK:STDOUT: return %F.call // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @F(constants.%T.67db0b.1) { // CHECK:STDOUT: %T.loc4_15.1 => constants.%T.67db0b.1 // CHECK:STDOUT: %pattern_type => constants.%pattern_type.51d1c4.1 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @F(constants.%i32) { // CHECK:STDOUT: %T.loc4_15.1 => constants.%i32 // CHECK:STDOUT: %pattern_type => constants.%pattern_type.7ce // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete => constants.%complete_type.f8a // CHECK:STDOUT: %.loc5_16.3 => constants.%inst.as_compatible.db5 // CHECK:STDOUT: %.loc5_16.4 => constants.%inst.splice_block.e80 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @F(constants.%C) { // CHECK:STDOUT: %T.loc4_15.1 => constants.%C // CHECK:STDOUT: %pattern_type => constants.%pattern_type.7c7 // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete => constants.%complete_type.54b // CHECK:STDOUT: %.loc5_16.3 => constants.%inst.as_compatible.97f // CHECK:STDOUT: %.loc5_16.4 => constants.%inst.splice_block.f09 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- fail_cannot_convert.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %type: type = facet_type [concrete] // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self] // CHECK:STDOUT: %pattern_type.98f: type = pattern_type type [concrete] // CHECK:STDOUT: %T.67db0b.1: type = symbolic_binding T, 0, template [template] // CHECK:STDOUT: %pattern_type.51d1c4.1: type = pattern_type %T.67db0b.1 [template] // CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [concrete] // CHECK:STDOUT: %Int.type: type = generic_class_type @Int [concrete] // CHECK:STDOUT: %Int.generic: %Int.type = struct_value () [concrete] // CHECK:STDOUT: %N: Core.IntLiteral = symbolic_binding N, 0 [symbolic] // CHECK:STDOUT: %i32: type = class_type @Int, @Int(%int_32) [concrete] // CHECK:STDOUT: %.ff5: Core.Form = init_form %i32 [concrete] // CHECK:STDOUT: %pattern_type.7ce: type = pattern_type %i32 [concrete] // CHECK:STDOUT: %F.type: type = fn_type @F [concrete] // CHECK:STDOUT: %F: %F.type = struct_value () [concrete] // CHECK:STDOUT: %require_complete.944: = require_complete_type %T.67db0b.1 [template] // CHECK:STDOUT: %Copy.type: type = facet_type <@Copy> [concrete] // CHECK:STDOUT: %Int.as.Copy.impl.Op.type.824: type = fn_type @Int.as.Copy.impl.Op, @Int.as.Copy.impl(%N) [symbolic] // CHECK:STDOUT: %Int.as.Copy.impl.Op.9b9: %Int.as.Copy.impl.Op.type.824 = struct_value () [symbolic] // CHECK:STDOUT: %Copy.impl_witness.f17: = impl_witness imports.%Copy.impl_witness_table.e76, @Int.as.Copy.impl(%int_32) [concrete] // CHECK:STDOUT: %Int.as.Copy.impl.Op.type.546: type = fn_type @Int.as.Copy.impl.Op, @Int.as.Copy.impl(%int_32) [concrete] // CHECK:STDOUT: %Int.as.Copy.impl.Op.664: %Int.as.Copy.impl.Op.type.546 = struct_value () [concrete] // CHECK:STDOUT: %Copy.facet: %Copy.type = facet_value %i32, (%Copy.impl_witness.f17) [concrete] // CHECK:STDOUT: %Copy.WithSelf.Op.type.081: type = fn_type @Copy.WithSelf.Op, @Copy.WithSelf(%Copy.facet) [concrete] // CHECK:STDOUT: %.8e2: type = fn_type_with_self_type %Copy.WithSelf.Op.type.081, %Copy.facet [concrete] // CHECK:STDOUT: %Int.as.Copy.impl.Op.specific_fn: = specific_function %Int.as.Copy.impl.Op.664, @Int.as.Copy.impl.Op(%int_32) [concrete] // CHECK:STDOUT: %D: type = class_type @D [concrete] // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %complete_type.357: = complete_type_witness %empty_struct_type [concrete] // CHECK:STDOUT: %pattern_type.9c8: type = pattern_type %D [concrete] // CHECK:STDOUT: %Test.type: type = fn_type @Test [concrete] // CHECK:STDOUT: %Test: %Test.type = struct_value () [concrete] // CHECK:STDOUT: %F.specific_fn: = specific_function %F, @F(%D) [concrete] // CHECK:STDOUT: %inst.as_compatible: = inst_value [concrete] { // CHECK:STDOUT: %.759: %D = as_compatible @F.%x.ref // CHECK:STDOUT: } // CHECK:STDOUT: %ImplicitAs.type.cc7: type = generic_interface_type @ImplicitAs [concrete] // CHECK:STDOUT: %ImplicitAs.generic: %ImplicitAs.type.cc7 = struct_value () [concrete] // CHECK:STDOUT: %inst.splice_block: = inst_value [concrete] { // CHECK:STDOUT: %.e29: = splice_block [concrete = ] { // CHECK:STDOUT: %.1ae: %i32 = converted %.759, [concrete = ] // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: .Int = %Core.Int // CHECK:STDOUT: .Copy = %Core.Copy // CHECK:STDOUT: .ImplicitAs = %Core.ImplicitAs // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: %Core.Int: %Int.type = import_ref Core//prelude/parts/int, Int, loaded [concrete = constants.%Int.generic] // CHECK:STDOUT: %Core.Copy: type = import_ref Core//prelude/parts/copy, Copy, loaded [concrete = constants.%Copy.type] // CHECK:STDOUT: %Core.import_ref.18d: @Int.as.Copy.impl.%Int.as.Copy.impl.Op.type (%Int.as.Copy.impl.Op.type.824) = import_ref Core//prelude/parts/int, loc{{\d+_\d+}}, loaded [symbolic = @Int.as.Copy.impl.%Int.as.Copy.impl.Op (constants.%Int.as.Copy.impl.Op.9b9)] // CHECK:STDOUT: %Copy.impl_witness_table.e76 = impl_witness_table (%Core.import_ref.18d), @Int.as.Copy.impl [concrete] // CHECK:STDOUT: %Core.ImplicitAs: %ImplicitAs.type.cc7 = import_ref Core//prelude/parts/as, ImplicitAs, loaded [concrete = constants.%ImplicitAs.generic] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .F = %F.decl // CHECK:STDOUT: .D = %D.decl // CHECK:STDOUT: .Test = %Test.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %F.decl: %F.type = fn_decl @F [concrete = constants.%F] { // CHECK:STDOUT: %T.patt: %pattern_type.98f = symbolic_binding_pattern T, 0, template [concrete] // CHECK:STDOUT: %x.patt: @F.%pattern_type (%pattern_type.51d1c4.1) = value_binding_pattern x [concrete] // CHECK:STDOUT: %x.param_patt: @F.%pattern_type (%pattern_type.51d1c4.1) = value_param_pattern %x.patt [concrete] // CHECK:STDOUT: %return.patt: %pattern_type.7ce = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: %pattern_type.7ce = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %i32.loc4: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %.loc4_34: Core.Form = init_form %i32.loc4 [concrete = constants.%.ff5] // CHECK:STDOUT: %.loc4_19.1: type = splice_block %.loc4_19.2 [concrete = type] { // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %.loc4_19.2: type = type_literal type [concrete = type] // CHECK:STDOUT: } // CHECK:STDOUT: %T.loc4_15.2: type = symbolic_binding T, 0, template [template = %T.loc4_15.1 (constants.%T.67db0b.1)] // CHECK:STDOUT: %x.param: @F.%T.loc4_15.1 (%T.67db0b.1) = value_param call_param0 // CHECK:STDOUT: %T.ref: type = name_ref T, %T.loc4_15.2 [template = %T.loc4_15.1 (constants.%T.67db0b.1)] // CHECK:STDOUT: %x: @F.%T.loc4_15.1 (%T.67db0b.1) = value_binding x, %x.param // CHECK:STDOUT: %return.param: ref %i32 = out_param call_param1 // CHECK:STDOUT: %return: ref %i32 = return_slot %return.param // CHECK:STDOUT: } // CHECK:STDOUT: %D.decl: type = class_decl @D [concrete = constants.%D] {} {} // CHECK:STDOUT: %Test.decl: %Test.type = fn_decl @Test [concrete = constants.%Test] { // CHECK:STDOUT: %d.patt: %pattern_type.9c8 = value_binding_pattern d [concrete] // CHECK:STDOUT: %d.param_patt: %pattern_type.9c8 = value_param_pattern %d.patt [concrete] // CHECK:STDOUT: %return.patt: %pattern_type.7ce = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: %pattern_type.7ce = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %i32: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %.loc11: Core.Form = init_form %i32 [concrete = constants.%.ff5] // CHECK:STDOUT: %d.param: %D = value_param call_param0 // CHECK:STDOUT: %D.ref: type = name_ref D, file.%D.decl [concrete = constants.%D] // CHECK:STDOUT: %d: %D = value_binding d, %d.param // CHECK:STDOUT: %return.param: ref %i32 = out_param call_param1 // CHECK:STDOUT: %return: ref %i32 = return_slot %return.param // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @D { // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type.357] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%D // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic fn @F(%T.loc4_15.2: type) { // CHECK:STDOUT: %T.loc4_15.1: type = symbolic_binding T, 0, template [template = %T.loc4_15.1 (constants.%T.67db0b.1)] // CHECK:STDOUT: %pattern_type: type = pattern_type %T.loc4_15.1 [template = %pattern_type (constants.%pattern_type.51d1c4.1)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete: = require_complete_type %T.loc4_15.1 [template = %require_complete (constants.%require_complete.944)] // CHECK:STDOUT: %.loc5_16.3: = refine_type_action %x.ref, %T.loc4_15.1 [template] // CHECK:STDOUT: %.loc5_16.4: = convert_to_value_action %.loc5_16.1, constants.%i32 [template] // CHECK:STDOUT: // CHECK:STDOUT: fn(%x.param: @F.%T.loc4_15.1 (%T.67db0b.1)) -> out %return.param: %i32 { // CHECK:STDOUT: !entry: // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %n.patt: %pattern_type.7ce = value_binding_pattern n [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %x.ref: @F.%T.loc4_15.1 (%T.67db0b.1) = name_ref x, %x // CHECK:STDOUT: %i32.loc5: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %.loc5_16.1: @F.%T.loc4_15.1 (%T.67db0b.1) = splice_inst %.loc5_16.3 // CHECK:STDOUT: %.loc5_16.2: %i32 = splice_inst %.loc5_16.4 // CHECK:STDOUT: %n: %i32 = value_binding n, %.loc5_16.2 // CHECK:STDOUT: %n.ref: %i32 = name_ref n, %n // CHECK:STDOUT: %impl.elem0: %.8e2 = impl_witness_access constants.%Copy.impl_witness.f17, element0 [concrete = constants.%Int.as.Copy.impl.Op.664] // CHECK:STDOUT: %bound_method.loc6_10.1: = bound_method %n.ref, %impl.elem0 // CHECK:STDOUT: %specific_fn: = specific_function %impl.elem0, @Int.as.Copy.impl.Op(constants.%int_32) [concrete = constants.%Int.as.Copy.impl.Op.specific_fn] // CHECK:STDOUT: %bound_method.loc6_10.2: = bound_method %n.ref, %specific_fn // CHECK:STDOUT: %Int.as.Copy.impl.Op.call: init %i32 = call %bound_method.loc6_10.2(%n.ref) // CHECK:STDOUT: return %Int.as.Copy.impl.Op.call // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @Test(%d.param: %D) -> out %return.param: %i32 { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %F.ref: %F.type = name_ref F, file.%F.decl [concrete = constants.%F] // CHECK:STDOUT: %d.ref: %D = name_ref d, %d // CHECK:STDOUT: %F.specific_fn: = specific_function %F.ref, @F(constants.%D) [concrete = constants.%F.specific_fn] // CHECK:STDOUT: %F.call: init %i32 = call %F.specific_fn(%d.ref) // CHECK:STDOUT: return %F.call // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @F(constants.%T.67db0b.1) { // CHECK:STDOUT: %T.loc4_15.1 => constants.%T.67db0b.1 // CHECK:STDOUT: %pattern_type => constants.%pattern_type.51d1c4.1 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @F(constants.%D) { // CHECK:STDOUT: %T.loc4_15.1 => constants.%D // CHECK:STDOUT: %pattern_type => constants.%pattern_type.9c8 // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete => constants.%complete_type.357 // CHECK:STDOUT: %.loc5_16.3 => constants.%inst.as_compatible // CHECK:STDOUT: %.loc5_16.4 => constants.%inst.splice_block // CHECK:STDOUT: } // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/generic/template/fail_todo_template_access_assoc_const.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/convert.carbon // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/generic/template/fail_todo_template_access_assoc_const.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/generic/template/fail_todo_template_access_assoc_const.carbon library "[[@TEST_NAME]]"; // TODO: This should check. The `T.I1` access should be deferred until we know // the value of the `template T`. // // CHECK:STDERR: fail_todo_template_access_assoc_const.carbon:[[@LINE+4]]:36: error: cannot evaluate type expression [TypeExprEvaluationFailure] // CHECK:STDERR: interface I(template T:! type, N:! T.I1) { // CHECK:STDERR: ^~~~ // CHECK:STDERR: interface I(template T:! type, N:! T.I1) { let I1:! type; } ================================================ FILE: toolchain/check/testdata/generic/template/member_access.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/int.carbon // TODO: Add ranges and switch to "--dump-sem-ir-ranges=only". // EXTRA-ARGS: --dump-sem-ir-ranges=if-present // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/generic/template/member_access.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/generic/template/member_access.carbon // --- member_access.carbon library "[[@TEST_NAME]]"; fn F[template T:! type](x: T) -> i32 { let n: i32 = x.n; return n; } class C { var n: i32; } fn Test1(c: C) { F(c); } fn Test2(x: {.m: i32, .n: i32}) { F(x); } // --- fail_no_such_member.carbon library "[[@TEST_NAME]]"; fn F[template T:! type](x: T) -> i32 { let n: i32 = x.n; return n; } class D { var m: i32; } fn Test(d: D) { // CHECK:STDERR: fail_no_such_member.carbon:[[@LINE+7]]:3: error: unable to monomorphize specific `F(D)` [ResolvingSpecificHere] // CHECK:STDERR: F(d); // CHECK:STDERR: ^ // CHECK:STDERR: fail_no_such_member.carbon:[[@LINE-12]]:16: note: member name `n` not found in `D` [MemberNameNotFoundInInstScope] // CHECK:STDERR: let n: i32 = x.n; // CHECK:STDERR: ^~~ // CHECK:STDERR: F(d); } // --- fail_member_wrong_type.carbon library "[[@TEST_NAME]]"; fn F[template T:! type](x: T) -> i32 { let n: i32 = x.n; return n; } class E { class F {} var n: F; } fn Test(e: E) { // CHECK:STDERR: fail_member_wrong_type.carbon:[[@LINE+10]]:3: error: unable to monomorphize specific `F(E)` [ResolvingSpecificHere] // CHECK:STDERR: F(e); // CHECK:STDERR: ^ // CHECK:STDERR: fail_member_wrong_type.carbon:[[@LINE-13]]:16: note: cannot implicitly convert expression of type `F` to `i32` [ConversionFailure] // CHECK:STDERR: let n: i32 = x.n; // CHECK:STDERR: ^~~ // CHECK:STDERR: fail_member_wrong_type.carbon:[[@LINE-16]]:16: note: type `F` does not implement interface `Core.ImplicitAs(i32)` [MissingImplInMemberAccessInContext] // CHECK:STDERR: let n: i32 = x.n; // CHECK:STDERR: ^~~ // CHECK:STDERR: F(e); } // CHECK:STDOUT: --- member_access.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %type: type = facet_type [concrete] // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self] // CHECK:STDOUT: %pattern_type.98f: type = pattern_type type [concrete] // CHECK:STDOUT: %T.67db0b.1: type = symbolic_binding T, 0, template [template] // CHECK:STDOUT: %pattern_type.51d: type = pattern_type %T.67db0b.1 [template] // CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [concrete] // CHECK:STDOUT: %Int.type: type = generic_class_type @Int [concrete] // CHECK:STDOUT: %Int.generic: %Int.type = struct_value () [concrete] // CHECK:STDOUT: %N: Core.IntLiteral = symbolic_binding N, 0 [symbolic] // CHECK:STDOUT: %i32: type = class_type @Int, @Int(%int_32) [concrete] // CHECK:STDOUT: %.ff5: Core.Form = init_form %i32 [concrete] // CHECK:STDOUT: %pattern_type.7ce: type = pattern_type %i32 [concrete] // CHECK:STDOUT: %F.type: type = fn_type @F [concrete] // CHECK:STDOUT: %F: %F.type = struct_value () [concrete] // CHECK:STDOUT: %require_complete.944: = require_complete_type %T.67db0b.1 [template] // CHECK:STDOUT: %Copy.type: type = facet_type <@Copy> [concrete] // CHECK:STDOUT: %Int.as.Copy.impl.Op.type.824: type = fn_type @Int.as.Copy.impl.Op, @Int.as.Copy.impl(%N) [symbolic] // CHECK:STDOUT: %Int.as.Copy.impl.Op.9b9: %Int.as.Copy.impl.Op.type.824 = struct_value () [symbolic] // CHECK:STDOUT: %Copy.impl_witness.f17: = impl_witness imports.%Copy.impl_witness_table.e76, @Int.as.Copy.impl(%int_32) [concrete] // CHECK:STDOUT: %Int.as.Copy.impl.Op.type.546: type = fn_type @Int.as.Copy.impl.Op, @Int.as.Copy.impl(%int_32) [concrete] // CHECK:STDOUT: %Int.as.Copy.impl.Op.664: %Int.as.Copy.impl.Op.type.546 = struct_value () [concrete] // CHECK:STDOUT: %Copy.facet: %Copy.type = facet_value %i32, (%Copy.impl_witness.f17) [concrete] // CHECK:STDOUT: %Copy.WithSelf.Op.type.081: type = fn_type @Copy.WithSelf.Op, @Copy.WithSelf(%Copy.facet) [concrete] // CHECK:STDOUT: %.8e2: type = fn_type_with_self_type %Copy.WithSelf.Op.type.081, %Copy.facet [concrete] // CHECK:STDOUT: %Int.as.Copy.impl.Op.specific_fn: = specific_function %Int.as.Copy.impl.Op.664, @Int.as.Copy.impl.Op(%int_32) [concrete] // CHECK:STDOUT: %C: type = class_type @C [concrete] // CHECK:STDOUT: %C.elem: type = unbound_element_type %C, %i32 [concrete] // CHECK:STDOUT: %struct_type.n: type = struct_type {.n: %i32} [concrete] // CHECK:STDOUT: %complete_type.54b: = complete_type_witness %struct_type.n [concrete] // CHECK:STDOUT: %pattern_type.7c7: type = pattern_type %C [concrete] // CHECK:STDOUT: %Test1.type: type = fn_type @Test1 [concrete] // CHECK:STDOUT: %Test1: %Test1.type = struct_value () [concrete] // CHECK:STDOUT: %F.specific_fn.540: = specific_function %F, @F(%C) [concrete] // CHECK:STDOUT: %struct_type.m.n: type = struct_type {.m: %i32, .n: %i32} [concrete] // CHECK:STDOUT: %pattern_type.811: type = pattern_type %struct_type.m.n [concrete] // CHECK:STDOUT: %Test2.type: type = fn_type @Test2 [concrete] // CHECK:STDOUT: %Test2: %Test2.type = struct_value () [concrete] // CHECK:STDOUT: %F.specific_fn.bfc: = specific_function %F, @F(%struct_type.m.n) [concrete] // CHECK:STDOUT: %inst.as_compatible.97f: = inst_value [concrete] { // CHECK:STDOUT: %.b3d: %C = as_compatible @F.%x.ref // CHECK:STDOUT: } // CHECK:STDOUT: %inst.splice_block.21d: = inst_value [concrete] { // CHECK:STDOUT: %.b39: %i32 = splice_block %.b60 { // CHECK:STDOUT: %n.ref: %C.elem = name_ref n, @C.%.loc10 [concrete = @C.%.loc10] // CHECK:STDOUT: %.b01: ref %i32 = class_element_access %.b3d, element0 // CHECK:STDOUT: %.b60: %i32 = acquire_value %.b01 // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: %inst.splice_block.ae1: = inst_value [concrete] { // CHECK:STDOUT: %.eda: %i32 = splice_block %.b39 {} // CHECK:STDOUT: } // CHECK:STDOUT: %complete_type.622: = complete_type_witness %struct_type.m.n [concrete] // CHECK:STDOUT: %inst.as_compatible.5b4: = inst_value [concrete] { // CHECK:STDOUT: %.298: %struct_type.m.n = as_compatible @F.%x.ref // CHECK:STDOUT: } // CHECK:STDOUT: %inst.struct_access: = inst_value [concrete] { // CHECK:STDOUT: %.4b6: %i32 = struct_access %.298, element1 // CHECK:STDOUT: } // CHECK:STDOUT: %inst.splice_block.48b: = inst_value [concrete] { // CHECK:STDOUT: %.498: %i32 = splice_block %.4b6 {} // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: .Int = %Core.Int // CHECK:STDOUT: .Copy = %Core.Copy // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: %Core.Int: %Int.type = import_ref Core//prelude/parts/int, Int, loaded [concrete = constants.%Int.generic] // CHECK:STDOUT: %Core.Copy: type = import_ref Core//prelude/parts/copy, Copy, loaded [concrete = constants.%Copy.type] // CHECK:STDOUT: %Core.import_ref.18d: @Int.as.Copy.impl.%Int.as.Copy.impl.Op.type (%Int.as.Copy.impl.Op.type.824) = import_ref Core//prelude/parts/int, loc{{\d+_\d+}}, loaded [symbolic = @Int.as.Copy.impl.%Int.as.Copy.impl.Op (constants.%Int.as.Copy.impl.Op.9b9)] // CHECK:STDOUT: %Copy.impl_witness_table.e76 = impl_witness_table (%Core.import_ref.18d), @Int.as.Copy.impl [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .F = %F.decl // CHECK:STDOUT: .C = %C.decl // CHECK:STDOUT: .Test1 = %Test1.decl // CHECK:STDOUT: .Test2 = %Test2.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %F.decl: %F.type = fn_decl @F [concrete = constants.%F] { // CHECK:STDOUT: %T.patt: %pattern_type.98f = symbolic_binding_pattern T, 0, template [concrete] // CHECK:STDOUT: %x.patt: @F.%pattern_type (%pattern_type.51d) = value_binding_pattern x [concrete] // CHECK:STDOUT: %x.param_patt: @F.%pattern_type (%pattern_type.51d) = value_param_pattern %x.patt [concrete] // CHECK:STDOUT: %return.patt: %pattern_type.7ce = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: %pattern_type.7ce = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %i32.loc4: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %.loc4_34: Core.Form = init_form %i32.loc4 [concrete = constants.%.ff5] // CHECK:STDOUT: %.loc4_19.1: type = splice_block %.loc4_19.2 [concrete = type] { // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %.loc4_19.2: type = type_literal type [concrete = type] // CHECK:STDOUT: } // CHECK:STDOUT: %T.loc4_15.2: type = symbolic_binding T, 0, template [template = %T.loc4_15.1 (constants.%T.67db0b.1)] // CHECK:STDOUT: %x.param: @F.%T.loc4_15.1 (%T.67db0b.1) = value_param call_param0 // CHECK:STDOUT: %T.ref: type = name_ref T, %T.loc4_15.2 [template = %T.loc4_15.1 (constants.%T.67db0b.1)] // CHECK:STDOUT: %x: @F.%T.loc4_15.1 (%T.67db0b.1) = value_binding x, %x.param // CHECK:STDOUT: %return.param: ref %i32 = out_param call_param1 // CHECK:STDOUT: %return: ref %i32 = return_slot %return.param // CHECK:STDOUT: } // CHECK:STDOUT: %C.decl: type = class_decl @C [concrete = constants.%C] {} {} // CHECK:STDOUT: %Test1.decl: %Test1.type = fn_decl @Test1 [concrete = constants.%Test1] { // CHECK:STDOUT: %c.patt: %pattern_type.7c7 = value_binding_pattern c [concrete] // CHECK:STDOUT: %c.param_patt: %pattern_type.7c7 = value_param_pattern %c.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %c.param: %C = value_param call_param0 // CHECK:STDOUT: %C.ref: type = name_ref C, file.%C.decl [concrete = constants.%C] // CHECK:STDOUT: %c: %C = value_binding c, %c.param // CHECK:STDOUT: } // CHECK:STDOUT: %Test2.decl: %Test2.type = fn_decl @Test2 [concrete = constants.%Test2] { // CHECK:STDOUT: %x.patt: %pattern_type.811 = value_binding_pattern x [concrete] // CHECK:STDOUT: %x.param_patt: %pattern_type.811 = value_param_pattern %x.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %x.param: %struct_type.m.n = value_param call_param0 // CHECK:STDOUT: %.loc17: type = splice_block %struct_type.m.n [concrete = constants.%struct_type.m.n] { // CHECK:STDOUT: %i32.loc17_18: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %i32.loc17_27: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %struct_type.m.n: type = struct_type {.m: %i32, .n: %i32} [concrete = constants.%struct_type.m.n] // CHECK:STDOUT: } // CHECK:STDOUT: %x: %struct_type.m.n = value_binding x, %x.param // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @C { // CHECK:STDOUT: %i32: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %.loc10: %C.elem = field_decl n, element0 [concrete] // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%struct_type.n [concrete = constants.%complete_type.54b] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%C // CHECK:STDOUT: .n = %.loc10 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic fn @F(%T.loc4_15.2: type) { // CHECK:STDOUT: %T.loc4_15.1: type = symbolic_binding T, 0, template [template = %T.loc4_15.1 (constants.%T.67db0b.1)] // CHECK:STDOUT: %pattern_type: type = pattern_type %T.loc4_15.1 [template = %pattern_type (constants.%pattern_type.51d)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete: = require_complete_type %T.loc4_15.1 [template = %require_complete (constants.%require_complete.944)] // CHECK:STDOUT: %.loc5_17.4: = refine_type_action %x.ref, %T.loc4_15.1 [template] // CHECK:STDOUT: %.loc5_17.5: = access_member_action %.loc5_17.1, n [template] // CHECK:STDOUT: %.loc5_17.6: type = type_of_inst %.loc5_17.5 [template] // CHECK:STDOUT: %.loc5_17.7: = convert_to_value_action %.loc5_17.2, constants.%i32 [template] // CHECK:STDOUT: // CHECK:STDOUT: fn(%x.param: @F.%T.loc4_15.1 (%T.67db0b.1)) -> out %return.param: %i32 { // CHECK:STDOUT: !entry: // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %n.patt: %pattern_type.7ce = value_binding_pattern n [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %x.ref: @F.%T.loc4_15.1 (%T.67db0b.1) = name_ref x, %x // CHECK:STDOUT: %.loc5_17.1: @F.%T.loc4_15.1 (%T.67db0b.1) = splice_inst %.loc5_17.4 // CHECK:STDOUT: %.loc5_17.2: @F.%.loc5_17.6 (@F.%.loc5_17.6) = splice_inst %.loc5_17.5 // CHECK:STDOUT: %i32.loc5: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %.loc5_17.3: %i32 = splice_inst %.loc5_17.7 // CHECK:STDOUT: %n: %i32 = value_binding n, %.loc5_17.3 // CHECK:STDOUT: %n.ref: %i32 = name_ref n, %n // CHECK:STDOUT: %impl.elem0: %.8e2 = impl_witness_access constants.%Copy.impl_witness.f17, element0 [concrete = constants.%Int.as.Copy.impl.Op.664] // CHECK:STDOUT: %bound_method.loc6_10.1: = bound_method %n.ref, %impl.elem0 // CHECK:STDOUT: %specific_fn: = specific_function %impl.elem0, @Int.as.Copy.impl.Op(constants.%int_32) [concrete = constants.%Int.as.Copy.impl.Op.specific_fn] // CHECK:STDOUT: %bound_method.loc6_10.2: = bound_method %n.ref, %specific_fn // CHECK:STDOUT: %Int.as.Copy.impl.Op.call: init %i32 = call %bound_method.loc6_10.2(%n.ref) // CHECK:STDOUT: return %Int.as.Copy.impl.Op.call // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @Test1(%c.param: %C) { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %F.ref: %F.type = name_ref F, file.%F.decl [concrete = constants.%F] // CHECK:STDOUT: %c.ref: %C = name_ref c, %c // CHECK:STDOUT: %F.specific_fn: = specific_function %F.ref, @F(constants.%C) [concrete = constants.%F.specific_fn.540] // CHECK:STDOUT: %F.call: init %i32 = call %F.specific_fn(%c.ref) // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @Test2(%x.param: %struct_type.m.n) { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %F.ref: %F.type = name_ref F, file.%F.decl [concrete = constants.%F] // CHECK:STDOUT: %x.ref: %struct_type.m.n = name_ref x, %x // CHECK:STDOUT: %F.specific_fn: = specific_function %F.ref, @F(constants.%struct_type.m.n) [concrete = constants.%F.specific_fn.bfc] // CHECK:STDOUT: %F.call: init %i32 = call %F.specific_fn(%x.ref) // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @F(constants.%T.67db0b.1) { // CHECK:STDOUT: %T.loc4_15.1 => constants.%T.67db0b.1 // CHECK:STDOUT: %pattern_type => constants.%pattern_type.51d // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @F(constants.%C) { // CHECK:STDOUT: %T.loc4_15.1 => constants.%C // CHECK:STDOUT: %pattern_type => constants.%pattern_type.7c7 // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete => constants.%complete_type.54b // CHECK:STDOUT: %.loc5_17.4 => constants.%inst.as_compatible.97f // CHECK:STDOUT: %.loc5_17.5 => constants.%inst.splice_block.21d // CHECK:STDOUT: %.loc5_17.6 => constants.%i32 // CHECK:STDOUT: %.loc5_17.7 => constants.%inst.splice_block.ae1 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @F(constants.%struct_type.m.n) { // CHECK:STDOUT: %T.loc4_15.1 => constants.%struct_type.m.n // CHECK:STDOUT: %pattern_type => constants.%pattern_type.811 // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete => constants.%complete_type.622 // CHECK:STDOUT: %.loc5_17.4 => constants.%inst.as_compatible.5b4 // CHECK:STDOUT: %.loc5_17.5 => constants.%inst.struct_access // CHECK:STDOUT: %.loc5_17.6 => constants.%i32 // CHECK:STDOUT: %.loc5_17.7 => constants.%inst.splice_block.48b // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- fail_no_such_member.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %type: type = facet_type [concrete] // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self] // CHECK:STDOUT: %pattern_type.98f: type = pattern_type type [concrete] // CHECK:STDOUT: %T.67db0b.1: type = symbolic_binding T, 0, template [template] // CHECK:STDOUT: %pattern_type.51d: type = pattern_type %T.67db0b.1 [template] // CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [concrete] // CHECK:STDOUT: %Int.type: type = generic_class_type @Int [concrete] // CHECK:STDOUT: %Int.generic: %Int.type = struct_value () [concrete] // CHECK:STDOUT: %N: Core.IntLiteral = symbolic_binding N, 0 [symbolic] // CHECK:STDOUT: %i32: type = class_type @Int, @Int(%int_32) [concrete] // CHECK:STDOUT: %.ff5: Core.Form = init_form %i32 [concrete] // CHECK:STDOUT: %pattern_type.7ce: type = pattern_type %i32 [concrete] // CHECK:STDOUT: %F.type: type = fn_type @F [concrete] // CHECK:STDOUT: %F: %F.type = struct_value () [concrete] // CHECK:STDOUT: %require_complete.944: = require_complete_type %T.67db0b.1 [template] // CHECK:STDOUT: %Copy.type: type = facet_type <@Copy> [concrete] // CHECK:STDOUT: %Int.as.Copy.impl.Op.type.824: type = fn_type @Int.as.Copy.impl.Op, @Int.as.Copy.impl(%N) [symbolic] // CHECK:STDOUT: %Int.as.Copy.impl.Op.9b9: %Int.as.Copy.impl.Op.type.824 = struct_value () [symbolic] // CHECK:STDOUT: %Copy.impl_witness.f17: = impl_witness imports.%Copy.impl_witness_table.e76, @Int.as.Copy.impl(%int_32) [concrete] // CHECK:STDOUT: %Int.as.Copy.impl.Op.type.546: type = fn_type @Int.as.Copy.impl.Op, @Int.as.Copy.impl(%int_32) [concrete] // CHECK:STDOUT: %Int.as.Copy.impl.Op.664: %Int.as.Copy.impl.Op.type.546 = struct_value () [concrete] // CHECK:STDOUT: %Copy.facet: %Copy.type = facet_value %i32, (%Copy.impl_witness.f17) [concrete] // CHECK:STDOUT: %Copy.WithSelf.Op.type.081: type = fn_type @Copy.WithSelf.Op, @Copy.WithSelf(%Copy.facet) [concrete] // CHECK:STDOUT: %.8e2: type = fn_type_with_self_type %Copy.WithSelf.Op.type.081, %Copy.facet [concrete] // CHECK:STDOUT: %Int.as.Copy.impl.Op.specific_fn: = specific_function %Int.as.Copy.impl.Op.664, @Int.as.Copy.impl.Op(%int_32) [concrete] // CHECK:STDOUT: %D: type = class_type @D [concrete] // CHECK:STDOUT: %D.elem: type = unbound_element_type %D, %i32 [concrete] // CHECK:STDOUT: %struct_type.m: type = struct_type {.m: %i32} [concrete] // CHECK:STDOUT: %complete_type.218: = complete_type_witness %struct_type.m [concrete] // CHECK:STDOUT: %pattern_type.9c8: type = pattern_type %D [concrete] // CHECK:STDOUT: %Test.type: type = fn_type @Test [concrete] // CHECK:STDOUT: %Test: %Test.type = struct_value () [concrete] // CHECK:STDOUT: %F.specific_fn: = specific_function %F, @F(%D) [concrete] // CHECK:STDOUT: %inst.as_compatible: = inst_value [concrete] { // CHECK:STDOUT: %.759: %D = as_compatible @F.%x.ref // CHECK:STDOUT: } // CHECK:STDOUT: %inst.name_ref: = inst_value [concrete] { // CHECK:STDOUT: %n.ref: = name_ref n, [concrete = ] // CHECK:STDOUT: } // CHECK:STDOUT: %inst.splice_block: = inst_value [concrete] { // CHECK:STDOUT: %.432: = splice_block [concrete = ] {} // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: .Int = %Core.Int // CHECK:STDOUT: .Copy = %Core.Copy // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: %Core.Int: %Int.type = import_ref Core//prelude/parts/int, Int, loaded [concrete = constants.%Int.generic] // CHECK:STDOUT: %Core.Copy: type = import_ref Core//prelude/parts/copy, Copy, loaded [concrete = constants.%Copy.type] // CHECK:STDOUT: %Core.import_ref.18d: @Int.as.Copy.impl.%Int.as.Copy.impl.Op.type (%Int.as.Copy.impl.Op.type.824) = import_ref Core//prelude/parts/int, loc{{\d+_\d+}}, loaded [symbolic = @Int.as.Copy.impl.%Int.as.Copy.impl.Op (constants.%Int.as.Copy.impl.Op.9b9)] // CHECK:STDOUT: %Copy.impl_witness_table.e76 = impl_witness_table (%Core.import_ref.18d), @Int.as.Copy.impl [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .F = %F.decl // CHECK:STDOUT: .D = %D.decl // CHECK:STDOUT: .Test = %Test.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %F.decl: %F.type = fn_decl @F [concrete = constants.%F] { // CHECK:STDOUT: %T.patt: %pattern_type.98f = symbolic_binding_pattern T, 0, template [concrete] // CHECK:STDOUT: %x.patt: @F.%pattern_type (%pattern_type.51d) = value_binding_pattern x [concrete] // CHECK:STDOUT: %x.param_patt: @F.%pattern_type (%pattern_type.51d) = value_param_pattern %x.patt [concrete] // CHECK:STDOUT: %return.patt: %pattern_type.7ce = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: %pattern_type.7ce = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %i32.loc4: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %.loc4_34: Core.Form = init_form %i32.loc4 [concrete = constants.%.ff5] // CHECK:STDOUT: %.loc4_19.1: type = splice_block %.loc4_19.2 [concrete = type] { // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %.loc4_19.2: type = type_literal type [concrete = type] // CHECK:STDOUT: } // CHECK:STDOUT: %T.loc4_15.2: type = symbolic_binding T, 0, template [template = %T.loc4_15.1 (constants.%T.67db0b.1)] // CHECK:STDOUT: %x.param: @F.%T.loc4_15.1 (%T.67db0b.1) = value_param call_param0 // CHECK:STDOUT: %T.ref: type = name_ref T, %T.loc4_15.2 [template = %T.loc4_15.1 (constants.%T.67db0b.1)] // CHECK:STDOUT: %x: @F.%T.loc4_15.1 (%T.67db0b.1) = value_binding x, %x.param // CHECK:STDOUT: %return.param: ref %i32 = out_param call_param1 // CHECK:STDOUT: %return: ref %i32 = return_slot %return.param // CHECK:STDOUT: } // CHECK:STDOUT: %D.decl: type = class_decl @D [concrete = constants.%D] {} {} // CHECK:STDOUT: %Test.decl: %Test.type = fn_decl @Test [concrete = constants.%Test] { // CHECK:STDOUT: %d.patt: %pattern_type.9c8 = value_binding_pattern d [concrete] // CHECK:STDOUT: %d.param_patt: %pattern_type.9c8 = value_param_pattern %d.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %d.param: %D = value_param call_param0 // CHECK:STDOUT: %D.ref: type = name_ref D, file.%D.decl [concrete = constants.%D] // CHECK:STDOUT: %d: %D = value_binding d, %d.param // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @D { // CHECK:STDOUT: %i32: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %.loc10: %D.elem = field_decl m, element0 [concrete] // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%struct_type.m [concrete = constants.%complete_type.218] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%D // CHECK:STDOUT: .m = %.loc10 // CHECK:STDOUT: .n = // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic fn @F(%T.loc4_15.2: type) { // CHECK:STDOUT: %T.loc4_15.1: type = symbolic_binding T, 0, template [template = %T.loc4_15.1 (constants.%T.67db0b.1)] // CHECK:STDOUT: %pattern_type: type = pattern_type %T.loc4_15.1 [template = %pattern_type (constants.%pattern_type.51d)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete: = require_complete_type %T.loc4_15.1 [template = %require_complete (constants.%require_complete.944)] // CHECK:STDOUT: %.loc5_17.4: = refine_type_action %x.ref, %T.loc4_15.1 [template] // CHECK:STDOUT: %.loc5_17.5: = access_member_action %.loc5_17.1, n [template] // CHECK:STDOUT: %.loc5_17.6: type = type_of_inst %.loc5_17.5 [template] // CHECK:STDOUT: %.loc5_17.7: = convert_to_value_action %.loc5_17.2, constants.%i32 [template] // CHECK:STDOUT: // CHECK:STDOUT: fn(%x.param: @F.%T.loc4_15.1 (%T.67db0b.1)) -> out %return.param: %i32 { // CHECK:STDOUT: !entry: // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %n.patt: %pattern_type.7ce = value_binding_pattern n [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %x.ref: @F.%T.loc4_15.1 (%T.67db0b.1) = name_ref x, %x // CHECK:STDOUT: %.loc5_17.1: @F.%T.loc4_15.1 (%T.67db0b.1) = splice_inst %.loc5_17.4 // CHECK:STDOUT: %.loc5_17.2: @F.%.loc5_17.6 (@F.%.loc5_17.6) = splice_inst %.loc5_17.5 // CHECK:STDOUT: %i32.loc5: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %.loc5_17.3: %i32 = splice_inst %.loc5_17.7 // CHECK:STDOUT: %n: %i32 = value_binding n, %.loc5_17.3 // CHECK:STDOUT: %n.ref: %i32 = name_ref n, %n // CHECK:STDOUT: %impl.elem0: %.8e2 = impl_witness_access constants.%Copy.impl_witness.f17, element0 [concrete = constants.%Int.as.Copy.impl.Op.664] // CHECK:STDOUT: %bound_method.loc6_10.1: = bound_method %n.ref, %impl.elem0 // CHECK:STDOUT: %specific_fn: = specific_function %impl.elem0, @Int.as.Copy.impl.Op(constants.%int_32) [concrete = constants.%Int.as.Copy.impl.Op.specific_fn] // CHECK:STDOUT: %bound_method.loc6_10.2: = bound_method %n.ref, %specific_fn // CHECK:STDOUT: %Int.as.Copy.impl.Op.call: init %i32 = call %bound_method.loc6_10.2(%n.ref) // CHECK:STDOUT: return %Int.as.Copy.impl.Op.call // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @Test(%d.param: %D) { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %F.ref: %F.type = name_ref F, file.%F.decl [concrete = constants.%F] // CHECK:STDOUT: %d.ref: %D = name_ref d, %d // CHECK:STDOUT: %F.specific_fn: = specific_function %F.ref, @F(constants.%D) [concrete = constants.%F.specific_fn] // CHECK:STDOUT: %F.call: init %i32 = call %F.specific_fn(%d.ref) // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @F(constants.%T.67db0b.1) { // CHECK:STDOUT: %T.loc4_15.1 => constants.%T.67db0b.1 // CHECK:STDOUT: %pattern_type => constants.%pattern_type.51d // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @F(constants.%D) { // CHECK:STDOUT: %T.loc4_15.1 => constants.%D // CHECK:STDOUT: %pattern_type => constants.%pattern_type.9c8 // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete => constants.%complete_type.218 // CHECK:STDOUT: %.loc5_17.4 => constants.%inst.as_compatible // CHECK:STDOUT: %.loc5_17.5 => constants.%inst.name_ref // CHECK:STDOUT: %.loc5_17.6 => // CHECK:STDOUT: %.loc5_17.7 => constants.%inst.splice_block // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- fail_member_wrong_type.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %type: type = facet_type [concrete] // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self] // CHECK:STDOUT: %pattern_type.98f: type = pattern_type type [concrete] // CHECK:STDOUT: %T.67db0b.1: type = symbolic_binding T, 0, template [template] // CHECK:STDOUT: %pattern_type.51d1c4.1: type = pattern_type %T.67db0b.1 [template] // CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [concrete] // CHECK:STDOUT: %Int.type: type = generic_class_type @Int [concrete] // CHECK:STDOUT: %Int.generic: %Int.type = struct_value () [concrete] // CHECK:STDOUT: %N: Core.IntLiteral = symbolic_binding N, 0 [symbolic] // CHECK:STDOUT: %i32: type = class_type @Int, @Int(%int_32) [concrete] // CHECK:STDOUT: %.ff5: Core.Form = init_form %i32 [concrete] // CHECK:STDOUT: %pattern_type.7ce: type = pattern_type %i32 [concrete] // CHECK:STDOUT: %F.type: type = fn_type @F.loc4 [concrete] // CHECK:STDOUT: %F.d98: %F.type = struct_value () [concrete] // CHECK:STDOUT: %require_complete.944: = require_complete_type %T.67db0b.1 [template] // CHECK:STDOUT: %Copy.type: type = facet_type <@Copy> [concrete] // CHECK:STDOUT: %Int.as.Copy.impl.Op.type.824: type = fn_type @Int.as.Copy.impl.Op, @Int.as.Copy.impl(%N) [symbolic] // CHECK:STDOUT: %Int.as.Copy.impl.Op.9b9: %Int.as.Copy.impl.Op.type.824 = struct_value () [symbolic] // CHECK:STDOUT: %Copy.impl_witness.f17: = impl_witness imports.%Copy.impl_witness_table.e76, @Int.as.Copy.impl(%int_32) [concrete] // CHECK:STDOUT: %Int.as.Copy.impl.Op.type.546: type = fn_type @Int.as.Copy.impl.Op, @Int.as.Copy.impl(%int_32) [concrete] // CHECK:STDOUT: %Int.as.Copy.impl.Op.664: %Int.as.Copy.impl.Op.type.546 = struct_value () [concrete] // CHECK:STDOUT: %Copy.facet: %Copy.type = facet_value %i32, (%Copy.impl_witness.f17) [concrete] // CHECK:STDOUT: %Copy.WithSelf.Op.type.081: type = fn_type @Copy.WithSelf.Op, @Copy.WithSelf(%Copy.facet) [concrete] // CHECK:STDOUT: %.8e2: type = fn_type_with_self_type %Copy.WithSelf.Op.type.081, %Copy.facet [concrete] // CHECK:STDOUT: %Int.as.Copy.impl.Op.specific_fn: = specific_function %Int.as.Copy.impl.Op.664, @Int.as.Copy.impl.Op(%int_32) [concrete] // CHECK:STDOUT: %E: type = class_type @E [concrete] // CHECK:STDOUT: %F.c40: type = class_type @F.loc10 [concrete] // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %complete_type.357: = complete_type_witness %empty_struct_type [concrete] // CHECK:STDOUT: %E.elem: type = unbound_element_type %E, %F.c40 [concrete] // CHECK:STDOUT: %struct_type.n.cae: type = struct_type {.n: %F.c40} [concrete] // CHECK:STDOUT: %complete_type.7a8: = complete_type_witness %struct_type.n.cae [concrete] // CHECK:STDOUT: %pattern_type.99f: type = pattern_type %E [concrete] // CHECK:STDOUT: %Test.type: type = fn_type @Test [concrete] // CHECK:STDOUT: %Test: %Test.type = struct_value () [concrete] // CHECK:STDOUT: %F.specific_fn: = specific_function %F.d98, @F.loc4(%E) [concrete] // CHECK:STDOUT: %inst.as_compatible: = inst_value [concrete] { // CHECK:STDOUT: %.988: %E = as_compatible @F.loc4.%x.ref // CHECK:STDOUT: } // CHECK:STDOUT: %inst.splice_block.518: = inst_value [concrete] { // CHECK:STDOUT: %.619: %F.c40 = splice_block %.cde { // CHECK:STDOUT: %n.ref: %E.elem = name_ref n, @E.%.loc11 [concrete = @E.%.loc11] // CHECK:STDOUT: %.4af: ref %F.c40 = class_element_access %.988, element0 // CHECK:STDOUT: %.cde: %F.c40 = acquire_value %.4af // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: %ImplicitAs.type.cc7: type = generic_interface_type @ImplicitAs [concrete] // CHECK:STDOUT: %ImplicitAs.generic: %ImplicitAs.type.cc7 = struct_value () [concrete] // CHECK:STDOUT: %inst.splice_block.34e: = inst_value [concrete] { // CHECK:STDOUT: %.a50: = splice_block [concrete = ] { // CHECK:STDOUT: %.ea5: %i32 = converted %.619, [concrete = ] // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: .Int = %Core.Int // CHECK:STDOUT: .Copy = %Core.Copy // CHECK:STDOUT: .ImplicitAs = %Core.ImplicitAs // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: %Core.Int: %Int.type = import_ref Core//prelude/parts/int, Int, loaded [concrete = constants.%Int.generic] // CHECK:STDOUT: %Core.Copy: type = import_ref Core//prelude/parts/copy, Copy, loaded [concrete = constants.%Copy.type] // CHECK:STDOUT: %Core.import_ref.18d: @Int.as.Copy.impl.%Int.as.Copy.impl.Op.type (%Int.as.Copy.impl.Op.type.824) = import_ref Core//prelude/parts/int, loc{{\d+_\d+}}, loaded [symbolic = @Int.as.Copy.impl.%Int.as.Copy.impl.Op (constants.%Int.as.Copy.impl.Op.9b9)] // CHECK:STDOUT: %Copy.impl_witness_table.e76 = impl_witness_table (%Core.import_ref.18d), @Int.as.Copy.impl [concrete] // CHECK:STDOUT: %Core.ImplicitAs: %ImplicitAs.type.cc7 = import_ref Core//prelude/parts/as, ImplicitAs, loaded [concrete = constants.%ImplicitAs.generic] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .F = %F.decl // CHECK:STDOUT: .E = %E.decl // CHECK:STDOUT: .Test = %Test.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %F.decl: %F.type = fn_decl @F.loc4 [concrete = constants.%F.d98] { // CHECK:STDOUT: %T.patt: %pattern_type.98f = symbolic_binding_pattern T, 0, template [concrete] // CHECK:STDOUT: %x.patt: @F.loc4.%pattern_type (%pattern_type.51d1c4.1) = value_binding_pattern x [concrete] // CHECK:STDOUT: %x.param_patt: @F.loc4.%pattern_type (%pattern_type.51d1c4.1) = value_param_pattern %x.patt [concrete] // CHECK:STDOUT: %return.patt: %pattern_type.7ce = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: %pattern_type.7ce = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %i32.loc4: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %.loc4_34: Core.Form = init_form %i32.loc4 [concrete = constants.%.ff5] // CHECK:STDOUT: %.loc4_19.1: type = splice_block %.loc4_19.2 [concrete = type] { // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %.loc4_19.2: type = type_literal type [concrete = type] // CHECK:STDOUT: } // CHECK:STDOUT: %T.loc4_15.2: type = symbolic_binding T, 0, template [template = %T.loc4_15.1 (constants.%T.67db0b.1)] // CHECK:STDOUT: %x.param: @F.loc4.%T.loc4_15.1 (%T.67db0b.1) = value_param call_param0 // CHECK:STDOUT: %T.ref: type = name_ref T, %T.loc4_15.2 [template = %T.loc4_15.1 (constants.%T.67db0b.1)] // CHECK:STDOUT: %x: @F.loc4.%T.loc4_15.1 (%T.67db0b.1) = value_binding x, %x.param // CHECK:STDOUT: %return.param: ref %i32 = out_param call_param1 // CHECK:STDOUT: %return: ref %i32 = return_slot %return.param // CHECK:STDOUT: } // CHECK:STDOUT: %E.decl: type = class_decl @E [concrete = constants.%E] {} {} // CHECK:STDOUT: %Test.decl: %Test.type = fn_decl @Test [concrete = constants.%Test] { // CHECK:STDOUT: %e.patt: %pattern_type.99f = value_binding_pattern e [concrete] // CHECK:STDOUT: %e.param_patt: %pattern_type.99f = value_param_pattern %e.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %e.param: %E = value_param call_param0 // CHECK:STDOUT: %E.ref: type = name_ref E, file.%E.decl [concrete = constants.%E] // CHECK:STDOUT: %e: %E = value_binding e, %e.param // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @E { // CHECK:STDOUT: %F.decl: type = class_decl @F.loc10 [concrete = constants.%F.c40] {} {} // CHECK:STDOUT: %F.ref: type = name_ref F, %F.decl [concrete = constants.%F.c40] // CHECK:STDOUT: %.loc11: %E.elem = field_decl n, element0 [concrete] // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%struct_type.n.cae [concrete = constants.%complete_type.7a8] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%E // CHECK:STDOUT: .F = %F.decl // CHECK:STDOUT: .n = %.loc11 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @F.loc10 { // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type.357] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%F.c40 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic fn @F.loc4(%T.loc4_15.2: type) { // CHECK:STDOUT: %T.loc4_15.1: type = symbolic_binding T, 0, template [template = %T.loc4_15.1 (constants.%T.67db0b.1)] // CHECK:STDOUT: %pattern_type: type = pattern_type %T.loc4_15.1 [template = %pattern_type (constants.%pattern_type.51d1c4.1)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete: = require_complete_type %T.loc4_15.1 [template = %require_complete (constants.%require_complete.944)] // CHECK:STDOUT: %.loc5_17.4: = refine_type_action %x.ref, %T.loc4_15.1 [template] // CHECK:STDOUT: %.loc5_17.5: = access_member_action %.loc5_17.1, n [template] // CHECK:STDOUT: %.loc5_17.6: type = type_of_inst %.loc5_17.5 [template] // CHECK:STDOUT: %.loc5_17.7: = convert_to_value_action %.loc5_17.2, constants.%i32 [template] // CHECK:STDOUT: // CHECK:STDOUT: fn(%x.param: @F.loc4.%T.loc4_15.1 (%T.67db0b.1)) -> out %return.param: %i32 { // CHECK:STDOUT: !entry: // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %n.patt: %pattern_type.7ce = value_binding_pattern n [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %x.ref: @F.loc4.%T.loc4_15.1 (%T.67db0b.1) = name_ref x, %x // CHECK:STDOUT: %.loc5_17.1: @F.loc4.%T.loc4_15.1 (%T.67db0b.1) = splice_inst %.loc5_17.4 // CHECK:STDOUT: %.loc5_17.2: @F.loc4.%.loc5_17.6 (@F.loc4.%.loc5_17.6) = splice_inst %.loc5_17.5 // CHECK:STDOUT: %i32.loc5: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %.loc5_17.3: %i32 = splice_inst %.loc5_17.7 // CHECK:STDOUT: %n: %i32 = value_binding n, %.loc5_17.3 // CHECK:STDOUT: %n.ref: %i32 = name_ref n, %n // CHECK:STDOUT: %impl.elem0: %.8e2 = impl_witness_access constants.%Copy.impl_witness.f17, element0 [concrete = constants.%Int.as.Copy.impl.Op.664] // CHECK:STDOUT: %bound_method.loc6_10.1: = bound_method %n.ref, %impl.elem0 // CHECK:STDOUT: %specific_fn: = specific_function %impl.elem0, @Int.as.Copy.impl.Op(constants.%int_32) [concrete = constants.%Int.as.Copy.impl.Op.specific_fn] // CHECK:STDOUT: %bound_method.loc6_10.2: = bound_method %n.ref, %specific_fn // CHECK:STDOUT: %Int.as.Copy.impl.Op.call: init %i32 = call %bound_method.loc6_10.2(%n.ref) // CHECK:STDOUT: return %Int.as.Copy.impl.Op.call // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @Test(%e.param: %E) { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %F.ref: %F.type = name_ref F, file.%F.decl [concrete = constants.%F.d98] // CHECK:STDOUT: %e.ref: %E = name_ref e, %e // CHECK:STDOUT: %F.specific_fn: = specific_function %F.ref, @F.loc4(constants.%E) [concrete = constants.%F.specific_fn] // CHECK:STDOUT: %F.call: init %i32 = call %F.specific_fn(%e.ref) // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @F.loc4(constants.%T.67db0b.1) { // CHECK:STDOUT: %T.loc4_15.1 => constants.%T.67db0b.1 // CHECK:STDOUT: %pattern_type => constants.%pattern_type.51d1c4.1 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @F.loc4(constants.%E) { // CHECK:STDOUT: %T.loc4_15.1 => constants.%E // CHECK:STDOUT: %pattern_type => constants.%pattern_type.99f // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete => constants.%complete_type.7a8 // CHECK:STDOUT: %.loc5_17.4 => constants.%inst.as_compatible // CHECK:STDOUT: %.loc5_17.5 => constants.%inst.splice_block.518 // CHECK:STDOUT: %.loc5_17.6 => constants.%F.c40 // CHECK:STDOUT: %.loc5_17.7 => constants.%inst.splice_block.34e // CHECK:STDOUT: } // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/generic/template/unimplemented.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/full.carbon // TODO: Add ranges and switch to "--dump-sem-ir-ranges=only". // EXTRA-ARGS: --dump-sem-ir-ranges=if-present // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/generic/template/unimplemented.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/generic/template/unimplemented.carbon // --- fail_todo_unimplemented_operator.carbon library "[[@TEST_NAME]]"; // Check that we get a reasonable diagnostic for an unimplemented operation on a // template dependent expression. fn F[template T:! type](x: T) -> i32 { // CHECK:STDERR: fail_todo_unimplemented_operator.carbon:[[@LINE+4]]:10: error: cannot access member of interface `Core.MulWith(Core.IntLiteral)` in type `` that does not implement that interface [MissingImplInMemberAccess] // CHECK:STDERR: return x.n * 3; // CHECK:STDERR: ^~~~~~~ // CHECK:STDERR: return x.n * 3; } // --- fail_todo_unimplemented_value.carbon library "[[@TEST_NAME]]"; class C { var n: i32; } // Check that we get a reasonable diagnostic for an unimplemented operation on a // template dependent value where the type is concrete but determined through // the template dependent value. fn F(template c:! C) -> i32 { // CHECK:STDERR: fail_todo_unimplemented_value.carbon:[[@LINE+4]]:10: error: cannot access member of interface `Core.MulWith(Core.IntLiteral)` in type `` that does not implement that interface [MissingImplInMemberAccess] // CHECK:STDERR: return c.n * 3; // CHECK:STDERR: ^~~~~~~ // CHECK:STDERR: return c.n * 3; } // --- fail_todo_unimplemented_convert.carbon library "[[@TEST_NAME]]"; fn F[template T:! Core.Destroy](x: T) { // TODO: These diagnostics aren't very good, and we should only produce one error here. // CHECK:STDERR: fail_todo_unimplemented_convert.carbon:[[@LINE+8]]:3: error: member name of type `` in compound member access is not an instance member or an interface member [CompoundMemberAccessDoesNotUseBase] // CHECK:STDERR: var unused v: T = 0; // CHECK:STDERR: ^~~~~~~~~~~~~~~ // CHECK:STDERR: // CHECK:STDERR: fail_todo_unimplemented_convert.carbon:[[@LINE+4]]:3: error: value of type `` is not callable [CallToNonCallable] // CHECK:STDERR: var unused v: T = 0; // CHECK:STDERR: ^~~~~~~~~~~~~~~ // CHECK:STDERR: var unused v: T = 0; // CHECK:STDERR: fail_todo_unimplemented_convert.carbon:[[@LINE+7]]:3: error: cannot implicitly convert expression of type `T` to `i32` [ConversionFailure] // CHECK:STDERR: var unused w: i32 = x; // CHECK:STDERR: ^~~~~~~~~~~~~~~~~ // CHECK:STDERR: fail_todo_unimplemented_convert.carbon:[[@LINE+4]]:3: note: type `T` does not implement interface `Core.ImplicitAs(i32)` [MissingImplInMemberAccessInContext] // CHECK:STDERR: var unused w: i32 = x; // CHECK:STDERR: ^~~~~~~~~~~~~~~~~ // CHECK:STDERR: var unused w: i32 = x; } // CHECK:STDOUT: --- fail_todo_unimplemented_operator.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %type: type = facet_type [concrete] // CHECK:STDOUT: %.Self.c39: %type = symbolic_binding .Self [symbolic_self] // CHECK:STDOUT: %pattern_type.98f: type = pattern_type type [concrete] // CHECK:STDOUT: %T: type = symbolic_binding T, 0, template [template] // CHECK:STDOUT: %pattern_type.51d1c4.1: type = pattern_type %T [template] // CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [concrete] // CHECK:STDOUT: %Int.type: type = generic_class_type @Int [concrete] // CHECK:STDOUT: %Int.generic: %Int.type = struct_value () [concrete] // CHECK:STDOUT: %i32: type = class_type @Int, @Int(%int_32) [concrete] // CHECK:STDOUT: %.ff5: Core.Form = init_form %i32 [concrete] // CHECK:STDOUT: %pattern_type.7ce: type = pattern_type %i32 [concrete] // CHECK:STDOUT: %F.type: type = fn_type @F [concrete] // CHECK:STDOUT: %F: %F.type = struct_value () [concrete] // CHECK:STDOUT: %require_complete.944: = require_complete_type %T [template] // CHECK:STDOUT: %int_3: Core.IntLiteral = int_value 3 [concrete] // CHECK:STDOUT: %MulWith.type.8b4: type = generic_interface_type @MulWith [concrete] // CHECK:STDOUT: %MulWith.generic: %MulWith.type.8b4 = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: .Int = %Core.Int // CHECK:STDOUT: .MulWith = %Core.MulWith // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: %Core.Int: %Int.type = import_ref Core//prelude/types/int, Int, loaded [concrete = constants.%Int.generic] // CHECK:STDOUT: %Core.MulWith: %MulWith.type.8b4 = import_ref Core//prelude/operators/arithmetic, MulWith, loaded [concrete = constants.%MulWith.generic] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .F = %F.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %F.decl: %F.type = fn_decl @F [concrete = constants.%F] { // CHECK:STDOUT: %T.patt: %pattern_type.98f = symbolic_binding_pattern T, 0, template [concrete] // CHECK:STDOUT: %x.patt: @F.%pattern_type (%pattern_type.51d1c4.1) = value_binding_pattern x [concrete] // CHECK:STDOUT: %x.param_patt: @F.%pattern_type (%pattern_type.51d1c4.1) = value_param_pattern %x.patt [concrete] // CHECK:STDOUT: %return.patt: %pattern_type.7ce = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: %pattern_type.7ce = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %i32: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %.loc6_34: Core.Form = init_form %i32 [concrete = constants.%.ff5] // CHECK:STDOUT: %.loc6_19.1: type = splice_block %.loc6_19.2 [concrete = type] { // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self = constants.%.Self.c39] // CHECK:STDOUT: %.loc6_19.2: type = type_literal type [concrete = type] // CHECK:STDOUT: } // CHECK:STDOUT: %T.loc6_15.2: type = symbolic_binding T, 0, template [template = %T.loc6_15.1 (constants.%T)] // CHECK:STDOUT: %x.param: @F.%T.loc6_15.1 (%T) = value_param call_param0 // CHECK:STDOUT: %T.ref: type = name_ref T, %T.loc6_15.2 [template = %T.loc6_15.1 (constants.%T)] // CHECK:STDOUT: %x: @F.%T.loc6_15.1 (%T) = value_binding x, %x.param // CHECK:STDOUT: %return.param: ref %i32 = out_param call_param1 // CHECK:STDOUT: %return: ref %i32 = return_slot %return.param // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic fn @F(%T.loc6_15.2: type) { // CHECK:STDOUT: %T.loc6_15.1: type = symbolic_binding T, 0, template [template = %T.loc6_15.1 (constants.%T)] // CHECK:STDOUT: %pattern_type: type = pattern_type %T.loc6_15.1 [template = %pattern_type (constants.%pattern_type.51d1c4.1)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete: = require_complete_type %T.loc6_15.1 [template = %require_complete (constants.%require_complete.944)] // CHECK:STDOUT: %.loc11_11.3: = refine_type_action %x.ref, %T.loc6_15.1 [template] // CHECK:STDOUT: %.loc11_11.4: = access_member_action %.loc11_11.1, n [template] // CHECK:STDOUT: %.loc11_11.5: type = type_of_inst %.loc11_11.4 [template] // CHECK:STDOUT: // CHECK:STDOUT: fn(%x.param: @F.%T.loc6_15.1 (%T)) -> out %return.param: %i32 { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %x.ref: @F.%T.loc6_15.1 (%T) = name_ref x, %x // CHECK:STDOUT: %.loc11_11.1: @F.%T.loc6_15.1 (%T) = splice_inst %.loc11_11.3 // CHECK:STDOUT: %.loc11_11.2: @F.%.loc11_11.5 (@F.%.loc11_11.5) = splice_inst %.loc11_11.4 // CHECK:STDOUT: %int_3: Core.IntLiteral = int_value 3 [concrete = constants.%int_3] // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @F(constants.%T) { // CHECK:STDOUT: %T.loc6_15.1 => constants.%T // CHECK:STDOUT: %pattern_type => constants.%pattern_type.51d1c4.1 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- fail_todo_unimplemented_value.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %C: type = class_type @C [concrete] // CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [concrete] // CHECK:STDOUT: %Int.type: type = generic_class_type @Int [concrete] // CHECK:STDOUT: %Int.generic: %Int.type = struct_value () [concrete] // CHECK:STDOUT: %i32: type = class_type @Int, @Int(%int_32) [concrete] // CHECK:STDOUT: %C.elem: type = unbound_element_type %C, %i32 [concrete] // CHECK:STDOUT: %struct_type.n: type = struct_type {.n: %i32} [concrete] // CHECK:STDOUT: %complete_type.54b: = complete_type_witness %struct_type.n [concrete] // CHECK:STDOUT: %type: type = facet_type [concrete] // CHECK:STDOUT: %.Self.c39: %type = symbolic_binding .Self [symbolic_self] // CHECK:STDOUT: %pattern_type.7c7: type = pattern_type %C [concrete] // CHECK:STDOUT: %c: %C = symbolic_binding c, 0, template [template] // CHECK:STDOUT: %.ff5: Core.Form = init_form %i32 [concrete] // CHECK:STDOUT: %pattern_type.7ce: type = pattern_type %i32 [concrete] // CHECK:STDOUT: %F.type: type = fn_type @F [concrete] // CHECK:STDOUT: %F: %F.type = struct_value () [concrete] // CHECK:STDOUT: %int_3: Core.IntLiteral = int_value 3 [concrete] // CHECK:STDOUT: %MulWith.type.8b4: type = generic_interface_type @MulWith [concrete] // CHECK:STDOUT: %MulWith.generic: %MulWith.type.8b4 = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: .Int = %Core.Int // CHECK:STDOUT: .MulWith = %Core.MulWith // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: %Core.Int: %Int.type = import_ref Core//prelude/types/int, Int, loaded [concrete = constants.%Int.generic] // CHECK:STDOUT: %Core.MulWith: %MulWith.type.8b4 = import_ref Core//prelude/operators/arithmetic, MulWith, loaded [concrete = constants.%MulWith.generic] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .C = %C.decl // CHECK:STDOUT: .F = %F.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %C.decl: type = class_decl @C [concrete = constants.%C] {} {} // CHECK:STDOUT: %F.decl: %F.type = fn_decl @F [concrete = constants.%F] { // CHECK:STDOUT: %c.patt: %pattern_type.7c7 = symbolic_binding_pattern c, 0, template [concrete] // CHECK:STDOUT: %return.patt: %pattern_type.7ce = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: %pattern_type.7ce = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %i32: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %.loc11_25: Core.Form = init_form %i32 [concrete = constants.%.ff5] // CHECK:STDOUT: %.loc11_19: type = splice_block %C.ref [concrete = constants.%C] { // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self = constants.%.Self.c39] // CHECK:STDOUT: %C.ref: type = name_ref C, file.%C.decl [concrete = constants.%C] // CHECK:STDOUT: } // CHECK:STDOUT: %c.loc11_15.2: %C = symbolic_binding c, 0, template [template = %c.loc11_15.1 (constants.%c)] // CHECK:STDOUT: %return.param: ref %i32 = out_param call_param0 // CHECK:STDOUT: %return: ref %i32 = return_slot %return.param // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @C { // CHECK:STDOUT: %i32: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %.loc5: %C.elem = field_decl n, element0 [concrete] // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%struct_type.n [concrete = constants.%complete_type.54b] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%C // CHECK:STDOUT: .n = %.loc5 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic fn @F(%c.loc11_15.2: %C) { // CHECK:STDOUT: %c.loc11_15.1: %C = symbolic_binding c, 0, template [template = %c.loc11_15.1 (constants.%c)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %.loc16_11.2: = access_member_action %c.ref, n [template] // CHECK:STDOUT: %.loc16_11.3: type = type_of_inst %.loc16_11.2 [template] // CHECK:STDOUT: // CHECK:STDOUT: fn() -> out %return.param: %i32 { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %c.ref: %C = name_ref c, %c.loc11_15.2 [template = %c.loc11_15.1 (constants.%c)] // CHECK:STDOUT: %.loc16_11.1: @F.%.loc16_11.3 (@F.%.loc16_11.3) = splice_inst %.loc16_11.2 // CHECK:STDOUT: %int_3: Core.IntLiteral = int_value 3 [concrete = constants.%int_3] // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @F(constants.%c) { // CHECK:STDOUT: %c.loc11_15.1 => constants.%c // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- fail_todo_unimplemented_convert.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %type: type = facet_type [concrete] // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self] // CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] // CHECK:STDOUT: %Destroy.type: type = facet_type <@Destroy> [concrete] // CHECK:STDOUT: %pattern_type.cac: type = pattern_type %Destroy.type [concrete] // CHECK:STDOUT: %T.765: %Destroy.type = symbolic_binding T, 0, template [template] // CHECK:STDOUT: %T.binding.as_type.140: type = symbolic_binding_type T, 0, template, %T.765 [template] // CHECK:STDOUT: %pattern_type.a64db1.2: type = pattern_type %T.binding.as_type.140 [template] // CHECK:STDOUT: %F.type: type = fn_type @F [concrete] // CHECK:STDOUT: %F: %F.type = struct_value () [concrete] // CHECK:STDOUT: %require_complete.6d1: = require_complete_type %T.binding.as_type.140 [template] // CHECK:STDOUT: %int_0: Core.IntLiteral = int_value 0 [concrete] // CHECK:STDOUT: %ImplicitAs.type.cc7: type = generic_interface_type @ImplicitAs [concrete] // CHECK:STDOUT: %ImplicitAs.generic: %ImplicitAs.type.cc7 = struct_value () [concrete] // CHECK:STDOUT: %ImplicitAs.type.112: type = facet_type <@ImplicitAs, @ImplicitAs(%T.binding.as_type.140)> [template] // CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [concrete] // CHECK:STDOUT: %Int.type: type = generic_class_type @Int [concrete] // CHECK:STDOUT: %Int.generic: %Int.type = struct_value () [concrete] // CHECK:STDOUT: %i32: type = class_type @Int, @Int(%int_32) [concrete] // CHECK:STDOUT: %pattern_type.7ce: type = pattern_type %i32 [concrete] // CHECK:STDOUT: %Destroy.Op.type: type = fn_type @Destroy.Op [concrete] // CHECK:STDOUT: %Destroy.Op: %Destroy.Op.type = struct_value () [concrete] // CHECK:STDOUT: %Destroy.lookup_impl_witness: = lookup_impl_witness %T.765, @Destroy [template] // CHECK:STDOUT: %Destroy.WithSelf.Op.type.cb2e47.2: type = fn_type @Destroy.WithSelf.Op, @Destroy.WithSelf(%T.765) [template] // CHECK:STDOUT: %.d5f: type = fn_type_with_self_type %Destroy.WithSelf.Op.type.cb2e47.2, %T.765 [template] // CHECK:STDOUT: %impl.elem0.a6b: %.d5f = impl_witness_access %Destroy.lookup_impl_witness, element0 [template] // CHECK:STDOUT: %specific_impl_fn.761: = specific_impl_function %impl.elem0.a6b, @Destroy.WithSelf.Op(%T.765) [template] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: .Destroy = %Core.Destroy // CHECK:STDOUT: .ImplicitAs = %Core.ImplicitAs // CHECK:STDOUT: .Int = %Core.Int // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: %Core.Destroy: type = import_ref Core//prelude/destroy, Destroy, loaded [concrete = constants.%Destroy.type] // CHECK:STDOUT: %Core.ImplicitAs: %ImplicitAs.type.cc7 = import_ref Core//prelude/operators/as, ImplicitAs, loaded [concrete = constants.%ImplicitAs.generic] // CHECK:STDOUT: %Core.Int: %Int.type = import_ref Core//prelude/types/int, Int, loaded [concrete = constants.%Int.generic] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .F = %F.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %F.decl: %F.type = fn_decl @F [concrete = constants.%F] { // CHECK:STDOUT: %T.patt: %pattern_type.cac = symbolic_binding_pattern T, 0, template [concrete] // CHECK:STDOUT: %x.patt: @F.%pattern_type (%pattern_type.a64db1.2) = value_binding_pattern x [concrete] // CHECK:STDOUT: %x.param_patt: @F.%pattern_type (%pattern_type.a64db1.2) = value_param_pattern %x.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %.loc4_23: type = splice_block %Destroy.ref [concrete = constants.%Destroy.type] { // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %Core.ref: = name_ref Core, imports.%Core [concrete = imports.%Core] // CHECK:STDOUT: %Destroy.ref: type = name_ref Destroy, imports.%Core.Destroy [concrete = constants.%Destroy.type] // CHECK:STDOUT: } // CHECK:STDOUT: %T.loc4_15.2: %Destroy.type = symbolic_binding T, 0, template [template = %T.loc4_15.1 (constants.%T.765)] // CHECK:STDOUT: %x.param: @F.%T.binding.as_type (%T.binding.as_type.140) = value_param call_param0 // CHECK:STDOUT: %.loc4_36.1: type = splice_block %.loc4_36.2 [template = %T.binding.as_type (constants.%T.binding.as_type.140)] { // CHECK:STDOUT: %T.ref.loc4: %Destroy.type = name_ref T, %T.loc4_15.2 [template = %T.loc4_15.1 (constants.%T.765)] // CHECK:STDOUT: %T.as_type.loc4: type = facet_access_type %T.ref.loc4 [template = %T.binding.as_type (constants.%T.binding.as_type.140)] // CHECK:STDOUT: %.loc4_36.2: type = converted %T.ref.loc4, %T.as_type.loc4 [template = %T.binding.as_type (constants.%T.binding.as_type.140)] // CHECK:STDOUT: } // CHECK:STDOUT: %x: @F.%T.binding.as_type (%T.binding.as_type.140) = value_binding x, %x.param // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic fn @F(%T.loc4_15.2: %Destroy.type) { // CHECK:STDOUT: %T.loc4_15.1: %Destroy.type = symbolic_binding T, 0, template [template = %T.loc4_15.1 (constants.%T.765)] // CHECK:STDOUT: %T.binding.as_type: type = symbolic_binding_type T, 0, template, %T.loc4_15.1 [template = %T.binding.as_type (constants.%T.binding.as_type.140)] // CHECK:STDOUT: %pattern_type: type = pattern_type %T.binding.as_type [template = %pattern_type (constants.%pattern_type.a64db1.2)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete: = require_complete_type %T.binding.as_type [template = %require_complete (constants.%require_complete.6d1)] // CHECK:STDOUT: %ImplicitAs.type.loc14_3.2: type = facet_type <@ImplicitAs, @ImplicitAs(%T.binding.as_type)> [template = %ImplicitAs.type.loc14_3.2 (constants.%ImplicitAs.type.112)] // CHECK:STDOUT: %.loc14_3.3: = access_member_action %ImplicitAs.type.loc14_3.1, Convert [template] // CHECK:STDOUT: %.loc14_3.4: type = type_of_inst %.loc14_3.3 [template] // CHECK:STDOUT: %Destroy.lookup_impl_witness: = lookup_impl_witness %T.loc4_15.1, @Destroy [template = %Destroy.lookup_impl_witness (constants.%Destroy.lookup_impl_witness)] // CHECK:STDOUT: %Destroy.WithSelf.Op.type: type = fn_type @Destroy.WithSelf.Op, @Destroy.WithSelf(%T.loc4_15.1) [template = %Destroy.WithSelf.Op.type (constants.%Destroy.WithSelf.Op.type.cb2e47.2)] // CHECK:STDOUT: %.loc14_3.5: type = fn_type_with_self_type %Destroy.WithSelf.Op.type, %T.loc4_15.1 [template = %.loc14_3.5 (constants.%.d5f)] // CHECK:STDOUT: %impl.elem0.loc14_3.2: @F.%.loc14_3.5 (%.d5f) = impl_witness_access %Destroy.lookup_impl_witness, element0 [template = %impl.elem0.loc14_3.2 (constants.%impl.elem0.a6b)] // CHECK:STDOUT: %specific_impl_fn.loc14_3.2: = specific_impl_function %impl.elem0.loc14_3.2, @Destroy.WithSelf.Op(%T.loc4_15.1) [template = %specific_impl_fn.loc14_3.2 (constants.%specific_impl_fn.761)] // CHECK:STDOUT: // CHECK:STDOUT: fn(%x.param: @F.%T.binding.as_type (%T.binding.as_type.140)) { // CHECK:STDOUT: !entry: // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %v.patt: @F.%pattern_type (%pattern_type.a64db1.2) = ref_binding_pattern v [concrete] // CHECK:STDOUT: %v.var_patt: @F.%pattern_type (%pattern_type.a64db1.2) = var_pattern %v.patt [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %v.var: ref @F.%T.binding.as_type (%T.binding.as_type.140) = var %v.var_patt // CHECK:STDOUT: %int_0: Core.IntLiteral = int_value 0 [concrete = constants.%int_0] // CHECK:STDOUT: %ImplicitAs.type.loc14_3.1: type = facet_type <@ImplicitAs, @ImplicitAs(constants.%T.binding.as_type.140)> [template = %ImplicitAs.type.loc14_3.2 (constants.%ImplicitAs.type.112)] // CHECK:STDOUT: %.loc14_3.1: @F.%.loc14_3.4 (@F.%.loc14_3.4) = splice_inst %.loc14_3.3 // CHECK:STDOUT: %.loc14_3.2: @F.%T.binding.as_type (%T.binding.as_type.140) = converted %int_0, [concrete = ] // CHECK:STDOUT: assign %v.var, // CHECK:STDOUT: %.loc14_17.1: type = splice_block %.loc14_17.2 [template = %T.binding.as_type (constants.%T.binding.as_type.140)] { // CHECK:STDOUT: %T.ref.loc14: %Destroy.type = name_ref T, %T.loc4_15.2 [template = %T.loc4_15.1 (constants.%T.765)] // CHECK:STDOUT: %T.as_type.loc14: type = facet_access_type %T.ref.loc14 [template = %T.binding.as_type (constants.%T.binding.as_type.140)] // CHECK:STDOUT: %.loc14_17.2: type = converted %T.ref.loc14, %T.as_type.loc14 [template = %T.binding.as_type (constants.%T.binding.as_type.140)] // CHECK:STDOUT: } // CHECK:STDOUT: %v: ref @F.%T.binding.as_type (%T.binding.as_type.140) = ref_binding v, %v.var // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %w.patt: %pattern_type.7ce = ref_binding_pattern w [concrete] // CHECK:STDOUT: %w.var_patt: %pattern_type.7ce = var_pattern %w.patt [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %w.var: ref %i32 = var %w.var_patt // CHECK:STDOUT: %x.ref: @F.%T.binding.as_type (%T.binding.as_type.140) = name_ref x, %x // CHECK:STDOUT: %.loc22: %i32 = converted %x.ref, [concrete = ] // CHECK:STDOUT: assign %w.var, // CHECK:STDOUT: %i32: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %w: ref %i32 = ref_binding w, %w.var // CHECK:STDOUT: %Destroy.Op.bound: = bound_method %w.var, constants.%Destroy.Op // CHECK:STDOUT: %Destroy.Op.call: init %empty_tuple.type = call %Destroy.Op.bound(%w.var) // CHECK:STDOUT: %impl.elem0.loc14_3.1: @F.%.loc14_3.5 (%.d5f) = impl_witness_access constants.%Destroy.lookup_impl_witness, element0 [template = %impl.elem0.loc14_3.2 (constants.%impl.elem0.a6b)] // CHECK:STDOUT: %bound_method.loc14_3.1: = bound_method %v.var, %impl.elem0.loc14_3.1 // CHECK:STDOUT: %specific_impl_fn.loc14_3.1: = specific_impl_function %impl.elem0.loc14_3.1, @Destroy.WithSelf.Op(constants.%T.765) [template = %specific_impl_fn.loc14_3.2 (constants.%specific_impl_fn.761)] // CHECK:STDOUT: %bound_method.loc14_3.2: = bound_method %v.var, %specific_impl_fn.loc14_3.1 // CHECK:STDOUT: %Destroy.WithSelf.Op.call: init %empty_tuple.type = call %bound_method.loc14_3.2(%v.var) // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @Destroy.Op(%self.param: ref %i32) = "no_op"; // CHECK:STDOUT: // CHECK:STDOUT: specific @F(constants.%T.765) { // CHECK:STDOUT: %T.loc4_15.1 => constants.%T.765 // CHECK:STDOUT: %T.binding.as_type => constants.%T.binding.as_type.140 // CHECK:STDOUT: %pattern_type => constants.%pattern_type.a64db1.2 // CHECK:STDOUT: } // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/generic/template_dependence.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/convert.carbon // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/generic/template_dependence.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/generic/template_dependence.carbon // --- type.carbon library "[[@TEST_NAME]]"; //@dump-sem-ir-begin fn F[template T:! type](x: T**) -> T* { return *x; } //@dump-sem-ir-end // --- mixed.carbon library "[[@TEST_NAME]]"; //@dump-sem-ir-begin fn F(template T:! type, U:! type) -> (T, U) { return F(T, U); } //@dump-sem-ir-end // CHECK:STDOUT: --- type.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %pattern_type.98f: type = pattern_type type [concrete] // CHECK:STDOUT: %T.67db0b.1: type = symbolic_binding T, 0, template [template] // CHECK:STDOUT: %ptr.e8f8f9.1: type = ptr_type %T.67db0b.1 [template] // CHECK:STDOUT: %ptr.125: type = ptr_type %ptr.e8f8f9.1 [template] // CHECK:STDOUT: %pattern_type.8bb: type = pattern_type %ptr.125 [template] // CHECK:STDOUT: %.cb6cb9.1: Core.Form = init_form %ptr.e8f8f9.1 [template] // CHECK:STDOUT: %pattern_type.4f4b84.1: type = pattern_type %ptr.e8f8f9.1 [template] // CHECK:STDOUT: %F.type: type = fn_type @F [concrete] // CHECK:STDOUT: %F: %F.type = struct_value () [concrete] // CHECK:STDOUT: %require_complete.fbe: = require_complete_type %ptr.125 [template] // CHECK:STDOUT: %require_complete.ef162c.1: = require_complete_type %ptr.e8f8f9.1 [template] // CHECK:STDOUT: %Copy.type: type = facet_type <@Copy> [concrete] // CHECK:STDOUT: %.2f2: require_specific_def_type = require_specific_def @ptr.as.Copy.impl(%T.67db0b.1) [template] // CHECK:STDOUT: %Copy.lookup_impl_witness.2e6: = lookup_impl_witness %ptr.e8f8f9.1, @Copy [template] // CHECK:STDOUT: %Copy.facet: %Copy.type = facet_value %ptr.e8f8f9.1, (%Copy.lookup_impl_witness.2e6) [template] // CHECK:STDOUT: %Copy.WithSelf.Op.type.d82: type = fn_type @Copy.WithSelf.Op, @Copy.WithSelf(%Copy.facet) [template] // CHECK:STDOUT: %.299: type = fn_type_with_self_type %Copy.WithSelf.Op.type.d82, %Copy.facet [template] // CHECK:STDOUT: %impl.elem0.1c7: %.299 = impl_witness_access %Copy.lookup_impl_witness.2e6, element0 [template] // CHECK:STDOUT: %specific_impl_fn.366: = specific_impl_function %impl.elem0.1c7, @Copy.WithSelf.Op(%Copy.facet) [template] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: %F.decl: %F.type = fn_decl @F [concrete = constants.%F] { // CHECK:STDOUT: %T.patt: %pattern_type.98f = symbolic_binding_pattern T, 0, template [concrete] // CHECK:STDOUT: %x.patt: @F.%pattern_type.loc5_25 (%pattern_type.8bb) = value_binding_pattern x [concrete] // CHECK:STDOUT: %x.param_patt: @F.%pattern_type.loc5_25 (%pattern_type.8bb) = value_param_pattern %x.patt [concrete] // CHECK:STDOUT: %return.patt: @F.%pattern_type.loc5_33 (%pattern_type.4f4b84.1) = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: @F.%pattern_type.loc5_33 (%pattern_type.4f4b84.1) = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %T.ref.loc5_36: type = name_ref T, %T.loc5_15.2 [template = %T.loc5_15.1 (constants.%T.67db0b.1)] // CHECK:STDOUT: %ptr.loc5_37: type = ptr_type %T.ref.loc5_36 [template = %ptr.loc5_29.1 (constants.%ptr.e8f8f9.1)] // CHECK:STDOUT: %.loc5_37.2: Core.Form = init_form %ptr.loc5_37 [template = %.loc5_37.1 (constants.%.cb6cb9.1)] // CHECK:STDOUT: %.loc5_19.1: type = splice_block %.loc5_19.2 [concrete = type] { // CHECK:STDOUT: // CHECK:STDOUT: %.loc5_19.2: type = type_literal type [concrete = type] // CHECK:STDOUT: } // CHECK:STDOUT: %T.loc5_15.2: type = symbolic_binding T, 0, template [template = %T.loc5_15.1 (constants.%T.67db0b.1)] // CHECK:STDOUT: %x.param: @F.%ptr.loc5_30.1 (%ptr.125) = value_param call_param0 // CHECK:STDOUT: %.loc5_30: type = splice_block %ptr.loc5_30.2 [template = %ptr.loc5_30.1 (constants.%ptr.125)] { // CHECK:STDOUT: %T.ref.loc5_28: type = name_ref T, %T.loc5_15.2 [template = %T.loc5_15.1 (constants.%T.67db0b.1)] // CHECK:STDOUT: %ptr.loc5_29.2: type = ptr_type %T.ref.loc5_28 [template = %ptr.loc5_29.1 (constants.%ptr.e8f8f9.1)] // CHECK:STDOUT: %ptr.loc5_30.2: type = ptr_type %ptr.loc5_29.2 [template = %ptr.loc5_30.1 (constants.%ptr.125)] // CHECK:STDOUT: } // CHECK:STDOUT: %x: @F.%ptr.loc5_30.1 (%ptr.125) = value_binding x, %x.param // CHECK:STDOUT: %return.param: ref @F.%ptr.loc5_29.1 (%ptr.e8f8f9.1) = out_param call_param1 // CHECK:STDOUT: %return: ref @F.%ptr.loc5_29.1 (%ptr.e8f8f9.1) = return_slot %return.param // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic fn @F(%T.loc5_15.2: type) { // CHECK:STDOUT: %T.loc5_15.1: type = symbolic_binding T, 0, template [template = %T.loc5_15.1 (constants.%T.67db0b.1)] // CHECK:STDOUT: %ptr.loc5_29.1: type = ptr_type %T.loc5_15.1 [template = %ptr.loc5_29.1 (constants.%ptr.e8f8f9.1)] // CHECK:STDOUT: %ptr.loc5_30.1: type = ptr_type %ptr.loc5_29.1 [template = %ptr.loc5_30.1 (constants.%ptr.125)] // CHECK:STDOUT: %pattern_type.loc5_25: type = pattern_type %ptr.loc5_30.1 [template = %pattern_type.loc5_25 (constants.%pattern_type.8bb)] // CHECK:STDOUT: %.loc5_37.1: Core.Form = init_form %ptr.loc5_29.1 [template = %.loc5_37.1 (constants.%.cb6cb9.1)] // CHECK:STDOUT: %pattern_type.loc5_33: type = pattern_type %ptr.loc5_29.1 [template = %pattern_type.loc5_33 (constants.%pattern_type.4f4b84.1)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete.loc5_26: = require_complete_type %ptr.loc5_30.1 [template = %require_complete.loc5_26 (constants.%require_complete.fbe)] // CHECK:STDOUT: %require_complete.loc5_37: = require_complete_type %ptr.loc5_29.1 [template = %require_complete.loc5_37 (constants.%require_complete.ef162c.1)] // CHECK:STDOUT: %.loc6_10.3: require_specific_def_type = require_specific_def @ptr.as.Copy.impl(%T.loc5_15.1) [template = %.loc6_10.3 (constants.%.2f2)] // CHECK:STDOUT: %Copy.lookup_impl_witness: = lookup_impl_witness %ptr.loc5_29.1, @Copy [template = %Copy.lookup_impl_witness (constants.%Copy.lookup_impl_witness.2e6)] // CHECK:STDOUT: %Copy.facet: %Copy.type = facet_value %ptr.loc5_29.1, (%Copy.lookup_impl_witness) [template = %Copy.facet (constants.%Copy.facet)] // CHECK:STDOUT: %Copy.WithSelf.Op.type: type = fn_type @Copy.WithSelf.Op, @Copy.WithSelf(%Copy.facet) [template = %Copy.WithSelf.Op.type (constants.%Copy.WithSelf.Op.type.d82)] // CHECK:STDOUT: %.loc6_10.4: type = fn_type_with_self_type %Copy.WithSelf.Op.type, %Copy.facet [template = %.loc6_10.4 (constants.%.299)] // CHECK:STDOUT: %impl.elem0.loc6_10.2: @F.%.loc6_10.4 (%.299) = impl_witness_access %Copy.lookup_impl_witness, element0 [template = %impl.elem0.loc6_10.2 (constants.%impl.elem0.1c7)] // CHECK:STDOUT: %specific_impl_fn.loc6_10.2: = specific_impl_function %impl.elem0.loc6_10.2, @Copy.WithSelf.Op(%Copy.facet) [template = %specific_impl_fn.loc6_10.2 (constants.%specific_impl_fn.366)] // CHECK:STDOUT: // CHECK:STDOUT: fn(%x.param: @F.%ptr.loc5_30.1 (%ptr.125)) -> out %return.param: @F.%ptr.loc5_29.1 (%ptr.e8f8f9.1) { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %x.ref: @F.%ptr.loc5_30.1 (%ptr.125) = name_ref x, %x // CHECK:STDOUT: %.loc6_10.1: ref @F.%ptr.loc5_29.1 (%ptr.e8f8f9.1) = deref %x.ref // CHECK:STDOUT: %.loc6_10.2: @F.%ptr.loc5_29.1 (%ptr.e8f8f9.1) = acquire_value %.loc6_10.1 // CHECK:STDOUT: %impl.elem0.loc6_10.1: @F.%.loc6_10.4 (%.299) = impl_witness_access constants.%Copy.lookup_impl_witness.2e6, element0 [template = %impl.elem0.loc6_10.2 (constants.%impl.elem0.1c7)] // CHECK:STDOUT: %bound_method.loc6_10.1: = bound_method %.loc6_10.2, %impl.elem0.loc6_10.1 // CHECK:STDOUT: %specific_impl_fn.loc6_10.1: = specific_impl_function %impl.elem0.loc6_10.1, @Copy.WithSelf.Op(constants.%Copy.facet) [template = %specific_impl_fn.loc6_10.2 (constants.%specific_impl_fn.366)] // CHECK:STDOUT: %bound_method.loc6_10.2: = bound_method %.loc6_10.2, %specific_impl_fn.loc6_10.1 // CHECK:STDOUT: %Copy.WithSelf.Op.call: init @F.%ptr.loc5_29.1 (%ptr.e8f8f9.1) = call %bound_method.loc6_10.2(%.loc6_10.2) // CHECK:STDOUT: return %Copy.WithSelf.Op.call // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @F(constants.%T.67db0b.1) { // CHECK:STDOUT: %T.loc5_15.1 => constants.%T.67db0b.1 // CHECK:STDOUT: %ptr.loc5_29.1 => constants.%ptr.e8f8f9.1 // CHECK:STDOUT: %ptr.loc5_30.1 => constants.%ptr.125 // CHECK:STDOUT: %pattern_type.loc5_25 => constants.%pattern_type.8bb // CHECK:STDOUT: %.loc5_37.1 => constants.%.cb6cb9.1 // CHECK:STDOUT: %pattern_type.loc5_33 => constants.%pattern_type.4f4b84.1 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- mixed.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %pattern_type.98f: type = pattern_type type [concrete] // CHECK:STDOUT: %T: type = symbolic_binding T, 0, template [template] // CHECK:STDOUT: %U: type = symbolic_binding U, 1 [symbolic] // CHECK:STDOUT: %tuple.type.24b: type = tuple_type (type, type) [concrete] // CHECK:STDOUT: %tuple: %tuple.type.24b = tuple_value (%T, %U) [template] // CHECK:STDOUT: %tuple.type.a5e: type = tuple_type (%T, %U) [template] // CHECK:STDOUT: %.f18: Core.Form = init_form %tuple.type.a5e [template] // CHECK:STDOUT: %pattern_type.eee: type = pattern_type %tuple.type.a5e [template] // CHECK:STDOUT: %F.type: type = fn_type @F [concrete] // CHECK:STDOUT: %F: %F.type = struct_value () [concrete] // CHECK:STDOUT: %require_complete: = require_complete_type %tuple.type.a5e [template] // CHECK:STDOUT: %F.specific_fn: = specific_function %F, @F(%T, %U) [template] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: %F.decl: %F.type = fn_decl @F [concrete = constants.%F] { // CHECK:STDOUT: %T.patt: %pattern_type.98f = symbolic_binding_pattern T, 0, template [concrete] // CHECK:STDOUT: %U.patt: %pattern_type.98f = symbolic_binding_pattern U, 1 [concrete] // CHECK:STDOUT: %return.patt: @F.%pattern_type (%pattern_type.eee) = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: @F.%pattern_type (%pattern_type.eee) = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %T.ref.loc5: type = name_ref T, %T.loc5_15.2 [template = %T.loc5_15.1 (constants.%T)] // CHECK:STDOUT: %U.ref.loc5: type = name_ref U, %U.loc5_25.2 [symbolic = %U.loc5_25.1 (constants.%U)] // CHECK:STDOUT: %.loc5_43.3: %tuple.type.24b = tuple_literal (%T.ref.loc5, %U.ref.loc5) [template = %tuple (constants.%tuple)] // CHECK:STDOUT: %.loc5_43.4: type = converted %.loc5_43.3, constants.%tuple.type.a5e [template = %tuple.type (constants.%tuple.type.a5e)] // CHECK:STDOUT: %.loc5_43.5: Core.Form = init_form %.loc5_43.4 [template = %.loc5_43.2 (constants.%.f18)] // CHECK:STDOUT: %.loc5_19.1: type = splice_block %.loc5_19.2 [concrete = type] { // CHECK:STDOUT: // CHECK:STDOUT: %.loc5_19.2: type = type_literal type [concrete = type] // CHECK:STDOUT: } // CHECK:STDOUT: %T.loc5_15.2: type = symbolic_binding T, 0, template [template = %T.loc5_15.1 (constants.%T)] // CHECK:STDOUT: %.loc5_29.1: type = splice_block %.loc5_29.2 [concrete = type] { // CHECK:STDOUT: // CHECK:STDOUT: %.loc5_29.2: type = type_literal type [concrete = type] // CHECK:STDOUT: } // CHECK:STDOUT: %U.loc5_25.2: type = symbolic_binding U, 1 [symbolic = %U.loc5_25.1 (constants.%U)] // CHECK:STDOUT: %return.param: ref @F.%tuple.type (%tuple.type.a5e) = out_param call_param0 // CHECK:STDOUT: %return: ref @F.%tuple.type (%tuple.type.a5e) = return_slot %return.param // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic fn @F(%T.loc5_15.2: type, %U.loc5_25.2: type) { // CHECK:STDOUT: %T.loc5_15.1: type = symbolic_binding T, 0, template [template = %T.loc5_15.1 (constants.%T)] // CHECK:STDOUT: %U.loc5_25.1: type = symbolic_binding U, 1 [symbolic = %U.loc5_25.1 (constants.%U)] // CHECK:STDOUT: %tuple: %tuple.type.24b = tuple_value (%T.loc5_15.1, %U.loc5_25.1) [template = %tuple (constants.%tuple)] // CHECK:STDOUT: %tuple.type: type = tuple_type (%T.loc5_15.1, %U.loc5_25.1) [template = %tuple.type (constants.%tuple.type.a5e)] // CHECK:STDOUT: %.loc5_43.2: Core.Form = init_form %tuple.type [template = %.loc5_43.2 (constants.%.f18)] // CHECK:STDOUT: %pattern_type: type = pattern_type %tuple.type [template = %pattern_type (constants.%pattern_type.eee)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete: = require_complete_type %tuple.type [template = %require_complete (constants.%require_complete)] // CHECK:STDOUT: %F.specific_fn.loc6_10.2: = specific_function constants.%F, @F(%T.loc5_15.1, %U.loc5_25.1) [template = %F.specific_fn.loc6_10.2 (constants.%F.specific_fn)] // CHECK:STDOUT: // CHECK:STDOUT: fn() -> out %return.param: @F.%tuple.type (%tuple.type.a5e) { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %F.ref: %F.type = name_ref F, file.%F.decl [concrete = constants.%F] // CHECK:STDOUT: %T.ref.loc6: type = name_ref T, %T.loc5_15.2 [template = %T.loc5_15.1 (constants.%T)] // CHECK:STDOUT: %U.ref.loc6: type = name_ref U, %U.loc5_25.2 [symbolic = %U.loc5_25.1 (constants.%U)] // CHECK:STDOUT: %F.specific_fn.loc6_10.1: = specific_function %F.ref, @F(constants.%T, constants.%U) [template = %F.specific_fn.loc6_10.2 (constants.%F.specific_fn)] // CHECK:STDOUT: %.loc5_43.1: ref @F.%tuple.type (%tuple.type.a5e) = splice_block %return.param {} // CHECK:STDOUT: %F.call: init @F.%tuple.type (%tuple.type.a5e) to %.loc5_43.1 = call %F.specific_fn.loc6_10.1() // CHECK:STDOUT: return %F.call to %return.param // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @F(constants.%T, constants.%U) { // CHECK:STDOUT: %T.loc5_15.1 => constants.%T // CHECK:STDOUT: %U.loc5_25.1 => constants.%U // CHECK:STDOUT: %tuple => constants.%tuple // CHECK:STDOUT: %tuple.type => constants.%tuple.type.a5e // CHECK:STDOUT: %.loc5_43.2 => constants.%.f18 // CHECK:STDOUT: %pattern_type => constants.%pattern_type.eee // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete => constants.%require_complete // CHECK:STDOUT: %F.specific_fn.loc6_10.2 => constants.%F.specific_fn // CHECK:STDOUT: } // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/global/basics.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/convert.carbon // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/global/basics.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/global/basics.carbon // --- decl.carbon library "[[@TEST_NAME]]"; //@dump-sem-ir-begin var a: {}; //@dump-sem-ir-end // --- simple_init.carbon library "[[@TEST_NAME]]"; //@dump-sem-ir-begin var a: {} = {}; //@dump-sem-ir-end // --- simple_init_with_fn.carbon library "[[@TEST_NAME]]"; fn Make() -> {}; //@dump-sem-ir-begin var a: {} = Make(); //@dump-sem-ir-end // --- class_init.carbon library "[[@TEST_NAME]]"; class A {} //@dump-sem-ir-begin var a: A = {}; //@dump-sem-ir-end // --- class_init_from_fn.carbon library "[[@TEST_NAME]]"; class A {} fn Make() -> A; //@dump-sem-ir-begin var a: A = Make(); //@dump-sem-ir-end // CHECK:STDOUT: --- decl.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %empty_struct: %empty_struct_type = struct_value () [concrete] // CHECK:STDOUT: %pattern_type.a96: type = pattern_type %empty_struct_type [concrete] // CHECK:STDOUT: %DefaultOrUnformed.type: type = facet_type <@DefaultOrUnformed> [concrete] // CHECK:STDOUT: %T: type = symbolic_binding T, 0 [symbolic] // CHECK:STDOUT: %T.as.DefaultOrUnformed.impl.Op.type.6c6: type = fn_type @T.as.DefaultOrUnformed.impl.Op, @T.as.DefaultOrUnformed.impl(%T) [symbolic] // CHECK:STDOUT: %T.as.DefaultOrUnformed.impl.Op.ae1: %T.as.DefaultOrUnformed.impl.Op.type.6c6 = struct_value () [symbolic] // CHECK:STDOUT: %DefaultOrUnformed.impl_witness.3f9: = impl_witness imports.%DefaultOrUnformed.impl_witness_table, @T.as.DefaultOrUnformed.impl(%empty_struct_type) [concrete] // CHECK:STDOUT: %DefaultOrUnformed.facet: %DefaultOrUnformed.type = facet_value %empty_struct_type, (%DefaultOrUnformed.impl_witness.3f9) [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core.import_ref.01d: @T.as.DefaultOrUnformed.impl.%T.as.DefaultOrUnformed.impl.Op.type (%T.as.DefaultOrUnformed.impl.Op.type.6c6) = import_ref Core//prelude/parts/default, loc{{\d+_\d+}}, loaded [symbolic = @T.as.DefaultOrUnformed.impl.%T.as.DefaultOrUnformed.impl.Op (constants.%T.as.DefaultOrUnformed.impl.Op.ae1)] // CHECK:STDOUT: %DefaultOrUnformed.impl_witness_table = impl_witness_table (%Core.import_ref.01d), @T.as.DefaultOrUnformed.impl [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %a.patt: %pattern_type.a96 = ref_binding_pattern a [concrete] // CHECK:STDOUT: %a.var_patt: %pattern_type.a96 = var_pattern %a.patt [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %a.var: ref %empty_struct_type = var %a.var_patt [concrete] // CHECK:STDOUT: %.loc4_9.1: type = splice_block %.loc4_9.3 [concrete = constants.%empty_struct_type] { // CHECK:STDOUT: %.loc4_9.2: %empty_struct_type = struct_literal () [concrete = constants.%empty_struct] // CHECK:STDOUT: %.loc4_9.3: type = converted %.loc4_9.2, constants.%empty_struct_type [concrete = constants.%empty_struct_type] // CHECK:STDOUT: } // CHECK:STDOUT: %a: ref %empty_struct_type = ref_binding a, %a.var [concrete = %a.var] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @__global_init() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %DefaultOrUnformed.facet: %DefaultOrUnformed.type = facet_value constants.%empty_struct_type, (constants.%DefaultOrUnformed.impl_witness.3f9) [concrete = constants.%DefaultOrUnformed.facet] // CHECK:STDOUT: %.loc4_10.1: %DefaultOrUnformed.type = converted constants.%empty_struct_type, %DefaultOrUnformed.facet [concrete = constants.%DefaultOrUnformed.facet] // CHECK:STDOUT: %as_type: type = facet_access_type %.loc4_10.1 [concrete = constants.%empty_struct_type] // CHECK:STDOUT: %.loc4_10.2: type = converted %.loc4_10.1, %as_type [concrete = constants.%empty_struct_type] // CHECK:STDOUT: // CHECK:STDOUT: %T.as.DefaultOrUnformed.impl.Op.call: init %empty_struct_type = call %T.as.DefaultOrUnformed.impl.Op.specific_fn() // CHECK:STDOUT: assign file.%a.var, %T.as.DefaultOrUnformed.impl.Op.call // CHECK:STDOUT: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- simple_init.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %empty_struct: %empty_struct_type = struct_value () [concrete] // CHECK:STDOUT: %pattern_type: type = pattern_type %empty_struct_type [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %a.patt: %pattern_type = ref_binding_pattern a [concrete] // CHECK:STDOUT: %a.var_patt: %pattern_type = var_pattern %a.patt [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %a.var: ref %empty_struct_type = var %a.var_patt [concrete] // CHECK:STDOUT: %.loc4_9.1: type = splice_block %.loc4_9.3 [concrete = constants.%empty_struct_type] { // CHECK:STDOUT: %.loc4_9.2: %empty_struct_type = struct_literal () [concrete = constants.%empty_struct] // CHECK:STDOUT: %.loc4_9.3: type = converted %.loc4_9.2, constants.%empty_struct_type [concrete = constants.%empty_struct_type] // CHECK:STDOUT: } // CHECK:STDOUT: %a: ref %empty_struct_type = ref_binding a, %a.var [concrete = %a.var] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @__global_init() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %.loc4_14.1: %empty_struct_type = struct_literal () [concrete = constants.%empty_struct] // CHECK:STDOUT: %.loc4_14.2: init %empty_struct_type = struct_init () [concrete = constants.%empty_struct] // CHECK:STDOUT: %.loc4_1: init %empty_struct_type = converted %.loc4_14.1, %.loc4_14.2 [concrete = constants.%empty_struct] // CHECK:STDOUT: assign file.%a.var, %.loc4_1 // CHECK:STDOUT: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- simple_init_with_fn.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %empty_struct: %empty_struct_type = struct_value () [concrete] // CHECK:STDOUT: %pattern_type: type = pattern_type %empty_struct_type [concrete] // CHECK:STDOUT: %Make.type: type = fn_type @Make [concrete] // CHECK:STDOUT: %Make: %Make.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %a.patt: %pattern_type = ref_binding_pattern a [concrete] // CHECK:STDOUT: %a.var_patt: %pattern_type = var_pattern %a.patt [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %a.var: ref %empty_struct_type = var %a.var_patt [concrete] // CHECK:STDOUT: %.loc6_9.1: type = splice_block %.loc6_9.3 [concrete = constants.%empty_struct_type] { // CHECK:STDOUT: %.loc6_9.2: %empty_struct_type = struct_literal () [concrete = constants.%empty_struct] // CHECK:STDOUT: %.loc6_9.3: type = converted %.loc6_9.2, constants.%empty_struct_type [concrete = constants.%empty_struct_type] // CHECK:STDOUT: } // CHECK:STDOUT: %a: ref %empty_struct_type = ref_binding a, %a.var [concrete = %a.var] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @__global_init() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %Make.ref: %Make.type = name_ref Make, file.%Make.decl [concrete = constants.%Make] // CHECK:STDOUT: %Make.call: init %empty_struct_type = call %Make.ref() // CHECK:STDOUT: assign file.%a.var, %Make.call // CHECK:STDOUT: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- class_init.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %A: type = class_type @A [concrete] // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %pattern_type: type = pattern_type %A [concrete] // CHECK:STDOUT: %empty_struct: %empty_struct_type = struct_value () [concrete] // CHECK:STDOUT: %A.val: %A = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %a.patt: %pattern_type = ref_binding_pattern a [concrete] // CHECK:STDOUT: %a.var_patt: %pattern_type = var_pattern %a.patt [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %a.var: ref %A = var %a.var_patt [concrete] // CHECK:STDOUT: %A.ref: type = name_ref A, %A.decl [concrete = constants.%A] // CHECK:STDOUT: %a: ref %A = ref_binding a, %a.var [concrete = %a.var] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @__global_init() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %.loc6_13.1: %empty_struct_type = struct_literal () [concrete = constants.%empty_struct] // CHECK:STDOUT: %.loc6_13.2: init %A to file.%a.var = class_init () [concrete = constants.%A.val] // CHECK:STDOUT: %.loc6_1: init %A = converted %.loc6_13.1, %.loc6_13.2 [concrete = constants.%A.val] // CHECK:STDOUT: assign file.%a.var, %.loc6_1 // CHECK:STDOUT: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- class_init_from_fn.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %A: type = class_type @A [concrete] // CHECK:STDOUT: %pattern_type: type = pattern_type %A [concrete] // CHECK:STDOUT: %Make.type: type = fn_type @Make [concrete] // CHECK:STDOUT: %Make: %Make.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %a.patt: %pattern_type = ref_binding_pattern a [concrete] // CHECK:STDOUT: %a.var_patt: %pattern_type = var_pattern %a.patt [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %a.var: ref %A = var %a.var_patt [concrete] // CHECK:STDOUT: %A.ref: type = name_ref A, %A.decl [concrete = constants.%A] // CHECK:STDOUT: %a: ref %A = ref_binding a, %a.var [concrete = %a.var] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @__global_init() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %Make.ref: %Make.type = name_ref Make, file.%Make.decl [concrete = constants.%Make] // CHECK:STDOUT: %.loc8: ref %A = splice_block file.%a.var [concrete = file.%a.var] {} // CHECK:STDOUT: %Make.call: init %A to %.loc8 = call %Make.ref() // CHECK:STDOUT: assign file.%a.var, %Make.call // CHECK:STDOUT: // CHECK:STDOUT: } // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/if/basics.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/bool.carbon // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/if/basics.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/if/basics.carbon // --- else.carbon library "[[@TEST_NAME]]"; fn F() {} fn G() {} fn H() {} //@dump-sem-ir-begin fn If(b: bool) { if (b) { F(); } else { G(); } H(); } //@dump-sem-ir-end // --- no_else.carbon library "[[@TEST_NAME]]"; fn F() {} fn G() {} //@dump-sem-ir-begin fn If(b: bool) { if (b) { F(); } G(); } //@dump-sem-ir-end // --- unreachable_fallthrough.carbon library "[[@TEST_NAME]]"; //@dump-sem-ir-begin fn If(b: bool) -> bool { if (b) { return false; } else { return true; } // Missing return here is OK. } //@dump-sem-ir-end // --- fail_reachable_fallthrough.carbon library "[[@TEST_NAME]]"; fn If1(b: bool) -> bool { if (b) { return false; } else { } // CHECK:STDERR: fail_reachable_fallthrough.carbon:[[@LINE+4]]:1: error: missing `return` at end of function with declared return type [MissingReturnStatement] // CHECK:STDERR: } // CHECK:STDERR: ^ // CHECK:STDERR: } fn If2(b: bool) -> bool { if (b) { } else { return true; } // CHECK:STDERR: fail_reachable_fallthrough.carbon:[[@LINE+4]]:1: error: missing `return` at end of function with declared return type [MissingReturnStatement] // CHECK:STDERR: } // CHECK:STDERR: ^ // CHECK:STDERR: } fn If3(b: bool) -> bool { if (b) { return false; } // CHECK:STDERR: fail_reachable_fallthrough.carbon:[[@LINE+4]]:1: error: missing `return` at end of function with declared return type [MissingReturnStatement] // CHECK:STDERR: } // CHECK:STDERR: ^ // CHECK:STDERR: } // --- fail_scope.carbon library "[[@TEST_NAME]]"; fn VarScope(b: bool) -> bool { if (b) { var n: bool = true; return n; } // CHECK:STDERR: fail_scope.carbon:[[@LINE+4]]:10: error: name `n` not found [NameNotFound] // CHECK:STDERR: return n; // CHECK:STDERR: ^ // CHECK:STDERR: return n; } // CHECK:STDOUT: --- else.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %F.type: type = fn_type @F [concrete] // CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] // CHECK:STDOUT: %F: %F.type = struct_value () [concrete] // CHECK:STDOUT: %G.type: type = fn_type @G [concrete] // CHECK:STDOUT: %G: %G.type = struct_value () [concrete] // CHECK:STDOUT: %H.type: type = fn_type @H [concrete] // CHECK:STDOUT: %H: %H.type = struct_value () [concrete] // CHECK:STDOUT: %pattern_type.831: type = pattern_type bool [concrete] // CHECK:STDOUT: %If.type: type = fn_type @If [concrete] // CHECK:STDOUT: %If: %If.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: %If.decl: %If.type = fn_decl @If [concrete = constants.%If] { // CHECK:STDOUT: %b.patt: %pattern_type.831 = value_binding_pattern b [concrete] // CHECK:STDOUT: %b.param_patt: %pattern_type.831 = value_param_pattern %b.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %b.param: bool = value_param call_param0 // CHECK:STDOUT: %.loc8: type = type_literal bool [concrete = bool] // CHECK:STDOUT: %b: bool = value_binding b, %b.param // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @If(%b.param: bool) { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %b.ref: bool = name_ref b, %b // CHECK:STDOUT: if %b.ref br !if.then else br !if.else // CHECK:STDOUT: // CHECK:STDOUT: !if.then: // CHECK:STDOUT: %F.ref: %F.type = name_ref F, file.%F.decl [concrete = constants.%F] // CHECK:STDOUT: %F.call: init %empty_tuple.type = call %F.ref() // CHECK:STDOUT: br !if.done // CHECK:STDOUT: // CHECK:STDOUT: !if.else: // CHECK:STDOUT: %G.ref: %G.type = name_ref G, file.%G.decl [concrete = constants.%G] // CHECK:STDOUT: %G.call: init %empty_tuple.type = call %G.ref() // CHECK:STDOUT: br !if.done // CHECK:STDOUT: // CHECK:STDOUT: !if.done: // CHECK:STDOUT: %H.ref: %H.type = name_ref H, file.%H.decl [concrete = constants.%H] // CHECK:STDOUT: %H.call: init %empty_tuple.type = call %H.ref() // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- no_else.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %F.type: type = fn_type @F [concrete] // CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] // CHECK:STDOUT: %F: %F.type = struct_value () [concrete] // CHECK:STDOUT: %G.type: type = fn_type @G [concrete] // CHECK:STDOUT: %G: %G.type = struct_value () [concrete] // CHECK:STDOUT: %pattern_type.831: type = pattern_type bool [concrete] // CHECK:STDOUT: %If.type: type = fn_type @If [concrete] // CHECK:STDOUT: %If: %If.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: %If.decl: %If.type = fn_decl @If [concrete = constants.%If] { // CHECK:STDOUT: %b.patt: %pattern_type.831 = value_binding_pattern b [concrete] // CHECK:STDOUT: %b.param_patt: %pattern_type.831 = value_param_pattern %b.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %b.param: bool = value_param call_param0 // CHECK:STDOUT: %.loc7: type = type_literal bool [concrete = bool] // CHECK:STDOUT: %b: bool = value_binding b, %b.param // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @If(%b.param: bool) { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %b.ref: bool = name_ref b, %b // CHECK:STDOUT: if %b.ref br !if.then else br !if.else // CHECK:STDOUT: // CHECK:STDOUT: !if.then: // CHECK:STDOUT: %F.ref: %F.type = name_ref F, file.%F.decl [concrete = constants.%F] // CHECK:STDOUT: %F.call: init %empty_tuple.type = call %F.ref() // CHECK:STDOUT: br !if.else // CHECK:STDOUT: // CHECK:STDOUT: !if.else: // CHECK:STDOUT: %G.ref: %G.type = name_ref G, file.%G.decl [concrete = constants.%G] // CHECK:STDOUT: %G.call: init %empty_tuple.type = call %G.ref() // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- unreachable_fallthrough.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %pattern_type.831: type = pattern_type bool [concrete] // CHECK:STDOUT: %.f34: Core.Form = init_form bool [concrete] // CHECK:STDOUT: %If.type: type = fn_type @If [concrete] // CHECK:STDOUT: %If: %If.type = struct_value () [concrete] // CHECK:STDOUT: %false: bool = bool_literal false [concrete] // CHECK:STDOUT: %Copy.type: type = facet_type <@Copy> [concrete] // CHECK:STDOUT: %Copy.impl_witness.348: = impl_witness imports.%Copy.impl_witness_table.3cc [concrete] // CHECK:STDOUT: %Copy.facet: %Copy.type = facet_value bool, (%Copy.impl_witness.348) [concrete] // CHECK:STDOUT: %Copy.WithSelf.Op.type.6dd: type = fn_type @Copy.WithSelf.Op, @Copy.WithSelf(%Copy.facet) [concrete] // CHECK:STDOUT: %.86d: type = fn_type_with_self_type %Copy.WithSelf.Op.type.6dd, %Copy.facet [concrete] // CHECK:STDOUT: %bool.as.Copy.impl.Op.type: type = fn_type @bool.as.Copy.impl.Op [concrete] // CHECK:STDOUT: %bool.as.Copy.impl.Op: %bool.as.Copy.impl.Op.type = struct_value () [concrete] // CHECK:STDOUT: %bool.as.Copy.impl.Op.bound.082: = bound_method %false, %bool.as.Copy.impl.Op [concrete] // CHECK:STDOUT: %true: bool = bool_literal true [concrete] // CHECK:STDOUT: %bool.as.Copy.impl.Op.bound.12b: = bound_method %true, %bool.as.Copy.impl.Op [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core.import_ref.595: %bool.as.Copy.impl.Op.type = import_ref Core//prelude/parts/copy, loc{{\d+_\d+}}, loaded [concrete = constants.%bool.as.Copy.impl.Op] // CHECK:STDOUT: %Copy.impl_witness_table.3cc = impl_witness_table (%Core.import_ref.595), @bool.as.Copy.impl [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: %If.decl: %If.type = fn_decl @If [concrete = constants.%If] { // CHECK:STDOUT: %b.patt: %pattern_type.831 = value_binding_pattern b [concrete] // CHECK:STDOUT: %b.param_patt: %pattern_type.831 = value_param_pattern %b.patt [concrete] // CHECK:STDOUT: %return.patt: %pattern_type.831 = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: %pattern_type.831 = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %.loc4_19.1: type = type_literal bool [concrete = bool] // CHECK:STDOUT: %.loc4_19.2: Core.Form = init_form %.loc4_19.1 [concrete = constants.%.f34] // CHECK:STDOUT: %b.param: bool = value_param call_param0 // CHECK:STDOUT: %.loc4_10: type = type_literal bool [concrete = bool] // CHECK:STDOUT: %b: bool = value_binding b, %b.param // CHECK:STDOUT: %return.param: ref bool = out_param call_param1 // CHECK:STDOUT: %return: ref bool = return_slot %return.param // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @If(%b.param: bool) -> out %return.param: bool { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %b.ref: bool = name_ref b, %b // CHECK:STDOUT: if %b.ref br !if.then else br !if.else // CHECK:STDOUT: // CHECK:STDOUT: !if.then: // CHECK:STDOUT: %false: bool = bool_literal false [concrete = constants.%false] // CHECK:STDOUT: %impl.elem0.loc6: %.86d = impl_witness_access constants.%Copy.impl_witness.348, element0 [concrete = constants.%bool.as.Copy.impl.Op] // CHECK:STDOUT: %bound_method.loc6: = bound_method %false, %impl.elem0.loc6 [concrete = constants.%bool.as.Copy.impl.Op.bound.082] // CHECK:STDOUT: %bool.as.Copy.impl.Op.call.loc6: init bool = call %bound_method.loc6(%false) [concrete = constants.%false] // CHECK:STDOUT: return %bool.as.Copy.impl.Op.call.loc6 // CHECK:STDOUT: // CHECK:STDOUT: !if.else: // CHECK:STDOUT: %true: bool = bool_literal true [concrete = constants.%true] // CHECK:STDOUT: %impl.elem0.loc8: %.86d = impl_witness_access constants.%Copy.impl_witness.348, element0 [concrete = constants.%bool.as.Copy.impl.Op] // CHECK:STDOUT: %bound_method.loc8: = bound_method %true, %impl.elem0.loc8 [concrete = constants.%bool.as.Copy.impl.Op.bound.12b] // CHECK:STDOUT: %bool.as.Copy.impl.Op.call.loc8: init bool = call %bound_method.loc8(%true) [concrete = constants.%true] // CHECK:STDOUT: return %bool.as.Copy.impl.Op.call.loc8 // CHECK:STDOUT: } // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/if_expr/basic.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/primitives.carbon // TODO: Add ranges and switch to "--dump-sem-ir-ranges=only". // EXTRA-ARGS: --dump-sem-ir-ranges=if-present // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/if_expr/basic.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/if_expr/basic.carbon fn F(b: bool, n: i32, m: i32) -> i32 { var x: array(i32, 1) = (0,); return if b then x[m] else x[n]; } // CHECK:STDOUT: --- basic.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %Bool.type: type = fn_type @Bool [concrete] // CHECK:STDOUT: %Bool: %Bool.type = struct_value () [concrete] // CHECK:STDOUT: %pattern_type.831: type = pattern_type bool [concrete] // CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [concrete] // CHECK:STDOUT: %Int.type: type = generic_class_type @Int [concrete] // CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] // CHECK:STDOUT: %Int.generic: %Int.type = struct_value () [concrete] // CHECK:STDOUT: %N: Core.IntLiteral = symbolic_binding N, 0 [symbolic] // CHECK:STDOUT: %i32: type = class_type @Int, @Int(%int_32) [concrete] // CHECK:STDOUT: %pattern_type.7ce: type = pattern_type %i32 [concrete] // CHECK:STDOUT: %.ff5: Core.Form = init_form %i32 [concrete] // CHECK:STDOUT: %F.type: type = fn_type @F [concrete] // CHECK:STDOUT: %F: %F.type = struct_value () [concrete] // CHECK:STDOUT: %int_1: Core.IntLiteral = int_value 1 [concrete] // CHECK:STDOUT: %array_type: type = array_type %int_1, %i32 [concrete] // CHECK:STDOUT: %pattern_type.a98: type = pattern_type %array_type [concrete] // CHECK:STDOUT: %int_0.5c6: Core.IntLiteral = int_value 0 [concrete] // CHECK:STDOUT: %tuple.type.985: type = tuple_type (Core.IntLiteral) [concrete] // CHECK:STDOUT: %tuple.4f2: %tuple.type.985 = tuple_value (%int_0.5c6) [concrete] // CHECK:STDOUT: %ImplicitAs.type.cc7: type = generic_interface_type @ImplicitAs [concrete] // CHECK:STDOUT: %ImplicitAs.generic: %ImplicitAs.type.cc7 = struct_value () [concrete] // CHECK:STDOUT: %ImplicitAs.type.e8c: type = facet_type <@ImplicitAs, @ImplicitAs(%i32)> [concrete] // CHECK:STDOUT: %To: Core.IntLiteral = symbolic_binding To, 0 [symbolic] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.4e6: type = fn_type @Core.IntLiteral.as.ImplicitAs.impl.Convert, @Core.IntLiteral.as.ImplicitAs.impl(%To) [symbolic] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.3c2: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.4e6 = struct_value () [symbolic] // CHECK:STDOUT: %ImplicitAs.impl_witness.6bc: = impl_witness imports.%ImplicitAs.impl_witness_table.74f, @Core.IntLiteral.as.ImplicitAs.impl(%int_32) [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.e0d: type = fn_type @Core.IntLiteral.as.ImplicitAs.impl.Convert, @Core.IntLiteral.as.ImplicitAs.impl(%int_32) [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.e0d = struct_value () [concrete] // CHECK:STDOUT: %ImplicitAs.facet: %ImplicitAs.type.e8c = facet_value Core.IntLiteral, (%ImplicitAs.impl_witness.6bc) [concrete] // CHECK:STDOUT: %ImplicitAs.WithSelf.Convert.type.b37: type = fn_type @ImplicitAs.WithSelf.Convert, @ImplicitAs.WithSelf(%i32, %ImplicitAs.facet) [concrete] // CHECK:STDOUT: %.545: type = fn_type_with_self_type %ImplicitAs.WithSelf.Convert.type.b37, %ImplicitAs.facet [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.bound: = bound_method %int_0.5c6, %Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5 [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn: = specific_function %Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5, @Core.IntLiteral.as.ImplicitAs.impl.Convert(%int_32) [concrete] // CHECK:STDOUT: %bound_method: = bound_method %int_0.5c6, %Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn [concrete] // CHECK:STDOUT: %int_0.6a9: %i32 = int_value 0 [concrete] // CHECK:STDOUT: %array: %array_type = tuple_value (%int_0.6a9) [concrete] // CHECK:STDOUT: %Copy.type: type = facet_type <@Copy> [concrete] // CHECK:STDOUT: %Int.as.Copy.impl.Op.type.824: type = fn_type @Int.as.Copy.impl.Op, @Int.as.Copy.impl(%N) [symbolic] // CHECK:STDOUT: %Int.as.Copy.impl.Op.9b9: %Int.as.Copy.impl.Op.type.824 = struct_value () [symbolic] // CHECK:STDOUT: %Copy.impl_witness.f17: = impl_witness imports.%Copy.impl_witness_table.e76, @Int.as.Copy.impl(%int_32) [concrete] // CHECK:STDOUT: %Int.as.Copy.impl.Op.type.546: type = fn_type @Int.as.Copy.impl.Op, @Int.as.Copy.impl(%int_32) [concrete] // CHECK:STDOUT: %Int.as.Copy.impl.Op.664: %Int.as.Copy.impl.Op.type.546 = struct_value () [concrete] // CHECK:STDOUT: %Copy.facet: %Copy.type = facet_value %i32, (%Copy.impl_witness.f17) [concrete] // CHECK:STDOUT: %Copy.WithSelf.Op.type.081: type = fn_type @Copy.WithSelf.Op, @Copy.WithSelf(%Copy.facet) [concrete] // CHECK:STDOUT: %.8e2: type = fn_type_with_self_type %Copy.WithSelf.Op.type.081, %Copy.facet [concrete] // CHECK:STDOUT: %Int.as.Copy.impl.Op.specific_fn: = specific_function %Int.as.Copy.impl.Op.664, @Int.as.Copy.impl.Op(%int_32) [concrete] // CHECK:STDOUT: %Destroy.type: type = facet_type <@Destroy> [concrete] // CHECK:STDOUT: %Destroy.Op.type: type = fn_type @Destroy.Op [concrete] // CHECK:STDOUT: %Destroy.Op: %Destroy.Op.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: .Bool = %Core.Bool // CHECK:STDOUT: .Int = %Core.Int // CHECK:STDOUT: .ImplicitAs = %Core.ImplicitAs // CHECK:STDOUT: .Copy = %Core.Copy // CHECK:STDOUT: .Destroy = %Core.Destroy // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: %Core.Bool: %Bool.type = import_ref Core//prelude/parts/bool, Bool, loaded [concrete = constants.%Bool] // CHECK:STDOUT: %Core.Int: %Int.type = import_ref Core//prelude/parts/int, Int, loaded [concrete = constants.%Int.generic] // CHECK:STDOUT: %Core.ImplicitAs: %ImplicitAs.type.cc7 = import_ref Core//prelude/parts/as, ImplicitAs, loaded [concrete = constants.%ImplicitAs.generic] // CHECK:STDOUT: %Core.import_ref.42d: @Core.IntLiteral.as.ImplicitAs.impl.%Core.IntLiteral.as.ImplicitAs.impl.Convert.type (%Core.IntLiteral.as.ImplicitAs.impl.Convert.type.4e6) = import_ref Core//prelude/parts/int, loc{{\d+_\d+}}, loaded [symbolic = @Core.IntLiteral.as.ImplicitAs.impl.%Core.IntLiteral.as.ImplicitAs.impl.Convert (constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.3c2)] // CHECK:STDOUT: %ImplicitAs.impl_witness_table.74f = impl_witness_table (%Core.import_ref.42d), @Core.IntLiteral.as.ImplicitAs.impl [concrete] // CHECK:STDOUT: %Core.Copy: type = import_ref Core//prelude/parts/copy, Copy, loaded [concrete = constants.%Copy.type] // CHECK:STDOUT: %Core.import_ref.18d: @Int.as.Copy.impl.%Int.as.Copy.impl.Op.type (%Int.as.Copy.impl.Op.type.824) = import_ref Core//prelude/parts/int, loc{{\d+_\d+}}, loaded [symbolic = @Int.as.Copy.impl.%Int.as.Copy.impl.Op (constants.%Int.as.Copy.impl.Op.9b9)] // CHECK:STDOUT: %Copy.impl_witness_table.e76 = impl_witness_table (%Core.import_ref.18d), @Int.as.Copy.impl [concrete] // CHECK:STDOUT: %Core.Destroy: type = import_ref Core//prelude/parts/destroy, Destroy, loaded [concrete = constants.%Destroy.type] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .F = %F.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %F.decl: %F.type = fn_decl @F [concrete = constants.%F] { // CHECK:STDOUT: %b.patt: %pattern_type.831 = value_binding_pattern b [concrete] // CHECK:STDOUT: %b.param_patt: %pattern_type.831 = value_param_pattern %b.patt [concrete] // CHECK:STDOUT: %n.patt: %pattern_type.7ce = value_binding_pattern n [concrete] // CHECK:STDOUT: %n.param_patt: %pattern_type.7ce = value_param_pattern %n.patt [concrete] // CHECK:STDOUT: %m.patt: %pattern_type.7ce = value_binding_pattern m [concrete] // CHECK:STDOUT: %m.param_patt: %pattern_type.7ce = value_param_pattern %m.patt [concrete] // CHECK:STDOUT: %return.patt: %pattern_type.7ce = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: %pattern_type.7ce = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %i32.loc15_34: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %.loc15_34: Core.Form = init_form %i32.loc15_34 [concrete = constants.%.ff5] // CHECK:STDOUT: %b.param: bool = value_param call_param0 // CHECK:STDOUT: %.loc15_9: type = type_literal bool [concrete = bool] // CHECK:STDOUT: %b: bool = value_binding b, %b.param // CHECK:STDOUT: %n.param: %i32 = value_param call_param1 // CHECK:STDOUT: %i32.loc15_18: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %n: %i32 = value_binding n, %n.param // CHECK:STDOUT: %m.param: %i32 = value_param call_param2 // CHECK:STDOUT: %i32.loc15_26: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %m: %i32 = value_binding m, %m.param // CHECK:STDOUT: %return.param: ref %i32 = out_param call_param3 // CHECK:STDOUT: %return: ref %i32 = return_slot %return.param // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @F(%b.param: bool, %n.param: %i32, %m.param: %i32) -> out %return.param: %i32 { // CHECK:STDOUT: !entry: // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %x.patt: %pattern_type.a98 = ref_binding_pattern x [concrete] // CHECK:STDOUT: %x.var_patt: %pattern_type.a98 = var_pattern %x.patt [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %x.var: ref %array_type = var %x.var_patt // CHECK:STDOUT: %int_0.loc16_27: Core.IntLiteral = int_value 0 [concrete = constants.%int_0.5c6] // CHECK:STDOUT: %.loc16_29.1: %tuple.type.985 = tuple_literal (%int_0.loc16_27) [concrete = constants.%tuple.4f2] // CHECK:STDOUT: %impl.elem0.loc16: %.545 = impl_witness_access constants.%ImplicitAs.impl_witness.6bc, element0 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5] // CHECK:STDOUT: %bound_method.loc16_29.1: = bound_method %int_0.loc16_27, %impl.elem0.loc16 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.bound] // CHECK:STDOUT: %specific_fn.loc16: = specific_function %impl.elem0.loc16, @Core.IntLiteral.as.ImplicitAs.impl.Convert(constants.%int_32) [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn] // CHECK:STDOUT: %bound_method.loc16_29.2: = bound_method %int_0.loc16_27, %specific_fn.loc16 [concrete = constants.%bound_method] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.call: init %i32 = call %bound_method.loc16_29.2(%int_0.loc16_27) [concrete = constants.%int_0.6a9] // CHECK:STDOUT: %.loc16_29.2: init %i32 = converted %int_0.loc16_27, %Core.IntLiteral.as.ImplicitAs.impl.Convert.call [concrete = constants.%int_0.6a9] // CHECK:STDOUT: %int_0.loc16_29: Core.IntLiteral = int_value 0 [concrete = constants.%int_0.5c6] // CHECK:STDOUT: %.loc16_29.3: ref %i32 = array_index %x.var, %int_0.loc16_29 // CHECK:STDOUT: %.loc16_29.4: init %i32 to %.loc16_29.3 = in_place_init %.loc16_29.2 [concrete = constants.%int_0.6a9] // CHECK:STDOUT: %.loc16_29.5: init %array_type to %x.var = array_init (%.loc16_29.4) [concrete = constants.%array] // CHECK:STDOUT: %.loc16_3: init %array_type = converted %.loc16_29.1, %.loc16_29.5 [concrete = constants.%array] // CHECK:STDOUT: assign %x.var, %.loc16_3 // CHECK:STDOUT: %.loc16_22: type = splice_block %array_type [concrete = constants.%array_type] { // CHECK:STDOUT: %i32.loc16: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %int_1: Core.IntLiteral = int_value 1 [concrete = constants.%int_1] // CHECK:STDOUT: %array_type: type = array_type %int_1, %i32.loc16 [concrete = constants.%array_type] // CHECK:STDOUT: } // CHECK:STDOUT: %x: ref %array_type = ref_binding x, %x.var // CHECK:STDOUT: %b.ref: bool = name_ref b, %b // CHECK:STDOUT: if %b.ref br !if.expr.then else br !if.expr.else // CHECK:STDOUT: // CHECK:STDOUT: !if.expr.then: // CHECK:STDOUT: %x.ref.loc17_20: ref %array_type = name_ref x, %x // CHECK:STDOUT: %m.ref: %i32 = name_ref m, %m // CHECK:STDOUT: %.loc17_23.1: ref %i32 = array_index %x.ref.loc17_20, %m.ref // CHECK:STDOUT: %.loc17_23.2: %i32 = acquire_value %.loc17_23.1 // CHECK:STDOUT: br !if.expr.result(%.loc17_23.2) // CHECK:STDOUT: // CHECK:STDOUT: !if.expr.else: // CHECK:STDOUT: %x.ref.loc17_30: ref %array_type = name_ref x, %x // CHECK:STDOUT: %n.ref: %i32 = name_ref n, %n // CHECK:STDOUT: %.loc17_33.1: ref %i32 = array_index %x.ref.loc17_30, %n.ref // CHECK:STDOUT: %.loc17_33.2: %i32 = acquire_value %.loc17_33.1 // CHECK:STDOUT: br !if.expr.result(%.loc17_33.2) // CHECK:STDOUT: // CHECK:STDOUT: !if.expr.result: // CHECK:STDOUT: %.loc17_10: %i32 = block_arg !if.expr.result // CHECK:STDOUT: %impl.elem0.loc17: %.8e2 = impl_witness_access constants.%Copy.impl_witness.f17, element0 [concrete = constants.%Int.as.Copy.impl.Op.664] // CHECK:STDOUT: %bound_method.loc17_10.1: = bound_method %.loc17_10, %impl.elem0.loc17 // CHECK:STDOUT: %specific_fn.loc17: = specific_function %impl.elem0.loc17, @Int.as.Copy.impl.Op(constants.%int_32) [concrete = constants.%Int.as.Copy.impl.Op.specific_fn] // CHECK:STDOUT: %bound_method.loc17_10.2: = bound_method %.loc17_10, %specific_fn.loc17 // CHECK:STDOUT: %Int.as.Copy.impl.Op.call: init %i32 = call %bound_method.loc17_10.2(%.loc17_10) // CHECK:STDOUT: %Destroy.Op.bound: = bound_method %x.var, constants.%Destroy.Op // CHECK:STDOUT: %Destroy.Op.call: init %empty_tuple.type = call %Destroy.Op.bound(%x.var) // CHECK:STDOUT: return %Int.as.Copy.impl.Op.call // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @Destroy.Op(%self.param: ref %array_type) = "no_op"; // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/if_expr/constant_condition.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/int.carbon // TODO: Add ranges and switch to "--dump-sem-ir-ranges=only". // EXTRA-ARGS: --dump-sem-ir-ranges=if-present // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/if_expr/constant_condition.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/if_expr/constant_condition.carbon fn A() -> i32 { return 1; } fn B() -> i32 { return 2; } fn F() -> i32 { return if true then A() else B(); } fn G() -> i32 { return if false then A() else B(); } fn Constant() -> i32 { var v: if true then i32 else i32* = 1; var w: if false then i32 else i32* = &v; return *w; } fn PartiallyConstant(t: type) -> i32 { var v: if true then i32 else t = 1; var w: if false then t else i32* = &v; return *w; } // CHECK:STDOUT: --- constant_condition.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [concrete] // CHECK:STDOUT: %Int.type: type = generic_class_type @Int [concrete] // CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] // CHECK:STDOUT: %Int.generic: %Int.type = struct_value () [concrete] // CHECK:STDOUT: %N: Core.IntLiteral = symbolic_binding N, 0 [symbolic] // CHECK:STDOUT: %i32: type = class_type @Int, @Int(%int_32) [concrete] // CHECK:STDOUT: %.ff5: Core.Form = init_form %i32 [concrete] // CHECK:STDOUT: %pattern_type.7ce: type = pattern_type %i32 [concrete] // CHECK:STDOUT: %A.type: type = fn_type @A [concrete] // CHECK:STDOUT: %A: %A.type = struct_value () [concrete] // CHECK:STDOUT: %int_1.5b8: Core.IntLiteral = int_value 1 [concrete] // CHECK:STDOUT: %ImplicitAs.type.cc7: type = generic_interface_type @ImplicitAs [concrete] // CHECK:STDOUT: %ImplicitAs.generic: %ImplicitAs.type.cc7 = struct_value () [concrete] // CHECK:STDOUT: %pattern_type.98f: type = pattern_type type [concrete] // CHECK:STDOUT: %ImplicitAs.type.e8c: type = facet_type <@ImplicitAs, @ImplicitAs(%i32)> [concrete] // CHECK:STDOUT: %To: Core.IntLiteral = symbolic_binding To, 0 [symbolic] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.4e6: type = fn_type @Core.IntLiteral.as.ImplicitAs.impl.Convert, @Core.IntLiteral.as.ImplicitAs.impl(%To) [symbolic] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.3c2: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.4e6 = struct_value () [symbolic] // CHECK:STDOUT: %ImplicitAs.impl_witness.6bc: = impl_witness imports.%ImplicitAs.impl_witness_table.74f, @Core.IntLiteral.as.ImplicitAs.impl(%int_32) [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.e0d: type = fn_type @Core.IntLiteral.as.ImplicitAs.impl.Convert, @Core.IntLiteral.as.ImplicitAs.impl(%int_32) [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.e0d = struct_value () [concrete] // CHECK:STDOUT: %ImplicitAs.facet: %ImplicitAs.type.e8c = facet_value Core.IntLiteral, (%ImplicitAs.impl_witness.6bc) [concrete] // CHECK:STDOUT: %ImplicitAs.WithSelf.Convert.type.b37: type = fn_type @ImplicitAs.WithSelf.Convert, @ImplicitAs.WithSelf(%i32, %ImplicitAs.facet) [concrete] // CHECK:STDOUT: %.545: type = fn_type_with_self_type %ImplicitAs.WithSelf.Convert.type.b37, %ImplicitAs.facet [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.bound.215: = bound_method %int_1.5b8, %Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5 [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn: = specific_function %Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5, @Core.IntLiteral.as.ImplicitAs.impl.Convert(%int_32) [concrete] // CHECK:STDOUT: %bound_method.38b: = bound_method %int_1.5b8, %Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn [concrete] // CHECK:STDOUT: %int_1.5d2: %i32 = int_value 1 [concrete] // CHECK:STDOUT: %B.type: type = fn_type @B [concrete] // CHECK:STDOUT: %B: %B.type = struct_value () [concrete] // CHECK:STDOUT: %int_2.ecc: Core.IntLiteral = int_value 2 [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.bound.4e5: = bound_method %int_2.ecc, %Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5 [concrete] // CHECK:STDOUT: %bound_method.646: = bound_method %int_2.ecc, %Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn [concrete] // CHECK:STDOUT: %int_2.ef8: %i32 = int_value 2 [concrete] // CHECK:STDOUT: %F.type: type = fn_type @F [concrete] // CHECK:STDOUT: %F: %F.type = struct_value () [concrete] // CHECK:STDOUT: %true: bool = bool_literal true [concrete] // CHECK:STDOUT: %Copy.type: type = facet_type <@Copy> [concrete] // CHECK:STDOUT: %Int.as.Copy.impl.Op.type.824: type = fn_type @Int.as.Copy.impl.Op, @Int.as.Copy.impl(%N) [symbolic] // CHECK:STDOUT: %Int.as.Copy.impl.Op.9b9: %Int.as.Copy.impl.Op.type.824 = struct_value () [symbolic] // CHECK:STDOUT: %T.67d: type = symbolic_binding T, 0 [symbolic] // CHECK:STDOUT: %ptr.as.Copy.impl.Op.type.2d4: type = fn_type @ptr.as.Copy.impl.Op, @ptr.as.Copy.impl(%T.67d) [symbolic] // CHECK:STDOUT: %ptr.as.Copy.impl.Op.74e: %ptr.as.Copy.impl.Op.type.2d4 = struct_value () [symbolic] // CHECK:STDOUT: %Copy.impl_witness.f17: = impl_witness imports.%Copy.impl_witness_table.e76, @Int.as.Copy.impl(%int_32) [concrete] // CHECK:STDOUT: %Int.as.Copy.impl.Op.type.546: type = fn_type @Int.as.Copy.impl.Op, @Int.as.Copy.impl(%int_32) [concrete] // CHECK:STDOUT: %Int.as.Copy.impl.Op.664: %Int.as.Copy.impl.Op.type.546 = struct_value () [concrete] // CHECK:STDOUT: %Copy.facet.de4: %Copy.type = facet_value %i32, (%Copy.impl_witness.f17) [concrete] // CHECK:STDOUT: %Copy.WithSelf.Op.type.081: type = fn_type @Copy.WithSelf.Op, @Copy.WithSelf(%Copy.facet.de4) [concrete] // CHECK:STDOUT: %.8e2: type = fn_type_with_self_type %Copy.WithSelf.Op.type.081, %Copy.facet.de4 [concrete] // CHECK:STDOUT: %Int.as.Copy.impl.Op.specific_fn: = specific_function %Int.as.Copy.impl.Op.664, @Int.as.Copy.impl.Op(%int_32) [concrete] // CHECK:STDOUT: %G.type: type = fn_type @G [concrete] // CHECK:STDOUT: %G: %G.type = struct_value () [concrete] // CHECK:STDOUT: %false: bool = bool_literal false [concrete] // CHECK:STDOUT: %Constant.type: type = fn_type @Constant [concrete] // CHECK:STDOUT: %Constant: %Constant.type = struct_value () [concrete] // CHECK:STDOUT: %ptr.235: type = ptr_type %i32 [concrete] // CHECK:STDOUT: %pattern_type.fe8: type = pattern_type %ptr.235 [concrete] // CHECK:STDOUT: %Copy.impl_witness.843: = impl_witness imports.%Copy.impl_witness_table.c3a, @ptr.as.Copy.impl(%i32) [concrete] // CHECK:STDOUT: %ptr.as.Copy.impl.Op.type.c3c: type = fn_type @ptr.as.Copy.impl.Op, @ptr.as.Copy.impl(%i32) [concrete] // CHECK:STDOUT: %ptr.as.Copy.impl.Op.011: %ptr.as.Copy.impl.Op.type.c3c = struct_value () [concrete] // CHECK:STDOUT: %Copy.facet.a7b: %Copy.type = facet_value %ptr.235, (%Copy.impl_witness.843) [concrete] // CHECK:STDOUT: %Copy.WithSelf.Op.type.e01: type = fn_type @Copy.WithSelf.Op, @Copy.WithSelf(%Copy.facet.a7b) [concrete] // CHECK:STDOUT: %.a62: type = fn_type_with_self_type %Copy.WithSelf.Op.type.e01, %Copy.facet.a7b [concrete] // CHECK:STDOUT: %ptr.as.Copy.impl.Op.specific_fn: = specific_function %ptr.as.Copy.impl.Op.011, @ptr.as.Copy.impl.Op(%i32) [concrete] // CHECK:STDOUT: %Destroy.type: type = facet_type <@Destroy> [concrete] // CHECK:STDOUT: %Destroy.Op.type.bae255.1: type = fn_type @Destroy.Op.loc28 [concrete] // CHECK:STDOUT: %Destroy.Op.651ba6.1: %Destroy.Op.type.bae255.1 = struct_value () [concrete] // CHECK:STDOUT: %Destroy.Op.type.bae255.2: type = fn_type @Destroy.Op.loc27 [concrete] // CHECK:STDOUT: %Destroy.Op.651ba6.2: %Destroy.Op.type.bae255.2 = struct_value () [concrete] // CHECK:STDOUT: %PartiallyConstant.type: type = fn_type @PartiallyConstant [concrete] // CHECK:STDOUT: %PartiallyConstant: %PartiallyConstant.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: .Int = %Core.Int // CHECK:STDOUT: .ImplicitAs = %Core.ImplicitAs // CHECK:STDOUT: .Copy = %Core.Copy // CHECK:STDOUT: .Destroy = %Core.Destroy // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: %Core.Int: %Int.type = import_ref Core//prelude/parts/int, Int, loaded [concrete = constants.%Int.generic] // CHECK:STDOUT: %Core.ImplicitAs: %ImplicitAs.type.cc7 = import_ref Core//prelude/parts/as, ImplicitAs, loaded [concrete = constants.%ImplicitAs.generic] // CHECK:STDOUT: %Core.import_ref.42d: @Core.IntLiteral.as.ImplicitAs.impl.%Core.IntLiteral.as.ImplicitAs.impl.Convert.type (%Core.IntLiteral.as.ImplicitAs.impl.Convert.type.4e6) = import_ref Core//prelude/parts/int, loc{{\d+_\d+}}, loaded [symbolic = @Core.IntLiteral.as.ImplicitAs.impl.%Core.IntLiteral.as.ImplicitAs.impl.Convert (constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.3c2)] // CHECK:STDOUT: %ImplicitAs.impl_witness_table.74f = impl_witness_table (%Core.import_ref.42d), @Core.IntLiteral.as.ImplicitAs.impl [concrete] // CHECK:STDOUT: %Core.Copy: type = import_ref Core//prelude/parts/copy, Copy, loaded [concrete = constants.%Copy.type] // CHECK:STDOUT: %Core.import_ref.18d: @Int.as.Copy.impl.%Int.as.Copy.impl.Op.type (%Int.as.Copy.impl.Op.type.824) = import_ref Core//prelude/parts/int, loc{{\d+_\d+}}, loaded [symbolic = @Int.as.Copy.impl.%Int.as.Copy.impl.Op (constants.%Int.as.Copy.impl.Op.9b9)] // CHECK:STDOUT: %Copy.impl_witness_table.e76 = impl_witness_table (%Core.import_ref.18d), @Int.as.Copy.impl [concrete] // CHECK:STDOUT: %Core.import_ref.203: @ptr.as.Copy.impl.%ptr.as.Copy.impl.Op.type (%ptr.as.Copy.impl.Op.type.2d4) = import_ref Core//prelude/parts/copy, loc{{\d+_\d+}}, loaded [symbolic = @ptr.as.Copy.impl.%ptr.as.Copy.impl.Op (constants.%ptr.as.Copy.impl.Op.74e)] // CHECK:STDOUT: %Copy.impl_witness_table.c3a = impl_witness_table (%Core.import_ref.203), @ptr.as.Copy.impl [concrete] // CHECK:STDOUT: %Core.Destroy: type = import_ref Core//prelude/parts/destroy, Destroy, loaded [concrete = constants.%Destroy.type] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .A = %A.decl // CHECK:STDOUT: .B = %B.decl // CHECK:STDOUT: .F = %F.decl // CHECK:STDOUT: .G = %G.decl // CHECK:STDOUT: .Constant = %Constant.decl // CHECK:STDOUT: .PartiallyConstant = %PartiallyConstant.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %A.decl: %A.type = fn_decl @A [concrete = constants.%A] { // CHECK:STDOUT: %return.patt: %pattern_type.7ce = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: %pattern_type.7ce = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %i32: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %.loc15_11: Core.Form = init_form %i32 [concrete = constants.%.ff5] // CHECK:STDOUT: %return.param: ref %i32 = out_param call_param0 // CHECK:STDOUT: %return: ref %i32 = return_slot %return.param // CHECK:STDOUT: } // CHECK:STDOUT: %B.decl: %B.type = fn_decl @B [concrete = constants.%B] { // CHECK:STDOUT: %return.patt: %pattern_type.7ce = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: %pattern_type.7ce = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %i32: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %.loc16_11: Core.Form = init_form %i32 [concrete = constants.%.ff5] // CHECK:STDOUT: %return.param: ref %i32 = out_param call_param0 // CHECK:STDOUT: %return: ref %i32 = return_slot %return.param // CHECK:STDOUT: } // CHECK:STDOUT: %F.decl: %F.type = fn_decl @F [concrete = constants.%F] { // CHECK:STDOUT: %return.patt: %pattern_type.7ce = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: %pattern_type.7ce = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %i32: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %.loc18: Core.Form = init_form %i32 [concrete = constants.%.ff5] // CHECK:STDOUT: %return.param: ref %i32 = out_param call_param0 // CHECK:STDOUT: %return: ref %i32 = return_slot %return.param // CHECK:STDOUT: } // CHECK:STDOUT: %G.decl: %G.type = fn_decl @G [concrete = constants.%G] { // CHECK:STDOUT: %return.patt: %pattern_type.7ce = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: %pattern_type.7ce = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %i32: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %.loc22: Core.Form = init_form %i32 [concrete = constants.%.ff5] // CHECK:STDOUT: %return.param: ref %i32 = out_param call_param0 // CHECK:STDOUT: %return: ref %i32 = return_slot %return.param // CHECK:STDOUT: } // CHECK:STDOUT: %Constant.decl: %Constant.type = fn_decl @Constant [concrete = constants.%Constant] { // CHECK:STDOUT: %return.patt: %pattern_type.7ce = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: %pattern_type.7ce = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %i32.loc26: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %.loc26: Core.Form = init_form %i32.loc26 [concrete = constants.%.ff5] // CHECK:STDOUT: %return.param: ref %i32 = out_param call_param0 // CHECK:STDOUT: %return: ref %i32 = return_slot %return.param // CHECK:STDOUT: } // CHECK:STDOUT: %PartiallyConstant.decl: %PartiallyConstant.type = fn_decl @PartiallyConstant [concrete = constants.%PartiallyConstant] { // CHECK:STDOUT: %t.patt: %pattern_type.98f = value_binding_pattern t [concrete] // CHECK:STDOUT: %t.param_patt: %pattern_type.98f = value_param_pattern %t.patt [concrete] // CHECK:STDOUT: %return.patt: %pattern_type.7ce = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: %pattern_type.7ce = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %i32.loc32: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %.loc32_34: Core.Form = init_form %i32.loc32 [concrete = constants.%.ff5] // CHECK:STDOUT: %t.param: type = value_param call_param0 // CHECK:STDOUT: %.loc32_25: type = type_literal type [concrete = type] // CHECK:STDOUT: %t: type = value_binding t, %t.param // CHECK:STDOUT: %return.param: ref %i32 = out_param call_param1 // CHECK:STDOUT: %return: ref %i32 = return_slot %return.param // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @A() -> out %return.param: %i32 { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %int_1: Core.IntLiteral = int_value 1 [concrete = constants.%int_1.5b8] // CHECK:STDOUT: %impl.elem0: %.545 = impl_witness_access constants.%ImplicitAs.impl_witness.6bc, element0 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5] // CHECK:STDOUT: %bound_method.loc15_25.1: = bound_method %int_1, %impl.elem0 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.bound.215] // CHECK:STDOUT: %specific_fn: = specific_function %impl.elem0, @Core.IntLiteral.as.ImplicitAs.impl.Convert(constants.%int_32) [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn] // CHECK:STDOUT: %bound_method.loc15_25.2: = bound_method %int_1, %specific_fn [concrete = constants.%bound_method.38b] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.call: init %i32 = call %bound_method.loc15_25.2(%int_1) [concrete = constants.%int_1.5d2] // CHECK:STDOUT: %.loc15_25: init %i32 = converted %int_1, %Core.IntLiteral.as.ImplicitAs.impl.Convert.call [concrete = constants.%int_1.5d2] // CHECK:STDOUT: return %.loc15_25 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @B() -> out %return.param: %i32 { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %int_2: Core.IntLiteral = int_value 2 [concrete = constants.%int_2.ecc] // CHECK:STDOUT: %impl.elem0: %.545 = impl_witness_access constants.%ImplicitAs.impl_witness.6bc, element0 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5] // CHECK:STDOUT: %bound_method.loc16_25.1: = bound_method %int_2, %impl.elem0 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.bound.4e5] // CHECK:STDOUT: %specific_fn: = specific_function %impl.elem0, @Core.IntLiteral.as.ImplicitAs.impl.Convert(constants.%int_32) [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn] // CHECK:STDOUT: %bound_method.loc16_25.2: = bound_method %int_2, %specific_fn [concrete = constants.%bound_method.646] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.call: init %i32 = call %bound_method.loc16_25.2(%int_2) [concrete = constants.%int_2.ef8] // CHECK:STDOUT: %.loc16_25: init %i32 = converted %int_2, %Core.IntLiteral.as.ImplicitAs.impl.Convert.call [concrete = constants.%int_2.ef8] // CHECK:STDOUT: return %.loc16_25 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @F() -> out %return.param: %i32 { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %true: bool = bool_literal true [concrete = constants.%true] // CHECK:STDOUT: if %true br !if.expr.then else br !if.expr.else // CHECK:STDOUT: // CHECK:STDOUT: !if.expr.then: // CHECK:STDOUT: %A.ref: %A.type = name_ref A, file.%A.decl [concrete = constants.%A] // CHECK:STDOUT: %A.call: init %i32 = call %A.ref() // CHECK:STDOUT: %.loc19_25.1: %i32 = value_of_initializer %A.call // CHECK:STDOUT: %.loc19_25.2: %i32 = converted %A.call, %.loc19_25.1 // CHECK:STDOUT: br !if.expr.result(%.loc19_25.2) // CHECK:STDOUT: // CHECK:STDOUT: !if.expr.else: // CHECK:STDOUT: %B.ref: %B.type = name_ref B, file.%B.decl [concrete = constants.%B] // CHECK:STDOUT: %B.call: init %i32 = call %B.ref() // CHECK:STDOUT: %.loc19_27.1: %i32 = value_of_initializer %B.call // CHECK:STDOUT: %.loc19_27.2: %i32 = converted %B.call, %.loc19_27.1 // CHECK:STDOUT: br !if.expr.result(%.loc19_27.2) // CHECK:STDOUT: // CHECK:STDOUT: !if.expr.result: // CHECK:STDOUT: %.loc19_10: %i32 = block_arg !if.expr.result // CHECK:STDOUT: %impl.elem0: %.8e2 = impl_witness_access constants.%Copy.impl_witness.f17, element0 [concrete = constants.%Int.as.Copy.impl.Op.664] // CHECK:STDOUT: %bound_method.loc19_10.1: = bound_method %.loc19_10, %impl.elem0 // CHECK:STDOUT: %specific_fn: = specific_function %impl.elem0, @Int.as.Copy.impl.Op(constants.%int_32) [concrete = constants.%Int.as.Copy.impl.Op.specific_fn] // CHECK:STDOUT: %bound_method.loc19_10.2: = bound_method %.loc19_10, %specific_fn // CHECK:STDOUT: %Int.as.Copy.impl.Op.call: init %i32 = call %bound_method.loc19_10.2(%.loc19_10) // CHECK:STDOUT: return %Int.as.Copy.impl.Op.call // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @G() -> out %return.param: %i32 { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %false: bool = bool_literal false [concrete = constants.%false] // CHECK:STDOUT: if %false br !if.expr.then else br !if.expr.else // CHECK:STDOUT: // CHECK:STDOUT: !if.expr.then: // CHECK:STDOUT: %A.ref: %A.type = name_ref A, file.%A.decl [concrete = constants.%A] // CHECK:STDOUT: %A.call: init %i32 = call %A.ref() // CHECK:STDOUT: %.loc23_26.1: %i32 = value_of_initializer %A.call // CHECK:STDOUT: %.loc23_26.2: %i32 = converted %A.call, %.loc23_26.1 // CHECK:STDOUT: br !if.expr.result(%.loc23_26.2) // CHECK:STDOUT: // CHECK:STDOUT: !if.expr.else: // CHECK:STDOUT: %B.ref: %B.type = name_ref B, file.%B.decl [concrete = constants.%B] // CHECK:STDOUT: %B.call: init %i32 = call %B.ref() // CHECK:STDOUT: %.loc23_28.1: %i32 = value_of_initializer %B.call // CHECK:STDOUT: %.loc23_28.2: %i32 = converted %B.call, %.loc23_28.1 // CHECK:STDOUT: br !if.expr.result(%.loc23_28.2) // CHECK:STDOUT: // CHECK:STDOUT: !if.expr.result: // CHECK:STDOUT: %.loc23_10: %i32 = block_arg !if.expr.result // CHECK:STDOUT: %impl.elem0: %.8e2 = impl_witness_access constants.%Copy.impl_witness.f17, element0 [concrete = constants.%Int.as.Copy.impl.Op.664] // CHECK:STDOUT: %bound_method.loc23_10.1: = bound_method %.loc23_10, %impl.elem0 // CHECK:STDOUT: %specific_fn: = specific_function %impl.elem0, @Int.as.Copy.impl.Op(constants.%int_32) [concrete = constants.%Int.as.Copy.impl.Op.specific_fn] // CHECK:STDOUT: %bound_method.loc23_10.2: = bound_method %.loc23_10, %specific_fn // CHECK:STDOUT: %Int.as.Copy.impl.Op.call: init %i32 = call %bound_method.loc23_10.2(%.loc23_10) // CHECK:STDOUT: return %Int.as.Copy.impl.Op.call // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @Constant() -> out %return.param: %i32 { // CHECK:STDOUT: !entry: // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %v.patt: %pattern_type.7ce = ref_binding_pattern v [concrete] // CHECK:STDOUT: %v.var_patt: %pattern_type.7ce = var_pattern %v.patt [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %v.var: ref %i32 = var %v.var_patt // CHECK:STDOUT: %int_1: Core.IntLiteral = int_value 1 [concrete = constants.%int_1.5b8] // CHECK:STDOUT: %impl.elem0.loc27: %.545 = impl_witness_access constants.%ImplicitAs.impl_witness.6bc, element0 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5] // CHECK:STDOUT: %bound_method.loc27_3.1: = bound_method %int_1, %impl.elem0.loc27 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.bound.215] // CHECK:STDOUT: %specific_fn.loc27: = specific_function %impl.elem0.loc27, @Core.IntLiteral.as.ImplicitAs.impl.Convert(constants.%int_32) [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn] // CHECK:STDOUT: %bound_method.loc27_3.2: = bound_method %int_1, %specific_fn.loc27 [concrete = constants.%bound_method.38b] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.call: init %i32 = call %bound_method.loc27_3.2(%int_1) [concrete = constants.%int_1.5d2] // CHECK:STDOUT: %.loc27_3: init %i32 = converted %int_1, %Core.IntLiteral.as.ImplicitAs.impl.Convert.call [concrete = constants.%int_1.5d2] // CHECK:STDOUT: assign %v.var, %.loc27_3 // CHECK:STDOUT: br !.loc27_13 // CHECK:STDOUT: // CHECK:STDOUT: !.loc27_13: // CHECK:STDOUT: %true: bool = bool_literal true [concrete = constants.%true] // CHECK:STDOUT: if %true br !if.expr.then.loc27 else br !if.expr.else.loc27 // CHECK:STDOUT: // CHECK:STDOUT: !if.expr.then.loc27: // CHECK:STDOUT: %i32.loc27_23: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: br !if.expr.result.loc27(%i32.loc27_23) // CHECK:STDOUT: // CHECK:STDOUT: !if.expr.else.loc27: // CHECK:STDOUT: %i32.loc27_32: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %ptr.loc27: type = ptr_type %i32.loc27_32 [concrete = constants.%ptr.235] // CHECK:STDOUT: br !if.expr.result.loc27(%ptr.loc27) // CHECK:STDOUT: // CHECK:STDOUT: !if.expr.result.loc27: // CHECK:STDOUT: %.loc27_10: type = block_arg !if.expr.result.loc27 [concrete = constants.%i32] // CHECK:STDOUT: br !.loc27_7 // CHECK:STDOUT: // CHECK:STDOUT: !.loc27_7: // CHECK:STDOUT: %v: ref %i32 = ref_binding v, %v.var // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %w.patt: %pattern_type.fe8 = ref_binding_pattern w [concrete] // CHECK:STDOUT: %w.var_patt: %pattern_type.fe8 = var_pattern %w.patt [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %w.var: ref %ptr.235 = var %w.var_patt // CHECK:STDOUT: %v.ref: ref %i32 = name_ref v, %v // CHECK:STDOUT: %addr: %ptr.235 = addr_of %v.ref // CHECK:STDOUT: %impl.elem0.loc28: %.a62 = impl_witness_access constants.%Copy.impl_witness.843, element0 [concrete = constants.%ptr.as.Copy.impl.Op.011] // CHECK:STDOUT: %bound_method.loc28_40.1: = bound_method %addr, %impl.elem0.loc28 // CHECK:STDOUT: %specific_fn.loc28: = specific_function %impl.elem0.loc28, @ptr.as.Copy.impl.Op(constants.%i32) [concrete = constants.%ptr.as.Copy.impl.Op.specific_fn] // CHECK:STDOUT: %bound_method.loc28_40.2: = bound_method %addr, %specific_fn.loc28 // CHECK:STDOUT: %ptr.as.Copy.impl.Op.call: init %ptr.235 = call %bound_method.loc28_40.2(%addr) // CHECK:STDOUT: assign %w.var, %ptr.as.Copy.impl.Op.call // CHECK:STDOUT: br !.loc28_13 // CHECK:STDOUT: // CHECK:STDOUT: !.loc28_13: // CHECK:STDOUT: %false: bool = bool_literal false [concrete = constants.%false] // CHECK:STDOUT: if %false br !if.expr.then.loc28 else br !if.expr.else.loc28 // CHECK:STDOUT: // CHECK:STDOUT: !if.expr.then.loc28: // CHECK:STDOUT: %i32.loc28_24: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: br !if.expr.result.loc28(%i32.loc28_24) // CHECK:STDOUT: // CHECK:STDOUT: !if.expr.else.loc28: // CHECK:STDOUT: %i32.loc28_33: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %ptr.loc28: type = ptr_type %i32.loc28_33 [concrete = constants.%ptr.235] // CHECK:STDOUT: br !if.expr.result.loc28(%ptr.loc28) // CHECK:STDOUT: // CHECK:STDOUT: !if.expr.result.loc28: // CHECK:STDOUT: %.loc28: type = block_arg !if.expr.result.loc28 [concrete = constants.%ptr.235] // CHECK:STDOUT: br !.loc28_7 // CHECK:STDOUT: // CHECK:STDOUT: !.loc28_7: // CHECK:STDOUT: %w: ref %ptr.235 = ref_binding w, %w.var // CHECK:STDOUT: %w.ref: ref %ptr.235 = name_ref w, %w // CHECK:STDOUT: %.loc29_11: %ptr.235 = acquire_value %w.ref // CHECK:STDOUT: %.loc29_10.1: ref %i32 = deref %.loc29_11 // CHECK:STDOUT: %.loc29_10.2: %i32 = acquire_value %.loc29_10.1 // CHECK:STDOUT: %impl.elem0.loc29: %.8e2 = impl_witness_access constants.%Copy.impl_witness.f17, element0 [concrete = constants.%Int.as.Copy.impl.Op.664] // CHECK:STDOUT: %bound_method.loc29_10.1: = bound_method %.loc29_10.2, %impl.elem0.loc29 // CHECK:STDOUT: %specific_fn.loc29: = specific_function %impl.elem0.loc29, @Int.as.Copy.impl.Op(constants.%int_32) [concrete = constants.%Int.as.Copy.impl.Op.specific_fn] // CHECK:STDOUT: %bound_method.loc29_10.2: = bound_method %.loc29_10.2, %specific_fn.loc29 // CHECK:STDOUT: %Int.as.Copy.impl.Op.call: init %i32 = call %bound_method.loc29_10.2(%.loc29_10.2) // CHECK:STDOUT: %Destroy.Op.bound.loc28: = bound_method %w.var, constants.%Destroy.Op.651ba6.1 // CHECK:STDOUT: %Destroy.Op.call.loc28: init %empty_tuple.type = call %Destroy.Op.bound.loc28(%w.var) // CHECK:STDOUT: %Destroy.Op.bound.loc27: = bound_method %v.var, constants.%Destroy.Op.651ba6.2 // CHECK:STDOUT: %Destroy.Op.call.loc27: init %empty_tuple.type = call %Destroy.Op.bound.loc27(%v.var) // CHECK:STDOUT: return %Int.as.Copy.impl.Op.call // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @Destroy.Op.loc28(%self.param: ref %ptr.235) = "no_op"; // CHECK:STDOUT: // CHECK:STDOUT: fn @Destroy.Op.loc27(%self.param: ref %i32) = "no_op"; // CHECK:STDOUT: // CHECK:STDOUT: fn @PartiallyConstant(%t.param: type) -> out %return.param: %i32 { // CHECK:STDOUT: !entry: // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %v.patt: %pattern_type.7ce = ref_binding_pattern v [concrete] // CHECK:STDOUT: %v.var_patt: %pattern_type.7ce = var_pattern %v.patt [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %v.var: ref %i32 = var %v.var_patt // CHECK:STDOUT: %int_1: Core.IntLiteral = int_value 1 [concrete = constants.%int_1.5b8] // CHECK:STDOUT: %impl.elem0.loc33: %.545 = impl_witness_access constants.%ImplicitAs.impl_witness.6bc, element0 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5] // CHECK:STDOUT: %bound_method.loc33_3.1: = bound_method %int_1, %impl.elem0.loc33 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.bound.215] // CHECK:STDOUT: %specific_fn.loc33: = specific_function %impl.elem0.loc33, @Core.IntLiteral.as.ImplicitAs.impl.Convert(constants.%int_32) [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn] // CHECK:STDOUT: %bound_method.loc33_3.2: = bound_method %int_1, %specific_fn.loc33 [concrete = constants.%bound_method.38b] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.call: init %i32 = call %bound_method.loc33_3.2(%int_1) [concrete = constants.%int_1.5d2] // CHECK:STDOUT: %.loc33_3: init %i32 = converted %int_1, %Core.IntLiteral.as.ImplicitAs.impl.Convert.call [concrete = constants.%int_1.5d2] // CHECK:STDOUT: assign %v.var, %.loc33_3 // CHECK:STDOUT: br !.loc33_13 // CHECK:STDOUT: // CHECK:STDOUT: !.loc33_13: // CHECK:STDOUT: %true: bool = bool_literal true [concrete = constants.%true] // CHECK:STDOUT: if %true br !if.expr.then.loc33 else br !if.expr.else.loc33 // CHECK:STDOUT: // CHECK:STDOUT: !if.expr.then.loc33: // CHECK:STDOUT: %i32.loc33: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: br !if.expr.result.loc33(%i32.loc33) // CHECK:STDOUT: // CHECK:STDOUT: !if.expr.else.loc33: // CHECK:STDOUT: %t.ref.loc33: type = name_ref t, %t // CHECK:STDOUT: br !if.expr.result.loc33(%t.ref.loc33) // CHECK:STDOUT: // CHECK:STDOUT: !if.expr.result.loc33: // CHECK:STDOUT: %.loc33_10: type = block_arg !if.expr.result.loc33 [concrete = constants.%i32] // CHECK:STDOUT: br !.loc33_7 // CHECK:STDOUT: // CHECK:STDOUT: !.loc33_7: // CHECK:STDOUT: %v: ref %i32 = ref_binding v, %v.var // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %w.patt: %pattern_type.fe8 = ref_binding_pattern w [concrete] // CHECK:STDOUT: %w.var_patt: %pattern_type.fe8 = var_pattern %w.patt [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %w.var: ref %ptr.235 = var %w.var_patt // CHECK:STDOUT: %v.ref: ref %i32 = name_ref v, %v // CHECK:STDOUT: %addr: %ptr.235 = addr_of %v.ref // CHECK:STDOUT: %impl.elem0.loc34: %.a62 = impl_witness_access constants.%Copy.impl_witness.843, element0 [concrete = constants.%ptr.as.Copy.impl.Op.011] // CHECK:STDOUT: %bound_method.loc34_38.1: = bound_method %addr, %impl.elem0.loc34 // CHECK:STDOUT: %specific_fn.loc34: = specific_function %impl.elem0.loc34, @ptr.as.Copy.impl.Op(constants.%i32) [concrete = constants.%ptr.as.Copy.impl.Op.specific_fn] // CHECK:STDOUT: %bound_method.loc34_38.2: = bound_method %addr, %specific_fn.loc34 // CHECK:STDOUT: %ptr.as.Copy.impl.Op.call: init %ptr.235 = call %bound_method.loc34_38.2(%addr) // CHECK:STDOUT: assign %w.var, %ptr.as.Copy.impl.Op.call // CHECK:STDOUT: br !.loc34_13 // CHECK:STDOUT: // CHECK:STDOUT: !.loc34_13: // CHECK:STDOUT: %false: bool = bool_literal false [concrete = constants.%false] // CHECK:STDOUT: if %false br !if.expr.then.loc34 else br !if.expr.else.loc34 // CHECK:STDOUT: // CHECK:STDOUT: !if.expr.then.loc34: // CHECK:STDOUT: %t.ref.loc34: type = name_ref t, %t // CHECK:STDOUT: br !if.expr.result.loc34(%t.ref.loc34) // CHECK:STDOUT: // CHECK:STDOUT: !if.expr.else.loc34: // CHECK:STDOUT: %i32.loc34: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %ptr: type = ptr_type %i32.loc34 [concrete = constants.%ptr.235] // CHECK:STDOUT: br !if.expr.result.loc34(%ptr) // CHECK:STDOUT: // CHECK:STDOUT: !if.expr.result.loc34: // CHECK:STDOUT: %.loc34: type = block_arg !if.expr.result.loc34 [concrete = constants.%ptr.235] // CHECK:STDOUT: br !.loc34_7 // CHECK:STDOUT: // CHECK:STDOUT: !.loc34_7: // CHECK:STDOUT: %w: ref %ptr.235 = ref_binding w, %w.var // CHECK:STDOUT: %w.ref: ref %ptr.235 = name_ref w, %w // CHECK:STDOUT: %.loc35_11: %ptr.235 = acquire_value %w.ref // CHECK:STDOUT: %.loc35_10.1: ref %i32 = deref %.loc35_11 // CHECK:STDOUT: %.loc35_10.2: %i32 = acquire_value %.loc35_10.1 // CHECK:STDOUT: %impl.elem0.loc35: %.8e2 = impl_witness_access constants.%Copy.impl_witness.f17, element0 [concrete = constants.%Int.as.Copy.impl.Op.664] // CHECK:STDOUT: %bound_method.loc35_10.1: = bound_method %.loc35_10.2, %impl.elem0.loc35 // CHECK:STDOUT: %specific_fn.loc35: = specific_function %impl.elem0.loc35, @Int.as.Copy.impl.Op(constants.%int_32) [concrete = constants.%Int.as.Copy.impl.Op.specific_fn] // CHECK:STDOUT: %bound_method.loc35_10.2: = bound_method %.loc35_10.2, %specific_fn.loc35 // CHECK:STDOUT: %Int.as.Copy.impl.Op.call: init %i32 = call %bound_method.loc35_10.2(%.loc35_10.2) // CHECK:STDOUT: %Destroy.Op.bound.loc34: = bound_method %w.var, constants.%Destroy.Op.651ba6.1 // CHECK:STDOUT: %Destroy.Op.call.loc34: init %empty_tuple.type = call %Destroy.Op.bound.loc34(%w.var) // CHECK:STDOUT: %Destroy.Op.bound.loc33: = bound_method %v.var, constants.%Destroy.Op.651ba6.2 // CHECK:STDOUT: %Destroy.Op.call.loc33: init %empty_tuple.type = call %Destroy.Op.bound.loc33(%v.var) // CHECK:STDOUT: return %Int.as.Copy.impl.Op.call // CHECK:STDOUT: } // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/if_expr/control_flow.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/primitives.carbon // TODO: Add ranges and switch to "--dump-sem-ir-ranges=only". // EXTRA-ARGS: --dump-sem-ir-ranges=if-present // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/if_expr/control_flow.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/if_expr/control_flow.carbon fn A() -> i32 { return 1; } fn B() -> i32 { return 2; } fn F(b: bool) -> i32 { return if b then A() else B(); } // CHECK:STDOUT: --- control_flow.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [concrete] // CHECK:STDOUT: %Int.type: type = generic_class_type @Int [concrete] // CHECK:STDOUT: %Int.generic: %Int.type = struct_value () [concrete] // CHECK:STDOUT: %N: Core.IntLiteral = symbolic_binding N, 0 [symbolic] // CHECK:STDOUT: %i32: type = class_type @Int, @Int(%int_32) [concrete] // CHECK:STDOUT: %.ff5: Core.Form = init_form %i32 [concrete] // CHECK:STDOUT: %pattern_type.7ce: type = pattern_type %i32 [concrete] // CHECK:STDOUT: %A.type: type = fn_type @A [concrete] // CHECK:STDOUT: %A: %A.type = struct_value () [concrete] // CHECK:STDOUT: %int_1.5b8: Core.IntLiteral = int_value 1 [concrete] // CHECK:STDOUT: %ImplicitAs.type.cc7: type = generic_interface_type @ImplicitAs [concrete] // CHECK:STDOUT: %ImplicitAs.generic: %ImplicitAs.type.cc7 = struct_value () [concrete] // CHECK:STDOUT: %ImplicitAs.type.e8c: type = facet_type <@ImplicitAs, @ImplicitAs(%i32)> [concrete] // CHECK:STDOUT: %To: Core.IntLiteral = symbolic_binding To, 0 [symbolic] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.4e6: type = fn_type @Core.IntLiteral.as.ImplicitAs.impl.Convert, @Core.IntLiteral.as.ImplicitAs.impl(%To) [symbolic] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.3c2: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.4e6 = struct_value () [symbolic] // CHECK:STDOUT: %ImplicitAs.impl_witness.6bc: = impl_witness imports.%ImplicitAs.impl_witness_table.74f, @Core.IntLiteral.as.ImplicitAs.impl(%int_32) [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.e0d: type = fn_type @Core.IntLiteral.as.ImplicitAs.impl.Convert, @Core.IntLiteral.as.ImplicitAs.impl(%int_32) [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.e0d = struct_value () [concrete] // CHECK:STDOUT: %ImplicitAs.facet: %ImplicitAs.type.e8c = facet_value Core.IntLiteral, (%ImplicitAs.impl_witness.6bc) [concrete] // CHECK:STDOUT: %ImplicitAs.WithSelf.Convert.type.b37: type = fn_type @ImplicitAs.WithSelf.Convert, @ImplicitAs.WithSelf(%i32, %ImplicitAs.facet) [concrete] // CHECK:STDOUT: %.545: type = fn_type_with_self_type %ImplicitAs.WithSelf.Convert.type.b37, %ImplicitAs.facet [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.bound.215: = bound_method %int_1.5b8, %Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5 [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn: = specific_function %Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5, @Core.IntLiteral.as.ImplicitAs.impl.Convert(%int_32) [concrete] // CHECK:STDOUT: %bound_method.38b: = bound_method %int_1.5b8, %Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn [concrete] // CHECK:STDOUT: %int_1.5d2: %i32 = int_value 1 [concrete] // CHECK:STDOUT: %B.type: type = fn_type @B [concrete] // CHECK:STDOUT: %B: %B.type = struct_value () [concrete] // CHECK:STDOUT: %int_2.ecc: Core.IntLiteral = int_value 2 [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.bound.4e5: = bound_method %int_2.ecc, %Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5 [concrete] // CHECK:STDOUT: %bound_method.646: = bound_method %int_2.ecc, %Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn [concrete] // CHECK:STDOUT: %int_2.ef8: %i32 = int_value 2 [concrete] // CHECK:STDOUT: %Bool.type: type = fn_type @Bool [concrete] // CHECK:STDOUT: %Bool: %Bool.type = struct_value () [concrete] // CHECK:STDOUT: %pattern_type.831: type = pattern_type bool [concrete] // CHECK:STDOUT: %F.type: type = fn_type @F [concrete] // CHECK:STDOUT: %F: %F.type = struct_value () [concrete] // CHECK:STDOUT: %Copy.type: type = facet_type <@Copy> [concrete] // CHECK:STDOUT: %Int.as.Copy.impl.Op.type.824: type = fn_type @Int.as.Copy.impl.Op, @Int.as.Copy.impl(%N) [symbolic] // CHECK:STDOUT: %Int.as.Copy.impl.Op.9b9: %Int.as.Copy.impl.Op.type.824 = struct_value () [symbolic] // CHECK:STDOUT: %Copy.impl_witness.f17: = impl_witness imports.%Copy.impl_witness_table.e76, @Int.as.Copy.impl(%int_32) [concrete] // CHECK:STDOUT: %Int.as.Copy.impl.Op.type.546: type = fn_type @Int.as.Copy.impl.Op, @Int.as.Copy.impl(%int_32) [concrete] // CHECK:STDOUT: %Int.as.Copy.impl.Op.664: %Int.as.Copy.impl.Op.type.546 = struct_value () [concrete] // CHECK:STDOUT: %Copy.facet: %Copy.type = facet_value %i32, (%Copy.impl_witness.f17) [concrete] // CHECK:STDOUT: %Copy.WithSelf.Op.type.081: type = fn_type @Copy.WithSelf.Op, @Copy.WithSelf(%Copy.facet) [concrete] // CHECK:STDOUT: %.8e2: type = fn_type_with_self_type %Copy.WithSelf.Op.type.081, %Copy.facet [concrete] // CHECK:STDOUT: %Int.as.Copy.impl.Op.specific_fn: = specific_function %Int.as.Copy.impl.Op.664, @Int.as.Copy.impl.Op(%int_32) [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: .Int = %Core.Int // CHECK:STDOUT: .ImplicitAs = %Core.ImplicitAs // CHECK:STDOUT: .Bool = %Core.Bool // CHECK:STDOUT: .Copy = %Core.Copy // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: %Core.Int: %Int.type = import_ref Core//prelude/parts/int, Int, loaded [concrete = constants.%Int.generic] // CHECK:STDOUT: %Core.ImplicitAs: %ImplicitAs.type.cc7 = import_ref Core//prelude/parts/as, ImplicitAs, loaded [concrete = constants.%ImplicitAs.generic] // CHECK:STDOUT: %Core.import_ref.42d: @Core.IntLiteral.as.ImplicitAs.impl.%Core.IntLiteral.as.ImplicitAs.impl.Convert.type (%Core.IntLiteral.as.ImplicitAs.impl.Convert.type.4e6) = import_ref Core//prelude/parts/int, loc{{\d+_\d+}}, loaded [symbolic = @Core.IntLiteral.as.ImplicitAs.impl.%Core.IntLiteral.as.ImplicitAs.impl.Convert (constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.3c2)] // CHECK:STDOUT: %ImplicitAs.impl_witness_table.74f = impl_witness_table (%Core.import_ref.42d), @Core.IntLiteral.as.ImplicitAs.impl [concrete] // CHECK:STDOUT: %Core.Bool: %Bool.type = import_ref Core//prelude/parts/bool, Bool, loaded [concrete = constants.%Bool] // CHECK:STDOUT: %Core.Copy: type = import_ref Core//prelude/parts/copy, Copy, loaded [concrete = constants.%Copy.type] // CHECK:STDOUT: %Core.import_ref.18d: @Int.as.Copy.impl.%Int.as.Copy.impl.Op.type (%Int.as.Copy.impl.Op.type.824) = import_ref Core//prelude/parts/int, loc{{\d+_\d+}}, loaded [symbolic = @Int.as.Copy.impl.%Int.as.Copy.impl.Op (constants.%Int.as.Copy.impl.Op.9b9)] // CHECK:STDOUT: %Copy.impl_witness_table.e76 = impl_witness_table (%Core.import_ref.18d), @Int.as.Copy.impl [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .A = %A.decl // CHECK:STDOUT: .B = %B.decl // CHECK:STDOUT: .F = %F.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %A.decl: %A.type = fn_decl @A [concrete = constants.%A] { // CHECK:STDOUT: %return.patt: %pattern_type.7ce = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: %pattern_type.7ce = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %i32: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %.loc15_11: Core.Form = init_form %i32 [concrete = constants.%.ff5] // CHECK:STDOUT: %return.param: ref %i32 = out_param call_param0 // CHECK:STDOUT: %return: ref %i32 = return_slot %return.param // CHECK:STDOUT: } // CHECK:STDOUT: %B.decl: %B.type = fn_decl @B [concrete = constants.%B] { // CHECK:STDOUT: %return.patt: %pattern_type.7ce = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: %pattern_type.7ce = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %i32: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %.loc16_11: Core.Form = init_form %i32 [concrete = constants.%.ff5] // CHECK:STDOUT: %return.param: ref %i32 = out_param call_param0 // CHECK:STDOUT: %return: ref %i32 = return_slot %return.param // CHECK:STDOUT: } // CHECK:STDOUT: %F.decl: %F.type = fn_decl @F [concrete = constants.%F] { // CHECK:STDOUT: %b.patt: %pattern_type.831 = value_binding_pattern b [concrete] // CHECK:STDOUT: %b.param_patt: %pattern_type.831 = value_param_pattern %b.patt [concrete] // CHECK:STDOUT: %return.patt: %pattern_type.7ce = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: %pattern_type.7ce = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %i32: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %.loc18_18: Core.Form = init_form %i32 [concrete = constants.%.ff5] // CHECK:STDOUT: %b.param: bool = value_param call_param0 // CHECK:STDOUT: %.loc18_9: type = type_literal bool [concrete = bool] // CHECK:STDOUT: %b: bool = value_binding b, %b.param // CHECK:STDOUT: %return.param: ref %i32 = out_param call_param1 // CHECK:STDOUT: %return: ref %i32 = return_slot %return.param // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @A() -> out %return.param: %i32 { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %int_1: Core.IntLiteral = int_value 1 [concrete = constants.%int_1.5b8] // CHECK:STDOUT: %impl.elem0: %.545 = impl_witness_access constants.%ImplicitAs.impl_witness.6bc, element0 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5] // CHECK:STDOUT: %bound_method.loc15_25.1: = bound_method %int_1, %impl.elem0 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.bound.215] // CHECK:STDOUT: %specific_fn: = specific_function %impl.elem0, @Core.IntLiteral.as.ImplicitAs.impl.Convert(constants.%int_32) [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn] // CHECK:STDOUT: %bound_method.loc15_25.2: = bound_method %int_1, %specific_fn [concrete = constants.%bound_method.38b] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.call: init %i32 = call %bound_method.loc15_25.2(%int_1) [concrete = constants.%int_1.5d2] // CHECK:STDOUT: %.loc15_25: init %i32 = converted %int_1, %Core.IntLiteral.as.ImplicitAs.impl.Convert.call [concrete = constants.%int_1.5d2] // CHECK:STDOUT: return %.loc15_25 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @B() -> out %return.param: %i32 { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %int_2: Core.IntLiteral = int_value 2 [concrete = constants.%int_2.ecc] // CHECK:STDOUT: %impl.elem0: %.545 = impl_witness_access constants.%ImplicitAs.impl_witness.6bc, element0 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5] // CHECK:STDOUT: %bound_method.loc16_25.1: = bound_method %int_2, %impl.elem0 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.bound.4e5] // CHECK:STDOUT: %specific_fn: = specific_function %impl.elem0, @Core.IntLiteral.as.ImplicitAs.impl.Convert(constants.%int_32) [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn] // CHECK:STDOUT: %bound_method.loc16_25.2: = bound_method %int_2, %specific_fn [concrete = constants.%bound_method.646] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.call: init %i32 = call %bound_method.loc16_25.2(%int_2) [concrete = constants.%int_2.ef8] // CHECK:STDOUT: %.loc16_25: init %i32 = converted %int_2, %Core.IntLiteral.as.ImplicitAs.impl.Convert.call [concrete = constants.%int_2.ef8] // CHECK:STDOUT: return %.loc16_25 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @F(%b.param: bool) -> out %return.param: %i32 { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %b.ref: bool = name_ref b, %b // CHECK:STDOUT: if %b.ref br !if.expr.then else br !if.expr.else // CHECK:STDOUT: // CHECK:STDOUT: !if.expr.then: // CHECK:STDOUT: %A.ref: %A.type = name_ref A, file.%A.decl [concrete = constants.%A] // CHECK:STDOUT: %A.call: init %i32 = call %A.ref() // CHECK:STDOUT: %.loc19_22.1: %i32 = value_of_initializer %A.call // CHECK:STDOUT: %.loc19_22.2: %i32 = converted %A.call, %.loc19_22.1 // CHECK:STDOUT: br !if.expr.result(%.loc19_22.2) // CHECK:STDOUT: // CHECK:STDOUT: !if.expr.else: // CHECK:STDOUT: %B.ref: %B.type = name_ref B, file.%B.decl [concrete = constants.%B] // CHECK:STDOUT: %B.call: init %i32 = call %B.ref() // CHECK:STDOUT: %.loc19_24.1: %i32 = value_of_initializer %B.call // CHECK:STDOUT: %.loc19_24.2: %i32 = converted %B.call, %.loc19_24.1 // CHECK:STDOUT: br !if.expr.result(%.loc19_24.2) // CHECK:STDOUT: // CHECK:STDOUT: !if.expr.result: // CHECK:STDOUT: %.loc19_10: %i32 = block_arg !if.expr.result // CHECK:STDOUT: %impl.elem0: %.8e2 = impl_witness_access constants.%Copy.impl_witness.f17, element0 [concrete = constants.%Int.as.Copy.impl.Op.664] // CHECK:STDOUT: %bound_method.loc19_10.1: = bound_method %.loc19_10, %impl.elem0 // CHECK:STDOUT: %specific_fn: = specific_function %impl.elem0, @Int.as.Copy.impl.Op(constants.%int_32) [concrete = constants.%Int.as.Copy.impl.Op.specific_fn] // CHECK:STDOUT: %bound_method.loc19_10.2: = bound_method %.loc19_10, %specific_fn // CHECK:STDOUT: %Int.as.Copy.impl.Op.call: init %i32 = call %bound_method.loc19_10.2(%.loc19_10) // CHECK:STDOUT: return %Int.as.Copy.impl.Op.call // CHECK:STDOUT: } // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/if_expr/fail_not_in_function.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/primitives.carbon // TODO: Add ranges and switch to "--dump-sem-ir-ranges=only". // EXTRA-ARGS: --dump-sem-ir-ranges=if-present // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/if_expr/fail_not_in_function.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/if_expr/fail_not_in_function.carbon // --- fail_basic.carbon library "[[@TEST_NAME]]"; // CHECK:STDERR: fail_basic.carbon:[[@LINE+12]]:14: error: semantics TODO: `Control flow expressions are currently only supported inside functions.` [SemanticsTodo] // CHECK:STDERR: let x: i32 = if true then 1 else 0; // CHECK:STDERR: ^~~~~~~ // CHECK:STDERR: // CHECK:STDERR: fail_basic.carbon:[[@LINE+8]]:22: error: semantics TODO: `Control flow expressions are currently only supported inside functions.` [SemanticsTodo] // CHECK:STDERR: let x: i32 = if true then 1 else 0; // CHECK:STDERR: ^~~~~~ // CHECK:STDERR: // CHECK:STDERR: fail_basic.carbon:[[@LINE+4]]:14: error: semantics TODO: `Control flow expressions are currently only supported inside functions.` [SemanticsTodo] // CHECK:STDERR: let x: i32 = if true then 1 else 0; // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: let x: i32 = if true then 1 else 0; // --- fail_types.carbon library "[[@TEST_NAME]]"; // CHECK:STDERR: fail_types.carbon:[[@LINE+4]]:8: error: semantics TODO: `Control flow expressions are currently only supported inside functions.` [SemanticsTodo] // CHECK:STDERR: var y: if true then i32 else f64; // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: var y: if true then i32 else f64; // --- fail_in_param.carbon library "[[@TEST_NAME]]"; // CHECK:STDERR: fail_in_param.carbon:[[@LINE+4]]:9: error: semantics TODO: `Control flow expressions are currently only supported inside functions.` [SemanticsTodo] // CHECK:STDERR: fn F(a: if true then i32 else f64); // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: fn F(a: if true then i32 else f64); // --- fail_class.carbon library "[[@TEST_NAME]]"; class C { // CHECK:STDERR: fail_class.carbon:[[@LINE+12]]:10: error: semantics TODO: `Control flow expressions are currently only supported inside functions.` [SemanticsTodo] // CHECK:STDERR: var n: if true then i32 else f64; // CHECK:STDERR: ^~~~~~~ // CHECK:STDERR: // CHECK:STDERR: fail_class.carbon:[[@LINE+8]]:18: error: semantics TODO: `Control flow expressions are currently only supported inside functions.` [SemanticsTodo] // CHECK:STDERR: var n: if true then i32 else f64; // CHECK:STDERR: ^~~~~~~~ // CHECK:STDERR: // CHECK:STDERR: fail_class.carbon:[[@LINE+4]]:10: error: semantics TODO: `Control flow expressions are currently only supported inside functions.` [SemanticsTodo] // CHECK:STDERR: var n: if true then i32 else f64; // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: var n: if true then i32 else f64; } // --- fail_nested_class.carbon library "[[@TEST_NAME]]"; base class C(T:! type) {} fn F() { class B { // CHECK:STDERR: fail_nested_class.carbon:[[@LINE+4]]:20: error: semantics TODO: `Control flow expressions are currently only supported inside functions.` [SemanticsTodo] // CHECK:STDERR: extend base: C(if true then i32 else i32); // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: extend base: C(if true then i32 else i32); } } // CHECK:STDOUT: --- fail_basic.carbon // CHECK:STDOUT: // CHECK:STDOUT: --- fail_types.carbon // CHECK:STDOUT: // CHECK:STDOUT: --- fail_in_param.carbon // CHECK:STDOUT: // CHECK:STDOUT: --- fail_class.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %C: type = class_type @C [concrete] // CHECK:STDOUT: %true: bool = bool_literal true [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @C { // CHECK:STDOUT: %true: bool = bool_literal true [concrete = constants.%true] // CHECK:STDOUT: if %true br !if.expr.then else br !if.expr.else // CHECK:STDOUT: complete_type_witness = invalid // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%C // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- fail_nested_class.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %T: type = symbolic_binding T, 0 [symbolic] // CHECK:STDOUT: %C.type: type = generic_class_type @C [concrete] // CHECK:STDOUT: %C.generic: %C.type = struct_value () [concrete] // CHECK:STDOUT: %C: type = class_type @C, @C(%T) [symbolic] // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %complete_type.357: = complete_type_witness %empty_struct_type [concrete] // CHECK:STDOUT: %B: type = class_type @B [concrete] // CHECK:STDOUT: %true: bool = bool_literal true [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic class @C(.inst{{[0-9A-F]+}}.loc4_14: type) { // CHECK:STDOUT: %T: type = symbolic_binding T, 0 [symbolic = %T (constants.%T)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: // CHECK:STDOUT: class { // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type.357] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%C // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @B { // CHECK:STDOUT: %C.ref: %C.type = name_ref C, .inst{{[0-9A-F]+}}.loc4_24 [concrete = constants.%C.generic] // CHECK:STDOUT: %true: bool = bool_literal true [concrete = constants.%true] // CHECK:STDOUT: if %true br !if.expr.then else br !if.expr.else // CHECK:STDOUT: complete_type_witness = invalid // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%B // CHECK:STDOUT: .C = // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @F(); // CHECK:STDOUT: // CHECK:STDOUT: specific @C(constants.%T) { // CHECK:STDOUT: %T => constants.%T // CHECK:STDOUT: } // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/if_expr/fail_partial_constant.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/primitives.carbon // TODO: Add ranges and switch to "--dump-sem-ir-ranges=only". // EXTRA-ARGS: --dump-sem-ir-ranges=if-present // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/if_expr/fail_partial_constant.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/if_expr/fail_partial_constant.carbon // --- fail_non_constant_condition.carbon package NonConstantCondition; fn ConditionIsNonConstant(b: bool) { // We choose to not accept this even if both arms evaluate to the same // constant value, because it notionally involves evaluating a non-constant // condition. // CHECK:STDERR: fail_non_constant_condition.carbon:[[@LINE+4]]:17: error: cannot evaluate type expression [TypeExprEvaluationFailure] // CHECK:STDERR: var unused v: if b then i32 else i32 = 1; // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: var unused v: if b then i32 else i32 = 1; } // --- fail_non_constant_result.carbon package NonConstantResult; fn ChosenBranchIsNonConstant(t: type) { // CHECK:STDERR: fail_non_constant_result.carbon:[[@LINE+4]]:17: error: cannot evaluate type expression [TypeExprEvaluationFailure] // CHECK:STDERR: var unused v: if true then t else i32 = 1; // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: var unused v: if true then t else i32 = 1; // CHECK:STDERR: fail_non_constant_result.carbon:[[@LINE+4]]:17: error: cannot evaluate type expression [TypeExprEvaluationFailure] // CHECK:STDERR: var unused w: if false then i32 else t = 1; // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: var unused w: if false then i32 else t = 1; } // CHECK:STDOUT: --- fail_non_constant_condition.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %Bool.type: type = fn_type @Bool [concrete] // CHECK:STDOUT: %Bool: %Bool.type = struct_value () [concrete] // CHECK:STDOUT: %pattern_type.831: type = pattern_type bool [concrete] // CHECK:STDOUT: %ConditionIsNonConstant.type: type = fn_type @ConditionIsNonConstant [concrete] // CHECK:STDOUT: %ConditionIsNonConstant: %ConditionIsNonConstant.type = struct_value () [concrete] // CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [concrete] // CHECK:STDOUT: %Int.type: type = generic_class_type @Int [concrete] // CHECK:STDOUT: %Int.generic: %Int.type = struct_value () [concrete] // CHECK:STDOUT: %i32: type = class_type @Int, @Int(%int_32) [concrete] // CHECK:STDOUT: %int_1: Core.IntLiteral = int_value 1 [concrete] // CHECK:STDOUT: %Destroy.type: type = facet_type <@Destroy> [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: .Bool = %Core.Bool // CHECK:STDOUT: .Int = %Core.Int // CHECK:STDOUT: .Destroy = %Core.Destroy // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: %Core.Bool: %Bool.type = import_ref Core//prelude/parts/bool, Bool, loaded [concrete = constants.%Bool] // CHECK:STDOUT: %Core.Int: %Int.type = import_ref Core//prelude/parts/int, Int, loaded [concrete = constants.%Int.generic] // CHECK:STDOUT: %Core.Destroy: type = import_ref Core//prelude/parts/destroy, Destroy, loaded [concrete = constants.%Destroy.type] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .ConditionIsNonConstant = %ConditionIsNonConstant.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %ConditionIsNonConstant.decl: %ConditionIsNonConstant.type = fn_decl @ConditionIsNonConstant [concrete = constants.%ConditionIsNonConstant] { // CHECK:STDOUT: %b.patt: %pattern_type.831 = value_binding_pattern b [concrete] // CHECK:STDOUT: %b.param_patt: %pattern_type.831 = value_param_pattern %b.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %b.param: bool = value_param call_param0 // CHECK:STDOUT: %.loc4: type = type_literal bool [concrete = bool] // CHECK:STDOUT: %b: bool = value_binding b, %b.param // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @ConditionIsNonConstant(%b.param: bool) { // CHECK:STDOUT: !entry: // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %v.patt: = ref_binding_pattern v [concrete] // CHECK:STDOUT: %v.var_patt: = var_pattern %v.patt [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %v.var: ref = var %v.var_patt [concrete = ] // CHECK:STDOUT: %int_1: Core.IntLiteral = int_value 1 [concrete = constants.%int_1] // CHECK:STDOUT: assign %v.var, // CHECK:STDOUT: br !.loc12_20 // CHECK:STDOUT: // CHECK:STDOUT: !.loc12_20: // CHECK:STDOUT: %b.ref: bool = name_ref b, %b // CHECK:STDOUT: if %b.ref br !if.expr.then else br !if.expr.else // CHECK:STDOUT: // CHECK:STDOUT: !if.expr.then: // CHECK:STDOUT: %i32.loc12_27: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: br !if.expr.result(%i32.loc12_27) // CHECK:STDOUT: // CHECK:STDOUT: !if.expr.else: // CHECK:STDOUT: %i32.loc12_36: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: br !if.expr.result(%i32.loc12_36) // CHECK:STDOUT: // CHECK:STDOUT: !if.expr.result: // CHECK:STDOUT: %.loc12: type = block_arg !if.expr.result // CHECK:STDOUT: br !.loc12_14 // CHECK:STDOUT: // CHECK:STDOUT: !.loc12_14: // CHECK:STDOUT: %v: ref = ref_binding v, [concrete = ] // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- fail_non_constant_result.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %pattern_type.98f: type = pattern_type type [concrete] // CHECK:STDOUT: %ChosenBranchIsNonConstant.type: type = fn_type @ChosenBranchIsNonConstant [concrete] // CHECK:STDOUT: %ChosenBranchIsNonConstant: %ChosenBranchIsNonConstant.type = struct_value () [concrete] // CHECK:STDOUT: %true: bool = bool_literal true [concrete] // CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [concrete] // CHECK:STDOUT: %Int.type: type = generic_class_type @Int [concrete] // CHECK:STDOUT: %Int.generic: %Int.type = struct_value () [concrete] // CHECK:STDOUT: %i32: type = class_type @Int, @Int(%int_32) [concrete] // CHECK:STDOUT: %int_1: Core.IntLiteral = int_value 1 [concrete] // CHECK:STDOUT: %false: bool = bool_literal false [concrete] // CHECK:STDOUT: %Destroy.type: type = facet_type <@Destroy> [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: .Int = %Core.Int // CHECK:STDOUT: .Destroy = %Core.Destroy // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: %Core.Int: %Int.type = import_ref Core//prelude/parts/int, Int, loaded [concrete = constants.%Int.generic] // CHECK:STDOUT: %Core.Destroy: type = import_ref Core//prelude/parts/destroy, Destroy, loaded [concrete = constants.%Destroy.type] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .ChosenBranchIsNonConstant = %ChosenBranchIsNonConstant.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %ChosenBranchIsNonConstant.decl: %ChosenBranchIsNonConstant.type = fn_decl @ChosenBranchIsNonConstant [concrete = constants.%ChosenBranchIsNonConstant] { // CHECK:STDOUT: %t.patt: %pattern_type.98f = value_binding_pattern t [concrete] // CHECK:STDOUT: %t.param_patt: %pattern_type.98f = value_param_pattern %t.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %t.param: type = value_param call_param0 // CHECK:STDOUT: %.loc4: type = type_literal type [concrete = type] // CHECK:STDOUT: %t: type = value_binding t, %t.param // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @ChosenBranchIsNonConstant(%t.param: type) { // CHECK:STDOUT: !entry: // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %v.patt: = ref_binding_pattern v [concrete] // CHECK:STDOUT: %v.var_patt: = var_pattern %v.patt [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %v.var: ref = var %v.var_patt [concrete = ] // CHECK:STDOUT: %int_1.loc9: Core.IntLiteral = int_value 1 [concrete = constants.%int_1] // CHECK:STDOUT: assign %v.var, // CHECK:STDOUT: br !.loc9_20 // CHECK:STDOUT: // CHECK:STDOUT: !.loc9_20: // CHECK:STDOUT: %true: bool = bool_literal true [concrete = constants.%true] // CHECK:STDOUT: if %true br !if.expr.then.loc9 else br !if.expr.else.loc9 // CHECK:STDOUT: // CHECK:STDOUT: !if.expr.then.loc9: // CHECK:STDOUT: %t.ref.loc9: type = name_ref t, %t // CHECK:STDOUT: br !if.expr.result.loc9(%t.ref.loc9) // CHECK:STDOUT: // CHECK:STDOUT: !if.expr.else.loc9: // CHECK:STDOUT: %i32.loc9: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: br !if.expr.result.loc9(%i32.loc9) // CHECK:STDOUT: // CHECK:STDOUT: !if.expr.result.loc9: // CHECK:STDOUT: %.loc9: type = block_arg !if.expr.result.loc9 // CHECK:STDOUT: br !.loc9_14 // CHECK:STDOUT: // CHECK:STDOUT: !.loc9_14: // CHECK:STDOUT: %v: ref = ref_binding v, [concrete = ] // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %w.patt: = ref_binding_pattern w [concrete] // CHECK:STDOUT: %w.var_patt: = var_pattern %w.patt [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %w.var: ref = var %w.var_patt [concrete = ] // CHECK:STDOUT: %int_1.loc14: Core.IntLiteral = int_value 1 [concrete = constants.%int_1] // CHECK:STDOUT: assign %w.var, // CHECK:STDOUT: br !.loc14_20 // CHECK:STDOUT: // CHECK:STDOUT: !.loc14_20: // CHECK:STDOUT: %false: bool = bool_literal false [concrete = constants.%false] // CHECK:STDOUT: if %false br !if.expr.then.loc14 else br !if.expr.else.loc14 // CHECK:STDOUT: // CHECK:STDOUT: !if.expr.then.loc14: // CHECK:STDOUT: %i32.loc14: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: br !if.expr.result.loc14(%i32.loc14) // CHECK:STDOUT: // CHECK:STDOUT: !if.expr.else.loc14: // CHECK:STDOUT: %t.ref.loc14: type = name_ref t, %t // CHECK:STDOUT: br !if.expr.result.loc14(%t.ref.loc14) // CHECK:STDOUT: // CHECK:STDOUT: !if.expr.result.loc14: // CHECK:STDOUT: %.loc14: type = block_arg !if.expr.result.loc14 // CHECK:STDOUT: br !.loc14_14 // CHECK:STDOUT: // CHECK:STDOUT: !.loc14_14: // CHECK:STDOUT: %w: ref = ref_binding w, [concrete = ] // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/if_expr/nested.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/primitives.carbon // TODO: Add ranges and switch to "--dump-sem-ir-ranges=only". // EXTRA-ARGS: --dump-sem-ir-ranges=if-present // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/if_expr/nested.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/if_expr/nested.carbon fn F(a: bool, b: bool, c: bool) -> i32 { return if a then if b then 1 else 2 else if c then 3 else 4; } // CHECK:STDOUT: --- nested.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %Bool.type: type = fn_type @Bool [concrete] // CHECK:STDOUT: %Bool: %Bool.type = struct_value () [concrete] // CHECK:STDOUT: %pattern_type.831: type = pattern_type bool [concrete] // CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [concrete] // CHECK:STDOUT: %Int.type: type = generic_class_type @Int [concrete] // CHECK:STDOUT: %Int.generic: %Int.type = struct_value () [concrete] // CHECK:STDOUT: %N: Core.IntLiteral = symbolic_binding N, 0 [symbolic] // CHECK:STDOUT: %i32: type = class_type @Int, @Int(%int_32) [concrete] // CHECK:STDOUT: %.ff5: Core.Form = init_form %i32 [concrete] // CHECK:STDOUT: %pattern_type.7ce: type = pattern_type %i32 [concrete] // CHECK:STDOUT: %F.type: type = fn_type @F [concrete] // CHECK:STDOUT: %F: %F.type = struct_value () [concrete] // CHECK:STDOUT: %int_1.5b8: Core.IntLiteral = int_value 1 [concrete] // CHECK:STDOUT: %ImplicitAs.type.cc7: type = generic_interface_type @ImplicitAs [concrete] // CHECK:STDOUT: %ImplicitAs.generic: %ImplicitAs.type.cc7 = struct_value () [concrete] // CHECK:STDOUT: %ImplicitAs.type.e8c: type = facet_type <@ImplicitAs, @ImplicitAs(%i32)> [concrete] // CHECK:STDOUT: %To: Core.IntLiteral = symbolic_binding To, 0 [symbolic] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.4e6: type = fn_type @Core.IntLiteral.as.ImplicitAs.impl.Convert, @Core.IntLiteral.as.ImplicitAs.impl(%To) [symbolic] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.3c2: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.4e6 = struct_value () [symbolic] // CHECK:STDOUT: %ImplicitAs.impl_witness.6bc: = impl_witness imports.%ImplicitAs.impl_witness_table.74f, @Core.IntLiteral.as.ImplicitAs.impl(%int_32) [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.e0d: type = fn_type @Core.IntLiteral.as.ImplicitAs.impl.Convert, @Core.IntLiteral.as.ImplicitAs.impl(%int_32) [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.e0d = struct_value () [concrete] // CHECK:STDOUT: %ImplicitAs.facet: %ImplicitAs.type.e8c = facet_value Core.IntLiteral, (%ImplicitAs.impl_witness.6bc) [concrete] // CHECK:STDOUT: %ImplicitAs.WithSelf.Convert.type.b37: type = fn_type @ImplicitAs.WithSelf.Convert, @ImplicitAs.WithSelf(%i32, %ImplicitAs.facet) [concrete] // CHECK:STDOUT: %.545: type = fn_type_with_self_type %ImplicitAs.WithSelf.Convert.type.b37, %ImplicitAs.facet [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.bound.215: = bound_method %int_1.5b8, %Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5 [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn: = specific_function %Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5, @Core.IntLiteral.as.ImplicitAs.impl.Convert(%int_32) [concrete] // CHECK:STDOUT: %bound_method.38b: = bound_method %int_1.5b8, %Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn [concrete] // CHECK:STDOUT: %int_1.5d2: %i32 = int_value 1 [concrete] // CHECK:STDOUT: %int_2.ecc: Core.IntLiteral = int_value 2 [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.bound.4e5: = bound_method %int_2.ecc, %Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5 [concrete] // CHECK:STDOUT: %bound_method.646: = bound_method %int_2.ecc, %Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn [concrete] // CHECK:STDOUT: %int_2.ef8: %i32 = int_value 2 [concrete] // CHECK:STDOUT: %int_3.1ba: Core.IntLiteral = int_value 3 [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.bound.061: = bound_method %int_3.1ba, %Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5 [concrete] // CHECK:STDOUT: %bound_method.fa7: = bound_method %int_3.1ba, %Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn [concrete] // CHECK:STDOUT: %int_3.822: %i32 = int_value 3 [concrete] // CHECK:STDOUT: %int_4.0c1: Core.IntLiteral = int_value 4 [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.bound.f0c: = bound_method %int_4.0c1, %Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5 [concrete] // CHECK:STDOUT: %bound_method.6d7: = bound_method %int_4.0c1, %Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn [concrete] // CHECK:STDOUT: %int_4.940: %i32 = int_value 4 [concrete] // CHECK:STDOUT: %Copy.type: type = facet_type <@Copy> [concrete] // CHECK:STDOUT: %Int.as.Copy.impl.Op.type.824: type = fn_type @Int.as.Copy.impl.Op, @Int.as.Copy.impl(%N) [symbolic] // CHECK:STDOUT: %Int.as.Copy.impl.Op.9b9: %Int.as.Copy.impl.Op.type.824 = struct_value () [symbolic] // CHECK:STDOUT: %Copy.impl_witness.f17: = impl_witness imports.%Copy.impl_witness_table.e76, @Int.as.Copy.impl(%int_32) [concrete] // CHECK:STDOUT: %Int.as.Copy.impl.Op.type.546: type = fn_type @Int.as.Copy.impl.Op, @Int.as.Copy.impl(%int_32) [concrete] // CHECK:STDOUT: %Int.as.Copy.impl.Op.664: %Int.as.Copy.impl.Op.type.546 = struct_value () [concrete] // CHECK:STDOUT: %Copy.facet: %Copy.type = facet_value %i32, (%Copy.impl_witness.f17) [concrete] // CHECK:STDOUT: %Copy.WithSelf.Op.type.081: type = fn_type @Copy.WithSelf.Op, @Copy.WithSelf(%Copy.facet) [concrete] // CHECK:STDOUT: %.8e2: type = fn_type_with_self_type %Copy.WithSelf.Op.type.081, %Copy.facet [concrete] // CHECK:STDOUT: %Int.as.Copy.impl.Op.specific_fn: = specific_function %Int.as.Copy.impl.Op.664, @Int.as.Copy.impl.Op(%int_32) [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: .Bool = %Core.Bool // CHECK:STDOUT: .Int = %Core.Int // CHECK:STDOUT: .ImplicitAs = %Core.ImplicitAs // CHECK:STDOUT: .Copy = %Core.Copy // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: %Core.Bool: %Bool.type = import_ref Core//prelude/parts/bool, Bool, loaded [concrete = constants.%Bool] // CHECK:STDOUT: %Core.Int: %Int.type = import_ref Core//prelude/parts/int, Int, loaded [concrete = constants.%Int.generic] // CHECK:STDOUT: %Core.ImplicitAs: %ImplicitAs.type.cc7 = import_ref Core//prelude/parts/as, ImplicitAs, loaded [concrete = constants.%ImplicitAs.generic] // CHECK:STDOUT: %Core.import_ref.42d: @Core.IntLiteral.as.ImplicitAs.impl.%Core.IntLiteral.as.ImplicitAs.impl.Convert.type (%Core.IntLiteral.as.ImplicitAs.impl.Convert.type.4e6) = import_ref Core//prelude/parts/int, loc{{\d+_\d+}}, loaded [symbolic = @Core.IntLiteral.as.ImplicitAs.impl.%Core.IntLiteral.as.ImplicitAs.impl.Convert (constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.3c2)] // CHECK:STDOUT: %ImplicitAs.impl_witness_table.74f = impl_witness_table (%Core.import_ref.42d), @Core.IntLiteral.as.ImplicitAs.impl [concrete] // CHECK:STDOUT: %Core.Copy: type = import_ref Core//prelude/parts/copy, Copy, loaded [concrete = constants.%Copy.type] // CHECK:STDOUT: %Core.import_ref.18d: @Int.as.Copy.impl.%Int.as.Copy.impl.Op.type (%Int.as.Copy.impl.Op.type.824) = import_ref Core//prelude/parts/int, loc{{\d+_\d+}}, loaded [symbolic = @Int.as.Copy.impl.%Int.as.Copy.impl.Op (constants.%Int.as.Copy.impl.Op.9b9)] // CHECK:STDOUT: %Copy.impl_witness_table.e76 = impl_witness_table (%Core.import_ref.18d), @Int.as.Copy.impl [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .F = %F.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %F.decl: %F.type = fn_decl @F [concrete = constants.%F] { // CHECK:STDOUT: %a.patt: %pattern_type.831 = value_binding_pattern a [concrete] // CHECK:STDOUT: %a.param_patt: %pattern_type.831 = value_param_pattern %a.patt [concrete] // CHECK:STDOUT: %b.patt: %pattern_type.831 = value_binding_pattern b [concrete] // CHECK:STDOUT: %b.param_patt: %pattern_type.831 = value_param_pattern %b.patt [concrete] // CHECK:STDOUT: %c.patt: %pattern_type.831 = value_binding_pattern c [concrete] // CHECK:STDOUT: %c.param_patt: %pattern_type.831 = value_param_pattern %c.patt [concrete] // CHECK:STDOUT: %return.patt: %pattern_type.7ce = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: %pattern_type.7ce = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %i32: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %.loc15_36: Core.Form = init_form %i32 [concrete = constants.%.ff5] // CHECK:STDOUT: %a.param: bool = value_param call_param0 // CHECK:STDOUT: %.loc15_9: type = type_literal bool [concrete = bool] // CHECK:STDOUT: %a: bool = value_binding a, %a.param // CHECK:STDOUT: %b.param: bool = value_param call_param1 // CHECK:STDOUT: %.loc15_18: type = type_literal bool [concrete = bool] // CHECK:STDOUT: %b: bool = value_binding b, %b.param // CHECK:STDOUT: %c.param: bool = value_param call_param2 // CHECK:STDOUT: %.loc15_27: type = type_literal bool [concrete = bool] // CHECK:STDOUT: %c: bool = value_binding c, %c.param // CHECK:STDOUT: %return.param: ref %i32 = out_param call_param3 // CHECK:STDOUT: %return: ref %i32 = return_slot %return.param // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @F(%a.param: bool, %b.param: bool, %c.param: bool) -> out %return.param: %i32 { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %a.ref: bool = name_ref a, %a // CHECK:STDOUT: if %a.ref br !if.expr.then.loc16_10 else br !if.expr.else.loc16_10 // CHECK:STDOUT: // CHECK:STDOUT: !if.expr.then.loc16_10: // CHECK:STDOUT: %b.ref: bool = name_ref b, %b // CHECK:STDOUT: if %b.ref br !if.expr.then.loc16_20 else br !if.expr.else.loc16_20 // CHECK:STDOUT: // CHECK:STDOUT: !if.expr.then.loc16_20: // CHECK:STDOUT: %int_1: Core.IntLiteral = int_value 1 [concrete = constants.%int_1.5b8] // CHECK:STDOUT: %impl.elem0.loc16_25: %.545 = impl_witness_access constants.%ImplicitAs.impl_witness.6bc, element0 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5] // CHECK:STDOUT: %bound_method.loc16_25.1: = bound_method %int_1, %impl.elem0.loc16_25 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.bound.215] // CHECK:STDOUT: %specific_fn.loc16_25: = specific_function %impl.elem0.loc16_25, @Core.IntLiteral.as.ImplicitAs.impl.Convert(constants.%int_32) [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn] // CHECK:STDOUT: %bound_method.loc16_25.2: = bound_method %int_1, %specific_fn.loc16_25 [concrete = constants.%bound_method.38b] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc16_25: init %i32 = call %bound_method.loc16_25.2(%int_1) [concrete = constants.%int_1.5d2] // CHECK:STDOUT: %.loc16_25.1: %i32 = value_of_initializer %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc16_25 [concrete = constants.%int_1.5d2] // CHECK:STDOUT: %.loc16_25.2: %i32 = converted %int_1, %.loc16_25.1 [concrete = constants.%int_1.5d2] // CHECK:STDOUT: br !if.expr.result.loc16_20(%.loc16_25.2) // CHECK:STDOUT: // CHECK:STDOUT: !if.expr.else.loc16_20: // CHECK:STDOUT: %int_2: Core.IntLiteral = int_value 2 [concrete = constants.%int_2.ecc] // CHECK:STDOUT: %impl.elem0.loc16_32: %.545 = impl_witness_access constants.%ImplicitAs.impl_witness.6bc, element0 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5] // CHECK:STDOUT: %bound_method.loc16_32.1: = bound_method %int_2, %impl.elem0.loc16_32 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.bound.4e5] // CHECK:STDOUT: %specific_fn.loc16_32: = specific_function %impl.elem0.loc16_32, @Core.IntLiteral.as.ImplicitAs.impl.Convert(constants.%int_32) [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn] // CHECK:STDOUT: %bound_method.loc16_32.2: = bound_method %int_2, %specific_fn.loc16_32 [concrete = constants.%bound_method.646] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc16_32: init %i32 = call %bound_method.loc16_32.2(%int_2) [concrete = constants.%int_2.ef8] // CHECK:STDOUT: %.loc16_32.1: %i32 = value_of_initializer %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc16_32 [concrete = constants.%int_2.ef8] // CHECK:STDOUT: %.loc16_32.2: %i32 = converted %int_2, %.loc16_32.1 [concrete = constants.%int_2.ef8] // CHECK:STDOUT: br !if.expr.result.loc16_20(%.loc16_32.2) // CHECK:STDOUT: // CHECK:STDOUT: !if.expr.result.loc16_20: // CHECK:STDOUT: %.loc16_20: %i32 = block_arg !if.expr.result.loc16_20 // CHECK:STDOUT: br !if.expr.result.loc16_10(%.loc16_20) // CHECK:STDOUT: // CHECK:STDOUT: !if.expr.else.loc16_10: // CHECK:STDOUT: %c.ref: bool = name_ref c, %c // CHECK:STDOUT: if %c.ref br !if.expr.then.loc16_44 else br !if.expr.else.loc16_44 // CHECK:STDOUT: // CHECK:STDOUT: !if.expr.then.loc16_44: // CHECK:STDOUT: %int_3: Core.IntLiteral = int_value 3 [concrete = constants.%int_3.1ba] // CHECK:STDOUT: %impl.elem0.loc16_49: %.545 = impl_witness_access constants.%ImplicitAs.impl_witness.6bc, element0 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5] // CHECK:STDOUT: %bound_method.loc16_49.1: = bound_method %int_3, %impl.elem0.loc16_49 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.bound.061] // CHECK:STDOUT: %specific_fn.loc16_49: = specific_function %impl.elem0.loc16_49, @Core.IntLiteral.as.ImplicitAs.impl.Convert(constants.%int_32) [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn] // CHECK:STDOUT: %bound_method.loc16_49.2: = bound_method %int_3, %specific_fn.loc16_49 [concrete = constants.%bound_method.fa7] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc16_49: init %i32 = call %bound_method.loc16_49.2(%int_3) [concrete = constants.%int_3.822] // CHECK:STDOUT: %.loc16_49.1: %i32 = value_of_initializer %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc16_49 [concrete = constants.%int_3.822] // CHECK:STDOUT: %.loc16_49.2: %i32 = converted %int_3, %.loc16_49.1 [concrete = constants.%int_3.822] // CHECK:STDOUT: br !if.expr.result.loc16_44(%.loc16_49.2) // CHECK:STDOUT: // CHECK:STDOUT: !if.expr.else.loc16_44: // CHECK:STDOUT: %int_4: Core.IntLiteral = int_value 4 [concrete = constants.%int_4.0c1] // CHECK:STDOUT: %impl.elem0.loc16_56: %.545 = impl_witness_access constants.%ImplicitAs.impl_witness.6bc, element0 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5] // CHECK:STDOUT: %bound_method.loc16_56.1: = bound_method %int_4, %impl.elem0.loc16_56 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.bound.f0c] // CHECK:STDOUT: %specific_fn.loc16_56: = specific_function %impl.elem0.loc16_56, @Core.IntLiteral.as.ImplicitAs.impl.Convert(constants.%int_32) [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn] // CHECK:STDOUT: %bound_method.loc16_56.2: = bound_method %int_4, %specific_fn.loc16_56 [concrete = constants.%bound_method.6d7] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc16_56: init %i32 = call %bound_method.loc16_56.2(%int_4) [concrete = constants.%int_4.940] // CHECK:STDOUT: %.loc16_56.1: %i32 = value_of_initializer %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc16_56 [concrete = constants.%int_4.940] // CHECK:STDOUT: %.loc16_56.2: %i32 = converted %int_4, %.loc16_56.1 [concrete = constants.%int_4.940] // CHECK:STDOUT: br !if.expr.result.loc16_44(%.loc16_56.2) // CHECK:STDOUT: // CHECK:STDOUT: !if.expr.result.loc16_44: // CHECK:STDOUT: %.loc16_44: %i32 = block_arg !if.expr.result.loc16_44 // CHECK:STDOUT: br !if.expr.result.loc16_10(%.loc16_44) // CHECK:STDOUT: // CHECK:STDOUT: !if.expr.result.loc16_10: // CHECK:STDOUT: %.loc16_10: %i32 = block_arg !if.expr.result.loc16_10 // CHECK:STDOUT: %impl.elem0.loc16_10: %.8e2 = impl_witness_access constants.%Copy.impl_witness.f17, element0 [concrete = constants.%Int.as.Copy.impl.Op.664] // CHECK:STDOUT: %bound_method.loc16_10.1: = bound_method %.loc16_10, %impl.elem0.loc16_10 // CHECK:STDOUT: %specific_fn.loc16_10: = specific_function %impl.elem0.loc16_10, @Int.as.Copy.impl.Op(constants.%int_32) [concrete = constants.%Int.as.Copy.impl.Op.specific_fn] // CHECK:STDOUT: %bound_method.loc16_10.2: = bound_method %.loc16_10, %specific_fn.loc16_10 // CHECK:STDOUT: %Int.as.Copy.impl.Op.call: init %i32 = call %bound_method.loc16_10.2(%.loc16_10) // CHECK:STDOUT: return %Int.as.Copy.impl.Op.call // CHECK:STDOUT: } // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/if_expr/struct.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/primitives.carbon // TODO: Add ranges and switch to "--dump-sem-ir-ranges=only". // EXTRA-ARGS: --dump-sem-ir-ranges=if-present // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/if_expr/struct.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/if_expr/struct.carbon fn G(s: {.a: i32, .b: i32}); fn F(cond: bool) { var a: {.a: i32, .b: i32} = {.a = 1, .b = 2}; G(if cond then a else a); } // CHECK:STDOUT: --- struct.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [concrete] // CHECK:STDOUT: %Int.type: type = generic_class_type @Int [concrete] // CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] // CHECK:STDOUT: %Int.generic: %Int.type = struct_value () [concrete] // CHECK:STDOUT: %i32: type = class_type @Int, @Int(%int_32) [concrete] // CHECK:STDOUT: %struct_type.a.b.501: type = struct_type {.a: %i32, .b: %i32} [concrete] // CHECK:STDOUT: %pattern_type.851: type = pattern_type %struct_type.a.b.501 [concrete] // CHECK:STDOUT: %G.type: type = fn_type @G [concrete] // CHECK:STDOUT: %G: %G.type = struct_value () [concrete] // CHECK:STDOUT: %Bool.type: type = fn_type @Bool [concrete] // CHECK:STDOUT: %Bool: %Bool.type = struct_value () [concrete] // CHECK:STDOUT: %pattern_type.831: type = pattern_type bool [concrete] // CHECK:STDOUT: %F.type: type = fn_type @F [concrete] // CHECK:STDOUT: %F: %F.type = struct_value () [concrete] // CHECK:STDOUT: %int_1.5b8: Core.IntLiteral = int_value 1 [concrete] // CHECK:STDOUT: %int_2.ecc: Core.IntLiteral = int_value 2 [concrete] // CHECK:STDOUT: %struct_type.a.b.cfd: type = struct_type {.a: Core.IntLiteral, .b: Core.IntLiteral} [concrete] // CHECK:STDOUT: %struct.4aa: %struct_type.a.b.cfd = struct_value (%int_1.5b8, %int_2.ecc) [concrete] // CHECK:STDOUT: %ImplicitAs.type.cc7: type = generic_interface_type @ImplicitAs [concrete] // CHECK:STDOUT: %ImplicitAs.generic: %ImplicitAs.type.cc7 = struct_value () [concrete] // CHECK:STDOUT: %ImplicitAs.type.e8c: type = facet_type <@ImplicitAs, @ImplicitAs(%i32)> [concrete] // CHECK:STDOUT: %To: Core.IntLiteral = symbolic_binding To, 0 [symbolic] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.4e6: type = fn_type @Core.IntLiteral.as.ImplicitAs.impl.Convert, @Core.IntLiteral.as.ImplicitAs.impl(%To) [symbolic] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.3c2: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.4e6 = struct_value () [symbolic] // CHECK:STDOUT: %ImplicitAs.impl_witness.6bc: = impl_witness imports.%ImplicitAs.impl_witness_table.74f, @Core.IntLiteral.as.ImplicitAs.impl(%int_32) [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.e0d: type = fn_type @Core.IntLiteral.as.ImplicitAs.impl.Convert, @Core.IntLiteral.as.ImplicitAs.impl(%int_32) [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.e0d = struct_value () [concrete] // CHECK:STDOUT: %ImplicitAs.facet: %ImplicitAs.type.e8c = facet_value Core.IntLiteral, (%ImplicitAs.impl_witness.6bc) [concrete] // CHECK:STDOUT: %ImplicitAs.WithSelf.Convert.type.b37: type = fn_type @ImplicitAs.WithSelf.Convert, @ImplicitAs.WithSelf(%i32, %ImplicitAs.facet) [concrete] // CHECK:STDOUT: %.545: type = fn_type_with_self_type %ImplicitAs.WithSelf.Convert.type.b37, %ImplicitAs.facet [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.bound.215: = bound_method %int_1.5b8, %Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5 [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn: = specific_function %Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5, @Core.IntLiteral.as.ImplicitAs.impl.Convert(%int_32) [concrete] // CHECK:STDOUT: %bound_method.38b: = bound_method %int_1.5b8, %Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn [concrete] // CHECK:STDOUT: %int_1.5d2: %i32 = int_value 1 [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.bound.4e5: = bound_method %int_2.ecc, %Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5 [concrete] // CHECK:STDOUT: %bound_method.646: = bound_method %int_2.ecc, %Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn [concrete] // CHECK:STDOUT: %int_2.ef8: %i32 = int_value 2 [concrete] // CHECK:STDOUT: %struct.ed5: %struct_type.a.b.501 = struct_value (%int_1.5d2, %int_2.ef8) [concrete] // CHECK:STDOUT: %Destroy.type: type = facet_type <@Destroy> [concrete] // CHECK:STDOUT: %Destroy.Op.type: type = fn_type @Destroy.Op [concrete] // CHECK:STDOUT: %Destroy.Op: %Destroy.Op.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: .Int = %Core.Int // CHECK:STDOUT: .Bool = %Core.Bool // CHECK:STDOUT: .ImplicitAs = %Core.ImplicitAs // CHECK:STDOUT: .Destroy = %Core.Destroy // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: %Core.Int: %Int.type = import_ref Core//prelude/parts/int, Int, loaded [concrete = constants.%Int.generic] // CHECK:STDOUT: %Core.Bool: %Bool.type = import_ref Core//prelude/parts/bool, Bool, loaded [concrete = constants.%Bool] // CHECK:STDOUT: %Core.ImplicitAs: %ImplicitAs.type.cc7 = import_ref Core//prelude/parts/as, ImplicitAs, loaded [concrete = constants.%ImplicitAs.generic] // CHECK:STDOUT: %Core.import_ref.42d: @Core.IntLiteral.as.ImplicitAs.impl.%Core.IntLiteral.as.ImplicitAs.impl.Convert.type (%Core.IntLiteral.as.ImplicitAs.impl.Convert.type.4e6) = import_ref Core//prelude/parts/int, loc{{\d+_\d+}}, loaded [symbolic = @Core.IntLiteral.as.ImplicitAs.impl.%Core.IntLiteral.as.ImplicitAs.impl.Convert (constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.3c2)] // CHECK:STDOUT: %ImplicitAs.impl_witness_table.74f = impl_witness_table (%Core.import_ref.42d), @Core.IntLiteral.as.ImplicitAs.impl [concrete] // CHECK:STDOUT: %Core.Destroy: type = import_ref Core//prelude/parts/destroy, Destroy, loaded [concrete = constants.%Destroy.type] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .G = %G.decl // CHECK:STDOUT: .F = %F.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %G.decl: %G.type = fn_decl @G [concrete = constants.%G] { // CHECK:STDOUT: %s.patt: %pattern_type.851 = value_binding_pattern s [concrete] // CHECK:STDOUT: %s.param_patt: %pattern_type.851 = value_param_pattern %s.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %s.param: %struct_type.a.b.501 = value_param call_param0 // CHECK:STDOUT: %.loc15: type = splice_block %struct_type.a.b [concrete = constants.%struct_type.a.b.501] { // CHECK:STDOUT: %i32.loc15_14: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %i32.loc15_23: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %struct_type.a.b: type = struct_type {.a: %i32, .b: %i32} [concrete = constants.%struct_type.a.b.501] // CHECK:STDOUT: } // CHECK:STDOUT: %s: %struct_type.a.b.501 = value_binding s, %s.param // CHECK:STDOUT: } // CHECK:STDOUT: %F.decl: %F.type = fn_decl @F [concrete = constants.%F] { // CHECK:STDOUT: %cond.patt: %pattern_type.831 = value_binding_pattern cond [concrete] // CHECK:STDOUT: %cond.param_patt: %pattern_type.831 = value_param_pattern %cond.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %cond.param: bool = value_param call_param0 // CHECK:STDOUT: %.loc17: type = type_literal bool [concrete = bool] // CHECK:STDOUT: %cond: bool = value_binding cond, %cond.param // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @G(%s.param: %struct_type.a.b.501); // CHECK:STDOUT: // CHECK:STDOUT: fn @F(%cond.param: bool) { // CHECK:STDOUT: !entry: // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %a.patt: %pattern_type.851 = ref_binding_pattern a [concrete] // CHECK:STDOUT: %a.var_patt: %pattern_type.851 = var_pattern %a.patt [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %a.var: ref %struct_type.a.b.501 = var %a.var_patt // CHECK:STDOUT: %int_1: Core.IntLiteral = int_value 1 [concrete = constants.%int_1.5b8] // CHECK:STDOUT: %int_2: Core.IntLiteral = int_value 2 [concrete = constants.%int_2.ecc] // CHECK:STDOUT: %.loc18_46.1: %struct_type.a.b.cfd = struct_literal (%int_1, %int_2) [concrete = constants.%struct.4aa] // CHECK:STDOUT: %impl.elem0.loc18_46.1: %.545 = impl_witness_access constants.%ImplicitAs.impl_witness.6bc, element0 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5] // CHECK:STDOUT: %bound_method.loc18_46.1: = bound_method %int_1, %impl.elem0.loc18_46.1 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.bound.215] // CHECK:STDOUT: %specific_fn.loc18_46.1: = specific_function %impl.elem0.loc18_46.1, @Core.IntLiteral.as.ImplicitAs.impl.Convert(constants.%int_32) [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn] // CHECK:STDOUT: %bound_method.loc18_46.2: = bound_method %int_1, %specific_fn.loc18_46.1 [concrete = constants.%bound_method.38b] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc18_46.1: init %i32 = call %bound_method.loc18_46.2(%int_1) [concrete = constants.%int_1.5d2] // CHECK:STDOUT: %.loc18_46.2: init %i32 = converted %int_1, %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc18_46.1 [concrete = constants.%int_1.5d2] // CHECK:STDOUT: %.loc18_46.3: ref %i32 = struct_access %a.var, element0 // CHECK:STDOUT: %.loc18_46.4: init %i32 to %.loc18_46.3 = in_place_init %.loc18_46.2 [concrete = constants.%int_1.5d2] // CHECK:STDOUT: %impl.elem0.loc18_46.2: %.545 = impl_witness_access constants.%ImplicitAs.impl_witness.6bc, element0 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5] // CHECK:STDOUT: %bound_method.loc18_46.3: = bound_method %int_2, %impl.elem0.loc18_46.2 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.bound.4e5] // CHECK:STDOUT: %specific_fn.loc18_46.2: = specific_function %impl.elem0.loc18_46.2, @Core.IntLiteral.as.ImplicitAs.impl.Convert(constants.%int_32) [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn] // CHECK:STDOUT: %bound_method.loc18_46.4: = bound_method %int_2, %specific_fn.loc18_46.2 [concrete = constants.%bound_method.646] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc18_46.2: init %i32 = call %bound_method.loc18_46.4(%int_2) [concrete = constants.%int_2.ef8] // CHECK:STDOUT: %.loc18_46.5: init %i32 = converted %int_2, %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc18_46.2 [concrete = constants.%int_2.ef8] // CHECK:STDOUT: %.loc18_46.6: ref %i32 = struct_access %a.var, element1 // CHECK:STDOUT: %.loc18_46.7: init %i32 to %.loc18_46.6 = in_place_init %.loc18_46.5 [concrete = constants.%int_2.ef8] // CHECK:STDOUT: %.loc18_46.8: init %struct_type.a.b.501 to %a.var = struct_init (%.loc18_46.4, %.loc18_46.7) [concrete = constants.%struct.ed5] // CHECK:STDOUT: %.loc18_3: init %struct_type.a.b.501 = converted %.loc18_46.1, %.loc18_46.8 [concrete = constants.%struct.ed5] // CHECK:STDOUT: assign %a.var, %.loc18_3 // CHECK:STDOUT: %.loc18_27: type = splice_block %struct_type.a.b [concrete = constants.%struct_type.a.b.501] { // CHECK:STDOUT: %i32.loc18_15: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %i32.loc18_24: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %struct_type.a.b: type = struct_type {.a: %i32, .b: %i32} [concrete = constants.%struct_type.a.b.501] // CHECK:STDOUT: } // CHECK:STDOUT: %a: ref %struct_type.a.b.501 = ref_binding a, %a.var // CHECK:STDOUT: %G.ref: %G.type = name_ref G, file.%G.decl [concrete = constants.%G] // CHECK:STDOUT: %cond.ref: bool = name_ref cond, %cond // CHECK:STDOUT: if %cond.ref br !if.expr.then else br !if.expr.else // CHECK:STDOUT: // CHECK:STDOUT: !if.expr.then: // CHECK:STDOUT: %a.ref.loc19_18: ref %struct_type.a.b.501 = name_ref a, %a // CHECK:STDOUT: %.loc19_18.1: ref %i32 = struct_access %a.ref.loc19_18, element0 // CHECK:STDOUT: %.loc19_18.2: %i32 = acquire_value %.loc19_18.1 // CHECK:STDOUT: %.loc19_18.3: ref %i32 = struct_access %a.ref.loc19_18, element1 // CHECK:STDOUT: %.loc19_18.4: %i32 = acquire_value %.loc19_18.3 // CHECK:STDOUT: %struct.loc19_18: %struct_type.a.b.501 = struct_value (%.loc19_18.2, %.loc19_18.4) // CHECK:STDOUT: %.loc19_18.5: %struct_type.a.b.501 = converted %a.ref.loc19_18, %struct.loc19_18 // CHECK:STDOUT: br !if.expr.result(%.loc19_18.5) // CHECK:STDOUT: // CHECK:STDOUT: !if.expr.else: // CHECK:STDOUT: %a.ref.loc19_25: ref %struct_type.a.b.501 = name_ref a, %a // CHECK:STDOUT: %.loc19_25.1: ref %i32 = struct_access %a.ref.loc19_25, element0 // CHECK:STDOUT: %.loc19_25.2: %i32 = acquire_value %.loc19_25.1 // CHECK:STDOUT: %.loc19_25.3: ref %i32 = struct_access %a.ref.loc19_25, element1 // CHECK:STDOUT: %.loc19_25.4: %i32 = acquire_value %.loc19_25.3 // CHECK:STDOUT: %struct.loc19_25: %struct_type.a.b.501 = struct_value (%.loc19_25.2, %.loc19_25.4) // CHECK:STDOUT: %.loc19_20: %struct_type.a.b.501 = converted %a.ref.loc19_25, %struct.loc19_25 // CHECK:STDOUT: br !if.expr.result(%.loc19_20) // CHECK:STDOUT: // CHECK:STDOUT: !if.expr.result: // CHECK:STDOUT: %.loc19_5: %struct_type.a.b.501 = block_arg !if.expr.result // CHECK:STDOUT: %G.call: init %empty_tuple.type = call %G.ref(%.loc19_5) // CHECK:STDOUT: %Destroy.Op.bound: = bound_method %a.var, constants.%Destroy.Op // CHECK:STDOUT: %Destroy.Op.call: init %empty_tuple.type = call %Destroy.Op.bound(%a.var) // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @Destroy.Op(%self.param: ref %struct_type.a.b.501) = "no_op"; // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/impl/assoc_const_self.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/int.carbon // TODO: Add ranges and switch to "--dump-sem-ir-ranges=only". // EXTRA-ARGS: --dump-sem-ir-ranges=if-present // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/impl/assoc_const_self.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/impl/assoc_const_self.carbon // --- assoc_const_self.carbon library "[[@TEST_NAME]]"; interface I { let V:! Self; } impl {} as I where .V = {} {} impl i32 as I where .V = 0 {} // --- fail_assoc_const_mismatch.carbon library "[[@TEST_NAME]]"; interface I { let V:! Self; } // CHECK:STDERR: fail_assoc_const_mismatch.carbon:[[@LINE+7]]:12: error: cannot implicitly convert expression of type `Core.IntLiteral` to `{}` [ConversionFailure] // CHECK:STDERR: impl {} as I where .V = 0 {} // CHECK:STDERR: ^~~~~~~~~~~~~~ // CHECK:STDERR: fail_assoc_const_mismatch.carbon:[[@LINE+4]]:12: note: type `Core.IntLiteral` does not implement interface `Core.ImplicitAs({})` [MissingImplInMemberAccessInContext] // CHECK:STDERR: impl {} as I where .V = 0 {} // CHECK:STDERR: ^~~~~~~~~~~~~~ // CHECK:STDERR: impl {} as I where .V = 0 {} // --- fail_assoc_const_not_constant_after_conversion.carbon library "[[@TEST_NAME]]"; interface I { let V:! Self; } class C {} impl () as Core.ImplicitAs(C) { fn Convert[unused self: ()]() -> C { return {}; } } // CHECK:STDERR: fail_assoc_const_not_constant_after_conversion.carbon:[[@LINE+4]]:11: error: associated constant V given value `()` that is not constant after conversion to `C` [AssociatedConstantNotConstantAfterConversion] // CHECK:STDERR: impl C as I where .V = () {} // CHECK:STDERR: ^~~~~~~~~~~~~~~ // CHECK:STDERR: impl C as I where .V = () {} // --- fail_monomorphization_failure.carbon library "[[@TEST_NAME]]"; interface I(N:! Core.IntLiteral()) { let V:! array(Self, N); } // CHECK:STDERR: fail_monomorphization_failure.carbon:[[@LINE+7]]:24: error: member access into facet of incomplete type `I(-1)` [IncompleteTypeInMemberAccessOfFacet] // CHECK:STDERR: impl {} as I(-1) where .V = () {} // CHECK:STDERR: ^~ // CHECK:STDERR: fail_monomorphization_failure.carbon:[[@LINE-6]]:17: note: array bound of -1 is negative [ArrayBoundNegative] // CHECK:STDERR: let V:! array(Self, N); // CHECK:STDERR: ^~~~ // CHECK:STDERR: impl {} as I(-1) where .V = () {} // --- fail_todo_constrained_fn.carbon library "[[@TEST_NAME]]"; interface I { let V:! Self; } fn F(T:! I where {} impls Core.ImplicitAs(.Self) and .V = {}); fn CallF() { // TODO: This call should eventually work. // CHECK:STDERR: fail_todo_constrained_fn.carbon:[[@LINE+7]]:3: error: cannot convert type `{}` into type implementing `I where .(I.V) = {} and...` [ConversionFailureTypeToFacet] // CHECK:STDERR: F({}); // CHECK:STDERR: ^~~~~ // CHECK:STDERR: fail_todo_constrained_fn.carbon:[[@LINE-7]]:6: note: initializing generic parameter `T` declared here [InitializingGenericParam] // CHECK:STDERR: fn F(T:! I where {} impls Core.ImplicitAs(.Self) and .V = {}); // CHECK:STDERR: ^ // CHECK:STDERR: F({}); } // CHECK:STDOUT: --- assoc_const_self.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %I.type: type = facet_type <@I> [concrete] // CHECK:STDOUT: %Self.ab9: %I.type = symbolic_binding Self, 0 [symbolic] // CHECK:STDOUT: %Self.binding.as_type.d31: type = symbolic_binding_type Self, 0, %Self.ab9 [symbolic] // CHECK:STDOUT: %I.assoc_type: type = assoc_entity_type @I [concrete] // CHECK:STDOUT: %assoc0.490: %I.assoc_type = assoc_entity element0, @I.WithSelf.%V [concrete] // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %empty_struct: %empty_struct_type = struct_value () [concrete] // CHECK:STDOUT: %.Self: %I.type = symbolic_binding .Self [symbolic_self] // CHECK:STDOUT: %.Self.binding.as_type: type = symbolic_binding_type .Self, %.Self [symbolic_self] // CHECK:STDOUT: %I.lookup_impl_witness: = lookup_impl_witness %.Self, @I [symbolic_self] // CHECK:STDOUT: %impl.elem0: %.Self.binding.as_type = impl_witness_access %I.lookup_impl_witness, element0 [symbolic_self] // CHECK:STDOUT: %I_where.type.ec3: type = facet_type <@I where %impl.elem0 = %empty_struct> [concrete] // CHECK:STDOUT: %I.impl_witness.df7: = impl_witness @empty_struct_type.as.I.impl.%I.impl_witness_table [concrete] // CHECK:STDOUT: %type: type = facet_type [concrete] // CHECK:STDOUT: %empty_struct.type.facet: %type = facet_value %empty_struct_type, () [concrete] // CHECK:STDOUT: %I.facet.47f: %I.type = facet_value %empty_struct_type, (%I.impl_witness.df7) [concrete] // CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [concrete] // CHECK:STDOUT: %Int.type: type = generic_class_type @Int [concrete] // CHECK:STDOUT: %Int.generic: %Int.type = struct_value () [concrete] // CHECK:STDOUT: %i32: type = class_type @Int, @Int(%int_32) [concrete] // CHECK:STDOUT: %int_0.5c6: Core.IntLiteral = int_value 0 [concrete] // CHECK:STDOUT: %I_where.type.b52: type = facet_type <@I where %impl.elem0 = %int_0.5c6> [concrete] // CHECK:STDOUT: %I.impl_witness.550: = impl_witness @i32.as.I.impl.%I.impl_witness_table [concrete] // CHECK:STDOUT: %Int.type.facet: %type = facet_value %i32, () [concrete] // CHECK:STDOUT: %ImplicitAs.type.cc7: type = generic_interface_type @ImplicitAs [concrete] // CHECK:STDOUT: %ImplicitAs.generic: %ImplicitAs.type.cc7 = struct_value () [concrete] // CHECK:STDOUT: %ImplicitAs.type.e8c: type = facet_type <@ImplicitAs, @ImplicitAs(%i32)> [concrete] // CHECK:STDOUT: %To: Core.IntLiteral = symbolic_binding To, 0 [symbolic] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.4e6: type = fn_type @Core.IntLiteral.as.ImplicitAs.impl.Convert, @Core.IntLiteral.as.ImplicitAs.impl(%To) [symbolic] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.3c2: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.4e6 = struct_value () [symbolic] // CHECK:STDOUT: %ImplicitAs.impl_witness.6bc: = impl_witness imports.%ImplicitAs.impl_witness_table.74f, @Core.IntLiteral.as.ImplicitAs.impl(%int_32) [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.e0d: type = fn_type @Core.IntLiteral.as.ImplicitAs.impl.Convert, @Core.IntLiteral.as.ImplicitAs.impl(%int_32) [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.e0d = struct_value () [concrete] // CHECK:STDOUT: %ImplicitAs.facet: %ImplicitAs.type.e8c = facet_value Core.IntLiteral, (%ImplicitAs.impl_witness.6bc) [concrete] // CHECK:STDOUT: %ImplicitAs.WithSelf.Convert.type.b37: type = fn_type @ImplicitAs.WithSelf.Convert, @ImplicitAs.WithSelf(%i32, %ImplicitAs.facet) [concrete] // CHECK:STDOUT: %.545: type = fn_type_with_self_type %ImplicitAs.WithSelf.Convert.type.b37, %ImplicitAs.facet [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.bound: = bound_method %int_0.5c6, %Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5 [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn: = specific_function %Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5, @Core.IntLiteral.as.ImplicitAs.impl.Convert(%int_32) [concrete] // CHECK:STDOUT: %bound_method: = bound_method %int_0.5c6, %Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn [concrete] // CHECK:STDOUT: %int_0.6a9: %i32 = int_value 0 [concrete] // CHECK:STDOUT: %I.facet.608: %I.type = facet_value %i32, (%I.impl_witness.550) [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: .Int = %Core.Int // CHECK:STDOUT: .ImplicitAs = %Core.ImplicitAs // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: %Core.Int: %Int.type = import_ref Core//prelude/parts/int, Int, loaded [concrete = constants.%Int.generic] // CHECK:STDOUT: %Core.ImplicitAs: %ImplicitAs.type.cc7 = import_ref Core//prelude/parts/as, ImplicitAs, loaded [concrete = constants.%ImplicitAs.generic] // CHECK:STDOUT: %Core.import_ref.42d: @Core.IntLiteral.as.ImplicitAs.impl.%Core.IntLiteral.as.ImplicitAs.impl.Convert.type (%Core.IntLiteral.as.ImplicitAs.impl.Convert.type.4e6) = import_ref Core//prelude/parts/int, loc{{\d+_\d+}}, loaded [symbolic = @Core.IntLiteral.as.ImplicitAs.impl.%Core.IntLiteral.as.ImplicitAs.impl.Convert (constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.3c2)] // CHECK:STDOUT: %ImplicitAs.impl_witness_table.74f = impl_witness_table (%Core.import_ref.42d), @Core.IntLiteral.as.ImplicitAs.impl [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .I = %I.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %I.decl: type = interface_decl @I [concrete = constants.%I.type] {} {} // CHECK:STDOUT: impl_decl @empty_struct_type.as.I.impl [concrete] {} { // CHECK:STDOUT: %.loc8_7.1: %empty_struct_type = struct_literal () [concrete = constants.%empty_struct] // CHECK:STDOUT: %.loc8_7.2: type = converted %.loc8_7.1, constants.%empty_struct_type [concrete = constants.%empty_struct_type] // CHECK:STDOUT: %I.ref: type = name_ref I, file.%I.decl [concrete = constants.%I.type] // CHECK:STDOUT: %.Self: %I.type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %.Self.ref: %I.type = name_ref .Self, %.Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %.Self.as_type: type = facet_access_type %.Self.ref [symbolic_self = constants.%.Self.binding.as_type] // CHECK:STDOUT: %.loc8_20: type = converted %.Self.ref, %.Self.as_type [symbolic_self = constants.%.Self.binding.as_type] // CHECK:STDOUT: %V.ref: %I.assoc_type = name_ref V, @V.%assoc0 [concrete = constants.%assoc0.490] // CHECK:STDOUT: %impl.elem0: %.Self.binding.as_type = impl_witness_access constants.%I.lookup_impl_witness, element0 [symbolic_self = constants.%impl.elem0] // CHECK:STDOUT: %.loc8_26.1: %empty_struct_type = struct_literal () [concrete = constants.%empty_struct] // CHECK:STDOUT: %empty_struct: %empty_struct_type = struct_value () [concrete = constants.%empty_struct] // CHECK:STDOUT: %.loc8_26.2: %empty_struct_type = converted %.loc8_26.1, %empty_struct [concrete = constants.%empty_struct] // CHECK:STDOUT: %.loc8_14: type = where_expr %.Self [concrete = constants.%I_where.type.ec3] { // CHECK:STDOUT: requirement_base_facet_type constants.%I.type // CHECK:STDOUT: requirement_rewrite %impl.elem0, %.loc8_26.2 // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: impl_decl @i32.as.I.impl [concrete] {} { // CHECK:STDOUT: %i32: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %I.ref: type = name_ref I, file.%I.decl [concrete = constants.%I.type] // CHECK:STDOUT: %.Self: %I.type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %.Self.ref: %I.type = name_ref .Self, %.Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %.Self.as_type: type = facet_access_type %.Self.ref [symbolic_self = constants.%.Self.binding.as_type] // CHECK:STDOUT: %.loc10_21: type = converted %.Self.ref, %.Self.as_type [symbolic_self = constants.%.Self.binding.as_type] // CHECK:STDOUT: %V.ref: %I.assoc_type = name_ref V, @V.%assoc0 [concrete = constants.%assoc0.490] // CHECK:STDOUT: %impl.elem0.loc10_21: %.Self.binding.as_type = impl_witness_access constants.%I.lookup_impl_witness, element0 [symbolic_self = constants.%impl.elem0] // CHECK:STDOUT: %int_0: Core.IntLiteral = int_value 0 [concrete = constants.%int_0.5c6] // CHECK:STDOUT: %.loc10_15.1: type = where_expr %.Self [concrete = constants.%I_where.type.b52] { // CHECK:STDOUT: requirement_base_facet_type constants.%I.type // CHECK:STDOUT: requirement_rewrite %impl.elem0.loc10_21, %int_0 // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: interface @I { // CHECK:STDOUT: %Self: %I.type = symbolic_binding Self, 0 [symbolic = constants.%Self.ab9] // CHECK:STDOUT: %I.WithSelf.decl = interface_with_self_decl @I [concrete] // CHECK:STDOUT: // CHECK:STDOUT: !with Self: // CHECK:STDOUT: %V: @I.WithSelf.%Self.binding.as_type (%Self.binding.as_type.d31) = assoc_const_decl @V [concrete] { // CHECK:STDOUT: %assoc0: %I.assoc_type = assoc_entity element0, @I.WithSelf.%V [concrete = constants.%assoc0.490] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = %Self // CHECK:STDOUT: .V = @V.%assoc0 // CHECK:STDOUT: witness = (@I.WithSelf.%V) // CHECK:STDOUT: // CHECK:STDOUT: !requires: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: impl @empty_struct_type.as.I.impl: %.loc8_7.2 as %.loc8_14 { // CHECK:STDOUT: %I.impl_witness_table = impl_witness_table (%impl_witness_assoc_constant), @empty_struct_type.as.I.impl [concrete] // CHECK:STDOUT: %I.impl_witness: = impl_witness %I.impl_witness_table [concrete = constants.%I.impl_witness.df7] // CHECK:STDOUT: %impl_witness_assoc_constant: %empty_struct_type = impl_witness_assoc_constant constants.%empty_struct [concrete = constants.%empty_struct] // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: witness = %I.impl_witness // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: impl @i32.as.I.impl: %i32 as %.loc10_15.1 { // CHECK:STDOUT: %I.impl_witness_table = impl_witness_table (%impl_witness_assoc_constant), @i32.as.I.impl [concrete] // CHECK:STDOUT: %I.impl_witness: = impl_witness %I.impl_witness_table [concrete = constants.%I.impl_witness.550] // CHECK:STDOUT: %impl.elem0.loc10_15: %.545 = impl_witness_access constants.%ImplicitAs.impl_witness.6bc, element0 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5] // CHECK:STDOUT: %bound_method.loc10_15.1: = bound_method constants.%int_0.5c6, %impl.elem0.loc10_15 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.bound] // CHECK:STDOUT: %specific_fn: = specific_function %impl.elem0.loc10_15, @Core.IntLiteral.as.ImplicitAs.impl.Convert(constants.%int_32) [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn] // CHECK:STDOUT: %bound_method.loc10_15.2: = bound_method constants.%int_0.5c6, %specific_fn [concrete = constants.%bound_method] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.call: init %i32 = call %bound_method.loc10_15.2(constants.%int_0.5c6) [concrete = constants.%int_0.6a9] // CHECK:STDOUT: %.loc10_15.2: %i32 = value_of_initializer %Core.IntLiteral.as.ImplicitAs.impl.Convert.call [concrete = constants.%int_0.6a9] // CHECK:STDOUT: %.loc10_15.3: %i32 = converted constants.%int_0.5c6, %.loc10_15.2 [concrete = constants.%int_0.6a9] // CHECK:STDOUT: %impl_witness_assoc_constant: %i32 = impl_witness_assoc_constant constants.%int_0.6a9 [concrete = constants.%int_0.6a9] // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: witness = %I.impl_witness // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @I.WithSelf(constants.%Self.ab9) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: %Self => constants.%Self.ab9 // CHECK:STDOUT: %Self.binding.as_type => constants.%Self.binding.as_type.d31 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @I.WithSelf(constants.%.Self) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: %Self => constants.%.Self // CHECK:STDOUT: %Self.binding.as_type => constants.%.Self.binding.as_type // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @I.WithSelf(constants.%empty_struct.type.facet) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: %Self => constants.%empty_struct.type.facet // CHECK:STDOUT: %Self.binding.as_type => constants.%empty_struct_type // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @I.WithSelf(constants.%I.facet.47f) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: %Self => constants.%I.facet.47f // CHECK:STDOUT: %Self.binding.as_type => constants.%empty_struct_type // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @I.WithSelf(constants.%Int.type.facet) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: %Self => constants.%Int.type.facet // CHECK:STDOUT: %Self.binding.as_type => constants.%i32 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @I.WithSelf(constants.%I.facet.608) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: %Self => constants.%I.facet.608 // CHECK:STDOUT: %Self.binding.as_type => constants.%i32 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- fail_assoc_const_mismatch.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %I.type: type = facet_type <@I> [concrete] // CHECK:STDOUT: %Self.ab9: %I.type = symbolic_binding Self, 0 [symbolic] // CHECK:STDOUT: %Self.binding.as_type.d31: type = symbolic_binding_type Self, 0, %Self.ab9 [symbolic] // CHECK:STDOUT: %I.assoc_type: type = assoc_entity_type @I [concrete] // CHECK:STDOUT: %assoc0.490: %I.assoc_type = assoc_entity element0, @I.WithSelf.%V [concrete] // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %empty_struct: %empty_struct_type = struct_value () [concrete] // CHECK:STDOUT: %.Self: %I.type = symbolic_binding .Self [symbolic_self] // CHECK:STDOUT: %.Self.binding.as_type: type = symbolic_binding_type .Self, %.Self [symbolic_self] // CHECK:STDOUT: %I.lookup_impl_witness: = lookup_impl_witness %.Self, @I [symbolic_self] // CHECK:STDOUT: %impl.elem0: %.Self.binding.as_type = impl_witness_access %I.lookup_impl_witness, element0 [symbolic_self] // CHECK:STDOUT: %int_0: Core.IntLiteral = int_value 0 [concrete] // CHECK:STDOUT: %I_where.type: type = facet_type <@I where %impl.elem0 = %int_0> [concrete] // CHECK:STDOUT: %I.impl_witness: = impl_witness @empty_struct_type.as.I.impl.%I.impl_witness_table [concrete] // CHECK:STDOUT: %type: type = facet_type [concrete] // CHECK:STDOUT: %empty_struct.type.facet: %type = facet_value %empty_struct_type, () [concrete] // CHECK:STDOUT: %ImplicitAs.type.cc7: type = generic_interface_type @ImplicitAs [concrete] // CHECK:STDOUT: %ImplicitAs.generic: %ImplicitAs.type.cc7 = struct_value () [concrete] // CHECK:STDOUT: %I.facet: %I.type = facet_value %empty_struct_type, (%I.impl_witness) [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: .ImplicitAs = %Core.ImplicitAs // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: %Core.ImplicitAs: %ImplicitAs.type.cc7 = import_ref Core//prelude/parts/as, ImplicitAs, loaded [concrete = constants.%ImplicitAs.generic] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .I = %I.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %I.decl: type = interface_decl @I [concrete = constants.%I.type] {} {} // CHECK:STDOUT: impl_decl @empty_struct_type.as.I.impl [concrete] {} { // CHECK:STDOUT: %.loc15_7.1: %empty_struct_type = struct_literal () [concrete = constants.%empty_struct] // CHECK:STDOUT: %.loc15_7.2: type = converted %.loc15_7.1, constants.%empty_struct_type [concrete = constants.%empty_struct_type] // CHECK:STDOUT: %I.ref: type = name_ref I, file.%I.decl [concrete = constants.%I.type] // CHECK:STDOUT: %.Self: %I.type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %.Self.ref: %I.type = name_ref .Self, %.Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %.Self.as_type: type = facet_access_type %.Self.ref [symbolic_self = constants.%.Self.binding.as_type] // CHECK:STDOUT: %.loc15_20: type = converted %.Self.ref, %.Self.as_type [symbolic_self = constants.%.Self.binding.as_type] // CHECK:STDOUT: %V.ref: %I.assoc_type = name_ref V, @V.%assoc0 [concrete = constants.%assoc0.490] // CHECK:STDOUT: %impl.elem0: %.Self.binding.as_type = impl_witness_access constants.%I.lookup_impl_witness, element0 [symbolic_self = constants.%impl.elem0] // CHECK:STDOUT: %int_0: Core.IntLiteral = int_value 0 [concrete = constants.%int_0] // CHECK:STDOUT: %.loc15_14.1: type = where_expr %.Self [concrete = constants.%I_where.type] { // CHECK:STDOUT: requirement_base_facet_type constants.%I.type // CHECK:STDOUT: requirement_rewrite %impl.elem0, %int_0 // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: interface @I { // CHECK:STDOUT: %Self: %I.type = symbolic_binding Self, 0 [symbolic = constants.%Self.ab9] // CHECK:STDOUT: %I.WithSelf.decl = interface_with_self_decl @I [concrete] // CHECK:STDOUT: // CHECK:STDOUT: !with Self: // CHECK:STDOUT: %V: @I.WithSelf.%Self.binding.as_type (%Self.binding.as_type.d31) = assoc_const_decl @V [concrete] { // CHECK:STDOUT: %assoc0: %I.assoc_type = assoc_entity element0, @I.WithSelf.%V [concrete = constants.%assoc0.490] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = %Self // CHECK:STDOUT: .V = @V.%assoc0 // CHECK:STDOUT: witness = (@I.WithSelf.%V) // CHECK:STDOUT: // CHECK:STDOUT: !requires: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: impl @empty_struct_type.as.I.impl: %.loc15_7.2 as %.loc15_14.1 { // CHECK:STDOUT: %I.impl_witness_table = impl_witness_table (%impl_witness_assoc_constant), @empty_struct_type.as.I.impl [concrete] // CHECK:STDOUT: %I.impl_witness: = impl_witness %I.impl_witness_table [concrete = constants.%I.impl_witness] // CHECK:STDOUT: %.loc15_14.2: %empty_struct_type = converted constants.%int_0, [concrete = ] // CHECK:STDOUT: %impl_witness_assoc_constant: = impl_witness_assoc_constant [concrete = ] // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: witness = %I.impl_witness // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @I.WithSelf(constants.%Self.ab9) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: %Self => constants.%Self.ab9 // CHECK:STDOUT: %Self.binding.as_type => constants.%Self.binding.as_type.d31 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @I.WithSelf(constants.%.Self) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: %Self => constants.%.Self // CHECK:STDOUT: %Self.binding.as_type => constants.%.Self.binding.as_type // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @I.WithSelf(constants.%empty_struct.type.facet) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: %Self => constants.%empty_struct.type.facet // CHECK:STDOUT: %Self.binding.as_type => constants.%empty_struct_type // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @I.WithSelf(constants.%I.facet) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: %Self => constants.%I.facet // CHECK:STDOUT: %Self.binding.as_type => constants.%empty_struct_type // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- fail_assoc_const_not_constant_after_conversion.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %I.type: type = facet_type <@I> [concrete] // CHECK:STDOUT: %Self.ab9: %I.type = symbolic_binding Self, 0 [symbolic] // CHECK:STDOUT: %Self.binding.as_type.d31: type = symbolic_binding_type Self, 0, %Self.ab9 [symbolic] // CHECK:STDOUT: %I.assoc_type: type = assoc_entity_type @I [concrete] // CHECK:STDOUT: %assoc0.490: %I.assoc_type = assoc_entity element0, @I.WithSelf.%V [concrete] // CHECK:STDOUT: %C: type = class_type @C [concrete] // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %complete_type: = complete_type_witness %empty_struct_type [concrete] // CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] // CHECK:STDOUT: %empty_tuple: %empty_tuple.type = tuple_value () [concrete] // CHECK:STDOUT: %ImplicitAs.type.cc7: type = generic_interface_type @ImplicitAs [concrete] // CHECK:STDOUT: %ImplicitAs.generic: %ImplicitAs.type.cc7 = struct_value () [concrete] // CHECK:STDOUT: %ImplicitAs.type.20a: type = facet_type <@ImplicitAs, @ImplicitAs(%C)> [concrete] // CHECK:STDOUT: %ImplicitAs.impl_witness: = impl_witness @empty_tuple.type.as.ImplicitAs.impl.%ImplicitAs.impl_witness_table [concrete] // CHECK:STDOUT: %pattern_type.cb1: type = pattern_type %empty_tuple.type [concrete] // CHECK:STDOUT: %.a69: Core.Form = init_form %C [concrete] // CHECK:STDOUT: %pattern_type.7c7: type = pattern_type %C [concrete] // CHECK:STDOUT: %empty_tuple.type.as.ImplicitAs.impl.Convert.type: type = fn_type @empty_tuple.type.as.ImplicitAs.impl.Convert [concrete] // CHECK:STDOUT: %empty_tuple.type.as.ImplicitAs.impl.Convert: %empty_tuple.type.as.ImplicitAs.impl.Convert.type = struct_value () [concrete] // CHECK:STDOUT: %ImplicitAs.facet: %ImplicitAs.type.20a = facet_value %empty_tuple.type, (%ImplicitAs.impl_witness) [concrete] // CHECK:STDOUT: %ImplicitAs.WithSelf.Convert.type.59f: type = fn_type @ImplicitAs.WithSelf.Convert, @ImplicitAs.WithSelf(%C, %ImplicitAs.facet) [concrete] // CHECK:STDOUT: %empty_struct: %empty_struct_type = struct_value () [concrete] // CHECK:STDOUT: %C.val: %C = struct_value () [concrete] // CHECK:STDOUT: %.Self: %I.type = symbolic_binding .Self [symbolic_self] // CHECK:STDOUT: %.Self.binding.as_type: type = symbolic_binding_type .Self, %.Self [symbolic_self] // CHECK:STDOUT: %I.lookup_impl_witness: = lookup_impl_witness %.Self, @I [symbolic_self] // CHECK:STDOUT: %impl.elem0: %.Self.binding.as_type = impl_witness_access %I.lookup_impl_witness, element0 [symbolic_self] // CHECK:STDOUT: %I_where.type: type = facet_type <@I where %impl.elem0 = %empty_tuple> [concrete] // CHECK:STDOUT: %I.impl_witness: = impl_witness @C.as.I.impl.%I.impl_witness_table [concrete] // CHECK:STDOUT: %type: type = facet_type [concrete] // CHECK:STDOUT: %C.type.facet: %type = facet_value %C, () [concrete] // CHECK:STDOUT: %.e9b: type = fn_type_with_self_type %ImplicitAs.WithSelf.Convert.type.59f, %ImplicitAs.facet [concrete] // CHECK:STDOUT: %empty_tuple.type.as.ImplicitAs.impl.Convert.bound: = bound_method %empty_tuple, %empty_tuple.type.as.ImplicitAs.impl.Convert [concrete] // CHECK:STDOUT: %I.facet: %I.type = facet_value %C, (%I.impl_witness) [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: .ImplicitAs = %Core.ImplicitAs // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: %Core.ImplicitAs: %ImplicitAs.type.cc7 = import_ref Core//prelude/parts/as, ImplicitAs, loaded [concrete = constants.%ImplicitAs.generic] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .I = %I.decl // CHECK:STDOUT: .C = %C.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %I.decl: type = interface_decl @I [concrete = constants.%I.type] {} {} // CHECK:STDOUT: %C.decl: type = class_decl @C [concrete = constants.%C] {} {} // CHECK:STDOUT: impl_decl @empty_tuple.type.as.ImplicitAs.impl [concrete] {} { // CHECK:STDOUT: %.loc10_7.1: %empty_tuple.type = tuple_literal () [concrete = constants.%empty_tuple] // CHECK:STDOUT: %.loc10_7.2: type = converted %.loc10_7.1, constants.%empty_tuple.type [concrete = constants.%empty_tuple.type] // CHECK:STDOUT: %Core.ref: = name_ref Core, imports.%Core [concrete = imports.%Core] // CHECK:STDOUT: %ImplicitAs.ref: %ImplicitAs.type.cc7 = name_ref ImplicitAs, imports.%Core.ImplicitAs [concrete = constants.%ImplicitAs.generic] // CHECK:STDOUT: %C.ref: type = name_ref C, file.%C.decl [concrete = constants.%C] // CHECK:STDOUT: %ImplicitAs.type: type = facet_type <@ImplicitAs, @ImplicitAs(constants.%C)> [concrete = constants.%ImplicitAs.type.20a] // CHECK:STDOUT: } // CHECK:STDOUT: impl_decl @C.as.I.impl [concrete] {} { // CHECK:STDOUT: %C.ref: type = name_ref C, file.%C.decl [concrete = constants.%C] // CHECK:STDOUT: %I.ref: type = name_ref I, file.%I.decl [concrete = constants.%I.type] // CHECK:STDOUT: %.Self: %I.type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %.Self.ref: %I.type = name_ref .Self, %.Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %.Self.as_type: type = facet_access_type %.Self.ref [symbolic_self = constants.%.Self.binding.as_type] // CHECK:STDOUT: %.loc18_19: type = converted %.Self.ref, %.Self.as_type [symbolic_self = constants.%.Self.binding.as_type] // CHECK:STDOUT: %V.ref: %I.assoc_type = name_ref V, @V.%assoc0 [concrete = constants.%assoc0.490] // CHECK:STDOUT: %impl.elem0.loc18_19: %.Self.binding.as_type = impl_witness_access constants.%I.lookup_impl_witness, element0 [symbolic_self = constants.%impl.elem0] // CHECK:STDOUT: %.loc18_25.1: %empty_tuple.type = tuple_literal () [concrete = constants.%empty_tuple] // CHECK:STDOUT: %empty_tuple: %empty_tuple.type = tuple_value () [concrete = constants.%empty_tuple] // CHECK:STDOUT: %.loc18_25.2: %empty_tuple.type = converted %.loc18_25.1, %empty_tuple [concrete = constants.%empty_tuple] // CHECK:STDOUT: %.loc18_13.1: type = where_expr %.Self [concrete = constants.%I_where.type] { // CHECK:STDOUT: requirement_base_facet_type constants.%I.type // CHECK:STDOUT: requirement_rewrite %impl.elem0.loc18_19, %.loc18_25.2 // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: interface @I { // CHECK:STDOUT: %Self: %I.type = symbolic_binding Self, 0 [symbolic = constants.%Self.ab9] // CHECK:STDOUT: %I.WithSelf.decl = interface_with_self_decl @I [concrete] // CHECK:STDOUT: // CHECK:STDOUT: !with Self: // CHECK:STDOUT: %V: @I.WithSelf.%Self.binding.as_type (%Self.binding.as_type.d31) = assoc_const_decl @V [concrete] { // CHECK:STDOUT: %assoc0: %I.assoc_type = assoc_entity element0, @I.WithSelf.%V [concrete = constants.%assoc0.490] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = %Self // CHECK:STDOUT: .V = @V.%assoc0 // CHECK:STDOUT: witness = (@I.WithSelf.%V) // CHECK:STDOUT: // CHECK:STDOUT: !requires: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: impl @empty_tuple.type.as.ImplicitAs.impl: %.loc10_7.2 as %ImplicitAs.type { // CHECK:STDOUT: %empty_tuple.type.as.ImplicitAs.impl.Convert.decl: %empty_tuple.type.as.ImplicitAs.impl.Convert.type = fn_decl @empty_tuple.type.as.ImplicitAs.impl.Convert [concrete = constants.%empty_tuple.type.as.ImplicitAs.impl.Convert] { // CHECK:STDOUT: %self.patt: %pattern_type.cb1 = value_binding_pattern self [concrete] // CHECK:STDOUT: %self.param_patt: %pattern_type.cb1 = value_param_pattern %self.patt [concrete] // CHECK:STDOUT: %return.patt: %pattern_type.7c7 = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: %pattern_type.7c7 = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %C.ref: type = name_ref C, file.%C.decl [concrete = constants.%C] // CHECK:STDOUT: %.loc11_36: Core.Form = init_form %C.ref [concrete = constants.%.a69] // CHECK:STDOUT: %self.param: %empty_tuple.type = value_param call_param0 // CHECK:STDOUT: %.loc11_28.1: type = splice_block %.loc11_28.3 [concrete = constants.%empty_tuple.type] { // CHECK:STDOUT: %.loc11_28.2: %empty_tuple.type = tuple_literal () [concrete = constants.%empty_tuple] // CHECK:STDOUT: %.loc11_28.3: type = converted %.loc11_28.2, constants.%empty_tuple.type [concrete = constants.%empty_tuple.type] // CHECK:STDOUT: } // CHECK:STDOUT: %self: %empty_tuple.type = value_binding self, %self.param // CHECK:STDOUT: %return.param: ref %C = out_param call_param1 // CHECK:STDOUT: %return: ref %C = return_slot %return.param // CHECK:STDOUT: } // CHECK:STDOUT: %ImplicitAs.impl_witness_table = impl_witness_table (%empty_tuple.type.as.ImplicitAs.impl.Convert.decl), @empty_tuple.type.as.ImplicitAs.impl [concrete] // CHECK:STDOUT: %ImplicitAs.impl_witness: = impl_witness %ImplicitAs.impl_witness_table [concrete = constants.%ImplicitAs.impl_witness] // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .C = // CHECK:STDOUT: .Convert = %empty_tuple.type.as.ImplicitAs.impl.Convert.decl // CHECK:STDOUT: witness = %ImplicitAs.impl_witness // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: impl @C.as.I.impl: %C.ref as %.loc18_13.1 { // CHECK:STDOUT: %I.impl_witness_table = impl_witness_table (%impl_witness_assoc_constant), @C.as.I.impl [concrete] // CHECK:STDOUT: %I.impl_witness: = impl_witness %I.impl_witness_table [concrete = constants.%I.impl_witness] // CHECK:STDOUT: %impl.elem0.loc18_13: %.e9b = impl_witness_access constants.%ImplicitAs.impl_witness, element0 [concrete = constants.%empty_tuple.type.as.ImplicitAs.impl.Convert] // CHECK:STDOUT: %bound_method: = bound_method constants.%empty_tuple, %impl.elem0.loc18_13 [concrete = constants.%empty_tuple.type.as.ImplicitAs.impl.Convert.bound] // CHECK:STDOUT: %.loc18_13.2: ref %C = temporary_storage // CHECK:STDOUT: %empty_tuple.type.as.ImplicitAs.impl.Convert.call: init %C to %.loc18_13.2 = call %bound_method(constants.%empty_tuple) // CHECK:STDOUT: %.loc18_13.3: init %C = converted constants.%empty_tuple, %empty_tuple.type.as.ImplicitAs.impl.Convert.call // CHECK:STDOUT: %.loc18_13.4: ref %C = temporary %.loc18_13.2, %.loc18_13.3 // CHECK:STDOUT: %.loc18_13.5: %C = acquire_value %.loc18_13.4 // CHECK:STDOUT: %impl_witness_assoc_constant: = impl_witness_assoc_constant [concrete = ] // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: witness = %I.impl_witness // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @C { // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%C // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @empty_tuple.type.as.ImplicitAs.impl.Convert(%self.param: %empty_tuple.type) -> out %return.param: %C { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %.loc11_48.1: %empty_struct_type = struct_literal () [concrete = constants.%empty_struct] // CHECK:STDOUT: %.loc11_48.2: init %C to %return.param = class_init () [concrete = constants.%C.val] // CHECK:STDOUT: %.loc11_49: init %C = converted %.loc11_48.1, %.loc11_48.2 [concrete = constants.%C.val] // CHECK:STDOUT: return %.loc11_49 to %return.param // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @I.WithSelf(constants.%Self.ab9) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: %Self => constants.%Self.ab9 // CHECK:STDOUT: %Self.binding.as_type => constants.%Self.binding.as_type.d31 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @I.WithSelf(constants.%.Self) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: %Self => constants.%.Self // CHECK:STDOUT: %Self.binding.as_type => constants.%.Self.binding.as_type // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @I.WithSelf(constants.%C.type.facet) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: %Self => constants.%C.type.facet // CHECK:STDOUT: %Self.binding.as_type => constants.%C // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @I.WithSelf(constants.%I.facet) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: %Self => constants.%I.facet // CHECK:STDOUT: %Self.binding.as_type => constants.%C // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- fail_monomorphization_failure.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %type: type = facet_type [concrete] // CHECK:STDOUT: %.Self.c39: %type = symbolic_binding .Self [symbolic_self] // CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] // CHECK:STDOUT: %IntLiteral.type: type = fn_type @IntLiteral [concrete] // CHECK:STDOUT: %IntLiteral: %IntLiteral.type = struct_value () [concrete] // CHECK:STDOUT: %pattern_type.dc0: type = pattern_type Core.IntLiteral [concrete] // CHECK:STDOUT: %N: Core.IntLiteral = symbolic_binding N, 0 [symbolic] // CHECK:STDOUT: %I.type.609: type = generic_interface_type @I [concrete] // CHECK:STDOUT: %I.generic: %I.type.609 = struct_value () [concrete] // CHECK:STDOUT: %I.type.68b: type = facet_type <@I, @I(%N)> [symbolic] // CHECK:STDOUT: %Self.cad: %I.type.68b = symbolic_binding Self, 1 [symbolic] // CHECK:STDOUT: %Self.binding.as_type.6f3: type = symbolic_binding_type Self, 1, %Self.cad [symbolic] // CHECK:STDOUT: %array_type: type = array_type %N, %Self.binding.as_type.6f3 [symbolic] // CHECK:STDOUT: %I.assoc_type.223: type = assoc_entity_type @I, @I(%N) [symbolic] // CHECK:STDOUT: %assoc0.b3d: %I.assoc_type.223 = assoc_entity element0, @I.WithSelf.%V [symbolic] // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %empty_struct: %empty_struct_type = struct_value () [concrete] // CHECK:STDOUT: %int_1: Core.IntLiteral = int_value 1 [concrete] // CHECK:STDOUT: %Negate.type: type = facet_type <@Negate> [concrete] // CHECK:STDOUT: %Negate.impl_witness: = impl_witness imports.%Negate.impl_witness_table [concrete] // CHECK:STDOUT: %Negate.facet: %Negate.type = facet_value Core.IntLiteral, (%Negate.impl_witness) [concrete] // CHECK:STDOUT: %Negate.WithSelf.Op.type.4dd: type = fn_type @Negate.WithSelf.Op, @Negate.WithSelf(%Negate.facet) [concrete] // CHECK:STDOUT: %.b42: type = fn_type_with_self_type %Negate.WithSelf.Op.type.4dd, %Negate.facet [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.Negate.impl.Op.type: type = fn_type @Core.IntLiteral.as.Negate.impl.Op [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.Negate.impl.Op: %Core.IntLiteral.as.Negate.impl.Op.type = struct_value () [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.Negate.impl.Op.bound: = bound_method %int_1, %Core.IntLiteral.as.Negate.impl.Op [concrete] // CHECK:STDOUT: %int_-1: Core.IntLiteral = int_value -1 [concrete] // CHECK:STDOUT: %I.type.54b: type = facet_type <@I, @I(%int_-1)> [concrete] // CHECK:STDOUT: %.Self.6e0: %I.type.54b = symbolic_binding .Self [symbolic_self] // CHECK:STDOUT: %Self.575: %I.type.54b = symbolic_binding Self, 1 [symbolic] // CHECK:STDOUT: %I.assoc_type.e1a: type = assoc_entity_type @I, @I(%int_-1) [concrete] // CHECK:STDOUT: %assoc0.578: %I.assoc_type.e1a = assoc_entity element0, @I.WithSelf.%V [concrete] // CHECK:STDOUT: %.Self.binding.as_type: type = symbolic_binding_type .Self, %.Self.6e0 [symbolic_self] // CHECK:STDOUT: %empty_tuple: %empty_tuple.type = tuple_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: .IntLiteral = %Core.IntLiteral // CHECK:STDOUT: .Negate = %Core.Negate // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: %Core.IntLiteral: %IntLiteral.type = import_ref Core//prelude/parts/int_literal, IntLiteral, loaded [concrete = constants.%IntLiteral] // CHECK:STDOUT: %Core.Negate: type = import_ref Core//prelude/parts/int_literal, Negate, loaded [concrete = constants.%Negate.type] // CHECK:STDOUT: %Core.import_ref.abd = import_ref Core//prelude/parts/int_literal, loc{{\d+_\d+}}, unloaded // CHECK:STDOUT: %Core.import_ref.82e: %Core.IntLiteral.as.Negate.impl.Op.type = import_ref Core//prelude/parts/int_literal, loc{{\d+_\d+}}, loaded [concrete = constants.%Core.IntLiteral.as.Negate.impl.Op] // CHECK:STDOUT: %Negate.impl_witness_table = impl_witness_table (%Core.import_ref.abd, %Core.import_ref.82e), @Core.IntLiteral.as.Negate.impl [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .I = %I.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %I.decl: %I.type.609 = interface_decl @I [concrete = constants.%I.generic] { // CHECK:STDOUT: %N.patt: %pattern_type.dc0 = symbolic_binding_pattern N, 0 [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %.loc4_33.1: type = splice_block %.loc4_33.3 [concrete = Core.IntLiteral] { // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self = constants.%.Self.c39] // CHECK:STDOUT: %Core.ref: = name_ref Core, imports.%Core [concrete = imports.%Core] // CHECK:STDOUT: %IntLiteral.ref: %IntLiteral.type = name_ref IntLiteral, imports.%Core.IntLiteral [concrete = constants.%IntLiteral] // CHECK:STDOUT: %IntLiteral.call: init type = call %IntLiteral.ref() [concrete = Core.IntLiteral] // CHECK:STDOUT: %.loc4_33.2: type = value_of_initializer %IntLiteral.call [concrete = Core.IntLiteral] // CHECK:STDOUT: %.loc4_33.3: type = converted %IntLiteral.call, %.loc4_33.2 [concrete = Core.IntLiteral] // CHECK:STDOUT: } // CHECK:STDOUT: %N.loc4_13.2: Core.IntLiteral = symbolic_binding N, 0 [symbolic = %N.loc4_13.1 (constants.%N)] // CHECK:STDOUT: } // CHECK:STDOUT: impl_decl @empty_struct_type.as..impl [concrete] {} { // CHECK:STDOUT: %.loc15_7.1: %empty_struct_type = struct_literal () [concrete = constants.%empty_struct] // CHECK:STDOUT: %.loc15_7.2: type = converted %.loc15_7.1, constants.%empty_struct_type [concrete = constants.%empty_struct_type] // CHECK:STDOUT: %I.ref: %I.type.609 = name_ref I, file.%I.decl [concrete = constants.%I.generic] // CHECK:STDOUT: %int_1: Core.IntLiteral = int_value 1 [concrete = constants.%int_1] // CHECK:STDOUT: %impl.elem1: %.b42 = impl_witness_access constants.%Negate.impl_witness, element1 [concrete = constants.%Core.IntLiteral.as.Negate.impl.Op] // CHECK:STDOUT: %bound_method: = bound_method %int_1, %impl.elem1 [concrete = constants.%Core.IntLiteral.as.Negate.impl.Op.bound] // CHECK:STDOUT: %Core.IntLiteral.as.Negate.impl.Op.call: init Core.IntLiteral = call %bound_method(%int_1) [concrete = constants.%int_-1] // CHECK:STDOUT: %.loc15_16.1: Core.IntLiteral = value_of_initializer %Core.IntLiteral.as.Negate.impl.Op.call [concrete = constants.%int_-1] // CHECK:STDOUT: %.loc15_16.2: Core.IntLiteral = converted %Core.IntLiteral.as.Negate.impl.Op.call, %.loc15_16.1 [concrete = constants.%int_-1] // CHECK:STDOUT: %I.type: type = facet_type <@I, @I(constants.%int_-1)> [concrete = constants.%I.type.54b] // CHECK:STDOUT: %.Self: %I.type.54b = symbolic_binding .Self [symbolic_self = constants.%.Self.6e0] // CHECK:STDOUT: %.Self.ref: %I.type.54b = name_ref .Self, %.Self [symbolic_self = constants.%.Self.6e0] // CHECK:STDOUT: %.Self.as_type: type = facet_access_type %.Self.ref [symbolic_self = constants.%.Self.binding.as_type] // CHECK:STDOUT: %.loc15_24: type = converted %.Self.ref, %.Self.as_type [symbolic_self = constants.%.Self.binding.as_type] // CHECK:STDOUT: %V.ref: = name_ref V, [concrete = ] // CHECK:STDOUT: %.loc15_30: %empty_tuple.type = tuple_literal () [concrete = constants.%empty_tuple] // CHECK:STDOUT: %.loc15_18: type = where_expr %.Self [concrete = ] { // CHECK:STDOUT: requirement_base_facet_type constants.%I.type.54b // CHECK:STDOUT: requirement_rewrite %V.ref, // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic interface @I(%N.loc4_13.2: Core.IntLiteral) { // CHECK:STDOUT: %N.loc4_13.1: Core.IntLiteral = symbolic_binding N, 0 [symbolic = %N.loc4_13.1 (constants.%N)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %I.type: type = facet_type <@I, @I(%N.loc4_13.1)> [symbolic = %I.type (constants.%I.type.68b)] // CHECK:STDOUT: %Self.loc4_36.2: @I.%I.type (%I.type.68b) = symbolic_binding Self, 1 [symbolic = %Self.loc4_36.2 (constants.%Self.cad)] // CHECK:STDOUT: // CHECK:STDOUT: interface { // CHECK:STDOUT: %Self.loc4_36.1: @I.%I.type (%I.type.68b) = symbolic_binding Self, 1 [symbolic = %Self.loc4_36.2 (constants.%Self.cad)] // CHECK:STDOUT: %I.WithSelf.decl = interface_with_self_decl @I [concrete] // CHECK:STDOUT: // CHECK:STDOUT: !with Self: // CHECK:STDOUT: %V: @I.WithSelf.%array_type (%array_type) = assoc_const_decl @V [concrete] { // CHECK:STDOUT: %assoc0: @I.WithSelf.%I.assoc_type (%I.assoc_type.223) = assoc_entity element0, @I.WithSelf.%V [symbolic = @I.WithSelf.%assoc0 (constants.%assoc0.b3d)] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = %Self.loc4_36.1 // CHECK:STDOUT: .N = // CHECK:STDOUT: .N = // CHECK:STDOUT: .V = @V.%assoc0 // CHECK:STDOUT: witness = (@I.WithSelf.%V) // CHECK:STDOUT: // CHECK:STDOUT: !requires: // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: impl @empty_struct_type.as..impl: %.loc15_7.2 as %.loc15_18 { // CHECK:STDOUT: !members: // CHECK:STDOUT: witness = // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @I(constants.%N) { // CHECK:STDOUT: %N.loc4_13.1 => constants.%N // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @I.WithSelf(constants.%N, constants.%Self.cad) {} // CHECK:STDOUT: // CHECK:STDOUT: specific @I(constants.%int_-1) { // CHECK:STDOUT: %N.loc4_13.1 => constants.%int_-1 // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %I.type => constants.%I.type.54b // CHECK:STDOUT: %Self.loc4_36.2 => constants.%Self.575 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @I.WithSelf(constants.%int_-1, constants.%Self.cad) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: %N => constants.%int_-1 // CHECK:STDOUT: %I.type => constants.%I.type.54b // CHECK:STDOUT: %Self => constants.%Self.cad // CHECK:STDOUT: %Self.binding.as_type => constants.%Self.binding.as_type.6f3 // CHECK:STDOUT: %array_type => // CHECK:STDOUT: %I.assoc_type => constants.%I.assoc_type.e1a // CHECK:STDOUT: %assoc0 => constants.%assoc0.578 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- fail_todo_constrained_fn.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %I.type: type = facet_type <@I> [concrete] // CHECK:STDOUT: %Self.ab9: %I.type = symbolic_binding Self, 0 [symbolic] // CHECK:STDOUT: %Self.binding.as_type.d31: type = symbolic_binding_type Self, 0, %Self.ab9 [symbolic] // CHECK:STDOUT: %I.assoc_type: type = assoc_entity_type @I [concrete] // CHECK:STDOUT: %assoc0.490: %I.assoc_type = assoc_entity element0, @I.WithSelf.%V [concrete] // CHECK:STDOUT: %type: type = facet_type [concrete] // CHECK:STDOUT: %.Self.c39: %type = symbolic_binding .Self [symbolic_self] // CHECK:STDOUT: %.Self.1dc: %I.type = symbolic_binding .Self [symbolic_self] // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %empty_struct: %empty_struct_type = struct_value () [concrete] // CHECK:STDOUT: %ImplicitAs.type.cc7: type = generic_interface_type @ImplicitAs [concrete] // CHECK:STDOUT: %ImplicitAs.generic: %ImplicitAs.type.cc7 = struct_value () [concrete] // CHECK:STDOUT: %.Self.binding.as_type: type = symbolic_binding_type .Self, %.Self.1dc [symbolic_self] // CHECK:STDOUT: %ImplicitAs.type.d65: type = facet_type <@ImplicitAs, @ImplicitAs(%.Self.binding.as_type)> [symbolic_self] // CHECK:STDOUT: %I.lookup_impl_witness: = lookup_impl_witness %.Self.1dc, @I [symbolic_self] // CHECK:STDOUT: %impl.elem0: %.Self.binding.as_type = impl_witness_access %I.lookup_impl_witness, element0 [symbolic_self] // CHECK:STDOUT: %I_where.type: type = facet_type <@I where %impl.elem0 = %empty_struct and TODO> [concrete] // CHECK:STDOUT: %pattern_type.d53: type = pattern_type %I_where.type [concrete] // CHECK:STDOUT: %T: %I_where.type = symbolic_binding T, 0 [symbolic] // CHECK:STDOUT: %F.type: type = fn_type @F [concrete] // CHECK:STDOUT: %F: %F.type = struct_value () [concrete] // CHECK:STDOUT: %CallF.type: type = fn_type @CallF [concrete] // CHECK:STDOUT: %CallF: %CallF.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: .ImplicitAs = %Core.ImplicitAs // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: %Core.ImplicitAs: %ImplicitAs.type.cc7 = import_ref Core//prelude/parts/as, ImplicitAs, loaded [concrete = constants.%ImplicitAs.generic] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .I = %I.decl // CHECK:STDOUT: .F = %F.decl // CHECK:STDOUT: .CallF = %CallF.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %I.decl: type = interface_decl @I [concrete = constants.%I.type] {} {} // CHECK:STDOUT: %F.decl: %F.type = fn_decl @F [concrete = constants.%F] { // CHECK:STDOUT: %T.patt: %pattern_type.d53 = symbolic_binding_pattern T, 0 [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %.loc8_12.1: type = splice_block %.loc8_12.2 [concrete = constants.%I_where.type] { // CHECK:STDOUT: %.Self.1: %type = symbolic_binding .Self [symbolic_self = constants.%.Self.c39] // CHECK:STDOUT: %I.ref: type = name_ref I, file.%I.decl [concrete = constants.%I.type] // CHECK:STDOUT: %.Self.2: %I.type = symbolic_binding .Self [symbolic_self = constants.%.Self.1dc] // CHECK:STDOUT: %.loc8_19.1: %empty_struct_type = struct_literal () [concrete = constants.%empty_struct] // CHECK:STDOUT: %Core.ref: = name_ref Core, imports.%Core [concrete = imports.%Core] // CHECK:STDOUT: %ImplicitAs.ref: %ImplicitAs.type.cc7 = name_ref ImplicitAs, imports.%Core.ImplicitAs [concrete = constants.%ImplicitAs.generic] // CHECK:STDOUT: %.Self.ref.loc8_43: %I.type = name_ref .Self, %.Self.2 [symbolic_self = constants.%.Self.1dc] // CHECK:STDOUT: %.Self.as_type.loc8_48: type = facet_access_type %.Self.ref.loc8_43 [symbolic_self = constants.%.Self.binding.as_type] // CHECK:STDOUT: %.loc8_48: type = converted %.Self.ref.loc8_43, %.Self.as_type.loc8_48 [symbolic_self = constants.%.Self.binding.as_type] // CHECK:STDOUT: %ImplicitAs.type: type = facet_type <@ImplicitAs, @ImplicitAs(constants.%.Self.binding.as_type)> [symbolic_self = constants.%ImplicitAs.type.d65] // CHECK:STDOUT: %.loc8_19.2: type = converted %.loc8_19.1, constants.%empty_struct_type [concrete = constants.%empty_struct_type] // CHECK:STDOUT: %.Self.ref.loc8_54: %I.type = name_ref .Self, %.Self.2 [symbolic_self = constants.%.Self.1dc] // CHECK:STDOUT: %.Self.as_type.loc8_54: type = facet_access_type %.Self.ref.loc8_54 [symbolic_self = constants.%.Self.binding.as_type] // CHECK:STDOUT: %.loc8_54: type = converted %.Self.ref.loc8_54, %.Self.as_type.loc8_54 [symbolic_self = constants.%.Self.binding.as_type] // CHECK:STDOUT: %V.ref: %I.assoc_type = name_ref V, @V.%assoc0 [concrete = constants.%assoc0.490] // CHECK:STDOUT: %impl.elem0: %.Self.binding.as_type = impl_witness_access constants.%I.lookup_impl_witness, element0 [symbolic_self = constants.%impl.elem0] // CHECK:STDOUT: %.loc8_60.1: %empty_struct_type = struct_literal () [concrete = constants.%empty_struct] // CHECK:STDOUT: %empty_struct: %empty_struct_type = struct_value () [concrete = constants.%empty_struct] // CHECK:STDOUT: %.loc8_60.2: %empty_struct_type = converted %.loc8_60.1, %empty_struct [concrete = constants.%empty_struct] // CHECK:STDOUT: %.loc8_12.2: type = where_expr %.Self.2 [concrete = constants.%I_where.type] { // CHECK:STDOUT: requirement_base_facet_type constants.%I.type // CHECK:STDOUT: requirement_impls %.loc8_19.2, %ImplicitAs.type // CHECK:STDOUT: requirement_rewrite %impl.elem0, %.loc8_60.2 // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: %T.loc8_6.2: %I_where.type = symbolic_binding T, 0 [symbolic = %T.loc8_6.1 (constants.%T)] // CHECK:STDOUT: } // CHECK:STDOUT: %CallF.decl: %CallF.type = fn_decl @CallF [concrete = constants.%CallF] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: interface @I { // CHECK:STDOUT: %Self: %I.type = symbolic_binding Self, 0 [symbolic = constants.%Self.ab9] // CHECK:STDOUT: %I.WithSelf.decl = interface_with_self_decl @I [concrete] // CHECK:STDOUT: // CHECK:STDOUT: !with Self: // CHECK:STDOUT: %V: @I.WithSelf.%Self.binding.as_type (%Self.binding.as_type.d31) = assoc_const_decl @V [concrete] { // CHECK:STDOUT: %assoc0: %I.assoc_type = assoc_entity element0, @I.WithSelf.%V [concrete = constants.%assoc0.490] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = %Self // CHECK:STDOUT: .V = @V.%assoc0 // CHECK:STDOUT: witness = (@I.WithSelf.%V) // CHECK:STDOUT: // CHECK:STDOUT: !requires: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic fn @F(%T.loc8_6.2: %I_where.type) { // CHECK:STDOUT: %T.loc8_6.1: %I_where.type = symbolic_binding T, 0 [symbolic = %T.loc8_6.1 (constants.%T)] // CHECK:STDOUT: // CHECK:STDOUT: fn(); // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @CallF() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %F.ref: %F.type = name_ref F, file.%F.decl [concrete = constants.%F] // CHECK:STDOUT: %.loc19: %empty_struct_type = struct_literal () [concrete = constants.%empty_struct] // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @I.WithSelf(constants.%Self.ab9) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: %Self => constants.%Self.ab9 // CHECK:STDOUT: %Self.binding.as_type => constants.%Self.binding.as_type.d31 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @I.WithSelf(constants.%.Self.1dc) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: %Self => constants.%.Self.1dc // CHECK:STDOUT: %Self.binding.as_type => constants.%.Self.binding.as_type // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @F(constants.%T) { // CHECK:STDOUT: %T.loc8_6.1 => constants.%T // CHECK:STDOUT: } // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/impl/basic.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/none.carbon // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/impl/basic.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/impl/basic.carbon // --- basic.carbon library "[[@TEST_NAME]]"; interface Simple { fn F(); } class C {} //@dump-sem-ir-begin impl C as Simple { fn F() {} } //@dump-sem-ir-end // --- fail_invalid_impl.carbon library "[[@TEST_NAME]]"; interface I { fn Op[unused self: Self](); } class C {} // This produces an invalid impl. // CHECK:STDERR: fail_invalid_impl.carbon:[[@LINE+4]]:6: error: name `Unknown` not found [NameNotFound] // CHECK:STDERR: impl Unknown as I { // CHECK:STDERR: ^~~~~~~ // CHECK:STDERR: impl Unknown as I { fn Op[unused self: Self]() {} } // --- fail_import_invalid_impl.carbon library "[[@TEST_NAME]]"; import library "invalid_impl"; fn F() { // This impl doesn't exist, but it still tests that we can ignore the // `impl Unknown as I` on import. // CHECK:STDERR: fail_import_invalid_impl.carbon:[[@LINE+4]]:3: error: cannot access member of interface `I` in type `type` that does not implement that interface [MissingImplInMemberAccess] // CHECK:STDERR: C.(I.Op)(); // CHECK:STDERR: ^~~~~~~~ // CHECK:STDERR: C.(I.Op)(); } // --- fail_conflicting_bad_impls.carbon library "[[@TEST_NAME]]"; interface I {} class C(T:! type) {} // CHECK:STDERR: fail_conflicting_bad_impls.carbon:[[@LINE+4]]:12: error: name `invalid` not found [NameNotFound] // CHECK:STDERR: final impl invalid as I {} // CHECK:STDERR: ^~~~~~~ // CHECK:STDERR: final impl invalid as I {} // CHECK:STDERR: fail_conflicting_bad_impls.carbon:[[@LINE+11]]:12: error: name `invalid` not found [NameNotFound] // CHECK:STDERR: final impl invalid as I {} // CHECK:STDERR: ^~~~~~~ // CHECK:STDERR: // CHECK:STDERR: fail_conflicting_bad_impls.carbon:[[@LINE+7]]:1: error: redefinition of `impl as I` [ImplRedefinition] // CHECK:STDERR: final impl invalid as I {} // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: fail_conflicting_bad_impls.carbon:[[@LINE-8]]:1: note: previous definition was here [ImplPreviousDefinition] // CHECK:STDERR: final impl invalid as I {} // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: final impl invalid as I {} // CHECK:STDERR: fail_conflicting_bad_impls.carbon:[[@LINE+4]]:12: error: name `alsoinvalid` not found [NameNotFound] // CHECK:STDERR: final impl alsoinvalid as I {} // CHECK:STDERR: ^~~~~~~~~~~ // CHECK:STDERR: final impl alsoinvalid as I {} // CHECK:STDERR: fail_conflicting_bad_impls.carbon:[[@LINE+4]]:14: error: name `invalid` not found [NameNotFound] // CHECK:STDERR: final impl C(invalid) as I {} // CHECK:STDERR: ^~~~~~~ // CHECK:STDERR: final impl C(invalid) as I {} // CHECK:STDERR: fail_conflicting_bad_impls.carbon:[[@LINE+11]]:14: error: name `invalid` not found [NameNotFound] // CHECK:STDERR: final impl C(invalid) as I {} // CHECK:STDERR: ^~~~~~~ // CHECK:STDERR: // CHECK:STDERR: fail_conflicting_bad_impls.carbon:[[@LINE+7]]:1: error: redefinition of `impl as I` [ImplRedefinition] // CHECK:STDERR: final impl C(invalid) as I {} // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: fail_conflicting_bad_impls.carbon:[[@LINE-8]]:1: note: previous definition was here [ImplPreviousDefinition] // CHECK:STDERR: final impl C(invalid) as I {} // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: final impl C(invalid) as I {} // CHECK:STDOUT: --- basic.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %Simple.type: type = facet_type <@Simple> [concrete] // CHECK:STDOUT: %C: type = class_type @C [concrete] // CHECK:STDOUT: %Simple.impl_witness: = impl_witness @C.as.Simple.impl.%Simple.impl_witness_table [concrete] // CHECK:STDOUT: %C.as.Simple.impl.F.type: type = fn_type @C.as.Simple.impl.F [concrete] // CHECK:STDOUT: %C.as.Simple.impl.F: %C.as.Simple.impl.F.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: impl_decl @C.as.Simple.impl [concrete] {} { // CHECK:STDOUT: %C.ref: type = name_ref C, file.%C.decl [concrete = constants.%C] // CHECK:STDOUT: %Simple.ref: type = name_ref Simple, file.%Simple.decl [concrete = constants.%Simple.type] // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: impl @C.as.Simple.impl: %C.ref as %Simple.ref { // CHECK:STDOUT: %C.as.Simple.impl.F.decl: %C.as.Simple.impl.F.type = fn_decl @C.as.Simple.impl.F [concrete = constants.%C.as.Simple.impl.F] {} {} // CHECK:STDOUT: %Simple.impl_witness_table = impl_witness_table (%C.as.Simple.impl.F.decl), @C.as.Simple.impl [concrete] // CHECK:STDOUT: %Simple.impl_witness: = impl_witness %Simple.impl_witness_table [concrete = constants.%Simple.impl_witness] // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .F = %C.as.Simple.impl.F.decl // CHECK:STDOUT: witness = %Simple.impl_witness // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @C.as.Simple.impl.F() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/impl/compound.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/none.carbon // TODO: Add ranges and switch to "--dump-sem-ir-ranges=only". // EXTRA-ARGS: --dump-sem-ir-ranges=if-present // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/impl/compound.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/impl/compound.carbon // --- core.carbon package Core; interface ImplicitAs(Dest:! type) { fn Convert[self: Self]() -> Dest; } // --- non-instance_success.carbon library "[[@TEST_NAME]]"; interface NonInstance1 { fn F1(); } impl {.a: ()} as NonInstance1 { fn F1() {} } fn NonInstanceCall1() { {.a: ()}.(NonInstance1.F1)(); } // --- fail_non-instance.carbon library "[[@TEST_NAME]]"; import Core; interface NonInstance2 { fn F2(); } impl {.b: ()} as NonInstance2 { fn F2() {} } fn NonInstanceCall2(n: {.b: ()}) { // CHECK:STDERR: fail_non-instance.carbon:[[@LINE+7]]:3: error: cannot implicitly convert non-type value of type `{.b: ()}` into type implementing `NonInstance2` [ConversionFailureNonTypeToFacet] // CHECK:STDERR: n.(NonInstance2.F2)(); // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: fail_non-instance.carbon:[[@LINE+4]]:3: note: type `{.b: ()}` does not implement interface `Core.ImplicitAs(NonInstance2)` [MissingImplInMemberAccessInContext] // CHECK:STDERR: n.(NonInstance2.F2)(); // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: n.(NonInstance2.F2)(); } // --- fail_non-instance_indirect.carbon library "[[@TEST_NAME]]"; import Core; interface NonInstance3 { fn F3(); } impl {.c: ()} as NonInstance3 { fn F3() {} } fn NonInstanceCallIndirect(p: {.c: ()}*) { // CHECK:STDERR: fail_non-instance_indirect.carbon:[[@LINE+7]]:3: error: cannot implicitly convert non-type value of type `{.c: ()}` into type implementing `NonInstance3` [ConversionFailureNonTypeToFacet] // CHECK:STDERR: p->(NonInstance3.F3)(); // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: fail_non-instance_indirect.carbon:[[@LINE+4]]:3: note: type `{.c: ()}` does not implement interface `Core.ImplicitAs(NonInstance3)` [MissingImplInMemberAccessInContext] // CHECK:STDERR: p->(NonInstance3.F3)(); // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: p->(NonInstance3.F3)(); } // --- instance_success.carbon library "[[@TEST_NAME]]"; interface Instance1 { fn G1[self: Self](); } impl {.d: ()} as Instance1 { fn G1[unused self: Self]() {} } fn InstanceCall(n: {.d: ()}) { n.(Instance1.G1)(); } fn InstanceCallIndirect(p: {.d: ()}*) { p->(Instance1.G1)(); } // --- fail_instance.carbon library "[[@TEST_NAME]]"; interface Instance2 { fn G2[self: Self](); } impl {.e: ()} as Instance2 { fn G2[unused self: Self]() {} } fn InstanceCallFail() { // CHECK:STDERR: fail_instance.carbon:[[@LINE+4]]:3: error: cannot access member of interface `Instance2` in type `type` that does not implement that interface [MissingImplInMemberAccess] // CHECK:STDERR: {.e: ()}.(Instance2.G2)(); // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: {.e: ()}.(Instance2.G2)(); } // CHECK:STDOUT: --- core.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %type: type = facet_type [concrete] // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self] // CHECK:STDOUT: %pattern_type.98f: type = pattern_type type [concrete] // CHECK:STDOUT: %Dest: type = symbolic_binding Dest, 0 [symbolic] // CHECK:STDOUT: %ImplicitAs.type.595: type = generic_interface_type @ImplicitAs [concrete] // CHECK:STDOUT: %ImplicitAs.generic: %ImplicitAs.type.595 = struct_value () [concrete] // CHECK:STDOUT: %ImplicitAs.type.9fe: type = facet_type <@ImplicitAs, @ImplicitAs(%Dest)> [symbolic] // CHECK:STDOUT: %Self: %ImplicitAs.type.9fe = symbolic_binding Self, 1 [symbolic] // CHECK:STDOUT: %Self.binding.as_type: type = symbolic_binding_type Self, 1, %Self [symbolic] // CHECK:STDOUT: %pattern_type.8de: type = pattern_type %Self.binding.as_type [symbolic] // CHECK:STDOUT: %.184: Core.Form = init_form %Dest [symbolic] // CHECK:STDOUT: %pattern_type.51d: type = pattern_type %Dest [symbolic] // CHECK:STDOUT: %ImplicitAs.WithSelf.Convert.type: type = fn_type @ImplicitAs.WithSelf.Convert, @ImplicitAs.WithSelf(%Dest, %Self) [symbolic] // CHECK:STDOUT: %ImplicitAs.WithSelf.Convert: %ImplicitAs.WithSelf.Convert.type = struct_value () [symbolic] // CHECK:STDOUT: %ImplicitAs.assoc_type: type = assoc_entity_type @ImplicitAs, @ImplicitAs(%Dest) [symbolic] // CHECK:STDOUT: %assoc0: %ImplicitAs.assoc_type = assoc_entity element0, @ImplicitAs.WithSelf.%ImplicitAs.WithSelf.Convert.decl [symbolic] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .ImplicitAs = %ImplicitAs.decl // CHECK:STDOUT: } // CHECK:STDOUT: %ImplicitAs.decl: %ImplicitAs.type.595 = interface_decl @ImplicitAs [concrete = constants.%ImplicitAs.generic] { // CHECK:STDOUT: %Dest.patt: %pattern_type.98f = symbolic_binding_pattern Dest, 0 [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %.loc3_29.1: type = splice_block %.loc3_29.2 [concrete = type] { // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %.loc3_29.2: type = type_literal type [concrete = type] // CHECK:STDOUT: } // CHECK:STDOUT: %Dest.loc3_22.2: type = symbolic_binding Dest, 0 [symbolic = %Dest.loc3_22.1 (constants.%Dest)] // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic interface @ImplicitAs(%Dest.loc3_22.2: type) { // CHECK:STDOUT: %Dest.loc3_22.1: type = symbolic_binding Dest, 0 [symbolic = %Dest.loc3_22.1 (constants.%Dest)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %ImplicitAs.type: type = facet_type <@ImplicitAs, @ImplicitAs(%Dest.loc3_22.1)> [symbolic = %ImplicitAs.type (constants.%ImplicitAs.type.9fe)] // CHECK:STDOUT: %Self.loc3_35.2: @ImplicitAs.%ImplicitAs.type (%ImplicitAs.type.9fe) = symbolic_binding Self, 1 [symbolic = %Self.loc3_35.2 (constants.%Self)] // CHECK:STDOUT: // CHECK:STDOUT: interface { // CHECK:STDOUT: %Self.loc3_35.1: @ImplicitAs.%ImplicitAs.type (%ImplicitAs.type.9fe) = symbolic_binding Self, 1 [symbolic = %Self.loc3_35.2 (constants.%Self)] // CHECK:STDOUT: %ImplicitAs.WithSelf.decl = interface_with_self_decl @ImplicitAs [concrete] // CHECK:STDOUT: // CHECK:STDOUT: !with Self: // CHECK:STDOUT: %ImplicitAs.WithSelf.Convert.decl: @ImplicitAs.WithSelf.%ImplicitAs.WithSelf.Convert.type (%ImplicitAs.WithSelf.Convert.type) = fn_decl @ImplicitAs.WithSelf.Convert [symbolic = @ImplicitAs.WithSelf.%ImplicitAs.WithSelf.Convert (constants.%ImplicitAs.WithSelf.Convert)] { // CHECK:STDOUT: %self.patt: @ImplicitAs.WithSelf.Convert.%pattern_type.loc4_14 (%pattern_type.8de) = value_binding_pattern self [concrete] // CHECK:STDOUT: %self.param_patt: @ImplicitAs.WithSelf.Convert.%pattern_type.loc4_14 (%pattern_type.8de) = value_param_pattern %self.patt [concrete] // CHECK:STDOUT: %return.patt: @ImplicitAs.WithSelf.Convert.%pattern_type.loc4_28 (%pattern_type.51d) = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: @ImplicitAs.WithSelf.Convert.%pattern_type.loc4_28 (%pattern_type.51d) = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %Dest.ref: type = name_ref Dest, @ImplicitAs.%Dest.loc3_22.2 [symbolic = %Dest (constants.%Dest)] // CHECK:STDOUT: %.loc4_31.2: Core.Form = init_form %Dest.ref [symbolic = %.loc4_31.1 (constants.%.184)] // CHECK:STDOUT: %self.param: @ImplicitAs.WithSelf.Convert.%Self.binding.as_type (%Self.binding.as_type) = value_param call_param0 // CHECK:STDOUT: %.loc4_20.1: type = splice_block %.loc4_20.3 [symbolic = %Self.binding.as_type (constants.%Self.binding.as_type)] { // CHECK:STDOUT: %.loc4_20.2: @ImplicitAs.WithSelf.Convert.%ImplicitAs.type (%ImplicitAs.type.9fe) = specific_constant @ImplicitAs.%Self.loc3_35.1, @ImplicitAs(constants.%Dest) [symbolic = %Self (constants.%Self)] // CHECK:STDOUT: %Self.ref: @ImplicitAs.WithSelf.Convert.%ImplicitAs.type (%ImplicitAs.type.9fe) = name_ref Self, %.loc4_20.2 [symbolic = %Self (constants.%Self)] // CHECK:STDOUT: %Self.as_type: type = facet_access_type %Self.ref [symbolic = %Self.binding.as_type (constants.%Self.binding.as_type)] // CHECK:STDOUT: %.loc4_20.3: type = converted %Self.ref, %Self.as_type [symbolic = %Self.binding.as_type (constants.%Self.binding.as_type)] // CHECK:STDOUT: } // CHECK:STDOUT: %self: @ImplicitAs.WithSelf.Convert.%Self.binding.as_type (%Self.binding.as_type) = value_binding self, %self.param // CHECK:STDOUT: %return.param: ref @ImplicitAs.WithSelf.Convert.%Dest (%Dest) = out_param call_param1 // CHECK:STDOUT: %return: ref @ImplicitAs.WithSelf.Convert.%Dest (%Dest) = return_slot %return.param // CHECK:STDOUT: } // CHECK:STDOUT: %assoc0.loc4_35.1: @ImplicitAs.WithSelf.%ImplicitAs.assoc_type (%ImplicitAs.assoc_type) = assoc_entity element0, %ImplicitAs.WithSelf.Convert.decl [symbolic = %assoc0.loc4_35.2 (constants.%assoc0)] // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = %Self.loc3_35.1 // CHECK:STDOUT: .Dest = // CHECK:STDOUT: .Dest = // CHECK:STDOUT: .Convert = @ImplicitAs.WithSelf.%assoc0.loc4_35.1 // CHECK:STDOUT: witness = (@ImplicitAs.WithSelf.%ImplicitAs.WithSelf.Convert.decl) // CHECK:STDOUT: // CHECK:STDOUT: !requires: // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic fn @ImplicitAs.WithSelf.Convert(@ImplicitAs.%Dest.loc3_22.2: type, @ImplicitAs.%Self.loc3_35.1: @ImplicitAs.%ImplicitAs.type (%ImplicitAs.type.9fe)) { // CHECK:STDOUT: %Dest: type = symbolic_binding Dest, 0 [symbolic = %Dest (constants.%Dest)] // CHECK:STDOUT: %ImplicitAs.type: type = facet_type <@ImplicitAs, @ImplicitAs(%Dest)> [symbolic = %ImplicitAs.type (constants.%ImplicitAs.type.9fe)] // CHECK:STDOUT: %Self: @ImplicitAs.WithSelf.Convert.%ImplicitAs.type (%ImplicitAs.type.9fe) = symbolic_binding Self, 1 [symbolic = %Self (constants.%Self)] // CHECK:STDOUT: %Self.binding.as_type: type = symbolic_binding_type Self, 1, %Self [symbolic = %Self.binding.as_type (constants.%Self.binding.as_type)] // CHECK:STDOUT: %pattern_type.loc4_14: type = pattern_type %Self.binding.as_type [symbolic = %pattern_type.loc4_14 (constants.%pattern_type.8de)] // CHECK:STDOUT: %.loc4_31.1: Core.Form = init_form %Dest [symbolic = %.loc4_31.1 (constants.%.184)] // CHECK:STDOUT: %pattern_type.loc4_28: type = pattern_type %Dest [symbolic = %pattern_type.loc4_28 (constants.%pattern_type.51d)] // CHECK:STDOUT: // CHECK:STDOUT: fn(%self.param: @ImplicitAs.WithSelf.Convert.%Self.binding.as_type (%Self.binding.as_type)) -> out %return.param: @ImplicitAs.WithSelf.Convert.%Dest (%Dest); // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @ImplicitAs(constants.%Dest) { // CHECK:STDOUT: %Dest.loc3_22.1 => constants.%Dest // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @ImplicitAs.WithSelf(constants.%Dest, constants.%Self) {} // CHECK:STDOUT: // CHECK:STDOUT: specific @ImplicitAs.WithSelf.Convert(constants.%Dest, constants.%Self) { // CHECK:STDOUT: %Dest => constants.%Dest // CHECK:STDOUT: %ImplicitAs.type => constants.%ImplicitAs.type.9fe // CHECK:STDOUT: %Self => constants.%Self // CHECK:STDOUT: %Self.binding.as_type => constants.%Self.binding.as_type // CHECK:STDOUT: %pattern_type.loc4_14 => constants.%pattern_type.8de // CHECK:STDOUT: %.loc4_31.1 => constants.%.184 // CHECK:STDOUT: %pattern_type.loc4_28 => constants.%pattern_type.51d // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- non-instance_success.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %NonInstance1.type: type = facet_type <@NonInstance1> [concrete] // CHECK:STDOUT: %Self: %NonInstance1.type = symbolic_binding Self, 0 [symbolic] // CHECK:STDOUT: %NonInstance1.WithSelf.F1.type.3a6: type = fn_type @NonInstance1.WithSelf.F1, @NonInstance1.WithSelf(%Self) [symbolic] // CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] // CHECK:STDOUT: %NonInstance1.WithSelf.F1.898: %NonInstance1.WithSelf.F1.type.3a6 = struct_value () [symbolic] // CHECK:STDOUT: %NonInstance1.assoc_type: type = assoc_entity_type @NonInstance1 [concrete] // CHECK:STDOUT: %assoc0: %NonInstance1.assoc_type = assoc_entity element0, @NonInstance1.WithSelf.%NonInstance1.WithSelf.F1.decl [concrete] // CHECK:STDOUT: %empty_tuple: %empty_tuple.type = tuple_value () [concrete] // CHECK:STDOUT: %struct_type.a: type = struct_type {.a: %empty_tuple.type} [concrete] // CHECK:STDOUT: %NonInstance1.impl_witness: = impl_witness @struct_type.a.as.NonInstance1.impl.%NonInstance1.impl_witness_table [concrete] // CHECK:STDOUT: %struct_type.a.as.NonInstance1.impl.F1.type: type = fn_type @struct_type.a.as.NonInstance1.impl.F1 [concrete] // CHECK:STDOUT: %struct_type.a.as.NonInstance1.impl.F1: %struct_type.a.as.NonInstance1.impl.F1.type = struct_value () [concrete] // CHECK:STDOUT: %NonInstance1.facet: %NonInstance1.type = facet_value %struct_type.a, (%NonInstance1.impl_witness) [concrete] // CHECK:STDOUT: %NonInstance1.WithSelf.F1.type.a2c: type = fn_type @NonInstance1.WithSelf.F1, @NonInstance1.WithSelf(%NonInstance1.facet) [concrete] // CHECK:STDOUT: %NonInstance1.WithSelf.F1.e33: %NonInstance1.WithSelf.F1.type.a2c = struct_value () [concrete] // CHECK:STDOUT: %NonInstanceCall1.type: type = fn_type @NonInstanceCall1 [concrete] // CHECK:STDOUT: %NonInstanceCall1: %NonInstanceCall1.type = struct_value () [concrete] // CHECK:STDOUT: %.737: type = fn_type_with_self_type %NonInstance1.WithSelf.F1.type.a2c, %NonInstance1.facet [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .NonInstance1 = %NonInstance1.decl // CHECK:STDOUT: .NonInstanceCall1 = %NonInstanceCall1.decl // CHECK:STDOUT: } // CHECK:STDOUT: %NonInstance1.decl: type = interface_decl @NonInstance1 [concrete = constants.%NonInstance1.type] {} {} // CHECK:STDOUT: impl_decl @struct_type.a.as.NonInstance1.impl [concrete] {} { // CHECK:STDOUT: %.loc7_12.1: %empty_tuple.type = tuple_literal () [concrete = constants.%empty_tuple] // CHECK:STDOUT: %.loc7_12.2: type = converted %.loc7_12.1, constants.%empty_tuple.type [concrete = constants.%empty_tuple.type] // CHECK:STDOUT: %struct_type.a: type = struct_type {.a: %empty_tuple.type} [concrete = constants.%struct_type.a] // CHECK:STDOUT: %NonInstance1.ref: type = name_ref NonInstance1, file.%NonInstance1.decl [concrete = constants.%NonInstance1.type] // CHECK:STDOUT: } // CHECK:STDOUT: %NonInstanceCall1.decl: %NonInstanceCall1.type = fn_decl @NonInstanceCall1 [concrete = constants.%NonInstanceCall1] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: interface @NonInstance1 { // CHECK:STDOUT: %Self: %NonInstance1.type = symbolic_binding Self, 0 [symbolic = constants.%Self] // CHECK:STDOUT: %NonInstance1.WithSelf.decl = interface_with_self_decl @NonInstance1 [concrete] // CHECK:STDOUT: // CHECK:STDOUT: !with Self: // CHECK:STDOUT: %NonInstance1.WithSelf.F1.decl: @NonInstance1.WithSelf.%NonInstance1.WithSelf.F1.type (%NonInstance1.WithSelf.F1.type.3a6) = fn_decl @NonInstance1.WithSelf.F1 [symbolic = @NonInstance1.WithSelf.%NonInstance1.WithSelf.F1 (constants.%NonInstance1.WithSelf.F1.898)] {} {} // CHECK:STDOUT: %assoc0: %NonInstance1.assoc_type = assoc_entity element0, %NonInstance1.WithSelf.F1.decl [concrete = constants.%assoc0] // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = %Self // CHECK:STDOUT: .F1 = @NonInstance1.WithSelf.%assoc0 // CHECK:STDOUT: witness = (@NonInstance1.WithSelf.%NonInstance1.WithSelf.F1.decl) // CHECK:STDOUT: // CHECK:STDOUT: !requires: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: impl @struct_type.a.as.NonInstance1.impl: %struct_type.a as %NonInstance1.ref { // CHECK:STDOUT: %struct_type.a.as.NonInstance1.impl.F1.decl: %struct_type.a.as.NonInstance1.impl.F1.type = fn_decl @struct_type.a.as.NonInstance1.impl.F1 [concrete = constants.%struct_type.a.as.NonInstance1.impl.F1] {} {} // CHECK:STDOUT: %NonInstance1.impl_witness_table = impl_witness_table (%struct_type.a.as.NonInstance1.impl.F1.decl), @struct_type.a.as.NonInstance1.impl [concrete] // CHECK:STDOUT: %NonInstance1.impl_witness: = impl_witness %NonInstance1.impl_witness_table [concrete = constants.%NonInstance1.impl_witness] // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .F1 = %struct_type.a.as.NonInstance1.impl.F1.decl // CHECK:STDOUT: witness = %NonInstance1.impl_witness // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic fn @NonInstance1.WithSelf.F1(@NonInstance1.%Self: %NonInstance1.type) { // CHECK:STDOUT: fn(); // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @struct_type.a.as.NonInstance1.impl.F1() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @NonInstanceCall1() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %.loc12_9.1: %empty_tuple.type = tuple_literal () [concrete = constants.%empty_tuple] // CHECK:STDOUT: %.loc12_9.2: type = converted %.loc12_9.1, constants.%empty_tuple.type [concrete = constants.%empty_tuple.type] // CHECK:STDOUT: %struct_type.a: type = struct_type {.a: %empty_tuple.type} [concrete = constants.%struct_type.a] // CHECK:STDOUT: %NonInstance1.ref: type = name_ref NonInstance1, file.%NonInstance1.decl [concrete = constants.%NonInstance1.type] // CHECK:STDOUT: %F1.ref: %NonInstance1.assoc_type = name_ref F1, @NonInstance1.WithSelf.%assoc0 [concrete = constants.%assoc0] // CHECK:STDOUT: %NonInstance1.facet: %NonInstance1.type = facet_value %struct_type.a, (constants.%NonInstance1.impl_witness) [concrete = constants.%NonInstance1.facet] // CHECK:STDOUT: %.loc12_11: %NonInstance1.type = converted %struct_type.a, %NonInstance1.facet [concrete = constants.%NonInstance1.facet] // CHECK:STDOUT: %impl.elem0: %.737 = impl_witness_access constants.%NonInstance1.impl_witness, element0 [concrete = constants.%struct_type.a.as.NonInstance1.impl.F1] // CHECK:STDOUT: %struct_type.a.as.NonInstance1.impl.F1.call: init %empty_tuple.type = call %impl.elem0() // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @NonInstance1.WithSelf(constants.%Self) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: %Self => constants.%Self // CHECK:STDOUT: %NonInstance1.WithSelf.F1.type => constants.%NonInstance1.WithSelf.F1.type.3a6 // CHECK:STDOUT: %NonInstance1.WithSelf.F1 => constants.%NonInstance1.WithSelf.F1.898 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @NonInstance1.WithSelf.F1(constants.%Self) {} // CHECK:STDOUT: // CHECK:STDOUT: specific @NonInstance1.WithSelf(constants.%NonInstance1.facet) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: %Self => constants.%NonInstance1.facet // CHECK:STDOUT: %NonInstance1.WithSelf.F1.type => constants.%NonInstance1.WithSelf.F1.type.a2c // CHECK:STDOUT: %NonInstance1.WithSelf.F1 => constants.%NonInstance1.WithSelf.F1.e33 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @NonInstance1.WithSelf.F1(constants.%NonInstance1.facet) {} // CHECK:STDOUT: // CHECK:STDOUT: --- fail_non-instance.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %NonInstance2.type: type = facet_type <@NonInstance2> [concrete] // CHECK:STDOUT: %Self.e6c: %NonInstance2.type = symbolic_binding Self, 0 [symbolic] // CHECK:STDOUT: %NonInstance2.WithSelf.F2.type.824: type = fn_type @NonInstance2.WithSelf.F2, @NonInstance2.WithSelf(%Self.e6c) [symbolic] // CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] // CHECK:STDOUT: %NonInstance2.WithSelf.F2.9da: %NonInstance2.WithSelf.F2.type.824 = struct_value () [symbolic] // CHECK:STDOUT: %NonInstance2.assoc_type: type = assoc_entity_type @NonInstance2 [concrete] // CHECK:STDOUT: %assoc0.56f: %NonInstance2.assoc_type = assoc_entity element0, @NonInstance2.WithSelf.%NonInstance2.WithSelf.F2.decl [concrete] // CHECK:STDOUT: %empty_tuple: %empty_tuple.type = tuple_value () [concrete] // CHECK:STDOUT: %struct_type.b: type = struct_type {.b: %empty_tuple.type} [concrete] // CHECK:STDOUT: %NonInstance2.impl_witness: = impl_witness @struct_type.b.as.NonInstance2.impl.%NonInstance2.impl_witness_table [concrete] // CHECK:STDOUT: %struct_type.b.as.NonInstance2.impl.F2.type: type = fn_type @struct_type.b.as.NonInstance2.impl.F2 [concrete] // CHECK:STDOUT: %struct_type.b.as.NonInstance2.impl.F2: %struct_type.b.as.NonInstance2.impl.F2.type = struct_value () [concrete] // CHECK:STDOUT: %NonInstance2.facet: %NonInstance2.type = facet_value %struct_type.b, (%NonInstance2.impl_witness) [concrete] // CHECK:STDOUT: %NonInstance2.WithSelf.F2.type.041: type = fn_type @NonInstance2.WithSelf.F2, @NonInstance2.WithSelf(%NonInstance2.facet) [concrete] // CHECK:STDOUT: %NonInstance2.WithSelf.F2.fc4: %NonInstance2.WithSelf.F2.type.041 = struct_value () [concrete] // CHECK:STDOUT: %pattern_type.d5c: type = pattern_type %struct_type.b [concrete] // CHECK:STDOUT: %NonInstanceCall2.type: type = fn_type @NonInstanceCall2 [concrete] // CHECK:STDOUT: %NonInstanceCall2: %NonInstanceCall2.type = struct_value () [concrete] // CHECK:STDOUT: %ImplicitAs.type.cc7: type = generic_interface_type @ImplicitAs [concrete] // CHECK:STDOUT: %ImplicitAs.generic: %ImplicitAs.type.cc7 = struct_value () [concrete] // CHECK:STDOUT: %Dest: type = symbolic_binding Dest, 0 [symbolic] // CHECK:STDOUT: %ImplicitAs.type.031: type = facet_type <@ImplicitAs, @ImplicitAs(%Dest)> [symbolic] // CHECK:STDOUT: %Self.738: %ImplicitAs.type.031 = symbolic_binding Self, 1 [symbolic] // CHECK:STDOUT: %ImplicitAs.assoc_type.ff3: type = assoc_entity_type @ImplicitAs, @ImplicitAs(%Dest) [symbolic] // CHECK:STDOUT: %ImplicitAs.WithSelf.Convert.type.b3a: type = fn_type @ImplicitAs.WithSelf.Convert, @ImplicitAs.WithSelf(%Dest, %Self.738) [symbolic] // CHECK:STDOUT: %ImplicitAs.WithSelf.Convert.1de: %ImplicitAs.WithSelf.Convert.type.b3a = struct_value () [symbolic] // CHECK:STDOUT: %pattern_type.51d: type = pattern_type %Dest [symbolic] // CHECK:STDOUT: %Self.binding.as_type: type = symbolic_binding_type Self, 1, %Self.738 [symbolic] // CHECK:STDOUT: %pattern_type.e9a: type = pattern_type %Self.binding.as_type [symbolic] // CHECK:STDOUT: %.184: Core.Form = init_form %Dest [symbolic] // CHECK:STDOUT: %ImplicitAs.type.282: type = facet_type <@ImplicitAs, @ImplicitAs(%NonInstance2.type)> [concrete] // CHECK:STDOUT: %Self.df1: %ImplicitAs.type.282 = symbolic_binding Self, 1 [symbolic] // CHECK:STDOUT: %ImplicitAs.WithSelf.Convert.type.03b: type = fn_type @ImplicitAs.WithSelf.Convert, @ImplicitAs.WithSelf(%NonInstance2.type, %Self.738) [symbolic] // CHECK:STDOUT: %ImplicitAs.WithSelf.Convert.4fe: %ImplicitAs.WithSelf.Convert.type.03b = struct_value () [symbolic] // CHECK:STDOUT: %ImplicitAs.assoc_type.cd6: type = assoc_entity_type @ImplicitAs, @ImplicitAs(%NonInstance2.type) [concrete] // CHECK:STDOUT: %assoc0.af4: %ImplicitAs.assoc_type.cd6 = assoc_entity element0, imports.%Core.import_ref.201 [concrete] // CHECK:STDOUT: %assoc0.843: %ImplicitAs.assoc_type.ff3 = assoc_entity element0, imports.%Core.import_ref.cc1 [symbolic] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: .ImplicitAs = %Core.ImplicitAs // CHECK:STDOUT: import Core//default // CHECK:STDOUT: } // CHECK:STDOUT: %Core.ImplicitAs: %ImplicitAs.type.cc7 = import_ref Core//default, ImplicitAs, loaded [concrete = constants.%ImplicitAs.generic] // CHECK:STDOUT: %Core.import_ref.178: @ImplicitAs.WithSelf.%ImplicitAs.assoc_type (%ImplicitAs.assoc_type.ff3) = import_ref Core//default, loc{{\d+_\d+}}, loaded [symbolic = @ImplicitAs.WithSelf.%assoc0 (constants.%assoc0.843)] // CHECK:STDOUT: %Core.Convert = import_ref Core//default, Convert, unloaded // CHECK:STDOUT: %Core.import_ref.201: @ImplicitAs.WithSelf.%ImplicitAs.WithSelf.Convert.type (%ImplicitAs.WithSelf.Convert.type.b3a) = import_ref Core//default, loc{{\d+_\d+}}, loaded [symbolic = @ImplicitAs.WithSelf.%ImplicitAs.WithSelf.Convert (constants.%ImplicitAs.WithSelf.Convert.1de)] // CHECK:STDOUT: %Core.import_ref.b3bc94.2: type = import_ref Core//default, loc{{\d+_\d+}}, loaded [symbolic = @ImplicitAs.%Dest (constants.%Dest)] // CHECK:STDOUT: %Core.import_ref.ac4dc5.2: @ImplicitAs.%ImplicitAs.type (%ImplicitAs.type.031) = import_ref Core//default, loc{{\d+_\d+}}, loaded [symbolic = @ImplicitAs.%Self (constants.%Self.738)] // CHECK:STDOUT: %Core.import_ref.9ec = import_ref Core//default, loc{{\d+_\d+}}, unloaded // CHECK:STDOUT: %Core.import_ref.b3bc94.3: type = import_ref Core//default, loc{{\d+_\d+}}, loaded [symbolic = @ImplicitAs.%Dest (constants.%Dest)] // CHECK:STDOUT: %Core.import_ref.cc1 = import_ref Core//default, loc{{\d+_\d+}}, unloaded // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .NonInstance2 = %NonInstance2.decl // CHECK:STDOUT: .NonInstanceCall2 = %NonInstanceCall2.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %NonInstance2.decl: type = interface_decl @NonInstance2 [concrete = constants.%NonInstance2.type] {} {} // CHECK:STDOUT: impl_decl @struct_type.b.as.NonInstance2.impl [concrete] {} { // CHECK:STDOUT: %.loc9_12.1: %empty_tuple.type = tuple_literal () [concrete = constants.%empty_tuple] // CHECK:STDOUT: %.loc9_12.2: type = converted %.loc9_12.1, constants.%empty_tuple.type [concrete = constants.%empty_tuple.type] // CHECK:STDOUT: %struct_type.b: type = struct_type {.b: %empty_tuple.type} [concrete = constants.%struct_type.b] // CHECK:STDOUT: %NonInstance2.ref: type = name_ref NonInstance2, file.%NonInstance2.decl [concrete = constants.%NonInstance2.type] // CHECK:STDOUT: } // CHECK:STDOUT: %NonInstanceCall2.decl: %NonInstanceCall2.type = fn_decl @NonInstanceCall2 [concrete = constants.%NonInstanceCall2] { // CHECK:STDOUT: %n.patt: %pattern_type.d5c = value_binding_pattern n [concrete] // CHECK:STDOUT: %n.param_patt: %pattern_type.d5c = value_param_pattern %n.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %n.param: %struct_type.b = value_param call_param0 // CHECK:STDOUT: %.loc13_31: type = splice_block %struct_type.b [concrete = constants.%struct_type.b] { // CHECK:STDOUT: %.loc13_30.1: %empty_tuple.type = tuple_literal () [concrete = constants.%empty_tuple] // CHECK:STDOUT: %.loc13_30.2: type = converted %.loc13_30.1, constants.%empty_tuple.type [concrete = constants.%empty_tuple.type] // CHECK:STDOUT: %struct_type.b: type = struct_type {.b: %empty_tuple.type} [concrete = constants.%struct_type.b] // CHECK:STDOUT: } // CHECK:STDOUT: %n: %struct_type.b = value_binding n, %n.param // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: interface @NonInstance2 { // CHECK:STDOUT: %Self: %NonInstance2.type = symbolic_binding Self, 0 [symbolic = constants.%Self.e6c] // CHECK:STDOUT: %NonInstance2.WithSelf.decl = interface_with_self_decl @NonInstance2 [concrete] // CHECK:STDOUT: // CHECK:STDOUT: !with Self: // CHECK:STDOUT: %NonInstance2.WithSelf.F2.decl: @NonInstance2.WithSelf.%NonInstance2.WithSelf.F2.type (%NonInstance2.WithSelf.F2.type.824) = fn_decl @NonInstance2.WithSelf.F2 [symbolic = @NonInstance2.WithSelf.%NonInstance2.WithSelf.F2 (constants.%NonInstance2.WithSelf.F2.9da)] {} {} // CHECK:STDOUT: %assoc0: %NonInstance2.assoc_type = assoc_entity element0, %NonInstance2.WithSelf.F2.decl [concrete = constants.%assoc0.56f] // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = %Self // CHECK:STDOUT: .F2 = @NonInstance2.WithSelf.%assoc0 // CHECK:STDOUT: witness = (@NonInstance2.WithSelf.%NonInstance2.WithSelf.F2.decl) // CHECK:STDOUT: // CHECK:STDOUT: !requires: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic interface @ImplicitAs(imports.%Core.import_ref.b3bc94.3: type) [from "core.carbon"] { // CHECK:STDOUT: %Dest: type = symbolic_binding Dest, 0 [symbolic = %Dest (constants.%Dest)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %ImplicitAs.type: type = facet_type <@ImplicitAs, @ImplicitAs(%Dest)> [symbolic = %ImplicitAs.type (constants.%ImplicitAs.type.031)] // CHECK:STDOUT: %Self: @ImplicitAs.%ImplicitAs.type (%ImplicitAs.type.031) = symbolic_binding Self, 1 [symbolic = %Self (constants.%Self.738)] // CHECK:STDOUT: // CHECK:STDOUT: interface { // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = imports.%Core.import_ref.9ec // CHECK:STDOUT: .Convert = imports.%Core.import_ref.178 // CHECK:STDOUT: witness = (imports.%Core.Convert) // CHECK:STDOUT: // CHECK:STDOUT: !requires: // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: impl @struct_type.b.as.NonInstance2.impl: %struct_type.b as %NonInstance2.ref { // CHECK:STDOUT: %struct_type.b.as.NonInstance2.impl.F2.decl: %struct_type.b.as.NonInstance2.impl.F2.type = fn_decl @struct_type.b.as.NonInstance2.impl.F2 [concrete = constants.%struct_type.b.as.NonInstance2.impl.F2] {} {} // CHECK:STDOUT: %NonInstance2.impl_witness_table = impl_witness_table (%struct_type.b.as.NonInstance2.impl.F2.decl), @struct_type.b.as.NonInstance2.impl [concrete] // CHECK:STDOUT: %NonInstance2.impl_witness: = impl_witness %NonInstance2.impl_witness_table [concrete = constants.%NonInstance2.impl_witness] // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .F2 = %struct_type.b.as.NonInstance2.impl.F2.decl // CHECK:STDOUT: witness = %NonInstance2.impl_witness // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic fn @NonInstance2.WithSelf.F2(@NonInstance2.%Self: %NonInstance2.type) { // CHECK:STDOUT: fn(); // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @struct_type.b.as.NonInstance2.impl.F2() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @NonInstanceCall2(%n.param: %struct_type.b) { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %n.ref: %struct_type.b = name_ref n, %n // CHECK:STDOUT: %NonInstance2.ref: type = name_ref NonInstance2, file.%NonInstance2.decl [concrete = constants.%NonInstance2.type] // CHECK:STDOUT: %F2.ref: %NonInstance2.assoc_type = name_ref F2, @NonInstance2.WithSelf.%assoc0 [concrete = constants.%assoc0.56f] // CHECK:STDOUT: %.loc21: %NonInstance2.type = converted %n.ref, [concrete = ] // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic fn @ImplicitAs.WithSelf.Convert(imports.%Core.import_ref.b3bc94.2: type, imports.%Core.import_ref.ac4dc5.2: @ImplicitAs.%ImplicitAs.type (%ImplicitAs.type.031)) [from "core.carbon"] { // CHECK:STDOUT: %Dest: type = symbolic_binding Dest, 0 [symbolic = %Dest (constants.%Dest)] // CHECK:STDOUT: %ImplicitAs.type: type = facet_type <@ImplicitAs, @ImplicitAs(%Dest)> [symbolic = %ImplicitAs.type (constants.%ImplicitAs.type.031)] // CHECK:STDOUT: %Self: @ImplicitAs.WithSelf.Convert.%ImplicitAs.type (%ImplicitAs.type.031) = symbolic_binding Self, 1 [symbolic = %Self (constants.%Self.738)] // CHECK:STDOUT: %Self.binding.as_type: type = symbolic_binding_type Self, 1, %Self [symbolic = %Self.binding.as_type (constants.%Self.binding.as_type)] // CHECK:STDOUT: %pattern_type.1: type = pattern_type %Self.binding.as_type [symbolic = %pattern_type.1 (constants.%pattern_type.e9a)] // CHECK:STDOUT: %.1: Core.Form = init_form %Dest [symbolic = %.1 (constants.%.184)] // CHECK:STDOUT: %pattern_type.2: type = pattern_type %Dest [symbolic = %pattern_type.2 (constants.%pattern_type.51d)] // CHECK:STDOUT: // CHECK:STDOUT: fn; // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @NonInstance2.WithSelf(constants.%Self.e6c) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: %Self => constants.%Self.e6c // CHECK:STDOUT: %NonInstance2.WithSelf.F2.type => constants.%NonInstance2.WithSelf.F2.type.824 // CHECK:STDOUT: %NonInstance2.WithSelf.F2 => constants.%NonInstance2.WithSelf.F2.9da // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @NonInstance2.WithSelf.F2(constants.%Self.e6c) {} // CHECK:STDOUT: // CHECK:STDOUT: specific @NonInstance2.WithSelf(constants.%NonInstance2.facet) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: %Self => constants.%NonInstance2.facet // CHECK:STDOUT: %NonInstance2.WithSelf.F2.type => constants.%NonInstance2.WithSelf.F2.type.041 // CHECK:STDOUT: %NonInstance2.WithSelf.F2 => constants.%NonInstance2.WithSelf.F2.fc4 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @NonInstance2.WithSelf.F2(constants.%NonInstance2.facet) {} // CHECK:STDOUT: // CHECK:STDOUT: specific @ImplicitAs(constants.%Dest) { // CHECK:STDOUT: %Dest => constants.%Dest // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @ImplicitAs.WithSelf(constants.%Dest, constants.%Self.738) {} // CHECK:STDOUT: // CHECK:STDOUT: specific @ImplicitAs.WithSelf.Convert(constants.%Dest, constants.%Self.738) { // CHECK:STDOUT: %Dest => constants.%Dest // CHECK:STDOUT: %ImplicitAs.type => constants.%ImplicitAs.type.031 // CHECK:STDOUT: %Self => constants.%Self.738 // CHECK:STDOUT: %Self.binding.as_type => constants.%Self.binding.as_type // CHECK:STDOUT: %pattern_type.1 => constants.%pattern_type.e9a // CHECK:STDOUT: %.1 => constants.%.184 // CHECK:STDOUT: %pattern_type.2 => constants.%pattern_type.51d // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @ImplicitAs(constants.%NonInstance2.type) { // CHECK:STDOUT: %Dest => constants.%NonInstance2.type // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %ImplicitAs.type => constants.%ImplicitAs.type.282 // CHECK:STDOUT: %Self => constants.%Self.df1 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @ImplicitAs.WithSelf(constants.%NonInstance2.type, constants.%Self.738) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: %Dest => constants.%NonInstance2.type // CHECK:STDOUT: %ImplicitAs.type => constants.%ImplicitAs.type.282 // CHECK:STDOUT: %Self => constants.%Self.738 // CHECK:STDOUT: %ImplicitAs.WithSelf.Convert.type => constants.%ImplicitAs.WithSelf.Convert.type.03b // CHECK:STDOUT: %ImplicitAs.WithSelf.Convert => constants.%ImplicitAs.WithSelf.Convert.4fe // CHECK:STDOUT: %ImplicitAs.assoc_type => constants.%ImplicitAs.assoc_type.cd6 // CHECK:STDOUT: %assoc0 => constants.%assoc0.af4 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- fail_non-instance_indirect.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %NonInstance3.type: type = facet_type <@NonInstance3> [concrete] // CHECK:STDOUT: %Self.0da: %NonInstance3.type = symbolic_binding Self, 0 [symbolic] // CHECK:STDOUT: %NonInstance3.WithSelf.F3.type.c21: type = fn_type @NonInstance3.WithSelf.F3, @NonInstance3.WithSelf(%Self.0da) [symbolic] // CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] // CHECK:STDOUT: %NonInstance3.WithSelf.F3.b62: %NonInstance3.WithSelf.F3.type.c21 = struct_value () [symbolic] // CHECK:STDOUT: %NonInstance3.assoc_type: type = assoc_entity_type @NonInstance3 [concrete] // CHECK:STDOUT: %assoc0.342: %NonInstance3.assoc_type = assoc_entity element0, @NonInstance3.WithSelf.%NonInstance3.WithSelf.F3.decl [concrete] // CHECK:STDOUT: %empty_tuple: %empty_tuple.type = tuple_value () [concrete] // CHECK:STDOUT: %struct_type.c: type = struct_type {.c: %empty_tuple.type} [concrete] // CHECK:STDOUT: %NonInstance3.impl_witness: = impl_witness @struct_type.c.as.NonInstance3.impl.%NonInstance3.impl_witness_table [concrete] // CHECK:STDOUT: %struct_type.c.as.NonInstance3.impl.F3.type: type = fn_type @struct_type.c.as.NonInstance3.impl.F3 [concrete] // CHECK:STDOUT: %struct_type.c.as.NonInstance3.impl.F3: %struct_type.c.as.NonInstance3.impl.F3.type = struct_value () [concrete] // CHECK:STDOUT: %NonInstance3.facet: %NonInstance3.type = facet_value %struct_type.c, (%NonInstance3.impl_witness) [concrete] // CHECK:STDOUT: %NonInstance3.WithSelf.F3.type.a3f: type = fn_type @NonInstance3.WithSelf.F3, @NonInstance3.WithSelf(%NonInstance3.facet) [concrete] // CHECK:STDOUT: %NonInstance3.WithSelf.F3.04b: %NonInstance3.WithSelf.F3.type.a3f = struct_value () [concrete] // CHECK:STDOUT: %ptr: type = ptr_type %struct_type.c [concrete] // CHECK:STDOUT: %pattern_type.5f5: type = pattern_type %ptr [concrete] // CHECK:STDOUT: %NonInstanceCallIndirect.type: type = fn_type @NonInstanceCallIndirect [concrete] // CHECK:STDOUT: %NonInstanceCallIndirect: %NonInstanceCallIndirect.type = struct_value () [concrete] // CHECK:STDOUT: %ImplicitAs.type.cc7: type = generic_interface_type @ImplicitAs [concrete] // CHECK:STDOUT: %ImplicitAs.generic: %ImplicitAs.type.cc7 = struct_value () [concrete] // CHECK:STDOUT: %Dest: type = symbolic_binding Dest, 0 [symbolic] // CHECK:STDOUT: %ImplicitAs.type.031: type = facet_type <@ImplicitAs, @ImplicitAs(%Dest)> [symbolic] // CHECK:STDOUT: %Self.738: %ImplicitAs.type.031 = symbolic_binding Self, 1 [symbolic] // CHECK:STDOUT: %ImplicitAs.assoc_type.ff3: type = assoc_entity_type @ImplicitAs, @ImplicitAs(%Dest) [symbolic] // CHECK:STDOUT: %ImplicitAs.WithSelf.Convert.type.b3a: type = fn_type @ImplicitAs.WithSelf.Convert, @ImplicitAs.WithSelf(%Dest, %Self.738) [symbolic] // CHECK:STDOUT: %ImplicitAs.WithSelf.Convert.1de: %ImplicitAs.WithSelf.Convert.type.b3a = struct_value () [symbolic] // CHECK:STDOUT: %pattern_type.51d: type = pattern_type %Dest [symbolic] // CHECK:STDOUT: %Self.binding.as_type: type = symbolic_binding_type Self, 1, %Self.738 [symbolic] // CHECK:STDOUT: %pattern_type.e9a: type = pattern_type %Self.binding.as_type [symbolic] // CHECK:STDOUT: %.184: Core.Form = init_form %Dest [symbolic] // CHECK:STDOUT: %ImplicitAs.type.deb: type = facet_type <@ImplicitAs, @ImplicitAs(%NonInstance3.type)> [concrete] // CHECK:STDOUT: %Self.9cf: %ImplicitAs.type.deb = symbolic_binding Self, 1 [symbolic] // CHECK:STDOUT: %ImplicitAs.WithSelf.Convert.type.8ec: type = fn_type @ImplicitAs.WithSelf.Convert, @ImplicitAs.WithSelf(%NonInstance3.type, %Self.738) [symbolic] // CHECK:STDOUT: %ImplicitAs.WithSelf.Convert.0e4: %ImplicitAs.WithSelf.Convert.type.8ec = struct_value () [symbolic] // CHECK:STDOUT: %ImplicitAs.assoc_type.eea: type = assoc_entity_type @ImplicitAs, @ImplicitAs(%NonInstance3.type) [concrete] // CHECK:STDOUT: %assoc0.605: %ImplicitAs.assoc_type.eea = assoc_entity element0, imports.%Core.import_ref.201 [concrete] // CHECK:STDOUT: %assoc0.843: %ImplicitAs.assoc_type.ff3 = assoc_entity element0, imports.%Core.import_ref.cc1 [symbolic] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: .ImplicitAs = %Core.ImplicitAs // CHECK:STDOUT: import Core//default // CHECK:STDOUT: } // CHECK:STDOUT: %Core.ImplicitAs: %ImplicitAs.type.cc7 = import_ref Core//default, ImplicitAs, loaded [concrete = constants.%ImplicitAs.generic] // CHECK:STDOUT: %Core.import_ref.178: @ImplicitAs.WithSelf.%ImplicitAs.assoc_type (%ImplicitAs.assoc_type.ff3) = import_ref Core//default, loc{{\d+_\d+}}, loaded [symbolic = @ImplicitAs.WithSelf.%assoc0 (constants.%assoc0.843)] // CHECK:STDOUT: %Core.Convert = import_ref Core//default, Convert, unloaded // CHECK:STDOUT: %Core.import_ref.201: @ImplicitAs.WithSelf.%ImplicitAs.WithSelf.Convert.type (%ImplicitAs.WithSelf.Convert.type.b3a) = import_ref Core//default, loc{{\d+_\d+}}, loaded [symbolic = @ImplicitAs.WithSelf.%ImplicitAs.WithSelf.Convert (constants.%ImplicitAs.WithSelf.Convert.1de)] // CHECK:STDOUT: %Core.import_ref.b3bc94.2: type = import_ref Core//default, loc{{\d+_\d+}}, loaded [symbolic = @ImplicitAs.%Dest (constants.%Dest)] // CHECK:STDOUT: %Core.import_ref.ac4dc5.2: @ImplicitAs.%ImplicitAs.type (%ImplicitAs.type.031) = import_ref Core//default, loc{{\d+_\d+}}, loaded [symbolic = @ImplicitAs.%Self (constants.%Self.738)] // CHECK:STDOUT: %Core.import_ref.9ec = import_ref Core//default, loc{{\d+_\d+}}, unloaded // CHECK:STDOUT: %Core.import_ref.b3bc94.3: type = import_ref Core//default, loc{{\d+_\d+}}, loaded [symbolic = @ImplicitAs.%Dest (constants.%Dest)] // CHECK:STDOUT: %Core.import_ref.cc1 = import_ref Core//default, loc{{\d+_\d+}}, unloaded // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .NonInstance3 = %NonInstance3.decl // CHECK:STDOUT: .NonInstanceCallIndirect = %NonInstanceCallIndirect.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %NonInstance3.decl: type = interface_decl @NonInstance3 [concrete = constants.%NonInstance3.type] {} {} // CHECK:STDOUT: impl_decl @struct_type.c.as.NonInstance3.impl [concrete] {} { // CHECK:STDOUT: %.loc9_12.1: %empty_tuple.type = tuple_literal () [concrete = constants.%empty_tuple] // CHECK:STDOUT: %.loc9_12.2: type = converted %.loc9_12.1, constants.%empty_tuple.type [concrete = constants.%empty_tuple.type] // CHECK:STDOUT: %struct_type.c: type = struct_type {.c: %empty_tuple.type} [concrete = constants.%struct_type.c] // CHECK:STDOUT: %NonInstance3.ref: type = name_ref NonInstance3, file.%NonInstance3.decl [concrete = constants.%NonInstance3.type] // CHECK:STDOUT: } // CHECK:STDOUT: %NonInstanceCallIndirect.decl: %NonInstanceCallIndirect.type = fn_decl @NonInstanceCallIndirect [concrete = constants.%NonInstanceCallIndirect] { // CHECK:STDOUT: %p.patt: %pattern_type.5f5 = value_binding_pattern p [concrete] // CHECK:STDOUT: %p.param_patt: %pattern_type.5f5 = value_param_pattern %p.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %p.param: %ptr = value_param call_param0 // CHECK:STDOUT: %.loc13_39: type = splice_block %ptr [concrete = constants.%ptr] { // CHECK:STDOUT: %.loc13_37.1: %empty_tuple.type = tuple_literal () [concrete = constants.%empty_tuple] // CHECK:STDOUT: %.loc13_37.2: type = converted %.loc13_37.1, constants.%empty_tuple.type [concrete = constants.%empty_tuple.type] // CHECK:STDOUT: %struct_type.c: type = struct_type {.c: %empty_tuple.type} [concrete = constants.%struct_type.c] // CHECK:STDOUT: %ptr: type = ptr_type %struct_type.c [concrete = constants.%ptr] // CHECK:STDOUT: } // CHECK:STDOUT: %p: %ptr = value_binding p, %p.param // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: interface @NonInstance3 { // CHECK:STDOUT: %Self: %NonInstance3.type = symbolic_binding Self, 0 [symbolic = constants.%Self.0da] // CHECK:STDOUT: %NonInstance3.WithSelf.decl = interface_with_self_decl @NonInstance3 [concrete] // CHECK:STDOUT: // CHECK:STDOUT: !with Self: // CHECK:STDOUT: %NonInstance3.WithSelf.F3.decl: @NonInstance3.WithSelf.%NonInstance3.WithSelf.F3.type (%NonInstance3.WithSelf.F3.type.c21) = fn_decl @NonInstance3.WithSelf.F3 [symbolic = @NonInstance3.WithSelf.%NonInstance3.WithSelf.F3 (constants.%NonInstance3.WithSelf.F3.b62)] {} {} // CHECK:STDOUT: %assoc0: %NonInstance3.assoc_type = assoc_entity element0, %NonInstance3.WithSelf.F3.decl [concrete = constants.%assoc0.342] // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = %Self // CHECK:STDOUT: .F3 = @NonInstance3.WithSelf.%assoc0 // CHECK:STDOUT: witness = (@NonInstance3.WithSelf.%NonInstance3.WithSelf.F3.decl) // CHECK:STDOUT: // CHECK:STDOUT: !requires: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic interface @ImplicitAs(imports.%Core.import_ref.b3bc94.3: type) [from "core.carbon"] { // CHECK:STDOUT: %Dest: type = symbolic_binding Dest, 0 [symbolic = %Dest (constants.%Dest)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %ImplicitAs.type: type = facet_type <@ImplicitAs, @ImplicitAs(%Dest)> [symbolic = %ImplicitAs.type (constants.%ImplicitAs.type.031)] // CHECK:STDOUT: %Self: @ImplicitAs.%ImplicitAs.type (%ImplicitAs.type.031) = symbolic_binding Self, 1 [symbolic = %Self (constants.%Self.738)] // CHECK:STDOUT: // CHECK:STDOUT: interface { // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = imports.%Core.import_ref.9ec // CHECK:STDOUT: .Convert = imports.%Core.import_ref.178 // CHECK:STDOUT: witness = (imports.%Core.Convert) // CHECK:STDOUT: // CHECK:STDOUT: !requires: // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: impl @struct_type.c.as.NonInstance3.impl: %struct_type.c as %NonInstance3.ref { // CHECK:STDOUT: %struct_type.c.as.NonInstance3.impl.F3.decl: %struct_type.c.as.NonInstance3.impl.F3.type = fn_decl @struct_type.c.as.NonInstance3.impl.F3 [concrete = constants.%struct_type.c.as.NonInstance3.impl.F3] {} {} // CHECK:STDOUT: %NonInstance3.impl_witness_table = impl_witness_table (%struct_type.c.as.NonInstance3.impl.F3.decl), @struct_type.c.as.NonInstance3.impl [concrete] // CHECK:STDOUT: %NonInstance3.impl_witness: = impl_witness %NonInstance3.impl_witness_table [concrete = constants.%NonInstance3.impl_witness] // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .F3 = %struct_type.c.as.NonInstance3.impl.F3.decl // CHECK:STDOUT: witness = %NonInstance3.impl_witness // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic fn @NonInstance3.WithSelf.F3(@NonInstance3.%Self: %NonInstance3.type) { // CHECK:STDOUT: fn(); // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @struct_type.c.as.NonInstance3.impl.F3() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @NonInstanceCallIndirect(%p.param: %ptr) { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %p.ref: %ptr = name_ref p, %p // CHECK:STDOUT: %NonInstance3.ref: type = name_ref NonInstance3, file.%NonInstance3.decl [concrete = constants.%NonInstance3.type] // CHECK:STDOUT: %F3.ref: %NonInstance3.assoc_type = name_ref F3, @NonInstance3.WithSelf.%assoc0 [concrete = constants.%assoc0.342] // CHECK:STDOUT: %.loc21_4.1: ref %struct_type.c = deref %p.ref // CHECK:STDOUT: %.loc21_4.2: %NonInstance3.type = converted %.loc21_4.1, [concrete = ] // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic fn @ImplicitAs.WithSelf.Convert(imports.%Core.import_ref.b3bc94.2: type, imports.%Core.import_ref.ac4dc5.2: @ImplicitAs.%ImplicitAs.type (%ImplicitAs.type.031)) [from "core.carbon"] { // CHECK:STDOUT: %Dest: type = symbolic_binding Dest, 0 [symbolic = %Dest (constants.%Dest)] // CHECK:STDOUT: %ImplicitAs.type: type = facet_type <@ImplicitAs, @ImplicitAs(%Dest)> [symbolic = %ImplicitAs.type (constants.%ImplicitAs.type.031)] // CHECK:STDOUT: %Self: @ImplicitAs.WithSelf.Convert.%ImplicitAs.type (%ImplicitAs.type.031) = symbolic_binding Self, 1 [symbolic = %Self (constants.%Self.738)] // CHECK:STDOUT: %Self.binding.as_type: type = symbolic_binding_type Self, 1, %Self [symbolic = %Self.binding.as_type (constants.%Self.binding.as_type)] // CHECK:STDOUT: %pattern_type.1: type = pattern_type %Self.binding.as_type [symbolic = %pattern_type.1 (constants.%pattern_type.e9a)] // CHECK:STDOUT: %.1: Core.Form = init_form %Dest [symbolic = %.1 (constants.%.184)] // CHECK:STDOUT: %pattern_type.2: type = pattern_type %Dest [symbolic = %pattern_type.2 (constants.%pattern_type.51d)] // CHECK:STDOUT: // CHECK:STDOUT: fn; // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @NonInstance3.WithSelf(constants.%Self.0da) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: %Self => constants.%Self.0da // CHECK:STDOUT: %NonInstance3.WithSelf.F3.type => constants.%NonInstance3.WithSelf.F3.type.c21 // CHECK:STDOUT: %NonInstance3.WithSelf.F3 => constants.%NonInstance3.WithSelf.F3.b62 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @NonInstance3.WithSelf.F3(constants.%Self.0da) {} // CHECK:STDOUT: // CHECK:STDOUT: specific @NonInstance3.WithSelf(constants.%NonInstance3.facet) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: %Self => constants.%NonInstance3.facet // CHECK:STDOUT: %NonInstance3.WithSelf.F3.type => constants.%NonInstance3.WithSelf.F3.type.a3f // CHECK:STDOUT: %NonInstance3.WithSelf.F3 => constants.%NonInstance3.WithSelf.F3.04b // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @NonInstance3.WithSelf.F3(constants.%NonInstance3.facet) {} // CHECK:STDOUT: // CHECK:STDOUT: specific @ImplicitAs(constants.%Dest) { // CHECK:STDOUT: %Dest => constants.%Dest // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @ImplicitAs.WithSelf(constants.%Dest, constants.%Self.738) {} // CHECK:STDOUT: // CHECK:STDOUT: specific @ImplicitAs.WithSelf.Convert(constants.%Dest, constants.%Self.738) { // CHECK:STDOUT: %Dest => constants.%Dest // CHECK:STDOUT: %ImplicitAs.type => constants.%ImplicitAs.type.031 // CHECK:STDOUT: %Self => constants.%Self.738 // CHECK:STDOUT: %Self.binding.as_type => constants.%Self.binding.as_type // CHECK:STDOUT: %pattern_type.1 => constants.%pattern_type.e9a // CHECK:STDOUT: %.1 => constants.%.184 // CHECK:STDOUT: %pattern_type.2 => constants.%pattern_type.51d // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @ImplicitAs(constants.%NonInstance3.type) { // CHECK:STDOUT: %Dest => constants.%NonInstance3.type // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %ImplicitAs.type => constants.%ImplicitAs.type.deb // CHECK:STDOUT: %Self => constants.%Self.9cf // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @ImplicitAs.WithSelf(constants.%NonInstance3.type, constants.%Self.738) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: %Dest => constants.%NonInstance3.type // CHECK:STDOUT: %ImplicitAs.type => constants.%ImplicitAs.type.deb // CHECK:STDOUT: %Self => constants.%Self.738 // CHECK:STDOUT: %ImplicitAs.WithSelf.Convert.type => constants.%ImplicitAs.WithSelf.Convert.type.8ec // CHECK:STDOUT: %ImplicitAs.WithSelf.Convert => constants.%ImplicitAs.WithSelf.Convert.0e4 // CHECK:STDOUT: %ImplicitAs.assoc_type => constants.%ImplicitAs.assoc_type.eea // CHECK:STDOUT: %assoc0 => constants.%assoc0.605 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- instance_success.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %Instance1.type: type = facet_type <@Instance1> [concrete] // CHECK:STDOUT: %Self: %Instance1.type = symbolic_binding Self, 0 [symbolic] // CHECK:STDOUT: %Self.binding.as_type: type = symbolic_binding_type Self, 0, %Self [symbolic] // CHECK:STDOUT: %pattern_type.97b: type = pattern_type %Self.binding.as_type [symbolic] // CHECK:STDOUT: %Instance1.WithSelf.G1.type.291: type = fn_type @Instance1.WithSelf.G1, @Instance1.WithSelf(%Self) [symbolic] // CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] // CHECK:STDOUT: %Instance1.WithSelf.G1.db7: %Instance1.WithSelf.G1.type.291 = struct_value () [symbolic] // CHECK:STDOUT: %Instance1.assoc_type: type = assoc_entity_type @Instance1 [concrete] // CHECK:STDOUT: %assoc0: %Instance1.assoc_type = assoc_entity element0, @Instance1.WithSelf.%Instance1.WithSelf.G1.decl [concrete] // CHECK:STDOUT: %empty_tuple: %empty_tuple.type = tuple_value () [concrete] // CHECK:STDOUT: %struct_type.d: type = struct_type {.d: %empty_tuple.type} [concrete] // CHECK:STDOUT: %Instance1.impl_witness: = impl_witness @struct_type.d.as.Instance1.impl.%Instance1.impl_witness_table [concrete] // CHECK:STDOUT: %pattern_type.515: type = pattern_type %struct_type.d [concrete] // CHECK:STDOUT: %struct_type.d.as.Instance1.impl.G1.type: type = fn_type @struct_type.d.as.Instance1.impl.G1 [concrete] // CHECK:STDOUT: %struct_type.d.as.Instance1.impl.G1: %struct_type.d.as.Instance1.impl.G1.type = struct_value () [concrete] // CHECK:STDOUT: %Instance1.facet: %Instance1.type = facet_value %struct_type.d, (%Instance1.impl_witness) [concrete] // CHECK:STDOUT: %Instance1.WithSelf.G1.type.ba6: type = fn_type @Instance1.WithSelf.G1, @Instance1.WithSelf(%Instance1.facet) [concrete] // CHECK:STDOUT: %Instance1.WithSelf.G1.ceb: %Instance1.WithSelf.G1.type.ba6 = struct_value () [concrete] // CHECK:STDOUT: %InstanceCall.type: type = fn_type @InstanceCall [concrete] // CHECK:STDOUT: %InstanceCall: %InstanceCall.type = struct_value () [concrete] // CHECK:STDOUT: %.276: type = fn_type_with_self_type %Instance1.WithSelf.G1.type.ba6, %Instance1.facet [concrete] // CHECK:STDOUT: %ptr: type = ptr_type %struct_type.d [concrete] // CHECK:STDOUT: %pattern_type.8d6: type = pattern_type %ptr [concrete] // CHECK:STDOUT: %InstanceCallIndirect.type: type = fn_type @InstanceCallIndirect [concrete] // CHECK:STDOUT: %InstanceCallIndirect: %InstanceCallIndirect.type = struct_value () [concrete] // CHECK:STDOUT: %struct: %struct_type.d = struct_value (%empty_tuple) [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Instance1 = %Instance1.decl // CHECK:STDOUT: .InstanceCall = %InstanceCall.decl // CHECK:STDOUT: .InstanceCallIndirect = %InstanceCallIndirect.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Instance1.decl: type = interface_decl @Instance1 [concrete = constants.%Instance1.type] {} {} // CHECK:STDOUT: impl_decl @struct_type.d.as.Instance1.impl [concrete] {} { // CHECK:STDOUT: %.loc7_12.1: %empty_tuple.type = tuple_literal () [concrete = constants.%empty_tuple] // CHECK:STDOUT: %.loc7_12.2: type = converted %.loc7_12.1, constants.%empty_tuple.type [concrete = constants.%empty_tuple.type] // CHECK:STDOUT: %struct_type.d: type = struct_type {.d: %empty_tuple.type} [concrete = constants.%struct_type.d] // CHECK:STDOUT: %Instance1.ref: type = name_ref Instance1, file.%Instance1.decl [concrete = constants.%Instance1.type] // CHECK:STDOUT: } // CHECK:STDOUT: %InstanceCall.decl: %InstanceCall.type = fn_decl @InstanceCall [concrete = constants.%InstanceCall] { // CHECK:STDOUT: %n.patt: %pattern_type.515 = value_binding_pattern n [concrete] // CHECK:STDOUT: %n.param_patt: %pattern_type.515 = value_param_pattern %n.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %n.param: %struct_type.d = value_param call_param0 // CHECK:STDOUT: %.loc11_27: type = splice_block %struct_type.d [concrete = constants.%struct_type.d] { // CHECK:STDOUT: %.loc11_26.1: %empty_tuple.type = tuple_literal () [concrete = constants.%empty_tuple] // CHECK:STDOUT: %.loc11_26.2: type = converted %.loc11_26.1, constants.%empty_tuple.type [concrete = constants.%empty_tuple.type] // CHECK:STDOUT: %struct_type.d: type = struct_type {.d: %empty_tuple.type} [concrete = constants.%struct_type.d] // CHECK:STDOUT: } // CHECK:STDOUT: %n: %struct_type.d = value_binding n, %n.param // CHECK:STDOUT: } // CHECK:STDOUT: %InstanceCallIndirect.decl: %InstanceCallIndirect.type = fn_decl @InstanceCallIndirect [concrete = constants.%InstanceCallIndirect] { // CHECK:STDOUT: %p.patt: %pattern_type.8d6 = value_binding_pattern p [concrete] // CHECK:STDOUT: %p.param_patt: %pattern_type.8d6 = value_param_pattern %p.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %p.param: %ptr = value_param call_param0 // CHECK:STDOUT: %.loc15_36: type = splice_block %ptr [concrete = constants.%ptr] { // CHECK:STDOUT: %.loc15_34.1: %empty_tuple.type = tuple_literal () [concrete = constants.%empty_tuple] // CHECK:STDOUT: %.loc15_34.2: type = converted %.loc15_34.1, constants.%empty_tuple.type [concrete = constants.%empty_tuple.type] // CHECK:STDOUT: %struct_type.d: type = struct_type {.d: %empty_tuple.type} [concrete = constants.%struct_type.d] // CHECK:STDOUT: %ptr: type = ptr_type %struct_type.d [concrete = constants.%ptr] // CHECK:STDOUT: } // CHECK:STDOUT: %p: %ptr = value_binding p, %p.param // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: interface @Instance1 { // CHECK:STDOUT: %Self: %Instance1.type = symbolic_binding Self, 0 [symbolic = constants.%Self] // CHECK:STDOUT: %Instance1.WithSelf.decl = interface_with_self_decl @Instance1 [concrete] // CHECK:STDOUT: // CHECK:STDOUT: !with Self: // CHECK:STDOUT: %Instance1.WithSelf.G1.decl: @Instance1.WithSelf.%Instance1.WithSelf.G1.type (%Instance1.WithSelf.G1.type.291) = fn_decl @Instance1.WithSelf.G1 [symbolic = @Instance1.WithSelf.%Instance1.WithSelf.G1 (constants.%Instance1.WithSelf.G1.db7)] { // CHECK:STDOUT: %self.patt: @Instance1.WithSelf.G1.%pattern_type (%pattern_type.97b) = value_binding_pattern self [concrete] // CHECK:STDOUT: %self.param_patt: @Instance1.WithSelf.G1.%pattern_type (%pattern_type.97b) = value_param_pattern %self.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %self.param: @Instance1.WithSelf.G1.%Self.binding.as_type (%Self.binding.as_type) = value_param call_param0 // CHECK:STDOUT: %.loc4_15.1: type = splice_block %.loc4_15.2 [symbolic = %Self.binding.as_type (constants.%Self.binding.as_type)] { // CHECK:STDOUT: %Self.ref: %Instance1.type = name_ref Self, @Instance1.%Self [symbolic = %Self (constants.%Self)] // CHECK:STDOUT: %Self.as_type: type = facet_access_type %Self.ref [symbolic = %Self.binding.as_type (constants.%Self.binding.as_type)] // CHECK:STDOUT: %.loc4_15.2: type = converted %Self.ref, %Self.as_type [symbolic = %Self.binding.as_type (constants.%Self.binding.as_type)] // CHECK:STDOUT: } // CHECK:STDOUT: %self: @Instance1.WithSelf.G1.%Self.binding.as_type (%Self.binding.as_type) = value_binding self, %self.param // CHECK:STDOUT: } // CHECK:STDOUT: %assoc0: %Instance1.assoc_type = assoc_entity element0, %Instance1.WithSelf.G1.decl [concrete = constants.%assoc0] // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = %Self // CHECK:STDOUT: .G1 = @Instance1.WithSelf.%assoc0 // CHECK:STDOUT: witness = (@Instance1.WithSelf.%Instance1.WithSelf.G1.decl) // CHECK:STDOUT: // CHECK:STDOUT: !requires: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: impl @struct_type.d.as.Instance1.impl: %struct_type.d as %Instance1.ref { // CHECK:STDOUT: %struct_type.d.as.Instance1.impl.G1.decl: %struct_type.d.as.Instance1.impl.G1.type = fn_decl @struct_type.d.as.Instance1.impl.G1 [concrete = constants.%struct_type.d.as.Instance1.impl.G1] { // CHECK:STDOUT: %self.patt: %pattern_type.515 = value_binding_pattern self [concrete] // CHECK:STDOUT: %self.param_patt: %pattern_type.515 = value_param_pattern %self.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %self.param: %struct_type.d = value_param call_param0 // CHECK:STDOUT: %Self.ref: type = name_ref Self, @struct_type.d.as.Instance1.impl.%struct_type.d [concrete = constants.%struct_type.d] // CHECK:STDOUT: %self: %struct_type.d = value_binding self, %self.param // CHECK:STDOUT: } // CHECK:STDOUT: %Instance1.impl_witness_table = impl_witness_table (%struct_type.d.as.Instance1.impl.G1.decl), @struct_type.d.as.Instance1.impl [concrete] // CHECK:STDOUT: %Instance1.impl_witness: = impl_witness %Instance1.impl_witness_table [concrete = constants.%Instance1.impl_witness] // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .G1 = %struct_type.d.as.Instance1.impl.G1.decl // CHECK:STDOUT: witness = %Instance1.impl_witness // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic fn @Instance1.WithSelf.G1(@Instance1.%Self: %Instance1.type) { // CHECK:STDOUT: %Self: %Instance1.type = symbolic_binding Self, 0 [symbolic = %Self (constants.%Self)] // CHECK:STDOUT: %Self.binding.as_type: type = symbolic_binding_type Self, 0, %Self [symbolic = %Self.binding.as_type (constants.%Self.binding.as_type)] // CHECK:STDOUT: %pattern_type: type = pattern_type %Self.binding.as_type [symbolic = %pattern_type (constants.%pattern_type.97b)] // CHECK:STDOUT: // CHECK:STDOUT: fn(%self.param: @Instance1.WithSelf.G1.%Self.binding.as_type (%Self.binding.as_type)); // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @struct_type.d.as.Instance1.impl.G1(%self.param: %struct_type.d) { // CHECK:STDOUT: !entry: // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @InstanceCall(%n.param: %struct_type.d) { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %n.ref: %struct_type.d = name_ref n, %n // CHECK:STDOUT: %Instance1.ref: type = name_ref Instance1, file.%Instance1.decl [concrete = constants.%Instance1.type] // CHECK:STDOUT: %G1.ref: %Instance1.assoc_type = name_ref G1, @Instance1.WithSelf.%assoc0 [concrete = constants.%assoc0] // CHECK:STDOUT: %impl.elem0: %.276 = impl_witness_access constants.%Instance1.impl_witness, element0 [concrete = constants.%struct_type.d.as.Instance1.impl.G1] // CHECK:STDOUT: %bound_method: = bound_method %n.ref, %impl.elem0 // CHECK:STDOUT: %struct_type.d.as.Instance1.impl.G1.call: init %empty_tuple.type = call %bound_method(%n.ref) // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @InstanceCallIndirect(%p.param: %ptr) { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %p.ref: %ptr = name_ref p, %p // CHECK:STDOUT: %Instance1.ref: type = name_ref Instance1, file.%Instance1.decl [concrete = constants.%Instance1.type] // CHECK:STDOUT: %G1.ref: %Instance1.assoc_type = name_ref G1, @Instance1.WithSelf.%assoc0 [concrete = constants.%assoc0] // CHECK:STDOUT: %.loc16_4.1: ref %struct_type.d = deref %p.ref // CHECK:STDOUT: %impl.elem0: %.276 = impl_witness_access constants.%Instance1.impl_witness, element0 [concrete = constants.%struct_type.d.as.Instance1.impl.G1] // CHECK:STDOUT: %bound_method: = bound_method %.loc16_4.1, %impl.elem0 // CHECK:STDOUT: %.loc16_4.2: ref %empty_tuple.type = struct_access %.loc16_4.1, element0 // CHECK:STDOUT: %tuple: %empty_tuple.type = tuple_value () [concrete = constants.%empty_tuple] // CHECK:STDOUT: %.loc16_4.3: %empty_tuple.type = converted %.loc16_4.2, %tuple [concrete = constants.%empty_tuple] // CHECK:STDOUT: %struct: %struct_type.d = struct_value (%.loc16_4.3) [concrete = constants.%struct] // CHECK:STDOUT: %.loc16_4.4: %struct_type.d = converted %.loc16_4.1, %struct [concrete = constants.%struct] // CHECK:STDOUT: %struct_type.d.as.Instance1.impl.G1.call: init %empty_tuple.type = call %bound_method(%.loc16_4.4) // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @Instance1.WithSelf(constants.%Self) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: %Self => constants.%Self // CHECK:STDOUT: %Instance1.WithSelf.G1.type => constants.%Instance1.WithSelf.G1.type.291 // CHECK:STDOUT: %Instance1.WithSelf.G1 => constants.%Instance1.WithSelf.G1.db7 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @Instance1.WithSelf.G1(constants.%Self) { // CHECK:STDOUT: %Self => constants.%Self // CHECK:STDOUT: %Self.binding.as_type => constants.%Self.binding.as_type // CHECK:STDOUT: %pattern_type => constants.%pattern_type.97b // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @Instance1.WithSelf(constants.%Instance1.facet) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: %Self => constants.%Instance1.facet // CHECK:STDOUT: %Instance1.WithSelf.G1.type => constants.%Instance1.WithSelf.G1.type.ba6 // CHECK:STDOUT: %Instance1.WithSelf.G1 => constants.%Instance1.WithSelf.G1.ceb // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @Instance1.WithSelf.G1(constants.%Instance1.facet) { // CHECK:STDOUT: %Self => constants.%Instance1.facet // CHECK:STDOUT: %Self.binding.as_type => constants.%struct_type.d // CHECK:STDOUT: %pattern_type => constants.%pattern_type.515 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- fail_instance.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %Instance2.type: type = facet_type <@Instance2> [concrete] // CHECK:STDOUT: %Self: %Instance2.type = symbolic_binding Self, 0 [symbolic] // CHECK:STDOUT: %Self.binding.as_type: type = symbolic_binding_type Self, 0, %Self [symbolic] // CHECK:STDOUT: %pattern_type.a35: type = pattern_type %Self.binding.as_type [symbolic] // CHECK:STDOUT: %Instance2.WithSelf.G2.type.e15: type = fn_type @Instance2.WithSelf.G2, @Instance2.WithSelf(%Self) [symbolic] // CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] // CHECK:STDOUT: %Instance2.WithSelf.G2.6c8: %Instance2.WithSelf.G2.type.e15 = struct_value () [symbolic] // CHECK:STDOUT: %Instance2.assoc_type: type = assoc_entity_type @Instance2 [concrete] // CHECK:STDOUT: %assoc0: %Instance2.assoc_type = assoc_entity element0, @Instance2.WithSelf.%Instance2.WithSelf.G2.decl [concrete] // CHECK:STDOUT: %empty_tuple: %empty_tuple.type = tuple_value () [concrete] // CHECK:STDOUT: %struct_type.e: type = struct_type {.e: %empty_tuple.type} [concrete] // CHECK:STDOUT: %Instance2.impl_witness: = impl_witness @struct_type.e.as.Instance2.impl.%Instance2.impl_witness_table [concrete] // CHECK:STDOUT: %pattern_type.efd: type = pattern_type %struct_type.e [concrete] // CHECK:STDOUT: %struct_type.e.as.Instance2.impl.G2.type: type = fn_type @struct_type.e.as.Instance2.impl.G2 [concrete] // CHECK:STDOUT: %struct_type.e.as.Instance2.impl.G2: %struct_type.e.as.Instance2.impl.G2.type = struct_value () [concrete] // CHECK:STDOUT: %Instance2.facet: %Instance2.type = facet_value %struct_type.e, (%Instance2.impl_witness) [concrete] // CHECK:STDOUT: %Instance2.WithSelf.G2.type.008: type = fn_type @Instance2.WithSelf.G2, @Instance2.WithSelf(%Instance2.facet) [concrete] // CHECK:STDOUT: %Instance2.WithSelf.G2.f23: %Instance2.WithSelf.G2.type.008 = struct_value () [concrete] // CHECK:STDOUT: %InstanceCallFail.type: type = fn_type @InstanceCallFail [concrete] // CHECK:STDOUT: %InstanceCallFail: %InstanceCallFail.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Instance2 = %Instance2.decl // CHECK:STDOUT: .InstanceCallFail = %InstanceCallFail.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Instance2.decl: type = interface_decl @Instance2 [concrete = constants.%Instance2.type] {} {} // CHECK:STDOUT: impl_decl @struct_type.e.as.Instance2.impl [concrete] {} { // CHECK:STDOUT: %.loc7_12.1: %empty_tuple.type = tuple_literal () [concrete = constants.%empty_tuple] // CHECK:STDOUT: %.loc7_12.2: type = converted %.loc7_12.1, constants.%empty_tuple.type [concrete = constants.%empty_tuple.type] // CHECK:STDOUT: %struct_type.e: type = struct_type {.e: %empty_tuple.type} [concrete = constants.%struct_type.e] // CHECK:STDOUT: %Instance2.ref: type = name_ref Instance2, file.%Instance2.decl [concrete = constants.%Instance2.type] // CHECK:STDOUT: } // CHECK:STDOUT: %InstanceCallFail.decl: %InstanceCallFail.type = fn_decl @InstanceCallFail [concrete = constants.%InstanceCallFail] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: interface @Instance2 { // CHECK:STDOUT: %Self: %Instance2.type = symbolic_binding Self, 0 [symbolic = constants.%Self] // CHECK:STDOUT: %Instance2.WithSelf.decl = interface_with_self_decl @Instance2 [concrete] // CHECK:STDOUT: // CHECK:STDOUT: !with Self: // CHECK:STDOUT: %Instance2.WithSelf.G2.decl: @Instance2.WithSelf.%Instance2.WithSelf.G2.type (%Instance2.WithSelf.G2.type.e15) = fn_decl @Instance2.WithSelf.G2 [symbolic = @Instance2.WithSelf.%Instance2.WithSelf.G2 (constants.%Instance2.WithSelf.G2.6c8)] { // CHECK:STDOUT: %self.patt: @Instance2.WithSelf.G2.%pattern_type (%pattern_type.a35) = value_binding_pattern self [concrete] // CHECK:STDOUT: %self.param_patt: @Instance2.WithSelf.G2.%pattern_type (%pattern_type.a35) = value_param_pattern %self.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %self.param: @Instance2.WithSelf.G2.%Self.binding.as_type (%Self.binding.as_type) = value_param call_param0 // CHECK:STDOUT: %.loc4_15.1: type = splice_block %.loc4_15.2 [symbolic = %Self.binding.as_type (constants.%Self.binding.as_type)] { // CHECK:STDOUT: %Self.ref: %Instance2.type = name_ref Self, @Instance2.%Self [symbolic = %Self (constants.%Self)] // CHECK:STDOUT: %Self.as_type: type = facet_access_type %Self.ref [symbolic = %Self.binding.as_type (constants.%Self.binding.as_type)] // CHECK:STDOUT: %.loc4_15.2: type = converted %Self.ref, %Self.as_type [symbolic = %Self.binding.as_type (constants.%Self.binding.as_type)] // CHECK:STDOUT: } // CHECK:STDOUT: %self: @Instance2.WithSelf.G2.%Self.binding.as_type (%Self.binding.as_type) = value_binding self, %self.param // CHECK:STDOUT: } // CHECK:STDOUT: %assoc0: %Instance2.assoc_type = assoc_entity element0, %Instance2.WithSelf.G2.decl [concrete = constants.%assoc0] // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = %Self // CHECK:STDOUT: .G2 = @Instance2.WithSelf.%assoc0 // CHECK:STDOUT: witness = (@Instance2.WithSelf.%Instance2.WithSelf.G2.decl) // CHECK:STDOUT: // CHECK:STDOUT: !requires: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: impl @struct_type.e.as.Instance2.impl: %struct_type.e as %Instance2.ref { // CHECK:STDOUT: %struct_type.e.as.Instance2.impl.G2.decl: %struct_type.e.as.Instance2.impl.G2.type = fn_decl @struct_type.e.as.Instance2.impl.G2 [concrete = constants.%struct_type.e.as.Instance2.impl.G2] { // CHECK:STDOUT: %self.patt: %pattern_type.efd = value_binding_pattern self [concrete] // CHECK:STDOUT: %self.param_patt: %pattern_type.efd = value_param_pattern %self.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %self.param: %struct_type.e = value_param call_param0 // CHECK:STDOUT: %Self.ref: type = name_ref Self, @struct_type.e.as.Instance2.impl.%struct_type.e [concrete = constants.%struct_type.e] // CHECK:STDOUT: %self: %struct_type.e = value_binding self, %self.param // CHECK:STDOUT: } // CHECK:STDOUT: %Instance2.impl_witness_table = impl_witness_table (%struct_type.e.as.Instance2.impl.G2.decl), @struct_type.e.as.Instance2.impl [concrete] // CHECK:STDOUT: %Instance2.impl_witness: = impl_witness %Instance2.impl_witness_table [concrete = constants.%Instance2.impl_witness] // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .G2 = %struct_type.e.as.Instance2.impl.G2.decl // CHECK:STDOUT: witness = %Instance2.impl_witness // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic fn @Instance2.WithSelf.G2(@Instance2.%Self: %Instance2.type) { // CHECK:STDOUT: %Self: %Instance2.type = symbolic_binding Self, 0 [symbolic = %Self (constants.%Self)] // CHECK:STDOUT: %Self.binding.as_type: type = symbolic_binding_type Self, 0, %Self [symbolic = %Self.binding.as_type (constants.%Self.binding.as_type)] // CHECK:STDOUT: %pattern_type: type = pattern_type %Self.binding.as_type [symbolic = %pattern_type (constants.%pattern_type.a35)] // CHECK:STDOUT: // CHECK:STDOUT: fn(%self.param: @Instance2.WithSelf.G2.%Self.binding.as_type (%Self.binding.as_type)); // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @struct_type.e.as.Instance2.impl.G2(%self.param: %struct_type.e) { // CHECK:STDOUT: !entry: // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @InstanceCallFail() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %.loc16_9.1: %empty_tuple.type = tuple_literal () [concrete = constants.%empty_tuple] // CHECK:STDOUT: %.loc16_9.2: type = converted %.loc16_9.1, constants.%empty_tuple.type [concrete = constants.%empty_tuple.type] // CHECK:STDOUT: %struct_type.e: type = struct_type {.e: %empty_tuple.type} [concrete = constants.%struct_type.e] // CHECK:STDOUT: %Instance2.ref: type = name_ref Instance2, file.%Instance2.decl [concrete = constants.%Instance2.type] // CHECK:STDOUT: %G2.ref: %Instance2.assoc_type = name_ref G2, @Instance2.WithSelf.%assoc0 [concrete = constants.%assoc0] // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @Instance2.WithSelf(constants.%Self) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: %Self => constants.%Self // CHECK:STDOUT: %Instance2.WithSelf.G2.type => constants.%Instance2.WithSelf.G2.type.e15 // CHECK:STDOUT: %Instance2.WithSelf.G2 => constants.%Instance2.WithSelf.G2.6c8 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @Instance2.WithSelf.G2(constants.%Self) { // CHECK:STDOUT: %Self => constants.%Self // CHECK:STDOUT: %Self.binding.as_type => constants.%Self.binding.as_type // CHECK:STDOUT: %pattern_type => constants.%pattern_type.a35 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @Instance2.WithSelf(constants.%Instance2.facet) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: %Self => constants.%Instance2.facet // CHECK:STDOUT: %Instance2.WithSelf.G2.type => constants.%Instance2.WithSelf.G2.type.008 // CHECK:STDOUT: %Instance2.WithSelf.G2 => constants.%Instance2.WithSelf.G2.f23 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @Instance2.WithSelf.G2(constants.%Instance2.facet) { // CHECK:STDOUT: %Self => constants.%Instance2.facet // CHECK:STDOUT: %Self.binding.as_type => constants.%struct_type.e // CHECK:STDOUT: %pattern_type => constants.%pattern_type.efd // CHECK:STDOUT: } // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/impl/custom_witness/int_fits_in.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/primitives.carbon // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/impl/custom_witness/int_fits_in.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/impl/custom_witness/int_fits_in.carbon // --- core.carbon package Core; interface IntFitsIn(Dest:! type) {} fn CheckFitsIn[U:! type, T:! IntFitsIn(U)](unused t: T, unused u: U) {} // --- valid.carbon library "[[@TEST_NAME]]"; import Core; fn CheckOk() { Core.CheckFitsIn(1 as i32, 2 as i64); Core.CheckFitsIn(1 as u32, 2 as u64); Core.CheckFitsIn(1 as u32, 2 as i64); } // --- fail_narrowing.carbon library "[[@TEST_NAME]]"; import Core; fn CheckFail() { // CHECK:STDERR: fail_narrowing.carbon:[[@LINE+7]]:3: error: cannot convert type `i64` into type implementing `Core.IntFitsIn(i32)` [ConversionFailureTypeToFacet] // CHECK:STDERR: Core.CheckFitsIn(1 as i64, 2 as i32); // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: core.carbon:6:1: note: while deducing parameters of generic declared here [DeductionGenericHere] // CHECK:STDERR: fn CheckFitsIn[U:! type, T:! IntFitsIn(U)](unused t: T, unused u: U) {} // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: Core.CheckFitsIn(1 as i64, 2 as i32); // CHECK:STDERR: fail_narrowing.carbon:[[@LINE+7]]:3: error: cannot convert type `i64` into type implementing `Core.IntFitsIn(u32)` [ConversionFailureTypeToFacet] // CHECK:STDERR: Core.CheckFitsIn(1 as i64, 2 as u32); // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: core.carbon:6:1: note: while deducing parameters of generic declared here [DeductionGenericHere] // CHECK:STDERR: fn CheckFitsIn[U:! type, T:! IntFitsIn(U)](unused t: T, unused u: U) {} // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: Core.CheckFitsIn(1 as i64, 2 as u32); // CHECK:STDERR: fail_narrowing.carbon:[[@LINE+7]]:3: error: cannot convert type `u64` into type implementing `Core.IntFitsIn(i32)` [ConversionFailureTypeToFacet] // CHECK:STDERR: Core.CheckFitsIn(1 as u64, 2 as i32); // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: core.carbon:6:1: note: while deducing parameters of generic declared here [DeductionGenericHere] // CHECK:STDERR: fn CheckFitsIn[U:! type, T:! IntFitsIn(U)](unused t: T, unused u: U) {} // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: Core.CheckFitsIn(1 as u64, 2 as i32); // CHECK:STDERR: fail_narrowing.carbon:[[@LINE+7]]:3: error: cannot convert type `u64` into type implementing `Core.IntFitsIn(u32)` [ConversionFailureTypeToFacet] // CHECK:STDERR: Core.CheckFitsIn(1 as u64, 2 as u32); // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: core.carbon:6:1: note: while deducing parameters of generic declared here [DeductionGenericHere] // CHECK:STDERR: fn CheckFitsIn[U:! type, T:! IntFitsIn(U)](unused t: T, unused u: U) {} // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: Core.CheckFitsIn(1 as u64, 2 as u32); } // --- fail_same_size_sign_change.carbon library "[[@TEST_NAME]]"; import Core; fn CheckFail() { // CHECK:STDERR: fail_same_size_sign_change.carbon:[[@LINE+7]]:3: error: cannot convert type `u32` into type implementing `Core.IntFitsIn(i32)` [ConversionFailureTypeToFacet] // CHECK:STDERR: Core.CheckFitsIn(1 as u32, 2 as i32); // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: core.carbon:6:1: note: while deducing parameters of generic declared here [DeductionGenericHere] // CHECK:STDERR: fn CheckFitsIn[U:! type, T:! IntFitsIn(U)](unused t: T, unused u: U) {} // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: Core.CheckFitsIn(1 as u32, 2 as i32); // CHECK:STDERR: fail_same_size_sign_change.carbon:[[@LINE+7]]:3: error: cannot convert type `i32` into type implementing `Core.IntFitsIn(u32)` [ConversionFailureTypeToFacet] // CHECK:STDERR: Core.CheckFitsIn(1 as i32, 2 as u32); // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: core.carbon:6:1: note: while deducing parameters of generic declared here [DeductionGenericHere] // CHECK:STDERR: fn CheckFitsIn[U:! type, T:! IntFitsIn(U)](unused t: T, unused u: U) {} // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: Core.CheckFitsIn(1 as i32, 2 as u32); } // --- fail_signed_to_wider_unsigned.carbon library "[[@TEST_NAME]]"; import Core; fn CheckFail() { // CHECK:STDERR: fail_signed_to_wider_unsigned.carbon:[[@LINE+7]]:3: error: cannot convert type `i32` into type implementing `Core.IntFitsIn(u32)` [ConversionFailureTypeToFacet] // CHECK:STDERR: Core.CheckFitsIn(1 as i32, 2 as u32); // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: core.carbon:6:1: note: while deducing parameters of generic declared here [DeductionGenericHere] // CHECK:STDERR: fn CheckFitsIn[U:! type, T:! IntFitsIn(U)](unused t: T, unused u: U) {} // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: Core.CheckFitsIn(1 as i32, 2 as u32); } ================================================ FILE: toolchain/check/testdata/impl/declaration.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/int.carbon // TODO: Add ranges and switch to "--dump-sem-ir-ranges=only". // EXTRA-ARGS: --dump-sem-ir-ranges=if-present // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/impl/declaration.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/impl/declaration.carbon interface I {} impl i32 as I; impl i32 as I {} // CHECK:STDOUT: --- declaration.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %I.type: type = facet_type <@I> [concrete] // CHECK:STDOUT: %Self: %I.type = symbolic_binding Self, 0 [symbolic] // CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [concrete] // CHECK:STDOUT: %Int.type: type = generic_class_type @Int [concrete] // CHECK:STDOUT: %Int.generic: %Int.type = struct_value () [concrete] // CHECK:STDOUT: %i32: type = class_type @Int, @Int(%int_32) [concrete] // CHECK:STDOUT: %I.impl_witness: = impl_witness @i32.as.I.impl.%I.impl_witness_table [concrete] // CHECK:STDOUT: %I.facet: %I.type = facet_value %i32, (%I.impl_witness) [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: .Int = %Core.Int // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: %Core.Int: %Int.type = import_ref Core//prelude/parts/int, Int, loaded [concrete = constants.%Int.generic] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .I = %I.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %I.decl: type = interface_decl @I [concrete = constants.%I.type] {} {} // CHECK:STDOUT: impl_decl @i32.as.I.impl [concrete] {} { // CHECK:STDOUT: %i32.loc17: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %I.ref.loc17: type = name_ref I, file.%I.decl [concrete = constants.%I.type] // CHECK:STDOUT: } // CHECK:STDOUT: impl_decl @i32.as.I.impl [concrete] {} { // CHECK:STDOUT: %i32.loc19: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %I.ref.loc19: type = name_ref I, file.%I.decl [concrete = constants.%I.type] // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: interface @I { // CHECK:STDOUT: %Self: %I.type = symbolic_binding Self, 0 [symbolic = constants.%Self] // CHECK:STDOUT: %I.WithSelf.decl = interface_with_self_decl @I [concrete] // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = %Self // CHECK:STDOUT: witness = () // CHECK:STDOUT: // CHECK:STDOUT: !requires: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: impl @i32.as.I.impl: %i32.loc17 as %I.ref.loc17 { // CHECK:STDOUT: %I.impl_witness_table = impl_witness_table (), @i32.as.I.impl [concrete] // CHECK:STDOUT: %I.impl_witness: = impl_witness %I.impl_witness_table [concrete = constants.%I.impl_witness] // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: witness = %I.impl_witness // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @I.WithSelf(constants.%Self) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @I.WithSelf(constants.%I.facet) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: } // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/impl/empty.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/int.carbon // TODO: Add ranges and switch to "--dump-sem-ir-ranges=only". // EXTRA-ARGS: --dump-sem-ir-ranges=if-present // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/impl/empty.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/impl/empty.carbon interface Empty { } impl i32 as Empty { } // CHECK:STDOUT: --- empty.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %Empty.type: type = facet_type <@Empty> [concrete] // CHECK:STDOUT: %Self: %Empty.type = symbolic_binding Self, 0 [symbolic] // CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [concrete] // CHECK:STDOUT: %Int.type: type = generic_class_type @Int [concrete] // CHECK:STDOUT: %Int.generic: %Int.type = struct_value () [concrete] // CHECK:STDOUT: %i32: type = class_type @Int, @Int(%int_32) [concrete] // CHECK:STDOUT: %Empty.impl_witness: = impl_witness @i32.as.Empty.impl.%Empty.impl_witness_table [concrete] // CHECK:STDOUT: %Empty.facet: %Empty.type = facet_value %i32, (%Empty.impl_witness) [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: .Int = %Core.Int // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: %Core.Int: %Int.type = import_ref Core//prelude/parts/int, Int, loaded [concrete = constants.%Int.generic] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .Empty = %Empty.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %Empty.decl: type = interface_decl @Empty [concrete = constants.%Empty.type] {} {} // CHECK:STDOUT: impl_decl @i32.as.Empty.impl [concrete] {} { // CHECK:STDOUT: %i32: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %Empty.ref: type = name_ref Empty, file.%Empty.decl [concrete = constants.%Empty.type] // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: interface @Empty { // CHECK:STDOUT: %Self: %Empty.type = symbolic_binding Self, 0 [symbolic = constants.%Self] // CHECK:STDOUT: %Empty.WithSelf.decl = interface_with_self_decl @Empty [concrete] // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = %Self // CHECK:STDOUT: witness = () // CHECK:STDOUT: // CHECK:STDOUT: !requires: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: impl @i32.as.Empty.impl: %i32 as %Empty.ref { // CHECK:STDOUT: %Empty.impl_witness_table = impl_witness_table (), @i32.as.Empty.impl [concrete] // CHECK:STDOUT: %Empty.impl_witness: = impl_witness %Empty.impl_witness_table [concrete = constants.%Empty.impl_witness] // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: witness = %Empty.impl_witness // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @Empty.WithSelf(constants.%Self) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @Empty.WithSelf(constants.%Empty.facet) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: } // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/impl/error_recovery.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/none.carbon // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/impl/error_recovery.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/impl/error_recovery.carbon // --- fail_runtime_generic_param.carbon library "[[@TEST_NAME]]"; class C {} interface I {} //@dump-sem-ir-begin // CHECK:STDERR: fail_runtime_generic_param.carbon:[[@LINE+4]]:14: error: parameters of generic types must be constant [GenericParamMustBeConstant] // CHECK:STDERR: impl forall [T: type] C as I {} // CHECK:STDERR: ^~~~~~~ // CHECK:STDERR: impl forall [T: type] C as I {} //@dump-sem-ir-end // --- fail_nonfinal_lookup_impl_witness_error_in_import.carbon library "[[@TEST_NAME]]"; interface Z {} // CHECK:STDERR: fail_nonfinal_lookup_impl_witness_error_in_import.carbon:[[@LINE+4]]:1: error: impl declared but not defined [ImplMissingDefinition] // CHECK:STDERR: impl forall [T:! type] T as Z; // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: impl forall [T:! type] T as Z; fn F(unused U:! Z) {} //@dump-sem-ir-begin fn G(T:! type) { // This makes a LookupImplWitness instruction, but future lookups (evaluation // of this instruction with a specific) will result in an error since the impl // is never defined and is left with an error as its witness at the end of the // file. The lookups should not fail entirely, just result in an error // witness. F(T); } //@dump-sem-ir-end // --- nonfinal_lookup_impl_witness_error_in_import.impl.carbon impl library "[[@TEST_NAME]]"; fn H() { // The specific here contains errors, but does not fail entirely and crash // when resolving the LookupImplWitness. G(()); } // --- fail_nonfinal_lookup_impl_witness_error.carbon library "[[@TEST_NAME]]"; interface Z {} // CHECK:STDERR: fail_nonfinal_lookup_impl_witness_error.carbon:[[@LINE+4]]:1: error: impl declared but not defined [ImplMissingDefinition] // CHECK:STDERR: impl forall [T:! type] T as Z; // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: impl forall [T:! type] T as Z; fn F(unused U:! Z) {} //@dump-sem-ir-begin fn G(T:! type) { // This makes a LookupImplWitness instruction, but future lookups (evaluation // of this instruction with a specific) will fail with an error since the impl // is never defined and is left with an error as its witness at the end of the // file. The lookups should not fail entirely, just result in an error // witness. F(T); } //@dump-sem-ir-end fn H() { // The specific here contains errors, but does not fail entirely and crash // when resolving the LookupImplWitness. G(()); } // --- fail_final_lookup_impl_witness_error.carbon library "[[@TEST_NAME]]"; interface Z {} // CHECK:STDERR: fail_final_lookup_impl_witness_error.carbon:[[@LINE+4]]:1: error: impl declared but not defined [ImplMissingDefinition] // CHECK:STDERR: impl forall [T:! type] T as Z; // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: impl forall [T:! type] T as Z; fn F(unused U:! Z) {} fn G() { // This impl lookup resolves to a final witness, which poisons any future // queries. At the end of the file, the poisoned queries are replayed to make // sure they don't change. However, here it is changed by the impl being // diagnosed with an error. The poisoning check should handle that gracefully. //@dump-sem-ir-begin F(()); //@dump-sem-ir-end } // --- fail_invalid_fn_syntax_in_impl.carbon library "[[@TEST_NAME]]"; interface I { fn Op[self: Self](); } class C {}; //@dump-sem-ir-begin impl C as I { // This leaves the impl with a placeholder instruction in the witness table. // CHECK:STDERR: fail_invalid_fn_syntax_in_impl.carbon:[[@LINE+8]]:11: error: expected `:`, `:!`, or `:?` in binding pattern [ExpectedBindingPattern] // CHECK:STDERR: fn Op[x self: C]() {} // CHECK:STDERR: ^~~~ // CHECK:STDERR: // CHECK:STDERR: fail_invalid_fn_syntax_in_impl.carbon:[[@LINE+4]]:11: error: semantics TODO: `handle invalid parse trees in `check`` [SemanticsTodo] // CHECK:STDERR: fn Op[x self: C]() {} // CHECK:STDERR: ^~~~ // CHECK:STDERR: fn Op[x self: C]() {} } //@dump-sem-ir-end // CHECK:STDOUT: --- fail_runtime_generic_param.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %C: type = class_type @C [concrete] // CHECK:STDOUT: %I.type: type = facet_type <@I> [concrete] // CHECK:STDOUT: %I.impl_witness: = impl_witness @C.as.I.impl.%I.impl_witness_table [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: impl_decl @C.as.I.impl [concrete] {} { // CHECK:STDOUT: %C.ref: type = name_ref C, file.%C.decl [concrete = constants.%C] // CHECK:STDOUT: %I.ref: type = name_ref I, file.%I.decl [concrete = constants.%I.type] // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: impl @C.as.I.impl: %C.ref as %I.ref { // CHECK:STDOUT: %I.impl_witness_table = impl_witness_table (), @C.as.I.impl [concrete] // CHECK:STDOUT: %I.impl_witness: = impl_witness %I.impl_witness_table [concrete = constants.%I.impl_witness] // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: witness = %I.impl_witness // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- fail_nonfinal_lookup_impl_witness_error_in_import.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %Z.type: type = facet_type <@Z> [concrete] // CHECK:STDOUT: %pattern_type.98f: type = pattern_type type [concrete] // CHECK:STDOUT: %T: type = symbolic_binding T, 0 [symbolic] // CHECK:STDOUT: %F.type: type = fn_type @F [concrete] // CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] // CHECK:STDOUT: %F: %F.type = struct_value () [concrete] // CHECK:STDOUT: %G.type: type = fn_type @G [concrete] // CHECK:STDOUT: %G: %G.type = struct_value () [concrete] // CHECK:STDOUT: %.242: require_specific_def_type = require_specific_def @T.as.Z.impl(%T) [symbolic] // CHECK:STDOUT: %Z.lookup_impl_witness: = lookup_impl_witness %T, @Z [symbolic] // CHECK:STDOUT: %Z.facet: %Z.type = facet_value %T, (%Z.lookup_impl_witness) [symbolic] // CHECK:STDOUT: %F.specific_fn: = specific_function %F, @F(%Z.facet) [symbolic] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: %G.decl: %G.type = fn_decl @G [concrete = constants.%G] { // CHECK:STDOUT: %T.patt: %pattern_type.98f = symbolic_binding_pattern T, 0 [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %.loc13_10.1: type = splice_block %.loc13_10.2 [concrete = type] { // CHECK:STDOUT: // CHECK:STDOUT: %.loc13_10.2: type = type_literal type [concrete = type] // CHECK:STDOUT: } // CHECK:STDOUT: %T.loc13_6.2: type = symbolic_binding T, 0 [symbolic = %T.loc13_6.1 (constants.%T)] // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic fn @G(%T.loc13_6.2: type) { // CHECK:STDOUT: %T.loc13_6.1: type = symbolic_binding T, 0 [symbolic = %T.loc13_6.1 (constants.%T)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %.loc19_6.2: require_specific_def_type = require_specific_def @T.as.Z.impl(%T.loc13_6.1) [symbolic = %.loc19_6.2 (constants.%.242)] // CHECK:STDOUT: %Z.lookup_impl_witness: = lookup_impl_witness %T.loc13_6.1, @Z [symbolic = %Z.lookup_impl_witness (constants.%Z.lookup_impl_witness)] // CHECK:STDOUT: %Z.facet.loc19_6.2: %Z.type = facet_value %T.loc13_6.1, (%Z.lookup_impl_witness) [symbolic = %Z.facet.loc19_6.2 (constants.%Z.facet)] // CHECK:STDOUT: %F.specific_fn.loc19_3.2: = specific_function constants.%F, @F(%Z.facet.loc19_6.2) [symbolic = %F.specific_fn.loc19_3.2 (constants.%F.specific_fn)] // CHECK:STDOUT: // CHECK:STDOUT: fn() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %F.ref: %F.type = name_ref F, file.%F.decl [concrete = constants.%F] // CHECK:STDOUT: %T.ref: type = name_ref T, %T.loc13_6.2 [symbolic = %T.loc13_6.1 (constants.%T)] // CHECK:STDOUT: %Z.facet.loc19_6.1: %Z.type = facet_value %T.ref, (constants.%Z.lookup_impl_witness) [symbolic = %Z.facet.loc19_6.2 (constants.%Z.facet)] // CHECK:STDOUT: %.loc19_6.1: %Z.type = converted %T.ref, %Z.facet.loc19_6.1 [symbolic = %Z.facet.loc19_6.2 (constants.%Z.facet)] // CHECK:STDOUT: %F.specific_fn.loc19_3.1: = specific_function %F.ref, @F(constants.%Z.facet) [symbolic = %F.specific_fn.loc19_3.2 (constants.%F.specific_fn)] // CHECK:STDOUT: %F.call: init %empty_tuple.type = call %F.specific_fn.loc19_3.1() // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @G(constants.%T) { // CHECK:STDOUT: %T.loc13_6.1 => constants.%T // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- fail_nonfinal_lookup_impl_witness_error.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %Z.type: type = facet_type <@Z> [concrete] // CHECK:STDOUT: %pattern_type.98f: type = pattern_type type [concrete] // CHECK:STDOUT: %T: type = symbolic_binding T, 0 [symbolic] // CHECK:STDOUT: %F.type: type = fn_type @F [concrete] // CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] // CHECK:STDOUT: %F: %F.type = struct_value () [concrete] // CHECK:STDOUT: %G.type: type = fn_type @G [concrete] // CHECK:STDOUT: %G: %G.type = struct_value () [concrete] // CHECK:STDOUT: %.242: require_specific_def_type = require_specific_def @T.as.Z.impl(%T) [symbolic] // CHECK:STDOUT: %Z.lookup_impl_witness: = lookup_impl_witness %T, @Z [symbolic] // CHECK:STDOUT: %Z.facet: %Z.type = facet_value %T, (%Z.lookup_impl_witness) [symbolic] // CHECK:STDOUT: %F.specific_fn: = specific_function %F, @F(%Z.facet) [symbolic] // CHECK:STDOUT: %.194: require_specific_def_type = require_specific_def @T.as.Z.impl(%empty_tuple.type) [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: %G.decl: %G.type = fn_decl @G [concrete = constants.%G] { // CHECK:STDOUT: %T.patt: %pattern_type.98f = symbolic_binding_pattern T, 0 [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %.loc13_10.1: type = splice_block %.loc13_10.2 [concrete = type] { // CHECK:STDOUT: // CHECK:STDOUT: %.loc13_10.2: type = type_literal type [concrete = type] // CHECK:STDOUT: } // CHECK:STDOUT: %T.loc13_6.2: type = symbolic_binding T, 0 [symbolic = %T.loc13_6.1 (constants.%T)] // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic fn @G(%T.loc13_6.2: type) { // CHECK:STDOUT: %T.loc13_6.1: type = symbolic_binding T, 0 [symbolic = %T.loc13_6.1 (constants.%T)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %.loc19_6.2: require_specific_def_type = require_specific_def @T.as.Z.impl(%T.loc13_6.1) [symbolic = %.loc19_6.2 (constants.%.242)] // CHECK:STDOUT: %Z.lookup_impl_witness: = lookup_impl_witness %T.loc13_6.1, @Z [symbolic = %Z.lookup_impl_witness (constants.%Z.lookup_impl_witness)] // CHECK:STDOUT: %Z.facet.loc19_6.2: %Z.type = facet_value %T.loc13_6.1, (%Z.lookup_impl_witness) [symbolic = %Z.facet.loc19_6.2 (constants.%Z.facet)] // CHECK:STDOUT: %F.specific_fn.loc19_3.2: = specific_function constants.%F, @F(%Z.facet.loc19_6.2) [symbolic = %F.specific_fn.loc19_3.2 (constants.%F.specific_fn)] // CHECK:STDOUT: // CHECK:STDOUT: fn() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %F.ref: %F.type = name_ref F, file.%F.decl [concrete = constants.%F] // CHECK:STDOUT: %T.ref: type = name_ref T, %T.loc13_6.2 [symbolic = %T.loc13_6.1 (constants.%T)] // CHECK:STDOUT: %Z.facet.loc19_6.1: %Z.type = facet_value %T.ref, (constants.%Z.lookup_impl_witness) [symbolic = %Z.facet.loc19_6.2 (constants.%Z.facet)] // CHECK:STDOUT: %.loc19_6.1: %Z.type = converted %T.ref, %Z.facet.loc19_6.1 [symbolic = %Z.facet.loc19_6.2 (constants.%Z.facet)] // CHECK:STDOUT: %F.specific_fn.loc19_3.1: = specific_function %F.ref, @F(constants.%Z.facet) [symbolic = %F.specific_fn.loc19_3.2 (constants.%F.specific_fn)] // CHECK:STDOUT: %F.call: init %empty_tuple.type = call %F.specific_fn.loc19_3.1() // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @G(constants.%T) { // CHECK:STDOUT: %T.loc13_6.1 => constants.%T // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @G(constants.%empty_tuple.type) { // CHECK:STDOUT: %T.loc13_6.1 => constants.%empty_tuple.type // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %.loc19_6.2 => constants.%.194 // CHECK:STDOUT: %Z.lookup_impl_witness => // CHECK:STDOUT: %Z.facet.loc19_6.2 => // CHECK:STDOUT: %F.specific_fn.loc19_3.2 => // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- fail_final_lookup_impl_witness_error.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %Z.type: type = facet_type <@Z> [concrete] // CHECK:STDOUT: %F.type: type = fn_type @F [concrete] // CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] // CHECK:STDOUT: %F: %F.type = struct_value () [concrete] // CHECK:STDOUT: %empty_tuple: %empty_tuple.type = tuple_value () [concrete] // CHECK:STDOUT: %Z.impl_witness.db1: = impl_witness @T.as.Z.impl.%Z.impl_witness_table, @T.as.Z.impl(%empty_tuple.type) [concrete] // CHECK:STDOUT: %Z.facet: %Z.type = facet_value %empty_tuple.type, (%Z.impl_witness.db1) [concrete] // CHECK:STDOUT: %F.specific_fn: = specific_function %F, @F(%Z.facet) [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @G() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %F.ref: %F.type = name_ref F, file.%F.decl [concrete = constants.%F] // CHECK:STDOUT: %.loc18_6: %empty_tuple.type = tuple_literal () [concrete = constants.%empty_tuple] // CHECK:STDOUT: %Z.facet: %Z.type = facet_value constants.%empty_tuple.type, (constants.%Z.impl_witness.db1) [concrete = constants.%Z.facet] // CHECK:STDOUT: %.loc18_7: %Z.type = converted %.loc18_6, %Z.facet [concrete = constants.%Z.facet] // CHECK:STDOUT: %F.specific_fn: = specific_function %F.ref, @F(constants.%Z.facet) [concrete = constants.%F.specific_fn] // CHECK:STDOUT: %F.call: init %empty_tuple.type = call %F.specific_fn() // CHECK:STDOUT: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- fail_invalid_fn_syntax_in_impl.carbon // CHECK:STDOUT: // CHECK:STDOUT: impl @: .inst{{[0-9A-F]+}}.loc9_6 as .inst{{[0-9A-F]+}}.loc9_11; // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/impl/eval_musteval.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/convert.carbon // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/impl/eval_musteval.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/impl/eval_musteval.carbon // --- interface.carbon library "[[@TEST_NAME]]"; interface Runtime { fn F[self: Self]() -> type; } interface Eval { eval fn F[self: Self]() -> type; } interface MustEval { musteval fn F[self: Self]() -> type; } // --- impl_runtime.carbon library "[[@TEST_NAME]]"; import library "interface"; class A { adapt {}; } impl A as Runtime { fn F[unused self: Self]() -> type { return A; } } impl A as Eval { // TODO: Consider rejecting this, as compile-time evaluation would always // fail. fn F[unused self: Self]() -> type { return A; } } impl A as MustEval { // TODO: Consider rejecting this, as compile-time evaluation would always // fail. fn F[unused self: Self]() -> type { return A; } } fn Call() { ({} as A).(Runtime.F)(); ({} as A).(Eval.F)(); ({} as A).(MustEval.F)(); } // --- fail_impl_runtime_eval.carbon library "[[@TEST_NAME]]"; import library "interface"; class A { adapt {}; } impl A as Runtime { fn F[unused self: Self]() -> type { return A; } } impl A as Eval { fn F[unused self: Self]() -> type { return A; } } impl A as MustEval { fn F[unused self: Self]() -> type { return A; } } fn Call() { // CHECK:STDERR: fail_impl_runtime_eval.carbon:[[@LINE+4]]:17: error: cannot evaluate type expression [TypeExprEvaluationFailure] // CHECK:STDERR: let unused t: ({} as A).(Runtime.F)() = {} as A; // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: let unused t: ({} as A).(Runtime.F)() = {} as A; // CHECK:STDERR: fail_impl_runtime_eval.carbon:[[@LINE+4]]:17: error: cannot evaluate type expression [TypeExprEvaluationFailure] // CHECK:STDERR: let unused u: ({} as A).(Eval.F)() = {} as A; // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: let unused u: ({} as A).(Eval.F)() = {} as A; // CHECK:STDERR: fail_impl_runtime_eval.carbon:[[@LINE+4]]:17: error: cannot evaluate type expression [TypeExprEvaluationFailure] // CHECK:STDERR: let unused v: ({} as A).(MustEval.F)() = {} as A; // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: let unused v: ({} as A).(MustEval.F)() = {} as A; } // --- impl_eval.carbon library "[[@TEST_NAME]]"; import library "interface"; class A { adapt {}; } impl A as Runtime { eval fn F[unused self: Self]() -> type { return A; } } impl A as Eval { eval fn F[unused self: Self]() -> type { return A; } } impl A as MustEval { eval fn F[unused self: Self]() -> type { return A; } } fn Call() { // TODO: Should we allow calling this at compile time? This is not allowed if // we generate a thunk; see the next split for an example. let unused t: ({} as A).(Runtime.F)() = {} as A; let unused u: ({} as A).(Eval.F)() = {} as A; let unused v: ({} as A).(MustEval.F)() = {} as A; } // --- fail_todo_impl_eval_from_runtime_thunk.carbon library "[[@TEST_NAME]]"; import library "interface"; class B {} class BView {} impl B as Core.ImplicitAs(BView) { fn Convert[unused self: Self]() -> BView { return {}; } } impl B as Runtime { eval fn F[unused self: BView]() -> type { return B; } } fn Call() { // TODO: Should we allow calling this at compile time? This is allowed if we // don't generate a thunk. // CHECK:STDERR: fail_todo_impl_eval_from_runtime_thunk.carbon:[[@LINE+4]]:17: error: cannot evaluate type expression [TypeExprEvaluationFailure] // CHECK:STDERR: let unused t: ({} as B).(Runtime.F)() = {} as B; // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: let unused t: ({} as B).(Runtime.F)() = {} as B; } // --- fail_impl_musteval.carbon library "[[@TEST_NAME]]"; import library "interface"; class B { adapt {}; } impl B as Runtime { // CHECK:STDERR: fail_impl_musteval.carbon:[[@LINE+11]]:3: error: non-constant call to compile-time-only function [NonConstantCallToCompTimeOnlyFunction] // CHECK:STDERR: musteval fn F[unused self: Self]() -> type { return B; } // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: fail_impl_musteval.carbon:[[@LINE+8]]:3: note: compile-time-only function declared here [CompTimeOnlyFunctionHere] // CHECK:STDERR: musteval fn F[unused self: Self]() -> type { return B; } // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: fail_impl_musteval.carbon:[[@LINE-11]]:1: in import [InImport] // CHECK:STDERR: interface.carbon:5:3: note: while building thunk to match the signature of this function [ThunkSignature] // CHECK:STDERR: fn F[self: Self]() -> type; // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: musteval fn F[unused self: Self]() -> type { return B; } } impl B as Eval { // CHECK:STDERR: fail_impl_musteval.carbon:[[@LINE+11]]:3: error: non-constant call to compile-time-only function [NonConstantCallToCompTimeOnlyFunction] // CHECK:STDERR: musteval fn F[unused self: Self]() -> type { return B; } // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: fail_impl_musteval.carbon:[[@LINE+8]]:3: note: compile-time-only function declared here [CompTimeOnlyFunctionHere] // CHECK:STDERR: musteval fn F[unused self: Self]() -> type { return B; } // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: fail_impl_musteval.carbon:[[@LINE-26]]:1: in import [InImport] // CHECK:STDERR: interface.carbon:9:3: note: while building thunk to match the signature of this function [ThunkSignature] // CHECK:STDERR: eval fn F[self: Self]() -> type; // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: musteval fn F[unused self: Self]() -> type { return B; } } // --- impl_musteval_from_musteval.carbon library "[[@TEST_NAME]]"; import library "interface"; class B { adapt {}; } impl B as MustEval { musteval fn F[unused self: Self]() -> type { return B; } } fn Call() { let unused t: ({} as B).(MustEval.F)() = {} as B; } // --- fail_todo_impl_musteval_from_musteval_thunk.carbon library "[[@TEST_NAME]]"; import library "interface"; class B {} class BView {} impl B as Core.ImplicitAs(BView) { fn Convert[unused self: Self]() -> BView { return {}; } } impl B as MustEval { // CHECK:STDERR: fail_todo_impl_musteval_from_musteval_thunk.carbon:[[@LINE+11]]:3: error: non-constant call to compile-time-only function [NonConstantCallToCompTimeOnlyFunction] // CHECK:STDERR: musteval fn F[unused self: BView]() -> type { return B; } // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: fail_todo_impl_musteval_from_musteval_thunk.carbon:[[@LINE+8]]:3: note: compile-time-only function declared here [CompTimeOnlyFunctionHere] // CHECK:STDERR: musteval fn F[unused self: BView]() -> type { return B; } // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: fail_todo_impl_musteval_from_musteval_thunk.carbon:[[@LINE-16]]:1: in import [InImport] // CHECK:STDERR: interface.carbon:13:3: note: while building thunk to match the signature of this function [ThunkSignature] // CHECK:STDERR: musteval fn F[self: Self]() -> type; // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: musteval fn F[unused self: BView]() -> type { return B; } } ================================================ FILE: toolchain/check/testdata/impl/extend_final.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/convert.carbon // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/impl/extend_final.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/impl/extend_final.carbon // --- extend_final_impl.carbon library "[[@TEST_NAME]]"; interface Z { let X:! type; } class C { extend final impl as Z where .X = () {} } fn F() { let unused b: C.X = (); } // --- fail_final_extend_impl.carbon library "[[@TEST_NAME]]"; interface Z {} class C { // CHECK:STDERR: fail_final_extend_impl.carbon:[[@LINE+7]]:9: error: `extend` must appear before `final` [ModifierMustAppearBefore] // CHECK:STDERR: final extend impl as Z {} // CHECK:STDERR: ^~~~~~ // CHECK:STDERR: fail_final_extend_impl.carbon:[[@LINE+4]]:3: note: `final` previously appeared here [ModifierPrevious] // CHECK:STDERR: final extend impl as Z {} // CHECK:STDERR: ^~~~~ // CHECK:STDERR: final extend impl as Z {} } // --- fail_final_extend_impl_outside_class.carbon library "[[@TEST_NAME]]"; interface Z {} class C {} // CHECK:STDERR: fail_final_extend_impl_outside_class.carbon:[[@LINE+4]]:1: error: `extend impl` can only be used in an interface or class [ExtendImplOutsideClass] // CHECK:STDERR: extend final impl C as Z {} // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: extend final impl C as Z {} ================================================ FILE: toolchain/check/testdata/impl/extend_impl.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/none.carbon // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/impl/extend_impl.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/impl/extend_impl.carbon // --- extend_impl.carbon library "[[@TEST_NAME]]"; interface HasF { fn F(); } //@dump-sem-ir-begin class C { extend impl as HasF { fn F() {} } } //@dump-sem-ir-end fn G(c: C) { C.F(); c.F(); } // --- fail_extend_impl_nonexistent.carbon library "[[@TEST_NAME]]"; interface I {} class C { // CHECK:STDERR: fail_extend_impl_nonexistent.carbon:[[@LINE+4]]:15: error: name `nonexistent` not found [NameNotFound] // CHECK:STDERR: extend impl nonexistent as I {} // CHECK:STDERR: ^~~~~~~~~~~ // CHECK:STDERR: extend impl nonexistent as I {} fn F() { // The erroneous self-type for an `extend` causes the error to be propagated // into the class scope, which prevents errors if we fail to find a name // in that scope. Self.A; } } // --- fail_impl_nonexistent.carbon library "[[@TEST_NAME]]"; interface I {} class C { // CHECK:STDERR: fail_impl_nonexistent.carbon:[[@LINE+4]]:8: error: name `nonexistent` not found [NameNotFound] // CHECK:STDERR: impl nonexistent as I {} // CHECK:STDERR: ^~~~~~~~~~~ // CHECK:STDERR: impl nonexistent as I {} fn F() { // The name lookup error still happens, since the `require` is not `extend`. // CHECK:STDERR: fail_impl_nonexistent.carbon:[[@LINE+4]]:5: error: member name `A` not found in `C` [MemberNameNotFoundInInstScope] // CHECK:STDERR: Self.A; // CHECK:STDERR: ^~~~~~ // CHECK:STDERR: Self.A; } } // --- fail_extend_impl_nonexistent_pointer.carbon library "[[@TEST_NAME]]"; interface I {} class C { // CHECK:STDERR: fail_extend_impl_nonexistent_pointer.carbon:[[@LINE+4]]:15: error: name `nonexistent` not found [NameNotFound] // CHECK:STDERR: extend impl nonexistent* as I {} // CHECK:STDERR: ^~~~~~~~~~~ // CHECK:STDERR: extend impl nonexistent* as I {} fn F() { // The erroneous self-type for an `extend` causes the error to be propagated // into the class scope, which prevents errors if we fail to find a name // in that scope. Self.A; } } // --- fail_extend_impl_nonexistent_outside_class.carbon library "[[@TEST_NAME]]"; interface I {} // CHECK:STDERR: fail_extend_impl_nonexistent_outside_class.carbon:[[@LINE+4]]:13: error: name `nonexistent` not found [NameNotFound] // CHECK:STDERR: extend impl nonexistent* as I {} // CHECK:STDERR: ^~~~~~~~~~~ // CHECK:STDERR: extend impl nonexistent* as I {} // CHECK:STDOUT: --- extend_impl.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %HasF.type: type = facet_type <@HasF> [concrete] // CHECK:STDOUT: %C: type = class_type @C [concrete] // CHECK:STDOUT: %HasF.impl_witness: = impl_witness @C.as.HasF.impl.%HasF.impl_witness_table [concrete] // CHECK:STDOUT: %C.as.HasF.impl.F.type: type = fn_type @C.as.HasF.impl.F [concrete] // CHECK:STDOUT: %C.as.HasF.impl.F: %C.as.HasF.impl.F.type = struct_value () [concrete] // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %complete_type: = complete_type_witness %empty_struct_type [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: %C.decl: type = class_decl @C [concrete = constants.%C] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: impl @C.as.HasF.impl: %Self.ref as %HasF.ref { // CHECK:STDOUT: %C.as.HasF.impl.F.decl: %C.as.HasF.impl.F.type = fn_decl @C.as.HasF.impl.F [concrete = constants.%C.as.HasF.impl.F] {} {} // CHECK:STDOUT: %HasF.impl_witness_table = impl_witness_table (%C.as.HasF.impl.F.decl), @C.as.HasF.impl [concrete] // CHECK:STDOUT: %HasF.impl_witness: = impl_witness %HasF.impl_witness_table [concrete = constants.%HasF.impl_witness] // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .F = %C.as.HasF.impl.F.decl // CHECK:STDOUT: witness = %HasF.impl_witness // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @C { // CHECK:STDOUT: impl_decl @C.as.HasF.impl [concrete] {} { // CHECK:STDOUT: %Self.ref: type = name_ref Self, constants.%C [concrete = constants.%C] // CHECK:STDOUT: %HasF.ref: type = name_ref HasF, file.%HasF.decl [concrete = constants.%HasF.type] // CHECK:STDOUT: } // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%C // CHECK:STDOUT: .HasF = // CHECK:STDOUT: .F = // CHECK:STDOUT: extend @C.as.HasF.impl.%HasF.ref // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @C.as.HasF.impl.F() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/impl/extend_impl_generic.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/int.carbon // TODO: Add ranges and switch to "--dump-sem-ir-ranges=only". // EXTRA-ARGS: --dump-sem-ir-ranges=if-present // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/impl/extend_impl_generic.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/impl/extend_impl_generic.carbon // --- extend_impl_generic_interface.carbon library "[[@TEST_NAME]]"; interface HasF(T:! type) { fn F() -> T; } class Param { var x: i32; } class C { extend impl as HasF(Param) { fn F() -> Param { return {.x = 2}; } } } fn G(c: C) { let unused a: i32 = C.F().x; var unused b: i32 = c.F().x; } // --- extend_impl_generic_class.carbon library "[[@TEST_NAME]]"; interface I(T:! type) { fn F[self: Self](t: T); } class X(U:! type) { extend impl as I(U) { fn F[unused self: Self](unused t: U) { } } } // CHECK:STDOUT: --- extend_impl_generic_interface.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %type: type = facet_type [concrete] // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self] // CHECK:STDOUT: %pattern_type.98f: type = pattern_type type [concrete] // CHECK:STDOUT: %T.67d: type = symbolic_binding T, 0 [symbolic] // CHECK:STDOUT: %HasF.type.ee1: type = generic_interface_type @HasF [concrete] // CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] // CHECK:STDOUT: %HasF.generic: %HasF.type.ee1 = struct_value () [concrete] // CHECK:STDOUT: %HasF.type.8a4: type = facet_type <@HasF, @HasF(%T.67d)> [symbolic] // CHECK:STDOUT: %Self.c1f: %HasF.type.8a4 = symbolic_binding Self, 1 [symbolic] // CHECK:STDOUT: %.184347.1: Core.Form = init_form %T.67d [symbolic] // CHECK:STDOUT: %pattern_type.51d1c4.1: type = pattern_type %T.67d [symbolic] // CHECK:STDOUT: %HasF.WithSelf.F.type.246: type = fn_type @HasF.WithSelf.F, @HasF.WithSelf(%T.67d, %Self.c1f) [symbolic] // CHECK:STDOUT: %HasF.WithSelf.F.58b: %HasF.WithSelf.F.type.246 = struct_value () [symbolic] // CHECK:STDOUT: %HasF.assoc_type.196: type = assoc_entity_type @HasF, @HasF(%T.67d) [symbolic] // CHECK:STDOUT: %assoc0.b54: %HasF.assoc_type.196 = assoc_entity element0, @HasF.WithSelf.%HasF.WithSelf.F.decl [symbolic] // CHECK:STDOUT: %Param: type = class_type @Param [concrete] // CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [concrete] // CHECK:STDOUT: %Int.type: type = generic_class_type @Int [concrete] // CHECK:STDOUT: %Int.generic: %Int.type = struct_value () [concrete] // CHECK:STDOUT: %N: Core.IntLiteral = symbolic_binding N, 0 [symbolic] // CHECK:STDOUT: %i32: type = class_type @Int, @Int(%int_32) [concrete] // CHECK:STDOUT: %Param.elem: type = unbound_element_type %Param, %i32 [concrete] // CHECK:STDOUT: %struct_type.x.ed6: type = struct_type {.x: %i32} [concrete] // CHECK:STDOUT: %complete_type.1ec: = complete_type_witness %struct_type.x.ed6 [concrete] // CHECK:STDOUT: %C: type = class_type @C [concrete] // CHECK:STDOUT: %HasF.type.a3d: type = facet_type <@HasF, @HasF(%Param)> [concrete] // CHECK:STDOUT: %HasF.impl_witness: = impl_witness @C.as.HasF.impl.%HasF.impl_witness_table [concrete] // CHECK:STDOUT: %Self.5e8: %HasF.type.a3d = symbolic_binding Self, 1 [symbolic] // CHECK:STDOUT: %HasF.WithSelf.F.type.a94: type = fn_type @HasF.WithSelf.F, @HasF.WithSelf(%Param, %Self.c1f) [symbolic] // CHECK:STDOUT: %HasF.WithSelf.F.1c0: %HasF.WithSelf.F.type.a94 = struct_value () [symbolic] // CHECK:STDOUT: %HasF.assoc_type.078: type = assoc_entity_type @HasF, @HasF(%Param) [concrete] // CHECK:STDOUT: %assoc0.22c: %HasF.assoc_type.078 = assoc_entity element0, @HasF.WithSelf.%HasF.WithSelf.F.decl [concrete] // CHECK:STDOUT: %.436: Core.Form = init_form %Param [concrete] // CHECK:STDOUT: %pattern_type.9ea: type = pattern_type %Param [concrete] // CHECK:STDOUT: %C.as.HasF.impl.F.type: type = fn_type @C.as.HasF.impl.F [concrete] // CHECK:STDOUT: %C.as.HasF.impl.F: %C.as.HasF.impl.F.type = struct_value () [concrete] // CHECK:STDOUT: %HasF.facet: %HasF.type.a3d = facet_value %C, (%HasF.impl_witness) [concrete] // CHECK:STDOUT: %HasF.WithSelf.F.type.a36: type = fn_type @HasF.WithSelf.F, @HasF.WithSelf(%Param, %HasF.facet) [concrete] // CHECK:STDOUT: %HasF.WithSelf.F.753: %HasF.WithSelf.F.type.a36 = struct_value () [concrete] // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %complete_type.357: = complete_type_witness %empty_struct_type [concrete] // CHECK:STDOUT: %int_2.ecc: Core.IntLiteral = int_value 2 [concrete] // CHECK:STDOUT: %struct_type.x.c96: type = struct_type {.x: Core.IntLiteral} [concrete] // CHECK:STDOUT: %struct: %struct_type.x.c96 = struct_value (%int_2.ecc) [concrete] // CHECK:STDOUT: %ImplicitAs.type.cc7: type = generic_interface_type @ImplicitAs [concrete] // CHECK:STDOUT: %ImplicitAs.generic: %ImplicitAs.type.cc7 = struct_value () [concrete] // CHECK:STDOUT: %ImplicitAs.type.e8c: type = facet_type <@ImplicitAs, @ImplicitAs(%i32)> [concrete] // CHECK:STDOUT: %To: Core.IntLiteral = symbolic_binding To, 0 [symbolic] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.4e6: type = fn_type @Core.IntLiteral.as.ImplicitAs.impl.Convert, @Core.IntLiteral.as.ImplicitAs.impl(%To) [symbolic] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.3c2: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.4e6 = struct_value () [symbolic] // CHECK:STDOUT: %ImplicitAs.impl_witness.6bc: = impl_witness imports.%ImplicitAs.impl_witness_table.74f, @Core.IntLiteral.as.ImplicitAs.impl(%int_32) [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.e0d: type = fn_type @Core.IntLiteral.as.ImplicitAs.impl.Convert, @Core.IntLiteral.as.ImplicitAs.impl(%int_32) [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.e0d = struct_value () [concrete] // CHECK:STDOUT: %ImplicitAs.facet: %ImplicitAs.type.e8c = facet_value Core.IntLiteral, (%ImplicitAs.impl_witness.6bc) [concrete] // CHECK:STDOUT: %ImplicitAs.WithSelf.Convert.type.b37: type = fn_type @ImplicitAs.WithSelf.Convert, @ImplicitAs.WithSelf(%i32, %ImplicitAs.facet) [concrete] // CHECK:STDOUT: %.545: type = fn_type_with_self_type %ImplicitAs.WithSelf.Convert.type.b37, %ImplicitAs.facet [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.bound: = bound_method %int_2.ecc, %Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5 [concrete] // CHECK:STDOUT: %pattern_type.7ce: type = pattern_type %i32 [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn: = specific_function %Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5, @Core.IntLiteral.as.ImplicitAs.impl.Convert(%int_32) [concrete] // CHECK:STDOUT: %bound_method: = bound_method %int_2.ecc, %Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn [concrete] // CHECK:STDOUT: %int_2.ef8: %i32 = int_value 2 [concrete] // CHECK:STDOUT: %Param.val: %Param = struct_value (%int_2.ef8) [concrete] // CHECK:STDOUT: %pattern_type.7c7: type = pattern_type %C [concrete] // CHECK:STDOUT: %G.type: type = fn_type @G [concrete] // CHECK:STDOUT: %G: %G.type = struct_value () [concrete] // CHECK:STDOUT: %C.type.facet: %type = facet_value %C, () [concrete] // CHECK:STDOUT: %HasF.WithSelf.F.type.70d: type = fn_type @HasF.WithSelf.F, @HasF.WithSelf(%Param, %C.type.facet) [concrete] // CHECK:STDOUT: %HasF.WithSelf.F.489: %HasF.WithSelf.F.type.70d = struct_value () [concrete] // CHECK:STDOUT: %.095: type = fn_type_with_self_type %HasF.WithSelf.F.type.a36, %HasF.facet [concrete] // CHECK:STDOUT: %Copy.type: type = facet_type <@Copy> [concrete] // CHECK:STDOUT: %Int.as.Copy.impl.Op.type.824: type = fn_type @Int.as.Copy.impl.Op, @Int.as.Copy.impl(%N) [symbolic] // CHECK:STDOUT: %Int.as.Copy.impl.Op.9b9: %Int.as.Copy.impl.Op.type.824 = struct_value () [symbolic] // CHECK:STDOUT: %Copy.impl_witness.f17: = impl_witness imports.%Copy.impl_witness_table.e76, @Int.as.Copy.impl(%int_32) [concrete] // CHECK:STDOUT: %Int.as.Copy.impl.Op.type.546: type = fn_type @Int.as.Copy.impl.Op, @Int.as.Copy.impl(%int_32) [concrete] // CHECK:STDOUT: %Int.as.Copy.impl.Op.664: %Int.as.Copy.impl.Op.type.546 = struct_value () [concrete] // CHECK:STDOUT: %Copy.facet: %Copy.type = facet_value %i32, (%Copy.impl_witness.f17) [concrete] // CHECK:STDOUT: %Copy.WithSelf.Op.type.081: type = fn_type @Copy.WithSelf.Op, @Copy.WithSelf(%Copy.facet) [concrete] // CHECK:STDOUT: %.8e2: type = fn_type_with_self_type %Copy.WithSelf.Op.type.081, %Copy.facet [concrete] // CHECK:STDOUT: %Int.as.Copy.impl.Op.specific_fn: = specific_function %Int.as.Copy.impl.Op.664, @Int.as.Copy.impl.Op(%int_32) [concrete] // CHECK:STDOUT: %Destroy.type: type = facet_type <@Destroy> [concrete] // CHECK:STDOUT: %Destroy.Op.type.bae255.1: type = fn_type @Destroy.Op.loc22_27 [concrete] // CHECK:STDOUT: %Destroy.Op.651ba6.1: %Destroy.Op.type.bae255.1 = struct_value () [concrete] // CHECK:STDOUT: %Destroy.Op.type.bae255.2: type = fn_type @Destroy.Op.loc22_3 [concrete] // CHECK:STDOUT: %Destroy.Op.651ba6.2: %Destroy.Op.type.bae255.2 = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: .Int = %Core.Int // CHECK:STDOUT: .ImplicitAs = %Core.ImplicitAs // CHECK:STDOUT: .Copy = %Core.Copy // CHECK:STDOUT: .Destroy = %Core.Destroy // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: %Core.Int: %Int.type = import_ref Core//prelude/parts/int, Int, loaded [concrete = constants.%Int.generic] // CHECK:STDOUT: %Core.ImplicitAs: %ImplicitAs.type.cc7 = import_ref Core//prelude/parts/as, ImplicitAs, loaded [concrete = constants.%ImplicitAs.generic] // CHECK:STDOUT: %Core.import_ref.42d: @Core.IntLiteral.as.ImplicitAs.impl.%Core.IntLiteral.as.ImplicitAs.impl.Convert.type (%Core.IntLiteral.as.ImplicitAs.impl.Convert.type.4e6) = import_ref Core//prelude/parts/int, loc{{\d+_\d+}}, loaded [symbolic = @Core.IntLiteral.as.ImplicitAs.impl.%Core.IntLiteral.as.ImplicitAs.impl.Convert (constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.3c2)] // CHECK:STDOUT: %ImplicitAs.impl_witness_table.74f = impl_witness_table (%Core.import_ref.42d), @Core.IntLiteral.as.ImplicitAs.impl [concrete] // CHECK:STDOUT: %Core.Copy: type = import_ref Core//prelude/parts/copy, Copy, loaded [concrete = constants.%Copy.type] // CHECK:STDOUT: %Core.import_ref.18d: @Int.as.Copy.impl.%Int.as.Copy.impl.Op.type (%Int.as.Copy.impl.Op.type.824) = import_ref Core//prelude/parts/int, loc{{\d+_\d+}}, loaded [symbolic = @Int.as.Copy.impl.%Int.as.Copy.impl.Op (constants.%Int.as.Copy.impl.Op.9b9)] // CHECK:STDOUT: %Copy.impl_witness_table.e76 = impl_witness_table (%Core.import_ref.18d), @Int.as.Copy.impl [concrete] // CHECK:STDOUT: %Core.Destroy: type = import_ref Core//prelude/parts/destroy, Destroy, loaded [concrete = constants.%Destroy.type] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .HasF = %HasF.decl // CHECK:STDOUT: .Param = %Param.decl // CHECK:STDOUT: .C = %C.decl // CHECK:STDOUT: .G = %G.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %HasF.decl: %HasF.type.ee1 = interface_decl @HasF [concrete = constants.%HasF.generic] { // CHECK:STDOUT: %T.patt: %pattern_type.98f = symbolic_binding_pattern T, 0 [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %.loc4_20.1: type = splice_block %.loc4_20.2 [concrete = type] { // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %.loc4_20.2: type = type_literal type [concrete = type] // CHECK:STDOUT: } // CHECK:STDOUT: %T.loc4_16.2: type = symbolic_binding T, 0 [symbolic = %T.loc4_16.1 (constants.%T.67d)] // CHECK:STDOUT: } // CHECK:STDOUT: %Param.decl: type = class_decl @Param [concrete = constants.%Param] {} {} // CHECK:STDOUT: %C.decl: type = class_decl @C [concrete = constants.%C] {} {} // CHECK:STDOUT: %G.decl: %G.type = fn_decl @G [concrete = constants.%G] { // CHECK:STDOUT: %c.patt: %pattern_type.7c7 = value_binding_pattern c [concrete] // CHECK:STDOUT: %c.param_patt: %pattern_type.7c7 = value_param_pattern %c.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %c.param: %C = value_param call_param0 // CHECK:STDOUT: %C.ref.loc20: type = name_ref C, file.%C.decl [concrete = constants.%C] // CHECK:STDOUT: %c: %C = value_binding c, %c.param // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic interface @HasF(%T.loc4_16.2: type) { // CHECK:STDOUT: %T.loc4_16.1: type = symbolic_binding T, 0 [symbolic = %T.loc4_16.1 (constants.%T.67d)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %HasF.type: type = facet_type <@HasF, @HasF(%T.loc4_16.1)> [symbolic = %HasF.type (constants.%HasF.type.8a4)] // CHECK:STDOUT: %Self.loc4_26.2: @HasF.%HasF.type (%HasF.type.8a4) = symbolic_binding Self, 1 [symbolic = %Self.loc4_26.2 (constants.%Self.c1f)] // CHECK:STDOUT: // CHECK:STDOUT: interface { // CHECK:STDOUT: %Self.loc4_26.1: @HasF.%HasF.type (%HasF.type.8a4) = symbolic_binding Self, 1 [symbolic = %Self.loc4_26.2 (constants.%Self.c1f)] // CHECK:STDOUT: %HasF.WithSelf.decl = interface_with_self_decl @HasF [concrete] // CHECK:STDOUT: // CHECK:STDOUT: !with Self: // CHECK:STDOUT: %HasF.WithSelf.F.decl: @HasF.WithSelf.%HasF.WithSelf.F.type (%HasF.WithSelf.F.type.246) = fn_decl @HasF.WithSelf.F [symbolic = @HasF.WithSelf.%HasF.WithSelf.F (constants.%HasF.WithSelf.F.58b)] { // CHECK:STDOUT: %return.patt: @HasF.WithSelf.F.%pattern_type (%pattern_type.51d1c4.1) = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: @HasF.WithSelf.F.%pattern_type (%pattern_type.51d1c4.1) = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %T.ref: type = name_ref T, @HasF.%T.loc4_16.2 [symbolic = %T (constants.%T.67d)] // CHECK:STDOUT: %.loc5_13.2: Core.Form = init_form %T.ref [symbolic = %.loc5_13.1 (constants.%.184347.1)] // CHECK:STDOUT: %return.param: ref @HasF.WithSelf.F.%T (%T.67d) = out_param call_param0 // CHECK:STDOUT: %return: ref @HasF.WithSelf.F.%T (%T.67d) = return_slot %return.param // CHECK:STDOUT: } // CHECK:STDOUT: %assoc0.loc5_14.1: @HasF.WithSelf.%HasF.assoc_type (%HasF.assoc_type.196) = assoc_entity element0, %HasF.WithSelf.F.decl [symbolic = %assoc0.loc5_14.2 (constants.%assoc0.b54)] // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = %Self.loc4_26.1 // CHECK:STDOUT: .T = // CHECK:STDOUT: .T = // CHECK:STDOUT: .F = @HasF.WithSelf.%assoc0.loc5_14.1 // CHECK:STDOUT: .Param = // CHECK:STDOUT: witness = (@HasF.WithSelf.%HasF.WithSelf.F.decl) // CHECK:STDOUT: // CHECK:STDOUT: !requires: // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: impl @C.as.HasF.impl: %Self.ref as %HasF.type { // CHECK:STDOUT: %C.as.HasF.impl.F.decl: %C.as.HasF.impl.F.type = fn_decl @C.as.HasF.impl.F [concrete = constants.%C.as.HasF.impl.F] { // CHECK:STDOUT: %return.patt: %pattern_type.9ea = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: %pattern_type.9ea = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %Param.ref: type = name_ref Param, file.%Param.decl [concrete = constants.%Param] // CHECK:STDOUT: %.loc14: Core.Form = init_form %Param.ref [concrete = constants.%.436] // CHECK:STDOUT: %return.param: ref %Param = out_param call_param0 // CHECK:STDOUT: %return: ref %Param = return_slot %return.param // CHECK:STDOUT: } // CHECK:STDOUT: %HasF.impl_witness_table = impl_witness_table (%C.as.HasF.impl.F.decl), @C.as.HasF.impl [concrete] // CHECK:STDOUT: %HasF.impl_witness: = impl_witness %HasF.impl_witness_table [concrete = constants.%HasF.impl_witness] // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Param = // CHECK:STDOUT: .F = %C.as.HasF.impl.F.decl // CHECK:STDOUT: witness = %HasF.impl_witness // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @Param { // CHECK:STDOUT: %i32: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %.loc9: %Param.elem = field_decl x, element0 [concrete] // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%struct_type.x.ed6 [concrete = constants.%complete_type.1ec] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%Param // CHECK:STDOUT: .x = %.loc9 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @C { // CHECK:STDOUT: impl_decl @C.as.HasF.impl [concrete] {} { // CHECK:STDOUT: %Self.ref: type = name_ref Self, constants.%C [concrete = constants.%C] // CHECK:STDOUT: %HasF.ref: %HasF.type.ee1 = name_ref HasF, file.%HasF.decl [concrete = constants.%HasF.generic] // CHECK:STDOUT: %Param.ref: type = name_ref Param, file.%Param.decl [concrete = constants.%Param] // CHECK:STDOUT: %HasF.type: type = facet_type <@HasF, @HasF(constants.%Param)> [concrete = constants.%HasF.type.a3d] // CHECK:STDOUT: } // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type.357] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%C // CHECK:STDOUT: .HasF = // CHECK:STDOUT: .Param = // CHECK:STDOUT: .F = // CHECK:STDOUT: extend @C.as.HasF.impl.%HasF.type // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic fn @HasF.WithSelf.F(@HasF.%T.loc4_16.2: type, @HasF.%Self.loc4_26.1: @HasF.%HasF.type (%HasF.type.8a4)) { // CHECK:STDOUT: %T: type = symbolic_binding T, 0 [symbolic = %T (constants.%T.67d)] // CHECK:STDOUT: %.loc5_13.1: Core.Form = init_form %T [symbolic = %.loc5_13.1 (constants.%.184347.1)] // CHECK:STDOUT: %pattern_type: type = pattern_type %T [symbolic = %pattern_type (constants.%pattern_type.51d1c4.1)] // CHECK:STDOUT: // CHECK:STDOUT: fn() -> out %return.param: @HasF.WithSelf.F.%T (%T.67d); // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @C.as.HasF.impl.F() -> out %return.param: %Param { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %int_2: Core.IntLiteral = int_value 2 [concrete = constants.%int_2.ecc] // CHECK:STDOUT: %.loc15_21.1: %struct_type.x.c96 = struct_literal (%int_2) [concrete = constants.%struct] // CHECK:STDOUT: %impl.elem0: %.545 = impl_witness_access constants.%ImplicitAs.impl_witness.6bc, element0 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5] // CHECK:STDOUT: %bound_method.loc15_21.1: = bound_method %int_2, %impl.elem0 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.bound] // CHECK:STDOUT: %specific_fn: = specific_function %impl.elem0, @Core.IntLiteral.as.ImplicitAs.impl.Convert(constants.%int_32) [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn] // CHECK:STDOUT: %bound_method.loc15_21.2: = bound_method %int_2, %specific_fn [concrete = constants.%bound_method] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.call: init %i32 = call %bound_method.loc15_21.2(%int_2) [concrete = constants.%int_2.ef8] // CHECK:STDOUT: %.loc15_21.2: init %i32 = converted %int_2, %Core.IntLiteral.as.ImplicitAs.impl.Convert.call [concrete = constants.%int_2.ef8] // CHECK:STDOUT: %.loc15_21.3: ref %i32 = class_element_access %return.param, element0 // CHECK:STDOUT: %.loc15_21.4: init %i32 to %.loc15_21.3 = in_place_init %.loc15_21.2 [concrete = constants.%int_2.ef8] // CHECK:STDOUT: %.loc15_21.5: init %Param to %return.param = class_init (%.loc15_21.4) [concrete = constants.%Param.val] // CHECK:STDOUT: %.loc15_22: init %Param = converted %.loc15_21.1, %.loc15_21.5 [concrete = constants.%Param.val] // CHECK:STDOUT: return %.loc15_22 to %return.param // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @G(%c.param: %C) { // CHECK:STDOUT: !entry: // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %a.patt: %pattern_type.7ce = value_binding_pattern a [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %C.ref.loc21: type = name_ref C, file.%C.decl [concrete = constants.%C] // CHECK:STDOUT: %.loc21_24: %HasF.assoc_type.078 = specific_constant @HasF.WithSelf.%assoc0.loc5_14.1, @HasF.WithSelf(constants.%Param, constants.%C.type.facet) [concrete = constants.%assoc0.22c] // CHECK:STDOUT: %F.ref.loc21: %HasF.assoc_type.078 = name_ref F, %.loc21_24 [concrete = constants.%assoc0.22c] // CHECK:STDOUT: %impl.elem0.loc21: %.095 = impl_witness_access constants.%HasF.impl_witness, element0 [concrete = constants.%C.as.HasF.impl.F] // CHECK:STDOUT: %.loc21_27.1: ref %Param = temporary_storage // CHECK:STDOUT: %C.as.HasF.impl.F.call.loc21: init %Param to %.loc21_27.1 = call %impl.elem0.loc21() // CHECK:STDOUT: %.loc21_27.2: ref %Param = temporary %.loc21_27.1, %C.as.HasF.impl.F.call.loc21 // CHECK:STDOUT: %x.ref.loc21: %Param.elem = name_ref x, @Param.%.loc9 [concrete = @Param.%.loc9] // CHECK:STDOUT: %.loc21_28.1: ref %i32 = class_element_access %.loc21_27.2, element0 // CHECK:STDOUT: %i32.loc21: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %.loc21_28.2: %i32 = acquire_value %.loc21_28.1 // CHECK:STDOUT: %a: %i32 = value_binding a, %.loc21_28.2 // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %b.patt: %pattern_type.7ce = ref_binding_pattern b [concrete] // CHECK:STDOUT: %b.var_patt: %pattern_type.7ce = var_pattern %b.patt [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %b.var: ref %i32 = var %b.var_patt // CHECK:STDOUT: %c.ref: %C = name_ref c, %c // CHECK:STDOUT: %.loc22_24: %HasF.assoc_type.078 = specific_constant @HasF.WithSelf.%assoc0.loc5_14.1, @HasF.WithSelf(constants.%Param, constants.%C.type.facet) [concrete = constants.%assoc0.22c] // CHECK:STDOUT: %F.ref.loc22: %HasF.assoc_type.078 = name_ref F, %.loc22_24 [concrete = constants.%assoc0.22c] // CHECK:STDOUT: %impl.elem0.loc22_24: %.095 = impl_witness_access constants.%HasF.impl_witness, element0 [concrete = constants.%C.as.HasF.impl.F] // CHECK:STDOUT: %.loc22_27.1: ref %Param = temporary_storage // CHECK:STDOUT: %C.as.HasF.impl.F.call.loc22: init %Param to %.loc22_27.1 = call %impl.elem0.loc22_24() // CHECK:STDOUT: %.loc22_27.2: ref %Param = temporary %.loc22_27.1, %C.as.HasF.impl.F.call.loc22 // CHECK:STDOUT: %x.ref.loc22: %Param.elem = name_ref x, @Param.%.loc9 [concrete = @Param.%.loc9] // CHECK:STDOUT: %.loc22_28.1: ref %i32 = class_element_access %.loc22_27.2, element0 // CHECK:STDOUT: %.loc22_28.2: %i32 = acquire_value %.loc22_28.1 // CHECK:STDOUT: %impl.elem0.loc22_28: %.8e2 = impl_witness_access constants.%Copy.impl_witness.f17, element0 [concrete = constants.%Int.as.Copy.impl.Op.664] // CHECK:STDOUT: %bound_method.loc22_28.1: = bound_method %.loc22_28.2, %impl.elem0.loc22_28 // CHECK:STDOUT: %specific_fn: = specific_function %impl.elem0.loc22_28, @Int.as.Copy.impl.Op(constants.%int_32) [concrete = constants.%Int.as.Copy.impl.Op.specific_fn] // CHECK:STDOUT: %bound_method.loc22_28.2: = bound_method %.loc22_28.2, %specific_fn // CHECK:STDOUT: %Int.as.Copy.impl.Op.call: init %i32 = call %bound_method.loc22_28.2(%.loc22_28.2) // CHECK:STDOUT: assign %b.var, %Int.as.Copy.impl.Op.call // CHECK:STDOUT: %i32.loc22: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %b: ref %i32 = ref_binding b, %b.var // CHECK:STDOUT: %Destroy.Op.bound.loc22_27: = bound_method %.loc22_27.2, constants.%Destroy.Op.651ba6.1 // CHECK:STDOUT: %Destroy.Op.call.loc22_27: init %empty_tuple.type = call %Destroy.Op.bound.loc22_27(%.loc22_27.2) // CHECK:STDOUT: %Destroy.Op.bound.loc22_3: = bound_method %b.var, constants.%Destroy.Op.651ba6.2 // CHECK:STDOUT: %Destroy.Op.call.loc22_3: init %empty_tuple.type = call %Destroy.Op.bound.loc22_3(%b.var) // CHECK:STDOUT: %Destroy.Op.bound.loc21: = bound_method %.loc21_27.2, constants.%Destroy.Op.651ba6.1 // CHECK:STDOUT: %Destroy.Op.call.loc21: init %empty_tuple.type = call %Destroy.Op.bound.loc21(%.loc21_27.2) // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @Destroy.Op.loc22_27(%self.param: ref %Param) = "no_op"; // CHECK:STDOUT: // CHECK:STDOUT: fn @Destroy.Op.loc22_3(%self.param: ref %i32) = "no_op"; // CHECK:STDOUT: // CHECK:STDOUT: specific @HasF(constants.%T.67d) { // CHECK:STDOUT: %T.loc4_16.1 => constants.%T.67d // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @HasF.WithSelf(constants.%T.67d, constants.%Self.c1f) {} // CHECK:STDOUT: // CHECK:STDOUT: specific @HasF.WithSelf.F(constants.%T.67d, constants.%Self.c1f) { // CHECK:STDOUT: %T => constants.%T.67d // CHECK:STDOUT: %.loc5_13.1 => constants.%.184347.1 // CHECK:STDOUT: %pattern_type => constants.%pattern_type.51d1c4.1 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @HasF(constants.%Param) { // CHECK:STDOUT: %T.loc4_16.1 => constants.%Param // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %HasF.type => constants.%HasF.type.a3d // CHECK:STDOUT: %Self.loc4_26.2 => constants.%Self.5e8 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @HasF.WithSelf(constants.%Param, constants.%Self.c1f) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: %T => constants.%Param // CHECK:STDOUT: %HasF.type => constants.%HasF.type.a3d // CHECK:STDOUT: %Self => constants.%Self.c1f // CHECK:STDOUT: %HasF.WithSelf.F.type => constants.%HasF.WithSelf.F.type.a94 // CHECK:STDOUT: %HasF.WithSelf.F => constants.%HasF.WithSelf.F.1c0 // CHECK:STDOUT: %HasF.assoc_type => constants.%HasF.assoc_type.078 // CHECK:STDOUT: %assoc0.loc5_14.2 => constants.%assoc0.22c // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @HasF.WithSelf(constants.%Param, constants.%HasF.facet) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: %T => constants.%Param // CHECK:STDOUT: %HasF.type => constants.%HasF.type.a3d // CHECK:STDOUT: %Self => constants.%HasF.facet // CHECK:STDOUT: %HasF.WithSelf.F.type => constants.%HasF.WithSelf.F.type.a36 // CHECK:STDOUT: %HasF.WithSelf.F => constants.%HasF.WithSelf.F.753 // CHECK:STDOUT: %HasF.assoc_type => constants.%HasF.assoc_type.078 // CHECK:STDOUT: %assoc0.loc5_14.2 => constants.%assoc0.22c // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @HasF.WithSelf.F(constants.%Param, constants.%HasF.facet) { // CHECK:STDOUT: %T => constants.%Param // CHECK:STDOUT: %.loc5_13.1 => constants.%.436 // CHECK:STDOUT: %pattern_type => constants.%pattern_type.9ea // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @HasF.WithSelf(constants.%Param, constants.%C.type.facet) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: %T => constants.%Param // CHECK:STDOUT: %HasF.type => constants.%HasF.type.a3d // CHECK:STDOUT: %Self => constants.%C.type.facet // CHECK:STDOUT: %HasF.WithSelf.F.type => constants.%HasF.WithSelf.F.type.70d // CHECK:STDOUT: %HasF.WithSelf.F => constants.%HasF.WithSelf.F.489 // CHECK:STDOUT: %HasF.assoc_type => constants.%HasF.assoc_type.078 // CHECK:STDOUT: %assoc0.loc5_14.2 => constants.%assoc0.22c // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- extend_impl_generic_class.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %type: type = facet_type [concrete] // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self] // CHECK:STDOUT: %pattern_type.98f: type = pattern_type type [concrete] // CHECK:STDOUT: %T: type = symbolic_binding T, 0 [symbolic] // CHECK:STDOUT: %I.type.609: type = generic_interface_type @I [concrete] // CHECK:STDOUT: %I.generic: %I.type.609 = struct_value () [concrete] // CHECK:STDOUT: %I.type.1ab3e4.1: type = facet_type <@I, @I(%T)> [symbolic] // CHECK:STDOUT: %Self.fdb544.1: %I.type.1ab3e4.1 = symbolic_binding Self, 1 [symbolic] // CHECK:STDOUT: %Self.binding.as_type: type = symbolic_binding_type Self, 1, %Self.fdb544.1 [symbolic] // CHECK:STDOUT: %pattern_type.a8a: type = pattern_type %Self.binding.as_type [symbolic] // CHECK:STDOUT: %pattern_type.51d1c4.1: type = pattern_type %T [symbolic] // CHECK:STDOUT: %I.WithSelf.F.type.1d69d8.1: type = fn_type @I.WithSelf.F, @I.WithSelf(%T, %Self.fdb544.1) [symbolic] // CHECK:STDOUT: %I.WithSelf.F.cd4fc9.1: %I.WithSelf.F.type.1d69d8.1 = struct_value () [symbolic] // CHECK:STDOUT: %I.assoc_type.76c031.1: type = assoc_entity_type @I, @I(%T) [symbolic] // CHECK:STDOUT: %assoc0.203667.1: %I.assoc_type.76c031.1 = assoc_entity element0, @I.WithSelf.%I.WithSelf.F.decl [symbolic] // CHECK:STDOUT: %U: type = symbolic_binding U, 0 [symbolic] // CHECK:STDOUT: %X.type: type = generic_class_type @X [concrete] // CHECK:STDOUT: %X.generic: %X.type = struct_value () [concrete] // CHECK:STDOUT: %X: type = class_type @X, @X(%U) [symbolic] // CHECK:STDOUT: %I.type.1ab3e4.2: type = facet_type <@I, @I(%U)> [symbolic] // CHECK:STDOUT: %I.impl_witness: = impl_witness @X.as.I.impl.%I.impl_witness_table, @X.as.I.impl(%U) [symbolic] // CHECK:STDOUT: %Self.fdb544.2: %I.type.1ab3e4.2 = symbolic_binding Self, 1 [symbolic] // CHECK:STDOUT: %I.WithSelf.F.type.1d69d8.2: type = fn_type @I.WithSelf.F, @I.WithSelf(%U, %Self.fdb544.1) [symbolic] // CHECK:STDOUT: %I.WithSelf.F.cd4fc9.2: %I.WithSelf.F.type.1d69d8.2 = struct_value () [symbolic] // CHECK:STDOUT: %I.assoc_type.76c031.2: type = assoc_entity_type @I, @I(%U) [symbolic] // CHECK:STDOUT: %assoc0.203667.2: %I.assoc_type.76c031.2 = assoc_entity element0, @I.WithSelf.%I.WithSelf.F.decl [symbolic] // CHECK:STDOUT: %require_complete.e36: = require_complete_type %I.type.1ab3e4.2 [symbolic] // CHECK:STDOUT: %pattern_type.e07: type = pattern_type %X [symbolic] // CHECK:STDOUT: %pattern_type.51d1c4.2: type = pattern_type %U [symbolic] // CHECK:STDOUT: %X.as.I.impl.F.type: type = fn_type @X.as.I.impl.F, @X.as.I.impl(%U) [symbolic] // CHECK:STDOUT: %X.as.I.impl.F: %X.as.I.impl.F.type = struct_value () [symbolic] // CHECK:STDOUT: %I.facet: %I.type.1ab3e4.2 = facet_value %X, (%I.impl_witness) [symbolic] // CHECK:STDOUT: %I.WithSelf.F.type.94a: type = fn_type @I.WithSelf.F, @I.WithSelf(%U, %I.facet) [symbolic] // CHECK:STDOUT: %I.WithSelf.F.8fe: %I.WithSelf.F.type.94a = struct_value () [symbolic] // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %complete_type: = complete_type_witness %empty_struct_type [concrete] // CHECK:STDOUT: %require_complete.5dd: = require_complete_type %X [symbolic] // CHECK:STDOUT: %require_complete.944: = require_complete_type %U [symbolic] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .I = %I.decl // CHECK:STDOUT: .X = %X.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %I.decl: %I.type.609 = interface_decl @I [concrete = constants.%I.generic] { // CHECK:STDOUT: %T.patt: %pattern_type.98f = symbolic_binding_pattern T, 0 [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %.loc4_17.1: type = splice_block %.loc4_17.2 [concrete = type] { // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %.loc4_17.2: type = type_literal type [concrete = type] // CHECK:STDOUT: } // CHECK:STDOUT: %T.loc4_13.2: type = symbolic_binding T, 0 [symbolic = %T.loc4_13.1 (constants.%T)] // CHECK:STDOUT: } // CHECK:STDOUT: %X.decl: %X.type = class_decl @X [concrete = constants.%X.generic] { // CHECK:STDOUT: %U.patt: %pattern_type.98f = symbolic_binding_pattern U, 0 [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %.loc8_13.1: type = splice_block %.loc8_13.2 [concrete = type] { // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %.loc8_13.2: type = type_literal type [concrete = type] // CHECK:STDOUT: } // CHECK:STDOUT: %U.loc8_9.2: type = symbolic_binding U, 0 [symbolic = %U.loc8_9.1 (constants.%U)] // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic interface @I(%T.loc4_13.2: type) { // CHECK:STDOUT: %T.loc4_13.1: type = symbolic_binding T, 0 [symbolic = %T.loc4_13.1 (constants.%T)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %I.type: type = facet_type <@I, @I(%T.loc4_13.1)> [symbolic = %I.type (constants.%I.type.1ab3e4.1)] // CHECK:STDOUT: %Self.loc4_23.2: @I.%I.type (%I.type.1ab3e4.1) = symbolic_binding Self, 1 [symbolic = %Self.loc4_23.2 (constants.%Self.fdb544.1)] // CHECK:STDOUT: // CHECK:STDOUT: interface { // CHECK:STDOUT: %Self.loc4_23.1: @I.%I.type (%I.type.1ab3e4.1) = symbolic_binding Self, 1 [symbolic = %Self.loc4_23.2 (constants.%Self.fdb544.1)] // CHECK:STDOUT: %I.WithSelf.decl = interface_with_self_decl @I [concrete] // CHECK:STDOUT: // CHECK:STDOUT: !with Self: // CHECK:STDOUT: %I.WithSelf.F.decl: @I.WithSelf.%I.WithSelf.F.type (%I.WithSelf.F.type.1d69d8.1) = fn_decl @I.WithSelf.F [symbolic = @I.WithSelf.%I.WithSelf.F (constants.%I.WithSelf.F.cd4fc9.1)] { // CHECK:STDOUT: %self.patt: @I.WithSelf.F.%pattern_type.loc5_8 (%pattern_type.a8a) = value_binding_pattern self [concrete] // CHECK:STDOUT: %self.param_patt: @I.WithSelf.F.%pattern_type.loc5_8 (%pattern_type.a8a) = value_param_pattern %self.patt [concrete] // CHECK:STDOUT: %t.patt: @I.WithSelf.F.%pattern_type.loc5_20 (%pattern_type.51d1c4.1) = value_binding_pattern t [concrete] // CHECK:STDOUT: %t.param_patt: @I.WithSelf.F.%pattern_type.loc5_20 (%pattern_type.51d1c4.1) = value_param_pattern %t.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %self.param: @I.WithSelf.F.%Self.binding.as_type (%Self.binding.as_type) = value_param call_param0 // CHECK:STDOUT: %.loc5_14.1: type = splice_block %.loc5_14.3 [symbolic = %Self.binding.as_type (constants.%Self.binding.as_type)] { // CHECK:STDOUT: %.loc5_14.2: @I.WithSelf.F.%I.type (%I.type.1ab3e4.1) = specific_constant @I.%Self.loc4_23.1, @I(constants.%T) [symbolic = %Self (constants.%Self.fdb544.1)] // CHECK:STDOUT: %Self.ref: @I.WithSelf.F.%I.type (%I.type.1ab3e4.1) = name_ref Self, %.loc5_14.2 [symbolic = %Self (constants.%Self.fdb544.1)] // CHECK:STDOUT: %Self.as_type: type = facet_access_type %Self.ref [symbolic = %Self.binding.as_type (constants.%Self.binding.as_type)] // CHECK:STDOUT: %.loc5_14.3: type = converted %Self.ref, %Self.as_type [symbolic = %Self.binding.as_type (constants.%Self.binding.as_type)] // CHECK:STDOUT: } // CHECK:STDOUT: %self: @I.WithSelf.F.%Self.binding.as_type (%Self.binding.as_type) = value_binding self, %self.param // CHECK:STDOUT: %t.param: @I.WithSelf.F.%T (%T) = value_param call_param1 // CHECK:STDOUT: %T.ref: type = name_ref T, @I.%T.loc4_13.2 [symbolic = %T (constants.%T)] // CHECK:STDOUT: %t: @I.WithSelf.F.%T (%T) = value_binding t, %t.param // CHECK:STDOUT: } // CHECK:STDOUT: %assoc0.loc5_25.1: @I.WithSelf.%I.assoc_type (%I.assoc_type.76c031.1) = assoc_entity element0, %I.WithSelf.F.decl [symbolic = %assoc0.loc5_25.2 (constants.%assoc0.203667.1)] // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = %Self.loc4_23.1 // CHECK:STDOUT: .T = // CHECK:STDOUT: .T = // CHECK:STDOUT: .F = @I.WithSelf.%assoc0.loc5_25.1 // CHECK:STDOUT: .U = // CHECK:STDOUT: witness = (@I.WithSelf.%I.WithSelf.F.decl) // CHECK:STDOUT: // CHECK:STDOUT: !requires: // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic impl @X.as.I.impl(@X.%U.loc8_9.2: type) { // CHECK:STDOUT: %U: type = symbolic_binding U, 0 [symbolic = %U (constants.%U)] // CHECK:STDOUT: %X: type = class_type @X, @X(%U) [symbolic = %X (constants.%X)] // CHECK:STDOUT: %I.type.loc9_21.1: type = facet_type <@I, @I(%U)> [symbolic = %I.type.loc9_21.1 (constants.%I.type.1ab3e4.2)] // CHECK:STDOUT: %I.impl_witness.loc9_23.2: = impl_witness %I.impl_witness_table, @X.as.I.impl(%U) [symbolic = %I.impl_witness.loc9_23.2 (constants.%I.impl_witness)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete: = require_complete_type %I.type.loc9_21.1 [symbolic = %require_complete (constants.%require_complete.e36)] // CHECK:STDOUT: %X.as.I.impl.F.type: type = fn_type @X.as.I.impl.F, @X.as.I.impl(%U) [symbolic = %X.as.I.impl.F.type (constants.%X.as.I.impl.F.type)] // CHECK:STDOUT: %X.as.I.impl.F: @X.as.I.impl.%X.as.I.impl.F.type (%X.as.I.impl.F.type) = struct_value () [symbolic = %X.as.I.impl.F (constants.%X.as.I.impl.F)] // CHECK:STDOUT: // CHECK:STDOUT: impl: %Self.ref as %I.type.loc9_21.2 { // CHECK:STDOUT: %X.as.I.impl.F.decl: @X.as.I.impl.%X.as.I.impl.F.type (%X.as.I.impl.F.type) = fn_decl @X.as.I.impl.F [symbolic = @X.as.I.impl.%X.as.I.impl.F (constants.%X.as.I.impl.F)] { // CHECK:STDOUT: %self.patt: @X.as.I.impl.F.%pattern_type.loc10_17 (%pattern_type.e07) = value_binding_pattern self [concrete] // CHECK:STDOUT: %self.param_patt: @X.as.I.impl.F.%pattern_type.loc10_17 (%pattern_type.e07) = value_param_pattern %self.patt [concrete] // CHECK:STDOUT: %t.patt: @X.as.I.impl.F.%pattern_type.loc10_36 (%pattern_type.51d1c4.2) = value_binding_pattern t [concrete] // CHECK:STDOUT: %t.param_patt: @X.as.I.impl.F.%pattern_type.loc10_36 (%pattern_type.51d1c4.2) = value_param_pattern %t.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %self.param: @X.as.I.impl.F.%X (%X) = value_param call_param0 // CHECK:STDOUT: %.loc10_23.1: type = splice_block %Self.ref [symbolic = %X (constants.%X)] { // CHECK:STDOUT: %.loc10_23.2: type = specific_constant constants.%X, @X(constants.%U) [symbolic = %X (constants.%X)] // CHECK:STDOUT: %Self.ref: type = name_ref Self, %.loc10_23.2 [symbolic = %X (constants.%X)] // CHECK:STDOUT: } // CHECK:STDOUT: %self: @X.as.I.impl.F.%X (%X) = value_binding self, %self.param // CHECK:STDOUT: %t.param: @X.as.I.impl.F.%U (%U) = value_param call_param1 // CHECK:STDOUT: %U.ref: type = name_ref U, @X.%U.loc8_9.2 [symbolic = %U (constants.%U)] // CHECK:STDOUT: %t: @X.as.I.impl.F.%U (%U) = value_binding t, %t.param // CHECK:STDOUT: } // CHECK:STDOUT: %I.impl_witness_table = impl_witness_table (%X.as.I.impl.F.decl), @X.as.I.impl [concrete] // CHECK:STDOUT: %I.impl_witness.loc9_23.1: = impl_witness %I.impl_witness_table, @X.as.I.impl(constants.%U) [symbolic = %I.impl_witness.loc9_23.2 (constants.%I.impl_witness)] // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .U = // CHECK:STDOUT: .F = %X.as.I.impl.F.decl // CHECK:STDOUT: witness = %I.impl_witness.loc9_23.1 // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic class @X(%U.loc8_9.2: type) { // CHECK:STDOUT: %U.loc8_9.1: type = symbolic_binding U, 0 [symbolic = %U.loc8_9.1 (constants.%U)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %I.type: type = facet_type <@I, @I(%U.loc8_9.1)> [symbolic = %I.type (constants.%I.type.1ab3e4.2)] // CHECK:STDOUT: %require_complete: = require_complete_type %I.type [symbolic = %require_complete (constants.%require_complete.e36)] // CHECK:STDOUT: // CHECK:STDOUT: class { // CHECK:STDOUT: impl_decl @X.as.I.impl [concrete] {} { // CHECK:STDOUT: %Self.ref: type = name_ref Self, constants.%X [symbolic = %X (constants.%X)] // CHECK:STDOUT: %I.ref: %I.type.609 = name_ref I, file.%I.decl [concrete = constants.%I.generic] // CHECK:STDOUT: %U.ref: type = name_ref U, @X.%U.loc8_9.2 [symbolic = %U (constants.%U)] // CHECK:STDOUT: %I.type.loc9_21.2: type = facet_type <@I, @I(constants.%U)> [symbolic = %I.type.loc9_21.1 (constants.%I.type.1ab3e4.2)] // CHECK:STDOUT: } // CHECK:STDOUT: %.loc9: type = specific_constant @X.as.I.impl.%I.type.loc9_21.2, @X.as.I.impl(constants.%U) [symbolic = %I.type (constants.%I.type.1ab3e4.2)] // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%X // CHECK:STDOUT: .I = // CHECK:STDOUT: .U = // CHECK:STDOUT: extend %.loc9 // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic fn @I.WithSelf.F(@I.%T.loc4_13.2: type, @I.%Self.loc4_23.1: @I.%I.type (%I.type.1ab3e4.1)) { // CHECK:STDOUT: %T: type = symbolic_binding T, 0 [symbolic = %T (constants.%T)] // CHECK:STDOUT: %I.type: type = facet_type <@I, @I(%T)> [symbolic = %I.type (constants.%I.type.1ab3e4.1)] // CHECK:STDOUT: %Self: @I.WithSelf.F.%I.type (%I.type.1ab3e4.1) = symbolic_binding Self, 1 [symbolic = %Self (constants.%Self.fdb544.1)] // CHECK:STDOUT: %Self.binding.as_type: type = symbolic_binding_type Self, 1, %Self [symbolic = %Self.binding.as_type (constants.%Self.binding.as_type)] // CHECK:STDOUT: %pattern_type.loc5_8: type = pattern_type %Self.binding.as_type [symbolic = %pattern_type.loc5_8 (constants.%pattern_type.a8a)] // CHECK:STDOUT: %pattern_type.loc5_20: type = pattern_type %T [symbolic = %pattern_type.loc5_20 (constants.%pattern_type.51d1c4.1)] // CHECK:STDOUT: // CHECK:STDOUT: fn(%self.param: @I.WithSelf.F.%Self.binding.as_type (%Self.binding.as_type), %t.param: @I.WithSelf.F.%T (%T)); // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic fn @X.as.I.impl.F(@X.%U.loc8_9.2: type) { // CHECK:STDOUT: %U: type = symbolic_binding U, 0 [symbolic = %U (constants.%U)] // CHECK:STDOUT: %X: type = class_type @X, @X(%U) [symbolic = %X (constants.%X)] // CHECK:STDOUT: %pattern_type.loc10_17: type = pattern_type %X [symbolic = %pattern_type.loc10_17 (constants.%pattern_type.e07)] // CHECK:STDOUT: %I.type: type = facet_type <@I, @I(%U)> [symbolic = %I.type (constants.%I.type.1ab3e4.2)] // CHECK:STDOUT: %require_complete.loc10_39: = require_complete_type %I.type [symbolic = %require_complete.loc10_39 (constants.%require_complete.e36)] // CHECK:STDOUT: %pattern_type.loc10_36: type = pattern_type %U [symbolic = %pattern_type.loc10_36 (constants.%pattern_type.51d1c4.2)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete.loc10_21: = require_complete_type %X [symbolic = %require_complete.loc10_21 (constants.%require_complete.5dd)] // CHECK:STDOUT: %require_complete.loc10_37: = require_complete_type %U [symbolic = %require_complete.loc10_37 (constants.%require_complete.944)] // CHECK:STDOUT: // CHECK:STDOUT: fn(%self.param: @X.as.I.impl.F.%X (%X), %t.param: @X.as.I.impl.F.%U (%U)) { // CHECK:STDOUT: !entry: // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @I(constants.%T) { // CHECK:STDOUT: %T.loc4_13.1 => constants.%T // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @I.WithSelf(constants.%T, constants.%Self.fdb544.1) {} // CHECK:STDOUT: // CHECK:STDOUT: specific @I.WithSelf.F(constants.%T, constants.%Self.fdb544.1) { // CHECK:STDOUT: %T => constants.%T // CHECK:STDOUT: %I.type => constants.%I.type.1ab3e4.1 // CHECK:STDOUT: %Self => constants.%Self.fdb544.1 // CHECK:STDOUT: %Self.binding.as_type => constants.%Self.binding.as_type // CHECK:STDOUT: %pattern_type.loc5_8 => constants.%pattern_type.a8a // CHECK:STDOUT: %pattern_type.loc5_20 => constants.%pattern_type.51d1c4.1 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @X(constants.%U) { // CHECK:STDOUT: %U.loc8_9.1 => constants.%U // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %I.type => constants.%I.type.1ab3e4.2 // CHECK:STDOUT: %require_complete => constants.%require_complete.e36 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @I(constants.%U) { // CHECK:STDOUT: %T.loc4_13.1 => constants.%U // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %I.type => constants.%I.type.1ab3e4.2 // CHECK:STDOUT: %Self.loc4_23.2 => constants.%Self.fdb544.2 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @X.as.I.impl(constants.%U) { // CHECK:STDOUT: %U => constants.%U // CHECK:STDOUT: %X => constants.%X // CHECK:STDOUT: %I.type.loc9_21.1 => constants.%I.type.1ab3e4.2 // CHECK:STDOUT: %I.impl_witness.loc9_23.2 => constants.%I.impl_witness // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete => constants.%require_complete.e36 // CHECK:STDOUT: %X.as.I.impl.F.type => constants.%X.as.I.impl.F.type // CHECK:STDOUT: %X.as.I.impl.F => constants.%X.as.I.impl.F // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @I.WithSelf(constants.%U, constants.%Self.fdb544.1) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: %T => constants.%U // CHECK:STDOUT: %I.type => constants.%I.type.1ab3e4.2 // CHECK:STDOUT: %Self => constants.%Self.fdb544.1 // CHECK:STDOUT: %I.WithSelf.F.type => constants.%I.WithSelf.F.type.1d69d8.2 // CHECK:STDOUT: %I.WithSelf.F => constants.%I.WithSelf.F.cd4fc9.2 // CHECK:STDOUT: %I.assoc_type => constants.%I.assoc_type.76c031.2 // CHECK:STDOUT: %assoc0.loc5_25.2 => constants.%assoc0.203667.2 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @X.as.I.impl.F(constants.%U) { // CHECK:STDOUT: %U => constants.%U // CHECK:STDOUT: %X => constants.%X // CHECK:STDOUT: %pattern_type.loc10_17 => constants.%pattern_type.e07 // CHECK:STDOUT: %I.type => constants.%I.type.1ab3e4.2 // CHECK:STDOUT: %require_complete.loc10_39 => constants.%require_complete.e36 // CHECK:STDOUT: %pattern_type.loc10_36 => constants.%pattern_type.51d1c4.2 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @I.WithSelf(constants.%U, constants.%I.facet) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: %T => constants.%U // CHECK:STDOUT: %I.type => constants.%I.type.1ab3e4.2 // CHECK:STDOUT: %Self => constants.%I.facet // CHECK:STDOUT: %I.WithSelf.F.type => constants.%I.WithSelf.F.type.94a // CHECK:STDOUT: %I.WithSelf.F => constants.%I.WithSelf.F.8fe // CHECK:STDOUT: %I.assoc_type => constants.%I.assoc_type.76c031.2 // CHECK:STDOUT: %assoc0.loc5_25.2 => constants.%assoc0.203667.2 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @I.WithSelf.F(constants.%U, constants.%I.facet) { // CHECK:STDOUT: %T => constants.%U // CHECK:STDOUT: %I.type => constants.%I.type.1ab3e4.2 // CHECK:STDOUT: %Self => constants.%I.facet // CHECK:STDOUT: %Self.binding.as_type => constants.%X // CHECK:STDOUT: %pattern_type.loc5_8 => constants.%pattern_type.e07 // CHECK:STDOUT: %pattern_type.loc5_20 => constants.%pattern_type.51d1c4.2 // CHECK:STDOUT: } // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/impl/fail_alias.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/none.carbon // TODO: Add ranges and switch to "--dump-sem-ir-ranges=only". // EXTRA-ARGS: --dump-sem-ir-ranges=if-present // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/impl/fail_alias.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/impl/fail_alias.carbon interface I {} class C {} alias AI = I; alias AC = C; impl AC as AI {} // CHECK:STDERR: fail_alias.carbon:[[@LINE+7]]:1: error: redefinition of `impl AC as AI` [ImplRedefinition] // CHECK:STDERR: impl AC as AI {} // CHECK:STDERR: ^~~~~~~~~~~~~~~ // CHECK:STDERR: fail_alias.carbon:[[@LINE-5]]:1: note: previous definition was here [ImplPreviousDefinition] // CHECK:STDERR: impl AC as AI {} // CHECK:STDERR: ^~~~~~~~~~~~~~~ // CHECK:STDERR: impl AC as AI {} // CHECK:STDOUT: --- fail_alias.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %I.type: type = facet_type <@I> [concrete] // CHECK:STDOUT: %Self: %I.type = symbolic_binding Self, 0 [symbolic] // CHECK:STDOUT: %C: type = class_type @C [concrete] // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %complete_type: = complete_type_witness %empty_struct_type [concrete] // CHECK:STDOUT: %I.impl_witness: = impl_witness @C.as.I.impl.e47215.1.%I.impl_witness_table [concrete] // CHECK:STDOUT: %I.facet: %I.type = facet_value %C, (%I.impl_witness) [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .I = %I.decl // CHECK:STDOUT: .C = %C.decl // CHECK:STDOUT: .AI = %AI // CHECK:STDOUT: .AC = %AC // CHECK:STDOUT: } // CHECK:STDOUT: %I.decl: type = interface_decl @I [concrete = constants.%I.type] {} {} // CHECK:STDOUT: %C.decl: type = class_decl @C [concrete = constants.%C] {} {} // CHECK:STDOUT: %I.ref: type = name_ref I, %I.decl [concrete = constants.%I.type] // CHECK:STDOUT: %AI: type = alias_binding AI, %I.decl [concrete = constants.%I.type] // CHECK:STDOUT: %C.ref: type = name_ref C, %C.decl [concrete = constants.%C] // CHECK:STDOUT: %AC: type = alias_binding AC, %C.decl [concrete = constants.%C] // CHECK:STDOUT: impl_decl @C.as.I.impl.e47215.1 [concrete] {} { // CHECK:STDOUT: %AC.ref: type = name_ref AC, file.%AC [concrete = constants.%C] // CHECK:STDOUT: %AI.ref: type = name_ref AI, file.%AI [concrete = constants.%I.type] // CHECK:STDOUT: } // CHECK:STDOUT: impl_decl @C.as.I.impl.e47215.2 [concrete] {} { // CHECK:STDOUT: %AC.ref: type = name_ref AC, file.%AC [concrete = constants.%C] // CHECK:STDOUT: %AI.ref: type = name_ref AI, file.%AI [concrete = constants.%I.type] // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: interface @I { // CHECK:STDOUT: %Self: %I.type = symbolic_binding Self, 0 [symbolic = constants.%Self] // CHECK:STDOUT: %I.WithSelf.decl = interface_with_self_decl @I [concrete] // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = %Self // CHECK:STDOUT: witness = () // CHECK:STDOUT: // CHECK:STDOUT: !requires: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: impl @C.as.I.impl.e47215.1: %AC.ref as %AI.ref { // CHECK:STDOUT: %I.impl_witness_table = impl_witness_table (), @C.as.I.impl.e47215.1 [concrete] // CHECK:STDOUT: %I.impl_witness: = impl_witness %I.impl_witness_table [concrete = constants.%I.impl_witness] // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: witness = %I.impl_witness // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: impl @C.as.I.impl.e47215.2: %AC.ref as %AI.ref { // CHECK:STDOUT: !members: // CHECK:STDOUT: witness = // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @C { // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%C // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @I.WithSelf(constants.%Self) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @I.WithSelf(constants.%I.facet) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: } // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/impl/fail_call_invalid.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/int.carbon // TODO: Add ranges and switch to "--dump-sem-ir-ranges=only". // EXTRA-ARGS: --dump-sem-ir-ranges=if-present // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/impl/fail_call_invalid.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/impl/fail_call_invalid.carbon interface Simple { fn G[self: Self](); } impl i32 as Simple { // CHECK:STDERR: fail_call_invalid.carbon:[[@LINE+4]]:14: error: name `Undeclared` not found [NameNotFound] // CHECK:STDERR: fn G[self: Undeclared](); // CHECK:STDERR: ^~~~~~~~~~ // CHECK:STDERR: fn G[self: Undeclared](); } fn InstanceCall(n: i32) { n.(Simple.G)(); } // CHECK:STDOUT: --- fail_call_invalid.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %Simple.type: type = facet_type <@Simple> [concrete] // CHECK:STDOUT: %Self: %Simple.type = symbolic_binding Self, 0 [symbolic] // CHECK:STDOUT: %Self.binding.as_type: type = symbolic_binding_type Self, 0, %Self [symbolic] // CHECK:STDOUT: %pattern_type.5e9: type = pattern_type %Self.binding.as_type [symbolic] // CHECK:STDOUT: %Simple.WithSelf.G.type.d78: type = fn_type @Simple.WithSelf.G, @Simple.WithSelf(%Self) [symbolic] // CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] // CHECK:STDOUT: %Simple.WithSelf.G.41d: %Simple.WithSelf.G.type.d78 = struct_value () [symbolic] // CHECK:STDOUT: %Simple.assoc_type: type = assoc_entity_type @Simple [concrete] // CHECK:STDOUT: %assoc0: %Simple.assoc_type = assoc_entity element0, @Simple.WithSelf.%Simple.WithSelf.G.decl [concrete] // CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [concrete] // CHECK:STDOUT: %Int.type: type = generic_class_type @Int [concrete] // CHECK:STDOUT: %Int.generic: %Int.type = struct_value () [concrete] // CHECK:STDOUT: %i32: type = class_type @Int, @Int(%int_32) [concrete] // CHECK:STDOUT: %Simple.impl_witness: = impl_witness @i32.as.Simple.impl.%Simple.impl_witness_table [concrete] // CHECK:STDOUT: %i32.as.Simple.impl.G.type.32ad7a.1: type = fn_type @i32.as.Simple.impl.G.loc24_27.1 [concrete] // CHECK:STDOUT: %i32.as.Simple.impl.G.4f6260.1: %i32.as.Simple.impl.G.type.32ad7a.1 = struct_value () [concrete] // CHECK:STDOUT: %Simple.facet: %Simple.type = facet_value %i32, (%Simple.impl_witness) [concrete] // CHECK:STDOUT: %Simple.WithSelf.G.type.9d9: type = fn_type @Simple.WithSelf.G, @Simple.WithSelf(%Simple.facet) [concrete] // CHECK:STDOUT: %Simple.WithSelf.G.26c: %Simple.WithSelf.G.type.9d9 = struct_value () [concrete] // CHECK:STDOUT: %pattern_type.7ce: type = pattern_type %i32 [concrete] // CHECK:STDOUT: %i32.as.Simple.impl.G.type.32ad7a.2: type = fn_type @i32.as.Simple.impl.G.loc24_27.2 [concrete] // CHECK:STDOUT: %i32.as.Simple.impl.G.4f6260.2: %i32.as.Simple.impl.G.type.32ad7a.2 = struct_value () [concrete] // CHECK:STDOUT: %InstanceCall.type: type = fn_type @InstanceCall [concrete] // CHECK:STDOUT: %InstanceCall: %InstanceCall.type = struct_value () [concrete] // CHECK:STDOUT: %.a46: type = fn_type_with_self_type %Simple.WithSelf.G.type.9d9, %Simple.facet [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: .Int = %Core.Int // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: %Core.Int: %Int.type = import_ref Core//prelude/parts/int, Int, loaded [concrete = constants.%Int.generic] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .Simple = %Simple.decl // CHECK:STDOUT: .Undeclared = // CHECK:STDOUT: .InstanceCall = %InstanceCall.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %Simple.decl: type = interface_decl @Simple [concrete = constants.%Simple.type] {} {} // CHECK:STDOUT: impl_decl @i32.as.Simple.impl [concrete] {} { // CHECK:STDOUT: %i32: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %Simple.ref: type = name_ref Simple, file.%Simple.decl [concrete = constants.%Simple.type] // CHECK:STDOUT: } // CHECK:STDOUT: %InstanceCall.decl: %InstanceCall.type = fn_decl @InstanceCall [concrete = constants.%InstanceCall] { // CHECK:STDOUT: %n.patt: %pattern_type.7ce = value_binding_pattern n [concrete] // CHECK:STDOUT: %n.param_patt: %pattern_type.7ce = value_param_pattern %n.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %n.param: %i32 = value_param call_param0 // CHECK:STDOUT: %i32: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %n: %i32 = value_binding n, %n.param // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: interface @Simple { // CHECK:STDOUT: %Self: %Simple.type = symbolic_binding Self, 0 [symbolic = constants.%Self] // CHECK:STDOUT: %Simple.WithSelf.decl = interface_with_self_decl @Simple [concrete] // CHECK:STDOUT: // CHECK:STDOUT: !with Self: // CHECK:STDOUT: %Simple.WithSelf.G.decl: @Simple.WithSelf.%Simple.WithSelf.G.type (%Simple.WithSelf.G.type.d78) = fn_decl @Simple.WithSelf.G [symbolic = @Simple.WithSelf.%Simple.WithSelf.G (constants.%Simple.WithSelf.G.41d)] { // CHECK:STDOUT: %self.patt: @Simple.WithSelf.G.%pattern_type (%pattern_type.5e9) = value_binding_pattern self [concrete] // CHECK:STDOUT: %self.param_patt: @Simple.WithSelf.G.%pattern_type (%pattern_type.5e9) = value_param_pattern %self.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %self.param: @Simple.WithSelf.G.%Self.binding.as_type (%Self.binding.as_type) = value_param call_param0 // CHECK:STDOUT: %.loc16_14.1: type = splice_block %.loc16_14.2 [symbolic = %Self.binding.as_type (constants.%Self.binding.as_type)] { // CHECK:STDOUT: %Self.ref: %Simple.type = name_ref Self, @Simple.%Self [symbolic = %Self (constants.%Self)] // CHECK:STDOUT: %Self.as_type: type = facet_access_type %Self.ref [symbolic = %Self.binding.as_type (constants.%Self.binding.as_type)] // CHECK:STDOUT: %.loc16_14.2: type = converted %Self.ref, %Self.as_type [symbolic = %Self.binding.as_type (constants.%Self.binding.as_type)] // CHECK:STDOUT: } // CHECK:STDOUT: %self: @Simple.WithSelf.G.%Self.binding.as_type (%Self.binding.as_type) = value_binding self, %self.param // CHECK:STDOUT: } // CHECK:STDOUT: %assoc0: %Simple.assoc_type = assoc_entity element0, %Simple.WithSelf.G.decl [concrete = constants.%assoc0] // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = %Self // CHECK:STDOUT: .G = @Simple.WithSelf.%assoc0 // CHECK:STDOUT: witness = (@Simple.WithSelf.%Simple.WithSelf.G.decl) // CHECK:STDOUT: // CHECK:STDOUT: !requires: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: impl @i32.as.Simple.impl: %i32 as %Simple.ref { // CHECK:STDOUT: %i32.as.Simple.impl.G.decl.loc24_27.1: %i32.as.Simple.impl.G.type.32ad7a.1 = fn_decl @i32.as.Simple.impl.G.loc24_27.1 [concrete = constants.%i32.as.Simple.impl.G.4f6260.1] { // CHECK:STDOUT: %self.patt: = value_binding_pattern self [concrete] // CHECK:STDOUT: %self.param_patt: = value_param_pattern %self.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %self.param: = value_param call_param0 // CHECK:STDOUT: %Undeclared.ref: = name_ref Undeclared, [concrete = ] // CHECK:STDOUT: %self: = value_binding self, %self.param // CHECK:STDOUT: } // CHECK:STDOUT: %i32.as.Simple.impl.G.decl.loc24_27.2: %i32.as.Simple.impl.G.type.32ad7a.2 = fn_decl @i32.as.Simple.impl.G.loc24_27.2 [concrete = constants.%i32.as.Simple.impl.G.4f6260.2] { // CHECK:STDOUT: %self.patt: %pattern_type.7ce = value_binding_pattern self [concrete] // CHECK:STDOUT: %self.param_patt: %pattern_type.7ce = value_param_pattern %self.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %self.param: %i32 = value_param call_param0 // CHECK:STDOUT: %self: %i32 = value_binding self, %self.param // CHECK:STDOUT: } // CHECK:STDOUT: %Simple.impl_witness_table = impl_witness_table (%i32.as.Simple.impl.G.decl.loc24_27.2), @i32.as.Simple.impl [concrete] // CHECK:STDOUT: %Simple.impl_witness: = impl_witness %Simple.impl_witness_table [concrete = constants.%Simple.impl_witness] // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Undeclared = // CHECK:STDOUT: .G = %i32.as.Simple.impl.G.decl.loc24_27.1 // CHECK:STDOUT: witness = %Simple.impl_witness // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic fn @Simple.WithSelf.G(@Simple.%Self: %Simple.type) { // CHECK:STDOUT: %Self: %Simple.type = symbolic_binding Self, 0 [symbolic = %Self (constants.%Self)] // CHECK:STDOUT: %Self.binding.as_type: type = symbolic_binding_type Self, 0, %Self [symbolic = %Self.binding.as_type (constants.%Self.binding.as_type)] // CHECK:STDOUT: %pattern_type: type = pattern_type %Self.binding.as_type [symbolic = %pattern_type (constants.%pattern_type.5e9)] // CHECK:STDOUT: // CHECK:STDOUT: fn(%self.param: @Simple.WithSelf.G.%Self.binding.as_type (%Self.binding.as_type)); // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @i32.as.Simple.impl.G.loc24_27.1(%self.param: ); // CHECK:STDOUT: // CHECK:STDOUT: fn @i32.as.Simple.impl.G.loc24_27.2(%self.param: %i32) [thunk @i32.as.Simple.impl.%i32.as.Simple.impl.G.decl.loc24_27.1] { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %G.ref: %i32.as.Simple.impl.G.type.32ad7a.1 = name_ref G, @i32.as.Simple.impl.%i32.as.Simple.impl.G.decl.loc24_27.1 [concrete = constants.%i32.as.Simple.impl.G.4f6260.1] // CHECK:STDOUT: %self.ref: %i32 = name_ref self, %self.param // CHECK:STDOUT: %i32.as.Simple.impl.G.bound: = bound_method %self.ref, %G.ref // CHECK:STDOUT: %i32.as.Simple.impl.G.call: init %empty_tuple.type = call %i32.as.Simple.impl.G.bound() // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @InstanceCall(%n.param: %i32) { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %n.ref: %i32 = name_ref n, %n // CHECK:STDOUT: %Simple.ref: type = name_ref Simple, file.%Simple.decl [concrete = constants.%Simple.type] // CHECK:STDOUT: %G.ref.loc28_12: %Simple.assoc_type = name_ref G, @Simple.WithSelf.%assoc0 [concrete = constants.%assoc0] // CHECK:STDOUT: %impl.elem0: %.a46 = impl_witness_access constants.%Simple.impl_witness, element0 [concrete = constants.%i32.as.Simple.impl.G.4f6260.2] // CHECK:STDOUT: %bound_method: = bound_method %n.ref, %impl.elem0 // CHECK:STDOUT: %G.ref.loc28_16: %i32.as.Simple.impl.G.type.32ad7a.1 = name_ref G, @i32.as.Simple.impl.%i32.as.Simple.impl.G.decl.loc24_27.1 [concrete = constants.%i32.as.Simple.impl.G.4f6260.1] // CHECK:STDOUT: %i32.as.Simple.impl.G.bound: = bound_method %n.ref, %G.ref.loc28_16 // CHECK:STDOUT: %i32.as.Simple.impl.G.call: init %empty_tuple.type = call %i32.as.Simple.impl.G.bound() // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @Simple.WithSelf(constants.%Self) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: %Self => constants.%Self // CHECK:STDOUT: %Simple.WithSelf.G.type => constants.%Simple.WithSelf.G.type.d78 // CHECK:STDOUT: %Simple.WithSelf.G => constants.%Simple.WithSelf.G.41d // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @Simple.WithSelf.G(constants.%Self) { // CHECK:STDOUT: %Self => constants.%Self // CHECK:STDOUT: %Self.binding.as_type => constants.%Self.binding.as_type // CHECK:STDOUT: %pattern_type => constants.%pattern_type.5e9 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @Simple.WithSelf(constants.%Simple.facet) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: %Self => constants.%Simple.facet // CHECK:STDOUT: %Simple.WithSelf.G.type => constants.%Simple.WithSelf.G.type.9d9 // CHECK:STDOUT: %Simple.WithSelf.G => constants.%Simple.WithSelf.G.26c // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @Simple.WithSelf.G(constants.%Simple.facet) { // CHECK:STDOUT: %Self => constants.%Simple.facet // CHECK:STDOUT: %Self.binding.as_type => constants.%i32 // CHECK:STDOUT: %pattern_type => constants.%pattern_type.7ce // CHECK:STDOUT: } // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/impl/fail_extend_impl_forall.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/none.carbon // TODO: Add ranges and switch to "--dump-sem-ir-ranges=only". // EXTRA-ARGS: --dump-sem-ir-ranges=if-present // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/impl/fail_extend_impl_forall.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/impl/fail_extend_impl_forall.carbon interface GenericInterface(T:! type) { fn F(x: T); } class C { // CHECK:STDERR: fail_extend_impl_forall.carbon:[[@LINE+4]]:3: error: cannot `extend` a parameterized `impl` [ExtendImplForall] // CHECK:STDERR: extend impl forall [T:! type] as GenericInterface(T) { // CHECK:STDERR: ^~~~~~ // CHECK:STDERR: extend impl forall [T:! type] as GenericInterface(T) { fn F(unused x: T) {} } } // CHECK:STDOUT: --- fail_extend_impl_forall.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %type: type = facet_type [concrete] // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self] // CHECK:STDOUT: %pattern_type.98f: type = pattern_type type [concrete] // CHECK:STDOUT: %T: type = symbolic_binding T, 0 [symbolic] // CHECK:STDOUT: %GenericInterface.type.21d: type = generic_interface_type @GenericInterface [concrete] // CHECK:STDOUT: %GenericInterface.generic: %GenericInterface.type.21d = struct_value () [concrete] // CHECK:STDOUT: %GenericInterface.type.8bc: type = facet_type <@GenericInterface, @GenericInterface(%T)> [symbolic] // CHECK:STDOUT: %Self: %GenericInterface.type.8bc = symbolic_binding Self, 1 [symbolic] // CHECK:STDOUT: %pattern_type.51d: type = pattern_type %T [symbolic] // CHECK:STDOUT: %GenericInterface.WithSelf.F.type: type = fn_type @GenericInterface.WithSelf.F, @GenericInterface.WithSelf(%T, %Self) [symbolic] // CHECK:STDOUT: %GenericInterface.WithSelf.F: %GenericInterface.WithSelf.F.type = struct_value () [symbolic] // CHECK:STDOUT: %GenericInterface.assoc_type: type = assoc_entity_type @GenericInterface, @GenericInterface(%T) [symbolic] // CHECK:STDOUT: %assoc0: %GenericInterface.assoc_type = assoc_entity element0, @GenericInterface.WithSelf.%GenericInterface.WithSelf.F.decl [symbolic] // CHECK:STDOUT: %C: type = class_type @C [concrete] // CHECK:STDOUT: %GenericInterface.impl_witness: = impl_witness @C.as.GenericInterface.impl.%GenericInterface.impl_witness_table, @C.as.GenericInterface.impl(%T) [symbolic] // CHECK:STDOUT: %C.as.GenericInterface.impl.F.type: type = fn_type @C.as.GenericInterface.impl.F, @C.as.GenericInterface.impl(%T) [symbolic] // CHECK:STDOUT: %C.as.GenericInterface.impl.F: %C.as.GenericInterface.impl.F.type = struct_value () [symbolic] // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %complete_type: = complete_type_witness %empty_struct_type [concrete] // CHECK:STDOUT: %require_complete: = require_complete_type %T [symbolic] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .GenericInterface = %GenericInterface.decl // CHECK:STDOUT: .C = %C.decl // CHECK:STDOUT: } // CHECK:STDOUT: %GenericInterface.decl: %GenericInterface.type.21d = interface_decl @GenericInterface [concrete = constants.%GenericInterface.generic] { // CHECK:STDOUT: %T.patt: %pattern_type.98f = symbolic_binding_pattern T, 0 [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %.loc15_32.1: type = splice_block %.loc15_32.2 [concrete = type] { // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %.loc15_32.2: type = type_literal type [concrete = type] // CHECK:STDOUT: } // CHECK:STDOUT: %T.loc15_28.2: type = symbolic_binding T, 0 [symbolic = %T.loc15_28.1 (constants.%T)] // CHECK:STDOUT: } // CHECK:STDOUT: %C.decl: type = class_decl @C [concrete = constants.%C] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic interface @GenericInterface(%T.loc15_28.2: type) { // CHECK:STDOUT: %T.loc15_28.1: type = symbolic_binding T, 0 [symbolic = %T.loc15_28.1 (constants.%T)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %GenericInterface.type: type = facet_type <@GenericInterface, @GenericInterface(%T.loc15_28.1)> [symbolic = %GenericInterface.type (constants.%GenericInterface.type.8bc)] // CHECK:STDOUT: %Self.loc15_38.2: @GenericInterface.%GenericInterface.type (%GenericInterface.type.8bc) = symbolic_binding Self, 1 [symbolic = %Self.loc15_38.2 (constants.%Self)] // CHECK:STDOUT: // CHECK:STDOUT: interface { // CHECK:STDOUT: %Self.loc15_38.1: @GenericInterface.%GenericInterface.type (%GenericInterface.type.8bc) = symbolic_binding Self, 1 [symbolic = %Self.loc15_38.2 (constants.%Self)] // CHECK:STDOUT: %GenericInterface.WithSelf.decl = interface_with_self_decl @GenericInterface [concrete] // CHECK:STDOUT: // CHECK:STDOUT: !with Self: // CHECK:STDOUT: %GenericInterface.WithSelf.F.decl: @GenericInterface.WithSelf.%GenericInterface.WithSelf.F.type (%GenericInterface.WithSelf.F.type) = fn_decl @GenericInterface.WithSelf.F [symbolic = @GenericInterface.WithSelf.%GenericInterface.WithSelf.F (constants.%GenericInterface.WithSelf.F)] { // CHECK:STDOUT: %x.patt: @GenericInterface.WithSelf.F.%pattern_type (%pattern_type.51d) = value_binding_pattern x [concrete] // CHECK:STDOUT: %x.param_patt: @GenericInterface.WithSelf.F.%pattern_type (%pattern_type.51d) = value_param_pattern %x.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %x.param: @GenericInterface.WithSelf.F.%T (%T) = value_param call_param0 // CHECK:STDOUT: %T.ref: type = name_ref T, @GenericInterface.%T.loc15_28.2 [symbolic = %T (constants.%T)] // CHECK:STDOUT: %x: @GenericInterface.WithSelf.F.%T (%T) = value_binding x, %x.param // CHECK:STDOUT: } // CHECK:STDOUT: %assoc0.loc16_13.1: @GenericInterface.WithSelf.%GenericInterface.assoc_type (%GenericInterface.assoc_type) = assoc_entity element0, %GenericInterface.WithSelf.F.decl [symbolic = %assoc0.loc16_13.2 (constants.%assoc0)] // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = %Self.loc15_38.1 // CHECK:STDOUT: .T = // CHECK:STDOUT: .T = // CHECK:STDOUT: .F = @GenericInterface.WithSelf.%assoc0.loc16_13.1 // CHECK:STDOUT: witness = (@GenericInterface.WithSelf.%GenericInterface.WithSelf.F.decl) // CHECK:STDOUT: // CHECK:STDOUT: !requires: // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic impl @C.as.GenericInterface.impl(%T.loc24_23.2: type) { // CHECK:STDOUT: %T.loc24_23.1: type = symbolic_binding T, 0 [symbolic = %T.loc24_23.1 (constants.%T)] // CHECK:STDOUT: %GenericInterface.type.loc24_54.1: type = facet_type <@GenericInterface, @GenericInterface(%T.loc24_23.1)> [symbolic = %GenericInterface.type.loc24_54.1 (constants.%GenericInterface.type.8bc)] // CHECK:STDOUT: %GenericInterface.impl_witness.loc24_56.2: = impl_witness %GenericInterface.impl_witness_table, @C.as.GenericInterface.impl(%T.loc24_23.1) [symbolic = %GenericInterface.impl_witness.loc24_56.2 (constants.%GenericInterface.impl_witness)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %C.as.GenericInterface.impl.F.type: type = fn_type @C.as.GenericInterface.impl.F, @C.as.GenericInterface.impl(%T.loc24_23.1) [symbolic = %C.as.GenericInterface.impl.F.type (constants.%C.as.GenericInterface.impl.F.type)] // CHECK:STDOUT: %C.as.GenericInterface.impl.F: @C.as.GenericInterface.impl.%C.as.GenericInterface.impl.F.type (%C.as.GenericInterface.impl.F.type) = struct_value () [symbolic = %C.as.GenericInterface.impl.F (constants.%C.as.GenericInterface.impl.F)] // CHECK:STDOUT: // CHECK:STDOUT: impl: %Self.ref as %GenericInterface.type.loc24_54.2 { // CHECK:STDOUT: %C.as.GenericInterface.impl.F.decl: @C.as.GenericInterface.impl.%C.as.GenericInterface.impl.F.type (%C.as.GenericInterface.impl.F.type) = fn_decl @C.as.GenericInterface.impl.F [symbolic = @C.as.GenericInterface.impl.%C.as.GenericInterface.impl.F (constants.%C.as.GenericInterface.impl.F)] { // CHECK:STDOUT: %x.patt: @C.as.GenericInterface.impl.F.%pattern_type (%pattern_type.51d) = value_binding_pattern x [concrete] // CHECK:STDOUT: %x.param_patt: @C.as.GenericInterface.impl.F.%pattern_type (%pattern_type.51d) = value_param_pattern %x.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %x.param: @C.as.GenericInterface.impl.F.%T (%T) = value_param call_param0 // CHECK:STDOUT: %T.ref: type = name_ref T, @C.as.GenericInterface.impl.%T.loc24_23.2 [symbolic = %T (constants.%T)] // CHECK:STDOUT: %x: @C.as.GenericInterface.impl.F.%T (%T) = value_binding x, %x.param // CHECK:STDOUT: } // CHECK:STDOUT: %GenericInterface.impl_witness_table = impl_witness_table (), @C.as.GenericInterface.impl [concrete] // CHECK:STDOUT: %GenericInterface.impl_witness.loc24_56.1: = impl_witness %GenericInterface.impl_witness_table, @C.as.GenericInterface.impl(constants.%T) [symbolic = %GenericInterface.impl_witness.loc24_56.2 (constants.%GenericInterface.impl_witness)] // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .T = // CHECK:STDOUT: .F = %C.as.GenericInterface.impl.F.decl // CHECK:STDOUT: witness = // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @C { // CHECK:STDOUT: impl_decl @C.as.GenericInterface.impl [concrete] { // CHECK:STDOUT: %T.patt: %pattern_type.98f = symbolic_binding_pattern T, 0 [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %Self.ref: type = name_ref Self, constants.%C [concrete = constants.%C] // CHECK:STDOUT: %GenericInterface.ref: %GenericInterface.type.21d = name_ref GenericInterface, file.%GenericInterface.decl [concrete = constants.%GenericInterface.generic] // CHECK:STDOUT: %T.ref: type = name_ref T, %T.loc24_23.2 [symbolic = %T.loc24_23.1 (constants.%T)] // CHECK:STDOUT: %GenericInterface.type.loc24_54.2: type = facet_type <@GenericInterface, @GenericInterface(constants.%T)> [symbolic = %GenericInterface.type.loc24_54.1 (constants.%GenericInterface.type.8bc)] // CHECK:STDOUT: %.loc24_27.1: type = splice_block %.loc24_27.2 [concrete = type] { // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %.loc24_27.2: type = type_literal type [concrete = type] // CHECK:STDOUT: } // CHECK:STDOUT: %T.loc24_23.2: type = symbolic_binding T, 0 [symbolic = %T.loc24_23.1 (constants.%T)] // CHECK:STDOUT: } // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%C // CHECK:STDOUT: .GenericInterface = // CHECK:STDOUT: has_error // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic fn @GenericInterface.WithSelf.F(@GenericInterface.%T.loc15_28.2: type, @GenericInterface.%Self.loc15_38.1: @GenericInterface.%GenericInterface.type (%GenericInterface.type.8bc)) { // CHECK:STDOUT: %T: type = symbolic_binding T, 0 [symbolic = %T (constants.%T)] // CHECK:STDOUT: %pattern_type: type = pattern_type %T [symbolic = %pattern_type (constants.%pattern_type.51d)] // CHECK:STDOUT: // CHECK:STDOUT: fn(%x.param: @GenericInterface.WithSelf.F.%T (%T)); // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic fn @C.as.GenericInterface.impl.F(@C.as.GenericInterface.impl.%T.loc24_23.2: type) { // CHECK:STDOUT: %T: type = symbolic_binding T, 0 [symbolic = %T (constants.%T)] // CHECK:STDOUT: %pattern_type: type = pattern_type %T [symbolic = %pattern_type (constants.%pattern_type.51d)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete: = require_complete_type %T [symbolic = %require_complete (constants.%require_complete)] // CHECK:STDOUT: // CHECK:STDOUT: fn(%x.param: @C.as.GenericInterface.impl.F.%T (%T)) { // CHECK:STDOUT: !entry: // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @GenericInterface(constants.%T) { // CHECK:STDOUT: %T.loc15_28.1 => constants.%T // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @GenericInterface.WithSelf(constants.%T, constants.%Self) {} // CHECK:STDOUT: // CHECK:STDOUT: specific @GenericInterface.WithSelf.F(constants.%T, constants.%Self) { // CHECK:STDOUT: %T => constants.%T // CHECK:STDOUT: %pattern_type => constants.%pattern_type.51d // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @C.as.GenericInterface.impl(constants.%T) { // CHECK:STDOUT: %T.loc24_23.1 => constants.%T // CHECK:STDOUT: %GenericInterface.type.loc24_54.1 => constants.%GenericInterface.type.8bc // CHECK:STDOUT: %GenericInterface.impl_witness.loc24_56.2 => constants.%GenericInterface.impl_witness // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %C.as.GenericInterface.impl.F.type => constants.%C.as.GenericInterface.impl.F.type // CHECK:STDOUT: %C.as.GenericInterface.impl.F => constants.%C.as.GenericInterface.impl.F // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @C.as.GenericInterface.impl.F(constants.%T) { // CHECK:STDOUT: %T => constants.%T // CHECK:STDOUT: %pattern_type => constants.%pattern_type.51d // CHECK:STDOUT: } // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/impl/fail_extend_impl_scope.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/destroy.carbon // TODO: Add ranges and switch to "--dump-sem-ir-ranges=only". // EXTRA-ARGS: --dump-sem-ir-ranges=if-present // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/impl/fail_extend_impl_scope.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/impl/fail_extend_impl_scope.carbon // --- fail_extend_impl_file_scope.carbon library "[[@TEST_NAME]]"; interface I {} // CHECK:STDERR: fail_extend_impl_file_scope.carbon:[[@LINE+4]]:1: error: `extend impl` can only be used in an interface or class [ExtendImplOutsideClass] // CHECK:STDERR: extend impl () as I {} // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: extend impl () as I {} // --- fail_extend_impl_function_scope.carbon library "[[@TEST_NAME]]"; interface J {} fn F() { // CHECK:STDERR: fail_extend_impl_function_scope.carbon:[[@LINE+4]]:3: error: `extend impl` can only be used in an interface or class [ExtendImplOutsideClass] // CHECK:STDERR: extend impl {} as J {} // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: extend impl {} as J {} } // --- fail_extend_impl_self_interface.carbon library "[[@TEST_NAME]]"; interface Z { fn Zero(); // CHECK:STDERR: fail_extend_impl_self_interface.carbon:[[@LINE+4]]:15: error: `impl as` can only be used in a class [ImplAsOutsideClass] // CHECK:STDERR: extend impl as Z { // CHECK:STDERR: ^~ // CHECK:STDERR: extend impl as Z { fn Zero() {} } } class Point { extend impl as Z { fn Zero() {} } } fn F() { // Even if the `extend impl` is diagnosed above, we must not add the impl of // the interface to itself in a way that allows it to be used during impl // lookup, or we end up with infinite impl lookup recursion here. ({} as Point).Zero(); } // CHECK:STDOUT: --- fail_extend_impl_file_scope.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %I.type: type = facet_type <@I> [concrete] // CHECK:STDOUT: %Self: %I.type = symbolic_binding Self, 0 [symbolic] // CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] // CHECK:STDOUT: %empty_tuple: %empty_tuple.type = tuple_value () [concrete] // CHECK:STDOUT: %I.impl_witness: = impl_witness @empty_tuple.type.as.I.impl.%I.impl_witness_table [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .I = %I.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %I.decl: type = interface_decl @I [concrete = constants.%I.type] {} {} // CHECK:STDOUT: impl_decl @empty_tuple.type.as.I.impl [concrete] {} { // CHECK:STDOUT: %.loc9_14.1: %empty_tuple.type = tuple_literal () [concrete = constants.%empty_tuple] // CHECK:STDOUT: %.loc9_14.2: type = converted %.loc9_14.1, constants.%empty_tuple.type [concrete = constants.%empty_tuple.type] // CHECK:STDOUT: %I.ref: type = name_ref I, file.%I.decl [concrete = constants.%I.type] // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: interface @I { // CHECK:STDOUT: %Self: %I.type = symbolic_binding Self, 0 [symbolic = constants.%Self] // CHECK:STDOUT: %I.WithSelf.decl = interface_with_self_decl @I [concrete] // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = %Self // CHECK:STDOUT: witness = () // CHECK:STDOUT: // CHECK:STDOUT: !requires: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: impl @empty_tuple.type.as.I.impl: %.loc9_14.2 as %I.ref { // CHECK:STDOUT: %I.impl_witness_table = impl_witness_table (), @empty_tuple.type.as.I.impl [concrete] // CHECK:STDOUT: %I.impl_witness: = impl_witness %I.impl_witness_table [concrete = constants.%I.impl_witness] // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: witness = // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @I.WithSelf(constants.%Self) {} // CHECK:STDOUT: // CHECK:STDOUT: --- fail_extend_impl_function_scope.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %J.type: type = facet_type <@J> [concrete] // CHECK:STDOUT: %Self: %J.type = symbolic_binding Self, 0 [symbolic] // CHECK:STDOUT: %F.type: type = fn_type @F [concrete] // CHECK:STDOUT: %F: %F.type = struct_value () [concrete] // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %empty_struct: %empty_struct_type = struct_value () [concrete] // CHECK:STDOUT: %J.impl_witness: = impl_witness @empty_struct_type.as.J.impl.%J.impl_witness_table [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .J = %J.decl // CHECK:STDOUT: .F = %F.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %J.decl: type = interface_decl @J [concrete = constants.%J.type] {} {} // CHECK:STDOUT: %F.decl: %F.type = fn_decl @F [concrete = constants.%F] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: interface @J { // CHECK:STDOUT: %Self: %J.type = symbolic_binding Self, 0 [symbolic = constants.%Self] // CHECK:STDOUT: %J.WithSelf.decl = interface_with_self_decl @J [concrete] // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = %Self // CHECK:STDOUT: witness = () // CHECK:STDOUT: // CHECK:STDOUT: !requires: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: impl @empty_struct_type.as.J.impl: %.loc10_16.2 as %J.ref { // CHECK:STDOUT: %J.impl_witness_table = impl_witness_table (), @empty_struct_type.as.J.impl [concrete] // CHECK:STDOUT: %J.impl_witness: = impl_witness %J.impl_witness_table [concrete = constants.%J.impl_witness] // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: witness = // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @F() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: impl_decl @empty_struct_type.as.J.impl [concrete] {} { // CHECK:STDOUT: %.loc10_16.1: %empty_struct_type = struct_literal () [concrete = constants.%empty_struct] // CHECK:STDOUT: %.loc10_16.2: type = converted %.loc10_16.1, constants.%empty_struct_type [concrete = constants.%empty_struct_type] // CHECK:STDOUT: %J.ref: type = name_ref J, file.%J.decl [concrete = constants.%J.type] // CHECK:STDOUT: } // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @J.WithSelf(constants.%Self) {} // CHECK:STDOUT: // CHECK:STDOUT: --- fail_extend_impl_self_interface.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %Z.type: type = facet_type <@Z> [concrete] // CHECK:STDOUT: %Self.c59: %Z.type = symbolic_binding Self, 0 [symbolic] // CHECK:STDOUT: %Z.WithSelf.Zero.type.e4a: type = fn_type @Z.WithSelf.Zero, @Z.WithSelf(%Self.c59) [symbolic] // CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] // CHECK:STDOUT: %Z.WithSelf.Zero.3f4: %Z.WithSelf.Zero.type.e4a = struct_value () [symbolic] // CHECK:STDOUT: %Z.assoc_type: type = assoc_entity_type @Z [concrete] // CHECK:STDOUT: %assoc0.cc0: %Z.assoc_type = assoc_entity element0, @Z.WithSelf.%Z.WithSelf.Zero.decl [concrete] // CHECK:STDOUT: %.as.Z.impl.Zero.type: type = fn_type @.as.Z.impl.Zero, @.as.Z.impl(%Self.c59) [symbolic] // CHECK:STDOUT: %.as.Z.impl.Zero: %.as.Z.impl.Zero.type = struct_value () [symbolic] // CHECK:STDOUT: %Point: type = class_type @Point [concrete] // CHECK:STDOUT: %Z.impl_witness: = impl_witness @Point.as.Z.impl.%Z.impl_witness_table [concrete] // CHECK:STDOUT: %Point.as.Z.impl.Zero.type: type = fn_type @Point.as.Z.impl.Zero [concrete] // CHECK:STDOUT: %Point.as.Z.impl.Zero: %Point.as.Z.impl.Zero.type = struct_value () [concrete] // CHECK:STDOUT: %Z.facet: %Z.type = facet_value %Point, (%Z.impl_witness) [concrete] // CHECK:STDOUT: %Z.WithSelf.Zero.type.bb1: type = fn_type @Z.WithSelf.Zero, @Z.WithSelf(%Z.facet) [concrete] // CHECK:STDOUT: %Z.WithSelf.Zero.22c: %Z.WithSelf.Zero.type.bb1 = struct_value () [concrete] // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %complete_type: = complete_type_witness %empty_struct_type [concrete] // CHECK:STDOUT: %F.type: type = fn_type @F [concrete] // CHECK:STDOUT: %F: %F.type = struct_value () [concrete] // CHECK:STDOUT: %empty_struct: %empty_struct_type = struct_value () [concrete] // CHECK:STDOUT: %Point.val: %Point = struct_value () [concrete] // CHECK:STDOUT: %type: type = facet_type [concrete] // CHECK:STDOUT: %Point.type.facet: %type = facet_value %Point, () [concrete] // CHECK:STDOUT: %Z.WithSelf.Zero.type.15e: type = fn_type @Z.WithSelf.Zero, @Z.WithSelf(%Point.type.facet) [concrete] // CHECK:STDOUT: %Z.WithSelf.Zero.55b: %Z.WithSelf.Zero.type.15e = struct_value () [concrete] // CHECK:STDOUT: %.c1b: type = fn_type_with_self_type %Z.WithSelf.Zero.type.bb1, %Z.facet [concrete] // CHECK:STDOUT: %Destroy.type: type = facet_type <@Destroy> [concrete] // CHECK:STDOUT: %Destroy.Op.type: type = fn_type @Destroy.Op [concrete] // CHECK:STDOUT: %Destroy.Op: %Destroy.Op.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: .Destroy = %Core.Destroy // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: %Core.Destroy: type = import_ref Core//prelude/parts/destroy, Destroy, loaded [concrete = constants.%Destroy.type] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .Z = %Z.decl // CHECK:STDOUT: .Point = %Point.decl // CHECK:STDOUT: .F = %F.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %Z.decl: type = interface_decl @Z [concrete = constants.%Z.type] {} {} // CHECK:STDOUT: %Point.decl: type = class_decl @Point [concrete = constants.%Point] {} {} // CHECK:STDOUT: %F.decl: %F.type = fn_decl @F [concrete = constants.%F] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: interface @Z { // CHECK:STDOUT: %Self: %Z.type = symbolic_binding Self, 0 [symbolic = constants.%Self.c59] // CHECK:STDOUT: %Z.WithSelf.decl = interface_with_self_decl @Z [concrete] // CHECK:STDOUT: // CHECK:STDOUT: !with Self: // CHECK:STDOUT: %Z.WithSelf.Zero.decl: @Z.WithSelf.%Z.WithSelf.Zero.type (%Z.WithSelf.Zero.type.e4a) = fn_decl @Z.WithSelf.Zero [symbolic = @Z.WithSelf.%Z.WithSelf.Zero (constants.%Z.WithSelf.Zero.3f4)] {} {} // CHECK:STDOUT: %assoc0: %Z.assoc_type = assoc_entity element0, %Z.WithSelf.Zero.decl [concrete = constants.%assoc0.cc0] // CHECK:STDOUT: impl_decl @.as.Z.impl [concrete] {} { // CHECK:STDOUT: %Z.ref: type = name_ref Z, file.%Z.decl [concrete = constants.%Z.type] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = %Self // CHECK:STDOUT: .Z = // CHECK:STDOUT: .Zero = @Z.WithSelf.%assoc0 // CHECK:STDOUT: .Z = // CHECK:STDOUT: witness = (@Z.WithSelf.%Z.WithSelf.Zero.decl) // CHECK:STDOUT: // CHECK:STDOUT: !requires: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic impl @.as.Z.impl(@Z.%Self: %Z.type) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: %Self: %Z.type = symbolic_binding Self, 0 [symbolic = %Self (constants.%Self.c59)] // CHECK:STDOUT: %.as.Z.impl.Zero.type: type = fn_type @.as.Z.impl.Zero, @.as.Z.impl(%Self) [symbolic = %.as.Z.impl.Zero.type (constants.%.as.Z.impl.Zero.type)] // CHECK:STDOUT: %.as.Z.impl.Zero: @.as.Z.impl.%.as.Z.impl.Zero.type (%.as.Z.impl.Zero.type) = struct_value () [symbolic = %.as.Z.impl.Zero (constants.%.as.Z.impl.Zero)] // CHECK:STDOUT: // CHECK:STDOUT: impl: as %Z.ref { // CHECK:STDOUT: %.as.Z.impl.Zero.decl: @.as.Z.impl.%.as.Z.impl.Zero.type (%.as.Z.impl.Zero.type) = fn_decl @.as.Z.impl.Zero [symbolic = @.as.Z.impl.%.as.Z.impl.Zero (constants.%.as.Z.impl.Zero)] {} {} // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Zero = %.as.Z.impl.Zero.decl // CHECK:STDOUT: witness = // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: impl @Point.as.Z.impl: %Self.ref as %Z.ref { // CHECK:STDOUT: %Point.as.Z.impl.Zero.decl: %Point.as.Z.impl.Zero.type = fn_decl @Point.as.Z.impl.Zero [concrete = constants.%Point.as.Z.impl.Zero] {} {} // CHECK:STDOUT: %Z.impl_witness_table = impl_witness_table (%Point.as.Z.impl.Zero.decl), @Point.as.Z.impl [concrete] // CHECK:STDOUT: %Z.impl_witness: = impl_witness %Z.impl_witness_table [concrete = constants.%Z.impl_witness] // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Zero = %Point.as.Z.impl.Zero.decl // CHECK:STDOUT: witness = %Z.impl_witness // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @Point { // CHECK:STDOUT: impl_decl @Point.as.Z.impl [concrete] {} { // CHECK:STDOUT: %Self.ref: type = name_ref Self, constants.%Point [concrete = constants.%Point] // CHECK:STDOUT: %Z.ref: type = name_ref Z, file.%Z.decl [concrete = constants.%Z.type] // CHECK:STDOUT: } // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%Point // CHECK:STDOUT: .Z = // CHECK:STDOUT: .Zero = // CHECK:STDOUT: extend @Point.as.Z.impl.%Z.ref // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic fn @Z.WithSelf.Zero(@Z.%Self: %Z.type) { // CHECK:STDOUT: fn(); // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic fn @.as.Z.impl.Zero(@Z.%Self: %Z.type) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: // CHECK:STDOUT: fn() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @Point.as.Z.impl.Zero() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @F() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %.loc25_5.1: %empty_struct_type = struct_literal () [concrete = constants.%empty_struct] // CHECK:STDOUT: %Point.ref: type = name_ref Point, file.%Point.decl [concrete = constants.%Point] // CHECK:STDOUT: %.loc25_5.2: ref %Point = temporary_storage // CHECK:STDOUT: %.loc25_5.3: init %Point to %.loc25_5.2 = class_init () [concrete = constants.%Point.val] // CHECK:STDOUT: %.loc25_7.1: init %Point = converted %.loc25_5.1, %.loc25_5.3 [concrete = constants.%Point.val] // CHECK:STDOUT: %.loc25_7.2: ref %Point = temporary %.loc25_5.2, %.loc25_7.1 // CHECK:STDOUT: %Zero.ref: %Z.assoc_type = name_ref Zero, @Z.WithSelf.%assoc0 [concrete = constants.%assoc0.cc0] // CHECK:STDOUT: %impl.elem0: %.c1b = impl_witness_access constants.%Z.impl_witness, element0 [concrete = constants.%Point.as.Z.impl.Zero] // CHECK:STDOUT: %Point.as.Z.impl.Zero.call: init %empty_tuple.type = call %impl.elem0() // CHECK:STDOUT: %Destroy.Op.bound: = bound_method %.loc25_7.2, constants.%Destroy.Op // CHECK:STDOUT: %Destroy.Op.call: init %empty_tuple.type = call %Destroy.Op.bound(%.loc25_7.2) // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @Destroy.Op(%self.param: ref %Point) = "no_op"; // CHECK:STDOUT: // CHECK:STDOUT: specific @Z.WithSelf(constants.%Self.c59) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: %Self => constants.%Self.c59 // CHECK:STDOUT: %Z.WithSelf.Zero.type => constants.%Z.WithSelf.Zero.type.e4a // CHECK:STDOUT: %Z.WithSelf.Zero => constants.%Z.WithSelf.Zero.3f4 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @Z.WithSelf.Zero(constants.%Self.c59) {} // CHECK:STDOUT: // CHECK:STDOUT: specific @.as.Z.impl(constants.%Self.c59) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: %Self => constants.%Self.c59 // CHECK:STDOUT: %.as.Z.impl.Zero.type => constants.%.as.Z.impl.Zero.type // CHECK:STDOUT: %.as.Z.impl.Zero => constants.%.as.Z.impl.Zero // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @.as.Z.impl.Zero(constants.%Self.c59) {} // CHECK:STDOUT: // CHECK:STDOUT: specific @Z.WithSelf(constants.%Z.facet) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: %Self => constants.%Z.facet // CHECK:STDOUT: %Z.WithSelf.Zero.type => constants.%Z.WithSelf.Zero.type.bb1 // CHECK:STDOUT: %Z.WithSelf.Zero => constants.%Z.WithSelf.Zero.22c // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @Z.WithSelf.Zero(constants.%Z.facet) {} // CHECK:STDOUT: // CHECK:STDOUT: specific @Z.WithSelf(constants.%Point.type.facet) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: %Self => constants.%Point.type.facet // CHECK:STDOUT: %Z.WithSelf.Zero.type => constants.%Z.WithSelf.Zero.type.15e // CHECK:STDOUT: %Z.WithSelf.Zero => constants.%Z.WithSelf.Zero.55b // CHECK:STDOUT: } // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/impl/fail_extend_impl_type_as.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/int.carbon // TODO: Add ranges and switch to "--dump-sem-ir-ranges=only". // EXTRA-ARGS: --dump-sem-ir-ranges=if-present // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/impl/fail_extend_impl_type_as.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/impl/fail_extend_impl_type_as.carbon interface I {} class C { // CHECK:STDERR: fail_extend_impl_type_as.carbon:[[@LINE+4]]:3: error: cannot `extend` an `impl` with an explicit self type [ExtendImplSelfAs] // CHECK:STDERR: extend impl i32 as I {} // CHECK:STDERR: ^~~~~~ // CHECK:STDERR: extend impl i32 as I {} } class D { // CHECK:STDERR: fail_extend_impl_type_as.carbon:[[@LINE+7]]:3: error: cannot `extend` an `impl` with an explicit self type [ExtendImplSelfAs] // CHECK:STDERR: extend impl D as I; // CHECK:STDERR: ^~~~~~ // CHECK:STDERR: fail_extend_impl_type_as.carbon:[[@LINE+4]]:15: note: remove the explicit `Self` type here [ExtendImplSelfAsDefault] // CHECK:STDERR: extend impl D as I; // CHECK:STDERR: ^ // CHECK:STDERR: extend impl D as I; } class E { // CHECK:STDERR: fail_extend_impl_type_as.carbon:[[@LINE+11]]:3: error: cannot `extend` an `impl` with an explicit self type [ExtendImplSelfAs] // CHECK:STDERR: extend impl Self as I {} // CHECK:STDERR: ^~~~~~ // CHECK:STDERR: fail_extend_impl_type_as.carbon:[[@LINE+8]]:15: note: remove the explicit `Self` type here [ExtendImplSelfAsDefault] // CHECK:STDERR: extend impl Self as I {} // CHECK:STDERR: ^~~~ // CHECK:STDERR: // CHECK:STDERR: fail_extend_impl_type_as.carbon:[[@LINE-11]]:3: error: impl declared but not defined [ImplMissingDefinition] // CHECK:STDERR: extend impl D as I; // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: extend impl Self as I {} } // CHECK:STDOUT: --- fail_extend_impl_type_as.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %I.type: type = facet_type <@I> [concrete] // CHECK:STDOUT: %Self: %I.type = symbolic_binding Self, 0 [symbolic] // CHECK:STDOUT: %C: type = class_type @C [concrete] // CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [concrete] // CHECK:STDOUT: %Int.type: type = generic_class_type @Int [concrete] // CHECK:STDOUT: %Int.generic: %Int.type = struct_value () [concrete] // CHECK:STDOUT: %i32: type = class_type @Int, @Int(%int_32) [concrete] // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %complete_type.357: = complete_type_witness %empty_struct_type [concrete] // CHECK:STDOUT: %D: type = class_type @D [concrete] // CHECK:STDOUT: %E: type = class_type @E [concrete] // CHECK:STDOUT: %I.impl_witness.1f6: = impl_witness @E.as.I.impl.%I.impl_witness_table [concrete] // CHECK:STDOUT: %I.facet: %I.type = facet_value %E, (%I.impl_witness.1f6) [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: .Int = %Core.Int // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: %Core.Int: %Int.type = import_ref Core//prelude/parts/int, Int, loaded [concrete = constants.%Int.generic] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .I = %I.decl // CHECK:STDOUT: .C = %C.decl // CHECK:STDOUT: .D = %D.decl // CHECK:STDOUT: .E = %E.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %I.decl: type = interface_decl @I [concrete = constants.%I.type] {} {} // CHECK:STDOUT: %C.decl: type = class_decl @C [concrete = constants.%C] {} {} // CHECK:STDOUT: %D.decl: type = class_decl @D [concrete = constants.%D] {} {} // CHECK:STDOUT: %E.decl: type = class_decl @E [concrete = constants.%E] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: interface @I { // CHECK:STDOUT: %Self: %I.type = symbolic_binding Self, 0 [symbolic = constants.%Self] // CHECK:STDOUT: %I.WithSelf.decl = interface_with_self_decl @I [concrete] // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = %Self // CHECK:STDOUT: witness = () // CHECK:STDOUT: // CHECK:STDOUT: !requires: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: impl @.as.I.impl: as %I.ref { // CHECK:STDOUT: !members: // CHECK:STDOUT: witness = // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: impl @D.as.I.impl: %D.ref as %I.ref; // CHECK:STDOUT: // CHECK:STDOUT: impl @E.as.I.impl: %Self.ref as %I.ref { // CHECK:STDOUT: %I.impl_witness_table = impl_witness_table (), @E.as.I.impl [concrete] // CHECK:STDOUT: %I.impl_witness: = impl_witness %I.impl_witness_table [concrete = constants.%I.impl_witness.1f6] // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: witness = %I.impl_witness // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @C { // CHECK:STDOUT: impl_decl @.as.I.impl [concrete] {} { // CHECK:STDOUT: %i32: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %I.ref: type = name_ref I, file.%I.decl [concrete = constants.%I.type] // CHECK:STDOUT: } // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type.357] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%C // CHECK:STDOUT: .I = // CHECK:STDOUT: has_error // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @D { // CHECK:STDOUT: impl_decl @D.as.I.impl [concrete] {} { // CHECK:STDOUT: %D.ref: type = name_ref D, file.%D.decl [concrete = constants.%D] // CHECK:STDOUT: %I.ref: type = name_ref I, file.%I.decl [concrete = constants.%I.type] // CHECK:STDOUT: } // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type.357] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%D // CHECK:STDOUT: .D = // CHECK:STDOUT: .I = // CHECK:STDOUT: extend @D.as.I.impl.%I.ref // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @E { // CHECK:STDOUT: impl_decl @E.as.I.impl [concrete] {} { // CHECK:STDOUT: %Self.ref: type = name_ref Self, constants.%E [concrete = constants.%E] // CHECK:STDOUT: %I.ref: type = name_ref I, file.%I.decl [concrete = constants.%I.type] // CHECK:STDOUT: } // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type.357] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%E // CHECK:STDOUT: .I = // CHECK:STDOUT: extend @E.as.I.impl.%I.ref // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @I.WithSelf(constants.%Self) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @I.WithSelf(constants.%I.facet) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: } // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/impl/fail_extend_non_interface.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/int.carbon // TODO: Add ranges and switch to "--dump-sem-ir-ranges=only". // EXTRA-ARGS: --dump-sem-ir-ranges=if-present // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/impl/fail_extend_non_interface.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/impl/fail_extend_non_interface.carbon class C { // CHECK:STDERR: fail_extend_non_interface.carbon:[[@LINE+4]]:3: error: impl as non-facet type `i32` [ImplAsNonFacetType] // CHECK:STDERR: extend impl as i32; // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: extend impl as i32; } // CHECK:STDOUT: --- fail_extend_non_interface.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %C: type = class_type @C [concrete] // CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [concrete] // CHECK:STDOUT: %Int.type: type = generic_class_type @Int [concrete] // CHECK:STDOUT: %Int.generic: %Int.type = struct_value () [concrete] // CHECK:STDOUT: %i32: type = class_type @Int, @Int(%int_32) [concrete] // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %complete_type.357: = complete_type_witness %empty_struct_type [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: .Int = %Core.Int // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: %Core.Int: %Int.type = import_ref Core//prelude/parts/int, Int, loaded [concrete = constants.%Int.generic] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .C = %C.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %C.decl: type = class_decl @C [concrete = constants.%C] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: impl @C.as..impl: %Self.ref as %i32; // CHECK:STDOUT: // CHECK:STDOUT: class @C { // CHECK:STDOUT: impl_decl @C.as..impl [concrete] {} { // CHECK:STDOUT: %Self.ref: type = name_ref Self, constants.%C [concrete = constants.%C] // CHECK:STDOUT: %i32: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: } // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type.357] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%C // CHECK:STDOUT: has_error // CHECK:STDOUT: } // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/impl/fail_extend_partially_defined_interface.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/none.carbon // TODO: Add ranges and switch to "--dump-sem-ir-ranges=only". // EXTRA-ARGS: --dump-sem-ir-ranges=if-present // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/impl/fail_extend_partially_defined_interface.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/impl/fail_extend_partially_defined_interface.carbon interface I { class C { // CHECK:STDERR: fail_extend_partially_defined_interface.carbon:[[@LINE+7]]:5: error: `extend impl as` incomplete facet type `I` [ExtendImplAsIncomplete] // CHECK:STDERR: extend impl as I; // CHECK:STDERR: ^~~~~~~~~~~~~~~~~ // CHECK:STDERR: fail_extend_partially_defined_interface.carbon:[[@LINE-5]]:1: note: interface is currently being defined [InterfaceIncompleteWithinDefinition] // CHECK:STDERR: interface I { // CHECK:STDERR: ^~~~~~~~~~~~~ // CHECK:STDERR: extend impl as I; } } // CHECK:STDOUT: --- fail_extend_partially_defined_interface.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %I.type: type = facet_type <@I> [concrete] // CHECK:STDOUT: %Self: %I.type = symbolic_binding Self, 0 [symbolic] // CHECK:STDOUT: %C: type = class_type @C, @C(%Self) [symbolic] // CHECK:STDOUT: %I.impl_witness: = impl_witness @C.as.I.impl.%I.impl_witness_table, @C.as.I.impl(%Self) [symbolic] // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %complete_type: = complete_type_witness %empty_struct_type [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .I = %I.decl // CHECK:STDOUT: } // CHECK:STDOUT: %I.decl: type = interface_decl @I [concrete = constants.%I.type] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: interface @I { // CHECK:STDOUT: %Self: %I.type = symbolic_binding Self, 0 [symbolic = constants.%Self] // CHECK:STDOUT: %I.WithSelf.decl = interface_with_self_decl @I [concrete] // CHECK:STDOUT: // CHECK:STDOUT: !with Self: // CHECK:STDOUT: %C.decl: type = class_decl @C [symbolic = @I.WithSelf.%C (constants.%C)] {} {} // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = %Self // CHECK:STDOUT: .I = // CHECK:STDOUT: .C = @I.WithSelf.%C.decl // CHECK:STDOUT: .I = // CHECK:STDOUT: witness = () // CHECK:STDOUT: // CHECK:STDOUT: !requires: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic impl @C.as.I.impl(@I.%Self: %I.type) { // CHECK:STDOUT: %Self: %I.type = symbolic_binding Self, 0 [symbolic = %Self (constants.%Self)] // CHECK:STDOUT: %C: type = class_type @C, @C(%Self) [symbolic = %C (constants.%C)] // CHECK:STDOUT: %I.impl_witness.loc24_21.2: = impl_witness %I.impl_witness_table, @C.as.I.impl(%Self) [symbolic = %I.impl_witness.loc24_21.2 (constants.%I.impl_witness)] // CHECK:STDOUT: // CHECK:STDOUT: impl: %Self.ref as %I.ref; // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic class @C(@I.%Self: %I.type) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: // CHECK:STDOUT: class { // CHECK:STDOUT: impl_decl @C.as.I.impl [concrete] {} { // CHECK:STDOUT: %Self.ref: type = name_ref Self, constants.%C [symbolic = %C (constants.%C)] // CHECK:STDOUT: %I.ref: type = name_ref I, file.%I.decl [concrete = constants.%I.type] // CHECK:STDOUT: } // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%C // CHECK:STDOUT: .I = // CHECK:STDOUT: has_error // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @I.WithSelf(constants.%Self) {} // CHECK:STDOUT: // CHECK:STDOUT: specific @C(constants.%Self) {} // CHECK:STDOUT: // CHECK:STDOUT: specific @C.as.I.impl(constants.%Self) { // CHECK:STDOUT: %Self => constants.%Self // CHECK:STDOUT: %C => constants.%C // CHECK:STDOUT: %I.impl_witness.loc24_21.2 => constants.%I.impl_witness // CHECK:STDOUT: } // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/impl/fail_extend_undefined_interface.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/none.carbon // TODO: Add ranges and switch to "--dump-sem-ir-ranges=only". // EXTRA-ARGS: --dump-sem-ir-ranges=if-present // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/impl/fail_extend_undefined_interface.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/impl/fail_extend_undefined_interface.carbon interface I; class C { // CHECK:STDERR: fail_extend_undefined_interface.carbon:[[@LINE+7]]:3: error: `extend impl as` incomplete facet type `I` [ExtendImplAsIncomplete] // CHECK:STDERR: extend impl as I; // CHECK:STDERR: ^~~~~~~~~~~~~~~~~ // CHECK:STDERR: fail_extend_undefined_interface.carbon:[[@LINE-6]]:1: note: interface was forward declared here [InterfaceForwardDeclaredHere] // CHECK:STDERR: interface I; // CHECK:STDERR: ^~~~~~~~~~~~ // CHECK:STDERR: extend impl as I; } fn F(c: C) { C.F(); c.F(); } // CHECK:STDOUT: --- fail_extend_undefined_interface.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %I.type: type = facet_type <@I> [concrete] // CHECK:STDOUT: %C: type = class_type @C [concrete] // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %complete_type: = complete_type_witness %empty_struct_type [concrete] // CHECK:STDOUT: %pattern_type: type = pattern_type %C [concrete] // CHECK:STDOUT: %F.type: type = fn_type @F [concrete] // CHECK:STDOUT: %F: %F.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .I = %I.decl // CHECK:STDOUT: .C = %C.decl // CHECK:STDOUT: .F = %F.decl // CHECK:STDOUT: } // CHECK:STDOUT: %I.decl: type = interface_decl @I [concrete = constants.%I.type] {} {} // CHECK:STDOUT: %C.decl: type = class_decl @C [concrete = constants.%C] {} {} // CHECK:STDOUT: %F.decl: %F.type = fn_decl @F [concrete = constants.%F] { // CHECK:STDOUT: %c.patt: %pattern_type = value_binding_pattern c [concrete] // CHECK:STDOUT: %c.param_patt: %pattern_type = value_param_pattern %c.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %c.param: %C = value_param call_param0 // CHECK:STDOUT: %C.ref.loc28: type = name_ref C, file.%C.decl [concrete = constants.%C] // CHECK:STDOUT: %c: %C = value_binding c, %c.param // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: interface @I; // CHECK:STDOUT: // CHECK:STDOUT: impl @C.as.I.impl: %Self.ref as %I.ref; // CHECK:STDOUT: // CHECK:STDOUT: class @C { // CHECK:STDOUT: impl_decl @C.as.I.impl [concrete] {} { // CHECK:STDOUT: %Self.ref: type = name_ref Self, constants.%C [concrete = constants.%C] // CHECK:STDOUT: %I.ref: type = name_ref I, file.%I.decl [concrete = constants.%I.type] // CHECK:STDOUT: } // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%C // CHECK:STDOUT: .I = // CHECK:STDOUT: .F = // CHECK:STDOUT: has_error // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @F(%c.param: %C) { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %C.ref.loc29: type = name_ref C, file.%C.decl [concrete = constants.%C] // CHECK:STDOUT: %F.ref.loc29: = name_ref F, [concrete = ] // CHECK:STDOUT: %c.ref: %C = name_ref c, %c // CHECK:STDOUT: %F.ref.loc30: = name_ref F, [concrete = ] // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/impl/fail_impl_as_scope.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/destroy.carbon // TODO: Add ranges and switch to "--dump-sem-ir-ranges=only". // EXTRA-ARGS: --dump-sem-ir-ranges=if-present // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/impl/fail_impl_as_scope.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/impl/fail_impl_as_scope.carbon // --- fail_impl_as_file_scope.carbon library "[[@TEST_NAME]]"; interface I {} fn G() {} // CHECK:STDERR: fail_impl_as_file_scope.carbon:[[@LINE+4]]:6: error: `impl as` can only be used in a class [ImplAsOutsideClass] // CHECK:STDERR: impl as I {} // CHECK:STDERR: ^~ // CHECK:STDERR: impl as I {} fn F() { G(); } // --- fail_impl_as_function_scope.carbon library "[[@TEST_NAME]]"; interface J {} fn G() {} fn F() { // CHECK:STDERR: fail_impl_as_function_scope.carbon:[[@LINE+4]]:8: error: `impl as` can only be used in a class [ImplAsOutsideClass] // CHECK:STDERR: impl as J {} // CHECK:STDERR: ^~ // CHECK:STDERR: impl as J {} G(); } // --- fail_impl_as_self_interface.carbon library "[[@TEST_NAME]]"; interface Z { fn Zero(); fn Method[self: Self](); // CHECK:STDERR: fail_impl_as_self_interface.carbon:[[@LINE+4]]:9: error: `impl as` can only be used in a class [ImplAsOutsideClass] // CHECK:STDERR: impl as Z { // CHECK:STDERR: ^~ // CHECK:STDERR: impl as Z { fn Zero() {} fn Method[unused self: Self]() {} } } class Point { impl as Z { fn Zero() {} fn Method[unused self: Self]() {} } } fn F() { // Even if the `impl` is diagnosed above, we must not add the impl of the // interface to itself in a way that allows it to be used during impl lookup, // or we end up with infinite impl lookup recursion here. Point.(Z.Zero)(); ({} as Point).(Z.Method)(); } // --- fail_impl_as_other_interface.carbon library "[[@TEST_NAME]]"; interface A { fn B(); } interface C {} fn G() {} class X { impl as A { // CHECK:STDERR: fail_impl_as_other_interface.carbon:[[@LINE+4]]:10: error: `impl as` can only be used in a class [ImplAsOutsideClass] // CHECK:STDERR: impl as C {} // CHECK:STDERR: ^~ // CHECK:STDERR: impl as C {} fn B() { G(); } } } // CHECK:STDOUT: --- fail_impl_as_file_scope.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %I.type: type = facet_type <@I> [concrete] // CHECK:STDOUT: %Self: %I.type = symbolic_binding Self, 0 [symbolic] // CHECK:STDOUT: %G.type: type = fn_type @G [concrete] // CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] // CHECK:STDOUT: %G: %G.type = struct_value () [concrete] // CHECK:STDOUT: %F.type: type = fn_type @F [concrete] // CHECK:STDOUT: %F: %F.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .I = %I.decl // CHECK:STDOUT: .G = %G.decl // CHECK:STDOUT: .F = %F.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %I.decl: type = interface_decl @I [concrete = constants.%I.type] {} {} // CHECK:STDOUT: %G.decl: %G.type = fn_decl @G [concrete = constants.%G] {} {} // CHECK:STDOUT: impl_decl @.as.I.impl [concrete] {} { // CHECK:STDOUT: %I.ref: type = name_ref I, file.%I.decl [concrete = constants.%I.type] // CHECK:STDOUT: } // CHECK:STDOUT: %F.decl: %F.type = fn_decl @F [concrete = constants.%F] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: interface @I { // CHECK:STDOUT: %Self: %I.type = symbolic_binding Self, 0 [symbolic = constants.%Self] // CHECK:STDOUT: %I.WithSelf.decl = interface_with_self_decl @I [concrete] // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = %Self // CHECK:STDOUT: witness = () // CHECK:STDOUT: // CHECK:STDOUT: !requires: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: impl @.as.I.impl: as %I.ref { // CHECK:STDOUT: !members: // CHECK:STDOUT: witness = // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @G() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @F() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %G.ref: %G.type = name_ref G, file.%G.decl [concrete = constants.%G] // CHECK:STDOUT: %G.call: init %empty_tuple.type = call %G.ref() // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @I.WithSelf(constants.%Self) {} // CHECK:STDOUT: // CHECK:STDOUT: --- fail_impl_as_function_scope.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %J.type: type = facet_type <@J> [concrete] // CHECK:STDOUT: %Self: %J.type = symbolic_binding Self, 0 [symbolic] // CHECK:STDOUT: %G.type: type = fn_type @G [concrete] // CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] // CHECK:STDOUT: %G: %G.type = struct_value () [concrete] // CHECK:STDOUT: %F.type: type = fn_type @F [concrete] // CHECK:STDOUT: %F: %F.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .J = %J.decl // CHECK:STDOUT: .G = %G.decl // CHECK:STDOUT: .F = %F.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %J.decl: type = interface_decl @J [concrete = constants.%J.type] {} {} // CHECK:STDOUT: %G.decl: %G.type = fn_decl @G [concrete = constants.%G] {} {} // CHECK:STDOUT: %F.decl: %F.type = fn_decl @F [concrete = constants.%F] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: interface @J { // CHECK:STDOUT: %Self: %J.type = symbolic_binding Self, 0 [symbolic = constants.%Self] // CHECK:STDOUT: %J.WithSelf.decl = interface_with_self_decl @J [concrete] // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = %Self // CHECK:STDOUT: witness = () // CHECK:STDOUT: // CHECK:STDOUT: !requires: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: impl @.as.J.impl: as %J.ref { // CHECK:STDOUT: !members: // CHECK:STDOUT: witness = // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @G() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @F() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: impl_decl @.as.J.impl [concrete] {} { // CHECK:STDOUT: %J.ref: type = name_ref J, file.%J.decl [concrete = constants.%J.type] // CHECK:STDOUT: } // CHECK:STDOUT: %G.ref: %G.type = name_ref G, file.%G.decl [concrete = constants.%G] // CHECK:STDOUT: %G.call: init %empty_tuple.type = call %G.ref() // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @J.WithSelf(constants.%Self) {} // CHECK:STDOUT: // CHECK:STDOUT: --- fail_impl_as_self_interface.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %Z.type: type = facet_type <@Z> [concrete] // CHECK:STDOUT: %Self.c59: %Z.type = symbolic_binding Self, 0 [symbolic] // CHECK:STDOUT: %Z.WithSelf.Zero.type.e4a: type = fn_type @Z.WithSelf.Zero, @Z.WithSelf(%Self.c59) [symbolic] // CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] // CHECK:STDOUT: %Z.WithSelf.Zero.3f4: %Z.WithSelf.Zero.type.e4a = struct_value () [symbolic] // CHECK:STDOUT: %Z.assoc_type: type = assoc_entity_type @Z [concrete] // CHECK:STDOUT: %assoc0.cc0: %Z.assoc_type = assoc_entity element0, @Z.WithSelf.%Z.WithSelf.Zero.decl [concrete] // CHECK:STDOUT: %Self.binding.as_type.62d: type = symbolic_binding_type Self, 0, %Self.c59 [symbolic] // CHECK:STDOUT: %pattern_type.84b: type = pattern_type %Self.binding.as_type.62d [symbolic] // CHECK:STDOUT: %Z.WithSelf.Method.type.dfd: type = fn_type @Z.WithSelf.Method, @Z.WithSelf(%Self.c59) [symbolic] // CHECK:STDOUT: %Z.WithSelf.Method.324: %Z.WithSelf.Method.type.dfd = struct_value () [symbolic] // CHECK:STDOUT: %assoc1: %Z.assoc_type = assoc_entity element1, @Z.WithSelf.%Z.WithSelf.Method.decl [concrete] // CHECK:STDOUT: %.as.Z.impl.Zero.type: type = fn_type @.as.Z.impl.Zero, @.as.Z.impl(%Self.c59) [symbolic] // CHECK:STDOUT: %.as.Z.impl.Zero: %.as.Z.impl.Zero.type = struct_value () [symbolic] // CHECK:STDOUT: %.as.Z.impl.Method.type: type = fn_type @.as.Z.impl.Method, @.as.Z.impl(%Self.c59) [symbolic] // CHECK:STDOUT: %.as.Z.impl.Method: %.as.Z.impl.Method.type = struct_value () [symbolic] // CHECK:STDOUT: %require_complete: = require_complete_type %Self.binding.as_type.62d [symbolic] // CHECK:STDOUT: %Point: type = class_type @Point [concrete] // CHECK:STDOUT: %Z.impl_witness: = impl_witness @Point.as.Z.impl.%Z.impl_witness_table [concrete] // CHECK:STDOUT: %Point.as.Z.impl.Zero.type: type = fn_type @Point.as.Z.impl.Zero [concrete] // CHECK:STDOUT: %Point.as.Z.impl.Zero: %Point.as.Z.impl.Zero.type = struct_value () [concrete] // CHECK:STDOUT: %pattern_type.c86: type = pattern_type %Point [concrete] // CHECK:STDOUT: %Point.as.Z.impl.Method.type: type = fn_type @Point.as.Z.impl.Method [concrete] // CHECK:STDOUT: %Point.as.Z.impl.Method: %Point.as.Z.impl.Method.type = struct_value () [concrete] // CHECK:STDOUT: %Z.facet: %Z.type = facet_value %Point, (%Z.impl_witness) [concrete] // CHECK:STDOUT: %Z.WithSelf.Zero.type.949: type = fn_type @Z.WithSelf.Zero, @Z.WithSelf(%Z.facet) [concrete] // CHECK:STDOUT: %Z.WithSelf.Zero.3ae: %Z.WithSelf.Zero.type.949 = struct_value () [concrete] // CHECK:STDOUT: %Z.WithSelf.Method.type.707: type = fn_type @Z.WithSelf.Method, @Z.WithSelf(%Z.facet) [concrete] // CHECK:STDOUT: %Z.WithSelf.Method.74d: %Z.WithSelf.Method.type.707 = struct_value () [concrete] // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %complete_type: = complete_type_witness %empty_struct_type [concrete] // CHECK:STDOUT: %F.type: type = fn_type @F [concrete] // CHECK:STDOUT: %F: %F.type = struct_value () [concrete] // CHECK:STDOUT: %.8c9: type = fn_type_with_self_type %Z.WithSelf.Zero.type.949, %Z.facet [concrete] // CHECK:STDOUT: %empty_struct: %empty_struct_type = struct_value () [concrete] // CHECK:STDOUT: %Point.val: %Point = struct_value () [concrete] // CHECK:STDOUT: %.429: type = fn_type_with_self_type %Z.WithSelf.Method.type.707, %Z.facet [concrete] // CHECK:STDOUT: %Point.as.Z.impl.Method.bound: = bound_method %Point.val, %Point.as.Z.impl.Method [concrete] // CHECK:STDOUT: %Destroy.type: type = facet_type <@Destroy> [concrete] // CHECK:STDOUT: %Destroy.Op.type: type = fn_type @Destroy.Op [concrete] // CHECK:STDOUT: %Destroy.Op: %Destroy.Op.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: .Destroy = %Core.Destroy // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: %Core.Destroy: type = import_ref Core//prelude/parts/destroy, Destroy, loaded [concrete = constants.%Destroy.type] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .Z = %Z.decl // CHECK:STDOUT: .Point = %Point.decl // CHECK:STDOUT: .F = %F.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %Z.decl: type = interface_decl @Z [concrete = constants.%Z.type] {} {} // CHECK:STDOUT: %Point.decl: type = class_decl @Point [concrete = constants.%Point] {} {} // CHECK:STDOUT: %F.decl: %F.type = fn_decl @F [concrete = constants.%F] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: interface @Z { // CHECK:STDOUT: %Self: %Z.type = symbolic_binding Self, 0 [symbolic = constants.%Self.c59] // CHECK:STDOUT: %Z.WithSelf.decl = interface_with_self_decl @Z [concrete] // CHECK:STDOUT: // CHECK:STDOUT: !with Self: // CHECK:STDOUT: %Z.WithSelf.Zero.decl: @Z.WithSelf.%Z.WithSelf.Zero.type (%Z.WithSelf.Zero.type.e4a) = fn_decl @Z.WithSelf.Zero [symbolic = @Z.WithSelf.%Z.WithSelf.Zero (constants.%Z.WithSelf.Zero.3f4)] {} {} // CHECK:STDOUT: %assoc0: %Z.assoc_type = assoc_entity element0, %Z.WithSelf.Zero.decl [concrete = constants.%assoc0.cc0] // CHECK:STDOUT: %Z.WithSelf.Method.decl: @Z.WithSelf.%Z.WithSelf.Method.type (%Z.WithSelf.Method.type.dfd) = fn_decl @Z.WithSelf.Method [symbolic = @Z.WithSelf.%Z.WithSelf.Method (constants.%Z.WithSelf.Method.324)] { // CHECK:STDOUT: %self.patt: @Z.WithSelf.Method.%pattern_type (%pattern_type.84b) = value_binding_pattern self [concrete] // CHECK:STDOUT: %self.param_patt: @Z.WithSelf.Method.%pattern_type (%pattern_type.84b) = value_param_pattern %self.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %self.param: @Z.WithSelf.Method.%Self.binding.as_type (%Self.binding.as_type.62d) = value_param call_param0 // CHECK:STDOUT: %.loc5_19.1: type = splice_block %.loc5_19.2 [symbolic = %Self.binding.as_type (constants.%Self.binding.as_type.62d)] { // CHECK:STDOUT: %Self.ref: %Z.type = name_ref Self, @Z.%Self [symbolic = %Self (constants.%Self.c59)] // CHECK:STDOUT: %Self.as_type: type = facet_access_type %Self.ref [symbolic = %Self.binding.as_type (constants.%Self.binding.as_type.62d)] // CHECK:STDOUT: %.loc5_19.2: type = converted %Self.ref, %Self.as_type [symbolic = %Self.binding.as_type (constants.%Self.binding.as_type.62d)] // CHECK:STDOUT: } // CHECK:STDOUT: %self: @Z.WithSelf.Method.%Self.binding.as_type (%Self.binding.as_type.62d) = value_binding self, %self.param // CHECK:STDOUT: } // CHECK:STDOUT: %assoc1: %Z.assoc_type = assoc_entity element1, %Z.WithSelf.Method.decl [concrete = constants.%assoc1] // CHECK:STDOUT: impl_decl @.as.Z.impl [concrete] {} { // CHECK:STDOUT: %Z.ref: type = name_ref Z, file.%Z.decl [concrete = constants.%Z.type] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = %Self // CHECK:STDOUT: .Z = // CHECK:STDOUT: .Zero = @Z.WithSelf.%assoc0 // CHECK:STDOUT: .Method = @Z.WithSelf.%assoc1 // CHECK:STDOUT: .Z = // CHECK:STDOUT: witness = (@Z.WithSelf.%Z.WithSelf.Zero.decl, @Z.WithSelf.%Z.WithSelf.Method.decl) // CHECK:STDOUT: // CHECK:STDOUT: !requires: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic impl @.as.Z.impl(@Z.%Self: %Z.type) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: %Self: %Z.type = symbolic_binding Self, 0 [symbolic = %Self (constants.%Self.c59)] // CHECK:STDOUT: %.as.Z.impl.Zero.type: type = fn_type @.as.Z.impl.Zero, @.as.Z.impl(%Self) [symbolic = %.as.Z.impl.Zero.type (constants.%.as.Z.impl.Zero.type)] // CHECK:STDOUT: %.as.Z.impl.Zero: @.as.Z.impl.%.as.Z.impl.Zero.type (%.as.Z.impl.Zero.type) = struct_value () [symbolic = %.as.Z.impl.Zero (constants.%.as.Z.impl.Zero)] // CHECK:STDOUT: %.as.Z.impl.Method.type: type = fn_type @.as.Z.impl.Method, @.as.Z.impl(%Self) [symbolic = %.as.Z.impl.Method.type (constants.%.as.Z.impl.Method.type)] // CHECK:STDOUT: %.as.Z.impl.Method: @.as.Z.impl.%.as.Z.impl.Method.type (%.as.Z.impl.Method.type) = struct_value () [symbolic = %.as.Z.impl.Method (constants.%.as.Z.impl.Method)] // CHECK:STDOUT: // CHECK:STDOUT: impl: as %Z.ref { // CHECK:STDOUT: %.as.Z.impl.Zero.decl: @.as.Z.impl.%.as.Z.impl.Zero.type (%.as.Z.impl.Zero.type) = fn_decl @.as.Z.impl.Zero [symbolic = @.as.Z.impl.%.as.Z.impl.Zero (constants.%.as.Z.impl.Zero)] {} {} // CHECK:STDOUT: %.as.Z.impl.Method.decl: @.as.Z.impl.%.as.Z.impl.Method.type (%.as.Z.impl.Method.type) = fn_decl @.as.Z.impl.Method [symbolic = @.as.Z.impl.%.as.Z.impl.Method (constants.%.as.Z.impl.Method)] { // CHECK:STDOUT: %self.patt: @.as.Z.impl.Method.%pattern_type (%pattern_type.84b) = value_binding_pattern self [concrete] // CHECK:STDOUT: %self.param_patt: @.as.Z.impl.Method.%pattern_type (%pattern_type.84b) = value_param_pattern %self.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %self.param: @.as.Z.impl.Method.%Self.binding.as_type (%Self.binding.as_type.62d) = value_param call_param0 // CHECK:STDOUT: %.loc13_28.1: type = splice_block %.loc13_28.2 [symbolic = %Self.binding.as_type (constants.%Self.binding.as_type.62d)] { // CHECK:STDOUT: %Self.ref: %Z.type = name_ref Self, @Z.%Self [symbolic = %Self (constants.%Self.c59)] // CHECK:STDOUT: %Self.as_type: type = facet_access_type %Self.ref [symbolic = %Self.binding.as_type (constants.%Self.binding.as_type.62d)] // CHECK:STDOUT: %.loc13_28.2: type = converted %Self.ref, %Self.as_type [symbolic = %Self.binding.as_type (constants.%Self.binding.as_type.62d)] // CHECK:STDOUT: } // CHECK:STDOUT: %self: @.as.Z.impl.Method.%Self.binding.as_type (%Self.binding.as_type.62d) = value_binding self, %self.param // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Zero = %.as.Z.impl.Zero.decl // CHECK:STDOUT: .Method = %.as.Z.impl.Method.decl // CHECK:STDOUT: witness = // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: impl @Point.as.Z.impl: %Self.ref as %Z.ref { // CHECK:STDOUT: %Point.as.Z.impl.Zero.decl: %Point.as.Z.impl.Zero.type = fn_decl @Point.as.Z.impl.Zero [concrete = constants.%Point.as.Z.impl.Zero] {} {} // CHECK:STDOUT: %Point.as.Z.impl.Method.decl: %Point.as.Z.impl.Method.type = fn_decl @Point.as.Z.impl.Method [concrete = constants.%Point.as.Z.impl.Method] { // CHECK:STDOUT: %self.patt: %pattern_type.c86 = value_binding_pattern self [concrete] // CHECK:STDOUT: %self.param_patt: %pattern_type.c86 = value_param_pattern %self.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %self.param: %Point = value_param call_param0 // CHECK:STDOUT: %Self.ref: type = name_ref Self, constants.%Point [concrete = constants.%Point] // CHECK:STDOUT: %self: %Point = value_binding self, %self.param // CHECK:STDOUT: } // CHECK:STDOUT: %Z.impl_witness_table = impl_witness_table (%Point.as.Z.impl.Zero.decl, %Point.as.Z.impl.Method.decl), @Point.as.Z.impl [concrete] // CHECK:STDOUT: %Z.impl_witness: = impl_witness %Z.impl_witness_table [concrete = constants.%Z.impl_witness] // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Zero = %Point.as.Z.impl.Zero.decl // CHECK:STDOUT: .Method = %Point.as.Z.impl.Method.decl // CHECK:STDOUT: witness = %Z.impl_witness // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @Point { // CHECK:STDOUT: impl_decl @Point.as.Z.impl [concrete] {} { // CHECK:STDOUT: %Self.ref: type = name_ref Self, constants.%Point [concrete = constants.%Point] // CHECK:STDOUT: %Z.ref: type = name_ref Z, file.%Z.decl [concrete = constants.%Z.type] // CHECK:STDOUT: } // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%Point // CHECK:STDOUT: .Z = // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic fn @Z.WithSelf.Zero(@Z.%Self: %Z.type) { // CHECK:STDOUT: fn(); // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic fn @Z.WithSelf.Method(@Z.%Self: %Z.type) { // CHECK:STDOUT: %Self: %Z.type = symbolic_binding Self, 0 [symbolic = %Self (constants.%Self.c59)] // CHECK:STDOUT: %Self.binding.as_type: type = symbolic_binding_type Self, 0, %Self [symbolic = %Self.binding.as_type (constants.%Self.binding.as_type.62d)] // CHECK:STDOUT: %pattern_type: type = pattern_type %Self.binding.as_type [symbolic = %pattern_type (constants.%pattern_type.84b)] // CHECK:STDOUT: // CHECK:STDOUT: fn(%self.param: @Z.WithSelf.Method.%Self.binding.as_type (%Self.binding.as_type.62d)); // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic fn @.as.Z.impl.Zero(@Z.%Self: %Z.type) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: // CHECK:STDOUT: fn() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic fn @.as.Z.impl.Method(@Z.%Self: %Z.type) { // CHECK:STDOUT: %Self: %Z.type = symbolic_binding Self, 0 [symbolic = %Self (constants.%Self.c59)] // CHECK:STDOUT: %Self.binding.as_type: type = symbolic_binding_type Self, 0, %Self [symbolic = %Self.binding.as_type (constants.%Self.binding.as_type.62d)] // CHECK:STDOUT: %pattern_type: type = pattern_type %Self.binding.as_type [symbolic = %pattern_type (constants.%pattern_type.84b)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete: = require_complete_type %Self.binding.as_type [symbolic = %require_complete (constants.%require_complete)] // CHECK:STDOUT: // CHECK:STDOUT: fn(%self.param: @.as.Z.impl.Method.%Self.binding.as_type (%Self.binding.as_type.62d)) { // CHECK:STDOUT: !entry: // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @Point.as.Z.impl.Zero() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @Point.as.Z.impl.Method(%self.param: %Point) { // CHECK:STDOUT: !entry: // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @F() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %Point.ref.loc28: type = name_ref Point, file.%Point.decl [concrete = constants.%Point] // CHECK:STDOUT: %Z.ref.loc28: type = name_ref Z, file.%Z.decl [concrete = constants.%Z.type] // CHECK:STDOUT: %Zero.ref: %Z.assoc_type = name_ref Zero, @Z.WithSelf.%assoc0 [concrete = constants.%assoc0.cc0] // CHECK:STDOUT: %Z.facet: %Z.type = facet_value %Point.ref.loc28, (constants.%Z.impl_witness) [concrete = constants.%Z.facet] // CHECK:STDOUT: %.loc28: %Z.type = converted %Point.ref.loc28, %Z.facet [concrete = constants.%Z.facet] // CHECK:STDOUT: %impl.elem0: %.8c9 = impl_witness_access constants.%Z.impl_witness, element0 [concrete = constants.%Point.as.Z.impl.Zero] // CHECK:STDOUT: %Point.as.Z.impl.Zero.call: init %empty_tuple.type = call %impl.elem0() // CHECK:STDOUT: %.loc29_5.1: %empty_struct_type = struct_literal () [concrete = constants.%empty_struct] // CHECK:STDOUT: %Point.ref.loc29: type = name_ref Point, file.%Point.decl [concrete = constants.%Point] // CHECK:STDOUT: %.loc29_5.2: ref %Point = temporary_storage // CHECK:STDOUT: %.loc29_5.3: init %Point to %.loc29_5.2 = class_init () [concrete = constants.%Point.val] // CHECK:STDOUT: %.loc29_7.1: init %Point = converted %.loc29_5.1, %.loc29_5.3 [concrete = constants.%Point.val] // CHECK:STDOUT: %Z.ref.loc29: type = name_ref Z, file.%Z.decl [concrete = constants.%Z.type] // CHECK:STDOUT: %Method.ref: %Z.assoc_type = name_ref Method, @Z.WithSelf.%assoc1 [concrete = constants.%assoc1] // CHECK:STDOUT: %impl.elem1: %.429 = impl_witness_access constants.%Z.impl_witness, element1 [concrete = constants.%Point.as.Z.impl.Method] // CHECK:STDOUT: %bound_method: = bound_method %.loc29_7.1, %impl.elem1 [concrete = constants.%Point.as.Z.impl.Method.bound] // CHECK:STDOUT: %.loc29_7.2: ref %Point = temporary %.loc29_5.2, %.loc29_7.1 // CHECK:STDOUT: %.loc29_7.3: %Point = acquire_value %.loc29_7.2 // CHECK:STDOUT: %Point.as.Z.impl.Method.call: init %empty_tuple.type = call %bound_method(%.loc29_7.3) // CHECK:STDOUT: %Destroy.Op.bound: = bound_method %.loc29_7.2, constants.%Destroy.Op // CHECK:STDOUT: %Destroy.Op.call: init %empty_tuple.type = call %Destroy.Op.bound(%.loc29_7.2) // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @Destroy.Op(%self.param: ref %Point) = "no_op"; // CHECK:STDOUT: // CHECK:STDOUT: specific @Z.WithSelf(constants.%Self.c59) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: %Self => constants.%Self.c59 // CHECK:STDOUT: %Z.WithSelf.Zero.type => constants.%Z.WithSelf.Zero.type.e4a // CHECK:STDOUT: %Z.WithSelf.Zero => constants.%Z.WithSelf.Zero.3f4 // CHECK:STDOUT: %Z.WithSelf.Method.type => constants.%Z.WithSelf.Method.type.dfd // CHECK:STDOUT: %Z.WithSelf.Method => constants.%Z.WithSelf.Method.324 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @Z.WithSelf.Zero(constants.%Self.c59) {} // CHECK:STDOUT: // CHECK:STDOUT: specific @Z.WithSelf.Method(constants.%Self.c59) { // CHECK:STDOUT: %Self => constants.%Self.c59 // CHECK:STDOUT: %Self.binding.as_type => constants.%Self.binding.as_type.62d // CHECK:STDOUT: %pattern_type => constants.%pattern_type.84b // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @.as.Z.impl(constants.%Self.c59) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: %Self => constants.%Self.c59 // CHECK:STDOUT: %.as.Z.impl.Zero.type => constants.%.as.Z.impl.Zero.type // CHECK:STDOUT: %.as.Z.impl.Zero => constants.%.as.Z.impl.Zero // CHECK:STDOUT: %.as.Z.impl.Method.type => constants.%.as.Z.impl.Method.type // CHECK:STDOUT: %.as.Z.impl.Method => constants.%.as.Z.impl.Method // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @.as.Z.impl.Zero(constants.%Self.c59) {} // CHECK:STDOUT: // CHECK:STDOUT: specific @.as.Z.impl.Method(constants.%Self.c59) { // CHECK:STDOUT: %Self => constants.%Self.c59 // CHECK:STDOUT: %Self.binding.as_type => constants.%Self.binding.as_type.62d // CHECK:STDOUT: %pattern_type => constants.%pattern_type.84b // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @Z.WithSelf(constants.%Z.facet) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: %Self => constants.%Z.facet // CHECK:STDOUT: %Z.WithSelf.Zero.type => constants.%Z.WithSelf.Zero.type.949 // CHECK:STDOUT: %Z.WithSelf.Zero => constants.%Z.WithSelf.Zero.3ae // CHECK:STDOUT: %Z.WithSelf.Method.type => constants.%Z.WithSelf.Method.type.707 // CHECK:STDOUT: %Z.WithSelf.Method => constants.%Z.WithSelf.Method.74d // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @Z.WithSelf.Zero(constants.%Z.facet) {} // CHECK:STDOUT: // CHECK:STDOUT: specific @Z.WithSelf.Method(constants.%Z.facet) { // CHECK:STDOUT: %Self => constants.%Z.facet // CHECK:STDOUT: %Self.binding.as_type => constants.%Point // CHECK:STDOUT: %pattern_type => constants.%pattern_type.c86 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- fail_impl_as_other_interface.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %A.type: type = facet_type <@A> [concrete] // CHECK:STDOUT: %Self.c51: %A.type = symbolic_binding Self, 0 [symbolic] // CHECK:STDOUT: %A.WithSelf.B.type.386: type = fn_type @A.WithSelf.B, @A.WithSelf(%Self.c51) [symbolic] // CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] // CHECK:STDOUT: %A.WithSelf.B.b88: %A.WithSelf.B.type.386 = struct_value () [symbolic] // CHECK:STDOUT: %A.assoc_type: type = assoc_entity_type @A [concrete] // CHECK:STDOUT: %assoc0: %A.assoc_type = assoc_entity element0, @A.WithSelf.%A.WithSelf.B.decl [concrete] // CHECK:STDOUT: %C.type: type = facet_type <@C> [concrete] // CHECK:STDOUT: %Self.b7c: %C.type = symbolic_binding Self, 0 [symbolic] // CHECK:STDOUT: %G.type: type = fn_type @G [concrete] // CHECK:STDOUT: %G: %G.type = struct_value () [concrete] // CHECK:STDOUT: %X: type = class_type @X [concrete] // CHECK:STDOUT: %A.impl_witness: = impl_witness @X.as.A.impl.%A.impl_witness_table [concrete] // CHECK:STDOUT: %X.as.A.impl.B.type: type = fn_type @X.as.A.impl.B [concrete] // CHECK:STDOUT: %X.as.A.impl.B: %X.as.A.impl.B.type = struct_value () [concrete] // CHECK:STDOUT: %A.facet: %A.type = facet_value %X, (%A.impl_witness) [concrete] // CHECK:STDOUT: %A.WithSelf.B.type.c6b: type = fn_type @A.WithSelf.B, @A.WithSelf(%A.facet) [concrete] // CHECK:STDOUT: %A.WithSelf.B.f2a: %A.WithSelf.B.type.c6b = struct_value () [concrete] // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %complete_type: = complete_type_witness %empty_struct_type [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .A = %A.decl // CHECK:STDOUT: .C = %C.decl // CHECK:STDOUT: .G = %G.decl // CHECK:STDOUT: .X = %X.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %A.decl: type = interface_decl @A [concrete = constants.%A.type] {} {} // CHECK:STDOUT: %C.decl: type = interface_decl @C [concrete = constants.%C.type] {} {} // CHECK:STDOUT: %G.decl: %G.type = fn_decl @G [concrete = constants.%G] {} {} // CHECK:STDOUT: %X.decl: type = class_decl @X [concrete = constants.%X] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: interface @A { // CHECK:STDOUT: %Self: %A.type = symbolic_binding Self, 0 [symbolic = constants.%Self.c51] // CHECK:STDOUT: %A.WithSelf.decl = interface_with_self_decl @A [concrete] // CHECK:STDOUT: // CHECK:STDOUT: !with Self: // CHECK:STDOUT: %A.WithSelf.B.decl: @A.WithSelf.%A.WithSelf.B.type (%A.WithSelf.B.type.386) = fn_decl @A.WithSelf.B [symbolic = @A.WithSelf.%A.WithSelf.B (constants.%A.WithSelf.B.b88)] {} {} // CHECK:STDOUT: %assoc0: %A.assoc_type = assoc_entity element0, %A.WithSelf.B.decl [concrete = constants.%assoc0] // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = %Self // CHECK:STDOUT: .B = @A.WithSelf.%assoc0 // CHECK:STDOUT: witness = (@A.WithSelf.%A.WithSelf.B.decl) // CHECK:STDOUT: // CHECK:STDOUT: !requires: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: interface @C { // CHECK:STDOUT: %Self: %C.type = symbolic_binding Self, 0 [symbolic = constants.%Self.b7c] // CHECK:STDOUT: %C.WithSelf.decl = interface_with_self_decl @C [concrete] // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = %Self // CHECK:STDOUT: witness = () // CHECK:STDOUT: // CHECK:STDOUT: !requires: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: impl @X.as.A.impl: %Self.ref as %A.ref { // CHECK:STDOUT: impl_decl @.as.C.impl [concrete] {} { // CHECK:STDOUT: %C.ref: type = name_ref C, file.%C.decl [concrete = constants.%C.type] // CHECK:STDOUT: } // CHECK:STDOUT: %X.as.A.impl.B.decl: %X.as.A.impl.B.type = fn_decl @X.as.A.impl.B [concrete = constants.%X.as.A.impl.B] {} {} // CHECK:STDOUT: %A.impl_witness_table = impl_witness_table (%X.as.A.impl.B.decl), @X.as.A.impl [concrete] // CHECK:STDOUT: %A.impl_witness: = impl_witness %A.impl_witness_table [concrete = constants.%A.impl_witness] // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .C = // CHECK:STDOUT: .B = %X.as.A.impl.B.decl // CHECK:STDOUT: .G = // CHECK:STDOUT: witness = %A.impl_witness // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: impl @.as.C.impl: as %C.ref { // CHECK:STDOUT: !members: // CHECK:STDOUT: witness = // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @X { // CHECK:STDOUT: impl_decl @X.as.A.impl [concrete] {} { // CHECK:STDOUT: %Self.ref: type = name_ref Self, constants.%X [concrete = constants.%X] // CHECK:STDOUT: %A.ref: type = name_ref A, file.%A.decl [concrete = constants.%A.type] // CHECK:STDOUT: } // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%X // CHECK:STDOUT: .A = // CHECK:STDOUT: .C = // CHECK:STDOUT: .G = // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic fn @A.WithSelf.B(@A.%Self: %A.type) { // CHECK:STDOUT: fn(); // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @G() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @X.as.A.impl.B() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %G.ref: %G.type = name_ref G, file.%G.decl [concrete = constants.%G] // CHECK:STDOUT: %G.call: init %empty_tuple.type = call %G.ref() // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @A.WithSelf(constants.%Self.c51) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: %Self => constants.%Self.c51 // CHECK:STDOUT: %A.WithSelf.B.type => constants.%A.WithSelf.B.type.386 // CHECK:STDOUT: %A.WithSelf.B => constants.%A.WithSelf.B.b88 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @A.WithSelf.B(constants.%Self.c51) {} // CHECK:STDOUT: // CHECK:STDOUT: specific @C.WithSelf(constants.%Self.b7c) {} // CHECK:STDOUT: // CHECK:STDOUT: specific @A.WithSelf(constants.%A.facet) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: %Self => constants.%A.facet // CHECK:STDOUT: %A.WithSelf.B.type => constants.%A.WithSelf.B.type.c6b // CHECK:STDOUT: %A.WithSelf.B => constants.%A.WithSelf.B.f2a // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @A.WithSelf.B(constants.%A.facet) {} // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/impl/fail_impl_bad_assoc_const.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/none.carbon // TODO: Add ranges and switch to "--dump-sem-ir-ranges=only". // EXTRA-ARGS: --dump-sem-ir-ranges=if-present // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/impl/fail_impl_bad_assoc_const.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/impl/fail_impl_bad_assoc_const.carbon interface I { let T:! type; } // CHECK:STDERR: fail_impl_bad_assoc_const.carbon:[[@LINE+7]]:1: error: associated constant T not given a value in impl of interface I [ImplAssociatedConstantNeedsValue] // CHECK:STDERR: impl () as I {} // CHECK:STDERR: ^~~~~~~~~~~~~~ // CHECK:STDERR: fail_impl_bad_assoc_const.carbon:[[@LINE-5]]:19: note: associated constant declared here [AssociatedConstantHere] // CHECK:STDERR: interface I { let T:! type; } // CHECK:STDERR: ^~~~~~~~ // CHECK:STDERR: impl () as I {} // CHECK:STDOUT: --- fail_impl_bad_assoc_const.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %I.type: type = facet_type <@I> [concrete] // CHECK:STDOUT: %Self: %I.type = symbolic_binding Self, 0 [symbolic] // CHECK:STDOUT: %I.assoc_type: type = assoc_entity_type @I [concrete] // CHECK:STDOUT: %assoc0: %I.assoc_type = assoc_entity element0, @I.WithSelf.%T [concrete] // CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] // CHECK:STDOUT: %empty_tuple: %empty_tuple.type = tuple_value () [concrete] // CHECK:STDOUT: %I.impl_witness: = impl_witness @empty_tuple.type.as.I.impl.%I.impl_witness_table [concrete] // CHECK:STDOUT: %I.facet: %I.type = facet_value %empty_tuple.type, (%I.impl_witness) [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .I = %I.decl // CHECK:STDOUT: } // CHECK:STDOUT: %I.decl: type = interface_decl @I [concrete = constants.%I.type] {} {} // CHECK:STDOUT: impl_decl @empty_tuple.type.as.I.impl [concrete] {} { // CHECK:STDOUT: %.loc24_7.1: %empty_tuple.type = tuple_literal () [concrete = constants.%empty_tuple] // CHECK:STDOUT: %.loc24_7.2: type = converted %.loc24_7.1, constants.%empty_tuple.type [concrete = constants.%empty_tuple.type] // CHECK:STDOUT: %I.ref: type = name_ref I, file.%I.decl [concrete = constants.%I.type] // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: interface @I { // CHECK:STDOUT: %Self: %I.type = symbolic_binding Self, 0 [symbolic = constants.%Self] // CHECK:STDOUT: %I.WithSelf.decl = interface_with_self_decl @I [concrete] // CHECK:STDOUT: // CHECK:STDOUT: !with Self: // CHECK:STDOUT: %T: type = assoc_const_decl @T [concrete] { // CHECK:STDOUT: %assoc0: %I.assoc_type = assoc_entity element0, @I.WithSelf.%T [concrete = constants.%assoc0] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = %Self // CHECK:STDOUT: .T = @T.%assoc0 // CHECK:STDOUT: witness = (@I.WithSelf.%T) // CHECK:STDOUT: // CHECK:STDOUT: !requires: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: impl @empty_tuple.type.as.I.impl: %.loc24_7.2 as %I.ref { // CHECK:STDOUT: %I.impl_witness_table = impl_witness_table (), @empty_tuple.type.as.I.impl [concrete] // CHECK:STDOUT: %I.impl_witness: = impl_witness %I.impl_witness_table [concrete = constants.%I.impl_witness] // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: witness = %I.impl_witness // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @I.WithSelf(constants.%Self) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @I.WithSelf(constants.%I.facet) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: } // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/impl/fail_impl_bad_assoc_fn.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/primitives.carbon // TODO: Add ranges and switch to "--dump-sem-ir-ranges=only". // EXTRA-ARGS: --dump-sem-ir-ranges=if-present // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/impl/fail_impl_bad_assoc_fn.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/impl/fail_impl_bad_assoc_fn.carbon interface I { fn F(); } class NoF { // CHECK:STDERR: fail_impl_bad_assoc_fn.carbon:[[@LINE+7]]:3: error: missing implementation of F in impl of interface I [ImplMissingFunction] // CHECK:STDERR: impl as I {} // CHECK:STDERR: ^~~~~~~~~~~ // CHECK:STDERR: fail_impl_bad_assoc_fn.carbon:[[@LINE-6]]:15: note: associated function F declared here [AssociatedFunctionHere] // CHECK:STDERR: interface I { fn F(); } // CHECK:STDERR: ^~~~~~~ // CHECK:STDERR: impl as I {} } class FNotFunction { impl as I { // CHECK:STDERR: fail_impl_bad_assoc_fn.carbon:[[@LINE+7]]:5: error: associated function F implemented by non-function [ImplFunctionWithNonFunction] // CHECK:STDERR: class F; // CHECK:STDERR: ^~~~~~~~ // CHECK:STDERR: fail_impl_bad_assoc_fn.carbon:[[@LINE-18]]:15: note: associated function F declared here [AssociatedFunctionHere] // CHECK:STDERR: interface I { fn F(); } // CHECK:STDERR: ^~~~~~~ // CHECK:STDERR: class F; } } fn PossiblyF(); // TODO: Should this be permitted? class FAlias { impl as I { // CHECK:STDERR: fail_impl_bad_assoc_fn.carbon:[[@LINE+7]]:11: error: associated function F implemented by non-function [ImplFunctionWithNonFunction] // CHECK:STDERR: alias F = PossiblyF; // CHECK:STDERR: ^ // CHECK:STDERR: fail_impl_bad_assoc_fn.carbon:[[@LINE-34]]:15: note: associated function F declared here [AssociatedFunctionHere] // CHECK:STDERR: interface I { fn F(); } // CHECK:STDERR: ^~~~~~~ // CHECK:STDERR: alias F = PossiblyF; } } class FExtraParam { impl as I { // CHECK:STDERR: fail_impl_bad_assoc_fn.carbon:[[@LINE+10]]:5: error: 0 arguments passed to function expecting 1 argument [CallArgCountMismatch] // CHECK:STDERR: fn F(b: bool); // CHECK:STDERR: ^~~~~~~~~~~~~~ // CHECK:STDERR: fail_impl_bad_assoc_fn.carbon:[[@LINE+7]]:5: note: calling function declared here [InCallToEntity] // CHECK:STDERR: fn F(b: bool); // CHECK:STDERR: ^~~~~~~~~~~~~~ // CHECK:STDERR: fail_impl_bad_assoc_fn.carbon:[[@LINE-50]]:15: note: while building thunk to match the signature of this function [ThunkSignature] // CHECK:STDERR: interface I { fn F(); } // CHECK:STDERR: ^~~~~~~ // CHECK:STDERR: fn F(b: bool); } } class FExtraImplicitParam { impl as I { // CHECK:STDERR: fail_impl_bad_assoc_fn.carbon:[[@LINE+10]]:5: error: missing object argument in method call [MissingObjectInMethodCall] // CHECK:STDERR: fn F[self: Self](); // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: fail_impl_bad_assoc_fn.carbon:[[@LINE+7]]:5: note: calling function declared here [InCallToFunction] // CHECK:STDERR: fn F[self: Self](); // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: fail_impl_bad_assoc_fn.carbon:[[@LINE-66]]:15: note: while building thunk to match the signature of this function [ThunkSignature] // CHECK:STDERR: interface I { fn F(); } // CHECK:STDERR: ^~~~~~~ // CHECK:STDERR: fn F[self: Self](); } } // TODO: Should this be permitted? class FExtraReturnType { impl as I { // CHECK:STDERR: fail_impl_bad_assoc_fn.carbon:[[@LINE+7]]:5: error: function redeclaration differs because return type is `bool` [FunctionRedeclReturnTypeDiffers] // CHECK:STDERR: fn F() -> bool; // CHECK:STDERR: ^~~~~~~~~~~~~~~ // CHECK:STDERR: fail_impl_bad_assoc_fn.carbon:[[@LINE-80]]:15: note: previously declared with no return type [FunctionRedeclReturnTypePreviousNoReturn] // CHECK:STDERR: interface I { fn F(); } // CHECK:STDERR: ^~~~~~~ // CHECK:STDERR: fn F() -> bool; } } interface J { fn F[self: bool](b: bool) -> bool; } class FMissingParam { impl as J { // CHECK:STDERR: fail_impl_bad_assoc_fn.carbon:[[@LINE+10]]:5: error: 1 argument passed to function expecting 0 arguments [CallArgCountMismatch] // CHECK:STDERR: fn F[self: bool]() -> bool; // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: fail_impl_bad_assoc_fn.carbon:[[@LINE+7]]:5: note: calling function declared here [InCallToEntity] // CHECK:STDERR: fn F[self: bool]() -> bool; // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: fail_impl_bad_assoc_fn.carbon:[[@LINE-10]]:15: note: while building thunk to match the signature of this function [ThunkSignature] // CHECK:STDERR: interface J { fn F[self: bool](b: bool) -> bool; } // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: fn F[self: bool]() -> bool; } } class FMissingImplicitParam { impl as J { // CHECK:STDERR: fail_impl_bad_assoc_fn.carbon:[[@LINE+7]]:5: error: member name of type `` in compound member access is not an instance member or an interface member [CompoundMemberAccessDoesNotUseBase] // CHECK:STDERR: fn F(b: bool) -> bool; // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: fail_impl_bad_assoc_fn.carbon:[[@LINE-23]]:15: note: while building thunk to match the signature of this function [ThunkSignature] // CHECK:STDERR: interface J { fn F[self: bool](b: bool) -> bool; } // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: fn F(b: bool) -> bool; } } class FMissingReturnType { impl as J { // CHECK:STDERR: fail_impl_bad_assoc_fn.carbon:[[@LINE+16]]:5: error: cannot implicitly convert expression of type `()` to `bool` [ConversionFailure] // CHECK:STDERR: fn F[self: bool](b: bool); // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: fail_impl_bad_assoc_fn.carbon:[[@LINE+13]]:5: note: type `()` does not implement interface `Core.ImplicitAs(bool)` [MissingImplInMemberAccessInContext] // CHECK:STDERR: fn F[self: bool](b: bool); // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: fail_impl_bad_assoc_fn.carbon:[[@LINE-39]]:15: note: while building thunk to match the signature of this function [ThunkSignature] // CHECK:STDERR: interface J { fn F[self: bool](b: bool) -> bool; } // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: // CHECK:STDERR: fail_impl_bad_assoc_fn.carbon:[[@LINE-43]]:32: error: cannot implicitly convert expression of type `bool` to `FDifferentParamType` [ConversionFailure] // CHECK:STDERR: interface J { fn F[self: bool](b: bool) -> bool; } // CHECK:STDERR: ^~~~~~~ // CHECK:STDERR: fail_impl_bad_assoc_fn.carbon:[[@LINE-46]]:32: note: type `bool` does not implement interface `Core.ImplicitAs(FDifferentParamType)` [MissingImplInMemberAccessInContext] // CHECK:STDERR: interface J { fn F[self: bool](b: bool) -> bool; } // CHECK:STDERR: ^~~~~~~ fn F[self: bool](b: bool); } } class FDifferentParamType { impl as J { // CHECK:STDERR: fail_impl_bad_assoc_fn.carbon:[[@LINE+13]]:22: note: initializing function parameter [InCallToFunctionParam] // CHECK:STDERR: fn F[self: bool](b: Self) -> bool; // CHECK:STDERR: ^~~~~~~ // CHECK:STDERR: fail_impl_bad_assoc_fn.carbon:[[@LINE-58]]:15: note: while building thunk to match the signature of this function [ThunkSignature] // CHECK:STDERR: interface J { fn F[self: bool](b: bool) -> bool; } // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: // CHECK:STDERR: fail_impl_bad_assoc_fn.carbon:[[@LINE-62]]:20: error: cannot implicitly convert expression of type `bool` to `FDifferentImplicitParamType` [ConversionFailure] // CHECK:STDERR: interface J { fn F[self: bool](b: bool) -> bool; } // CHECK:STDERR: ^~~~~~~~~~ // CHECK:STDERR: fail_impl_bad_assoc_fn.carbon:[[@LINE-65]]:20: note: type `bool` does not implement interface `Core.ImplicitAs(FDifferentImplicitParamType)` [MissingImplInMemberAccessInContext] // CHECK:STDERR: interface J { fn F[self: bool](b: bool) -> bool; } // CHECK:STDERR: ^~~~~~~~~~ fn F[self: bool](b: Self) -> bool; } } class FDifferentImplicitParamType { impl as J { // CHECK:STDERR: fail_impl_bad_assoc_fn.carbon:[[@LINE+7]]:10: note: initializing function parameter [InCallToFunctionParam] // CHECK:STDERR: fn F[self: Self](b: bool) -> bool; // CHECK:STDERR: ^~~~~~~~~~ // CHECK:STDERR: fail_impl_bad_assoc_fn.carbon:[[@LINE-77]]:15: note: while building thunk to match the signature of this function [ThunkSignature] // CHECK:STDERR: interface J { fn F[self: bool](b: bool) -> bool; } // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: fn F[self: Self](b: bool) -> bool; } } class FDifferentReturnType { impl as J { // CHECK:STDERR: fail_impl_bad_assoc_fn.carbon:[[@LINE+10]]:5: error: cannot implicitly convert expression of type `FDifferentReturnType` to `bool` [ConversionFailure] // CHECK:STDERR: fn F[self: bool](b: bool) -> Self; // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: fail_impl_bad_assoc_fn.carbon:[[@LINE+7]]:5: note: type `FDifferentReturnType` does not implement interface `Core.ImplicitAs(bool)` [MissingImplInMemberAccessInContext] // CHECK:STDERR: fn F[self: bool](b: bool) -> Self; // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: fail_impl_bad_assoc_fn.carbon:[[@LINE-93]]:15: note: while building thunk to match the signature of this function [ThunkSignature] // CHECK:STDERR: interface J { fn F[self: bool](b: bool) -> bool; } // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: fn F[self: bool](b: bool) -> Self; } } interface SelfNested { // CHECK:STDERR: fail_impl_bad_assoc_fn.carbon:[[@LINE+6]]:8: error: cannot implicitly convert expression of type `SelfNestedBadParam` to `i32` [ConversionFailure] // CHECK:STDERR: fn F(x: (Self*, {.x: Self, .y: i32})) -> array(Self, 4); // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: fail_impl_bad_assoc_fn.carbon:[[@LINE+3]]:8: note: type `SelfNestedBadParam` does not implement interface `Core.ImplicitAs(i32)` [MissingImplInMemberAccessInContext] // CHECK:STDERR: fn F(x: (Self*, {.x: Self, .y: i32})) -> array(Self, 4); // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ fn F(x: (Self*, {.x: Self, .y: i32})) -> array(Self, 4); } class SelfNestedBadParam { impl as SelfNested { // CHECK:STDERR: fail_impl_bad_assoc_fn.carbon:[[@LINE+7]]:10: note: initializing function parameter [InCallToFunctionParam] // CHECK:STDERR: fn F(x: (SelfNestedBadParam*, {.x: i32, .y: i32})) -> array(SelfNestedBadParam, 4); // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: fail_impl_bad_assoc_fn.carbon:[[@LINE-8]]:3: note: while building thunk to match the signature of this function [ThunkSignature] // CHECK:STDERR: fn F(x: (Self*, {.x: Self, .y: i32})) -> array(Self, 4); // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: fn F(x: (SelfNestedBadParam*, {.x: i32, .y: i32})) -> array(SelfNestedBadParam, 4); } } class SelfNestedBadReturnType { impl as SelfNested { // CHECK:STDERR: fail_impl_bad_assoc_fn.carbon:[[@LINE+10]]:5: error: cannot implicitly convert expression of type `array(SelfNestedBadParam, 4)` to `array(SelfNestedBadReturnType, 4)` [ConversionFailure] // CHECK:STDERR: fn F(x: (SelfNestedBadReturnType*, {.x: SelfNestedBadReturnType, .y: i32})) -> array(SelfNestedBadParam, 4); // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: fail_impl_bad_assoc_fn.carbon:[[@LINE+7]]:5: note: type `array(SelfNestedBadParam, 4)` does not implement interface `Core.ImplicitAs(array(SelfNestedBadReturnType, 4))` [MissingImplInMemberAccessInContext] // CHECK:STDERR: fn F(x: (SelfNestedBadReturnType*, {.x: SelfNestedBadReturnType, .y: i32})) -> array(SelfNestedBadParam, 4); // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: fail_impl_bad_assoc_fn.carbon:[[@LINE-24]]:3: note: while building thunk to match the signature of this function [ThunkSignature] // CHECK:STDERR: fn F(x: (Self*, {.x: Self, .y: i32})) -> array(Self, 4); // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: fn F(x: (SelfNestedBadReturnType*, {.x: SelfNestedBadReturnType, .y: i32})) -> array(SelfNestedBadParam, 4); } } // CHECK:STDOUT: --- fail_impl_bad_assoc_fn.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %I.type: type = facet_type <@I> [concrete] // CHECK:STDOUT: %Self.ab9: %I.type = symbolic_binding Self, 0 [symbolic] // CHECK:STDOUT: %I.WithSelf.F.type.08c: type = fn_type @I.WithSelf.F, @I.WithSelf(%Self.ab9) [symbolic] // CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] // CHECK:STDOUT: %I.WithSelf.F.705: %I.WithSelf.F.type.08c = struct_value () [symbolic] // CHECK:STDOUT: %I.assoc_type: type = assoc_entity_type @I [concrete] // CHECK:STDOUT: %assoc0.18e: %I.assoc_type = assoc_entity element0, @I.WithSelf.%I.WithSelf.F.decl [concrete] // CHECK:STDOUT: %NoF: type = class_type @NoF [concrete] // CHECK:STDOUT: %I.impl_witness.187: = impl_witness @NoF.as.I.impl.%I.impl_witness_table [concrete] // CHECK:STDOUT: %I.facet.fc8: %I.type = facet_value %NoF, (%I.impl_witness.187) [concrete] // CHECK:STDOUT: %I.WithSelf.F.type.cec: type = fn_type @I.WithSelf.F, @I.WithSelf(%I.facet.fc8) [concrete] // CHECK:STDOUT: %I.WithSelf.F.733: %I.WithSelf.F.type.cec = struct_value () [concrete] // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %complete_type.357: = complete_type_witness %empty_struct_type [concrete] // CHECK:STDOUT: %FNotFunction: type = class_type @FNotFunction [concrete] // CHECK:STDOUT: %I.impl_witness.cf4: = impl_witness @FNotFunction.as.I.impl.%I.impl_witness_table [concrete] // CHECK:STDOUT: %F: type = class_type @F [concrete] // CHECK:STDOUT: %I.facet.c1a: %I.type = facet_value %FNotFunction, (%I.impl_witness.cf4) [concrete] // CHECK:STDOUT: %I.WithSelf.F.type.1b8: type = fn_type @I.WithSelf.F, @I.WithSelf(%I.facet.c1a) [concrete] // CHECK:STDOUT: %I.WithSelf.F.7e3: %I.WithSelf.F.type.1b8 = struct_value () [concrete] // CHECK:STDOUT: %PossiblyF.type: type = fn_type @PossiblyF [concrete] // CHECK:STDOUT: %PossiblyF: %PossiblyF.type = struct_value () [concrete] // CHECK:STDOUT: %FAlias: type = class_type @FAlias [concrete] // CHECK:STDOUT: %I.impl_witness.109: = impl_witness @FAlias.as.I.impl.%I.impl_witness_table [concrete] // CHECK:STDOUT: %I.facet.574: %I.type = facet_value %FAlias, (%I.impl_witness.109) [concrete] // CHECK:STDOUT: %I.WithSelf.F.type.f26: type = fn_type @I.WithSelf.F, @I.WithSelf(%I.facet.574) [concrete] // CHECK:STDOUT: %I.WithSelf.F.5e6: %I.WithSelf.F.type.f26 = struct_value () [concrete] // CHECK:STDOUT: %FExtraParam: type = class_type @FExtraParam [concrete] // CHECK:STDOUT: %I.impl_witness.627: = impl_witness @FExtraParam.as.I.impl.%I.impl_witness_table [concrete] // CHECK:STDOUT: %Bool.type: type = fn_type @Bool [concrete] // CHECK:STDOUT: %Bool: %Bool.type = struct_value () [concrete] // CHECK:STDOUT: %pattern_type.831: type = pattern_type bool [concrete] // CHECK:STDOUT: %FExtraParam.as.I.impl.F.type.531438.1: type = fn_type @FExtraParam.as.I.impl.F.loc69_18.1 [concrete] // CHECK:STDOUT: %FExtraParam.as.I.impl.F.33e1fe.1: %FExtraParam.as.I.impl.F.type.531438.1 = struct_value () [concrete] // CHECK:STDOUT: %I.facet.b53: %I.type = facet_value %FExtraParam, (%I.impl_witness.627) [concrete] // CHECK:STDOUT: %I.WithSelf.F.type.99d: type = fn_type @I.WithSelf.F, @I.WithSelf(%I.facet.b53) [concrete] // CHECK:STDOUT: %I.WithSelf.F.eab: %I.WithSelf.F.type.99d = struct_value () [concrete] // CHECK:STDOUT: %FExtraParam.as.I.impl.F.type.531438.2: type = fn_type @FExtraParam.as.I.impl.F.loc69_18.2 [concrete] // CHECK:STDOUT: %FExtraParam.as.I.impl.F.33e1fe.2: %FExtraParam.as.I.impl.F.type.531438.2 = struct_value () [concrete] // CHECK:STDOUT: %FExtraImplicitParam: type = class_type @FExtraImplicitParam [concrete] // CHECK:STDOUT: %I.impl_witness.e9e: = impl_witness @FExtraImplicitParam.as.I.impl.%I.impl_witness_table [concrete] // CHECK:STDOUT: %pattern_type.235: type = pattern_type %FExtraImplicitParam [concrete] // CHECK:STDOUT: %FExtraImplicitParam.as.I.impl.F.type.9dd145.1: type = fn_type @FExtraImplicitParam.as.I.impl.F.loc85_23.1 [concrete] // CHECK:STDOUT: %FExtraImplicitParam.as.I.impl.F.99eae8.1: %FExtraImplicitParam.as.I.impl.F.type.9dd145.1 = struct_value () [concrete] // CHECK:STDOUT: %I.facet.5a5: %I.type = facet_value %FExtraImplicitParam, (%I.impl_witness.e9e) [concrete] // CHECK:STDOUT: %I.WithSelf.F.type.e46: type = fn_type @I.WithSelf.F, @I.WithSelf(%I.facet.5a5) [concrete] // CHECK:STDOUT: %I.WithSelf.F.5bc: %I.WithSelf.F.type.e46 = struct_value () [concrete] // CHECK:STDOUT: %FExtraImplicitParam.as.I.impl.F.type.9dd145.2: type = fn_type @FExtraImplicitParam.as.I.impl.F.loc85_23.2 [concrete] // CHECK:STDOUT: %FExtraImplicitParam.as.I.impl.F.99eae8.2: %FExtraImplicitParam.as.I.impl.F.type.9dd145.2 = struct_value () [concrete] // CHECK:STDOUT: %FExtraReturnType: type = class_type @FExtraReturnType [concrete] // CHECK:STDOUT: %I.impl_witness.247: = impl_witness @FExtraReturnType.as.I.impl.%I.impl_witness_table [concrete] // CHECK:STDOUT: %.f34: Core.Form = init_form bool [concrete] // CHECK:STDOUT: %FExtraReturnType.as.I.impl.F.type: type = fn_type @FExtraReturnType.as.I.impl.F [concrete] // CHECK:STDOUT: %FExtraReturnType.as.I.impl.F: %FExtraReturnType.as.I.impl.F.type = struct_value () [concrete] // CHECK:STDOUT: %I.facet.1ed: %I.type = facet_value %FExtraReturnType, (%I.impl_witness.247) [concrete] // CHECK:STDOUT: %I.WithSelf.F.type.cd5: type = fn_type @I.WithSelf.F, @I.WithSelf(%I.facet.1ed) [concrete] // CHECK:STDOUT: %I.WithSelf.F.aee: %I.WithSelf.F.type.cd5 = struct_value () [concrete] // CHECK:STDOUT: %J.type: type = facet_type <@J> [concrete] // CHECK:STDOUT: %Self.8a1: %J.type = symbolic_binding Self, 0 [symbolic] // CHECK:STDOUT: %J.WithSelf.F.type.adb: type = fn_type @J.WithSelf.F, @J.WithSelf(%Self.8a1) [symbolic] // CHECK:STDOUT: %J.WithSelf.F.569: %J.WithSelf.F.type.adb = struct_value () [symbolic] // CHECK:STDOUT: %J.assoc_type: type = assoc_entity_type @J [concrete] // CHECK:STDOUT: %assoc0.481: %J.assoc_type = assoc_entity element0, @J.WithSelf.%J.WithSelf.F.decl [concrete] // CHECK:STDOUT: %FMissingParam: type = class_type @FMissingParam [concrete] // CHECK:STDOUT: %J.impl_witness.5e2: = impl_witness @FMissingParam.as.J.impl.%J.impl_witness_table [concrete] // CHECK:STDOUT: %FMissingParam.as.J.impl.F.type.0f71d8.1: type = fn_type @FMissingParam.as.J.impl.F.loc117_31.1 [concrete] // CHECK:STDOUT: %FMissingParam.as.J.impl.F.7540f6.1: %FMissingParam.as.J.impl.F.type.0f71d8.1 = struct_value () [concrete] // CHECK:STDOUT: %J.facet.567: %J.type = facet_value %FMissingParam, (%J.impl_witness.5e2) [concrete] // CHECK:STDOUT: %J.WithSelf.F.type.54f: type = fn_type @J.WithSelf.F, @J.WithSelf(%J.facet.567) [concrete] // CHECK:STDOUT: %J.WithSelf.F.7e8: %J.WithSelf.F.type.54f = struct_value () [concrete] // CHECK:STDOUT: %FMissingParam.as.J.impl.F.type.0f71d8.2: type = fn_type @FMissingParam.as.J.impl.F.loc117_31.2 [concrete] // CHECK:STDOUT: %FMissingParam.as.J.impl.F.7540f6.2: %FMissingParam.as.J.impl.F.type.0f71d8.2 = struct_value () [concrete] // CHECK:STDOUT: %FMissingImplicitParam: type = class_type @FMissingImplicitParam [concrete] // CHECK:STDOUT: %J.impl_witness.1e4: = impl_witness @FMissingImplicitParam.as.J.impl.%J.impl_witness_table [concrete] // CHECK:STDOUT: %FMissingImplicitParam.as.J.impl.F.type.80a56b.1: type = fn_type @FMissingImplicitParam.as.J.impl.F.loc130_26.1 [concrete] // CHECK:STDOUT: %FMissingImplicitParam.as.J.impl.F.c6a0b7.1: %FMissingImplicitParam.as.J.impl.F.type.80a56b.1 = struct_value () [concrete] // CHECK:STDOUT: %J.facet.266: %J.type = facet_value %FMissingImplicitParam, (%J.impl_witness.1e4) [concrete] // CHECK:STDOUT: %J.WithSelf.F.type.56e: type = fn_type @J.WithSelf.F, @J.WithSelf(%J.facet.266) [concrete] // CHECK:STDOUT: %J.WithSelf.F.d17: %J.WithSelf.F.type.56e = struct_value () [concrete] // CHECK:STDOUT: %FMissingImplicitParam.as.J.impl.F.type.80a56b.2: type = fn_type @FMissingImplicitParam.as.J.impl.F.loc130_26.2 [concrete] // CHECK:STDOUT: %FMissingImplicitParam.as.J.impl.F.c6a0b7.2: %FMissingImplicitParam.as.J.impl.F.type.80a56b.2 = struct_value () [concrete] // CHECK:STDOUT: %FMissingReturnType: type = class_type @FMissingReturnType [concrete] // CHECK:STDOUT: %J.impl_witness.7c7: = impl_witness @FMissingReturnType.as.J.impl.%J.impl_witness_table [concrete] // CHECK:STDOUT: %FMissingReturnType.as.J.impl.F.type.ce0d21.1: type = fn_type @FMissingReturnType.as.J.impl.F.loc152_30.1 [concrete] // CHECK:STDOUT: %FMissingReturnType.as.J.impl.F.5b613a.1: %FMissingReturnType.as.J.impl.F.type.ce0d21.1 = struct_value () [concrete] // CHECK:STDOUT: %J.facet.a9e: %J.type = facet_value %FMissingReturnType, (%J.impl_witness.7c7) [concrete] // CHECK:STDOUT: %J.WithSelf.F.type.8be: type = fn_type @J.WithSelf.F, @J.WithSelf(%J.facet.a9e) [concrete] // CHECK:STDOUT: %J.WithSelf.F.2a5: %J.WithSelf.F.type.8be = struct_value () [concrete] // CHECK:STDOUT: %FMissingReturnType.as.J.impl.F.type.ce0d21.2: type = fn_type @FMissingReturnType.as.J.impl.F.loc152_30.2 [concrete] // CHECK:STDOUT: %FMissingReturnType.as.J.impl.F.5b613a.2: %FMissingReturnType.as.J.impl.F.type.ce0d21.2 = struct_value () [concrete] // CHECK:STDOUT: %ImplicitAs.type.cc7: type = generic_interface_type @ImplicitAs [concrete] // CHECK:STDOUT: %ImplicitAs.generic: %ImplicitAs.type.cc7 = struct_value () [concrete] // CHECK:STDOUT: %FDifferentParamType: type = class_type @FDifferentParamType [concrete] // CHECK:STDOUT: %J.impl_witness.cd7: = impl_witness @FDifferentParamType.as.J.impl.%J.impl_witness_table [concrete] // CHECK:STDOUT: %pattern_type.b79: type = pattern_type %FDifferentParamType [concrete] // CHECK:STDOUT: %FDifferentParamType.as.J.impl.F.type.10926d.1: type = fn_type @FDifferentParamType.as.J.impl.F.loc171_38.1 [concrete] // CHECK:STDOUT: %FDifferentParamType.as.J.impl.F.f1245a.1: %FDifferentParamType.as.J.impl.F.type.10926d.1 = struct_value () [concrete] // CHECK:STDOUT: %J.facet.bd2: %J.type = facet_value %FDifferentParamType, (%J.impl_witness.cd7) [concrete] // CHECK:STDOUT: %J.WithSelf.F.type.06a: type = fn_type @J.WithSelf.F, @J.WithSelf(%J.facet.bd2) [concrete] // CHECK:STDOUT: %J.WithSelf.F.cad: %J.WithSelf.F.type.06a = struct_value () [concrete] // CHECK:STDOUT: %FDifferentParamType.as.J.impl.F.type.10926d.2: type = fn_type @FDifferentParamType.as.J.impl.F.loc171_38.2 [concrete] // CHECK:STDOUT: %FDifferentParamType.as.J.impl.F.f1245a.2: %FDifferentParamType.as.J.impl.F.type.10926d.2 = struct_value () [concrete] // CHECK:STDOUT: %FDifferentImplicitParamType: type = class_type @FDifferentImplicitParamType [concrete] // CHECK:STDOUT: %J.impl_witness.4d2: = impl_witness @FDifferentImplicitParamType.as.J.impl.%J.impl_witness_table [concrete] // CHECK:STDOUT: %pattern_type.b8f: type = pattern_type %FDifferentImplicitParamType [concrete] // CHECK:STDOUT: %FDifferentImplicitParamType.as.J.impl.F.type.955391.1: type = fn_type @FDifferentImplicitParamType.as.J.impl.F.loc184_38.1 [concrete] // CHECK:STDOUT: %FDifferentImplicitParamType.as.J.impl.F.a9ece4.1: %FDifferentImplicitParamType.as.J.impl.F.type.955391.1 = struct_value () [concrete] // CHECK:STDOUT: %J.facet.030: %J.type = facet_value %FDifferentImplicitParamType, (%J.impl_witness.4d2) [concrete] // CHECK:STDOUT: %J.WithSelf.F.type.f29: type = fn_type @J.WithSelf.F, @J.WithSelf(%J.facet.030) [concrete] // CHECK:STDOUT: %J.WithSelf.F.5b7: %J.WithSelf.F.type.f29 = struct_value () [concrete] // CHECK:STDOUT: %FDifferentImplicitParamType.as.J.impl.F.type.955391.2: type = fn_type @FDifferentImplicitParamType.as.J.impl.F.loc184_38.2 [concrete] // CHECK:STDOUT: %FDifferentImplicitParamType.as.J.impl.F.a9ece4.2: %FDifferentImplicitParamType.as.J.impl.F.type.955391.2 = struct_value () [concrete] // CHECK:STDOUT: %FDifferentReturnType: type = class_type @FDifferentReturnType [concrete] // CHECK:STDOUT: %J.impl_witness.a49: = impl_witness @FDifferentReturnType.as.J.impl.%J.impl_witness_table [concrete] // CHECK:STDOUT: %.2a9: Core.Form = init_form %FDifferentReturnType [concrete] // CHECK:STDOUT: %pattern_type.5c8: type = pattern_type %FDifferentReturnType [concrete] // CHECK:STDOUT: %FDifferentReturnType.as.J.impl.F.type.7bcb67.1: type = fn_type @FDifferentReturnType.as.J.impl.F.loc200_38.1 [concrete] // CHECK:STDOUT: %FDifferentReturnType.as.J.impl.F.a709b1.1: %FDifferentReturnType.as.J.impl.F.type.7bcb67.1 = struct_value () [concrete] // CHECK:STDOUT: %J.facet.4a1: %J.type = facet_value %FDifferentReturnType, (%J.impl_witness.a49) [concrete] // CHECK:STDOUT: %J.WithSelf.F.type.8e3: type = fn_type @J.WithSelf.F, @J.WithSelf(%J.facet.4a1) [concrete] // CHECK:STDOUT: %J.WithSelf.F.182: %J.WithSelf.F.type.8e3 = struct_value () [concrete] // CHECK:STDOUT: %FDifferentReturnType.as.J.impl.F.type.7bcb67.2: type = fn_type @FDifferentReturnType.as.J.impl.F.loc200_38.2 [concrete] // CHECK:STDOUT: %FDifferentReturnType.as.J.impl.F.a709b1.2: %FDifferentReturnType.as.J.impl.F.type.7bcb67.2 = struct_value () [concrete] // CHECK:STDOUT: %SelfNested.type: type = facet_type <@SelfNested> [concrete] // CHECK:STDOUT: %Self.aed: %SelfNested.type = symbolic_binding Self, 0 [symbolic] // CHECK:STDOUT: %Self.binding.as_type.07b: type = symbolic_binding_type Self, 0, %Self.aed [symbolic] // CHECK:STDOUT: %ptr.d99: type = ptr_type %Self.binding.as_type.07b [symbolic] // CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [concrete] // CHECK:STDOUT: %Int.type: type = generic_class_type @Int [concrete] // CHECK:STDOUT: %Int.generic: %Int.type = struct_value () [concrete] // CHECK:STDOUT: %i32: type = class_type @Int, @Int(%int_32) [concrete] // CHECK:STDOUT: %struct_type.x.y.e05: type = struct_type {.x: %Self.binding.as_type.07b, .y: %i32} [symbolic] // CHECK:STDOUT: %tuple.type.24b: type = tuple_type (type, type) [concrete] // CHECK:STDOUT: %tuple.179: %tuple.type.24b = tuple_value (%ptr.d99, %struct_type.x.y.e05) [symbolic] // CHECK:STDOUT: %tuple.type.9cf: type = tuple_type (%ptr.d99, %struct_type.x.y.e05) [symbolic] // CHECK:STDOUT: %pattern_type.37a: type = pattern_type %tuple.type.9cf [symbolic] // CHECK:STDOUT: %int_4: Core.IntLiteral = int_value 4 [concrete] // CHECK:STDOUT: %array_type.eba: type = array_type %int_4, %Self.binding.as_type.07b [symbolic] // CHECK:STDOUT: %.efe: Core.Form = init_form %array_type.eba [symbolic] // CHECK:STDOUT: %pattern_type.dfe: type = pattern_type %array_type.eba [symbolic] // CHECK:STDOUT: %SelfNested.WithSelf.F.type.430: type = fn_type @SelfNested.WithSelf.F, @SelfNested.WithSelf(%Self.aed) [symbolic] // CHECK:STDOUT: %SelfNested.WithSelf.F.257: %SelfNested.WithSelf.F.type.430 = struct_value () [symbolic] // CHECK:STDOUT: %SelfNested.assoc_type: type = assoc_entity_type @SelfNested [concrete] // CHECK:STDOUT: %assoc0.da4: %SelfNested.assoc_type = assoc_entity element0, @SelfNested.WithSelf.%SelfNested.WithSelf.F.decl [concrete] // CHECK:STDOUT: %SelfNestedBadParam: type = class_type @SelfNestedBadParam [concrete] // CHECK:STDOUT: %SelfNested.impl_witness.7e8: = impl_witness @SelfNestedBadParam.as.SelfNested.impl.%SelfNested.impl_witness_table [concrete] // CHECK:STDOUT: %ptr.cf2: type = ptr_type %SelfNestedBadParam [concrete] // CHECK:STDOUT: %struct_type.x.y.871: type = struct_type {.x: %i32, .y: %i32} [concrete] // CHECK:STDOUT: %tuple.bb3: %tuple.type.24b = tuple_value (%ptr.cf2, %struct_type.x.y.871) [concrete] // CHECK:STDOUT: %tuple.type.70f: type = tuple_type (%ptr.cf2, %struct_type.x.y.871) [concrete] // CHECK:STDOUT: %pattern_type.395: type = pattern_type %tuple.type.70f [concrete] // CHECK:STDOUT: %array_type.388: type = array_type %int_4, %SelfNestedBadParam [concrete] // CHECK:STDOUT: %.0cb: Core.Form = init_form %array_type.388 [concrete] // CHECK:STDOUT: %pattern_type.ec1: type = pattern_type %array_type.388 [concrete] // CHECK:STDOUT: %SelfNestedBadParam.as.SelfNested.impl.F.type.789632.1: type = fn_type @SelfNestedBadParam.as.SelfNested.impl.F.loc223_87.1 [concrete] // CHECK:STDOUT: %SelfNestedBadParam.as.SelfNested.impl.F.6e1661.1: %SelfNestedBadParam.as.SelfNested.impl.F.type.789632.1 = struct_value () [concrete] // CHECK:STDOUT: %SelfNested.facet.b9f: %SelfNested.type = facet_value %SelfNestedBadParam, (%SelfNested.impl_witness.7e8) [concrete] // CHECK:STDOUT: %SelfNested.WithSelf.F.type.4de: type = fn_type @SelfNested.WithSelf.F, @SelfNested.WithSelf(%SelfNested.facet.b9f) [concrete] // CHECK:STDOUT: %SelfNested.WithSelf.F.5e9: %SelfNested.WithSelf.F.type.4de = struct_value () [concrete] // CHECK:STDOUT: %struct_type.x.y.62a: type = struct_type {.x: %SelfNestedBadParam, .y: %i32} [concrete] // CHECK:STDOUT: %tuple.73b: %tuple.type.24b = tuple_value (%ptr.cf2, %struct_type.x.y.62a) [concrete] // CHECK:STDOUT: %tuple.type.7ea: type = tuple_type (%ptr.cf2, %struct_type.x.y.62a) [concrete] // CHECK:STDOUT: %pattern_type.8d7: type = pattern_type %tuple.type.7ea [concrete] // CHECK:STDOUT: %SelfNestedBadParam.as.SelfNested.impl.F.type.789632.2: type = fn_type @SelfNestedBadParam.as.SelfNested.impl.F.loc223_87.2 [concrete] // CHECK:STDOUT: %SelfNestedBadParam.as.SelfNested.impl.F.6e1661.2: %SelfNestedBadParam.as.SelfNested.impl.F.type.789632.2 = struct_value () [concrete] // CHECK:STDOUT: %SelfNestedBadReturnType: type = class_type @SelfNestedBadReturnType [concrete] // CHECK:STDOUT: %SelfNested.impl_witness.d9c: = impl_witness @SelfNestedBadReturnType.as.SelfNested.impl.%SelfNested.impl_witness_table [concrete] // CHECK:STDOUT: %ptr.cbe: type = ptr_type %SelfNestedBadReturnType [concrete] // CHECK:STDOUT: %struct_type.x.y.cfa: type = struct_type {.x: %SelfNestedBadReturnType, .y: %i32} [concrete] // CHECK:STDOUT: %tuple.c42: %tuple.type.24b = tuple_value (%ptr.cbe, %struct_type.x.y.cfa) [concrete] // CHECK:STDOUT: %tuple.type.064: type = tuple_type (%ptr.cbe, %struct_type.x.y.cfa) [concrete] // CHECK:STDOUT: %pattern_type.1f1: type = pattern_type %tuple.type.064 [concrete] // CHECK:STDOUT: %SelfNestedBadReturnType.as.SelfNested.impl.F.type.75d128.1: type = fn_type @SelfNestedBadReturnType.as.SelfNested.impl.F.loc239_112.1 [concrete] // CHECK:STDOUT: %SelfNestedBadReturnType.as.SelfNested.impl.F.587f77.1: %SelfNestedBadReturnType.as.SelfNested.impl.F.type.75d128.1 = struct_value () [concrete] // CHECK:STDOUT: %SelfNested.facet.23f: %SelfNested.type = facet_value %SelfNestedBadReturnType, (%SelfNested.impl_witness.d9c) [concrete] // CHECK:STDOUT: %SelfNested.WithSelf.F.type.156: type = fn_type @SelfNested.WithSelf.F, @SelfNested.WithSelf(%SelfNested.facet.23f) [concrete] // CHECK:STDOUT: %SelfNested.WithSelf.F.edc: %SelfNested.WithSelf.F.type.156 = struct_value () [concrete] // CHECK:STDOUT: %array_type.31b: type = array_type %int_4, %SelfNestedBadReturnType [concrete] // CHECK:STDOUT: %.369: Core.Form = init_form %array_type.31b [concrete] // CHECK:STDOUT: %pattern_type.1fd: type = pattern_type %array_type.31b [concrete] // CHECK:STDOUT: %SelfNestedBadReturnType.as.SelfNested.impl.F.type.75d128.2: type = fn_type @SelfNestedBadReturnType.as.SelfNested.impl.F.loc239_112.2 [concrete] // CHECK:STDOUT: %SelfNestedBadReturnType.as.SelfNested.impl.F.587f77.2: %SelfNestedBadReturnType.as.SelfNested.impl.F.type.75d128.2 = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: .Bool = %Core.Bool // CHECK:STDOUT: .ImplicitAs = %Core.ImplicitAs // CHECK:STDOUT: .Int = %Core.Int // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: %Core.Bool: %Bool.type = import_ref Core//prelude/parts/bool, Bool, loaded [concrete = constants.%Bool] // CHECK:STDOUT: %Core.ImplicitAs: %ImplicitAs.type.cc7 = import_ref Core//prelude/parts/as, ImplicitAs, loaded [concrete = constants.%ImplicitAs.generic] // CHECK:STDOUT: %Core.Int: %Int.type = import_ref Core//prelude/parts/int, Int, loaded [concrete = constants.%Int.generic] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .I = %I.decl // CHECK:STDOUT: .NoF = %NoF.decl // CHECK:STDOUT: .FNotFunction = %FNotFunction.decl // CHECK:STDOUT: .PossiblyF = %PossiblyF.decl // CHECK:STDOUT: .FAlias = %FAlias.decl // CHECK:STDOUT: .FExtraParam = %FExtraParam.decl // CHECK:STDOUT: .FExtraImplicitParam = %FExtraImplicitParam.decl // CHECK:STDOUT: .FExtraReturnType = %FExtraReturnType.decl // CHECK:STDOUT: .J = %J.decl // CHECK:STDOUT: .FMissingParam = %FMissingParam.decl // CHECK:STDOUT: .FMissingImplicitParam = %FMissingImplicitParam.decl // CHECK:STDOUT: .FMissingReturnType = %FMissingReturnType.decl // CHECK:STDOUT: .FDifferentParamType = %FDifferentParamType.decl // CHECK:STDOUT: .FDifferentImplicitParamType = %FDifferentImplicitParamType.decl // CHECK:STDOUT: .FDifferentReturnType = %FDifferentReturnType.decl // CHECK:STDOUT: .SelfNested = %SelfNested.decl // CHECK:STDOUT: .SelfNestedBadParam = %SelfNestedBadParam.decl // CHECK:STDOUT: .SelfNestedBadReturnType = %SelfNestedBadReturnType.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %I.decl: type = interface_decl @I [concrete = constants.%I.type] {} {} // CHECK:STDOUT: %NoF.decl: type = class_decl @NoF [concrete = constants.%NoF] {} {} // CHECK:STDOUT: %FNotFunction.decl: type = class_decl @FNotFunction [concrete = constants.%FNotFunction] {} {} // CHECK:STDOUT: %PossiblyF.decl: %PossiblyF.type = fn_decl @PossiblyF [concrete = constants.%PossiblyF] {} {} // CHECK:STDOUT: %FAlias.decl: type = class_decl @FAlias [concrete = constants.%FAlias] {} {} // CHECK:STDOUT: %FExtraParam.decl: type = class_decl @FExtraParam [concrete = constants.%FExtraParam] {} {} // CHECK:STDOUT: %FExtraImplicitParam.decl: type = class_decl @FExtraImplicitParam [concrete = constants.%FExtraImplicitParam] {} {} // CHECK:STDOUT: %FExtraReturnType.decl: type = class_decl @FExtraReturnType [concrete = constants.%FExtraReturnType] {} {} // CHECK:STDOUT: %J.decl: type = interface_decl @J [concrete = constants.%J.type] {} {} // CHECK:STDOUT: %FMissingParam.decl: type = class_decl @FMissingParam [concrete = constants.%FMissingParam] {} {} // CHECK:STDOUT: %FMissingImplicitParam.decl: type = class_decl @FMissingImplicitParam [concrete = constants.%FMissingImplicitParam] {} {} // CHECK:STDOUT: %FMissingReturnType.decl: type = class_decl @FMissingReturnType [concrete = constants.%FMissingReturnType] {} {} // CHECK:STDOUT: %FDifferentParamType.decl: type = class_decl @FDifferentParamType [concrete = constants.%FDifferentParamType] {} {} // CHECK:STDOUT: %FDifferentImplicitParamType.decl: type = class_decl @FDifferentImplicitParamType [concrete = constants.%FDifferentImplicitParamType] {} {} // CHECK:STDOUT: %FDifferentReturnType.decl: type = class_decl @FDifferentReturnType [concrete = constants.%FDifferentReturnType] {} {} // CHECK:STDOUT: %SelfNested.decl: type = interface_decl @SelfNested [concrete = constants.%SelfNested.type] {} {} // CHECK:STDOUT: %SelfNestedBadParam.decl: type = class_decl @SelfNestedBadParam [concrete = constants.%SelfNestedBadParam] {} {} // CHECK:STDOUT: %SelfNestedBadReturnType.decl: type = class_decl @SelfNestedBadReturnType [concrete = constants.%SelfNestedBadReturnType] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: interface @I { // CHECK:STDOUT: %Self: %I.type = symbolic_binding Self, 0 [symbolic = constants.%Self.ab9] // CHECK:STDOUT: %I.WithSelf.decl = interface_with_self_decl @I [concrete] // CHECK:STDOUT: // CHECK:STDOUT: !with Self: // CHECK:STDOUT: %I.WithSelf.F.decl: @I.WithSelf.%I.WithSelf.F.type (%I.WithSelf.F.type.08c) = fn_decl @I.WithSelf.F [symbolic = @I.WithSelf.%I.WithSelf.F (constants.%I.WithSelf.F.705)] {} {} // CHECK:STDOUT: %assoc0: %I.assoc_type = assoc_entity element0, %I.WithSelf.F.decl [concrete = constants.%assoc0.18e] // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = %Self // CHECK:STDOUT: .F = @I.WithSelf.%assoc0 // CHECK:STDOUT: witness = (@I.WithSelf.%I.WithSelf.F.decl) // CHECK:STDOUT: // CHECK:STDOUT: !requires: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: interface @J { // CHECK:STDOUT: %Self: %J.type = symbolic_binding Self, 0 [symbolic = constants.%Self.8a1] // CHECK:STDOUT: %J.WithSelf.decl = interface_with_self_decl @J [concrete] // CHECK:STDOUT: // CHECK:STDOUT: !with Self: // CHECK:STDOUT: %J.WithSelf.F.decl: @J.WithSelf.%J.WithSelf.F.type (%J.WithSelf.F.type.adb) = fn_decl @J.WithSelf.F [symbolic = @J.WithSelf.%J.WithSelf.F (constants.%J.WithSelf.F.569)] { // CHECK:STDOUT: %self.patt: %pattern_type.831 = value_binding_pattern self [concrete] // CHECK:STDOUT: %self.param_patt: %pattern_type.831 = value_param_pattern %self.patt [concrete] // CHECK:STDOUT: %b.patt: %pattern_type.831 = value_binding_pattern b [concrete] // CHECK:STDOUT: %b.param_patt: %pattern_type.831 = value_param_pattern %b.patt [concrete] // CHECK:STDOUT: %return.patt: %pattern_type.831 = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: %pattern_type.831 = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %.loc103_44.1: type = type_literal bool [concrete = bool] // CHECK:STDOUT: %.loc103_44.2: Core.Form = init_form %.loc103_44.1 [concrete = constants.%.f34] // CHECK:STDOUT: %self.param: bool = value_param call_param0 // CHECK:STDOUT: %.loc103_26: type = type_literal bool [concrete = bool] // CHECK:STDOUT: %self: bool = value_binding self, %self.param // CHECK:STDOUT: %b.param: bool = value_param call_param1 // CHECK:STDOUT: %.loc103_35: type = type_literal bool [concrete = bool] // CHECK:STDOUT: %b: bool = value_binding b, %b.param // CHECK:STDOUT: %return.param: ref bool = out_param call_param2 // CHECK:STDOUT: %return: ref bool = return_slot %return.param // CHECK:STDOUT: } // CHECK:STDOUT: %assoc0: %J.assoc_type = assoc_entity element0, %J.WithSelf.F.decl [concrete = constants.%assoc0.481] // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = %Self // CHECK:STDOUT: .F = @J.WithSelf.%assoc0 // CHECK:STDOUT: witness = (@J.WithSelf.%J.WithSelf.F.decl) // CHECK:STDOUT: // CHECK:STDOUT: !requires: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: interface @SelfNested { // CHECK:STDOUT: %Self: %SelfNested.type = symbolic_binding Self, 0 [symbolic = constants.%Self.aed] // CHECK:STDOUT: %SelfNested.WithSelf.decl = interface_with_self_decl @SelfNested [concrete] // CHECK:STDOUT: // CHECK:STDOUT: !with Self: // CHECK:STDOUT: %SelfNested.WithSelf.F.decl: @SelfNested.WithSelf.%SelfNested.WithSelf.F.type (%SelfNested.WithSelf.F.type.430) = fn_decl @SelfNested.WithSelf.F [symbolic = @SelfNested.WithSelf.%SelfNested.WithSelf.F (constants.%SelfNested.WithSelf.F.257)] { // CHECK:STDOUT: %x.patt: @SelfNested.WithSelf.F.%pattern_type.loc211_8 (%pattern_type.37a) = value_binding_pattern x [concrete] // CHECK:STDOUT: %x.param_patt: @SelfNested.WithSelf.F.%pattern_type.loc211_8 (%pattern_type.37a) = value_param_pattern %x.patt [concrete] // CHECK:STDOUT: %return.patt: @SelfNested.WithSelf.F.%pattern_type.loc211_41 (%pattern_type.dfe) = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: @SelfNested.WithSelf.F.%pattern_type.loc211_41 (%pattern_type.dfe) = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %Self.ref.loc211_50: %SelfNested.type = name_ref Self, @SelfNested.%Self [symbolic = %Self (constants.%Self.aed)] // CHECK:STDOUT: %int_4: Core.IntLiteral = int_value 4 [concrete = constants.%int_4] // CHECK:STDOUT: %Self.as_type.loc211_50: type = facet_access_type %Self.ref.loc211_50 [symbolic = %Self.binding.as_type (constants.%Self.binding.as_type.07b)] // CHECK:STDOUT: %.loc211_50: type = converted %Self.ref.loc211_50, %Self.as_type.loc211_50 [symbolic = %Self.binding.as_type (constants.%Self.binding.as_type.07b)] // CHECK:STDOUT: %array_type.loc211_57.2: type = array_type %int_4, %.loc211_50 [symbolic = %array_type.loc211_57.1 (constants.%array_type.eba)] // CHECK:STDOUT: %.loc211_57.2: Core.Form = init_form %array_type.loc211_57.2 [symbolic = %.loc211_57.1 (constants.%.efe)] // CHECK:STDOUT: %x.param: @SelfNested.WithSelf.F.%tuple.type (%tuple.type.9cf) = value_param call_param0 // CHECK:STDOUT: %.loc211_38.1: type = splice_block %.loc211_38.3 [symbolic = %tuple.type (constants.%tuple.type.9cf)] { // CHECK:STDOUT: %Self.ref.loc211_12: %SelfNested.type = name_ref Self, @SelfNested.%Self [symbolic = %Self (constants.%Self.aed)] // CHECK:STDOUT: %Self.as_type.loc211_16: type = facet_access_type %Self.ref.loc211_12 [symbolic = %Self.binding.as_type (constants.%Self.binding.as_type.07b)] // CHECK:STDOUT: %.loc211_16: type = converted %Self.ref.loc211_12, %Self.as_type.loc211_16 [symbolic = %Self.binding.as_type (constants.%Self.binding.as_type.07b)] // CHECK:STDOUT: %ptr.loc211_16.2: type = ptr_type %.loc211_16 [symbolic = %ptr.loc211_16.1 (constants.%ptr.d99)] // CHECK:STDOUT: %Self.ref.loc211_24: %SelfNested.type = name_ref Self, @SelfNested.%Self [symbolic = %Self (constants.%Self.aed)] // CHECK:STDOUT: %Self.as_type.loc211_24: type = facet_access_type %Self.ref.loc211_24 [symbolic = %Self.binding.as_type (constants.%Self.binding.as_type.07b)] // CHECK:STDOUT: %.loc211_24: type = converted %Self.ref.loc211_24, %Self.as_type.loc211_24 [symbolic = %Self.binding.as_type (constants.%Self.binding.as_type.07b)] // CHECK:STDOUT: %i32: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %struct_type.x.y.loc211_37.2: type = struct_type {.x: @SelfNested.WithSelf.F.%Self.binding.as_type (%Self.binding.as_type.07b), .y: %i32} [symbolic = %struct_type.x.y.loc211_37.1 (constants.%struct_type.x.y.e05)] // CHECK:STDOUT: %.loc211_38.2: %tuple.type.24b = tuple_literal (%ptr.loc211_16.2, %struct_type.x.y.loc211_37.2) [symbolic = %tuple (constants.%tuple.179)] // CHECK:STDOUT: %.loc211_38.3: type = converted %.loc211_38.2, constants.%tuple.type.9cf [symbolic = %tuple.type (constants.%tuple.type.9cf)] // CHECK:STDOUT: } // CHECK:STDOUT: %x: @SelfNested.WithSelf.F.%tuple.type (%tuple.type.9cf) = value_binding x, %x.param // CHECK:STDOUT: %return.param: ref @SelfNested.WithSelf.F.%array_type.loc211_57.1 (%array_type.eba) = out_param call_param1 // CHECK:STDOUT: %return: ref @SelfNested.WithSelf.F.%array_type.loc211_57.1 (%array_type.eba) = return_slot %return.param // CHECK:STDOUT: } // CHECK:STDOUT: %assoc0: %SelfNested.assoc_type = assoc_entity element0, %SelfNested.WithSelf.F.decl [concrete = constants.%assoc0.da4] // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = %Self // CHECK:STDOUT: .F = @SelfNested.WithSelf.%assoc0 // CHECK:STDOUT: witness = (@SelfNested.WithSelf.%SelfNested.WithSelf.F.decl) // CHECK:STDOUT: // CHECK:STDOUT: !requires: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: impl @NoF.as.I.impl: %Self.ref as %I.ref { // CHECK:STDOUT: %I.impl_witness_table = impl_witness_table (), @NoF.as.I.impl [concrete] // CHECK:STDOUT: %I.impl_witness: = impl_witness %I.impl_witness_table [concrete = constants.%I.impl_witness.187] // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .F = // CHECK:STDOUT: witness = %I.impl_witness // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: impl @FNotFunction.as.I.impl: %Self.ref as %I.ref { // CHECK:STDOUT: %F.decl: type = class_decl @F [concrete = constants.%F] {} {} // CHECK:STDOUT: %I.impl_witness_table = impl_witness_table (), @FNotFunction.as.I.impl [concrete] // CHECK:STDOUT: %I.impl_witness: = impl_witness %I.impl_witness_table [concrete = constants.%I.impl_witness.cf4] // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .F = %F.decl // CHECK:STDOUT: witness = %I.impl_witness // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: impl @FAlias.as.I.impl: %Self.ref as %I.ref { // CHECK:STDOUT: %PossiblyF.ref: %PossiblyF.type = name_ref PossiblyF, file.%PossiblyF.decl [concrete = constants.%PossiblyF] // CHECK:STDOUT: %F: %PossiblyF.type = alias_binding F, file.%PossiblyF.decl [concrete = constants.%PossiblyF] // CHECK:STDOUT: %I.impl_witness_table = impl_witness_table (), @FAlias.as.I.impl [concrete] // CHECK:STDOUT: %I.impl_witness: = impl_witness %I.impl_witness_table [concrete = constants.%I.impl_witness.109] // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .PossiblyF = // CHECK:STDOUT: .F = %F // CHECK:STDOUT: witness = %I.impl_witness // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: impl @FExtraParam.as.I.impl: %Self.ref as %I.ref { // CHECK:STDOUT: %FExtraParam.as.I.impl.F.decl.loc69_18.1: %FExtraParam.as.I.impl.F.type.531438.1 = fn_decl @FExtraParam.as.I.impl.F.loc69_18.1 [concrete = constants.%FExtraParam.as.I.impl.F.33e1fe.1] { // CHECK:STDOUT: %b.patt: %pattern_type.831 = value_binding_pattern b [concrete] // CHECK:STDOUT: %b.param_patt: %pattern_type.831 = value_param_pattern %b.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %b.param: bool = value_param call_param0 // CHECK:STDOUT: %.loc69: type = type_literal bool [concrete = bool] // CHECK:STDOUT: %b: bool = value_binding b, %b.param // CHECK:STDOUT: } // CHECK:STDOUT: %FExtraParam.as.I.impl.F.decl.loc69_18.2: %FExtraParam.as.I.impl.F.type.531438.2 = fn_decl @FExtraParam.as.I.impl.F.loc69_18.2 [concrete = constants.%FExtraParam.as.I.impl.F.33e1fe.2] {} {} // CHECK:STDOUT: %I.impl_witness_table = impl_witness_table (%FExtraParam.as.I.impl.F.decl.loc69_18.2), @FExtraParam.as.I.impl [concrete] // CHECK:STDOUT: %I.impl_witness: = impl_witness %I.impl_witness_table [concrete = constants.%I.impl_witness.627] // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .F = %FExtraParam.as.I.impl.F.decl.loc69_18.1 // CHECK:STDOUT: witness = %I.impl_witness // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: impl @FExtraImplicitParam.as.I.impl: %Self.ref as %I.ref { // CHECK:STDOUT: %FExtraImplicitParam.as.I.impl.F.decl.loc85_23.1: %FExtraImplicitParam.as.I.impl.F.type.9dd145.1 = fn_decl @FExtraImplicitParam.as.I.impl.F.loc85_23.1 [concrete = constants.%FExtraImplicitParam.as.I.impl.F.99eae8.1] { // CHECK:STDOUT: %self.patt: %pattern_type.235 = value_binding_pattern self [concrete] // CHECK:STDOUT: %self.param_patt: %pattern_type.235 = value_param_pattern %self.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %self.param: %FExtraImplicitParam = value_param call_param0 // CHECK:STDOUT: %Self.ref: type = name_ref Self, constants.%FExtraImplicitParam [concrete = constants.%FExtraImplicitParam] // CHECK:STDOUT: %self: %FExtraImplicitParam = value_binding self, %self.param // CHECK:STDOUT: } // CHECK:STDOUT: %FExtraImplicitParam.as.I.impl.F.decl.loc85_23.2: %FExtraImplicitParam.as.I.impl.F.type.9dd145.2 = fn_decl @FExtraImplicitParam.as.I.impl.F.loc85_23.2 [concrete = constants.%FExtraImplicitParam.as.I.impl.F.99eae8.2] {} {} // CHECK:STDOUT: %I.impl_witness_table = impl_witness_table (%FExtraImplicitParam.as.I.impl.F.decl.loc85_23.2), @FExtraImplicitParam.as.I.impl [concrete] // CHECK:STDOUT: %I.impl_witness: = impl_witness %I.impl_witness_table [concrete = constants.%I.impl_witness.e9e] // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .F = %FExtraImplicitParam.as.I.impl.F.decl.loc85_23.1 // CHECK:STDOUT: witness = %I.impl_witness // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: impl @FExtraReturnType.as.I.impl: %Self.ref as %I.ref { // CHECK:STDOUT: %FExtraReturnType.as.I.impl.F.decl: %FExtraReturnType.as.I.impl.F.type = fn_decl @FExtraReturnType.as.I.impl.F [concrete = constants.%FExtraReturnType.as.I.impl.F] { // CHECK:STDOUT: %return.patt: %pattern_type.831 = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: %pattern_type.831 = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %.loc99_15.1: type = type_literal bool [concrete = bool] // CHECK:STDOUT: %.loc99_15.2: Core.Form = init_form %.loc99_15.1 [concrete = constants.%.f34] // CHECK:STDOUT: %return.param: ref bool = out_param call_param0 // CHECK:STDOUT: %return: ref bool = return_slot %return.param // CHECK:STDOUT: } // CHECK:STDOUT: %I.impl_witness_table = impl_witness_table (), @FExtraReturnType.as.I.impl [concrete] // CHECK:STDOUT: %I.impl_witness: = impl_witness %I.impl_witness_table [concrete = constants.%I.impl_witness.247] // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .F = %FExtraReturnType.as.I.impl.F.decl // CHECK:STDOUT: witness = %I.impl_witness // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: impl @FMissingParam.as.J.impl: %Self.ref as %J.ref { // CHECK:STDOUT: %FMissingParam.as.J.impl.F.decl.loc117_31.1: %FMissingParam.as.J.impl.F.type.0f71d8.1 = fn_decl @FMissingParam.as.J.impl.F.loc117_31.1 [concrete = constants.%FMissingParam.as.J.impl.F.7540f6.1] { // CHECK:STDOUT: %self.patt: %pattern_type.831 = value_binding_pattern self [concrete] // CHECK:STDOUT: %self.param_patt: %pattern_type.831 = value_param_pattern %self.patt [concrete] // CHECK:STDOUT: %return.patt: %pattern_type.831 = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: %pattern_type.831 = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %.loc117_27.1: type = type_literal bool [concrete = bool] // CHECK:STDOUT: %.loc117_27.2: Core.Form = init_form %.loc117_27.1 [concrete = constants.%.f34] // CHECK:STDOUT: %self.param: bool = value_param call_param0 // CHECK:STDOUT: %.loc117_16: type = type_literal bool [concrete = bool] // CHECK:STDOUT: %self: bool = value_binding self, %self.param // CHECK:STDOUT: %return.param: ref bool = out_param call_param1 // CHECK:STDOUT: %return: ref bool = return_slot %return.param // CHECK:STDOUT: } // CHECK:STDOUT: %.loc103_44.1: type = specific_constant @J.WithSelf.F.%.loc103_44.1, @J.WithSelf.F(constants.%J.facet.567) [concrete = bool] // CHECK:STDOUT: %.loc103_44.2: type = specific_constant @J.WithSelf.F.%.loc103_44.2, @J.WithSelf.F(constants.%J.facet.567) [concrete = constants.%.f34] // CHECK:STDOUT: %FMissingParam.as.J.impl.F.decl.loc117_31.2: %FMissingParam.as.J.impl.F.type.0f71d8.2 = fn_decl @FMissingParam.as.J.impl.F.loc117_31.2 [concrete = constants.%FMissingParam.as.J.impl.F.7540f6.2] { // CHECK:STDOUT: %self.patt: %pattern_type.831 = value_binding_pattern self [concrete] // CHECK:STDOUT: %self.param_patt: %pattern_type.831 = value_param_pattern %self.patt [concrete] // CHECK:STDOUT: %b.patt: %pattern_type.831 = value_binding_pattern b [concrete] // CHECK:STDOUT: %b.param_patt: %pattern_type.831 = value_param_pattern %b.patt [concrete] // CHECK:STDOUT: %return.patt: %pattern_type.831 = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: %pattern_type.831 = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %self.param: bool = value_param call_param0 // CHECK:STDOUT: %self: bool = value_binding self, %self.param // CHECK:STDOUT: %b.param: bool = value_param call_param1 // CHECK:STDOUT: %b: bool = value_binding b, %b.param // CHECK:STDOUT: %return.param: ref bool = out_param call_param2 // CHECK:STDOUT: %return: ref bool = return_slot %return.param // CHECK:STDOUT: } // CHECK:STDOUT: %J.impl_witness_table = impl_witness_table (%FMissingParam.as.J.impl.F.decl.loc117_31.2), @FMissingParam.as.J.impl [concrete] // CHECK:STDOUT: %J.impl_witness: = impl_witness %J.impl_witness_table [concrete = constants.%J.impl_witness.5e2] // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .F = %FMissingParam.as.J.impl.F.decl.loc117_31.1 // CHECK:STDOUT: witness = %J.impl_witness // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: impl @FMissingImplicitParam.as.J.impl: %Self.ref as %J.ref { // CHECK:STDOUT: %FMissingImplicitParam.as.J.impl.F.decl.loc130_26.1: %FMissingImplicitParam.as.J.impl.F.type.80a56b.1 = fn_decl @FMissingImplicitParam.as.J.impl.F.loc130_26.1 [concrete = constants.%FMissingImplicitParam.as.J.impl.F.c6a0b7.1] { // CHECK:STDOUT: %b.patt: %pattern_type.831 = value_binding_pattern b [concrete] // CHECK:STDOUT: %b.param_patt: %pattern_type.831 = value_param_pattern %b.patt [concrete] // CHECK:STDOUT: %return.patt: %pattern_type.831 = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: %pattern_type.831 = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %.loc130_22.1: type = type_literal bool [concrete = bool] // CHECK:STDOUT: %.loc130_22.2: Core.Form = init_form %.loc130_22.1 [concrete = constants.%.f34] // CHECK:STDOUT: %b.param: bool = value_param call_param0 // CHECK:STDOUT: %.loc130_13: type = type_literal bool [concrete = bool] // CHECK:STDOUT: %b: bool = value_binding b, %b.param // CHECK:STDOUT: %return.param: ref bool = out_param call_param1 // CHECK:STDOUT: %return: ref bool = return_slot %return.param // CHECK:STDOUT: } // CHECK:STDOUT: %.loc103_44.1: type = specific_constant @J.WithSelf.F.%.loc103_44.1, @J.WithSelf.F(constants.%J.facet.266) [concrete = bool] // CHECK:STDOUT: %.loc103_44.2: type = specific_constant @J.WithSelf.F.%.loc103_44.2, @J.WithSelf.F(constants.%J.facet.266) [concrete = constants.%.f34] // CHECK:STDOUT: %FMissingImplicitParam.as.J.impl.F.decl.loc130_26.2: %FMissingImplicitParam.as.J.impl.F.type.80a56b.2 = fn_decl @FMissingImplicitParam.as.J.impl.F.loc130_26.2 [concrete = constants.%FMissingImplicitParam.as.J.impl.F.c6a0b7.2] { // CHECK:STDOUT: %self.patt: %pattern_type.831 = value_binding_pattern self [concrete] // CHECK:STDOUT: %self.param_patt: %pattern_type.831 = value_param_pattern %self.patt [concrete] // CHECK:STDOUT: %b.patt: %pattern_type.831 = value_binding_pattern b [concrete] // CHECK:STDOUT: %b.param_patt: %pattern_type.831 = value_param_pattern %b.patt [concrete] // CHECK:STDOUT: %return.patt: %pattern_type.831 = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: %pattern_type.831 = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %self.param: bool = value_param call_param0 // CHECK:STDOUT: %self: bool = value_binding self, %self.param // CHECK:STDOUT: %b.param: bool = value_param call_param1 // CHECK:STDOUT: %b: bool = value_binding b, %b.param // CHECK:STDOUT: %return.param: ref bool = out_param call_param2 // CHECK:STDOUT: %return: ref bool = return_slot %return.param // CHECK:STDOUT: } // CHECK:STDOUT: %J.impl_witness_table = impl_witness_table (%FMissingImplicitParam.as.J.impl.F.decl.loc130_26.2), @FMissingImplicitParam.as.J.impl [concrete] // CHECK:STDOUT: %J.impl_witness: = impl_witness %J.impl_witness_table [concrete = constants.%J.impl_witness.1e4] // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .F = %FMissingImplicitParam.as.J.impl.F.decl.loc130_26.1 // CHECK:STDOUT: witness = %J.impl_witness // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: impl @FMissingReturnType.as.J.impl: %Self.ref as %J.ref { // CHECK:STDOUT: %FMissingReturnType.as.J.impl.F.decl.loc152_30.1: %FMissingReturnType.as.J.impl.F.type.ce0d21.1 = fn_decl @FMissingReturnType.as.J.impl.F.loc152_30.1 [concrete = constants.%FMissingReturnType.as.J.impl.F.5b613a.1] { // CHECK:STDOUT: %self.patt: %pattern_type.831 = value_binding_pattern self [concrete] // CHECK:STDOUT: %self.param_patt: %pattern_type.831 = value_param_pattern %self.patt [concrete] // CHECK:STDOUT: %b.patt: %pattern_type.831 = value_binding_pattern b [concrete] // CHECK:STDOUT: %b.param_patt: %pattern_type.831 = value_param_pattern %b.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %self.param: bool = value_param call_param0 // CHECK:STDOUT: %.loc152_16: type = type_literal bool [concrete = bool] // CHECK:STDOUT: %self: bool = value_binding self, %self.param // CHECK:STDOUT: %b.param: bool = value_param call_param1 // CHECK:STDOUT: %.loc152_25: type = type_literal bool [concrete = bool] // CHECK:STDOUT: %b: bool = value_binding b, %b.param // CHECK:STDOUT: } // CHECK:STDOUT: %.loc103_44.1: type = specific_constant @J.WithSelf.F.%.loc103_44.1, @J.WithSelf.F(constants.%J.facet.a9e) [concrete = bool] // CHECK:STDOUT: %.loc103_44.2: type = specific_constant @J.WithSelf.F.%.loc103_44.2, @J.WithSelf.F(constants.%J.facet.a9e) [concrete = constants.%.f34] // CHECK:STDOUT: %FMissingReturnType.as.J.impl.F.decl.loc152_30.2: %FMissingReturnType.as.J.impl.F.type.ce0d21.2 = fn_decl @FMissingReturnType.as.J.impl.F.loc152_30.2 [concrete = constants.%FMissingReturnType.as.J.impl.F.5b613a.2] { // CHECK:STDOUT: %self.patt: %pattern_type.831 = value_binding_pattern self [concrete] // CHECK:STDOUT: %self.param_patt: %pattern_type.831 = value_param_pattern %self.patt [concrete] // CHECK:STDOUT: %b.patt: %pattern_type.831 = value_binding_pattern b [concrete] // CHECK:STDOUT: %b.param_patt: %pattern_type.831 = value_param_pattern %b.patt [concrete] // CHECK:STDOUT: %return.patt: %pattern_type.831 = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: %pattern_type.831 = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %self.param: bool = value_param call_param0 // CHECK:STDOUT: %self: bool = value_binding self, %self.param // CHECK:STDOUT: %b.param: bool = value_param call_param1 // CHECK:STDOUT: %b: bool = value_binding b, %b.param // CHECK:STDOUT: %return.param: ref bool = out_param call_param2 // CHECK:STDOUT: %return: ref bool = return_slot %return.param // CHECK:STDOUT: } // CHECK:STDOUT: %J.impl_witness_table = impl_witness_table (%FMissingReturnType.as.J.impl.F.decl.loc152_30.2), @FMissingReturnType.as.J.impl [concrete] // CHECK:STDOUT: %J.impl_witness: = impl_witness %J.impl_witness_table [concrete = constants.%J.impl_witness.7c7] // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .F = %FMissingReturnType.as.J.impl.F.decl.loc152_30.1 // CHECK:STDOUT: witness = %J.impl_witness // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: impl @FDifferentParamType.as.J.impl: %Self.ref as %J.ref { // CHECK:STDOUT: %FDifferentParamType.as.J.impl.F.decl.loc171_38.1: %FDifferentParamType.as.J.impl.F.type.10926d.1 = fn_decl @FDifferentParamType.as.J.impl.F.loc171_38.1 [concrete = constants.%FDifferentParamType.as.J.impl.F.f1245a.1] { // CHECK:STDOUT: %self.patt: %pattern_type.831 = value_binding_pattern self [concrete] // CHECK:STDOUT: %self.param_patt: %pattern_type.831 = value_param_pattern %self.patt [concrete] // CHECK:STDOUT: %b.patt: %pattern_type.b79 = value_binding_pattern b [concrete] // CHECK:STDOUT: %b.param_patt: %pattern_type.b79 = value_param_pattern %b.patt [concrete] // CHECK:STDOUT: %return.patt: %pattern_type.831 = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: %pattern_type.831 = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %.loc171_34.1: type = type_literal bool [concrete = bool] // CHECK:STDOUT: %.loc171_34.2: Core.Form = init_form %.loc171_34.1 [concrete = constants.%.f34] // CHECK:STDOUT: %self.param: bool = value_param call_param0 // CHECK:STDOUT: %.loc171_16: type = type_literal bool [concrete = bool] // CHECK:STDOUT: %self: bool = value_binding self, %self.param // CHECK:STDOUT: %b.param: %FDifferentParamType = value_param call_param1 // CHECK:STDOUT: %Self.ref: type = name_ref Self, constants.%FDifferentParamType [concrete = constants.%FDifferentParamType] // CHECK:STDOUT: %b: %FDifferentParamType = value_binding b, %b.param // CHECK:STDOUT: %return.param: ref bool = out_param call_param2 // CHECK:STDOUT: %return: ref bool = return_slot %return.param // CHECK:STDOUT: } // CHECK:STDOUT: %.loc103_44.1: type = specific_constant @J.WithSelf.F.%.loc103_44.1, @J.WithSelf.F(constants.%J.facet.bd2) [concrete = bool] // CHECK:STDOUT: %.loc103_44.2: type = specific_constant @J.WithSelf.F.%.loc103_44.2, @J.WithSelf.F(constants.%J.facet.bd2) [concrete = constants.%.f34] // CHECK:STDOUT: %FDifferentParamType.as.J.impl.F.decl.loc171_38.2: %FDifferentParamType.as.J.impl.F.type.10926d.2 = fn_decl @FDifferentParamType.as.J.impl.F.loc171_38.2 [concrete = constants.%FDifferentParamType.as.J.impl.F.f1245a.2] { // CHECK:STDOUT: %self.patt: %pattern_type.831 = value_binding_pattern self [concrete] // CHECK:STDOUT: %self.param_patt: %pattern_type.831 = value_param_pattern %self.patt [concrete] // CHECK:STDOUT: %b.patt: %pattern_type.831 = value_binding_pattern b [concrete] // CHECK:STDOUT: %b.param_patt: %pattern_type.831 = value_param_pattern %b.patt [concrete] // CHECK:STDOUT: %return.patt: %pattern_type.831 = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: %pattern_type.831 = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %self.param: bool = value_param call_param0 // CHECK:STDOUT: %self: bool = value_binding self, %self.param // CHECK:STDOUT: %b.param: bool = value_param call_param1 // CHECK:STDOUT: %b: bool = value_binding b, %b.param // CHECK:STDOUT: %return.param: ref bool = out_param call_param2 // CHECK:STDOUT: %return: ref bool = return_slot %return.param // CHECK:STDOUT: } // CHECK:STDOUT: %J.impl_witness_table = impl_witness_table (%FDifferentParamType.as.J.impl.F.decl.loc171_38.2), @FDifferentParamType.as.J.impl [concrete] // CHECK:STDOUT: %J.impl_witness: = impl_witness %J.impl_witness_table [concrete = constants.%J.impl_witness.cd7] // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .F = %FDifferentParamType.as.J.impl.F.decl.loc171_38.1 // CHECK:STDOUT: witness = %J.impl_witness // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: impl @FDifferentImplicitParamType.as.J.impl: %Self.ref as %J.ref { // CHECK:STDOUT: %FDifferentImplicitParamType.as.J.impl.F.decl.loc184_38.1: %FDifferentImplicitParamType.as.J.impl.F.type.955391.1 = fn_decl @FDifferentImplicitParamType.as.J.impl.F.loc184_38.1 [concrete = constants.%FDifferentImplicitParamType.as.J.impl.F.a9ece4.1] { // CHECK:STDOUT: %self.patt: %pattern_type.b8f = value_binding_pattern self [concrete] // CHECK:STDOUT: %self.param_patt: %pattern_type.b8f = value_param_pattern %self.patt [concrete] // CHECK:STDOUT: %b.patt: %pattern_type.831 = value_binding_pattern b [concrete] // CHECK:STDOUT: %b.param_patt: %pattern_type.831 = value_param_pattern %b.patt [concrete] // CHECK:STDOUT: %return.patt: %pattern_type.831 = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: %pattern_type.831 = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %.loc184_34.1: type = type_literal bool [concrete = bool] // CHECK:STDOUT: %.loc184_34.2: Core.Form = init_form %.loc184_34.1 [concrete = constants.%.f34] // CHECK:STDOUT: %self.param: %FDifferentImplicitParamType = value_param call_param0 // CHECK:STDOUT: %Self.ref: type = name_ref Self, constants.%FDifferentImplicitParamType [concrete = constants.%FDifferentImplicitParamType] // CHECK:STDOUT: %self: %FDifferentImplicitParamType = value_binding self, %self.param // CHECK:STDOUT: %b.param: bool = value_param call_param1 // CHECK:STDOUT: %.loc184_25: type = type_literal bool [concrete = bool] // CHECK:STDOUT: %b: bool = value_binding b, %b.param // CHECK:STDOUT: %return.param: ref bool = out_param call_param2 // CHECK:STDOUT: %return: ref bool = return_slot %return.param // CHECK:STDOUT: } // CHECK:STDOUT: %.loc103_44.1: type = specific_constant @J.WithSelf.F.%.loc103_44.1, @J.WithSelf.F(constants.%J.facet.030) [concrete = bool] // CHECK:STDOUT: %.loc103_44.2: type = specific_constant @J.WithSelf.F.%.loc103_44.2, @J.WithSelf.F(constants.%J.facet.030) [concrete = constants.%.f34] // CHECK:STDOUT: %FDifferentImplicitParamType.as.J.impl.F.decl.loc184_38.2: %FDifferentImplicitParamType.as.J.impl.F.type.955391.2 = fn_decl @FDifferentImplicitParamType.as.J.impl.F.loc184_38.2 [concrete = constants.%FDifferentImplicitParamType.as.J.impl.F.a9ece4.2] { // CHECK:STDOUT: %self.patt: %pattern_type.831 = value_binding_pattern self [concrete] // CHECK:STDOUT: %self.param_patt: %pattern_type.831 = value_param_pattern %self.patt [concrete] // CHECK:STDOUT: %b.patt: %pattern_type.831 = value_binding_pattern b [concrete] // CHECK:STDOUT: %b.param_patt: %pattern_type.831 = value_param_pattern %b.patt [concrete] // CHECK:STDOUT: %return.patt: %pattern_type.831 = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: %pattern_type.831 = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %self.param: bool = value_param call_param0 // CHECK:STDOUT: %self: bool = value_binding self, %self.param // CHECK:STDOUT: %b.param: bool = value_param call_param1 // CHECK:STDOUT: %b: bool = value_binding b, %b.param // CHECK:STDOUT: %return.param: ref bool = out_param call_param2 // CHECK:STDOUT: %return: ref bool = return_slot %return.param // CHECK:STDOUT: } // CHECK:STDOUT: %J.impl_witness_table = impl_witness_table (%FDifferentImplicitParamType.as.J.impl.F.decl.loc184_38.2), @FDifferentImplicitParamType.as.J.impl [concrete] // CHECK:STDOUT: %J.impl_witness: = impl_witness %J.impl_witness_table [concrete = constants.%J.impl_witness.4d2] // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .F = %FDifferentImplicitParamType.as.J.impl.F.decl.loc184_38.1 // CHECK:STDOUT: witness = %J.impl_witness // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: impl @FDifferentReturnType.as.J.impl: %Self.ref as %J.ref { // CHECK:STDOUT: %FDifferentReturnType.as.J.impl.F.decl.loc200_38.1: %FDifferentReturnType.as.J.impl.F.type.7bcb67.1 = fn_decl @FDifferentReturnType.as.J.impl.F.loc200_38.1 [concrete = constants.%FDifferentReturnType.as.J.impl.F.a709b1.1] { // CHECK:STDOUT: %self.patt: %pattern_type.831 = value_binding_pattern self [concrete] // CHECK:STDOUT: %self.param_patt: %pattern_type.831 = value_param_pattern %self.patt [concrete] // CHECK:STDOUT: %b.patt: %pattern_type.831 = value_binding_pattern b [concrete] // CHECK:STDOUT: %b.param_patt: %pattern_type.831 = value_param_pattern %b.patt [concrete] // CHECK:STDOUT: %return.patt: %pattern_type.5c8 = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: %pattern_type.5c8 = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %Self.ref: type = name_ref Self, constants.%FDifferentReturnType [concrete = constants.%FDifferentReturnType] // CHECK:STDOUT: %.loc200_34: Core.Form = init_form %Self.ref [concrete = constants.%.2a9] // CHECK:STDOUT: %self.param: bool = value_param call_param0 // CHECK:STDOUT: %.loc200_16: type = type_literal bool [concrete = bool] // CHECK:STDOUT: %self: bool = value_binding self, %self.param // CHECK:STDOUT: %b.param: bool = value_param call_param1 // CHECK:STDOUT: %.loc200_25: type = type_literal bool [concrete = bool] // CHECK:STDOUT: %b: bool = value_binding b, %b.param // CHECK:STDOUT: %return.param: ref %FDifferentReturnType = out_param call_param2 // CHECK:STDOUT: %return: ref %FDifferentReturnType = return_slot %return.param // CHECK:STDOUT: } // CHECK:STDOUT: %.loc103_44.1: type = specific_constant @J.WithSelf.F.%.loc103_44.1, @J.WithSelf.F(constants.%J.facet.4a1) [concrete = bool] // CHECK:STDOUT: %.loc103_44.2: type = specific_constant @J.WithSelf.F.%.loc103_44.2, @J.WithSelf.F(constants.%J.facet.4a1) [concrete = constants.%.f34] // CHECK:STDOUT: %FDifferentReturnType.as.J.impl.F.decl.loc200_38.2: %FDifferentReturnType.as.J.impl.F.type.7bcb67.2 = fn_decl @FDifferentReturnType.as.J.impl.F.loc200_38.2 [concrete = constants.%FDifferentReturnType.as.J.impl.F.a709b1.2] { // CHECK:STDOUT: %self.patt: %pattern_type.831 = value_binding_pattern self [concrete] // CHECK:STDOUT: %self.param_patt: %pattern_type.831 = value_param_pattern %self.patt [concrete] // CHECK:STDOUT: %b.patt: %pattern_type.831 = value_binding_pattern b [concrete] // CHECK:STDOUT: %b.param_patt: %pattern_type.831 = value_param_pattern %b.patt [concrete] // CHECK:STDOUT: %return.patt: %pattern_type.831 = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: %pattern_type.831 = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %self.param: bool = value_param call_param0 // CHECK:STDOUT: %self: bool = value_binding self, %self.param // CHECK:STDOUT: %b.param: bool = value_param call_param1 // CHECK:STDOUT: %b: bool = value_binding b, %b.param // CHECK:STDOUT: %return.param: ref bool = out_param call_param2 // CHECK:STDOUT: %return: ref bool = return_slot %return.param // CHECK:STDOUT: } // CHECK:STDOUT: %J.impl_witness_table = impl_witness_table (%FDifferentReturnType.as.J.impl.F.decl.loc200_38.2), @FDifferentReturnType.as.J.impl [concrete] // CHECK:STDOUT: %J.impl_witness: = impl_witness %J.impl_witness_table [concrete = constants.%J.impl_witness.a49] // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .F = %FDifferentReturnType.as.J.impl.F.decl.loc200_38.1 // CHECK:STDOUT: witness = %J.impl_witness // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: impl @SelfNestedBadParam.as.SelfNested.impl: %Self.ref as %SelfNested.ref { // CHECK:STDOUT: %SelfNestedBadParam.as.SelfNested.impl.F.decl.loc223_87.1: %SelfNestedBadParam.as.SelfNested.impl.F.type.789632.1 = fn_decl @SelfNestedBadParam.as.SelfNested.impl.F.loc223_87.1 [concrete = constants.%SelfNestedBadParam.as.SelfNested.impl.F.6e1661.1] { // CHECK:STDOUT: %x.patt: %pattern_type.395 = value_binding_pattern x [concrete] // CHECK:STDOUT: %x.param_patt: %pattern_type.395 = value_param_pattern %x.patt [concrete] // CHECK:STDOUT: %return.patt: %pattern_type.ec1 = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: %pattern_type.ec1 = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %SelfNestedBadParam.ref.loc223_65: type = name_ref SelfNestedBadParam, file.%SelfNestedBadParam.decl [concrete = constants.%SelfNestedBadParam] // CHECK:STDOUT: %int_4: Core.IntLiteral = int_value 4 [concrete = constants.%int_4] // CHECK:STDOUT: %array_type: type = array_type %int_4, %SelfNestedBadParam.ref.loc223_65 [concrete = constants.%array_type.388] // CHECK:STDOUT: %.loc223_86: Core.Form = init_form %array_type [concrete = constants.%.0cb] // CHECK:STDOUT: %x.param: %tuple.type.70f = value_param call_param0 // CHECK:STDOUT: %.loc223_53.1: type = splice_block %.loc223_53.3 [concrete = constants.%tuple.type.70f] { // CHECK:STDOUT: %SelfNestedBadParam.ref.loc223_14: type = name_ref SelfNestedBadParam, file.%SelfNestedBadParam.decl [concrete = constants.%SelfNestedBadParam] // CHECK:STDOUT: %ptr: type = ptr_type %SelfNestedBadParam.ref.loc223_14 [concrete = constants.%ptr.cf2] // CHECK:STDOUT: %i32.loc223_40: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %i32.loc223_49: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %struct_type.x.y: type = struct_type {.x: %i32, .y: %i32} [concrete = constants.%struct_type.x.y.871] // CHECK:STDOUT: %.loc223_53.2: %tuple.type.24b = tuple_literal (%ptr, %struct_type.x.y) [concrete = constants.%tuple.bb3] // CHECK:STDOUT: %.loc223_53.3: type = converted %.loc223_53.2, constants.%tuple.type.70f [concrete = constants.%tuple.type.70f] // CHECK:STDOUT: } // CHECK:STDOUT: %x: %tuple.type.70f = value_binding x, %x.param // CHECK:STDOUT: %return.param: ref %array_type.388 = out_param call_param1 // CHECK:STDOUT: %return: ref %array_type.388 = return_slot %return.param // CHECK:STDOUT: } // CHECK:STDOUT: %.loc211_57.1: type = specific_constant @SelfNested.WithSelf.F.%array_type.loc211_57.2, @SelfNested.WithSelf.F(constants.%SelfNested.facet.b9f) [concrete = constants.%array_type.388] // CHECK:STDOUT: %.loc211_57.2: type = specific_constant @SelfNested.WithSelf.F.%.loc211_57.2, @SelfNested.WithSelf.F(constants.%SelfNested.facet.b9f) [concrete = constants.%.0cb] // CHECK:STDOUT: %SelfNestedBadParam.as.SelfNested.impl.F.decl.loc223_87.2: %SelfNestedBadParam.as.SelfNested.impl.F.type.789632.2 = fn_decl @SelfNestedBadParam.as.SelfNested.impl.F.loc223_87.2 [concrete = constants.%SelfNestedBadParam.as.SelfNested.impl.F.6e1661.2] { // CHECK:STDOUT: %x.patt: %pattern_type.8d7 = value_binding_pattern x [concrete] // CHECK:STDOUT: %x.param_patt: %pattern_type.8d7 = value_param_pattern %x.patt [concrete] // CHECK:STDOUT: %return.patt: %pattern_type.ec1 = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: %pattern_type.ec1 = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %x.param: %tuple.type.7ea = value_param call_param0 // CHECK:STDOUT: %x: %tuple.type.7ea = value_binding x, %x.param // CHECK:STDOUT: %return.param: ref %array_type.388 = out_param call_param1 // CHECK:STDOUT: %return: ref %array_type.388 = return_slot %return.param // CHECK:STDOUT: } // CHECK:STDOUT: %SelfNested.impl_witness_table = impl_witness_table (%SelfNestedBadParam.as.SelfNested.impl.F.decl.loc223_87.2), @SelfNestedBadParam.as.SelfNested.impl [concrete] // CHECK:STDOUT: %SelfNested.impl_witness: = impl_witness %SelfNested.impl_witness_table [concrete = constants.%SelfNested.impl_witness.7e8] // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .SelfNestedBadParam = // CHECK:STDOUT: .F = %SelfNestedBadParam.as.SelfNested.impl.F.decl.loc223_87.1 // CHECK:STDOUT: witness = %SelfNested.impl_witness // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: impl @SelfNestedBadReturnType.as.SelfNested.impl: %Self.ref as %SelfNested.ref { // CHECK:STDOUT: %SelfNestedBadReturnType.as.SelfNested.impl.F.decl.loc239_112.1: %SelfNestedBadReturnType.as.SelfNested.impl.F.type.75d128.1 = fn_decl @SelfNestedBadReturnType.as.SelfNested.impl.F.loc239_112.1 [concrete = constants.%SelfNestedBadReturnType.as.SelfNested.impl.F.587f77.1] { // CHECK:STDOUT: %x.patt: %pattern_type.1f1 = value_binding_pattern x [concrete] // CHECK:STDOUT: %x.param_patt: %pattern_type.1f1 = value_param_pattern %x.patt [concrete] // CHECK:STDOUT: %return.patt: %pattern_type.ec1 = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: %pattern_type.ec1 = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %SelfNestedBadParam.ref: type = name_ref SelfNestedBadParam, file.%SelfNestedBadParam.decl [concrete = constants.%SelfNestedBadParam] // CHECK:STDOUT: %int_4: Core.IntLiteral = int_value 4 [concrete = constants.%int_4] // CHECK:STDOUT: %array_type: type = array_type %int_4, %SelfNestedBadParam.ref [concrete = constants.%array_type.388] // CHECK:STDOUT: %.loc239_111: Core.Form = init_form %array_type [concrete = constants.%.0cb] // CHECK:STDOUT: %x.param: %tuple.type.064 = value_param call_param0 // CHECK:STDOUT: %.loc239_78.1: type = splice_block %.loc239_78.3 [concrete = constants.%tuple.type.064] { // CHECK:STDOUT: %SelfNestedBadReturnType.ref.loc239_14: type = name_ref SelfNestedBadReturnType, file.%SelfNestedBadReturnType.decl [concrete = constants.%SelfNestedBadReturnType] // CHECK:STDOUT: %ptr: type = ptr_type %SelfNestedBadReturnType.ref.loc239_14 [concrete = constants.%ptr.cbe] // CHECK:STDOUT: %SelfNestedBadReturnType.ref.loc239_45: type = name_ref SelfNestedBadReturnType, file.%SelfNestedBadReturnType.decl [concrete = constants.%SelfNestedBadReturnType] // CHECK:STDOUT: %i32: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %struct_type.x.y: type = struct_type {.x: %SelfNestedBadReturnType, .y: %i32} [concrete = constants.%struct_type.x.y.cfa] // CHECK:STDOUT: %.loc239_78.2: %tuple.type.24b = tuple_literal (%ptr, %struct_type.x.y) [concrete = constants.%tuple.c42] // CHECK:STDOUT: %.loc239_78.3: type = converted %.loc239_78.2, constants.%tuple.type.064 [concrete = constants.%tuple.type.064] // CHECK:STDOUT: } // CHECK:STDOUT: %x: %tuple.type.064 = value_binding x, %x.param // CHECK:STDOUT: %return.param: ref %array_type.388 = out_param call_param1 // CHECK:STDOUT: %return: ref %array_type.388 = return_slot %return.param // CHECK:STDOUT: } // CHECK:STDOUT: %.loc211_57.1: type = specific_constant @SelfNested.WithSelf.F.%array_type.loc211_57.2, @SelfNested.WithSelf.F(constants.%SelfNested.facet.23f) [concrete = constants.%array_type.31b] // CHECK:STDOUT: %.loc211_57.2: type = specific_constant @SelfNested.WithSelf.F.%.loc211_57.2, @SelfNested.WithSelf.F(constants.%SelfNested.facet.23f) [concrete = constants.%.369] // CHECK:STDOUT: %SelfNestedBadReturnType.as.SelfNested.impl.F.decl.loc239_112.2: %SelfNestedBadReturnType.as.SelfNested.impl.F.type.75d128.2 = fn_decl @SelfNestedBadReturnType.as.SelfNested.impl.F.loc239_112.2 [concrete = constants.%SelfNestedBadReturnType.as.SelfNested.impl.F.587f77.2] { // CHECK:STDOUT: %x.patt: %pattern_type.1f1 = value_binding_pattern x [concrete] // CHECK:STDOUT: %x.param_patt: %pattern_type.1f1 = value_param_pattern %x.patt [concrete] // CHECK:STDOUT: %return.patt: %pattern_type.1fd = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: %pattern_type.1fd = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %x.param: %tuple.type.064 = value_param call_param0 // CHECK:STDOUT: %x: %tuple.type.064 = value_binding x, %x.param // CHECK:STDOUT: %return.param: ref %array_type.31b = out_param call_param1 // CHECK:STDOUT: %return: ref %array_type.31b = return_slot %return.param // CHECK:STDOUT: } // CHECK:STDOUT: %SelfNested.impl_witness_table = impl_witness_table (%SelfNestedBadReturnType.as.SelfNested.impl.F.decl.loc239_112.2), @SelfNestedBadReturnType.as.SelfNested.impl [concrete] // CHECK:STDOUT: %SelfNested.impl_witness: = impl_witness %SelfNested.impl_witness_table [concrete = constants.%SelfNested.impl_witness.d9c] // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .SelfNestedBadReturnType = // CHECK:STDOUT: .SelfNestedBadParam = // CHECK:STDOUT: .F = %SelfNestedBadReturnType.as.SelfNested.impl.F.decl.loc239_112.1 // CHECK:STDOUT: witness = %SelfNested.impl_witness // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @NoF { // CHECK:STDOUT: impl_decl @NoF.as.I.impl [concrete] {} { // CHECK:STDOUT: %Self.ref: type = name_ref Self, constants.%NoF [concrete = constants.%NoF] // CHECK:STDOUT: %I.ref: type = name_ref I, file.%I.decl [concrete = constants.%I.type] // CHECK:STDOUT: } // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type.357] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%NoF // CHECK:STDOUT: .I = // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @FNotFunction { // CHECK:STDOUT: impl_decl @FNotFunction.as.I.impl [concrete] {} { // CHECK:STDOUT: %Self.ref: type = name_ref Self, constants.%FNotFunction [concrete = constants.%FNotFunction] // CHECK:STDOUT: %I.ref: type = name_ref I, file.%I.decl [concrete = constants.%I.type] // CHECK:STDOUT: } // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type.357] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%FNotFunction // CHECK:STDOUT: .I = // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @F; // CHECK:STDOUT: // CHECK:STDOUT: class @FAlias { // CHECK:STDOUT: impl_decl @FAlias.as.I.impl [concrete] {} { // CHECK:STDOUT: %Self.ref: type = name_ref Self, constants.%FAlias [concrete = constants.%FAlias] // CHECK:STDOUT: %I.ref: type = name_ref I, file.%I.decl [concrete = constants.%I.type] // CHECK:STDOUT: } // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type.357] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%FAlias // CHECK:STDOUT: .I = // CHECK:STDOUT: .PossiblyF = // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @FExtraParam { // CHECK:STDOUT: impl_decl @FExtraParam.as.I.impl [concrete] {} { // CHECK:STDOUT: %Self.ref: type = name_ref Self, constants.%FExtraParam [concrete = constants.%FExtraParam] // CHECK:STDOUT: %I.ref: type = name_ref I, file.%I.decl [concrete = constants.%I.type] // CHECK:STDOUT: } // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type.357] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%FExtraParam // CHECK:STDOUT: .I = // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @FExtraImplicitParam { // CHECK:STDOUT: impl_decl @FExtraImplicitParam.as.I.impl [concrete] {} { // CHECK:STDOUT: %Self.ref: type = name_ref Self, constants.%FExtraImplicitParam [concrete = constants.%FExtraImplicitParam] // CHECK:STDOUT: %I.ref: type = name_ref I, file.%I.decl [concrete = constants.%I.type] // CHECK:STDOUT: } // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type.357] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%FExtraImplicitParam // CHECK:STDOUT: .I = // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @FExtraReturnType { // CHECK:STDOUT: impl_decl @FExtraReturnType.as.I.impl [concrete] {} { // CHECK:STDOUT: %Self.ref: type = name_ref Self, constants.%FExtraReturnType [concrete = constants.%FExtraReturnType] // CHECK:STDOUT: %I.ref: type = name_ref I, file.%I.decl [concrete = constants.%I.type] // CHECK:STDOUT: } // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type.357] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%FExtraReturnType // CHECK:STDOUT: .I = // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @FMissingParam { // CHECK:STDOUT: impl_decl @FMissingParam.as.J.impl [concrete] {} { // CHECK:STDOUT: %Self.ref: type = name_ref Self, constants.%FMissingParam [concrete = constants.%FMissingParam] // CHECK:STDOUT: %J.ref: type = name_ref J, file.%J.decl [concrete = constants.%J.type] // CHECK:STDOUT: } // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type.357] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%FMissingParam // CHECK:STDOUT: .J = // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @FMissingImplicitParam { // CHECK:STDOUT: impl_decl @FMissingImplicitParam.as.J.impl [concrete] {} { // CHECK:STDOUT: %Self.ref: type = name_ref Self, constants.%FMissingImplicitParam [concrete = constants.%FMissingImplicitParam] // CHECK:STDOUT: %J.ref: type = name_ref J, file.%J.decl [concrete = constants.%J.type] // CHECK:STDOUT: } // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type.357] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%FMissingImplicitParam // CHECK:STDOUT: .J = // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @FMissingReturnType { // CHECK:STDOUT: impl_decl @FMissingReturnType.as.J.impl [concrete] {} { // CHECK:STDOUT: %Self.ref: type = name_ref Self, constants.%FMissingReturnType [concrete = constants.%FMissingReturnType] // CHECK:STDOUT: %J.ref: type = name_ref J, file.%J.decl [concrete = constants.%J.type] // CHECK:STDOUT: } // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type.357] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%FMissingReturnType // CHECK:STDOUT: .J = // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @FDifferentParamType { // CHECK:STDOUT: impl_decl @FDifferentParamType.as.J.impl [concrete] {} { // CHECK:STDOUT: %Self.ref: type = name_ref Self, constants.%FDifferentParamType [concrete = constants.%FDifferentParamType] // CHECK:STDOUT: %J.ref: type = name_ref J, file.%J.decl [concrete = constants.%J.type] // CHECK:STDOUT: } // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type.357] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%FDifferentParamType // CHECK:STDOUT: .J = // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @FDifferentImplicitParamType { // CHECK:STDOUT: impl_decl @FDifferentImplicitParamType.as.J.impl [concrete] {} { // CHECK:STDOUT: %Self.ref: type = name_ref Self, constants.%FDifferentImplicitParamType [concrete = constants.%FDifferentImplicitParamType] // CHECK:STDOUT: %J.ref: type = name_ref J, file.%J.decl [concrete = constants.%J.type] // CHECK:STDOUT: } // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type.357] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%FDifferentImplicitParamType // CHECK:STDOUT: .J = // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @FDifferentReturnType { // CHECK:STDOUT: impl_decl @FDifferentReturnType.as.J.impl [concrete] {} { // CHECK:STDOUT: %Self.ref: type = name_ref Self, constants.%FDifferentReturnType [concrete = constants.%FDifferentReturnType] // CHECK:STDOUT: %J.ref: type = name_ref J, file.%J.decl [concrete = constants.%J.type] // CHECK:STDOUT: } // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type.357] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%FDifferentReturnType // CHECK:STDOUT: .J = // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @SelfNestedBadParam { // CHECK:STDOUT: impl_decl @SelfNestedBadParam.as.SelfNested.impl [concrete] {} { // CHECK:STDOUT: %Self.ref: type = name_ref Self, constants.%SelfNestedBadParam [concrete = constants.%SelfNestedBadParam] // CHECK:STDOUT: %SelfNested.ref: type = name_ref SelfNested, file.%SelfNested.decl [concrete = constants.%SelfNested.type] // CHECK:STDOUT: } // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type.357] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%SelfNestedBadParam // CHECK:STDOUT: .SelfNested = // CHECK:STDOUT: .SelfNestedBadParam = // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @SelfNestedBadReturnType { // CHECK:STDOUT: impl_decl @SelfNestedBadReturnType.as.SelfNested.impl [concrete] {} { // CHECK:STDOUT: %Self.ref: type = name_ref Self, constants.%SelfNestedBadReturnType [concrete = constants.%SelfNestedBadReturnType] // CHECK:STDOUT: %SelfNested.ref: type = name_ref SelfNested, file.%SelfNested.decl [concrete = constants.%SelfNested.type] // CHECK:STDOUT: } // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type.357] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%SelfNestedBadReturnType // CHECK:STDOUT: .SelfNested = // CHECK:STDOUT: .SelfNestedBadReturnType = // CHECK:STDOUT: .SelfNestedBadParam = // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic fn @I.WithSelf.F(@I.%Self: %I.type) { // CHECK:STDOUT: fn(); // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @PossiblyF(); // CHECK:STDOUT: // CHECK:STDOUT: fn @FExtraParam.as.I.impl.F.loc69_18.1(%b.param: bool); // CHECK:STDOUT: // CHECK:STDOUT: fn @FExtraParam.as.I.impl.F.loc69_18.2() [thunk @FExtraParam.as.I.impl.%FExtraParam.as.I.impl.F.decl.loc69_18.1] { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %F.ref: %FExtraParam.as.I.impl.F.type.531438.1 = name_ref F, @FExtraParam.as.I.impl.%FExtraParam.as.I.impl.F.decl.loc69_18.1 [concrete = constants.%FExtraParam.as.I.impl.F.33e1fe.1] // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @FExtraImplicitParam.as.I.impl.F.loc85_23.1(%self.param: %FExtraImplicitParam); // CHECK:STDOUT: // CHECK:STDOUT: fn @FExtraImplicitParam.as.I.impl.F.loc85_23.2() [thunk @FExtraImplicitParam.as.I.impl.%FExtraImplicitParam.as.I.impl.F.decl.loc85_23.1] { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %F.ref: %FExtraImplicitParam.as.I.impl.F.type.9dd145.1 = name_ref F, @FExtraImplicitParam.as.I.impl.%FExtraImplicitParam.as.I.impl.F.decl.loc85_23.1 [concrete = constants.%FExtraImplicitParam.as.I.impl.F.99eae8.1] // CHECK:STDOUT: %FExtraImplicitParam.as.I.impl.F.call: init %empty_tuple.type = call %F.ref() // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @FExtraReturnType.as.I.impl.F() -> out %return.param: bool; // CHECK:STDOUT: // CHECK:STDOUT: generic fn @J.WithSelf.F(@J.%Self: %J.type) { // CHECK:STDOUT: fn(%self.param: bool, %b.param: bool) -> out %return.param: bool; // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @FMissingParam.as.J.impl.F.loc117_31.1(%self.param: bool) -> out %return.param: bool; // CHECK:STDOUT: // CHECK:STDOUT: fn @FMissingParam.as.J.impl.F.loc117_31.2(%self.param: bool, %b.param: bool) -> out %return.param: bool [thunk @FMissingParam.as.J.impl.%FMissingParam.as.J.impl.F.decl.loc117_31.1] { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %F.ref: %FMissingParam.as.J.impl.F.type.0f71d8.1 = name_ref F, @FMissingParam.as.J.impl.%FMissingParam.as.J.impl.F.decl.loc117_31.1 [concrete = constants.%FMissingParam.as.J.impl.F.7540f6.1] // CHECK:STDOUT: %self.ref: bool = name_ref self, %self.param // CHECK:STDOUT: %b.ref: bool = name_ref b, %b.param // CHECK:STDOUT: %return.ref: ref bool = name_ref , %return.param // CHECK:STDOUT: %FMissingParam.as.J.impl.F.bound: = bound_method %self.ref, %F.ref // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @FMissingImplicitParam.as.J.impl.F.loc130_26.1(%b.param: bool) -> out %return.param: bool; // CHECK:STDOUT: // CHECK:STDOUT: fn @FMissingImplicitParam.as.J.impl.F.loc130_26.2(%self.param: bool, %b.param: bool) -> out %return.param: bool [thunk @FMissingImplicitParam.as.J.impl.%FMissingImplicitParam.as.J.impl.F.decl.loc130_26.1] { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %F.ref: %FMissingImplicitParam.as.J.impl.F.type.80a56b.1 = name_ref F, @FMissingImplicitParam.as.J.impl.%FMissingImplicitParam.as.J.impl.F.decl.loc130_26.1 [concrete = constants.%FMissingImplicitParam.as.J.impl.F.c6a0b7.1] // CHECK:STDOUT: %self.ref: bool = name_ref self, %self.param // CHECK:STDOUT: %b.ref: bool = name_ref b, %b.param // CHECK:STDOUT: %return.ref: ref bool = name_ref , %return.param // CHECK:STDOUT: %FMissingImplicitParam.as.J.impl.F.call: init bool = call %F.ref(%b.ref) // CHECK:STDOUT: return %FMissingImplicitParam.as.J.impl.F.call // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @FMissingReturnType.as.J.impl.F.loc152_30.1(%self.param: bool, %b.param: bool); // CHECK:STDOUT: // CHECK:STDOUT: fn @FMissingReturnType.as.J.impl.F.loc152_30.2(%self.param: bool, %b.param: bool) -> out %return.param: bool [thunk @FMissingReturnType.as.J.impl.%FMissingReturnType.as.J.impl.F.decl.loc152_30.1] { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %F.ref: %FMissingReturnType.as.J.impl.F.type.ce0d21.1 = name_ref F, @FMissingReturnType.as.J.impl.%FMissingReturnType.as.J.impl.F.decl.loc152_30.1 [concrete = constants.%FMissingReturnType.as.J.impl.F.5b613a.1] // CHECK:STDOUT: %self.ref: bool = name_ref self, %self.param // CHECK:STDOUT: %b.ref: bool = name_ref b, %b.param // CHECK:STDOUT: %return.ref: ref bool = name_ref , %return.param // CHECK:STDOUT: %FMissingReturnType.as.J.impl.F.bound: = bound_method %self.ref, %F.ref // CHECK:STDOUT: %FMissingReturnType.as.J.impl.F.call: init %empty_tuple.type = call %FMissingReturnType.as.J.impl.F.bound(%self.ref, %b.ref) // CHECK:STDOUT: %.loc152: bool = converted %FMissingReturnType.as.J.impl.F.call, [concrete = ] // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @FDifferentParamType.as.J.impl.F.loc171_38.1(%self.param: bool, %b.param: %FDifferentParamType) -> out %return.param: bool; // CHECK:STDOUT: // CHECK:STDOUT: fn @FDifferentParamType.as.J.impl.F.loc171_38.2(%self.param: bool, %b.param: bool) -> out %return.param: bool [thunk @FDifferentParamType.as.J.impl.%FDifferentParamType.as.J.impl.F.decl.loc171_38.1] { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %F.ref: %FDifferentParamType.as.J.impl.F.type.10926d.1 = name_ref F, @FDifferentParamType.as.J.impl.%FDifferentParamType.as.J.impl.F.decl.loc171_38.1 [concrete = constants.%FDifferentParamType.as.J.impl.F.f1245a.1] // CHECK:STDOUT: %self.ref: bool = name_ref self, %self.param // CHECK:STDOUT: %b.ref: bool = name_ref b, %b.param // CHECK:STDOUT: %return.ref: ref bool = name_ref , %return.param // CHECK:STDOUT: %FDifferentParamType.as.J.impl.F.bound: = bound_method %self.ref, %F.ref // CHECK:STDOUT: %.loc103: %FDifferentParamType = converted %b.ref, [concrete = ] // CHECK:STDOUT: %FDifferentParamType.as.J.impl.F.call: init bool = call %FDifferentParamType.as.J.impl.F.bound(%self.ref, ) // CHECK:STDOUT: return %FDifferentParamType.as.J.impl.F.call // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @FDifferentImplicitParamType.as.J.impl.F.loc184_38.1(%self.param: %FDifferentImplicitParamType, %b.param: bool) -> out %return.param: bool; // CHECK:STDOUT: // CHECK:STDOUT: fn @FDifferentImplicitParamType.as.J.impl.F.loc184_38.2(%self.param: bool, %b.param: bool) -> out %return.param: bool [thunk @FDifferentImplicitParamType.as.J.impl.%FDifferentImplicitParamType.as.J.impl.F.decl.loc184_38.1] { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %F.ref: %FDifferentImplicitParamType.as.J.impl.F.type.955391.1 = name_ref F, @FDifferentImplicitParamType.as.J.impl.%FDifferentImplicitParamType.as.J.impl.F.decl.loc184_38.1 [concrete = constants.%FDifferentImplicitParamType.as.J.impl.F.a9ece4.1] // CHECK:STDOUT: %self.ref: bool = name_ref self, %self.param // CHECK:STDOUT: %b.ref: bool = name_ref b, %b.param // CHECK:STDOUT: %return.ref: ref bool = name_ref , %return.param // CHECK:STDOUT: %FDifferentImplicitParamType.as.J.impl.F.bound: = bound_method %self.ref, %F.ref // CHECK:STDOUT: %.loc103: %FDifferentImplicitParamType = converted %self.ref, [concrete = ] // CHECK:STDOUT: %FDifferentImplicitParamType.as.J.impl.F.call: init bool = call %FDifferentImplicitParamType.as.J.impl.F.bound(, %b.ref) // CHECK:STDOUT: return %FDifferentImplicitParamType.as.J.impl.F.call // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @FDifferentReturnType.as.J.impl.F.loc200_38.1(%self.param: bool, %b.param: bool) -> out %return.param: %FDifferentReturnType; // CHECK:STDOUT: // CHECK:STDOUT: fn @FDifferentReturnType.as.J.impl.F.loc200_38.2(%self.param: bool, %b.param: bool) -> out %return.param: bool [thunk @FDifferentReturnType.as.J.impl.%FDifferentReturnType.as.J.impl.F.decl.loc200_38.1] { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %F.ref: %FDifferentReturnType.as.J.impl.F.type.7bcb67.1 = name_ref F, @FDifferentReturnType.as.J.impl.%FDifferentReturnType.as.J.impl.F.decl.loc200_38.1 [concrete = constants.%FDifferentReturnType.as.J.impl.F.a709b1.1] // CHECK:STDOUT: %self.ref: bool = name_ref self, %self.param // CHECK:STDOUT: %b.ref: bool = name_ref b, %b.param // CHECK:STDOUT: %return.ref: ref bool = name_ref , %return.param // CHECK:STDOUT: %FDifferentReturnType.as.J.impl.F.bound: = bound_method %self.ref, %F.ref // CHECK:STDOUT: %.loc200_38.1: ref %FDifferentReturnType = temporary_storage // CHECK:STDOUT: %FDifferentReturnType.as.J.impl.F.call: init %FDifferentReturnType to %.loc200_38.1 = call %FDifferentReturnType.as.J.impl.F.bound(%self.ref, %b.ref) // CHECK:STDOUT: %.loc200_38.2: bool = converted %FDifferentReturnType.as.J.impl.F.call, [concrete = ] // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic fn @SelfNested.WithSelf.F(@SelfNested.%Self: %SelfNested.type) { // CHECK:STDOUT: %Self: %SelfNested.type = symbolic_binding Self, 0 [symbolic = %Self (constants.%Self.aed)] // CHECK:STDOUT: %Self.binding.as_type: type = symbolic_binding_type Self, 0, %Self [symbolic = %Self.binding.as_type (constants.%Self.binding.as_type.07b)] // CHECK:STDOUT: %ptr.loc211_16.1: type = ptr_type %Self.binding.as_type [symbolic = %ptr.loc211_16.1 (constants.%ptr.d99)] // CHECK:STDOUT: %struct_type.x.y.loc211_37.1: type = struct_type {.x: @SelfNested.WithSelf.F.%Self.binding.as_type (%Self.binding.as_type.07b), .y: %i32} [symbolic = %struct_type.x.y.loc211_37.1 (constants.%struct_type.x.y.e05)] // CHECK:STDOUT: %tuple: %tuple.type.24b = tuple_value (%ptr.loc211_16.1, %struct_type.x.y.loc211_37.1) [symbolic = %tuple (constants.%tuple.179)] // CHECK:STDOUT: %tuple.type: type = tuple_type (%ptr.loc211_16.1, %struct_type.x.y.loc211_37.1) [symbolic = %tuple.type (constants.%tuple.type.9cf)] // CHECK:STDOUT: %pattern_type.loc211_8: type = pattern_type %tuple.type [symbolic = %pattern_type.loc211_8 (constants.%pattern_type.37a)] // CHECK:STDOUT: %array_type.loc211_57.1: type = array_type constants.%int_4, %Self.binding.as_type [symbolic = %array_type.loc211_57.1 (constants.%array_type.eba)] // CHECK:STDOUT: %.loc211_57.1: Core.Form = init_form %array_type.loc211_57.1 [symbolic = %.loc211_57.1 (constants.%.efe)] // CHECK:STDOUT: %pattern_type.loc211_41: type = pattern_type %array_type.loc211_57.1 [symbolic = %pattern_type.loc211_41 (constants.%pattern_type.dfe)] // CHECK:STDOUT: // CHECK:STDOUT: fn(%x.param: @SelfNested.WithSelf.F.%tuple.type (%tuple.type.9cf)) -> out %return.param: @SelfNested.WithSelf.F.%array_type.loc211_57.1 (%array_type.eba); // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @SelfNestedBadParam.as.SelfNested.impl.F.loc223_87.1(%x.param: %tuple.type.70f) -> out %return.param: %array_type.388; // CHECK:STDOUT: // CHECK:STDOUT: fn @SelfNestedBadParam.as.SelfNested.impl.F.loc223_87.2(%x.param: %tuple.type.7ea) -> out %return.param: %array_type.388 [thunk @SelfNestedBadParam.as.SelfNested.impl.%SelfNestedBadParam.as.SelfNested.impl.F.decl.loc223_87.1] { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %F.ref: %SelfNestedBadParam.as.SelfNested.impl.F.type.789632.1 = name_ref F, @SelfNestedBadParam.as.SelfNested.impl.%SelfNestedBadParam.as.SelfNested.impl.F.decl.loc223_87.1 [concrete = constants.%SelfNestedBadParam.as.SelfNested.impl.F.6e1661.1] // CHECK:STDOUT: %x.ref: %tuple.type.7ea = name_ref x, %x.param // CHECK:STDOUT: %return.ref: ref %array_type.388 = name_ref , %return.param // CHECK:STDOUT: %.loc211_57: ref %array_type.388 = splice_block %return.param {} // CHECK:STDOUT: %tuple.elem0: %ptr.cf2 = tuple_access %x.ref, element0 // CHECK:STDOUT: %tuple.elem1: %struct_type.x.y.62a = tuple_access %x.ref, element1 // CHECK:STDOUT: %.loc211_9.1: %SelfNestedBadParam = struct_access %tuple.elem1, element0 // CHECK:STDOUT: %.loc211_9.2: %i32 = converted %.loc211_9.1, [concrete = ] // CHECK:STDOUT: %SelfNestedBadParam.as.SelfNested.impl.F.call: init %array_type.388 to %.loc211_57 = call %F.ref() // CHECK:STDOUT: return %SelfNestedBadParam.as.SelfNested.impl.F.call to %return.param // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @SelfNestedBadReturnType.as.SelfNested.impl.F.loc239_112.1(%x.param: %tuple.type.064) -> out %return.param: %array_type.388; // CHECK:STDOUT: // CHECK:STDOUT: fn @SelfNestedBadReturnType.as.SelfNested.impl.F.loc239_112.2(%x.param: %tuple.type.064) -> out %return.param: %array_type.31b [thunk @SelfNestedBadReturnType.as.SelfNested.impl.%SelfNestedBadReturnType.as.SelfNested.impl.F.decl.loc239_112.1] { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %F.ref: %SelfNestedBadReturnType.as.SelfNested.impl.F.type.75d128.1 = name_ref F, @SelfNestedBadReturnType.as.SelfNested.impl.%SelfNestedBadReturnType.as.SelfNested.impl.F.decl.loc239_112.1 [concrete = constants.%SelfNestedBadReturnType.as.SelfNested.impl.F.587f77.1] // CHECK:STDOUT: %x.ref: %tuple.type.064 = name_ref x, %x.param // CHECK:STDOUT: %return.ref: ref %array_type.31b = name_ref , %return.param // CHECK:STDOUT: %.loc239_112.1: ref %array_type.388 = temporary_storage // CHECK:STDOUT: %SelfNestedBadReturnType.as.SelfNested.impl.F.call: init %array_type.388 to %.loc239_112.1 = call %F.ref(%x.ref) // CHECK:STDOUT: %.loc239_112.2: %array_type.31b = converted %SelfNestedBadReturnType.as.SelfNested.impl.F.call, [concrete = ] // CHECK:STDOUT: return to %return.param // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @I.WithSelf(constants.%Self.ab9) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: %Self => constants.%Self.ab9 // CHECK:STDOUT: %I.WithSelf.F.type => constants.%I.WithSelf.F.type.08c // CHECK:STDOUT: %I.WithSelf.F => constants.%I.WithSelf.F.705 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @I.WithSelf.F(constants.%Self.ab9) {} // CHECK:STDOUT: // CHECK:STDOUT: specific @I.WithSelf(constants.%I.facet.fc8) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: %Self => constants.%I.facet.fc8 // CHECK:STDOUT: %I.WithSelf.F.type => constants.%I.WithSelf.F.type.cec // CHECK:STDOUT: %I.WithSelf.F => constants.%I.WithSelf.F.733 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @I.WithSelf(constants.%I.facet.c1a) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: %Self => constants.%I.facet.c1a // CHECK:STDOUT: %I.WithSelf.F.type => constants.%I.WithSelf.F.type.1b8 // CHECK:STDOUT: %I.WithSelf.F => constants.%I.WithSelf.F.7e3 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @I.WithSelf(constants.%I.facet.574) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: %Self => constants.%I.facet.574 // CHECK:STDOUT: %I.WithSelf.F.type => constants.%I.WithSelf.F.type.f26 // CHECK:STDOUT: %I.WithSelf.F => constants.%I.WithSelf.F.5e6 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @I.WithSelf(constants.%I.facet.b53) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: %Self => constants.%I.facet.b53 // CHECK:STDOUT: %I.WithSelf.F.type => constants.%I.WithSelf.F.type.99d // CHECK:STDOUT: %I.WithSelf.F => constants.%I.WithSelf.F.eab // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @I.WithSelf.F(constants.%I.facet.b53) {} // CHECK:STDOUT: // CHECK:STDOUT: specific @I.WithSelf(constants.%I.facet.5a5) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: %Self => constants.%I.facet.5a5 // CHECK:STDOUT: %I.WithSelf.F.type => constants.%I.WithSelf.F.type.e46 // CHECK:STDOUT: %I.WithSelf.F => constants.%I.WithSelf.F.5bc // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @I.WithSelf.F(constants.%I.facet.5a5) {} // CHECK:STDOUT: // CHECK:STDOUT: specific @I.WithSelf(constants.%I.facet.1ed) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: %Self => constants.%I.facet.1ed // CHECK:STDOUT: %I.WithSelf.F.type => constants.%I.WithSelf.F.type.cd5 // CHECK:STDOUT: %I.WithSelf.F => constants.%I.WithSelf.F.aee // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @I.WithSelf.F(constants.%I.facet.1ed) {} // CHECK:STDOUT: // CHECK:STDOUT: specific @J.WithSelf(constants.%Self.8a1) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: %Self => constants.%Self.8a1 // CHECK:STDOUT: %J.WithSelf.F.type => constants.%J.WithSelf.F.type.adb // CHECK:STDOUT: %J.WithSelf.F => constants.%J.WithSelf.F.569 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @J.WithSelf.F(constants.%Self.8a1) {} // CHECK:STDOUT: // CHECK:STDOUT: specific @J.WithSelf(constants.%J.facet.567) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: %Self => constants.%J.facet.567 // CHECK:STDOUT: %J.WithSelf.F.type => constants.%J.WithSelf.F.type.54f // CHECK:STDOUT: %J.WithSelf.F => constants.%J.WithSelf.F.7e8 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @J.WithSelf.F(constants.%J.facet.567) {} // CHECK:STDOUT: // CHECK:STDOUT: specific @J.WithSelf(constants.%J.facet.266) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: %Self => constants.%J.facet.266 // CHECK:STDOUT: %J.WithSelf.F.type => constants.%J.WithSelf.F.type.56e // CHECK:STDOUT: %J.WithSelf.F => constants.%J.WithSelf.F.d17 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @J.WithSelf.F(constants.%J.facet.266) {} // CHECK:STDOUT: // CHECK:STDOUT: specific @J.WithSelf(constants.%J.facet.a9e) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: %Self => constants.%J.facet.a9e // CHECK:STDOUT: %J.WithSelf.F.type => constants.%J.WithSelf.F.type.8be // CHECK:STDOUT: %J.WithSelf.F => constants.%J.WithSelf.F.2a5 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @J.WithSelf.F(constants.%J.facet.a9e) {} // CHECK:STDOUT: // CHECK:STDOUT: specific @J.WithSelf(constants.%J.facet.bd2) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: %Self => constants.%J.facet.bd2 // CHECK:STDOUT: %J.WithSelf.F.type => constants.%J.WithSelf.F.type.06a // CHECK:STDOUT: %J.WithSelf.F => constants.%J.WithSelf.F.cad // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @J.WithSelf.F(constants.%J.facet.bd2) {} // CHECK:STDOUT: // CHECK:STDOUT: specific @J.WithSelf(constants.%J.facet.030) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: %Self => constants.%J.facet.030 // CHECK:STDOUT: %J.WithSelf.F.type => constants.%J.WithSelf.F.type.f29 // CHECK:STDOUT: %J.WithSelf.F => constants.%J.WithSelf.F.5b7 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @J.WithSelf.F(constants.%J.facet.030) {} // CHECK:STDOUT: // CHECK:STDOUT: specific @J.WithSelf(constants.%J.facet.4a1) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: %Self => constants.%J.facet.4a1 // CHECK:STDOUT: %J.WithSelf.F.type => constants.%J.WithSelf.F.type.8e3 // CHECK:STDOUT: %J.WithSelf.F => constants.%J.WithSelf.F.182 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @J.WithSelf.F(constants.%J.facet.4a1) {} // CHECK:STDOUT: // CHECK:STDOUT: specific @SelfNested.WithSelf(constants.%Self.aed) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: %Self => constants.%Self.aed // CHECK:STDOUT: %SelfNested.WithSelf.F.type => constants.%SelfNested.WithSelf.F.type.430 // CHECK:STDOUT: %SelfNested.WithSelf.F => constants.%SelfNested.WithSelf.F.257 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @SelfNested.WithSelf.F(constants.%Self.aed) { // CHECK:STDOUT: %Self => constants.%Self.aed // CHECK:STDOUT: %Self.binding.as_type => constants.%Self.binding.as_type.07b // CHECK:STDOUT: %ptr.loc211_16.1 => constants.%ptr.d99 // CHECK:STDOUT: %struct_type.x.y.loc211_37.1 => constants.%struct_type.x.y.e05 // CHECK:STDOUT: %tuple => constants.%tuple.179 // CHECK:STDOUT: %tuple.type => constants.%tuple.type.9cf // CHECK:STDOUT: %pattern_type.loc211_8 => constants.%pattern_type.37a // CHECK:STDOUT: %array_type.loc211_57.1 => constants.%array_type.eba // CHECK:STDOUT: %.loc211_57.1 => constants.%.efe // CHECK:STDOUT: %pattern_type.loc211_41 => constants.%pattern_type.dfe // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @SelfNested.WithSelf(constants.%SelfNested.facet.b9f) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: %Self => constants.%SelfNested.facet.b9f // CHECK:STDOUT: %SelfNested.WithSelf.F.type => constants.%SelfNested.WithSelf.F.type.4de // CHECK:STDOUT: %SelfNested.WithSelf.F => constants.%SelfNested.WithSelf.F.5e9 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @SelfNested.WithSelf.F(constants.%SelfNested.facet.b9f) { // CHECK:STDOUT: %Self => constants.%SelfNested.facet.b9f // CHECK:STDOUT: %Self.binding.as_type => constants.%SelfNestedBadParam // CHECK:STDOUT: %ptr.loc211_16.1 => constants.%ptr.cf2 // CHECK:STDOUT: %struct_type.x.y.loc211_37.1 => constants.%struct_type.x.y.62a // CHECK:STDOUT: %tuple => constants.%tuple.73b // CHECK:STDOUT: %tuple.type => constants.%tuple.type.7ea // CHECK:STDOUT: %pattern_type.loc211_8 => constants.%pattern_type.8d7 // CHECK:STDOUT: %array_type.loc211_57.1 => constants.%array_type.388 // CHECK:STDOUT: %.loc211_57.1 => constants.%.0cb // CHECK:STDOUT: %pattern_type.loc211_41 => constants.%pattern_type.ec1 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @SelfNested.WithSelf(constants.%SelfNested.facet.23f) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: %Self => constants.%SelfNested.facet.23f // CHECK:STDOUT: %SelfNested.WithSelf.F.type => constants.%SelfNested.WithSelf.F.type.156 // CHECK:STDOUT: %SelfNested.WithSelf.F => constants.%SelfNested.WithSelf.F.edc // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @SelfNested.WithSelf.F(constants.%SelfNested.facet.23f) { // CHECK:STDOUT: %Self => constants.%SelfNested.facet.23f // CHECK:STDOUT: %Self.binding.as_type => constants.%SelfNestedBadReturnType // CHECK:STDOUT: %ptr.loc211_16.1 => constants.%ptr.cbe // CHECK:STDOUT: %struct_type.x.y.loc211_37.1 => constants.%struct_type.x.y.cfa // CHECK:STDOUT: %tuple => constants.%tuple.c42 // CHECK:STDOUT: %tuple.type => constants.%tuple.type.064 // CHECK:STDOUT: %pattern_type.loc211_8 => constants.%pattern_type.1f1 // CHECK:STDOUT: %array_type.loc211_57.1 => constants.%array_type.31b // CHECK:STDOUT: %.loc211_57.1 => constants.%.369 // CHECK:STDOUT: %pattern_type.loc211_41 => constants.%pattern_type.1fd // CHECK:STDOUT: } // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/impl/fail_impl_bad_interface.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/primitives.carbon // TODO: Add ranges and switch to "--dump-sem-ir-ranges=only". // EXTRA-ARGS: --dump-sem-ir-ranges=if-present // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/impl/fail_impl_bad_interface.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/impl/fail_impl_bad_interface.carbon // --- fail_impl_as_false.carbon library "[[@TEST_NAME]]"; // CHECK:STDERR: fail_impl_as_false.carbon:[[@LINE+7]]:13: error: cannot implicitly convert non-type value of type `bool` to `type` [ConversionFailureNonTypeToFacet] // CHECK:STDERR: impl i32 as false {} // CHECK:STDERR: ^~~~~ // CHECK:STDERR: fail_impl_as_false.carbon:[[@LINE+4]]:13: note: type `bool` does not implement interface `Core.ImplicitAs(type)` [MissingImplInMemberAccessInContext] // CHECK:STDERR: impl i32 as false {} // CHECK:STDERR: ^~~~~ // CHECK:STDERR: impl i32 as false {} // --- fail_impl_as_type.carbon library "[[@TEST_NAME]]"; // CHECK:STDERR: fail_impl_as_type.carbon:[[@LINE+4]]:1: error: impl as non-facet type `type` [ImplAsNonFacetType] // CHECK:STDERR: impl bool as type {} // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: impl bool as type {} // --- fail_impl_as_type_where.carbon library "[[@TEST_NAME]]"; // CHECK:STDERR: fail_impl_as_type_where.carbon:[[@LINE+4]]:1: error: impl as 0 interfaces, expected 1 [ImplOfNotOneInterface] // CHECK:STDERR: impl f64 as type where .Self impls type {} // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: impl f64 as type where .Self impls type {} // --- fail_impl_as_type_where_impls_interface.carbon library "[[@TEST_NAME]]"; interface I {} // CHECK:STDERR: fail_impl_as_type_where_impls_interface.carbon:[[@LINE+4]]:1: error: impl as 0 interfaces, expected 1 [ImplOfNotOneInterface] // CHECK:STDERR: impl {.a: bool} as type where .Self impls I {} // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: impl {.a: bool} as type where .Self impls I {} // CHECK:STDOUT: --- fail_impl_as_false.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [concrete] // CHECK:STDOUT: %Int.type: type = generic_class_type @Int [concrete] // CHECK:STDOUT: %Int.generic: %Int.type = struct_value () [concrete] // CHECK:STDOUT: %i32: type = class_type @Int, @Int(%int_32) [concrete] // CHECK:STDOUT: %false: bool = bool_literal false [concrete] // CHECK:STDOUT: %ImplicitAs.type.cc7: type = generic_interface_type @ImplicitAs [concrete] // CHECK:STDOUT: %ImplicitAs.generic: %ImplicitAs.type.cc7 = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: .Int = %Core.Int // CHECK:STDOUT: .ImplicitAs = %Core.ImplicitAs // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: %Core.Int: %Int.type = import_ref Core//prelude/parts/int, Int, loaded [concrete = constants.%Int.generic] // CHECK:STDOUT: %Core.ImplicitAs: %ImplicitAs.type.cc7 = import_ref Core//prelude/parts/as, ImplicitAs, loaded [concrete = constants.%ImplicitAs.generic] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %.loc11: type = converted @i32.as..impl.%false, [concrete = ] // CHECK:STDOUT: impl_decl @i32.as..impl [concrete] {} { // CHECK:STDOUT: %i32: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %false: bool = bool_literal false [concrete = constants.%false] // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: impl @i32.as..impl: %i32 as { // CHECK:STDOUT: !members: // CHECK:STDOUT: witness = // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- fail_impl_as_type.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %Bool.type: type = fn_type @Bool [concrete] // CHECK:STDOUT: %Bool: %Bool.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: .Bool = %Core.Bool // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: %Core.Bool: %Bool.type = import_ref Core//prelude/parts/bool, Bool, loaded [concrete = constants.%Bool] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: impl_decl @bool.as..impl [concrete] {} { // CHECK:STDOUT: %.loc8_6: type = type_literal bool [concrete = bool] // CHECK:STDOUT: %.loc8_14: type = type_literal type [concrete = type] // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: impl @bool.as..impl: %.loc8_6 as %.loc8_14 { // CHECK:STDOUT: !members: // CHECK:STDOUT: witness = // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- fail_impl_as_type_where.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %int_64: Core.IntLiteral = int_value 64 [concrete] // CHECK:STDOUT: %Float.type: type = generic_class_type @Float [concrete] // CHECK:STDOUT: %Float.generic: %Float.type = struct_value () [concrete] // CHECK:STDOUT: %f64: type = class_type @Float, @Float(%int_64) [concrete] // CHECK:STDOUT: %.Self: type = symbolic_binding .Self [symbolic_self] // CHECK:STDOUT: %type: type = facet_type [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: .Float = %Core.Float // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: %Core.Float: %Float.type = import_ref Core//prelude/parts/float, Float, loaded [concrete = constants.%Float.generic] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: impl_decl @f64.as..impl [concrete] {} { // CHECK:STDOUT: %f64: type = type_literal constants.%f64 [concrete = constants.%f64] // CHECK:STDOUT: %.loc8_13: type = type_literal type [concrete = type] // CHECK:STDOUT: %.Self: type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %.Self.ref: type = name_ref .Self, %.Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %.loc8_36: type = type_literal type [concrete = type] // CHECK:STDOUT: %.loc8_18: type = where_expr %.Self [concrete = constants.%type] { // CHECK:STDOUT: requirement_base_facet_type type // CHECK:STDOUT: requirement_impls %.Self.ref, %.loc8_36 // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: impl @f64.as..impl: %f64 as %.loc8_18 { // CHECK:STDOUT: !members: // CHECK:STDOUT: witness = // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- fail_impl_as_type_where_impls_interface.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %I.type: type = facet_type <@I> [concrete] // CHECK:STDOUT: %Self: %I.type = symbolic_binding Self, 0 [symbolic] // CHECK:STDOUT: %Bool.type: type = fn_type @Bool [concrete] // CHECK:STDOUT: %Bool: %Bool.type = struct_value () [concrete] // CHECK:STDOUT: %struct_type.a: type = struct_type {.a: bool} [concrete] // CHECK:STDOUT: %.Self: type = symbolic_binding .Self [symbolic_self] // CHECK:STDOUT: %type_where: type = facet_type [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: .Bool = %Core.Bool // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: %Core.Bool: %Bool.type = import_ref Core//prelude/parts/bool, Bool, loaded [concrete = constants.%Bool] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .I = %I.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %I.decl: type = interface_decl @I [concrete = constants.%I.type] {} {} // CHECK:STDOUT: impl_decl @struct_type.a.as..impl [concrete] {} { // CHECK:STDOUT: %.loc10_11: type = type_literal bool [concrete = bool] // CHECK:STDOUT: %struct_type.a: type = struct_type {.a: bool} [concrete = constants.%struct_type.a] // CHECK:STDOUT: %.loc10_20: type = type_literal type [concrete = type] // CHECK:STDOUT: %.Self: type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %.Self.ref: type = name_ref .Self, %.Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %I.ref: type = name_ref I, file.%I.decl [concrete = constants.%I.type] // CHECK:STDOUT: %.loc10_25: type = where_expr %.Self [concrete = constants.%type_where] { // CHECK:STDOUT: requirement_base_facet_type type // CHECK:STDOUT: requirement_impls %.Self.ref, %I.ref // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: interface @I { // CHECK:STDOUT: %Self: %I.type = symbolic_binding Self, 0 [symbolic = constants.%Self] // CHECK:STDOUT: %I.WithSelf.decl = interface_with_self_decl @I [concrete] // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = %Self // CHECK:STDOUT: witness = () // CHECK:STDOUT: // CHECK:STDOUT: !requires: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: impl @struct_type.a.as..impl: %struct_type.a as %.loc10_25 { // CHECK:STDOUT: !members: // CHECK:STDOUT: witness = // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @I.WithSelf(constants.%Self) {} // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/impl/fail_impl_bad_type.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/convert.carbon // TODO: Add ranges and switch to "--dump-sem-ir-ranges=only". // EXTRA-ARGS: --dump-sem-ir-ranges=if-present // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/impl/fail_impl_bad_type.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/impl/fail_impl_bad_type.carbon interface I {} // CHECK:STDERR: fail_impl_bad_type.carbon:[[@LINE+7]]:6: error: cannot implicitly convert non-type value of type `bool` to `type` [ConversionFailureNonTypeToFacet] // CHECK:STDERR: impl true as I {} // CHECK:STDERR: ^~~~ // CHECK:STDERR: fail_impl_bad_type.carbon:[[@LINE+4]]:6: note: type `bool` does not implement interface `Core.ImplicitAs(type)` [MissingImplInMemberAccessInContext] // CHECK:STDERR: impl true as I {} // CHECK:STDERR: ^~~~ // CHECK:STDERR: impl true as I {} // CHECK:STDOUT: --- fail_impl_bad_type.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %I.type: type = facet_type <@I> [concrete] // CHECK:STDOUT: %Self.ab9: %I.type = symbolic_binding Self, 0 [symbolic] // CHECK:STDOUT: %true: bool = bool_literal true [concrete] // CHECK:STDOUT: %ImplicitAs.type.cc7: type = generic_interface_type @ImplicitAs [concrete] // CHECK:STDOUT: %ImplicitAs.generic: %ImplicitAs.type.cc7 = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: .ImplicitAs = %Core.ImplicitAs // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: %Core.ImplicitAs: %ImplicitAs.type.cc7 = import_ref Core//prelude/parts/as, ImplicitAs, loaded [concrete = constants.%ImplicitAs.generic] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .I = %I.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %I.decl: type = interface_decl @I [concrete = constants.%I.type] {} {} // CHECK:STDOUT: impl_decl @.as.I.impl [concrete] {} { // CHECK:STDOUT: %true: bool = bool_literal true [concrete = constants.%true] // CHECK:STDOUT: %.loc24: type = converted %true, [concrete = ] // CHECK:STDOUT: %I.ref: type = name_ref I, file.%I.decl [concrete = constants.%I.type] // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: interface @I { // CHECK:STDOUT: %Self: %I.type = symbolic_binding Self, 0 [symbolic = constants.%Self.ab9] // CHECK:STDOUT: %I.WithSelf.decl = interface_with_self_decl @I [concrete] // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = %Self // CHECK:STDOUT: witness = () // CHECK:STDOUT: // CHECK:STDOUT: !requires: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: impl @.as.I.impl: as %I.ref { // CHECK:STDOUT: !members: // CHECK:STDOUT: witness = // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @I.WithSelf(constants.%Self.ab9) {} // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/impl/fail_redefinition.carbon ================================================ [File too large to display: 4.3 KB] ================================================ FILE: toolchain/check/testdata/impl/fail_self_type_mismatch.carbon ================================================ [File too large to display: 19.7 KB] ================================================ FILE: toolchain/check/testdata/impl/fail_todo_form_thunk.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/none.carbon // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/impl/fail_todo_form_thunk.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/impl/fail_todo_form_thunk.carbon interface I { let T:! type; // CHECK:STDERR: fail_todo_form_thunk.carbon:[[@LINE+4]]:8: error: semantics TODO: `Support for cloning form bindings` [SemanticsTodo] // CHECK:STDERR: fn F(x:? form(ref ()), y: T); // CHECK:STDERR: ^ // CHECK:STDERR: fn F(x:? form(ref ()), y: T); } class C { impl as I where .T = () { fn F(x:? form(ref ()), ()); } } ================================================ FILE: toolchain/check/testdata/impl/forward_decls.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/convert.carbon // TODO: Add ranges and switch to "--dump-sem-ir-ranges=only". // EXTRA-ARGS: --dump-sem-ir-ranges=if-present // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/impl/forward_decls.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/impl/forward_decls.carbon // --- empty.carbon library "[[@TEST_NAME]]"; interface I; impl {} as I; interface I {} impl {} as I {} // --- method.carbon library "[[@TEST_NAME]]"; interface I; impl {} as I; interface I { fn G(); } impl {} as I { fn G() {} } // --- combine.carbon library "[[@TEST_NAME]]"; interface I; impl {} as I & I; interface I {} impl {} as I & I {} // --- associated_const.carbon library "[[@TEST_NAME]]"; interface I { let T:! type; } impl {} as I where .T = (); impl {} as I where .T = () {} // --- associated_const_compound_member_access.carbon library "[[@TEST_NAME]]"; interface I { let T:! type; } class C {} impl C as I where .T = (); let x: C.(I.T) = (); impl C as I where .T = () {} // --- associated_const_of_facet.carbon library "[[@TEST_NAME]]"; interface I { let T:! type; } class C {} impl C as I where .T = (); let x: (C as I).T = (); impl C as I where .T = () {} // --- fail_unset_associated_const.carbon library "[[@TEST_NAME]]"; interface I { let T:! type; } class C {} impl C as I; // CHECK:STDERR: fail_unset_associated_const.carbon:[[@LINE+4]]:8: error: accessing member from impl before it has a defined value [ImplAccessMemberBeforeSet] // CHECK:STDERR: let x: (C as I).T = (); // CHECK:STDERR: ^~~~~~~~~~ // CHECK:STDERR: let x: (C as I).T = (); // CHECK:STDERR: fail_unset_associated_const.carbon:[[@LINE+7]]:1: error: associated constant T not given a value in impl of interface I [ImplAssociatedConstantNeedsValue] // CHECK:STDERR: impl C as I {} // CHECK:STDERR: ^~~~~~~~~~~~~ // CHECK:STDERR: fail_unset_associated_const.carbon:[[@LINE-14]]:7: note: associated constant declared here [AssociatedConstantHere] // CHECK:STDERR: let T:! type; // CHECK:STDERR: ^~~~~~~~ // CHECK:STDERR: impl C as I {} // --- fail_associated_const_before_interface_definition.carbon library "[[@TEST_NAME]]"; interface I; class C; class D {} // CHECK:STDERR: fail_associated_const_before_interface_definition.carbon:[[@LINE+7]]:19: error: member access into facet of incomplete type `I` [IncompleteTypeInMemberAccessOfFacet] // CHECK:STDERR: impl D as I where .T = C; // CHECK:STDERR: ^~ // CHECK:STDERR: fail_associated_const_before_interface_definition.carbon:[[@LINE-6]]:1: note: interface was forward declared here [InterfaceForwardDeclaredHere] // CHECK:STDERR: interface I; // CHECK:STDERR: ^~~~~~~~~~~~ // CHECK:STDERR: impl D as I where .T = C; interface I {} class C {} // CHECK:STDERR: fail_associated_const_before_interface_definition.carbon:[[@LINE+4]]:19: error: member name `T` not found in `I` [MemberNameNotFoundInSpecificScope] // CHECK:STDERR: impl D as I where .T = C {} // CHECK:STDERR: ^~ // CHECK:STDERR: impl D as I where .T = C {} // --- associated_const_of_parameterized.carbon library "[[@TEST_NAME]]"; interface I(U:! type) { let T:! type; } class C; class D {} impl D as I(C) where .T = C; class C {} impl D as I(C) where .T = C {} // --- find_incomplete_impl.carbon library "[[@TEST_NAME]]"; interface I; class D {} impl D as I; class C(T:! I); fn F(x: C(D)); interface I {} impl D as I {} class C(T:! I) {} fn F(unused x: C(D)) {} // --- fail_todo_two_interfaces.carbon library "[[@TEST_NAME]]"; interface I; interface J; // CHECK:STDERR: fail_todo_two_interfaces.carbon:[[@LINE+4]]:1: error: impl as 2 interfaces, expected 1 [ImplOfNotOneInterface] // CHECK:STDERR: impl {} as I & J; // CHECK:STDERR: ^~~~~~~~~~~~~~~~~ // CHECK:STDERR: impl {} as I & J; interface I {} interface J {} // --- fail_never_assigned_associated_const.carbon library "[[@TEST_NAME]]"; interface I { let T:! type; let U:! type; } class C {} impl C as I where .T = (); // CHECK:STDERR: fail_never_assigned_associated_const.carbon:[[@LINE+7]]:1: error: associated constant U not given a value in impl of interface I [ImplAssociatedConstantNeedsValue] // CHECK:STDERR: impl C as I where .T = () {} // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: fail_never_assigned_associated_const.carbon:[[@LINE-9]]:7: note: associated constant declared here [AssociatedConstantHere] // CHECK:STDERR: let U:! type; // CHECK:STDERR: ^~~~~~~~ // CHECK:STDERR: impl C as I where .T = () {} // --- example_from_proposal_5168.carbon library "[[@TEST_NAME]]"; interface X; // Allowed to use incomplete interfaces in function declarations. fn F(_:! X); fn G[U:! X](u: U); class C; // Allowed to use incomplete types and interfaces in impl declarations. impl C as X; interface Y; interface X { // TODO: Should be allowed to use an incomplete interface here, // but `require Self impls` is not supported yet. // require Self impls Y; } // Classes must be defined before being used in a function definition. class C {} fn H(c: C) { // Allowed since C is complete and we have a declaration `impl C as X;` F(C); G(c); } // The above declarations require that `interface Y`, `fn F` (since it is // generic), and `impl C as X` are defined in the same file. interface Y {} fn F(_:! X) {} fn G[U:! X](unused u: U) {} impl C as Y; impl C as X {} impl C as Y {} // --- fail_todo_impl_in_interface_definition.carbon library "[[@TEST_NAME]]"; // This test uses many unsupported features, and is expected to change. interface I { // CHECK:STDERR: fail_todo_impl_in_interface_definition.carbon:[[@LINE+4]]:3: error: semantics TODO: `interface modifier` [SemanticsTodo] // CHECK:STDERR: default fn F() { // CHECK:STDERR: ^~~~~~~ // CHECK:STDERR: default fn F() { class C {} impl C as I; // CHECK:STDERR: fail_todo_impl_in_interface_definition.carbon:[[@LINE+7]]:5: error: missing implementation of F in impl of interface I [ImplMissingFunction] // CHECK:STDERR: impl C as I {} // CHECK:STDERR: ^~~~~~~~~~~~~ // CHECK:STDERR: fail_todo_impl_in_interface_definition.carbon:[[@LINE-7]]:3: note: associated function F declared here [AssociatedFunctionHere] // CHECK:STDERR: default fn F() { // CHECK:STDERR: ^~~~~~~~~~~~~~~~ // CHECK:STDERR: impl C as I {} } } // --- fail_todo_impl_in_interface_definition_with_associated.carbon library "[[@TEST_NAME]]"; // This test uses many unsupported features, and is expected to change. interface I { let U:! type; // CHECK:STDERR: fail_todo_impl_in_interface_definition_with_associated.carbon:[[@LINE+4]]:3: error: semantics TODO: `interface modifier` [SemanticsTodo] // CHECK:STDERR: default fn F() { // CHECK:STDERR: ^~~~~~~ // CHECK:STDERR: default fn F() { class C {} impl C as I where .U = C; // CHECK:STDERR: fail_todo_impl_in_interface_definition_with_associated.carbon:[[@LINE+7]]:5: error: missing implementation of F in impl of interface I [ImplMissingFunction] // CHECK:STDERR: impl C as I where .U = C {} // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: fail_todo_impl_in_interface_definition_with_associated.carbon:[[@LINE-7]]:3: note: associated function F declared here [AssociatedFunctionHere] // CHECK:STDERR: default fn F() { // CHECK:STDERR: ^~~~~~~~~~~~~~~~ // CHECK:STDERR: impl C as I where .U = C {} } } // CHECK:STDOUT: --- empty.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %I.type: type = facet_type <@I> [concrete] // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %empty_struct: %empty_struct_type = struct_value () [concrete] // CHECK:STDOUT: %I.impl_witness: = impl_witness @empty_struct_type.as.I.impl.%I.impl_witness_table [concrete] // CHECK:STDOUT: %Self: %I.type = symbolic_binding Self, 0 [symbolic] // CHECK:STDOUT: %I.facet: %I.type = facet_value %empty_struct_type, (%I.impl_witness) [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .I = %I.decl.loc3 // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %I.decl.loc3: type = interface_decl @I [concrete = constants.%I.type] {} {} // CHECK:STDOUT: impl_decl @empty_struct_type.as.I.impl [concrete] {} { // CHECK:STDOUT: %.loc4_7.1: %empty_struct_type = struct_literal () [concrete = constants.%empty_struct] // CHECK:STDOUT: %.loc4_7.2: type = converted %.loc4_7.1, constants.%empty_struct_type [concrete = constants.%empty_struct_type] // CHECK:STDOUT: %I.ref.loc4: type = name_ref I, file.%I.decl.loc3 [concrete = constants.%I.type] // CHECK:STDOUT: } // CHECK:STDOUT: %I.decl.loc6: type = interface_decl @I [concrete = constants.%I.type] {} {} // CHECK:STDOUT: impl_decl @empty_struct_type.as.I.impl [concrete] {} { // CHECK:STDOUT: %.loc7_7.1: %empty_struct_type = struct_literal () [concrete = constants.%empty_struct] // CHECK:STDOUT: %.loc7_7.2: type = converted %.loc7_7.1, constants.%empty_struct_type [concrete = constants.%empty_struct_type] // CHECK:STDOUT: %I.ref.loc7: type = name_ref I, file.%I.decl.loc3 [concrete = constants.%I.type] // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: interface @I { // CHECK:STDOUT: %Self: %I.type = symbolic_binding Self, 0 [symbolic = constants.%Self] // CHECK:STDOUT: %I.WithSelf.decl = interface_with_self_decl @I [concrete] // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = %Self // CHECK:STDOUT: witness = () // CHECK:STDOUT: // CHECK:STDOUT: !requires: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: impl @empty_struct_type.as.I.impl: %.loc4_7.2 as %I.ref.loc4 { // CHECK:STDOUT: %I.impl_witness_table = impl_witness_table (), @empty_struct_type.as.I.impl [concrete] // CHECK:STDOUT: %I.impl_witness: = impl_witness %I.impl_witness_table [concrete = constants.%I.impl_witness] // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: witness = %I.impl_witness // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @I.WithSelf(constants.%Self) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @I.WithSelf(constants.%I.facet) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- method.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %I.type: type = facet_type <@I> [concrete] // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %empty_struct: %empty_struct_type = struct_value () [concrete] // CHECK:STDOUT: %I.impl_witness: = impl_witness @empty_struct_type.as.I.impl.%I.impl_witness_table [concrete] // CHECK:STDOUT: %Self: %I.type = symbolic_binding Self, 0 [symbolic] // CHECK:STDOUT: %I.WithSelf.G.type.047: type = fn_type @I.WithSelf.G, @I.WithSelf(%Self) [symbolic] // CHECK:STDOUT: %I.WithSelf.G.8c4: %I.WithSelf.G.type.047 = struct_value () [symbolic] // CHECK:STDOUT: %I.assoc_type: type = assoc_entity_type @I [concrete] // CHECK:STDOUT: %assoc0: %I.assoc_type = assoc_entity element0, @I.WithSelf.%I.WithSelf.G.decl [concrete] // CHECK:STDOUT: %empty_struct_type.as.I.impl.G.type: type = fn_type @empty_struct_type.as.I.impl.G [concrete] // CHECK:STDOUT: %empty_struct_type.as.I.impl.G: %empty_struct_type.as.I.impl.G.type = struct_value () [concrete] // CHECK:STDOUT: %I.facet: %I.type = facet_value %empty_struct_type, (%I.impl_witness) [concrete] // CHECK:STDOUT: %I.WithSelf.G.type.ed6: type = fn_type @I.WithSelf.G, @I.WithSelf(%I.facet) [concrete] // CHECK:STDOUT: %I.WithSelf.G.42b: %I.WithSelf.G.type.ed6 = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .I = %I.decl.loc3 // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %I.decl.loc3: type = interface_decl @I [concrete = constants.%I.type] {} {} // CHECK:STDOUT: impl_decl @empty_struct_type.as.I.impl [concrete] {} { // CHECK:STDOUT: %.loc4_7.1: %empty_struct_type = struct_literal () [concrete = constants.%empty_struct] // CHECK:STDOUT: %.loc4_7.2: type = converted %.loc4_7.1, constants.%empty_struct_type [concrete = constants.%empty_struct_type] // CHECK:STDOUT: %I.ref.loc4: type = name_ref I, file.%I.decl.loc3 [concrete = constants.%I.type] // CHECK:STDOUT: } // CHECK:STDOUT: %I.decl.loc6: type = interface_decl @I [concrete = constants.%I.type] {} {} // CHECK:STDOUT: impl_decl @empty_struct_type.as.I.impl [concrete] {} { // CHECK:STDOUT: %.loc9_7.1: %empty_struct_type = struct_literal () [concrete = constants.%empty_struct] // CHECK:STDOUT: %.loc9_7.2: type = converted %.loc9_7.1, constants.%empty_struct_type [concrete = constants.%empty_struct_type] // CHECK:STDOUT: %I.ref.loc9: type = name_ref I, file.%I.decl.loc3 [concrete = constants.%I.type] // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: interface @I { // CHECK:STDOUT: %Self: %I.type = symbolic_binding Self, 0 [symbolic = constants.%Self] // CHECK:STDOUT: %I.WithSelf.decl = interface_with_self_decl @I [concrete] // CHECK:STDOUT: // CHECK:STDOUT: !with Self: // CHECK:STDOUT: %I.WithSelf.G.decl: @I.WithSelf.%I.WithSelf.G.type (%I.WithSelf.G.type.047) = fn_decl @I.WithSelf.G [symbolic = @I.WithSelf.%I.WithSelf.G (constants.%I.WithSelf.G.8c4)] {} {} // CHECK:STDOUT: %assoc0: %I.assoc_type = assoc_entity element0, %I.WithSelf.G.decl [concrete = constants.%assoc0] // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = %Self // CHECK:STDOUT: .G = @I.WithSelf.%assoc0 // CHECK:STDOUT: witness = (@I.WithSelf.%I.WithSelf.G.decl) // CHECK:STDOUT: // CHECK:STDOUT: !requires: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: impl @empty_struct_type.as.I.impl: %.loc4_7.2 as %I.ref.loc4 { // CHECK:STDOUT: %empty_struct_type.as.I.impl.G.decl: %empty_struct_type.as.I.impl.G.type = fn_decl @empty_struct_type.as.I.impl.G [concrete = constants.%empty_struct_type.as.I.impl.G] {} {} // CHECK:STDOUT: %I.impl_witness_table = impl_witness_table (%empty_struct_type.as.I.impl.G.decl), @empty_struct_type.as.I.impl [concrete] // CHECK:STDOUT: %I.impl_witness: = impl_witness %I.impl_witness_table [concrete = constants.%I.impl_witness] // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .G = %empty_struct_type.as.I.impl.G.decl // CHECK:STDOUT: witness = %I.impl_witness // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic fn @I.WithSelf.G(@I.%Self: %I.type) { // CHECK:STDOUT: fn(); // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @empty_struct_type.as.I.impl.G() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @I.WithSelf(constants.%Self) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: %Self => constants.%Self // CHECK:STDOUT: %I.WithSelf.G.type => constants.%I.WithSelf.G.type.047 // CHECK:STDOUT: %I.WithSelf.G => constants.%I.WithSelf.G.8c4 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @I.WithSelf.G(constants.%Self) {} // CHECK:STDOUT: // CHECK:STDOUT: specific @I.WithSelf(constants.%I.facet) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: %Self => constants.%I.facet // CHECK:STDOUT: %I.WithSelf.G.type => constants.%I.WithSelf.G.type.ed6 // CHECK:STDOUT: %I.WithSelf.G => constants.%I.WithSelf.G.42b // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @I.WithSelf.G(constants.%I.facet) {} // CHECK:STDOUT: // CHECK:STDOUT: --- combine.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %I.type: type = facet_type <@I> [concrete] // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %empty_struct: %empty_struct_type = struct_value () [concrete] // CHECK:STDOUT: %BitAndWith.type.f2e: type = generic_interface_type @BitAndWith [concrete] // CHECK:STDOUT: %BitAndWith.generic: %BitAndWith.type.f2e = struct_value () [concrete] // CHECK:STDOUT: %BitAndWith.type.b10: type = facet_type <@BitAndWith, @BitAndWith(type)> [concrete] // CHECK:STDOUT: %BitAndWith.impl_witness: = impl_witness imports.%BitAndWith.impl_witness_table [concrete] // CHECK:STDOUT: %BitAndWith.facet: %BitAndWith.type.b10 = facet_value type, (%BitAndWith.impl_witness) [concrete] // CHECK:STDOUT: %BitAndWith.WithSelf.Op.type.4bd: type = fn_type @BitAndWith.WithSelf.Op, @BitAndWith.WithSelf(type, %BitAndWith.facet) [concrete] // CHECK:STDOUT: %.d15: type = fn_type_with_self_type %BitAndWith.WithSelf.Op.type.4bd, %BitAndWith.facet [concrete] // CHECK:STDOUT: %type.as.BitAndWith.impl.Op.type: type = fn_type @type.as.BitAndWith.impl.Op [concrete] // CHECK:STDOUT: %type.as.BitAndWith.impl.Op: %type.as.BitAndWith.impl.Op.type = struct_value () [concrete] // CHECK:STDOUT: %type.as.BitAndWith.impl.Op.bound: = bound_method %I.type, %type.as.BitAndWith.impl.Op [concrete] // CHECK:STDOUT: %I.impl_witness: = impl_witness @empty_struct_type.as.I.impl.%I.impl_witness_table [concrete] // CHECK:STDOUT: %Self.ab9: %I.type = symbolic_binding Self, 0 [symbolic] // CHECK:STDOUT: %I.facet: %I.type = facet_value %empty_struct_type, (%I.impl_witness) [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: .BitAndWith = %Core.BitAndWith // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: %Core.BitAndWith: %BitAndWith.type.f2e = import_ref Core//prelude/parts/as, BitAndWith, loaded [concrete = constants.%BitAndWith.generic] // CHECK:STDOUT: %Core.import_ref.8d3: %type.as.BitAndWith.impl.Op.type = import_ref Core//prelude/parts/as, loc{{\d+_\d+}}, loaded [concrete = constants.%type.as.BitAndWith.impl.Op] // CHECK:STDOUT: %BitAndWith.impl_witness_table = impl_witness_table (%Core.import_ref.8d3), @type.as.BitAndWith.impl [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .I = %I.decl.loc3 // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %I.decl.loc3: type = interface_decl @I [concrete = constants.%I.type] {} {} // CHECK:STDOUT: %.loc4_14.1: type = value_of_initializer @empty_struct_type.as.I.impl.%type.as.BitAndWith.impl.Op.call.loc4 [concrete = constants.%I.type] // CHECK:STDOUT: %.loc4_14.2: type = converted @empty_struct_type.as.I.impl.%type.as.BitAndWith.impl.Op.call.loc4, %.loc4_14.1 [concrete = constants.%I.type] // CHECK:STDOUT: impl_decl @empty_struct_type.as.I.impl [concrete] {} { // CHECK:STDOUT: %.loc4_7.1: %empty_struct_type = struct_literal () [concrete = constants.%empty_struct] // CHECK:STDOUT: %.loc4_7.2: type = converted %.loc4_7.1, constants.%empty_struct_type [concrete = constants.%empty_struct_type] // CHECK:STDOUT: %I.ref.loc4_12: type = name_ref I, file.%I.decl.loc3 [concrete = constants.%I.type] // CHECK:STDOUT: %I.ref.loc4_16: type = name_ref I, file.%I.decl.loc3 [concrete = constants.%I.type] // CHECK:STDOUT: %impl.elem0.loc4: %.d15 = impl_witness_access constants.%BitAndWith.impl_witness, element0 [concrete = constants.%type.as.BitAndWith.impl.Op] // CHECK:STDOUT: %bound_method.loc4: = bound_method %I.ref.loc4_12, %impl.elem0.loc4 [concrete = constants.%type.as.BitAndWith.impl.Op.bound] // CHECK:STDOUT: %type.as.BitAndWith.impl.Op.call.loc4: init type = call %bound_method.loc4(%I.ref.loc4_12, %I.ref.loc4_16) [concrete = constants.%I.type] // CHECK:STDOUT: } // CHECK:STDOUT: %I.decl.loc6: type = interface_decl @I [concrete = constants.%I.type] {} {} // CHECK:STDOUT: %.loc7_14.1: type = value_of_initializer @empty_struct_type.as.I.impl.%type.as.BitAndWith.impl.Op.call.loc7 [concrete = constants.%I.type] // CHECK:STDOUT: %.loc7_14.2: type = converted @empty_struct_type.as.I.impl.%type.as.BitAndWith.impl.Op.call.loc7, %.loc7_14.1 [concrete = constants.%I.type] // CHECK:STDOUT: impl_decl @empty_struct_type.as.I.impl [concrete] {} { // CHECK:STDOUT: %.loc7_7.1: %empty_struct_type = struct_literal () [concrete = constants.%empty_struct] // CHECK:STDOUT: %.loc7_7.2: type = converted %.loc7_7.1, constants.%empty_struct_type [concrete = constants.%empty_struct_type] // CHECK:STDOUT: %I.ref.loc7_12: type = name_ref I, file.%I.decl.loc3 [concrete = constants.%I.type] // CHECK:STDOUT: %I.ref.loc7_16: type = name_ref I, file.%I.decl.loc3 [concrete = constants.%I.type] // CHECK:STDOUT: %impl.elem0.loc7: %.d15 = impl_witness_access constants.%BitAndWith.impl_witness, element0 [concrete = constants.%type.as.BitAndWith.impl.Op] // CHECK:STDOUT: %bound_method.loc7: = bound_method %I.ref.loc7_12, %impl.elem0.loc7 [concrete = constants.%type.as.BitAndWith.impl.Op.bound] // CHECK:STDOUT: %type.as.BitAndWith.impl.Op.call.loc7: init type = call %bound_method.loc7(%I.ref.loc7_12, %I.ref.loc7_16) [concrete = constants.%I.type] // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: interface @I { // CHECK:STDOUT: %Self: %I.type = symbolic_binding Self, 0 [symbolic = constants.%Self.ab9] // CHECK:STDOUT: %I.WithSelf.decl = interface_with_self_decl @I [concrete] // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = %Self // CHECK:STDOUT: witness = () // CHECK:STDOUT: // CHECK:STDOUT: !requires: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: impl @empty_struct_type.as.I.impl: %.loc4_7.2 as file.%.loc4_14.2 { // CHECK:STDOUT: %I.impl_witness_table = impl_witness_table (), @empty_struct_type.as.I.impl [concrete] // CHECK:STDOUT: %I.impl_witness: = impl_witness %I.impl_witness_table [concrete = constants.%I.impl_witness] // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: witness = %I.impl_witness // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @I.WithSelf(constants.%Self.ab9) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @I.WithSelf(constants.%I.facet) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- associated_const.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %I.type: type = facet_type <@I> [concrete] // CHECK:STDOUT: %Self: %I.type = symbolic_binding Self, 0 [symbolic] // CHECK:STDOUT: %I.assoc_type: type = assoc_entity_type @I [concrete] // CHECK:STDOUT: %assoc0: %I.assoc_type = assoc_entity element0, @I.WithSelf.%T [concrete] // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %empty_struct: %empty_struct_type = struct_value () [concrete] // CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] // CHECK:STDOUT: %.Self: %I.type = symbolic_binding .Self [symbolic_self] // CHECK:STDOUT: %.Self.binding.as_type: type = symbolic_binding_type .Self, %.Self [symbolic_self] // CHECK:STDOUT: %I.lookup_impl_witness: = lookup_impl_witness %.Self, @I [symbolic_self] // CHECK:STDOUT: %impl.elem0: type = impl_witness_access %I.lookup_impl_witness, element0 [symbolic_self] // CHECK:STDOUT: %empty_tuple: %empty_tuple.type = tuple_value () [concrete] // CHECK:STDOUT: %I_where.type: type = facet_type <@I where %impl.elem0 = %empty_tuple.type> [concrete] // CHECK:STDOUT: %I.impl_witness: = impl_witness @empty_struct_type.as.I.impl.%I.impl_witness_table [concrete] // CHECK:STDOUT: %I.facet: %I.type = facet_value %empty_struct_type, (%I.impl_witness) [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .I = %I.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %I.decl: type = interface_decl @I [concrete = constants.%I.type] {} {} // CHECK:STDOUT: impl_decl @empty_struct_type.as.I.impl [concrete] {} { // CHECK:STDOUT: %.loc6_7.1: %empty_struct_type = struct_literal () [concrete = constants.%empty_struct] // CHECK:STDOUT: %.loc6_7.2: type = converted %.loc6_7.1, constants.%empty_struct_type [concrete = constants.%empty_struct_type] // CHECK:STDOUT: %I.ref.loc6: type = name_ref I, file.%I.decl [concrete = constants.%I.type] // CHECK:STDOUT: %.Self.2: %I.type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %.Self.ref.loc6: %I.type = name_ref .Self, %.Self.2 [symbolic_self = constants.%.Self] // CHECK:STDOUT: %.Self.as_type.loc6: type = facet_access_type %.Self.ref.loc6 [symbolic_self = constants.%.Self.binding.as_type] // CHECK:STDOUT: %.loc6_20: type = converted %.Self.ref.loc6, %.Self.as_type.loc6 [symbolic_self = constants.%.Self.binding.as_type] // CHECK:STDOUT: %T.ref.loc6: %I.assoc_type = name_ref T, @T.%assoc0 [concrete = constants.%assoc0] // CHECK:STDOUT: %impl.elem0.loc6: type = impl_witness_access constants.%I.lookup_impl_witness, element0 [symbolic_self = constants.%impl.elem0] // CHECK:STDOUT: %.loc6_26.1: %empty_tuple.type = tuple_literal () [concrete = constants.%empty_tuple] // CHECK:STDOUT: %.loc6_26.2: type = converted %.loc6_26.1, constants.%empty_tuple.type [concrete = constants.%empty_tuple.type] // CHECK:STDOUT: %.loc6_14: type = where_expr %.Self.2 [concrete = constants.%I_where.type] { // CHECK:STDOUT: requirement_base_facet_type constants.%I.type // CHECK:STDOUT: requirement_rewrite %impl.elem0.loc6, %.loc6_26.2 // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: impl_decl @empty_struct_type.as.I.impl [concrete] {} { // CHECK:STDOUT: %.loc8_7.1: %empty_struct_type = struct_literal () [concrete = constants.%empty_struct] // CHECK:STDOUT: %.loc8_7.2: type = converted %.loc8_7.1, constants.%empty_struct_type [concrete = constants.%empty_struct_type] // CHECK:STDOUT: %I.ref.loc8: type = name_ref I, file.%I.decl [concrete = constants.%I.type] // CHECK:STDOUT: %.Self.1: %I.type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %.Self.ref.loc8: %I.type = name_ref .Self, %.Self.1 [symbolic_self = constants.%.Self] // CHECK:STDOUT: %.Self.as_type.loc8: type = facet_access_type %.Self.ref.loc8 [symbolic_self = constants.%.Self.binding.as_type] // CHECK:STDOUT: %.loc8_20: type = converted %.Self.ref.loc8, %.Self.as_type.loc8 [symbolic_self = constants.%.Self.binding.as_type] // CHECK:STDOUT: %T.ref.loc8: %I.assoc_type = name_ref T, @T.%assoc0 [concrete = constants.%assoc0] // CHECK:STDOUT: %impl.elem0.loc8: type = impl_witness_access constants.%I.lookup_impl_witness, element0 [symbolic_self = constants.%impl.elem0] // CHECK:STDOUT: %.loc8_26.1: %empty_tuple.type = tuple_literal () [concrete = constants.%empty_tuple] // CHECK:STDOUT: %.loc8_26.2: type = converted %.loc8_26.1, constants.%empty_tuple.type [concrete = constants.%empty_tuple.type] // CHECK:STDOUT: %.loc8_14: type = where_expr %.Self.1 [concrete = constants.%I_where.type] { // CHECK:STDOUT: requirement_base_facet_type constants.%I.type // CHECK:STDOUT: requirement_rewrite %impl.elem0.loc8, %.loc8_26.2 // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: interface @I { // CHECK:STDOUT: %Self: %I.type = symbolic_binding Self, 0 [symbolic = constants.%Self] // CHECK:STDOUT: %I.WithSelf.decl = interface_with_self_decl @I [concrete] // CHECK:STDOUT: // CHECK:STDOUT: !with Self: // CHECK:STDOUT: %T: type = assoc_const_decl @T [concrete] { // CHECK:STDOUT: %assoc0: %I.assoc_type = assoc_entity element0, @I.WithSelf.%T [concrete = constants.%assoc0] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = %Self // CHECK:STDOUT: .T = @T.%assoc0 // CHECK:STDOUT: witness = (@I.WithSelf.%T) // CHECK:STDOUT: // CHECK:STDOUT: !requires: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: impl @empty_struct_type.as.I.impl: %.loc6_7.2 as %.loc6_14 { // CHECK:STDOUT: %I.impl_witness_table = impl_witness_table (%impl_witness_assoc_constant), @empty_struct_type.as.I.impl [concrete] // CHECK:STDOUT: %I.impl_witness: = impl_witness %I.impl_witness_table [concrete = constants.%I.impl_witness] // CHECK:STDOUT: %impl_witness_assoc_constant: type = impl_witness_assoc_constant constants.%empty_tuple.type [concrete = constants.%empty_tuple.type] // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: witness = %I.impl_witness // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @I.WithSelf(constants.%Self) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @I.WithSelf(constants.%.Self) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @I.WithSelf(constants.%I.facet) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- associated_const_compound_member_access.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %I.type: type = facet_type <@I> [concrete] // CHECK:STDOUT: %Self: %I.type = symbolic_binding Self, 0 [symbolic] // CHECK:STDOUT: %I.assoc_type: type = assoc_entity_type @I [concrete] // CHECK:STDOUT: %assoc0: %I.assoc_type = assoc_entity element0, @I.WithSelf.%T [concrete] // CHECK:STDOUT: %C: type = class_type @C [concrete] // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %complete_type: = complete_type_witness %empty_struct_type [concrete] // CHECK:STDOUT: %.Self: %I.type = symbolic_binding .Self [symbolic_self] // CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] // CHECK:STDOUT: %.Self.binding.as_type: type = symbolic_binding_type .Self, %.Self [symbolic_self] // CHECK:STDOUT: %I.lookup_impl_witness: = lookup_impl_witness %.Self, @I [symbolic_self] // CHECK:STDOUT: %impl.elem0: type = impl_witness_access %I.lookup_impl_witness, element0 [symbolic_self] // CHECK:STDOUT: %empty_tuple: %empty_tuple.type = tuple_value () [concrete] // CHECK:STDOUT: %I_where.type: type = facet_type <@I where %impl.elem0 = %empty_tuple.type> [concrete] // CHECK:STDOUT: %I.impl_witness: = impl_witness @C.as.I.impl.%I.impl_witness_table [concrete] // CHECK:STDOUT: %I.facet: %I.type = facet_value %C, (%I.impl_witness) [concrete] // CHECK:STDOUT: %pattern_type: type = pattern_type %empty_tuple.type [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .I = %I.decl // CHECK:STDOUT: .C = %C.decl // CHECK:STDOUT: .x = %x // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %I.decl: type = interface_decl @I [concrete = constants.%I.type] {} {} // CHECK:STDOUT: %C.decl: type = class_decl @C [concrete = constants.%C] {} {} // CHECK:STDOUT: impl_decl @C.as.I.impl [concrete] {} { // CHECK:STDOUT: %C.ref.loc7: type = name_ref C, file.%C.decl [concrete = constants.%C] // CHECK:STDOUT: %I.ref.loc7: type = name_ref I, file.%I.decl [concrete = constants.%I.type] // CHECK:STDOUT: %.Self.2: %I.type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %.Self.ref.loc7: %I.type = name_ref .Self, %.Self.2 [symbolic_self = constants.%.Self] // CHECK:STDOUT: %.Self.as_type.loc7: type = facet_access_type %.Self.ref.loc7 [symbolic_self = constants.%.Self.binding.as_type] // CHECK:STDOUT: %.loc7_19: type = converted %.Self.ref.loc7, %.Self.as_type.loc7 [symbolic_self = constants.%.Self.binding.as_type] // CHECK:STDOUT: %T.ref.loc7: %I.assoc_type = name_ref T, @T.%assoc0 [concrete = constants.%assoc0] // CHECK:STDOUT: %impl.elem0.loc7: type = impl_witness_access constants.%I.lookup_impl_witness, element0 [symbolic_self = constants.%impl.elem0] // CHECK:STDOUT: %.loc7_25.1: %empty_tuple.type = tuple_literal () [concrete = constants.%empty_tuple] // CHECK:STDOUT: %.loc7_25.2: type = converted %.loc7_25.1, constants.%empty_tuple.type [concrete = constants.%empty_tuple.type] // CHECK:STDOUT: %.loc7_13: type = where_expr %.Self.2 [concrete = constants.%I_where.type] { // CHECK:STDOUT: requirement_base_facet_type constants.%I.type // CHECK:STDOUT: requirement_rewrite %impl.elem0.loc7, %.loc7_25.2 // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %x.patt: %pattern_type = value_binding_pattern x [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %.loc9_9.1: type = splice_block %impl.elem0 [concrete = constants.%empty_tuple.type] { // CHECK:STDOUT: %C.ref: type = name_ref C, %C.decl [concrete = constants.%C] // CHECK:STDOUT: %I.ref: type = name_ref I, %I.decl [concrete = constants.%I.type] // CHECK:STDOUT: %T.ref: %I.assoc_type = name_ref T, @T.%assoc0 [concrete = constants.%assoc0] // CHECK:STDOUT: %I.facet: %I.type = facet_value %C.ref, (constants.%I.impl_witness) [concrete = constants.%I.facet] // CHECK:STDOUT: %.loc9_9.2: %I.type = converted %C.ref, %I.facet [concrete = constants.%I.facet] // CHECK:STDOUT: %impl.elem0: type = impl_witness_access constants.%I.impl_witness, element0 [concrete = constants.%empty_tuple.type] // CHECK:STDOUT: } // CHECK:STDOUT: %empty_tuple: %empty_tuple.type = tuple_value () [concrete = constants.%empty_tuple] // CHECK:STDOUT: %.loc9_19: %empty_tuple.type = converted @__global_init.%.loc9, %empty_tuple [concrete = constants.%empty_tuple] // CHECK:STDOUT: %x: %empty_tuple.type = value_binding x, %.loc9_19 // CHECK:STDOUT: impl_decl @C.as.I.impl [concrete] {} { // CHECK:STDOUT: %C.ref.loc11: type = name_ref C, file.%C.decl [concrete = constants.%C] // CHECK:STDOUT: %I.ref.loc11: type = name_ref I, file.%I.decl [concrete = constants.%I.type] // CHECK:STDOUT: %.Self.1: %I.type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %.Self.ref.loc11: %I.type = name_ref .Self, %.Self.1 [symbolic_self = constants.%.Self] // CHECK:STDOUT: %.Self.as_type.loc11: type = facet_access_type %.Self.ref.loc11 [symbolic_self = constants.%.Self.binding.as_type] // CHECK:STDOUT: %.loc11_19: type = converted %.Self.ref.loc11, %.Self.as_type.loc11 [symbolic_self = constants.%.Self.binding.as_type] // CHECK:STDOUT: %T.ref.loc11: %I.assoc_type = name_ref T, @T.%assoc0 [concrete = constants.%assoc0] // CHECK:STDOUT: %impl.elem0.loc11: type = impl_witness_access constants.%I.lookup_impl_witness, element0 [symbolic_self = constants.%impl.elem0] // CHECK:STDOUT: %.loc11_25.1: %empty_tuple.type = tuple_literal () [concrete = constants.%empty_tuple] // CHECK:STDOUT: %.loc11_25.2: type = converted %.loc11_25.1, constants.%empty_tuple.type [concrete = constants.%empty_tuple.type] // CHECK:STDOUT: %.loc11_13: type = where_expr %.Self.1 [concrete = constants.%I_where.type] { // CHECK:STDOUT: requirement_base_facet_type constants.%I.type // CHECK:STDOUT: requirement_rewrite %impl.elem0.loc11, %.loc11_25.2 // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: interface @I { // CHECK:STDOUT: %Self: %I.type = symbolic_binding Self, 0 [symbolic = constants.%Self] // CHECK:STDOUT: %I.WithSelf.decl = interface_with_self_decl @I [concrete] // CHECK:STDOUT: // CHECK:STDOUT: !with Self: // CHECK:STDOUT: %T: type = assoc_const_decl @T [concrete] { // CHECK:STDOUT: %assoc0: %I.assoc_type = assoc_entity element0, @I.WithSelf.%T [concrete = constants.%assoc0] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = %Self // CHECK:STDOUT: .T = @T.%assoc0 // CHECK:STDOUT: witness = (@I.WithSelf.%T) // CHECK:STDOUT: // CHECK:STDOUT: !requires: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: impl @C.as.I.impl: %C.ref.loc7 as %.loc7_13 { // CHECK:STDOUT: %I.impl_witness_table = impl_witness_table (%impl_witness_assoc_constant), @C.as.I.impl [concrete] // CHECK:STDOUT: %I.impl_witness: = impl_witness %I.impl_witness_table [concrete = constants.%I.impl_witness] // CHECK:STDOUT: %impl_witness_assoc_constant: type = impl_witness_assoc_constant constants.%empty_tuple.type [concrete = constants.%empty_tuple.type] // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: witness = %I.impl_witness // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @C { // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%C // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @__global_init() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %.loc9: %empty_tuple.type = tuple_literal () [concrete = constants.%empty_tuple] // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @I.WithSelf(constants.%Self) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @I.WithSelf(constants.%.Self) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @I.WithSelf(constants.%I.facet) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- associated_const_of_facet.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %I.type: type = facet_type <@I> [concrete] // CHECK:STDOUT: %Self: %I.type = symbolic_binding Self, 0 [symbolic] // CHECK:STDOUT: %I.assoc_type: type = assoc_entity_type @I [concrete] // CHECK:STDOUT: %assoc0: %I.assoc_type = assoc_entity element0, @I.WithSelf.%T [concrete] // CHECK:STDOUT: %C: type = class_type @C [concrete] // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %complete_type: = complete_type_witness %empty_struct_type [concrete] // CHECK:STDOUT: %.Self: %I.type = symbolic_binding .Self [symbolic_self] // CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] // CHECK:STDOUT: %.Self.binding.as_type: type = symbolic_binding_type .Self, %.Self [symbolic_self] // CHECK:STDOUT: %I.lookup_impl_witness: = lookup_impl_witness %.Self, @I [symbolic_self] // CHECK:STDOUT: %impl.elem0: type = impl_witness_access %I.lookup_impl_witness, element0 [symbolic_self] // CHECK:STDOUT: %empty_tuple: %empty_tuple.type = tuple_value () [concrete] // CHECK:STDOUT: %I_where.type: type = facet_type <@I where %impl.elem0 = %empty_tuple.type> [concrete] // CHECK:STDOUT: %I.impl_witness: = impl_witness @C.as.I.impl.%I.impl_witness_table [concrete] // CHECK:STDOUT: %I.facet: %I.type = facet_value %C, (%I.impl_witness) [concrete] // CHECK:STDOUT: %pattern_type: type = pattern_type %empty_tuple.type [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .I = %I.decl // CHECK:STDOUT: .C = %C.decl // CHECK:STDOUT: .x = %x // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %I.decl: type = interface_decl @I [concrete = constants.%I.type] {} {} // CHECK:STDOUT: %C.decl: type = class_decl @C [concrete = constants.%C] {} {} // CHECK:STDOUT: impl_decl @C.as.I.impl [concrete] {} { // CHECK:STDOUT: %C.ref.loc7: type = name_ref C, file.%C.decl [concrete = constants.%C] // CHECK:STDOUT: %I.ref.loc7: type = name_ref I, file.%I.decl [concrete = constants.%I.type] // CHECK:STDOUT: %.Self.2: %I.type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %.Self.ref.loc7: %I.type = name_ref .Self, %.Self.2 [symbolic_self = constants.%.Self] // CHECK:STDOUT: %.Self.as_type.loc7: type = facet_access_type %.Self.ref.loc7 [symbolic_self = constants.%.Self.binding.as_type] // CHECK:STDOUT: %.loc7_19: type = converted %.Self.ref.loc7, %.Self.as_type.loc7 [symbolic_self = constants.%.Self.binding.as_type] // CHECK:STDOUT: %T.ref.loc7: %I.assoc_type = name_ref T, @T.%assoc0 [concrete = constants.%assoc0] // CHECK:STDOUT: %impl.elem0.loc7: type = impl_witness_access constants.%I.lookup_impl_witness, element0 [symbolic_self = constants.%impl.elem0] // CHECK:STDOUT: %.loc7_25.1: %empty_tuple.type = tuple_literal () [concrete = constants.%empty_tuple] // CHECK:STDOUT: %.loc7_25.2: type = converted %.loc7_25.1, constants.%empty_tuple.type [concrete = constants.%empty_tuple.type] // CHECK:STDOUT: %.loc7_13: type = where_expr %.Self.2 [concrete = constants.%I_where.type] { // CHECK:STDOUT: requirement_base_facet_type constants.%I.type // CHECK:STDOUT: requirement_rewrite %impl.elem0.loc7, %.loc7_25.2 // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %x.patt: %pattern_type = value_binding_pattern x [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %.loc9_16.1: type = splice_block %impl.elem0 [concrete = constants.%empty_tuple.type] { // CHECK:STDOUT: %C.ref: type = name_ref C, %C.decl [concrete = constants.%C] // CHECK:STDOUT: %I.ref: type = name_ref I, %I.decl [concrete = constants.%I.type] // CHECK:STDOUT: %I.facet: %I.type = facet_value %C.ref, (constants.%I.impl_witness) [concrete = constants.%I.facet] // CHECK:STDOUT: %.loc9_11: %I.type = converted %C.ref, %I.facet [concrete = constants.%I.facet] // CHECK:STDOUT: %as_type: type = facet_access_type %.loc9_11 [concrete = constants.%C] // CHECK:STDOUT: %.loc9_16.2: type = converted %.loc9_11, %as_type [concrete = constants.%C] // CHECK:STDOUT: %T.ref: %I.assoc_type = name_ref T, @T.%assoc0 [concrete = constants.%assoc0] // CHECK:STDOUT: %impl.elem0: type = impl_witness_access constants.%I.impl_witness, element0 [concrete = constants.%empty_tuple.type] // CHECK:STDOUT: } // CHECK:STDOUT: %empty_tuple: %empty_tuple.type = tuple_value () [concrete = constants.%empty_tuple] // CHECK:STDOUT: %.loc9_22: %empty_tuple.type = converted @__global_init.%.loc9, %empty_tuple [concrete = constants.%empty_tuple] // CHECK:STDOUT: %x: %empty_tuple.type = value_binding x, %.loc9_22 // CHECK:STDOUT: impl_decl @C.as.I.impl [concrete] {} { // CHECK:STDOUT: %C.ref.loc11: type = name_ref C, file.%C.decl [concrete = constants.%C] // CHECK:STDOUT: %I.ref.loc11: type = name_ref I, file.%I.decl [concrete = constants.%I.type] // CHECK:STDOUT: %.Self.1: %I.type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %.Self.ref.loc11: %I.type = name_ref .Self, %.Self.1 [symbolic_self = constants.%.Self] // CHECK:STDOUT: %.Self.as_type.loc11: type = facet_access_type %.Self.ref.loc11 [symbolic_self = constants.%.Self.binding.as_type] // CHECK:STDOUT: %.loc11_19: type = converted %.Self.ref.loc11, %.Self.as_type.loc11 [symbolic_self = constants.%.Self.binding.as_type] // CHECK:STDOUT: %T.ref.loc11: %I.assoc_type = name_ref T, @T.%assoc0 [concrete = constants.%assoc0] // CHECK:STDOUT: %impl.elem0.loc11: type = impl_witness_access constants.%I.lookup_impl_witness, element0 [symbolic_self = constants.%impl.elem0] // CHECK:STDOUT: %.loc11_25.1: %empty_tuple.type = tuple_literal () [concrete = constants.%empty_tuple] // CHECK:STDOUT: %.loc11_25.2: type = converted %.loc11_25.1, constants.%empty_tuple.type [concrete = constants.%empty_tuple.type] // CHECK:STDOUT: %.loc11_13: type = where_expr %.Self.1 [concrete = constants.%I_where.type] { // CHECK:STDOUT: requirement_base_facet_type constants.%I.type // CHECK:STDOUT: requirement_rewrite %impl.elem0.loc11, %.loc11_25.2 // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: interface @I { // CHECK:STDOUT: %Self: %I.type = symbolic_binding Self, 0 [symbolic = constants.%Self] // CHECK:STDOUT: %I.WithSelf.decl = interface_with_self_decl @I [concrete] // CHECK:STDOUT: // CHECK:STDOUT: !with Self: // CHECK:STDOUT: %T: type = assoc_const_decl @T [concrete] { // CHECK:STDOUT: %assoc0: %I.assoc_type = assoc_entity element0, @I.WithSelf.%T [concrete = constants.%assoc0] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = %Self // CHECK:STDOUT: .T = @T.%assoc0 // CHECK:STDOUT: witness = (@I.WithSelf.%T) // CHECK:STDOUT: // CHECK:STDOUT: !requires: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: impl @C.as.I.impl: %C.ref.loc7 as %.loc7_13 { // CHECK:STDOUT: %I.impl_witness_table = impl_witness_table (%impl_witness_assoc_constant), @C.as.I.impl [concrete] // CHECK:STDOUT: %I.impl_witness: = impl_witness %I.impl_witness_table [concrete = constants.%I.impl_witness] // CHECK:STDOUT: %impl_witness_assoc_constant: type = impl_witness_assoc_constant constants.%empty_tuple.type [concrete = constants.%empty_tuple.type] // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: witness = %I.impl_witness // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @C { // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%C // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @__global_init() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %.loc9: %empty_tuple.type = tuple_literal () [concrete = constants.%empty_tuple] // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @I.WithSelf(constants.%Self) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @I.WithSelf(constants.%.Self) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @I.WithSelf(constants.%I.facet) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- fail_unset_associated_const.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %I.type: type = facet_type <@I> [concrete] // CHECK:STDOUT: %Self: %I.type = symbolic_binding Self, 0 [symbolic] // CHECK:STDOUT: %I.assoc_type: type = assoc_entity_type @I [concrete] // CHECK:STDOUT: %assoc0: %I.assoc_type = assoc_entity element0, @I.WithSelf.%T [concrete] // CHECK:STDOUT: %C: type = class_type @C [concrete] // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %complete_type: = complete_type_witness %empty_struct_type [concrete] // CHECK:STDOUT: %I.impl_witness: = impl_witness @C.as.I.impl.%I.impl_witness_table [concrete] // CHECK:STDOUT: %I.facet: %I.type = facet_value %C, (%I.impl_witness) [concrete] // CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] // CHECK:STDOUT: %empty_tuple: %empty_tuple.type = tuple_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .I = %I.decl // CHECK:STDOUT: .C = %C.decl // CHECK:STDOUT: .x = %x // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %I.decl: type = interface_decl @I [concrete = constants.%I.type] {} {} // CHECK:STDOUT: %C.decl: type = class_decl @C [concrete = constants.%C] {} {} // CHECK:STDOUT: impl_decl @C.as.I.impl [concrete] {} { // CHECK:STDOUT: %C.ref.loc7: type = name_ref C, file.%C.decl [concrete = constants.%C] // CHECK:STDOUT: %I.ref.loc7: type = name_ref I, file.%I.decl [concrete = constants.%I.type] // CHECK:STDOUT: } // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %x.patt: = value_binding_pattern x [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %.loc13_16.1: type = splice_block %impl.elem0 [concrete = ] { // CHECK:STDOUT: %C.ref: type = name_ref C, %C.decl [concrete = constants.%C] // CHECK:STDOUT: %I.ref: type = name_ref I, %I.decl [concrete = constants.%I.type] // CHECK:STDOUT: %I.facet: %I.type = facet_value %C.ref, (constants.%I.impl_witness) [concrete = constants.%I.facet] // CHECK:STDOUT: %.loc13_11: %I.type = converted %C.ref, %I.facet [concrete = constants.%I.facet] // CHECK:STDOUT: %as_type: type = facet_access_type %.loc13_11 [concrete = constants.%C] // CHECK:STDOUT: %.loc13_16.2: type = converted %.loc13_11, %as_type [concrete = constants.%C] // CHECK:STDOUT: %T.ref: %I.assoc_type = name_ref T, @T.%assoc0 [concrete = constants.%assoc0] // CHECK:STDOUT: %impl.elem0: type = impl_witness_access constants.%I.impl_witness, element0 [concrete = ] // CHECK:STDOUT: } // CHECK:STDOUT: %x: = value_binding x, [concrete = ] // CHECK:STDOUT: impl_decl @C.as.I.impl [concrete] {} { // CHECK:STDOUT: %C.ref.loc22: type = name_ref C, file.%C.decl [concrete = constants.%C] // CHECK:STDOUT: %I.ref.loc22: type = name_ref I, file.%I.decl [concrete = constants.%I.type] // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: interface @I { // CHECK:STDOUT: %Self: %I.type = symbolic_binding Self, 0 [symbolic = constants.%Self] // CHECK:STDOUT: %I.WithSelf.decl = interface_with_self_decl @I [concrete] // CHECK:STDOUT: // CHECK:STDOUT: !with Self: // CHECK:STDOUT: %T: type = assoc_const_decl @T [concrete] { // CHECK:STDOUT: %assoc0: %I.assoc_type = assoc_entity element0, @I.WithSelf.%T [concrete = constants.%assoc0] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = %Self // CHECK:STDOUT: .T = @T.%assoc0 // CHECK:STDOUT: witness = (@I.WithSelf.%T) // CHECK:STDOUT: // CHECK:STDOUT: !requires: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: impl @C.as.I.impl: %C.ref.loc7 as %I.ref.loc7 { // CHECK:STDOUT: %I.impl_witness_table = impl_witness_table (), @C.as.I.impl [concrete] // CHECK:STDOUT: %I.impl_witness: = impl_witness %I.impl_witness_table [concrete = constants.%I.impl_witness] // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: witness = %I.impl_witness // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @C { // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%C // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @__global_init() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %.loc13: %empty_tuple.type = tuple_literal () [concrete = constants.%empty_tuple] // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @I.WithSelf(constants.%Self) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @I.WithSelf(constants.%I.facet) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- fail_associated_const_before_interface_definition.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %I.type: type = facet_type <@I> [concrete] // CHECK:STDOUT: %C: type = class_type @C [concrete] // CHECK:STDOUT: %D: type = class_type @D [concrete] // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %complete_type: = complete_type_witness %empty_struct_type [concrete] // CHECK:STDOUT: %.Self: %I.type = symbolic_binding .Self [symbolic_self] // CHECK:STDOUT: %.Self.binding.as_type: type = symbolic_binding_type .Self, %.Self [symbolic_self] // CHECK:STDOUT: %Self: %I.type = symbolic_binding Self, 0 [symbolic] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .I = %I.decl.loc3 // CHECK:STDOUT: .C = %C.decl.loc4 // CHECK:STDOUT: .D = %D.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %I.decl.loc3: type = interface_decl @I [concrete = constants.%I.type] {} {} // CHECK:STDOUT: %C.decl.loc4: type = class_decl @C [concrete = constants.%C] {} {} // CHECK:STDOUT: %D.decl: type = class_decl @D [concrete = constants.%D] {} {} // CHECK:STDOUT: impl_decl @D.as..impl [concrete] {} { // CHECK:STDOUT: %D.ref.loc13: type = name_ref D, file.%D.decl [concrete = constants.%D] // CHECK:STDOUT: %I.ref.loc13: type = name_ref I, file.%I.decl.loc3 [concrete = constants.%I.type] // CHECK:STDOUT: %.Self.2: %I.type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %.Self.ref.loc13: %I.type = name_ref .Self, %.Self.2 [symbolic_self = constants.%.Self] // CHECK:STDOUT: %.Self.as_type.loc13: type = facet_access_type %.Self.ref.loc13 [symbolic_self = constants.%.Self.binding.as_type] // CHECK:STDOUT: %.loc13_19: type = converted %.Self.ref.loc13, %.Self.as_type.loc13 [symbolic_self = constants.%.Self.binding.as_type] // CHECK:STDOUT: %T.ref.loc13: = name_ref T, [concrete = ] // CHECK:STDOUT: %C.ref.loc13: type = name_ref C, file.%C.decl.loc4 [concrete = constants.%C] // CHECK:STDOUT: %.loc13_13: type = where_expr %.Self.2 [concrete = ] { // CHECK:STDOUT: requirement_base_facet_type constants.%I.type // CHECK:STDOUT: requirement_rewrite %T.ref.loc13, // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: %I.decl.loc15: type = interface_decl @I [concrete = constants.%I.type] {} {} // CHECK:STDOUT: %C.decl.loc16: type = class_decl @C [concrete = constants.%C] {} {} // CHECK:STDOUT: impl_decl @D.as..impl [concrete] {} { // CHECK:STDOUT: %D.ref.loc21: type = name_ref D, file.%D.decl [concrete = constants.%D] // CHECK:STDOUT: %I.ref.loc21: type = name_ref I, file.%I.decl.loc3 [concrete = constants.%I.type] // CHECK:STDOUT: %.Self.1: %I.type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %.Self.ref.loc21: %I.type = name_ref .Self, %.Self.1 [symbolic_self = constants.%.Self] // CHECK:STDOUT: %.Self.as_type.loc21: type = facet_access_type %.Self.ref.loc21 [symbolic_self = constants.%.Self.binding.as_type] // CHECK:STDOUT: %.loc21_19: type = converted %.Self.ref.loc21, %.Self.as_type.loc21 [symbolic_self = constants.%.Self.binding.as_type] // CHECK:STDOUT: %T.ref.loc21: = name_ref T, [concrete = ] // CHECK:STDOUT: %C.ref.loc21: type = name_ref C, file.%C.decl.loc4 [concrete = constants.%C] // CHECK:STDOUT: %.loc21_13: type = where_expr %.Self.1 [concrete = ] { // CHECK:STDOUT: requirement_base_facet_type constants.%I.type // CHECK:STDOUT: requirement_rewrite %T.ref.loc21, // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: interface @I { // CHECK:STDOUT: %Self: %I.type = symbolic_binding Self, 0 [symbolic = constants.%Self] // CHECK:STDOUT: %I.WithSelf.decl = interface_with_self_decl @I [concrete] // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = %Self // CHECK:STDOUT: .T = // CHECK:STDOUT: witness = () // CHECK:STDOUT: // CHECK:STDOUT: !requires: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: impl @D.as..impl: %D.ref.loc13 as %.loc13_13 { // CHECK:STDOUT: !members: // CHECK:STDOUT: witness = // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @C { // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%C // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @D { // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%D // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @I.WithSelf(constants.%Self) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @I.WithSelf(constants.%.Self) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- associated_const_of_parameterized.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %type: type = facet_type [concrete] // CHECK:STDOUT: %.Self.c39: %type = symbolic_binding .Self [symbolic_self] // CHECK:STDOUT: %pattern_type: type = pattern_type type [concrete] // CHECK:STDOUT: %U: type = symbolic_binding U, 0 [symbolic] // CHECK:STDOUT: %I.type.609: type = generic_interface_type @I [concrete] // CHECK:STDOUT: %I.generic: %I.type.609 = struct_value () [concrete] // CHECK:STDOUT: %I.type.1ab: type = facet_type <@I, @I(%U)> [symbolic] // CHECK:STDOUT: %Self.fdb: %I.type.1ab = symbolic_binding Self, 1 [symbolic] // CHECK:STDOUT: %I.assoc_type.76c: type = assoc_entity_type @I, @I(%U) [symbolic] // CHECK:STDOUT: %assoc0.eea: %I.assoc_type.76c = assoc_entity element0, @I.WithSelf.%T [symbolic] // CHECK:STDOUT: %C: type = class_type @C [concrete] // CHECK:STDOUT: %D: type = class_type @D [concrete] // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %complete_type: = complete_type_witness %empty_struct_type [concrete] // CHECK:STDOUT: %I.type.807: type = facet_type <@I, @I(%C)> [concrete] // CHECK:STDOUT: %.Self.9dc: %I.type.807 = symbolic_binding .Self [symbolic_self] // CHECK:STDOUT: %Self.b60: %I.type.807 = symbolic_binding Self, 1 [symbolic] // CHECK:STDOUT: %I.assoc_type.2e0: type = assoc_entity_type @I, @I(%C) [concrete] // CHECK:STDOUT: %assoc0.501: %I.assoc_type.2e0 = assoc_entity element0, @I.WithSelf.%T [concrete] // CHECK:STDOUT: %.Self.binding.as_type: type = symbolic_binding_type .Self, %.Self.9dc [symbolic_self] // CHECK:STDOUT: %I.lookup_impl_witness: = lookup_impl_witness %.Self.9dc, @I, @I(%C) [symbolic_self] // CHECK:STDOUT: %impl.elem0: type = impl_witness_access %I.lookup_impl_witness, element0 [symbolic_self] // CHECK:STDOUT: %I_where.type: type = facet_type <@I, @I(%C) where %impl.elem0 = %C> [concrete] // CHECK:STDOUT: %I.impl_witness: = impl_witness @D.as.I.impl.%I.impl_witness_table [concrete] // CHECK:STDOUT: %I.facet: %I.type.807 = facet_value %D, (%I.impl_witness) [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .I = %I.decl // CHECK:STDOUT: .C = %C.decl.loc6 // CHECK:STDOUT: .D = %D.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %I.decl: %I.type.609 = interface_decl @I [concrete = constants.%I.generic] { // CHECK:STDOUT: %U.patt: %pattern_type = symbolic_binding_pattern U, 0 [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %.loc3_17.1: type = splice_block %.loc3_17.2 [concrete = type] { // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self = constants.%.Self.c39] // CHECK:STDOUT: %.loc3_17.2: type = type_literal type [concrete = type] // CHECK:STDOUT: } // CHECK:STDOUT: %U.loc3_13.2: type = symbolic_binding U, 0 [symbolic = %U.loc3_13.1 (constants.%U)] // CHECK:STDOUT: } // CHECK:STDOUT: %C.decl.loc6: type = class_decl @C [concrete = constants.%C] {} {} // CHECK:STDOUT: %D.decl: type = class_decl @D [concrete = constants.%D] {} {} // CHECK:STDOUT: impl_decl @D.as.I.impl [concrete] {} { // CHECK:STDOUT: %D.ref.loc8: type = name_ref D, file.%D.decl [concrete = constants.%D] // CHECK:STDOUT: %I.ref.loc8: %I.type.609 = name_ref I, file.%I.decl [concrete = constants.%I.generic] // CHECK:STDOUT: %C.ref.loc8_13: type = name_ref C, file.%C.decl.loc6 [concrete = constants.%C] // CHECK:STDOUT: %I.type.loc8: type = facet_type <@I, @I(constants.%C)> [concrete = constants.%I.type.807] // CHECK:STDOUT: %.Self.2: %I.type.807 = symbolic_binding .Self [symbolic_self = constants.%.Self.9dc] // CHECK:STDOUT: %.Self.ref.loc8: %I.type.807 = name_ref .Self, %.Self.2 [symbolic_self = constants.%.Self.9dc] // CHECK:STDOUT: %.Self.as_type.loc8: type = facet_access_type %.Self.ref.loc8 [symbolic_self = constants.%.Self.binding.as_type] // CHECK:STDOUT: %.loc8_22.1: type = converted %.Self.ref.loc8, %.Self.as_type.loc8 [symbolic_self = constants.%.Self.binding.as_type] // CHECK:STDOUT: %.loc8_22.2: %I.assoc_type.2e0 = specific_constant @T.%assoc0, @I.WithSelf(constants.%C, constants.%.Self.9dc) [concrete = constants.%assoc0.501] // CHECK:STDOUT: %T.ref.loc8: %I.assoc_type.2e0 = name_ref T, %.loc8_22.2 [concrete = constants.%assoc0.501] // CHECK:STDOUT: %impl.elem0.loc8: type = impl_witness_access constants.%I.lookup_impl_witness, element0 [symbolic_self = constants.%impl.elem0] // CHECK:STDOUT: %C.ref.loc8_27: type = name_ref C, file.%C.decl.loc6 [concrete = constants.%C] // CHECK:STDOUT: %.loc8_16: type = where_expr %.Self.2 [concrete = constants.%I_where.type] { // CHECK:STDOUT: requirement_base_facet_type constants.%I.type.807 // CHECK:STDOUT: requirement_rewrite %impl.elem0.loc8, %C.ref.loc8_27 // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: %C.decl.loc10: type = class_decl @C [concrete = constants.%C] {} {} // CHECK:STDOUT: impl_decl @D.as.I.impl [concrete] {} { // CHECK:STDOUT: %D.ref.loc11: type = name_ref D, file.%D.decl [concrete = constants.%D] // CHECK:STDOUT: %I.ref.loc11: %I.type.609 = name_ref I, file.%I.decl [concrete = constants.%I.generic] // CHECK:STDOUT: %C.ref.loc11_13: type = name_ref C, file.%C.decl.loc6 [concrete = constants.%C] // CHECK:STDOUT: %I.type.loc11: type = facet_type <@I, @I(constants.%C)> [concrete = constants.%I.type.807] // CHECK:STDOUT: %.Self.1: %I.type.807 = symbolic_binding .Self [symbolic_self = constants.%.Self.9dc] // CHECK:STDOUT: %.Self.ref.loc11: %I.type.807 = name_ref .Self, %.Self.1 [symbolic_self = constants.%.Self.9dc] // CHECK:STDOUT: %.Self.as_type.loc11: type = facet_access_type %.Self.ref.loc11 [symbolic_self = constants.%.Self.binding.as_type] // CHECK:STDOUT: %.loc11_22.1: type = converted %.Self.ref.loc11, %.Self.as_type.loc11 [symbolic_self = constants.%.Self.binding.as_type] // CHECK:STDOUT: %.loc11_22.2: %I.assoc_type.2e0 = specific_constant @T.%assoc0, @I.WithSelf(constants.%C, constants.%.Self.9dc) [concrete = constants.%assoc0.501] // CHECK:STDOUT: %T.ref.loc11: %I.assoc_type.2e0 = name_ref T, %.loc11_22.2 [concrete = constants.%assoc0.501] // CHECK:STDOUT: %impl.elem0.loc11: type = impl_witness_access constants.%I.lookup_impl_witness, element0 [symbolic_self = constants.%impl.elem0] // CHECK:STDOUT: %C.ref.loc11_27: type = name_ref C, file.%C.decl.loc6 [concrete = constants.%C] // CHECK:STDOUT: %.loc11_16: type = where_expr %.Self.1 [concrete = constants.%I_where.type] { // CHECK:STDOUT: requirement_base_facet_type constants.%I.type.807 // CHECK:STDOUT: requirement_rewrite %impl.elem0.loc11, %C.ref.loc11_27 // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic interface @I(%U.loc3_13.2: type) { // CHECK:STDOUT: %U.loc3_13.1: type = symbolic_binding U, 0 [symbolic = %U.loc3_13.1 (constants.%U)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %I.type: type = facet_type <@I, @I(%U.loc3_13.1)> [symbolic = %I.type (constants.%I.type.1ab)] // CHECK:STDOUT: %Self.loc3_23.2: @I.%I.type (%I.type.1ab) = symbolic_binding Self, 1 [symbolic = %Self.loc3_23.2 (constants.%Self.fdb)] // CHECK:STDOUT: // CHECK:STDOUT: interface { // CHECK:STDOUT: %Self.loc3_23.1: @I.%I.type (%I.type.1ab) = symbolic_binding Self, 1 [symbolic = %Self.loc3_23.2 (constants.%Self.fdb)] // CHECK:STDOUT: %I.WithSelf.decl = interface_with_self_decl @I [concrete] // CHECK:STDOUT: // CHECK:STDOUT: !with Self: // CHECK:STDOUT: %T: type = assoc_const_decl @T [concrete] { // CHECK:STDOUT: %assoc0: @I.WithSelf.%I.assoc_type (%I.assoc_type.76c) = assoc_entity element0, @I.WithSelf.%T [symbolic = @I.WithSelf.%assoc0 (constants.%assoc0.eea)] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = %Self.loc3_23.1 // CHECK:STDOUT: .T = @T.%assoc0 // CHECK:STDOUT: witness = (@I.WithSelf.%T) // CHECK:STDOUT: // CHECK:STDOUT: !requires: // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: impl @D.as.I.impl: %D.ref.loc8 as %.loc8_16 { // CHECK:STDOUT: %I.impl_witness_table = impl_witness_table (%impl_witness_assoc_constant), @D.as.I.impl [concrete] // CHECK:STDOUT: %I.impl_witness: = impl_witness %I.impl_witness_table [concrete = constants.%I.impl_witness] // CHECK:STDOUT: %impl_witness_assoc_constant: type = impl_witness_assoc_constant constants.%C [concrete = constants.%C] // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: witness = %I.impl_witness // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @C { // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%C // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @D { // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%D // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @I(constants.%U) { // CHECK:STDOUT: %U.loc3_13.1 => constants.%U // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @I.WithSelf(constants.%U, constants.%Self.fdb) {} // CHECK:STDOUT: // CHECK:STDOUT: specific @I(constants.%C) { // CHECK:STDOUT: %U.loc3_13.1 => constants.%C // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %I.type => constants.%I.type.807 // CHECK:STDOUT: %Self.loc3_23.2 => constants.%Self.b60 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @I.WithSelf(constants.%C, constants.%Self.fdb) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: %U => constants.%C // CHECK:STDOUT: %I.assoc_type => constants.%I.assoc_type.2e0 // CHECK:STDOUT: %assoc0 => constants.%assoc0.501 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @I.WithSelf(constants.%C, constants.%.Self.9dc) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: %U => constants.%C // CHECK:STDOUT: %I.assoc_type => constants.%I.assoc_type.2e0 // CHECK:STDOUT: %assoc0 => constants.%assoc0.501 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @I.WithSelf(constants.%C, constants.%I.facet) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: %U => constants.%C // CHECK:STDOUT: %I.assoc_type => constants.%I.assoc_type.2e0 // CHECK:STDOUT: %assoc0 => constants.%assoc0.501 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- find_incomplete_impl.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %I.type: type = facet_type <@I> [concrete] // CHECK:STDOUT: %D: type = class_type @D [concrete] // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %complete_type: = complete_type_witness %empty_struct_type [concrete] // CHECK:STDOUT: %I.impl_witness: = impl_witness @D.as.I.impl.%I.impl_witness_table [concrete] // CHECK:STDOUT: %type: type = facet_type [concrete] // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self] // CHECK:STDOUT: %pattern_type.9d9: type = pattern_type %I.type [concrete] // CHECK:STDOUT: %T: %I.type = symbolic_binding T, 0 [symbolic] // CHECK:STDOUT: %C.type: type = generic_class_type @C [concrete] // CHECK:STDOUT: %C.generic: %C.type = struct_value () [concrete] // CHECK:STDOUT: %C.bba: type = class_type @C, @C(%T) [symbolic] // CHECK:STDOUT: %I.facet: %I.type = facet_value %D, (%I.impl_witness) [concrete] // CHECK:STDOUT: %C.229: type = class_type @C, @C(%I.facet) [concrete] // CHECK:STDOUT: %pattern_type.b40: type = pattern_type %C.229 [concrete] // CHECK:STDOUT: %F.type: type = fn_type @F [concrete] // CHECK:STDOUT: %F: %F.type = struct_value () [concrete] // CHECK:STDOUT: %Self: %I.type = symbolic_binding Self, 0 [symbolic] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .I = %I.decl.loc3 // CHECK:STDOUT: .D = %D.decl // CHECK:STDOUT: .C = %C.decl.loc6 // CHECK:STDOUT: .F = %F.decl.loc8 // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %I.decl.loc3: type = interface_decl @I [concrete = constants.%I.type] {} {} // CHECK:STDOUT: %D.decl: type = class_decl @D [concrete = constants.%D] {} {} // CHECK:STDOUT: impl_decl @D.as.I.impl [concrete] {} { // CHECK:STDOUT: %D.ref.loc5: type = name_ref D, file.%D.decl [concrete = constants.%D] // CHECK:STDOUT: %I.ref.loc5: type = name_ref I, file.%I.decl.loc3 [concrete = constants.%I.type] // CHECK:STDOUT: } // CHECK:STDOUT: %C.decl.loc6: %C.type = class_decl @C [concrete = constants.%C.generic] { // CHECK:STDOUT: %T.patt: %pattern_type.9d9 = symbolic_binding_pattern T, 0 [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %.loc6: type = splice_block %I.ref.loc6 [concrete = constants.%I.type] { // CHECK:STDOUT: %.Self.2: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %I.ref.loc6: type = name_ref I, file.%I.decl.loc3 [concrete = constants.%I.type] // CHECK:STDOUT: } // CHECK:STDOUT: %T.loc6_9.2: %I.type = symbolic_binding T, 0 [symbolic = %T.loc6_9.1 (constants.%T)] // CHECK:STDOUT: } // CHECK:STDOUT: %F.decl.loc8: %F.type = fn_decl @F [concrete = constants.%F] { // CHECK:STDOUT: %x.patt: %pattern_type.b40 = value_binding_pattern x [concrete] // CHECK:STDOUT: %x.param_patt: %pattern_type.b40 = value_param_pattern %x.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %x.param.loc8: %C.229 = value_param call_param0 // CHECK:STDOUT: %.loc8_12.1: type = splice_block %C.loc8 [concrete = constants.%C.229] { // CHECK:STDOUT: %C.ref.loc8: %C.type = name_ref C, file.%C.decl.loc6 [concrete = constants.%C.generic] // CHECK:STDOUT: %D.ref.loc8: type = name_ref D, file.%D.decl [concrete = constants.%D] // CHECK:STDOUT: %I.facet.loc8: %I.type = facet_value %D.ref.loc8, (constants.%I.impl_witness) [concrete = constants.%I.facet] // CHECK:STDOUT: %.loc8_12.2: %I.type = converted %D.ref.loc8, %I.facet.loc8 [concrete = constants.%I.facet] // CHECK:STDOUT: %C.loc8: type = class_type @C, @C(constants.%I.facet) [concrete = constants.%C.229] // CHECK:STDOUT: } // CHECK:STDOUT: %x.loc8: %C.229 = value_binding x, %x.param.loc8 // CHECK:STDOUT: } // CHECK:STDOUT: %I.decl.loc10: type = interface_decl @I [concrete = constants.%I.type] {} {} // CHECK:STDOUT: impl_decl @D.as.I.impl [concrete] {} { // CHECK:STDOUT: %D.ref.loc11: type = name_ref D, file.%D.decl [concrete = constants.%D] // CHECK:STDOUT: %I.ref.loc11: type = name_ref I, file.%I.decl.loc3 [concrete = constants.%I.type] // CHECK:STDOUT: } // CHECK:STDOUT: %C.decl.loc12: %C.type = class_decl @C [concrete = constants.%C.generic] { // CHECK:STDOUT: %T.patt: %pattern_type.9d9 = symbolic_binding_pattern T, 0 [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %.loc12: type = splice_block %I.ref.loc12 [concrete = constants.%I.type] { // CHECK:STDOUT: %.Self.1: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %I.ref.loc12: type = name_ref I, file.%I.decl.loc3 [concrete = constants.%I.type] // CHECK:STDOUT: } // CHECK:STDOUT: %T.loc12: %I.type = symbolic_binding T, 0 [symbolic = %T.loc6_9.1 (constants.%T)] // CHECK:STDOUT: } // CHECK:STDOUT: %F.decl.loc14: %F.type = fn_decl @F [concrete = constants.%F] { // CHECK:STDOUT: %x.patt: %pattern_type.b40 = value_binding_pattern x [concrete] // CHECK:STDOUT: %x.param_patt: %pattern_type.b40 = value_param_pattern %x.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %x.param.loc14: %C.229 = value_param call_param0 // CHECK:STDOUT: %.loc14_19.1: type = splice_block %C.loc14 [concrete = constants.%C.229] { // CHECK:STDOUT: %C.ref.loc14: %C.type = name_ref C, file.%C.decl.loc6 [concrete = constants.%C.generic] // CHECK:STDOUT: %D.ref.loc14: type = name_ref D, file.%D.decl [concrete = constants.%D] // CHECK:STDOUT: %I.facet.loc14: %I.type = facet_value %D.ref.loc14, (constants.%I.impl_witness) [concrete = constants.%I.facet] // CHECK:STDOUT: %.loc14_19.2: %I.type = converted %D.ref.loc14, %I.facet.loc14 [concrete = constants.%I.facet] // CHECK:STDOUT: %C.loc14: type = class_type @C, @C(constants.%I.facet) [concrete = constants.%C.229] // CHECK:STDOUT: } // CHECK:STDOUT: %x.loc14: %C.229 = value_binding x, %x.param.loc14 // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: interface @I { // CHECK:STDOUT: %Self: %I.type = symbolic_binding Self, 0 [symbolic = constants.%Self] // CHECK:STDOUT: %I.WithSelf.decl = interface_with_self_decl @I [concrete] // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = %Self // CHECK:STDOUT: witness = () // CHECK:STDOUT: // CHECK:STDOUT: !requires: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: impl @D.as.I.impl: %D.ref.loc5 as %I.ref.loc5 { // CHECK:STDOUT: %I.impl_witness_table = impl_witness_table (), @D.as.I.impl [concrete] // CHECK:STDOUT: %I.impl_witness: = impl_witness %I.impl_witness_table [concrete = constants.%I.impl_witness] // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: witness = %I.impl_witness // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @D { // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%D // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic class @C(%T.loc6_9.2: %I.type) { // CHECK:STDOUT: %T.loc6_9.1: %I.type = symbolic_binding T, 0 [symbolic = %T.loc6_9.1 (constants.%T)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: // CHECK:STDOUT: class { // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%C.bba // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @F(%x.param.loc14: %C.229) { // CHECK:STDOUT: !entry: // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @C(constants.%T) { // CHECK:STDOUT: %T.loc6_9.1 => constants.%T // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @C(constants.%I.facet) { // CHECK:STDOUT: %T.loc6_9.1 => constants.%I.facet // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @I.WithSelf(constants.%Self) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @I.WithSelf(constants.%I.facet) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- fail_todo_two_interfaces.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %I.type: type = facet_type <@I> [concrete] // CHECK:STDOUT: %J.type: type = facet_type <@J> [concrete] // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %empty_struct: %empty_struct_type = struct_value () [concrete] // CHECK:STDOUT: %BitAndWith.type.f2e: type = generic_interface_type @BitAndWith [concrete] // CHECK:STDOUT: %BitAndWith.generic: %BitAndWith.type.f2e = struct_value () [concrete] // CHECK:STDOUT: %BitAndWith.type.b10: type = facet_type <@BitAndWith, @BitAndWith(type)> [concrete] // CHECK:STDOUT: %BitAndWith.impl_witness: = impl_witness imports.%BitAndWith.impl_witness_table [concrete] // CHECK:STDOUT: %BitAndWith.facet: %BitAndWith.type.b10 = facet_value type, (%BitAndWith.impl_witness) [concrete] // CHECK:STDOUT: %BitAndWith.WithSelf.Op.type.4bd: type = fn_type @BitAndWith.WithSelf.Op, @BitAndWith.WithSelf(type, %BitAndWith.facet) [concrete] // CHECK:STDOUT: %.d15: type = fn_type_with_self_type %BitAndWith.WithSelf.Op.type.4bd, %BitAndWith.facet [concrete] // CHECK:STDOUT: %type.as.BitAndWith.impl.Op.type: type = fn_type @type.as.BitAndWith.impl.Op [concrete] // CHECK:STDOUT: %type.as.BitAndWith.impl.Op: %type.as.BitAndWith.impl.Op.type = struct_value () [concrete] // CHECK:STDOUT: %type.as.BitAndWith.impl.Op.bound: = bound_method %I.type, %type.as.BitAndWith.impl.Op [concrete] // CHECK:STDOUT: %facet_type: type = facet_type <@I & @J> [concrete] // CHECK:STDOUT: %Self.ab9: %I.type = symbolic_binding Self, 0 [symbolic] // CHECK:STDOUT: %Self.8a1: %J.type = symbolic_binding Self, 0 [symbolic] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: .BitAndWith = %Core.BitAndWith // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: %Core.BitAndWith: %BitAndWith.type.f2e = import_ref Core//prelude/parts/as, BitAndWith, loaded [concrete = constants.%BitAndWith.generic] // CHECK:STDOUT: %Core.import_ref.8d3: %type.as.BitAndWith.impl.Op.type = import_ref Core//prelude/parts/as, loc{{\d+_\d+}}, loaded [concrete = constants.%type.as.BitAndWith.impl.Op] // CHECK:STDOUT: %BitAndWith.impl_witness_table = impl_witness_table (%Core.import_ref.8d3), @type.as.BitAndWith.impl [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .I = %I.decl.loc3 // CHECK:STDOUT: .J = %J.decl.loc4 // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %I.decl.loc3: type = interface_decl @I [concrete = constants.%I.type] {} {} // CHECK:STDOUT: %J.decl.loc4: type = interface_decl @J [concrete = constants.%J.type] {} {} // CHECK:STDOUT: %.loc10_14.1: type = value_of_initializer @empty_struct_type.as..impl.%type.as.BitAndWith.impl.Op.call [concrete = constants.%facet_type] // CHECK:STDOUT: %.loc10_14.2: type = converted @empty_struct_type.as..impl.%type.as.BitAndWith.impl.Op.call, %.loc10_14.1 [concrete = constants.%facet_type] // CHECK:STDOUT: impl_decl @empty_struct_type.as..impl [concrete] {} { // CHECK:STDOUT: %.loc10_7.1: %empty_struct_type = struct_literal () [concrete = constants.%empty_struct] // CHECK:STDOUT: %.loc10_7.2: type = converted %.loc10_7.1, constants.%empty_struct_type [concrete = constants.%empty_struct_type] // CHECK:STDOUT: %I.ref: type = name_ref I, file.%I.decl.loc3 [concrete = constants.%I.type] // CHECK:STDOUT: %J.ref: type = name_ref J, file.%J.decl.loc4 [concrete = constants.%J.type] // CHECK:STDOUT: %impl.elem0: %.d15 = impl_witness_access constants.%BitAndWith.impl_witness, element0 [concrete = constants.%type.as.BitAndWith.impl.Op] // CHECK:STDOUT: %bound_method: = bound_method %I.ref, %impl.elem0 [concrete = constants.%type.as.BitAndWith.impl.Op.bound] // CHECK:STDOUT: %type.as.BitAndWith.impl.Op.call: init type = call %bound_method(%I.ref, %J.ref) [concrete = constants.%facet_type] // CHECK:STDOUT: } // CHECK:STDOUT: %I.decl.loc12: type = interface_decl @I [concrete = constants.%I.type] {} {} // CHECK:STDOUT: %J.decl.loc13: type = interface_decl @J [concrete = constants.%J.type] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: interface @I { // CHECK:STDOUT: %Self: %I.type = symbolic_binding Self, 0 [symbolic = constants.%Self.ab9] // CHECK:STDOUT: %I.WithSelf.decl = interface_with_self_decl @I [concrete] // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = %Self // CHECK:STDOUT: witness = () // CHECK:STDOUT: // CHECK:STDOUT: !requires: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: interface @J { // CHECK:STDOUT: %Self: %J.type = symbolic_binding Self, 0 [symbolic = constants.%Self.8a1] // CHECK:STDOUT: %J.WithSelf.decl = interface_with_self_decl @J [concrete] // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = %Self // CHECK:STDOUT: witness = () // CHECK:STDOUT: // CHECK:STDOUT: !requires: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: impl @empty_struct_type.as..impl: %.loc10_7.2 as file.%.loc10_14.2; // CHECK:STDOUT: // CHECK:STDOUT: specific @I.WithSelf(constants.%Self.ab9) {} // CHECK:STDOUT: // CHECK:STDOUT: specific @J.WithSelf(constants.%Self.8a1) {} // CHECK:STDOUT: // CHECK:STDOUT: --- fail_never_assigned_associated_const.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %I.type: type = facet_type <@I> [concrete] // CHECK:STDOUT: %Self: %I.type = symbolic_binding Self, 0 [symbolic] // CHECK:STDOUT: %I.assoc_type: type = assoc_entity_type @I [concrete] // CHECK:STDOUT: %assoc0: %I.assoc_type = assoc_entity element0, @I.WithSelf.%T [concrete] // CHECK:STDOUT: %assoc1: %I.assoc_type = assoc_entity element1, @I.WithSelf.%U [concrete] // CHECK:STDOUT: %C: type = class_type @C [concrete] // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %complete_type: = complete_type_witness %empty_struct_type [concrete] // CHECK:STDOUT: %.Self: %I.type = symbolic_binding .Self [symbolic_self] // CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] // CHECK:STDOUT: %.Self.binding.as_type: type = symbolic_binding_type .Self, %.Self [symbolic_self] // CHECK:STDOUT: %I.lookup_impl_witness: = lookup_impl_witness %.Self, @I [symbolic_self] // CHECK:STDOUT: %impl.elem0: type = impl_witness_access %I.lookup_impl_witness, element0 [symbolic_self] // CHECK:STDOUT: %empty_tuple: %empty_tuple.type = tuple_value () [concrete] // CHECK:STDOUT: %I_where.type: type = facet_type <@I where %impl.elem0 = %empty_tuple.type> [concrete] // CHECK:STDOUT: %I.impl_witness: = impl_witness @C.as.I.impl.%I.impl_witness_table [concrete] // CHECK:STDOUT: %I.facet: %I.type = facet_value %C, (%I.impl_witness) [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .I = %I.decl // CHECK:STDOUT: .C = %C.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %I.decl: type = interface_decl @I [concrete = constants.%I.type] {} {} // CHECK:STDOUT: %C.decl: type = class_decl @C [concrete = constants.%C] {} {} // CHECK:STDOUT: impl_decl @C.as.I.impl [concrete] {} { // CHECK:STDOUT: %C.ref.loc9: type = name_ref C, file.%C.decl [concrete = constants.%C] // CHECK:STDOUT: %I.ref.loc9: type = name_ref I, file.%I.decl [concrete = constants.%I.type] // CHECK:STDOUT: %.Self.2: %I.type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %.Self.ref.loc9: %I.type = name_ref .Self, %.Self.2 [symbolic_self = constants.%.Self] // CHECK:STDOUT: %.Self.as_type.loc9: type = facet_access_type %.Self.ref.loc9 [symbolic_self = constants.%.Self.binding.as_type] // CHECK:STDOUT: %.loc9_19: type = converted %.Self.ref.loc9, %.Self.as_type.loc9 [symbolic_self = constants.%.Self.binding.as_type] // CHECK:STDOUT: %T.ref.loc9: %I.assoc_type = name_ref T, @T.%assoc0 [concrete = constants.%assoc0] // CHECK:STDOUT: %impl.elem0.loc9: type = impl_witness_access constants.%I.lookup_impl_witness, element0 [symbolic_self = constants.%impl.elem0] // CHECK:STDOUT: %.loc9_25.1: %empty_tuple.type = tuple_literal () [concrete = constants.%empty_tuple] // CHECK:STDOUT: %.loc9_25.2: type = converted %.loc9_25.1, constants.%empty_tuple.type [concrete = constants.%empty_tuple.type] // CHECK:STDOUT: %.loc9_13: type = where_expr %.Self.2 [concrete = constants.%I_where.type] { // CHECK:STDOUT: requirement_base_facet_type constants.%I.type // CHECK:STDOUT: requirement_rewrite %impl.elem0.loc9, %.loc9_25.2 // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: impl_decl @C.as.I.impl [concrete] {} { // CHECK:STDOUT: %C.ref.loc18: type = name_ref C, file.%C.decl [concrete = constants.%C] // CHECK:STDOUT: %I.ref.loc18: type = name_ref I, file.%I.decl [concrete = constants.%I.type] // CHECK:STDOUT: %.Self.1: %I.type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %.Self.ref.loc18: %I.type = name_ref .Self, %.Self.1 [symbolic_self = constants.%.Self] // CHECK:STDOUT: %.Self.as_type.loc18: type = facet_access_type %.Self.ref.loc18 [symbolic_self = constants.%.Self.binding.as_type] // CHECK:STDOUT: %.loc18_19: type = converted %.Self.ref.loc18, %.Self.as_type.loc18 [symbolic_self = constants.%.Self.binding.as_type] // CHECK:STDOUT: %T.ref.loc18: %I.assoc_type = name_ref T, @T.%assoc0 [concrete = constants.%assoc0] // CHECK:STDOUT: %impl.elem0.loc18: type = impl_witness_access constants.%I.lookup_impl_witness, element0 [symbolic_self = constants.%impl.elem0] // CHECK:STDOUT: %.loc18_25.1: %empty_tuple.type = tuple_literal () [concrete = constants.%empty_tuple] // CHECK:STDOUT: %.loc18_25.2: type = converted %.loc18_25.1, constants.%empty_tuple.type [concrete = constants.%empty_tuple.type] // CHECK:STDOUT: %.loc18_13: type = where_expr %.Self.1 [concrete = constants.%I_where.type] { // CHECK:STDOUT: requirement_base_facet_type constants.%I.type // CHECK:STDOUT: requirement_rewrite %impl.elem0.loc18, %.loc18_25.2 // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: interface @I { // CHECK:STDOUT: %Self: %I.type = symbolic_binding Self, 0 [symbolic = constants.%Self] // CHECK:STDOUT: %I.WithSelf.decl = interface_with_self_decl @I [concrete] // CHECK:STDOUT: // CHECK:STDOUT: !with Self: // CHECK:STDOUT: %T: type = assoc_const_decl @T [concrete] { // CHECK:STDOUT: %assoc0: %I.assoc_type = assoc_entity element0, @I.WithSelf.%T [concrete = constants.%assoc0] // CHECK:STDOUT: } // CHECK:STDOUT: %U: type = assoc_const_decl @U [concrete] { // CHECK:STDOUT: %assoc1: %I.assoc_type = assoc_entity element1, @I.WithSelf.%U [concrete = constants.%assoc1] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = %Self // CHECK:STDOUT: .T = @T.%assoc0 // CHECK:STDOUT: .U = @U.%assoc1 // CHECK:STDOUT: witness = (@I.WithSelf.%T, @I.WithSelf.%U) // CHECK:STDOUT: // CHECK:STDOUT: !requires: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: impl @C.as.I.impl: %C.ref.loc9 as %.loc9_13 { // CHECK:STDOUT: %I.impl_witness_table = impl_witness_table (%impl_witness_assoc_constant, ), @C.as.I.impl [concrete] // CHECK:STDOUT: %I.impl_witness: = impl_witness %I.impl_witness_table [concrete = constants.%I.impl_witness] // CHECK:STDOUT: %impl_witness_assoc_constant: type = impl_witness_assoc_constant constants.%empty_tuple.type [concrete = constants.%empty_tuple.type] // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: witness = %I.impl_witness // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @C { // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%C // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @I.WithSelf(constants.%Self) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @I.WithSelf(constants.%.Self) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @I.WithSelf(constants.%I.facet) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- example_from_proposal_5168.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %X.type: type = facet_type <@X> [concrete] // CHECK:STDOUT: %type: type = facet_type [concrete] // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self] // CHECK:STDOUT: %pattern_type.b4b: type = pattern_type %X.type [concrete] // CHECK:STDOUT: %_: %X.type = symbolic_binding _, 0 [symbolic] // CHECK:STDOUT: %F.type: type = fn_type @F [concrete] // CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] // CHECK:STDOUT: %F: %F.type = struct_value () [concrete] // CHECK:STDOUT: %U: %X.type = symbolic_binding U, 0 [symbolic] // CHECK:STDOUT: %U.binding.as_type: type = symbolic_binding_type U, 0, %U [symbolic] // CHECK:STDOUT: %pattern_type.b36: type = pattern_type %U.binding.as_type [symbolic] // CHECK:STDOUT: %G.type: type = fn_type @G [concrete] // CHECK:STDOUT: %G: %G.type = struct_value () [concrete] // CHECK:STDOUT: %C: type = class_type @C [concrete] // CHECK:STDOUT: %X.impl_witness: = impl_witness @C.as.X.impl.%X.impl_witness_table [concrete] // CHECK:STDOUT: %Y.type: type = facet_type <@Y> [concrete] // CHECK:STDOUT: %Self.e52: %X.type = symbolic_binding Self, 0 [symbolic] // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %complete_type: = complete_type_witness %empty_struct_type [concrete] // CHECK:STDOUT: %pattern_type.7c7: type = pattern_type %C [concrete] // CHECK:STDOUT: %H.type: type = fn_type @H [concrete] // CHECK:STDOUT: %H: %H.type = struct_value () [concrete] // CHECK:STDOUT: %X.facet: %X.type = facet_value %C, (%X.impl_witness) [concrete] // CHECK:STDOUT: %F.specific_fn: = specific_function %F, @F(%X.facet) [concrete] // CHECK:STDOUT: %G.specific_fn: = specific_function %G, @G(%X.facet) [concrete] // CHECK:STDOUT: %Self.162: %Y.type = symbolic_binding Self, 0 [symbolic] // CHECK:STDOUT: %require_complete: = require_complete_type %U.binding.as_type [symbolic] // CHECK:STDOUT: %Y.impl_witness: = impl_witness @C.as.Y.impl.%Y.impl_witness_table [concrete] // CHECK:STDOUT: %Y.facet: %Y.type = facet_value %C, (%Y.impl_witness) [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .X = %X.decl.loc3 // CHECK:STDOUT: .F = %F.decl.loc6 // CHECK:STDOUT: .G = %G.decl.loc7 // CHECK:STDOUT: .C = %C.decl.loc9 // CHECK:STDOUT: .Y = %Y.decl.loc14 // CHECK:STDOUT: .H = %H.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %X.decl.loc3: type = interface_decl @X [concrete = constants.%X.type] {} {} // CHECK:STDOUT: %F.decl.loc6: %F.type = fn_decl @F [concrete = constants.%F] { // CHECK:STDOUT: %_.patt: %pattern_type.b4b = symbolic_binding_pattern _, 0 [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %.loc6: type = splice_block %X.ref.loc6 [concrete = constants.%X.type] { // CHECK:STDOUT: %.Self.2: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %X.ref.loc6: type = name_ref X, file.%X.decl.loc3 [concrete = constants.%X.type] // CHECK:STDOUT: } // CHECK:STDOUT: %_.loc6_6.2: %X.type = symbolic_binding _, 0 [symbolic = %_.loc6_6.1 (constants.%_)] // CHECK:STDOUT: } // CHECK:STDOUT: %G.decl.loc7: %G.type = fn_decl @G [concrete = constants.%G] { // CHECK:STDOUT: %U.patt: %pattern_type.b4b = symbolic_binding_pattern U, 0 [concrete] // CHECK:STDOUT: %u.patt: @G.%pattern_type (%pattern_type.b36) = value_binding_pattern u [concrete] // CHECK:STDOUT: %u.param_patt: @G.%pattern_type (%pattern_type.b36) = value_param_pattern %u.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %.loc7_10: type = splice_block %X.ref.loc7 [concrete = constants.%X.type] { // CHECK:STDOUT: %.Self.2: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %X.ref.loc7: type = name_ref X, file.%X.decl.loc3 [concrete = constants.%X.type] // CHECK:STDOUT: } // CHECK:STDOUT: %U.loc7_6.2: %X.type = symbolic_binding U, 0 [symbolic = %U.loc7_6.1 (constants.%U)] // CHECK:STDOUT: %u.param.loc7: @G.%U.binding.as_type (%U.binding.as_type) = value_param call_param0 // CHECK:STDOUT: %.loc7_16.1: type = splice_block %.loc7_16.2 [symbolic = %U.binding.as_type (constants.%U.binding.as_type)] { // CHECK:STDOUT: %U.ref.loc7: %X.type = name_ref U, %U.loc7_6.2 [symbolic = %U.loc7_6.1 (constants.%U)] // CHECK:STDOUT: %U.as_type.loc7: type = facet_access_type %U.ref.loc7 [symbolic = %U.binding.as_type (constants.%U.binding.as_type)] // CHECK:STDOUT: %.loc7_16.2: type = converted %U.ref.loc7, %U.as_type.loc7 [symbolic = %U.binding.as_type (constants.%U.binding.as_type)] // CHECK:STDOUT: } // CHECK:STDOUT: %u.loc7: @G.%U.binding.as_type (%U.binding.as_type) = value_binding u, %u.param.loc7 // CHECK:STDOUT: } // CHECK:STDOUT: %C.decl.loc9: type = class_decl @C [concrete = constants.%C] {} {} // CHECK:STDOUT: impl_decl @C.as.X.impl [concrete] {} { // CHECK:STDOUT: %C.ref.loc12: type = name_ref C, file.%C.decl.loc9 [concrete = constants.%C] // CHECK:STDOUT: %X.ref.loc12: type = name_ref X, file.%X.decl.loc3 [concrete = constants.%X.type] // CHECK:STDOUT: } // CHECK:STDOUT: %Y.decl.loc14: type = interface_decl @Y [concrete = constants.%Y.type] {} {} // CHECK:STDOUT: %X.decl.loc16: type = interface_decl @X [concrete = constants.%X.type] {} {} // CHECK:STDOUT: %C.decl.loc23: type = class_decl @C [concrete = constants.%C] {} {} // CHECK:STDOUT: %H.decl: %H.type = fn_decl @H [concrete = constants.%H] { // CHECK:STDOUT: %c.patt: %pattern_type.7c7 = value_binding_pattern c [concrete] // CHECK:STDOUT: %c.param_patt: %pattern_type.7c7 = value_param_pattern %c.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %c.param: %C = value_param call_param0 // CHECK:STDOUT: %C.ref.loc25: type = name_ref C, file.%C.decl.loc9 [concrete = constants.%C] // CHECK:STDOUT: %c: %C = value_binding c, %c.param // CHECK:STDOUT: } // CHECK:STDOUT: %Y.decl.loc33: type = interface_decl @Y [concrete = constants.%Y.type] {} {} // CHECK:STDOUT: %F.decl.loc34: %F.type = fn_decl @F [concrete = constants.%F] { // CHECK:STDOUT: %_.patt: %pattern_type.b4b = symbolic_binding_pattern _, 0 [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %.loc34: type = splice_block %X.ref.loc34 [concrete = constants.%X.type] { // CHECK:STDOUT: %.Self.1: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %X.ref.loc34: type = name_ref X, file.%X.decl.loc3 [concrete = constants.%X.type] // CHECK:STDOUT: } // CHECK:STDOUT: %_.loc34: %X.type = symbolic_binding _, 0 [symbolic = %_.loc6_6.1 (constants.%_)] // CHECK:STDOUT: } // CHECK:STDOUT: %G.decl.loc35: %G.type = fn_decl @G [concrete = constants.%G] { // CHECK:STDOUT: %U.patt: %pattern_type.b4b = symbolic_binding_pattern U, 0 [concrete] // CHECK:STDOUT: %u.patt: @G.%pattern_type (%pattern_type.b36) = value_binding_pattern u [concrete] // CHECK:STDOUT: %u.param_patt: @G.%pattern_type (%pattern_type.b36) = value_param_pattern %u.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %.loc35_10: type = splice_block %X.ref.loc35 [concrete = constants.%X.type] { // CHECK:STDOUT: %.Self.1: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %X.ref.loc35: type = name_ref X, file.%X.decl.loc3 [concrete = constants.%X.type] // CHECK:STDOUT: } // CHECK:STDOUT: %U.loc35: %X.type = symbolic_binding U, 0 [symbolic = %U.loc7_6.1 (constants.%U)] // CHECK:STDOUT: %u.param.loc35: @G.%U.binding.as_type (%U.binding.as_type) = value_param call_param0 // CHECK:STDOUT: %.loc35_23.1: type = splice_block %.loc35_23.2 [symbolic = %U.binding.as_type (constants.%U.binding.as_type)] { // CHECK:STDOUT: %U.ref.loc35: %X.type = name_ref U, %U.loc35 [symbolic = %U.loc7_6.1 (constants.%U)] // CHECK:STDOUT: %U.as_type.loc35: type = facet_access_type %U.ref.loc35 [symbolic = %U.binding.as_type (constants.%U.binding.as_type)] // CHECK:STDOUT: %.loc35_23.2: type = converted %U.ref.loc35, %U.as_type.loc35 [symbolic = %U.binding.as_type (constants.%U.binding.as_type)] // CHECK:STDOUT: } // CHECK:STDOUT: %u.loc35: @G.%U.binding.as_type (%U.binding.as_type) = value_binding u, %u.param.loc35 // CHECK:STDOUT: } // CHECK:STDOUT: impl_decl @C.as.Y.impl [concrete] {} { // CHECK:STDOUT: %C.ref.loc36: type = name_ref C, file.%C.decl.loc9 [concrete = constants.%C] // CHECK:STDOUT: %Y.ref.loc36: type = name_ref Y, file.%Y.decl.loc14 [concrete = constants.%Y.type] // CHECK:STDOUT: } // CHECK:STDOUT: impl_decl @C.as.X.impl [concrete] {} { // CHECK:STDOUT: %C.ref.loc37: type = name_ref C, file.%C.decl.loc9 [concrete = constants.%C] // CHECK:STDOUT: %X.ref.loc37: type = name_ref X, file.%X.decl.loc3 [concrete = constants.%X.type] // CHECK:STDOUT: } // CHECK:STDOUT: impl_decl @C.as.Y.impl [concrete] {} { // CHECK:STDOUT: %C.ref.loc38: type = name_ref C, file.%C.decl.loc9 [concrete = constants.%C] // CHECK:STDOUT: %Y.ref.loc38: type = name_ref Y, file.%Y.decl.loc14 [concrete = constants.%Y.type] // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: interface @X { // CHECK:STDOUT: %Self: %X.type = symbolic_binding Self, 0 [symbolic = constants.%Self.e52] // CHECK:STDOUT: %X.WithSelf.decl = interface_with_self_decl @X [concrete] // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = %Self // CHECK:STDOUT: witness = () // CHECK:STDOUT: // CHECK:STDOUT: !requires: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: interface @Y { // CHECK:STDOUT: %Self: %Y.type = symbolic_binding Self, 0 [symbolic = constants.%Self.162] // CHECK:STDOUT: %Y.WithSelf.decl = interface_with_self_decl @Y [concrete] // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = %Self // CHECK:STDOUT: witness = () // CHECK:STDOUT: // CHECK:STDOUT: !requires: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: impl @C.as.X.impl: %C.ref.loc12 as %X.ref.loc12 { // CHECK:STDOUT: %X.impl_witness_table = impl_witness_table (), @C.as.X.impl [concrete] // CHECK:STDOUT: %X.impl_witness: = impl_witness %X.impl_witness_table [concrete = constants.%X.impl_witness] // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: witness = %X.impl_witness // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: impl @C.as.Y.impl: %C.ref.loc36 as %Y.ref.loc36 { // CHECK:STDOUT: %Y.impl_witness_table = impl_witness_table (), @C.as.Y.impl [concrete] // CHECK:STDOUT: %Y.impl_witness: = impl_witness %Y.impl_witness_table [concrete = constants.%Y.impl_witness] // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: witness = %Y.impl_witness // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @C { // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%C // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic fn @F(%_.loc6_6.2: %X.type) { // CHECK:STDOUT: %_.loc6_6.1: %X.type = symbolic_binding _, 0 [symbolic = %_.loc6_6.1 (constants.%_)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: // CHECK:STDOUT: fn() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic fn @G(%U.loc7_6.2: %X.type) { // CHECK:STDOUT: %U.loc7_6.1: %X.type = symbolic_binding U, 0 [symbolic = %U.loc7_6.1 (constants.%U)] // CHECK:STDOUT: %U.binding.as_type: type = symbolic_binding_type U, 0, %U.loc7_6.1 [symbolic = %U.binding.as_type (constants.%U.binding.as_type)] // CHECK:STDOUT: %pattern_type: type = pattern_type %U.binding.as_type [symbolic = %pattern_type (constants.%pattern_type.b36)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete: = require_complete_type %U.binding.as_type [symbolic = %require_complete (constants.%require_complete)] // CHECK:STDOUT: // CHECK:STDOUT: fn(%u.param.loc35: @G.%U.binding.as_type (%U.binding.as_type)) { // CHECK:STDOUT: !entry: // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @H(%c.param: %C) { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %F.ref: %F.type = name_ref F, file.%F.decl.loc6 [concrete = constants.%F] // CHECK:STDOUT: %C.ref.loc27: type = name_ref C, file.%C.decl.loc9 [concrete = constants.%C] // CHECK:STDOUT: %X.facet.loc27: %X.type = facet_value %C.ref.loc27, (constants.%X.impl_witness) [concrete = constants.%X.facet] // CHECK:STDOUT: %.loc27: %X.type = converted %C.ref.loc27, %X.facet.loc27 [concrete = constants.%X.facet] // CHECK:STDOUT: %F.specific_fn: = specific_function %F.ref, @F(constants.%X.facet) [concrete = constants.%F.specific_fn] // CHECK:STDOUT: %F.call: init %empty_tuple.type = call %F.specific_fn() // CHECK:STDOUT: %G.ref: %G.type = name_ref G, file.%G.decl.loc7 [concrete = constants.%G] // CHECK:STDOUT: %c.ref: %C = name_ref c, %c // CHECK:STDOUT: %X.facet.loc28: %X.type = facet_value constants.%C, (constants.%X.impl_witness) [concrete = constants.%X.facet] // CHECK:STDOUT: %.loc28: %X.type = converted constants.%C, %X.facet.loc28 [concrete = constants.%X.facet] // CHECK:STDOUT: %G.specific_fn: = specific_function %G.ref, @G(constants.%X.facet) [concrete = constants.%G.specific_fn] // CHECK:STDOUT: %G.call: init %empty_tuple.type = call %G.specific_fn(%c.ref) // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @F(constants.%_) { // CHECK:STDOUT: %_.loc6_6.1 => constants.%_ // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @G(constants.%U) { // CHECK:STDOUT: %U.loc7_6.1 => constants.%U // CHECK:STDOUT: %U.binding.as_type => constants.%U.binding.as_type // CHECK:STDOUT: %pattern_type => constants.%pattern_type.b36 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @X.WithSelf(constants.%Self.e52) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @F(constants.%X.facet) { // CHECK:STDOUT: %_.loc6_6.1 => constants.%X.facet // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @G(constants.%X.facet) { // CHECK:STDOUT: %U.loc7_6.1 => constants.%X.facet // CHECK:STDOUT: %U.binding.as_type => constants.%C // CHECK:STDOUT: %pattern_type => constants.%pattern_type.7c7 // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete => constants.%complete_type // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @Y.WithSelf(constants.%Self.162) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @X.WithSelf(constants.%X.facet) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @Y.WithSelf(constants.%Y.facet) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- fail_todo_impl_in_interface_definition.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %I.type: type = facet_type <@I> [concrete] // CHECK:STDOUT: %Self: %I.type = symbolic_binding Self, 0 [symbolic] // CHECK:STDOUT: %I.WithSelf.F.type.08c: type = fn_type @I.WithSelf.F, @I.WithSelf(%Self) [symbolic] // CHECK:STDOUT: %I.WithSelf.F.705: %I.WithSelf.F.type.08c = struct_value () [symbolic] // CHECK:STDOUT: %I.assoc_type: type = assoc_entity_type @I [concrete] // CHECK:STDOUT: %assoc0: %I.assoc_type = assoc_entity element0, @I.WithSelf.%I.WithSelf.F.decl [concrete] // CHECK:STDOUT: %C: type = class_type @C, @C(%Self) [symbolic] // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %complete_type: = complete_type_witness %empty_struct_type [concrete] // CHECK:STDOUT: %I.impl_witness: = impl_witness @C.as.I.impl.%I.impl_witness_table, @C.as.I.impl(%Self) [symbolic] // CHECK:STDOUT: %I.facet: %I.type = facet_value %C, (%I.impl_witness) [symbolic] // CHECK:STDOUT: %I.WithSelf.F.type.5dd: type = fn_type @I.WithSelf.F, @I.WithSelf(%I.facet) [symbolic] // CHECK:STDOUT: %I.WithSelf.F.f5e: %I.WithSelf.F.type.5dd = struct_value () [symbolic] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .I = %I.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %I.decl: type = interface_decl @I [concrete = constants.%I.type] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: interface @I { // CHECK:STDOUT: %Self: %I.type = symbolic_binding Self, 0 [symbolic = constants.%Self] // CHECK:STDOUT: %I.WithSelf.decl = interface_with_self_decl @I [concrete] // CHECK:STDOUT: // CHECK:STDOUT: !with Self: // CHECK:STDOUT: %I.WithSelf.F.decl: @I.WithSelf.%I.WithSelf.F.type (%I.WithSelf.F.type.08c) = fn_decl @I.WithSelf.F [symbolic = @I.WithSelf.%I.WithSelf.F (constants.%I.WithSelf.F.705)] {} {} // CHECK:STDOUT: %assoc0: %I.assoc_type = assoc_entity element0, %I.WithSelf.F.decl [concrete = constants.%assoc0] // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = %Self // CHECK:STDOUT: .I = // CHECK:STDOUT: .F = @I.WithSelf.%assoc0 // CHECK:STDOUT: .I = // CHECK:STDOUT: witness = (@I.WithSelf.%I.WithSelf.F.decl) // CHECK:STDOUT: // CHECK:STDOUT: !requires: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic impl @C.as.I.impl(@I.%Self: %I.type) { // CHECK:STDOUT: %Self: %I.type = symbolic_binding Self, 0 [symbolic = %Self (constants.%Self)] // CHECK:STDOUT: %C: type = class_type @C, @C(%Self) [symbolic = %C (constants.%C)] // CHECK:STDOUT: %I.impl_witness.loc11_16.2: = impl_witness %I.impl_witness_table, @C.as.I.impl(%Self) [symbolic = %I.impl_witness.loc11_16.2 (constants.%I.impl_witness)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: // CHECK:STDOUT: impl: %C.ref.loc11 as %I.ref.loc11 { // CHECK:STDOUT: %I.impl_witness_table = impl_witness_table (), @C.as.I.impl [concrete] // CHECK:STDOUT: %I.impl_witness.loc11_16.1: = impl_witness %I.impl_witness_table, @C.as.I.impl(constants.%Self) [symbolic = %I.impl_witness.loc11_16.2 (constants.%I.impl_witness)] // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .F = // CHECK:STDOUT: witness = %I.impl_witness.loc11_16.1 // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic class @C(@I.%Self: %I.type) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: // CHECK:STDOUT: class { // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%C // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic fn @I.WithSelf.F(@I.%Self: %I.type) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: %Self: %I.type = symbolic_binding Self, 0 [symbolic = %Self (constants.%Self)] // CHECK:STDOUT: %C: type = class_type @C, @C(%Self) [symbolic = %C (constants.%C)] // CHECK:STDOUT: // CHECK:STDOUT: fn() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %C.decl: type = class_decl @C [symbolic = @I.WithSelf.F.%C (constants.%C)] {} {} // CHECK:STDOUT: impl_decl @C.as.I.impl [concrete] {} { // CHECK:STDOUT: %C.ref.loc11: type = name_ref C, @I.WithSelf.F.%C.decl [symbolic = %C (constants.%C)] // CHECK:STDOUT: %I.ref.loc11: type = name_ref I, file.%I.decl [concrete = constants.%I.type] // CHECK:STDOUT: } // CHECK:STDOUT: impl_decl @C.as.I.impl [concrete] {} { // CHECK:STDOUT: %C.ref.loc20: type = name_ref C, @I.WithSelf.F.%C.decl [symbolic = %C (constants.%C)] // CHECK:STDOUT: %I.ref.loc20: type = name_ref I, file.%I.decl [concrete = constants.%I.type] // CHECK:STDOUT: } // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @I.WithSelf(constants.%Self) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: %Self => constants.%Self // CHECK:STDOUT: %I.WithSelf.F.type => constants.%I.WithSelf.F.type.08c // CHECK:STDOUT: %I.WithSelf.F => constants.%I.WithSelf.F.705 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @I.WithSelf.F(constants.%Self) {} // CHECK:STDOUT: // CHECK:STDOUT: specific @C(constants.%Self) {} // CHECK:STDOUT: // CHECK:STDOUT: specific @C.as.I.impl(constants.%Self) { // CHECK:STDOUT: %Self => constants.%Self // CHECK:STDOUT: %C => constants.%C // CHECK:STDOUT: %I.impl_witness.loc11_16.2 => constants.%I.impl_witness // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @I.WithSelf(constants.%I.facet) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: %Self => constants.%I.facet // CHECK:STDOUT: %I.WithSelf.F.type => constants.%I.WithSelf.F.type.5dd // CHECK:STDOUT: %I.WithSelf.F => constants.%I.WithSelf.F.f5e // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- fail_todo_impl_in_interface_definition_with_associated.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %I.type: type = facet_type <@I> [concrete] // CHECK:STDOUT: %Self: %I.type = symbolic_binding Self, 0 [symbolic] // CHECK:STDOUT: %I.assoc_type: type = assoc_entity_type @I [concrete] // CHECK:STDOUT: %assoc0: %I.assoc_type = assoc_entity element0, @I.WithSelf.%U [concrete] // CHECK:STDOUT: %I.WithSelf.F.type.08c: type = fn_type @I.WithSelf.F, @I.WithSelf(%Self) [symbolic] // CHECK:STDOUT: %I.WithSelf.F.705: %I.WithSelf.F.type.08c = struct_value () [symbolic] // CHECK:STDOUT: %assoc1: %I.assoc_type = assoc_entity element1, @I.WithSelf.%I.WithSelf.F.decl [concrete] // CHECK:STDOUT: %C: type = class_type @C, @C(%Self) [symbolic] // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %complete_type: = complete_type_witness %empty_struct_type [concrete] // CHECK:STDOUT: %.Self: %I.type = symbolic_binding .Self [symbolic_self] // CHECK:STDOUT: %I.WithSelf.F.type.29d: type = fn_type @I.WithSelf.F, @I.WithSelf(%.Self) [symbolic_self] // CHECK:STDOUT: %I.WithSelf.F.8ec: %I.WithSelf.F.type.29d = struct_value () [symbolic_self] // CHECK:STDOUT: %.Self.binding.as_type: type = symbolic_binding_type .Self, %.Self [symbolic_self] // CHECK:STDOUT: %I.lookup_impl_witness: = lookup_impl_witness %.Self, @I [symbolic_self] // CHECK:STDOUT: %impl.elem0: type = impl_witness_access %I.lookup_impl_witness, element0 [symbolic_self] // CHECK:STDOUT: %I_where.type: type = facet_type <@I where %impl.elem0 = %C> [symbolic] // CHECK:STDOUT: %I.impl_witness: = impl_witness @C.as.I.impl.%I.impl_witness_table, @C.as.I.impl(%Self) [symbolic] // CHECK:STDOUT: %require_complete: = require_complete_type %I_where.type [symbolic] // CHECK:STDOUT: %I.facet: %I.type = facet_value %C, (%I.impl_witness) [symbolic] // CHECK:STDOUT: %I.WithSelf.F.type.000: type = fn_type @I.WithSelf.F, @I.WithSelf(%I.facet) [symbolic] // CHECK:STDOUT: %I.WithSelf.F.a8c: %I.WithSelf.F.type.000 = struct_value () [symbolic] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .I = %I.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %I.decl: type = interface_decl @I [concrete = constants.%I.type] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: interface @I { // CHECK:STDOUT: %Self: %I.type = symbolic_binding Self, 0 [symbolic = constants.%Self] // CHECK:STDOUT: %I.WithSelf.decl = interface_with_self_decl @I [concrete] // CHECK:STDOUT: // CHECK:STDOUT: !with Self: // CHECK:STDOUT: %U: type = assoc_const_decl @U [concrete] { // CHECK:STDOUT: %assoc0: %I.assoc_type = assoc_entity element0, @I.WithSelf.%U [concrete = constants.%assoc0] // CHECK:STDOUT: } // CHECK:STDOUT: %I.WithSelf.F.decl: @I.WithSelf.%I.WithSelf.F.type (%I.WithSelf.F.type.08c) = fn_decl @I.WithSelf.F [symbolic = @I.WithSelf.%I.WithSelf.F (constants.%I.WithSelf.F.705)] {} {} // CHECK:STDOUT: %assoc1: %I.assoc_type = assoc_entity element1, %I.WithSelf.F.decl [concrete = constants.%assoc1] // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = %Self // CHECK:STDOUT: .I = // CHECK:STDOUT: .U = @U.%assoc0 // CHECK:STDOUT: .F = @I.WithSelf.%assoc1 // CHECK:STDOUT: .I = // CHECK:STDOUT: witness = (@I.WithSelf.%U, @I.WithSelf.%I.WithSelf.F.decl) // CHECK:STDOUT: // CHECK:STDOUT: !requires: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic impl @C.as.I.impl(@I.%Self: %I.type) { // CHECK:STDOUT: %Self: %I.type = symbolic_binding Self, 0 [symbolic = %Self (constants.%Self)] // CHECK:STDOUT: %C: type = class_type @C, @C(%Self) [symbolic = %C (constants.%C)] // CHECK:STDOUT: %I_where.type: type = facet_type <@I where constants.%impl.elem0 = %C> [symbolic = %I_where.type (constants.%I_where.type)] // CHECK:STDOUT: %I.impl_witness.loc12_29.2: = impl_witness %I.impl_witness_table, @C.as.I.impl(%Self) [symbolic = %I.impl_witness.loc12_29.2 (constants.%I.impl_witness)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete: = require_complete_type %I_where.type [symbolic = %require_complete (constants.%require_complete)] // CHECK:STDOUT: // CHECK:STDOUT: impl: %C.ref.loc12_10 as %.loc12_17 { // CHECK:STDOUT: %I.impl_witness_table = impl_witness_table (%impl_witness_assoc_constant, ), @C.as.I.impl [concrete] // CHECK:STDOUT: %I.impl_witness.loc12_29.1: = impl_witness %I.impl_witness_table, @C.as.I.impl(constants.%Self) [symbolic = %I.impl_witness.loc12_29.2 (constants.%I.impl_witness)] // CHECK:STDOUT: %impl_witness_assoc_constant: type = impl_witness_assoc_constant constants.%C [symbolic = %C (constants.%C)] // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .F = // CHECK:STDOUT: witness = %I.impl_witness.loc12_29.1 // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic class @C(@I.%Self: %I.type) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: // CHECK:STDOUT: class { // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%C // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic fn @I.WithSelf.F(@I.%Self: %I.type) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: %Self: %I.type = symbolic_binding Self, 0 [symbolic = %Self (constants.%Self)] // CHECK:STDOUT: %C: type = class_type @C, @C(%Self) [symbolic = %C (constants.%C)] // CHECK:STDOUT: // CHECK:STDOUT: fn() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %C.decl: type = class_decl @C [symbolic = @I.WithSelf.F.%C (constants.%C)] {} {} // CHECK:STDOUT: impl_decl @C.as.I.impl [concrete] {} { // CHECK:STDOUT: %C.ref.loc12_10: type = name_ref C, @I.WithSelf.F.%C.decl [symbolic = %C (constants.%C)] // CHECK:STDOUT: %I.ref.loc12: type = name_ref I, file.%I.decl [concrete = constants.%I.type] // CHECK:STDOUT: %.Self.2: %I.type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %.Self.ref.loc12: %I.type = name_ref .Self, %.Self.2 [symbolic_self = constants.%.Self] // CHECK:STDOUT: %.Self.as_type.loc12: type = facet_access_type %.Self.ref.loc12 [symbolic_self = constants.%.Self.binding.as_type] // CHECK:STDOUT: %.loc12_23: type = converted %.Self.ref.loc12, %.Self.as_type.loc12 [symbolic_self = constants.%.Self.binding.as_type] // CHECK:STDOUT: %U.ref.loc12: %I.assoc_type = name_ref U, @U.%assoc0 [concrete = constants.%assoc0] // CHECK:STDOUT: %impl.elem0.loc12: type = impl_witness_access constants.%I.lookup_impl_witness, element0 [symbolic_self = constants.%impl.elem0] // CHECK:STDOUT: %C.ref.loc12_28: type = name_ref C, @I.WithSelf.F.%C.decl [symbolic = %C (constants.%C)] // CHECK:STDOUT: %.loc12_17: type = where_expr %.Self.2 [symbolic = %I_where.type (constants.%I_where.type)] { // CHECK:STDOUT: requirement_base_facet_type constants.%I.type // CHECK:STDOUT: requirement_rewrite %impl.elem0.loc12, %C.ref.loc12_28 // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: impl_decl @C.as.I.impl [concrete] {} { // CHECK:STDOUT: %C.ref.loc21_10: type = name_ref C, @I.WithSelf.F.%C.decl [symbolic = %C (constants.%C)] // CHECK:STDOUT: %I.ref.loc21: type = name_ref I, file.%I.decl [concrete = constants.%I.type] // CHECK:STDOUT: %.Self.1: %I.type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %.Self.ref.loc21: %I.type = name_ref .Self, %.Self.1 [symbolic_self = constants.%.Self] // CHECK:STDOUT: %.Self.as_type.loc21: type = facet_access_type %.Self.ref.loc21 [symbolic_self = constants.%.Self.binding.as_type] // CHECK:STDOUT: %.loc21_23: type = converted %.Self.ref.loc21, %.Self.as_type.loc21 [symbolic_self = constants.%.Self.binding.as_type] // CHECK:STDOUT: %U.ref.loc21: %I.assoc_type = name_ref U, @U.%assoc0 [concrete = constants.%assoc0] // CHECK:STDOUT: %impl.elem0.loc21: type = impl_witness_access constants.%I.lookup_impl_witness, element0 [symbolic_self = constants.%impl.elem0] // CHECK:STDOUT: %C.ref.loc21_28: type = name_ref C, @I.WithSelf.F.%C.decl [symbolic = %C (constants.%C)] // CHECK:STDOUT: %.loc21_17: type = where_expr %.Self.1 [symbolic = %I_where.type (constants.%I_where.type)] { // CHECK:STDOUT: requirement_base_facet_type constants.%I.type // CHECK:STDOUT: requirement_rewrite %impl.elem0.loc21, %C.ref.loc21_28 // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @I.WithSelf(constants.%Self) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: %Self => constants.%Self // CHECK:STDOUT: %I.WithSelf.F.type => constants.%I.WithSelf.F.type.08c // CHECK:STDOUT: %I.WithSelf.F => constants.%I.WithSelf.F.705 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @I.WithSelf.F(constants.%Self) {} // CHECK:STDOUT: // CHECK:STDOUT: specific @C(constants.%Self) {} // CHECK:STDOUT: // CHECK:STDOUT: specific @I.WithSelf(constants.%.Self) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: %Self => constants.%.Self // CHECK:STDOUT: %I.WithSelf.F.type => constants.%I.WithSelf.F.type.29d // CHECK:STDOUT: %I.WithSelf.F => constants.%I.WithSelf.F.8ec // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @C.as.I.impl(constants.%Self) { // CHECK:STDOUT: %Self => constants.%Self // CHECK:STDOUT: %C => constants.%C // CHECK:STDOUT: %I_where.type => constants.%I_where.type // CHECK:STDOUT: %I.impl_witness.loc12_29.2 => constants.%I.impl_witness // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @I.WithSelf(constants.%I.facet) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: %Self => constants.%I.facet // CHECK:STDOUT: %I.WithSelf.F.type => constants.%I.WithSelf.F.type.000 // CHECK:STDOUT: %I.WithSelf.F => constants.%I.WithSelf.F.a8c // CHECK:STDOUT: } // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/impl/generic_redeclaration.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/destroy.carbon // TODO: Add ranges and switch to "--dump-sem-ir-ranges=only". // EXTRA-ARGS: --dump-sem-ir-ranges=if-present // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/impl/generic_redeclaration.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/impl/generic_redeclaration.carbon // --- fail_todo_same_self_and_interface.carbon library "[[@TEST_NAME]]"; interface Interface {} interface I {} interface J {} interface K {} interface L {} // TODO: Put these in a match_first so the test will pass again. impl forall [T:! I] T as Interface; impl forall [T:! J] T as Interface; impl forall [T:! K] T as Interface; impl forall [T:! L] T as Interface; // These are different impls, so they are not redefinitions, even though the // self type and constraint type are the same. impl forall [T:! I] T as Interface {} // CHECK:STDERR: fail_todo_same_self_and_interface.carbon:[[@LINE+7]]:1: error: found non-final `impl` with the same type structure as another non-final `impl` [ImplNonFinalSameTypeStructure] // CHECK:STDERR: impl forall [T:! J] T as Interface {} // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: fail_todo_same_self_and_interface.carbon:[[@LINE-4]]:1: note: other `impl` here [ImplNonFinalSameTypeStructureNote] // CHECK:STDERR: impl forall [T:! I] T as Interface {} // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: impl forall [T:! J] T as Interface {} // CHECK:STDERR: fail_todo_same_self_and_interface.carbon:[[@LINE+7]]:1: error: found non-final `impl` with the same type structure as another non-final `impl` [ImplNonFinalSameTypeStructure] // CHECK:STDERR: impl forall [T:! K] T as Interface {} // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: fail_todo_same_self_and_interface.carbon:[[@LINE-12]]:1: note: other `impl` here [ImplNonFinalSameTypeStructureNote] // CHECK:STDERR: impl forall [T:! I] T as Interface {} // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: impl forall [T:! K] T as Interface {} // CHECK:STDERR: fail_todo_same_self_and_interface.carbon:[[@LINE+7]]:1: error: found non-final `impl` with the same type structure as another non-final `impl` [ImplNonFinalSameTypeStructure] // CHECK:STDERR: impl forall [T:! L] T as Interface {} // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: fail_todo_same_self_and_interface.carbon:[[@LINE-20]]:1: note: other `impl` here [ImplNonFinalSameTypeStructureNote] // CHECK:STDERR: impl forall [T:! I] T as Interface {} // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: impl forall [T:! L] T as Interface {} // --- fail_same_self_and_interface_redefined.carbon library "[[@TEST_NAME]]"; interface I {} interface J {} impl forall [T:! I] T as J {} // CHECK:STDERR: fail_same_self_and_interface_redefined.carbon:[[@LINE+7]]:1: error: redefinition of `impl T as J` [ImplRedefinition] // CHECK:STDERR: impl forall [T:! I] T as J {} // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: fail_same_self_and_interface_redefined.carbon:[[@LINE-4]]:1: note: previous definition was here [ImplPreviousDefinition] // CHECK:STDERR: impl forall [T:! I] T as J {} // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: impl forall [T:! I] T as J {} // --- fail_same_type_different_spelling.carbon library "[[@TEST_NAME]]"; class C; interface I {} // Since the spelling is different, it's not caught as a redefinition, but it // is diagnosed as an overlapping impl without using a `match_first` block. impl C as I {} // CHECK:STDERR: fail_same_type_different_spelling.carbon:[[@LINE+7]]:1: error: found non-final `impl` with the same type structure as another non-final `impl` [ImplNonFinalSameTypeStructure] // CHECK:STDERR: impl (C, C).0 as I {} // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: fail_same_type_different_spelling.carbon:[[@LINE-4]]:1: note: other `impl` here [ImplNonFinalSameTypeStructureNote] // CHECK:STDERR: impl C as I {} // CHECK:STDERR: ^~~~~~~~~~~~~ // CHECK:STDERR: impl (C, C).0 as I {} // --- fail_redefinition_generic_regions.carbon interface I {} impl forall [T:! type] T as I { fn A() {} } // CHECK:STDERR: fail_redefinition_generic_regions.carbon:[[@LINE+7]]:1: error: redefinition of `impl T as I` [ImplRedefinition] // CHECK:STDERR: impl forall [T:! type] T as I { // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: fail_redefinition_generic_regions.carbon:[[@LINE-7]]:1: note: previous definition was here [ImplPreviousDefinition] // CHECK:STDERR: impl forall [T:! type] T as I { // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: impl forall [T:! type] T as I { // Although not referenced, B has the same generic region index of A, and // makes C a different index. We used to merge, and this was a crash. fn B() {} fn C() -> () { return (); } fn D() -> () { return C(); } } // CHECK:STDOUT: --- fail_todo_same_self_and_interface.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %Interface.type: type = facet_type <@Interface> [concrete] // CHECK:STDOUT: %Self.887: %Interface.type = symbolic_binding Self, 0 [symbolic] // CHECK:STDOUT: %I.type: type = facet_type <@I> [concrete] // CHECK:STDOUT: %Self.ab9: %I.type = symbolic_binding Self, 0 [symbolic] // CHECK:STDOUT: %J.type: type = facet_type <@J> [concrete] // CHECK:STDOUT: %Self.8a1: %J.type = symbolic_binding Self, 0 [symbolic] // CHECK:STDOUT: %K.type: type = facet_type <@K> [concrete] // CHECK:STDOUT: %Self.44d: %K.type = symbolic_binding Self, 0 [symbolic] // CHECK:STDOUT: %L.type: type = facet_type <@L> [concrete] // CHECK:STDOUT: %Self.cbc: %L.type = symbolic_binding Self, 0 [symbolic] // CHECK:STDOUT: %type: type = facet_type [concrete] // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self] // CHECK:STDOUT: %pattern_type.9d9: type = pattern_type %I.type [concrete] // CHECK:STDOUT: %T.651: %I.type = symbolic_binding T, 0 [symbolic] // CHECK:STDOUT: %T.binding.as_type.3af: type = symbolic_binding_type T, 0, %T.651 [symbolic] // CHECK:STDOUT: %Interface.impl_witness.779: = impl_witness @T.binding.as_type.as.Interface.impl.973.%Interface.impl_witness_table, @T.binding.as_type.as.Interface.impl.973(%T.651) [symbolic] // CHECK:STDOUT: %pattern_type.f76: type = pattern_type %J.type [concrete] // CHECK:STDOUT: %T.612: %J.type = symbolic_binding T, 0 [symbolic] // CHECK:STDOUT: %T.binding.as_type.50b: type = symbolic_binding_type T, 0, %T.612 [symbolic] // CHECK:STDOUT: %Interface.impl_witness.8e2: = impl_witness @T.binding.as_type.as.Interface.impl.5eb.%Interface.impl_witness_table, @T.binding.as_type.as.Interface.impl.5eb(%T.612) [symbolic] // CHECK:STDOUT: %pattern_type.250: type = pattern_type %K.type [concrete] // CHECK:STDOUT: %T.9b2: %K.type = symbolic_binding T, 0 [symbolic] // CHECK:STDOUT: %T.binding.as_type.bec: type = symbolic_binding_type T, 0, %T.9b2 [symbolic] // CHECK:STDOUT: %Interface.impl_witness.84e: = impl_witness @T.binding.as_type.as.Interface.impl.edf.%Interface.impl_witness_table, @T.binding.as_type.as.Interface.impl.edf(%T.9b2) [symbolic] // CHECK:STDOUT: %pattern_type.88f: type = pattern_type %L.type [concrete] // CHECK:STDOUT: %T.0fc: %L.type = symbolic_binding T, 0 [symbolic] // CHECK:STDOUT: %T.binding.as_type.29d: type = symbolic_binding_type T, 0, %T.0fc [symbolic] // CHECK:STDOUT: %Interface.impl_witness.8e9: = impl_witness @T.binding.as_type.as.Interface.impl.410.%Interface.impl_witness_table, @T.binding.as_type.as.Interface.impl.410(%T.0fc) [symbolic] // CHECK:STDOUT: %Interface.facet.9bb: %Interface.type = facet_value %T.binding.as_type.3af, (%Interface.impl_witness.779) [symbolic] // CHECK:STDOUT: %Interface.facet.08d: %Interface.type = facet_value %T.binding.as_type.50b, (%Interface.impl_witness.8e2) [symbolic] // CHECK:STDOUT: %Interface.facet.842: %Interface.type = facet_value %T.binding.as_type.bec, (%Interface.impl_witness.84e) [symbolic] // CHECK:STDOUT: %Interface.facet.f9d: %Interface.type = facet_value %T.binding.as_type.29d, (%Interface.impl_witness.8e9) [symbolic] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .Interface = %Interface.decl // CHECK:STDOUT: .I = %I.decl // CHECK:STDOUT: .J = %J.decl // CHECK:STDOUT: .K = %K.decl // CHECK:STDOUT: .L = %L.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %Interface.decl: type = interface_decl @Interface [concrete = constants.%Interface.type] {} {} // CHECK:STDOUT: %I.decl: type = interface_decl @I [concrete = constants.%I.type] {} {} // CHECK:STDOUT: %J.decl: type = interface_decl @J [concrete = constants.%J.type] {} {} // CHECK:STDOUT: %K.decl: type = interface_decl @K [concrete = constants.%K.type] {} {} // CHECK:STDOUT: %L.decl: type = interface_decl @L [concrete = constants.%L.type] {} {} // CHECK:STDOUT: impl_decl @T.binding.as_type.as.Interface.impl.973 [concrete] { // CHECK:STDOUT: %T.patt: %pattern_type.9d9 = symbolic_binding_pattern T, 0 [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %T.ref.loc12: %I.type = name_ref T, %T.loc12_14.1 [symbolic = %T.loc12_14.2 (constants.%T.651)] // CHECK:STDOUT: %T.as_type.loc12: type = facet_access_type %T.ref.loc12 [symbolic = %T.binding.as_type (constants.%T.binding.as_type.3af)] // CHECK:STDOUT: %.loc12_21: type = converted %T.ref.loc12, %T.as_type.loc12 [symbolic = %T.binding.as_type (constants.%T.binding.as_type.3af)] // CHECK:STDOUT: %Interface.ref.loc12: type = name_ref Interface, file.%Interface.decl [concrete = constants.%Interface.type] // CHECK:STDOUT: %.loc12_18: type = splice_block %I.ref.loc12 [concrete = constants.%I.type] { // CHECK:STDOUT: %.Self.2: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %I.ref.loc12: type = name_ref I, file.%I.decl [concrete = constants.%I.type] // CHECK:STDOUT: } // CHECK:STDOUT: %T.loc12_14.1: %I.type = symbolic_binding T, 0 [symbolic = %T.loc12_14.2 (constants.%T.651)] // CHECK:STDOUT: } // CHECK:STDOUT: impl_decl @T.binding.as_type.as.Interface.impl.5eb [concrete] { // CHECK:STDOUT: %T.patt: %pattern_type.f76 = symbolic_binding_pattern T, 0 [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %T.ref.loc13: %J.type = name_ref T, %T.loc13_14.1 [symbolic = %T.loc13_14.2 (constants.%T.612)] // CHECK:STDOUT: %T.as_type.loc13: type = facet_access_type %T.ref.loc13 [symbolic = %T.binding.as_type (constants.%T.binding.as_type.50b)] // CHECK:STDOUT: %.loc13_21: type = converted %T.ref.loc13, %T.as_type.loc13 [symbolic = %T.binding.as_type (constants.%T.binding.as_type.50b)] // CHECK:STDOUT: %Interface.ref.loc13: type = name_ref Interface, file.%Interface.decl [concrete = constants.%Interface.type] // CHECK:STDOUT: %.loc13_18: type = splice_block %J.ref.loc13 [concrete = constants.%J.type] { // CHECK:STDOUT: %.Self.2: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %J.ref.loc13: type = name_ref J, file.%J.decl [concrete = constants.%J.type] // CHECK:STDOUT: } // CHECK:STDOUT: %T.loc13_14.1: %J.type = symbolic_binding T, 0 [symbolic = %T.loc13_14.2 (constants.%T.612)] // CHECK:STDOUT: } // CHECK:STDOUT: impl_decl @T.binding.as_type.as.Interface.impl.edf [concrete] { // CHECK:STDOUT: %T.patt: %pattern_type.250 = symbolic_binding_pattern T, 0 [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %T.ref.loc14: %K.type = name_ref T, %T.loc14_14.1 [symbolic = %T.loc14_14.2 (constants.%T.9b2)] // CHECK:STDOUT: %T.as_type.loc14: type = facet_access_type %T.ref.loc14 [symbolic = %T.binding.as_type (constants.%T.binding.as_type.bec)] // CHECK:STDOUT: %.loc14_21: type = converted %T.ref.loc14, %T.as_type.loc14 [symbolic = %T.binding.as_type (constants.%T.binding.as_type.bec)] // CHECK:STDOUT: %Interface.ref.loc14: type = name_ref Interface, file.%Interface.decl [concrete = constants.%Interface.type] // CHECK:STDOUT: %.loc14_18: type = splice_block %K.ref.loc14 [concrete = constants.%K.type] { // CHECK:STDOUT: %.Self.2: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %K.ref.loc14: type = name_ref K, file.%K.decl [concrete = constants.%K.type] // CHECK:STDOUT: } // CHECK:STDOUT: %T.loc14_14.1: %K.type = symbolic_binding T, 0 [symbolic = %T.loc14_14.2 (constants.%T.9b2)] // CHECK:STDOUT: } // CHECK:STDOUT: impl_decl @T.binding.as_type.as.Interface.impl.410 [concrete] { // CHECK:STDOUT: %T.patt: %pattern_type.88f = symbolic_binding_pattern T, 0 [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %T.ref.loc15: %L.type = name_ref T, %T.loc15_14.1 [symbolic = %T.loc15_14.2 (constants.%T.0fc)] // CHECK:STDOUT: %T.as_type.loc15: type = facet_access_type %T.ref.loc15 [symbolic = %T.binding.as_type (constants.%T.binding.as_type.29d)] // CHECK:STDOUT: %.loc15_21: type = converted %T.ref.loc15, %T.as_type.loc15 [symbolic = %T.binding.as_type (constants.%T.binding.as_type.29d)] // CHECK:STDOUT: %Interface.ref.loc15: type = name_ref Interface, file.%Interface.decl [concrete = constants.%Interface.type] // CHECK:STDOUT: %.loc15_18: type = splice_block %L.ref.loc15 [concrete = constants.%L.type] { // CHECK:STDOUT: %.Self.2: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %L.ref.loc15: type = name_ref L, file.%L.decl [concrete = constants.%L.type] // CHECK:STDOUT: } // CHECK:STDOUT: %T.loc15_14.1: %L.type = symbolic_binding T, 0 [symbolic = %T.loc15_14.2 (constants.%T.0fc)] // CHECK:STDOUT: } // CHECK:STDOUT: impl_decl @T.binding.as_type.as.Interface.impl.973 [concrete] { // CHECK:STDOUT: %T.patt: %pattern_type.9d9 = symbolic_binding_pattern T, 0 [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %T.ref.loc19: %I.type = name_ref T, %T.loc19 [symbolic = %T.loc12_14.2 (constants.%T.651)] // CHECK:STDOUT: %T.as_type.loc19: type = facet_access_type %T.ref.loc19 [symbolic = %T.binding.as_type (constants.%T.binding.as_type.3af)] // CHECK:STDOUT: %.loc19_21: type = converted %T.ref.loc19, %T.as_type.loc19 [symbolic = %T.binding.as_type (constants.%T.binding.as_type.3af)] // CHECK:STDOUT: %Interface.ref.loc19: type = name_ref Interface, file.%Interface.decl [concrete = constants.%Interface.type] // CHECK:STDOUT: %.loc19_18: type = splice_block %I.ref.loc19 [concrete = constants.%I.type] { // CHECK:STDOUT: %.Self.1: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %I.ref.loc19: type = name_ref I, file.%I.decl [concrete = constants.%I.type] // CHECK:STDOUT: } // CHECK:STDOUT: %T.loc19: %I.type = symbolic_binding T, 0 [symbolic = %T.loc12_14.2 (constants.%T.651)] // CHECK:STDOUT: } // CHECK:STDOUT: impl_decl @T.binding.as_type.as.Interface.impl.5eb [concrete] { // CHECK:STDOUT: %T.patt: %pattern_type.f76 = symbolic_binding_pattern T, 0 [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %T.ref.loc27: %J.type = name_ref T, %T.loc27 [symbolic = %T.loc13_14.2 (constants.%T.612)] // CHECK:STDOUT: %T.as_type.loc27: type = facet_access_type %T.ref.loc27 [symbolic = %T.binding.as_type (constants.%T.binding.as_type.50b)] // CHECK:STDOUT: %.loc27_21: type = converted %T.ref.loc27, %T.as_type.loc27 [symbolic = %T.binding.as_type (constants.%T.binding.as_type.50b)] // CHECK:STDOUT: %Interface.ref.loc27: type = name_ref Interface, file.%Interface.decl [concrete = constants.%Interface.type] // CHECK:STDOUT: %.loc27_18: type = splice_block %J.ref.loc27 [concrete = constants.%J.type] { // CHECK:STDOUT: %.Self.1: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %J.ref.loc27: type = name_ref J, file.%J.decl [concrete = constants.%J.type] // CHECK:STDOUT: } // CHECK:STDOUT: %T.loc27: %J.type = symbolic_binding T, 0 [symbolic = %T.loc13_14.2 (constants.%T.612)] // CHECK:STDOUT: } // CHECK:STDOUT: impl_decl @T.binding.as_type.as.Interface.impl.edf [concrete] { // CHECK:STDOUT: %T.patt: %pattern_type.250 = symbolic_binding_pattern T, 0 [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %T.ref.loc35: %K.type = name_ref T, %T.loc35 [symbolic = %T.loc14_14.2 (constants.%T.9b2)] // CHECK:STDOUT: %T.as_type.loc35: type = facet_access_type %T.ref.loc35 [symbolic = %T.binding.as_type (constants.%T.binding.as_type.bec)] // CHECK:STDOUT: %.loc35_21: type = converted %T.ref.loc35, %T.as_type.loc35 [symbolic = %T.binding.as_type (constants.%T.binding.as_type.bec)] // CHECK:STDOUT: %Interface.ref.loc35: type = name_ref Interface, file.%Interface.decl [concrete = constants.%Interface.type] // CHECK:STDOUT: %.loc35_18: type = splice_block %K.ref.loc35 [concrete = constants.%K.type] { // CHECK:STDOUT: %.Self.1: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %K.ref.loc35: type = name_ref K, file.%K.decl [concrete = constants.%K.type] // CHECK:STDOUT: } // CHECK:STDOUT: %T.loc35: %K.type = symbolic_binding T, 0 [symbolic = %T.loc14_14.2 (constants.%T.9b2)] // CHECK:STDOUT: } // CHECK:STDOUT: impl_decl @T.binding.as_type.as.Interface.impl.410 [concrete] { // CHECK:STDOUT: %T.patt: %pattern_type.88f = symbolic_binding_pattern T, 0 [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %T.ref.loc43: %L.type = name_ref T, %T.loc43 [symbolic = %T.loc15_14.2 (constants.%T.0fc)] // CHECK:STDOUT: %T.as_type.loc43: type = facet_access_type %T.ref.loc43 [symbolic = %T.binding.as_type (constants.%T.binding.as_type.29d)] // CHECK:STDOUT: %.loc43_21: type = converted %T.ref.loc43, %T.as_type.loc43 [symbolic = %T.binding.as_type (constants.%T.binding.as_type.29d)] // CHECK:STDOUT: %Interface.ref.loc43: type = name_ref Interface, file.%Interface.decl [concrete = constants.%Interface.type] // CHECK:STDOUT: %.loc43_18: type = splice_block %L.ref.loc43 [concrete = constants.%L.type] { // CHECK:STDOUT: %.Self.1: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %L.ref.loc43: type = name_ref L, file.%L.decl [concrete = constants.%L.type] // CHECK:STDOUT: } // CHECK:STDOUT: %T.loc43: %L.type = symbolic_binding T, 0 [symbolic = %T.loc15_14.2 (constants.%T.0fc)] // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: interface @Interface { // CHECK:STDOUT: %Self: %Interface.type = symbolic_binding Self, 0 [symbolic = constants.%Self.887] // CHECK:STDOUT: %Interface.WithSelf.decl = interface_with_self_decl @Interface [concrete] // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = %Self // CHECK:STDOUT: witness = () // CHECK:STDOUT: // CHECK:STDOUT: !requires: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: interface @I { // CHECK:STDOUT: %Self: %I.type = symbolic_binding Self, 0 [symbolic = constants.%Self.ab9] // CHECK:STDOUT: %I.WithSelf.decl = interface_with_self_decl @I [concrete] // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = %Self // CHECK:STDOUT: witness = () // CHECK:STDOUT: // CHECK:STDOUT: !requires: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: interface @J { // CHECK:STDOUT: %Self: %J.type = symbolic_binding Self, 0 [symbolic = constants.%Self.8a1] // CHECK:STDOUT: %J.WithSelf.decl = interface_with_self_decl @J [concrete] // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = %Self // CHECK:STDOUT: witness = () // CHECK:STDOUT: // CHECK:STDOUT: !requires: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: interface @K { // CHECK:STDOUT: %Self: %K.type = symbolic_binding Self, 0 [symbolic = constants.%Self.44d] // CHECK:STDOUT: %K.WithSelf.decl = interface_with_self_decl @K [concrete] // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = %Self // CHECK:STDOUT: witness = () // CHECK:STDOUT: // CHECK:STDOUT: !requires: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: interface @L { // CHECK:STDOUT: %Self: %L.type = symbolic_binding Self, 0 [symbolic = constants.%Self.cbc] // CHECK:STDOUT: %L.WithSelf.decl = interface_with_self_decl @L [concrete] // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = %Self // CHECK:STDOUT: witness = () // CHECK:STDOUT: // CHECK:STDOUT: !requires: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic impl @T.binding.as_type.as.Interface.impl.973(%T.loc12_14.1: %I.type) { // CHECK:STDOUT: %T.loc12_14.2: %I.type = symbolic_binding T, 0 [symbolic = %T.loc12_14.2 (constants.%T.651)] // CHECK:STDOUT: %T.binding.as_type: type = symbolic_binding_type T, 0, %T.loc12_14.2 [symbolic = %T.binding.as_type (constants.%T.binding.as_type.3af)] // CHECK:STDOUT: %Interface.impl_witness.loc12_35.2: = impl_witness %Interface.impl_witness_table, @T.binding.as_type.as.Interface.impl.973(%T.loc12_14.2) [symbolic = %Interface.impl_witness.loc12_35.2 (constants.%Interface.impl_witness.779)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: // CHECK:STDOUT: impl: %.loc12_21 as %Interface.ref.loc12 { // CHECK:STDOUT: %Interface.impl_witness_table = impl_witness_table (), @T.binding.as_type.as.Interface.impl.973 [concrete] // CHECK:STDOUT: %Interface.impl_witness.loc12_35.1: = impl_witness %Interface.impl_witness_table, @T.binding.as_type.as.Interface.impl.973(constants.%T.651) [symbolic = %Interface.impl_witness.loc12_35.2 (constants.%Interface.impl_witness.779)] // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: witness = %Interface.impl_witness.loc12_35.1 // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic impl @T.binding.as_type.as.Interface.impl.5eb(%T.loc13_14.1: %J.type) { // CHECK:STDOUT: %T.loc13_14.2: %J.type = symbolic_binding T, 0 [symbolic = %T.loc13_14.2 (constants.%T.612)] // CHECK:STDOUT: %T.binding.as_type: type = symbolic_binding_type T, 0, %T.loc13_14.2 [symbolic = %T.binding.as_type (constants.%T.binding.as_type.50b)] // CHECK:STDOUT: %Interface.impl_witness.loc13_35.2: = impl_witness %Interface.impl_witness_table, @T.binding.as_type.as.Interface.impl.5eb(%T.loc13_14.2) [symbolic = %Interface.impl_witness.loc13_35.2 (constants.%Interface.impl_witness.8e2)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: // CHECK:STDOUT: impl: %.loc13_21 as %Interface.ref.loc13 { // CHECK:STDOUT: %Interface.impl_witness_table = impl_witness_table (), @T.binding.as_type.as.Interface.impl.5eb [concrete] // CHECK:STDOUT: %Interface.impl_witness.loc13_35.1: = impl_witness %Interface.impl_witness_table, @T.binding.as_type.as.Interface.impl.5eb(constants.%T.612) [symbolic = %Interface.impl_witness.loc13_35.2 (constants.%Interface.impl_witness.8e2)] // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: witness = %Interface.impl_witness.loc13_35.1 // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic impl @T.binding.as_type.as.Interface.impl.edf(%T.loc14_14.1: %K.type) { // CHECK:STDOUT: %T.loc14_14.2: %K.type = symbolic_binding T, 0 [symbolic = %T.loc14_14.2 (constants.%T.9b2)] // CHECK:STDOUT: %T.binding.as_type: type = symbolic_binding_type T, 0, %T.loc14_14.2 [symbolic = %T.binding.as_type (constants.%T.binding.as_type.bec)] // CHECK:STDOUT: %Interface.impl_witness.loc14_35.2: = impl_witness %Interface.impl_witness_table, @T.binding.as_type.as.Interface.impl.edf(%T.loc14_14.2) [symbolic = %Interface.impl_witness.loc14_35.2 (constants.%Interface.impl_witness.84e)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: // CHECK:STDOUT: impl: %.loc14_21 as %Interface.ref.loc14 { // CHECK:STDOUT: %Interface.impl_witness_table = impl_witness_table (), @T.binding.as_type.as.Interface.impl.edf [concrete] // CHECK:STDOUT: %Interface.impl_witness.loc14_35.1: = impl_witness %Interface.impl_witness_table, @T.binding.as_type.as.Interface.impl.edf(constants.%T.9b2) [symbolic = %Interface.impl_witness.loc14_35.2 (constants.%Interface.impl_witness.84e)] // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: witness = %Interface.impl_witness.loc14_35.1 // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic impl @T.binding.as_type.as.Interface.impl.410(%T.loc15_14.1: %L.type) { // CHECK:STDOUT: %T.loc15_14.2: %L.type = symbolic_binding T, 0 [symbolic = %T.loc15_14.2 (constants.%T.0fc)] // CHECK:STDOUT: %T.binding.as_type: type = symbolic_binding_type T, 0, %T.loc15_14.2 [symbolic = %T.binding.as_type (constants.%T.binding.as_type.29d)] // CHECK:STDOUT: %Interface.impl_witness.loc15_35.2: = impl_witness %Interface.impl_witness_table, @T.binding.as_type.as.Interface.impl.410(%T.loc15_14.2) [symbolic = %Interface.impl_witness.loc15_35.2 (constants.%Interface.impl_witness.8e9)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: // CHECK:STDOUT: impl: %.loc15_21 as %Interface.ref.loc15 { // CHECK:STDOUT: %Interface.impl_witness_table = impl_witness_table (), @T.binding.as_type.as.Interface.impl.410 [concrete] // CHECK:STDOUT: %Interface.impl_witness.loc15_35.1: = impl_witness %Interface.impl_witness_table, @T.binding.as_type.as.Interface.impl.410(constants.%T.0fc) [symbolic = %Interface.impl_witness.loc15_35.2 (constants.%Interface.impl_witness.8e9)] // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: witness = %Interface.impl_witness.loc15_35.1 // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @Interface.WithSelf(constants.%Self.887) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @I.WithSelf(constants.%Self.ab9) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @J.WithSelf(constants.%Self.8a1) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @K.WithSelf(constants.%Self.44d) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @L.WithSelf(constants.%Self.cbc) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @T.binding.as_type.as.Interface.impl.973(constants.%T.651) { // CHECK:STDOUT: %T.loc12_14.2 => constants.%T.651 // CHECK:STDOUT: %T.binding.as_type => constants.%T.binding.as_type.3af // CHECK:STDOUT: %Interface.impl_witness.loc12_35.2 => constants.%Interface.impl_witness.779 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @T.binding.as_type.as.Interface.impl.5eb(constants.%T.612) { // CHECK:STDOUT: %T.loc13_14.2 => constants.%T.612 // CHECK:STDOUT: %T.binding.as_type => constants.%T.binding.as_type.50b // CHECK:STDOUT: %Interface.impl_witness.loc13_35.2 => constants.%Interface.impl_witness.8e2 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @T.binding.as_type.as.Interface.impl.edf(constants.%T.9b2) { // CHECK:STDOUT: %T.loc14_14.2 => constants.%T.9b2 // CHECK:STDOUT: %T.binding.as_type => constants.%T.binding.as_type.bec // CHECK:STDOUT: %Interface.impl_witness.loc14_35.2 => constants.%Interface.impl_witness.84e // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @T.binding.as_type.as.Interface.impl.410(constants.%T.0fc) { // CHECK:STDOUT: %T.loc15_14.2 => constants.%T.0fc // CHECK:STDOUT: %T.binding.as_type => constants.%T.binding.as_type.29d // CHECK:STDOUT: %Interface.impl_witness.loc15_35.2 => constants.%Interface.impl_witness.8e9 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @Interface.WithSelf(constants.%Interface.facet.9bb) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @Interface.WithSelf(constants.%Interface.facet.08d) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @Interface.WithSelf(constants.%Interface.facet.842) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @Interface.WithSelf(constants.%Interface.facet.f9d) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- fail_same_self_and_interface_redefined.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %I.type: type = facet_type <@I> [concrete] // CHECK:STDOUT: %Self.ab9: %I.type = symbolic_binding Self, 0 [symbolic] // CHECK:STDOUT: %J.type: type = facet_type <@J> [concrete] // CHECK:STDOUT: %Self.8a1: %J.type = symbolic_binding Self, 0 [symbolic] // CHECK:STDOUT: %type: type = facet_type [concrete] // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self] // CHECK:STDOUT: %pattern_type: type = pattern_type %I.type [concrete] // CHECK:STDOUT: %T: %I.type = symbolic_binding T, 0 [symbolic] // CHECK:STDOUT: %T.binding.as_type: type = symbolic_binding_type T, 0, %T [symbolic] // CHECK:STDOUT: %J.impl_witness: = impl_witness @T.binding.as_type.as.J.impl.c4ee06.1.%J.impl_witness_table, @T.binding.as_type.as.J.impl.c4ee06.1(%T) [symbolic] // CHECK:STDOUT: %J.facet: %J.type = facet_value %T.binding.as_type, (%J.impl_witness) [symbolic] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .I = %I.decl // CHECK:STDOUT: .J = %J.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %I.decl: type = interface_decl @I [concrete = constants.%I.type] {} {} // CHECK:STDOUT: %J.decl: type = interface_decl @J [concrete = constants.%J.type] {} {} // CHECK:STDOUT: impl_decl @T.binding.as_type.as.J.impl.c4ee06.1 [concrete] { // CHECK:STDOUT: %T.patt: %pattern_type = symbolic_binding_pattern T, 0 [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %T.ref: %I.type = name_ref T, %T.loc7_14.1 [symbolic = %T.loc7_14.2 (constants.%T)] // CHECK:STDOUT: %T.as_type: type = facet_access_type %T.ref [symbolic = %T.binding.as_type (constants.%T.binding.as_type)] // CHECK:STDOUT: %.loc7_21: type = converted %T.ref, %T.as_type [symbolic = %T.binding.as_type (constants.%T.binding.as_type)] // CHECK:STDOUT: %J.ref: type = name_ref J, file.%J.decl [concrete = constants.%J.type] // CHECK:STDOUT: %.loc7_18: type = splice_block %I.ref [concrete = constants.%I.type] { // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %I.ref: type = name_ref I, file.%I.decl [concrete = constants.%I.type] // CHECK:STDOUT: } // CHECK:STDOUT: %T.loc7_14.1: %I.type = symbolic_binding T, 0 [symbolic = %T.loc7_14.2 (constants.%T)] // CHECK:STDOUT: } // CHECK:STDOUT: impl_decl @T.binding.as_type.as.J.impl.c4ee06.2 [concrete] { // CHECK:STDOUT: %T.patt: %pattern_type = symbolic_binding_pattern T, 0 [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %T.ref: %I.type = name_ref T, %T.loc15_14.1 [symbolic = %T.loc15_14.2 (constants.%T)] // CHECK:STDOUT: %T.as_type: type = facet_access_type %T.ref [symbolic = %T.binding.as_type (constants.%T.binding.as_type)] // CHECK:STDOUT: %.loc15_21: type = converted %T.ref, %T.as_type [symbolic = %T.binding.as_type (constants.%T.binding.as_type)] // CHECK:STDOUT: %J.ref: type = name_ref J, file.%J.decl [concrete = constants.%J.type] // CHECK:STDOUT: %.loc15_18: type = splice_block %I.ref [concrete = constants.%I.type] { // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %I.ref: type = name_ref I, file.%I.decl [concrete = constants.%I.type] // CHECK:STDOUT: } // CHECK:STDOUT: %T.loc15_14.1: %I.type = symbolic_binding T, 0 [symbolic = %T.loc15_14.2 (constants.%T)] // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: interface @I { // CHECK:STDOUT: %Self: %I.type = symbolic_binding Self, 0 [symbolic = constants.%Self.ab9] // CHECK:STDOUT: %I.WithSelf.decl = interface_with_self_decl @I [concrete] // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = %Self // CHECK:STDOUT: witness = () // CHECK:STDOUT: // CHECK:STDOUT: !requires: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: interface @J { // CHECK:STDOUT: %Self: %J.type = symbolic_binding Self, 0 [symbolic = constants.%Self.8a1] // CHECK:STDOUT: %J.WithSelf.decl = interface_with_self_decl @J [concrete] // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = %Self // CHECK:STDOUT: witness = () // CHECK:STDOUT: // CHECK:STDOUT: !requires: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic impl @T.binding.as_type.as.J.impl.c4ee06.1(%T.loc7_14.1: %I.type) { // CHECK:STDOUT: %T.loc7_14.2: %I.type = symbolic_binding T, 0 [symbolic = %T.loc7_14.2 (constants.%T)] // CHECK:STDOUT: %T.binding.as_type: type = symbolic_binding_type T, 0, %T.loc7_14.2 [symbolic = %T.binding.as_type (constants.%T.binding.as_type)] // CHECK:STDOUT: %J.impl_witness.loc7_28.2: = impl_witness %J.impl_witness_table, @T.binding.as_type.as.J.impl.c4ee06.1(%T.loc7_14.2) [symbolic = %J.impl_witness.loc7_28.2 (constants.%J.impl_witness)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: // CHECK:STDOUT: impl: %.loc7_21 as %J.ref { // CHECK:STDOUT: %J.impl_witness_table = impl_witness_table (), @T.binding.as_type.as.J.impl.c4ee06.1 [concrete] // CHECK:STDOUT: %J.impl_witness.loc7_28.1: = impl_witness %J.impl_witness_table, @T.binding.as_type.as.J.impl.c4ee06.1(constants.%T) [symbolic = %J.impl_witness.loc7_28.2 (constants.%J.impl_witness)] // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: witness = %J.impl_witness.loc7_28.1 // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic impl @T.binding.as_type.as.J.impl.c4ee06.2(%T.loc15_14.1: %I.type) { // CHECK:STDOUT: %T.loc15_14.2: %I.type = symbolic_binding T, 0 [symbolic = %T.loc15_14.2 (constants.%T)] // CHECK:STDOUT: %T.binding.as_type: type = symbolic_binding_type T, 0, %T.loc15_14.2 [symbolic = %T.binding.as_type (constants.%T.binding.as_type)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: // CHECK:STDOUT: impl: %.loc15_21 as %J.ref { // CHECK:STDOUT: !members: // CHECK:STDOUT: witness = // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @I.WithSelf(constants.%Self.ab9) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @J.WithSelf(constants.%Self.8a1) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @T.binding.as_type.as.J.impl.c4ee06.1(constants.%T) { // CHECK:STDOUT: %T.loc7_14.2 => constants.%T // CHECK:STDOUT: %T.binding.as_type => constants.%T.binding.as_type // CHECK:STDOUT: %J.impl_witness.loc7_28.2 => constants.%J.impl_witness // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @J.WithSelf(constants.%J.facet) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @T.binding.as_type.as.J.impl.c4ee06.2(constants.%T) { // CHECK:STDOUT: %T.loc15_14.2 => constants.%T // CHECK:STDOUT: %T.binding.as_type => constants.%T.binding.as_type // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- fail_same_type_different_spelling.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %C: type = class_type @C [concrete] // CHECK:STDOUT: %I.type: type = facet_type <@I> [concrete] // CHECK:STDOUT: %Self: %I.type = symbolic_binding Self, 0 [symbolic] // CHECK:STDOUT: %I.impl_witness.6465e0.1: = impl_witness @C.as.I.impl.e47215.1.%I.impl_witness_table [concrete] // CHECK:STDOUT: %I.facet.f94759.1: %I.type = facet_value %C, (%I.impl_witness.6465e0.1) [concrete] // CHECK:STDOUT: %tuple.type: type = tuple_type (type, type) [concrete] // CHECK:STDOUT: %tuple: %tuple.type = tuple_value (%C, %C) [concrete] // CHECK:STDOUT: %int_0: Core.IntLiteral = int_value 0 [concrete] // CHECK:STDOUT: %I.impl_witness.6465e0.2: = impl_witness @C.as.I.impl.e47215.2.%I.impl_witness_table [concrete] // CHECK:STDOUT: %I.facet.f94759.2: %I.type = facet_value %C, (%I.impl_witness.6465e0.2) [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .C = %C.decl // CHECK:STDOUT: .I = %I.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %C.decl: type = class_decl @C [concrete = constants.%C] {} {} // CHECK:STDOUT: %I.decl: type = interface_decl @I [concrete = constants.%I.type] {} {} // CHECK:STDOUT: impl_decl @C.as.I.impl.e47215.1 [concrete] {} { // CHECK:STDOUT: %C.ref: type = name_ref C, file.%C.decl [concrete = constants.%C] // CHECK:STDOUT: %I.ref: type = name_ref I, file.%I.decl [concrete = constants.%I.type] // CHECK:STDOUT: } // CHECK:STDOUT: impl_decl @C.as.I.impl.e47215.2 [concrete] {} { // CHECK:STDOUT: %C.ref.loc17_7: type = name_ref C, file.%C.decl [concrete = constants.%C] // CHECK:STDOUT: %C.ref.loc17_10: type = name_ref C, file.%C.decl [concrete = constants.%C] // CHECK:STDOUT: %.loc17_11.1: %tuple.type = tuple_literal (%C.ref.loc17_7, %C.ref.loc17_10) [concrete = constants.%tuple] // CHECK:STDOUT: %int_0: Core.IntLiteral = int_value 0 [concrete = constants.%int_0] // CHECK:STDOUT: %tuple: %tuple.type = tuple_value (%C.ref.loc17_7, %C.ref.loc17_10) [concrete = constants.%tuple] // CHECK:STDOUT: %.loc17_11.2: %tuple.type = converted %.loc17_11.1, %tuple [concrete = constants.%tuple] // CHECK:STDOUT: %tuple.elem0: type = tuple_access %.loc17_11.2, element0 [concrete = constants.%C] // CHECK:STDOUT: %I.ref: type = name_ref I, file.%I.decl [concrete = constants.%I.type] // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: interface @I { // CHECK:STDOUT: %Self: %I.type = symbolic_binding Self, 0 [symbolic = constants.%Self] // CHECK:STDOUT: %I.WithSelf.decl = interface_with_self_decl @I [concrete] // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = %Self // CHECK:STDOUT: witness = () // CHECK:STDOUT: // CHECK:STDOUT: !requires: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: impl @C.as.I.impl.e47215.1: %C.ref as %I.ref { // CHECK:STDOUT: %I.impl_witness_table = impl_witness_table (), @C.as.I.impl.e47215.1 [concrete] // CHECK:STDOUT: %I.impl_witness: = impl_witness %I.impl_witness_table [concrete = constants.%I.impl_witness.6465e0.1] // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: witness = %I.impl_witness // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: impl @C.as.I.impl.e47215.2: %tuple.elem0 as %I.ref { // CHECK:STDOUT: %I.impl_witness_table = impl_witness_table (), @C.as.I.impl.e47215.2 [concrete] // CHECK:STDOUT: %I.impl_witness: = impl_witness %I.impl_witness_table [concrete = constants.%I.impl_witness.6465e0.2] // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: witness = %I.impl_witness // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @C; // CHECK:STDOUT: // CHECK:STDOUT: specific @I.WithSelf(constants.%Self) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @I.WithSelf(constants.%I.facet.f94759.1) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @I.WithSelf(constants.%I.facet.f94759.2) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- fail_redefinition_generic_regions.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %I.type: type = facet_type <@I> [concrete] // CHECK:STDOUT: %Self: %I.type = symbolic_binding Self, 0 [symbolic] // CHECK:STDOUT: %type: type = facet_type [concrete] // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self] // CHECK:STDOUT: %pattern_type.98f: type = pattern_type type [concrete] // CHECK:STDOUT: %T: type = symbolic_binding T, 0 [symbolic] // CHECK:STDOUT: %I.impl_witness: = impl_witness @T.as.I.impl.f7b5a3.1.%I.impl_witness_table, @T.as.I.impl.f7b5a3.1(%T) [symbolic] // CHECK:STDOUT: %T.as.I.impl.A.type: type = fn_type @T.as.I.impl.A, @T.as.I.impl.f7b5a3.1(%T) [symbolic] // CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] // CHECK:STDOUT: %T.as.I.impl.A: %T.as.I.impl.A.type = struct_value () [symbolic] // CHECK:STDOUT: %I.facet: %I.type = facet_value %T, (%I.impl_witness) [symbolic] // CHECK:STDOUT: %T.as.I.impl.B.type: type = fn_type @T.as.I.impl.B, @T.as.I.impl.f7b5a3.2(%T) [symbolic] // CHECK:STDOUT: %T.as.I.impl.B: %T.as.I.impl.B.type = struct_value () [symbolic] // CHECK:STDOUT: %empty_tuple: %empty_tuple.type = tuple_value () [concrete] // CHECK:STDOUT: %.262: Core.Form = init_form %empty_tuple.type [concrete] // CHECK:STDOUT: %pattern_type.cb1: type = pattern_type %empty_tuple.type [concrete] // CHECK:STDOUT: %T.as.I.impl.C.type: type = fn_type @T.as.I.impl.C, @T.as.I.impl.f7b5a3.2(%T) [symbolic] // CHECK:STDOUT: %T.as.I.impl.C: %T.as.I.impl.C.type = struct_value () [symbolic] // CHECK:STDOUT: %T.as.I.impl.D.type: type = fn_type @T.as.I.impl.D, @T.as.I.impl.f7b5a3.2(%T) [symbolic] // CHECK:STDOUT: %T.as.I.impl.D: %T.as.I.impl.D.type = struct_value () [symbolic] // CHECK:STDOUT: %T.as.I.impl.C.specific_fn: = specific_function %T.as.I.impl.C, @T.as.I.impl.C(%T) [symbolic] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .I = %I.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %I.decl: type = interface_decl @I [concrete = constants.%I.type] {} {} // CHECK:STDOUT: impl_decl @T.as.I.impl.f7b5a3.1 [concrete] { // CHECK:STDOUT: %T.patt: %pattern_type.98f = symbolic_binding_pattern T, 0 [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %T.ref: type = name_ref T, %T.loc4_14.2 [symbolic = %T.loc4_14.1 (constants.%T)] // CHECK:STDOUT: %I.ref: type = name_ref I, file.%I.decl [concrete = constants.%I.type] // CHECK:STDOUT: %.loc4_18.1: type = splice_block %.loc4_18.2 [concrete = type] { // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %.loc4_18.2: type = type_literal type [concrete = type] // CHECK:STDOUT: } // CHECK:STDOUT: %T.loc4_14.2: type = symbolic_binding T, 0 [symbolic = %T.loc4_14.1 (constants.%T)] // CHECK:STDOUT: } // CHECK:STDOUT: impl_decl @T.as.I.impl.f7b5a3.2 [concrete] { // CHECK:STDOUT: %T.patt: %pattern_type.98f = symbolic_binding_pattern T, 0 [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %T.ref: type = name_ref T, %T.loc15_14.2 [symbolic = %T.loc15_14.1 (constants.%T)] // CHECK:STDOUT: %I.ref: type = name_ref I, file.%I.decl [concrete = constants.%I.type] // CHECK:STDOUT: %.loc15_18.1: type = splice_block %.loc15_18.2 [concrete = type] { // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %.loc15_18.2: type = type_literal type [concrete = type] // CHECK:STDOUT: } // CHECK:STDOUT: %T.loc15_14.2: type = symbolic_binding T, 0 [symbolic = %T.loc15_14.1 (constants.%T)] // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: interface @I { // CHECK:STDOUT: %Self: %I.type = symbolic_binding Self, 0 [symbolic = constants.%Self] // CHECK:STDOUT: %I.WithSelf.decl = interface_with_self_decl @I [concrete] // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = %Self // CHECK:STDOUT: witness = () // CHECK:STDOUT: // CHECK:STDOUT: !requires: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic impl @T.as.I.impl.f7b5a3.1(%T.loc4_14.2: type) { // CHECK:STDOUT: %T.loc4_14.1: type = symbolic_binding T, 0 [symbolic = %T.loc4_14.1 (constants.%T)] // CHECK:STDOUT: %I.impl_witness.loc4_31.2: = impl_witness %I.impl_witness_table, @T.as.I.impl.f7b5a3.1(%T.loc4_14.1) [symbolic = %I.impl_witness.loc4_31.2 (constants.%I.impl_witness)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %T.as.I.impl.A.type: type = fn_type @T.as.I.impl.A, @T.as.I.impl.f7b5a3.1(%T.loc4_14.1) [symbolic = %T.as.I.impl.A.type (constants.%T.as.I.impl.A.type)] // CHECK:STDOUT: %T.as.I.impl.A: @T.as.I.impl.f7b5a3.1.%T.as.I.impl.A.type (%T.as.I.impl.A.type) = struct_value () [symbolic = %T.as.I.impl.A (constants.%T.as.I.impl.A)] // CHECK:STDOUT: // CHECK:STDOUT: impl: %T.ref as %I.ref { // CHECK:STDOUT: %T.as.I.impl.A.decl: @T.as.I.impl.f7b5a3.1.%T.as.I.impl.A.type (%T.as.I.impl.A.type) = fn_decl @T.as.I.impl.A [symbolic = @T.as.I.impl.f7b5a3.1.%T.as.I.impl.A (constants.%T.as.I.impl.A)] {} {} // CHECK:STDOUT: %I.impl_witness_table = impl_witness_table (), @T.as.I.impl.f7b5a3.1 [concrete] // CHECK:STDOUT: %I.impl_witness.loc4_31.1: = impl_witness %I.impl_witness_table, @T.as.I.impl.f7b5a3.1(constants.%T) [symbolic = %I.impl_witness.loc4_31.2 (constants.%I.impl_witness)] // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .A = %T.as.I.impl.A.decl // CHECK:STDOUT: witness = %I.impl_witness.loc4_31.1 // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic impl @T.as.I.impl.f7b5a3.2(%T.loc15_14.2: type) { // CHECK:STDOUT: %T.loc15_14.1: type = symbolic_binding T, 0 [symbolic = %T.loc15_14.1 (constants.%T)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %T.as.I.impl.B.type: type = fn_type @T.as.I.impl.B, @T.as.I.impl.f7b5a3.2(%T.loc15_14.1) [symbolic = %T.as.I.impl.B.type (constants.%T.as.I.impl.B.type)] // CHECK:STDOUT: %T.as.I.impl.B: @T.as.I.impl.f7b5a3.2.%T.as.I.impl.B.type (%T.as.I.impl.B.type) = struct_value () [symbolic = %T.as.I.impl.B (constants.%T.as.I.impl.B)] // CHECK:STDOUT: %T.as.I.impl.C.type: type = fn_type @T.as.I.impl.C, @T.as.I.impl.f7b5a3.2(%T.loc15_14.1) [symbolic = %T.as.I.impl.C.type (constants.%T.as.I.impl.C.type)] // CHECK:STDOUT: %T.as.I.impl.C: @T.as.I.impl.f7b5a3.2.%T.as.I.impl.C.type (%T.as.I.impl.C.type) = struct_value () [symbolic = %T.as.I.impl.C (constants.%T.as.I.impl.C)] // CHECK:STDOUT: %T.as.I.impl.D.type: type = fn_type @T.as.I.impl.D, @T.as.I.impl.f7b5a3.2(%T.loc15_14.1) [symbolic = %T.as.I.impl.D.type (constants.%T.as.I.impl.D.type)] // CHECK:STDOUT: %T.as.I.impl.D: @T.as.I.impl.f7b5a3.2.%T.as.I.impl.D.type (%T.as.I.impl.D.type) = struct_value () [symbolic = %T.as.I.impl.D (constants.%T.as.I.impl.D)] // CHECK:STDOUT: // CHECK:STDOUT: impl: %T.ref as %I.ref { // CHECK:STDOUT: %T.as.I.impl.B.decl: @T.as.I.impl.f7b5a3.2.%T.as.I.impl.B.type (%T.as.I.impl.B.type) = fn_decl @T.as.I.impl.B [symbolic = @T.as.I.impl.f7b5a3.2.%T.as.I.impl.B (constants.%T.as.I.impl.B)] {} {} // CHECK:STDOUT: %T.as.I.impl.C.decl: @T.as.I.impl.f7b5a3.2.%T.as.I.impl.C.type (%T.as.I.impl.C.type) = fn_decl @T.as.I.impl.C [symbolic = @T.as.I.impl.f7b5a3.2.%T.as.I.impl.C (constants.%T.as.I.impl.C)] { // CHECK:STDOUT: %return.patt: %pattern_type.cb1 = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: %pattern_type.cb1 = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %.loc19_14.1: %empty_tuple.type = tuple_literal () [concrete = constants.%empty_tuple] // CHECK:STDOUT: %.loc19_14.2: type = converted %.loc19_14.1, constants.%empty_tuple.type [concrete = constants.%empty_tuple.type] // CHECK:STDOUT: %.loc19_14.3: Core.Form = init_form %.loc19_14.2 [concrete = constants.%.262] // CHECK:STDOUT: %return.param: ref %empty_tuple.type = out_param call_param0 // CHECK:STDOUT: %return: ref %empty_tuple.type = return_slot %return.param // CHECK:STDOUT: } // CHECK:STDOUT: %T.as.I.impl.D.decl: @T.as.I.impl.f7b5a3.2.%T.as.I.impl.D.type (%T.as.I.impl.D.type) = fn_decl @T.as.I.impl.D [symbolic = @T.as.I.impl.f7b5a3.2.%T.as.I.impl.D (constants.%T.as.I.impl.D)] { // CHECK:STDOUT: %return.patt: %pattern_type.cb1 = return_slot_pattern [concrete] // CHECK:STDOUT: %return.param_patt: %pattern_type.cb1 = out_param_pattern %return.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %.loc20_14.1: %empty_tuple.type = tuple_literal () [concrete = constants.%empty_tuple] // CHECK:STDOUT: %.loc20_14.2: type = converted %.loc20_14.1, constants.%empty_tuple.type [concrete = constants.%empty_tuple.type] // CHECK:STDOUT: %.loc20_14.3: Core.Form = init_form %.loc20_14.2 [concrete = constants.%.262] // CHECK:STDOUT: %return.param: ref %empty_tuple.type = out_param call_param0 // CHECK:STDOUT: %return: ref %empty_tuple.type = return_slot %return.param // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .B = %T.as.I.impl.B.decl // CHECK:STDOUT: .C = %T.as.I.impl.C.decl // CHECK:STDOUT: .D = %T.as.I.impl.D.decl // CHECK:STDOUT: witness = // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic fn @T.as.I.impl.A(@T.as.I.impl.f7b5a3.1.%T.loc4_14.2: type) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: // CHECK:STDOUT: fn() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic fn @T.as.I.impl.B(@T.as.I.impl.f7b5a3.2.%T.loc15_14.2: type) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: // CHECK:STDOUT: fn() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic fn @T.as.I.impl.C(@T.as.I.impl.f7b5a3.2.%T.loc15_14.2: type) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: // CHECK:STDOUT: fn() -> out %return.param: %empty_tuple.type { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %.loc19_26.1: %empty_tuple.type = tuple_literal () [concrete = constants.%empty_tuple] // CHECK:STDOUT: %.loc19_26.2: init %empty_tuple.type = tuple_init () [concrete = constants.%empty_tuple] // CHECK:STDOUT: %.loc19_27: init %empty_tuple.type = converted %.loc19_26.1, %.loc19_26.2 [concrete = constants.%empty_tuple] // CHECK:STDOUT: return %.loc19_27 // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic fn @T.as.I.impl.D(@T.as.I.impl.f7b5a3.2.%T.loc15_14.2: type) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: %T: type = symbolic_binding T, 0 [symbolic = %T (constants.%T)] // CHECK:STDOUT: %T.as.I.impl.C.type: type = fn_type @T.as.I.impl.C, @T.as.I.impl.f7b5a3.2(%T) [symbolic = %T.as.I.impl.C.type (constants.%T.as.I.impl.C.type)] // CHECK:STDOUT: %T.as.I.impl.C: @T.as.I.impl.D.%T.as.I.impl.C.type (%T.as.I.impl.C.type) = struct_value () [symbolic = %T.as.I.impl.C (constants.%T.as.I.impl.C)] // CHECK:STDOUT: %T.as.I.impl.C.specific_fn.loc21_12.2: = specific_function %T.as.I.impl.C, @T.as.I.impl.C(%T) [symbolic = %T.as.I.impl.C.specific_fn.loc21_12.2 (constants.%T.as.I.impl.C.specific_fn)] // CHECK:STDOUT: // CHECK:STDOUT: fn() -> out %return.param: %empty_tuple.type { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %.loc21: @T.as.I.impl.D.%T.as.I.impl.C.type (%T.as.I.impl.C.type) = specific_constant @T.as.I.impl.f7b5a3.2.%T.as.I.impl.C.decl, @T.as.I.impl.f7b5a3.2(constants.%T) [symbolic = %T.as.I.impl.C (constants.%T.as.I.impl.C)] // CHECK:STDOUT: %C.ref: @T.as.I.impl.D.%T.as.I.impl.C.type (%T.as.I.impl.C.type) = name_ref C, %.loc21 [symbolic = %T.as.I.impl.C (constants.%T.as.I.impl.C)] // CHECK:STDOUT: %T.as.I.impl.C.specific_fn.loc21_12.1: = specific_function %C.ref, @T.as.I.impl.C(constants.%T) [symbolic = %T.as.I.impl.C.specific_fn.loc21_12.2 (constants.%T.as.I.impl.C.specific_fn)] // CHECK:STDOUT: %T.as.I.impl.C.call: init %empty_tuple.type = call %T.as.I.impl.C.specific_fn.loc21_12.1() // CHECK:STDOUT: return %T.as.I.impl.C.call // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @I.WithSelf(constants.%Self) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @T.as.I.impl.f7b5a3.1(constants.%T) { // CHECK:STDOUT: %T.loc4_14.1 => constants.%T // CHECK:STDOUT: %I.impl_witness.loc4_31.2 => constants.%I.impl_witness // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %T.as.I.impl.A.type => constants.%T.as.I.impl.A.type // CHECK:STDOUT: %T.as.I.impl.A => constants.%T.as.I.impl.A // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @T.as.I.impl.A(constants.%T) {} // CHECK:STDOUT: // CHECK:STDOUT: specific @I.WithSelf(constants.%I.facet) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @T.as.I.impl.f7b5a3.2(constants.%T) { // CHECK:STDOUT: %T.loc15_14.1 => constants.%T // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %T.as.I.impl.B.type => constants.%T.as.I.impl.B.type // CHECK:STDOUT: %T.as.I.impl.B => constants.%T.as.I.impl.B // CHECK:STDOUT: %T.as.I.impl.C.type => constants.%T.as.I.impl.C.type // CHECK:STDOUT: %T.as.I.impl.C => constants.%T.as.I.impl.C // CHECK:STDOUT: %T.as.I.impl.D.type => constants.%T.as.I.impl.D.type // CHECK:STDOUT: %T.as.I.impl.D => constants.%T.as.I.impl.D // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @T.as.I.impl.B(constants.%T) {} // CHECK:STDOUT: // CHECK:STDOUT: specific @T.as.I.impl.C(constants.%T) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @T.as.I.impl.D(constants.%T) {} // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/impl/impl_as.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/destroy.carbon // TODO: Add ranges and switch to "--dump-sem-ir-ranges=only". // EXTRA-ARGS: --dump-sem-ir-ranges=if-present // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/impl/impl_as.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/impl/impl_as.carbon interface Simple { fn F(); } class C { impl as Simple { fn F() { // C is a complete type here. var unused c: C = {}; } } } // CHECK:STDOUT: --- impl_as.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %Simple.type: type = facet_type <@Simple> [concrete] // CHECK:STDOUT: %Self.af2: %Simple.type = symbolic_binding Self, 0 [symbolic] // CHECK:STDOUT: %Simple.WithSelf.F.type.75b: type = fn_type @Simple.WithSelf.F, @Simple.WithSelf(%Self.af2) [symbolic] // CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] // CHECK:STDOUT: %Simple.WithSelf.F.58c: %Simple.WithSelf.F.type.75b = struct_value () [symbolic] // CHECK:STDOUT: %Simple.assoc_type: type = assoc_entity_type @Simple [concrete] // CHECK:STDOUT: %assoc0.db6: %Simple.assoc_type = assoc_entity element0, @Simple.WithSelf.%Simple.WithSelf.F.decl [concrete] // CHECK:STDOUT: %C: type = class_type @C [concrete] // CHECK:STDOUT: %Simple.impl_witness: = impl_witness @C.as.Simple.impl.%Simple.impl_witness_table [concrete] // CHECK:STDOUT: %C.as.Simple.impl.F.type: type = fn_type @C.as.Simple.impl.F [concrete] // CHECK:STDOUT: %C.as.Simple.impl.F: %C.as.Simple.impl.F.type = struct_value () [concrete] // CHECK:STDOUT: %Simple.facet: %Simple.type = facet_value %C, (%Simple.impl_witness) [concrete] // CHECK:STDOUT: %Simple.WithSelf.F.type.839: type = fn_type @Simple.WithSelf.F, @Simple.WithSelf(%Simple.facet) [concrete] // CHECK:STDOUT: %Simple.WithSelf.F.79e: %Simple.WithSelf.F.type.839 = struct_value () [concrete] // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %complete_type: = complete_type_witness %empty_struct_type [concrete] // CHECK:STDOUT: %pattern_type.7c7: type = pattern_type %C [concrete] // CHECK:STDOUT: %empty_struct: %empty_struct_type = struct_value () [concrete] // CHECK:STDOUT: %C.val: %C = struct_value () [concrete] // CHECK:STDOUT: %Destroy.type: type = facet_type <@Destroy> [concrete] // CHECK:STDOUT: %Destroy.Op.type: type = fn_type @Destroy.Op [concrete] // CHECK:STDOUT: %Destroy.Op: %Destroy.Op.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: .Destroy = %Core.Destroy // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: %Core.Destroy: type = import_ref Core//prelude/parts/destroy, Destroy, loaded [concrete = constants.%Destroy.type] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .Simple = %Simple.decl // CHECK:STDOUT: .C = %C.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %Simple.decl: type = interface_decl @Simple [concrete = constants.%Simple.type] {} {} // CHECK:STDOUT: %C.decl: type = class_decl @C [concrete = constants.%C] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: interface @Simple { // CHECK:STDOUT: %Self: %Simple.type = symbolic_binding Self, 0 [symbolic = constants.%Self.af2] // CHECK:STDOUT: %Simple.WithSelf.decl = interface_with_self_decl @Simple [concrete] // CHECK:STDOUT: // CHECK:STDOUT: !with Self: // CHECK:STDOUT: %Simple.WithSelf.F.decl: @Simple.WithSelf.%Simple.WithSelf.F.type (%Simple.WithSelf.F.type.75b) = fn_decl @Simple.WithSelf.F [symbolic = @Simple.WithSelf.%Simple.WithSelf.F (constants.%Simple.WithSelf.F.58c)] {} {} // CHECK:STDOUT: %assoc0: %Simple.assoc_type = assoc_entity element0, %Simple.WithSelf.F.decl [concrete = constants.%assoc0.db6] // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = %Self // CHECK:STDOUT: .F = @Simple.WithSelf.%assoc0 // CHECK:STDOUT: witness = (@Simple.WithSelf.%Simple.WithSelf.F.decl) // CHECK:STDOUT: // CHECK:STDOUT: !requires: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: impl @C.as.Simple.impl: %Self.ref as %Simple.ref { // CHECK:STDOUT: %C.as.Simple.impl.F.decl: %C.as.Simple.impl.F.type = fn_decl @C.as.Simple.impl.F [concrete = constants.%C.as.Simple.impl.F] {} {} // CHECK:STDOUT: %Simple.impl_witness_table = impl_witness_table (%C.as.Simple.impl.F.decl), @C.as.Simple.impl [concrete] // CHECK:STDOUT: %Simple.impl_witness: = impl_witness %Simple.impl_witness_table [concrete = constants.%Simple.impl_witness] // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .F = %C.as.Simple.impl.F.decl // CHECK:STDOUT: .C = // CHECK:STDOUT: witness = %Simple.impl_witness // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @C { // CHECK:STDOUT: impl_decl @C.as.Simple.impl [concrete] {} { // CHECK:STDOUT: %Self.ref: type = name_ref Self, constants.%C [concrete = constants.%C] // CHECK:STDOUT: %Simple.ref: type = name_ref Simple, file.%Simple.decl [concrete = constants.%Simple.type] // CHECK:STDOUT: } // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%C // CHECK:STDOUT: .Simple = // CHECK:STDOUT: .C = // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic fn @Simple.WithSelf.F(@Simple.%Self: %Simple.type) { // CHECK:STDOUT: fn(); // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @C.as.Simple.impl.F() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %c.patt: %pattern_type.7c7 = ref_binding_pattern c [concrete] // CHECK:STDOUT: %c.var_patt: %pattern_type.7c7 = var_pattern %c.patt [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %c.var: ref %C = var %c.var_patt // CHECK:STDOUT: %.loc23_26.1: %empty_struct_type = struct_literal () [concrete = constants.%empty_struct] // CHECK:STDOUT: %.loc23_26.2: init %C to %c.var = class_init () [concrete = constants.%C.val] // CHECK:STDOUT: %.loc23_7: init %C = converted %.loc23_26.1, %.loc23_26.2 [concrete = constants.%C.val] // CHECK:STDOUT: assign %c.var, %.loc23_7 // CHECK:STDOUT: %C.ref: type = name_ref C, file.%C.decl [concrete = constants.%C] // CHECK:STDOUT: %c: ref %C = ref_binding c, %c.var // CHECK:STDOUT: %Destroy.Op.bound: = bound_method %c.var, constants.%Destroy.Op // CHECK:STDOUT: %Destroy.Op.call: init %empty_tuple.type = call %Destroy.Op.bound(%c.var) // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @Destroy.Op(%self.param: ref %C) = "no_op"; // CHECK:STDOUT: // CHECK:STDOUT: specific @Simple.WithSelf(constants.%Self.af2) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: %Self => constants.%Self.af2 // CHECK:STDOUT: %Simple.WithSelf.F.type => constants.%Simple.WithSelf.F.type.75b // CHECK:STDOUT: %Simple.WithSelf.F => constants.%Simple.WithSelf.F.58c // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @Simple.WithSelf.F(constants.%Self.af2) {} // CHECK:STDOUT: // CHECK:STDOUT: specific @Simple.WithSelf(constants.%Simple.facet) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: %Self => constants.%Simple.facet // CHECK:STDOUT: %Simple.WithSelf.F.type => constants.%Simple.WithSelf.F.type.839 // CHECK:STDOUT: %Simple.WithSelf.F => constants.%Simple.WithSelf.F.79e // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @Simple.WithSelf.F(constants.%Simple.facet) {} // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/impl/impl_as_named_constraint.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/convert.carbon // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/impl/impl_as_named_constraint.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/impl/impl_as_named_constraint.carbon // --- fail_empty_constraint.carbon library "[[@TEST_NAME]]"; constraint A {} // CHECK:STDERR: fail_empty_constraint.carbon:[[@LINE+4]]:1: error: impl as 0 interfaces, expected 1 [ImplOfNotOneInterface] // CHECK:STDERR: impl () as A {} // CHECK:STDERR: ^~~~~~~~~~~~~~ // CHECK:STDERR: impl () as A {} // --- fail_too_many_interfaces_in_constraint.carbon library "[[@TEST_NAME]]"; interface A1 {} interface A2 {} constraint B { extend require impls A1; extend require impls A2; } // CHECK:STDERR: fail_too_many_interfaces_in_constraint.carbon:[[@LINE+4]]:1: error: impl as 2 interfaces, expected 1 [ImplOfNotOneInterface] // CHECK:STDERR: impl () as B {} // CHECK:STDERR: ^~~~~~~~~~~~~~ // CHECK:STDERR: impl () as B {} // --- one_extend_impls_interface_in_constraint.carbon library "[[@TEST_NAME]]"; interface A {} constraint B { extend require impls A; } impl () as B {} // --- fail_one_impls_interface_in_constraint.carbon library "[[@TEST_NAME]]"; interface A; constraint B { require impls A; } // CHECK:STDERR: fail_one_impls_interface_in_constraint.carbon:[[@LINE+4]]:1: error: impl as 0 interfaces, expected 1 [ImplOfNotOneInterface] // CHECK:STDERR: impl () as B {} // CHECK:STDERR: ^~~~~~~~~~~~~~ // CHECK:STDERR: impl () as B {} // --- nested_constraints.carbon library "[[@TEST_NAME]]"; interface A {} constraint B { extend require impls A; } constraint C { extend require impls B; } impl () as C {} // --- fail_nested_constraints_not_extend_outer.carbon library "[[@TEST_NAME]]"; interface A {} constraint B { extend require impls A; } constraint C { require impls B; } // CHECK:STDERR: fail_nested_constraints_not_extend_outer.carbon:[[@LINE+4]]:1: error: impl as 0 interfaces, expected 1 [ImplOfNotOneInterface] // CHECK:STDERR: impl () as C {} // CHECK:STDERR: ^~~~~~~~~~~~~~ // CHECK:STDERR: impl () as C {} // --- fail_nested_constraints_not_extend_inner.carbon library "[[@TEST_NAME]]"; interface A {} constraint B { require impls A; } constraint C { extend require impls B; } // CHECK:STDERR: fail_nested_constraints_not_extend_inner.carbon:[[@LINE+4]]:1: error: impl as 0 interfaces, expected 1 [ImplOfNotOneInterface] // CHECK:STDERR: impl () as C {} // CHECK:STDERR: ^~~~~~~~~~~~~~ // CHECK:STDERR: impl () as C {} // --- fail_nested_constraints_not_extend_both.carbon library "[[@TEST_NAME]]"; interface A {} constraint B { require impls A; } constraint C { require impls B; } // CHECK:STDERR: fail_nested_constraints_not_extend_both.carbon:[[@LINE+4]]:1: error: impl as 0 interfaces, expected 1 [ImplOfNotOneInterface] // CHECK:STDERR: impl () as C {} // CHECK:STDERR: ^~~~~~~~~~~~~~ // CHECK:STDERR: impl () as C {} // --- fail_duplicate_through_generic_constraint.carbon library "[[@TEST_NAME]]"; interface A(T:! type) {} constraint B(X:! type, Y:! type) { extend require impls A(Y); } // This should impl () as A({}). impl () as B((), {}) {} // CHECK:STDERR: fail_duplicate_through_generic_constraint.carbon:[[@LINE+7]]:1: error: found non-final `impl` with the same type structure as another non-final `impl` [ImplNonFinalSameTypeStructure] // CHECK:STDERR: impl () as A({}) {} // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~ // CHECK:STDERR: fail_duplicate_through_generic_constraint.carbon:[[@LINE-5]]:1: note: other `impl` here [ImplNonFinalSameTypeStructureNote] // CHECK:STDERR: impl () as B((), {}) {} // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: impl () as A({}) {} // --- fail_error_self_in_require.carbon library "[[@TEST_NAME]]"; interface A(T:! type) {} // This makes the type of `Self` into an ErrorInst. No `require` is added to // the constraint. //@dump-sem-ir-begin // // CHECK:STDERR: fail_error_self_in_require.carbon:[[@LINE+4]]:18: error: name `Undefined` not found [NameNotFound] // CHECK:STDERR: constraint B(X:! Undefined) { // CHECK:STDERR: ^~~~~~~~~ // CHECK:STDERR: constraint B(X:! Undefined) { extend require impls A(X); } //@dump-sem-ir-end impl () as B(1) {} // CHECK:STDOUT: --- fail_error_self_in_require.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %B.type: type = generic_named_constaint_type @B [concrete] // CHECK:STDOUT: %empty_struct: %B.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: %B.decl: %B.type = constraint_decl @B [concrete = constants.%empty_struct] { // CHECK:STDOUT: %X.patt: = symbolic_binding_pattern X, 0 [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: // CHECK:STDOUT: %X: = symbolic_binding X, 0 [concrete = ] // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic constraint @B(%X: ) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: // CHECK:STDOUT: constraint { // CHECK:STDOUT: %Self: = symbolic_binding Self, 1 [concrete = ] // CHECK:STDOUT: %B.WithSelf.decl = constraint_with_self_decl @B [concrete] // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = %Self // CHECK:STDOUT: .A = // CHECK:STDOUT: .X = // CHECK:STDOUT: .A = // CHECK:STDOUT: .X = // CHECK:STDOUT: has_error // CHECK:STDOUT: // CHECK:STDOUT: !requires: // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @B() {} // CHECK:STDOUT: // CHECK:STDOUT: specific @B.WithSelf(, ) {} // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/impl/impl_assoc_const.carbon ================================================ [File too large to display: 30.8 KB] ================================================ FILE: toolchain/check/testdata/impl/impl_assoc_const_with_prelude.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/full.carbon // TODO: Add ranges and switch to "--dump-sem-ir-ranges=only". // EXTRA-ARGS: --dump-sem-ir-ranges=if-present // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/impl/impl_assoc_const_with_prelude.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/impl/impl_assoc_const_with_prelude.carbon // --- same_non_type.carbon library "[[@TEST_NAME]]"; interface I { let X:! {.a: bool, .b: (i32, i32)}; } impl () as I where .X = {.a = true, .b = (1, 2)} and .X = {.a = not false, .b = (3 - 2, 4 / 2)} {} // --- fail_two_different_non_type.carbon library "[[@TEST_NAME]]"; interface I { let X:! {.a: bool, .b: (i32, i32)}; } // CHECK:STDERR: fail_two_different_non_type.carbon:[[@LINE+4]]:12: error: associated constant `.(I.X)` given two different values `{.a = true, .b = (1, 2)}` and `{.a = false, .b = (3, 4)}` [AssociatedConstantWithDifferentValues] // CHECK:STDERR: impl () as I where .X = {.a = true, .b = (1, 2)} and .X = {.a = false, .b = (3, 4)} {} // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: impl () as I where .X = {.a = true, .b = (1, 2)} and .X = {.a = false, .b = (3, 4)} {} // CHECK:STDOUT: --- same_non_type.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %I.type: type = facet_type <@I> [concrete] // CHECK:STDOUT: %Self.ab9: %I.type = symbolic_binding Self, 0 [symbolic] // CHECK:STDOUT: %Bool.type: type = fn_type @Bool [concrete] // CHECK:STDOUT: %Bool: %Bool.type = struct_value () [concrete] // CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [concrete] // CHECK:STDOUT: %Int.type: type = generic_class_type @Int [concrete] // CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] // CHECK:STDOUT: %Int.generic: %Int.type = struct_value () [concrete] // CHECK:STDOUT: %i32: type = class_type @Int, @Int(%int_32) [concrete] // CHECK:STDOUT: %tuple.type.d07: type = tuple_type (%i32, %i32) [concrete] // CHECK:STDOUT: %struct_type.a.b.fe2: type = struct_type {.a: bool, .b: %tuple.type.d07} [concrete] // CHECK:STDOUT: %I.assoc_type: type = assoc_entity_type @I [concrete] // CHECK:STDOUT: %assoc0.be0: %I.assoc_type = assoc_entity element0, @I.WithSelf.%X [concrete] // CHECK:STDOUT: %empty_tuple: %empty_tuple.type = tuple_value () [concrete] // CHECK:STDOUT: %.Self.1dc: %I.type = symbolic_binding .Self [symbolic_self] // CHECK:STDOUT: %.Self.binding.as_type: type = symbolic_binding_type .Self, %.Self.1dc [symbolic_self] // CHECK:STDOUT: %I.lookup_impl_witness: = lookup_impl_witness %.Self.1dc, @I [symbolic_self] // CHECK:STDOUT: %impl.elem0.358da: %struct_type.a.b.fe2 = impl_witness_access %I.lookup_impl_witness, element0 [symbolic_self] // CHECK:STDOUT: %true: bool = bool_literal true [concrete] // CHECK:STDOUT: %int_1.5b8: Core.IntLiteral = int_value 1 [concrete] // CHECK:STDOUT: %int_2.ecc: Core.IntLiteral = int_value 2 [concrete] // CHECK:STDOUT: %tuple.type.f94: type = tuple_type (Core.IntLiteral, Core.IntLiteral) [concrete] // CHECK:STDOUT: %tuple.ad8: %tuple.type.f94 = tuple_value (%int_1.5b8, %int_2.ecc) [concrete] // CHECK:STDOUT: %struct_type.a.b.aa4: type = struct_type {.a: bool, .b: %tuple.type.f94} [concrete] // CHECK:STDOUT: %struct.e57: %struct_type.a.b.aa4 = struct_value (%true, %tuple.ad8) [concrete] // CHECK:STDOUT: %ImplicitAs.type.cc7: type = generic_interface_type @ImplicitAs [concrete] // CHECK:STDOUT: %ImplicitAs.generic: %ImplicitAs.type.cc7 = struct_value () [concrete] // CHECK:STDOUT: %ImplicitAs.type.e8c: type = facet_type <@ImplicitAs, @ImplicitAs(%i32)> [concrete] // CHECK:STDOUT: %To: Core.IntLiteral = symbolic_binding To, 0 [symbolic] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.4e6: type = fn_type @Core.IntLiteral.as.ImplicitAs.impl.Convert, @Core.IntLiteral.as.ImplicitAs.impl(%To) [symbolic] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.3c2: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.4e6 = struct_value () [symbolic] // CHECK:STDOUT: %ImplicitAs.impl_witness.6bc: = impl_witness imports.%ImplicitAs.impl_witness_table.74f, @Core.IntLiteral.as.ImplicitAs.impl(%int_32) [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.e0d: type = fn_type @Core.IntLiteral.as.ImplicitAs.impl.Convert, @Core.IntLiteral.as.ImplicitAs.impl(%int_32) [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.e0d = struct_value () [concrete] // CHECK:STDOUT: %ImplicitAs.facet: %ImplicitAs.type.e8c = facet_value Core.IntLiteral, (%ImplicitAs.impl_witness.6bc) [concrete] // CHECK:STDOUT: %ImplicitAs.WithSelf.Convert.type.b37: type = fn_type @ImplicitAs.WithSelf.Convert, @ImplicitAs.WithSelf(%i32, %ImplicitAs.facet) [concrete] // CHECK:STDOUT: %.545: type = fn_type_with_self_type %ImplicitAs.WithSelf.Convert.type.b37, %ImplicitAs.facet [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.bound.215: = bound_method %int_1.5b8, %Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5 [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn: = specific_function %Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5, @Core.IntLiteral.as.ImplicitAs.impl.Convert(%int_32) [concrete] // CHECK:STDOUT: %bound_method.38b: = bound_method %int_1.5b8, %Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn [concrete] // CHECK:STDOUT: %int_1.5d2: %i32 = int_value 1 [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.bound.4e5: = bound_method %int_2.ecc, %Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5 [concrete] // CHECK:STDOUT: %bound_method.646: = bound_method %int_2.ecc, %Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn [concrete] // CHECK:STDOUT: %int_2.ef8: %i32 = int_value 2 [concrete] // CHECK:STDOUT: %tuple.21c: %tuple.type.d07 = tuple_value (%int_1.5d2, %int_2.ef8) [concrete] // CHECK:STDOUT: %struct.682: %struct_type.a.b.fe2 = struct_value (%true, %tuple.21c) [concrete] // CHECK:STDOUT: %false: bool = bool_literal false [concrete] // CHECK:STDOUT: %int_3: Core.IntLiteral = int_value 3 [concrete] // CHECK:STDOUT: %SubWith.type.378: type = generic_interface_type @SubWith [concrete] // CHECK:STDOUT: %SubWith.generic: %SubWith.type.378 = struct_value () [concrete] // CHECK:STDOUT: %SubWith.type.a89: type = facet_type <@SubWith, @SubWith(Core.IntLiteral)> [concrete] // CHECK:STDOUT: %SubWith.impl_witness: = impl_witness imports.%SubWith.impl_witness_table [concrete] // CHECK:STDOUT: %SubWith.facet: %SubWith.type.a89 = facet_value Core.IntLiteral, (%SubWith.impl_witness) [concrete] // CHECK:STDOUT: %SubWith.WithSelf.Op.type.7ed: type = fn_type @SubWith.WithSelf.Op, @SubWith.WithSelf(Core.IntLiteral, %SubWith.facet) [concrete] // CHECK:STDOUT: %.24b: type = fn_type_with_self_type %SubWith.WithSelf.Op.type.7ed, %SubWith.facet [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.SubWith.impl.Op.type: type = fn_type @Core.IntLiteral.as.SubWith.impl.Op [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.SubWith.impl.Op: %Core.IntLiteral.as.SubWith.impl.Op.type = struct_value () [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.SubWith.impl.Op.bound: = bound_method %int_3, %Core.IntLiteral.as.SubWith.impl.Op [concrete] // CHECK:STDOUT: %int_4: Core.IntLiteral = int_value 4 [concrete] // CHECK:STDOUT: %DivWith.type.fc9: type = generic_interface_type @DivWith [concrete] // CHECK:STDOUT: %DivWith.generic: %DivWith.type.fc9 = struct_value () [concrete] // CHECK:STDOUT: %DivWith.type.234: type = facet_type <@DivWith, @DivWith(Core.IntLiteral)> [concrete] // CHECK:STDOUT: %DivWith.impl_witness: = impl_witness imports.%DivWith.impl_witness_table [concrete] // CHECK:STDOUT: %DivWith.facet: %DivWith.type.234 = facet_value Core.IntLiteral, (%DivWith.impl_witness) [concrete] // CHECK:STDOUT: %DivWith.WithSelf.Op.type.04d: type = fn_type @DivWith.WithSelf.Op, @DivWith.WithSelf(Core.IntLiteral, %DivWith.facet) [concrete] // CHECK:STDOUT: %.d00: type = fn_type_with_self_type %DivWith.WithSelf.Op.type.04d, %DivWith.facet [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.DivWith.impl.Op.type: type = fn_type @Core.IntLiteral.as.DivWith.impl.Op [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.DivWith.impl.Op: %Core.IntLiteral.as.DivWith.impl.Op.type = struct_value () [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.DivWith.impl.Op.bound: = bound_method %int_4, %Core.IntLiteral.as.DivWith.impl.Op [concrete] // CHECK:STDOUT: %I_where.type: type = facet_type <@I where %impl.elem0.358da = %struct.682> [concrete] // CHECK:STDOUT: %I.impl_witness: = impl_witness @empty_tuple.type.as.I.impl.%I.impl_witness_table [concrete] // CHECK:STDOUT: %I.facet: %I.type = facet_value %empty_tuple.type, (%I.impl_witness) [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: .Bool = %Core.Bool // CHECK:STDOUT: .Int = %Core.Int // CHECK:STDOUT: .ImplicitAs = %Core.ImplicitAs // CHECK:STDOUT: .SubWith = %Core.SubWith // CHECK:STDOUT: .DivWith = %Core.DivWith // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: %Core.Bool: %Bool.type = import_ref Core//prelude/types/bool, Bool, loaded [concrete = constants.%Bool] // CHECK:STDOUT: %Core.Int: %Int.type = import_ref Core//prelude/types/int, Int, loaded [concrete = constants.%Int.generic] // CHECK:STDOUT: %Core.ImplicitAs: %ImplicitAs.type.cc7 = import_ref Core//prelude/operators/as, ImplicitAs, loaded [concrete = constants.%ImplicitAs.generic] // CHECK:STDOUT: %Core.import_ref.42d: @Core.IntLiteral.as.ImplicitAs.impl.%Core.IntLiteral.as.ImplicitAs.impl.Convert.type (%Core.IntLiteral.as.ImplicitAs.impl.Convert.type.4e6) = import_ref Core//prelude/types/int, loc{{\d+_\d+}}, loaded [symbolic = @Core.IntLiteral.as.ImplicitAs.impl.%Core.IntLiteral.as.ImplicitAs.impl.Convert (constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.3c2)] // CHECK:STDOUT: %ImplicitAs.impl_witness_table.74f = impl_witness_table (%Core.import_ref.42d), @Core.IntLiteral.as.ImplicitAs.impl [concrete] // CHECK:STDOUT: %Core.SubWith: %SubWith.type.378 = import_ref Core//prelude/operators/arithmetic, SubWith, loaded [concrete = constants.%SubWith.generic] // CHECK:STDOUT: %Core.import_ref.abd97d.1 = import_ref Core//prelude/operators/arithmetic, loc{{\d+_\d+}}, unloaded // CHECK:STDOUT: %Core.import_ref.d95: %Core.IntLiteral.as.SubWith.impl.Op.type = import_ref Core//prelude/operators/arithmetic, loc{{\d+_\d+}}, loaded [concrete = constants.%Core.IntLiteral.as.SubWith.impl.Op] // CHECK:STDOUT: %SubWith.impl_witness_table = impl_witness_table (%Core.import_ref.abd97d.1, %Core.import_ref.d95), @Core.IntLiteral.as.SubWith.impl [concrete] // CHECK:STDOUT: %Core.DivWith: %DivWith.type.fc9 = import_ref Core//prelude/operators/arithmetic, DivWith, loaded [concrete = constants.%DivWith.generic] // CHECK:STDOUT: %Core.import_ref.abd97d.2 = import_ref Core//prelude/operators/arithmetic, loc{{\d+_\d+}}, unloaded // CHECK:STDOUT: %Core.import_ref.93eb: %Core.IntLiteral.as.DivWith.impl.Op.type = import_ref Core//prelude/operators/arithmetic, loc{{\d+_\d+}}, loaded [concrete = constants.%Core.IntLiteral.as.DivWith.impl.Op] // CHECK:STDOUT: %DivWith.impl_witness_table = impl_witness_table (%Core.import_ref.abd97d.2, %Core.import_ref.93eb), @Core.IntLiteral.as.DivWith.impl [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .I = %I.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %I.decl: type = interface_decl @I [concrete = constants.%I.type] {} {} // CHECK:STDOUT: impl_decl @empty_tuple.type.as.I.impl [concrete] {} { // CHECK:STDOUT: %.loc6_7.1: %empty_tuple.type = tuple_literal () [concrete = constants.%empty_tuple] // CHECK:STDOUT: %.loc6_7.2: type = converted %.loc6_7.1, constants.%empty_tuple.type [concrete = constants.%empty_tuple.type] // CHECK:STDOUT: %I.ref: type = name_ref I, file.%I.decl [concrete = constants.%I.type] // CHECK:STDOUT: %.Self: %I.type = symbolic_binding .Self [symbolic_self = constants.%.Self.1dc] // CHECK:STDOUT: %.Self.ref.loc6_20: %I.type = name_ref .Self, %.Self [symbolic_self = constants.%.Self.1dc] // CHECK:STDOUT: %.Self.as_type.loc6_20: type = facet_access_type %.Self.ref.loc6_20 [symbolic_self = constants.%.Self.binding.as_type] // CHECK:STDOUT: %.loc6_20: type = converted %.Self.ref.loc6_20, %.Self.as_type.loc6_20 [symbolic_self = constants.%.Self.binding.as_type] // CHECK:STDOUT: %X.ref.loc6_20: %I.assoc_type = name_ref X, @X.%assoc0 [concrete = constants.%assoc0.be0] // CHECK:STDOUT: %impl.elem0.loc6_20: %struct_type.a.b.fe2 = impl_witness_access constants.%I.lookup_impl_witness, element0 [symbolic_self = constants.%impl.elem0.358da] // CHECK:STDOUT: %true: bool = bool_literal true [concrete = constants.%true] // CHECK:STDOUT: %int_1: Core.IntLiteral = int_value 1 [concrete = constants.%int_1.5b8] // CHECK:STDOUT: %int_2.loc6_46: Core.IntLiteral = int_value 2 [concrete = constants.%int_2.ecc] // CHECK:STDOUT: %.loc6_47.1: %tuple.type.f94 = tuple_literal (%int_1, %int_2.loc6_46) [concrete = constants.%tuple.ad8] // CHECK:STDOUT: %.loc6_48.1: %struct_type.a.b.aa4 = struct_literal (%true, %.loc6_47.1) [concrete = constants.%struct.e57] // CHECK:STDOUT: %impl.elem0.loc6_47.1: %.545 = impl_witness_access constants.%ImplicitAs.impl_witness.6bc, element0 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5] // CHECK:STDOUT: %bound_method.loc6_47.1: = bound_method %int_1, %impl.elem0.loc6_47.1 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.bound.215] // CHECK:STDOUT: %specific_fn.loc6_47.1: = specific_function %impl.elem0.loc6_47.1, @Core.IntLiteral.as.ImplicitAs.impl.Convert(constants.%int_32) [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn] // CHECK:STDOUT: %bound_method.loc6_47.2: = bound_method %int_1, %specific_fn.loc6_47.1 [concrete = constants.%bound_method.38b] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc6_47.1: init %i32 = call %bound_method.loc6_47.2(%int_1) [concrete = constants.%int_1.5d2] // CHECK:STDOUT: %.loc6_47.2: %i32 = value_of_initializer %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc6_47.1 [concrete = constants.%int_1.5d2] // CHECK:STDOUT: %.loc6_47.3: %i32 = converted %int_1, %.loc6_47.2 [concrete = constants.%int_1.5d2] // CHECK:STDOUT: %impl.elem0.loc6_47.2: %.545 = impl_witness_access constants.%ImplicitAs.impl_witness.6bc, element0 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5] // CHECK:STDOUT: %bound_method.loc6_47.3: = bound_method %int_2.loc6_46, %impl.elem0.loc6_47.2 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.bound.4e5] // CHECK:STDOUT: %specific_fn.loc6_47.2: = specific_function %impl.elem0.loc6_47.2, @Core.IntLiteral.as.ImplicitAs.impl.Convert(constants.%int_32) [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn] // CHECK:STDOUT: %bound_method.loc6_47.4: = bound_method %int_2.loc6_46, %specific_fn.loc6_47.2 [concrete = constants.%bound_method.646] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc6_47.2: init %i32 = call %bound_method.loc6_47.4(%int_2.loc6_46) [concrete = constants.%int_2.ef8] // CHECK:STDOUT: %.loc6_47.4: %i32 = value_of_initializer %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc6_47.2 [concrete = constants.%int_2.ef8] // CHECK:STDOUT: %.loc6_47.5: %i32 = converted %int_2.loc6_46, %.loc6_47.4 [concrete = constants.%int_2.ef8] // CHECK:STDOUT: %tuple.loc6_47: %tuple.type.d07 = tuple_value (%.loc6_47.3, %.loc6_47.5) [concrete = constants.%tuple.21c] // CHECK:STDOUT: %.loc6_48.2: %tuple.type.d07 = converted %.loc6_47.1, %tuple.loc6_47 [concrete = constants.%tuple.21c] // CHECK:STDOUT: %struct.loc6_48: %struct_type.a.b.fe2 = struct_value (%true, %.loc6_48.2) [concrete = constants.%struct.682] // CHECK:STDOUT: %.loc6_48.3: %struct_type.a.b.fe2 = converted %.loc6_48.1, %struct.loc6_48 [concrete = constants.%struct.682] // CHECK:STDOUT: %.Self.ref.loc6_54: %I.type = name_ref .Self, %.Self [symbolic_self = constants.%.Self.1dc] // CHECK:STDOUT: %.Self.as_type.loc6_54: type = facet_access_type %.Self.ref.loc6_54 [symbolic_self = constants.%.Self.binding.as_type] // CHECK:STDOUT: %.loc6_54: type = converted %.Self.ref.loc6_54, %.Self.as_type.loc6_54 [symbolic_self = constants.%.Self.binding.as_type] // CHECK:STDOUT: %X.ref.loc6_54: %I.assoc_type = name_ref X, @X.%assoc0 [concrete = constants.%assoc0.be0] // CHECK:STDOUT: %impl.elem0.loc6_54: %struct_type.a.b.fe2 = impl_witness_access constants.%I.lookup_impl_witness, element0 [symbolic_self = constants.%impl.elem0.358da] // CHECK:STDOUT: %impl.elem0.subst: %struct_type.a.b.fe2 = impl_witness_access_substituted %impl.elem0.loc6_54, %.loc6_48.3 [concrete = constants.%struct.682] // CHECK:STDOUT: %false: bool = bool_literal false [concrete = constants.%false] // CHECK:STDOUT: %.loc6_65: bool = not %false [concrete = constants.%true] // CHECK:STDOUT: %int_3: Core.IntLiteral = int_value 3 [concrete = constants.%int_3] // CHECK:STDOUT: %int_2.loc6_86: Core.IntLiteral = int_value 2 [concrete = constants.%int_2.ecc] // CHECK:STDOUT: %impl.elem1.loc6_84: %.24b = impl_witness_access constants.%SubWith.impl_witness, element1 [concrete = constants.%Core.IntLiteral.as.SubWith.impl.Op] // CHECK:STDOUT: %bound_method.loc6_84: = bound_method %int_3, %impl.elem1.loc6_84 [concrete = constants.%Core.IntLiteral.as.SubWith.impl.Op.bound] // CHECK:STDOUT: %Core.IntLiteral.as.SubWith.impl.Op.call: init Core.IntLiteral = call %bound_method.loc6_84(%int_3, %int_2.loc6_86) [concrete = constants.%int_1.5b8] // CHECK:STDOUT: %int_4: Core.IntLiteral = int_value 4 [concrete = constants.%int_4] // CHECK:STDOUT: %int_2.loc6_93: Core.IntLiteral = int_value 2 [concrete = constants.%int_2.ecc] // CHECK:STDOUT: %impl.elem1.loc6_91: %.d00 = impl_witness_access constants.%DivWith.impl_witness, element1 [concrete = constants.%Core.IntLiteral.as.DivWith.impl.Op] // CHECK:STDOUT: %bound_method.loc6_91: = bound_method %int_4, %impl.elem1.loc6_91 [concrete = constants.%Core.IntLiteral.as.DivWith.impl.Op.bound] // CHECK:STDOUT: %Core.IntLiteral.as.DivWith.impl.Op.call: init Core.IntLiteral = call %bound_method.loc6_91(%int_4, %int_2.loc6_93) [concrete = constants.%int_2.ecc] // CHECK:STDOUT: %.loc6_94.1: %tuple.type.f94 = tuple_literal (%Core.IntLiteral.as.SubWith.impl.Op.call, %Core.IntLiteral.as.DivWith.impl.Op.call) [concrete = constants.%tuple.ad8] // CHECK:STDOUT: %.loc6_95.1: %struct_type.a.b.aa4 = struct_literal (%.loc6_65, %.loc6_94.1) [concrete = constants.%struct.e57] // CHECK:STDOUT: %impl.elem0.loc6_94.1: %.545 = impl_witness_access constants.%ImplicitAs.impl_witness.6bc, element0 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5] // CHECK:STDOUT: %bound_method.loc6_94.1: = bound_method %Core.IntLiteral.as.SubWith.impl.Op.call, %impl.elem0.loc6_94.1 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.bound.215] // CHECK:STDOUT: %specific_fn.loc6_94.1: = specific_function %impl.elem0.loc6_94.1, @Core.IntLiteral.as.ImplicitAs.impl.Convert(constants.%int_32) [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn] // CHECK:STDOUT: %bound_method.loc6_94.2: = bound_method %Core.IntLiteral.as.SubWith.impl.Op.call, %specific_fn.loc6_94.1 [concrete = constants.%bound_method.38b] // CHECK:STDOUT: %.loc6_84.1: Core.IntLiteral = value_of_initializer %Core.IntLiteral.as.SubWith.impl.Op.call [concrete = constants.%int_1.5b8] // CHECK:STDOUT: %.loc6_84.2: Core.IntLiteral = converted %Core.IntLiteral.as.SubWith.impl.Op.call, %.loc6_84.1 [concrete = constants.%int_1.5b8] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc6_94.1: init %i32 = call %bound_method.loc6_94.2(%.loc6_84.2) [concrete = constants.%int_1.5d2] // CHECK:STDOUT: %.loc6_94.2: %i32 = value_of_initializer %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc6_94.1 [concrete = constants.%int_1.5d2] // CHECK:STDOUT: %.loc6_94.3: %i32 = converted %Core.IntLiteral.as.SubWith.impl.Op.call, %.loc6_94.2 [concrete = constants.%int_1.5d2] // CHECK:STDOUT: %impl.elem0.loc6_94.2: %.545 = impl_witness_access constants.%ImplicitAs.impl_witness.6bc, element0 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5] // CHECK:STDOUT: %bound_method.loc6_94.3: = bound_method %Core.IntLiteral.as.DivWith.impl.Op.call, %impl.elem0.loc6_94.2 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.bound.4e5] // CHECK:STDOUT: %specific_fn.loc6_94.2: = specific_function %impl.elem0.loc6_94.2, @Core.IntLiteral.as.ImplicitAs.impl.Convert(constants.%int_32) [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn] // CHECK:STDOUT: %bound_method.loc6_94.4: = bound_method %Core.IntLiteral.as.DivWith.impl.Op.call, %specific_fn.loc6_94.2 [concrete = constants.%bound_method.646] // CHECK:STDOUT: %.loc6_91.1: Core.IntLiteral = value_of_initializer %Core.IntLiteral.as.DivWith.impl.Op.call [concrete = constants.%int_2.ecc] // CHECK:STDOUT: %.loc6_91.2: Core.IntLiteral = converted %Core.IntLiteral.as.DivWith.impl.Op.call, %.loc6_91.1 [concrete = constants.%int_2.ecc] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc6_94.2: init %i32 = call %bound_method.loc6_94.4(%.loc6_91.2) [concrete = constants.%int_2.ef8] // CHECK:STDOUT: %.loc6_94.4: %i32 = value_of_initializer %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc6_94.2 [concrete = constants.%int_2.ef8] // CHECK:STDOUT: %.loc6_94.5: %i32 = converted %Core.IntLiteral.as.DivWith.impl.Op.call, %.loc6_94.4 [concrete = constants.%int_2.ef8] // CHECK:STDOUT: %tuple.loc6_94: %tuple.type.d07 = tuple_value (%.loc6_94.3, %.loc6_94.5) [concrete = constants.%tuple.21c] // CHECK:STDOUT: %.loc6_95.2: %tuple.type.d07 = converted %.loc6_94.1, %tuple.loc6_94 [concrete = constants.%tuple.21c] // CHECK:STDOUT: %struct.loc6_95: %struct_type.a.b.fe2 = struct_value (%.loc6_65, %.loc6_95.2) [concrete = constants.%struct.682] // CHECK:STDOUT: %.loc6_95.3: %struct_type.a.b.fe2 = converted %.loc6_95.1, %struct.loc6_95 [concrete = constants.%struct.682] // CHECK:STDOUT: %.loc6_14: type = where_expr %.Self [concrete = constants.%I_where.type] { // CHECK:STDOUT: requirement_base_facet_type constants.%I.type // CHECK:STDOUT: requirement_rewrite %impl.elem0.loc6_20, %.loc6_48.3 // CHECK:STDOUT: requirement_rewrite %impl.elem0.subst, %.loc6_95.3 // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: interface @I { // CHECK:STDOUT: %Self: %I.type = symbolic_binding Self, 0 [symbolic = constants.%Self.ab9] // CHECK:STDOUT: %I.WithSelf.decl = interface_with_self_decl @I [concrete] // CHECK:STDOUT: // CHECK:STDOUT: !with Self: // CHECK:STDOUT: %X: %struct_type.a.b.fe2 = assoc_const_decl @X [concrete] { // CHECK:STDOUT: %assoc0: %I.assoc_type = assoc_entity element0, @I.WithSelf.%X [concrete = constants.%assoc0.be0] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = %Self // CHECK:STDOUT: .X = @X.%assoc0 // CHECK:STDOUT: witness = (@I.WithSelf.%X) // CHECK:STDOUT: // CHECK:STDOUT: !requires: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: impl @empty_tuple.type.as.I.impl: %.loc6_7.2 as %.loc6_14 { // CHECK:STDOUT: %I.impl_witness_table = impl_witness_table (%impl_witness_assoc_constant), @empty_tuple.type.as.I.impl [concrete] // CHECK:STDOUT: %I.impl_witness: = impl_witness %I.impl_witness_table [concrete = constants.%I.impl_witness] // CHECK:STDOUT: %impl_witness_assoc_constant: %struct_type.a.b.fe2 = impl_witness_assoc_constant constants.%struct.682 [concrete = constants.%struct.682] // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: witness = %I.impl_witness // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @I.WithSelf(constants.%Self.ab9) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @I.WithSelf(constants.%.Self.1dc) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @I.WithSelf(constants.%I.facet) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- fail_two_different_non_type.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %I.type: type = facet_type <@I> [concrete] // CHECK:STDOUT: %Self.ab9: %I.type = symbolic_binding Self, 0 [symbolic] // CHECK:STDOUT: %Bool.type: type = fn_type @Bool [concrete] // CHECK:STDOUT: %Bool: %Bool.type = struct_value () [concrete] // CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [concrete] // CHECK:STDOUT: %Int.type: type = generic_class_type @Int [concrete] // CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] // CHECK:STDOUT: %Int.generic: %Int.type = struct_value () [concrete] // CHECK:STDOUT: %i32: type = class_type @Int, @Int(%int_32) [concrete] // CHECK:STDOUT: %tuple.type.d07: type = tuple_type (%i32, %i32) [concrete] // CHECK:STDOUT: %struct_type.a.b.fe2: type = struct_type {.a: bool, .b: %tuple.type.d07} [concrete] // CHECK:STDOUT: %I.assoc_type: type = assoc_entity_type @I [concrete] // CHECK:STDOUT: %assoc0.be0: %I.assoc_type = assoc_entity element0, @I.WithSelf.%X [concrete] // CHECK:STDOUT: %empty_tuple: %empty_tuple.type = tuple_value () [concrete] // CHECK:STDOUT: %.Self: %I.type = symbolic_binding .Self [symbolic_self] // CHECK:STDOUT: %.Self.binding.as_type: type = symbolic_binding_type .Self, %.Self [symbolic_self] // CHECK:STDOUT: %I.lookup_impl_witness: = lookup_impl_witness %.Self, @I [symbolic_self] // CHECK:STDOUT: %impl.elem0.358: %struct_type.a.b.fe2 = impl_witness_access %I.lookup_impl_witness, element0 [symbolic_self] // CHECK:STDOUT: %true: bool = bool_literal true [concrete] // CHECK:STDOUT: %int_1.5b8: Core.IntLiteral = int_value 1 [concrete] // CHECK:STDOUT: %int_2.ecc: Core.IntLiteral = int_value 2 [concrete] // CHECK:STDOUT: %tuple.type.f94: type = tuple_type (Core.IntLiteral, Core.IntLiteral) [concrete] // CHECK:STDOUT: %tuple.ad8: %tuple.type.f94 = tuple_value (%int_1.5b8, %int_2.ecc) [concrete] // CHECK:STDOUT: %struct_type.a.b.aa4: type = struct_type {.a: bool, .b: %tuple.type.f94} [concrete] // CHECK:STDOUT: %struct.e57: %struct_type.a.b.aa4 = struct_value (%true, %tuple.ad8) [concrete] // CHECK:STDOUT: %ImplicitAs.type.cc7: type = generic_interface_type @ImplicitAs [concrete] // CHECK:STDOUT: %ImplicitAs.generic: %ImplicitAs.type.cc7 = struct_value () [concrete] // CHECK:STDOUT: %ImplicitAs.type.e8c: type = facet_type <@ImplicitAs, @ImplicitAs(%i32)> [concrete] // CHECK:STDOUT: %To: Core.IntLiteral = symbolic_binding To, 0 [symbolic] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.4e6: type = fn_type @Core.IntLiteral.as.ImplicitAs.impl.Convert, @Core.IntLiteral.as.ImplicitAs.impl(%To) [symbolic] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.3c2: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.4e6 = struct_value () [symbolic] // CHECK:STDOUT: %ImplicitAs.impl_witness.6bc: = impl_witness imports.%ImplicitAs.impl_witness_table.74f, @Core.IntLiteral.as.ImplicitAs.impl(%int_32) [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.e0d: type = fn_type @Core.IntLiteral.as.ImplicitAs.impl.Convert, @Core.IntLiteral.as.ImplicitAs.impl(%int_32) [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.e0d = struct_value () [concrete] // CHECK:STDOUT: %ImplicitAs.facet: %ImplicitAs.type.e8c = facet_value Core.IntLiteral, (%ImplicitAs.impl_witness.6bc) [concrete] // CHECK:STDOUT: %ImplicitAs.WithSelf.Convert.type.b37: type = fn_type @ImplicitAs.WithSelf.Convert, @ImplicitAs.WithSelf(%i32, %ImplicitAs.facet) [concrete] // CHECK:STDOUT: %.545: type = fn_type_with_self_type %ImplicitAs.WithSelf.Convert.type.b37, %ImplicitAs.facet [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.bound.215: = bound_method %int_1.5b8, %Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5 [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn: = specific_function %Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5, @Core.IntLiteral.as.ImplicitAs.impl.Convert(%int_32) [concrete] // CHECK:STDOUT: %bound_method.38b: = bound_method %int_1.5b8, %Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn [concrete] // CHECK:STDOUT: %int_1.5d2: %i32 = int_value 1 [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.bound.4e5: = bound_method %int_2.ecc, %Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5 [concrete] // CHECK:STDOUT: %bound_method.646: = bound_method %int_2.ecc, %Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn [concrete] // CHECK:STDOUT: %int_2.ef8: %i32 = int_value 2 [concrete] // CHECK:STDOUT: %tuple.21c: %tuple.type.d07 = tuple_value (%int_1.5d2, %int_2.ef8) [concrete] // CHECK:STDOUT: %struct.682: %struct_type.a.b.fe2 = struct_value (%true, %tuple.21c) [concrete] // CHECK:STDOUT: %false: bool = bool_literal false [concrete] // CHECK:STDOUT: %int_3.1ba: Core.IntLiteral = int_value 3 [concrete] // CHECK:STDOUT: %int_4.0c1: Core.IntLiteral = int_value 4 [concrete] // CHECK:STDOUT: %tuple.302: %tuple.type.f94 = tuple_value (%int_3.1ba, %int_4.0c1) [concrete] // CHECK:STDOUT: %struct.1ec: %struct_type.a.b.aa4 = struct_value (%false, %tuple.302) [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.bound.061: = bound_method %int_3.1ba, %Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5 [concrete] // CHECK:STDOUT: %bound_method.fa7: = bound_method %int_3.1ba, %Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn [concrete] // CHECK:STDOUT: %int_3.822: %i32 = int_value 3 [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.bound.f0c: = bound_method %int_4.0c1, %Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5 [concrete] // CHECK:STDOUT: %bound_method.6d7: = bound_method %int_4.0c1, %Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn [concrete] // CHECK:STDOUT: %int_4.940: %i32 = int_value 4 [concrete] // CHECK:STDOUT: %tuple.ffd: %tuple.type.d07 = tuple_value (%int_3.822, %int_4.940) [concrete] // CHECK:STDOUT: %struct.68c: %struct_type.a.b.fe2 = struct_value (%false, %tuple.ffd) [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: .Bool = %Core.Bool // CHECK:STDOUT: .Int = %Core.Int // CHECK:STDOUT: .ImplicitAs = %Core.ImplicitAs // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } // CHECK:STDOUT: %Core.Bool: %Bool.type = import_ref Core//prelude/types/bool, Bool, loaded [concrete = constants.%Bool] // CHECK:STDOUT: %Core.Int: %Int.type = import_ref Core//prelude/types/int, Int, loaded [concrete = constants.%Int.generic] // CHECK:STDOUT: %Core.ImplicitAs: %ImplicitAs.type.cc7 = import_ref Core//prelude/operators/as, ImplicitAs, loaded [concrete = constants.%ImplicitAs.generic] // CHECK:STDOUT: %Core.import_ref.42d: @Core.IntLiteral.as.ImplicitAs.impl.%Core.IntLiteral.as.ImplicitAs.impl.Convert.type (%Core.IntLiteral.as.ImplicitAs.impl.Convert.type.4e6) = import_ref Core//prelude/types/int, loc{{\d+_\d+}}, loaded [symbolic = @Core.IntLiteral.as.ImplicitAs.impl.%Core.IntLiteral.as.ImplicitAs.impl.Convert (constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.3c2)] // CHECK:STDOUT: %ImplicitAs.impl_witness_table.74f = impl_witness_table (%Core.import_ref.42d), @Core.IntLiteral.as.ImplicitAs.impl [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Core = imports.%Core // CHECK:STDOUT: .I = %I.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Core.import = import Core // CHECK:STDOUT: %I.decl: type = interface_decl @I [concrete = constants.%I.type] {} {} // CHECK:STDOUT: impl_decl @empty_tuple.type.as..impl [concrete] {} { // CHECK:STDOUT: %.loc10_7.1: %empty_tuple.type = tuple_literal () [concrete = constants.%empty_tuple] // CHECK:STDOUT: %.loc10_7.2: type = converted %.loc10_7.1, constants.%empty_tuple.type [concrete = constants.%empty_tuple.type] // CHECK:STDOUT: %I.ref: type = name_ref I, file.%I.decl [concrete = constants.%I.type] // CHECK:STDOUT: %.Self: %I.type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %.Self.ref.loc10_20: %I.type = name_ref .Self, %.Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %.Self.as_type.loc10_20: type = facet_access_type %.Self.ref.loc10_20 [symbolic_self = constants.%.Self.binding.as_type] // CHECK:STDOUT: %.loc10_20: type = converted %.Self.ref.loc10_20, %.Self.as_type.loc10_20 [symbolic_self = constants.%.Self.binding.as_type] // CHECK:STDOUT: %X.ref.loc10_20: %I.assoc_type = name_ref X, @X.%assoc0 [concrete = constants.%assoc0.be0] // CHECK:STDOUT: %impl.elem0.loc10_20: %struct_type.a.b.fe2 = impl_witness_access constants.%I.lookup_impl_witness, element0 [symbolic_self = constants.%impl.elem0.358] // CHECK:STDOUT: %true: bool = bool_literal true [concrete = constants.%true] // CHECK:STDOUT: %int_1: Core.IntLiteral = int_value 1 [concrete = constants.%int_1.5b8] // CHECK:STDOUT: %int_2: Core.IntLiteral = int_value 2 [concrete = constants.%int_2.ecc] // CHECK:STDOUT: %.loc10_47.1: %tuple.type.f94 = tuple_literal (%int_1, %int_2) [concrete = constants.%tuple.ad8] // CHECK:STDOUT: %.loc10_48.1: %struct_type.a.b.aa4 = struct_literal (%true, %.loc10_47.1) [concrete = constants.%struct.e57] // CHECK:STDOUT: %impl.elem0.loc10_47.1: %.545 = impl_witness_access constants.%ImplicitAs.impl_witness.6bc, element0 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5] // CHECK:STDOUT: %bound_method.loc10_47.1: = bound_method %int_1, %impl.elem0.loc10_47.1 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.bound.215] // CHECK:STDOUT: %specific_fn.loc10_47.1: = specific_function %impl.elem0.loc10_47.1, @Core.IntLiteral.as.ImplicitAs.impl.Convert(constants.%int_32) [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn] // CHECK:STDOUT: %bound_method.loc10_47.2: = bound_method %int_1, %specific_fn.loc10_47.1 [concrete = constants.%bound_method.38b] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc10_47.1: init %i32 = call %bound_method.loc10_47.2(%int_1) [concrete = constants.%int_1.5d2] // CHECK:STDOUT: %.loc10_47.2: %i32 = value_of_initializer %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc10_47.1 [concrete = constants.%int_1.5d2] // CHECK:STDOUT: %.loc10_47.3: %i32 = converted %int_1, %.loc10_47.2 [concrete = constants.%int_1.5d2] // CHECK:STDOUT: %impl.elem0.loc10_47.2: %.545 = impl_witness_access constants.%ImplicitAs.impl_witness.6bc, element0 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5] // CHECK:STDOUT: %bound_method.loc10_47.3: = bound_method %int_2, %impl.elem0.loc10_47.2 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.bound.4e5] // CHECK:STDOUT: %specific_fn.loc10_47.2: = specific_function %impl.elem0.loc10_47.2, @Core.IntLiteral.as.ImplicitAs.impl.Convert(constants.%int_32) [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn] // CHECK:STDOUT: %bound_method.loc10_47.4: = bound_method %int_2, %specific_fn.loc10_47.2 [concrete = constants.%bound_method.646] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc10_47.2: init %i32 = call %bound_method.loc10_47.4(%int_2) [concrete = constants.%int_2.ef8] // CHECK:STDOUT: %.loc10_47.4: %i32 = value_of_initializer %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc10_47.2 [concrete = constants.%int_2.ef8] // CHECK:STDOUT: %.loc10_47.5: %i32 = converted %int_2, %.loc10_47.4 [concrete = constants.%int_2.ef8] // CHECK:STDOUT: %tuple.loc10_47: %tuple.type.d07 = tuple_value (%.loc10_47.3, %.loc10_47.5) [concrete = constants.%tuple.21c] // CHECK:STDOUT: %.loc10_48.2: %tuple.type.d07 = converted %.loc10_47.1, %tuple.loc10_47 [concrete = constants.%tuple.21c] // CHECK:STDOUT: %struct.loc10_48: %struct_type.a.b.fe2 = struct_value (%true, %.loc10_48.2) [concrete = constants.%struct.682] // CHECK:STDOUT: %.loc10_48.3: %struct_type.a.b.fe2 = converted %.loc10_48.1, %struct.loc10_48 [concrete = constants.%struct.682] // CHECK:STDOUT: %.Self.ref.loc10_54: %I.type = name_ref .Self, %.Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %.Self.as_type.loc10_54: type = facet_access_type %.Self.ref.loc10_54 [symbolic_self = constants.%.Self.binding.as_type] // CHECK:STDOUT: %.loc10_54: type = converted %.Self.ref.loc10_54, %.Self.as_type.loc10_54 [symbolic_self = constants.%.Self.binding.as_type] // CHECK:STDOUT: %X.ref.loc10_54: %I.assoc_type = name_ref X, @X.%assoc0 [concrete = constants.%assoc0.be0] // CHECK:STDOUT: %impl.elem0.loc10_54: %struct_type.a.b.fe2 = impl_witness_access constants.%I.lookup_impl_witness, element0 [symbolic_self = constants.%impl.elem0.358] // CHECK:STDOUT: %impl.elem0.subst: %struct_type.a.b.fe2 = impl_witness_access_substituted %impl.elem0.loc10_54, %.loc10_48.3 [concrete = constants.%struct.682] // CHECK:STDOUT: %false: bool = bool_literal false [concrete = constants.%false] // CHECK:STDOUT: %int_3: Core.IntLiteral = int_value 3 [concrete = constants.%int_3.1ba] // CHECK:STDOUT: %int_4: Core.IntLiteral = int_value 4 [concrete = constants.%int_4.0c1] // CHECK:STDOUT: %.loc10_82.1: %tuple.type.f94 = tuple_literal (%int_3, %int_4) [concrete = constants.%tuple.302] // CHECK:STDOUT: %.loc10_83.1: %struct_type.a.b.aa4 = struct_literal (%false, %.loc10_82.1) [concrete = constants.%struct.1ec] // CHECK:STDOUT: %impl.elem0.loc10_82.1: %.545 = impl_witness_access constants.%ImplicitAs.impl_witness.6bc, element0 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5] // CHECK:STDOUT: %bound_method.loc10_82.1: = bound_method %int_3, %impl.elem0.loc10_82.1 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.bound.061] // CHECK:STDOUT: %specific_fn.loc10_82.1: = specific_function %impl.elem0.loc10_82.1, @Core.IntLiteral.as.ImplicitAs.impl.Convert(constants.%int_32) [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn] // CHECK:STDOUT: %bound_method.loc10_82.2: = bound_method %int_3, %specific_fn.loc10_82.1 [concrete = constants.%bound_method.fa7] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc10_82.1: init %i32 = call %bound_method.loc10_82.2(%int_3) [concrete = constants.%int_3.822] // CHECK:STDOUT: %.loc10_82.2: %i32 = value_of_initializer %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc10_82.1 [concrete = constants.%int_3.822] // CHECK:STDOUT: %.loc10_82.3: %i32 = converted %int_3, %.loc10_82.2 [concrete = constants.%int_3.822] // CHECK:STDOUT: %impl.elem0.loc10_82.2: %.545 = impl_witness_access constants.%ImplicitAs.impl_witness.6bc, element0 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5] // CHECK:STDOUT: %bound_method.loc10_82.3: = bound_method %int_4, %impl.elem0.loc10_82.2 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.bound.f0c] // CHECK:STDOUT: %specific_fn.loc10_82.2: = specific_function %impl.elem0.loc10_82.2, @Core.IntLiteral.as.ImplicitAs.impl.Convert(constants.%int_32) [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn] // CHECK:STDOUT: %bound_method.loc10_82.4: = bound_method %int_4, %specific_fn.loc10_82.2 [concrete = constants.%bound_method.6d7] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc10_82.2: init %i32 = call %bound_method.loc10_82.4(%int_4) [concrete = constants.%int_4.940] // CHECK:STDOUT: %.loc10_82.4: %i32 = value_of_initializer %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc10_82.2 [concrete = constants.%int_4.940] // CHECK:STDOUT: %.loc10_82.5: %i32 = converted %int_4, %.loc10_82.4 [concrete = constants.%int_4.940] // CHECK:STDOUT: %tuple.loc10_82: %tuple.type.d07 = tuple_value (%.loc10_82.3, %.loc10_82.5) [concrete = constants.%tuple.ffd] // CHECK:STDOUT: %.loc10_83.2: %tuple.type.d07 = converted %.loc10_82.1, %tuple.loc10_82 [concrete = constants.%tuple.ffd] // CHECK:STDOUT: %struct.loc10_83: %struct_type.a.b.fe2 = struct_value (%false, %.loc10_83.2) [concrete = constants.%struct.68c] // CHECK:STDOUT: %.loc10_83.3: %struct_type.a.b.fe2 = converted %.loc10_83.1, %struct.loc10_83 [concrete = constants.%struct.68c] // CHECK:STDOUT: %.loc10_14: type = where_expr %.Self [concrete = ] { // CHECK:STDOUT: requirement_base_facet_type constants.%I.type // CHECK:STDOUT: requirement_rewrite %impl.elem0.loc10_20, %.loc10_48.3 // CHECK:STDOUT: requirement_rewrite %impl.elem0.subst, %.loc10_83.3 // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: interface @I { // CHECK:STDOUT: %Self: %I.type = symbolic_binding Self, 0 [symbolic = constants.%Self.ab9] // CHECK:STDOUT: %I.WithSelf.decl = interface_with_self_decl @I [concrete] // CHECK:STDOUT: // CHECK:STDOUT: !with Self: // CHECK:STDOUT: %X: %struct_type.a.b.fe2 = assoc_const_decl @X [concrete] { // CHECK:STDOUT: %assoc0: %I.assoc_type = assoc_entity element0, @I.WithSelf.%X [concrete = constants.%assoc0.be0] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = %Self // CHECK:STDOUT: .X = @X.%assoc0 // CHECK:STDOUT: witness = (@I.WithSelf.%X) // CHECK:STDOUT: // CHECK:STDOUT: !requires: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: impl @empty_tuple.type.as..impl: %.loc10_7.2 as %.loc10_14 { // CHECK:STDOUT: !members: // CHECK:STDOUT: witness = // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @I.WithSelf(constants.%Self.ab9) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @I.WithSelf(constants.%.Self) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: } // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/impl/impl_forall.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/none.carbon // TODO: Add ranges and switch to "--dump-sem-ir-ranges=only". // EXTRA-ARGS: --dump-sem-ir-ranges=if-present // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/impl/impl_forall.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/impl/impl_forall.carbon interface Simple { fn F(); } impl forall [T:! type] T as Simple { fn F() {} } // CHECK:STDOUT: --- impl_forall.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %Simple.type: type = facet_type <@Simple> [concrete] // CHECK:STDOUT: %Self: %Simple.type = symbolic_binding Self, 0 [symbolic] // CHECK:STDOUT: %Simple.WithSelf.F.type.75b: type = fn_type @Simple.WithSelf.F, @Simple.WithSelf(%Self) [symbolic] // CHECK:STDOUT: %Simple.WithSelf.F.58c: %Simple.WithSelf.F.type.75b = struct_value () [symbolic] // CHECK:STDOUT: %Simple.assoc_type: type = assoc_entity_type @Simple [concrete] // CHECK:STDOUT: %assoc0: %Simple.assoc_type = assoc_entity element0, @Simple.WithSelf.%Simple.WithSelf.F.decl [concrete] // CHECK:STDOUT: %type: type = facet_type [concrete] // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self] // CHECK:STDOUT: %pattern_type: type = pattern_type type [concrete] // CHECK:STDOUT: %T: type = symbolic_binding T, 0 [symbolic] // CHECK:STDOUT: %Simple.impl_witness: = impl_witness @T.as.Simple.impl.%Simple.impl_witness_table, @T.as.Simple.impl(%T) [symbolic] // CHECK:STDOUT: %T.as.Simple.impl.F.type: type = fn_type @T.as.Simple.impl.F, @T.as.Simple.impl(%T) [symbolic] // CHECK:STDOUT: %T.as.Simple.impl.F: %T.as.Simple.impl.F.type = struct_value () [symbolic] // CHECK:STDOUT: %Simple.facet: %Simple.type = facet_value %T, (%Simple.impl_witness) [symbolic] // CHECK:STDOUT: %Simple.WithSelf.F.type.b56: type = fn_type @Simple.WithSelf.F, @Simple.WithSelf(%Simple.facet) [symbolic] // CHECK:STDOUT: %Simple.WithSelf.F.2ed: %Simple.WithSelf.F.type.b56 = struct_value () [symbolic] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Simple = %Simple.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Simple.decl: type = interface_decl @Simple [concrete = constants.%Simple.type] {} {} // CHECK:STDOUT: impl_decl @T.as.Simple.impl [concrete] { // CHECK:STDOUT: %T.patt: %pattern_type = symbolic_binding_pattern T, 0 [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %T.ref: type = name_ref T, %T.loc19_14.2 [symbolic = %T.loc19_14.1 (constants.%T)] // CHECK:STDOUT: %Simple.ref: type = name_ref Simple, file.%Simple.decl [concrete = constants.%Simple.type] // CHECK:STDOUT: %.loc19_18.1: type = splice_block %.loc19_18.2 [concrete = type] { // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %.loc19_18.2: type = type_literal type [concrete = type] // CHECK:STDOUT: } // CHECK:STDOUT: %T.loc19_14.2: type = symbolic_binding T, 0 [symbolic = %T.loc19_14.1 (constants.%T)] // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: interface @Simple { // CHECK:STDOUT: %Self: %Simple.type = symbolic_binding Self, 0 [symbolic = constants.%Self] // CHECK:STDOUT: %Simple.WithSelf.decl = interface_with_self_decl @Simple [concrete] // CHECK:STDOUT: // CHECK:STDOUT: !with Self: // CHECK:STDOUT: %Simple.WithSelf.F.decl: @Simple.WithSelf.%Simple.WithSelf.F.type (%Simple.WithSelf.F.type.75b) = fn_decl @Simple.WithSelf.F [symbolic = @Simple.WithSelf.%Simple.WithSelf.F (constants.%Simple.WithSelf.F.58c)] {} {} // CHECK:STDOUT: %assoc0: %Simple.assoc_type = assoc_entity element0, %Simple.WithSelf.F.decl [concrete = constants.%assoc0] // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = %Self // CHECK:STDOUT: .F = @Simple.WithSelf.%assoc0 // CHECK:STDOUT: witness = (@Simple.WithSelf.%Simple.WithSelf.F.decl) // CHECK:STDOUT: // CHECK:STDOUT: !requires: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic impl @T.as.Simple.impl(%T.loc19_14.2: type) { // CHECK:STDOUT: %T.loc19_14.1: type = symbolic_binding T, 0 [symbolic = %T.loc19_14.1 (constants.%T)] // CHECK:STDOUT: %Simple.impl_witness.loc19_36.2: = impl_witness %Simple.impl_witness_table, @T.as.Simple.impl(%T.loc19_14.1) [symbolic = %Simple.impl_witness.loc19_36.2 (constants.%Simple.impl_witness)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %T.as.Simple.impl.F.type: type = fn_type @T.as.Simple.impl.F, @T.as.Simple.impl(%T.loc19_14.1) [symbolic = %T.as.Simple.impl.F.type (constants.%T.as.Simple.impl.F.type)] // CHECK:STDOUT: %T.as.Simple.impl.F: @T.as.Simple.impl.%T.as.Simple.impl.F.type (%T.as.Simple.impl.F.type) = struct_value () [symbolic = %T.as.Simple.impl.F (constants.%T.as.Simple.impl.F)] // CHECK:STDOUT: // CHECK:STDOUT: impl: %T.ref as %Simple.ref { // CHECK:STDOUT: %T.as.Simple.impl.F.decl: @T.as.Simple.impl.%T.as.Simple.impl.F.type (%T.as.Simple.impl.F.type) = fn_decl @T.as.Simple.impl.F [symbolic = @T.as.Simple.impl.%T.as.Simple.impl.F (constants.%T.as.Simple.impl.F)] {} {} // CHECK:STDOUT: %Simple.impl_witness_table = impl_witness_table (%T.as.Simple.impl.F.decl), @T.as.Simple.impl [concrete] // CHECK:STDOUT: %Simple.impl_witness.loc19_36.1: = impl_witness %Simple.impl_witness_table, @T.as.Simple.impl(constants.%T) [symbolic = %Simple.impl_witness.loc19_36.2 (constants.%Simple.impl_witness)] // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .F = %T.as.Simple.impl.F.decl // CHECK:STDOUT: witness = %Simple.impl_witness.loc19_36.1 // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic fn @Simple.WithSelf.F(@Simple.%Self: %Simple.type) { // CHECK:STDOUT: fn(); // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic fn @T.as.Simple.impl.F(@T.as.Simple.impl.%T.loc19_14.2: type) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: // CHECK:STDOUT: fn() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @Simple.WithSelf(constants.%Self) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: %Self => constants.%Self // CHECK:STDOUT: %Simple.WithSelf.F.type => constants.%Simple.WithSelf.F.type.75b // CHECK:STDOUT: %Simple.WithSelf.F => constants.%Simple.WithSelf.F.58c // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @Simple.WithSelf.F(constants.%Self) {} // CHECK:STDOUT: // CHECK:STDOUT: specific @T.as.Simple.impl(constants.%T) { // CHECK:STDOUT: %T.loc19_14.1 => constants.%T // CHECK:STDOUT: %Simple.impl_witness.loc19_36.2 => constants.%Simple.impl_witness // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %T.as.Simple.impl.F.type => constants.%T.as.Simple.impl.F.type // CHECK:STDOUT: %T.as.Simple.impl.F => constants.%T.as.Simple.impl.F // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @T.as.Simple.impl.F(constants.%T) {} // CHECK:STDOUT: // CHECK:STDOUT: specific @Simple.WithSelf(constants.%Simple.facet) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: %Self => constants.%Simple.facet // CHECK:STDOUT: %Simple.WithSelf.F.type => constants.%Simple.WithSelf.F.type.b56 // CHECK:STDOUT: %Simple.WithSelf.F => constants.%Simple.WithSelf.F.2ed // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @Simple.WithSelf.F(constants.%Simple.facet) {} // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/impl/impl_self_as.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/none.carbon // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/impl/impl_self_as.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/impl/impl_self_as.carbon // --- match.carbon library "[[@TEST_NAME]]"; interface I1 {} interface I2 {} interface J1(T1:! type) {} interface J2(T2:! type) {} // `impl Self as` should match `impl as`, so these should not trigger impl // declaration without definition diagnostics. class C1 { impl Self as I1; impl as I1 {} impl as I2; impl Self as I2 {} impl forall [U:! type] Self as J1(U); impl forall [U:! type] as J1(U) {} impl forall [V:! type] as J2(V); impl forall [V:! type] Self as J2(V) {} } class C2(W:! type) { impl Self as I1; impl as I1 {} impl as I2; impl Self as I2 {} impl forall [X:! type] Self as J1(X); impl forall [X:! type] as J1(X) {} impl forall [Y:! type] as J2(Y); impl forall [Y:! type] Self as J2(Y) {} } // --- fail_no_match.carbon library "[[@TEST_NAME]]"; interface I3 {} interface I4 {} interface I5 {} interface I6 {} interface J3(T3:! type) {} interface J4(T4:! type) {} interface J5(T5:! type) {} interface J6(T6:! type) {} // `impl C as` should not match `impl Self as` or `impl as`. class C3 { // CHECK:STDERR: fail_no_match.carbon:[[@LINE+4]]:3: error: impl declared but not defined [ImplMissingDefinition] // CHECK:STDERR: impl C3 as I3; // CHECK:STDERR: ^~~~~~~~~~~~~~ // CHECK:STDERR: impl C3 as I3; impl as I3 {} // CHECK:STDERR: fail_no_match.carbon:[[@LINE+4]]:3: error: impl declared but not defined [ImplMissingDefinition] // CHECK:STDERR: impl C3 as I4; // CHECK:STDERR: ^~~~~~~~~~~~~~ // CHECK:STDERR: impl C3 as I4; impl Self as I4 {} // CHECK:STDERR: fail_no_match.carbon:[[@LINE+4]]:3: error: impl declared but not defined [ImplMissingDefinition] // CHECK:STDERR: impl as I5; // CHECK:STDERR: ^~~~~~~~~~~ // CHECK:STDERR: impl as I5; impl C3 as I5 {} // CHECK:STDERR: fail_no_match.carbon:[[@LINE+4]]:3: error: impl declared but not defined [ImplMissingDefinition] // CHECK:STDERR: impl Self as I6; // CHECK:STDERR: ^~~~~~~~~~~~~~~~ // CHECK:STDERR: impl Self as I6; impl C3 as I6 {} // CHECK:STDERR: fail_no_match.carbon:[[@LINE+4]]:3: error: impl declared but not defined [ImplMissingDefinition] // CHECK:STDERR: impl forall [Z3:! type] C3 as J3(Z3); // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: impl forall [Z3:! type] C3 as J3(Z3); impl forall [Z3:! type] as J3(Z3) {} // CHECK:STDERR: fail_no_match.carbon:[[@LINE+4]]:3: error: impl declared but not defined [ImplMissingDefinition] // CHECK:STDERR: impl forall [Z4:! type] C3 as J4(Z4); // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: impl forall [Z4:! type] C3 as J4(Z4); impl forall [Z4:! type] Self as J4(Z4) {} // CHECK:STDERR: fail_no_match.carbon:[[@LINE+4]]:3: error: impl declared but not defined [ImplMissingDefinition] // CHECK:STDERR: impl forall [Z5:! type] as J5(Z5); // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: impl forall [Z5:! type] as J5(Z5); impl forall [Z5:! type] C3 as J5(Z5) {} // CHECK:STDERR: fail_no_match.carbon:[[@LINE+4]]:3: error: impl declared but not defined [ImplMissingDefinition] // CHECK:STDERR: impl forall [Z6:! type] Self as J6(Z6); // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: impl forall [Z6:! type] Self as J6(Z6); impl forall [Z6:! type] C3 as J6(Z6) {} } class C4(A:! type) { // CHECK:STDERR: fail_no_match.carbon:[[@LINE+4]]:3: error: impl declared but not defined [ImplMissingDefinition] // CHECK:STDERR: impl C4(A) as I3; // CHECK:STDERR: ^~~~~~~~~~~~~~~~~ // CHECK:STDERR: impl C4(A) as I3; impl as I3 {} // CHECK:STDERR: fail_no_match.carbon:[[@LINE+4]]:3: error: impl declared but not defined [ImplMissingDefinition] // CHECK:STDERR: impl C4(A) as I4; // CHECK:STDERR: ^~~~~~~~~~~~~~~~~ // CHECK:STDERR: impl C4(A) as I4; impl Self as I4 {} // CHECK:STDERR: fail_no_match.carbon:[[@LINE+4]]:3: error: impl declared but not defined [ImplMissingDefinition] // CHECK:STDERR: impl as I5; // CHECK:STDERR: ^~~~~~~~~~~ // CHECK:STDERR: impl as I5; impl C4(A) as I5 {} // CHECK:STDERR: fail_no_match.carbon:[[@LINE+4]]:3: error: impl declared but not defined [ImplMissingDefinition] // CHECK:STDERR: impl Self as I6; // CHECK:STDERR: ^~~~~~~~~~~~~~~~ // CHECK:STDERR: impl Self as I6; impl C4(A) as I6 {} // CHECK:STDERR: fail_no_match.carbon:[[@LINE+4]]:3: error: impl declared but not defined [ImplMissingDefinition] // CHECK:STDERR: impl forall [B3:! type] C4(A) as J3(B3); // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: impl forall [B3:! type] C4(A) as J3(B3); impl forall [B3:! type] as J3(B3) {} // CHECK:STDERR: fail_no_match.carbon:[[@LINE+4]]:3: error: impl declared but not defined [ImplMissingDefinition] // CHECK:STDERR: impl forall [B4:! type] C4(A) as J4(B4); // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: impl forall [B4:! type] C4(A) as J4(B4); impl forall [B4:! type] Self as J4(B4) {} // CHECK:STDERR: fail_no_match.carbon:[[@LINE+4]]:3: error: impl declared but not defined [ImplMissingDefinition] // CHECK:STDERR: impl forall [B5:! type] as J5(B5); // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: impl forall [B5:! type] as J5(B5); impl forall [B5:! type] C4(A) as J5(B5) {} // CHECK:STDERR: fail_no_match.carbon:[[@LINE+4]]:3: error: impl declared but not defined [ImplMissingDefinition] // CHECK:STDERR: impl forall [B6:! type] Self as J6(B6); // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: impl forall [B6:! type] Self as J6(B6); impl forall [B6:! type] C4(A) as J6(B6) {} } ================================================ FILE: toolchain/check/testdata/impl/impl_thunk.carbon ================================================ [File too large to display: 81.0 KB] ================================================ FILE: toolchain/check/testdata/impl/impl_thunk_min_prelude.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/int.carbon // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/impl/impl_thunk_min_prelude.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/impl/impl_thunk_min_prelude.carbon // --- convert_in_class.carbon library "[[@TEST_NAME]]"; interface X(T:! type, U:! type) { fn F(t: T) -> U; } class ConvertsToA {} class ConvertsToB {} // Check that we don't try to define a thunk for `A.B.(as X).F` until we reach // the end of `A`. If we tried earlier, we wouldn't find a conversion from // `ConvertsToA` to `A` or from `ConvertsToB` to `B`. class A { class B { impl as X(ConvertsToA, B) { fn F(a: A) -> ConvertsToB; } impl ConvertsToB as Core.ImplicitAs(B) { fn Convert[unused self: Self]() -> B { return {}; } } } impl ConvertsToA as Core.ImplicitAs(A) { fn Convert[unused self: Self]() -> A { return {}; } } } // --- fail_todo_out_of_line_thunk.carbon library "[[@TEST_NAME]]"; class Wrap(T:! type) {} interface OpWith(U:! type) { fn Op[self: Self](u: U); } impl forall [T:! type, U:! Core.ImplicitAs(Wrap(T))] Wrap(T) as OpWith(U) { // CHECK:STDERR: fail_todo_out_of_line_thunk.carbon:[[@LINE+7]]:3: error: use of undefined generic function [MissingGenericFunctionDefinition] // CHECK:STDERR: fn Op[self: Self](other: Self); // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: fail_todo_out_of_line_thunk.carbon:[[@LINE+4]]:3: note: generic function declared here [MissingGenericFunctionDefinitionHere] // CHECK:STDERR: fn Op[self: Self](other: Self); // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: fn Op[self: Self](other: Self); } // TODO: Once we support the syntax for defining impl members out of line, // define the above function here. // fn (forall [T:! type, U:! Core.ImplicitAs(Wrap(T))] Wrap(T) as OpWith(U)).Op[self: Self](other: Self) {} // --- builtin_thunk.carbon library "[[@TEST_NAME]]"; class Wrap(T:! type) {} interface OpWith(U:! type) { fn Op[self: Self](u: U); } impl forall [T:! type, U:! Core.ImplicitAs(Wrap(T))] Wrap(T) as OpWith(U) { fn Op[self: Self](other: Self) = "no_op"; } // --- thunk_literal_convert.carbon library "[[@TEST_NAME]]"; interface Add(T:! type) { fn Op(a: Self, b: T) -> Self; } impl forall [T:! Core.ImplicitAs(i32)] i32 as Add(T) { fn Op(a: Self, b: Self) -> Self = "int.sadd"; } fn Call() -> i32 { let a: i32 = 1; // The conversion from 2 to IntLiteral here relies on having the // constant value available, so is only possible if the thunk is // inlined. return i32.(Add(Core.IntLiteral()).Op) //@dump-sem-ir-begin (a, 2) //@dump-sem-ir-end ; } // CHECK:STDOUT: --- thunk_literal_convert.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [concrete] // CHECK:STDOUT: %i32: type = class_type @Int, @Int(%int_32) [concrete] // CHECK:STDOUT: %ImplicitAs.type.e8c: type = facet_type <@ImplicitAs, @ImplicitAs(%i32)> [concrete] // CHECK:STDOUT: %To: Core.IntLiteral = symbolic_binding To, 0 [symbolic] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.4e6: type = fn_type @Core.IntLiteral.as.ImplicitAs.impl.Convert, @Core.IntLiteral.as.ImplicitAs.impl(%To) [symbolic] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.3c2: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.4e6 = struct_value () [symbolic] // CHECK:STDOUT: %ImplicitAs.impl_witness.6bc: = impl_witness imports.%ImplicitAs.impl_witness_table.74f, @Core.IntLiteral.as.ImplicitAs.impl(%int_32) [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.e0d: type = fn_type @Core.IntLiteral.as.ImplicitAs.impl.Convert, @Core.IntLiteral.as.ImplicitAs.impl(%int_32) [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.e0d = struct_value () [concrete] // CHECK:STDOUT: %ImplicitAs.facet: %ImplicitAs.type.e8c = facet_value Core.IntLiteral, (%ImplicitAs.impl_witness.6bc) [concrete] // CHECK:STDOUT: %ImplicitAs.WithSelf.Convert.type.b37: type = fn_type @ImplicitAs.WithSelf.Convert, @ImplicitAs.WithSelf(%i32, %ImplicitAs.facet) [concrete] // CHECK:STDOUT: %.545: type = fn_type_with_self_type %ImplicitAs.WithSelf.Convert.type.b37, %ImplicitAs.facet [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn: = specific_function %Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5, @Core.IntLiteral.as.ImplicitAs.impl.Convert(%int_32) [concrete] // CHECK:STDOUT: %i32.as.Add.impl.Op.type.73828b.1: type = fn_type @i32.as.Add.impl.Op.loc8_35.1, @i32.as.Add.impl(%ImplicitAs.facet) [concrete] // CHECK:STDOUT: %i32.as.Add.impl.Op.0aa775.1: %i32.as.Add.impl.Op.type.73828b.1 = struct_value () [concrete] // CHECK:STDOUT: %int_2.ecc: Core.IntLiteral = int_value 2 [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.bound.4e5: = bound_method %int_2.ecc, %Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5 [concrete] // CHECK:STDOUT: %bound_method.646: = bound_method %int_2.ecc, %Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn [concrete] // CHECK:STDOUT: %int_2.ef8: %i32 = int_value 2 [concrete] // CHECK:STDOUT: %i32.as.Add.impl.Op.specific_fn.0f6a75.2: = specific_function %i32.as.Add.impl.Op.0aa775.1, @i32.as.Add.impl.Op.loc8_35.1(%ImplicitAs.facet) [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core.import_ref.42d: @Core.IntLiteral.as.ImplicitAs.impl.%Core.IntLiteral.as.ImplicitAs.impl.Convert.type (%Core.IntLiteral.as.ImplicitAs.impl.Convert.type.4e6) = import_ref Core//prelude/parts/int, loc{{\d+_\d+}}, loaded [symbolic = @Core.IntLiteral.as.ImplicitAs.impl.%Core.IntLiteral.as.ImplicitAs.impl.Convert (constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.3c2)] // CHECK:STDOUT: %ImplicitAs.impl_witness_table.74f = impl_witness_table (%Core.import_ref.42d), @Core.IntLiteral.as.ImplicitAs.impl [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @Call() -> out %return.param: %i32 { // CHECK:STDOUT: !entry: // CHECK:STDOUT: // CHECK:STDOUT: %a.ref: %i32 = name_ref a, %a // CHECK:STDOUT: %int_2: Core.IntLiteral = int_value 2 [concrete = constants.%int_2.ecc] // CHECK:STDOUT: %ImplicitAs.facet: %ImplicitAs.type.e8c = facet_value Core.IntLiteral, (constants.%ImplicitAs.impl_witness.6bc) [concrete = constants.%ImplicitAs.facet] // CHECK:STDOUT: %.loc18_14.1: %ImplicitAs.type.e8c = converted Core.IntLiteral, %ImplicitAs.facet [concrete = constants.%ImplicitAs.facet] // CHECK:STDOUT: // CHECK:STDOUT: %.loc18_14.2: %i32.as.Add.impl.Op.type.73828b.1 = specific_constant @i32.as.Add.impl.%i32.as.Add.impl.Op.decl.loc8_35.1, @i32.as.Add.impl(constants.%ImplicitAs.facet) [concrete = constants.%i32.as.Add.impl.Op.0aa775.1] // CHECK:STDOUT: %Op.ref.loc18: %i32.as.Add.impl.Op.type.73828b.1 = name_ref Op, %.loc18_14.2 [concrete = constants.%i32.as.Add.impl.Op.0aa775.1] // CHECK:STDOUT: %impl.elem0.loc18_14: %.545 = impl_witness_access constants.%ImplicitAs.impl_witness.6bc, element0 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5] // CHECK:STDOUT: %bound_method.loc18_14.1: = bound_method %int_2, %impl.elem0.loc18_14 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.bound.4e5] // CHECK:STDOUT: %specific_fn.loc18_14: = specific_function %impl.elem0.loc18_14, @Core.IntLiteral.as.ImplicitAs.impl.Convert(constants.%int_32) [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn] // CHECK:STDOUT: %bound_method.loc18_14.2: = bound_method %int_2, %specific_fn.loc18_14 [concrete = constants.%bound_method.646] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc18_14: init %i32 = call %bound_method.loc18_14.2(%int_2) [concrete = constants.%int_2.ef8] // CHECK:STDOUT: %.loc18_14.3: %i32 = value_of_initializer %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc18_14 [concrete = constants.%int_2.ef8] // CHECK:STDOUT: %.loc18_14.4: %i32 = converted %int_2, %.loc18_14.3 [concrete = constants.%int_2.ef8] // CHECK:STDOUT: %i32.as.Add.impl.Op.specific_fn: = specific_function %Op.ref.loc18, @i32.as.Add.impl.Op.loc8_35.1(constants.%ImplicitAs.facet) [concrete = constants.%i32.as.Add.impl.Op.specific_fn.0f6a75.2] // CHECK:STDOUT: %impl.elem0.loc18_13: %.545 = impl_witness_access constants.%ImplicitAs.impl_witness.6bc, element0 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5] // CHECK:STDOUT: %bound_method.loc18_13.1: = bound_method %int_2, %impl.elem0.loc18_13 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.bound.4e5] // CHECK:STDOUT: %specific_fn.loc18_13: = specific_function %impl.elem0.loc18_13, @Core.IntLiteral.as.ImplicitAs.impl.Convert(constants.%int_32) [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn] // CHECK:STDOUT: %bound_method.loc18_13.2: = bound_method %int_2, %specific_fn.loc18_13 [concrete = constants.%bound_method.646] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc18_13: init %i32 = call %bound_method.loc18_13.2(%int_2) [concrete = constants.%int_2.ef8] // CHECK:STDOUT: %.loc18_13.1: %i32 = value_of_initializer %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc18_13 [concrete = constants.%int_2.ef8] // CHECK:STDOUT: %.loc18_13.2: %i32 = converted %int_2, %.loc18_13.1 [concrete = constants.%int_2.ef8] // CHECK:STDOUT: %i32.as.Add.impl.Op.call: init %i32 = call %i32.as.Add.impl.Op.specific_fn(%a.ref, %.loc18_13.2) // CHECK:STDOUT: // CHECK:STDOUT: } // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/impl/impl_where_redecl.carbon ================================================ [File too large to display: 3.6 KB] ================================================ FILE: toolchain/check/testdata/impl/import_builtin_call.carbon ================================================ [File too large to display: 96.1 KB] ================================================ FILE: toolchain/check/testdata/impl/import_canonical_witnesses.carbon ================================================ [File too large to display: 2.7 KB] ================================================ FILE: toolchain/check/testdata/impl/import_compound.carbon ================================================ [File too large to display: 53.0 KB] ================================================ FILE: toolchain/check/testdata/impl/import_extend_impl.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/none.carbon // TODO: Add ranges and switch to "--dump-sem-ir-ranges=only". // EXTRA-ARGS: --dump-sem-ir-ranges=if-present // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/impl/import_extend_impl.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/impl/import_extend_impl.carbon // --- extend_impl_library.carbon library "[[@TEST_NAME]]"; interface I { fn F(); } class C { extend impl as I { fn F() {} } } // --- use_imported_class_extend_impl.carbon library "[[@TEST_NAME]]"; import library "extend_impl_library"; fn G(c: C) { C.F(); c.F(); } // CHECK:STDOUT: --- extend_impl_library.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %I.type: type = facet_type <@I> [concrete] // CHECK:STDOUT: %Self: %I.type = symbolic_binding Self, 0 [symbolic] // CHECK:STDOUT: %I.WithSelf.F.type.08c: type = fn_type @I.WithSelf.F, @I.WithSelf(%Self) [symbolic] // CHECK:STDOUT: %I.WithSelf.F.705: %I.WithSelf.F.type.08c = struct_value () [symbolic] // CHECK:STDOUT: %I.assoc_type: type = assoc_entity_type @I [concrete] // CHECK:STDOUT: %assoc0: %I.assoc_type = assoc_entity element0, @I.WithSelf.%I.WithSelf.F.decl [concrete] // CHECK:STDOUT: %C: type = class_type @C [concrete] // CHECK:STDOUT: %I.impl_witness: = impl_witness @C.as.I.impl.%I.impl_witness_table [concrete] // CHECK:STDOUT: %C.as.I.impl.F.type: type = fn_type @C.as.I.impl.F [concrete] // CHECK:STDOUT: %C.as.I.impl.F: %C.as.I.impl.F.type = struct_value () [concrete] // CHECK:STDOUT: %I.facet: %I.type = facet_value %C, (%I.impl_witness) [concrete] // CHECK:STDOUT: %I.WithSelf.F.type.ba3: type = fn_type @I.WithSelf.F, @I.WithSelf(%I.facet) [concrete] // CHECK:STDOUT: %I.WithSelf.F.0e5: %I.WithSelf.F.type.ba3 = struct_value () [concrete] // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %complete_type: = complete_type_witness %empty_struct_type [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .I = %I.decl // CHECK:STDOUT: .C = %C.decl // CHECK:STDOUT: } // CHECK:STDOUT: %I.decl: type = interface_decl @I [concrete = constants.%I.type] {} {} // CHECK:STDOUT: %C.decl: type = class_decl @C [concrete = constants.%C] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: interface @I { // CHECK:STDOUT: %Self: %I.type = symbolic_binding Self, 0 [symbolic = constants.%Self] // CHECK:STDOUT: %I.WithSelf.decl = interface_with_self_decl @I [concrete] // CHECK:STDOUT: // CHECK:STDOUT: !with Self: // CHECK:STDOUT: %I.WithSelf.F.decl: @I.WithSelf.%I.WithSelf.F.type (%I.WithSelf.F.type.08c) = fn_decl @I.WithSelf.F [symbolic = @I.WithSelf.%I.WithSelf.F (constants.%I.WithSelf.F.705)] {} {} // CHECK:STDOUT: %assoc0: %I.assoc_type = assoc_entity element0, %I.WithSelf.F.decl [concrete = constants.%assoc0] // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = %Self // CHECK:STDOUT: .F = @I.WithSelf.%assoc0 // CHECK:STDOUT: witness = (@I.WithSelf.%I.WithSelf.F.decl) // CHECK:STDOUT: // CHECK:STDOUT: !requires: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: impl @C.as.I.impl: %Self.ref as %I.ref { // CHECK:STDOUT: %C.as.I.impl.F.decl: %C.as.I.impl.F.type = fn_decl @C.as.I.impl.F [concrete = constants.%C.as.I.impl.F] {} {} // CHECK:STDOUT: %I.impl_witness_table = impl_witness_table (%C.as.I.impl.F.decl), @C.as.I.impl [concrete] // CHECK:STDOUT: %I.impl_witness: = impl_witness %I.impl_witness_table [concrete = constants.%I.impl_witness] // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .F = %C.as.I.impl.F.decl // CHECK:STDOUT: witness = %I.impl_witness // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @C { // CHECK:STDOUT: impl_decl @C.as.I.impl [concrete] {} { // CHECK:STDOUT: %Self.ref: type = name_ref Self, constants.%C [concrete = constants.%C] // CHECK:STDOUT: %I.ref: type = name_ref I, file.%I.decl [concrete = constants.%I.type] // CHECK:STDOUT: } // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%C // CHECK:STDOUT: .I = // CHECK:STDOUT: extend @C.as.I.impl.%I.ref // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic fn @I.WithSelf.F(@I.%Self: %I.type) { // CHECK:STDOUT: fn(); // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @C.as.I.impl.F() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @I.WithSelf(constants.%Self) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: %Self => constants.%Self // CHECK:STDOUT: %I.WithSelf.F.type => constants.%I.WithSelf.F.type.08c // CHECK:STDOUT: %I.WithSelf.F => constants.%I.WithSelf.F.705 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @I.WithSelf.F(constants.%Self) {} // CHECK:STDOUT: // CHECK:STDOUT: specific @I.WithSelf(constants.%I.facet) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: %Self => constants.%I.facet // CHECK:STDOUT: %I.WithSelf.F.type => constants.%I.WithSelf.F.type.ba3 // CHECK:STDOUT: %I.WithSelf.F => constants.%I.WithSelf.F.0e5 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @I.WithSelf.F(constants.%I.facet) {} // CHECK:STDOUT: // CHECK:STDOUT: --- use_imported_class_extend_impl.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %C: type = class_type @C [concrete] // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %complete_type: = complete_type_witness %empty_struct_type [concrete] // CHECK:STDOUT: %pattern_type: type = pattern_type %C [concrete] // CHECK:STDOUT: %G.type: type = fn_type @G [concrete] // CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] // CHECK:STDOUT: %G: %G.type = struct_value () [concrete] // CHECK:STDOUT: %I.type: type = facet_type <@I> [concrete] // CHECK:STDOUT: %Self: %I.type = symbolic_binding Self, 0 [symbolic] // CHECK:STDOUT: %I.WithSelf.F.type.a1f: type = fn_type @I.WithSelf.F, @I.WithSelf(%Self) [symbolic] // CHECK:STDOUT: %I.WithSelf.F.cc3: %I.WithSelf.F.type.a1f = struct_value () [symbolic] // CHECK:STDOUT: %type: type = facet_type [concrete] // CHECK:STDOUT: %C.type.facet: %type = facet_value %C, () [concrete] // CHECK:STDOUT: %I.WithSelf.F.type.b59: type = fn_type @I.WithSelf.F, @I.WithSelf(%C.type.facet) [concrete] // CHECK:STDOUT: %I.WithSelf.F.521: %I.WithSelf.F.type.b59 = struct_value () [concrete] // CHECK:STDOUT: %I.assoc_type: type = assoc_entity_type @I [concrete] // CHECK:STDOUT: %assoc0: %I.assoc_type = assoc_entity element0, imports.%Main.import_ref.972 [concrete] // CHECK:STDOUT: %I.impl_witness: = impl_witness imports.%I.impl_witness_table [concrete] // CHECK:STDOUT: %I.facet: %I.type = facet_value %C, (%I.impl_witness) [concrete] // CHECK:STDOUT: %I.WithSelf.F.type.7f9: type = fn_type @I.WithSelf.F, @I.WithSelf(%I.facet) [concrete] // CHECK:STDOUT: %I.WithSelf.F.c5a: %I.WithSelf.F.type.7f9 = struct_value () [concrete] // CHECK:STDOUT: %.a40: type = fn_type_with_self_type %I.WithSelf.F.type.7f9, %I.facet [concrete] // CHECK:STDOUT: %C.as.I.impl.F.type: type = fn_type @C.as.I.impl.F [concrete] // CHECK:STDOUT: %C.as.I.impl.F: %C.as.I.impl.F.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Main.I = import_ref Main//extend_impl_library, I, unloaded // CHECK:STDOUT: %Main.C: type = import_ref Main//extend_impl_library, C, loaded [concrete = constants.%C] // CHECK:STDOUT: %Main.import_ref.8f2: = import_ref Main//extend_impl_library, loc12_1, loaded [concrete = constants.%complete_type] // CHECK:STDOUT: %Main.import_ref.743 = import_ref Main//extend_impl_library, inst{{[0-9A-F]+}} [no loc], unloaded // CHECK:STDOUT: %Main.import_ref.72a4a5.1: type = import_ref Main//extend_impl_library, loc9_18, loaded [concrete = constants.%I.type] // CHECK:STDOUT: %Main.import_ref.a54: %I.assoc_type = import_ref Main//extend_impl_library, loc5_9, loaded [concrete = constants.%assoc0] // CHECK:STDOUT: %Main.F = import_ref Main//extend_impl_library, F, unloaded // CHECK:STDOUT: %Main.import_ref.2362f8.2: %I.type = import_ref Main//extend_impl_library, loc4_13, loaded [symbolic = constants.%Self] // CHECK:STDOUT: %Main.import_ref.c82 = import_ref Main//extend_impl_library, loc4_13, unloaded // CHECK:STDOUT: %Main.import_ref.972: @I.WithSelf.%I.WithSelf.F.type (%I.WithSelf.F.type.a1f) = import_ref Main//extend_impl_library, loc5_9, loaded [symbolic = @I.WithSelf.%I.WithSelf.F (constants.%I.WithSelf.F.cc3)] // CHECK:STDOUT: %Main.import_ref.a3d: = import_ref Main//extend_impl_library, loc9_20, loaded [concrete = constants.%I.impl_witness] // CHECK:STDOUT: %Main.import_ref.569: type = import_ref Main//extend_impl_library, loc9_15, loaded [concrete = constants.%C] // CHECK:STDOUT: %Main.import_ref.72a4a5.2: type = import_ref Main//extend_impl_library, loc9_18, loaded [concrete = constants.%I.type] // CHECK:STDOUT: %Main.import_ref.d12: %C.as.I.impl.F.type = import_ref Main//extend_impl_library, loc10_12, loaded [concrete = constants.%C.as.I.impl.F] // CHECK:STDOUT: %I.impl_witness_table = impl_witness_table (%Main.import_ref.d12), @C.as.I.impl [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .I = imports.%Main.I // CHECK:STDOUT: .C = imports.%Main.C // CHECK:STDOUT: .G = %G.decl // CHECK:STDOUT: } // CHECK:STDOUT: %default.import = import // CHECK:STDOUT: %G.decl: %G.type = fn_decl @G [concrete = constants.%G] { // CHECK:STDOUT: %c.patt: %pattern_type = value_binding_pattern c [concrete] // CHECK:STDOUT: %c.param_patt: %pattern_type = value_param_pattern %c.patt [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %c.param: %C = value_param call_param0 // CHECK:STDOUT: %C.ref.loc6: type = name_ref C, imports.%Main.C [concrete = constants.%C] // CHECK:STDOUT: %c: %C = value_binding c, %c.param // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: interface @I [from "extend_impl_library.carbon"] { // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = imports.%Main.import_ref.c82 // CHECK:STDOUT: .F = imports.%Main.import_ref.a54 // CHECK:STDOUT: witness = (imports.%Main.F) // CHECK:STDOUT: // CHECK:STDOUT: !requires: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: impl @C.as.I.impl: imports.%Main.import_ref.569 as imports.%Main.import_ref.72a4a5.2 [from "extend_impl_library.carbon"] { // CHECK:STDOUT: !members: // CHECK:STDOUT: witness = imports.%Main.import_ref.a3d // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @C [from "extend_impl_library.carbon"] { // CHECK:STDOUT: complete_type_witness = imports.%Main.import_ref.8f2 // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = imports.%Main.import_ref.743 // CHECK:STDOUT: .F = // CHECK:STDOUT: extend imports.%Main.import_ref.72a4a5.1 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @G(%c.param: %C) { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %C.ref.loc7: type = name_ref C, imports.%Main.C [concrete = constants.%C] // CHECK:STDOUT: %F.ref.loc7: %I.assoc_type = name_ref F, imports.%Main.import_ref.a54 [concrete = constants.%assoc0] // CHECK:STDOUT: %impl.elem0.loc7: %.a40 = impl_witness_access constants.%I.impl_witness, element0 [concrete = constants.%C.as.I.impl.F] // CHECK:STDOUT: %C.as.I.impl.F.call.loc7: init %empty_tuple.type = call %impl.elem0.loc7() // CHECK:STDOUT: %c.ref: %C = name_ref c, %c // CHECK:STDOUT: %F.ref.loc8: %I.assoc_type = name_ref F, imports.%Main.import_ref.a54 [concrete = constants.%assoc0] // CHECK:STDOUT: %impl.elem0.loc8: %.a40 = impl_witness_access constants.%I.impl_witness, element0 [concrete = constants.%C.as.I.impl.F] // CHECK:STDOUT: %C.as.I.impl.F.call.loc8: init %empty_tuple.type = call %impl.elem0.loc8() // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic fn @I.WithSelf.F(imports.%Main.import_ref.2362f8.2: %I.type) [from "extend_impl_library.carbon"] { // CHECK:STDOUT: fn; // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @C.as.I.impl.F [from "extend_impl_library.carbon"]; // CHECK:STDOUT: // CHECK:STDOUT: specific @I.WithSelf(constants.%Self) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: %Self => constants.%Self // CHECK:STDOUT: %I.WithSelf.F.type => constants.%I.WithSelf.F.type.a1f // CHECK:STDOUT: %I.WithSelf.F => constants.%I.WithSelf.F.cc3 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @I.WithSelf.F(constants.%Self) {} // CHECK:STDOUT: // CHECK:STDOUT: specific @I.WithSelf(constants.%C.type.facet) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: %Self => constants.%C.type.facet // CHECK:STDOUT: %I.WithSelf.F.type => constants.%I.WithSelf.F.type.b59 // CHECK:STDOUT: %I.WithSelf.F => constants.%I.WithSelf.F.521 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @I.WithSelf(constants.%I.facet) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: %Self => constants.%I.facet // CHECK:STDOUT: %I.WithSelf.F.type => constants.%I.WithSelf.F.type.7f9 // CHECK:STDOUT: %I.WithSelf.F => constants.%I.WithSelf.F.c5a // CHECK:STDOUT: } // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/impl/import_generic.carbon ================================================ // Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/none.carbon // TODO: Add ranges and switch to "--dump-sem-ir-ranges=only". // EXTRA-ARGS: --dump-sem-ir-ranges=if-present // // AUTOUPDATE // TIP: To test this file alone, run: // TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/impl/import_generic.carbon // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/impl/import_generic.carbon // --- basic_import_generic_interface.carbon library "[[@TEST_NAME]]"; interface I(T:! type) {} interface J(T:! type) { extend require impls I(T); } // --- basic_import_generic_interface.impl.carbon impl library "[[@TEST_NAME]]"; class C {} impl C as I({}) {} impl C as J({}) {} // --- basic_import_generic_constraint.carbon library "[[@TEST_NAME]]"; interface I(T:! type) {} constraint J(T:! type) { extend require impls I(T); } // --- basic_import_generic_constraint.impl.carbon impl library "[[@TEST_NAME]]"; class C {} impl C as J({}) {} // --- import_generic.carbon library "[[@TEST_NAME]]"; class C {} interface I(T:! type) {} // Has both declaration and definition. impl forall [T:! type] C as I(T); impl forall [T:! type] C as I(T) {} // Only has definition. impl forall [T:! type] C as I(T*) {} constraint N(T:! type) { extend require impls I(T); } // --- fail_import_generic.impl.carbon impl library "[[@TEST_NAME]]"; // CHECK:STDERR: fail_import_generic.impl.carbon:[[@LINE+4]]:1: error: redeclaration of imported impl [RedeclImportedImpl] // CHECK:STDERR: impl forall [T:! type] C as I(T); // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: impl forall [T:! type] C as I(T); // CHECK:STDERR: fail_import_generic.impl.carbon:[[@LINE+4]]:1: error: redeclaration of imported impl [RedeclImportedImpl] // CHECK:STDERR: impl forall [T:! type] C as I(T) {} // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: impl forall [T:! type] C as I(T) {} // CHECK:STDERR: fail_import_generic.impl.carbon:[[@LINE+4]]:1: error: redeclaration of imported impl [RedeclImportedImpl] // CHECK:STDERR: impl forall [T:! type] C as I(T*); // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: impl forall [T:! type] C as I(T*); // CHECK:STDERR: fail_import_generic.impl.carbon:[[@LINE+4]]:1: error: redeclaration of imported impl [RedeclImportedImpl] // CHECK:STDERR: impl forall [T:! type] C as I(T*) {} // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: impl forall [T:! type] C as I(T*) {} // CHECK:STDERR: fail_import_generic.impl.carbon:[[@LINE+4]]:1: error: redeclaration of imported impl [RedeclImportedImpl] // CHECK:STDERR: impl forall [T:! type] C as N(T); // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: impl forall [T:! type] C as N(T); // CHECK:STDERR: fail_import_generic.impl.carbon:[[@LINE+4]]:1: error: redeclaration of imported impl [RedeclImportedImpl] // CHECK:STDERR: impl forall [T:! type] C as N(T*); // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: impl forall [T:! type] C as N(T*); // --- import_generic_with_different_specific.carbon library "[[@TEST_NAME]]"; class C {} interface I(T:! type) {} impl forall [T:! type] C as I(T) {} constraint N(T:! type) { extend require impls I(T); } // --- import_generic_with_different_specific.impl.carbon impl library "[[@TEST_NAME]]"; // These have different specifics than any impl decl in the api file, so they // are allowed. impl forall [T:! type] C as I(T*) {} impl forall [T:! type] C as N(T**) {} // --- fail_import_generic_decl.carbon library "[[@TEST_NAME]]"; class D {} interface J(T:! type) {} constraint N(T:! type) { extend require impls J(T); } // CHECK:STDERR: fail_import_generic_decl.carbon:[[@LINE+4]]:1: error: impl declared but not defined [ImplMissingDefinition] // CHECK:STDERR: impl forall [T:! type] D as J(T); // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: impl forall [T:! type] D as J(T); // CHECK:STDERR: fail_import_generic_decl.carbon:[[@LINE+4]]:1: error: impl declared but not defined [ImplMissingDefinition] // CHECK:STDERR: impl forall [T:! type] D as J(T*); // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: impl forall [T:! type] D as J(T*); // --- fail_import_generic_decl.impl.carbon impl library "[[@TEST_NAME]]"; // CHECK:STDERR: fail_import_generic_decl.impl.carbon:[[@LINE+4]]:1: error: redeclaration of imported impl [RedeclImportedImpl] // CHECK:STDERR: impl forall [T:! type] D as J(T); // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: impl forall [T:! type] D as J(T); // CHECK:STDERR: fail_import_generic_decl.impl.carbon:[[@LINE+4]]:1: error: redeclaration of imported impl [RedeclImportedImpl] // CHECK:STDERR: impl forall [T:! type] D as J(T) {} // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: impl forall [T:! type] D as J(T) {} // CHECK:STDERR: fail_import_generic_decl.impl.carbon:[[@LINE+4]]:1: error: redeclaration of imported impl [RedeclImportedImpl] // CHECK:STDERR: impl forall [T:! type] D as J(T*); // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: impl forall [T:! type] D as J(T*); // CHECK:STDERR: fail_import_generic_decl.impl.carbon:[[@LINE+4]]:1: error: redeclaration of imported impl [RedeclImportedImpl] // CHECK:STDERR: impl forall [T:! type] D as J(T*) {} // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: impl forall [T:! type] D as J(T*) {} // CHECK:STDERR: fail_import_generic_decl.impl.carbon:[[@LINE+4]]:1: error: redeclaration of imported impl [RedeclImportedImpl] // CHECK:STDERR: impl forall [T:! type] D as N(T) {} // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: impl forall [T:! type] D as N(T) {} // CHECK:STDERR: fail_import_generic_decl.impl.carbon:[[@LINE+4]]:1: error: redeclaration of imported impl [RedeclImportedImpl] // CHECK:STDERR: impl forall [T:! type] D as N(T*) {} // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: impl forall [T:! type] D as N(T*) {} // CHECK:STDOUT: --- basic_import_generic_interface.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %type: type = facet_type [concrete] // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self] // CHECK:STDOUT: %pattern_type: type = pattern_type type [concrete] // CHECK:STDOUT: %T: type = symbolic_binding T, 0 [symbolic] // CHECK:STDOUT: %I.type.609: type = generic_interface_type @I [concrete] // CHECK:STDOUT: %I.generic: %I.type.609 = struct_value () [concrete] // CHECK:STDOUT: %I.type.1ab: type = facet_type <@I, @I(%T)> [symbolic] // CHECK:STDOUT: %Self.fdb: %I.type.1ab = symbolic_binding Self, 1 [symbolic] // CHECK:STDOUT: %J.type.885: type = generic_interface_type @J [concrete] // CHECK:STDOUT: %J.generic: %J.type.885 = struct_value () [concrete] // CHECK:STDOUT: %J.type.04e: type = facet_type <@J, @J(%T)> [symbolic] // CHECK:STDOUT: %Self.a60: %J.type.04e = symbolic_binding Self, 1 [symbolic] // CHECK:STDOUT: %Self.binding.as_type: type = symbolic_binding_type Self, 1, %Self.a60 [symbolic] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .I = %I.decl // CHECK:STDOUT: .J = %J.decl // CHECK:STDOUT: } // CHECK:STDOUT: %I.decl: %I.type.609 = interface_decl @I [concrete = constants.%I.generic] { // CHECK:STDOUT: %T.patt: %pattern_type = symbolic_binding_pattern T, 0 [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %.loc3_17.1: type = splice_block %.loc3_17.2 [concrete = type] { // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %.loc3_17.2: type = type_literal type [concrete = type] // CHECK:STDOUT: } // CHECK:STDOUT: %T.loc3_13.2: type = symbolic_binding T, 0 [symbolic = %T.loc3_13.1 (constants.%T)] // CHECK:STDOUT: } // CHECK:STDOUT: %J.decl: %J.type.885 = interface_decl @J [concrete = constants.%J.generic] { // CHECK:STDOUT: %T.patt: %pattern_type = symbolic_binding_pattern T, 0 [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %.loc4_17.1: type = splice_block %.loc4_17.2 [concrete = type] { // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %.loc4_17.2: type = type_literal type [concrete = type] // CHECK:STDOUT: } // CHECK:STDOUT: %T.loc4_13.2: type = symbolic_binding T, 0 [symbolic = %T.loc4_13.1 (constants.%T)] // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic interface @I(%T.loc3_13.2: type) { // CHECK:STDOUT: %T.loc3_13.1: type = symbolic_binding T, 0 [symbolic = %T.loc3_13.1 (constants.%T)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %I.type: type = facet_type <@I, @I(%T.loc3_13.1)> [symbolic = %I.type (constants.%I.type.1ab)] // CHECK:STDOUT: %Self.loc3_23.2: @I.%I.type (%I.type.1ab) = symbolic_binding Self, 1 [symbolic = %Self.loc3_23.2 (constants.%Self.fdb)] // CHECK:STDOUT: // CHECK:STDOUT: interface { // CHECK:STDOUT: %Self.loc3_23.1: @I.%I.type (%I.type.1ab) = symbolic_binding Self, 1 [symbolic = %Self.loc3_23.2 (constants.%Self.fdb)] // CHECK:STDOUT: %I.WithSelf.decl = interface_with_self_decl @I [concrete] // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = %Self.loc3_23.1 // CHECK:STDOUT: witness = () // CHECK:STDOUT: // CHECK:STDOUT: !requires: // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic interface @J(%T.loc4_13.2: type) { // CHECK:STDOUT: %T.loc4_13.1: type = symbolic_binding T, 0 [symbolic = %T.loc4_13.1 (constants.%T)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %J.type: type = facet_type <@J, @J(%T.loc4_13.1)> [symbolic = %J.type (constants.%J.type.04e)] // CHECK:STDOUT: %Self.loc4_23.2: @J.%J.type (%J.type.04e) = symbolic_binding Self, 1 [symbolic = %Self.loc4_23.2 (constants.%Self.a60)] // CHECK:STDOUT: // CHECK:STDOUT: interface { // CHECK:STDOUT: %Self.loc4_23.1: @J.%J.type (%J.type.04e) = symbolic_binding Self, 1 [symbolic = %Self.loc4_23.2 (constants.%Self.a60)] // CHECK:STDOUT: %J.WithSelf.decl = interface_with_self_decl @J [concrete] // CHECK:STDOUT: // CHECK:STDOUT: !with Self: // CHECK:STDOUT: %J.WithSelf.Self.binding.as_type.impls.I.type.require.decl = require_decl @J.WithSelf.Self.binding.as_type.impls.I.type.require [concrete] { // CHECK:STDOUT: require %Self.as_type impls %I.type.loc5_27.1 // CHECK:STDOUT: } { // CHECK:STDOUT: %Self.as_type: type = facet_access_type @J.%Self.loc4_23.1 [symbolic = %Self.binding.as_type (constants.%Self.binding.as_type)] // CHECK:STDOUT: %I.ref: %I.type.609 = name_ref I, file.%I.decl [concrete = constants.%I.generic] // CHECK:STDOUT: %T.ref: type = name_ref T, @J.%T.loc4_13.2 [symbolic = %T (constants.%T)] // CHECK:STDOUT: %I.type.loc5_27.1: type = facet_type <@I, @I(constants.%T)> [symbolic = %I.type.loc5_27.2 (constants.%I.type.1ab)] // CHECK:STDOUT: } // CHECK:STDOUT: %.loc5: type = specific_constant @J.WithSelf.Self.binding.as_type.impls.I.type.require.%I.type.loc5_27.1, @J.WithSelf.Self.binding.as_type.impls.I.type.require(constants.%T, constants.%Self.a60) [symbolic = %I.type (constants.%I.type.1ab)] // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = %Self.loc4_23.1 // CHECK:STDOUT: .I = // CHECK:STDOUT: .T = // CHECK:STDOUT: .I = // CHECK:STDOUT: .T = // CHECK:STDOUT: extend @J.WithSelf.%.loc5 // CHECK:STDOUT: witness = () // CHECK:STDOUT: // CHECK:STDOUT: !requires: // CHECK:STDOUT: @J.WithSelf.Self.binding.as_type.impls.I.type.require { // CHECK:STDOUT: require @J.WithSelf.Self.binding.as_type.impls.I.type.require.%Self.as_type impls @J.WithSelf.Self.binding.as_type.impls.I.type.require.%I.type.loc5_27.1 // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic require @J.WithSelf.Self.binding.as_type.impls.I.type.require(@J.%T.loc4_13.2: type, @J.%Self.loc4_23.1: @J.%J.type (%J.type.04e)) { // CHECK:STDOUT: %T: type = symbolic_binding T, 0 [symbolic = %T (constants.%T)] // CHECK:STDOUT: %J.type: type = facet_type <@J, @J(%T)> [symbolic = %J.type (constants.%J.type.04e)] // CHECK:STDOUT: %Self: @J.WithSelf.Self.binding.as_type.impls.I.type.require.%J.type (%J.type.04e) = symbolic_binding Self, 1 [symbolic = %Self (constants.%Self.a60)] // CHECK:STDOUT: %Self.binding.as_type: type = symbolic_binding_type Self, 1, %Self [symbolic = %Self.binding.as_type (constants.%Self.binding.as_type)] // CHECK:STDOUT: %I.type.loc5_27.2: type = facet_type <@I, @I(%T)> [symbolic = %I.type.loc5_27.2 (constants.%I.type.1ab)] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @I(constants.%T) { // CHECK:STDOUT: %T.loc3_13.1 => constants.%T // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %I.type => constants.%I.type.1ab // CHECK:STDOUT: %Self.loc3_23.2 => constants.%Self.fdb // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @I.WithSelf(constants.%T, constants.%Self.fdb) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @J(constants.%T) { // CHECK:STDOUT: %T.loc4_13.1 => constants.%T // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @J.WithSelf(constants.%T, constants.%Self.a60) {} // CHECK:STDOUT: // CHECK:STDOUT: specific @J.WithSelf.Self.binding.as_type.impls.I.type.require(constants.%T, constants.%Self.a60) { // CHECK:STDOUT: %T => constants.%T // CHECK:STDOUT: %J.type => constants.%J.type.04e // CHECK:STDOUT: %Self => constants.%Self.a60 // CHECK:STDOUT: %Self.binding.as_type => constants.%Self.binding.as_type // CHECK:STDOUT: %I.type.loc5_27.2 => constants.%I.type.1ab // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- basic_import_generic_interface.impl.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %C: type = class_type @C [concrete] // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %complete_type.357: = complete_type_witness %empty_struct_type [concrete] // CHECK:STDOUT: %I.type.609: type = generic_interface_type @I [concrete] // CHECK:STDOUT: %I.generic: %I.type.609 = struct_value () [concrete] // CHECK:STDOUT: %T: type = symbolic_binding T, 0 [symbolic] // CHECK:STDOUT: %I.type.1ab: type = facet_type <@I, @I(%T)> [symbolic] // CHECK:STDOUT: %Self.c1b: %I.type.1ab = symbolic_binding Self, 1 [symbolic] // CHECK:STDOUT: %empty_struct: %empty_struct_type = struct_value () [concrete] // CHECK:STDOUT: %I.type.ab5: type = facet_type <@I, @I(%empty_struct_type)> [concrete] // CHECK:STDOUT: %I.impl_witness: = impl_witness @C.as.I.impl.%I.impl_witness_table [concrete] // CHECK:STDOUT: %Self.025: %I.type.ab5 = symbolic_binding Self, 1 [symbolic] // CHECK:STDOUT: %I.facet: %I.type.ab5 = facet_value %C, (%I.impl_witness) [concrete] // CHECK:STDOUT: %J.type.885: type = generic_interface_type @J [concrete] // CHECK:STDOUT: %J.generic: %J.type.885 = struct_value () [concrete] // CHECK:STDOUT: %J.type.04e: type = facet_type <@J, @J(%T)> [symbolic] // CHECK:STDOUT: %Self.c25: %J.type.04e = symbolic_binding Self, 1 [symbolic] // CHECK:STDOUT: %Self.binding.as_type: type = symbolic_binding_type Self, 1, %Self.c25 [symbolic] // CHECK:STDOUT: %J.type.d6f: type = facet_type <@J, @J(%empty_struct_type)> [concrete] // CHECK:STDOUT: %J.impl_witness: = impl_witness @C.as.J.impl.%J.impl_witness_table [concrete] // CHECK:STDOUT: %type: type = facet_type [concrete] // CHECK:STDOUT: %C.type.facet: %type = facet_value %C, () [concrete] // CHECK:STDOUT: %complete_type.5b1: = complete_type_witness %I.type.ab5 [concrete] // CHECK:STDOUT: %Self.9e4: %J.type.d6f = symbolic_binding Self, 1 [symbolic] // CHECK:STDOUT: %J.facet: %J.type.d6f = facet_value %C, (%J.impl_witness) [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Main.I: %I.type.609 = import_ref Main//basic_import_generic_interface, I, loaded [concrete = constants.%I.generic] // CHECK:STDOUT: %Main.J: %J.type.885 = import_ref Main//basic_import_generic_interface, J, loaded [concrete = constants.%J.generic] // CHECK:STDOUT: %Main.import_ref.3fa = import_ref Main//basic_import_generic_interface, loc3_23, unloaded // CHECK:STDOUT: %Main.import_ref.b3bc94.2: type = import_ref Main//basic_import_generic_interface, loc3_13, loaded [symbolic = @I.%T (constants.%T)] // CHECK:STDOUT: %Main.import_ref.7bf = import_ref Main//basic_import_generic_interface, loc5_28, unloaded // CHECK:STDOUT: %Main.import_ref.2ff: type = import_ref Main//basic_import_generic_interface, loc5_18, loaded [symbolic = @J.WithSelf.Self.binding.as_type.impls.I.type.require.%Self.binding.as_type (constants.%Self.binding.as_type)] // CHECK:STDOUT: %Main.import_ref.358: type = import_ref Main//basic_import_generic_interface, loc5_27, loaded [symbolic = @J.WithSelf.Self.binding.as_type.impls.I.type.require.%I.type (constants.%I.type.1ab)] // CHECK:STDOUT: %Main.import_ref.b3bc94.4: type = import_ref Main//basic_import_generic_interface, loc4_13, loaded [symbolic = @J.%T (constants.%T)] // CHECK:STDOUT: %Main.import_ref.99f327.2: @J.%J.type (%J.type.04e) = import_ref Main//basic_import_generic_interface, loc4_23, loaded [symbolic = @J.%Self (constants.%Self.c25)] // CHECK:STDOUT: %Main.import_ref.e9b = import_ref Main//basic_import_generic_interface, loc4_23, unloaded // CHECK:STDOUT: %Main.import_ref.b3bc94.5: type = import_ref Main//basic_import_generic_interface, loc4_13, loaded [symbolic = @J.%T (constants.%T)] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .I = imports.%Main.I // CHECK:STDOUT: .J = imports.%Main.J // CHECK:STDOUT: .C = %C.decl // CHECK:STDOUT: } // CHECK:STDOUT: %default.import.loc1_46.1 = import // CHECK:STDOUT: %default.import.loc1_46.2 = import // CHECK:STDOUT: %C.decl: type = class_decl @C [concrete = constants.%C] {} {} // CHECK:STDOUT: impl_decl @C.as.I.impl [concrete] {} { // CHECK:STDOUT: %C.ref: type = name_ref C, file.%C.decl [concrete = constants.%C] // CHECK:STDOUT: %I.ref: %I.type.609 = name_ref I, imports.%Main.I [concrete = constants.%I.generic] // CHECK:STDOUT: %.loc5_14: %empty_struct_type = struct_literal () [concrete = constants.%empty_struct] // CHECK:STDOUT: %.loc5_15: type = converted %.loc5_14, constants.%empty_struct_type [concrete = constants.%empty_struct_type] // CHECK:STDOUT: %I.type: type = facet_type <@I, @I(constants.%empty_struct_type)> [concrete = constants.%I.type.ab5] // CHECK:STDOUT: } // CHECK:STDOUT: impl_decl @C.as.J.impl [concrete] {} { // CHECK:STDOUT: %C.ref: type = name_ref C, file.%C.decl [concrete = constants.%C] // CHECK:STDOUT: %J.ref: %J.type.885 = name_ref J, imports.%Main.J [concrete = constants.%J.generic] // CHECK:STDOUT: %.loc7_14: %empty_struct_type = struct_literal () [concrete = constants.%empty_struct] // CHECK:STDOUT: %.loc7_15: type = converted %.loc7_14, constants.%empty_struct_type [concrete = constants.%empty_struct_type] // CHECK:STDOUT: %J.type: type = facet_type <@J, @J(constants.%empty_struct_type)> [concrete = constants.%J.type.d6f] // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic interface @I(imports.%Main.import_ref.b3bc94.2: type) [from "basic_import_generic_interface.carbon"] { // CHECK:STDOUT: %T: type = symbolic_binding T, 0 [symbolic = %T (constants.%T)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %I.type: type = facet_type <@I, @I(%T)> [symbolic = %I.type (constants.%I.type.1ab)] // CHECK:STDOUT: %Self: @I.%I.type (%I.type.1ab) = symbolic_binding Self, 1 [symbolic = %Self (constants.%Self.c1b)] // CHECK:STDOUT: // CHECK:STDOUT: interface { // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = imports.%Main.import_ref.3fa // CHECK:STDOUT: witness = () // CHECK:STDOUT: // CHECK:STDOUT: !requires: // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic interface @J(imports.%Main.import_ref.b3bc94.5: type) [from "basic_import_generic_interface.carbon"] { // CHECK:STDOUT: %T: type = symbolic_binding T, 0 [symbolic = %T (constants.%T)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %J.type: type = facet_type <@J, @J(%T)> [symbolic = %J.type (constants.%J.type.04e)] // CHECK:STDOUT: %Self: @J.%J.type (%J.type.04e) = symbolic_binding Self, 1 [symbolic = %Self (constants.%Self.c25)] // CHECK:STDOUT: // CHECK:STDOUT: interface { // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = imports.%Main.import_ref.e9b // CHECK:STDOUT: extend imports.%Main.import_ref.7bf // CHECK:STDOUT: witness = () // CHECK:STDOUT: // CHECK:STDOUT: !requires: // CHECK:STDOUT: @J.WithSelf.Self.binding.as_type.impls.I.type.require { // CHECK:STDOUT: require imports.%Main.import_ref.2ff impls imports.%Main.import_ref.358 // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic require @J.WithSelf.Self.binding.as_type.impls.I.type.require(imports.%Main.import_ref.b3bc94.4: type, imports.%Main.import_ref.99f327.2: @J.%J.type (%J.type.04e)) [from "basic_import_generic_interface.carbon"] { // CHECK:STDOUT: %T: type = symbolic_binding T, 0 [symbolic = %T (constants.%T)] // CHECK:STDOUT: %J.type: type = facet_type <@J, @J(%T)> [symbolic = %J.type (constants.%J.type.04e)] // CHECK:STDOUT: %Self: @J.WithSelf.Self.binding.as_type.impls.I.type.require.%J.type (%J.type.04e) = symbolic_binding Self, 1 [symbolic = %Self (constants.%Self.c25)] // CHECK:STDOUT: %Self.binding.as_type: type = symbolic_binding_type Self, 1, %Self [symbolic = %Self.binding.as_type (constants.%Self.binding.as_type)] // CHECK:STDOUT: %I.type: type = facet_type <@I, @I(%T)> [symbolic = %I.type (constants.%I.type.1ab)] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: impl @C.as.I.impl: %C.ref as %I.type { // CHECK:STDOUT: %I.impl_witness_table = impl_witness_table (), @C.as.I.impl [concrete] // CHECK:STDOUT: %I.impl_witness: = impl_witness %I.impl_witness_table [concrete = constants.%I.impl_witness] // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: witness = %I.impl_witness // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: impl @C.as.J.impl: %C.ref as %J.type { // CHECK:STDOUT: %J.impl_witness_table = impl_witness_table (), @C.as.J.impl [concrete] // CHECK:STDOUT: %J.impl_witness: = impl_witness %J.impl_witness_table [concrete = constants.%J.impl_witness] // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: witness = %J.impl_witness // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @C { // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type.357] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%C // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @I(constants.%T) { // CHECK:STDOUT: %T => constants.%T // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %I.type => constants.%I.type.1ab // CHECK:STDOUT: %Self => constants.%Self.c1b // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @I.WithSelf(constants.%T, constants.%Self.c1b) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @I(constants.%empty_struct_type) { // CHECK:STDOUT: %T => constants.%empty_struct_type // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %I.type => constants.%I.type.ab5 // CHECK:STDOUT: %Self => constants.%Self.025 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @I.WithSelf(constants.%empty_struct_type, constants.%Self.c1b) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @I.WithSelf(constants.%empty_struct_type, constants.%I.facet) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @J(constants.%T) { // CHECK:STDOUT: %T => constants.%T // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @J.WithSelf(constants.%T, constants.%Self.c25) {} // CHECK:STDOUT: // CHECK:STDOUT: specific @J.WithSelf.Self.binding.as_type.impls.I.type.require(constants.%T, constants.%Self.c25) { // CHECK:STDOUT: %T => constants.%T // CHECK:STDOUT: %J.type => constants.%J.type.04e // CHECK:STDOUT: %Self => constants.%Self.c25 // CHECK:STDOUT: %Self.binding.as_type => constants.%Self.binding.as_type // CHECK:STDOUT: %I.type => constants.%I.type.1ab // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @J(constants.%empty_struct_type) { // CHECK:STDOUT: %T => constants.%empty_struct_type // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %J.type => constants.%J.type.d6f // CHECK:STDOUT: %Self => constants.%Self.9e4 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @J.WithSelf(constants.%empty_struct_type, constants.%C.type.facet) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: %T => constants.%empty_struct_type // CHECK:STDOUT: %I.type => constants.%I.type.ab5 // CHECK:STDOUT: %require_complete => constants.%complete_type.5b1 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @J.WithSelf.Self.binding.as_type.impls.I.type.require(constants.%empty_struct_type, constants.%C.type.facet) { // CHECK:STDOUT: %T => constants.%empty_struct_type // CHECK:STDOUT: %J.type => constants.%J.type.d6f // CHECK:STDOUT: %Self => constants.%C.type.facet // CHECK:STDOUT: %Self.binding.as_type => constants.%C // CHECK:STDOUT: %I.type => constants.%I.type.ab5 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @J.WithSelf(constants.%empty_struct_type, constants.%Self.c25) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: %T => constants.%empty_struct_type // CHECK:STDOUT: %I.type => constants.%I.type.ab5 // CHECK:STDOUT: %require_complete => constants.%complete_type.5b1 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @J.WithSelf(constants.%empty_struct_type, constants.%J.facet) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: %T => constants.%empty_struct_type // CHECK:STDOUT: %I.type => constants.%I.type.ab5 // CHECK:STDOUT: %require_complete => constants.%complete_type.5b1 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- basic_import_generic_constraint.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %type: type = facet_type [concrete] // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self] // CHECK:STDOUT: %pattern_type: type = pattern_type type [concrete] // CHECK:STDOUT: %T: type = symbolic_binding T, 0 [symbolic] // CHECK:STDOUT: %I.type.609: type = generic_interface_type @I [concrete] // CHECK:STDOUT: %I.generic: %I.type.609 = struct_value () [concrete] // CHECK:STDOUT: %I.type.1ab: type = facet_type <@I, @I(%T)> [symbolic] // CHECK:STDOUT: %Self.fdb: %I.type.1ab = symbolic_binding Self, 1 [symbolic] // CHECK:STDOUT: %J.type.d08: type = generic_named_constaint_type @J [concrete] // CHECK:STDOUT: %empty_struct: %J.type.d08 = struct_value () [concrete] // CHECK:STDOUT: %J.type.d68: type = facet_type <@J, @J(%T)> [symbolic] // CHECK:STDOUT: %Self.31c: %J.type.d68 = symbolic_binding Self, 1 [symbolic] // CHECK:STDOUT: %Self.binding.as_type: type = symbolic_binding_type Self, 1, %Self.31c [symbolic] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .I = %I.decl // CHECK:STDOUT: .J = %J.decl // CHECK:STDOUT: } // CHECK:STDOUT: %I.decl: %I.type.609 = interface_decl @I [concrete = constants.%I.generic] { // CHECK:STDOUT: %T.patt: %pattern_type = symbolic_binding_pattern T, 0 [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %.loc3_17.1: type = splice_block %.loc3_17.2 [concrete = type] { // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %.loc3_17.2: type = type_literal type [concrete = type] // CHECK:STDOUT: } // CHECK:STDOUT: %T.loc3_13.2: type = symbolic_binding T, 0 [symbolic = %T.loc3_13.1 (constants.%T)] // CHECK:STDOUT: } // CHECK:STDOUT: %J.decl: %J.type.d08 = constraint_decl @J [concrete = constants.%empty_struct] { // CHECK:STDOUT: %T.patt: %pattern_type = symbolic_binding_pattern T, 0 [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %.loc4_18.1: type = splice_block %.loc4_18.2 [concrete = type] { // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %.loc4_18.2: type = type_literal type [concrete = type] // CHECK:STDOUT: } // CHECK:STDOUT: %T.loc4_14.2: type = symbolic_binding T, 0 [symbolic = %T.loc4_14.1 (constants.%T)] // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic interface @I(%T.loc3_13.2: type) { // CHECK:STDOUT: %T.loc3_13.1: type = symbolic_binding T, 0 [symbolic = %T.loc3_13.1 (constants.%T)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %I.type: type = facet_type <@I, @I(%T.loc3_13.1)> [symbolic = %I.type (constants.%I.type.1ab)] // CHECK:STDOUT: %Self.loc3_23.2: @I.%I.type (%I.type.1ab) = symbolic_binding Self, 1 [symbolic = %Self.loc3_23.2 (constants.%Self.fdb)] // CHECK:STDOUT: // CHECK:STDOUT: interface { // CHECK:STDOUT: %Self.loc3_23.1: @I.%I.type (%I.type.1ab) = symbolic_binding Self, 1 [symbolic = %Self.loc3_23.2 (constants.%Self.fdb)] // CHECK:STDOUT: %I.WithSelf.decl = interface_with_self_decl @I [concrete] // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = %Self.loc3_23.1 // CHECK:STDOUT: witness = () // CHECK:STDOUT: // CHECK:STDOUT: !requires: // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic constraint @J(%T.loc4_14.2: type) { // CHECK:STDOUT: %T.loc4_14.1: type = symbolic_binding T, 0 [symbolic = %T.loc4_14.1 (constants.%T)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %J.type: type = facet_type <@J, @J(%T.loc4_14.1)> [symbolic = %J.type (constants.%J.type.d68)] // CHECK:STDOUT: %Self.loc4_24.2: @J.%J.type (%J.type.d68) = symbolic_binding Self, 1 [symbolic = %Self.loc4_24.2 (constants.%Self.31c)] // CHECK:STDOUT: // CHECK:STDOUT: constraint { // CHECK:STDOUT: %Self.loc4_24.1: @J.%J.type (%J.type.d68) = symbolic_binding Self, 1 [symbolic = %Self.loc4_24.2 (constants.%Self.31c)] // CHECK:STDOUT: %J.WithSelf.decl = constraint_with_self_decl @J [concrete] // CHECK:STDOUT: // CHECK:STDOUT: !with Self: // CHECK:STDOUT: %J.WithSelf.Self.binding.as_type.impls.I.type.require.decl = require_decl @J.WithSelf.Self.binding.as_type.impls.I.type.require [concrete] { // CHECK:STDOUT: require %Self.as_type impls %I.type.loc5_27.1 // CHECK:STDOUT: } { // CHECK:STDOUT: %Self.as_type: type = facet_access_type @J.%Self.loc4_24.1 [symbolic = %Self.binding.as_type (constants.%Self.binding.as_type)] // CHECK:STDOUT: %I.ref: %I.type.609 = name_ref I, file.%I.decl [concrete = constants.%I.generic] // CHECK:STDOUT: %T.ref: type = name_ref T, @J.%T.loc4_14.2 [symbolic = %T (constants.%T)] // CHECK:STDOUT: %I.type.loc5_27.1: type = facet_type <@I, @I(constants.%T)> [symbolic = %I.type.loc5_27.2 (constants.%I.type.1ab)] // CHECK:STDOUT: } // CHECK:STDOUT: %.loc5: type = specific_constant @J.WithSelf.Self.binding.as_type.impls.I.type.require.%I.type.loc5_27.1, @J.WithSelf.Self.binding.as_type.impls.I.type.require(constants.%T, constants.%Self.31c) [symbolic = %I.type (constants.%I.type.1ab)] // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = %Self.loc4_24.1 // CHECK:STDOUT: .I = // CHECK:STDOUT: .T = // CHECK:STDOUT: .I = // CHECK:STDOUT: .T = // CHECK:STDOUT: extend @J.WithSelf.%.loc5 // CHECK:STDOUT: // CHECK:STDOUT: !requires: // CHECK:STDOUT: @J.WithSelf.Self.binding.as_type.impls.I.type.require { // CHECK:STDOUT: require @J.WithSelf.Self.binding.as_type.impls.I.type.require.%Self.as_type impls @J.WithSelf.Self.binding.as_type.impls.I.type.require.%I.type.loc5_27.1 // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic require @J.WithSelf.Self.binding.as_type.impls.I.type.require(@J.%T.loc4_14.2: type, @J.%Self.loc4_24.1: @J.%J.type (%J.type.d68)) { // CHECK:STDOUT: %T: type = symbolic_binding T, 0 [symbolic = %T (constants.%T)] // CHECK:STDOUT: %J.type: type = facet_type <@J, @J(%T)> [symbolic = %J.type (constants.%J.type.d68)] // CHECK:STDOUT: %Self: @J.WithSelf.Self.binding.as_type.impls.I.type.require.%J.type (%J.type.d68) = symbolic_binding Self, 1 [symbolic = %Self (constants.%Self.31c)] // CHECK:STDOUT: %Self.binding.as_type: type = symbolic_binding_type Self, 1, %Self [symbolic = %Self.binding.as_type (constants.%Self.binding.as_type)] // CHECK:STDOUT: %I.type.loc5_27.2: type = facet_type <@I, @I(%T)> [symbolic = %I.type.loc5_27.2 (constants.%I.type.1ab)] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @I(constants.%T) { // CHECK:STDOUT: %T.loc3_13.1 => constants.%T // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %I.type => constants.%I.type.1ab // CHECK:STDOUT: %Self.loc3_23.2 => constants.%Self.fdb // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @I.WithSelf(constants.%T, constants.%Self.fdb) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @J(constants.%T) { // CHECK:STDOUT: %T.loc4_14.1 => constants.%T // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @J.WithSelf(constants.%T, constants.%Self.31c) {} // CHECK:STDOUT: // CHECK:STDOUT: specific @J.WithSelf.Self.binding.as_type.impls.I.type.require(constants.%T, constants.%Self.31c) { // CHECK:STDOUT: %T => constants.%T // CHECK:STDOUT: %J.type => constants.%J.type.d68 // CHECK:STDOUT: %Self => constants.%Self.31c // CHECK:STDOUT: %Self.binding.as_type => constants.%Self.binding.as_type // CHECK:STDOUT: %I.type.loc5_27.2 => constants.%I.type.1ab // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- basic_import_generic_constraint.impl.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %C: type = class_type @C [concrete] // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %complete_type.357: = complete_type_witness %empty_struct_type [concrete] // CHECK:STDOUT: %J.type.d08: type = generic_named_constaint_type @J [concrete] // CHECK:STDOUT: %empty_struct.d2a: %J.type.d08 = struct_value () [concrete] // CHECK:STDOUT: %T: type = symbolic_binding T, 0 [symbolic] // CHECK:STDOUT: %J.type.d682d6.1: type = facet_type <@J, @J(%T)> [symbolic] // CHECK:STDOUT: %Self.e1f642.1: %J.type.d682d6.1 = symbolic_binding Self, 1 [symbolic] // CHECK:STDOUT: %I.type.1ab: type = facet_type <@I, @I(%T)> [symbolic] // CHECK:STDOUT: %Self.c1b: %I.type.1ab = symbolic_binding Self, 1 [symbolic] // CHECK:STDOUT: %Self.binding.as_type: type = symbolic_binding_type Self, 1, %Self.e1f642.1 [symbolic] // CHECK:STDOUT: %empty_struct.a40: %empty_struct_type = struct_value () [concrete] // CHECK:STDOUT: %J.type.d682d6.2: type = facet_type <@J, @J(%empty_struct_type)> [concrete] // CHECK:STDOUT: %Self.e1f642.2: %J.type.d682d6.2 = symbolic_binding Self, 1 [symbolic] // CHECK:STDOUT: %I.type.ab5: type = facet_type <@I, @I(%empty_struct_type)> [concrete] // CHECK:STDOUT: %Self.025: %I.type.ab5 = symbolic_binding Self, 1 [symbolic] // CHECK:STDOUT: %complete_type.5b1: = complete_type_witness %I.type.ab5 [concrete] // CHECK:STDOUT: %type: type = facet_type [concrete] // CHECK:STDOUT: %C.type.facet: %type = facet_value %C, () [concrete] // CHECK:STDOUT: %I.impl_witness: = impl_witness @C.as.I.impl.%I.impl_witness_table [concrete] // CHECK:STDOUT: %I.facet: %I.type.ab5 = facet_value %C, (%I.impl_witness) [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Main.I = import_ref Main//basic_import_generic_constraint, I, unloaded // CHECK:STDOUT: %Main.J: %J.type.d08 = import_ref Main//basic_import_generic_constraint, J, loaded [concrete = constants.%empty_struct.d2a] // CHECK:STDOUT: %Main.import_ref.7b1 = import_ref Main//basic_import_generic_constraint, loc5_28, unloaded // CHECK:STDOUT: %Main.import_ref.3fa = import_ref Main//basic_import_generic_constraint, loc3_23, unloaded // CHECK:STDOUT: %Main.import_ref.b3bc94.3: type = import_ref Main//basic_import_generic_constraint, loc3_13, loaded [symbolic = @I.%T (constants.%T)] // CHECK:STDOUT: %Main.import_ref.213: type = import_ref Main//basic_import_generic_constraint, loc5_18, loaded [symbolic = @J.WithSelf.Self.binding.as_type.impls.I.type.require.%Self.binding.as_type (constants.%Self.binding.as_type)] // CHECK:STDOUT: %Main.import_ref.358: type = import_ref Main//basic_import_generic_constraint, loc5_27, loaded [symbolic = @J.WithSelf.Self.binding.as_type.impls.I.type.require.%I.type (constants.%I.type.1ab)] // CHECK:STDOUT: %Main.import_ref.b3bc94.4: type = import_ref Main//basic_import_generic_constraint, loc4_14, loaded [symbolic = @J.%T (constants.%T)] // CHECK:STDOUT: %Main.import_ref.2fc394.2: @J.%J.type (%J.type.d682d6.1) = import_ref Main//basic_import_generic_constraint, loc4_24, loaded [symbolic = @J.%Self (constants.%Self.e1f642.1)] // CHECK:STDOUT: %Main.import_ref.830 = import_ref Main//basic_import_generic_constraint, loc4_24, unloaded // CHECK:STDOUT: %Main.import_ref.b3bc94.5: type = import_ref Main//basic_import_generic_constraint, loc4_14, loaded [symbolic = @J.%T (constants.%T)] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .I = imports.%Main.I // CHECK:STDOUT: .J = imports.%Main.J // CHECK:STDOUT: .C = %C.decl // CHECK:STDOUT: } // CHECK:STDOUT: %default.import.loc1_47.1 = import // CHECK:STDOUT: %default.import.loc1_47.2 = import // CHECK:STDOUT: %C.decl: type = class_decl @C [concrete = constants.%C] {} {} // CHECK:STDOUT: impl_decl @C.as.I.impl [concrete] {} { // CHECK:STDOUT: %C.ref: type = name_ref C, file.%C.decl [concrete = constants.%C] // CHECK:STDOUT: %J.ref: %J.type.d08 = name_ref J, imports.%Main.J [concrete = constants.%empty_struct.d2a] // CHECK:STDOUT: %.loc5_14: %empty_struct_type = struct_literal () [concrete = constants.%empty_struct.a40] // CHECK:STDOUT: %.loc5_15: type = converted %.loc5_14, constants.%empty_struct_type [concrete = constants.%empty_struct_type] // CHECK:STDOUT: %J.type: type = facet_type <@J, @J(constants.%empty_struct_type)> [concrete = constants.%J.type.d682d6.2] // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic interface @I(imports.%Main.import_ref.b3bc94.3: type) [from "basic_import_generic_constraint.carbon"] { // CHECK:STDOUT: %T: type = symbolic_binding T, 0 [symbolic = %T (constants.%T)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %I.type: type = facet_type <@I, @I(%T)> [symbolic = %I.type (constants.%I.type.1ab)] // CHECK:STDOUT: %Self: @I.%I.type (%I.type.1ab) = symbolic_binding Self, 1 [symbolic = %Self (constants.%Self.c1b)] // CHECK:STDOUT: // CHECK:STDOUT: interface { // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = imports.%Main.import_ref.3fa // CHECK:STDOUT: witness = () // CHECK:STDOUT: // CHECK:STDOUT: !requires: // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic constraint @J(imports.%Main.import_ref.b3bc94.5: type) [from "basic_import_generic_constraint.carbon"] { // CHECK:STDOUT: %T: type = symbolic_binding T, 0 [symbolic = %T (constants.%T)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %J.type: type = facet_type <@J, @J(%T)> [symbolic = %J.type (constants.%J.type.d682d6.1)] // CHECK:STDOUT: %Self: @J.%J.type (%J.type.d682d6.1) = symbolic_binding Self, 1 [symbolic = %Self (constants.%Self.e1f642.1)] // CHECK:STDOUT: // CHECK:STDOUT: constraint { // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = imports.%Main.import_ref.830 // CHECK:STDOUT: extend imports.%Main.import_ref.7b1 // CHECK:STDOUT: // CHECK:STDOUT: !requires: // CHECK:STDOUT: @J.WithSelf.Self.binding.as_type.impls.I.type.require { // CHECK:STDOUT: require imports.%Main.import_ref.213 impls imports.%Main.import_ref.358 // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic require @J.WithSelf.Self.binding.as_type.impls.I.type.require(imports.%Main.import_ref.b3bc94.4: type, imports.%Main.import_ref.2fc394.2: @J.%J.type (%J.type.d682d6.1)) [from "basic_import_generic_constraint.carbon"] { // CHECK:STDOUT: %T: type = symbolic_binding T, 0 [symbolic = %T (constants.%T)] // CHECK:STDOUT: %J.type: type = facet_type <@J, @J(%T)> [symbolic = %J.type (constants.%J.type.d682d6.1)] // CHECK:STDOUT: %Self: @J.WithSelf.Self.binding.as_type.impls.I.type.require.%J.type (%J.type.d682d6.1) = symbolic_binding Self, 1 [symbolic = %Self (constants.%Self.e1f642.1)] // CHECK:STDOUT: %Self.binding.as_type: type = symbolic_binding_type Self, 1, %Self [symbolic = %Self.binding.as_type (constants.%Self.binding.as_type)] // CHECK:STDOUT: %I.type: type = facet_type <@I, @I(%T)> [symbolic = %I.type (constants.%I.type.1ab)] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: impl @C.as.I.impl: %C.ref as %J.type { // CHECK:STDOUT: %I.impl_witness_table = impl_witness_table (), @C.as.I.impl [concrete] // CHECK:STDOUT: %I.impl_witness: = impl_witness %I.impl_witness_table [concrete = constants.%I.impl_witness] // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: witness = %I.impl_witness // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @C { // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type.357] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%C // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @J(constants.%T) { // CHECK:STDOUT: %T => constants.%T // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @I(constants.%T) { // CHECK:STDOUT: %T => constants.%T // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %I.type => constants.%I.type.1ab // CHECK:STDOUT: %Self => constants.%Self.c1b // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @I.WithSelf(constants.%T, constants.%Self.c1b) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @J.WithSelf(constants.%T, constants.%Self.e1f642.1) {} // CHECK:STDOUT: // CHECK:STDOUT: specific @J.WithSelf.Self.binding.as_type.impls.I.type.require(constants.%T, constants.%Self.e1f642.1) { // CHECK:STDOUT: %T => constants.%T // CHECK:STDOUT: %J.type => constants.%J.type.d682d6.1 // CHECK:STDOUT: %Self => constants.%Self.e1f642.1 // CHECK:STDOUT: %Self.binding.as_type => constants.%Self.binding.as_type // CHECK:STDOUT: %I.type => constants.%I.type.1ab // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @J(constants.%empty_struct_type) { // CHECK:STDOUT: %T => constants.%empty_struct_type // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %J.type => constants.%J.type.d682d6.2 // CHECK:STDOUT: %Self => constants.%Self.e1f642.2 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @J.WithSelf(constants.%empty_struct_type, constants.%Self.e1f642.1) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: %T => constants.%empty_struct_type // CHECK:STDOUT: %I.type => constants.%I.type.ab5 // CHECK:STDOUT: %require_complete => constants.%complete_type.5b1 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @I(constants.%empty_struct_type) { // CHECK:STDOUT: %T => constants.%empty_struct_type // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %I.type => constants.%I.type.ab5 // CHECK:STDOUT: %Self => constants.%Self.025 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @I.WithSelf(constants.%empty_struct_type, constants.%Self.c1b) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @J.WithSelf(constants.%empty_struct_type, constants.%C.type.facet) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: %T => constants.%empty_struct_type // CHECK:STDOUT: %I.type => constants.%I.type.ab5 // CHECK:STDOUT: %require_complete => constants.%complete_type.5b1 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @J.WithSelf.Self.binding.as_type.impls.I.type.require(constants.%empty_struct_type, constants.%C.type.facet) { // CHECK:STDOUT: %T => constants.%empty_struct_type // CHECK:STDOUT: %J.type => constants.%J.type.d682d6.2 // CHECK:STDOUT: %Self => constants.%C.type.facet // CHECK:STDOUT: %Self.binding.as_type => constants.%C // CHECK:STDOUT: %I.type => constants.%I.type.ab5 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @I.WithSelf(constants.%empty_struct_type, constants.%I.facet) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- import_generic.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %C: type = class_type @C [concrete] // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %complete_type: = complete_type_witness %empty_struct_type [concrete] // CHECK:STDOUT: %type: type = facet_type [concrete] // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self] // CHECK:STDOUT: %pattern_type: type = pattern_type type [concrete] // CHECK:STDOUT: %T: type = symbolic_binding T, 0 [symbolic] // CHECK:STDOUT: %I.type.609: type = generic_interface_type @I [concrete] // CHECK:STDOUT: %I.generic: %I.type.609 = struct_value () [concrete] // CHECK:STDOUT: %I.type.1ab: type = facet_type <@I, @I(%T)> [symbolic] // CHECK:STDOUT: %Self.fdb: %I.type.1ab = symbolic_binding Self, 1 [symbolic] // CHECK:STDOUT: %I.impl_witness.9f5: = impl_witness @C.as.I.impl.431.%I.impl_witness_table, @C.as.I.impl.431(%T) [symbolic] // CHECK:STDOUT: %require_complete.e36: = require_complete_type %I.type.1ab [symbolic] // CHECK:STDOUT: %I.facet.b43: %I.type.1ab = facet_value %C, (%I.impl_witness.9f5) [symbolic] // CHECK:STDOUT: %ptr: type = ptr_type %T [symbolic] // CHECK:STDOUT: %I.type.e19: type = facet_type <@I, @I(%ptr)> [symbolic] // CHECK:STDOUT: %I.impl_witness.18a: = impl_witness @C.as.I.impl.815.%I.impl_witness_table, @C.as.I.impl.815(%T) [symbolic] // CHECK:STDOUT: %Self.eff: %I.type.e19 = symbolic_binding Self, 1 [symbolic] // CHECK:STDOUT: %require_complete.ffd: = require_complete_type %I.type.e19 [symbolic] // CHECK:STDOUT: %I.facet.064: %I.type.e19 = facet_value %C, (%I.impl_witness.18a) [symbolic] // CHECK:STDOUT: %N.type.b47: type = generic_named_constaint_type @N [concrete] // CHECK:STDOUT: %empty_struct: %N.type.b47 = struct_value () [concrete] // CHECK:STDOUT: %N.type.d68: type = facet_type <@N, @N(%T)> [symbolic] // CHECK:STDOUT: %Self.31c: %N.type.d68 = symbolic_binding Self, 1 [symbolic] // CHECK:STDOUT: %Self.binding.as_type: type = symbolic_binding_type Self, 1, %Self.31c [symbolic] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .C = %C.decl // CHECK:STDOUT: .I = %I.decl // CHECK:STDOUT: .N = %N.decl // CHECK:STDOUT: } // CHECK:STDOUT: %C.decl: type = class_decl @C [concrete = constants.%C] {} {} // CHECK:STDOUT: %I.decl: %I.type.609 = interface_decl @I [concrete = constants.%I.generic] { // CHECK:STDOUT: %T.patt: %pattern_type = symbolic_binding_pattern T, 0 [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %.loc5_17.1: type = splice_block %.loc5_17.2 [concrete = type] { // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %.loc5_17.2: type = type_literal type [concrete = type] // CHECK:STDOUT: } // CHECK:STDOUT: %T.loc5_13.2: type = symbolic_binding T, 0 [symbolic = %T.loc5_13.1 (constants.%T)] // CHECK:STDOUT: } // CHECK:STDOUT: impl_decl @C.as.I.impl.431 [concrete] { // CHECK:STDOUT: %T.patt: %pattern_type = symbolic_binding_pattern T, 0 [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %C.ref.loc8: type = name_ref C, file.%C.decl [concrete = constants.%C] // CHECK:STDOUT: %I.ref.loc8: %I.type.609 = name_ref I, file.%I.decl [concrete = constants.%I.generic] // CHECK:STDOUT: %T.ref.loc8: type = name_ref T, %T.loc8_14.1 [symbolic = %T.loc8_14.2 (constants.%T)] // CHECK:STDOUT: %I.type.loc8_32.1: type = facet_type <@I, @I(constants.%T)> [symbolic = %I.type.loc8_32.2 (constants.%I.type.1ab)] // CHECK:STDOUT: %.loc8_18.1: type = splice_block %.loc8_18.2 [concrete = type] { // CHECK:STDOUT: %.Self.2: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %.loc8_18.2: type = type_literal type [concrete = type] // CHECK:STDOUT: } // CHECK:STDOUT: %T.loc8_14.1: type = symbolic_binding T, 0 [symbolic = %T.loc8_14.2 (constants.%T)] // CHECK:STDOUT: } // CHECK:STDOUT: impl_decl @C.as.I.impl.431 [concrete] { // CHECK:STDOUT: %T.patt: %pattern_type = symbolic_binding_pattern T, 0 [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %C.ref.loc9: type = name_ref C, file.%C.decl [concrete = constants.%C] // CHECK:STDOUT: %I.ref.loc9: %I.type.609 = name_ref I, file.%I.decl [concrete = constants.%I.generic] // CHECK:STDOUT: %T.ref.loc9: type = name_ref T, %T.loc9 [symbolic = %T.loc8_14.2 (constants.%T)] // CHECK:STDOUT: %I.type.loc9: type = facet_type <@I, @I(constants.%T)> [symbolic = %I.type.loc8_32.2 (constants.%I.type.1ab)] // CHECK:STDOUT: %.loc9_18.1: type = splice_block %.loc9_18.2 [concrete = type] { // CHECK:STDOUT: %.Self.1: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %.loc9_18.2: type = type_literal type [concrete = type] // CHECK:STDOUT: } // CHECK:STDOUT: %T.loc9: type = symbolic_binding T, 0 [symbolic = %T.loc8_14.2 (constants.%T)] // CHECK:STDOUT: } // CHECK:STDOUT: impl_decl @C.as.I.impl.815 [concrete] { // CHECK:STDOUT: %T.patt: %pattern_type = symbolic_binding_pattern T, 0 [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %C.ref: type = name_ref C, file.%C.decl [concrete = constants.%C] // CHECK:STDOUT: %I.ref: %I.type.609 = name_ref I, file.%I.decl [concrete = constants.%I.generic] // CHECK:STDOUT: %T.ref: type = name_ref T, %T.loc12_14.1 [symbolic = %T.loc12_14.2 (constants.%T)] // CHECK:STDOUT: %ptr.loc12_32.1: type = ptr_type %T.ref [symbolic = %ptr.loc12_32.2 (constants.%ptr)] // CHECK:STDOUT: %I.type.loc12_33.1: type = facet_type <@I, @I(constants.%ptr)> [symbolic = %I.type.loc12_33.2 (constants.%I.type.e19)] // CHECK:STDOUT: %.loc12_18.1: type = splice_block %.loc12_18.2 [concrete = type] { // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %.loc12_18.2: type = type_literal type [concrete = type] // CHECK:STDOUT: } // CHECK:STDOUT: %T.loc12_14.1: type = symbolic_binding T, 0 [symbolic = %T.loc12_14.2 (constants.%T)] // CHECK:STDOUT: } // CHECK:STDOUT: %N.decl: %N.type.b47 = constraint_decl @N [concrete = constants.%empty_struct] { // CHECK:STDOUT: %T.patt: %pattern_type = symbolic_binding_pattern T, 0 [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %.loc14_18.1: type = splice_block %.loc14_18.2 [concrete = type] { // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %.loc14_18.2: type = type_literal type [concrete = type] // CHECK:STDOUT: } // CHECK:STDOUT: %T.loc14_14.2: type = symbolic_binding T, 0 [symbolic = %T.loc14_14.1 (constants.%T)] // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic interface @I(%T.loc5_13.2: type) { // CHECK:STDOUT: %T.loc5_13.1: type = symbolic_binding T, 0 [symbolic = %T.loc5_13.1 (constants.%T)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %I.type: type = facet_type <@I, @I(%T.loc5_13.1)> [symbolic = %I.type (constants.%I.type.1ab)] // CHECK:STDOUT: %Self.loc5_23.2: @I.%I.type (%I.type.1ab) = symbolic_binding Self, 1 [symbolic = %Self.loc5_23.2 (constants.%Self.fdb)] // CHECK:STDOUT: // CHECK:STDOUT: interface { // CHECK:STDOUT: %Self.loc5_23.1: @I.%I.type (%I.type.1ab) = symbolic_binding Self, 1 [symbolic = %Self.loc5_23.2 (constants.%Self.fdb)] // CHECK:STDOUT: %I.WithSelf.decl = interface_with_self_decl @I [concrete] // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = %Self.loc5_23.1 // CHECK:STDOUT: witness = () // CHECK:STDOUT: // CHECK:STDOUT: !requires: // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic constraint @N(%T.loc14_14.2: type) { // CHECK:STDOUT: %T.loc14_14.1: type = symbolic_binding T, 0 [symbolic = %T.loc14_14.1 (constants.%T)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %N.type: type = facet_type <@N, @N(%T.loc14_14.1)> [symbolic = %N.type (constants.%N.type.d68)] // CHECK:STDOUT: %Self.loc14_24.2: @N.%N.type (%N.type.d68) = symbolic_binding Self, 1 [symbolic = %Self.loc14_24.2 (constants.%Self.31c)] // CHECK:STDOUT: // CHECK:STDOUT: constraint { // CHECK:STDOUT: %Self.loc14_24.1: @N.%N.type (%N.type.d68) = symbolic_binding Self, 1 [symbolic = %Self.loc14_24.2 (constants.%Self.31c)] // CHECK:STDOUT: %N.WithSelf.decl = constraint_with_self_decl @N [concrete] // CHECK:STDOUT: // CHECK:STDOUT: !with Self: // CHECK:STDOUT: %N.WithSelf.Self.binding.as_type.impls.I.type.require.decl = require_decl @N.WithSelf.Self.binding.as_type.impls.I.type.require [concrete] { // CHECK:STDOUT: require %Self.as_type impls %I.type.loc15_27.1 // CHECK:STDOUT: } { // CHECK:STDOUT: %Self.as_type: type = facet_access_type @N.%Self.loc14_24.1 [symbolic = %Self.binding.as_type (constants.%Self.binding.as_type)] // CHECK:STDOUT: %I.ref: %I.type.609 = name_ref I, file.%I.decl [concrete = constants.%I.generic] // CHECK:STDOUT: %T.ref: type = name_ref T, @N.%T.loc14_14.2 [symbolic = %T (constants.%T)] // CHECK:STDOUT: %I.type.loc15_27.1: type = facet_type <@I, @I(constants.%T)> [symbolic = %I.type.loc15_27.2 (constants.%I.type.1ab)] // CHECK:STDOUT: } // CHECK:STDOUT: %.loc15: type = specific_constant @N.WithSelf.Self.binding.as_type.impls.I.type.require.%I.type.loc15_27.1, @N.WithSelf.Self.binding.as_type.impls.I.type.require(constants.%T, constants.%Self.31c) [symbolic = %I.type (constants.%I.type.1ab)] // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = %Self.loc14_24.1 // CHECK:STDOUT: .I = // CHECK:STDOUT: .T = // CHECK:STDOUT: .I = // CHECK:STDOUT: .T = // CHECK:STDOUT: extend @N.WithSelf.%.loc15 // CHECK:STDOUT: // CHECK:STDOUT: !requires: // CHECK:STDOUT: @N.WithSelf.Self.binding.as_type.impls.I.type.require { // CHECK:STDOUT: require @N.WithSelf.Self.binding.as_type.impls.I.type.require.%Self.as_type impls @N.WithSelf.Self.binding.as_type.impls.I.type.require.%I.type.loc15_27.1 // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic require @N.WithSelf.Self.binding.as_type.impls.I.type.require(@N.%T.loc14_14.2: type, @N.%Self.loc14_24.1: @N.%N.type (%N.type.d68)) { // CHECK:STDOUT: %T: type = symbolic_binding T, 0 [symbolic = %T (constants.%T)] // CHECK:STDOUT: %N.type: type = facet_type <@N, @N(%T)> [symbolic = %N.type (constants.%N.type.d68)] // CHECK:STDOUT: %Self: @N.WithSelf.Self.binding.as_type.impls.I.type.require.%N.type (%N.type.d68) = symbolic_binding Self, 1 [symbolic = %Self (constants.%Self.31c)] // CHECK:STDOUT: %Self.binding.as_type: type = symbolic_binding_type Self, 1, %Self [symbolic = %Self.binding.as_type (constants.%Self.binding.as_type)] // CHECK:STDOUT: %I.type.loc15_27.2: type = facet_type <@I, @I(%T)> [symbolic = %I.type.loc15_27.2 (constants.%I.type.1ab)] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic impl @C.as.I.impl.431(%T.loc8_14.1: type) { // CHECK:STDOUT: %T.loc8_14.2: type = symbolic_binding T, 0 [symbolic = %T.loc8_14.2 (constants.%T)] // CHECK:STDOUT: %I.type.loc8_32.2: type = facet_type <@I, @I(%T.loc8_14.2)> [symbolic = %I.type.loc8_32.2 (constants.%I.type.1ab)] // CHECK:STDOUT: %I.impl_witness.loc8_33.2: = impl_witness %I.impl_witness_table, @C.as.I.impl.431(%T.loc8_14.2) [symbolic = %I.impl_witness.loc8_33.2 (constants.%I.impl_witness.9f5)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete: = require_complete_type %I.type.loc8_32.2 [symbolic = %require_complete (constants.%require_complete.e36)] // CHECK:STDOUT: // CHECK:STDOUT: impl: %C.ref.loc8 as %I.type.loc8_32.1 { // CHECK:STDOUT: %I.impl_witness_table = impl_witness_table (), @C.as.I.impl.431 [concrete] // CHECK:STDOUT: %I.impl_witness.loc8_33.1: = impl_witness %I.impl_witness_table, @C.as.I.impl.431(constants.%T) [symbolic = %I.impl_witness.loc8_33.2 (constants.%I.impl_witness.9f5)] // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: witness = %I.impl_witness.loc8_33.1 // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic impl @C.as.I.impl.815(%T.loc12_14.1: type) { // CHECK:STDOUT: %T.loc12_14.2: type = symbolic_binding T, 0 [symbolic = %T.loc12_14.2 (constants.%T)] // CHECK:STDOUT: %ptr.loc12_32.2: type = ptr_type %T.loc12_14.2 [symbolic = %ptr.loc12_32.2 (constants.%ptr)] // CHECK:STDOUT: %I.type.loc12_33.2: type = facet_type <@I, @I(%ptr.loc12_32.2)> [symbolic = %I.type.loc12_33.2 (constants.%I.type.e19)] // CHECK:STDOUT: %I.impl_witness.loc12_35.2: = impl_witness %I.impl_witness_table, @C.as.I.impl.815(%T.loc12_14.2) [symbolic = %I.impl_witness.loc12_35.2 (constants.%I.impl_witness.18a)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete: = require_complete_type %I.type.loc12_33.2 [symbolic = %require_complete (constants.%require_complete.ffd)] // CHECK:STDOUT: // CHECK:STDOUT: impl: %C.ref as %I.type.loc12_33.1 { // CHECK:STDOUT: %I.impl_witness_table = impl_witness_table (), @C.as.I.impl.815 [concrete] // CHECK:STDOUT: %I.impl_witness.loc12_35.1: = impl_witness %I.impl_witness_table, @C.as.I.impl.815(constants.%T) [symbolic = %I.impl_witness.loc12_35.2 (constants.%I.impl_witness.18a)] // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: witness = %I.impl_witness.loc12_35.1 // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @C { // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%C // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @I(constants.%T) { // CHECK:STDOUT: %T.loc5_13.1 => constants.%T // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %I.type => constants.%I.type.1ab // CHECK:STDOUT: %Self.loc5_23.2 => constants.%Self.fdb // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @I.WithSelf(constants.%T, constants.%Self.fdb) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @C.as.I.impl.431(constants.%T) { // CHECK:STDOUT: %T.loc8_14.2 => constants.%T // CHECK:STDOUT: %I.type.loc8_32.2 => constants.%I.type.1ab // CHECK:STDOUT: %I.impl_witness.loc8_33.2 => constants.%I.impl_witness.9f5 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @I.WithSelf(constants.%T, constants.%I.facet.b43) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @I(constants.%ptr) { // CHECK:STDOUT: %T.loc5_13.1 => constants.%ptr // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %I.type => constants.%I.type.e19 // CHECK:STDOUT: %Self.loc5_23.2 => constants.%Self.eff // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @C.as.I.impl.815(constants.%T) { // CHECK:STDOUT: %T.loc12_14.2 => constants.%T // CHECK:STDOUT: %ptr.loc12_32.2 => constants.%ptr // CHECK:STDOUT: %I.type.loc12_33.2 => constants.%I.type.e19 // CHECK:STDOUT: %I.impl_witness.loc12_35.2 => constants.%I.impl_witness.18a // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @I.WithSelf(constants.%ptr, constants.%Self.fdb) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @I.WithSelf(constants.%ptr, constants.%I.facet.064) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @N(constants.%T) { // CHECK:STDOUT: %T.loc14_14.1 => constants.%T // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @N.WithSelf(constants.%T, constants.%Self.31c) {} // CHECK:STDOUT: // CHECK:STDOUT: specific @N.WithSelf.Self.binding.as_type.impls.I.type.require(constants.%T, constants.%Self.31c) { // CHECK:STDOUT: %T => constants.%T // CHECK:STDOUT: %N.type => constants.%N.type.d68 // CHECK:STDOUT: %Self => constants.%Self.31c // CHECK:STDOUT: %Self.binding.as_type => constants.%Self.binding.as_type // CHECK:STDOUT: %I.type.loc15_27.2 => constants.%I.type.1ab // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- fail_import_generic.impl.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %T: type = symbolic_binding T, 0 [symbolic] // CHECK:STDOUT: %I.type.609: type = generic_interface_type @I [concrete] // CHECK:STDOUT: %I.generic: %I.type.609 = struct_value () [concrete] // CHECK:STDOUT: %I.type.1ab: type = facet_type <@I, @I(%T)> [symbolic] // CHECK:STDOUT: %Self.c1b: %I.type.1ab = symbolic_binding Self, 1 [symbolic] // CHECK:STDOUT: %pattern_type: type = pattern_type type [concrete] // CHECK:STDOUT: %C: type = class_type @C [concrete] // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %complete_type: = complete_type_witness %empty_struct_type [concrete] // CHECK:STDOUT: %I.impl_witness.9f5: = impl_witness imports.%I.impl_witness_table.2ac, @C.as.I.impl.431d5e.1(%T) [symbolic] // CHECK:STDOUT: %require_complete.e36: = require_complete_type %I.type.1ab [symbolic] // CHECK:STDOUT: %ptr: type = ptr_type %T [symbolic] // CHECK:STDOUT: %I.type.e19: type = facet_type <@I, @I(%ptr)> [symbolic] // CHECK:STDOUT: %Self.dda: %I.type.e19 = symbolic_binding Self, 1 [symbolic] // CHECK:STDOUT: %I.impl_witness.18a: = impl_witness imports.%I.impl_witness_table.a23, @C.as.I.impl.8159c7.1(%T) [symbolic] // CHECK:STDOUT: %require_complete.ffd: = require_complete_type %I.type.e19 [symbolic] // CHECK:STDOUT: %type: type = facet_type [concrete] // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self] // CHECK:STDOUT: %N.type.b47: type = generic_named_constaint_type @N [concrete] // CHECK:STDOUT: %empty_struct: %N.type.b47 = struct_value () [concrete] // CHECK:STDOUT: %N.type.d682d6.1: type = facet_type <@N, @N(%T)> [symbolic] // CHECK:STDOUT: %Self.e1f642.1: %N.type.d682d6.1 = symbolic_binding Self, 1 [symbolic] // CHECK:STDOUT: %Self.binding.as_type: type = symbolic_binding_type Self, 1, %Self.e1f642.1 [symbolic] // CHECK:STDOUT: %C.type.facet: %type = facet_value %C, () [concrete] // CHECK:STDOUT: %N.type.d682d6.2: type = facet_type <@N, @N(%ptr)> [symbolic] // CHECK:STDOUT: %Self.e1f642.2: %N.type.d682d6.2 = symbolic_binding Self, 1 [symbolic] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Main.C: type = import_ref Main//import_generic, C, loaded [concrete = constants.%C] // CHECK:STDOUT: %Main.I: %I.type.609 = import_ref Main//import_generic, I, loaded [concrete = constants.%I.generic] // CHECK:STDOUT: %Main.N: %N.type.b47 = import_ref Main//import_generic, N, loaded [concrete = constants.%empty_struct] // CHECK:STDOUT: %Main.import_ref.3fa = import_ref Main//import_generic, loc5_23, unloaded // CHECK:STDOUT: %Main.import_ref.b3bc94.2: type = import_ref Main//import_generic, loc5_13, loaded [symbolic = @I.%T (constants.%T)] // CHECK:STDOUT: %Main.import_ref.e08 = import_ref Main//import_generic, loc8_33, unloaded // CHECK:STDOUT: %Main.import_ref.8f2: = import_ref Main//import_generic, loc4_10, loaded [concrete = constants.%complete_type] // CHECK:STDOUT: %Main.import_ref.743 = import_ref Main//import_generic, inst{{[0-9A-F]+}} [no loc], unloaded // CHECK:STDOUT: %I.impl_witness_table.2ac = impl_witness_table (), @C.as.I.impl.431d5e.1 [concrete] // CHECK:STDOUT: %Main.import_ref.61cd7d.1: type = import_ref Main//import_generic, loc8_24, loaded [concrete = constants.%C] // CHECK:STDOUT: %Main.import_ref.3582b1.1: type = import_ref Main//import_generic, loc8_32, loaded [symbolic = @C.as.I.impl.431d5e.1.%I.type (constants.%I.type.1ab)] // CHECK:STDOUT: %Main.import_ref.b3bc94.3: type = import_ref Main//import_generic, loc8_14, loaded [symbolic = @C.as.I.impl.431d5e.1.%T (constants.%T)] // CHECK:STDOUT: %Main.import_ref.2c7 = import_ref Main//import_generic, loc12_35, unloaded // CHECK:STDOUT: %I.impl_witness_table.a23 = impl_witness_table (), @C.as.I.impl.8159c7.1 [concrete] // CHECK:STDOUT: %Main.import_ref.61cd7d.2: type = import_ref Main//import_generic, loc12_24, loaded [concrete = constants.%C] // CHECK:STDOUT: %Main.import_ref.ada: type = import_ref Main//import_generic, loc12_33, loaded [symbolic = @C.as.I.impl.8159c7.1.%I.type (constants.%I.type.e19)] // CHECK:STDOUT: %Main.import_ref.b3bc94.4: type = import_ref Main//import_generic, loc12_14, loaded [symbolic = @C.as.I.impl.8159c7.1.%T (constants.%T)] // CHECK:STDOUT: %Main.import_ref.a5e = import_ref Main//import_generic, loc15_28, unloaded // CHECK:STDOUT: %Main.import_ref.213: type = import_ref Main//import_generic, loc15_18, loaded [symbolic = @N.WithSelf.Self.binding.as_type.impls.I.type.require.%Self.binding.as_type (constants.%Self.binding.as_type)] // CHECK:STDOUT: %Main.import_ref.3582b1.2: type = import_ref Main//import_generic, loc15_27, loaded [symbolic = @N.WithSelf.Self.binding.as_type.impls.I.type.require.%I.type (constants.%I.type.1ab)] // CHECK:STDOUT: %Main.import_ref.b3bc94.6: type = import_ref Main//import_generic, loc14_14, loaded [symbolic = @N.%T (constants.%T)] // CHECK:STDOUT: %Main.import_ref.2fc394.2: @N.%N.type (%N.type.d682d6.1) = import_ref Main//import_generic, loc14_24, loaded [symbolic = @N.%Self (constants.%Self.e1f642.1)] // CHECK:STDOUT: %Main.import_ref.830 = import_ref Main//import_generic, loc14_24, unloaded // CHECK:STDOUT: %Main.import_ref.b3bc94.7: type = import_ref Main//import_generic, loc14_14, loaded [symbolic = @N.%T (constants.%T)] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .C = imports.%Main.C // CHECK:STDOUT: .I = imports.%Main.I // CHECK:STDOUT: .N = imports.%Main.N // CHECK:STDOUT: } // CHECK:STDOUT: %default.import.loc2_30.1 = import // CHECK:STDOUT: %default.import.loc2_30.2 = import // CHECK:STDOUT: impl_decl @C.as.I.impl.431d5e.2 [concrete] { // CHECK:STDOUT: %T.patt: %pattern_type = symbolic_binding_pattern T, 0 [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %C.ref: type = name_ref C, imports.%Main.C [concrete = constants.%C] // CHECK:STDOUT: %I.ref: %I.type.609 = name_ref I, imports.%Main.I [concrete = constants.%I.generic] // CHECK:STDOUT: %T.ref: type = name_ref T, %T.loc8_14.1 [symbolic = %T.loc8_14.2 (constants.%T)] // CHECK:STDOUT: %I.type.loc8_32.1: type = facet_type <@I, @I(constants.%T)> [symbolic = %I.type.loc8_32.2 (constants.%I.type.1ab)] // CHECK:STDOUT: %.loc8_18.1: type = splice_block %.loc8_18.2 [concrete = type] { // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %.loc8_18.2: type = type_literal type [concrete = type] // CHECK:STDOUT: } // CHECK:STDOUT: %T.loc8_14.1: type = symbolic_binding T, 0 [symbolic = %T.loc8_14.2 (constants.%T)] // CHECK:STDOUT: } // CHECK:STDOUT: impl_decl @C.as.I.impl.431d5e.3 [concrete] { // CHECK:STDOUT: %T.patt: %pattern_type = symbolic_binding_pattern T, 0 [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %C.ref: type = name_ref C, imports.%Main.C [concrete = constants.%C] // CHECK:STDOUT: %I.ref: %I.type.609 = name_ref I, imports.%Main.I [concrete = constants.%I.generic] // CHECK:STDOUT: %T.ref: type = name_ref T, %T.loc14_14.1 [symbolic = %T.loc14_14.2 (constants.%T)] // CHECK:STDOUT: %I.type.loc14_32.1: type = facet_type <@I, @I(constants.%T)> [symbolic = %I.type.loc14_32.2 (constants.%I.type.1ab)] // CHECK:STDOUT: %.loc14_18.1: type = splice_block %.loc14_18.2 [concrete = type] { // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %.loc14_18.2: type = type_literal type [concrete = type] // CHECK:STDOUT: } // CHECK:STDOUT: %T.loc14_14.1: type = symbolic_binding T, 0 [symbolic = %T.loc14_14.2 (constants.%T)] // CHECK:STDOUT: } // CHECK:STDOUT: impl_decl @C.as.I.impl.8159c7.2 [concrete] { // CHECK:STDOUT: %T.patt: %pattern_type = symbolic_binding_pattern T, 0 [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %C.ref: type = name_ref C, imports.%Main.C [concrete = constants.%C] // CHECK:STDOUT: %I.ref: %I.type.609 = name_ref I, imports.%Main.I [concrete = constants.%I.generic] // CHECK:STDOUT: %T.ref: type = name_ref T, %T.loc20_14.1 [symbolic = %T.loc20_14.2 (constants.%T)] // CHECK:STDOUT: %ptr.loc20_32.1: type = ptr_type %T.ref [symbolic = %ptr.loc20_32.2 (constants.%ptr)] // CHECK:STDOUT: %I.type.loc20_33.1: type = facet_type <@I, @I(constants.%ptr)> [symbolic = %I.type.loc20_33.2 (constants.%I.type.e19)] // CHECK:STDOUT: %.loc20_18.1: type = splice_block %.loc20_18.2 [concrete = type] { // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %.loc20_18.2: type = type_literal type [concrete = type] // CHECK:STDOUT: } // CHECK:STDOUT: %T.loc20_14.1: type = symbolic_binding T, 0 [symbolic = %T.loc20_14.2 (constants.%T)] // CHECK:STDOUT: } // CHECK:STDOUT: impl_decl @C.as.I.impl.8159c7.3 [concrete] { // CHECK:STDOUT: %T.patt: %pattern_type = symbolic_binding_pattern T, 0 [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %C.ref: type = name_ref C, imports.%Main.C [concrete = constants.%C] // CHECK:STDOUT: %I.ref: %I.type.609 = name_ref I, imports.%Main.I [concrete = constants.%I.generic] // CHECK:STDOUT: %T.ref: type = name_ref T, %T.loc26_14.1 [symbolic = %T.loc26_14.2 (constants.%T)] // CHECK:STDOUT: %ptr.loc26_32.1: type = ptr_type %T.ref [symbolic = %ptr.loc26_32.2 (constants.%ptr)] // CHECK:STDOUT: %I.type.loc26_33.1: type = facet_type <@I, @I(constants.%ptr)> [symbolic = %I.type.loc26_33.2 (constants.%I.type.e19)] // CHECK:STDOUT: %.loc26_18.1: type = splice_block %.loc26_18.2 [concrete = type] { // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %.loc26_18.2: type = type_literal type [concrete = type] // CHECK:STDOUT: } // CHECK:STDOUT: %T.loc26_14.1: type = symbolic_binding T, 0 [symbolic = %T.loc26_14.2 (constants.%T)] // CHECK:STDOUT: } // CHECK:STDOUT: impl_decl @C.as.I.impl.c9332a.1 [concrete] { // CHECK:STDOUT: %T.patt: %pattern_type = symbolic_binding_pattern T, 0 [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %C.ref: type = name_ref C, imports.%Main.C [concrete = constants.%C] // CHECK:STDOUT: %N.ref: %N.type.b47 = name_ref N, imports.%Main.N [concrete = constants.%empty_struct] // CHECK:STDOUT: %T.ref: type = name_ref T, %T.loc32_14.1 [symbolic = %T.loc32_14.2 (constants.%T)] // CHECK:STDOUT: %N.type.loc32_32.1: type = facet_type <@N, @N(constants.%T)> [symbolic = %N.type.loc32_32.2 (constants.%N.type.d682d6.1)] // CHECK:STDOUT: %.loc32_18.1: type = splice_block %.loc32_18.2 [concrete = type] { // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %.loc32_18.2: type = type_literal type [concrete = type] // CHECK:STDOUT: } // CHECK:STDOUT: %T.loc32_14.1: type = symbolic_binding T, 0 [symbolic = %T.loc32_14.2 (constants.%T)] // CHECK:STDOUT: } // CHECK:STDOUT: impl_decl @C.as.I.impl.c9332a.2 [concrete] { // CHECK:STDOUT: %T.patt: %pattern_type = symbolic_binding_pattern T, 0 [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %C.ref: type = name_ref C, imports.%Main.C [concrete = constants.%C] // CHECK:STDOUT: %N.ref: %N.type.b47 = name_ref N, imports.%Main.N [concrete = constants.%empty_struct] // CHECK:STDOUT: %T.ref: type = name_ref T, %T.loc37_14.1 [symbolic = %T.loc37_14.2 (constants.%T)] // CHECK:STDOUT: %ptr.loc37_32.1: type = ptr_type %T.ref [symbolic = %ptr.loc37_32.2 (constants.%ptr)] // CHECK:STDOUT: %N.type.loc37_33.1: type = facet_type <@N, @N(constants.%ptr)> [symbolic = %N.type.loc37_33.2 (constants.%N.type.d682d6.2)] // CHECK:STDOUT: %.loc37_18.1: type = splice_block %.loc37_18.2 [concrete = type] { // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %.loc37_18.2: type = type_literal type [concrete = type] // CHECK:STDOUT: } // CHECK:STDOUT: %T.loc37_14.1: type = symbolic_binding T, 0 [symbolic = %T.loc37_14.2 (constants.%T)] // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic interface @I(imports.%Main.import_ref.b3bc94.2: type) [from "import_generic.carbon"] { // CHECK:STDOUT: %T: type = symbolic_binding T, 0 [symbolic = %T (constants.%T)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %I.type: type = facet_type <@I, @I(%T)> [symbolic = %I.type (constants.%I.type.1ab)] // CHECK:STDOUT: %Self: @I.%I.type (%I.type.1ab) = symbolic_binding Self, 1 [symbolic = %Self (constants.%Self.c1b)] // CHECK:STDOUT: // CHECK:STDOUT: interface { // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = imports.%Main.import_ref.3fa // CHECK:STDOUT: witness = () // CHECK:STDOUT: // CHECK:STDOUT: !requires: // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic constraint @N(imports.%Main.import_ref.b3bc94.7: type) [from "import_generic.carbon"] { // CHECK:STDOUT: %T: type = symbolic_binding T, 0 [symbolic = %T (constants.%T)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %N.type: type = facet_type <@N, @N(%T)> [symbolic = %N.type (constants.%N.type.d682d6.1)] // CHECK:STDOUT: %Self: @N.%N.type (%N.type.d682d6.1) = symbolic_binding Self, 1 [symbolic = %Self (constants.%Self.e1f642.1)] // CHECK:STDOUT: // CHECK:STDOUT: constraint { // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = imports.%Main.import_ref.830 // CHECK:STDOUT: extend imports.%Main.import_ref.a5e // CHECK:STDOUT: // CHECK:STDOUT: !requires: // CHECK:STDOUT: @N.WithSelf.Self.binding.as_type.impls.I.type.require { // CHECK:STDOUT: require imports.%Main.import_ref.213 impls imports.%Main.import_ref.3582b1.2 // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic require @N.WithSelf.Self.binding.as_type.impls.I.type.require(imports.%Main.import_ref.b3bc94.6: type, imports.%Main.import_ref.2fc394.2: @N.%N.type (%N.type.d682d6.1)) [from "import_generic.carbon"] { // CHECK:STDOUT: %T: type = symbolic_binding T, 0 [symbolic = %T (constants.%T)] // CHECK:STDOUT: %N.type: type = facet_type <@N, @N(%T)> [symbolic = %N.type (constants.%N.type.d682d6.1)] // CHECK:STDOUT: %Self: @N.WithSelf.Self.binding.as_type.impls.I.type.require.%N.type (%N.type.d682d6.1) = symbolic_binding Self, 1 [symbolic = %Self (constants.%Self.e1f642.1)] // CHECK:STDOUT: %Self.binding.as_type: type = symbolic_binding_type Self, 1, %Self [symbolic = %Self.binding.as_type (constants.%Self.binding.as_type)] // CHECK:STDOUT: %I.type: type = facet_type <@I, @I(%T)> [symbolic = %I.type (constants.%I.type.1ab)] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic impl @C.as.I.impl.431d5e.1(imports.%Main.import_ref.b3bc94.3: type) [from "import_generic.carbon"] { // CHECK:STDOUT: %T: type = symbolic_binding T, 0 [symbolic = %T (constants.%T)] // CHECK:STDOUT: %I.type: type = facet_type <@I, @I(%T)> [symbolic = %I.type (constants.%I.type.1ab)] // CHECK:STDOUT: %I.impl_witness: = impl_witness imports.%I.impl_witness_table.2ac, @C.as.I.impl.431d5e.1(%T) [symbolic = %I.impl_witness (constants.%I.impl_witness.9f5)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete: = require_complete_type %I.type [symbolic = %require_complete (constants.%require_complete.e36)] // CHECK:STDOUT: // CHECK:STDOUT: impl: imports.%Main.import_ref.61cd7d.1 as imports.%Main.import_ref.3582b1.1 { // CHECK:STDOUT: !members: // CHECK:STDOUT: witness = imports.%Main.import_ref.e08 // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic impl @C.as.I.impl.8159c7.1(imports.%Main.import_ref.b3bc94.4: type) [from "import_generic.carbon"] { // CHECK:STDOUT: %T: type = symbolic_binding T, 0 [symbolic = %T (constants.%T)] // CHECK:STDOUT: %ptr: type = ptr_type %T [symbolic = %ptr (constants.%ptr)] // CHECK:STDOUT: %I.type: type = facet_type <@I, @I(%ptr)> [symbolic = %I.type (constants.%I.type.e19)] // CHECK:STDOUT: %I.impl_witness: = impl_witness imports.%I.impl_witness_table.a23, @C.as.I.impl.8159c7.1(%T) [symbolic = %I.impl_witness (constants.%I.impl_witness.18a)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete: = require_complete_type %I.type [symbolic = %require_complete (constants.%require_complete.ffd)] // CHECK:STDOUT: // CHECK:STDOUT: impl: imports.%Main.import_ref.61cd7d.2 as imports.%Main.import_ref.ada { // CHECK:STDOUT: !members: // CHECK:STDOUT: witness = imports.%Main.import_ref.2c7 // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic impl @C.as.I.impl.431d5e.2(%T.loc8_14.1: type) { // CHECK:STDOUT: %T.loc8_14.2: type = symbolic_binding T, 0 [symbolic = %T.loc8_14.2 (constants.%T)] // CHECK:STDOUT: %I.type.loc8_32.2: type = facet_type <@I, @I(%T.loc8_14.2)> [symbolic = %I.type.loc8_32.2 (constants.%I.type.1ab)] // CHECK:STDOUT: // CHECK:STDOUT: impl: %C.ref as %I.type.loc8_32.1; // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic impl @C.as.I.impl.431d5e.3(%T.loc14_14.1: type) { // CHECK:STDOUT: %T.loc14_14.2: type = symbolic_binding T, 0 [symbolic = %T.loc14_14.2 (constants.%T)] // CHECK:STDOUT: %I.type.loc14_32.2: type = facet_type <@I, @I(%T.loc14_14.2)> [symbolic = %I.type.loc14_32.2 (constants.%I.type.1ab)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: // CHECK:STDOUT: impl: %C.ref as %I.type.loc14_32.1 { // CHECK:STDOUT: !members: // CHECK:STDOUT: witness = // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic impl @C.as.I.impl.8159c7.2(%T.loc20_14.1: type) { // CHECK:STDOUT: %T.loc20_14.2: type = symbolic_binding T, 0 [symbolic = %T.loc20_14.2 (constants.%T)] // CHECK:STDOUT: %ptr.loc20_32.2: type = ptr_type %T.loc20_14.2 [symbolic = %ptr.loc20_32.2 (constants.%ptr)] // CHECK:STDOUT: %I.type.loc20_33.2: type = facet_type <@I, @I(%ptr.loc20_32.2)> [symbolic = %I.type.loc20_33.2 (constants.%I.type.e19)] // CHECK:STDOUT: // CHECK:STDOUT: impl: %C.ref as %I.type.loc20_33.1; // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic impl @C.as.I.impl.8159c7.3(%T.loc26_14.1: type) { // CHECK:STDOUT: %T.loc26_14.2: type = symbolic_binding T, 0 [symbolic = %T.loc26_14.2 (constants.%T)] // CHECK:STDOUT: %ptr.loc26_32.2: type = ptr_type %T.loc26_14.2 [symbolic = %ptr.loc26_32.2 (constants.%ptr)] // CHECK:STDOUT: %I.type.loc26_33.2: type = facet_type <@I, @I(%ptr.loc26_32.2)> [symbolic = %I.type.loc26_33.2 (constants.%I.type.e19)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: // CHECK:STDOUT: impl: %C.ref as %I.type.loc26_33.1 { // CHECK:STDOUT: !members: // CHECK:STDOUT: witness = // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic impl @C.as.I.impl.c9332a.1(%T.loc32_14.1: type) { // CHECK:STDOUT: %T.loc32_14.2: type = symbolic_binding T, 0 [symbolic = %T.loc32_14.2 (constants.%T)] // CHECK:STDOUT: %N.type.loc32_32.2: type = facet_type <@N, @N(%T.loc32_14.2)> [symbolic = %N.type.loc32_32.2 (constants.%N.type.d682d6.1)] // CHECK:STDOUT: // CHECK:STDOUT: impl: %C.ref as %N.type.loc32_32.1; // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic impl @C.as.I.impl.c9332a.2(%T.loc37_14.1: type) { // CHECK:STDOUT: %T.loc37_14.2: type = symbolic_binding T, 0 [symbolic = %T.loc37_14.2 (constants.%T)] // CHECK:STDOUT: %ptr.loc37_32.2: type = ptr_type %T.loc37_14.2 [symbolic = %ptr.loc37_32.2 (constants.%ptr)] // CHECK:STDOUT: %N.type.loc37_33.2: type = facet_type <@N, @N(%ptr.loc37_32.2)> [symbolic = %N.type.loc37_33.2 (constants.%N.type.d682d6.2)] // CHECK:STDOUT: // CHECK:STDOUT: impl: %C.ref as %N.type.loc37_33.1; // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @C [from "import_generic.carbon"] { // CHECK:STDOUT: complete_type_witness = imports.%Main.import_ref.8f2 // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = imports.%Main.import_ref.743 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @I(constants.%T) { // CHECK:STDOUT: %T => constants.%T // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %I.type => constants.%I.type.1ab // CHECK:STDOUT: %Self => constants.%Self.c1b // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @I.WithSelf(constants.%T, constants.%Self.c1b) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @C.as.I.impl.431d5e.1(constants.%T) { // CHECK:STDOUT: %T => constants.%T // CHECK:STDOUT: %I.type => constants.%I.type.1ab // CHECK:STDOUT: %I.impl_witness => constants.%I.impl_witness.9f5 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @I(constants.%ptr) { // CHECK:STDOUT: %T => constants.%ptr // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %I.type => constants.%I.type.e19 // CHECK:STDOUT: %Self => constants.%Self.dda // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @C.as.I.impl.8159c7.1(constants.%T) { // CHECK:STDOUT: %T => constants.%T // CHECK:STDOUT: %ptr => constants.%ptr // CHECK:STDOUT: %I.type => constants.%I.type.e19 // CHECK:STDOUT: %I.impl_witness => constants.%I.impl_witness.18a // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @C.as.I.impl.431d5e.2(constants.%T) { // CHECK:STDOUT: %T.loc8_14.2 => constants.%T // CHECK:STDOUT: %I.type.loc8_32.2 => constants.%I.type.1ab // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @C.as.I.impl.431d5e.3(constants.%T) { // CHECK:STDOUT: %T.loc14_14.2 => constants.%T // CHECK:STDOUT: %I.type.loc14_32.2 => constants.%I.type.1ab // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @C.as.I.impl.8159c7.2(constants.%T) { // CHECK:STDOUT: %T.loc20_14.2 => constants.%T // CHECK:STDOUT: %ptr.loc20_32.2 => constants.%ptr // CHECK:STDOUT: %I.type.loc20_33.2 => constants.%I.type.e19 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @C.as.I.impl.8159c7.3(constants.%T) { // CHECK:STDOUT: %T.loc26_14.2 => constants.%T // CHECK:STDOUT: %ptr.loc26_32.2 => constants.%ptr // CHECK:STDOUT: %I.type.loc26_33.2 => constants.%I.type.e19 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @N(constants.%T) { // CHECK:STDOUT: %T => constants.%T // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %N.type => constants.%N.type.d682d6.1 // CHECK:STDOUT: %Self => constants.%Self.e1f642.1 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @N.WithSelf(constants.%T, constants.%Self.e1f642.1) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: %T => constants.%T // CHECK:STDOUT: %I.type => constants.%I.type.1ab // CHECK:STDOUT: %require_complete => constants.%require_complete.e36 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @N.WithSelf.Self.binding.as_type.impls.I.type.require(constants.%T, constants.%Self.e1f642.1) { // CHECK:STDOUT: %T => constants.%T // CHECK:STDOUT: %N.type => constants.%N.type.d682d6.1 // CHECK:STDOUT: %Self => constants.%Self.e1f642.1 // CHECK:STDOUT: %Self.binding.as_type => constants.%Self.binding.as_type // CHECK:STDOUT: %I.type => constants.%I.type.1ab // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @N.WithSelf(constants.%T, constants.%C.type.facet) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: %T => constants.%T // CHECK:STDOUT: %I.type => constants.%I.type.1ab // CHECK:STDOUT: %require_complete => constants.%require_complete.e36 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @N.WithSelf.Self.binding.as_type.impls.I.type.require(constants.%T, constants.%C.type.facet) { // CHECK:STDOUT: %T => constants.%T // CHECK:STDOUT: %N.type => constants.%N.type.d682d6.1 // CHECK:STDOUT: %Self => constants.%C.type.facet // CHECK:STDOUT: %Self.binding.as_type => constants.%C // CHECK:STDOUT: %I.type => constants.%I.type.1ab // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @C.as.I.impl.c9332a.1(constants.%T) { // CHECK:STDOUT: %T.loc32_14.2 => constants.%T // CHECK:STDOUT: %N.type.loc32_32.2 => constants.%N.type.d682d6.1 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @N(constants.%ptr) { // CHECK:STDOUT: %T => constants.%ptr // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %N.type => constants.%N.type.d682d6.2 // CHECK:STDOUT: %Self => constants.%Self.e1f642.2 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @N.WithSelf(constants.%ptr, constants.%Self.e1f642.1) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: %T => constants.%ptr // CHECK:STDOUT: %I.type => constants.%I.type.e19 // CHECK:STDOUT: %require_complete => constants.%require_complete.ffd // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @N.WithSelf(constants.%ptr, constants.%C.type.facet) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: %T => constants.%ptr // CHECK:STDOUT: %I.type => constants.%I.type.e19 // CHECK:STDOUT: %require_complete => constants.%require_complete.ffd // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @N.WithSelf.Self.binding.as_type.impls.I.type.require(constants.%ptr, constants.%C.type.facet) { // CHECK:STDOUT: %T => constants.%ptr // CHECK:STDOUT: %N.type => constants.%N.type.d682d6.2 // CHECK:STDOUT: %Self => constants.%C.type.facet // CHECK:STDOUT: %Self.binding.as_type => constants.%C // CHECK:STDOUT: %I.type => constants.%I.type.e19 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @C.as.I.impl.c9332a.2(constants.%T) { // CHECK:STDOUT: %T.loc37_14.2 => constants.%T // CHECK:STDOUT: %ptr.loc37_32.2 => constants.%ptr // CHECK:STDOUT: %N.type.loc37_33.2 => constants.%N.type.d682d6.2 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- import_generic_with_different_specific.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %C: type = class_type @C [concrete] // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %complete_type: = complete_type_witness %empty_struct_type [concrete] // CHECK:STDOUT: %type: type = facet_type [concrete] // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self] // CHECK:STDOUT: %pattern_type: type = pattern_type type [concrete] // CHECK:STDOUT: %T: type = symbolic_binding T, 0 [symbolic] // CHECK:STDOUT: %I.type.609: type = generic_interface_type @I [concrete] // CHECK:STDOUT: %I.generic: %I.type.609 = struct_value () [concrete] // CHECK:STDOUT: %I.type.1ab: type = facet_type <@I, @I(%T)> [symbolic] // CHECK:STDOUT: %Self.fdb: %I.type.1ab = symbolic_binding Self, 1 [symbolic] // CHECK:STDOUT: %I.impl_witness: = impl_witness @C.as.I.impl.%I.impl_witness_table, @C.as.I.impl(%T) [symbolic] // CHECK:STDOUT: %require_complete: = require_complete_type %I.type.1ab [symbolic] // CHECK:STDOUT: %I.facet: %I.type.1ab = facet_value %C, (%I.impl_witness) [symbolic] // CHECK:STDOUT: %N.type.b47: type = generic_named_constaint_type @N [concrete] // CHECK:STDOUT: %empty_struct: %N.type.b47 = struct_value () [concrete] // CHECK:STDOUT: %N.type.d68: type = facet_type <@N, @N(%T)> [symbolic] // CHECK:STDOUT: %Self.31c: %N.type.d68 = symbolic_binding Self, 1 [symbolic] // CHECK:STDOUT: %Self.binding.as_type: type = symbolic_binding_type Self, 1, %Self.31c [symbolic] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .C = %C.decl // CHECK:STDOUT: .I = %I.decl // CHECK:STDOUT: .N = %N.decl // CHECK:STDOUT: } // CHECK:STDOUT: %C.decl: type = class_decl @C [concrete = constants.%C] {} {} // CHECK:STDOUT: %I.decl: %I.type.609 = interface_decl @I [concrete = constants.%I.generic] { // CHECK:STDOUT: %T.patt: %pattern_type = symbolic_binding_pattern T, 0 [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %.loc5_17.1: type = splice_block %.loc5_17.2 [concrete = type] { // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %.loc5_17.2: type = type_literal type [concrete = type] // CHECK:STDOUT: } // CHECK:STDOUT: %T.loc5_13.2: type = symbolic_binding T, 0 [symbolic = %T.loc5_13.1 (constants.%T)] // CHECK:STDOUT: } // CHECK:STDOUT: impl_decl @C.as.I.impl [concrete] { // CHECK:STDOUT: %T.patt: %pattern_type = symbolic_binding_pattern T, 0 [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %C.ref: type = name_ref C, file.%C.decl [concrete = constants.%C] // CHECK:STDOUT: %I.ref: %I.type.609 = name_ref I, file.%I.decl [concrete = constants.%I.generic] // CHECK:STDOUT: %T.ref: type = name_ref T, %T.loc7_14.1 [symbolic = %T.loc7_14.2 (constants.%T)] // CHECK:STDOUT: %I.type.loc7_32.1: type = facet_type <@I, @I(constants.%T)> [symbolic = %I.type.loc7_32.2 (constants.%I.type.1ab)] // CHECK:STDOUT: %.loc7_18.1: type = splice_block %.loc7_18.2 [concrete = type] { // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %.loc7_18.2: type = type_literal type [concrete = type] // CHECK:STDOUT: } // CHECK:STDOUT: %T.loc7_14.1: type = symbolic_binding T, 0 [symbolic = %T.loc7_14.2 (constants.%T)] // CHECK:STDOUT: } // CHECK:STDOUT: %N.decl: %N.type.b47 = constraint_decl @N [concrete = constants.%empty_struct] { // CHECK:STDOUT: %T.patt: %pattern_type = symbolic_binding_pattern T, 0 [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %.loc9_18.1: type = splice_block %.loc9_18.2 [concrete = type] { // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %.loc9_18.2: type = type_literal type [concrete = type] // CHECK:STDOUT: } // CHECK:STDOUT: %T.loc9_14.2: type = symbolic_binding T, 0 [symbolic = %T.loc9_14.1 (constants.%T)] // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic interface @I(%T.loc5_13.2: type) { // CHECK:STDOUT: %T.loc5_13.1: type = symbolic_binding T, 0 [symbolic = %T.loc5_13.1 (constants.%T)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %I.type: type = facet_type <@I, @I(%T.loc5_13.1)> [symbolic = %I.type (constants.%I.type.1ab)] // CHECK:STDOUT: %Self.loc5_23.2: @I.%I.type (%I.type.1ab) = symbolic_binding Self, 1 [symbolic = %Self.loc5_23.2 (constants.%Self.fdb)] // CHECK:STDOUT: // CHECK:STDOUT: interface { // CHECK:STDOUT: %Self.loc5_23.1: @I.%I.type (%I.type.1ab) = symbolic_binding Self, 1 [symbolic = %Self.loc5_23.2 (constants.%Self.fdb)] // CHECK:STDOUT: %I.WithSelf.decl = interface_with_self_decl @I [concrete] // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = %Self.loc5_23.1 // CHECK:STDOUT: witness = () // CHECK:STDOUT: // CHECK:STDOUT: !requires: // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic constraint @N(%T.loc9_14.2: type) { // CHECK:STDOUT: %T.loc9_14.1: type = symbolic_binding T, 0 [symbolic = %T.loc9_14.1 (constants.%T)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %N.type: type = facet_type <@N, @N(%T.loc9_14.1)> [symbolic = %N.type (constants.%N.type.d68)] // CHECK:STDOUT: %Self.loc9_24.2: @N.%N.type (%N.type.d68) = symbolic_binding Self, 1 [symbolic = %Self.loc9_24.2 (constants.%Self.31c)] // CHECK:STDOUT: // CHECK:STDOUT: constraint { // CHECK:STDOUT: %Self.loc9_24.1: @N.%N.type (%N.type.d68) = symbolic_binding Self, 1 [symbolic = %Self.loc9_24.2 (constants.%Self.31c)] // CHECK:STDOUT: %N.WithSelf.decl = constraint_with_self_decl @N [concrete] // CHECK:STDOUT: // CHECK:STDOUT: !with Self: // CHECK:STDOUT: %N.WithSelf.Self.binding.as_type.impls.I.type.require.decl = require_decl @N.WithSelf.Self.binding.as_type.impls.I.type.require [concrete] { // CHECK:STDOUT: require %Self.as_type impls %I.type.loc10_27.1 // CHECK:STDOUT: } { // CHECK:STDOUT: %Self.as_type: type = facet_access_type @N.%Self.loc9_24.1 [symbolic = %Self.binding.as_type (constants.%Self.binding.as_type)] // CHECK:STDOUT: %I.ref: %I.type.609 = name_ref I, file.%I.decl [concrete = constants.%I.generic] // CHECK:STDOUT: %T.ref: type = name_ref T, @N.%T.loc9_14.2 [symbolic = %T (constants.%T)] // CHECK:STDOUT: %I.type.loc10_27.1: type = facet_type <@I, @I(constants.%T)> [symbolic = %I.type.loc10_27.2 (constants.%I.type.1ab)] // CHECK:STDOUT: } // CHECK:STDOUT: %.loc10: type = specific_constant @N.WithSelf.Self.binding.as_type.impls.I.type.require.%I.type.loc10_27.1, @N.WithSelf.Self.binding.as_type.impls.I.type.require(constants.%T, constants.%Self.31c) [symbolic = %I.type (constants.%I.type.1ab)] // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = %Self.loc9_24.1 // CHECK:STDOUT: .I = // CHECK:STDOUT: .T = // CHECK:STDOUT: .I = // CHECK:STDOUT: .T = // CHECK:STDOUT: extend @N.WithSelf.%.loc10 // CHECK:STDOUT: // CHECK:STDOUT: !requires: // CHECK:STDOUT: @N.WithSelf.Self.binding.as_type.impls.I.type.require { // CHECK:STDOUT: require @N.WithSelf.Self.binding.as_type.impls.I.type.require.%Self.as_type impls @N.WithSelf.Self.binding.as_type.impls.I.type.require.%I.type.loc10_27.1 // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic require @N.WithSelf.Self.binding.as_type.impls.I.type.require(@N.%T.loc9_14.2: type, @N.%Self.loc9_24.1: @N.%N.type (%N.type.d68)) { // CHECK:STDOUT: %T: type = symbolic_binding T, 0 [symbolic = %T (constants.%T)] // CHECK:STDOUT: %N.type: type = facet_type <@N, @N(%T)> [symbolic = %N.type (constants.%N.type.d68)] // CHECK:STDOUT: %Self: @N.WithSelf.Self.binding.as_type.impls.I.type.require.%N.type (%N.type.d68) = symbolic_binding Self, 1 [symbolic = %Self (constants.%Self.31c)] // CHECK:STDOUT: %Self.binding.as_type: type = symbolic_binding_type Self, 1, %Self [symbolic = %Self.binding.as_type (constants.%Self.binding.as_type)] // CHECK:STDOUT: %I.type.loc10_27.2: type = facet_type <@I, @I(%T)> [symbolic = %I.type.loc10_27.2 (constants.%I.type.1ab)] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic impl @C.as.I.impl(%T.loc7_14.1: type) { // CHECK:STDOUT: %T.loc7_14.2: type = symbolic_binding T, 0 [symbolic = %T.loc7_14.2 (constants.%T)] // CHECK:STDOUT: %I.type.loc7_32.2: type = facet_type <@I, @I(%T.loc7_14.2)> [symbolic = %I.type.loc7_32.2 (constants.%I.type.1ab)] // CHECK:STDOUT: %I.impl_witness.loc7_34.2: = impl_witness %I.impl_witness_table, @C.as.I.impl(%T.loc7_14.2) [symbolic = %I.impl_witness.loc7_34.2 (constants.%I.impl_witness)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete: = require_complete_type %I.type.loc7_32.2 [symbolic = %require_complete (constants.%require_complete)] // CHECK:STDOUT: // CHECK:STDOUT: impl: %C.ref as %I.type.loc7_32.1 { // CHECK:STDOUT: %I.impl_witness_table = impl_witness_table (), @C.as.I.impl [concrete] // CHECK:STDOUT: %I.impl_witness.loc7_34.1: = impl_witness %I.impl_witness_table, @C.as.I.impl(constants.%T) [symbolic = %I.impl_witness.loc7_34.2 (constants.%I.impl_witness)] // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: witness = %I.impl_witness.loc7_34.1 // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @C { // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%C // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @I(constants.%T) { // CHECK:STDOUT: %T.loc5_13.1 => constants.%T // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %I.type => constants.%I.type.1ab // CHECK:STDOUT: %Self.loc5_23.2 => constants.%Self.fdb // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @I.WithSelf(constants.%T, constants.%Self.fdb) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @C.as.I.impl(constants.%T) { // CHECK:STDOUT: %T.loc7_14.2 => constants.%T // CHECK:STDOUT: %I.type.loc7_32.2 => constants.%I.type.1ab // CHECK:STDOUT: %I.impl_witness.loc7_34.2 => constants.%I.impl_witness // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @I.WithSelf(constants.%T, constants.%I.facet) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @N(constants.%T) { // CHECK:STDOUT: %T.loc9_14.1 => constants.%T // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @N.WithSelf(constants.%T, constants.%Self.31c) {} // CHECK:STDOUT: // CHECK:STDOUT: specific @N.WithSelf.Self.binding.as_type.impls.I.type.require(constants.%T, constants.%Self.31c) { // CHECK:STDOUT: %T => constants.%T // CHECK:STDOUT: %N.type => constants.%N.type.d68 // CHECK:STDOUT: %Self => constants.%Self.31c // CHECK:STDOUT: %Self.binding.as_type => constants.%Self.binding.as_type // CHECK:STDOUT: %I.type.loc10_27.2 => constants.%I.type.1ab // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- import_generic_with_different_specific.impl.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %T: type = symbolic_binding T, 0 [symbolic] // CHECK:STDOUT: %I.type.609: type = generic_interface_type @I [concrete] // CHECK:STDOUT: %I.generic: %I.type.609 = struct_value () [concrete] // CHECK:STDOUT: %I.type.1ab: type = facet_type <@I, @I(%T)> [symbolic] // CHECK:STDOUT: %Self.c1b: %I.type.1ab = symbolic_binding Self, 1 [symbolic] // CHECK:STDOUT: %pattern_type: type = pattern_type type [concrete] // CHECK:STDOUT: %C: type = class_type @C [concrete] // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %complete_type: = complete_type_witness %empty_struct_type [concrete] // CHECK:STDOUT: %I.impl_witness.9f5: = impl_witness imports.%I.impl_witness_table, @C.as.I.impl.431(%T) [symbolic] // CHECK:STDOUT: %require_complete.e36: = require_complete_type %I.type.1ab [symbolic] // CHECK:STDOUT: %type: type = facet_type [concrete] // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self] // CHECK:STDOUT: %ptr.e8f: type = ptr_type %T [symbolic] // CHECK:STDOUT: %I.type.e19: type = facet_type <@I, @I(%ptr.e8f)> [symbolic] // CHECK:STDOUT: %I.impl_witness.18a: = impl_witness @C.as.I.impl.815.%I.impl_witness_table, @C.as.I.impl.815(%T) [symbolic] // CHECK:STDOUT: %Self.dda: %I.type.e19 = symbolic_binding Self, 1 [symbolic] // CHECK:STDOUT: %require_complete.ffd: = require_complete_type %I.type.e19 [symbolic] // CHECK:STDOUT: %I.facet.064: %I.type.e19 = facet_value %C, (%I.impl_witness.18a) [symbolic] // CHECK:STDOUT: %N.type.b47: type = generic_named_constaint_type @N [concrete] // CHECK:STDOUT: %empty_struct: %N.type.b47 = struct_value () [concrete] // CHECK:STDOUT: %N.type.d682d6.1: type = facet_type <@N, @N(%T)> [symbolic] // CHECK:STDOUT: %Self.e1f642.1: %N.type.d682d6.1 = symbolic_binding Self, 1 [symbolic] // CHECK:STDOUT: %Self.binding.as_type: type = symbolic_binding_type Self, 1, %Self.e1f642.1 [symbolic] // CHECK:STDOUT: %ptr.125: type = ptr_type %ptr.e8f [symbolic] // CHECK:STDOUT: %N.type.d682d6.2: type = facet_type <@N, @N(%ptr.125)> [symbolic] // CHECK:STDOUT: %Self.e1f642.2: %N.type.d682d6.2 = symbolic_binding Self, 1 [symbolic] // CHECK:STDOUT: %I.type.08a: type = facet_type <@I, @I(%ptr.125)> [symbolic] // CHECK:STDOUT: %require_complete.ea6: = require_complete_type %I.type.08a [symbolic] // CHECK:STDOUT: %C.type.facet: %type = facet_value %C, () [concrete] // CHECK:STDOUT: %I.impl_witness.d57: = impl_witness @C.as.I.impl.c93.%I.impl_witness_table, @C.as.I.impl.c93(%T) [symbolic] // CHECK:STDOUT: %require_complete.533: = require_complete_type %N.type.d682d6.2 [symbolic] // CHECK:STDOUT: %I.facet.3e9: %I.type.08a = facet_value %C, (%I.impl_witness.d57) [symbolic] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Main.C: type = import_ref Main//import_generic_with_different_specific, C, loaded [concrete = constants.%C] // CHECK:STDOUT: %Main.I: %I.type.609 = import_ref Main//import_generic_with_different_specific, I, loaded [concrete = constants.%I.generic] // CHECK:STDOUT: %Main.N: %N.type.b47 = import_ref Main//import_generic_with_different_specific, N, loaded [concrete = constants.%empty_struct] // CHECK:STDOUT: %Main.import_ref.3fa = import_ref Main//import_generic_with_different_specific, loc5_23, unloaded // CHECK:STDOUT: %Main.import_ref.b3bc94.2: type = import_ref Main//import_generic_with_different_specific, loc5_13, loaded [symbolic = @I.%T (constants.%T)] // CHECK:STDOUT: %Main.import_ref.e08 = import_ref Main//import_generic_with_different_specific, loc7_34, unloaded // CHECK:STDOUT: %Main.import_ref.8f2: = import_ref Main//import_generic_with_different_specific, loc4_10, loaded [concrete = constants.%complete_type] // CHECK:STDOUT: %Main.import_ref.743 = import_ref Main//import_generic_with_different_specific, inst{{[0-9A-F]+}} [no loc], unloaded // CHECK:STDOUT: %I.impl_witness_table = impl_witness_table (), @C.as.I.impl.431 [concrete] // CHECK:STDOUT: %Main.import_ref.61c: type = import_ref Main//import_generic_with_different_specific, loc7_24, loaded [concrete = constants.%C] // CHECK:STDOUT: %Main.import_ref.3582b1.1: type = import_ref Main//import_generic_with_different_specific, loc7_32, loaded [symbolic = @C.as.I.impl.431.%I.type (constants.%I.type.1ab)] // CHECK:STDOUT: %Main.import_ref.b3bc94.3: type = import_ref Main//import_generic_with_different_specific, loc7_14, loaded [symbolic = @C.as.I.impl.431.%T (constants.%T)] // CHECK:STDOUT: %Main.import_ref.a5e = import_ref Main//import_generic_with_different_specific, loc10_28, unloaded // CHECK:STDOUT: %Main.import_ref.213: type = import_ref Main//import_generic_with_different_specific, loc10_18, loaded [symbolic = @N.WithSelf.Self.binding.as_type.impls.I.type.require.%Self.binding.as_type (constants.%Self.binding.as_type)] // CHECK:STDOUT: %Main.import_ref.3582b1.2: type = import_ref Main//import_generic_with_different_specific, loc10_27, loaded [symbolic = @N.WithSelf.Self.binding.as_type.impls.I.type.require.%I.type (constants.%I.type.1ab)] // CHECK:STDOUT: %Main.import_ref.b3bc94.5: type = import_ref Main//import_generic_with_different_specific, loc9_14, loaded [symbolic = @N.%T (constants.%T)] // CHECK:STDOUT: %Main.import_ref.2fc394.2: @N.%N.type (%N.type.d682d6.1) = import_ref Main//import_generic_with_different_specific, loc9_24, loaded [symbolic = @N.%Self (constants.%Self.e1f642.1)] // CHECK:STDOUT: %Main.import_ref.830 = import_ref Main//import_generic_with_different_specific, loc9_24, unloaded // CHECK:STDOUT: %Main.import_ref.b3bc94.6: type = import_ref Main//import_generic_with_different_specific, loc9_14, loaded [symbolic = @N.%T (constants.%T)] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .C = imports.%Main.C // CHECK:STDOUT: .I = imports.%Main.I // CHECK:STDOUT: .N = imports.%Main.N // CHECK:STDOUT: } // CHECK:STDOUT: %default.import.loc1_54.1 = import // CHECK:STDOUT: %default.import.loc1_54.2 = import // CHECK:STDOUT: impl_decl @C.as.I.impl.815 [concrete] { // CHECK:STDOUT: %T.patt: %pattern_type = symbolic_binding_pattern T, 0 [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %C.ref: type = name_ref C, imports.%Main.C [concrete = constants.%C] // CHECK:STDOUT: %I.ref: %I.type.609 = name_ref I, imports.%Main.I [concrete = constants.%I.generic] // CHECK:STDOUT: %T.ref: type = name_ref T, %T.loc5_14.1 [symbolic = %T.loc5_14.2 (constants.%T)] // CHECK:STDOUT: %ptr.loc5_32.1: type = ptr_type %T.ref [symbolic = %ptr.loc5_32.2 (constants.%ptr.e8f)] // CHECK:STDOUT: %I.type.loc5_33.1: type = facet_type <@I, @I(constants.%ptr.e8f)> [symbolic = %I.type.loc5_33.2 (constants.%I.type.e19)] // CHECK:STDOUT: %.loc5_18.1: type = splice_block %.loc5_18.2 [concrete = type] { // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %.loc5_18.2: type = type_literal type [concrete = type] // CHECK:STDOUT: } // CHECK:STDOUT: %T.loc5_14.1: type = symbolic_binding T, 0 [symbolic = %T.loc5_14.2 (constants.%T)] // CHECK:STDOUT: } // CHECK:STDOUT: impl_decl @C.as.I.impl.c93 [concrete] { // CHECK:STDOUT: %T.patt: %pattern_type = symbolic_binding_pattern T, 0 [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %C.ref: type = name_ref C, imports.%Main.C [concrete = constants.%C] // CHECK:STDOUT: %N.ref: %N.type.b47 = name_ref N, imports.%Main.N [concrete = constants.%empty_struct] // CHECK:STDOUT: %T.ref: type = name_ref T, %T.loc6_14.1 [symbolic = %T.loc6_14.2 (constants.%T)] // CHECK:STDOUT: %ptr.loc6_32.1: type = ptr_type %T.ref [symbolic = %ptr.loc6_32.2 (constants.%ptr.e8f)] // CHECK:STDOUT: %ptr.loc6_33.1: type = ptr_type %ptr.loc6_32.1 [symbolic = %ptr.loc6_33.2 (constants.%ptr.125)] // CHECK:STDOUT: %N.type.loc6_34.1: type = facet_type <@N, @N(constants.%ptr.125)> [symbolic = %N.type.loc6_34.2 (constants.%N.type.d682d6.2)] // CHECK:STDOUT: %.loc6_18.1: type = splice_block %.loc6_18.2 [concrete = type] { // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %.loc6_18.2: type = type_literal type [concrete = type] // CHECK:STDOUT: } // CHECK:STDOUT: %T.loc6_14.1: type = symbolic_binding T, 0 [symbolic = %T.loc6_14.2 (constants.%T)] // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic interface @I(imports.%Main.import_ref.b3bc94.2: type) [from "import_generic_with_different_specific.carbon"] { // CHECK:STDOUT: %T: type = symbolic_binding T, 0 [symbolic = %T (constants.%T)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %I.type: type = facet_type <@I, @I(%T)> [symbolic = %I.type (constants.%I.type.1ab)] // CHECK:STDOUT: %Self: @I.%I.type (%I.type.1ab) = symbolic_binding Self, 1 [symbolic = %Self (constants.%Self.c1b)] // CHECK:STDOUT: // CHECK:STDOUT: interface { // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = imports.%Main.import_ref.3fa // CHECK:STDOUT: witness = () // CHECK:STDOUT: // CHECK:STDOUT: !requires: // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic constraint @N(imports.%Main.import_ref.b3bc94.6: type) [from "import_generic_with_different_specific.carbon"] { // CHECK:STDOUT: %T: type = symbolic_binding T, 0 [symbolic = %T (constants.%T)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %N.type: type = facet_type <@N, @N(%T)> [symbolic = %N.type (constants.%N.type.d682d6.1)] // CHECK:STDOUT: %Self: @N.%N.type (%N.type.d682d6.1) = symbolic_binding Self, 1 [symbolic = %Self (constants.%Self.e1f642.1)] // CHECK:STDOUT: // CHECK:STDOUT: constraint { // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = imports.%Main.import_ref.830 // CHECK:STDOUT: extend imports.%Main.import_ref.a5e // CHECK:STDOUT: // CHECK:STDOUT: !requires: // CHECK:STDOUT: @N.WithSelf.Self.binding.as_type.impls.I.type.require { // CHECK:STDOUT: require imports.%Main.import_ref.213 impls imports.%Main.import_ref.3582b1.2 // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic require @N.WithSelf.Self.binding.as_type.impls.I.type.require(imports.%Main.import_ref.b3bc94.5: type, imports.%Main.import_ref.2fc394.2: @N.%N.type (%N.type.d682d6.1)) [from "import_generic_with_different_specific.carbon"] { // CHECK:STDOUT: %T: type = symbolic_binding T, 0 [symbolic = %T (constants.%T)] // CHECK:STDOUT: %N.type: type = facet_type <@N, @N(%T)> [symbolic = %N.type (constants.%N.type.d682d6.1)] // CHECK:STDOUT: %Self: @N.WithSelf.Self.binding.as_type.impls.I.type.require.%N.type (%N.type.d682d6.1) = symbolic_binding Self, 1 [symbolic = %Self (constants.%Self.e1f642.1)] // CHECK:STDOUT: %Self.binding.as_type: type = symbolic_binding_type Self, 1, %Self [symbolic = %Self.binding.as_type (constants.%Self.binding.as_type)] // CHECK:STDOUT: %I.type: type = facet_type <@I, @I(%T)> [symbolic = %I.type (constants.%I.type.1ab)] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic impl @C.as.I.impl.431(imports.%Main.import_ref.b3bc94.3: type) [from "import_generic_with_different_specific.carbon"] { // CHECK:STDOUT: %T: type = symbolic_binding T, 0 [symbolic = %T (constants.%T)] // CHECK:STDOUT: %I.type: type = facet_type <@I, @I(%T)> [symbolic = %I.type (constants.%I.type.1ab)] // CHECK:STDOUT: %I.impl_witness: = impl_witness imports.%I.impl_witness_table, @C.as.I.impl.431(%T) [symbolic = %I.impl_witness (constants.%I.impl_witness.9f5)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete: = require_complete_type %I.type [symbolic = %require_complete (constants.%require_complete.e36)] // CHECK:STDOUT: // CHECK:STDOUT: impl: imports.%Main.import_ref.61c as imports.%Main.import_ref.3582b1.1 { // CHECK:STDOUT: !members: // CHECK:STDOUT: witness = imports.%Main.import_ref.e08 // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic impl @C.as.I.impl.815(%T.loc5_14.1: type) { // CHECK:STDOUT: %T.loc5_14.2: type = symbolic_binding T, 0 [symbolic = %T.loc5_14.2 (constants.%T)] // CHECK:STDOUT: %ptr.loc5_32.2: type = ptr_type %T.loc5_14.2 [symbolic = %ptr.loc5_32.2 (constants.%ptr.e8f)] // CHECK:STDOUT: %I.type.loc5_33.2: type = facet_type <@I, @I(%ptr.loc5_32.2)> [symbolic = %I.type.loc5_33.2 (constants.%I.type.e19)] // CHECK:STDOUT: %I.impl_witness.loc5_35.2: = impl_witness %I.impl_witness_table, @C.as.I.impl.815(%T.loc5_14.2) [symbolic = %I.impl_witness.loc5_35.2 (constants.%I.impl_witness.18a)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete: = require_complete_type %I.type.loc5_33.2 [symbolic = %require_complete (constants.%require_complete.ffd)] // CHECK:STDOUT: // CHECK:STDOUT: impl: %C.ref as %I.type.loc5_33.1 { // CHECK:STDOUT: %I.impl_witness_table = impl_witness_table (), @C.as.I.impl.815 [concrete] // CHECK:STDOUT: %I.impl_witness.loc5_35.1: = impl_witness %I.impl_witness_table, @C.as.I.impl.815(constants.%T) [symbolic = %I.impl_witness.loc5_35.2 (constants.%I.impl_witness.18a)] // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: witness = %I.impl_witness.loc5_35.1 // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic impl @C.as.I.impl.c93(%T.loc6_14.1: type) { // CHECK:STDOUT: %T.loc6_14.2: type = symbolic_binding T, 0 [symbolic = %T.loc6_14.2 (constants.%T)] // CHECK:STDOUT: %ptr.loc6_32.2: type = ptr_type %T.loc6_14.2 [symbolic = %ptr.loc6_32.2 (constants.%ptr.e8f)] // CHECK:STDOUT: %ptr.loc6_33.2: type = ptr_type %ptr.loc6_32.2 [symbolic = %ptr.loc6_33.2 (constants.%ptr.125)] // CHECK:STDOUT: %N.type.loc6_34.2: type = facet_type <@N, @N(%ptr.loc6_33.2)> [symbolic = %N.type.loc6_34.2 (constants.%N.type.d682d6.2)] // CHECK:STDOUT: %I.impl_witness.loc6_36.2: = impl_witness %I.impl_witness_table, @C.as.I.impl.c93(%T.loc6_14.2) [symbolic = %I.impl_witness.loc6_36.2 (constants.%I.impl_witness.d57)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete: = require_complete_type %N.type.loc6_34.2 [symbolic = %require_complete (constants.%require_complete.533)] // CHECK:STDOUT: // CHECK:STDOUT: impl: %C.ref as %N.type.loc6_34.1 { // CHECK:STDOUT: %I.impl_witness_table = impl_witness_table (), @C.as.I.impl.c93 [concrete] // CHECK:STDOUT: %I.impl_witness.loc6_36.1: = impl_witness %I.impl_witness_table, @C.as.I.impl.c93(constants.%T) [symbolic = %I.impl_witness.loc6_36.2 (constants.%I.impl_witness.d57)] // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: witness = %I.impl_witness.loc6_36.1 // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @C [from "import_generic_with_different_specific.carbon"] { // CHECK:STDOUT: complete_type_witness = imports.%Main.import_ref.8f2 // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = imports.%Main.import_ref.743 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @I(constants.%T) { // CHECK:STDOUT: %T => constants.%T // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %I.type => constants.%I.type.1ab // CHECK:STDOUT: %Self => constants.%Self.c1b // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @I.WithSelf(constants.%T, constants.%Self.c1b) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @C.as.I.impl.431(constants.%T) { // CHECK:STDOUT: %T => constants.%T // CHECK:STDOUT: %I.type => constants.%I.type.1ab // CHECK:STDOUT: %I.impl_witness => constants.%I.impl_witness.9f5 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @I(constants.%ptr.e8f) { // CHECK:STDOUT: %T => constants.%ptr.e8f // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %I.type => constants.%I.type.e19 // CHECK:STDOUT: %Self => constants.%Self.dda // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @C.as.I.impl.815(constants.%T) { // CHECK:STDOUT: %T.loc5_14.2 => constants.%T // CHECK:STDOUT: %ptr.loc5_32.2 => constants.%ptr.e8f // CHECK:STDOUT: %I.type.loc5_33.2 => constants.%I.type.e19 // CHECK:STDOUT: %I.impl_witness.loc5_35.2 => constants.%I.impl_witness.18a // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @I.WithSelf(constants.%ptr.e8f, constants.%Self.c1b) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @I.WithSelf(constants.%ptr.e8f, constants.%I.facet.064) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @N(constants.%T) { // CHECK:STDOUT: %T => constants.%T // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @N.WithSelf(constants.%T, constants.%Self.e1f642.1) {} // CHECK:STDOUT: // CHECK:STDOUT: specific @N.WithSelf.Self.binding.as_type.impls.I.type.require(constants.%T, constants.%Self.e1f642.1) { // CHECK:STDOUT: %T => constants.%T // CHECK:STDOUT: %N.type => constants.%N.type.d682d6.1 // CHECK:STDOUT: %Self => constants.%Self.e1f642.1 // CHECK:STDOUT: %Self.binding.as_type => constants.%Self.binding.as_type // CHECK:STDOUT: %I.type => constants.%I.type.1ab // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @N(constants.%ptr.125) { // CHECK:STDOUT: %T => constants.%ptr.125 // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %N.type => constants.%N.type.d682d6.2 // CHECK:STDOUT: %Self => constants.%Self.e1f642.2 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @N.WithSelf(constants.%ptr.125, constants.%Self.e1f642.1) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: %T => constants.%ptr.125 // CHECK:STDOUT: %I.type => constants.%I.type.08a // CHECK:STDOUT: %require_complete => constants.%require_complete.ea6 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @I(constants.%ptr.125) { // CHECK:STDOUT: %T => constants.%ptr.125 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @N.WithSelf(constants.%ptr.125, constants.%C.type.facet) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: %T => constants.%ptr.125 // CHECK:STDOUT: %I.type => constants.%I.type.08a // CHECK:STDOUT: %require_complete => constants.%require_complete.ea6 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @N.WithSelf.Self.binding.as_type.impls.I.type.require(constants.%ptr.125, constants.%C.type.facet) { // CHECK:STDOUT: %T => constants.%ptr.125 // CHECK:STDOUT: %N.type => constants.%N.type.d682d6.2 // CHECK:STDOUT: %Self => constants.%C.type.facet // CHECK:STDOUT: %Self.binding.as_type => constants.%C // CHECK:STDOUT: %I.type => constants.%I.type.08a // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @C.as.I.impl.c93(constants.%T) { // CHECK:STDOUT: %T.loc6_14.2 => constants.%T // CHECK:STDOUT: %ptr.loc6_32.2 => constants.%ptr.e8f // CHECK:STDOUT: %ptr.loc6_33.2 => constants.%ptr.125 // CHECK:STDOUT: %N.type.loc6_34.2 => constants.%N.type.d682d6.2 // CHECK:STDOUT: %I.impl_witness.loc6_36.2 => constants.%I.impl_witness.d57 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @I.WithSelf(constants.%ptr.125, constants.%I.facet.3e9) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- fail_import_generic_decl.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %D: type = class_type @D [concrete] // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %complete_type: = complete_type_witness %empty_struct_type [concrete] // CHECK:STDOUT: %type: type = facet_type [concrete] // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self] // CHECK:STDOUT: %pattern_type: type = pattern_type type [concrete] // CHECK:STDOUT: %T: type = symbolic_binding T, 0 [symbolic] // CHECK:STDOUT: %J.type.885: type = generic_interface_type @J [concrete] // CHECK:STDOUT: %J.generic: %J.type.885 = struct_value () [concrete] // CHECK:STDOUT: %J.type.04e: type = facet_type <@J, @J(%T)> [symbolic] // CHECK:STDOUT: %Self.a60: %J.type.04e = symbolic_binding Self, 1 [symbolic] // CHECK:STDOUT: %N.type.b47: type = generic_named_constaint_type @N [concrete] // CHECK:STDOUT: %empty_struct: %N.type.b47 = struct_value () [concrete] // CHECK:STDOUT: %N.type.d68: type = facet_type <@N, @N(%T)> [symbolic] // CHECK:STDOUT: %Self.31c: %N.type.d68 = symbolic_binding Self, 1 [symbolic] // CHECK:STDOUT: %Self.binding.as_type: type = symbolic_binding_type Self, 1, %Self.31c [symbolic] // CHECK:STDOUT: %J.impl_witness.861: = impl_witness @D.as.J.impl.b25.%J.impl_witness_table, @D.as.J.impl.b25(%T) [symbolic] // CHECK:STDOUT: %ptr: type = ptr_type %T [symbolic] // CHECK:STDOUT: %J.type.80f: type = facet_type <@J, @J(%ptr)> [symbolic] // CHECK:STDOUT: %J.impl_witness.f4f: = impl_witness @D.as.J.impl.f05.%J.impl_witness_table, @D.as.J.impl.f05(%T) [symbolic] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .D = %D.decl // CHECK:STDOUT: .J = %J.decl // CHECK:STDOUT: .N = %N.decl // CHECK:STDOUT: } // CHECK:STDOUT: %D.decl: type = class_decl @D [concrete = constants.%D] {} {} // CHECK:STDOUT: %J.decl: %J.type.885 = interface_decl @J [concrete = constants.%J.generic] { // CHECK:STDOUT: %T.patt: %pattern_type = symbolic_binding_pattern T, 0 [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %.loc5_17.1: type = splice_block %.loc5_17.2 [concrete = type] { // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %.loc5_17.2: type = type_literal type [concrete = type] // CHECK:STDOUT: } // CHECK:STDOUT: %T.loc5_13.2: type = symbolic_binding T, 0 [symbolic = %T.loc5_13.1 (constants.%T)] // CHECK:STDOUT: } // CHECK:STDOUT: %N.decl: %N.type.b47 = constraint_decl @N [concrete = constants.%empty_struct] { // CHECK:STDOUT: %T.patt: %pattern_type = symbolic_binding_pattern T, 0 [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %.loc7_18.1: type = splice_block %.loc7_18.2 [concrete = type] { // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %.loc7_18.2: type = type_literal type [concrete = type] // CHECK:STDOUT: } // CHECK:STDOUT: %T.loc7_14.2: type = symbolic_binding T, 0 [symbolic = %T.loc7_14.1 (constants.%T)] // CHECK:STDOUT: } // CHECK:STDOUT: impl_decl @D.as.J.impl.b25 [concrete] { // CHECK:STDOUT: %T.patt: %pattern_type = symbolic_binding_pattern T, 0 [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %D.ref: type = name_ref D, file.%D.decl [concrete = constants.%D] // CHECK:STDOUT: %J.ref: %J.type.885 = name_ref J, file.%J.decl [concrete = constants.%J.generic] // CHECK:STDOUT: %T.ref: type = name_ref T, %T.loc15_14.1 [symbolic = %T.loc15_14.2 (constants.%T)] // CHECK:STDOUT: %J.type.loc15_32.1: type = facet_type <@J, @J(constants.%T)> [symbolic = %J.type.loc15_32.2 (constants.%J.type.04e)] // CHECK:STDOUT: %.loc15_18.1: type = splice_block %.loc15_18.2 [concrete = type] { // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %.loc15_18.2: type = type_literal type [concrete = type] // CHECK:STDOUT: } // CHECK:STDOUT: %T.loc15_14.1: type = symbolic_binding T, 0 [symbolic = %T.loc15_14.2 (constants.%T)] // CHECK:STDOUT: } // CHECK:STDOUT: impl_decl @D.as.J.impl.f05 [concrete] { // CHECK:STDOUT: %T.patt: %pattern_type = symbolic_binding_pattern T, 0 [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %D.ref: type = name_ref D, file.%D.decl [concrete = constants.%D] // CHECK:STDOUT: %J.ref: %J.type.885 = name_ref J, file.%J.decl [concrete = constants.%J.generic] // CHECK:STDOUT: %T.ref: type = name_ref T, %T.loc21_14.1 [symbolic = %T.loc21_14.2 (constants.%T)] // CHECK:STDOUT: %ptr.loc21_32.1: type = ptr_type %T.ref [symbolic = %ptr.loc21_32.2 (constants.%ptr)] // CHECK:STDOUT: %J.type.loc21_33.1: type = facet_type <@J, @J(constants.%ptr)> [symbolic = %J.type.loc21_33.2 (constants.%J.type.80f)] // CHECK:STDOUT: %.loc21_18.1: type = splice_block %.loc21_18.2 [concrete = type] { // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %.loc21_18.2: type = type_literal type [concrete = type] // CHECK:STDOUT: } // CHECK:STDOUT: %T.loc21_14.1: type = symbolic_binding T, 0 [symbolic = %T.loc21_14.2 (constants.%T)] // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic interface @J(%T.loc5_13.2: type) { // CHECK:STDOUT: %T.loc5_13.1: type = symbolic_binding T, 0 [symbolic = %T.loc5_13.1 (constants.%T)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %J.type: type = facet_type <@J, @J(%T.loc5_13.1)> [symbolic = %J.type (constants.%J.type.04e)] // CHECK:STDOUT: %Self.loc5_23.2: @J.%J.type (%J.type.04e) = symbolic_binding Self, 1 [symbolic = %Self.loc5_23.2 (constants.%Self.a60)] // CHECK:STDOUT: // CHECK:STDOUT: interface { // CHECK:STDOUT: %Self.loc5_23.1: @J.%J.type (%J.type.04e) = symbolic_binding Self, 1 [symbolic = %Self.loc5_23.2 (constants.%Self.a60)] // CHECK:STDOUT: %J.WithSelf.decl = interface_with_self_decl @J [concrete] // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = %Self.loc5_23.1 // CHECK:STDOUT: witness = () // CHECK:STDOUT: // CHECK:STDOUT: !requires: // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic constraint @N(%T.loc7_14.2: type) { // CHECK:STDOUT: %T.loc7_14.1: type = symbolic_binding T, 0 [symbolic = %T.loc7_14.1 (constants.%T)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %N.type: type = facet_type <@N, @N(%T.loc7_14.1)> [symbolic = %N.type (constants.%N.type.d68)] // CHECK:STDOUT: %Self.loc7_24.2: @N.%N.type (%N.type.d68) = symbolic_binding Self, 1 [symbolic = %Self.loc7_24.2 (constants.%Self.31c)] // CHECK:STDOUT: // CHECK:STDOUT: constraint { // CHECK:STDOUT: %Self.loc7_24.1: @N.%N.type (%N.type.d68) = symbolic_binding Self, 1 [symbolic = %Self.loc7_24.2 (constants.%Self.31c)] // CHECK:STDOUT: %N.WithSelf.decl = constraint_with_self_decl @N [concrete] // CHECK:STDOUT: // CHECK:STDOUT: !with Self: // CHECK:STDOUT: %N.WithSelf.Self.binding.as_type.impls.J.type.require.decl = require_decl @N.WithSelf.Self.binding.as_type.impls.J.type.require [concrete] { // CHECK:STDOUT: require %Self.as_type impls %J.type.loc8_27.1 // CHECK:STDOUT: } { // CHECK:STDOUT: %Self.as_type: type = facet_access_type @N.%Self.loc7_24.1 [symbolic = %Self.binding.as_type (constants.%Self.binding.as_type)] // CHECK:STDOUT: %J.ref: %J.type.885 = name_ref J, file.%J.decl [concrete = constants.%J.generic] // CHECK:STDOUT: %T.ref: type = name_ref T, @N.%T.loc7_14.2 [symbolic = %T (constants.%T)] // CHECK:STDOUT: %J.type.loc8_27.1: type = facet_type <@J, @J(constants.%T)> [symbolic = %J.type.loc8_27.2 (constants.%J.type.04e)] // CHECK:STDOUT: } // CHECK:STDOUT: %.loc8: type = specific_constant @N.WithSelf.Self.binding.as_type.impls.J.type.require.%J.type.loc8_27.1, @N.WithSelf.Self.binding.as_type.impls.J.type.require(constants.%T, constants.%Self.31c) [symbolic = %J.type (constants.%J.type.04e)] // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = %Self.loc7_24.1 // CHECK:STDOUT: .J = // CHECK:STDOUT: .T = // CHECK:STDOUT: .J = // CHECK:STDOUT: .T = // CHECK:STDOUT: extend @N.WithSelf.%.loc8 // CHECK:STDOUT: // CHECK:STDOUT: !requires: // CHECK:STDOUT: @N.WithSelf.Self.binding.as_type.impls.J.type.require { // CHECK:STDOUT: require @N.WithSelf.Self.binding.as_type.impls.J.type.require.%Self.as_type impls @N.WithSelf.Self.binding.as_type.impls.J.type.require.%J.type.loc8_27.1 // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic require @N.WithSelf.Self.binding.as_type.impls.J.type.require(@N.%T.loc7_14.2: type, @N.%Self.loc7_24.1: @N.%N.type (%N.type.d68)) { // CHECK:STDOUT: %T: type = symbolic_binding T, 0 [symbolic = %T (constants.%T)] // CHECK:STDOUT: %N.type: type = facet_type <@N, @N(%T)> [symbolic = %N.type (constants.%N.type.d68)] // CHECK:STDOUT: %Self: @N.WithSelf.Self.binding.as_type.impls.J.type.require.%N.type (%N.type.d68) = symbolic_binding Self, 1 [symbolic = %Self (constants.%Self.31c)] // CHECK:STDOUT: %Self.binding.as_type: type = symbolic_binding_type Self, 1, %Self [symbolic = %Self.binding.as_type (constants.%Self.binding.as_type)] // CHECK:STDOUT: %J.type.loc8_27.2: type = facet_type <@J, @J(%T)> [symbolic = %J.type.loc8_27.2 (constants.%J.type.04e)] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic impl @D.as.J.impl.b25(%T.loc15_14.1: type) { // CHECK:STDOUT: %T.loc15_14.2: type = symbolic_binding T, 0 [symbolic = %T.loc15_14.2 (constants.%T)] // CHECK:STDOUT: %J.type.loc15_32.2: type = facet_type <@J, @J(%T.loc15_14.2)> [symbolic = %J.type.loc15_32.2 (constants.%J.type.04e)] // CHECK:STDOUT: %J.impl_witness.loc15_33.2: = impl_witness %J.impl_witness_table, @D.as.J.impl.b25(%T.loc15_14.2) [symbolic = %J.impl_witness.loc15_33.2 (constants.%J.impl_witness.861)] // CHECK:STDOUT: // CHECK:STDOUT: impl: %D.ref as %J.type.loc15_32.1; // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic impl @D.as.J.impl.f05(%T.loc21_14.1: type) { // CHECK:STDOUT: %T.loc21_14.2: type = symbolic_binding T, 0 [symbolic = %T.loc21_14.2 (constants.%T)] // CHECK:STDOUT: %ptr.loc21_32.2: type = ptr_type %T.loc21_14.2 [symbolic = %ptr.loc21_32.2 (constants.%ptr)] // CHECK:STDOUT: %J.type.loc21_33.2: type = facet_type <@J, @J(%ptr.loc21_32.2)> [symbolic = %J.type.loc21_33.2 (constants.%J.type.80f)] // CHECK:STDOUT: %J.impl_witness.loc21_34.2: = impl_witness %J.impl_witness_table, @D.as.J.impl.f05(%T.loc21_14.2) [symbolic = %J.impl_witness.loc21_34.2 (constants.%J.impl_witness.f4f)] // CHECK:STDOUT: // CHECK:STDOUT: impl: %D.ref as %J.type.loc21_33.1; // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @D { // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%D // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @J(constants.%T) { // CHECK:STDOUT: %T.loc5_13.1 => constants.%T // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %J.type => constants.%J.type.04e // CHECK:STDOUT: %Self.loc5_23.2 => constants.%Self.a60 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @J.WithSelf(constants.%T, constants.%Self.a60) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @N(constants.%T) { // CHECK:STDOUT: %T.loc7_14.1 => constants.%T // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @N.WithSelf(constants.%T, constants.%Self.31c) {} // CHECK:STDOUT: // CHECK:STDOUT: specific @N.WithSelf.Self.binding.as_type.impls.J.type.require(constants.%T, constants.%Self.31c) { // CHECK:STDOUT: %T => constants.%T // CHECK:STDOUT: %N.type => constants.%N.type.d68 // CHECK:STDOUT: %Self => constants.%Self.31c // CHECK:STDOUT: %Self.binding.as_type => constants.%Self.binding.as_type // CHECK:STDOUT: %J.type.loc8_27.2 => constants.%J.type.04e // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @D.as.J.impl.b25(constants.%T) { // CHECK:STDOUT: %T.loc15_14.2 => constants.%T // CHECK:STDOUT: %J.type.loc15_32.2 => constants.%J.type.04e // CHECK:STDOUT: %J.impl_witness.loc15_33.2 => constants.%J.impl_witness.861 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @J(constants.%ptr) { // CHECK:STDOUT: %T.loc5_13.1 => constants.%ptr // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @D.as.J.impl.f05(constants.%T) { // CHECK:STDOUT: %T.loc21_14.2 => constants.%T // CHECK:STDOUT: %ptr.loc21_32.2 => constants.%ptr // CHECK:STDOUT: %J.type.loc21_33.2 => constants.%J.type.80f // CHECK:STDOUT: %J.impl_witness.loc21_34.2 => constants.%J.impl_witness.f4f // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- fail_import_generic_decl.impl.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %T: type = symbolic_binding T, 0 [symbolic] // CHECK:STDOUT: %J.type.885: type = generic_interface_type @J [concrete] // CHECK:STDOUT: %J.generic: %J.type.885 = struct_value () [concrete] // CHECK:STDOUT: %J.type.04e: type = facet_type <@J, @J(%T)> [symbolic] // CHECK:STDOUT: %Self.c25: %J.type.04e = symbolic_binding Self, 1 [symbolic] // CHECK:STDOUT: %pattern_type: type = pattern_type type [concrete] // CHECK:STDOUT: %D: type = class_type @D [concrete] // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %complete_type: = complete_type_witness %empty_struct_type [concrete] // CHECK:STDOUT: %J.impl_witness.861: = impl_witness imports.%J.impl_witness_table.50b, @D.as.J.impl.b25a4e.1(%T) [symbolic] // CHECK:STDOUT: %ptr: type = ptr_type %T [symbolic] // CHECK:STDOUT: %J.type.80f: type = facet_type <@J, @J(%ptr)> [symbolic] // CHECK:STDOUT: %J.impl_witness.f4f: = impl_witness imports.%J.impl_witness_table.a37, @D.as.J.impl.f050b9.1(%T) [symbolic] // CHECK:STDOUT: %type: type = facet_type [concrete] // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self] // CHECK:STDOUT: %N.type.b47: type = generic_named_constaint_type @N [concrete] // CHECK:STDOUT: %empty_struct: %N.type.b47 = struct_value () [concrete] // CHECK:STDOUT: %N.type.d682d6.1: type = facet_type <@N, @N(%T)> [symbolic] // CHECK:STDOUT: %Self.e1f642.1: %N.type.d682d6.1 = symbolic_binding Self, 1 [symbolic] // CHECK:STDOUT: %require_complete.cc6: = require_complete_type %J.type.04e [symbolic] // CHECK:STDOUT: %Self.binding.as_type: type = symbolic_binding_type Self, 1, %Self.e1f642.1 [symbolic] // CHECK:STDOUT: %D.type.facet: %type = facet_value %D, () [concrete] // CHECK:STDOUT: %N.type.d682d6.2: type = facet_type <@N, @N(%ptr)> [symbolic] // CHECK:STDOUT: %Self.e1f642.2: %N.type.d682d6.2 = symbolic_binding Self, 1 [symbolic] // CHECK:STDOUT: %require_complete.19b: = require_complete_type %J.type.80f [symbolic] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Main.D: type = import_ref Main//import_generic_decl, D, loaded [concrete = constants.%D] // CHECK:STDOUT: %Main.J: %J.type.885 = import_ref Main//import_generic_decl, J, loaded [concrete = constants.%J.generic] // CHECK:STDOUT: %Main.N: %N.type.b47 = import_ref Main//import_generic_decl, N, loaded [concrete = constants.%empty_struct] // CHECK:STDOUT: %Main.import_ref.e9b = import_ref Main//import_generic_decl, loc5_23, unloaded // CHECK:STDOUT: %Main.import_ref.b3bc94.2: type = import_ref Main//import_generic_decl, loc5_13, loaded [symbolic = @J.%T (constants.%T)] // CHECK:STDOUT: %Main.import_ref.8f2: = import_ref Main//import_generic_decl, loc4_10, loaded [concrete = constants.%complete_type] // CHECK:STDOUT: %Main.import_ref.1d5 = import_ref Main//import_generic_decl, inst{{[0-9A-F]+}} [no loc], unloaded // CHECK:STDOUT: %J.impl_witness_table.50b = impl_witness_table (), @D.as.J.impl.b25a4e.1 [concrete] // CHECK:STDOUT: %Main.import_ref.4ae308.1: type = import_ref Main//import_generic_decl, loc15_24, loaded [concrete = constants.%D] // CHECK:STDOUT: %Main.import_ref.f2a55c.1: type = import_ref Main//import_generic_decl, loc15_32, loaded [symbolic = @D.as.J.impl.b25a4e.1.%J.type (constants.%J.type.04e)] // CHECK:STDOUT: %Main.import_ref.b3bc94.3: type = import_ref Main//import_generic_decl, loc15_14, loaded [symbolic = @D.as.J.impl.b25a4e.1.%T (constants.%T)] // CHECK:STDOUT: %J.impl_witness_table.a37 = impl_witness_table (), @D.as.J.impl.f050b9.1 [concrete] // CHECK:STDOUT: %Main.import_ref.4ae308.2: type = import_ref Main//import_generic_decl, loc21_24, loaded [concrete = constants.%D] // CHECK:STDOUT: %Main.import_ref.739: type = import_ref Main//import_generic_decl, loc21_33, loaded [symbolic = @D.as.J.impl.f050b9.1.%J.type (constants.%J.type.80f)] // CHECK:STDOUT: %Main.import_ref.b3bc94.4: type = import_ref Main//import_generic_decl, loc21_14, loaded [symbolic = @D.as.J.impl.f050b9.1.%T (constants.%T)] // CHECK:STDOUT: %Main.import_ref.9d6 = import_ref Main//import_generic_decl, loc8_28, unloaded // CHECK:STDOUT: %Main.import_ref.213: type = import_ref Main//import_generic_decl, loc8_18, loaded [symbolic = @N.WithSelf.Self.binding.as_type.impls.J.type.require.%Self.binding.as_type (constants.%Self.binding.as_type)] // CHECK:STDOUT: %Main.import_ref.f2a55c.2: type = import_ref Main//import_generic_decl, loc8_27, loaded [symbolic = @N.WithSelf.Self.binding.as_type.impls.J.type.require.%J.type (constants.%J.type.04e)] // CHECK:STDOUT: %Main.import_ref.b3bc94.6: type = import_ref Main//import_generic_decl, loc7_14, loaded [symbolic = @N.%T (constants.%T)] // CHECK:STDOUT: %Main.import_ref.2fc394.2: @N.%N.type (%N.type.d682d6.1) = import_ref Main//import_generic_decl, loc7_24, loaded [symbolic = @N.%Self (constants.%Self.e1f642.1)] // CHECK:STDOUT: %Main.import_ref.830 = import_ref Main//import_generic_decl, loc7_24, unloaded // CHECK:STDOUT: %Main.import_ref.b3bc94.7: type = import_ref Main//import_generic_decl, loc7_14, loaded [symbolic = @N.%T (constants.%T)] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .D = imports.%Main.D // CHECK:STDOUT: .J = imports.%Main.J // CHECK:STDOUT: .N = imports.%Main.N // CHECK:STDOUT: } // CHECK:STDOUT: %default.import.loc2_35.1 = import // CHECK:STDOUT: %default.import.loc2_35.2 = import // CHECK:STDOUT: impl_decl @D.as.J.impl.b25a4e.2 [concrete] { // CHECK:STDOUT: %T.patt: %pattern_type = symbolic_binding_pattern T, 0 [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %D.ref: type = name_ref D, imports.%Main.D [concrete = constants.%D] // CHECK:STDOUT: %J.ref: %J.type.885 = name_ref J, imports.%Main.J [concrete = constants.%J.generic] // CHECK:STDOUT: %T.ref: type = name_ref T, %T.loc8_14.1 [symbolic = %T.loc8_14.2 (constants.%T)] // CHECK:STDOUT: %J.type.loc8_32.1: type = facet_type <@J, @J(constants.%T)> [symbolic = %J.type.loc8_32.2 (constants.%J.type.04e)] // CHECK:STDOUT: %.loc8_18.1: type = splice_block %.loc8_18.2 [concrete = type] { // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %.loc8_18.2: type = type_literal type [concrete = type] // CHECK:STDOUT: } // CHECK:STDOUT: %T.loc8_14.1: type = symbolic_binding T, 0 [symbolic = %T.loc8_14.2 (constants.%T)] // CHECK:STDOUT: } // CHECK:STDOUT: impl_decl @D.as.J.impl.b25a4e.3 [concrete] { // CHECK:STDOUT: %T.patt: %pattern_type = symbolic_binding_pattern T, 0 [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %D.ref: type = name_ref D, imports.%Main.D [concrete = constants.%D] // CHECK:STDOUT: %J.ref: %J.type.885 = name_ref J, imports.%Main.J [concrete = constants.%J.generic] // CHECK:STDOUT: %T.ref: type = name_ref T, %T.loc14_14.1 [symbolic = %T.loc14_14.2 (constants.%T)] // CHECK:STDOUT: %J.type.loc14_32.1: type = facet_type <@J, @J(constants.%T)> [symbolic = %J.type.loc14_32.2 (constants.%J.type.04e)] // CHECK:STDOUT: %.loc14_18.1: type = splice_block %.loc14_18.2 [concrete = type] { // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %.loc14_18.2: type = type_literal type [concrete = type] // CHECK:STDOUT: } // CHECK:STDOUT: %T.loc14_14.1: type = symbolic_binding T, 0 [symbolic = %T.loc14_14.2 (constants.%T)] // CHECK:STDOUT: } // CHECK:STDOUT: impl_decl @D.as.J.impl.f050b9.2 [concrete] { // CHECK:STDOUT: %T.patt: %pattern_type = symbolic_binding_pattern T, 0 [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %D.ref: type = name_ref D, imports.%Main.D [concrete = constants.%D] // CHECK:STDOUT: %J.ref: %J.type.885 = name_ref J, imports.%Main.J [concrete = constants.%J.generic] // CHECK:STDOUT: %T.ref: type = name_ref T, %T.loc20_14.1 [symbolic = %T.loc20_14.2 (constants.%T)] // CHECK:STDOUT: %ptr.loc20_32.1: type = ptr_type %T.ref [symbolic = %ptr.loc20_32.2 (constants.%ptr)] // CHECK:STDOUT: %J.type.loc20_33.1: type = facet_type <@J, @J(constants.%ptr)> [symbolic = %J.type.loc20_33.2 (constants.%J.type.80f)] // CHECK:STDOUT: %.loc20_18.1: type = splice_block %.loc20_18.2 [concrete = type] { // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %.loc20_18.2: type = type_literal type [concrete = type] // CHECK:STDOUT: } // CHECK:STDOUT: %T.loc20_14.1: type = symbolic_binding T, 0 [symbolic = %T.loc20_14.2 (constants.%T)] // CHECK:STDOUT: } // CHECK:STDOUT: impl_decl @D.as.J.impl.f050b9.3 [concrete] { // CHECK:STDOUT: %T.patt: %pattern_type = symbolic_binding_pattern T, 0 [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %D.ref: type = name_ref D, imports.%Main.D [concrete = constants.%D] // CHECK:STDOUT: %J.ref: %J.type.885 = name_ref J, imports.%Main.J [concrete = constants.%J.generic] // CHECK:STDOUT: %T.ref: type = name_ref T, %T.loc26_14.1 [symbolic = %T.loc26_14.2 (constants.%T)] // CHECK:STDOUT: %ptr.loc26_32.1: type = ptr_type %T.ref [symbolic = %ptr.loc26_32.2 (constants.%ptr)] // CHECK:STDOUT: %J.type.loc26_33.1: type = facet_type <@J, @J(constants.%ptr)> [symbolic = %J.type.loc26_33.2 (constants.%J.type.80f)] // CHECK:STDOUT: %.loc26_18.1: type = splice_block %.loc26_18.2 [concrete = type] { // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %.loc26_18.2: type = type_literal type [concrete = type] // CHECK:STDOUT: } // CHECK:STDOUT: %T.loc26_14.1: type = symbolic_binding T, 0 [symbolic = %T.loc26_14.2 (constants.%T)] // CHECK:STDOUT: } // CHECK:STDOUT: impl_decl @D.as.J.impl.e28c26.1 [concrete] { // CHECK:STDOUT: %T.patt: %pattern_type = symbolic_binding_pattern T, 0 [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %D.ref: type = name_ref D, imports.%Main.D [concrete = constants.%D] // CHECK:STDOUT: %N.ref: %N.type.b47 = name_ref N, imports.%Main.N [concrete = constants.%empty_struct] // CHECK:STDOUT: %T.ref: type = name_ref T, %T.loc32_14.1 [symbolic = %T.loc32_14.2 (constants.%T)] // CHECK:STDOUT: %N.type.loc32_32.1: type = facet_type <@N, @N(constants.%T)> [symbolic = %N.type.loc32_32.2 (constants.%N.type.d682d6.1)] // CHECK:STDOUT: %.loc32_18.1: type = splice_block %.loc32_18.2 [concrete = type] { // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %.loc32_18.2: type = type_literal type [concrete = type] // CHECK:STDOUT: } // CHECK:STDOUT: %T.loc32_14.1: type = symbolic_binding T, 0 [symbolic = %T.loc32_14.2 (constants.%T)] // CHECK:STDOUT: } // CHECK:STDOUT: impl_decl @D.as.J.impl.e28c26.2 [concrete] { // CHECK:STDOUT: %T.patt: %pattern_type = symbolic_binding_pattern T, 0 [concrete] // CHECK:STDOUT: } { // CHECK:STDOUT: %D.ref: type = name_ref D, imports.%Main.D [concrete = constants.%D] // CHECK:STDOUT: %N.ref: %N.type.b47 = name_ref N, imports.%Main.N [concrete = constants.%empty_struct] // CHECK:STDOUT: %T.ref: type = name_ref T, %T.loc38_14.1 [symbolic = %T.loc38_14.2 (constants.%T)] // CHECK:STDOUT: %ptr.loc38_32.1: type = ptr_type %T.ref [symbolic = %ptr.loc38_32.2 (constants.%ptr)] // CHECK:STDOUT: %N.type.loc38_33.1: type = facet_type <@N, @N(constants.%ptr)> [symbolic = %N.type.loc38_33.2 (constants.%N.type.d682d6.2)] // CHECK:STDOUT: %.loc38_18.1: type = splice_block %.loc38_18.2 [concrete = type] { // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %.loc38_18.2: type = type_literal type [concrete = type] // CHECK:STDOUT: } // CHECK:STDOUT: %T.loc38_14.1: type = symbolic_binding T, 0 [symbolic = %T.loc38_14.2 (constants.%T)] // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic interface @J(imports.%Main.import_ref.b3bc94.2: type) [from "fail_import_generic_decl.carbon"] { // CHECK:STDOUT: %T: type = symbolic_binding T, 0 [symbolic = %T (constants.%T)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %J.type: type = facet_type <@J, @J(%T)> [symbolic = %J.type (constants.%J.type.04e)] // CHECK:STDOUT: %Self: @J.%J.type (%J.type.04e) = symbolic_binding Self, 1 [symbolic = %Self (constants.%Self.c25)] // CHECK:STDOUT: // CHECK:STDOUT: interface { // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = imports.%Main.import_ref.e9b // CHECK:STDOUT: witness = () // CHECK:STDOUT: // CHECK:STDOUT: !requires: // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic constraint @N(imports.%Main.import_ref.b3bc94.7: type) [from "fail_import_generic_decl.carbon"] { // CHECK:STDOUT: %T: type = symbolic_binding T, 0 [symbolic = %T (constants.%T)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %N.type: type = facet_type <@N, @N(%T)> [symbolic = %N.type (constants.%N.type.d682d6.1)] // CHECK:STDOUT: %Self: @N.%N.type (%N.type.d682d6.1) = symbolic_binding Self, 1 [symbolic = %Self (constants.%Self.e1f642.1)] // CHECK:STDOUT: // CHECK:STDOUT: constraint { // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = imports.%Main.import_ref.830 // CHECK:STDOUT: extend imports.%Main.import_ref.9d6 // CHECK:STDOUT: // CHECK:STDOUT: !requires: // CHECK:STDOUT: @N.WithSelf.Self.binding.as_type.impls.J.type.require { // CHECK:STDOUT: require imports.%Main.import_ref.213 impls imports.%Main.import_ref.f2a55c.2 // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic require @N.WithSelf.Self.binding.as_type.impls.J.type.require(imports.%Main.import_ref.b3bc94.6: type, imports.%Main.import_ref.2fc394.2: @N.%N.type (%N.type.d682d6.1)) [from "fail_import_generic_decl.carbon"] { // CHECK:STDOUT: %T: type = symbolic_binding T, 0 [symbolic = %T (constants.%T)] // CHECK:STDOUT: %N.type: type = facet_type <@N, @N(%T)> [symbolic = %N.type (constants.%N.type.d682d6.1)] // CHECK:STDOUT: %Self: @N.WithSelf.Self.binding.as_type.impls.J.type.require.%N.type (%N.type.d682d6.1) = symbolic_binding Self, 1 [symbolic = %Self (constants.%Self.e1f642.1)] // CHECK:STDOUT: %Self.binding.as_type: type = symbolic_binding_type Self, 1, %Self [symbolic = %Self.binding.as_type (constants.%Self.binding.as_type)] // CHECK:STDOUT: %J.type: type = facet_type <@J, @J(%T)> [symbolic = %J.type (constants.%J.type.04e)] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic impl @D.as.J.impl.b25a4e.1(imports.%Main.import_ref.b3bc94.3: type) [from "fail_import_generic_decl.carbon"] { // CHECK:STDOUT: %T: type = symbolic_binding T, 0 [symbolic = %T (constants.%T)] // CHECK:STDOUT: %J.type: type = facet_type <@J, @J(%T)> [symbolic = %J.type (constants.%J.type.04e)] // CHECK:STDOUT: %J.impl_witness: = impl_witness imports.%J.impl_witness_table.50b, @D.as.J.impl.b25a4e.1(%T) [symbolic = %J.impl_witness (constants.%J.impl_witness.861)] // CHECK:STDOUT: // CHECK:STDOUT: impl: imports.%Main.import_ref.4ae308.1 as imports.%Main.import_ref.f2a55c.1; // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic impl @D.as.J.impl.f050b9.1(imports.%Main.import_ref.b3bc94.4: type) [from "fail_import_generic_decl.carbon"] { // CHECK:STDOUT: %T: type = symbolic_binding T, 0 [symbolic = %T (constants.%T)] // CHECK:STDOUT: %ptr: type = ptr_type %T [symbolic = %ptr (constants.%ptr)] // CHECK:STDOUT: %J.type: type = facet_type <@J, @J(%ptr)> [symbolic = %J.type (constants.%J.type.80f)] // CHECK:STDOUT: %J.impl_witness: = impl_witness imports.%J.impl_witness_table.a37, @D.as.J.impl.f050b9.1(%T) [symbolic = %J.impl_witness (constants.%J.impl_witness.f4f)] // CHECK:STDOUT: // CHECK:STDOUT: impl: imports.%Main.import_ref.4ae308.2 as imports.%Main.import_ref.739; // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic impl @D.as.J.impl.b25a4e.2(%T.loc8_14.1: type) { // CHECK:STDOUT: %T.loc8_14.2: type = symbolic_binding T, 0 [symbolic = %T.loc8_14.2 (constants.%T)] // CHECK:STDOUT: %J.type.loc8_32.2: type = facet_type <@J, @J(%T.loc8_14.2)> [symbolic = %J.type.loc8_32.2 (constants.%J.type.04e)] // CHECK:STDOUT: // CHECK:STDOUT: impl: %D.ref as %J.type.loc8_32.1; // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic impl @D.as.J.impl.b25a4e.3(%T.loc14_14.1: type) { // CHECK:STDOUT: %T.loc14_14.2: type = symbolic_binding T, 0 [symbolic = %T.loc14_14.2 (constants.%T)] // CHECK:STDOUT: %J.type.loc14_32.2: type = facet_type <@J, @J(%T.loc14_14.2)> [symbolic = %J.type.loc14_32.2 (constants.%J.type.04e)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: // CHECK:STDOUT: impl: %D.ref as %J.type.loc14_32.1 { // CHECK:STDOUT: !members: // CHECK:STDOUT: witness = // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic impl @D.as.J.impl.f050b9.2(%T.loc20_14.1: type) { // CHECK:STDOUT: %T.loc20_14.2: type = symbolic_binding T, 0 [symbolic = %T.loc20_14.2 (constants.%T)] // CHECK:STDOUT: %ptr.loc20_32.2: type = ptr_type %T.loc20_14.2 [symbolic = %ptr.loc20_32.2 (constants.%ptr)] // CHECK:STDOUT: %J.type.loc20_33.2: type = facet_type <@J, @J(%ptr.loc20_32.2)> [symbolic = %J.type.loc20_33.2 (constants.%J.type.80f)] // CHECK:STDOUT: // CHECK:STDOUT: impl: %D.ref as %J.type.loc20_33.1; // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic impl @D.as.J.impl.f050b9.3(%T.loc26_14.1: type) { // CHECK:STDOUT: %T.loc26_14.2: type = symbolic_binding T, 0 [symbolic = %T.loc26_14.2 (constants.%T)] // CHECK:STDOUT: %ptr.loc26_32.2: type = ptr_type %T.loc26_14.2 [symbolic = %ptr.loc26_32.2 (constants.%ptr)] // CHECK:STDOUT: %J.type.loc26_33.2: type = facet_type <@J, @J(%ptr.loc26_32.2)> [symbolic = %J.type.loc26_33.2 (constants.%J.type.80f)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: // CHECK:STDOUT: impl: %D.ref as %J.type.loc26_33.1 { // CHECK:STDOUT: !members: // CHECK:STDOUT: witness = // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic impl @D.as.J.impl.e28c26.1(%T.loc32_14.1: type) { // CHECK:STDOUT: %T.loc32_14.2: type = symbolic_binding T, 0 [symbolic = %T.loc32_14.2 (constants.%T)] // CHECK:STDOUT: %N.type.loc32_32.2: type = facet_type <@N, @N(%T.loc32_14.2)> [symbolic = %N.type.loc32_32.2 (constants.%N.type.d682d6.1)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: // CHECK:STDOUT: impl: %D.ref as %N.type.loc32_32.1 { // CHECK:STDOUT: !members: // CHECK:STDOUT: witness = // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic impl @D.as.J.impl.e28c26.2(%T.loc38_14.1: type) { // CHECK:STDOUT: %T.loc38_14.2: type = symbolic_binding T, 0 [symbolic = %T.loc38_14.2 (constants.%T)] // CHECK:STDOUT: %ptr.loc38_32.2: type = ptr_type %T.loc38_14.2 [symbolic = %ptr.loc38_32.2 (constants.%ptr)] // CHECK:STDOUT: %N.type.loc38_33.2: type = facet_type <@N, @N(%ptr.loc38_32.2)> [symbolic = %N.type.loc38_33.2 (constants.%N.type.d682d6.2)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: // CHECK:STDOUT: impl: %D.ref as %N.type.loc38_33.1 { // CHECK:STDOUT: !members: // CHECK:STDOUT: witness = // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @D [from "fail_import_generic_decl.carbon"] { // CHECK:STDOUT: complete_type_witness = imports.%Main.import_ref.8f2 // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = imports.%Main.import_ref.1d5 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @J(constants.%T) { // CHECK:STDOUT: %T => constants.%T // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %J.type => constants.%J.type.04e // CHECK:STDOUT: %Self => constants.%Self.c25 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @J.WithSelf(constants.%T, constants.%Self.c25) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @D.as.J.impl.b25a4e.1(constants.%T) { // CHECK:STDOUT: %T => constants.%T // CHECK:STDOUT: %J.type => constants.%J.type.04e // CHECK:STDOUT: %J.impl_witness => constants.%J.impl_witness.861 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @J(constants.%ptr) { // CHECK:STDOUT: %T => constants.%ptr // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @D.as.J.impl.f050b9.1(constants.%T) { // CHECK:STDOUT: %T => constants.%T // CHECK:STDOUT: %ptr => constants.%ptr // CHECK:STDOUT: %J.type => constants.%J.type.80f // CHECK:STDOUT: %J.impl_witness => constants.%J.impl_witness.f4f // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @D.as.J.impl.b25a4e.2(constants.%T) { // CHECK:STDOUT: %T.loc8_14.2 => constants.%T // CHECK:STDOUT: %J.type.loc8_32.2 => constants.%J.type.04e // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @D.as.J.impl.b25a4e.3(constants.%T) { // CHECK:STDOUT: %T.loc14_14.2 => constants.%T // CHECK:STDOUT: %J.type.loc14_32.2 => constants.%J.type.04e // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @D.as.J.impl.f050b9.2(constants.%T) { // CHECK:STDOUT: %T.loc20_14.2 => constants.%T // CHECK:STDOUT: %ptr.loc20_32.2 => constants.%ptr // CHECK:STDOUT: %J.type.loc20_33.2 => constants.%J.type.80f // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @D.as.J.impl.f050b9.3(constants.%T) { // CHECK:STDOUT: %T.loc26_14.2 => constants.%T // CHECK:STDOUT: %ptr.loc26_32.2 => constants.%ptr // CHECK:STDOUT: %J.type.loc26_33.2 => constants.%J.type.80f // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @N(constants.%T) { // CHECK:STDOUT: %T => constants.%T // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %N.type => constants.%N.type.d682d6.1 // CHECK:STDOUT: %Self => constants.%Self.e1f642.1 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @N.WithSelf(constants.%T, constants.%Self.e1f642.1) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: %T => constants.%T // CHECK:STDOUT: %J.type => constants.%J.type.04e // CHECK:STDOUT: %require_complete => constants.%require_complete.cc6 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @N.WithSelf.Self.binding.as_type.impls.J.type.require(constants.%T, constants.%Self.e1f642.1) { // CHECK:STDOUT: %T => constants.%T // CHECK:STDOUT: %N.type => constants.%N.type.d682d6.1 // CHECK:STDOUT: %Self => constants.%Self.e1f642.1 // CHECK:STDOUT: %Self.binding.as_type => constants.%Self.binding.as_type // CHECK:STDOUT: %J.type => constants.%J.type.04e // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @N.WithSelf(constants.%T, constants.%D.type.facet) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: %T => constants.%T // CHECK:STDOUT: %J.type => constants.%J.type.04e // CHECK:STDOUT: %require_complete => constants.%require_complete.cc6 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @N.WithSelf.Self.binding.as_type.impls.J.type.require(constants.%T, constants.%D.type.facet) { // CHECK:STDOUT: %T => constants.%T // CHECK:STDOUT: %N.type => constants.%N.type.d682d6.1 // CHECK:STDOUT: %Self => constants.%D.type.facet // CHECK:STDOUT: %Self.binding.as_type => constants.%D // CHECK:STDOUT: %J.type => constants.%J.type.04e // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @D.as.J.impl.e28c26.1(constants.%T) { // CHECK:STDOUT: %T.loc32_14.2 => constants.%T // CHECK:STDOUT: %N.type.loc32_32.2 => constants.%N.type.d682d6.1 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @N(constants.%ptr) { // CHECK:STDOUT: %T => constants.%ptr // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %N.type => constants.%N.type.d682d6.2 // CHECK:STDOUT: %Self => constants.%Self.e1f642.2 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @N.WithSelf(constants.%ptr, constants.%Self.e1f642.1) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: %T => constants.%ptr // CHECK:STDOUT: %J.type => constants.%J.type.80f // CHECK:STDOUT: %require_complete => constants.%require_complete.19b // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @N.WithSelf(constants.%ptr, constants.%D.type.facet) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: %T => constants.%ptr // CHECK:STDOUT: %J.type => constants.%J.type.80f // CHECK:STDOUT: %require_complete => constants.%require_complete.19b // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @N.WithSelf.Self.binding.as_type.impls.J.type.require(constants.%ptr, constants.%D.type.facet) { // CHECK:STDOUT: %T => constants.%ptr // CHECK:STDOUT: %N.type => constants.%N.type.d682d6.2 // CHECK:STDOUT: %Self => constants.%D.type.facet // CHECK:STDOUT: %Self.binding.as_type => constants.%D // CHECK:STDOUT: %J.type => constants.%J.type.80f // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @D.as.J.impl.e28c26.2(constants.%T) { // CHECK:STDOUT: %T.loc38_14.2 => constants.%T // CHECK:STDOUT: %ptr.loc38_32.2 => constants.%ptr // CHECK:STDOUT: %N.type.loc38_33.2 => constants.%N.type.d682d6.2 // CHECK:STDOUT: } // CHECK:STDOUT: ================================================ FILE: toolchain/check/testdata/impl/import_impl_with_no_interface.carbon ================================================ [File too large to display: 1.2 KB] ================================================ FILE: toolchain/check/testdata/impl/import_interface_assoc_const.carbon ================================================ [File too large to display: 102.0 KB] ================================================ FILE: toolchain/check/testdata/impl/import_self.carbon ================================================ [File too large to display: 20.0 KB] ================================================ FILE: toolchain/check/testdata/impl/import_self_specific.carbon ================================================ [File too large to display: 43.6 KB] ================================================ FILE: toolchain/check/testdata/impl/import_thunk.carbon ================================================ [File too large to display: 45.9 KB] ================================================ FILE: toolchain/check/testdata/impl/import_use_generic.carbon ================================================ [File too large to display: 23.9 KB] ================================================ FILE: toolchain/check/testdata/impl/incomplete.carbon ================================================ [File too large to display: 48.9 KB] ================================================ FILE: toolchain/check/testdata/impl/interface_args.carbon ================================================ [File too large to display: 109.7 KB] ================================================ FILE: toolchain/check/testdata/impl/lookup/alias.carbon ================================================ [File too large to display: 8.0 KB] ================================================ FILE: toolchain/check/testdata/impl/lookup/canonical_query_self.carbon ================================================ [File too large to display: 39.0 KB] ================================================ FILE: toolchain/check/testdata/impl/lookup/fail_alias_impl_not_found.carbon ================================================ [File too large to display: 5.7 KB] ================================================ FILE: toolchain/check/testdata/impl/lookup/fail_function_types.carbon ================================================ [File too large to display: 2.4 KB] ================================================ FILE: toolchain/check/testdata/impl/lookup/fail_todo_undefined_impl.carbon ================================================ [File too large to display: 9.7 KB] ================================================ FILE: toolchain/check/testdata/impl/lookup/find_in_final.carbon ================================================ [File too large to display: 3.4 KB] ================================================ FILE: toolchain/check/testdata/impl/lookup/generic.carbon ================================================ [File too large to display: 111.7 KB] ================================================ FILE: toolchain/check/testdata/impl/lookup/impl_cycle.carbon ================================================ [File too large to display: 15.0 KB] ================================================ FILE: toolchain/check/testdata/impl/lookup/impl_forall.carbon ================================================ [File too large to display: 28.2 KB] ================================================ FILE: toolchain/check/testdata/impl/lookup/impl_overlap.carbon ================================================ [File too large to display: 15.4 KB] ================================================ FILE: toolchain/check/testdata/impl/lookup/impl_overlap_wrapped_types.carbon ================================================ [File too large to display: 3.5 KB] ================================================ FILE: toolchain/check/testdata/impl/lookup/import.carbon ================================================ [File too large to display: 188.4 KB] ================================================ FILE: toolchain/check/testdata/impl/lookup/import_error.carbon ================================================ [File too large to display: 1.9 KB] ================================================ FILE: toolchain/check/testdata/impl/lookup/import_final.carbon ================================================ [File too large to display: 1011 B] ================================================ FILE: toolchain/check/testdata/impl/lookup/instance_method.carbon ================================================ [File too large to display: 12.8 KB] ================================================ FILE: toolchain/check/testdata/impl/lookup/lookup_interface_with_enclosing_generic_inside_rewrite_constraint.carbon ================================================ [File too large to display: 61.7 KB] ================================================ FILE: toolchain/check/testdata/impl/lookup/overlap_with_non_types.carbon ================================================ [File too large to display: 1.0 KB] ================================================ FILE: toolchain/check/testdata/impl/lookup/specialization.carbon ================================================ [File too large to display: 12.7 KB] ================================================ FILE: toolchain/check/testdata/impl/lookup/specialization_poison.carbon ================================================ [File too large to display: 6.7 KB] ================================================ FILE: toolchain/check/testdata/impl/lookup/specialization_with_symbolic_rewrite.carbon ================================================ [File too large to display: 112.2 KB] ================================================ FILE: toolchain/check/testdata/impl/lookup/specific_args.carbon ================================================ [File too large to display: 57.8 KB] ================================================ FILE: toolchain/check/testdata/impl/lookup/struct.carbon ================================================ [File too large to display: 1.5 KB] ================================================ FILE: toolchain/check/testdata/impl/lookup/symbolic_lookup.carbon ================================================ [File too large to display: 2.9 KB] ================================================ FILE: toolchain/check/testdata/impl/lookup/transitive.carbon ================================================ [File too large to display: 22.5 KB] ================================================ FILE: toolchain/check/testdata/impl/lookup/unused_generic_binding.carbon ================================================ [File too large to display: 4.6 KB] ================================================ FILE: toolchain/check/testdata/impl/multiple_extend.carbon ================================================ [File too large to display: 59.1 KB] ================================================ FILE: toolchain/check/testdata/impl/name_poisoning.carbon ================================================ [File too large to display: 1.9 KB] ================================================ FILE: toolchain/check/testdata/impl/no_definition_in_impl_file.carbon ================================================ [File too large to display: 17.5 KB] ================================================ FILE: toolchain/check/testdata/impl/orphan.carbon ================================================ [File too large to display: 6.3 KB] ================================================ FILE: toolchain/check/testdata/impl/redeclaration.carbon ================================================ [File too large to display: 4.8 KB] ================================================ FILE: toolchain/check/testdata/impl/self_in_class.carbon ================================================ [File too large to display: 11.7 KB] ================================================ FILE: toolchain/check/testdata/impl/self_in_signature.carbon ================================================ [File too large to display: 34.5 KB] ================================================ FILE: toolchain/check/testdata/impl/todo_impl_with_unrelated_fn.carbon ================================================ [File too large to display: 6.1 KB] ================================================ FILE: toolchain/check/testdata/impl/use_assoc_entity.carbon ================================================ [File too large to display: 355.0 KB] ================================================ FILE: toolchain/check/testdata/impl/using_invalid_interface.carbon ================================================ [File too large to display: 5.6 KB] ================================================ FILE: toolchain/check/testdata/index/array_element_access.carbon ================================================ [File too large to display: 18.7 KB] ================================================ FILE: toolchain/check/testdata/index/expr_category.carbon ================================================ [File too large to display: 35.9 KB] ================================================ FILE: toolchain/check/testdata/index/fail_array_large_index.carbon ================================================ [File too large to display: 17.0 KB] ================================================ FILE: toolchain/check/testdata/index/fail_array_non_int_indexing.carbon ================================================ [File too large to display: 12.2 KB] ================================================ FILE: toolchain/check/testdata/index/fail_array_out_of_bound_access.carbon ================================================ [File too large to display: 13.3 KB] ================================================ FILE: toolchain/check/testdata/index/fail_expr_category.carbon ================================================ [File too large to display: 24.4 KB] ================================================ FILE: toolchain/check/testdata/index/fail_invalid_base.carbon ================================================ [File too large to display: 7.9 KB] ================================================ FILE: toolchain/check/testdata/index/fail_name_not_found.carbon ================================================ [File too large to display: 3.9 KB] ================================================ FILE: toolchain/check/testdata/index/fail_negative_indexing.carbon ================================================ [File too large to display: 17.5 KB] ================================================ FILE: toolchain/check/testdata/index/fail_non_tuple_access.carbon ================================================ [File too large to display: 2.7 KB] ================================================ FILE: toolchain/check/testdata/interface/as_type.carbon ================================================ [File too large to display: 2.7 KB] ================================================ FILE: toolchain/check/testdata/interface/as_type_of_type.carbon ================================================ [File too large to display: 14.4 KB] ================================================ FILE: toolchain/check/testdata/interface/assoc_const.carbon ================================================ [File too large to display: 3.7 KB] ================================================ FILE: toolchain/check/testdata/interface/assoc_const_in_generic.carbon ================================================ [File too large to display: 13.2 KB] ================================================ FILE: toolchain/check/testdata/interface/basic.carbon ================================================ [File too large to display: 4.2 KB] ================================================ FILE: toolchain/check/testdata/interface/compound_member_access.carbon ================================================ [File too large to display: 115.3 KB] ================================================ FILE: toolchain/check/testdata/interface/default_fn.carbon ================================================ [File too large to display: 11.6 KB] ================================================ FILE: toolchain/check/testdata/interface/export_name.carbon ================================================ [File too large to display: 5.4 KB] ================================================ FILE: toolchain/check/testdata/interface/fail_add_member_outside_definition.carbon ================================================ [File too large to display: 7.8 KB] ================================================ FILE: toolchain/check/testdata/interface/fail_assoc_const_alias.carbon ================================================ [File too large to display: 14.2 KB] ================================================ FILE: toolchain/check/testdata/interface/fail_assoc_const_bad_default.carbon ================================================ [File too large to display: 3.7 KB] ================================================ FILE: toolchain/check/testdata/interface/fail_assoc_fn_invalid_use.carbon ================================================ [File too large to display: 9.2 KB] ================================================ FILE: toolchain/check/testdata/interface/fail_definition_imported.carbon ================================================ [File too large to display: 3.1 KB] ================================================ FILE: toolchain/check/testdata/interface/fail_duplicate.carbon ================================================ [File too large to display: 13.6 KB] ================================================ FILE: toolchain/check/testdata/interface/fail_generic_redeclaration.carbon ================================================ [File too large to display: 12.3 KB] ================================================ FILE: toolchain/check/testdata/interface/fail_lookup_in_type_type.carbon ================================================ [File too large to display: 4.1 KB] ================================================ FILE: toolchain/check/testdata/interface/fail_lookup_undefined.carbon ================================================ [File too large to display: 7.0 KB] ================================================ FILE: toolchain/check/testdata/interface/fail_member_lookup.carbon ================================================ [File too large to display: 10.5 KB] ================================================ FILE: toolchain/check/testdata/interface/fail_modifiers.carbon ================================================ [File too large to display: 13.5 KB] ================================================ FILE: toolchain/check/testdata/interface/fail_redeclare_member.carbon ================================================ [File too large to display: 3.7 KB] ================================================ FILE: toolchain/check/testdata/interface/fail_todo_assoc_const_default.carbon ================================================ [File too large to display: 8.7 KB] ================================================ FILE: toolchain/check/testdata/interface/fail_todo_define_default_fn_inline.carbon ================================================ [File too large to display: 7.5 KB] ================================================ FILE: toolchain/check/testdata/interface/fail_todo_define_default_fn_out_of_line.carbon ================================================ [File too large to display: 24.6 KB] ================================================ FILE: toolchain/check/testdata/interface/fail_todo_modifiers.carbon ================================================ [File too large to display: 5.3 KB] ================================================ FILE: toolchain/check/testdata/interface/fail_type_as_facet.carbon ================================================ [File too large to display: 1.2 KB] ================================================ FILE: toolchain/check/testdata/interface/final.carbon ================================================ [File too large to display: 24.6 KB] ================================================ FILE: toolchain/check/testdata/interface/generic.carbon ================================================ [File too large to display: 38.8 KB] ================================================ FILE: toolchain/check/testdata/interface/generic_binding_after_assoc_const.carbon ================================================ [File too large to display: 5.8 KB] ================================================ FILE: toolchain/check/testdata/interface/generic_default_fn.carbon ================================================ [File too large to display: 785 B] ================================================ FILE: toolchain/check/testdata/interface/generic_import.carbon ================================================ [File too large to display: 13.2 KB] ================================================ FILE: toolchain/check/testdata/interface/generic_method.carbon ================================================ [File too large to display: 112.4 KB] ================================================ FILE: toolchain/check/testdata/interface/generic_vs_params.carbon ================================================ [File too large to display: 32.3 KB] ================================================ FILE: toolchain/check/testdata/interface/import.carbon ================================================ [File too large to display: 28.5 KB] ================================================ FILE: toolchain/check/testdata/interface/import_access.carbon ================================================ [File too large to display: 22.9 KB] ================================================ FILE: toolchain/check/testdata/interface/import_interface_decl.carbon ================================================ [File too large to display: 3.1 KB] ================================================ FILE: toolchain/check/testdata/interface/incomplete.carbon ================================================ [File too large to display: 24.1 KB] ================================================ FILE: toolchain/check/testdata/interface/local.carbon ================================================ [File too large to display: 9.6 KB] ================================================ FILE: toolchain/check/testdata/interface/member_lookup.carbon ================================================ [File too large to display: 43.5 KB] ================================================ FILE: toolchain/check/testdata/interface/name_poisoning.carbon ================================================ [File too large to display: 6.8 KB] ================================================ FILE: toolchain/check/testdata/interface/require.carbon ================================================ [File too large to display: 47.2 KB] ================================================ FILE: toolchain/check/testdata/interface/require_invalid_modifiers.carbon ================================================ [File too large to display: 4.7 KB] ================================================ FILE: toolchain/check/testdata/interface/self.carbon ================================================ [File too large to display: 6.3 KB] ================================================ FILE: toolchain/check/testdata/interface/syntactic_merge.carbon ================================================ [File too large to display: 64.0 KB] ================================================ FILE: toolchain/check/testdata/interface/todo_define_not_default.carbon ================================================ [File too large to display: 12.2 KB] ================================================ FILE: toolchain/check/testdata/interop/cpp/bad_flags.carbon ================================================ [File too large to display: 1.0 KB] ================================================ FILE: toolchain/check/testdata/interop/cpp/bad_import.carbon ================================================ [File too large to display: 4.3 KB] ================================================ FILE: toolchain/check/testdata/interop/cpp/builtins.carbon ================================================ [File too large to display: 50.2 KB] ================================================ FILE: toolchain/check/testdata/interop/cpp/builtins.llp64.carbon ================================================ [File too large to display: 483.8 KB] ================================================ FILE: toolchain/check/testdata/interop/cpp/builtins.lp64.carbon ================================================ [File too large to display: 473.3 KB] ================================================ FILE: toolchain/check/testdata/interop/cpp/class/abstract.carbon ================================================ [File too large to display: 4.7 KB] ================================================ FILE: toolchain/check/testdata/interop/cpp/class/access.carbon ================================================ [File too large to display: 178.0 KB] ================================================ FILE: toolchain/check/testdata/interop/cpp/class/base.carbon ================================================ [File too large to display: 38.9 KB] ================================================ FILE: toolchain/check/testdata/interop/cpp/class/class.carbon ================================================ [File too large to display: 25.6 KB] ================================================ FILE: toolchain/check/testdata/interop/cpp/class/constructor.carbon ================================================ [File too large to display: 85.8 KB] ================================================ FILE: toolchain/check/testdata/interop/cpp/class/field.carbon ================================================ [File too large to display: 29.1 KB] ================================================ FILE: toolchain/check/testdata/interop/cpp/class/invalid.carbon ================================================ [File too large to display: 1.1 KB] ================================================ FILE: toolchain/check/testdata/interop/cpp/class/method.carbon ================================================ [File too large to display: 42.8 KB] ================================================ FILE: toolchain/check/testdata/interop/cpp/class/nested.carbon ================================================ [File too large to display: 838 B] ================================================ FILE: toolchain/check/testdata/interop/cpp/class/non_aggregate_init.carbon ================================================ [File too large to display: 2.1 KB] ================================================ FILE: toolchain/check/testdata/interop/cpp/class/struct.carbon ================================================ [File too large to display: 2.6 KB] ================================================ FILE: toolchain/check/testdata/interop/cpp/class/template.carbon ================================================ [File too large to display: 17.5 KB] ================================================ FILE: toolchain/check/testdata/interop/cpp/class/union.carbon ================================================ [File too large to display: 18.2 KB] ================================================ FILE: toolchain/check/testdata/interop/cpp/constexpr.carbon ================================================ [File too large to display: 1.9 KB] ================================================ FILE: toolchain/check/testdata/interop/cpp/cpp_diagnostics.carbon ================================================ [File too large to display: 19.1 KB] ================================================ FILE: toolchain/check/testdata/interop/cpp/cpp_namespace.carbon ================================================ [File too large to display: 5.8 KB] ================================================ FILE: toolchain/check/testdata/interop/cpp/deref.carbon ================================================ [File too large to display: 3.7 KB] ================================================ FILE: toolchain/check/testdata/interop/cpp/enum/anonymous.carbon ================================================ [File too large to display: 7.3 KB] ================================================ FILE: toolchain/check/testdata/interop/cpp/enum/convert.carbon ================================================ [File too large to display: 11.9 KB] ================================================ FILE: toolchain/check/testdata/interop/cpp/enum/copy.carbon ================================================ [File too large to display: 3.8 KB] ================================================ FILE: toolchain/check/testdata/interop/cpp/enum/fixed.carbon ================================================ [File too large to display: 3.6 KB] ================================================ FILE: toolchain/check/testdata/interop/cpp/enum/incomplete.carbon ================================================ [File too large to display: 4.2 KB] ================================================ FILE: toolchain/check/testdata/interop/cpp/enum/invalid.carbon ================================================ [File too large to display: 1.1 KB] ================================================ FILE: toolchain/check/testdata/interop/cpp/enum/member.carbon ================================================ [File too large to display: 4.0 KB] ================================================ FILE: toolchain/check/testdata/interop/cpp/enum/scoped.carbon ================================================ [File too large to display: 3.1 KB] ================================================ FILE: toolchain/check/testdata/interop/cpp/enum/unscoped.carbon ================================================ [File too large to display: 5.3 KB] ================================================ FILE: toolchain/check/testdata/interop/cpp/enum/using.carbon ================================================ [File too large to display: 7.2 KB] ================================================ FILE: toolchain/check/testdata/interop/cpp/fail_fuzzing.carbon ================================================ [File too large to display: 984 B] ================================================ FILE: toolchain/check/testdata/interop/cpp/fail_todo_arithmetic_types_unmapped.carbon ================================================ [File too large to display: 1.2 KB] ================================================ FILE: toolchain/check/testdata/interop/cpp/file_not_found.carbon ================================================ [File too large to display: 957 B] ================================================ FILE: toolchain/check/testdata/interop/cpp/function/arithmetic_types_bridged.carbon ================================================ [File too large to display: 168.8 KB] ================================================ FILE: toolchain/check/testdata/interop/cpp/function/arithmetic_types_direct.carbon ================================================ [File too large to display: 81.9 KB] ================================================ FILE: toolchain/check/testdata/interop/cpp/function/class.carbon ================================================ [File too large to display: 53.4 KB] ================================================ FILE: toolchain/check/testdata/interop/cpp/function/decayed_param.carbon ================================================ [File too large to display: 40.0 KB] ================================================ FILE: toolchain/check/testdata/interop/cpp/function/default_arg.carbon ================================================ [File too large to display: 89.3 KB] ================================================ FILE: toolchain/check/testdata/interop/cpp/function/extern_c.carbon ================================================ [File too large to display: 14.4 KB] ================================================ FILE: toolchain/check/testdata/interop/cpp/function/full_semir.carbon ================================================ [File too large to display: 26.4 KB] ================================================ FILE: toolchain/check/testdata/interop/cpp/function/function.carbon ================================================ [File too large to display: 19.9 KB] ================================================ FILE: toolchain/check/testdata/interop/cpp/function/in_template.carbon ================================================ [File too large to display: 6.7 KB] ================================================ FILE: toolchain/check/testdata/interop/cpp/function/inline.carbon ================================================ [File too large to display: 29.5 KB] ================================================ FILE: toolchain/check/testdata/interop/cpp/function/multiple_too_few_args_calls.carbon ================================================ [File too large to display: 3.7 KB] ================================================ FILE: toolchain/check/testdata/interop/cpp/function/operators.carbon ================================================ [File too large to display: 206.0 KB] ================================================ FILE: toolchain/check/testdata/interop/cpp/function/overloads.carbon ================================================ [File too large to display: 298.3 KB] ================================================ FILE: toolchain/check/testdata/interop/cpp/function/param_unsupported.carbon ================================================ [File too large to display: 6.0 KB] ================================================ FILE: toolchain/check/testdata/interop/cpp/function/pointer.carbon ================================================ [File too large to display: 134.8 KB] ================================================ FILE: toolchain/check/testdata/interop/cpp/function/qualified_param.carbon ================================================ [File too large to display: 10.5 KB] ================================================ FILE: toolchain/check/testdata/interop/cpp/function/reference.carbon ================================================ [File too large to display: 58.6 KB] ================================================ FILE: toolchain/check/testdata/interop/cpp/function/return.carbon ================================================ [File too large to display: 21.5 KB] ================================================ FILE: toolchain/check/testdata/interop/cpp/function/struct.carbon ================================================ [File too large to display: 53.4 KB] ================================================ FILE: toolchain/check/testdata/interop/cpp/function/template.carbon ================================================ [File too large to display: 4.0 KB] ================================================ FILE: toolchain/check/testdata/interop/cpp/function/thunk_ast.carbon ================================================ [File too large to display: 7.1 KB] ================================================ FILE: toolchain/check/testdata/interop/cpp/function/union.carbon ================================================ [File too large to display: 49.9 KB] ================================================ FILE: toolchain/check/testdata/interop/cpp/function/variadic.carbon ================================================ [File too large to display: 15.4 KB] ================================================ FILE: toolchain/check/testdata/interop/cpp/function/void_pointer.carbon ================================================ [File too large to display: 43.6 KB] ================================================ FILE: toolchain/check/testdata/interop/cpp/impls/as.carbon ================================================ [File too large to display: 44.5 KB] ================================================ FILE: toolchain/check/testdata/interop/cpp/impls/copy.carbon ================================================ [File too large to display: 17.3 KB] ================================================ FILE: toolchain/check/testdata/interop/cpp/impls/destroy.carbon ================================================ [File too large to display: 22.3 KB] ================================================ FILE: toolchain/check/testdata/interop/cpp/impls/implicit_as.carbon ================================================ [File too large to display: 112.0 KB] ================================================ FILE: toolchain/check/testdata/interop/cpp/import.carbon ================================================ [File too large to display: 9.9 KB] ================================================ FILE: toolchain/check/testdata/interop/cpp/include.carbon ================================================ [File too large to display: 1.1 KB] ================================================ FILE: toolchain/check/testdata/interop/cpp/include_paths.carbon ================================================ [File too large to display: 1.1 KB] ================================================ FILE: toolchain/check/testdata/interop/cpp/inline.carbon ================================================ [File too large to display: 4.7 KB] ================================================ FILE: toolchain/check/testdata/interop/cpp/macros.carbon ================================================ [File too large to display: 104.7 KB] ================================================ FILE: toolchain/check/testdata/interop/cpp/modules.carbon ================================================ [File too large to display: 3.7 KB] ================================================ FILE: toolchain/check/testdata/interop/cpp/multiple_imports.carbon ================================================ [File too large to display: 1.1 KB] ================================================ FILE: toolchain/check/testdata/interop/cpp/namespace.carbon ================================================ [File too large to display: 19.2 KB] ================================================ FILE: toolchain/check/testdata/interop/cpp/reverse/simple.carbon ================================================ [File too large to display: 1.2 KB] ================================================ FILE: toolchain/check/testdata/interop/cpp/stdlib/initializer_list.carbon ================================================ [File too large to display: 36.1 KB] ================================================ FILE: toolchain/check/testdata/interop/cpp/stdlib/string_view.carbon ================================================ [File too large to display: 12.8 KB] ================================================ FILE: toolchain/check/testdata/interop/cpp/template/alias_template.carbon ================================================ [File too large to display: 9.0 KB] ================================================ FILE: toolchain/check/testdata/interop/cpp/template/argument_count.carbon ================================================ [File too large to display: 16.7 KB] ================================================ FILE: toolchain/check/testdata/interop/cpp/template/class_template.carbon ================================================ [File too large to display: 5.9 KB] ================================================ FILE: toolchain/check/testdata/interop/cpp/template/concept.carbon ================================================ [File too large to display: 7.7 KB] ================================================ FILE: toolchain/check/testdata/interop/cpp/template/generic_call.carbon ================================================ [File too large to display: 1.3 KB] ================================================ FILE: toolchain/check/testdata/interop/cpp/template/non_type_param.carbon ================================================ [File too large to display: 12.0 KB] ================================================ FILE: toolchain/check/testdata/interop/cpp/template/template_template_param.carbon ================================================ [File too large to display: 7.1 KB] ================================================ FILE: toolchain/check/testdata/interop/cpp/template/type_param.carbon ================================================ [File too large to display: 8.0 KB] ================================================ FILE: toolchain/check/testdata/interop/cpp/template/var_template.carbon ================================================ [File too large to display: 9.2 KB] ================================================ FILE: toolchain/check/testdata/interop/cpp/typedef.carbon ================================================ [File too large to display: 16.8 KB] ================================================ FILE: toolchain/check/testdata/interop/cpp/unsupported_decl_type.carbon ================================================ [File too large to display: 2.2 KB] ================================================ FILE: toolchain/check/testdata/interop/cpp/unused_internal.carbon ================================================ [File too large to display: 1.3 KB] ================================================ FILE: toolchain/check/testdata/interop/cpp/var/global.carbon ================================================ [File too large to display: 11.4 KB] ================================================ FILE: toolchain/check/testdata/interop/cpp/var/namespace.carbon ================================================ [File too large to display: 2.3 KB] ================================================ FILE: toolchain/check/testdata/interop/cpp/void.carbon ================================================ [File too large to display: 2.6 KB] ================================================ FILE: toolchain/check/testdata/interop/cpp/void_pointer.carbon ================================================ [File too large to display: 37.4 KB] ================================================ FILE: toolchain/check/testdata/let/compile_time_bindings.carbon ================================================ [File too large to display: 43.3 KB] ================================================ FILE: toolchain/check/testdata/let/convert.carbon ================================================ [File too large to display: 3.9 KB] ================================================ FILE: toolchain/check/testdata/let/fail_duplicate_decl.carbon ================================================ [File too large to display: 9.1 KB] ================================================ FILE: toolchain/check/testdata/let/fail_generic.carbon ================================================ [File too large to display: 1.4 KB] ================================================ FILE: toolchain/check/testdata/let/fail_generic_import.carbon ================================================ [File too large to display: 4.7 KB] ================================================ FILE: toolchain/check/testdata/let/fail_missing_value.carbon ================================================ [File too large to display: 3.0 KB] ================================================ FILE: toolchain/check/testdata/let/fail_modifiers.carbon ================================================ [File too large to display: 21.6 KB] ================================================ FILE: toolchain/check/testdata/let/fail_use_in_init.carbon ================================================ [File too large to display: 3.2 KB] ================================================ FILE: toolchain/check/testdata/let/generic.carbon ================================================ [File too large to display: 1.9 KB] ================================================ FILE: toolchain/check/testdata/let/generic_import.carbon ================================================ [File too large to display: 5.6 KB] ================================================ FILE: toolchain/check/testdata/let/global.carbon ================================================ [File too large to display: 9.9 KB] ================================================ FILE: toolchain/check/testdata/let/import.carbon ================================================ [File too large to display: 7.4 KB] ================================================ FILE: toolchain/check/testdata/let/import_access.carbon ================================================ [File too large to display: 8.1 KB] ================================================ FILE: toolchain/check/testdata/let/local.carbon ================================================ [File too large to display: 4.3 KB] ================================================ FILE: toolchain/check/testdata/let/ref.carbon ================================================ [File too large to display: 3.3 KB] ================================================ FILE: toolchain/check/testdata/let/shadowed_decl.carbon ================================================ [File too large to display: 10.2 KB] ================================================ FILE: toolchain/check/testdata/main_run/README.md ================================================ [File too large to display: 358 B] ================================================ FILE: toolchain/check/testdata/main_run/fail_mismatch_params.carbon ================================================ [File too large to display: 1.9 KB] ================================================ FILE: toolchain/check/testdata/main_run/fail_mismatch_return.carbon ================================================ [File too large to display: 934 B] ================================================ FILE: toolchain/check/testdata/main_run/no_return.carbon ================================================ [File too large to display: 4.5 KB] ================================================ FILE: toolchain/check/testdata/main_run/return_i32.carbon ================================================ [File too large to display: 14.4 KB] ================================================ FILE: toolchain/check/testdata/named_constraint/basic.carbon ================================================ [File too large to display: 4.2 KB] ================================================ FILE: toolchain/check/testdata/named_constraint/convert.carbon ================================================ [File too large to display: 9.4 KB] ================================================ FILE: toolchain/check/testdata/named_constraint/empty.carbon ================================================ [File too large to display: 12.0 KB] ================================================ FILE: toolchain/check/testdata/named_constraint/empty_generic.carbon ================================================ [File too large to display: 24.0 KB] ================================================ FILE: toolchain/check/testdata/named_constraint/generic.carbon ================================================ [File too large to display: 13.0 KB] ================================================ FILE: toolchain/check/testdata/named_constraint/import_constraint_decl.carbon ================================================ [File too large to display: 7.0 KB] ================================================ FILE: toolchain/check/testdata/named_constraint/incomplete.carbon ================================================ [File too large to display: 3.0 KB] ================================================ FILE: toolchain/check/testdata/named_constraint/invalid_members.carbon ================================================ [File too large to display: 2.5 KB] ================================================ FILE: toolchain/check/testdata/named_constraint/require.carbon ================================================ [File too large to display: 60.4 KB] ================================================ FILE: toolchain/check/testdata/named_constraint/require_invalid_modifiers.carbon ================================================ [File too large to display: 4.8 KB] ================================================ FILE: toolchain/check/testdata/namespace/add_to_import.carbon ================================================ [File too large to display: 8.8 KB] ================================================ FILE: toolchain/check/testdata/namespace/alias.carbon ================================================ [File too large to display: 9.7 KB] ================================================ FILE: toolchain/check/testdata/namespace/fail_conflict_after_merge.carbon ================================================ [File too large to display: 4.2 KB] ================================================ FILE: toolchain/check/testdata/namespace/fail_conflict_imported_namespace_first.carbon ================================================ [File too large to display: 3.4 KB] ================================================ FILE: toolchain/check/testdata/namespace/fail_conflict_imported_namespace_nested.carbon ================================================ [File too large to display: 3.9 KB] ================================================ FILE: toolchain/check/testdata/namespace/fail_conflict_imported_namespace_second.carbon ================================================ [File too large to display: 4.0 KB] ================================================ FILE: toolchain/check/testdata/namespace/fail_conflict_in_imports_namespace_first.carbon ================================================ [File too large to display: 4.9 KB] ================================================ FILE: toolchain/check/testdata/namespace/fail_conflict_in_imports_namespace_second.carbon ================================================ [File too large to display: 4.9 KB] ================================================ FILE: toolchain/check/testdata/namespace/fail_decl_in_alias.carbon ================================================ [File too large to display: 7.6 KB] ================================================ FILE: toolchain/check/testdata/namespace/fail_duplicate.carbon ================================================ [File too large to display: 2.6 KB] ================================================ FILE: toolchain/check/testdata/namespace/fail_modifiers.carbon ================================================ [File too large to display: 4.3 KB] ================================================ FILE: toolchain/check/testdata/namespace/fail_not_top_level.carbon ================================================ [File too large to display: 5.8 KB] ================================================ FILE: toolchain/check/testdata/namespace/fail_params.carbon ================================================ [File too large to display: 5.9 KB] ================================================ FILE: toolchain/check/testdata/namespace/fail_unresolved_scope.carbon ================================================ [File too large to display: 1.8 KB] ================================================ FILE: toolchain/check/testdata/namespace/function.carbon ================================================ [File too large to display: 3.1 KB] ================================================ FILE: toolchain/check/testdata/namespace/imported.carbon ================================================ [File too large to display: 9.5 KB] ================================================ FILE: toolchain/check/testdata/namespace/imported_indirect.carbon ================================================ [File too large to display: 15.5 KB] ================================================ FILE: toolchain/check/testdata/namespace/merging.carbon ================================================ [File too large to display: 7.5 KB] ================================================ FILE: toolchain/check/testdata/namespace/merging_with_indirections.carbon ================================================ [File too large to display: 14.7 KB] ================================================ FILE: toolchain/check/testdata/namespace/name_poisoning.carbon ================================================ [File too large to display: 6.3 KB] ================================================ FILE: toolchain/check/testdata/namespace/nested.carbon ================================================ [File too large to display: 2.8 KB] ================================================ FILE: toolchain/check/testdata/namespace/shadow.carbon ================================================ [File too large to display: 13.6 KB] ================================================ FILE: toolchain/check/testdata/namespace/unqualified_lookup.carbon ================================================ [File too large to display: 4.8 KB] ================================================ FILE: toolchain/check/testdata/operators/builtin/and.carbon ================================================ [File too large to display: 23.1 KB] ================================================ FILE: toolchain/check/testdata/operators/builtin/assignment.carbon ================================================ [File too large to display: 35.1 KB] ================================================ FILE: toolchain/check/testdata/operators/builtin/bit_and.carbon ================================================ [File too large to display: 2.7 KB] ================================================ FILE: toolchain/check/testdata/operators/builtin/fail_and_or_not_in_function.carbon ================================================ [File too large to display: 10.0 KB] ================================================ FILE: toolchain/check/testdata/operators/builtin/fail_and_or_partial_constant.carbon ================================================ [File too large to display: 14.6 KB] ================================================ FILE: toolchain/check/testdata/operators/builtin/fail_assignment_to_error.carbon ================================================ [File too large to display: 2.9 KB] ================================================ FILE: toolchain/check/testdata/operators/builtin/fail_assignment_to_non_assignable.carbon ================================================ [File too large to display: 37.8 KB] ================================================ FILE: toolchain/check/testdata/operators/builtin/fail_redundant_compound_access.carbon ================================================ [File too large to display: 13.7 KB] ================================================ FILE: toolchain/check/testdata/operators/builtin/fail_type_mismatch.carbon ================================================ [File too large to display: 4.5 KB] ================================================ FILE: toolchain/check/testdata/operators/builtin/fail_type_mismatch_assignment.carbon ================================================ [File too large to display: 8.4 KB] ================================================ FILE: toolchain/check/testdata/operators/builtin/fail_type_mismatch_once.carbon ================================================ [File too large to display: 4.3 KB] ================================================ FILE: toolchain/check/testdata/operators/builtin/fail_unimplemented_op.carbon ================================================ [File too large to display: 4.0 KB] ================================================ FILE: toolchain/check/testdata/operators/builtin/or.carbon ================================================ [File too large to display: 24.0 KB] ================================================ FILE: toolchain/check/testdata/operators/builtin/ref.carbon ================================================ [File too large to display: 1.6 KB] ================================================ FILE: toolchain/check/testdata/operators/builtin/unary_op.carbon ================================================ [File too large to display: 11.8 KB] ================================================ FILE: toolchain/check/testdata/operators/overloaded/add.carbon ================================================ [File too large to display: 4.4 KB] ================================================ FILE: toolchain/check/testdata/operators/overloaded/binary_op.carbon.tmpl ================================================ [File too large to display: 819 B] ================================================ FILE: toolchain/check/testdata/operators/overloaded/bit_and.carbon ================================================ [File too large to display: 4.5 KB] ================================================ FILE: toolchain/check/testdata/operators/overloaded/bit_complement.carbon ================================================ [File too large to display: 2.6 KB] ================================================ FILE: toolchain/check/testdata/operators/overloaded/bit_or.carbon ================================================ [File too large to display: 4.5 KB] ================================================ FILE: toolchain/check/testdata/operators/overloaded/bit_xor.carbon ================================================ [File too large to display: 4.5 KB] ================================================ FILE: toolchain/check/testdata/operators/overloaded/dec.carbon ================================================ [File too large to display: 2.3 KB] ================================================ FILE: toolchain/check/testdata/operators/overloaded/div.carbon ================================================ [File too large to display: 4.4 KB] ================================================ FILE: toolchain/check/testdata/operators/overloaded/eq.carbon ================================================ [File too large to display: 32.3 KB] ================================================ FILE: toolchain/check/testdata/operators/overloaded/fail_assign_non_ref.carbon ================================================ [File too large to display: 12.3 KB] ================================================ FILE: toolchain/check/testdata/operators/overloaded/fail_error_recovery.carbon ================================================ [File too large to display: 4.4 KB] ================================================ FILE: toolchain/check/testdata/operators/overloaded/fail_no_impl.carbon ================================================ [File too large to display: 10.6 KB] ================================================ FILE: toolchain/check/testdata/operators/overloaded/fail_no_impl_for_arg.carbon ================================================ [File too large to display: 1.6 KB] ================================================ FILE: toolchain/check/testdata/operators/overloaded/implicit_as.carbon ================================================ [File too large to display: 25.7 KB] ================================================ FILE: toolchain/check/testdata/operators/overloaded/inc.carbon ================================================ [File too large to display: 2.3 KB] ================================================ FILE: toolchain/check/testdata/operators/overloaded/index.carbon ================================================ [File too large to display: 36.7 KB] ================================================ FILE: toolchain/check/testdata/operators/overloaded/index_with_prelude.carbon ================================================ [File too large to display: 49.0 KB] ================================================ FILE: toolchain/check/testdata/operators/overloaded/left_shift.carbon ================================================ [File too large to display: 4.7 KB] ================================================ FILE: toolchain/check/testdata/operators/overloaded/make_tests.sh ================================================ [File too large to display: 1.4 KB] ================================================ FILE: toolchain/check/testdata/operators/overloaded/mod.carbon ================================================ [File too large to display: 4.4 KB] ================================================ FILE: toolchain/check/testdata/operators/overloaded/mul.carbon ================================================ [File too large to display: 4.4 KB] ================================================ FILE: toolchain/check/testdata/operators/overloaded/negate.carbon ================================================ [File too large to display: 2.5 KB] ================================================ FILE: toolchain/check/testdata/operators/overloaded/ordered.carbon ================================================ [File too large to display: 34.5 KB] ================================================ FILE: toolchain/check/testdata/operators/overloaded/right_shift.carbon ================================================ [File too large to display: 4.7 KB] ================================================ FILE: toolchain/check/testdata/operators/overloaded/string_indexing.carbon ================================================ [File too large to display: 12.5 KB] ================================================ FILE: toolchain/check/testdata/operators/overloaded/sub.carbon ================================================ [File too large to display: 4.4 KB] ================================================ FILE: toolchain/check/testdata/operators/overloaded/unary_op.carbon.tmpl ================================================ [File too large to display: 610 B] ================================================ FILE: toolchain/check/testdata/operators/overloaded/unary_stmt.carbon.tmpl ================================================ [File too large to display: 565 B] ================================================ FILE: toolchain/check/testdata/package_expr/fail_not_found.carbon ================================================ [File too large to display: 3.9 KB] ================================================ FILE: toolchain/check/testdata/package_expr/syntax.carbon ================================================ [File too large to display: 24.8 KB] ================================================ FILE: toolchain/check/testdata/packages/core_name_poisoning.carbon ================================================ [File too large to display: 1.7 KB] ================================================ FILE: toolchain/check/testdata/packages/cross_package_export.carbon ================================================ [File too large to display: 43.9 KB] ================================================ FILE: toolchain/check/testdata/packages/cross_package_import.carbon ================================================ [File too large to display: 23.5 KB] ================================================ FILE: toolchain/check/testdata/packages/explicit_imports.carbon ================================================ [File too large to display: 2.6 KB] ================================================ FILE: toolchain/check/testdata/packages/export_import.carbon ================================================ [File too large to display: 41.9 KB] ================================================ FILE: toolchain/check/testdata/packages/export_mixed.carbon ================================================ [File too large to display: 30.5 KB] ================================================ FILE: toolchain/check/testdata/packages/export_name.carbon ================================================ [File too large to display: 57.9 KB] ================================================ FILE: toolchain/check/testdata/packages/fail_api_not_found.carbon ================================================ [File too large to display: 2.5 KB] ================================================ FILE: toolchain/check/testdata/packages/fail_conflict_no_namespaces.carbon ================================================ [File too large to display: 7.6 KB] ================================================ FILE: toolchain/check/testdata/packages/fail_cycle.carbon ================================================ [File too large to display: 4.1 KB] ================================================ FILE: toolchain/check/testdata/packages/fail_duplicate_api.carbon ================================================ [File too large to display: 3.3 KB] ================================================ FILE: toolchain/check/testdata/packages/fail_export_name_member.carbon ================================================ [File too large to display: 4.4 KB] ================================================ FILE: toolchain/check/testdata/packages/fail_export_name_params.carbon ================================================ [File too large to display: 6.5 KB] ================================================ FILE: toolchain/check/testdata/packages/fail_extension.carbon ================================================ [File too large to display: 6.2 KB] ================================================ FILE: toolchain/check/testdata/packages/fail_import_default.carbon ================================================ [File too large to display: 2.8 KB] ================================================ FILE: toolchain/check/testdata/packages/fail_import_inline_not_cpp.carbon ================================================ [File too large to display: 932 B] ================================================ FILE: toolchain/check/testdata/packages/fail_import_invalid.carbon ================================================ [File too large to display: 5.6 KB] ================================================ FILE: toolchain/check/testdata/packages/fail_import_repeat.carbon ================================================ [File too large to display: 4.0 KB] ================================================ FILE: toolchain/check/testdata/packages/fail_import_type_error.carbon ================================================ [File too large to display: 9.7 KB] ================================================ FILE: toolchain/check/testdata/packages/fail_modifiers.carbon ================================================ [File too large to display: 5.0 KB] ================================================ FILE: toolchain/check/testdata/packages/fail_name_with_import_failure.carbon ================================================ [File too large to display: 2.6 KB] ================================================ FILE: toolchain/check/testdata/packages/implicit_imports_empty.carbon ================================================ [File too large to display: 3.5 KB] ================================================ FILE: toolchain/check/testdata/packages/implicit_imports_entities.carbon ================================================ [File too large to display: 31.3 KB] ================================================ FILE: toolchain/check/testdata/packages/implicit_imports_prelude.carbon ================================================ [File too large to display: 11.5 KB] ================================================ FILE: toolchain/check/testdata/packages/loaded_global.carbon ================================================ [File too large to display: 8.7 KB] ================================================ FILE: toolchain/check/testdata/packages/missing_prelude.carbon ================================================ [File too large to display: 37.7 KB] ================================================ FILE: toolchain/check/testdata/packages/raw_core.carbon ================================================ [File too large to display: 18.5 KB] ================================================ FILE: toolchain/check/testdata/packages/restricted_package_names.carbon ================================================ [File too large to display: 4.7 KB] ================================================ FILE: toolchain/check/testdata/packages/unused_lazy_import.carbon ================================================ [File too large to display: 1.8 KB] ================================================ FILE: toolchain/check/testdata/patterns/tuple.carbon ================================================ [File too large to display: 9.8 KB] ================================================ FILE: toolchain/check/testdata/patterns/underscore.carbon ================================================ [File too large to display: 23.4 KB] ================================================ FILE: toolchain/check/testdata/patterns/unused.carbon ================================================ [File too large to display: 9.6 KB] ================================================ FILE: toolchain/check/testdata/patterns/unused_underscore.carbon ================================================ [File too large to display: 1.2 KB] ================================================ FILE: toolchain/check/testdata/pointer/address_of_deref.carbon ================================================ [File too large to display: 11.0 KB] ================================================ FILE: toolchain/check/testdata/pointer/address_of_lvalue.carbon ================================================ [File too large to display: 29.2 KB] ================================================ FILE: toolchain/check/testdata/pointer/arrow.carbon ================================================ [File too large to display: 6.9 KB] ================================================ FILE: toolchain/check/testdata/pointer/basic.carbon ================================================ [File too large to display: 14.4 KB] ================================================ FILE: toolchain/check/testdata/pointer/convert_qualifiers.carbon ================================================ [File too large to display: 9.9 KB] ================================================ FILE: toolchain/check/testdata/pointer/fail_address_of_error.carbon ================================================ [File too large to display: 2.9 KB] ================================================ FILE: toolchain/check/testdata/pointer/fail_address_of_value.carbon ================================================ [File too large to display: 3.9 KB] ================================================ FILE: toolchain/check/testdata/pointer/fail_deref_error.carbon ================================================ [File too large to display: 3.6 KB] ================================================ FILE: toolchain/check/testdata/pointer/fail_deref_function.carbon ================================================ [File too large to display: 2.5 KB] ================================================ FILE: toolchain/check/testdata/pointer/fail_deref_namespace.carbon ================================================ [File too large to display: 2.6 KB] ================================================ FILE: toolchain/check/testdata/pointer/fail_deref_not_pointer.carbon ================================================ [File too large to display: 6.5 KB] ================================================ FILE: toolchain/check/testdata/pointer/fail_deref_type.carbon ================================================ [File too large to display: 4.0 KB] ================================================ FILE: toolchain/check/testdata/pointer/fail_type_mismatch.carbon ================================================ [File too large to display: 5.3 KB] ================================================ FILE: toolchain/check/testdata/pointer/import.carbon ================================================ [File too large to display: 15.9 KB] ================================================ FILE: toolchain/check/testdata/pointer/nested_const.carbon ================================================ [File too large to display: 8.2 KB] ================================================ FILE: toolchain/check/testdata/pointer/types.carbon ================================================ [File too large to display: 9.9 KB] ================================================ FILE: toolchain/check/testdata/primitives/import_symbolic.carbon ================================================ [File too large to display: 39.4 KB] ================================================ FILE: toolchain/check/testdata/primitives/int_conversions.carbon ================================================ [File too large to display: 12.2 KB] ================================================ FILE: toolchain/check/testdata/primitives/numeric_literals.carbon ================================================ [File too large to display: 10.2 KB] ================================================ FILE: toolchain/check/testdata/primitives/string_literals.carbon ================================================ [File too large to display: 6.1 KB] ================================================ FILE: toolchain/check/testdata/primitives/type_literals.carbon ================================================ [File too large to display: 46.0 KB] ================================================ FILE: toolchain/check/testdata/return/code_after_return.carbon ================================================ [File too large to display: 2.4 KB] ================================================ FILE: toolchain/check/testdata/return/code_after_return_value.carbon ================================================ [File too large to display: 8.1 KB] ================================================ FILE: toolchain/check/testdata/return/fail_call_in_type.carbon ================================================ [File too large to display: 5.9 KB] ================================================ FILE: toolchain/check/testdata/return/fail_error_in_type.carbon ================================================ [File too large to display: 1.9 KB] ================================================ FILE: toolchain/check/testdata/return/fail_let_in_type.carbon ================================================ [File too large to display: 7.0 KB] ================================================ FILE: toolchain/check/testdata/return/fail_missing_return.carbon ================================================ [File too large to display: 3.0 KB] ================================================ FILE: toolchain/check/testdata/return/fail_missing_return_empty_tuple.carbon ================================================ [File too large to display: 2.6 KB] ================================================ FILE: toolchain/check/testdata/return/fail_return_var_no_returned_var.carbon ================================================ [File too large to display: 2.8 KB] ================================================ FILE: toolchain/check/testdata/return/fail_return_with_returned_var.carbon ================================================ [File too large to display: 16.2 KB] ================================================ FILE: toolchain/check/testdata/return/fail_returned_var_form.carbon ================================================ [File too large to display: 1.2 KB] ================================================ FILE: toolchain/check/testdata/return/fail_returned_var_no_return_type.carbon ================================================ [File too large to display: 8.3 KB] ================================================ FILE: toolchain/check/testdata/return/fail_returned_var_shadow.carbon ================================================ [File too large to display: 19.6 KB] ================================================ FILE: toolchain/check/testdata/return/fail_returned_var_type.carbon ================================================ [File too large to display: 9.4 KB] ================================================ FILE: toolchain/check/testdata/return/fail_type_mismatch.carbon ================================================ [File too large to display: 4.0 KB] ================================================ FILE: toolchain/check/testdata/return/fail_value_disallowed.carbon ================================================ [File too large to display: 2.3 KB] ================================================ FILE: toolchain/check/testdata/return/fail_value_missing.carbon ================================================ [File too large to display: 3.2 KB] ================================================ FILE: toolchain/check/testdata/return/fail_var_in_type.carbon ================================================ [File too large to display: 5.1 KB] ================================================ FILE: toolchain/check/testdata/return/import_convert_function.carbon ================================================ [File too large to display: 123.5 KB] ================================================ FILE: toolchain/check/testdata/return/missing_return_no_return_type.carbon ================================================ [File too large to display: 1.7 KB] ================================================ FILE: toolchain/check/testdata/return/no_value.carbon ================================================ [File too large to display: 1.7 KB] ================================================ FILE: toolchain/check/testdata/return/returned_var.carbon ================================================ [File too large to display: 15.3 KB] ================================================ FILE: toolchain/check/testdata/return/returned_var_scope.carbon ================================================ [File too large to display: 18.6 KB] ================================================ FILE: toolchain/check/testdata/return/struct.carbon ================================================ [File too large to display: 7.8 KB] ================================================ FILE: toolchain/check/testdata/return/tuple.carbon ================================================ [File too large to display: 10.4 KB] ================================================ FILE: toolchain/check/testdata/return/value.carbon ================================================ [File too large to display: 6.9 KB] ================================================ FILE: toolchain/check/testdata/struct/empty.carbon ================================================ [File too large to display: 3.6 KB] ================================================ FILE: toolchain/check/testdata/struct/fail_access_into_invalid.carbon ================================================ [File too large to display: 2.1 KB] ================================================ FILE: toolchain/check/testdata/struct/fail_assign_empty.carbon ================================================ [File too large to display: 3.3 KB] ================================================ FILE: toolchain/check/testdata/struct/fail_assign_nested.carbon ================================================ [File too large to display: 3.0 KB] ================================================ FILE: toolchain/check/testdata/struct/fail_assign_to_empty.carbon ================================================ [File too large to display: 3.2 KB] ================================================ FILE: toolchain/check/testdata/struct/fail_duplicate_name.carbon ================================================ [File too large to display: 9.4 KB] ================================================ FILE: toolchain/check/testdata/struct/fail_field_name_mismatch.carbon ================================================ [File too large to display: 4.9 KB] ================================================ FILE: toolchain/check/testdata/struct/fail_field_type_mismatch.carbon ================================================ [File too large to display: 3.6 KB] ================================================ FILE: toolchain/check/testdata/struct/fail_keyword_name.carbon ================================================ [File too large to display: 1.5 KB] ================================================ FILE: toolchain/check/testdata/struct/fail_member_access_type.carbon ================================================ [File too large to display: 9.1 KB] ================================================ FILE: toolchain/check/testdata/struct/fail_member_of_function.carbon ================================================ [File too large to display: 2.0 KB] ================================================ FILE: toolchain/check/testdata/struct/fail_nested_incomplete.carbon ================================================ [File too large to display: 3.9 KB] ================================================ FILE: toolchain/check/testdata/struct/fail_non_member_access.carbon ================================================ [File too large to display: 8.5 KB] ================================================ FILE: toolchain/check/testdata/struct/fail_too_few_values.carbon ================================================ [File too large to display: 3.7 KB] ================================================ FILE: toolchain/check/testdata/struct/fail_type_assign.carbon ================================================ [File too large to display: 4.0 KB] ================================================ FILE: toolchain/check/testdata/struct/fail_value_as_type.carbon ================================================ [File too large to display: 3.4 KB] ================================================ FILE: toolchain/check/testdata/struct/import.carbon ================================================ [File too large to display: 28.2 KB] ================================================ FILE: toolchain/check/testdata/struct/literal_member_access.carbon ================================================ [File too large to display: 9.4 KB] ================================================ FILE: toolchain/check/testdata/struct/member_access.carbon ================================================ [File too large to display: 17.4 KB] ================================================ FILE: toolchain/check/testdata/struct/nested_struct_in_place.carbon ================================================ [File too large to display: 7.6 KB] ================================================ FILE: toolchain/check/testdata/struct/one_entry.carbon ================================================ [File too large to display: 11.3 KB] ================================================ FILE: toolchain/check/testdata/struct/partially_const.carbon ================================================ [File too large to display: 12.8 KB] ================================================ FILE: toolchain/check/testdata/struct/reorder_fields.carbon ================================================ [File too large to display: 14.1 KB] ================================================ FILE: toolchain/check/testdata/struct/tuple_as_element.carbon ================================================ [File too large to display: 17.2 KB] ================================================ FILE: toolchain/check/testdata/struct/two_entries.carbon ================================================ [File too large to display: 20.1 KB] ================================================ FILE: toolchain/check/testdata/tuple/basics.carbon ================================================ [File too large to display: 23.4 KB] ================================================ FILE: toolchain/check/testdata/tuple/class_tuples.carbon ================================================ [File too large to display: 3.0 KB] ================================================ FILE: toolchain/check/testdata/tuple/element_access.carbon ================================================ [File too large to display: 31.8 KB] ================================================ FILE: toolchain/check/testdata/tuple/import.carbon ================================================ [File too large to display: 31.6 KB] ================================================ FILE: toolchain/check/testdata/tuple/in_place_tuple_init.carbon ================================================ [File too large to display: 21.0 KB] ================================================ FILE: toolchain/check/testdata/tuple/size_mismatch.carbon ================================================ [File too large to display: 2.0 KB] ================================================ FILE: toolchain/check/testdata/tuple/tuple_pattern.carbon ================================================ [File too large to display: 33.2 KB] ================================================ FILE: toolchain/check/testdata/var/export_name.carbon ================================================ [File too large to display: 9.7 KB] ================================================ FILE: toolchain/check/testdata/var/fail_duplicate_decl.carbon ================================================ [File too large to display: 5.6 KB] ================================================ FILE: toolchain/check/testdata/var/fail_generic.carbon ================================================ [File too large to display: 1.3 KB] ================================================ FILE: toolchain/check/testdata/var/fail_init_type_mismatch.carbon ================================================ [File too large to display: 3.7 KB] ================================================ FILE: toolchain/check/testdata/var/fail_init_with_self.carbon ================================================ [File too large to display: 4.1 KB] ================================================ FILE: toolchain/check/testdata/var/fail_lookup_outside_scope.carbon ================================================ [File too large to display: 878 B] ================================================ FILE: toolchain/check/testdata/var/fail_modifiers.carbon ================================================ [File too large to display: 2.2 KB] ================================================ FILE: toolchain/check/testdata/var/fail_namespace_conflict.carbon ================================================ [File too large to display: 1.4 KB] ================================================ FILE: toolchain/check/testdata/var/fail_not_copyable.carbon ================================================ [File too large to display: 1.2 KB] ================================================ FILE: toolchain/check/testdata/var/fail_storage_is_literal.carbon ================================================ [File too large to display: 3.7 KB] ================================================ FILE: toolchain/check/testdata/var/fail_todo_control_flow_init.carbon ================================================ [File too large to display: 4.1 KB] ================================================ FILE: toolchain/check/testdata/var/global_decl.carbon ================================================ [File too large to display: 3.1 KB] ================================================ FILE: toolchain/check/testdata/var/global_decl_import.carbon ================================================ [File too large to display: 2.6 KB] ================================================ FILE: toolchain/check/testdata/var/global_decl_with_init.carbon ================================================ [File too large to display: 10.8 KB] ================================================ FILE: toolchain/check/testdata/var/global_lookup.carbon ================================================ [File too large to display: 5.2 KB] ================================================ FILE: toolchain/check/testdata/var/global_lookup_in_scope.carbon ================================================ [File too large to display: 6.4 KB] ================================================ FILE: toolchain/check/testdata/var/import.carbon ================================================ [File too large to display: 5.0 KB] ================================================ FILE: toolchain/check/testdata/var/import_access.carbon ================================================ [File too large to display: 9.4 KB] ================================================ FILE: toolchain/check/testdata/var/initialization.carbon ================================================ [File too large to display: 7.0 KB] ================================================ FILE: toolchain/check/testdata/var/lookup.carbon ================================================ [File too large to display: 3.8 KB] ================================================ FILE: toolchain/check/testdata/var/shadowing.carbon ================================================ [File too large to display: 7.9 KB] ================================================ FILE: toolchain/check/testdata/var/var_pattern.carbon ================================================ [File too large to display: 53.2 KB] ================================================ FILE: toolchain/check/testdata/where_expr/constraints.carbon ================================================ [File too large to display: 27.0 KB] ================================================ FILE: toolchain/check/testdata/where_expr/designator.carbon ================================================ [File too large to display: 11.1 KB] ================================================ FILE: toolchain/check/testdata/where_expr/dot_self_impls.carbon ================================================ [File too large to display: 8.0 KB] ================================================ FILE: toolchain/check/testdata/where_expr/dot_self_index.carbon ================================================ [File too large to display: 11.9 KB] ================================================ FILE: toolchain/check/testdata/where_expr/equal_rewrite.carbon ================================================ [File too large to display: 31.7 KB] ================================================ FILE: toolchain/check/testdata/where_expr/fail_not_facet.carbon ================================================ [File too large to display: 1.5 KB] ================================================ FILE: toolchain/check/testdata/where_expr/non_generic.carbon ================================================ [File too large to display: 3.8 KB] ================================================ FILE: toolchain/check/testdata/while/while.carbon ================================================ [File too large to display: 12.6 KB] ================================================ FILE: toolchain/check/thunk.cpp ================================================ [File too large to display: 20.0 KB] ================================================ FILE: toolchain/check/thunk.h ================================================ [File too large to display: 1.7 KB] ================================================ FILE: toolchain/check/type.cpp ================================================ [File too large to display: 11.7 KB] ================================================ FILE: toolchain/check/type.h ================================================ [File too large to display: 7.4 KB] ================================================ FILE: toolchain/check/type_completion.cpp ================================================ [File too large to display: 41.3 KB] ================================================ FILE: toolchain/check/type_completion.h ================================================ [File too large to display: 5.4 KB] ================================================ FILE: toolchain/check/type_structure.cpp ================================================ [File too large to display: 11.2 KB] ================================================ FILE: toolchain/check/type_structure.h ================================================ [File too large to display: 7.2 KB] ================================================ FILE: toolchain/check/unused.cpp ================================================ [File too large to display: 1.9 KB] ================================================ FILE: toolchain/check/unused.h ================================================ [File too large to display: 914 B] ================================================ FILE: toolchain/codegen/BUILD ================================================ [File too large to display: 796 B] ================================================ FILE: toolchain/codegen/codegen.cpp ================================================ [File too large to display: 1.5 KB] ================================================ FILE: toolchain/codegen/codegen.h ================================================ [File too large to display: 1.8 KB] ================================================ FILE: toolchain/codegen/testdata/assembly/basic.carbon ================================================ [File too large to display: 706 B] ================================================ FILE: toolchain/codegen/testdata/fail_target_triple.carbon ================================================ [File too large to display: 748 B] ================================================ FILE: toolchain/codegen/testdata/objcode/basic.carbon ================================================ [File too large to display: 768 B] ================================================ FILE: toolchain/diagnostics/BUILD ================================================ [File too large to display: 3.2 KB] ================================================ FILE: toolchain/diagnostics/check_diagnostics.py ================================================ [File too large to display: 3.5 KB] ================================================ FILE: toolchain/diagnostics/consumer.cpp ================================================ [File too large to display: 1.8 KB] ================================================ FILE: toolchain/diagnostics/consumer.h ================================================ [File too large to display: 3.0 KB] ================================================ FILE: toolchain/diagnostics/coverage_test.cpp ================================================ [File too large to display: 2.9 KB] ================================================ FILE: toolchain/diagnostics/diagnostic.cpp ================================================ [File too large to display: 2.3 KB] ================================================ FILE: toolchain/diagnostics/diagnostic.h ================================================ [File too large to display: 8.1 KB] ================================================ FILE: toolchain/diagnostics/emitter.h ================================================ [File too large to display: 20.0 KB] ================================================ FILE: toolchain/diagnostics/emitter_test.cpp ================================================ [File too large to display: 10.0 KB] ================================================ FILE: toolchain/diagnostics/file_diagnostics.h ================================================ [File too large to display: 1.1 KB] ================================================ FILE: toolchain/diagnostics/format_providers.cpp ================================================ [File too large to display: 3.3 KB] ================================================ FILE: toolchain/diagnostics/format_providers.h ================================================ [File too large to display: 2.4 KB] ================================================ FILE: toolchain/diagnostics/format_providers_test.cpp ================================================ [File too large to display: 4.9 KB] ================================================ FILE: toolchain/diagnostics/kind.cpp ================================================ [File too large to display: 483 B] ================================================ FILE: toolchain/diagnostics/kind.def ================================================ [File too large to display: 25.4 KB] ================================================ FILE: toolchain/diagnostics/kind.h ================================================ [File too large to display: 1.5 KB] ================================================ FILE: toolchain/diagnostics/mocks.cpp ================================================ [File too large to display: 1.1 KB] ================================================ FILE: toolchain/diagnostics/mocks.h ================================================ [File too large to display: 3.0 KB] ================================================ FILE: toolchain/diagnostics/null_diagnostics.h ================================================ [File too large to display: 1.3 KB] ================================================ FILE: toolchain/diagnostics/sorting_consumer.h ================================================ [File too large to display: 2.9 KB] ================================================ FILE: toolchain/diagnostics/sorting_consumer_test.cpp ================================================ [File too large to display: 5.0 KB] ================================================ FILE: toolchain/docs/README.md ================================================ [File too large to display: 4.1 KB] ================================================ FILE: toolchain/docs/adding_features.md ================================================ [File too large to display: 27.5 KB] ================================================ FILE: toolchain/docs/check/README.md ================================================ [File too large to display: 29.1 KB] ================================================ FILE: toolchain/docs/check/associated_constant.md ================================================ [File too large to display: 7.2 KB] ================================================ FILE: toolchain/docs/check/pattern_matching.md ================================================ [File too large to display: 14.7 KB] ================================================ FILE: toolchain/docs/coalesce_generic_lowering.md ================================================ [File too large to display: 11.4 KB] ================================================ FILE: toolchain/docs/debugging.md ================================================ [File too large to display: 9.2 KB] ================================================ FILE: toolchain/docs/design/README.md ================================================ [File too large to display: 267 B] ================================================ FILE: toolchain/docs/design/clang_api.md ================================================ [File too large to display: 8.1 KB] ================================================ FILE: toolchain/docs/diagnostics.md ================================================ [File too large to display: 13.6 KB] ================================================ FILE: toolchain/docs/driver.md ================================================ [File too large to display: 571 B] ================================================ FILE: toolchain/docs/idioms.md ================================================ [File too large to display: 13.1 KB] ================================================ FILE: toolchain/docs/lex.md ================================================ [File too large to display: 1.0 KB] ================================================ FILE: toolchain/docs/lower.md ================================================ [File too large to display: 7.8 KB] ================================================ FILE: toolchain/docs/parse.md ================================================ [File too large to display: 30.5 KB] ================================================ FILE: toolchain/driver/BUILD ================================================ [File too large to display: 12.9 KB] ================================================ FILE: toolchain/driver/bazel_build_clang_runtimes.cpp ================================================ [File too large to display: 8.7 KB] ================================================ FILE: toolchain/driver/build_runtimes_subcommand.cpp ================================================ [File too large to display: 4.1 KB] ================================================ FILE: toolchain/driver/build_runtimes_subcommand.h ================================================ [File too large to display: 1.4 KB] ================================================ FILE: toolchain/driver/clang_runner.cpp ================================================ [File too large to display: 20.4 KB] ================================================ FILE: toolchain/driver/clang_runner.h ================================================ [File too large to display: 6.2 KB] ================================================ FILE: toolchain/driver/clang_runner_test.cpp ================================================ [File too large to display: 9.4 KB] ================================================ FILE: toolchain/driver/clang_runtimes.cpp ================================================ [File too large to display: 20.0 KB] ================================================ FILE: toolchain/driver/clang_runtimes.h ================================================ [File too large to display: 12.4 KB] ================================================ FILE: toolchain/driver/clang_runtimes_test.cpp ================================================ [File too large to display: 12.7 KB] ================================================ FILE: toolchain/driver/clang_subcommand.cpp ================================================ [File too large to display: 2.9 KB] ================================================ FILE: toolchain/driver/clang_subcommand.h ================================================ [File too large to display: 1.2 KB] ================================================ FILE: toolchain/driver/codegen_options.cpp ================================================ [File too large to display: 806 B] ================================================ FILE: toolchain/driver/codegen_options.h ================================================ [File too large to display: 768 B] ================================================ FILE: toolchain/driver/compile_benchmark.cpp ================================================ [File too large to display: 6.1 KB] ================================================ FILE: toolchain/driver/compile_subcommand.cpp ================================================ [File too large to display: 45.3 KB] ================================================ FILE: toolchain/driver/compile_subcommand.h ================================================ [File too large to display: 2.8 KB] ================================================ FILE: toolchain/driver/config_subcommand.cpp ================================================ [File too large to display: 8.1 KB] ================================================ FILE: toolchain/driver/config_subcommand.h ================================================ [File too large to display: 1.2 KB] ================================================ FILE: toolchain/driver/driver.cpp ================================================ [File too large to display: 9.5 KB] ================================================ FILE: toolchain/driver/driver.h ================================================ [File too large to display: 2.5 KB] ================================================ FILE: toolchain/driver/driver_env.h ================================================ [File too large to display: 2.8 KB] ================================================ FILE: toolchain/driver/driver_fuzzer.cpp ================================================ [File too large to display: 3.1 KB] ================================================ FILE: toolchain/driver/driver_subcommand.cpp ================================================ [File too large to display: 851 B] ================================================ FILE: toolchain/driver/driver_subcommand.h ================================================ [File too large to display: 2.8 KB] ================================================ FILE: toolchain/driver/driver_test.cpp ================================================ [File too large to display: 13.1 KB] ================================================ FILE: toolchain/driver/flags.def ================================================ [File too large to display: 676 B] ================================================ FILE: toolchain/driver/format_subcommand.cpp ================================================ [File too large to display: 3.1 KB] ================================================ FILE: toolchain/driver/format_subcommand.h ================================================ [File too large to display: 1.1 KB] ================================================ FILE: toolchain/driver/fuzzer_corpus/3dbc1b67cab9a71bac41d7ee17fa31a8b32a9904 ================================================ [File too large to display: 4 B] ================================================ FILE: toolchain/driver/fuzzer_corpus/8b0653417087d3d7fd049166a72cd7bcbc5f6dd1 ================================================ [File too large to display: 4 B] ================================================ FILE: toolchain/driver/fuzzer_corpus/adc83b19e793491b1c6ea0fd8b46cd9f32e592fc ================================================ [File too large to display: 1 B] ================================================ FILE: toolchain/driver/language_server_subcommand.cpp ================================================ [File too large to display: 1.2 KB] ================================================ FILE: toolchain/driver/language_server_subcommand.h ================================================ [File too large to display: 960 B] ================================================ FILE: toolchain/driver/link_subcommand.cpp ================================================ [File too large to display: 5.6 KB] ================================================ FILE: toolchain/driver/link_subcommand.h ================================================ [File too large to display: 1.3 KB] ================================================ FILE: toolchain/driver/lld_runner.cpp ================================================ [File too large to display: 2.1 KB] ================================================ FILE: toolchain/driver/lld_runner.h ================================================ [File too large to display: 1.2 KB] ================================================ FILE: toolchain/driver/lld_runner_test.cpp ================================================ [File too large to display: 7.4 KB] ================================================ FILE: toolchain/driver/lld_subcommand.cpp ================================================ [File too large to display: 3.8 KB] ================================================ FILE: toolchain/driver/lld_subcommand.h ================================================ [File too large to display: 1.5 KB] ================================================ FILE: toolchain/driver/llvm_runner.cpp ================================================ [File too large to display: 1.3 KB] ================================================ FILE: toolchain/driver/llvm_runner.h ================================================ [File too large to display: 792 B] ================================================ FILE: toolchain/driver/llvm_runner_test.cpp ================================================ [File too large to display: 2.6 KB] ================================================ FILE: toolchain/driver/llvm_subcommand.cpp ================================================ [File too large to display: 2.6 KB] ================================================ FILE: toolchain/driver/llvm_subcommand.h ================================================ [File too large to display: 1.9 KB] ================================================ FILE: toolchain/driver/prebuilt_runtimes.bzl ================================================ [File too large to display: 7.4 KB] ================================================ FILE: toolchain/driver/runtimes_cache.cpp ================================================ [File too large to display: 19.1 KB] ================================================ FILE: toolchain/driver/runtimes_cache.h ================================================ [File too large to display: 17.8 KB] ================================================ FILE: toolchain/driver/runtimes_cache_test.cpp ================================================ [File too large to display: 33.1 KB] ================================================ FILE: toolchain/driver/testdata/compile/clang_args_warning.carbon ================================================ [File too large to display: 780 B] ================================================ FILE: toolchain/driver/testdata/compile/fail_clang_arg_extra_input.carbon ================================================ [File too large to display: 924 B] ================================================ FILE: toolchain/driver/testdata/compile/fail_clang_args.carbon ================================================ [File too large to display: 1.1 KB] ================================================ FILE: toolchain/driver/testdata/compile/fail_clang_args_error.carbon ================================================ [File too large to display: 792 B] ================================================ FILE: toolchain/driver/testdata/compile/init_array.carbon ================================================ [File too large to display: 928 B] ================================================ FILE: toolchain/driver/testdata/compile/no_init_array.carbon ================================================ [File too large to display: 923 B] ================================================ FILE: toolchain/driver/testdata/compile/optimize/clang_no_optimize_twice.carbon ================================================ [File too large to display: 4.1 KB] ================================================ FILE: toolchain/driver/testdata/compile/optimize/fail_clang_forward_optimize_size.carbon ================================================ [File too large to display: 825 B] ================================================ FILE: toolchain/driver/testdata/compile/optimize/fail_clang_forward_optimize_speed.carbon ================================================ [File too large to display: 828 B] ================================================ FILE: toolchain/driver/testdata/compile/optimize/fail_clang_no_optimize_arg.carbon ================================================ [File too large to display: 797 B] ================================================ FILE: toolchain/driver/testdata/compile/optimize/fail_clang_override_optimize_arg.carbon ================================================ [File too large to display: 842 B] ================================================ FILE: toolchain/driver/testdata/compile/optimize/optimize_debug.carbon ================================================ [File too large to display: 7.8 KB] ================================================ FILE: toolchain/driver/testdata/compile/optimize/optimize_default.carbon ================================================ [File too large to display: 7.8 KB] ================================================ FILE: toolchain/driver/testdata/compile/optimize/optimize_none.carbon ================================================ [File too large to display: 10.6 KB] ================================================ FILE: toolchain/driver/testdata/compile/optimize/optimize_size.carbon ================================================ [File too large to display: 10.6 KB] ================================================ FILE: toolchain/driver/testdata/compile/optimize/optimize_speed.carbon ================================================ [File too large to display: 8.8 KB] ================================================ FILE: toolchain/driver/testdata/dump_mem_usage.carbon ================================================ [File too large to display: 838 B] ================================================ FILE: toolchain/driver/testdata/dump_shared_values.carbon ================================================ [File too large to display: 1.6 KB] ================================================ FILE: toolchain/driver/testdata/dump_timings.carbon ================================================ [File too large to display: 882 B] ================================================ FILE: toolchain/driver/testdata/fail_bad_prebuilt_runtimes.carbon ================================================ [File too large to display: 893 B] ================================================ FILE: toolchain/driver/testdata/fail_bad_runtimes_cache.carbon ================================================ [File too large to display: 881 B] ================================================ FILE: toolchain/driver/testdata/fail_clang_fuzzing.cpp ================================================ [File too large to display: 771 B] ================================================ FILE: toolchain/driver/testdata/fail_clang_no_args.cpp ================================================ [File too large to display: 665 B] ================================================ FILE: toolchain/driver/testdata/fail_config.carbon ================================================ [File too large to display: 1.4 KB] ================================================ FILE: toolchain/driver/testdata/fail_dump_phase_conflict.carbon ================================================ [File too large to display: 756 B] ================================================ FILE: toolchain/driver/testdata/fail_errors_in_two_files.carbon ================================================ [File too large to display: 1.1 KB] ================================================ FILE: toolchain/driver/testdata/fail_errors_sorted.carbon ================================================ [File too large to display: 1.0 KB] ================================================ FILE: toolchain/driver/testdata/fail_errors_streamed.carbon ================================================ [File too large to display: 1.0 KB] ================================================ FILE: toolchain/driver/testdata/fail_flag.carbon ================================================ [File too large to display: 692 B] ================================================ FILE: toolchain/driver/testdata/fail_flush_errors.carbon ================================================ [File too large to display: 2.0 KB] ================================================ FILE: toolchain/driver/testdata/fail_fuzzing_invalid_clang_arg.carbon ================================================ [File too large to display: 834 B] ================================================ FILE: toolchain/driver/testdata/fail_fuzzing_invalid_target.carbon ================================================ [File too large to display: 836 B] ================================================ FILE: toolchain/driver/testdata/fail_input_is_directory.carbon ================================================ [File too large to display: 702 B] ================================================ FILE: toolchain/driver/testdata/fail_lld_fuzzing.carbon ================================================ [File too large to display: 783 B] ================================================ FILE: toolchain/driver/testdata/fail_llvm_fuzzing.carbon ================================================ [File too large to display: 781 B] ================================================ FILE: toolchain/driver/testdata/fail_missing_file.carbon ================================================ [File too large to display: 727 B] ================================================ FILE: toolchain/driver/testdata/fail_missing_stdin_output.carbon ================================================ [File too large to display: 905 B] ================================================ FILE: toolchain/driver/testdata/fail_output_is_directory.carbon ================================================ [File too large to display: 787 B] ================================================ FILE: toolchain/driver/testdata/link/fail_object_files_missing.carbon ================================================ [File too large to display: 792 B] ================================================ FILE: toolchain/driver/testdata/link/fail_output_missing.carbon ================================================ [File too large to display: 780 B] ================================================ FILE: toolchain/driver/testdata/multi_input_with_output.carbon ================================================ [File too large to display: 1.1 KB] ================================================ FILE: toolchain/driver/testdata/stdin.carbon ================================================ [File too large to display: 4.4 KB] ================================================ FILE: toolchain/driver/testdata/verbose.carbon ================================================ [File too large to display: 1.7 KB] ================================================ FILE: toolchain/driver/tool_runner_base.cpp ================================================ [File too large to display: 624 B] ================================================ FILE: toolchain/driver/tool_runner_base.h ================================================ [File too large to display: 1.7 KB] ================================================ FILE: toolchain/format/BUILD ================================================ [File too large to display: 693 B] ================================================ FILE: toolchain/format/format.cpp ================================================ [File too large to display: 461 B] ================================================ FILE: toolchain/format/format.h ================================================ [File too large to display: 770 B] ================================================ FILE: toolchain/format/formatter.cpp ================================================ [File too large to display: 2.9 KB] ================================================ FILE: toolchain/format/formatter.h ================================================ [File too large to display: 2.8 KB] ================================================ FILE: toolchain/format/testdata/basics/braces.carbon ================================================ [File too large to display: 1.1 KB] ================================================ FILE: toolchain/format/testdata/basics/comments.carbon ================================================ [File too large to display: 1.0 KB] ================================================ FILE: toolchain/format/testdata/basics/empty.carbon ================================================ [File too large to display: 567 B] ================================================ FILE: toolchain/format/testdata/basics/fail_invalid_comment.carbon ================================================ [File too large to display: 840 B] ================================================ FILE: toolchain/format/testdata/basics/fail_multi_file_one_output.carbon ================================================ [File too large to display: 799 B] ================================================ FILE: toolchain/format/testdata/basics/fail_nonexistent.carbon ================================================ [File too large to display: 751 B] ================================================ FILE: toolchain/format/testdata/basics/simple.carbon ================================================ [File too large to display: 712 B] ================================================ FILE: toolchain/install/BUILD ================================================ [File too large to display: 17.0 KB] ================================================ FILE: toolchain/install/bazel/carbon_cc_toolchain_config.bzl ================================================ [File too large to display: 11.4 KB] ================================================ FILE: toolchain/install/bazel/carbon_detected_variables.tpl.bzl ================================================ [File too large to display: 489 B] ================================================ FILE: toolchain/install/bazel/carbon_runtimes.bzl ================================================ [File too large to display: 6.3 KB] ================================================ FILE: toolchain/install/bazel/carbon_toolchain.bzl ================================================ [File too large to display: 2.1 KB] ================================================ FILE: toolchain/install/bazel/clang_resource_dir.BUILD ================================================ [File too large to display: 316 B] ================================================ FILE: toolchain/install/bazel/empty.BUILD ================================================ ================================================ FILE: toolchain/install/bazel/install.BUILD ================================================ [File too large to display: 686 B] ================================================ FILE: toolchain/install/bazel/install.MODULE.bazel ================================================ [File too large to display: 819 B] ================================================ FILE: toolchain/install/bazel/make_include_copts.bzl ================================================ [File too large to display: 879 B] ================================================ FILE: toolchain/install/bazel/runtimes.BUILD ================================================ [File too large to display: 5.1 KB] ================================================ FILE: toolchain/install/busybox_info.cpp ================================================ [File too large to display: 4.7 KB] ================================================ FILE: toolchain/install/busybox_info.h ================================================ [File too large to display: 1.3 KB] ================================================ FILE: toolchain/install/busybox_info_test.cpp ================================================ [File too large to display: 13.5 KB] ================================================ FILE: toolchain/install/busybox_main.cpp ================================================ [File too large to display: 5.5 KB] ================================================ FILE: toolchain/install/carbon_install.txt ================================================ [File too large to display: 230 B] ================================================ FILE: toolchain/install/configure_cmake_file.bzl ================================================ [File too large to display: 2.4 KB] ================================================ FILE: toolchain/install/configure_cmake_file_impl.py ================================================ [File too large to display: 3.1 KB] ================================================ FILE: toolchain/install/install_filegroups.bzl ================================================ [File too large to display: 6.0 KB] ================================================ FILE: toolchain/install/llvm_symlinks_test.py ================================================ [File too large to display: 4.6 KB] ================================================ FILE: toolchain/install/make_installation_digest.cpp ================================================ [File too large to display: 4.9 KB] ================================================ FILE: toolchain/install/pkg_helpers.bzl ================================================ [File too large to display: 2.8 KB] ================================================ FILE: toolchain/install/symlink_helpers.bzl ================================================ [File too large to display: 3.0 KB] ================================================ FILE: toolchain/install/toolchain_tar_test.py ================================================ [File too large to display: 1.5 KB] ================================================ FILE: toolchain/language_server/BUILD ================================================ [File too large to display: 2.6 KB] ================================================ FILE: toolchain/language_server/context.cpp ================================================ [File too large to display: 7.7 KB] ================================================ FILE: toolchain/language_server/context.h ================================================ [File too large to display: 3.3 KB] ================================================ FILE: toolchain/language_server/handle.h ================================================ [File too large to display: 1.7 KB] ================================================ FILE: toolchain/language_server/handle_document_symbol.cpp ================================================ [File too large to display: 6.7 KB] ================================================ FILE: toolchain/language_server/handle_initialize.cpp ================================================ [File too large to display: 774 B] ================================================ FILE: toolchain/language_server/handle_shutdown.cpp ================================================ [File too large to display: 845 B] ================================================ FILE: toolchain/language_server/handle_text_document.cpp ================================================ [File too large to display: 5.6 KB] ================================================ FILE: toolchain/language_server/incoming_messages.cpp ================================================ [File too large to display: 5.0 KB] ================================================ FILE: toolchain/language_server/incoming_messages.h ================================================ [File too large to display: 2.8 KB] ================================================ FILE: toolchain/language_server/language_server.cpp ================================================ [File too large to display: 3.0 KB] ================================================ FILE: toolchain/language_server/language_server.h ================================================ [File too large to display: 1.0 KB] ================================================ FILE: toolchain/language_server/outgoing_messages.h ================================================ [File too large to display: 1.3 KB] ================================================ FILE: toolchain/language_server/testdata/basics/exit.carbon ================================================ [File too large to display: 617 B] ================================================ FILE: toolchain/language_server/testdata/basics/fail_empty_stdin.carbon ================================================ [File too large to display: 710 B] ================================================ FILE: toolchain/language_server/testdata/basics/fail_no_stdin.carbon ================================================ [File too large to display: 714 B] ================================================ FILE: toolchain/language_server/testdata/basics/fail_shutdown_without_exit.carbon ================================================ [File too large to display: 1.3 KB] ================================================ FILE: toolchain/language_server/testdata/basics/initialize.carbon ================================================ [File too large to display: 1.2 KB] ================================================ FILE: toolchain/language_server/testdata/basics/notify_parse_error.carbon ================================================ [File too large to display: 1.4 KB] ================================================ FILE: toolchain/language_server/testdata/basics/unexpected_reply.carbon ================================================ [File too large to display: 928 B] ================================================ FILE: toolchain/language_server/testdata/basics/unsupported_call.carbon ================================================ [File too large to display: 976 B] ================================================ FILE: toolchain/language_server/testdata/basics/unsupported_notification.carbon ================================================ [File too large to display: 814 B] ================================================ FILE: toolchain/language_server/testdata/basics/verbose.carbon ================================================ [File too large to display: 817 B] ================================================ FILE: toolchain/language_server/testdata/document_symbol/basics.carbon ================================================ [File too large to display: 4.2 KB] ================================================ FILE: toolchain/language_server/testdata/document_symbol/language.carbon ================================================ [File too large to display: 1.4 KB] ================================================ FILE: toolchain/language_server/testdata/document_symbol/nested.carbon ================================================ [File too large to display: 4.1 KB] ================================================ FILE: toolchain/language_server/testdata/document_symbol/unknown.carbon ================================================ [File too large to display: 1.4 KB] ================================================ FILE: toolchain/language_server/testdata/text_document/change_unknown.carbon ================================================ [File too large to display: 1.5 KB] ================================================ FILE: toolchain/language_server/testdata/text_document/close_unknown.carbon ================================================ [File too large to display: 1.4 KB] ================================================ FILE: toolchain/language_server/testdata/text_document/diagnostics.carbon ================================================ [File too large to display: 5.0 KB] ================================================ FILE: toolchain/language_server/testdata/text_document/incremental_sync.carbon ================================================ [File too large to display: 10.4 KB] ================================================ FILE: toolchain/language_server/testdata/text_document/incremental_sync_multiline.carbon ================================================ [File too large to display: 6.1 KB] ================================================ FILE: toolchain/language_server/testdata/text_document/open_change_close.carbon ================================================ [File too large to display: 2.6 KB] ================================================ FILE: toolchain/language_server/testdata/text_document/open_duplicate.carbon ================================================ [File too large to display: 2.3 KB] ================================================ FILE: toolchain/language_server/testdata/text_document/open_with_cpp_nonexistent.carbon ================================================ [File too large to display: 2.7 KB] ================================================ FILE: toolchain/lex/BUILD ================================================ [File too large to display: 8.2 KB] ================================================ FILE: toolchain/lex/character_set.h ================================================ [File too large to display: 2.7 KB] ================================================ FILE: toolchain/lex/dump.cpp ================================================ [File too large to display: 882 B] ================================================ FILE: toolchain/lex/dump.h ================================================ [File too large to display: 1.0 KB] ================================================ FILE: toolchain/lex/fuzzer_corpus/numeric_literal/00b8cd836c457801a27431b2fae14dc889e3e92a ================================================ [File too large to display: 166 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/numeric_literal/020bf8374f78631e3832c436818bc4a077464c9f ================================================ [File too large to display: 82 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/numeric_literal/046ea6e92945727581cc0d3550754bf545fbcce3 ================================================ [File too large to display: 2 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/numeric_literal/048ca7783b27c2f9ffa11a6e415df2821de767e3 ================================================ [File too large to display: 30 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/numeric_literal/0523a3cf13b055ebe6d16f0edfe7cd1c54802372 ================================================ [File too large to display: 210 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/numeric_literal/0699989c219e1d7b336851c646e88a651859d081 ================================================ [File too large to display: 2 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/numeric_literal/06be63f8a856b3c2ff1e4f6456ae2a598dcdfe59 ================================================ [File too large to display: 41 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/numeric_literal/0733e1eab32796fc80c49e6a1b83b767d9728825 ================================================ [File too large to display: 5 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/numeric_literal/082fb8b49e412ef6a8c8946b149c9b19cd6e2837 ================================================ [File too large to display: 128 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/numeric_literal/0842360c9209267aefbc4bcafc6a14821257f942 ================================================ [File too large to display: 6 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/numeric_literal/0b7093469bb6c349a4a730b484bc99f8755a3cb5 ================================================ [File too large to display: 265 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/numeric_literal/0bbd8b6c814360f7eb032d8e921fb916f3af643a ================================================ [File too large to display: 124 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/numeric_literal/0c2730c2230eb2beaf907d1feb4b754a1d9fa322 ================================================ [File too large to display: 132 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/numeric_literal/111f083d72dc69da5c2df23c8142b96fde044fc3 ================================================ [File too large to display: 22 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/numeric_literal/1213ea480fd8224227d3016271c7dd8c96d839cc ================================================ [File too large to display: 218 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/numeric_literal/1245401b55dbe86b0881a84e17b7ad5424557a6c ================================================ [File too large to display: 3 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/numeric_literal/13c1daecff92125665a018520dbd23f495f7d21b ================================================ [File too large to display: 96 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/numeric_literal/14892954f44819d5064f3b2efeef3d59877956b1 ================================================ [File too large to display: 30 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/numeric_literal/1496a76414a61fd6d77fb52acf040b2176890a9c ================================================ [File too large to display: 6 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/numeric_literal/14a9830e5443add35bee80d7b59e56ed2ed58231 ================================================ [File too large to display: 6 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/numeric_literal/156aee9179b8f39e73332a9b59336a97d1526c97 ================================================ [File too large to display: 2 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/numeric_literal/164585b5e3297b62f49428fc82661b3d1a6bd524 ================================================ [File too large to display: 8 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/numeric_literal/17675e27a67f6ed960be339c6784fa902ffb568c ================================================ [File too large to display: 17 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/numeric_literal/181bd138b714c47c705d218938274a6ff2c0315a ================================================ [File too large to display: 2.1 KB] ================================================ FILE: toolchain/lex/fuzzer_corpus/numeric_literal/1a349dcc540a3978584510d982075f838b17cd6d ================================================ [File too large to display: 2 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/numeric_literal/1d5872c3a5cc3ac317e3611c7167eaa142f2d63c ================================================ [File too large to display: 129 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/numeric_literal/1d6b5255a8715ae63cd18e2b238f889f5a6a3dab ================================================ [File too large to display: 7 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/numeric_literal/1ddd636055b4b62ed55bd9c5406b98508df65094 ================================================ [File too large to display: 4 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/numeric_literal/1e9be73c8fc8acf59e2fd87e8a659b77fb8e1420 ================================================ [File too large to display: 16 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/numeric_literal/2047371f2b0343b6e64f40fb260c09b2037847e2 ================================================ [File too large to display: 4 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/numeric_literal/20602f5b4440f20ea21ced88bddcf8ba073ddac3 ================================================ [File too large to display: 166 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/numeric_literal/22cf82b68b95049bffb91128349ccc312a460b10 ================================================ [File too large to display: 4 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/numeric_literal/2368a873bce5809ef902426b152742363bc21800 ================================================ [File too large to display: 22 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/numeric_literal/25cc5d59a72a1c274cf218d947dafcde63c6c7b6 ================================================ [File too large to display: 128 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/numeric_literal/25f06d4d4f5e0a73147b3b758bf85754b40bd6f8 ================================================ [File too large to display: 84 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/numeric_literal/2b991489843e1d4b3960a9c905cc5e63dcb98768 ================================================ [File too large to display: 132 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/numeric_literal/2c4224570fe8a21dec1bafe6520a7ec5619b7511 ================================================ [File too large to display: 236 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/numeric_literal/2d1e7879ec3e332fe432986620d4f4648378473d ================================================ [File too large to display: 6 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/numeric_literal/2d7bd1ddf796ff4bbb5efed75ad66e55489780fd ================================================ [File too large to display: 268 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/numeric_literal/2fa6402156da7aed557f89e02fb9b8bd0a8bfb6a ================================================ [File too large to display: 38 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/numeric_literal/2fcefe6a06ecee3472758a9f259b7456da40cc8d ================================================ [File too large to display: 219 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/numeric_literal/2fd76b1e86feae72db5813fa27e0760a607a13f2 ================================================ [File too large to display: 130 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/numeric_literal/320cc080c41a4f5846cd740e82f007645010bfa1 ================================================ [File too large to display: 11 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/numeric_literal/3286cf588786f87fef481c7ee465dc60eee4391e ================================================ [File too large to display: 16 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/numeric_literal/32bb601f3f3aa9a3ac30ee4ad2138c5143251714 ================================================ [File too large to display: 3 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/numeric_literal/334200ab9eb04bd01cb44a21e16306f3e8de12be ================================================ [File too large to display: 6 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/numeric_literal/356a192b7913b04c54574d18c28d46e6395428ab ================================================ [File too large to display: 1 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/numeric_literal/36076c28fff33bea1867304432420ed685d27b91 ================================================ [File too large to display: 263 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/numeric_literal/362091df7a6a4b38be6f958d21f4cadc6fea5e8a ================================================ [File too large to display: 257 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/numeric_literal/37251be083d7f99af9e1c7a4f11936f2935f0b17 ================================================ [File too large to display: 4 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/numeric_literal/38d829e423d480cd24a1b83513ea6d9424627212 ================================================ [File too large to display: 87 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/numeric_literal/38f6d7875e3195bdaee448d2cb6917f3ae4994af ================================================ [File too large to display: 3 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/numeric_literal/392d029d257986310089d500b1d4521818f45cd5 ================================================ [File too large to display: 66 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/numeric_literal/3939dac890fba2eb21814e82eb539d6f7de9da07 ================================================ [File too large to display: 34 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/numeric_literal/3bfd922772e61d1c51ad7a3c0d6db7e686b70859 ================================================ [File too large to display: 5 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/numeric_literal/3c79ec3deaaa87395a6955a8f39be784643d52ea ================================================ [File too large to display: 7 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/numeric_literal/3cf5e1f649a73e69b4ee05c62da8e0d5252823df ================================================ [File too large to display: 118 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/numeric_literal/3e140e6d8d719ce82a88adbcbf3668e890b8d025 ================================================ [File too large to display: 9 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/numeric_literal/3e754b50d7ed4402123e6ee1351344d67b834918 ================================================ [File too large to display: 6 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/numeric_literal/3e9be1b258c67169e262e993f89656cb27c3da9c ================================================ [File too large to display: 157 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/numeric_literal/3ed54accbc161980c788a8951426f1d5931db238 ================================================ [File too large to display: 127 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/numeric_literal/3f675324a715fc02cf00afb8c485eadfba257aa3 ================================================ [File too large to display: 44 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/numeric_literal/3f9f95592f43e9c035a6a53f0c32026eed0d299d ================================================ [File too large to display: 13 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/numeric_literal/3ff2c6b37ae1c0fcc3cdd04a35f9d8e532fa7d20 ================================================ [File too large to display: 33 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/numeric_literal/41901ac30f9e7e68c32ba753436e9c5bc668d3c5 ================================================ [File too large to display: 67 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/numeric_literal/41add85724d3b9bff3d877a949acb2fc93fd76fc ================================================ [File too large to display: 4 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/numeric_literal/449812e74454e863ea558e7ab9e17cbe64572e97 ================================================ [File too large to display: 39 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/numeric_literal/45c1e6525e6d1b2496ac19b3064245b6473a49fc ================================================ [File too large to display: 85 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/numeric_literal/46448068fa6276bb5f7c4f56066fb34ece186c66 ================================================ [File too large to display: 36 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/numeric_literal/489a73b15ac1c4dffab102dfe6c109792a7e1dfa ================================================ [File too large to display: 1.0 KB] ================================================ FILE: toolchain/lex/fuzzer_corpus/numeric_literal/49237f66b40eaad2c23d8be346fead4c2a004636 ================================================ [File too large to display: 34 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/numeric_literal/4a095aef5824cd0a13c8706ff870a6ef2f3dd399 ================================================ [File too large to display: 88 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/numeric_literal/4e52ef8facabaeead3d4e12b91b0f4a4ec102732 ================================================ [File too large to display: 8 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/numeric_literal/4ff447b8ef42ca51fa6fb287bed8d40f49be58f1 ================================================ [File too large to display: 1 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/numeric_literal/51711e08538ddc6d50e7c3978cb2a00f50a5a305 ================================================ [File too large to display: 4 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/numeric_literal/52cfe1fb5fda21b7f088d637a41e5de1592fb2e0 ================================================ [File too large to display: 4 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/numeric_literal/550312ca5140e036ac9d728db3d44df0991a4d88 ================================================ [File too large to display: 49 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/numeric_literal/55cab295e6fb717b173702eed50ffbead9de2957 ================================================ [File too large to display: 5 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/numeric_literal/5667a766c3d9fdf13333fd6c5b6fdafd3cebb442 ================================================ [File too large to display: 77 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/numeric_literal/57c1c5becc3cf566581f06378dfe1987172dad7d ================================================ [File too large to display: 6 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/numeric_literal/58409e6b84395163cdbf63ba09ad2231527adedb ================================================ [File too large to display: 130 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/numeric_literal/5941347f0af754e5f70069077c4e87509cefde58 ================================================ [File too large to display: 6 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/numeric_literal/5cc4fc2171135fefc805291872a391ee7e35c015 ================================================ [File too large to display: 4 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/numeric_literal/5d870fa1cb981790514473e2a688fc61f0150307 ================================================ [File too large to display: 97 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/numeric_literal/5dfa4702490bf06ce54b742ebab131c459e21e8f ================================================ [File too large to display: 9 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/numeric_literal/5e0218964ab8a1d04b56e9b4e798729ce5499909 ================================================ [File too large to display: 4 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/numeric_literal/5e4d4905d69735de1b7fb0101dde0f8e4d17d536 ================================================ [File too large to display: 75 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/numeric_literal/5eb55dea99ee665801b7e3725a784a11ed846372 ================================================ [File too large to display: 7 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/numeric_literal/5efd0f0af5b1f1e870f78a2192c5f03bdca52d25 ================================================ [File too large to display: 182 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/numeric_literal/5f773135f0a7c8209d25b3297730293d3e238753 ================================================ [File too large to display: 211 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/numeric_literal/5fc5e3ad16ffb81cd87a08e3ab245390e6eb94e2 ================================================ [File too large to display: 7 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/numeric_literal/613cf0e439d1968b62526744b596b6cd832e3443 ================================================ [File too large to display: 9 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/numeric_literal/61b9f39ee191e759ef0ad6fd735396bfddb2cbf2 ================================================ [File too large to display: 51 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/numeric_literal/6240d35f66f3994f2007b36a162283da33bdf0b4 ================================================ [File too large to display: 9 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/numeric_literal/62db736f788068332552999e2042e8df794b798d ================================================ [File too large to display: 5 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/numeric_literal/6330f177b9d4ffc57bacb9009083de56d3a9fdf1 ================================================ [File too large to display: 42 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/numeric_literal/637a6f32cb071a302042abe613d71f135baf0c5e ================================================ [File too large to display: 264 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/numeric_literal/6417ac8b13eaa831f05ca81c8b289a4627d0635f ================================================ [File too large to display: 36 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/numeric_literal/6477186c0f869c7a5b1c91e08010767ecf61c68e ================================================ [File too large to display: 11 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/numeric_literal/666d79489b330d37cd5360ce87f3378a19c368d4 ================================================ [File too large to display: 4 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/numeric_literal/66c382031cc45a841fc9284e1c29b46ef4b7d8e6 ================================================ [File too large to display: 4 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/numeric_literal/66cf30ceb3ebffae51ec04fe54d14f0e1da764d0 ================================================ [File too large to display: 116 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/numeric_literal/684a3fef4c5504bd7c8ee2bb7d417e93d2ad80d8 ================================================ [File too large to display: 7 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/numeric_literal/6afec1f1f70abcf1cbae35977b37c2bc048b4f17 ================================================ [File too large to display: 287 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/numeric_literal/6b453b65ac16f51d4166e1bc52c24281a945385e ================================================ [File too large to display: 9 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/numeric_literal/6bec19b44bcdac7bca8bc9b10925d2cdc754175b ================================================ [File too large to display: 2 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/numeric_literal/6d45ff4bfda8922cdbd9117551d7479ef3bd9adb ================================================ [File too large to display: 520 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/numeric_literal/6e1eec8ed109fbdf7c12458769ea0c002cdeac56 ================================================ [File too large to display: 18 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/numeric_literal/6ef7576d39b7bc259e00aafe441718f698e3cd87 ================================================ [File too large to display: 5 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/numeric_literal/70384f05c971ac3ff0f4ebd9fa2d2f3d82b4993d ================================================ [File too large to display: 6 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/numeric_literal/718fce6b8f3937863dd947bc9c188136d05d7130 ================================================ [File too large to display: 5 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/numeric_literal/7343e86dc760c4b6e623d1646c6e82b378ba24b0 ================================================ [File too large to display: 112 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/numeric_literal/739d7debab4eb975e3b5aa91ed2b965948caf457 ================================================ [File too large to display: 4 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/numeric_literal/7540797d21454f5609272245c05b3981be33b7aa ================================================ [File too large to display: 111 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/numeric_literal/75e22f6c35544f9a4760dd136ee93d7c5f2518d4 ================================================ [File too large to display: 262 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/numeric_literal/76a6acc2af2d920a632b7addec28fbb42d6227f8 ================================================ [File too large to display: 134 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/numeric_literal/7715d946c6d57bc306d04052c49369f0afb2ac32 ================================================ [File too large to display: 6 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/numeric_literal/7744609c9961fca0f8d55e9c7616509e9e3df808 ================================================ [File too large to display: 7 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/numeric_literal/77c782bdb9521dbe6eb7682493fcde89ce762f44 ================================================ [File too large to display: 8 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/numeric_literal/7ae756e9681dcdb739fb1ae0e30a3bf8043fe60c ================================================ [File too large to display: 5 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/numeric_literal/7b41852284e3035d7a807d1769524a4514295373 ================================================ [File too large to display: 6 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/numeric_literal/7bdb26daf76ed012b61a65e0b6d521f0b34e55f6 ================================================ [File too large to display: 4 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/numeric_literal/7dcc9c53752ca6a800dc7dda472a0bd2ed09e7c4 ================================================ [File too large to display: 5 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/numeric_literal/7ef0a8d8149342fa93469f228609bc52508e4396 ================================================ [File too large to display: 6 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/numeric_literal/81304e0dcfb8fb2fd1ba2309d416ffe1f38f2a3d ================================================ [File too large to display: 36 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/numeric_literal/8149371e9bcb20ef99f926fe20c176cafa676285 ================================================ [File too large to display: 13 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/numeric_literal/814a3876b8285a2bcbecd99a201cf1af49e6b11a ================================================ [File too large to display: 154 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/numeric_literal/832dcee787c4ff526198f707d0d8e16b129767c2 ================================================ [File too large to display: 256 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/numeric_literal/83f542ffb794bd569945866aaf8e37228669a701 ================================================ [File too large to display: 90 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/numeric_literal/849af41229745fd6e9840d69bf6edd7079a311fd ================================================ [File too large to display: 7 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/numeric_literal/8500486975fcce132a3ba3e1a20f8de7b29508a7 ================================================ [File too large to display: 44 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/numeric_literal/85eef37d8dc68b042afa0b50e172897464034a74 ================================================ [File too large to display: 150 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/numeric_literal/85f3830484d7409a99017e899e2399f7985af94e ================================================ [File too large to display: 80 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/numeric_literal/87a2b80f9272583517c0207af176fc40ea55022c ================================================ [File too large to display: 2 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/numeric_literal/89608cabe045206c25384b6785aa92daf7c171bb ================================================ [File too large to display: 6 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/numeric_literal/8c954006e2cb2f5ffbc20dfbcbb7fe404be4ba5c ================================================ [File too large to display: 65 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/numeric_literal/8d5c212a40a328da7565d7bbd5fbe7517538ee3d ================================================ [File too large to display: 3 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/numeric_literal/8dceb2a8bb9a98ee0fcfe9a89d3a3ff3fdce414a ================================================ [File too large to display: 255 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/numeric_literal/8de1f2c13c48532f772061efda9edb38fe26c0a3 ================================================ [File too large to display: 42 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/numeric_literal/93221a7273be83763f9dd883946978eb0935e762 ================================================ [File too large to display: 18 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/numeric_literal/93b713025575996366c633ef8cea8fbdc737d8d8 ================================================ [File too large to display: 266 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/numeric_literal/950b8c9dd09799288af9d11ff958ed2e63d72005 ================================================ [File too large to display: 3 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/numeric_literal/95d9c8b6e3bf3de68dd56743596c43f819e818de ================================================ [File too large to display: 34 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/numeric_literal/99ce00d43f9fc7818a4f3423b7b8289d0bb87234 ================================================ [File too large to display: 66 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/numeric_literal/99df4151dcd9f163434a0b284c10936af0f4e5fb ================================================ [File too large to display: 92 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/numeric_literal/9ac784b1c686635d12495b0ed4a25029f0baaa9a ================================================ [File too large to display: 20 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/numeric_literal/9ae0a713e4c39c47492f77de7102534a7b96aab6 ================================================ [File too large to display: 156 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/numeric_literal/9bcb018977812159c15a834f373ac135b782a10a ================================================ [File too large to display: 220 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/numeric_literal/9d27e500ee25dbdecb9fc4680812eb96e48f1d2d ================================================ [File too large to display: 34 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/numeric_literal/9d41a9818e211ee98ac596495c1124f6696d09fc ================================================ [File too large to display: 66 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/numeric_literal/9d51f1423722c5e8aa4d107865e10d293a1fa140 ================================================ [File too large to display: 133 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/numeric_literal/9df9f3ac537c579ad5b0aceef46a2dcce31037de ================================================ [File too large to display: 17 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/numeric_literal/9ffdd9130c8e506c54bb4631080871fc1c7ba88f ================================================ [File too large to display: 19 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/numeric_literal/a1422e6a168630cdd214ac5e31ca01ae1bee8d92 ================================================ [File too large to display: 3 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/numeric_literal/a2eea62736951c8d78626f741bfbfc51bc6c9bd2 ================================================ [File too large to display: 5 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/numeric_literal/a41c607d984aec3d796569da2b55dda8f0048b0c ================================================ [File too large to display: 266 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/numeric_literal/a574380880b8038ee74cdb44cb7aa7f2b6617186 ================================================ [File too large to display: 33 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/numeric_literal/a5c4ec5775088e87a334b664e6e8e86dd7e10130 ================================================ [File too large to display: 6 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/numeric_literal/a6c9027065d9ae3dc8f1b1b880d5f1775240ca0c ================================================ [File too large to display: 40.5 KB] ================================================ FILE: toolchain/lex/fuzzer_corpus/numeric_literal/aa8250319828b4163695c726ea6e69cf4c140e30 ================================================ [File too large to display: 18 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/numeric_literal/aae7c0fb777f220875f1fe54665d0fda9b03890e ================================================ [File too large to display: 6 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/numeric_literal/aaf78b268bc0226a03e73e2bf184a61734c8aa0b ================================================ [File too large to display: 218 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/numeric_literal/ab3baefd06064b7a6137cb2893456835abcaa118 ================================================ [File too large to display: 10 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/numeric_literal/ae367cc8cabac896debece6700e842aa85c40b78 ================================================ [File too large to display: 135 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/numeric_literal/ae80bd232c1ebf8afacc37405c2a4505f475e2aa ================================================ [File too large to display: 35 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/numeric_literal/aeced42b612c49741b262e34706c1de01eaf97e4 ================================================ [File too large to display: 139 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/numeric_literal/af31568f6b0df0b3338588f29c0eb4460a99b69c ================================================ [File too large to display: 3 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/numeric_literal/af8720cb64bd570dc7e6d9c68481efe81aed22e0 ================================================ [File too large to display: 23 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/numeric_literal/b108a36270fd0835a7be248d5fd4a85a5250a7dd ================================================ [File too large to display: 2 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/numeric_literal/b419ff42ac4a0f2b5d3bc8979d5aba98d79798e8 ================================================ [File too large to display: 8 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/numeric_literal/b49182a7be9de06418c6af2c3f09a79e4ec4197a ================================================ [File too large to display: 98 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/numeric_literal/b61f36e968b85c99a3dfd19b9932595e782b8913 ================================================ [File too large to display: 16 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/numeric_literal/b6589fc6ab0dc82cf12099d1c2d40ab994e8410c ================================================ [File too large to display: 1 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/numeric_literal/b70cfeaa4a7ef5329e8ee37c5015ca3b648c450c ================================================ [File too large to display: 65 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/numeric_literal/b86fb9c87e493ea5af6004126150feb9bdb825c9 ================================================ [File too large to display: 7 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/numeric_literal/b8983fc9befbd305e19d529db1c66ad18320ac69 ================================================ [File too large to display: 9 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/numeric_literal/b8d80dd9af3eb7eb136f1d14a07b1c528f13e911 ================================================ [File too large to display: 10 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/numeric_literal/bab0d1528dc42ded1936a436f20afba10014f891 ================================================ [File too large to display: 319 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/numeric_literal/bc1ac6a8f86333e9fd372331e6d3f444be735402 ================================================ [File too large to display: 80 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/numeric_literal/bc977149aaf6eb3a51c1769e8cb19901b1829e92 ================================================ [File too large to display: 5 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/numeric_literal/bceea7f1cd74bebe2ce08e43dac9d261dbf18f91 ================================================ [File too large to display: 3 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/numeric_literal/bdb5dc3199e5448ada3fbdfe4c8af7943cbd235a ================================================ [File too large to display: 4 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/numeric_literal/bebc59f212a559756b4bbc3faba4ae10d315d497 ================================================ [File too large to display: 26 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/numeric_literal/c23f7da7070511444ebc75875fd9d202b5dd13cf ================================================ [File too large to display: 4 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/numeric_literal/c2e6e1aee1616fa868685dfabbcf5291438f9747 ================================================ [File too large to display: 5 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/numeric_literal/c2e71fd65bf2dd7fdfc5c0690c27b7fcd7ad1194 ================================================ [File too large to display: 156 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/numeric_literal/c2fb7620971c2d676b0aa62c0e1bbecd80b2d9be ================================================ [File too large to display: 35 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/numeric_literal/c368c154d2fbc8d41e38012e9a506bdc6cb36b3c ================================================ [File too large to display: 263 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/numeric_literal/c3d43e895d5403308b1f5d20ac68089f278f58bb ================================================ [File too large to display: 93 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/numeric_literal/c4d9aa3c1b056db569ee53f2a8576eee1648a803 ================================================ [File too large to display: 28 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/numeric_literal/c596925beec028955b43a195888e284ac19f62a1 ================================================ [File too large to display: 37 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/numeric_literal/c98cd4991b593a1979150edef362454ba06bc5a5 ================================================ [File too large to display: 9 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/numeric_literal/c9d3ec0e53562b12d9b1e081ed9511ff14dc7925 ================================================ [File too large to display: 130 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/numeric_literal/cb7064c4c88c44784df84f6babb078c0f8804741 ================================================ [File too large to display: 13 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/numeric_literal/cd949405982c4dc60814478e4aa7d4a3f0428479 ================================================ [File too large to display: 133 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/numeric_literal/ce201031c9ef369ba2bfe7d52b0fcc1dca0b13b2 ================================================ [File too large to display: 5 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/numeric_literal/d0d3d231e7108e8fa61e4e591fa559ecf2792a94 ================================================ [File too large to display: 11 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/numeric_literal/d1188e7f3438ba7c3ff7191398ecc2efa9470957 ================================================ [File too large to display: 33 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/numeric_literal/d1c2104e3492dc81240c303b52b64fdb0aa8f827 ================================================ [File too large to display: 6 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/numeric_literal/d208b7c3d0653df162f4ccbbadb15ec7dae74ab8 ================================================ [File too large to display: 19 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/numeric_literal/d48c09534c35bf5ce0cc6c64a6031723d0906bdd ================================================ [File too large to display: 106 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/numeric_literal/d48edc137f3088ed35681ccab30161061b3feb90 ================================================ [File too large to display: 17 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/numeric_literal/d5b7f37a21c4af8b3c102f969881e4ee39cc6a8f ================================================ [File too large to display: 94 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/numeric_literal/d625fc319a744dd916251e56a472cfef5a98f0b2 ================================================ [File too large to display: 8 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/numeric_literal/d650151dbb711eab8630ceaa36a4f8b1a9e39df0 ================================================ [File too large to display: 247 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/numeric_literal/d69e58e80c7e82f3fdb59db0191087c54f79089f ================================================ [File too large to display: 32 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/numeric_literal/d7f82bce8bd35cf64667f447a2138afdb496da3b ================================================ [File too large to display: 9 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/numeric_literal/d9ac7b8dc973d67c518cf931bdf42ad08cf561a4 ================================================ [File too large to display: 40 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/numeric_literal/da3293ce5d82067c6f11257981823849169f3403 ================================================ [File too large to display: 150 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/numeric_literal/dab0d12291e4cbd77a94f279ba1cbb1672d55c1c ================================================ [File too large to display: 17 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/numeric_literal/dbb93daa94c24b650e09cc72a7e56097e22fff84 ================================================ [File too large to display: 8 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/numeric_literal/dbd01297d254f7fa305219e2aa1a369ea7a22527 ================================================ [File too large to display: 70 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/numeric_literal/dc400bc8f31a8bb2593d4c28112e785e29d0f38f ================================================ [File too large to display: 269 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/numeric_literal/df984f06ec102745c15edefd29f33eda2a1c85a0 ================================================ [File too large to display: 33 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/numeric_literal/e38b56ceb12230a32054d611e0d1f7c056eedf6b ================================================ [File too large to display: 314 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/numeric_literal/e526dcac9c6b2eab2ef2ca472a99963347e2f87e ================================================ [File too large to display: 269 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/numeric_literal/e57269fa24e5a40f2292b37089d4366cec35c35d ================================================ [File too large to display: 233 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/numeric_literal/e57c8da00d97212eea169f7dadd281722334acd0 ================================================ [File too large to display: 263 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/numeric_literal/e6e85f599f4562e2d1397d023628b65965fa14d7 ================================================ [File too large to display: 33 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/numeric_literal/e79e527bbcef0c4e5e267131063d6866232cec3a ================================================ [File too large to display: 3 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/numeric_literal/e860ae2a7d700d40bd3bdeb7caca2b446ee824ef ================================================ [File too large to display: 25 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/numeric_literal/e889b88d53a1a45d01232047189825a7747e8c45 ================================================ [File too large to display: 27 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/numeric_literal/e8a863ef5127ed01bbba73449a6c9de20125812e ================================================ [File too large to display: 12 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/numeric_literal/e8b113846dd4cbe61e80e5f750280a16dd37c863 ================================================ [File too large to display: 118 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/numeric_literal/e922ba00132e742d435c6caafed22be03024e3dc ================================================ [File too large to display: 2 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/numeric_literal/e9a20a4af61c91b0232679bc583058b278fb2926 ================================================ [File too large to display: 4 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/numeric_literal/eb111d98d9cc8e8f4589864319f6c67c0da320d1 ================================================ [File too large to display: 130 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/numeric_literal/eb393579a5c6d84365c6c3e95d2de9ed10054d31 ================================================ [File too large to display: 4 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/numeric_literal/eb8edaf4c4c13ac51d85c39ff93366be8e4be7cb ================================================ [File too large to display: 3 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/numeric_literal/ec7f371c0fb5c2faaaaa23dc6fcdc99283489ba7 ================================================ [File too large to display: 6 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/numeric_literal/ee5f2d15733b40d12b0774af406c78a45ebe6148 ================================================ [File too large to display: 5 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/numeric_literal/eee6b2d3ea106e614f0491d195c8c8c192c85bf8 ================================================ [File too large to display: 32 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/numeric_literal/efba95a85c4192602fa2c3659143c92b571a5c94 ================================================ [File too large to display: 7 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/numeric_literal/eff4911030d04500307ffe2e0fded422fe1ae75b ================================================ [File too large to display: 30 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/numeric_literal/f08ed337a7f1ab963f1209b7848d84221b563f08 ================================================ [File too large to display: 132 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/numeric_literal/f0983d0151f1e21eef6430bbc8547242a477416e ================================================ [File too large to display: 10 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/numeric_literal/f197ac3297a72d1413edee2426e7f38917b45423 ================================================ [File too large to display: 5 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/numeric_literal/f1abd670358e036c31296e66b3b66c382ac00812 ================================================ [File too large to display: 2 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/numeric_literal/f1e14bac6731ce7bd468043a41efdf7b7254305e ================================================ [File too large to display: 46 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/numeric_literal/f25de8b0eaf377a3d96af8469ecd618aedd0cf9e ================================================ [File too large to display: 4 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/numeric_literal/f53f19616fe3d835283bfd6d318c604991b66c26 ================================================ [File too large to display: 147 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/numeric_literal/f57c45a511ae9289677ef448b07003946db78212 ================================================ [File too large to display: 141 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/numeric_literal/f5a1859ae6b51feea5a485d0d8143439f1a9991e ================================================ [File too large to display: 4 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/numeric_literal/f5df3991f088f7a6710535dcae3250052b786763 ================================================ [File too large to display: 5 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/numeric_literal/f5e4d64b7ba71c108572d4f0785e91bbe427512e ================================================ [File too large to display: 67 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/numeric_literal/f633db7e95d71992c0d246a910b6ef0c4f848dc0 ================================================ [File too large to display: 3 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/numeric_literal/f7185e0cf7f84c726e60c907e24f28954b68b8b3 ================================================ [File too large to display: 2 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/numeric_literal/f7486ae138c2b85cb854545476d92e7a0373ef83 ================================================ [File too large to display: 5 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/numeric_literal/f79cc4c8d9f8f447188eb98a03b6941c4c84947d ================================================ [File too large to display: 43 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/numeric_literal/f7a2155a1adc314c9df71252e9b5f6a6f750ecd6 ================================================ [File too large to display: 11 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/numeric_literal/f8de2e14b709554c407425de229e30296a529b80 ================================================ [File too large to display: 8 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/numeric_literal/f9610fa79c299e70839877aee63f39e96972a16d ================================================ [File too large to display: 17 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/numeric_literal/fa1d66dd261334900916180b83c948551f52275e ================================================ [File too large to display: 145 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/numeric_literal/fae00ad0f90febb0e1f8862e3b72edcf30be61b8 ================================================ [File too large to display: 5 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/numeric_literal/fb96549631c835eb239cd614cc6b5cb7d295121a ================================================ [File too large to display: 2 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/numeric_literal/fbd4a4ffc26cdf139663aed3057eedf51ce790f6 ================================================ [File too large to display: 8 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/numeric_literal/fcfe7aa00d7b88df2566fb89ac0d93e5b95f1251 ================================================ [File too large to display: 532 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/numeric_literal/fd08016e47acfabcb0032079b3243a3ba8aeec41 ================================================ [File too large to display: 5 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/string_literal/0376b433f55d2717b9d41f2b8138c50c46b2aae8 ================================================ [File too large to display: 51 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/string_literal/04c810f17ef88440680ff21b94aa2f8700f3bf1a ================================================ [File too large to display: 10 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/string_literal/06b234fddbe40ffc81affde9f21a7e5bb85958dd ================================================ [File too large to display: 28 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/string_literal/06fc68871d6c253e93ed33c7490417567a5397cc ================================================ [File too large to display: 31 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/string_literal/096d488c0e9abe2cd0e12bd2c39fb44301c8be8e ================================================ [File too large to display: 17 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/string_literal/099f3667fe4a3a217aa1dd17de0925690fb78bec ================================================ [File too large to display: 202 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/string_literal/0aa91ba10de9207476869788f14113120bbcd217 ================================================ [File too large to display: 16 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/string_literal/0b262e8f731e0757184f3d79722b87a0698c41a0 ================================================ [File too large to display: 17 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/string_literal/0b8ef897460cd34ddb05e34db985ce877f3d644a ================================================ [File too large to display: 6 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/string_literal/0d48eef2e43ee941fd2a921b1ffc2c9a960a03b1 ================================================ [File too large to display: 11 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/string_literal/0d8a52a207c1d24dada33edbf6c4c002a3bb5162 ================================================ [File too large to display: 6 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/string_literal/0e0f56266d53af7193855295e4474252bddd42b2 ================================================ [File too large to display: 55 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/string_literal/1144a2cfdfd3882ae4a12453ae570ed5ba9d68bc ================================================ [File too large to display: 11 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/string_literal/11823356e0d93c50ab5165897d16322b08e41ecb ================================================ [File too large to display: 8 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/string_literal/118bca83c4556dba2b8aa7eb11775454d99c7f52 ================================================ [File too large to display: 22 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/string_literal/1202a87fd052064983e01e897b34808f3ff6e7cd ================================================ [File too large to display: 8 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/string_literal/1207d0aceee2266d0619757d2c21bcde11b3ed61 ================================================ [File too large to display: 12 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/string_literal/14321e2ca435f16798a6c7b0ada316d4c298328d ================================================ [File too large to display: 4 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/string_literal/156f8635b11c9313551c85d49e008478e42dcce1 ================================================ [File too large to display: 16 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/string_literal/1580d4c19a6fbcdaf475fff8a4a5b7b007f1dd45 ================================================ [File too large to display: 10 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/string_literal/168b94a27056b46bb49207944a088ea147b01875 ================================================ [File too large to display: 6 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/string_literal/19ed681682ad5f130c19b58afcf93cd51ba6681b ================================================ [File too large to display: 10 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/string_literal/1dec96b1cc5b85730ef22ef6746bfccf6a285e54 ================================================ [File too large to display: 42 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/string_literal/1e7add2f35b6f74817a36dffade4d52d0a783e92 ================================================ [File too large to display: 18 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/string_literal/1eafed7ea98652650f95f073f49d6664ebdcb3a1 ================================================ [File too large to display: 8 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/string_literal/1f26d0df21008fefad883dbd3fdb7c32a68f4b3e ================================================ [File too large to display: 114 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/string_literal/202d546ab2df1c12c5e463cfd5d52357d27ee56f ================================================ [File too large to display: 16 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/string_literal/20b40ff2624a56b681d03737e2cfea7ed545726b ================================================ [File too large to display: 3 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/string_literal/20f5eb26a89cd1c5d84e325d17e8200f9692ef74 ================================================ [File too large to display: 131 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/string_literal/25b94b337518f8634eb71e7bbb2997de9d1cdb94 ================================================ [File too large to display: 9 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/string_literal/2660cec736ab868f3af22f5a38d83b46f4ec9a2f ================================================ [File too large to display: 42 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/string_literal/2795191870e070dfac97a76ab2225078d9d39e4a ================================================ [File too large to display: 6 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/string_literal/2a6db340a61cb534f3db621b68674bd28deb7bac ================================================ [File too large to display: 12 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/string_literal/2ace62c1befa19e3ea37dd52be9f6d508c5163e6 ================================================ [File too large to display: 1 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/string_literal/2dad69592eaf5c29fb6f946ea30345b94d4f0f46 ================================================ [File too large to display: 19 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/string_literal/2dc0d48e579bdf90ab944f87745559511e8330dd ================================================ [File too large to display: 11 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/string_literal/2f381733d9cddec878047164d26e926e22a9da9a ================================================ [File too large to display: 2 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/string_literal/3206a6a100fd709e5bfd6938b604f539089a6947 ================================================ [File too large to display: 44 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/string_literal/32b92c4a39dd4919ea65e6c8f6e797a38d6d2261 ================================================ [File too large to display: 36 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/string_literal/33b21ecc4cf191463b613684e4c3aa5f94f421b4 ================================================ [File too large to display: 850 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/string_literal/36436a22ce2c601dc2c13366fe59735c7ac36dba ================================================ [File too large to display: 8 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/string_literal/36973b3723dd44ff93e859fa66f7d91b80890fae ================================================ [File too large to display: 6 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/string_literal/37dcc3879b12d50e7a7b33f0fb825662a291ccc4 ================================================ [File too large to display: 3 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/string_literal/38eee8ead800a4f44d308c1f38d3505e2b7243c1 ================================================ [File too large to display: 137 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/string_literal/3af7aaa16a9328b8d00a75d2dd515fa7ac444502 ================================================ [File too large to display: 10 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/string_literal/3b7a06bb1102f7d788e3167ff05a9b20da93212e ================================================ [File too large to display: 2 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/string_literal/3b9535ea11e068bf92fc4bfc7ff57c47b815b9c0 ================================================ [File too large to display: 13 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/string_literal/3bca58822768446e54453af8ec624c4b48dc5095 ================================================ [File too large to display: 7 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/string_literal/3c05ef50a60513cedd1842a06c01bcfdc0323b1a ================================================ [File too large to display: 9 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/string_literal/3eee55022df10643b2e79632fb724ffa891e3d91 ================================================ [File too large to display: 16 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/string_literal/4087566889f6007e2e8770c94e1ebff6ad32bf5f ================================================ [File too large to display: 6 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/string_literal/413300f2a1d4692c7fab0112511a2411f465bc4e ================================================ [File too large to display: 11 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/string_literal/4150c8dc06ecb3f620577622b439d38425d51c78 ================================================ [File too large to display: 15 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/string_literal/44cd53e8e58df6df2fbc9a46f4a3c13ff8f09200 ================================================ [File too large to display: 19 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/string_literal/456ed7542e7eec5ebba7517e1b435578c32e2aff ================================================ [File too large to display: 4 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/string_literal/484640d261434a02a5091ebe283a9aaa2bc6bb95 ================================================ [File too large to display: 8 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/string_literal/499108a16874f242f9341089cad86162b8c03072 ================================================ [File too large to display: 25 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/string_literal/4b12ed448db88ad4c3d2f0d0737cb665b5b824b7 ================================================ [File too large to display: 14 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/string_literal/4dab43cb12e13a7224adf4dba343bdbab887f74b ================================================ [File too large to display: 5 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/string_literal/4fdff4cb139acc2c25fd2dfdd0dc19f5afbe42ab ================================================ [File too large to display: 8 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/string_literal/5056c7a8bcedf92a1b27c07c813dc282bca1d06c ================================================ [File too large to display: 9 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/string_literal/506825d4376c705a90d5811c7cac100db436efd8 ================================================ [File too large to display: 83 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/string_literal/510fb3639791c534198374f7423bfa9791774d28 ================================================ [File too large to display: 7 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/string_literal/5132c55dc1641aaab606231181d9896bf0154f7f ================================================ [File too large to display: 9 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/string_literal/531edc565c3e984aa9320a413dc471e68aebfee8 ================================================ [File too large to display: 59 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/string_literal/55e65784b6e7a55c2a8a46bb7e06e2bda659e629 ================================================ [File too large to display: 28 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/string_literal/560927416d9f52ac4c2cd4704c79f3595eb75dba ================================================ [File too large to display: 14 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/string_literal/595bd3da9cb8c029f3a7848eef7a2b8164f3cf3b ================================================ [File too large to display: 8 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/string_literal/59d6888e8e389be5174eb7269b457824da4521ef ================================================ [File too large to display: 32 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/string_literal/5bbf3bfcd7c31e9285dd2324fac77e0d7322e452 ================================================ [File too large to display: 9 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/string_literal/5e7a76d4a38b352010c4a8c8ad4aa461f6218793 ================================================ [File too large to display: 4 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/string_literal/5f28eb629ea31547978309b712407cf5f6538095 ================================================ [File too large to display: 7 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/string_literal/5f422d74bc3008c571d0abe58eeaf7ba3feda93b ================================================ [File too large to display: 25 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/string_literal/60b668789254f2427fd51d1bc30da5b31416ff59 ================================================ [File too large to display: 7 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/string_literal/615220b2a3800a887f14f42f5f642f1e524ea7b5 ================================================ [File too large to display: 695 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/string_literal/65d1bf6f2e113d4cd74bd512898dc0be3d850598 ================================================ [File too large to display: 6 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/string_literal/6750e65dfe53985ea620b25b74690e9adb912580 ================================================ [File too large to display: 9 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/string_literal/678b1652c59a1becf5eb8660b992e5ef3504f3ef ================================================ [File too large to display: 48 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/string_literal/6a3f865363698dc8eb11d530c234e830a30af5a0 ================================================ [File too large to display: 24 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/string_literal/6cf13d3995b0b218a5c0c0fdce5a0cec9089f850 ================================================ [File too large to display: 6 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/string_literal/6d19d3b77b3be81f82feeb998ffd7725fd701fd0 ================================================ [File too large to display: 6 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/string_literal/6def6e6cff73ce5d27104d5891dce3090e14ecf3 ================================================ [File too large to display: 31 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/string_literal/6e195d1aae39e27a34f7bc7a325066b675b5678b ================================================ [File too large to display: 9 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/string_literal/6e1f5e5722e7877bb4c3054c7f6be4a3a2c36d8c ================================================ [File too large to display: 6 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/string_literal/71f5d8f973ad94d40e91577cb62fc838dd61700e ================================================ [File too large to display: 6 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/string_literal/753889c6d4cd0e89717dccc0ff50ebd30415ac22 ================================================ [File too large to display: 8 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/string_literal/75b706660b62c7aaa9c11552320fe581f39c6820 ================================================ [File too large to display: 9 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/string_literal/77aa6fc67ffbab1ee2961d33c0ef55dc29f0f766 ================================================ [File too large to display: 7 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/string_literal/781ac37c9f8222af3404840604c0d4559d4089c6 ================================================ [File too large to display: 11 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/string_literal/78a7fb5dbfef4da9d0da838dbf594a3ad91cea5b ================================================ [File too large to display: 14 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/string_literal/79099ff6620cb6d92fd51f7f46fa274b58dd3173 ================================================ [File too large to display: 33 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/string_literal/794622981056b9f89a08e8ab5e1a00c38b1fa562 ================================================ [File too large to display: 118 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/string_literal/799e94b786fa81e26772bc5db0a71e94c5020da6 ================================================ [File too large to display: 9 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/string_literal/79d23363b6d0dea7ca3e03350d21ca3d24bede9b ================================================ [File too large to display: 8 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/string_literal/7ac82c42433bc4ca35742ecb8083deddd965efcb ================================================ [File too large to display: 25 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/string_literal/7bf6d56d723dec165ca1da8b089bb4403b992eb7 ================================================ [File too large to display: 20 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/string_literal/7c135e14fa77b5eb9b9e70eac7b9b629be1aeb80 ================================================ [File too large to display: 10 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/string_literal/7cbf6e6a1b2a43d310d2c69d0a772b34e794bd72 ================================================ [File too large to display: 66 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/string_literal/7f4fbaa57b3d81a406ad55f452e61d3192b0a91c ================================================ [File too large to display: 12 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/string_literal/8087a1e31c52972200c4a536a5192cbcda9fc2a0 ================================================ [File too large to display: 12 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/string_literal/80cb08da094850c934d89e75b20fd2ad366247ef ================================================ [File too large to display: 5 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/string_literal/831af40692156be859277cb8332cf5b3b575f4e0 ================================================ [File too large to display: 10 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/string_literal/8365cd0e25f4ee55757dead2fc50e5b89317896a ================================================ [File too large to display: 6 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/string_literal/84b2ec838d54669b11af47a63909e0511d59d80b ================================================ [File too large to display: 10 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/string_literal/857ffafcb227e709435d591d083a07cd61d9f9db ================================================ [File too large to display: 49 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/string_literal/85ec3d48177403439495300df0b885709a3a01df ================================================ [File too large to display: 4 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/string_literal/874000f9bb3f8caca4e38317ef3bf70dccd1fc45 ================================================ [File too large to display: 20 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/string_literal/8a57af39652be7bebe49059056452c57ebf29fad ================================================ [File too large to display: 14 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/string_literal/8ac23ef53588d00ec6eb2b23568399cb6b921897 ================================================ [File too large to display: 73 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/string_literal/8c3ed6d5c089b598cee0d1433d8b528a33b2e58a ================================================ [File too large to display: 23 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/string_literal/8fc905873a9165d927a1d8ba4805042155137d0c ================================================ [File too large to display: 22 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/string_literal/900ed842e35028aec93f6bca38c7505b839b22a6 ================================================ [File too large to display: 23 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/string_literal/91ab872f72d2bbc7185649dc9b7289813b16234c ================================================ [File too large to display: 18 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/string_literal/92b065254b2251dcd339dc32ac90deae8b474716 ================================================ [File too large to display: 15 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/string_literal/93ac8946882128457cd9e283b30ca851945e6690 ================================================ [File too large to display: 2 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/string_literal/95ce5b9a9edd09853e12884f214bebafd40fa46a ================================================ [File too large to display: 6 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/string_literal/96a149083217f06ec7b7805e2767d0f62ef90480 ================================================ [File too large to display: 24 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/string_literal/970edf01dbe20c4c0356cf71a62559b7144baa27 ================================================ [File too large to display: 22 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/string_literal/9730916030311413bb1d2a4e9d93223612d49e94 ================================================ [File too large to display: 8 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/string_literal/98356a8902ab5ea28b79329449aa5c10ec4f2cf3 ================================================ [File too large to display: 17 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/string_literal/986cc7b9818415e1fb08575a978aac57299ac5fb ================================================ [File too large to display: 12 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/string_literal/9a135d30087fdbb82c95afc4f4aba5d12cf2d410 ================================================ [File too large to display: 10 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/string_literal/9bba8c359dfe58cbc7043fa9a375d1d08ca3c62d ================================================ [File too large to display: 12 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/string_literal/9be0df2f364c213dafee965ae895ac70b54067f8 ================================================ [File too large to display: 61 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/string_literal/9c0dc6d4503a8f66596713a60ff5d101cce59430 ================================================ [File too large to display: 5 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/string_literal/9f4a4b6693ec585370525949dec5cd76552c7462 ================================================ [File too large to display: 9 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/string_literal/a0852c2b59e4ca5bfaa98c528aa74e850c2bd6d4 ================================================ [File too large to display: 61 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/string_literal/a12745c8f9d50c3c8c2e66930fb658125da3b93c ================================================ [File too large to display: 43 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/string_literal/a1bb38f60c9abc335ffbc40b5aa2f47428671698 ================================================ [File too large to display: 8 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/string_literal/a65d6b36db17585ada3f0852359fbadeb37378c3 ================================================ [File too large to display: 34 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/string_literal/a7a348b219303e69cee1d69c72434615c7ef181b ================================================ [File too large to display: 7 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/string_literal/a7a4917ae72b218ed27ae83f1e0c49b479e7f72f ================================================ [File too large to display: 6 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/string_literal/a7b765b566d7b56672dc8faa7338248730f2132d ================================================ [File too large to display: 6 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/string_literal/a80b93977a5192c9b2455afd07361f19a2fd3492 ================================================ [File too large to display: 21 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/string_literal/a8e45c402e1556bf48369f1218e80e243d590f2b ================================================ [File too large to display: 25 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/string_literal/abd02fcd74bd62e86f8b6580181df41017c8a72d ================================================ [File too large to display: 14 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/string_literal/ad29df63a3d387eaba14735348e2a5150e00b925 ================================================ [File too large to display: 6 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/string_literal/b055ba7c474ec23a2831f583338145959041d016 ================================================ [File too large to display: 14 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/string_literal/b08faa33bffa2203ba1a124367e2618949408850 ================================================ [File too large to display: 6 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/string_literal/b0d1d2c07a3538c670883fcc3380af3232794071 ================================================ [File too large to display: 10 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/string_literal/b179c14764f907b6b0459172bcca0bc917c0aa65 ================================================ [File too large to display: 12 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/string_literal/b204ac127143968fdb1858d15009767c604afdf6 ================================================ [File too large to display: 122 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/string_literal/b442231af8d19c70c66bd2254b2a3d12e9f809ee ================================================ [File too large to display: 8 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/string_literal/b75101049a878bc14368997b4828521e2fe112e3 ================================================ [File too large to display: 593 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/string_literal/b75b1ed7c86e1a2cb36df7553056fd0d51a9327e ================================================ [File too large to display: 8 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/string_literal/b818b239ef45f737cfd97008f3990ea01817451e ================================================ [File too large to display: 12 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/string_literal/ba009751c34ea9bcc3d4c62cfa2cb56ed9931e14 ================================================ [File too large to display: 35 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/string_literal/ba0bc0a9b4cb282d386e50fecffc6afe47db39c1 ================================================ [File too large to display: 7 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/string_literal/bf3797cd98ce8ccdb14feed4bb34f1a927bdeee2 ================================================ [File too large to display: 8 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/string_literal/c0159cb389c964f20c91b1e38177cbb0ad23848f ================================================ [File too large to display: 10 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/string_literal/c0d83e56b82082e617d31cd7f25064ce8a9fec1f ================================================ [File too large to display: 31 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/string_literal/c0eeec57dfa448064cc21a9648188b50baf012e9 ================================================ [File too large to display: 9 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/string_literal/c3949f3b731c8df590e4488d7a5f1a285be50d18 ================================================ [File too large to display: 8 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/string_literal/c54c99aae6a6ca7191b0d42e59fade69ad2c27f7 ================================================ [File too large to display: 65 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/string_literal/c672382d2badada3d2a86ac6dd9ffcd9bd3617d8 ================================================ [File too large to display: 23 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/string_literal/c773a17398b3951946a39579573e37acdfb5040f ================================================ [File too large to display: 10 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/string_literal/c958f0fbe4055c05d206111bd85dd9ecd7197f8d ================================================ [File too large to display: 32 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/string_literal/cc365a16b17bed8b3cd9ad02e26b8acf0a568e49 ================================================ [File too large to display: 13 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/string_literal/cd0046da5f05badbdc471fc58bc5291b44f34a55 ================================================ [File too large to display: 17 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/string_literal/cf1adcfef9425c515d37f5a0f1f1832939f85d4b ================================================ [File too large to display: 10 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/string_literal/cfaf498d71ae8a79ba6fc76b1d87fee1731fb350 ================================================ [File too large to display: 17 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/string_literal/d08f88df745fa7950b104e4a707a31cfce7b5841 ================================================ [File too large to display: 1 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/string_literal/d441e69fb96bc945e42bbdfe9dd1aabe68df0996 ================================================ [File too large to display: 34 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/string_literal/d58cff7c05b067edbb74b784e468527ebebb6d08 ================================================ [File too large to display: 20 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/string_literal/d696853b0a7dd15ba25337fb4bfbac3568fa55e7 ================================================ [File too large to display: 6 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/string_literal/d6d070ba545de29d35a476af9d5b09bd612cda5b ================================================ [File too large to display: 8 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/string_literal/d878897ed681351eb52f76b458616e19e5481e0e ================================================ [File too large to display: 10 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/string_literal/d8d9c2938593eef2a82b28ccd953d15fc0663799 ================================================ [File too large to display: 129 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/string_literal/da60ae57e2b87ce1988e49c9af5bfcc92e7067b4 ================================================ [File too large to display: 8 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/string_literal/daca17e1af88467ac5c66749941b5dc016c60941 ================================================ [File too large to display: 10 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/string_literal/dad8fef81affc1d18b9a0bd6a01e574300c87e2d ================================================ [File too large to display: 7 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/string_literal/db5efb7768292614e872abd9c7d8f4ef1d5e0b4e ================================================ [File too large to display: 11 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/string_literal/dd79c8cfb8beeacd0460429944b4ecbe95a31561 ================================================ [File too large to display: 1 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/string_literal/ddb4452ebd9245936764cd90aecd16a46a8ac6cd ================================================ [File too large to display: 9 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/string_literal/e36f0fd33d8f4f1469a9a28ff9c4932f3bcbc05d ================================================ [File too large to display: 10 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/string_literal/e3f7be8f63c58239a2dedaf66661259615cb472d ================================================ [File too large to display: 15 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/string_literal/e4dd444751a857cd6b8fe0ed1dc42e75f2461e0d ================================================ [File too large to display: 12 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/string_literal/e6fed90453a5dfe8ea1fb4088ef46b54cf7d7b77 ================================================ [File too large to display: 4 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/string_literal/e710f92e9fe937088ca313f6313ae910581d0d3f ================================================ [File too large to display: 22 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/string_literal/eb43923537146440042f13e5fd99f2aff7aaf999 ================================================ [File too large to display: 12 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/string_literal/ed33909c916d6c0e9768d4979c22216654e82a6a ================================================ [File too large to display: 8 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/string_literal/ee093801766e997f07bfb180f35ef3699b0fb80f ================================================ [File too large to display: 19 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/string_literal/f220d53bd7bd65026c20c376c81b447b65df63da ================================================ [File too large to display: 15 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/string_literal/f34ee07d8776fcd466cb565a724c361bcef329dd ================================================ [File too large to display: 3 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/string_literal/f372e11022b04fb607f4065d55532c34a91770be ================================================ [File too large to display: 10 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/string_literal/f3c35ce004d52c58bcab57d684ae96b6b21781a0 ================================================ [File too large to display: 10 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/string_literal/f56c4bc9276d9f47b3c20524825ab5ea3dd2284a ================================================ [File too large to display: 46 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/string_literal/f6cb218e3fc60c5b47dddf25c790f1f334a06c45 ================================================ [File too large to display: 8 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/string_literal/f7012a12dc520aa6d962300120f793feec6b2ea0 ================================================ [File too large to display: 10 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/string_literal/f7ed109685abbf92bcb50a847c4d3861a246b743 ================================================ [File too large to display: 21 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/string_literal/f86e634d1f56afeeb500bdfcb3aec9d2a0b65588 ================================================ [File too large to display: 8 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/string_literal/f8f498840b2a48396467a5b6f87bfb1cb74a39ca ================================================ [File too large to display: 148 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/string_literal/f92e3d66c814deb02ccd8fa344c51f515b692c8a ================================================ [File too large to display: 9 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/string_literal/f9702b2cf1a92261780a39aececfc85c22232caa ================================================ [File too large to display: 2 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/string_literal/ff546a9b073eefef4541c06b70c57ce5a7517aaf ================================================ [File too large to display: 12 B] ================================================ FILE: toolchain/lex/fuzzer_corpus/tokenized_buffer/adc83b19e793491b1c6ea0fd8b46cd9f32e592fc ================================================ [File too large to display: 1 B] ================================================ FILE: toolchain/lex/helpers.cpp ================================================ [File too large to display: 1.0 KB] ================================================ FILE: toolchain/lex/helpers.h ================================================ [File too large to display: 642 B] ================================================ FILE: toolchain/lex/lex.cpp ================================================ [File too large to display: 69.9 KB] ================================================ FILE: toolchain/lex/lex.h ================================================ [File too large to display: 1.4 KB] ================================================ FILE: toolchain/lex/numeric_literal.cpp ================================================ [File too large to display: 17.1 KB] ================================================ FILE: toolchain/lex/numeric_literal.h ================================================ [File too large to display: 2.4 KB] ================================================ FILE: toolchain/lex/numeric_literal_benchmark.cpp ================================================ [File too large to display: 3.8 KB] ================================================ FILE: toolchain/lex/numeric_literal_fuzzer.cpp ================================================ [File too large to display: 910 B] ================================================ FILE: toolchain/lex/numeric_literal_test.cpp ================================================ [File too large to display: 10.8 KB] ================================================ FILE: toolchain/lex/string_literal.cpp ================================================ [File too large to display: 23.0 KB] ================================================ FILE: toolchain/lex/string_literal.h ================================================ [File too large to display: 3.7 KB] ================================================ FILE: toolchain/lex/string_literal_benchmark.cpp ================================================ [File too large to display: 4.6 KB] ================================================ FILE: toolchain/lex/string_literal_fuzzer.cpp ================================================ [File too large to display: 1.8 KB] ================================================ FILE: toolchain/lex/string_literal_test.cpp ================================================ [File too large to display: 8.0 KB] ================================================ FILE: toolchain/lex/test_helpers.h ================================================ [File too large to display: 2.2 KB] ================================================ FILE: toolchain/lex/testdata/basic_syntax.carbon ================================================ [File too large to display: 2.6 KB] ================================================ FILE: toolchain/lex/testdata/char_literals.carbon ================================================ [File too large to display: 7.5 KB] ================================================ FILE: toolchain/lex/testdata/dump_sem_ir_range.carbon ================================================ [File too large to display: 6.4 KB] ================================================ FILE: toolchain/lex/testdata/fail_bad_comment_introducers.carbon ================================================ [File too large to display: 3.1 KB] ================================================ FILE: toolchain/lex/testdata/fail_bad_comment_introducers_mid_block_indent_change.carbon ================================================ [File too large to display: 1.8 KB] ================================================ FILE: toolchain/lex/testdata/fail_bad_raw_identifier.carbon ================================================ [File too large to display: 6.1 KB] ================================================ FILE: toolchain/lex/testdata/fail_block_string_second_line.carbon ================================================ [File too large to display: 1.9 KB] ================================================ FILE: toolchain/lex/testdata/fail_char_literals_bad_encoding.carbon ================================================ [File too large to display: 6.1 KB] ================================================ FILE: toolchain/lex/testdata/fail_mismatched_brackets.carbon ================================================ [File too large to display: 1.9 KB] ================================================ FILE: toolchain/lex/testdata/fail_mismatched_brackets_2.carbon ================================================ [File too large to display: 3.1 KB] ================================================ FILE: toolchain/lex/testdata/fail_multifile.carbon ================================================ [File too large to display: 1.3 KB] ================================================ FILE: toolchain/lex/testdata/fail_trailing_comments.carbon ================================================ [File too large to display: 4.7 KB] ================================================ FILE: toolchain/lex/testdata/include_in_dumps.carbon ================================================ [File too large to display: 641 B] ================================================ FILE: toolchain/lex/testdata/keywords.carbon ================================================ [File too large to display: 3.1 KB] ================================================ FILE: toolchain/lex/testdata/multifile.carbon ================================================ [File too large to display: 1.5 KB] ================================================ FILE: toolchain/lex/testdata/multiline_string_literals.carbon ================================================ [File too large to display: 1.7 KB] ================================================ FILE: toolchain/lex/testdata/numeric_literals.carbon ================================================ [File too large to display: 13.8 KB] ================================================ FILE: toolchain/lex/testdata/printing_digit_padding.carbon ================================================ [File too large to display: 1.2 KB] ================================================ FILE: toolchain/lex/testdata/printing_integer_literal.carbon ================================================ [File too large to display: 793 B] ================================================ FILE: toolchain/lex/testdata/printing_real_literal.carbon ================================================ [File too large to display: 790 B] ================================================ FILE: toolchain/lex/testdata/printing_token.carbon ================================================ [File too large to display: 761 B] ================================================ FILE: toolchain/lex/testdata/raw_identifier.carbon ================================================ [File too large to display: 1.8 KB] ================================================ FILE: toolchain/lex/testdata/repeated_tuple_index.carbon ================================================ [File too large to display: 4.7 KB] ================================================ FILE: toolchain/lex/testdata/string_literals.carbon ================================================ [File too large to display: 4.7 KB] ================================================ FILE: toolchain/lex/token_index.h ================================================ [File too large to display: 2.4 KB] ================================================ FILE: toolchain/lex/token_info.h ================================================ [File too large to display: 7.6 KB] ================================================ FILE: toolchain/lex/token_kind.cpp ================================================ [File too large to display: 2.6 KB] ================================================ FILE: toolchain/lex/token_kind.def ================================================ [File too large to display: 10.5 KB] ================================================ FILE: toolchain/lex/token_kind.h ================================================ [File too large to display: 5.3 KB] ================================================ FILE: toolchain/lex/token_kind_test.cpp ================================================ [File too large to display: 4.3 KB] ================================================ FILE: toolchain/lex/tokenized_buffer.cpp ================================================ [File too large to display: 16.4 KB] ================================================ FILE: toolchain/lex/tokenized_buffer.h ================================================ [File too large to display: 13.4 KB] ================================================ FILE: toolchain/lex/tokenized_buffer_benchmark.cpp ================================================ [File too large to display: 29.3 KB] ================================================ FILE: toolchain/lex/tokenized_buffer_fuzzer.cpp ================================================ [File too large to display: 2.3 KB] ================================================ FILE: toolchain/lex/tokenized_buffer_test.cpp ================================================ [File too large to display: 51.4 KB] ================================================ FILE: toolchain/lex/tokenized_buffer_test_helpers.h ================================================ [File too large to display: 5.3 KB] ================================================ FILE: toolchain/lower/BUILD ================================================ [File too large to display: 2.5 KB] ================================================ FILE: toolchain/lower/aggregate.cpp ================================================ [File too large to display: 10.0 KB] ================================================ FILE: toolchain/lower/aggregate.h ================================================ [File too large to display: 1.5 KB] ================================================ FILE: toolchain/lower/clang_global_decl.cpp ================================================ [File too large to display: 814 B] ================================================ FILE: toolchain/lower/clang_global_decl.h ================================================ [File too large to display: 649 B] ================================================ FILE: toolchain/lower/constant.cpp ================================================ [File too large to display: 13.8 KB] ================================================ FILE: toolchain/lower/constant.h ================================================ [File too large to display: 870 B] ================================================ FILE: toolchain/lower/context.cpp ================================================ [File too large to display: 4.5 KB] ================================================ FILE: toolchain/lower/context.h ================================================ [File too large to display: 7.2 KB] ================================================ FILE: toolchain/lower/file_context.cpp ================================================ [File too large to display: 54.6 KB] ================================================ FILE: toolchain/lower/file_context.h ================================================ [File too large to display: 13.1 KB] ================================================ FILE: toolchain/lower/function_context.cpp ================================================ [File too large to display: 18.0 KB] ================================================ FILE: toolchain/lower/function_context.h ================================================ [File too large to display: 14.3 KB] ================================================ FILE: toolchain/lower/handle.cpp ================================================ [File too large to display: 14.2 KB] ================================================ FILE: toolchain/lower/handle_aggregates.cpp ================================================ [File too large to display: 4.7 KB] ================================================ FILE: toolchain/lower/handle_call.cpp ================================================ [File too large to display: 30.6 KB] ================================================ FILE: toolchain/lower/handle_expr_category.cpp ================================================ [File too large to display: 3.4 KB] ================================================ FILE: toolchain/lower/lower.cpp ================================================ [File too large to display: 1.7 KB] ================================================ FILE: toolchain/lower/lower.h ================================================ [File too large to display: 961 B] ================================================ FILE: toolchain/lower/mangler.cpp ================================================ [File too large to display: 10.8 KB] ================================================ FILE: toolchain/lower/mangler.h ================================================ [File too large to display: 3.5 KB] ================================================ FILE: toolchain/lower/options.h ================================================ [File too large to display: 1.4 KB] ================================================ FILE: toolchain/lower/specific_coalescer.cpp ================================================ [File too large to display: 11.1 KB] ================================================ FILE: toolchain/lower/specific_coalescer.h ================================================ [File too large to display: 7.2 KB] ================================================ FILE: toolchain/lower/testdata/alias/local.carbon ================================================ [File too large to display: 2.5 KB] ================================================ FILE: toolchain/lower/testdata/array/array_in_place.carbon ================================================ [File too large to display: 3.0 KB] ================================================ FILE: toolchain/lower/testdata/array/assign_return_value.carbon ================================================ [File too large to display: 5.1 KB] ================================================ FILE: toolchain/lower/testdata/array/base.carbon ================================================ [File too large to display: 7.7 KB] ================================================ FILE: toolchain/lower/testdata/array/field.carbon ================================================ [File too large to display: 5.6 KB] ================================================ FILE: toolchain/lower/testdata/array/function_param.carbon ================================================ [File too large to display: 4.6 KB] ================================================ FILE: toolchain/lower/testdata/array/iterate.carbon ================================================ [File too large to display: 20.0 KB] ================================================ FILE: toolchain/lower/testdata/basics/empty.carbon ================================================ [File too large to display: 1.1 KB] ================================================ FILE: toolchain/lower/testdata/basics/fail_before_lowering.carbon ================================================ [File too large to display: 1.1 KB] ================================================ FILE: toolchain/lower/testdata/builtins/bool.carbon ================================================ [File too large to display: 4.5 KB] ================================================ FILE: toolchain/lower/testdata/builtins/char.carbon ================================================ [File too large to display: 2.6 KB] ================================================ FILE: toolchain/lower/testdata/builtins/cpp.carbon ================================================ [File too large to display: 7.8 KB] ================================================ FILE: toolchain/lower/testdata/builtins/float.carbon ================================================ [File too large to display: 20.6 KB] ================================================ FILE: toolchain/lower/testdata/builtins/int.carbon ================================================ [File too large to display: 72.8 KB] ================================================ FILE: toolchain/lower/testdata/builtins/int_literal.carbon ================================================ [File too large to display: 3.7 KB] ================================================ FILE: toolchain/lower/testdata/builtins/maybe_unformed.carbon ================================================ [File too large to display: 10.2 KB] ================================================ FILE: toolchain/lower/testdata/builtins/method_vs_nonmethod.carbon ================================================ [File too large to display: 3.3 KB] ================================================ FILE: toolchain/lower/testdata/builtins/no_op.carbon ================================================ [File too large to display: 4.1 KB] ================================================ FILE: toolchain/lower/testdata/builtins/overloaded_operator.carbon ================================================ [File too large to display: 2.6 KB] ================================================ FILE: toolchain/lower/testdata/builtins/pointer.carbon ================================================ [File too large to display: 2.9 KB] ================================================ FILE: toolchain/lower/testdata/builtins/print_read.carbon ================================================ [File too large to display: 4.3 KB] ================================================ FILE: toolchain/lower/testdata/builtins/types.carbon ================================================ [File too large to display: 3.2 KB] ================================================ FILE: toolchain/lower/testdata/builtins/uint.carbon ================================================ [File too large to display: 17.5 KB] ================================================ FILE: toolchain/lower/testdata/class/adapt.carbon ================================================ [File too large to display: 8.5 KB] ================================================ FILE: toolchain/lower/testdata/class/base.carbon ================================================ [File too large to display: 5.6 KB] ================================================ FILE: toolchain/lower/testdata/class/basic.carbon ================================================ [File too large to display: 2.8 KB] ================================================ FILE: toolchain/lower/testdata/class/convert.carbon ================================================ [File too large to display: 5.4 KB] ================================================ FILE: toolchain/lower/testdata/class/field.carbon ================================================ [File too large to display: 11.0 KB] ================================================ FILE: toolchain/lower/testdata/class/generic.carbon ================================================ [File too large to display: 27.5 KB] ================================================ FILE: toolchain/lower/testdata/class/import.carbon ================================================ [File too large to display: 2.6 KB] ================================================ FILE: toolchain/lower/testdata/class/method.carbon ================================================ [File too large to display: 2.5 KB] ================================================ FILE: toolchain/lower/testdata/class/self.carbon ================================================ [File too large to display: 3.3 KB] ================================================ FILE: toolchain/lower/testdata/class/value_access.carbon ================================================ [File too large to display: 4.1 KB] ================================================ FILE: toolchain/lower/testdata/class/virtual.carbon ================================================ [File too large to display: 42.2 KB] ================================================ FILE: toolchain/lower/testdata/debug/nodebug.carbon ================================================ [File too large to display: 981 B] ================================================ FILE: toolchain/lower/testdata/for/bindings.carbon ================================================ [File too large to display: 14.7 KB] ================================================ FILE: toolchain/lower/testdata/for/break_continue.carbon ================================================ [File too large to display: 21.0 KB] ================================================ FILE: toolchain/lower/testdata/for/for.carbon ================================================ [File too large to display: 19.9 KB] ================================================ FILE: toolchain/lower/testdata/function/call/empty_struct.carbon ================================================ [File too large to display: 2.9 KB] ================================================ FILE: toolchain/lower/testdata/function/call/empty_tuple.carbon ================================================ [File too large to display: 2.9 KB] ================================================ FILE: toolchain/lower/testdata/function/call/form.carbon ================================================ [File too large to display: 10.9 KB] ================================================ FILE: toolchain/lower/testdata/function/call/i32.carbon ================================================ [File too large to display: 3.2 KB] ================================================ FILE: toolchain/lower/testdata/function/call/implicit_empty_tuple_as_arg.carbon ================================================ [File too large to display: 3.8 KB] ================================================ FILE: toolchain/lower/testdata/function/call/params_one.carbon ================================================ [File too large to display: 2.6 KB] ================================================ FILE: toolchain/lower/testdata/function/call/params_one_comma.carbon ================================================ [File too large to display: 2.8 KB] ================================================ FILE: toolchain/lower/testdata/function/call/params_two.carbon ================================================ [File too large to display: 2.7 KB] ================================================ FILE: toolchain/lower/testdata/function/call/params_two_comma.carbon ================================================ [File too large to display: 2.9 KB] ================================================ FILE: toolchain/lower/testdata/function/call/params_zero.carbon ================================================ [File too large to display: 2.3 KB] ================================================ FILE: toolchain/lower/testdata/function/call/ref_param.carbon ================================================ [File too large to display: 3.2 KB] ================================================ FILE: toolchain/lower/testdata/function/call/ref_return.carbon ================================================ [File too large to display: 3.4 KB] ================================================ FILE: toolchain/lower/testdata/function/call/return_implicit.carbon ================================================ [File too large to display: 3.0 KB] ================================================ FILE: toolchain/lower/testdata/function/call/struct_param.carbon ================================================ [File too large to display: 2.9 KB] ================================================ FILE: toolchain/lower/testdata/function/call/tuple_param.carbon ================================================ [File too large to display: 2.9 KB] ================================================ FILE: toolchain/lower/testdata/function/call/tuple_param_with_return_slot.carbon ================================================ [File too large to display: 5.1 KB] ================================================ FILE: toolchain/lower/testdata/function/call/var_param.carbon ================================================ [File too large to display: 3.4 KB] ================================================ FILE: toolchain/lower/testdata/function/declaration/simple.carbon ================================================ [File too large to display: 2.3 KB] ================================================ FILE: toolchain/lower/testdata/function/definition/destroy.carbon ================================================ [File too large to display: 12.4 KB] ================================================ FILE: toolchain/lower/testdata/function/definition/empty_struct.carbon ================================================ [File too large to display: 1.8 KB] ================================================ FILE: toolchain/lower/testdata/function/definition/eval_musteval.carbon ================================================ [File too large to display: 4.8 KB] ================================================ FILE: toolchain/lower/testdata/function/definition/params_one.carbon ================================================ [File too large to display: 2.0 KB] ================================================ FILE: toolchain/lower/testdata/function/definition/params_two.carbon ================================================ [File too large to display: 2.1 KB] ================================================ FILE: toolchain/lower/testdata/function/definition/params_zero.carbon ================================================ [File too large to display: 1.8 KB] ================================================ FILE: toolchain/lower/testdata/function/definition/raw_name.carbon ================================================ [File too large to display: 1.8 KB] ================================================ FILE: toolchain/lower/testdata/function/definition/var_param.carbon ================================================ [File too large to display: 9.6 KB] ================================================ FILE: toolchain/lower/testdata/function/generic/call.carbon ================================================ [File too large to display: 7.9 KB] ================================================ FILE: toolchain/lower/testdata/function/generic/call_basic.carbon ================================================ [File too large to display: 20.7 KB] ================================================ FILE: toolchain/lower/testdata/function/generic/call_basic_depth.carbon ================================================ [File too large to display: 6.6 KB] ================================================ FILE: toolchain/lower/testdata/function/generic/call_dedup_ptr.carbon ================================================ [File too large to display: 4.9 KB] ================================================ FILE: toolchain/lower/testdata/function/generic/call_deref_ptr.carbon ================================================ [File too large to display: 10.2 KB] ================================================ FILE: toolchain/lower/testdata/function/generic/call_different_associated_const.carbon ================================================ [File too large to display: 4.5 KB] ================================================ FILE: toolchain/lower/testdata/function/generic/call_different_impls.carbon ================================================ [File too large to display: 5.1 KB] ================================================ FILE: toolchain/lower/testdata/function/generic/call_different_impls_with_const.carbon ================================================ [File too large to display: 6.8 KB] ================================================ FILE: toolchain/lower/testdata/function/generic/call_different_specific.carbon ================================================ [File too large to display: 10.0 KB] ================================================ FILE: toolchain/lower/testdata/function/generic/call_impl_function.carbon ================================================ [File too large to display: 4.4 KB] ================================================ FILE: toolchain/lower/testdata/function/generic/call_method.carbon ================================================ [File too large to display: 4.6 KB] ================================================ FILE: toolchain/lower/testdata/function/generic/call_recursive_basic.carbon ================================================ [File too large to display: 14.1 KB] ================================================ FILE: toolchain/lower/testdata/function/generic/call_recursive_diamond.carbon ================================================ [File too large to display: 22.8 KB] ================================================ FILE: toolchain/lower/testdata/function/generic/call_recursive_impl.carbon ================================================ [File too large to display: 7.6 KB] ================================================ FILE: toolchain/lower/testdata/function/generic/call_recursive_mutual.carbon ================================================ [File too large to display: 15.4 KB] ================================================ FILE: toolchain/lower/testdata/function/generic/call_recursive_reorder.carbon ================================================ [File too large to display: 7.2 KB] ================================================ FILE: toolchain/lower/testdata/function/generic/call_recursive_reorder_more.carbon ================================================ [File too large to display: 27.4 KB] ================================================ FILE: toolchain/lower/testdata/function/generic/call_recursive_sccs_deep.carbon ================================================ [File too large to display: 33.7 KB] ================================================ FILE: toolchain/lower/testdata/function/generic/call_specific_in_class.carbon ================================================ [File too large to display: 16.5 KB] ================================================ FILE: toolchain/lower/testdata/function/generic/cross_library_name_collision_private.carbon ================================================ [File too large to display: 6.8 KB] ================================================ FILE: toolchain/lower/testdata/function/generic/import.carbon ================================================ [File too large to display: 9.1 KB] ================================================ FILE: toolchain/lower/testdata/function/generic/local_function.carbon ================================================ [File too large to display: 3.7 KB] ================================================ FILE: toolchain/lower/testdata/function/generic/reverse_canonical.carbon ================================================ [File too large to display: 16.8 KB] ================================================ FILE: toolchain/lower/testdata/function/generic/self_canonical.carbon ================================================ [File too large to display: 12.9 KB] ================================================ FILE: toolchain/lower/testdata/function/generic/type_param.carbon ================================================ [File too large to display: 1.2 KB] ================================================ FILE: toolchain/lower/testdata/function/generic/type_representation.carbon ================================================ [File too large to display: 18.6 KB] ================================================ FILE: toolchain/lower/testdata/global/class_obj.carbon ================================================ [File too large to display: 2.6 KB] ================================================ FILE: toolchain/lower/testdata/global/class_with_fun.carbon ================================================ [File too large to display: 3.4 KB] ================================================ FILE: toolchain/lower/testdata/global/decl.carbon ================================================ [File too large to display: 2.1 KB] ================================================ FILE: toolchain/lower/testdata/global/simple_init.carbon ================================================ [File too large to display: 2.1 KB] ================================================ FILE: toolchain/lower/testdata/global/simple_with_fun.carbon ================================================ [File too large to display: 2.9 KB] ================================================ FILE: toolchain/lower/testdata/global/use.carbon ================================================ [File too large to display: 3.1 KB] ================================================ FILE: toolchain/lower/testdata/if/else.carbon ================================================ [File too large to display: 4.3 KB] ================================================ FILE: toolchain/lower/testdata/if/no_else.carbon ================================================ [File too large to display: 3.6 KB] ================================================ FILE: toolchain/lower/testdata/if_expr/basic.carbon ================================================ [File too large to display: 4.0 KB] ================================================ FILE: toolchain/lower/testdata/if_expr/empty_block.carbon ================================================ [File too large to display: 4.5 KB] ================================================ FILE: toolchain/lower/testdata/impl/assoc_fn_alias.carbon ================================================ [File too large to display: 3.2 KB] ================================================ FILE: toolchain/lower/testdata/impl/extend_impl.carbon ================================================ [File too large to display: 3.5 KB] ================================================ FILE: toolchain/lower/testdata/impl/impl.carbon ================================================ [File too large to display: 10.3 KB] ================================================ FILE: toolchain/lower/testdata/impl/import.carbon ================================================ [File too large to display: 4.2 KB] ================================================ FILE: toolchain/lower/testdata/impl/import_facet.carbon ================================================ [File too large to display: 7.4 KB] ================================================ FILE: toolchain/lower/testdata/impl/import_thunk.carbon ================================================ [File too large to display: 17.8 KB] ================================================ FILE: toolchain/lower/testdata/impl/instance_method.carbon ================================================ [File too large to display: 3.0 KB] ================================================ FILE: toolchain/lower/testdata/impl/thunk.carbon ================================================ [File too large to display: 18.3 KB] ================================================ FILE: toolchain/lower/testdata/index/array_element_access.carbon ================================================ [File too large to display: 8.0 KB] ================================================ FILE: toolchain/lower/testdata/interface/assoc.carbon ================================================ [File too large to display: 1.8 KB] ================================================ FILE: toolchain/lower/testdata/interface/basic.carbon ================================================ [File too large to display: 2.8 KB] ================================================ FILE: toolchain/lower/testdata/interface/mangle_declared_class.carbon ================================================ [File too large to display: 2.4 KB] ================================================ FILE: toolchain/lower/testdata/interface/where.carbon ================================================ [File too large to display: 1.8 KB] ================================================ FILE: toolchain/lower/testdata/interop/cpp/base.carbon ================================================ [File too large to display: 9.7 KB] ================================================ FILE: toolchain/lower/testdata/interop/cpp/clang_code_generator_callbacks.carbon ================================================ [File too large to display: 6.5 KB] ================================================ FILE: toolchain/lower/testdata/interop/cpp/constructor.carbon ================================================ [File too large to display: 40.3 KB] ================================================ FILE: toolchain/lower/testdata/interop/cpp/cpp_compat_int.carbon ================================================ [File too large to display: 6.0 KB] ================================================ FILE: toolchain/lower/testdata/interop/cpp/cpp_run.carbon ================================================ [File too large to display: 3.0 KB] ================================================ FILE: toolchain/lower/testdata/interop/cpp/enum.carbon ================================================ [File too large to display: 4.0 KB] ================================================ FILE: toolchain/lower/testdata/interop/cpp/extern_c.carbon ================================================ [File too large to display: 15.2 KB] ================================================ FILE: toolchain/lower/testdata/interop/cpp/field.carbon ================================================ [File too large to display: 17.2 KB] ================================================ FILE: toolchain/lower/testdata/interop/cpp/function_decl.carbon ================================================ [File too large to display: 23.0 KB] ================================================ FILE: toolchain/lower/testdata/interop/cpp/function_in_template.carbon ================================================ [File too large to display: 3.4 KB] ================================================ FILE: toolchain/lower/testdata/interop/cpp/globals.carbon ================================================ [File too large to display: 26.6 KB] ================================================ FILE: toolchain/lower/testdata/interop/cpp/import_inline.carbon ================================================ [File too large to display: 4.2 KB] ================================================ FILE: toolchain/lower/testdata/interop/cpp/method.carbon ================================================ [File too large to display: 18.8 KB] ================================================ FILE: toolchain/lower/testdata/interop/cpp/nullptr.carbon ================================================ [File too large to display: 13.9 KB] ================================================ FILE: toolchain/lower/testdata/interop/cpp/parameters.carbon ================================================ [File too large to display: 26.0 KB] ================================================ FILE: toolchain/lower/testdata/interop/cpp/pointer.carbon ================================================ [File too large to display: 20.9 KB] ================================================ FILE: toolchain/lower/testdata/interop/cpp/reference.carbon ================================================ [File too large to display: 32.5 KB] ================================================ FILE: toolchain/lower/testdata/interop/cpp/return.carbon ================================================ [File too large to display: 32.2 KB] ================================================ FILE: toolchain/lower/testdata/interop/cpp/std_initializer_list.carbon ================================================ [File too large to display: 31.5 KB] ================================================ FILE: toolchain/lower/testdata/interop/cpp/template.carbon ================================================ [File too large to display: 19.5 KB] ================================================ FILE: toolchain/lower/testdata/interop/cpp/virtual_base.carbon ================================================ [File too large to display: 12.0 KB] ================================================ FILE: toolchain/lower/testdata/interop/cpp/void.carbon ================================================ [File too large to display: 23.5 KB] ================================================ FILE: toolchain/lower/testdata/let/copy_value_rep.carbon ================================================ [File too large to display: 3.7 KB] ================================================ FILE: toolchain/lower/testdata/let/local.carbon ================================================ [File too large to display: 4.0 KB] ================================================ FILE: toolchain/lower/testdata/let/tuple.carbon ================================================ [File too large to display: 8.0 KB] ================================================ FILE: toolchain/lower/testdata/namespace/function.carbon ================================================ [File too large to display: 2.8 KB] ================================================ FILE: toolchain/lower/testdata/namespace/namespace_run.carbon ================================================ [File too large to display: 2.3 KB] ================================================ FILE: toolchain/lower/testdata/namespace/nested.carbon ================================================ [File too large to display: 2.4 KB] ================================================ FILE: toolchain/lower/testdata/operators/and.carbon ================================================ [File too large to display: 3.4 KB] ================================================ FILE: toolchain/lower/testdata/operators/and_empty_block.carbon ================================================ [File too large to display: 2.7 KB] ================================================ FILE: toolchain/lower/testdata/operators/arithmetic.carbon ================================================ [File too large to display: 14.1 KB] ================================================ FILE: toolchain/lower/testdata/operators/assignment.carbon ================================================ [File too large to display: 5.6 KB] ================================================ FILE: toolchain/lower/testdata/operators/increment.carbon ================================================ [File too large to display: 6.6 KB] ================================================ FILE: toolchain/lower/testdata/operators/not.carbon ================================================ [File too large to display: 2.1 KB] ================================================ FILE: toolchain/lower/testdata/operators/or.carbon ================================================ [File too large to display: 3.4 KB] ================================================ FILE: toolchain/lower/testdata/operators/or_empty_block.carbon ================================================ [File too large to display: 2.7 KB] ================================================ FILE: toolchain/lower/testdata/operators/overloaded.carbon ================================================ [File too large to display: 9.0 KB] ================================================ FILE: toolchain/lower/testdata/operators/string_indexing.carbon ================================================ [File too large to display: 3.5 KB] ================================================ FILE: toolchain/lower/testdata/packages/cross_package_call.carbon ================================================ [File too large to display: 4.6 KB] ================================================ FILE: toolchain/lower/testdata/packages/imported_package_mangle.carbon ================================================ [File too large to display: 3.5 KB] ================================================ FILE: toolchain/lower/testdata/pointer/address_of_field.carbon ================================================ [File too large to display: 3.6 KB] ================================================ FILE: toolchain/lower/testdata/pointer/address_of_unused.carbon ================================================ [File too large to display: 2.3 KB] ================================================ FILE: toolchain/lower/testdata/pointer/basic.carbon ================================================ [File too large to display: 3.4 KB] ================================================ FILE: toolchain/lower/testdata/pointer/convert.carbon ================================================ [File too large to display: 2.0 KB] ================================================ FILE: toolchain/lower/testdata/pointer/pointer_to_pointer.carbon ================================================ [File too large to display: 3.9 KB] ================================================ FILE: toolchain/lower/testdata/primitives/bool.carbon ================================================ [File too large to display: 3.5 KB] ================================================ FILE: toolchain/lower/testdata/primitives/int_types.carbon ================================================ [File too large to display: 24.8 KB] ================================================ FILE: toolchain/lower/testdata/primitives/numeric_literals.carbon ================================================ [File too large to display: 5.2 KB] ================================================ FILE: toolchain/lower/testdata/primitives/optional.carbon ================================================ [File too large to display: 30.1 KB] ================================================ FILE: toolchain/lower/testdata/primitives/string.carbon ================================================ [File too large to display: 4.8 KB] ================================================ FILE: toolchain/lower/testdata/primitives/type_values.carbon ================================================ [File too large to display: 3.0 KB] ================================================ FILE: toolchain/lower/testdata/primitives/zero.carbon ================================================ [File too large to display: 1.8 KB] ================================================ FILE: toolchain/lower/testdata/return/code_after_return.carbon ================================================ [File too large to display: 2.2 KB] ================================================ FILE: toolchain/lower/testdata/return/no_value.carbon ================================================ [File too large to display: 1.7 KB] ================================================ FILE: toolchain/lower/testdata/return/return_var.carbon ================================================ [File too large to display: 2.4 KB] ================================================ FILE: toolchain/lower/testdata/return/return_var_byval.carbon ================================================ [File too large to display: 2.6 KB] ================================================ FILE: toolchain/lower/testdata/return/value.carbon ================================================ [File too large to display: 1.8 KB] ================================================ FILE: toolchain/lower/testdata/return/var.carbon ================================================ [File too large to display: 2.5 KB] ================================================ FILE: toolchain/lower/testdata/struct/empty.carbon ================================================ [File too large to display: 2.6 KB] ================================================ FILE: toolchain/lower/testdata/struct/member_access.carbon ================================================ [File too large to display: 4.4 KB] ================================================ FILE: toolchain/lower/testdata/struct/nested_struct.carbon ================================================ [File too large to display: 1.9 KB] ================================================ FILE: toolchain/lower/testdata/struct/nested_struct_in_place.carbon ================================================ [File too large to display: 3.1 KB] ================================================ FILE: toolchain/lower/testdata/struct/one_entry.carbon ================================================ [File too large to display: 3.2 KB] ================================================ FILE: toolchain/lower/testdata/struct/partially_const.carbon ================================================ [File too large to display: 2.8 KB] ================================================ FILE: toolchain/lower/testdata/struct/two_entries.carbon ================================================ [File too large to display: 4.4 KB] ================================================ FILE: toolchain/lower/testdata/tuple/access/element_access.carbon ================================================ [File too large to display: 4.7 KB] ================================================ FILE: toolchain/lower/testdata/tuple/access/return_value_access.carbon ================================================ [File too large to display: 4.6 KB] ================================================ FILE: toolchain/lower/testdata/tuple/empty.carbon ================================================ [File too large to display: 2.6 KB] ================================================ FILE: toolchain/lower/testdata/tuple/nested_tuple.carbon ================================================ [File too large to display: 1.8 KB] ================================================ FILE: toolchain/lower/testdata/tuple/nested_tuple_in_place.carbon ================================================ [File too large to display: 3.1 KB] ================================================ FILE: toolchain/lower/testdata/tuple/one_entry.carbon ================================================ [File too large to display: 3.2 KB] ================================================ FILE: toolchain/lower/testdata/tuple/two_entries.carbon ================================================ [File too large to display: 4.6 KB] ================================================ FILE: toolchain/lower/testdata/tuple/value_formation.carbon ================================================ [File too large to display: 6.3 KB] ================================================ FILE: toolchain/lower/testdata/tuple/value_forwarding.carbon ================================================ [File too large to display: 2.8 KB] ================================================ FILE: toolchain/lower/testdata/var/global.carbon ================================================ [File too large to display: 11.7 KB] ================================================ FILE: toolchain/lower/testdata/var/import.carbon ================================================ [File too large to display: 10.2 KB] ================================================ FILE: toolchain/lower/testdata/var/local.carbon ================================================ [File too large to display: 2.5 KB] ================================================ FILE: toolchain/lower/testdata/var/nested.carbon ================================================ [File too large to display: 4.0 KB] ================================================ FILE: toolchain/lower/testdata/var/uninitialized.carbon ================================================ [File too large to display: 3.7 KB] ================================================ FILE: toolchain/lower/testdata/while/break_continue.carbon ================================================ [File too large to display: 4.1 KB] ================================================ FILE: toolchain/lower/testdata/while/preheader.carbon ================================================ [File too large to display: 4.7 KB] ================================================ FILE: toolchain/lower/testdata/while/unreachable_end.carbon ================================================ [File too large to display: 3.2 KB] ================================================ FILE: toolchain/lower/testdata/while/while.carbon ================================================ [File too large to display: 3.1 KB] ================================================ FILE: toolchain/parse/BUILD ================================================ [File too large to display: 5.8 KB] ================================================ FILE: toolchain/parse/context.cpp ================================================ [File too large to display: 18.3 KB] ================================================ FILE: toolchain/parse/context.h ================================================ [File too large to display: 19.3 KB] ================================================ FILE: toolchain/parse/coverage_test.cpp ================================================ [File too large to display: 1.0 KB] ================================================ FILE: toolchain/parse/dump.cpp ================================================ [File too large to display: 1.2 KB] ================================================ FILE: toolchain/parse/dump.h ================================================ [File too large to display: 1.1 KB] ================================================ FILE: toolchain/parse/extract.cpp ================================================ [File too large to display: 14.0 KB] ================================================ FILE: toolchain/parse/fuzzer_corpus/12ec48cdf6b94105e7ade03f1c2963d13ac0d77c ================================================ [File too large to display: 2 B] ================================================ FILE: toolchain/parse/fuzzer_corpus/f8407e180bd92589b728af21c5626c18770cf26b ================================================ [File too large to display: 1 B] ================================================ FILE: toolchain/parse/handle.h ================================================ [File too large to display: 574 B] ================================================ FILE: toolchain/parse/handle_adapt.cpp ================================================ [File too large to display: 1.1 KB] ================================================ FILE: toolchain/parse/handle_alias.cpp ================================================ [File too large to display: 1.7 KB] ================================================ FILE: toolchain/parse/handle_array_expr.cpp ================================================ [File too large to display: 1.7 KB] ================================================ FILE: toolchain/parse/handle_base.cpp ================================================ [File too large to display: 1.5 KB] ================================================ FILE: toolchain/parse/handle_binding_pattern.cpp ================================================ [File too large to display: 6.4 KB] ================================================ FILE: toolchain/parse/handle_brace_expr.cpp ================================================ [File too large to display: 7.9 KB] ================================================ FILE: toolchain/parse/handle_choice.cpp ================================================ [File too large to display: 3.2 KB] ================================================ FILE: toolchain/parse/handle_code_block.cpp ================================================ [File too large to display: 1.4 KB] ================================================ FILE: toolchain/parse/handle_decl_definition.cpp ================================================ [File too large to display: 3.2 KB] ================================================ FILE: toolchain/parse/handle_decl_name_and_params.cpp ================================================ [File too large to display: 3.9 KB] ================================================ FILE: toolchain/parse/handle_decl_scope_loop.cpp ================================================ [File too large to display: 12.8 KB] ================================================ FILE: toolchain/parse/handle_expr.cpp ================================================ [File too large to display: 17.8 KB] ================================================ FILE: toolchain/parse/handle_form_literal.cpp ================================================ [File too large to display: 2.6 KB] ================================================ FILE: toolchain/parse/handle_function.cpp ================================================ [File too large to display: 4.7 KB] ================================================ FILE: toolchain/parse/handle_if_expr.cpp ================================================ [File too large to display: 2.5 KB] ================================================ FILE: toolchain/parse/handle_impl.cpp ================================================ [File too large to display: 2.6 KB] ================================================ FILE: toolchain/parse/handle_import_and_package.cpp ================================================ [File too large to display: 10.5 KB] ================================================ FILE: toolchain/parse/handle_index_expr.cpp ================================================ [File too large to display: 858 B] ================================================ FILE: toolchain/parse/handle_lambda.cpp ================================================ [File too large to display: 4.0 KB] ================================================ FILE: toolchain/parse/handle_let.cpp ================================================ [File too large to display: 3.9 KB] ================================================ FILE: toolchain/parse/handle_match.cpp ================================================ [File too large to display: 9.0 KB] ================================================ FILE: toolchain/parse/handle_namespace.cpp ================================================ [File too large to display: 812 B] ================================================ FILE: toolchain/parse/handle_observe.cpp ================================================ [File too large to display: 2.5 KB] ================================================ FILE: toolchain/parse/handle_paren_condition.cpp ================================================ [File too large to display: 2.2 KB] ================================================ FILE: toolchain/parse/handle_paren_expr.cpp ================================================ [File too large to display: 4.5 KB] ================================================ FILE: toolchain/parse/handle_pattern.cpp ================================================ [File too large to display: 1.3 KB] ================================================ FILE: toolchain/parse/handle_pattern_list.cpp ================================================ [File too large to display: 5.3 KB] ================================================ FILE: toolchain/parse/handle_period.cpp ================================================ [File too large to display: 3.6 KB] ================================================ FILE: toolchain/parse/handle_require.cpp ================================================ [File too large to display: 1.7 KB] ================================================ FILE: toolchain/parse/handle_requirement.cpp ================================================ [File too large to display: 3.8 KB] ================================================ FILE: toolchain/parse/handle_statement.cpp ================================================ [File too large to display: 8.4 KB] ================================================ FILE: toolchain/parse/handle_type.cpp ================================================ [File too large to display: 1.2 KB] ================================================ FILE: toolchain/parse/handle_unused.cpp ================================================ [File too large to display: 1.2 KB] ================================================ FILE: toolchain/parse/handle_var.cpp ================================================ [File too large to display: 5.5 KB] ================================================ FILE: toolchain/parse/node_category.cpp ================================================ [File too large to display: 433 B] ================================================ FILE: toolchain/parse/node_category.h ================================================ [File too large to display: 2.6 KB] ================================================ FILE: toolchain/parse/node_ids.h ================================================ [File too large to display: 8.0 KB] ================================================ FILE: toolchain/parse/node_kind.cpp ================================================ [File too large to display: 1.4 KB] ================================================ FILE: toolchain/parse/node_kind.def ================================================ [File too large to display: 15.3 KB] ================================================ FILE: toolchain/parse/node_kind.h ================================================ [File too large to display: 6.3 KB] ================================================ FILE: toolchain/parse/parse.cpp ================================================ [File too large to display: 2.4 KB] ================================================ FILE: toolchain/parse/parse.h ================================================ [File too large to display: 1.2 KB] ================================================ FILE: toolchain/parse/parse_fuzzer.cpp ================================================ [File too large to display: 1.8 KB] ================================================ FILE: toolchain/parse/precedence.cpp ================================================ [File too large to display: 9.7 KB] ================================================ FILE: toolchain/parse/precedence.h ================================================ [File too large to display: 5.5 KB] ================================================ FILE: toolchain/parse/precedence_test.cpp ================================================ [File too large to display: 6.1 KB] ================================================ FILE: toolchain/parse/state.cpp ================================================ [File too large to display: 462 B] ================================================ FILE: toolchain/parse/state.def ================================================ [File too large to display: 40.6 KB] ================================================ FILE: toolchain/parse/state.h ================================================ [File too large to display: 994 B] ================================================ FILE: toolchain/parse/testdata/alias/basic.carbon ================================================ [File too large to display: 3.9 KB] ================================================ FILE: toolchain/parse/testdata/alias/fail_syntax.carbon ================================================ [File too large to display: 6.4 KB] ================================================ FILE: toolchain/parse/testdata/array/basic.carbon ================================================ [File too large to display: 1.5 KB] ================================================ FILE: toolchain/parse/testdata/array/fail_syntax.carbon ================================================ [File too large to display: 13.6 KB] ================================================ FILE: toolchain/parse/testdata/auto/let.carbon ================================================ [File too large to display: 1.2 KB] ================================================ FILE: toolchain/parse/testdata/auto/match_case.carbon ================================================ [File too large to display: 3.7 KB] ================================================ FILE: toolchain/parse/testdata/auto/return.carbon ================================================ [File too large to display: 1.5 KB] ================================================ FILE: toolchain/parse/testdata/auto/var.carbon ================================================ [File too large to display: 1.3 KB] ================================================ FILE: toolchain/parse/testdata/basics/empty.carbon ================================================ [File too large to display: 720 B] ================================================ FILE: toolchain/parse/testdata/basics/empty_decl.carbon ================================================ [File too large to display: 791 B] ================================================ FILE: toolchain/parse/testdata/basics/fail_bracket_recovery.carbon ================================================ [File too large to display: 6.0 KB] ================================================ FILE: toolchain/parse/testdata/basics/fail_invalid_designators.carbon ================================================ [File too large to display: 2.4 KB] ================================================ FILE: toolchain/parse/testdata/basics/fail_modifiers_before_semi.carbon ================================================ [File too large to display: 1.3 KB] ================================================ FILE: toolchain/parse/testdata/basics/fail_no_intro_with_semi.carbon ================================================ [File too large to display: 1.1 KB] ================================================ FILE: toolchain/parse/testdata/basics/fail_no_intro_without_semi.carbon ================================================ [File too large to display: 1.1 KB] ================================================ FILE: toolchain/parse/testdata/basics/fail_paren_match_regression.carbon ================================================ [File too large to display: 2.2 KB] ================================================ FILE: toolchain/parse/testdata/basics/form_literals.carbon ================================================ [File too large to display: 10.3 KB] ================================================ FILE: toolchain/parse/testdata/basics/function_call.carbon ================================================ [File too large to display: 2.8 KB] ================================================ FILE: toolchain/parse/testdata/basics/multifile.carbon ================================================ [File too large to display: 1.9 KB] ================================================ FILE: toolchain/parse/testdata/basics/multiline_token.carbon ================================================ [File too large to display: 1.3 KB] ================================================ FILE: toolchain/parse/testdata/basics/parens.carbon ================================================ [File too large to display: 2.4 KB] ================================================ FILE: toolchain/parse/testdata/basics/type_literals.carbon ================================================ [File too large to display: 3.1 KB] ================================================ FILE: toolchain/parse/testdata/basics/value_literals.carbon ================================================ [File too large to display: 8.1 KB] ================================================ FILE: toolchain/parse/testdata/choice/basic.carbon ================================================ [File too large to display: 1.6 KB] ================================================ FILE: toolchain/parse/testdata/choice/fail_alternative_with_implicit_params.carbon ================================================ [File too large to display: 1.4 KB] ================================================ FILE: toolchain/parse/testdata/choice/fail_invalid_alternative_identifier.carbon ================================================ [File too large to display: 1.7 KB] ================================================ FILE: toolchain/parse/testdata/choice/fail_invalid_braces.carbon ================================================ [File too large to display: 1.7 KB] ================================================ FILE: toolchain/parse/testdata/choice/fail_missing_definition.carbon ================================================ [File too large to display: 1.4 KB] ================================================ FILE: toolchain/parse/testdata/choice/fail_missing_definition_parameterized.carbon ================================================ [File too large to display: 1.9 KB] ================================================ FILE: toolchain/parse/testdata/choice/fail_qualified_alternative_name.carbon ================================================ [File too large to display: 1.4 KB] ================================================ FILE: toolchain/parse/testdata/choice/local.carbon ================================================ [File too large to display: 1.7 KB] ================================================ FILE: toolchain/parse/testdata/choice/modifiers.carbon ================================================ [File too large to display: 1.7 KB] ================================================ FILE: toolchain/parse/testdata/choice/parameterized.carbon ================================================ [File too large to display: 2.3 KB] ================================================ FILE: toolchain/parse/testdata/class/adapt.carbon ================================================ [File too large to display: 8.6 KB] ================================================ FILE: toolchain/parse/testdata/class/base.carbon ================================================ [File too large to display: 2.7 KB] ================================================ FILE: toolchain/parse/testdata/class/base_misplaced.carbon ================================================ [File too large to display: 2.1 KB] ================================================ FILE: toolchain/parse/testdata/class/basic.carbon ================================================ [File too large to display: 1.4 KB] ================================================ FILE: toolchain/parse/testdata/class/fail_base.carbon ================================================ [File too large to display: 2.8 KB] ================================================ FILE: toolchain/parse/testdata/class/fail_modifiers.carbon ================================================ [File too large to display: 3.1 KB] ================================================ FILE: toolchain/parse/testdata/class/fail_var_name.carbon ================================================ [File too large to display: 5.1 KB] ================================================ FILE: toolchain/parse/testdata/class/fn_definitions.carbon ================================================ [File too large to display: 5.2 KB] ================================================ FILE: toolchain/parse/testdata/class/introducer.carbon ================================================ [File too large to display: 3.3 KB] ================================================ FILE: toolchain/parse/testdata/class/local.carbon ================================================ [File too large to display: 3.0 KB] ================================================ FILE: toolchain/parse/testdata/class/mismatched_introducer.carbon ================================================ [File too large to display: 1.9 KB] ================================================ FILE: toolchain/parse/testdata/class/var.carbon ================================================ [File too large to display: 1.9 KB] ================================================ FILE: toolchain/parse/testdata/for/fail_colon_instead_of_in.carbon ================================================ [File too large to display: 2.6 KB] ================================================ FILE: toolchain/parse/testdata/for/fail_missing_cond.carbon ================================================ [File too large to display: 3.0 KB] ================================================ FILE: toolchain/parse/testdata/for/fail_missing_in.carbon ================================================ [File too large to display: 2.6 KB] ================================================ FILE: toolchain/parse/testdata/for/fail_returned_var.carbon ================================================ [File too large to display: 2.6 KB] ================================================ FILE: toolchain/parse/testdata/for/fail_square_brackets.carbon ================================================ [File too large to display: 3.2 KB] ================================================ FILE: toolchain/parse/testdata/for/nested.carbon ================================================ [File too large to display: 3.3 KB] ================================================ FILE: toolchain/parse/testdata/for/pattern.carbon ================================================ [File too large to display: 6.6 KB] ================================================ FILE: toolchain/parse/testdata/function/call.carbon ================================================ [File too large to display: 9.8 KB] ================================================ FILE: toolchain/parse/testdata/function/decl_statement.carbon ================================================ [File too large to display: 11.2 KB] ================================================ FILE: toolchain/parse/testdata/function/declaration.carbon ================================================ [File too large to display: 26.6 KB] ================================================ FILE: toolchain/parse/testdata/function/definition.carbon ================================================ [File too large to display: 12.8 KB] ================================================ FILE: toolchain/parse/testdata/function/eval_musteval.carbon ================================================ [File too large to display: 2.6 KB] ================================================ FILE: toolchain/parse/testdata/function/extern.carbon ================================================ [File too large to display: 6.9 KB] ================================================ FILE: toolchain/parse/testdata/function/terse.carbon ================================================ [File too large to display: 2.5 KB] ================================================ FILE: toolchain/parse/testdata/generics/deduced_params/empty.carbon ================================================ [File too large to display: 1.9 KB] ================================================ FILE: toolchain/parse/testdata/generics/deduced_params/no_parens.carbon ================================================ [File too large to display: 4.2 KB] ================================================ FILE: toolchain/parse/testdata/generics/deduced_params/one.carbon ================================================ [File too large to display: 2.4 KB] ================================================ FILE: toolchain/parse/testdata/generics/deduced_params/one_suffix_comma.carbon ================================================ [File too large to display: 2.5 KB] ================================================ FILE: toolchain/parse/testdata/generics/deduced_params/six.carbon ================================================ [File too large to display: 5.4 KB] ================================================ FILE: toolchain/parse/testdata/generics/deduced_params/two.carbon ================================================ [File too large to display: 3.0 KB] ================================================ FILE: toolchain/parse/testdata/generics/deduced_params/two_suffix_comma.carbon ================================================ [File too large to display: 3.1 KB] ================================================ FILE: toolchain/parse/testdata/generics/generic_params/basic.carbon ================================================ [File too large to display: 1.4 KB] ================================================ FILE: toolchain/parse/testdata/generics/generic_params/fail_template_no_param.carbon ================================================ [File too large to display: 5.7 KB] ================================================ FILE: toolchain/parse/testdata/generics/generic_params/template.carbon ================================================ [File too large to display: 5.0 KB] ================================================ FILE: toolchain/parse/testdata/generics/impl/basic.carbon ================================================ [File too large to display: 2.3 KB] ================================================ FILE: toolchain/parse/testdata/generics/impl/class.carbon ================================================ [File too large to display: 3.5 KB] ================================================ FILE: toolchain/parse/testdata/generics/impl/declaration.carbon ================================================ [File too large to display: 1.1 KB] ================================================ FILE: toolchain/parse/testdata/generics/impl/empty_body.carbon ================================================ [File too large to display: 1.2 KB] ================================================ FILE: toolchain/parse/testdata/generics/impl/fail_impl.carbon ================================================ [File too large to display: 10.4 KB] ================================================ FILE: toolchain/parse/testdata/generics/impl/fail_out_of_line_member.carbon ================================================ [File too large to display: 4.7 KB] ================================================ FILE: toolchain/parse/testdata/generics/impl/forall.carbon ================================================ [File too large to display: 3.3 KB] ================================================ FILE: toolchain/parse/testdata/generics/interface/associated_constants.carbon ================================================ [File too large to display: 5.6 KB] ================================================ FILE: toolchain/parse/testdata/generics/interface/basic.carbon ================================================ [File too large to display: 3.4 KB] ================================================ FILE: toolchain/parse/testdata/generics/interface/declaration.carbon ================================================ [File too large to display: 1005 B] ================================================ FILE: toolchain/parse/testdata/generics/interface/default_fn.carbon ================================================ [File too large to display: 4.1 KB] ================================================ FILE: toolchain/parse/testdata/generics/interface/empty_body.carbon ================================================ [File too large to display: 1.1 KB] ================================================ FILE: toolchain/parse/testdata/generics/interface/fail_associated_constants.carbon ================================================ [File too large to display: 9.6 KB] ================================================ FILE: toolchain/parse/testdata/generics/interface/fail_missing_name.carbon ================================================ [File too large to display: 1.2 KB] ================================================ FILE: toolchain/parse/testdata/generics/interface/fail_missing_open_curly.carbon ================================================ [File too large to display: 1.9 KB] ================================================ FILE: toolchain/parse/testdata/generics/interface/fail_self_param_syntax.carbon ================================================ [File too large to display: 4.0 KB] ================================================ FILE: toolchain/parse/testdata/generics/interface/final_fn.carbon ================================================ [File too large to display: 4.3 KB] ================================================ FILE: toolchain/parse/testdata/generics/interface/final_member_definition.carbon ================================================ [File too large to display: 3.0 KB] ================================================ FILE: toolchain/parse/testdata/generics/interface/non_instance_fn.carbon ================================================ [File too large to display: 1.6 KB] ================================================ FILE: toolchain/parse/testdata/generics/interface/require.carbon ================================================ [File too large to display: 13.6 KB] ================================================ FILE: toolchain/parse/testdata/generics/interface/self_ref.carbon ================================================ [File too large to display: 3.6 KB] ================================================ FILE: toolchain/parse/testdata/generics/named_constraint/basic.carbon ================================================ [File too large to display: 2.3 KB] ================================================ FILE: toolchain/parse/testdata/generics/named_constraint/fail_incomplete.carbon ================================================ [File too large to display: 1.7 KB] ================================================ FILE: toolchain/parse/testdata/generics/named_constraint/invalid_method.carbon ================================================ [File too large to display: 4.1 KB] ================================================ FILE: toolchain/parse/testdata/generics/named_constraint/template_constraint.carbon ================================================ [File too large to display: 1.8 KB] ================================================ FILE: toolchain/parse/testdata/generics/params/empty.carbon ================================================ [File too large to display: 1.6 KB] ================================================ FILE: toolchain/parse/testdata/generics/params/name_qualifier.carbon ================================================ [File too large to display: 13.1 KB] ================================================ FILE: toolchain/parse/testdata/generics/params/one.carbon ================================================ [File too large to display: 2.0 KB] ================================================ FILE: toolchain/parse/testdata/generics/params/one_suffix_comma.carbon ================================================ [File too large to display: 2.2 KB] ================================================ FILE: toolchain/parse/testdata/generics/params/six.carbon ================================================ [File too large to display: 5.0 KB] ================================================ FILE: toolchain/parse/testdata/generics/params/two.carbon ================================================ [File too large to display: 2.6 KB] ================================================ FILE: toolchain/parse/testdata/generics/params/two_suffix_comma.carbon ================================================ [File too large to display: 2.8 KB] ================================================ FILE: toolchain/parse/testdata/if/basic.carbon ================================================ [File too large to display: 2.7 KB] ================================================ FILE: toolchain/parse/testdata/if/else.carbon ================================================ [File too large to display: 4.6 KB] ================================================ FILE: toolchain/parse/testdata/if/fail_else_unbraced.carbon ================================================ [File too large to display: 5.5 KB] ================================================ FILE: toolchain/parse/testdata/if/fail_errors.carbon ================================================ [File too large to display: 4.0 KB] ================================================ FILE: toolchain/parse/testdata/if/fail_missing_cond.carbon ================================================ [File too large to display: 1.8 KB] ================================================ FILE: toolchain/parse/testdata/if/fail_square_brackets.carbon ================================================ [File too large to display: 2.6 KB] ================================================ FILE: toolchain/parse/testdata/if/fail_unbraced.carbon ================================================ [File too large to display: 3.4 KB] ================================================ FILE: toolchain/parse/testdata/if_expr/basic.carbon ================================================ [File too large to display: 2.8 KB] ================================================ FILE: toolchain/parse/testdata/if_expr/fail_condition_missing.carbon ================================================ [File too large to display: 2.4 KB] ================================================ FILE: toolchain/parse/testdata/if_expr/fail_else_expr_missing.carbon ================================================ [File too large to display: 2.5 KB] ================================================ FILE: toolchain/parse/testdata/if_expr/fail_else_missing.carbon ================================================ [File too large to display: 2.4 KB] ================================================ FILE: toolchain/parse/testdata/if_expr/fail_then_expr_missing.carbon ================================================ [File too large to display: 2.5 KB] ================================================ FILE: toolchain/parse/testdata/if_expr/fail_then_missing.carbon ================================================ [File too large to display: 2.4 KB] ================================================ FILE: toolchain/parse/testdata/if_expr/fail_top_level_if.carbon ================================================ [File too large to display: 2.5 KB] ================================================ FILE: toolchain/parse/testdata/if_expr/in_type.carbon ================================================ [File too large to display: 3.0 KB] ================================================ FILE: toolchain/parse/testdata/if_expr/precedence.carbon ================================================ [File too large to display: 3.0 KB] ================================================ FILE: toolchain/parse/testdata/index/assign_to_var.carbon ================================================ [File too large to display: 1.5 KB] ================================================ FILE: toolchain/parse/testdata/index/fail_empty_expr.carbon ================================================ [File too large to display: 1.7 KB] ================================================ FILE: toolchain/parse/testdata/index/fail_malformed_expr.carbon ================================================ [File too large to display: 1.8 KB] ================================================ FILE: toolchain/parse/testdata/lambda/lambda.carbon ================================================ [File too large to display: 10.8 KB] ================================================ FILE: toolchain/parse/testdata/let/fail_bad_name.carbon ================================================ [File too large to display: 1.4 KB] ================================================ FILE: toolchain/parse/testdata/let/fail_empty.carbon ================================================ [File too large to display: 1.3 KB] ================================================ FILE: toolchain/parse/testdata/let/fail_generic_ref.carbon ================================================ [File too large to display: 1.7 KB] ================================================ FILE: toolchain/parse/testdata/let/fail_missing_name.carbon ================================================ [File too large to display: 2.6 KB] ================================================ FILE: toolchain/parse/testdata/let/fail_missing_type.carbon ================================================ [File too large to display: 1.4 KB] ================================================ FILE: toolchain/parse/testdata/let/fail_no_semi.carbon ================================================ [File too large to display: 1.5 KB] ================================================ FILE: toolchain/parse/testdata/let/let.carbon ================================================ [File too large to display: 3.1 KB] ================================================ FILE: toolchain/parse/testdata/let/let_ref.carbon ================================================ [File too large to display: 3.4 KB] ================================================ FILE: toolchain/parse/testdata/let/let_tuple.carbon ================================================ [File too large to display: 2.4 KB] ================================================ FILE: toolchain/parse/testdata/let/missing_value.carbon ================================================ [File too large to display: 2.7 KB] ================================================ FILE: toolchain/parse/testdata/match/fail_cases_after_default.carbon ================================================ [File too large to display: 4.1 KB] ================================================ FILE: toolchain/parse/testdata/match/fail_missing_case_arrow.carbon ================================================ [File too large to display: 4.3 KB] ================================================ FILE: toolchain/parse/testdata/match/fail_missing_case_pattern.carbon ================================================ [File too large to display: 2.9 KB] ================================================ FILE: toolchain/parse/testdata/match/fail_missing_case_statements_block.carbon ================================================ [File too large to display: 2.9 KB] ================================================ FILE: toolchain/parse/testdata/match/fail_missing_cases.carbon ================================================ [File too large to display: 2.2 KB] ================================================ FILE: toolchain/parse/testdata/match/fail_missing_cases_block.carbon ================================================ [File too large to display: 2.1 KB] ================================================ FILE: toolchain/parse/testdata/match/fail_missing_default_arrow.carbon ================================================ [File too large to display: 2.7 KB] ================================================ FILE: toolchain/parse/testdata/match/fail_missing_default_statements_block.carbon ================================================ [File too large to display: 2.6 KB] ================================================ FILE: toolchain/parse/testdata/match/fail_missing_guard_close_paren.carbon ================================================ [File too large to display: 3.6 KB] ================================================ FILE: toolchain/parse/testdata/match/fail_missing_guard_open_paren.carbon ================================================ [File too large to display: 3.6 KB] ================================================ FILE: toolchain/parse/testdata/match/fail_missing_guard_parens_only_parse_errors.carbon ================================================ [File too large to display: 4.9 KB] ================================================ FILE: toolchain/parse/testdata/match/fail_missing_matched_expr.carbon ================================================ [File too large to display: 2.9 KB] ================================================ FILE: toolchain/parse/testdata/match/fail_unexpected_tokens_in_cases_block.carbon ================================================ [File too large to display: 7.5 KB] ================================================ FILE: toolchain/parse/testdata/match/match.carbon ================================================ [File too large to display: 7.4 KB] ================================================ FILE: toolchain/parse/testdata/member_access/compound.carbon ================================================ [File too large to display: 2.1 KB] ================================================ FILE: toolchain/parse/testdata/member_access/fail_keyword.carbon ================================================ [File too large to display: 4.4 KB] ================================================ FILE: toolchain/parse/testdata/member_access/keyword.carbon ================================================ [File too large to display: 1.8 KB] ================================================ FILE: toolchain/parse/testdata/member_access/simple.carbon ================================================ [File too large to display: 3.0 KB] ================================================ FILE: toolchain/parse/testdata/namespace/args.carbon ================================================ [File too large to display: 1.1 KB] ================================================ FILE: toolchain/parse/testdata/namespace/basic.carbon ================================================ [File too large to display: 1.9 KB] ================================================ FILE: toolchain/parse/testdata/namespace/fail_arrow.carbon ================================================ [File too large to display: 1.5 KB] ================================================ FILE: toolchain/parse/testdata/namespace/fail_incomplete.carbon ================================================ [File too large to display: 3.5 KB] ================================================ FILE: toolchain/parse/testdata/namespace/fail_incomplete_name.carbon ================================================ [File too large to display: 1.4 KB] ================================================ FILE: toolchain/parse/testdata/namespace/fail_modifiers.carbon ================================================ [File too large to display: 1.7 KB] ================================================ FILE: toolchain/parse/testdata/namespace/fail_no_name.carbon ================================================ [File too large to display: 1.2 KB] ================================================ FILE: toolchain/parse/testdata/namespace/local.carbon ================================================ [File too large to display: 1.4 KB] ================================================ FILE: toolchain/parse/testdata/namespace/modifiers.carbon ================================================ [File too large to display: 1.7 KB] ================================================ FILE: toolchain/parse/testdata/namespace/nested.carbon ================================================ [File too large to display: 2.8 KB] ================================================ FILE: toolchain/parse/testdata/observe/fail_invalid_operator.carbon ================================================ [File too large to display: 1.2 KB] ================================================ FILE: toolchain/parse/testdata/observe/fail_missing_expr.carbon ================================================ [File too large to display: 2.9 KB] ================================================ FILE: toolchain/parse/testdata/observe/observe.carbon ================================================ [File too large to display: 5.2 KB] ================================================ FILE: toolchain/parse/testdata/operators/assign.carbon ================================================ [File too large to display: 6.2 KB] ================================================ FILE: toolchain/parse/testdata/operators/associative.carbon ================================================ [File too large to display: 1.9 KB] ================================================ FILE: toolchain/parse/testdata/operators/fail_chained_assign.carbon ================================================ [File too large to display: 2.9 KB] ================================================ FILE: toolchain/parse/testdata/operators/fail_infix_uneven_space_after.carbon ================================================ [File too large to display: 1.8 KB] ================================================ FILE: toolchain/parse/testdata/operators/fail_infix_uneven_space_before.carbon ================================================ [File too large to display: 1.7 KB] ================================================ FILE: toolchain/parse/testdata/operators/fail_invalid_infix.carbon ================================================ [File too large to display: 3.8 KB] ================================================ FILE: toolchain/parse/testdata/operators/fail_postfix_space.carbon ================================================ [File too large to display: 1.6 KB] ================================================ FILE: toolchain/parse/testdata/operators/fail_postfix_space_before_comma.carbon ================================================ [File too large to display: 2.0 KB] ================================================ FILE: toolchain/parse/testdata/operators/fail_postfix_space_in_call.carbon ================================================ [File too large to display: 1.9 KB] ================================================ FILE: toolchain/parse/testdata/operators/fail_postfix_space_surrounding.carbon ================================================ [File too large to display: 1.7 KB] ================================================ FILE: toolchain/parse/testdata/operators/fail_postincrement.carbon ================================================ [File too large to display: 2.6 KB] ================================================ FILE: toolchain/parse/testdata/operators/fail_precedence_and_or.carbon ================================================ [File too large to display: 2.2 KB] ================================================ FILE: toolchain/parse/testdata/operators/fail_precedence_as.carbon ================================================ [File too large to display: 6.8 KB] ================================================ FILE: toolchain/parse/testdata/operators/fail_precedence_assign.carbon ================================================ [File too large to display: 5.2 KB] ================================================ FILE: toolchain/parse/testdata/operators/fail_precedence_or_and.carbon ================================================ [File too large to display: 2.2 KB] ================================================ FILE: toolchain/parse/testdata/operators/fail_precedence_where.carbon ================================================ [File too large to display: 15.4 KB] ================================================ FILE: toolchain/parse/testdata/operators/fail_prefix_repeat.carbon ================================================ [File too large to display: 1.9 KB] ================================================ FILE: toolchain/parse/testdata/operators/fail_prefix_space.carbon ================================================ [File too large to display: 1.6 KB] ================================================ FILE: toolchain/parse/testdata/operators/fail_prefix_uneven_space_with_assign.carbon ================================================ [File too large to display: 1.7 KB] ================================================ FILE: toolchain/parse/testdata/operators/fail_star_minus.carbon ================================================ [File too large to display: 2.1 KB] ================================================ FILE: toolchain/parse/testdata/operators/fail_star_star.carbon ================================================ [File too large to display: 1.8 KB] ================================================ FILE: toolchain/parse/testdata/operators/fail_star_star_no_space.carbon ================================================ [File too large to display: 2.0 KB] ================================================ FILE: toolchain/parse/testdata/operators/fail_variety.carbon ================================================ [File too large to display: 4.4 KB] ================================================ FILE: toolchain/parse/testdata/operators/fixity_in_call.carbon ================================================ [File too large to display: 2.0 KB] ================================================ FILE: toolchain/parse/testdata/operators/fixity_in_params.carbon ================================================ [File too large to display: 1.8 KB] ================================================ FILE: toolchain/parse/testdata/operators/fixity_in_var.carbon ================================================ [File too large to display: 2.6 KB] ================================================ FILE: toolchain/parse/testdata/operators/fixity_with_assign.carbon ================================================ [File too large to display: 3.1 KB] ================================================ FILE: toolchain/parse/testdata/operators/infix.carbon ================================================ [File too large to display: 2.7 KB] ================================================ FILE: toolchain/parse/testdata/operators/infix_no_space.carbon ================================================ [File too large to display: 1.5 KB] ================================================ FILE: toolchain/parse/testdata/operators/infix_with_paren_after.carbon ================================================ [File too large to display: 1.6 KB] ================================================ FILE: toolchain/parse/testdata/operators/infix_with_paren_before.carbon ================================================ [File too large to display: 1.6 KB] ================================================ FILE: toolchain/parse/testdata/operators/modifier.carbon ================================================ [File too large to display: 5.5 KB] ================================================ FILE: toolchain/parse/testdata/operators/postfix.carbon ================================================ [File too large to display: 1.4 KB] ================================================ FILE: toolchain/parse/testdata/operators/postfix_repeat.carbon ================================================ [File too large to display: 1.7 KB] ================================================ FILE: toolchain/parse/testdata/operators/postfix_space_after_op.carbon ================================================ [File too large to display: 1.4 KB] ================================================ FILE: toolchain/parse/testdata/operators/precedence_as.carbon ================================================ [File too large to display: 3.2 KB] ================================================ FILE: toolchain/parse/testdata/operators/precedence_assign.carbon ================================================ [File too large to display: 5.0 KB] ================================================ FILE: toolchain/parse/testdata/operators/precedence_not.carbon ================================================ [File too large to display: 2.1 KB] ================================================ FILE: toolchain/parse/testdata/operators/precedence_unary.carbon ================================================ [File too large to display: 3.6 KB] ================================================ FILE: toolchain/parse/testdata/operators/precedence_where.carbon ================================================ [File too large to display: 7.0 KB] ================================================ FILE: toolchain/parse/testdata/operators/prefix.carbon ================================================ [File too large to display: 3.2 KB] ================================================ FILE: toolchain/parse/testdata/operators/prefix_no_space.carbon ================================================ [File too large to display: 1.4 KB] ================================================ FILE: toolchain/parse/testdata/operators/prefix_repeat.carbon ================================================ [File too large to display: 1.7 KB] ================================================ FILE: toolchain/parse/testdata/operators/ref.carbon ================================================ [File too large to display: 5.4 KB] ================================================ FILE: toolchain/parse/testdata/operators/spaceship.carbon ================================================ [File too large to display: 1.5 KB] ================================================ FILE: toolchain/parse/testdata/operators/three_stars.carbon ================================================ [File too large to display: 1.8 KB] ================================================ FILE: toolchain/parse/testdata/operators/unary.carbon ================================================ [File too large to display: 3.5 KB] ================================================ FILE: toolchain/parse/testdata/package_expr/basic.carbon ================================================ [File too large to display: 1.6 KB] ================================================ FILE: toolchain/parse/testdata/package_expr/core.carbon ================================================ [File too large to display: 1.6 KB] ================================================ FILE: toolchain/parse/testdata/package_expr/fail_in_name.carbon ================================================ [File too large to display: 2.4 KB] ================================================ FILE: toolchain/parse/testdata/package_expr/fail_standalone.carbon ================================================ [File too large to display: 1.6 KB] ================================================ FILE: toolchain/parse/testdata/packages/export.carbon ================================================ [File too large to display: 12.6 KB] ================================================ FILE: toolchain/parse/testdata/packages/import/after_import.carbon ================================================ [File too large to display: 1.2 KB] ================================================ FILE: toolchain/parse/testdata/packages/import/after_package.carbon ================================================ [File too large to display: 1.2 KB] ================================================ FILE: toolchain/parse/testdata/packages/import/basic.carbon ================================================ [File too large to display: 968 B] ================================================ FILE: toolchain/parse/testdata/packages/import/core.carbon ================================================ [File too large to display: 1.3 KB] ================================================ FILE: toolchain/parse/testdata/packages/import/cpp_inline.carbon ================================================ [File too large to display: 7.3 KB] ================================================ FILE: toolchain/parse/testdata/packages/import/current_package_library.carbon ================================================ [File too large to display: 1.6 KB] ================================================ FILE: toolchain/parse/testdata/packages/import/export.carbon ================================================ [File too large to display: 6.7 KB] ================================================ FILE: toolchain/parse/testdata/packages/import/fail_after_decl.carbon ================================================ [File too large to display: 1.7 KB] ================================================ FILE: toolchain/parse/testdata/packages/import/fail_after_decl_repeated.carbon ================================================ [File too large to display: 3.7 KB] ================================================ FILE: toolchain/parse/testdata/packages/import/fail_extra_string.carbon ================================================ [File too large to display: 1.4 KB] ================================================ FILE: toolchain/parse/testdata/packages/import/fail_in_nested_scope.carbon ================================================ [File too large to display: 2.7 KB] ================================================ FILE: toolchain/parse/testdata/packages/import/fail_library_is_identifier.carbon ================================================ [File too large to display: 1.5 KB] ================================================ FILE: toolchain/parse/testdata/packages/import/fail_name_is_keyword.carbon ================================================ [File too large to display: 1.1 KB] ================================================ FILE: toolchain/parse/testdata/packages/import/fail_no_name.carbon ================================================ [File too large to display: 1.1 KB] ================================================ FILE: toolchain/parse/testdata/packages/import/fail_no_semi.carbon ================================================ [File too large to display: 1.2 KB] ================================================ FILE: toolchain/parse/testdata/packages/import/fail_omit_library_keyword.carbon ================================================ [File too large to display: 1.2 KB] ================================================ FILE: toolchain/parse/testdata/packages/import/fail_type.carbon ================================================ [File too large to display: 1.2 KB] ================================================ FILE: toolchain/parse/testdata/packages/import/library.carbon ================================================ [File too large to display: 1.1 KB] ================================================ FILE: toolchain/parse/testdata/packages/import/ordering.carbon ================================================ [File too large to display: 2.5 KB] ================================================ FILE: toolchain/parse/testdata/packages/import/semi_before.carbon ================================================ [File too large to display: 1.5 KB] ================================================ FILE: toolchain/parse/testdata/packages/keyword_names.carbon ================================================ [File too large to display: 1.9 KB] ================================================ FILE: toolchain/parse/testdata/packages/library/basic.carbon ================================================ [File too large to display: 1.5 KB] ================================================ FILE: toolchain/parse/testdata/packages/library/fail_in_nested_scope.carbon ================================================ [File too large to display: 4.8 KB] ================================================ FILE: toolchain/parse/testdata/packages/library/fail_invalid_name.carbon ================================================ [File too large to display: 4.5 KB] ================================================ FILE: toolchain/parse/testdata/packages/library/fail_semi_before.carbon ================================================ [File too large to display: 1.4 KB] ================================================ FILE: toolchain/parse/testdata/packages/library/fail_too_late.carbon ================================================ [File too large to display: 1.5 KB] ================================================ FILE: toolchain/parse/testdata/packages/package/api.carbon ================================================ [File too large to display: 968 B] ================================================ FILE: toolchain/parse/testdata/packages/package/api_library.carbon ================================================ [File too large to display: 1.1 KB] ================================================ FILE: toolchain/parse/testdata/packages/package/core.carbon ================================================ [File too large to display: 1.6 KB] ================================================ FILE: toolchain/parse/testdata/packages/package/fail_after_decl.carbon ================================================ [File too large to display: 1.6 KB] ================================================ FILE: toolchain/parse/testdata/packages/package/fail_after_import.carbon ================================================ [File too large to display: 1.5 KB] ================================================ FILE: toolchain/parse/testdata/packages/package/fail_after_package.carbon ================================================ [File too large to display: 1.5 KB] ================================================ FILE: toolchain/parse/testdata/packages/package/fail_extra_string.carbon ================================================ [File too large to display: 1.4 KB] ================================================ FILE: toolchain/parse/testdata/packages/package/fail_in_nested_scope.carbon ================================================ [File too large to display: 5.0 KB] ================================================ FILE: toolchain/parse/testdata/packages/package/fail_library_is_identifier.carbon ================================================ [File too large to display: 1.5 KB] ================================================ FILE: toolchain/parse/testdata/packages/package/fail_library_skips_name.carbon ================================================ [File too large to display: 1.2 KB] ================================================ FILE: toolchain/parse/testdata/packages/package/fail_name_is_keyword.carbon ================================================ [File too large to display: 1.1 KB] ================================================ FILE: toolchain/parse/testdata/packages/package/fail_no_name.carbon ================================================ [File too large to display: 1.1 KB] ================================================ FILE: toolchain/parse/testdata/packages/package/fail_no_semi.carbon ================================================ [File too large to display: 1.2 KB] ================================================ FILE: toolchain/parse/testdata/packages/package/fail_omit_library_keyword.carbon ================================================ [File too large to display: 1.3 KB] ================================================ FILE: toolchain/parse/testdata/packages/package/fail_semi_before.carbon ================================================ [File too large to display: 1.3 KB] ================================================ FILE: toolchain/parse/testdata/packages/package/fail_trailing_impl.carbon ================================================ [File too large to display: 1.2 KB] ================================================ FILE: toolchain/parse/testdata/packages/package/impl.carbon ================================================ [File too large to display: 3.4 KB] ================================================ FILE: toolchain/parse/testdata/packages/package/impl_library.carbon ================================================ [File too large to display: 1.2 KB] ================================================ FILE: toolchain/parse/testdata/packages/package/modifiers.carbon ================================================ [File too large to display: 1.2 KB] ================================================ FILE: toolchain/parse/testdata/pointer/const_pointer.carbon ================================================ [File too large to display: 4.6 KB] ================================================ FILE: toolchain/parse/testdata/pointer/fail_pointer_type_in_expr.carbon ================================================ [File too large to display: 2.0 KB] ================================================ FILE: toolchain/parse/testdata/pointer/fail_tuple_instead_of_compound_member_access.carbon ================================================ [File too large to display: 6.7 KB] ================================================ FILE: toolchain/parse/testdata/pointer/pointer_type.carbon ================================================ [File too large to display: 4.5 KB] ================================================ FILE: toolchain/parse/testdata/pointer/pointer_value.carbon ================================================ [File too large to display: 4.0 KB] ================================================ FILE: toolchain/parse/testdata/return/basic.carbon ================================================ [File too large to display: 1.3 KB] ================================================ FILE: toolchain/parse/testdata/return/expr.carbon ================================================ [File too large to display: 1.4 KB] ================================================ FILE: toolchain/parse/testdata/return/fail_expr_no_semi.carbon ================================================ [File too large to display: 1.6 KB] ================================================ FILE: toolchain/parse/testdata/return/fail_no_semi.carbon ================================================ [File too large to display: 1.8 KB] ================================================ FILE: toolchain/parse/testdata/return/fail_returned_no_var.carbon ================================================ [File too large to display: 1.7 KB] ================================================ FILE: toolchain/parse/testdata/return/fail_var_no_semi.carbon ================================================ [File too large to display: 1.6 KB] ================================================ FILE: toolchain/parse/testdata/return/returned_var.carbon ================================================ [File too large to display: 2.3 KB] ================================================ FILE: toolchain/parse/testdata/struct/fail_comma_only.carbon ================================================ [File too large to display: 1.9 KB] ================================================ FILE: toolchain/parse/testdata/struct/fail_comma_repeat_in_type.carbon ================================================ [File too large to display: 2.3 KB] ================================================ FILE: toolchain/parse/testdata/struct/fail_comma_repeat_in_value.carbon ================================================ [File too large to display: 2.3 KB] ================================================ FILE: toolchain/parse/testdata/struct/fail_extra_token_in_type.carbon ================================================ [File too large to display: 2.4 KB] ================================================ FILE: toolchain/parse/testdata/struct/fail_extra_token_in_value.carbon ================================================ [File too large to display: 2.4 KB] ================================================ FILE: toolchain/parse/testdata/struct/fail_identifier_colon.carbon ================================================ [File too large to display: 1.8 KB] ================================================ FILE: toolchain/parse/testdata/struct/fail_identifier_equals.carbon ================================================ [File too large to display: 1.8 KB] ================================================ FILE: toolchain/parse/testdata/struct/fail_identifier_only.carbon ================================================ [File too large to display: 1.8 KB] ================================================ FILE: toolchain/parse/testdata/struct/fail_invalid_struct_designator.carbon ================================================ [File too large to display: 2.0 KB] ================================================ FILE: toolchain/parse/testdata/struct/fail_missing_type.carbon ================================================ [File too large to display: 2.0 KB] ================================================ FILE: toolchain/parse/testdata/struct/fail_missing_value.carbon ================================================ [File too large to display: 2.0 KB] ================================================ FILE: toolchain/parse/testdata/struct/fail_mix_type_and_value.carbon ================================================ [File too large to display: 2.4 KB] ================================================ FILE: toolchain/parse/testdata/struct/fail_mix_value_and_type.carbon ================================================ [File too large to display: 2.2 KB] ================================================ FILE: toolchain/parse/testdata/struct/fail_mix_with_unknown.carbon ================================================ [File too large to display: 4.9 KB] ================================================ FILE: toolchain/parse/testdata/struct/fail_no_colon_or_equals.carbon ================================================ [File too large to display: 2.0 KB] ================================================ FILE: toolchain/parse/testdata/struct/fail_period_only.carbon ================================================ [File too large to display: 2.0 KB] ================================================ FILE: toolchain/parse/testdata/struct/fail_period_paren.carbon ================================================ [File too large to display: 1.8 KB] ================================================ FILE: toolchain/parse/testdata/struct/fail_period_string_colon.carbon ================================================ [File too large to display: 2.5 KB] ================================================ FILE: toolchain/parse/testdata/struct/fail_period_string_equals.carbon ================================================ [File too large to display: 2.5 KB] ================================================ FILE: toolchain/parse/testdata/struct/fail_type_no_designator.carbon ================================================ [File too large to display: 1.8 KB] ================================================ FILE: toolchain/parse/testdata/struct/no_entries.carbon ================================================ [File too large to display: 1.5 KB] ================================================ FILE: toolchain/parse/testdata/struct/one_entry_no_comma.carbon ================================================ [File too large to display: 2.1 KB] ================================================ FILE: toolchain/parse/testdata/struct/one_entry_with_comma.carbon ================================================ [File too large to display: 2.3 KB] ================================================ FILE: toolchain/parse/testdata/struct/two_entries.carbon ================================================ [File too large to display: 2.9 KB] ================================================ FILE: toolchain/parse/testdata/tuple/access/repeated.carbon ================================================ [File too large to display: 1.6 KB] ================================================ FILE: toolchain/parse/testdata/tuple/access/value_access.carbon ================================================ [File too large to display: 1.4 KB] ================================================ FILE: toolchain/parse/testdata/tuple/nested.carbon ================================================ [File too large to display: 3.4 KB] ================================================ FILE: toolchain/parse/testdata/tuple/two_entries.carbon ================================================ [File too large to display: 1.8 KB] ================================================ FILE: toolchain/parse/testdata/var/fail_bad_name.carbon ================================================ [File too large to display: 1.4 KB] ================================================ FILE: toolchain/parse/testdata/var/fail_empty.carbon ================================================ [File too large to display: 1.4 KB] ================================================ FILE: toolchain/parse/testdata/var/fail_in_interface.carbon ================================================ [File too large to display: 2.7 KB] ================================================ FILE: toolchain/parse/testdata/var/fail_no_semi.carbon ================================================ [File too large to display: 1.7 KB] ================================================ FILE: toolchain/parse/testdata/var/fail_ref.carbon ================================================ [File too large to display: 1.6 KB] ================================================ FILE: toolchain/parse/testdata/var/unused.carbon ================================================ [File too large to display: 30.1 KB] ================================================ FILE: toolchain/parse/testdata/var/var.carbon ================================================ [File too large to display: 3.9 KB] ================================================ FILE: toolchain/parse/testdata/var/var_pattern.carbon ================================================ [File too large to display: 7.7 KB] ================================================ FILE: toolchain/parse/testdata/var/var_tuple.carbon ================================================ [File too large to display: 2.5 KB] ================================================ FILE: toolchain/parse/testdata/where_expr/basic.carbon ================================================ [File too large to display: 5.7 KB] ================================================ FILE: toolchain/parse/testdata/where_expr/designators.carbon ================================================ [File too large to display: 8.0 KB] ================================================ FILE: toolchain/parse/testdata/where_expr/fail_rewrite.carbon ================================================ [File too large to display: 13.1 KB] ================================================ FILE: toolchain/parse/testdata/where_expr/impl_where.carbon ================================================ [File too large to display: 8.0 KB] ================================================ FILE: toolchain/parse/testdata/where_expr/where_and.carbon ================================================ [File too large to display: 9.6 KB] ================================================ FILE: toolchain/parse/testdata/while/basic.carbon ================================================ [File too large to display: 2.9 KB] ================================================ FILE: toolchain/parse/testdata/while/fail_missing_cond.carbon ================================================ [File too large to display: 1.9 KB] ================================================ FILE: toolchain/parse/testdata/while/fail_no_semi.carbon ================================================ [File too large to display: 3.3 KB] ================================================ FILE: toolchain/parse/testdata/while/fail_unbraced.carbon ================================================ [File too large to display: 2.1 KB] ================================================ FILE: toolchain/parse/tree.cpp ================================================ [File too large to display: 3.0 KB] ================================================ FILE: toolchain/parse/tree.h ================================================ [File too large to display: 12.6 KB] ================================================ FILE: toolchain/parse/tree_and_subtrees.cpp ================================================ [File too large to display: 10.4 KB] ================================================ FILE: toolchain/parse/tree_and_subtrees.h ================================================ [File too large to display: 11.5 KB] ================================================ FILE: toolchain/parse/tree_test.cpp ================================================ [File too large to display: 6.8 KB] ================================================ FILE: toolchain/parse/typed_nodes.h ================================================ [File too large to display: 55.6 KB] ================================================ FILE: toolchain/parse/typed_nodes_test.cpp ================================================ [File too large to display: 14.6 KB] ================================================ FILE: toolchain/run_tool.bzl ================================================ [File too large to display: 1.2 KB] ================================================ FILE: toolchain/sem_ir/BUILD ================================================ [File too large to display: 8.3 KB] ================================================ FILE: toolchain/sem_ir/absolute_node_id.cpp ================================================ [File too large to display: 5.4 KB] ================================================ FILE: toolchain/sem_ir/absolute_node_id.h ================================================ [File too large to display: 2.4 KB] ================================================ FILE: toolchain/sem_ir/associated_constant.h ================================================ [File too large to display: 1.5 KB] ================================================ FILE: toolchain/sem_ir/builtin_function_kind.cpp ================================================ [File too large to display: 32.3 KB] ================================================ FILE: toolchain/sem_ir/builtin_function_kind.def ================================================ [File too large to display: 5.4 KB] ================================================ FILE: toolchain/sem_ir/builtin_function_kind.h ================================================ [File too large to display: 1.8 KB] ================================================ FILE: toolchain/sem_ir/clang_decl.cpp ================================================ [File too large to display: 1.3 KB] ================================================ FILE: toolchain/sem_ir/clang_decl.h ================================================ [File too large to display: 4.7 KB] ================================================ FILE: toolchain/sem_ir/class.cpp ================================================ [File too large to display: 1.7 KB] ================================================ FILE: toolchain/sem_ir/class.h ================================================ [File too large to display: 4.2 KB] ================================================ FILE: toolchain/sem_ir/constant.cpp ================================================ [File too large to display: 2.5 KB] ================================================ FILE: toolchain/sem_ir/constant.h ================================================ [File too large to display: 14.2 KB] ================================================ FILE: toolchain/sem_ir/copy_on_write_block.h ================================================ [File too large to display: 3.0 KB] ================================================ FILE: toolchain/sem_ir/cpp_file.h ================================================ [File too large to display: 2.5 KB] ================================================ FILE: toolchain/sem_ir/cpp_global_var.h ================================================ [File too large to display: 2.0 KB] ================================================ FILE: toolchain/sem_ir/cpp_initializer_list.cpp ================================================ [File too large to display: 1.9 KB] ================================================ FILE: toolchain/sem_ir/cpp_initializer_list.h ================================================ [File too large to display: 1.3 KB] ================================================ FILE: toolchain/sem_ir/cpp_overload_set.h ================================================ [File too large to display: 1.5 KB] ================================================ FILE: toolchain/sem_ir/diagnostic_loc_converter.cpp ================================================ [File too large to display: 8.1 KB] ================================================ FILE: toolchain/sem_ir/diagnostic_loc_converter.h ================================================ [File too large to display: 3.4 KB] ================================================ FILE: toolchain/sem_ir/dump.cpp ================================================ [File too large to display: 17.2 KB] ================================================ FILE: toolchain/sem_ir/dump.h ================================================ [File too large to display: 2.3 KB] ================================================ FILE: toolchain/sem_ir/entity_name.h ================================================ [File too large to display: 5.0 KB] ================================================ FILE: toolchain/sem_ir/entity_with_params_base.h ================================================ [File too large to display: 4.7 KB] ================================================ FILE: toolchain/sem_ir/entry_point.cpp ================================================ [File too large to display: 815 B] ================================================ FILE: toolchain/sem_ir/entry_point.h ================================================ [File too large to display: 604 B] ================================================ FILE: toolchain/sem_ir/expr_info.cpp ================================================ [File too large to display: 9.4 KB] ================================================ FILE: toolchain/sem_ir/expr_info.h ================================================ [File too large to display: 1.8 KB] ================================================ FILE: toolchain/sem_ir/facet_type_info.cpp ================================================ [File too large to display: 10.1 KB] ================================================ FILE: toolchain/sem_ir/facet_type_info.h ================================================ [File too large to display: 8.8 KB] ================================================ FILE: toolchain/sem_ir/file.cpp ================================================ [File too large to display: 8.9 KB] ================================================ FILE: toolchain/sem_ir/file.h ================================================ [File too large to display: 15.5 KB] ================================================ FILE: toolchain/sem_ir/formatter.cpp ================================================ [File too large to display: 47.9 KB] ================================================ FILE: toolchain/sem_ir/formatter.h ================================================ [File too large to display: 16.8 KB] ================================================ FILE: toolchain/sem_ir/formatter_chunks.cpp ================================================ [File too large to display: 3.7 KB] ================================================ FILE: toolchain/sem_ir/formatter_chunks.h ================================================ [File too large to display: 4.3 KB] ================================================ FILE: toolchain/sem_ir/function.cpp ================================================ [File too large to display: 5.4 KB] ================================================ FILE: toolchain/sem_ir/function.h ================================================ [File too large to display: 13.9 KB] ================================================ FILE: toolchain/sem_ir/generic.cpp ================================================ [File too large to display: 5.4 KB] ================================================ FILE: toolchain/sem_ir/generic.h ================================================ [File too large to display: 8.3 KB] ================================================ FILE: toolchain/sem_ir/id_kind.h ================================================ [File too large to display: 1.7 KB] ================================================ FILE: toolchain/sem_ir/ids.cpp ================================================ [File too large to display: 7.5 KB] ================================================ FILE: toolchain/sem_ir/ids.h ================================================ [File too large to display: 40.3 KB] ================================================ FILE: toolchain/sem_ir/ids_test.cpp ================================================ [File too large to display: 4.8 KB] ================================================ FILE: toolchain/sem_ir/impl.cpp ================================================ [File too large to display: 1.6 KB] ================================================ FILE: toolchain/sem_ir/impl.h ================================================ [File too large to display: 7.8 KB] ================================================ FILE: toolchain/sem_ir/import_cpp.h ================================================ [File too large to display: 748 B] ================================================ FILE: toolchain/sem_ir/import_ir.cpp ================================================ [File too large to display: 1.8 KB] ================================================ FILE: toolchain/sem_ir/import_ir.h ================================================ [File too large to display: 2.7 KB] ================================================ FILE: toolchain/sem_ir/inst.cpp ================================================ [File too large to display: 1.9 KB] ================================================ FILE: toolchain/sem_ir/inst.h ================================================ [File too large to display: 26.7 KB] ================================================ FILE: toolchain/sem_ir/inst_categories.h ================================================ [File too large to display: 11.3 KB] ================================================ FILE: toolchain/sem_ir/inst_fingerprinter.cpp ================================================ [File too large to display: 18.0 KB] ================================================ FILE: toolchain/sem_ir/inst_fingerprinter.h ================================================ [File too large to display: 2.2 KB] ================================================ FILE: toolchain/sem_ir/inst_kind.cpp ================================================ [File too large to display: 1.0 KB] ================================================ FILE: toolchain/sem_ir/inst_kind.def ================================================ [File too large to display: 7.0 KB] ================================================ FILE: toolchain/sem_ir/inst_kind.h ================================================ [File too large to display: 20.1 KB] ================================================ FILE: toolchain/sem_ir/inst_namer.cpp ================================================ [File too large to display: 50.5 KB] ================================================ FILE: toolchain/sem_ir/inst_namer.h ================================================ [File too large to display: 11.6 KB] ================================================ FILE: toolchain/sem_ir/interface.h ================================================ [File too large to display: 2.6 KB] ================================================ FILE: toolchain/sem_ir/name.cpp ================================================ [File too large to display: 2.4 KB] ================================================ FILE: toolchain/sem_ir/name.h ================================================ [File too large to display: 2.5 KB] ================================================ FILE: toolchain/sem_ir/name_scope.cpp ================================================ [File too large to display: 3.1 KB] ================================================ FILE: toolchain/sem_ir/name_scope.h ================================================ [File too large to display: 13.2 KB] ================================================ FILE: toolchain/sem_ir/name_scope_test.cpp ================================================ [File too large to display: 16.8 KB] ================================================ FILE: toolchain/sem_ir/named_constraint.h ================================================ [File too large to display: 2.7 KB] ================================================ FILE: toolchain/sem_ir/pattern.cpp ================================================ [File too large to display: 2.8 KB] ================================================ FILE: toolchain/sem_ir/pattern.h ================================================ [File too large to display: 1.4 KB] ================================================ FILE: toolchain/sem_ir/require_impls.h ================================================ [File too large to display: 1.9 KB] ================================================ FILE: toolchain/sem_ir/singleton_insts.h ================================================ [File too large to display: 2.4 KB] ================================================ FILE: toolchain/sem_ir/specific_interface.h ================================================ [File too large to display: 1.3 KB] ================================================ FILE: toolchain/sem_ir/specific_named_constraint.h ================================================ [File too large to display: 1.0 KB] ================================================ FILE: toolchain/sem_ir/stringify.cpp ================================================ [File too large to display: 31.2 KB] ================================================ FILE: toolchain/sem_ir/stringify.h ================================================ [File too large to display: 2.0 KB] ================================================ FILE: toolchain/sem_ir/struct_type_field.h ================================================ [File too large to display: 1.3 KB] ================================================ FILE: toolchain/sem_ir/type.cpp ================================================ [File too large to display: 6.5 KB] ================================================ FILE: toolchain/sem_ir/type.h ================================================ [File too large to display: 11.0 KB] ================================================ FILE: toolchain/sem_ir/type_info.cpp ================================================ [File too large to display: 8.5 KB] ================================================ FILE: toolchain/sem_ir/type_info.h ================================================ [File too large to display: 8.8 KB] ================================================ FILE: toolchain/sem_ir/type_iterator.cpp ================================================ [File too large to display: 9.1 KB] ================================================ FILE: toolchain/sem_ir/type_iterator.h ================================================ [File too large to display: 7.4 KB] ================================================ FILE: toolchain/sem_ir/typed_insts.h ================================================ [File too large to display: 82.2 KB] ================================================ FILE: toolchain/sem_ir/typed_insts_test.cpp ================================================ [File too large to display: 4.9 KB] ================================================ FILE: toolchain/sem_ir/vtable.h ================================================ [File too large to display: 1.0 KB] ================================================ FILE: toolchain/sem_ir/yaml_test.cpp ================================================ [File too large to display: 5.8 KB] ================================================ FILE: toolchain/source/BUILD ================================================ [File too large to display: 971 B] ================================================ FILE: toolchain/source/source_buffer.cpp ================================================ [File too large to display: 3.3 KB] ================================================ FILE: toolchain/source/source_buffer.h ================================================ [File too large to display: 3.8 KB] ================================================ FILE: toolchain/source/source_buffer_test.cpp ================================================ [File too large to display: 2.3 KB] ================================================ FILE: toolchain/testing/BUILD ================================================ [File too large to display: 2.7 KB] ================================================ FILE: toolchain/testing/compile_helper.cpp ================================================ [File too large to display: 2.3 KB] ================================================ FILE: toolchain/testing/compile_helper.h ================================================ [File too large to display: 2.3 KB] ================================================ FILE: toolchain/testing/coverage_helper.h ================================================ [File too large to display: 2.7 KB] ================================================ FILE: toolchain/testing/file_test.cpp ================================================ [File too large to display: 14.4 KB] ================================================ FILE: toolchain/testing/testdata/min_prelude/bool.carbon ================================================ [File too large to display: 621 B] ================================================ FILE: toolchain/testing/testdata/min_prelude/convert.carbon ================================================ [File too large to display: 624 B] ================================================ FILE: toolchain/testing/testdata/min_prelude/destroy.carbon ================================================ [File too large to display: 514 B] ================================================ FILE: toolchain/testing/testdata/min_prelude/for.carbon ================================================ [File too large to display: 976 B] ================================================ FILE: toolchain/testing/testdata/min_prelude/full.carbon ================================================ [File too large to display: 635 B] ================================================ FILE: toolchain/testing/testdata/min_prelude/int.carbon ================================================ [File too large to display: 521 B] ================================================ FILE: toolchain/testing/testdata/min_prelude/none.carbon ================================================ [File too large to display: 398 B] ================================================ FILE: toolchain/testing/testdata/min_prelude/parts/as.carbon ================================================ [File too large to display: 1.1 KB] ================================================ FILE: toolchain/testing/testdata/min_prelude/parts/bool.carbon ================================================ [File too large to display: 435 B] ================================================ FILE: toolchain/testing/testdata/min_prelude/parts/char.carbon ================================================ [File too large to display: 576 B] ================================================ FILE: toolchain/testing/testdata/min_prelude/parts/copy.carbon ================================================ [File too large to display: 1.6 KB] ================================================ FILE: toolchain/testing/testdata/min_prelude/parts/default.carbon ================================================ [File too large to display: 562 B] ================================================ FILE: toolchain/testing/testdata/min_prelude/parts/destroy.carbon ================================================ [File too large to display: 625 B] ================================================ FILE: toolchain/testing/testdata/min_prelude/parts/float.carbon ================================================ [File too large to display: 1.3 KB] ================================================ FILE: toolchain/testing/testdata/min_prelude/parts/float_literal.carbon ================================================ [File too large to display: 348 B] ================================================ FILE: toolchain/testing/testdata/min_prelude/parts/form.carbon ================================================ [File too large to display: 313 B] ================================================ FILE: toolchain/testing/testdata/min_prelude/parts/int.carbon ================================================ [File too large to display: 1.6 KB] ================================================ FILE: toolchain/testing/testdata/min_prelude/parts/int_literal.carbon ================================================ [File too large to display: 529 B] ================================================ FILE: toolchain/testing/testdata/min_prelude/parts/iterate.carbon ================================================ [File too large to display: 701 B] ================================================ FILE: toolchain/testing/testdata/min_prelude/parts/maybe_unformed.carbon ================================================ [File too large to display: 538 B] ================================================ FILE: toolchain/testing/testdata/min_prelude/parts/optional.carbon ================================================ [File too large to display: 1.1 KB] ================================================ FILE: toolchain/testing/testdata/min_prelude/parts/string.carbon ================================================ [File too large to display: 536 B] ================================================ FILE: toolchain/testing/testdata/min_prelude/parts/uint.carbon ================================================ [File too large to display: 1.6 KB] ================================================ FILE: toolchain/testing/testdata/min_prelude/primitives.carbon ================================================ [File too large to display: 1.2 KB] ================================================ FILE: toolchain/testing/testdata/min_prelude/uint.carbon ================================================ [File too large to display: 661 B] ================================================ FILE: toolchain/testing/testdata/min_prelude/unformed.carbon ================================================ [File too large to display: 647 B] ================================================ FILE: toolchain/testing/yaml_test_helpers.cpp ================================================ [File too large to display: 3.9 KB] ================================================ FILE: toolchain/testing/yaml_test_helpers.h ================================================ [File too large to display: 5.7 KB] ================================================ FILE: toolchain/testing/yaml_test_helpers_test.cpp ================================================ [File too large to display: 1.5 KB] ================================================ FILE: utils/README.md ================================================ [File too large to display: 805 B] ================================================ FILE: utils/highlightjs/README.md ================================================ [File too large to display: 999 B] ================================================ FILE: utils/highlightjs/highlightjs_carbon_lang.js ================================================ [File too large to display: 11.0 KB] ================================================ FILE: utils/highlightjs/highlightjs_example.html ================================================ [File too large to display: 1.9 KB] ================================================ FILE: utils/nvim/README.md ================================================ [File too large to display: 665 B] ================================================ FILE: utils/nvim/carbon.lua ================================================ [File too large to display: 739 B] ================================================ FILE: utils/nvim/setup.sh ================================================ [File too large to display: 777 B] ================================================ FILE: utils/textmate/README.md ================================================ [File too large to display: 1.4 KB] ================================================ FILE: utils/textmate/Samples/choices.carbon ================================================ [File too large to display: 284 B] ================================================ FILE: utils/textmate/Samples/comments.carbon ================================================ [File too large to display: 515 B] ================================================ FILE: utils/textmate/Samples/customs.carbon ================================================ [File too large to display: 294 B] ================================================ FILE: utils/textmate/Samples/functions_variables.carbon ================================================ [File too large to display: 725 B] ================================================ FILE: utils/textmate/Samples/keywords.carbon ================================================ [File too large to display: 895 B] ================================================ FILE: utils/textmate/Samples/literals.carbon ================================================ [File too large to display: 614 B] ================================================ FILE: utils/textmate/Samples/main.carbon ================================================ [File too large to display: 884 B] ================================================ FILE: utils/textmate/Samples/types.carbon ================================================ [File too large to display: 853 B] ================================================ FILE: utils/textmate/Syntaxes/carbon.tmLanguage.json ================================================ [File too large to display: 11.3 KB] ================================================ FILE: utils/textmate/info.plist ================================================ [File too large to display: 760 B] ================================================ FILE: utils/tree_sitter/.gitignore ================================================ [File too large to display: 315 B] ================================================ FILE: utils/tree_sitter/BUILD ================================================ [File too large to display: 3.5 KB] ================================================ FILE: utils/tree_sitter/README.md ================================================ [File too large to display: 721 B] ================================================ FILE: utils/tree_sitter/grammar.js ================================================ [File too large to display: 13.9 KB] ================================================ FILE: utils/tree_sitter/helix.sh ================================================ [File too large to display: 920 B] ================================================ FILE: utils/tree_sitter/queries/highlights.scm ================================================ [File too large to display: 2.0 KB] ================================================ FILE: utils/tree_sitter/src/scanner.c ================================================ [File too large to display: 5.7 KB] ================================================ FILE: utils/tree_sitter/test_runner.cpp ================================================ [File too large to display: 1.8 KB] ================================================ FILE: utils/tree_sitter/testdata/string/block_inside_quotes.carbon ================================================ [File too large to display: 234 B] ================================================ FILE: utils/tree_sitter/testdata/string/fail_block_not_enough_quotes.carbon ================================================ [File too large to display: 226 B] ================================================ FILE: utils/tree_sitter/testdata/string/fail_block_single_quote.carbon ================================================ [File too large to display: 229 B] ================================================ FILE: utils/tree_sitter/testdata/string/fail_block_unclosed_string.carbon ================================================ [File too large to display: 229 B] ================================================ FILE: utils/tree_sitter/testdata/string/fail_newline.carbon ================================================ [File too large to display: 221 B] ================================================ FILE: utils/tree_sitter/testdata/string/fail_raw_block_more_hash_tags_on_left.carbon ================================================ [File too large to display: 276 B] ================================================ FILE: utils/tree_sitter/testdata/string/fail_raw_block_more_hash_tags_on_right.carbon ================================================ [File too large to display: 276 B] ================================================ FILE: utils/tree_sitter/testdata/string/fail_raw_block_quotes_not_on_own_line.carbon ================================================ [File too large to display: 253 B] ================================================ FILE: utils/tree_sitter/testdata/string/fail_raw_more_hash_tags_on_left.carbon ================================================ [File too large to display: 219 B] ================================================ FILE: utils/tree_sitter/testdata/string/fail_raw_more_hash_tags_on_right.carbon ================================================ [File too large to display: 217 B] ================================================ FILE: utils/tree_sitter/testdata/string/fail_simple_escaped_newline.carbon ================================================ [File too large to display: 225 B] ================================================ FILE: utils/tree_sitter/testdata/string/fail_unclosed_string.carbon ================================================ [File too large to display: 202 B] ================================================ FILE: utils/vim/README.md ================================================ [File too large to display: 1.2 KB] ================================================ FILE: utils/vim/ftdetect/carbon.vim ================================================ [File too large to display: 245 B] ================================================ FILE: utils/vim/ftplugin/carbon.vim ================================================ [File too large to display: 242 B] ================================================ FILE: utils/vim/syntax/carbon.vim ================================================ [File too large to display: 4.8 KB] ================================================ FILE: utils/vscode/.gitignore ================================================ [File too large to display: 217 B] ================================================ FILE: utils/vscode/.vscode/launch.json ================================================ [File too large to display: 318 B] ================================================ FILE: utils/vscode/.vscode/tasks.json ================================================ [File too large to display: 1.1 KB] ================================================ FILE: utils/vscode/.vscodeignore ================================================ [File too large to display: 287 B] ================================================ FILE: utils/vscode/README.md ================================================ [File too large to display: 1.6 KB] ================================================ FILE: utils/vscode/development.md ================================================ [File too large to display: 2.1 KB] ================================================ FILE: utils/vscode/esbuild.js ================================================ [File too large to display: 2.6 KB] ================================================ FILE: utils/vscode/eslint.config.mjs ================================================ [File too large to display: 1.1 KB] ================================================ FILE: utils/vscode/language-configuration.json ================================================ [File too large to display: 356 B] ================================================ FILE: utils/vscode/package.json ================================================ [File too large to display: 2.5 KB] ================================================ FILE: utils/vscode/src/extension.ts ================================================ [File too large to display: 3.4 KB] ================================================ FILE: utils/vscode/tsconfig.json ================================================ [File too large to display: 242 B] ================================================ FILE: version_base.bzl ================================================ [File too large to display: 862 B] ================================================ FILE: website/.ruby-version ================================================ [File too large to display: 4 B] ================================================ FILE: website/404.md ================================================ [File too large to display: 253 B] ================================================ FILE: website/Gemfile ================================================ [File too large to display: 417 B] ================================================ FILE: website/README.md ================================================ [File too large to display: 1.9 KB] ================================================ FILE: website/_config.yml ================================================ [File too large to display: 891 B] ================================================ FILE: website/_includes/footer_custom.html ================================================ [File too large to display: 937 B] ================================================ FILE: website/_includes/nav_footer_custom.html ================================================ [File too large to display: 376 B] ================================================ FILE: website/_sass/custom/custom.scss ================================================ [File too large to display: 967 B] ================================================ FILE: website/_sass/custom/setup.scss ================================================ [File too large to display: 556 B] ================================================ FILE: website/implementation.md ================================================ [File too large to display: 282 B] ================================================ FILE: website/prebuild.py ================================================ [File too large to display: 6.5 KB]